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 "common/scummsys.h"
24 #include "common/error.h"
25 #include "common/system.h"
26 #include "common/textconsole.h"
27 #include "common/translation.h"
28 #include "common/debug-channels.h"
29 
30 #include "common/events.h"
31 #include "common/file.h"
32 
33 #include "audio/mixer.h"
34 #include "graphics/palette.h"
35 
36 #include "cryomni3d/cryomni3d.h"
37 #include "cryomni3d/datstream.h"
38 
39 #include "cryomni3d/image/hlz.h"
40 #include "cryomni3d/video/hnm_decoder.h"
41 
42 namespace CryOmni3D {
43 
CryOmni3DEngine(OSystem * syst,const CryOmni3DGameDescription * gamedesc)44 CryOmni3DEngine::CryOmni3DEngine(OSystem *syst,
45                                  const CryOmni3DGameDescription *gamedesc) : Engine(syst), _gameDescription(gamedesc),
46 	_canLoadSave(false), _fontManager(), _sprites(), _dragStatus(kDragStatus_NoDrag),
47 	_autoRepeatNextEvent(uint(-1)) {
48 	if (!_mixer->isReady()) {
49 		error("Sound initialization failed");
50 	}
51 
52 	// Setup mixer
53 	syncSoundSettings();
54 
55 	unlockPalette();
56 
57 	DebugMan.addDebugChannel(kDebugFile, "File", "Track File Accesses");
58 	DebugMan.addDebugChannel(kDebugVariable, "Variable", "Track Variable Accesses");
59 	DebugMan.addDebugChannel(kDebugSaveLoad, "SaveLoad", "Track Save/Load Function");
60 }
61 
~CryOmni3DEngine()62 CryOmni3DEngine::~CryOmni3DEngine() {
63 	DebugMan.clearAllDebugChannels();
64 }
65 
run()66 Common::Error CryOmni3DEngine::run() {
67 	return Common::kNoError;
68 }
69 
pauseEngineIntern(bool pause)70 void CryOmni3DEngine::pauseEngineIntern(bool pause) {
71 	Engine::pauseEngineIntern(pause);
72 
73 	/*
74 	if (pause) {
75 	    _video->pauseVideos();
76 	} else {
77 	    _video->resumeVideos();
78 	    _system->updateScreen();
79 	}
80 	*/
81 }
82 
getStaticData(uint32 gameId,uint16 version) const83 DATSeekableStream *CryOmni3DEngine::getStaticData(uint32 gameId, uint16 version) const {
84 	Common::File *datFile = new Common::File();
85 
86 	if (!datFile->open("cryomni3d.dat")) {
87 		delete datFile;
88 		error("Failed to open cryomni3d.dat file");
89 		return nullptr;
90 	}
91 
92 	DATSeekableStream *gameStream = DATSeekableStream::getGame(datFile, gameId, version, getLanguage(),
93 	                                getPlatform());
94 	if (!gameStream) {
95 		delete datFile;
96 		error("Failed to find game in cryomni3d.dat file");
97 		return nullptr;
98 	}
99 
100 	return gameStream;
101 }
102 
prepareFileName(const Common::String & baseName,const char * const * extensions) const103 Common::String CryOmni3DEngine::prepareFileName(const Common::String &baseName,
104         const char *const *extensions) const {
105 	Common::String fname(baseName);
106 
107 	int lastDotPos = fname.size() - 1;
108 	for (; lastDotPos >= 0; --lastDotPos) {
109 		if (fname[lastDotPos] == '.') {
110 			break;
111 		}
112 	}
113 
114 	int extBegin;
115 	if (lastDotPos > -1) {
116 		extBegin = lastDotPos + 1;
117 		fname.erase(extBegin);
118 	} else {
119 		fname += ".";
120 		extBegin = fname.size();
121 	}
122 
123 	while (*extensions != nullptr) {
124 		fname += *extensions;
125 		debug("Trying file %s", fname.c_str());
126 		if (Common::File::exists(fname)) {
127 			return fname;
128 		}
129 		fname.erase(extBegin);
130 		extensions++;
131 	}
132 	fname.deleteLastChar();
133 	warning("Failed to find file %s/%s", baseName.c_str(), fname.c_str());
134 	return baseName;
135 }
136 
playHNM(const Common::String & filename,Audio::Mixer::SoundType soundType,HNMCallback beforeDraw,HNMCallback afterDraw)137 void CryOmni3DEngine::playHNM(const Common::String &filename, Audio::Mixer::SoundType soundType,
138                               HNMCallback beforeDraw, HNMCallback afterDraw) {
139 	const char *const extensions[] = { "hns", "hnm", nullptr };
140 	Common::String fname(prepareFileName(filename, extensions));
141 
142 	byte *currentPalette = new byte[256 * 3];
143 	g_system->getPaletteManager()->grabPalette(currentPalette, 0, 256);
144 
145 	// Pass the ownership of currentPalette to HNMDecoder
146 	Video::VideoDecoder *videoDecoder = new Video::HNMDecoder(false, currentPalette);
147 	videoDecoder->setSoundType(soundType);
148 
149 	if (!videoDecoder->loadFile(fname)) {
150 		warning("Failed to open movie file %s/%s", filename.c_str(), fname.c_str());
151 		delete videoDecoder;
152 		return;
153 	}
154 
155 	videoDecoder->start();
156 
157 	uint16 width = videoDecoder->getWidth();
158 	uint16 height = videoDecoder->getHeight();
159 
160 	bool skipVideo = false;
161 	uint frameNum = 0;
162 	while (!shouldAbort() && !videoDecoder->endOfVideo() && !skipVideo) {
163 		if (videoDecoder->needsUpdate()) {
164 			const Graphics::Surface *frame = videoDecoder->decodeNextFrame();
165 
166 			if (frame) {
167 				if (videoDecoder->hasDirtyPalette()) {
168 					const byte *palette = videoDecoder->getPalette();
169 					setPalette(palette, 0, 256);
170 				}
171 
172 				if (beforeDraw) {
173 					(this->*beforeDraw)(frameNum);
174 				}
175 				g_system->copyRectToScreen(frame->getPixels(), frame->pitch, 0, 0, width, height);
176 				if (afterDraw) {
177 					(this->*afterDraw)(frameNum);
178 				}
179 
180 				frameNum++;
181 			}
182 		}
183 		g_system->updateScreen();
184 		g_system->delayMillis(10);
185 
186 		if (pollEvents() && checkKeysPressed()) {
187 			skipVideo = true;
188 		}
189 	}
190 
191 	delete videoDecoder;
192 }
193 
loadHLZ(const Common::String & filename)194 Image::ImageDecoder *CryOmni3DEngine::loadHLZ(const Common::String &filename) {
195 	Common::String fname(prepareFileName(filename, "hlz"));
196 
197 	Image::ImageDecoder *imageDecoder = new Image::HLZFileDecoder();
198 
199 	Common::File file;
200 
201 	if (!file.open(fname)) {
202 		warning("Failed to open hlz file %s/%s", filename.c_str(), fname.c_str());
203 		return nullptr;
204 	}
205 
206 	if (!imageDecoder->loadStream(file)) {
207 		warning("Failed to open hlz file %s", fname.c_str());
208 		delete imageDecoder;
209 		imageDecoder = 0;
210 		return nullptr;
211 	}
212 
213 	return imageDecoder;
214 }
215 
displayHLZ(const Common::String & filename,uint32 timeout)216 bool CryOmni3DEngine::displayHLZ(const Common::String &filename, uint32 timeout) {
217 	Image::ImageDecoder *imageDecoder = loadHLZ(filename);
218 
219 	if (!imageDecoder) {
220 		return false;
221 	}
222 
223 	if (imageDecoder->hasPalette()) {
224 		const byte *palette = imageDecoder->getPalette();
225 		setPalette(palette, imageDecoder->getPaletteStartIndex(), imageDecoder->getPaletteColorCount());
226 	}
227 
228 	const Graphics::Surface *frame = imageDecoder->getSurface();
229 	g_system->copyRectToScreen(frame->getPixels(), frame->pitch, 0, 0, frame->w, frame->h);
230 	g_system->updateScreen();
231 
232 	uint32 end;
233 	if (timeout == uint(-1)) {
234 		end = uint(-1);
235 	} else {
236 		end = g_system->getMillis() + timeout;
237 	}
238 	bool exitImg = false;
239 	while (!shouldAbort() && !exitImg && g_system->getMillis() < end) {
240 		if (pollEvents()) {
241 			if (checkKeysPressed() || getCurrentMouseButton() == 1) {
242 				exitImg = true;
243 			}
244 		}
245 		g_system->updateScreen();
246 		g_system->delayMillis(10);
247 	}
248 
249 	delete imageDecoder;
250 
251 	return exitImg || shouldAbort();
252 }
253 
setCursor(const Graphics::Cursor & cursor) const254 void CryOmni3DEngine::setCursor(const Graphics::Cursor &cursor) const {
255 	CursorMan.replaceCursor(&cursor);
256 }
257 
setCursor(uint cursorId) const258 void CryOmni3DEngine::setCursor(uint cursorId) const {
259 	const Graphics::Cursor &cursor = _sprites.getCursor(cursorId);
260 	CursorMan.replaceCursor(&cursor);
261 }
262 
pollEvents()263 bool CryOmni3DEngine::pollEvents() {
264 	Common::Event event;
265 	int buttonMask;
266 	bool hasEvents = false;
267 
268 	// Don't take into transitional clicks for the drag
269 	buttonMask = g_system->getEventManager()->getButtonState();
270 	uint oldMouseButton;
271 	if (buttonMask & 0x1) {
272 		oldMouseButton = 1;
273 	} else if (buttonMask & 0x2) {
274 		oldMouseButton = 2;
275 	} else {
276 		oldMouseButton = 0;
277 	}
278 
279 	int transitionalMask = 0;
280 	while (g_system->getEventManager()->pollEvent(event)) {
281 		if (event.type == Common::EVENT_KEYDOWN) {
282 			_keysPressed.push(event.kbd);
283 		} else if (event.type == Common::EVENT_LBUTTONDOWN) {
284 			transitionalMask |= Common::EventManager::LBUTTON;
285 		} else if (event.type == Common::EVENT_RBUTTONDOWN) {
286 			transitionalMask |= Common::EventManager::RBUTTON;
287 		}
288 		hasEvents = true;
289 	}
290 
291 	// Merge current button state with any buttons pressed since last poll
292 	// That's to avoid missed clicks
293 	buttonMask = g_system->getEventManager()->getButtonState() |
294 	             transitionalMask;
295 	if (buttonMask & 0x1) {
296 		_lastMouseButton = 1;
297 	} else if (buttonMask & 0x2) {
298 		_lastMouseButton = 2;
299 	} else {
300 		_lastMouseButton = 0;
301 	}
302 
303 	_dragStatus = kDragStatus_NoDrag;
304 	uint currentMouseButton = getCurrentMouseButton();
305 	if (!oldMouseButton && currentMouseButton == 1) {
306 		// Starting the drag
307 		_dragStatus = kDragStatus_Pressed;
308 		_dragStart = getMousePos();
309 	} else if (oldMouseButton == 1) {
310 		// We were already pressing
311 		if (currentMouseButton == 1) {
312 			// We are still pressing
313 			Common::Point delta = _dragStart - getMousePos();
314 			if (ABS(delta.x) > 2 || ABS(delta.y) > 2) {
315 				// We moved from the start point
316 				_dragStatus = kDragStatus_Dragging;
317 			} else if (_autoRepeatNextEvent != uint(-1)) {
318 				// Check for auto repeat duration
319 				if (_autoRepeatNextEvent < g_system->getMillis()) {
320 					_dragStatus = kDragStatus_Pressed;
321 				}
322 			}
323 		} else {
324 			// We just finished dragging
325 			_dragStatus = kDragStatus_Finished;
326 			// Cancel auto repeat
327 			_autoRepeatNextEvent = uint(-1);
328 		}
329 	}
330 	// Else we weren't dragging and still aren't
331 
332 	return hasEvents;
333 }
334 
setAutoRepeatClick(uint millis)335 void CryOmni3DEngine::setAutoRepeatClick(uint millis) {
336 	_autoRepeatNextEvent = g_system->getMillis() + millis;
337 }
338 
waitMouseRelease()339 void CryOmni3DEngine::waitMouseRelease() {
340 	while (getCurrentMouseButton() != 0 && !shouldAbort()) {
341 		pollEvents();
342 		g_system->updateScreen();
343 		g_system->delayMillis(10);
344 	}
345 }
346 
setMousePos(const Common::Point & point)347 void CryOmni3DEngine::setMousePos(const Common::Point &point) {
348 	g_system->warpMouse(point.x, point.y);
349 	// Ensure to update mouse position in event manager
350 	pollEvents();
351 }
352 
getMousePos()353 Common::Point CryOmni3DEngine::getMousePos() {
354 	return g_system->getEventManager()->getMousePos();
355 }
356 
getNextKey()357 Common::KeyState CryOmni3DEngine::getNextKey() {
358 	if (_keysPressed.empty()) {
359 		return Common::KeyState();
360 	} else {
361 		return _keysPressed.pop();
362 	}
363 }
364 
checkKeysPressed()365 bool CryOmni3DEngine::checkKeysPressed() {
366 	Common::KeyCode kc = getNextKey().keycode;
367 	if (kc != Common::KEYCODE_INVALID) {
368 		clearKeys();
369 		return true;
370 	} else {
371 		return false;
372 	}
373 }
374 
checkKeysPressed(uint numKeys,...)375 bool CryOmni3DEngine::checkKeysPressed(uint numKeys, ...) {
376 	bool found = false;
377 	Common::KeyCode kc = getNextKey().keycode;
378 	while (!found && kc != Common::KEYCODE_INVALID) {
379 		va_list va;
380 		va_start(va, numKeys);
381 		for (uint i = 0; i < numKeys; i++) {
382 			// Compiler says that KeyCode is promoted to int, so we need this ugly cast
383 			Common::KeyCode match = (Common::KeyCode) va_arg(va, int);
384 			if (match == kc) {
385 				found = true;
386 				break;
387 			}
388 		}
389 		va_end(va);
390 		kc = getNextKey().keycode;
391 	}
392 	clearKeys();
393 	return found;
394 }
395 
copySubPalette(byte * dst,const byte * src,uint start,uint num)396 void CryOmni3DEngine::copySubPalette(byte *dst, const byte *src, uint start, uint num) {
397 	assert(start < 256);
398 	assert(start + num < 256);
399 	memcpy(&dst[3 * start], &src[3 * start], 3 * num * sizeof(*dst));
400 }
401 
setPalette(const byte * colors,uint start,uint num)402 void CryOmni3DEngine::setPalette(const byte *colors, uint start, uint num) {
403 	if (start < _lockPaletteStartRW) {
404 		colors = colors + 3 * (_lockPaletteStartRW - start);
405 		start = _lockPaletteStartRW;
406 	}
407 	uint end = start + num - 1;
408 	if (end > _lockPaletteEndRW) {
409 		num = num - (end - _lockPaletteEndRW);
410 		end = _lockPaletteEndRW;
411 	}
412 	g_system->getPaletteManager()->setPalette(colors, start, num);
413 	// Don't update screen there: palette will be updated with next updateScreen call
414 }
415 
fadeOutPalette()416 void CryOmni3DEngine::fadeOutPalette() {
417 	byte palOut[256 * 3];
418 	uint16 palWork[256 * 3];
419 	uint16 delta[256 * 3];
420 
421 	g_system->getPaletteManager()->grabPalette(palOut, 0, 256);
422 	for (uint i = 0; i < 256 * 3; i++) {
423 		palWork[i] = palOut[i] << 8;
424 		delta[i] = palWork[i] / 25;
425 	}
426 
427 	for (uint step = 0; step < 25 && !shouldAbort(); step++) {
428 		for (uint i = 0; i < 256 * 3; i++) {
429 			palWork[i] -= delta[i];
430 			palOut[i] = palWork[i] >> 8;
431 		}
432 		setPalette(palOut, 0, 256);
433 		// Wait 50ms between each steps but refresh screen every 10ms
434 		for (uint i = 0; i < 5; i++) {
435 			pollEvents();
436 			g_system->updateScreen();
437 			g_system->delayMillis(10);
438 		}
439 	}
440 	setBlackPalette();
441 	pollEvents();
442 	g_system->updateScreen();
443 	clearKeys();
444 }
445 
fadeInPalette(const byte * palette)446 void CryOmni3DEngine::fadeInPalette(const byte *palette) {
447 	byte palOut[256 * 3];
448 	uint16 palWork[256 * 3];
449 	uint16 delta[256 * 3];
450 
451 	memset(palOut, 0, sizeof(palOut));
452 	memset(palWork, 0, sizeof(palWork));
453 	for (uint i = 0; i < 256 * 3; i++) {
454 		delta[i] = (palette[i] << 8) / 25;
455 	}
456 
457 	setBlackPalette();
458 	for (uint step = 0; step < 25 && !shouldAbort(); step++) {
459 		for (uint i = 0; i < 256 * 3; i++) {
460 			palWork[i] += delta[i];
461 			palOut[i] = palWork[i] >> 8;
462 		}
463 		setPalette(palOut, 0, 256);
464 		// Wait 50ms between each steps but refresh screen every 10ms
465 		for (uint i = 0; i < 5; i++) {
466 			pollEvents();
467 			g_system->updateScreen();
468 			g_system->delayMillis(10);
469 		}
470 	}
471 	setPalette(palette, 0, 256);
472 	pollEvents();
473 	g_system->updateScreen();
474 	clearKeys();
475 }
476 
setBlackPalette()477 void CryOmni3DEngine::setBlackPalette() {
478 	byte pal[256 * 3];
479 	memset(pal, 0, 256 * 3);
480 	g_system->getPaletteManager()->setPalette(pal, 0, 256);
481 	g_system->updateScreen();
482 }
483 
fillSurface(byte color)484 void CryOmni3DEngine::fillSurface(byte color) {
485 	g_system->fillScreen(color);
486 	g_system->updateScreen();
487 }
488 } // End of namespace CryOmni3D
489