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  * Additional copyright for this file:
8  * Copyright (C) 1994-1998 Revolution Software Ltd.
9  *
10  * This program is free software; you can redistribute it and/or
11  * modify it under the terms of the GNU General Public License
12  * as published by the Free Software Foundation; either version 2
13  * of the License, or (at your option) any later version.
14  *
15  * This program is distributed in the hope that it will be useful,
16  * but WITHOUT ANY WARRANTY; without even the implied warranty of
17  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
18  * GNU General Public License for more details.
19  *
20  * You should have received a copy of the GNU General Public License
21  * along with this program; if not, write to the Free Software
22  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
23  */
24 
25 // ---------------------------------------------------------------------------
26 // SAVE_REST.CPP	save, restore & restart functions
27 //
28 // James 05feb97
29 //
30 // "Jesus Saves", but could he Restore or Restart? He can now...
31 //
32 // ---------------------------------------------------------------------------
33 
34 
35 #include "common/memstream.h"
36 #include "common/savefile.h"
37 #include "common/textconsole.h"
38 
39 #include "sword2/sword2.h"
40 #include "sword2/defs.h"
41 #include "sword2/header.h"
42 #include "sword2/logic.h"
43 #include "sword2/object.h"
44 #include "sword2/mouse.h"
45 #include "sword2/resman.h"
46 #include "sword2/saveload.h"
47 #include "sword2/screen.h"
48 #include "sword2/sound.h"
49 
50 namespace Sword2 {
51 
getSaveFileName(uint16 slotNo)52 Common::String Sword2Engine::getSaveFileName(uint16 slotNo) {
53 	return Common::String::format("%s.%.3d", _targetName.c_str(), slotNo);
54 }
55 
56 /**
57  * Calculate size of required savegame buffer. A savegame consists of a header
58  * and the global variables.
59  */
60 
findBufferSize()61 uint32 Sword2Engine::findBufferSize() {
62 	return 212 + _resman->fetchLen(1);
63 }
64 
65 /**
66  * Save the game.
67  */
68 
saveGame(uint16 slotNo,const byte * desc)69 uint32 Sword2Engine::saveGame(uint16 slotNo, const byte *desc) {
70 	char description[SAVE_DESCRIPTION_LEN];
71 	uint32 bufferSize = findBufferSize();
72 	byte *saveBuffer = (byte *)malloc(bufferSize);
73 	ScreenInfo *screenInfo = _screen->getScreenInfo();
74 
75 	memset(description, 0, sizeof(description));
76 	strncpy(description, (const char *)desc, SAVE_DESCRIPTION_LEN - 1);
77 
78 	Common::MemoryWriteStream writeS(saveBuffer, bufferSize);
79 
80 	byte *globalVars = _resman->openResource(1);
81 	byte *objectHub = _resman->openResource(CUR_PLAYER_ID) + ResHeader::size();
82 
83 	// Script no. 7 - 'george_savedata_request' calls fnPassPlayerSaveData
84 	_logic->runResScript(CUR_PLAYER_ID, 7);
85 
86 	writeS.writeUint32LE(0);	// Checksum
87 	writeS.write(description, SAVE_DESCRIPTION_LEN);
88 	writeS.writeUint32LE(_resman->fetchLen(1));
89 	writeS.writeUint32LE(screenInfo->background_layer_id);
90 	writeS.writeUint32LE(_logic->getRunList());
91 	writeS.writeUint32LE(screenInfo->feet_x);
92 	writeS.writeUint32LE(screenInfo->feet_y);
93 	writeS.writeUint32LE(_sound->getLoopingMusicId());
94 	writeS.write(objectHub, ObjectHub::size());
95 	writeS.write(_logic->_saveLogic, ObjectLogic::size());
96 	writeS.write(_logic->_saveGraphic, ObjectGraphic::size());
97 	writeS.write(_logic->_saveMega, ObjectMega::size());
98 	writeS.write(globalVars, _resman->fetchLen(1));
99 
100 	WRITE_LE_UINT32(saveBuffer, calcChecksum(saveBuffer + 4, bufferSize - 4));
101 
102 	_resman->closeResource(CUR_PLAYER_ID);
103 	_resman->closeResource(1);
104 
105 	uint32 errorCode = saveData(slotNo, saveBuffer, bufferSize);
106 
107 	free(saveBuffer);
108 
109 	if (errorCode != SR_OK) {
110 		uint32 textId;
111 
112 		switch (errorCode) {
113 		case SR_ERR_FILEOPEN:
114 			textId = TEXT_SAVE_CANT_OPEN;
115 			break;
116 		default:
117 			textId = TEXT_SAVE_FAILED;
118 			break;
119 		}
120 
121 		_screen->displayMsg(fetchTextLine(_resman->openResource(textId / SIZE), textId & 0xffff) + 2, 0);
122 	}
123 
124 	return errorCode;
125 }
126 
saveData(uint16 slotNo,byte * buffer,uint32 bufferSize)127 uint32 Sword2Engine::saveData(uint16 slotNo, byte *buffer, uint32 bufferSize) {
128 	Common::String saveFileName = getSaveFileName(slotNo);
129 
130 	Common::OutSaveFile *out;
131 
132 	if (!(out = _saveFileMan->openForSaving(saveFileName))) {
133 		return SR_ERR_FILEOPEN;
134 	}
135 
136 	out->write(buffer, bufferSize);
137 	out->finalize();
138 
139 	if (!out->err()) {
140 		delete out;
141 		return SR_OK;
142 	}
143 
144 	delete out;
145 	return SR_ERR_WRITEFAIL;
146 }
147 
148 /**
149  * Restore the game.
150  */
151 
restoreGame(uint16 slotNo)152 uint32 Sword2Engine::restoreGame(uint16 slotNo) {
153 	uint32 bufferSize = findBufferSize();
154 	byte *saveBufferMem = (byte *)malloc(bufferSize);
155 
156 	uint32 errorCode = restoreData(slotNo, saveBufferMem, bufferSize);
157 
158 	// If it was read in successfully, then restore the game from the
159 	// buffer & free the buffer. Note that restoreFromBuffer() frees the
160 	// buffer in order to clear it from memory before loading in the new
161 	// screen and runlist, so we only need to free it in case of failure.
162 
163 	if (errorCode == SR_OK)
164 		errorCode = restoreFromBuffer(saveBufferMem, bufferSize);
165 	else
166 		free(saveBufferMem);
167 
168 	if (errorCode != SR_OK) {
169 		uint32 textId;
170 
171 		switch (errorCode) {
172 		case SR_ERR_FILEOPEN:
173 			textId = TEXT_RESTORE_CANT_OPEN;
174 			break;
175 		case SR_ERR_INCOMPATIBLE:
176 			textId = TEXT_RESTORE_INCOMPATIBLE;
177 			break;
178 		default:
179 			textId = TEXT_RESTORE_FAILED;
180 			break;
181 		}
182 
183 		_screen->displayMsg(fetchTextLine(_resman->openResource(textId / SIZE), textId & 0xffff) + 2, 0);
184 	} else {
185 		// Prime system with a game cycle
186 
187 		// Reset the graphic 'BuildUnit' list before a new logic list
188 		// (see fnRegisterFrame)
189 		_screen->resetRenderLists();
190 
191 		// Reset the mouse hot-spot list. See fnRegisterMouse()
192 		// and fnRegisterFrame()
193 		_mouse->resetMouseList();
194 
195 		if (_logic->processSession())
196 			error("restore 1st cycle failed??");
197 	}
198 
199 	// Force the game engine to pick a cursor. This appears to be needed
200 	// when using the -x command-line option to restore a game.
201 	_mouse->setMouseTouching(1);
202 	return errorCode;
203 }
204 
restoreData(uint16 slotNo,byte * buffer,uint32 bufferSize)205 uint32 Sword2Engine::restoreData(uint16 slotNo, byte *buffer, uint32 bufferSize) {
206 	Common::String saveFileName = getSaveFileName(slotNo);
207 
208 	Common::InSaveFile *in;
209 
210 	if (!(in = _saveFileMan->openForLoading(saveFileName))) {
211 		// error: couldn't open file
212 		return SR_ERR_FILEOPEN;
213 	}
214 
215 	// Read savegame into the buffer
216 	uint32 itemsRead = in->read(buffer, bufferSize);
217 
218 	delete in;
219 
220 	if (itemsRead != bufferSize) {
221 		// We didn't get all of it. At the moment we have no way of
222 		// knowing why, so assume that it's an incompatible savegame.
223 
224 		return SR_ERR_INCOMPATIBLE;
225 	}
226 
227 	return SR_OK;
228 }
229 
restoreFromBuffer(byte * buffer,uint32 size)230 uint32 Sword2Engine::restoreFromBuffer(byte *buffer, uint32 size) {
231 	Common::MemoryReadStream readS(buffer, size);
232 
233 	// Calc checksum & check that aginst the value stored in the header
234 
235 	if (readS.readUint32LE() != calcChecksum(buffer + 4, size - 4)) {
236 		free(buffer);
237 		return SR_ERR_INCOMPATIBLE;
238 	}
239 
240 	readS.seek(SAVE_DESCRIPTION_LEN, SEEK_CUR);
241 
242 	// Check savegame against length of current global variables resource
243 	// This would most probably be trapped by the checksum test anyway,
244 	// but it doesn't do any harm to check this as well.
245 
246 	// Historical note: During development, earlier savegames would often
247 	// be shorter than the current expected length.
248 
249 	if (readS.readUint32LE() != _resman->fetchLen(1)) {
250 		free(buffer);
251 		return SR_ERR_INCOMPATIBLE;
252 	}
253 
254 	byte *globalVars = _resman->openResource(1);
255 	byte *objectHub = _resman->openResource(CUR_PLAYER_ID) + ResHeader::size();
256 
257 	uint32 screenId = readS.readUint32LE();
258 	uint32 runListId = readS.readUint32LE();
259 	uint32 feetX = readS.readUint32LE();
260 	uint32 feetY = readS.readUint32LE();
261 	uint32 musicId = readS.readUint32LE();
262 
263 	// Trash all resources from memory except player object & global vars
264 	_resman->killAll(false);
265 	_logic->resetKillList();
266 
267 	readS.read(objectHub, ObjectHub::size());
268 	readS.read(_logic->_saveLogic, ObjectLogic::size());
269 	readS.read(_logic->_saveGraphic, ObjectGraphic::size());
270 	readS.read(_logic->_saveMega, ObjectMega::size());
271 
272 	// Fill out the player object structures from the savegame structures.
273 	// Also run the appropriate scripts to set up George's anim tables and
274 	// walkdata, and Nico's anim tables.
275 
276 	// Script no. 8 - 'george_savedata_return' calls fnGetPlayerSaveData
277 	_logic->runResScript(CUR_PLAYER_ID, 8);
278 
279 	// Script no. 14 - 'set_up_nico_anim_tables'
280 	_logic->runResScript(CUR_PLAYER_ID, 14);
281 
282 	// Which megaset was the player at the time of saving?
283 	ObjectMega obMega(_logic->_saveMega);
284 
285 	uint32 scriptNo = 0;
286 
287 	switch (obMega.getMegasetRes()) {
288 	case 36:		// GeoMega:
289 		scriptNo = 9;	// script no.9	- 'player_is_george'
290 		break;
291 	case 2003:		// GeoMegaB:
292 		scriptNo = 13;	// script no.13 - 'player_is_georgeB'
293 		break;
294 	case 1366:		// NicMegaA:
295 		scriptNo = 11;	// script no.11 - 'player_is_nicoA'
296 		break;
297 	case 1437:		// NicMegaB:
298 		scriptNo = 12;	// script no.12 - 'player_is_nicoB'
299 		break;
300 	case 1575:		// NicMegaC:
301 		scriptNo = 10;	// script no.10 - 'player_is_nicoC'
302 		break;
303 	}
304 
305 	_logic->runResScript(CUR_PLAYER_ID, scriptNo);
306 
307 	// Copy variables from savegame buffer to memory
308 	readS.read(globalVars, _resman->fetchLen(1));
309 
310 	_resman->closeResource(CUR_PLAYER_ID);
311 	_resman->closeResource(1);
312 
313 	free(buffer);
314 
315 	int32 pars[2];
316 
317 	pars[0] = screenId;
318 	pars[1] = 1;
319 	_logic->fnInitBackground(pars);
320 
321 	ScreenInfo *screenInfo = _screen->getScreenInfo();
322 
323 	// So palette not restored immediately after control panel - we want to
324 	// fade up instead!
325 	screenInfo->new_palette = 99;
326 
327 	// These need setting after the defaults get set in fnInitBackground.
328 	// Remember that these can change through the game, so need saving &
329 	// restoring too.
330 
331 	screenInfo->feet_x = feetX;
332 	screenInfo->feet_y = feetY;
333 
334 	// Start the new run list
335 	_logic->expressChangeSession(runListId);
336 
337 	// Force in the new scroll position, so unsightly scroll-catch-up does
338 	// not occur when screen first draws after returning from restore panel
339 
340 	// Set the screen record of player position - ready for setScrolling()
341 
342 	screenInfo->player_feet_x = obMega.getFeetX();
343 	screenInfo->player_feet_y = obMega.getFeetY();
344 
345 	// if this screen is wide, recompute the scroll offsets now
346 	if (screenInfo->scroll_flag)
347 		_screen->setScrolling();
348 
349 	// Any music required will be started after we've returned from
350 	// restoreControl() - see systemMenuMouse() in mouse.cpp!
351 
352 	// Restart any looping music. Originally this was - and still is - done
353 	// in systemMenuMouse(), but with ScummVM we have other ways of
354 	// restoring savegames so it's easier to put it here as well.
355 
356 	if (musicId) {
357 		pars[0] = musicId;
358 		pars[1] = FX_LOOP;
359 		_logic->fnPlayMusic(pars);
360 	} else
361 		_logic->fnStopMusic(NULL);
362 
363 	return SR_OK;
364 }
365 
366 /**
367  * Get the description of a savegame
368  */
369 
getSaveDescription(uint16 slotNo,byte * description)370 uint32 Sword2Engine::getSaveDescription(uint16 slotNo, byte *description) {
371 	Common::String saveFileName = getSaveFileName(slotNo);
372 
373 	Common::InSaveFile *in;
374 
375 	if (!(in = _saveFileMan->openForLoading(saveFileName))) {
376 		return SR_ERR_FILEOPEN;
377 	}
378 
379 	in->readUint32LE();
380 	in->read(description, SAVE_DESCRIPTION_LEN);
381 
382 	delete in;
383 	return SR_OK;
384 }
385 
saveExists()386 bool Sword2Engine::saveExists() {
387 	Common::String pattern = _targetName + ".???";
388 	Common::StringArray filenames = _saveFileMan->listSavefiles(pattern);
389 
390 	return !filenames.empty();
391 }
392 
saveExists(uint16 slotNo)393 bool Sword2Engine::saveExists(uint16 slotNo) {
394 	Common::String saveFileName = getSaveFileName(slotNo);
395 	Common::InSaveFile *in;
396 
397 	if (!(in = _saveFileMan->openForLoading(saveFileName))) {
398 		return false;
399 	}
400 
401 	delete in;
402 	return true;
403 }
404 
calcChecksum(byte * buffer,uint32 size)405 uint32 Sword2Engine::calcChecksum(byte *buffer, uint32 size) {
406 	uint32 total = 0;
407 
408 	for (uint32 pos = 0; pos < size; pos++)
409 		total += buffer[pos];
410 
411 	return total;
412 }
413 
414 } // End of namespace Sword2
415