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 #include "common/endian.h"
25 #include "common/config-manager.h"
26 #include "common/events.h"
27 #include "common/system.h"
28 #include "common/savefile.h"
29 #include "common/textconsole.h"
30 
31 #include "gui/message.h"
32 #include "sky/compact.h"
33 #include "sky/control.h"
34 #include "sky/disk.h"
35 #include "sky/logic.h"
36 #include "sky/music/musicbase.h"
37 #include "sky/mouse.h"
38 #include "sky/screen.h"
39 #include "sky/sky.h"
40 #include "sky/skydefs.h"
41 #include "sky/sound.h"
42 #include "sky/struc.h"
43 #include "sky/text.h"
44 #include "sky/compact.h"
45 
46 #define ANIM_DELAY 20
47 #define CLICK_DELAY 150
48 
49 namespace Sky {
50 
ConResource(void * pSpData,uint32 pNSprites,uint32 pCurSprite,uint16 pX,uint16 pY,uint32 pText,uint8 pOnClick,OSystem * system,uint8 * screen)51 ConResource::ConResource(void *pSpData, uint32 pNSprites, uint32 pCurSprite, uint16 pX, uint16 pY, uint32 pText, uint8 pOnClick, OSystem *system, uint8 *screen) {
52 	_spriteData = (DataFileHeader *)pSpData;
53 	_numSprites = pNSprites;
54 	_curSprite = pCurSprite;
55 	_x = pX;
56 	_y = pY;
57 	_text = pText;
58 	_onClick = pOnClick;
59 	_system = system;
60 	_screen = screen;
61 }
62 
isMouseOver(uint32 mouseX,uint32 mouseY)63 bool ConResource::isMouseOver(uint32 mouseX, uint32 mouseY) {
64 	if ((mouseX >= _x) && (mouseY >= _y) && ((uint16)mouseX <= _x + _spriteData->s_width) && ((uint16)mouseY <= _y + _spriteData->s_height))
65 		return true;
66 	else
67 		return false;
68 }
69 
drawToScreen(bool doMask)70 void ConResource::drawToScreen(bool doMask) {
71 	uint8 *screenPos = _y * GAME_SCREEN_WIDTH + _x + _screen;
72 	uint8 *updatePos = screenPos;
73 
74 	if (!_spriteData)
75 		return;
76 	uint8 *spriteData = ((uint8 *)_spriteData) + sizeof(DataFileHeader);
77 	spriteData += _spriteData->s_sp_size * _curSprite;
78 	if (doMask) {
79 		for (uint16 cnty = 0; cnty < _spriteData->s_height; cnty++) {
80 			for (uint16 cntx = 0; cntx < _spriteData->s_width; cntx++) {
81 				if (spriteData[cntx]) screenPos[cntx] = spriteData[cntx];
82 			}
83 			screenPos += GAME_SCREEN_WIDTH;
84 			spriteData += _spriteData->s_width;
85 		}
86 	} else {
87 		for (uint16 cnty = 0; cnty < _spriteData->s_height; cnty++) {
88 			memcpy(screenPos, spriteData, _spriteData->s_width);
89 			screenPos += GAME_SCREEN_WIDTH;
90 			spriteData += _spriteData->s_width;
91 		}
92 	}
93 	_system->copyRectToScreen(updatePos, GAME_SCREEN_WIDTH, _x, _y, _spriteData->s_width, _spriteData->s_height);
94 }
95 
TextResource(void * pSpData,uint32 pNSprites,uint32 pCurSprite,uint16 pX,uint16 pY,uint32 pText,uint8 pOnClick,OSystem * system,uint8 * screen)96 TextResource::TextResource(void *pSpData, uint32 pNSprites, uint32 pCurSprite, uint16 pX, uint16 pY, uint32 pText, uint8 pOnClick, OSystem *system, uint8 *screen) :
97 	ConResource(pSpData, pNSprites, pCurSprite, pX, pY, pText, pOnClick, system, screen) {
98 		_oldScreen = (uint8 *)malloc(PAN_CHAR_HEIGHT * 3 * PAN_LINE_WIDTH);
99 		_oldY = 0;
100 		_oldX = GAME_SCREEN_WIDTH;
101 }
102 
~TextResource()103 TextResource::~TextResource() {
104 	free(_oldScreen);
105 }
106 
flushForRedraw()107 void TextResource::flushForRedraw() {
108 	if (_oldX < GAME_SCREEN_WIDTH) {
109 		uint16 cpWidth = (PAN_LINE_WIDTH > (GAME_SCREEN_WIDTH - _oldX))?(GAME_SCREEN_WIDTH - _oldX):(PAN_LINE_WIDTH);
110 		for (uint8 cnty = 0; cnty < PAN_CHAR_HEIGHT; cnty++)
111 			memcpy(_screen + (cnty + _oldY) * GAME_SCREEN_WIDTH + _oldX, _oldScreen + cnty * PAN_LINE_WIDTH, cpWidth);
112 	}
113 	_oldX = GAME_SCREEN_WIDTH;
114 }
115 
drawToScreen(bool doMask)116 void TextResource::drawToScreen(bool doMask) {
117 	(void)doMask;
118 	uint16 cnty, cntx, cpWidth, cpHeight;
119 	if ((_oldX == _x) && (_oldY == _y) && (_spriteData))
120 		return;
121 	if (_oldX < GAME_SCREEN_WIDTH) {
122 		cpWidth = (PAN_LINE_WIDTH > (GAME_SCREEN_WIDTH - _oldX))?(GAME_SCREEN_WIDTH - _oldX):(PAN_LINE_WIDTH);
123 		if (_spriteData && (cpWidth > _spriteData->s_width))
124 			cpWidth = _spriteData->s_width;
125 		if (_spriteData)
126 			cpHeight = (_spriteData->s_height > (GAME_SCREEN_HEIGHT - _oldY))?(GAME_SCREEN_HEIGHT - _oldY):(_spriteData->s_height);
127 		else
128 			cpHeight = PAN_CHAR_HEIGHT;
129 		for (cnty = 0; cnty < cpHeight; cnty++)
130 			memcpy(_screen + (cnty + _oldY) * GAME_SCREEN_WIDTH + _oldX, _oldScreen + cnty * PAN_LINE_WIDTH, cpWidth);
131 		_system->copyRectToScreen(_screen + _oldY * GAME_SCREEN_WIDTH + _oldX, GAME_SCREEN_WIDTH, _oldX, _oldY, cpWidth, PAN_CHAR_HEIGHT);
132 	}
133 	if (!_spriteData) {
134 		_oldX = GAME_SCREEN_WIDTH;
135 		return;
136 	}
137 	_oldX = _x;
138 	_oldY = _y;
139 	cpWidth = (PAN_LINE_WIDTH > (GAME_SCREEN_WIDTH - _x))?(GAME_SCREEN_WIDTH - _x):(PAN_LINE_WIDTH);
140 	if (cpWidth > _spriteData->s_width)
141 		cpWidth = _spriteData->s_width;
142 	cpHeight = (_spriteData->s_height > (GAME_SCREEN_HEIGHT - _y))?(GAME_SCREEN_HEIGHT - _y):(_spriteData->s_height);
143 
144 	uint8 *screenPos = _screen + _y * GAME_SCREEN_WIDTH + _x;
145 	uint8 *copyDest = _oldScreen;
146 	uint8 *copySrc = ((uint8 *)_spriteData) + sizeof(DataFileHeader);
147 	for (cnty = 0; cnty < cpHeight; cnty++) {
148 		memcpy(copyDest, screenPos, cpWidth);
149 		for (cntx = 0; cntx < cpWidth; cntx++)
150 			if (copySrc[cntx]) screenPos[cntx] = copySrc[cntx];
151 		copySrc += _spriteData->s_width;
152 		copyDest += PAN_LINE_WIDTH;
153 		screenPos += GAME_SCREEN_WIDTH;
154 	}
155 	_system->copyRectToScreen(_screen + _y * GAME_SCREEN_WIDTH + _x, GAME_SCREEN_WIDTH, _x, _y, cpWidth, cpHeight);
156 }
157 
ControlStatus(Text * skyText,OSystem * system,uint8 * scrBuf)158 ControlStatus::ControlStatus(Text *skyText, OSystem *system, uint8 *scrBuf) {
159 	_skyText = skyText;
160 	_system = system;
161 	_screenBuf = scrBuf;
162 	_textData = NULL;
163 	_statusText = new TextResource(NULL, 2, 1, 64, 163, 0, DO_NOTHING, _system, _screenBuf);
164 }
165 
~ControlStatus()166 ControlStatus::~ControlStatus() {
167 	free(_textData);
168 	delete _statusText;
169 }
170 
setToText(const char * newText)171 void ControlStatus::setToText(const char *newText) {
172 	char tmpLine[256];
173 	Common::strlcpy(tmpLine, newText, 256);
174 	if (_textData) {
175 		_statusText->flushForRedraw();
176 		free(_textData);
177 	}
178 	DisplayedText disText = _skyText->displayText(tmpLine, NULL, true, STATUS_WIDTH, 255);
179 	_textData = (DataFileHeader *)disText.textData;
180 	_statusText->setSprite(_textData);
181 	_statusText->drawToScreen(WITH_MASK);
182 }
183 
setToText(uint16 textNum)184 void ControlStatus::setToText(uint16 textNum) {
185 	free(_textData);
186 	DisplayedText disText = _skyText->displayText(textNum, NULL, true, STATUS_WIDTH, 255);
187 	_textData = (DataFileHeader *)disText.textData;
188 	_statusText->setSprite(_textData);
189 	_statusText->drawToScreen(WITH_MASK);
190 }
191 
drawToScreen()192 void ControlStatus::drawToScreen() {
193 	_statusText->flushForRedraw();
194 	_statusText->drawToScreen(WITH_MASK);
195 }
196 
Control(Common::SaveFileManager * saveFileMan,Screen * screen,Disk * disk,Mouse * mouse,Text * text,MusicBase * music,Logic * logic,Sound * sound,SkyCompact * skyCompact,OSystem * system)197 Control::Control(Common::SaveFileManager *saveFileMan, Screen *screen, Disk *disk, Mouse *mouse, Text *text, MusicBase *music, Logic *logic, Sound *sound, SkyCompact *skyCompact, OSystem *system) {
198 	_saveFileMan = saveFileMan;
199 
200 	_skyScreen = screen;
201 	_skyDisk = disk;
202 	_skyMouse = mouse;
203 	_skyText = text;
204 	_skyMusic = music;
205 	_skyLogic = logic;
206 	_skySound = sound;
207 	_skyCompact = skyCompact;
208 	_system = system;
209 	_controlPanel = NULL;
210 }
211 
createResource(void * pSpData,uint32 pNSprites,uint32 pCurSprite,int16 pX,int16 pY,uint32 pText,uint8 pOnClick,uint8 panelType)212 ConResource *Control::createResource(void *pSpData, uint32 pNSprites, uint32 pCurSprite, int16 pX, int16 pY, uint32 pText, uint8 pOnClick, uint8 panelType) {
213 	if (pText) pText += 0x7000;
214 	if (panelType == MAINPANEL) {
215 		pX += MPNL_X;
216 		pY += MPNL_Y;
217 	} else {
218 		pX += SPNL_X;
219 		pY += SPNL_Y;
220 	}
221 	return new ConResource(pSpData, pNSprites, pCurSprite, pX, pY, pText, pOnClick, _system, _screenBuf);
222 }
223 
removePanel()224 void Control::removePanel() {
225 	free(_screenBuf);
226 	free(_sprites.controlPanel);	free(_sprites.button);
227 	free(_sprites.buttonDown);		free(_sprites.savePanel);
228 	free(_sprites.yesNo);			free(_sprites.slide);
229 	free(_sprites.slide2);			free(_sprites.slode);
230 	free(_sprites.slode2);			free(_sprites.musicBodge);
231 	delete _controlPanel;			delete _exitButton;
232 	_controlPanel = NULL;
233 	delete _slide;				delete _slide2;
234 	delete _slode;				delete _restorePanButton;
235 	delete _savePanel;			delete _saveButton;
236 	delete _downFastButton;			delete _downSlowButton;
237 	delete _upFastButton;			delete _upSlowButton;
238 	delete _quitButton;			delete _autoSaveButton;
239 	delete _savePanButton;			delete _dosPanButton;
240 	delete _restartPanButton;		delete _fxPanButton;
241 	delete _musicPanButton;			delete _bodge;
242 	delete _yesNo;				delete _text;
243 	delete _statusBar;			delete _restoreButton;
244 
245 	if (_textSprite) {
246 		free(_textSprite);
247 		_textSprite = NULL;
248 	}
249 }
250 
initPanel()251 void Control::initPanel() {
252 	_screenBuf = (uint8 *)malloc(GAME_SCREEN_WIDTH * FULL_SCREEN_HEIGHT);
253 	memset(_screenBuf, 0, GAME_SCREEN_WIDTH * FULL_SCREEN_HEIGHT);
254 
255 	uint16 volY = (127 - _skyMusic->giveVolume()) / 4 + 59 - MPNL_Y; // volume slider's Y coordinate
256 	uint16 spdY = (SkyEngine::_systemVars.gameSpeed - 2) / SPEED_MULTIPLY;
257 	spdY += MPNL_Y + 83; // speed slider's initial position
258 
259 	_sprites.controlPanel	= _skyDisk->loadFile(60500);
260 	_sprites.button			= _skyDisk->loadFile(60501);
261 	_sprites.buttonDown		= _skyDisk->loadFile(60502);
262 	_sprites.savePanel		= _skyDisk->loadFile(60503);
263 	_sprites.yesNo			= _skyDisk->loadFile(60504);
264 	_sprites.slide			= _skyDisk->loadFile(60505);
265 	_sprites.slode			= _skyDisk->loadFile(60506);
266 	_sprites.slode2			= _skyDisk->loadFile(60507);
267 	_sprites.slide2			= _skyDisk->loadFile(60508);
268 	if (SkyEngine::_systemVars.gameVersion < 368)
269 		_sprites.musicBodge = NULL;
270 	else
271 		_sprites.musicBodge = _skyDisk->loadFile(60509);
272 
273 	//Main control panel:                                            X    Y Text       OnClick
274 	_controlPanel     = createResource(_sprites.controlPanel, 1, 0,  0,   0,  0,      DO_NOTHING, MAINPANEL);
275 	_exitButton       = createResource(      _sprites.button, 3, 0, 16, 125, 50,            EXIT, MAINPANEL);
276 	_slide            = createResource(      _sprites.slide2, 1, 0, 19,spdY, 95,     SPEED_SLIDE, MAINPANEL);
277 	_slide2           = createResource(      _sprites.slide2, 1, 0, 19,volY, 14,     MUSIC_SLIDE, MAINPANEL);
278 	_slode            = createResource(      _sprites.slode2, 1, 0,  9,  49,  0,      DO_NOTHING, MAINPANEL);
279 	_restorePanButton = createResource(      _sprites.button, 3, 0, 58,  19, 51, REST_GAME_PANEL, MAINPANEL);
280 	_savePanButton    = createResource(      _sprites.button, 3, 0, 58,  39, 48, SAVE_GAME_PANEL, MAINPANEL);
281 	_dosPanButton     = createResource(      _sprites.button, 3, 0, 58,  59, 93,     QUIT_TO_DOS, MAINPANEL);
282 	_restartPanButton = createResource(      _sprites.button, 3, 0, 58,  79, 94,         RESTART, MAINPANEL);
283 	_fxPanButton      = createResource(      _sprites.button, 3, 0, 58,  99, 90,       TOGGLE_FX, MAINPANEL);
284 
285 	if (SkyEngine::isCDVersion()) { // CD Version: Toggle text/speech
286 		_musicPanButton = createResource(    _sprites.button, 3, 0, 58, 119, 52,     TOGGLE_TEXT, MAINPANEL);
287 	} else {                       // disk version: toggle music on/off
288 		_musicPanButton = createResource(    _sprites.button, 3, 0, 58, 119, 91,       TOGGLE_MS, MAINPANEL);
289 	}
290 	_bodge            = createResource(  _sprites.musicBodge, 2, 1, 98, 115,  0,      DO_NOTHING, MAINPANEL);
291 	_yesNo            = createResource(       _sprites.yesNo, 1, 0, -2,  40,  0,      DO_NOTHING, MAINPANEL);
292 
293 	_text = new TextResource(NULL, 1, 0, 15, 137, 0, DO_NOTHING, _system, _screenBuf);
294 	_controlPanLookList[0] = _exitButton;
295 	_controlPanLookList[1] = _restorePanButton;
296 	_controlPanLookList[2] = _savePanButton;
297 	_controlPanLookList[3] = _dosPanButton;
298 	_controlPanLookList[4] = _restartPanButton;
299 	_controlPanLookList[5] = _fxPanButton;
300 	_controlPanLookList[6] = _musicPanButton;
301 	_controlPanLookList[7] = _slide;
302 	_controlPanLookList[8] = _slide2;
303 
304 	// save/restore panel
305 	_savePanel      = createResource( _sprites.savePanel, 1, 0,   0,   0,  0,      DO_NOTHING, SAVEPANEL);
306 	_saveButton     = createResource(    _sprites.button, 3, 0,  29, 129, 48,     SAVE_A_GAME, SAVEPANEL);
307 	_downFastButton = createResource(_sprites.buttonDown, 1, 0, 212, 114,  0, SHIFT_DOWN_FAST, SAVEPANEL);
308 	_downSlowButton = createResource(_sprites.buttonDown, 1, 0, 212, 104,  0, SHIFT_DOWN_SLOW, SAVEPANEL);
309 	_upFastButton   = createResource(_sprites.buttonDown, 1, 0, 212,  10,  0,   SHIFT_UP_FAST, SAVEPANEL);
310 	_upSlowButton   = createResource(_sprites.buttonDown, 1, 0, 212,  21,  0,   SHIFT_UP_SLOW, SAVEPANEL);
311 	_quitButton     = createResource(    _sprites.button, 3, 0,  72, 129, 49,       SP_CANCEL, SAVEPANEL);
312 	_restoreButton  = createResource(    _sprites.button, 3, 0,  29, 129, 51,  RESTORE_A_GAME, SAVEPANEL);
313 	_autoSaveButton = createResource(    _sprites.button, 3, 0, 115, 129, 0x8FFF,    RESTORE_AUTO, SAVEPANEL);
314 
315 	_savePanLookList[0] = _saveButton;
316 	_restorePanLookList[0] = _restoreButton;
317 	_restorePanLookList[1] = _savePanLookList[1] = _downSlowButton;
318 	_restorePanLookList[2] = _savePanLookList[2] = _downFastButton;
319 	_restorePanLookList[3] = _savePanLookList[3] = _upFastButton;
320 	_restorePanLookList[4] = _savePanLookList[4] = _upSlowButton;
321 	_restorePanLookList[5] = _savePanLookList[5] = _quitButton;
322 	_restorePanLookList[6] = _autoSaveButton;
323 
324 	_statusBar = new ControlStatus(_skyText, _system, _screenBuf);
325 
326 	_textSprite = NULL;
327 }
328 
buttonControl(ConResource * pButton)329 void Control::buttonControl(ConResource *pButton) {
330 	char autoSave[50] = "Restore Autosave";
331 
332 	if (Common::parseLanguage(ConfMan.get("language")) == Common::RU_RUS)
333 		strncpy(autoSave, "Zarpyzit/ abtocoxpahehie", 50);
334 
335 	if (pButton == NULL) {
336 		free(_textSprite);
337 		_textSprite = NULL;
338 		_curButtonText = 0;
339 		_text->setSprite(NULL);
340 		return;
341 	}
342 	if (_curButtonText != pButton->_text) {
343 		free(_textSprite);
344 		_textSprite = NULL;
345 		_curButtonText = pButton->_text;
346 		if (pButton->_text) {
347 			DisplayedText textRes;
348 			if (pButton->_text == 0xFFFF) // text for autosave button
349 				textRes = _skyText->displayText(autoSave, NULL, false, PAN_LINE_WIDTH, 255);
350 			else
351 				textRes = _skyText->displayText(pButton->_text, NULL, false, PAN_LINE_WIDTH, 255);
352 			_textSprite = (DataFileHeader *)textRes.textData;
353 			_text->setSprite(_textSprite);
354 		} else
355 			_text->setSprite(NULL);
356 	}
357 	Common::Point mouse = _system->getEventManager()->getMousePos();
358 	int destY = (mouse.y - 16 >= 0) ? mouse.y - 16 : 0;
359 	_text->setXY(mouse.x + 12, destY);
360 }
361 
drawTextCross(uint32 flags)362 void Control::drawTextCross(uint32 flags) {
363 	_bodge->drawToScreen(NO_MASK);
364 	if (!(flags & SF_ALLOW_SPEECH))
365 		drawCross(151, 124);
366 	if (!(flags & SF_ALLOW_TEXT))
367 		drawCross(173, 124);
368 }
369 
drawCross(uint16 x,uint16 y)370 void Control::drawCross(uint16 x, uint16 y) {
371 	_text->flushForRedraw();
372 	uint8 *bufPos, *crossPos;
373 	bufPos = _screenBuf + y * GAME_SCREEN_WIDTH + x;
374 	crossPos = _crossImg;
375 	for (uint16 cnty = 0; cnty < CROSS_SZ_Y; cnty++) {
376 		for (uint16 cntx = 0; cntx < CROSS_SZ_X; cntx++)
377 			if (crossPos[cntx] != 0xFF)
378 				bufPos[cntx] = crossPos[cntx];
379 		bufPos += GAME_SCREEN_WIDTH;
380 		crossPos += CROSS_SZ_X;
381 	}
382 	bufPos = _screenBuf + y * GAME_SCREEN_WIDTH + x;
383 	_system->copyRectToScreen(bufPos, GAME_SCREEN_WIDTH, x, y, CROSS_SZ_X, CROSS_SZ_Y);
384 	_text->drawToScreen(WITH_MASK);
385 }
386 
animClick(ConResource * pButton)387 void Control::animClick(ConResource *pButton) {
388 	if (pButton->_curSprite != pButton->_numSprites -1) {
389 		pButton->_curSprite++;
390 		_text->flushForRedraw();
391 		pButton->drawToScreen(NO_MASK);
392 		_text->drawToScreen(WITH_MASK);
393 		_system->updateScreen();
394 		delay(CLICK_DELAY);
395 		if (!_controlPanel)
396 			return;
397 		pButton->_curSprite--;
398 		_text->flushForRedraw();
399 		pButton->drawToScreen(NO_MASK);
400 		_text->drawToScreen(WITH_MASK);
401 		_system->updateScreen();
402 	}
403 }
404 
drawMainPanel()405 void Control::drawMainPanel() {
406 	memset(_screenBuf, 0, GAME_SCREEN_WIDTH * FULL_SCREEN_HEIGHT);
407 	_system->copyRectToScreen(_screenBuf, GAME_SCREEN_WIDTH, 0, 0, GAME_SCREEN_WIDTH, FULL_SCREEN_HEIGHT);
408 	if (_controlPanel)
409 		_controlPanel->drawToScreen(NO_MASK);
410 	_exitButton->drawToScreen(NO_MASK);
411 	_savePanButton->drawToScreen(NO_MASK);
412 	_restorePanButton->drawToScreen(NO_MASK);
413 	_dosPanButton->drawToScreen(NO_MASK);
414 	_restartPanButton->drawToScreen(NO_MASK);
415 	_fxPanButton->drawToScreen(NO_MASK);
416 	_musicPanButton->drawToScreen(NO_MASK);
417 	_slode->drawToScreen(WITH_MASK);
418 	_slide->drawToScreen(WITH_MASK);
419 	_slide2->drawToScreen(WITH_MASK);
420 	_bodge->drawToScreen(WITH_MASK);
421 	if (SkyEngine::isCDVersion())
422 		drawTextCross(SkyEngine::_systemVars.systemFlags & TEXT_FLAG_MASK);
423 	_statusBar->drawToScreen();
424 }
425 
doLoadSavePanel()426 void Control::doLoadSavePanel() {
427 	if (SkyEngine::isDemo())
428 		return; // I don't think this can even happen
429 	initPanel();
430 	_skyScreen->clearScreen();
431 	if (SkyEngine::_systemVars.gameVersion < 331)
432 		_skyScreen->setPalette(60509);
433 	else
434 		_skyScreen->setPalette(60510);
435 
436 	_savedMouse = _skyMouse->giveCurrentMouseType();
437 	_savedCharSet = _skyText->giveCurrentCharSet();
438 	_skyText->fnSetFont(2);
439 	_skyMouse->spriteMouse(MOUSE_NORMAL, 0, 0);
440 	_lastButton = -1;
441 	_curButtonText = 0;
442 
443 	saveRestorePanel(false);
444 
445 	memset(_screenBuf, 0, GAME_SCREEN_WIDTH * FULL_SCREEN_HEIGHT);
446 	_system->copyRectToScreen(_screenBuf, GAME_SCREEN_WIDTH, 0, 0, GAME_SCREEN_WIDTH, FULL_SCREEN_HEIGHT);
447 	_system->updateScreen();
448 	_skyScreen->forceRefresh();
449 	_skyScreen->setPaletteEndian((uint8 *)_skyCompact->fetchCpt(SkyEngine::_systemVars.currentPalette));
450 	removePanel();
451 	_skyMouse->spriteMouse(_savedMouse, 0, 0);
452 	_skyText->fnSetFont(_savedCharSet);
453 }
454 
doControlPanel()455 void Control::doControlPanel() {
456 	if (SkyEngine::isDemo()) {
457 		return;
458 	}
459 
460 	initPanel();
461 
462 	_savedCharSet = _skyText->giveCurrentCharSet();
463 	_skyText->fnSetFont(2);
464 
465 	_skyScreen->clearScreen();
466 	if (SkyEngine::_systemVars.gameVersion < 331)
467 		_skyScreen->setPalette(60509);
468 	else
469 		_skyScreen->setPalette(60510);
470 
471 	// Set initial button lights
472 	_fxPanButton->_curSprite =
473 		(SkyEngine::_systemVars.systemFlags & SF_FX_OFF ? 0 : 2);
474 
475 	// music button only available in floppy version
476 	if (!SkyEngine::isCDVersion())
477 		_musicPanButton->_curSprite =
478 			(SkyEngine::_systemVars.systemFlags & SF_MUS_OFF ? 0 : 2);
479 
480 	drawMainPanel();
481 
482 	_savedMouse = _skyMouse->giveCurrentMouseType();
483 
484 	_skyMouse->spriteMouse(MOUSE_NORMAL, 0, 0);
485 	bool quitPanel = false;
486 	_lastButton = -1;
487 	_curButtonText = 0;
488 	uint16 clickRes = 0;
489 
490 	while (!quitPanel && !Engine::shouldQuit()) {
491 		_text->drawToScreen(WITH_MASK);
492 		_system->updateScreen();
493 		_mouseClicked = false;
494 		delay(ANIM_DELAY);
495 		if (!_controlPanel)
496 			return;
497 		if (_keyPressed.keycode == Common::KEYCODE_ESCAPE) { // escape pressed
498 			_mouseClicked = false;
499 			quitPanel = true;
500 		}
501 		bool haveButton = false;
502 		Common::Point mouse = _system->getEventManager()->getMousePos();
503 		for (uint8 lookCnt = 0; lookCnt < 9; lookCnt++) {
504 			if (_controlPanLookList[lookCnt]->isMouseOver(mouse.x, mouse.y)) {
505 				haveButton = true;
506 				buttonControl(_controlPanLookList[lookCnt]);
507 				if (_mouseClicked && _controlPanLookList[lookCnt]->_onClick) {
508 					clickRes = handleClick(_controlPanLookList[lookCnt]);
509 					if (!_controlPanel) //game state was destroyed
510 						return;
511 					_text->flushForRedraw();
512 					drawMainPanel();
513 					_text->drawToScreen(WITH_MASK);
514 					if ((clickRes == QUIT_PANEL) || (clickRes == GAME_SAVED) ||
515 						(clickRes == GAME_RESTORED))
516 						quitPanel = true;
517 				}
518 				_mouseClicked = false;
519 			}
520 		}
521 		if (!haveButton)
522 			buttonControl(NULL);
523 	}
524 	memset(_screenBuf, 0, GAME_SCREEN_WIDTH * FULL_SCREEN_HEIGHT);
525 	_system->copyRectToScreen(_screenBuf, GAME_SCREEN_WIDTH, 0, 0, GAME_SCREEN_WIDTH, FULL_SCREEN_HEIGHT);
526 	if (!Engine::shouldQuit())
527 		_system->updateScreen();
528 	_skyScreen->forceRefresh();
529 	_skyScreen->setPaletteEndian((uint8 *)_skyCompact->fetchCpt(SkyEngine::_systemVars.currentPalette));
530 	removePanel();
531 	_skyMouse->spriteMouse(_savedMouse, 0, 0);
532 	_skyText->fnSetFont(_savedCharSet);
533 }
534 
handleClick(ConResource * pButton)535 uint16 Control::handleClick(ConResource *pButton) {
536 	char quitDos[50] = "Quit to DOS?";
537 	char restart[50] = "Restart?";
538 
539 	if (Common::parseLanguage(ConfMan.get("language")) == Common::RU_RUS) {
540 		strncpy(quitDos, "B[uti b DOC?", 50);
541 		strncpy(restart, "Hobaq irpa?", 50);
542 	}
543 
544 	switch (pButton->_onClick) {
545 	case DO_NOTHING:
546 		return 0;
547 	case REST_GAME_PANEL:
548 		if (!loadSaveAllowed())
549 			return CANCEL_PRESSED; // can't save/restore while choosing
550 		animClick(pButton);
551 		return saveRestorePanel(false); // texts can't be edited
552 	case SAVE_GAME_PANEL:
553 		if (!loadSaveAllowed())
554 			return CANCEL_PRESSED; // can't save/restore while choosing
555 		animClick(pButton);
556 		return saveRestorePanel(true); // texts can be edited
557 	case SAVE_A_GAME:
558 		animClick(pButton);
559 		return saveGameToFile(true);
560 	case RESTORE_A_GAME:
561 		animClick(pButton);
562 		return restoreGameFromFile(false);
563 	case RESTORE_AUTO:
564 		animClick(pButton);
565 		return restoreGameFromFile(true);
566 	case SP_CANCEL:
567 		animClick(pButton);
568 		return CANCEL_PRESSED;
569 	case SHIFT_DOWN_FAST:
570 		animClick(pButton);
571 		return shiftDown(FAST);
572 	case SHIFT_DOWN_SLOW:
573 		animClick(pButton);
574 		return shiftDown(SLOW);
575 	case SHIFT_UP_FAST:
576 		animClick(pButton);
577 		return shiftUp(FAST);
578 	case SHIFT_UP_SLOW:
579 		animClick(pButton);
580 		return shiftUp(SLOW);
581 	case SPEED_SLIDE:
582 		_mouseClicked = true;
583 		return doSpeedSlide();
584 	case MUSIC_SLIDE:
585 		_mouseClicked = true;
586 		return doMusicSlide();
587 	case TOGGLE_FX:
588 		toggleFx(pButton);
589 		return TOGGLED;
590 	case TOGGLE_MS:
591 		toggleMusic(pButton);
592 		return TOGGLED;
593 	case TOGGLE_TEXT:
594 		animClick(pButton);
595 		return toggleText();
596 	case EXIT:
597 		animClick(pButton);
598 		return QUIT_PANEL;
599 	case RESTART:
600 		animClick(pButton);
601 		if (getYesNo(restart)) {
602 			restartGame();
603 			return GAME_RESTORED;
604 		} else
605 			return 0;
606 	case QUIT_TO_DOS:
607 		animClick(pButton);
608 		if (getYesNo(quitDos))
609 			Engine::quitGame();
610 		return 0;
611 	default:
612 		error("Control::handleClick: unknown routine: %X",pButton->_onClick);
613 	}
614 }
615 
getYesNo(char * text)616 bool Control::getYesNo(char *text) {
617 	bool retVal = false;
618 	bool quitPanel = false;
619 	uint8 mouseType = MOUSE_NORMAL;
620 	uint8 wantMouse = MOUSE_NORMAL;
621 	DataFileHeader *dlgTextDat;
622 	uint16 textY = MPNL_Y;
623 
624 	_yesNo->drawToScreen(WITH_MASK);
625 	if (text) {
626 		DisplayedText dlgLtm = _skyText->displayText(text, NULL, true, _yesNo->_spriteData->s_width - 8, 37);
627 		dlgTextDat = (DataFileHeader *)dlgLtm.textData;
628 		textY = MPNL_Y + 44 + (28 - dlgTextDat->s_height) / 2;
629 	} else
630 		dlgTextDat = NULL;
631 
632 	TextResource *dlgText = new TextResource(dlgTextDat, 1, 0, MPNL_X+2, textY, 0, DO_NOTHING, _system, _screenBuf);
633 	dlgText->drawToScreen(WITH_MASK);
634 
635 	while (!quitPanel) {
636 		if (mouseType != wantMouse) {
637 			mouseType = wantMouse;
638 			_skyMouse->spriteMouse(mouseType, 0, 0);
639 		}
640 		_system->updateScreen();
641 		delay(ANIM_DELAY);
642 		if (!_controlPanel) {
643 			free(dlgTextDat);
644 			delete dlgText;
645 			return retVal;
646 		}
647 		Common::Point mouse = _system->getEventManager()->getMousePos();
648 		if ((mouse.y >= 83) && (mouse.y <= 110)) {
649 			if ((mouse.x >= 77) && (mouse.x <= 114)) { // over 'yes'
650 				wantMouse = MOUSE_CROSS;
651 				if (_mouseClicked) {
652 					quitPanel = true;
653 					retVal = true;
654 				}
655 			} else if ((mouse.x >= 156) && (mouse.x <= 193)) { // over 'no'
656 				wantMouse = MOUSE_CROSS;
657 				if (_mouseClicked) {
658 					quitPanel = true;
659 					retVal = false;
660 				}
661 			} else
662 				wantMouse = MOUSE_NORMAL;
663 		} else
664 			wantMouse = MOUSE_NORMAL;
665 	}
666 	_mouseClicked = false;
667 	_skyMouse->spriteMouse(MOUSE_NORMAL, 0, 0);
668 	free(dlgTextDat);
669 	delete dlgText;
670 	return retVal;
671 }
672 
doMusicSlide()673 uint16 Control::doMusicSlide() {
674 	Common::Point mouse = _system->getEventManager()->getMousePos();
675 	int ofsY = _slide2->_y - mouse.y;
676 	uint8 volume;
677 	while (_mouseClicked) {
678 		delay(ANIM_DELAY);
679 		if (!_controlPanel)
680 			return 0;
681 		mouse = _system->getEventManager()->getMousePos();
682 		int newY = ofsY + mouse.y;
683 		if (newY < 59) newY = 59;
684 		if (newY > 91) newY = 91;
685 		if (newY != _slide2->_y) {
686 			_slode->drawToScreen(NO_MASK);
687 			_slide2->setXY(_slide2->_x, (uint16)newY);
688 			_slide2->drawToScreen(WITH_MASK);
689 			_slide->drawToScreen(WITH_MASK);
690 			volume = (newY - 59) * 4;
691 			if (volume >= 128) volume = 0;
692 			else volume = 127 - volume;
693 			_skyMusic->setVolume(volume);
694 		}
695 		buttonControl(_slide2);
696 		_text->drawToScreen(WITH_MASK);
697 		_system->updateScreen();
698 	}
699 	return 0;
700 }
701 
doSpeedSlide()702 uint16 Control::doSpeedSlide() {
703 	Common::Point mouse = _system->getEventManager()->getMousePos();
704 	int ofsY = _slide->_y - mouse.y;
705 	uint16 speedDelay = _slide->_y - (MPNL_Y + 93);
706 	speedDelay *= SPEED_MULTIPLY;
707 	speedDelay += 2;
708 	while (_mouseClicked) {
709 		delay(ANIM_DELAY);
710 		if (!_controlPanel)
711 			return SPEED_CHANGED;
712 		mouse = _system->getEventManager()->getMousePos();
713 		int newY = ofsY + mouse.y;
714 		if (newY < MPNL_Y + 93) newY = MPNL_Y + 93;
715 		if (newY > MPNL_Y + 104) newY = MPNL_Y + 104;
716 		if ((newY == 110) || (newY == 108)) newY = 109;
717 		if (newY != _slide->_y) {
718 			_slode->drawToScreen(NO_MASK);
719 			_slide->setXY(_slide->_x, (uint16)newY);
720 			_slide->drawToScreen(WITH_MASK);
721 			_slide2->drawToScreen(WITH_MASK);
722 			speedDelay = newY - (MPNL_Y + 93);
723 			speedDelay *= SPEED_MULTIPLY;
724 			speedDelay += 2;
725 		}
726 		buttonControl(_slide);
727 		_text->drawToScreen(WITH_MASK);
728 		_system->updateScreen();
729 	}
730 	SkyEngine::_systemVars.gameSpeed = speedDelay;
731 	return SPEED_CHANGED;
732 }
733 
toggleFx(ConResource * pButton)734 void Control::toggleFx(ConResource *pButton) {
735 	SkyEngine::_systemVars.systemFlags ^= SF_FX_OFF;
736 	if (SkyEngine::_systemVars.systemFlags & SF_FX_OFF) {
737 		pButton->_curSprite = 0;
738 		_statusBar->setToText(0x7000 + 87);
739 	} else {
740 		pButton->_curSprite = 2;
741 		_statusBar->setToText(0x7000 + 86);
742 	}
743 
744 	ConfMan.setBool("sfx_mute", (SkyEngine::_systemVars.systemFlags & SF_FX_OFF) != 0);
745 
746 	pButton->drawToScreen(WITH_MASK);
747 	_system->updateScreen();
748 }
749 
toggleText()750 uint16 Control::toggleText() {
751 	uint32 flags = SkyEngine::_systemVars.systemFlags & TEXT_FLAG_MASK;
752 	SkyEngine::_systemVars.systemFlags &= ~TEXT_FLAG_MASK;
753 
754 	if (flags == SF_ALLOW_TEXT) {
755 		flags = SF_ALLOW_SPEECH;
756 		_statusBar->setToText(0x7000 + 21); // speech only
757 	} else if (flags == SF_ALLOW_SPEECH) {
758 		flags = SF_ALLOW_SPEECH | SF_ALLOW_TEXT;
759 		_statusBar->setToText(0x7000 + 52); // text and speech
760 	} else {
761 		flags = SF_ALLOW_TEXT;
762 		_statusBar->setToText(0x7000 + 35); // text only
763 	}
764 
765 	ConfMan.setBool("subtitles", (flags & SF_ALLOW_TEXT) != 0);
766 	ConfMan.setBool("speech_mute", (flags & SF_ALLOW_SPEECH) == 0);
767 
768 	SkyEngine::_systemVars.systemFlags |= flags;
769 
770 	drawTextCross(flags);
771 
772 	_system->updateScreen();
773 	return TOGGLED;
774 }
775 
toggleMusic(ConResource * pButton)776 void Control::toggleMusic(ConResource *pButton) {
777 	SkyEngine::_systemVars.systemFlags ^= SF_MUS_OFF;
778 	if (SkyEngine::_systemVars.systemFlags & SF_MUS_OFF) {
779 		_skyMusic->startMusic(0);
780 		pButton->_curSprite = 0;
781 		_statusBar->setToText(0x7000 + 89);
782 	} else {
783 		_skyMusic->startMusic(SkyEngine::_systemVars.currentMusic);
784 		pButton->_curSprite = 2;
785 		_statusBar->setToText(0x7000 + 88);
786 	}
787 
788 	ConfMan.setBool("music_mute", (SkyEngine::_systemVars.systemFlags & SF_MUS_OFF) != 0);
789 
790 	pButton->drawToScreen(WITH_MASK);
791 	_system->updateScreen();
792 }
793 
shiftDown(uint8 speed)794 uint16 Control::shiftDown(uint8 speed) {
795 	if (speed == SLOW) {
796 		if (_firstText >= MAX_SAVE_GAMES - MAX_ON_SCREEN)
797 			return 0;
798 		_firstText++;
799 	} else {
800 		if (_firstText <= MAX_SAVE_GAMES - 2 * MAX_ON_SCREEN)
801 			_firstText += MAX_ON_SCREEN;
802 		else if (_firstText < MAX_SAVE_GAMES - MAX_ON_SCREEN)
803 			_firstText = MAX_SAVE_GAMES - MAX_ON_SCREEN;
804 		else
805 			return 0;
806 	}
807 
808 	return SHIFTED;
809 }
810 
shiftUp(uint8 speed)811 uint16 Control::shiftUp(uint8 speed) {
812 	if (speed == SLOW) {
813 		if (_firstText > 0)
814 			_firstText--;
815 		else
816 			return 0;
817 	} else {
818 		if (_firstText >= MAX_ON_SCREEN)
819 			_firstText -= MAX_ON_SCREEN;
820 		else if (_firstText > 0)
821 			_firstText = 0;
822 		else
823 			return 0;
824 	}
825 	return SHIFTED;
826 }
827 
autoSaveExists()828 bool Control::autoSaveExists() {
829 	bool test = false;
830 	Common::InSaveFile *f;
831 	char fName[20];
832 	if (SkyEngine::isCDVersion())
833 		strcpy(fName, "SKY-VM-CD.ASD");
834 	else
835 		sprintf(fName, "SKY-VM%03d.ASD", SkyEngine::_systemVars.gameVersion);
836 
837 	f = _saveFileMan->openForLoading(fName);
838 	if (f != NULL) {
839 		test = true;
840 		delete f;
841 	}
842 	return test;
843 }
844 
saveRestorePanel(bool allowSave)845 uint16 Control::saveRestorePanel(bool allowSave) {
846 	_keyPressed.reset();
847 	_mouseWheel = 0;
848 	buttonControl(NULL);
849 	_text->drawToScreen(WITH_MASK); // flush text restore buffer
850 
851 	ConResource **lookList;
852 	uint16 cnt;
853 	uint8 lookListLen;
854 	if (allowSave) {
855 		lookList = _savePanLookList;
856 		lookListLen = 6;
857 		_system->setFeatureState(OSystem::kFeatureVirtualKeyboard, true);
858 	} else {
859 		lookList = _restorePanLookList;
860 		if (autoSaveExists())
861 			lookListLen = 7;
862 		else
863 			lookListLen = 6;
864 	}
865 	bool withAutoSave = (lookListLen == 7);
866 
867 	Common::StringArray saveGameTexts;
868 	DataFileHeader *textSprites[MAX_ON_SCREEN + 1];
869 	for (cnt = 0; cnt < MAX_ON_SCREEN + 1; cnt++)
870 		textSprites[cnt] = NULL;
871 	_firstText = 0;
872 
873 	loadDescriptions(saveGameTexts);
874 	_selectedGame = 0;
875 
876 	bool quitPanel = false;
877 	bool refreshNames = true;
878 	bool refreshAll = true;
879 	uint16 clickRes = 0;
880 	while (!quitPanel && !Engine::shouldQuit()) {
881 		clickRes = 0;
882 		if (refreshNames || refreshAll) {
883 			if (refreshAll) {
884 				_text->flushForRedraw();
885 				_savePanel->drawToScreen(NO_MASK);
886 				_quitButton->drawToScreen(NO_MASK);
887 				if (withAutoSave)
888 					_autoSaveButton->drawToScreen(NO_MASK);
889 				refreshAll = false;
890 			}
891 			for (cnt = 0; cnt < MAX_ON_SCREEN; cnt++)
892 				if (textSprites[cnt])
893 					free(textSprites[cnt]);
894 			setUpGameSprites(saveGameTexts, textSprites, _firstText, _selectedGame);
895 			showSprites(textSprites, allowSave);
896 			refreshNames = false;
897 		}
898 
899 		_text->drawToScreen(WITH_MASK);
900 		_system->updateScreen();
901 		_mouseClicked = false;
902 		delay(ANIM_DELAY);
903 		if (!_controlPanel)
904 			return clickRes;
905 		if (_keyPressed.keycode == Common::KEYCODE_ESCAPE) { // escape pressed
906 			_mouseClicked = false;
907 			clickRes = CANCEL_PRESSED;
908 			quitPanel = true;
909 		} else if ((_keyPressed.keycode == Common::KEYCODE_RETURN) || (_keyPressed.keycode == Common::KEYCODE_KP_ENTER)) {
910 			clickRes = handleClick(lookList[0]);
911 			if (!_controlPanel) //game state was destroyed
912 				return clickRes;
913 			if (clickRes == GAME_SAVED)
914 				saveDescriptions(saveGameTexts);
915 			else if (clickRes == NO_DISK_SPACE)
916 				displayMessage(0, "Could not save the game. (%s)", _saveFileMan->popErrorDesc().c_str());
917 			quitPanel = true;
918 			_mouseClicked = false;
919 			_keyPressed.reset();
920 		} if (allowSave && _keyPressed.keycode) {
921 			handleKeyPress(_keyPressed, saveGameTexts[_selectedGame]);
922 			refreshNames = true;
923 			_keyPressed.reset();
924 		}
925 
926 		if (_mouseWheel) {
927 			if (_mouseWheel < 0)
928 				clickRes = shiftUp(SLOW);
929 			else if (_mouseWheel > 0)
930 				clickRes = shiftDown(SLOW);
931 			_mouseWheel = 0;
932 			if (clickRes == SHIFTED) {
933 				_selectedGame = _firstText;
934 				refreshNames = true;
935 			}
936 		}
937 
938 		bool haveButton = false;
939 		Common::Point mouse = _system->getEventManager()->getMousePos();
940 		for (cnt = 0; cnt < lookListLen; cnt++)
941 			if (lookList[cnt]->isMouseOver(mouse.x, mouse.y)) {
942 				buttonControl(lookList[cnt]);
943 				haveButton = true;
944 
945 				if (_mouseClicked && lookList[cnt]->_onClick) {
946 					_mouseClicked = false;
947 
948 					clickRes = handleClick(lookList[cnt]);
949 					if (!_controlPanel) //game state was destroyed
950 						return clickRes;
951 
952 					if (clickRes == SHIFTED) {
953 						_selectedGame = _firstText;
954 						refreshNames = true;
955 					}
956 					if (clickRes == NO_DISK_SPACE) {
957 						displayMessage(0, "Could not save the game. (%s)", _saveFileMan->popErrorDesc().c_str());
958 						quitPanel = true;
959 					}
960 					if ((clickRes == CANCEL_PRESSED) || (clickRes == GAME_RESTORED))
961 						quitPanel = true;
962 
963 					if (clickRes == GAME_SAVED) {
964 						saveDescriptions(saveGameTexts);
965 						quitPanel = true;
966 					}
967 					if (clickRes == RESTORE_FAILED)
968 						refreshAll = true;
969 				}
970 			}
971 
972 		if (_mouseClicked) {
973 			if ((mouse.x >= GAME_NAME_X) && (mouse.x <= GAME_NAME_X + PAN_LINE_WIDTH) &&
974 				(mouse.y >= GAME_NAME_Y) && (mouse.y <= GAME_NAME_Y + PAN_CHAR_HEIGHT * MAX_ON_SCREEN)) {
975 
976 					_selectedGame = (mouse.y - GAME_NAME_Y) / PAN_CHAR_HEIGHT + _firstText;
977 					refreshNames = true;
978 			}
979 		}
980 		if (!haveButton)
981 			buttonControl(NULL);
982 	}
983 
984 	for (cnt = 0; cnt < MAX_ON_SCREEN + 1; cnt++)
985 		free(textSprites[cnt]);
986 
987 	if (allowSave) {
988 		_system->setFeatureState(OSystem::kFeatureVirtualKeyboard, false);
989 	}
990 
991 	return clickRes;
992 }
993 
handleKeyPress(Common::KeyState kbd,Common::String & textBuf)994 void Control::handleKeyPress(Common::KeyState kbd, Common::String &textBuf) {
995 	if (kbd.keycode == Common::KEYCODE_BACKSPACE) { // backspace
996 		if (textBuf.size() > 0)
997 			textBuf.deleteLastChar();
998 	} else if (kbd.ascii) {
999 		// Cannot enter text wider than the save/load panel
1000 		if (_enteredTextWidth >= PAN_LINE_WIDTH - 10)
1001 			return;
1002 
1003 		// Cannot enter text longer than MAX_TEXT_LEN-1 chars, since
1004 		// the storage is only so big. Note: The code used to incorrectly
1005 		// allow up to MAX_TEXT_LEN, which caused an out of bounds access,
1006 		// overwriting the next entry in the list of savegames partially.
1007 		// This could be triggered by e.g. entering lots of periods ".".
1008 		if (textBuf.size() >= MAX_TEXT_LEN - 1)
1009 			return;
1010 
1011 		// Allow the key only if is a letter, a digit, or one of a selected
1012 		// list of extra characters
1013 		if (Common::isAlnum(kbd.ascii) || strchr(" ,().='-&+!?\"", kbd.ascii) != 0) {
1014 			textBuf += kbd.ascii;
1015 		}
1016 	}
1017 }
1018 
setUpGameSprites(const Common::StringArray & saveGameNames,DataFileHeader ** nameSprites,uint16 firstNum,uint16 selectedGame)1019 void Control::setUpGameSprites(const Common::StringArray &saveGameNames, DataFileHeader **nameSprites, uint16 firstNum, uint16 selectedGame) {
1020 	char cursorChar[2] = "-";
1021 	DisplayedText textSpr;
1022 	if (!nameSprites[MAX_ON_SCREEN]) {
1023 		textSpr = _skyText->displayText(cursorChar, NULL, false, 15, 0);
1024 		nameSprites[MAX_ON_SCREEN] = (DataFileHeader *)textSpr.textData;
1025 	}
1026 	for (uint16 cnt = 0; cnt < MAX_ON_SCREEN; cnt++) {
1027 		char nameBuf[MAX_TEXT_LEN + 10];
1028 		sprintf(nameBuf, "%3d: %s", firstNum + cnt + 1, saveGameNames[firstNum + cnt].c_str());
1029 
1030 		if (firstNum + cnt == selectedGame)
1031 			textSpr = _skyText->displayText(nameBuf, NULL, false, PAN_LINE_WIDTH, 0);
1032 		else
1033 			textSpr = _skyText->displayText(nameBuf, NULL, false, PAN_LINE_WIDTH, 37);
1034 		nameSprites[cnt] = (DataFileHeader *)textSpr.textData;
1035 		if (firstNum + cnt == selectedGame) {
1036 			nameSprites[cnt]->flag = 1;
1037 			_enteredTextWidth = (uint16)textSpr.textWidth;
1038 		} else
1039 			nameSprites[cnt]->flag = 0;
1040 	}
1041 }
1042 
showSprites(DataFileHeader ** nameSprites,bool allowSave)1043 void Control::showSprites(DataFileHeader **nameSprites, bool allowSave) {
1044 	ConResource *drawResource = new ConResource(NULL, 1, 0, 0, 0, 0, 0, _system, _screenBuf);
1045 	for (uint16 cnt = 0; cnt < MAX_ON_SCREEN; cnt++) {
1046 		drawResource->setSprite(nameSprites[cnt]);
1047 		drawResource->setXY(GAME_NAME_X, GAME_NAME_Y + cnt * PAN_CHAR_HEIGHT);
1048 		if (nameSprites[cnt]->flag) { // name is highlighted
1049 			for (uint16 cnty = GAME_NAME_Y + cnt * PAN_CHAR_HEIGHT; cnty < GAME_NAME_Y + (cnt + 1) * PAN_CHAR_HEIGHT - 1; cnty++)
1050 				memset(_screenBuf + cnty * GAME_SCREEN_WIDTH + GAME_NAME_X, 37, PAN_LINE_WIDTH);
1051 			drawResource->drawToScreen(WITH_MASK);
1052 			if (allowSave) {
1053 				drawResource->setSprite(nameSprites[MAX_ON_SCREEN]);
1054 				drawResource->setXY(GAME_NAME_X + _enteredTextWidth + 1, GAME_NAME_Y + cnt * PAN_CHAR_HEIGHT + 4);
1055 				drawResource->drawToScreen(WITH_MASK);
1056 			}
1057 			_system->copyRectToScreen(_screenBuf + (GAME_NAME_Y + cnt * PAN_CHAR_HEIGHT) * GAME_SCREEN_WIDTH + GAME_NAME_X, GAME_SCREEN_WIDTH, GAME_NAME_X, GAME_NAME_Y + cnt * PAN_CHAR_HEIGHT, PAN_LINE_WIDTH, PAN_CHAR_HEIGHT);
1058 		} else
1059 			drawResource->drawToScreen(NO_MASK);
1060 	}
1061 	delete drawResource;
1062 }
1063 
loadDescriptions(Common::StringArray & savenames)1064 void Control::loadDescriptions(Common::StringArray &savenames) {
1065 	savenames.resize(MAX_SAVE_GAMES);
1066 
1067 	Common::InSaveFile *inf;
1068 	inf = _saveFileMan->openForLoading("SKY-VM.SAV");
1069 	if (inf != NULL) {
1070 		char *tmpBuf =  new char[MAX_SAVE_GAMES * MAX_TEXT_LEN];
1071 		char *tmpPtr = tmpBuf;
1072 		inf->read(tmpBuf, MAX_SAVE_GAMES * MAX_TEXT_LEN);
1073 		for (int i = 0; i < MAX_SAVE_GAMES; ++i) {
1074 			savenames[i] = tmpPtr;
1075 			tmpPtr += savenames[i].size() + 1;
1076 		}
1077 		delete inf;
1078 		delete[] tmpBuf;
1079 	}
1080 }
1081 
loadSaveAllowed()1082 bool Control::loadSaveAllowed() {
1083 	if (SkyEngine::_systemVars.systemFlags & SF_CHOOSING)
1084 		return false; // texts get lost during load/save, so don't allow it during choosing
1085 	if (Logic::_scriptVariables[SCREEN] >= 101)
1086 		return false; // same problem with LINC terminals
1087 	if ((Logic::_scriptVariables[SCREEN] >= 82) &&
1088 		(Logic::_scriptVariables[SCREEN] != 85) &&
1089 		(Logic::_scriptVariables[SCREEN] < 90))
1090 		return false; // don't allow saving in final rooms
1091 
1092 	return true;
1093 }
1094 
displayMessage(const char * altButton,const char * message,...)1095 int Control::displayMessage(const char *altButton, const char *message, ...) {
1096 	char buf[STRINGBUFLEN];
1097 	va_list va;
1098 
1099 	va_start(va, message);
1100 	vsnprintf(buf, STRINGBUFLEN, message, va);
1101 	va_end(va);
1102 
1103 	GUI::MessageDialog dialog(buf, "OK", altButton);
1104 	int result = dialog.runModal();
1105 	_skyMouse->spriteMouse(MOUSE_NORMAL, 0, 0);
1106 	return result;
1107 }
1108 
saveDescriptions(const Common::StringArray & list)1109 void Control::saveDescriptions(const Common::StringArray &list) {
1110 	Common::OutSaveFile *outf;
1111 
1112 	outf = _saveFileMan->openForSaving("SKY-VM.SAV");
1113 	bool ioFailed = true;
1114 	if (outf) {
1115 		for (uint16 cnt = 0; cnt < MAX_SAVE_GAMES; cnt++) {
1116 			outf->write(list[cnt].c_str(), list[cnt].size() + 1);
1117 		}
1118 		outf->finalize();
1119 		if (!outf->err())
1120 			ioFailed = false;
1121 		delete outf;
1122 	}
1123 	if (ioFailed)
1124 		displayMessage(NULL, "Unable to store Savegame names to file SKY-VM.SAV. (%s)", _saveFileMan->popErrorDesc().c_str());
1125 }
1126 
doAutoSave()1127 void Control::doAutoSave() {
1128 	char fName[20];
1129 	if (SkyEngine::isCDVersion())
1130 		strcpy(fName, "SKY-VM-CD.ASD");
1131 	else
1132 		sprintf(fName, "SKY-VM%03d.ASD", SkyEngine::_systemVars.gameVersion);
1133 
1134 	uint16 res = saveGameToFile(false, fName);
1135 
1136 	if (res != GAME_SAVED)
1137 		displayMessage(0, "Unable to perform autosave to '%s'. (%s)", fName, _saveFileMan->popErrorDesc().c_str());
1138 
1139 }
1140 
saveGameToFile(bool fromControlPanel,const char * filename)1141 uint16 Control::saveGameToFile(bool fromControlPanel, const char *filename) {
1142 	char fName[20];
1143 	if (!filename) {
1144 		sprintf(fName,"SKY-VM.%03d", _selectedGame);
1145 		filename = fName;
1146 	}
1147 
1148 	Common::OutSaveFile *outf;
1149 	outf = _saveFileMan->openForSaving(filename);
1150 	if (outf == NULL)
1151 		return NO_DISK_SPACE;
1152 
1153 	if (!fromControlPanel) {
1154 		// These variables are usually set when entering the control panel,
1155 		// but not when using the GMM.
1156 		_savedCharSet = _skyText->giveCurrentCharSet();
1157 		_savedMouse = _skyMouse->giveCurrentMouseType();
1158 	}
1159 
1160 	uint8 *saveData = (uint8 *)malloc(0x20000);
1161 	uint32 fSize = prepareSaveData(saveData);
1162 
1163 	uint32 writeRes = outf->write(saveData, fSize);
1164 	outf->finalize();
1165 	if (outf->err())
1166 		writeRes = 0;
1167 	free(saveData);
1168 	delete outf;
1169 
1170 	return (writeRes == fSize) ? GAME_SAVED : NO_DISK_SPACE;
1171 }
1172 
1173 #define STOSD(ptr, val) { *(uint32 *)(ptr) = TO_LE_32(val); (ptr) += 4; }
1174 #define STOSW(ptr, val) { *(uint16 *)(ptr) = TO_LE_16(val); (ptr) += 2; }
1175 
prepareSaveData(uint8 * destBuf)1176 uint32 Control::prepareSaveData(uint8 *destBuf) {
1177 	uint32 cnt;
1178 	memset(destBuf, 0, 4); // space for data size
1179 	uint8 *destPos = destBuf + 4;
1180 	STOSD(destPos, SAVE_FILE_REVISION);
1181 	STOSD(destPos, SkyEngine::_systemVars.gameVersion);
1182 
1183 	STOSW(destPos, _skySound->_saveSounds[0]);
1184 	STOSW(destPos, _skySound->_saveSounds[1]);
1185 
1186 	STOSD(destPos, _skyMusic->giveCurrentMusic());
1187 	STOSD(destPos, _savedCharSet);
1188 	STOSD(destPos, _savedMouse);
1189 	STOSD(destPos, SkyEngine::_systemVars.currentPalette);
1190 	for (cnt = 0; cnt < 838; cnt++)
1191 		STOSD(destPos, Logic::_scriptVariables[cnt]);
1192 	uint32 *loadedFilesList = _skyDisk->giveLoadedFilesList();
1193 
1194 	for (cnt = 0; cnt < 60; cnt++)
1195 		STOSD(destPos, loadedFilesList[cnt]);
1196 
1197 	for (cnt = 0; cnt < _skyCompact->_numSaveIds; cnt++) {
1198 		uint16 numElems;
1199 		uint16 *rawCpt = (uint16 *)_skyCompact->fetchCptInfo(_skyCompact->_saveIds[cnt], &numElems, NULL, NULL);
1200 		for (uint16 elemCnt = 0; elemCnt < numElems; elemCnt++)
1201 			STOSW(destPos, rawCpt[elemCnt]);
1202 	}
1203 
1204 	*(uint32 *)destBuf = TO_LE_32(destPos - destBuf); // save size
1205 	return destPos - destBuf;
1206 }
1207 
1208 #undef STOSD
1209 #undef STOSW
1210 
1211 #define LODSD(strPtr, val) { val = READ_LE_UINT32(strPtr); (strPtr) += 4; }
1212 #define LODSW(strPtr, val) { val = READ_LE_UINT16(strPtr); (strPtr) += 2; }
1213 
importOldMegaSet(uint8 ** srcPos,MegaSet * mega)1214 void Control::importOldMegaSet(uint8 **srcPos, MegaSet *mega) {
1215 	LODSW(*srcPos, mega->gridWidth);
1216 	LODSW(*srcPos, mega->colOffset);
1217 	LODSW(*srcPos, mega->colWidth);
1218 	LODSW(*srcPos, mega->lastChr);
1219 }
1220 
importOldCompact(Compact * destCpt,uint8 ** srcPos,uint16 numElems,uint16 type,char * name)1221 void Control::importOldCompact(Compact* destCpt, uint8 **srcPos, uint16 numElems, uint16 type, char *name) {
1222 	uint16 saveType;
1223 	LODSW(*srcPos, saveType);
1224 	if ((saveType & (SAVE_EXT | SAVE_TURNP)) && (numElems < 54))
1225 		error("Cpt %s: Savedata doesn't match cpt size (%d)", name, numElems);
1226 	if ((saveType & SAVE_MEGA0) && (numElems < 54 + 13))
1227 		error("Cpt %s: Savedata doesn't match cpt size (%d)", name, numElems);
1228 	if ((saveType & SAVE_MEGA1) && (numElems < 54 + 13 + 13))
1229 		error("Cpt %s: Savedata doesn't match cpt size (%d)", name, numElems);
1230 	if ((saveType & SAVE_MEGA2) && (numElems < 54 + 13 + 13 + 13))
1231 		error("Cpt %s: Savedata doesn't match cpt size (%d)", name, numElems);
1232 	if ((saveType & SAVE_MEGA3) && (numElems < 54 + 13 + 13 + 13))
1233 		error("Cpt %s: Savedata doesn't match cpt size (%d)", name, numElems);
1234 	if (saveType & SAVE_GRAFX) {
1235 		uint16 graphType, target, pos;
1236 		LODSW(*srcPos, graphType);
1237 		LODSW(*srcPos, target);
1238 		LODSW(*srcPos, pos);
1239 		// convert to new compact system..
1240 		destCpt->grafixProgPos = pos;
1241 		if (graphType == OG_PTR_NULL)
1242 			destCpt->grafixProgId = 0;
1243 		else if (graphType == OG_AUTOROUTE)
1244 			destCpt->grafixProgId = destCpt->animScratchId;
1245 		else if (graphType == OG_COMPACT)
1246 			destCpt->grafixProgId = target;
1247 		else if (graphType == OG_TALKTABLE)
1248 			destCpt->grafixProgId = ((uint16 *)_skyCompact->fetchCpt(CPT_TALK_TABLE_LIST))[target];
1249 		else if (graphType == OG_COMPACTELEM)
1250 			destCpt->grafixProgId = *(uint16 *)_skyCompact->getCompactElem(destCpt, target);
1251 		else
1252 			error("Illegal GrafixProg type encountered for compact %s", name);
1253 	}
1254 	if (saveType & SAVE_TURNP) {
1255 		// basically impossible to import these. simply set it to end-of-turn and hope the script
1256 		// will take care of it.
1257 		destCpt->turnProgId = 0x13B;
1258 		destCpt->turnProgPos = 1;
1259 		uint16 turnSkipLen;
1260 		LODSW(*srcPos, turnSkipLen);
1261 		*srcPos += 2 * turnSkipLen;
1262 	} else if (numElems >= 49) {
1263 		destCpt->turnProgId = 0;
1264 		destCpt->turnProgPos = 0;
1265 	}
1266 	LODSW(*srcPos, destCpt->logic);
1267 	LODSW(*srcPos, destCpt->status);
1268 	LODSW(*srcPos, destCpt->sync);
1269 	LODSW(*srcPos, destCpt->screen);
1270 	LODSW(*srcPos, destCpt->place);
1271 	// getToTable
1272 	LODSW(*srcPos, destCpt->xcood);
1273 	LODSW(*srcPos, destCpt->ycood);
1274 	LODSW(*srcPos, destCpt->frame);
1275 	LODSW(*srcPos, destCpt->cursorText);
1276 	LODSW(*srcPos, destCpt->mouseOn);
1277 	LODSW(*srcPos, destCpt->mouseOff);
1278 	LODSW(*srcPos, destCpt->mouseClick);
1279 	LODSW(*srcPos, destCpt->mouseRelX);
1280 	LODSW(*srcPos, destCpt->mouseRelY);
1281 	LODSW(*srcPos, destCpt->mouseSizeX);
1282 	LODSW(*srcPos, destCpt->mouseSizeY);
1283 	LODSW(*srcPos, destCpt->actionScript);
1284 	LODSW(*srcPos, destCpt->upFlag);
1285 	LODSW(*srcPos, destCpt->downFlag);
1286 	LODSW(*srcPos, destCpt->getToFlag);
1287 	LODSW(*srcPos, destCpt->flag);
1288 	LODSW(*srcPos, destCpt->mood);
1289 	// grafixProg
1290 	LODSW(*srcPos, destCpt->offset);
1291 	LODSW(*srcPos, destCpt->mode);
1292 	LODSW(*srcPos, destCpt->baseSub);
1293 	LODSW(*srcPos, destCpt->baseSub_off);
1294 	if (saveType & SAVE_EXT) {
1295 		LODSW(*srcPos, destCpt->actionSub);
1296 		LODSW(*srcPos, destCpt->actionSub_off);
1297 		LODSW(*srcPos, destCpt->getToSub);
1298 		LODSW(*srcPos, destCpt->getToSub_off);
1299 		LODSW(*srcPos, destCpt->extraSub);
1300 		LODSW(*srcPos, destCpt->extraSub_off);
1301 		LODSW(*srcPos, destCpt->dir);
1302 		LODSW(*srcPos, destCpt->stopScript);
1303 		LODSW(*srcPos, destCpt->miniBump);
1304 		LODSW(*srcPos, destCpt->leaving);
1305 		LODSW(*srcPos, destCpt->atWatch);
1306 		LODSW(*srcPos, destCpt->atWas);
1307 		LODSW(*srcPos, destCpt->alt);
1308 		LODSW(*srcPos, destCpt->request);
1309 		LODSW(*srcPos, destCpt->spWidth_xx);
1310 		LODSW(*srcPos, destCpt->spColor);
1311 		LODSW(*srcPos, destCpt->spTextId);
1312 		LODSW(*srcPos, destCpt->spTime);
1313 		LODSW(*srcPos, destCpt->arAnimIndex);
1314 		// turnProg
1315 		LODSW(*srcPos, destCpt->waitingFor);
1316 		LODSW(*srcPos, destCpt->arTargetX);
1317 		LODSW(*srcPos, destCpt->arTargetY);
1318 		// animScratch
1319 		LODSW(*srcPos, destCpt->megaSet);
1320 		if (saveType & SAVE_MEGA0)
1321 			importOldMegaSet(srcPos, &(destCpt->megaSet0));
1322 		if (saveType & SAVE_MEGA1)
1323 			importOldMegaSet(srcPos, &(destCpt->megaSet1));
1324 		if (saveType & SAVE_MEGA2)
1325 			importOldMegaSet(srcPos, &(destCpt->megaSet2));
1326 		if (saveType & SAVE_MEGA3)
1327 			importOldMegaSet(srcPos, &(destCpt->megaSet3));
1328 	}
1329 }
1330 
parseSaveData(uint8 * srcBuf)1331 uint16 Control::parseSaveData(uint8 *srcBuf) {
1332 	uint32 reloadList[60];
1333 	uint32 cnt;
1334 	uint8 *srcPos = srcBuf;
1335 	uint32 size;
1336 	uint32 saveRev;
1337 	uint32 gameVersion;
1338 	LODSD(srcPos, size);
1339 	LODSD(srcPos, saveRev);
1340 	if (saveRev > SAVE_FILE_REVISION) {
1341 		displayMessage(0, "Unknown save file revision (%d)", saveRev);
1342 		return RESTORE_FAILED;
1343 	} else if (saveRev < OLD_SAVEGAME_TYPE) {
1344 		displayMessage(0, "This saved game version is unsupported.");
1345 		return RESTORE_FAILED;
1346 	}
1347 	LODSD(srcPos, gameVersion);
1348 	if (gameVersion != SkyEngine::_systemVars.gameVersion) {
1349 		if ((!SkyEngine::isCDVersion()) || (gameVersion < 365)) { // cd versions are compatible
1350 			displayMessage(NULL, "This saved game was created by\n"
1351 				"Beneath a Steel Sky v0.0%03d\n"
1352 				"It cannot be loaded by this version (v0.0%3d)",
1353 				gameVersion, SkyEngine::_systemVars.gameVersion);
1354 			return RESTORE_FAILED;
1355 		}
1356 	}
1357 	SkyEngine::_systemVars.systemFlags |= SF_GAME_RESTORED;
1358 
1359 	LODSW(srcPos, _skySound->_saveSounds[0]);
1360 	LODSW(srcPos, _skySound->_saveSounds[1]);
1361 	_skySound->restoreSfx();
1362 
1363 	uint32 music, mouseType, palette;
1364 	LODSD(srcPos, music);
1365 	LODSD(srcPos, _savedCharSet);
1366 	LODSD(srcPos, mouseType);
1367 	LODSD(srcPos, palette);
1368 
1369 	_skyLogic->parseSaveData((uint32 *)srcPos);
1370 	srcPos += NUM_SKY_SCRIPTVARS * sizeof(uint32);
1371 
1372 	for (cnt = 0; cnt < 60; cnt++)
1373 		LODSD(srcPos, reloadList[cnt]);
1374 
1375 	if (saveRev == SAVE_FILE_REVISION) {
1376 		for (cnt = 0; cnt < _skyCompact->_numSaveIds; cnt++) {
1377 			uint16 numElems;
1378 			uint16 *rawCpt = (uint16 *)_skyCompact->fetchCptInfo(_skyCompact->_saveIds[cnt], &numElems, NULL, NULL);
1379 			for (uint16 elemCnt = 0; elemCnt < numElems; elemCnt++)
1380 				LODSW(srcPos, rawCpt[elemCnt]);
1381 		}
1382 	} else {	// import old savegame revision
1383 		for (cnt = 0; cnt < (uint32)(_skyCompact->_numSaveIds - 2); cnt++) {
1384 			uint16 numElems;
1385 			uint16 type;
1386 			char name[128];
1387 			uint16 *rawCpt = (uint16 *)_skyCompact->fetchCptInfo(_skyCompact->_saveIds[cnt], &numElems, &type, name);
1388 			if (type == COMPACT) {
1389 				importOldCompact((Compact *)rawCpt, &srcPos, numElems, type, name);
1390 			} else if (type == ROUTEBUF) {
1391 				assert(numElems == 32);
1392 				for (uint32 elemCnt = 0; elemCnt < numElems; elemCnt++)
1393 					LODSW(srcPos, rawCpt[elemCnt]);
1394 			}
1395 		}
1396 		uint16 *rawCpt = (uint16 *)_skyCompact->fetchCpt(0xBF);
1397 		for (cnt = 0; cnt < 3; cnt++)
1398 			LODSW(srcPos, rawCpt[cnt]);
1399 		rawCpt = (uint16 *)_skyCompact->fetchCpt(0xC2);
1400 		for (cnt = 0; cnt < 13; cnt++)
1401 			LODSW(srcPos, rawCpt[cnt]);
1402 	}
1403 
1404 	// make sure all text compacts are off
1405 	for (cnt = CPT_TEXT_1; cnt <= CPT_TEXT_11; cnt++)
1406 		_skyCompact->fetchCpt(cnt)->status = 0;
1407 
1408 	if (srcPos - srcBuf != (int32)size)
1409 		error("Restore failed! Savegame data = %lu bytes. Expected size: %d", (long)(srcPos-srcBuf), size);
1410 
1411 	_skyDisk->refreshFilesList(reloadList);
1412 	SkyEngine::_systemVars.currentMusic = (uint16)music;
1413 	if (!(SkyEngine::_systemVars.systemFlags & SF_MUS_OFF))
1414 		_skyMusic->startMusic((uint16)music);
1415 	_savedMouse = (uint16)mouseType;
1416 	SkyEngine::_systemVars.currentPalette = palette; // will be set when doControlPanel ends
1417 
1418 	return GAME_RESTORED;
1419 }
1420 
1421 
restoreGameFromFile(bool autoSave)1422 uint16 Control::restoreGameFromFile(bool autoSave) {
1423 	char fName[20];
1424 	if (autoSave) {
1425 		if (SkyEngine::isCDVersion())
1426 			strcpy(fName, "SKY-VM-CD.ASD");
1427 		else
1428 			sprintf(fName, "SKY-VM%03d.ASD", SkyEngine::_systemVars.gameVersion);
1429 	} else
1430 		sprintf(fName,"SKY-VM.%03d", _selectedGame);
1431 
1432 	Common::InSaveFile *inf;
1433 	inf = _saveFileMan->openForLoading(fName);
1434 	if (inf == NULL) {
1435 		return RESTORE_FAILED;
1436 	}
1437 
1438 	uint32 infSize = inf->readUint32LE();
1439 	if (infSize < 4) infSize = 4;
1440 	uint8 *saveData = (uint8 *)malloc(infSize);
1441 	*(uint32 *)saveData = TO_LE_32(infSize);
1442 
1443 	if (inf->read(saveData+4, infSize-4) != infSize-4) {
1444 		displayMessage(NULL, "Can't read from file '%s'", fName);
1445 		free(saveData);
1446 		delete inf;
1447 		return RESTORE_FAILED;
1448 	}
1449 
1450 	uint16 res = parseSaveData(saveData);
1451 	SkyEngine::_systemVars.pastIntro = true;
1452 	delete inf;
1453 	free(saveData);
1454 	return res;
1455 }
1456 
quickXRestore(uint16 slot)1457 uint16 Control::quickXRestore(uint16 slot) {
1458 	uint16 result;
1459 	if (!_controlPanel)
1460 		initPanel();
1461 	_mouseClicked = false;
1462 
1463 	_savedCharSet = _skyText->giveCurrentCharSet();
1464 	_skyText->fnSetFont(2);
1465 
1466 	_system->copyRectToScreen(_screenBuf, GAME_SCREEN_WIDTH, 0, 0, FULL_SCREEN_WIDTH, FULL_SCREEN_HEIGHT);
1467 	_system->updateScreen();
1468 
1469 	if (SkyEngine::_systemVars.gameVersion < 331)
1470 		_skyScreen->setPalette(60509);
1471 	else
1472 		_skyScreen->setPalette(60510);
1473 
1474 	_savedMouse = _skyMouse->giveCurrentMouseType();
1475 	_skyMouse->spriteMouse(MOUSE_NORMAL, 0, 0);
1476 
1477 	if (slot == 0)
1478 		result = restoreGameFromFile(true);
1479 	else {
1480 		_selectedGame = slot - 1;
1481 		result = restoreGameFromFile(false);
1482 	}
1483 	if (result == GAME_RESTORED) {
1484 		memset(_skyScreen->giveCurrent(), 0, GAME_SCREEN_WIDTH * GAME_SCREEN_HEIGHT);
1485 		_skyScreen->showScreen(_skyScreen->giveCurrent());
1486 		_skyScreen->forceRefresh();
1487 		_skyScreen->setPaletteEndian((uint8 *)_skyCompact->fetchCpt(SkyEngine::_systemVars.currentPalette));
1488 	} else {
1489 		memset(_screenBuf, 0, FULL_SCREEN_WIDTH * FULL_SCREEN_HEIGHT);
1490 		_system->copyRectToScreen(_screenBuf, GAME_SCREEN_WIDTH, 0, 0, GAME_SCREEN_WIDTH, FULL_SCREEN_HEIGHT);
1491 		_system->updateScreen();
1492 		_skyScreen->showScreen(_skyScreen->giveCurrent());
1493 		_skyScreen->setPalette(60111);
1494 	}
1495 	_skyMouse->spriteMouse(_savedMouse, 0, 0);
1496 	_skyText->fnSetFont(_savedCharSet);
1497 
1498 	removePanel();
1499 	return result;
1500 }
1501 
restartGame()1502 void Control::restartGame() {
1503 	if (SkyEngine::_systemVars.gameVersion <= 267)
1504 		return; // no restart for floppy demo
1505 
1506 	uint8 *resetData = _skyCompact->createResetData((uint16)SkyEngine::_systemVars.gameVersion);
1507 	parseSaveData((uint8 *)resetData);
1508 	free(resetData);
1509 	_skyScreen->forceRefresh();
1510 
1511 	memset(_skyScreen->giveCurrent(), 0, GAME_SCREEN_WIDTH * FULL_SCREEN_HEIGHT);
1512 	_skyScreen->showScreen(_skyScreen->giveCurrent());
1513 	_skyScreen->setPaletteEndian((uint8 *)_skyCompact->fetchCpt(SkyEngine::_systemVars.currentPalette));
1514 	_skyMouse->spriteMouse(_savedMouse, 0, 0);
1515 	SkyEngine::_systemVars.pastIntro = true;
1516 }
1517 
delay(unsigned int amount)1518 void Control::delay(unsigned int amount) {
1519 	Common::Event event;
1520 
1521 	uint32 start = _system->getMillis();
1522 	uint32 cur = start;
1523 	_keyPressed.reset();
1524 
1525 	do {
1526 		Common::EventManager *eventMan = _system->getEventManager();
1527 		while (eventMan->pollEvent(event)) {
1528 			switch (event.type) {
1529 			case Common::EVENT_KEYDOWN:
1530 				_keyPressed = event.kbd;
1531 				break;
1532 			case Common::EVENT_MOUSEMOVE:
1533 				if (!(SkyEngine::_systemVars.systemFlags & SF_MOUSE_LOCKED))
1534 					_skyMouse->mouseMoved(event.mouse.x, event.mouse.y);
1535 				break;
1536 			case Common::EVENT_LBUTTONDOWN:
1537 				_mouseClicked = true;
1538 				break;
1539 			case Common::EVENT_LBUTTONUP:
1540 				_mouseClicked = false;
1541 				break;
1542 			case Common::EVENT_RBUTTONDOWN:
1543 				break;
1544 			case Common::EVENT_WHEELUP:
1545 				_mouseWheel = -1;
1546 				break;
1547 			case Common::EVENT_WHEELDOWN:
1548 				_mouseWheel = 1;
1549 				break;
1550 			default:
1551 				break;
1552 			}
1553 		}
1554 
1555 		uint this_delay = 20; // 1?
1556 #ifdef _WIN32_WCE
1557 		this_delay = 10;
1558 #endif
1559 		if (this_delay > amount)
1560 			this_delay = amount;
1561 
1562 		if (this_delay > 0)	_system->delayMillis(this_delay);
1563 
1564 		cur = _system->getMillis();
1565 	} while (cur < start + amount);
1566 }
1567 
showGameQuitMsg()1568 void Control::showGameQuitMsg() {
1569 	_skyText->fnSetFont(0);
1570 	uint8 *textBuf1 = (uint8 *)malloc(GAME_SCREEN_WIDTH * 14 + sizeof(DataFileHeader));
1571 	uint8 *textBuf2 = (uint8 *)malloc(GAME_SCREEN_WIDTH * 14 + sizeof(DataFileHeader));
1572 	uint8 *screenData;
1573 	if (_skyScreen->sequenceRunning())
1574 		_skyScreen->stopSequence();
1575 
1576 	screenData = _skyScreen->giveCurrent();
1577 
1578 	if (Common::parseLanguage(ConfMan.get("language")) == Common::RU_RUS) {
1579 		_skyText->displayText(_quitTexts[8 * 2 + 0], textBuf1, true, 320, 255);
1580 		_skyText->displayText(_quitTexts[8 * 2 + 1], textBuf2, true, 320, 255);
1581 	} else {
1582 		_skyText->displayText(_quitTexts[SkyEngine::_systemVars.language * 2 + 0], textBuf1, true, 320, 255);
1583 		_skyText->displayText(_quitTexts[SkyEngine::_systemVars.language * 2 + 1], textBuf2, true, 320, 255);
1584 	}
1585 	uint8 *curLine1 = textBuf1 + sizeof(DataFileHeader);
1586 	uint8 *curLine2 = textBuf2 + sizeof(DataFileHeader);
1587 	uint8 *targetLine = screenData + GAME_SCREEN_WIDTH * 80;
1588 	for (uint8 cnty = 0; cnty < PAN_CHAR_HEIGHT; cnty++) {
1589 		for (uint16 cntx = 0; cntx < GAME_SCREEN_WIDTH; cntx++) {
1590 			if (curLine1[cntx])
1591 				targetLine[cntx] = curLine1[cntx];
1592 			if (curLine2[cntx])
1593 				(targetLine + 24 * GAME_SCREEN_WIDTH)[cntx] = curLine2[cntx];
1594 		}
1595 		curLine1 += GAME_SCREEN_WIDTH;
1596 		curLine2 += GAME_SCREEN_WIDTH;
1597 		targetLine += GAME_SCREEN_WIDTH;
1598 	}
1599 	_skyScreen->halvePalette();
1600 	_skyScreen->showScreen(screenData);
1601 	free(textBuf1);
1602 	free(textBuf2);
1603 }
1604 
1605 char Control::_quitTexts[18][35] = {
1606 	"Game over player one",
1607 	"BE VIGILANT",
1608 	"Das Spiel ist aus.",
1609 	"SEI WACHSAM",
1610 	"Game over joueur 1",
1611 	"SOYEZ VIGILANTS",
1612 	"Game over player one",
1613 	"BE VIGILANT",
1614 	"SPELET \x2Ar SLUT, Agent 1.",
1615 	"VAR VAKSAM",
1616 	"Game over giocatore 1",
1617 	"SIATE VIGILANTI",
1618 	"Fim de jogo para o jogador um",
1619 	"BE VIGILANT",
1620 	"Game over player one",
1621 	"BE VIGILANT",
1622 	"Irpa okohseha, irpok 1",
1623 	"JYD\x96 JDITELEH"
1624 };
1625 
1626 uint8 Control::_crossImg[594] = {
1627 	0xFF, 0xFF, 0xFF, 0xFF, 0x09, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
1628 	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x0B, 0x61, 0xFF, 0xFF, 0xFF, 0xFF, 0x4F, 0x4D, 0x61,
1629 	0x01, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
1630 	0x08, 0x4E, 0x53, 0x50, 0x4F, 0x0C, 0x4D, 0x4E, 0x51, 0x58, 0x58, 0x54, 0x4E, 0x08, 0xFF, 0xFF,
1631 	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x4E, 0x54, 0x58, 0x50, 0x4E, 0xFF,
1632 	0xFF, 0xFF, 0xFF, 0x50, 0x4E, 0x54, 0x58, 0x58, 0x54, 0x4E, 0x0C, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
1633 	0xFF, 0xFF, 0xFF, 0x61, 0x53, 0x58, 0x54, 0x4E, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
1634 	0x50, 0x4E, 0x55, 0x58, 0x58, 0x53, 0x4E, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x05, 0x51, 0x58, 0x58,
1635 	0x51, 0x50, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x4F, 0x51, 0x58,
1636 	0x59, 0x58, 0x51, 0x61, 0xFF, 0xFF, 0x61, 0x54, 0x58, 0x58, 0x4F, 0x52, 0xFF, 0xFF, 0xFF, 0xFF,
1637 	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x4E, 0x55, 0x58, 0x58, 0x57, 0x4E,
1638 	0x4F, 0x56, 0x58, 0x57, 0x61, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
1639 	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x4F, 0x51, 0x58, 0x58, 0x58, 0x58, 0x58, 0x54, 0x4E, 0xFF,
1640 	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
1641 	0xFF, 0xFF, 0x6A, 0x4F, 0x58, 0x58, 0x58, 0x58, 0x52, 0x06, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
1642 	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x04, 0x54, 0x58,
1643 	0x58, 0x58, 0x58, 0x57, 0x53, 0x61, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
1644 	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x04, 0x09, 0x58, 0x58, 0x58, 0x57, 0x56, 0x58, 0x58, 0x58,
1645 	0x57, 0x4F, 0x0A, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
1646 	0x61, 0x55, 0x58, 0x58, 0x58, 0x58, 0x4E, 0x64, 0x57, 0x58, 0x58, 0x58, 0x58, 0x53, 0x61, 0xFF,
1647 	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x61, 0x57, 0x58, 0x58, 0x58, 0x58,
1648 	0x50, 0xFF, 0xFF, 0x4E, 0x57, 0x58, 0x58, 0x58, 0x58, 0x56, 0x61, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
1649 	0xFF, 0xFF, 0xFF, 0xFF, 0x61, 0x58, 0x58, 0x58, 0x58, 0x58, 0x53, 0x09, 0xFF, 0xFF, 0xFF, 0x4E,
1650 	0x57, 0x58, 0x58, 0x58, 0x58, 0x58, 0x0B, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x61, 0x57,
1651 	0x58, 0x58, 0x58, 0x58, 0x56, 0x4E, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x61, 0x58, 0x58, 0x58, 0x58,
1652 	0x58, 0x57, 0x61, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x04, 0x55, 0x58, 0x58, 0x58, 0x58, 0x58, 0x4E,
1653 	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x4F, 0x58, 0x58, 0x58, 0x58, 0x4E, 0xFF, 0xFF, 0xFF,
1654 	0xFF, 0xFF, 0xFF, 0x06, 0x58, 0x58, 0x58, 0x58, 0x58, 0x52, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
1655 	0xFF, 0xFF, 0x0C, 0x52, 0x58, 0x58, 0x51, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x61, 0x56, 0x58,
1656 	0x58, 0x58, 0x58, 0x56, 0x61, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x61, 0x56,
1657 	0x58, 0x61, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x0F, 0x4D, 0x4D, 0x51, 0x56, 0x58, 0x58, 0x50, 0xFF,
1658 	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x4F, 0x54, 0x09, 0xFF, 0xFF, 0xFF,
1659 	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x4E, 0x50, 0x54, 0x61, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
1660 	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x06, 0x50, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
1661 	0xFF, 0xFF, 0xFF, 0x61, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
1662 	0xFF, 0x61, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x61, 0xFF,
1663 	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x61, 0xFF, 0xFF, 0xFF,
1664 	0xFF, 0xFF
1665 };
1666 
1667 } // End of namespace Sky
1668