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 "base/plugins.h"
24 
25 #include "common/config-manager.h"
26 #include "common/events.h"
27 #include "common/file.h"
28 #include "common/fs.h"
29 #include "common/savefile.h"
30 #include "common/system.h"
31 #include "common/textconsole.h"
32 
33 #include "engines/util.h"
34 
35 #include "queen/queen.h"
36 #include "queen/bankman.h"
37 #include "queen/command.h"
38 #include "queen/cutaway.h"
39 #include "queen/debug.h"
40 #include "queen/display.h"
41 #include "queen/graphics.h"
42 #include "queen/grid.h"
43 #include "queen/input.h"
44 #include "queen/logic.h"
45 #include "queen/resource.h"
46 #include "queen/sound.h"
47 #include "queen/talk.h"
48 #include "queen/walk.h"
49 
50 namespace Queen {
51 
QueenEngine(OSystem * syst)52 QueenEngine::QueenEngine(OSystem *syst)
53 	: Engine(syst), _gameStarted(false), _debugger(0), randomizer("queen") {
54 }
55 
~QueenEngine()56 QueenEngine::~QueenEngine() {
57 	delete _bam;
58 	delete _resource;
59 	delete _bankMan;
60 	delete _command;
61 	delete _debugger;
62 	delete _display;
63 	delete _graphics;
64 	delete _grid;
65 	delete _input;
66 	delete _logic;
67 	delete _sound;
68 	delete _walk;
69 }
70 
registerDefaultSettings()71 void QueenEngine::registerDefaultSettings() {
72 	ConfMan.registerDefault("talkspeed", Logic::DEFAULT_TALK_SPEED);
73 	ConfMan.registerDefault("subtitles", true);
74 	_subtitles = true;
75 }
76 
checkOptionSettings()77 void QueenEngine::checkOptionSettings() {
78 	// check talkspeed value
79 	if (_talkSpeed < MIN_TEXT_SPEED) {
80 		_talkSpeed = MIN_TEXT_SPEED;
81 	} else if (_talkSpeed > MAX_TEXT_SPEED) {
82 		_talkSpeed = MAX_TEXT_SPEED;
83 	}
84 
85 	// demo and interview versions don't have speech at all
86 	if (_sound->speechOn() && (_resource->isDemo() || _resource->isInterview())) {
87 		_sound->speechToggle(false);
88 	}
89 
90 	// ensure text is always on when voice is off
91 	if (!_sound->speechOn()) {
92 		_subtitles = true;
93 	}
94 }
95 
syncSoundSettings()96 void QueenEngine::syncSoundSettings() {
97 	Engine::syncSoundSettings();
98 
99 	readOptionSettings();
100 }
101 
readOptionSettings()102 void QueenEngine::readOptionSettings() {
103 	bool mute = false;
104 	if (ConfMan.hasKey("mute"))
105 		mute = ConfMan.getBool("mute");
106 
107 	_sound->setVolume(ConfMan.getInt("music_volume"));
108 	_sound->musicToggle(!(mute || ConfMan.getBool("music_mute")));
109 	_sound->sfxToggle(!(mute || ConfMan.getBool("sfx_mute")));
110 	_sound->speechToggle(!(mute || ConfMan.getBool("speech_mute")));
111 	_talkSpeed = (ConfMan.getInt("talkspeed") * (MAX_TEXT_SPEED - MIN_TEXT_SPEED) + 255 / 2) / 255 + MIN_TEXT_SPEED;
112 	_subtitles = ConfMan.getBool("subtitles");
113 	checkOptionSettings();
114 }
115 
writeOptionSettings()116 void QueenEngine::writeOptionSettings() {
117 	ConfMan.setInt("music_volume", _sound->getVolume());
118 	ConfMan.setBool("music_mute", !_sound->musicOn());
119 	ConfMan.setBool("sfx_mute", !_sound->sfxOn());
120 	ConfMan.setInt("talkspeed", ((_talkSpeed - MIN_TEXT_SPEED) * 255 + (MAX_TEXT_SPEED - MIN_TEXT_SPEED) / 2) / (MAX_TEXT_SPEED - MIN_TEXT_SPEED));
121 	ConfMan.setBool("speech_mute", !_sound->speechOn());
122 	ConfMan.setBool("subtitles", _subtitles);
123 	ConfMan.flushToDisk();
124 }
125 
update(bool checkPlayerInput)126 void QueenEngine::update(bool checkPlayerInput) {
127 	_debugger->onFrame();
128 
129 	_graphics->update(_logic->currentRoom());
130 	_logic->update();
131 
132 	int frameDelay = (_lastUpdateTime + Input::DELAY_NORMAL - _system->getMillis());
133 	if (frameDelay <= 0) {
134 		frameDelay = 1;
135 	}
136 	_input->delay(frameDelay);
137 	_lastUpdateTime = _system->getMillis();
138 
139 	if (!_resource->isInterview()) {
140 		_display->palCustomScroll(_logic->currentRoom());
141 	}
142 	BobSlot *joe = _graphics->bob(0);
143 	_display->update(joe->active, joe->x, joe->y);
144 
145 	_input->checkKeys();
146 	if (_input->debugger()) {
147 		_input->debuggerReset();
148 		_debugger->attach();
149 	}
150 	if (canLoadOrSave()) {
151 		if (_input->quickSave()) {
152 			_input->quickSaveReset();
153 			saveGameState(SLOT_QUICKSAVE, "Quicksave");
154 		}
155 		if (_input->quickLoad()) {
156 			_input->quickLoadReset();
157 			loadGameState(SLOT_QUICKSAVE);
158 		}
159 		if (shouldPerformAutoSave(_lastSaveTime)) {
160 			saveGameState(SLOT_AUTOSAVE, "Autosave");
161 			_lastSaveTime = _system->getMillis();
162 		}
163 	}
164 	if (!_input->cutawayRunning()) {
165 		if (checkPlayerInput) {
166 			_command->updatePlayer();
167 		}
168 		if (_input->idleTime() >= Input::DELAY_SCREEN_BLANKER) {
169 			_display->blankScreen();
170 		}
171 	}
172 	_sound->updateMusic();
173 }
174 
canLoadOrSave() const175 bool QueenEngine::canLoadOrSave() const {
176 	return !_input->cutawayRunning() && !(_resource->isDemo() || _resource->isInterview()) && _gameStarted;
177 }
178 
canLoadGameStateCurrently()179 bool QueenEngine::canLoadGameStateCurrently() {
180 	return canLoadOrSave();
181 }
182 
canSaveGameStateCurrently()183 bool QueenEngine::canSaveGameStateCurrently() {
184 	return canLoadOrSave();
185 }
186 
saveGameState(int slot,const Common::String & desc)187 Common::Error QueenEngine::saveGameState(int slot, const Common::String &desc) {
188 	debug(3, "Saving game to slot %d", slot);
189 	char name[20];
190 	Common::Error err = Common::kNoError;
191 	makeGameStateName(slot, name);
192 	Common::OutSaveFile *file = _saveFileMan->openForSaving(name);
193 	if (file) {
194 		// save data
195 		byte *saveData = new byte[SAVESTATE_MAX_SIZE];
196 		byte *p = saveData;
197 		_bam->saveState(p);
198 		_grid->saveState(p);
199 		_logic->saveState(p);
200 		_sound->saveState(p);
201 		uint32 dataSize = p - saveData;
202 		assert(dataSize < SAVESTATE_MAX_SIZE);
203 
204 		// write header
205 		file->writeUint32BE('SCVM');
206 		file->writeUint32BE(SAVESTATE_CUR_VER);
207 		file->writeUint32BE(0);
208 		file->writeUint32BE(dataSize);
209 		char description[32];
210 		Common::strlcpy(description, desc.c_str(), sizeof(description));
211 		file->write(description, sizeof(description));
212 
213 		// write save data
214 		file->write(saveData, dataSize);
215 		file->finalize();
216 
217 		// check for errors
218 		if (file->err()) {
219 			warning("Can't write file '%s'. (Disk full?)", name);
220 			err = Common::kWritingFailed;
221 		}
222 		delete[] saveData;
223 		delete file;
224 	} else {
225 		warning("Can't create file '%s', game not saved", name);
226 		err = Common::kCreatingFileFailed;
227 	}
228 
229 	return err;
230 }
231 
loadGameState(int slot)232 Common::Error QueenEngine::loadGameState(int slot) {
233 	debug(3, "Loading game from slot %d", slot);
234 	Common::Error err = Common::kNoError;
235 	GameStateHeader header;
236 	Common::InSaveFile *file = readGameStateHeader(slot, &header);
237 	if (file && header.dataSize != 0) {
238 		byte *saveData = new byte[header.dataSize];
239 		byte *p = saveData;
240 		if (file->read(saveData, header.dataSize) != header.dataSize) {
241 			warning("Error reading savegame file");
242 			err = Common::kReadingFailed;
243 		} else {
244 			_bam->loadState(header.version, p);
245 			_grid->loadState(header.version, p);
246 			_logic->loadState(header.version, p);
247 			_sound->loadState(header.version, p);
248 			if (header.dataSize != (uint32)(p - saveData)) {
249 				warning("Corrupted savegame file");
250 				err = Common::kReadingFailed;	// FIXME
251 			} else {
252 				_logic->setupRestoredGame();
253 			}
254 		}
255 		delete[] saveData;
256 		delete file;
257 	} else {
258 		err = Common::kReadingFailed;
259 	}
260 
261 	return err;
262 }
263 
readGameStateHeader(int slot,GameStateHeader * gsh)264 Common::InSaveFile *QueenEngine::readGameStateHeader(int slot, GameStateHeader *gsh) {
265 	char name[20];
266 	makeGameStateName(slot, name);
267 	Common::InSaveFile *file = _saveFileMan->openForLoading(name);
268 	if (file && file->readUint32BE() == MKTAG('S','C','V','M')) {
269 		gsh->version = file->readUint32BE();
270 		gsh->flags = file->readUint32BE();
271 		gsh->dataSize = file->readUint32BE();
272 		file->read(gsh->description, sizeof(gsh->description));
273 	} else {
274 		memset(gsh, 0, sizeof(GameStateHeader));
275 	}
276 	return file;
277 }
278 
makeGameStateName(int slot,char * buf) const279 void QueenEngine::makeGameStateName(int slot, char *buf) const {
280 	if (slot == SLOT_LISTPREFIX) {
281 		strcpy(buf, "queen.s??");
282 	} else if (slot == SLOT_AUTOSAVE) {
283 		strcpy(buf, "queen.asd");
284 	} else {
285 		assert(slot >= 0);
286 		sprintf(buf, "queen.s%02d", slot);
287 	}
288 }
289 
getGameStateSlot(const char * filename) const290 int QueenEngine::getGameStateSlot(const char *filename) const {
291 	int i = -1;
292 	const char *slot = strrchr(filename, '.');
293 	if (slot && (slot[1] == 's' || slot[1] == 'S')) {
294 		i = atoi(slot + 2);
295 	}
296 	return i;
297 }
298 
findGameStateDescriptions(char descriptions[100][32])299 void QueenEngine::findGameStateDescriptions(char descriptions[100][32]) {
300 	char prefix[20];
301 	makeGameStateName(SLOT_LISTPREFIX, prefix);
302 	Common::StringArray filenames = _saveFileMan->listSavefiles(prefix);
303 	for (Common::StringArray::const_iterator it = filenames.begin(); it != filenames.end(); ++it) {
304 		int i = getGameStateSlot(it->c_str());
305 		if (i >= 0 && i < SAVESTATE_MAX_NUM) {
306 			GameStateHeader header;
307 			Common::InSaveFile *f = readGameStateHeader(i, &header);
308 			strcpy(descriptions[i], header.description);
309 			delete f;
310 		}
311 	}
312 }
313 
getDebugger()314 GUI::Debugger *QueenEngine::getDebugger() {
315 	return _debugger;
316 }
317 
hasFeature(EngineFeature f) const318 bool Queen::QueenEngine::hasFeature(EngineFeature f) const {
319 	return
320 		(f == kSupportsRTL) ||
321 		(f == kSupportsLoadingDuringRuntime) ||
322 		(f == kSupportsSavingDuringRuntime) ||
323 		(f == kSupportsSubtitleOptions);
324 }
325 
run()326 Common::Error QueenEngine::run() {
327 	initGraphics(GAME_SCREEN_WIDTH, GAME_SCREEN_HEIGHT);
328 
329 	_resource = new Resource();
330 
331 	_bam = new BamScene(this);
332 	_bankMan = new BankManager(_resource);
333 	_command = new Command(this);
334 	_debugger = new Debugger(this);
335 	_display = new Display(this, _system);
336 	_graphics = new Graphics(this);
337 	_grid = new Grid(this);
338 	_input = new Input(_resource->getLanguage(), _system);
339 
340 	if (_resource->isDemo()) {
341 		_logic = new LogicDemo(this);
342 	} else if (_resource->isInterview()) {
343 		_logic = new LogicInterview(this);
344 	} else {
345 		_logic = new LogicGame(this);
346 	}
347 
348 	_sound = Sound::makeSoundInstance(_mixer, this, _resource->getCompression());
349 
350 	_walk = new Walk(this);
351 	//_talkspeedScale = (MAX_TEXT_SPEED - MIN_TEXT_SPEED) / 255.0;
352 
353 	registerDefaultSettings();
354 
355 	// Setup mixer
356 	syncSoundSettings();
357 
358 	_logic->start();
359 	if (ConfMan.hasKey("save_slot") && canLoadOrSave()) {
360 		loadGameState(ConfMan.getInt("save_slot"));
361 	}
362 	_lastSaveTime = _lastUpdateTime = _system->getMillis();
363 
364 	while (!shouldQuit()) {
365 		if (_logic->newRoom() > 0) {
366 			_logic->update();
367 			_logic->oldRoom(_logic->currentRoom());
368 			_logic->currentRoom(_logic->newRoom());
369 			_logic->changeRoom();
370 			_display->fullscreen(false);
371 			// From this point onwards it is safe to use the load/save
372 			// menu, so consider game to be 'started'
373 			_gameStarted = true;
374 			if (_logic->currentRoom() == _logic->newRoom()) {
375 				_logic->newRoom(0);
376 			}
377 		} else if (_logic->joeWalk() == JWM_EXECUTE) {
378 			_logic->joeWalk(JWM_NORMAL);
379 			_command->executeCurrentAction();
380 		} else {
381 			_logic->joeWalk(JWM_NORMAL);
382 			update(true);
383 		}
384 	}
385 
386 	return Common::kNoError;
387 }
388 
389 } // End of namespace Queen
390