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