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