mirror of
https://github.com/libretro/scummvm.git
synced 2024-12-13 04:28:37 +00:00
509 lines
13 KiB
C++
509 lines
13 KiB
C++
/* ScummVM - Graphic Adventure Engine
|
|
*
|
|
* ScummVM is the legal property of its developers, whose names
|
|
* are too numerous to list here. Please refer to the COPYRIGHT
|
|
* file distributed with this source distribution.
|
|
*
|
|
* This program is free software; you can redistribute it and/or
|
|
* modify it under the terms of the GNU General Public License
|
|
* as published by the Free Software Foundation; either version 2
|
|
* of the License, or (at your option) any later version.
|
|
*
|
|
* This program is distributed in the hope that it will be useful,
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
* GNU General Public License for more details.
|
|
*
|
|
* You should have received a copy of the GNU General Public License
|
|
* along with this program; if not, write to the Free Software
|
|
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
|
*
|
|
*/
|
|
|
|
#include "common/scummsys.h"
|
|
#include "common/error.h"
|
|
#include "common/system.h"
|
|
#include "common/textconsole.h"
|
|
#include "common/debug-channels.h"
|
|
|
|
#include "common/events.h"
|
|
#include "common/file.h"
|
|
|
|
#include "engines/util.h"
|
|
|
|
#include "audio/mixer.h"
|
|
#include "graphics/palette.h"
|
|
|
|
#include "cryomni3d/cryomni3d.h"
|
|
#include "cryomni3d/datstream.h"
|
|
|
|
#include "cryomni3d/image/hlz.h"
|
|
#include "video/hnm_decoder.h"
|
|
|
|
namespace CryOmni3D {
|
|
|
|
CryOmni3DEngine::CryOmni3DEngine(OSystem *syst,
|
|
const CryOmni3DGameDescription *gamedesc) : Engine(syst), _gameDescription(gamedesc),
|
|
_canLoadSave(false), _fontManager(), _sprites(), _dragStatus(kDragStatus_NoDrag), _lastMouseButton(0),
|
|
_autoRepeatNextEvent(uint(-1)), _hnmHasClip(false) {
|
|
if (!_mixer->isReady()) {
|
|
error("Sound initialization failed");
|
|
}
|
|
|
|
// Setup mixer
|
|
syncSoundSettings();
|
|
|
|
unlockPalette();
|
|
}
|
|
|
|
CryOmni3DEngine::~CryOmni3DEngine() {
|
|
}
|
|
|
|
Common::Error CryOmni3DEngine::run() {
|
|
return Common::kNoError;
|
|
}
|
|
|
|
void CryOmni3DEngine::pauseEngineIntern(bool pause) {
|
|
Engine::pauseEngineIntern(pause);
|
|
|
|
/*
|
|
if (pause) {
|
|
_video->pauseVideos();
|
|
} else {
|
|
_video->resumeVideos();
|
|
_system->updateScreen();
|
|
}
|
|
*/
|
|
}
|
|
|
|
DATSeekableStream *CryOmni3DEngine::getStaticData(uint32 gameId, uint16 version) const {
|
|
Common::File *datFile = new Common::File();
|
|
|
|
if (!datFile->open("cryomni3d.dat")) {
|
|
delete datFile;
|
|
error("Failed to open cryomni3d.dat file");
|
|
return nullptr;
|
|
}
|
|
|
|
DATSeekableStream *gameStream = DATSeekableStream::getGame(datFile, gameId, version, getLanguage(),
|
|
getPlatform());
|
|
if (!gameStream) {
|
|
delete datFile;
|
|
error("Failed to find game in cryomni3d.dat file");
|
|
return nullptr;
|
|
}
|
|
|
|
return gameStream;
|
|
}
|
|
|
|
Common::String CryOmni3DEngine::prepareFileName(const Common::String &baseName,
|
|
const char *const *extensions) const {
|
|
Common::String fname(baseName);
|
|
|
|
int lastDotPos = fname.size() - 1;
|
|
for (; lastDotPos >= 0; --lastDotPos) {
|
|
if (fname[lastDotPos] == '.') {
|
|
break;
|
|
}
|
|
}
|
|
|
|
int extBegin;
|
|
if (lastDotPos > -1) {
|
|
extBegin = lastDotPos + 1;
|
|
fname.erase(extBegin);
|
|
} else {
|
|
fname += ".";
|
|
extBegin = fname.size();
|
|
}
|
|
|
|
while (*extensions != nullptr) {
|
|
fname += *extensions;
|
|
debug("Trying file %s", fname.c_str());
|
|
if (Common::File::exists(fname)) {
|
|
return fname;
|
|
}
|
|
fname.erase(extBegin);
|
|
extensions++;
|
|
}
|
|
fname.deleteLastChar();
|
|
warning("Failed to find file %s/%s", baseName.c_str(), fname.c_str());
|
|
return baseName;
|
|
}
|
|
|
|
void CryOmni3DEngine::playHNM(const Common::String &filename, Audio::Mixer::SoundType soundType,
|
|
HNMCallback beforeDraw, HNMCallback afterDraw) {
|
|
const char *const extensions[] = { "hns", "hnm", "ubb", nullptr };
|
|
Common::String fname(prepareFileName(filename, extensions));
|
|
|
|
byte *currentPalette = new byte[256 * 3];
|
|
g_system->getPaletteManager()->grabPalette(currentPalette, 0, 256);
|
|
|
|
// Pass the ownership of currentPalette to HNMDecoder
|
|
Video::VideoDecoder *videoDecoder = new Video::HNMDecoder(false, currentPalette);
|
|
videoDecoder->setSoundType(soundType);
|
|
|
|
if (!videoDecoder->loadFile(fname)) {
|
|
warning("Failed to open movie file %s/%s", filename.c_str(), fname.c_str());
|
|
delete videoDecoder;
|
|
return;
|
|
}
|
|
|
|
videoDecoder->start();
|
|
|
|
uint16 width = videoDecoder->getWidth();
|
|
uint16 height = videoDecoder->getHeight();
|
|
|
|
bool skipVideo = false;
|
|
uint frameNum = 0;
|
|
while (!shouldAbort() && !videoDecoder->endOfVideo() && !skipVideo) {
|
|
if (videoDecoder->needsUpdate()) {
|
|
const Graphics::Surface *frame = videoDecoder->decodeNextFrame();
|
|
|
|
if (frame) {
|
|
if (videoDecoder->hasDirtyPalette()) {
|
|
const byte *palette = videoDecoder->getPalette();
|
|
setPalette(palette, 0, 256);
|
|
}
|
|
|
|
if (beforeDraw) {
|
|
(this->*beforeDraw)(frameNum);
|
|
}
|
|
|
|
if (_hnmHasClip) {
|
|
Common::Rect rct(width, height);
|
|
rct.clip(_hnmClipping);
|
|
g_system->copyRectToScreen(frame->getPixels(), frame->pitch, rct.left, rct.top, rct.width(),
|
|
rct.height());
|
|
} else {
|
|
g_system->copyRectToScreen(frame->getPixels(), frame->pitch, 0, 0, width, height);
|
|
}
|
|
|
|
if (afterDraw) {
|
|
(this->*afterDraw)(frameNum);
|
|
}
|
|
|
|
frameNum++;
|
|
}
|
|
}
|
|
g_system->updateScreen();
|
|
g_system->delayMillis(10);
|
|
|
|
if (pollEvents() && checkKeysPressed()) {
|
|
skipVideo = true;
|
|
}
|
|
}
|
|
|
|
delete videoDecoder;
|
|
}
|
|
|
|
Image::ImageDecoder *CryOmni3DEngine::loadHLZ(const Common::String &filename) {
|
|
Common::String fname(prepareFileName(filename, "hlz"));
|
|
|
|
Common::File file;
|
|
|
|
if (!file.open(fname)) {
|
|
warning("Failed to open hlz file %s/%s", filename.c_str(), fname.c_str());
|
|
return nullptr;
|
|
}
|
|
|
|
Image::ImageDecoder *imageDecoder = new Image::HLZFileDecoder();
|
|
|
|
if (!imageDecoder->loadStream(file)) {
|
|
warning("Failed to open hlz file %s", fname.c_str());
|
|
delete imageDecoder;
|
|
imageDecoder = 0;
|
|
return nullptr;
|
|
}
|
|
|
|
return imageDecoder;
|
|
}
|
|
|
|
bool CryOmni3DEngine::displayHLZ(const Common::String &filename, uint32 timeout) {
|
|
Image::ImageDecoder *imageDecoder = loadHLZ(filename);
|
|
|
|
if (!imageDecoder) {
|
|
return false;
|
|
}
|
|
|
|
if (imageDecoder->hasPalette()) {
|
|
const byte *palette = imageDecoder->getPalette();
|
|
setPalette(palette, imageDecoder->getPaletteStartIndex(), imageDecoder->getPaletteColorCount());
|
|
}
|
|
|
|
const Graphics::Surface *frame = imageDecoder->getSurface();
|
|
g_system->copyRectToScreen(frame->getPixels(), frame->pitch, 0, 0, frame->w, frame->h);
|
|
g_system->updateScreen();
|
|
|
|
uint32 end;
|
|
if (timeout == uint(-1)) {
|
|
end = uint(-1);
|
|
} else {
|
|
end = g_system->getMillis() + timeout;
|
|
}
|
|
bool exitImg = false;
|
|
while (!shouldAbort() && !exitImg && g_system->getMillis() < end) {
|
|
if (pollEvents()) {
|
|
if (checkKeysPressed() || getCurrentMouseButton() == 1) {
|
|
exitImg = true;
|
|
}
|
|
}
|
|
g_system->updateScreen();
|
|
g_system->delayMillis(10);
|
|
}
|
|
|
|
delete imageDecoder;
|
|
|
|
return exitImg || shouldAbort();
|
|
}
|
|
|
|
void CryOmni3DEngine::setCursor(const Graphics::Cursor &cursor) const {
|
|
CursorMan.replaceCursor(&cursor);
|
|
}
|
|
|
|
void CryOmni3DEngine::setCursor(uint cursorId) const {
|
|
const Graphics::Cursor &cursor = _sprites.getCursor(cursorId);
|
|
CursorMan.replaceCursor(&cursor);
|
|
}
|
|
|
|
bool CryOmni3DEngine::pollEvents() {
|
|
Common::Event event;
|
|
int buttonMask;
|
|
bool hasEvents = false;
|
|
|
|
// Don't take into transitional clicks for the drag
|
|
buttonMask = g_system->getEventManager()->getButtonState();
|
|
uint oldMouseButton;
|
|
if (buttonMask & 0x1) {
|
|
oldMouseButton = 1;
|
|
} else if (buttonMask & 0x2) {
|
|
oldMouseButton = 2;
|
|
} else {
|
|
oldMouseButton = 0;
|
|
}
|
|
|
|
int transitionalMask = 0;
|
|
while (g_system->getEventManager()->pollEvent(event)) {
|
|
if (event.type == Common::EVENT_KEYDOWN) {
|
|
_keysPressed.push(event.kbd);
|
|
} else if (event.type == Common::EVENT_LBUTTONDOWN) {
|
|
transitionalMask |= Common::EventManager::LBUTTON;
|
|
} else if (event.type == Common::EVENT_RBUTTONDOWN) {
|
|
transitionalMask |= Common::EventManager::RBUTTON;
|
|
}
|
|
hasEvents = true;
|
|
}
|
|
|
|
// Merge current button state with any buttons pressed since last poll
|
|
// That's to avoid missed clicks
|
|
buttonMask = g_system->getEventManager()->getButtonState() |
|
|
transitionalMask;
|
|
if (buttonMask & 0x1) {
|
|
_lastMouseButton = 1;
|
|
} else if (buttonMask & 0x2) {
|
|
_lastMouseButton = 2;
|
|
} else {
|
|
_lastMouseButton = 0;
|
|
}
|
|
|
|
_dragStatus = kDragStatus_NoDrag;
|
|
uint currentMouseButton = getCurrentMouseButton();
|
|
if (!oldMouseButton && currentMouseButton == 1) {
|
|
// Starting the drag
|
|
_dragStatus = kDragStatus_Pressed;
|
|
_dragStart = getMousePos();
|
|
} else if (oldMouseButton == 1) {
|
|
// We were already pressing
|
|
if (currentMouseButton == 1) {
|
|
// We are still pressing
|
|
Common::Point delta = _dragStart - getMousePos();
|
|
if (ABS(delta.x) > 2 || ABS(delta.y) > 2) {
|
|
// We moved from the start point
|
|
_dragStatus = kDragStatus_Dragging;
|
|
} else if (_autoRepeatNextEvent != uint(-1)) {
|
|
// Check for auto repeat duration
|
|
if (_autoRepeatNextEvent < g_system->getMillis()) {
|
|
_dragStatus = kDragStatus_Pressed;
|
|
}
|
|
}
|
|
} else {
|
|
// We just finished dragging
|
|
_dragStatus = kDragStatus_Finished;
|
|
// Cancel auto repeat
|
|
_autoRepeatNextEvent = uint(-1);
|
|
}
|
|
}
|
|
// Else we weren't dragging and still aren't
|
|
|
|
return hasEvents;
|
|
}
|
|
|
|
void CryOmni3DEngine::setAutoRepeatClick(uint millis) {
|
|
_autoRepeatNextEvent = g_system->getMillis() + millis;
|
|
}
|
|
|
|
void CryOmni3DEngine::waitMouseRelease() {
|
|
while (getCurrentMouseButton() != 0 && !shouldAbort()) {
|
|
pollEvents();
|
|
g_system->updateScreen();
|
|
g_system->delayMillis(10);
|
|
}
|
|
}
|
|
|
|
void CryOmni3DEngine::setMousePos(const Common::Point &point) {
|
|
g_system->warpMouse(point.x, point.y);
|
|
// Ensure to update mouse position in event manager
|
|
pollEvents();
|
|
}
|
|
|
|
Common::Point CryOmni3DEngine::getMousePos() {
|
|
return g_system->getEventManager()->getMousePos();
|
|
}
|
|
|
|
Common::KeyState CryOmni3DEngine::getNextKey() {
|
|
if (_keysPressed.empty()) {
|
|
return Common::KeyState();
|
|
} else {
|
|
return _keysPressed.pop();
|
|
}
|
|
}
|
|
|
|
bool CryOmni3DEngine::checkKeysPressed() {
|
|
Common::KeyCode kc = getNextKey().keycode;
|
|
if (kc != Common::KEYCODE_INVALID) {
|
|
clearKeys();
|
|
return true;
|
|
} else {
|
|
return false;
|
|
}
|
|
}
|
|
|
|
bool CryOmni3DEngine::checkKeysPressed(uint numKeys, ...) {
|
|
bool found = false;
|
|
Common::KeyCode kc = getNextKey().keycode;
|
|
while (!found && kc != Common::KEYCODE_INVALID) {
|
|
va_list va;
|
|
va_start(va, numKeys);
|
|
for (uint i = 0; i < numKeys; i++) {
|
|
// Compiler says that KeyCode is promoted to int, so we need this ugly cast
|
|
Common::KeyCode match = (Common::KeyCode) va_arg(va, int);
|
|
if (match == kc) {
|
|
found = true;
|
|
break;
|
|
}
|
|
}
|
|
va_end(va);
|
|
kc = getNextKey().keycode;
|
|
}
|
|
clearKeys();
|
|
return found;
|
|
}
|
|
|
|
void CryOmni3DEngine::copySubPalette(byte *dst, const byte *src, uint start, uint num) {
|
|
assert(start < 256);
|
|
assert(start + num < 256);
|
|
memcpy(&dst[3 * start], &src[3 * start], 3 * num * sizeof(*dst));
|
|
}
|
|
|
|
void CryOmni3DEngine::setPalette(const byte *colors, uint start, uint num) {
|
|
if (start < _lockPaletteStartRW) {
|
|
colors = colors + 3 * (_lockPaletteStartRW - start);
|
|
start = _lockPaletteStartRW;
|
|
}
|
|
uint end = start + num - 1;
|
|
if (end > _lockPaletteEndRW) {
|
|
num = num - (end - _lockPaletteEndRW);
|
|
end = _lockPaletteEndRW;
|
|
}
|
|
g_system->getPaletteManager()->setPalette(colors, start, num);
|
|
// Don't update screen there: palette will be updated with next updateScreen call
|
|
}
|
|
|
|
void CryOmni3DEngine::fadeOutPalette() {
|
|
byte palOut[256 * 3];
|
|
uint16 palWork[256 * 3];
|
|
uint16 delta[256 * 3];
|
|
|
|
g_system->getPaletteManager()->grabPalette(palOut, 0, 256);
|
|
for (uint i = 0; i < 256 * 3; i++) {
|
|
palWork[i] = palOut[i] << 8;
|
|
delta[i] = palWork[i] / 25;
|
|
}
|
|
|
|
for (uint step = 0; step < 25 && !shouldAbort(); step++) {
|
|
for (uint i = 0; i < 256 * 3; i++) {
|
|
palWork[i] -= delta[i];
|
|
palOut[i] = palWork[i] >> 8;
|
|
}
|
|
setPalette(palOut, 0, 256);
|
|
// Wait 50ms between each steps but refresh screen every 10ms
|
|
for (uint i = 0; i < 5; i++) {
|
|
pollEvents();
|
|
g_system->updateScreen();
|
|
g_system->delayMillis(10);
|
|
}
|
|
}
|
|
setBlackPalette();
|
|
pollEvents();
|
|
g_system->updateScreen();
|
|
clearKeys();
|
|
}
|
|
|
|
void CryOmni3DEngine::fadeInPalette(const byte *palette) {
|
|
byte palOut[256 * 3];
|
|
uint16 palWork[256 * 3];
|
|
uint16 delta[256 * 3];
|
|
|
|
memset(palOut, 0, sizeof(palOut));
|
|
memset(palWork, 0, sizeof(palWork));
|
|
for (uint i = 0; i < 256 * 3; i++) {
|
|
delta[i] = (palette[i] << 8) / 25;
|
|
}
|
|
|
|
setBlackPalette();
|
|
for (uint step = 0; step < 25 && !shouldAbort(); step++) {
|
|
for (uint i = 0; i < 256 * 3; i++) {
|
|
palWork[i] += delta[i];
|
|
palOut[i] = palWork[i] >> 8;
|
|
}
|
|
setPalette(palOut, 0, 256);
|
|
// Wait 50ms between each steps but refresh screen every 10ms
|
|
for (uint i = 0; i < 5; i++) {
|
|
pollEvents();
|
|
g_system->updateScreen();
|
|
g_system->delayMillis(10);
|
|
}
|
|
}
|
|
setPalette(palette, 0, 256);
|
|
pollEvents();
|
|
g_system->updateScreen();
|
|
clearKeys();
|
|
}
|
|
|
|
void CryOmni3DEngine::setBlackPalette() {
|
|
byte pal[256 * 3];
|
|
memset(pal, 0, 256 * 3);
|
|
g_system->getPaletteManager()->setPalette(pal, 0, 256);
|
|
g_system->updateScreen();
|
|
}
|
|
|
|
void CryOmni3DEngine::fillSurface(byte color) {
|
|
g_system->fillScreen(color);
|
|
g_system->updateScreen();
|
|
}
|
|
|
|
Common::Error CryOmni3DEngine_HNMPlayer::run() {
|
|
CryOmni3DEngine::run();
|
|
|
|
initGraphics(640, 480);
|
|
|
|
syncSoundSettings();
|
|
|
|
for (int i = 0; _gameDescription->desc.filesDescriptions[i].fileName; i++) {
|
|
playHNM(_gameDescription->desc.filesDescriptions[i].fileName, Audio::Mixer::kMusicSoundType);
|
|
}
|
|
|
|
return Common::kNoError;
|
|
}
|
|
|
|
} // End of namespace CryOmni3D
|