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 "common/config-manager.h"
24 #include "common/savefile.h"
25 #include "common/translation.h"
26 
27 #include "gui/saveload.h"
28 #include "graphics/thumbnail.h"
29 
30 #include "gnap/gnap.h"
31 #include "gnap/datarchive.h"
32 #include "gnap/gamesys.h"
33 #include "gnap/resource.h"
34 
35 namespace Gnap {
36 
createMenuSprite()37 void GnapEngine::createMenuSprite() {
38 	_menuBackgroundSurface = _gameSys->createSurface(0x10002);
39 }
40 
freeMenuSprite()41 void GnapEngine::freeMenuSprite() {
42 	_gameSys->removeSpriteDrawItem(_menuBackgroundSurface, 260);
43 	delayTicksCursor(5);
44 	deleteSurface(&_menuBackgroundSurface);
45 }
46 
initMenuHotspots1()47 void GnapEngine::initMenuHotspots1() {
48 	int curId = 0;
49 
50 	for (int i = 0; i < 3; ++i) {
51 		int top = 74 * i + 69;
52 		for (int j = 0; j < 3; ++j) {
53 			int left = 87 * j + 262;
54 			_hotspots[curId]._rect = Common::Rect(left, top, left + 79, top + 66);
55 			_hotspots[curId]._flags = SF_NONE;
56 			++curId;
57 		}
58 	}
59 
60 	_hotspots[curId]._rect = Common::Rect(330, 350, 430, 460);
61 	_hotspots[curId]._flags = SF_GRAB_CURSOR;
62 
63 	++curId;
64 	_hotspots[curId]._rect = Common::Rect(180, 15, 620, 580);
65 	_hotspots[curId]._flags = SF_NONE;
66 
67 	++curId;
68 	_hotspots[curId]._rect = Common::Rect(0, 0, 799, 599);
69 	_hotspots[curId]._flags = SF_NONE;
70 
71 	_hotspotsCount = curId + 1;
72 }
73 
initMenuHotspots2()74 void GnapEngine::initMenuHotspots2() {
75 	int curId = 0;
76 
77 	for (int i = 0; i < 4; ++i) {
78 		int top = 48 * i + 85;
79 		_hotspots[curId]._rect = Common::Rect(312, top, 465, top + 37);
80 		_hotspots[curId]._flags = SF_GRAB_CURSOR;
81 		++curId;
82 	}
83 
84 	_hotspots[curId]._rect = Common::Rect(500, 72, 527, 99);
85 	_hotspots[curId]._flags = SF_DISABLED;
86 
87 	++curId;
88 	_hotspots[curId]._rect = Common::Rect(330, 350, 430, 460);
89 	_hotspots[curId]._flags = SF_GRAB_CURSOR;
90 
91 	++curId;
92 	_hotspots[curId]._rect = Common::Rect(180, 15, 620, 580);
93 	_hotspots[curId]._flags = SF_NONE;
94 
95 	++curId;
96 	_hotspots[curId]._rect = Common::Rect(0, 0, 799, 599);
97 	_hotspots[curId]._flags = SF_NONE;
98 
99 	_hotspotsCount = curId + 1;
100 }
101 
initMenuQuitQueryHotspots()102 void GnapEngine::initMenuQuitQueryHotspots() {
103 	_hotspots[0]._rect = Common::Rect(311, 197, 377, 237);
104 	_hotspots[0]._flags = SF_GRAB_CURSOR;
105 
106 	_hotspots[1]._rect = Common::Rect(403, 197, 469, 237);
107 	_hotspots[1]._flags = SF_GRAB_CURSOR;
108 
109 	_hotspots[2]._rect = Common::Rect(330, 350, 430, 460);
110 	_hotspots[2]._flags = SF_GRAB_CURSOR;
111 
112 	_hotspots[3]._rect = Common::Rect(180, 15, 620, 580);
113 	_hotspots[3]._flags = SF_NONE;
114 
115 	_hotspots[4]._rect = Common::Rect(0, 0, 799, 599);
116 	_hotspots[4]._flags = SF_NONE;
117 
118 	_hotspotsCount = 5;
119 }
120 
initSaveLoadHotspots()121 void GnapEngine::initSaveLoadHotspots() {
122 	int curId = 0;
123 
124 	for (int i = 0; i < 7; ++i ) {
125 		int top = 31 * i + 74;
126 		_hotspots[curId]._rect = Common::Rect(288, top, 379, top + 22);
127 		_hotspots[curId]._flags = SF_GRAB_CURSOR;
128 		++curId;
129 	}
130 
131 	if (_menuStatus == 2) {
132 		_hotspots[curId]._rect = Common::Rect(416, 160, 499, 188);
133 		_hotspots[curId]._flags = SF_GRAB_CURSOR;
134 		++curId;
135 	}
136 
137 	_hotspots[curId]._rect = Common::Rect(416, 213, 499, 241);
138 	_hotspots[curId]._flags = SF_GRAB_CURSOR;
139 
140 	++curId;
141 	_hotspots[curId]._rect = Common::Rect(330, 350, 430, 460);
142 	_hotspots[curId]._flags = SF_GRAB_CURSOR;
143 
144 	++curId;
145 	_hotspots[curId]._rect = Common::Rect(180, 15, 620, 580);
146 	_hotspots[curId]._flags = SF_NONE;
147 
148 	++curId;
149 	_hotspots[curId]._rect = Common::Rect(0, 0, 799, 599);
150 	_hotspots[curId]._flags = SF_NONE;
151 
152 	_hotspotsCount = curId + 1;
153 }
154 
drawInventoryFrames()155 void GnapEngine::drawInventoryFrames() {
156 	for (int i = 0; i < 9; ++i)
157 		_gameSys->drawSpriteToSurface(_menuBackgroundSurface, _hotspots[i]._rect.left - 93, _hotspots[i]._rect.top, 0x10001);
158 }
159 
insertInventorySprites()160 void GnapEngine::insertInventorySprites() {
161 	for (int i = 0; i < 9; ++i) {
162 		_menuInventoryIndices[i] = -1;
163 		_gameSys->removeSpriteDrawItem(_menuInventorySprites[_sceneClickedHotspot], 261);
164 		_menuInventorySprites[i] = 0;
165 	}
166 
167 	_menuSpritesIndex = 0;
168 
169 	for (int index = 0; index < 30 && _menuSpritesIndex < 9; ++index) {
170 		if (invHas(index)) {
171 			_gameSys->drawSpriteToSurface(_menuBackgroundSurface,
172 				_hotspots[_menuSpritesIndex]._rect.left - 93, _hotspots[_menuSpritesIndex]._rect.top, 0x10000);
173 			_menuInventorySprites[_menuSpritesIndex] = _gameSys->createSurface(getInventoryItemSpriteNum(index) | 0x10000);
174 			if (index != _grabCursorSpriteIndex) {
175 				_menuInventoryIndices[_menuSpritesIndex] = index;
176 				_gameSys->insertSpriteDrawItem(_menuInventorySprites[_menuSpritesIndex],
177 					_hotspots[_menuSpritesIndex]._rect.left + ((79 - _menuInventorySprites[_menuSpritesIndex]->w) / 2),
178 					_hotspots[_menuSpritesIndex]._rect.top + ((66 - _menuInventorySprites[_menuSpritesIndex]->h) / 2),
179 					261);
180 			}
181 			_hotspots[_menuSpritesIndex]._flags = SF_GRAB_CURSOR;
182 			++_menuSpritesIndex;
183 		}
184 	}
185 }
186 
removeInventorySprites()187 void GnapEngine::removeInventorySprites() {
188 	for (int i = 0; i < _menuSpritesIndex; ++i)
189 		if (_menuInventorySprites[i])
190 			_gameSys->removeSpriteDrawItem(_menuInventorySprites[i], 261);
191 	delayTicksCursor(5);
192 	for (int j = 0; j < _menuSpritesIndex; ++j) {
193 		if (_menuInventorySprites[j]) {
194 			deleteSurface(&_menuInventorySprites[j]);
195 			_menuInventorySprites[j] = 0;
196 			_menuInventoryIndices[j] = -1;
197 		}
198 	}
199 	_menuSpritesIndex = 0;
200 }
201 
runMenu()202 void GnapEngine::runMenu() {
203 	_spriteHandle = nullptr;
204 	_cursorSprite = nullptr;
205 	_menuSprite1 = nullptr;
206 	_menuSprite2 = nullptr;
207 	_menuSaveLoadSprite = nullptr;
208 	_menuQuitQuerySprite = nullptr;
209 
210 	_menuStatus = 0;
211 	_menuDone = false;
212 
213 	delete _tempThumbnail;
214 	_tempThumbnail = new Common::MemoryWriteStreamDynamic(DisposeAfterUse::YES);
215 	Graphics::saveThumbnail(*_tempThumbnail);
216 
217 	createMenuSprite();
218 	insertDeviceIconActive();
219 
220 	for (int i = 0; i < 7; ++i) {
221 		_savegameFilenames[i][0] = 0;
222 		_savegameSprites[i] = nullptr;
223 	}
224 
225 	if (_menuStatus == 0) {
226 		invAdd(kItemMagazine);
227 		setGrabCursorSprite(-1);
228 		hideCursor();
229 		initMenuHotspots1();
230 		drawInventoryFrames();
231 		insertInventorySprites();
232 		_gameSys->insertSpriteDrawItem(_menuBackgroundSurface, 93, 0, 260);
233 		showCursor();
234 		// SetCursorPos(400, 300);
235 		setVerbCursor(GRAB_CURSOR);
236 		// pollMessages();
237 	}
238 
239 	_timers[2] = 10;
240 
241 	while (!isKeyStatus1(Common::KEYCODE_BACKSPACE) && !isKeyStatus1(Common::KEYCODE_ESCAPE) && !_sceneDone && !_menuDone) {
242 		updateCursorByHotspot();
243 
244 		switch (_menuStatus) {
245 		case 0:
246 			updateMenuStatusInventory();
247 			break;
248 		case 1:
249 			updateMenuStatusMainMenu();
250 			break;
251 		case 2:
252 			updateMenuStatusSaveGame();
253 			break;
254 		case 3:
255 			updateMenuStatusLoadGame();
256 			break;
257 		case 4:
258 			updateMenuStatusQueryQuit();
259 			break;
260 		}
261 
262 		gameUpdateTick();
263 	}
264 
265 	removeInventorySprites();
266 	if (_spriteHandle)
267 		_gameSys->removeSpriteDrawItem(_spriteHandle, 261);
268 	if (_menuSprite1)
269 		_gameSys->removeSpriteDrawItem(_menuSprite1, 262);
270 	if (_menuSprite2)
271 		_gameSys->removeSpriteDrawItem(_menuSprite2, 262);
272 	for (int i = 0; i < 7; ++i)
273 		if (_savegameSprites[i])
274 			_gameSys->removeSpriteDrawItem(_savegameSprites[i], 263);
275 	if (_cursorSprite)
276 		_gameSys->removeSpriteDrawItem(_cursorSprite, 264);
277 	if (_menuSaveLoadSprite)
278 		_gameSys->removeSpriteDrawItem(_menuSaveLoadSprite, 262);
279 	if (_menuQuitQuerySprite)
280 		_gameSys->removeSpriteDrawItem(_menuQuitQuerySprite, 262);
281 	if (_menuBackgroundSurface)
282 		_gameSys->removeSpriteDrawItem(_menuBackgroundSurface, 260);
283 
284 	delayTicksCursor(5);
285 
286 	deleteSurface(&_spriteHandle);
287 	deleteSurface(&_menuSprite1);
288 	deleteSurface(&_menuSprite2);
289 	for (int i = 0; i < 7; ++i)
290 		deleteSurface(&_savegameSprites[i]);
291 	deleteSurface(&_cursorSprite);
292 	deleteSurface(&_menuSaveLoadSprite);
293 	deleteSurface(&_menuQuitQuerySprite);
294 
295 	_sceneClickedHotspot = -1;
296 
297 	_timers[2] = getRandom(20) + 30;
298 	_timers[3] = getRandom(200) + 50;
299 	_timers[0] = getRandom(75) + 75;
300 	_timers[1] = getRandom(20) + 30;
301 
302 	clearAllKeyStatus1();
303 
304 	_mouseClickState._left = false;
305 
306 	removeDeviceIconActive();
307 
308 	freeMenuSprite();//??? CHECKME
309 }
310 
updateMenuStatusInventory()311 void GnapEngine::updateMenuStatusInventory() {
312 	static const struct {
313 		int item1, item2, resultItem;
314 	} kCombineItems[] = {
315 		{kItemGrass, kItemMud, kItemDisguise},
316 		{kItemDice, kItemQuarterWithHole, kItemDiceQuarterHole},
317 		{kItemPill, kItemBucketWithBeer, kItemBucketWithPill}
318 	};
319 
320 	updateGrabCursorSprite(0, 0);
321 	_hotspots[0]._rect = Common::Rect(262, 69, 341, 135);
322 	_sceneClickedHotspot = -1;
323 	if (_timers[2] == 0)
324 		_sceneClickedHotspot = getClickedHotspotId();
325 	if (_sceneClickedHotspot == -1 || _sceneClickedHotspot >= _menuSpritesIndex) {
326 		if (_sceneClickedHotspot == _hotspotsCount - 3) {
327 			if (_grabCursorSpriteIndex == -1) {
328 				_timers[2] = 10;
329 				playSound(0x108F4, false);
330 				_menuStatus = 1;
331 				Common::Rect dirtyRect(_hotspots[0]._rect.left, _hotspots[0]._rect.top, _hotspots[2]._rect.right, _hotspots[_hotspotsCount - 4]._rect.bottom);
332 				drawInventoryFrames();
333 				initMenuHotspots2();
334 				removeInventorySprites();
335 				if (!_menuSprite1)
336 					_menuSprite1 = _gameSys->createSurface(0x104F8);
337 				_gameSys->insertSpriteDrawItem(_menuSprite1, 288, 79, 262);
338 				_gameSys->insertDirtyRect(dirtyRect);
339 			} else {
340 				playSound(0x108F5, false);
341 			}
342 		} else if (_sceneClickedHotspot == _hotspotsCount - 1) {
343 			_timers[2] = 10;
344 			playSound(0x108F5, false);
345 			_menuDone = true;
346 		}
347 	} else if (_sceneClickedHotspot != -1 && _menuInventoryIndices[_sceneClickedHotspot] != -1 && _grabCursorSpriteIndex == -1) {
348 		_gameSys->removeSpriteDrawItem(_menuInventorySprites[_sceneClickedHotspot], 261);
349 		setGrabCursorSprite(_menuInventoryIndices[_sceneClickedHotspot]);
350 		_menuInventoryIndices[_sceneClickedHotspot] = -1;
351 	} else if (_sceneClickedHotspot != -1 && _menuInventoryIndices[_sceneClickedHotspot] == -1 && _grabCursorSpriteIndex != -1) {
352 		_menuInventoryIndices[_sceneClickedHotspot] = _grabCursorSpriteIndex;
353 		_gameSys->insertSpriteDrawItem(_menuInventorySprites[_sceneClickedHotspot],
354 			_hotspots[_sceneClickedHotspot]._rect.left + ((79 - _menuInventorySprites[_sceneClickedHotspot]->w) / 2),
355 			_hotspots[_sceneClickedHotspot]._rect.top + (66 - _menuInventorySprites[_sceneClickedHotspot]->h) / 2,
356 			261);
357 		setGrabCursorSprite(-1);
358 	} else if (_sceneClickedHotspot != -1 && _menuInventoryIndices[_sceneClickedHotspot] != -1 && _grabCursorSpriteIndex != -1) {
359 		int combineIndex = -1;
360 		for (int i = 0; i < ARRAYSIZE(kCombineItems); ++i) {
361 			if ((_grabCursorSpriteIndex == kCombineItems[i].item1 && _menuInventoryIndices[_sceneClickedHotspot] == kCombineItems[i].item2) ||
362 				(_grabCursorSpriteIndex == kCombineItems[i].item2 && _menuInventoryIndices[_sceneClickedHotspot] == kCombineItems[i].item1)) {
363 				combineIndex = i;
364 				break;
365 			}
366 		}
367 		if (combineIndex >= 0) {
368 			invRemove(kCombineItems[combineIndex].item1);
369 			invRemove(kCombineItems[combineIndex].item2);
370 			invAdd(kCombineItems[combineIndex].resultItem);
371 			playSound(0x108AE, false);
372 			deleteSurface(&_spriteHandle); // CHECKME
373 			_spriteHandle = _gameSys->createSurface(0x10001);
374 			_gameSys->insertSpriteDrawItem(_spriteHandle, _hotspots[_menuSpritesIndex - 1]._rect.left, _hotspots[_menuSpritesIndex - 1]._rect.top, 261);
375 			setGrabCursorSprite(kCombineItems[combineIndex].resultItem);
376 			removeInventorySprites();
377 			insertInventorySprites();
378 			delayTicksCursor(5);
379 		} else {
380 			playSound(0x108F5, false);
381 		}
382 	}
383 }
384 
updateMenuStatusMainMenu()385 void GnapEngine::updateMenuStatusMainMenu() {
386 	_hotspots[0]._rect = Common::Rect(312, 85, 465, 122);
387 	_sceneClickedHotspot = -1;
388 	if (!_timers[2])
389 		_sceneClickedHotspot = getClickedHotspotId();
390 
391 	if (_sceneClickedHotspot != 1 && _sceneClickedHotspot != 0) {
392 		if (_sceneClickedHotspot != 2 && _hotspotsCount - 1 != _sceneClickedHotspot) {
393 			if (_sceneClickedHotspot == 3) {
394 				// Quit
395 				_timers[2] = 10;
396 				playSound(0x108F4, false);
397 				_gameSys->removeSpriteDrawItem(_menuSprite1, 262);
398 				initMenuQuitQueryHotspots();
399 				_menuStatus = 4;
400 				if (!_menuQuitQuerySprite)
401 					_menuQuitQuerySprite = _gameSys->createSurface(0x104FC);
402 				_gameSys->insertSpriteDrawItem(_menuQuitQuerySprite, 254, 93, 262);
403 			} else if (_sceneClickedHotspot == 4) {
404 				// Pause ?
405 				playSound(0x108F4, false);
406 				Common::Rect dirtyRect(0, 0, 799, 599);
407 				hideCursor();
408 				_largeSprite = _gameSys->allocSurface(800, 600);
409 
410 				for (int i = 0; i < 3; ++i) {
411 					_timers[2] = 10;
412 
413 					if (i == 0) {
414 						_gameSys->drawSpriteToSurface(_largeSprite, 0, 0, 0x1078D);
415 						_gameSys->insertSpriteDrawItem(_largeSprite, 0, 0, 300);
416 						playMidi("pause.mid");
417 					} else if (i == 1) {
418 						_gameSys->drawSpriteToSurface(_largeSprite, 0, 0, 0x1078E);
419 						_gameSys->insertDirtyRect(dirtyRect);
420 					} else if (i == 2) {
421 						_gameSys->drawSpriteToSurface(_largeSprite, 0, 0, 0x1078F);
422 						_gameSys->insertDirtyRect(dirtyRect);
423 					}
424 
425 					while (!_mouseClickState._left && !isKeyStatus1(Common::KEYCODE_ESCAPE) && !isKeyStatus1(Common::KEYCODE_RETURN)
426 							&& !isKeyStatus1(Common::KEYCODE_SPACE) && !_timers[2] && !_gameDone)
427 						gameUpdateTick();
428 
429 					playSound(0x108F5, false);
430 					_mouseClickState._left = false;
431 					clearKeyStatus1(Common::KEYCODE_ESCAPE);
432 					clearKeyStatus1(Common::KEYCODE_RETURN);
433 					clearKeyStatus1(Common::KEYCODE_SPACE);
434 				}
435 
436 				_gameSys->removeSpriteDrawItem(_largeSprite, 300);
437 				delayTicksCursor(5);
438 				deleteSurface(&_largeSprite);
439 				showCursor();
440 			} else if (_hotspotsCount - 3 == _sceneClickedHotspot) {
441 				// Button - Return to the inventory
442 				_timers[2] = 10;
443 				playSound(0x108F4, false);
444 				initMenuHotspots1();
445 				_menuStatus = 0;
446 				if (_menuSprite1)
447 					_gameSys->removeSpriteDrawItem(_menuSprite1, 262);
448 				insertInventorySprites();
449 				Common::Rect dirtyRect(_hotspots[0]._rect.left, _hotspots[0]._rect.top, _hotspots[2]._rect.right, _hotspots[_hotspotsCount - 4]._rect.bottom);
450 				_gameSys->insertDirtyRect(dirtyRect);
451 			}
452 		} else {
453 			// Resume
454 			playSound(0x108F5, false);
455 			_menuDone = true;
456 		}
457 	} else {
458 		// Save / Load
459 #if 1
460 		_timers[2] = 10;
461 		playSound(0x108F4, false);
462 
463 		if (_sceneClickedHotspot == 1) {
464 			GUI::SaveLoadChooser *dialog = new GUI::SaveLoadChooser(_("Save game:"), _("Save"), true);
465 			int16 savegameId = dialog->runModalWithCurrentTarget();
466 			Common::String savegameDescription = dialog->getResultString();
467 			delete dialog;
468 
469 			if (savegameId != -1) {
470 				saveGameState(savegameId, savegameDescription);
471 			}
472 		} else {
473 			GUI::SaveLoadChooser *dialog = new GUI::SaveLoadChooser(_("Restore game:"), _("Restore"), false);
474 			int16 savegameId = dialog->runModalWithCurrentTarget();
475 			delete dialog;
476 
477 			if (savegameId != -1) {
478 				loadGameState(savegameId);
479 				_wasSavegameLoaded = true;
480 				_menuDone = true;
481 				_sceneDone = true;
482 				playSound(0x108F4, false);
483 			} else {
484 				playSound(0x108F5, false);
485 			}
486 		}
487 	}
488 #else
489 	// NOTE:
490 	// This is the code for the original behavior.
491 	// It's currently not working prolery, but could be
492 	// fixed to replace the ScummVM screens currently
493 	// used.
494 		_timers[2] = 10;
495 		playSound(0x108F4, false);
496 		_gameSys->removeSpriteDrawItem(_menuSprite1, 262);
497 		if (_menuSaveLoadSprite)
498 			deleteSurface(&_menuSaveLoadSprite);
499 		if (_sceneClickedHotspot == 1) {
500 			// Save
501 			_menuStatus = 2;
502 			initSaveLoadHotspots();
503 			_menuSaveLoadSprite = _gameSys->createSurface(0x104FB);
504 		} else {
505 			// Load
506 			_menuStatus = 3;
507 			initSaveLoadHotspots();
508 			_menuSaveLoadSprite = _gameSys->createSurface(0x104FA);
509 		}
510 		_gameSys->insertSpriteDrawItem(_menuSaveLoadSprite, 403, 72, 262);
511 		if (!_menuSprite2)
512 			_menuSprite2 = _gameSys->createSurface(0x104F9);
513 		_gameSys->insertSpriteDrawItem(_menuSprite2, 277, 66, 262);
514 		for (int i = 0; i < 7; ++i) {
515 			Common::String savegameDescription;
516 			if (!_savegameSprites[i])
517 				_savegameSprites[i] = _gameSys->allocSurface(111, 40);
518 			if (readSavegameDescription(i + 1, savegameDescription) == 0)
519 				strncpy(_savegameFilenames[i], savegameDescription.c_str(), 40);
520 			_gameSys->drawTextToSurface(_savegameSprites[i], 0, 0, 255, 0, 0, _savegameFilenames[i]);
521 			_gameSys->insertSpriteDrawItem(_savegameSprites[i], 288, _hotspots[i].top, 263);
522 		}
523 		_savegameIndex = -1;
524 	}
525 #endif
526 }
527 
saveGameState(int slot,const Common::String & desc)528 Common::Error GnapEngine::saveGameState(int slot, const Common::String &desc) {
529 	Common::OutSaveFile *out = g_system->getSavefileManager()->openForSaving(
530 		generateSaveName(slot));
531 	if (!out)
532 		return Common::kCreatingFileFailed;
533 
534 	GnapSavegameHeader header;
535 	header._saveName = desc;
536 	writeSavegameHeader(out, header);
537 
538 	Common::Serializer s(nullptr, out);
539 	synchronize(s);
540 
541 	out->finalize();
542 	delete out;
543 
544 	return Common::kNoError;
545 }
546 
synchronize(Common::Serializer & s)547 void GnapEngine::synchronize(Common::Serializer &s) {
548 	if (s.isSaving()) {
549 		s.syncAsSint32LE(_currentSceneNum);
550 		s.syncAsSint32LE(_prevSceneNum);
551 		s.syncAsSint32LE(_cursorValue);
552 		s.syncAsUint32LE(_inventory);
553 		s.syncAsUint32LE(_gameFlags);
554 	} else {
555 		s.syncAsSint32LE(_newSceneNum);
556 		s.syncAsSint32LE(_currentSceneNum);
557 		s.syncAsSint32LE(_newCursorValue);
558 		s.syncAsUint32LE(_inventory);
559 		s.syncAsUint32LE(_gameFlags);
560 
561 		if (isFlag(kGFUnk24))
562 			_timers[9] = 600;
563 	}
564 }
565 
566 const char *const SAVEGAME_STR = "GNAP";
567 #define SAVEGAME_STR_SIZE 4
writeSavegameHeader(Common::OutSaveFile * out,GnapSavegameHeader & header)568 void GnapEngine::writeSavegameHeader(Common::OutSaveFile *out, GnapSavegameHeader &header) {
569 	// Write out a savegame header
570 	out->write(SAVEGAME_STR, SAVEGAME_STR_SIZE + 1);
571 
572 	out->writeByte(GNAP_SAVEGAME_VERSION);
573 
574 	// Write savegame name
575 	out->writeString(header._saveName);
576 	out->writeByte('\0');
577 
578 	// This implies the menu is used
579 	// If we want to save/load at any time, then a check should be added
580 	out->write(_tempThumbnail->getData(), _tempThumbnail->size());
581 
582 	// Write out the save date/time
583 	TimeDate td;
584 	g_system->getTimeAndDate(td);
585 	out->writeSint16LE(td.tm_year + 1900);
586 	out->writeSint16LE(td.tm_mon + 1);
587 	out->writeSint16LE(td.tm_mday);
588 	out->writeSint16LE(td.tm_hour);
589 	out->writeSint16LE(td.tm_min);
590 }
591 
readSavegameHeader(Common::InSaveFile * in,GnapSavegameHeader & header,bool skipThumbnail)592 WARN_UNUSED_RESULT bool GnapEngine::readSavegameHeader(Common::InSaveFile *in, GnapSavegameHeader &header, bool skipThumbnail) {
593 	char saveIdentBuffer[SAVEGAME_STR_SIZE + 1];
594 
595 	// Validate the header Id
596 	in->read(saveIdentBuffer, SAVEGAME_STR_SIZE + 1);
597 	if (strncmp(saveIdentBuffer, SAVEGAME_STR, SAVEGAME_STR_SIZE))
598 		return false;
599 
600 	header._version = in->readByte();
601 	if (header._version > GNAP_SAVEGAME_VERSION)
602 		return false;
603 
604 	// Read in the string
605 	header._saveName.clear();
606 	char ch;
607 	while ((ch = (char)in->readByte()) != '\0')
608 		header._saveName += ch;
609 
610 	// Get the thumbnail, saved in v2 or later
611 	if (header._version == 1)
612 		header._thumbnail = nullptr;
613 	else {
614 		if (!Graphics::loadThumbnail(*in, header._thumbnail, skipThumbnail)) {
615 			return false;
616 		}
617 	}
618 
619 	// Read in save date/time
620 	header._year = in->readSint16LE();
621 	header._month = in->readSint16LE();
622 	header._day = in->readSint16LE();
623 	header._hour = in->readSint16LE();
624 	header._minute = in->readSint16LE();
625 
626 	return true;
627 }
628 
loadGameState(int slot)629 Common::Error GnapEngine::loadGameState(int slot) {
630 	Common::InSaveFile *saveFile = g_system->getSavefileManager()->openForLoading(
631 		generateSaveName(slot));
632 	if (!saveFile)
633 		return Common::kReadingFailed;
634 
635 	Common::Serializer s(saveFile, nullptr);
636 
637 	// Load the savegame header
638 	GnapSavegameHeader header;
639 	if (!readSavegameHeader(saveFile, header))
640 		error("Invalid savegame");
641 
642 	if (header._thumbnail) {
643 		header._thumbnail->free();
644 		delete header._thumbnail;
645 	}
646 
647 	synchronize(s);
648 	delete saveFile;
649 
650 	_loadGameSlot = slot;
651 	return Common::kNoError;
652 }
653 
generateSaveName(int slot)654 Common::String GnapEngine::generateSaveName(int slot) {
655 	return Common::String::format("%s.%03d", _targetName.c_str(), slot);
656 }
657 
updateMenuStatusSaveGame()658 void GnapEngine::updateMenuStatusSaveGame() {
659 #if 0
660 	// NOTE:
661 	// This is the code for the original screen game.
662 	// It could be eventually fixed and could replace
663 	// the ScummVM screens currently used.
664 
665 	char v43[30];
666 	int v46;
667 	v43[0] = '\0';
668 	_hotspots[0]._x1 = 288;
669 	_hotspots[0]._y1 = 74;
670 	_hotspots[0]._x2 = 379;
671 	_hotspots[0]._y2 = 96;
672 	_sceneClickedHotspot = -1;
673 
674 	if (!_timers[2])
675 		_sceneClickedHotspot = getClickedHotspotId();
676 
677 	if (_hotspotsCount - 3 == _sceneClickedHotspot) {
678 		// Button
679 		_timers[2] = 10;
680 		playSound(0x108F4, false);
681 		_menuStatus = 1;
682 		warning("writeSavegame(_savegameIndex + 1, (int)&_savegameFilenames[30 * _savegameIndex], 1);");
683 	} else if (_hotspotsCount - 4 == _sceneClickedHotspot) {
684 		// Cancel
685 		_timers[2] = 10;
686 		playSound(0x108F5, false);
687 		_menuStatus = 1;
688 		if (strcmp(v43, _savegameFilenames[_savegameIndex]) && _savegameIndex != -1) {
689 			strcpy(_savegameFilenames[_savegameIndex], v43);
690 			if (_savegameSprites[_savegameIndex] != nullptr) {
691 				_gameSys->removeSpriteDrawItem(_savegameSprites[_savegameIndex], 263);
692 				delayTicksCursor(5);
693 				warning("memFreeHandle(_savegameSprites[_savegameIndex]);");
694 			}
695 			int v16 = _gameSys->getSpriteWidthById(0x104F9);
696 			warning("_savegameSprites[_savegameIndex] = allocSprite(v16, 40, 128, 0);");
697 		}
698 	} else if (_hotspotsCount - 5 == _sceneClickedHotspot) {
699 		// OK
700 		_timers[2] = 10;
701 		playSound(0x108F4, false);
702 		if (_savegameIndex != -1)
703 			warning("writeSavegame(_savegameIndex + 1, (int)&_savegameFilenames[30 * _savegameIndex], 1);");
704 		_menuStatus = 1;
705 	} else if (_hotspotsCount - 1 == _sceneClickedHotspot) {
706 		// in background
707 		_menuDone = true;
708 	} else if (_sceneClickedHotspot != -1 && _hotspotsCount - 2 != _sceneClickedHotspot) {
709 		// Savegame name
710 		_timers[2] = 10;
711 		playSound(0x108F4, false);
712 		if (strcmp(v43, _savegameFilenames[_savegameIndex]) & (_savegameIndex != -1)) {
713 			strcpy(_savegameFilenames[_savegameIndex], v43);
714 			if (_savegameSprites[_savegameIndex] != nullptr) {
715 				_gameSys->removeSpriteDrawItem(_savegameSprites[_savegameIndex], 263);
716 				delayTicksCursor(5);
717 				warning("memFreeHandle(_savegameSprites[_savegameIndex]);");
718 			}
719 			int v18 = _gameSys->getSpriteWidthById(0x104F9);
720 			_savegameSprites[_savegameIndex] = _gameSys->allocSurface(v18, 40);
721 			_gameSys->drawTextToSurface(_savegameSprites[_savegameIndex], 0, 0, 255, 0, 0, _savegameFilenames[_savegameIndex]);
722 			_gameSys->insertSpriteDrawItem(_savegameSprites[_savegameIndex], 288, _hotspots[_savegameIndex]._y1, 263);
723 		}
724 		_savegameIndex = _sceneClickedHotspot;
725 		v46 = strlen(_savegameFilenames[_sceneClickedHotspot]);
726 		strcpy(v43, _savegameFilenames[_sceneClickedHotspot]);
727 		if (_cursorSprite == nullptr) {
728 			int v19 = _gameSys->getTextHeight("_");
729 			int v20 = _gameSys->getTextWidth("_");
730 			_cursorSprite = _gameSys->allocSurface(v20, v19);
731 			_gameSys->drawTextToSurface(_cursorSprite, 0, 0, 255, 0, 0, "_");
732 		} else {
733 			_gameSys->removeSpriteDrawItem(_cursorSprite, 264);
734 		}
735 		int v21 = _hotspots[_savegameIndex]._x2;
736 		int v22 = v21 - _gameSys->getTextWidth("_");
737 		if (v22 > _gameSys->getTextWidth(_savegameFilenames[_savegameIndex]) + 288) {
738 			int v25 = _gameSys->getTextWidth(_savegameFilenames[_savegameIndex]) + 288;
739 			_gameSys->insertSpriteDrawItem(_cursorSprite, v25, _hotspots[_savegameIndex]._y1, 264);
740 		} else {
741 			int v23 = _hotspots[_savegameIndex]._x2;
742 			int v24 = v23 - _gameSys->getTextWidth("_");
743 			_gameSys->insertSpriteDrawItem(_cursorSprite, v24, _hotspots[_savegameIndex]._y1, 264);
744 		}
745 	}
746 
747 	updateEvents();
748 	Common::Event event;
749 	_eventMan->pollEvent(event);
750 
751 	Common::KeyCode keycode = event.kbd.keycode;
752 	if (_savegameIndex != -1 && keycode) {
753 		if ((keycode < Common::KEYCODE_a || keycode > Common::KEYCODE_z) && (keycode < Common::KEYCODE_0 || keycode > Common::KEYCODE_9) && keycode != Common::KEYCODE_SPACE) {
754 			if (keycode == Common::KEYCODE_BACKSPACE) {
755 				if (v46 > 0)
756 					--v46;
757 				_savegameFilenames[_savegameIndex][v46] = '\0';
758 				if (_savegameSprites[_savegameIndex] != nullptr) {
759 					_gameSys->removeSpriteDrawItem(_savegameSprites[_savegameIndex], 263);
760 					warning("memFreeHandle(_savegameSprites[_savegameIndex]);");
761 				}
762 				int v32 = _gameSys->getSpriteWidthById(0x104F9);
763 				_savegameSprites[_savegameIndex] = _gameSys->allocSurface(v32, 40);
764 				_gameSys->drawTextToSurface(_savegameSprites[_savegameIndex], 0, 0, 255, 0, 0, _savegameFilenames[_savegameIndex]);
765 				_gameSys->insertSpriteDrawItem(_savegameSprites[_savegameIndex], 288, _hotspots[_savegameIndex]._y1, 263);
766 				_gameSys->removeSpriteDrawItem(_cursorSprite, 264);
767 				int v33 = _hotspots[_savegameIndex]._y1;
768 				int v34 = _gameSys->getTextWidth(_savegameFilenames[_savegameIndex]);
769 				_gameSys->insertSpriteDrawItem(_cursorSprite, _hotspots[_savegameIndex]._x1 + v34, v33, 264);
770 			} else if (keycode == Common::KEYCODE_RETURN) {
771 				_menuStatus = 1;
772 				warning("writeSavegame(_savegameIndex + 1, (int)&_savegameFilenames[30 * _savegameIndex], 1);");
773 			}
774 		} else {
775 			_savegameFilenames[_savegameIndex][v46] = event.kbd.ascii;
776 			if (v46 < 28)
777 				++v46;
778 			_savegameFilenames[_savegameIndex][v46] = '\0';
779 			if (_gameSys->getTextWidth(_savegameFilenames[_savegameIndex]) > 91) {
780 				--v46;
781 				_savegameFilenames[_savegameIndex][v46] = '\0';
782 			}
783 			_gameSys->drawTextToSurface(_savegameSprites[_savegameIndex], 0, 0, 255, 0, 0, _savegameFilenames[_savegameIndex]);
784 			int v26 = _gameSys->getTextWidth(_savegameFilenames[_savegameIndex]);
785 			Common::Rect rect;
786 			rect.right = _hotspots[_savegameIndex]._x1 + v26;
787 			int v27 = rect.right;
788 			rect.left = v27 - 2 * _gameSys->getTextWidth("W");
789 			rect.top = _hotspots[_savegameIndex]._y1;
790 			rect.bottom = _hotspots[_savegameIndex]._y2;
791 			_gameSys->insertDirtyRect(rect);
792 			_gameSys->removeSpriteDrawItem(_cursorSprite, 264);
793 			int v28 = _hotspots[_savegameIndex]._x2;
794 			int v29 = _gameSys->getTextWidth("_");
795 			if (v28 - v29 > rect.right)
796 				_gameSys->insertSpriteDrawItem(_cursorSprite, rect.right, rect.top, 264);
797 			else {
798 				int v30 = _hotspots[_savegameIndex]._x2;
799 				int v31 = v30 - _gameSys->getTextWidth("_");
800 				_gameSys->insertSpriteDrawItem(_cursorSprite, v31, rect.top, 264);
801 			}
802 			clearKeyStatus1(8);
803 		}
804 	}
805 
806 //	warning("keybChar = 0;");
807 	if (_menuStatus == 1 || _menuDone) {
808 		_gameSys->removeSpriteDrawItem(_menuSprite2, 262);
809 		_gameSys->removeSpriteDrawItem(_menuSaveLoadSprite, 262);
810 		for (int i = 0; i < 7; ++i)
811 			_gameSys->removeSpriteDrawItem(_savegameSprites[i], 263);
812 		_gameSys->removeSpriteDrawItem(_cursorSprite, 264);
813 		if (!_menuDone) {
814 			initMenuHotspots2();
815 			_gameSys->insertSpriteDrawItem(_menuSprite1, 288, 79, 262);
816 		}
817 	}
818 #endif
819 }
820 
updateMenuStatusLoadGame()821 void GnapEngine::updateMenuStatusLoadGame() {
822 	_hotspots[0]._rect = Common::Rect(288, 74, 379, 96);
823 	_sceneClickedHotspot = -1;
824 	if (!_timers[2])
825 		_sceneClickedHotspot = getClickedHotspotId();
826 	if (_sceneClickedHotspot != -1 && _hotspotsCount - 2 != _sceneClickedHotspot) {
827 		_timers[2] = 10;
828 		if (_hotspotsCount - 4 <= _sceneClickedHotspot) {
829 			playSound(0x108F5, false);
830 			_gameSys->removeSpriteDrawItem(_menuSprite2, 262);
831 			_gameSys->removeSpriteDrawItem(_menuSaveLoadSprite, 262);
832 			for (int i = 0; i < 7; ++i)
833 				_gameSys->removeSpriteDrawItem(_savegameSprites[i], 263);
834 			if (_hotspotsCount - 1 == _sceneClickedHotspot) {
835 				_menuDone = true;
836 			} else {
837 				_menuStatus = 1;
838 				initMenuHotspots2();
839 				_gameSys->insertSpriteDrawItem(_menuSprite1, 288, 79, 262);
840 			}
841 		} else if (loadSavegame(_sceneClickedHotspot + 1)) {
842 			playSound(0x108F5, false);
843 		} else {
844 			playSound(0x108F4, false);
845 			_sceneDone = true;
846 		}
847 	}
848 }
849 
updateMenuStatusQueryQuit()850 void GnapEngine::updateMenuStatusQueryQuit() {
851 	_hotspots[0]._rect = Common::Rect(311, 197, 377, 237);
852 	_sceneClickedHotspot = -1;
853 
854 	if (!_timers[2])
855 		_sceneClickedHotspot = getClickedHotspotId();
856 
857 	/*	_sceneClickedHotspot
858 		0	Yes
859 		1	No
860 		2	Button
861 		3	Display
862 		4	Background
863 	*/
864 
865 	if (_sceneClickedHotspot == 0) {
866 		// Quit the game
867 		playSound(0x108F5, false);
868 		_gameSys->removeSpriteDrawItem(_menuQuitQuerySprite, 262);
869 		_sceneDone = true;
870 		_gameDone = true;
871 	} else if (_sceneClickedHotspot == 4) {
872 		// Exit the device
873 		playSound(0x108F4, false);
874 		_gameSys->removeSpriteDrawItem(_menuQuitQuerySprite, 262);
875 		_menuDone = true;
876 	} else if (_sceneClickedHotspot != -1) {
877 		// Return to the main menu
878 		playSound(0x108F4, false);
879 		_gameSys->removeSpriteDrawItem(_menuQuitQuerySprite, 262);
880 		_timers[2] = 10;
881 		_menuStatus = 1;
882 		initMenuHotspots2();
883 		_gameSys->insertSpriteDrawItem(_menuSprite1, 288, 79, 262);
884 	}
885 }
886 
887 } // End of namespace Gnap
888