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 /*
24  * This code is based on original Tony Tough source code
25  *
26  * Copyright (c) 1997-2003 Nayma Software
27  */
28 
29 #include "common/savefile.h"
30 #include "tony/mpal/lzo.h"
31 #include "tony/mpal/mpalutils.h"
32 #include "tony/custom.h"
33 #include "tony/gfxengine.h"
34 #include "tony/tony.h"
35 
36 namespace Tony {
37 
38 /****************************************************************************\
39 *       RMGfxEngine Methods
40 \****************************************************************************/
41 
exitAllIdles(CORO_PARAM,const void * param)42 void exitAllIdles(CORO_PARAM, const void *param) {
43 	CORO_BEGIN_CONTEXT;
44 	CORO_END_CONTEXT(_ctx);
45 
46 	int nCurLoc = *(const int *)param;
47 
48 	CORO_BEGIN_CODE(_ctx);
49 
50 	// Closes idle
51 	GLOBALS._bSkipSfxNoLoop = true;
52 
53 	CORO_INVOKE_2(mpalEndIdlePoll, nCurLoc, NULL);
54 
55 	GLOBALS._bIdleExited = true;
56 	GLOBALS._bSkipSfxNoLoop = false;
57 
58 	CORO_END_CODE;
59 }
60 
RMGfxEngine()61 RMGfxEngine::RMGfxEngine() {
62 	// Create big buffer where the frame will be rendered
63 	_bigBuf.create(RM_BBX, RM_BBY, 16);
64 	_bigBuf.offsetY(RM_SKIPY);
65 	_bigBuf.setTrackDirtyRects(true);
66 
67 	_nCurLoc = 0;
68 	_curAction = TA_GOTO;
69 	_curActionObj = 0;
70 	_nWipeType = 0;
71 	_hWipeEvent = 0;
72 	_nWipeStep = 0;
73 	_bMustEnterMenu = false;
74 	_bWiping = false;
75 	_bGUIOption = false;
76 	_bGUIInterface = false;
77 	_bGUIInventory = false;
78 	_bAlwaysDrawMouse = false;
79 	_bOption = false;
80 	_bLocationLoaded = false;
81 	_bInput = false;
82 }
83 
~RMGfxEngine()84 RMGfxEngine::~RMGfxEngine() {
85 	// Close the buffer
86 	_bigBuf.destroy();
87 }
88 
openOptionScreen(CORO_PARAM,int type)89 void RMGfxEngine::openOptionScreen(CORO_PARAM, int type) {
90 	CORO_BEGIN_CONTEXT;
91 	bool bRes;
92 	CORO_END_CONTEXT(_ctx);
93 
94 	CORO_BEGIN_CODE(_ctx);
95 
96 	_ctx->bRes = false;
97 
98 	if (type == 0)
99 		CORO_INVOKE_2(_opt.init, _bigBuf, _ctx->bRes);
100 	else if (type == 1)
101 		CORO_INVOKE_3(_opt.initLoadMenuOnly, _bigBuf, true, _ctx->bRes);
102 	else if (type == 2)
103 		CORO_INVOKE_2(_opt.initNoLoadSave, _bigBuf, _ctx->bRes);
104 	else if (type == 3)
105 		CORO_INVOKE_3(_opt.initLoadMenuOnly, _bigBuf, false, _ctx->bRes);
106 	else if (type == 4)
107 		CORO_INVOKE_3(_opt.initSaveMenuOnly, _bigBuf, false, _ctx->bRes);
108 
109 	if (_ctx->bRes) {
110 		g_vm->pauseSound(true);
111 
112 		disableInput();
113 		_inv.endCombine();
114 		_curActionObj = 0;
115 		_curAction = TA_GOTO;
116 		_point.setAction(_curAction);
117 		_point.setSpecialPointer(RMPointer::PTR_NONE);
118 		_point.setCustomPointer(NULL);
119 		enableMouse();
120 		g_vm->grabThumbnail();
121 
122 		// Exists the IDLE to avoid premature death in loading
123 		_bMustEnterMenu = true;
124 		if (type == 1 || type == 2) {
125 			GLOBALS._bIdleExited = true;
126 		} else {
127 			CORO_INVOKE_0(_tony.stopNoAction);
128 
129 			GLOBALS._bIdleExited = false;
130 
131 			CoroScheduler.createProcess(exitAllIdles, &_nCurLoc, sizeof(int));
132 		}
133 	}
134 
135 	CORO_END_CODE;
136 }
137 
doFrame(CORO_PARAM,bool bDrawLocation)138 void RMGfxEngine::doFrame(CORO_PARAM, bool bDrawLocation) {
139 	CORO_BEGIN_CONTEXT;
140 	CORO_END_CONTEXT(_ctx);
141 
142 	CORO_BEGIN_CODE(_ctx);
143 
144 	// Poll of input devices
145 	_input.poll();
146 
147 	if (_bMustEnterMenu && GLOBALS._bIdleExited) {
148 		_bOption = true;
149 		_bMustEnterMenu = false;
150 		GLOBALS._bIdleExited = false;
151 	}
152 
153 	if (_bOption) {
154 		CORO_INVOKE_1(_opt.doFrame, &_input);
155 		_bOption = !_opt.isClosing();
156 		if (!_bOption) {
157 			disableMouse();
158 			enableInput();
159 			mpalStartIdlePoll(_nCurLoc);
160 			g_vm->pauseSound(false);
161 		}
162 	}
163 
164 	if (bDrawLocation && _bLocationLoaded) {
165 		// Location and objects
166 		_loc.doFrame(&_bigBuf);
167 
168 		// Check the mouse input
169 		if (_bInput && !_tony.inAction()) {
170 			// If we are on the inventory, it is it who controls all input
171 			if (_inv.haveFocus(_input.mousePos()) && !_inter.active()) {
172 				// Left Click
173 				// **********
174 				if (_input.mouseLeftClicked()/* && m_itemName.IsItemSelected()*/) {
175 					// Left click activates the combine, if we are on an object
176 					if (_inv.leftClick(_input.mousePos(), _curActionObj)) {
177 						_curAction = TA_COMBINE;
178 						_point.setAction(_curAction);
179 					}
180 				} else
181 
182 					// Right Click
183 					// ***********
184 					if (_input.mouseRightClicked()) {
185 						if (_itemName.isItemSelected()) {
186 							_curActionObj = 0;
187 							_inv.rightClick(_input.mousePos());
188 						} else
189 							_inv.rightClick(_input.mousePos());
190 					} else
191 
192 						// Right Release
193 						// *************
194 						if (_input.mouseRightReleased()) {
195 							if (_inv.rightRelease(_input.mousePos(), _curAction)) {
196 								CORO_INVOKE_3(_tony.moveAndDoAction, _itemName.getHotspot(), _itemName.getSelectedItem(), _curAction);
197 
198 								_curAction = TA_GOTO;
199 								_point.setAction(_curAction);
200 							}
201 						}
202 			} else {
203 				// Options Menu
204 				// ************
205 				if (_bGUIOption) {
206 					if (!_tony.inAction() && _bInput) {
207 						if ((_input.mouseLeftClicked() && _input.mousePos()._x < 3 && _input.mousePos()._y < 3)) {
208 							CORO_INVOKE_1(openOptionScreen, 0);
209 							goto SKIPCLICKSINISTRO;
210 						} else if (_input.getAsyncKeyState(Common::KEYCODE_ESCAPE))
211 							CORO_INVOKE_1(openOptionScreen, 0);
212 						else if (!g_vm->getIsDemo()) {
213 							if (_input.getAsyncKeyState(Common::KEYCODE_F3) || _input.getAsyncKeyState(Common::KEYCODE_F5))
214 								// Save game screen
215 								CORO_INVOKE_1(openOptionScreen, 4);
216 							else if (_input.getAsyncKeyState(Common::KEYCODE_F2) || _input.getAsyncKeyState(Common::KEYCODE_F7))
217 								// Load game screen
218 								CORO_INVOKE_1(openOptionScreen, 3);
219 						}
220 					}
221 				}
222 
223 				// Left Click
224 				// **************
225 				if (_input.mouseLeftClicked() && !_inter.active()) {
226 
227 					if (_curAction != TA_COMBINE)
228 						CORO_INVOKE_3(_tony.moveAndDoAction, _itemName.getHotspot(), _itemName.getSelectedItem(), _point.curAction());
229 					else if (_itemName.getSelectedItem() != NULL)
230 						CORO_INVOKE_4(_tony.moveAndDoAction, _itemName.getHotspot(), _itemName.getSelectedItem(), TA_COMBINE, _curActionObj);
231 
232 					if (_curAction == TA_COMBINE) {
233 						_inv.endCombine();
234 						_point.setSpecialPointer(RMPointer::PTR_NONE);
235 					}
236 
237 					_curAction = TA_GOTO;
238 					_point.setAction(_curAction);
239 				}
240 
241 SKIPCLICKSINISTRO:
242 				// Right Click
243 				// ************
244 				if (_curAction == TA_COMBINE) {
245 					// During a combine, it cancels it
246 					if (_input.mouseRightClicked()) {
247 						_inv.endCombine();
248 						_curActionObj = 0;
249 						_curAction = TA_GOTO;
250 						_point.setAction(_curAction);
251 						_point.setSpecialPointer(RMPointer::PTR_NONE);
252 					}
253 				} else if (_input.mouseRightClicked() && _itemName.isItemSelected() && _point.getSpecialPointer() == RMPointer::PTR_NONE) {
254 					if (_bGUIInterface) {
255 						// Before opening the interface, replaces GOTO
256 						_curAction = TA_GOTO;
257 						_curActionObj = 0;
258 						_point.setAction(_curAction);
259 						_inter.clicked(_input.mousePos());
260 					}
261 				}
262 
263 				// Right Release
264 				// *************
265 				if (_input.mouseRightReleased()) {
266 					if (_bGUIInterface) {
267 						if (_inter.released(_input.mousePos(), _curAction)) {
268 							_point.setAction(_curAction);
269 							CORO_INVOKE_3(_tony.moveAndDoAction, _itemName.getHotspot(), _itemName.getSelectedItem(), _curAction);
270 
271 							_curAction = TA_GOTO;
272 							_point.setAction(_curAction);
273 						}
274 					}
275 				}
276 			}
277 
278 			// Update the name under the mouse pointer
279 			_itemName.setMouseCoord(_input.mousePos());
280 			if (!_inter.active() && !_inv.miniActive())
281 				CORO_INVOKE_4(_itemName.doFrame, _bigBuf, _loc, _point, _inv);
282 		}
283 
284 		// Interface & Inventory
285 		_inter.doFrame(_bigBuf, _input.mousePos());
286 		_inv.doFrame(_bigBuf, _point, _input.mousePos(), (!_tony.inAction() && !_inter.active() && _bGUIInventory));
287 	}
288 
289 	// Animate Tony
290 	CORO_INVOKE_2(_tony.doFrame, &_bigBuf, _nCurLoc);
291 
292 	// Update screen scrolling to keep Tony in focus
293 	if (_tony.mustUpdateScrolling() && _bLocationLoaded) {
294 		RMPoint showThis = _tony.position();
295 		showThis._y -= 60;
296 		_loc.updateScrolling(showThis);
297 	}
298 
299 	if (_bLocationLoaded)
300 		_tony.setScrollPosition(_loc.scrollPosition());
301 
302 	if ((!_tony.inAction() && _bInput) || _bAlwaysDrawMouse) {
303 		_point.showCursor();
304 	} else {
305 		_point.hideCursor();
306 	}
307 	_point.doFrame();
308 
309 	// **********************
310 	// Draw the list in the OT
311 	// **********************
312 	CORO_INVOKE_0(_bigBuf.drawOT);
313 
314 #define FSTEP (480/32)
315 
316 	// Wipe
317 	if (_bWiping) {
318 		switch (_nWipeType) {
319 		case 1:
320 			if (!(_rcWipeEllipse.bottom - _rcWipeEllipse.top >= FSTEP * 2)) {
321 				CoroScheduler.setEvent(_hWipeEvent);
322 				_nWipeType = 3;
323 				break;
324 			}
325 
326 			_rcWipeEllipse.top += FSTEP;
327 			_rcWipeEllipse.left += FSTEP;
328 			_rcWipeEllipse.right -= FSTEP;
329 			_rcWipeEllipse.bottom -= FSTEP;
330 			break;
331 
332 		case 2:
333 			if (!(_rcWipeEllipse.bottom - _rcWipeEllipse.top < 480 - FSTEP)) {
334 				CoroScheduler.setEvent(_hWipeEvent);
335 				_nWipeType = 3;
336 				break;
337 			}
338 
339 			_rcWipeEllipse.top -= FSTEP;
340 			_rcWipeEllipse.left -= FSTEP;
341 			_rcWipeEllipse.right += FSTEP;
342 			_rcWipeEllipse.bottom += FSTEP;
343 			break;
344 		}
345 	}
346 
347 	CORO_END_CODE;
348 }
349 
initCustomDll()350 void RMGfxEngine::initCustomDll() {
351 	setupGlobalVars(&_tony, &_point, &g_vm->_theBoxes, &_loc, &_inv, &_input);
352 }
353 
itemIrq(uint32 dwItem,int nPattern,int nStatus)354 void RMGfxEngine::itemIrq(uint32 dwItem, int nPattern, int nStatus) {
355 	assert(GLOBALS._gfxEngine);
356 
357 	if (GLOBALS._gfxEngine->_bLocationLoaded) {
358 		RMItem *item = GLOBALS._gfxEngine->_loc.getItemFromCode(dwItem);
359 		if (item != NULL) {
360 			if (nPattern != -1) {
361 				item->setPattern(nPattern, true);
362 			}
363 			if (nStatus != -1)
364 				item->setStatus(nStatus);
365 		}
366 	}
367 }
368 
initForNewLocation(int nLoc,RMPoint ptTonyStart,RMPoint start)369 void RMGfxEngine::initForNewLocation(int nLoc, RMPoint ptTonyStart, RMPoint start) {
370 	if (start._x == -1 || start._y == -1) {
371 		start._x = ptTonyStart._x - RM_SX / 2;
372 		start._y = ptTonyStart._y - RM_SY / 2;
373 	}
374 
375 	_loc.setScrollPosition(start);
376 
377 	if (ptTonyStart._x == 0 && ptTonyStart._y == 0) {
378 	} else {
379 		_tony.setPosition(ptTonyStart, nLoc);
380 		_tony.setScrollPosition(start);
381 	}
382 
383 	_curAction = TA_GOTO;
384 	_point.setCustomPointer(NULL);
385 	_point.setSpecialPointer(RMPointer::PTR_NONE);
386 	_point.setAction(_curAction);
387 	_inter.reset();
388 	_inv.reset();
389 
390 	mpalStartIdlePoll(_nCurLoc);
391 }
392 
loadLocation(int nLoc,RMPoint ptTonyStart,RMPoint start)393 uint32 RMGfxEngine::loadLocation(int nLoc, RMPoint ptTonyStart, RMPoint start) {
394 	_nCurLoc = nLoc;
395 
396 	bool bLoaded = false;
397 	for (int i = 0; i < 5; i++) {
398 		// Try the loading of the location
399 		RMRes res(_nCurLoc);
400 		if (!res.isValid())
401 			continue;
402 
403 		Common::SeekableReadStream *ds = res.getReadStream();
404 		_loc.load(*ds);
405 		delete ds;
406 
407 		initForNewLocation(nLoc, ptTonyStart, start);
408 		bLoaded = true;
409 		break;
410 	}
411 
412 	if (!bLoaded)
413 		error("Location was not loaded");
414 
415 	if (_bOption)
416 		_opt.reInit(_bigBuf);
417 
418 	_bLocationLoaded = true;
419 
420 	// On entering the location
421 	return CORO_INVALID_PID_VALUE; //mpalQueryDoAction(0, m_nCurLoc, 0);
422 }
423 
unloadLocation(CORO_PARAM,bool bDoOnExit,uint32 * result)424 void RMGfxEngine::unloadLocation(CORO_PARAM, bool bDoOnExit, uint32 *result) {
425 	CORO_BEGIN_CONTEXT;
426 	uint32 h;
427 	CORO_END_CONTEXT(_ctx);
428 
429 	CORO_BEGIN_CODE(_ctx);
430 
431 	// Release the location
432 	CORO_INVOKE_2(mpalEndIdlePoll, _nCurLoc, NULL);
433 
434 	// On Exit?
435 	if (bDoOnExit) {
436 		_ctx->h = mpalQueryDoAction(1, _nCurLoc, 0);
437 		if (_ctx->h != CORO_INVALID_PID_VALUE)
438 			CORO_INVOKE_2(CoroScheduler.waitForSingleObject, _ctx->h, CORO_INFINITE);
439 	}
440 
441 	_bLocationLoaded = false;
442 
443 	_bigBuf.clearOT();
444 	_loc.unload();
445 
446 	if (result != NULL)
447 		*result = CORO_INVALID_PID_VALUE;
448 
449 	CORO_END_CODE;
450 }
451 
init()452 void RMGfxEngine::init() {
453 	// Screen loading
454 	RMGfxSourceBuffer16 *load = NULL;
455 	RMResRaw *raw;
456 	INIT_GFX16_FROMRAW(20038, load);
457 	_bigBuf.addPrim(new RMGfxPrimitive(load));
458 	_bigBuf.drawOT(Common::nullContext);
459 	_bigBuf.clearOT();
460 	delete load;
461 
462 	// Display 'Loading' screen
463 	_bigBuf.addDirtyRect(Common::Rect(0, 0, RM_SX, RM_SY));
464 	g_vm->_window.getNewFrame(*this, NULL);
465 	g_vm->_window.repaint();
466 
467 	// Activate GUI
468 	_bGUIOption = true;
469 	_bGUIInterface = true;
470 	_bGUIInventory = true;
471 
472 	GLOBALS._bSkipSfxNoLoop = false;
473 	_bMustEnterMenu = false;
474 	GLOBALS._bIdleExited = false;
475 	_bOption = false;
476 	_bWiping = false;
477 	_hWipeEvent = CoroScheduler.createEvent(false, false);
478 
479 	// Initialize the IRQ function for items for MPAL
480 	GLOBALS._gfxEngine = this;
481 	mpalInstallItemIrq(itemIrq);
482 
483 	// Initialize the mouse pointer
484 	_point.init();
485 
486 	// Initialize Tony
487 	_tony.init();
488 	_tony.linkToBoxes(&g_vm->_theBoxes);
489 
490 	// Initialize the inventory and the interface
491 	_inv.init();
492 	_inter.init();
493 
494 	// Download the location and set priorities   @@@@@
495 	_bLocationLoaded = false;
496 
497 	enableInput();
498 
499 	// Starting the game
500 	_tony.executeAction(20, 1, 0);
501 }
502 
close()503 void RMGfxEngine::close() {
504 	_bigBuf.clearOT();
505 
506 	_inter.close();
507 	_inv.close();
508 	_tony.close();
509 	_point.close();
510 }
511 
enableInput()512 void RMGfxEngine::enableInput() {
513 	_bInput = true;
514 }
515 
disableInput()516 void RMGfxEngine::disableInput() {
517 	_bInput = false;
518 	_inter.reset();
519 }
520 
enableMouse()521 void RMGfxEngine::enableMouse() {
522 	_bAlwaysDrawMouse = true;
523 }
524 
disableMouse()525 void RMGfxEngine::disableMouse() {
526 	_bAlwaysDrawMouse = false;
527 }
528 
529 #define TONY_SAVEGAME_VERSION 8
530 
saveState(const Common::String & fn,byte * curThumb,const Common::String & name)531 void RMGfxEngine::saveState(const Common::String &fn, byte *curThumb, const Common::String &name) {
532 	Common::OutSaveFile *f = g_system->getSavefileManager()->openForSaving(fn);
533 	if (f == NULL)
534 		return;
535 
536 	byte *state;
537 	char buf[4];
538 	RMPoint tp = _tony.position();
539 
540 	// Saving: MPAL variables, current location, and Tony inventory position
541 
542 	// For now, we only save the MPAL state
543 	uint size = mpalGetSaveStateSize();
544 	state = new byte[size];
545 	mpalSaveState(state);
546 
547 	uint thumbsize = 160 * 120 * 2;
548 
549 	buf[0] = 'R';
550 	buf[1] = 'M';
551 	buf[2] = 'S';
552 	buf[3] = TONY_SAVEGAME_VERSION;
553 
554 	f->write(buf, 4);
555 	f->writeUint32LE(thumbsize);
556 	f->write(curThumb, thumbsize);
557 
558 	// Difficulty level
559 	int i = mpalQueryGlobalVar("VERSIONEFACILE");
560 	f->writeByte(i);
561 
562 	i = strlen(name.c_str());
563 	f->writeByte(i);
564 	f->write(name.c_str(), i);
565 	f->writeUint32LE(_nCurLoc);
566 	f->writeUint32LE(tp._x);
567 	f->writeUint32LE(tp._y);
568 
569 	f->writeUint32LE(size);
570 	f->write(state, size);
571 	delete[] state;
572 
573 	// Inventory
574 	size = _inv.getSaveStateSize();
575 	state = new byte[size];
576 	_inv.saveState(state);
577 	f->writeUint32LE(size);
578 	f->write(state, size);
579 	delete[] state;
580 
581 	// boxes
582 	size = g_vm->_theBoxes.getSaveStateSize();
583 	state = new byte[size];
584 	g_vm->_theBoxes.saveState(state);
585 	f->writeUint32LE(size);
586 	f->write(state, size);
587 	delete[] state;
588 
589 	// New Ver5
590 	// Saves the state of the shepherdess and show yourself
591 	bool bStat = _tony.getShepherdess();
592 	f->writeByte(bStat);
593 	bStat = _inter.getPerorate();
594 	f->writeByte(bStat);
595 
596 	// Save the chars
597 	charsSaveAll(f);
598 
599 	// Save the options
600 	f->writeByte(GLOBALS._bCfgInvLocked);
601 	f->writeByte(GLOBALS._bCfgInvNoScroll);
602 	f->writeByte(GLOBALS._bCfgTimerizedText);
603 	f->writeByte(GLOBALS._bCfgInvUp);
604 	f->writeByte(GLOBALS._bCfgAnni30);
605 	f->writeByte(GLOBALS._bCfgAntiAlias);
606 	f->writeByte(GLOBALS._bShowSubtitles);
607 	f->writeByte(GLOBALS._bCfgTransparence);
608 	f->writeByte(GLOBALS._bCfgInterTips);
609 	f->writeByte(GLOBALS._bCfgDubbing);
610 	f->writeByte(GLOBALS._bCfgMusic);
611 	f->writeByte(GLOBALS._bCfgSFX);
612 	f->writeByte(GLOBALS._nCfgTonySpeed);
613 	f->writeByte(GLOBALS._nCfgTextSpeed);
614 	f->writeByte(GLOBALS._nCfgDubbingVolume);
615 	f->writeByte(GLOBALS._nCfgMusicVolume);
616 	f->writeByte(GLOBALS._nCfgSFXVolume);
617 
618 	// Save the hotspots
619 	saveChangedHotspot(f);
620 
621 	// Save the music
622 	saveMusic(f);
623 
624 	f->finalize();
625 	delete f;
626 }
627 
loadState(CORO_PARAM,const Common::String & fn)628 void RMGfxEngine::loadState(CORO_PARAM, const Common::String &fn) {
629 	// PROBLEM: You should change the location in a separate process to do the OnEnter
630 	CORO_BEGIN_CONTEXT;
631 	Common::InSaveFile *f;
632 	byte *state, *statecmp;
633 	uint32 size, sizecmp;
634 	char buf[4];
635 	RMPoint tp;
636 	int loc;
637 	int ver;
638 	int i;
639 	CORO_END_CONTEXT(_ctx);
640 
641 	CORO_BEGIN_CODE(_ctx);
642 
643 	_ctx->f = g_system->getSavefileManager()->openForLoading(fn);
644 	if (_ctx->f == NULL)
645 		return;
646 	_ctx->f->read(_ctx->buf, 4);
647 
648 	if (_ctx->buf[0] != 'R' || _ctx->buf[1] != 'M' || _ctx->buf[2] != 'S') {
649 		delete _ctx->f;
650 		return;
651 	}
652 
653 	_ctx->ver = _ctx->buf[3];
654 
655 	if (_ctx->ver == 0 || _ctx->ver > TONY_SAVEGAME_VERSION) {
656 		delete _ctx->f;
657 		return;
658 	}
659 
660 	if (_ctx->ver >= 0x3) {
661 		// There is a thumbnail. If the version is between 5 and 7, it's compressed
662 		if ((_ctx->ver >= 0x5) && (_ctx->ver <= 0x7)) {
663 			_ctx->i = 0;
664 			_ctx->i = _ctx->f->readUint32LE();
665 			_ctx->f->seek(_ctx->i);
666 		} else {
667 			if (_ctx->ver >= 8)
668 				// Skip thumbnail size
669 				_ctx->f->skip(4);
670 
671 			_ctx->f->seek(160 * 120 * 2, SEEK_CUR);
672 		}
673 	}
674 
675 	if (_ctx->ver >= 0x5) {
676 		// Skip the difficulty level
677 		_ctx->f->seek(1, SEEK_CUR);
678 	}
679 
680 	if (_ctx->ver >= 0x4) { // Skip the savegame name, which serves no purpose
681 		_ctx->i = _ctx->f->readByte();
682 		_ctx->f->seek(_ctx->i, SEEK_CUR);
683 	}
684 
685 	_ctx->loc = _ctx->f->readUint32LE();
686 	_ctx->tp._x = _ctx->f->readUint32LE();
687 	_ctx->tp._y = _ctx->f->readUint32LE();
688 	_ctx->size = _ctx->f->readUint32LE();
689 
690 	if ((_ctx->ver >= 0x5) && (_ctx->ver <= 7)) {
691 		// MPAL was packed!
692 		_ctx->sizecmp = _ctx->f->readUint32LE();
693 		_ctx->state = new byte[_ctx->size];
694 		_ctx->statecmp = new byte[_ctx->sizecmp];
695 		_ctx->f->read(_ctx->statecmp, _ctx->sizecmp);
696 		lzo1x_decompress(_ctx->statecmp, _ctx->sizecmp, _ctx->state, &_ctx->size);
697 		delete[] _ctx->statecmp;
698 	} else {
699 		// Read uncompressed MPAL data
700 		_ctx->state = new byte[_ctx->size];
701 		_ctx->f->read(_ctx->state, _ctx->size);
702 	}
703 
704 	mpalLoadState(_ctx->state);
705 	delete[] _ctx->state;
706 
707 	// Inventory
708 	_ctx->size = _ctx->f->readUint32LE();
709 	_ctx->state = new byte[_ctx->size];
710 	_ctx->f->read(_ctx->state, _ctx->size);
711 	_inv.loadState(_ctx->state);
712 	delete[] _ctx->state;
713 
714 	if (_ctx->ver >= 0x2) {   // Version 2: box please
715 		_ctx->size = _ctx->f->readUint32LE();
716 		_ctx->state = new byte[_ctx->size];
717 		_ctx->f->read(_ctx->state, _ctx->size);
718 		g_vm->_theBoxes.loadState(_ctx->state);
719 		delete[] _ctx->state;
720 	}
721 
722 	if (_ctx->ver >= 5) {
723 		// Version 5
724 		bool bStat = _ctx->f->readByte();
725 		_tony.setShepherdess(bStat);
726 		bStat = _ctx->f->readByte();
727 		_inter.setPerorate(bStat);
728 
729 		charsLoadAll(_ctx->f);
730 	}
731 
732 	if (_ctx->ver >= 6) {
733 		// Load options
734 		GLOBALS._bCfgInvLocked = _ctx->f->readByte();
735 		GLOBALS._bCfgInvNoScroll = _ctx->f->readByte();
736 		GLOBALS._bCfgTimerizedText = _ctx->f->readByte();
737 		GLOBALS._bCfgInvUp = _ctx->f->readByte();
738 		GLOBALS._bCfgAnni30 = _ctx->f->readByte();
739 		GLOBALS._bCfgAntiAlias = _ctx->f->readByte();
740 		GLOBALS._bShowSubtitles = _ctx->f->readByte();
741 		GLOBALS._bCfgTransparence = _ctx->f->readByte();
742 		GLOBALS._bCfgInterTips = _ctx->f->readByte();
743 		GLOBALS._bCfgDubbing = _ctx->f->readByte();
744 		GLOBALS._bCfgMusic = _ctx->f->readByte();
745 		GLOBALS._bCfgSFX = _ctx->f->readByte();
746 		GLOBALS._nCfgTonySpeed = _ctx->f->readByte();
747 		GLOBALS._nCfgTextSpeed = _ctx->f->readByte();
748 		GLOBALS._nCfgDubbingVolume = _ctx->f->readByte();
749 		GLOBALS._nCfgMusicVolume = _ctx->f->readByte();
750 		GLOBALS._nCfgSFXVolume = _ctx->f->readByte();
751 
752 		// Load hotspots
753 		loadChangedHotspot(_ctx->f);
754 	}
755 
756 	if (_ctx->ver >= 7) {
757 		loadMusic(_ctx->f);
758 	}
759 
760 	delete _ctx->f;
761 
762 	CORO_INVOKE_2(unloadLocation, false, NULL);
763 	loadLocation(_ctx->loc, _ctx->tp, RMPoint(-1, -1));
764 	_tony.setPattern(RMTony::PAT_STANDRIGHT);
765 
766 	// On older versions, need to an enter action
767 	if (_ctx->ver < 5)
768 		mpalQueryDoAction(0, _ctx->loc, 0);
769 	else {
770 		// In the new ones, we just reset the mcode
771 		mCharResetCodes();
772 	}
773 
774 	if (_ctx->ver >= 6)
775 		reapplyChangedHotspot();
776 
777 	CORO_INVOKE_0(restoreMusic);
778 
779 	_bGUIInterface = true;
780 	_bGUIInventory = true;
781 	_bGUIOption = true;
782 
783 	CORO_END_CODE;
784 }
785 
pauseSound(bool bPause)786 void RMGfxEngine::pauseSound(bool bPause) {
787 	if (_bLocationLoaded)
788 		_loc.pauseSound(bPause);
789 }
790 
initWipe(int type)791 void RMGfxEngine::initWipe(int type) {
792 	_bWiping = true;
793 	_nWipeType = type;
794 	_nWipeStep = 0;
795 
796 	if (_nWipeType == 1)
797 		_rcWipeEllipse = Common::Rect(80, 0, 640 - 80, 480);
798 	else if (_nWipeType == 2)
799 		_rcWipeEllipse = Common::Rect(320 - FSTEP, 240 - FSTEP, 320 + FSTEP, 240 + FSTEP);
800 }
801 
closeWipe()802 void RMGfxEngine::closeWipe() {
803 	_bWiping = false;
804 }
805 
waitWipeEnd(CORO_PARAM)806 void RMGfxEngine::waitWipeEnd(CORO_PARAM) {
807 	CoroScheduler.waitForSingleObject(coroParam, _hWipeEvent, CORO_INFINITE);
808 }
809 
canLoadSave()810 bool RMGfxEngine::canLoadSave() {
811 	return _bInput && !_tony.inAction() && !g_vm->getIsDemo();
812 }
813 
operator RMGfxTargetBuffer&()814 RMGfxEngine::operator RMGfxTargetBuffer &() {
815 	return _bigBuf;
816 }
817 
getInput()818 RMInput &RMGfxEngine::getInput() {
819 	return _input;
820 }
821 
getPointer()822 RMPointer &RMGfxEngine::getPointer() {
823 	return _point;
824 }
825 
826 /**
827  * Link to graphic task
828  */
linkGraphicTask(RMGfxTask * task)829 void RMGfxEngine::linkGraphicTask(RMGfxTask *task) {
830 	_bigBuf.addPrim(new RMGfxPrimitive(task));
831 }
832 
setPerorate(bool bpal)833 void RMGfxEngine::setPerorate(bool bpal) {
834 	_inter.setPerorate(bpal);
835 }
836 
837 } // End of namespace Tony
838