[Scummvm-git-logs] scummvm master -> 406dba241029029329b626f569a4cc9403bb5246

sev- noreply at scummvm.org
Tue Apr 28 22:22:56 UTC 2026


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

Summary:
2f7f01f11c CHAMBER: Implement save/load game state serialization
23d7606abb CHAMBER: Connect in-game save/load stubs to ScummVM API
f1d988c6b8 CHAMBER: Address save/load review feedback and fix post-load rendering
3759c403f5 CHAMBER: Move save/load methods to savegame and remove commented code
406dba2410 CHAMBER: Rename savegame to saveload


Commit: 2f7f01f11c311fb4e008aa58a26f57e912ccf5d9
    https://github.com/scummvm/scummvm/commit/2f7f01f11c311fb4e008aa58a26f57e912ccf5d9
Author: Ion Andrei Cristian (lecturatul2017 at gmail.com)
Date: 2026-04-29T00:22:50+02:00

Commit Message:
CHAMBER: Implement save/load game state serialization

Implement syncGameStream() to serialize all game state using
Common::Serializer. Pointers are stored as uint16 byte offsets from
their base arrays (0xFFFF = null) via syncPtrOffset(). After loading,
reset cur_spot_flags to prevent a spurious door-open animation, redraw
the room, blit sprites, and flush the backbuffer to screen.

Changed paths:
    engines/chamber/chamber.cpp


diff --git a/engines/chamber/chamber.cpp b/engines/chamber/chamber.cpp
index 611df714263..31334e559dc 100644
--- a/engines/chamber/chamber.cpp
+++ b/engines/chamber/chamber.cpp
@@ -34,6 +34,11 @@
 
 #include "chamber/chamber.h"
 #include "chamber/renderer.h"
+#include "chamber/script.h"
+#include "chamber/resdata.h"
+#include "chamber/room.h"
+#include "chamber/dialog.h"
+#include "chamber/cga.h"
 
 namespace Chamber {
 
@@ -89,9 +94,24 @@ bool ChamberEngine::hasFeature(EngineFeature f) const {
 		(f == kSupportsSavingDuringRuntime);
 }
 
+// Serializes a pointer as a byte offset from base; 0xFFFF represents null
+template<typename T>
+static void syncPtrOffset(Common::Serializer &s, T *&ptr, void *base) {
+	uint16 ofs = s.isSaving() ? (ptr ? (uint16)((byte *)ptr - (byte *)base) : 0xFFFF) : 0;
+	s.syncAsUint16LE(ofs);
+	if (s.isLoading())
+		ptr = (ofs == 0xFFFF) ? nullptr : (T *)((byte *)base + ofs);
+}
+
 Common::Error ChamberEngine::loadGameStream(Common::SeekableReadStream *stream) {
 	Common::Serializer s(stream, nullptr);
 	syncGameStream(s);
+	// Prevent door transition animation from firing on load (mirrors restartGame behavior)
+	script_byte_vars.cur_spot_flags = 0xFF;
+	memset(backbuffer, 0, sizeof(backbuffer));
+	drawRoomStatics();
+	blitSpritesToBackBuffer();
+	_renderer->backBufferToRealFull();
 	return Common::kNoError;
 }
 
@@ -102,9 +122,82 @@ Common::Error ChamberEngine::saveGameStream(Common::WriteStream *stream, bool is
 }
 
 void ChamberEngine::syncGameStream(Common::Serializer &s) {
-	// Use methods of Serializer to save/load fields
-	int16 dummy = 0;
-	s.syncAsUint16LE(dummy);
+	s.syncBytes((byte *)&script_byte_vars, sizeof(script_byte_vars));
+	s.syncBytes((byte *)&script_word_vars, sizeof(script_word_vars));
+	s.syncBytes(zones_data, RES_ZONES_MAX);
+	s.syncBytes((byte *)inventory_items, sizeof(item_t) * MAX_INV_ITEMS);
+	s.syncBytes((byte *)pers_list, sizeof(pers_t) * PERS_MAX);
+	s.syncBytes((byte *)dirty_rects, sizeof(dirty_rect_t) * MAX_DIRTY_RECT);
+	s.syncBytes((byte *)menu_commands_12, sizeof(uint16) * SPECIAL_COMMANDS_MAX);
+	s.syncBytes((byte *)menu_commands_22, sizeof(uint16) * SPECIAL_COMMANDS_MAX);
+	s.syncBytes((byte *)menu_commands_24, sizeof(uint16) * SPECIAL_COMMANDS_MAX);
+	s.syncBytes((byte *)menu_commands_23, sizeof(uint16) * SPECIAL_COMMANDS_MAX);
+	s.syncBytes((byte *)&room_bounds_rect, sizeof(rect_t));
+
+	s.syncAsUint16LE(next_vorts_cmd);
+	s.syncAsUint16LE(next_vorts_ticks);
+	s.syncAsUint16LE(next_turkey_cmd);
+	s.syncAsUint16LE(next_turkey_ticks);
+	s.syncAsUint16LE(next_protozorqs_ticks);
+	s.syncAsUint16LE(fight_pers_ofs);
+	s.syncAsUint16LE(drops_cleanup_time);
+
+	s.syncAsByte(wait_delta);
+	s.syncAsByte(in_de_profundis);
+	s.syncAsByte(zone_name);
+	s.syncAsByte(room_hint_bar_width);
+	s.syncAsByte(room_hint_bar_coords_x);
+	s.syncAsByte(room_hint_bar_coords_y);
+	s.syncAsByte(last_object_hint);
+	s.syncAsByte(object_hint);
+	s.syncAsByte(command_hint);
+	s.syncAsByte(last_command_hint);
+	s.syncAsByte(zone_spr_index);
+	s.syncAsByte(zone_obj_count);
+
+	for (int i = 0; i < 2; i++) {
+		s.syncAsByte(the_wall_doors[i].height);
+		s.syncAsByte(the_wall_doors[i].width);
+		s.syncAsUint16LE(the_wall_doors[i].pitch);
+		s.syncAsUint16LE(the_wall_doors[i].offs);
+		syncPtrOffset(s, the_wall_doors[i].pixels, sprit_load_buffer);
+	}
+
+	syncPtrOffset(s, script_vars[kScrPool0_WordVars0], &script_word_vars);
+	syncPtrOffset(s, script_vars[kScrPool1_WordVars1], &script_word_vars);
+	syncPtrOffset(s, script_vars[kScrPool2_ByteVars], &script_byte_vars);
+	syncPtrOffset(s, script_vars[kScrPool3_CurrentItem], inventory_items);
+	syncPtrOffset(s, script_vars[kScrPool4_ZoneSpots], zones_data);
+	syncPtrOffset(s, script_vars[kScrPool5_Persons], pers_list);
+	syncPtrOffset(s, script_vars[kScrPool6_Inventory], inventory_items);
+	syncPtrOffset(s, script_vars[kScrPool7_Zapstiks], inventory_items);
+	syncPtrOffset(s, script_vars[kScrPool8_CurrentPers], pers_list);
+
+	for (int i = 0; i < MAX_SPRITES; i++)
+		syncPtrOffset(s, sprites_list[i], scratch_mem1);
+	for (int i = 0; i < MAX_DOORS; i++)
+		syncPtrOffset(s, doors_list[i], arpla_data);
+
+	syncPtrOffset(s, zone_spots, zones_data);
+	syncPtrOffset(s, zone_spots_end, zones_data);
+	syncPtrOffset(s, zone_spots_cur, zones_data);
+
+	syncPtrOffset(s, script_stack_ptr, script_stack);
+	for (int i = 0; i < 5 * 2; i++)
+		syncPtrOffset(s, script_stack[i], templ_data);
+
+	syncPtrOffset(s, vort_ptr, pers_list);
+	syncPtrOffset(s, vortanims_ptr, vortsanim_list);
+	syncPtrOffset(s, turkeyanims_ptr, turkeyanim_list);
+	syncPtrOffset(s, aspirant_ptr, pers_list);
+	syncPtrOffset(s, aspirant_spot, zones_data);
+	syncPtrOffset(s, found_spot, zones_data);
+	syncPtrOffset(s, spot_sprite, sprites_list);
+
+	uint16 timedSeqOfs = s.isSaving() ? (timed_seq_ptr ? (uint16)(timed_seq_ptr - patrol_route) : 0xFFFF) : 0;
+	s.syncAsUint16LE(timedSeqOfs);
+	if (s.isLoading())
+		timed_seq_ptr = (timedSeqOfs == 0xFFFF) ? nullptr : patrol_route + timedSeqOfs;
 }
 
 int ChamberEngine::getX(int original_x) {


Commit: 23d7606abbaf4fbb121bdee42de89aee71cd3570
    https://github.com/scummvm/scummvm/commit/23d7606abbaf4fbb121bdee42de89aee71cd3570
Author: Ion Andrei Cristian (lecturatul2017 at gmail.com)
Date: 2026-04-29T00:22:50+02:00

Commit Message:
CHAMBER: Connect in-game save/load stubs to ScummVM API

Replace the warning stubs in loadScena() and saveScena() with calls to
g_vm->loadGameState() and g_vm->saveGameState(), wiring the original
game's save/load triggers into the ScummVM save system.

Changed paths:
    engines/chamber/savegame.cpp


diff --git a/engines/chamber/savegame.cpp b/engines/chamber/savegame.cpp
index 699c5d37751..de505a8543a 100644
--- a/engines/chamber/savegame.cpp
+++ b/engines/chamber/savegame.cpp
@@ -588,8 +588,8 @@ error:;
 #endif
 
 int16 loadScena(void) {
-	warning("STUB: loadScena()");
-	return 1;
+	Common::Error err = g_vm->loadGameState(0);
+	return err.getCode() == Common::kNoError ? 0 : 1;
 #if 0
 	int16 f;
 	int16 res;
@@ -622,8 +622,8 @@ int16 loadScena(void) {
 }
 
 int16 saveScena(void) {
-	warning("STUB: saveScena()");
-	return 1;
+	Common::Error err = g_vm->saveGameState(0, Common::U32String("Save"));
+	return err.getCode() == Common::kNoError ? 0 : 1;
 #if 0
 	int16 f;
 	int16 res;


Commit: f1d988c6b817ba31f53bf78907d160847114ad33
    https://github.com/scummvm/scummvm/commit/f1d988c6b817ba31f53bf78907d160847114ad33
Author: Ion Andrei Cristian (lecturatul2017 at gmail.com)
Date: 2026-04-29T00:22:50+02:00

Commit Message:
CHAMBER: Address save/load review feedback and fix post-load rendering

Open the ScummVM save/load chooser instead of hardcoding slot 0, version
the savegame with syncVersion(), check stream errors, and serialize
small structs (item_t, pers_t, dirty_rect_t, rect_t) field-by-field so
the format is host-portable. After loading, replace the stale-buffer
blit with backupSpotsImages() + drawPersons() and refresh the cursor via
selectCursor(), so the loaded scene renders correctly and the mouse
pointer stays visible.

Changed paths:
    engines/chamber/chamber.cpp
    engines/chamber/savegame.cpp


diff --git a/engines/chamber/chamber.cpp b/engines/chamber/chamber.cpp
index 31334e559dc..22ce2f5c968 100644
--- a/engines/chamber/chamber.cpp
+++ b/engines/chamber/chamber.cpp
@@ -39,6 +39,7 @@
 #include "chamber/room.h"
 #include "chamber/dialog.h"
 #include "chamber/cga.h"
+#include "chamber/cursor.h"
 
 namespace Chamber {
 
@@ -94,6 +95,8 @@ bool ChamberEngine::hasFeature(EngineFeature f) const {
 		(f == kSupportsSavingDuringRuntime);
 }
 
+static const byte kSaveVersion = 1;
+
 // Serializes a pointer as a byte offset from base; 0xFFFF represents null
 template<typename T>
 static void syncPtrOffset(Common::Serializer &s, T *&ptr, void *base) {
@@ -103,21 +106,62 @@ static void syncPtrOffset(Common::Serializer &s, T *&ptr, void *base) {
 		ptr = (ofs == 0xFFFF) ? nullptr : (T *)((byte *)base + ofs);
 }
 
+static void syncItem(Common::Serializer &s, item_t &it) {
+	s.syncAsByte(it.flags);
+	s.syncAsByte(it.area);
+	s.syncAsByte(it.sprite);
+	s.syncAsByte(it.name);
+	s.syncAsUint16LE(it.command);
+}
+
+static void syncPers(Common::Serializer &s, pers_t &p) {
+	s.syncAsByte(p.area);
+	s.syncAsByte(p.flags);
+	s.syncAsByte(p.name);
+	s.syncAsByte(p.index);
+	s.syncAsByte(p.item);
+}
+
+static void syncDirtyRect(Common::Serializer &s, dirty_rect_t &dr) {
+	s.syncAsByte(dr.kind);
+	s.syncAsUint16LE(dr.offs);
+	s.syncAsByte(dr.height);
+	s.syncAsByte(dr.width);
+	s.syncAsByte(dr.y);
+	s.syncAsByte(dr.x);
+}
+
+static void syncRect(Common::Serializer &s, rect_t &r) {
+	s.syncAsByte(r.sx);
+	s.syncAsByte(r.ex);
+	s.syncAsByte(r.sy);
+	s.syncAsByte(r.ey);
+}
+
 Common::Error ChamberEngine::loadGameStream(Common::SeekableReadStream *stream) {
 	Common::Serializer s(stream, nullptr);
+	if (!s.syncVersion(kSaveVersion))
+		return Common::Error(Common::kReadingFailed, "Save from a newer engine version");
 	syncGameStream(s);
+	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();
-	blitSpritesToBackBuffer();
+	backupSpotsImages();
+	drawPersons();
 	_renderer->backBufferToRealFull();
+	_renderer->selectCursor(CURSOR_FINGER);
 	return Common::kNoError;
 }
 
 Common::Error ChamberEngine::saveGameStream(Common::WriteStream *stream, bool isAutosave) {
 	Common::Serializer s(nullptr, stream);
+	s.syncVersion(kSaveVersion);
 	syncGameStream(s);
+	if (s.err())
+		return Common::kWritingFailed;
 	return Common::kNoError;
 }
 
@@ -125,14 +169,21 @@ void ChamberEngine::syncGameStream(Common::Serializer &s) {
 	s.syncBytes((byte *)&script_byte_vars, sizeof(script_byte_vars));
 	s.syncBytes((byte *)&script_word_vars, sizeof(script_word_vars));
 	s.syncBytes(zones_data, RES_ZONES_MAX);
-	s.syncBytes((byte *)inventory_items, sizeof(item_t) * MAX_INV_ITEMS);
-	s.syncBytes((byte *)pers_list, sizeof(pers_t) * PERS_MAX);
-	s.syncBytes((byte *)dirty_rects, sizeof(dirty_rect_t) * MAX_DIRTY_RECT);
-	s.syncBytes((byte *)menu_commands_12, sizeof(uint16) * SPECIAL_COMMANDS_MAX);
-	s.syncBytes((byte *)menu_commands_22, sizeof(uint16) * SPECIAL_COMMANDS_MAX);
-	s.syncBytes((byte *)menu_commands_24, sizeof(uint16) * SPECIAL_COMMANDS_MAX);
-	s.syncBytes((byte *)menu_commands_23, sizeof(uint16) * SPECIAL_COMMANDS_MAX);
-	s.syncBytes((byte *)&room_bounds_rect, sizeof(rect_t));
+
+	for (int i = 0; i < MAX_INV_ITEMS; i++)
+		syncItem(s, inventory_items[i]);
+	for (int i = 0; i < PERS_MAX; i++)
+		syncPers(s, pers_list[i]);
+	for (int i = 0; i < MAX_DIRTY_RECT; i++)
+		syncDirtyRect(s, dirty_rects[i]);
+
+	for (int i = 0; i < SPECIAL_COMMANDS_MAX; i++) {
+		s.syncAsUint16LE(menu_commands_12[i]);
+		s.syncAsUint16LE(menu_commands_22[i]);
+		s.syncAsUint16LE(menu_commands_24[i]);
+		s.syncAsUint16LE(menu_commands_23[i]);
+	}
+	syncRect(s, room_bounds_rect);
 
 	s.syncAsUint16LE(next_vorts_cmd);
 	s.syncAsUint16LE(next_vorts_ticks);
diff --git a/engines/chamber/savegame.cpp b/engines/chamber/savegame.cpp
index de505a8543a..ada736e41ac 100644
--- a/engines/chamber/savegame.cpp
+++ b/engines/chamber/savegame.cpp
@@ -588,8 +588,7 @@ error:;
 #endif
 
 int16 loadScena(void) {
-	Common::Error err = g_vm->loadGameState(0);
-	return err.getCode() == Common::kNoError ? 0 : 1;
+	return g_vm->loadGameDialog() ? 0 : 1;
 #if 0
 	int16 f;
 	int16 res;
@@ -622,8 +621,7 @@ int16 loadScena(void) {
 }
 
 int16 saveScena(void) {
-	Common::Error err = g_vm->saveGameState(0, Common::U32String("Save"));
-	return err.getCode() == Common::kNoError ? 0 : 1;
+	return g_vm->saveGameDialog() ? 0 : 1;
 #if 0
 	int16 f;
 	int16 res;


Commit: 3759c403f51f594586effa57c6c169f277c09cfc
    https://github.com/scummvm/scummvm/commit/3759c403f51f594586effa57c6c169f277c09cfc
Author: Ion Andrei Cristian (lecturatul2017 at gmail.com)
Date: 2026-04-29T00:22:50+02:00

Commit Message:
CHAMBER: Move save/load methods to savegame and remove commented code

Move loadGameStream, saveGameStream, syncGameStream and helper functions
from chamber.cpp into savegame.cpp. Remove commented-out original
save/load implementation.

Changed paths:
    engines/chamber/chamber.cpp
    engines/chamber/savegame.cpp


diff --git a/engines/chamber/chamber.cpp b/engines/chamber/chamber.cpp
index 22ce2f5c968..012299811c4 100644
--- a/engines/chamber/chamber.cpp
+++ b/engines/chamber/chamber.cpp
@@ -95,162 +95,6 @@ bool ChamberEngine::hasFeature(EngineFeature f) const {
 		(f == kSupportsSavingDuringRuntime);
 }
 
-static const byte kSaveVersion = 1;
-
-// Serializes a pointer as a byte offset from base; 0xFFFF represents null
-template<typename T>
-static void syncPtrOffset(Common::Serializer &s, T *&ptr, void *base) {
-	uint16 ofs = s.isSaving() ? (ptr ? (uint16)((byte *)ptr - (byte *)base) : 0xFFFF) : 0;
-	s.syncAsUint16LE(ofs);
-	if (s.isLoading())
-		ptr = (ofs == 0xFFFF) ? nullptr : (T *)((byte *)base + ofs);
-}
-
-static void syncItem(Common::Serializer &s, item_t &it) {
-	s.syncAsByte(it.flags);
-	s.syncAsByte(it.area);
-	s.syncAsByte(it.sprite);
-	s.syncAsByte(it.name);
-	s.syncAsUint16LE(it.command);
-}
-
-static void syncPers(Common::Serializer &s, pers_t &p) {
-	s.syncAsByte(p.area);
-	s.syncAsByte(p.flags);
-	s.syncAsByte(p.name);
-	s.syncAsByte(p.index);
-	s.syncAsByte(p.item);
-}
-
-static void syncDirtyRect(Common::Serializer &s, dirty_rect_t &dr) {
-	s.syncAsByte(dr.kind);
-	s.syncAsUint16LE(dr.offs);
-	s.syncAsByte(dr.height);
-	s.syncAsByte(dr.width);
-	s.syncAsByte(dr.y);
-	s.syncAsByte(dr.x);
-}
-
-static void syncRect(Common::Serializer &s, rect_t &r) {
-	s.syncAsByte(r.sx);
-	s.syncAsByte(r.ex);
-	s.syncAsByte(r.sy);
-	s.syncAsByte(r.ey);
-}
-
-Common::Error ChamberEngine::loadGameStream(Common::SeekableReadStream *stream) {
-	Common::Serializer s(stream, nullptr);
-	if (!s.syncVersion(kSaveVersion))
-		return Common::Error(Common::kReadingFailed, "Save from a newer engine version");
-	syncGameStream(s);
-	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();
-	backupSpotsImages();
-	drawPersons();
-	_renderer->backBufferToRealFull();
-	_renderer->selectCursor(CURSOR_FINGER);
-	return Common::kNoError;
-}
-
-Common::Error ChamberEngine::saveGameStream(Common::WriteStream *stream, bool isAutosave) {
-	Common::Serializer s(nullptr, stream);
-	s.syncVersion(kSaveVersion);
-	syncGameStream(s);
-	if (s.err())
-		return Common::kWritingFailed;
-	return Common::kNoError;
-}
-
-void ChamberEngine::syncGameStream(Common::Serializer &s) {
-	s.syncBytes((byte *)&script_byte_vars, sizeof(script_byte_vars));
-	s.syncBytes((byte *)&script_word_vars, sizeof(script_word_vars));
-	s.syncBytes(zones_data, RES_ZONES_MAX);
-
-	for (int i = 0; i < MAX_INV_ITEMS; i++)
-		syncItem(s, inventory_items[i]);
-	for (int i = 0; i < PERS_MAX; i++)
-		syncPers(s, pers_list[i]);
-	for (int i = 0; i < MAX_DIRTY_RECT; i++)
-		syncDirtyRect(s, dirty_rects[i]);
-
-	for (int i = 0; i < SPECIAL_COMMANDS_MAX; i++) {
-		s.syncAsUint16LE(menu_commands_12[i]);
-		s.syncAsUint16LE(menu_commands_22[i]);
-		s.syncAsUint16LE(menu_commands_24[i]);
-		s.syncAsUint16LE(menu_commands_23[i]);
-	}
-	syncRect(s, room_bounds_rect);
-
-	s.syncAsUint16LE(next_vorts_cmd);
-	s.syncAsUint16LE(next_vorts_ticks);
-	s.syncAsUint16LE(next_turkey_cmd);
-	s.syncAsUint16LE(next_turkey_ticks);
-	s.syncAsUint16LE(next_protozorqs_ticks);
-	s.syncAsUint16LE(fight_pers_ofs);
-	s.syncAsUint16LE(drops_cleanup_time);
-
-	s.syncAsByte(wait_delta);
-	s.syncAsByte(in_de_profundis);
-	s.syncAsByte(zone_name);
-	s.syncAsByte(room_hint_bar_width);
-	s.syncAsByte(room_hint_bar_coords_x);
-	s.syncAsByte(room_hint_bar_coords_y);
-	s.syncAsByte(last_object_hint);
-	s.syncAsByte(object_hint);
-	s.syncAsByte(command_hint);
-	s.syncAsByte(last_command_hint);
-	s.syncAsByte(zone_spr_index);
-	s.syncAsByte(zone_obj_count);
-
-	for (int i = 0; i < 2; i++) {
-		s.syncAsByte(the_wall_doors[i].height);
-		s.syncAsByte(the_wall_doors[i].width);
-		s.syncAsUint16LE(the_wall_doors[i].pitch);
-		s.syncAsUint16LE(the_wall_doors[i].offs);
-		syncPtrOffset(s, the_wall_doors[i].pixels, sprit_load_buffer);
-	}
-
-	syncPtrOffset(s, script_vars[kScrPool0_WordVars0], &script_word_vars);
-	syncPtrOffset(s, script_vars[kScrPool1_WordVars1], &script_word_vars);
-	syncPtrOffset(s, script_vars[kScrPool2_ByteVars], &script_byte_vars);
-	syncPtrOffset(s, script_vars[kScrPool3_CurrentItem], inventory_items);
-	syncPtrOffset(s, script_vars[kScrPool4_ZoneSpots], zones_data);
-	syncPtrOffset(s, script_vars[kScrPool5_Persons], pers_list);
-	syncPtrOffset(s, script_vars[kScrPool6_Inventory], inventory_items);
-	syncPtrOffset(s, script_vars[kScrPool7_Zapstiks], inventory_items);
-	syncPtrOffset(s, script_vars[kScrPool8_CurrentPers], pers_list);
-
-	for (int i = 0; i < MAX_SPRITES; i++)
-		syncPtrOffset(s, sprites_list[i], scratch_mem1);
-	for (int i = 0; i < MAX_DOORS; i++)
-		syncPtrOffset(s, doors_list[i], arpla_data);
-
-	syncPtrOffset(s, zone_spots, zones_data);
-	syncPtrOffset(s, zone_spots_end, zones_data);
-	syncPtrOffset(s, zone_spots_cur, zones_data);
-
-	syncPtrOffset(s, script_stack_ptr, script_stack);
-	for (int i = 0; i < 5 * 2; i++)
-		syncPtrOffset(s, script_stack[i], templ_data);
-
-	syncPtrOffset(s, vort_ptr, pers_list);
-	syncPtrOffset(s, vortanims_ptr, vortsanim_list);
-	syncPtrOffset(s, turkeyanims_ptr, turkeyanim_list);
-	syncPtrOffset(s, aspirant_ptr, pers_list);
-	syncPtrOffset(s, aspirant_spot, zones_data);
-	syncPtrOffset(s, found_spot, zones_data);
-	syncPtrOffset(s, spot_sprite, sprites_list);
-
-	uint16 timedSeqOfs = s.isSaving() ? (timed_seq_ptr ? (uint16)(timed_seq_ptr - patrol_route) : 0xFFFF) : 0;
-	s.syncAsUint16LE(timedSeqOfs);
-	if (s.isLoading())
-		timed_seq_ptr = (timedSeqOfs == 0xFFFF) ? nullptr : patrol_route + timedSeqOfs;
-}
-
 int ChamberEngine::getX(int original_x) {
 	return original_x;
 }
diff --git a/engines/chamber/savegame.cpp b/engines/chamber/savegame.cpp
index ada736e41ac..28493dba9f0 100644
--- a/engines/chamber/savegame.cpp
+++ b/engines/chamber/savegame.cpp
@@ -27,671 +27,181 @@
 #include "chamber/cga.h"
 #include "chamber/room.h"
 #include "chamber/dialog.h"
-#include "chamber/room.h"
+#include "chamber/cursor.h"
 
 namespace Chamber {
 
-#if 0
-static const char restart_name[] = "CLEARx.BIN";
-
-#ifdef VERSION_USA
-static const char savegame_name[] = "SCENACx.BIN";
-#else
-static const char savegame_name[] = "SCENAx.BIN";
-#endif
-#endif
-
-#ifdef VERSION_USA
-
-#define CGA_SAVE_BEG_OFS 0x769B
-#define CGA_SAVE_END_OFS 0x9EDA
-
-#define CGA_SAVE_WORD_VARS_OFS 0x79C1
-#define CGA_SAVE_BYTE_VARS_OFS 0x7B33
-#define CGA_SAVE_INVENTORY_OFS 0x7762
-#define CGA_SAVE_ZONES_OFS     0x7BA4
-#define CGA_SAVE_PERS_OFS      0x78E2
-#define CGA_SAVE_STACK_OFS     0x76DF
-#define CGA_SAVE_SPRITES_OFS   0x5264
-#define CGA_SAVE_SPRLIST_OFS   0x76AD
-#define CGA_SAVE_SPRBUFF_OFS   0x4C68
-#define CGA_SAVE_ARPLA_OFS     0x26F6
-#define CGA_SAVE_SCRSTACK_OFS  0x76DF
-#define CGA_SAVE_TEMPL_OFS     0x1AC7
-#define CGA_SAVE_VORTANIMS_OFS 0xA7B7
-#define CGA_SAVE_TURKEYANIMS_OFS 0xA8FC
-#define CGA_SAVE_TIMEDSEQ_OFS  0xA96E
-
-#else
-
-#define CGA_SAVE_BEG_OFS 0x751E
-#define CGA_SAVE_END_OFS 0x9D5D
-
-#define CGA_SAVE_WORD_VARS_OFS 0x7844
-#define CGA_SAVE_BYTE_VARS_OFS 0x79B6
-#define CGA_SAVE_INVENTORY_OFS 0x75E5
-#define CGA_SAVE_ZONES_OFS     0x7A27
-#define CGA_SAVE_PERS_OFS      0x7765
-#define CGA_SAVE_STACK_OFS     0x7562
-#define CGA_SAVE_SPRITES_OFS   0x5264
-#define CGA_SAVE_SPRLIST_OFS   0x7530
-#define CGA_SAVE_SPRBUFF_OFS   0x4C68
-#define CGA_SAVE_ARPLA_OFS     0x26F6
-#define CGA_SAVE_SCRSTACK_OFS  0x7562
-#define CGA_SAVE_TEMPL_OFS     0x182C
-#define CGA_SAVE_VORTANIMS_OFS 0xA609
-#define CGA_SAVE_TURKEYANIMS_OFS 0xA74E
-#define CGA_SAVE_TIMEDSEQ_OFS  0xA7C0
-
-#endif
-
-#define SAVEADDR(value, base, nativesize, origsize, origbase)   \
-	((value) ? LE16(((((byte*)(value)) - (byte*)(base)) / nativesize) * origsize + origbase) : 0)
-
-#define LOADADDR(value, base, nativesize, origsize, origbase)   \
-	((value) ? ((((LE16(value)) - (origbase)) / origsize) * nativesize + (byte*)base) : 0)
-
-#define WRITE(buffer, size) \
-	wlen = write(f, buffer, size); if(wlen != size) goto error;
-
-#define READ(buffer, size) \
-	rlen = read(f, buffer, size); if(rlen != size) goto error;
-
-#if 0
-int16 readSaveData(int16 f, int16 clean) {
-	int16 rlen;
-	uint16 zero = 0;
-	byte *p;
-	int16 i;
-
-#define BYTES(buffer, size) READ(buffer, size)
-#define UBYTE(variable) { byte temp_v; READ(&temp_v, 1); variable = temp_v; }
-#define SBYTE(variable) { int8 temp_v; READ(&temp_v, 1); variable = temp_v; }
-#define USHORT(variable) { uint16 temp_v; READ(&temp_v, 2); variable = temp_v; }
-#define SSHORT(variable) { int16 temp_v; READ(&temp_v, 2); variable = temp_v; }
-#define POINTER(variable, base, nativesize, origsize, origbase) \
-	{ int16 temp_v; READ(&temp_v, 2); variable = LOADADDR(temp_v, base, nativesize, origsize, origbase); }
-
-	/*script_vars pointers*/
-	POINTER(script_vars[ScrPool0_WordVars0], &script_word_vars, 2, 2, CGA_SAVE_WORD_VARS_OFS);
-	POINTER(script_vars[ScrPool1_WordVars1], &script_word_vars, 2, 2, CGA_SAVE_WORD_VARS_OFS);
-	POINTER(script_vars[ScrPool2_ByteVars], &script_byte_vars, 1, 1, CGA_SAVE_BYTE_VARS_OFS);
-	POINTER(script_vars[ScrPool3_CurrentItem], inventory_items, sizeof(item_t), sizeof(item_t), CGA_SAVE_INVENTORY_OFS);
-	POINTER(script_vars[ScrPool4_ZoneSpots], zones_data, 1, 1, CGA_SAVE_ZONES_OFS);
-	POINTER(script_vars[ScrPool5_Persons], pers_list, 1, 1, CGA_SAVE_PERS_OFS);
-	POINTER(script_vars[ScrPool6_Inventory], inventory_items, sizeof(item_t), sizeof(item_t), CGA_SAVE_INVENTORY_OFS);
-	POINTER(script_vars[ScrPool7_Zapstiks], inventory_items, sizeof(item_t), sizeof(item_t), CGA_SAVE_INVENTORY_OFS);
-	POINTER(script_vars[ScrPool8_CurrentPers], pers_list, 1, 1, CGA_SAVE_PERS_OFS);
-
-	/* sprites_list */
-	for (i = 0; i < MAX_SPRITES; i++) {
-		POINTER(sprites_list[i], scratch_mem1, 1, 1, CGA_SAVE_SPRITES_OFS);
-	}
-
-	/* doors list */
-	for (i = 0; i < MAX_DOORS; i++) {
-		POINTER(doors_list[i], arpla_data, 1, 1, CGA_SAVE_ARPLA_OFS);
-	}
-
-	/* zone_spots */
-	POINTER((byte *)zone_spots, zones_data, 1, 1, CGA_SAVE_ZONES_OFS);
-
-	/* zone_spots_end */
-	POINTER((byte *)zone_spots_end, zones_data, 1, 1, CGA_SAVE_ZONES_OFS);
-
-	/* zone_spots_cur */
-	POINTER((byte *)zone_spots_cur, zones_data, 1, 1, CGA_SAVE_ZONES_OFS);
-
-	/* script_stack_ptr */
-	/*TODO: FIX ME: original stack works in reverse order (from higher address to lower)*/
-	POINTER((byte *)script_stack_ptr, script_stack, 1, 1, CGA_SAVE_SCRSTACK_OFS);
-
-	/* script_stack */
-	/*TODO: FIX ME: original stack works in reverse order (from higher address to lower)*/
-	for (i = 0; i < 5 * 2; i++) {
-		POINTER(script_stack[i], templ_data, 1, 1, CGA_SAVE_TEMPL_OFS);
-	}
-
-	/* padding */
-	USHORT(zero);
-
-	/* vort_ptr */
-	POINTER((byte *)vort_ptr, pers_list, 1, 1, CGA_SAVE_PERS_OFS);
-
-	/* vortanims_ptr */
-	POINTER((byte *)vortanims_ptr, vortsanim_list, 1, 1, CGA_SAVE_VORTANIMS_OFS);
-
-	/* turkeyanims_ptr */
-	POINTER((byte *)turkeyanims_ptr, turkeyanim_list, 1, 1, CGA_SAVE_TURKEYANIMS_OFS);
-
-	/* aspirant_ptr */
-	POINTER((byte *)aspirant_ptr, pers_list, 1, 1, CGA_SAVE_PERS_OFS);
-
-	/* aspirant_spot */
-	POINTER((byte *)aspirant_spot, zones_data, 1, 1, CGA_SAVE_ZONES_OFS);
-
-	/* found_spot */
-	POINTER((byte *)found_spot, zones_data, 1, 1, CGA_SAVE_ZONES_OFS);
-
-	/* spot_sprite */
-	POINTER((byte *)spot_sprite, sprites_list, sizeof(sprites_list[0]), 2, CGA_SAVE_SPRLIST_OFS);
-
-	/* timed_seq_ptr */
-	POINTER(timed_seq_ptr, patrol_route, 1, 1, CGA_SAVE_TIMEDSEQ_OFS);
-
-	/* keep_sp */
-	/* TODO: how to save it? but it's probably useless anyway */
-	USHORT(zero);
-
-	/* unused ptr to script code */
-	p = templ_data;
-	POINTER(p, templ_data, 1, 1, CGA_SAVE_TEMPL_OFS);
-
-	/* padding */
-	UBYTE(zero);
-
-	/* the wall doors state */
-	for (i = 0; i < 2; i++) {
-		thewalldoor_t *door = &the_wall_doors[i];
-		UBYTE(door->height);
-		UBYTE(door->width);
-		USHORT(door->pitch);
-		USHORT(door->offs);
-		POINTER(door->pixels, sprit_load_buffer, 1, 1, CGA_SAVE_SPRBUFF_OFS);
-	}
-
-	/* wait_delta */
-	UBYTE(wait_delta);
-
-	/* padding */
-	UBYTE(zero);
-
-	/* dirty_rects */
-	for (i = 0; i < MAX_DIRTY_RECT; i++) {
-		dirty_rect_t *dr = &dirty_rects[i];
-		UBYTE(dr->kind);
-		USHORT(dr->offs);
-		UBYTE(dr->height);
-		UBYTE(dr->width);
-		UBYTE(dr->y);
-		UBYTE(dr->x);
-	}
-
-	/* inventory_items */
-	for (i = 0; i < MAX_INV_ITEMS; i++) {
-		/*TODO: properly serialize this*/
-		BYTES(&inventory_items[i], sizeof(item_t));
-	}
-
-	/* room_hint_bar_coords_y */
-	UBYTE(room_hint_bar_coords_y);
-
-	/* room_hint_bar_coords_x */
-	UBYTE(room_hint_bar_coords_x);
-
-	/* padding */
-	USHORT(zero);
-
-	/* fight_pers_ofs */
-	/* NB! raw offset */
-	USHORT(fight_pers_ofs);
-
-	/* pers_list */
-	for (i = 0; i < PERS_MAX; i++) {
-		/*TODO: properly serialize this*/
-		BYTES(&pers_list[i], sizeof(pers_t));
-	}
-
-	/* drops_cleanup_time */
-	USHORT(drops_cleanup_time);
-
-	/* room_bounds_rect */
-	/*TODO: properly serialize this*/
-	BYTES(&room_bounds_rect, sizeof(rect_t));
-
-	/* last_object_hint */
-	UBYTE(last_object_hint);
-
-	/* object_hint */
-	UBYTE(object_hint);
-
-	/* command_hint */
-	UBYTE(command_hint);
-
-	/* zone_name */
-	UBYTE(zone_name);
-
-	/* room_hint_bar_width */
-	UBYTE(room_hint_bar_width);
-
-	/* last_command_hint */
-	UBYTE(last_command_hint);
-
-	/* zone_spr_index */
-	UBYTE(zone_spr_index);
-
-	/* zone_obj_count */
-	UBYTE(zone_obj_count);
-
-	/* padding */
-	USHORT(zero);
-
-	/* padding */
-	UBYTE(zero);
-
-	/* in_de_profundis */
-	UBYTE(in_de_profundis);
-
-	/* script_word_vars */
-	BYTES(&script_word_vars, sizeof(script_word_vars));
-
-	/* menu_commands_12 */
-	BYTES(menu_commands_12, sizeof(menu_commands_12));
-
-	/* menu_commands_22 */
-	BYTES(menu_commands_22, sizeof(menu_commands_22));
+static const byte kSaveVersion = 1;
 
-	/* menu_commands_24 */
-	BYTES(menu_commands_24, sizeof(menu_commands_24));
-
-	/* menu_commands_23 */
-	BYTES(menu_commands_23, sizeof(menu_commands_23));
-
-	/* next_vorts_cmd */
-	USHORT(next_vorts_cmd);
-
-	/* next_vorts_ticks */
-	USHORT(next_vorts_ticks);
-
-	/* next_turkey_cmd */
-	USHORT(next_turkey_cmd);
-
-	/* next_turkey_ticks */
-	USHORT(next_turkey_ticks);
-
-	/* next_protozorqs_ticks */
-	USHORT(next_protozorqs_ticks);
-
-	/* padding */
-	for (i = 0; i < 7; i++) USHORT(zero);
-
-	/* script_byte_vars */
-	BYTES(&script_byte_vars, sizeof(script_byte_vars));
-
-	/* zones_data */
-	BYTES(zones_data, RES_ZONES_MAX);
-
-	if (clean == 0) {
-		/* screen data */
-		BYTES(backbuffer, 0x3FFF);
-
-		CGA_BackBufferToRealFull();
-		SelectPalette();
-
-		BYTES(backbuffer, 0x3FFF);
-	}
-
-#undef BYTES
-#undef UBYTE
-#undef SBYTE
-#undef USHORT
-#undef SSHORT
-#undef POINTER
-
-	return 0;
-
-error:;
-	return 1;
+// Serializes a pointer as a byte offset from base; 0xFFFF represents null
+template<typename T>
+static void syncPtrOffset(Common::Serializer &s, T *&ptr, void *base) {
+	uint16 ofs = s.isSaving() ? (ptr ? (uint16)((byte *)ptr - (byte *)base) : 0xFFFF) : 0;
+	s.syncAsUint16LE(ofs);
+	if (s.isLoading())
+		ptr = (ofs == 0xFFFF) ? nullptr : (T *)((byte *)base + ofs);
 }
 
-int16 writeSaveData(int16 f, int16 clean) {
-	int16 wlen;
-	uint16 zero = 0;
-	byte *p;
-	int16 i;
-
-#define BYTES(buffer, size) WRITE(buffer, size)
-#define UBYTE(variable) { byte temp_v = variable; WRITE(&temp_v, 1); }
-#define SBYTE(variable) { int8 temp_v = variable; WRITE(&temp_v, 1); }
-#define USHORT(variable) { uint16 temp_v = variable; WRITE(&temp_v, 2); }
-#define SSHORT(variable) { int16 temp_v = variable; WRITE(&temp_v, 2); }
-#define POINTER(variable, base, nativesize, origsize, origbase) \
-	{ int16 temp_v = SAVEADDR(variable, base, nativesize, origsize, origbase); WRITE(&temp_v, 2); }
-
-	/*script_vars pointers*/
-	POINTER(script_vars[ScrPool0_WordVars0], &script_word_vars, 2, 2, CGA_SAVE_WORD_VARS_OFS);
-	POINTER(script_vars[ScrPool1_WordVars1], &script_word_vars, 2, 2, CGA_SAVE_WORD_VARS_OFS);
-	POINTER(script_vars[ScrPool2_ByteVars], &script_byte_vars, 1, 1, CGA_SAVE_BYTE_VARS_OFS);
-	POINTER(script_vars[ScrPool3_CurrentItem], inventory_items, sizeof(item_t), sizeof(item_t), CGA_SAVE_INVENTORY_OFS);
-	POINTER(script_vars[ScrPool4_ZoneSpots], zones_data, 1, 1, CGA_SAVE_ZONES_OFS);
-	POINTER(script_vars[ScrPool5_Persons], pers_list, 1, 1, CGA_SAVE_PERS_OFS);
-	POINTER(script_vars[ScrPool6_Inventory], inventory_items, sizeof(item_t), sizeof(item_t), CGA_SAVE_INVENTORY_OFS);
-	POINTER(script_vars[ScrPool7_Zapstiks], inventory_items, sizeof(item_t), sizeof(item_t), CGA_SAVE_INVENTORY_OFS);
-	POINTER(script_vars[ScrPool8_CurrentPers], pers_list, 1, 1, CGA_SAVE_PERS_OFS);
-
-	/* sprites_list */
-	for (i = 0; i < MAX_SPRITES; i++) {
-		POINTER(sprites_list[i], scratch_mem1, 1, 1, CGA_SAVE_SPRITES_OFS);
-	}
-
-	/* doors list */
-	for (i = 0; i < MAX_DOORS; i++) {
-		POINTER(doors_list[i], arpla_data, 1, 1, CGA_SAVE_ARPLA_OFS);
-	}
-
-	/* zone_spots */
-	POINTER(zone_spots, zones_data, 1, 1, CGA_SAVE_ZONES_OFS);
-
-	/* zone_spots_end */
-	POINTER(zone_spots_end, zones_data, 1, 1, CGA_SAVE_ZONES_OFS);
-
-	/* zone_spots_cur */
-	POINTER(zone_spots_cur, zones_data, 1, 1, CGA_SAVE_ZONES_OFS);
-
-	/* script_stack_ptr */
-	/*TODO: FIX ME: original stack works in reverse order (from higher address to lower)*/
-	POINTER(script_stack_ptr, script_stack, 1, 1, CGA_SAVE_SCRSTACK_OFS);
-
-	/* script_stack */
-	/*TODO: FIX ME: original stack works in reverse order (from higher address to lower)*/
-	for (i = 0; i < 5 * 2; i++) {
-		POINTER(script_stack[i], templ_data, 1, 1, CGA_SAVE_TEMPL_OFS);
-	}
-
-	/* padding */
-	USHORT(zero);
-
-	/* vort_ptr */
-	POINTER(vort_ptr, pers_list, 1, 1, CGA_SAVE_PERS_OFS);
-
-	/* vortanims_ptr */
-	POINTER(vortanims_ptr, vortsanim_list, 1, 1, CGA_SAVE_VORTANIMS_OFS);
-
-	/* turkeyanims_ptr */
-	POINTER(turkeyanims_ptr, turkeyanim_list, 1, 1, CGA_SAVE_TURKEYANIMS_OFS);
-
-	/* aspirant_ptr */
-	POINTER(aspirant_ptr, pers_list, 1, 1, CGA_SAVE_PERS_OFS);
-
-	/* aspirant_spot */
-	POINTER(aspirant_spot, zones_data, 1, 1, CGA_SAVE_ZONES_OFS);
-
-	/* found_spot */
-	POINTER(found_spot, zones_data, 1, 1, CGA_SAVE_ZONES_OFS);
-
-	/* spot_sprite */
-	POINTER(spot_sprite, sprites_list, sizeof(sprites_list[0]), 2, CGA_SAVE_SPRLIST_OFS);
-
-	/* timed_seq_ptr */
-	POINTER(timed_seq_ptr, patrol_route, 1, 1, CGA_SAVE_TIMEDSEQ_OFS);
-
-	/* keep_sp */
-	/* TODO: how to save it? but it's probably useless anyway */
-	USHORT(zero);
-
-	/* unused ptr to script code */
-	p = templ_data;
-	POINTER(p, templ_data, 1, 1, CGA_SAVE_TEMPL_OFS);
-
-	/* padding */
-	UBYTE(zero);
-
-	/* the wall doors state */
-	for (i = 0; i < 2; i++) {
-		thewalldoor_t *door = &the_wall_doors[i];
-		UBYTE(door->height);
-		UBYTE(door->width);
-		USHORT(door->pitch);
-		USHORT(door->offs);
-		POINTER(door->pixels, sprit_load_buffer, 1, 1, CGA_SAVE_SPRBUFF_OFS);
-	}
-
-	/* wait_delta */
-	UBYTE(wait_delta);
-
-	/* padding */
-	UBYTE(zero);
-
-	/* dirty_rects */
-	for (i = 0; i < MAX_DIRTY_RECT; i++) {
-		dirty_rect_t *dr = &dirty_rects[i];
-		UBYTE(dr->kind);
-		USHORT(dr->offs);
-		UBYTE(dr->height);
-		UBYTE(dr->width);
-		UBYTE(dr->y);
-		UBYTE(dr->x);
-	}
-
-	/* inventory_items */
-	for (i = 0; i < MAX_INV_ITEMS; i++) {
-		/*TODO: properly serialize this*/
-		BYTES(&inventory_items[i], sizeof(item_t));
-	}
-
-	/* room_hint_bar_coords_y */
-	UBYTE(room_hint_bar_coords_y);
-
-	/* room_hint_bar_coords_x */
-	UBYTE(room_hint_bar_coords_x);
-
-	/* padding */
-	USHORT(zero);
-
-	/* fight_pers_ofs */
-	/* NB! raw offset */
-	USHORT(fight_pers_ofs);
-
-	/* pers_list */
-	for (i = 0; i < PERS_MAX; i++) {
-		/*TODO: properly serialize this*/
-		BYTES(&pers_list[i], sizeof(pers_t));
-	}
-
-	/* drops_cleanup_time */
-	USHORT(drops_cleanup_time);
-
-	/* room_bounds_rect */
-	/*TODO: properly serialize this*/
-	BYTES(&room_bounds_rect, sizeof(rect_t));
-
-	/* last_object_hint */
-	UBYTE(last_object_hint);
-
-	/* object_hint */
-	UBYTE(object_hint);
-
-	/* command_hint */
-	UBYTE(command_hint);
-
-	/* zone_name */
-	UBYTE(zone_name);
-
-	/* room_hint_bar_width */
-	UBYTE(room_hint_bar_width);
-
-	/* last_command_hint */
-	UBYTE(last_command_hint);
-
-	/* zone_spr_index */
-	UBYTE(zone_spr_index);
-
-	/* zone_obj_count */
-	UBYTE(zone_obj_count);
-
-	/* padding */
-	USHORT(zero);
-
-	/* padding */
-	UBYTE(zero);
-
-	/* in_de_profundis */
-	UBYTE(in_de_profundis);
-
-	/* script_word_vars */
-	BYTES(&script_word_vars, sizeof(script_word_vars));
-
-	/* menu_commands_12 */
-	BYTES(menu_commands_12, sizeof(menu_commands_12));
-
-	/* menu_commands_22 */
-	BYTES(menu_commands_22, sizeof(menu_commands_22));
-
-	/* menu_commands_24 */
-	BYTES(menu_commands_24, sizeof(menu_commands_24));
-
-	/* menu_commands_23 */
-	BYTES(menu_commands_23, sizeof(menu_commands_23));
-
-	/* next_vorts_cmd */
-	USHORT(next_vorts_cmd);
-
-	/* next_vorts_ticks */
-	USHORT(next_vorts_ticks);
-
-	/* next_turkey_cmd */
-	USHORT(next_turkey_cmd);
-
-	/* next_turkey_ticks */
-	USHORT(next_turkey_ticks);
-
-	/* next_protozorqs_ticks */
-	USHORT(next_protozorqs_ticks);
-
-	/* padding */
-	for (i = 0; i < 7; i++) USHORT(zero);
+static void syncItem(Common::Serializer &s, item_t &it) {
+	s.syncAsByte(it.flags);
+	s.syncAsByte(it.area);
+	s.syncAsByte(it.sprite);
+	s.syncAsByte(it.name);
+	s.syncAsUint16LE(it.command);
+}
 
-	/* script_byte_vars */
-	BYTES(&script_byte_vars, sizeof(script_byte_vars));
+static void syncPers(Common::Serializer &s, pers_t &p) {
+	s.syncAsByte(p.area);
+	s.syncAsByte(p.flags);
+	s.syncAsByte(p.name);
+	s.syncAsByte(p.index);
+	s.syncAsByte(p.item);
+}
 
-	/* zones_data */
-	BYTES(zones_data, RES_ZONES_MAX);
+static void syncDirtyRect(Common::Serializer &s, dirty_rect_t &dr) {
+	s.syncAsByte(dr.kind);
+	s.syncAsUint16LE(dr.offs);
+	s.syncAsByte(dr.height);
+	s.syncAsByte(dr.width);
+	s.syncAsByte(dr.y);
+	s.syncAsByte(dr.x);
+}
 
-	if (clean == 0) {
-		/* screen data */
-		BYTES(frontbuffer, 0x3FFF);
-		BYTES(backbuffer, 0x3FFF);
-	}
+static void syncRect(Common::Serializer &s, rect_t &r) {
+	s.syncAsByte(r.sx);
+	s.syncAsByte(r.ex);
+	s.syncAsByte(r.sy);
+	s.syncAsByte(r.ey);
+}
 
-#undef BYTES
-#undef UBYTE
-#undef SBYTE
-#undef USHORT
-#undef SSHORT
-#undef POINTER
+Common::Error ChamberEngine::loadGameStream(Common::SeekableReadStream *stream) {
+	Common::Serializer s(stream, nullptr);
+	if (!s.syncVersion(kSaveVersion))
+		return Common::Error(Common::kReadingFailed, "Save from a newer engine version");
+	syncGameStream(s);
+	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();
+	backupSpotsImages();
+	drawPersons();
+	_renderer->backBufferToRealFull();
+	_renderer->selectCursor(CURSOR_FINGER);
+	return Common::kNoError;
+}
 
-	return 0;
+Common::Error ChamberEngine::saveGameStream(Common::WriteStream *stream, bool isAutosave) {
+	Common::Serializer s(nullptr, stream);
+	s.syncVersion(kSaveVersion);
+	syncGameStream(s);
+	if (s.err())
+		return Common::kWritingFailed;
+	return Common::kNoError;
+}
 
-error:;
-	return 1;
+void ChamberEngine::syncGameStream(Common::Serializer &s) {
+	s.syncBytes((byte *)&script_byte_vars, sizeof(script_byte_vars));
+	s.syncBytes((byte *)&script_word_vars, sizeof(script_word_vars));
+	s.syncBytes(zones_data, RES_ZONES_MAX);
+
+	for (int i = 0; i < MAX_INV_ITEMS; i++)
+		syncItem(s, inventory_items[i]);
+	for (int i = 0; i < PERS_MAX; i++)
+		syncPers(s, pers_list[i]);
+	for (int i = 0; i < MAX_DIRTY_RECT; i++)
+		syncDirtyRect(s, dirty_rects[i]);
+
+	for (int i = 0; i < SPECIAL_COMMANDS_MAX; i++) {
+		s.syncAsUint16LE(menu_commands_12[i]);
+		s.syncAsUint16LE(menu_commands_22[i]);
+		s.syncAsUint16LE(menu_commands_24[i]);
+		s.syncAsUint16LE(menu_commands_23[i]);
+	}
+	syncRect(s, room_bounds_rect);
+
+	s.syncAsUint16LE(next_vorts_cmd);
+	s.syncAsUint16LE(next_vorts_ticks);
+	s.syncAsUint16LE(next_turkey_cmd);
+	s.syncAsUint16LE(next_turkey_ticks);
+	s.syncAsUint16LE(next_protozorqs_ticks);
+	s.syncAsUint16LE(fight_pers_ofs);
+	s.syncAsUint16LE(drops_cleanup_time);
+
+	s.syncAsByte(wait_delta);
+	s.syncAsByte(in_de_profundis);
+	s.syncAsByte(zone_name);
+	s.syncAsByte(room_hint_bar_width);
+	s.syncAsByte(room_hint_bar_coords_x);
+	s.syncAsByte(room_hint_bar_coords_y);
+	s.syncAsByte(last_object_hint);
+	s.syncAsByte(object_hint);
+	s.syncAsByte(command_hint);
+	s.syncAsByte(last_command_hint);
+	s.syncAsByte(zone_spr_index);
+	s.syncAsByte(zone_obj_count);
+
+	for (int i = 0; i < 2; i++) {
+		s.syncAsByte(the_wall_doors[i].height);
+		s.syncAsByte(the_wall_doors[i].width);
+		s.syncAsUint16LE(the_wall_doors[i].pitch);
+		s.syncAsUint16LE(the_wall_doors[i].offs);
+		syncPtrOffset(s, the_wall_doors[i].pixels, sprit_load_buffer);
+	}
+
+	syncPtrOffset(s, script_vars[kScrPool0_WordVars0], &script_word_vars);
+	syncPtrOffset(s, script_vars[kScrPool1_WordVars1], &script_word_vars);
+	syncPtrOffset(s, script_vars[kScrPool2_ByteVars], &script_byte_vars);
+	syncPtrOffset(s, script_vars[kScrPool3_CurrentItem], inventory_items);
+	syncPtrOffset(s, script_vars[kScrPool4_ZoneSpots], zones_data);
+	syncPtrOffset(s, script_vars[kScrPool5_Persons], pers_list);
+	syncPtrOffset(s, script_vars[kScrPool6_Inventory], inventory_items);
+	syncPtrOffset(s, script_vars[kScrPool7_Zapstiks], inventory_items);
+	syncPtrOffset(s, script_vars[kScrPool8_CurrentPers], pers_list);
+
+	for (int i = 0; i < MAX_SPRITES; i++)
+		syncPtrOffset(s, sprites_list[i], scratch_mem1);
+	for (int i = 0; i < MAX_DOORS; i++)
+		syncPtrOffset(s, doors_list[i], arpla_data);
+
+	syncPtrOffset(s, zone_spots, zones_data);
+	syncPtrOffset(s, zone_spots_end, zones_data);
+	syncPtrOffset(s, zone_spots_cur, zones_data);
+
+	syncPtrOffset(s, script_stack_ptr, script_stack);
+	for (int i = 0; i < 5 * 2; i++)
+		syncPtrOffset(s, script_stack[i], templ_data);
+
+	syncPtrOffset(s, vort_ptr, pers_list);
+	syncPtrOffset(s, vortanims_ptr, vortsanim_list);
+	syncPtrOffset(s, turkeyanims_ptr, turkeyanim_list);
+	syncPtrOffset(s, aspirant_ptr, pers_list);
+	syncPtrOffset(s, aspirant_spot, zones_data);
+	syncPtrOffset(s, found_spot, zones_data);
+	syncPtrOffset(s, spot_sprite, sprites_list);
+
+	uint16 timedSeqOfs = s.isSaving() ? (timed_seq_ptr ? (uint16)(timed_seq_ptr - patrol_route) : 0xFFFF) : 0;
+	s.syncAsUint16LE(timedSeqOfs);
+	if (s.isLoading())
+		timed_seq_ptr = (timedSeqOfs == 0xFFFF) ? nullptr : patrol_route + timedSeqOfs;
 }
-#endif
 
 int16 loadScena(void) {
 	return g_vm->loadGameDialog() ? 0 : 1;
-#if 0
-	int16 f;
-	int16 res;
-
-	script_byte_vars.game_paused = 1;
-
-	f = open(savegame_name, O_RDONLY | O_BINARY);
-	if (f == -1) {
-		script_byte_vars.game_paused = 0;
-		return 1;   /*error*/
-	}
-	/*
-	Save format:
-	  vars memory (751E-9D5D)
-	  frontbuffer (0x3FFF bytes)
-	  backbuffer  (0x3FFF bytes)
-	*/
-
-	res = ReadSaveData(f, 0);
-
-	if (res == 0) {
-		/*re-initialize sprites list*/
-		BackupSpotsImages();
-	}
-
-	close(f);
-	script_byte_vars.game_paused = 0;
-	return res;
-#endif
 }
 
 int16 saveScena(void) {
 	return g_vm->saveGameDialog() ? 0 : 1;
-#if 0
-	int16 f;
-	int16 res;
-
-	script_byte_vars.game_paused = 1;
-	BlitSpritesToBackBuffer();
-	command_hint = 100;
-	DrawCommandHint();
-	ShowCommandHint(frontbuffer);
-
-	f = open(savegame_name, O_CREAT | O_WRONLY | O_BINARY);
-	if (f == -1) {
-		script_byte_vars.game_paused = 0;
-		return 1;   /*error*/
-	}
-
-	res = WriteSaveData(f, 0);
-
-	close(f);
-
-	if (res != 0)
-		unlink(savegame_name);
-
-	script_byte_vars.game_paused = 0;
-	return res;
-#endif
 }
 
 void saveRestartGame(void) {
 	warning("STUB: saveRestartGame()");
-#if 0
-	int16 f;
-
-	f = open(restart_name, O_CREAT | O_WRONLY | O_BINARY);
-	if (f == -1) {
-		return;   /*error*/
-	}
-
-	WriteSaveData(f, 1);
-
-	close(f);
-	return;
-#endif
 }
 
-//extern jmp_buf restart_jmp;
-extern void askDisk2(void);
-
 void restartGame(void) {
 	warning("STUB: restartGame()");
 	g_vm->_shouldRestart = true;
-
-#if 0
-	int16 f;
-	int16 res;
-
-	for (;; askDisk2()) {
-		f = open(restart_name, O_RDONLY | O_BINARY);
-		if (f != -1) {
-			res = readSaveData(f, 1);
-			close(f);
-			if (res == 0)
-				break;
-		}
-	}
-
-	script_byte_vars.cur_spot_flags = 0xFF;
-	script_byte_vars.load_flag = 2;
-
-#endif
 }
 
 } // End of namespace Chamber


Commit: 406dba241029029329b626f569a4cc9403bb5246
    https://github.com/scummvm/scummvm/commit/406dba241029029329b626f569a4cc9403bb5246
Author: Ion Andrei Cristian (lecturatul2017 at gmail.com)
Date: 2026-04-29T00:22:50+02:00

Commit Message:
CHAMBER: Rename savegame to saveload

Rename savegame.cpp/h to saveload.cpp/h using git mv to preserve file
history. Update include guards and all references accordingly.

Changed paths:
  A engines/chamber/saveload.cpp
  A engines/chamber/saveload.h
  R engines/chamber/savegame.cpp
  R engines/chamber/savegame.h
    engines/chamber/kult.cpp
    engines/chamber/module.mk
    engines/chamber/script.cpp


diff --git a/engines/chamber/kult.cpp b/engines/chamber/kult.cpp
index d99687d68a3..75fd0e9d478 100644
--- a/engines/chamber/kult.cpp
+++ b/engines/chamber/kult.cpp
@@ -35,7 +35,7 @@
 #include "chamber/timer.h"
 #include "chamber/portrait.h"
 #include "chamber/room.h"
-#include "chamber/savegame.h"
+#include "chamber/saveload.h"
 #include "chamber/resdata.h"
 #include "chamber/script.h"
 #include "chamber/print.h"
diff --git a/engines/chamber/module.mk b/engines/chamber/module.mk
index bb54a94e06a..3f388b106a4 100644
--- a/engines/chamber/module.mk
+++ b/engines/chamber/module.mk
@@ -24,7 +24,7 @@ MODULE_OBJS := \
 	r_texts.o \
 	resdata.o \
 	room.o \
-	savegame.o \
+	saveload.o \
 	script.o \
 	sound.o \
 	timer.o
diff --git a/engines/chamber/savegame.cpp b/engines/chamber/saveload.cpp
similarity index 99%
rename from engines/chamber/savegame.cpp
rename to engines/chamber/saveload.cpp
index 28493dba9f0..9234688061a 100644
--- a/engines/chamber/savegame.cpp
+++ b/engines/chamber/saveload.cpp
@@ -21,7 +21,7 @@
 
 #include "chamber/chamber.h"
 #include "chamber/common.h"
-#include "chamber/savegame.h"
+#include "chamber/saveload.h"
 #include "chamber/resdata.h"
 #include "chamber/script.h"
 #include "chamber/cga.h"
diff --git a/engines/chamber/savegame.h b/engines/chamber/saveload.h
similarity index 95%
rename from engines/chamber/savegame.h
rename to engines/chamber/saveload.h
index 54eee5d5098..dd41f63903f 100644
--- a/engines/chamber/savegame.h
+++ b/engines/chamber/saveload.h
@@ -19,8 +19,8 @@
  *
  */
 
-#ifndef CHAMBER_SAVEGAME_H
-#define CHAMBER_SAVEGAME_H
+#ifndef CHAMBER_SAVELOAD_H
+#define CHAMBER_SAVELOAD_H
 
 namespace Chamber {
 
diff --git a/engines/chamber/script.cpp b/engines/chamber/script.cpp
index 6e0ddd59358..4ac42031c65 100644
--- a/engines/chamber/script.cpp
+++ b/engines/chamber/script.cpp
@@ -40,7 +40,7 @@
 #include "chamber/anim.h"
 #include "chamber/invent.h"
 #include "chamber/sound.h"
-#include "chamber/savegame.h"
+#include "chamber/saveload.h"
 #include "chamber/ifgm.h"
 
 #if 0




More information about the Scummvm-git-logs mailing list