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 "gui/saveload.h"
24 #include "common/config-manager.h"
25 #include "common/savefile.h"
26 #include "common/translation.h"
27 
28 #include "trecision/actor.h"
29 #include "trecision/dialog.h"
30 #include "trecision/graphics.h"
31 #include "trecision/logic.h"
32 #include "trecision/pathfinding3d.h"
33 #include "trecision/sound.h"
34 #include "trecision/trecision.h"
35 #include "trecision/video.h"
36 
37 namespace Trecision {
38 
loadSaveSlots(Common::StringArray & saveNames)39 void TrecisionEngine::loadSaveSlots(Common::StringArray &saveNames) {
40 	for (uint i = 0; i < ICONSHOWN; ++i) {
41 		SaveStateDescriptor saveState = getMetaEngine()->querySaveMetaInfos(_targetName.c_str(), i + 1);
42 		if (saveState.getSaveSlot() == -1) {
43 			saveNames.push_back(_sysText[kMessageEmptySpot]);
44 			_inventory.push_back(EMPTYSLOT);
45 		} else {
46 			saveNames.push_back(saveState.getDescription());
47 			_inventory.push_back(EMPTYSLOT + i + 1);
48 			_graphicsMgr->setSaveSlotThumbnail(i, saveState.getThumbnail());
49 		}
50 	}
51 
52 	refreshInventory(0, 0);
53 }
54 
dataSave()55 bool TrecisionEngine::dataSave() {
56 	const Common::Array<byte> savedInventory = _inventory;
57 	const uint8 savedIconBase = _iconBase;
58 	Common::StringArray saveNames;
59 	saveNames.reserve(MAXSAVEFILE);
60 	uint16 posx, LenText;
61 	bool ret = true;
62 
63 	_actor->actorStop();
64 	_pathFind->nextStep();
65 
66 	if (!ConfMan.getBool("originalsaveload")) {
67 		GUI::SaveLoadChooser *dialog = new GUI::SaveLoadChooser(_("Save game:"), _("Save"), true);
68 		int saveSlot = dialog->runModalWithCurrentTarget();
69 		Common::String saveName = dialog->getResultString();
70 		bool skipSave = saveSlot == -1;
71 		delete dialog;
72 
73 		// Remove the mouse click event from the save/load dialog
74 		eventLoop();
75 		_mouseLeftBtn = _mouseRightBtn = false;
76 
77 		if (!skipSave)
78 			saveGameState(saveSlot, saveName);
79 
80 		return skipSave;
81 	}
82 
83 	_graphicsMgr->clearScreenBufferTop();
84 
85 	SDText drawText;
86 	drawText.set(
87 		Common::Rect(0, TOP - 20, MAXX, CARHEI + (TOP - 20)),
88 		Common::Rect(0, 0, MAXX, CARHEI),
89 		MOUSECOL,
90 		_sysText[kMessageSavePosition]);
91 	drawText.draw(this);
92 
93 	_graphicsMgr->copyToScreen(0, 0, MAXX, TOP);
94 
95 	_graphicsMgr->clearScreenBufferInventory();
96 	_graphicsMgr->copyToScreen(0, TOP + AREA, MAXX, TOP);
97 
98 	_scheduler->resetQueues();
99 
100 	freeKey();
101 
102 	// Reset the inventory and turn it into save slots
103 	_inventory.clear();
104 	_iconBase = 0;
105 
106 insave:
107 
108 	int8 CurPos = -1;
109 	int8 OldPos = -1;
110 	bool skipSave = false;
111 
112 	loadSaveSlots(saveNames);
113 
114 	for (;;) {
115 		checkSystem();
116 		getKey();
117 
118 		int16 mx = _mousePos.x;
119 		int16 my = _mousePos.y;
120 
121 		if (my >= FIRSTLINE &&
122 			my < FIRSTLINE + ICONDY &&
123 			mx >= ICONMARGSX &&
124 			mx < MAXX - ICONMARGDX) {
125 			OldPos = CurPos;
126 			CurPos = ((mx - ICONMARGSX) / ICONDX);
127 
128 			if (OldPos != CurPos) {
129 				_graphicsMgr->clearScreenBufferSaveSlotDescriptions();
130 
131 				posx = ICONMARGSX + ((CurPos) * (ICONDX)) + ICONDX / 2;
132 				LenText = textLength(saveNames[CurPos]);
133 
134 				posx = CLIP(posx - (LenText / 2), 2, MAXX - 2 - LenText);
135 				drawText.set(
136 					Common::Rect(posx, FIRSTLINE + ICONDY + 10, LenText + posx, CARHEI + (FIRSTLINE + ICONDY + 10)),
137 					Common::Rect(0, 0, LenText, CARHEI),
138 					MOUSECOL,
139 					saveNames[CurPos].c_str());
140 				drawText.draw(this);
141 
142 				_graphicsMgr->copyToScreen(0, FIRSTLINE + ICONDY + 10, MAXX, CARHEI);
143 			}
144 
145 			if (_mouseLeftBtn) {
146 				_mouseLeftBtn = false;
147 				break;
148 			}
149 		} else {
150 			if (OldPos != -1) {
151 				_graphicsMgr->clearScreenBufferSaveSlotDescriptions();
152 				_graphicsMgr->copyToScreen(0, FIRSTLINE + ICONDY + 10, MAXX, CARHEI);
153 			}
154 
155 			OldPos = -1;
156 			CurPos = -1;
157 
158 			if (_mouseLeftBtn || _mouseRightBtn) {
159 				_mouseLeftBtn = _mouseRightBtn = false;
160 				skipSave = true;
161 				break;
162 			}
163 		}
164 	}
165 
166 	if (!skipSave) {
167 		if (_inventory[CurPos] == EMPTYSLOT) {
168 			saveNames[CurPos].clear();
169 
170 			_graphicsMgr->clearScreenBufferSaveSlotDescriptions();
171 			_graphicsMgr->copyToScreen(0, FIRSTLINE + ICONDY + 10, MAXX, CARHEI);
172 		}
173 
174 		for (;;) {
175 			_keybInput = true;
176 			checkSystem();
177 			uint16 ch = getKey();
178 			freeKey();
179 
180 			_keybInput = false;
181 
182 			if (ch == 0x1B) {
183 				ch = 0;
184 				_graphicsMgr->clearScreenBufferSaveSlotDescriptions();
185 				_graphicsMgr->copyToScreen(0, FIRSTLINE + ICONDY + 10, MAXX, CARHEI);
186 
187 				goto insave;
188 			}
189 
190 			if (ch == 8) // Backspace
191 				saveNames[CurPos].deleteLastChar();
192 			else if (ch == 13) // Enter
193 				break;
194 			else if (saveNames[CurPos].size() < 39 && Common::isPrint(ch))
195 				saveNames[CurPos] += ch;
196 
197 			_graphicsMgr->clearScreenBufferSaveSlotDescriptions();
198 
199 			saveNames[CurPos] += '_'; // add blinking cursor
200 
201 			posx = ICONMARGSX + ((CurPos) * (ICONDX)) + ICONDX / 2;
202 			LenText = textLength(saveNames[CurPos]);
203 
204 			posx = CLIP(posx - (LenText / 2), 2, MAXX - 2 - LenText);
205 			drawText.set(
206 				Common::Rect(posx, FIRSTLINE + ICONDY + 10, LenText + posx, CARHEI + (FIRSTLINE + ICONDY + 10)),
207 				Common::Rect(0, 0, LenText, CARHEI),
208 				MOUSECOL,
209 				saveNames[CurPos].c_str());
210 
211 			const bool hideLastChar = (readTime() / 8) & 1;
212 			drawText.draw(this, hideLastChar);
213 
214 			saveNames[CurPos].deleteLastChar(); // remove blinking cursor
215 
216 			_graphicsMgr->copyToScreen(0, FIRSTLINE + ICONDY + 10, MAXX, CARHEI);
217 		}
218 
219 		_graphicsMgr->clearScreenBufferInventory();
220 
221 		ret = false;
222 
223 		// Restore the inventory
224 		_inventory = savedInventory;
225 		_curInventory = 0;
226 		_iconBase = savedIconBase;
227 
228 		saveGameState(CurPos + 1, saveNames[CurPos]);
229 	}
230 
231 	_graphicsMgr->clearScreenBufferInventory();
232 	_graphicsMgr->copyToScreen(0, FIRSTLINE, MAXX, TOP);
233 
234 	_graphicsMgr->clearScreenBufferTop();
235 	_graphicsMgr->copyToScreen(0, 0, MAXX, TOP);
236 
237 	// Restore the inventory
238 	_inventory = savedInventory;
239 	_curInventory = 0;
240 	_iconBase = savedIconBase;
241 
242 	return ret;
243 }
244 
dataLoad()245 bool TrecisionEngine::dataLoad() {
246 	const Common::Array<byte> savedInventory = _inventory;
247 	const uint8 savedIconBase = _iconBase;
248 	Common::StringArray saveNames;
249 	saveNames.reserve(MAXSAVEFILE);
250 	bool retval = true;
251 
252 	if (!ConfMan.getBool("originalsaveload")) {
253 		GUI::SaveLoadChooser *dialog = new GUI::SaveLoadChooser(_("Load game:"), _("Load"), false);
254 		int saveSlot = dialog->runModalWithCurrentTarget();
255 		bool skipLoad = saveSlot == -1;
256 		delete dialog;
257 
258 		// Remove the mouse click event from the save/load dialog
259 		eventLoop();
260 		_mouseLeftBtn = _mouseRightBtn = false;
261 
262 		if (!skipLoad)
263 			loadGameState(saveSlot);
264 
265 		return !skipLoad;
266 	}
267 
268 	_graphicsMgr->clearScreenBufferTop();
269 
270 	_graphicsMgr->showCursor();
271 
272 	SDText drawText;
273 	drawText.set(
274 		Common::Rect(0, TOP - 20, MAXX, CARHEI + (TOP - 20)),
275 		Common::Rect(0, 0, MAXX, CARHEI),
276 		MOUSECOL,
277 		_sysText[kMessageLoadPosition]);
278 	drawText.draw(this);
279 
280 	_graphicsMgr->copyToScreen(0, 0, MAXX, TOP);
281 
282 	_graphicsMgr->clearScreenBufferInventory();
283 	_graphicsMgr->copyToScreen(0, TOP + AREA, MAXX, TOP);
284 
285 	_scheduler->resetQueues();
286 
287 	freeKey();
288 
289 	// Reset the inventory and turn it into save slots
290 	_inventory.clear();
291 	_iconBase = 0;
292 
293 	loadSaveSlots(saveNames);
294 
295 	bool skipLoad = false;
296 	int8 curPos = -1;
297 	int8 oldPos = -1;
298 
299 	for (;;) {
300 		checkSystem();
301 		getKey();
302 
303 		if (_mousePos.y >= FIRSTLINE &&
304 			_mousePos.y < (FIRSTLINE + ICONDY) &&
305 			_mousePos.x >= ICONMARGSX &&
306 			(_mousePos.x < (MAXX - ICONMARGDX))) {
307 			oldPos = curPos;
308 			curPos = (_mousePos.x - ICONMARGSX) / ICONDX;
309 
310 			if (oldPos != curPos) {
311 				_graphicsMgr->clearScreenBufferSaveSlotDescriptions();
312 
313 				uint16 posX = ICONMARGSX + ((curPos) * (ICONDX)) + ICONDX / 2;
314 				uint16 lenText = textLength(saveNames[curPos]);
315 				if (posX - (lenText / 2) < 2)
316 					posX = 2;
317 				else
318 					posX = posX - (lenText / 2);
319 				if (posX + lenText > MAXX - 2)
320 					posX = MAXX - 2 - lenText;
321 
322 				drawText.set(
323 					Common::Rect(posX, FIRSTLINE + ICONDY + 10, lenText + posX, CARHEI + (FIRSTLINE + ICONDY + 10)),
324 					Common::Rect(0, 0, lenText, CARHEI),
325 					MOUSECOL,
326 					saveNames[curPos].c_str());
327 				drawText.draw(this);
328 
329 				_graphicsMgr->copyToScreen(0, FIRSTLINE + ICONDY + 10, MAXX, CARHEI);
330 			}
331 
332 			if (_mouseLeftBtn && (_inventory[curPos] != EMPTYSLOT)) {
333 				_mouseLeftBtn = false;
334 				break;
335 			}
336 		} else {
337 			if (oldPos != -1) {
338 				_graphicsMgr->clearScreenBufferSaveSlotDescriptions();
339 				_graphicsMgr->copyToScreen(0, FIRSTLINE + ICONDY + 10, MAXX, CARHEI);
340 			}
341 
342 			oldPos = -1;
343 			curPos = -1;
344 
345 			if (_mouseLeftBtn || _mouseRightBtn) {
346 				_mouseLeftBtn = _mouseRightBtn = false;
347 				retval = false;
348 				skipLoad = true;
349 				break;
350 			}
351 		}
352 	}
353 
354 	if (!skipLoad) {
355 		loadGameState(curPos + 1);
356 	} else {
357 		_actor->actorStop();
358 		_pathFind->nextStep();
359 		checkSystem();
360 
361 		_graphicsMgr->clearScreenBufferInventory();
362 		_graphicsMgr->copyToScreen(0, FIRSTLINE, MAXX, TOP);
363 
364 		_graphicsMgr->clearScreenBufferTop();
365 		_graphicsMgr->copyToScreen(0, 0, MAXX, TOP);
366 
367 		if (_flagScriptActive) {
368 			_graphicsMgr->hideCursor();
369 		}
370 
371 		// Restore the inventory
372 		_inventory = savedInventory;
373 		_curInventory = 0;
374 		_iconBase = savedIconBase;
375 	}
376 
377 	return retval;
378 }
379 
loadGameStream(Common::SeekableReadStream * stream)380 Common::Error TrecisionEngine::loadGameStream(Common::SeekableReadStream *stream) {
381 	const byte version = stream->readByte();
382 	Common::Serializer ser(stream, nullptr);
383 	ser.setVersion(version);
384 	syncGameStream(ser);
385 
386 	_graphicsMgr->clearScreenBufferInventory();
387 
388 	_flagNoPaintScreen = true;
389 	_curStack = 0;
390 	_flagScriptActive = false;
391 
392 	_oldRoom = _curRoom;
393 	changeRoom(_curRoom);
394 
395 	_actor->actorStop();
396 	_pathFind->nextStep();
397 	checkSystem();
398 
399 	_graphicsMgr->clearScreenBufferInventory();
400 	_graphicsMgr->copyToScreen(0, FIRSTLINE, MAXX, TOP);
401 
402 	_graphicsMgr->clearScreenBufferTop();
403 	_graphicsMgr->copyToScreen(0, 0, MAXX, TOP);
404 
405 	if (_flagScriptActive) {
406 		_graphicsMgr->hideCursor();
407 	}
408 
409 	return Common::kNoError;
410 }
411 
saveGameStream(Common::WriteStream * stream,bool isAutosave)412 Common::Error TrecisionEngine::saveGameStream(Common::WriteStream *stream, bool isAutosave) {
413 	const byte version = SAVE_VERSION_SCUMMVM;
414 	Common::Serializer ser(nullptr, stream);
415 	ser.setVersion(version);
416 	stream->writeByte(version);
417 	syncGameStream(ser);
418 	return Common::kNoError;
419 }
420 
syncGameStream(Common::Serializer & ser)421 bool TrecisionEngine::syncGameStream(Common::Serializer &ser) {
422 	uint16 unused = 0;
423 
424 	if (ser.isLoading()) {
425 		ser.skip(40, SAVE_VERSION_ORIGINAL_MIN, SAVE_VERSION_ORIGINAL_MAX);                               // description
426 		ser.skip(ICONDX * ICONDY * sizeof(uint16), SAVE_VERSION_ORIGINAL_MIN, SAVE_VERSION_ORIGINAL_MAX); // thumbnail
427 	}
428 
429 	ser.syncAsUint16LE(_curRoom);
430 	ser.syncAsByte(unused, SAVE_VERSION_ORIGINAL_MIN, SAVE_VERSION_ORIGINAL_MAX); // _inventorySize
431 	ser.syncAsByte(unused, SAVE_VERSION_ORIGINAL_MIN, SAVE_VERSION_ORIGINAL_MAX); // _cyberInventorySize
432 	ser.syncAsByte(_iconBase);
433 	ser.syncAsSint16LE(_flagSkipTalk);
434 	ser.syncAsSint16LE(unused, SAVE_VERSION_ORIGINAL_MIN, SAVE_VERSION_ORIGINAL_MAX); // _flagSkipEnable
435 	ser.syncAsSint16LE(unused, SAVE_VERSION_ORIGINAL_MIN, SAVE_VERSION_ORIGINAL_MAX); // _flagMouseEnabled
436 	ser.syncAsSint16LE(unused, SAVE_VERSION_ORIGINAL_MIN, SAVE_VERSION_ORIGINAL_MAX); // _flagScreenRefreshed
437 	ser.syncAsSint16LE(_flagPaintCharacter);
438 	ser.syncAsSint16LE(_flagSomeoneSpeaks);
439 	ser.syncAsSint16LE(_flagCharacterSpeak);
440 	ser.syncAsSint16LE(_flagInventoryLocked);
441 	ser.syncAsSint16LE(_flagUseWithStarted);
442 	ser.syncAsSint16LE(unused, SAVE_VERSION_ORIGINAL_MIN, SAVE_VERSION_ORIGINAL_MAX); // _flagMousePolling
443 	ser.syncAsSint16LE(unused, SAVE_VERSION_ORIGINAL_MIN, SAVE_VERSION_ORIGINAL_MAX); // _flagDialogSolitaire
444 	ser.syncAsSint16LE(unused);	// _flagCharacterExists
445 
446 	syncInventory(ser);
447 	_actor->syncGameStream(ser);
448 	_pathFind->syncGameStream(ser);
449 
450 	for (int i = 0; i < MAXROOMS; i++)
451 		_room[i].syncGameStream(ser);
452 
453 	for (int i = 0; i < MAXOBJ; i++)
454 		_obj[i].syncGameStream(ser);
455 
456 	for (int i = 0; i < MAXINVENTORY; i++)
457 		_inventoryObj[i].syncGameStream(ser);
458 
459 	_animMgr->syncGameStream(ser);
460 	ser.skip(NUMSAMPLES * 2, SAVE_VERSION_ORIGINAL_MIN, SAVE_VERSION_ORIGINAL_MAX); // SoundManager::syncGameStream()
461 	_dialogMgr->syncGameStream(ser);
462 	_logicMgr->syncGameStream(ser);
463 
464 	return true;
465 }
466 
467 } // End of namespace Trecision
468