diff options
author | Eugene Sandulenko | 2006-09-29 08:37:24 +0000 |
---|---|---|
committer | Eugene Sandulenko | 2006-09-29 08:37:24 +0000 |
commit | 798c7ee6822517601ef3c4096ad76a2fc0eefb50 (patch) | |
tree | 02b63f44348a35d6e3b6ab53540e94b2e45738e7 /engines/agos | |
parent | 8e2c703baba570b18aec9d871fdc8ee7efe49e57 (diff) | |
download | scummvm-rg350-798c7ee6822517601ef3c4096ad76a2fc0eefb50.tar.gz scummvm-rg350-798c7ee6822517601ef3c4096ad76a2fc0eefb50.tar.bz2 scummvm-rg350-798c7ee6822517601ef3c4096ad76a2fc0eefb50.zip |
Phase 2 of Simon renaming. Simon directory renaming
svn-id: r24009
Diffstat (limited to 'engines/agos')
32 files changed, 23743 insertions, 0 deletions
diff --git a/engines/agos/animation.cpp b/engines/agos/animation.cpp new file mode 100644 index 0000000000..9302055241 --- /dev/null +++ b/engines/agos/animation.cpp @@ -0,0 +1,406 @@ +/* ScummVM - Scumm Interpreter + * Copyright (C) 2001 Ludvig Strigeus + * Copyright (C) 2001-2006 The ScummVM project + * + * 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. + * + * $URL$ + * $Id$ + * + */ + +#include "common/stdafx.h" + +#include "common/endian.h" +#include "common/system.h" + +#include "graphics/cursorman.h" + +#include "agos/animation.h" +#include "agos/intern.h" +#include "agos/agos.h" + +#include "sound/audiostream.h" +#include "sound/wave.h" + +namespace Simon { + +MoviePlayer::MoviePlayer(SimonEngine *vm, Audio::Mixer *mixer) + : DXAPlayer(), _vm(vm), _mixer(mixer) { + _omniTV = false; + + _leftButtonDown = false; + _rightButtonDown = false; + + memset(baseName, 0, sizeof(baseName)); + + _sequenceNum = 0; + _ticks = 0; +} + +bool MoviePlayer::load(const char *filename) { + char videoName[20]; + uint i; + + int baseLen = strlen(filename) - 4; + memset(baseName, 0, sizeof(baseName)); + memcpy(baseName, filename, baseLen); + + // Change file extension to dxa + sprintf(videoName, "%s.dxa", baseName); + + if (!loadFile(videoName)) { + // Check short filename to work around + // bug in a German Windows 2CD version. + if (baseLen >= 8) { + char shortName[20]; + memset(shortName, 0, sizeof(shortName)); + memcpy(shortName, filename, 6); + + sprintf(shortName, "%s~1", shortName); + + if (!loadFile(shortName)) + error("Failed to load video file %s or %s", videoName, shortName); + + memset(baseName, 0, sizeof(baseName)); + memcpy(baseName, shortName, 8); + debug(0, "Playing video %s", shortName); + } else { + error("Failed to load video file %s", videoName); + } + } else { + debug(0, "Playing video %s", videoName); + } + + CursorMan.showMouse(false); + + if ((_vm->getPlatform() == Common::kPlatformAmiga || _vm->getPlatform() == Common::kPlatformMacintosh) && + _vm->_language != Common::EN_ANY) { + _sequenceNum = 0; + for (i = 0; i < 90; i++) { + if (!scumm_stricmp(baseName, _sequenceList[i])) + _sequenceNum = i; + } + } + + return true; +} + +void MoviePlayer::playOmniTV() { + // Load OmniTV video + if (!_fd.isOpen()) { + _vm->_variableArray[254] = 6747; + return; + } else { + _vm->setBitFlag(42, false); + _omniTV = true; + startSound(); + return; + } +} + +void MoviePlayer::play() { + // The OmniTV videos were not included with Amiga and Macintosh versions. + if (_vm->getPlatform() == Common::kPlatformWindows && _vm->getBitFlag(40)) { + playOmniTV(); + return; + } + + if (!_fd.isOpen()) { + return; + } + + _leftButtonDown = false; + _rightButtonDown = false; + + _mixer->stopAll(); + + // Resolution is smaller in Amiga verison so always clear screen + if (_width == 384 && _height == 280) { + memset(_vm->_frontBuf, 0, _vm->_screenHeight * _vm->_screenWidth); + } + + _ticks = _vm->_system->getMillis(); + + startSound(); + + while (_frameNum < _framesCount) + handleNextFrame(); + + closeFile(); + + _vm->o_killAnimate(); + + if (_vm->getBitFlag(41)) { + memcpy(_vm->_backBuf, _vm->_frontBuf, _frameSize); + } else { + uint8 palette[1024]; + memset(palette, 0, sizeof(palette)); + _vm->dx_clear_surfaces(480); + _vm->_system->setPalette(palette, 0, 256); + } + + _vm->_fastFadeOutFlag = true; +} + +void MoviePlayer::startSound() { + byte *buffer; + uint32 offset, size, tag; + + tag = _fd.readUint32BE(); + if (tag == MKID_BE('WAVE')) { + size = _fd.readUint32BE(); + + if (_sequenceNum) { + Common::File in; + + _fd.seek(size, SEEK_CUR); + + in.open((const char *)"audio.wav"); + if (!in.isOpen()) { + error("Can't read offset file 'audio.wav'"); + } + + in.seek(_sequenceNum * 8, SEEK_SET); + offset = in.readUint32LE(); + size = in.readUint32LE(); + + buffer = (byte *)malloc(size); + in.seek(offset, SEEK_SET); + in.read(buffer, size); + in.close(); + } else { + buffer = (byte *)malloc(size); + _fd.read(buffer, size); + } + + Common::MemoryReadStream stream(buffer, size); + _bgSoundStream = Audio::makeWAVStream(stream); + free(buffer); + } else { + _bgSoundStream = Audio::AudioStream::openStreamFile(baseName); + } + + if (_bgSoundStream != NULL) { + _mixer->stopHandle(_bgSound); + _mixer->playInputStream(Audio::Mixer::kSFXSoundType, &_bgSound, _bgSoundStream); + } +} + +void MoviePlayer::nextFrame() { + if (!_omniTV) + return; + + if (_vm->getBitFlag(42)) { + _omniTV = false; + closeFile(); + return; + } + + if (_mixer->isSoundHandleActive(_bgSound) && (_mixer->getSoundElapsedTime(_bgSound) * _framesPerSec) / 1000 < _frameNum) { + copyFrameToBuffer(_vm->getBackBuf(), 465, 222, _vm->_screenWidth); + return; + } + + if (_frameNum < _framesCount) { + decodeNextFrame(); + copyFrameToBuffer(_vm->getBackBuf(), 465, 222, _vm->_screenWidth); + _frameNum++; + } else { + _omniTV = false; + closeFile(); + _vm->_variableArray[254] = 6747; + } +} + +void MoviePlayer::handleNextFrame() { + decodeNextFrame(); + processFrame(); + + _vm->_system->updateScreen(); + _frameNum++; + + OSystem::Event event; + while (_vm->_system->pollEvent(event)) { + switch (event.type) { + case OSystem::EVENT_KEYDOWN: + if (event.kbd.ascii == 27) { + _leftButtonDown = true; + _rightButtonDown = true; + } + break; + case OSystem::EVENT_LBUTTONDOWN: + _leftButtonDown = true; + break; + case OSystem::EVENT_RBUTTONDOWN: + _rightButtonDown = true; + break; + case OSystem::EVENT_LBUTTONUP: + _leftButtonDown = false; + break; + case OSystem::EVENT_RBUTTONUP: + _rightButtonDown = false; + break; + case OSystem::EVENT_QUIT: + _vm->_system->quit(); + break; + default: + break; + } + } + + if (_leftButtonDown && _rightButtonDown && !_vm->getBitFlag(41)) { + _frameNum = _framesCount; + } +} + +void MoviePlayer::setPalette(byte *pal) { + byte palette[1024]; + byte *p = palette; + + for (int i = 0; i < 256; i++) { + *p++ = *pal++; + *p++ = *pal++; + *p++ = *pal++; + *p++ = 0; + } + + _vm->_system->setPalette(palette, 0, 256); +} + +void MoviePlayer::processFrame() { + copyFrameToBuffer(_vm->getFrontBuf(), (_vm->_screenWidth - _width) / 2, (_vm->_screenHeight - _height) / 2, _vm->_screenWidth); + _vm->_system->copyRectToScreen(_vm->getFrontBuf(), _vm->_screenWidth, 0, 0, _vm->_screenWidth, _vm->_screenHeight); + + if ((_bgSoundStream == NULL) || ((int)(_mixer->getSoundElapsedTime(_bgSound) * _framesPerSec) / 1000 < _frameNum + 1) || + _frameSkipped > _framesPerSec) { + if (_frameSkipped > _framesPerSec) { + warning("force frame %i redraw", _frameNum); + _frameSkipped = 0; + } + + if (_bgSoundStream && _mixer->isSoundHandleActive(_bgSound)) { + while (_mixer->isSoundHandleActive(_bgSound) && (_mixer->getSoundElapsedTime(_bgSound) * _framesPerSec) / 1000 < _frameNum) { + _vm->_system->delayMillis(10); + } + // In case the background sound ends prematurely, update + // _ticks so that we can still fall back on the no-sound + // sync case for the subsequent frames. + _ticks = _vm->_system->getMillis(); + } else { + _ticks += _frameTicks; + while (_vm->_system->getMillis() < _ticks) + _vm->_system->delayMillis(10); + } + } else { + warning("dropped frame %i", _frameNum); + _frameSkipped++; + } +} + +const char * MoviePlayer::_sequenceList[90] = { + "agent32", + "Airlock", + "Badluck", + "bentalk1", + "bentalk2", + "bentalk3", + "BigFight", + "BLOWLAB", + "breakdown", + "bridge", + "button2", + "cargo", + "COACH", + "Colatalk", + "cygnus2", + "dream", + "escape2", + "FASALL", + "fbikewurb", + "feebdel", + "Feebohno", + "feebpump", + "feefone1", + "feefone2", + "founder2", + "founder3", + "founder4", + "fxmadsam", + "fxwakeup", + "gate", + "Get Car", + "getaxe", + "getlift", + "icetrench", + "intomb1", + "intomb2", + "Jackpot", + "knockout", + "labocto", + "longfeeb", + "Mainmin", + "maznat", + "meetsquid", + "mflirt", + "mfxHappy", + "Mix_Feeb1", + "Mix_Feeb2", + "Mix_Feeb3", + "Mix_Guardscn", + "Mlights1", + "MLights2", + "MProtest", + "mudman", + "munlock", + "MUS5P2", + "MUSOSP1", + "Omenter", + "Omnicofe", + "OUTMIN~1", + "Readbook", + "Rebelhq", + "RebelHQ2", + "Reedin", + "rescue1", + "rescue2", + "samcar", + "Samdead", + "scanner", + "Sleepy", + "spitbrai", + "statue1", + "statue2", + "sva1", + "sva2", + "Teeter", + "Temple2", + "Temple3", + "Temple4", + "Temple5", + "Temple6", + "Temple7", + "Temple8", + "Tic-tac2", + "torture", + "transmit", + "Typey", + "ventfall", + "ventoff", + "wasting", + "wurbatak" +}; + +} // End of namespace Simon diff --git a/engines/agos/animation.h b/engines/agos/animation.h new file mode 100644 index 0000000000..782cea87a9 --- /dev/null +++ b/engines/agos/animation.h @@ -0,0 +1,71 @@ +/* ScummVM - Scumm Interpreter + * Copyright (C) 2001 Ludvig Strigeus + * Copyright (C) 2001-2006 The ScummVM project + * + * 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. + * + * $URL$ + * $Id$ + * + */ + +#ifndef ANIMATION_H +#define ANIMATION_H + +#include "common/file.h" +#include "common/stream.h" + +#include "graphics/dxa_player.h" +#include "sound/mixer.h" + +namespace Simon { + +class SimonEngine; + +class MoviePlayer : public Graphics::DXAPlayer { + SimonEngine *_vm; + + Audio::Mixer *_mixer; + + Audio::SoundHandle _bgSound; + Audio::AudioStream *_bgSoundStream; + + bool _omniTV; + bool _leftButtonDown; + bool _rightButtonDown; + uint32 _ticks; + + char baseName[40]; + static const char *_sequenceList[90]; + uint8 _sequenceNum; +public: + MoviePlayer(SimonEngine *vm, Audio::Mixer *mixer); + + bool load(const char *filename); + void play(); + void nextFrame(); +protected: + virtual void setPalette(byte *pal); +private: + void playOmniTV(); + + void handleNextFrame(); + void processFrame(); + void startSound(); +}; + +} // End of namespace Simon + +#endif diff --git a/engines/agos/charset.cpp b/engines/agos/charset.cpp new file mode 100644 index 0000000000..f051197d4b --- /dev/null +++ b/engines/agos/charset.cpp @@ -0,0 +1,1629 @@ +/* ScummVM - Scumm Interpreter + * Copyright (C) 2001 Ludvig Strigeus + * Copyright (C) 2001-2006 The ScummVM project + * + * 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. + * + * $URL$ + * $Id$ + * + */ + +#include "common/stdafx.h" + +#include "agos/agos.h" +#include "agos/intern.h" + +namespace Simon { + +void SimonEngine::print_char_helper_1(const byte *src, uint len) { + uint idx; + + if (_textWindow == NULL) + return; + + while (len-- != 0) { + if (getGameType() == GType_FF || getGameType() == GType_PP) { + if (getBitFlag(93)) { + if (_curWindow == 3) { + if ((_newLines >= _textWindow->scrollY) && (_newLines < (_textWindow->scrollY + 3))) + windowPutChar(*src); + if (*src == '\n') // Do two top lines of text only + _newLines++; + src++; + } + } else { + if (getBitFlag(94)) { + if (_curWindow == 3) { + if (_newLines == (_textWindow->scrollY + 7)) + windowPutChar(*src); + if (*src == '\n') // Do two top lines of text only + _newLines++; + src++; + } + } else { + if (getBitFlag(92)) + delay(50); + windowPutChar(*src++); + } + } + } else { + if (*src != 12 && _textWindow->iconPtr != NULL && + _fcsData1[idx = getWindowNum(_textWindow)] != 2) { + + _fcsData1[idx] = 2; + _fcsData2[idx] = 1; + } + + windowPutChar(*src++); + } + } +} + +void SimonEngine::print_char_helper_5(WindowBlock *window) { + uint index = getWindowNum(window); + tidyIconArray(index); + _fcsData1[index] = 0; +} + +void SimonEngine::tidyIconArray(uint i) { + WindowBlock *window; + + if (_fcsData2[i]) { + mouseOff(); + window = _windowArray[i]; + drawIconArray(i, window->iconPtr->itemRef, window->iconPtr->line, window->iconPtr->classMask); + _fcsData2[i] = 0; + mouseOn(); + } +} + +void SimonEngine::renderStringAmiga(uint vga_sprite_id, uint color, uint width, uint height, const char *txt) { + VgaPointersEntry *vpe = &_vgaBufferPointers[2]; + byte *src, *dst, *dst_org, chr; + uint count; + + if (vga_sprite_id >= 100) { + vga_sprite_id -= 100; + vpe++; + } + + src = dst = vpe->vgaFile2; + + count = 499; + if (vga_sprite_id == 1) + count *= 2; + + src += vga_sprite_id * 8; + dst += READ_BE_UINT32(src); + WRITE_BE_UINT16(dst + 4, height); + WRITE_BE_UINT16(dst + 6, width); + + uint charsize = width/8 * height; + memset(dst, 0, count); + dst_org = dst; + int delta = 0; + while ((chr = *txt++) != 0) { + int tmp = chr; + if (chr == 10) { + dst_org += width * 10; + dst = dst_org; + delta = 0; + } else if ((tmp -= '!') < 0) { + delta += 6; + if (delta > 8) + { + delta -= 8; + dst_org++; + } + } else { + byte *img = src + chr * 41; + int CTR = img[40]; + int D3 = 8 - delta; + for (int D2 = 9; D2 != 0; D2--) + { + byte *cur_dst = dst_org; + for (int D7 = 2; D7 != 0; D7--) + { + chr = *img >> delta; + if (chr) + { + if (color & 1) *(cur_dst + charsize * 0) |= chr; + if (color & 2) *(cur_dst + charsize * 1) |= chr; + if (color & 4) *(cur_dst + charsize * 2) |= chr; + if (color & 8) *(cur_dst + charsize * 3) |= chr; + } + if ((D3 >= CTR) && (chr = *img++ << (D3))) + { + if (color & 1) *(cur_dst + charsize * 0) |= chr; + if (color & 2) *(cur_dst + charsize * 1) |= chr; + if (color & 4) *(cur_dst + charsize * 2) |= chr; + if (color & 8) *(cur_dst + charsize * 3) |= chr; + } + color++; + } + chr = *img >> delta; + if (chr) + { + *(cur_dst + charsize * 0) |= chr; + *(cur_dst + charsize * 1) |= chr; + *(cur_dst + charsize * 2) |= chr; + *(cur_dst + charsize * 3) |= chr; + } + if ((D3 >= CTR) && (chr = *img++ << (D3))) + { + *(cur_dst + charsize * 0) |= chr; + *(cur_dst + charsize * 1) |= chr; + *(cur_dst + charsize * 2) |= chr; + *(cur_dst + charsize * 3) |= chr; + } + cur_dst += width/8; + } + delta += CTR; + if (delta > 8) + { + delta -= 8; + dst_org++; + } + } + } +} + +void SimonEngine::renderString(uint vga_sprite_id, uint color, uint width, uint height, const char *txt) { + VgaPointersEntry *vpe = &_vgaBufferPointers[2]; + byte *src, *dst, *p, *dst_org, chr; + const int textHeight = (getGameType() == GType_FF || getGameType() == GType_PP) ? 15: 10; + uint count = 0; + + if (vga_sprite_id >= 100) { + vga_sprite_id -= 100; + vpe++; + } + + src = dst = vpe->vgaFile2; + + if (getGameType() == GType_FF || getGameType() == GType_PP) { + if (vga_sprite_id == 1) + count = 45000; + } else { + count = 4000; + if (vga_sprite_id == 1) + count *= 2; + } + + p = dst + vga_sprite_id * 8; + + if (getGameType() == GType_FF || getGameType() == GType_PP) { + WRITE_LE_UINT16(p + 4, height); + WRITE_LE_UINT16(p + 6, width); + // We need to adjust the offset to the next buffer to be right + // after this one. By default, each buffer is only 9000 bytes + // long. A two-line string can very well be more than twice + // that size! + // + // The original seems to make an exception for sprite id 1, but + // even the first conversation option can be a long line. For + // some reason, I cannot reproduce the text corruption with the + // original interpreter, though, so maybe we're missing some + // detail here. Let's hope it's safe to always adjust the + // buffer size anyway. + WRITE_LE_UINT16(p + 8, READ_LE_UINT32(p) + width * height); + } else { + WRITE_BE_UINT16(p + 4, height); + WRITE_BE_UINT16(p + 6, width); + } + dst += readUint32Wrapper(p); + + if (count != 0) + memset(dst, 0, count); + + if (_language == Common::HB_ISR) + dst += width - 1; // For Hebrew, start at the right edge, not the left. + + dst_org = dst; + while ((chr = *txt++) != 0) { + if (chr == 10) { + dst_org += width * textHeight; + dst = dst_org; + } else if ((chr -= ' ') == 0) { + dst += (_language == Common::HB_ISR ? -6 : 6); // Hebrew moves to the left, all others to the right + } else { + byte *img_hdr, *img; + uint i, img_width, img_height; + + if (getGameType() == GType_FF || getGameType() == GType_PP) { + img_hdr = src + 96 + chr * 8; + img_height = READ_LE_UINT16(img_hdr + 4); + img_width = READ_LE_UINT16(img_hdr + 6); + img = src + READ_LE_UINT32(img_hdr); + } else { + img_hdr = src + 48 + chr * 4; + img_height = img_hdr[2]; + img_width = img_hdr[3]; + img = src + READ_LE_UINT16(img_hdr); + } + + if (_language == Common::HB_ISR) + dst -= img_width - 1; // For Hebrew, move from right edge to left edge of image. + byte *cur_dst = dst; + + assert(img_width > 0 && img_width < 50 && img_height > 0 && img_height < 50); + + do { + for (i = 0; i != img_width; i++) { + chr = *img++; + if (chr) { + if (chr == 0xF) + chr = 207; + else + chr += color; + cur_dst[i] = chr; + } + } + cur_dst += width; + } while (--img_height); + + if (_language != Common::HB_ISR) // Hebrew character movement is done higher up + dst += img_width - 1; + } + } +} + +static const byte feebleFontSize[208] = { + 8, 2, 5, 7, 8, 8, 8, 2, 4, 4, 8, 8, 3, 8, 2, 9, + 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 2, 3, 5, 8, 5, 8, + 8, 8, 8, 8, 8, 8, 8, 8, 8, 4, 8, 8, 8, 8, 8, 8, + 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 4, 9, 4, 4, 9, + 4, 8, 8, 8, 8, 8, 7, 8, 8, 4, 5, 7, 3, 8, 8, 8, + 8, 8, 8, 7, 7, 8, 8, 8, 8, 8, 8, 5, 2, 5, 8, 8, + 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 4, 4, 4, 8, 8, + 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 8, 4, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 2, 8, 8, + 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, +}; + +uint SimonEngine::getFeebleFontSize(byte chr) { + return feebleFontSize[chr - 32]; +} + +void SimonEngine::showMessageFormat(const char *s, ...) { + char buf[STRINGBUFLEN]; + char *str; + va_list va; + + va_start(va, s); + vsnprintf(buf, STRINGBUFLEN, s, va); + va_end(va); + + if (!_fcsData1[_curWindow]) { + openTextWindow(); + if (!_showMessageFlag) { + _windowArray[0] = _textWindow; + if (getGameType() == GType_FF || getGameType() == GType_PP) + showmessage_helper_3(_textWindow->textColumn, _textWindow->width); + else + showmessage_helper_3(_textWindow->textLength, _textWindow->textMaxLength); + } + _showMessageFlag = true; + _fcsData1[_curWindow] = 1; + } + + for (str = buf; *str; str++) + showmessage_print_char(*str); +} + +void SimonEngine::showmessage_print_char(byte chr) { + if (chr == 12) { + _numLettersToPrint = 0; + _printCharCurPos = 0; + _printCharPixelCount = 0; + print_char_helper_1(&chr, 1); + print_char_helper_5(_textWindow); + } else if (chr == 0 || chr == ' ' || chr == 10) { + bool fit; + + // Note that in FF, _printCharCurPos may be greater than + // _printCharMaxPos. In Simon, that is probably prevented by + // testing if _printCharCurPos == _printCharMaxPos below. + + if (getGameType() == GType_FF || getGameType() == GType_PP) { + fit = _printCharMaxPos > _printCharCurPos + _printCharPixelCount; + } else { + fit = _printCharMaxPos - _printCharCurPos >= _printCharPixelCount; + } + + if (fit) { + _printCharCurPos += _printCharPixelCount; + print_char_helper_1(_lettersToPrintBuf, _numLettersToPrint); + + if (_printCharCurPos == _printCharMaxPos) { + _printCharCurPos = 0; + } else { + if (chr) + print_char_helper_1(&chr, 1); + if (chr == 10) + _printCharCurPos = 0; + else if (chr != 0) + _printCharCurPos += (getGameType() == GType_FF || getGameType() == GType_PP) ? feebleFontSize[chr - 32] : 1; + } + } else { + const byte newline_character = 10; + _printCharCurPos = _printCharPixelCount; + print_char_helper_1(&newline_character, 1); + print_char_helper_1(_lettersToPrintBuf, _numLettersToPrint); + if (chr == ' ') { + print_char_helper_1(&chr, 1); + _printCharCurPos += (getGameType() == GType_FF || getGameType() == GType_PP) ? feebleFontSize[chr - 32] : 1; + } else { + print_char_helper_1(&chr, 1); + _printCharCurPos = 0; + } + } + _numLettersToPrint = 0; + _printCharPixelCount = 0; + } else { + _lettersToPrintBuf[_numLettersToPrint++] = chr; + _printCharPixelCount += (getGameType() == GType_FF || getGameType() == GType_PP) ? feebleFontSize[chr - 32] : 1; + } +} + +void SimonEngine::openTextWindow() { + if (_textWindow) + return; + + if (getGameType() == GType_FF || getGameType() == GType_PP) + _textWindow = openWindow(64, 96, 384, 172, 1, 0, 15); + else + _textWindow = openWindow(8, 144, 24, 6, 1, 0, 15); +} + +void SimonEngine::showmessage_helper_3(uint a, uint b) { + _printCharCurPos = a; + _printCharMaxPos = b; + _printCharPixelCount = 0; + _numLettersToPrint = 0; + _newLines = 0; +} + +void SimonEngine::windowPutChar(WindowBlock *window, byte c, byte b) { + byte width = 6; + + if (c == 12) { + clearWindow(window); + } else if (c == 13 || c == 10) { + video_putchar_newline(window); + } else if ((c == 1 && _language != Common::HB_ISR) || (c == 8)) { + if (_language == Common::HB_ISR) { + if (b >= 64 && b < 91) + width = _hebrewCharWidths [b - 64]; + + if (window->textLength != 0) { + window->textLength--; + window->textColumnOffset += width; + if (window->textColumnOffset >= 8) { + window->textColumnOffset -= 8; + window->textColumn--; + } + } + } else { + int8 val = (c == 8) ? 6 : 4; + + if (window->textLength != 0) { + window->textLength--; + window->textColumnOffset -= val; + if ((int8)window->textColumnOffset < val) { + window->textColumnOffset += 8; + window->textColumn--; + } + } + } + } else if (c >= 32) { + if (getGameType() == GType_FF || getGameType() == GType_PP) { + video_putchar_drawchar(window, window->textColumn + window->x, window->textRow + window->y, c); + window->textColumn += feebleFontSize[c - 32]; + return; + } + + if (window->textLength == window->textMaxLength) { + video_putchar_newline(window); + } else if (window->textRow == window->height) { + video_putchar_newline(window); + window->textRow--; + } + + if (_language == Common::HB_ISR) { + if (c >= 64 && c < 91) + width = _hebrewCharWidths [c - 64]; + window->textColumnOffset -= width; + if (window->textColumnOffset >= width) { + window->textColumnOffset += 8; + window->textColumn++; + } + video_putchar_drawchar(window, (window->width + window->x - window->textColumn) * 8, window->textRow * 8 + window->y, c); + window->textLength++; + } else { + video_putchar_drawchar(window, (window->textColumn + window->x) * 8, window->textRow * 8 + window->y, c); + + window->textLength++; + window->textColumnOffset += 6; + if (c == 'i' || c == 'l') + window->textColumnOffset -= 2; + + if (window->textColumnOffset >= 8) { + window->textColumnOffset -= 8; + window->textColumn++; + } + } + } +} + +void SimonEngine::video_putchar_newline(WindowBlock *window) { + if (getGameType() == GType_FF) { + if (_noOracleScroll == 0) { + if (window->height < window->textRow + 30) { + if (!getBitFlag(94)) { + _noOracleScroll = 1; + if (getBitFlag(92)) { + _noOracleScroll = 0; + checkLinkBox(); + scrollOracle(); + linksUp(); + window->scrollY++; + _oracleMaxScrollY++; + } else { + _oracleMaxScrollY++; + checkLinkBox(); + } + } + } else { + window->textRow += 15; + checkLinkBox(); + } + } else { + _oracleMaxScrollY++; + checkLinkBox(); + } + } else { + if (window->textRow != window->height) + window->textRow++; + } + + window->textColumn = 0; + window->textColumnOffset = 0; + window->textLength = 0; +} + +#ifdef PALMOS_68K +static const byte *russian_video_font; +static const byte *polish_video_font; +static const byte *french_video_font; +static const byte *german_video_font; +static const byte *hebrew_video_font; +static const byte *italian_video_font; +static const byte *spanish_video_font; +static const byte *video_font; +#else +static const byte russian_video_font[] = { + 0, 0, 0, 0, 0, 0, 0, 0, + 32, 112, 112, 32, 32, 0, 32, 0, + 48, 48, 96, 0, 0, 0, 0, 0, + 0, 0, 100, 40, 48, 40, 100, 0, + 0, 0, 96, 48, 40, 40, 112, 0, + 60, 68, 68, 60, 36, 68, 68, 0, + 0, 16, 40, 16, 42, 68, 58, 0, + 48, 48, 96, 0, 0, 0, 0, 0, + 0, 4, 8, 8, 8, 8, 4, 0, + 0, 32, 16, 16, 16, 16, 32, 0, + 72, 84, 84, 116, 84, 84, 72, 0, + 0, 0, 60, 68, 60, 36, 100, 0, + 0, 0, 0, 0, 0, 48, 48, 96, + 0, 0, 0, 240, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 48, 48, 0, + 16, 32, 0, 120, 112, 64, 56, 0, + 112, 136, 152, 168, 200, 136, 112, 0, + 32, 96, 32, 32, 32, 32, 112, 0, + 112, 136, 8, 48, 64, 136, 248, 0, + 112, 136, 8, 48, 8, 136, 112, 0, + 16, 48, 80, 144, 248, 16, 56, 0, + 248, 128, 240, 8, 8, 136, 112, 0, + 48, 64, 128, 240, 136, 136, 112, 0, + 248, 136, 8, 16, 32, 32, 32, 0, + 112, 136, 136, 112, 136, 136, 112, 0, + 112, 136, 136, 120, 8, 16, 96, 0, + 0, 0, 48, 48, 0, 48, 48, 0, + 32, 16, 0, 112, 8, 248, 120, 0, + 0, 0, 96, 48, 40, 40, 112, 0, + 0, 0, 112, 88, 20, 20, 56, 0, + 0, 0, 120, 4, 28, 4, 120, 0, + 112, 136, 8, 16, 32, 0, 32, 0, + 0, 0, 84, 84, 84, 84, 56, 4, + 56, 68, 68, 124, 68, 68, 68, 0, + 124, 36, 32, 56, 36, 36, 120, 0, + 120, 36, 36, 56, 36, 36, 120, 0, + 124, 36, 32, 32, 32, 32, 112, 0, + 56, 40, 40, 40, 40, 40, 124, 68, + 124, 36, 32, 56, 32, 36, 124, 0, + 84, 84, 84, 56, 84, 84, 84, 0, + 56, 68, 4, 24, 4, 68, 56, 0, + 68, 68, 76, 84, 100, 68, 68, 0, + 100, 40, 40, 48, 40, 36, 100, 0, + 28, 36, 36, 36, 36, 36, 100, 0, + 68, 108, 84, 68, 68, 68, 68, 0, + 68, 68, 68, 124, 68, 68, 68, 0, + 56, 68, 68, 68, 68, 68, 56, 0, + 124, 68, 68, 68, 68, 68, 68, 0, + 120, 36, 36, 56, 32, 32, 112, 0, + 56, 68, 64, 64, 64, 68, 56, 0, + 124, 84, 16, 16, 16, 16, 56, 0, + 100, 36, 36, 28, 4, 4, 56, 0, + 56, 84, 84, 84, 56, 16, 56, 0, + 108, 40, 16, 16, 40, 40, 108, 0, + 72, 72, 72, 72, 72, 72, 60, 4, + 76, 72, 72, 56, 8, 8, 28, 0, + 84, 84, 84, 84, 84, 84, 60, 0, + 84, 84, 84, 84, 84, 84, 56, 4, + 56, 68, 4, 28, 4, 68, 56, 0, + 0, 0, 68, 100, 84, 84, 100, 0, + 0, 0, 72, 84, 116, 84, 72, 0, + 0, 0, 60, 68, 60, 36, 100, 0, + 0, 0, 120, 4, 24, 4, 120, 0, + 0, 0, 100, 40, 48, 40, 100, 0, + 60, 68, 68, 60, 36, 68, 68, 0, + 0, 0, 56, 4, 60, 68, 60, 0, + 60, 64, 32, 56, 68, 68, 56, 0, + 48, 72, 80, 120, 68, 68, 56, 0, + 0, 0, 120, 4, 56, 64, 60, 0, + 56, 4, 4, 60, 68, 68, 56, 0, + 0, 0, 56, 68, 120, 64, 56, 0, + 40, 0, 56, 68, 120, 64, 56, 0, + 0, 0, 84, 84, 56, 84, 84, 0, + 64, 0, 192, 64, 64, 64, 224, 0, + 0, 0, 68, 68, 68, 68, 60, 0, + 56, 0, 68, 68, 68, 68, 60, 0, + 192, 64, 64, 64, 64, 64, 224, 0, + 0, 0, 28, 36, 36, 36, 100, 0, + 0, 0, 68, 108, 84, 68, 68, 0, + 0, 0, 56, 68, 68, 68, 56, 0, + 0, 0, 68, 68, 124, 68, 68, 0, + 0, 0, 124, 68, 68, 68, 68, 0, + 0, 0, 120, 36, 36, 56, 32, 112, + 0, 0, 60, 64, 64, 64, 60, 0, + 0, 0, 124, 84, 16, 16, 56, 0, + 0, 0, 68, 68, 60, 4, 56, 0, + 48, 16, 56, 84, 84, 56, 16, 56, + 0, 0, 68, 40, 16, 40, 68, 0, + 0, 0, 72, 72, 72, 72, 60, 4, + 0, 0, 76, 72, 72, 56, 8, 28, + 0, 0, 84, 84, 84, 84, 60, 0, + 32, 80, 0, 96, 144, 144, 96, 0, + 0, 14, 8, 48, 8, 8, 14, 0, + 0, 8, 8, 8, 8, 8, 8, 0, + 0, 112, 16, 12, 16, 16, 112, 0, + 0, 0, 0, 0, 0, 0, 248, 0, + 252, 252, 252, 252, 252, 252, 252, 252, + 240, 240, 240, 240, 240, 240, 240, 240, +}; + +static const byte polish_video_font[] = { + 0, 0, 0, 0, 0, 0, 0, 0, + 32, 112, 112, 32, 32, 0, 32, 0, + 48, 48, 96, 0, 0, 0, 0, 0, + 0, 0, 112, 136, 248, 128, 112, 8, + 0, 16, 120, 128, 112, 8, 240, 0, + 192, 64, 64, 96, 192, 64, 224, 0, + 0, 16, 40, 16, 42, 68, 58, 0, + 48, 48, 96, 0, 0, 0, 0, 0, + 0, 4, 8, 8, 8, 8, 4, 0, + 0, 32, 16, 16, 16, 16, 32, 0, + 0, 0, 20, 8, 62, 8, 20, 0, + 0, 32, 112, 136, 136, 136, 112, 0, + 0, 0, 0, 0, 0, 48, 48, 96, + 0, 0, 0, 240, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 48, 48, 0, + 0, 32, 112, 136, 128, 136, 112, 0, + 112, 136, 152, 168, 200, 136, 112, 0, + 32, 96, 32, 32, 32, 32, 112, 0, + 112, 136, 8, 48, 64, 136, 248, 0, + 112, 136, 8, 48, 8, 136, 112, 0, + 16, 48, 80, 144, 248, 16, 56, 0, + 248, 128, 240, 8, 8, 136, 112, 0, + 48, 64, 128, 240, 136, 136, 112, 0, + 248, 136, 8, 16, 32, 32, 32, 0, + 112, 136, 136, 112, 136, 136, 112, 0, + 112, 136, 136, 120, 8, 16, 96, 0, + 0, 0, 48, 48, 0, 48, 48, 0, + 0, 32, 240, 136, 136, 136, 136, 0, + 80, 0, 136, 136, 136, 136, 112, 0, + 0, 32, 248, 144, 32, 72, 248, 0, + 8, 32, 248, 144, 32, 72, 248, 0, + 112, 136, 8, 16, 32, 0, 32, 0, + 0, 0, 112, 8, 120, 136, 120, 4, + 112, 136, 136, 248, 136, 136, 136, 0, + 240, 72, 72, 112, 72, 72, 240, 0, + 48, 72, 128, 128, 128, 72, 48, 0, + 224, 80, 72, 72, 72, 80, 224, 0, + 248, 72, 64, 112, 64, 72, 248, 0, + 248, 72, 64, 112, 64, 64, 224, 0, + 48, 72, 128, 152, 136, 72, 56, 0, + 136, 136, 136, 248, 136, 136, 136, 0, + 248, 32, 32, 32, 32, 32, 248, 0, + 24, 8, 8, 8, 136, 136, 112, 0, + 200, 72, 80, 96, 80, 72, 200, 0, + 224, 64, 64, 64, 64, 72, 248, 0, + 136, 216, 168, 168, 136, 136, 136, 0, + 136, 200, 168, 152, 136, 136, 136, 0, + 112, 136, 136, 136, 136, 136, 112, 0, + 240, 72, 72, 112, 64, 64, 224, 0, + 112, 136, 136, 136, 136, 168, 112, 8, + 240, 72, 72, 112, 72, 72, 200, 0, + 112, 136, 128, 112, 8, 136, 112, 0, + 248, 168, 32, 32, 32, 32, 112, 0, + 136, 136, 136, 136, 136, 136, 120, 0, + 136, 136, 136, 80, 80, 32, 32, 0, + 136, 136, 136, 136, 168, 216, 136, 0, + 136, 136, 80, 32, 80, 136, 136, 0, + 136, 136, 136, 112, 32, 32, 112, 0, + 248, 136, 16, 32, 64, 136, 248, 0, + 0, 14, 8, 8, 8, 8, 14, 0, + 0, 128, 64, 32, 16, 8, 4, 0, + 224, 64, 64, 96, 192, 72, 248, 0, + 16, 120, 128, 112, 8, 136, 112, 0, + 248, 72, 64, 112, 64, 72, 248, 16, + 32, 248, 16, 32, 64, 136, 248, 0, + 0, 0, 112, 8, 120, 136, 120, 0, + 192, 64, 80, 104, 72, 72, 112, 0, + 0, 0, 112, 136, 128, 136, 112, 0, + 24, 16, 80, 176, 144, 144, 112, 0, + 0, 0, 112, 136, 248, 128, 112, 0, + 48, 72, 64, 224, 64, 64, 224, 0, + 0, 0, 104, 144, 144, 112, 136, 112, + 192, 64, 80, 104, 72, 72, 200, 0, + 64, 0, 192, 64, 64, 64, 224, 0, + 8, 0, 8, 8, 8, 8, 136, 112, + 192, 64, 72, 80, 96, 80, 200, 0, + 192, 64, 64, 64, 64, 64, 224, 0, + 0, 0, 144, 216, 168, 136, 136, 0, + 0, 0, 240, 136, 136, 136, 136, 0, + 0, 0, 112, 136, 136, 136, 112, 0, + 0, 0, 176, 72, 72, 112, 64, 224, + 0, 0, 104, 144, 144, 112, 16, 56, + 0, 0, 176, 72, 72, 64, 224, 0, + 0, 0, 120, 128, 112, 8, 240, 0, + 64, 64, 240, 64, 64, 72, 48, 0, + 0, 0, 144, 144, 144, 144, 104, 0, + 0, 0, 136, 136, 136, 80, 32, 0, + 0, 0, 136, 136, 168, 216, 144, 0, + 0, 0, 136, 80, 32, 80, 136, 0, + 0, 0, 136, 136, 136, 112, 32, 192, + 0, 0, 248, 144, 32, 72, 248, 0, + 32, 80, 0, 96, 144, 144, 96, 0, + 0, 14, 8, 48, 8, 8, 14, 0, + 0, 8, 8, 8, 8, 8, 8, 0, + 0, 112, 16, 12, 16, 16, 112, 0, + 0, 0, 0, 0, 0, 0, 248, 0, + 252, 252, 252, 252, 252, 252, 252, 252, + 240, 240, 240, 240, 240, 240, 240, 240, +}; + +static const byte french_video_font[] = { + 0, 0, 0, 0, 0, 0, 0, 0, + 32, 112, 112, 32, 32, 0, 32, 0, + 48, 48, 96, 0, 0, 0, 0, 0, + 32, 80, 0, 112, 136, 136, 112, 0, + 32, 80, 0, 112, 8, 248, 120, 0, + 112, 136, 128, 128, 136, 112, 32, 96, + 0, 16, 40, 16, 42, 68, 58, 0, + 48, 48, 96, 0, 0, 0, 0, 0, + 0, 4, 8, 8, 8, 8, 4, 0, + 0, 32, 16, 16, 16, 16, 32, 0, + 0, 0, 20, 8, 62, 8, 20, 0, + 112, 136, 128, 128, 136, 112, 32, 96, + 0, 0, 0, 0, 0, 48, 48, 96, + 0, 0, 0, 240, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 48, 48, 0, + 32, 64, 0, 112, 248, 128, 112, 0, + 112, 136, 152, 168, 200, 136, 112, 0, + 32, 96, 32, 32, 32, 32, 112, 0, + 112, 136, 8, 48, 64, 136, 248, 0, + 112, 136, 8, 48, 8, 136, 112, 0, + 16, 48, 80, 144, 248, 16, 56, 0, + 248, 128, 240, 8, 8, 136, 112, 0, + 48, 64, 128, 240, 136, 136, 112, 0, + 248, 136, 8, 16, 32, 32, 32, 0, + 112, 136, 136, 112, 136, 136, 112, 0, + 112, 136, 136, 120, 8, 16, 96, 0, + 0, 0, 48, 48, 0, 48, 48, 0, + 32, 16, 0, 112, 8, 248, 120, 0, + 32, 80, 0, 144, 144, 144, 104, 0, + 32, 16, 0, 112, 248, 128, 112, 0, + 32, 80, 0, 112, 248, 128, 112, 0, + 112, 136, 8, 16, 32, 0, 32, 0, + 32, 80, 0, 192, 64, 64, 224, 0, + 112, 136, 136, 248, 136, 136, 136, 0, + 240, 72, 72, 112, 72, 72, 240, 0, + 48, 72, 128, 128, 128, 72, 48, 0, + 224, 80, 72, 72, 72, 80, 224, 0, + 248, 72, 64, 112, 64, 72, 248, 0, + 248, 72, 64, 112, 64, 64, 224, 0, + 48, 72, 128, 152, 136, 72, 56, 0, + 136, 136, 136, 248, 136, 136, 136, 0, + 248, 32, 32, 32, 32, 32, 248, 0, + 24, 8, 8, 8, 136, 136, 112, 0, + 200, 72, 80, 96, 80, 72, 200, 0, + 224, 64, 64, 64, 64, 72, 248, 0, + 136, 216, 168, 168, 136, 136, 136, 0, + 136, 200, 168, 152, 136, 136, 136, 0, + 112, 136, 136, 136, 136, 136, 112, 0, + 240, 72, 72, 112, 64, 64, 224, 0, + 112, 136, 136, 136, 136, 168, 112, 8, + 240, 72, 72, 112, 72, 72, 200, 0, + 112, 136, 128, 112, 8, 136, 112, 0, + 248, 168, 32, 32, 32, 32, 112, 0, + 136, 136, 136, 136, 136, 136, 120, 0, + 136, 136, 136, 80, 80, 32, 32, 0, + 136, 136, 136, 136, 168, 216, 136, 0, + 136, 136, 80, 32, 80, 136, 136, 0, + 136, 136, 136, 112, 32, 32, 112, 0, + 248, 136, 16, 32, 64, 136, 248, 0, + 0, 14, 8, 8, 8, 8, 14, 0, + 0, 128, 64, 32, 16, 8, 4, 0, + 0, 112, 16, 16, 16, 16, 112, 0, + 0, 0, 112, 136, 128, 112, 32, 96, + 160, 0, 192, 64, 64, 64, 224, 0, + 32, 16, 0, 144, 144, 144, 104, 0, + 0, 0, 112, 8, 120, 136, 120, 0, + 192, 64, 80, 104, 72, 72, 112, 0, + 0, 0, 112, 136, 128, 136, 112, 0, + 24, 16, 80, 176, 144, 144, 112, 0, + 0, 0, 112, 136, 248, 128, 112, 0, + 48, 72, 64, 224, 64, 64, 224, 0, + 0, 0, 104, 144, 144, 112, 136, 112, + 192, 64, 80, 104, 72, 72, 200, 0, + 64, 0, 192, 64, 64, 64, 224, 0, + 8, 0, 8, 8, 8, 8, 136, 112, + 192, 64, 72, 80, 96, 80, 200, 0, + 192, 64, 64, 64, 64, 64, 224, 0, + 0, 0, 144, 216, 168, 136, 136, 0, + 0, 0, 240, 136, 136, 136, 136, 0, + 0, 0, 112, 136, 136, 136, 112, 0, + 0, 0, 176, 72, 72, 112, 64, 224, + 0, 0, 104, 144, 144, 112, 16, 56, + 0, 0, 176, 72, 72, 64, 224, 0, + 0, 0, 120, 128, 112, 8, 240, 0, + 64, 64, 240, 64, 64, 72, 48, 0, + 0, 0, 144, 144, 144, 144, 104, 0, + 0, 0, 136, 136, 136, 80, 32, 0, + 0, 0, 136, 136, 168, 216, 144, 0, + 0, 0, 136, 80, 32, 80, 136, 0, + 0, 0, 136, 136, 136, 112, 32, 192, + 0, 0, 248, 144, 32, 72, 248, 0, + 32, 80, 0, 96, 144, 144, 96, 0, + 0, 14, 8, 48, 8, 8, 14, 0, + 0, 8, 8, 8, 8, 8, 8, 0, + 0, 112, 16, 12, 16, 16, 112, 0, + 0, 0, 0, 0, 0, 0, 248, 0, + 252, 252, 252, 252, 252, 252, 252, 252, + 240, 240, 240, 240, 240, 240, 240, 240, +}; + +static const byte german_video_font[] = { + 0, 0, 0, 0, 0, 0, 0, 0, + 32, 112, 112, 32, 32, 0, 32, 0, + 48, 48, 96, 0, 0, 0, 0, 0, + 80, 0, 112, 8, 120, 136, 120, 0, + 80, 0, 112, 136, 136, 136, 112, 0, + 80, 0, 144, 144, 144, 144, 104, 0, + 0, 16, 40, 16, 42, 68, 58, 0, + 48, 48, 96, 0, 0, 0, 0, 0, + 0, 4, 8, 8, 8, 8, 4, 0, + 0, 32, 16, 16, 16, 16, 32, 0, + 0, 0, 20, 8, 62, 8, 20, 0, + 96, 144, 144, 160, 144, 144, 160, 128, + 0, 0, 0, 0, 0, 48, 48, 96, + 0, 0, 0, 240, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 48, 48, 0, + 80, 0, 112, 136, 248, 136, 136, 0, + 112, 136, 152, 168, 200, 136, 112, 0, + 32, 96, 32, 32, 32, 32, 112, 0, + 112, 136, 8, 48, 64, 136, 248, 0, + 112, 136, 8, 48, 8, 136, 112, 0, + 16, 48, 80, 144, 248, 16, 56, 0, + 248, 128, 240, 8, 8, 136, 112, 0, + 48, 64, 128, 240, 136, 136, 112, 0, + 248, 136, 8, 16, 32, 32, 32, 0, + 112, 136, 136, 112, 136, 136, 112, 0, + 112, 136, 136, 120, 8, 16, 96, 0, + 0, 0, 48, 48, 0, 48, 48, 0, + 136, 112, 136, 136, 136, 136, 112, 0, + 80, 0, 136, 136, 136, 136, 112, 0, + 80, 0, 144, 144, 144, 144, 104, 0, + 32, 64, 0, 112, 248, 128, 112, 0, + 112, 136, 8, 16, 32, 0, 32, 0, + 32, 80, 0, 192, 64, 64, 224, 0, + 112, 136, 136, 248, 136, 136, 136, 0, + 240, 72, 72, 112, 72, 72, 240, 0, + 48, 72, 128, 128, 128, 72, 48, 0, + 224, 80, 72, 72, 72, 80, 224, 0, + 248, 72, 64, 112, 64, 72, 248, 0, + 248, 72, 64, 112, 64, 64, 224, 0, + 48, 72, 128, 152, 136, 72, 56, 0, + 136, 136, 136, 248, 136, 136, 136, 0, + 248, 32, 32, 32, 32, 32, 248, 0, + 24, 8, 8, 8, 136, 136, 112, 0, + 200, 72, 80, 96, 80, 72, 200, 0, + 224, 64, 64, 64, 64, 72, 248, 0, + 136, 216, 168, 168, 136, 136, 136, 0, + 136, 200, 168, 152, 136, 136, 136, 0, + 112, 136, 136, 136, 136, 136, 112, 0, + 240, 72, 72, 112, 64, 64, 224, 0, + 112, 136, 136, 136, 136, 168, 112, 8, + 240, 72, 72, 112, 72, 72, 200, 0, + 112, 136, 128, 112, 8, 136, 112, 0, + 248, 168, 32, 32, 32, 32, 112, 0, + 136, 136, 136, 136, 136, 136, 120, 0, + 136, 136, 136, 80, 80, 32, 32, 0, + 136, 136, 136, 136, 168, 216, 136, 0, + 136, 136, 80, 32, 80, 136, 136, 0, + 136, 136, 136, 112, 32, 32, 112, 0, + 248, 136, 16, 32, 64, 136, 248, 0, + 0, 14, 8, 8, 8, 8, 14, 0, + 0, 128, 64, 32, 16, 8, 4, 0, + 0, 112, 16, 16, 16, 16, 112, 0, + 0, 48, 72, 64, 72, 48, 16, 48, + 0, 80, 0, 96, 32, 40, 48, 0, + 32, 16, 0, 152, 144, 144, 232, 0, + 0, 0, 112, 8, 120, 136, 120, 0, + 192, 64, 80, 104, 72, 72, 112, 0, + 0, 0, 112, 136, 128, 136, 112, 0, + 24, 16, 80, 176, 144, 144, 112, 0, + 0, 0, 112, 136, 248, 128, 112, 0, + 48, 72, 64, 224, 64, 64, 224, 0, + 0, 0, 104, 144, 144, 112, 136, 112, + 192, 64, 80, 104, 72, 72, 200, 0, + 64, 0, 192, 64, 64, 64, 224, 0, + 8, 0, 8, 8, 8, 8, 136, 112, + 192, 64, 72, 80, 96, 80, 200, 0, + 192, 64, 64, 64, 64, 64, 224, 0, + 0, 0, 144, 216, 168, 136, 136, 0, + 0, 0, 240, 136, 136, 136, 136, 0, + 0, 0, 112, 136, 136, 136, 112, 0, + 0, 0, 176, 72, 72, 112, 64, 224, + 0, 0, 104, 144, 144, 112, 16, 56, + 0, 0, 176, 72, 72, 64, 224, 0, + 0, 0, 120, 128, 112, 8, 240, 0, + 64, 64, 240, 64, 64, 72, 48, 0, + 0, 0, 144, 144, 144, 144, 104, 0, + 0, 0, 136, 136, 136, 80, 32, 0, + 0, 0, 136, 136, 168, 216, 144, 0, + 0, 0, 136, 80, 32, 80, 136, 0, + 0, 0, 136, 136, 136, 112, 32, 192, + 0, 0, 248, 144, 32, 72, 248, 0, + 32, 80, 0, 96, 144, 144, 96, 0, + 0, 14, 8, 48, 8, 8, 14, 0, + 0, 8, 8, 8, 8, 8, 8, 0, + 0, 112, 16, 12, 16, 16, 112, 0, + 0, 0, 0, 0, 0, 0, 248, 0, + 252, 252, 252, 252, 252, 252, 252, 252, + 240, 240, 240, 240, 240, 240, 240, 240, +}; + +static const byte hebrew_video_font[] = { + 0, 0, 0, 0, 0, 0, 0, 0, + 32, 112, 112, 32, 32, 0, 32, 0, + 48, 48, 96, 0, 0, 0, 0, 0, + 0, 144, 0, 96, 144, 144, 104, 0, + 0, 144, 0, 96, 144, 144, 96, 0, + 0, 144, 0, 144, 144, 144, 96, 0, + 0, 16, 40, 16, 42, 68, 58, 0, + 48, 48, 96, 0, 0, 0, 0, 0, + 0, 4, 8, 8, 8, 8, 4, 0, + 0, 32, 16, 16, 16, 16, 32, 0, + 0, 0, 20, 8, 62, 8, 20, 0, + 0, 112, 136, 240, 136, 136, 240, 0, + 0, 0, 0, 0, 0, 48, 48, 96, + 0, 0, 0, 240, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 48, 48, 0, + 16, 32, 0, 120, 112, 64, 56, 0, + 112, 136, 152, 168, 200, 136, 112, 0, + 32, 96, 32, 32, 32, 32, 112, 0, + 112, 136, 8, 48, 64, 136, 248, 0, + 112, 136, 8, 48, 8, 136, 112, 0, + 16, 48, 80, 144, 248, 16, 56, 0, + 248, 128, 240, 8, 8, 136, 112, 0, + 48, 64, 128, 240, 136, 136, 112, 0, + 248, 136, 8, 16, 32, 32, 32, 0, + 112, 136, 136, 112, 136, 136, 112, 0, + 112, 136, 136, 120, 8, 16, 96, 0, + 0, 0, 48, 48, 0, 48, 48, 0, + 5, 5, 4, 6, 5, 3, 4, 5, + 6, 3, 5, 5, 4, 6, 5, 3, + 4, 6, 5, 6, 6, 6, 5, 5, + 5, 6, 5, 6, 6, 6, 6, 6, + 112, 136, 8, 16, 32, 0, 32, 0, + 0, 0, 144, 80, 160, 144, 144, 0, + 0, 0, 224, 32, 32, 32, 240, 0, + 0, 0, 224, 32, 96, 160, 160, 0, + 0, 0, 248, 16, 16, 16, 16, 0, + 0, 0, 240, 16, 16, 144, 144, 0, + 0, 0, 192, 64, 64, 64, 64, 0, + 0, 0, 224, 64, 32, 64, 64, 0, + 0, 0, 240, 144, 144, 144, 144, 0, + 0, 0, 184, 168, 136, 136, 112, 0, + 0, 0, 192, 64, 0, 0, 0, 0, + 0, 0, 240, 16, 16, 16, 16, 16, + 0, 0, 224, 16, 16, 16, 224, 0, + 128, 128, 224, 32, 32, 32, 192, 0, + 0, 0, 248, 72, 72, 72, 120, 0, + 0, 0, 176, 208, 144, 144, 176, 0, + 0, 0, 192, 64, 64, 64, 64, 64, + 0, 0, 96, 32, 32, 32, 224, 0, + 0, 0, 248, 72, 72, 72, 48, 0, + 0, 0, 80, 80, 80, 80, 224, 0, + 0, 0, 248, 72, 104, 8, 8, 8, + 0, 0, 248, 72, 104, 8, 248, 0, + 0, 0, 216, 72, 48, 16, 16, 16, + 0, 0, 144, 80, 32, 16, 240, 0, + 0, 0, 240, 16, 144, 160, 128, 128, + 0, 0, 240, 16, 16, 16, 16, 0, + 0, 0, 168, 168, 200, 136, 112, 0, + 0, 0, 240, 80, 80, 80, 208, 0, + 0, 14, 8, 8, 8, 8, 14, 0, + 0, 128, 64, 32, 16, 8, 4, 0, + 0, 112, 16, 16, 16, 16, 112, 0, + 0, 48, 72, 64, 72, 48, 16, 48, + 0, 80, 0, 96, 32, 40, 48, 0, + 32, 16, 0, 152, 144, 144, 232, 0, + 0, 0, 112, 8, 120, 136, 120, 0, + 192, 64, 80, 104, 72, 72, 112, 0, + 0, 0, 112, 136, 128, 136, 112, 0, + 24, 16, 80, 176, 144, 144, 112, 0, + 0, 0, 112, 136, 248, 128, 112, 0, + 48, 72, 64, 224, 64, 64, 224, 0, + 0, 0, 104, 144, 144, 112, 136, 112, + 192, 64, 80, 104, 72, 72, 200, 0, + 64, 0, 192, 64, 64, 64, 224, 0, + 8, 0, 8, 8, 8, 8, 136, 112, + 192, 64, 72, 80, 96, 80, 200, 0, + 192, 64, 64, 64, 64, 64, 224, 0, + 0, 0, 144, 216, 168, 136, 136, 0, + 0, 0, 240, 136, 136, 136, 136, 0, + 0, 0, 112, 136, 136, 136, 112, 0, + 0, 0, 176, 72, 72, 112, 64, 224, + 0, 0, 104, 144, 144, 112, 16, 56, + 0, 0, 176, 72, 72, 64, 224, 0, + 0, 0, 120, 128, 112, 8, 240, 0, + 64, 64, 240, 64, 64, 72, 48, 0, + 0, 0, 144, 144, 144, 144, 104, 0, + 0, 0, 136, 136, 136, 80, 32, 0, + 0, 0, 136, 136, 168, 216, 144, 0, + 0, 0, 136, 80, 32, 80, 136, 0, + 0, 0, 136, 136, 136, 112, 32, 192, + 0, 0, 248, 144, 32, 72, 248, 0, + 32, 80, 0, 96, 144, 144, 96, 0, + 0, 14, 8, 48, 8, 8, 14, 0, + 0, 8, 8, 8, 8, 8, 8, 0, + 0, 112, 16, 12, 16, 16, 112, 0, + 0, 0, 0, 0, 0, 0, 248, 0, + 252, 252, 252, 252, 252, 252, 252, 252, + 240, 240, 240, 240, 240, 240, 240, 240, +}; + +static const byte italian_video_font[] = { + 0, 0, 0, 0, 0, 0, 0, 0, + 32, 112, 112, 32, 32, 0, 32, 0, + 48, 48, 96, 0, 0, 0, 0, 0, + 80, 0, 112, 8, 120, 136, 120, 0, + 80, 0, 112, 136, 136, 136, 112, 0, + 32, 16, 0, 112, 136, 136, 112, 0, + 0, 16, 40, 16, 42, 68, 58, 0, + 48, 48, 96, 0, 0, 0, 0, 0, + 0, 4, 8, 8, 8, 8, 4, 0, + 0, 32, 16, 16, 16, 16, 32, 0, + 0, 0, 20, 8, 62, 8, 20, 0, + 32, 16, 0, 192, 64, 64, 224, 0, + 0, 0, 0, 0, 0, 48, 48, 96, + 0, 0, 0, 240, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 48, 48, 0, + 32, 64, 0, 112, 248, 128, 112, 0, + 112, 136, 152, 168, 200, 136, 112, 0, + 32, 96, 32, 32, 32, 32, 112, 0, + 112, 136, 8, 48, 64, 136, 248, 0, + 112, 136, 8, 48, 8, 136, 112, 0, + 16, 48, 80, 144, 248, 16, 56, 0, + 248, 128, 240, 8, 8, 136, 112, 0, + 48, 64, 128, 240, 136, 136, 112, 0, + 248, 136, 8, 16, 32, 32, 32, 0, + 112, 136, 136, 112, 136, 136, 112, 0, + 112, 136, 136, 120, 8, 16, 96, 0, + 0, 0, 48, 48, 0, 48, 48, 0, + 32, 16, 0, 112, 8, 248, 120, 0, + 32, 16, 0, 112, 136, 136, 112, 0, + 32, 16, 0, 112, 248, 128, 112, 0, + 32, 80, 0, 112, 248, 128, 112, 0, + 112, 136, 8, 16, 32, 0, 32, 0, + 32, 80, 0, 96, 32, 40, 48, 0, + 112, 136, 136, 248, 136, 136, 136, 0, + 240, 72, 72, 112, 72, 72, 240, 0, + 48, 72, 128, 128, 128, 72, 48, 0, + 224, 80, 72, 72, 72, 80, 224, 0, + 248, 72, 64, 112, 64, 72, 248, 0, + 248, 72, 64, 112, 64, 64, 224, 0, + 48, 72, 128, 152, 136, 72, 56, 0, + 136, 136, 136, 248, 136, 136, 136, 0, + 248, 32, 32, 32, 32, 32, 248, 0, + 24, 8, 8, 8, 136, 136, 112, 0, + 200, 72, 80, 96, 80, 72, 200, 0, + 224, 64, 64, 64, 64, 72, 248, 0, + 136, 216, 168, 168, 136, 136, 136, 0, + 136, 200, 168, 152, 136, 136, 136, 0, + 112, 136, 136, 136, 136, 136, 112, 0, + 240, 72, 72, 112, 64, 64, 224, 0, + 112, 136, 136, 136, 136, 168, 112, 8, + 240, 72, 72, 112, 72, 72, 200, 0, + 112, 136, 128, 112, 8, 136, 112, 0, + 248, 168, 32, 32, 32, 32, 112, 0, + 136, 136, 136, 136, 136, 136, 120, 0, + 136, 136, 136, 80, 80, 32, 32, 0, + 136, 136, 136, 136, 168, 216, 136, 0, + 136, 136, 80, 32, 80, 136, 136, 0, + 136, 136, 136, 112, 32, 32, 112, 0, + 248, 136, 16, 32, 64, 136, 248, 0, + 0, 14, 8, 8, 8, 8, 14, 0, + 0, 128, 64, 32, 16, 8, 4, 0, + 0, 112, 16, 16, 16, 16, 112, 0, + 0, 0, 112, 136, 128, 112, 32, 96, + 160, 0, 192, 64, 64, 64, 224, 0, + 32, 16, 0, 144, 144, 144, 104, 0, + 0, 0, 112, 8, 120, 136, 120, 0, + 192, 64, 80, 104, 72, 72, 112, 0, + 0, 0, 112, 136, 128, 136, 112, 0, + 24, 16, 80, 176, 144, 144, 112, 0, + 0, 0, 112, 136, 248, 128, 112, 0, + 48, 72, 64, 224, 64, 64, 224, 0, + 0, 0, 104, 144, 144, 112, 136, 112, + 192, 64, 80, 104, 72, 72, 200, 0, + 64, 0, 192, 64, 64, 64, 224, 0, + 8, 0, 8, 8, 8, 8, 136, 112, + 192, 64, 72, 80, 96, 80, 200, 0, + 192, 64, 64, 64, 64, 64, 224, 0, + 0, 0, 144, 216, 168, 136, 136, 0, + 0, 0, 240, 136, 136, 136, 136, 0, + 0, 0, 112, 136, 136, 136, 112, 0, + 0, 0, 176, 72, 72, 112, 64, 224, + 0, 0, 104, 144, 144, 112, 16, 56, + 0, 0, 176, 72, 72, 64, 224, 0, + 0, 0, 120, 128, 112, 8, 240, 0, + 64, 64, 240, 64, 64, 72, 48, 0, + 0, 0, 144, 144, 144, 144, 104, 0, + 0, 0, 136, 136, 136, 80, 32, 0, + 0, 0, 136, 136, 168, 216, 144, 0, + 0, 0, 136, 80, 32, 80, 136, 0, + 0, 0, 136, 136, 136, 112, 32, 192, + 0, 0, 248, 144, 32, 72, 248, 0, + 32, 80, 0, 96, 144, 144, 96, 0, + 0, 14, 8, 48, 8, 8, 14, 0, + 0, 8, 8, 8, 8, 8, 8, 0, + 0, 112, 16, 12, 16, 16, 112, 0, + 0, 0, 0, 0, 0, 0, 248, 0, + 252, 252, 252, 252, 252, 252, 252, 252, + 240, 240, 240, 240, 240, 240, 240, 240, +}; + +static const byte spanish_video_font[] = { + 0, 0, 0, 0, 0, 0, 0, 0, + 32, 112, 112, 32, 32, 0, 32, 0, + 48, 48, 96, 0, 0, 0, 0, 0, + 80, 0, 112, 8, 120, 136, 120, 0, + 80, 0, 112, 136, 136, 136, 112, 0, + 80, 0, 144, 144, 144, 144, 104, 0, + 0, 16, 40, 16, 42, 68, 58, 0, + 48, 48, 96, 0, 0, 0, 0, 0, + 0, 4, 8, 8, 8, 8, 4, 0, + 0, 32, 16, 16, 16, 16, 32, 0, + 0, 0, 20, 8, 62, 8, 20, 0, + 96, 144, 144, 160, 144, 144, 160, 128, + 0, 0, 0, 0, 0, 48, 48, 96, + 0, 0, 0, 240, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 48, 48, 0, + 32, 64, 0, 112, 248, 128, 112, 0, + 112, 136, 152, 168, 200, 136, 112, 0, + 32, 96, 32, 32, 32, 32, 112, 0, + 112, 136, 8, 48, 64, 136, 248, 0, + 112, 136, 8, 48, 8, 136, 112, 0, + 16, 48, 80, 144, 248, 16, 56, 0, + 248, 128, 240, 8, 8, 136, 112, 0, + 48, 64, 128, 240, 136, 136, 112, 0, + 248, 136, 8, 16, 32, 32, 32, 0, + 112, 136, 136, 112, 136, 136, 112, 0, + 112, 136, 136, 120, 8, 16, 96, 0, + 0, 0, 48, 48, 0, 48, 48, 0, + 32, 64, 0, 112, 8, 248, 120, 0, + 32, 64, 0, 192, 64, 64, 224, 0, + 32, 64, 0, 112, 136, 136, 112, 0, + 32, 64, 0, 144, 144, 144, 104, 0, + 112, 136, 8, 16, 32, 0, 32, 0, + 80, 160, 0, 240, 136, 136, 136, 0, + 112, 136, 136, 248, 136, 136, 136, 0, + 240, 72, 72, 112, 72, 72, 240, 0, + 48, 72, 128, 128, 128, 72, 48, 0, + 224, 80, 72, 72, 72, 80, 224, 0, + 248, 72, 64, 112, 64, 72, 248, 0, + 248, 72, 64, 112, 64, 64, 224, 0, + 48, 72, 128, 152, 136, 72, 56, 0, + 136, 136, 136, 248, 136, 136, 136, 0, + 248, 32, 32, 32, 32, 32, 248, 0, + 24, 8, 8, 8, 136, 136, 112, 0, + 200, 72, 80, 96, 80, 72, 200, 0, + 224, 64, 64, 64, 64, 72, 248, 0, + 136, 216, 168, 168, 136, 136, 136, 0, + 136, 200, 168, 152, 136, 136, 136, 0, + 112, 136, 136, 136, 136, 136, 112, 0, + 240, 72, 72, 112, 64, 64, 224, 0, + 112, 136, 136, 136, 136, 168, 112, 8, + 240, 72, 72, 112, 72, 72, 200, 0, + 112, 136, 128, 112, 8, 136, 112, 0, + 248, 168, 32, 32, 32, 32, 112, 0, + 136, 136, 136, 136, 136, 136, 120, 0, + 136, 136, 136, 80, 80, 32, 32, 0, + 136, 136, 136, 136, 168, 216, 136, 0, + 136, 136, 80, 32, 80, 136, 136, 0, + 136, 136, 136, 112, 32, 32, 112, 0, + 248, 136, 16, 32, 64, 136, 248, 0, + 0, 14, 8, 8, 8, 8, 14, 0, + 0, 128, 64, 32, 16, 8, 4, 0, + 0, 112, 16, 16, 16, 16, 112, 0, + 32, 0, 32, 64, 128, 136, 112, 0, + 32, 0, 32, 32, 112, 112, 32, 0, + 80, 0, 144, 144, 144, 144, 104, 0, + 0, 0, 112, 8, 120, 136, 120, 0, + 192, 64, 80, 104, 72, 72, 112, 0, + 0, 0, 112, 136, 128, 136, 112, 0, + 24, 16, 80, 176, 144, 144, 112, 0, + 0, 0, 112, 136, 248, 128, 112, 0, + 48, 72, 64, 224, 64, 64, 224, 0, + 0, 0, 104, 144, 144, 112, 136, 112, + 192, 64, 80, 104, 72, 72, 200, 0, + 64, 0, 192, 64, 64, 64, 224, 0, + 8, 0, 8, 8, 8, 8, 136, 112, + 192, 64, 72, 80, 96, 80, 200, 0, + 192, 64, 64, 64, 64, 64, 224, 0, + 0, 0, 144, 216, 168, 136, 136, 0, + 0, 0, 240, 136, 136, 136, 136, 0, + 0, 0, 112, 136, 136, 136, 112, 0, + 0, 0, 176, 72, 72, 112, 64, 224, + 0, 0, 104, 144, 144, 112, 16, 56, + 0, 0, 176, 72, 72, 64, 224, 0, + 0, 0, 120, 128, 112, 8, 240, 0, + 64, 64, 240, 64, 64, 72, 48, 0, + 0, 0, 144, 144, 144, 144, 104, 0, + 0, 0, 136, 136, 136, 80, 32, 0, + 0, 0, 136, 136, 168, 216, 144, 0, + 0, 0, 136, 80, 32, 80, 136, 0, + 0, 0, 136, 136, 136, 112, 32, 192, + 0, 0, 248, 144, 32, 72, 248, 0, + 32, 80, 0, 96, 144, 144, 96, 0, + 0, 14, 8, 48, 8, 8, 14, 0, + 0, 8, 8, 8, 8, 8, 8, 0, + 0, 112, 16, 12, 16, 16, 112, 0, + 0, 0, 0, 0, 0, 0, 248, 0, + 252, 252, 252, 252, 252, 252, 252, 252, + 240, 240, 240, 240, 240, 240, 240, 240, +}; + +static const byte video_font[] = { + 0, 0, 0, 0, 0, 0, 0, 0, + 32, 112, 112, 32, 32, 0, 32, 0, + 48, 48, 96, 0, 0, 0, 0, 0, + 0, 144, 0, 96, 144, 144, 104, 0, + 0, 144, 0, 96, 144, 144, 96, 0, + 0, 144, 0, 144, 144, 144, 96, 0, + 0, 16, 40, 16, 42, 68, 58, 0, + 48, 48, 96, 0, 0, 0, 0, 0, + 0, 4, 8, 8, 8, 8, 4, 0, + 0, 32, 16, 16, 16, 16, 32, 0, + 0, 0, 20, 8, 62, 8, 20, 0, + 0, 112, 136, 240, 136, 136, 240, 0, + 0, 0, 0, 0, 0, 48, 48, 96, + 0, 0, 0, 240, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 48, 48, 0, + 16, 32, 0, 120, 112, 64, 56, 0, + 112, 136, 152, 168, 200, 136, 112, 0, + 32, 96, 32, 32, 32, 32, 112, 0, + 112, 136, 8, 48, 64, 136, 248, 0, + 112, 136, 8, 48, 8, 136, 112, 0, + 16, 48, 80, 144, 248, 16, 56, 0, + 248, 128, 240, 8, 8, 136, 112, 0, + 48, 64, 128, 240, 136, 136, 112, 0, + 248, 136, 8, 16, 32, 32, 32, 0, + 112, 136, 136, 112, 136, 136, 112, 0, + 112, 136, 136, 120, 8, 16, 96, 0, + 0, 0, 48, 48, 0, 48, 48, 0, + 32, 16, 0, 112, 8, 248, 120, 0, + 32, 80, 0, 144, 144, 144, 104, 0, + 80, 0, 144, 144, 144, 144, 104, 0, + 32, 80, 0, 112, 248, 128, 112, 0, + 112, 136, 8, 16, 32, 0, 32, 0, + 32, 80, 0, 192, 64, 64, 224, 0, + 112, 136, 136, 248, 136, 136, 136, 0, + 240, 72, 72, 112, 72, 72, 240, 0, + 48, 72, 128, 128, 128, 72, 48, 0, + 224, 80, 72, 72, 72, 80, 224, 0, + 248, 72, 64, 112, 64, 72, 248, 0, + 248, 72, 64, 112, 64, 64, 224, 0, + 48, 72, 128, 152, 136, 72, 56, 0, + 136, 136, 136, 248, 136, 136, 136, 0, + 248, 32, 32, 32, 32, 32, 248, 0, + 24, 8, 8, 8, 136, 136, 112, 0, + 200, 72, 80, 96, 80, 72, 200, 0, + 224, 64, 64, 64, 64, 72, 248, 0, + 136, 216, 168, 168, 136, 136, 136, 0, + 136, 200, 168, 152, 136, 136, 136, 0, + 112, 136, 136, 136, 136, 136, 112, 0, + 240, 72, 72, 112, 64, 64, 224, 0, + 112, 136, 136, 136, 136, 168, 112, 8, + 240, 72, 72, 112, 72, 72, 200, 0, + 112, 136, 128, 112, 8, 136, 112, 0, + 248, 168, 32, 32, 32, 32, 112, 0, + 136, 136, 136, 136, 136, 136, 120, 0, + 136, 136, 136, 80, 80, 32, 32, 0, + 136, 136, 136, 136, 168, 216, 136, 0, + 136, 136, 80, 32, 80, 136, 136, 0, + 136, 136, 136, 112, 32, 32, 112, 0, + 248, 136, 16, 32, 64, 136, 248, 0, + 0, 14, 8, 8, 8, 8, 14, 0, + 0, 128, 64, 32, 16, 8, 4, 0, + 0, 112, 16, 16, 16, 16, 112, 0, + 0, 48, 72, 64, 72, 48, 16, 48, + 0, 80, 0, 96, 32, 40, 48, 0, + 32, 16, 0, 152, 144, 144, 232, 0, + 0, 0, 112, 8, 120, 136, 120, 0, + 192, 64, 80, 104, 72, 72, 112, 0, + 0, 0, 112, 136, 128, 136, 112, 0, + 24, 16, 80, 176, 144, 144, 112, 0, + 0, 0, 112, 136, 248, 128, 112, 0, + 48, 72, 64, 224, 64, 64, 224, 0, + 0, 0, 104, 144, 144, 112, 136, 112, + 192, 64, 80, 104, 72, 72, 200, 0, + 64, 0, 192, 64, 64, 64, 224, 0, + 8, 0, 8, 8, 8, 8, 136, 112, + 192, 64, 72, 80, 96, 80, 200, 0, + 192, 64, 64, 64, 64, 64, 224, 0, + 0, 0, 144, 216, 168, 136, 136, 0, + 0, 0, 240, 136, 136, 136, 136, 0, + 0, 0, 112, 136, 136, 136, 112, 0, + 0, 0, 176, 72, 72, 112, 64, 224, + 0, 0, 104, 144, 144, 112, 16, 56, + 0, 0, 176, 72, 72, 64, 224, 0, + 0, 0, 120, 128, 112, 8, 240, 0, + 64, 64, 240, 64, 64, 72, 48, 0, + 0, 0, 144, 144, 144, 144, 104, 0, + 0, 0, 136, 136, 136, 80, 32, 0, + 0, 0, 136, 136, 168, 216, 144, 0, + 0, 0, 136, 80, 32, 80, 136, 0, + 0, 0, 136, 136, 136, 112, 32, 192, + 0, 0, 248, 144, 32, 72, 248, 0, + 32, 80, 0, 96, 144, 144, 96, 0, + 0, 14, 8, 48, 8, 8, 14, 0, + 0, 8, 8, 8, 8, 8, 8, 0, + 0, 112, 16, 12, 16, 16, 112, 0, + 0, 0, 0, 0, 0, 0, 248, 0, + 252, 252, 252, 252, 252, 252, 252, 252, + 240, 240, 240, 240, 240, 240, 240, 240, +}; +#endif + +static const byte feeble_video_font[] = { + 0,0,0,0,0,0,0,0,0,0,0,0,0, + 128,128,128,128,128,128,128,0,0,128,0,0,0, + 144,144,144,0,0,0,0,0,0,0,0,0,0, + 0,72,252,72,72,252,72,0,0,0,0,0,0, + 124,146,144,144,124,18,18,18,146,124,16,16,0, + 62,98,146,100,8,16,32,76,146,140,0,0,0, + 0,112,136,136,80,32,82,138,132,122,0,0,0, + 128,128,128,0,0,0,0,0,0,0,0,0,0, + 32,64,128,128,128,128,128,128,64,32,0,0,0, + 128,64,32,32,32,32,32,32,64,128,0,0,0, + 16,146,84,56,56,84,146,16,0,0,0,0,0, + 0,0,16,16,16,254,16,16,16,0,0,0,0, + 0,0,0,0,0,0,0,0,0,64,64,128,0, + 0,0,0,0,0,254,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,128,0,0,0, + 0,0,1,2,4,8,16,32,64,128,128,0,0, + 120,132,132,132,132,132,132,132,132,120,0,0,0, + 16,48,16,16,16,16,16,16,16,56,0,0,0, + 120,132,132,4,4,8,16,32,64,252,0,0,0, + 124,130,130,2,2,28,2,2,130,124,0,0,0, + 4,12,20,36,68,132,254,4,4,4,0,0,0, + 254,128,128,252,2,2,2,2,130,124,0,0,0, + 62,64,128,252,130,130,130,130,130,124,0,0,0, + 254,2,2,2,2,4,8,16,16,16,0,0,0, + 120,132,132,132,120,132,132,132,132,120,0,0,0, + 124,130,130,130,130,130,126,2,2,124,0,0,0, + 0,0,0,0,128,0,0,0,128,0,0,0,0, + 0,0,0,0,0,64,0,0,0,64,64,128,0, + 0,0,16,32,64,128,64,32,16,0,0,0,0, + 0,0,0,254,0,0,254,0,0,0,0,0,0, + 0,0,128,64,32,16,32,64,128,0,0,0,0, + 60,66,130,2,4,8,16,16,0,16,16,0,0, + 60,66,130,154,166,166,156,128,128,126,0,0,0, + 124,130,130,130,130,254,130,130,130,130,0,0,0, + 252,130,130,130,252,130,130,130,130,252,0,0,0, + 124,130,130,128,128,128,128,130,130,124,0,0,0, + 252,130,130,130,130,130,130,130,130,252,0,0,0, + 254,128,128,128,128,252,128,128,128,254,0,0,0, + 254,128,128,128,128,252,128,128,128,128,0,0,0, + 124,130,130,128,128,134,130,130,130,124,0,0,0, + 130,130,130,130,130,254,130,130,130,130,0,0,0, + 224,64,64,64,64,64,64,64,64,224,0,0,0, + 30,2,2,2,2,2,130,130,130,124,0,0,0, + 130,130,132,136,144,224,144,136,132,130,0,0,0, + 128,128,128,128,128,128,128,128,128,254,0,0,0, + 130,198,170,146,130,130,130,130,130,130,0,0,0, + 130,130,194,162,146,138,134,130,130,130,0,0,0, + 124,130,130,130,130,130,130,130,130,124,0,0,0, + 252,130,130,130,130,252,128,128,128,128,0,0,0, + 124,130,130,130,130,130,130,130,130,126,2,2,0, + 252,130,130,130,130,252,130,130,130,130,0,0,0, + 124,130,128,128,124,2,2,130,130,124,0,0,0, + 254,16,16,16,16,16,16,16,16,16,0,0,0, + 130,130,130,130,130,130,130,130,130,124,0,0,0, + 130,130,130,130,130,130,130,68,40,16,0,0,0, + 130,130,130,130,130,130,130,146,170,198,0,0,0, + 130,68,40,16,16,16,16,40,68,130,0,0,0, + 130,130,130,130,68,40,16,16,16,16,0,0,0, + 254,2,2,4,8,16,32,64,128,254,0,0,0, + 224,128,128,128,128,128,128,128,128,224,0,0,0, + 128,128,64,32,16,8,4,2,1,0,0,0,0, + 224,32,32,32,32,32,32,32,32,224,0,0,0, + 160,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,255,0,0,0, + 32,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,62,66,130,130,130,134,122,0,0,0, + 128,128,128,252,130,130,130,130,130,252,0,0,0, + 0,0,0,124,130,128,128,128,130,124,0,0,0, + 2,2,2,126,130,130,130,130,130,126,0,0,0, + 0,0,0,124,130,130,254,128,128,126,0,0,0, + 28,32,32,248,32,32,32,32,32,32,0,0,0, + 0,0,0,62,66,130,130,130,130,126,2,2,124, + 128,128,128,252,130,130,130,130,130,130,0,0,0, + 64,0,0,64,192,64,64,64,64,224,0,0,0, + 16,0,0,16,16,16,16,16,16,16,16,16,224, + 128,128,128,132,136,144,224,144,136,132,0,0,0, + 192,64,64,64,64,64,64,64,64,64,0,0,0, + 0,0,0,252,146,146,146,146,146,146,0,0,0, + 0,0,0,156,162,194,130,130,130,130,0,0,0, + 0,0,0,124,130,130,130,130,130,124,0,0,0, + 0,0,0,252,130,130,130,130,130,252,128,128,128, + 0,0,0,126,130,130,130,130,130,126,2,2,2, + 0,0,0,156,162,194,128,128,128,128,0,0,0, + 0,0,0,124,128,128,120,4,4,248,0,0,0, + 32,32,32,248,32,32,32,32,32,28,0,0,0, + 0,0,0,130,130,130,130,130,130,126,0,0,0, + 0,0,0,130,130,130,130,68,40,16,0,0,0, + 0,0,0,146,146,146,146,146,146,124,0,0,0, + 0,0,0,130,68,40,16,40,68,130,0,0,0, + 0,0,0,130,130,130,130,130,130,126,2,2,124, + 0,0,0,254,4,8,16,32,64,254,0,0,0, + 48,64,64,64,64,128,64,64,64,64,48,0,0, + 128,128,128,128,128,128,128,128,128,128,128,128,0, + 192,32,32,32,32,16,32,32,32,32,192,0,0, + 152,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,254,0,0, + 124,130,130,128,128,128,128,130,130,124,16,16,48, + 0,40,0,130,130,130,130,130,130,126,0,0,0, + 16,32,0,124,130,130,254,128,128,126,0,0,0, + 16,40,0,62,66,130,130,130,134,122,0,0,0, + 0,40,0,62,66,130,130,130,134,122,0,0,0, + 16,8,0,62,66,130,130,130,134,122,0,0,0, + 8,20,8,62,66,130,130,130,134,122,0,0,0, + 0,0,0,124,130,128,128,128,130,124,16,16,48, + 16,40,0,124,130,130,254,128,128,126,0,0,0, + 0,40,0,124,130,130,254,128,128,126,0,0,0, + 16,8,0,124,130,130,254,128,128,126,0,0,0, + 0,160,0,64,192,64,64,64,64,224,0,0,0, + 64,160,0,64,192,64,64,64,64,224,0,0,0, + 128,64,0,64,192,64,64,64,64,224,0,0,0, + 40,0,124,130,130,130,254,130,130,130,0,0,0, + 16,40,124,130,130,130,254,130,130,130,0,0,0, + 40,0,254,128,128,252,128,128,128,254,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0, + 16,40,0,124,130,130,130,130,130,124,0,0,0, + 0,40,0,124,130,130,130,130,130,124,0,0,0, + 32,16,0,124,130,130,130,130,130,124,0,0,0, + 16,40,0,130,130,130,130,130,130,126,0,0,0, + 16,8,0,130,130,130,130,130,130,126,0,0,0, + 0,40,0,130,130,130,130,130,130,126,2,2,124, + 40,0,124,130,130,130,130,130,130,124,0,0,0, + 40,0,130,130,130,130,130,130,130,124,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0, + 8,16,0,62,66,130,130,130,134,122,0,0,0, + 64,128,0,64,192,64,64,64,64,224,0,0,0, + 16,32,0,124,130,130,130,130,130,124,0,0,0, + 8,16,0,130,130,130,130,130,130,126,0,0,0, + 20,40,0,156,162,194,130,130,130,130,0,0,0, + 20,40,130,194,162,146,138,134,130,130,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0, + 16,16,0,16,16,32,64,128,130,132,120,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0, + 128,0,0,128,128,128,128,128,128,128,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0, + 60,66,130,130,130,132,132,130,130,130,156,128,128, + 0,0,0,0,0,0,0,0,0,0,0,0,0, +}; + +void SimonEngine::video_putchar_drawchar(WindowBlock *window, uint x, uint y, byte chr) { + const byte *src; + byte color, *dst; + uint h, w, i; + + if (_noOracleScroll) + return; + + _lockWord |= 0x8000; + + dst = getFrontBuf() + y * _dxSurfacePitch + x + window->textColumnOffset; + + if (getGameType() == GType_FF || getGameType() == GType_PP) { + h = 13; + w = feebleFontSize[chr - 0x20]; + + src = feeble_video_font + (chr - 0x20) * 13; + } else { + h = 8; + w = 6; + + switch (_language) { + case Common::RU_RUS: + src = russian_video_font + (chr - 0x20) * 8; + break; + case Common::PL_POL: + src = polish_video_font + (chr - 0x20) * 8; + break; + case Common::HB_ISR: + src = hebrew_video_font + (chr - 0x20) * 8; + break; + case Common::ES_ESP: + src = spanish_video_font + (chr - 0x20) * 8; + break; + case Common::IT_ITA: + src = italian_video_font + (chr - 0x20) * 8; + break; + case Common::FR_FRA: + src = french_video_font + (chr - 0x20) * 8; + break; + case Common::DE_DEU: + src = german_video_font + (chr - 0x20) * 8; + break; + case Common::EN_ANY: + src = video_font + (chr - 0x20) * 8; + break; + default: + error("video_putchar_drawchar: Unknown language %d\n", _language); + } + } + + color = window->text_color; + + do { + int8 b = *src++; + i = 0; + do { + if (b < 0) { + if (getGameType() == GType_FF || getGameType() == GType_PP) { + if (dst[i] == 0) + dst[i] = color; + } else { + dst[i] = color; + } + } + + b <<= 1; + } while (++i != w); + dst += _dxSurfacePitch; + } while (--h); + + _lockWord &= ~0x8000; +} + +} // End of namespace Simon + +#ifdef PALMOS_68K +#include "scumm_globals.h" + +_GINIT(AGOS_Charset) +_GSETPTR(Simon::russian_video_font, GBVARS_RUSSIANVIDEOFONT_INDEX, byte, GBVARS_SIMON) +//_GSETPTR(Simon::polish_video_font, GBVARS_POLISHVIDEOFONT_INDEX, byte, GBVARS_SIMON) +_GSETPTR(Simon::french_video_font, GBVARS_FRENCHVIDEOFONT_INDEX, byte, GBVARS_SIMON) +_GSETPTR(Simon::german_video_font, GBVARS_GERMANVIDEOFONT_INDEX, byte, GBVARS_SIMON) +_GSETPTR(Simon::hebrew_video_font, GBVARS_HEBREWVIDEOFONT_INDEX, byte, GBVARS_SIMON) +_GSETPTR(Simon::italian_video_font, GBVARS_ITALIANVIDEOFONT_INDEX, byte, GBVARS_SIMON) +_GSETPTR(Simon::spanish_video_font, GBVARS_SPANISHVIDEOFONT_INDEX, byte, GBVARS_SIMON) +_GSETPTR(Simon::video_font, GBVARS_VIDEOFONT_INDEX, byte, GBVARS_SIMON) +_GEND + +_GRELEASE(AGOS_Charset) +_GRELEASEPTR(GBVARS_RUSSIANVIDEOFONT_INDEX, GBVARS_SIMON) +//_GRELEASEPTR(GBVARS_POLISHVIDEOFONT_INDEX, GBVARS_SIMON) +_GRELEASEPTR(GBVARS_FRENCHVIDEOFONT_INDEX, GBVARS_SIMON) +_GRELEASEPTR(GBVARS_GERMANVIDEOFONT_INDEX, GBVARS_SIMON) +_GRELEASEPTR(GBVARS_HEBREWVIDEOFONT_INDEX, GBVARS_SIMON) +_GRELEASEPTR(GBVARS_ITALIANVIDEOFONT_INDEX, GBVARS_SIMON) +_GRELEASEPTR(GBVARS_SPANISHVIDEOFONT_INDEX, GBVARS_SIMON) +_GRELEASEPTR(GBVARS_VIDEOFONT_INDEX, GBVARS_SIMON) +_GEND + +#endif diff --git a/engines/agos/cursor.cpp b/engines/agos/cursor.cpp new file mode 100644 index 0000000000..fe342f635d --- /dev/null +++ b/engines/agos/cursor.cpp @@ -0,0 +1,495 @@ +/* ScummVM - Scumm Interpreter + * Copyright (C) 2001 Ludvig Strigeus + * Copyright (C) 2001-2006 The ScummVM project + * + * 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. + * + * $URL$ + * $Id$ + * + */ + +#include "common/stdafx.h" + +#include "common/system.h" + +#include "graphics/cursorman.h" + +#include "agos/agos.h" + +namespace Simon { + +#ifdef PALMOS_68K +static const byte *_simon1_cursor; +#else +static const byte _simon1_cursor[256] = { + 0xe1,0xe0,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, + 0xe1,0xe1,0xe0,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, + 0xe1,0xe1,0xe1,0xe0,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, + 0xe1,0xe1,0xe1,0xe1,0xe0,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, + 0xe1,0xe1,0xe1,0xe1,0xe1,0xe0,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, + 0xe1,0xe1,0xe1,0xe1,0xe1,0xe1,0xe0,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, + 0xe1,0xe1,0xe1,0xe1,0xe0,0xe0,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, + 0xe1,0xff,0xff,0xe1,0xe0,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, + 0xff,0xff,0xff,0xff,0xe1,0xe0,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, + 0xff,0xff,0xff,0xff,0xe1,0xe0,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, + 0xff,0xff,0xff,0xff,0xff,0xe1,0xe0,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, + 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, + 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, + 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, + 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, + 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, +}; +#endif +static const byte _simon2_cursors[10][256] = { + // cross hair + { 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xec,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, + 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xec,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, + 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xec,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, + 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xec,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, + 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xec,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, + 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xef,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, + 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, + 0xec,0xec,0xec,0xec,0xec,0xef,0xff,0xea,0xff,0xef,0xec,0xec,0xec,0xec,0xec,0xff, + 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, + 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xef,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, + 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xec,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, + 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xec,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, + 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xec,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, + 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xec,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, + 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xec,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, + 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff }, + // examine + { 0xff,0xff,0xef,0xef,0xef,0xef,0xef,0xef,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, + 0xff,0xef,0xee,0xeb,0xe4,0xe4,0xe4,0xee,0xef,0xff,0xff,0xff,0xff,0xff,0xff,0xff, + 0xef,0xee,0xeb,0xee,0xef,0xef,0xee,0xec,0xee,0xef,0xff,0xff,0xff,0xff,0xff,0xff, + 0xef,0xeb,0xee,0xef,0xee,0xee,0xef,0xee,0xe4,0xef,0xff,0xff,0xff,0xff,0xff,0xff, + 0xef,0xeb,0xef,0xef,0xef,0xec,0xee,0xef,0xe4,0xef,0xff,0xff,0xff,0xff,0xff,0xff, + 0xef,0xeb,0xef,0xef,0xee,0xef,0xef,0xef,0xe4,0xef,0xff,0xff,0xff,0xff,0xff,0xff, + 0xef,0xeb,0xee,0xef,0xef,0xef,0xef,0xee,0xe4,0xef,0xff,0xff,0xff,0xff,0xff,0xff, + 0xef,0xee,0xeb,0xee,0xef,0xef,0xee,0xe4,0xee,0xef,0xff,0xff,0xff,0xff,0xff,0xff, + 0xff,0xef,0xee,0xeb,0xeb,0xeb,0xeb,0xee,0xe4,0xec,0xef,0xff,0xff,0xff,0xff,0xff, + 0xff,0xff,0xef,0xef,0xef,0xef,0xef,0xef,0xeb,0xe4,0xee,0xef,0xff,0xff,0xff,0xff, + 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xef,0xee,0xe4,0xeb,0xef,0xff,0xff,0xff, + 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xef,0xeb,0xe4,0xeb,0xef,0xff,0xff, + 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xef,0xeb,0xec,0xeb,0xef,0xff, + 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xef,0xeb,0xe4,0xef,0xff, + 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xef,0xef,0xff,0xff, + 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff }, + // pick up + { 0xff,0xff,0xff,0xff,0xff,0xe5,0xe5,0xe5,0xe5,0xe5,0xe5,0xff,0xff,0xff,0xff,0xff, + 0xff,0xff,0xff,0xff,0xe5,0xe6,0xe6,0xe7,0xe7,0xe6,0xe6,0xe5,0xff,0xff,0xff,0xff, + 0xff,0xff,0xff,0xe5,0xe7,0xe7,0xe7,0xe7,0xe8,0xe8,0xe8,0xe8,0xe5,0xff,0xff,0xff, + 0xff,0xff,0xe5,0xe6,0xe7,0xe7,0xe7,0xe7,0xe7,0xe7,0xe8,0xe9,0xe7,0xe5,0xff,0xff, + 0xff,0xe5,0xe6,0xe7,0xe6,0xe5,0xff,0xff,0xff,0xff,0xe5,0xe6,0xe8,0xe6,0xe5,0xff, + 0xff,0xe5,0xe7,0xe7,0xe5,0xff,0xff,0xff,0xff,0xff,0xff,0xe5,0xe8,0xe7,0xe5,0xff, + 0xff,0xe5,0xe7,0xe7,0xe5,0xff,0xff,0xff,0xff,0xff,0xff,0xe5,0xe7,0xe7,0xe5,0xff, + 0xff,0xef,0xeb,0xeb,0xef,0xff,0xff,0xff,0xff,0xff,0xff,0xef,0xeb,0xeb,0xef,0xff, + 0xff,0xef,0xee,0xeb,0xee,0xef,0xff,0xff,0xff,0xff,0xef,0xee,0xeb,0xee,0xef,0xff, + 0xff,0xff,0xef,0xeb,0xeb,0xef,0xff,0xff,0xff,0xff,0xef,0xeb,0xeb,0xef,0xff,0xff, + 0xff,0xff,0xef,0xee,0xe4,0xee,0xef,0xff,0xff,0xef,0xee,0xe4,0xee,0xef,0xff,0xff, + 0xff,0xff,0xff,0xef,0xe4,0xeb,0xef,0xff,0xff,0xef,0xeb,0xe4,0xef,0xff,0xff,0xff, + 0xff,0xff,0xff,0xff,0xeb,0xeb,0xeb,0xef,0xef,0xeb,0xeb,0xeb,0xff,0xff,0xff,0xff, + 0xff,0xff,0xff,0xff,0xef,0xee,0xee,0xee,0xee,0xe1,0xe1,0xef,0xff,0xff,0xff,0xe4, + 0xef,0xee,0xeb,0xeb,0xeb,0xeb,0xeb,0xe4,0xe4,0xe4,0xe4,0xe4,0xe4,0xe4,0xeb,0xec, + 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xe4 }, + // give + { 0xff,0xff,0xff,0xff,0xff,0xe5,0xe7,0xe5,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, + 0xff,0xff,0xff,0xff,0xe5,0xe7,0xe8,0xe7,0xe5,0xff,0xff,0xff,0xff,0xff,0xff,0xff, + 0xff,0xff,0xff,0xe9,0xe7,0xe8,0xe8,0xe8,0xe7,0xe9,0xff,0xff,0xff,0xff,0xff,0xff, + 0xff,0xff,0xe5,0xe7,0xea,0xe8,0xe8,0xe8,0xea,0xe7,0xe5,0xff,0xff,0xff,0xff,0xff, + 0xff,0xe5,0xe7,0xe8,0xe8,0xea,0xe9,0xea,0xe8,0xe8,0xe7,0xe5,0xff,0xff,0xff,0xff, + 0xe5,0xe7,0xe9,0xe8,0xe8,0xe9,0xec,0xe9,0xe8,0xe8,0xe8,0xe7,0xe5,0xff,0xff,0xff, + 0xe5,0xe7,0xe7,0xe9,0xe8,0xec,0xe9,0xec,0xe8,0xe9,0xe7,0xe6,0xe5,0xff,0xff,0xff, + 0xe5,0xe7,0xe7,0xe8,0xec,0xe9,0xe9,0xe9,0xec,0xe7,0xe6,0xe6,0xe5,0xff,0xff,0xff, + 0xe5,0xe7,0xe7,0xea,0xe8,0xe9,0xe9,0xe9,0xe7,0xec,0xec,0xe4,0xe5,0xff,0xff,0xff, + 0xe5,0xe7,0xe7,0xe9,0xe7,0xe8,0xe9,0xe7,0xe6,0xec,0xe4,0xec,0xe4,0xef,0xff,0xff, + 0xe5,0xe6,0xe7,0xe9,0xe7,0xe7,0xe8,0xe6,0xe6,0xe4,0xec,0xe4,0xec,0xe4,0xef,0xff, + 0xff,0xe5,0xe6,0xe9,0xe7,0xe7,0xe8,0xe6,0xe6,0xe8,0xe4,0xec,0xe4,0xec,0xeb,0xff, + 0xff,0xff,0xe5,0xe9,0xe7,0xe7,0xe8,0xe6,0xe6,0xe8,0xe6,0xe4,0xec,0xeb,0xef,0xff, + 0xff,0xff,0xff,0xe8,0xe7,0xe7,0xe8,0xe6,0xe6,0xe7,0xff,0xef,0xeb,0xef,0xff,0xff, + 0xff,0xff,0xff,0xff,0xe5,0xe7,0xe8,0xe6,0xe5,0xff,0xff,0xff,0xff,0xff,0xff,0xff, + 0xff,0xff,0xff,0xff,0xff,0xe5,0xe6,0xe5,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff }, + // talk + { 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, + 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, + 0xff,0xff,0xff,0xe5,0xe5,0xe5,0xe5,0xe5,0xe5,0xff,0xff,0xff,0xff,0xff,0xff,0xff, + 0xff,0xff,0xe5,0xe7,0xe8,0xe8,0xe8,0xe7,0xe6,0xe5,0xe5,0xe5,0xff,0xff,0xff,0xff, + 0xff,0xe5,0xe6,0xe9,0xea,0xe6,0xea,0xe9,0xe8,0xe9,0xe8,0xe7,0xe5,0xff,0xff,0xff, + 0xff,0xe5,0xe7,0xe5,0xef,0xe5,0xec,0xea,0xe5,0xea,0xec,0xe5,0xe9,0xe6,0xff,0xff, + 0xff,0xe5,0xe6,0xe5,0xef,0xef,0xef,0xe5,0xef,0xef,0xe5,0xef,0xef,0xe8,0xe5,0xff, + 0xff,0xe5,0xe9,0xea,0xe5,0xe8,0xe7,0xe6,0xe6,0xe8,0xe7,0xe5,0xec,0xe9,0xe5,0xff, + 0xff,0xe5,0xe9,0xe8,0xe5,0xe7,0xe8,0xe8,0xe9,0xe9,0xe8,0xe5,0xe9,0xe9,0xe5,0xff, + 0xff,0xe5,0xe6,0xec,0xea,0xe5,0xe6,0xe6,0xe7,0xe7,0xe6,0xe5,0xec,0xe8,0xe5,0xff, + 0xff,0xff,0xe5,0xe9,0xe8,0xe9,0xe5,0xe8,0xe5,0xe8,0xe5,0xe9,0xe9,0xe7,0xe5,0xff, + 0xff,0xff,0xe5,0xe7,0xe9,0xec,0xe8,0xec,0xe8,0xec,0xe8,0xec,0xe8,0xe5,0xff,0xff, + 0xff,0xff,0xff,0xe5,0xe6,0xe8,0xe9,0xe9,0xe9,0xe9,0xe9,0xe8,0xe5,0xff,0xff,0xff, + 0xff,0xff,0xff,0xff,0xe5,0xe5,0xe5,0xe5,0xe5,0xe5,0xe5,0xe5,0xff,0xff,0xff,0xff, + 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, + 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff }, + // use + { 0xff,0xff,0xff,0xff,0xff,0xee,0xe1,0xeb,0xee,0xef,0xef,0xff,0xff,0xff,0xff,0xff, + 0xff,0xff,0xff,0xff,0xff,0xef,0xef,0xef,0xe4,0xeb,0xee,0xe5,0xff,0xff,0xff,0xff, + 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xeb,0xe4,0xe4,0xeb,0xe5,0xff,0xff,0xff, + 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xeb,0xe4,0xec,0xe4,0xef,0xff,0xff,0xff, + 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xeb,0xeb,0xe4,0xe4,0xee,0xef,0xff,0xff, + 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xee,0xeb,0xeb,0xeb,0xe1,0xef,0xee,0xef, + 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xe5,0xe6,0xef,0xef,0xee,0xeb,0xeb,0xe4,0xee, + 0xff,0xff,0xff,0xff,0xff,0xff,0xe5,0xe6,0xff,0xff,0xff,0xef,0xeb,0xec,0xeb,0xef, + 0xff,0xff,0xff,0xff,0xff,0xe5,0xe6,0xe5,0xff,0xff,0xff,0xee,0xe4,0xeb,0xef,0xff, + 0xff,0xff,0xff,0xe5,0xe5,0xe6,0xe5,0xff,0xff,0xff,0xff,0xef,0xee,0xef,0xff,0xff, + 0xff,0xff,0xe5,0xe6,0xe8,0xe5,0xff,0xff,0xff,0xff,0xff,0xff,0xef,0xff,0xff,0xff, + 0xff,0xe5,0xe6,0xe8,0xe6,0xe5,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, + 0xe5,0xe6,0xe8,0xe6,0xe5,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, + 0xe5,0xe6,0xe6,0xe5,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, + 0xff,0xe5,0xe5,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, + 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff }, + // wear + { 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, + 0xff,0xff,0xff,0xef,0xef,0xef,0xef,0xef,0xef,0xef,0xef,0xef,0xef,0xff,0xff,0xff, + 0xff,0xff,0xff,0xef,0xeb,0xed,0xe4,0xe2,0xeb,0xee,0xee,0xee,0xef,0xff,0xff,0xff, + 0xff,0xff,0xff,0xef,0xe2,0xec,0xe2,0xe1,0xee,0xef,0xef,0xee,0xef,0xff,0xff,0xff, + 0xff,0xff,0xff,0xef,0xeb,0xed,0xeb,0xee,0xef,0xef,0xef,0xee,0xef,0xff,0xff,0xff, + 0xff,0xff,0xff,0xef,0xee,0xe4,0xeb,0xee,0xef,0xef,0xee,0xef,0xef,0xff,0xff,0xff, + 0xff,0xff,0xff,0xff,0xef,0xe4,0xeb,0xee,0xef,0xef,0xee,0xef,0xff,0xff,0xff,0xff, + 0xff,0xff,0xff,0xff,0xef,0xe2,0xeb,0xee,0xef,0xef,0xee,0xef,0xff,0xff,0xff,0xff, + 0xff,0xff,0xff,0xff,0xef,0xeb,0xe1,0xee,0xef,0xef,0xee,0xef,0xff,0xff,0xff,0xff, + 0xff,0xff,0xff,0xff,0xef,0xeb,0xe1,0xee,0xef,0xef,0xef,0xef,0xff,0xff,0xff,0xff, + 0xff,0xef,0xef,0xef,0xe1,0xe4,0xe4,0xe4,0xe1,0xeb,0xee,0xef,0xef,0xef,0xff,0xff, + 0xef,0xee,0xee,0xef,0xee,0xee,0xee,0xee,0xee,0xef,0xef,0xef,0xee,0xee,0xef,0xff, + 0xff,0xef,0xef,0xee,0xe1,0xe2,0xe4,0xe4,0xe4,0xeb,0xe1,0xee,0xef,0xef,0xff,0xff, + 0xff,0xff,0xff,0xef,0xef,0xef,0xef,0xef,0xef,0xef,0xef,0xef,0xff,0xff,0xff,0xff, + 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, + 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff }, + // move + { 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, + 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, + 0xff,0xe1,0xe1,0xe1,0xe1,0xe1,0xe1,0xe1,0xe1,0xe1,0xe1,0xe1,0xe1,0xe1,0xe1,0xff, + 0xff,0xe1,0xe2,0xe2,0xe2,0xe2,0xe2,0xe2,0xe2,0xe2,0xe2,0xe2,0xe2,0xe2,0xe1,0xff, + 0xff,0xe1,0xe3,0xe3,0xe3,0xed,0xe3,0xe3,0xe3,0xe3,0xed,0xe3,0xe3,0xe3,0xe1,0xff, + 0xff,0xe1,0xe3,0xe3,0xed,0xec,0xe3,0xe3,0xe3,0xe3,0xec,0xed,0xe3,0xe3,0xe1,0xff, + 0xff,0xe1,0xe3,0xed,0xec,0xec,0xec,0xec,0xec,0xec,0xec,0xec,0xed,0xe3,0xe1,0xff, + 0xff,0xe1,0xed,0xec,0xec,0xec,0xec,0xec,0xec,0xec,0xec,0xec,0xec,0xed,0xe1,0xff, + 0xff,0xe1,0xe3,0xed,0xec,0xec,0xec,0xec,0xec,0xec,0xec,0xec,0xed,0xe3,0xe1,0xff, + 0xff,0xe1,0xe3,0xe3,0xed,0xec,0xe3,0xe3,0xe3,0xe3,0xec,0xed,0xe3,0xe3,0xe1,0xff, + 0xff,0xe1,0xe3,0xe3,0xe3,0xed,0xe3,0xe3,0xe3,0xe3,0xed,0xe3,0xe3,0xe3,0xe1,0xff, + 0xff,0xe1,0xe2,0xe2,0xe2,0xe2,0xe2,0xe2,0xe2,0xe2,0xe2,0xe2,0xe2,0xe2,0xe1,0xff, + 0xff,0xe1,0xe1,0xe1,0xe1,0xe1,0xe1,0xe1,0xe1,0xe1,0xe1,0xe1,0xe1,0xe1,0xe1,0xff, + 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, + 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, + 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff }, + // open + { 0xff,0xff,0xe5,0xe8,0xe8,0xe7,0xe5,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, + 0xff,0xff,0xe5,0xe8,0xe7,0xe5,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, + 0xff,0xff,0xe5,0xe7,0xe5,0xe7,0xe5,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, + 0xff,0xff,0xff,0xe5,0xff,0xe5,0xe7,0xe6,0xe9,0xff,0xff,0xff,0xff,0xff,0xff,0xff, + 0xff,0xff,0xff,0xff,0xff,0xff,0xe6,0xea,0xe6,0xff,0xff,0xff,0xff,0xff,0xff,0xff, + 0xff,0xff,0xff,0xff,0xff,0xe6,0xea,0xe6,0xe7,0xe5,0xff,0xe5,0xff,0xff,0xff,0xff, + 0xff,0xff,0xff,0xff,0xe6,0xea,0xe6,0xff,0xe5,0xe7,0xe5,0xe7,0xe5,0xff,0xff,0xff, + 0xff,0xff,0xff,0xe6,0xea,0xe6,0xff,0xff,0xff,0xe5,0xe7,0xe8,0xe5,0xff,0xff,0xff, + 0xff,0xe5,0xe6,0xea,0xe6,0xff,0xff,0xff,0xe5,0xe7,0xe8,0xe8,0xe5,0xff,0xff,0xff, + 0xff,0xe5,0xe9,0xea,0xea,0xea,0xea,0xea,0xea,0xea,0xea,0xe5,0xff,0xff,0xff,0xff, + 0xff,0xe5,0xe9,0xe7,0xe7,0xe7,0xe7,0xe7,0xe7,0xe7,0xea,0xe5,0xff,0xff,0xff,0xff, + 0xff,0xe5,0xe9,0xe5,0xe5,0xe5,0xe5,0xe5,0xe5,0xe5,0xe9,0xe5,0xff,0xff,0xff,0xff, + 0xff,0xe5,0xe9,0xe8,0xe8,0xe8,0xe8,0xe8,0xe7,0xe7,0xe9,0xe5,0xff,0xff,0xff,0xff, + 0xff,0xe5,0xe9,0xe6,0xe6,0xe6,0xe6,0xe6,0xe6,0xe5,0xe9,0xe5,0xff,0xff,0xff,0xff, + 0xff,0xe5,0xe9,0xe8,0xe8,0xe8,0xe8,0xe8,0xe8,0xe7,0xe9,0xe5,0xff,0xff,0xff,0xff, + 0xff,0xe5,0xe9,0xe9,0xe9,0xe9,0xe9,0xe9,0xe9,0xe9,0xe9,0xe5,0xff,0xff,0xff,0xff }, + // question mark + { 0xff,0xff,0xff,0xff,0xff,0xe5,0xe5,0xe5,0xe5,0xe5,0xe5,0xff,0xff,0xff,0xff,0xff, + 0xff,0xff,0xff,0xff,0xe5,0xe7,0xea,0xec,0xec,0xec,0xe9,0xe5,0xff,0xff,0xff,0xff, + 0xff,0xff,0xff,0xe5,0xe7,0xea,0xec,0xea,0xe9,0xea,0xec,0xe9,0xe5,0xff,0xff,0xff, + 0xff,0xff,0xff,0xe5,0xe9,0xec,0xe9,0xe8,0xe7,0xe8,0xea,0xec,0xe5,0xff,0xff,0xff, + 0xff,0xff,0xff,0xe5,0xe8,0xe9,0xe8,0xe5,0xe5,0xe8,0xe9,0xec,0xe5,0xff,0xff,0xff, + 0xff,0xff,0xff,0xff,0xe5,0xe5,0xe5,0xe5,0xe8,0xe9,0xec,0xe9,0xe5,0xff,0xff,0xff, + 0xff,0xff,0xff,0xff,0xff,0xff,0xe5,0xe9,0xec,0xec,0xe9,0xe5,0xff,0xff,0xff,0xff, + 0xff,0xff,0xff,0xff,0xff,0xe5,0xe8,0xec,0xea,0xe8,0xe5,0xff,0xff,0xff,0xff,0xff, + 0xff,0xff,0xff,0xff,0xff,0xe5,0xe9,0xec,0xe9,0xe5,0xff,0xff,0xff,0xff,0xff,0xff, + 0xff,0xff,0xff,0xff,0xff,0xe5,0xe9,0xea,0xe9,0xe5,0xff,0xff,0xff,0xff,0xff,0xff, + 0xff,0xff,0xff,0xff,0xff,0xe5,0xe7,0xe9,0xe7,0xe5,0xff,0xff,0xff,0xff,0xff,0xff, + 0xff,0xff,0xff,0xff,0xff,0xff,0xe5,0xe5,0xe5,0xff,0xff,0xff,0xff,0xff,0xff,0xff, + 0xff,0xff,0xff,0xff,0xff,0xe5,0xe8,0xe9,0xe8,0xe5,0xff,0xff,0xff,0xff,0xff,0xff, + 0xff,0xff,0xff,0xff,0xff,0xe5,0xe9,0xec,0xe9,0xe5,0xff,0xff,0xff,0xff,0xff,0xff, + 0xff,0xff,0xff,0xff,0xff,0xe5,0xe8,0xe9,0xe8,0xe5,0xff,0xff,0xff,0xff,0xff,0xff, + 0xff,0xff,0xff,0xff,0xff,0xff,0xe5,0xe5,0xe5,0xff,0xff,0xff,0xff,0xff,0xff,0xff }, +}; + +void SimonEngine::drawMousePointer() { + if (getGameType() == GType_SIMON2) { + CursorMan.replaceCursor(_simon2_cursors[_mouseCursor], 16, 16, 7, 7); + } else { + CursorMan.replaceCursor(_simon1_cursor, 16, 16, 0, 0); + } +} + +void SimonEngine::handleMouseMoved() { + uint x; + + if (_mouseHideCount) { + CursorMan.showMouse(false); + return; + } + + CursorMan.showMouse(true); + pollMouseXY(); + + if (_mouseX <= 0) + _mouseX = 0; + if (_mouseX >= _screenWidth - 1) + _mouseX = _screenWidth - 1; + + if (_mouseY <= 0) + _mouseY = 0; + if (_mouseY >= _screenHeight - 1) + _mouseY = _screenHeight - 1; + + if (_defaultVerb) { + uint id = 101; + if (_mouseY >= 136) + id = 102; + if (_defaultVerb != id) + resetVerbs(); + } + + if (getGameType() == GType_FF) { + if (getBitFlag(99)) { // Oracle + if (_mouseX >= 10 && _mouseX <= 635 && _mouseY >= 5 && _mouseY <= 475) { + setBitFlag(98, true); + } else { + if (getBitFlag(98)) { + _variableArray[254] = 63; + } + } + } else if (getBitFlag(88)) { // Close Up + if (_mouseX >= 10 && _mouseX <= 635 && _mouseY >= 5 && _mouseY <= 475) { + setBitFlag(87, true); + } else { + if (getBitFlag(87)) { + _variableArray[254] = 75; + } + } + } + + if (_rightButtonDown) { + _rightButtonDown = 0; + setVerb(NULL); + } + } + if (getGameType() == GType_SIMON2) { + if (getBitFlag(79)) { + if (!_vgaVar9) { + if (_mouseX >= 315 || _mouseX < 9) + goto get_out2; + _vgaVar9 = 1; + } + if (_scrollCount == 0) { + if (_mouseX >= 315) { + if (_scrollX != _scrollXMax) + _scrollFlag = 1; + } else if (_mouseX < 8) { + if (_scrollX != 0) + _scrollFlag = -1; + } + } + } else { + get_out2:; + _vgaVar9 = 0; + } + } + + if (_mouseX != _mouseXOld || _mouseY != _mouseYOld) + _needHitAreaRecalc++; + + x = 0; + if (_lastHitArea3 == 0 && _leftButtonDown != 0) { + _leftButtonDown = 0; + x = 1; + } else { + if (_hitarea_unk_3 == 0 && _needHitAreaRecalc == 0) + goto get_out; + } + + boxController(_mouseX, _mouseY, x); + _lastHitArea3 = _lastHitArea; + if (x == 1 && _lastHitArea == NULL) + _lastHitArea3 = (HitArea *) -1; + +get_out: + if (getGameType() == GType_FF) + drawMousePointer_FF(); + else + drawMousePointer(); + + _needHitAreaRecalc = 0; +} + +void SimonEngine::mouseOff() { + _mouseHideCount++; +} + +void SimonEngine::mouseOn() { + _lockWord |= 1; + + if (_mouseHideCount != 0) + _mouseHideCount--; + + _lockWord &= ~1; +} + +void SimonEngine::pollMouseXY() { + _mouseX = _sdlMouseX; + _mouseY = _sdlMouseY; +} + +// Feeble Files specific +static const byte _mouseOffs[29 * 32] = { + 6,0,15,21,16,21,14,21,15,21,16,21,16,21,16,21,15,21,15,21,15,21,14,21,12,21,12,21,12,21,12,21, + 6,2,10,12,9,12,8,11,7,10,6,9,4,8,3,7,1,7,0,6,3,7,4,8,6,9,7,10,8,11,9,12, + 0,0,0,0,0,0,0,0,0,1,0,3,0,3,0,4,1,4,1,3,2,3,2,2,1,3,0,4,0,3,0,0, + + 0,0,5,16,4,19,2,21,1,21,1,21,1,21,1,18,3,9,6,2,6,0,3,6,4,12,4,13,4,13,4,14, + 0,0,6,13,5,15,4,16,3,19,2,19,2,19,2,18,1,16,4,10,7,3,7,0,4,2,4,6,0,0,0,0, + + 0,0,7,0,7,1,8,1,11,1,13,1,9,1,6,1,6,0,6,0,6,0,7,0,11,0,13,0,9,0,7,0, + + 0,0,7,6,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + +// SAM icons + 0,0,2,3,2,3,2,3,2,3,2,3,2,3,2,3,2,3,2,3,2,3,2,3,2,3,2,3,2,3,2,3, + 0,0,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2, + 0,0,5,5,5,5,5,5,5,5,5,4,1,1,2,2,3,3,5,5,7,6,9,8,11,10,14,13,16,16,0,0, + 0,0,4,3,5,2,4,2,4,3,5,3,5,2,4,2,4,3,5,3,5,2,4,3,4,3,5,3,5,2,4,2, + +// Asteroid Map icons + 0,0,3,0,4,1,5,0,4,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,3,0,4,1,5,0,4,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,8,0,7,0,8,0,9,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +// Other icons + 0,0,9,9,9,10,8,11,7,11,7,11,8,11,9,10,9,9,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,10,7,10,6,10,5,10,4,10,3,10,4,10,5,10,6,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,7,3,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,7,3,4,3,2,4,0,5,0,7,0,7,0,5,2,4,4,3,7,3,0,0,0,0,0,0,0,0,0,0, + 0,0,12,15,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +// Vent icons + 0,0,8,3,7,3,6,3,5,3,4,3,3,3,2,3,1,3,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,2,3,3,3,4,3,8,3,10,3,12,3,14,3,17,3,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,3,14,4,12,5,10,6,9,7,8,7,7,8,6,9,5,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,2,3,3,2,3,4,3,4,3,5,3,4,3,3,3,3,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + + 0,0,7,6,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,9,9,9,10,8,11,7,11,7,11,8,11,9,10,9,9,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,10,7,10,6,10,5,10,4,10,3,10,4,10,5,10,6,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +}; + +void SimonEngine::drawMousePointer_FF() { + uint cursor; + int image, offs; + + if (_animatePointer != 0) { + if (getBitFlag(99)) { + _mouseToggle ^= 1; + if (_mouseToggle != 0) + _mouseAnim++; + } else { + _mouseAnim++; + } + if (_mouseAnim == _mouseAnimMax) + _mouseAnim = 1; + } + + cursor = _mouseCursor; + + if (_animatePointer == 0 && getBitFlag(99)) { + _mouseAnim = 1; + cursor = 6; + } else if (_mouseCursor != 5 && getBitFlag(72)) { + cursor += 7; + } + + if (cursor != _currentMouseCursor || _mouseAnim != _currentMouseAnim) { + _currentMouseCursor = cursor; + _currentMouseAnim = _mouseAnim; + + memset(_mouseData, 0, sizeof(_mouseData)); + + image = cursor * 16 + 1; + offs = cursor * 32; + drawMousePart(image, _mouseOffs[offs], _mouseOffs[offs + 1]); + + image = cursor * 16 + 1 + _mouseAnim; + offs = cursor * 32 + _mouseAnim * 2; + drawMousePart(image, _mouseOffs[offs], _mouseOffs[offs + 1]); + + int hotspotX = 19; + int hotspotY = 19; + + if (_mouseCursor == 14) { + // Finger pointing away from screen. Not sure where + // this is used. + hotspotX += 4; + hotspotY -= 6; + } else if (_mouseCursor == 15) { + // Finger pointing down. Used for the oh-so-annoying + // Cygnus Alpha tile puzzle. + hotspotY += 18; + } + + CursorMan.replaceCursor(_mouseData, kMaxCursorWidth, kMaxCursorHeight, hotspotX, hotspotY, 0); + } +} + +void SimonEngine::drawMousePart(int image, byte x, byte y) { + VgaPointersEntry *vpe = &_vgaBufferPointers[7]; + byte *src; + int width, height; + + byte *dst = _mouseData + y * kMaxCursorWidth + x; + + src = vpe->vgaFile2 + image * 8; + width = READ_LE_UINT16(src + 6); + height = READ_LE_UINT16(src + 4); + + src = vpe->vgaFile2 + READ_LE_UINT32(src); + + assert(width + x <= kMaxCursorWidth); + assert(height + y <= kMaxCursorWidth); + + for (int h = 0; h < height; h++) { + for (int w = 0; w < width; w++) { + if (src[w] != 0) + dst[w] = src[w]; + } + src += width; + dst += kMaxCursorWidth; + } +} + +} // End of namespace Simon + +#ifdef PALMOS_68K +#include "scumm_globals.h" + +_GINIT(AGOS_Cursor) +_GSETPTR(Simon::_simon1_cursor, GBVARS_SIMON1CURSOR_INDEX, byte, GBVARS_SIMON) +_GEND + +_GRELEASE(AGOS_Cursor) +_GRELEASEPTR(GBVARS_SIMON1CURSOR_INDEX, GBVARS_SIMON) +_GEND + +#endif diff --git a/engines/agos/debug.cpp b/engines/agos/debug.cpp new file mode 100644 index 0000000000..a0e980f807 --- /dev/null +++ b/engines/agos/debug.cpp @@ -0,0 +1,473 @@ +/* ScummVM - Scumm Interpreter + * Copyright (C) 2001 Ludvig Strigeus + * Copyright (C) 2001-2006 The ScummVM project + * + * 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. + * + * $URL$ + * $Id$ + * + */ + +// AGOS debug functions +#include "common/stdafx.h" + +#include "agos/debug.h" +#include "agos/agos.h" +#include "agos/intern.h" +#include "agos/vga.h" + +#include <sys/stat.h> + +namespace Simon { + +const byte *SimonEngine::dumpOpcode(const byte *p) { + uint opcode; + const char *s, *st; + + if (getGameType() == GType_ELVIRA || getGameType() == GType_ELVIRA2) { + opcode = READ_BE_UINT16(p); + p += 2; + if (opcode == 10000) + return NULL; + } else { + opcode = *p++; + if (opcode == 255) + return NULL; + } + + if (getGameType() == GType_FF || getGameType() == GType_PP) { + st = s = feeblefiles_opcode_name_table[opcode]; + } else if (getGameType() == GType_SIMON2 && getFeatures() & GF_TALKIE) { + st = s = simon2talkie_opcode_name_table[opcode]; + } else if (getFeatures() & GF_TALKIE) { + st = s = simon1talkie_opcode_name_table[opcode]; + } else if (getGameType() == GType_SIMON2) { + st = s = simon2dos_opcode_name_table[opcode]; + } else if (getGameType() == GType_SIMON1) { + st = s = simon1dos_opcode_name_table[opcode]; + } else { + st = s = ww_opcode_name_table[opcode]; + } + if (s == NULL) { + //error("INVALID OPCODE %d", opcode); + return NULL; + } + while (*st != '|') + st++; + printf("%s ", st + 1); + + for (;;) { + switch (*s++) { + case 'x': + printf("\n"); + return NULL; + case '|': + printf("\n"); + return p; + case 'B':{ + byte b = *p++; + if (b == 255) + printf("[%d] ", *p++); + else + printf("%d ", b); + break; + } + case 'V':{ + byte b = *p++; + if (b == 255) + printf("[[%d]] ", *p++); + else + printf("[%d] ", b); + break; + } + + case 'W':{ + int n = (int16)((p[0] << 8) | p[1]); + p += 2; + if (n >= 30000 && n < 30512) + printf("[%d] ", n - 30000); + else + printf("%d ", n); + break; + } + + case 'w':{ + int n = (int16)((p[0] << 8) | p[1]); + p += 2; + printf("%d ", n); + break; + } + + case 'I':{ + int n = (int16)((p[0] << 8) | p[1]);; + p += 2; + if (n == -1) + printf("ITEM_M1 "); + else if (n == -3) + printf("ITEM_M3 "); + else if (n == -5) + printf("ITEM_1 "); + else if (n == -7) + printf("ITEM_0 "); + else if (n == -9) + printf("ITEM_A_PARENT "); + else + printf("<%d> ", n); + break; + } + + case 'J':{ + printf("-> "); + } + break; + + case 'T':{ + uint n = ((p[0] << 8) | p[1]); + p += 2; + if (n != 0xFFFF) + printf("\"%s\"(%d) ", getStringPtrByID(n), n); + else + printf("NULL_STRING "); + } + break; + } + } +} + +void SimonEngine::dumpSubroutineLine(SubroutineLine *sl, Subroutine *sub) { + const byte *p; + + printf("; ****\n"); + + p = (byte *)sl + SUBROUTINE_LINE_SMALL_SIZE; + if (sub->id == 0) { + printf("; verb=%d, noun1=%d, noun2=%d\n", sl->verb, sl->noun1, sl->noun2); + p = (byte *)sl + SUBROUTINE_LINE_BIG_SIZE; + } + + for (;;) { + p = dumpOpcode(p); + if (p == NULL) + break; + } +} + +void SimonEngine::dumpSubroutine(Subroutine *sub) { + SubroutineLine *sl; + + printf("\n******************************************\n;Subroutine, ID=%d:\nSUB_%d:\n", sub->id, sub->id); + sl = (SubroutineLine *)((byte *)sub + sub->first); + for (; (byte *)sl != (byte *)sub; sl = (SubroutineLine *)((byte *)sub + sl->next)) { + dumpSubroutineLine(sl, sub); + } + printf("\nEND ******************************************\n"); +} + +void SimonEngine::dumpSubroutines() { + Subroutine *sub = _subroutineList; + for (; sub; sub = sub->next) { + dumpSubroutine(sub); + } +} + +void SimonEngine::dump_video_script(const byte *src, bool one_opcode_only) { + uint opcode; + const char *str, *strn; + + do { + if (getGameType() == GType_SIMON1 || getGameType() == GType_WW) { + opcode = READ_BE_UINT16(src); + src += 2; + } else { + opcode = *src++; + } + + if (opcode >= _numVideoOpcodes) { + error("Invalid opcode %x\n", opcode); + return; + } + + if (getGameType() == GType_FF || getGameType() == GType_PP) { + strn = str = feeblefiles_video_opcode_name_table[opcode]; + } else if (getGameType() == GType_SIMON2) { + strn = str = simon2_video_opcode_name_table[opcode]; + } else if (getGameType() == GType_SIMON1) { + strn = str = simon1_video_opcode_name_table[opcode]; + } else { + strn = str = ww_video_opcode_name_table[opcode]; + } + + while (*strn != '|') + strn++; + printf("%.2d: %s ", opcode, strn + 1); + + int end = (getGameType() == GType_FF || getGameType() == GType_PP) ? 9999 : 999; + for (; *str != '|'; str++) { + switch (*str) { + case 'x': + printf("\n"); + return; + case 'b': + printf("%d ", *src++); + break; + case 'd': + printf("%d ", (int16)readUint16Wrapper(src)); + src += 2; + break; + case 'v': + printf("[%d] ", readUint16Wrapper(src)); + src += 2; + break; + case 'i': + printf("%d ", (int16)readUint16Wrapper(src)); + src += 2; + break; + case 'q': + while (readUint16Wrapper(src) != end) { + printf("(%d,%d) ", readUint16Wrapper(src), + readUint16Wrapper(src + 2)); + src += 4; + } + src++; + break; + default: + error("Invalid fmt string '%c' in decompile VGA", *str); + } + } + + printf("\n"); + } while (!one_opcode_only); +} + +void SimonEngine::dump_vga_file(const byte *vga) { + const byte *pp; + const byte *p; + int count; + + pp = vga; + p = pp + READ_BE_UINT16(&((const VgaFileHeader_Simon *) pp)->hdr2_start); + count = READ_BE_UINT16(&((const VgaFileHeader2_Simon *) p)->animationCount); + p = pp + READ_BE_UINT16(&((const VgaFileHeader2_Simon *) p)->animationTable); + while (--count >= 0) { + int id = READ_BE_UINT16(&((const AnimationHeader_Simon *) p)->id); + + dump_vga_script_always(vga + READ_BE_UINT16(&((const AnimationHeader_Simon *) p)->scriptOffs), id / 100, id); + p += sizeof(AnimationHeader_Simon); + } + + pp = vga; + p = pp + READ_BE_UINT16(&((const VgaFileHeader_Simon *) pp)->hdr2_start); + count = READ_BE_UINT16(&((const VgaFileHeader2_Simon *) p)->imageCount); + p = pp + READ_BE_UINT16(&((const VgaFileHeader2_Simon *) p)->imageTable); + + while (--count >= 0) { + int id = READ_BE_UINT16(&((const ImageHeader_Simon *) p)->id); + + dump_vga_script_always(vga + READ_BE_UINT16(&((const ImageHeader_Simon *) p)->scriptOffs), id / 100, id); + p += sizeof(ImageHeader_Simon); + } +} + +static const byte bmp_hdr[] = { + 0x42, 0x4D, + 0x9E, 0x14, 0x00, 0x00, /* offset 2, file size */ + 0x00, 0x00, 0x00, 0x00, + 0x36, 0x04, 0x00, 0x00, + 0x28, 0x00, 0x00, 0x00, + + 0x3C, 0x00, 0x00, 0x00, /* image width */ + 0x46, 0x00, 0x00, 0x00, /* image height */ + 0x01, 0x00, 0x08, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + + 0x00, 0x01, 0x00, 0x00, + 0x00, 0x01, 0x00, 0x00, +}; + +void dump_bmp(const char *filename, int w, int h, const byte *bytes, const uint32 *palette) { + FILE *out = fopen(filename, "wb"); + byte my_hdr[sizeof(bmp_hdr)]; + int i; + + if (out == NULL) { + printf("DUMP ERROR\n"); + return; + } + + memcpy(my_hdr, bmp_hdr, sizeof(bmp_hdr)); + + *(uint32 *)(my_hdr + 2) = w * h + 1024 + sizeof(bmp_hdr); + *(uint32 *)(my_hdr + 18) = w; + *(uint32 *)(my_hdr + 22) = h; + + + fwrite(my_hdr, 1, sizeof(my_hdr), out); + + for (i = 0; i != 256; i++, palette++) { + byte color[4]; + color[0] = (byte)(*palette >> 16); + color[1] = (byte)(*palette >> 8); + color[2] = (byte)(*palette); + color[3] = 0; + fwrite(color, 1, 4, out); + } + + while (--h >= 0) { + fwrite(bytes + h * ((w + 3) & ~3), ((w + 3) & ~3), 1, out); + } + + fclose(out); +} + +void SimonEngine::dump_bitmap(const char *filename, const byte *offs, int w, int h, int flags, const byte *palette, + byte base) { + + if (getGameType() == GType_SIMON1 || getGameType() == GType_SIMON2) + w *= 16; + + /* allocate */ + byte *b = (byte *)malloc(w * h); + int i, j; + + VC10_state state; + + state.depack_cont = -0x80; + state.depack_src = offs; + state.dh = h; + state.y_skip = 0; + + if (getGameType() == GType_FF || getGameType() == GType_PP) { + for (i = 0; i != w; i++) { + byte *c = vc10_depackColumn(&state); + for (j = 0; j != h; j++) { + byte pix = c[j]; + b[j * w + i] = pix; + } + } + } else { + for (i = 0; i != w; i += 2) { + byte *c = vc10_depackColumn(&state); + for (j = 0; j != h; j++) { + byte pix = c[j]; + b[j * w + i] = (pix >> 4) | base; + b[j * w + i + 1] = (pix & 0xF) | base; + } + } + } + + dump_bmp(filename, w, h, b, (const uint32 *)palette); + free(b); +} + +void SimonEngine::dump_single_bitmap(int file, int image, const byte *offs, int w, int h, byte base) { + char buf[40]; +#if !defined(PALMOS_MODE) && !defined(__DC__) && !defined(__PSP__) && !defined(__PLAYSTATION2__) + struct stat statbuf; +#endif + +#if defined(MACOS_CARBON) + sprintf(buf, ":dumps:File%d_Image%d.bmp", file, image); +#else + sprintf(buf, "dumps/File%d_Image%d.bmp", file, image); +#endif + +#if !defined(PALMOS_MODE) && !defined(__DC__) && !defined(__PSP__) && !defined(__PLAYSTATION2__) + if (stat(buf, &statbuf) == 0) + return; +#endif + + dump_bitmap(buf, offs, w, h, 0, _displayPalette, base); +} + +void pal_load(byte *pal, const byte *vga1, int a, int b) { + uint num = (a == 0) ? 0x20 : 0x10; + byte *palptr; + const byte *src; + + palptr = (byte *)&pal[a << 4]; + src = vga1 + 6 + b * 96; + + do { + palptr[0] = src[0] << 2; + palptr[1] = src[1] << 2; + palptr[2] = src[2] << 2; + palptr[3] = 0; + + palptr += 4; + src += 3; + } while (--num); +} + +void SimonEngine::dump_vga_bitmaps(const byte *vga, byte *vga1, int res) { + + int i; + uint32 offs; + const byte *p2; + byte pal[768]; + + memset(pal, 0, sizeof(pal)); + pal_load(pal, vga1, 2, 0); + pal_load(pal, vga1, 3, 1); + pal_load(pal, vga1, 4, 2); + pal_load(pal, vga1, 5, 3); + + int width, height, flags; + + i = 538; + + for (i = 1; ; i++) { + p2 = vga + i * 8; + offs = READ_BE_UINT32(p2); + + /* try to detect end of images. + * assume the end when offset >= 200kb */ + if (offs >= 200*1024) + return; + + width = READ_BE_UINT16(p2 + 6); + height = p2[5]; + flags = p2[4]; + + printf("Image %d. Width=%d, Height=%d, Flags=0x%X\n", i, width, height, flags); + + /* dump bitmap */ + char buf[40]; +#if defined(MACOS_CARBON) + sprintf(buf, ":dumps:Res%d_Image%d.bmp", res, i); +#else + sprintf(buf, "dumps/Res%d_Image%d.bmp", res, i); +#endif + + dump_bitmap(buf, vga + offs, width, height, flags, pal, 0); + + } +} + +void SimonEngine::dump_vga_script_always(const byte *ptr, uint res, uint sprite_id) { + printf("; address=%x, vgafile=%d vgasprite=%d\n", + (unsigned int)(ptr - _vgaBufferPointers[res].vgaFile1), res, sprite_id); + dump_video_script(ptr, false); + printf("; end\n"); +} + +void SimonEngine::dump_vga_script(const byte *ptr, uint res, uint sprite_id) { + dump_vga_script_always(ptr, res, sprite_id); +} + +} // End of namespace Simon diff --git a/engines/agos/debug.h b/engines/agos/debug.h new file mode 100644 index 0000000000..e6f478deee --- /dev/null +++ b/engines/agos/debug.h @@ -0,0 +1,1855 @@ +/* ScummVM - Scumm Interpreter + * Copyright (C) 2001 Ludvig Strigeus + * Copyright (C) 2001-2006 The ScummVM project + * + * 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. + * + * $URL$ + * $Id$ + * + */ + +#ifndef AGOS_DEBUG_H +#define AGOS_DEBUG_H + +namespace Simon { + +static const char *const ww_opcode_name_table[256] = { + /* 0 */ + "|NOT", + "IJ|AT", + "IJ|NOT_AT", + NULL, + /* 4 */ + NULL, + "IJ|CARRIED", + "IJ|NOT_CARRIED", + "IIJ|IS_AT", + /* 8 */ + NULL, + NULL, + NULL, + "VJ|IS_ZERO", + /* 12 */ + "VJ|ISNOT_ZERO", + "VWJ|IS_EQ", + "VWJ|IS_NEQ", + "VWJ|IS_LE", + /* 16 */ + "VWJ|IS_GE", + "VVJ|IS_EQF", + "VVJ|IS_NEQF", + "VVJ|IS_LEF", + /* 20 */ + "VVJ|IS_GEF", + NULL, + NULL, + "WJ|CHANCE", + /* 24 */ + NULL, + "IJ|IS_ROOM", + "IJ|IS_OBJECT", + "IWJ|ITEM_STATE_IS", + /* 28 */ + "IBJ|OBJECT_HAS_FLAG", + NULL, + NULL, + "I|SET_NO_PARENT", + /* 32 */ + NULL, + "II|SET_PARENT", + NULL, + NULL, + /* 36 */ + "VV|MOVE", + NULL, + NULL, + NULL, + /* 40 */ + NULL, + "V|ZERO", + "VW|SET", + "VW|ADD", + /* 44 */ + "VW|SUB", + "VV|ADDF", + "VV|SUBF", + "VW|MUL", + /* 48 */ + "VW|DIV", + "VV|MULF", + "VV|DIVF", + "VW|MOD", + /* 52 */ + "VV|MODF", + "VW|RANDOM", + NULL, + "I|SET_A_PARENT", + /* 56 */ + "IB|SET_CHILD2_BIT", + "IB|CLEAR_CHILD2_BIT", + "II|MAKE_SIBLING", + "I|INC_STATE", + /* 60 */ + "I|DEC_STATE", + "IW|SET_STATE", + "V|SHOW_INT", + "T|SHOW_STRING_NL", + /* 64 */ + "T|SHOW_STRING", + "WWWWWB|ADD_TEXT_BOX", + "BT|SET_SHORT_TEXT", + "BT|SET_LONG_TEXT", + /* 68 */ + "x|END", + "x|DONE", + "V|SHOW_STRING_AR3", + "W|START_SUB", + /* 72 */ + NULL, + NULL, + NULL, + NULL, + /* 76 */ + "WW|ADD_TIMEOUT", + "J|IS_M1_EMPTY", + "J|IS_M3_EMPTY", + "ITJ|CHILD_FR2_IS", + /* 80 */ + "IIJ|IS_ITEM_EQ", + NULL, + "B|DEBUG", + "|RESCAN", + /* 84 */ + NULL, + "IBB|WHERE_TO", + NULL, + "W|COMMENT", + /* 88 */ + "|STOP_ANIMATION", + "|RESTART_ANIMATION", + "IB|GET_PARENT", + "IB|GET_NEXT", + /* 92 */ + "IB|GET_CHILDREN", + NULL, + NULL, + NULL, + /* 96 */ + "WB|PICTURE", + "W|LOAD_ZONE", + "WBWWW|ANIMATE", + "W|STOP_ANIMATE", + /* 100 */ + "|KILL_ANIMATE", + "BWWWWWW|DEFINE_WINDOW", + "B|CHANGE_WINDOW", + "|CLS", + /* 104 */ + "B|CLOSE_WINDOW", + "B|MENU", + "BB|TEXT_MENU", + "WWWWWIW|ADD_BOX", + /* 108 */ + "W|DEL_BOX", + "W|ENABLE_BOX", + "W|DISABLE_BOX", + "WWW|MOVE_BOX", + /* 112 */ + NULL, + NULL, + "IB|DO_ICONS", + "IBJ|IS_CLASS", + /* 116 */ + "IB|SET_CLASS", + "IB|UNSET_CLASS", + NULL, + "W|WAIT_SYNC", + /* 120 */ + "W|SYNC", + "BI|DEF_OBJ", + NULL, + NULL, + /* 124 */ + NULL, + "IJ|IS_SIBLING_WITH_A", + "IBB|DO_CLASS_ICONS", + "WW|PLAY_TUNE", + /* 128 */ + "W|WAIT_END_TUNE", + "W|IF_END_TUNE", + "Bww|SET_ADJ_NOUN", + NULL, + /* 132 */ + "|SAVE_GAME", + "|LOAD_GAME", + "|DUMMYPROC_134", + "|QUIT_IF_USER_PRESSES_Y", + /* 136 */ + "IV|COPY_SF", + "B|RESTORE_ICONS", + "|FREEZE_ZONES", + "II|SET_PARENT_SPECIAL", + /* 140 */ + "|CLEAR_TIMERS", + "BI|SET_M1_OR_M3", + "WJ|IS_HITAREA_0x40_CLEAR", + "I|START_ITEM_SUB", + /* 144 */ + NULL, + NULL, + NULL, + NULL, + /* 148 */ + "IB|IF_DOOR_OPEN", + NULL, + NULL, + "BI|SET_ARRAY6_TO", + /* 152 */ + "BB|SET_M1_M3_TO_ARRAY6", + "B|SET_BIT", + "B|CLEAR_BIT", + "BJ|IS_BIT_CLEAR", + /* 156 */ + "BJ|IS_BIT_SET", + "IBB|GET_ITEM_PROP", + "IBW|SET_ITEM_PROP", + NULL, + /* 160 */ + "B|SET_INK", + "BWBW|SETUP_TEXT", + "BBT|PRINT_STR", + "W|PLAY_EFFECT", + /* 164 */ + "|getDollar2", + "IWWJ|IS_ADJ_NOUN", + "B|SET_BIT2", + "B|CLEAR_BIT2", + /* 168 */ + "BJ|IS_BIT2_CLEAR", + "BJ|IS_BIT2_SET", + NULL, + NULL, + /* 172 */ + NULL, + NULL, + NULL, + "|LOCK_ZONES", + /* 176 */ + "|UNLOCK_ZONES", + "BBI|SCREEN_TEXT_POBJ", + "WWBB|GETPATHPOSN", + "IWWJ|IS_ADJ_NOUN", + /* 180 */ + "B|SET_BIT2", + "B|CLEAR_BIT2", + "BJ|IS_BIT2_CLEAR", + "BJ|IS_BIT2_SET", + /* 184 */ + "W|UNLOAD_ZONE", + "W|LOAD_SOUND_FILES", + "|UNFREEZE_ZONES", + "|FADE_TO_BLACK", +}; + +static const char *const simon1dos_opcode_name_table[256] = { + /* 0 */ + "|NOT", + "IJ|AT", + "IJ|NOT_AT", + NULL, + /* 4 */ + NULL, + "IJ|CARRIED", + "IJ|NOT_CARRIED", + "IIJ|IS_AT", + /* 8 */ + NULL, + NULL, + NULL, + "VJ|IS_ZERO", + /* 12 */ + "VJ|ISNOT_ZERO", + "VWJ|IS_EQ", + "VWJ|IS_NEQ", + "VWJ|IS_LE", + /* 16 */ + "VWJ|IS_GE", + "VVJ|IS_EQF", + "VVJ|IS_NEQF", + "VVJ|IS_LEF", + /* 20 */ + "VVJ|IS_GEF", + NULL, + NULL, + "WJ|CHANCE", + /* 24 */ + NULL, + "IJ|IS_ROOM", + "IJ|IS_OBJECT", + "IWJ|ITEM_STATE_IS", + /* 28 */ + "IBJ|OBJECT_HAS_FLAG", + NULL, + NULL, + "I|SET_NO_PARENT", + /* 32 */ + NULL, + "II|SET_PARENT", + NULL, + NULL, + /* 36 */ + "VV|MOVE", + NULL, + NULL, + NULL, + /* 40 */ + NULL, + "V|ZERO", + "VW|SET", + "VW|ADD", + /* 44 */ + "VW|SUB", + "VV|ADDF", + "VV|SUBF", + "VW|MUL", + /* 48 */ + "VW|DIV", + "VV|MULF", + "VV|DIVF", + "VW|MOD", + /* 52 */ + "VV|MODF", + "VW|RANDOM", + NULL, + "I|SET_A_PARENT", + /* 56 */ + "IB|SET_CHILD2_BIT", + "IB|CLEAR_CHILD2_BIT", + "II|MAKE_SIBLING", + "I|INC_STATE", + /* 60 */ + "I|DEC_STATE", + "IW|SET_STATE", + "V|SHOW_INT", + "T|SHOW_STRING_NL", + /* 64 */ + "T|SHOW_STRING", + "WWWWWB|ADD_TEXT_BOX", + "BT|SET_SHORT_TEXT", + "BT|SET_LONG_TEXT", + /* 68 */ + "x|END", + "x|DONE", + "V|SHOW_STRING_AR3", + "W|START_SUB", + /* 72 */ + NULL, + NULL, + NULL, + NULL, + /* 76 */ + "WW|ADD_TIMEOUT", + "J|IS_M1_EMPTY", + "J|IS_M3_EMPTY", + "ITJ|CHILD_FR2_IS", + /* 80 */ + "IIJ|IS_ITEM_EQ", + NULL, + "B|DEBUG", + "|RESCAN", + /* 84 */ + NULL, + NULL, + NULL, + "W|COMMENT", + /* 88 */ + "|STOP_ANIMATION", + "|RESTART_ANIMATION", + "IB|GET_PARENT", + "IB|GET_NEXT", + /* 92 */ + "IB|GET_CHILDREN", + NULL, + NULL, + NULL, + /* 96 */ + "WB|PICTURE", + "W|LOAD_ZONE", + "WBWWW|ANIMATE", + "W|STOP_ANIMATE", + /* 100 */ + "|KILL_ANIMATE", + "BWWWWWW|DEFINE_WINDOW", + "B|CHANGE_WINDOW", + "|CLS", + /* 104 */ + "B|CLOSE_WINDOW", + NULL, + NULL, + "WWWWWIW|ADD_BOX", + /* 108 */ + "W|DEL_BOX", + "W|ENABLE_BOX", + "W|DISABLE_BOX", + "WWW|MOVE_BOX", + /* 112 */ + NULL, + NULL, + "IB|DO_ICONS", + "IBJ|IS_CLASS", + /* 116 */ + "IB|SET_CLASS", + "IB|UNSET_CLASS", + NULL, + "W|WAIT_SYNC", + /* 120 */ + "W|SYNC", + "BI|DEF_OBJ", + NULL, + NULL, + /* 124 */ + NULL, + "IJ|IS_SIBLING_WITH_A", + "IBB|DO_CLASS_ICONS", + "WW|PLAY_TUNE", + /* 128 */ + "W|WAIT_END_TUNE", + "W|IF_END_TUNE", + "Bww|SET_ADJ_NOUN", + NULL, + /* 132 */ + "|SAVE_GAME", + "|LOAD_GAME", + "|DUMMYPROC_134", + "|QUIT_IF_USER_PRESSES_Y", + /* 136 */ + "IV|COPY_SF", + "B|RESTORE_ICONS", + "|FREEZE_ZONES", + "II|SET_PARENT_SPECIAL", + /* 140 */ + "|CLEAR_TIMERS", + "BI|SET_M1_OR_M3", + "WJ|IS_HITAREA_0x40_CLEAR", + "I|START_ITEM_SUB", + /* 144 */ + NULL, + NULL, + NULL, + NULL, + /* 148 */ + NULL, + NULL, + NULL, + "BI|SET_ARRAY6_TO", + /* 152 */ + "BB|SET_M1_M3_TO_ARRAY6", + "B|SET_BIT", + "B|CLEAR_BIT", + "BJ|IS_BIT_CLEAR", + /* 156 */ + "BJ|IS_BIT_SET", + "IBB|GET_ITEM_PROP", + "IBW|SET_ITEM_PROP", + NULL, + /* 160 */ + "B|SET_INK", + "BWBW|SETUP_TEXT", + "BBT|PRINT_STR", + "W|PLAY_EFFECT", + /* 164 */ + "|getDollar2", + "IWWJ|IS_ADJ_NOUN", + "B|SET_BIT2", + "B|CLEAR_BIT2", + /* 168 */ + "BJ|IS_BIT2_CLEAR", + "BJ|IS_BIT2_SET", + NULL, + NULL, + /* 172 */ + NULL, + NULL, + NULL, + "|LOCK_ZONES", + /* 176 */ + "|UNLOCK_ZONES", + "BBI|SCREEN_TEXT_POBJ", + "WWBB|GETPATHPOSN", + "BBB|SCREEN_TEXT_LONG_TEXT", + /* 180 */ + "|MOUSE_ON", + "|MOUSE_OFF", + "|LOAD_BEARD", + "|UNLOAD_BEARD", + /* 184 */ + "W|UNLOAD_ZONE", + "W|LOAD_SOUND_FILES", + "|UNFREEZE_ZONES", + "|FADE_TO_BLACK", +}; + +static const char *const simon1talkie_opcode_name_table[256] = { + /* 0 */ + "|NOT", + "IJ|AT", + "IJ|NOT_AT", + NULL, + /* 4 */ + NULL, + "IJ|CARRIED", + "IJ|NOT_CARRIED", + "IIJ|IS_AT", + /* 8 */ + NULL, + NULL, + NULL, + "VJ|IS_ZERO", + /* 12 */ + "VJ|ISNOT_ZERO", + "VWJ|IS_EQ", + "VWJ|IS_NEQ", + "VWJ|IS_LE", + /* 16 */ + "VWJ|IS_GE", + "VVJ|IS_EQF", + "VVJ|IS_NEQF", + "VVJ|IS_LEF", + /* 20 */ + "VVJ|IS_GEF", + NULL, + NULL, + "WJ|CHANCE", + /* 24 */ + NULL, + "IJ|IS_ROOM", + "IJ|IS_OBJECT", + "IWJ|ITEM_STATE_IS", + /* 28 */ + "IBJ|OBJECT_HAS_FLAG", + NULL, + NULL, + "I|SET_NO_PARENT", + /* 32 */ + NULL, + "II|SET_PARENT", + NULL, + NULL, + /* 36 */ + "VV|MOVE", + NULL, + NULL, + NULL, + /* 40 */ + NULL, + "V|ZERO", + "VW|SET", + "VW|ADD", + /* 44 */ + "VW|SUB", + "VV|ADDF", + "VV|SUBF", + "VW|MUL", + /* 48 */ + "VW|DIV", + "VV|MULF", + "VV|DIVF", + "VW|MOD", + /* 52 */ + "VV|MODF", + "VW|RANDOM", + NULL, + "I|SET_A_PARENT", + /* 56 */ + "IB|SET_CHILD2_BIT", + "IB|CLEAR_CHILD2_BIT", + "II|MAKE_SIBLING", + "I|INC_STATE", + /* 60 */ + "I|DEC_STATE", + "IW|SET_STATE", + "V|SHOW_INT", + "T|SHOW_STRING_NL", + /* 64 */ + "T|SHOW_STRING", + "WWWWWB|ADD_TEXT_BOX", + "BT|SET_SHORT_TEXT", + "BTw|SET_LONG_TEXT", + /* 68 */ + "x|END", + "x|DONE", + "V|SHOW_STRING_AR3", + "W|START_SUB", + /* 72 */ + NULL, + NULL, + NULL, + NULL, + /* 76 */ + "WW|ADD_TIMEOUT", + "J|IS_M1_EMPTY", + "J|IS_M3_EMPTY", + "ITJ|CHILD_FR2_IS", + /* 80 */ + "IIJ|IS_ITEM_EQ", + NULL, + "B|DEBUG", + "|RESCAN", + /* 84 */ + NULL, + NULL, + NULL, + "W|COMMENT", + /* 88 */ + "|STOP_ANIMATION", + "|RESTART_ANIMATION", + "IB|GET_PARENT", + "IB|GET_NEXT", + /* 92 */ + "IB|GET_CHILDREN", + NULL, + NULL, + NULL, + /* 96 */ + "WB|PICTURE", + "W|LOAD_ZONE", + "WBWWW|ANIMATE", + "W|STOP_ANIMATE", + /* 100 */ + "|KILL_ANIMATE", + "BWWWWWW|DEFINE_WINDOW", + "B|CHANGE_WINDOW", + "|CLS", + /* 104 */ + "B|CLOSE_WINDOW", + NULL, + NULL, + "WWWWWIW|ADD_BOX", + /* 108 */ + "W|DEL_BOX", + "W|ENABLE_BOX", + "W|DISABLE_BOX", + "WWW|MOVE_BOX", + /* 112 */ + NULL, + NULL, + "IB|DO_ICONS", + "IBJ|IS_CLASS", + /* 116 */ + "IB|SET_CLASS", + "IB|UNSET_CLASS", + NULL, + "W|WAIT_SYNC", + /* 120 */ + "W|SYNC", + "BI|DEF_OBJ", + NULL, + NULL, + /* 124 */ + NULL, + "IJ|IS_SIBLING_WITH_A", + "IBB|DO_CLASS_ICONS", + "WW|PLAY_TUNE", + /* 128 */ + "W|WAIT_END_TUNE", + "W|IF_END_TUNE", + "Bww|SET_ADJ_NOUN", + NULL, + /* 132 */ + "|SAVE_GAME", + "|LOAD_GAME", + "|DUMMYPROC_134", + "|QUIT_IF_USER_PRESSES_Y", + /* 136 */ + "IV|COPY_SF", + "B|RESTORE_ICONS", + "|FREEZE_ZONES", + "II|SET_PARENT_SPECIAL", + /* 140 */ + "|CLEAR_TIMERS", + "BI|SET_M1_OR_M3", + "WJ|IS_HITAREA_0x40_CLEAR", + "I|START_ITEM_SUB", + /* 144 */ + NULL, + NULL, + NULL, + NULL, + /* 148 */ + NULL, + NULL, + NULL, + "BI|SET_ARRAY6_TO", + /* 152 */ + "BB|SET_M1_M3_TO_ARRAY6", + "B|SET_BIT", + "B|CLEAR_BIT", + "BJ|IS_BIT_CLEAR", + /* 156 */ + "BJ|IS_BIT_SET", + "IBB|GET_ITEM_PROP", + "IBW|SET_ITEM_PROP", + NULL, + /* 160 */ + "B|SET_INK", + "BWBW|SETUP_TEXT", + "BBTW|PRINT_STR", + "W|PLAY_EFFECT", + /* 164 */ + "|getDollar2", + "IWWJ|IS_ADJ_NOUN", + "B|SET_BIT2", + "B|CLEAR_BIT2", + /* 168 */ + "BJ|IS_BIT2_CLEAR", + "BJ|IS_BIT2_SET", + NULL, + NULL, + /* 172 */ + NULL, + NULL, + NULL, + "|LOCK_ZONES", + /* 176 */ + "|UNLOCK_ZONES", + "BBI|SCREEN_TEXT_POBJ", + "WWBB|GETPATHPOSN", + "BBB|SCREEN_TEXT_LONG_TEXT", + /* 180 */ + "|MOUSE_ON", + "|MOUSE_OFF", + "|LOAD_BEARD", + "|UNLOAD_BEARD", + /* 184 */ + "W|UNLOAD_ZONE", + "W|LOAD_SOUND_FILES", + "|UNFREEZE_ZONES", + "|FADE_TO_BLACK", +}; + +static const char *const simon2dos_opcode_name_table[256] = { + /* 0 */ + "|NOT", + "IJ|AT", + "IJ|NOT_AT", + NULL, + /* 4 */ + NULL, + "IJ|CARRIED", + "IJ|NOT_CARRIED", + "IIJ|IS_AT", + /* 8 */ + NULL, + NULL, + NULL, + "VJ|IS_ZERO", + /* 12 */ + "VJ|ISNOT_ZERO", + "VWJ|IS_EQ", + "VWJ|IS_NEQ", + "VWJ|IS_LE", + /* 16 */ + "VWJ|IS_GE", + "VVJ|IS_EQF", + "VVJ|IS_NEQF", + "VVJ|IS_LEF", + /* 20 */ + "VVJ|IS_GEF", + NULL, + NULL, + "WJ|CHANCE", + /* 24 */ + NULL, + "IJ|IS_ROOM", + "IJ|IS_OBJECT", + "IWJ|ITEM_STATE_IS", + /* 28 */ + "IBJ|OBJECT_HAS_FLAG", + NULL, + NULL, + "I|SET_NO_PARENT", + /* 32 */ + NULL, + "II|SET_PARENT", + NULL, + NULL, + /* 36 */ + "VV|MOVE", + NULL, + NULL, + NULL, + /* 40 */ + NULL, + "V|ZERO", + "VW|SET", + "VW|ADD", + /* 44 */ + "VW|SUB", + "VV|ADDF", + "VV|SUBF", + "VW|MUL", + /* 48 */ + "VW|DIV", + "VV|MULF", + "VV|DIVF", + "VW|MOD", + /* 52 */ + "VV|MODF", + "VW|RANDOM", + NULL, + "I|SET_A_PARENT", + /* 56 */ + "IB|SET_CHILD2_BIT", + "IB|CLEAR_CHILD2_BIT", + "II|MAKE_SIBLING", + "I|INC_STATE", + /* 60 */ + "I|DEC_STATE", + "IW|SET_STATE", + "V|SHOW_INT", + "T|SHOW_STRING_NL", + /* 64 */ + "T|SHOW_STRING", + "WWWWWB|ADD_TEXT_BOX", + "BT|SET_SHORT_TEXT", + "BT|SET_LONG_TEXT", + /* 68 */ + "x|END", + "x|DONE", + "V|SHOW_STRING_AR3", + "W|START_SUB", + /* 72 */ + NULL, + NULL, + NULL, + NULL, + /* 76 */ + "WW|ADD_TIMEOUT", + "J|IS_M1_EMPTY", + "J|IS_M3_EMPTY", + "ITJ|CHILD_FR2_IS", + /* 80 */ + "IIJ|IS_ITEM_EQ", + NULL, + "B|DEBUG", + "|RESCAN", + /* 84 */ + NULL, + NULL, + NULL, + "W|COMMENT", + /* 88 */ + "|STOP_ANIMATION", + "|RESTART_ANIMATION", + "IB|GET_PARENT", + "IB|GET_NEXT", + /* 92 */ + "IB|GET_CHILDREN", + NULL, + NULL, + NULL, + /* 96 */ + "WB|PICTURE", + "W|LOAD_ZONE", + "WWBWWW|ANIMATE", + "WW|STOP_ANIMATE", + /* 100 */ + "|KILL_ANIMATE", + "BWWWWWW|DEFINE_WINDOW", + "B|CHANGE_WINDOW", + "|CLS", + /* 104 */ + "B|CLOSE_WINDOW", + NULL, + NULL, + "WWWWWIW|ADD_BOX", + /* 108 */ + "W|DEL_BOX", + "W|ENABLE_BOX", + "W|DISABLE_BOX", + "WWW|MOVE_BOX", + /* 112 */ + NULL, + NULL, + "IB|DO_ICONS", + "IBJ|IS_CLASS", + /* 116 */ + "IB|SET_CLASS", + "IB|UNSET_CLASS", + NULL, + "W|WAIT_SYNC", + /* 120 */ + "W|SYNC", + "BI|DEF_OBJ", + NULL, + NULL, + /* 124 */ + NULL, + "IJ|IS_SIBLING_WITH_A", + "IBB|DO_CLASS_ICONS", + "WWB|PLAY_TUNE", + /* 128 */ + "W|WAIT_END_TUNE", + "W|IF_END_TUNE", + "Bww|SET_ADJ_NOUN", + NULL, + /* 132 */ + "|SAVE_GAME", + "|LOAD_GAME", + "|DUMMYPROC_134", + "|QUIT_IF_USER_PRESSES_Y", + /* 136 */ + "IV|COPY_SF", + "B|RESTORE_ICONS", + "|FREEZE_ZONES", + "II|SET_PARENT_SPECIAL", + /* 140 */ + "|CLEAR_TIMERS", + "BI|SET_M1_OR_M3", + "WJ|IS_HITAREA_0x40_CLEAR", + "I|START_ITEM_SUB", + /* 144 */ + NULL, + NULL, + NULL, + NULL, + /* 148 */ + NULL, + NULL, + NULL, + "BI|SET_ARRAY6_TO", + /* 152 */ + "BB|SET_M1_M3_TO_ARRAY6", + "B|SET_BIT", + "B|CLEAR_BIT", + "BJ|IS_BIT_CLEAR", + /* 156 */ + "BJ|IS_BIT_SET", + "IBB|GET_ITEM_PROP", + "IBW|SET_ITEM_PROP", + NULL, + /* 160 */ + "B|SET_INK", + "BWBW|SETUP_TEXT", + "BBT|PRINT_STR", + "W|PLAY_EFFECT", + /* 164 */ + "|getDollar2", + "IWWJ|IS_ADJ_NOUN", + "B|SET_BIT2", + "B|CLEAR_BIT2", + /* 168 */ + "BJ|IS_BIT2_CLEAR", + "BJ|IS_BIT2_SET", + NULL, + NULL, + /* 172 */ + NULL, + NULL, + NULL, + "|LOCK_ZONES", + /* 176 */ + "|UNLOCK_ZONES", + "BBI|SCREEN_TEXT_POBJ", + "WWBB|GETPATHPOSN", + "BBB|SCREEN_TEXT_LONG_TEXT", + /* 180 */ + "|MOUSE_ON", + "|MOUSE_OFF", + NULL, + NULL, + /* 184 */ + "W|UNLOAD_ZONE", + NULL, + "|UNFREEZE_ZONES", + NULL, + /* 188 */ + "BSJ|STRING2_IS", + "|CLEAR_MARKS", + "B|WAIT_FOR_MARK", +}; + +static const char *const simon2talkie_opcode_name_table[256] = { + /* 0 */ + "|NOT", + "IJ|AT", + "IJ|NOT_AT", + NULL, + /* 4 */ + NULL, + "IJ|CARRIED", + "IJ|NOT_CARRIED", + "IIJ|IS_AT", + /* 8 */ + NULL, + NULL, + NULL, + "VJ|IS_ZERO", + /* 12 */ + "VJ|ISNOT_ZERO", + "VWJ|IS_EQ", + "VWJ|IS_NEQ", + "VWJ|IS_LE", + /* 16 */ + "VWJ|IS_GE", + "VVJ|IS_EQF", + "VVJ|IS_NEQF", + "VVJ|IS_LEF", + /* 20 */ + "VVJ|IS_GEF", + NULL, + NULL, + "WJ|CHANCE", + /* 24 */ + NULL, + "IJ|IS_ROOM", + "IJ|IS_OBJECT", + "IWJ|ITEM_STATE_IS", + /* 28 */ + "IBJ|OBJECT_HAS_FLAG", + NULL, + NULL, + "I|SET_NO_PARENT", + /* 32 */ + NULL, + "II|SET_PARENT", + NULL, + NULL, + /* 36 */ + "VV|MOVE", + NULL, + NULL, + NULL, + /* 40 */ + NULL, + "V|ZERO", + "VW|SET", + "VW|ADD", + /* 44 */ + "VW|SUB", + "VV|ADDF", + "VV|SUBF", + "VW|MUL", + /* 48 */ + "VW|DIV", + "VV|MULF", + "VV|DIVF", + "VW|MOD", + /* 52 */ + "VV|MODF", + "VW|RANDOM", + NULL, + "I|SET_A_PARENT", + /* 56 */ + "IB|SET_CHILD2_BIT", + "IB|CLEAR_CHILD2_BIT", + "II|MAKE_SIBLING", + "I|INC_STATE", + /* 60 */ + "I|DEC_STATE", + "IW|SET_STATE", + "V|SHOW_INT", + "T|SHOW_STRING_NL", + /* 64 */ + "T|SHOW_STRING", + "WWWWWB|ADD_TEXT_BOX", + "BT|SET_SHORT_TEXT", + "BTw|SET_LONG_TEXT", + /* 68 */ + "x|END", + "x|DONE", + "V|SHOW_STRING_AR3", + "W|START_SUB", + /* 72 */ + NULL, + NULL, + NULL, + NULL, + /* 76 */ + "WW|ADD_TIMEOUT", + "J|IS_M1_EMPTY", + "J|IS_M3_EMPTY", + "ITJ|CHILD_FR2_IS", + /* 80 */ + "IIJ|IS_ITEM_EQ", + NULL, + "B|DEBUG", + "|RESCAN", + /* 84 */ + NULL, + NULL, + NULL, + "W|COMMENT", + /* 88 */ + "|STOP_ANIMATION", + "|RESTART_ANIMATION", + "IB|GET_PARENT", + "IB|GET_NEXT", + /* 92 */ + "IB|GET_CHILDREN", + NULL, + NULL, + NULL, + /* 96 */ + "WB|PICTURE", + "W|LOAD_ZONE", + "WWBWWW|ANIMATE", + "WW|STOP_ANIMATE", + /* 100 */ + "|KILL_ANIMATE", + "BWWWWWW|DEFINE_WINDOW", + "B|CHANGE_WINDOW", + "|CLS", + /* 104 */ + "B|CLOSE_WINDOW", + NULL, + NULL, + "WWWWWIW|ADD_BOX", + /* 108 */ + "W|DEL_BOX", + "W|ENABLE_BOX", + "W|DISABLE_BOX", + "WWW|MOVE_BOX", + /* 112 */ + NULL, + NULL, + "IB|DO_ICONS", + "IBJ|IS_CLASS", + /* 116 */ + "IB|SET_CLASS", + "IB|UNSET_CLASS", + NULL, + "W|WAIT_SYNC", + /* 120 */ + "W|SYNC", + "BI|DEF_OBJ", + NULL, + NULL, + /* 124 */ + NULL, + "IJ|IS_SIBLING_WITH_A", + "IBB|DO_CLASS_ICONS", + "WWB|PLAY_TUNE", + /* 128 */ + "W|WAIT_END_TUNE", + "W|IF_END_TUNE", + "Bww|SET_ADJ_NOUN", + NULL, + /* 132 */ + "|SAVE_GAME", + "|LOAD_GAME", + "|DUMMYPROC_134", + "|QUIT_IF_USER_PRESSES_Y", + /* 136 */ + "IV|COPY_SF", + "B|RESTORE_ICONS", + "|FREEZE_ZONES", + "II|SET_PARENT_SPECIAL", + /* 140 */ + "|CLEAR_TIMERS", + "BI|SET_M1_OR_M3", + "WJ|IS_HITAREA_0x40_CLEAR", + "I|START_ITEM_SUB", + /* 144 */ + NULL, + NULL, + NULL, + NULL, + /* 148 */ + NULL, + NULL, + NULL, + "BI|SET_ARRAY6_TO", + /* 152 */ + "BB|SET_M1_M3_TO_ARRAY6", + "B|SET_BIT", + "B|CLEAR_BIT", + "BJ|IS_BIT_CLEAR", + /* 156 */ + "BJ|IS_BIT_SET", + "IBB|GET_ITEM_PROP", + "IBW|SET_ITEM_PROP", + NULL, + /* 160 */ + "B|SET_INK", + "BWBW|SETUP_TEXT", + "BBTW|PRINT_STR", + "W|PLAY_EFFECT", + /* 164 */ + "|getDollar2", + "IWWJ|IS_ADJ_NOUN", + "B|SET_BIT2", + "B|CLEAR_BIT2", + /* 168 */ + "BJ|IS_BIT2_CLEAR", + "BJ|IS_BIT2_SET", + NULL, + NULL, + /* 172 */ + NULL, + NULL, + NULL, + "|LOCK_ZONES", + /* 176 */ + "|UNLOCK_ZONES", + "BBI|SCREEN_TEXT_POBJ", + "WWBB|GETPATHPOSN", + "BBB|SCREEN_TEXT_LONG_TEXT", + /* 180 */ + "|MOUSE_ON", + "|MOUSE_OFF", + NULL, + NULL, + /* 184 */ + "W|UNLOAD_ZONE", + NULL, + "|UNFREEZE_ZONES", + NULL, + /* 188 */ + "BSJ|STRING2_IS", + "|CLEAR_MARKS", + "B|WAIT_FOR_MARK", +}; + +static const char *const feeblefiles_opcode_name_table[256] = { + /* 0 */ + "|NOT", + "IJ|AT", + "IJ|NOT_AT", + NULL, + /* 4 */ + NULL, + "IJ|CARRIED", + "IJ|NOT_CARRIED", + "IIJ|IS_AT", + /* 8 */ + NULL, + NULL, + NULL, + "VJ|IS_ZERO", + /* 12 */ + "VJ|ISNOT_ZERO", + "VWJ|IS_EQ", + "VWJ|IS_NEQ", + "VWJ|IS_LE", + /* 16 */ + "VWJ|IS_GE", + "VVJ|IS_EQF", + "VVJ|IS_NEQF", + "VVJ|IS_LEF", + /* 20 */ + "VVJ|IS_GEF", + NULL, + NULL, + "WJ|CHANCE", + /* 24 */ + NULL, + "IJ|IS_ROOM", + "IJ|IS_OBJECT", + "IWJ|ITEM_STATE_IS", + /* 28 */ + "IBJ|OBJECT_HAS_FLAG", + NULL, + NULL, + "I|SET_NO_PARENT", + /* 32 */ + NULL, + "II|SET_PARENT", + NULL, + NULL, + /* 36 */ + "VV|MOVE", + "|JUMP_OUT", + NULL, + NULL, + /* 40 */ + NULL, + "V|ZERO", + "VW|SET", + "VW|ADD", + /* 44 */ + "VW|SUB", + "VV|ADDF", + "VV|SUBF", + "VW|MUL", + /* 48 */ + "VW|DIV", + "VV|MULF", + "VV|DIVF", + "VW|MOD", + /* 52 */ + "VV|MODF", + "VW|RANDOM", + NULL, + "I|SET_A_PARENT", + /* 56 */ + "IB|SET_CHILD2_BIT", + "IB|CLEAR_CHILD2_BIT", + "II|MAKE_SIBLING", + "I|INC_STATE", + /* 60 */ + "I|DEC_STATE", + "IW|SET_STATE", + "V|SHOW_INT", + "T|SHOW_STRING_NL", + /* 64 */ + "T|SHOW_STRING", + "WWWWWB|ADD_TEXT_BOX", + "BT|SET_SHORT_TEXT", + "BTw|SET_LONG_TEXT", + /* 68 */ + "x|END", + "x|DONE", + "V|SHOW_STRING_AR3", + "W|START_SUB", + /* 72 */ + NULL, + NULL, + NULL, + NULL, + /* 76 */ + "WW|ADD_TIMEOUT", + "J|IS_M1_EMPTY", + "J|IS_M3_EMPTY", + "ITJ|CHILD_FR2_IS", + /* 80 */ + "IIJ|IS_ITEM_EQ", + NULL, + "B|DEBUG", + "|RESCAN", + /* 84 */ + NULL, + NULL, + NULL, + "W|COMMENT", + /* 88 */ + "|STOP_ANIMATION", + "|RESTART_ANIMATION", + "IB|GET_PARENT", + "IB|GET_NEXT", + /* 92 */ + "IB|GET_CHILDREN", + NULL, + NULL, + NULL, + /* 96 */ + "WB|PICTURE", + "W|LOAD_ZONE", + "WWBWWW|ANIMATE", + "WW|STOP_ANIMATE", + /* 100 */ + "|KILL_ANIMATE", + "BWWWWWW|DEFINE_WINDOW", + "B|CHANGE_WINDOW", + "|CLS", + /* 104 */ + "B|CLOSE_WINDOW", + NULL, + NULL, + "WWWWWIW|ADD_BOX", + /* 108 */ + "W|DEL_BOX", + "W|ENABLE_BOX", + "W|DISABLE_BOX", + "WWW|MOVE_BOX", + /* 112 */ + NULL, + NULL, + "IB|DO_ICONS", + "IBJ|IS_CLASS", + /* 116 */ + "IB|SET_CLASS", + "IB|UNSET_CLASS", + NULL, + "W|WAIT_SYNC", + /* 120 */ + "W|SYNC", + "BI|DEF_OBJ", + "|ORACLE_TEXT_DOWN", + "|ORACLE_TEXT_UP", + /* 124 */ + "WJ|IF_TIME", + "IJ|IS_SIBLING_WITH_A", + "IBB|DO_CLASS_ICONS", + "WWB|PLAY_TUNE", + /* 128 */ + "W|WAIT_END_TUNE", + "W|IF_END_TUNE", + "Bww|SET_ADJ_NOUN", + "|SET_TIME", + /* 132 */ + "|SAVE_GAME", + "|LOAD_GAME", + "|LIST_SAVED_GAMES", + "|SWITCH_CD", + /* 136 */ + "IV|COPY_SF", + "B|RESTORE_ICONS", + "|FREEZE_ZONES", + "II|SET_PARENT_SPECIAL", + /* 140 */ + "|CLEAR_TIMERS", + "BI|SET_M1_OR_M3", + "WJ|IS_HITAREA_0x40_CLEAR", + "I|START_ITEM_SUB", + /* 144 */ + NULL, + NULL, + NULL, + NULL, + /* 148 */ + NULL, + NULL, + NULL, + "BI|SET_ARRAY6_TO", + /* 152 */ + "BB|SET_M1_M3_TO_ARRAY6", + "B|SET_BIT", + "B|CLEAR_BIT", + "BJ|IS_BIT_CLEAR", + /* 156 */ + "BJ|IS_BIT_SET", + "IBB|GET_ITEM_PROP", + "IBW|SET_ITEM_PROP", + NULL, + /* 160 */ + "B|SET_INK", + "BWWW|SETUP_TEXT", + "BBTW|PRINT_STR", + "W|PLAY_EFFECT", + /* 164 */ + "|getDollar2", + "IWWJ|IS_ADJ_NOUN", + "B|SET_BIT2", + "B|CLEAR_BIT2", + /* 168 */ + "BJ|IS_BIT2_CLEAR", + "BJ|IS_BIT2_SET", + NULL, + "W|HYPERLINK_ON", + /* 172 */ + "|HYPERLINK_OFF", + "|CHECK_PATHS", + NULL, + "|LOCK_ZONES", + /* 176 */ + "|UNLOCK_ZONES", + "BBI|SCREEN_TEXT_POBJ", + "WWBB|GETPATHPOSN", + "BBB|SCREEN_TEXT_LONG_TEXT", + /* 180 */ + "|MOUSE_ON", + "|MOUSE_OFF", + "T|LOAD_VIDEO", + "|PLAY_VIDEO", + /* 184 */ + "W|UNLOAD_ZONE", + NULL, + "|UNFREEZE_ZONES", + "|CENTRE_SCROLL", + /* 188 */ + "BSJ|STRING2_IS", + "|CLEAR_MARKS", + "B|WAIT_FOR_MARK", + "|RESET_PV_COUNT", + /* 192 */ + "BBBB|SET_PATH_VALUES", + "|STOP_CLOCK", + "|RESTART_CLOCK", + "BBBB|SET_COLOR", + /* 196 */ + "B|B3_SET", + "B|B3_CLEAR", + "B|B3_ZERO", + "B|B3_NOT_ZERO", +}; + +const char *const ww_video_opcode_name_table[] = { + /* 0 */ + "x|RET", + "ddd|FADEOUT", + "d|CALL", + "ddddd|NEW_SPRITE", + /* 4 */ + "ddd|FADEIN", + "vd|SKIP_IF_NEQ", + "d|SKIP_IFN_SIB_WITH_A", + "d|SKIP_IF_SIB_WITH_A", + /* 8 */ + "dd|SKIP_IF_PARENT_IS", + "dd|SKIP_IF_UNK3_IS", + "dddd|DRAW", + "d|VC_11", + /* 12 */ + "d|DELAY", + "d|SET_SPRITE_OFFSET_X", + "d|SET_SPRITE_OFFSET_Y", + "d|IDENT_WAKEUP", + /* 16 */ + "d|IDENT_SLEEP", + "d|VC_17", + "i|JUMP_REL", + "|CHAIN_TO", + /* 20 */ + "dd|SET_REPEAT", + "i|END_REPEAT", + "d|SET_PALETTE", + "d|SET_PRIORITY", + /* 24 */ + "diid|SET_SPRITE_XY", + "x|HALT_SPRITE", + "ddddd|SET_WINDOW", + "|RESET", + /* 28 */ + "dddd|PLAY_SOUND", + "|STOP_ALL_SOUNDS", + "d|SET_FRAME_RATE", + "d|SET_WINDOW", + /* 32 */ + "|VC_32", + "|MOUSE_ON", + "|MOUSE_OFF", + "dd|CLEAR_WINDOW", + /* 36 */ + "dd|SAVELOAD_THING", + "dd|VC_37", + "v|SKIP_IF_VAR_ZERO", + "vd|SET_VAR", + /* 40 */ + "vd|ADD_VAR", + "vd|SUB_VAR", + "vd|DELAY_IF_NOT_EQ", + "d|SKIP_IF_BIT_CLEAR", + /* 44 */ + "d|SKIP_IF_BIT_SET", + "dd|VC_45", + "v|SET_SPRITE_Y", + "d|VC_47", + /* 48 */ + "d|VC_48", + "d|SET_BIT", + "d|CLEAR_BIT", + "d|ENABLE_BOX", + /* 52 */ + "d|PLAY_EFFECT", + "dd|DUMMY_53", + "ddd|DUMMY_54", + "ddd|MOVE_BOX", + /* 56 */ + "|FULL_SCREEN", + "|BLACK_PALETTE", + "|SET_PRIORITIES", + "|SKIP_IF_NOT_EGA", + /* 60 */ + "d|STOP_ANIMATE", + "d|VC_61", + "|FASTFADEOUT", + "|FASTFADEIN", +}; + +const char *const simon1_video_opcode_name_table[] = { + /* 0 */ + "x|RET", + "ddd|FADEOUT", + "d|CALL", + "ddddd|NEW_SPRITE", + /* 4 */ + "ddd|FADEIN", + "vd|SKIP_IF_NEQ", + "d|SKIP_IFN_SIB_WITH_A", + "d|SKIP_IF_SIB_WITH_A", + /* 8 */ + "dd|SKIP_IF_PARENT_IS", + "dd|SKIP_IF_UNK3_IS", + "ddddd|DRAW", + "|CLEAR_PATHFIND_ARRAY", + /* 12 */ + "d|DELAY", + "d|SET_SPRITE_OFFSET_X", + "d|SET_SPRITE_OFFSET_Y", + "d|IDENT_WAKEUP", + /* 16 */ + "d|IDENT_SLEEP", + "dq|SET_PATHFIND_ITEM", + "i|JUMP_REL", + "|CHAIN_TO", + /* 20 */ + "dd|SET_REPEAT", + "i|END_REPEAT", + "dd|SET_PALETTE", + "d|SET_PRIORITY", + /* 24 */ + "diid|SET_SPRITE_XY", + "x|HALT_SPRITE", + "ddddd|SET_WINDOW", + "|RESET", + /* 28 */ + "dddd|PLAY_SOUND", + "|STOP_ALL_SOUNDS", + "d|SET_FRAME_RATE", + "d|SET_WINDOW", + /* 32 */ + "vv|COPY_VAR", + "|MOUSE_ON", + "|MOUSE_OFF", + "dd|CLEAR_WINDOW", + /* 36 */ + "dd|SAVELOAD_THING", + "v|SET_SPRITE_OFFSET_Y", + "v|SKIP_IF_VAR_ZERO", + "vd|SET_VAR", + /* 40 */ + "vd|ADD_VAR", + "vd|SUB_VAR", + "vd|DELAY_IF_NOT_EQ", + "d|SKIP_IF_BIT_CLEAR", + /* 44 */ + "d|SKIP_IF_BIT_SET", + "v|SET_SPRITE_X", + "v|SET_SPRITE_Y", + "vv|ADD_VAR_F", + /* 48 */ + "|COMPUTE_YOFS", + "d|SET_BIT", + "d|CLEAR_BIT", + "d|ENABLE_BOX", + /* 52 */ + "d|PLAY_EFFECT", + "dd|DUMMY_53", + "ddd|DUMMY_54", + "ddd|MOVE_BOX", + /* 56 */ + "|DUMMY_56", + "|BLACK_PALETTE", + "|SET_PRIORITIES", + "|SKIP_IF_VOICE", + /* 60 */ + "d|STOP_ANIMATE", + "ddd|MASK", + "|FASTFADEOUT", + "|FASTFADEIN", +}; + +const char *const simon2_video_opcode_name_table[] = { + /* 0 */ + "x|RET", + "ddd|FADEOUT", + "d|CALL", + "ddddd|NEW_SPRITE", + /* 4 */ + "ddd|FADEIN", + "vd|SKIP_IF_NEQ", + "d|SKIP_IFN_SIB_WITH_A", + "d|SKIP_IF_SIB_WITH_A", + /* 8 */ + "dd|SKIP_IF_PARENT_IS", + "dd|SKIP_IF_UNK3_IS", + "ddddb|DRAW", + "|CLEAR_PATHFIND_ARRAY", + /* 12 */ + "b|DELAY", + "d|SET_SPRITE_OFFSET_X", + "d|SET_SPRITE_OFFSET_Y", + "d|IDENT_WAKEUP", + /* 16 */ + "d|IDENT_SLEEP", + "dq|SET_PATHFIND_ITEM", + "i|JUMP_REL", + "|CHAIN_TO", + /* 20 */ + "dd|SET_REPEAT", + "i|END_REPEAT", + "dd|SET_PALETTE", + "d|SET_PRIORITY", + /* 24 */ + "diib|SET_SPRITE_XY", + "x|HALT_SPRITE", + "ddddd|SET_WINDOW", + "|RESET", + /* 28 */ + "dddd|PLAY_SOUND", + "|STOP_ALL_SOUNDS", + "d|SET_FRAME_RATE", + "d|SET_WINDOW", + /* 32 */ + "vv|COPY_VAR", + "|MOUSE_ON", + "|MOUSE_OFF", + "dd|CLEAR_WINDOW", + /* 36 */ + "dd|SAVELOAD_THING", + "v|SET_SPRITE_OFFSET_Y", + "v|SKIP_IF_VAR_ZERO", + "vd|SET_VAR", + /* 40 */ + "vd|ADD_VAR", + "vd|SUB_VAR", + "vd|DELAY_IF_NOT_EQ", + "d|SKIP_IF_BIT_CLEAR", + /* 44 */ + "d|SKIP_IF_BIT_SET", + "v|SET_SPRITE_X", + "v|SET_SPRITE_Y", + "vv|ADD_VAR_F", + /* 48 */ + "|COMPUTE_YOFS", + "d|SET_BIT", + "d|CLEAR_BIT", + "d|ENABLE_BOX", + /* 52 */ + "d|PLAY_EFFECT", + "dd|DUMMY_53", + "ddd|DUMMY_54", + "ddd|MOVE_BOX", + /* 56 */ + "i|WAIT_BIG", + "|BLACK_PALETTE", + "|SET_PRIORITIES", + "ddd|STOP_ANIMATIONS", + /* 60 */ + "dd|STOP_ANIMATE", + "ddd|MASK", + "|FASTFADEOUT", + "|FASTFADEIN", + /* 64 */ + "|SKIP_IF_VOICE", + "|SLOW_FADE_IN", + "|SKIP_IF_NZ", + "|SKIP_IF_GE", + /* 68 */ + "|SKIP_IF_LE", + "dd|PLAY_TRACK", + "dd|QUEUE_MUSIC", + "|CHECK_MUSIC_QUEUE", + /* 72 */ + "dd|PLAY_TRACK_2", + "bb|SET_MARK", + "bb|CLEAR_MARK", +}; + +const char *const feeblefiles_video_opcode_name_table[] = { + /* 0 */ + "x|RET", + "ddd|FADEOUT", + "d|CALL", + "ddddd|NEW_SPRITE", + /* 4 */ + "ddd|FADEIN", + "vd|SKIP_IF_NEQ", + "d|SKIP_IFN_SIB_WITH_A", + "d|SKIP_IF_SIB_WITH_A", + /* 8 */ + "dd|SKIP_IF_PARENT_IS", + "dd|SKIP_IF_UNK3_IS", + "ddddb|DRAW", + "|CLEAR_PATHFIND_ARRAY", + /* 12 */ + "b|DELAY", + "d|SET_SPRITE_OFFSET_X", + "d|SET_SPRITE_OFFSET_Y", + "d|IDENT_WAKEUP", + /* 16 */ + "d|IDENT_SLEEP", + "dq|SET_PATHFIND_ITEM", + "i|JUMP_REL", + "|CHAIN_TO", + /* 20 */ + "dd|SET_REPEAT", + "i|END_REPEAT", + "dd|SET_PALETTE", + "d|SET_PRIORITY", + /* 24 */ + "diib|SET_SPRITE_XY", + "x|HALT_SPRITE", + "ddddd|SET_WINDOW", + "|RESET", + /* 28 */ + "dddd|PLAY_SOUND", + "|STOP_ALL_SOUNDS", + "d|SET_FRAME_RATE", + "d|SET_WINDOW", + /* 32 */ + "vv|COPY_VAR", + "|MOUSE_ON", + "|MOUSE_OFF", + "dd|CLEAR_WINDOW", + /* 36 */ + "dd|SAVELOAD_THING", + "v|SET_SPRITE_OFFSET_Y", + "v|SKIP_IF_VAR_ZERO", + "vd|SET_VAR", + /* 40 */ + "vd|ADD_VAR", + "vd|SUB_VAR", + "vd|DELAY_IF_NOT_EQ", + "d|SKIP_IF_BIT_CLEAR", + /* 44 */ + "d|SKIP_IF_BIT_SET", + "v|SET_SPRITE_X", + "v|SET_SPRITE_Y", + "vv|ADD_VAR_F", + /* 48 */ + "|COMPUTE_YOFS", + "d|SET_BIT", + "d|CLEAR_BIT", + "d|ENABLE_BOX", + /* 52 */ + "ddd|PLAY_EFFECT", + "ddd|PAN_SFX", + "ddd|DUMMY_54", + "ddd|MOVE_BOX", + /* 56 */ + "i|WAIT_BIG", + "|BLACK_PALETTE", + "|SET_PRIORITIES", + "ddd|STOP_ANIMATIONS", + /* 60 */ + "dd|STOP_ANIMATE", + "ddd|MASK", + "|FASTFADEOUT", + "|FASTFADEIN", + /* 64 */ + "|SKIP_IF_VOICE", + "|SLOW_FADE_IN", + "|SKIP_IF_NZ", + "|SKIP_IF_GE", + /* 68 */ + "|SKIP_IF_LE", + "dd|PLAY_TRACK", + "dd|QUEUE_MUSIC", + "|CHECK_MUSIC_QUEUE", + /* 72 */ + "dd|PLAY_TRACK_2", + "bb|SET_MARK", + "bb|CLEAR_MARK", + "dd|SETSCALE", + /* 76 */ + "ddd|SETSCALEXOFFS", + "ddd|SETSCALEYOFFS", + "|COMPUTEXY", + "|COMPUTEPOSNUM", + /* 80 */ + "ddd|SETOVERLAYIMAGE", + "dd|SETRANDOM", + "d|GETPATHVALUE", + "ddd|PLAYSOUNDLOOP", + "|STOPSOUNDLOOP", +}; + +} // End of namespace Simon + +#endif + diff --git a/engines/agos/debugger.cpp b/engines/agos/debugger.cpp new file mode 100644 index 0000000000..8fc4702d1e --- /dev/null +++ b/engines/agos/debugger.cpp @@ -0,0 +1,179 @@ +/* ScummVM - Scumm Interpreter + * Copyright (C) 2001 Ludvig Strigeus + * Copyright (C) 2001-2006 The ScummVM project + * + * 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. + * + * $URL$ + * $Id$ + * + */ + +#include "common/stdafx.h" + +#include "common/config-manager.h" + +#include "agos/debugger.h" +#include "agos/agos.h" + +namespace Simon { + +Debugger::Debugger(SimonEngine *vm) + : GUI::Debugger() { + _vm = vm; + + DCmd_Register("continue", WRAP_METHOD(Debugger, Cmd_Exit)); + DCmd_Register("level", WRAP_METHOD(Debugger, Cmd_DebugLevel)); + DCmd_Register("music", WRAP_METHOD(Debugger, Cmd_PlayMusic)); + DCmd_Register("sound", WRAP_METHOD(Debugger, Cmd_PlaySound)); + DCmd_Register("voice", WRAP_METHOD(Debugger, Cmd_PlayVoice)); + DCmd_Register("bit", WRAP_METHOD(Debugger, Cmd_SetBit)); + DCmd_Register("var", WRAP_METHOD(Debugger, Cmd_SetVar)); + DCmd_Register("sub", WRAP_METHOD(Debugger, Cmd_StartSubroutine)); + +} + + +void Debugger::preEnter() { + //_vm->midi.pause(1); +} + + +void Debugger::postEnter() { + //_vm->midi.pause(0); +} + + +bool Debugger::Cmd_DebugLevel(int argc, const char **argv) { + if (argc == 1) { + if (_vm->_debugMode == false) + DebugPrintf("Debugging is not enabled at this time\n"); + else + DebugPrintf("Debugging is currently set at level %d\n", gDebugLevel); + } else { // set level + gDebugLevel = atoi(argv[1]); + if (gDebugLevel >= 0 && gDebugLevel < 10) { + _vm->_debugMode = true; + DebugPrintf("Debug level set to level %d\n", gDebugLevel); + } else if (gDebugLevel < 0) { + _vm->_debugMode = false; + DebugPrintf("Debugging is now disabled\n"); + } else + DebugPrintf("Not a valid debug level (0 - 10)\n"); + } + + return true; +} + +bool Debugger::Cmd_PlayMusic(int argc, const char **argv) { + if (argc > 1) { + uint music = atoi(argv[1]); + uint range = (_vm->getGameType() == GType_SIMON2) ? 93 : 34; + if (music <= range) { + _vm->loadMusic (music); + if (_vm->getGameType() == GType_SIMON2) + _vm->midi.startTrack (0); + } else + DebugPrintf("Music out of range (0 - %d)\n", range); + } else + DebugPrintf("Syntax: music <musicnum>\n"); + + return true; +} + +bool Debugger::Cmd_PlaySound(int argc, const char **argv) { + if (argc > 1) { + uint sound = atoi(argv[1]); + uint range = (_vm->getGameType() == GType_SIMON2) ? 222 : 127; + if (sound <= range) + _vm->_sound->playEffects(sound); + else + DebugPrintf("Sound out of range (0 - %d)\n", range); + } else + DebugPrintf("Syntax: sound <soundnum>\n"); + + return true; +} + +bool Debugger::Cmd_PlayVoice(int argc, const char **argv) { + if (argc > 1) { + uint voice = atoi(argv[1]); + uint range = (_vm->getGameType() == GType_SIMON2) ? 3632 : 1996; + if (voice <= range) + _vm->_sound->playVoice(voice); + else + DebugPrintf("Voice out of range (0 - %d)\n", range); + } else + DebugPrintf("Syntax: voice <voicenum>\n"); + + return true; +} + +bool Debugger::Cmd_SetBit(int argc, const char **argv) { + uint bit, value; + if (argc > 2) { + bit = atoi(argv[1]); + value = atoi(argv[2]); + if (value <= 1) { + _vm->setBitFlag(bit, value != 0); + DebugPrintf("Set bit %d to %d\n", bit, value); + } else + DebugPrintf("Bit value out of range (0 - 1)\n"); + } else if (argc > 1) { + bit = atoi(argv[1]); + value = _vm->getBitFlag(bit); + DebugPrintf("Bit %d is %d\n", bit, value); + } else + DebugPrintf("Syntax: bit <bitnum> <value>\n"); + + return true; +} + +bool Debugger::Cmd_SetVar(int argc, const char **argv) { + uint var, value; + if (argc > 1) { + var = atoi(argv[1]); + if (var <= 254) { + if (argc > 2) { + value = atoi(argv[2]); + _vm->writeVariable(var, value); + DebugPrintf("Set var %d to %d\n", var, value); + } else { + value = _vm->readVariable(var); + DebugPrintf("Var %d is %d\n", var, value); + } + } else + DebugPrintf("Var out of range (0 - 254)\n"); + } else + DebugPrintf("Syntax: var <varnum> <value>\n"); + + return true; +} + +bool Debugger::Cmd_StartSubroutine(int argc, const char **argv) { + if (argc > 1) { + uint subroutine = atoi(argv[1]); + Subroutine *sub; + sub = _vm->getSubroutineByID(subroutine); + if (sub != NULL) + _vm->startSubroutine(sub); + } else + DebugPrintf("Subroutine %d\n", _vm->_subroutine); + + return true; +} + +} // End of namespace Simon + diff --git a/engines/agos/debugger.h b/engines/agos/debugger.h new file mode 100644 index 0000000000..cd43f3e1eb --- /dev/null +++ b/engines/agos/debugger.h @@ -0,0 +1,55 @@ +/* ScummVM - Scumm Interpreter + * Copyright (C) 2001 Ludvig Strigeus + * Copyright (C) 2001-2006 The ScummVM project + * + * 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. + * + * $URL$ + * $Id$ + * + */ + +#ifndef AGOS_DEBUGGER_H +#define AGOS_DEBUGGER_H + +#include "gui/debugger.h" + +namespace Simon { + +class SimonEngine; + +class Debugger : public GUI::Debugger { +public: + Debugger(SimonEngine *vm); + virtual ~Debugger() {} // we need this for __SYMBIAN32__ archaic gcc/UIQ + +protected: + SimonEngine *_vm; + + virtual void preEnter(); + virtual void postEnter(); + + bool Cmd_DebugLevel(int argc, const char **argv); + bool Cmd_PlayMusic(int argc, const char **argv); + bool Cmd_PlaySound(int argc, const char **argv); + bool Cmd_PlayVoice(int argc, const char **argv); + bool Cmd_SetBit(int argc, const char **argv); + bool Cmd_SetVar(int argc, const char **argv); + bool Cmd_StartSubroutine(int argc, const char **argv); +}; + +} // End of namespace Simon + +#endif diff --git a/engines/agos/draw.cpp b/engines/agos/draw.cpp new file mode 100644 index 0000000000..ab65cd0af2 --- /dev/null +++ b/engines/agos/draw.cpp @@ -0,0 +1,512 @@ +/* ScummVM - Scumm Interpreter + * Copyright (C) 2001 Ludvig Strigeus + * Copyright (C) 2001-2006 The ScummVM project + * + * 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. + * + * $URL$ + * $Id$ + * + */ + +#include "common/stdafx.h" + +#include "common/system.h" + +#include "agos/agos.h" +#include "agos/intern.h" + +namespace Simon { + +byte *SimonEngine::getFrontBuf() { + _dxSurfacePitch = _screenWidth; + return _frontBuf; +} + +byte *SimonEngine::getBackBuf() { + _dxSurfacePitch = _screenWidth; + return _useBackGround ? _backGroundBuf : _backBuf; +} + +byte *SimonEngine::getBackGround() { + _dxSurfacePitch = _screenWidth; + return _backGroundBuf; +} + +byte *SimonEngine::getScaleBuf() { + _dxSurfacePitch = _screenWidth; + return _scaleBuf; +} + +void SimonEngine::animateSprites() { + VgaSprite *vsp; + VgaPointersEntry *vpe; + const byte *vc_ptr_org = _vcPtr; + uint16 params[5]; // parameters to vc10 + + if (_paletteFlag == 2) + _paletteFlag = 1; + + if (getGameType() == GType_FF && _scrollCount) { + scrollEvent(); + } + if (getGameType() == GType_SIMON2 && _scrollFlag) { + scrollScreen(); + } + + if (getGameType() == GType_FF && getBitFlag(84)) { + animateSpritesByY(); + return; + } + + vsp = _vgaSprites; + + while (vsp->id != 0) { + vsp->windowNum &= 0x7FFF; + + vpe = &_vgaBufferPointers[vsp->zoneNum]; + _curVgaFile1 = vpe->vgaFile1; + _curVgaFile2 = vpe->vgaFile2; + _curSfxFile = vpe->sfxFile; + _windowNum = vsp->windowNum; + _vgaCurSpriteId = vsp->id; + _vgaCurSpritePriority = vsp->priority; + + params[0] = readUint16Wrapper(&vsp->image); + if (getGameType() == GType_WW) { + params[1] = readUint16Wrapper(&vsp->x); + params[2] = readUint16Wrapper(&vsp->y); + params[3] = READ_BE_UINT16(&vsp->flags); + } else { + params[1] = readUint16Wrapper(&vsp->palette); + params[2] = readUint16Wrapper(&vsp->x); + params[3] = readUint16Wrapper(&vsp->y); + + if (getGameType() == GType_SIMON1) { + params[4] = READ_BE_UINT16(&vsp->flags); + } else { + *(byte *)(¶ms[4]) = (byte)vsp->flags; + } + } + + _vcPtr = (const byte *)params; + vc10_draw(); + + vsp++; + } + + if (_drawImagesDebug) + memset(_backBuf, 0, _screenWidth * _screenHeight); + + _updateScreen = true; + _vcPtr = vc_ptr_org; +} + +void SimonEngine::animateSpritesDebug() { + VgaSprite *vsp; + VgaPointersEntry *vpe; + const byte *vc_ptr_org = _vcPtr; + uint16 params[5]; // parameters to vc10_draw + + if (_paletteFlag == 2) + _paletteFlag = 1; + + vsp = _vgaSprites; + while (vsp->id != 0) { + vsp->windowNum &= 0x7FFF; + + vpe = &_vgaBufferPointers[vsp->zoneNum]; + _curVgaFile1 = vpe->vgaFile1; + _curVgaFile2 = vpe->vgaFile2; + _curSfxFile = vpe->sfxFile; + _windowNum = vsp->windowNum; + _vgaCurSpriteId = vsp->id; + + if (vsp->image) + printf("id:%5d image:%3d base-color:%3d x:%3d y:%3d flags:%x\n", + vsp->id, vsp->image, vsp->palette, vsp->x, vsp->y, vsp->flags); + params[0] = readUint16Wrapper(&vsp->image); + if (getGameType() == GType_WW) { + params[1] = readUint16Wrapper(&vsp->x); + params[2] = readUint16Wrapper(&vsp->y); + params[3] = READ_BE_UINT16(&vsp->flags); + } else { + params[1] = readUint16Wrapper(&vsp->palette); + params[2] = readUint16Wrapper(&vsp->x); + params[3] = readUint16Wrapper(&vsp->y); + + if (getGameType() == GType_SIMON1) { + params[4] = READ_BE_UINT16(&vsp->flags); + } else { + *(byte *)(¶ms[4]) = (byte)vsp->flags; + } + } + + _vcPtr = (const byte *)params; + vc10_draw(); + + vsp++; + } + + _updateScreen = true; + _vcPtr = vc_ptr_org; +} + +void SimonEngine::animateSpritesByY() { + VgaSprite *vsp; + VgaPointersEntry *vpe; + const byte *vc_ptr_org = _vcPtr; + uint16 params[5]; // parameters to vc10 + int16 spriteTable[180][2]; + + byte *src; + int height, slot, y; + uint i, numSprites = 0; + + vsp = _vgaSprites; + while (vsp->id != 0) { + if (vsp->flags & kDFScaled) { + y = vsp->y; + } else if (vsp->flags & kDFMasked) { + vpe = &_vgaBufferPointers[vsp->zoneNum]; + src = vpe->vgaFile2 + vsp->image * 8; + height = READ_LE_UINT16(src + 4) & 0x7FFF; + y = vsp->y + height; + } else { + y = vsp->priority; + } + + spriteTable[numSprites][0] = y; + spriteTable[numSprites][1] = numSprites; + numSprites++; + vsp++; + } + + while (1) { + y = spriteTable[0][0]; + slot = spriteTable[0][1]; + + for (i = 0; i < numSprites; i++) { + if (y >= spriteTable[i][0]) { + y = spriteTable[i][0]; + slot = spriteTable[i][1]; + } + } + + if (y == 9999) + break; + + for (i = 0; i < numSprites; i++) { + if (slot == spriteTable[i][1]) { + spriteTable[i][0] = 9999; + break; + } + } + + vsp = &_vgaSprites[slot]; + vsp->windowNum &= 0x7FFF; + + vpe = &_vgaBufferPointers[vsp->zoneNum]; + _curVgaFile1 = vpe->vgaFile1; + _curVgaFile2 = vpe->vgaFile2; + _curSfxFile = vpe->sfxFile; + _windowNum = vsp->windowNum; + _vgaCurSpriteId = vsp->id; + _vgaCurSpritePriority = vsp->priority; + + params[0] = readUint16Wrapper(&vsp->image); + params[1] = readUint16Wrapper(&vsp->palette); + params[2] = readUint16Wrapper(&vsp->x); + params[3] = readUint16Wrapper(&vsp->y); + *(byte *)(¶ms[4]) = (byte)vsp->flags; + + _vcPtr = (const byte *)params; + vc10_draw(); + } + + _updateScreen = true; + _vcPtr = vc_ptr_org; +} + +void SimonEngine::displayBoxStars() { + HitArea *ha, *dha; + uint count; + uint y_, x_; + byte *dst; + uint b, color; + + _lockWord |= 0x8000; + + if (getGameType() == GType_SIMON2) + color = 236; + else + color = 225; + + uint limit = (getGameType() == GType_SIMON2) ? 200 : 134; + + for (int i = 0; i < 5; i++) { + ha = _hitAreas; + count = ARRAYSIZE(_hitAreas); + + animateSprites(); + + do { + if (ha->id != 0 && ha->flags & kBFBoxInUse && !(ha->flags & kBFBoxDead)) { + + dha = _hitAreas; + if (ha->flags & kBFTextBox) { + while (dha != ha && dha->flags != ha->flags) + ++dha; + if (dha != ha && dha->flags == ha->flags) + continue; + } else { + dha = _hitAreas; + while (dha != ha && dha->item_ptr != ha->item_ptr) + ++dha; + if (dha != ha && dha->item_ptr == ha->item_ptr) + continue; + } + + if (ha->y >= limit || ((getGameType() == GType_SIMON2) && ha->y >= _boxStarHeight)) + continue; + + y_ = (ha->height / 2) - 4 + ha->y; + + x_ = (ha->width / 2) - 4 + ha->x - (_scrollX * 8); + + if (x_ >= 311) + continue; + + dst = getBackBuf(); + + dst += (((_dxSurfacePitch / 4) * y_) * 4) + x_; + + b = _dxSurfacePitch; + dst[4] = color; + dst[b+1] = color; + dst[b+4] = color; + dst[b+7] = color; + b += _dxSurfacePitch; + dst[b+2] = color; + dst[b+4] = color; + dst[b+6] = color; + b += _dxSurfacePitch; + dst[b+3] = color; + dst[b+5] = color; + b += _dxSurfacePitch; + dst[b] = color; + dst[b+1] = color; + dst[b+2] = color; + dst[b+6] = color; + dst[b+7] = color; + dst[b+8] = color; + b += _dxSurfacePitch; + dst[b+3] = color; + dst[b+5] = color; + b += _dxSurfacePitch; + dst[b+2] = color; + dst[b+4] = color; + dst[b+6] = color; + b += _dxSurfacePitch; + dst[b+1] = color; + dst[b+4] = color; + dst[b+7] = color; + b += _dxSurfacePitch; + dst[b+4] = color; + } + } while (ha++, --count); + + dx_update_screen_and_palette(); + delay(100); + animateSprites(); + dx_update_screen_and_palette(); + delay(100); + } + + _lockWord &= ~0x8000; +} + +void SimonEngine::scrollScreen() { + byte *dst = getFrontBuf(); + const byte *src; + uint x, y; + + if (_scrollXMax == 0) { + uint screenSize = 8 * _screenWidth; + if (_scrollFlag < 0) { + memmove(dst + screenSize, dst, _scrollWidth * _screenHeight - screenSize); + } else { + memmove(dst, dst + screenSize, _scrollWidth * _screenHeight - screenSize); + } + + y = _scrollY - 8; + + if (_scrollFlag > 0) { + dst += _screenHeight * _screenWidth - screenSize; + y += 488; + } + + src = _scrollImage + y / 2; + decodeRow(dst, src + readUint32Wrapper(src), _scrollWidth); + + _scrollY += _scrollFlag; + vcWriteVar(250, _scrollY); + + memcpy(_backBuf, _frontBuf, _screenWidth * _screenHeight); + memcpy(_backGroundBuf, _backBuf, _screenHeight * _scrollWidth); + } else { + if (_scrollFlag < 0) { + memmove(dst + 8, dst, _screenWidth * _scrollHeight - 8); + } else { + memmove(dst, dst + 8, _screenWidth * _scrollHeight - 8); + } + + x = _scrollX; + x -= (getGameType() == GType_FF) ? 8 : 1; + + if (_scrollFlag > 0) { + dst += _screenWidth - 8; + x += (getGameType() == GType_FF) ? 648 : 41; + } + + if (getGameType() == GType_FF) + src = _scrollImage + x / 2; + else + src = _scrollImage + x * 4; + decodeColumn(dst, src + readUint32Wrapper(src), _scrollHeight); + + _scrollX += _scrollFlag; + vcWriteVar(251, _scrollX); + + memcpy(_backBuf, _frontBuf, _screenWidth * _screenHeight); + memcpy(_backGroundBuf, _backBuf, _scrollHeight * _screenWidth); + } + + _scrollFlag = 0; +} + +void SimonEngine::clearBackFromTop(uint lines) { + memset(_backBuf, 0, lines * _screenWidth); +} + +void SimonEngine::dx_clear_surfaces(uint num_lines) { + memset(_backBuf, 0, num_lines * _screenWidth); + + _system->copyRectToScreen(_backBuf, _screenWidth, 0, 0, _screenWidth, num_lines); + + if (_useBackGround) { + memset(_frontBuf, 0, num_lines * _screenWidth); + memset(_backGroundBuf, 0, num_lines * _screenWidth); + } +} + +void SimonEngine::fillFrontFromBack(uint x, uint y, uint w, uint h) { + uint offs = x + y * _screenWidth; + byte *s = _backBuf + offs; + byte *d = _frontBuf + offs; + + do { + memcpy(d, s, w); + d += _screenWidth; + s += _screenWidth; + } while (--h); +} + +void SimonEngine::fillBackFromFront(uint x, uint y, uint w, uint h) { + uint offs = x + y * _screenWidth; + byte *s = _frontBuf + offs; + byte *d = _backBuf + offs; + + do { + memcpy(d, s, w); + d += _screenWidth; + s += _screenWidth; + } while (--h); +} + +void SimonEngine::fillBackGroundFromBack(uint lines) { + memcpy(_backGroundBuf, _backBuf, lines * _screenWidth); +} + +void SimonEngine::dx_update_screen_and_palette() { + if (_fastFadeInFlag == 0 && _paletteFlag == 1) { + _paletteFlag = 0; + if (memcmp(_displayPalette, _currentPalette, 1024)) { + memcpy(_currentPalette, _displayPalette, 1024); + _system->setPalette(_displayPalette, 0, 256); + } + } + + _system->copyRectToScreen(_backBuf, _screenWidth, 0, 0, _screenWidth, _screenHeight); + _system->updateScreen(); + + memcpy(_backBuf, _frontBuf, _screenWidth * _screenHeight); + + if (getGameType() == GType_FF && _scrollFlag) { + scrollScreen(); + } + + if (_fastFadeInFlag) { + if (getGameType() == GType_SIMON1 && _usePaletteDelay) { + delay(100); + _usePaletteDelay = false; + } + fastFadeIn(); + } +} + +void SimonEngine::fastFadeIn() { + if (_fastFadeInFlag & 0x8000) { + slowFadeIn(); + } else { + _paletteFlag = false; + memcpy(_currentPalette, _displayPalette, 1024); + _system->setPalette(_displayPalette, 0, _fastFadeInFlag); + _fastFadeInFlag = 0; + } +} + +void SimonEngine::slowFadeIn() { + uint8 *src, *dst; + int c, p; + + _fastFadeInFlag &= 0x7fff; + _paletteFlag = false; + + memset(_videoBuf1, 0, 1024); + memcpy(_currentPalette, _displayPalette, 1024); + memcpy(_videoBuf1 + 1024, _displayPalette, 1024); + + for (c = 255; c >= 0; c -= 4) { + src = _videoBuf1 + 1024; + dst = _videoBuf1; + + for (p = _fastFadeInFlag; p !=0 ; p -= 3) { + if (src[0] >= c) + dst[0] += 4; + if (src[1] >= c) + dst[1] += 4; + if (src[2] >= c) + dst[2] += 4; + src += 4; + dst += 4; + } + _system->setPalette(_videoBuf1, 0, _fastFadeCount); + delay(5); + } + _fastFadeInFlag = 0; +} + +} // End of namespace Simon diff --git a/engines/agos/event.cpp b/engines/agos/event.cpp new file mode 100644 index 0000000000..4755dea987 --- /dev/null +++ b/engines/agos/event.cpp @@ -0,0 +1,376 @@ +/* ScummVM - Scumm Interpreter + * Copyright (C) 2001 Ludvig Strigeus + * Copyright (C) 2001-2006 The ScummVM project + * + * 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. + * + * $URL$ + * $Id$ + * + */ + +#include "common/stdafx.h" + +#include "agos/agos.h" +#include "agos/intern.h" + +namespace Simon { + +void SimonEngine::addTimeEvent(uint timeout, uint subroutine_id) { + TimeEvent *te = (TimeEvent *)malloc(sizeof(TimeEvent)), *first, *last = NULL; + time_t cur_time; + + time(&cur_time); + + te->time = cur_time + timeout - _gameStoppedClock; + if (_clockStopped) + te->time -= ((uint32)time(NULL) - _clockStopped); + te->subroutine_id = subroutine_id; + + first = _firstTimeStruct; + while (first) { + if (te->time <= first->time) { + if (last) { + last->next = te; + te->next = first; + return; + } + te->next = _firstTimeStruct; + _firstTimeStruct = te; + return; + } + + last = first; + first = first->next; + } + + if (last) { + last->next = te; + te->next = NULL; + } else { + _firstTimeStruct = te; + te->next = NULL; + } +} + +void SimonEngine::delTimeEvent(TimeEvent *te) { + TimeEvent *cur; + + if (te == _pendingDeleteTimeEvent) + _pendingDeleteTimeEvent = NULL; + + if (te == _firstTimeStruct) { + _firstTimeStruct = te->next; + free(te); + return; + } + + cur = _firstTimeStruct; + if (cur == NULL) + error("delTimeEvent: none available"); + + for (;;) { + if (cur->next == NULL) + error("delTimeEvent: no such te"); + if (te == cur->next) { + cur->next = te->next; + free(te); + return; + } + cur = cur->next; + } +} + +void SimonEngine::invokeTimeEvent(TimeEvent *te) { + Subroutine *sub; + + _scriptVerb = 0; + + if (_runScriptReturn1) + return; + + sub = getSubroutineByID(te->subroutine_id); + if (sub != NULL) + startSubroutineEx(sub); + + _runScriptReturn1 = false; +} + +void SimonEngine::killAllTimers() { + TimeEvent *cur, *next; + + for (cur = _firstTimeStruct; cur; cur = next) { + next = cur->next; + delTimeEvent(cur); + } +} + +bool SimonEngine::kickoffTimeEvents() { + time_t cur_time; + TimeEvent *te; + bool result = false; + + if (_clockStopped) + return result; + + time(&cur_time); + cur_time -= _gameStoppedClock; + + while ((te = _firstTimeStruct) != NULL && te->time <= (uint32)cur_time) { + result = true; + _pendingDeleteTimeEvent = te; + invokeTimeEvent(te); + if (_pendingDeleteTimeEvent) { + _pendingDeleteTimeEvent = NULL; + delTimeEvent(te); + } + } + + return result; +} + +void SimonEngine::addVgaEvent(uint16 num, const byte *code_ptr, uint16 cur_sprite, uint16 curZoneNum, int32 param) { + VgaTimerEntry *vte; + + // When Simon talks to the Golum about stew in French version of + // Simon the Sorcerer 1 the code_ptr is at wrong location for + // sprite 200. This was a bug in the original game, which + // caused several glitches in this scene. + // We work around the problem by correcting the code_ptr for sprite + // 200 in this scene, if it is wrong. + if (getGameType() == GType_SIMON1 && _language == Common::FR_FRA && + (code_ptr - _vgaBufferPointers[curZoneNum].vgaFile1 == 4) && (cur_sprite == 200) && (curZoneNum == 2)) + code_ptr += 0x66; + + _lockWord |= 1; + + for (vte = _vgaTimerList; vte->delay; vte++) { + } + + vte->delay = num; + vte->script_pointer = code_ptr; + vte->sprite_id = cur_sprite; + vte->cur_vga_file = curZoneNum; + vte->param = param; + + _lockWord &= ~1; +} + +void SimonEngine::deleteVgaEvent(VgaTimerEntry * vte) { + _lockWord |= 1; + + if (vte + 1 <= _nextVgaTimerToProcess) { + _nextVgaTimerToProcess--; + } + + do { + memcpy(vte, vte + 1, sizeof(VgaTimerEntry)); + vte++; + } while (vte->delay); + + _lockWord &= ~1; +} + +void SimonEngine::processVgaEvents() { + VgaTimerEntry *vte = _vgaTimerList; + uint timer = (getGameType() == GType_FF) ? 5 : 1; + + _vgaTickCounter++; + + while (vte->delay) { + vte->delay -= timer; + if (vte->delay <= 0) { + uint16 curZoneNum = vte->cur_vga_file; + uint16 cur_sprite = vte->sprite_id; + const byte *script_ptr = vte->script_pointer; + int32 param = vte->param; + + _nextVgaTimerToProcess = vte + 1; + deleteVgaEvent(vte); + + if (getGameType() == GType_FF && script_ptr == NULL) { + panEvent(curZoneNum, cur_sprite, param); + } else if (getGameType() == GType_SIMON2 && script_ptr == NULL) { + scrollEvent(); + } else { + animateEvent(script_ptr, curZoneNum, cur_sprite); + } + vte = _nextVgaTimerToProcess; + } else { + vte++; + } + } +} + +void SimonEngine::animateEvent(const byte *code_ptr, uint16 curZoneNum, uint16 cur_sprite) { + VgaPointersEntry *vpe; + + _vgaCurSpriteId = cur_sprite; + + _vgaCurZoneNum = curZoneNum; + _zoneNumber = curZoneNum; + vpe = &_vgaBufferPointers[curZoneNum]; + + _curVgaFile1 = vpe->vgaFile1; + _curVgaFile2 = vpe->vgaFile2; + _curSfxFile = vpe->sfxFile; + + _vcPtr = code_ptr; + + runVgaScript(); +} + +void SimonEngine::panEvent(uint16 curZoneNum, uint16 cur_sprite, int32 param) { + _vgaCurSpriteId = cur_sprite; + _vgaCurZoneNum = curZoneNum; + + VgaSprite *vsp = findCurSprite(); + + param &= 0x10; + + int32 pan = (vsp->x - _scrollX + param) * 8 - 2560; + if (pan < -10000) + pan = -10000; + if (pan > 10000) + pan = 10000; + + //setSfxPan(param, pan); + + if (pan != 0) + addVgaEvent(10, NULL, _vgaCurSpriteId, _vgaCurZoneNum); /* pan event */ + debug(0, "panEvent: param %d pan %d", param, pan); +} + +void SimonEngine::scrollEvent() { + if (_scrollCount == 0) + return; + + if (getGameType() == GType_FF) { + if (_scrollCount < 0) { + if (_scrollFlag != -8) { + _scrollFlag = -8; + _scrollCount += 8; + } + } else { + if (_scrollFlag != 8) { + _scrollFlag = 8; + _scrollCount -= 8; + } + } + } else { + if (_scrollCount < 0) { + if (_scrollFlag != -1) { + _scrollFlag = -1; + if (++_scrollCount == 0) + return; + } + } else { + if (_scrollFlag != 1) { + _scrollFlag = 1; + if (--_scrollCount == 0) + return; + } + } + + addVgaEvent(6, NULL, 0, 0); /* scroll event */ + } +} + +void SimonEngine::timer_callback() { + if (_timer5 != 0) { + _syncFlag2 = true; + _timer5--; + } else { + timer_proc1(); + } +} + +void SimonEngine::timer_proc1() { + _timer4++; + + if (_lockWord & 0x80E9 || _lockWord & 2) + return; + + _syncCount++; + + _lockWord |= 2; + + if (!(_lockWord & 0x10)) { + if (getGameType() == GType_FF) { + _syncFlag2 ^= 1; + if (!_syncFlag2) { + processVgaEvents(); + } else { + // Double speed on Oracle + if (getBitFlag(99)) { + processVgaEvents(); + } else if (_scrollCount == 0) { + _lockWord &= ~2; + return; + } + } + } else { + processVgaEvents(); + processVgaEvents(); + _syncFlag2 ^= 1; + _cepeFlag ^= 1; + if (!_cepeFlag) + processVgaEvents(); + + if (_mouseHideCount != 0 && _syncFlag2) { + _lockWord &= ~2; + return; + } + } + } + + if (getGameType() == GType_FF) + _moviePlay->nextFrame(); + + animateSprites(); + if (_drawImagesDebug) + animateSpritesDebug(); + + if (_copyPartialMode == 1) { + fillBackFromFront(80, 46, 208 - 80, 94 - 46); + } + + if (_copyPartialMode == 2) { + if (getGameType() == GType_FF) { + fillFrontFromBack(0, 0, _screenWidth, _screenHeight); + } else { + fillFrontFromBack(176, 61, _screenWidth - 176, 134 - 61); + } + _copyPartialMode = 0; + } + + if (_updateScreen) { + if (getGameType() == GType_FF) { + if (!getBitFlag(78)) { + oracleLogo(); + } + if (getBitFlag(76)) { + swapCharacterLogo(); + } + } + handleMouseMoved(); + dx_update_screen_and_palette(); + _updateScreen = false; + } + + _lockWord &= ~2; +} + +} // End of namespace Simon diff --git a/engines/agos/game.cpp b/engines/agos/game.cpp new file mode 100644 index 0000000000..f4997520ab --- /dev/null +++ b/engines/agos/game.cpp @@ -0,0 +1,1704 @@ +/* ScummVM - Scumm Interpreter + * Copyright (C) 2001 Ludvig Strigeus + * Copyright (C) 2001-2006 The ScummVM project + * + * 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. + * + * $URL$ + * $Id$ + * + */ + +#include "common/stdafx.h" + +#include "base/plugins.h" + +#include "common/config-manager.h" +#include "common/file.h" +#include "common/fs.h" +#include "common/md5.h" +#include "common/hashmap.h" +#include "common/hash-str.h" + +#include "agos/agos.h" + +namespace Simon { +static DetectedGameList GAME_detectGames(const FSList &fslist); +} + +using Common::File; + +struct ObsoleteGameID { + const char *from; + const char *to; + Common::Platform platform; +}; + +/** + * Conversion table mapping old obsolete target names to the + * corresponding new target and platform combination. + * + */ +static const ObsoleteGameID obsoleteGameIDsTable[] = { + {"simon1acorn", "simon1", Common::kPlatformAcorn}, + {"simon1amiga", "simon1", Common::kPlatformAmiga}, + {"simon1cd32", "simon1", Common::kPlatformAmiga}, + {"simon1demo", "simon1", Common::kPlatformPC}, + {"simon1dos", "simon1", Common::kPlatformPC}, + {"simon1talkie", "simon1", Common::kPlatformPC}, + {"simon1win", "simon1", Common::kPlatformWindows}, + {"simon2dos", "simon2", Common::kPlatformPC}, + {"simon2talkie", "simon2", Common::kPlatformPC}, + {"simon2mac", "simon2", Common::kPlatformMacintosh}, + {"simon2win", "simon2", Common::kPlatformWindows}, + {NULL, NULL, Common::kPlatformUnknown} +}; + +static const PlainGameDescriptor simonGames[] = { + {"elvira", "Elvira"}, + {"elvira2", "Elvira 2"}, + {"waxworks", "Waxworks"}, + {"simon1", "Simon the Sorcerer 1"}, + {"simon2", "Simon the Sorcerer 2"}, + {"feeble", "The Feeble Files"}, + {"dimp", "Demon in my Pocket"}, + {"jumble", "Jumble"}, + {"puzzle", "NoPatience"}, + {"swampy", "Swampy Adventures"}, + {NULL, NULL} +}; + +GameList Engine_AGOS_gameIDList() { + GameList games; + const PlainGameDescriptor *g = simonGames; + while (g->gameid) { + games.push_back(*g); + g++; + } + + return games; +} + +GameDescriptor Engine_AGOS_findGameID(const char *gameid) { + // First search the list of supported game IDs. + const PlainGameDescriptor *g = simonGames; + while (g->gameid) { + if (!scumm_stricmp(gameid, g->gameid)) + return *g; + g++; + } + + // If we didn't find the gameid in the main list, check if it + // is an obsolete game id. + GameDescriptor gs; + const ObsoleteGameID *o = obsoleteGameIDsTable; + while (o->from) { + if (0 == scumm_stricmp(gameid, o->from)) { + gs.gameid = gameid; + gs.description = "Obsolete game ID"; + return gs; + } + o++; + } + return gs; +} + +DetectedGameList Engine_AGOS_detectGames(const FSList &fslist) { + return Simon::GAME_detectGames(fslist); +} + +PluginError Engine_AGOS_create(OSystem *syst, Engine **engine) { + assert(syst); + assert(engine); + const char *gameid = ConfMan.get("gameid").c_str(); + + for (const ObsoleteGameID *o = obsoleteGameIDsTable; o->from; ++o) { + if (!scumm_stricmp(gameid, o->from)) { + // Match found, perform upgrade + gameid = o->to; + ConfMan.set("gameid", o->to); + + if (o->platform != Common::kPlatformUnknown) + ConfMan.set("platform", Common::getPlatformCode(o->platform)); + + warning("Target upgraded from %s to %s", o->from, o->to); + ConfMan.flushToDisk(); + break; + } + } + + FSList fslist; + FilesystemNode dir(ConfMan.get("path")); + if (!dir.listDir(fslist, FilesystemNode::kListFilesOnly)) { + warning("SimonEngine: invalid game path '%s'", dir.path().c_str()); + return kInvalidPathError; + } + + // Invoke the detector + DetectedGameList detectedGames = Engine_AGOS_detectGames(fslist); + + for (uint i = 0; i < detectedGames.size(); i++) { + if (detectedGames[i].gameid == gameid) { + *engine = new Simon::SimonEngine(syst); + return kNoError; + } + } + + warning("SimonEngine: Unable to locate game data at path '%s'", dir.path().c_str()); + return kNoGameDataFoundError; +} + +REGISTER_PLUGIN(AGOS, "AGOS", "AGOS (C) Adventure Soft"); + +namespace Simon { + +#define FILE_MD5_BYTES 5000 + +static GameFileDescription SIMON1ACORNDEMO_GameFiles[] = { + { "data", GAME_GMEFILE, "b4a7526ced425ba8ad0d548d0ec69900"}, + { "gamebase", GAME_BASEFILE, "425c7d1957699d35abca7e12a08c7422"}, + { "icondata", GAME_ICONFILE, "22107c24dfb31b66ac503c28a6e20b19"}, + { "stripped", GAME_STRFILE, "d9de7542612d9f4e0819ad0df5eac56b"}, + { "tbllist", GAME_TBLFILE, "d198a80de2c59e4a0cd24b98814849e8"}, +}; + +static GameFileDescription SIMON1ACORN_GameFiles[] = { + { "data", GAME_GMEFILE, "64958b3a38afdcb85da1eeed85169806"}, + { "gamebase", GAME_BASEFILE, "28261b99cd9da1242189b4f6f2841bd6"}, + { "icondata", GAME_ICONFILE, "22107c24dfb31b66ac503c28a6e20b19"}, + { "stripped", GAME_STRFILE, "f3b27a3fbb45dcd323a48159496e45e8"}, + { "tbllist", GAME_TBLFILE, "d198a80de2c59e4a0cd24b98814849e8"}, +}; + +static GameFileDescription SIMON1AMIGA_GameFiles[] = { + { "gameamiga", GAME_BASEFILE, "6c9ad2ff571d34a4cf0c696cf4e13500"}, + { "icon.pkd", GAME_ICONFILE, "565ef7a98dcc21ef526a2bb10b6f42ed"}, + { "stripped.txt", GAME_STRFILE, "c649fcc0439766810e5097ee7e81d4c8"}, + { "tbllist", GAME_TBLFILE, "f9d5bf2ce09f82289c791c3ca26e1e4b"}, +}; + +static GameFileDescription SIMON1AMIGA_FR_GameFiles[] = { + { "gameamiga", GAME_BASEFILE, "bd9828b9d4e5d89b50fe8c47a8e6bc07"}, + { "icon.pkd", GAME_ICONFILE, "565ef7a98dcc21ef526a2bb10b6f42ed"}, + { "stripped.txt", GAME_STRFILE, "2297baec985617d0d5612a0124bac359"}, + { "tbllist", GAME_TBLFILE, "f9d5bf2ce09f82289c791c3ca26e1e4b"}, +}; + +static GameFileDescription SIMON1AMIGA_DE_GameFiles[] = { + { "gameamiga", GAME_BASEFILE, "a2de9553f3b73064369948b5af38bb30"}, + { "icon.pkd", GAME_ICONFILE, "565ef7a98dcc21ef526a2bb10b6f42ed"}, + { "stripped.txt", GAME_STRFILE, "c649fcc0439766810e5097ee7e81d4c8"}, + { "tbllist", GAME_TBLFILE, "f9d5bf2ce09f82289c791c3ca26e1e4b"}, +}; + +static GameFileDescription SIMON1AMIGADEMO_GameFiles[] = { + { "gameamiga", GAME_BASEFILE, "a12b696170f14eca5ff75f1549829251"}, // Unpacked version + { "icon.pkd", GAME_ICONFILE, "ebc96af15bfaf75ba8210326b9260d2f"}, + { "stripped.txt", GAME_STRFILE, "8edde5b9498dc9f31da1093028da467c"}, + { "tbllist", GAME_TBLFILE, "1247e024e1f13ca54c1e354120c7519c"}, +}; + +static GameFileDescription SIMON1CD32_GameFiles[] = { + { "gameamiga", GAME_BASEFILE, "bab7f19237cf7d7619b6c73631da1854"}, + { "icon.pkd", GAME_ICONFILE, "565ef7a98dcc21ef526a2bb10b6f42ed"}, + { "stripped.txt", GAME_STRFILE, "59be788020441e21861e284236fd08c1"}, + { "tbllist", GAME_TBLFILE, "f9d5bf2ce09f82289c791c3ca26e1e4b"}, +}; + +static GameFileDescription SIMON1CD32_2_GameFiles[] = { + { "gameamiga", GAME_BASEFILE, "ec5358680c117f29b128cbbb322111a4"}, + { "icon.pkd", GAME_ICONFILE, "8ce5a46466a4f8f6d0f780b0ef00d5f5"}, + { "stripped.txt", GAME_STRFILE, "59be788020441e21861e284236fd08c1"}, + { "tbllist", GAME_TBLFILE, "f9d5bf2ce09f82289c791c3ca26e1e4b"}, +}; + +static GameFileDescription SIMON1DOS_INF_GameFiles[] = { + { "gamepc", GAME_BASEFILE, "9f93d27432ce44a787eef10adb640870"}, + { "icon.dat", GAME_ICONFILE, "22107c24dfb31b66ac503c28a6e20b19"}, + { "stripped.txt", GAME_STRFILE, "2af9affc5981eec44b90d4c556145cb8"}, + { "tbllist", GAME_TBLFILE, "d198a80de2c59e4a0cd24b98814849e8"}, +}; + +static GameFileDescription SIMON1DOS_INF_RU_GameFiles[] = { + { "gamepc", GAME_BASEFILE, "605fb866e03ec1c41b10c6a518ddfa49"}, + { "icon.dat", GAME_ICONFILE, "22107c24dfb31b66ac503c28a6e20b19"}, + { "stripped.txt", GAME_STRFILE, "2af9affc5981eec44b90d4c556145cb8"}, + { "tbllist", GAME_TBLFILE, "d198a80de2c59e4a0cd24b98814849e8"}, +}; + +static GameFileDescription SIMON1DOS_GameFiles[] = { + { "gamepc", GAME_BASEFILE, "c392e494dcabed797b98cbcfc687b33a"}, + { "icon.dat", GAME_ICONFILE, "22107c24dfb31b66ac503c28a6e20b19"}, + { "stripped.txt", GAME_STRFILE, "c95a0a1ee973e19c2a1c5d12026c139f"}, + { "tbllist", GAME_TBLFILE, "d198a80de2c59e4a0cd24b98814849e8"}, +}; + +static GameFileDescription SIMON1DOS_RU_GameFiles[] = { + { "gamepc", GAME_BASEFILE, "605fb866e03ec1c41b10c6a518ddfa49"}, + { "icon.dat", GAME_ICONFILE, "22107c24dfb31b66ac503c28a6e20b19"}, + { "stripped.txt", GAME_STRFILE, "c95a0a1ee973e19c2a1c5d12026c139f"}, + { "tbllist", GAME_TBLFILE, "d198a80de2c59e4a0cd24b98814849e8"}, +}; + +static GameFileDescription SIMON1DOS_FR_GameFiles[] = { + { "gamepc", GAME_BASEFILE, "34759d0d4285a2f4b21b8e03b8fcefb3"}, + { "icon.dat", GAME_ICONFILE, "22107c24dfb31b66ac503c28a6e20b19"}, + { "stripped.txt", GAME_STRFILE, "aa01e7386057abc0c3e27dbaa9c4ba5b"}, + { "tbllist", GAME_TBLFILE, "d198a80de2c59e4a0cd24b98814849e8"}, +}; + +static GameFileDescription SIMON1DOS_DE_GameFiles[] = { + { "gamepc", GAME_BASEFILE, "063015e6ce7d90b570dbc21fe0c667b1"}, + { "icon.dat", GAME_ICONFILE, "22107c24dfb31b66ac503c28a6e20b19"}, + { "stripped.txt", GAME_STRFILE, "c95a0a1ee973e19c2a1c5d12026c139f"}, + { "tbllist", GAME_TBLFILE, "d198a80de2c59e4a0cd24b98814849e8"}, +}; + +static GameFileDescription SIMON1DOS_IT_GameFiles[] = { + { "gamepc", GAME_BASEFILE, "65c9b2dea57df84ef55d1eaf384ebd30"}, + { "icon.dat", GAME_ICONFILE, "22107c24dfb31b66ac503c28a6e20b19"}, + { "stripped.txt", GAME_STRFILE, "2af9affc5981eec44b90d4c556145cb8"}, + { "tbllist", GAME_TBLFILE, "d198a80de2c59e4a0cd24b98814849e8"}, +}; + +static GameFileDescription SIMON1DOS_ES_GameFiles[] = { + { "gamepc", GAME_BASEFILE, "5374fafdea2068134f33deab225feed3"}, + { "icon.dat", GAME_ICONFILE, "22107c24dfb31b66ac503c28a6e20b19"}, + { "stripped.txt", GAME_STRFILE, "2af9affc5981eec44b90d4c556145cb8"}, + { "tbllist", GAME_TBLFILE, "d198a80de2c59e4a0cd24b98814849e8"}, +}; + +static GameFileDescription SIMON1DEMO_GameFiles[] = { + { "gdemo", GAME_BASEFILE, "2be4a21bc76e2fdc071867c130651439"}, + { "icon.dat", GAME_ICONFILE, "55af3b4d93972bc58bfee38a86b76c3f"}, + { "stripped.txt", GAME_STRFILE, "33a2e329b97b2a349858d6a093159eb7"}, + { "tbllist", GAME_TBLFILE, "1247e024e1f13ca54c1e354120c7519c"}, +}; + +static GameFileDescription SIMON1TALKIE_GameFiles[] = { + { "gamepc", GAME_BASEFILE, "28261b99cd9da1242189b4f6f2841bd6"}, + { "icon.dat", GAME_ICONFILE, "22107c24dfb31b66ac503c28a6e20b19"}, + { "simon.gme", GAME_GMEFILE, "64958b3a38afdcb85da1eeed85169806"}, + { "stripped.txt", GAME_STRFILE, "f3b27a3fbb45dcd323a48159496e45e8"}, + { "tbllist", GAME_TBLFILE, "d198a80de2c59e4a0cd24b98814849e8"}, +}; + +static GameFileDescription SIMON1TALKIE2_GameFiles[] = { + { "gamepc", GAME_BASEFILE, "c0b948b6821d2140f8b977144f21027a"}, + { "icon.dat", GAME_ICONFILE, "22107c24dfb31b66ac503c28a6e20b19"}, + { "simon.gme", GAME_GMEFILE, "64f73e94639b63af846ac4a8a94a23d8"}, + { "stripped.txt", GAME_STRFILE, "f3b27a3fbb45dcd323a48159496e45e8"}, + { "tbllist", GAME_TBLFILE, "d198a80de2c59e4a0cd24b98814849e8"}, +}; + +static GameFileDescription SIMON1TALKIE_FR_GameFiles[] = { + { "gamepc", GAME_BASEFILE, "3cfb9d1ff4ec725af9924140126cf69f"}, + { "icon.dat", GAME_ICONFILE, "22107c24dfb31b66ac503c28a6e20b19"}, + { "simon.gme", GAME_GMEFILE, "638049fa5d41b81fb6fb11671721b871"}, + { "stripped.txt", GAME_STRFILE, "ef51ac74c946881ae4d7ca66cc7a0d1e"}, + { "tbllist", GAME_TBLFILE, "d198a80de2c59e4a0cd24b98814849e8"}, +}; + +static GameFileDescription SIMON1TALKIE_DE_GameFiles[] = { + { "gamepc", GAME_BASEFILE, "48b1f3499e2e0d731047f4d481ff7817"}, + { "icon.dat", GAME_ICONFILE, "22107c24dfb31b66ac503c28a6e20b19"}, + { "simon.gme", GAME_GMEFILE, "7db9912acac4f1d965a64bdcfc370ba1"}, + { "stripped.txt", GAME_STRFILE, "40d68bec54042ef930f084ad9a4342a1"}, + { "tbllist", GAME_TBLFILE, "d198a80de2c59e4a0cd24b98814849e8"}, +}; + +static GameFileDescription SIMON1TALKIE_HB_GameFiles[] = { + { "gamepc", GAME_BASEFILE, "bc66e9c0b296e1b155a246917133f71a"}, + { "icon.dat", GAME_ICONFILE, "22107c24dfb31b66ac503c28a6e20b19"}, + { "simon.gme", GAME_GMEFILE, "a34b2c8642f2e3676d7088b5c8b3e884"}, + { "stripped.txt", GAME_STRFILE, "9d31bef42db1a8abe4e9f368014df1d5"}, + { "tbllist", GAME_TBLFILE, "d198a80de2c59e4a0cd24b98814849e8"}, +}; + +static GameFileDescription SIMON1TALKIE_IT_GameFiles[] = { + { "gamepc", GAME_BASEFILE, "8d3ca654e158c91b860c7eae31d65312"}, + { "icon.dat", GAME_ICONFILE, "22107c24dfb31b66ac503c28a6e20b19"}, + { "simon.gme", GAME_GMEFILE, "52e315e0e02feca86d15cc82e3306b6c"}, + { "stripped.txt", GAME_STRFILE, "9d31bef42db1a8abe4e9f368014df1d5"}, + { "tbllist", GAME_TBLFILE, "d198a80de2c59e4a0cd24b98814849e8"}, +}; + +static GameFileDescription SIMON1TALKIE_IT2_GameFiles[] = { + { "gamepc", GAME_BASEFILE, "8d3ca654e158c91b860c7eae31d65312"}, + { "icon.dat", GAME_ICONFILE, "22107c24dfb31b66ac503c28a6e20b19"}, + { "simon.gme", GAME_GMEFILE, "104efd83c8f3edf545982e07d87f66ac"}, + { "stripped.txt", GAME_STRFILE, "9d31bef42db1a8abe4e9f368014df1d5"}, + { "tbllist", GAME_TBLFILE, "d198a80de2c59e4a0cd24b98814849e8"}, +}; + +static GameFileDescription SIMON1TALKIE_ES_GameFiles[] = { + { "gamepc", GAME_BASEFILE, "439f801ba52c02c9d1844600d1ce0f5e"}, + { "icon.dat", GAME_ICONFILE, "22107c24dfb31b66ac503c28a6e20b19"}, + { "simon.gme", GAME_GMEFILE, "eff2774a73890b9eac533db90cd1afa1"}, + { "stripped.txt", GAME_STRFILE, "9d31bef42db1a8abe4e9f368014df1d5"}, + { "tbllist", GAME_TBLFILE, "d198a80de2c59e4a0cd24b98814849e8"}, +}; + +static GameFileDescription SIMON1WIN_GameFiles[] = { + { "gamepc", GAME_BASEFILE, "c7c12fea7f6d0bfd22af5cdbc8166862"}, + { "icon.dat", GAME_ICONFILE, "22107c24dfb31b66ac503c28a6e20b19"}, + { "simon.gme", GAME_GMEFILE, "b1b18d0731b64c0738c5cc4a2ee792fc"}, + { "stripped.txt", GAME_STRFILE, "a27e87a9ba21212d769804b3df47bfb2"}, + { "tbllist", GAME_TBLFILE, "d198a80de2c59e4a0cd24b98814849e8"}, +}; + +static GameFileDescription SIMON1WIN_RU_GameFiles[] = { + { "gamepc", GAME_BASEFILE, "4536a706412b36d628f12142bfa97af0"}, + { "icon.dat", GAME_ICONFILE, "22107c24dfb31b66ac503c28a6e20b19"}, + { "simon.gme", GAME_GMEFILE, "b1b18d0731b64c0738c5cc4a2ee792fc"}, + { "stripped.txt", GAME_STRFILE, "a27e87a9ba21212d769804b3df47bfb2"}, + { "tbllist", GAME_TBLFILE, "d198a80de2c59e4a0cd24b98814849e8"}, +}; + +static GameFileDescription SIMON1WIN_DE_GameFiles[] = { + { "gamepc", GAME_BASEFILE, "48b1f3499e2e0d731047f4d481ff7817"}, + { "icon.dat", GAME_ICONFILE, "22107c24dfb31b66ac503c28a6e20b19"}, + { "simon.gme", GAME_GMEFILE, "acd9cc438525b142d93b15c77a6f551b"}, + { "stripped.txt", GAME_STRFILE, "40d68bec54042ef930f084ad9a4342a1"}, + { "tbllist", GAME_TBLFILE, "d198a80de2c59e4a0cd24b98814849e8"}, +}; + +static GameFileDescription SIMON2DOS_GameFiles[] = { + { "game32", GAME_BASEFILE, "27c8e7feada80c75b70b9c2f6088d519"}, + { "icon.dat", GAME_ICONFILE, "ee92d1f84893195a60449f2430d07285"}, + { "simon2.gme", GAME_GMEFILE, "eefcc32b1f2c0482c1a59a963a146345"}, + { "stripped.txt", GAME_STRFILE, "e229f84d46fa83f99b4a7115679f3fb6"}, + { "tbllist", GAME_TBLFILE, "2082f8d02075e590300478853a91ffd9"}, +}; + +static GameFileDescription SIMON2DOS_RU_GameFiles[] = { + { "game32", GAME_BASEFILE, "7edfc633dd50f8caa719c478443db70b"}, + { "icon.dat", GAME_ICONFILE, "ee92d1f84893195a60449f2430d07285"}, + { "simon2.gme", GAME_GMEFILE, "eefcc32b1f2c0482c1a59a963a146345"}, + { "stripped.txt", GAME_STRFILE, "e229f84d46fa83f99b4a7115679f3fb6"}, + { "tbllist", GAME_TBLFILE, "2082f8d02075e590300478853a91ffd9"}, +}; + +static GameFileDescription SIMON2DOS2_GameFiles[] = { + { "game32", GAME_BASEFILE, "604d04315935e77624bd356ac926e068"}, + { "icon.dat", GAME_ICONFILE, "72096a62d36e6034ea9fecc13b2dbdab"}, + { "simon2.gme", GAME_GMEFILE, "aa6840420899a31874204f90bb214108"}, + { "stripped.txt", GAME_STRFILE, "e229f84d46fa83f99b4a7115679f3fb6"}, + { "tbllist", GAME_TBLFILE, "2082f8d02075e590300478853a91ffd9"}, +}; + +static GameFileDescription SIMON2DOS2_RU_GameFiles[] = { + { "game32", GAME_BASEFILE, "eb8bde3685842a8fd38f60bc476ef8e9"}, + { "icon.dat", GAME_ICONFILE, "72096a62d36e6034ea9fecc13b2dbdab"}, + { "simon2.gme", GAME_GMEFILE, "aa6840420899a31874204f90bb214108"}, + { "stripped.txt", GAME_STRFILE, "e229f84d46fa83f99b4a7115679f3fb6"}, + { "tbllist", GAME_TBLFILE, "2082f8d02075e590300478853a91ffd9"}, +}; + +static GameFileDescription SIMON2DOS_DE_GameFiles[] = { + { "game32", GAME_BASEFILE, "eb6e3e37fe52993f948d7e2d6b869828"}, + { "icon.dat", GAME_ICONFILE, "72096a62d36e6034ea9fecc13b2dbdab"}, + { "simon2.gme", GAME_GMEFILE, "5fa9d080b04c610f526bd685be1bf747"}, + { "stripped.txt", GAME_STRFILE, "fd30df01cc248ecbaef302af855e0212"}, + { "tbllist", GAME_TBLFILE, "2082f8d02075e590300478853a91ffd9"}, +}; + +static GameFileDescription SIMON2DOS_IT_GameFiles[] = { + { "game32", GAME_BASEFILE, "3e11d400bea0638f360a724687005cd1"}, + { "icon.dat", GAME_ICONFILE, "72096a62d36e6034ea9fecc13b2dbdab"}, + { "simon2.gme", GAME_GMEFILE, "f306a397565d7f13bec7ecf14c723de7"}, + { "stripped.txt", GAME_STRFILE, "bea6843fb9f3b2144fcb146d62db0b9a"}, + { "tbllist", GAME_TBLFILE, "2082f8d02075e590300478853a91ffd9"}, +}; + +static GameFileDescription SIMON2DEMO_GameFiles[] = { + { "gsptr30", GAME_BASEFILE, "3794c15887539b8578bacab694ccf08a"}, + { "icon.dat", GAME_ICONFILE, "72096a62d36e6034ea9fecc13b2dbdab"}, + { "simon2.gme", GAME_GMEFILE, "f8c9e6df1e55923a749e115ba74210c4"}, + { "stripped.txt", GAME_STRFILE, "e229f84d46fa83f99b4a7115679f3fb6"}, + { "tbllist", GAME_TBLFILE, "a0d5a494b5d3d209d1a1d76cc8d76601"}, +}; + +static GameFileDescription SIMON2TALKIE_GameFiles[] = { + { "gsptr30", GAME_BASEFILE, "8c301fb9c4fcf119d2730ccd2a565eb3"}, + { "icon.dat", GAME_ICONFILE, "72096a62d36e6034ea9fecc13b2dbdab"}, + { "simon2.gme", GAME_GMEFILE, "9c535d403966750ae98bdaf698375a38"}, + { "stripped.txt", GAME_STRFILE, "e229f84d46fa83f99b4a7115679f3fb6"}, + { "tbllist", GAME_TBLFILE, "2082f8d02075e590300478853a91ffd9"}, +}; + +static GameFileDescription SIMON2TALKIE2_GameFiles[] = { + { "gsptr30", GAME_BASEFILE, "608e277904d87dd28725fa08eacc2c0d"}, + { "icon.dat", GAME_ICONFILE, "72096a62d36e6034ea9fecc13b2dbdab"}, + { "simon2.gme", GAME_GMEFILE, "8d6dcc65577e285dbca03ff6d7d9323c"}, + { "stripped.txt", GAME_STRFILE, "e229f84d46fa83f99b4a7115679f3fb6"}, + { "tbllist", GAME_TBLFILE, "a0d5a494b5d3d209d1a1d76cc8d76601"}, +}; + +static GameFileDescription SIMON2TALKIE_FR_GameFiles[] = { + { "gsptr30", GAME_BASEFILE, "43b3a04d2f0a0cbd1b024c814856561a"}, + { "icon.dat", GAME_ICONFILE, "72096a62d36e6034ea9fecc13b2dbdab"}, + { "simon2.gme", GAME_GMEFILE, "8af0e02c0c3344db64dffc12196eb59d"}, + { "stripped.txt", GAME_STRFILE, "5ea27977b4d7dcfd50eb5074e162ebbf"}, + { "tbllist", GAME_TBLFILE, "2082f8d02075e590300478853a91ffd9"}, +}; + +static GameFileDescription SIMON2TALKIE_DE_GameFiles[] = { + { "gsptr30", GAME_BASEFILE, "0d05c3f4c06c9a4ceb3d2f5bc0b18e11"}, + { "icon.dat", GAME_ICONFILE, "72096a62d36e6034ea9fecc13b2dbdab"}, + { "simon2.gme", GAME_GMEFILE, "6c5fdfdd0eab9038767c2d22858406b2"}, + { "stripped.txt", GAME_STRFILE, "6de6292c9ac11bfb2e70fdb0f773ba85"}, + { "tbllist", GAME_TBLFILE, "2082f8d02075e590300478853a91ffd9"}, +}; + +static GameFileDescription SIMON2TALKIE_DE2_GameFiles[] = { + { "gsptr30", GAME_BASEFILE, "a76ea940076b5d9316796dea225a9b69"}, + { "icon.dat", GAME_ICONFILE, "72096a62d36e6034ea9fecc13b2dbdab"}, + { "simon2.gme", GAME_GMEFILE, "ec9f0f24fd895e7ea72e3c8e448c0240"}, + { "stripped.txt", GAME_STRFILE, "6de6292c9ac11bfb2e70fdb0f773ba85"}, + { "tbllist", GAME_TBLFILE, "2082f8d02075e590300478853a91ffd9"}, +}; + +static GameFileDescription SIMON2TALKIE_HB_GameFiles[] = { + { "gsptr30", GAME_BASEFILE, "952a2b1be23c3c609ba8d988a9a1627d"}, + { "icon.dat", GAME_ICONFILE, "72096a62d36e6034ea9fecc13b2dbdab"}, + { "simon2.gme", GAME_GMEFILE, "a2b249a82ea182af09789eb95fb6c5be"}, + { "stripped.txt", GAME_STRFILE, "de9dbc24158660e153483fa0cf6c3172"}, + { "tbllist", GAME_TBLFILE, "2082f8d02075e590300478853a91ffd9"}, +}; + +static GameFileDescription SIMON2TALKIE_IT_GameFiles[] = { + { "gsptr30", GAME_BASEFILE, "3e11d400bea0638f360a724687005cd1"}, + { "icon.dat", GAME_ICONFILE, "72096a62d36e6034ea9fecc13b2dbdab"}, + { "simon2.gme", GAME_GMEFILE, "344aca58e5ad5e25c517d5eb1d85c435"}, + { "stripped.txt", GAME_STRFILE, "bea6843fb9f3b2144fcb146d62db0b9a"}, + { "tbllist", GAME_TBLFILE, "2082f8d02075e590300478853a91ffd9"}, +}; + +static GameFileDescription SIMON2TALKIE_ES_GameFiles[] = { + { "gsptr30", GAME_BASEFILE, "268dc322aa73bcf27bb016b8e8ceb889"}, + { "icon.dat", GAME_ICONFILE, "72096a62d36e6034ea9fecc13b2dbdab"}, + { "simon2.gme", GAME_GMEFILE, "4f43bd06b6cc78dbd25a7475ca964eb1"}, + { "stripped.txt", GAME_STRFILE, "d13753796bd81bf313a2449f34d8b112"}, + { "tbllist", GAME_TBLFILE, "2082f8d02075e590300478853a91ffd9"}, +}; + +static GameFileDescription SIMON2WIN_GameFiles[] = { + { "gsptr30", GAME_BASEFILE, "608e277904d87dd28725fa08eacc2c0d"}, + { "icon.dat", GAME_ICONFILE, "72096a62d36e6034ea9fecc13b2dbdab"}, + { "simon2.gme", GAME_GMEFILE, "e749c4c103d7e7d51b34620ed76c5a04"}, + { "stripped.txt", GAME_STRFILE, "e229f84d46fa83f99b4a7115679f3fb6"}, + { "tbllist", GAME_TBLFILE, "2082f8d02075e590300478853a91ffd9"}, +}; + +static GameFileDescription SIMON2WIN_DE_GameFiles[] = { + { "gsptr30", GAME_BASEFILE, "a76ea940076b5d9316796dea225a9b69"}, + { "icon.dat", GAME_ICONFILE, "72096a62d36e6034ea9fecc13b2dbdab"}, + { "simon2.gme", GAME_GMEFILE, "9609a933c541fed2e00c6c3479d7c181"}, + { "stripped.txt", GAME_STRFILE, "6de6292c9ac11bfb2e70fdb0f773ba85"}, + { "tbllist", GAME_TBLFILE, "2082f8d02075e590300478853a91ffd9"}, +}; + +static GameFileDescription SIMON2WIN_DE2_GameFiles[] = { + { "gsptr30", GAME_BASEFILE, "9e858b3bb189c134c3a5f34c3385a8d3"}, + { "icon.dat", GAME_ICONFILE, "ee92d1f84893195a60449f2430d07285"}, + { "simon2.gme", GAME_GMEFILE, "16d574da07e93bcae43cee353dab8c7e"}, + { "stripped.txt", GAME_STRFILE, "6de6292c9ac11bfb2e70fdb0f773ba85"}, + { "tbllist", GAME_TBLFILE, "2082f8d02075e590300478853a91ffd9"}, +}; + +static GameFileDescription SIMON2WIN_PL_GameFiles[] = { + { "gsptr30", GAME_BASEFILE, "657fd873f5d0637097ee02315b447e6f"}, + { "icon.dat", GAME_ICONFILE, "72096a62d36e6034ea9fecc13b2dbdab"}, + { "simon2.gme", GAME_GMEFILE, "7b9afcf82a94722707e0d025c0192be8"}, + { "stripped.txt", GAME_STRFILE, "e229f84d46fa83f99b4a7115679f3fb6"}, + { "tbllist", GAME_TBLFILE, "2082f8d02075e590300478853a91ffd9"}, +}; + +static GameFileDescription FEEBLEFILES_AMI_DE_GameFiles[] = { + { "game22", GAME_BASEFILE, "bcd76ac080003eee3649df18db25b60e"}, + { "gfxindex.dat", GAME_GFXIDXFILE,"f550f7915c5ce3a68c9f870f507449c2"}, + { "tbllist", GAME_TBLFILE, "0bbfee8e69739111eb36b0d138da8ddf"}, +}; + +static GameFileDescription FEEBLEFILES_AMI_UK_GameFiles[] = { + { "game22", GAME_BASEFILE, "629762ea9ca9ee9ff85f4774d219f5c7"}, + { "gfxindex.dat", GAME_GFXIDXFILE,"f550f7915c5ce3a68c9f870f507449c2"}, + { "tbllist", GAME_TBLFILE, "0bbfee8e69739111eb36b0d138da8ddf"}, +}; + +static GameFileDescription FEEBLEFILES_MAC_DE_GameFiles[] = { + { "game22", GAME_BASEFILE, "bcd76ac080003eee3649df18db25b60e"}, + { "graphics.vga", GAME_GFXIDXFILE,"11a4853cb35956846976e9473ee0e41e"}, + { "tbllist", GAME_TBLFILE, "0bbfee8e69739111eb36b0d138da8ddf"}, +}; + +static GameFileDescription FEEBLEFILES_MAC_FR_GameFiles[] = { + { "game22", GAME_BASEFILE, "ba90b40a47726039671d9e91630dd7ed"}, + { "graphics.vga", GAME_GFXIDXFILE,"11a4853cb35956846976e9473ee0e41e"}, + { "tbllist", GAME_TBLFILE, "0bbfee8e69739111eb36b0d138da8ddf"}, +}; + +static GameFileDescription FEEBLEFILES_MAC_ES_GameFiles[] = { + { "game22", GAME_BASEFILE, "71d7d2d5e479b053c5a9757f1702c9c3"}, + { "graphics.vga", GAME_GFXIDXFILE,"11a4853cb35956846976e9473ee0e41e"}, + { "tbllist", GAME_TBLFILE, "0bbfee8e69739111eb36b0d138da8ddf"}, +}; + +static GameFileDescription FEEBLEFILES_MAC_UK_GameFiles[] = { + { "game22", GAME_BASEFILE, "629762ea9ca9ee9ff85f4774d219f5c7"}, + { "graphics.vga", GAME_GFXIDXFILE,"11a4853cb35956846976e9473ee0e41e"}, + { "tbllist", GAME_TBLFILE, "0bbfee8e69739111eb36b0d138da8ddf"}, +}; + +static GameFileDescription FEEBLEFILES_2CD_GameFiles[] = { + { "game22", GAME_BASEFILE, "629762ea9ca9ee9ff85f4774d219f5c7"}, + { "tbllist", GAME_TBLFILE, "0bbfee8e69739111eb36b0d138da8ddf"}, +}; + +static GameFileDescription FEEBLEFILES_4CD_GameFiles[] = { + { "game22", GAME_BASEFILE, "a8746407a5b20a7da0da0a14c380af1c"}, + { "tbllist", GAME_TBLFILE, "0bbfee8e69739111eb36b0d138da8ddf"}, +}; + +static GameFileDescription FEEBLEFILES_DE_GameFiles[] = { + { "game22", GAME_BASEFILE, "bcd76ac080003eee3649df18db25b60e"}, + { "tbllist", GAME_TBLFILE, "0bbfee8e69739111eb36b0d138da8ddf"}, +}; + +static GameFileDescription FEEBLEFILES_FR_GameFiles[] = { + { "game22", GAME_BASEFILE, "ba90b40a47726039671d9e91630dd7ed"}, + { "tbllist", GAME_TBLFILE, "0bbfee8e69739111eb36b0d138da8ddf"}, +}; + +static GameFileDescription FEEBLEFILES_IT_GameFiles[] = { + { "game22", GAME_BASEFILE, "80576f2e1ed4c912b63921fe77af313e"}, + { "tbllist", GAME_TBLFILE, "0bbfee8e69739111eb36b0d138da8ddf"}, +}; + +static GameFileDescription FEEBLEFILES_ES_GameFiles[] = { + { "game22", GAME_BASEFILE, "71d7d2d5e479b053c5a9757f1702c9c3"}, + { "tbllist", GAME_TBLFILE, "0bbfee8e69739111eb36b0d138da8ddf"}, +}; + +static GameFileDescription WAXWORKS_GameFiles[] = { + { "gamepc", GAME_BASEFILE, "7751e9358e894e32ef40ef3b3bae0f2a"}, + { "icon.dat", GAME_ICONFILE, "ef1b8ad3494cf103dc10a99fe152ef9a"}, + { "roomslst", GAME_RMSLFILE, "e3758c46ab8f3c23a1ac012bd607108d"}, + { "stripped.txt", GAME_STRFILE, "f259e3e07a1cde8d0404a767d815e12c"}, + { "tbllist", GAME_TBLFILE, "95c44bfc380770a6b6dd0dfcc69e80a0"}, + { "xtbllist", GAME_XTBLFILE, "6c7b3db345d46349a5226f695c03e20f"}, +}; + +static GameFileDescription ELVIRA2_GameFiles[] = { + { "gamepc", GAME_BASEFILE, "3313254722031b22d833a2cf45a91fd7"}, + { "icon.dat", GAME_ICONFILE, "83a7278bff55c82fbb3aef92981866c9"}, + { "stripped.txt", GAME_STRFILE, "c2533277b7ff11f5495967d55355ea17"}, + { "tbllist", GAME_TBLFILE, "8252660df0edbdbc3e6377e155bbd0c5"}, +}; + +static GameFileDescription ELVIRA_GameFiles[] = { + { "gamepc", GAME_BASEFILE, "9076d507d60cc454df662316438ec843"}, + { "icon.dat", GAME_ICONFILE, "fda48c9da7f3e72d0313e2f5f760fc45"}, + { "tbllist", GAME_TBLFILE, "319f6b227c7822a551f57d24e70f8149"}, +}; + +static GameFileDescription DIMP_GameFiles[] = { + { "Gdimp", GAME_BASEFILE, "0b1e89ae1dc2e012b7fa7a987b4ac42a"}, +}; + +static GameFileDescription JUMBLE_GameFiles[] = { + { "Gjumble", GAME_BASEFILE, "d54cce46d339038d1a6b74ea213655bc"}, +}; + +static GameFileDescription PUZZLE_GameFiles[] = { + { "Gpuzzle", GAME_BASEFILE, "3f80dac8e0d85401a1058a560fe49ab6"}, +}; + +static GameFileDescription SWAMPY_GameFiles[] = { + { "Gswampy", GAME_BASEFILE, "3a6d4d7b2433e660f2483f9396cc87a2"}, +}; + +static GameDescription gameDescriptions[] = { + // Elvira - English Floppy + { + "elvira", + GType_ELVIRA, + GID_ELVIRA, + "Floppy", + ARRAYSIZE(ELVIRA_GameFiles), + ELVIRA_GameFiles, + GF_OLD_BUNDLE, + Common::EN_ANY, + Common::kPlatformPC, + }, + + // Elvira 2 - English Floppy + { + "elvira2", + GType_ELVIRA2, + GID_ELVIRA2, + "Floppy", + ARRAYSIZE(ELVIRA2_GameFiles), + ELVIRA2_GameFiles, + GF_OLD_BUNDLE, + Common::EN_ANY, + Common::kPlatformPC, + }, + + // Waxworks - English Floppy + { + "waxworks", + GType_WW, + GID_WAXWORKS, + "Floppy", + ARRAYSIZE(WAXWORKS_GameFiles), + WAXWORKS_GameFiles, + GF_OLD_BUNDLE, + Common::EN_ANY, + Common::kPlatformPC, + }, + + // Simon the Sorcerer 1 - English Acorn CD Demo + { + "simon1", + GType_SIMON1, + GID_SIMON1ACORNDEMO, + "CD Demo", + ARRAYSIZE(SIMON1ACORNDEMO_GameFiles), + SIMON1ACORNDEMO_GameFiles, + GF_TALKIE, + Common::EN_ANY, + Common::kPlatformAcorn, + }, + + // Simon the Sorcerer 1 - English Acorn CD + { + "simon1", + GType_SIMON1, + GID_SIMON1ACORN, + "CD", + ARRAYSIZE(SIMON1ACORN_GameFiles), + SIMON1ACORN_GameFiles, + GF_TALKIE, + Common::EN_ANY, + Common::kPlatformAcorn, + }, + + // Simon the Sorcerer 1 - English AGA Floppy + { + "simon1", + GType_SIMON1, + GID_SIMON1AMIGA, + "AGA Floppy", + ARRAYSIZE(SIMON1AMIGA_GameFiles), + SIMON1AMIGA_GameFiles, + GF_CRUNCHED | GF_OLD_BUNDLE, + Common::EN_ANY, + Common::kPlatformAmiga, + }, + + // Simon the Sorcerer 1 - French AGA Floppy + { + "simon1", + GType_SIMON1, + GID_SIMON1AMIGA_FR, + "AGA Floppy", + ARRAYSIZE(SIMON1AMIGA_FR_GameFiles), + SIMON1AMIGA_FR_GameFiles, + GF_CRUNCHED | GF_OLD_BUNDLE, + Common::FR_FRA, + Common::kPlatformAmiga, + }, + + // Simon the Sorcerer 1 - German AGA Floppy + { + "simon1", + GType_SIMON1, + GID_SIMON1AMIGA_DE, + "AGA Floppy", + ARRAYSIZE(SIMON1AMIGA_DE_GameFiles), + SIMON1AMIGA_DE_GameFiles, + GF_CRUNCHED | GF_OLD_BUNDLE, + Common::DE_DEU, + Common::kPlatformAmiga, + }, + + // Simon the Sorcerer 1 - English Amiga ECS Demo + { + "simon1", + GType_SIMON1, + GID_SIMON1AMIGADEMO, + "ECS Demo", + ARRAYSIZE(SIMON1AMIGADEMO_GameFiles), + SIMON1AMIGADEMO_GameFiles, + GF_CRUNCHED | GF_OLD_BUNDLE, + Common::EN_ANY, + Common::kPlatformAmiga, + }, + + // Simon the Sorcerer 1 - English Amiga CD32 + { + "simon1", + GType_SIMON1, + GID_SIMON1CD32, + "CD32", + ARRAYSIZE(SIMON1CD32_GameFiles), + SIMON1CD32_GameFiles, + GF_TALKIE | GF_OLD_BUNDLE, + Common::EN_ANY, + Common::kPlatformAmiga, + }, + + // Simon the Sorcerer 1 - English Amiga CD32 alternative? + { + "simon1", + GType_SIMON1, + GID_SIMON1CD32_2, + "CD32", + ARRAYSIZE(SIMON1CD32_2_GameFiles), + SIMON1CD32_2_GameFiles, + GF_TALKIE | GF_OLD_BUNDLE, + Common::EN_ANY, + Common::kPlatformAmiga, + }, + + // Simon the Sorcerer 1 - English DOS Floppy Demo + { + "simon1", + GType_SIMON1, + GID_SIMON1DEMO, + "Floppy Demo", + ARRAYSIZE(SIMON1DEMO_GameFiles), + SIMON1DEMO_GameFiles, + GF_OLD_BUNDLE, + Common::EN_ANY, + Common::kPlatformPC, + }, + + // Simon the Sorcerer 1 - English DOS Floppy + { + "simon1", + GType_SIMON1, + GID_SIMON1DOS, + "Floppy", + ARRAYSIZE(SIMON1DOS_GameFiles), + SIMON1DOS_GameFiles, + GF_OLD_BUNDLE, + Common::EN_ANY, + Common::kPlatformPC, + }, + + // Simon the Sorcerer 1 - English DOS Floppy with Russian patch + { + "simon1", + GType_SIMON1, + GID_SIMON1DOS_RU, + "Floppy", + ARRAYSIZE(SIMON1DOS_RU_GameFiles), + SIMON1DOS_RU_GameFiles, + GF_OLD_BUNDLE, + Common::RU_RUS, + Common::kPlatformPC, + }, + + // Simon the Sorcerer 1 - English DOS Floppy (Infocom) + { + "simon1", + GType_SIMON1, + GID_SIMON1DOS_INF, + "Floppy", + ARRAYSIZE(SIMON1DOS_INF_GameFiles), + SIMON1DOS_INF_GameFiles, + GF_OLD_BUNDLE, + Common::EN_ANY, + Common::kPlatformPC, + }, + + // Simon the Sorcerer 1 - English DOS Floppy (Infocom) with Russian patch + { + "simon1", + GType_SIMON1, + GID_SIMON1DOS_INF_RU, + "Floppy", + ARRAYSIZE(SIMON1DOS_INF_RU_GameFiles), + SIMON1DOS_INF_RU_GameFiles, + GF_OLD_BUNDLE, + Common::RU_RUS, + Common::kPlatformPC, + }, + + // Simon the Sorcerer 1 - French DOS Floppy + { + "simon1", + GType_SIMON1, + GID_SIMON1DOS_FR, + "Floppy", + ARRAYSIZE(SIMON1DOS_FR_GameFiles), + SIMON1DOS_FR_GameFiles, + GF_OLD_BUNDLE, + Common::FR_FRA, + Common::kPlatformPC, + }, + + // Simon the Sorcerer 1 - German DOS Floppy + { + "simon1", + GType_SIMON1, + GID_SIMON1DOS_DE, + "Floppy", + ARRAYSIZE(SIMON1DOS_DE_GameFiles), + SIMON1DOS_DE_GameFiles, + GF_OLD_BUNDLE, + Common::DE_DEU, + Common::kPlatformPC, + }, + + // Simon the Sorcerer 1 - Italian DOS Floppy + { + "simon1", + GType_SIMON1, + GID_SIMON1DOS_IT, + "Floppy", + ARRAYSIZE(SIMON1DOS_IT_GameFiles), + SIMON1DOS_IT_GameFiles, + GF_OLD_BUNDLE, + Common::IT_ITA, + Common::kPlatformPC, + }, + + // Simon the Sorcerer 1 - Spanish DOS Floppy + { + "simon1", + GType_SIMON1, + GID_SIMON1DOS_ES, + "Floppy", + ARRAYSIZE(SIMON1DOS_ES_GameFiles), + SIMON1DOS_ES_GameFiles, + GF_OLD_BUNDLE, + Common::ES_ESP, + Common::kPlatformPC, + }, + + // Simon the Sorcerer 1 - English DOS CD + { + "simon1", + GType_SIMON1, + GID_SIMON1TALKIE, + "CD", + ARRAYSIZE(SIMON1TALKIE_GameFiles), + SIMON1TALKIE_GameFiles, + GF_TALKIE, + Common::EN_ANY, + Common::kPlatformPC, + }, + + // Simon the Sorcerer 1 - English DOS CD alternate? + { + "simon1", + GType_SIMON1, + GID_SIMON1TALKIE2, + "CD", + ARRAYSIZE(SIMON1TALKIE2_GameFiles), + SIMON1TALKIE2_GameFiles, + GF_TALKIE, + Common::EN_ANY, + Common::kPlatformPC, + }, + + // Simon the Sorcerer 1 - French DOS CD + { + "simon1", + GType_SIMON1, + GID_SIMON1TALKIE_FR, + "CD", + ARRAYSIZE(SIMON1TALKIE_FR_GameFiles), + SIMON1TALKIE_FR_GameFiles, + GF_TALKIE, + Common::FR_FRA, + Common::kPlatformPC, + }, + + // Simon the Sorcerer 1 - German DOS CD + { + "simon1", + GType_SIMON1, + GID_SIMON1TALKIE_DE, + "CD", + ARRAYSIZE(SIMON1TALKIE_DE_GameFiles), + SIMON1TALKIE_DE_GameFiles, + GF_TALKIE, + Common::DE_DEU, + Common::kPlatformPC, + }, + + // Simon the Sorcerer 1 - Hebrew DOS CD + { + "simon1", + GType_SIMON1, + GID_SIMON1TALKIE_HB, + "CD", + ARRAYSIZE(SIMON1TALKIE_HB_GameFiles), + SIMON1TALKIE_HB_GameFiles, + GF_TALKIE, + Common::HB_ISR, + Common::kPlatformPC, + }, + + // Simon the Sorcerer 1 - Italian DOS CD + { + "simon1", + GType_SIMON1, + GID_SIMON1TALKIE_IT, + "CD", + ARRAYSIZE(SIMON1TALKIE_IT_GameFiles), + SIMON1TALKIE_IT_GameFiles, + GF_TALKIE, + Common::IT_ITA, + Common::kPlatformPC, + }, + + // Simon the Sorcerer 1 - Italian DOS CD alternate + { + "simon1", + GType_SIMON1, + GID_SIMON1TALKIE_IT2, + "CD", + ARRAYSIZE(SIMON1TALKIE_IT2_GameFiles), + SIMON1TALKIE_IT2_GameFiles, + GF_TALKIE, + Common::IT_ITA, + // FIXME: DOS version which uses WAV format + Common::kPlatformWindows, + }, + + // Simon the Sorcerer 1 - Spanish DOS CD + { + "simon1", + GType_SIMON1, + GID_SIMON1TALKIE_ES, + "CD", + ARRAYSIZE(SIMON1TALKIE_ES_GameFiles), + SIMON1TALKIE_ES_GameFiles, + GF_TALKIE, + Common::ES_ESP, + Common::kPlatformPC, + }, + + // Simon the Sorcerer 1 - English Windows CD + { + "simon1", + GType_SIMON1, + GID_SIMON1WIN, + "CD", + ARRAYSIZE(SIMON1WIN_GameFiles), + SIMON1WIN_GameFiles, + GF_TALKIE, + Common::EN_ANY, + Common::kPlatformWindows, + }, + + // Simon the Sorcerer 1 - English Windows CD with Russian patch + { + "simon1", + GType_SIMON1, + GID_SIMON1WIN_RU, + "CD", + ARRAYSIZE(SIMON1WIN_RU_GameFiles), + SIMON1WIN_RU_GameFiles, + GF_TALKIE, + Common::RU_RUS, + Common::kPlatformWindows, + }, + + // Simon the Sorcerer 1 - German Windows CD + { + "simon1", + GType_SIMON1, + GID_SIMON1WIN_DE, + "CD", + ARRAYSIZE(SIMON1WIN_DE_GameFiles), + SIMON1WIN_DE_GameFiles, + GF_TALKIE, + Common::DE_DEU, + Common::kPlatformWindows, + }, + + // Simon the Sorcerer 2 - English DOS Floppy + { + "simon2", + GType_SIMON2, + GID_SIMON2DOS, + "Floppy", + ARRAYSIZE(SIMON2DOS_GameFiles), + SIMON2DOS_GameFiles, + 0, + Common::EN_ANY, + Common::kPlatformPC, + }, + + // Simon the Sorcerer 2 - English DOS Floppy with Russian patch + { + "simon2", + GType_SIMON2, + GID_SIMON2DOS_RU, + "Floppy", + ARRAYSIZE(SIMON2DOS_RU_GameFiles), + SIMON2DOS_RU_GameFiles, + 0, + Common::RU_RUS, + Common::kPlatformPC, + }, + + // Simon the Sorcerer 2 - English DOS Floppy alternate? + { + "simon2", + GType_SIMON2, + GID_SIMON2DOS2, + "Floppy", + ARRAYSIZE(SIMON2DOS2_GameFiles), + SIMON2DOS2_GameFiles, + 0, + Common::EN_ANY, + Common::kPlatformPC, + }, + + // Simon the Sorcerer 2 - English DOS Floppy alternate? with Russian patch + { + "simon2", + GType_SIMON2, + GID_SIMON2DOS2_RU, + "Floppy", + ARRAYSIZE(SIMON2DOS2_RU_GameFiles), + SIMON2DOS2_RU_GameFiles, + 0, + Common::RU_RUS, + Common::kPlatformPC, + }, + + // Simon the Sorcerer 2 - German DOS Floppy + { + "simon2", + GType_SIMON2, + GID_SIMON2DOS_DE, + "Floppy", + ARRAYSIZE(SIMON2DOS_DE_GameFiles), + SIMON2DOS_DE_GameFiles, + 0, + Common::DE_DEU, + Common::kPlatformPC, + }, + + // Simon the Sorcerer 2 - Italian DOS Floppy + { + "simon2", + GType_SIMON2, + GID_SIMON2DOS_IT, + "Floppy", + ARRAYSIZE(SIMON2DOS_IT_GameFiles), + SIMON2DOS_IT_GameFiles, + 0, + Common::IT_ITA, + Common::kPlatformPC, + }, + + // Simon the Sorcerer 2 - English DOS CD Demo + { + "simon2", + GType_SIMON2, + GID_SIMON2DEMO, + "CD Demo", + ARRAYSIZE(SIMON2DEMO_GameFiles), + SIMON2DEMO_GameFiles, + GF_TALKIE, + Common::EN_ANY, + Common::kPlatformPC, + }, + + // Simon the Sorcerer 2 - English DOS CD + { + "simon2", + GType_SIMON2, + GID_SIMON2TALKIE, + "CD", + ARRAYSIZE(SIMON2TALKIE_GameFiles), + SIMON2TALKIE_GameFiles, + GF_TALKIE, + Common::EN_ANY, + Common::kPlatformPC, + }, + + + // Simon the Sorcerer 2 - English DOS CD alternate? + { + "simon2", + GType_SIMON2, + GID_SIMON2TALKIE2, + "CD", + ARRAYSIZE(SIMON2TALKIE2_GameFiles), + SIMON2TALKIE2_GameFiles, + GF_TALKIE, + Common::EN_ANY, + Common::kPlatformPC, + }, + + // Simon the Sorcerer 2 - French DOS CD + { + "simon2", + GType_SIMON2, + GID_SIMON2TALKIE_FR, + "CD", + ARRAYSIZE(SIMON2TALKIE_FR_GameFiles), + SIMON2TALKIE_FR_GameFiles, + GF_TALKIE, + Common::FR_FRA, + Common::kPlatformPC, + }, + + // Simon the Sorcerer 2 - German DOS CD + { + "simon2", + GType_SIMON2, + GID_SIMON2TALKIE_DE, + "CD", + ARRAYSIZE(SIMON2TALKIE_DE_GameFiles), + SIMON2TALKIE_DE_GameFiles, + GF_TALKIE, + Common::DE_DEU, + Common::kPlatformPC, + }, + + // Simon the Sorcerer 2 - German DOS CD alternate? + { + "simon2", + GType_SIMON2, + GID_SIMON2TALKIE_DE2, + "CD", + ARRAYSIZE(SIMON2TALKIE_DE2_GameFiles), + SIMON2TALKIE_DE2_GameFiles, + GF_TALKIE, + Common::DE_DEU, + Common::kPlatformPC, + }, + + // Simon the Sorcerer 2 - Hebrew DOS CD + { + "simon2", + GType_SIMON2, + GID_SIMON2TALKIE_HB, + "CD", + ARRAYSIZE(SIMON2TALKIE_HB_GameFiles), + SIMON2TALKIE_HB_GameFiles, + GF_TALKIE, + Common::HB_ISR, + Common::kPlatformPC, + }, + + // Simon the Sorcerer 2 - Italian DOS CD + { + "simon2", + GType_SIMON2, + GID_SIMON2TALKIE_IT, + "CD", + ARRAYSIZE(SIMON2TALKIE_IT_GameFiles), + SIMON2TALKIE_IT_GameFiles, + GF_TALKIE, + Common::IT_ITA, + // FIXME: DOS version which uses WAV format + Common::kPlatformWindows, + }, + + // Simon the Sorcerer 2 - Spanish DOS CD + { + "simon2", + GType_SIMON2, + GID_SIMON2TALKIE_ES, + "CD", + ARRAYSIZE(SIMON2TALKIE_ES_GameFiles), + SIMON2TALKIE_ES_GameFiles, + GF_TALKIE, + Common::ES_ESP, + Common::kPlatformPC, + }, + + // Simon the Sorcerer 2 - English Windows CD + { + "simon2", + GType_SIMON2, + GID_SIMON2WIN, + "CD", + ARRAYSIZE(SIMON2WIN_GameFiles), + SIMON2WIN_GameFiles, + GF_TALKIE, + Common::EN_ANY, + Common::kPlatformWindows, + }, + + // Simon the Sorcerer 2 - German Windows CD + { + "simon2", + GType_SIMON2, + GID_SIMON2WIN_DE, + "CD", + ARRAYSIZE(SIMON2WIN_DE_GameFiles), + SIMON2WIN_DE_GameFiles, + GF_TALKIE, + Common::DE_DEU, + Common::kPlatformWindows, + }, + + // Simon the Sorcerer 2 - German Windows CD 1.1 + { + "simon2", + GType_SIMON2, + GID_SIMON2WIN_DE2, + "CD", + ARRAYSIZE(SIMON2WIN_DE2_GameFiles), + SIMON2WIN_DE2_GameFiles, + GF_TALKIE, + Common::DE_DEU, + Common::kPlatformWindows, + }, + + // Simon the Sorcerer 2 - Polish Windows CD + { + "simon2", + GType_SIMON2, + GID_SIMON2WIN_PL, + "CD", + ARRAYSIZE(SIMON2WIN_PL_GameFiles), + SIMON2WIN_PL_GameFiles, + GF_TALKIE, + Common::PL_POL, + Common::kPlatformWindows, + }, + + // The Feeble Files - English Amiga CD + { + "feeble", + GType_FF, + GID_FEEBLEFILES_AMI_UK, + "CD", + ARRAYSIZE(FEEBLEFILES_AMI_UK_GameFiles), + FEEBLEFILES_AMI_UK_GameFiles, + GF_OLD_BUNDLE | GF_ZLIBCOMP | GF_TALKIE, + Common::EN_ANY, + Common::kPlatformAmiga, + }, + + // The Feeble Files - German Amiga CD + { + "feeble", + GType_FF, + GID_FEEBLEFILES_AMI_DE, + "CD", + ARRAYSIZE(FEEBLEFILES_AMI_DE_GameFiles), + FEEBLEFILES_AMI_DE_GameFiles, + GF_OLD_BUNDLE | GF_ZLIBCOMP | GF_TALKIE, + Common::DE_DEU, + Common::kPlatformAmiga, + }, + + // The Feeble Files - English Macintosh CD + { + "feeble", + GType_FF, + GID_FEEBLEFILES_MAC_UK, + "CD", + ARRAYSIZE(FEEBLEFILES_MAC_UK_GameFiles), + FEEBLEFILES_MAC_UK_GameFiles, + GF_OLD_BUNDLE | GF_ZLIBCOMP | GF_TALKIE, + Common::EN_ANY, + Common::kPlatformMacintosh, + }, + + // The Feeble Files - French Macintosh CD + { + "feeble", + GType_FF, + GID_FEEBLEFILES_MAC_FR, + "CD", + ARRAYSIZE(FEEBLEFILES_MAC_FR_GameFiles), + FEEBLEFILES_MAC_FR_GameFiles, + GF_OLD_BUNDLE | GF_ZLIBCOMP | GF_TALKIE, + Common::FR_FRA, + Common::kPlatformMacintosh, + }, + + // The Feeble Files - German Macintosh CD + { + "feeble", + GType_FF, + GID_FEEBLEFILES_MAC_DE, + "CD", + ARRAYSIZE(FEEBLEFILES_MAC_DE_GameFiles), + FEEBLEFILES_MAC_DE_GameFiles, + GF_OLD_BUNDLE | GF_ZLIBCOMP | GF_TALKIE, + Common::DE_DEU, + Common::kPlatformMacintosh, + }, + + // The Feeble Files - Spanish Macintosh CD + { + "feeble", + GType_FF, + GID_FEEBLEFILES_MAC_ES, + "CD", + ARRAYSIZE(FEEBLEFILES_MAC_ES_GameFiles), + FEEBLEFILES_MAC_ES_GameFiles, + GF_OLD_BUNDLE | GF_ZLIBCOMP | GF_TALKIE, + Common::ES_ESP, + Common::kPlatformMacintosh, + }, + + // The Feeble Files - English Windows 2CD + { + "feeble", + GType_FF, + GID_FEEBLEFILES_2CD, + "2CD", + ARRAYSIZE(FEEBLEFILES_2CD_GameFiles), + FEEBLEFILES_2CD_GameFiles, + GF_OLD_BUNDLE | GF_TALKIE, + Common::EN_ANY, + Common::kPlatformWindows, + }, + + // The Feeble Files - English Windows 4CD + { + "feeble", + GType_FF, + GID_FEEBLEFILES_4CD, + "4CD", + ARRAYSIZE(FEEBLEFILES_4CD_GameFiles), + FEEBLEFILES_4CD_GameFiles, + GF_OLD_BUNDLE | GF_TALKIE, + Common::EN_ANY, + Common::kPlatformWindows, + }, + + // The Feeble Files - French Windows 4CD + { + "feeble", + GType_FF, + GID_FEEBLEFILES_FR, + "4CD", + ARRAYSIZE(FEEBLEFILES_FR_GameFiles), + FEEBLEFILES_FR_GameFiles, + GF_OLD_BUNDLE | GF_TALKIE, + Common::FR_FRA, + Common::kPlatformWindows, + }, + + // The Feeble Files - German Windows 4CD + { + "feeble", + GType_FF, + GID_FEEBLEFILES_DE, + "4CD", + ARRAYSIZE(FEEBLEFILES_DE_GameFiles), + FEEBLEFILES_DE_GameFiles, + GF_OLD_BUNDLE | GF_TALKIE, + Common::DE_DEU, + Common::kPlatformWindows, + }, + + // The Feeble Files - Italian Windows 4CD + { + "feeble", + GType_FF, + GID_FEEBLEFILES_IT, + "4CD", + ARRAYSIZE(FEEBLEFILES_IT_GameFiles), + FEEBLEFILES_IT_GameFiles, + GF_OLD_BUNDLE | GF_TALKIE, + Common::IT_ITA, + Common::kPlatformWindows, + }, + + // The Feeble Files - Spanish Windows 4CD + { + "feeble", + GType_FF, + GID_FEEBLEFILES_ES, + "4CD", + ARRAYSIZE(FEEBLEFILES_ES_GameFiles), + FEEBLEFILES_ES_GameFiles, + GF_OLD_BUNDLE | GF_TALKIE, + Common::ES_ESP, + Common::kPlatformWindows, + }, + + // Simon the Sorcerer's Puzzle Pack - Demon in my Pocket + { + "dimp", + GType_PP, + GID_DIMP, + "CD", + ARRAYSIZE(DIMP_GameFiles), + DIMP_GameFiles, + GF_OLD_BUNDLE | GF_TALKIE, + Common::EN_ANY, + Common::kPlatformWindows, + }, + + // Simon the Sorcerer's Puzzle Pack - Jumble + { + "jumble", + GType_PP, + GID_JUMBLE, + "CD", + ARRAYSIZE(JUMBLE_GameFiles), + JUMBLE_GameFiles, + GF_OLD_BUNDLE | GF_TALKIE, + Common::EN_ANY, + Common::kPlatformWindows, + }, + + // Simon the Sorcerer's Puzzle Pack - NoPatience + { + "puzzle", + GType_PP, + GID_PUZZLE, + "CD", + ARRAYSIZE(PUZZLE_GameFiles), + PUZZLE_GameFiles, + GF_OLD_BUNDLE | GF_TALKIE, + Common::EN_ANY, + Common::kPlatformWindows, + }, + + // Simon the Sorcerer's Puzzle Pack - Swampy Adventures + { + "swampy", + GType_PP, + GID_SWAMPY, + "CD", + ARRAYSIZE(SWAMPY_GameFiles), + SWAMPY_GameFiles, + GF_OLD_BUNDLE | GF_TALKIE, + Common::EN_ANY, + Common::kPlatformWindows, + }, + +}; + +DetectedGame toDetectedGame(const GameDescription &g) { + const char *title; + + const PlainGameDescriptor *sg = simonGames; + while (sg->gameid) { + if (!scumm_stricmp(g.name, sg->gameid)) + title = sg->description; + sg++; + } + + DetectedGame dg(g.name, title, g.language, g.platform); + dg.updateDesc(g.extra); + return dg; +} + +static int detectGame(const FSList *fslist, Common::Language language, Common::Platform platform, int*& returnMatches) { + int gamesCount = ARRAYSIZE(gameDescriptions); + int filesCount; + + typedef Common::HashMap<Common::String, bool, Common::CaseSensitiveString_Hash, Common::CaseSensitiveString_EqualTo> StringSet; + StringSet filesList; + + typedef Common::StringMap StringMap; + StringMap filesMD5; + + Common::String tstr, tstr2; + + int i, j; + char md5str[32+1]; + uint8 md5sum[16]; + + int matched[ARRAYSIZE(gameDescriptions)]; + int matchedCount = 0; + bool fileMissing; + GameFileDescription *fileDesc; + + // First we compose list of files which we need MD5s for + for (i = 0; i < gamesCount; i++) { + for (j = 0; j < gameDescriptions[i].filesCount; j++) { + tstr = Common::String(gameDescriptions[i].filesDescriptions[j].fileName); + tstr.toLowercase(); + tstr2 = tstr + "."; + filesList[tstr] = true; + filesList[tstr2] = true; + } + } + + if (fslist != NULL) { + for (FSList::const_iterator file = fslist->begin(); file != fslist->end(); ++file) { + if (file->isDirectory()) continue; + tstr = file->name(); + tstr.toLowercase(); + tstr2 = tstr + "."; + + if (!filesList.contains(tstr) && !filesList.contains(tstr2)) continue; + + if (!Common::md5_file(*file, md5sum, FILE_MD5_BYTES)) continue; + for (j = 0; j < 16; j++) { + sprintf(md5str + j*2, "%02x", (int)md5sum[j]); + } + filesMD5[tstr] = Common::String(md5str); + filesMD5[tstr2] = Common::String(md5str); + } + } else { + Common::File testFile; + + for (StringSet::const_iterator file = filesList.begin(); file != filesList.end(); ++file) { + tstr = file->_key; + tstr.toLowercase(); + + if (!filesMD5.contains(tstr)) { + if (testFile.open(file->_key)) { + testFile.close(); + + if (Common::md5_file(file->_key.c_str(), md5sum, FILE_MD5_BYTES)) { + for (j = 0; j < 16; j++) { + sprintf(md5str + j*2, "%02x", (int)md5sum[j]); + } + filesMD5[tstr] = Common::String(md5str); + } + } + } + } + } + + for (i = 0; i < gamesCount; i++) { + filesCount = gameDescriptions[i].filesCount; + fileMissing = false; + + // Try to open all files for this game + for (j = 0; j < filesCount; j++) { + fileDesc = &gameDescriptions[i].filesDescriptions[j]; + tstr = fileDesc->fileName; + tstr.toLowercase(); + tstr2 = tstr + "."; + + if (!filesMD5.contains(tstr) && !filesMD5.contains(tstr2)) { + fileMissing = true; + break; + } + if (strcmp(fileDesc->md5, filesMD5[tstr].c_str()) && strcmp(fileDesc->md5, filesMD5[tstr2].c_str())) { + fileMissing = true; + break; + } + } + if (!fileMissing) { + debug(2, "Found game: %s", toDetectedGame(gameDescriptions[i]).description.c_str()); + matched[matchedCount++] = i; + } + } + + if (!filesMD5.empty() && (matchedCount == 0)) { + printf("MD5s of your game version are unknown. Please, report following data to\n"); + printf("ScummVM team along with your game name and version:\n"); + + for (StringMap::const_iterator file = filesMD5.begin(); file != filesMD5.end(); ++file) + printf("%s: %s\n", file->_key.c_str(), file->_value.c_str()); + } + + // We have some resource sets which are superpositions of other + // Now remove lesser set if bigger matches too + + if (matchedCount > 1) { + // Search max number + int maxcount = 0; + for (i = 0; i < matchedCount; i++) { + maxcount = MAX(gameDescriptions[matched[i]].filesCount, maxcount); + } + + // Now purge targets with number of files lesser than max + for (i = 0; i < matchedCount; i++) { + if ((gameDescriptions[matched[i]].language != language && language != Common::UNK_LANG) || + (gameDescriptions[matched[i]].platform != platform && platform != Common::kPlatformUnknown)) { + debug(2, "Purged %s", toDetectedGame(gameDescriptions[matched[i]]).description.c_str()); + matched[i] = -1; + continue; + } + + if (gameDescriptions[matched[i]].filesCount < maxcount) { + debug(2, "Purged: %s", toDetectedGame(gameDescriptions[matched[i]]).description.c_str()); + matched[i] = -1; + } + } + } + + + returnMatches = (int *)malloc(matchedCount * sizeof(int)); + j = 0; + for (i = 0; i < matchedCount; i++) + if (matched[i] != -1) + returnMatches[j++] = matched[i]; + return j; +} + +bool SimonEngine::initGame() { + uint16 gameCount = ARRAYSIZE(gameDescriptions); + int gameNumber = -1; + + DetectedGameList detectedGames; + int count; + int* matches; + Common::Language language = Common::UNK_LANG; + Common::Platform platform = Common::kPlatformUnknown; + + if (ConfMan.hasKey("language")) + language = Common::parseLanguage(ConfMan.get("language")); + if (ConfMan.hasKey("platform")) + platform = Common::parsePlatform(ConfMan.get("platform")); + + count = detectGame(NULL, language, platform, matches); + + if (count == 0) { + warning("No valid games were found in the specified directory."); + return false; + } + + if (count != 1) + warning("Conflicting targets detected (%d)", count); + + gameNumber = matches[0]; + + free(matches); + + if (gameNumber >= gameCount || gameNumber == -1) { + error("SimonEngine::loadGame wrong gameNumber"); + } + + debug(2, "Running %s", toDetectedGame(gameDescriptions[gameNumber]).description.c_str()); + + _gameDescription = &gameDescriptions[gameNumber]; + + return true; +} + +DetectedGameList GAME_detectGames(const FSList &fslist) { + DetectedGameList detectedGames; + int count; + int* matches; + count = detectGame(&fslist, Common::UNK_LANG, Common::kPlatformUnknown, matches); + + for (int i = 0; i < count; i++) + detectedGames.push_back(toDetectedGame(gameDescriptions[matches[i]])); + free(matches); + return detectedGames; +} + +} // End of namespace Simon diff --git a/engines/agos/icons.cpp b/engines/agos/icons.cpp new file mode 100644 index 0000000000..2904d9024d --- /dev/null +++ b/engines/agos/icons.cpp @@ -0,0 +1,571 @@ +/* ScummVM - Scumm Interpreter + * Copyright (C) 2001 Ludvig Strigeus + * Copyright (C) 2001-2006 The ScummVM project + * + * 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. + * + * $URL$ + * $Id$ + * + */ + +#include "common/stdafx.h" + +#include "common/file.h" + +#include "agos/agos.h" + +namespace Simon { + +void SimonEngine::loadIconFile() { + Common::File in; + uint size; + + in.open(getFileName(GAME_ICONFILE)); + if (in.isOpen() == false) + error("Can't open icons file '%s'", getFileName(GAME_ICONFILE)); + + size = in.size(); + + _iconFilePtr = (byte *)malloc(size); + if (_iconFilePtr == NULL) + error("Out of icon memory"); + + in.read(_iconFilePtr, size); + in.close(); +} + +void SimonEngine::loadIconData() { + loadZone(8); + VgaPointersEntry *vpe = &_vgaBufferPointers[8]; + + byte *src = vpe->vgaFile2 + READ_LE_UINT32(vpe->vgaFile2 + 8); + + _iconFilePtr = (byte *)malloc(43 * 336); + if (_iconFilePtr == NULL) + error("Out of icon memory"); + + memcpy(_iconFilePtr, src, 43 * 336); + o_unfreezeZones(); +} + +// Thanks to Stuart Caie for providing the original +// C conversion upon which this function is based. +void decompressIconAmiga (byte *dst, byte *src, byte base, uint pitch) { + byte icon_pln[288]; + byte *i, *o, x, y; + + // Decode RLE planar icon data + i = src; + o = icon_pln; + while (o < &icon_pln[288]) { + x = *i++; + if (x < 128) { + do { + *o++ = *i++; + *o++ = *i++; + *o++ = *i++; + } while (x-- > 0); + } else { + x = 256 - x; + do { + *o++ = i[0]; + *o++ = i[1]; + *o++ = i[2]; + } while (x-- > 0); + i += 3; + } + } + + // Translate planar data to chunky (very slow method) + for (y = 0; y < 24; y++) { + for (x = 0; x < 24; x++) { + byte pixel = + (icon_pln[(( y) * 3) + (x >> 3)] & (1 << (7 - (x & 7))) ? 1 : 0) + | (icon_pln[((24 + y) * 3) + (x >> 3)] & (1 << (7 - (x & 7))) ? 2 : 0) + | (icon_pln[((48 + y) * 3) + (x >> 3)] & (1 << (7 - (x & 7))) ? 4 : 0) + | (icon_pln[((72 + y) * 3) + (x >> 3)] & (1 << (7 - (x & 7))) ? 8 : 0); + if (pixel) + dst[x] = pixel | base; + } + dst += pitch; + } +} + +static void decompressIcon(byte *dst, byte *src, uint w, uint h_org, byte base, uint pitch) { + int8 reps; + byte color_1, color_2; + byte *dst_org = dst; + uint h = h_org; + + for (;;) { + reps = *src++; + if (reps < 0) { + reps--; + color_1 = *src >> 4; + if (color_1 != 0) + color_1 |= base; + color_2 = *src++ & 0xF; + if (color_2 != 0) + color_2 |= base; + + do { + if (color_1 != 0) + *dst = color_1; + dst += pitch; + if (color_2 != 0) + *dst = color_2; + dst += pitch; + + // reached bottom? + if (--h == 0) { + // reached right edge? + if (--w == 0) + return; + dst = ++dst_org; + h = h_org; + } + } while (++reps != 0); + } else { + do { + color_1 = *src >> 4; + if (color_1 != 0) + *dst = color_1 | base; + dst += pitch; + + color_2 = *src++ & 0xF; + if (color_2 != 0) + *dst = color_2 | base; + dst += pitch; + + // reached bottom? + if (--h == 0) { + // reached right edge? + if (--w == 0) + return; + dst = ++dst_org; + h = h_org; + } + } while (--reps >= 0); + } + } +} + +void SimonEngine::draw_icon_c(WindowBlock *window, uint icon, uint x, uint y) { + byte *dst; + byte *src; + + _lockWord |= 0x8000; + dst = getFrontBuf(); + + if (getGameType() == GType_SIMON1) { + // Simon 1 + dst += (x + window->x) * 8; + dst += (y * 25 + window->y) * _dxSurfacePitch; + + if (getPlatform() == Common::kPlatformAmiga) { + src = _iconFilePtr; + src += READ_BE_UINT32(&((uint32 *)src)[icon]); + decompressIconAmiga (dst, src, 224, _dxSurfacePitch); + } else { + src = _iconFilePtr; + src += READ_LE_UINT16(&((uint16 *)src)[icon]); + decompressIcon(dst, src, 24, 12, 224, _dxSurfacePitch); + } + } else { + // Simon 2 + dst += 110; + dst += x; + dst += (y + window->y) * _dxSurfacePitch; + + src = _iconFilePtr; + src += READ_LE_UINT16(&((uint16 *)src)[icon * 2 + 0]); + decompressIcon(dst, src, 20, 10, 224, _dxSurfacePitch); + + src = _iconFilePtr; + src += READ_LE_UINT16(&((uint16 *)src)[icon * 2 + 1]); + decompressIcon(dst, src, 20, 10, 208, _dxSurfacePitch); + } + + _lockWord &= ~0x8000; +} + +void SimonEngine::drawIconArray(uint num, Item *itemRef, int line, int classMask) { + if (getGameType() == GType_FF) { + drawIconArray_FF(num, itemRef, line, classMask); + } else { + drawIconArray_Simon(num, itemRef, line, classMask); + } +} + +void SimonEngine::drawIconArray_Simon(uint num, Item *itemRef, int line, int classMask) { + Item *item_ptr_org = itemRef; + WindowBlock *window; + uint width, height; + uint k, i, curWidth; + bool item_again, showArrows; + uint x_pos, y_pos; + const int iconSize = (getGameType() == GType_SIMON1) ? 1 : 20; + + window = _windowArray[num & 7]; + + if (getGameType() == GType_SIMON1) { + width = window->width / 3; + height = window->height / 3; + } else { + width = 100; + height = 40; + } + + i = 0; + + if (window == NULL) + return; + + if (window->iconPtr) + removeIconArray(num); + + window->iconPtr = (IconBlock *) malloc(sizeof(IconBlock)); + window->iconPtr->itemRef = itemRef; + window->iconPtr->upArrow = -1; + window->iconPtr->downArrow = -1; + window->iconPtr->line = line; + window->iconPtr->classMask = classMask; + + itemRef = derefItem(itemRef->child); + + while (itemRef && line-- != 0) { + curWidth = 0; + while (itemRef && width > curWidth) { + if ((classMask == 0 || itemRef->classFlags & classMask) && has_item_childflag_0x10(itemRef)) + curWidth += iconSize; + itemRef = derefItem(itemRef->sibling); + } + } + + if (itemRef == NULL) { + window->iconPtr->line = 0; + itemRef = derefItem(item_ptr_org->child); + } + + x_pos = 0; + y_pos = 0; + k = 0; + item_again = false; + showArrows = false; + + while (itemRef) { + if ((classMask == 0 || itemRef->classFlags & classMask) && has_item_childflag_0x10(itemRef)) { + if (item_again == false) { + window->iconPtr->iconArray[k].item = itemRef; + if (getGameType() == GType_SIMON1) { + draw_icon_c(window, itemGetIconNumber(itemRef), x_pos * 3, y_pos); + window->iconPtr->iconArray[k].boxCode = + setupIconHitArea(window, 0, x_pos * 3, y_pos, itemRef); + } else { + draw_icon_c(window, itemGetIconNumber(itemRef), x_pos, y_pos); + window->iconPtr->iconArray[k].boxCode = + setupIconHitArea(window, 0, x_pos, y_pos, itemRef); + } + k++; + } else { + window->iconPtr->iconArray[k].item = NULL; + showArrows = 1; + } + + x_pos += iconSize; + if (x_pos >= width) { + x_pos = 0; + y_pos += iconSize; + if (y_pos >= height) + item_again = true; + } + } + itemRef = derefItem(itemRef->sibling); + } + + window->iconPtr->iconArray[k].item = NULL; + + if (showArrows != 0 || window->iconPtr->line != 0) { + /* Plot arrows and add their boxes */ + defineArrowBoxes(window); + window->iconPtr->upArrow = _scrollUpHitArea; + window->iconPtr->downArrow = _scrollDownHitArea; + } +} + +void SimonEngine::drawIconArray_FF(uint num, Item *itemRef, int line, int classMask) { + Item *item_ptr_org = itemRef; + WindowBlock *window; + uint16 flagnumber = 201; + uint16 iconperline = 458; + uint16 iconsdown = 384; + uint16 idone = 0; + uint16 icount = 0; + uint16 xp = 188, yp = 306; + int k; + _iOverflow = 0; + + line = _variableArray[30]; + if (line == 0) + _variableArray[31] = 0; + + window = _windowArray[num & 7]; + if (window == NULL) + return; + + for (k = flagnumber; k <= flagnumber + 18; k++) + _variableArray[k] = 0; + + if (window->iconPtr) + removeIconArray(num); + + window->iconPtr=(IconBlock *)malloc(sizeof(IconBlock)); + window->iconPtr->itemRef = itemRef; + window->iconPtr->upArrow = -1; + window->iconPtr->downArrow = -1; + window->iconPtr->line = line; + window->iconPtr->classMask = classMask; + + itemRef = derefItem(itemRef->child); + k = flagnumber; + + while (itemRef && (line > 65)) { + uint16 ct = xp; + while (itemRef && ct < iconperline) { + if ((classMask == 0) || ((itemRef->classFlags & classMask) != 0)) { + if (has_item_childflag_0x10(itemRef)) { + ct += 45; + k++; + } + } + itemRef = derefItem(itemRef->sibling); + } + line -= 52; + if (k == (flagnumber + 18)) + k = flagnumber; + } + yp -= line; // Adjust starting y + + if (itemRef == NULL) { + window->iconPtr->line = 0; + itemRef = derefItem(item_ptr_org->child); + } + + while (itemRef) { + if ((classMask != 0) && ((itemRef->classFlags & classMask) == 0)) + goto l1; + if (has_item_childflag_0x10(itemRef) == 0) + goto l1; + if (!idone) { +/* + * Create thee icon and graphics rendering + */ + window->iconPtr->iconArray[icount].item = itemRef; + _variableArray[k] = itemGetIconNumber(itemRef); + window->iconPtr->iconArray[icount++].boxCode = + setupIconHitArea(window, k++, xp, yp, itemRef); + } else { +/* + * Just remember the overflow has occured + */ + window->iconPtr->iconArray[icount].item = NULL; /* END MARKINGS */ + _iOverflow = 1; + } + xp += 45; + if (xp >= iconperline) { /* End of line ? */ + if (k == (flagnumber + 18)) + k = flagnumber; + xp = 188; + yp += 52; /* Move down */ + if (yp >= iconsdown) { /* Full ? */ + idone = 1; /* Note completed screen */ + } + } +l1:; itemRef = derefItem(itemRef->sibling); + } + window->iconPtr->iconArray[icount].item = NULL; /* END MARKINGS */ + if (_variableArray[30] == 0) { + if (yp != 306) + _variableArray[31] = 52; + if ((xp == 188) && (yp == 358)) + _variableArray[31] = 0; + } + + /* Plot arrows and add their boxes */ + defineArrowBoxes(window); + window->iconPtr->upArrow = _scrollUpHitArea; + window->iconPtr->downArrow = _scrollDownHitArea; +} + +void SimonEngine::defineArrowBoxes(WindowBlock *window) { + HitArea *ha; + + ha = findEmptyHitArea(); + _scrollUpHitArea = ha - _hitAreas; + if (getGameType() == GType_FF) { + ha->x = 496; + ha->y = 279; + ha->width = 30; + ha->height = 45; + ha->flags = kBFBoxInUse | kBFNoTouchName; + ha->id = 0x7FFB; + ha->priority = 100; + ha->window = window; + ha->verb = 1; + } else if (getGameType() == GType_SIMON2) { + ha->x = 81; + ha->y = 158; + ha->width = 12; + ha->height = 26; + ha->flags = kBFBoxInUse | kBFNoTouchName; + ha->id = 0x7FFB; + ha->priority = 100; + ha->window = window; + ha->verb = 1; + } else { + ha->x = 308; + ha->y = 149; + ha->width = 12; + ha->height = 17; + ha->flags = kBFBoxInUse | kBFNoTouchName; + ha->id = 0x7FFB; + ha->priority = 100; + ha->window = window; + ha->verb = 1; + } + + ha = findEmptyHitArea(); + _scrollDownHitArea = ha - _hitAreas; + + if (getGameType() == GType_FF) { + ha->x = 496; + ha->y = 324; + ha->width = 30; + ha->height = 44; + ha->flags = kBFBoxInUse | kBFNoTouchName; + ha->id = 0x7FFC; + ha->priority = 100; + ha->window = window; + ha->verb = 1; + } else if (getGameType() == GType_SIMON2) { + ha->x = 227; + ha->y = 162; + ha->width = 12; + ha->height = 26; + ha->flags = kBFBoxInUse | kBFNoTouchName; + ha->id = 0x7FFC; + ha->priority = 100; + ha->window = window; + ha->verb = 1; + } else { + ha->x = 308; + ha->y = 176; + ha->width = 12; + ha->height = 17; + ha->flags = kBFBoxInUse | kBFNoTouchName; + ha->id = 0x7FFC; + ha->priority = 100; + ha->window = window; + ha->verb = 1; + + stopAnimateSimon1(128); + loadSprite(0, 1, 128, 0, 0, 14); + } +} + +uint SimonEngine::setupIconHitArea(WindowBlock *window, uint num, uint x, uint y, Item *item_ptr) { + HitArea *ha; + + ha = findEmptyHitArea(); + + if (getGameType() == GType_FF) { + ha->x = x; + ha->y = y; + ha->item_ptr = item_ptr; + ha->width = 45; + ha->height = 44; + ha->flags = kBFBoxInUse | kBFBoxItem; + ha->id = num; + ha->priority = 100; + ha->verb = 208; + } else if (getGameType() == GType_SIMON2) { + ha->x = x + 110; + ha->y = window->y + y; + ha->item_ptr = item_ptr; + ha->width = 20; + ha->height = 20; + ha->flags = kBFDragBox | kBFBoxInUse | kBFBoxItem; + ha->id = 0x7FFD; + ha->priority = 100; + ha->verb = 208; + } else { + ha->x = (x + window->x) * 8; + ha->y = y * 25 + window->y; + ha->item_ptr = item_ptr; + ha->width = 24; + ha->height = 24; + ha->flags = kBFDragBox | kBFBoxInUse | kBFBoxItem; + ha->id = 0x7FFD; + ha->priority = 100; + ha->verb = 208; + } + + return ha - _hitAreas; +} + +void SimonEngine::removeIconArray(uint num) { + WindowBlock *window; + uint16 curWindow; + uint16 i; + + window = _windowArray[num & 7]; + curWindow = _curWindow; + + if (window == NULL || window->iconPtr == NULL) + return; + + if (getGameType() == GType_SIMON1 || getGameType() == GType_SIMON2) { + changeWindow(num); + windowPutChar(12); + changeWindow(curWindow); + } + + for (i = 0; window->iconPtr->iconArray[i].item != NULL; i++) { + delete_hitarea_by_index(window->iconPtr->iconArray[i].boxCode); + } + + if (window->iconPtr->upArrow != -1) { + delete_hitarea_by_index(window->iconPtr->upArrow); + } + + if (window->iconPtr->downArrow != -1) { + delete_hitarea_by_index(window->iconPtr->downArrow); + if (getGameType() == GType_SIMON1) + removeArrows(window, num); + } + + free(window->iconPtr); + window->iconPtr = NULL; + + _fcsData1[num] = 0; + _fcsData2[num] = 0; +} + +void SimonEngine::removeArrows(WindowBlock *window, uint num) { + stopAnimateSimon1(128); +} + +} // End of namespace Simon diff --git a/engines/agos/intern.h b/engines/agos/intern.h new file mode 100644 index 0000000000..587eca621c --- /dev/null +++ b/engines/agos/intern.h @@ -0,0 +1,262 @@ +/* ScummVM - Scumm Interpreter + * Copyright (C) 2001 Ludvig Strigeus + * Copyright (C) 2001-2006 The ScummVM project + * + * 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. + * + * $URL$ + * $Id$ + * + */ + +#ifndef AGOS_INTERN_H +#define AGOS_INTERN_H + +namespace Simon { + +struct Child { + Child *next; + uint16 type; +}; + +struct SubRoom : Child { + uint16 subroutine_id; + uint16 roomExitStates; + uint16 roomExit[1]; +}; + +struct SubObject : Child { + uint16 objectName; + uint32 objectFlags; + int16 objectFlagValue[1]; +}; + +struct SubUserChain : Child { + uint16 subroutine_id; + uint16 chChained; +}; + +struct SubUserInherit : Child { + uint16 subroutine_id; + uint16 inMaster; +}; + +struct SubUserFlag : Child { + uint16 subroutine_id; + uint16 userFlags[4]; +}; + +enum { + SubRoom_SIZE = sizeof(SubRoom) - sizeof(uint16), + SubObject_SIZE = sizeof(SubObject) - sizeof(int16) +}; + +struct Item { + uint16 parent; + uint16 child; + uint16 sibling; + int16 noun; + int16 adjective; + int16 state; /* signed int */ + uint16 classFlags; + uint16 itemName; + Child *children; + + Item() { memset(this, 0, sizeof(*this)); } +}; + +struct IconEntry { + Item *item; + uint16 boxCode; +}; + +struct IconBlock { + int16 line; + Item *itemRef; + IconEntry iconArray[64]; + int16 upArrow, downArrow; + uint16 classMask; +}; + +struct WindowBlock { + byte mode; + byte flags; + uint16 x, y; + uint16 width, height; + uint16 textColumn, textRow; + uint16 textColumnOffset, textLength, textMaxLength; + uint16 scrollY; + uint8 fill_color, text_color; + IconBlock *iconPtr; + WindowBlock() { memset(this, 0, sizeof(*this)); } +}; +// note on text offset: +// the actual x-coordinate is: textColumn * 8 + textColumnOffset +// the actual y-coordinate is: textRow * 8 + +enum { + SUBROUTINE_LINE_SMALL_SIZE = 2, + SUBROUTINE_LINE_BIG_SIZE = 8 +}; + +struct Subroutine { + uint16 id; /* subroutine ID */ + uint16 first; /* offset from subroutine start to first subroutine line */ + Subroutine *next; /* next subroutine in linked list */ +}; + +struct SubroutineLine { + uint16 next; + int16 verb; + int16 noun1; + int16 noun2; +}; + +struct TimeEvent { + uint32 time; + uint16 subroutine_id; + TimeEvent *next; +}; + +struct GameSpecificSettings { +#ifndef PALMOS_68K + const char *effects_filename; + const char *speech_filename; + #else + const char effects_filename[12]; + const char speech_filename[12]; + #endif +}; + +enum BoxFlags { + kBFTextBox = 0x1, + kBFBoxSelected = 0x2, + kBFNoTouchName = 0x4, + kBFInvertTouch = 0x8, + kBFDragBox = 0x10, // Simon 1/2 + kBFHyperBox = 0x10, // Feeble Files + kBFBoxInUse = 0x20, + kBFBoxDead = 0x40, + kBFBoxItem = 0x80 +}; + +enum SubObjectFlags { + kOFText = 0x1, + kOFSize = 0x2, + kOFWeight = 0x4, + kOFVolume = 0x8, + kOFIcon = 0x10, + kOFKeyColor1 = 0x20, + kOFKeyColor2 = 0x40, + kOFMenu = 0x80, + kOFNumber = 0x100, + kOFVoice = 0x200 +}; + +enum GameFeatures { + GF_TALKIE = 1 << 0, + GF_OLD_BUNDLE = 1 << 1, + GF_CRUNCHED = 1 << 2, + GF_ZLIBCOMP = 1 << 3 +}; + +enum GameFileTypes { + GAME_BASEFILE = 1 << 0, + GAME_ICONFILE = 1 << 1, + GAME_GMEFILE = 1 << 2, + GAME_STRFILE = 1 << 3, + GAME_RMSLFILE = 1 << 4, + GAME_TBLFILE = 1 << 5, + GAME_XTBLFILE = 1 << 6, + + GAME_GFXIDXFILE = 1 << 7 +}; + +enum GameIds { + GID_ELVIRA, + GID_ELVIRA2, + GID_WAXWORKS, + + GID_SIMON1DOS, + GID_SIMON1DOS_RU, + GID_SIMON1DOS_INF, + GID_SIMON1DOS_INF_RU, + GID_SIMON1DOS_FR, + GID_SIMON1DOS_DE, + GID_SIMON1DOS_IT, + GID_SIMON1DOS_ES, + GID_SIMON1DEMO, + GID_SIMON1AMIGA, + GID_SIMON1AMIGA_FR, + GID_SIMON1AMIGA_DE, + GID_SIMON1AMIGADEMO, + GID_SIMON1CD32, + GID_SIMON1CD32_2, + GID_SIMON1ACORN, + GID_SIMON1ACORNDEMO, + GID_SIMON1TALKIE, + GID_SIMON1TALKIE2, + GID_SIMON1TALKIE_FR, + GID_SIMON1TALKIE_DE, + GID_SIMON1TALKIE_HB, + GID_SIMON1TALKIE_IT, + GID_SIMON1TALKIE_IT2, + GID_SIMON1TALKIE_ES, + GID_SIMON1WIN, + GID_SIMON1WIN_RU, + GID_SIMON1WIN_DE, + + GID_SIMON2DOS, + GID_SIMON2DOS_RU, + GID_SIMON2DOS2, + GID_SIMON2DOS2_RU, + GID_SIMON2DOS_DE, + GID_SIMON2DOS_IT, + GID_SIMON2DEMO, + GID_SIMON2TALKIE, + GID_SIMON2TALKIE2, + GID_SIMON2TALKIE_FR, + GID_SIMON2TALKIE_DE, + GID_SIMON2TALKIE_DE2, + GID_SIMON2TALKIE_HB, + GID_SIMON2TALKIE_IT, + GID_SIMON2TALKIE_ES, + GID_SIMON2WIN, + GID_SIMON2WIN_DE, + GID_SIMON2WIN_DE2, + GID_SIMON2WIN_PL, + + GID_FEEBLEFILES_AMI_DE, + GID_FEEBLEFILES_AMI_UK, + GID_FEEBLEFILES_MAC_FR, + GID_FEEBLEFILES_MAC_DE, + GID_FEEBLEFILES_MAC_ES, + GID_FEEBLEFILES_MAC_UK, + GID_FEEBLEFILES_2CD, + GID_FEEBLEFILES_4CD, + GID_FEEBLEFILES_FR, + GID_FEEBLEFILES_DE, + GID_FEEBLEFILES_IT, + GID_FEEBLEFILES_ES, + + GID_DIMP, + GID_JUMBLE, + GID_PUZZLE, + GID_SWAMPY +}; + +} // End of namespace Simon + +#endif diff --git a/engines/agos/items.cpp b/engines/agos/items.cpp new file mode 100644 index 0000000000..b908d0f5ab --- /dev/null +++ b/engines/agos/items.cpp @@ -0,0 +1,2552 @@ +/* ScummVM - Scumm Interpreter + * Copyright (C) 2001 Ludvig Strigeus + * Copyright (C) 2001-2006 The ScummVM project + * + * 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. + * + * $URL$ + * $Id$ + * + */ + +// Item script opcodes for Simon1/Simon2 + +#include "common/stdafx.h" + +#include "common/system.h" + +#include "agos/animation.h" +#include "agos/agos.h" + +#ifdef _WIN32_WCE +extern bool isSmartphone(void); +#endif + +namespace Simon { + +// Opcode table +void SimonEngine::setupOpcodes() { + // This opcode table is for Simon 1. Changes for Simon 2 and FF are + // made below. + + static OpcodeProc opcode_table[200] = { + // 0 - 4 + NULL, + &SimonEngine::o_at, + &SimonEngine::o_notAt, + NULL, + NULL, + // 5 - 9 + &SimonEngine::o_carried, + &SimonEngine::o_notCarried, + &SimonEngine::o_isAt, + NULL, + NULL, + // 10 - 14 + NULL, + &SimonEngine::o_zero, + &SimonEngine::o_notZero, + &SimonEngine::o_eq, + &SimonEngine::o_notEq, + // 15 - 19 + &SimonEngine::o_gt, + &SimonEngine::o_lt, + &SimonEngine::o_eqf, + &SimonEngine::o_notEqf, + &SimonEngine::o_ltf, + // 20 - 24 + &SimonEngine::o_gtf, + NULL, + NULL, + &SimonEngine::o_chance, + NULL, + // 25 - 29 + &SimonEngine::o_isRoom, + &SimonEngine::o_isObject, + &SimonEngine::o_state, + &SimonEngine::o_oflag, + NULL, + // 30 - 34 + NULL, + &SimonEngine::o_destroy, + NULL, + &SimonEngine::o_place, + NULL, + // 35 - 39 + NULL, + &SimonEngine::o_copyff, + NULL, + NULL, + NULL, + // 40 - 44 + NULL, + &SimonEngine::o_clear, + &SimonEngine::o_let, + &SimonEngine::o_add, + &SimonEngine::o_sub, + // 45 - 49 + &SimonEngine::o_addf, + &SimonEngine::o_subf, + &SimonEngine::o_mul, + &SimonEngine::o_div, + &SimonEngine::o_mulf, + // 50 - 54 + &SimonEngine::o_divf, + &SimonEngine::o_mod, + &SimonEngine::o_modf, + &SimonEngine::o_random, + NULL, + // 55 - 59 + &SimonEngine::o_goto, + &SimonEngine::o_oset, + &SimonEngine::o_oclear, + &SimonEngine::o_putBy, + &SimonEngine::o_inc, + // 60 - 64 + &SimonEngine::o_dec, + &SimonEngine::o_setState, + &SimonEngine::o_print, + &SimonEngine::o_message, + &SimonEngine::o_msg, + // 65 - 69 + &SimonEngine::o_addTextBox, + &SimonEngine::o_setShortText, + &SimonEngine::o_setLongText, + &SimonEngine::o_end, + &SimonEngine::o_done, + // 70 - 74 + NULL, + &SimonEngine::o_process, + NULL, + NULL, + NULL, + // 75 - 79 + NULL, + &SimonEngine::o_when, + &SimonEngine::o_if1, + &SimonEngine::o_if2, + &SimonEngine::o_isCalled, + // 80 - 84 + &SimonEngine::o_is, + NULL, + &SimonEngine::o_debug, + NULL, + NULL, + // 85 - 89 + NULL, + NULL, + &SimonEngine::o_comment, + &SimonEngine::o_haltAnimation, + &SimonEngine::o_restartAnimation, + // 90 - 94 + &SimonEngine::o_getParent, + &SimonEngine::o_getNext, + &SimonEngine::o_getChildren, + NULL, + NULL, + // 95 - 99 + NULL, + &SimonEngine::o_picture, + &SimonEngine::o_loadZone, + NULL, + NULL, + // 100 - 104 + &SimonEngine::o_killAnimate, + &SimonEngine::o_defWindow, + &SimonEngine::o_window, + &SimonEngine::o_cls, + &SimonEngine::o_closeWindow, + // 105 - 109 + NULL, + NULL, + &SimonEngine::o_addBox, + &SimonEngine::o_delBox, + &SimonEngine::o_enableBox, + // 110 - 114 + &SimonEngine::o_disableBox, + &SimonEngine::o_moveBox, + NULL, + NULL, + &SimonEngine::o_doIcons, + // 115 - 119 + &SimonEngine::o_isClass, + &SimonEngine::o_setClass, + &SimonEngine::o_unsetClass, + NULL, + &SimonEngine::o_waitSync, + // 120 - 124 + &SimonEngine::o_sync, + &SimonEngine::o_defObj, + NULL, + NULL, + NULL, + // 125 - 129 + &SimonEngine::o_here, + &SimonEngine::o_doClassIcons, + NULL, + &SimonEngine::o_waitEndTune, + &SimonEngine::o_ifEndTune, + // 130 - 134 + &SimonEngine::o_setAdjNoun, + NULL, + &SimonEngine::o_saveUserGame, + &SimonEngine::o_loadUserGame, + &SimonEngine::o_stopTune, + // 135 - 139 + &SimonEngine::o_pauseGame, + &SimonEngine::o_copysf, + &SimonEngine::o_restoreIcons, + &SimonEngine::o_freezeZones, + &SimonEngine::o_placeNoIcons, + // 140 - 144 + &SimonEngine::o_clearTimers, + &SimonEngine::o_setDollar, + &SimonEngine::o_isBox, + &SimonEngine::o_doTable, + NULL, + // 145 - 149 + NULL, + NULL, + NULL, + NULL, + NULL, + // 150 - 154 + NULL, + &SimonEngine::o_storeItem, + &SimonEngine::o_getItem, + &SimonEngine::o_bSet, + &SimonEngine::o_bClear, + // 155 - 159 + &SimonEngine::o_bZero, + &SimonEngine::o_bNotZero, + &SimonEngine::o_getOValue, + &SimonEngine::o_setOValue, + NULL, + // 160 - 164 + &SimonEngine::o_ink, + &SimonEngine::o_screenTextBox, + &SimonEngine::o_screenTextMsg, + &SimonEngine::o_playEffect, + &SimonEngine::o_getDollar2, + // 165 - 169 + &SimonEngine::o_isAdjNoun, + &SimonEngine::o_b2Set, + &SimonEngine::o_b2Clear, + &SimonEngine::o_b2Zero, + &SimonEngine::o_b2NotZero, + // 170 - 174 + NULL, + NULL, + NULL, + NULL, + NULL, + // 175 - 179 + &SimonEngine::o_lockZones, + &SimonEngine::o_unlockZones, + NULL, + &SimonEngine::o_getPathPosn, + &SimonEngine::o_scnTxtLongText, + // 180 - 184 + &SimonEngine::o_mouseOn, + NULL, + NULL, + NULL, + &SimonEngine::o_unloadZone, + // 185 - 189 + NULL, + &SimonEngine::o_unfreezeZones, + NULL, + NULL, + NULL, + // 190 - 194 + NULL, + NULL, + NULL, + NULL, + NULL, + // 195 - 199 + NULL, + NULL, + NULL, + NULL, + NULL + }; + + _opcode_table = opcode_table; + _numOpcodes = ARRAYSIZE(opcode_table); + + switch (getGameType()) { + case GType_ELVIRA: + case GType_ELVIRA2: + // Confirmed + opcode_table[48] = &SimonEngine::o_destroy; + opcode_table[51] = &SimonEngine::o_place; + opcode_table[91] = &SimonEngine::o_message; + + opcode_table[70] = &SimonEngine::o1_printLongText; + opcode_table[83] = &SimonEngine::o1_rescan; + opcode_table[98] = &SimonEngine::o1_animate; + opcode_table[99] = &SimonEngine::o1_stopAnimate; + opcode_table[85] = &SimonEngine::oww_whereTo; + opcode_table[105] = &SimonEngine::oww_menu; + opcode_table[106] = &SimonEngine::oww_textMenu; + opcode_table[127] = &SimonEngine::o1_playTune; + opcode_table[148] = &SimonEngine::oww_ifDoorOpen; + opcode_table[179] = &SimonEngine::o_isAdjNoun; + opcode_table[180] = &SimonEngine::o_b2Set; + opcode_table[181] = &SimonEngine::o_b2Clear; + opcode_table[182] = &SimonEngine::o_b2Zero; + opcode_table[183] = &SimonEngine::o_b2NotZero; + + // Code difference, check if triggered + opcode_table[161] = NULL; + opcode_table[162] = NULL; + opcode_table[163] = NULL; + opcode_table[164] = NULL; + opcode_table[165] = NULL; + opcode_table[166] = NULL; + opcode_table[167] = NULL; + opcode_table[168] = NULL; + opcode_table[169] = NULL; + opcode_table[170] = NULL; + opcode_table[171] = NULL; + opcode_table[172] = NULL; + opcode_table[173] = NULL; + opcode_table[174] = NULL; + opcode_table[175] = NULL; + opcode_table[176] = NULL; + opcode_table[177] = NULL; + opcode_table[178] = NULL; + opcode_table[184] = NULL; + opcode_table[185] = NULL; + opcode_table[186] = NULL; + opcode_table[187] = NULL; + opcode_table[188] = NULL; + opcode_table[189] = NULL; + opcode_table[190] = NULL; + break; + case GType_WW: + // Confirmed + opcode_table[70] = &SimonEngine::o1_printLongText; + opcode_table[83] = &SimonEngine::o1_rescan; + opcode_table[98] = &SimonEngine::o1_animate; + opcode_table[99] = &SimonEngine::o1_stopAnimate; + opcode_table[85] = &SimonEngine::oww_whereTo; + opcode_table[105] = &SimonEngine::oww_menu; + opcode_table[106] = &SimonEngine::oww_textMenu; + opcode_table[127] = &SimonEngine::o1_playTune; + opcode_table[148] = &SimonEngine::oww_ifDoorOpen; + opcode_table[179] = &SimonEngine::o_isAdjNoun; + opcode_table[180] = &SimonEngine::o_b2Set; + opcode_table[181] = &SimonEngine::o_b2Clear; + opcode_table[182] = &SimonEngine::o_b2Zero; + opcode_table[183] = &SimonEngine::o_b2NotZero; + + // Code difference, check if triggered + opcode_table[161] = NULL; + opcode_table[162] = NULL; + opcode_table[163] = NULL; + opcode_table[164] = NULL; + opcode_table[165] = NULL; + opcode_table[166] = NULL; + opcode_table[167] = NULL; + opcode_table[168] = NULL; + opcode_table[169] = NULL; + opcode_table[170] = NULL; + opcode_table[171] = NULL; + opcode_table[172] = NULL; + opcode_table[173] = NULL; + opcode_table[174] = NULL; + opcode_table[175] = NULL; + opcode_table[176] = NULL; + opcode_table[177] = NULL; + opcode_table[178] = NULL; + opcode_table[184] = NULL; + opcode_table[185] = NULL; + opcode_table[186] = NULL; + opcode_table[187] = NULL; + opcode_table[188] = NULL; + opcode_table[189] = NULL; + opcode_table[190] = NULL; + break; + case GType_SIMON1: + opcode_table[70] = &SimonEngine::o1_printLongText; + opcode_table[83] = &SimonEngine::o1_rescan; + opcode_table[98] = &SimonEngine::o1_animate; + opcode_table[99] = &SimonEngine::o1_stopAnimate; + opcode_table[127] = &SimonEngine::o1_playTune; + opcode_table[177] = &SimonEngine::o1_screenTextPObj; + opcode_table[181] = &SimonEngine::o1_mouseOff; + opcode_table[182] = &SimonEngine::o1_loadBeard; + opcode_table[183] = &SimonEngine::o1_unloadBeard; + opcode_table[185] = &SimonEngine::o1_loadStrings; + opcode_table[187] = &SimonEngine::o1_specialFade; + break; + case GType_SIMON2: + opcode_table[70] = &SimonEngine::o2_printLongText; + opcode_table[83] = &SimonEngine::o2_rescan; + opcode_table[98] = &SimonEngine::o2_animate; + opcode_table[99] = &SimonEngine::o2_stopAnimate; + opcode_table[127] = &SimonEngine::o2_playTune; + opcode_table[177] = &SimonEngine::o2_screenTextPObj; + opcode_table[181] = &SimonEngine::o2_mouseOff; + opcode_table[188] = &SimonEngine::o2_isShortText; + opcode_table[189] = &SimonEngine::o2_clearMarks; + opcode_table[190] = &SimonEngine::o2_waitMark; + break; + case GType_FF: + opcode_table[23] = &SimonEngine::o3_chance; + opcode_table[37] = &SimonEngine::o3_jumpOut; + opcode_table[65] = &SimonEngine::o3_addTextBox; + opcode_table[70] = &SimonEngine::o3_printLongText; + opcode_table[83] = &SimonEngine::o2_rescan; + opcode_table[98] = &SimonEngine::o2_animate; + opcode_table[99] = &SimonEngine::o2_stopAnimate; + opcode_table[107] = &SimonEngine::o3_addBox; + opcode_table[122] = &SimonEngine::o3_oracleTextDown; + opcode_table[123] = &SimonEngine::o3_oracleTextUp; + opcode_table[124] = &SimonEngine::o3_ifTime; + opcode_table[127] = &SimonEngine::o3_playTune; + opcode_table[131] = &SimonEngine::o3_setTime; + opcode_table[132] = &SimonEngine::o3_saveUserGame, + opcode_table[133] = &SimonEngine::o3_loadUserGame; + opcode_table[134] = &SimonEngine::o3_listSaveGames; + opcode_table[135] = &SimonEngine::o3_checkCD; + opcode_table[161] = &SimonEngine::o3_screenTextBox; + opcode_table[165] = &SimonEngine::o3_isAdjNoun; + opcode_table[171] = &SimonEngine::o3_hyperLinkOn; + opcode_table[172] = &SimonEngine::o3_hyperLinkOff; + opcode_table[173] = &SimonEngine::o3_checkPaths; + opcode_table[177] = &SimonEngine::o3_screenTextPObj; + opcode_table[181] = &SimonEngine::o3_mouseOff; + opcode_table[182] = &SimonEngine::o3_loadVideo; + opcode_table[183] = &SimonEngine::o3_playVideo; + opcode_table[187] = &SimonEngine::o3_centreScroll; + opcode_table[188] = &SimonEngine::o2_isShortText; + opcode_table[189] = &SimonEngine::o2_clearMarks; + opcode_table[190] = &SimonEngine::o2_waitMark; + opcode_table[191] = &SimonEngine::o3_resetPVCount; + opcode_table[192] = &SimonEngine::o3_setPathValues; + opcode_table[193] = &SimonEngine::o3_stopClock; + opcode_table[194] = &SimonEngine::o3_restartClock; + opcode_table[195] = &SimonEngine::o3_setColour; + opcode_table[196] = &SimonEngine::o3_b3Set; + opcode_table[197] = &SimonEngine::o3_b3Clear; + opcode_table[198] = &SimonEngine::o3_b3Zero; + opcode_table[199] = &SimonEngine::o3_b3NotZero; + break; + case GType_PP: + // Confirmed + opcode_table[30] = &SimonEngine::o4_opcode30; + opcode_table[37] = &SimonEngine::o4_checkTiles; + opcode_table[38] = &SimonEngine::o4_opcode38; + opcode_table[105] = &SimonEngine::o4_loadHiScores; + opcode_table[106] = &SimonEngine::o4_checkHiScores; + opcode_table[133] = &SimonEngine::o4_loadUserGame; + opcode_table[166] = NULL; + opcode_table[167] = NULL; + opcode_table[168] = NULL; + opcode_table[169] = NULL; + opcode_table[173] = &SimonEngine::o4_saveOopsPosition; + opcode_table[191] = &SimonEngine::o4_resetPVCount; + opcode_table[192] = &SimonEngine::o4_setPathValues; + + // Code difference, check if triggered + opcode_table[132] = &SimonEngine::o3_saveUserGame, + opcode_table[187] = &SimonEngine::o4_resetGameTime; + + // Code difference. Some kind of logging? + opcode_table[190] = &SimonEngine::o2_waitMark; + + // To check + opcode_table[23] = &SimonEngine::o3_chance; + opcode_table[65] = &SimonEngine::o3_addTextBox; + opcode_table[70] = &SimonEngine::o3_printLongText; + opcode_table[83] = &SimonEngine::o2_rescan; + opcode_table[98] = &SimonEngine::o2_animate; + opcode_table[99] = &SimonEngine::o2_stopAnimate; + opcode_table[107] = &SimonEngine::o3_addBox; + opcode_table[122] = &SimonEngine::o3_oracleTextDown; + opcode_table[123] = &SimonEngine::o3_oracleTextUp; + opcode_table[124] = &SimonEngine::o3_ifTime; + opcode_table[127] = &SimonEngine::o3_playTune; + opcode_table[131] = &SimonEngine::o3_setTime; + opcode_table[134] = &SimonEngine::o3_listSaveGames; + opcode_table[161] = &SimonEngine::o3_screenTextBox; + opcode_table[165] = &SimonEngine::o3_isAdjNoun; + opcode_table[171] = &SimonEngine::o3_hyperLinkOn; + opcode_table[172] = &SimonEngine::o3_hyperLinkOff; + opcode_table[177] = &SimonEngine::o3_screenTextPObj; + opcode_table[181] = &SimonEngine::o3_mouseOff; + opcode_table[188] = &SimonEngine::o2_isShortText; + opcode_table[189] = &SimonEngine::o2_clearMarks; + opcode_table[193] = &SimonEngine::o3_stopClock; + opcode_table[194] = &SimonEngine::o3_restartClock; + opcode_table[195] = &SimonEngine::o3_setColour; + break; + default: + error("setupOpcodes: Unknown game"); + } +} + +void SimonEngine::setScriptCondition(bool cond) { + _runScriptCondition[_recursionDepth] = cond; +} + +bool SimonEngine::getScriptCondition() { + return _runScriptCondition[_recursionDepth]; +} + +void SimonEngine::setScriptReturn(int ret) { + _runScriptReturn[_recursionDepth] = ret; +} + +int SimonEngine::getScriptReturn() { + return _runScriptReturn[_recursionDepth]; +} + +// ----------------------------------------------------------------------- +// Common Opcodes +// ----------------------------------------------------------------------- + +void SimonEngine::o_at() { + // 1: ptrA parent is + setScriptCondition(me()->parent == getNextItemID()); +} + +void SimonEngine::o_notAt() { + // 2: ptrA parent is not + setScriptCondition(me()->parent != getNextItemID()); +} + +void SimonEngine::o_carried() { + // 5: parent is 1 + setScriptCondition(getNextItemPtr()->parent == getItem1ID()); +} + +void SimonEngine::o_notCarried() { + // 6: parent isnot 1 + setScriptCondition(getNextItemPtr()->parent != getItem1ID()); +} + +void SimonEngine::o_isAt() { + // 7: parent is + Item *item = getNextItemPtr(); + setScriptCondition(item->parent == getNextItemID()); +} + +void SimonEngine::o_zero() { + // 11: is zero + setScriptCondition(getNextVarContents() == 0); +} + +void SimonEngine::o_notZero() { + // 12: isnot zero + setScriptCondition(getNextVarContents() != 0); +} + +void SimonEngine::o_eq() { + // 13: equal + uint tmp = getNextVarContents(); + setScriptCondition(tmp == getVarOrWord()); +} + +void SimonEngine::o_notEq() { + // 14: not equal + uint tmp = getNextVarContents(); + setScriptCondition(tmp != getVarOrWord()); +} + +void SimonEngine::o_gt() { + // 15: is greater + uint tmp = getNextVarContents(); + setScriptCondition(tmp > getVarOrWord()); +} + +void SimonEngine::o_lt() { + // 16: is less + uint tmp = getNextVarContents(); + setScriptCondition(tmp < getVarOrWord()); +} + +void SimonEngine::o_eqf() { + // 17: is eq f + uint tmp = getNextVarContents(); + setScriptCondition(tmp == getNextVarContents()); +} + +void SimonEngine::o_notEqf() { + // 18: is not equal f + uint tmp = getNextVarContents(); + setScriptCondition(tmp != getNextVarContents()); +} + +void SimonEngine::o_ltf() { + // 19: is greater f + uint tmp = getNextVarContents(); + setScriptCondition(tmp < getNextVarContents()); +} + +void SimonEngine::o_gtf() { + // 20: is less f + uint tmp = getNextVarContents(); + setScriptCondition(tmp > getNextVarContents()); +} + +void SimonEngine::o_chance() { + // 23 + uint a = getVarOrWord(); + + if (a == 0) { + setScriptCondition(false); + return; + } + + if (a == 100) { + setScriptCondition(true); + return; + } + + a += _chanceModifier; + + if (a <= 0) { + _chanceModifier = 0; + setScriptCondition(false); + } else if ((uint)_rnd.getRandomNumber(99) < a) { + if (_chanceModifier <= 0) + _chanceModifier -= 5; + else + _chanceModifier = 0; + setScriptCondition(true); + } else { + if (_chanceModifier >= 0) + _chanceModifier += 5; + else + _chanceModifier = 0; + setScriptCondition(false); + } +} + +void SimonEngine::o_isRoom() { + // 25: is room + setScriptCondition(isRoom(getNextItemPtr())); +} + +void SimonEngine::o_isObject() { + // 26: is object + setScriptCondition(isObject(getNextItemPtr())); +} + +void SimonEngine::o_state() { + // 27: item state is + Item *item = getNextItemPtr(); + setScriptCondition((uint) item->state == getVarOrWord()); +} + +void SimonEngine::o_oflag() { + // 28: item has prop + SubObject *subObject = (SubObject *)findChildOfType(getNextItemPtr(), 2); + byte num = getVarOrByte(); + setScriptCondition(subObject != NULL && (subObject->objectFlags & (1 << num)) != 0); +} + +void SimonEngine::o_destroy() { + // 31: set no parent + setItemParent(getNextItemPtr(), NULL); +} + +void SimonEngine::o_place() { + // 33: set item parent + Item *item = getNextItemPtr(); + setItemParent(item, getNextItemPtr()); +} + +void SimonEngine::o_copyff() { + // 36: copy var + uint value = getNextVarContents(); + writeNextVarContents(value); +} + +void SimonEngine::o_clear() { + // 41: zero var + writeNextVarContents(0); +} + +void SimonEngine::o_let() { + // 42: set var + uint var = getVarWrapper(); + writeVariable(var, getVarOrWord()); +} + +void SimonEngine::o_add() { + // 43: add + uint var = getVarWrapper(); + writeVariable(var, readVariable(var) + getVarOrWord()); +} + +void SimonEngine::o_sub() { + // 44: sub + uint var = getVarWrapper(); + writeVariable(var, readVariable(var) - getVarOrWord()); +} + +void SimonEngine::o_addf() { + // 45: add f + uint var = getVarWrapper(); + writeVariable(var, readVariable(var) + getNextVarContents()); +} + +void SimonEngine::o_subf() { + // 46: sub f + uint var = getVarWrapper(); + writeVariable(var, readVariable(var) - getNextVarContents()); +} + +void SimonEngine::o_mul() { + // 47: mul + uint var = getVarWrapper(); + writeVariable(var, readVariable(var) * getVarOrWord()); +} + +void SimonEngine::o_div() { + // 48: div + uint var = getVarWrapper(); + int value = getVarOrWord(); + if (value == 0) + error("o_div: Division by zero"); + writeVariable(var, readVariable(var) / value); +} + +void SimonEngine::o_mulf() { + // 49: mul f + uint var = getVarWrapper(); + writeVariable(var, readVariable(var) * getNextVarContents()); +} + +void SimonEngine::o_divf() { + // 50: div f + uint var = getVarWrapper(); + int value = getNextVarContents(); + if (value == 0) + error("o_divf: Division by zero"); + writeVariable(var, readVariable(var) / value); +} + +void SimonEngine::o_mod() { + // 51: mod + uint var = getVarWrapper(); + int value = getVarOrWord(); + if (value == 0) + error("o_mod: Division by zero"); + writeVariable(var, readVariable(var) % value); +} + +void SimonEngine::o_modf() { + // 52: mod f + uint var = getVarWrapper(); + int value = getNextVarContents(); + if (value == 0) + error("o_modf: Division by zero"); + writeVariable(var, readVariable(var) % value); +} + +void SimonEngine::o_random() { + // 53: random + uint var = getVarWrapper(); + uint value = (uint16)getVarOrWord(); + writeVariable(var, _rnd.getRandomNumber(value - 1)); +} + +void SimonEngine::o_goto() { + // 55: set itemA parent + uint item = getNextItemID(); + if (_itemArrayPtr[item] == NULL) { + setItemParent(me(), NULL); + loadRoomItems(item); + } + setItemParent(me(), _itemArrayPtr[item]); +} + +void SimonEngine::o_oset() { + // 56: set child2 fr bit + SubObject *subObject = (SubObject *)findChildOfType(getNextItemPtr(), 2); + int value = getVarOrByte(); + if (subObject != NULL && value >= 0x10) + subObject->objectFlags |= (1 << value); +} + +void SimonEngine::o_oclear() { + // 57: clear child2 fr bit + SubObject *subObject = (SubObject *)findChildOfType(getNextItemPtr(), 2); + int value = getVarOrByte(); + if (subObject != NULL && value >= 0x10) + subObject->objectFlags &= ~(1 << value); +} + +void SimonEngine::o_putBy() { + // 58: make siblings + Item *item = getNextItemPtr(); + setItemParent(item, derefItem(getNextItemPtr()->parent)); +} + +void SimonEngine::o_inc() { + // 59: item inc state + Item *item = getNextItemPtr(); + if (item->state <= 30000) + setItemState(item, item->state + 1); +} + +void SimonEngine::o_dec() { + // 60: item dec state + Item *item = getNextItemPtr(); + if (item->state >= 0) + setItemState(item, item->state - 1); +} + +void SimonEngine::o_setState() { + // 61: item set state + Item *item = getNextItemPtr(); + int value = getVarOrWord(); + if (value < 0) + value = 0; + if (value > 30000) + value = 30000; + setItemState(item, value); +} + +void SimonEngine::o_print() { + // 62: show int + showMessageFormat("%d", getNextVarContents()); +} + +void SimonEngine::o_message() { + // 63: show string nl + showMessageFormat("%s\n", getStringPtrByID(getNextStringID())); +} + +void SimonEngine::o_msg() { + // 64: show string + showMessageFormat("%s", getStringPtrByID(getNextStringID())); +} + +void SimonEngine::o_addTextBox() { + // 65: add hit area + uint id = getVarOrWord(); + uint x = getVarOrWord(); + uint y = getVarOrWord(); + uint w = getVarOrWord(); + uint h = getVarOrWord(); + uint number = getVarOrByte(); + if (number < _numTextBoxes) + defineBox(id, x, y, w, h, (number << 8) + 129, 208, _dummyItem2); +} + +void SimonEngine::o_setShortText() { + // 66: set item name + uint var = getVarOrByte(); + uint stringId = getNextStringID(); + if (var < _numTextBoxes) { + _shortText[var] = stringId; + if (getGameType() == GType_PP) { + _shortTextX[var] = getVarOrWord(); + _shortTextY[var] = getVarOrWord(); + } + } +} + +void SimonEngine::o_setLongText() { + // 67: set item description + uint var = getVarOrByte(); + uint stringId = getNextStringID(); + if (getFeatures() & GF_TALKIE) { + uint speechId = getNextWord(); + if (var < _numTextBoxes) { + _longText[var] = stringId; + _longSound[var] = speechId; + } + } else { + if (var < _numTextBoxes) { + _longText[var] = stringId; + } + } +} + +void SimonEngine::o_end() { + // 68: exit interpreter + shutdown(); +} + +void SimonEngine::o_done() { + // 69: return 1 + setScriptReturn(1); +} + +void SimonEngine::o_process() { + // 71: start subroutine + Subroutine *sub = getSubroutineByID(getVarOrWord()); + if (sub != NULL) + startSubroutine(sub); +} + +void SimonEngine::o_when() { + // 76: add timeout + uint timeout = getVarOrWord(); + addTimeEvent(timeout, getVarOrWord()); +} + +void SimonEngine::o_if1() { + // 77: has item minus 1 + setScriptCondition(_subjectItem != NULL); +} + +void SimonEngine::o_if2() { + // 78: has item minus 3 + setScriptCondition(_objectItem != NULL); +} + +void SimonEngine::o_isCalled() { + // 79: childstruct fr2 is + SubObject *subObject = (SubObject *)findChildOfType(getNextItemPtr(), 2); + uint stringId = getNextStringID(); + setScriptCondition((subObject != NULL) && subObject->objectName == stringId); +} + +void SimonEngine::o_is() { + // 80: item equal + setScriptCondition(getNextItemPtr() == getNextItemPtr()); +} + +void SimonEngine::o_debug() { + // 82: debug opcode + getVarOrByte(); +} + +void SimonEngine::o_comment() { + // 87: comment + getNextStringID(); +} + +void SimonEngine::o_haltAnimation() { + // 88: stop animation + _lockWord |= 0x10; +} + +void SimonEngine::o_restartAnimation() { + // 89: restart animation + _lockWord &= ~0x10; +} + +void SimonEngine::o_getParent() { + // 90: set minusitem to parent + Item *item = derefItem(getNextItemPtr()->parent); + switch (getVarOrByte()) { + case 0: + _objectItem = item; + break; + case 1: + _subjectItem = item; + break; + default: + error("o_getParent: invalid subcode"); + } +} + +void SimonEngine::o_getNext() { + // 91: set minusitem to sibling + Item *item = derefItem(getNextItemPtr()->sibling); + switch (getVarOrByte()) { + case 0: + _objectItem = item; + break; + case 1: + _subjectItem = item; + break; + default: + error("o_getNext: invalid subcode"); + } +} + +void SimonEngine::o_getChildren() { + // 92: set minusitem to child + Item *item = derefItem(getNextItemPtr()->child); + switch (getVarOrByte()) { + case 0: + _objectItem = item; + break; + case 1: + _subjectItem = item; + break; + default: + error("o_getChildren: invalid subcode"); + } +} + +void SimonEngine::o_picture() { + // 96 + uint vga_res = getVarOrWord(); + uint mode = getVarOrByte(); + + if (mode == 4) + vc29_stopAllSounds(); + + if (_lockWord & 0x10) + error("o_picture: _lockWord & 0x10"); + + set_video_mode_internal(mode, vga_res); +} + +void SimonEngine::o_loadZone() { + // 97: load vga + uint vga_res = getVarOrWord(); + + _lockWord |= 0x80; + loadZone(vga_res); + _lockWord &= ~0x80; +} + +void SimonEngine::o_killAnimate() { + // 100: vga reset + _lockWord |= 0x8000; + vc27_resetSprite(); + _lockWord &= ~0x8000; +} + +void SimonEngine::o_defWindow() { + // 101 + uint num = getVarOrByte(); + uint x = getVarOrWord(); + uint y = getVarOrWord(); + uint w = getVarOrWord(); + uint h = getVarOrWord(); + uint flags = getVarOrWord(); + uint fill_color = getVarOrWord(); + uint text_color = 0; + + num &= 7; + + if (_windowArray[num]) + closeWindow(num); + + _windowArray[num] = openWindow(x, y, w, h, flags, fill_color, text_color); + + if (num == _curWindow) { + _textWindow = _windowArray[num]; + if (getGameType() == GType_FF) + showmessage_helper_3(_textWindow->textColumn, _textWindow->width); + else + showmessage_helper_3(_textWindow->textLength, _textWindow->textMaxLength); + } +} + +void SimonEngine::o_window() { + // 102 + changeWindow(getVarOrByte() & 7); +} + +void SimonEngine::o_cls() { + // 103 + mouseOff(); + removeIconArray(_curWindow); + showMessageFormat("\x0C"); + _oracleMaxScrollY = 0; + _noOracleScroll = 0; + mouseOn(); +} + +void SimonEngine::o_closeWindow() { + // 104 + closeWindow(getVarOrByte() & 7); +} + +void SimonEngine::o_addBox() { + // 107: add item hitarea + uint flags = 0; + uint id = getVarOrWord(); + uint params = id / 1000; + uint x, y, w, h, verb; + Item *item; + + id = id % 1000; + + if (params & 1) + flags |= kBFInvertTouch; + if (params & 2) + flags |= kBFNoTouchName; + if (params & 4) + flags |= kBFBoxItem; + if (params & 8) + flags |= kBFTextBox; + if (params & 16) + flags |= 0x10; + + x = getVarOrWord(); + y = getVarOrWord(); + w = getVarOrWord(); + h = getVarOrWord(); + item = getNextItemPtrStrange(); + verb = getVarOrWord(); + if (x >= 1000) { + verb += 0x4000; + x -= 1000; + } + defineBox(id, x, y, w, h, flags, verb, item); +} + +void SimonEngine::o_delBox() { + // 108: delete hitarea + undefineBox(getVarOrWord()); +} + +void SimonEngine::o_enableBox() { + // 109: clear hitarea bit 0x40 + enableBox(getVarOrWord()); +} + +void SimonEngine::o_disableBox() { + // 110: set hitarea bit 0x40 + disableBox(getVarOrWord()); +} + +void SimonEngine::o_moveBox() { + // 111: set hitarea xy + uint hitarea_id = getVarOrWord(); + uint x = getVarOrWord(); + uint y = getVarOrWord(); + moveBox(hitarea_id, x, y); +} + +void SimonEngine::o_doIcons() { + // 114 + Item *item = getNextItemPtr(); + uint num = getVarOrByte(); + mouseOff(); + drawIconArray(num, item, 0, 0); + mouseOn(); +} + +void SimonEngine::o_isClass() { + // 115: item has flag + Item *item = getNextItemPtr(); + setScriptCondition((item->classFlags & (1 << getVarOrByte())) != 0); +} + +void SimonEngine::o_setClass() { + // 116: item set flag + Item *item = getNextItemPtr(); + item->classFlags |= (1 << getVarOrByte()); +} + +void SimonEngine::o_unsetClass() { + // 117: item clear flag + Item *item = getNextItemPtr(); + item->classFlags &= ~(1 << getVarOrByte()); +} + +void SimonEngine::o_waitSync() { + // 119: wait vga + uint var = getVarOrWord(); + _scriptVar2 = (var == 200); + + if (var != 200 || !_skipVgaWait) + waitForSync(var); + _skipVgaWait = false; +} + +void SimonEngine::o_sync() { + // 120: sync + sendSync(getVarOrWord()); +} + +void SimonEngine::o_defObj() { + // 121: set vga item + uint slot = getVarOrByte(); + _objectArray[slot] = getNextItemPtr(); +} + +void SimonEngine::o_here() { + // 125: item is sibling with item 1 + Item *item = getNextItemPtr(); + setScriptCondition(me()->parent == item->parent); +} + +void SimonEngine::o_doClassIcons() { + // 126 + Item *item = getNextItemPtr(); + uint num = getVarOrByte(); + uint a = 1 << getVarOrByte(); + mouseOff(); + drawIconArray(num, item, 1, a); + mouseOn(); +} + +void SimonEngine::o_waitEndTune() { + // 128: dummy instruction + getVarOrWord(); +} + +void SimonEngine::o_ifEndTune() { + // 129: dummy instruction + getVarOrWord(); + setScriptCondition(true); +} + +void SimonEngine::o_setAdjNoun() { + // 130: set adj noun + uint var = getVarOrByte(); + if (var == 1) { + _scriptAdj1 = getNextWord(); + _scriptNoun1 = getNextWord(); + } else { + _scriptAdj2 = getNextWord(); + _scriptNoun2 = getNextWord(); + } +} + +void SimonEngine::o_saveUserGame() { + // 132: save game + _system->setFeatureState(OSystem::kFeatureVirtualKeyboard, true); + userGame(false); + _system->setFeatureState(OSystem::kFeatureVirtualKeyboard, false); +} + +void SimonEngine::o_loadUserGame() { + // 133: load game + _system->setFeatureState(OSystem::kFeatureVirtualKeyboard, true); + userGame(true); + _system->setFeatureState(OSystem::kFeatureVirtualKeyboard, false); +} + +void SimonEngine::o_stopTune() { + // 134: dummy opcode? + midi.stop(); + _lastMusicPlayed = -1; +} + +void SimonEngine::o_pauseGame() { + // 135: quit if user presses y + _system->setFeatureState(OSystem::kFeatureVirtualKeyboard, true); + + // If all else fails, use English as fallback. + byte keyYes = 'y'; + byte keyNo = 'n'; + + switch (_language) { + case Common::RU_RUS: + break; + case Common::PL_POL: + keyYes = 't'; + break; + case Common::HB_ISR: + keyYes = 'f'; + break; + case Common::ES_ESP: + keyYes = 's'; + break; + case Common::IT_ITA: + keyYes = 's'; + break; + case Common::FR_FRA: + keyYes = 'o'; + break; + case Common::DE_DEU: + keyYes = 'j'; + break; + default: + break; + } + + for (;;) { + delay(1); +#ifdef _WIN32_WCE + if (isSmartphone()) { + if (_keyPressed) { + if (_keyPressed == 13) + shutdown(); + else + break; + } + } +#endif + if (_keyPressed == keyYes || _keyPressed == (keyYes - 32)) + shutdown(); + else if (_keyPressed == keyNo || _keyPressed == (keyNo - 32)) + break; + } + + _system->setFeatureState(OSystem::kFeatureVirtualKeyboard, false); +} + +void SimonEngine::o_copysf() { + // 136: set var to item unk3 + Item *item = getNextItemPtr(); + writeNextVarContents(item->state); +} + +void SimonEngine::o_restoreIcons() { + // 137 + uint num = getVarOrByte(); + WindowBlock *window = _windowArray[num & 7]; + if (window->iconPtr) + drawIconArray(num, window->iconPtr->itemRef, window->iconPtr->line, window->iconPtr->classMask); +} + +void SimonEngine::o_freezeZones() { + // 138: vga pointer op 4 + freezeBottom(); +} + +void SimonEngine::o_placeNoIcons() { + // 139: set parent special + Item *item = getNextItemPtr(); + _noParentNotify = true; + setItemParent(item, getNextItemPtr()); + _noParentNotify = false; +} + +void SimonEngine::o_clearTimers() { + // 140: del te and add one + killAllTimers(); + addTimeEvent(3, 0xA0); +} + +void SimonEngine::o_setDollar() { + // 141: set m1 to m3 + uint which = getVarOrByte(); + Item *item = getNextItemPtr(); + if (which == 1) { + _subjectItem = item; + } else { + _objectItem = item; + } +} + +void SimonEngine::o_isBox() { + // 142: is hitarea 0x40 clear + setScriptCondition(isBoxDead(getVarOrWord())); +} + +void SimonEngine::o_doTable() { + // 143: start item sub + SubRoom *subRoom = (SubRoom *)findChildOfType(getNextItemPtr(), 1); + if (subRoom != NULL) { + Subroutine *sub = getSubroutineByID(subRoom->subroutine_id); + if (sub) + startSubroutine(sub); + } +} + +void SimonEngine::o_storeItem() { + // 151: set array6 to item + uint var = getVarOrByte(); + Item *item = getNextItemPtr(); + _itemStore[var] = item; +} + +void SimonEngine::o_getItem() { + // 152: set m1 to m3 to array 6 + Item *item = _itemStore[getVarOrByte()]; + uint var = getVarOrByte(); + if (var == 1) { + _subjectItem = item; + } else { + _objectItem = item; + } +} + +void SimonEngine::o_bSet() { + // 153: set bit + setBitFlag(getVarWrapper(), true); +} + +void SimonEngine::o_bClear() { + // 154: clear bit + setBitFlag(getVarWrapper(), false); +} + +void SimonEngine::o_bZero() { + // 155: is bit clear + setScriptCondition(!getBitFlag(getVarWrapper())); +} + +void SimonEngine::o_bNotZero() { + // 156: is bit set + uint bit = getVarWrapper(); + + // WORKAROUND: Fix for glitch in some versions + if (getGameType() == GType_SIMON1 && _subroutine == 2962 && bit == 63) { + bit = 50; + } + + setScriptCondition(getBitFlag(bit)); +} + +void SimonEngine::o_getOValue() { + // 157: get item int prop + Item *item = getNextItemPtr(); + SubObject *subObject = (SubObject *)findChildOfType(item, 2); + uint prop = getVarOrByte(); + + if (subObject != NULL && subObject->objectFlags & (1 << prop) && prop < 16) { + uint offs = getOffsetOfChild2Param(subObject, 1 << prop); + writeNextVarContents(subObject->objectFlagValue[offs]); + } else { + writeNextVarContents(0); + } +} + +void SimonEngine::o_setOValue() { + // 158: set item prop + Item *item = getNextItemPtr(); + SubObject *subObject = (SubObject *)findChildOfType(item, 2); + uint prop = getVarOrByte(); + int value = getVarOrWord(); + + if (subObject != NULL && subObject->objectFlags & (1 << prop) && prop < 16) { + uint offs = getOffsetOfChild2Param(subObject, 1 << prop); + subObject->objectFlagValue[offs] = value; + } +} + +void SimonEngine::o_ink() { + // 160 + setTextColor(getVarOrByte()); +} + +void SimonEngine::o_screenTextBox() { + // 161: setup text + TextLocation *tl = getTextLocation(getVarOrByte()); + + tl->x = getVarOrWord(); + tl->y = getVarOrByte(); + tl->width = getVarOrWord(); +} + +void SimonEngine::o_screenTextMsg() { + // 162: print string + uint vgaSpriteId = getVarOrByte(); + uint color = getVarOrByte(); + uint stringId = getNextStringID(); + const byte *string_ptr = NULL; + uint speechId = 0; + TextLocation *tl; + + if (stringId != 0xFFFF) + string_ptr = getStringPtrByID(stringId); + + if (getFeatures() & GF_TALKIE) { + if (getGameType() == GType_FF || getGameType() == GType_PP) + speechId = (uint16)getVarOrWord(); + else + speechId = (uint16)getNextWord(); + } + + if (getGameType() == GType_FF || getGameType() == GType_PP) + vgaSpriteId = 1; + + tl = getTextLocation(vgaSpriteId); + if (_speech && speechId != 0) + playSpeech(speechId, vgaSpriteId); + if (((getGameType() == GType_SIMON2 && (getFeatures() & GF_TALKIE)) || getGameType() == GType_FF) && + speechId == 0) { + stopAnimateSimon2(2, vgaSpriteId + 2); + } + + if (string_ptr != NULL && (speechId == 0 || _subtitles)) + printScreenText(vgaSpriteId, color, (const char *)string_ptr, tl->x, tl->y, tl->width); + +} + +void SimonEngine::o_playEffect() { + // 163: play sound + uint soundId = getVarOrWord(); + + if (getGameType() == GType_FF) + error("o_playEffect: triggered"); + + if (getGameId() == GID_SIMON1DOS) + playSting(soundId); + else + _sound->playEffects(soundId); +} + +void SimonEngine::o_getDollar2() { + // 164 + _showPreposition = true; + + setup_cond_c_helper(); + + _objectItem = _hitAreaObjectItem; + + if (_objectItem == _dummyItem2) + _objectItem = me(); + + if (_objectItem == _dummyItem3) + _objectItem = derefItem(me()->parent); + + if (_objectItem != NULL) { + _scriptNoun2 = _objectItem->noun; + _scriptAdj2 = _objectItem->adjective; + } else { + _scriptNoun2 = -1; + _scriptAdj2 = -1; + } + + _showPreposition = false; +} + +void SimonEngine::o_isAdjNoun() { + // 165: item unk1 unk2 is + Item *item = getNextItemPtr(); + int16 a = getNextWord(), b = getNextWord(); + setScriptCondition(item->adjective == a && item->noun == b); +} + +void SimonEngine::o_b2Set() { + // 166: set bit2 + uint bit = getVarOrByte(); + _bitArrayTwo[bit / 16] |= (1 << (bit & 15)); +} + +void SimonEngine::o_b2Clear() { + // 167: clear bit2 + uint bit = getVarOrByte(); + _bitArrayTwo[bit / 16] &= ~(1 << (bit & 15)); +} + +void SimonEngine::o_b2Zero() { + // 168: is bit2 clear + uint bit = getVarOrByte(); + setScriptCondition((_bitArrayTwo[bit / 16] & (1 << (bit & 15))) == 0); +} + +void SimonEngine::o_b2NotZero() { + // 169: is bit2 set + uint bit = getVarOrByte(); + setScriptCondition((_bitArrayTwo[bit / 16] & (1 << (bit & 15))) != 0); +} + +void SimonEngine::o_lockZones() { + // 175: vga pointer op 1 + _vgaMemBase = _vgaMemPtr; +} + +void SimonEngine::o_unlockZones() { + // 176: vga pointer op 2 + _vgaMemPtr = _vgaFrozenBase; + _vgaMemBase = _vgaFrozenBase; +} + +void SimonEngine::o_getPathPosn() { + // 178: path find + uint x = getVarOrWord(); + uint y = getVarOrWord(); + uint var_1 = getVarOrByte(); + uint var_2 = getVarOrByte(); + + const uint16 *p; + uint i, j; + uint prev_i; + uint x_diff, y_diff; + uint best_i = 0, best_j = 0, best_dist = 0xFFFFFFFF; + uint maxPath = (getGameType() == GType_FF) ? 100 : 20; + + if (getGameType() == GType_FF) { + x += _scrollX; + y += _scrollY; + } + if (getGameType() == GType_SIMON2) { + x += _scrollX * 8; + } + + int end = (getGameType() == GType_FF) ? 9999 : 999; + prev_i = maxPath + 1 - readVariable(12); + for (i = maxPath; i != 0; --i) { + p = (const uint16 *)_pathFindArray[maxPath - i]; + if (!p) + continue; + for (j = 0; readUint16Wrapper(&p[0]) != end; j++, p += 2) { + x_diff = ABS((int16)(readUint16Wrapper(&p[0]) - x)); + y_diff = ABS((int16)(readUint16Wrapper(&p[1]) - 12 - y)); + + if (x_diff < y_diff) { + x_diff /= 4; + y_diff *= 4; + } + x_diff += y_diff /= 4; + + if (x_diff < best_dist || x_diff == best_dist && prev_i == i) { + best_dist = x_diff; + best_i = maxPath + 1 - i; + best_j = j; + } + } + } + + writeVariable(var_1, best_i); + writeVariable(var_2, best_j); +} + +void SimonEngine::o_scnTxtLongText() { + // 179: conversation responses and room descriptions + uint vgaSpriteId = getVarOrByte(); + uint color = getVarOrByte(); + uint stringId = getVarOrByte(); + uint speechId = 0; + TextLocation *tl; + + const char *string_ptr = (const char *)getStringPtrByID(_longText[stringId]); + if (getFeatures() & GF_TALKIE) + speechId = _longSound[stringId]; + + if (getGameType() == GType_FF) + vgaSpriteId = 1; + tl = getTextLocation(vgaSpriteId); + + if (_speech && speechId != 0) + playSpeech(speechId, vgaSpriteId); + if (string_ptr != NULL && _subtitles) + printScreenText(vgaSpriteId, color, string_ptr, tl->x, tl->y, tl->width); +} + +void SimonEngine::o_mouseOn() { + // 180: force mouseOn + scriptMouseOn(); +} + +void SimonEngine::o_unloadZone() { + // 184: clear vgapointer entry + uint a = getVarOrWord(); + VgaPointersEntry *vpe = &_vgaBufferPointers[a]; + + vpe->sfxFile = NULL; + vpe->vgaFile1 = NULL; + vpe->vgaFile2 = NULL; +} + +void SimonEngine::o_unfreezeZones() { + // 186: vga pointer op 3 + unfreezeBottom(); +} + +// ----------------------------------------------------------------------- +// Waxworks 1 Opcodes +// ----------------------------------------------------------------------- + +void SimonEngine::oww_whereTo() { + // 85: where to + Item *i = getNextItemPtr(); + int16 d = getVarOrByte(); + int16 f = getVarOrByte(); + + if (f == 1) + _subjectItem = _itemArrayPtr[getExitOf(i, d)]; + else + _objectItem = _itemArrayPtr[getExitOf(i, d)]; +} + +void SimonEngine::oww_menu() { + // 105: menu + getVarOrByte(); +} + +void SimonEngine::oww_textMenu() { + // 106: text menu + + /* byte tmp = getVarOrByte(); + TextMenu[tmp] = getVarOrByte(); */ + + getVarOrByte(); + getVarOrByte(); +} + +void SimonEngine::oww_ifDoorOpen() { + // 148: if door open + Item *item = getNextItemPtr(); + uint16 d = getVarOrByte(); + setScriptCondition(getDoorState(item, d) != 0); +} + +// ----------------------------------------------------------------------- +// Simon 1 Opcodes +// ----------------------------------------------------------------------- + +void SimonEngine::o1_printLongText() { + // 70: show string from array + const char *str = (const char *)getStringPtrByID(_longText[getVarOrByte()]); + showMessageFormat("%s\n", str); +} + +void SimonEngine::o1_rescan() { + // 83: restart subroutine + setScriptReturn(-10); +} + +void SimonEngine::o1_animate() { + // 98: start vga + uint vga_res, vgaSpriteId, windowNum, x, y, palette; + vgaSpriteId = getVarOrWord(); + vga_res = vgaSpriteId / 100; + windowNum = getVarOrByte(); + x = getVarOrWord(); + y = getVarOrWord(); + palette = getVarOrWord(); + loadSprite(windowNum, vga_res, vgaSpriteId, x, y, palette); +} + +void SimonEngine::o1_stopAnimate() { + // 99: kill sprite + stopAnimateSimon1(getVarOrWord()); +} + +void SimonEngine::o1_playTune() { + // 127: deals with music + int music = getVarOrWord(); + int track = getVarOrWord(); + + // Jamieson630: + // This appears to be a "load or play music" command. + // The music resource is specified, and optionally + // a track as well. Normally we see two calls being + // made, one to load the resource and another to + // actually start a track (so the resource is + // effectively preloaded so there's no latency when + // starting playback). + + if (music != _lastMusicPlayed) { + _lastMusicPlayed = music; + loadMusic(music); + midi.startTrack(track); + } +} + +void SimonEngine::o1_screenTextPObj() { + // 177: inventory descriptions + uint vgaSpriteId = getVarOrByte(); + uint color = getVarOrByte(); + + SubObject *subObject = (SubObject *)findChildOfType(getNextItemPtr(), 2); + if (getFeatures() & GF_TALKIE) { + if (subObject != NULL && subObject->objectFlags & kOFVoice) { + uint offs = getOffsetOfChild2Param(subObject, kOFVoice); + playSpeech(subObject->objectFlagValue[offs], vgaSpriteId); + } else if (subObject != NULL && subObject->objectFlags & kOFNumber) { + uint offs = getOffsetOfChild2Param(subObject, kOFNumber); + playSpeech(subObject->objectFlagValue[offs] + 3550, vgaSpriteId); + } + } + + if (subObject != NULL && subObject->objectFlags & kOFText && _subtitles) { + const char *stringPtr = (const char *)getStringPtrByID(subObject->objectFlagValue[0]); + TextLocation *tl = getTextLocation(vgaSpriteId); + char buf[256]; + int j, k; + + if (subObject->objectFlags & kOFNumber) { + if (_language == Common::HB_ISR) { + j = subObject->objectFlagValue[getOffsetOfChild2Param(subObject, kOFNumber)]; + k = (j % 10) * 10; + k += j / 10; + if (!(j % 10)) + sprintf(buf,"0%d%s", k, stringPtr); + else + sprintf(buf,"%d%s", k, stringPtr); + } else { + sprintf(buf,"%d%s", subObject->objectFlagValue[getOffsetOfChild2Param(subObject, kOFNumber)], stringPtr); + } + stringPtr = buf; + } + if (stringPtr != NULL) + printScreenText(vgaSpriteId, color, stringPtr, tl->x, tl->y, tl->width); + } +} + +void SimonEngine::o1_mouseOff() { + // 181: force mouseOff + scriptMouseOff(); +} + +void SimonEngine::o1_loadBeard() { + // 182: load beard + if (_beardLoaded == false) { + _beardLoaded = true; + _lockWord |= 0x8000; + loadSimonVGAFile(328); + _lockWord &= ~0x8000; + } +} + +void SimonEngine::o1_unloadBeard() { + // 183: unload beard + if (_beardLoaded == true) { + _beardLoaded = false; + _lockWord |= 0x8000; + loadSimonVGAFile(23); + _lockWord &= ~0x8000; + } +} + +void SimonEngine::o1_loadStrings() { + // 185: load sound files + _soundFileId = getVarOrWord(); + if (getPlatform() == Common::kPlatformAmiga && getFeatures() & GF_TALKIE) { + char buf[10]; + sprintf(buf, "%d%s", _soundFileId, "Effects"); + _sound->readSfxFile(buf); + sprintf(buf, "%d%s", _soundFileId, "simon"); + _sound->readVoiceFile(buf); + } +} + +void SimonEngine::o1_specialFade() { + // 187: fade to black + uint i; + + memcpy(_videoBuf1, _currentPalette, 4 * 256); + + for (i = 32; i != 0; --i) { + paletteFadeOut(_videoBuf1, 32, 8); + paletteFadeOut(_videoBuf1 + 4 * 48, 144, 8); + paletteFadeOut(_videoBuf1 + 4 * 208, 48, 8); + _system->setPalette(_videoBuf1, 0, 256); + delay(5); + } + + memcpy(_currentPalette, _videoBuf1, 1024); + memcpy(_displayPalette, _videoBuf1, 1024); +} + +// ----------------------------------------------------------------------- +// Simon 2 Opcodes +// ----------------------------------------------------------------------- + +void SimonEngine::o2_printLongText() { + // 70: show string from array + const char *str = (const char *)getStringPtrByID(_longText[getVarOrByte()]); + writeVariable(51, strlen(str) / 53 * 8 + 8); + showMessageFormat("%s\n", str); +} + +void SimonEngine::o2_rescan() { + // 83: restart subroutine + if (_exitCutscene) { + if (getBitFlag(9)) { + endCutscene(); + } + } else { + processSpecialKeys(); + } + + setScriptReturn(-10); +} + +void SimonEngine::o2_animate() { + // 98: start vga + uint vga_res = getVarOrWord(); + uint vgaSpriteId = getVarOrWord(); + uint windowNum = getVarOrByte(); + uint x = getVarOrWord(); + uint y = getVarOrWord(); + uint palette = getVarOrWord(); + loadSprite(windowNum, vga_res, vgaSpriteId, x, y, palette); +} + +void SimonEngine::o2_stopAnimate() { + // 99: kill sprite + uint a = getVarOrWord(); + uint b = getVarOrWord(); + stopAnimateSimon2(a, b); +} + +void SimonEngine::o2_playTune() { + // 127: deals with music + int music = getVarOrWord(); + int track = getVarOrWord(); + int loop = getVarOrByte(); + + // Jamieson630: + // This appears to be a "load or play music" command. + // The music resource is specified, and optionally + // a track as well. Normally we see two calls being + // made, one to load the resource and another to + // actually start a track (so the resource is + // effectively preloaded so there's no latency when + // starting playback). + + midi.setLoop(loop != 0); + if (_lastMusicPlayed != music) + _nextMusicToPlay = music; + else + midi.startTrack(track); +} + +void SimonEngine::o2_screenTextPObj() { + // 177: inventory descriptions + uint vgaSpriteId = getVarOrByte(); + uint color = getVarOrByte(); + + SubObject *subObject = (SubObject *)findChildOfType(getNextItemPtr(), 2); + if (getFeatures() & GF_TALKIE) { + if (subObject != NULL && subObject->objectFlags & kOFVoice) { + uint speechId = subObject->objectFlagValue[getOffsetOfChild2Param(subObject, kOFVoice)]; + + if (subObject->objectFlags & kOFNumber) { + uint speechIdOffs = subObject->objectFlagValue[getOffsetOfChild2Param(subObject, kOFNumber)]; + + if (speechId == 116) + speechId = speechIdOffs + 115; + if (speechId == 92) + speechId = speechIdOffs + 98; + if (speechId == 99) + speechId = 9; + if (speechId == 97) { + switch (speechIdOffs) { + case 12: + speechId = 109; + break; + case 14: + speechId = 108; + break; + case 18: + speechId = 107; + break; + case 20: + speechId = 106; + break; + case 22: + speechId = 105; + break; + case 28: + speechId = 104; + break; + case 90: + speechId = 103; + break; + case 92: + speechId = 102; + break; + case 100: + speechId = 51; + break; + default: + error("o2_screenTextPObj: invalid case %d", speechIdOffs); + } + } + } + + if (_speech) + playSpeech(speechId, vgaSpriteId); + } + + } + + if (subObject != NULL && subObject->objectFlags & kOFText && _subtitles) { + const char *stringPtr = (const char *)getStringPtrByID(subObject->objectFlagValue[0]); + TextLocation *tl = getTextLocation(vgaSpriteId); + char buf[256]; + int j, k; + + if (subObject->objectFlags & kOFNumber) { + if (_language == Common::HB_ISR) { + j = subObject->objectFlagValue[getOffsetOfChild2Param(subObject, kOFNumber)]; + k = (j % 10) * 10; + k += j / 10; + if (!(j % 10)) + sprintf(buf,"0%d%s", k, stringPtr); + else + sprintf(buf,"%d%s", k, stringPtr); + } else { + sprintf(buf,"%d%s", subObject->objectFlagValue[getOffsetOfChild2Param(subObject, kOFNumber)], stringPtr); + } + stringPtr = buf; + } + if (stringPtr != NULL) + printScreenText(vgaSpriteId, color, stringPtr, tl->x, tl->y, tl->width); + } +} + +void SimonEngine::o2_mouseOff() { + // 181: force mouseOff + scriptMouseOff(); + changeWindow(1); + showMessageFormat("\xC"); +} + +void SimonEngine::o2_isShortText() { + // 188: string2 is + uint i = getVarOrByte(); + uint str = getNextStringID(); + setScriptCondition(str < _numTextBoxes && _shortText[i] == str); +} + +void SimonEngine::o2_clearMarks() { + // 189: clear_op189_flag + _marks = 0; +} + +void SimonEngine::o2_waitMark() { + // 190 + uint i = getVarOrByte(); + if (!(_marks & (1 << i))) + waitForMark(i); +} + +// ----------------------------------------------------------------------- +// Feeble Files Opcodes +// ----------------------------------------------------------------------- + +void SimonEngine::o3_chance() { + // 23 + uint a = getVarOrWord(); + + if (a == 0) { + setScriptCondition(false); + return; + } + + if (a == 100) { + setScriptCondition(true); + return; + } + + if ((uint)_rnd.getRandomNumber(99) < a) + setScriptCondition(true); + else + setScriptCondition(false); +} + +void SimonEngine::o3_jumpOut() { + // 37 + getVarOrByte(); + setScriptReturn(1); +} + +void SimonEngine::o3_addTextBox() { + // 65: add hit area + uint flags = kBFTextBox | kBFBoxItem; + uint id = getVarOrWord(); + uint params = id / 1000; + uint x, y, w, h, num; + + id %= 1000; + + if (params & 1) + flags |= kBFInvertTouch; + + x = getVarOrWord(); + y = getVarOrWord(); + w = getVarOrWord(); + h = getVarOrWord(); + num = getVarOrByte(); + if (num < _numTextBoxes) + defineBox(id, x, y, w, h, flags + (num << 8), 208, _dummyItem2); +} + +void SimonEngine::o3_printLongText() { + // 70: show string from array + int num = getVarOrByte(); + const char *str = (const char *)getStringPtrByID(_longText[num]); + sendInteractText(num, "%d. %s\n", num, str); +} + +void SimonEngine::o3_addBox() { + // 107: add item hitarea + uint flags = 0; + uint id = getVarOrWord(); + uint params = id / 1000; + uint x, y, w, h, verb; + Item *item; + + id = id % 1000; + + if (params & 1) + flags |= kBFInvertTouch; + if (params & 2) + flags |= kBFNoTouchName; + if (params & 4) + flags |= kBFBoxItem; + if (params & 8) + flags |= kBFTextBox; + if (params & 16) + flags |= 0x10; + + x = getVarOrWord(); + y = getVarOrWord(); + w = getVarOrWord(); + h = getVarOrWord(); + item = getNextItemPtrStrange(); + verb = getVarOrWord(); + defineBox(id, x, y, w, h, flags, verb, item); +} + +void SimonEngine::o3_oracleTextDown() { + // 122: oracle text down + oracleTextDown(); +} + +void SimonEngine::o3_oracleTextUp() { + // 123: oracle text up + oracleTextUp(); +} + +void SimonEngine::o3_ifTime() { + // 124: if time + time_t t; + + uint a = getVarOrWord(); + time(&t); + t -= _gameStoppedClock; + t -= a; + if (t >= _timeStore) + setScriptCondition(true); + else + setScriptCondition(false); +} + +void SimonEngine::o3_playTune() { + // 127: usually deals with music, but is a no-op in FF. + getVarOrWord(); + getVarOrWord(); + getVarOrByte(); +} + +void SimonEngine::o3_setTime() { + // 131 + time(&_timeStore); + _timeStore -= _gameStoppedClock; +} + +void SimonEngine::o3_saveUserGame() { + // 132: save game + _noOracleScroll = 0; + _system->setFeatureState(OSystem::kFeatureVirtualKeyboard, true); + saveUserGame(countSaveGames() + 1 - readVariable(55)); + _system->setFeatureState(OSystem::kFeatureVirtualKeyboard, false); +} + +void SimonEngine::o3_loadUserGame() { + // 133: load game + loadGame(readVariable(55)); +} + +void SimonEngine::o3_listSaveGames() { + // 134: dummy opcode? + listSaveGames(1); +} + +void SimonEngine::o3_checkCD() { + // 135: switch CD + uint disc = readVariable(97); + + if (!strcmp(_gameDescription->extra, "4CD")) { + _sound->switchVoiceFile(gss, disc); + } else if (!strcmp(_gameDescription->extra, "2CD")) { + if (disc == 1 || disc == 2) + _sound->switchVoiceFile(gss, 1); + else if (disc == 3 || disc == 4) + _sound->switchVoiceFile(gss, 2); + } + + debug(0, "Switch to CD number %d", disc); +} + +void SimonEngine::o3_screenTextBox() { + // 161: setup text + TextLocation *tl = getTextLocation(getVarOrByte()); + + tl->x = getVarOrWord(); + tl->y = getVarOrWord(); + tl->width = getVarOrWord(); +} + +void SimonEngine::o3_isAdjNoun() { + // 165: item unk1 unk2 is + Item *item = getNextItemPtr(); + int16 a = getNextWord(), b = getNextWord(); + if (item->adjective == a && item->noun == b) + setScriptCondition(true); + else if (a == -1 && item->noun == b) + setScriptCondition(true); + else + setScriptCondition(false); +} + +void SimonEngine::o3_hyperLinkOn() { + // 171: oracle hyperlink on + hyperLinkOn(getVarOrWord()); +} + +void SimonEngine::o3_hyperLinkOff() { + // 172: oracle hyperlink off + hyperLinkOff(); +} + +void SimonEngine::o3_checkPaths() { + // 173 check paths + int i, count; + const uint8 *pathVal1 = _pathValues1; + bool result = false; + + count = _variableArray2[38]; + for (i = 0; i < count; i++) { + uint8 val = pathVal1[2]; + if (val == _variableArray2[50] || + val == _variableArray2[51] || + val == _variableArray2[201] || + val == _variableArray2[203] || + val == _variableArray2[205] || + val == _variableArray2[207] || + val == _variableArray2[209] || + val == _variableArray2[211] || + val == _variableArray2[213] || + val == _variableArray2[215] || + val == _variableArray2[219] || + val == _variableArray2[220] || + val == _variableArray2[221] || + val == _variableArray2[222] || + val == _variableArray2[223] || + val == _variableArray2[224] || + val == _variableArray2[225] || + val == _variableArray2[226]) { + result = true; + break; + } + pathVal1 += 4; + } + + _variableArray2[52] = result; +} + +void SimonEngine::o3_screenTextPObj() { + // 177: inventory descriptions + uint vgaSpriteId = getVarOrByte(); + uint color = getVarOrByte(); + const char *string_ptr = NULL; + TextLocation *tl = NULL; + char buf[256]; + + SubObject *subObject = (SubObject *)findChildOfType(getNextItemPtr(), 2); + if (subObject != NULL && subObject->objectFlags & kOFText) { + string_ptr = (const char *)getStringPtrByID(subObject->objectFlagValue[0]); + tl = getTextLocation(vgaSpriteId); + } + + if (subObject != NULL && subObject->objectFlags & kOFVoice) { + uint offs = getOffsetOfChild2Param(subObject, kOFVoice); + playSpeech(subObject->objectFlagValue[offs], vgaSpriteId); + } + + if (subObject != NULL && (subObject->objectFlags & kOFText) && _subtitles) { + if (subObject->objectFlags & kOFNumber) { + sprintf(buf, "%d%s", subObject->objectFlagValue[getOffsetOfChild2Param(subObject, kOFNumber)], string_ptr); + string_ptr = buf; + } + if (string_ptr != NULL) + printScreenText(vgaSpriteId, color, string_ptr, tl->x, tl->y, tl->width); + } +} + +void SimonEngine::o3_mouseOff() { + // 181: force mouseOff + scriptMouseOff(); + clearName(); +} + +void SimonEngine::o3_loadVideo() { + // 182: load video file + const byte *filename = getStringPtrByID(getNextStringID()); + _moviePlay->load((const char *)filename); +} + +void SimonEngine::o3_playVideo() { + // 183: play video + _moviePlay->play(); +} + +void SimonEngine::o3_centreScroll() { + // 187 + centreScroll(); +} + +void SimonEngine::o3_resetPVCount() { + // 191 + if (getBitFlag(83)) { + _PVCount1 = 0; + _GPVCount1 = 0; + } else { + _PVCount = 0; + _GPVCount = 0; + } +} + +void SimonEngine::o3_setPathValues() { + // 192 + uint8 a = getVarOrByte(); + uint8 b = getVarOrByte(); + uint8 c = getVarOrByte(); + uint8 d = getVarOrByte(); + if (getBitFlag(83)) { + _pathValues1[_PVCount1++] = a; + _pathValues1[_PVCount1++] = b; + _pathValues1[_PVCount1++] = c; + _pathValues1[_PVCount1++] = d; + } else { + _pathValues[_PVCount++] = a; + _pathValues[_PVCount++] = b; + _pathValues[_PVCount++] = c; + _pathValues[_PVCount++] = d; + } +} + +void SimonEngine::o3_stopClock() { + // 193: pause clock + _clockStopped = time(NULL); +} + +void SimonEngine::o3_restartClock() { + // 194: resume clock + if (_clockStopped != 0) + _gameStoppedClock += time(NULL) - _clockStopped; + _clockStopped = 0; +} + +void SimonEngine::o3_setColour() { + // 195: set palette colour + uint c = getVarOrByte() * 4; + uint r = getVarOrByte(); + uint g = getVarOrByte(); + uint b = getVarOrByte(); + + _displayPalette[c + 0] = r; + _displayPalette[c + 1] = g; + _displayPalette[c + 2] = b; + + _paletteFlag = 2; +} + +void SimonEngine::o3_b3Set() { + // 196: set bit3 + uint bit = getVarOrByte(); + _bitArrayThree[bit / 16] |= (1 << (bit & 15)); +} + +void SimonEngine::o3_b3Clear() { + // 197: clear bit3 + uint bit = getVarOrByte(); + _bitArrayThree[bit / 16] &= ~(1 << (bit & 15)); +} + +void SimonEngine::o3_b3Zero() { + // 198: is bit3 clear + uint bit = getVarOrByte(); + setScriptCondition((_bitArrayThree[bit / 16] & (1 << (bit & 15))) == 0); +} + +void SimonEngine::o3_b3NotZero() { + // 199: is bit3 set + uint bit = getVarOrByte(); + setScriptCondition((_bitArrayThree[bit / 16] & (1 << (bit & 15))) != 0); +} + +// ----------------------------------------------------------------------- +// Puzzle Pack Opcodes +// ----------------------------------------------------------------------- + +void SimonEngine::o4_opcode30() { + // 30 + getNextItemPtr(); +} + +void SimonEngine::o4_checkTiles() { + // 37: for MahJongg game + getVarOrByte(); +} + +void SimonEngine::o4_opcode38() { + // 38 + getVarOrByte(); + getNextItemPtr(); +} + +void SimonEngine::o4_loadHiScores() { + // 105 + getVarOrByte(); +} + +void SimonEngine::o4_checkHiScores() { + // 106 + getVarOrByte(); + getVarOrByte(); +} + +void SimonEngine::o4_loadUserGame() { + // 133 +} + +void SimonEngine::o4_saveOopsPosition() { + // 173 +} + +void SimonEngine::o4_resetGameTime() { + // 187 +} + +void SimonEngine::o4_resetPVCount() { + // 191 + _PVCount = 0; + _GPVCount = 0; +} + +void SimonEngine::o4_setPathValues() { + // 192 + _pathValues[_PVCount++] = getVarOrByte(); + _pathValues[_PVCount++] = getVarOrByte(); + _pathValues[_PVCount++] = getVarOrByte(); + _pathValues[_PVCount++] = getVarOrByte(); +} + +// ----------------------------------------------------------------------- + +int SimonEngine::runScript() { + int opcode; + bool flag; + + do { + if (_continousMainScript) + dumpOpcode(_codePtr); + + if (getGameType() == GType_ELVIRA || getGameType() == GType_ELVIRA2) { + opcode = getVarOrWord(); + if (opcode == 10000) + return 0; + } else { + opcode = getByte(); + if (opcode == 0xFF) + return 0; + } + debug(1, "runScript: opcode %d", opcode); + + if (_runScriptReturn1) + return 1; + + /* Invert condition? */ + flag = false; + if (getGameType() == GType_ELVIRA || getGameType() == GType_ELVIRA2) { + if (opcode == 203) { + flag = true; + opcode = getVarOrWord(); + if (opcode == 10000) + return 0; + } + } else { + if (opcode == 0) { + flag = true; + opcode = getByte(); + if (opcode == 0xFF) + return 0; + } + } + + setScriptCondition(true); + setScriptReturn(0); + + if (opcode > _numOpcodes || !_opcode_table[opcode]) + error("Invalid opcode '%d' encountered", opcode); + + (this->*_opcode_table[opcode]) (); + } while (getScriptCondition() != flag && !getScriptReturn()); + + return getScriptReturn(); +} + +void SimonEngine::scriptMouseOn() { + if (getGameType() == GType_FF && _mouseCursor != 5) { + resetVerbs(); + _noRightClick = 0; + } + if (getGameType() == GType_SIMON2 && getBitFlag(79)) { + _mouseCursor = 0; + } + _mouseHideCount = 0; +} + +void SimonEngine::scriptMouseOff() { + _lockWord |= 0x8000; + vc34_setMouseOff(); + _lockWord &= ~0x8000; +} + +void SimonEngine::waitForMark(uint i) { + _exitCutscene = false; + while (!(_marks & (1 << i))) { + if (_exitCutscene) { + if (getBitFlag(9)) { + endCutscene(); + break; + } + } else { + processSpecialKeys(); + } + + delay(10); + } +} + +void SimonEngine::freezeBottom() { + _vgaMemBase = _vgaMemPtr; + _vgaFrozenBase = _vgaMemPtr; +} + +void SimonEngine::unfreezeBottom() { + _vgaMemPtr = _vgaRealBase; + _vgaMemBase = _vgaRealBase; + _vgaFrozenBase = _vgaRealBase; +} + +void SimonEngine::sendSync(uint a) { + uint16 id = to16Wrapper(a); + _lockWord |= 0x8000; + _vcPtr = (byte *)&id; + vc15_sync(); + _lockWord &= ~0x8000; +} + +void SimonEngine::setTextColor(uint color) { + WindowBlock *window; + + window = _windowArray[_curWindow]; + window->text_color = color; +} + +void SimonEngine::stopAnimateSimon1(uint a) { + uint16 b = to16Wrapper(a); + _lockWord |= 0x8000; + _vcPtr = (byte *)&b; + vc60_killSprite(); + _lockWord &= ~0x8000; +} + +void SimonEngine::stopAnimateSimon2(uint a, uint b) { + uint16 items[2]; + + items[0] = to16Wrapper(a); + items[1] = to16Wrapper(b); + + _lockWord |= 0x8000; + _vcPtr = (byte *)&items; + vc60_killSprite(); + _lockWord &= ~0x8000; +} + +} // End of namespace Simon diff --git a/engines/agos/midi.cpp b/engines/agos/midi.cpp new file mode 100644 index 0000000000..2ff9bbd8ad --- /dev/null +++ b/engines/agos/midi.cpp @@ -0,0 +1,574 @@ +/* ScummVM - Scumm Interpreter + * Copyright (C) 2001 Ludvig Strigeus + * Copyright (C) 2001-2006 The ScummVM project + * + * 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. + * + * $URL$ + * $Id$ + * + */ + +#include "common/stdafx.h" + +#include "common/file.h" +#include "common/system.h" + +#include "agos/agos.h" + +#include "agos/agos.h" + +namespace Simon { + + +// MidiParser_S1D is not considered part of the standard +// MidiParser suite, but we still try to mask its details +// and just provide a factory function. +extern MidiParser *MidiParser_createS1D(); + +MidiPlayer::MidiPlayer(OSystem *system) { + // Since initialize() is called every time the music changes, + // this is where we'll initialize stuff that must persist + // between songs. + _driver = 0; + _map_mt32_to_gm = false; + _passThrough = false; + + _enable_sfx = true; + _current = 0; + + _masterVolume = 255; + resetVolumeTable(); + _paused = false; + + _currentTrack = 255; + _loopTrack = 0; + _queuedTrack = 255; + _loopQueuedTrack = 0; +} + +MidiPlayer::~MidiPlayer() { + _mutex.lock(); + close(); + _mutex.unlock(); +} + +int MidiPlayer::open() { + // Don't ever call open without first setting the output driver! + if (!_driver) + return 255; + + int ret = _driver->open(); + if (ret) + return ret; + _driver->setTimerCallback(this, &onTimer); + return 0; +} + +void MidiPlayer::close() { + stop(); +// _system->lockMutex(_mutex); + if (_driver) + _driver->close(); + _driver = NULL; + clearConstructs(); +// _system->unlockMutex(_mutex); +} + +void MidiPlayer::send(uint32 b) { + if (!_current) + return; + + if (_passThrough) { + _driver->send(b); + return; + } + + byte channel = (byte)(b & 0x0F); + if ((b & 0xFFF0) == 0x07B0) { + // Adjust volume changes by master volume. + byte volume = (byte)((b >> 16) & 0x7F); + _current->volume[channel] = volume; + volume = volume * _masterVolume / 255; + b = (b & 0xFF00FFFF) | (volume << 16); + } else if ((b & 0xF0) == 0xC0 && _map_mt32_to_gm) { + b = (b & 0xFFFF00FF) | (MidiDriver::_mt32ToGm[(b >> 8) & 0xFF] << 8); + } else if ((b & 0xFFF0) == 0x007BB0) { + // Only respond to an All Notes Off if this channel + // has already been allocated. + if (!_current->channel[b & 0x0F]) + return; + } else if ((b & 0xFFF0) == 0x79B0) { + // "Reset All Controllers". There seems to be some confusion + // about what this message should do to the volume controller. + // See http://www.midi.org/about-midi/rp15.shtml for more + // information. + // + // If I understand it correctly, the current standard indicates + // that the volume should be reset, but the next revision will + // exclude it. On my system, both ALSA and FluidSynth seem to + // reset it, while Adlib does not. Let's follow the majority. + + _current->volume[channel] = 127; + } + + if (!_current->channel[channel]) + _current->channel[channel] = (channel == 9) ? _driver->getPercussionChannel() : _driver->allocateChannel(); + if (_current->channel[channel]) { + if (channel == 9) + _current->channel[9]->volume(_current->volume[9] * _masterVolume / 255); + _current->channel[channel]->send(b); + if ((b & 0xFFF0) == 0x79B0) { + // We have received a "Reset All Controllers" message + // and passed it on to the MIDI driver. This may or may + // not have affected the volume controller. To ensure + // consistent behaviour, explicitly set the volume to + // what we think it should be. + + _current->channel[channel]->volume(_current->volume[channel] * _masterVolume / 255); + } + } +} + +void MidiPlayer::metaEvent(byte type, byte *data, uint16 length) { + // Only thing we care about is End of Track. + if (!_current || type != 0x2F) { + return; + } else if (_current == &_sfx) { + clearConstructs(_sfx); + } else if (_loopTrack) { + _current->parser->jumpToTick(0); + } else if (_queuedTrack != 255) { + _currentTrack = 255; + byte destination = _queuedTrack; + _queuedTrack = 255; + _loopTrack = _loopQueuedTrack; + _loopQueuedTrack = false; + + // Remember, we're still inside the locked mutex. + // Have to unlock it before calling jump() + // (which locks it itself), and then relock it + // upon returning. + _mutex.unlock(); + startTrack(destination); + _mutex.lock(); + } else { + stop(); + } +} + +void MidiPlayer::onTimer(void *data) { + MidiPlayer *p = (MidiPlayer *)data; + Common::StackLock lock(p->_mutex); + + if (!p->_paused) { + if (p->_music.parser && p->_currentTrack != 255) { + p->_current = &p->_music; + p->_music.parser->onTimer(); + } + } + if (p->_sfx.parser) { + p->_current = &p->_sfx; + p->_sfx.parser->onTimer(); + } + p->_current = 0; +} + +void MidiPlayer::startTrack(int track) { + if (track == _currentTrack) + return; + + if (_music.num_songs > 0) { + if (track >= _music.num_songs) + return; + + _mutex.lock(); + + if (_music.parser) { + _current = &_music; + delete _music.parser; + _current = 0; + _music.parser = 0; + } + + MidiParser *parser = MidiParser::createParser_SMF(); + parser->property (MidiParser::mpMalformedPitchBends, 1); + parser->setMidiDriver(this); + parser->setTimerRate(_driver->getBaseTempo()); + if (!parser->loadMusic(_music.songs[track], _music.song_sizes[track])) { + printf ("Error reading track!\n"); + delete parser; + parser = 0; + } + + _currentTrack = (byte)track; + _music.parser = parser; // That plugs the power cord into the wall + } else if (_music.parser) { + _mutex.lock(); + if (!_music.parser->setTrack(track)) { + _mutex.unlock(); + return; + } + _currentTrack = (byte)track; + _current = &_music; + _music.parser->jumpToTick(0); + _current = 0; + } + + _mutex.unlock(); +} + +void MidiPlayer::stop() { + Common::StackLock lock(_mutex); + + if (_music.parser) { + _current = &_music; + _music.parser->jumpToTick(0); + } + _current = 0; + _currentTrack = 255; +} + +void MidiPlayer::pause(bool b) { + if (_paused == b || !_driver) + return; + _paused = b; + + Common::StackLock lock(_mutex); + for (int i = 0; i < 16; ++i) { + if (_music.channel[i]) + _music.channel[i]->volume(_paused ? 0 : (_music.volume[i] * _masterVolume / 255)); + if (_sfx.channel[i]) + _sfx.channel[i]->volume(_paused ? 0 : (_sfx.volume[i] * _masterVolume / 255)); + } +} + +void MidiPlayer::set_volume(int volume) { + if (volume < 0) + volume = 0; + else if (volume > 255) + volume = 255; + + if (_masterVolume == volume) + return; + _masterVolume = volume; + + // Now tell all the channels this. + Common::StackLock lock(_mutex); + if (_driver && !_paused) { + for (int i = 0; i < 16; ++i) { + if (_music.channel[i]) + _music.channel[i]->volume(_music.volume[i] * _masterVolume / 255); + if (_sfx.channel[i]) + _sfx.channel[i]->volume(_sfx.volume[i] * _masterVolume / 255); + } + } +} + +void MidiPlayer::set_driver(MidiDriver *md) { + // Don't try to set this more than once. + if (_driver) + return; + _driver = md; +} + +void MidiPlayer::mapMT32toGM(bool map) { + Common::StackLock lock(_mutex); + + _map_mt32_to_gm = map; +} + +void MidiPlayer::setLoop(bool loop) { + Common::StackLock lock(_mutex); + + _loopTrack = loop; +} + +void MidiPlayer::queueTrack(int track, bool loop) { + _mutex.lock(); + if (_currentTrack == 255) { + _mutex.unlock(); + setLoop(loop); + startTrack(track); + } else { + _queuedTrack = track; + _loopQueuedTrack = loop; + _mutex.unlock(); + } +} + +void MidiPlayer::clearConstructs() { + clearConstructs(_music); + clearConstructs(_sfx); +} + +void MidiPlayer::clearConstructs(MusicInfo &info) { + int i; + if (info.num_songs > 0) { + for (i = 0; i < info.num_songs; ++i) + free(info.songs[i]); + info.num_songs = 0; + } + + if (info.data) { + free(info.data); + info.data = 0; + } // end if + + if (info.parser) { + delete info.parser; + info.parser = 0; + } + + if (_driver) { + for (i = 0; i < 16; ++i) { + if (info.channel[i]) { + info.channel[i]->allNotesOff(); + info.channel[i]->release(); + } + } + } + info.clear(); +} + +void MidiPlayer::resetVolumeTable() { + int i; + for (i = 0; i < 16; ++i) { + _music.volume[i] = _sfx.volume[i] = 127; + if (_driver) + _driver->send(((_masterVolume >> 1) << 16) | 0x7B0 | i); + } +} + +static int simon1_gmf_size[] = { + 8900, 12166, 2848, 3442, 4034, 4508, 7064, 9730, 6014, 4742, 3138, + 6570, 5384, 8909, 6457, 16321, 2742, 8968, 4804, 8442, 7717, + 9444, 5800, 1381, 5660, 6684, 2456, 4744, 2455, 1177, 1232, + 17256, 5103, 8794, 4884, 16 +}; + +void MidiPlayer::loadSMF(Common::File *in, int song, bool sfx) { + Common::StackLock lock(_mutex); + + MusicInfo *p = sfx ? &_sfx : &_music; + clearConstructs(*p); + + uint32 startpos = in->pos(); + byte header[4]; + in->read(header, 4); + bool isGMF = !memcmp(header, "GMF\x1", 4); + in->seek(startpos, SEEK_SET); + + uint32 size = in->size() - in->pos(); + if (isGMF) { + if (sfx) { + // Multiple GMF resources are stored in the SFX files, + // but each one is referenced by a pointer at the + // beginning of the file. Those pointers can be used + // to determine file size. + in->seek(0, SEEK_SET); + uint16 value = in->readUint16LE() >> 2; // Number of resources + if (song != value - 1) { + in->seek(song * 2 + 2, SEEK_SET); + value = in->readUint16LE(); + size = value - startpos; + } + in->seek(startpos, SEEK_SET); + } else if (size >= 64000) { + // For GMF resources not in separate + // files, we're going to have to use + // hardcoded size tables. + size = simon1_gmf_size[song]; + } + } + + // When allocating space, add 4 bytes in case + // this is a GMF and we have to tack on our own + // End of Track event. + p->data = (byte *)calloc(size + 4, 1); + in->read(p->data, size); + + uint32 timerRate = _driver->getBaseTempo(); + + if (!memcmp(p->data, "GMF\x1", 4)) { + // The GMF header + // 3 BYTES: 'GMF' + // 1 BYTE : Major version + // 1 BYTE : Minor version + // 1 BYTE : Ticks (Ranges from 2 - 8, always 2 for SFX) + // 1 BYTE : Loop control. 0 = no loop, 1 = loop + + // In the original, the ticks value indicated how many + // times the music timer was called before it actually + // did something. The larger the value the slower the + // music. + // + // We, on the other hand, have a timer rate which is + // used to control by how much the music advances on + // each onTimer() call. The larger the value, the + // faster the music. + // + // It seems that 4 corresponds to our base tempo, so + // this should be the right way to calculate it. + timerRate = (4 * _driver->getBaseTempo()) / p->data[5]; + + // According to bug #1004919 calling setLoop() from + // within a lock causes a lockup, though I have no + // idea when this actually happens. + _loopTrack = (p->data[6] != 0); + } + + MidiParser *parser = MidiParser::createParser_SMF(); + parser->property(MidiParser::mpMalformedPitchBends, 1); + parser->setMidiDriver(this); + parser->setTimerRate(timerRate); + if (!parser->loadMusic(p->data, size)) { + printf("Error reading track!\n"); + delete parser; + parser = 0; + } + + if (!sfx) { + _currentTrack = 255; + resetVolumeTable(); + } + p->parser = parser; // That plugs the power cord into the wall +} + +void MidiPlayer::loadMultipleSMF(Common::File *in, bool sfx) { + // This is a special case for Simon 2 Windows. + // Instead of having multiple sequences as + // separate tracks in a Type 2 file, simon2win + // has multiple songs, each of which is a Type 1 + // file. Thus, preceding the songs is a single + // byte specifying how many songs are coming. + // We need to load ALL the songs and then + // treat them as separate tracks -- for the + // purpose of jumps, anyway. + Common::StackLock lock(_mutex); + + MusicInfo *p = sfx ? &_sfx : &_music; + clearConstructs(*p); + + p->num_songs = in->readByte(); + if (p->num_songs > 16) { + printf ("playMultipleSMF: %d is too many songs to keep track of!\n", (int)p->num_songs); + return; + } + + byte i; + for (i = 0; i < p->num_songs; ++i) { + byte buf[5]; + uint32 pos = in->pos(); + + // Make sure there's a MThd + in->read(buf, 4); + if (memcmp(buf, "MThd", 4)) { + printf("Expected MThd but found '%c%c%c%c' instead!\n", buf[0], buf[1], buf[2], buf[3]); + return; + } + in->seek(in->readUint32BE(), SEEK_CUR); + + // Now skip all the MTrk blocks + while (true) { + in->read(buf, 4); + if (memcmp(buf, "MTrk", 4)) + break; + in->seek(in->readUint32BE(), SEEK_CUR); + } + + uint32 pos2 = in->pos() - 4; + uint32 size = pos2 - pos; + p->songs[i] = (byte *)calloc(size, 1); + in->seek(pos, SEEK_SET); + in->read(p->songs[i], size); + p->song_sizes[i] = size; + } + + if (!sfx) { + _currentTrack = 255; + resetVolumeTable(); + } +} + +void MidiPlayer::loadXMIDI(Common::File *in, bool sfx) { + Common::StackLock lock(_mutex); + MusicInfo *p = sfx ? &_sfx : &_music; + clearConstructs(*p); + + char buf[4]; + uint32 pos = in->pos(); + uint32 size = 4; + in->read(buf, 4); + if (!memcmp(buf, "FORM", 4)) { + int i; + for (i = 0; i < 16; ++i) { + if (!memcmp(buf, "CAT ", 4)) + break; + size += 2; + memcpy(buf, &buf[2], 2); + in->read(&buf[2], 2); + } + if (memcmp(buf, "CAT ", 4)) { + error("Could not find 'CAT ' tag to determine resource size"); + } + size += 4 + in->readUint32BE(); + in->seek(pos, 0); + p->data = (byte *)calloc(size, 1); + in->read(p->data, size); + } else { + error("Expected 'FORM' tag but found '%c%c%c%c' instead", buf[0], buf[1], buf[2], buf[3]); + } + + MidiParser *parser = MidiParser::createParser_XMIDI(); + parser->setMidiDriver(this); + parser->setTimerRate(_driver->getBaseTempo()); + if (!parser->loadMusic(p->data, size)) + error("Error reading track"); + + if (!sfx) { + _currentTrack = 255; + resetVolumeTable(); + } + p->parser = parser; // That plugs the power cord into the wall +} + +void MidiPlayer::loadS1D(Common::File *in, bool sfx) { + Common::StackLock lock(_mutex); + MusicInfo *p = sfx ? &_sfx : &_music; + clearConstructs(*p); + + uint16 size = in->readUint16LE(); + if (size != in->size() - 2) { + error("Size mismatch in simon1demo MUS file (%ld versus reported %d)", (long)in->size() - 2, (int)size); + } + + p->data = (byte *)calloc(size, 1); + in->read(p->data, size); + + MidiParser *parser = MidiParser_createS1D(); + parser->setMidiDriver(this); + parser->setTimerRate(_driver->getBaseTempo()); + if (!parser->loadMusic(p->data, size)) + error("Error reading track"); + + if (!sfx) { + _currentTrack = 255; + resetVolumeTable(); + } + p->parser = parser; // That plugs the power cord into the wall +} + +} // End of namespace Simon diff --git a/engines/agos/midi.h b/engines/agos/midi.h new file mode 100644 index 0000000000..cf055bffac --- /dev/null +++ b/engines/agos/midi.h @@ -0,0 +1,128 @@ +/* ScummVM - Scumm Interpreter + * Copyright (C) 2001 Ludvig Strigeus + * Copyright (C) 2001-2006 The ScummVM project + * + * 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. + * + * $URL$ + * $Id$ + * + */ + +#ifndef AGOS_MIDI_H +#define AGOS_MIDI_H + +#include "sound/mididrv.h" +#include "sound/midiparser.h" +#include "common/mutex.h" + +namespace Common { + class File; +} + +namespace Simon { + +struct MusicInfo { + MidiParser *parser; + byte *data; + byte num_songs; // For Type 1 SMF resources + byte *songs[16]; // For Type 1 SMF resources + uint32 song_sizes[16]; // For Type 1 SMF resources + + MidiChannel *channel[16]; // Dynamic remapping of channels to resolve conflicts + byte volume[16]; // Current channel volume + + MusicInfo() { clear(); } + void clear() { + parser = 0; data = 0; num_songs = 0; + memset(songs, 0, sizeof(songs)); + memset(song_sizes, 0, sizeof(song_sizes)); + memset(channel, 0, sizeof(channel)); + } +}; + +class MidiPlayer : public MidiDriver { +protected: + Common::Mutex _mutex; + MidiDriver *_driver; + bool _map_mt32_to_gm; + bool _passThrough; + + MusicInfo _music; + MusicInfo _sfx; + MusicInfo *_current; // Allows us to establish current context for operations. + + // These are maintained for both music and SFX + byte _masterVolume; // 0-255 + bool _paused; + + // These are only used for music. + byte _currentTrack; + bool _loopTrack; + byte _queuedTrack; + bool _loopQueuedTrack; + +protected: + static void onTimer(void *data); + void clearConstructs(); + void clearConstructs(MusicInfo &info); + void resetVolumeTable(); + +public: + bool _enable_sfx; + +public: + MidiPlayer(OSystem *system); + virtual ~MidiPlayer(); + + void loadSMF(Common::File *in, int song, bool sfx = false); + void loadMultipleSMF(Common::File *in, bool sfx = false); + void loadXMIDI(Common::File *in, bool sfx = false); + void loadS1D(Common::File *in, bool sfx = false); + + void mapMT32toGM(bool map); + void setLoop(bool loop); + void startTrack(int track); + void queueTrack(int track, bool loop); + bool isPlaying(bool check_queued = false) { return (_currentTrack != 255 && (_queuedTrack != 255 || !check_queued)); } + + void stop(); + void pause(bool b); + + int get_volume() { return _masterVolume; } + void set_volume(int volume); + void set_driver(MidiDriver *md); + +public: + // MidiDriver interface implementation + int open(); + void close(); + void send(uint32 b); + + void metaEvent(byte type, byte *data, uint16 length); + void setPassThrough(bool b) { _passThrough = b; } + + // Timing functions - MidiDriver now operates timers + void setTimerCallback(void *timer_param, void (*timer_proc) (void *)) { } + uint32 getBaseTempo(void) { return _driver ? _driver->getBaseTempo() : 0; } + + // Channel allocation functions + MidiChannel *allocateChannel() { return 0; } + MidiChannel *getPercussionChannel() { return 0; } +}; + +} // End of namespace Simon + +#endif diff --git a/engines/agos/midiparser_s1d.cpp b/engines/agos/midiparser_s1d.cpp new file mode 100644 index 0000000000..2b58c9ffa1 --- /dev/null +++ b/engines/agos/midiparser_s1d.cpp @@ -0,0 +1,155 @@ +/* ScummVM - Scumm Interpreter + * Copyright (C) 2001 Ludvig Strigeus + * Copyright (C) 2001-2006 The ScummVM project + * + * 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. + * + * $URL$ + * $Id$ + * + */ + +#include "common/stdafx.h" + +#include "common/util.h" + +#include "sound/mididrv.h" +#include "sound/midiparser.h" + +#include <stdio.h> + +namespace Simon { + +/** + * Simon 1 Demo version of MidiParser. + * + * This parser is the result of eyeballing the one MUS file that's included + * with simon1demo. So there might be some things missing. I've tried to notate + * question-mark areas where they occur. + */ +class MidiParser_S1D : public MidiParser { +protected: + byte *_data; + bool _no_delta; + +protected: + void parseNextEvent (EventInfo &info); + void resetTracking(); + uint32 readVLQ2(byte * &data); + +public: + MidiParser_S1D() : _data(0), _no_delta(false) {} + + bool loadMusic(byte *data, uint32 size); +}; + + +// The VLQs for simon1demo seem to be +// in Little Endian format. +uint32 MidiParser_S1D::readVLQ2(byte * &data) { + byte str; + uint32 value = 0; + int i; + + for (i = 0; i < 4; ++i) { + str = data[0]; + ++data; + value |= (str & 0x7F) << (i * 7); + if (!(str & 0x80)) + break; + } + return value; +} + +void MidiParser_S1D::parseNextEvent(EventInfo &info) { + info.start = _position._play_pos; + info.delta = _no_delta ? 0 : readVLQ2(_position._play_pos); + + _no_delta = false; + info.event = *(_position._play_pos++); + if (info.command() < 0x8) { + _no_delta = true; + info.event += 0x80; + } + + switch (info.command()) { + case 0x8: + info.basic.param1 = *(_position._play_pos++); + info.basic.param2 = 0; + info.length = 0; + break; + + case 0x9: + info.basic.param1 = *(_position._play_pos++); + info.basic.param2 = *(_position._play_pos++); // I'm ASSUMING this byte is velocity! + info.length = 0; + break; + + case 0xC: + info.basic.param1 = *(_position._play_pos++); + info.basic.param2 = 0; + ++_position._play_pos; // I have NO IDEA what the second byte is for. + break; + + case 0xF: + if (info.event == 0xFC) { + // This means End of Track. + // Rewrite in SMF (MIDI transmission) form. + info.event = 0xFF; + info.ext.type = 0x2F; + info.length = 0; + break; + } + // OTherwise fall through to default. + + default: + debug(3, "MidiParser_S1D: Unexpected byte 0x%02X found", (int) info.command()); + break; + } +} + +bool MidiParser_S1D::loadMusic(byte *data, uint32 size) { + unloadMusic(); + + byte *pos = data; + if (*(pos++) != 0xFC) + error("Expected 0xFC header but found 0x%02X instead", (int) *pos); + + // The next 3 bytes MIGHT be tempo, but we skip them and use the default. +// setTempo (*(pos++) | (*(pos++) << 8) | (*(pos++) << 16)); + pos += 3; + + // And now we're at the actual data. Only one track. + _num_tracks = 1; + _data = pos; + _tracks[0] = pos; + + // Note that we assume the original data passed in + // will persist beyond this call, i.e. we do NOT + // copy the data to our own buffer. Take warning.... + resetTracking(); + setTempo(666667); + setTrack(0); + return true; +} + +void MidiParser_S1D::resetTracking() { + MidiParser::resetTracking(); + _no_delta = false; +} + +MidiParser *MidiParser_createS1D() { return new MidiParser_S1D; } + +} // End of namespace Simon diff --git a/engines/agos/module.mk b/engines/agos/module.mk new file mode 100644 index 0000000000..d58c545866 --- /dev/null +++ b/engines/agos/module.mk @@ -0,0 +1,34 @@ +MODULE := engines/agos + +MODULE_OBJS := \ + agos.o \ + animation.o \ + charset.o \ + cursor.o \ + debug.o \ + debugger.o \ + draw.o \ + event.o \ + game.o \ + icons.o \ + items.o \ + midi.o \ + midiparser_s1d.o \ + oracle.o \ + res.o \ + rooms.o \ + saveload.o \ + sound.o \ + string.o \ + subroutine.o \ + verb.o \ + vga.o \ + window.o + +# This module can be built as a plugin +ifdef BUILD_PLUGINS +PLUGIN := 1 +endif + +# Include common rules +include $(srcdir)/rules.mk diff --git a/engines/agos/oracle.cpp b/engines/agos/oracle.cpp new file mode 100644 index 0000000000..3e6448fbae --- /dev/null +++ b/engines/agos/oracle.cpp @@ -0,0 +1,432 @@ +/* ScummVM - Scumm Interpreter + * Copyright (C) 2001 Ludvig Strigeus + * Copyright (C) 2001-2006 The ScummVM project + * + * 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. + * + * $URL$ + * $Id$ + * + */ + +#include "common/stdafx.h" + +#include "common/savefile.h" + +#include "agos/agos.h" +#include "agos/intern.h" +#include "agos/vga.h" + +namespace Simon { + +void SimonEngine::checkLinkBox() { // Check for boxes spilling over to next row of text + if (_hyperLink != 0) { + _variableArray[52] = _textWindow->x + _textWindow->textColumn - _variableArray[50]; + if (_variableArray[52] != 0) { + defineBox(_variableArray[53], _variableArray[50], _variableArray[51], _variableArray[52], 15, 145, 208, _dummyItem2); + _variableArray[53]++; + } + _variableArray[50] = _textWindow->x; + _variableArray[51] = _textWindow->textRow + _textWindow->y + (_oracleMaxScrollY-_textWindow->scrollY) * 15; + } +} + +void SimonEngine::hyperLinkOn(uint16 x) { + if (!getBitFlag(51)) + return; + + _hyperLink = x; + _variableArray[50] = _textWindow->textColumn + _textWindow->x; + _variableArray[51] = _textWindow->textRow + _textWindow->y + (_oracleMaxScrollY - _textWindow->scrollY) * 15; +} + + +void SimonEngine::hyperLinkOff() { + if (!getBitFlag(51)) + return; + + _variableArray[52] = _textWindow->x + _textWindow->textColumn - _variableArray[50]; + defineBox(_variableArray[53], _variableArray[50], _variableArray[51], _variableArray[52], 15, 145, 208, _dummyItem2); + _variableArray[53]++; + _hyperLink = 0; +} + +void SimonEngine::linksUp() { // Scroll Oracle Links + uint16 j; + for (j = 700; j < _variableArray[53]; j++) { + moveBox(j, 0, -15); + } +} + +void SimonEngine::linksDown() { + uint16 i; + for (i = 700; i < _variableArray[53]; i++) { + moveBox(i,0, 15); + } +} + +void SimonEngine::scrollOracle() { + int i; + + for (i = 0; i < 5; i++) + scrollOracleUp(); +} + +void SimonEngine::oracleTextUp() { + Subroutine *sub; + int i = 0; + changeWindow(3); + _noOracleScroll = 0; + + if (_textWindow->scrollY > _oracleMaxScrollY) // For scroll up + _oracleMaxScrollY = _textWindow->scrollY; + while (1) { + if (_textWindow->scrollY == _oracleMaxScrollY) + break; + _textWindow->textRow = 105; + for (i = 0; i < 5; i++) { + _newLines = 0; + _textWindow->textColumn = 0; + _textWindow->textRow -= 3; + if (i == 2) { + _textWindow->scrollY += 1; + _textWindow->textRow += 15; + linksUp(); + } + scrollOracleUp(); + setBitFlag(94, true); + sub = getSubroutineByID(_variableArray[104]); + if (sub) + startSubroutineEx(sub); + setBitFlag(94, false); + } + if (_currentBoxNumber != 601 || !getBitFlag(89)) + break; + delay(100); + } +} + +void SimonEngine::oracleTextDown() { + Subroutine *sub; + int i = 0; + changeWindow(3); + _noOracleScroll = 0; + + if (_textWindow->scrollY > _oracleMaxScrollY) // For scroll up + _oracleMaxScrollY = _textWindow->scrollY; + while (1) { + if (_textWindow->scrollY == 0) + break; + + for (i = 0; i < 5; i++) { + _newLines = 0; + _textWindow->textColumn = 0; + _textWindow->textRow = (i + 1) * 3; + if (i == 4) { + _textWindow->scrollY -= 1; + _textWindow->textRow = 0; + linksDown(); + } + scrollOracleDown(); + setBitFlag(93, true); + sub = getSubroutineByID(_variableArray[104]); + if (sub) + startSubroutineEx(sub); + setBitFlag(93, false); + } + if (_currentBoxNumber != 600 || !getBitFlag(89)) + break; + delay(100); + } +} + +void SimonEngine::scrollOracleUp() { + byte *src, *dst; + uint16 w, h; + + dst = getFrontBuf() + 103 * _screenWidth + 136; + src = getFrontBuf() + 106 * _screenWidth + 136; + + for (h = 0; h < 21; h++) { + for (w = 0; w < 360; w++) { + if (dst[w] == 0 || dst[w] == 113 || dst[w] == 116 || dst[w] == 252) + dst[w] = src[w]; + } + dst += _screenWidth; + src += _screenWidth; + } + + for (h = 0; h < 80; h++) { + memcpy(dst, src, 360); + dst += _screenWidth; + src += _screenWidth; + } + + for (h = 0; h < 3; h++) { + memset(dst, 0, 360); + dst += _screenWidth; + src += _screenWidth; + } +} + +void SimonEngine::scrollOracleDown() { + byte *src, *dst; + uint16 w, h; + + src = getFrontBuf() + 203 * _screenWidth + 136; + dst = getFrontBuf() + 206 * _screenWidth + 136; + + for (h = 0; h < 77; h++) { + memcpy(dst, src, 360); + dst -= _screenWidth; + src -= _screenWidth; + } + + for (h = 0; h < 24; h++) { + for (w = 0; w < 360; w++) { + if (src[w] == 0) + dst[w] = src[w]; + + if (src[w] == 113 || src[w] == 116 || src[w] == 252) { + dst[w] = src[w]; + src[w] = 0; + } + } + dst -= _screenWidth; + src -= _screenWidth; + } +} + +void SimonEngine::oracleLogo() { + Common::Rect srcRect, dstRect; + byte *src, *dst; + uint16 w, h; + + dstRect.left = 16; + dstRect.top = 16; + dstRect.right = 58; + dstRect.bottom = 59; + + srcRect.left = 0; + srcRect.top = 0; + srcRect.right = 42; + srcRect.bottom = 43; + + src = _iconFilePtr; + dst = getBackBuf() + _screenWidth * dstRect.top + dstRect.left; + + for (h = 0; h < dstRect.height(); h++) { + for (w = 0; w < dstRect.width(); w++) { + if (src[w]) + dst[w] = src[w]; + } + src += 336; + dst += _screenWidth; + } +} + +void SimonEngine::swapCharacterLogo() { + Common::Rect srcRect, dstRect; + byte *src, *dst; + uint16 w, h; + int x; + + dstRect.left = 64; + dstRect.top = 16; + dstRect.right = 106; + dstRect.bottom = 59; + + srcRect.top = 0; + srcRect.bottom = 43; + + x = _variableArray[91]; + if (x > _variableArray[90]) + x--; + if (x < _variableArray[90]) + x++; + _variableArray[91] = x; + + x++; + x *= 42; + + srcRect.left = x; + srcRect.right = srcRect.left + 42; + + src = _iconFilePtr + srcRect.top * 336 + srcRect.left; + dst = getBackBuf() + _screenWidth * dstRect.top + dstRect.left; + + for (h = 0; h < dstRect.height(); h++) { + for (w = 0; w < dstRect.width(); w++) { + if (src[w]) + dst[w] = src[w]; + } + src += 336; + dst += _screenWidth; + } +} + +void SimonEngine::listSaveGames(int n) { + char b[108]; + Common::InSaveFile *in; + uint16 j, k, z, maxFiles; + int OK; + memset(b, 0, 108); + + maxFiles = countSaveGames() - 1; + j = maxFiles - n + 1; + k = maxFiles - j + 1; + z = maxFiles; + if (getBitFlag(95)) { + j++; + z++; + } + + while (1) { + OK = 1; + if (getBitFlag(93) || getBitFlag(94)) { + OK = 0; + if (j > z) + break; + } + + if (getBitFlag(93)) { + if (((_newLines + 1) >= _textWindow->scrollY) && ((_newLines + 1) < (_textWindow->scrollY + 3))) + OK = 1; + } + + if (getBitFlag(94)) { + if ((_newLines + 1) == (_textWindow->scrollY + 7)) + OK = 1; + } + + + if (OK == 1) { + if (j == maxFiles + 1) { + showMessageFormat("\n"); + hyperLinkOn(j + 400); + setTextColor(116); + showMessageFormat(" %d. ",1); + hyperLinkOff(); + setTextColor(113); + k++; + j--; + } + + if (!(in = _saveFileMan->openForLoading(genSaveName(j)))) + break; + in->read(b, 100); + delete in; + } + + showMessageFormat("\n"); + hyperLinkOn(j + 400); + setTextColor(116); + if (k < 10) + showMessageFormat(" "); + showMessageFormat("%d. ",k); + setTextColor(113); + showMessageFormat("%s ",b); + hyperLinkOff(); + j--; + k++; + } +} + +void SimonEngine::saveUserGame(int slot) { + WindowBlock *window; + Common::InSaveFile *in; + char name[108]; + int len; + memset(name, 0, 108); + + window = _windowArray[3]; + + window->textRow = (slot + 1 - window->scrollY) * 15; + window->textColumn = 26; + + if ((in = _saveFileMan->openForLoading(genSaveName(readVariable(55))))) { + in->read(name, 100); + delete in; + } + + len = 0; + while (name[len]) { + byte chr = name[len]; + window->textColumn += getFeebleFontSize(chr); + len++; + } + + windowPutChar(window, 0x7f); + for (;;) { + _keyPressed = 0; + delay(1); + + if (_keyPressed == 0 || _keyPressed >= 127) + continue; + + window->textColumn -= getFeebleFontSize(127); + name[len] = 0; + windowBackSpace(_windowArray[3]); + + if (_keyPressed == 27) { + _variableArray[55] = _keyPressed; + break; + } + if (_keyPressed == 10 || _keyPressed == 13) { + if (!saveGame(readVariable(55), name)) + _variableArray[55] = (int16)0xFFFF; + else + _variableArray[55] = 0; + break; + } + if (_keyPressed == 8 && len != 0) { + len--; + byte chr = name[len]; + window->textColumn -= getFeebleFontSize(chr); + name[len] = 0; + windowBackSpace(_windowArray[3]); + } + if (_keyPressed >= 32 && window->textColumn + 26 <= window->width) { + name[len++] = _keyPressed; + windowPutChar(_windowArray[3], _keyPressed); + } + + windowPutChar(window, 0x7f); + } +} + +void SimonEngine::windowBackSpace(WindowBlock *window) { + byte *dst; + uint x, y, h, w; + + _lockWord |= 0x8000; + + x = window->x + window->textColumn; + y = window->y + window->textRow; + + dst = getFrontBuf() + _dxSurfacePitch * y + x; + + for (h = 0; h < 13; h++) { + for (w = 0; w < 8; w++) { + if (dst[w] == 113 || dst[w] == 116 || dst[w] == 252) + dst[w] = 0; + } + dst += _screenWidth; + } + + _lockWord &= ~0x8000; +} + +} // End of namespace Simon diff --git a/engines/agos/res.cpp b/engines/agos/res.cpp new file mode 100644 index 0000000000..e8d4f2fd04 --- /dev/null +++ b/engines/agos/res.cpp @@ -0,0 +1,1055 @@ +/* ScummVM - Scumm Interpreter + * Copyright (C) 2001 Ludvig Strigeus + * Copyright (C) 2001-2006 The ScummVM project + * + * 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. + * + * $URL$ + * $Id$ + * + */ + +// Resource file routines for Simon1/Simon2 +#include "common/stdafx.h" + +#include "common/file.h" + +#include "agos/agos.h" +#include "agos/intern.h" +#include "agos/sound.h" + + +#ifdef USE_ZLIB +#include <zlib.h> +#endif + +using Common::File; + +namespace Simon { + +// Script opcodes to load into memory +static const char *const opcode_arg_table_elvira[300] = { + "I ", "I ", "I ", "I ", "I ", "I ", "I ", "I ", "II ", "II ", "II ", "II ", "F ", "F ", "FN ", /* EQ", */ + "FN ", "FN ", "FN ", "FF ", "FF ", "FF ", "FF ", "II ", "II ", "a ", "a ", "n ", "n ", "p ", /* PREP", */ + "N ", "I ", "I ", "I ", "I ", "IN ", "IB ", "IB ", "II ", "IB ", "N ", " ", " ", " ", "I ", /* GET", */ + "I ","I ","I ", "I ","I ","I ", "II ","II ","II ","II ","IBF ", "FIB ", "FF ", "N ", "NI ", + "IF ", "F ", "F ", "IB ", "IB ", "FN ", "FN ", "FN ", "FF ", "FF ", "FN ", "FN ", "FF ", "FF ", /* DIVF", */ + "FN ", "FF ", "FN ", "F ", "I ", "IN ", "IN ", "IB ", "IB ", "IB ", "IB ", "II ", "I ", "I ", /* DEC", */ + "IN ", "T ", "F ", " ", "T ", "T ", "I ", "I ", " ", " ", "T ", " ", " ", " ", " ", " ", "T ", /* PARSE", */ + " ", "N ", "INN ", "II ", "II ", "ITN ", "ITIN ", "ITIN ", "I3 ", "IN ", "I ", "I ", "Ivnn ", + "vnn ", "Ivnn ", "NN ", "IT ", "INN ", " ", "N ", "N ", "N ", "T ", "v ", " ", " ", " ", " ", + "FN ", "I ", "TN ", "IT ", "II ", "I ", " ", "N ", "I ", " ", "I ", "NI ", "I ", "I ", "T ", /* BECOME", */ + "I ", "I ", "N ", "N ", " ", "N ", "IF ", "IF ", "IF ", "IF ", "IF ", "IF ", "T ", "IB ", + "IB ", "IB ", "I ", " ", "vnnN ", "Ivnn ", "T ", "T ", "T ", "IF ", " ", " ", " ", "Ivnn ", + "IF ", "INI ", "INN ", "IN ", "II ", "IFF ", "IIF ", "I ", "II ", "I ", "I ", "IN ", "IN ", /* ROPENEXT", */ + "II ", "II ", "II ", "II ", "IIN ", "IIN ", "IN ", "II ", "IN ", "IN ", "T ", "vanpan ", + "vIpI ", "T ", "T ", " ", " ", "IN ", "IN ", "IN ", "IN ", "N ", "INTTT ", "ITTT ", + "ITTT ", "I ", "I ", "IN ", "I ", " ", "F ", "NN ", "INN ", "INN ", "INNN ", "TF ", "NN ", /* PICTURE", */ + "N ", "NNNNN ", "N ", " ", "NNNNNNN ", "N ", " ", "N ", "NN ", "N ", "NNNNNIN ", "N ", "N ", /* ENABLEBOX", */ + "N ", "NNN ", "NNNN ", "INNN ", "IN ", "IN ", "TT ", "I ", "I ", "I ", "TTT ", "IN ", "IN ", /* UNSETCLASS",*/ + "FN ", "FN ", "FN ", "N ", "N ", "N ", "NI ", " ", " ", "N ", "I ", "INN ", "NN ", "N ", /* WAITENDTUNE */ + "N ", "Nan ", "NN ", " ", " ", " ", " ", " ", " ", " ", "IF ", "N ", " ", " ", " ", "II ", /* PLACENOICONS*/ + " ", "NI ","N ", +}; + +static const char *const opcode_arg_table_waxworks[256] = { + " ", "I ", "I ", "I ", "I ", "I ", "I ", "II ", "II ", "II ", "II ", "B ", "B ", "BN ", "BN ", + "BN ", "BN ", "BB ", "BB ", "BB ", "BB ", "II ", "II ", "N ", "I ", "I ", "I ", "IN ", "IB ", + "II ", "I ", "I ", "II ", "II ", "IBB ", "BIB ", "BB ", "B ", "BI ", "IB ", "B ", "B ", "BN ", + "BN ", "BN ", "BB ", "BB ", "BN ", "BN ", "BB ", "BB ", "BN ", "BB ", "BN ", "B ", "I ", "IB ", + "IB ", "II ", "I ", "I ", "IN ", "B ", "T ", "T ", "NNNNNB ", "BT ", "BT ", "T ", " ", "B ", + "N ", "IBN ", "I ", "I ", "I ", "NN ", " ", " ", "IT ", "II ", "I ", "B ", " ", "IB ", "IBB ", + "IIB ", "T ", "T ", "T ", "IB ", "IB ", "IB ", "B ", "BB ", "IBB ", "NB ", "N ", "NBNNN ", "N ", + " ", "BNNNNNN ", "B ", " ", "B ", "B ", "BB ", "NNNNNIN ", "N ", "N ", "N ", "NNN ", "NBNN ", + "IBNN ", "IB ", "IB ", "IB ", "IB ", "N ", "N ", "N ", "BI ", " ", " ", "N ", "I ", "IBB ", + "NN ", "N ", "N ", "Ban ", "BB ", " ", " ", " ", " ", "IB ", "B ", " ", "II ", " ", "BI ", "N ", + "I ", "IB ", "IB ", "IB ", "IB ", "IB ", "IB ", "IB ", "BI ", "BB ", "B ", "B ", "B ", "B ", + "IBB ", "IBN ", "IB ", "B ", " ", "TB ", "TB ", "I ", "N ", "B ", "INB ", "INB ", "INB ", "INB ", + "INB ", "INB ", "INB ", "N ", " ", "INBB ", "B ", "B ", "Ian ", "B ", "B ", "B ", "B ", "T ", + "T ", "B ", " ", "I ", " ", " " +}; + +static const char *const opcode_arg_table_simon1win[256] = { + " ", "I ", "I ", "I ", "I ", "I ", "I ", "II ", "II ", "II ", "II ", "B ", "B ", "BN ", "BN ", + "BN ", "BN ", "BB ", "BB ", "BB ", "BB ", "II ", "II ", "N ", "I ", "I ", "I ", "IN ", "IB ", + "II ", "I ", "I ", "II ", "II ", "IBB ", "BIB ", "BB ", "B ", "BI ", "IB ", "B ", "B ", "BN ", + "BN ", "BN ", "BB ", "BB ", "BN ", "BN ", "BB ", "BB ", "BN ", "BB ", "BN ", "B ", "I ", "IB ", + "IB ", "II ", "I ", "I ", "IN ", "B ", "T ", "T ", "NNNNNB ", "BT ", "BTS ", "T ", " ", "B ", + "N ", "IBN ", "I ", "I ", "I ", "NN ", " ", " ", "IT ", "II ", "I ", "B ", " ", "IB ", "IBB ", + "IIB ", "T ", " ", " ", "IB ", "IB ", "IB ", "B ", "BB ", "IBB ", "NB ", "N ", "NBNNN ", "N ", + " ", "BNNNNNN ", "B ", " ", "B ", "B ", "BB ", "NNNNNIN ", "N ", "N ", "N ", "NNN ", "NBNN ", + "IBNN ", "IB ", "IB ", "IB ", "IB ", "N ", "N ", "N ", "BI ", " ", " ", "N ", "I ", "IBB ", + "NN ", "N ", "N ", "Ban ", "BB ", " ", " ", " ", " ", "IB ", "B ", " ", "II ", " ", "BI ", "N ", + "I ", "IB ", "IB ", "IB ", "IB ", "IB ", "IB ", "IB ", "BI ", "BB ", "B ", "B ", "B ", "B ", + "IBB ", "IBN ", "IB ", "B ", "BNBN ", "BBTS ", "N ", " ", "Ian ", "B ", "B ", "B ", "B ", "T ", + "T ", "B ", " ", "I ", " ", " ", "BBI ", "NNBB ", "BBB ", " ", " ", " ", " ", "N ", "N ", " ", + " ", +}; + +static const char *const opcode_arg_table_simon1dos[256] = { + " ", "I ", "I ", "I ", "I ", "I ", "I ", "II ", "II ", "II ", "II ", "B ", "B ", "BN ", "BN ", + "BN ", "BN ", "BB ", "BB ", "BB ", "BB ", "II ", "II ", "N ", "I ", "I ", "I ", "IN ", "IB ", + "II ", "I ", "I ", "II ", "II ", "IBB ", "BIB ", "BB ", "B ", "BI ", "IB ", "B ", "B ", "BN ", + "BN ", "BN ", "BB ", "BB ", "BN ", "BN ", "BB ", "BB ", "BN ", "BB ", "BN ", "B ", "I ", "IB ", + "IB ", "II ", "I ", "I ", "IN ", "B ", "T ", "T ", "NNNNNB ", "BT ", "BT ", "T ", " ", "B ", + "N ", "IBN ", "I ", "I ", "I ", "NN ", " ", " ", "IT ", "II ", "I ", "B ", " ", "IB ", "IBB ", + "IIB ", "T ", " ", " ", "IB ", "IB ", "IB ", "B ", "BB ", "IBB ", "NB ", "N ", "NBNNN ", "N ", + " ", "BNNNNNN ", "B ", " ", "B ", "B ", "BB ", "NNNNNIN ", "N ", "N ", "N ", "NNN ", "NBNN ", + "IBNN ", "IB ", "IB ", "IB ", "IB ", "N ", "N ", "N ", "BI ", " ", " ", "N ", "I ", "IBB ", + "NN ", "N ", "N ", "Ban ", "BB ", " ", " ", " ", " ", "IB ", "B ", " ", "II ", " ", "BI ", "N ", + "I ", "IB ", "IB ", "IB ", "IB ", "IB ", "IB ", "IB ", "BI ", "BB ", "B ", "B ", "B ", "B ", + "IBB ", "IBN ", "IB ", "B ", "BNBN ", "BBT ", "N ", " ", "Ian ", "B ", "B ", "B ", "B ", "T ", + "T ", "B ", " ", "I ", " ", " ", "BBI ", "NNBB ", "BBB ", " ", " ", " ", " ", "N ", "N ", " ", + " ", +}; + +static const char *const opcode_arg_table_simon2win[256] = { + " ", "I ", "I ", "I ", "I ", "I ", "I ", "II ", "II ", "II ", "II ", "B ", "B ", "BN ", "BN ", + "BN ", "BN ", "BB ", "BB ", "BB ", "BB ", "II ", "II ", "N ", "I ", "I ", "I ", "IN ", "IB ", + "II ", "I ", "I ", "II ", "II ", "IBB ", "BIB ", "BB ", "B ", "BI ", "IB ", "B ", "B ", "BN ", + "BN ", "BN ", "BB ", "BB ", "BN ", "BN ", "BB ", "BB ", "BN ", "BB ", "BN ", "B ", "I ", "IB ", + "IB ", "II ", "I ", "I ", "IN ", "B ", "T ", "T ", "NNNNNB ", "BT ", "BTS ", "T ", " ", "B ", + "N ", "IBN ", "I ", "I ", "I ", "NN ", " ", " ", "IT ", "II ", "I ", "B ", " ", "IB ", "IBB ", + "IIB ", "T ", " ", " ", "IB ", "IB ", "IB ", "B ", "BB ", "IBB ", "NB ", "N ", "NNBNNN ", "NN ", + " ", "BNNNNNN ", "B ", " ", "B ", "B ", "BB ", "NNNNNIN ", "N ", "N ", "N ", "NNN ", "NBNN ", + "IBNN ", "IB ", "IB ", "IB ", "IB ", "N ", "N ", "N ", "BI ", " ", " ", "N ", "I ", "IBB ", + "NNB ", "N ", "N ", "Ban ", "BB ", " ", " ", " ", " ", "IB ", "B ", " ", "II ", " ", "BI ", + "N ", "I ", "IB ", "IB ", "IB ", "IB ", "IB ", "IB ", "IB ", "BI ", "BB ", "B ", "B ", "B ", + "B ", "IBB ", "IBN ", "IB ", "B ", "BNBN ", "BBTS ", "N ", " ", "Ian ", "B ", "B ", "B ", "B ", + "T ", "T ", "B ", " ", "I ", " ", " ", "BBI ", "NNBB ", "BBB ", " ", " ", " ", " ", "N ", "N ", + " ", " ", "BT ", " ", "B " +}; + +static const char *const opcode_arg_table_simon2dos[256] = { + " ", "I ", "I ", "I ", "I ", "I ", "I ", "II ", "II ", "II ", "II ", "B ", "B ", "BN ", "BN ", + "BN ", "BN ", "BB ", "BB ", "BB ", "BB ", "II ", "II ", "N ", "I ", "I ", "I ", "IN ", "IB ", + "II ", "I ", "I ", "II ", "II ", "IBB ", "BIB ", "BB ", "B ", "BI ", "IB ", "B ", "B ", "BN ", + "BN ", "BN ", "BB ", "BB ", "BN ", "BN ", "BB ", "BB ", "BN ", "BB ", "BN ", "B ", "I ", "IB ", + "IB ", "II ", "I ", "I ", "IN ", "B ", "T ", "T ", "NNNNNB ", "BT ", "BT ", "T ", " ", "B ", + "N ", "IBN ", "I ", "I ", "I ", "NN ", " ", " ", "IT ", "II ", "I ", "B ", " ", "IB ", "IBB ", + "IIB ", "T ", " ", " ", "IB ", "IB ", "IB ", "B ", "BB ", "IBB ", "NB ", "N ", "NNBNNN ", "NN ", + " ", "BNNNNNN ", "B ", " ", "B ", "B ", "BB ", "NNNNNIN ", "N ", "N ", "N ", "NNN ", "NBNN ", + "IBNN ", "IB ", "IB ", "IB ", "IB ", "N ", "N ", "N ", "BI ", " ", " ", "N ", "I ", "IBB ", + "NNB ", "N ", "N ", "Ban ", "BB ", " ", " ", " ", " ", "IB ", "B ", " ", "II ", " ", "BI ", + "N ", "I ", "IB ", "IB ", "IB ", "IB ", "IB ", "IB ", "IB ", "BI ", "BB ", "B ", "B ", "B ", + "B ", "IBB ", "IBN ", "IB ", "B ", "BNBN ", "BBT ", "N ", " ", "Ian ", "B ", "B ", "B ", "B ", + "T ", "T ", "B ", " ", "I ", " ", " ", "BBI ", "NNBB ", "BBB ", " ", " ", " ", " ", "N ", "N ", + " ", " ", "BT ", " ", "B " +}; + +static const char *const opcode_arg_table_feeblefiles[256] = { + " ", "I ", "I ", "I ", "I ", "I ", "I ", "II ", "II ", "II ", "II ", "B ", "B ", "BN ", "BN ", + "BN ", "BN ", "BB ", "BB ", "BB ", "BB ", "II ", "II ", "N ", "I ", "I ", "I ", "IN ", "IB ", + "II ", "I ", "I ", "II ", "II ", "IBB ", "BIB ", "BB ", "B ", "BI ", "IB ", "B ", "B ", "BN ", + "BN ", "BN ", "BB ", "BB ", "BN ", "BN ", "BB ", "BB ", "BN ", "BB ", "BN ", "B ", "I ", "IB ", + "IB ", "II ", "I ", "I ", "IN ", "B ", "T ", "T ", "NNNNNB ", "BT ", "BTS ", "T ", " ", "B ", + "N ", "IBN ", "I ", "I ", "I ", "NN ", " ", " ", "IT ", "II ", "I ", "B ", " ", "IB ", "IBB ", + "IIB ", "T ", " ", " ", "IB ", "IB ", "IB ", "B ", "BB ", "IBB ", "NB ", "N ", "NNBNNN ", "NN ", + " ", "BNNNNNN ", "B ", " ", "B ", "B ", "BB ", "NNNNNIN ", "N ", "N ", "N ", "NNN ", "NBNN ", + "IBNN ", "IB ", "IB ", "IB ", "IB ", "N ", "N ", "N ", "BI ", " ", " ", "N ", "I ", "IBB ", + "NNB ", "N ", "N ", "Ban ", " ", " ", " ", " ", " ", "IB ", "B ", " ", "II ", " ", "BI ", + "N ", "I ", "IB ", "IB ", "IB ", "IB ", "IB ", "IB ", "IB ", "BI ", "BB ", "B ", "B ", "B ", + "B ", "IBB ", "IBN ", "IB ", "B ", "BNNN ", "BBTS ", "N ", " ", "Ian ", "B ", "B ", "B ", "B ", + "T ", "N ", " ", " ", "I ", " ", " ", "BBI ", "NNBB ", "BBB ", " ", " ", "T ", " ", "N ", "N ", + " ", " ", "BT ", " ", "B ", " ", "BBBB ", " ", " ", "BBBB ", "B ", "B ", "B ", "B " +}; + +static const char *const opcode_arg_table_puzzlepack[256] = { + " ", "I ", "I ", "I ", "I ", "I ", "I ", "II ", "II ", "II ", "II ", "N ", "N ", "NN ", "NN ", + "NN ", "NN ", "NN ", "NN ", "NN ", "NN ", "II ", "II ", "N ", "I ", "I ", "I ", "IN ", "IB ", + "II ", "I ", "I ", "II ", "II ", "IBN ", "NIB ", "NN ", "B ", "BI ", "IN ", "N ", "N ", "NN ", + "NN ", "NN ", "NN ", "NN ", "NN ", "NN ", "NN ", "NN ", "NN ", "NN ", "NN ", "B ", "I ", "IB ", + "IB ", "II ", "I ", "I ", "IN ", "N ", "T ", "T ", "NNNNNB ", "BTNN ", "BTS ", "T ", " ", "B ", + "N ", "IBN ", "I ", "I ", "I ", "NN ", " ", " ", "IT ", "II ", "I ", "B ", " ", "IB ", "IBB ", + "IIB ", "T ", " ", " ", "IB ", "IB ", "IB ", "B ", "BB ", "IBB ", "NB ", "N ", "NNBNNN ", "NN ", + " ", "BNNNNNN ", "B ", " ", "B ", "B ", "BB ", "NNNNNIN ", "N ", "N ", "N ", "NNN ", "NBNN ", + "IBNN ", "IB ", "IB ", "IB ", "IB ", "N ", "N ", "N ", "BI ", " ", " ", "N ", "I ", "IBB ", + "NNB ", "N ", "N ", "Ban ", " ", " ", " ", " ", " ", "IN ", "B ", " ", "II ", " ", "BI ", + "N ", "I ", "IB ", "IB ", "IB ", "IB ", "IB ", "IB ", "IB ", "BI ", "BB ", "N ", "N ", "N ", + "N ", "IBN ", "IBN ", "IN ", "B ", "BNNN ", "BBTS ", "N ", " ", "Ian ", "B ", "B ", "B ", "B ", + "T ", "N ", " ", " ", "I ", " ", " ", "BBI ", "NNBB ", "BBB ", " ", " ", "T ", " ", "N ", "N ", + " ", " ", "BT ", " ", "B ", " ", "BBBB ", " ", " ", "BBBB ", "B ", "B ", "B ", "B " +}; + +uint16 SimonEngine::to16Wrapper(uint value) { + if (getGameType() == GType_FF || getGameType() == GType_PP) + return TO_LE_16(value); + else + return TO_BE_16(value); +} + +uint16 SimonEngine::readUint16Wrapper(const void *src) { + if (getGameType() == GType_FF || getGameType() == GType_PP) + return READ_LE_UINT16(src); + else + return READ_BE_UINT16(src); +} + +uint32 SimonEngine::readUint32Wrapper(const void *src) { + if (getGameType() == GType_FF || getGameType() == GType_PP) + return READ_LE_UINT32(src); + else + return READ_BE_UINT32(src); +} + +void SimonEngine::decompressData(const char *srcName, byte *dst, uint32 offset, uint32 srcSize, uint32 dstSize) { +#ifdef USE_ZLIB + File in; + in.open(srcName); + if (in.isOpen() == false) + error("decompressData: Can't load %s", srcName); + + in.seek(offset, SEEK_SET); + if (srcSize != dstSize) { + byte *srcBuffer = (byte *)malloc(srcSize); + + if (in.read(srcBuffer, srcSize) != srcSize) + error("decompressData: Read failed"); + + unsigned long decompressedSize = dstSize; + int result = uncompress(dst, &decompressedSize, srcBuffer, srcSize); + if (result != Z_OK) + error("decompressData: Zlib uncompress error"); + free(srcBuffer); + } else { + if (in.read(dst, dstSize) != dstSize) + error("decompressData: Read failed"); + } + in.close(); +#else + error("Zlib support is required for Amiga and Macintosh versions"); +#endif +} + +void SimonEngine::loadOffsets(const char *filename, int number, uint32 &file, uint32 &offset, uint32 &srcSize, uint32 &dstSize) { + Common::File in; + + int offsSize = (getPlatform() == Common::kPlatformAmiga) ? 16 : 12; + + /* read offsets from index */ + in.open(filename); + if (in.isOpen() == false) { + error("loadOffsets: Can't load index file '%s'", filename); + } + + in.seek(number * offsSize, SEEK_SET); + offset = in.readUint32LE(); + dstSize = in.readUint32LE(); + srcSize = in.readUint32LE(); + file = in.readUint32LE(); + in.close(); +} + +int SimonEngine::allocGamePcVars(File *in) { + uint item_array_size, item_array_inited, stringtable_num; + uint32 version; + uint i, start; + + item_array_size = in->readUint32BE(); + version = in->readUint32BE(); + item_array_inited = in->readUint32BE(); + stringtable_num = in->readUint32BE(); + + if (getGameType() == GType_ELVIRA || getGameType() == GType_ELVIRA2) { + item_array_inited = item_array_size; + start = 0; + } else { + item_array_inited += 2; // first two items are predefined + item_array_size += 2; + start = 1; + } + + if (version != 0x80) + error("allocGamePcVars: Not a runtime database"); + + _itemArrayPtr = (Item **)calloc(item_array_size, sizeof(Item *)); + if (_itemArrayPtr == NULL) + error("allocGamePcVars: Out of memory for Item array"); + + _itemArraySize = item_array_size; + _itemArrayInited = item_array_inited; + + for (i = start; i < item_array_inited; i++) { + _itemArrayPtr[i] = (Item *)allocateItem(sizeof(Item)); + } + + // The rest is cleared automatically by calloc + allocateStringTable(stringtable_num + 10); + _stringTabNum = stringtable_num; + + return item_array_inited; +} + +void SimonEngine::loadGamePcFile() { + Common::File in; + int num_inited_objects; + int i, file_size; + + /* read main gamepc file */ + in.open(getFileName(GAME_BASEFILE)); + if (in.isOpen() == false) { + error("loadGamePcFile: Can't load gamepc file '%s'", getFileName(GAME_BASEFILE)); + } + + num_inited_objects = allocGamePcVars(&in); + + createPlayer(); + readGamePcText(&in); + + int start; + if (getGameType() == GType_ELVIRA || getGameType() == GType_ELVIRA2) { + start = 0; + } else { + start = 2; + } + + for (i = start; i < num_inited_objects; i++) { + readItemFromGamePc(&in, _itemArrayPtr[i]); + } + + readSubroutineBlock(&in); + + in.close(); + + if (getGameType() == GType_PP) + return; + + /* Read list of TABLE resources */ + in.open(getFileName(GAME_TBLFILE)); + if (in.isOpen() == false) { + error("loadGamePcFile: Can't load table resources file '%s'", getFileName(GAME_TBLFILE)); + } + + file_size = in.size(); + + _tblList = (byte *)malloc(file_size); + if (_tblList == NULL) + error("loadGamePcFile: Out of memory for strip table list"); + in.read(_tblList, file_size); + in.close(); + + /* Remember the current state */ + _subroutineListOrg = _subroutineList; + _tablesHeapPtrOrg = _tablesHeapPtr; + _tablesHeapCurPosOrg = _tablesHeapCurPos; + + if (getGameType() == GType_ELVIRA || getGameType() == GType_FF) + return; + + /* Read list of TEXT resources */ + in.open(getFileName(GAME_STRFILE)); + if (in.isOpen() == false) + error("loadGamePcFile: Can't load text resources file '%s'", getFileName(GAME_STRFILE)); + + file_size = in.size(); + _strippedTxtMem = (byte *)malloc(file_size); + if (_strippedTxtMem == NULL) + error("loadGamePcFile: Out of memory for strip text list"); + in.read(_strippedTxtMem, file_size); + in.close(); + + if (getGameType() != GType_WW) + return; + + /* Read list of ROOM ITEMS resources */ + in.open(getFileName(GAME_RMSLFILE)); + if (in.isOpen() == false) { + error("loadGamePcFile: Can't load room resources file '%s'", getFileName(GAME_XTBLFILE)); + } + + file_size = in.size(); + + _roomsList = (byte *)malloc(file_size); + if (_roomsList == NULL) + error("loadGamePcFile: Out of memory for room items list"); + in.read(_roomsList, file_size); + in.close(); + + /* Read list of XTABLE resources */ + in.open(getFileName(GAME_XTBLFILE)); + if (in.isOpen() == false) { + error("loadGamePcFile: Can't load xtable resources file '%s'", getFileName(GAME_XTBLFILE)); + } + + file_size = in.size(); + + _xtblList = (byte *)malloc(file_size); + if (_xtblList == NULL) + error("loadGamePcFile: Out of memory for strip xtable list"); + in.read(_xtblList, file_size); + in.close(); + + /* Remember the current state */ + _xsubroutineListOrg = _subroutineList; + _xtablesHeapPtrOrg = _tablesHeapPtr; + _xtablesHeapCurPosOrg = _tablesHeapCurPos; + +} + +void SimonEngine::readGamePcText(Common::File *in) { + _textSize = in->readUint32BE(); + _textMem = (byte *)malloc(_textSize); + if (_textMem == NULL) + error("readGamePcText: Out of text memory"); + + in->read(_textMem, _textSize); + + setupStringTable(_textMem, _stringTabNum); +} + +void SimonEngine::readItemFromGamePc(Common::File *in, Item *item) { + uint32 type; + + if (getGameType() == GType_ELVIRA || getGameType() == GType_ELVIRA2) { + item->itemName = (uint16)in->readUint32BE(); + item->adjective = in->readUint16BE(); + item->noun = in->readUint16BE(); + item->state = in->readUint16BE(); + in->readUint16BE(); + item->sibling = (uint16)fileReadItemID(in); + item->child = (uint16)fileReadItemID(in); + item->parent = (uint16)fileReadItemID(in); + in->readUint16BE(); + in->readUint16BE(); + in->readUint16BE(); + item->classFlags = in->readUint16BE(); + item->children = NULL; + } else { + item->adjective = in->readUint16BE(); + item->noun = in->readUint16BE(); + item->state = in->readUint16BE(); + item->sibling = (uint16)fileReadItemID(in); + item->child = (uint16)fileReadItemID(in); + item->parent = (uint16)fileReadItemID(in); + in->readUint16BE(); + item->classFlags = in->readUint16BE(); + item->children = NULL; + } + + + type = in->readUint32BE(); + while (type) { + type = in->readUint16BE(); + if (type != 0) + readItemChildren(in, item, type); + } +} + +void SimonEngine::readItemChildren(Common::File *in, Item *item, uint type) { + if (type == 1) { + if (getGameType() == GType_ELVIRA || getGameType() == GType_ELVIRA2) { + // FIXME + in->readUint32BE(); + in->readUint32BE(); + in->readUint16BE(); + } else { + uint fr1 = in->readUint16BE(); + uint fr2 = in->readUint16BE(); + uint i, size; + uint j, k; + SubRoom *subRoom; + + size = SubRoom_SIZE; + for (i = 0, j = fr2; i != 6; i++, j >>= 2) + if (j & 3) + size += sizeof(subRoom->roomExit[0]); + + subRoom = (SubRoom *)allocateChildBlock(item, 1, size); + subRoom->subroutine_id = fr1; + subRoom->roomExitStates = fr2; + + for (i = k = 0, j = fr2; i != 6; i++, j >>= 2) + if (j & 3) + subRoom->roomExit[k++] = (uint16)fileReadItemID(in); + } + } else if (type == 2) { + uint32 fr = in->readUint32BE(); + uint i, k, size; + SubObject *subObject; + + size = SubObject_SIZE; + for (i = 0; i != 16; i++) + if (fr & (1 << i)) + size += sizeof(subObject->objectFlagValue[0]); + + subObject = (SubObject *)allocateChildBlock(item, 2, size); + subObject->objectFlags = fr; + + k = 0; + if (fr & 1) { + subObject->objectFlagValue[k++] = (uint16)in->readUint32BE(); + } + for (i = 1; i != 16; i++) + if (fr & (1 << i)) + subObject->objectFlagValue[k++] = in->readUint16BE(); + + subObject->objectName = (uint16)in->readUint32BE(); + } else if (type == 4) { + // FIXME + fileReadItemID(in); + fileReadItemID(in); + fileReadItemID(in); + fileReadItemID(in); + fileReadItemID(in); + fileReadItemID(in); + fileReadItemID(in); + fileReadItemID(in); + fileReadItemID(in); + fileReadItemID(in); + fileReadItemID(in); + fileReadItemID(in); + } else if (type == 7) { + // FIXME + in->readUint16BE(); + in->readUint16BE(); + } else if (type == 8) { + SubUserChain *chain = (SubUserChain *)allocateChildBlock(item, 8, sizeof(SubUserChain)); + chain->chChained = (uint16)fileReadItemID(in); + } else if (type == 9) { + setUserFlag(item, 0, in->readUint16BE()); + setUserFlag(item, 1, in->readUint16BE()); + setUserFlag(item, 2, in->readUint16BE()); + setUserFlag(item, 3, in->readUint16BE()); + if (getGameType() == GType_ELVIRA || getGameType() == GType_ELVIRA2) { + setUserFlag(item, 4, in->readUint16BE()); + setUserFlag(item, 5, in->readUint16BE()); + setUserFlag(item, 6, in->readUint16BE()); + setUserFlag(item, 7, in->readUint16BE()); + // FIXME + fileReadItemID(in); + fileReadItemID(in); + fileReadItemID(in); + fileReadItemID(in); + } + } else if (type == 255) { + SubUserInherit *inherit = (SubUserInherit *)allocateChildBlock(item, 255, sizeof(SubUserInherit)); + inherit->inMaster = (uint16)fileReadItemID(in); + } else { + error("readItemChildren: invalid type %d", type); + } +} + +uint fileReadItemID(Common::File *in) { + uint32 val = in->readUint32BE(); + if (val == 0xFFFFFFFF) + return 0; + return val + 2; +} + +byte *SimonEngine::readSingleOpcode(Common::File *in, byte *ptr) { + int i, l; + const char *string_ptr; + uint opcode, val; + + const char *const *table; + + if (getGameType() == GType_PP) + table = opcode_arg_table_puzzlepack; + else if (getGameType() == GType_FF) + table = opcode_arg_table_feeblefiles; + else if (getGameType() == GType_SIMON2 && (getFeatures() & GF_TALKIE)) + table = opcode_arg_table_simon2win; + else if (getGameType() == GType_SIMON2) + table = opcode_arg_table_simon2dos; + else if (getGameType() == GType_SIMON1 && (getFeatures() & GF_TALKIE)) + table = opcode_arg_table_simon1win; + else if (getGameType() == GType_SIMON1) + table = opcode_arg_table_simon1dos; + else if (getGameType() == GType_WW) + table = opcode_arg_table_waxworks; + else + table = opcode_arg_table_elvira; + + i = 0; + if (getGameType() == GType_ELVIRA || getGameType() == GType_ELVIRA2) { + opcode = READ_BE_UINT16(ptr); + ptr += 2; + } else { + opcode = *ptr++; + } + + string_ptr = table[opcode]; + if (!string_ptr) + error("Unable to locate opcode table. Perhaps you are using the wrong game target?"); + + for (;;) { + if (string_ptr[i] == ' ') + return ptr; + + l = string_ptr[i++]; + + switch (l) { + case 'F': + case 'N': + case 'S': + case 'a': + case 'n': + case 'p': + case 'v': + case '3': + val = in->readUint16BE(); + *ptr++ = val >> 8; + *ptr++ = val & 255; + break; + + case 'B': + if (getGameType() == GType_ELVIRA || getGameType() == GType_ELVIRA2) { + val = in->readUint16BE(); + *ptr++ = val >> 8; + *ptr++ = val & 255; + } else { + *ptr++ = in->readByte(); + if (ptr[-1] == 0xFF) { + *ptr++ = in->readByte(); + } + } + break; + + case 'I': + val = in->readUint16BE(); + switch (val) { + case 1: + val = 0xFFFF; + break; + case 3: + val = 0xFFFD; + break; + case 5: + val = 0xFFFB; + break; + case 7: + val = 0xFFF9; + break; + case 9: + val = 0xFFF7; + break; + default: + val = fileReadItemID(in);; + } + *ptr++ = val >> 8; + *ptr++ = val & 255; + break; + + case 'T': + val = in->readUint16BE(); + switch (val) { + case 0: + val = 0xFFFF; + break; + case 3: + val = 0xFFFD; + break; + default: + val = (uint16)in->readUint32BE(); + break; + } + *ptr++ = val >> 8; + *ptr++ = val & 255; + break; + default: + error("readSingleOpcode: Bad cmd table entry %c", l); + } + } +} + +void SimonEngine::openGameFile() { + if (!(getFeatures() & GF_OLD_BUNDLE)) { + _gameFile = new File(); + _gameFile->open(getFileName(GAME_GMEFILE)); + + if (_gameFile->isOpen() == false) + error("openGameFile: Can't load game file '%s'", getFileName(GAME_GMEFILE)); + + uint32 size = _gameFile->readUint32LE(); + + _gameOffsetsPtr = (uint32 *)malloc(size); + if (_gameOffsetsPtr == NULL) + error("openGameFile: Out of memory, game offsets"); + + readGameFile(_gameOffsetsPtr, 0, size); +#if defined(SCUMM_BIG_ENDIAN) + for (uint r = 0; r < size / 4; r++) + _gameOffsetsPtr[r] = FROM_LE_32(_gameOffsetsPtr[r]); +#endif + } +} + +void SimonEngine::readGameFile(void *dst, uint32 offs, uint32 size) { + _gameFile->seek(offs, SEEK_SET); + if (_gameFile->read(dst, size) != size) + error("readGameFile: Read failed (%d,%d)", offs, size); +} + +// Thanks to Stuart Caie for providing the original +// C conversion upon which this decruncher is based. + +#define SD_GETBIT(var) do { \ + if (!bits--) { \ + s -= 4; \ + if (s < src) \ + return false; \ + bb = READ_BE_UINT32(s); \ + bits = 31; \ + } \ + (var) = bb & 1; \ + bb >>= 1; \ +}while (0) + +#define SD_GETBITS(var, nbits) do { \ + bc = (nbits); \ + (var) = 0; \ + while (bc--) { \ + (var) <<= 1; \ + SD_GETBIT(bit); \ + (var) |= bit; \ + } \ +}while (0) + +#define SD_TYPE_LITERAL (0) +#define SD_TYPE_MATCH (1) + +static bool decrunchFile(byte *src, byte *dst, uint32 size) { + byte *s = src + size - 4; + uint32 destlen = READ_BE_UINT32 (s); + uint32 bb, x, y; + byte *d = dst + destlen; + byte bc, bit, bits, type; + + // Initialize bit buffer. + s -= 4; + bb = x = READ_BE_UINT32 (s); + bits = 0; + do { + x >>= 1; + bits++; + } while (x); + bits--; + + while (d > dst) { + SD_GETBIT(x); + if (x) { + SD_GETBITS(x, 2); + switch (x) { + case 0: + type = SD_TYPE_MATCH; + x = 9; + y = 2; + break; + + case 1: + type = SD_TYPE_MATCH; + x = 10; + y = 3; + break; + + case 2: + type = SD_TYPE_MATCH; + x = 12; + SD_GETBITS(y, 8); + break; + + default: + type = SD_TYPE_LITERAL; + x = 8; + y = 8; + } + } else { + SD_GETBIT(x); + if (x) { + type = SD_TYPE_MATCH; + x = 8; + y = 1; + } else { + type = SD_TYPE_LITERAL; + x = 3; + y = 0; + } + } + + if (type == SD_TYPE_LITERAL) { + SD_GETBITS(x, x); + y += x; + if ((int)(y + 1) > (d - dst)) + return false; // Overflow? + do { + SD_GETBITS(x, 8); + *--d = x; + } while (y-- > 0); + } else { + if ((int)(y + 1) > (d - dst)) + return false; // Overflow? + SD_GETBITS(x, x); + if ((d + x) > (dst + destlen)) + return false; // Offset overflow? + do { + d--; + *d = d[x]; + } while (y-- > 0); + } + } + + // Successful decrunch. + return true; +} + +#undef SD_GETBIT +#undef SD_GETBITS +#undef SD_TYPE_LITERAL +#undef SD_TYPE_MATCH + +void SimonEngine::loadSimonVGAFile(uint vga_id) { + uint32 offs, size; + + if (getFeatures() & GF_OLD_BUNDLE) { + File in; + char filename[15]; + if (vga_id == 23) + vga_id = 112; + if (vga_id == 328) + vga_id = 119; + + if (getPlatform() == Common::kPlatformAmiga) { + if (getFeatures() & GF_TALKIE) + sprintf(filename, "0%d.out", vga_id); + else + sprintf(filename, "0%d.pkd", vga_id); + } else { + sprintf(filename, "0%d.VGA", vga_id); + } + + in.open(filename); + if (in.isOpen() == false) + error("loadSimonVGAFile: Can't load %s", filename); + + size = in.size(); + if (getFeatures() & GF_CRUNCHED) { + byte *srcBuffer = (byte *)malloc(size); + if (in.read(srcBuffer, size) != size) + error("loadSimonVGAFile: Read failed"); + decrunchFile(srcBuffer, _vgaBufferPointers[11].vgaFile2, size); + free(srcBuffer); + } else { + if (in.read(_vgaBufferPointers[11].vgaFile2, size) != size) + error("loadSimonVGAFile: Read failed"); + } + in.close(); + } else { + offs = _gameOffsetsPtr[vga_id]; + + size = _gameOffsetsPtr[vga_id + 1] - offs; + readGameFile(_vgaBufferPointers[11].vgaFile2, offs, size); + } +} + +byte *SimonEngine::loadVGAFile(uint id, uint type, uint32 &dstSize) { + File in; + char filename[15]; + byte *dst = NULL; + uint32 file, offs, srcSize; + uint extraBuffer = 0; + + if (getGameType() == GType_SIMON1 || getGameType() == GType_SIMON2) { + // !!! HACK !!! + // Allocate more space for text to cope with foreign languages that use + // up more space than english. I hope 6400 bytes are enough. This number + // is base on: 2 (lines) * 320 (screen width) * 10 (textheight) -- olki + extraBuffer = (id == 5 ? 6400 : 0); + } + + if (getFeatures() & GF_ZLIBCOMP) { + if (getPlatform() == Common::kPlatformAmiga) { + loadOffsets((const char*)"gfxindex.dat", id / 2 * 3 + type, file, offs, srcSize, dstSize); + } else { + loadOffsets((const char*)"graphics.vga", id / 2 * 3 + type, file, offs, srcSize, dstSize); + } + + if (getPlatform() == Common::kPlatformAmiga) + sprintf(filename, "GFX%d.VGA", file); + else + sprintf(filename, "graphics.vga"); + + dst = allocBlock(dstSize + extraBuffer); + decompressData(filename, dst, offs, srcSize, dstSize); + } else if (getFeatures() & GF_OLD_BUNDLE) { + if (getPlatform() == Common::kPlatformAmiga) { + if (getFeatures() & GF_TALKIE) + sprintf(filename, "%.3d%d.out", id / 2, type); + else + sprintf(filename, "%.3d%d.pkd", id / 2, type); + } else { + if (getGameType() == GType_WW) { + sprintf(filename, "%.2d%d.VGA", id / 2, type); + } else { + sprintf(filename, "%.3d%d.VGA", id / 2, type); + } + } + + in.open(filename); + if (in.isOpen() == false) { + if (type == 3) + return NULL; + else + error("loadVGAFile: Can't load %s", filename); + } + + dstSize = srcSize = in.size(); + if (getFeatures() & GF_CRUNCHED) { + byte *srcBuffer = (byte *)malloc(srcSize); + if (in.read(srcBuffer, srcSize) != srcSize) + error("loadVGAFile: Read failed"); + + dstSize = READ_BE_UINT32(srcBuffer + srcSize - 4); + dst = allocBlock (dstSize + extraBuffer); + decrunchFile(srcBuffer, dst, srcSize); + free(srcBuffer); + } else { + dst = allocBlock(dstSize + extraBuffer); + if (in.read(dst, dstSize) != dstSize) + error("loadVGAFile: Read failed"); + } + in.close(); + } else { + offs = _gameOffsetsPtr[id]; + + dstSize = _gameOffsetsPtr[id + 1] - offs; + dst = allocBlock(dstSize + extraBuffer); + readGameFile(dst, offs, dstSize); + } + + dstSize += extraBuffer; + return dst; +} + +static const char *dimpSoundList[32] = { + "Beep", + "Birth", + "Boiling", + "Burp", + "Cough", + "Die1", + "Die2", + "Fart", + "Inject", + "Killchik", + "Puke", + "Lights", + "Shock", + "Snore", + "Snotty", + "Whip", + "Whistle", + "Work1", + "Work2", + "Yawn", + "And0w", + "And0x", + "And0y", + "And0z", + "And10", + "And11", + "And12", + "And13", + "And14", + "And15", + "And16", + "And17", +}; + + +void SimonEngine::loadSound(uint sound, int pan, int vol, uint type) { + byte *dst; + + if (getGameId() == GID_DIMP) { + File in; + char filename[15]; + + assert(sound >= 1 && sound <= 32); + sprintf(filename, "%s.wav", dimpSoundList[sound - 1]); + + in.open(filename); + if (in.isOpen() == false) + error("loadSound: Can't load %s", filename); + + uint32 dstSize = in.size(); + dst = (byte *)malloc(dstSize); + if (in.read(dst, dstSize) != dstSize) + error("loadSound: Read failed"); + in.close(); + } else if (getFeatures() & GF_ZLIBCOMP) { + char filename[15]; + + uint32 file, offset, srcSize, dstSize; + if (getPlatform() == Common::kPlatformAmiga) { + loadOffsets((const char*)"sfxindex.dat", _zoneNumber * 22 + sound, file, offset, srcSize, dstSize); + } else { + loadOffsets((const char*)"effects.wav", _zoneNumber * 22 + sound, file, offset, srcSize, dstSize); + } + + if (getPlatform() == Common::kPlatformAmiga) + sprintf(filename, "sfx%d.wav", file); + else + sprintf(filename, "effects.wav"); + + dst = (byte *)malloc(dstSize); + decompressData(filename, dst, offset, srcSize, dstSize); + } else { + if (!_curSfxFile) + error("loadSound: Can't load sound data file '%d3.VGA'", _zoneNumber); + + dst = _curSfxFile + READ_LE_UINT32(_curSfxFile + sound * 4); + } + + if (type == 3) + _sound->playSfx5Data(dst, sound, pan, vol); + else if (type == 2) + _sound->playAmbientData(dst, sound, pan, vol); + else + _sound->playSfxData(dst, sound, pan, vol); +} + +void SimonEngine::loadVoice(uint speechId) { + if (getGameType() == GType_PP && speechId == 99) + return; + + if (getFeatures() & GF_ZLIBCOMP) { + char filename[15]; + + uint32 file, offset, srcSize, dstSize; + if (getPlatform() == Common::kPlatformAmiga) { + loadOffsets((const char*)"spindex.dat", speechId, file, offset, srcSize, dstSize); + } else { + loadOffsets((const char*)"speech.wav", speechId, file, offset, srcSize, dstSize); + } + + // Voice segment doesn't exist + if (offset == 0xFFFFFFFF && srcSize == 0xFFFFFFFF && dstSize == 0xFFFFFFFF) { + debug(0, "loadVoice: speechId %d removed", speechId); + return; + } + + if (getPlatform() == Common::kPlatformAmiga) + sprintf(filename, "sp%d.wav", file); + else + sprintf(filename, "speech.wav"); + + byte *dst = (byte *)malloc(dstSize); + decompressData(filename, dst, offset, srcSize, dstSize); + _sound->playVoiceData(dst, speechId); + } else { + _sound->playVoice(speechId); + } +} + +} // End of namespace Simon diff --git a/engines/agos/rooms.cpp b/engines/agos/rooms.cpp new file mode 100644 index 0000000000..ca2b84e9f9 --- /dev/null +++ b/engines/agos/rooms.cpp @@ -0,0 +1,114 @@ +/* ScummVM - Scumm Interpreter + * Copyright (C) 2001 Ludvig Strigeus + * Copyright (C) 2001-2006 The ScummVM project + * + * 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. + * + * $URL$ + * $Id$ + * + */ + +#include "common/stdafx.h" + +#include "agos/agos.h" +#include "agos/intern.h" + +using Common::File; + +namespace Simon { + +uint16 SimonEngine::getDoorState(Item *item, uint16 d) { + uint16 mask = 3; + uint16 n; + + SubRoom *subRoom = (SubRoom *)findChildOfType(item, 1); + if (subRoom == NULL) + return 0; + + d <<= 1; + mask <<= d; + n = subRoom->roomExitStates & mask; + n >>= d; + + return n; +} + +uint16 SimonEngine::getExitOf(Item *item, uint16 d) { + uint16 x; + uint16 y = 0; + + SubRoom *subRoom = (SubRoom *)findChildOfType(item, 1); + if (subRoom == NULL) + return 0; + x = d; + while (x > y) { + if (getDoorState(item, y) == 0) + d--; + y++; + } + return subRoom->roomExit[d]; +} + +bool SimonEngine::loadRoomItems(uint item) { + byte *p; + uint i, min_num, max_num; + char filename[30]; + File in; + + p = _roomsList; + if (p == NULL) + return 0; + + while (*p) { + for (i = 0; *p; p++, i++) + filename[i] = *p; + filename[i] = 0; + p++; + + for (;;) { + min_num = (p[0] * 256) | p[1]; + p += 2; + + if (min_num == 0) + break; + + max_num = (p[0] * 256) | p[1]; + p += 2; + + if (item >= min_num && item <= max_num) { + + in.open(filename); + if (in.isOpen() == false) { + error("loadRoomItems: Can't load rooms file '%s'", filename); + } + + for (i = min_num; i <= max_num; i++) { + _itemArrayPtr[i] = (Item *)allocateItem(sizeof(Item)); + in.readUint16BE(); + readItemFromGamePc(&in, _itemArrayPtr[i]); + } + in.close(); + + return 1; + } + } + } + + debug(1,"loadRoomItems: didn't find %d", item); + return 0; +} + +} // End of namespace Simon diff --git a/engines/agos/saveload.cpp b/engines/agos/saveload.cpp new file mode 100644 index 0000000000..37907024b6 --- /dev/null +++ b/engines/agos/saveload.cpp @@ -0,0 +1,821 @@ +/* ScummVM - Scumm Interpreter + * Copyright (C) 2001 Ludvig Strigeus + * Copyright (C) 2001-2006 The ScummVM project + * + * 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. + * + * $URL$ + * $Id$ + * + */ + +#include "common/stdafx.h" + +#include "common/savefile.h" +#include "common/system.h" + +#include "gui/about.h" +#include "gui/message.h" + +#include "agos/agos.h" +#include "agos/intern.h" + +namespace Simon { + +int SimonEngine::countSaveGames() { + Common::InSaveFile *f; + uint i = 1; + bool marks[256]; + + char *prefix = genSaveName(999); + prefix[strlen(prefix)-3] = '\0'; + _saveFileMan->listSavefiles(prefix, marks, 256); + + while (i < 256) { + if (marks[i] && + (f = _saveFileMan->openForLoading(genSaveName(i)))) { + i++; + delete f; + } else + break; + } + return i; +} + +int SimonEngine::displaySaveGameList(int curpos, bool load, char *dst) { + int slot, last_slot; + Common::InSaveFile *in; + + showMessageFormat("\xC"); + + memset(dst, 0, 108); + + slot = curpos; + + while (curpos + 6 > slot) { + if (!(in = _saveFileMan->openForLoading(genSaveName(slot)))) + break; + + in->read(dst, 18); + delete in; + + last_slot = slot; + if (slot < 10) { + showMessageFormat(" "); + } else if (_language == Common::HB_ISR) { + last_slot = (slot % 10) * 10; + last_slot += slot / 10; + } + + if (_language == Common::HB_ISR && !(slot % 10)) + showMessageFormat("0"); + showMessageFormat("%d", last_slot); + showMessageFormat(".%s\n", dst); + dst += 18; + slot++; + } + // while_break + if (!load) { + if (curpos + 6 == slot) + slot++; + else { + if (slot < 10) + showMessageFormat(" "); + showMessageFormat("%d.\n", slot); + } + } else { + if (curpos + 6 == slot) { + if ((in = _saveFileMan->openForLoading(genSaveName(slot)))) { + slot++; + delete in; + } + } + } + + return slot - curpos; +} + +char *SimonEngine::genSaveName(int slot) { + static char buf[15]; + + if (getGameType() == GType_FF) { + if (slot == 999) { + // Restart state + if (getPlatform() == Common::kPlatformWindows) + sprintf(buf, "save.%.3d", slot); + else + sprintf(buf, "setup"); + } else { + sprintf(buf, "feeble.%.3d", slot); + } + } else if (getGameType() == GType_SIMON2) { + sprintf(buf, "simon2.%.3d", slot); + } else { + sprintf(buf, "simon1.%.3d", slot); + } + return buf; +} + +void SimonEngine::quickLoadOrSave() { + // The demo of Simon 1 (DOS Floppy) is missing too many segments + // and the Feeble Files doesn't always allow a load or save + if (getGameId() == GID_SIMON1DEMO || getGameType() == GType_FF) + return; + + bool success; + char buf[50]; + + char *filename = genSaveName(_saveLoadSlot); + if (_saveLoadType == 2) { + Subroutine *sub; + success = loadGame(_saveLoadSlot); + if (!success) { + sprintf(buf, "Failed to load game state to file:\n\n%s", filename); + } else { + // Redraw Inventory + mouseOff(); + drawIconArray(2, me(), 0, 0); + mouseOn(); + // Reset engine? + setBitFlag(97, true); + sub = getSubroutineByID(100); + startSubroutine(sub); + } + } else { + success = saveGame(_saveLoadSlot, _saveLoadName); + if (!success) + sprintf(buf, "Failed to save game state to file:\n\n%s", filename); + } + + if (!success) { + GUI::MessageDialog dialog(buf, "OK"); + dialog.runModal(); + + } else if (_saveLoadType == 1) { + sprintf(buf, "Successfully saved game state in file:\n\n%s", filename); + GUI::TimedMessageDialog dialog(buf, 1500); + dialog.runModal(); + + } + + _saveLoadType = 0; +} + +void SimonEngine::listSaveGames(char *buf) { + int i; + + disableFileBoxes(); + + i = displaySaveGameList(_saveLoadRowCurPos, _saveOrLoad, buf); + + _saveDialogFlag = true; + + if (i != 7) { + i++; + if (!_saveOrLoad) + i++; + _saveDialogFlag = false; + } + + if (!--i) + return; + + do { + enableBox(208 + i - 1); + } while (--i); +} + + +const byte hebrewKeyTable[96] = { + 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 90, 45, 85, 47, 48, 49, 50, + 51, 52, 53, 54, 55, 56, 57, 83, 83, 90, 61, 85, 63, 35, 89, 80, 65, 66, 87, + 75, 82, 73, 79, 71, 76, 74, 86, 78, 77, 84, 47, 88, 67, 64, 69, 68, 44, 81, + 72, 70, 91, 92, 93, 94, 95, 96, 89, 80, 65, 66, 87, 75, 82, 73, 79, 71, 76, + 74, 86, 78, 77, 84, 47, 88, 67, 64, 69, 68, 44, 81, 72, 70, + 123, 124, 125, 126, 127, +}; + +void SimonEngine::userGame(bool load) { + time_t save_time; + int number_of_savegames; + int i, name_len, result; + WindowBlock *window; + char *name; + bool b; + char buf[108]; + int maxChar = (_language == Common::HB_ISR) ? 155: 128; + + _saveOrLoad = load; + + save_time = time(NULL); + + _copyPartialMode = 1; + + number_of_savegames = countSaveGames(); + if (!load) + number_of_savegames++; + number_of_savegames -= 6; + if (number_of_savegames < 0) + number_of_savegames = 0; + number_of_savegames++; + _numSaveGameRows = number_of_savegames; + + _saveLoadRowCurPos = 1; + if (!load) + _saveLoadRowCurPos = number_of_savegames; + + _saveLoadEdit = false; + +restart:; + do { + i = userGameGetKey(&b, buf); + } while (!b); + + if (i == 205) + goto get_out; + if (!load) { + // if_1 + if_1:; + result = i; + + disableBox(208 + i); + leaveHitAreaById(208 + i); + + window = _windowArray[5]; + + window->textRow = result; + + // init x offset with a 2 character savegame number + a period (18 pix) + if (_language == Common::HB_ISR) { + window->textColumn = 3; + window->textColumnOffset = 6; + window->textLength = 3; + } else { + window->textColumn = 2; + window->textColumnOffset = 2; + window->textLength = 3; + } + + name = buf + i * 18; + + // now process entire savegame name to get correct x offset for cursor + name_len = 0; + while (name[name_len]) { + if (_language == Common::HB_ISR) { + byte width = 6; + if (name[name_len] >= 64 && name[name_len] < 91) + width = _hebrewCharWidths [name[name_len] - 64]; + window->textLength++; + window->textColumnOffset -= width; + if (window->textColumnOffset < width) { + window->textColumnOffset += 8; + window->textColumn++; + } + } else { + window->textLength++; + window->textColumnOffset += 6; + if (name[name_len] == 'i' || name[name_len] == 'l') + window->textColumnOffset -= 2; + if (window->textColumnOffset >= 8) { + window->textColumnOffset -= 8; + window->textColumn++; + } + } + name_len++; + } + // while_1_end + + // do_3_start + for (;;) { + windowPutChar(window, 0x7f); + + _saveLoadEdit = true; + + // do_2 + do { + i = userGameGetKey(&b, buf); + + if (b) { + if (i == 205) + goto get_out; + enableBox(208 + result); + if (_saveLoadEdit) { + userGameBackSpace(_windowArray[5], 8); + } + goto if_1; + } + + // is_not_b + if (!_saveLoadEdit) { + enableBox(208 + result); + goto restart; + } + } while (i >= maxChar || i == 0); + + if (_language == Common::HB_ISR) { + if (i >= 128) + i -= 64; + else if (i >= 32) + i = hebrewKeyTable[i - 32]; + } + + // after_do_2 + userGameBackSpace(_windowArray[5], 8); + if (i == 10 || i == 13) + break; + if (i == 8) { + // do_backspace + if (name_len != 0) { + int x; + byte m; + + name_len--; + m = name[name_len]; + + if (_language == Common::HB_ISR) + x = 8; + else + x = (name[name_len] == 'i' || name[name_len] == 'l') ? 1 : 8; + + name[name_len] = 0; + + userGameBackSpace(_windowArray[5], x, m); + } + } else if (i >= 32 && name_len != 17) { + name[name_len++] = i; + + windowPutChar(_windowArray[5], i); + } + } + + // do_save + if (!saveGame(_saveLoadRowCurPos + result, buf + result * 18)) + fileError(_windowArray[5], true); + } else { + if (!loadGame(_saveLoadRowCurPos + i)) + fileError(_windowArray[5], false); + } + +get_out:; + disableFileBoxes(); + + _gameStoppedClock = time(NULL) - save_time + _gameStoppedClock; + _copyPartialMode = 0; + + restoreBlock(94, 208, 46, 80); + + i = _timer4; + do { + delay(10); + } while (i == _timer4); +} + +int SimonEngine::userGameGetKey(bool *b, char *buf) { + HitArea *ha; + *b = true; + + if (!_saveLoadEdit) { + listSaveGames(buf); + } + + _keyPressed = 0; + + for (;;) { + _lastHitArea = NULL; + _lastHitArea3 = NULL; + + do { + if (_saveLoadEdit && _keyPressed != 0) { + *b = false; + return _keyPressed; + } + delay(10); + } while (_lastHitArea3 == 0); + + ha = _lastHitArea; + + if (ha == NULL || ha->id < 205) { + } else if (ha->id == 205) { + return ha->id; + } else if (ha->id == 206) { + if (_saveLoadRowCurPos != 1) { + if (_saveLoadRowCurPos < 7) + _saveLoadRowCurPos = 1; + else + _saveLoadRowCurPos -= 6; + + _saveLoadEdit = false; + listSaveGames(buf); + } + } else if (ha->id == 207) { + if (_saveDialogFlag) { + _saveLoadRowCurPos += 6; + if (_saveLoadRowCurPos >= _numSaveGameRows) + _saveLoadRowCurPos = _numSaveGameRows; + + _saveLoadEdit = false; + listSaveGames(buf); + } + } else if (ha->id < 214) { + return ha->id - 208; + } + } +} + +void SimonEngine::disableFileBoxes() { + for (int i = 208; i != 214; i++) + disableBox(i); +} + +void SimonEngine::userGameBackSpace(WindowBlock *window, int x, byte b) { + byte old_text; + + windowPutChar(window, x, b); + old_text = window->text_color; + window->text_color = window->fill_color; + + if (_language == Common::HB_ISR) { + x = 128; + } else { + x += 120; + if (x != 128) + x = 129; + } + + windowPutChar(window, x); + + window->text_color = old_text; + windowPutChar(window, 8); +} + +void SimonEngine::fileError(WindowBlock *window, bool save_error) { + HitArea *ha; + const char *string1, *string2; + + if (save_error) { + switch (_language) { + case Common::RU_RUS: + if (getGameType() == GType_SIMON2) { + string1 = "\r Mf sowrap+fts+."; + string2 = "\r Nzjb#a ejs#a."; + } else { + string1 = "\r Mf sowrap]fts]."; + string2 = "\r Nzjb_a ejs_a."; + } + break; + case Common::PL_POL: + string1 = "\r Blad zapisu. "; + string2 = "\rBlad dysku. "; + break; + case Common::ES_ESP: + string1 = "\r Error al salvar"; + string2 = "\r Intenta con otro disco"; + break; + case Common::IT_ITA: + string1 = "\r Salvataggio non riuscito"; + string2 = "\r Prova un""\x27""altro disco"; + break; + case Common::FR_FRA: + string1 = "\r Echec sauvegarde"; + string2 = "\rEssayez une autre disquette"; + break; + case Common::DE_DEU: + string1 = "\r Sicherung erfolglos."; + string2 = "\rVersuche eine andere Diskette."; + break; + default: + string1 = "\r Save failed."; + string2 = "\r Disk error."; + break; + } + } else { + switch (_language) { + case Common::RU_RUS: + if (getGameType() == GType_SIMON2) { + string1 = "\r Mf ^adruhafts+."; + string2 = "\r Takm pf pakefp."; + } else { + string1 = "\r Mf ^adruhafts]."; + string2 = "\r Takm pf pakefp."; + } + break; + case Common::PL_POL: + string1 = "\r Blad odczytu. "; + string2 = "\r Nie znaleziono pliku."; + break; + case Common::ES_ESP: + string1 = "\r Error al cargar"; + string2 = "\r Archivo no encontrado"; + break; + case Common::IT_ITA: + string1 = "\r Caricamento non riuscito"; + string2 = "\r File non trovato"; + break; + case Common::FR_FRA: + string1 = "\r Echec chargement"; + string2 = "\r Fichier introuvable"; + break; + case Common::DE_DEU: + string1 = "\r Laden erfolglos."; + string2 = "\r Datei nicht gefunden."; + break; + default: + string1 = "\r Load failed."; + string2 = "\r File not found."; + break; + } + } + + windowPutChar(window, 0xC); + for (; *string1; string1++) + windowPutChar(window, *string1); + for (; *string2; string2++) + windowPutChar(window, *string2); + + window->textColumn = (window->width / 2) - 3; + window->textRow = window->height - 1; + window->textLength = 0; + + string1 = "[ OK ]"; + for (; *string1; string1++) + windowPutChar(window, *string1); + + ha = findEmptyHitArea(); + ha->x = ((window->width / 2) + (window->x - 3)) * 8; + ha->y = (window->height * 8) + window->y - 8; + ha->width = 48; + ha->height = 8; + ha->flags = kBFBoxInUse; + ha->id = 0x7FFF; + ha->priority = 0x3EF; + +loop:; + _lastHitArea = _lastHitArea3 = 0; + + do { + delay(1); + } while (_lastHitArea3 == 0); + + ha = _lastHitArea; + if (ha == NULL || ha->id != 0x7FFF) + goto loop; + + // Return + undefineBox(0x7FFF); +} + +bool SimonEngine::saveGame(uint slot, char *caption) { + Common::WriteStream *f; + uint item_index, num_item, i, j; + TimeEvent *te; + uint32 curTime = 0; + uint32 gsc = _gameStoppedClock; + + _lockWord |= 0x100; + + f = _saveFileMan->openForSaving(genSaveName(slot)); + if (f == NULL) { + warning("saveGame: Failed to save slot %d", slot); + _lockWord &= ~0x100; + return false; + } + + if (getGameType() == GType_FF) { + f->write(caption, 100); + curTime = time(NULL); + } else { + f->write(caption, 18); + } + + f->writeUint32BE(_itemArrayInited - 1); + f->writeUint32BE(0xFFFFFFFF); + f->writeUint32BE(0); + f->writeUint32BE(0); + + i = 0; + for (te = _firstTimeStruct; te; te = te->next) + i++; + f->writeUint32BE(i); + + if (_clockStopped) + gsc += ((uint32)time(NULL) - _clockStopped); + for (te = _firstTimeStruct; te; te = te->next) { + f->writeUint32BE(te->time - curTime + gsc); + f->writeUint16BE(te->subroutine_id); + } + + item_index = 1; + for (num_item = _itemArrayInited - 1; num_item; num_item--) { + Item *item = _itemArrayPtr[item_index++]; + + f->writeUint16BE(item->parent); + f->writeUint16BE(item->sibling); + f->writeUint16BE(item->state); + f->writeUint16BE(item->classFlags); + + SubRoom *subRoom = (SubRoom *)findChildOfType(item, 1); + if (subRoom) { + f->writeUint16BE(subRoom->roomExitStates); + } + + SubObject *subObject = (SubObject *)findChildOfType(item, 2); + if (subObject) { + f->writeUint32BE(subObject->objectFlags); + i = subObject->objectFlags & 1; + + for (j = 1; j < 16; j++) { + if (subObject->objectFlags & (1 << j)) { + f->writeUint16BE(subObject->objectFlagValue[i++]); + } + } + } + + SubUserFlag *subUserFlag = (SubUserFlag *)findChildOfType(item, 9); + if (subUserFlag) { + for (i = 0; i != 4; i++) { + f->writeUint16BE(subUserFlag->userFlags[i]); + } + } + } + + // write the 255 variables + for (i = 0; i != 255; i++) { + f->writeUint16BE(readVariable(i)); + } + + // write the items in array 6 + for (i = 0; i != 10; i++) { + f->writeUint16BE(itemPtrToID(_itemStore[i])); + } + + // Write the bits in array 1 + for (i = 0; i != 16; i++) + f->writeUint16BE(_bitArray[i]); + + // Write the bits in array 2 + for (i = 0; i != 16; i++) + f->writeUint16BE(_bitArrayTwo[i]); + + // Write the bits in array 3 + if (getGameType() == GType_FF) { + for (i = 0; i != 16; i++) + f->writeUint16BE(_bitArrayThree[i]); + } + + f->flush(); + bool result = !f->ioFailed(); + + delete f; + _lockWord &= ~0x100; + + return result; +} + +bool SimonEngine::loadGame(uint slot) { + char ident[100]; + Common::SeekableReadStream *f = NULL; + uint num, item_index, i, j; + + _lockWord |= 0x100; + + if (getGameType() == GType_FF && slot == 999) { + // Load restart state + Common::File *file = new Common::File(); + file->open(genSaveName(slot), Common::File::kFileReadMode); + if (!file->isOpen()) { + delete file; + } else { + f = file; + } + } else { + f = _saveFileMan->openForLoading(genSaveName(slot)); + } + + if (f == NULL) { + warning("loadGame: Failed to load slot %d", slot); + _lockWord &= ~0x100; + return false; + } + + if (getGameType() == GType_FF) { + f->read(ident, 100); + } else { + f->read(ident, 18); + } + + num = f->readUint32BE(); + + if (f->readUint32BE() != 0xFFFFFFFF || num != _itemArrayInited - 1) { + delete f; + _lockWord &= ~0x100; + return false; + } + + f->readUint32BE(); + f->readUint32BE(); + _noParentNotify = true; + + + // add all timers + killAllTimers(); + for (num = f->readUint32BE(); num; num--) { + uint32 timeout = f->readUint32BE(); + uint16 func_to_call = f->readUint16BE(); + addTimeEvent(timeout, func_to_call); + } + + item_index = 1; + for (num = _itemArrayInited - 1; num; num--) { + Item *item = _itemArrayPtr[item_index++], *parent_item; + + uint parent = f->readUint16BE(); + uint sibling = f->readUint16BE(); + + parent_item = derefItem(parent); + + setItemParent(item, parent_item); + + if (parent_item == NULL) { + item->parent = parent; + item->sibling = sibling; + } + + item->state = f->readUint16BE(); + item->classFlags = f->readUint16BE(); + + SubRoom *subRoom = (SubRoom *)findChildOfType(item, 1); + if (subRoom != NULL) { + subRoom->roomExitStates = f->readUint16BE(); + } + + SubObject *subObject = (SubObject *)findChildOfType(item, 2); + if (subObject != NULL) { + subObject->objectFlags = f->readUint32BE(); + i = subObject->objectFlags & 1; + + for (j = 1; j < 16; j++) { + if (subObject->objectFlags & (1 << j)) { + subObject->objectFlagValue[i++] = f->readUint16BE(); + } + } + } + + SubUserFlag *subUserFlag = (SubUserFlag *) findChildOfType(item, 9); + if (subUserFlag) { + for (i = 0; i != 4; i++) { + subUserFlag->userFlags[i] = f->readUint16BE(); + } + } + } + + + // read the 255 variables + for (i = 0; i != 255; i++) { + writeVariable(i, f->readUint16BE()); + } + + // read the items in array 6 + for (i = 0; i != 10; i++) { + _itemStore[i] = derefItem(f->readUint16BE()); + } + + // Read the bits in array 1 + for (i = 0; i != 16; i++) + _bitArray[i] = f->readUint16BE(); + + // Read the bits in array 2 + for (i = 0; i != 16; i++) + _bitArrayTwo[i] = f->readUint16BE(); + + // Read the bits in array 3 + if (getGameType() == GType_FF) { + for (i = 0; i != 16; i++) + _bitArrayThree[i] = f->readUint16BE(); + } + + if (f->ioFailed()) { + error("load failed"); + } + + delete f; + + _noParentNotify = false; + + _lockWord &= ~0x100; + + return true; +} + +} // End of namespace Simon diff --git a/engines/agos/simon.cpp b/engines/agos/simon.cpp new file mode 100644 index 0000000000..4248e311ce --- /dev/null +++ b/engines/agos/simon.cpp @@ -0,0 +1,2292 @@ +/* ScummVM - Scumm Interpreter + * Copyright (C) 2001 Ludvig Strigeus + * Copyright (C) 2001-2006 The ScummVM project + * + * 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. + * + * $URL$ + * $Id$ + * + */ + +#include "common/stdafx.h" + +#include "common/config-manager.h" +#include "common/file.h" +#include "common/fs.h" +#include "common/system.h" + +#include "gui/about.h" + +#include "agos/debugger.h" +#include "agos/intern.h" +#include "agos/agos.h" +#include "agos/vga.h" + +#include "sound/mididrv.h" + +#ifdef PALMOS_68K +#include "globals.h" +#endif + +using Common::File; + +namespace Simon { + +#ifdef PALMOS_68K +#define PTR(a) a +static const GameSpecificSettings *simon1_settings; +static const GameSpecificSettings *simon2_settings; +static const GameSpecificSettings *feeblefiles_settings; +#else +#define PTR(a) &a +static const GameSpecificSettings simon1_settings = { + "EFFECTS", // effects_filename + "SIMON", // speech_filename +}; + +static const GameSpecificSettings simon2_settings = { + "", // effects_filename + "SIMON2", // speech_filename +}; + +static const GameSpecificSettings feeblefiles_settings = { + "", // effects_filename + "VOICES", // speech_filename +}; + +static const GameSpecificSettings puzzlepack_settings = { + "", // effects_filename + "MUSIC", // speech_filename +}; +#endif + +SimonEngine::SimonEngine(OSystem *syst) + : Engine(syst), midi(syst) { + _vcPtr = 0; + _vc_get_out_of_code = 0; + _gameOffsetsPtr = 0; + + _debugger = 0; + setupVgaOpcodes(); + + _keyPressed = 0; + + _gameFile = 0; + + _strippedTxtMem = 0; + _textMem = 0; + _textSize = 0; + _stringTabNum = 0; + _stringTabPos = 0; + _stringtab_numalloc = 0; + _stringTabPtr = 0; + + _itemArrayPtr = 0; + _itemArraySize = 0; + _itemArrayInited = 0; + + _itemHeapPtr = 0; + _itemHeapCurPos = 0; + _itemHeapSize = 0; + + _iconFilePtr = 0; + + _codePtr = 0; + + _localStringtable = 0; + _stringIdLocalMin = 0; + _stringIdLocalMax = 0; + + _roomsList = 0; + + _xtblList = 0; + _xtablesHeapPtrOrg = 0; + _xtablesHeapCurPosOrg = 0; + _xsubroutineListOrg = 0; + + _tblList = 0; + _tablesHeapPtr = 0; + _tablesHeapPtrOrg = 0; + _tablesheapPtrNew = 0; + _tablesHeapSize = 0; + _tablesHeapCurPos = 0; + _tablesHeapCurPosOrg = 0; + _tablesHeapCurPosNew = 0; + _subroutineListOrg = 0; + + _subroutineList = 0; + _subroutine = 0; + + _dxSurfacePitch = 0; + + _recursionDepth = 0; + + _lastVgaTick = 0; + + _marks = 0; + + _scriptVar2 = 0; + _runScriptReturn1 = 0; + _skipVgaWait = 0; + _noParentNotify = 0; + _beardLoaded = 0; + _hitarea_unk_3 = 0; + _mortalFlag = 0; + _updateScreen = false; + _usePaletteDelay = 0; + _syncFlag2 = 0; + _inCallBack = 0; + _cepeFlag = 0; + _copyPartialMode = 0; + _fastMode = 0; + _useBackGround = 0; + + _debugMode = 0; + _startMainScript = false; + _continousMainScript = false; + _startVgaScript = false; + _continousVgaScript = false; + _drawImagesDebug = false; + _dumpImages = false; + + _pause = false; + _speech = false; + _subtitles = false; + + _animatePointer = 0; + _mouseCursor = 0; + _mouseAnim = 0; + _mouseAnimMax = 0; + _oldMouseCursor = 0; + _currentMouseCursor = 0; + _currentMouseAnim = 0; + _oldMouseAnimMax = 0; + + _vgaVar9 = 0; + _chanceModifier = 0; + _restoreWindow6 = 0; + _scrollX = 0; + _scrollY = 0; + _scrollXMax = 0; + _scrollYMax = 0; + _scrollCount = 0; + _scrollFlag = 0; + _scrollHeight = 0; + _scrollWidth = 0; + _scrollImage = 0; + _boxStarHeight = 0; + + _scriptVerb = 0; + _scriptNoun1 = 0; + _scriptNoun2 = 0; + _scriptAdj1 = 0; + _scriptAdj2 = 0; + + _curWindow = 0; + _textWindow = 0; + + _subjectItem = 0; + _objectItem = 0; + _currentPlayer = 0; + + _currentBoxNumber = 0; + _iOverflow = 0; + _hitAreaObjectItem = 0; + _lastHitArea = 0; + _lastNameOn = 0; + _lastHitArea3 = 0; + _hitAreaSubjectItem = 0; + _currentVerbBox = 0; + _lastVerbOn = 0; + _needHitAreaRecalc = 0; + _verbHitArea = 0; + _defaultVerb = 0; + _mouseHideCount = 0; + + _windowNum = 0; + + _printCharCurPos = 0; + _printCharMaxPos = 0; + _printCharPixelCount = 0; + _numLettersToPrint = 0; + + _numTextBoxes = 0; + + _clockStopped = 0; + _gameStoppedClock = 0; + _lastTime = 0; + + _firstTimeStruct = 0; + _pendingDeleteTimeEvent = 0; + + _mouseX = 0; + _mouseY = 0; + _mouseXOld = 0; + _mouseYOld = 0; + + _leftButtonDown = 0; + _rightButtonDown = 0; + _noRightClick = false; + + _dummyItem1 = new Item(); + _dummyItem2 = new Item(); + _dummyItem3 = new Item(); + + _lockWord = 0; + _scrollUpHitArea = 0; + _scrollDownHitArea = 0; + + _fastFadeInFlag = 0; + + _noOverWrite = 0; + _rejectBlock = false; + + _fastFadeOutFlag = 0; + _unkPalFlag = 0; + _exitCutscene = 0; + _paletteFlag = 0; + + _soundFileId = 0; + _lastMusicPlayed = 0; + _nextMusicToPlay = 0; + + _showPreposition = 0; + _showMessageFlag = 0; + + _fastFadeCount = 0; + + _vgaSpriteChanged = 0; + + _vgaMemPtr = 0; + _vgaMemEnd = 0; + _vgaMemBase = 0; + _vgaFrozenBase = 0; + _vgaRealBase = 0; + _zoneBuffers = 0; + + _curVgaFile1 = 0; + _curVgaFile2 = 0; + _curSfxFile = 0; + + _syncCount = 0; + _timer5 = 0; + _timer4 = 0; + + _frameRate = 0; + + _zoneNumber = 0; + + _vgaWaitFor = 0; + _lastVgaWaitFor = 0; + + _vgaCurZoneNum = 0; + _vgaCurSpriteId = 0; + _vgaCurSpritePriority = 0; + + _baseY = 0; + _scale = 0; + + _feebleRect.left = 0; + _feebleRect.right = 0; + _feebleRect.top = 0; + _feebleRect.bottom = 0; + + _scaleX = 0; + _scaleY = 0; + _scaleWidth = 0; + _scaleHeight = 0; + + _nextVgaTimerToProcess = 0; + + memset(_objectArray, 0, sizeof(_objectArray)); + memset(_itemStore, 0, sizeof(_itemStore)); + + memset(_shortText, 0, sizeof(_shortText)); + memset(_shortTextX, 0, sizeof(_shortText)); + memset(_shortTextY, 0, sizeof(_shortText)); + memset(_longText, 0, sizeof(_longText)); + memset(_longSound, 0, sizeof(_longSound)); + + memset(_bitArray, 0, sizeof(_bitArray)); + memset(_bitArrayTwo, 0, sizeof(_bitArrayTwo)); + memset(_bitArrayThree, 0, sizeof(_bitArrayThree)); + + _variableArray = 0; + _variableArray2 = 0; + _variableArrayPtr = 0; + + memset(_windowArray, 0, sizeof(_windowArray)); + + memset(_fcsData1, 0, sizeof(_fcsData1)); + memset(_fcsData2, 0, sizeof(_fcsData2)); + + _freeStringSlot = 0; + + memset(_stringReturnBuffer, 0, sizeof(_stringReturnBuffer)); + + memset(_pathFindArray, 0, sizeof(_pathFindArray)); + + memset(_pathValues, 0, sizeof(_pathValues)); + _PVCount = 0; + _GPVCount = 0; + + memset(_pathValues1, 0, sizeof(_pathValues1)); + _PVCount1 = 0; + _GPVCount1 = 0; + + memset(_currentPalette, 0, sizeof(_currentPalette)); + memset(_displayPalette, 0, sizeof(_displayPalette)); + + memset(_videoBuf1, 0, sizeof(_videoBuf1)); + + _windowList = new WindowBlock[16]; + + memset(_lettersToPrintBuf, 0, sizeof(_lettersToPrintBuf)); + + _vgaTickCounter = 0; + + _moviePlay = 0; + _sound = 0; + + _effectsPaused = false; + _ambientPaused = false; + _musicPaused = false; + + _saveLoadType = 0; + _saveLoadSlot = 0; + memset(_saveLoadName, 0, sizeof(_saveLoadName)); + + _saveLoadRowCurPos = 0; + _numSaveGameRows = 0; + _saveDialogFlag = false; + _saveOrLoad = false; + _saveLoadEdit = false; + + _hyperLink = 0; + _interactY = 0; + _oracleMaxScrollY = 0; + _noOracleScroll = 0; + + _sdlMouseX = 0; + _sdlMouseY = 0; + + _backGroundBuf = 0; + _frontBuf = 0; + _backBuf = 0; + _scaleBuf = 0; + + _vc10BasePtrOld = 0; + memcpy (_hebrewCharWidths, + "\x5\x5\x4\x6\x5\x3\x4\x5\x6\x3\x5\x5\x4\x6\x5\x3\x4\x6\x5\x6\x6\x6\x5\x5\x5\x6\x5\x6\x6\x6\x6\x6", 32); + + + // Add default file directories for Acorn version of + // Simon the Sorcerer 1 + File::addDefaultDirectory(_gameDataPath + "execute"); + File::addDefaultDirectory(_gameDataPath + "EXECUTE"); + + // Add default file directories for Amiga/Macintosh + // verisons of Simon the Sorcerer 2 + File::addDefaultDirectory(_gameDataPath + "voices"); + File::addDefaultDirectory(_gameDataPath + "VOICES"); + + // Add default file directories for Amiga & Macintosh + // versions of The Feeble Files + File::addDefaultDirectory(_gameDataPath + "gfx"); + File::addDefaultDirectory(_gameDataPath + "GFX"); + File::addDefaultDirectory(_gameDataPath + "movies"); + File::addDefaultDirectory(_gameDataPath + "MOVIES"); + File::addDefaultDirectory(_gameDataPath + "sfx"); + File::addDefaultDirectory(_gameDataPath + "SFX"); + File::addDefaultDirectory(_gameDataPath + "speech"); + File::addDefaultDirectory(_gameDataPath + "SPEECH"); +} + +int SimonEngine::init() { + // Detect game + if (!initGame()) { + GUIErrorMessage("No valid games were found in the specified directory."); + return -1; + } + + if (getGameId() == GID_DIMP) { + _screenWidth = 496; + _screenHeight = 400; + } else if (getGameType() == GType_FF || getGameType() == GType_PP) { + _screenWidth = 640; + _screenHeight = 480; + } else { + _screenWidth = 320; + _screenHeight = 200; + } + + _system->beginGFXTransaction(); + initCommonGFX(getGameType() == GType_FF || getGameType() == GType_PP); + _system->initSize(_screenWidth, _screenHeight); + _system->endGFXTransaction(); + + // Setup mixer + if (!_mixer->isReady()) + warning("Sound initialization failed. " + "Features of the game that depend on sound synchronization will most likely break"); + set_volume(ConfMan.getInt("sfx_volume")); + _mixer->setVolumeForSoundType(Audio::Mixer::kMusicSoundType, ConfMan.getInt("music_volume")); + + // Setup midi driver + MidiDriver *driver = 0; + if (getGameType() == GType_FF || getGameType() == GType_PP || getGameId() == GID_SIMON1CD32) { + driver = MidiDriver::createMidi(MD_NULL); + _native_mt32 = false; + } else { + int midiDriver = MidiDriver::detectMusicDriver(MDT_ADLIB | MDT_MIDI); + _native_mt32 = ((midiDriver == MD_MT32) || ConfMan.getBool("native_mt32")); + driver = MidiDriver::createMidi(midiDriver); + if (_native_mt32) { + driver->property(MidiDriver::PROP_CHANNEL_MASK, 0x03FE); + } + } + + midi.mapMT32toGM (getGameType() == GType_SIMON1 && !_native_mt32); + + midi.set_driver(driver); + int ret = midi.open(); + if (ret) + warning ("MIDI Player init failed: \"%s\"", midi.getErrorName (ret)); + midi.set_volume(ConfMan.getInt("music_volume")); + + if (ConfMan.hasKey("music_mute") && ConfMan.getBool("music_mute") == 1) + midi.pause(_musicPaused ^= 1); + + // allocate buffers + _backGroundBuf = (byte *)calloc(_screenWidth * _screenHeight, 1); + _frontBuf = (byte *)calloc(_screenWidth * _screenHeight, 1); + _backBuf = (byte *)calloc(_screenWidth * _screenHeight, 1); + if (getGameType() == GType_FF || getGameType() == GType_PP) + _scaleBuf = (byte *)calloc(_screenWidth * _screenHeight, 1); + + setupGame(); + + _debugger = new Debugger(this); + _moviePlay = new MoviePlayer(this, _mixer); + _sound = new Sound(this, gss, _mixer); + + if (ConfMan.hasKey("sfx_mute") && ConfMan.getBool("sfx_mute") == 1) { + if (getGameId() == GID_SIMON1DOS) + midi._enable_sfx ^= 1; + else + _sound->effectsPause(_effectsPaused ^= 1); + } + + _language = Common::parseLanguage(ConfMan.get("language")); + + if (getGameType() == GType_PP) { + _speech = true; + _subtitles = false; + } else if (getFeatures() & GF_TALKIE) { + _speech = !ConfMan.getBool("speech_mute"); + _subtitles = ConfMan.getBool("subtitles"); + + if (getGameType() == GType_SIMON1) { + // English and German versions don't have full subtitles + if (_language == Common::EN_ANY || _language == Common::DE_DEU) + _subtitles = false; + // Other versions require speech to be enabled + else + _speech = true; + } + + // Default to speech only, if both speech and subtitles disabled + if (!_speech && !_subtitles) + _speech = true; + } else { + _speech = false; + _subtitles = true; + } + + _debugMode = (gDebugLevel >= 0); + if (gDebugLevel == 2) + _continousMainScript = true; + if (gDebugLevel == 3) + _continousVgaScript = true; + if (gDebugLevel == 4) + _startMainScript = true; + if (gDebugLevel == 5) + _startVgaScript = true; + + return 0; +} + +void SimonEngine::setupGame() { + if (getGameType() == GType_PP) { + gss = PTR(puzzlepack_settings); + _numTextBoxes = 40; + _numVideoOpcodes = 85; +#ifndef PALMOS_68K + _vgaMemSize = 7000000; +#else + _vgaMemSize = gVars->memory[kMemSimon2Games]; +#endif + _tableMemSize = 200000; + _vgaBaseDelay = 5; + _numVars = 2048; + } else if (getGameType() == GType_FF) { + gss = PTR(feeblefiles_settings); + _numTextBoxes = 40; + _numVideoOpcodes = 85; +#ifndef PALMOS_68K + _vgaMemSize = 7000000; +#else + _vgaMemSize = gVars->memory[kMemSimon2Games]; +#endif + _tableMemSize = 200000; + _vgaBaseDelay = 5; + _numVars = 256; + } else if (getGameType() == GType_SIMON2) { + gss = PTR(simon2_settings); + _tableIndexBase = 1580 / 4; + _textIndexBase = 1500 / 4; + _numTextBoxes = 20; + _numVideoOpcodes = 75; +#ifndef PALMOS_68K + _vgaMemSize = 2000000; +#else + _vgaMemSize = gVars->memory[kMemSimon2Games]; +#endif + _tableMemSize = 100000; + // Check whether to use MT-32 MIDI tracks in Simon the Sorcerer 2 + if ((getGameType() == GType_SIMON2) && _native_mt32) + _musicIndexBase = (1128 + 612) / 4; + else + _musicIndexBase = 1128 / 4; + _soundIndexBase = 1660 / 4; + _vgaBaseDelay = 1; + _numVars = 256; + } else { + gss = PTR(simon1_settings); + _tableIndexBase = 1576 / 4; + _textIndexBase = 1460 / 4; + _numTextBoxes = 20; + _numVideoOpcodes = 64; +#ifndef PALMOS_68K + _vgaMemSize = 1000000; +#else + _vgaMemSize = gVars->memory[kMemSimon1Games]; +#endif + _tableMemSize = 150000; + _musicIndexBase = 1316 / 4; + _soundIndexBase = 0; + _vgaBaseDelay = 1; + _numVars = 256; + } + + allocItemHeap(); + allocTablesHeap(); + + _variableArray = (int16 *)calloc(_numVars, sizeof(int16)); + _variableArray2 = (int16 *)calloc(_numVars, sizeof(int16)); + + setupOpcodes(); + + setZoneBuffers(); + + _currentMouseCursor = 255; + _currentMouseAnim = 255; + + _frameRate = 1; + + _lastMusicPlayed = -1; + _nextMusicToPlay = -1; + + _noOverWrite = 0xFFFF; + + _stringIdLocalMin = 1; + + _variableArrayPtr = _variableArray; +} + +SimonEngine::~SimonEngine() { + delete _gameFile; + + midi.close(); + + free(_itemHeapPtr - _itemHeapCurPos); + free(_tablesHeapPtr - _tablesHeapCurPos); + + free(_gameOffsetsPtr); + free(_iconFilePtr); + free(_itemArrayPtr); + free(_stringTabPtr); + free(_strippedTxtMem); + free(_tblList); + free(_textMem); + + free(_backGroundBuf); + free(_frontBuf); + free(_backBuf); + free(_scaleBuf); + + delete _dummyItem1; + delete _dummyItem2; + delete _dummyItem3; + + delete [] _windowList; + + delete _debugger; + delete _moviePlay; + delete _sound; +} + +GUI::Debugger *SimonEngine::getDebugger() { + return _debugger; +} + +void SimonEngine::paletteFadeOut(byte *palPtr, uint num, uint size) { + byte *p = palPtr; + + do { + if (p[0] >= size) + p[0] -= size; + else + p[0] = 0; + if (p[1] >= size) + p[1] -= size; + else + p[1] = 0; + if (p[2] >= size) + p[2] -= size; + else + p[2] = 0; + p += 4; + } while (--num); +} + +byte *SimonEngine::allocateItem(uint size) { + byte *org = _itemHeapPtr; + size = (size + sizeof(void*) - 1) & ~(sizeof(void*) - 1); + + _itemHeapPtr += size; + _itemHeapCurPos += size; + + if (_itemHeapCurPos > _itemHeapSize) + error("allocateItem: Itemheap overflow"); + + return org; +} + +void SimonEngine::setUserFlag(Item *item, int a, int b) { + SubUserFlag *subUserFlag; + + subUserFlag = (SubUserFlag *) findChildOfType(item, 9); + if (subUserFlag == NULL) { + subUserFlag = (SubUserFlag *) allocateChildBlock(item, 9, sizeof(SubUserFlag)); + } + + if (a >= 0 && a <= 3) + subUserFlag->userFlags[a] = b; +} + +void SimonEngine::createPlayer() { + Child *child; + + _currentPlayer = _itemArrayPtr[1]; + _currentPlayer->adjective = -1; + _currentPlayer->noun = 10000; + + child = (Child *)allocateChildBlock(_currentPlayer, 3, sizeof(Child)); + if (child == NULL) + error("createPlayer: player create failure"); + + setUserFlag(_currentPlayer, 0, 0); +} + +Child *SimonEngine::findChildOfType(Item *i, uint type) { + Child *child = i->children; + for (; child; child = child->next) + if (child->type == type) + return child; + return NULL; +} + +bool SimonEngine::isRoom(Item *item) { + return findChildOfType(item, 1) != NULL; +} + +bool SimonEngine::isObject(Item *item) { + return findChildOfType(item, 2) != NULL; +} + +uint SimonEngine::getOffsetOfChild2Param(SubObject *child, uint prop) { + uint m = 1; + uint offset = 0; + while (m != prop) { + if (child->objectFlags & m) + offset++; + m *= 2; + } + return offset; +} + +Child *SimonEngine::allocateChildBlock(Item *i, uint type, uint size) { + Child *child = (Child *)allocateItem(size); + child->next = i->children; + i->children = child; + child->type = type; + return child; +} + +void SimonEngine::allocItemHeap() { + _itemHeapSize = 64000; + _itemHeapCurPos = 0; + _itemHeapPtr = (byte *)calloc(64000, 1); +} + +void SimonEngine::allocTablesHeap() { + _tablesHeapSize = _tableMemSize; + _tablesHeapCurPos = 0; + _tablesHeapPtr = (byte *)calloc(_tableMemSize, 1); +} + +void SimonEngine::setItemState(Item *item, int value) { + item->state = value; +} + +byte SimonEngine::getByte() { + return *_codePtr++; +} + +int SimonEngine::getNextWord() { + int16 a = (int16)READ_BE_UINT16(_codePtr); + _codePtr += 2; + return a; +} + +uint SimonEngine::getNextStringID() { + return (uint16)getNextWord(); +} + +uint SimonEngine::getVarOrByte() { + uint a = *_codePtr++; + if (a != 255) + return a; + return readVariable(*_codePtr++); +} + +uint SimonEngine::getVarOrWord() { + uint a = READ_BE_UINT16(_codePtr); + _codePtr += 2; + if (getGameType() == GType_PP) { + if (a >= 60000 && a < 62048) { + return readVariable(a - 60000); + } + } else { + if (a >= 30000 && a < 30512) { + return readVariable(a - 30000); + } + } + return a; +} + +uint SimonEngine::getVarWrapper() { + if (getGameType() == GType_PP) + return getVarOrWord(); + else + return getVarOrByte(); +} + +Item *SimonEngine::getNextItemPtr() { + int a = getNextWord(); + + switch (a) { + case -1: + return _subjectItem; + case -3: + return _objectItem; + case -5: + return me(); + case -7: + return actor(); + case -9: + return derefItem(me()->parent); + default: + return derefItem(a); + } +} + +Item *SimonEngine::getNextItemPtrStrange() { + int a = getNextWord(); + switch (a) { + case -1: + return _subjectItem; + case -3: + return _objectItem; + case -5: + return _dummyItem2; + case -7: + return NULL; + case -9: + return _dummyItem3; + default: + return derefItem(a); + } +} + +uint SimonEngine::getNextItemID() { + int a = getNextWord(); + switch (a) { + case -1: + return itemPtrToID(_subjectItem); + case -3: + return itemPtrToID(_objectItem); + case -5: + return getItem1ID(); + case -7: + return 0; + case -9: + return me()->parent; + default: + return a; + } +} + +Item *SimonEngine::me() { + if (_currentPlayer) + return _currentPlayer; + return _dummyItem1; +} + +Item *SimonEngine::actor() { + error("actor: is this code ever used?"); + //if (_actorPlayer) + // return _actorPlayer; + return _dummyItem1; +} + +uint SimonEngine::getNextVarContents() { + return (uint16)readVariable(getVarWrapper()); +} + +uint SimonEngine::readVariable(uint variable) { + if (variable >= _numVars) + error("readVariable: Variable %d out of range", variable); + + if (getGameType() == GType_PP) { + return (uint16)_variableArray[variable]; + } else if (getGameType() == GType_FF) { + if (getBitFlag(83)) + return (uint16)_variableArray2[variable]; + else + return (uint16)_variableArray[variable]; + } else { + return _variableArray[variable]; + } +} + +void SimonEngine::writeNextVarContents(uint16 contents) { + writeVariable(getVarWrapper(), contents); +} + +void SimonEngine::writeVariable(uint variable, uint16 contents) { + if (variable >= _numVars) + error("writeVariable: Variable %d out of range", variable); + + if (getGameType() == GType_FF && getBitFlag(83)) + _variableArray2[variable] = contents; + else + _variableArray[variable] = contents; +} + +void SimonEngine::setItemParent(Item *item, Item *parent) { + Item *old_parent = derefItem(item->parent); + + if (item == parent) + error("setItemParent: Trying to set item as its own parent"); + + // unlink it if it has a parent + if (old_parent) + unlinkItem(item); + itemChildrenChanged(old_parent); + linkItem(item, parent); + itemChildrenChanged(parent); +} + +void SimonEngine::itemChildrenChanged(Item *item) { + int i; + WindowBlock *window; + + if (_noParentNotify) + return; + + mouseOff(); + + for (i = 0; i != 8; i++) { + window = _windowArray[i]; + if (window && window->iconPtr && window->iconPtr->itemRef == item) { + if (_fcsData1[i]) { + _fcsData2[i] = true; + } else { + _fcsData2[i] = false; + drawIconArray(i, item, window->iconPtr->line, window->iconPtr->classMask); + } + } + } + + mouseOn(); +} + +void SimonEngine::unlinkItem(Item *item) { + Item *first, *parent, *next; + + // can't unlink item without parent + if (item->parent == 0) + return; + + // get parent and first child of parent + parent = derefItem(item->parent); + first = derefItem(parent->child); + + // the node to remove is first in the parent's children? + if (first == item) { + parent->child = item->sibling; + item->parent = 0; + item->sibling = 0; + return; + } + + for (;;) { + if (!first) + error("unlinkItem: parent empty"); + if (first->sibling == 0) { + warning("unlinkItem: parent does not contain child"); + return; + } + + next = derefItem(first->sibling); + if (next == item) { + first->sibling = next->sibling; + item->parent = 0; + item->sibling = 0; + return; + } + first = next; + } +} + +void SimonEngine::linkItem(Item *item, Item *parent) { + uint id; + // Don't allow that an item that is already linked is relinked + if (item->parent) + return; + + id = itemPtrToID(parent); + item->parent = id; + + if (parent != 0) { + item->sibling = parent->child; + parent->child = itemPtrToID(item); + } else { + item->sibling = 0; + } +} + +void SimonEngine::setup_cond_c_helper() { + HitArea *last; + uint id; + + _noRightClick = 1; + + if (getGameType() == GType_FF) { + int cursor = 5; + int animMax = 16; + + if (getBitFlag(200)) { + cursor = 11; + animMax = 5; + } else if (getBitFlag(201)) { + cursor = 12; + animMax = 5; + } else if (getBitFlag(202)) { + cursor = 13; + animMax = 5; + } else if (getBitFlag(203)) { + cursor = 14; + animMax = 9; + } else if (getBitFlag(205)) { + cursor = 17; + animMax = 11; + } else if (getBitFlag(206)) { + cursor = 16; + animMax = 2; + } else if (getBitFlag(208)) { + cursor = 26; + animMax = 2; + } else if (getBitFlag(209)) { + cursor = 27; + animMax = 9; + } else if (getBitFlag(210)) { + cursor = 28; + animMax = 9; + } + + _animatePointer = 0; + _mouseCursor = cursor; + _mouseAnimMax = animMax; + _mouseAnim = 1; + _needHitAreaRecalc++; + } + + if (getGameType() == GType_SIMON2) { + _mouseCursor = 0; + if (_defaultVerb != 999) { + _mouseCursor = 9; + _needHitAreaRecalc++; + _defaultVerb = 0; + } + } + + _lastHitArea = 0; + _hitAreaObjectItem = NULL; + + last = _lastNameOn; + clearName(); + _lastNameOn = last; + + for (;;) { + _lastHitArea = NULL; + _lastHitArea3 = 0; + _leftButtonDown = 0; + + do { + if (_exitCutscene && getBitFlag(9)) { + endCutscene(); + goto out_of_here; + } + + if (getGameType() == GType_FF) { + if (_variableArray[254] == 63) { + hitarea_stuff_helper_2(); + } else if (_variableArray[254] == 75) { + hitarea_stuff_helper_2(); + _variableArray[60] = 9999; + goto out_of_here; + } + } + + delay(100); + } while (_lastHitArea3 == (HitArea *) -1 || _lastHitArea3 == 0); + + if (_lastHitArea == NULL) { + } else if (_lastHitArea->id == 0x7FFB) { + inventoryUp(_lastHitArea->window); + } else if (_lastHitArea->id == 0x7FFC) { + inventoryDown(_lastHitArea->window); + } else if (_lastHitArea->item_ptr != NULL) { + _hitAreaObjectItem = _lastHitArea->item_ptr; + id = 0xFFFF; + if (_lastHitArea->flags & kBFTextBox) { + if (getGameType() == GType_PP) + id = _lastHitArea->id; + else if (getGameType() == GType_FF && (_lastHitArea->flags & kBFHyperBox)) + id = _lastHitArea->data; + else + id = _lastHitArea->flags / 256; + } + if (getGameType() == GType_PP) + _variableArray[199] = id; + else + _variableArray[60] = id; + break; + } + } + +out_of_here: + _lastHitArea3 = 0; + _lastHitArea = 0; + _lastNameOn = NULL; + _mouseCursor = 0; + _noRightClick = 0; +} + +void SimonEngine::endCutscene() { + Subroutine *sub; + + _sound->stopVoice(); + + sub = getSubroutineByID(170); + if (sub != NULL) + startSubroutineEx(sub); + + _runScriptReturn1 = true; +} + +bool SimonEngine::has_item_childflag_0x10(Item *item) { + SubObject *child = (SubObject *)findChildOfType(item, 2); + return child && (child->objectFlags & kOFIcon) != 0; +} + +uint SimonEngine::itemGetIconNumber(Item *item) { + SubObject *child = (SubObject *)findChildOfType(item, 2); + uint offs; + + if (child == NULL || !(child->objectFlags & kOFIcon)) + return 0; + + offs = getOffsetOfChild2Param(child, 0x10); + return child->objectFlagValue[offs]; +} + +void SimonEngine::hitarea_stuff() { + HitArea *ha; + uint id; + + _leftButtonDown = 0; + _lastHitArea = 0; + _verbHitArea = 0; + _hitAreaSubjectItem = NULL; + _hitAreaObjectItem = NULL; + + resetVerbs(); + +startOver: + for (;;) { + _lastHitArea = NULL; + _lastHitArea3 = NULL; + + for (;;) { + if ((getGameType() == GType_SIMON1 || getGameType() == GType_SIMON2) && _keyPressed == 35) + displayBoxStars(); + processSpecialKeys(); + if (_lastHitArea3 == (HitArea *) -1) + goto startOver; + if (_lastHitArea3 != 0) + break; + hitarea_stuff_helper(); + delay(100); + } + + ha = _lastHitArea; + + if (ha == NULL) { + } else if (ha->id == 0x7FFB) { + inventoryUp(ha->window); + } else if (ha->id == 0x7FFC) { + inventoryDown(ha->window); + } else if (ha->id >= 101 && ha->id < 113) { + _verbHitArea = ha->verb; + setVerb(ha); + _defaultVerb = 0; + } else { + if ((_verbHitArea != 0 || _hitAreaSubjectItem != ha->item_ptr && ha->flags & kBFBoxItem) && + ha->item_ptr) { + if_1:; + _hitAreaSubjectItem = ha->item_ptr; + id = 0xFFFF; + if (ha->flags & kBFTextBox) { + if (getGameType() == GType_PP) + id = _lastHitArea->id; + else if (getGameType() == GType_FF && (ha->flags & kBFHyperBox)) + id = ha->data; + else + id = ha->flags / 256; + } + if (getGameType() == GType_PP) + _variableArray[199] = id; + else + _variableArray[60] = id; + displayName(ha); + if (_verbHitArea != 0) + break; + } else { + // else 1 + if (ha->verb == 0) { + if (ha->item_ptr) + goto if_1; + } else { + _verbHitArea = ha->verb & 0xBFFF; + if (ha->verb & 0x4000) { + _hitAreaSubjectItem = ha->item_ptr; + break; + } + if (_hitAreaSubjectItem != NULL) + break; + } + } + } + } + + _needHitAreaRecalc++; +} + +void SimonEngine::hitarea_stuff_helper() { + time_t cur_time; + + if (getGameType() == GType_SIMON2 || getGameType() == GType_FF || getGameType() == GType_PP) { + if (_variableArray[254] || _variableArray[249]) { + hitarea_stuff_helper_2(); + } + } else { + uint subr_id = (uint16)_variableArray[254]; + if (subr_id != 0) { + Subroutine *sub = getSubroutineByID(subr_id); + if (sub != NULL) { + startSubroutineEx(sub); + permitInput(); + } + _variableArray[254] = 0; + _runScriptReturn1 = false; + } + } + + time(&cur_time); + if ((uint) cur_time != _lastTime) { + _lastTime = cur_time; + if (kickoffTimeEvents()) + permitInput(); + } +} + +void SimonEngine::hitarea_stuff_helper_2() { + uint subr_id; + Subroutine *sub; + + subr_id = (uint16)_variableArray[249]; + if (subr_id != 0) { + sub = getSubroutineByID(subr_id); + if (sub != NULL) { + _variableArray[249] = 0; + startSubroutineEx(sub); + permitInput(); + } + _variableArray[249] = 0; + } + + subr_id = (uint16)_variableArray[254]; + if (subr_id != 0) { + sub = getSubroutineByID(subr_id); + if (sub != NULL) { + _variableArray[254] = 0; + startSubroutineEx(sub); + permitInput(); + } + _variableArray[254] = 0; + } + + _runScriptReturn1 = false; +} + +void SimonEngine::permitInput() { + if (!_mortalFlag) { + _mortalFlag = true; + showmessage_print_char(0); + _curWindow = 0; + if (_windowArray[0] != 0) { + _textWindow = _windowArray[0]; + if (getGameType() == GType_FF || getGameType() == GType_PP) + showmessage_helper_3(_textWindow->textColumn, _textWindow->width); + else + showmessage_helper_3(_textWindow->textLength, _textWindow->textMaxLength); + } + _mortalFlag = false; + } +} + +TextLocation *SimonEngine::getTextLocation(uint a) { + switch (a) { + case 1: + return &_textLocation1; + case 2: + return &_textLocation2; + case 101: + return &_textLocation3; + case 102: + return &_textLocation4; + default: + error("getTextLocation: Invalid text location %d", a); + } + return NULL; +} + +void SimonEngine::loadZone(uint vga_res) { + VgaPointersEntry *vpe; + uint32 size; + + CHECK_BOUNDS(vga_res, _vgaBufferPointers); + + vpe = _vgaBufferPointers + vga_res; + if (vpe->vgaFile1 != NULL) + return; + + vpe->vgaFile1 = loadVGAFile(vga_res * 2, 1, size); + vpe->vgaFile1End = vpe->vgaFile1 + size; + + vpe->vgaFile2 = loadVGAFile(vga_res * 2 + 1, 2, size); + vpe->vgaFile2End = vpe->vgaFile2 + size; + + vpe->sfxFile = NULL; + if (!(getFeatures() & GF_ZLIBCOMP)) { + vpe->sfxFile = loadVGAFile(vga_res * 2, 3, size); + vpe->sfxFileEnd = vpe->sfxFile + size; + } +} + +void SimonEngine::setZoneBuffers() { + _zoneBuffers = (byte *)malloc(_vgaMemSize); + + _vgaMemPtr = _zoneBuffers; + _vgaMemBase = _zoneBuffers; + _vgaFrozenBase = _zoneBuffers; + _vgaRealBase = _zoneBuffers; + _vgaMemEnd = _zoneBuffers + _vgaMemSize; +} + +byte *SimonEngine::allocBlock(uint32 size) { + byte *block, *blockEnd; + uint i; + + for (i = 0; i < _vgaMemSize / size; i++) { + block = _vgaMemPtr; + blockEnd = block + size; + + if (blockEnd >= _vgaMemEnd) { + _vgaMemPtr = _vgaMemBase; + } else { + _rejectBlock = false; + checkNoOverWrite(blockEnd); + if (_rejectBlock) + continue; + checkRunningAnims(blockEnd); + if (_rejectBlock) + continue; + checkZonePtrs(blockEnd); + _vgaMemPtr = blockEnd; + return block; + } + } + + error("allocBlock: Couldn't find free block"); +} + +void SimonEngine::checkNoOverWrite(byte *end) { + VgaPointersEntry *vpe; + + if (_noOverWrite == 0xFFFF) + return; + + vpe = &_vgaBufferPointers[_noOverWrite]; + + if (getGameType() == GType_FF || getGameType() == GType_PP) { + if (vpe->vgaFile1 < end && vpe->vgaFile1End > _vgaMemPtr) { + _rejectBlock = true; + _vgaMemPtr = vpe->vgaFile1End; + } else if (vpe->vgaFile2 < end && vpe->vgaFile2End > _vgaMemPtr) { + _rejectBlock = true; + _vgaMemPtr = vpe->vgaFile2End; + } else if (vpe->sfxFile && vpe->sfxFile < end && vpe->sfxFileEnd > _vgaMemPtr) { + _rejectBlock = true; + _vgaMemPtr = vpe->sfxFileEnd; + } else { + _rejectBlock = false; + } + } else { + if (_vgaMemPtr <= vpe->vgaFile1 && end >= vpe->vgaFile1 || + _vgaMemPtr <= vpe->vgaFile2 && end >= vpe->vgaFile2) { + _rejectBlock = true; + _vgaMemPtr = vpe->vgaFile1 + 0x5000; + } else { + _rejectBlock = false; + } + } +} + +void SimonEngine::checkRunningAnims(byte *end) { + VgaSprite *vsp; + if (getGameType() != GType_FF && getGameType() != GType_PP && (_lockWord & 0x20)) { + return; + } + + for (vsp = _vgaSprites; vsp->id; vsp++) { + checkAnims(vsp->zoneNum, end); + if (_rejectBlock == true) + return; + } +} + +void SimonEngine::checkAnims(uint a, byte *end) { + VgaPointersEntry *vpe; + + vpe = &_vgaBufferPointers[a]; + + if (getGameType() == GType_FF || getGameType() == GType_PP) { + if (vpe->vgaFile1 < end && vpe->vgaFile1End > _vgaMemPtr) { + _rejectBlock = true; + _vgaMemPtr = vpe->vgaFile1End; + } else if (vpe->vgaFile2 < end && vpe->vgaFile2End > _vgaMemPtr) { + _rejectBlock = true; + _vgaMemPtr = vpe->vgaFile2End; + } else if (vpe->sfxFile && vpe->sfxFile < end && vpe->sfxFileEnd > _vgaMemPtr) { + _rejectBlock = true; + _vgaMemPtr = vpe->sfxFileEnd; + } else { + _rejectBlock = false; + } + } else { + if (_vgaMemPtr <= vpe->vgaFile1 && end >= vpe->vgaFile1 || + _vgaMemPtr <= vpe->vgaFile2 && end >= vpe->vgaFile2) { + _rejectBlock = true; + _vgaMemPtr = vpe->vgaFile1 + 0x5000; + } else { + _rejectBlock = false; + } + } +} + +void SimonEngine::checkZonePtrs(byte *end) { + uint count = ARRAYSIZE(_vgaBufferPointers); + VgaPointersEntry *vpe = _vgaBufferPointers; + do { + if (getGameType() == GType_FF || getGameType() == GType_PP) { + if (vpe->vgaFile1 < end && vpe->vgaFile1End > _vgaMemPtr || + vpe->vgaFile2 < end && vpe->vgaFile2End > _vgaMemPtr || + vpe->sfxFile < end && vpe->sfxFileEnd > _vgaMemPtr) { + vpe->vgaFile1 = NULL; + vpe->vgaFile1End = NULL; + vpe->vgaFile2 = NULL; + vpe->vgaFile2End = NULL; + vpe->sfxFile = NULL; + vpe->sfxFileEnd = NULL; + } + } else { + if (_vgaMemPtr <= vpe->vgaFile1 && end >= vpe->vgaFile1 || + _vgaMemPtr <= vpe->vgaFile2 && end >= vpe->vgaFile2) { + vpe->vgaFile1 = NULL; + vpe->vgaFile2 = NULL; + } + } + } while (++vpe, --count); +} + +void SimonEngine::set_video_mode_internal(uint16 mode, uint16 vga_res_id) { + uint num, num_lines; + VgaPointersEntry *vpe; + byte *bb, *b; + uint16 count; + const byte *vc_ptr_org; + + _windowNum = mode; + _lockWord |= 0x20; + + if (getGameType() == GType_FF || getGameType() == GType_PP) { + vc27_resetSprite(); + } + + if (vga_res_id == 0) { + if (getGameType() == GType_SIMON1) { + _unkPalFlag = true; + } else if (getGameType() == GType_SIMON2) { + _useBackGround = true; + _restoreWindow6 = true; + } + } + + _zoneNumber = num = vga_res_id / 100; + + for (;;) { + vpe = &_vgaBufferPointers[num]; + + _curVgaFile1 = vpe->vgaFile1; + _curVgaFile2 = vpe->vgaFile2; + _curSfxFile = vpe->sfxFile; + + if (vpe->vgaFile1 != NULL) + break; + + loadZone(num); + } + + // ensure flipping complete + + bb = _curVgaFile1; + + if (getGameType() == GType_FF || getGameType() == GType_PP) { + b = bb + READ_LE_UINT16(&((VgaFileHeader_Feeble *) bb)->hdr2_start); + count = READ_LE_UINT16(&((VgaFileHeader2_Feeble *) b)->imageCount); + b = bb + READ_LE_UINT16(&((VgaFileHeader2_Feeble *) b)->imageTable); + + while (count--) { + if (READ_LE_UINT16(&((ImageHeader_Feeble *) b)->id) == vga_res_id) + break; + b += sizeof(ImageHeader_Feeble); + } + assert(READ_LE_UINT16(&((ImageHeader_Feeble *) b)->id) == vga_res_id); + + } else if (getGameType() == GType_SIMON1 || getGameType() == GType_SIMON2) { + b = bb + READ_BE_UINT16(&((VgaFileHeader_Simon *) bb)->hdr2_start); + count = READ_BE_UINT16(&((VgaFileHeader2_Simon *) b)->imageCount); + b = bb + READ_BE_UINT16(&((VgaFileHeader2_Simon *) b)->imageTable); + + while (count--) { + if (READ_BE_UINT16(&((ImageHeader_Simon *) b)->id) == vga_res_id) + break; + b += sizeof(ImageHeader_Simon); + } + assert(READ_BE_UINT16(&((ImageHeader_Simon *) b)->id) == vga_res_id); + } else { + b = bb + READ_BE_UINT16(bb + 10); + b += 20; + + count = READ_BE_UINT16(&((VgaFileHeader2_WW *) b)->imageCount); + b = bb + READ_BE_UINT16(&((VgaFileHeader2_WW *) b)->imageTable); + + while (count--) { + if (READ_BE_UINT16(&((ImageHeader_WW *) b)->id) == vga_res_id) + break; + b += sizeof(ImageHeader_WW); + } + assert(READ_BE_UINT16(&((ImageHeader_WW *) b)->id) == vga_res_id); + } + + if (getGameType() == GType_SIMON1) { + if (vga_res_id == 16300) { + clearBackFromTop(134); + _usePaletteDelay = true; + } + } else { + _scrollX = 0; + _scrollY = 0; + _scrollXMax = 0; + _scrollYMax = 0; + _scrollCount = 0; + _scrollFlag = 0; + _scrollHeight = 134; + _variableArrayPtr = _variableArray; + if (_variableArray[34] >= 0) { + if (getGameType() == GType_FF) + _variableArray[250] = 0; + _variableArray[251] = 0; + } + } + + vc_ptr_org = _vcPtr; + + if (getGameType() == GType_FF || getGameType() == GType_PP) { + _vcPtr = _curVgaFile1 + READ_LE_UINT16(&((ImageHeader_Feeble *) b)->scriptOffs); + } else if (getGameType() == GType_SIMON1 || getGameType() == GType_SIMON2) { + _vcPtr = _curVgaFile1 + READ_BE_UINT16(&((ImageHeader_Simon *) b)->scriptOffs); + } else { + _vcPtr = _curVgaFile1 + READ_BE_UINT16(&((ImageHeader_WW *) b)->scriptOffs); + } + + //dump_vga_script(_vcPtr, num, vga_res_id); + runVgaScript(); + _vcPtr = vc_ptr_org; + + + if (getGameType() == GType_FF || getGameType() == GType_PP) { + fillFrontFromBack(0, 0, _screenWidth, _screenHeight); + fillBackGroundFromBack(_screenHeight); + _syncFlag2 = 1; + } else if (getGameType() == GType_SIMON2) { + if (!_useBackGround) { + num_lines = _windowNum == 4 ? 134 : 200; + _boxStarHeight = num_lines; + fillFrontFromBack(0, 0, _screenWidth, num_lines); + fillBackGroundFromBack(num_lines); + _syncFlag2 = 1; + } + _useBackGround = false; + } else { + // Allow one section of Simon the Sorcerer 1 introduction to be displayed + // in lower half of screen + if (_subroutine == 2923 || _subroutine == 2926) + num_lines = 200; + else + num_lines = _windowNum == 4 ? 134 : 200; + + fillFrontFromBack(0, 0, _screenWidth, num_lines); + fillBackGroundFromBack(num_lines); + + _syncFlag2 = 1; + _timer5 = 0; + } + + _lockWord &= ~0x20; + + if (getGameType() == GType_SIMON1) { + if (_unkPalFlag) { + _unkPalFlag = false; + while (_fastFadeInFlag != 0) { + delay(10); + } + } + } +} + +void SimonEngine::waitForSync(uint a) { + const uint maxCount = (getGameType() == GType_SIMON1) ? 500 : 1000; + + if (getGameType() == GType_SIMON1 && (getFeatures() & GF_TALKIE)) { + if (a != 200) { + uint16 tmp = _lastVgaWaitFor; + _lastVgaWaitFor = 0; + if (tmp == a) + return; + } + } + + _vgaWaitFor = a; + _syncCount = 0; + _exitCutscene = false; + _rightButtonDown = false; + + while (_vgaWaitFor != 0) { + if (_rightButtonDown) { + if (_vgaWaitFor == 200 && (getGameType() == GType_FF || !getBitFlag(14))) { + skipSpeech(); + break; + } + } + if (_exitCutscene) { + if (getBitFlag(9)) { + endCutscene(); + break; + } + } + processSpecialKeys(); + + if (_syncCount >= maxCount) { + warning("waitForSync: wait timed out"); + break; + } + + delay(1); + } +} + +void SimonEngine::skipSpeech() { + _sound->stopVoice(); + if (!getBitFlag(28)) { + setBitFlag(14, true); + if (getGameType() == GType_FF) { + _variableArray[103] = 5; + loadSprite(4, 2, 13, 0, 0, 0); + waitForSync(213); + stopAnimateSimon2(2, 1); + } else if (getGameType() == GType_SIMON2) { + _variableArray[100] = 5; + loadSprite(4, 1, 30, 0, 0, 0); + waitForSync(130); + stopAnimateSimon2(2, 1); + } else { + _variableArray[100] = 15; + loadSprite(4, 1, 130, 0, 0, 0); + waitForSync(130); + stopAnimateSimon1(1); + } + } +} + +Item *SimonEngine::derefItem(uint item) { + if (item >= _itemArraySize) + error("derefItem: invalid item %d", item); + return _itemArrayPtr[item]; +} + +uint SimonEngine::itemPtrToID(Item *id) { + uint i; + for (i = 0; i != _itemArraySize; i++) + if (_itemArrayPtr[i] == id) + return i; + error("itemPtrToID: not found"); + return 0; +} + +bool SimonEngine::isSpriteLoaded(uint16 id, uint16 zoneNum) { + VgaSprite *vsp = _vgaSprites; + while (vsp->id) { + if (getGameType() == GType_SIMON1 || getGameType() == GType_WW) { + if (vsp->id == id) + return true; + } else { + if (vsp->id == id && vsp->zoneNum == zoneNum) + return true; + } + vsp++; + } + return false; +} + +void SimonEngine::processSpecialKeys() { + switch (_keyPressed) { + case 27: // escape + _exitCutscene = true; + break; + case 59: // F1 + if (getGameType() == GType_SIMON1) { + vcWriteVar(5, 40); + } else { + vcWriteVar(5, 50); + } + vcWriteVar(86, 0); + break; + case 60: // F2 + if (getGameType() == GType_SIMON1) { + vcWriteVar(5, 60); + } else { + vcWriteVar(5, 75); + } + vcWriteVar(86, 1); + break; + case 61: // F3 + if (getGameType() == GType_SIMON1) { + vcWriteVar(5, 100); + } else { + vcWriteVar(5, 125); + } + vcWriteVar(86, 2); + break; + case 63: // F5 + if (getGameType() == GType_SIMON2 || getGameType() == GType_FF) + _exitCutscene = true; + break; + case 65: // F7 + if (getGameType() == GType_FF && getBitFlag(76)) + _variableArray[254] = 70; + break; + case 67: // F9 + if (getGameType() == GType_FF) + setBitFlag(73, !getBitFlag(73)); + break; + case 'p': + pause(); + break; + case 't': + if (getGameType() == GType_FF || (getGameType() == GType_SIMON2 && (getFeatures() & GF_TALKIE)) || + ((getFeatures() & GF_TALKIE) && _language != Common::EN_ANY && _language != Common::DE_DEU)) { + if (_speech) + _subtitles ^= 1; + } + break; + case 'v': + if (getGameType() == GType_FF || (getGameType() == GType_SIMON2 && (getFeatures() & GF_TALKIE))) { + if (_subtitles) + _speech ^= 1; + } + case '+': + midi.set_volume(midi.get_volume() + 16); + break; + case '-': + midi.set_volume(midi.get_volume() - 16); + break; + case 'm': + midi.pause(_musicPaused ^= 1); + break; + case 's': + if (getGameId() == GID_SIMON1DOS) + midi._enable_sfx ^= 1; + else + _sound->effectsPause(_effectsPaused ^= 1); + break; + case 'b': + _sound->ambientPause(_ambientPaused ^= 1); + break; + case 'r': + if (_debugMode) + _startMainScript ^= 1; + break; + case 'o': + if (_debugMode) + _continousMainScript ^= 1; + break; + case 'a': + if (_debugMode) + _startVgaScript ^= 1; + break; + case 'g': + if (_debugMode) + _continousVgaScript ^= 1; + break; + case 'i': + if (_debugMode) + _drawImagesDebug ^= 1; + break; + case 'd': + if (_debugMode) + _dumpImages ^=1; + break; + } + + _keyPressed = 0; +} + +void SimonEngine::pause() { + _keyPressed = 1; + _pause = 1; + bool ambient_status = _ambientPaused; + bool music_status = _musicPaused; + + midi.pause(true); + _sound->ambientPause(true); + while (_pause) { + delay(1); + if (_keyPressed == 'p') + _pause = 0; + } + midi.pause(music_status); + _sound->ambientPause(ambient_status); + +} + +void SimonEngine::loadSprite(uint windowNum, uint zoneNum, uint vgaSpriteId, uint x, uint y, uint palette) { + VgaSprite *vsp; + VgaPointersEntry *vpe; + byte *p, *pp; + uint count; + + if (vgaSpriteId >= 400) + _lastVgaWaitFor = 0; + + _lockWord |= 0x40; + + if (isSpriteLoaded(vgaSpriteId, zoneNum)) { + _lockWord &= ~0x40; + return; + } + + vsp = _vgaSprites; + while (vsp->id != 0) + vsp++; + + vsp->windowNum = windowNum; + vsp->priority = 0; + vsp->flags = 0; + + vsp->y = y; + vsp->x = x; + vsp->image = 0; + if (getGameType() == GType_WW) + vsp->palette = 0; + else + vsp->palette = palette; + vsp->id = vgaSpriteId; + if (getGameType() == GType_SIMON1 || getGameType() == GType_WW) + vsp->zoneNum = zoneNum = vgaSpriteId / 100; + else + vsp->zoneNum = zoneNum; + + + for (;;) { + vpe = &_vgaBufferPointers[zoneNum]; + _zoneNumber = zoneNum; + _curVgaFile1 = vpe->vgaFile1; + if (vpe->vgaFile1 != NULL) + break; + loadZone(zoneNum); + } + + pp = _curVgaFile1; + if (getGameType() == GType_FF || getGameType() == GType_PP) { + p = pp + READ_LE_UINT16(&((VgaFileHeader_Feeble *) pp)->hdr2_start); + count = READ_LE_UINT16(&((VgaFileHeader2_Feeble *) p)->animationCount); + p = pp + READ_LE_UINT16(&((VgaFileHeader2_Feeble *) p)->animationTable); + } else if (getGameType() == GType_SIMON1 || getGameType() == GType_SIMON2) { + p = pp + READ_BE_UINT16(&((VgaFileHeader_Simon *) pp)->hdr2_start); + count = READ_BE_UINT16(&((VgaFileHeader2_Simon *) p)->animationCount); + p = pp + READ_BE_UINT16(&((VgaFileHeader2_Simon *) p)->animationTable); + } else { + p = pp + READ_BE_UINT16(pp + 10); + p += 20; + + count = READ_BE_UINT16(&((VgaFileHeader2_WW *) p)->animationCount); + p = pp + READ_BE_UINT16(&((VgaFileHeader2_WW *) p)->animationTable); + } + + for (;;) { + if (getGameType() == GType_FF || getGameType() == GType_PP) { + if (READ_LE_UINT16(&((AnimationHeader_Feeble *) p)->id) == vgaSpriteId) { + if (_startVgaScript) + dump_vga_script(pp + READ_LE_UINT16(&((AnimationHeader_Feeble*)p)->scriptOffs), zoneNum, vgaSpriteId); + + addVgaEvent(_vgaBaseDelay, pp + READ_LE_UINT16(&((AnimationHeader_Feeble *) p)->scriptOffs), vgaSpriteId, zoneNum); + break; + } + p += sizeof(AnimationHeader_Feeble); + } else if (getGameType() == GType_SIMON1 || getGameType() == GType_SIMON2) { + if (READ_BE_UINT16(&((AnimationHeader_Simon *) p)->id) == vgaSpriteId) { + if (_startVgaScript) + dump_vga_script(pp + READ_BE_UINT16(&((AnimationHeader_Simon*)p)->scriptOffs), zoneNum, vgaSpriteId); + + addVgaEvent(_vgaBaseDelay, pp + READ_BE_UINT16(&((AnimationHeader_Simon *) p)->scriptOffs), vgaSpriteId, zoneNum); + break; + } + p += sizeof(AnimationHeader_Simon); + } else { + if (READ_BE_UINT16(&((AnimationHeader_WW *) p)->id) == vgaSpriteId) { + if (_startVgaScript) + dump_vga_script(pp + READ_BE_UINT16(&((AnimationHeader_WW *)p)->scriptOffs), zoneNum, vgaSpriteId); + + addVgaEvent(_vgaBaseDelay, pp + READ_BE_UINT16(&((AnimationHeader_WW *) p)->scriptOffs), vgaSpriteId, zoneNum); + break; + } + p += sizeof(AnimationHeader_WW); + } + + if (!--count) { + vsp->id = 0; + break; + } + } + + _lockWord &= ~0x40; +} + +void SimonEngine::playSpeech(uint speech_id, uint vgaSpriteId) { + if (getGameType() == GType_SIMON1) { + if (speech_id == 9999) { + if (_subtitles) + return; + if (!getBitFlag(14) && !getBitFlag(28)) { + setBitFlag(14, true); + _variableArray[100] = 15; + loadSprite(4, 1, 130, 0, 0, 0); + waitForSync(130); + } + _skipVgaWait = true; + } else { + if (_subtitles && _scriptVar2) { + loadSprite(4, 2, 204, 0, 0, 0); + waitForSync(204); + stopAnimateSimon1(204); + } + stopAnimateSimon1(vgaSpriteId + 201); + loadVoice(speech_id); + loadSprite(4, 2, vgaSpriteId + 201, 0, 0, 0); + } + } else { + if (speech_id == 0xFFFF) { + if (_subtitles) + return; + if (!getBitFlag(14) && !getBitFlag(28)) { + setBitFlag(14, true); + _variableArray[100] = 5; + loadSprite(4, 1, 30, 0, 0, 0); + waitForSync(130); + } + _skipVgaWait = true; + } else { + if (getGameType() == GType_SIMON2 && _subtitles && _language != Common::HB_ISR) { + loadVoice(speech_id); + return; + } + + if (_subtitles && _scriptVar2) { + loadSprite(4, 2, 5, 0, 0, 0); + waitForSync(205); + stopAnimateSimon2(2,5); + } + + stopAnimateSimon2(2, vgaSpriteId + 2); + loadVoice(speech_id); + loadSprite(4, 2, vgaSpriteId + 2, 0, 0, 0); + } + } +} + +int SimonEngine::go() { + + loadGamePcFile(); + + addTimeEvent(0, 1); + openGameFile(); + + if (getGameType() == GType_FF) + loadIconData(); + else if (getGameType() != GType_PP) + loadIconFile(); + + vc34_setMouseOff(); + + if ((getPlatform() == Common::kPlatformAmiga || getPlatform() == Common::kPlatformMacintosh) && + getGameType() == GType_FF) { + _moviePlay->load((const char *)"epic.dxa"); + _moviePlay->play(); + } + + runSubroutine101(); + permitInput(); + + while (1) { + hitarea_stuff(); + handleVerbClicked(_verbHitArea); + delay(100); + } + + return 0; +} + +void SimonEngine::shutdown() { + delete _gameFile; + + midi.close(); + + free(_stringTabPtr); + free(_itemArrayPtr); + free(_itemHeapPtr - _itemHeapCurPos); + free(_tablesHeapPtr - _tablesHeapCurPos); + free(_tblList); + free(_zoneBuffers); + free(_iconFilePtr); + free(_gameOffsetsPtr); + + _system->quit(); +} + +void SimonEngine::delay(uint amount) { + OSystem::Event event; + + uint32 start = _system->getMillis(); + uint32 cur = start; + uint this_delay, vga_period; + + if (_debugger->isAttached()) + _debugger->onFrame(); + + if (_fastMode) + vga_period = 10; + else if (getGameType() == GType_SIMON2) + vga_period = 45; + else + vga_period = 50; + + _rnd.getRandomNumber(2); + + do { + while (!_inCallBack && cur >= _lastVgaTick + vga_period && !_pause) { + _lastVgaTick += vga_period; + + // don't get too many frames behind + if (cur >= _lastVgaTick + vga_period * 2) + _lastVgaTick = cur; + + _inCallBack = true; + timer_callback(); + _inCallBack = false; + } + + while (_system->pollEvent(event)) { + switch (event.type) { + case OSystem::EVENT_KEYDOWN: + if (event.kbd.keycode >= '0' && event.kbd.keycode <='9' + && (event.kbd.flags == OSystem::KBD_ALT || + event.kbd.flags == OSystem::KBD_CTRL)) { + _saveLoadSlot = event.kbd.keycode - '0'; + + // There is no save slot 0 + if (_saveLoadSlot == 0) + _saveLoadSlot = 10; + + sprintf(_saveLoadName, "Quicksave %d", _saveLoadSlot); + _saveLoadType = (event.kbd.flags == OSystem::KBD_ALT) ? 1 : 2; + + // We should only allow a load or save when it was possible in original + // This stops load/save during copy protection, conversations and cut scenes + if (!_mouseHideCount && !_showPreposition) + quickLoadOrSave(); + } else if (event.kbd.flags == OSystem::KBD_CTRL) { + if (event.kbd.keycode == 'a') { + GUI::Dialog *_aboutDialog; + _aboutDialog = new GUI::AboutDialog(); + _aboutDialog->runModal(); + } else if (event.kbd.keycode == 'f') + _fastMode ^= 1; + else if (event.kbd.keycode == 'd') + _debugger->attach(); + } + // Make sure backspace works right (this fixes a small issue on OS X) + if (event.kbd.keycode == 8) + _keyPressed = 8; + else + _keyPressed = (byte)event.kbd.ascii; + break; + case OSystem::EVENT_MOUSEMOVE: + _sdlMouseX = event.mouse.x; + _sdlMouseY = event.mouse.y; + break; + case OSystem::EVENT_LBUTTONDOWN: + if (getGameType() == GType_FF) + setBitFlag(89, true); + _leftButtonDown++; +#if defined (_WIN32_WCE) || defined(PALMOS_MODE) + _sdlMouseX = event.mouse.x; + _sdlMouseY = event.mouse.y; +#endif + break; + case OSystem::EVENT_LBUTTONUP: + if (getGameType() == GType_FF) + setBitFlag(89, false); + break; + case OSystem::EVENT_RBUTTONDOWN: + if (getGameType() == GType_FF) + setBitFlag(92, false); + _rightButtonDown++; + break; + case OSystem::EVENT_QUIT: + shutdown(); + return; + default: + break; + } + } + + _system->updateScreen(); + + if (amount == 0) + break; + + this_delay = _fastMode ? 1 : 20; + if (this_delay > amount) + this_delay = amount; + _system->delayMillis(this_delay); + + cur = _system->getMillis(); + } while (cur < start + amount); +} + +void SimonEngine::loadMusic(uint music) { + char buf[4]; + + if (getGameType() == GType_SIMON2) { // Simon 2 music + midi.stop(); + _gameFile->seek(_gameOffsetsPtr[_musicIndexBase + music - 1], SEEK_SET); + _gameFile->read(buf, 4); + if (!memcmp(buf, "FORM", 4)) { + _gameFile->seek(_gameOffsetsPtr[_musicIndexBase + music - 1], SEEK_SET); + midi.loadXMIDI (_gameFile); + } else { + _gameFile->seek(_gameOffsetsPtr[_musicIndexBase + music - 1], SEEK_SET); + midi.loadMultipleSMF (_gameFile); + } + + _lastMusicPlayed = music; + _nextMusicToPlay = -1; + } else if (getGameType() == GType_SIMON1) { // Simon 1 music + if (getPlatform() == Common::kPlatformAmiga) { + if (getFeatures() & GF_CRUNCHED) { + // TODO Add support for decruncher + debug(5,"loadMusic - Decrunch %dtune attempt", music); + } + // TODO Add Protracker support for simon1amiga/cd32 + debug(5,"playMusic - Load %dtune attempt", music); + return; + } + + midi.stop(); + midi.setLoop (true); // Must do this BEFORE loading music. (GMF may have its own override.) + + if (getFeatures() & GF_TALKIE) { + // FIXME: The very last music resource, a cymbal crash for when the + // two demons crash into each other, should NOT be looped like the + // other music tracks. In simon1dos/talkie the GMF resource includes + // a loop override that acomplishes this, but there seems to be nothing + // for this in the SMF resources. + if (music == 35) + midi.setLoop (false); + + _gameFile->seek(_gameOffsetsPtr[_musicIndexBase + music], SEEK_SET); + _gameFile->read(buf, 4); + if (!memcmp(buf, "GMF\x1", 4)) { + _gameFile->seek(_gameOffsetsPtr[_musicIndexBase + music], SEEK_SET); + midi.loadSMF (_gameFile, music); + } else { + _gameFile->seek(_gameOffsetsPtr[_musicIndexBase + music], SEEK_SET); + midi.loadMultipleSMF (_gameFile); + } + + } else { + char filename[15]; + File f; + sprintf(filename, "MOD%d.MUS", music); + f.open(filename); + if (f.isOpen() == false) + error("loadMusic: Can't load music from '%s'", filename); + + if (getGameId() == GID_SIMON1DEMO) + midi.loadS1D (&f); + else + midi.loadSMF (&f, music); + } + + midi.startTrack (0); + } else { + midi.stop(); + midi.setLoop (true); // Must do this BEFORE loading music. (GMF may have its own override.) + + char filename[15]; + File f; + sprintf(filename, "MOD%d.MUS", music); + f.open(filename); + if (f.isOpen() == false) + error("loadMusic: Can't load music from '%s'", filename); + + midi.loadS1D (&f); + midi.startTrack (0); + } +} + +void SimonEngine::playSting(uint a) { + if (!midi._enable_sfx) + return; + + char filename[15]; + + File mus_file; + uint16 mus_offset; + + sprintf(filename, "STINGS%i.MUS", _soundFileId); + mus_file.open(filename); + if (!mus_file.isOpen()) + error("playSting: Can't load sound effect from '%s'", filename); + + mus_file.seek(a * 2, SEEK_SET); + mus_offset = mus_file.readUint16LE(); + if (mus_file.ioFailed()) + error("playSting: Can't read sting %d offset", a); + + mus_file.seek(mus_offset, SEEK_SET); + midi.loadSMF(&mus_file, a, true); + midi.startTrack(0); +} + +void SimonEngine::set_volume(int volume) { + _mixer->setVolumeForSoundType(Audio::Mixer::kSFXSoundType, volume); +} + +} // End of namespace Simon + +#ifdef PALMOS_68K +#include "scumm_globals.h" + +_GINIT(AGOS_AGOS) +_GSETPTR(Simon::simon1_settings, GBVARS_SIMON1SETTINGS_INDEX, Simon::GameSpecificSettings, GBVARS_SIMON) +_GSETPTR(Simon::simon2_settings, GBVARS_SIMON2SETTINGS_INDEX, Simon::GameSpecificSettings, GBVARS_SIMON) +_GSETPTR(Simon::feeblefiles_settings, GBVARS_FEEBLEFILESSETTINGS_INDEX, Simon::GameSpecificSettings, GBVARS_SIMON) +_GEND + +_GRELEASE(AGOS_AGOS) +_GRELEASEPTR(GBVARS_SIMON1SETTINGS_INDEX, GBVARS_SIMON) +_GRELEASEPTR(GBVARS_SIMON2SETTINGS_INDEX, GBVARS_SIMON) +_GRELEASEPTR(GBVARS_FEEBLEFILESSETTINGS_INDEX, GBVARS_SIMON) +_GEND + +#endif diff --git a/engines/agos/simon.h b/engines/agos/simon.h new file mode 100644 index 0000000000..90f3a1506c --- /dev/null +++ b/engines/agos/simon.h @@ -0,0 +1,1191 @@ +/* ScummVM - Scumm Interpreter + * Copyright (C) 2001 Ludvig Strigeus + * Copyright (C) 2001-2006 The ScummVM project + * + * 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. + * + * $URL$ + * $Id$ + * + */ + +#ifndef AGOS_AGOS_H +#define AGOS_AGOS_H + +#include "engines/engine.h" + +#include "common/rect.h" +#include "common/util.h" + +#include "agos/animation.h" +#include "agos/midi.h" +#include "agos/sound.h" +#include "agos/vga.h" + +namespace Simon { + +/* Various other settings */ +//#define DUMP_FILE_NR 8 +//#define DUMP_BITMAPS_FILE_NR 8 + +uint fileReadItemID(Common::File *in); + +#define CHECK_BOUNDS(x, y) assert((uint)(x) < ARRAYSIZE(y)) + +struct Child; +struct SubObject; + +struct Item; +struct WindowBlock; +struct Subroutine; +struct SubroutineLine; +struct TimeEvent; + +struct TextLocation { + int16 x, y, width; + TextLocation() { memset(this, 0, sizeof(*this)); } +}; + +struct HitArea { + uint16 x, y; + uint16 width, height; + uint16 flags; + uint16 id; + uint16 data; + WindowBlock *window; + Item *item_ptr; + uint16 verb; + uint16 priority; + HitArea() { memset(this, 0, sizeof(*this)); } +}; + +struct VgaPointersEntry { + byte *vgaFile1; + byte *vgaFile1End; + byte *vgaFile2; + byte *vgaFile2End; + byte *sfxFile; + byte *sfxFileEnd; + VgaPointersEntry() { memset(this, 0, sizeof(*this)); } +}; + +struct VgaSprite { + uint16 id; + uint16 image; + uint16 palette; + uint16 x, y; /* actually signed numbers */ + uint16 flags; + uint16 priority; + uint16 windowNum, zoneNum; + VgaSprite() { memset(this, 0, sizeof(*this)); } +}; + +struct VgaSleepStruct { + uint16 ident; + const byte *code_ptr; + uint16 sprite_id; + uint16 cur_vga_file; + VgaSleepStruct() { memset(this, 0, sizeof(*this)); } +}; + +struct VgaTimerEntry { + int16 delay; + const byte *script_pointer; + uint16 sprite_id; + uint16 cur_vga_file; + int32 param; + VgaTimerEntry() { memset(this, 0, sizeof(*this)); } +}; + +enum SIMONGameType { + GType_ELVIRA = 0, + GType_ELVIRA2 = 1, + GType_WW = 2, + GType_SIMON1 = 3, + GType_SIMON2 = 4, + GType_FF = 6, + GType_PP = 7 +}; + +struct GameFileDescription { + const char *fileName; + uint16 fileType; + const char *md5; +}; + +struct GameDescription { + const char *name; + SIMONGameType gameType; + GameIds gameId; + const char *extra; + int filesCount; + GameFileDescription *filesDescriptions; + uint32 features; + Common::Language language; + Common::Platform platform; +}; + +struct GameSpecificSettings; + +class Debugger; + +class SimonEngine : public Engine { + friend class Debugger; + friend class MoviePlayer; + + GUI::Debugger *getDebugger(); + + typedef void (SimonEngine::*OpcodeProc) (); + void setupOpcodes(); + const OpcodeProc *_opcode_table; + int _numOpcodes; + + typedef void (SimonEngine::*VgaOpcodeProc) (); + void setupVgaOpcodes(); + const VgaOpcodeProc *_vga_opcode_table; + +public: + GameDescription *_gameDescription; + + bool initGame(void); + void setupGame(); + + int getGameId() const { return _gameDescription->gameId; } + int getGameType() const { return _gameDescription->gameType; } + uint32 getFeatures() const { return _gameDescription->features; } + Common::Language getLanguage() const { return _gameDescription->language; } + Common::Platform getPlatform() const { return _gameDescription->platform; } + const char *getFileName(int type) const { + for (int i = 0; i < _gameDescription->filesCount; i++) { + if (_gameDescription->filesDescriptions[i].fileType == type) + return _gameDescription->filesDescriptions[i].fileName; + } + error("getFileName: Invalid type %d", type); + } + +protected: + void playSting(uint a); + + const byte *_vcPtr; /* video code ptr */ + uint16 _vc_get_out_of_code; + + + uint32 *_gameOffsetsPtr; + + uint _vgaBaseDelay; + uint _tableIndexBase; + uint _textIndexBase; + uint _numVideoOpcodes; + uint _vgaMemSize; + uint _tableMemSize; + uint _musicIndexBase; + uint _soundIndexBase; + uint _numVars; + const GameSpecificSettings *gss; + + byte _keyPressed; + + typedef enum { + FORMAT_NONE, + FORMAT_MP3, + FORMAT_WAV, + FORMAT_VOC + } SoundFormat; + + Common::File *_gameFile; + + byte *_strippedTxtMem; + byte *_textMem; + uint _textSize; + uint _stringTabNum, _stringTabPos, _stringtab_numalloc; + byte **_stringTabPtr; + + Item **_itemArrayPtr; + uint _itemArraySize; + uint _itemArrayInited; + + byte *_itemHeapPtr; + uint _itemHeapCurPos; + uint _itemHeapSize; + + byte *_iconFilePtr; + + const byte *_codePtr; + + byte **_localStringtable; + uint _stringIdLocalMin, _stringIdLocalMax; + + byte *_roomsList; + + byte *_xtblList; + byte *_xtablesHeapPtrOrg; + uint _xtablesHeapCurPosOrg; + Subroutine *_xsubroutineListOrg; + + byte *_tblList; + byte *_tablesHeapPtr, *_tablesHeapPtrOrg, *_tablesheapPtrNew; + uint _tablesHeapSize, _tablesHeapCurPos, _tablesHeapCurPosOrg; + uint _tablesHeapCurPosNew; + Subroutine *_subroutineListOrg; + + Subroutine *_subroutineList; + uint _subroutine; + + uint _dxSurfacePitch; + + uint _recursionDepth; + + uint32 _lastVgaTick; + + uint16 _marks; + + bool _scriptVar2; + bool _runScriptReturn1; + bool _runScriptCondition[40]; + int _runScriptReturn[40]; + bool _skipVgaWait; + bool _noParentNotify; + bool _beardLoaded; + bool _hitarea_unk_3; + bool _mortalFlag; + bool _updateScreen; + bool _usePaletteDelay; + bool _syncFlag2; + bool _inCallBack; + bool _cepeFlag; + byte _copyPartialMode; + bool _fastMode; + bool _useBackGround; + + uint16 _debugMode; + uint16 _language; + bool _pause; + bool _startMainScript; + bool _continousMainScript; + bool _startVgaScript; + bool _continousVgaScript; + bool _drawImagesDebug; + bool _dumpImages; + bool _speech; + bool _subtitles; + bool _vgaVar9; + int16 _chanceModifier; + bool _restoreWindow6; + int _scrollX, _scrollXMax, _scrollWidth; + int _scrollY, _scrollYMax, _scrollHeight; + int _scrollCount, _scrollFlag; + const byte *_scrollImage; + byte _boxStarHeight; + + uint16 _hyperLink, _newLines; + uint16 _oracleMaxScrollY, _noOracleScroll; + uint16 _interactY; + + int16 _scriptVerb, _scriptNoun1, _scriptNoun2; + int16 _scriptAdj1, _scriptAdj2; + + uint16 _curWindow; + WindowBlock *_textWindow; + + Item *_subjectItem, *_objectItem; + Item *_currentPlayer; + + Item *_hitAreaObjectItem; + HitArea *_lastHitArea; + HitArea *_lastNameOn; + HitArea *_lastHitArea3; + Item *_hitAreaSubjectItem; + HitArea *_currentVerbBox, *_lastVerbOn; + uint _needHitAreaRecalc; + uint _verbHitArea; + uint16 _defaultVerb; + uint _currentBoxNumber; + uint _iOverflow; + + uint16 _windowNum; + + uint _printCharCurPos, _printCharMaxPos, _printCharPixelCount; + uint _numLettersToPrint; + + uint _numTextBoxes; + + uint _lastTime; + uint32 _clockStopped, _gameStoppedClock; + time_t _timeStore; + + TimeEvent *_firstTimeStruct, *_pendingDeleteTimeEvent; + + int _mouseX, _mouseY; + int _mouseXOld, _mouseYOld; + + enum { + kMaxCursorWidth = 40, + kMaxCursorHeight = 40 + }; + + byte _mouseData[kMaxCursorWidth * kMaxCursorHeight]; + byte _animatePointer; + byte _mouseCursor, _mouseAnim, _mouseAnimMax; + byte _currentMouseCursor, _currentMouseAnim; + byte _oldMouseCursor, _oldMouseAnimMax; + uint _mouseHideCount; + bool _mouseToggle; + + byte _leftButtonDown; + byte _rightButtonDown; + bool _noRightClick; + + Item *_dummyItem1; + Item *_dummyItem2; + Item *_dummyItem3; + + volatile uint16 _lockWord; + uint16 _scrollUpHitArea; + uint16 _scrollDownHitArea; + + bool _fastFadeOutFlag; + bool _unkPalFlag; + byte _paletteFlag; + uint _fastFadeCount; + volatile uint16 _fastFadeInFlag; + + int _screenWidth, _screenHeight; + + uint16 _noOverWrite; + bool _rejectBlock; + + bool _exitCutscene; + + uint _soundFileId; + int16 _lastMusicPlayed; + int16 _nextMusicToPlay; + + bool _showPreposition; + bool _showMessageFlag; + + uint _vgaSpriteChanged; + + byte *_vgaMemPtr, *_vgaMemEnd, *_vgaMemBase; + byte *_vgaFrozenBase, *_vgaRealBase; + byte *_zoneBuffers; + + byte *_curVgaFile1; + byte *_curVgaFile2; + byte *_curSfxFile; + + uint16 _syncCount, _timer5, _timer4; + + uint16 _frameRate; + + uint16 _zoneNumber; + uint16 _vgaWaitFor, _lastVgaWaitFor; + uint16 _vgaCurSpriteId, _vgaCurZoneNum; + uint16 _vgaCurSpritePriority; + + int16 _baseY; + float _scale; + Common::Rect _feebleRect; + int _scaleX, _scaleY, _scaleWidth, _scaleHeight; + + VgaTimerEntry *_nextVgaTimerToProcess; + + Item *_objectArray[20]; + Item *_itemStore[20]; + + uint16 _shortText[40]; + uint16 _shortTextX[40]; + uint16 _shortTextY[40]; + uint16 _longText[40]; + uint16 _longSound[40]; + + uint16 _bitArray[128]; + uint16 _bitArrayTwo[16]; + uint16 _bitArrayThree[16]; + int16 *_variableArray; + int16 *_variableArray2; + int16 *_variableArrayPtr; + + WindowBlock *_windowArray[16]; + + byte _fcsData1[8]; + bool _fcsData2[8]; + + TextLocation _textLocation1, _textLocation2, _textLocation3, _textLocation4; + + int _freeStringSlot; + + byte _stringReturnBuffer[2][180]; + + HitArea _hitAreas[250]; + + VgaPointersEntry _vgaBufferPointers[450]; + VgaSprite _vgaSprites[180]; + VgaSleepStruct _vgaSleepStructs[60]; + + const uint16 *_pathFindArray[100]; + + uint8 _pathValues[400]; + uint16 _PVCount; + uint16 _GPVCount; + + uint8 _pathValues1[400]; + uint16 _PVCount1; + uint16 _GPVCount1; + + uint8 _currentPalette[1024]; + uint8 _displayPalette[1024]; + + byte _videoBuf1[3000]; + + VgaTimerEntry _vgaTimerList[900]; + + WindowBlock *_windowList; + + byte _lettersToPrintBuf[80]; + + MidiPlayer midi; + bool _native_mt32; + + int _vgaTickCounter; + + MoviePlayer *_moviePlay; + + Sound *_sound; + + bool _effectsPaused; + bool _ambientPaused; + bool _musicPaused; + + Debugger *_debugger; + + int _saveLoadRowCurPos; + int _numSaveGameRows; + bool _saveDialogFlag; + bool _saveOrLoad; + bool _saveLoadEdit; + + byte _saveLoadType, _saveLoadSlot; + char _saveLoadName[108]; + + int _sdlMouseX, _sdlMouseY; + + byte *_backGroundBuf; + byte *_frontBuf; + byte *_backBuf; + byte *_scaleBuf; + + Common::RandomSource _rnd; + + const byte *_vc10BasePtrOld; + byte _hebrewCharWidths[32]; + +public: + SimonEngine(OSystem *syst); + virtual ~SimonEngine(); + +protected: + uint16 to16Wrapper(uint value); + uint16 readUint16Wrapper(const void *src); + uint32 readUint32Wrapper(const void *src); + + int allocGamePcVars(Common::File *in); + void setUserFlag(Item *item, int a, int b); + void createPlayer(); + void allocateStringTable(int num); + void setupStringTable(byte *mem, int num); + void setupLocalStringTable(byte *mem, int num); + void readGamePcText(Common::File *in); + void readItemChildren(Common::File *in, Item *item, uint tmp); + void readItemFromGamePc(Common::File *in, Item *item); + void loadGamePcFile(); + void decompressData(const char *srcName, byte *dst, uint32 offset, uint32 srcSize, uint32 dstSize); + void loadOffsets(const char *filename, int number, uint32 &file, uint32 &offset, uint32 &compressedSize, uint32 &size); + void loadSound(uint sound, int pan, int vol, uint type); + void loadVoice(uint speechId); + + void paletteFadeOut(byte *palPtr, uint num, uint size); + + byte *allocateItem(uint size); + byte *allocateTable(uint size); + void alignTableMem(); + + Child *findChildOfType(Item *i, uint child); + Child *allocateChildBlock(Item *i, uint type, uint size); + + void allocItemHeap(); + void allocTablesHeap(); + + Subroutine *createSubroutine(uint a); + void readSubroutine(Common::File *in, Subroutine *sub); + SubroutineLine *createSubroutineLine(Subroutine *sub, int a); + void readSubroutineLine(Common::File *in, SubroutineLine *new_table, Subroutine *sub); + byte *readSingleOpcode(Common::File *in, byte *ptr); + void readSubroutineBlock(Common::File *in); + + Subroutine *getSubroutineByID(uint subroutine_id); + + /* used in debugger */ + void dumpSubroutines(); + void dumpSubroutine(Subroutine *sub); + void dumpSubroutineLine(SubroutineLine *sl, Subroutine *sub); + const byte *dumpOpcode(const byte *p); + + int startSubroutine(Subroutine *sub); + int startSubroutineEx(Subroutine *sub); + + bool checkIfToRunSubroutineLine(SubroutineLine *sl, Subroutine *sub); + + int runScript(); + + Item *getNextItemPtr(); + uint getNextItemID(); + uint getItem1ID() {return 1;} + Item *me(); + Item *actor(); + + byte getByte(); + int getNextWord(); + + uint getNextVarContents(); + uint getVarWrapper(); + uint getVarOrWord(); + uint getVarOrByte(); + uint readVariable(uint variable); + void writeNextVarContents(uint16 contents); + void writeVariable(uint variable, uint16 contents); + + void setItemParent(Item *item, Item *parent); + + uint itemPtrToID(Item *id); + + Item *derefItem(uint item); + void setItemState(Item *item, int value); + + void showMessageFormat(const char *s, ...); + const byte *getStringPtrByID(uint stringId); + const byte *getLocalStringByID(uint stringId); + uint getNextStringID(); + + void addTimeEvent(uint timeout, uint subroutine_id); + void delTimeEvent(TimeEvent *te); + + bool isRoom(Item *item); + bool isObject(Item *item); + + void itemChildrenChanged(Item *item); + void unlinkItem(Item *item); + void linkItem(Item *item, Item *parent); + + void stopAnimateSimon1(uint a); + void stopAnimateSimon2(uint a, uint b); + + void enableBox(uint hitarea); + void disableBox(uint hitarea); + void moveBox(uint hitarea, int x, int y); + bool isBoxDead(uint hitarea); + void undefineBox(uint hitarea); + void defineBox(int id, int x, int y, int width, int height, int flags, int verb, Item *item_ptr); + HitArea *findEmptyHitArea(); + + void resetVerbs(); + void setVerb(HitArea * ha); + void hitarea_leave(HitArea * ha, bool state = false); + void leaveHitAreaById(uint hitarea_id); + + void sendSync(uint a); + void waitForSync(uint a); + + uint getOffsetOfChild2Param(SubObject *child, uint prop); + void setTextColor(uint color); + void scriptMouseOn(); + void scriptMouseOff(); + void freezeBottom(); + void unfreezeBottom(); + + TextLocation *getTextLocation(uint a); + void setup_cond_c_helper(); + + void checkLinkBox(); + void hyperLinkOn(uint16 x); + void hyperLinkOff(); + void linksUp(); + void linksDown(); + void oracleTextUp(); + void oracleTextDown(); + void listSaveGames(int n); + void saveUserGame(int slot); + void windowBackSpace(WindowBlock *window); + + void oracleLogo(); + void scrollOracle(); + void scrollOracleUp(); + void scrollOracleDown(); + void swapCharacterLogo(); + + void mouseOff(); + void mouseOn(); + + bool loadTablesIntoMem(uint subr_id); + bool loadTablesOldIntoMem(uint subr_id); + bool loadTablesNewIntoMem(uint subr_id); + bool loadXTablesIntoMem(uint subr_id); + void loadTextIntoMem(uint stringId); + + bool loadRoomItems(uint item); + + uint loadTextFile(const char *filename, byte *dst); + Common::File *openTablesFile(const char *filename); + void closeTablesFile(Common::File *in); + + uint loadTextFile_simon1(const char *filename, byte *dst); + Common::File *openTablesFile_simon1(const char *filename); + + uint loadTextFile_gme(const char *filename, byte *dst); + Common::File *openTablesFile_gme(const char *filename); + + void invokeTimeEvent(TimeEvent *te); + bool kickoffTimeEvents(); + void killAllTimers(); + + void endCutscene(); + void runSubroutine101(); + + void checkUp(WindowBlock *window); + void checkDown(WindowBlock *window); + void inventoryUp(WindowBlock *window); + void inventoryDown(WindowBlock *window); + + WindowBlock *openWindow(uint x, uint y, uint w, uint h, uint flags, uint fill_color, uint text_color); + uint getWindowNum(WindowBlock *window); + void clearWindow(WindowBlock *window); + void changeWindow(uint a); + void closeWindow(uint a); + void windowPutChar(WindowBlock *window, byte c, byte b = 0); + + HitArea *findBox(uint hitarea_id); + void boxController(uint x, uint y, uint mode); + void handleVerbClicked(uint verb); + void clearName(); + void displayName(HitArea * ha); + void resetNameWindow(); + void displayBoxStars(); + void hitarea_stuff(); + void invertBox_FF(HitArea *ha, bool state); + void invertBox(HitArea * ha, byte a, byte b, byte c, byte d); + + void handleMouseMoved(); + void pollMouseXY(); + void drawMousePointer(); + void drawMousePointer_FF(); + void drawMousePart(int image, byte x, byte y); + + void defineArrowBoxes(WindowBlock *window); + void removeArrows(WindowBlock *window, uint num); + + void draw_icon_c(WindowBlock *window, uint icon, uint x, uint y); + bool has_item_childflag_0x10(Item *item); + uint itemGetIconNumber(Item *item); + uint setupIconHitArea(WindowBlock *window, uint num, uint x, uint y, Item *item_ptr); + void drawIconArray(uint i, Item *item_ptr, int line, int classMask); + void drawIconArray_FF(uint i, Item *item_ptr, int line, int classMask); + void drawIconArray_Simon(uint i, Item *item_ptr, int line, int classMask); + void removeIconArray(uint num); + + void loadIconData(); + void loadIconFile(); + + void processSpecialKeys(); + void hitarea_stuff_helper(); + + void permitInput(); + + uint getFeebleFontSize(byte chr); + void showmessage_helper_3(uint a, uint b); + void showmessage_print_char(byte chr); + + void set_video_mode_internal(uint16 mode, uint16 vga_res_id); + + void loadZone(uint vga_res); + + void loadSprite(uint windowNum, uint vga_res, uint vga_sprite_id, uint x, uint y, uint palette); + void playSpeech(uint speech_id, uint vga_sprite_id); + void skipSpeech(); + + bool printNameOf(Item *item, uint x, uint y); + bool printTextOf(uint a, uint x, uint y); + void printVerbOf(uint hitarea_id); + void showActionString(const byte *string); + + void printScreenText(uint vga_sprite_id, uint color, const char *string_ptr, int16 x, int16 y, int16 width); + void sendInteractText(uint16 num, const char *fmt, ...); + void printInteractText(uint16 num, const char *string); + + void renderStringAmiga(uint vga_sprite_id, uint color, uint width, uint height, const char *txt); + void renderString(uint vga_sprite_id, uint color, uint width, uint height, const char *txt); + + byte *allocBlock(uint32 size); + void checkNoOverWrite(byte *end); + void checkRunningAnims(byte *end); + void checkAnims(uint a, byte *end); + void checkZonePtrs(byte *end); + void setZoneBuffers(); + + void runVgaScript(); + +public: + bool getBitFlag(uint bit); + void setBitFlag(uint bit, bool value); + + // Simon1/Simon2 video script opcodes + void vc1_fadeOut(); + void vc2_call(); + void vc3_loadSprite(); + void vc4_fadeIn(); + void vc5_skip_if_neq(); + void vc6_skip_ifn_sib_with_a(); + void vc7_skip_if_sib_with_a(); + void vc8_skip_if_parent_is(); + void vc9_skip_if_unk3_is(); + void vc10_draw(); + void vc11_clearPathFinder(); + void vc12_delay(); + void vc13_addToSpriteX(); + void vc14_addToSpriteY(); + void vc15_sync(); + void vc16_waitSync(); + void vc17_setPathfinderItem(); + void vc18_jump(); + void vc19_chain_to_script(); + void vc20_setRepeat(); + void vc21_endRepeat(); + void vc22_setSpritePalette(); + void vc23_setSpritePriority(); + void vc24_setSpriteXY(); + void vc25_halt_sprite(); + void vc26_setSubWindow(); + void vc27_resetSprite(); + void vc28_dummy_op(); + void vc29_stopAllSounds(); + void vc30_setFrameRate(); + void vc31_setWindow(); + void vc32_copyVar(); + void vc33_setMouseOn(); + void vc34_setMouseOff(); + void vc35_clearWindow(); + void vc36_setWindowImage(); + void vc37_addToSpriteY(); + void vc38_skipIfVarZero(); + void vc39_setVar(); + void vc40(); + void vc41(); + void vc42_delayIfNotEQ(); + void vc43_skipIfBitClear(); + void vc44_skipIfBitSet(); + void vc45_setSpriteX(); + void vc46_setSpriteY(); + void vc47_addToVar(); + void vc48_setPathFinder(); + void vc49_setBit(); + void vc50_clearBit(); + void vc51_enableBox(); + void vc52_playSound(); + void vc53_panSFX(); + void vc54_no_op(); + void vc55_moveBox(); + void vc56_delay(); + void vc57_blackPalette(); + void vc58(); + void vc59(); + void vc60_killSprite(); + void vc61_setMaskImage(); + void vc62_fastFadeOut(); + void vc63_fastFadeIn(); + + // Simon2 specific Video Script Opcodes + void vc64_skipIfSpeechEnded(); + void vc65_slowFadeIn(); + void vc66_skipIfNotEqual(); + void vc67_skipIfGE(); + void vc68_skipIfLE(); + void vc69_playTrack(); + void vc70_queueMusic(); + void vc71_checkMusicQueue(); + void vc72_play_track_2(); + void vc73_setMark(); + void vc74_clearMark(); + + // Feeble specific Video Script Opcodes + void vc75_setScale(); + void vc76_setScaleXOffs(); + void vc77_setScaleYOffs(); + void vc78_computeXY(); + void vc79_computePosNum(); + void vc80_setOverlayImage(); + void vc81_setRandom(); + void vc82_getPathValue(); + void vc83_playSoundLoop(); + void vc84_stopSoundLoop(); + + void setScriptCondition(bool cond); + bool getScriptCondition(); + void setScriptReturn(int ret); + int getScriptReturn(); + + // Opcodes, Simon 1 and later + void o_at(); + void o_notAt(); + void o_carried(); + void o_notCarried(); + void o_isAt(); + void o_zero(); + void o_notZero(); + void o_eq(); + void o_notEq(); + void o_gt(); + void o_lt(); + void o_eqf(); + void o_notEqf(); + void o_ltf(); + void o_gtf(); + void o_chance(); + void o_isRoom(); + void o_isObject(); + void o_state(); + void o_oflag(); + void o_destroy(); + void o_place(); + void o_copyff(); + void o_clear(); + void o_let(); + void o_add(); + void o_sub(); + void o_addf(); + void o_subf(); + void o_mul(); + void o_div(); + void o_mulf(); + void o_divf(); + void o_mod(); + void o_modf(); + void o_random(); + void o_goto(); + void o_oset(); + void o_oclear(); + void o_putBy(); + void o_inc(); + void o_dec(); + void o_setState(); + void o_print(); + void o_message(); + void o_msg(); + void o_addTextBox(); + void o_setShortText(); + void o_setLongText(); + void o_end(); + void o_done(); + void o_process(); + void o_when(); + void o_if1(); + void o_if2(); + void o_isCalled(); + void o_is(); + void o_debug(); + void o_comment(); + void o_haltAnimation(); + void o_restartAnimation(); + void o_getParent(); + void o_getNext(); + void o_getChildren(); + void o_picture(); + void o_loadZone(); + void o_killAnimate(); + void o_defWindow(); + void o_window(); + void o_cls(); + void o_closeWindow(); + void o_addBox(); + void o_delBox(); + void o_enableBox(); + void o_disableBox(); + void o_moveBox(); + void o_doIcons(); + void o_isClass(); + void o_setClass(); + void o_unsetClass(); + void o_waitSync(); + void o_sync(); + void o_defObj(); + void o_here(); + void o_doClassIcons(); + void o_waitEndTune(); + void o_ifEndTune(); + void o_setAdjNoun(); + void o_saveUserGame(); + void o_loadUserGame(); + void o_stopTune(); + void o_pauseGame(); + void o_copysf(); + void o_restoreIcons(); + void o_freezeZones(); + void o_placeNoIcons(); + void o_clearTimers(); + void o_setDollar(); + void o_isBox(); + void o_doTable(); + void o_storeItem(); + void o_getItem(); + void o_bSet(); + void o_bClear(); + void o_bZero(); + void o_bNotZero(); + void o_getOValue(); + void o_setOValue(); + void o_ink(); + void o_screenTextBox(); + void o_screenTextMsg(); + void o_playEffect(); + void o_getDollar2(); + void o_isAdjNoun(); + void o_b2Set(); + void o_b2Clear(); + void o_b2Zero(); + void o_b2NotZero(); + void o_lockZones(); + void o_unlockZones(); + void o_getPathPosn(); + void o_scnTxtLongText(); + void o_mouseOn(); + void o_unloadZone(); + void o_unfreezeZones(); + + uint16 getDoorState(Item *item, uint16 d); + uint16 getExitOf(Item *item, uint16 d); + + // Opcodes, Waxworks only + void oww_whereTo(); + void oww_menu(); + void oww_textMenu(); + void oww_ifDoorOpen(); + + // Opcodes, Simon 1 only + void o1_printLongText(); + void o1_rescan(); + void o1_animate(); + void o1_stopAnimate(); + void o1_playTune(); + void o1_screenTextPObj(); + void o1_mouseOff(); + void o1_loadBeard(); + void o1_unloadBeard(); + void o1_loadStrings(); + void o1_specialFade(); + + // Opcodes, Simon 2 and later + void o2_printLongText(); + void o2_rescan(); + void o2_animate(); + void o2_stopAnimate(); + void o2_playTune(); + void o2_screenTextPObj(); + void o2_mouseOff(); + void o2_isShortText(); + void o2_clearMarks(); + void o2_waitMark(); + + // Opcodes, Feeble Files only + void o3_chance(); + void o3_jumpOut(); + void o3_addTextBox(); + void o3_printLongText(); + void o3_addBox(); + void o3_oracleTextDown(); + void o3_oracleTextUp(); + void o3_ifTime(); + void o3_playTune(); + void o3_setTime(); + void o3_saveUserGame(); + void o3_loadUserGame(); + void o3_listSaveGames(); + void o3_checkCD(); + void o3_screenTextBox(); + void o3_isAdjNoun(); + void o3_hyperLinkOn(); + void o3_hyperLinkOff(); + void o3_checkPaths(); + void o3_screenTextPObj(); + void o3_mouseOff(); + void o3_loadVideo(); + void o3_playVideo(); + void o3_centreScroll(); + void o3_resetPVCount(); + void o3_setPathValues(); + void o3_stopClock(); + void o3_restartClock(); + void o3_setColour(); + void o3_b3Set(); + void o3_b3Clear(); + void o3_b3Zero(); + void o3_b3NotZero(); + + // Opcodes, Puzzle Pack only + void o4_opcode30(); + void o4_checkTiles(); + void o4_opcode38(); + void o4_loadHiScores(); + void o4_checkHiScores(); + void o4_loadUserGame(); + void o4_saveOopsPosition(); + void o4_resetGameTime(); + void o4_resetPVCount(); + void o4_setPathValues(); + +protected: + void drawImages(VC10_state *state); + void drawImages_Feeble(VC10_state *state); + bool drawImages_clip(VC10_state *state); + void scaleClip(int16 h, int16 w, int16 y, int16 x, int16 scrollY); + void horizontalScroll(VC10_state *state); + void verticalScroll(VC10_state *state); + + int vcReadVarOrWord(); + uint vcReadNextWord(); + uint vcReadNextByte(); + uint vcReadVar(uint var); + void vcWriteVar(uint var, int16 value); + void vcSkipNextInstruction(); + + int getScale(int16 y, int16 x); + void checkScrollX(int16 x, int16 xpos); + void checkScrollY(int16 y, int16 ypos); + void centreScroll(); + + bool itemIsSiblingOf(uint16 val); + bool itemIsParentOf(uint16 a, uint16 b); + bool vc_maybe_skip_proc_1(uint16 a, int16 b); + + void addVgaEvent(uint16 num, const byte *code_ptr, uint16 cur_sprite, uint16 curZoneNum, int32 param = 0); + void deleteVgaEvent(VgaTimerEntry * vte); + void processVgaEvents(); + void animateEvent(const byte *code_ptr, uint16 curZoneNum, uint16 cur_sprite); + void panEvent(uint16 curZoneNum, uint16 cur_sprite, int32 param); + void scrollEvent(); + + VgaSprite *findCurSprite(); + + bool isSpriteLoaded(uint16 id, uint16 zoneNum); + + void resetWindow(WindowBlock *window); + void delete_hitarea_by_index(uint index); + + void windowPutChar(uint a); + + void restoreWindow(WindowBlock *window); + void colorWindow(WindowBlock *window); + + void restoreBlock(uint h, uint w, uint y, uint x); + + byte *getFrontBuf(); + byte *getBackBuf(); + byte *getBackGround(); + byte *getScaleBuf(); + + byte *loadVGAFile(uint id, uint type, uint32 &dstSize); + void loadSimonVGAFile(uint vga_id); + + int init(); + int go(); + + void openGameFile(); + void readGameFile(void *dst, uint32 offs, uint32 size); + + void timer_callback(); + void timer_proc1(); + + void animateSprites(); + void animateSpritesDebug(); + void animateSpritesByY(); + + void dx_clear_surfaces(uint num_lines); + void dx_update_screen_and_palette(); + + void dump_video_script(const byte *src, bool one_opcode_only); + void dump_vga_file(const byte *vga); + void dump_vga_script(const byte *ptr, uint res, uint sprite_id); + void dump_vga_script_always(const byte *ptr, uint res, uint sprite_id); + void dump_vga_bitmaps(const byte *vga, byte *vga1, int res); + void dump_single_bitmap(int file, int image, const byte *offs, int w, int h, byte base); + void dump_bitmap(const char *filename, const byte *offs, int w, int h, int flags, const byte *palette, byte base); + + void clearBackFromTop(uint lines); + void fillFrontFromBack(uint x, uint y, uint w, uint h); + void fillBackGroundFromBack(uint lines); + void fillBackFromFront(uint x, uint y, uint w, uint h); + + void print_char_helper_1(const byte *src, uint len); + void print_char_helper_5(WindowBlock *window); + + void quickLoadOrSave(); + void shutdown(); + + byte *vc10_uncompressFlip(const byte *src, uint w, uint h); + byte *vc10_flip(const byte *src, uint w, uint h); + + Item *getNextItemPtrStrange(); + + bool saveGame(uint slot, char *caption); + bool loadGame(uint slot); + + void openTextWindow(); + void tidyIconArray(uint i); + + void video_putchar_newline(WindowBlock *window); + void video_putchar_drawchar(WindowBlock *window, uint x, uint y, byte chr); + + void loadMusic(uint music); + void checkTimerCallback(); + void delay(uint delay); + void pause(); + + void waitForMark(uint i); + void scrollScreen(); + + void decodeColumn(byte *dst, const byte *src, int height); + void decodeRow(byte *dst, const byte *src, int width); + void hitarea_stuff_helper_2(); + void fastFadeIn(); + void slowFadeIn(); + + void vc_kill_sprite(uint file, uint sprite); + + void set_dummy_cursor(); + + void set_volume(int volume); + + void userGame(bool load); + void disableFileBoxes(); + int userGameGetKey(bool *b, char *buf); + void userGameBackSpace(WindowBlock *window, int x, byte b = 0); + void listSaveGames(char *buf); + void fileError(WindowBlock *window, bool save_error); + + int countSaveGames(); + int displaySaveGameList(int curpos, bool load, char *dst); + + char *genSaveName(int slot); +}; + +} // End of namespace Simon + +#endif diff --git a/engines/agos/sound.cpp b/engines/agos/sound.cpp new file mode 100644 index 0000000000..42c2df1723 --- /dev/null +++ b/engines/agos/sound.cpp @@ -0,0 +1,714 @@ +/* ScummVM - Scumm Interpreter + * Copyright (C) 2001 Ludvig Strigeus + * Copyright (C) 2001-2006 The ScummVM project + * + * 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. + * + * $URL$ + * $Id$ + * + */ + +#include "common/stdafx.h" + +#include "common/file.h" +#include "common/util.h" + +#include "agos/agos.h" +#include "agos/sound.h" + +#include "sound/adpcm.h" +#include "sound/audiostream.h" +#include "sound/flac.h" +#include "sound/mp3.h" +#include "sound/voc.h" +#include "sound/vorbis.h" +#include "sound/wave.h" + +using Common::File; + +namespace Simon { + +#define SOUND_BIG_ENDIAN true + +class BaseSound { +protected: + File *_file; + uint32 *_offsets; + Audio::Mixer *_mixer; + bool _freeOffsets; + +public: + BaseSound(Audio::Mixer *mixer, File *file, uint32 base = 0, bool bigendian = false); + BaseSound(Audio::Mixer *mixer, File *file, uint32 *offsets, bool bigendian = false); + virtual ~BaseSound(); + virtual void playSound(uint sound, Audio::SoundHandle *handle, byte flags) = 0; +}; + +class WavSound : public BaseSound { +public: + WavSound(Audio::Mixer *mixer, File *file, uint32 base = 0, bool bigendian = false) : BaseSound(mixer, file, base, bigendian) {}; + WavSound(Audio::Mixer *mixer, File *file, uint32 *offsets) : BaseSound(mixer, file, offsets) {}; + void playSound(uint sound, Audio::SoundHandle *handle, byte flags); +}; + +class VocSound : public BaseSound { +public: + VocSound(Audio::Mixer *mixer, File *file, uint32 base = 0, bool bigendian = false) : BaseSound(mixer, file, base, bigendian) {}; + void playSound(uint sound, Audio::SoundHandle *handle, byte flags); +}; +class RawSound : public BaseSound { +public: + RawSound(Audio::Mixer *mixer, File *file, uint32 base = 0, bool bigendian = false) : BaseSound(mixer, file, base, bigendian) {}; + void playSound(uint sound, Audio::SoundHandle *handle, byte flags); +}; + +BaseSound::BaseSound(Audio::Mixer *mixer, File *file, uint32 base, bool bigendian) { + _mixer = mixer; + _file = file; + + uint res = 0; + uint32 size; + + _file->seek(base + sizeof(uint32), SEEK_SET); + if (bigendian) + size = _file->readUint32BE(); + else + size = _file->readUint32LE(); + + // The Feeble Files uses set amount of voice offsets + if (size == 0) + size = 40000; + + res = size / sizeof(uint32); + + _offsets = (uint32 *)malloc(size + sizeof(uint32)); + _freeOffsets = true; + + _file->seek(base, SEEK_SET); + + if (_file->read(_offsets, size) != size) + error("BaseSound: Can't read offsets"); + + for (uint i = 0; i < res; i++) { +#if defined(SCUMM_BIG_ENDIAN) + if (!(bigendian)) + _offsets[i] = FROM_LE_32(_offsets[i]); +#endif + if (bigendian) + _offsets[i] = TO_BE_32(_offsets[i]); + _offsets[i] += base; + } + + // only needed for mp3 + _offsets[res] = _file->size(); +} + +BaseSound::BaseSound(Audio::Mixer *mixer, File *file, uint32 *offsets, bool bigendian) { + _mixer = mixer; + _file = file; + _offsets = offsets; + _freeOffsets = false; +} + +BaseSound::~BaseSound() { + if (_freeOffsets) + free(_offsets); + delete _file; +} + +void WavSound::playSound(uint sound, Audio::SoundHandle *handle, byte flags) { + if (_offsets == NULL) + return; + + _file->seek(_offsets[sound], SEEK_SET); + + byte wavFlags; + int size, rate; + if (!Audio::loadWAVFromStream(*_file, size, rate, wavFlags)) + error("playSound: Not a valid WAV file"); + + flags |= wavFlags; + + byte *buffer = (byte *)malloc(size); + // Check whether malloc was successful. + // TODO: Maybe we can handle this more graceful, by reverting to a smaller + // buffer and reading the audio data one piece at a time? + if (buffer) { + _file->read(buffer, size); + _mixer->playRaw(handle, buffer, size, rate, flags | Audio::Mixer::FLAG_AUTOFREE); + } +} + +void VocSound::playSound(uint sound, Audio::SoundHandle *handle, byte flags) { + if (_offsets == NULL) + return; + + _file->seek(_offsets[sound], SEEK_SET); + + int size, rate; + byte *buffer = Audio::loadVOCFromStream(*_file, size, rate); + _mixer->playRaw(handle, buffer, size, rate, flags | Audio::Mixer::FLAG_AUTOFREE); +} + +void RawSound::playSound(uint sound, Audio::SoundHandle *handle, byte flags) { + if (_offsets == NULL) + return; + + _file->seek(_offsets[sound], SEEK_SET); + + uint size = _file->readUint32BE(); + byte *buffer = (byte *)malloc(size); + // Check whether malloc was successful. + // TODO: Maybe we can handle this more graceful, by reverting to a smaller + // buffer and reading the audio data one piece at a time? + if (buffer) { + _file->read(buffer, size); + _mixer->playRaw(handle, buffer, size, 22050, flags | Audio::Mixer::FLAG_AUTOFREE); + } +} + +#ifdef USE_MAD +class MP3Sound : public BaseSound { +public: + MP3Sound(Audio::Mixer *mixer, File *file, uint32 base = 0) : BaseSound(mixer, file, base) {}; + void playSound(uint sound, Audio::SoundHandle *handle, byte flags); +}; + +void MP3Sound::playSound(uint sound, Audio::SoundHandle *handle, byte flags) +{ + if (_offsets == NULL) + return; + + _file->seek(_offsets[sound], SEEK_SET); + + int i = 1; + while (_offsets[sound + i] == _offsets[sound]) + i++; + + uint32 size = _offsets[sound + i] - _offsets[sound]; + + _mixer->playInputStream(Audio::Mixer::kSFXSoundType, handle, Audio::makeMP3Stream(_file, size)); +} +#endif + +#ifdef USE_VORBIS +class VorbisSound : public BaseSound { +public: + VorbisSound(Audio::Mixer *mixer, File *file, uint32 base = 0) : BaseSound(mixer, file, base) {}; + void playSound(uint sound, Audio::SoundHandle *handle, byte flags); +}; + +void VorbisSound::playSound(uint sound, Audio::SoundHandle *handle, byte flags) +{ + if (_offsets == NULL) + return; + + _file->seek(_offsets[sound], SEEK_SET); + + int i = 1; + while (_offsets[sound + i] == _offsets[sound]) + i++; + + uint32 size = _offsets[sound + i] - _offsets[sound]; + + _mixer->playInputStream(Audio::Mixer::kSFXSoundType, handle, Audio::makeVorbisStream(_file, size)); +} +#endif + +#ifdef USE_FLAC +class FlacSound : public BaseSound { +public: + FlacSound(Audio::Mixer *mixer, File *file, uint32 base = 0) : BaseSound(mixer, file, base) {}; + void playSound(uint sound, Audio::SoundHandle *handle, byte flags); +}; + +void FlacSound::playSound(uint sound, Audio::SoundHandle *handle, byte flags) +{ + if (_offsets == NULL) + return; + + _file->seek(_offsets[sound], SEEK_SET); + + int i = 1; + while (_offsets[sound + i] == _offsets[sound]) + i++; + + uint32 size = _offsets[sound + i] - _offsets[sound]; + + _mixer->playInputStream(Audio::Mixer::kSFXSoundType, handle, Audio::makeFlacStream(_file, size)); +} +#endif + +Sound::Sound(SimonEngine *vm, const GameSpecificSettings *gss, Audio::Mixer *mixer) + : _vm(vm), _mixer(mixer) { + _voice = 0; + _effects = 0; + + _effectsPaused = false; + _ambientPaused = false; + _sfx5Paused = false; + + _filenums = 0; + _lastVoiceFile = 0; + _offsets = 0; + + _hasEffectsFile = false; + _hasVoiceFile = false; + + _ambientPlaying = 0; + + if (_vm->getFeatures() & GF_TALKIE) { + loadVoiceFile(gss); + + if (_vm->getGameType() == GType_SIMON1) + loadSfxFile(gss); + } +} + +Sound::~Sound() { + delete _voice; + delete _effects; + + free(_filenums); + free(_offsets); +} + +void Sound::loadVoiceFile(const GameSpecificSettings *gss) { + // Game versions which use separate voice files + if (_vm->getGameType() == GType_FF || _vm->getGameId() == GID_SIMON1CD32) + return; + + char filename[16]; + File *file = new File(); + +#ifdef USE_FLAC + if (!_hasVoiceFile) { + sprintf(filename, "%s.fla", gss->speech_filename); + file->open(filename); + if (file->isOpen()) { + _hasVoiceFile = true; + _voice = new FlacSound(_mixer, file); + } + } +#endif +#ifdef USE_MAD + if (!_hasVoiceFile) { + sprintf(filename, "%s.mp3", gss->speech_filename); + file->open(filename); + if (file->isOpen()) { + _hasVoiceFile = true; + _voice = new MP3Sound(_mixer, file); + } + } +#endif +#ifdef USE_VORBIS + if (!_hasVoiceFile) { + sprintf(filename, "%s.ogg", gss->speech_filename); + file->open(filename); + if (file->isOpen()) { + _hasVoiceFile = true; + _voice = new VorbisSound(_mixer, file); + } + } +#endif + if (!_hasVoiceFile && _vm->getGameType() == GType_SIMON2) { + // for simon2 mac/amiga, only read index file + file->open("voices.idx"); + if (file->isOpen() == true) { + int end = file->size(); + _filenums = (uint16 *)malloc((end / 6 + 1) * 2); + _offsets = (uint32 *)malloc((end / 6 + 1) * 4); + + for (int i = 1; i <= end / 6; i++) { + _filenums[i] = file->readUint16BE(); + _offsets[i] = file->readUint32BE(); + } + _hasVoiceFile = true; + } + } + if (!_hasVoiceFile) { + sprintf(filename, "%s.wav", gss->speech_filename); + file->open(filename); + if (file->isOpen()) { + _hasVoiceFile = true; + _voice = new WavSound(_mixer, file); + } + } + if (!_hasVoiceFile) { + sprintf(filename, "%s.voc", gss->speech_filename); + file->open(filename); + if (file->isOpen()) { + _hasVoiceFile = true; + _voice = new VocSound(_mixer, file); + } + } + if (!_hasVoiceFile) { + sprintf(filename, "%s", gss->speech_filename); + file->open(filename); + if (file->isOpen()) { + _hasVoiceFile = true; + if (_vm->getGameType() == GType_PP) + _voice = new WavSound(_mixer, file); + else + _voice = new VocSound(_mixer, file); + } + } +} + +void Sound::loadSfxFile(const GameSpecificSettings *gss) { + char filename[16]; + File *file = new File(); + +#ifdef USE_MAD + if (!_hasEffectsFile) { + sprintf(filename, "%s.mp3", gss->effects_filename); + file->open(filename); + if (file->isOpen()) { + _hasEffectsFile = true; + _effects = new MP3Sound(_mixer, file); + } + } +#endif +#ifdef USE_VORBIS + if (!_hasEffectsFile) { + sprintf(filename, "%s.ogg", gss->effects_filename); + file->open(filename); + if (file->isOpen()) { + _hasEffectsFile = true; + _effects = new VorbisSound(_mixer, file); + } + } +#endif +#ifdef USE_FLAC + if (!_hasEffectsFile) { + sprintf(filename, "%s.fla", gss->effects_filename); + file->open(filename); + if (file->isOpen()) { + _hasEffectsFile = true; + _effects = new FlacSound(_mixer, file); + } + } +#endif + if (!_hasEffectsFile) { + sprintf(filename, "%s.voc", gss->effects_filename); + file->open(filename); + if (file->isOpen()) { + _hasEffectsFile = true; + _effects = new VocSound(_mixer, file); + } + } + if (!_hasEffectsFile) { + sprintf(filename, "%s", gss->effects_filename); + file->open(filename); + if (file->isOpen()) { + _hasEffectsFile = true; + _effects = new VocSound(_mixer, file); + } + } +} + +void Sound::readSfxFile(const char *filename) { + if (_hasEffectsFile) + return; + + stopAll(); + + File *file = new File(); + file->open(filename); + + if (file->isOpen() == false) { + error("readSfxFile: Can't load sfx file %s", filename); + } + + delete _effects; + if (_vm->getGameId() == GID_SIMON1CD32) { + _effects = new VocSound(_mixer, file, 0, SOUND_BIG_ENDIAN); + } else + _effects = new WavSound(_mixer, file); +} + +void Sound::loadSfxTable(File *gameFile, uint32 base) { + stopAll(); + + if (_vm->getPlatform() == Common::kPlatformWindows) + _effects = new WavSound(_mixer, gameFile, base); + else + _effects = new VocSound(_mixer, gameFile, base); +} + +void Sound::readVoiceFile(const char *filename) { + stopAll(); + + File *file = new File(); + file->open(filename); + + if (file->isOpen() == false) + error("readVoiceFile: Can't load voice file %s", filename); + + delete _voice; + _voice = new RawSound(_mixer, file, 0, SOUND_BIG_ENDIAN); +} + +void Sound::playVoice(uint sound) { + if (_filenums) { + if (_lastVoiceFile != _filenums[sound]) { + stopAll(); + + char filename[16]; + _lastVoiceFile = _filenums[sound]; + sprintf(filename, "voices%d.dat", _filenums[sound]); + File *file = new File(); + file->open(filename); + if (file->isOpen() == false) + error("playVoice: Can't load voice file %s", filename); + + delete _voice; + _voice = new WavSound(_mixer, file, _offsets); + } + } + + if (!_voice) + return; + + _mixer->stopHandle(_voiceHandle); + if (_vm->getGameType() == GType_PP) { + _voice->playSound(sound, &_voiceHandle, Audio::Mixer::FLAG_LOOP); + } else if (_vm->getGameType() == GType_FF || _vm->getGameId() == GID_SIMON1CD32) { + _voice->playSound(sound, &_voiceHandle, 0); + } else { + _voice->playSound(sound, &_voiceHandle, Audio::Mixer::FLAG_UNSIGNED); + } +} + +void Sound::playEffects(uint sound) { + if (!_effects) + return; + + if (_effectsPaused) + return; + + _effects->playSound(sound, &_effectsHandle, (_vm->getGameId() == GID_SIMON1CD32) ? 0 : Audio::Mixer::FLAG_UNSIGNED); +} + +void Sound::playAmbient(uint sound) { + if (!_effects) + return; + + if (sound == _ambientPlaying) + return; + + _ambientPlaying = sound; + + if (_ambientPaused) + return; + + _mixer->stopHandle(_ambientHandle); + _effects->playSound(sound, &_ambientHandle, Audio::Mixer::FLAG_LOOP|Audio::Mixer::FLAG_UNSIGNED); +} + +bool Sound::hasVoice() const { + return _hasVoiceFile; +} + +bool Sound::isVoiceActive() const { + return _mixer->isSoundHandleActive(_voiceHandle); +} + +void Sound::stopAllSfx() { + _mixer->stopHandle(_ambientHandle); + _mixer->stopHandle(_effectsHandle); + _mixer->stopHandle(_sfx5Handle); + _ambientPlaying = 0; +} + +void Sound::stopVoice() { + _mixer->stopHandle(_voiceHandle); +} + +void Sound::stopAll() { + _mixer->stopAll(); + _ambientPlaying = 0; +} + +void Sound::effectsPause(bool b) { + _effectsPaused = b; + _sfx5Paused = b; +} + +void Sound::ambientPause(bool b) { + _ambientPaused = b; + + if (_ambientPaused && _ambientPlaying) { + _mixer->stopHandle(_ambientHandle); + } else if (_ambientPlaying) { + uint tmp = _ambientPlaying; + _ambientPlaying = 0; + playAmbient(tmp); + } +} + +// Feeble Files specific +void Sound::playAmbientData(byte *soundData, uint sound, uint pan, uint vol) { + if (sound == _ambientPlaying) + return; + + _ambientPlaying = sound; + + if (_ambientPaused) + return; + + _mixer->stopHandle(_ambientHandle); + playSoundData(&_ambientHandle, soundData, sound, pan, vol, true); +} + +void Sound::playSfxData(byte *soundData, uint sound, uint pan, uint vol) { + if (_effectsPaused) + return; + + playSoundData(&_effectsHandle, soundData, sound, pan, vol, false); +} + +void Sound::playSfx5Data(byte *soundData, uint sound, uint pan, uint vol) { + if (_sfx5Paused) + return; + + _mixer->stopHandle(_sfx5Handle); + playSoundData(&_sfx5Handle, soundData, sound, pan, vol, true); +} + +void Sound::playVoiceData(byte *soundData, uint sound) { + _mixer->stopHandle(_voiceHandle); + playSoundData(&_voiceHandle, soundData, sound); +} + +void Sound::playSoundData(Audio::SoundHandle *handle, byte *soundData, uint sound, int pan, int vol, bool loop) { + byte *buffer, flags; + uint16 compType; + int blockAlign, rate; + + int size = READ_LE_UINT32(soundData + 4); + Common::MemoryReadStream stream(soundData, size); + if (!Audio::loadWAVFromStream(stream, size, rate, flags, &compType, &blockAlign)) + error("playSoundData: Not a valid WAV data"); + + // The Feeble Files originally used DirectSound, which specifies volume + // and panning differently than ScummVM does, using a logarithmic scale + // rather than a linear one. + // + // Volume is a value between -10,000 and 0. + // Panning is a value between -10,000 and 10,000. + // + // In both cases, the -10,000 represents -100 dB. When panning, only + // one speaker's volume is affected - just like in ScummVM - with + // negative values affecting the left speaker, and positive values + // affecting the right speaker. Thus -10,000 means the left speaker is + // silent. + + int v, p; + + vol = CLIP(vol, -10000, 0); + pan = CLIP(pan, -10000, 10000); + + if (vol) { + v = (int)((double)Audio::Mixer::kMaxChannelVolume * pow(10.0, (double)vol / 2000.0) + 0.5); + } else { + v = Audio::Mixer::kMaxChannelVolume; + } + + if (pan < 0) { + p = (int)(255.0 * pow(10.0, (double)pan / 2000.0) + 127.5); + } else if (pan > 0) { + p = (int)(255.0 * pow(10.0, (double)pan / -2000.0) - 127.5); + } else { + p = 0; + } + + if (loop == true) + flags |= Audio::Mixer::FLAG_LOOP; + + if (compType == 2) { + Audio::AudioStream *sndStream = Audio::makeADPCMStream(&stream, size, Audio::kADPCMMS, rate, (flags & Audio::Mixer::FLAG_STEREO) ? 2 : 1, blockAlign); + buffer = (byte *)malloc(size * 4); + size = sndStream->readBuffer((int16*)buffer, size * 2); + size *= 2; // 16bits. + delete sndStream; + } else { + buffer = (byte *)malloc(size); + memcpy(buffer, soundData + stream.pos(), size); + } + + _mixer->playRaw(handle, buffer, size, rate, flags | Audio::Mixer::FLAG_AUTOFREE, -1, v, p); +} + +void Sound::stopSfx5() { + _mixer->stopHandle(_sfx5Handle); +} + +void Sound::switchVoiceFile(const GameSpecificSettings *gss, uint disc) { + if (_lastVoiceFile == disc) + return; + + stopAll(); + delete _voice; + + _hasVoiceFile = false; + _lastVoiceFile = disc; + + char filename[16]; + File *file = new File(); + +#ifdef USE_FLAC + if (!_hasVoiceFile) { + sprintf(filename, "%s%d.fla", gss->speech_filename, disc); + file->open(filename); + if (file->isOpen()) { + _hasVoiceFile = true; + _voice = new FlacSound(_mixer, file); + } + } +#endif +#ifdef USE_MAD + if (!_hasVoiceFile) { + sprintf(filename, "%s%d.mp3", gss->speech_filename, disc); + file->open(filename); + if (file->isOpen()) { + _hasVoiceFile = true; + _voice = new MP3Sound(_mixer, file); + } + } +#endif +#ifdef USE_VORBIS + if (!_hasVoiceFile) { + sprintf(filename, "%s%d.ogg", gss->speech_filename, disc); + file->open(filename); + if (file->isOpen()) { + _hasVoiceFile = true; + _voice = new VorbisSound(_mixer, file); + } + } +#endif + if (!_hasVoiceFile) { + sprintf(filename, "%s%d.wav", gss->speech_filename, disc); + file->open(filename); + if (file->isOpen() == false) { + error("switchVoiceFile: Can't load voice file %s", filename); + } + _hasVoiceFile = true; + _voice = new WavSound(_mixer, file); + } +} + +} // End of namespace Simon diff --git a/engines/agos/sound.h b/engines/agos/sound.h new file mode 100644 index 0000000000..ce7b655b90 --- /dev/null +++ b/engines/agos/sound.h @@ -0,0 +1,98 @@ +/* ScummVM - Scumm Interpreter + * Copyright (C) 2001 Ludvig Strigeus + * Copyright (C) 2001-2006 The ScummVM project + * + * 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. + * + * $URL$ + * $Id$ + * + */ + +#ifndef AGOS_SOUND_H +#define AGOS_SOUND_H + +#include "sound/mixer.h" +#include "agos/intern.h" +#include "common/str.h" + +namespace Simon { + +class BaseSound; + +class SimonEngine; + +class Sound { +private: + SimonEngine *_vm; + + Audio::Mixer *_mixer; + + BaseSound *_voice; + BaseSound *_effects; + + bool _effectsPaused; + bool _ambientPaused; + bool _sfx5Paused; + + uint16 *_filenums; + uint32 *_offsets; + uint16 _lastVoiceFile; + + Audio::SoundHandle _voiceHandle; + Audio::SoundHandle _effectsHandle; + Audio::SoundHandle _ambientHandle; + Audio::SoundHandle _sfx5Handle; + + bool _hasEffectsFile; + bool _hasVoiceFile; + uint _ambientPlaying; + +public: + Sound(SimonEngine *vm, const GameSpecificSettings *gss, Audio::Mixer *mixer); + ~Sound(); + + void loadVoiceFile(const GameSpecificSettings *gss); + void loadSfxFile(const GameSpecificSettings *gss); + + void readSfxFile(const char *filename); + void loadSfxTable(Common::File *gameFile, uint32 base); + void readVoiceFile(const char *filename); + + void playVoice(uint sound); + void playEffects(uint sound); + void playAmbient(uint sound); + + // Feeble Files specific + void playAmbientData(byte *soundData, uint sound, uint pan, uint vol); + void playSfxData(byte *soundData, uint sound, uint pan, uint vol); + void playSfx5Data(byte *soundData, uint sound, uint pan, uint vol); + void playSoundData(Audio::SoundHandle *handle, byte *soundData, uint sound, int pan = 0, int vol = 0, bool loop = false); + void playVoiceData(byte *soundData, uint sound); + void switchVoiceFile(const GameSpecificSettings *gss, uint disc); + + bool hasVoice() const; + bool isVoiceActive() const; + void stopAllSfx(); + void stopSfx5(); + void stopVoice(); + void stopAll(); + void effectsPause(bool b); + void ambientPause(bool b); +}; + +} // End of namespace Simon + +#endif diff --git a/engines/agos/string.cpp b/engines/agos/string.cpp new file mode 100644 index 0000000000..b85eb9a9dd --- /dev/null +++ b/engines/agos/string.cpp @@ -0,0 +1,470 @@ +/* ScummVM - Scumm Interpreter + * Copyright (C) 2001 Ludvig Strigeus + * Copyright (C) 2001-2006 The ScummVM project + * + * 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. + * + * $URL$ + * $Id$ + * + */ + +#include "common/stdafx.h" + +#include "agos/agos.h" +#include "agos/intern.h" + +using Common::File; + +namespace Simon { + +const byte *SimonEngine::getStringPtrByID(uint stringId) { + const byte *string_ptr; + byte *dst; + + _freeStringSlot ^= 1; + + if (stringId < 0x8000) { + string_ptr = _stringTabPtr[stringId]; + } else { + string_ptr = getLocalStringByID(stringId); + } + + dst = _stringReturnBuffer[_freeStringSlot]; + strcpy((char *)dst, (const char *)string_ptr); + return dst; +} + +const byte *SimonEngine::getLocalStringByID(uint stringId) { + if (stringId < _stringIdLocalMin || stringId >= _stringIdLocalMax) { + loadTextIntoMem(stringId); + } + return _localStringtable[stringId - _stringIdLocalMin]; +} + +void SimonEngine::allocateStringTable(int num) { + _stringTabPtr = (byte **)calloc(num, sizeof(byte *)); + _stringTabPos = 0; + _stringtab_numalloc = num; +} + +void SimonEngine::setupStringTable(byte *mem, int num) { + int i = 0; + for (;;) { + _stringTabPtr[i++] = mem; + if (--num == 0) + break; + for (; *mem; mem++); + mem++; + } + + _stringTabPos = i; +} + +void SimonEngine::setupLocalStringTable(byte *mem, int num) { + int i = 0; + for (;;) { + _localStringtable[i++] = mem; + if (--num == 0) + break; + for (; *mem; mem++); + mem++; + } +} + +uint SimonEngine::loadTextFile(const char *filename, byte *dst) { + if (getFeatures() & GF_OLD_BUNDLE) + return loadTextFile_simon1(filename, dst); + else + return loadTextFile_gme(filename, dst); +} + +uint SimonEngine::loadTextFile_simon1(const char *filename, byte *dst) { + File fo; + fo.open(filename); + uint32 size; + + if (fo.isOpen() == false) + error("loadTextFile: Can't open '%s'", filename); + + size = fo.size(); + + if (fo.read(dst, size) != size) + error("loadTextFile: fread failed"); + fo.close(); + + return size; +} + +uint SimonEngine::loadTextFile_gme(const char *filename, byte *dst) { + uint res; + uint32 offs; + uint32 size; + + res = atoi(filename + 4) + _textIndexBase - 1; + offs = _gameOffsetsPtr[res]; + size = _gameOffsetsPtr[res + 1] - offs; + + readGameFile(dst, offs, size); + + return size; +} + +void SimonEngine::loadTextIntoMem(uint stringId) { + byte *p; + char filename[30]; + int i; + uint base_min = 0x8000, base_max, size; + + _tablesHeapPtr = _tablesheapPtrNew; + _tablesHeapCurPos = _tablesHeapCurPosNew; + + p = _strippedTxtMem; + + // get filename + while (*p) { + for (i = 0; *p; p++, i++) + filename[i] = *p; + filename[i] = 0; + p++; + + base_max = (p[0] * 256) | p[1]; + p += 2; + + if (stringId < base_max) { + _stringIdLocalMin = base_min; + _stringIdLocalMax = base_max; + + _localStringtable = (byte **)_tablesHeapPtr; + + size = (base_max - base_min + 1) * sizeof(byte *); + _tablesHeapPtr += size; + _tablesHeapCurPos += size; + + size = loadTextFile(filename, _tablesHeapPtr); + + setupLocalStringTable(_tablesHeapPtr, base_max - base_min + 1); + + _tablesHeapPtr += size; + _tablesHeapCurPos += size; + + if (_tablesHeapCurPos > _tablesHeapSize) { + error("loadTextIntoMem: Out of table memory"); + } + return; + } + + base_min = base_max; + } + + error("loadTextIntoMem: didn't find %d", stringId); +} + +static const byte charWidth[226] = { + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 6, 2, 4, 8, 6,10, 9, 2, + 4, 4, 6, 6, 3, 4, 2, 3, 6, 4, + 6, 6, 7, 6, 6, 6, 6, 6, 2, 3, + 7, 7, 7, 6,11, 8, 7, 8, 8, 7, + 6, 9, 8, 2, 6, 7, 6,10, 8, 9, + 7, 9, 7, 7, 8, 8, 8,12, 8, 8, + 7, 3, 3, 3, 6, 8, 3, 7, 7, 6, + 7, 7, 4, 7, 6, 2, 3, 6, 2,10, + 6, 7, 7, 7, 5, 6, 4, 7, 7,10, + 6, 6, 6, 0, 0, 0, 0, 0, 8, 6, + 7, 7, 7, 7, 7, 6, 7, 7, 7, 4, + 4, 3, 8, 8, 7, 0, 0, 7, 7, 7, + 6, 6, 6, 9, 8, 0, 0, 0, 0, 0, + 7, 3, 7, 6, 6, 8, 0, 6, 0, 0, + 0, 0, 0, 2, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 7 +}; + +const char *getPixelLength(const char *string, uint16 maxWidth, uint16 &pixels) { + pixels = 0; + + while (*string != 0) { + byte chr = *string; + if ((pixels + charWidth[chr]) > maxWidth) + break; + pixels += charWidth[chr]; + string++; + } + + return string; +} + +bool SimonEngine::printTextOf(uint a, uint x, uint y) { + const byte *stringPtr; + uint16 pixels, w; + + if (getGameType() == GType_SIMON2) { + if (getBitFlag(79)) { + Subroutine *sub; + _variableArray[84] = a; + sub = getSubroutineByID(5003); + if (sub != NULL) + startSubroutineEx(sub); + return true; + } + } + + if (a >= _numTextBoxes) + return false; + + + stringPtr = getStringPtrByID(_shortText[a]); + if (getGameType() == GType_FF) { + getPixelLength((const char *)stringPtr, 400, pixels); + w = pixels + 1; + x -= w / 2; + printScreenText(6, 0, (const char *)stringPtr, x, y, w); + } else { + showActionString(stringPtr); + } + + return true; +} + +bool SimonEngine::printNameOf(Item *item, uint x, uint y) { + SubObject *subObject; + const byte *stringPtr; + uint16 pixels, w; + + if (item == 0 || item == _dummyItem2 || item == _dummyItem3) + return false; + + subObject = (SubObject *)findChildOfType(item, 2); + if (subObject == NULL) + return false; + + stringPtr = getStringPtrByID(subObject->objectName); + if (getGameType() == GType_FF) { + getPixelLength((const char *)stringPtr, 400, pixels); + w = pixels + 1; + x -= w / 2; + printScreenText(6, 0, (const char *)stringPtr, x, y, w); + } else { + showActionString(stringPtr); + } + + return true; +} + +void SimonEngine::printInteractText(uint16 num, const char *string) { + char convertedString[320]; + char *convertedString2 = convertedString; + const char *string2 = string; + uint16 height = 15; + uint16 w = 0xFFFF; + uint16 b, pixels, x; + + // It doesn't really matter what 'w' is to begin with, as long as it's + // something that cannot be generated by getPixelLength(). The original + // used 620, which was a potential problem. + + while (1) { + string2 = getPixelLength(string, 620, pixels); + if (*string2 == 0x00) { + if (w == 0xFFFF) + w = pixels; + strcpy(convertedString2, string); + break; + } + while (*string2 != ' ') { + byte chr = *string2; + pixels -= charWidth[chr]; + string2--; + } + if (w == 0xFFFF) + w = pixels; + b = string2 - string; + strncpy(convertedString2, string, b); + convertedString2 += b; + *convertedString2++ = '\n'; + height += 15; + string = string2; + } + + // ScrollX + x = _variableArray[251]; + x += 20; + + if (num == 1) + _interactY = 385; + + // Returned values for box definition + _variableArray[51] = x; + _variableArray[52] = _interactY; + _variableArray[53] = w; + _variableArray[54] = height; + + stopAnimateSimon2(2, num + 6); + renderString(num, 0, w, height, convertedString); + loadSprite(4, 2, num + 6, x, _interactY, 12); + + _interactY += height; +} + +void SimonEngine::sendInteractText(uint16 num, const char *fmt, ...) { + va_list arglist; + char string[256]; + + va_start(arglist, fmt); + vsprintf(string, fmt, arglist); + va_end(arglist); + + printInteractText(num, string); +} + +void SimonEngine::printScreenText(uint vgaSpriteId, uint color, const char *string, int16 x, int16 y, int16 width) { + char convertedString[320]; + char *convertedString2 = convertedString; + const char *string2 = string; + int16 height, talkDelay; + int stringLength = strlen(string); + int padding, lettersPerRow, lettersPerRowJustified; + const int textHeight = (getGameType() == GType_FF) ? 15: 10; + + height = textHeight; + lettersPerRow = width / 6; + lettersPerRowJustified = stringLength / (stringLength / lettersPerRow + 1) + 1; + + talkDelay = (stringLength + 3) / 3; + if ((getGameType() == GType_SIMON1) && (getFeatures() & GF_TALKIE)) { + if (_variableArray[141] == 0) + _variableArray[141] = 9; + _variableArray[85] = _variableArray[141] * talkDelay; + } else { + if (_variableArray[86] == 0) + talkDelay /= 2; + if (_variableArray[86] == 2) + talkDelay *= 2; + _variableArray[85] = talkDelay * 5; + } + + assert(stringLength > 0); + + if (getGameType() == GType_FF) { + uint16 b, pixels, spaces; + + while (1) { + string2 = getPixelLength(string, width, pixels); + if (*string2 == 0) { + spaces = (width - pixels) / 12; + if (spaces != 0) + spaces--; + while (spaces) { + *convertedString2++ = ' '; + spaces--; + } + strcpy(convertedString2, string); + break; + } + while (*string2 != ' ') { + byte chr = *string2; + pixels -= charWidth[chr]; + string2--; + } + spaces = (width - pixels) / 12; + if (spaces != 0) + spaces--; + while (spaces) { + *convertedString2++ = ' '; + spaces--; + } + b = string2 - string; + strncpy(convertedString2, string, b); + convertedString2 += b; + *convertedString2++ = '\n'; + height += textHeight; + y -= textHeight; + if (y < 2) + y = 2; + string = string2; + } + } else { + while (stringLength > 0) { + int pos = 0; + if (stringLength > lettersPerRow) { + int removeLastWord = 0; + if (lettersPerRow > lettersPerRowJustified) { + pos = lettersPerRowJustified; + while (string[pos] != ' ') + pos++; + if (pos > lettersPerRow) + removeLastWord = 1; + } + if (lettersPerRow <= lettersPerRowJustified || removeLastWord) { + pos = lettersPerRow; + while (string[pos] != ' ' && pos > 0) + pos--; + } + height += textHeight; + y -= textHeight; + } else + pos = stringLength; + padding = (lettersPerRow - pos) % 2 ? + (lettersPerRow - pos) / 2 + 1 : (lettersPerRow - pos) / 2; + while (padding--) + *convertedString2++ = ' '; + stringLength -= pos; + while (pos--) + *convertedString2++ = *string++; + *convertedString2++ = '\n'; + string++; // skip space + stringLength--; // skip space + } + *(convertedString2 - 1) = '\0'; + } + + if (getGameType() == GType_SIMON1) + stopAnimateSimon1(vgaSpriteId + 199); + else + stopAnimateSimon2(2, vgaSpriteId); + + if (getGameType() == GType_FF) { + renderString(1, color, width, height, convertedString); + } else { + color = color * 3 + 192; + if (getPlatform() == Common::kPlatformAmiga) + renderStringAmiga(vgaSpriteId, color, width, height, convertedString); + else + renderString(vgaSpriteId, color, width, height, convertedString); + } + + int b = 4; + if (getGameType() == GType_SIMON1 || getGameType() == GType_SIMON2) { + if (!getBitFlag(133)) + b = 3; + + x /= 8; + if (y < 2) + y = 2; + } + + if (getGameType() == GType_SIMON1) + loadSprite(b, 2, vgaSpriteId + 199, x, y, 12); + else + loadSprite(b, 2, vgaSpriteId, x, y, 12); +} + +} // End of namespace Simon diff --git a/engines/agos/subroutine.cpp b/engines/agos/subroutine.cpp new file mode 100644 index 0000000000..b5a7c1f9f7 --- /dev/null +++ b/engines/agos/subroutine.cpp @@ -0,0 +1,479 @@ +/* ScummVM - Scumm Interpreter + * Copyright (C) 2001 Ludvig Strigeus + * Copyright (C) 2001-2006 The ScummVM project + * + * 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. + * + * $URL$ + * $Id$ + * + */ + +#include "common/stdafx.h" + +#include "agos/agos.h" +#include "agos/intern.h" + +using Common::File; + +namespace Simon { + +Subroutine *SimonEngine::getSubroutineByID(uint subroutine_id) { + Subroutine *cur; + + _subroutine = subroutine_id; + + for (cur = _subroutineList; cur; cur = cur->next) { + if (cur->id == subroutine_id) + return cur; + } + + if (loadXTablesIntoMem(subroutine_id)) { + for (cur = _subroutineList; cur; cur = cur->next) { + if (cur->id == subroutine_id) + return cur; + } + } + + if (loadTablesIntoMem(subroutine_id)) { + for (cur = _subroutineList; cur; cur = cur->next) { + if (cur->id == subroutine_id) + return cur; + } + } + + if (subroutine_id != 160) + debug(0,"getSubroutineByID: subroutine %d not found", subroutine_id); + return NULL; +} + +void SimonEngine::alignTableMem() { + if ((unsigned long)_tablesHeapPtr & 3) { + _tablesHeapPtr += 2; + _tablesHeapCurPos += 2; + } +} + +byte *SimonEngine::allocateTable(uint size) { + byte *org = _tablesHeapPtr; + + size = (size + 1) & ~1; + + _tablesHeapPtr += size; + _tablesHeapCurPos += size; + + if (_tablesHeapCurPos > _tablesHeapSize) + error("Tablesheap overflow"); + + return org; +} + +File *SimonEngine::openTablesFile(const char *filename) { + if (getFeatures() & GF_OLD_BUNDLE) + return openTablesFile_simon1(filename); + else + return openTablesFile_gme(filename); +} + +File *SimonEngine::openTablesFile_simon1(const char *filename) { + File *fo = new File(); + fo->open(filename); + if (fo->isOpen() == false) + error("openTablesFile: Can't open '%s'", filename); + return fo; +} + +File *SimonEngine::openTablesFile_gme(const char *filename) { + uint res; + uint32 offs; + + res = atoi(filename + 6) + _tableIndexBase - 1; + offs = _gameOffsetsPtr[res]; + + _gameFile->seek(offs, SEEK_SET); + return _gameFile; +} + +bool SimonEngine::loadTablesIntoMem(uint subr_id) { + if (getGameType() == GType_ELVIRA || getGameType() == GType_ELVIRA2) + return loadTablesOldIntoMem(subr_id); + else + return loadTablesNewIntoMem(subr_id); +} + + +bool SimonEngine::loadTablesOldIntoMem(uint subr_id) { + byte *p; + uint16 min_num, max_num, file_num; + File *in; + char filename[30]; + + if (_tblList == NULL) + return 0; + + p = _tblList + 32; + + min_num = READ_BE_UINT16(p); + max_num = READ_BE_UINT16(p + 2); + file_num = *(p + 4); + p += 6; + + while (min_num) { + if ((subr_id >= min_num) && (subr_id <= max_num)) { + _subroutineList = _subroutineListOrg; + _tablesHeapPtr = _tablesHeapPtrOrg; + _tablesHeapCurPos = _tablesHeapCurPosOrg; + _stringIdLocalMin = 1; + _stringIdLocalMax = 0; + + sprintf(filename, "TABLES%.2d", file_num); + in = openTablesFile(filename); + readSubroutineBlock(in); + closeTablesFile(in); + + alignTableMem(); + + _tablesheapPtrNew = _tablesHeapPtr; + _tablesHeapCurPosNew = _tablesHeapCurPos; + + if (_tablesHeapCurPos > _tablesHeapSize) + error("loadTablesOldIntoMem: Out of table memory"); + return 1; + } + + min_num = READ_BE_UINT16(p); + max_num = READ_BE_UINT16(p + 2); + file_num = *(p + 4); + p += 6; + } + + debug(1,"loadTablesOldIntoMem: didn't find %d", subr_id); + return 0; +} + +bool SimonEngine::loadTablesNewIntoMem(uint subr_id) { + byte *p; + int i; + uint min_num, max_num; + char filename[30]; + File *in; + + p = _tblList; + if (p == NULL) + return 0; + + while (*p) { + for (i = 0; *p; p++, i++) + filename[i] = *p; + filename[i] = 0; + p++; + + for (;;) { + min_num = READ_BE_UINT16(p); p += 2; + if (min_num == 0) + break; + + max_num = READ_BE_UINT16(p); p += 2; + + if (subr_id >= min_num && subr_id <= max_num) { + _subroutineList = _subroutineListOrg; + _tablesHeapPtr = _tablesHeapPtrOrg; + _tablesHeapCurPos = _tablesHeapCurPosOrg; + _stringIdLocalMin = 1; + _stringIdLocalMax = 0; + + in = openTablesFile(filename); + readSubroutineBlock(in); + closeTablesFile(in); + if (getGameType() == GType_SIMON2) { + _sound->loadSfxTable(_gameFile, _gameOffsetsPtr[atoi(filename + 6) - 1 + _soundIndexBase]); + } else if (getGameType() == GType_SIMON1 && getPlatform() == Common::kPlatformWindows) { + memcpy(filename, "SFXXXX", 6); + if (atoi(filename + 6) != 1 && atoi(filename + 6) != 30) + _sound->readSfxFile(filename); + } + + alignTableMem(); + + _tablesheapPtrNew = _tablesHeapPtr; + _tablesHeapCurPosNew = _tablesHeapCurPos; + + if (_tablesHeapCurPos > _tablesHeapSize) + error("loadTablesNewIntoMem: Out of table memory"); + return 1; + } + } + } + + debug(1,"loadTablesNewIntoMem: didn't find %d", subr_id); + return 0; +} + +bool SimonEngine::loadXTablesIntoMem(uint subr_id) { + byte *p; + int i; + uint min_num, max_num; + char filename[30]; + File *in; + + p = _xtblList; + if (p == NULL) + return 0; + + while (*p) { + for (i = 0; *p; p++, i++) + filename[i] = *p; + filename[i] = 0; + p++; + + for (;;) { + min_num = READ_BE_UINT16(p); + p += 2; + + if (min_num == 0) + break; + + max_num = READ_BE_UINT16(p); + p += 2; + + if (subr_id >= min_num && subr_id <= max_num) { + _subroutineList = _xsubroutineListOrg; + _tablesHeapPtr = _xtablesHeapPtrOrg; + _tablesHeapCurPos = _xtablesHeapCurPosOrg; + _stringIdLocalMin = 1; + _stringIdLocalMax = 0; + + in = openTablesFile(filename); + readSubroutineBlock(in); + closeTablesFile(in); + + alignTableMem(); + + _subroutineListOrg = _subroutineList; + _tablesHeapPtrOrg = _tablesHeapPtr; + _tablesHeapCurPosOrg = _tablesHeapCurPos; + _tablesheapPtrNew = _tablesHeapPtr; + _tablesHeapCurPosNew = _tablesHeapCurPos; + + return 1; + } + } + } + + debug(1,"loadXTablesIntoMem: didn't find %d", subr_id); + return 0; +} + +void SimonEngine::closeTablesFile(File *in) { + if (getFeatures() & GF_OLD_BUNDLE) { + in->close(); + delete in; + } +} + +Subroutine *SimonEngine::createSubroutine(uint id) { + Subroutine *sub; + + alignTableMem(); + + sub = (Subroutine *)allocateTable(sizeof(Subroutine)); + sub->id = id; + sub->first = 0; + sub->next = _subroutineList; + _subroutineList = sub; + return sub; +} + +SubroutineLine *SimonEngine::createSubroutineLine(Subroutine *sub, int where) { + SubroutineLine *sl, *cur_sl = NULL, *last_sl = NULL; + + if (sub->id == 0) + sl = (SubroutineLine *)allocateTable(SUBROUTINE_LINE_BIG_SIZE); + else + sl = (SubroutineLine *)allocateTable(SUBROUTINE_LINE_SMALL_SIZE); + + // where is what offset to insert the line at, locate the proper beginning line + if (sub->first != 0) { + cur_sl = (SubroutineLine *)((byte *)sub + sub->first); + while (where) { + last_sl = cur_sl; + cur_sl = (SubroutineLine *)((byte *)sub + cur_sl->next); + if ((byte *)cur_sl == (byte *)sub) + break; + where--; + } + } + + if (last_sl != NULL) { + // Insert the subroutine line in the middle of the link + last_sl->next = (byte *)sl - (byte *)sub; + sl->next = (byte *)cur_sl - (byte *)sub; + } else { + // Insert the subroutine line at the head of the link + sl->next = sub->first; + sub->first = (byte *)sl - (byte *)sub; + } + + return sl; +} + +void SimonEngine::runSubroutine101() { + Subroutine *sub; + + sub = getSubroutineByID(101); + if (sub != NULL) + startSubroutineEx(sub); + + permitInput(); +} + +int SimonEngine::startSubroutine(Subroutine *sub) { + int result = -1; + SubroutineLine *sl; + const byte *old_code_ptr; + + if (_startMainScript) + dumpSubroutine(sub); + + old_code_ptr = _codePtr; + + if (++_recursionDepth > 40) + error("Recursion error"); + + // WORKAROUND: Bit Flag 171 isn't set when Simon rides the lion to the + // goblin camp in non-English versions. Bit Flag 171 is required to display + // the red trail between locations on the map, during the ride. + if (getGameType() == GType_SIMON2) { + if (sub->id == 13020) + setBitFlag(171, true); + if (sub->id == 13021) + setBitFlag(171, false); + } + + sl = (SubroutineLine *)((byte *)sub + sub->first); + + while ((byte *)sl != (byte *)sub) { + if (checkIfToRunSubroutineLine(sl, sub)) { + result = 0; + _codePtr = (byte *)sl; + if (sub->id) + _codePtr += 2; + else + _codePtr += 8; + + if (_continousMainScript) + printf("; %d\n", sub->id); + result = runScript(); + if (result != 0) { + /* result -10 means restart subroutine */ + if (result == -10) { + delay(0); /* maybe leave control to the VGA */ + sl = (SubroutineLine *)((byte *)sub + sub->first); + continue; + } + break; + } + } + sl = (SubroutineLine *)((byte *)sub + sl->next); + } + + _codePtr = old_code_ptr; + + _recursionDepth--; + return result; +} + +int SimonEngine::startSubroutineEx(Subroutine *sub) { + return startSubroutine(sub); +} + +bool SimonEngine::checkIfToRunSubroutineLine(SubroutineLine *sl, Subroutine *sub) { + if (sub->id) + return true; + + if (sl->verb != -1 && sl->verb != _scriptVerb && + (sl->verb != -2 || _scriptVerb != -1)) + return false; + + if (sl->noun1 != -1 && sl->noun1 != _scriptNoun1 && + (sl->noun1 != -2 || _scriptNoun1 != -1)) + return false; + + if (sl->noun2 != -1 && sl->noun2 != _scriptNoun2 && + (sl->noun2 != -2 || _scriptNoun2 != -1)) + return false; + + return true; +} + +void SimonEngine::readSubroutine(File *in, Subroutine *sub) { + while (in->readUint16BE() == 0) { + readSubroutineLine(in, createSubroutineLine(sub, 0xFFFF), sub); + } +} + +void SimonEngine::readSubroutineLine(File *in, SubroutineLine *sl, Subroutine *sub) { + byte line_buffer[2048], *q = line_buffer; + int size; + + if (sub->id == 0) { + sl->verb = in->readUint16BE(); + sl->noun1 = in->readUint16BE(); + sl->noun2 = in->readUint16BE(); + } else if (getGameType() == GType_ELVIRA || getGameType() == GType_ELVIRA2) { + in->readUint16BE(); + in->readUint16BE(); + in->readUint16BE(); + } + + if (getGameType() == GType_ELVIRA || getGameType() == GType_ELVIRA2) { + int16 tmp; + + tmp = in->readUint16BE(); + WRITE_BE_UINT16(q, tmp); + while (tmp != 10000) { + if (READ_BE_UINT16(q) == 0xC6) { + in->readUint16BE(); + } else { + q = readSingleOpcode(in, q); + } + + tmp = in->readUint16BE(); + WRITE_BE_UINT16(q, tmp); + } + + size = (q - line_buffer + 1) * 2; + memcpy(allocateTable(size), line_buffer, size); + } else { + while ((*q = in->readByte()) != 0xFF) { + if (*q == 87) { + in->readUint16BE(); + } else { + q = readSingleOpcode(in, q); + } + } + + size = (q - line_buffer + 1); + memcpy(allocateTable(size), line_buffer, size); + } +} + +void SimonEngine::readSubroutineBlock(File *in) { + while (in->readUint16BE() == 0) { + readSubroutine(in, createSubroutine(in->readUint16BE())); + } +} + +} // End of namespace Simon diff --git a/engines/agos/verb.cpp b/engines/agos/verb.cpp new file mode 100644 index 0000000000..014ca36df7 --- /dev/null +++ b/engines/agos/verb.cpp @@ -0,0 +1,906 @@ +/* ScummVM - Scumm Interpreter + * Copyright (C) 2001 Ludvig Strigeus + * Copyright (C) 2001-2006 The ScummVM project + * + * 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. + * + * $URL$ + * $Id$ + * + */ + +// Verb and hitarea handling +#include "common/stdafx.h" + +#include "agos/agos.h" +#include "agos/intern.h" + +namespace Simon { + +static const char *const russian_verb_names[] = { + "Ietj _", + "Qnotrft< pa", + "Nt_r[t<", + "Ecjdat<", + "Q=fst<", + "C^]t<", + "Ha_r[t<", + "Isqom<^ocat<", + "Docorjt<", + "Qp]t<", + "Neft<", + "Eat<" +}; + +static const char *const hebrew_verb_names[] = { + "LJ @L", + "DQZKL RL", + "TZG", + "DFF", + "@KEL", + "DXM", + "QBEX", + "DYZNY", + "CAX @L", + "DQX", + "LAY", + "ZO" +}; + +static const char *const spanish_verb_names[] = { + "Caminar", + "Mirar", + "Abrir", + "Mover", + "Consumir", + "Coger", + "Cerrar", + "Usar", + "Hablar", + "Quitar", + "Llevar", + "Dar" +}; + +static const char *const italian_verb_names[] = { + "Vai verso", + "Osserva", + "Apri", + "Sposta", + "Mangia", + "Raccogli", + "Chiudi", + "Usa", + "Parla a", + "Togli", + "Indossa", + "Dai" +}; + +static const char *const french_verb_names[] = { + "Aller vers", + "Regarder", + "Ouvrir", + "D/placer", + "Consommer", + "Prendre", + "Fermer", + "Utiliser", + "Parler ;", + "Enlever", + "Mettre", + "Donner" +}; + +static const char *const german_verb_names[] = { + "Gehe zu", + "Schau an", + ";ffne", + "Bewege", + "Verzehre", + "Nimm", + "Schlie+e", + "Benutze", + "Rede mit", + "Entferne", + "Trage", + "Gib" +}; + +static const char *const english_verb_names[] = { + "Walk to", + "Look at", + "Open", + "Move", + "Consume", + "Pick up", + "Close", + "Use", + "Talk to", + "Remove", + "Wear", + "Give" +}; + +static const char *const russian_verb_prep_names[] = { + "", "", "", "", + "", "", "", "s yfn?", + "", "", "", "_onu ?" +}; + +static const char *const hebrew_verb_prep_names[] = { + "", "", "", "", + "", "", "", "RM ND ?", + "", "", "", "LNI ?" +}; + +static const char *const spanish_verb_prep_names[] = { + "", "", "", "", + "", "", "", "^con qu/?", + "", "", "", "^a qui/n?" +}; + +static const char *const italian_verb_prep_names[] = { + "", "", "", "", + "", "", "", "con cosa ?", + "", "", "", "a chi ?" +}; + +static const char *const french_verb_prep_names[] = { + "", "", "", "", + "", "", "", "avec quoi ?", + "", "", "", "; qui ?" +}; + +static const char *const german_verb_prep_names[] = { + "", "", "", "", + "", "", "", "mit was ?", + "", "", "", "zu wem ?" +}; + +static const char *const english_verb_prep_names[] = { + "", "", "", "", + "", "", "", "with what ?", + "", "", "", "to whom ?" +}; + +void SimonEngine::clearName() { + HitArea *last; + HitArea *ha; + + if (getGameType() == GType_FF || getGameType() == GType_PP) { + stopAnimateSimon2(2, 6); + _lastNameOn = NULL; + _animatePointer = 0; + _mouseAnim = 1; + return; + } + + if (getGameType() == GType_SIMON2) { + if (getBitFlag(79)) { + sendSync(202); + _lastNameOn = NULL; + return; + } + } + + last = _currentVerbBox; + + if (last == _lastVerbOn) + return; + + resetNameWindow(); + _lastVerbOn = last; + + if (last != NULL && (ha = findBox(200)) && (ha->flags & kBFBoxDead) && !(last->flags & kBFBoxDead)) + printVerbOf(last->id); +} + +void SimonEngine::printVerbOf(uint hitarea_id) { + const char *txt; + const char * const *verb_names; + const char * const *verb_prep_names; + + hitarea_id -= 101; + + if (_showPreposition) { + switch (_language) { + case Common::RU_RUS: + verb_prep_names = russian_verb_prep_names; + break; + case Common::HB_ISR: + verb_prep_names = hebrew_verb_prep_names; + break; + case Common::ES_ESP: + verb_prep_names = spanish_verb_prep_names; + break; + case Common::IT_ITA: + verb_prep_names = italian_verb_prep_names; + break; + case Common::FR_FRA: + verb_prep_names = french_verb_prep_names; + break; + case Common::DE_DEU: + verb_prep_names = german_verb_prep_names; + break; + default: + verb_prep_names = english_verb_prep_names; + break; + } + CHECK_BOUNDS(hitarea_id, english_verb_prep_names); + txt = verb_prep_names[hitarea_id]; + } else { + switch (_language) { + case Common::RU_RUS: + verb_names = russian_verb_names; + break; + case Common::HB_ISR: + verb_names = hebrew_verb_names; + break; + case Common::ES_ESP: + verb_names = spanish_verb_names; + break; + case Common::IT_ITA: + verb_names = italian_verb_names; + break; + case Common::FR_FRA: + verb_names = french_verb_names; + break; + case Common::DE_DEU: + verb_names = german_verb_names; + break; + default: + verb_names = english_verb_names; + break; + } + CHECK_BOUNDS(hitarea_id, english_verb_names); + txt = verb_names[hitarea_id]; + } + showActionString((const byte *)txt); +} + +void SimonEngine::showActionString(const byte *string) { + WindowBlock *window; + uint x; + + window = _windowArray[1]; + if (window == NULL || window->text_color == 0) + return; + + // Arisme : hack for long strings in the French version + if ((strlen((const char*)string) - 1) <= 53) + x = (53 - (strlen((const char *)string) - 1)) * 3; + else + x = 0; + + window->textColumn = x / 8; + window->textColumnOffset = x & 7; + + for (; *string; string++) + windowPutChar(window, *string); +} + +void SimonEngine::handleVerbClicked(uint verb) { + Subroutine *sub; + int result; + + _objectItem = _hitAreaObjectItem; + if (_objectItem == _dummyItem2) { + _objectItem = me(); + } + if (_objectItem == _dummyItem3) { + _objectItem = derefItem(me()->parent); + } + + _subjectItem = _hitAreaSubjectItem; + if (_subjectItem == _dummyItem2) { + _subjectItem = me(); + } + if (_subjectItem == _dummyItem3) { + _subjectItem = derefItem(me()->parent); + } + + if (_subjectItem) { + _scriptNoun1 = _subjectItem->noun; + _scriptAdj1 = _subjectItem->adjective; + } else { + _scriptNoun1 = -1; + _scriptAdj1 = -1; + } + + if (_objectItem) { + _scriptNoun2 = _objectItem->noun; + _scriptAdj2 = _objectItem->adjective; + } else { + _scriptNoun2 = -1; + _scriptAdj2 = -1; + } + + _scriptVerb = _verbHitArea; + + sub = getSubroutineByID(0); + if (sub == NULL) + return; + + result = startSubroutine(sub); + if (result == -1) + showMessageFormat("I don't understand"); + + _runScriptReturn1 = false; + + sub = getSubroutineByID(100); + if (sub) + startSubroutine(sub); + + if (getGameType() == GType_SIMON2 || getGameType() == GType_FF || getGameType() == GType_PP) + _runScriptReturn1 = false; + + permitInput(); +} + +void SimonEngine::resetNameWindow() { + WindowBlock *window; + + if (getGameType() == GType_SIMON2 && getBitFlag(79)) + return; + + window = _windowArray[1]; + if (window != NULL && window->text_color != 0) + clearWindow(window); + + _lastNameOn = NULL; + _lastVerbOn = NULL; +} + +HitArea *SimonEngine::findBox(uint hitarea_id) { + HitArea *ha = _hitAreas; + uint count = ARRAYSIZE(_hitAreas); + + do { + if (ha->id == hitarea_id && ha->flags != 0) + return ha; + } while (ha++, --count); + return NULL; +} + +HitArea *SimonEngine::findEmptyHitArea() { + HitArea *ha = _hitAreas; + uint count = ARRAYSIZE(_hitAreas); + + do { + if (ha->flags == 0) + return ha; + } while (ha++, --count); + return NULL; +} + +void SimonEngine::delete_hitarea_by_index(uint index) { + CHECK_BOUNDS(index, _hitAreas); + _hitAreas[index].flags = 0; +} + +void SimonEngine::enableBox(uint hitarea) { + HitArea *ha = findBox(hitarea); + if (ha != NULL) + ha->flags &= ~kBFBoxDead; +} + +void SimonEngine::disableBox(uint hitarea) { + HitArea *ha = findBox(hitarea); + if (ha != NULL) { + ha->flags |= kBFBoxDead; + ha->flags &= ~kBFBoxSelected; + if (hitarea == 102) + resetVerbs(); + } +} + +void SimonEngine::moveBox(uint hitarea, int x, int y) { + HitArea *ha = findBox(hitarea); + if (ha != NULL) { + if (getGameType() == GType_FF || getGameType() == GType_PP) { + ha->x += x; + ha->y += y; + } else { + ha->x = x; + ha->y = y; + } + } +} + +void SimonEngine::undefineBox(uint hitarea) { + HitArea *ha = findBox(hitarea); + if (ha != NULL) { + ha->flags = 0; + if (ha == _lastNameOn) + clearName(); + _needHitAreaRecalc++; + } +} + +bool SimonEngine::isBoxDead(uint hitarea) { + HitArea *ha = findBox(hitarea); + if (ha == NULL) + return false; + return (ha->flags & kBFBoxDead) == 0; +} + +void SimonEngine::defineBox(int id, int x, int y, int width, int height, int flags, int verb, Item *item_ptr) { + HitArea *ha; + undefineBox(id); + + ha = findEmptyHitArea(); + ha->x = x; + ha->y = y; + ha->width = width; + ha->height = height; + ha->flags = flags | kBFBoxInUse; + ha->id = ha->priority = id; + ha->verb = verb; + ha->item_ptr = item_ptr; + + if ((getGameType() == GType_FF || getGameType() == GType_PP) && + (ha->flags & kBFHyperBox)) { + ha->data = _hyperLink; + ha->priority = 50; + } + + _needHitAreaRecalc++; +} + +void SimonEngine::resetVerbs() { + if (getGameType() == GType_PP) { + _verbHitArea = 300; + return; + } else if (getGameType() == GType_FF) { + _verbHitArea = 300; + int cursor = 0; + int animMax = 16; + + if (getBitFlag(203)) { + cursor = 14; + animMax = 9; + } else if (getBitFlag(204)) { + cursor = 15; + animMax = 9; + } else if (getBitFlag(207)) { + cursor = 26; + animMax = 2; + } + + _mouseCursor = cursor; + _mouseAnimMax = animMax; + _mouseAnim = 1; + _needHitAreaRecalc++; + + if (getBitFlag(99)) { + setVerb(NULL); + } + } else { + uint id; + HitArea *ha; + + if (getGameType() == GType_SIMON2) { + id = 2; + if (!getBitFlag(79)) + id = (_mouseY >= 136) ? 102 : 101; + } else { + id = (_mouseY >= 136) ? 102 : 101; + } + + _defaultVerb = id; + + ha = findBox(id); + if (ha == NULL) + return; + + if (ha->flags & kBFBoxDead) { + _defaultVerb = 999; + _currentVerbBox = NULL; + } else { + _verbHitArea = ha->verb; + setVerb(ha); + } + } +} + +void SimonEngine::setVerb(HitArea *ha) { + if (getGameType() == GType_PP) { + return; + } else if (getGameType() == GType_FF) { + int cursor = _mouseCursor; + if (_noRightClick) + return; + + if (cursor > 13) + cursor = 0; + cursor++; + if (cursor == 5) + cursor = 1; + if (cursor == 4) { + if (getBitFlag(72)) { + cursor = 1; + } + } else if (cursor == 2) { + if (getBitFlag(99)) { + cursor = 3; + } + } + + _mouseCursor = cursor; + _mouseAnimMax = (cursor == 4) ? 14: 16; + _mouseAnim = 1; + _needHitAreaRecalc++; + _verbHitArea = cursor + 300; + } else { + HitArea *tmp = _currentVerbBox; + + if (ha == tmp) + return; + + if (getGameType() == GType_SIMON1) { + if (tmp != NULL) { + tmp->flags |= kBFInvertTouch; + invertBox(tmp, 213, 208, 213, 10); + } + + if (ha->flags & kBFBoxSelected) + invertBox(ha, 218, 213, 213, 5); + else + invertBox(ha, 223, 218, 218, 10); + + ha->flags &= ~(kBFBoxSelected + kBFInvertTouch); + } else { + if (ha->id < 101) + return; + _mouseCursor = ha->id - 101; + _needHitAreaRecalc++; + } + _currentVerbBox = ha; + } +} + +void SimonEngine::hitarea_leave(HitArea *ha, bool state) { + if (getGameType() == GType_FF) { + invertBox_FF(ha, state); + } else if (getGameType() == GType_SIMON2) { + invertBox(ha, 231, 229, 230, 1); + } else { + invertBox(ha, 223, 213, 218, 5); + } +} + +void SimonEngine::leaveHitAreaById(uint hitarea_id) { + HitArea *ha = findBox(hitarea_id); + if (ha) + hitarea_leave(ha); +} + +void SimonEngine::checkUp(WindowBlock *window) { + uint16 j, k; + + if (((_variableArray[31] - _variableArray[30]) == 40) && (_variableArray[31] > 52)) { + k = (((_variableArray[31] / 52) - 2) % 3); + j = k * 6; + if (!isBoxDead(j + 201)) { + uint index = getWindowNum(window); + drawIconArray(index, window->iconPtr->itemRef, 0, window->iconPtr->classMask); + loadSprite(4, 9, k + 34, 0, 0, 0); + } + } + if ((_variableArray[31] - _variableArray[30]) == 76) { + k = ((_variableArray[31] / 52) % 3); + j = k * 6; + if (isBoxDead(j + 201)) { + loadSprite(4, 9, k + 31, 0, 0, 0); + undefineBox(j + 201); + undefineBox(j + 202); + undefineBox(j + 203); + undefineBox(j + 204); + undefineBox(j + 205); + undefineBox(j + 206); + } + _variableArray[31] -= 52; + _iOverflow = 1; + } +} + +void SimonEngine::checkDown(WindowBlock *window) { + uint16 j, k; + + if (((_variableArray[31] - _variableArray[30]) == 24) && (_iOverflow == 1)) { + uint index = getWindowNum(window); + drawIconArray(index, window->iconPtr->itemRef, 0, window->iconPtr->classMask); + k = ((_variableArray[31] / 52) % 3); + loadSprite(4, 9, k + 25, 0, 0, 0); + _variableArray[31] += 52; + } + if (((_variableArray[31] - _variableArray[30]) == 40) && (_variableArray[30] > 52)) { + k = (((_variableArray[31] / 52) + 1) % 3); + j = k * 6; + if (isBoxDead(j + 201)) { + loadSprite(4, 9, k + 28, 0, 0, 0); + undefineBox(j + 201); + undefineBox(j + 202); + undefineBox(j + 203); + undefineBox(j + 204); + undefineBox(j + 205); + undefineBox(j + 206); + } + } +} + +void SimonEngine::inventoryUp(WindowBlock *window) { + if (getGameType() == GType_FF) { + _marks = 0; + checkUp(window); + loadSprite(4, 9, 21, 0 ,0, 0); + while (1) { + if (_currentBoxNumber != 0x7FFB || !getBitFlag(89)) + break; + checkUp(window); + delay(1); + } + waitForMark(2); + checkUp(window); + sendSync(922); + waitForMark(1); + checkUp(window); + } else { + if (window->iconPtr->line == 0) + return; + + mouseOff(); + uint index = getWindowNum(window); + drawIconArray(index, window->iconPtr->itemRef, window->iconPtr->line - 1, window->iconPtr->classMask); + mouseOn(); + } +} + +void SimonEngine::inventoryDown(WindowBlock *window) { + if (getGameType() == GType_FF) { + _marks = 0; + checkDown(window); + loadSprite(4, 9, 23, 0, 0, 0); + while (1) { + if (_currentBoxNumber != 0x7FFC || !getBitFlag(89)) + break; + checkDown(window); + delay(1); + } + waitForMark(2); + checkDown(window); + sendSync(924); + waitForMark(1); + checkDown(window); + } else { + mouseOff(); + uint index = getWindowNum(window); + drawIconArray(index, window->iconPtr->itemRef, window->iconPtr->line + 1, window->iconPtr->classMask); + mouseOn(); + } +} + +void SimonEngine::boxController(uint x, uint y, uint mode) { + HitArea *best_ha; + HitArea *ha = _hitAreas; + uint count = ARRAYSIZE(_hitAreas); + uint16 priority = 0; + uint16 x_ = x; + uint16 y_ = y; + + if (getGameType() == GType_FF) { + x_ += _scrollX; + y_ += _scrollY; + } + if (getGameType() == GType_SIMON2) { + if (getBitFlag(79) || y < 134) { + x_ += _scrollX * 8; + } + } + + best_ha = NULL; + + do { + if (ha->flags & kBFBoxInUse) { + if (!(ha->flags & kBFBoxDead)) { + if (x_ >= ha->x && y_ >= ha->y && + x_ - ha->x < ha->width && y_ - ha->y < ha->height && priority <= ha->priority) { + priority = ha->priority; + best_ha = ha; + } else { + if (ha->flags & kBFBoxSelected) { + hitarea_leave(ha , true); + ha->flags &= ~kBFBoxSelected; + } + } + } else { + ha->flags &= ~kBFBoxSelected; + } + } + } while (ha++, --count); + + _currentBoxNumber = 0; + + if (best_ha == NULL) { + clearName(); + return; + } + + _currentBoxNumber = best_ha->id; + + if (mode != 0 && mode != 3) { + _lastHitArea = best_ha; + if (getGameType() == GType_PP) { + _variableArray[400] = x; + _variableArray[401] = y; + } else { + _variableArray[1] = x; + _variableArray[2] = y; + } + } + + if (best_ha->flags & kBFNoTouchName) { + clearName(); + } else if (best_ha != _lastNameOn) { + displayName(best_ha); + } + + if (best_ha->flags & kBFInvertTouch && !(best_ha->flags & kBFBoxSelected)) { + hitarea_leave(best_ha, false); + best_ha->flags |= kBFBoxSelected; + } + + return; +} + +void SimonEngine::displayName(HitArea *ha) { + bool result; + int x = 0, y = 0; + + if (getGameType() == GType_PP) { + if (ha->flags & kBFHyperBox) { + _lastNameOn = ha; + return; + } + if (findBox(50)) + return; + + y = ha->y; + y -= 17; + if (y < 0) + y = 0; + y += 2; + x = ha->width / 2 + ha->x; + } else if (getGameType() == GType_FF) { + if (ha->flags & kBFHyperBox) { + _lastNameOn = ha; + return; + } + if (findBox(50)) + return; + + if (getBitFlag(99)) + _animatePointer = ((ha->flags & kBFTextBox) == 0); + else + _animatePointer = 1; + + if (!getBitFlag(73)) + return; + + y = ha->y; + if (getBitFlag(99) && y > 288) + y = 288; + y -= 17; + if (y < 0) + y = 0; + y += 2; + x = ha->width / 2 + ha->x; + } else { + resetNameWindow(); + } + + if (ha->flags & kBFTextBox) { + result = printTextOf(ha->flags / 256, x, y); + } else { + result = printNameOf(ha->item_ptr, x, y); + } + + if (result) + _lastNameOn = ha; +} + +void SimonEngine::invertBox_FF(HitArea *ha, bool state) { + if (getBitFlag(205) || getBitFlag(206)) { + if (state != 0) { + _mouseAnimMax = _oldMouseAnimMax; + _mouseCursor = _oldMouseCursor; + } else if (_mouseCursor != 18) { + _oldMouseCursor = _mouseCursor; + _animatePointer = 0; + _oldMouseAnimMax = _mouseAnimMax; + _mouseAnimMax = 2; + _mouseCursor = 18; + } + } else { + if (getBitFlag(207)) { + if (state != 0) { + _noRightClick = 0; + resetVerbs(); + } else { + int cursor = ha->id + 9; + if (cursor >= 23) + cursor = 21; + _mouseCursor = cursor; + _mouseAnimMax = 8; + _noRightClick = 1; + } + } else { + VgaSprite *vsp = _vgaSprites; + + int id = ha->id - 43; + while (vsp->id) { + if (vsp->id == id && vsp->zoneNum == 2) { + if (state == 0) + vsp->flags |= kDFShaded; + else + vsp->flags &= ~kDFShaded; + break; + } + vsp++; + } + } + } +} + +void SimonEngine::invertBox(HitArea * ha, byte a, byte b, byte c, byte d) { + byte *src, color; + int w, h, i; + + _lockWord |= 0x8000; + src = getFrontBuf() + ha->y * _dxSurfacePitch + ha->x; + + _hitarea_unk_3 = true; + + w = ha->width; + h = ha->height; + + // Works around bug in original Simon the Sorcerer 2 + // Animations continue in background when load/save dialog is open + // often causing the savegame name highlighter to be cut short + if (!(h > 0 && w > 0 && ha->x + w <= _screenWidth && ha->y + h <= _screenHeight)) { + debug(1,"Invalid coordinates in invertBox (%d,%d,%d,%d)", ha->x, ha->y, ha->width, ha->height); + _lockWord &= ~0x8000; + return; + } + + do { + for (i = 0; i != w; ++i) { + color = src[i]; + if (a >= color && b < color) { + if (c >= color) + color += d; + else + color -= d; + src[i] = color; + } + } + src += _dxSurfacePitch; + } while (--h); + + _lockWord &= ~0x8000; +} + +} // End of namespace Simon diff --git a/engines/agos/vga.cpp b/engines/agos/vga.cpp new file mode 100644 index 0000000000..d7deb2fbc0 --- /dev/null +++ b/engines/agos/vga.cpp @@ -0,0 +1,2788 @@ +/* ScummVM - Scumm Interpreter + * Copyright (C) 2001 Ludvig Strigeus + * Copyright (C) 2001-2006 The ScummVM project + * + * 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. + * + * $URL$ + * $Id$ + * + */ + +// Video script opcodes for Simon1/Simon2 +#include "common/stdafx.h" + +#include "agos/agos.h" +#include "agos/intern.h" +#include "agos/vga.h" + +#include "common/system.h" + +namespace Simon { + +// Opcode tables +void SimonEngine::setupVgaOpcodes() { + static const VgaOpcodeProc vga_opcode_table[] = { + NULL, + &SimonEngine::vc1_fadeOut, + &SimonEngine::vc2_call, + &SimonEngine::vc3_loadSprite, + &SimonEngine::vc4_fadeIn, + &SimonEngine::vc5_skip_if_neq, + &SimonEngine::vc6_skip_ifn_sib_with_a, + &SimonEngine::vc7_skip_if_sib_with_a, + &SimonEngine::vc8_skip_if_parent_is, + &SimonEngine::vc9_skip_if_unk3_is, + &SimonEngine::vc10_draw, + &SimonEngine::vc11_clearPathFinder, + &SimonEngine::vc12_delay, + &SimonEngine::vc13_addToSpriteX, + &SimonEngine::vc14_addToSpriteY, + &SimonEngine::vc15_sync, + &SimonEngine::vc16_waitSync, + &SimonEngine::vc17_setPathfinderItem, + &SimonEngine::vc18_jump, + &SimonEngine::vc19_chain_to_script, + &SimonEngine::vc20_setRepeat, + &SimonEngine::vc21_endRepeat, + &SimonEngine::vc22_setSpritePalette, + &SimonEngine::vc23_setSpritePriority, + &SimonEngine::vc24_setSpriteXY, + &SimonEngine::vc25_halt_sprite, + &SimonEngine::vc26_setSubWindow, + &SimonEngine::vc27_resetSprite, + &SimonEngine::vc28_dummy_op, + &SimonEngine::vc29_stopAllSounds, + &SimonEngine::vc30_setFrameRate, + &SimonEngine::vc31_setWindow, + &SimonEngine::vc32_copyVar, + &SimonEngine::vc33_setMouseOn, + &SimonEngine::vc34_setMouseOff, + &SimonEngine::vc35_clearWindow, + &SimonEngine::vc36_setWindowImage, + &SimonEngine::vc37_addToSpriteY, + &SimonEngine::vc38_skipIfVarZero, + &SimonEngine::vc39_setVar, + &SimonEngine::vc40, + &SimonEngine::vc41, + &SimonEngine::vc42_delayIfNotEQ, + &SimonEngine::vc43_skipIfBitClear, + &SimonEngine::vc44_skipIfBitSet, + &SimonEngine::vc45_setSpriteX, + &SimonEngine::vc46_setSpriteY, + &SimonEngine::vc47_addToVar, + &SimonEngine::vc48_setPathFinder, + &SimonEngine::vc49_setBit, + &SimonEngine::vc50_clearBit, + &SimonEngine::vc51_enableBox, + &SimonEngine::vc52_playSound, + &SimonEngine::vc53_panSFX, + &SimonEngine::vc54_no_op, + &SimonEngine::vc55_moveBox, + &SimonEngine::vc56_delay, + &SimonEngine::vc57_blackPalette, + &SimonEngine::vc58, + &SimonEngine::vc59, + &SimonEngine::vc60_killSprite, + &SimonEngine::vc61_setMaskImage, + &SimonEngine::vc62_fastFadeOut, + &SimonEngine::vc63_fastFadeIn, + &SimonEngine::vc64_skipIfSpeechEnded, + &SimonEngine::vc65_slowFadeIn, + &SimonEngine::vc66_skipIfNotEqual, + &SimonEngine::vc67_skipIfGE, + &SimonEngine::vc68_skipIfLE, + &SimonEngine::vc69_playTrack, + &SimonEngine::vc70_queueMusic, + &SimonEngine::vc71_checkMusicQueue, + &SimonEngine::vc72_play_track_2, + &SimonEngine::vc73_setMark, + &SimonEngine::vc74_clearMark, + &SimonEngine::vc75_setScale, + &SimonEngine::vc76_setScaleXOffs, + &SimonEngine::vc77_setScaleYOffs, + &SimonEngine::vc78_computeXY, + &SimonEngine::vc79_computePosNum, + &SimonEngine::vc80_setOverlayImage, + &SimonEngine::vc81_setRandom, + &SimonEngine::vc82_getPathValue, + &SimonEngine::vc83_playSoundLoop, + &SimonEngine::vc84_stopSoundLoop, + }; + + _vga_opcode_table = vga_opcode_table; +} + +// Script parser +void SimonEngine::runVgaScript() { + for (;;) { + uint opcode; + + if (_continousVgaScript) { + if (_vcPtr != (const byte *)&_vc_get_out_of_code) { + printf("%.5d %.5X: %5d %4d ", _vgaTickCounter, (unsigned int)(_vcPtr - _curVgaFile1), _vgaCurSpriteId, _vgaCurZoneNum); + dump_video_script(_vcPtr, true); + } + } + + if (getGameType() == GType_SIMON1 || getGameType() == GType_WW) { + opcode = READ_BE_UINT16(_vcPtr); + _vcPtr += 2; + } else { + opcode = *_vcPtr++; + } + + if (opcode >= _numVideoOpcodes) + error("Invalid VGA opcode '%d' encountered", opcode); + + if (opcode == 0) + return; + + (this->*_vga_opcode_table[opcode]) (); + } +} + +bool SimonEngine::itemIsSiblingOf(uint16 a) { + Item *item; + + CHECK_BOUNDS(a, _objectArray); + + item = _objectArray[a]; + if (item == NULL) + return true; + + return me()->parent == item->parent; +} + +bool SimonEngine::itemIsParentOf(uint16 a, uint16 b) { + Item *item_a, *item_b; + + CHECK_BOUNDS(a, _objectArray); + CHECK_BOUNDS(b, _objectArray); + + item_a = _objectArray[a]; + item_b = _objectArray[b]; + + if (item_a == NULL || item_b == NULL) + return true; + + return derefItem(item_a->parent) == item_b; +} + +bool SimonEngine::vc_maybe_skip_proc_1(uint16 a, int16 b) { + Item *item; + + CHECK_BOUNDS(a, _objectArray); + + item = _objectArray[a]; + if (item == NULL) + return true; + return item->state == b; +} + +VgaSprite *SimonEngine::findCurSprite() { + VgaSprite *vsp = _vgaSprites; + while (vsp->id) { + if (getGameType() == GType_SIMON1 || getGameType() == GType_WW) { + if (vsp->id == _vgaCurSpriteId) + break; + } else { + if (vsp->id == _vgaCurSpriteId && vsp->zoneNum == _vgaCurZoneNum) + break; + } + vsp++; + } + return vsp; +} + +int SimonEngine::vcReadVarOrWord() { + int16 var = vcReadNextWord(); + if (var < 0) + var = vcReadVar(-var); + return var; +} + +uint SimonEngine::vcReadNextWord() { + uint a; + a = readUint16Wrapper(_vcPtr); + _vcPtr += 2; + return a; +} + +uint SimonEngine::vcReadNextByte() { + return *_vcPtr++; +} + +uint SimonEngine::vcReadVar(uint var) { + assert(var < _numVars); + return (uint16)_variableArrayPtr[var]; +} + +void SimonEngine::vcWriteVar(uint var, int16 value) { + assert(var < _numVars); + _variableArrayPtr[var] = value; +} + +void SimonEngine::vcSkipNextInstruction() { + static const byte opcodeParamLenWW[] = { + 0, 6, 2, 10, 6, 4, 2, 2, + 4, 4, 8, 2, 2, 2, 2, 2, + 2, 2, 2, 0, 4, 2, 2, 2, + 8, 0, 10, 0, 8, 0, 2, 2, + 0, 0, 0, 4, 4, 4, 2, 4, + 4, 4, 4, 2, 2, 4, 2, 2, + 2, 2, 2, 2, 2, 4, 6, 6, + 0, 0, 0, 0, 2, 2, 0, 0, + }; + + static const byte opcodeParamLenSimon1[] = { + 0, 6, 2, 10, 6, 4, 2, 2, + 4, 4, 10, 0, 2, 2, 2, 2, + 2, 0, 2, 0, 4, 2, 4, 2, + 8, 0, 10, 0, 8, 0, 2, 2, + 4, 0, 0, 4, 4, 2, 2, 4, + 4, 4, 4, 2, 2, 2, 2, 4, + 0, 2, 2, 2, 2, 4, 6, 6, + 0, 0, 0, 0, 2, 6, 0, 0, + }; + + static const byte opcodeParamLenSimon2[] = { + 0, 6, 2, 12, 6, 4, 2, 2, + 4, 4, 9, 0, 1, 2, 2, 2, + 2, 0, 2, 0, 4, 2, 4, 2, + 7, 0, 10, 0, 8, 0, 2, 2, + 4, 0, 0, 4, 4, 2, 2, 4, + 4, 4, 4, 2, 2, 2, 2, 4, + 0, 2, 2, 2, 2, 4, 6, 6, + 2, 0, 6, 6, 4, 6, 0, 0, + 0, 0, 4, 4, 4, 4, 4, 0, + 4, 2, 2 + }; + + static const byte opcodeParamLenFeebleFiles[] = { + 0, 6, 2, 12, 6, 4, 2, 2, + 4, 4, 9, 0, 1, 2, 2, 2, + 2, 0, 2, 0, 4, 2, 4, 2, + 7, 0, 10, 0, 8, 0, 2, 2, + 4, 0, 0, 4, 4, 2, 2, 4, + 4, 4, 4, 2, 2, 2, 2, 4, + 0, 2, 2, 2, 6, 6, 6, 6, + 2, 0, 6, 6, 4, 6, 0, 0, + 0, 0, 4, 4, 4, 4, 4, 0, + 4, 2, 2, 4, 6, 6, 0, 0, + 6, 4, 2, 6, 0 + }; + + uint16 opcode; + if (getGameType() == GType_FF || getGameType() == GType_PP) { + opcode = vcReadNextByte(); + _vcPtr += opcodeParamLenFeebleFiles[opcode]; + } else if (getGameType() == GType_SIMON2) { + opcode = vcReadNextByte(); + _vcPtr += opcodeParamLenSimon2[opcode]; + } else if (getGameType() == GType_SIMON1) { + opcode = vcReadNextWord(); + _vcPtr += opcodeParamLenSimon1[opcode]; + } else { + opcode = vcReadNextWord(); + _vcPtr += opcodeParamLenWW[opcode]; + } + + if (_continousVgaScript) + printf("; skipped\n"); +} + +// VGA Script commands +void SimonEngine::vc1_fadeOut() { + /* dummy opcode */ + _vcPtr += 6; +} + +void SimonEngine::vc2_call() { + VgaPointersEntry *vpe; + uint16 count, num, res; + byte *old_file_1, *old_file_2; + byte *b, *bb; + const byte *vcPtrOrg; + + num = vcReadVarOrWord(); + + old_file_1 = _curVgaFile1; + old_file_2 = _curVgaFile2; + + for (;;) { + res = num / 100; + vpe = &_vgaBufferPointers[res]; + + _curVgaFile1 = vpe->vgaFile1; + _curVgaFile2 = vpe->vgaFile2; + if (vpe->vgaFile1 != NULL) + break; + if (_zoneNumber != res) + _noOverWrite = _zoneNumber; + + loadZone(res); + _noOverWrite = 0xFFFF; + } + + + bb = _curVgaFile1; + if (getGameType() == GType_FF || getGameType() == GType_PP) { + b = bb + READ_LE_UINT16(&((VgaFileHeader_Feeble *) bb)->hdr2_start); + count = READ_LE_UINT16(&((VgaFileHeader2_Feeble *) b)->imageCount); + b = bb + READ_LE_UINT16(&((VgaFileHeader2_Feeble *) b)->imageTable); + + while (count--) { + if (READ_LE_UINT16(&((ImageHeader_Feeble *) b)->id) == num) + break; + b += sizeof(ImageHeader_Feeble); + } + assert(READ_LE_UINT16(&((ImageHeader_Feeble *) b)->id) == num); + } else if (getGameType() == GType_SIMON1 || getGameType() == GType_SIMON2) { + b = bb + READ_BE_UINT16(&((VgaFileHeader_Simon *) bb)->hdr2_start); + count = READ_BE_UINT16(&((VgaFileHeader2_Simon *) b)->imageCount); + b = bb + READ_BE_UINT16(&((VgaFileHeader2_Simon *) b)->imageTable); + + while (count--) { + if (READ_BE_UINT16(&((ImageHeader_Simon *) b)->id) == num) + break; + b += sizeof(ImageHeader_Simon); + } + assert(READ_BE_UINT16(&((ImageHeader_Simon *) b)->id) == num); + } else { + b = bb + READ_BE_UINT16(bb + 10); + b += 20; + + count = READ_BE_UINT16(&((VgaFileHeader2_WW *) b)->imageCount); + b = bb + READ_BE_UINT16(&((VgaFileHeader2_WW *) b)->imageTable); + + while (count--) { + if (READ_BE_UINT16(&((ImageHeader_WW *) b)->id) == num) + break; + b += sizeof(ImageHeader_WW); + } + assert(READ_BE_UINT16(&((ImageHeader_WW *) b)->id) == num); + } + + vcPtrOrg = _vcPtr; + + if (getGameType() == GType_FF || getGameType() == GType_PP) { + _vcPtr = _curVgaFile1 + READ_LE_UINT16(&((ImageHeader_Feeble *) b)->scriptOffs); + } else if (getGameType() == GType_SIMON1 || getGameType() == GType_SIMON2) { + _vcPtr = _curVgaFile1 + READ_BE_UINT16(&((ImageHeader_Simon *) b)->scriptOffs); + } else { + _vcPtr = _curVgaFile1 + READ_BE_UINT16(&((ImageHeader_WW *) b)->scriptOffs); + } + + //dump_vga_script(_vcPtr, res, num); + runVgaScript(); + + _curVgaFile1 = old_file_1; + _curVgaFile2 = old_file_2; + + _vcPtr = vcPtrOrg; +} + +void SimonEngine::vc3_loadSprite() { + uint16 windowNum, zoneNum, palette, x, y, vgaSpriteId; + uint16 count, res; + VgaSprite *vsp; + VgaPointersEntry *vpe; + byte *p, *pp; + byte *old_file_1; + + windowNum = vcReadNextWord(); /* 0 */ + + if (getGameType() == GType_SIMON1 || getGameType() == GType_WW) { + vgaSpriteId = vcReadNextWord(); /* 2 */ + zoneNum = vgaSpriteId / 100; + } else { + zoneNum = vcReadNextWord(); /* 0 */ + vgaSpriteId = vcReadNextWord(); /* 2 */ + } + + x = vcReadNextWord(); /* 4 */ + y = vcReadNextWord(); /* 6 */ + palette = vcReadNextWord(); /* 8 */ + + if (isSpriteLoaded(vgaSpriteId, zoneNum)) + return; + + vsp = _vgaSprites; + while (vsp->id) + vsp++; + + if (getGameType() == GType_WW) + vsp->palette = 0; + else + vsp->palette = palette; + vsp->windowNum = windowNum; + vsp->priority = 0; + vsp->flags = 0; + vsp->image = 0; + vsp->x = x; + vsp->y = y; + vsp->id = vgaSpriteId; + vsp->zoneNum = res = zoneNum; + + old_file_1 = _curVgaFile1; + for (;;) { + vpe = &_vgaBufferPointers[res]; + _curVgaFile1 = vpe->vgaFile1; + + if (vpe->vgaFile1 != NULL) + break; + if (_zoneNumber != res) + _noOverWrite = _zoneNumber; + + loadZone(res); + _noOverWrite = 0xFFFF; + } + + pp = _curVgaFile1; + if (getGameType() == GType_FF || getGameType() == GType_PP) { + p = pp + READ_LE_UINT16(&((VgaFileHeader_Feeble *) pp)->hdr2_start); + count = READ_LE_UINT16(&((VgaFileHeader2_Feeble *) p)->animationCount); + p = pp + READ_LE_UINT16(&((VgaFileHeader2_Feeble *) p)->animationTable); + + while (count--) { + if (READ_LE_UINT16(&((AnimationHeader_Feeble *) p)->id) == vgaSpriteId) + break; + p += sizeof(AnimationHeader_Feeble); + } + assert(READ_LE_UINT16(&((AnimationHeader_Feeble *) p)->id) == vgaSpriteId); + } else if (getGameType() == GType_SIMON1 || getGameType() == GType_SIMON2) { + p = pp + READ_BE_UINT16(&((VgaFileHeader_Simon *) pp)->hdr2_start); + count = READ_BE_UINT16(&((VgaFileHeader2_Simon *) p)->animationCount); + p = pp + READ_BE_UINT16(&((VgaFileHeader2_Simon *) p)->animationTable); + + while (count--) { + if (READ_BE_UINT16(&((AnimationHeader_Simon *) p)->id) == vgaSpriteId) + break; + p += sizeof(AnimationHeader_Simon); + } + assert(READ_BE_UINT16(&((AnimationHeader_Simon *) p)->id) == vgaSpriteId); + } else { + p = pp + READ_BE_UINT16(pp + 10); + p += 20; + + count = READ_BE_UINT16(&((VgaFileHeader2_WW *) p)->animationCount); + p = pp + READ_BE_UINT16(&((VgaFileHeader2_WW *) p)->animationTable); + + while (count--) { + if (READ_BE_UINT16(&((AnimationHeader_WW *) p)->id) == vgaSpriteId) + break; + p += sizeof(AnimationHeader_WW); + } + assert(READ_BE_UINT16(&((AnimationHeader_WW *) p)->id) == vgaSpriteId); + } + +#ifdef DUMP_FILE_NR + { + static bool dumped = false; + if (res == DUMP_FILE_NR && !dumped) { + dumped = true; + dump_vga_file(_curVgaFile1); + } + } +#endif + +#ifdef DUMP_BITMAPS_FILE_NR + { + static bool dumped = false; + if (res == DUMP_BITMAPS_FILE_NR && !dumped) { + dumped = true; + dump_vga_bitmaps(_curVgaFile2, _curVgaFile1, res); + } + } +#endif + + if (_startVgaScript) { + if (getGameType() == GType_FF || getGameType() == GType_PP) { + dump_vga_script(_curVgaFile1 + READ_LE_UINT16(&((AnimationHeader_Feeble*)p)->scriptOffs), res, vgaSpriteId); + } else if (getGameType() == GType_SIMON1 || getGameType() == GType_SIMON2) { + dump_vga_script(_curVgaFile1 + READ_BE_UINT16(&((AnimationHeader_Simon*)p)->scriptOffs), res, vgaSpriteId); + } else { + dump_vga_script(_curVgaFile1 + READ_BE_UINT16(&((AnimationHeader_WW*)p)->scriptOffs), res, vgaSpriteId); + } + } + + if (getGameType() == GType_FF || getGameType() == GType_PP) { + addVgaEvent(_vgaBaseDelay, _curVgaFile1 + READ_LE_UINT16(&((AnimationHeader_Feeble *) p)->scriptOffs), vgaSpriteId, res); + } else if (getGameType() == GType_SIMON1 || getGameType() == GType_SIMON2) { + addVgaEvent(_vgaBaseDelay, _curVgaFile1 + READ_BE_UINT16(&((AnimationHeader_Simon *) p)->scriptOffs), vgaSpriteId, res); + } else { + addVgaEvent(_vgaBaseDelay, _curVgaFile1 + READ_BE_UINT16(&((AnimationHeader_WW *) p)->scriptOffs), vgaSpriteId, res); + } + + _curVgaFile1 = old_file_1; +} + +void SimonEngine::vc4_fadeIn() { + /* dummy opcode */ + _vcPtr += 6; +} + +void SimonEngine::vc5_skip_if_neq() { + uint16 var = vcReadNextWord(); + uint16 value = vcReadNextWord(); + if (vcReadVar(var) != value) + vcSkipNextInstruction(); +} + +void SimonEngine::vc6_skip_ifn_sib_with_a() { + if (!itemIsSiblingOf(vcReadNextWord())) + vcSkipNextInstruction(); +} + +void SimonEngine::vc7_skip_if_sib_with_a() { + if (itemIsSiblingOf(vcReadNextWord())) + vcSkipNextInstruction(); +} + +void SimonEngine::vc8_skip_if_parent_is() { + uint16 a = vcReadNextWord(); + uint16 b = vcReadNextWord(); + if (!itemIsParentOf(a, b)) + vcSkipNextInstruction(); +} + +void SimonEngine::vc9_skip_if_unk3_is() { + uint16 a = vcReadNextWord(); + uint16 b = vcReadNextWord(); + if (!vc_maybe_skip_proc_1(a, b)) + vcSkipNextInstruction(); +} + +byte *vc10_depackColumn(VC10_state * vs) { + int8 a = vs->depack_cont; + const byte *src = vs->depack_src; + byte *dst = vs->depack_dest; + uint16 dh = vs->dh; + byte color; + + if (a == -0x80) + a = *src++; + + for (;;) { + if (a >= 0) { + color = *src++; + do { + *dst++ = color; + if (!--dh) { + if (--a < 0) + a = -0x80; + else + src--; + goto get_out; + } + } while (--a >= 0); + } else { + do { + *dst++ = *src++; + if (!--dh) { + if (++a == 0) + a = -0x80; + goto get_out; + } + } while (++a != 0); + } + a = *src++; + } + +get_out:; + vs->depack_src = src; + vs->depack_cont = a; + return vs->depack_dest + vs->y_skip; +} + +void vc10_skip_cols(VC10_state *vs) { + while (vs->x_skip) { + vc10_depackColumn(vs); + vs->x_skip--; + } +} + +byte *SimonEngine::vc10_uncompressFlip(const byte *src, uint w, uint h) { + w *= 8; + + byte *src_org, *dst_org; + byte color; + int8 cur = -0x80; + uint i, w_cur = w; + + dst_org = _videoBuf1 + w; + + do { + byte *dst = dst_org; + uint h_cur = h; + + if (cur == -0x80) + cur = *src++; + + for (;;) { + if (cur >= 0) { + /* rle_same */ + color = *src++; + do { + *dst = color; + dst += w; + if (!--h_cur) { + if (--cur < 0) + cur = -0x80; + else + src--; + goto next_line; + } + } while (--cur >= 0); + } else { + /* rle_diff */ + do { + *dst = *src++; + dst += w; + if (!--h_cur) { + if (++cur == 0) + cur = -0x80; + goto next_line; + } + } while (++cur != 0); + } + cur = *src++; + } + next_line: + dst_org++; + } while (--w_cur); + + + src_org = dst_org = _videoBuf1 + w; + + do { + byte *dst = dst_org; + for (i = 0; i != w; ++i) { + byte b = src_org[i]; + b = (b >> 4) | (b << 4); + *--dst = b; + } + + src_org += w; + dst_org += w; + } while (--h); + + return _videoBuf1; +} + +byte *SimonEngine::vc10_flip(const byte *src, uint w, uint h) { + if (src == _vc10BasePtrOld) + return _videoBuf1; + + _vc10BasePtrOld = src; + + byte *dst_org, *src_org; + uint i; + + w *= 8; + src_org = dst_org = _videoBuf1 + w; + + do { + byte *dst = dst_org; + for (i = 0; i != w; ++i) { + byte b = src_org[i]; + b = (b >> 4) | (b << 4); + *--dst = b; + } + + src_org += w; + dst_org += w; + } while (--h); + + return _videoBuf1; +} + +/* must not be const */ +static uint16 _video_windows[128] = { + 0, 0, 20, 200, + 0, 0, 3, 136, + 17, 0, 3, 136, + 0, 0, 20, 200, + 0, 0, 20, 134 +}; + +/* Elvira 1/2 & Waxworks +static uint16 _video_windows[128] = { + 3, 0, 14, 136, + 0, 0, 3, 136, + 17, 0, 3, 136, + 0, 0, 20, 200, + 3, 3, 14, 127, +}; + */ + +void SimonEngine::decodeColumn(byte *dst, const byte *src, int height) { + const uint pitch = _dxSurfacePitch; + int8 reps = (int8)0x80; + byte color; + byte *dst_org = dst; + uint h = height, w = 8; + + for (;;) { + reps = *src++; + if (reps >= 0) { + color = *src++; + + do { + *dst = color; + dst += pitch; + + /* reached bottom? */ + if (--h == 0) { + /* reached right edge? */ + if (--w == 0) + return; + dst = ++dst_org; + h = height; + } + } while (--reps >= 0); + } else { + + do { + *dst = *src++; + dst += pitch; + + /* reached bottom? */ + if (--h == 0) { + /* reached right edge? */ + if (--w == 0) + return; + dst = ++dst_org; + h = height; + } + } while (++reps != 0); + } + } +} + +void SimonEngine::decodeRow(byte *dst, const byte *src, int width) { + const uint pitch = _dxSurfacePitch; + int8 reps = (int8)0x80; + byte color; + byte *dst_org = dst; + uint w = width, h = 8; + + for (;;) { + reps = *src++; + if (reps >= 0) { + color = *src++; + + do { + *dst++ = color; + + /* reached right edge? */ + if (--w == 0) { + /* reached bottom? */ + if (--h == 0) + return; + dst_org += pitch; + dst = dst_org; + w = width; + } + } while (--reps >= 0); + } else { + + do { + *dst++ = *src++; + + /* reached right edge? */ + if (--w == 0) { + /* reached bottom? */ + if (--h == 0) + return; + dst_org += pitch; + dst = dst_org; + w = width; + } + } while (++reps != 0); + } + } +} + +void SimonEngine::vc10_draw() { + byte *p2; + uint width, height; + byte flags; + VC10_state state; + + state.image = (int16)vcReadNextWord(); + if (state.image == 0) + return; + + if (getGameType() == GType_FF || getGameType() == GType_PP) { + state.palette = (_vcPtr[0] * 16); + _vcPtr += 2; + } else if (getGameType() == GType_SIMON1 || getGameType() == GType_SIMON2) { + state.palette = (_vcPtr[1] * 16); + _vcPtr += 2; + } else { + state.palette = 0; + } + + state.x = (int16)vcReadNextWord(); + state.x -= _scrollX; + + state.y = (int16)vcReadNextWord(); + state.y -= _scrollY; + + if (getGameType() == GType_SIMON1 || getGameType() == GType_WW) { + state.flags = vcReadNextWord(); + } else { + state.flags = vcReadNextByte(); + } + + if (state.image < 0) + state.image = vcReadVar(-state.image); + + p2 = _curVgaFile2 + state.image * 8; + state.depack_src = _curVgaFile2 + readUint32Wrapper(p2); + if (getGameType() == GType_FF || getGameType() == GType_PP) { + width = READ_LE_UINT16(p2 + 6); + height = READ_LE_UINT16(p2 + 4) & 0x7FFF; + flags = p2[5]; + } else { + width = READ_BE_UINT16(p2 + 6) / 16; + height = p2[5]; + flags = p2[4]; + } + + if (height == 0 || width == 0) + return; + + if (_dumpImages) + dump_single_bitmap(_vgaCurZoneNum, state.image, state.depack_src, width, height, + state.palette); + // Check if image is compressed + if (getGameType() == GType_FF || getGameType() == GType_PP) { + if (flags & 0x80) { + state.flags |= kDFCompressed; + } + } else { + if (flags & 0x80 && !(state.flags & kDFCompressedFlip)) { + if (state.flags & kDFFlip) { + state.flags &= ~kDFFlip; + state.flags |= kDFCompressedFlip; + } else { + state.flags |= kDFCompressed; + } + } + } + + state.width = state.draw_width = width; /* cl */ + state.height = state.draw_height = height; /* ch */ + + state.depack_cont = -0x80; + + state.x_skip = 0; /* colums to skip = bh */ + state.y_skip = 0; /* rows to skip = bl */ + + uint maxWidth = (getGameType() == GType_FF || getGameType() == GType_PP) ? 640 : 20; + if ((getGameType() == GType_SIMON2 || getGameType() == GType_FF) && width > maxWidth) { + horizontalScroll(&state); + return; + } + if (getGameType() == GType_FF && height > 480) { + verticalScroll(&state); + return; + } + + if (getGameType() == GType_SIMON1 || getGameType() == GType_SIMON2 || getGameType() == GType_WW) { + if (state.flags & kDFCompressedFlip) { + state.depack_src = vc10_uncompressFlip(state.depack_src, width, height); + } else if (state.flags & kDFFlip) { + state.depack_src = vc10_flip(state.depack_src, width, height); + } + } + + state.surf2_addr = getFrontBuf(); + state.surf2_pitch = _dxSurfacePitch; + + state.surf_addr = getBackBuf(); + state.surf_pitch = _dxSurfacePitch; + + if (getGameType() == GType_FF || getGameType() == GType_PP) { + drawImages_Feeble(&state); + } else { + drawImages(&state); + } +} + +bool SimonEngine::drawImages_clip(VC10_state *state) { + const uint16 *vlut; + uint maxWidth, maxHeight; + int cur; + + vlut = &_video_windows[_windowNum * 4]; + + if (getGameType() == GType_SIMON1 || getGameType() == GType_SIMON2 || getGameType() == GType_WW) { + state->draw_width = state->width * 2; + } + + cur = state->x; + if (cur < 0) { + do { + if (!--state->draw_width) + return 0; + state->x_skip++; + } while (++cur); + } + state->x = cur; + + maxWidth = (getGameType() == GType_FF || getGameType() == GType_PP) ? _screenWidth : (vlut[2] * 2); + cur += state->draw_width - maxWidth; + if (cur > 0) { + do { + if (!--state->draw_width) + return 0; + } while (--cur); + } + + cur = state->y; + if (cur < 0) { + do { + if (!--state->draw_height) + return 0; + state->y_skip++; + } while (++cur); + } + state->y = cur; + + maxHeight = (getGameType() == GType_FF || getGameType() == GType_PP) ? _screenHeight : vlut[3]; + cur += state->draw_height - maxHeight; + if (cur > 0) { + do { + if (!--state->draw_height) + return 0; + } while (--cur); + } + + assert(state->draw_width != 0 && state->draw_height != 0); + + if (getGameType() == GType_SIMON1 || getGameType() == GType_SIMON2 || getGameType() == GType_WW) { + state->draw_width *= 4; + } + + return 1; +} + +void SimonEngine::drawImages_Feeble(VC10_state *state) { + if (state->flags & kDFCompressed) { + if (state->flags & kDFScaled) { + state->surf_addr = getScaleBuf(); + state->surf_pitch = _dxSurfacePitch; + + uint w, h; + byte *src, *dst, *dst_org; + + state->dl = state->width; + state->dh = state->height; + + dst_org = state->surf_addr; + w = 0; + do { + src = vc10_depackColumn(state); + dst = dst_org; + + h = 0; + do { + *dst = *src; + dst += _screenWidth; + src++; + } while (++h != state->draw_height); + dst_org++; + } while (++w != state->draw_width); + + if (_vgaCurSpritePriority % 10 != 9) { + _scaleX = state->x; + _scaleY = state->y; + _scaleWidth = state->width; + _scaleHeight = state->height; + } else { + scaleClip(state->height, state->width, state->y, state->x, state->y + _scrollY); + } + } else if (state->flags & kDFOverlayed) { + state->surf_addr = getScaleBuf(); + state->surf_pitch = _dxSurfacePitch; + state->surf_addr += (state->x + _scrollX) + (state->y + _scrollY) * state->surf_pitch; + + uint w, h; + byte *src, *dst, *dst_org; + + state->dl = state->width; + state->dh = state->height; + + dst_org = state->surf_addr; + w = 0; + do { + byte color; + + src = vc10_depackColumn(state); + dst = dst_org; + + h = 0; + do { + color = *src; + if (color != 0) + *dst = color; + dst += _screenWidth; + src++; + } while (++h != state->draw_height); + dst_org++; + } while (++w != state->draw_width); + + if (_vgaCurSpritePriority % 10 == 9) { + scaleClip(_scaleHeight, _scaleWidth, _scaleY, _scaleX, _scaleY + _scrollY); + } + } else { + if (drawImages_clip(state) == 0) + return; + + state->surf_addr += state->x + state->y * state->surf_pitch; + + uint w, h; + byte *src, *dst, *dst_org; + + state->dl = state->width; + state->dh = state->height; + + vc10_skip_cols(state); + + + if (state->flags & kDFMasked) { + if (getGameType() == GType_FF && !getBitFlag(81)) { + if (state->x > _feebleRect.right) + return; + if (state->y > _feebleRect.bottom) + return; + if (state->x + state->width < _feebleRect.left) + return; + if (state->y + state->height < _feebleRect.top) + return; + } + + dst_org = state->surf_addr; + w = 0; + do { + byte color; + + src = vc10_depackColumn(state); + dst = dst_org; + + h = 0; + do { + color = *src; + if (color) + *dst = color; + dst += _screenWidth; + src++; + } while (++h != state->draw_height); + dst_org++; + } while (++w != state->draw_width); + } else { + dst_org = state->surf_addr; + w = 0; + do { + byte color; + + src = vc10_depackColumn(state); + dst = dst_org; + + h = 0; + do { + color = *src; + if ((state->flags & kDFNonTrans) || color != 0) + *dst = color; + dst += _screenWidth; + src++; + } while (++h != state->draw_height); + dst_org++; + } while (++w != state->draw_width); + } + } + } else { + if (drawImages_clip(state) == 0) + return; + + state->surf_addr += state->x + state->y * state->surf_pitch; + + const byte *src; + byte *dst; + uint count; + + src = state->depack_src + state->width * state->y_skip; + dst = state->surf_addr; + do { + for (count = 0; count != state->draw_width; count++) { + byte color; + color = src[count + state->x_skip]; + if (color) { + if ((state->flags & kDFShaded) && color == 220) + color = 244; + + dst[count] = color; + } + } + dst += _screenWidth; + src += state->width; + } while (--state->draw_height); + } +} + +void SimonEngine::drawImages(VC10_state *state) { + const uint16 *vlut = &_video_windows[_windowNum * 4]; + + if (drawImages_clip(state) == 0) + return; + + uint offs, offs2; + // Allow one section of Simon the Sorcerer 1 introduction to be displayed + // in lower half of screen + if (getGameType() == GType_WW) { + //if (_windowNum == 4 || _windowNum >= 10) { + offs = state->x * 8; + offs2 = state->y; + //} else { + // offs = ((vlut[0] - _video_windows[16]) * 2 + state->x) * 8; + // offs2 = (vlut[1] - _video_windows[17] + state->y); + //} + } else if (getGameType() == GType_SIMON1) { + if (_windowNum != 2 || _windowNum != 3) { + offs = ((vlut[0]) * 2 + state->x) * 8; + offs2 = (vlut[1] + state->y); + } else { + offs = ((vlut[0] - _video_windows[16]) * 2 + state->x) * 8; + offs2 = (vlut[1] - _video_windows[17] + state->y); + } + } else { + offs = ((vlut[0] - _video_windows[16]) * 2 + state->x) * 8; + offs2 = (vlut[1] - _video_windows[17] + state->y); + } + + state->surf2_addr += offs + offs2 * state->surf2_pitch; + state->surf_addr += offs + offs2 * state->surf_pitch; + + if (state->flags & kDFMasked) { + byte *mask, *src, *dst; + byte h; + uint w; + + state->x_skip *= 4; + state->dl = state->width; + state->dh = state->height; + + vc10_skip_cols(state); + + w = 0; + do { + mask = vc10_depackColumn(state); /* esi */ + src = state->surf2_addr + w * 2; /* ebx */ + dst = state->surf_addr + w * 2; /* edi */ + + h = state->draw_height; + if ((getGameType() == GType_SIMON1) && getBitFlag(88)) { + /* transparency */ + do { + if (mask[0] & 0xF0) { + if ((dst[0] & 0x0F0) == 0x20) + dst[0] = src[0]; + } + if (mask[0] & 0x0F) { + if ((dst[1] & 0x0F0) == 0x20) + dst[1] = src[1]; + } + mask++; + dst += state->surf_pitch; + src += state->surf2_pitch; + } while (--h); + } else { + /* no transparency */ + do { + if (mask[0] & 0xF0) + dst[0] = src[0]; + if (mask[0] & 0x0F) + dst[1] = src[1]; + mask++; + dst += state->surf_pitch; + src += state->surf2_pitch; + } while (--h); + } + } while (++w != state->draw_width); + + /* vc10_helper_5 */ + } else if ((((_lockWord & 0x20) && state->palette == 0) || state->palette == 0xC0) && + getGameType() != GType_WW) { + const byte *src; + byte *dst; + uint h, i; + + if (state->flags & kDFCompressed) { + byte *dst_org = state->surf_addr; + src = state->depack_src; + /* AAAAAAAA BBBBBBBB CCCCCCCC DDDDDDDD EEEEEEEE + * aaaaabbb bbcccccd ddddeeee efffffgg ggghhhhh + */ + + do { + uint count = state->draw_width / 4; + + dst = dst_org; + do { + uint32 bits = (src[0] << 24) | (src[1] << 16) | (src[2] << 8) | (src[3]); + byte color; + + color = (byte)((bits >> (32 - 5)) & 31); + if ((state->flags & kDFNonTrans) || color) + dst[0] = color; + color = (byte)((bits >> (32 - 10)) & 31); + if ((state->flags & kDFNonTrans) || color) + dst[1] = color; + color = (byte)((bits >> (32 - 15)) & 31); + if ((state->flags & kDFNonTrans) || color) + dst[2] = color; + color = (byte)((bits >> (32 - 20)) & 31); + if ((state->flags & kDFNonTrans) || color) + dst[3] = color; + color = (byte)((bits >> (32 - 25)) & 31); + if ((state->flags & kDFNonTrans) || color) + dst[4] = color; + color = (byte)((bits >> (32 - 30)) & 31); + if ((state->flags & kDFNonTrans) || color) + dst[5] = color; + + bits = (bits << 8) | src[4]; + + color = (byte)((bits >> (40 - 35)) & 31); + if ((state->flags & kDFNonTrans) || color) + dst[6] = color; + color = (byte)((bits) & 31); + if ((state->flags & kDFNonTrans) || color) + dst[7] = color; + + dst += 8; + src += 5; + } while (--count); + dst_org += _screenWidth; + } while (--state->draw_height); + } else { + src = state->depack_src + (state->width * state->y_skip * 16) + (state->x_skip * 8); + dst = state->surf_addr; + + state->draw_width *= 2; + + h = state->draw_height; + do { + for (i = 0; i != state->draw_width; i++) + if ((state->flags & kDFNonTrans) || src[i]) + dst[i] = src[i]; + dst += _screenWidth; + src += state->width * 16; + } while (--h); + } + /* vc10_helper_4 */ + } else { + if (getGameType() == GType_SIMON2 && state->flags & kDFUseFrontBuf && getBitFlag(171)) { + state->surf_addr = state->surf2_addr; + state->surf_pitch = state->surf2_pitch; + } + + if (state->flags & kDFCompressed) { + uint w, h; + byte *src, *dst, *dst_org; + + state->x_skip *= 4; /* reached */ + + state->dl = state->width; + state->dh = state->height; + + vc10_skip_cols(state); + + dst_org = state->surf_addr; + if (!(state->flags & kDFNonTrans) && (state->flags & 0x40)) { /* reached */ + dst_org += vcReadVar(252); + } + w = 0; + do { + byte color; + + src = vc10_depackColumn(state); + dst = dst_org; + + h = 0; + do { + color = (*src / 16); + if ((state->flags & kDFNonTrans) || color != 0) + dst[0] = color | state->palette; + color = (*src & 15); + if ((state->flags & kDFNonTrans) || color != 0) + dst[1] = color | state->palette; + dst += _screenWidth; + src++; + } while (++h != state->draw_height); + dst_org += 2; + } while (++w != state->draw_width); + /* vc10_helper_6 */ + } else { + const byte *src; + byte *dst; + uint count; + + src = state->depack_src + (state->width * state->y_skip) * 8; + dst = state->surf_addr; + state->x_skip *= 4; + do { + for (count = 0; count != state->draw_width; count++) { + byte color; + color = (src[count + state->x_skip] / 16); + if ((state->flags & kDFNonTrans) || color) + dst[count * 2] = color | state->palette; + color = (src[count + state->x_skip] & 15); + if ((state->flags & kDFNonTrans) || color) + dst[count * 2 + 1] = color | state->palette; + } + dst += _screenWidth; + src += state->width * 8; + } while (--state->draw_height); + /* vc10_helper_7 */ + } + } +} + +void SimonEngine::horizontalScroll(VC10_state *state) { + const byte *src; + byte *dst; + int w; + + if (getGameType() == GType_FF) + _scrollXMax = state->width - 640; + else + _scrollXMax = state->width * 2 - 40; + _scrollYMax = 0; + _scrollImage = state->depack_src; + _scrollHeight = state->height; + if (_variableArrayPtr[34] < 0) + state->x = _variableArrayPtr[251]; + + _scrollX = state->x; + + vcWriteVar(251, _scrollX); + + dst = getBackBuf(); + + if (getGameType() == GType_FF) + src = state->depack_src + _scrollX / 2; + else + src = state->depack_src + _scrollX * 4; + + for (w = 0; w < _screenWidth; w += 8) { + decodeColumn(dst, src + readUint32Wrapper(src), state->height); + dst += 8; + src += 4; + } +} + +void SimonEngine::verticalScroll(VC10_state *state) { + const byte *src; + byte *dst; + int h; + + _scrollXMax = 0; + _scrollYMax = state->height - 480; + _scrollImage = state->depack_src; + _scrollWidth = state->width; + if (_variableArrayPtr[34] < 0) + state->y = _variableArrayPtr[250]; + + _scrollY = state->y; + + vcWriteVar(250, _scrollY); + + dst = getBackBuf(); + src = state->depack_src + _scrollY / 2; + + for (h = 0; h < _screenHeight; h += 8) { + decodeRow(dst, src + READ_LE_UINT32(src), state->width); + dst += 8 * state->width; + src += 4; + } +} + +void SimonEngine::scaleClip(int16 h, int16 w, int16 y, int16 x, int16 scrollY) { + Common::Rect srcRect, dstRect; + float factor, xscale; + + srcRect.left = 0; + srcRect.top = 0; + srcRect.right = w; + srcRect.bottom = h; + + if (scrollY > _baseY) + factor = 1 + ((scrollY - _baseY) * _scale); + else + factor = 1 - ((_baseY - scrollY) * _scale); + + xscale = ((w * factor) / 2); + + dstRect.left = (int16)(x - xscale); + if (dstRect.left > _screenWidth - 1) + return; + dstRect.top = (int16)(y - (h * factor)); + if (dstRect.top > _screenHeight - 1) + return; + + dstRect.right = (int16)(x + xscale); + dstRect.bottom = y; + + _feebleRect = dstRect; + + _variableArray[20] = _feebleRect.top; + _variableArray[21] = _feebleRect.left; + _variableArray[22] = _feebleRect.bottom; + _variableArray[23] = _feebleRect.right; + + debug(5, "Left %d Right %d Top %d Bottom %d", dstRect.left, dstRect.right, dstRect.top, dstRect.bottom); + + // Unlike normal rectangles in ScummVM, it seems that in the case of + // the destination rectangle the bottom and right coordinates are + // considered to be inside the rectangle. For the source rectangle, + // I believe that they are not. + + int scaledW = dstRect.width() + 1; + int scaledH = dstRect.height() + 1; + + byte *src = getScaleBuf(); + byte *dst = getBackBuf(); + + dst += _dxSurfacePitch * dstRect.top + dstRect.left; + + for (int dstY = 0; dstY < scaledH; dstY++) { + if (dstRect.top + dstY >= 0 && dstRect.top + dstY < _screenHeight) { + int srcY = (dstY * h) / scaledH; + byte *srcPtr = src + _dxSurfacePitch * srcY; + byte *dstPtr = dst + _dxSurfacePitch * dstY; + for (int dstX = 0; dstX < scaledW; dstX++) { + if (dstRect.left + dstX >= 0 && dstRect.left + dstX < _screenWidth) { + int srcX = (dstX * w) / scaledW; + if (srcPtr[srcX]) + dstPtr[dstX] = srcPtr[srcX]; + } + } + } + } +} + +void SimonEngine::vc11_clearPathFinder() { + if (getGameType() == GType_WW) { + // FIXME + vcReadNextWord(); + } else { + memset(&_pathFindArray, 0, sizeof(_pathFindArray)); + } +} + +void SimonEngine::vc12_delay() { + VgaSprite *vsp = findCurSprite(); + uint16 num; + + if (getGameType() == GType_FF || getGameType() == GType_PP) { + num = vcReadNextByte(); + } else if (getGameType() == GType_SIMON2) { + num = vcReadNextByte() * _frameRate; + } else { + num = vcReadVarOrWord(); + } + + // Work around to allow inventory arrows to be + // shown in some versions of Simon the Sorcerer 1 + if ((getGameType() == GType_SIMON1) && vsp->id == 128) + num = 0; + else + num += _vgaBaseDelay; + + addVgaEvent(num, _vcPtr, _vgaCurSpriteId, _vgaCurZoneNum); + _vcPtr = (byte *)&_vc_get_out_of_code; +} + +void SimonEngine::vc13_addToSpriteX() { + VgaSprite *vsp = findCurSprite(); + vsp->x += (int16)vcReadNextWord(); + _vgaSpriteChanged++; +} + +void SimonEngine::vc14_addToSpriteY() { + VgaSprite *vsp = findCurSprite(); + vsp->y += (int16)vcReadNextWord(); + _vgaSpriteChanged++; +} + +void SimonEngine::vc15_sync() { + VgaSleepStruct *vfs = _vgaSleepStructs, *vfs_tmp; + uint16 id = vcReadNextWord(); + while (vfs->ident != 0) { + if (vfs->ident == id) { + addVgaEvent(_vgaBaseDelay, vfs->code_ptr, vfs->sprite_id, vfs->cur_vga_file); + vfs_tmp = vfs; + do { + memcpy(vfs_tmp, vfs_tmp + 1, sizeof(VgaSleepStruct)); + vfs_tmp++; + } while (vfs_tmp->ident != 0); + } else { + vfs++; + } + } + + _lastVgaWaitFor = id; + /* clear a wait event */ + if (id == _vgaWaitFor) + _vgaWaitFor = 0; +} + +void SimonEngine::vc16_waitSync() { + VgaSleepStruct *vfs = _vgaSleepStructs; + while (vfs->ident) + vfs++; + + vfs->ident = vcReadNextWord(); + vfs->code_ptr = _vcPtr; + vfs->sprite_id = _vgaCurSpriteId; + vfs->cur_vga_file = _vgaCurZoneNum; + + _vcPtr = (byte *)&_vc_get_out_of_code; +} + +void SimonEngine::vc17_setPathfinderItem() { + if (getGameType() == GType_WW) { + // FIXME + vcReadNextWord(); + } else { + uint16 a = vcReadNextWord(); + _pathFindArray[a - 1] = (const uint16 *)_vcPtr; + + int end = (getGameType() == GType_FF || getGameType() == GType_PP) ? 9999 : 999; + while (readUint16Wrapper(_vcPtr) != end) + _vcPtr += 4; + _vcPtr += 2; + } +} + +void SimonEngine::vc18_jump() { + int16 offs = vcReadNextWord(); + _vcPtr += offs; +} + +/* chain to script? */ +void SimonEngine::vc19_chain_to_script() { + /* unused */ + error("vc19_chain_to_script: not implemented"); +} + +/* helper routines */ + +void SimonEngine::vc20_setRepeat() { + /* FIXME: This opcode is somewhat strange: it first reads a BE word from + * the script (advancing the script pointer in doing so); then it writes + * back the same word, this time as LE, into the script. + */ + uint16 a = vcReadNextWord(); + WRITE_LE_UINT16(const_cast<byte *>(_vcPtr), a); + _vcPtr += 2; +} + +void SimonEngine::vc21_endRepeat() { + int16 a = vcReadNextWord(); + const byte *tmp = _vcPtr + a; + if (getGameType() == GType_SIMON1 || getGameType() == GType_WW) + tmp += 4; + else + tmp += 3; + + uint16 val = READ_LE_UINT16(tmp); + if (val != 0) { + // Decrement counter + WRITE_LE_UINT16(const_cast<byte *>(tmp), val - 1); + _vcPtr = tmp + 2; + } +} + +void SimonEngine::vc22_setSpritePalette() { + byte *offs, *palptr, *src; + uint16 a, b, num, palSize; + + if (getGameType() != GType_WW) + a = vcReadNextWord(); + b = vcReadNextWord(); + + if (getGameType() == GType_FF || getGameType() == GType_PP) { + num = 256; + palSize = 768; + + palptr = _displayPalette; + offs = _curVgaFile1 + 6; + } else if (getGameType() == GType_SIMON1 || getGameType() == GType_SIMON2) { + num = a == 0 ? 32 : 16; + palSize = 96; + + palptr = &_displayPalette[(a * 64)]; + offs = _curVgaFile1 + 6; + } else { + num = 16; + palSize = 32; + palptr = _displayPalette; + offs = _curVgaFile1 + READ_BE_UINT16(_curVgaFile1 + 6); + } + + src = offs + b * palSize; + + do { + if (getGameType() == GType_WW) { + uint16 color = READ_BE_UINT16(src); + palptr[2] = ((color & 0x00f) >> 0) * 32; + palptr[1] = ((color & 0x0f0) >> 4) * 32; + palptr[0] = ((color & 0xf00) >> 8) * 32; + } else { + palptr[0] = src[0] * 4; + palptr[1] = src[1] * 4; + palptr[2] = src[2] * 4; + } + palptr[3] = 0; + + palptr += 4; + src += (getGameType() == GType_WW) ? 2 : 3; + } while (--num); + + _paletteFlag = 2; + _vgaSpriteChanged++; +} + +void SimonEngine::vc23_setSpritePriority() { + VgaSprite *vsp = findCurSprite(), *vus2; + uint16 pri = vcReadNextWord(); + VgaSprite bak; + + if (vsp->id == 0) + return; + + memcpy(&bak, vsp, sizeof(bak)); + bak.priority = pri; + bak.windowNum |= 0x8000; + + vus2 = vsp; + + if (vsp != _vgaSprites && pri < vsp[-1].priority) { + do { + vsp--; + } while (vsp != _vgaSprites && pri < vsp[-1].priority); + do { + memcpy(vus2, vus2 - 1, sizeof(VgaSprite)); + } while (--vus2 != vsp); + memcpy(vus2, &bak, sizeof(VgaSprite)); + } else if (vsp[1].id != 0 && pri >= vsp[1].priority) { + do { + vsp++; + } while (vsp[1].id != 0 && pri >= vsp[1].priority); + do { + memcpy(vus2, vus2 + 1, sizeof(VgaSprite)); + } while (++vus2 != vsp); + memcpy(vus2, &bak, sizeof(VgaSprite)); + } else { + vsp->priority = pri; + } + _vgaSpriteChanged++; +} + +void SimonEngine::vc24_setSpriteXY() { + VgaSprite *vsp = findCurSprite(); + vsp->image = vcReadVarOrWord(); + + vsp->x += (int16)vcReadNextWord(); + vsp->y += (int16)vcReadNextWord(); + if (getGameType() == GType_SIMON1 || getGameType() == GType_WW) { + vsp->flags = vcReadNextWord(); + } else { + vsp->flags = vcReadNextByte(); + } + + _vgaSpriteChanged++; +} + +void SimonEngine::vc25_halt_sprite() { + VgaSprite *vsp = findCurSprite(); + while (vsp->id != 0) { + memcpy(vsp, vsp + 1, sizeof(VgaSprite)); + vsp++; + } + _vcPtr = (byte *)&_vc_get_out_of_code; + _vgaSpriteChanged++; +} + +void SimonEngine::vc26_setSubWindow() { + uint16 *as = &_video_windows[vcReadNextWord() * 4]; // number + as[0] = vcReadNextWord(); // x + as[1] = vcReadNextWord(); // y + as[2] = vcReadNextWord(); // width + as[3] = vcReadNextWord(); // height +} + +void SimonEngine::vc27_resetSprite() { + VgaSprite bak, *vsp; + VgaSleepStruct *vfs; + VgaTimerEntry *vte, *vte2; + + _lockWord |= 8; + + _lastVgaWaitFor = 0; + + memset(&bak, 0, sizeof(bak)); + + vsp = _vgaSprites; + while (vsp->id) { + if ((getGameType() == GType_SIMON1) && vsp->id == 128) { + memcpy(&bak, vsp, sizeof(VgaSprite)); + } + vsp->id = 0; + vsp++; + } + + if (bak.id != 0) + memcpy(_vgaSprites, &bak, sizeof(VgaSprite)); + + vfs = _vgaSleepStructs; + while (vfs->ident) { + vfs->ident = 0; + vfs++; + } + + vte = _vgaTimerList; + while (vte->delay) { + if ((getGameType() == GType_SIMON1) && vte->sprite_id == 128) { + vte++; + } else { + vte2 = vte; + while (vte2->delay) { + memcpy(vte2, vte2 + 1, sizeof(VgaTimerEntry)); + vte2++; + } + } + } + + vcWriteVar(254, 0); + + if (getGameType() == GType_FF) + setBitFlag(42, true); + + _lockWord &= ~8; +} + +void SimonEngine::vc28_dummy_op() { + /* unused */ + _vcPtr += 8; +} + +void SimonEngine::vc29_stopAllSounds() { + if (getGameType() != GType_PP) + _sound->stopVoice(); + + _sound->stopAllSfx(); +} + +void SimonEngine::vc30_setFrameRate() { + _frameRate = vcReadNextWord(); +} + +void SimonEngine::vc31_setWindow() { + _windowNum = vcReadNextWord(); +} + +void SimonEngine::vc32_copyVar() { + if (getGameType() == GType_WW) { + // FIXME + } else { + uint16 a = vcReadVar(vcReadNextWord()); + vcWriteVar(vcReadNextWord(), a); + } +} + +void SimonEngine::vc33_setMouseOn() { + if (_mouseHideCount != 0) { + _mouseHideCount = 1; + mouseOn(); + } +} + +void SimonEngine::vc34_setMouseOff() { + mouseOff(); + _mouseHideCount = 200; + _leftButtonDown = 0; +} + +void SimonEngine::vc35_clearWindow() { + /* unused */ + _vcPtr += 4; + _vgaSpriteChanged++; +} + +void SimonEngine::vc36_setWindowImage() { + _updateScreen = false; + uint16 vga_res = vcReadNextWord(); + uint16 windowNum = vcReadNextWord(); + + if (getGameType() == GType_FF || getGameType() == GType_PP) { + _copyPartialMode = 2; + } else if (getGameType() == GType_SIMON2) { + set_video_mode_internal(windowNum, vga_res); + } else if (getGameType() == GType_SIMON1) { + if (windowNum == 16) { + _copyPartialMode = 2; + } else { + set_video_mode_internal(windowNum, vga_res); + } + } +} + +void SimonEngine::vc37_addToSpriteY() { + if (getGameType() == GType_WW) { + // FIXME + vcReadNextWord(); + vcReadNextWord(); + } else { + VgaSprite *vsp = findCurSprite(); + vsp->y += vcReadVar(vcReadNextWord()); + _vgaSpriteChanged++; + } +} + +void SimonEngine::vc38_skipIfVarZero() { + uint16 var = vcReadNextWord(); + if (vcReadVar(var) == 0) + vcSkipNextInstruction(); +} + +void SimonEngine::vc39_setVar() { + uint16 var = vcReadNextWord(); + int16 value = vcReadNextWord(); + vcWriteVar(var, value); +} + +void SimonEngine::vc40() { + uint16 var = vcReadNextWord(); + int16 value = vcReadVar(var) + vcReadNextWord(); + + if ((getGameType() == GType_SIMON2) && var == 15 && !getBitFlag(80)) { + int16 tmp; + + if (_scrollCount != 0) { + if (_scrollCount >= 0) + goto no_scroll; + _scrollCount = 0; + } else { + if (_scrollFlag != 0) + goto no_scroll; + } + + if (value - _scrollX >= 30) { + _scrollCount = 20; + tmp = _scrollXMax - _scrollX; + if (tmp < 20) + _scrollCount = tmp; + addVgaEvent(6, NULL, 0, 0); /* scroll event */ + } + } +no_scroll:; + + vcWriteVar(var, value); +} + +void SimonEngine::vc41() { + uint16 var = vcReadNextWord(); + int16 value = vcReadVar(var) - vcReadNextWord(); + + if ((getGameType() == GType_SIMON2) && var == 15 && !getBitFlag(80)) { + if (_scrollCount != 0) { + if (_scrollCount < 0) + goto no_scroll; + _scrollCount = 0; + } else { + if (_scrollFlag != 0) + goto no_scroll; + } + + if ((uint16)(value - _scrollX) < 11) { + _scrollCount = -20; + if (_scrollX < 20) + _scrollCount = -_scrollX; + addVgaEvent(6, NULL, 0, 0); /* scroll event */ + } + } +no_scroll:; + + vcWriteVar(var, value); +} + +void SimonEngine::vc42_delayIfNotEQ() { + uint16 val = vcReadVar(vcReadNextWord()); + if (val != vcReadNextWord()) { + + addVgaEvent(_frameRate + 1, _vcPtr - 4, _vgaCurSpriteId, _vgaCurZoneNum); + _vcPtr = (byte *)&_vc_get_out_of_code; + } +} + +void SimonEngine::vc43_skipIfBitClear() { + if (!getBitFlag(vcReadNextWord())) { + vcSkipNextInstruction(); + } +} + +void SimonEngine::vc44_skipIfBitSet() { + if (getBitFlag(vcReadNextWord())) { + vcSkipNextInstruction(); + } +} + +void SimonEngine::vc45_setSpriteX() { + if (getGameType() == GType_WW) { + //FIXME + vcReadNextWord(); + vcReadNextWord(); + } else { + VgaSprite *vsp = findCurSprite(); + vsp->x = vcReadVar(vcReadNextWord()); + _vgaSpriteChanged++; + } +} + +void SimonEngine::vc46_setSpriteY() { + VgaSprite *vsp = findCurSprite(); + vsp->y = vcReadVar(vcReadNextWord()); + _vgaSpriteChanged++; +} + +void SimonEngine::vc47_addToVar() { + if (getGameType() == GType_WW) { + //FIXME + vcReadNextWord(); + } else { + uint16 var = vcReadNextWord(); + vcWriteVar(var, vcReadVar(var) + vcReadVar(vcReadNextWord())); + } +} + +void SimonEngine::vc48_setPathFinder() { + uint16 a = (uint16)_variableArrayPtr[12]; + const uint16 *p = _pathFindArray[a - 1]; + + if (getGameType() == GType_WW) { + //FIXME + vcReadNextWord(); + } else if (getGameType() == GType_FF || getGameType() == GType_PP) { + VgaSprite *vsp = findCurSprite(); + int16 x, y, ydiff; + int16 x1, y1, x2, y2; + uint pos = 0; + + x = vsp->x; + while (x >= (int16)readUint16Wrapper(p + 2)) { + p += 2; + pos++; + } + + x1 = readUint16Wrapper(p); + y1 = readUint16Wrapper(p + 1); + x2 = readUint16Wrapper(p + 2); + y2 = readUint16Wrapper(p + 3); + + if (x2 != 9999) { + ydiff = y2 - y1; + if (ydiff < 0) { + ydiff = -ydiff; + x = vsp->x & 7; + ydiff *= x; + ydiff /= 8; + ydiff = -ydiff; + } else { + x = vsp->x & 7; + ydiff *= x; + ydiff /= 8; + } + y1 += ydiff; + } + + y = vsp->y; + vsp->y = y1; + checkScrollY(y1 - y, y1); + + _variableArrayPtr[11] = x1; + _variableArrayPtr[13] = pos; + } else { + uint b = (uint16)_variableArray[13]; + p += b * 2 + 1; + int c = _variableArray[14]; + + int step; + int y1, y2; + int16 *vp; + + step = 2; + if (c < 0) { + c = -c; + step = -2; + } + + vp = &_variableArray[20]; + + do { + y2 = readUint16Wrapper(p); + p += step; + y1 = readUint16Wrapper(p) - y2; + + vp[0] = y1 / 2; + vp[1] = y1 - (y1 / 2); + + vp += 2; + } while (--c); + } +} + +void SimonEngine::setBitFlag(uint bit, bool value) { + uint16 *bits = &_bitArray[bit / 16]; + *bits = (*bits & ~(1 << (bit & 15))) | (value << (bit & 15)); +} + +bool SimonEngine::getBitFlag(uint bit) { + uint16 *bits = &_bitArray[bit / 16]; + return (*bits & (1 << (bit & 15))) != 0; +} + +void SimonEngine::vc49_setBit() { + uint16 bit = vcReadNextWord(); + if (getGameType() == GType_FF && bit == 82) { + _variableArrayPtr = _variableArray2; + } + setBitFlag(bit, true); +} + +void SimonEngine::vc50_clearBit() { + uint16 bit = vcReadNextWord(); + if (getGameType() == GType_FF && bit == 82) { + _variableArrayPtr = _variableArray; + } + setBitFlag(bit, false); +} + +void SimonEngine::vc51_enableBox() { + enableBox(vcReadNextWord()); +} + +void SimonEngine::vc52_playSound() { + bool ambient = false; + + uint16 sound = vcReadNextWord(); + if (sound >= 0x8000) { + ambient = true; + sound = -sound; + } + + if (getGameType() == GType_FF || getGameType() == GType_PP) { + int16 pan = vcReadNextWord(); + int16 vol = vcReadNextWord(); + + if (ambient) + loadSound(sound, pan, vol, 2); + else + loadSound(sound, pan, vol, 1); + } else if (getGameType() == GType_SIMON2) { + if (ambient) + _sound->playAmbient(sound); + else + _sound->playEffects(sound); + } else if (getFeatures() & GF_TALKIE) { + _sound->playEffects(sound); + } else if (getGameId() == GID_SIMON1DOS) { + playSting(sound); + } +} + +void SimonEngine::vc53_panSFX() { + VgaSprite *vsp = findCurSprite(); + int pan; + + uint16 sound = vcReadNextWord(); + int16 xoffs = vcReadNextWord(); + int16 vol = vcReadNextWord(); + + pan = (vsp->x - _scrollX + xoffs) * 8 - 2560; + if (pan < -10000) + pan = -10000; + if (pan > 10000) + pan = 10000; + + loadSound(sound, 0, vol, 1); + + if (xoffs != 2) + xoffs |= 0x10; + + addVgaEvent(10, NULL, _vgaCurSpriteId, _vgaCurZoneNum, xoffs); /* pan event */ + debug(0, "vc53_panSFX: snd %d xoffs %d vol %d", sound, xoffs, vol); +} + +void SimonEngine::vc54_no_op() { + /* unused */ + _vcPtr += 6; +} + +void SimonEngine::vc55_moveBox() { + HitArea *ha = _hitAreas; + uint count = ARRAYSIZE(_hitAreas); + uint16 id = vcReadNextWord(); + int16 x = vcReadNextWord(); + int16 y = vcReadNextWord(); + + for (;;) { + if (ha->id == id) { + ha->x += x; + ha->y += y; + break; + } + ha++; + if (!--count) + break; + } + + _needHitAreaRecalc++; +} + +void SimonEngine::vc56_delay() { + if (getGameType() == GType_WW) { + byte *src = _curVgaFile2 + 32; + byte *dst = getBackBuf(); + + uint8 palette[1024]; + for (int i = 0; i < 256; i++) { + palette[i * 4 + 0] = *src++ * 4; + palette[i * 4 + 1] = *src++ * 4; + palette[i * 4 + 2] = *src++ * 4; + palette[i * 4 + 3] = 0; + } + + _system->setPalette(palette, 0, 256); + memcpy(dst, src, _screenHeight * _screenWidth); + } else { + uint16 num = vcReadVarOrWord() * _frameRate; + + addVgaEvent(num + _vgaBaseDelay, _vcPtr, _vgaCurSpriteId, _vgaCurZoneNum); + _vcPtr = (byte *)&_vc_get_out_of_code; + } +} + +void SimonEngine::vc57_blackPalette() { + if (getGameType() == GType_WW) { + uint8 palette[1024]; + memset(palette, 0, sizeof(palette)); + _system->setPalette(palette, 0, 256); + } +} + +void SimonEngine::vc58() { + if (getGameType() == GType_WW) + return; + + uint16 sprite = _vgaCurSpriteId; + uint16 file = _vgaCurZoneNum; + const byte *vcPtrOrg; + uint16 tmp; + + _vgaCurZoneNum = vcReadNextWord(); + _vgaCurSpriteId = vcReadNextWord(); + + tmp = to16Wrapper(vcReadNextWord()); + + vcPtrOrg = _vcPtr; + _vcPtr = (byte *)&tmp; + vc23_setSpritePriority(); + + _vcPtr = vcPtrOrg; + _vgaCurSpriteId = sprite; + _vgaCurZoneNum = file; +} + +void SimonEngine::vc59() { + if (getGameType() == GType_SIMON2 || getGameType() == GType_FF || getGameType() == GType_PP) { + uint16 file = vcReadNextWord(); + uint16 start = vcReadNextWord(); + uint16 end = vcReadNextWord() + 1; + + do { + vc_kill_sprite(file, start); + } while (++start != end); + } else if (getGameType() == GType_SIMON1) { + if (!_sound->isVoiceActive()) + vcSkipNextInstruction(); + } else { + // Skip if not EGA + vcSkipNextInstruction(); + } +} + +void SimonEngine::vc_kill_sprite(uint file, uint sprite) { + uint16 old_sprite_id, old_cur_file_id; + VgaSleepStruct *vfs; + VgaSprite *vsp; + VgaTimerEntry *vte; + const byte *vcPtrOrg; + + old_sprite_id = _vgaCurSpriteId; + old_cur_file_id = _vgaCurZoneNum; + vcPtrOrg = _vcPtr; + + _vgaCurZoneNum = file; + _vgaCurSpriteId = sprite; + + vfs = _vgaSleepStructs; + while (vfs->ident != 0) { + if (vfs->sprite_id == _vgaCurSpriteId && ((getGameType() == GType_SIMON1) || vfs->cur_vga_file == _vgaCurZoneNum)) { + while (vfs->ident != 0) { + memcpy(vfs, vfs + 1, sizeof(VgaSleepStruct)); + vfs++; + } + break; + } + vfs++; + } + + vsp = findCurSprite(); + if (vsp->id) { + vc25_halt_sprite(); + + vte = _vgaTimerList; + while (vte->delay != 0) { + if (vte->sprite_id == _vgaCurSpriteId && ((getGameType() == GType_SIMON1) || vte->cur_vga_file == _vgaCurZoneNum)) { + deleteVgaEvent(vte); + break; + } + vte++; + } + } + + _vgaCurZoneNum = old_cur_file_id; + _vgaCurSpriteId = old_sprite_id; + _vcPtr = vcPtrOrg; +} + +void SimonEngine::vc60_killSprite() { + uint16 zoneNum; + + if (getGameType() == GType_SIMON1) { + zoneNum = _vgaCurZoneNum; + } else { + zoneNum = vcReadNextWord(); + } + uint16 sprite = vcReadNextWord(); + vc_kill_sprite(zoneNum, sprite); +} + +void SimonEngine::vc61_setMaskImage() { + if (getGameType() == GType_WW) { + // FIXME + vcReadVarOrWord(); + } else { + VgaSprite *vsp = findCurSprite(); + + vsp->image = vcReadVarOrWord(); + vsp->x += vcReadNextWord(); + vsp->y += vcReadNextWord(); + vsp->flags = kDFMasked | kDFUseFrontBuf; + + _vgaSpriteChanged++; + } +} + +void SimonEngine::vc62_fastFadeOut() { + vc29_stopAllSounds(); + + if (!_fastFadeOutFlag) { + uint i, fadeSize, fadeCount; + + if (getGameType() != GType_WW) + _fastFadeOutFlag = true; + + _fastFadeCount = 256; + if (getGameType() == GType_SIMON1 || getGameType() == GType_SIMON2) { + if (_windowNum == 4) + _fastFadeCount = 208; + } + + memcpy(_videoBuf1, _currentPalette, _fastFadeCount * 4); + + if ((getGameType() == GType_FF || getGameType() == GType_PP) && !getBitFlag(75)) { + fadeCount = 32; + fadeSize = 8; + } else { + fadeCount = 4; + fadeSize = 64; + } + + for (i = fadeCount; i != 0; --i) { + paletteFadeOut(_videoBuf1, _fastFadeCount, fadeSize); + _system->setPalette(_videoBuf1, 0, _fastFadeCount); + delay(5); + } + + if (getGameType() == GType_SIMON1) { + uint16 params[5]; /* parameters to vc10_draw */ + VgaSprite *vsp; + VgaPointersEntry *vpe; + const byte *vcPtrOrg = _vcPtr; + + vsp = _vgaSprites; + while (vsp->id != 0) { + if (vsp->id == 128) { + byte *old_file_1 = _curVgaFile1; + byte *old_file_2 = _curVgaFile2; + uint palmode = _windowNum; + + vpe = &_vgaBufferPointers[vsp->zoneNum]; + _curVgaFile1 = vpe->vgaFile1; + _curVgaFile2 = vpe->vgaFile2; + _windowNum = vsp->windowNum; + + params[0] = READ_BE_UINT16(&vsp->image); + params[1] = READ_BE_UINT16(&vsp->palette); + params[2] = READ_BE_UINT16(&vsp->x); + params[3] = READ_BE_UINT16(&vsp->y); + params[4] = READ_BE_UINT16(&vsp->flags); + _vcPtr = (byte *)params; + vc10_draw(); + + _windowNum = palmode; + _curVgaFile1 = old_file_1; + _curVgaFile2 = old_file_2; + break; + } + vsp++; + } + _vcPtr = vcPtrOrg; + } + + // Allow one section of Simon the Sorcerer 1 introduction to be displayed + // in lower half of screen + if ((getGameType() == GType_SIMON1) && (_subroutine == 2923 || _subroutine == 2926)) { + dx_clear_surfaces(200); + } else if (getGameType() == GType_FF || getGameType() == GType_PP) { + dx_clear_surfaces(480); + } else { + dx_clear_surfaces(_windowNum == 4 ? 134 : 200); + } + } + if (getGameType() == GType_SIMON2) { + if (_nextMusicToPlay != -1) + loadMusic(_nextMusicToPlay); + } +} + +void SimonEngine::vc63_fastFadeIn() { + if (getGameType() == GType_FF || getGameType() == GType_PP) { + _fastFadeInFlag = 256; + } else { + _fastFadeInFlag = 208; + if (_windowNum != 4) { + _fastFadeInFlag = 256; + } + } + _fastFadeOutFlag = false; +} + +void SimonEngine::vc64_skipIfSpeechEnded() { + if ((getGameType() == GType_SIMON2 && _subtitles && _language != Common::HB_ISR) || + !_sound->isVoiceActive()) { + vcSkipNextInstruction(); + } +} + +void SimonEngine::vc65_slowFadeIn() { + _fastFadeInFlag = 624; + _fastFadeCount = 208; + if (_windowNum != 4) { + _fastFadeInFlag = 768; + _fastFadeCount = 256; + } + _fastFadeInFlag |= 0x8000; + _fastFadeOutFlag = false; +} + +void SimonEngine::vc66_skipIfNotEqual() { + uint16 a = vcReadNextWord(); + uint16 b = vcReadNextWord(); + + if (vcReadVar(a) != vcReadVar(b)) + vcSkipNextInstruction(); +} + +void SimonEngine::vc67_skipIfGE() { + uint16 a = vcReadNextWord(); + uint16 b = vcReadNextWord(); + + if (vcReadVar(a) >= vcReadVar(b)) + vcSkipNextInstruction(); +} + +void SimonEngine::vc68_skipIfLE() { + uint16 a = vcReadNextWord(); + uint16 b = vcReadNextWord(); + + if (vcReadVar(a) <= vcReadVar(b)) + vcSkipNextInstruction(); +} + +void SimonEngine::vc69_playTrack() { + int16 track = vcReadNextWord(); + int16 loop = vcReadNextWord(); + + // Jamieson630: + // This is a "play track". The original + // design stored the track to play if one was + // already in progress, so that the next time a + // "fill MIDI stream" event occured, the MIDI + // player would find the change and switch + // tracks. We use a different architecture that + // allows for an immediate response here, but + // we'll simulate the variable changes so other + // scripts don't get thrown off. + // NOTE: This opcode looks very similar in function + // to vc72(), except that vc72() may allow for + // specifying a non-valid track number (999 or -1) + // as a means of stopping what music is currently + // playing. + midi.setLoop(loop != 0); + midi.startTrack(track); +} + +void SimonEngine::vc70_queueMusic() { + // Simon2 + uint16 track = vcReadNextWord(); + uint16 loop = vcReadNextWord(); + + // Jamieson630: + // This sets the "on end of track" action. + // It specifies whether to loop the current + // track and, if not, whether to switch to + // a different track upon completion. + if (track != 0xFFFF && track != 999) + midi.queueTrack(track, loop != 0); + else + midi.setLoop(loop != 0); +} + +void SimonEngine::vc71_checkMusicQueue() { + // Jamieson630: + // This command skips the next instruction + // unless (1) there is a track playing, AND + // (2) there is a track queued to play after it. + if (!midi.isPlaying (true)) + vcSkipNextInstruction(); +} + +void SimonEngine::vc72_play_track_2() { + // Jamieson630: + // This is a "play or stop track". Note that + // this opcode looks very similar in function + // to vc69(), except that this opcode may allow + // for specifying a track of 999 or -1 in order to + // stop the music. We'll code it that way for now. + + // NOTE: It's possible that when "stopping" a track, + // we're supposed to just go on to the next queued + // track, if any. Must find out if there is ANY + // case where this is used to stop a track in the + // first place. + + int16 track = vcReadNextWord(); + int16 loop = vcReadNextWord(); + + if (track == -1 || track == 999) { + midi.stop(); + } else { + midi.setLoop (loop != 0); + midi.startTrack (track); + } +} + +void SimonEngine::vc73_setMark() { + _marks |= (1 << vcReadNextWord()); +} + +void SimonEngine::vc74_clearMark() { + _marks &= ~(1 << vcReadNextWord()); +} + +int SimonEngine::getScale(int16 y, int16 x) { + int16 z; + + if (y > _baseY) { + return((int16)(x * (1 + ((y - _baseY) * _scale)))); + } else { + if (x == 0) + return(0); + if (x < 0) { + z = ((int16)((x * (1 - ((_baseY - y)* _scale))) - 0.5)); + if (z >- 2) + return(-2); + return(z); + } + + z = ((int16)((x * (1 - ((_baseY - y) * _scale))) + 0.5)); + if (z < 2) + return(2); + + return(z); + } +} + +void SimonEngine::vc75_setScale() { + _baseY = vcReadNextWord(); + _scale = (float)vcReadNextWord() / 1000000.; +} + +void SimonEngine::vc76_setScaleXOffs() { + VgaSprite *vsp = findCurSprite(); + + vsp->image = vcReadNextWord(); + int16 x = vcReadNextWord(); + uint16 var = vcReadNextWord(); + + vsp->x += getScale(vsp->y, x); + _variableArrayPtr[var] = vsp->x; + + checkScrollX(x, vsp->x); + + vsp->flags = kDFScaled; +} + +void SimonEngine::vc77_setScaleYOffs() { + VgaSprite *vsp = findCurSprite(); + + vsp->image = vcReadNextWord(); + int16 y = vcReadNextWord(); + uint16 var = vcReadNextWord(); + + vsp->y += getScale(vsp->y, y); + _variableArrayPtr[var] = vsp->y; + + if (y != 0) + checkScrollY(y, vsp->y); + + vsp->flags = kDFScaled; +} + +void SimonEngine::vc78_computeXY() { + VgaSprite *vsp = findCurSprite(); + + uint16 a = (uint16)_variableArrayPtr[12]; + uint16 b = (uint16)_variableArrayPtr[13]; + + const uint16 *p = _pathFindArray[a - 1]; + p += b * 2; + + uint16 posx = readUint16Wrapper(p); + _variableArrayPtr[15] = posx; + vsp->x = posx; + + uint16 posy = readUint16Wrapper(p + 1); + _variableArrayPtr[16] = posy; + vsp->y = posy; + + setBitFlag(85, false); + if (getBitFlag(74)) { + centreScroll(); + } +} + +void SimonEngine::vc79_computePosNum() { + uint a = (uint16)_variableArrayPtr[12]; + const uint16 *p = _pathFindArray[a - 1]; + uint pos = 0; + + int16 y = _variableArrayPtr[16]; + while (y >= (int16)readUint16Wrapper(p + 1)) { + p += 2; + pos++; + } + + _variableArrayPtr[13] = pos; +} + +void SimonEngine::vc80_setOverlayImage() { + VgaSprite *vsp = findCurSprite(); + + vsp->image = vcReadVarOrWord(); + + vsp->x += vcReadNextWord(); + vsp->y += vcReadNextWord(); + vsp->flags = kDFOverlayed; + + _vgaSpriteChanged++; +} + +void SimonEngine::vc81_setRandom() { + uint16 var = vcReadNextWord(); + uint16 value = vcReadNextWord(); + + _variableArray[var] = _rnd.getRandomNumber(value - 1); +} + +void SimonEngine::vc82_getPathValue() { + uint8 val; + + uint16 var = vcReadNextWord(); + + if (getBitFlag(82)) { + val = _pathValues1[_GPVCount1++]; + } else { + val = _pathValues[_GPVCount++]; + } + + vcWriteVar(var, val); +} + +void SimonEngine::vc83_playSoundLoop() { + uint16 sound = vcReadNextWord(); + int16 vol = vcReadNextWord(); + int16 pan = vcReadNextWord(); + + loadSound(sound, pan, vol, 3); +} + +void SimonEngine::vc84_stopSoundLoop() { + _sound->stopSfx5(); +} + +// Scrolling functions for Feeble Files +void SimonEngine::checkScrollX(int16 x, int16 xpos) { + if (_scrollXMax == 0 || getBitFlag(80) || getBitFlag(82) || x == 0) + return; + + int16 tmp; + if (x > 0) { + if (_scrollCount != 0) { + if (_scrollCount >= 0) + return; + _scrollCount = 0; + } else { + if (_scrollFlag != 0) + return; + } + + if (xpos - _scrollX >= 480) { + _scrollCount = 320; + tmp = _scrollXMax - _scrollX; + if (tmp < 320) + _scrollCount = tmp; + } + } else { + if (_scrollCount != 0) { + if (_scrollCount < 0) + return; + _scrollCount = 0; + } else { + if (_scrollFlag != 0) + return; + } + + if (xpos - _scrollX < 161) { + _scrollCount = -320; + if (_scrollX < 320) + _scrollCount = -_scrollX; + } + } +} + +void SimonEngine::checkScrollY(int16 y, int16 ypos) { + if (_scrollYMax == 0 || getBitFlag(80)) + return; + + int16 tmp; + if (y >= 0) { + if (_scrollCount != 0) { + if (_scrollCount >= 0) + return; + } else { + if (_scrollFlag != 0) + return; + } + + if (ypos - _scrollY >= 440) { + _scrollCount = 240; + tmp = _scrollYMax - _scrollY; + if (tmp < 240) + _scrollCount = tmp; + } + } else { + if (_scrollCount != 0) { + if (_scrollCount < 0) + return; + } else { + if (_scrollFlag != 0) + return; + } + + if (ypos - _scrollY < 100) { + _scrollCount = -240; + if (_scrollY < 240) + _scrollCount = -_scrollY; + } + } +} + +void SimonEngine::centreScroll() { + int16 x, y, tmp; + + if (_scrollXMax != 0) { + _scrollCount = 0; + x = _variableArray[15] - _scrollX; + if (x < 17 || (getBitFlag(85) && x < 320)) { + x -= 320; + if (_scrollX < -x) + x = -_scrollX; + _scrollCount = x; + } else if ((getBitFlag(85) && x >= 320) || x >= 624) { + x -= 320; + tmp = _scrollXMax - _scrollX; + if (tmp < x) + x = tmp; + _scrollCount = x; + } + } else if (_scrollYMax != 0) { + _scrollCount = 0; + y = _variableArray[16] - _scrollY; + if (y < 30) { + y -= 240; + if (_scrollY < -y) + y = -_scrollY; + _scrollCount = y; + } else if (y >= 460) { + y -= 240; + tmp = _scrollYMax - _scrollY; + if (tmp < y) + y = tmp; + _scrollCount = y; + } + } +} + +} // End of namespace Simon diff --git a/engines/agos/vga.h b/engines/agos/vga.h new file mode 100644 index 0000000000..e963d0350b --- /dev/null +++ b/engines/agos/vga.h @@ -0,0 +1,167 @@ +/* ScummVM - Scumm Interpreter + * Copyright (C) 2001 Ludvig Strigeus + * Copyright (C) 2002-2006 The ScummVM project + * + * 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. + * + * $URL$ + * $Id$ + * + */ + +#ifndef AGOS_VGA_H +#define AGOS_VGA_H + +namespace Simon { + +#include "common/pack-start.h" // START STRUCT PACKING + +// Feeble Files +struct VgaFileHeader_Feeble { + uint16 x_1; + uint16 hdr2_start; + uint16 x_2, x_3; +}; + +struct VgaFileHeader2_Feeble { + uint16 imageCount; + uint16 x_2; + uint16 animationCount; + uint16 x_3; + uint16 imageTable; + uint16 x_4; + uint16 animationTable; + uint16 x_5; +}; + +struct ImageHeader_Feeble { + uint16 id; + uint16 x_1; + uint16 scriptOffs; + uint16 x_2; +}; + +struct AnimationHeader_Feeble { + uint16 scriptOffs; + uint16 x_2; + uint16 id; +}; + +// Simon 1/2 +struct VgaFileHeader_Simon { + uint16 x_1, x_2; + uint16 hdr2_start; + uint16 x_3, x_4; +}; + +struct VgaFileHeader2_Simon { + uint16 x_1; + uint16 imageCount; + uint16 x_2; + uint16 animationCount; + uint16 x_3; + uint16 imageTable; + uint16 x_4; + uint16 animationTable; + uint16 x_5; +}; + +struct ImageHeader_Simon { + uint16 id; + uint16 x_1; + uint16 x_2; + uint16 scriptOffs; +}; + +struct AnimationHeader_Simon { + uint16 id; + uint16 x_2; + uint16 scriptOffs; +}; + + +// Waxworks +struct VgaFileHeader2_WW { + uint16 x_1; + uint16 imageCount; + uint16 x_2; + uint16 animationCount; + uint16 x_3; + uint16 imageTable; + uint16 x_4; + uint16 animationTable; + uint16 x_5; +}; + +struct ImageHeader_WW { + uint16 id; + uint16 x_1; + uint16 x_2; + uint16 scriptOffs; +}; + +struct AnimationHeader_WW { + uint16 id; + uint16 x_1; + uint16 x_2; + uint16 scriptOffs; +}; + + +#include "common/pack-end.h" // END STRUCT PACKING + +enum DrawFlags { + kDFFlip = 0x1, + kDFNonTrans = 0x2, + kDFUseFrontBuf = 0x4, + kDFCompressed = 0x8, + kDFCompressedFlip = 0x10, + kDFMasked = 0x20, + + // Feeble specific + kDFOverlayed = 0x10, + kDFScaled = 0x40, + kDFShaded = 0x80 +}; + +struct VC10_state { + int image; + uint16 flags; + byte palette; + + int x, y; + int width, height; + uint draw_width, draw_height; + uint x_skip, y_skip; + + byte *surf2_addr; + uint surf2_pitch; + + byte *surf_addr; + uint surf_pitch; + + uint16 dl, dh; + + const byte *depack_src; + int8 depack_cont; + + byte depack_dest[480]; +}; + +byte *vc10_depackColumn(VC10_state *vs); + +} // End of namespace Simon + +#endif diff --git a/engines/agos/window.cpp b/engines/agos/window.cpp new file mode 100644 index 0000000000..c21f8ad6d2 --- /dev/null +++ b/engines/agos/window.cpp @@ -0,0 +1,185 @@ +/* ScummVM - Scumm Interpreter + * Copyright (C) 2001 Ludvig Strigeus + * Copyright (C) 2001-2006 The ScummVM project + * + * 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. + * + * $URL$ + * $Id$ + * + */ + +#include "common/stdafx.h" + +#include "agos/agos.h" +#include "agos/intern.h" + +namespace Simon { + +uint SimonEngine::getWindowNum(WindowBlock *window) { + uint i; + + for (i = 0; i != ARRAYSIZE(_windowArray); i++) + if (_windowArray[i] == window) + return i; + + error("getWindowNum: not found"); + return 0; +} + +WindowBlock *SimonEngine::openWindow(uint x, uint y, uint w, uint h, uint flags, uint fill_color, uint text_color) { + WindowBlock *window; + + window = _windowList; + while (window->mode != 0) + window++; + + window->mode = 2; + window->x = x; + window->y = y; + window->width = w; + window->height = h; + window->flags = flags; + window->fill_color = fill_color; + window->text_color = text_color; + window->textColumn = 0; + window->textRow = 0; + window->textColumnOffset = 0; + window->textMaxLength = window->width * 8 / 6; // characters are 6 pixels + window->scrollY = 0; + return window; +} + +void SimonEngine::changeWindow(uint a) { + a &= 7; + + if (_windowArray[a] == NULL || _curWindow == a) + return; + + _curWindow = a; + showmessage_print_char(0); + _textWindow = _windowArray[a]; + + if (getGameType() == GType_FF || getGameType() == GType_PP) + showmessage_helper_3(_textWindow->textColumn, _textWindow->width); + else + showmessage_helper_3(_textWindow->textLength, _textWindow->textMaxLength); +} + +void SimonEngine::closeWindow(uint a) { + if (_windowArray[a] == NULL) + return; + removeIconArray(a); + resetWindow(_windowArray[a]); + _windowArray[a] = NULL; + if (_curWindow == a) { + _textWindow = NULL; + changeWindow(0); + } +} + +void SimonEngine::clearWindow(WindowBlock *window) { + if (window->flags & 0x10) + restoreWindow(window); + else + colorWindow(window); + + window->textColumn = 0; + window->textRow = 0; + window->textColumnOffset = 0; + window->textLength = 0; + window->scrollY = 0; +} + +void SimonEngine::colorWindow(WindowBlock *window) { + byte *dst; + uint h, w; + + _lockWord |= 0x8000; + + if (getGameType() == GType_FF || getGameType() == GType_PP) { + dst = getFrontBuf() + _dxSurfacePitch * window->y + window->x; + + for (h = 0; h < window->height; h++) { + for (w = 0; w < window->width; w++) { + if (dst[w] == 113 || dst[w] == 116 || dst[w] == 252) + dst[w] = window->fill_color; + } + dst += _screenWidth; + } + } else { + dst = getFrontBuf() + _dxSurfacePitch * window->y + window->x * 8; + h = window->height * 8; + w = window->width * 8; + + do { + memset(dst, window->fill_color, w); + dst += _dxSurfacePitch; + } while (--h); + } + + _lockWord &= ~0x8000; +} + +void SimonEngine::resetWindow(WindowBlock *window) { + if (window->flags & 8) + restoreWindow(window); + window->mode = 0; +} + +void SimonEngine::restoreWindow(WindowBlock *window) { + _lockWord |= 0x8000; + + if (getGameType() == GType_FF || getGameType() == GType_PP) { + restoreBlock(window->y + window->height, window->x + window->width, window->y, window->x); + } else if (getGameType() == GType_SIMON2) { + if (_restoreWindow6 && _windowArray[2] == window) { + window = _windowArray[6]; + _restoreWindow6 = 0; + } + + restoreBlock(window->y + window->height * 8, (window->x + window->width) * 8, window->y, window->x * 8); + } else { + restoreBlock(window->y + window->height * 8 + ((window == _windowArray[2]) ? 1 : 0), (window->x + window->width) * 8, window->y, window->x * 8); + } + + _lockWord &= ~0x8000; +} + +void SimonEngine::restoreBlock(uint h, uint w, uint y, uint x) { + byte *dst, *src; + uint i; + + dst = getFrontBuf(); + src = _backGroundBuf; + + dst += y * _dxSurfacePitch; + src += y * _dxSurfacePitch; + + while (y < h) { + for (i = x; i < w; i++) + dst[i] = src[i]; + y++; + dst += _dxSurfacePitch; + src += _dxSurfacePitch; + } +} + +void SimonEngine::windowPutChar(uint a) { + if (_textWindow != _windowArray[0]) + windowPutChar(_textWindow, a); +} + +} // End of namespace Simon |