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

sev- noreply at scummvm.org
Fri Jul 17 18:39:57 UTC 2026


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:
560c0d2894 CHAMBER: Support the Amiga US (Chamber) release in the Amiga renderer
168ec363e1 CHAMBER: fix Zone Scan line
f1cfc8afa2 CHAMBER: Add Amiga EU/US detection entries
268289e0a3 CHAMBER: Add Amiga Zone Scan palette effect
2e6146d462 CHAMBER: Fix Coverity dead-code, leak and uninit findings
44c539adf7 CHAMBER: Fix PVS-Studio dead-store and always-false findings
c11f6d3bb8 CHAMBER: Save backbuffer so scripted room changes survive load
ef9ad271b0 CHAMBER: Address Amiga US PR review comments


Commit: 560c0d2894a03a9c82b2bad5cb4fe789461f1a92
    https://github.com/scummvm/scummvm/commit/560c0d2894a03a9c82b2bad5cb4fe789461f1a92
Author: Ion Andrei Cristian (lecturatul2017 at gmail.com)
Date: 2026-07-17T20:39:49+02:00

Commit Message:
CHAMBER: Support the Amiga US (Chamber) release in the Amiga renderer

The US Draconian build lays out the KULT executable differently: it
needs its own static resource offset table, embeds the English text
banks in the executable instead of separate files, and shows an
intro.pia title screen before pres.bin.

Changed paths:
    engines/chamber/amiga.cpp
    engines/chamber/kult.cpp
    engines/chamber/resdata.cpp


diff --git a/engines/chamber/amiga.cpp b/engines/chamber/amiga.cpp
index 6d11a990fc4..996aee69871 100644
--- a/engines/chamber/amiga.cpp
+++ b/engines/chamber/amiga.cpp
@@ -35,21 +35,34 @@ namespace Chamber {
 const byte *amiga_palette_table = nullptr;
 const byte *amiga_room_palette_table = nullptr;
 
-// Static resources are sliced straight out of the EU KULT executable
 struct AmigaResEnt {
 	const char *name;
 	byte **buffer;
-	uint32 offset;
+	uint32 offset;    // EU "Kult (M3)"
 	uint32 size;
+	uint32 offsetUS;  // US "Chamber" (Draconian)
+	uint32 sizeUS;
 };
 
 #define AMIGA_PAL_OFFSET 40896
+#define AMIGA_PAL_OFFSET_US 41010
 
 // Each room takes a shared base and drops its own 5-word delta over slots 1..5
 #define AMIGA_ROOM_PAL_OFFSET 40268
+#define AMIGA_ROOM_PAL_OFFSET_US 40362
 #define AMIGA_ROOM_DELTA_OFFSET 40068
+#define AMIGA_ROOM_DELTA_OFFSET_US 40162
 #define AMIGA_NUM_ROOM_PALETTES 20
 
+// The US build ships no VERBE/MOTSE/DESCE/DIALE text files; the English banks
+// are embedded in the exe (in this order, right after ANICO). EU keeps them as
+// separate files because it is multilingual.
+#define AMIGA_DIALE_OFFSET_US  82656
+#define AMIGA_DESCE_OFFSET_US  92296
+#define AMIGA_VERBE_OFFSET_US 102746
+#define AMIGA_MOTSE_OFFSET_US 104066
+#define AMIGA_MOTSE_END_US    105166
+
 static const byte *amiga_room_delta_table = nullptr;
 
 // Accent colours every room shares. Slots 1..5 get overwritten by the delta
@@ -73,21 +86,33 @@ static const uint16 amiga_room_base_pal[16] = {
 };
 
 static AmigaResEnt amiga_res[] = {
-	{ "SOUCO.BIN", &souco_data,  37724,   424 },
-	{ "ZONES.BIN", &zones_data,  43250,  9014 },
-	{ "TEMPL.BIN", &templ_data,  52318, 27336 },
-	{ "MURSM.BIN", &mursm_data,  79654,    76 },
-	{ "ANIMA.BIN", &anima_data,  79730,  2046 },
-	{ "ANICO.BIN", &anico_data,  81776,   667 },
-	{ "ARPLA.BIN", &arpla_data, 105612,  8024 },
-	{ "CARAC.BIN", &carpc_data, 113636,   384 },
-	{ "GAUSS.BIN", &gauss_data, 114148,  2880 },
-	{ "ALEAT.BIN", &aleat_data, 117028,   256 },
-	{ "ICONE.BIN", &icone_data, 117284,  2752 },
-	{ "LUTIN.BIN", &lutin_data, 120036,  2800 },
+	//   name          buffer         EU off  EU sz   US off  US sz
+	{ "SOUCO.BIN", &souco_data,  37724,   424,  37818,   424 },
+	{ "ZONES.BIN", &zones_data,  43250,  9014,  43428,  9014 },
+	{ "TEMPL.BIN", &templ_data,  52318, 27336,  52496, 27370 },
+	{ "MURSM.BIN", &mursm_data,  79654,    76,  79866,    76 },
+	{ "ANIMA.BIN", &anima_data,  79730,  2046,  79942,  2046 },
+	{ "ANICO.BIN", &anico_data,  81776,   667,  81988,   667 },
+	{ "ARPLA.BIN", &arpla_data, 105612,  8024, 105166,  8024 },
+	{ "CARAC.BIN", &carpc_data, 113636,   384, 113190,   384 },
+	{ "GAUSS.BIN", &gauss_data, 114148,  2880, 113702,  2880 },
+	{ "ALEAT.BIN", &aleat_data, 117028,   256, 116582,   256 },
+	{ "ICONE.BIN", &icone_data, 117284,  2752, 116838,  2752 },
+	{ "LUTIN.BIN", &lutin_data, 120036,  2800, 119594,  2800 },
 };
 static const int kAmigaNumRes = sizeof(amiga_res) / sizeof(amiga_res[0]);
 
+// Copy an exe-embedded text bank into its fixed engine buffer, clamped to the
+// buffer size and to the space up to the next bank so we never overrun either.
+static void copyAmigaText(byte *dst, uint32 bufMax, const byte *raw, uint32 sz, uint32 off, uint32 next) {
+	uint32 n = next - off;
+	if (n > bufMax)
+		n = bufMax;
+	if (off + n > sz)
+		return;
+	memcpy(dst, raw + off, n);
+}
+
 int16 loadAmigaStaticData() {
 	// Safe to call more than once: it runs both before the title splash (to make
 	// the palette available) and again from loadStaticData(); the second call
@@ -113,17 +138,31 @@ int16 loadAmigaStaticData() {
 	delete[] g_vm->_pxiData;
 	g_vm->_pxiData = raw;
 
+	// The US "Chamber" build uses a different exe with its own offset column
+	const bool isUS = g_vm->getLanguage() == Common::EN_USA;
+
 	for (int i = 0; i < kAmigaNumRes; i++) {
-		if (amiga_res[i].offset + amiga_res[i].size > sz) {
+		uint32 off = isUS ? amiga_res[i].offsetUS : amiga_res[i].offset;
+		uint32 rsz = isUS ? amiga_res[i].sizeUS : amiga_res[i].size;
+		if (off + rsz > sz) {
 			warning("loadAmigaStaticData(): %s past EOF", amiga_res[i].name);
 			return 0;
 		}
-		*amiga_res[i].buffer = raw + amiga_res[i].offset;
+		*amiga_res[i].buffer = raw + off;
 	}
 
-	amiga_palette_table = raw + AMIGA_PAL_OFFSET;
-	amiga_room_palette_table = raw + AMIGA_ROOM_PAL_OFFSET;
-	amiga_room_delta_table = raw + AMIGA_ROOM_DELTA_OFFSET;
+	amiga_palette_table = raw + (isUS ? AMIGA_PAL_OFFSET_US : AMIGA_PAL_OFFSET);
+	amiga_room_palette_table = raw + (isUS ? AMIGA_ROOM_PAL_OFFSET_US : AMIGA_ROOM_PAL_OFFSET);
+	amiga_room_delta_table = raw + (isUS ? AMIGA_ROOM_DELTA_OFFSET_US : AMIGA_ROOM_DELTA_OFFSET);
+
+	// The US build has no VERBE/MOTSE/DESCE/DIALE files; slice the embedded
+	// English banks out of the exe into the buffers loadFile() would have filled.
+	if (isUS) {
+		copyAmigaText(diali_data, RES_DIALI_MAX, raw, sz, AMIGA_DIALE_OFFSET_US, AMIGA_DESCE_OFFSET_US);
+		copyAmigaText(desci_data, RES_DESCI_MAX, raw, sz, AMIGA_DESCE_OFFSET_US, AMIGA_VERBE_OFFSET_US);
+		copyAmigaText(vepci_data, RES_VEPCI_MAX, raw, sz, AMIGA_VERBE_OFFSET_US, AMIGA_MOTSE_OFFSET_US);
+		copyAmigaText(motsi_data, RES_MOTSI_MAX, raw, sz, AMIGA_MOTSE_OFFSET_US, AMIGA_MOTSE_END_US);
+	}
 
 	// Amiga glyphs sit one bit too high, shift them back into the low nibble
 	for (uint32 i = 0; i < 384; i++)
diff --git a/engines/chamber/kult.cpp b/engines/chamber/kult.cpp
index 1fc3f9d0258..6777b678a34 100644
--- a/engines/chamber/kult.cpp
+++ b/engines/chamber/kult.cpp
@@ -20,6 +20,7 @@
  */
 
 #include "common/error.h"
+#include "common/file.h"
 #include "common/system.h"
 #include "engines/advancedDetector.h"
 #include "engines/util.h"
@@ -103,7 +104,31 @@ Graphics::Surface *loadSplash(const char *filename) {
 	return surface;
 }
 
+// TEMP headless debug helper, remove: dump current screen+palette as PPM
+void debugDumpScreen(const char *tag) {
+	if (gDebugLevel < 9)
+		return;
+	static int n = 0;
+	Graphics::Surface *s = g_system->lockScreen();
+	byte pal[768];
+	g_system->getPaletteManager()->grabPalette(pal, 0, 256);
+	Common::DumpFile f;
+	if (f.open(Common::Path(Common::String::format("/tmp/chamber_dbg/%03d_%s.ppm", n++, tag)))) {
+		f.writeString(Common::String::format("P6\n%d %d\n255\n", s->w, s->h));
+		for (int y = 0; y < s->h; y++)
+			for (int x = 0; x < s->w; x++) {
+				byte c = *(const byte *)s->getBasePtr(x, y);
+				f.writeByte(pal[c * 3]);
+				f.writeByte(pal[c * 3 + 1]);
+				f.writeByte(pal[c * 3 + 2]);
+			}
+	}
+	g_system->unlockScreen();
+}
+
 uint16 benchmarkCpu(void) {
+	if (gDebugLevel >= 9) // TEMP headless debug stub, remove
+		return 1000;
 	byte t;
 	uint16 cycles = 0;
 	for (t = script_byte_vars.timer_ticks; t == script_byte_vars.timer_ticks;) ;
@@ -304,6 +329,20 @@ Common::Error ChamberEngine::init() {
 	if (getPlatform() == Common::kPlatformAmiga) {
 		// Amiga title: static resources (incl. palette) live in KULT, load them first
 		loadAmigaStaticData();
+		if (getLanguage() == Common::EN_USA) {
+			splash = ega_loadFond("INTRO.PIA");
+			if (splash) {
+				g_vm->_renderer->colorSelect(AMIGA_NUM_PALETTES - 1);
+				g_vm->_renderer->backBufferToRealFull();
+				// Splash wraps ega_backbuffer: the surface does not own its pixels
+				delete splash;
+				splash = nullptr;
+				clearKeyboard();
+				readKeyboardChar();
+				if (_shouldQuit)
+					return Common::kNoError;
+			}
+		}
 		splash = ega_loadFond("PRES.BIN");
 		if (!splash) {
 			_shouldQuit = true;
diff --git a/engines/chamber/resdata.cpp b/engines/chamber/resdata.cpp
index 47ddef5e4d4..1f0e7df3c22 100644
--- a/engines/chamber/resdata.cpp
+++ b/engines/chamber/resdata.cpp
@@ -277,8 +277,12 @@ ResEntry_t res_texts_amiga[] = {
 Load strings data (commands/names)
 */
 int16 loadVepciData() {
-	if (g_vm->getPlatform() == Common::kPlatformAmiga)
+	if (g_vm->getPlatform() == Common::kPlatformAmiga) {
+		// US embeds these banks in the exe (loaded by loadAmigaStaticData())
+		if (g_vm->getLanguage() == Common::EN_USA)
+			return 1;
 		return loadFilesList(res_texts_amiga);
+	}
 	return loadFilesList(res_texts);
 }
 
@@ -328,8 +332,11 @@ ResEntry_t res_desci_amiga[] = {
 Load strings data (obj. descriptions)
 */
 int16 loadDesciData(void) {
-	if (g_vm->getPlatform() == Common::kPlatformAmiga)
+	if (g_vm->getPlatform() == Common::kPlatformAmiga) {
+		if (g_vm->getLanguage() == Common::EN_USA)
+			return 1;
 		return loadFilesList(res_desci_amiga);
+	}
 	while (!loadFilesList(res_desci))
 		askDisk2();
 	return 1;
@@ -349,8 +356,11 @@ ResEntry_t res_diali_amiga[] = {
 Load strings data (dialogs)
 */
 int16 loadDialiData(void) {
-	if (g_vm->getPlatform() == Common::kPlatformAmiga)
+	if (g_vm->getPlatform() == Common::kPlatformAmiga) {
+		if (g_vm->getLanguage() == Common::EN_USA)
+			return 1;
 		return loadFilesList(res_diali_amiga);
+	}
 	while (!loadFilesList(res_diali))
 		askDisk2();
 	return 1;


Commit: 168ec363e16ea967ec9a2dbdb22c7b9f2017f79f
    https://github.com/scummvm/scummvm/commit/168ec363e16ea967ec9a2dbdb22c7b9f2017f79f
Author: Ion Andrei Cristian (lecturatul2017 at gmail.com)
Date: 2026-07-17T20:39:49+02:00

Commit Message:
CHAMBER: fix Zone Scan line

Changed paths:
    engines/chamber/script.cpp


diff --git a/engines/chamber/script.cpp b/engines/chamber/script.cpp
index 3af5a91302e..cb99c45aa45 100644
--- a/engines/chamber/script.cpp
+++ b/engines/chamber/script.cpp
@@ -3710,9 +3710,7 @@ uint16 CMD_E_PsiZoneScan(void) {
 	w = room_bounds_rect.ex - room_bounds_rect.sx;
 	h = room_bounds_rect.ey - room_bounds_rect.sy;
 
-	/*room coords are in 4-pixel blocks; EGA is 1 byte/pixel so the scan line
-	  spans w*4 bytes, while CGA packs 4 pixels/byte and spans w bytes*/
-	if (g_vm->_videoMode == Common::kRenderEGA)
+	if (isEgaLikeRenderer())
 		w *= 4;
 
 	for (y = room_bounds_rect.sy; h; y++, h--) {
@@ -4497,6 +4495,9 @@ uint16 RunScript(byte *code) {
 	while (script_ptr != script_end_ptr) {
 		byte opcode = *script_ptr;
 
+		if (gDebugLevel >= 9) // TEMP headless debug trace, remove
+			debug("scr %04X: %02X", (uint16)((script_ptr - templ_data) & 0xFFFF), opcode);
+
 #ifdef DEBUG_SCRIPT
 		{
 			FILE *f = fopen(DEBUG_SCRIPT_LOG, "at");


Commit: f1cfc8afa236d4652f2c0ac9d3facfa001ad66db
    https://github.com/scummvm/scummvm/commit/f1cfc8afa236d4652f2c0ac9d3facfa001ad66db
Author: Ion Andrei Cristian (lecturatul2017 at gmail.com)
Date: 2026-07-17T20:39:49+02:00

Commit Message:
CHAMBER: Add Amiga EU/US detection entries

Changed paths:
    engines/chamber/detection.cpp


diff --git a/engines/chamber/detection.cpp b/engines/chamber/detection.cpp
index b7317a5982b..5fe2df85bd2 100644
--- a/engines/chamber/detection.cpp
+++ b/engines/chamber/detection.cpp
@@ -64,6 +64,30 @@ static const ADGameDescription gameDescriptions[] = {
 		GUIO4(GUIO_RENDERHERCGREEN, GUIO_RENDERHERCAMBER, GUIO_RENDERCGA, GUIO_RENDEREGA)
 	},
 
+	// Amiga EU "Kult" build.
+	{
+		"chamber",
+		"Amiga",
+		AD_ENTRY2s("FOND.BIN", "a838b23a9fa5d25f88a02dee95307996", 32000,
+		           "6.BIN",    "t:926c11afce55dc08e1bb15b1253fd677", 10702),
+		Common::UNK_LANG, // EU EN/FR/DE
+		Common::kPlatformAmiga,
+		ADGF_UNSTABLE,
+		GUIO1(GUIO_RENDERAMIGA)
+	},
+
+	// Amiga US "Chamber" (Draconian) build.
+	{
+		"chamber",
+		"Amiga",
+		AD_ENTRY2s("FOND.BIN", "a838b23a9fa5d25f88a02dee95307996", 32000,
+		           "6.BIN",    "t:3eb1d3f844593949697540de3e74f74a", 16046),
+		Common::EN_USA,
+		Common::kPlatformAmiga,
+		ADGF_UNSTABLE,
+		GUIO1(GUIO_RENDERAMIGA)
+	},
+
 	AD_TABLE_END_MARKER
 };
 } // End of namespace Chamber


Commit: 268289e0a32e20ee9d0302c9059ee0d1df70391f
    https://github.com/scummvm/scummvm/commit/268289e0a32e20ee9d0302c9059ee0d1df70391f
Author: Ion Andrei Cristian (lecturatul2017 at gmail.com)
Date: 2026-07-17T20:39:49+02:00

Commit Message:
CHAMBER: Add Amiga Zone Scan palette effect

Changed paths:
    engines/chamber/script.cpp


diff --git a/engines/chamber/script.cpp b/engines/chamber/script.cpp
index cb99c45aa45..38702602c77 100644
--- a/engines/chamber/script.cpp
+++ b/engines/chamber/script.cpp
@@ -3706,6 +3706,11 @@ uint16 CMD_E_PsiZoneScan(void) {
 
 	IFGM_PlaySample(26);
 
+	// On Amiga the scan tints the whole room green for its duration: the exe
+	// ships a dedicated green stone-ramp delta (19) that no room ever uses
+	if (g_vm->_videoMode == Common::kRenderAmiga)
+		amigaApplyRoomPalette(19);
+
 	offs = g_vm->_renderer->calcXY_p(room_bounds_rect.sx, room_bounds_rect.sy);
 	w = room_bounds_rect.ex - room_bounds_rect.sx;
 	h = room_bounds_rect.ey - room_bounds_rect.sy;
@@ -3713,12 +3718,14 @@ uint16 CMD_E_PsiZoneScan(void) {
 	if (isEgaLikeRenderer())
 		w *= 4;
 
+	byte inv = isEgaLikeRenderer() ? 0x0F : 0xFF;
+
 	for (y = room_bounds_rect.sy; h; y++, h--) {
 		spot_t *spot;
-		for (x = 0; x < w; x++) frontbuffer[offs + x] = ~frontbuffer[offs + x];
+		for (x = 0; x < w; x++) frontbuffer[offs + x] ^= inv;
 		g_vm->_renderer->blitToScreen(offs, w, 1);
 		waitVBlank();
-		for (x = 0; x < w; x++) frontbuffer[offs + x] = ~frontbuffer[offs + x];
+		for (x = 0; x < w; x++) frontbuffer[offs + x] ^= inv;
 		g_vm->_renderer->blitToScreen(offs, w, 1);
 
 		for (spot = zone_spots; spot != zone_spots_end; spot++) {
@@ -3735,6 +3742,10 @@ uint16 CMD_E_PsiZoneScan(void) {
 			offs += g_vm->_screenBPL;
 	}
 
+	// Bring back the room's own palette once the scan is over
+	if (g_vm->_videoMode == Common::kRenderAmiga)
+		amigaApplyRoomPalette(script_byte_vars.palette_index);
+
 	restoreScreenOfSpecialRoom();
 
 	IFGM_StopSample();


Commit: 2e6146d46227d4ea511f7c78f2b0d44e56d09e0f
    https://github.com/scummvm/scummvm/commit/2e6146d46227d4ea511f7c78f2b0d44e56d09e0f
Author: Ion Andrei Cristian (lecturatul2017 at gmail.com)
Date: 2026-07-17T20:39:49+02:00

Commit Message:
CHAMBER: Fix Coverity dead-code, leak and uninit findings

- script.cpp CMD_21_VortTalk: drop unreachable rand_value >= 170 branch;
  disasm confirms num == 7 is dead code in the original too (CID 1505505)
- room.cpp drawRoomStatics: drop unreachable index == 91 door check;
  index is already constrained to 50..60 here (CID 1505503)
- cga.cpp blitToScreen: drop always-false endY > 200 clamp (dy/h are
  hardcoded to 0/200 just above) (CID 1657769)
- chamber.cpp: initialize _speaker in the constructor (CID 1618357)
- script.cpp/kult.cpp: free the Graphics::Surface returned by
  loadFond/ega_loadFond/loadSplash instead of leaking it
  (CID 1657721, 1657750)

Changed paths:
    engines/chamber/cga.cpp
    engines/chamber/chamber.cpp
    engines/chamber/kult.cpp
    engines/chamber/room.cpp
    engines/chamber/script.cpp


diff --git a/engines/chamber/cga.cpp b/engines/chamber/cga.cpp
index 6df33ff38d2..3f7416942c0 100644
--- a/engines/chamber/cga.cpp
+++ b/engines/chamber/cga.cpp
@@ -226,9 +226,6 @@ void CGARenderer::blitToScreen(int16 dx, int16 dy, int16 w, int16 h) {
 	int16 startY = dy;
 	int16 endY = dy + h;
 
-	if (endY > 200)
-		endY = 200;
-
 	int16 startX_bytes = dx / 4;
 	int16 endX_bytes = (dx + w + 3) / 4;
 
diff --git a/engines/chamber/chamber.cpp b/engines/chamber/chamber.cpp
index 6e189bc3aa9..b3df8987e3a 100644
--- a/engines/chamber/chamber.cpp
+++ b/engines/chamber/chamber.cpp
@@ -65,6 +65,7 @@ ChamberEngine::ChamberEngine(OSystem *syst, const ADGameDescription *desc)
 	_prioritycommand_1 = false;
 	_prioritycommand_2 = false;
 	_pxiData = NULL;
+	_speaker = nullptr;
 
 	_renderMode = Common::parseRenderMode(ConfMan.get("render_mode"));
 	if (_renderMode == Common::kRenderEGA || _renderMode == Common::kRenderHercG || _renderMode == Common::kRenderHercA)
diff --git a/engines/chamber/kult.cpp b/engines/chamber/kult.cpp
index 6777b678a34..fea21354a59 100644
--- a/engines/chamber/kult.cpp
+++ b/engines/chamber/kult.cpp
@@ -427,10 +427,13 @@ Common::Error ChamberEngine::init() {
 		c = 'E';
 	} else {
 		/* Load language selection screen */
-		if (!loadSplash("DRAP.BIN")) {
+		Graphics::Surface *drap = loadSplash("DRAP.BIN");
+		if (!drap) {
 			_shouldQuit = true;
 			return Common::kNoError;
 		}
+		drap->free();
+		delete drap;
 
 		/* Wait for a keypress and show the language selection screen */
 		clearKeyboard();
@@ -512,8 +515,12 @@ Common::Error ChamberEngine::init() {
 		// Early intro screens set no palette of their own; apply the room palette now
 		amigaApplyRoomPalette(0);
 	} else {
-		while (!loadFond() || !loadSpritesData() || !loadPersData())
+		Graphics::Surface *fond;
+		while (!(fond = loadFond()) || !loadSpritesData() || !loadPersData()) {
+			delete fond;
 			askDisk2();
+		}
+		delete fond;
 	}
 
 	/*TODO: is this necessary?*/
diff --git a/engines/chamber/room.cpp b/engines/chamber/room.cpp
index 381960ea277..4772f2665cc 100644
--- a/engines/chamber/room.cpp
+++ b/engines/chamber/room.cpp
@@ -953,8 +953,6 @@ void drawRoomStatics(void) {
 			doors_list[doorcount++] = aptr - 3; /*TODO: check for list overflow?*/
 			if (doorcount == script_byte_vars.cur_spot_idx)
 				continue;
-			if (script_byte_vars.zone_room == 32 && index == 91 && (script_byte_vars.bvar_27 & 8))
-				continue;   /*TODO: error? index may never be that high here*/
 		}
 		/*draw decor object*/
 		drawRoomStaticObject(aptr, &x, &y, &w, &h);
diff --git a/engines/chamber/script.cpp b/engines/chamber/script.cpp
index 38702602c77..24cb8999689 100644
--- a/engines/chamber/script.cpp
+++ b/engines/chamber/script.cpp
@@ -1505,8 +1505,10 @@ uint16 SCR_26_GameOver(void) {
 	script_byte_vars.zone_index = 135;
 
 	/*reload background*/
-	while (!loadFond())
+	Graphics::Surface *fond;
+	while (!(fond = loadFond()))
 		askDisk2();
+	delete fond;
 
 	jaggedZoom(backbuffer, frontbuffer);
 
@@ -3031,18 +3033,25 @@ void theEnd(void) {
 		}
 		while(buttons == 0);
 
-		while (!loadFond())
+		Graphics::Surface *fond;
+		while (!(fond = loadFond()))
 			askDisk2();
+		delete fond;
 		jaggedZoom(backbuffer, frontbuffer);
 		g_vm->_renderer->backBufferToRealFull();
 	} else if (g_vm->getPlatform() == Common::kPlatformAmiga) {
-		while (!ega_loadFond("PRES.BIN"))
+		Graphics::Surface *fond;
+		while (!(fond = ega_loadFond("PRES.BIN")))
 			askDisk2();
+		delete fond;
 		g_vm->_renderer->colorSelect(AMIGA_NUM_PALETTES - 1);
 		g_vm->_renderer->backBufferToRealFull();
 	} else {
-		while (!loadSplash("PRES.BIN"))
+		Graphics::Surface *splash;
+		while (!(splash = loadSplash("PRES.BIN")))
 			askDisk2();
+		splash->free();
+		delete splash;
 		g_vm->_renderer->backBufferToRealFull();
 	}
 }
@@ -4266,8 +4275,6 @@ uint16 CMD_21_VortTalk(void) {
 
 	if (script_byte_vars.rand_value >= 85)
 		num = 6;
-	else if (script_byte_vars.rand_value >= 170)
-		num = 7;
 	else
 		num = 35;
 


Commit: 44c539adf76a2d7027ec569d1dc80b1e5a61159c
    https://github.com/scummvm/scummvm/commit/44c539adf76a2d7027ec569d1dc80b1e5a61159c
Author: Ion Andrei Cristian (lecturatul2017 at gmail.com)
Date: 2026-07-17T20:39:49+02:00

Commit Message:
CHAMBER: Fix PVS-Studio dead-store and always-false findings

- cga.cpp blitToScreen: drop always-false endX_bytes > 80 clamp (dx/w are
  hardcoded to 0/320 above, so endX_bytes == 80) (V547)
- cga.cpp cga_ZoomInplace: drop dead ofs++ (ofs is reassigned to oofs on
  the next line) (V519)
- script.cpp SCR_23_HidePortrait: collapse the right_button branch; both
  arms were identical (this handler has no special effect to skip) (V523)
- script.cpp SCR_46_DeProfundisLowerHook: don't store the discarded
  getPuzzlSprite() return into sprofs (it is overwritten right after) (V519)
- script.cpp/menu.cpp: give the intentional empty busy-wait delay loops an
  explicit {} body instead of a bare ; (V529)

Changed paths:
    engines/chamber/cga.cpp
    engines/chamber/menu.cpp
    engines/chamber/script.cpp


diff --git a/engines/chamber/cga.cpp b/engines/chamber/cga.cpp
index 3f7416942c0..7f0059eabdc 100644
--- a/engines/chamber/cga.cpp
+++ b/engines/chamber/cga.cpp
@@ -229,9 +229,6 @@ void CGARenderer::blitToScreen(int16 dx, int16 dy, int16 w, int16 h) {
 	int16 startX_bytes = dx / 4;
 	int16 endX_bytes = (dx + w + 3) / 4;
 
-	if (endX_bytes > 80)
-		endX_bytes = 80;
-
 	for (int y = startY; y < endY; y++) {
 		uint16 bank = (y % 2) * 8192;
 		uint16 line = (y / 2) * 80;
@@ -1664,7 +1661,6 @@ void cga_ZoomInplace(zoom_t *params, byte tw, byte th, byte *source, byte *targe
 
 		/*right partial pixel*/
 		target[ofs] = (source[ofs] & ~(0xFF << (sc * 2))) | (pix << (sc * 2));
-		ofs++;
 		params->fw++;
 
 		/*ofs -= params->fw;*/
diff --git a/engines/chamber/menu.cpp b/engines/chamber/menu.cpp
index 8f1cfde8e83..625cac856fb 100644
--- a/engines/chamber/menu.cpp
+++ b/engines/chamber/menu.cpp
@@ -168,11 +168,11 @@ void actionsMenu(byte **pinfo) {
 
 		/*draw dot explosion animation*/
 		drawSpriteN(24, act_dot_rects[choice].sx, act_dot_rects[choice].sy, SCREENBUFFER);
-		for (i = 0; i < 0xFFF; i++) ; /*TODO: weak delay*/
+		for (i = 0; i < 0xFFF; i++) {} /*TODO: weak delay*/
 		drawSpriteN(2, act_dot_rects[choice].sx, act_dot_rects[choice].sy, SCREENBUFFER);
-		for (i = 0; i < 0xFFF; i++) ; /*TODO: weak delay*/
+		for (i = 0; i < 0xFFF; i++) {} /*TODO: weak delay*/
 		drawSpriteN(25, act_dot_rects[choice].sx, act_dot_rects[choice].sy, SCREENBUFFER);
-		for (i = 0; i < 0xFFF; i++) ; /*TODO: weak delay*/
+		for (i = 0; i < 0xFFF; i++) {} /*TODO: weak delay*/
 	}
 	g_vm->_renderer->restoreBackupImage(SCREENBUFFER);
 
diff --git a/engines/chamber/script.cpp b/engines/chamber/script.cpp
index 24cb8999689..0a4cce1a643 100644
--- a/engines/chamber/script.cpp
+++ b/engines/chamber/script.cpp
@@ -1921,11 +1921,6 @@ uint16 SCR_23_HidePortrait(void) {
 	index = *script_ptr++;
 
 	getDirtyRectAndFree(index, &kind, &x, &y, &width, &height, &offs);
-	if (right_button) {
-		g_vm->_renderer->copyScreenBlock(backbuffer, width, height, SCREENBUFFER, offs);
-		return 0;
-	}
-
 	g_vm->_renderer->copyScreenBlock(backbuffer, width, height, SCREENBUFFER, offs);
 
 	return 0;
@@ -2464,7 +2459,7 @@ uint16 SCR_46_DeProfundisLowerHook(void) {
 	script_ptr++;
 
 	/*draw Hook*/
-	sprofs = getPuzzlSprite(96, 140 / 4, 18, &w, &h, &ofs);
+	getPuzzlSprite(96, 140 / 4, 18, &w, &h, &ofs);
 
 	h = 1;
 	y = 15;
@@ -3007,7 +3002,7 @@ static void AnimSaucer(void) {
 		height_prev = height_new;
 
 		waitVBlank();
-		for (i = delay; i--;) ; /*TODO: weak delay*/
+		for (i = delay; i--;) {} /*TODO: weak delay*/
 		delay += 500;
 	}
 }


Commit: c11f6d3bb8018e52bee7d57efeca1547e7abc12c
    https://github.com/scummvm/scummvm/commit/c11f6d3bb8018e52bee7d57efeca1547e7abc12c
Author: Ion Andrei Cristian (lecturatul2017 at gmail.com)
Date: 2026-07-17T20:39:49+02:00

Commit Message:
CHAMBER: Save backbuffer so scripted room changes survive load

Scripts draw persistent room changes (alternate decor sets, extra
objects, animation finals) directly into the backbuffer; rebuilding
the room from the base decor on load discarded them, so e.g. the De
Profundis monster or Deilos would not reappear after loading. Bump
the save version to 2 and store zone_palette, the video mode and the
backbuffer contents; restore the backbuffer verbatim when the video
mode matches, falling back to the old redraw otherwise. Also reapply
the room palette on load and honor the launcher save_slot option.

Changed paths:
    engines/chamber/kult.cpp
    engines/chamber/saveload.cpp


diff --git a/engines/chamber/kult.cpp b/engines/chamber/kult.cpp
index fea21354a59..673d7899f98 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/file.h"
 #include "common/system.h"
@@ -580,6 +581,12 @@ Common::Error ChamberEngine::execute() {
 	/* Initialize cursor backup */
 	processInput();
 
+	if (ConfMan.hasKey("save_slot") && ConfMan.getInt("save_slot") >= 0) {
+		int slot = ConfMan.getInt("save_slot");
+		ConfMan.set("save_slot", "-1");
+		loadGameState(slot);
+	}
+
 #ifdef DEBUG_ENDING
 	script_byte_vars.game_paused = 5;
 	theEnd();
diff --git a/engines/chamber/saveload.cpp b/engines/chamber/saveload.cpp
index 0d571d03bb4..a6194ba550a 100644
--- a/engines/chamber/saveload.cpp
+++ b/engines/chamber/saveload.cpp
@@ -33,7 +33,7 @@
 
 namespace Chamber {
 
-static const byte kSaveVersion = 1;
+static const byte kSaveVersion = 2;
 
 // Serializes a pointer as a byte offset from base; 0xFFFF represents null
 template<typename T>
@@ -81,12 +81,23 @@ Common::Error ChamberEngine::loadGameStream(Common::SeekableReadStream *stream)
 	if (!s.syncVersion(kSaveVersion))
 		return Common::Error(Common::kReadingFailed, "Save from a newer engine version");
 	syncGameStream(s);
+	bool backbufferRestored = false;
+	if (s.getVersion() >= 2) {
+		s.syncAsByte(zone_palette);
+		byte mode = 0;
+		s.syncAsByte(mode);
+		s.syncBytes(backbuffer, sizeof(backbuffer));
+		backbufferRestored = (mode == (byte)_videoMode);
+	}
 	if (s.err())
 		return Common::kReadingFailed;
 	// Prevent door transition animation from firing on load (mirrors restartGame behavior)
 	script_byte_vars.cur_spot_flags = 0xFF;
-	memset(backbuffer, 0, sizeof(backbuffer));
-	drawRoomStatics();
+	if (!backbufferRestored) {
+		memset(backbuffer, 0, sizeof(backbuffer));
+		drawRoomStatics();
+	}
+	selectPalette();
 	backupSpotsImages();
 	drawPersons();
 	_renderer->backBufferToRealFull();
@@ -98,6 +109,10 @@ Common::Error ChamberEngine::saveGameStream(Common::WriteStream *stream, bool is
 	Common::Serializer s(nullptr, stream);
 	s.syncVersion(kSaveVersion);
 	syncGameStream(s);
+	s.syncAsByte(zone_palette);
+	byte mode = (byte)_videoMode;
+	s.syncAsByte(mode);
+	s.syncBytes(backbuffer, sizeof(backbuffer));
 	if (s.err())
 		return Common::kWritingFailed;
 	return Common::kNoError;


Commit: ef9ad271b061272eee66693b5af1696252b9801a
    https://github.com/scummvm/scummvm/commit/ef9ad271b061272eee66693b5af1696252b9801a
Author: Ion Andrei Cristian (lecturatul2017 at gmail.com)
Date: 2026-07-17T20:39:49+02:00

Commit Message:
CHAMBER: Address Amiga US PR review comments

Changed paths:
    engines/chamber/kult.cpp
    engines/chamber/menu.cpp
    engines/chamber/resdata.cpp
    engines/chamber/script.cpp


diff --git a/engines/chamber/kult.cpp b/engines/chamber/kult.cpp
index 673d7899f98..4297ee973c1 100644
--- a/engines/chamber/kult.cpp
+++ b/engines/chamber/kult.cpp
@@ -105,31 +105,7 @@ Graphics::Surface *loadSplash(const char *filename) {
 	return surface;
 }
 
-// TEMP headless debug helper, remove: dump current screen+palette as PPM
-void debugDumpScreen(const char *tag) {
-	if (gDebugLevel < 9)
-		return;
-	static int n = 0;
-	Graphics::Surface *s = g_system->lockScreen();
-	byte pal[768];
-	g_system->getPaletteManager()->grabPalette(pal, 0, 256);
-	Common::DumpFile f;
-	if (f.open(Common::Path(Common::String::format("/tmp/chamber_dbg/%03d_%s.ppm", n++, tag)))) {
-		f.writeString(Common::String::format("P6\n%d %d\n255\n", s->w, s->h));
-		for (int y = 0; y < s->h; y++)
-			for (int x = 0; x < s->w; x++) {
-				byte c = *(const byte *)s->getBasePtr(x, y);
-				f.writeByte(pal[c * 3]);
-				f.writeByte(pal[c * 3 + 1]);
-				f.writeByte(pal[c * 3 + 2]);
-			}
-	}
-	g_system->unlockScreen();
-}
-
 uint16 benchmarkCpu(void) {
-	if (gDebugLevel >= 9) // TEMP headless debug stub, remove
-		return 1000;
 	byte t;
 	uint16 cycles = 0;
 	for (t = script_byte_vars.timer_ticks; t == script_byte_vars.timer_ticks;) ;
@@ -430,8 +406,9 @@ Common::Error ChamberEngine::init() {
 		/* Load language selection screen */
 		Graphics::Surface *drap = loadSplash("DRAP.BIN");
 		if (!drap) {
+			warning("File DRAP.BIN not found");
 			_shouldQuit = true;
-			return Common::kNoError;
+			return Common::kReadingFailed;
 		}
 		drap->free();
 		delete drap;
diff --git a/engines/chamber/menu.cpp b/engines/chamber/menu.cpp
index 625cac856fb..2769342751c 100644
--- a/engines/chamber/menu.cpp
+++ b/engines/chamber/menu.cpp
@@ -19,6 +19,7 @@
  *
  */
 
+#include "common/system.h"
 #include "chamber/chamber.h"
 #include "chamber/common.h"
 #include "chamber/menu.h"
@@ -168,11 +169,11 @@ void actionsMenu(byte **pinfo) {
 
 		/*draw dot explosion animation*/
 		drawSpriteN(24, act_dot_rects[choice].sx, act_dot_rects[choice].sy, SCREENBUFFER);
-		for (i = 0; i < 0xFFF; i++) {} /*TODO: weak delay*/
+		g_system->delayMillis(50);
 		drawSpriteN(2, act_dot_rects[choice].sx, act_dot_rects[choice].sy, SCREENBUFFER);
-		for (i = 0; i < 0xFFF; i++) {} /*TODO: weak delay*/
+		g_system->delayMillis(50);
 		drawSpriteN(25, act_dot_rects[choice].sx, act_dot_rects[choice].sy, SCREENBUFFER);
-		for (i = 0; i < 0xFFF; i++) {} /*TODO: weak delay*/
+		g_system->delayMillis(50);
 	}
 	g_vm->_renderer->restoreBackupImage(SCREENBUFFER);
 
diff --git a/engines/chamber/resdata.cpp b/engines/chamber/resdata.cpp
index 1f0e7df3c22..8adf9a8eab4 100644
--- a/engines/chamber/resdata.cpp
+++ b/engines/chamber/resdata.cpp
@@ -333,6 +333,7 @@ Load strings data (obj. descriptions)
 */
 int16 loadDesciData(void) {
 	if (g_vm->getPlatform() == Common::kPlatformAmiga) {
+		// US embeds these banks in the exe (loaded by loadAmigaStaticData())
 		if (g_vm->getLanguage() == Common::EN_USA)
 			return 1;
 		return loadFilesList(res_desci_amiga);
@@ -357,6 +358,7 @@ Load strings data (dialogs)
 */
 int16 loadDialiData(void) {
 	if (g_vm->getPlatform() == Common::kPlatformAmiga) {
+		// US embeds these banks in the exe (loaded by loadAmigaStaticData())
 		if (g_vm->getLanguage() == Common::EN_USA)
 			return 1;
 		return loadFilesList(res_diali_amiga);
diff --git a/engines/chamber/script.cpp b/engines/chamber/script.cpp
index 0a4cce1a643..0d5cfead939 100644
--- a/engines/chamber/script.cpp
+++ b/engines/chamber/script.cpp
@@ -3002,7 +3002,7 @@ static void AnimSaucer(void) {
 		height_prev = height_new;
 
 		waitVBlank();
-		for (i = delay; i--;) {} /*TODO: weak delay*/
+		g_system->delayMillis(delay / 250);
 		delay += 500;
 	}
 }
@@ -4508,8 +4508,7 @@ uint16 RunScript(byte *code) {
 	while (script_ptr != script_end_ptr) {
 		byte opcode = *script_ptr;
 
-		if (gDebugLevel >= 9) // TEMP headless debug trace, remove
-			debug("scr %04X: %02X", (uint16)((script_ptr - templ_data) & 0xFFFF), opcode);
+		debug(9, "scr %04X: %02X", (uint16)((script_ptr - templ_data) & 0xFFFF), opcode);
 
 #ifdef DEBUG_SCRIPT
 		{




More information about the Scummvm-git-logs mailing list