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