diff options
51 files changed, 550 insertions, 178 deletions
@@ -1322,34 +1322,39 @@ sequencer support does not work, you can always fall back on Adlib emulation. 7.6.1) Playing sound with ALSA sequencer: [UNIX ONLY] ------ ---------------------------------- -If you have installed the ALSA driver with the sequencer support, then -set the environment variable SCUMMVM_PORT or the config file parameter -alsa_port to your sequencer port. The default is "65:0". +If you have installed the ALSA driver with the sequencer support, then set the +environment variable SCUMMVM_PORT or the config file parameter alsa_port to +your sequencer port. The default is to try both "65:0" and "17:0". Here is a little howto on how to use the ALSA sequencer with your soundcard. In all cases, to have a list of all the sequencer ports you have, try the command "aconnect -o -l". This should give output similar to: -client 64: 'External MIDI 0' [type=kernel] - 0 'MIDI 0-0 ' -client 65: 'Emu10k1 WaveTable' [type=kernel] + +client 14: 'Midi Through' [type=kernel] + 0 'Midi Through Port-0' +client 16: 'SBLive! Value [CT4832]' [type=kernel] + 0 'EMU10K1 MPU-401 (UART)' +client 17: 'Emu10k1 WaveTable' [type=kernel] 0 'Emu10k1 Port 0 ' 1 'Emu10k1 Port 1 ' 2 'Emu10k1 Port 2 ' 3 'Emu10k1 Port 3 ' -client 128: 'Client-128' [type=user] +client 128: 'TiMidity' [type=user] 0 'TiMidity port 0 ' 1 'TiMidity port 1 ' + 2 'TiMidity port 2 ' + 3 'TiMidity port 3 ' -This means the external MIDI output of the sound card is located on the -port 64:0, four WaveTable MIDI outputs in 65:0, 65:1, 65:2 -and 65:3, and two TiMidity ports, located at 128:0 and 128:1. +The most important bit here is that there are four WaveTable MIDI outputs +located at 17:0, 17:1, 17:2 and 17:3, and four TiMidity ports located at 128:0, +128:1, 128:2 and 128:3. If you have a FM-chip on your card, like the SB16, then you have to load the SoundFonts using the sbiload software. Example: - sbiload -p 65:0 /etc/std.o3 /etc/drums.o3 + sbiload -p 17:0 /etc/std.o3 /etc/drums.o3 If you have a WaveTable capable sound card, you have to load a sbk or sf2 -SoundFont using the sfxload software. Example: +SoundFont using the sfxload or asfxload software. Example: sfxload /path/to/8mbgmsfx.sf2 If you don't have a MIDI capable soundcard, there are two options: FluidSynth diff --git a/backends/fs/windows/windows-fs.cpp b/backends/fs/windows/windows-fs.cpp index ac2f521e21..77530c6073 100644 --- a/backends/fs/windows/windows-fs.cpp +++ b/backends/fs/windows/windows-fs.cpp @@ -260,11 +260,6 @@ AbstractFilesystemNode *WindowsFilesystemNode::getChild(const String &n) const { newPath += '\\'; newPath += n; - // Check whether the directory actually exists - DWORD fileAttribs = GetFileAttributes(toUnicode(newPath.c_str())); - if (fileAttribs == INVALID_FILE_ATTRIBUTES) - return 0; - return new WindowsFilesystemNode(newPath, false); } diff --git a/backends/midi/alsa.cpp b/backends/midi/alsa.cpp index 2a252b9323..5a978a0fd2 100644 --- a/backends/midi/alsa.cpp +++ b/backends/midi/alsa.cpp @@ -79,20 +79,18 @@ MidiDriver_ALSA::MidiDriver_ALSA() } int MidiDriver_ALSA::open() { - const char *var; + const char *var = NULL; if (_isOpen) return MERR_ALREADY_OPEN; _isOpen = true; - if (!(var = getenv("SCUMMVM_PORT"))) { - // use config option if no var specified + var = getenv("SCUMMVM_PORT"); + if (!var && ConfMan.hasKey("alsa_port")) { var = ConfMan.get("alsa_port").c_str(); - if (parse_addr(var, &seq_client, &seq_port) < 0) { - error("Invalid port %s", var); - return -1; - } - } else { + } + + if (var) { if (parse_addr(var, &seq_client, &seq_port) < 0) { error("Invalid port %s", var); return -1; @@ -120,14 +118,32 @@ int MidiDriver_ALSA::open() { return -1; } - if (seq_client != SND_SEQ_ADDRESS_SUBSCRIBERS) { - /* subscribe to MIDI port */ - if (snd_seq_connect_to(seq_handle, my_port, seq_client, seq_port) < 0) { - error("Can't subscribe to MIDI port (%d:%d) see README for help", seq_client, seq_port); + if (var) { + if (seq_client != SND_SEQ_ADDRESS_SUBSCRIBERS) { + // subscribe to MIDI port + if (snd_seq_connect_to(seq_handle, my_port, seq_client, seq_port) < 0) { + error("Can't subscribe to MIDI port (%d:%d) see README for help", seq_client, seq_port); + } } - else printf("Connected to Alsa sequencer client [%d:%d]\n", seq_client, seq_port); + } else { + int defaultPorts[] = { + 65, 0, + 17, 0 + }; + int i; + + for (i = 0; i < ARRAYSIZE(defaultPorts); i += 2) { + seq_client = defaultPorts[i]; + seq_port = defaultPorts[i + 1]; + if (snd_seq_connect_to(seq_handle, my_port, seq_client, seq_port) >= 0) + break; + } + + if (i >= ARRAYSIZE(defaultPorts)) + error("Can't subscribe to MIDI port (65:0) or (17:0)"); } + printf("Connected to Alsa sequencer client [%d:%d]\n", seq_client, seq_port); printf("ALSA client initialised [%d:0]\n", my_client); return 0; diff --git a/backends/platform/wince/CEActionsPocket.cpp b/backends/platform/wince/CEActionsPocket.cpp index 45310dba88..932599004d 100644 --- a/backends/platform/wince/CEActionsPocket.cpp +++ b/backends/platform/wince/CEActionsPocket.cpp @@ -121,7 +121,7 @@ void CEActionsPocket::initInstanceGame() { bool is_comi = (strncmp(gameid.c_str(), "comi", 4) == 0); bool is_gob = (strncmp(gameid.c_str(), "gob", 3) == 0); bool is_saga = (gameid == "saga"); - bool is_kyra = (gameid == "kyra1"); + bool is_kyra = (strncmp(gameid.c_str(), "kyra",4) == 0); bool is_samnmax = (gameid == "samnmax"); bool is_cine = (gameid == "cine"); bool is_touche = (gameid == "touche"); diff --git a/backends/platform/wince/CEActionsSmartphone.cpp b/backends/platform/wince/CEActionsSmartphone.cpp index 97d780d534..6877c343e3 100644 --- a/backends/platform/wince/CEActionsSmartphone.cpp +++ b/backends/platform/wince/CEActionsSmartphone.cpp @@ -111,7 +111,7 @@ void CEActionsSmartphone::initInstanceGame() { bool is_comi = (strncmp(gameid.c_str(), "comi", 4) == 0); bool is_gob = (strncmp(gameid.c_str(), "gob", 3) == 0); bool is_saga = (gameid == "saga"); - bool is_kyra = (gameid == "kyra1"); + bool is_kyra = (strncmp(gameid.c_str(), "kyra",4) == 0); bool is_samnmax = (gameid == "samnmax"); bool is_cine = (gameid == "cine"); bool is_touche = (gameid == "touche"); diff --git a/backends/platform/wince/missing/missing.cpp b/backends/platform/wince/missing/missing.cpp index c760b1f7df..f03f00bb9a 100644 --- a/backends/platform/wince/missing/missing.cpp +++ b/backends/platform/wince/missing/missing.cpp @@ -171,7 +171,7 @@ int _access(const char *path, int mode) { MultiByteToWideChar(CP_ACP, 0, path, -1, fname, sizeof(fname)/sizeof(TCHAR)); WIN32_FIND_DATA ffd; - HANDLE h=FindFirstFile(fname, &ffd); + HANDLE h = FindFirstFile(fname, &ffd); FindClose(h); if (h == INVALID_HANDLE_VALUE) @@ -179,9 +179,14 @@ int _access(const char *path, int mode) { if (ffd.dwFileAttributes&FILE_ATTRIBUTE_DIRECTORY) { // WORKAROUND: WinCE (or the emulator) sometimes returns bogus direcotry - // hits for files that don't exist. Checking for the same fname twice + // hits for files that don't exist. TRIPLE checking for the same fname // seems to weed out those false positives. - HANDLE h=FindFirstFile(fname, &ffd); + // Exhibited in kyra engine. + HANDLE h = FindFirstFile(fname, &ffd); + FindClose(h); + if (h == INVALID_HANDLE_VALUE) + return -1; //Can't find file + h = FindFirstFile(fname, &ffd); FindClose(h); if (h == INVALID_HANDLE_VALUE) return -1; //Can't find file diff --git a/base/commandLine.cpp b/base/commandLine.cpp index 84f2013ae9..f8ca8a90cd 100644 --- a/base/commandLine.cpp +++ b/base/commandLine.cpp @@ -193,9 +193,6 @@ void registerDefaults() { ConfMan.registerDefault("joystick_num", -1); ConfMan.registerDefault("confirm_exit", false); ConfMan.registerDefault("disable_sdl_parachute", false); -#ifdef USE_ALSA - ConfMan.registerDefault("alsa_port", "65:0"); -#endif ConfMan.registerDefault("record_mode", "none"); ConfMan.registerDefault("record_file_name", "record.bin"); diff --git a/common/unarj.cpp b/common/unarj.cpp index da88c11fc9..9a7766a41f 100644 --- a/common/unarj.cpp +++ b/common/unarj.cpp @@ -75,7 +75,7 @@ static uint32 GetCRC(byte *data, int len) { return CRC ^ 0xFFFFFFFF; } -ArjFile::ArjFile() { +ArjFile::ArjFile() : _uncompressedData(NULL) { InitCRC(); _isOpen = false; _fallBack = false; @@ -256,6 +256,11 @@ bool ArjFile::open(const Common::String &filename) { _compsize = hdr->compSize; _origsize = hdr->origSize; + // FIXME: This hotfix prevents Drascula from leaking memory. + // As far as sanity checks go this is not bad, but the engine should be fixed. + if (_uncompressedData) + free(_uncompressedData); + _uncompressedData = (byte *)malloc(_origsize); _outstream = new MemoryWriteStream(_uncompressedData, _origsize); diff --git a/engines/agos/animation.cpp b/engines/agos/animation.cpp index 1b6626fa89..f29baefde1 100644 --- a/engines/agos/animation.cpp +++ b/engines/agos/animation.cpp @@ -150,7 +150,7 @@ void MoviePlayer::play() { startSound(); - while (_frameNum < _framesCount) + while (_frameNum < _framesCount && !_vm->_quit) handleNextFrame(); closeFile(); @@ -166,7 +166,7 @@ void MoviePlayer::play() { _vm->_system->setPalette(palette, 0, 256); } - _vm->fillBackGroundFromBack(); + _vm->fillBackGroundFromBack(); _vm->_fastFadeOutFlag = true; } diff --git a/engines/agos/input.cpp b/engines/agos/input.cpp index 3ee81e1375..ca6b67fa61 100644 --- a/engines/agos/input.cpp +++ b/engines/agos/input.cpp @@ -123,7 +123,7 @@ void AGOSEngine::setup_cond_c_helper() { clearName(); _lastNameOn = last; - for (;;) { + while (!_quit) { _lastHitArea = NULL; _lastHitArea3 = 0; _leftButtonDown = 0; @@ -145,7 +145,7 @@ void AGOSEngine::setup_cond_c_helper() { } delay(100); - } while (_lastHitArea3 == (HitArea *) -1 || _lastHitArea3 == 0); + } while ((_lastHitArea3 == (HitArea *) -1 || _lastHitArea3 == 0) && !_quit); if (_lastHitArea == NULL) { } else if (_lastHitArea->id == 0x7FFB) { diff --git a/engines/agos/oracle.cpp b/engines/agos/oracle.cpp index a113c8e2ea..2d2feb7b9e 100644 --- a/engines/agos/oracle.cpp +++ b/engines/agos/oracle.cpp @@ -459,7 +459,7 @@ void AGOSEngine_Feeble::saveUserGame(int slot) { } windowPutChar(window, 0x7f); - for (;;) { + while (!_quit) { _keyPressed.reset(); delay(1); diff --git a/engines/agos/saveload.cpp b/engines/agos/saveload.cpp index 4a5c43e706..9779630d47 100644 --- a/engines/agos/saveload.cpp +++ b/engines/agos/saveload.cpp @@ -279,11 +279,11 @@ restart: name = buf; _saveGameNameLen = 0; - for (;;) { + while (!_quit) { windowPutChar(window, 128); _keyPressed.reset(); - for (;;) { + while (!_quit) { delay(10); if (_keyPressed.ascii && _keyPressed.ascii < 128) { i = _keyPressed.ascii; @@ -443,7 +443,7 @@ void AGOSEngine_Elvira2::userGame(bool load) { name = buf + 192; - for (;;) { + while (!_quit) { windowPutChar(window, 128); _saveLoadEdit = true; @@ -516,7 +516,7 @@ int AGOSEngine_Elvira2::userGameGetKey(bool *b, char *buf, uint maxChar) { _keyPressed.reset(); - for (;;) { + while (!_quit) { _lastHitArea = NULL; _lastHitArea3 = NULL; @@ -526,7 +526,7 @@ int AGOSEngine_Elvira2::userGameGetKey(bool *b, char *buf, uint maxChar) { return _keyPressed.ascii; } delay(10); - } while (_lastHitArea3 == 0); + } while (_lastHitArea3 == 0 && !_quit); ha = _lastHitArea; if (ha == NULL || ha->id < 200) { @@ -543,6 +543,8 @@ int AGOSEngine_Elvira2::userGameGetKey(bool *b, char *buf, uint maxChar) { return ha->id - 200; } } + + return 225; } void AGOSEngine_Simon1::listSaveGames(char *dst) { @@ -706,7 +708,7 @@ restart:; _saveGameNameLen++; } - for (;;) { + while (!_quit) { windowPutChar(window, 127); _saveLoadEdit = true; @@ -785,7 +787,7 @@ int AGOSEngine_Simon1::userGameGetKey(bool *b, char *buf, uint maxChar) { _keyPressed.reset(); - for (;;) { + while (!_quit) { _lastHitArea = NULL; _lastHitArea3 = NULL; @@ -795,7 +797,7 @@ int AGOSEngine_Simon1::userGameGetKey(bool *b, char *buf, uint maxChar) { return _keyPressed.ascii; } delay(10); - } while (_lastHitArea3 == 0); + } while (_lastHitArea3 == 0 && !_quit); ha = _lastHitArea; if (ha == NULL || ha->id < 205) { @@ -824,6 +826,8 @@ int AGOSEngine_Simon1::userGameGetKey(bool *b, char *buf, uint maxChar) { return ha->id - 208; } } + + return 205; } void AGOSEngine::disableFileBoxes() { diff --git a/engines/agos/script.cpp b/engines/agos/script.cpp index a0151d7713..2a2ddeeb86 100644 --- a/engines/agos/script.cpp +++ b/engines/agos/script.cpp @@ -1012,7 +1012,7 @@ int AGOSEngine::runScript() { executeOpcode(_opcode); } while (getScriptCondition() != flag && !getScriptReturn() && !quit()); - return getScriptReturn(); + return (_quit) ? 1 : getScriptReturn(); } Child *nextSub(Child *sub, int16 key) { diff --git a/engines/agos/script_e1.cpp b/engines/agos/script_e1.cpp index 9ac308b114..d3ee4297c0 100644 --- a/engines/agos/script_e1.cpp +++ b/engines/agos/script_e1.cpp @@ -1052,11 +1052,11 @@ uint AGOSEngine::confirmYesOrNo(uint16 x, uint16 y) { ha->priority = 999; ha->window = 0; - for (;;) { + while (!_quit) { _lastHitArea = NULL; _lastHitArea3 = NULL; - for (;;) { + while (!_quit) { if (_lastHitArea3 != 0) break; delay(1); @@ -1101,11 +1101,11 @@ uint AGOSEngine::continueOrQuit() { ha->priority = 999; ha->window = 0; - for (;;) { + while (!_quit) { _lastHitArea = NULL; _lastHitArea3 = NULL; - for (;;) { + while (!_quit) { if (_lastHitArea3 != 0) break; delay(1); diff --git a/engines/agos/script_e2.cpp b/engines/agos/script_e2.cpp index da9afc5a7d..6f6db8efb4 100644 --- a/engines/agos/script_e2.cpp +++ b/engines/agos/script_e2.cpp @@ -370,11 +370,11 @@ void AGOSEngine_Elvira2::oe2_pauseGame() { uint32 pauseTime = getTime(); haltAnimation(); - for (;;) { + while (!_quit) { _lastHitArea = NULL; _lastHitArea3 = NULL; - for (;;) { + while (!_quit) { if (processSpecialKeys() != 0 || _lastHitArea3 != 0) break; delay(1); diff --git a/engines/agos/script_s1.cpp b/engines/agos/script_s1.cpp index 8bd9159ad8..86088a4385 100644 --- a/engines/agos/script_s1.cpp +++ b/engines/agos/script_s1.cpp @@ -338,18 +338,8 @@ void AGOSEngine_Simon1::os1_pauseGame() { break; } - for (;;) { + while (!_quit) { delay(1); -#ifdef _WIN32_WCE - if (isSmartphone()) { - if (_keyPressed.keycode) { - if (_keyPressed.keycode == Common::KEYCODE_RETURN) - quitGame(); - else - break; - } - } -#endif if (_keyPressed.keycode == keyYes) quitGame(); else if (_keyPressed.keycode == keyNo) diff --git a/engines/agos/script_ww.cpp b/engines/agos/script_ww.cpp index 5fd83312c3..8dc915f6e8 100644 --- a/engines/agos/script_ww.cpp +++ b/engines/agos/script_ww.cpp @@ -368,11 +368,11 @@ void AGOSEngine_Waxworks::oww_pauseGame() { uint32 pauseTime = getTime(); haltAnimation(); - for (;;) { + while (!_quit) { _lastHitArea = NULL; _lastHitArea3 = NULL; - for (;;) { + while (!_quit) { if (_lastHitArea3 != 0) break; delay(1); diff --git a/engines/agos/verb.cpp b/engines/agos/verb.cpp index 963bd6bd86..c8f6d40f1f 100644 --- a/engines/agos/verb.cpp +++ b/engines/agos/verb.cpp @@ -343,6 +343,9 @@ void AGOSEngine::handleVerbClicked(uint verb) { Subroutine *sub; int result; + if (_quit) + return; + _objectItem = _hitAreaObjectItem; if (_objectItem == _dummyItem2) { _objectItem = me(); diff --git a/engines/agos/window.cpp b/engines/agos/window.cpp index e25bd6b438..e1f986b92e 100644 --- a/engines/agos/window.cpp +++ b/engines/agos/window.cpp @@ -298,7 +298,7 @@ void AGOSEngine::waitWindow(WindowBlock *window) { ha->id = 0x7FFF; ha->priority = 999; - for (;;) { + while (!_quit) { _lastHitArea = NULL; _lastHitArea3 = NULL; diff --git a/engines/cine/anim.cpp b/engines/cine/anim.cpp index eb820804a6..c19435c59a 100644 --- a/engines/cine/anim.cpp +++ b/engines/cine/anim.cpp @@ -535,10 +535,14 @@ int loadSpl(const char *resourceName, int16 idx) { /*! \brief Load 1bpp mask * \param resourceName Mask filename * \param idx Target index in animDataTable (-1 if any empty space will do) - * \return The number of the animDataTable entry after the loaded mask + * \return The number of the animDataTable entry after the loaded mask (-1 if error) */ int loadMsk(const char *resourceName, int16 idx) { int16 foundFileIdx = findFileInBundle(resourceName); + if (foundFileIdx < 0) { + return -1; + } + int entry = 0; byte *dataPtr = readBundleFile(foundFileIdx); byte *ptr; @@ -562,10 +566,14 @@ int loadMsk(const char *resourceName, int16 idx) { /*! \brief Load animation * \param resourceName Animation filename * \param idx Target index in animDataTable (-1 if any empty space will do) - * \return The number of the animDataTable entry after the loaded animation + * \return The number of the animDataTable entry after the loaded animation (-1 if error) */ int loadAni(const char *resourceName, int16 idx) { int16 foundFileIdx = findFileInBundle(resourceName); + if (foundFileIdx < 0) { + return -1; + } + int entry = 0; byte *dataPtr = readBundleFile(foundFileIdx); byte *ptr; @@ -651,7 +659,7 @@ void convert8BBP2(byte *dest, byte *source, int16 width, int16 height) { /*! \brief Load image set * \param resourceName Image set filename * \param idx Target index in animDataTable (-1 if any empty space will do) - * \return The number of the animDataTable entry after the loaded image set + * \return The number of the animDataTable entry after the loaded image set (-1 if error) */ int loadSet(const char *resourceName, int16 idx) { AnimHeader2Struct header2; @@ -661,6 +669,10 @@ int loadSet(const char *resourceName, int16 idx) { byte *ptr, *startOfDataPtr, *dataPtr, *origDataPtr; int type; + if (foundFileIdx < 0) { + return -1; + } + origDataPtr = dataPtr = readBundleFile(foundFileIdx); assert(!memcmp(dataPtr, "SET", 3)); ptr = dataPtr + 4; @@ -708,10 +720,14 @@ int loadSet(const char *resourceName, int16 idx) { /*! \brief Load SEQ data into animDataTable * \param resourceName SEQ data filename * \param idx Target index in animDataTable (-1 if any empty space will do) - * \return The number of the animDataTable entry after the loaded SEQ data + * \return The number of the animDataTable entry after the loaded SEQ data (-1 if error) */ int loadSeq(const char *resourceName, int16 idx) { int16 foundFileIdx = findFileInBundle(resourceName); + if (foundFileIdx < 0) { + return -1; + } + byte *dataPtr = readBundleFile(foundFileIdx); int entry = idx < 0 ? emptyAnimSpace() : idx; diff --git a/engines/cine/bg.cpp b/engines/cine/bg.cpp index 2a4e7f0ab1..45bfae7925 100644 --- a/engines/cine/bg.cpp +++ b/engines/cine/bg.cpp @@ -56,7 +56,7 @@ byte loadCtFW(const char *ctName) { header[i] = readS.readUint16BE(); } - gfxConvertSpriteToRaw(page3Raw, ptr + 0x80, 160, 200); + gfxConvertSpriteToRaw(collisionPage, ptr + 0x80, 160, 200); free(dataPtr); return 0; @@ -74,10 +74,10 @@ byte loadCtOS(const char *ctName) { ptr += 2; if (bpp == 8) { - memcpy(page3Raw, ptr + 256 * 3, 320 * 200); + memcpy(collisionPage, ptr + 256 * 3, 320 * 200); renderer->loadCt256(ptr, ctName); } else { - gfxConvertSpriteToRaw(page3Raw, ptr + 32, 160, 200); + gfxConvertSpriteToRaw(collisionPage, ptr + 32, 160, 200); renderer->loadCt16(ptr, ctName); } diff --git a/engines/cine/cine.cpp b/engines/cine/cine.cpp index 50aefe5ff9..127d390f66 100644 --- a/engines/cine/cine.cpp +++ b/engines/cine/cine.cpp @@ -56,6 +56,10 @@ CineEngine::CineEngine(OSystem *syst, const CINEGameDescription *gameDesc) : Eng // Setup mixer _mixer->setVolumeForSoundType(Audio::Mixer::kSFXSoundType, ConfMan.getInt("sfx_volume")); _mixer->setVolumeForSoundType(Audio::Mixer::kMusicSoundType, ConfMan.getInt("music_volume")); + // Use music volume for plain sound types (At least the Adlib player uses a plain sound type + // so previously the music and sfx volume controls didn't affect it at all). + // FIXME: Make Adlib player differentiate between playing sound effects and music and remove this. + _mixer->setVolumeForSoundType(Audio::Mixer::kPlainSoundType, ConfMan.getInt("music_volume")); g_cine = this; @@ -99,14 +103,28 @@ int CineEngine::go() { mainLoop(1); delete renderer; - delete[] page3Raw; + delete[] collisionPage; delete g_sound; return _eventMan->shouldRTL(); } +int CineEngine::getTimerDelay() const { + return (10923000 * _timerDelayMultiplier) / 1193180; +} + +/*! \brief Modify game speed + * \param speedChange Negative values slow game down, positive values speed it up, zero does nothing + * \return Timer delay multiplier's value after the game speed change + */ +int CineEngine::modifyGameSpeed(int speedChange) { + // If we want more speed we decrement the timer delay multiplier and vice versa. + _timerDelayMultiplier = CLIP(_timerDelayMultiplier - speedChange, 1, 50); + return _timerDelayMultiplier; +} void CineEngine::initialize() { + _timerDelayMultiplier = 12; // Set default speed setupOpcodes(); initLanguage(g_cine->getLanguage()); @@ -117,7 +135,7 @@ void CineEngine::initialize() { renderer = new FWRenderer; } - page3Raw = new byte[320 * 200]; + collisionPage = new byte[320 * 200]; textDataPtr = (byte *)malloc(8000); partBuffer = (PartBuffer *)malloc(NUM_MAX_PARTDATA * sizeof(PartBuffer)); diff --git a/engines/cine/cine.h b/engines/cine/cine.h index eaae555812..6011036eb1 100644 --- a/engines/cine/cine.h +++ b/engines/cine/cine.h @@ -59,7 +59,8 @@ enum CineGameType { enum CineGameFeatures { GF_CD = 1 << 0, GF_DEMO = 1 << 1, - GF_ALT_FONT = 1 << 2 + GF_ALT_FONT = 1 << 2, + GF_CRYPTED_BOOT_PRC = 1 << 3 }; struct CINEGameDescription; @@ -86,6 +87,8 @@ public: bool loadSaveDirectory(void); void makeSystemMenu(void); + int modifyGameSpeed(int speedChange); + int getTimerDelay() const; const CINEGameDescription *_gameDescription; Common::File _partFileHandle; @@ -109,6 +112,7 @@ private: void readVolCnf(); bool _preLoad; + int _timerDelayMultiplier; }; extern CineEngine *g_cine; diff --git a/engines/cine/detection.cpp b/engines/cine/detection.cpp index 899e4c4754..1dc6f89c49 100644 --- a/engines/cine/detection.cpp +++ b/engines/cine/detection.cpp @@ -76,6 +76,25 @@ static const CINEGameDescription gameDescriptions[] = { 0, }, + // This is a CD version of Future Wars published by Sony. + // This version has a crypted AUTO00.PRC. + { + { + "fw", + "Sony CD version", + { + { "AUTO00.PRC", 0, "4fe1e7930b38e3c63f0f2474d471bf8f", -1}, + { "PART01", 0, "61d003202d301c29dd399acfb1354310", -1}, + { NULL, 0, NULL, 0} + }, + Common::EN_USA, + Common::kPlatformPC, + Common::ADGF_CD + }, + GType_FW, + GF_CD | GF_CRYPTED_BOOT_PRC, + }, + { // This is the version included in the UK "Classic Collection" { @@ -251,6 +270,21 @@ static const CINEGameDescription gameDescriptions[] = { }, { + // This is a 16 color PC version (It came on three 720kB 3.5" disks). + // The protagonist is named John Glames in this version. + { + "os", + "", + AD_ENTRY1("procs1", "9629129b86979fa592c1787385bf3695"), + Common::EN_GRB, + Common::kPlatformPC, + Common::ADGF_NO_FLAGS + }, + GType_OS, + 0, + }, + + { { "os", "", diff --git a/engines/cine/gfx.cpp b/engines/cine/gfx.cpp index cbddf0fc59..b882c9760e 100644 --- a/engines/cine/gfx.cpp +++ b/engines/cine/gfx.cpp @@ -36,7 +36,7 @@ namespace Cine { -byte *page3Raw; +byte *collisionPage; FWRenderer *renderer = NULL; static const byte mouseCursorNormal[] = { @@ -282,20 +282,43 @@ void FWRenderer::drawMessage(const char *str, int x, int y, int width, byte colo */ void FWRenderer::drawPlainBox(int x, int y, int width, int height, byte color) { int i; - byte *dest = _backBuffer + y * 320 + x; + // Handle horizontally flipped boxes if (width < 0) { x += width; - width = -width; + width = ABS(width); } + // Handle vertically flipped boxes if (height < 0) { y += height; - height = -height; + height = ABS(height); } - for (i = 0; i < height; i++) { - memset(dest + i * 320, color, width); + // Handle horizontal boundaries + if (x < 0) { + width += x; // Remove invisible columns + x = 0; // Start drawing at the screen's left border + } else if (x > 319) { + // Nothing left to draw as we're over the screen's right border + width = 0; + } + + // Handle vertical boundaries + if (y < 0) { + height += y; // Remove invisible rows + y = 0; // Start drawing at the screen's top border + } else if (y > 199) { + // Nothing left to draw as we're below the screen's bottom border + height = 0; + } + + // Draw the box if it's not empty + if (width > 0 && height > 0) { + byte *dest = _backBuffer + y * 320 + x; + for (i = 0; i < height; i++) { + memset(dest + i * 320, color, width); + } } } @@ -422,6 +445,7 @@ void FWRenderer::renderOverlay(const Common::List<overlay>::iterator &it) { _messageLen += messageTable[it->objIdx].size(); drawMessage(messageTable[it->objIdx].c_str(), it->x, it->y, it->width, it->color); + waitForPlayerClick = 1; break; // action failure message @@ -433,6 +457,7 @@ void FWRenderer::renderOverlay(const Common::List<overlay>::iterator &it) { width = width > 300 ? 300 : width; drawMessage(failureMessages[idx], (320 - width) / 2, 80, width, 4); + waitForPlayerClick = 1; break; // bitmap @@ -617,6 +642,11 @@ void FWRenderer::saveBgNames(Common::OutSaveFile &fHandle) { fHandle.write(_bgName, 13); } +const char *FWRenderer::getBgName(uint idx) const { + assert(idx == 0); + return _bgName; +} + /*! \brief Restore active and backup palette from save * \param fHandle Savefile open for reading */ @@ -1011,8 +1041,12 @@ void OSRenderer::drawBackground() { assert(scroll); - memcpy(_backBuffer, main + mainShift, mainSize); - memcpy(_backBuffer + mainSize, scroll, mainShift); + if (mainSize > 0) { // Just a precaution + memcpy(_backBuffer, main + mainShift, mainSize); + } + if (mainShift > 0) { // Just a precaution + memcpy(_backBuffer + mainSize, scroll, mainShift); + } } } @@ -1041,6 +1075,19 @@ void OSRenderer::renderOverlay(const Common::List<overlay>::iterator &it) { delete[] mask; break; + // game message + case 2: + if (it->objIdx >= messageTable.size()) { + return; + } + + _messageLen += messageTable[it->objIdx].size(); + drawMessage(messageTable[it->objIdx].c_str(), it->x, it->y, it->width, it->color); + if (it->color >= 0) { // This test isn't in Future Wars's implementation + waitForPlayerClick = 1; + } + break; + // bitmap case 4: if (objectTable[it->objIdx].frame >= 0) { @@ -1051,16 +1098,27 @@ void OSRenderer::renderOverlay(const Common::List<overlay>::iterator &it) { // masked background case 20: assert(it->objIdx < NUM_MAX_OBJECT); + var5 = it->x; // A global variable updated here! obj = objectTable + it->objIdx; sprite = animDataTable + obj->frame; - if (obj->frame < 0 || it->x > 8 || !_bgTable[it->x].bg || sprite->_bpp != 1) { + if (obj->frame < 0 || it->x < 0 || it->x > 8 || !_bgTable[it->x].bg || sprite->_bpp != 1) { break; } maskBgOverlay(_bgTable[it->x].bg, sprite->data(), sprite->_realWidth, sprite->_height, _backBuffer, obj->x, obj->y); break; + // TODO: Figure out what this overlay type is and name it + // TODO: Check it this implementation really works correctly (Some things might be wrong, needs testing) + case 22: { + assert(it->objIdx < NUM_MAX_OBJECT); + obj = objectTable + it->objIdx; + byte transCol = obj->part & 0x0F; + drawPlainBox(obj->x, obj->y, obj->frame, obj->costume, transCol); + break; + } + // something else default: FWRenderer::renderOverlay(it); @@ -1332,6 +1390,11 @@ void OSRenderer::saveBgNames(Common::OutSaveFile &fHandle) { } } +const char *OSRenderer::getBgName(uint idx) const { + assert(idx < 9); + return _bgTable[idx].name; +} + /*! \brief Fade to black * \bug Operation Stealth sometimes seems to fade to black using * transformPalette resulting in double fadeout diff --git a/engines/cine/gfx.h b/engines/cine/gfx.h index 6a3aa1ef89..078954e3b9 100644 --- a/engines/cine/gfx.h +++ b/engines/cine/gfx.h @@ -111,6 +111,7 @@ public: virtual uint getScroll() const; virtual void removeBg(unsigned int idx); virtual void saveBgNames(Common::OutSaveFile &fHandle); + virtual const char *getBgName(uint idx = 0) const; virtual void refreshPalette(); virtual void reloadPalette(); @@ -168,6 +169,7 @@ public: uint getScroll() const; void removeBg(unsigned int idx); void saveBgNames(Common::OutSaveFile &fHandle); + const char *getBgName(uint idx = 0) const; void refreshPalette(); void reloadPalette(); @@ -181,7 +183,7 @@ public: void gfxDrawSprite(byte *src4, uint16 sw, uint16 sh, byte *dst4, int16 sx, int16 sy); -extern byte *page3Raw; +extern byte *collisionPage; extern FWRenderer *renderer; void setMouseCursor(int cursor); diff --git a/engines/cine/main_loop.cpp b/engines/cine/main_loop.cpp index deac4fd57f..9361eb3822 100644 --- a/engines/cine/main_loop.cpp +++ b/engines/cine/main_loop.cpp @@ -121,11 +121,68 @@ static void processEvent(Common::Event &event) { g_cine->makeSystemMenu(); } break; + case Common::KEYCODE_MINUS: + case Common::KEYCODE_KP_MINUS: + g_cine->modifyGameSpeed(-1); // Slower + break; + case Common::KEYCODE_PLUS: + case Common::KEYCODE_KP_PLUS: + g_cine->modifyGameSpeed(+1); // Faster + break; + case Common::KEYCODE_LEFT: + case Common::KEYCODE_KP4: + moveUsingKeyboard(-1, 0); // Left + break; + case Common::KEYCODE_RIGHT: + case Common::KEYCODE_KP6: + moveUsingKeyboard(+1, 0); // Right + break; + case Common::KEYCODE_UP: + case Common::KEYCODE_KP8: + moveUsingKeyboard(0, +1); // Up + break; + case Common::KEYCODE_DOWN: + case Common::KEYCODE_KP2: + moveUsingKeyboard(0, -1); // Down + break; + case Common::KEYCODE_KP9: + moveUsingKeyboard(+1, +1); // Up & Right + break; + case Common::KEYCODE_KP7: + moveUsingKeyboard(-1, +1); // Up & Left + break; + case Common::KEYCODE_KP1: + moveUsingKeyboard(-1, -1); // Down & Left + break; + case Common::KEYCODE_KP3: + moveUsingKeyboard(+1, -1); // Down & Right + break; default: lastKeyStroke = event.kbd.keycode; break; } break; + case Common::EVENT_KEYUP: + switch (event.kbd.keycode) { + case Common::KEYCODE_KP5: // Emulated left mouse button click + case Common::KEYCODE_LEFT: // Left + case Common::KEYCODE_KP4: // Left + case Common::KEYCODE_RIGHT: // Right + case Common::KEYCODE_KP6: // Right + case Common::KEYCODE_UP: // Up + case Common::KEYCODE_KP8: // Up + case Common::KEYCODE_DOWN: // Down + case Common::KEYCODE_KP2: // Down + case Common::KEYCODE_KP9: // Up & Right + case Common::KEYCODE_KP7: // Up & Left + case Common::KEYCODE_KP1: // Down & Left + case Common::KEYCODE_KP3: // Down & Right + // Stop ego movement made with keyboard when releasing a known key + moveUsingKeyboard(0, 0); + break; + default: + break; + } default: break; } @@ -134,7 +191,7 @@ static void processEvent(Common::Event &event) { void manageEvents() { Common::EventManager *eventMan = g_system->getEventManager(); - uint32 nextFrame = g_system->getMillis() + kGameTimerDelay * kGameSpeed; + uint32 nextFrame = g_system->getMillis() + g_cine->getTimerDelay(); do { Common::Event event; while (eventMan->pollEvent(event)) { @@ -239,6 +296,38 @@ void CineEngine::mainLoop(int bootScriptIdx) { } do { + // HACK: Force amount of oxygen left to maximum during Operation Stealth's first arcade sequence. + // This makes it possible to pass the arcade sequence for now. + // FIXME: Remove the hack and make the first arcade sequence normally playable. + if (g_cine->getGameType() == Cine::GType_OS) { + Common::String bgName(renderer->getBgName()); + // Check if the background is one of the three backgrounds + // that are only used during the first arcade sequence. + if (bgName == "28.PI1" || bgName == "29.PI1" || bgName == "30.PI1") { + static const uint oxygenObjNum = 202, maxOxygen = 264; + // Force the amount of oxygen left to the maximum. + objectTable[oxygenObjNum].x = maxOxygen; + } + } + + // HACK: In Operation Stealth after the first arcade sequence jump player's position to avoid getting stuck. + // After the first arcade sequence the player comes up stairs from + // the water in Santa Paragua's downtown in front of the flower shop. + // Previously he was completely stuck after getting up the stairs. + // If the background is the one used in the flower shop scene ("21.PI1") + // and the player is at the exact location after getting up the stairs + // then we just nudge him a tiny bit away from the stairs and voila, he's free! + // Maybe the real problem behind all this is collision data related as it looks + // like there's some boundary right there near position (204, 110) which we can + // jump over by moving the character to (204, 109). The script handling the + // flower shop scene is AIRPORT.PRC's 13th script. + // FIXME: Remove the hack and solve what's really causing the problem in the first place. + if (g_cine->getGameType() == Cine::GType_OS) { + if (scumm_stricmp(renderer->getBgName(), "21.PI1") == 0 && objectTable[1].x == 204 && objectTable[1].y == 110) { + objectTable[1].y--; // Move the player character upward on-screen by one pixel + } + } + stopMusicAfterFadeOut(); di = executePlayerInput(); @@ -271,6 +360,11 @@ void CineEngine::mainLoop(int bootScriptIdx) { renderer->drawFrame(); } + // NOTE: In the original Future Wars and Operation Stealth messages + // were removed when running the drawOverlays function which is + // currently called from the renderer's drawFrame function. + removeMessages(); + if (waitForPlayerClick) { playerAction = false; @@ -300,8 +394,6 @@ void CineEngine::mainLoop(int bootScriptIdx) { } while (mouseButton != 0); waitForPlayerClick = 0; - - removeMessages(); } if (checkForPendingDataLoadSwitch) { diff --git a/engines/cine/main_loop.h b/engines/cine/main_loop.h index a2f828fd34..c729b324ca 100644 --- a/engines/cine/main_loop.h +++ b/engines/cine/main_loop.h @@ -28,11 +28,6 @@ namespace Cine { -enum { - kGameTimerDelay = 1000 / (1193180 / 10923), - kGameSpeed = 12 -}; - void mainLoop(int bootScriptIdx); void manageEvents(); diff --git a/engines/cine/part.cpp b/engines/cine/part.cpp index 88f2dcef52..d605bdd623 100644 --- a/engines/cine/part.cpp +++ b/engines/cine/part.cpp @@ -259,7 +259,13 @@ byte *readBundleSoundFile(const char *entryName, uint32 *size) { return data; } -byte *readFile(const char *filename) { +/*! \brief Rotate byte value to the left by n bits */ +byte rolByte(byte value, uint n) { + n %= 8; + return (byte) ((value << n) | (value >> (8 - n))); +} + +byte *readFile(const char *filename, bool crypted) { Common::File in; in.open(filename); @@ -272,6 +278,16 @@ byte *readFile(const char *filename) { byte *dataPtr = (byte *)malloc(size); in.read(dataPtr, size); + // The Sony published CD version of Future Wars has its + // AUTO00.PRC file's bytes rotated to the right by one. + // So we decode the so called crypting by rotating all + // the bytes to the left by one. + if (crypted) { + for (uint index = 0; index < size; index++) { + dataPtr[index] = rolByte(dataPtr[index], 1); + } + } + return dataPtr; } diff --git a/engines/cine/part.h b/engines/cine/part.h index 2a979e4879..72dc944db3 100644 --- a/engines/cine/part.h +++ b/engines/cine/part.h @@ -48,7 +48,7 @@ void readFromPart(int16 idx, byte *dataPtr); byte *readBundleFile(int16 foundFileIdx); byte *readBundleSoundFile(const char *entryName, uint32 *size = 0); -byte *readFile(const char *filename); +byte *readFile(const char *filename, bool crypted = false); void checkDataDisk(int16 param); diff --git a/engines/cine/prc.cpp b/engines/cine/prc.cpp index 657edf96af..797a354c4f 100644 --- a/engines/cine/prc.cpp +++ b/engines/cine/prc.cpp @@ -64,7 +64,7 @@ bool loadPrc(const char *pPrcName) { checkDataDisk(-1); if ((g_cine->getGameType() == Cine::GType_FW) && (!scumm_stricmp(pPrcName, BOOT_PRC_NAME) || !scumm_stricmp(pPrcName, "demo.prc"))) { - scriptPtr = dataPtr = readFile(pPrcName); + scriptPtr = dataPtr = readFile(pPrcName, (g_cine->getFeatures() & GF_CRYPTED_BOOT_PRC) != 0); } else { scriptPtr = dataPtr = readBundleFile(findFileInBundle(pPrcName)); } diff --git a/engines/cine/script_fw.cpp b/engines/cine/script_fw.cpp index e761a0c8e4..97f45488f2 100644 --- a/engines/cine/script_fw.cpp +++ b/engines/cine/script_fw.cpp @@ -1319,6 +1319,7 @@ int FWScript::o1_loadBg() { return 0; } +/*! \brief Load collision table data */ int FWScript::o1_loadCt() { const char *param = getNextString(); @@ -1789,7 +1790,14 @@ int16 checkCollision(int16 objIdx, int16 x, int16 y, int16 numZones, int16 zoneI int16 result = 0; for (int16 i = 0; i < numZones; i++) { - idx = getZoneFromPositionRaw(page3Raw, lx + i, ly, 320); + // Don't try to read data in Operation Stealth if position isn't in 320x200 screen bounds. + if (g_cine->getGameType() == Cine::GType_OS) { + if ((lx + i) < 0 || (lx + i) > 319 || ly < 0 || ly > 199) { + continue; + } + } + + idx = getZoneFromPositionRaw(collisionPage, lx + i, ly, 320); assert(idx >= 0 && idx < NUM_MAX_ZONE); diff --git a/engines/cine/script_os.cpp b/engines/cine/script_os.cpp index 0289a2a0bc..e8f16ebfcc 100644 --- a/engines/cine/script_os.cpp +++ b/engines/cine/script_os.cpp @@ -365,6 +365,7 @@ FWScript *OSScriptInfo::create(const RawObjectScript &script, int16 index, const // OPERATION STEALTH opcodes // ------------------------------------------------------------------------ +/*! \brief Load collision table data */ int FWScript::o2_loadCt() { const char *param = getNextString(); @@ -636,7 +637,28 @@ int FWScript::o2_loadAbs() { const char *param2 = getNextString(); debugC(5, kCineDebugScript, "Line: %d: loadABS(%d,%s)", _line, param1, param2); - loadResource(param2, param1); + // Load the resource to an absolute position + if (loadResource(param2, param1) == -1) { // Check if the loading failed + // WORKAROUND: In the 256 color PC version of Operation Stealth when + // walking out of the airport in Santa Paragua to the street the + // player character should be seen as a grey silhuette while walking + // behind the glass. But actually the player character is completely + // invisible when walking behind the glass because the animation files + // used are wrongly loaded. In AIRPORT.PRC's 6th script there are + // calls loadAbs("JOHN01.ANI", 73) and loadAbs("JOHN02.ANI", 37) to + // load the animations involved but no such files are found with the + // game. Corresponding SET-files are found though. As it worked and + // looked fine when I tried loading them instead of the missing ANI + // files I'm doing so here. NOTE: At least the German Amiga version + // of Operation Stealth seems to have all the files involved + // (JOHN01.ANI, JOHN02.ANI, JOHN01.SET and JOHN02.SET). + if (scumm_stricmp(param2, "JOHN01.ANI") == 0 && param1 == 73) { + loadResource("JOHN01.SET", param1); + } else if (scumm_stricmp(param2, "JOHN02.ANI") == 0 && param1 == 37) { + loadResource("JOHN02.SET", param1); + } + } + return 0; } diff --git a/engines/cine/various.cpp b/engines/cine/various.cpp index c490756403..0ead13e3ab 100644 --- a/engines/cine/various.cpp +++ b/engines/cine/various.cpp @@ -94,11 +94,23 @@ int16 saveVar2; byte isInPause = 0; -// TODO: Implement inputVar0's changes in the program -// Currently inputVar0 isn't updated anywhere even though it's used at least in processSeqListElement. -uint16 inputVar0 = 0; -byte inputVar1 = 0; -uint16 inputVar2 = 0, inputVar3 = 0; +/*! \brief Values used by the xMoveKeyb variable */ +enum xMoveKeybEnums { + kKeybMoveCenterX = 0, + kKeybMoveRight = 1, + kKeybMoveLeft = 2 +}; + +/*! \brief Values used by the yMoveKeyb variable */ +enum yMoveKeybEnums { + kKeybMoveCenterY = 0, + kKeybMoveDown = 1, + kKeybMoveUp = 2 +}; + +uint16 xMoveKeyb = kKeybMoveCenterX; +bool egoMovedWithKeyboard = false; +uint16 yMoveKeyb = kKeybMoveCenterY; SelectedObjStruct currentSelectedObject; @@ -115,6 +127,31 @@ int16 objListTab[20]; uint16 zoneData[NUM_MAX_ZONE]; uint16 zoneQuery[NUM_MAX_ZONE]; //!< Only exists in Operation Stealth +/*! \brief Move the player character using the keyboard + * \param x Negative values move left, positive right, zero not at all + * \param y Negative values move down, positive up, zero not at all + * NOTE: If both x and y are zero then the character stops + * FIXME: This seems to only work in Operation Stealth. May need code changes somewhere else... + */ +void moveUsingKeyboard(int x, int y) { + if (x > 0) { + xMoveKeyb = kKeybMoveRight; + } else if (x < 0) { + xMoveKeyb = kKeybMoveLeft; + } else { + xMoveKeyb = kKeybMoveCenterX; + } + + if (y > 0) { + yMoveKeyb = kKeybMoveUp; + } else if (y < 0) { + yMoveKeyb = kKeybMoveDown; + } else { + yMoveKeyb = kKeybMoveCenterY; + } + + egoMovedWithKeyboard = x || y; +} void stopMusicAfterFadeOut(void) { // if (g_sfxPlayer->_fadeOutCounter != 0 && g_sfxPlayer->_fadeOutCounter < 100) { @@ -139,7 +176,6 @@ void addPlayerCommandMessage(int16 cmd) { tmp.type = 3; overlayList.push_back(tmp); - waitForPlayerClick = 1; } int16 getRelEntryForObject(uint16 param1, uint16 param2, SelectedObjStruct *pSelectedObject) { @@ -1516,6 +1552,7 @@ void makeCommandLine(void) { } if (!disableSystemMenu) { + isDrawCommandEnabled = 1; renderer->setCommand(commandBuffer); } } @@ -1688,6 +1725,11 @@ uint16 executePlayerInput(void) { } if (allowPlayerInput) { + if (isDrawCommandEnabled) { + renderer->setCommand(commandBuffer); + isDrawCommandEnabled = 0; + } + getMouseData(mouseUpdateStatus, &mouseButton, &mouseX, &mouseY); while (mouseButton && currentEntry < 200) { @@ -1868,8 +1910,8 @@ uint16 executePlayerInput(void) { var_2 = 0; } - if (inputVar1 && allowPlayerInput) { // use keyboard - inputVar1 = 0; + if (egoMovedWithKeyboard && allowPlayerInput) { // use keyboard + egoMovedWithKeyboard = false; switch (globalVars[VAR_MOUSE_X_MODE]) { case 1: @@ -1901,8 +1943,8 @@ uint16 executePlayerInput(void) { globalVars[VAR_MOUSE_X_POS] = mouseX; globalVars[VAR_MOUSE_Y_POS] = mouseY; } else { - if (inputVar2) { - if (inputVar2 == 2) { + if (xMoveKeyb) { + if (xMoveKeyb == kKeybMoveLeft) { globalVars[VAR_MOUSE_X_POS] = 1; } else { globalVars[VAR_MOUSE_X_POS] = 320; @@ -1911,8 +1953,8 @@ uint16 executePlayerInput(void) { globalVars[VAR_MOUSE_X_POS] = mouseX; } - if (inputVar3) { - if (inputVar3 == 2) { + if (yMoveKeyb) { + if (yMoveKeyb == kKeybMoveUp) { globalVars[VAR_MOUSE_Y_POS] = 1; } else { globalVars[VAR_MOUSE_Y_POS] = 200; @@ -1994,9 +2036,22 @@ void drawSprite(Common::List<overlay>::iterator it, const byte *spritePtr, const void removeMessages() { Common::List<overlay>::iterator it; + bool remove; for (it = overlayList.begin(); it != overlayList.end(); ) { - if (it->type == 2 || it->type == 3) { + if (g_cine->getGameType() == Cine::GType_OS) { + // NOTE: These are really removeOverlay calls that have been deferred. + // In Operation Stealth's disassembly elements are removed from the + // overlay list right in the drawOverlays function (And actually in + // some other places too) and that's where incrementing a the overlay's + // last parameter by one if it's negative and testing it for positivity + // comes from too. + remove = it->type == 3 || (it->type == 2 && (it->color >= 0 || ++it->color >= 0)); + } else { // Future Wars + remove = it->type == 2 || it->type == 3; + } + + if (remove) { it = overlayList.erase(it); } else { ++it; @@ -2079,7 +2134,6 @@ void addMessage(byte param1, int16 param2, int16 param3, int16 param4, int16 par tmp.color = param5; overlayList.push_back(tmp); - waitForPlayerClick = 1; } Common::List<SeqListElement> seqList; @@ -2338,9 +2392,9 @@ void processSeqListElement(SeqListElement &element) { } computeMove1(element, ptr1[4] + x, ptr1[5] + y, param1, param2, x2, y2); } else { - if (inputVar0 && allowPlayerInput) { + if (xMoveKeyb && allowPlayerInput) { int16 adder = param1 + 1; - if (inputVar0 != 1) { + if (xMoveKeyb != kKeybMoveRight) { adder = -adder; } // FIXME: In Operation Stealth's disassembly global variable 251 is used here @@ -2349,9 +2403,9 @@ void processSeqListElement(SeqListElement &element) { globalVars[VAR_MOUSE_X_POS] = globalVars[251] = ptr1[4] + x + adder; } - if (inputVar1 && allowPlayerInput) { + if (yMoveKeyb && allowPlayerInput) { int16 adder = param2 + 1; - if (inputVar1 != 1) { + if (yMoveKeyb != kKeybMoveDown) { adder = -adder; } // TODO: Name currently unnamed global variable 252 diff --git a/engines/cine/various.h b/engines/cine/various.h index 5f24d502be..acab15e235 100644 --- a/engines/cine/various.h +++ b/engines/cine/various.h @@ -143,6 +143,7 @@ void processSeqList(void); void resetGfxEntityEntry(uint16 objIdx); bool makeTextEntryMenu(const char *caption, char *string, int strLen, int y); +void moveUsingKeyboard(int x, int y); } // End of namespace Cine diff --git a/engines/kyra/kyra_hof.cpp b/engines/kyra/kyra_hof.cpp index fd14e8c909..0c9b117866 100644 --- a/engines/kyra/kyra_hof.cpp +++ b/engines/kyra/kyra_hof.cpp @@ -295,10 +295,12 @@ int KyraEngine_HoF::go() { if (_menuChoice != 4) { // load just the pak files needed for ingame _res->loadPakFile(StaticResource::staticDataFilename()); - if (_flags.platform == Common::kPlatformPC && _flags.isTalkie) - _res->loadFileList("FILEDATA.FDT"); - else + if (_flags.platform == Common::kPlatformPC && _flags.isTalkie) { + if (!_res->loadFileList("FILEDATA.FDT")) + error("couldn't load 'FILEDATA.FDT'"); + } else { _res->loadFileList(_ingamePakList, _ingamePakListSize); + } if (_flags.platform == Common::kPlatformPC98) _res->loadPakFile("AUDIO.PAK"); diff --git a/engines/kyra/lol.cpp b/engines/kyra/lol.cpp index 148b7daf4d..2011f65b3e 100644 --- a/engines/kyra/lol.cpp +++ b/engines/kyra/lol.cpp @@ -87,6 +87,9 @@ int LoLEngine::init() { _screen->setAnimBlockPtr(10000); _screen->setScreenDim(0); + if (!_sound->init()) + error("Couldn't init sound"); + return 0; } diff --git a/engines/kyra/resource.cpp b/engines/kyra/resource.cpp index 5d3c5ff715..91150ad354 100644 --- a/engines/kyra/resource.cpp +++ b/engines/kyra/resource.cpp @@ -89,14 +89,17 @@ bool Resource::reset() { return true; } else if (_vm->game() == GI_KYRA3) { - if (_vm->gameFlags().useInstallerPackage) - loadPakFile("WESTWOOD.001"); + if (_vm->gameFlags().useInstallerPackage) { + if (!loadPakFile("WESTWOOD.001")) + error("couldn't load file: 'WESTWOOD.001'"); + } // Add default file directories Common::File::addDefaultDirectory(ConfMan.get("path") + "malcolm"); Common::File::addDefaultDirectory(ConfMan.get("path") + "MALCOLM"); - loadFileList("FILEDATA.FDT"); + if (!loadFileList("FILEDATA.FDT")) + error("couldn't load file: 'FILEDATA.FDT'"); return true; } else if (_vm->game() == GI_LOL) { diff --git a/engines/kyra/saveload.cpp b/engines/kyra/saveload.cpp index 0dc7cf2c02..cffd0c7800 100644 --- a/engines/kyra/saveload.cpp +++ b/engines/kyra/saveload.cpp @@ -37,7 +37,7 @@ namespace Kyra { -KyraEngine_v1::kReadSaveHeaderError KyraEngine_v1::readSaveHeader(Common::InSaveFile *in, SaveHeader &header) { +KyraEngine_v1::kReadSaveHeaderError KyraEngine_v1::readSaveHeader(Common::SeekableReadStream *in, SaveHeader &header) { uint32 type = in->readUint32BE(); header.originalSave = false; header.oldHeader = false; @@ -111,10 +111,10 @@ KyraEngine_v1::kReadSaveHeaderError KyraEngine_v1::readSaveHeader(Common::InSave return (in->ioFailed() ? kRSHEIoError : kRSHENoError); } -Common::InSaveFile *KyraEngine_v1::openSaveForReading(const char *filename, SaveHeader &header) { +Common::SeekableReadStream *KyraEngine_v1::openSaveForReading(const char *filename, SaveHeader &header) { debugC(9, kDebugLevelMain, "KyraEngine_v1::openSaveForReading('%s', -)", filename); - Common::InSaveFile *in = 0; + Common::SeekableReadStream *in = 0; if (!(in = _saveFileMan->openForLoading(filename))) return 0; @@ -162,12 +162,12 @@ Common::InSaveFile *KyraEngine_v1::openSaveForReading(const char *filename, Save return in; } -Common::OutSaveFile *KyraEngine_v1::openSaveForWriting(const char *filename, const char *saveName) const { +Common::WriteStream *KyraEngine_v1::openSaveForWriting(const char *filename, const char *saveName) const { debugC(9, kDebugLevelMain, "KyraEngine_v1::openSaveForWriting('%s', '%s')", filename, saveName); if (quit()) return 0; - Common::OutSaveFile *out = 0; + Common::WriteStream *out = 0; if (!(out = _saveFileMan->openForSaving(filename))) { warning("Can't create file '%s', game not saved", filename); return 0; @@ -212,7 +212,7 @@ bool KyraEngine_v1::saveFileLoadable(int slot) { return false; SaveHeader header; - Common::InSaveFile *in = openSaveForReading(getSavegameFilename(slot), header); + Common::SeekableReadStream *in = openSaveForReading(getSavegameFilename(slot), header); if (in) { delete in; diff --git a/engines/kyra/scene_mr.cpp b/engines/kyra/scene_mr.cpp index 716c139129..ad4ce63b6c 100644 --- a/engines/kyra/scene_mr.cpp +++ b/engines/kyra/scene_mr.cpp @@ -378,10 +378,11 @@ void KyraEngine_MR::loadSceneMsc() { _screen->loadBitmap(filename, 5, 5, 0, true); // HACK - uint8 data[320*200]; + uint8 *data = new uint8[320*200]; _screen->copyRegionToBuffer(5, 0, 0, 320, 200, data); _screen->clearPage(5); _screen->copyBlockToPage(5, 0, _maskPageMinY, 320, height, data); + delete[] data; musicUpdate(0); } diff --git a/engines/scumm/dialogs.cpp b/engines/scumm/dialogs.cpp index cb9113fe65..b590bba65e 100644 --- a/engines/scumm/dialogs.cpp +++ b/engines/scumm/dialogs.cpp @@ -364,37 +364,32 @@ void SaveLoadChooser::reflowLayout() { void SaveLoadChooser::updateInfos(bool redraw) { int selItem = _list->getSelected(); - Graphics::Surface *thumb; - thumb = _vm->loadThumbnailFromSlot(_saveMode ? selItem + 1 : selItem); + Graphics::Surface *thumb = 0; + if (selItem >= 0 && !_list->getSelectedString().empty()) + thumb = _vm->loadThumbnailFromSlot(_saveMode ? selItem + 1 : selItem); if (thumb) { _gfxWidget->setGfx(thumb); _gfxWidget->useAlpha(256); thumb->free(); + delete thumb; } else { _gfxWidget->setGfx(-1, -1, _fillR, _fillG, _fillB); } - delete thumb; - if (redraw) - _gfxWidget->draw(); - InfoStuff infos; memset(&infos, 0, sizeof(InfoStuff)); - char buffer[32]; - if (_vm->loadInfosFromSlot(_saveMode ? selItem + 1 : selItem, &infos)) { + if (selItem >= 0 && !_list->getSelectedString().empty() + && _vm->loadInfosFromSlot(_saveMode ? selItem + 1 : selItem, &infos)) { + char buffer[32]; snprintf(buffer, 32, "Date: %.2d.%.2d.%.4d", (infos.date >> 24) & 0xFF, (infos.date >> 16) & 0xFF, infos.date & 0xFFFF); _date->setLabel(buffer); - if (redraw) - _date->draw(); snprintf(buffer, 32, "Time: %.2d:%.2d", (infos.time >> 8) & 0xFF, infos.time & 0xFF); _time->setLabel(buffer); - if (redraw) - _time->draw(); int minutes = infos.playtime / 60; int hours = minutes / 60; @@ -403,23 +398,17 @@ void SaveLoadChooser::updateInfos(bool redraw) { snprintf(buffer, 32, "Playtime: %.2d:%.2d", hours & 0xFF, minutes & 0xFF); _playtime->setLabel(buffer); - if (redraw) - _playtime->draw(); } else { - snprintf(buffer, 32, "No date saved"); - _date->setLabel(buffer); - if (redraw) - _date->draw(); - - snprintf(buffer, 32, "No time saved"); - _time->setLabel(buffer); - if (redraw) - _time->draw(); + _date->setLabel("No date saved"); + _time->setLabel("No time saved"); + _playtime->setLabel("No playtime saved"); + } - snprintf(buffer, 32, "No playtime saved"); - _playtime->setLabel(buffer); - if (redraw) - _playtime->draw(); + if (redraw) { + _gfxWidget->draw(); + _date->draw(); + _time->draw(); + _playtime->draw(); } } diff --git a/engines/scumm/script.cpp b/engines/scumm/script.cpp index 9268768f2e..c727b59c64 100644 --- a/engines/scumm/script.cpp +++ b/engines/scumm/script.cpp @@ -769,14 +769,14 @@ void ScummEngine::runInventoryScript(int i) { args[0] = i; if (VAR(VAR_INVENTORY_SCRIPT)) { if (_game.id == GID_INDY3 && _game.platform == Common::kPlatformMacintosh) { - inventoryScript(); + inventoryScriptIndy3Mac(); } else { runScript(VAR(VAR_INVENTORY_SCRIPT), 0, 0, args); } } } -void ScummEngine::inventoryScript() { +void ScummEngine::inventoryScriptIndy3Mac() { VerbSlot *vs; int args[24]; int j, slot; @@ -1201,11 +1201,11 @@ void ScummEngine::runInputScript(int clickArea, int val, int mode) { if (clickArea == kVerbClickArea && (val >= 101 && val <= 108)) { if (val == 107) { VAR(67) -= 2; - inventoryScript(); + inventoryScriptIndy3Mac(); return; } else if (val == 108) { VAR(67) += 2; - inventoryScript(); + inventoryScriptIndy3Mac(); return; } else { args[0] = 3; diff --git a/engines/scumm/script_v6.cpp b/engines/scumm/script_v6.cpp index f8502cbbe7..245dca883a 100644 --- a/engines/scumm/script_v6.cpp +++ b/engines/scumm/script_v6.cpp @@ -2390,6 +2390,15 @@ void ScummEngine_v6::o6_talkActor() { _actorToPrintStrFor = pop(); + // WORKAROUND for bug #2016521: "DOTT: Bernard impersonating LaVerne" + // Original script did not check for VAR_EGO == 2 before executing + // a talkActor opcode. + if (_game.id == GID_TENTACLE && vm.slot[_currentScript].number == 307 + && VAR(VAR_EGO) != 2 && _actorToPrintStrFor == 2) { + _scriptPointer += resStrLen(_scriptPointer) + 1; + return; + } + _string[0].loadDefault(); actorTalk(_scriptPointer); diff --git a/engines/scumm/scumm.h b/engines/scumm/scumm.h index b839d37b08..054a1f59b9 100644 --- a/engines/scumm/scumm.h +++ b/engines/scumm/scumm.h @@ -669,7 +669,7 @@ protected: void executeScript(); void updateScriptPtr(); virtual void runInventoryScript(int i); - void inventoryScript(); + void inventoryScriptIndy3Mac(); void checkAndRunSentenceScript(); void runExitScript(); void runEntryScript(); diff --git a/engines/touche/graphics.cpp b/engines/touche/graphics.cpp index 999aa8005c..ab711beba0 100644 --- a/engines/touche/graphics.cpp +++ b/engines/touche/graphics.cpp @@ -76,10 +76,13 @@ int Graphics::getCharWidth16(uint8 chr) { return chrData[2]; } -void Graphics::drawString16(uint8 *dst, int dstPitch, uint16 color, int x, int y, const char *str) { +void Graphics::drawString16(uint8 *dst, int dstPitch, uint16 color, int x, int y, const char *str, int xmax) { while (*str) { uint8 chr = (uint8)*str++; x += drawChar16(dst, dstPitch, chr, x, y, color); + if (xmax != 0 && x > xmax) { + break; + } } } diff --git a/engines/touche/graphics.h b/engines/touche/graphics.h index 6b4072d896..9c928f983c 100644 --- a/engines/touche/graphics.h +++ b/engines/touche/graphics.h @@ -40,7 +40,7 @@ public: static void setupFont(Common::Language language); static int getStringWidth16(const char *str); static int getCharWidth16(uint8 chr); - static void drawString16(uint8 *dst, int dstPitch, uint16 color, int x, int y, const char *str); + static void drawString16(uint8 *dst, int dstPitch, uint16 color, int x, int y, const char *str, int xmax = 0); static int drawChar16(uint8 *dst, int dstPitch, uint8 chr, int x, int y, uint16 color); static void fillRect(uint8 *dst, int dstPitch, int x, int y, int w, int h, uint8 color); static void drawRect(uint8 *dst, int dstPitch, int x, int y, int w, int h, uint8 color1, uint8 color2); diff --git a/engines/touche/staticres.cpp b/engines/touche/staticres.cpp index ec9a0ea903..a377a6e45f 100644 --- a/engines/touche/staticres.cpp +++ b/engines/touche/staticres.cpp @@ -889,6 +889,7 @@ const uint8 Graphics::_freGerFontData[] = { 0xC0, 0x00, 0x00, }; +// spanish charset differs from original executable, see tracker item #2040311. const uint16 Graphics::_spaFontOffs[] = { 0x0000, 0x0007, 0x0024, 0x0043, 0x0072, 0x00AD, 0x00E0, 0x0113, 0x0124, 0x0141, 0x015E, 0x0191, 0x01C4, 0x01E3, 0x01F8, 0x0215, 0x0232, 0x0269, 0x0286, 0x02BD, @@ -904,11 +905,11 @@ const uint16 Graphics::_spaFontOffs[] = { 0x1462, 0x1499, 0x14A4, 0x14DB, 0x1512, 0x1549, 0x1580, 0x15B7, 0x15EE, 0x1625, 0x165C, 0x1667, 0x169E, 0x16D5, 0x16E0, 0x16EB, 0x1722, 0x172D, 0x1738, 0x176F, 0x178C, 0x17C3, 0x17FA, 0x1831, 0x1868, 0x1873, 0x187E, 0x18B5, 0x18C0, 0x18CB, - 0x18D6, 0x18E1, 0x18FE, 0x1929, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x18D6, 0x18E1, 0x18FE, 0x1929, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x054B, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0703, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x1954 }; diff --git a/engines/touche/touche.cpp b/engines/touche/touche.cpp index a7a362f2b1..d1d7528517 100644 --- a/engines/touche/touche.cpp +++ b/engines/touche/touche.cpp @@ -1256,10 +1256,11 @@ int ToucheEngine::getStringWidth(int num) const { return Graphics::getStringWidth16(str); } -void ToucheEngine::drawString(uint16 color, int x, int y, int16 num) { +void ToucheEngine::drawString(uint16 color, int x, int y, int16 num, StringType strType) { + const int xmax = (_language == Common::ES_ESP && strType == kStringTypeConversation) ? kScreenWidth - 20 : 0; if (num) { const char *str = getString(num); - Graphics::drawString16(_offscreenBuffer, kScreenWidth, color, x, y, str); + Graphics::drawString16(_offscreenBuffer, kScreenWidth, color, x, y, str, xmax); } } @@ -2422,7 +2423,7 @@ void ToucheEngine::drawCharacterConversation() { } drawConversationPanel(); for (int i = 0; i < 4; ++i) { - drawString(214, 42, 328 + i * kTextHeight, _conversationChoicesTable[_scrollConversationChoiceOffset + i].msg); + drawString(214, 42, 328 + i * kTextHeight, _conversationChoicesTable[_scrollConversationChoiceOffset + i].msg, kStringTypeConversation); } updateScreenArea(0, 320, kScreenWidth, kScreenHeight - 320); _conversationAreaCleared = false; @@ -2430,7 +2431,7 @@ void ToucheEngine::drawCharacterConversation() { void ToucheEngine::drawConversationString(int num, uint16 color) { const int y = 328 + num * kTextHeight; - drawString(color, 42, y, _conversationChoicesTable[num + _scrollConversationChoiceOffset].msg); + drawString(color, 42, y, _conversationChoicesTable[num + _scrollConversationChoiceOffset].msg, kStringTypeConversation); updateScreenArea(0, y, kScreenWidth, kTextHeight); } diff --git a/engines/touche/touche.h b/engines/touche/touche.h index 90ab9933bd..f341769422 100644 --- a/engines/touche/touche.h +++ b/engines/touche/touche.h @@ -333,6 +333,11 @@ enum { kCurrentGameStateVersion = 6 // for --list-saves support }; +enum StringType { + kStringTypeDefault, + kStringTypeConversation +}; + class MidiPlayer; class ToucheEngine: public Engine { @@ -402,7 +407,7 @@ protected: void setKeyCharMoney(); const char *getString(int num) const; int getStringWidth(int num) const; - void drawString(uint16 color, int x, int y, int16 num); + void drawString(uint16 color, int x, int y, int16 num, StringType strType = kStringTypeDefault); void drawGameString(uint16 color, int x1, int y, const char *str); int restartKeyCharScriptOnAction(int action, int obj1, int obj2); void buildSpriteScalingTable(int z1, int z2); diff --git a/graphics/scaler/hq2x_i386.asm b/graphics/scaler/hq2x_i386.asm index ed194441ed..f176c340c1 100644 --- a/graphics/scaler/hq2x_i386.asm +++ b/graphics/scaler/hq2x_i386.asm @@ -1840,3 +1840,8 @@ FuncTable2: dd ..@cross8, ..@flag0, ..@flag0, ..@flag0, dd ..@flag0, ..@flag0, ..@flag0, ..@flag0 + +%ifidn __OUTPUT_FORMAT__,elf +section .note.GNU-stack noalloc noexec nowrite progbits +%endif + diff --git a/graphics/scaler/hq3x_i386.asm b/graphics/scaler/hq3x_i386.asm index e713fdd51a..8ffb2d5aba 100644 --- a/graphics/scaler/hq3x_i386.asm +++ b/graphics/scaler/hq3x_i386.asm @@ -2432,3 +2432,8 @@ FuncTable2: dd ..@cross8, ..@flag0, ..@flag0, ..@flag0, dd ..@flag0, ..@flag0, ..@flag0, ..@flag0 + +%ifidn __OUTPUT_FORMAT__,elf +section .note.GNU-stack noalloc noexec nowrite progbits +%endif + |