1 /* ScummVM - Graphic Adventure Engine
2  *
3  * ScummVM is the legal property of its developers, whose names
4  * are too numerous to list here. Please refer to the COPYRIGHT
5  * file distributed with this source distribution.
6  *
7  * This program is free software; you can redistribute it and/or
8  * modify it under the terms of the GNU General Public License
9  * as published by the Free Software Foundation; either version 2
10  * of the License, or (at your option) any later version.
11  *
12  * This program is distributed in the hope that it will be useful,
13  * but WITHOUT ANY WARRANTY; without even the implied warranty of
14  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15  * GNU General Public License for more details.
16  *
17  * You should have received a copy of the GNU General Public License
18  * along with this program; if not, write to the Free Software
19  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
20  *
21  */
22 
23 #include "ags/lib/std/map.h"
24 #include "ags/shared/ac/audio_clip_type.h"
25 #include "ags/engine/ac/character.h"
26 #include "ags/shared/ac/common.h"
27 #include "ags/shared/ac/dialog_topic.h"
28 #include "ags/engine/ac/draw.h"
29 #include "ags/engine/ac/dynamic_sprite.h"
30 #include "ags/engine/ac/game.h"
31 #include "ags/shared/ac/game_setup_struct.h"
32 #include "ags/engine/ac/game_state.h"
33 #include "ags/engine/ac/gui.h"
34 #include "ags/engine/ac/mouse.h"
35 #include "ags/engine/ac/move_list.h"
36 #include "ags/engine/ac/overlay.h"
37 #include "ags/engine/ac/room_status.h"
38 #include "ags/engine/ac/screen_overlay.h"
39 #include "ags/shared/ac/sprite_cache.h"
40 #include "ags/shared/ac/view.h"
41 #include "ags/engine/ac/system.h"
42 #include "ags/engine/ac/dynobj/cc_serializer.h"
43 #include "ags/shared/debugging/out.h"
44 #include "ags/engine/game/savegame_components.h"
45 #include "ags/engine/game/savegame_internal.h"
46 #include "ags/shared/gfx/bitmap.h"
47 #include "ags/engine/gui/animating_gui_button.h"
48 #include "ags/shared/gui/gui_button.h"
49 #include "ags/shared/gui/gui_inv.h"
50 #include "ags/shared/gui/gui_label.h"
51 #include "ags/shared/gui/gui_listbox.h"
52 #include "ags/shared/gui/gui_main.h"
53 #include "ags/shared/gui/gui_slider.h"
54 #include "ags/shared/gui/gui_textbox.h"
55 #include "ags/plugins/ags_plugin.h"
56 #include "ags/plugins/plugin_engine.h"
57 #include "ags/shared/script/cc_error.h"
58 #include "ags/engine/script/script.h"
59 #include "ags/shared/util/file_stream.h" // TODO: needed only because plugins expect file handle
60 #include "ags/engine/media/audio/audio_system.h"
61 
62 namespace AGS3 {
63 
64 using namespace Shared;
65 
66 namespace AGS {
67 namespace Engine {
68 
69 namespace SavegameComponents {
70 
71 const String ComponentListTag = "Components";
72 
WriteFormatTag(Stream * out,const String & tag,bool open=true)73 void WriteFormatTag(Stream *out, const String &tag, bool open = true) {
74 	String full_tag = String::FromFormat(open ? "<%s>" : "</%s>", tag.GetCStr());
75 	out->Write(full_tag.GetCStr(), full_tag.GetLength());
76 }
77 
ReadFormatTag(Stream * in,String & tag,bool open=true)78 bool ReadFormatTag(Stream *in, String &tag, bool open = true) {
79 	if (in->ReadByte() != '<')
80 		return false;
81 	if (!open && in->ReadByte() != '/')
82 		return false;
83 	tag.Empty();
84 	while (!in->EOS()) {
85 		char c = in->ReadByte();
86 		if (c == '>')
87 			return true;
88 		tag.AppendChar(c);
89 	}
90 	return false; // reached EOS before closing symbol
91 }
92 
AssertFormatTag(Stream * in,const String & tag,bool open=true)93 bool AssertFormatTag(Stream *in, const String &tag, bool open = true) {
94 	String read_tag;
95 	if (!ReadFormatTag(in, read_tag, open))
96 		return false;
97 	return read_tag.Compare(tag) == 0;
98 }
99 
AssertFormatTagStrict(HSaveError & err,Stream * in,const String & tag,bool open=true)100 bool AssertFormatTagStrict(HSaveError &err, Stream *in, const String &tag, bool open = true) {
101 
102 	String read_tag;
103 	if (!ReadFormatTag(in, read_tag, open) || read_tag.Compare(tag) != 0) {
104 		err = new SavegameError(kSvgErr_InconsistentFormat,
105 		                        String::FromFormat("Mismatching tag: %s.", tag.GetCStr()));
106 		return false;
107 	}
108 	return true;
109 }
110 
AssertCompatLimit(HSaveError & err,int count,int max_count,const char * content_name)111 inline bool AssertCompatLimit(HSaveError &err, int count, int max_count, const char *content_name) {
112 	if (count > max_count) {
113 		err = new SavegameError(kSvgErr_IncompatibleEngine,
114 		                        String::FromFormat("Incompatible number of %s (count: %d, max: %d).",
115 		                                content_name, count, max_count));
116 		return false;
117 	}
118 	return true;
119 }
120 
AssertCompatRange(HSaveError & err,int value,int min_value,int max_value,const char * content_name)121 inline bool AssertCompatRange(HSaveError &err, int value, int min_value, int max_value, const char *content_name) {
122 	if (value < min_value || value > max_value) {
123 		err = new SavegameError(kSvgErr_IncompatibleEngine,
124 		                        String::FromFormat("Restore game error: incompatible %s (id: %d, range: %d - %d).",
125 		                                content_name, value, min_value, max_value));
126 		return false;
127 	}
128 	return true;
129 }
130 
AssertGameContent(HSaveError & err,int new_val,int original_val,const char * content_name)131 inline bool AssertGameContent(HSaveError &err, int new_val, int original_val, const char *content_name) {
132 	if (new_val != original_val) {
133 		err = new SavegameError(kSvgErr_GameContentAssertion,
134 		                        String::FromFormat("Mismatching number of %s (game: %d, save: %d).",
135 		                                content_name, original_val, new_val));
136 		return false;
137 	}
138 	return true;
139 }
140 
AssertGameObjectContent(HSaveError & err,int new_val,int original_val,const char * content_name,const char * obj_type,int obj_id)141 inline bool AssertGameObjectContent(HSaveError &err, int new_val, int original_val, const char *content_name,
142                                     const char *obj_type, int obj_id) {
143 	if (new_val != original_val) {
144 		err = new SavegameError(kSvgErr_GameContentAssertion,
145 		                        String::FromFormat("Mismatching number of %s, %s #%d (game: %d, save: %d).",
146 		                                content_name, obj_type, obj_id, original_val, new_val));
147 		return false;
148 	}
149 	return true;
150 }
151 
AssertGameObjectContent2(HSaveError & err,int new_val,int original_val,const char * content_name,const char * obj1_type,int obj1_id,const char * obj2_type,int obj2_id)152 inline bool AssertGameObjectContent2(HSaveError &err, int new_val, int original_val, const char *content_name,
153                                      const char *obj1_type, int obj1_id, const char *obj2_type, int obj2_id) {
154 	if (new_val != original_val) {
155 		err = new SavegameError(kSvgErr_GameContentAssertion,
156 		                        String::FromFormat("Mismatching number of %s, %s #%d, %s #%d (game: %d, save: %d).",
157 		                                content_name, obj1_type, obj1_id, obj2_type, obj2_id, original_val, new_val));
158 		return false;
159 	}
160 	return true;
161 }
162 
163 
WriteCameraState(const Camera & cam,Stream * out)164 void WriteCameraState(const Camera &cam, Stream *out) {
165 	int flags = 0;
166 	if (cam.IsLocked()) flags |= kSvgCamPosLocked;
167 	out->WriteInt32(flags);
168 	const Rect &rc = cam.GetRect();
169 	out->WriteInt32(rc.Left);
170 	out->WriteInt32(rc.Top);
171 	out->WriteInt32(rc.GetWidth());
172 	out->WriteInt32(rc.GetHeight());
173 }
174 
WriteViewportState(const Viewport & view,Stream * out)175 void WriteViewportState(const Viewport &view, Stream *out) {
176 	int flags = 0;
177 	if (view.IsVisible()) flags |= kSvgViewportVisible;
178 	out->WriteInt32(flags);
179 	const Rect &rc = view.GetRect();
180 	out->WriteInt32(rc.Left);
181 	out->WriteInt32(rc.Top);
182 	out->WriteInt32(rc.GetWidth());
183 	out->WriteInt32(rc.GetHeight());
184 	out->WriteInt32(view.GetZOrder());
185 	auto cam = view.GetCamera();
186 	if (cam)
187 		out->WriteInt32(cam->GetID());
188 	else
189 		out->WriteInt32(-1);
190 }
191 
WriteGameState(Stream * out)192 HSaveError WriteGameState(Stream *out) {
193 	// Game base
194 	_GP(game).WriteForSavegame(out);
195 	// Game palette
196 	// TODO: probably no need to save this for hi/true-res game
197 	out->WriteArray(_G(palette), sizeof(RGB), 256);
198 
199 	if (_G(loaded_game_file_version) <= kGameVersion_272) {
200 		// Global variables
201 		out->WriteInt32(_G(numGlobalVars));
202 		for (int i = 0; i < _G(numGlobalVars); ++i)
203 			_G(globalvars)[i].Write(out);
204 	}
205 
206 	// Game state
207 	_GP(play).WriteForSavegame(out);
208 	// Other dynamic values
209 	out->WriteInt32(_G(frames_per_second));
210 	out->WriteInt32(_G(loopcounter));
211 	out->WriteInt32(_G(ifacepopped));
212 	out->WriteInt32(_G(game_paused));
213 	// Mouse cursor
214 	out->WriteInt32(_G(cur_mode));
215 	out->WriteInt32(_G(cur_cursor));
216 	out->WriteInt32(_G(mouse_on_iface));
217 
218 	// Viewports and cameras
219 	int viewcam_flags = 0;
220 	if (_GP(play).IsAutoRoomViewport())
221 		viewcam_flags |= kSvgGameAutoRoomView;
222 	out->WriteInt32(viewcam_flags);
223 	out->WriteInt32(_GP(play).GetRoomCameraCount());
224 	for (int i = 0; i < _GP(play).GetRoomCameraCount(); ++i)
225 		WriteCameraState(*_GP(play).GetRoomCamera(i), out);
226 	out->WriteInt32(_GP(play).GetRoomViewportCount());
227 	for (int i = 0; i < _GP(play).GetRoomViewportCount(); ++i)
228 		WriteViewportState(*_GP(play).GetRoomViewport(i), out);
229 
230 	return HSaveError::None();
231 }
232 
ReadLegacyCameraState(Stream * in,RestoredData & r_data)233 void ReadLegacyCameraState(Stream *in, RestoredData &r_data) {
234 	// Precreate viewport and camera and save data in temp structs
235 	int camx = in->ReadInt32();
236 	int camy = in->ReadInt32();
237 	_GP(play).CreateRoomCamera();
238 	_GP(play).CreateRoomViewport();
239 	const auto &main_view = _GP(play).GetMainViewport();
240 	RestoredData::CameraData cam_dat;
241 	cam_dat.ID = 0;
242 	cam_dat.Left = camx;
243 	cam_dat.Top = camy;
244 	cam_dat.Width = main_view.GetWidth();
245 	cam_dat.Height = main_view.GetHeight();
246 	r_data.Cameras.push_back(cam_dat);
247 	RestoredData::ViewportData view_dat;
248 	view_dat.ID = 0;
249 	view_dat.Width = main_view.GetWidth();
250 	view_dat.Height = main_view.GetHeight();
251 	view_dat.Flags = kSvgViewportVisible;
252 	view_dat.CamID = 0;
253 	r_data.Viewports.push_back(view_dat);
254 }
255 
ReadCameraState(RestoredData & r_data,Stream * in)256 void ReadCameraState(RestoredData &r_data, Stream *in) {
257 	RestoredData::CameraData cam;
258 	cam.ID = r_data.Cameras.size();
259 	cam.Flags = in->ReadInt32();
260 	cam.Left = in->ReadInt32();
261 	cam.Top = in->ReadInt32();
262 	cam.Width = in->ReadInt32();
263 	cam.Height = in->ReadInt32();
264 	r_data.Cameras.push_back(cam);
265 }
266 
ReadViewportState(RestoredData & r_data,Stream * in)267 void ReadViewportState(RestoredData &r_data, Stream *in) {
268 	RestoredData::ViewportData view;
269 	view.ID = r_data.Viewports.size();
270 	view.Flags = in->ReadInt32();
271 	view.Left = in->ReadInt32();
272 	view.Top = in->ReadInt32();
273 	view.Width = in->ReadInt32();
274 	view.Height = in->ReadInt32();
275 	view.ZOrder = in->ReadInt32();
276 	view.CamID = in->ReadInt32();
277 	r_data.Viewports.push_back(view);
278 }
279 
ReadGameState(Stream * in,int32_t cmp_ver,const PreservedParams & pp,RestoredData & r_data)280 HSaveError ReadGameState(Stream *in, int32_t cmp_ver, const PreservedParams &pp, RestoredData &r_data) {
281 	HSaveError err;
282 	GameStateSvgVersion svg_ver = (GameStateSvgVersion)cmp_ver;
283 	// Game base
284 	_GP(game).ReadFromSavegame(in);
285 	// Game palette
286 	in->ReadArray(_G(palette), sizeof(RGB), 256);
287 
288 	if (_G(loaded_game_file_version) <= kGameVersion_272) {
289 		// Legacy interaction global variables
290 		if (!AssertGameContent(err, in->ReadInt32(), _G(numGlobalVars), "Global Variables"))
291 			return err;
292 		for (int i = 0; i < _G(numGlobalVars); ++i)
293 			_G(globalvars)[i].Read(in);
294 	}
295 
296 	// Game state
297 	_GP(play).ReadFromSavegame(in, svg_ver, r_data);
298 
299 	// Other dynamic values
300 	r_data.FPS = in->ReadInt32();
301 	set_loop_counter(in->ReadInt32());
302 	_G(ifacepopped) = in->ReadInt32();
303 	_G(game_paused) = in->ReadInt32();
304 	// Mouse cursor state
305 	r_data.CursorMode = in->ReadInt32();
306 	r_data.CursorID = in->ReadInt32();
307 	_G(mouse_on_iface) = in->ReadInt32();
308 
309 	// Viewports and cameras
310 	if (svg_ver < kGSSvgVersion_350_10) {
311 		ReadLegacyCameraState(in, r_data);
312 		r_data.Cameras[0].Flags = r_data.Camera0_Flags;
313 	} else {
314 		int viewcam_flags = in->ReadInt32();
315 		_GP(play).SetAutoRoomViewport((viewcam_flags & kSvgGameAutoRoomView) != 0);
316 		// TODO: we create viewport and camera objects here because they are
317 		// required for the managed pool deserialization, but read actual
318 		// data into temp structs because we need to apply it after active
319 		// room is loaded.
320 		// See comments to RestoredData struct for further details.
321 		int cam_count = in->ReadInt32();
322 		for (int i = 0; i < cam_count; ++i) {
323 			_GP(play).CreateRoomCamera();
324 			ReadCameraState(r_data, in);
325 		}
326 		int view_count = in->ReadInt32();
327 		for (int i = 0; i < view_count; ++i) {
328 			_GP(play).CreateRoomViewport();
329 			ReadViewportState(r_data, in);
330 		}
331 	}
332 	return err;
333 }
334 
WriteAudio(Stream * out)335 HSaveError WriteAudio(Stream *out) {
336 	AudioChannelsLock lock;
337 
338 	// Game content assertion
339 	out->WriteInt32(_GP(game).audioClipTypes.size());
340 	out->WriteInt32(_GP(game).audioClips.size()); // [ivan-mogilko] not necessary, kept only to avoid changing save format
341 	// Audio types
342 	for (size_t i = 0; i < _GP(game).audioClipTypes.size(); ++i) {
343 		_GP(game).audioClipTypes[i].WriteToSavegame(out);
344 		out->WriteInt32(_GP(play).default_audio_type_volumes[i]);
345 	}
346 
347 	// Audio clips and crossfade
348 	for (int i = 0; i <= MAX_SOUND_CHANNELS; i++) {
349 		auto *ch = lock.GetChannelIfPlaying(i);
350 		if ((ch != nullptr) && (ch->_sourceClip != nullptr)) {
351 			out->WriteInt32(((ScriptAudioClip *)ch->_sourceClip)->id);
352 			out->WriteInt32(ch->get_pos());
353 			out->WriteInt32(ch->_priority);
354 			out->WriteInt32(ch->_repeat ? 1 : 0);
355 			out->WriteInt32(ch->_vol);
356 			out->WriteInt32(ch->_panning);
357 			out->WriteInt32(ch->_volAsPercentage);
358 			out->WriteInt32(ch->_panningAsPercentage);
359 			out->WriteInt32(ch->get_speed());
360 			// since version 1
361 			out->WriteInt32(ch->_xSource);
362 			out->WriteInt32(ch->_ySource);
363 			out->WriteInt32(ch->_maximumPossibleDistanceAway);
364 		} else {
365 			out->WriteInt32(-1);
366 		}
367 	}
368 	out->WriteInt32(_G(crossFading));
369 	out->WriteInt32(_G(crossFadeVolumePerStep));
370 	out->WriteInt32(_G(crossFadeStep));
371 	out->WriteInt32(_G(crossFadeVolumeAtStart));
372 	// CHECKME: why this needs to be saved?
373 	out->WriteInt32(_G(current_music_type));
374 
375 	// Ambient sound
376 	for (int i = 0; i < MAX_SOUND_CHANNELS; ++i)
377 		_GP(ambient)[i].WriteToFile(out);
378 	return HSaveError::None();
379 }
380 
ReadAudio(Stream * in,int32_t cmp_ver,const PreservedParams & pp,RestoredData & r_data)381 HSaveError ReadAudio(Stream *in, int32_t cmp_ver, const PreservedParams &pp, RestoredData &r_data) {
382 	HSaveError err;
383 	// Game content assertion
384 	if (!AssertGameContent(err, in->ReadInt32(), _GP(game).audioClipTypes.size(), "Audio Clip Types"))
385 		return err;
386 	in->ReadInt32(); // audio clip count
387 	/* [ivan-mogilko] looks like it's not necessary to assert, as there's no data serialized for clips
388 	if (!AssertGameContent(err, in->ReadInt32(), _GP(game).audioClips.size(), "Audio Clips"))
389 	    return err;*/
390 
391 	// Audio types
392 	for (size_t i = 0; i < _GP(game).audioClipTypes.size(); ++i) {
393 		_GP(game).audioClipTypes[i].ReadFromSavegame(in);
394 		_GP(play).default_audio_type_volumes[i] = in->ReadInt32();
395 	}
396 
397 	// Audio clips and crossfade
398 	for (int i = 0; i <= MAX_SOUND_CHANNELS; ++i) {
399 		RestoredData::ChannelInfo &chan_info = r_data.AudioChans[i];
400 		chan_info.Pos = 0;
401 		chan_info.ClipID = in->ReadInt32();
402 		if (chan_info.ClipID >= 0) {
403 			chan_info.Pos = in->ReadInt32();
404 			if (chan_info.Pos < 0)
405 				chan_info.Pos = 0;
406 			chan_info.Priority = in->ReadInt32();
407 			chan_info.Repeat = in->ReadInt32();
408 			chan_info.Vol = in->ReadInt32();
409 			chan_info.Pan = in->ReadInt32();
410 			chan_info.VolAsPercent = in->ReadInt32();
411 			chan_info.PanAsPercent = in->ReadInt32();
412 			chan_info.Speed = 1000;
413 			chan_info.Speed = in->ReadInt32();
414 			if (cmp_ver >= 1) {
415 				chan_info.XSource = in->ReadInt32();
416 				chan_info.YSource = in->ReadInt32();
417 				chan_info.MaxDist = in->ReadInt32();
418 			}
419 		}
420 	}
421 	_G(crossFading) = in->ReadInt32();
422 	_G(crossFadeVolumePerStep) = in->ReadInt32();
423 	_G(crossFadeStep) = in->ReadInt32();
424 	_G(crossFadeVolumeAtStart) = in->ReadInt32();
425 	// preserve legacy music type setting
426 	_G(current_music_type) = in->ReadInt32();
427 
428 	// Ambient sound
429 	for (int i = 0; i < MAX_SOUND_CHANNELS; ++i)
430 		_GP(ambient)[i].ReadFromFile(in);
431 	for (int i = 1; i < MAX_SOUND_CHANNELS; ++i) {
432 		if (_GP(ambient)[i].channel == 0) {
433 			r_data.DoAmbient[i] = 0;
434 		} else {
435 			r_data.DoAmbient[i] = _GP(ambient)[i].num;
436 			_GP(ambient)[i].channel = 0;
437 		}
438 	}
439 	return err;
440 }
441 
WriteTimesRun272(const Interaction & intr,Stream * out)442 void WriteTimesRun272(const Interaction &intr, Stream *out) {
443 	for (size_t i = 0; i < intr.Events.size(); ++i)
444 		out->WriteInt32(intr.Events[i].TimesRun);
445 }
446 
WriteInteraction272(const Interaction & intr,Stream * out)447 void WriteInteraction272(const Interaction &intr, Stream *out) {
448 	const size_t evt_count = intr.Events.size();
449 	out->WriteInt32(evt_count);
450 	for (size_t i = 0; i < evt_count; ++i)
451 		out->WriteInt32(intr.Events[i].Type);
452 	WriteTimesRun272(intr, out);
453 }
454 
ReadTimesRun272(Interaction & intr,Stream * in)455 void ReadTimesRun272(Interaction &intr, Stream *in) {
456 	for (size_t i = 0; i < intr.Events.size(); ++i)
457 		intr.Events[i].TimesRun = in->ReadInt32();
458 }
459 
ReadInteraction272(Interaction & intr,Stream * in)460 HSaveError ReadInteraction272(Interaction &intr, Stream *in) {
461 	HSaveError err;
462 	const size_t evt_count = in->ReadInt32();
463 	if (!AssertCompatLimit(err, evt_count, MAX_NEWINTERACTION_EVENTS, "interactions"))
464 		return err;
465 	intr.Events.resize(evt_count);
466 	for (size_t i = 0; i < evt_count; ++i)
467 		intr.Events[i].Type = in->ReadInt32();
468 	ReadTimesRun272(intr, in);
469 	return err;
470 }
471 
WriteCharacters(Stream * out)472 HSaveError WriteCharacters(Stream *out) {
473 	out->WriteInt32(_GP(game).numcharacters);
474 	for (int i = 0; i < _GP(game).numcharacters; ++i) {
475 		_GP(game).chars[i].WriteToFile(out);
476 		_G(charextra)[i].WriteToFile(out);
477 		Properties::WriteValues(_GP(play).charProps[i], out);
478 		if (_G(loaded_game_file_version) <= kGameVersion_272)
479 			WriteTimesRun272(*_GP(game).intrChar[i], out);
480 		// character movement path cache
481 		_G(mls)[CHMLSOFFS + i].WriteToFile(out);
482 	}
483 	return HSaveError::None();
484 }
485 
ReadCharacters(Stream * in,int32_t cmp_ver,const PreservedParams & pp,RestoredData & r_data)486 HSaveError ReadCharacters(Stream *in, int32_t cmp_ver, const PreservedParams &pp, RestoredData &r_data) {
487 	HSaveError err;
488 	if (!AssertGameContent(err, in->ReadInt32(), _GP(game).numcharacters, "Characters"))
489 		return err;
490 	for (int i = 0; i < _GP(game).numcharacters; ++i) {
491 		_GP(game).chars[i].ReadFromFile(in);
492 		_G(charextra)[i].ReadFromFile(in);
493 		Properties::ReadValues(_GP(play).charProps[i], in);
494 		if (_G(loaded_game_file_version) <= kGameVersion_272)
495 			ReadTimesRun272(*_GP(game).intrChar[i], in);
496 		// character movement path cache
497 		err = _G(mls)[CHMLSOFFS + i].ReadFromFile(in, cmp_ver > 0 ? 1 : 0);
498 		if (!err)
499 			return err;
500 	}
501 	return err;
502 }
503 
WriteDialogs(Stream * out)504 HSaveError WriteDialogs(Stream *out) {
505 	out->WriteInt32(_GP(game).numdialog);
506 	for (int i = 0; i < _GP(game).numdialog; ++i) {
507 		_G(dialog)[i].WriteToSavegame(out);
508 	}
509 	return HSaveError::None();
510 }
511 
ReadDialogs(Stream * in,int32_t cmp_ver,const PreservedParams & pp,RestoredData & r_data)512 HSaveError ReadDialogs(Stream *in, int32_t cmp_ver, const PreservedParams &pp, RestoredData &r_data) {
513 	HSaveError err;
514 	if (!AssertGameContent(err, in->ReadInt32(), _GP(game).numdialog, "Dialogs"))
515 		return err;
516 	for (int i = 0; i < _GP(game).numdialog; ++i) {
517 		_G(dialog)[i].ReadFromSavegame(in);
518 	}
519 	return err;
520 }
521 
WriteGUI(Stream * out)522 HSaveError WriteGUI(Stream *out) {
523 	// GUI state
524 	WriteFormatTag(out, "GUIs");
525 	out->WriteInt32(_GP(game).numgui);
526 	for (int i = 0; i < _GP(game).numgui; ++i)
527 		_GP(guis)[i].WriteToSavegame(out);
528 
529 	WriteFormatTag(out, "GUIButtons");
530 	out->WriteInt32(_G(numguibuts));
531 	for (int i = 0; i < _G(numguibuts); ++i)
532 		_GP(guibuts)[i].WriteToSavegame(out);
533 
534 	WriteFormatTag(out, "GUILabels");
535 	out->WriteInt32(_G(numguilabels));
536 	for (int i = 0; i < _G(numguilabels); ++i)
537 		_GP(guilabels)[i].WriteToSavegame(out);
538 
539 	WriteFormatTag(out, "GUIInvWindows");
540 	out->WriteInt32(_G(numguiinv));
541 	for (int i = 0; i < _G(numguiinv); ++i)
542 		_GP(guiinv)[i].WriteToSavegame(out);
543 
544 	WriteFormatTag(out, "GUISliders");
545 	out->WriteInt32(_G(numguislider));
546 	for (int i = 0; i < _G(numguislider); ++i)
547 		_GP(guislider)[i].WriteToSavegame(out);
548 
549 	WriteFormatTag(out, "GUITextBoxes");
550 	out->WriteInt32(_G(numguitext));
551 	for (int i = 0; i < _G(numguitext); ++i)
552 		_GP(guitext)[i].WriteToSavegame(out);
553 
554 	WriteFormatTag(out, "GUIListBoxes");
555 	out->WriteInt32(_G(numguilist));
556 	for (int i = 0; i < _G(numguilist); ++i)
557 		_GP(guilist)[i].WriteToSavegame(out);
558 
559 	// Animated buttons
560 	WriteFormatTag(out, "AnimatedButtons");
561 	out->WriteInt32(_G(numAnimButs));
562 	for (int i = 0; i < _G(numAnimButs); ++i)
563 		_G(animbuts)[i].WriteToFile(out);
564 	return HSaveError::None();
565 }
566 
ReadGUI(Stream * in,int32_t cmp_ver,const PreservedParams & pp,RestoredData & r_data)567 HSaveError ReadGUI(Stream *in, int32_t cmp_ver, const PreservedParams &pp, RestoredData &r_data) {
568 	HSaveError err;
569 	const GuiSvgVersion svg_ver = (GuiSvgVersion)cmp_ver;
570 	// GUI state
571 	if (!AssertFormatTagStrict(err, in, "GUIs"))
572 		return err;
573 	if (!AssertGameContent(err, in->ReadInt32(), _GP(game).numgui, "GUIs"))
574 		return err;
575 	for (int i = 0; i < _GP(game).numgui; ++i)
576 		_GP(guis)[i].ReadFromSavegame(in, svg_ver);
577 
578 	if (!AssertFormatTagStrict(err, in, "GUIButtons"))
579 		return err;
580 	if (!AssertGameContent(err, in->ReadInt32(), _G(numguibuts), "GUI Buttons"))
581 		return err;
582 	for (int i = 0; i < _G(numguibuts); ++i)
583 		_GP(guibuts)[i].ReadFromSavegame(in, svg_ver);
584 
585 	if (!AssertFormatTagStrict(err, in, "GUILabels"))
586 		return err;
587 	if (!AssertGameContent(err, in->ReadInt32(), _G(numguilabels), "GUI Labels"))
588 		return err;
589 	for (int i = 0; i < _G(numguilabels); ++i)
590 		_GP(guilabels)[i].ReadFromSavegame(in, svg_ver);
591 
592 	if (!AssertFormatTagStrict(err, in, "GUIInvWindows"))
593 		return err;
594 	if (!AssertGameContent(err, in->ReadInt32(), _G(numguiinv), "GUI InvWindows"))
595 		return err;
596 	for (int i = 0; i < _G(numguiinv); ++i)
597 		_GP(guiinv)[i].ReadFromSavegame(in, svg_ver);
598 
599 	if (!AssertFormatTagStrict(err, in, "GUISliders"))
600 		return err;
601 	if (!AssertGameContent(err, in->ReadInt32(), _G(numguislider), "GUI Sliders"))
602 		return err;
603 	for (int i = 0; i < _G(numguislider); ++i)
604 		_GP(guislider)[i].ReadFromSavegame(in, svg_ver);
605 
606 	if (!AssertFormatTagStrict(err, in, "GUITextBoxes"))
607 		return err;
608 	if (!AssertGameContent(err, in->ReadInt32(), _G(numguitext), "GUI TextBoxes"))
609 		return err;
610 	for (int i = 0; i < _G(numguitext); ++i)
611 		_GP(guitext)[i].ReadFromSavegame(in, svg_ver);
612 
613 	if (!AssertFormatTagStrict(err, in, "GUIListBoxes"))
614 		return err;
615 	if (!AssertGameContent(err, in->ReadInt32(), _G(numguilist), "GUI ListBoxes"))
616 		return err;
617 	for (int i = 0; i < _G(numguilist); ++i)
618 		_GP(guilist)[i].ReadFromSavegame(in, svg_ver);
619 
620 	// Animated buttons
621 	if (!AssertFormatTagStrict(err, in, "AnimatedButtons"))
622 		return err;
623 	int anim_count = in->ReadInt32();
624 	if (!AssertCompatLimit(err, anim_count, MAX_ANIMATING_BUTTONS, "animated buttons"))
625 		return err;
626 	_G(numAnimButs) = anim_count;
627 	for (int i = 0; i < _G(numAnimButs); ++i)
628 		_G(animbuts)[i].ReadFromFile(in);
629 	return err;
630 }
631 
WriteInventory(Stream * out)632 HSaveError WriteInventory(Stream *out) {
633 	out->WriteInt32(_GP(game).numinvitems);
634 	for (int i = 0; i < _GP(game).numinvitems; ++i) {
635 		_GP(game).invinfo[i].WriteToSavegame(out);
636 		Properties::WriteValues(_GP(play).invProps[i], out);
637 		if (_G(loaded_game_file_version) <= kGameVersion_272)
638 			WriteTimesRun272(*_GP(game).intrInv[i], out);
639 	}
640 	return HSaveError::None();
641 }
642 
ReadInventory(Stream * in,int32_t cmp_ver,const PreservedParams & pp,RestoredData & r_data)643 HSaveError ReadInventory(Stream *in, int32_t cmp_ver, const PreservedParams &pp, RestoredData &r_data) {
644 	HSaveError err;
645 	if (!AssertGameContent(err, in->ReadInt32(), _GP(game).numinvitems, "Inventory Items"))
646 		return err;
647 	for (int i = 0; i < _GP(game).numinvitems; ++i) {
648 		_GP(game).invinfo[i].ReadFromSavegame(in);
649 		Properties::ReadValues(_GP(play).invProps[i], in);
650 		if (_G(loaded_game_file_version) <= kGameVersion_272)
651 			ReadTimesRun272(*_GP(game).intrInv[i], in);
652 	}
653 	return err;
654 }
655 
WriteMouseCursors(Stream * out)656 HSaveError WriteMouseCursors(Stream *out) {
657 	out->WriteInt32(_GP(game).numcursors);
658 	for (int i = 0; i < _GP(game).numcursors; ++i) {
659 		_GP(game).mcurs[i].WriteToSavegame(out);
660 	}
661 	return HSaveError::None();
662 }
663 
ReadMouseCursors(Stream * in,int32_t cmp_ver,const PreservedParams & pp,RestoredData & r_data)664 HSaveError ReadMouseCursors(Stream *in, int32_t cmp_ver, const PreservedParams &pp, RestoredData &r_data) {
665 	HSaveError err;
666 	if (!AssertGameContent(err, in->ReadInt32(), _GP(game).numcursors, "Mouse Cursors"))
667 		return err;
668 	for (int i = 0; i < _GP(game).numcursors; ++i) {
669 		_GP(game).mcurs[i].ReadFromSavegame(in);
670 	}
671 	return err;
672 }
673 
WriteViews(Stream * out)674 HSaveError WriteViews(Stream *out) {
675 	out->WriteInt32(_GP(game).numviews);
676 	for (int view = 0; view < _GP(game).numviews; ++view) {
677 		out->WriteInt32(_G(views)[view].numLoops);
678 		for (int loop = 0; loop < _G(views)[view].numLoops; ++loop) {
679 			out->WriteInt32(_G(views)[view].loops[loop].numFrames);
680 			for (int frame = 0; frame < _G(views)[view].loops[loop].numFrames; ++frame) {
681 				out->WriteInt32(_G(views)[view].loops[loop].frames[frame].sound);
682 				out->WriteInt32(_G(views)[view].loops[loop].frames[frame].pic);
683 			}
684 		}
685 	}
686 	return HSaveError::None();
687 }
688 
ReadViews(Stream * in,int32_t cmp_ver,const PreservedParams & pp,RestoredData & r_data)689 HSaveError ReadViews(Stream *in, int32_t cmp_ver, const PreservedParams &pp, RestoredData &r_data) {
690 	HSaveError err;
691 	if (!AssertGameContent(err, in->ReadInt32(), _GP(game).numviews, "Views"))
692 		return err;
693 	for (int view = 0; view < _GP(game).numviews; ++view) {
694 		if (!AssertGameObjectContent(err, in->ReadInt32(), _G(views)[view].numLoops,
695 		                             "Loops", "View", view))
696 			return err;
697 		for (int loop = 0; loop < _G(views)[view].numLoops; ++loop) {
698 			if (!AssertGameObjectContent2(err, in->ReadInt32(), _G(views)[view].loops[loop].numFrames,
699 			                              "Frame", "View", view, "Loop", loop))
700 				return err;
701 			for (int frame = 0; frame < _G(views)[view].loops[loop].numFrames; ++frame) {
702 				_G(views)[view].loops[loop].frames[frame].sound = in->ReadInt32();
703 				_G(views)[view].loops[loop].frames[frame].pic = in->ReadInt32();
704 			}
705 		}
706 	}
707 	return err;
708 }
709 
WriteDynamicSprites(Stream * out)710 HSaveError WriteDynamicSprites(Stream *out) {
711 	const soff_t ref_pos = out->GetPosition();
712 	out->WriteInt32(0); // number of dynamic sprites
713 	out->WriteInt32(0); // top index
714 	int count = 0;
715 	int top_index = 1;
716 	for (size_t i = 1; i < _GP(spriteset).GetSpriteSlotCount(); ++i) {
717 		if (_GP(game).SpriteInfos[i].Flags & SPF_DYNAMICALLOC) {
718 			count++;
719 			top_index = i;
720 			out->WriteInt32(i);
721 			out->WriteInt32(_GP(game).SpriteInfos[i].Flags);
722 			serialize_bitmap(_GP(spriteset)[i], out);
723 		}
724 	}
725 	const soff_t end_pos = out->GetPosition();
726 	out->Seek(ref_pos, kSeekBegin);
727 	out->WriteInt32(count);
728 	out->WriteInt32(top_index);
729 	out->Seek(end_pos, kSeekBegin);
730 	return HSaveError::None();
731 }
732 
ReadDynamicSprites(Stream * in,int32_t cmp_ver,const PreservedParams & pp,RestoredData & r_data)733 HSaveError ReadDynamicSprites(Stream *in, int32_t cmp_ver, const PreservedParams &pp, RestoredData &r_data) {
734 	HSaveError err;
735 	const int spr_count = in->ReadInt32();
736 	// ensure the sprite set is at least large enough
737 	// to accomodate top dynamic sprite index
738 	const int top_index = in->ReadInt32();
739 	_GP(spriteset).EnlargeTo(top_index);
740 	for (int i = 0; i < spr_count; ++i) {
741 		int id = in->ReadInt32();
742 		int flags = in->ReadInt32();
743 		add_dynamic_sprite(id, read_serialized_bitmap(in));
744 		_GP(game).SpriteInfos[id].Flags = flags;
745 	}
746 	return err;
747 }
748 
WriteOverlays(Stream * out)749 HSaveError WriteOverlays(Stream *out) {
750 	out->WriteInt32(_G(numscreenover));
751 	for (int i = 0; i < _G(numscreenover); ++i) {
752 		_G(screenover)[i].WriteToFile(out);
753 		serialize_bitmap(_G(screenover)[i].pic, out);
754 	}
755 	return HSaveError::None();
756 }
757 
ReadOverlays(Stream * in,int32_t cmp_ver,const PreservedParams & pp,RestoredData & r_data)758 HSaveError ReadOverlays(Stream *in, int32_t cmp_ver, const PreservedParams &pp, RestoredData &r_data) {
759 	HSaveError err;
760 	int over_count = in->ReadInt32();
761 	if (!AssertCompatLimit(err, over_count, MAX_SCREEN_OVERLAYS, "overlays"))
762 		return err;
763 	_G(numscreenover) = over_count;
764 	for (int i = 0; i < _G(numscreenover); ++i) {
765 		_G(screenover)[i].ReadFromFile(in, cmp_ver);
766 		if (_G(screenover)[i].hasSerializedBitmap)
767 			_G(screenover)[i].pic = read_serialized_bitmap(in);
768 	}
769 	return err;
770 }
771 
WriteDynamicSurfaces(Stream * out)772 HSaveError WriteDynamicSurfaces(Stream *out) {
773 	out->WriteInt32(MAX_DYNAMIC_SURFACES);
774 	for (int i = 0; i < MAX_DYNAMIC_SURFACES; ++i) {
775 		if (_G(dynamicallyCreatedSurfaces)[i] == nullptr) {
776 			out->WriteInt8(0);
777 		} else {
778 			out->WriteInt8(1);
779 			serialize_bitmap(_G(dynamicallyCreatedSurfaces)[i], out);
780 		}
781 	}
782 	return HSaveError::None();
783 }
784 
ReadDynamicSurfaces(Stream * in,int32_t cmp_ver,const PreservedParams & pp,RestoredData & r_data)785 HSaveError ReadDynamicSurfaces(Stream *in, int32_t cmp_ver, const PreservedParams &pp, RestoredData &r_data) {
786 	HSaveError err;
787 	if (!AssertCompatLimit(err, in->ReadInt32(), MAX_DYNAMIC_SURFACES, "Dynamic Surfaces"))
788 		return err;
789 	// Load the surfaces into a temporary array since ccUnserialiseObjects will destroy them otherwise
790 	r_data.DynamicSurfaces.resize(MAX_DYNAMIC_SURFACES);
791 	for (int i = 0; i < MAX_DYNAMIC_SURFACES; ++i) {
792 		if (in->ReadInt8() == 0)
793 			r_data.DynamicSurfaces[i] = nullptr;
794 		else
795 			r_data.DynamicSurfaces[i] = read_serialized_bitmap(in);
796 	}
797 	return err;
798 }
799 
WriteScriptModules(Stream * out)800 HSaveError WriteScriptModules(Stream *out) {
801 	// write the data segment of the global script
802 	int data_len = _G(gameinst)->globaldatasize;
803 	out->WriteInt32(data_len);
804 	if (data_len > 0)
805 		out->Write(_G(gameinst)->globaldata, data_len);
806 	// write the script modules data segments
807 	out->WriteInt32(_G(numScriptModules));
808 	for (int i = 0; i < _G(numScriptModules); ++i) {
809 		data_len = _GP(moduleInst)[i]->globaldatasize;
810 		out->WriteInt32(data_len);
811 		if (data_len > 0)
812 			out->Write(_GP(moduleInst)[i]->globaldata, data_len);
813 	}
814 	return HSaveError::None();
815 }
816 
ReadScriptModules(Stream * in,int32_t cmp_ver,const PreservedParams & pp,RestoredData & r_data)817 HSaveError ReadScriptModules(Stream *in, int32_t cmp_ver, const PreservedParams &pp, RestoredData &r_data) {
818 	HSaveError err;
819 	// read the global script data segment
820 	int data_len = in->ReadInt32();
821 	if (!AssertGameContent(err, data_len, pp.GlScDataSize, "global script data"))
822 		return err;
823 	r_data.GlobalScript.Len = data_len;
824 	r_data.GlobalScript.Data.reset(new char[data_len]);
825 	in->Read(r_data.GlobalScript.Data.get(), data_len);
826 
827 	if (!AssertGameContent(err, in->ReadInt32(), _G(numScriptModules), "Script Modules"))
828 		return err;
829 	r_data.ScriptModules.resize(_G(numScriptModules));
830 	for (int i = 0; i < _G(numScriptModules); ++i) {
831 		data_len = in->ReadInt32();
832 		if (!AssertGameObjectContent(err, data_len, pp.ScMdDataSize[i], "script module data", "module", i))
833 			return err;
834 		r_data.ScriptModules[i].Len = data_len;
835 		r_data.ScriptModules[i].Data.reset(new char[data_len]);
836 		in->Read(r_data.ScriptModules[i].Data.get(), data_len);
837 	}
838 	return err;
839 }
840 
WriteRoomStates(Stream * out)841 HSaveError WriteRoomStates(Stream *out) {
842 	// write the room state for all the rooms the player has been in
843 	out->WriteInt32(MAX_ROOMS);
844 	for (int i = 0; i < MAX_ROOMS; ++i) {
845 		if (isRoomStatusValid(i)) {
846 			RoomStatus *roomstat = getRoomStatus(i);
847 			if (roomstat->beenhere) {
848 				out->WriteInt32(i);
849 				WriteFormatTag(out, "RoomState", true);
850 				roomstat->WriteToSavegame(out);
851 				WriteFormatTag(out, "RoomState", false);
852 			} else
853 				out->WriteInt32(-1);
854 		} else
855 			out->WriteInt32(-1);
856 	}
857 	return HSaveError::None();
858 }
859 
ReadRoomStates(Stream * in,int32_t cmp_ver,const PreservedParams & pp,RestoredData & r_data)860 HSaveError ReadRoomStates(Stream *in, int32_t cmp_ver, const PreservedParams &pp, RestoredData &r_data) {
861 	HSaveError err;
862 	int roomstat_count = in->ReadInt32();
863 	for (; roomstat_count > 0; --roomstat_count) {
864 		int id = in->ReadInt32();
865 		// If id == -1, then the player has not been there yet (or room state was reset)
866 		if (id != -1) {
867 			if (!AssertCompatRange(err, id, 0, MAX_ROOMS - 1, "room index"))
868 				return err;
869 			if (!AssertFormatTagStrict(err, in, "RoomState", true))
870 				return err;
871 			RoomStatus *roomstat = getRoomStatus(id);
872 			roomstat->ReadFromSavegame(in);
873 			if (!AssertFormatTagStrict(err, in, "RoomState", false))
874 				return err;
875 		}
876 	}
877 	return HSaveError::None();
878 }
879 
WriteThisRoom(Stream * out)880 HSaveError WriteThisRoom(Stream *out) {
881 	out->WriteInt32(_G(displayed_room));
882 	if (_G(displayed_room) < 0)
883 		return HSaveError::None();
884 
885 	// modified room backgrounds
886 	for (int i = 0; i < MAX_ROOM_BGFRAMES; ++i) {
887 		out->WriteBool(_GP(play).raw_modified[i] != 0);
888 		if (_GP(play).raw_modified[i])
889 			serialize_bitmap(_GP(thisroom).BgFrames[i].Graphic.get(), out);
890 	}
891 	out->WriteBool(_G(raw_saved_screen) != nullptr);
892 	if (_G(raw_saved_screen))
893 		serialize_bitmap(_G(raw_saved_screen), out);
894 
895 	// room region state
896 	for (int i = 0; i < MAX_ROOM_REGIONS; ++i) {
897 		out->WriteInt32(_GP(thisroom).Regions[i].Light);
898 		out->WriteInt32(_GP(thisroom).Regions[i].Tint);
899 	}
900 	for (int i = 0; i < MAX_WALK_AREAS + 1; ++i) {
901 		out->WriteInt32(_GP(thisroom).WalkAreas[i].ScalingFar);
902 		out->WriteInt32(_GP(thisroom).WalkAreas[i].ScalingNear);
903 	}
904 
905 	// room object movement paths cache
906 	out->WriteInt32(_GP(thisroom).ObjectCount + 1);
907 	for (size_t i = 0; i < _GP(thisroom).ObjectCount + 1; ++i) {
908 		_G(mls)[i].WriteToFile(out);
909 	}
910 
911 	// room music volume
912 	out->WriteInt32(_GP(thisroom).Options.MusicVolume);
913 
914 	// persistent room's indicator
915 	const bool persist = _G(displayed_room) < MAX_ROOMS;
916 	out->WriteBool(persist);
917 	// write the current troom state, in case they save in temporary room
918 	if (!persist)
919 		_GP(troom).WriteToSavegame(out);
920 	return HSaveError::None();
921 }
922 
ReadThisRoom(Stream * in,int32_t cmp_ver,const PreservedParams & pp,RestoredData & r_data)923 HSaveError ReadThisRoom(Stream *in, int32_t cmp_ver, const PreservedParams &pp, RestoredData &r_data) {
924 	HSaveError err;
925 	_G(displayed_room) = in->ReadInt32();
926 	if (_G(displayed_room) < 0)
927 		return err;
928 
929 	// modified room backgrounds
930 	for (int i = 0; i < MAX_ROOM_BGFRAMES; ++i) {
931 		_GP(play).raw_modified[i] = in->ReadBool();
932 		if (_GP(play).raw_modified[i])
933 			r_data.RoomBkgScene[i].reset(read_serialized_bitmap(in));
934 		else
935 			r_data.RoomBkgScene[i] = nullptr;
936 	}
937 	if (in->ReadBool())
938 		_G(raw_saved_screen) = read_serialized_bitmap(in);
939 
940 	// room region state
941 	for (int i = 0; i < MAX_ROOM_REGIONS; ++i) {
942 		r_data.RoomLightLevels[i] = in->ReadInt32();
943 		r_data.RoomTintLevels[i] = in->ReadInt32();
944 	}
945 	for (int i = 0; i < MAX_WALK_AREAS + 1; ++i) {
946 		r_data.RoomZoomLevels1[i] = in->ReadInt32();
947 		r_data.RoomZoomLevels2[i] = in->ReadInt32();
948 	}
949 
950 	// room object movement paths cache
951 	int objmls_count = in->ReadInt32();
952 	if (!AssertCompatLimit(err, objmls_count, CHMLSOFFS, "room object move lists"))
953 		return err;
954 	for (int i = 0; i < objmls_count; ++i) {
955 		err = _G(mls)[i].ReadFromFile(in, cmp_ver > 0 ? 1 : 0);
956 		if (!err)
957 			return err;
958 	}
959 
960 	// save the new room music vol for later use
961 	r_data.RoomVolume = (RoomVolumeMod)in->ReadInt32();
962 
963 	// read the current troom state, in case they saved in temporary room
964 	if (!in->ReadBool())
965 		_GP(troom).ReadFromSavegame(in);
966 
967 	return HSaveError::None();
968 }
969 
WriteManagedPool(Stream * out)970 HSaveError WriteManagedPool(Stream *out) {
971 	ccSerializeAllObjects(out);
972 	return HSaveError::None();
973 }
974 
ReadManagedPool(Stream * in,int32_t cmp_ver,const PreservedParams & pp,RestoredData & r_data)975 HSaveError ReadManagedPool(Stream *in, int32_t cmp_ver, const PreservedParams &pp, RestoredData &r_data) {
976 	if (ccUnserializeAllObjects(in, &_GP(ccUnserializer))) {
977 		return new SavegameError(kSvgErr_GameObjectInitFailed,
978 		                         String::FromFormat("Managed pool deserialization failed: %s", _G(ccErrorString).GetCStr()));
979 	}
980 	return HSaveError::None();
981 }
982 
WritePluginData(Stream * out)983 HSaveError WritePluginData(Stream *out) {
984 	auto pluginFileHandle = AGSE_SAVEGAME;
985 	pl_set_file_handle(pluginFileHandle, out);
986 	pl_run_plugin_hooks(AGSE_SAVEGAME, pluginFileHandle);
987 	pl_clear_file_handle();
988 	return HSaveError::None();
989 }
990 
ReadPluginData(Stream * in,int32_t cmp_ver,const PreservedParams & pp,RestoredData & r_data)991 HSaveError ReadPluginData(Stream *in, int32_t cmp_ver, const PreservedParams &pp, RestoredData &r_data) {
992 	auto pluginFileHandle = AGSE_RESTOREGAME;
993 	pl_set_file_handle(pluginFileHandle, in);
994 	pl_run_plugin_hooks(AGSE_RESTOREGAME, pluginFileHandle);
995 	pl_clear_file_handle();
996 	return HSaveError::None();
997 }
998 
999 
1000 // Description of a supported game state serialization component
1001 struct ComponentHandler {
1002 	String             Name;    // internal component's ID
1003 	int32_t            Version; // current version to write and the highest supported version
1004 	int32_t            LowestVersion; // lowest supported version that the engine can read
1005 	HSaveError(*Serialize)(Stream *);
1006 	HSaveError(*Unserialize)(Stream *, int32_t cmp_ver, const PreservedParams &, RestoredData &);
1007 };
1008 
1009 // Array of supported components
1010 ComponentHandler ComponentHandlers[] = {
1011 	{
1012 		"Game State",
1013 		kGSSvgVersion_350_10,
1014 		kGSSvgVersion_Initial,
1015 		WriteGameState,
1016 		ReadGameState
1017 	},
1018 	{
1019 		"Audio",
1020 		1,
1021 		0,
1022 		WriteAudio,
1023 		ReadAudio
1024 	},
1025 	{
1026 		"Characters",
1027 		1,
1028 		0,
1029 		WriteCharacters,
1030 		ReadCharacters
1031 	},
1032 	{
1033 		"Dialogs",
1034 		0,
1035 		0,
1036 		WriteDialogs,
1037 		ReadDialogs
1038 	},
1039 	{
1040 		"GUI",
1041 		kGuiSvgVersion_350,
1042 		kGuiSvgVersion_Initial,
1043 		WriteGUI,
1044 		ReadGUI
1045 	},
1046 	{
1047 		"Inventory Items",
1048 		0,
1049 		0,
1050 		WriteInventory,
1051 		ReadInventory
1052 	},
1053 	{
1054 		"Mouse Cursors",
1055 		0,
1056 		0,
1057 		WriteMouseCursors,
1058 		ReadMouseCursors
1059 	},
1060 	{
1061 		"Views",
1062 		0,
1063 		0,
1064 		WriteViews,
1065 		ReadViews
1066 	},
1067 	{
1068 		"Dynamic Sprites",
1069 		0,
1070 		0,
1071 		WriteDynamicSprites,
1072 		ReadDynamicSprites
1073 	},
1074 	{
1075 		"Overlays",
1076 		1,
1077 		0,
1078 		WriteOverlays,
1079 		ReadOverlays
1080 	},
1081 	{
1082 		"Dynamic Surfaces",
1083 		0,
1084 		0,
1085 		WriteDynamicSurfaces,
1086 		ReadDynamicSurfaces
1087 	},
1088 	{
1089 		"Script Modules",
1090 		0,
1091 		0,
1092 		WriteScriptModules,
1093 		ReadScriptModules
1094 	},
1095 	{
1096 		"Room States",
1097 		0,
1098 		0,
1099 		WriteRoomStates,
1100 		ReadRoomStates
1101 	},
1102 	{
1103 		"Loaded Room State",
1104 		1,
1105 		0,
1106 		WriteThisRoom,
1107 		ReadThisRoom
1108 	},
1109 	{
1110 		"Managed Pool",
1111 		0,
1112 		0,
1113 		WriteManagedPool,
1114 		ReadManagedPool
1115 	},
1116 	{
1117 		"Plugin Data",
1118 		0,
1119 		0,
1120 		WritePluginData,
1121 		ReadPluginData
1122 	},
1123 	{ nullptr, 0, 0, nullptr, nullptr } // end of array
1124 };
1125 
1126 
1127 typedef std::map<String, ComponentHandler> HandlersMap;
GenerateHandlersMap(HandlersMap & map)1128 void GenerateHandlersMap(HandlersMap &map) {
1129 	map.clear();
1130 	for (int i = 0; !ComponentHandlers[i].Name.IsEmpty(); ++i)
1131 		map[ComponentHandlers[i].Name] = ComponentHandlers[i];
1132 }
1133 
1134 // A helper struct to pass to (de)serialization handlers
1135 struct SvgCmpReadHelper {
1136 	SavegameVersion       Version;  // general savegame version
1137 	const PreservedParams &PP;      // previous game state kept for reference
1138 	RestoredData          &RData;   // temporary storage for loaded data, that
1139 	// will be applied after loading is done
1140 	// The map of serialization handlers, one per supported component type ID
1141 	HandlersMap            Handlers;
1142 
SvgCmpReadHelperAGS3::AGS::Engine::SavegameComponents::SvgCmpReadHelper1143 	SvgCmpReadHelper(SavegameVersion svg_version, const PreservedParams &pp, RestoredData &r_data)
1144 		: Version(svg_version)
1145 		, PP(pp)
1146 		, RData(r_data) {
1147 	}
1148 };
1149 
1150 // The basic information about deserialized component, used for debugging purposes
1151 struct ComponentInfo {
1152 	String  Name;       // internal component's ID
1153 	int32_t Version;    // data format version
1154 	soff_t  Offset;     // offset at which an opening tag is located
1155 	soff_t  DataOffset; // offset at which component data begins
1156 	soff_t  DataSize;   // expected size of component data
1157 
ComponentInfoAGS3::AGS::Engine::SavegameComponents::ComponentInfo1158 	ComponentInfo() : Version(-1), Offset(0), DataOffset(0), DataSize(0) {}
1159 };
1160 
ReadComponent(Stream * in,SvgCmpReadHelper & hlp,ComponentInfo & info)1161 HSaveError ReadComponent(Stream *in, SvgCmpReadHelper &hlp, ComponentInfo &info) {
1162 	info = ComponentInfo(); // reset in case of early error
1163 	info.Offset = in->GetPosition();
1164 	if (!ReadFormatTag(in, info.Name, true))
1165 		return new SavegameError(kSvgErr_ComponentOpeningTagFormat);
1166 	info.Version = in->ReadInt32();
1167 	info.DataSize = hlp.Version >= kSvgVersion_Cmp_64bit ? in->ReadInt64() : in->ReadInt32();
1168 	info.DataOffset = in->GetPosition();
1169 
1170 	// WORKAROUND: For some period, the component "Dynamic Surfaces"
1171 	// was mis-named as "Drawing Surfaces"
1172 	String componentName = info.Name;
1173 	if (componentName == "Drawing Surfaces")
1174 		componentName = "Dynamic Surfaces";
1175 
1176 	const ComponentHandler *handler = nullptr;
1177 	std::map<String, ComponentHandler>::const_iterator it_hdr = hlp.Handlers.find(componentName);
1178 	if (it_hdr != hlp.Handlers.end())
1179 		handler = &it_hdr->_value;
1180 
1181 	// WORKAROUND: Managed Pool was incorrectly set as version 1
1182 	// in the codebase originally imported to ScummVM
1183 	if (componentName == "Managed Pool" && info.Version == 1
1184 		&& handler && handler->Version == 0)
1185 		info.Version = 0;
1186 
1187 	if (!handler || !handler->Unserialize)
1188 		return new SavegameError(kSvgErr_UnsupportedComponent);
1189 	if (info.Version > handler->Version || info.Version < handler->LowestVersion)
1190 		return new SavegameError(kSvgErr_UnsupportedComponentVersion, String::FromFormat("Saved version: %d, supported: %d - %d", info.Version, handler->LowestVersion, handler->Version));
1191 	HSaveError err = handler->Unserialize(in, info.Version, hlp.PP, hlp.RData);
1192 	if (!err)
1193 		return err;
1194 	if (in->GetPosition() - info.DataOffset != info.DataSize)
1195 		return new SavegameError(kSvgErr_ComponentSizeMismatch, String::FromFormat("Expected: %lld, actual: %lld", info.DataSize, in->GetPosition() - info.DataOffset));
1196 	if (!AssertFormatTag(in, info.Name, false))
1197 		return new SavegameError(kSvgErr_ComponentClosingTagFormat);
1198 	return HSaveError::None();
1199 }
1200 
ReadAll(Stream * in,SavegameVersion svg_version,const PreservedParams & pp,RestoredData & r_data)1201 HSaveError ReadAll(Stream *in, SavegameVersion svg_version, const PreservedParams &pp, RestoredData &r_data) {
1202 	// Prepare a helper struct we will be passing to the block reading proc
1203 	SvgCmpReadHelper hlp(svg_version, pp, r_data);
1204 	GenerateHandlersMap(hlp.Handlers);
1205 
1206 	size_t idx = 0;
1207 	if (!AssertFormatTag(in, ComponentListTag, true))
1208 		return new SavegameError(kSvgErr_ComponentListOpeningTagFormat);
1209 	do {
1210 		// Look out for the end of the component list:
1211 		// this is the only way how this function ends with success
1212 		soff_t off = in->GetPosition();
1213 		if (AssertFormatTag(in, ComponentListTag, false))
1214 			return HSaveError::None();
1215 		// If the list's end was not detected, then seek back and continue reading
1216 		in->Seek(off, kSeekBegin);
1217 
1218 		ComponentInfo info;
1219 		HSaveError err = ReadComponent(in, hlp, info);
1220 		if (!err) {
1221 			return new SavegameError(kSvgErr_ComponentUnserialization,
1222 			    String::FromFormat("(#%d) %s, version %i, at offset %lld.",
1223 					idx, info.Name.IsEmpty() ? "unknown" : info.Name.GetCStr(), info.Version, info.Offset),
1224 				err);
1225 		}
1226 		update_polled_stuff_if_runtime();
1227 		idx++;
1228 	} while (!in->EOS());
1229 	return new SavegameError(kSvgErr_ComponentListClosingTagMissing);
1230 }
1231 
WriteComponent(Stream * out,ComponentHandler & hdlr)1232 HSaveError WriteComponent(Stream *out, ComponentHandler &hdlr) {
1233 	WriteFormatTag(out, hdlr.Name, true);
1234 	out->WriteInt32(hdlr.Version);
1235 	soff_t ref_pos = out->GetPosition();
1236 	out->WriteInt64(0); // placeholder for the component size
1237 	HSaveError err = hdlr.Serialize(out);
1238 	soff_t end_pos = out->GetPosition();
1239 	out->Seek(ref_pos, kSeekBegin);
1240 	out->WriteInt64(end_pos - ref_pos - sizeof(int64_t)); // size of serialized component data
1241 	out->Seek(end_pos, kSeekBegin);
1242 	if (err)
1243 		WriteFormatTag(out, hdlr.Name, false);
1244 	return err;
1245 }
1246 
WriteAllCommon(Stream * out)1247 HSaveError WriteAllCommon(Stream *out) {
1248 	WriteFormatTag(out, ComponentListTag, true);
1249 	for (int type = 0; !ComponentHandlers[type].Name.IsEmpty(); ++type) {
1250 		HSaveError err = WriteComponent(out, ComponentHandlers[type]);
1251 		if (!err) {
1252 			return new SavegameError(kSvgErr_ComponentSerialization,
1253 			                         String::FromFormat("Component: (#%d) %s", type, ComponentHandlers[type].Name.GetCStr()),
1254 			                         err);
1255 		}
1256 		update_polled_stuff_if_runtime();
1257 	}
1258 	WriteFormatTag(out, ComponentListTag, false);
1259 	return HSaveError::None();
1260 }
1261 
1262 } // namespace SavegameBlocks
1263 } // namespace Engine
1264 } // namespace AGS
1265 } // namespace AGS3
1266