diff options
Diffstat (limited to 'engines/scumm')
68 files changed, 3751 insertions, 3144 deletions
diff --git a/engines/scumm/actor.cpp b/engines/scumm/actor.cpp index a06939dc51..ca1dc8869f 100644 --- a/engines/scumm/actor.cpp +++ b/engines/scumm/actor.cpp @@ -691,6 +691,7 @@ void Actor_v3::walkActor() { int Actor::remapDirection(int dir, bool is_walking) { int specdir; byte flags; + byte mask; bool flipX; bool flipY; @@ -769,6 +770,14 @@ int Actor::remapDirection(int dir, bool is_walking) { case 6: return 180; } + + // MM C64 stores flags as a part of the mask + if (_vm->_game.version == 0) { + mask = _vm->getMaskFromBox(_walkbox); + // face the wall if climbing/descending a ladder + if ((mask & 0x8C) == 0x84) + return 0; + } } // OR 1024 in to signal direction interpolation should be done return normalizeAngle(dir) | 1024; @@ -1043,9 +1052,17 @@ static int checkXYInBoxBounds(int boxnum, int x, int y, int &destX, int &destY) // yDist must be divided by 4, as we are using 8x2 pixels // blocks for actor coordinates). int xDist = ABS(x - destX); - int yDist = ABS(y - destY) / 4; + int yDist; int dist; + // MM C64: This fixes the trunk bug (#3070065), as well + // as the fruit bowl, however im not sure if its + // the proper solution or not. + if( g_scumm->_game.version == 0 ) + yDist = ABS(y - destY); + else + yDist = ABS(y - destY) / 4; + if (xDist < yDist) dist = (xDist >> 1) + yDist; else @@ -1073,6 +1090,7 @@ AdjustBoxResult Actor_v2::adjustXYToBeInBox(const int dstX, const int dstY) { abr.x = foundX; abr.y = foundY; abr.box = box; + break; } if (dist < bestDist) { @@ -2159,7 +2177,12 @@ void ScummEngine::stopTalk() { ((ScummEngine_v7 *)this)->clearSubtitleQueue(); #endif } else { - restoreCharsetBg(); +#ifndef DISABLE_TOWNS_DUAL_LAYER_MODE + if (_game.platform == Common::kPlatformFMTowns) + towns_restoreCharsetBg(); + else +#endif + restoreCharsetBg(); } } @@ -2254,7 +2277,7 @@ void Actor::setActorCostume(int c) { } } -static const char* v0ActorNames[0x19] = { +static const char* v0ActorNames_English[25] = { "Syd", "Razor", "Dave", @@ -2270,10 +2293,36 @@ static const char* v0ActorNames[0x19] = { "Purple Tentacle", "Green Tentacle", "Meteor", + "", + "", + "", "Plant", "", "", "", + "Sandy" +}; + +static const char* v0ActorNames_German[25] = { + "Syd", + "Razor", + "Dave", + "Michael", + "Bernard", + "Wendy", + "Jeff", + "", + "Dr.Fred", + "Schwester Edna", + "Weird Ed", + "Ted", + "Lila Tentakel", + "Gr<nes Tentakel", + "Meteor", + "", + "", + "", + "Pflanze", "", "", "", @@ -2284,8 +2333,15 @@ const byte *Actor::getActorName() { const byte *ptr = NULL; if (_vm->_game.version == 0) { - if (_number) - ptr = (const byte *)v0ActorNames[_number - 1]; + if (_number) { + switch (_vm->_language) { + case Common::DE_DEU: + ptr = (const byte *)v0ActorNames_German[_number - 1]; + break; + default: + ptr = (const byte *)v0ActorNames_English[_number - 1]; + } + } } else { ptr = _vm->getResourceAddress(rtActorName, _number); } @@ -2556,6 +2612,21 @@ void ScummEngine_v71he::queueAuxEntry(int actorNum, int subIndex) { #endif +void ActorC64::saveLoadWithSerializer(Serializer *ser) { + Actor::saveLoadWithSerializer(ser); + + static const SaveLoadEntry actorEntries[] = { + MKLINE(ActorC64, _costCommand, sleByte, VER(84)), + MKLINE(ActorC64, _costFrame, sleByte, VER(84)), + MKLINE(ActorC64, _miscflags, sleByte, VER(84)), + MKLINE(ActorC64, _speaking, sleByte, VER(84)), + MKLINE(ActorC64, _speakingPrev, sleByte, VER(84)), + MKEND() + }; + + ser->saveLoadEntries(this, actorEntries); +} + void Actor::saveLoadWithSerializer(Serializer *ser) { static const SaveLoadEntry actorEntries[] = { MKLINE(Actor, _pos.x, sleInt16, VER(8)), diff --git a/engines/scumm/actor.h b/engines/scumm/actor.h index 88ba9902b4..98854ec5ba 100644 --- a/engines/scumm/actor.h +++ b/engines/scumm/actor.h @@ -305,7 +305,7 @@ public: void classChanged(int cls, bool value); // Used by the save/load system: - void saveLoadWithSerializer(Serializer *ser); + virtual void saveLoadWithSerializer(Serializer *ser); protected: bool isInClass(int cls); @@ -381,14 +381,16 @@ protected: class ActorC64 : public Actor_v2 { public: - // FIXME: These vars are never saved, which might lead to broken save states. - byte _miscflags; - byte _speaking, _speakingPrev; byte _costCommand, _costFrame; + byte _miscflags; // 0x1: strong, 0x8: Ed's enemy, 0x40: stop moving, 0x80: hide(dead/radiation suit) + byte _speaking, _speakingPrev; public: ActorC64(ScummEngine *scumm, int id) : Actor_v2(scumm, id) { - _speaking = _speakingPrev = _costCommand = _costFrame = 0; + _costCommand = 0; + _costFrame = 0; + _speaking = 0; + _speakingPrev = 0; } virtual void initActor(int mode) { Actor_v2::initActor(mode); @@ -397,6 +399,9 @@ public: } } + // Used by the save/load system: + virtual void saveLoadWithSerializer(Serializer *ser); + protected: }; diff --git a/engines/scumm/akos.cpp b/engines/scumm/akos.cpp index d5d6b6182b..354a1d4491 100644 --- a/engines/scumm/akos.cpp +++ b/engines/scumm/akos.cpp @@ -1087,10 +1087,10 @@ void AkosRenderer::akos16SetupBitReader(const byte *src) { } #define AKOS16_FILL_BITS() \ - if (_akos16.numbits <= 8) { \ - _akos16.bits |= (*_akos16.dataptr++) << _akos16.numbits; \ - _akos16.numbits += 8; \ - } + if (_akos16.numbits <= 8) { \ + _akos16.bits |= (*_akos16.dataptr++) << _akos16.numbits; \ + _akos16.numbits += 8; \ + } #define AKOS16_EAT_BITS(n) \ _akos16.numbits -= (n); \ diff --git a/engines/scumm/boxes.cpp b/engines/scumm/boxes.cpp index dc6f10696f..fb8e128415 100644 --- a/engines/scumm/boxes.cpp +++ b/engines/scumm/boxes.cpp @@ -614,10 +614,8 @@ BoxCoords ScummEngine::getBoxCoordinates(int boxnum) { box->lr.x = bp->c64.x2; box->lr.y = bp->c64.y2; - if (bp->c64.mask & 0x88) { + if ((bp->c64.mask & 0x88) == 0x88) { // walkbox for (right/left) corner - // TODO: ladders (incl. man-eating plant) have mask 0x8A, - // must those walkboxes be adjusted? if (bp->c64.mask & 0x04) box->ur = box->ul; else diff --git a/engines/scumm/charset.cpp b/engines/scumm/charset.cpp index fa4804ce7d..e75a45212e 100644 --- a/engines/scumm/charset.cpp +++ b/engines/scumm/charset.cpp @@ -52,20 +52,15 @@ void ScummEngine::loadCJKFont() { _newLineCharacter = 0; if (_game.version <= 5 && _game.platform == Common::kPlatformFMTowns && _language == Common::JA_JPN) { // FM-TOWNS v3 / v5 Kanji - int numChar = 256 * 32; - _2byteWidth = 16; - _2byteHeight = 16; - // use FM-TOWNS font rom, since game files don't have kanji font resources - if (!fp.open("fmt_fnt.rom")) { - error("SCUMM::Font: Couldn't open fmt_fnt.rom"); - } else { - _useCJKMode = true; - debug(2, "Loading FM-TOWNS Kanji rom"); - _2byteFontPtr = new byte[((_2byteWidth + 7) / 8) * _2byteHeight * numChar]; - fp.read(_2byteFontPtr, ((_2byteWidth + 7) / 8) * _2byteHeight * numChar); - fp.close(); - } +#ifdef DISABLE_TOWNS_DUAL_LAYER_MODE + error("FM-Towns Kanji font drawing requires dual graphics layer support which is disabled in this build"); +#endif + // use FM-TOWNS font rom, since game files don't have kanji font resources + _cjkFont = Graphics::FontSJIS::createFont(Common::kPlatformFMTowns); + if (!_cjkFont) + error("SCUMM::Font: Couldn't open file 'FMT_FNT.ROM'"); _textSurfaceMultiplier = 2; + _useCJKMode = true; } else if (_game.id == GID_LOOM && _game.platform == Common::kPlatformPCEngine && _language == Common::JA_JPN) { int numChar = 3418; _2byteWidth = 12; @@ -167,90 +162,6 @@ void ScummEngine::loadCJKFont() { } } -static int SJIStoFMTChunk(int f, int s) { //converts sjis code to fmt font offset - enum { - KANA = 0, - KANJI = 1, - EKANJI = 2 - }; - int base = s - ((s + 1) % 32); - int c = 0, p = 0, chunk_f = 0, chunk = 0, cr = 0, kanjiType = KANA; - - if (f >= 0x81 && f <= 0x84) kanjiType = KANA; - if (f >= 0x88 && f <= 0x9f) kanjiType = KANJI; - if (f >= 0xe0 && f <= 0xea) kanjiType = EKANJI; - - if ((f > 0xe8 || (f == 0xe8 && base >= 0x9f)) || (f > 0x90 || (f == 0x90 && base >= 0x9f))) { - c = 48; //correction - p = -8; //correction - } - - if (kanjiType == KANA) {//Kana - chunk_f = (f - 0x81) * 2; - } else if (kanjiType == KANJI) {//Standard Kanji - p += f - 0x88; - chunk_f = c + 2 * p; - } else if (kanjiType == EKANJI) {//Enhanced Kanji - p += f - 0xe0; - chunk_f = c + 2 * p; - } - - // Base corrections - if (base == 0x7f && s == 0x7f) - base -= 0x20; - if (base == 0x9f && s == 0xbe) - base += 0x20; - if (base == 0xbf && s == 0xde) - base += 0x20; - //if (base == 0x7f && s == 0x9e) - // base += 0x20; - - switch (base) { - case 0x3f: - cr = 0; //3f - if (kanjiType == KANA) chunk = 1; - else if (kanjiType == KANJI) chunk = 31; - else if (kanjiType == EKANJI) chunk = 111; - break; - case 0x5f: - cr = 0; //5f - if (kanjiType == KANA) chunk = 17; - else if (kanjiType == KANJI) chunk = 47; - else if (kanjiType == EKANJI) chunk = 127; - break; - case 0x7f: - cr = -1; //80 - if (kanjiType == KANA) chunk = 9; - else if (kanjiType == KANJI) chunk = 63; - else if (kanjiType == EKANJI) chunk = 143; - break; - case 0x9f: - cr = 1; //9e - if (kanjiType == KANA) chunk = 2; - else if (kanjiType == KANJI) chunk = 32; - else if (kanjiType == EKANJI) chunk = 112; - break; - case 0xbf: - cr = 1; //be - if (kanjiType == KANA) chunk = 18; - else if (kanjiType == KANJI) chunk = 48; - else if (kanjiType == EKANJI) chunk = 128; - break; - case 0xdf: - cr = 1; //de - if (kanjiType == KANA) chunk = 10; - else if (kanjiType == KANJI) chunk = 64; - else if (kanjiType == EKANJI) chunk = 144; - break; - default: - debug(4, "Invalid Char! f %x s %x base %x c %d p %d", f, s, base, c, p); - return 0; - } - - debug(6, "Kanji: %c%c f 0x%x s 0x%x base 0x%x c %d p %d chunk %d cr %d index %d", f, s, f, s, base, c, p, chunk, cr, ((chunk_f + chunk) * 32 + (s - base)) + cr); - return ((chunk_f + chunk) * 32 + (s - base)) + cr; -} - static int SJIStoPCEChunk(int f, int s) { //converts sjis code to pce font offset // rangeTbl maps SJIS char-codes to the PCE System Card font rom. // Each pair {<upperBound>,<lowerBound>} in the array represents a SJIS range. @@ -327,9 +238,8 @@ byte *ScummEngine::get2byteCharPtr(int idx) { } idx = (SWAP_CONSTANT_16(idx) & 0x7fff) - 1; - } else { - idx = SJIStoFMTChunk((idx % 256), (idx / 256)); } + break; case Common::ZH_TWN: { @@ -453,30 +363,79 @@ void CharsetRendererV3::setCurID(int32 id) { } int CharsetRendererCommon::getFontHeight() { - if (_vm->_useCJKMode) - return MAX(_vm->_2byteHeight + 1, _fontHeight); - else + if (_vm->_useCJKMode) { + if (_vm->_game.platform == Common::kPlatformFMTowns) { + static const uint8 sjisFontHeightM1[] = { 0, 8, 9, 8, 9, 8, 9, 0, 0, 0 }; + static const uint8 sjisFontHeightM2[] = { 0, 8, 9, 9, 9, 8, 9, 9, 9, 8 }; + static const uint8 sjisFontHeightI4[] = { 0, 8, 9, 9, 9, 8, 8, 8, 8, 8 }; + const uint8 *htbl = (_vm->_game.id == GID_MONKEY) ? sjisFontHeightM1 : ((_vm->_game.id == GID_INDY4) ? sjisFontHeightI4 : sjisFontHeightM2); + return (_vm->_game.version == 3) ? 8 : htbl[_curId]; + } else { + return MAX(_vm->_2byteHeight + 1, _fontHeight); + } + } else return _fontHeight; } // do spacing for variable width old-style font -int CharsetRendererClassic::getCharWidth(byte chr) { - if (chr >= 0x80 && _vm->_useCJKMode) - return _vm->_2byteWidth / 2; +int CharsetRendererClassic::getCharWidth(uint16 chr) { int spacing = 0; - int offs = READ_LE_UINT32(_fontPtr + chr * 4 + 4); - if (offs) { - spacing = _fontPtr[offs] + (signed char)_fontPtr[offs + 2]; + if (_vm->_useCJKMode) { + if (_vm->_game.platform == Common::kPlatformFMTowns) { + if ((chr & 0xff00) == 0xfd00) { + chr &= 0xff; + } else if (chr >= 256) { + spacing = 8; + } else if (useTownsFontRomCharacter(chr)) { + spacing = 4; + } + + if (spacing) { + if (_vm->_game.id == GID_MONKEY) { + spacing++; + if (_curId == 2) + spacing++; + } else if (_vm->_game.id != GID_INDY4 && _curId == 1) { + spacing++; + } + } + + } else if (chr >= 0x80) { + return _vm->_2byteWidth / 2; + } + } + + if (!spacing) { + int offs = READ_LE_UINT32(_fontPtr + chr * 4 + 4); + if (offs) { + spacing = _fontPtr[offs] + (signed char)_fontPtr[offs + 2]; + } } return spacing; } +bool CharsetRendererClassic::useTownsFontRomCharacter(uint16 chr) { +#ifndef DISABLE_TOWNS_DUAL_LAYER_MODE + if (_vm->_game.platform != Common::kPlatformFMTowns || !_vm->_useCJKMode) + return false; + + if (chr < 128) { + if (((_vm->_game.id == GID_MONKEY2 && _curId != 0) || (_vm->_game.id == GID_INDY4 && _curId != 3)) && (chr > 31 && chr != 94 && chr != 95 && chr != 126 && chr != 127)) + return true; + return false; + } + return true; +#else + return false; +#endif +} + int CharsetRenderer::getStringWidth(int arg, const byte *text) { int pos = 0; int width = 1; - byte chr; + int chr; int oldID = getCurID(); int code = (_vm->_game.heversion >= 80) ? 127 : 64; @@ -534,12 +493,20 @@ int CharsetRenderer::getStringWidth(int arg, const byte *text) { } } } - if ((chr & 0x80) && _vm->_useCJKMode) { - pos++; - width += _vm->_2byteWidth; - } else { - width += getCharWidth(chr); + + if (_vm->_useCJKMode) { + if (_vm->_game.platform == Common::kPlatformFMTowns) { + if (checkSJISCode(chr)) + // This strange character conversion is the exact way the original does it here. + // This is the only way to get an accurate text formatting in the MI1 intro. + chr = (int8)text[pos++] | (chr << 8); + } else if (chr & 0x80) { + pos++; + width += _vm->_2byteWidth; + continue; + } } + width += getCharWidth(chr); } setCurID(oldID); @@ -550,7 +517,7 @@ int CharsetRenderer::getStringWidth(int arg, const byte *text) { void CharsetRenderer::addLinebreaks(int a, byte *str, int pos, int maxwidth) { int lastspace = -1; int curw = 1; - byte chr; + int chr; int oldID = getCurID(); int code = (_vm->_game.heversion >= 80) ? 127 : 64; @@ -612,9 +579,17 @@ void CharsetRenderer::addLinebreaks(int a, byte *str, int pos, int maxwidth) { if (chr == _vm->_newLineCharacter) lastspace = pos - 1; - if ((chr & 0x80) && _vm->_useCJKMode) { - pos++; - curw += _vm->_2byteWidth; + if (_vm->_useCJKMode) { + if (_vm->_game.platform == Common::kPlatformFMTowns) { + if (checkSJISCode(chr)) + // This strange character conversion is the exact way the original does it here. + // This is the only way to get an accurate text formatting in the MI1 intro. + chr = (int8)str[pos++] | (chr << 8); + curw += getCharWidth(chr); + } else if (chr & 0x80) { + pos++; + curw += _vm->_2byteWidth; + } } else { curw += getCharWidth(chr); } @@ -631,12 +606,22 @@ void CharsetRenderer::addLinebreaks(int a, byte *str, int pos, int maxwidth) { setCurID(oldID); } -int CharsetRendererV3::getCharWidth(byte chr) { - if (chr & 0x80 && _vm->_useCJKMode) - return _vm->_2byteWidth / 2; +int CharsetRendererV3::getCharWidth(uint16 chr) { int spacing = 0; - spacing = *(_widthTable + chr); + if (_vm->_useCJKMode) { + if (_vm->_game.platform == Common::kPlatformFMTowns) { + if (chr >= 256) + spacing = 8; + else if (chr >= 128) + spacing = 4; + } else if (chr & 0x80) { + spacing = _vm->_2byteWidth / 2; + } + } + + if (!spacing) + spacing = *(_widthTable + chr); return spacing; } @@ -655,6 +640,14 @@ void CharsetRendererV3::setColor(byte color) { } else useShadow = false; +#ifndef DISABLE_TOWNS_DUAL_LAYER_MODE + if (_vm->_game.platform == Common::kPlatformFMTowns) { + _color = (_color & 0x0f) | ((_color & 0x0f) << 4); + if (_color == 0) + _color = 0x88; + } +#endif + enableShadow(useShadow); translateColor(); @@ -673,17 +666,37 @@ void CharsetRendererCommon::enableShadow(bool enable) { if (enable) { if (_vm->_game.platform == Common::kPlatformFMTowns) { _shadowColor = 8; +#ifndef DISABLE_TOWNS_DUAL_LAYER_MODE + _shadowColor = _vm->_game.version == 5 ? _vm->_townsCharsetColorMap[0] : 0x88; + if (_vm->_cjkFont) { + if (_vm->_game.version == 5) { + if (((_vm->_game.id == GID_MONKEY) && (_curId == 2 || _curId == 4 || _curId == 6)) || + ((_vm->_game.id == GID_MONKEY2) && (_curId != 1 && _curId != 5 && _curId != 9)) || + ((_vm->_game.id == GID_INDY4) && (_curId == 2 || _curId == 3 || _curId == 4))) { + _vm->_cjkFont->setDrawingMode(Graphics::FontSJIS::kOutlineMode); + } else { + _vm->_cjkFont->setDrawingMode(Graphics::FontSJIS::kDefaultMode); + } + _vm->_cjkFont->toggleFlippedMode((_vm->_game.id == GID_MONKEY || _vm->_game.id == GID_MONKEY2) && _curId == 3); + } else { + _vm->_cjkFont->setDrawingMode(Graphics::FontSJIS::kShadowMode); + } + } +#endif _shadowMode = kFMTOWNSShadowMode; } else { _shadowColor = 0; _shadowMode = kNormalShadowMode; } } else { + if (_vm->_cjkFont) { + _vm->_cjkFont->setDrawingMode(Graphics::FontSJIS::kDefaultMode); + _vm->_cjkFont->toggleFlippedMode(false); + } _shadowMode = kNoShadowMode; } } - void CharsetRendererV3::printChar(int chr, bool ignoreCharsetMask) { // WORKAROUND for bug #1509509: Indy3 Mac does not show black // characters (such as in the grail diary) if ignoreCharsetMask @@ -696,7 +709,7 @@ void CharsetRendererV3::printChar(int chr, bool ignoreCharsetMask) { VirtScreen *vs; const byte *charPtr; byte *dst; - int is2byte = (chr >= 0x80 && _vm->_useCJKMode) ? 1 : 0; + int is2byte = (chr >= 256 && _vm->_useCJKMode) ? 1 : 0; assertRange(0, _curId, _vm->_numCharsets - 1, "charset"); @@ -706,10 +719,16 @@ void CharsetRendererV3::printChar(int chr, bool ignoreCharsetMask) { if (chr == '@') return; - if (is2byte) { - charPtr = _vm->get2byteCharPtr(chr); - width = _vm->_2byteWidth; - height = _vm->_2byteHeight; + if (_vm->_useCJKMode && chr > 127) { + if (_vm->_game.platform == Common::kPlatformFMTowns) { + charPtr = 0; + width = _vm->_cjkFont->getCharWidth(chr); + height = _vm->_cjkFont->getFontHeight(); + } else { + width = _vm->_2byteWidth; + height = _vm->_2byteHeight; + charPtr = _vm->get2byteCharPtr(chr); + } } else { charPtr = _fontPtr + chr * 8; width = getCharWidth(chr); @@ -744,18 +763,31 @@ void CharsetRendererV3::printChar(int chr, bool ignoreCharsetMask) { _hasMask = true; _textScreenID = vs->number; } - if ((ignoreCharsetMask || !vs->hasTwoBuffers) && !(_vm->_useCJKMode && _vm->_textSurfaceMultiplier == 2)) { + + if ( +#ifndef DISABLE_TOWNS_DUAL_LAYER_MODE + (_vm->_game.platform != Common::kPlatformFMTowns) && +#endif + (ignoreCharsetMask || !vs->hasTwoBuffers)) { dst = vs->getPixels(_left, drawTop); - drawBits1(*vs, dst, charPtr, drawTop, origWidth, origHeight, vs->bytesPerPixel); + if (charPtr) + drawBits1(*vs, dst, charPtr, drawTop, origWidth, origHeight, vs->bytesPerPixel); + else if (_vm->_cjkFont) + _vm->_cjkFont->drawChar(dst, chr, vs->pitch, vs->bytesPerPixel, _color, _shadowColor); } else { dst = (byte *)_vm->_textSurface.getBasePtr(_left * _vm->_textSurfaceMultiplier, _top * _vm->_textSurfaceMultiplier); - drawBits1(_vm->_textSurface, dst, charPtr, drawTop, origWidth, origHeight, _vm->_textSurface.bytesPerPixel); + if (charPtr) + drawBits1(_vm->_textSurface, dst, charPtr, drawTop, origWidth, origHeight, _vm->_textSurface.bytesPerPixel, (_vm->_textSurfaceMultiplier == 2 && !is2byte)); + else if (_vm->_cjkFont) + _vm->_cjkFont->drawChar(dst, chr, _vm->_textSurface.pitch, vs->bytesPerPixel, _color, _shadowColor); + if (is2byte) + origWidth /= _vm->_textSurfaceMultiplier; } if (_str.left > _left) _str.left = _left; - _left += origWidth / _vm->_textSurfaceMultiplier; + _left += origWidth; if (_str.right < _left) { _str.right = _left; @@ -773,9 +805,17 @@ void CharsetRendererV3::drawChar(int chr, const Graphics::Surface &s, int x, int int width, height; int is2byte = (chr >= 0x80 && _vm->_useCJKMode) ? 1 : 0; if (is2byte) { - charPtr = _vm->get2byteCharPtr(chr); - width = _vm->_2byteWidth; - height = _vm->_2byteHeight; + if (_vm->_game.platform == Common::kPlatformFMTowns) { + width = _vm->_cjkFont->getCharWidth(chr); + height = _vm->_cjkFont->getFontHeight(); + dst = (byte *)s.pixels + y * s.pitch + x; + _vm->_cjkFont->drawChar(dst, chr, s.pitch, s.bytesPerPixel, _color, _shadowColor); + return; + } else { + charPtr = _vm->get2byteCharPtr(chr); + width = _vm->_2byteWidth; + height = _vm->_2byteHeight; + } } else { charPtr = _fontPtr + chr * 8; // width = height = 8; @@ -801,6 +841,29 @@ void CharsetRenderer::translateColor() { } } +#ifndef DISABLE_TOWNS_DUAL_LAYER_MODE +void CharsetRenderer::processTownsCharsetColors(uint8 bytesPerPixel) { + if (_vm->_game.platform == Common::kPlatformFMTowns) { + for (int i = 0; i < (1 << bytesPerPixel); i++) { + uint8 c = _vm->_charsetColorMap[i]; + + if (c > 16) { + uint8 t = (_vm->_currentPalette[c * 3] < 32) ? 4 : 12; + t |= ((_vm->_currentPalette[c * 3 + 1] < 32) ? 2 : 10); + t |= ((_vm->_currentPalette[c * 3 + 1] < 32) ? 1 : 9); + c = t; + } + + if (c == 0) + c = _vm->_townsOverrideShadowColor; + + c = ((c & 0x0f) << 4) | (c & 0x0f); + _vm->_townsCharsetColorMap[i] = c; + } + } +} +#endif + void CharsetRenderer::saveLoadWithSerializer(Serializer *ser) { static const SaveLoadEntry charsetRendererEntries[] = { MKLINE_OLD(CharsetRenderer, _curId, sleByte, VER(73), VER(73)), @@ -822,7 +885,7 @@ void CharsetRendererClassic::printChar(int chr, bool ignoreCharsetMask) { int offsX, offsY; VirtScreen *vs; const byte *charPtr; - bool is2byte = (chr >= 0x80 && _vm->_useCJKMode); + bool is2byte = (chr >= 256 && _vm->_useCJKMode); assertRange(1, _curId, _vm->_numCharsets - 1, "charset"); @@ -836,12 +899,44 @@ void CharsetRendererClassic::printChar(int chr, bool ignoreCharsetMask) { _vm->_charsetColorMap[1] = _color; - if (is2byte) { +#ifndef DISABLE_TOWNS_DUAL_LAYER_MODE + processTownsCharsetColors(_bytesPerPixel); + bool noSjis = false; + + if (_vm->_game.platform == Common::kPlatformFMTowns && _vm->_useCJKMode) { + if ((chr & 0x00ff) == 0x00fd) { + chr >>= 8; + noSjis = true; + } + } + + if (useTownsFontRomCharacter(chr) && !noSjis) { + charPtr = 0; + _vm->_cjkChar = chr; enableShadow(true); + + width = getCharWidth(chr); + // For whatever reason MI1 uses a different font width + // for alignment calculation and for drawing when + // charset 2 is active. This fixes some subtle glitches. + if (_vm->_game.id == GID_MONKEY && _curId == 2) + width--; + origWidth = width; + + origHeight = height = getFontHeight(); + offsX = offsY = 0; + } else +#endif + if (_vm->_useCJKMode && (chr >= 128) && !noSjis) { + enableShadow(true); + origWidth = width = _vm->_2byteWidth; + origHeight = height = _vm->_2byteHeight; charPtr = _vm->get2byteCharPtr(chr); - width = _vm->_2byteWidth; - height = _vm->_2byteHeight; offsX = offsY = 0; + if (_shadowMode != kNoShadowMode) { + width++; + height++; + } } else { uint32 charOffs = READ_LE_UINT32(_fontPtr + chr * 4 + 4); assert(charOffs < 0x14000); @@ -849,8 +944,8 @@ void CharsetRendererClassic::printChar(int chr, bool ignoreCharsetMask) { return; charPtr = _fontPtr + charOffs; - width = charPtr[0]; - height = charPtr[1]; + width = origWidth = charPtr[0]; + height = origHeight = charPtr[1]; if (_disableOffsX) { offsX = 0; @@ -862,13 +957,7 @@ void CharsetRendererClassic::printChar(int chr, bool ignoreCharsetMask) { charPtr += 4; // Skip over char header } - origWidth = width; - origHeight = height; - if (_shadowMode != kNoShadowMode) { - width++; - height++; - } if (_firstChar) { _str.left = 0; _str.top = 0; @@ -879,8 +968,8 @@ void CharsetRendererClassic::printChar(int chr, bool ignoreCharsetMask) { _top += offsY; _left += offsX; - if (_left + origWidth / _vm->_textSurfaceMultiplier > _right + 1 || _left < 0) { - _left += origWidth / _vm->_textSurfaceMultiplier; + if (_left + origWidth > _right + 1 || _left < 0) { + _left += origWidth; _top -= offsY; return; } @@ -905,23 +994,29 @@ void CharsetRendererClassic::printChar(int chr, bool ignoreCharsetMask) { _vm->markRectAsDirty(vs->number, _left, _left + width, drawTop, drawTop + height); - if (!ignoreCharsetMask) { + // This check for kPlatformFMTowns and kMainVirtScreen is at least required for the chat with + // the navigator's head in front of the ghost ship in Monkey Island 1 + if (!ignoreCharsetMask +#ifndef DISABLE_TOWNS_DUAL_LAYER_MODE + || (_vm->_game.platform == Common::kPlatformFMTowns && vs->number == kMainVirtScreen) +#endif + ) { _hasMask = true; _textScreenID = vs->number; } printCharIntern(is2byte, charPtr, origWidth, origHeight, width, height, vs, ignoreCharsetMask); - _left += origWidth / _vm->_textSurfaceMultiplier; + _left += origWidth; if (_str.right < _left) { _str.right = _left; - if (_shadowMode != kNoShadowMode) + if (_vm->_game.platform != Common::kPlatformFMTowns && _shadowMode != kNoShadowMode) _str.right++; } - if (_str.bottom < _top + height / _vm->_textSurfaceMultiplier) - _str.bottom = _top + height / _vm->_textSurfaceMultiplier; + if (_str.bottom < _top + origHeight) + _str.bottom = _top + origHeight; _top -= offsY; } @@ -961,7 +1056,11 @@ void CharsetRendererClassic::printCharIntern(bool is2byte, const byte *charPtr, } else { Graphics::Surface dstSurface; Graphics::Surface backSurface; - if ((ignoreCharsetMask || !vs->hasTwoBuffers) && !(_vm->_useCJKMode && _vm->_textSurfaceMultiplier == 2)) { + if ((ignoreCharsetMask || !vs->hasTwoBuffers) +#ifndef DISABLE_TOWNS_DUAL_LAYER_MODE + && (_vm->_game.platform != Common::kPlatformFMTowns) +#endif + ) { dstSurface = *vs; dstPtr = vs->getPixels(_left, drawTop); } else { @@ -980,10 +1079,12 @@ void CharsetRendererClassic::printCharIntern(bool is2byte, const byte *charPtr, drawTop = _top - _vm->_screenTop; } - if (is2byte) { + if (!charPtr && _vm->_cjkFont) + _vm->_cjkFont->drawChar(dstPtr, _vm->_cjkChar, dstSurface.pitch, dstSurface.bytesPerPixel, _vm->_townsCharsetColorMap[1], _shadowColor); + else if (is2byte) { drawBits1(dstSurface, dstPtr, charPtr, drawTop, origWidth, origHeight, dstSurface.bytesPerPixel); } else { - drawBitsN(dstSurface, dstPtr, charPtr, *_fontPtr, drawTop, origWidth, origHeight); + drawBitsN(dstSurface, dstPtr, charPtr, *_fontPtr, drawTop, origWidth, origHeight, _vm->_textSurfaceMultiplier == 2); } if (_blitAlso && vs->hasTwoBuffers) { @@ -1031,9 +1132,17 @@ void CharsetRendererClassic::drawChar(int chr, const Graphics::Surface &s, int x if (is2byte) { enableShadow(true); - charPtr = _vm->get2byteCharPtr(chr); - width = _vm->_2byteWidth; - height = _vm->_2byteHeight; + if (_vm->_game.platform == Common::kPlatformFMTowns) { + width = _vm->_cjkFont->getCharWidth(chr); + height = _vm->_cjkFont->getFontHeight(); + dst = (byte *)s.pixels + y * s.pitch + x; + _vm->_cjkFont->drawChar(dst, chr, s.pitch, s.bytesPerPixel, _color, _shadowColor); + return; + } else { + charPtr = _vm->get2byteCharPtr(chr); + width = _vm->_2byteWidth; + height = _vm->_2byteHeight; + } } else { uint32 charOffs = READ_LE_UINT32(_fontPtr + chr * 4 + 4); assert(charOffs < 0x10000); @@ -1056,23 +1165,55 @@ void CharsetRendererClassic::drawChar(int chr, const Graphics::Surface &s, int x } } -void CharsetRendererClassic::drawBitsN(const Graphics::Surface &s, byte *dst, const byte *src, byte bpp, int drawTop, int width, int height) { +void CharsetRendererClassic::drawBitsN(const Graphics::Surface &s, byte *dst, const byte *src, byte bpp, int drawTop, int width, int height, +#ifndef DISABLE_TOWNS_DUAL_LAYER_MODE + bool scale2x) { +#else + bool) { +#endif + int y, x; int color; byte numbits, bits; + byte *dst2 = dst; + int pitch = s.pitch - width; + assert(bpp == 1 || bpp == 2 || bpp == 4 || bpp == 8); bits = *src++; numbits = 8; + byte *cmap = _vm->_charsetColorMap; + +#ifndef DISABLE_TOWNS_DUAL_LAYER_MODE + if (_vm->_game.platform == Common::kPlatformFMTowns) + cmap = _vm->_townsCharsetColorMap; + if (scale2x) { + dst2 += s.pitch; + pitch <<= 1; + } +#endif for (y = 0; y < height && y + drawTop < s.h; y++) { for (x = 0; x < width; x++) { color = (bits >> (8 - bpp)) & 0xFF; if (color && y + drawTop >= 0) { - *dst = _vm->_charsetColorMap[color]; + *dst = cmap[color]; + +#ifndef DISABLE_TOWNS_DUAL_LAYER_MODE + if (scale2x) + dst[1] = dst2[0] = dst2[1] = dst[0]; +#endif } dst++; + +#ifndef DISABLE_TOWNS_DUAL_LAYER_MODE + if (scale2x) { + dst++; + dst2 += 2; + } +#endif + bits <<= bpp; numbits -= bpp; if (numbits == 0) { @@ -1080,13 +1221,37 @@ void CharsetRendererClassic::drawBitsN(const Graphics::Surface &s, byte *dst, co numbits = 8; } } - dst += s.pitch - width; + dst += pitch; +#ifndef DISABLE_TOWNS_DUAL_LAYER_MODE + dst2 += pitch; +#endif } } -void CharsetRendererCommon::drawBits1(const Graphics::Surface &s, byte *dst, const byte *src, int drawTop, int width, int height, uint8 bitDepth) { +void CharsetRendererCommon::drawBits1(const Graphics::Surface &s, byte *dst, const byte *src, int drawTop, int width, int height, uint8 bitDepth, +#ifndef DISABLE_TOWNS_DUAL_LAYER_MODE + bool scale2x) { +#else + bool) { +#endif + int y, x; byte bits = 0; + uint8 col = _color; + int pitch = s.pitch - width * bitDepth; + byte *dst2 = dst + s.pitch; + +#ifndef DISABLE_TOWNS_DUAL_LAYER_MODE + byte *dst3 = dst2; + byte *dst4 = dst2; + if (scale2x) { + dst3 = dst2 + s.pitch; + dst4 = dst3 + s.pitch; + pitch <<= 1; + } + if (_vm->_game.platform == Common::kPlatformFMTowns && _vm->_game.version == 5) + col = _vm->_townsCharsetColorMap[1]; +#endif for (y = 0; y < height && y + drawTop < s.h; y++) { for (x = 0; x < width; x++) { @@ -1103,23 +1268,49 @@ void CharsetRendererCommon::drawBits1(const Graphics::Surface &s, byte *dst, con WRITE_UINT16(dst, _vm->_16BitPalette[_color]); } else { if (_shadowMode != kNoShadowMode) { - *(dst + 1) = _shadowColor; - *(dst + s.pitch) = _shadowColor; - if (_shadowMode != kFMTOWNSShadowMode) - *(dst + s.pitch + 1) = _shadowColor; +#ifndef DISABLE_TOWNS_DUAL_LAYER_MODE + if (scale2x) { + dst[2] = dst[3] = dst2[2] = dst2[3] = _shadowColor; + dst3[0] = dst4[0] = dst3[1] = dst4[1] = _shadowColor; + } else +#endif + { + dst[1] = dst2[0] = _shadowColor; + if (_shadowMode != kFMTOWNSShadowMode) + dst2[1] = _shadowColor; + } } - *dst = _color; + dst[0] = col; + +#ifndef DISABLE_TOWNS_DUAL_LAYER_MODE + if (scale2x) + dst[1] = dst2[0] = dst2[1] = col; +#endif } } dst += bitDepth; + dst2 += bitDepth; +#ifndef DISABLE_TOWNS_DUAL_LAYER_MODE + if (scale2x) { + dst++; + dst2++; + dst3 += 2; + dst4 += 2; + } +#endif } - dst += s.pitch - width * bitDepth; + dst += pitch; + dst2 += pitch; +#ifndef DISABLE_TOWNS_DUAL_LAYER_MODE + dst3 += pitch; + dst4 += pitch; +#endif } } #ifdef USE_RGB_COLOR -void CharsetRendererPCE::drawBits1(const Graphics::Surface &s, byte *dst, const byte *src, int drawTop, int width, int height, uint8 bitDepth) { +void CharsetRendererPCE::drawBits1(const Graphics::Surface &s, byte *dst, const byte *src, int drawTop, int width, int height, uint8 bitDepth, bool scalex) { int y, x; int bitCount = 0; byte bits = 0; @@ -1191,7 +1382,7 @@ int CharsetRendererNut::getCharHeight(byte chr) { return _current->getCharHeight(chr); } -int CharsetRendererNut::getCharWidth(byte chr) { +int CharsetRendererNut::getCharWidth(uint16 chr) { assert(_current); return _current->getCharWidth(chr); } @@ -1349,7 +1540,7 @@ void CharsetRendererNES::drawChar(int chr, const Graphics::Surface &s, int x, in drawBits1(s, dst, charPtr, y, width, height, s.bytesPerPixel); } -void CharsetRendererNES::drawBits1(const Graphics::Surface &s, byte *dst, const byte *src, int drawTop, int width, int height, uint8 bitDepth) { +void CharsetRendererNES::drawBits1(const Graphics::Surface &s, byte *dst, const byte *src, int drawTop, int width, int height, uint8 bitDepth, bool scalex) { for (int i = 0; i < 8; i++) { byte c0 = src[i]; byte c1 = src[i + 8]; diff --git a/engines/scumm/charset.h b/engines/scumm/charset.h index dca254669b..991ea2c8ad 100644 --- a/engines/scumm/charset.h +++ b/engines/scumm/charset.h @@ -27,6 +27,7 @@ #include "common/scummsys.h" #include "common/rect.h" +#include "graphics/sjis.h" #include "scumm/gfx.h" #include "scumm/saveload.h" @@ -37,9 +38,9 @@ class NutRenderer; struct VirtScreen; static inline bool checkSJISCode(byte c) { - if ((c > 0x84 && c < 0x88) || (c > 0x9f && c < 0xe0) || (c > 0xea /* && c <= 0xff */)) - return false; - return true; + if ((c >= 0x80 && c <= 0x9f) || (c >= 0xe0 && c <= 0xfd)) + return true; + return false; } @@ -80,12 +81,16 @@ public: void addLinebreaks(int a, byte *str, int pos, int maxwidth); void translateColor(); +#ifndef DISABLE_TOWNS_DUAL_LAYER_MODE + void processTownsCharsetColors(uint8 bytesPerPixel); +#endif + virtual void setCurID(int32 id) = 0; int getCurID() { return _curId; } virtual int getFontHeight() = 0; virtual int getCharHeight(byte chr) { return getFontHeight(); } - virtual int getCharWidth(byte chr) = 0; + virtual int getCharWidth(uint16 chr) = 0; virtual void setColor(byte color) { _color = color; translateColor(); } @@ -108,7 +113,8 @@ protected: ShadowMode _shadowMode; void enableShadow(bool enable); - virtual void drawBits1(const Graphics::Surface &s, byte *dst, const byte *src, int drawTop, int width, int height, uint8 bitDepth); + virtual void drawBits1(const Graphics::Surface &s, byte *dst, const byte *src, int drawTop, int width, int height, uint8 bitDepth, bool scale2x = false); + public: CharsetRendererCommon(ScummEngine *vm); @@ -120,7 +126,7 @@ public: class CharsetRendererClassic : public CharsetRendererCommon { protected: - void drawBitsN(const Graphics::Surface &s, byte *dst, const byte *src, byte bpp, int drawTop, int width, int height); + void drawBitsN(const Graphics::Surface &s, byte *dst, const byte *src, byte bpp, int drawTop, int width, int height, bool scale2x = false); void printCharIntern(bool is2byte, const byte *charPtr, int origWidth, int origHeight, int width, int height, VirtScreen *vs, bool ignoreCharsetMask); @@ -130,14 +136,19 @@ public: void printChar(int chr, bool ignoreCharsetMask); void drawChar(int chr, const Graphics::Surface &s, int x, int y); - int getCharWidth(byte chr); + int getCharWidth(uint16 chr); + + // Some SCUMM 5 games contain hard coded logic to determine whether to use + // the SCUMM fonts or the FM-Towns font rom to draw a character. For the other + // games we will simply check for a character greater 127. + bool useTownsFontRomCharacter(uint16 chr); }; class CharsetRendererNES : public CharsetRendererCommon { protected: byte *_trTable; - void drawBits1(const Graphics::Surface &s, byte *dst, const byte *src, int drawTop, int width, int height, uint8 bitDepth); + void drawBits1(const Graphics::Surface &s, byte *dst, const byte *src, int drawTop, int width, int height, uint8 bitDepth, bool scale2x = false); public: CharsetRendererNES(ScummEngine *vm) : CharsetRendererCommon(vm) {} @@ -147,7 +158,7 @@ public: void drawChar(int chr, const Graphics::Surface &s, int x, int y); int getFontHeight() { return 8; } - int getCharWidth(byte chr) { return 8; } + int getCharWidth(uint16 chr) { return 8; } }; class CharsetRendererV3 : public CharsetRendererCommon { @@ -161,13 +172,13 @@ public: void drawChar(int chr, const Graphics::Surface &s, int x, int y); void setCurID(int32 id); void setColor(byte color); - int getCharWidth(byte chr); + int getCharWidth(uint16 chr); }; #ifdef USE_RGB_COLOR class CharsetRendererPCE : public CharsetRendererV3 { protected: - void drawBits1(const Graphics::Surface &s, byte *dst, const byte *src, int drawTop, int width, int height, uint8 bitDepth); + void drawBits1(const Graphics::Surface &s, byte *dst, const byte *src, int drawTop, int width, int height, uint8 bitDepth, bool scale2x = false); public: CharsetRendererPCE(ScummEngine *vm) : CharsetRendererV3(vm) {} @@ -185,7 +196,7 @@ public: ~CharsetRendererV2(); void setCurID(int32 id) {} - int getCharWidth(byte chr) { return 8; } + int getCharWidth(uint16 chr) { return 8; } }; #ifdef ENABLE_SCUMM_7_8 @@ -204,7 +215,7 @@ public: int getFontHeight(); int getCharHeight(byte chr); - int getCharWidth(byte chr); + int getCharWidth(uint16 chr); }; #endif diff --git a/engines/scumm/cursor.cpp b/engines/scumm/cursor.cpp index b1f8f2ae2b..8e211a5041 100644 --- a/engines/scumm/cursor.cpp +++ b/engines/scumm/cursor.cpp @@ -554,11 +554,16 @@ void ScummEngine_v5::setBuiltinCursor(int idx) { uint16 color; const uint16 *src = _cursorImages[_currentCursor]; - if (_bytesPerPixel == 2) { + if (_bytesPerPixelOutput == 2) { if (_game.id == GID_LOOM && _game.platform == Common::kPlatformPCEngine) { byte r, g, b; colorPCEToRGB(default_pce_cursor_colors[idx], &r, &g, &b); color = get16BitColor(r, g, b); +#ifndef DISABLE_TOWNS_DUAL_LAYER_MODE + } else if (_game.platform == Common::kPlatformFMTowns) { + byte *palEntry = &_textPalette[default_cursor_colors[idx] * 3]; + color = get16BitColor(palEntry[0], palEntry[1], palEntry[2]); +#endif } else { color = _16BitPalette[default_cursor_colors[idx]]; } @@ -570,18 +575,28 @@ void ScummEngine_v5::setBuiltinCursor(int idx) { memset(_grabbedCursor, 0xFF, sizeof(_grabbedCursor)); } - _cursor.hotspotX = _cursorHotspots[2 * _currentCursor]; - _cursor.hotspotY = _cursorHotspots[2 * _currentCursor + 1]; - _cursor.width = 16; - _cursor.height = 16; + _cursor.hotspotX = _cursorHotspots[2 * _currentCursor] * _textSurfaceMultiplier; + _cursor.hotspotY = _cursorHotspots[2 * _currentCursor + 1] * _textSurfaceMultiplier; + _cursor.width = 16 * _textSurfaceMultiplier; + _cursor.height = 16 * _textSurfaceMultiplier; + + int scl = _bytesPerPixelOutput * _textSurfaceMultiplier; for (i = 0; i < 16; i++) { for (j = 0; j < 16; j++) { if (src[i] & (1 << j)) { - if (_bytesPerPixel == 2) - WRITE_UINT16(_grabbedCursor + 32 * i + (15 - j) * 2, color); - else - _grabbedCursor[16 * i + 15 - j] = color; + byte *dst1 = _grabbedCursor + 16 * scl * i * _textSurfaceMultiplier + (15 - j) * scl; + byte *dst2 = (_textSurfaceMultiplier == 2) ? dst1 + 16 * scl : dst1; + if (_bytesPerPixelOutput == 2) { + for (int b = 0; b < scl; b += 2) { + *((uint16*)dst1) = *((uint16*)dst2) = color; + dst1 += 2; + dst2 += 2; + } + } else { + for (int b = 0; b < scl; b++) + *dst1++ = *dst2++ = color; + } } } } diff --git a/engines/scumm/debugger.cpp b/engines/scumm/debugger.cpp index b5a4070f0b..a348d9c942 100644 --- a/engines/scumm/debugger.cpp +++ b/engines/scumm/debugger.cpp @@ -35,7 +35,6 @@ #include "scumm/debugger.h" #include "scumm/imuse/imuse.h" #include "scumm/object.h" -#include "scumm/player_v2.h" #include "scumm/scumm.h" #include "scumm/sound.h" @@ -87,7 +86,7 @@ ScummDebugger::ScummDebugger(ScummEngine *s) if (_vm->_game.id == GID_LOOM) DCmd_Register("drafts", WRAP_METHOD(ScummDebugger, Cmd_PrintDraft)); - if (_vm->_game.id == GID_MONKEY && Common::kPlatformSegaCD) + if (_vm->_game.id == GID_MONKEY && _vm->_game.platform == Common::kPlatformSegaCD) DCmd_Register("passcode", WRAP_METHOD(ScummDebugger, Cmd_Passcode)); DCmd_Register("loadgame", WRAP_METHOD(ScummDebugger, Cmd_LoadGame)); @@ -529,7 +528,7 @@ bool ScummDebugger::Cmd_Debug(int argc, const char **argv) { } else { DebugPrintf("Usage: debug [+CHANNEL|-CHANNEL]\n"); DebugPrintf("Enables or disables the given debug channel.\n"); - DebugPrintf("When used without parameters, lists all avaiable debug channels and their status.\n"); + DebugPrintf("When used without parameters, lists all available debug channels and their status.\n"); } return true; diff --git a/engines/scumm/detection.cpp b/engines/scumm/detection.cpp index 9010cb84c3..467282bd43 100644 --- a/engines/scumm/detection.cpp +++ b/engines/scumm/detection.cpp @@ -1198,12 +1198,7 @@ SaveStateDescriptor ScummMetaEngine::querySaveMetaInfos(const char *target, int int minutes = infos.time & 0xFF; desc.setSaveTime(hour, minutes); - - minutes = infos.playtime / 60; - hour = minutes / 60; - minutes %= 60; - - desc.setPlayTime(hour, minutes); + desc.setPlayTime(infos.playtime * 1000); } return desc; diff --git a/engines/scumm/detection_tables.h b/engines/scumm/detection_tables.h index 98fab9468a..a5542ca868 100644 --- a/engines/scumm/detection_tables.h +++ b/engines/scumm/detection_tables.h @@ -220,6 +220,7 @@ static const GameSettings gameVariantsTable[] = { {"zak", "V2", "v2", GID_ZAK, 2, 0, MDT_PCSPK | MDT_PCJR, 0, UNK, GUIO_NOSPEECH | GUIO_NOMIDI}, {"zak", "FM-TOWNS", 0, GID_ZAK, 3, 0, MDT_TOWNS, GF_OLD256 | GF_AUDIOTRACKS, Common::kPlatformFMTowns, GUIO_NOSPEECH | GUIO_NOMIDI | GUIO_MIDITOWNS}, + {"indy3", "EGA", "ega", GID_INDY3, 3, 0, MDT_PCSPK | MDT_PCJR | MDT_CMS | MDT_ADLIB, 0, UNK, GUIO_NOSPEECH | GUIO_NOMIDI}, {"indy3", "No AdLib", "ega", GID_INDY3, 3, 0, MDT_PCSPK | MDT_PCJR, 0, UNK, GUIO_NOSPEECH | GUIO_NOMIDI}, {"indy3", "VGA", "vga", GID_INDY3, 3, 0, MDT_PCSPK | MDT_PCJR | MDT_ADLIB, GF_OLD256 | GF_FEW_LOCALS, Common::kPlatformPC, GUIO_NOSPEECH | GUIO_NOMIDI}, @@ -243,10 +244,12 @@ static const GameSettings gameVariantsTable[] = { {"monkey", "FM-TOWNS", 0, GID_MONKEY, 5, 0, MDT_TOWNS, GF_AUDIOTRACKS, Common::kPlatformFMTowns, GUIO_NOSPEECH | GUIO_NOMIDI | GUIO_MIDITOWNS}, {"monkey", "SEGA", 0, GID_MONKEY, 5, 0, MDT_NONE, GF_AUDIOTRACKS, Common::kPlatformSegaCD, GUIO_NOSPEECH | GUIO_NOMIDI}, - {"monkey2", 0, 0, GID_MONKEY2, 5, 0, MDT_ADLIB | MDT_MIDI | MDT_PREFER_MT32, 0, UNK, GUIO_NOSPEECH}, + {"monkey2", "", 0, GID_MONKEY2, 5, 0, MDT_ADLIB | MDT_MIDI | MDT_PREFER_MT32, 0, UNK, GUIO_NOSPEECH}, + {"monkey2", "FM-TOWNS", 0, GID_MONKEY2, 5, 0, MDT_TOWNS | MDT_ADLIB | MDT_MIDI | MDT_PREFER_MT32, 0, Common::kPlatformFMTowns, GUIO_NOSPEECH}, - {"atlantis", "" , 0, GID_INDY4, 5, 0, MDT_ADLIB | MDT_MIDI | MDT_PREFER_MT32, 0, UNK, GUIO_NONE}, + {"atlantis", "", 0, GID_INDY4, 5, 0, MDT_ADLIB | MDT_MIDI | MDT_PREFER_MT32, 0, UNK, GUIO_NONE}, {"atlantis", "Floppy", 0, GID_INDY4, 5, 0, MDT_ADLIB | MDT_MIDI | MDT_PREFER_MT32, 0, UNK, GUIO_NOSPEECH}, + {"atlantis", "FM-TOWNS", 0, GID_INDY4, 5, 0, MDT_TOWNS | MDT_ADLIB | MDT_MIDI | MDT_PREFER_MT32, 0, Common::kPlatformFMTowns, GUIO_NONE}, {"tentacle", "", 0, GID_TENTACLE, 6, 0, MDT_ADLIB | MDT_MIDI | MDT_PREFER_GM, GF_USE_KEY, UNK, GUIO_NONE}, {"tentacle", "Floppy", 0, GID_TENTACLE, 6, 0, MDT_ADLIB | MDT_MIDI | MDT_PREFER_GM, GF_USE_KEY, UNK, GUIO_NOSPEECH}, @@ -367,7 +370,7 @@ static const GameSettings gameVariantsTable[] = { #ifdef USE_RGB_COLOR // Added 16bit color {"arttime", 0, 0, GID_HEGAME, 6, 99, MDT_NONE, GF_USE_KEY | GF_HE_LOCALIZED | GF_16BIT_COLOR, UNK, GUIO_NOLAUNCHLOAD | GUIO_NOMIDI}, - {"baseball2001", 0, 0, GID_HEGAME, 6, 99, MDT_NONE, GF_USE_KEY | GF_16BIT_COLOR, UNK, GUIO_NOLAUNCHLOAD | GUIO_NOMIDI}, + {"baseball2001", 0, 0, GID_BASEBALL2001, 6, 99, MDT_NONE, GF_USE_KEY | GF_16BIT_COLOR, UNK, GUIO_NOLAUNCHLOAD | GUIO_NOMIDI}, {"readtime", 0, 0, GID_HEGAME, 6, 99, MDT_NONE, GF_USE_KEY | GF_HE_LOCALIZED | GF_16BIT_COLOR, UNK, GUIO_NOLAUNCHLOAD | GUIO_NOMIDI}, {"SoccerMLS", 0, 0, GID_SOCCER, 6, 99, MDT_NONE, GF_USE_KEY | GF_HE_LOCALIZED | GF_16BIT_COLOR, UNK, GUIO_NOLAUNCHLOAD | GUIO_NOMIDI}, {"spyozon", 0, 0, GID_HEGAME, 6, 99, MDT_NONE, GF_USE_KEY | GF_HE_LOCALIZED | GF_16BIT_COLOR, UNK, GUIO_NOLAUNCHLOAD | GUIO_NOMIDI}, diff --git a/engines/scumm/dialogs.cpp b/engines/scumm/dialogs.cpp index 1e0bf6d4be..093a103cc8 100644 --- a/engines/scumm/dialogs.cpp +++ b/engines/scumm/dialogs.cpp @@ -45,7 +45,6 @@ #include "scumm/scumm.h" #include "scumm/imuse/imuse.h" #include "scumm/imuse_digi/dimuse.h" -#include "scumm/player_v2.h" #include "scumm/verbs.h" #include "sound/mididrv.h" #include "sound/mixer.h" @@ -473,14 +472,25 @@ void PauseDialog::handleKeyDown(Common::KeyState state) { } ConfirmDialog::ConfirmDialog(ScummEngine *scumm, int res) - : InfoDialog(scumm, res) { + : InfoDialog(scumm, res), _yesKey('y'), _noKey('n') { + + if (_message.lastChar() != ')') { + _yesKey = _message.lastChar(); + _message.deleteLastChar(); + + if (_yesKey >= 'A' && _yesKey <= 'Z') + _yesKey += 'a' - 'A'; + + _text->setLabel(_message); + reflowLayout(); + } } void ConfirmDialog::handleKeyDown(Common::KeyState state) { - if (state.keycode == Common::KEYCODE_n) { + if (state.keycode == Common::KEYCODE_n || state.ascii == _noKey) { setResult(0); close(); - } else if (state.keycode == Common::KEYCODE_y) { + } else if (state.keycode == Common::KEYCODE_y || state.ascii == _yesKey) { setResult(1); close(); } else diff --git a/engines/scumm/dialogs.h b/engines/scumm/dialogs.h index 41a8ec83c1..0e6e18905f 100644 --- a/engines/scumm/dialogs.h +++ b/engines/scumm/dialogs.h @@ -118,6 +118,9 @@ class ConfirmDialog : public InfoDialog { public: ConfirmDialog(ScummEngine *scumm, int res); virtual void handleKeyDown(Common::KeyState state); + +protected: + char _yesKey, _noKey; }; /** diff --git a/engines/scumm/file_nes.cpp b/engines/scumm/file_nes.cpp index 0caf63b410..cd761db81a 100644 --- a/engines/scumm/file_nes.cpp +++ b/engines/scumm/file_nes.cpp @@ -1077,7 +1077,7 @@ uint16 ScummNESFile::extractResource(Common::WriteStream *output, const Resource case NES_PREPLIST: len = res->length; - reslen += write_word(output, 0x002A); + reslen += write_word(output, 0x002A); reslen += write_byte(output, ' '); for (i = 1; i < 8; i++) diff --git a/engines/scumm/gfx.cpp b/engines/scumm/gfx.cpp index 3b8d9c296a..8daee06a88 100644 --- a/engines/scumm/gfx.cpp +++ b/engines/scumm/gfx.cpp @@ -51,7 +51,6 @@ static void copy8Col(byte *dst, int dstPitch, const byte *src, int height, uint8 static void clear8Col(byte *dst, int dstPitch, int height, uint8 bitDepth); static void ditherHerc(byte *src, byte *hercbuf, int srcPitch, int *x, int *y, int *width, int *height); -static void scale2x(byte *dst, int dstPitch, const byte *src, int srcPitch, int w, int h); struct StripTable { int offsets[160]; @@ -323,6 +322,18 @@ void ScummEngine::initScreens(int b, int h) { _res->nukeResource(rtBuffer, i + 5); } +#ifndef DISABLE_TOWNS_DUAL_LAYER_MODE + if (_townsScreen) { + if (!_townsClearLayerFlag && (h - b != _virtscr[kMainVirtScreen].h)) + _townsScreen->clearLayer(0); + + if (_game.id != GID_MONKEY) { + _textSurface.fillRect(Common::Rect(0, 0, _textSurface.w * _textSurfaceMultiplier, _textSurface.h * _textSurfaceMultiplier), 0); + _townsScreen->clearLayer(1); + } + } +#endif + if (!getResourceAddress(rtBuffer, 4)) { // Since the size of screen 3 is fixed, there is no need to reallocate // it if its size changed. @@ -611,16 +622,7 @@ void ScummEngine::drawStripToScreen(VirtScreen *vs, int x, int width, int top, i int m = _textSurfaceMultiplier; int vsPitch; int pitch = vs->pitch; - - if (_useCJKMode && _textSurfaceMultiplier == 2) { - scale2x(_fmtownsBuf, _screenWidth * m, (const byte *)src, vs->pitch, width, height); - src = _fmtownsBuf; - - vsPitch = _screenWidth * m - width * m; - - } else { - vsPitch = vs->pitch - width * vs->bytesPerPixel; - } + vsPitch = vs->pitch - width * vs->bytesPerPixel; if (_game.version < 7) { @@ -643,7 +645,13 @@ void ScummEngine::drawStripToScreen(VirtScreen *vs, int x, int width, int top, i #ifdef USE_ARM_GFX_ASM asmDrawStripToScreen(height, width, text, src, _compositeBuf, vs->pitch, width, _textSurface.pitch); #else - if (_bytesPerPixel == 2) { +#ifndef DISABLE_TOWNS_DUAL_LAYER_MODE + if (_game.platform == Common::kPlatformFMTowns) { + towns_drawStripToScreen(vs, x, y, x, top, width, height); + return; + } else +#endif + if (_bytesPerPixelOutput == 2) { const byte *srcPtr = (const byte *)src; const byte *textPtr = (byte *)_textSurface.getBasePtr(x * m, y * m); byte *dstPtr = _compositeBuf; @@ -824,28 +832,6 @@ void ditherHerc(byte *src, byte *hercbuf, int srcPitch, int *x, int *y, int *wid *height = dsty - *y; } -void scale2x(byte *dst, int dstPitch, const byte *src, int srcPitch, int w, int h) { - /* dst and dstPitch should both be even. So the use of (void *) in - * the following casts to avoid the unnecessary warning is valid. */ - uint16 *dstL1 = (uint16 *)(void *)dst; - uint16 *dstL2 = (uint16 *)(void *)(dst + dstPitch); - - const int dstAdd = dstPitch - w; - const int srcAdd = srcPitch - w; - - while (h--) { - for (int x = 0; x < w; ++x) { - uint16 col = *src++; - col |= col << 8; - *dstL1++ = col; - *dstL2++ = col; - } - dstL1 += dstAdd; dstL2 += dstAdd; - src += srcAdd; - } -} - - #pragma mark - #pragma mark --- Background buffers & charset mask --- #pragma mark - @@ -1017,7 +1003,7 @@ void ScummEngine::restoreBackground(Common::Rect rect, byte backColor) { VirtScreen *vs; byte *screenBuf; - if (rect.top < 0) + if (rect.top < 0) rect.top = 0; if (rect.left >= rect.right || rect.top >= rect.bottom) return; @@ -1027,30 +1013,51 @@ void ScummEngine::restoreBackground(Common::Rect rect, byte backColor) { if (rect.left > vs->w) return; - + // Convert 'rect' to local (virtual screen) coordinates rect.top -= vs->topline; rect.bottom -= vs->topline; rect.clip(vs->w, vs->h); + const int height = rect.height(); + const int width = rect.width(); + +#ifndef DISABLE_TOWNS_DUAL_LAYER_MODE + if (_game.platform == Common::kPlatformFMTowns && _game.id == GID_MONKEY && vs->number == kVerbVirtScreen && rect.bottom <= 154) + rect.right = 320; +#endif + markRectAsDirty(vs->number, rect, USAGE_BIT_RESTORED); screenBuf = vs->getPixels(rect.left, rect.top); - const int height = rect.height(); - const int width = rect.width(); - if (!height) return; if (vs->hasTwoBuffers && _currentRoom != 0 && isLightOn()) { blit(screenBuf, vs->pitch, vs->getBackPixels(rect.left, rect.top), vs->pitch, width, height, vs->bytesPerPixel); if (vs->number == kMainVirtScreen && _charset->_hasMask) { - byte *mask = (byte *)_textSurface.getBasePtr(rect.left, rect.top - _screenTop); - fill(mask, _textSurface.pitch, CHARSET_MASK_TRANSPARENCY, width, height, _textSurface.bytesPerPixel); +#ifndef DISABLE_TOWNS_DUAL_LAYER_MODE + if (_game.platform == Common::kPlatformFMTowns) { + byte *mask = (byte *)_textSurface.getBasePtr(rect.left * _textSurfaceMultiplier, (rect.top + vs->topline) * _textSurfaceMultiplier); + fill(mask, _textSurface.pitch, 0, width * _textSurfaceMultiplier, height * _textSurfaceMultiplier, _textSurface.bytesPerPixel); + } else +#endif + { + byte *mask = (byte *)_textSurface.getBasePtr(rect.left, rect.top - _screenTop); + fill(mask, _textSurface.pitch, CHARSET_MASK_TRANSPARENCY, width * _textSurfaceMultiplier, height * _textSurfaceMultiplier, _textSurface.bytesPerPixel); + } } } else { +#ifndef DISABLE_TOWNS_DUAL_LAYER_MODE + if (_game.platform == Common::kPlatformFMTowns) { + backColor |= (backColor << 4); + byte *mask = (byte *)_textSurface.getBasePtr(rect.left * _textSurfaceMultiplier, (rect.top + vs->topline) * _textSurfaceMultiplier); + fill(mask, _textSurface.pitch, backColor, width * _textSurfaceMultiplier, height * _textSurfaceMultiplier, _textSurface.bytesPerPixel); + } +#endif + if (_game.features & GF_16BIT_COLOR) fill(screenBuf, vs->pitch, _16BitPalette[backColor], width, height, vs->bytesPerPixel); else @@ -1102,7 +1109,16 @@ void ScummEngine::clearCharsetMask() { } void ScummEngine::clearTextSurface() { - fill((byte*)_textSurface.pixels, _textSurface.pitch, CHARSET_MASK_TRANSPARENCY, _textSurface.w, _textSurface.h, _textSurface.bytesPerPixel); +#ifndef DISABLE_TOWNS_DUAL_LAYER_MODE + if (_townsScreen) + _townsScreen->fillLayerRect(1, 0, 0, _textSurface.w, _textSurface.h, 0); +#endif + + fill((byte*)_textSurface.pixels, _textSurface.pitch, +#ifndef DISABLE_TOWNS_DUAL_LAYER_MODE + _game.platform == Common::kPlatformFMTowns ? 0 : +#endif + CHARSET_MASK_TRANSPARENCY, _textSurface.w, _textSurface.h, _textSurface.bytesPerPixel); } byte *ScummEngine::getMaskBuffer(int x, int y, int z) { @@ -1256,13 +1272,32 @@ void ScummEngine::drawBox(int x, int y, int x2, int y2, int color) { backbuff = vs->getPixels(x, y); bgbuff = vs->getBackPixels(x, y); - if (color == -1) { - if (vs->number != kMainVirtScreen) - error("can only copy bg to main window"); - blit(backbuff, vs->pitch, bgbuff, vs->pitch, width, height, vs->bytesPerPixel); - if (_charset->_hasMask) { - byte *mask = (byte *)_textSurface.getBasePtr(x * _textSurfaceMultiplier, (y - _screenTop) * _textSurfaceMultiplier); - fill(mask, _textSurface.pitch, CHARSET_MASK_TRANSPARENCY, width * _textSurfaceMultiplier, height * _textSurfaceMultiplier, _textSurface.bytesPerPixel); + // A check for -1 might be wrong in all cases since o5_drawBox() in its current form + // is definitely not capable of passing a parameter of -1 (color range is 0 - 255). + // Just to make sure I don't break anything I restrict the code change to FM-Towns + // version 5 games where this change is necessary to fix certain long standing bugs. + if (color == -1 +#ifndef DISABLE_TOWNS_DUAL_LAYER_MODE + || (color >= 254 && _game.platform == Common::kPlatformFMTowns && (_game.id == GID_MONKEY2 || _game.id == GID_INDY4)) +#endif + ) { +#ifndef DISABLE_TOWNS_DUAL_LAYER_MODE + if (_game.platform == Common::kPlatformFMTowns) { + if (color == 254) { + color = color; + towns_setupPalCycleField(x, y, x2, y2); + } + } else +#endif + { + if (vs->number != kMainVirtScreen) + error("can only copy bg to main window"); + + blit(backbuff, vs->pitch, bgbuff, vs->pitch, width, height, vs->bytesPerPixel); + if (_charset->_hasMask) { + byte *mask = (byte *)_textSurface.getBasePtr(x * _textSurfaceMultiplier, (y - _screenTop) * _textSurfaceMultiplier); + fill(mask, _textSurface.pitch, CHARSET_MASK_TRANSPARENCY, width * _textSurfaceMultiplier, height * _textSurfaceMultiplier, _textSurface.bytesPerPixel); + } } } else if (_game.heversion >= 72) { // Flags are used for different methods in HE games @@ -1293,10 +1328,22 @@ void ScummEngine::drawBox(int x, int y, int x2, int y2, int color) { fill(backbuff, vs->pitch, flags, width, height, vs->bytesPerPixel); } } else { - if (_game.features & GF_16BIT_COLOR) + if (_game.features & GF_16BIT_COLOR) { fill(backbuff, vs->pitch, _16BitPalette[color], width, height, vs->bytesPerPixel); - else + } else { +#ifndef DISABLE_TOWNS_DUAL_LAYER_MODE + if (_game.platform == Common::kPlatformFMTowns) { + color = ((color & 0x0f) << 4) | (color & 0x0f); + byte *mask = (byte *)_textSurface.getBasePtr(x * _textSurfaceMultiplier, (y - _screenTop + vs->topline) * _textSurfaceMultiplier); + fill(mask, _textSurface.pitch, color, width * _textSurfaceMultiplier, height * _textSurfaceMultiplier, _textSurface.bytesPerPixel); + + if (_game.id == GID_MONKEY2 || _game.id == GID_INDY4 || ((_game.id == GID_INDY3 || _game.id == GID_ZAK) && vs->number != kTextVirtScreen) || (_game.id == GID_LOOM && vs->number == kMainVirtScreen)) + return; + } +#endif + fill(backbuff, vs->pitch, color, width, height, vs->bytesPerPixel); + } } } @@ -1703,6 +1750,13 @@ void Gdi::drawBitmap(const byte *ptr, VirtScreen *vs, int x, const int y, const warning("Gdi::drawBitmap, strip drawn to %d below window bottom %d", y + height, vs->h); } +#ifndef DISABLE_TOWNS_DUAL_LAYER_MODE + if (_vm->_townsPaletteFlags & 2) { + int cx = (x - _vm->_screenStartStrip) << 3; + _vm->_textSurface.fillRect(Common::Rect(cx * _vm->_textSurfaceMultiplier, y * _vm->_textSurfaceMultiplier, (cx + width - 1) * _vm->_textSurfaceMultiplier, (y + height - 1) * _vm->_textSurfaceMultiplier), 0); + } +#endif + _vertStripNextInc = height * vs->pitch - 1 * vs->bytesPerPixel; _objectMode = (flag & dbObjectMode) == dbObjectMode; @@ -2403,7 +2457,7 @@ void ScummEngine::decodeNESBaseTiles() { } static const int v1MMNEScostTables[2][6] = { - /* desc lens offs data gfx pal */ + /* desc lens offs data gfx pal */ { 25, 27, 29, 31, 33, 35}, { 26, 28, 30, 32, 34, 36} }; @@ -3653,12 +3707,16 @@ void ScummEngine::fadeOut(int effect) { if (_game.version < 7) camera._last.x = camera._cur.x; +#ifndef DISABLE_TOWNS_DUAL_LAYER_MODE + if (_game.version == 3 && _game.platform == Common::kPlatformFMTowns) + _textSurface.fillRect(Common::Rect(0, vs->topline * _textSurfaceMultiplier, _textSurface.pitch, (vs->topline + vs->h) * _textSurfaceMultiplier), 0); +#endif + // TheDig can disable fadeIn(), and may call fadeOut() several times // successively. Disabling the _screenEffectFlag check forces the screen // to get cleared. This fixes glitches, at least, in the first cutscenes // when bypassed of FT and TheDig. if ((_game.version == 7 || _screenEffectFlag) && effect != 0) { - // Fill screen 0 with black memset(vs->getPixels(0, 0), 0, vs->pitch * vs->h); @@ -3679,6 +3737,10 @@ void ScummEngine::fadeOut(int effect) { // Just blit screen 0 to the display (i.e. display will be black) vs->setDirtyRange(0, vs->h); updateDirtyScreen(kMainVirtScreen); +#ifndef DISABLE_TOWNS_DUAL_LAYER_MODE + if (_townsScreen) + _townsScreen->update(); +#endif break; case 134: dissolveEffect(1, 1); @@ -3856,15 +3918,12 @@ void ScummEngine::dissolveEffect(int width, int height) { x = offsets[i] % vs->pitch; y = offsets[i] / vs->pitch; - if (_useCJKMode && _textSurfaceMultiplier == 2) { - int m = _textSurfaceMultiplier; - byte *dst = _fmtownsBuf + x * m + y * m * _screenWidth * m; - scale2x(dst, _screenWidth * m, vs->getPixels(x, y), vs->pitch, width, height); - - _system->copyRectToScreen(dst, _screenWidth * m, x * m, (y + vs->topline) * m, width * m, height * m); - } else { +#ifndef DISABLE_TOWNS_DUAL_LAYER_MODE + if (_game.platform == Common::kPlatformFMTowns) + towns_drawStripToScreen(vs, x, y + vs->topline, x, y, width, height); + else +#endif _system->copyRectToScreen(vs->getPixels(x, y), vs->pitch, x, y + vs->topline, width, height); - } if (++blits >= blits_before_refresh) { @@ -3904,23 +3963,21 @@ void ScummEngine::scrollEffect(int dir) { y = 1 + step; while (y < vs->h) { moveScreen(0, -step, vs->h); - - src = vs->getPixels(0, y - step); - if (_useCJKMode && m == 2) { - int x1 = 0, y1 = vs->h - step; - byte *dst = _fmtownsBuf + x1 * m + y1 * m * _screenWidth * m; - scale2x(dst, _screenWidth * m, src, vs->pitch, vs->w, step); - src = dst; - vsPitch = _screenWidth * 2; +#ifndef DISABLE_TOWNS_DUAL_LAYER_MODE + if (_townsScreen) { + towns_drawStripToScreen(vs, 0, vs->topline + vs->h - step, 0, y - step, vs->w, step); + } else +#endif + { + src = vs->getPixels(0, y - step); + _system->copyRectToScreen(src, + vsPitch, + 0, (vs->h - step) * m, + vs->w * m, step * m); + _system->updateScreen(); } - - _system->copyRectToScreen(src, - vsPitch, - 0 * m, (vs->h - step) * m, - vs->w * m, step * m); - _system->updateScreen(); + waitForTimer(delay); - y += step; } break; @@ -3929,21 +3986,21 @@ void ScummEngine::scrollEffect(int dir) { y = 1 + step; while (y < vs->h) { moveScreen(0, step, vs->h); - src = vs->getPixels(0, vs->h - y); - if (_useCJKMode && m == 2) { - int x1 = 0, y1 = 0; - byte *dst = _fmtownsBuf + x1 * m + y1 * m * _screenWidth * m; - scale2x(dst, _screenWidth * m, src, vs->pitch, vs->w, step); - src = dst; - vsPitch = _screenWidth * 2; +#ifndef DISABLE_TOWNS_DUAL_LAYER_MODE + if (_townsScreen) { + towns_drawStripToScreen(vs, 0, vs->topline, 0, vs->h - y, vs->w, step); + } else +#endif + { + src = vs->getPixels(0, vs->h - y); + _system->copyRectToScreen(src, + vsPitch, + 0, 0, + vs->w * m, step * m); + _system->updateScreen(); } - _system->copyRectToScreen(src, - vsPitch, - 0, 0, - vs->w * m, step * m); - _system->updateScreen(); + waitForTimer(delay); - y += step; } break; @@ -3952,21 +4009,22 @@ void ScummEngine::scrollEffect(int dir) { x = 1 + step; while (x < vs->w) { moveScreen(-step, 0, vs->h); - src = vs->getPixels(x - step, 0); - if (_useCJKMode && m == 2) { - int x1 = vs->w - step, y1 = 0; - byte *dst = _fmtownsBuf + x1 * m + y1 * m * _screenWidth * m; - scale2x(dst, _screenWidth * m, src, vs->pitch, step, vs->h); - src = dst; - vsPitch = _screenWidth * 2; + +#ifndef DISABLE_TOWNS_DUAL_LAYER_MODE + if (_townsScreen) { + towns_drawStripToScreen(vs, vs->w - step, vs->topline, x - step, 0, step, vs->h); + } else +#endif + { + src = vs->getPixels(x - step, 0); + _system->copyRectToScreen(src, + vsPitch, + (vs->w - step) * m, 0, + step * m, vs->h * m); + _system->updateScreen(); } - _system->copyRectToScreen(src, - vsPitch, - (vs->w - step) * m, 0, - step * m, vs->h * m); - _system->updateScreen(); - waitForTimer(delay); + waitForTimer(delay); x += step; } break; @@ -3975,21 +4033,22 @@ void ScummEngine::scrollEffect(int dir) { x = 1 + step; while (x < vs->w) { moveScreen(step, 0, vs->h); - src = vs->getPixels(vs->w - x, 0); - if (_useCJKMode && m == 2) { - int x1 = 0, y1 = 0; - byte *dst = _fmtownsBuf + x1 * m + y1 * m * _screenWidth * m; - scale2x(dst, _screenWidth * m, src, vs->pitch, step, vs->h); - src = dst; - vsPitch = _screenWidth * 2; - } - _system->copyRectToScreen(src, - vsPitch, - 0, 0, - step, vs->h); - _system->updateScreen(); - waitForTimer(delay); +#ifndef DISABLE_TOWNS_DUAL_LAYER_MODE + if (_townsScreen) { + towns_drawStripToScreen(vs, 0, vs->topline, vs->w - x, 0, step, vs->h); + } else +#endif + { + src = vs->getPixels(vs->w - x, 0); + _system->copyRectToScreen(src, + vsPitch, + 0, 0, + step, vs->h); + _system->updateScreen(); + } + + waitForTimer(delay); x += step; } break; diff --git a/engines/scumm/gfx.h b/engines/scumm/gfx.h index cdb473a67c..c6062ef9be 100644 --- a/engines/scumm/gfx.h +++ b/engines/scumm/gfx.h @@ -26,6 +26,9 @@ #ifndef SCUMM_GFX_H #define SCUMM_GFX_H +#include "common/system.h" +#include "common/list.h" + #include "graphics/surface.h" namespace Scumm { @@ -421,6 +424,66 @@ public: }; #endif +#ifndef DISABLE_TOWNS_DUAL_LAYER_MODE +// Helper class for FM-Towns output (required for specific hardware effects like +// switching graphics layers on and off). +class TownsScreen { +public: + TownsScreen(OSystem *system, int width, int height, int bpp); + ~TownsScreen(); + + void setupLayer(int layer, int width, int height, int numCol, void *srcPal = 0); + void clearLayer(int layer); + void fillLayerRect(int layer, int x, int y, int w, int h, int col); + //void copyRectToLayer(int layer, int x, int y, int w, int h, const uint8 *src); + + uint8 *getLayerPixels(int layer, int x, int y); + int getLayerPitch(int layer); + int getLayerHeight(int layer); + int getLayerBpp(int layer); + int getLayerScaleW(int layer); + int getLayerScaleH(int layer); + + void addDirtyRect(int x, int y, int w, int h); + void toggleLayers(int flag); + void update(); + +private: + void updateOutputBuffer(); + void outputToScreen(); + uint16 calc16BitColor(const uint8 *palEntry); + + struct TownsScreenLayer { + uint8 *pixels; + uint8 *palette; + int pitch; + int height; + int bpp; + int numCol; + uint8 scaleW; + uint8 scaleH; + bool onBottom; + bool enabled; + bool ready; + + uint16 *bltInternX; + uint8 **bltInternY; + uint16 *bltTmpPal; + } _layers[2]; + + uint8 *_outBuffer; + + int _height; + int _width; + int _pitch; + int _bpp; + + int _numDirtyRects; + Common::List<Common::Rect> _dirtyRects; + OSystem *_system; +}; +#endif // DISABLE_TOWNS_DUAL_LAYER_MODE + } // End of namespace Scumm #endif diff --git a/engines/scumm/gfx_towns.cpp b/engines/scumm/gfx_towns.cpp new file mode 100644 index 0000000000..33b1779b0b --- /dev/null +++ b/engines/scumm/gfx_towns.cpp @@ -0,0 +1,522 @@ +/* ScummVM - Graphic Adventure Engine + * + * ScummVM is the legal property of its developers, whose names + * are too numerous to list here. Please refer to the COPYRIGHT + * file distributed with this source distribution. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * $URL$ + * $Id$ + * + */ + +#include "common/endian.h" + +#include "scumm/scumm.h" +#include "scumm/charset.h" +#include "scumm/util.h" +#include "scumm/resource.h" + +#ifndef DISABLE_TOWNS_DUAL_LAYER_MODE + +namespace Scumm { + +void ScummEngine::towns_drawStripToScreen(VirtScreen *vs, int dstX, int dstY, int srcX, int srcY, int width, int height) { + if (width <= 0 || height <= 0) + return; + + assert(_textSurface.pixels); + + int m = _textSurfaceMultiplier; + + uint8 *src1 = vs->getPixels(srcX, srcY); + uint8 *src2 = (uint8*)_textSurface.getBasePtr(srcX * m, (srcY + vs->topline - _screenTop) * m); + uint8 *dst1 = _townsScreen->getLayerPixels(0, dstX, dstY); + uint8 *dst2 = _townsScreen->getLayerPixels(1, dstX * m, dstY * m); + + int dp1 = _townsScreen->getLayerPitch(0) - width * _townsScreen->getLayerBpp(0); + int dp2 = _townsScreen->getLayerPitch(1) - width * m * _townsScreen->getLayerBpp(1); + int sp1 = vs->pitch - (width * vs->bytesPerPixel); + int sp2 = _textSurface.pitch - width * m; + + if (vs->number == kMainVirtScreen || _game.id == GID_INDY3 || _game.id == GID_ZAK) { + for (int h = 0; h < height; ++h) { + if (_bytesPerPixelOutput == 2) { + for (int w = 0; w < width; ++w) { + *(uint16*)dst1 = _16BitPalette[*src1++]; + dst1 += _bytesPerPixelOutput; + } + + src1 += sp1; + dst1 += dp1; + } else { + memcpy(dst1, src1, width); + src1 += vs->pitch; + dst1 += _townsScreen->getLayerPitch(0); + } + + for (int sH = 0; sH < m; ++sH) { + memcpy(dst2, src2, width * m); + src2 += _textSurface.pitch; + dst2 += _townsScreen->getLayerPitch(1); + } + } + } else { + dst1 = dst2; + for (int h = 0; h < height; ++h) { + for (int w = 0; w < width; ++w) { + uint8 t = (*src1++) & 0x0f; + memset(dst1, (t << 4) | t, m); + dst1 += m; + } + + dst1 = dst2; + uint8 *src3 = src2; + + if (m == 2) { + dst2 += _townsScreen->getLayerPitch(1); + src3 += _townsScreen->getLayerPitch(1); + } + + for (int w = 0; w < width * m; ++w) { + *dst2++ = (*src3 | (*dst1 & _townsLayer2Mask[*src3])); + *dst1 = (*src2 | (*dst1 & _townsLayer2Mask[*src2])); + src2++; + src3++; + dst1++; + } + + src1 += sp1; + src2 = src3 + sp2; + dst1 = dst2 + dp2; + dst2 += dp2; + } + } + + _townsScreen->addDirtyRect(dstX * m, dstY * m, width * m, height * m); +} + +bool ScummEngine::towns_isRectInStringBox(int x1, int y1, int x2, int y2) { + if (_game.platform == Common::kPlatformFMTowns && _charset->_hasMask && y1 <= _curStringRect.bottom && x1 <= _curStringRect.right && y2 >= _curStringRect.top && x2 >= _curStringRect.left) + return true; + return false; +} + +void ScummEngine::towns_restoreCharsetBg() { + if (_curStringRect.left != -1) { + restoreBackground(_curStringRect, 0); + _curStringRect.left = -1; + _charset->_hasMask = false; + _nextLeft = _string[0].xpos; + } + + _nextLeft = _string[0].xpos; + _nextTop = _string[0].ypos; +} + +#ifdef USE_RGB_COLOR +void ScummEngine::towns_setPaletteFromPtr(const byte *ptr, int numcolor) { + setPaletteFromPtr(ptr, numcolor); + + if (_game.version == 5) + towns_setTextPaletteFromPtr(_currentPalette); + + _townsOverrideShadowColor = 1; + int m = 48; + for (int i = 1; i < 16; ++i) { + int val = _currentPalette[i * 3] + _currentPalette[i * 3 + 1] + _currentPalette[i * 3 + 2]; + if (m > val) { + _townsOverrideShadowColor = i; + m = val; + } + } +} + +void ScummEngine::towns_setTextPaletteFromPtr(const byte *ptr) { + memcpy(_textPalette, ptr, 48); +} +#endif + +void ScummEngine::towns_setupPalCycleField(int x1, int y1, int x2, int y2) { + if (_numCyclRects >= 10) + return; + _cyclRects[_numCyclRects].left = x1; + _cyclRects[_numCyclRects].top = y1; + _cyclRects[_numCyclRects].right = x2; + _cyclRects[_numCyclRects].bottom = y2; + _numCyclRects++; + _townsPaletteFlags |= 1; +} + +void ScummEngine::towns_processPalCycleField() { + for (int i = 0; i < _numCyclRects; i++) { + int x1 = _cyclRects[i].left - _virtscr[kMainVirtScreen].xstart; + int x2 = _cyclRects[i].right - _virtscr[kMainVirtScreen].xstart; + if (x1 < 0) + x1 = 0; + if (x2 > 320) + x2 = 320; + if (x2 > 0) + markRectAsDirty(kMainVirtScreen, x1, x2, _cyclRects[i].top, _cyclRects[i].bottom); + } +} + +void ScummEngine::towns_resetPalCycleFields() { + _numCyclRects = 0; + _townsPaletteFlags &= ~1; +} + +const uint8 ScummEngine::_townsLayer2Mask[] = { + 0xFF, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, + 0x0F, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x0F, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x0F, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x0F, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x0F, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x0F, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x0F, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x0F, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x0F, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x0F, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x0F, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x0F, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x0F, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x0F, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x0F, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 +}; + +#define DIRTY_RECTS_MAX 20 +#define FULL_REDRAW (DIRTY_RECTS_MAX + 1) + +TownsScreen::TownsScreen(OSystem *system, int width, int height, int bpp) : + _system(system), _width(width), _height(height), _bpp(bpp), _pitch(width * bpp) { + memset(&_layers[0], 0, sizeof(TownsScreenLayer)); + memset(&_layers[1], 0, sizeof(TownsScreenLayer)); + _outBuffer = new byte[_pitch * _height]; + memset(_outBuffer, 0, _pitch * _height); + + setupLayer(0, width, height, 256); +} + +TownsScreen::~TownsScreen() { + delete[] _layers[0].pixels; + delete[] _layers[1].pixels; + delete[] _layers[0].bltInternX; + delete[] _layers[1].bltInternX; + delete[] _layers[0].bltInternY; + delete[] _layers[1].bltInternY; + delete[] _layers[0].bltTmpPal; + delete[] _layers[1].bltTmpPal; + delete[] _outBuffer; + _dirtyRects.clear(); +} + +void TownsScreen::setupLayer(int layer, int width, int height, int numCol, void *pal) { + if (layer < 0 || layer > 1) + return; + + TownsScreenLayer *l = &_layers[layer]; + + if (numCol >> 15) + error("TownsScreen::setupLayer(): No more than 32767 colors supported."); + + if (width > _width || height > _height) + error("TownsScreen::setupLayer(): Layer width/height must be equal or less than screen width/height"); + + l->scaleW = _width / width; + l->scaleH = _height / height; + + if ((float)l->scaleW != ((float)_width / (float)width) || (float)l->scaleH != ((float)_height / (float)height)) + error("TownsScreen::setupLayer(): Layer width/height must be equal or an EXACT half, third, etc. of screen width/height.\n More complex aspect ratio scaling is not supported."); + + if (width <= 0 || height <= 0 || numCol < 16) + error("TownsScreen::setupLayer(): Invalid width/height/number of colors setting."); + + l->height = height; + l->numCol = numCol; + l->bpp = ((numCol - 1) & 0xff00) ? 2 : 1; + l->pitch = width * l->bpp; + l->palette = (uint8*)pal; + + if (l->palette && _bpp == 1) + warning("TownsScreen::setupLayer(): Layer palette usage requires 15 bit graphics setting.\nLayer palette will be ignored."); + + delete[] l->pixels; + l->pixels = new uint8[l->pitch * l->height]; + assert(l->pixels); + memset(l->pixels, 0, l->pitch * l->height); + + // build offset tables to speed up merging/scaling layers + delete[] l->bltInternX; + l->bltInternX = new uint16[_width]; + for (int i = 0; i < _width; ++i) + l->bltInternX[i] = (i / l->scaleW) * l->bpp; + + delete[] l->bltInternY; + l->bltInternY = new uint8*[_height]; + for (int i = 0; i < _height; ++i) + l->bltInternY[i] = l->pixels + (i / l->scaleH) * l->pitch; + + delete[] l->bltTmpPal; + l->bltTmpPal = (l->bpp == 1 && _bpp == 2) ? new uint16[l->numCol] : 0; + + l->enabled = true; + l->onBottom = (!layer || !_layers[0].enabled); + l->ready = true; +} + +void TownsScreen::clearLayer(int layer) { + if (layer < 0 || layer > 1) + return; + + TownsScreenLayer *l = &_layers[layer]; + if (!l->ready) + return; + + memset(l->pixels, 0, l->pitch * l->height); + _dirtyRects.push_back(Common::Rect(_width - 1, _height - 1)); + _numDirtyRects = FULL_REDRAW; +} + + +void TownsScreen::fillLayerRect(int layer, int x, int y, int w, int h, int col) { + if (layer < 0 || layer > 1 || w <= 0 || h <= 0) + return; + + TownsScreenLayer *l = &_layers[layer]; + if (!l->ready) + return; + + assert(x >= 0 && y >= 0 && ((x + w) * l->bpp) <= (l->pitch) && (y + h) <= (l->height)); + + uint8 *pos = l->pixels + y * l->pitch + x * l->bpp; + + for (int i = 0; i < h; ++i) { + if (l->bpp == 2) { + for (int ii = 0; ii < w; ++ii) { + *(uint16*)pos = col; + pos += 2; + } + pos += (l->pitch - w * 2); + } else { + memset(pos, col, w); + pos += l->pitch; + } + } + addDirtyRect(x * l->scaleW, y * l->scaleH, w * l->scaleW, h * l->scaleH); +} + +uint8 *TownsScreen::getLayerPixels(int layer, int x, int y) { + if (layer < 0 || layer > 1) + return 0; + + TownsScreenLayer *l = &_layers[layer]; + if (!l->ready) + return 0; + + return l->pixels + y * l->pitch + x * l->bpp; +} + +int TownsScreen::getLayerPitch(int layer) { + if (layer >= 0 && layer < 2) + return _layers[layer].pitch; + return 0; +} + +int TownsScreen::getLayerHeight(int layer) { + if (layer >= 0 && layer < 2) + return _layers[layer].height; + return 0; +} + +int TownsScreen::getLayerBpp(int layer) { + if (layer >= 0 && layer < 2) + return _layers[layer].bpp; + return 0; +} + +int TownsScreen::getLayerScaleW(int layer) { + if (layer >= 0 && layer < 2) + return _layers[layer].scaleW; + return 0; +} + +int TownsScreen::getLayerScaleH(int layer) { + if (layer >= 0 && layer < 2) + return _layers[layer].scaleH; + return 0; +} + +void TownsScreen::addDirtyRect(int x, int y, int w, int h) { + if (w <= 0 || h <= 0 || _numDirtyRects > DIRTY_RECTS_MAX) + return; + + if (_numDirtyRects == DIRTY_RECTS_MAX) { + // full redraw + _dirtyRects.clear(); + _dirtyRects.push_back(Common::Rect(_width - 1, _height - 1)); + _numDirtyRects++; + return; + } + + int x2 = x + w - 1; + int y2 = y + h - 1; + + assert(x >= 0 && y >= 0 && x2 <= _width && y2 <= _height); + + bool skip = false; + for (Common::List<Common::Rect>::iterator r = _dirtyRects.begin(); r != _dirtyRects.end(); ++r) { + // Try to merge new rect with an existing rect (only once, since trying to merge + // more than one overlapping rect would be causing more overhead than doing any good). + if (x > r->left && x < r->right && y > r->top && y < r->bottom) { + x = r->left; + y = r->top; + skip = true; + } + + if (x2 > r->left && x2 < r->right && y > r->top && y < r->bottom) { + x2 = r->right; + y = r->top; + skip = true; + } + + if (x2 > r->left && x2 < r->right && y2 > r->top && y2 < r->bottom) { + x2 = r->right; + y2 = r->bottom; + skip = true; + } + + if (x > r->left && x < r->right && y2 > r->top && y2 < r->bottom) { + x = r->left; + y2 = r->bottom; + skip = true; + } + + if (skip) { + r->left = x; + r->top = y; + r->right = x2; + r->bottom = y2; + break; + } + } + + if (!skip) { + _dirtyRects.push_back(Common::Rect(x, y, x2, y2)); + _numDirtyRects++; + } +} + +void TownsScreen::toggleLayers(int flag) { + if (flag < 0 || flag > 3) + return; + + for (int i = 0; i < 2; ++i) { + _layers[i].enabled = (flag & (i + 1)) ? true : false; + _layers[i].onBottom = (!i || !_layers[0].enabled); + } + + _dirtyRects.clear(); + _dirtyRects.push_back(Common::Rect(_width - 1, _height - 1)); + _numDirtyRects = FULL_REDRAW; + + memset(_outBuffer, 0, _pitch * _height); + updateOutputBuffer(); + outputToScreen(); + + _system->updateScreen(); +} + +void TownsScreen::update() { + updateOutputBuffer(); + outputToScreen(); +} + +void TownsScreen::updateOutputBuffer() { + for (Common::List<Common::Rect>::iterator r = _dirtyRects.begin(); r != _dirtyRects.end(); ++r) { + for (int i = 0; i < 2; i++) { + + TownsScreenLayer *l = &_layers[i]; + if (!l->enabled || !l->ready) + continue; + + uint8 *dst = _outBuffer + r->top * _pitch + r->left * _bpp; + int ptch = _pitch - (r->right - r->left + 1) * _bpp; + + if (_bpp == 2 && l->bpp == 1) { + for (int ic = 0; ic < l->numCol; ic++) + l->bltTmpPal[ic] = calc16BitColor(&l->palette[ic * 3]); + } + + for (int y = r->top; y <= r->bottom; ++y) { + if (l->bpp == _bpp && l->scaleW == 1 && l->onBottom) { + memcpy(dst, l->bltInternY[y] + l->bltInternX[r->left], (r->right + 1 - r->left) * _bpp); + dst += _pitch; + + } else if (_bpp == 2) { + for (int x = r->left; x <= r->right; ++x) { + uint8 *src = l->bltInternY[y] + l->bltInternX[x]; + if (l->bpp == 1) { + uint8 col = *src; + if (col || l->onBottom) { + if (l->numCol == 16) + col = (col >> 4) & (col & 0x0f); + *(uint16*)dst = l->bltTmpPal[col]; + } + } else { + *(uint16*)dst = *(uint16*)src; + } + dst += 2; + } + dst += ptch; + + } else { + for (int x = r->left; x <= r->right; ++x) { + uint8 col = *(l->bltInternY[y] + l->bltInternX[x]); + if (col || l->onBottom) { + if (l->numCol == 16) + col = (col >> 4) & (col & 0x0f); + *dst = col; + } + dst++; + } + dst += ptch; + } + } + } + } +} + +void TownsScreen::outputToScreen() { + for (Common::List<Common::Rect>::iterator i = _dirtyRects.begin(); i != _dirtyRects.end(); ++i) + _system->copyRectToScreen(_outBuffer + i->top * _pitch + i->left * _bpp, _pitch, i->left, i->top, i->right - i->left + 1, i->bottom - i->top + 1); + _dirtyRects.clear(); + _numDirtyRects = 0; +} + +uint16 TownsScreen::calc16BitColor(const uint8 *palEntry) { + uint16 ar = (palEntry[0] & 0xf8) << 7; + uint16 ag = (palEntry[1] & 0xf8) << 2; + uint16 ab = (palEntry[2] >> 3); + uint16 col = ar | ag | ab; + return col; +} + +#undef DIRTY_RECTS_MAX +#undef FULL_REDRAW + +} // End of namespace Scumm + +#endif // DISABLE_TOWNS_DUAL_LAYER_MODE diff --git a/engines/scumm/he/logic_he.cpp b/engines/scumm/he/logic_he.cpp index add9b982e2..36aeaf4a3c 100644 --- a/engines/scumm/he/logic_he.cpp +++ b/engines/scumm/he/logic_he.cpp @@ -232,7 +232,7 @@ int32 LogicHErace::op_1101(int32 *args) { int32 retval; float temp; - temp = args[0] / _userData[532]; + temp = args[0] / _userData[532]; if (_userData[519] != temp) { _userData[519] = temp; op_sub3(temp); @@ -955,6 +955,30 @@ int LogicHEsoccer::op_1021(int32 *args) { } /*********************** + * Backyard Baseball 2001 + * + */ + +int LogicHEbaseball2001::versionID() { + return 1; +} + +int32 LogicHEbaseball2001::dispatch(int op, int numArgs, int32 *args) { + int res = 0; + + switch (op) { + case 3001: + // Check network status + break; + + default: + LogicHE::dispatch(op, numArgs, args); + } + + return res; +} + +/*********************** * Backyard Basketball * */ diff --git a/engines/scumm/he/logic_he.h b/engines/scumm/he/logic_he.h index 7dd141c5b1..ab952abd5e 100644 --- a/engines/scumm/he/logic_he.h +++ b/engines/scumm/he/logic_he.h @@ -133,6 +133,14 @@ private: int op_1021(int32 *args); }; +class LogicHEbaseball2001 : public LogicHE { +public: + LogicHEbaseball2001(ScummEngine_v90he *vm) : LogicHE(vm) {} + + int versionID(); + int32 dispatch(int op, int numArgs, int32 *args); +}; + class LogicHEbasketball : public LogicHE { public: LogicHEbasketball(ScummEngine_v90he *vm) : LogicHE(vm) {} diff --git a/engines/scumm/he/palette_he.cpp b/engines/scumm/he/palette_he.cpp index 6ef68d981e..ad3f90b8eb 100644 --- a/engines/scumm/he/palette_he.cpp +++ b/engines/scumm/he/palette_he.cpp @@ -203,8 +203,8 @@ void ScummEngine_v90he::setHEPaletteFromImage(int palSlot, int resId, int state) uint8 *data = getResourceAddress(rtImage, resId); assert(data); const uint8 *rgbs = findWrappedBlock(MKID_BE('RGBS'), data, state, 0); - assert(rgbs); - setHEPaletteFromPtr(palSlot, rgbs); + if (rgbs) + setHEPaletteFromPtr(palSlot, rgbs); } void ScummEngine_v90he::setHEPaletteFromRoom(int palSlot, int resId, int state) { diff --git a/engines/scumm/he/resource_he.cpp b/engines/scumm/he/resource_he.cpp index c259c3ffd2..b42441ceaf 100644 --- a/engines/scumm/he/resource_he.cpp +++ b/engines/scumm/he/resource_he.cpp @@ -23,7 +23,6 @@ * */ - #include "scumm/scumm.h" #include "scumm/file.h" #include "scumm/he/intern_he.h" @@ -40,9 +39,13 @@ namespace Scumm { +#if defined(SCUMM_LITTLE_ENDIAN) +#define LE16(x) +#define LE32(x) +#elif defined(SCUMM_BIG_ENDIAN) #define LE16(x) ((x) = TO_LE_16(x)) #define LE32(x) ((x) = TO_LE_32(x)) - +#endif ResExtractor::ResExtractor(ScummEngine_v70he *scumm) : _vm(scumm) { @@ -207,9 +210,9 @@ int Win32ResExtractor::extractResource_(const char *resType, char *resName, byte } -/* res_type_id_to_string: - * Translate a numeric resource type to it's corresponding string type. - * (For informative-ness.) +/** + * Translate a numeric resource type to it's corresponding string type. + * (For informative-ness.) */ const char *Win32ResExtractor::res_type_id_to_string(int id) { if (id == 241) @@ -219,9 +222,9 @@ const char *Win32ResExtractor::res_type_id_to_string(int id) { return NULL; } -/* res_type_string_to_id: - * Translate a resource type string to integer. - * (Used to convert the --type option.) +/** + * Translate a resource type string to integer. + * (Used to convert the --type option.) */ const char *Win32ResExtractor::res_type_string_to_id(const char *type) { static const char *res_type_ids[] = { @@ -242,22 +245,18 @@ const char *Win32ResExtractor::res_type_string_to_id(const char *type) { return type; } -/* return the resource id quoted if it's a string, otherwise just return it */ -char *Win32ResExtractor::WinResource::get_resource_id_quoted() { - // FIXME: Using a static var here is EVIL and in fact, broken when - // used multiple times in a row, e.g. in a single call to printf() - // or debug()... which is in fact how we use this function... :-) - static char tmp[WINRES_ID_MAXLEN+2]; - +/** + * Return the resource id quoted if it is a string, otherwise (i.e. if + * it is numeric) just return it. + */ +Common::String Win32ResExtractor::WinResource::getQuotedResourceId() const { if (numeric_id || id[0] == '\0') return id; - - sprintf(tmp, "'%s'", id); - return tmp; + return '"' + Common::String(id) + '"'; } int Win32ResExtractor::extract_resources(WinLibrary *fi, WinResource *wr, - WinResource *type_wr, WinResource *name_wr, + WinResource *type_wr, WinResource *name_wr, WinResource *lang_wr, byte **data) { int size; bool free_it; @@ -281,19 +280,21 @@ int Win32ResExtractor::extract_resources(WinLibrary *fi, WinResource *wr, if ((id = strtol(type_wr->id, 0, 10)) != 0) type = res_type_id_to_string(id); - debugC(DEBUG_RESOURCE, "extractCursor(). Found cursor name: %s%s%s [size=%d]", - name_wr->get_resource_id_quoted(), - (lang_wr->id[0] != '\0' ? " language: " : ""), - lang_wr->get_resource_id_quoted(), size); - + if (lang_wr != NULL && lang_wr->id[0] != '\0') { + debugC(DEBUG_RESOURCE, "extractCursor(). Found cursor name: %s language: %s [size=%d]", + name_wr->getQuotedResourceId().c_str(), lang_wr->getQuotedResourceId().c_str(), size); + } else { + debugC(DEBUG_RESOURCE, "extractCursor(). Found cursor name: %s [size=%d]", + name_wr->getQuotedResourceId().c_str(), size); + } return size; } -/* extract_resource: - * Extract a resource, returning pointer to data. +/** + * Extract a resource, returning pointer to data. */ byte *Win32ResExtractor::extract_resource(WinLibrary *fi, WinResource *wr, int *size, - bool *free_it, char *type, char *lang, bool raw) { + bool *free_it, char *type, char *lang, bool raw) { char *str; int32 intval; @@ -320,20 +321,20 @@ byte *Win32ResExtractor::extract_resource(WinLibrary *fi, WinResource *wr, int * return NULL; } -/* extract_group_icon_resource: - * Create a complete RT_GROUP_ICON resource, that can be written to - * an `.ico' file without modifications. Returns an allocated - * memory block that should be freed with free() once used. +/** + * Create a complete RT_GROUP_ICON resource, that can be written to + * an `.ico' file without modifications. Returns an allocated + * memory block that should be freed with free() once used. * - * `root' is the offset in file that specifies the resource. - * `base' is the offset that string pointers are calculated from. - * `ressize' should point to an integer variable where the size of - * the returned memory block will be placed. - * `is_icon' indicates whether resource to be extracted is icon - * or cursor group. + * `root' is the offset in file that specifies the resource. + * `base' is the offset that string pointers are calculated from. + * `ressize' should point to an integer variable where the size of + * the returned memory block will be placed. + * `is_icon' indicates whether resource to be extracted is icon + * or cursor group. */ byte *Win32ResExtractor::extract_group_icon_cursor_resource(WinLibrary *fi, WinResource *wr, char *lang, - int *ressize, bool is_icon) { + int *ressize, bool is_icon) { Win32CursorIconDir *icondir; Win32CursorIconFileDir *fileicondir; byte *memory; @@ -374,20 +375,20 @@ byte *Win32ResExtractor::extract_group_icon_cursor_resource(WinLibrary *fi, WinR } if (get_resource_entry(fi, fwr, &iconsize) != NULL) { - if (iconsize == 0) { + if (iconsize == 0) { debugC(DEBUG_RESOURCE, "%s: icon resource `%s' is empty, skipping", _fileName.c_str(), name); skipped++; continue; - } - if ((uint32)iconsize != FROM_LE_32(icondir->entries[c].bytes_in_res)) { + } + if ((uint32)iconsize != FROM_LE_32(icondir->entries[c].bytes_in_res)) { debugC(DEBUG_RESOURCE, "%s: mismatch of size in icon resource `%s' and group (%d != %d)", _fileName.c_str(), name, iconsize, FROM_LE_32(icondir->entries[c].bytes_in_res)); - } - size += iconsize; /* size += FROM_LE_32(icondir->entries[c].bytes_in_res); */ + } + size += iconsize; /* size += FROM_LE_32(icondir->entries[c].bytes_in_res); */ - /* cursor resources have two additional WORDs that contain - * hotspot info */ - if (!is_icon) + /* cursor resources have two additional WORDs that contain + * hotspot info */ + if (!is_icon) size -= sizeof(uint16)*2; } } @@ -428,8 +429,8 @@ byte *Win32ResExtractor::extract_group_icon_cursor_resource(WinLibrary *fi, WinR return NULL; } if (size == 0) { - skipped++; - continue; + skipped++; + continue; } /* copy ICONDIRENTRY (not including last dwImageOffset) */ @@ -465,10 +466,10 @@ byte *Win32ResExtractor::extract_group_icon_cursor_resource(WinLibrary *fi, WinR return memory; } -/* check_offset: - * Check if a chunk of data (determined by offset and size) - * is within the bounds of the WinLibrary file. - * Usually not called directly. +/** + * Check if a chunk of data (determined by offset and size) + * is within the bounds of the WinLibrary file. + * Usually not called directly. */ bool Win32ResExtractor::check_offset(byte *memory, int total_size, const char *name, void *offset, int size) { int need_size = (int)((byte *)offset - memory + size); @@ -485,8 +486,8 @@ bool Win32ResExtractor::check_offset(byte *memory, int total_size, const char *n } -/* do_resources: - * Do something for each resource matching type, name and lang. +/** + * Do something for each resource matching type, name and lang. */ int Win32ResExtractor::do_resources(WinLibrary *fi, const char *type, char *name, char *lang, byte **data) { WinResource *type_wr; @@ -494,7 +495,7 @@ int Win32ResExtractor::do_resources(WinLibrary *fi, const char *type, char *name WinResource *lang_wr; int size; - type_wr = (WinResource *)calloc(sizeof(WinResource)*3, 1); + type_wr = (WinResource *)calloc(3, sizeof(WinResource)); name_wr = type_wr + 1; lang_wr = type_wr + 2; @@ -521,14 +522,11 @@ int Win32ResExtractor::do_resources_recurs(WinLibrary *fi, WinResource *base, /* get a list of all resources at this level */ wr = list_resources(fi, base, &rescnt); if (wr == NULL) { - if (size != 0) - return size; - else - return 0; + return size; } /* process each resource listed */ - for (c = 0 ; c < rescnt ; c++) { + for (c = 0; c < rescnt; c++) { /* (over)write the corresponding WinResource holder with the current */ memcpy(WINRESOURCE_BY_LEVEL(wr[c].level), wr+c, sizeof(WinResource)); @@ -555,7 +553,9 @@ bool Win32ResExtractor::compare_resource_id(WinResource *wr, const char *id) { return false; if (id[0] == '-') id++; - if (!(cmp1 = strtol(wr->id, 0, 10)) || !(cmp2 = strtol(id, 0, 10)) || cmp1 != cmp2) + cmp1 = strtol(wr->id, 0, 10); + cmp2 = strtol(id, 0, 10); + if (!cmp1 || !cmp2 || cmp1 != cmp2) return false; } else { if (id[0] == '-') @@ -643,9 +643,9 @@ Win32ResExtractor::WinResource *Win32ResExtractor::list_pe_resources(WinLibrary } -/* list_resources: - * Return an array of WinResource's in the current - * resource level specified by _res-> +/** + * Return an array of WinResource's in the current + * resource level specified by _res-> */ Win32ResExtractor::WinResource *Win32ResExtractor::list_resources(WinLibrary *fi, WinResource *res, int *count) { if (res != NULL && !res->is_directory) @@ -657,10 +657,9 @@ Win32ResExtractor::WinResource *Win32ResExtractor::list_resources(WinLibrary *fi count); } -/* read_library: - * Read header and get resource directory offset in a Windows library - * (AKA module). - * +/** + * Read header and get resource directory offset in a Windows library + * (AKA module). */ bool Win32ResExtractor::read_library(WinLibrary *fi) { /* check for DOS header signature `MZ' */ @@ -695,7 +694,9 @@ bool Win32ResExtractor::read_library(WinLibrary *fi) { /* calc_vma_size has reported error */ return false; } - fi->memory = (byte *)realloc(fi->memory, fi->total_size); + byte *ptr = (byte *)realloc(fi->memory, fi->total_size); + assert(ptr); + fi->memory = ptr; /* relocate memory, start from last section */ pe_header = PE_HEADER(fi->memory); @@ -739,13 +740,13 @@ bool Win32ResExtractor::read_library(WinLibrary *fi) { return false; } -/* calc_vma_size: - * Calculate the total amount of memory needed for a 32-bit Windows - * module. Returns -1 if file was too small. +/** + * Calculate the total amount of memory needed for a 32-bit Windows + * module. Returns -1 if file was too small. */ int Win32ResExtractor::calc_vma_size(WinLibrary *fi) { - Win32ImageSectionHeader *seg; - int c, segcount, size; + Win32ImageSectionHeader *seg; + int c, segcount, size; size = 0; RETURN_IF_BAD_POINTER(-1, PE_HEADER(fi->memory)->file_header.number_of_sections); @@ -760,7 +761,7 @@ int Win32ResExtractor::calc_vma_size(WinLibrary *fi) { seg = PE_SECTIONS(fi->memory); RETURN_IF_BAD_POINTER(-1, *seg); - for (c = 0 ; c < segcount ; c++) { + for (c = 0 ; c < segcount ; c++) { RETURN_IF_BAD_POINTER(0, *seg); fix_win32_image_section_header(seg); @@ -768,9 +769,9 @@ int Win32ResExtractor::calc_vma_size(WinLibrary *fi) { /* I have no idea what misc.virtual_size is for... */ size = MAX((uint32)size, seg->virtual_address + seg->misc.virtual_size); seg++; - } + } - return size; + return size; } Win32ResExtractor::WinResource *Win32ResExtractor::find_with_resource_array(WinLibrary *fi, WinResource *wr, const char *id) { @@ -914,8 +915,8 @@ int Win32ResExtractor::convertIcons(byte *data, int datasize, byte **cursor, int if (entries[c].dib_size != bitmap.size + image_size + mask_size + palette_count * sizeof(Win32RGBQuad)) debugC(DEBUG_RESOURCE, "incorrect total size of bitmap (%d specified; %d real)", - entries[c].dib_size, - (int)(bitmap.size + image_size + mask_size + palette_count * sizeof(Win32RGBQuad)) + entries[c].dib_size, + (int)(bitmap.size + image_size + mask_size + palette_count * sizeof(Win32RGBQuad)) ); image_data = (byte *)malloc(image_size); @@ -977,9 +978,9 @@ int Win32ResExtractor::convertIcons(byte *data, int datasize, byte **cursor, int row[4*x+2] = (color >> 0) & 0xFF; } if (bitmap.bit_count == 32) - row[4*x+3] = (color >> 24) & 0xFF; + row[4*x+3] = (color >> 24) & 0xFF; else - row[4*x+3] = simple_vec(mask_data, x + mmod, 1) ? 0 : 0xFF; + row[4*x+3] = simple_vec(mask_data, x + mmod, 1) ? 0 : 0xFF; */ } @@ -1051,93 +1052,93 @@ uint32 Win32ResExtractor::simple_vec(byte *data, uint32 ofs, byte size) { } void Win32ResExtractor::fix_win32_cursor_icon_file_dir_endian(Win32CursorIconFileDir *obj) { - LE16(obj->reserved); + LE16(obj->reserved); LE16(obj->type); - LE16(obj->count); + LE16(obj->count); } void Win32ResExtractor::fix_win32_bitmap_info_header_endian(Win32BitmapInfoHeader *obj) { - LE32(obj->size); - LE32(obj->width); - LE32(obj->height); - LE16(obj->planes); - LE16(obj->bit_count); - LE32(obj->compression); - LE32(obj->size_image); - LE32(obj->x_pels_per_meter); - LE32(obj->y_pels_per_meter); - LE32(obj->clr_used); - LE32(obj->clr_important); + LE32(obj->size); + LE32(obj->width); + LE32(obj->height); + LE16(obj->planes); + LE16(obj->bit_count); + LE32(obj->compression); + LE32(obj->size_image); + LE32(obj->x_pels_per_meter); + LE32(obj->y_pels_per_meter); + LE32(obj->clr_used); + LE32(obj->clr_important); } void Win32ResExtractor::fix_win32_cursor_icon_file_dir_entry_endian(Win32CursorIconFileDirEntry *obj) { - LE16(obj->hotspot_x); - LE16(obj->hotspot_y); - LE32(obj->dib_size); - LE32(obj->dib_offset); + LE16(obj->hotspot_x); + LE16(obj->hotspot_y); + LE32(obj->dib_size); + LE32(obj->dib_offset); } void Win32ResExtractor::fix_win32_image_section_header(Win32ImageSectionHeader *obj) { - LE32(obj->misc.physical_address); - LE32(obj->virtual_address); - LE32(obj->size_of_raw_data); - LE32(obj->pointer_to_raw_data); - LE32(obj->pointer_to_relocations); - LE32(obj->pointer_to_linenumbers); - LE16(obj->number_of_relocations); - LE16(obj->number_of_linenumbers); - LE32(obj->characteristics); + LE32(obj->misc.physical_address); + LE32(obj->virtual_address); + LE32(obj->size_of_raw_data); + LE32(obj->pointer_to_raw_data); + LE32(obj->pointer_to_relocations); + LE32(obj->pointer_to_linenumbers); + LE16(obj->number_of_relocations); + LE16(obj->number_of_linenumbers); + LE32(obj->characteristics); } /* fix_win32_image_header_endian: * NOTE: This assumes that the optional header is always available. */ void Win32ResExtractor::fix_win32_image_header_endian(Win32ImageNTHeaders *obj) { - LE32(obj->signature); - LE16(obj->file_header.machine); - LE16(obj->file_header.number_of_sections); - LE32(obj->file_header.time_date_stamp); - LE32(obj->file_header.pointer_to_symbol_table); - LE32(obj->file_header.number_of_symbols); - LE16(obj->file_header.size_of_optional_header); - LE16(obj->file_header.characteristics); + LE32(obj->signature); + LE16(obj->file_header.machine); + LE16(obj->file_header.number_of_sections); + LE32(obj->file_header.time_date_stamp); + LE32(obj->file_header.pointer_to_symbol_table); + LE32(obj->file_header.number_of_symbols); + LE16(obj->file_header.size_of_optional_header); + LE16(obj->file_header.characteristics); // FIXME: Does this assert ever trigger? If so, we should modify this function // to properly deal with it. assert(obj->file_header.size_of_optional_header >= sizeof(obj->optional_header)); - LE16(obj->optional_header.magic); - LE32(obj->optional_header.size_of_code); - LE32(obj->optional_header.size_of_initialized_data); - LE32(obj->optional_header.size_of_uninitialized_data); - LE32(obj->optional_header.address_of_entry_point); - LE32(obj->optional_header.base_of_code); - LE32(obj->optional_header.base_of_data); - LE32(obj->optional_header.image_base); - LE32(obj->optional_header.section_alignment); - LE32(obj->optional_header.file_alignment); - LE16(obj->optional_header.major_operating_system_version); - LE16(obj->optional_header.minor_operating_system_version); - LE16(obj->optional_header.major_image_version); - LE16(obj->optional_header.minor_image_version); - LE16(obj->optional_header.major_subsystem_version); - LE16(obj->optional_header.minor_subsystem_version); - LE32(obj->optional_header.win32_version_value); - LE32(obj->optional_header.size_of_image); - LE32(obj->optional_header.size_of_headers); - LE32(obj->optional_header.checksum); - LE16(obj->optional_header.subsystem); - LE16(obj->optional_header.dll_characteristics); - LE32(obj->optional_header.size_of_stack_reserve); - LE32(obj->optional_header.size_of_stack_commit); - LE32(obj->optional_header.size_of_heap_reserve); - LE32(obj->optional_header.size_of_heap_commit); - LE32(obj->optional_header.loader_flags); - LE32(obj->optional_header.number_of_rva_and_sizes); + LE16(obj->optional_header.magic); + LE32(obj->optional_header.size_of_code); + LE32(obj->optional_header.size_of_initialized_data); + LE32(obj->optional_header.size_of_uninitialized_data); + LE32(obj->optional_header.address_of_entry_point); + LE32(obj->optional_header.base_of_code); + LE32(obj->optional_header.base_of_data); + LE32(obj->optional_header.image_base); + LE32(obj->optional_header.section_alignment); + LE32(obj->optional_header.file_alignment); + LE16(obj->optional_header.major_operating_system_version); + LE16(obj->optional_header.minor_operating_system_version); + LE16(obj->optional_header.major_image_version); + LE16(obj->optional_header.minor_image_version); + LE16(obj->optional_header.major_subsystem_version); + LE16(obj->optional_header.minor_subsystem_version); + LE32(obj->optional_header.win32_version_value); + LE32(obj->optional_header.size_of_image); + LE32(obj->optional_header.size_of_headers); + LE32(obj->optional_header.checksum); + LE16(obj->optional_header.subsystem); + LE16(obj->optional_header.dll_characteristics); + LE32(obj->optional_header.size_of_stack_reserve); + LE32(obj->optional_header.size_of_stack_commit); + LE32(obj->optional_header.size_of_heap_reserve); + LE32(obj->optional_header.size_of_heap_commit); + LE32(obj->optional_header.loader_flags); + LE32(obj->optional_header.number_of_rva_and_sizes); } void Win32ResExtractor::fix_win32_image_data_directory(Win32ImageDataDirectory *obj) { - LE32(obj->virtual_address); - LE32(obj->size); + LE32(obj->virtual_address); + LE32(obj->size); } diff --git a/engines/scumm/he/resource_he.h b/engines/scumm/he/resource_he.h index 4567c598a3..65190ea41c 100644 --- a/engines/scumm/he/resource_he.h +++ b/engines/scumm/he/resource_he.h @@ -184,7 +184,7 @@ class Win32ResExtractor : public ResExtractor { bool numeric_id; bool is_directory; - char *get_resource_id_quoted(); + Common::String getQuotedResourceId() const; } PACKED_STRUCT; diff --git a/engines/scumm/he/script_v100he.cpp b/engines/scumm/he/script_v100he.cpp index 3555f55d95..ca4a65ac74 100644 --- a/engines/scumm/he/script_v100he.cpp +++ b/engines/scumm/he/script_v100he.cpp @@ -34,7 +34,6 @@ #include "scumm/he/intern_he.h" #include "scumm/object.h" #include "scumm/resource.h" -#include "scumm/he/resource_he.h" #include "scumm/scumm.h" #include "scumm/he/sound_he.h" #include "scumm/he/sprite_he.h" @@ -1623,13 +1622,11 @@ void ScummEngine_v100he::o100_roomOps() { case 137: byte buffer[256]; - int r; copyScriptString((byte *)buffer, sizeof(buffer)); - r = convertFilePath(buffer, sizeof(buffer)); - memcpy(_saveLoadFileName, buffer + r, sizeof(buffer) - r); - debug(1, "o100_roomOps: case 137: filename %s", _saveLoadFileName); + _saveLoadFileName = (char *)buffer + convertFilePath(buffer, sizeof(buffer)); + debug(1, "o100_roomOps: case 137: filename %s", _saveLoadFileName.c_str()); _saveLoadFlag = pop(); _saveLoadSlot = 255; diff --git a/engines/scumm/he/script_v60he.cpp b/engines/scumm/he/script_v60he.cpp index 8ade78c1b5..9d62a31f6d 100644 --- a/engines/scumm/he/script_v60he.cpp +++ b/engines/scumm/he/script_v60he.cpp @@ -283,15 +283,14 @@ void ScummEngine_v60he::o60_roomOps() { break; case 221: byte buffer[100]; - int len, r; + int len; convertMessageToString(_scriptPointer, buffer, sizeof(buffer)); len = resStrLen(_scriptPointer); _scriptPointer += len + 1; - r = convertFilePath(buffer, sizeof(buffer)); - memcpy(_saveLoadFileName, buffer + r, sizeof(buffer) - r); - debug(1, "o60_roomOps: case 221: filename %s", _saveLoadFileName); + _saveLoadFileName = (char *)buffer + convertFilePath(buffer, sizeof(buffer)); + debug(1, "o60_roomOps: case 221: filename %s", _saveLoadFileName.c_str()); _saveLoadFlag = pop(); _saveLoadSlot = 255; diff --git a/engines/scumm/he/script_v70he.cpp b/engines/scumm/he/script_v70he.cpp index 979b2b3df4..9b160151b0 100644 --- a/engines/scumm/he/script_v70he.cpp +++ b/engines/scumm/he/script_v70he.cpp @@ -31,7 +31,6 @@ #include "scumm/he/intern_he.h" #include "scumm/object.h" #include "scumm/resource.h" -#include "scumm/he/resource_he.h" #include "scumm/scumm.h" #include "scumm/he/sound_he.h" #include "scumm/verbs.h" diff --git a/engines/scumm/he/script_v72he.cpp b/engines/scumm/he/script_v72he.cpp index baa57c7821..76daacbd54 100644 --- a/engines/scumm/he/script_v72he.cpp +++ b/engines/scumm/he/script_v72he.cpp @@ -36,7 +36,6 @@ #include "scumm/he/intern_he.h" #include "scumm/object.h" #include "scumm/resource.h" -#include "scumm/he/resource_he.h" #include "scumm/scumm.h" #include "scumm/he/sound_he.h" #include "scumm/util.h" @@ -173,7 +172,7 @@ int ScummEngine_v72he::readArray(int array, int idx2, int idx1) { } const int offset = (FROM_LE_32(ah->dim1end) - FROM_LE_32(ah->dim1start) + 1) * - (idx2 - FROM_LE_32(ah->dim2start)) - FROM_LE_32(ah->dim1start) + idx1; + (idx2 - FROM_LE_32(ah->dim2start)) + (idx1 - FROM_LE_32(ah->dim1start)); switch (FROM_LE_32(ah->type)) { case kByteArray: @@ -711,13 +710,11 @@ void ScummEngine_v72he::o72_roomOps() { case 221: byte buffer[256]; - int r; copyScriptString((byte *)buffer, sizeof(buffer)); - r = convertFilePath(buffer, sizeof(buffer)); - memcpy(_saveLoadFileName, buffer + r, sizeof(buffer) - r); - debug(1, "o72_roomOps: case 221: filename %s", _saveLoadFileName); + _saveLoadFileName = (char *)buffer + convertFilePath(buffer, sizeof(buffer)); + debug(1, "o72_roomOps: case 221: filename %s", _saveLoadFileName.c_str()); _saveLoadFlag = pop(); _saveLoadSlot = 255; diff --git a/engines/scumm/he/script_v80he.cpp b/engines/scumm/he/script_v80he.cpp index dd44180fa0..b06dc712d9 100644 --- a/engines/scumm/he/script_v80he.cpp +++ b/engines/scumm/he/script_v80he.cpp @@ -35,7 +35,6 @@ #include "scumm/he/intern_he.h" #include "scumm/object.h" #include "scumm/resource.h" -#include "scumm/he/resource_he.h" #include "scumm/scumm.h" #include "scumm/he/sound_he.h" diff --git a/engines/scumm/he/script_v90he.cpp b/engines/scumm/he/script_v90he.cpp index 6acc16a804..841eba960d 100644 --- a/engines/scumm/he/script_v90he.cpp +++ b/engines/scumm/he/script_v90he.cpp @@ -32,7 +32,6 @@ #include "scumm/he/logic_he.h" #include "scumm/object.h" #include "scumm/resource.h" -#include "scumm/he/resource_he.h" #include "scumm/scumm.h" #include "scumm/sound.h" #include "scumm/he/sprite_he.h" @@ -1948,42 +1947,51 @@ void ScummEngine_v90he::getArrayDim(int array, int *dim2start, int *dim2end, int } } +static int sortArrayOffset; + static int compareByteArray(const void *a, const void *b) { - int va = *((const uint8 *)a); - int vb = *((const uint8 *)a); + int va = *((const uint8 *)a + sortArrayOffset); + int vb = *((const uint8 *)b + sortArrayOffset); return va - vb; } static int compareByteArrayReverse(const void *a, const void *b) { - int va = *((const uint8 *)a); - int vb = *((const uint8 *)a); + int va = *((const uint8 *)a + sortArrayOffset); + int vb = *((const uint8 *)b + sortArrayOffset); return vb - va; } static int compareIntArray(const void *a, const void *b) { - int va = (int16)READ_LE_UINT16((const uint8 *)a); - int vb = (int16)READ_LE_UINT16((const uint8 *)b); + int va = (int16)READ_LE_UINT16((const uint8 *)a + sortArrayOffset * 2); + int vb = (int16)READ_LE_UINT16((const uint8 *)b + sortArrayOffset * 2); return va - vb; } static int compareIntArrayReverse(const void *a, const void *b) { - int va = (int16)READ_LE_UINT16((const uint8 *)a); - int vb = (int16)READ_LE_UINT16((const uint8 *)b); + int va = (int16)READ_LE_UINT16((const uint8 *)a + sortArrayOffset * 2); + int vb = (int16)READ_LE_UINT16((const uint8 *)b + sortArrayOffset * 2); return vb - va; } static int compareDwordArray(const void *a, const void *b) { - int va = (int32)READ_LE_UINT32((const uint8 *)a); - int vb = (int32)READ_LE_UINT32((const uint8 *)b); + int va = (int32)READ_LE_UINT32((const uint8 *)a + sortArrayOffset * 4); + int vb = (int32)READ_LE_UINT32((const uint8 *)b + sortArrayOffset * 4); return va - vb; } static int compareDwordArrayReverse(const void *a, const void *b) { - int va = (int32)READ_LE_UINT32((const uint8 *)a); - int vb = (int32)READ_LE_UINT32((const uint8 *)b); + int va = (int32)READ_LE_UINT32((const uint8 *)a + sortArrayOffset * 4); + int vb = (int32)READ_LE_UINT32((const uint8 *)b + sortArrayOffset * 4); return vb - va; } + +/** + * Sort a row range in a two-dimensional array by the value in a given column. + * + * We sort the data in the row range [dim2start..dim2end], according to the value + * in column dim1start == dim1end. + */ void ScummEngine_v90he::sortArray(int array, int dim2start, int dim2end, int dim1start, int dim1end, int sortOrder) { debug(9, "sortArray(%d, [%d,%d,%d,%d], %d)", array, dim2start, dim2end, dim1start, dim1end, sortOrder); @@ -1992,11 +2000,21 @@ void ScummEngine_v90he::sortArray(int array, int dim2start, int dim2end, int dim ArrayHeader *ah = (ArrayHeader *)getResourceAddress(rtString, readVar(array)); assert(ah); - const int num = dim2end - dim2start + 1; - const int pitch = FROM_LE_32(ah->dim1end) - FROM_LE_32(ah->dim1start) + 1; - const int offset = pitch * (dim2start - FROM_LE_32(ah->dim2start)) - + dim1start - FROM_LE_32(ah->dim1start); - + const int num = dim2end - dim2start + 1; // number of rows to sort + const int pitch = FROM_LE_32(ah->dim1end) - FROM_LE_32(ah->dim1start) + 1; // length of a row = number of columns in it + const int offset = pitch * (dim2start - FROM_LE_32(ah->dim2start)); // memory offset to the first row to be sorted + sortArrayOffset = dim1start - FROM_LE_32(ah->dim1start); // offset to the column by which we sort + + // Now we just have to invoke qsort on the appropriate row range. We + // need to pass sortArrayOffset as an implicit parameter to the + // comparison functions, which makes it necessary to use a global + // (albeit local to this file) variable. + // This could be avoided by using qsort_r or a self-written portable + // analog (this function passes an additional, user determined + // parameter to the comparison function). + // Another idea would be to use Common::sort, but that only is + // suitable if you sort objects of fixed size, which must be known + // during compilation time; clearly this not the case here. switch (FROM_LE_32(ah->type)) { case kByteArray: case kStringArray: @@ -2039,7 +2057,6 @@ void ScummEngine_v90he::o90_sortArray() { int dim2end = pop(); int dim2start = pop(); getArrayDim(array, &dim2start, &dim2end, &dim1start, &dim1end); - checkArrayLimits(array, dim2start, dim2end, dim1start, dim1end); sortArray(array, dim2start, dim2end, dim1start, dim1end, sortOrder); } break; diff --git a/engines/scumm/he/sound_he.cpp b/engines/scumm/he/sound_he.cpp index 7bfd1de4cf..318cb818b6 100644 --- a/engines/scumm/he/sound_he.cpp +++ b/engines/scumm/he/sound_he.cpp @@ -241,7 +241,7 @@ int SoundHE::isSoundCodeUsed(int sound) { chan = i; } - if (_mixer->isSoundHandleActive(_heSoundChannels[chan]) && chan != -1) { + if (chan != -1 && _mixer->isSoundHandleActive(_heSoundChannels[chan])) { return _heChannel[chan].sbngBlock; } else { return 0; @@ -255,7 +255,7 @@ int SoundHE::getSoundPos(int sound) { chan = i; } - if (_mixer->isSoundHandleActive(_heSoundChannels[chan]) && chan != -1) { + if (chan != -1 && _mixer->isSoundHandleActive(_heSoundChannels[chan])) { int time = _vm->getHETimer(chan + 4) * _heChannel[chan].rate / 1000; return time; } else { @@ -276,7 +276,7 @@ int SoundHE::getSoundVar(int sound, int var) { chan = i; } - if (_mixer->isSoundHandleActive(_heSoundChannels[chan]) && chan != -1) { + if (chan != -1 && _mixer->isSoundHandleActive(_heSoundChannels[chan])) { debug(5, "getSoundVar: sound %d var %d result %d", sound, var, _heChannel[chan].soundVars[var]); return _heChannel[chan].soundVars[var]; } else { diff --git a/engines/scumm/he/wiz_he.cpp b/engines/scumm/he/wiz_he.cpp index 1ac7e98689..ddbbb2101f 100644 --- a/engines/scumm/he/wiz_he.cpp +++ b/engines/scumm/he/wiz_he.cpp @@ -358,6 +358,7 @@ static bool calcClipRects(int dst_w, int dst_h, int src_x, int src_y, int src_w, void Wiz::writeColor(uint8 *dstPtr, int dstType, uint16 color) { switch (dstType) { + case kDstCursor: case kDstScreen: WRITE_UINT16(dstPtr, color); break; @@ -1519,7 +1520,7 @@ uint8 *Wiz::drawWizImage(int resNum, int state, int maskNum, int maskState, int cw = width; ch = height; dstPitch = cw * _vm->_bytesPerPixel; - dstType = kDstMemory; + dstType = (_cursorImage) ? kDstCursor : kDstMemory; } else { if (dstResNum) { uint8 *dstPtr = _vm->getResourceAddress(rtImage, dstResNum); @@ -2088,7 +2089,7 @@ void Wiz::displayWizComplexImage(const WizParameters *params) { if (_vm->_fullRedraw && dstResNum == 0) { if (sourceImage != 0 || (params->processFlags & (kWPFScaled | kWPFRotate))) - error("Can't do this command in the enter script."); + error("Can't do this command in the enter script"); assert(_imagesNum < ARRAYSIZE(_images)); WizImage *pwi = &_images[_imagesNum]; diff --git a/engines/scumm/he/wiz_he.h b/engines/scumm/he/wiz_he.h index 1fa9564486..c255e27d14 100644 --- a/engines/scumm/he/wiz_he.h +++ b/engines/scumm/he/wiz_he.h @@ -145,7 +145,8 @@ enum { enum DstSurface { kDstScreen = 0, kDstMemory = 1, - kDstResource = 2 + kDstResource = 2, + kDstCursor = 3 }; class ScummEngine_v71he; diff --git a/engines/scumm/imuse/imuse_player.cpp b/engines/scumm/imuse/imuse_player.cpp index 6b38f80df1..a90915e438 100644 --- a/engines/scumm/imuse/imuse_player.cpp +++ b/engines/scumm/imuse/imuse_player.cpp @@ -192,13 +192,6 @@ int Player::start_seq_sound(int sound, bool reset_vars) { if (!memcmp(ptr, "RO", 2)) { // Old style 'RO' resource _parser = MidiParser_createRO(); - } else if (!memcmp(ptr, "SO", 2)) { - // Euphony (FM-TOWNS) resource - - //////////// REMOVE - //_parser = MidiParser_createEUP(); - /////////// - } else if (!memcmp(ptr, "FORM", 4)) { // Humongous Games XMIDI resource _parser = MidiParser::createParser_XMIDI(); diff --git a/engines/scumm/insane/insane.cpp b/engines/scumm/insane/insane.cpp index 3876bd4e80..f2e50382b3 100644 --- a/engines/scumm/insane/insane.cpp +++ b/engines/scumm/insane/insane.cpp @@ -944,7 +944,7 @@ bool Insane::actor1StateFlags(int state) { bool retvalue = 0; unsigned int i; - for (i = 0; i < sizeof(spans); i++) { + for (i = 0; i < ARRAYSIZE(spans); i++) { retvalue = !retvalue; if (spans[i] <= state) break; @@ -1099,7 +1099,7 @@ bool Insane::actor0StateFlags1(int state) { bool retvalue = 1; unsigned int i; - for (i = 0; i < sizeof(spans); i++) { + for (i = 0; i < ARRAYSIZE(spans); i++) { retvalue = !retvalue; if (spans[i] >= state) break; @@ -1119,7 +1119,7 @@ bool Insane::actor0StateFlags2(int state) { bool retvalue = 1; unsigned int i; - for (i = 0; i < sizeof(spans); i++) { + for (i = 0; i < ARRAYSIZE(spans); i++) { retvalue = !retvalue; if (spans[i] >= state) break; diff --git a/engines/scumm/insane/insane_ben.cpp b/engines/scumm/insane/insane_ben.cpp index 9ddb4c6670..05775f1585 100644 --- a/engines/scumm/insane/insane_ben.cpp +++ b/engines/scumm/insane/insane_ben.cpp @@ -1163,7 +1163,7 @@ void Insane::actor02Reaction(int32 buttons) { setBenState(); _actor[0].act[2].tilt = 0; // for some reason there is no break at this - // place, so tilt gets overriden on next line + // place, so tilt gets overridden on next line } _actor[0].act[2].tilt = calcTilt(_actor[0].tilt); break; diff --git a/engines/scumm/insane/insane_enemy.cpp b/engines/scumm/insane/insane_enemy.cpp index 2291b2c37b..e8d97d3875 100644 --- a/engines/scumm/insane/insane_enemy.cpp +++ b/engines/scumm/insane/insane_enemy.cpp @@ -1328,7 +1328,7 @@ void Insane::turnEnemy(bool battle) { if (_actor[1].damage < _actor[1].maxdamage) { _actor[1].lost = false; } else { - if (!_actor[1].lost && !_actor[1].lost) { + if (!_actor[1].lost && !_actor[0].lost) { _actor[1].lost = true; _actor[1].act[2].state = 36; _actor[1].act[1].state = 36; @@ -2072,7 +2072,7 @@ void Insane::actor12Reaction(int32 buttons) { setEnemyState(); _actor[1].act[2].tilt = 0; // for some reason there is no break at this - // place, so tilt gets overriden on next line + // place, so tilt gets overridden on next line } _actor[1].act[2].tilt = calcTilt(_actor[1].tilt); break; diff --git a/engines/scumm/module.mk b/engines/scumm/module.mk index 14d1f5fdd4..1a60564a9e 100644 --- a/engines/scumm/module.mk +++ b/engines/scumm/module.mk @@ -16,6 +16,7 @@ MODULE_OBJS := \ dialogs.o \ file.o \ file_nes.o \ + gfx_towns.o \ gfx.o \ he/resource_he.o \ he/script_v60he.o \ @@ -40,6 +41,7 @@ MODULE_OBJS := \ player_v1.o \ player_v2.o \ player_v2a.o \ + player_v2base.o \ player_v2cms.o \ player_v3a.o \ player_v4a.o \ diff --git a/engines/scumm/object.cpp b/engines/scumm/object.cpp index c6ac53b862..c44043ca81 100644 --- a/engines/scumm/object.cpp +++ b/engines/scumm/object.cpp @@ -315,6 +315,10 @@ int ScummEngine::getObjectIndex(int object) const { return -1; for (i = (_numLocalObjects-1); i > 0; i--) { + if (_game.version == 0 ) + if( _objs[i].flags != _v0ObjectFlag ) + continue; + if (_objs[i].obj_nr == object) return i; } @@ -526,6 +530,9 @@ int ScummEngine::findObject(int x, int y) { #endif if (_objs[i].x_pos <= x && _objs[i].width + _objs[i].x_pos > x && _objs[i].y_pos <= y && _objs[i].height + _objs[i].y_pos > y) { + // MMC64: Set the object search flag + if (_game.version == 0) + _v0ObjectFlag = _objs[i].flags; if (_game.version == 0 && _v0ObjectIndex) return i; else @@ -714,7 +721,7 @@ void ScummEngine_v70he::storeFlObject(int slot) { memcpy(&_storedFlObjects[_numStoredFlObjects], &_objs[slot], sizeof(_objs[slot])); _numStoredFlObjects++; if (_numStoredFlObjects > 100) - error("Too many flobjects saved on room transition."); + error("Too many flobjects saved on room transition"); } void ScummEngine_v70he::restoreFlObjects() { @@ -993,6 +1000,7 @@ void ScummEngine::resetRoomObject(ObjectData *od, const byte *room, const byte * od->flags = Gdi::dbAllowMaskOr; if (_game.version == 8) { + assert(imhd); od->obj_nr = READ_LE_UINT16(&(cdhd->v7.obj_id)); od->parent = cdhd->v7.parent; @@ -1008,6 +1016,7 @@ void ScummEngine::resetRoomObject(ObjectData *od, const byte *room, const byte * od->flags = ((((byte)READ_LE_UINT32(&imhd->v8.flags)) & 16) == 0) ? Gdi::dbAllowMaskOr : 0; } else if (_game.version == 7) { + assert(imhd); od->obj_nr = READ_LE_UINT16(&(cdhd->v7.obj_id)); od->parent = cdhd->v7.parent; @@ -1020,6 +1029,7 @@ void ScummEngine::resetRoomObject(ObjectData *od, const byte *room, const byte * od->actordir = (byte)READ_LE_UINT16(&imhd->v7.actordir); } else if (_game.version == 6) { + assert(imhd); od->obj_nr = READ_LE_UINT16(&(cdhd->v6.obj_id)); od->width = READ_LE_UINT16(&cdhd->v6.w); diff --git a/engines/scumm/palette.cpp b/engines/scumm/palette.cpp index 7659e5fe1a..672af1e191 100644 --- a/engines/scumm/palette.cpp +++ b/engines/scumm/palette.cpp @@ -30,6 +30,7 @@ #include "scumm/scumm_v6.h" #include "scumm/scumm_v8.h" #include "scumm/util.h" +#include "scumm/charset.h" namespace Scumm { @@ -70,7 +71,7 @@ void ScummEngine::resetPalette() { // Use 17 color table for v1 games to allow correct color for inventory and // sentence line. Original games used some kind of dynamic color table // remapping between rooms. - 0xFF, 0x55, 0xFF + 0x7F, 0x3B, 0xA6 }; static const byte tableNESPalette[] = { @@ -139,6 +140,24 @@ void ScummEngine::resetPalette() { 0x00, 0x00, 0x00, 0x00, 0xFF, 0x00 }; +#ifdef USE_RGB_COLOR +#ifndef DISABLE_TOWNS_DUAL_LAYER_MODE + static const byte tableTownsV3Palette[] = { + 0x00, 0x00, 0x00, 0x00, 0x00, 0xA0, 0x00, 0xA0, 0x00, 0x00, 0xA0, 0xA0, + 0xA0, 0x00, 0x00, 0xA0, 0x00, 0xA0, 0xA0, 0x60, 0x00, 0xA0, 0xA0, 0xA0, + 0x60, 0x60, 0x60, 0x60, 0x60, 0xE0, 0x00, 0xE0, 0x00, 0x00, 0xE0, 0xE0, + 0xE0, 0x80, 0x80, 0xE0, 0x00, 0xE0, 0xE0, 0xE0, 0x00, 0xE0, 0xE0, 0xE0 + }; + + static const byte tableTownsLoomPalette[] = { + 0x00, 0x00, 0x00, 0x00, 0x00, 0xAB, 0x00, 0xAB, 0x00, 0x00, 0xAB, 0xAB, + 0xAB, 0x00, 0x00, 0x69, 0x29, 0x45, 0x8C, 0x4D, 0x14, 0xAB, 0xAB, 0xAB, + 0x57, 0x3F, 0x57, 0x57, 0x57, 0xFF, 0x57, 0xFF, 0x57, 0x57, 0xFF, 0xFF, + 0xFF, 0x57, 0x57, 0xD6, 0x94, 0x40, 0xFF, 0xFF, 0x57, 0xFF, 0xFF, 0xFF + }; +#endif +#endif + if (_game.version <= 1) { if (_game.platform == Common::kPlatformApple2GS) { // TODO: unique palette? @@ -198,6 +217,19 @@ void ScummEngine::resetPalette() { // else we initialise and then lock down the first 16 colors. if (_renderMode != Common::kRenderEGA) setPaletteFromTable(tableAmigaMIPalette, sizeof(tableAmigaMIPalette) / 3); +#ifndef DISABLE_TOWNS_DUAL_LAYER_MODE + } else if (_game.platform == Common::kPlatformFMTowns) { + if (_game.id == GID_INDY4 || _game.id == GID_MONKEY2) + _townsClearLayerFlag = 0; +#ifdef USE_RGB_COLOR + else if (_game.id == GID_LOOM) + towns_setTextPaletteFromPtr(tableTownsLoomPalette); + else if (_game.version == 3) + towns_setTextPaletteFromPtr(tableTownsV3Palette); +#endif + + _townsScreen->toggleLayers(_townsActiveLayerFlags); +#endif // DISABLE_TOWNS_DUAL_LAYER_MODE } setDirtyColors(0, 255); } @@ -465,6 +497,11 @@ void ScummEngine::cyclePalette() { int valueToAdd; int i, j; +#ifndef DISABLE_TOWNS_DUAL_LAYER_MODE + if (_game.platform == Common::kPlatformFMTowns && !(_townsPaletteFlags & 1)) + return; +#endif + valueToAdd = VAR(VAR_TIMER); if (valueToAdd < VAR(VAR_TIMER_NEXT)) valueToAdd = VAR(VAR_TIMER_NEXT); @@ -506,6 +543,11 @@ void ScummEngine::moveMemInPalRes(int start, int end, byte direction) { } void ScummEngine::palManipulateInit(int resID, int start, int end, int time) { +#ifndef DISABLE_TOWNS_DUAL_LAYER_MODE + if (_game.platform == Common::kPlatformFMTowns && !(_townsPaletteFlags & 1)) + return; +#endif + byte *string1 = getStringAddress(resID); byte *string2 = getStringAddress(resID + 1); byte *string3 = getStringAddress(resID + 2); @@ -973,6 +1015,12 @@ void ScummEngine::setCurrentPalette(int palindex) { pals = getPalettePtr(_curPalIndex, _roomResource); if (_game.id == GID_LOOM && _game.platform == Common::kPlatformPCEngine) { setPCEPaletteFromPtr(pals); +#ifdef USE_RGB_COLOR +#ifndef DISABLE_TOWNS_DUAL_LAYER_MODE + } else if (_game.platform == Common::kPlatformFMTowns) { + towns_setPaletteFromPtr(pals); +#endif +#endif } else { setPaletteFromPtr(pals); } @@ -1069,10 +1117,23 @@ void ScummEngine::updatePalette() { } } - _system->setPalette(palette_colors, first, num); - _palDirtyMax = -1; _palDirtyMin = 256; + +#ifdef USE_RGB_COLOR +#ifndef DISABLE_TOWNS_DUAL_LAYER_MODE + if (_game.platform == Common::kPlatformFMTowns) { + p = palette_colors; + for (i = first; i < first + num; ++i) { + _16BitPalette[i] = get16BitColor(p[0], p[1], p[2]); + p += 4; + } + return; + } +#endif +#endif + + _system->setPalette(palette_colors, first, num); } } // End of namespace Scumm diff --git a/engines/scumm/player_nes.h b/engines/scumm/player_nes.h index b2eafb79b0..89292f7c24 100644 --- a/engines/scumm/player_nes.h +++ b/engines/scumm/player_nes.h @@ -75,8 +75,6 @@ private: void APU_writeControl(byte value); byte APU_readStatus(); - void do_mix(int16 *buf, uint len); - ScummEngine *_vm; Audio::Mixer *_mixer; Audio::SoundHandle _soundHandle; diff --git a/engines/scumm/player_pce.cpp b/engines/scumm/player_pce.cpp index a17aab4d59..4236fb2d6b 100644 --- a/engines/scumm/player_pce.cpp +++ b/engines/scumm/player_pce.cpp @@ -152,7 +152,7 @@ static const uint16 sounds[13][6] = { // 0xB2A1 static const byte data_table[482] = { /* 0*/ 0xE2, 0x0A, 0xE1, 0x0D, 0xE6, 0xED, 0xE0, 0x0F, 0xE2, 0x00, 0xE1, 0x00, - 0xF2, 0xF2, 0xB2, 0xE1, 0x01, 0xF2, 0xF2, 0xB2, 0xE1, 0x02, 0xF2, 0xF2, + 0xF2, 0xF2, 0xB2, 0xE1, 0x01, 0xF2, 0xF2, 0xB2, 0xE1, 0x02, 0xF2, 0xF2, 0xB2, 0xE1, 0x03, 0xF2, 0xF2, 0xB2, 0xE1, 0x04, 0xF2, 0xF2, 0xB2, 0xE1, 0x05, 0xF2, 0xF2, 0xB2, 0xE1, 0x06, 0xF2, 0xF2, 0xB2, 0xE1, 0x07, 0xF2, 0xF2, 0xB2, 0xE1, 0x08, 0xF2, 0xF2, 0xB2, 0xE1, 0x09, 0xF2, 0xF2, 0xB2, @@ -208,10 +208,10 @@ static const byte data_table[482] = { /*395*/ 0xE2, 0x0C, 0xE1, 0x00, 0xE0, 0x04, 0xE6, 0xED, 0xD4, 0x0B, 0xE8, 0x0B, 0xFF, /*408*/ 0xE2, 0x0C, 0xE1, 0x03, 0xE0, 0x04, 0xE6, 0xED, 0xD4, 0xF0, 0x0C, 0x00, - 0xE8, 0x10, 0xE8, 0x00, 0xE8, 0x10, 0xE8, 0x00, 0xE8, 0x10, 0xE8, 0x00, - 0xE8, 0x10, 0xE8, 0x00, 0xE8, 0x10, 0xE8, 0x00, 0xE8, 0x10, 0xE8, 0x00, - 0xE8, 0x10, 0xE8, 0x00, 0xE8, 0x10, 0xE8, 0x00, 0xE8, 0x10, 0xE8, 0x00, - 0xE8, 0x10, 0xE8, 0x00, 0xE8, 0x10, 0xE8, 0x00, 0xE8, 0x10, 0xFF, + 0xE8, 0x10, 0xE8, 0x00, 0xE8, 0x10, 0xE8, 0x00, 0xE8, 0x10, 0xE8, 0x00, + 0xE8, 0x10, 0xE8, 0x00, 0xE8, 0x10, 0xE8, 0x00, 0xE8, 0x10, 0xE8, 0x00, + 0xE8, 0x10, 0xE8, 0x00, 0xE8, 0x10, 0xE8, 0x00, 0xE8, 0x10, 0xE8, 0x00, + 0xE8, 0x10, 0xE8, 0x00, 0xE8, 0x10, 0xE8, 0x00, 0xE8, 0x10, 0xFF, /*467*/ 0xE2, 0x0C, 0xE1, 0x00, 0xE0, 0x04, 0xE6, 0xED, 0xD4, 0x1B, 0xE8, 0x1B, 0xFF, /*480*/ 0xFF, @@ -238,11 +238,11 @@ private: double _clock; double _rate; uint8 _select; - uint8 _balance; - channel_t _channel[8]; - int16 _volumeTable[32]; - uint32 _noiseFreqTable[32]; - uint32 _waveFreqTable[4096]; + uint8 _balance; + channel_t _channel[8]; + int16 _volumeTable[32]; + uint32 _noiseFreqTable[32]; + uint32 _waveFreqTable[4096]; public: void init(); @@ -255,180 +255,180 @@ public: PSG_HuC6280::PSG_HuC6280(double clock, double samplerate) { _clock = clock; - _rate = samplerate; + _rate = samplerate; - // Initialize PSG_HuC6280 emulator - init(); + // Initialize PSG_HuC6280 emulator + init(); } void PSG_HuC6280::init() { - int i; - double step; + int i; + double step; - // Loudest volume level for table - double level = 65535.0 / 6.0 / 32.0; + // Loudest volume level for table + double level = 65535.0 / 6.0 / 32.0; - // Clear context + // Clear context reset(); - // Make waveform frequency table - for(i = 0; i < 4096; i++) { - step = ((_clock / _rate) * 4096) / (i+1); - _waveFreqTable[(1 + i) & 0xFFF] = (uint32)step; - } - - // Make noise frequency table - for(i = 0; i < 32; i++) { - step = ((_clock / _rate) * 32) / (i+1); - _noiseFreqTable[i] = (uint32)step; - } - - // Make volume table - // PSG_HuC6280 has 48dB volume range spread over 32 steps - step = 48.0 / 32.0; - for(i = 0; i < 31; i++) { - _volumeTable[i] = (uint16)level; - level /= pow(10.0, step / 20.0); - } - _volumeTable[31] = 0; + // Make waveform frequency table + for(i = 0; i < 4096; i++) { + step = ((_clock / _rate) * 4096) / (i+1); + _waveFreqTable[(1 + i) & 0xFFF] = (uint32)step; + } + + // Make noise frequency table + for(i = 0; i < 32; i++) { + step = ((_clock / _rate) * 32) / (i+1); + _noiseFreqTable[i] = (uint32)step; + } + + // Make volume table + // PSG_HuC6280 has 48dB volume range spread over 32 steps + step = 48.0 / 32.0; + for(i = 0; i < 31; i++) { + _volumeTable[i] = (uint16)level; + level /= pow(10.0, step / 20.0); + } + _volumeTable[31] = 0; } void PSG_HuC6280::reset() { _select = 0; - _balance = 0xFF; - memset(_channel, 0, sizeof(_channel)); - memset(_volumeTable, 0, sizeof(_volumeTable)); - memset(_noiseFreqTable, 0, sizeof(_noiseFreqTable)); - memset(_waveFreqTable, 0, sizeof(_waveFreqTable)); + _balance = 0xFF; + memset(_channel, 0, sizeof(_channel)); + memset(_volumeTable, 0, sizeof(_volumeTable)); + memset(_noiseFreqTable, 0, sizeof(_noiseFreqTable)); + memset(_waveFreqTable, 0, sizeof(_waveFreqTable)); } void PSG_HuC6280::write(int offset, byte data) { - channel_t *chan = &_channel[_select]; - - switch(offset & 0x0F) { - case 0x00: // Channel select - _select = data & 0x07; - break; - - case 0x01: // Global balance - _balance = data; - break; - - case 0x02: // Channel frequency (LSB) - chan->frequency = (chan->frequency & 0x0F00) | data; - chan->frequency &= 0x0FFF; - break; - - case 0x03: // Channel frequency (MSB) - chan->frequency = (chan->frequency & 0x00FF) | (data << 8); - chan->frequency &= 0x0FFF; - break; - - case 0x04: // Channel control (key-on, DDA mode, volume) - // 1-to-0 transition of DDA bit resets waveform index - if((chan->control & 0x40) && ((data & 0x40) == 0)) { - chan->index = 0; - } - chan->control = data; - break; - - case 0x05: // Channel balance - chan->balance = data; - break; - - case 0x06: // Channel waveform data - switch(chan->control & 0xC0) { - case 0x00: - chan->waveform[chan->index & 0x1F] = data & 0x1F; - chan->index = (chan->index + 1) & 0x1F; - break; - - case 0x40: - break; - - case 0x80: - chan->waveform[chan->index & 0x1F] = data & 0x1F; - chan->index = (chan->index + 1) & 0x1F; - break; - - case 0xC0: - chan->dda = data & 0x1F; - break; - } - - break; - - case 0x07: // Noise control (enable, frequency) - case 0x08: // LFO frequency - case 0x09: // LFO control (enable, mode) - break; - - default: - break; - } + channel_t *chan = &_channel[_select]; + + switch(offset & 0x0F) { + case 0x00: // Channel select + _select = data & 0x07; + break; + + case 0x01: // Global balance + _balance = data; + break; + + case 0x02: // Channel frequency (LSB) + chan->frequency = (chan->frequency & 0x0F00) | data; + chan->frequency &= 0x0FFF; + break; + + case 0x03: // Channel frequency (MSB) + chan->frequency = (chan->frequency & 0x00FF) | (data << 8); + chan->frequency &= 0x0FFF; + break; + + case 0x04: // Channel control (key-on, DDA mode, volume) + // 1-to-0 transition of DDA bit resets waveform index + if((chan->control & 0x40) && ((data & 0x40) == 0)) { + chan->index = 0; + } + chan->control = data; + break; + + case 0x05: // Channel balance + chan->balance = data; + break; + + case 0x06: // Channel waveform data + switch(chan->control & 0xC0) { + case 0x00: + chan->waveform[chan->index & 0x1F] = data & 0x1F; + chan->index = (chan->index + 1) & 0x1F; + break; + + case 0x40: + break; + + case 0x80: + chan->waveform[chan->index & 0x1F] = data & 0x1F; + chan->index = (chan->index + 1) & 0x1F; + break; + + case 0xC0: + chan->dda = data & 0x1F; + break; + } + + break; + + case 0x07: // Noise control (enable, frequency) + case 0x08: // LFO frequency + case 0x09: // LFO control (enable, mode) + break; + + default: + break; + } } void PSG_HuC6280::update(int16* samples, int sampleCnt) { - static const int scale_tab[] = { - 0x00, 0x03, 0x05, 0x07, 0x09, 0x0B, 0x0D, 0x0F, - 0x10, 0x13, 0x15, 0x17, 0x19, 0x1B, 0x1D, 0x1F - }; - int ch; - int i; + static const int scale_tab[] = { + 0x00, 0x03, 0x05, 0x07, 0x09, 0x0B, 0x0D, 0x0F, + 0x10, 0x13, 0x15, 0x17, 0x19, 0x1B, 0x1D, 0x1F + }; + int ch; + int i; - int lmal = (_balance >> 4) & 0x0F; - int rmal = (_balance >> 0) & 0x0F; - int vll, vlr; + int lmal = (_balance >> 4) & 0x0F; + int rmal = (_balance >> 0) & 0x0F; + int vll, vlr; - lmal = scale_tab[lmal]; - rmal = scale_tab[rmal]; + lmal = scale_tab[lmal]; + rmal = scale_tab[rmal]; - // Clear buffer + // Clear buffer memset(samples, 0, 2 * sampleCnt * sizeof(int16)); - for(ch = 0; ch < 6; ch++) { - // Only look at enabled channels - if(_channel[ch].control & 0x80) { - int lal = (_channel[ch].balance >> 4) & 0x0F; - int ral = (_channel[ch].balance >> 0) & 0x0F; - int al = _channel[ch].control & 0x1F; - - lal = scale_tab[lal]; - ral = scale_tab[ral]; - - // Calculate volume just as the patent says - vll = (0x1F - lal) + (0x1F - al) + (0x1F - lmal); - if(vll > 0x1F) vll = 0x1F; - - vlr = (0x1F - ral) + (0x1F - al) + (0x1F - rmal); - if(vlr > 0x1F) vlr = 0x1F; - - vll = _volumeTable[vll]; - vlr = _volumeTable[vlr]; - - // Check channel mode - if(_channel[ch].control & 0x40) { - /* DDA mode */ - for(i = 0; i < sampleCnt; i++) { - samples[2*i] += (int16)(vll * (_channel[ch].dda - 16)); - samples[2*i + 1] += (int16)(vlr * (_channel[ch].dda - 16)); - } - } else { - /* Waveform mode */ - uint32 step = _waveFreqTable[_channel[ch].frequency]; - for(i = 0; i < sampleCnt; i += 1) { - int offset; - int16 data; - offset = (_channel[ch].counter >> 12) & 0x1F; - _channel[ch].counter += step; - _channel[ch].counter &= 0x1FFFF; - data = _channel[ch].waveform[offset]; - samples[2*i] += (int16)(vll * (data - 16)); - samples[2*i + 1] += (int16)(vlr * (data - 16)); - } - } - } - } + for(ch = 0; ch < 6; ch++) { + // Only look at enabled channels + if(_channel[ch].control & 0x80) { + int lal = (_channel[ch].balance >> 4) & 0x0F; + int ral = (_channel[ch].balance >> 0) & 0x0F; + int al = _channel[ch].control & 0x1F; + + lal = scale_tab[lal]; + ral = scale_tab[ral]; + + // Calculate volume just as the patent says + vll = (0x1F - lal) + (0x1F - al) + (0x1F - lmal); + if(vll > 0x1F) vll = 0x1F; + + vlr = (0x1F - ral) + (0x1F - al) + (0x1F - rmal); + if(vlr > 0x1F) vlr = 0x1F; + + vll = _volumeTable[vll]; + vlr = _volumeTable[vlr]; + + // Check channel mode + if(_channel[ch].control & 0x40) { + /* DDA mode */ + for(i = 0; i < sampleCnt; i++) { + samples[2*i] += (int16)(vll * (_channel[ch].dda - 16)); + samples[2*i + 1] += (int16)(vlr * (_channel[ch].dda - 16)); + } + } else { + /* Waveform mode */ + uint32 step = _waveFreqTable[_channel[ch].frequency]; + for(i = 0; i < sampleCnt; i += 1) { + int offset; + int16 data; + offset = (_channel[ch].counter >> 12) & 0x1F; + _channel[ch].counter += step; + _channel[ch].counter &= 0x1FFFF; + data = _channel[ch].waveform[offset]; + samples[2*i] += (int16)(vll * (data - 16)); + samples[2*i + 1] += (int16)(vlr * (data - 16)); + } + } + } + } } @@ -466,7 +466,7 @@ void Player_PCE::procA541(channel_t *channel) { channel->controlVec24 = false; channel->controlVec21 = 0; - channel->waveformCtrl = 0x80; + channel->waveformCtrl = 0x80; } // A592 diff --git a/engines/scumm/player_towns.cpp b/engines/scumm/player_towns.cpp index 871bd67546..06f97fd671 100644 --- a/engines/scumm/player_towns.cpp +++ b/engines/scumm/player_towns.cpp @@ -29,33 +29,184 @@ namespace Scumm { -Player_Towns::Player_Towns(ScummEngine *vm, Audio::Mixer *mixer) : _vm(vm) { +Player_Towns::Player_Towns(ScummEngine *vm, bool isVersion2) : _vm(vm), _v2(isVersion2), _numSoundMax(isVersion2 ? 256 : 200) { + memset(_pcmCurrentSound, 0, sizeof(_pcmCurrentSound)); + _unkFlags = 0x33; + _intf = 0; +} + +void Player_Towns::setSfxVolume(int vol) { + if (!_intf) + return; + _intf->setSoundEffectVolume(vol); +} + +int Player_Towns::getSoundStatus(int sound) const { + if (!_intf) + return 0; + for (int i = 1; i < 9; i++) { + if (_pcmCurrentSound[i].index == sound) + return _intf->callback(40, 0x3f + i) ? 1 : 0; + } + return 0; +} + +void Player_Towns::saveLoadWithSerializer(Serializer *ser) { + static const SaveLoadEntry pcmEntries[] = { + MKLINE(PcmCurrentSound, index, sleInt16, VER(81)), + MKLINE(PcmCurrentSound, chan, sleInt16, VER(81)), + MKLINE(PcmCurrentSound, note, sleUint8, VER(81)), + MKLINE(PcmCurrentSound, velo, sleUint8, VER(81)), + MKLINE(PcmCurrentSound, pan, sleUint8, VER(81)), + MKLINE(PcmCurrentSound, paused, sleUint8, VER(81)), + MKLINE(PcmCurrentSound, looping, sleUint8, VER(81)), + MKLINE(PcmCurrentSound, priority, sleUint32, VER(81)), + MKEND() + }; + + for (int i = 1; i < 9; i++) { + if (!_pcmCurrentSound[i].index) + continue; + + if (_intf->callback(40, i + 0x3f)) + continue; + + _intf->callback(39, i + 0x3f); + + _pcmCurrentSound[i].index = 0; + } + + ser->saveLoadArrayOf(_pcmCurrentSound, 9, sizeof(PcmCurrentSound), pcmEntries); +} + +void Player_Towns::restoreAfterLoad() { + for (int i = 1; i < 9; i++) { + if (!_pcmCurrentSound[i].index || _pcmCurrentSound[i].index == 0xffff) + continue; + + uint8 *ptr = _vm->getResourceAddress(rtSound, _pcmCurrentSound[i].index); + if (!ptr) + continue; + + if (_vm->_game.version != 3) + ptr += 2; + + if (ptr[13]) + continue; + + playPcmTrack(_pcmCurrentSound[i].index, ptr + 6, _pcmCurrentSound[i].velo, _pcmCurrentSound[i].pan, _pcmCurrentSound[i].note, _pcmCurrentSound[i].priority); + } +} + +void Player_Towns::playPcmTrack(int sound, const uint8 *data, int velo, int pan, int note, int priority) { + if (!_intf) + return; + + const uint8 *sfxData = data + 16; + + int numChan = _v2 ? 1 : data[14]; + for (int i = 0; i < numChan; i++) { + int chan = allocatePcmChannel(sound, i, priority); + if (!chan) + return; + + _intf->callback(70, _unkFlags); + _intf->callback(3, chan + 0x3f, pan); + _intf->callback(37, chan + 0x3f, note, velo, sfxData); + + _pcmCurrentSound[chan].note = note; + _pcmCurrentSound[chan].velo = velo; + _pcmCurrentSound[chan].pan = pan; + _pcmCurrentSound[chan].paused = 0; + _pcmCurrentSound[chan].looping = READ_LE_UINT32(&sfxData[20]) ? 1 : 0; + + sfxData += (READ_LE_UINT32(&sfxData[12]) + 32); + } +} + +void Player_Towns::stopPcmTrack(int sound) { + if (!_intf) + return; + + for (int i = 1; i < 9; i++) { + if (sound == _pcmCurrentSound[i].index || !sound) { + _intf->callback(39, i + 0x3f); + _pcmCurrentSound[i].index = 0; + } + } +} + +int Player_Towns::allocatePcmChannel(int sound, int sfxChanRelIndex, uint32 priority) { + if (!_intf) + return 0; + + int chan = 0; + + if (_v2 && priority > 255) { + chan = 8; + if (_intf->callback(40, 0x47)) + _intf->callback(39, 0x47); + } else { + for (int i = 8; i; i--) { + if (!_pcmCurrentSound[i].index) { + chan = i; + continue; + } + + if (_intf->callback(40, i + 0x3f)) + continue; + + chan = i; + if (_pcmCurrentSound[chan].index == 0xffff) + _intf->callback(39, chan + 0x3f); + else + _vm->_sound->stopSound(_pcmCurrentSound[chan].index); + } + + if (!chan) { + for (int i = 1; i < 9; i++) { + if (priority >= _pcmCurrentSound[i].priority) + chan = i; + } + if (_pcmCurrentSound[chan].index == 0xffff) + _intf->callback(39, chan + 0x3f); + else + _vm->_sound->stopSound(_pcmCurrentSound[chan].index); + } + } + + if (chan) { + _pcmCurrentSound[chan].index = sound; + _pcmCurrentSound[chan].chan = sfxChanRelIndex; + _pcmCurrentSound[chan].priority = priority; + } + + return chan; +} + +Player_Towns_v1::Player_Towns_v1(ScummEngine *vm, Audio::Mixer *mixer) : Player_Towns(vm, false) { + _soundOverride = 0; _cdaCurrentSound = _eupCurrentSound = _cdaNumLoops = 0; _cdaForceRestart = 0; - memset(_pcmCurrentSound, 0, sizeof(_pcmCurrentSound)); _cdaVolLeft = _cdaVolRight = 0; _eupVolLeft = _eupVolRight = 0; - memset(&_ovrCur, 0, sizeof(SoundOvrParameters)); - _soundOverride = 0; + _eupLooping = false; if (_vm->_game.version == 3) { - _soundOverride = new SoundOvrParameters[200]; - memset(_soundOverride, 0, 200 * sizeof(SoundOvrParameters)); - } - - _eupLooping = false; - _unkFlags = 0x33; + _soundOverride = new SoundOvrParameters[_numSoundMax]; + memset(_soundOverride, 0, _numSoundMax * sizeof(SoundOvrParameters)); + } _driver = new TownsEuphonyDriver(mixer); } -Player_Towns::~Player_Towns() { - delete[] _soundOverride; +Player_Towns_v1::~Player_Towns_v1() { delete _driver; + delete[] _soundOverride; } -bool Player_Towns::init() { +bool Player_Towns_v1::init() { if (!_driver) return false; @@ -63,47 +214,53 @@ bool Player_Towns::init() { return false; _driver->reserveSoundEffectChannels(8); + _intf = _driver->intf(); // Treat all 6 fm channels and all 8 pcm channels as sound effect channels // since music seems to exist as CD audio only in the games which use this // MusicEngine implementation. - _driver->intf()->setSoundEffectChanMask(-1); + _intf->setSoundEffectChanMask(-1); setVolumeCD(255, 255); return true; } -void Player_Towns::setMusicVolume(int vol) { +void Player_Towns_v1::setMusicVolume(int vol) { _driver->setMusicVolume(vol); } -void Player_Towns::setSfxVolume(int vol) { - _driver->setSoundEffectVolume(vol); -} - -void Player_Towns::startSound(int sound) { +void Player_Towns_v1::startSound(int sound) { uint8 *ptr = _vm->getResourceAddress(rtSound, sound); - if (_vm->_game.version != 3) { + if (_vm->_game.version != 3) ptr += 2; - } else if (_soundOverride && sound > 0 && sound < 200) { - memcpy(&_ovrCur, &_soundOverride[sound], sizeof(SoundOvrParameters)); - memset(&_soundOverride[sound], 0, sizeof(SoundOvrParameters)); - } int type = ptr[13]; if (type == 0) { - playPcmTrack(sound, ptr + 6); + uint8 velocity = 0; + uint8 note = 0; + + if (_vm->_game.version == 3) { + velocity = (_soundOverride[sound].vLeft + _soundOverride[sound].vRight); + note = _soundOverride[sound].note; + } + + velocity = velocity ? velocity >> 2 : ptr[14] >> 1; + playPcmTrack(sound, ptr + 6, velocity, 64, note ? note : ptr[50], READ_LE_UINT16(ptr + 10)); + } else if (type == 1) { playEuphonyTrack(sound, ptr + 6); + } else if (type == 2) { playCdaTrack(sound, ptr + 6); } - memset(&_ovrCur, 0, sizeof(SoundOvrParameters)); + + if (_vm->_game.version == 3) + _soundOverride[sound].vLeft = _soundOverride[sound].vRight = _soundOverride[sound].note = 0; } -void Player_Towns::stopSound(int sound) { +void Player_Towns_v1::stopSound(int sound) { if (sound == 0 || sound == _cdaCurrentSound) { _cdaCurrentSound = 0; _vm->_sound->stopCD(); @@ -119,7 +276,7 @@ void Player_Towns::stopSound(int sound) { stopPcmTrack(sound); } -void Player_Towns::stopAllSounds() { +void Player_Towns_v1::stopAllSounds() { _cdaCurrentSound = 0; _vm->_sound->stopCD(); _vm->_sound->stopCDTimer(); @@ -131,19 +288,15 @@ void Player_Towns::stopAllSounds() { stopPcmTrack(0); } -int Player_Towns::getSoundStatus(int sound) const { +int Player_Towns_v1::getSoundStatus(int sound) const { if (sound == _cdaCurrentSound) return _vm->_sound->pollCD(); if (sound == _eupCurrentSound) return _driver->parserIsPlaying() ? 1 : 0; - for (int i = 1; i < 9; i++) { - if (_pcmCurrentSound[i].index == sound) - return _driver->soundEffectIsPlaying(i + 0x3f) ? 1 : 0; - } - return 0; + return Player_Towns::getSoundStatus(sound); } -int32 Player_Towns::doCommand(int numargs, int args[]) { +int32 Player_Towns_v1::doCommand(int numargs, int args[]) { int32 res = 0; switch (args[0]) { @@ -176,40 +329,40 @@ int32 Player_Towns::doCommand(int numargs, int args[]) { break; default: - warning("Player_Towns::doCommand: Unknown command %d", args[0]); + warning("Player_Towns_v1::doCommand: Unknown command %d", args[0]); break; } return res; } -void Player_Towns::setVolumeCD(int left, int right) { +void Player_Towns_v1::setVolumeCD(int left, int right) { _cdaVolLeft = left & 0xff; _cdaVolRight = right & 0xff; _driver->setOutputVolume(1, left >> 1, right >> 1); } -void Player_Towns::setSoundVolume(int sound, int left, int right) { - if (_soundOverride && sound > 0 && sound < 200) { +void Player_Towns_v1::setSoundVolume(int sound, int left, int right) { + if (_soundOverride && sound > 0 && sound < _numSoundMax) { _soundOverride[sound].vLeft = left; _soundOverride[sound].vRight = right; } } -void Player_Towns::setSoundNote(int sound, int note) { - if (_soundOverride && sound > 0 && sound < 200) +void Player_Towns_v1::setSoundNote(int sound, int note) { + if (_soundOverride && sound > 0 && sound < _numSoundMax) _soundOverride[sound].note = note; } -void Player_Towns::saveLoadWithSerializer(Serializer *ser) { +void Player_Towns_v1::saveLoadWithSerializer(Serializer *ser) { _cdaCurrentSoundTemp = (_vm->_sound->pollCD() && _cdaNumLoops > 1) ? _cdaCurrentSound & 0xff : 0; _cdaNumLoopsTemp = _cdaNumLoops & 0xff; static const SaveLoadEntry cdEntries[] = { - MKLINE(Player_Towns, _cdaCurrentSoundTemp, sleUint8, VER(81)), - MKLINE(Player_Towns, _cdaNumLoopsTemp, sleUint8, VER(81)), - MKLINE(Player_Towns, _cdaVolLeft, sleUint8, VER(81)), - MKLINE(Player_Towns, _cdaVolRight, sleUint8, VER(81)), + MKLINE(Player_Towns_v1, _cdaCurrentSoundTemp, sleUint8, VER(81)), + MKLINE(Player_Towns_v1, _cdaNumLoopsTemp, sleUint8, VER(81)), + MKLINE(Player_Towns_v1, _cdaVolLeft, sleUint8, VER(81)), + MKLINE(Player_Towns_v1, _cdaVolRight, sleUint8, VER(81)), MKEND() }; @@ -219,43 +372,19 @@ void Player_Towns::saveLoadWithSerializer(Serializer *ser) { _eupCurrentSound = 0; static const SaveLoadEntry eupEntries[] = { - MKLINE(Player_Towns, _eupCurrentSound, sleUint8, VER(81)), - MKLINE(Player_Towns, _eupLooping, sleUint8, VER(81)), - MKLINE(Player_Towns, _eupVolLeft, sleUint8, VER(81)), - MKLINE(Player_Towns, _eupVolRight, sleUint8, VER(81)), + MKLINE(Player_Towns_v1, _eupCurrentSound, sleUint8, VER(81)), + MKLINE(Player_Towns_v1, _eupLooping, sleUint8, VER(81)), + MKLINE(Player_Towns_v1, _eupVolLeft, sleUint8, VER(81)), + MKLINE(Player_Towns_v1, _eupVolRight, sleUint8, VER(81)), MKEND() }; ser->saveLoadEntries(this, eupEntries); - static const SaveLoadEntry pcmEntries[] = { - MKLINE(PcmCurrentSound, index, sleInt16, VER(81)), - MKLINE(PcmCurrentSound, chan, sleInt16, VER(81)), - MKLINE(PcmCurrentSound, note, sleUint8, VER(81)), - MKLINE(PcmCurrentSound, velo, sleUint8, VER(81)), - MKLINE(PcmCurrentSound, pan, sleUint8, VER(81)), - MKLINE(PcmCurrentSound, paused, sleUint8, VER(81)), - MKLINE(PcmCurrentSound, looping, sleUint8, VER(81)), - MKLINE(PcmCurrentSound, priority, sleUint32, VER(81)), - MKEND() - }; - - for (int i = 1; i < 9; i++) { - if (!_pcmCurrentSound[i].index) - continue; - - if (_driver->soundEffectIsPlaying(i + 0x3f)) - continue; - - _driver->stopSoundEffect(i + 0x3f); - - _pcmCurrentSound[i].index = 0; - } - - ser->saveLoadArrayOf(_pcmCurrentSound, 9, sizeof(PcmCurrentSound), pcmEntries); + Player_Towns::saveLoadWithSerializer(ser); } -void Player_Towns::restoreAfterLoad() { +void Player_Towns_v1::restoreAfterLoad() { setVolumeCD(_cdaVolLeft, _cdaVolRight); if (_cdaCurrentSoundTemp) { @@ -281,67 +410,10 @@ void Player_Towns::restoreAfterLoad() { } } - for (int i = 1; i < 9; i++) { - if (!_pcmCurrentSound[i].index) - continue; - - uint8 *ptr = _vm->getResourceAddress(rtSound, _pcmCurrentSound[i].index); - if (!ptr) - continue; - - if (_vm->_game.version != 3) - ptr += 2; - - if (ptr[13]) - continue; - - playPcmTrack(_pcmCurrentSound[i].index, ptr + 6, _pcmCurrentSound[i].velo, _pcmCurrentSound[i].pan, _pcmCurrentSound[i].note); - } -} - -int Player_Towns::getNextFreePcmChannel(int sound, int sfxChanRelIndex) { - int chan = 0; - for (int i = 8; i; i--) { - if (!_pcmCurrentSound[i].index) { - chan = i; - continue; - } - - if (_driver->soundEffectIsPlaying(i + 0x3f)) - continue; - - chan = i; - _vm->_sound->stopSound(_pcmCurrentSound[chan].index); - } - - if (!chan) { - uint16 l = 0xffff; - uint8 *ptr = 0; - for (int i = 8; i; i--) { - ptr = _vm->getResourceAddress(rtSound, _pcmCurrentSound[i].index) + 6; - uint16 a = READ_LE_UINT16(ptr + 10); - if (a <= l) { - chan = i; - l = a; - } - } - - ptr = _vm->getResourceAddress(rtSound, sound) + 6; - if (l <= READ_LE_UINT16(ptr + 10)) - _vm->_sound->stopSound(_pcmCurrentSound[chan].index); - else - chan = 0; - } - - if (chan) { - _pcmCurrentSound[chan].index = sound; - _pcmCurrentSound[chan].chan = sfxChanRelIndex; - } - - return chan; + Player_Towns::restoreAfterLoad(); } -void Player_Towns::restartLoopingSounds() { +void Player_Towns_v1::restartLoopingSounds() { if (_cdaNumLoops && !_cdaForceRestart) _cdaForceRestart = 1; @@ -368,7 +440,7 @@ void Player_Towns::restartLoopingSounds() { _driver->intf()->callback(73, 1); } -void Player_Towns::startSoundEx(int sound, int velo, int pan, int note) { +void Player_Towns_v1::startSoundEx(int sound, int velo, int pan, int note) { uint8 *ptr = _vm->getResourceAddress(rtSound, sound) + 2; if (pan > 99) @@ -376,6 +448,7 @@ void Player_Towns::startSoundEx(int sound, int velo, int pan, int note) { velo = velo ? (velo * ptr[14] + 50) / 100 : ptr[14]; velo = CLIP(velo, 1, 255); + uint16 pri = READ_LE_UINT16(ptr + 10); if (ptr[13] == 0) { velo >>= 1; @@ -385,7 +458,7 @@ void Player_Towns::startSoundEx(int sound, int velo, int pan, int note) { pan = pan ? (((pan << 7) - pan) + 50) / 100 : 64; - playPcmTrack(sound, ptr + 6, velo, pan, note); + playPcmTrack(sound, ptr + 6, velo ? velo : ptr[14] >> 1, pan, note ? note : ptr[50], pri); } else if (ptr[13] == 2) { int volLeft = velo; @@ -405,7 +478,7 @@ void Player_Towns::startSoundEx(int sound, int velo, int pan, int note) { } } -void Player_Towns::stopSoundSuspendLooping(int sound) { +void Player_Towns_v1::stopSoundSuspendLooping(int sound) { if (!sound) { return; } else if (sound == _cdaCurrentSound) { @@ -426,7 +499,7 @@ void Player_Towns::stopSoundSuspendLooping(int sound) { } } -void Player_Towns::playEuphonyTrack(int sound, const uint8 *data) { +void Player_Towns_v1::playEuphonyTrack(int sound, const uint8 *data) { const uint8 *pos = data + 16; const uint8 *src = pos + data[14] * 48; const uint8 *trackData = src + 150; @@ -451,9 +524,9 @@ void Player_Towns::playEuphonyTrack(int sound, const uint8 *data) { _driver->intf()->callback(4, i, i); } - _eupVolLeft = _ovrCur.vLeft; - _eupVolRight = _ovrCur.vRight; - int lvl = _ovrCur.vLeft + _ovrCur.vRight; + _eupVolLeft = _soundOverride[sound].vLeft; + _eupVolRight = _soundOverride[sound].vRight; + int lvl = _soundOverride[sound].vLeft + _soundOverride[sound].vRight; if (!lvl) lvl = data[8] + data[9]; lvl >>= 2; @@ -474,58 +547,21 @@ void Player_Towns::playEuphonyTrack(int sound, const uint8 *data) { _eupCurrentSound = sound; } -void Player_Towns::playPcmTrack(int sound, const uint8 *data, int velo, int pan, int note) { - const uint8 *ptr = data; - const uint8 *sfxData = ptr + 16; - - int note2, velocity; - - if (velo) - velocity = velo; - else if (_ovrCur.vLeft + _ovrCur.vRight) - velocity = (_ovrCur.vLeft + _ovrCur.vRight) >> 2; - else - velocity = ptr[8] >> 1; - - int numChan = ptr[14]; - for (int i = 0; i < numChan; i++) { - int chan = getNextFreePcmChannel(sound, i); - if (!chan) - return; - - _driver->intf()->callback(70, _unkFlags); - _driver->chanPanPos(chan + 0x3f, pan); - - if (note) - note2 = note; - else if (_ovrCur.note) - note2 = _ovrCur.note; - else - note2 = sfxData[28]; - - _driver->playSoundEffect(chan + 0x3f, note2, velocity, sfxData); - - _pcmCurrentSound[chan].note = note2; - _pcmCurrentSound[chan].velo = velocity; - _pcmCurrentSound[chan].pan = pan; - _pcmCurrentSound[chan].paused = 0; - _pcmCurrentSound[chan].looping = READ_LE_UINT32(&sfxData[20]) ? 1 : 0; - - sfxData += (READ_LE_UINT32(&sfxData[12]) + 32); - } -} - -void Player_Towns::playCdaTrack(int sound, const uint8 *data, bool skipTrackVelo) { +void Player_Towns_v1::playCdaTrack(int sound, const uint8 *data, bool skipTrackVelo) { const uint8 *ptr = data; if (!sound) return; if (!skipTrackVelo) { - if (_ovrCur.vLeft + _ovrCur.vRight) - setVolumeCD(_ovrCur.vLeft, _ovrCur.vRight); - else + if (_vm->_game.version == 3) { + if (_soundOverride[sound].vLeft + _soundOverride[sound].vRight) + setVolumeCD(_soundOverride[sound].vLeft, _soundOverride[sound].vRight); + else + setVolumeCD(ptr[8], ptr[9]); + } else { setVolumeCD(ptr[8], ptr[9]); + } } if (sound == _cdaCurrentSound && _vm->_sound->pollCD() == 1) @@ -543,14 +579,170 @@ void Player_Towns::playCdaTrack(int sound, const uint8 *data, bool skipTrackVelo _cdaCurrentSound = sound; } -void Player_Towns::stopPcmTrack(int sound) { - for (int i = 1; i < 9; i++) { - if (sound == _pcmCurrentSound[i].index || !sound) { - _driver->stopSoundEffect(i + 0x3f); - _pcmCurrentSound[i].index = 0; +Player_Towns_v2::Player_Towns_v2(ScummEngine *vm, IMuse *imuse, Audio::Mixer *mixer, bool disposeIMuse) : Player_Towns(vm, true), _imuse(imuse), _imuseDispose(disposeIMuse) { + _soundOverride = new SoundOvrParameters[_numSoundMax]; + memset(_soundOverride, 0, _numSoundMax * sizeof(SoundOvrParameters)); + _sblData = 0; + _intf = new TownsAudioInterface(mixer, 0); +} + +Player_Towns_v2::~Player_Towns_v2() { + delete _intf; + + if (_imuseDispose) + delete _imuse; + + delete[] _sblData; + delete[] _soundOverride; +} + +bool Player_Towns_v2::init() { + if (!_intf) + return false; + + if (!_intf->init()) + return false; + + _intf->callback(33, 8); + _intf->setSoundEffectChanMask(~0x3f); + + return true; +} + +void Player_Towns_v2::setMusicVolume(int vol) { + _imuse->setMusicVolume(vol); +} + +int Player_Towns_v2::getSoundStatus(int sound) const { + if (_soundOverride[sound].type == 7) + return Player_Towns::getSoundStatus(sound); + return _imuse->getSoundStatus(sound); +} + +void Player_Towns_v2::startSound(int sound) { + uint8 *ptr = _vm->getResourceAddress(rtSound, sound); + + if (READ_BE_UINT32(ptr) == MKID_BE('TOWS')) { + _soundOverride[sound].type = 7; + uint8 velo = _soundOverride[sound].velo ? _soundOverride[sound].velo - 1: (ptr[10] + ptr[11] + 1) >> 1; + uint8 pan = _soundOverride[sound].pan ? _soundOverride[sound].pan - 1 : 64; + uint8 pri = ptr[9]; + _soundOverride[sound].velo = _soundOverride[sound].pan = 0; + playPcmTrack(sound, ptr + 8, velo, pan, ptr[52], pri); + + } else if (READ_BE_UINT32(ptr) == MKID_BE('SBL ')) { + _soundOverride[sound].type = 5; + playVocTrack(ptr + 27); + + } else { + _soundOverride[sound].type = 3; + _imuse->startSound(sound); + } +} + +void Player_Towns_v2::stopSound(int sound) { + if (_soundOverride[sound].type == 7) { + stopPcmTrack(sound); + } else { + _imuse->stopSound(sound); + } +} + +void Player_Towns_v2::stopAllSounds() { + stopPcmTrack(0); + _imuse->stopAllSounds(); +} + +int32 Player_Towns_v2::doCommand(int numargs, int args[]) { + int32 res = -1; + uint8 *ptr = 0; + + switch (args[0]) { + case 8: + startSound(args[1]); + res = 0; + break; + + case 9: + case 15: + stopSound(args[1]); + res = 0; + break; + + case 11: + stopPcmTrack(0); + break; + + case 13: + res = getSoundStatus(args[1]); + break; + + case 258: + if (_soundOverride[args[1]].type == 0) { + ptr = _vm->getResourceAddress(rtSound, args[1]); + if (READ_BE_UINT32(ptr) == MKID_BE('TOWS')) + _soundOverride[args[1]].type = 7; + } + if (_soundOverride[args[1]].type == 7) { + _soundOverride[args[1]].velo = args[2] + 1; + res = 0; } + break; + + case 259: + if (_soundOverride[args[1]].type == 0) { + ptr = _vm->getResourceAddress(rtSound, args[1]); + if (READ_BE_UINT32(ptr) == MKID_BE('TOWS')) + _soundOverride[args[1]].type = 7; + } + if (_soundOverride[args[1]].type == 7) { + _soundOverride[args[1]].pan = 64 - CLIP<int>(args[2], -63, 63); + res = 0; + } + break; + + default: + break; } + + if (res == -1) + return _imuse->doCommand(numargs, args); + + return res; } -} // End of namespace Scumm +void Player_Towns_v2::saveLoadWithSerializer(Serializer *ser) { + if (ser->getVersion() >= 83) + Player_Towns::saveLoadWithSerializer(ser); +} + +void Player_Towns_v2::playVocTrack(const uint8 *data) { + static const uint8 header[] = { + 0x54, 0x61, 0x6C, 0x6B, 0x69, 0x65, 0x20, 0x20, + 0x78, 0x56, 0x34, 0x12, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x36, 0x04, 0x00, 0x00, 0x3C, 0x00, 0x00, 0x00 + }; + + uint32 len = (READ_LE_UINT32(data) >> 8) - 2; + + int chan = allocatePcmChannel(0xffff, 0, 0x1000); + if (!chan) + return; + + delete[] _sblData; + _sblData = new uint8[len + 32]; + + memcpy(_sblData, header, 32); + WRITE_LE_UINT32(_sblData + 12, len); + const uint8 *src = data + 6; + uint8 *dst = _sblData + 32; + for (uint32 i = 0; i < len; i++) + *dst++ = *src & 0x80 ? (*src++ & 0x7f) : -*src++; + + _intf->callback(37, 0x3f + chan, 60, 127, _sblData); + _pcmCurrentSound[chan].paused = 0; +} + +} // End of namespace Scumm diff --git a/engines/scumm/player_towns.h b/engines/scumm/player_towns.h index 6d87c93c09..e5023d25c2 100644 --- a/engines/scumm/player_towns.h +++ b/engines/scumm/player_towns.h @@ -27,20 +27,68 @@ #define SCUMM_PLAYER_TOWNS_H #include "scumm/scumm.h" -#include "scumm/music.h" +#include "scumm/imuse/imuse.h" #include "sound/softsynth/fmtowns_pc98/towns_euphony.h" namespace Scumm { class Player_Towns : public MusicEngine { public: - Player_Towns(ScummEngine *vm, Audio::Mixer *mixer); - virtual ~Player_Towns(); + Player_Towns(ScummEngine *vm, bool isVersion2); + virtual ~Player_Towns() {} + + virtual bool init() = 0; + + void setSfxVolume(int vol); + + int getSoundStatus(int sound) const; + + virtual int32 doCommand(int numargs, int args[]) = 0; + + virtual void saveLoadWithSerializer(Serializer *ser); + virtual void restoreAfterLoad(); + + // version 1 specific + virtual int getCurrentCdaSound() { return 0; } + virtual int getCurrentCdaVolume() { return 0; } + virtual void setVolumeCD(int left, int right) {} + virtual void setSoundVolume(int sound, int left, int right) {} + virtual void setSoundNote(int sound, int note) {} + +protected: + void playPcmTrack(int sound, const uint8 *data, int velo = 0, int pan = 64, int note = 0, int priority = 0); + void stopPcmTrack(int sound); + + int allocatePcmChannel(int sound, int sfxChanRelIndex, uint32 priority); + + struct PcmCurrentSound { + uint16 index; + uint16 chan; + uint8 note; + uint8 velo; + uint8 pan; + uint8 paused; + uint8 looping; + uint32 priority; + } _pcmCurrentSound[9]; + + uint8 _unkFlags; + + TownsAudioInterface *_intf; + ScummEngine *_vm; + + const int _numSoundMax; + const bool _v2; +}; + +class Player_Towns_v1 : public Player_Towns { +public: + Player_Towns_v1(ScummEngine *vm, Audio::Mixer *mixer); + ~Player_Towns_v1(); bool init(); void setMusicVolume(int vol); - void setSfxVolume(int vol); void startSound(int sound); void stopSound(int sound); void stopAllSounds(); @@ -49,7 +97,7 @@ public: int getCurrentCdaSound() { return _cdaCurrentSound; } int getCurrentCdaVolume() { return (_cdaVolLeft + _cdaVolRight + 1) >> 1; } - virtual int32 doCommand(int numargs, int args[]); + int32 doCommand(int numargs, int args[]); void setVolumeCD(int left, int right); void setSoundVolume(int sound, int left, int right); @@ -60,23 +108,14 @@ public: TownsEuphonyDriver *driver() { return _driver; } -protected: - virtual int getNextFreePcmChannel(int sound, int sfxChanRelIndex); - private: void restartLoopingSounds(); void startSoundEx(int sound, int velo, int pan, int note); void stopSoundSuspendLooping(int sound); void playEuphonyTrack(int sound, const uint8 *data); - void playPcmTrack(int sound, const uint8 *data, int velo = 0, int pan = 64, int note = 0); void playCdaTrack(int sound, const uint8 *data, bool skipTrackVelo = false); - void stopPcmTrack(int sound); - - uint8 _cdaVolLeft; - uint8 _cdaVolRight; - struct SoundOvrParameters { uint8 vLeft; uint8 vRight; @@ -84,21 +123,10 @@ private: }; SoundOvrParameters *_soundOverride; - SoundOvrParameters _ovrCur; - - uint8 _unkFlags; - - struct PcmCurrentSound { - uint16 index; - uint16 chan; - uint8 note; - uint8 velo; - uint8 pan; - uint8 paused; - uint8 looping; - uint32 priority; - } _pcmCurrentSound[9]; + uint8 _cdaVolLeft; + uint8 _cdaVolRight; + uint8 _eupCurrentSound; uint8 _eupLooping; uint8 _eupVolLeft; @@ -112,7 +140,40 @@ private: uint8 _cdaNumLoopsTemp; TownsEuphonyDriver *_driver; - ScummEngine *_vm; +}; + +class Player_Towns_v2 : public Player_Towns { +public: + Player_Towns_v2(ScummEngine *vm, IMuse *imuse, Audio::Mixer *mixer, bool disposeIMuse); + ~Player_Towns_v2(); + + bool init(); + + void setMusicVolume(int vol); + + int getSoundStatus(int sound) const; + void startSound(int sound); + void stopSound(int sound); + void stopAllSounds(); + + int32 doCommand(int numargs, int args[]); + + void saveLoadWithSerializer(Serializer *ser); + +private: + void playVocTrack(const uint8 *data); + + struct SoundOvrParameters { + uint8 velo; + uint8 pan; + uint8 type; + }; + + SoundOvrParameters *_soundOverride; + + uint8 *_sblData; + IMuse *_imuse; + const bool _imuseDispose; }; } // End of namespace Scumm diff --git a/engines/scumm/player_v2.cpp b/engines/scumm/player_v2.cpp index c1c9b3930e..60103b20fb 100644 --- a/engines/scumm/player_v2.cpp +++ b/engines/scumm/player_v2.cpp @@ -23,8 +23,6 @@ * */ - -#include "engines/engine.h" #include "scumm/player_v2.h" #include "scumm/scumm.h" #include "sound/mididrv.h" @@ -32,373 +30,51 @@ namespace Scumm { -#define FREQ_HZ 236 // Don't change! - #define SPK_DECAY 0xa000 /* Depends on sample rate */ #define PCJR_DECAY 0xa000 /* Depends on sample rate */ -#define FIXP_SHIFT 16 -#define MAX_OUTPUT 0x7fff - #define NG_PRESET 0x0f35 /* noise generator preset */ #define FB_WNOISE 0x12000 /* feedback for white noise */ #define FB_PNOISE 0x08000 /* feedback for periodic noise */ -const uint8 note_lengths[] = { - 0, - 0, 0, 2, - 0, 3, 4, - 5, 6, 8, - 9, 12, 16, - 18, 24, 32, - 36, 48, 64, - 72, 96 -}; - -static const uint16 hull_offsets[] = { - 0, 12, 24, 36, 48, 60, - 72, 88, 104, 120, 136, 256, - 152, 164, 180 -}; - -static const int16 hulls[] = { - // hull 0 - 3, -1, 0, 0, 0, 0, 0, 0, - 0, -1, 0, 0, - // hull 1 (staccato) - 3, -1, 0, 32, 0, -1, 0, 0, - 0, -1, 0, 0, - // hull 2 (legato) - 3, -1, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, - // hull 3 (staccatissimo) - 3, -1, 0, 2, 0, -1, 0, 0, - 0, -1, 0, 0, - // hull 4 - 3, -1, 0, 6, 0, -1, 0, 0, - 0, -1, 0, 0, - // hull 5 - 3, -1, 0, 16, 0, -1, 0, 0, - 0, -1, 0, 0, - // hull 6 - (int16) 60000, -1, -1000, 20, 0, 0, 0, 0, - (int16) 40000, -1, -5000, 5, 0, -1, 0, 0, - // hull 7 - (int16) 50000, -1, 0, 8, 30000, -1, 0, 0, - 28000, -1, -5000, 5, 0, -1, 0, 0, - // hull 8 - (int16) 60000, -1, -2000, 16, 0, 0, 0, 0, - 28000, -1, -6000, 5, 0, -1, 0, 0, - // hull 9 - (int16) 55000, -1, 0, 8, (int16) 35000, -1, 0, 0, - (int16) 40000, -1, -2000, 10, 0, -1, 0, 0, - // hull 10 - (int16) 60000, -1, 0, 4, -2000, 8, 0, 0, - (int16) 40000, -1, -6000, 5, 0, -1, 0, 0, - // hull 12 - 0, -1, 150, 340, -150, 340, 0, -1, - 0, -1, 0, 0, - // hull 13 == 164 - 20000, -1, 4000, 7, 1000, 15, 0, 0, - (int16) 35000, -1, -2000, 15, 0, -1, 0, 0, - - // hull 14 == 180 - (int16) 35000, -1, 500, 20, 0, 0, 0, 0, - (int16) 45000, -1, -500, 60, 0, -1, 0, 0, - - // hull misc = 196 - (int16) 44000, -1, -4400, 10, 0, -1, 0, 0, - 0, -1, 0, 0, - - (int16) 53000, -1, -5300, 10, 0, -1, 0, 0, - 0, -1, 0, 0, - - (int16) 63000, -1, -6300, 10, 0, -1, 0, 0, - 0, -1, 0, 0, - - (int16) 44000, -1, -1375, 32, 0, -1, 0, 0, - 0, -1, 0, 0, - - (int16) 53000, -1, -1656, 32, 0, -1, 0, 0, - 0, -1, 0, 0, - - // hull 11 == 256 - (int16) 63000, -1, -1968, 32, 0, -1, 0, 0, - 0, -1, 0, 0, - - (int16) 44000, -1, - 733, 60, 0, -1, 0, 0, - 0, -1, 0, 0, - - (int16) 53000, -1, - 883, 60, 0, -1, 0, 0, - 0, -1, 0, 0, - - (int16) 63000, -1, -1050, 60, 0, -1, 0, 0, - 0, -1, 0, 0, - - (int16) 44000, -1, - 488, 90, 0, -1, 0, 0, - 0, -1, 0, 0, - - (int16) 53000, -1, - 588, 90, 0, -1, 0, 0, - 0, -1, 0, 0, - - (int16) 63000, -1, - 700, 90, 0, -1, 0, 0, - 0, -1, 0, 0 -}; - -static const uint16 freqmod_lengths[] = { - 0x1000, 0x1000, 0x20, 0x2000, 0x1000 -}; - -static const uint16 freqmod_offsets[] = { - 0, 0x100, 0x200, 0x302, 0x202 -}; - -static const int8 freqmod_table[0x502] = { - 0, 3, 6, 9, 12, 15, 18, 21, - 24, 27, 30, 33, 36, 39, 42, 45, - 48, 51, 54, 57, 59, 62, 65, 67, - 70, 73, 75, 78, 80, 82, 85, 87, - 89, 91, 94, 96, 98, 100, 102, 103, - 105, 107, 108, 110, 112, 113, 114, 116, - 117, 118, 119, 120, 121, 122, 123, 123, - 124, 125, 125, 126, 126, 126, 126, 126, - 126, 126, 126, 126, 126, 126, 125, 125, - 124, 123, 123, 122, 121, 120, 119, 118, - 117, 116, 114, 113, 112, 110, 108, 107, - 105, 103, 102, 100, 98, 96, 94, 91, - 89, 87, 85, 82, 80, 78, 75, 73, - 70, 67, 65, 62, 59, 57, 54, 51, - 48, 45, 42, 39, 36, 33, 30, 27, - 24, 21, 18, 15, 12, 9, 6, 3, - 0, -3, -6, -9, -12, -15, -18, -21, - -24, -27, -30, -33, -36, -39, -42, -45, - -48, -51, -54, -57, -59, -62, -65, -67, - -70, -73, -75, -78, -80, -82, -85, -87, - -89, -91, -94, -96, -98,-100,-102,-103, - -105,-107,-108,-110,-112,-113,-114,-116, - -117,-118,-119,-120,-121,-122,-123,-123, - -124,-125,-125,-126,-126,-126,-126,-126, - -126,-126,-126,-126,-126,-126,-125,-125, - -124,-123,-123,-122,-121,-120,-119,-118, - -117,-116,-114,-113,-112,-110,-108,-107, - -105,-103,-102,-100, -98, -96, -94, -91, - -89, -87, -85, -82, -80, -78, -75, -73, - -70, -67, -65, -62, -59, -57, -54, -51, - -48, -45, -42, -39, -36, -33, -30, -27, - -24, -21, -18, -15, -12, -9, -6, -3, - - 0, 1, 2, 3, 4, 5, 6, 7, - 8, 9, 10, 11, 12, 13, 14, 15, - 16, 17, 18, 19, 20, 21, 22, 23, - 24, 25, 26, 27, 28, 29, 30, 31, - 32, 33, 34, 35, 36, 37, 38, 39, - 40, 41, 42, 43, 44, 45, 46, 47, - 48, 49, 50, 51, 52, 53, 54, 55, - 56, 57, 58, 59, 60, 61, 62, 63, - 64, 65, 66, 67, 68, 69, 70, 71, - 72, 73, 74, 75, 76, 77, 78, 79, - 80, 81, 82, 83, 84, 85, 86, 87, - 88, 89, 90, 91, 92, 93, 94, 95, - 96, 97, 98, 99, 100, 101, 102, 103, - 104, 105, 106, 107, 108, 109, 110, 111, - 112, 113, 114, 115, 116, 117, 118, 119, - 120, 121, 122, 123, 124, 125, 126, 127, - -128,-127,-126,-125,-124,-123,-122,-121, - -120,-119,-118,-117,-116,-115,-114,-113, - -112,-111,-110,-109,-108,-107,-106,-105, - -104,-103,-102,-101,-100, -99, -98, -97, - -96, -95, -94, -93, -92, -91, -90, -89, - -88, -87, -86, -85, -84, -83, -82, -81, - -80, -79, -78, -77, -76, -75, -74, -73, - -72, -71, -70, -69, -68, -67, -66, -65, - -64, -63, -62, -61, -60, -59, -58, -57, - -56, -55, -54, -53, -52, -51, -50, -49, - -48, -47, -46, -45, -44, -43, -42, -41, - -40, -39, -38, -37, -36, -35, -34, -33, - -32, -31, -30, -29, -28, -27, -26, -25, - -24, -23, -22, -21, -20, -19, -18, -17, - -16, -15, -14, -13, -12, -11, -10, -9, - -8, -7, -6, -5, -4, -3, -2, -1, - - -120, 120, - - -120,-120,-120,-120,-120,-120,-120,-120, - -120,-120,-120,-120,-120,-120,-120,-120, - -120,-120,-120,-120,-120,-120,-120,-120, - -120,-120,-120,-120,-120,-120,-120,-120, - -120,-120,-120,-120,-120,-120,-120,-120, - -120,-120,-120,-120,-120,-120,-120,-120, - -120,-120,-120,-120,-120,-120,-120,-120, - -120,-120,-120,-120,-120,-120,-120,-120, - -120,-120,-120,-120,-120,-120,-120,-120, - -120,-120,-120,-120,-120,-120,-120,-120, - -120,-120,-120,-120,-120,-120,-120,-120, - -120,-120,-120,-120,-120,-120,-120,-120, - -120,-120,-120,-120,-120,-120,-120,-120, - -120,-120,-120,-120,-120,-120,-120,-120, - -120,-120,-120,-120,-120,-120,-120,-120, - -120,-120,-120,-120,-120,-120,-120,-120, - 120, 120, 120, 120, 120, 120, 120, 120, - 120, 120, 120, 120, 120, 120, 120, 120, - 120, 120, 120, 120, 120, 120, 120, 120, - 120, 120, 120, 120, 120, 120, 120, 120, - 120, 120, 120, 120, 120, 120, 120, 120, - 120, 120, 120, 120, 120, 120, 120, 120, - 120, 120, 120, 120, 120, 120, 120, 120, - 120, 120, 120, 120, 120, 120, 120, 120, - 120, 120, 120, 120, 120, 120, 120, 120, - 120, 120, 120, 120, 120, 120, 120, 120, - 120, 120, 120, 120, 120, 120, 120, 120, - 120, 120, 120, 120, 120, 120, 120, 120, - 120, 120, 120, 120, 120, 120, 120, 120, - 120, 120, 120, 120, 120, 120, 120, 120, - 120, 120, 120, 120, 120, 120, 120, 120, - 120, 120, 120, 120, 120, 120, 120, 120, - - 41, 35, -66,-124, -31, 108, -42, -82, - 82,-112, 73, -15, -15, -69, -23, -21, - -77, -90, -37, 60,-121, 12, 62,-103, - 36, 94, 13, 28, 6, -73, 71, -34, - -77, 18, 77, -56, 67, -69,-117, -90, - 31, 3, 90, 125, 9, 56, 37, 31, - 93, -44, -53, -4,-106, -11, 69, 59, - 19, 13,-119, 10, 28, -37, -82, 50, - 32,-102, 80, -18, 64, 120, 54, -3, - 18, 73, 50, -10, -98, 125, 73, -36, - -83, 79, 20, -14, 68, 64, 102, -48, - 107, -60, 48, -73, 50, 59, -95, 34, - -10, 34,-111, -99, -31,-117, 31, -38, - -80, -54,-103, 2, -71, 114, -99, 73, - 44,-128, 126, -59,-103, -43, -23,-128, - -78, -22, -55, -52, 83, -65, 103, -42, - -65, 20, -42, 126, 45, -36,-114, 102, - -125, -17, 87, 73, 97, -1, 105,-113, - 97, -51, -47, 30, -99,-100, 22, 114, - 114, -26, 29, -16,-124, 79, 74, 119, - 2, -41, -24, 57, 44, 83, -53, -55, - 18, 30, 51, 116, -98, 12, -12, -43, - -44, -97, -44, -92, 89, 126, 53, -49, - 50, 34, -12, -52, -49, -45,-112, 45, - 72, -45,-113, 117, -26, -39, 29, 42, - -27, -64, -9, 43, 120,-127,-121, 68, - 14, 95, 80, 0, -44, 97,-115, -66, - 123, 5, 21, 7, 59, 51,-126, 31, - 24, 112,-110, -38, 100, 84, -50, -79, - -123, 62, 105, 21, -8, 70, 106, 4, - -106, 115, 14, -39, 22, 47, 103, 104, - -44, -9, 74, 74, -48, 87, 104, 118, - -6, 22, -69, 17, -83, -82, 36,-120, - 121, -2, 82, -37, 37, 67, -27, 60, - -12, 69, -45, -40, 40, -50, 11, -11, - -59, 96, 89, 61,-105, 39,-118, 89, - 118, 45, -48, -62, -55, -51, 104, -44, - 73, 106, 121, 37, 8, 97, 64, 20, - -79, 59, 106, -91, 17, 40, -63,-116, - -42, -87, 11,-121,-105,-116, 47, -15, - 21, 29,-102,-107, -63,-101, -31, -64, - 126, -23, -88,-102, -89,-122, -62, -75, - 84, -65,-102, -25, -39, 35, -47, 85, - -112, 56, 40, -47, -39, 108, -95, 102, - 94, 78, -31, 48,-100, -2, -39, 113, - -97, -30, -91, -30, 12,-101, -76, 71, - 101, 56, 42, 70,-119, -87,-126, 121, - 122, 118, 120, -62, 99, -79, 38, -33, - -38, 41, 109, 62, 98, -32,-106, 18, - 52, -65, 57, -90, 63,-119, 94, -15, - 109, 14, -29, 108, 40, -95, 30, 32, - 29, -53, -62, 3, 63, 65, 7,-124, - 15, 20, 5, 101, 27, 40, 97, -55, - -59, -25, 44,-114, 70, 54, 8, -36, - -13, -88,-115, -2, -66, -14, -21, 113, - -1, -96, -48, 59, 117, 6,-116, 126, - -121, 120, 115, 77, -48, -66,-126, -66, - -37, -62, 70, 65, 43,-116, -6, 48, - 127, 112, -16, -89, 84,-122, 50,-107, - -86, 91, 104, 19, 11, -26, -4, -11, - -54, -66, 125, -97,-119,-118, 65, 27, - -3, -72, 79, 104, -10, 114, 123, 20, - -103, -51, -45, 13, -16, 68, 58, -76, - -90, 102, 83, 51, 11, -53, -95, 16 -}; - - -static const uint16 spk_freq_table[12] = { - 36484, 34436, 32503, 30679, 28957, 27332, - 25798, 24350, 22983, 21693, 20476, 19326 -}; - -static const uint16 pcjr_freq_table[12] = { - 65472, 61760, 58304, 55040, 52032, 49024, - 46272, 43648, 41216, 38912, 36736, 34624 -}; - - -Player_V2::Player_V2(ScummEngine *scumm, Audio::Mixer *mixer, bool pcjr) { - int i; - - _isV3Game = (scumm->_game.version >= 3); - _vm = scumm; - _mixer = mixer; - _sampleRate = _mixer->getOutputRate(); - - _header_len = (scumm->_game.features & GF_OLD_BUNDLE) ? 4 : 6; - // Initialize sound queue - _current_nr = _next_nr = 0; - _current_data = _next_data = 0; +Player_V2::Player_V2(ScummEngine *scumm, Audio::Mixer *mixer, bool pcjr) + : Player_V2Base(scumm, mixer, pcjr) { - // Initialize channel code - for (i = 0; i < 4; ++i) - clear_channel(i); - - _next_tick = 0; - _tick_len = (_sampleRate << FIXP_SHIFT) / FREQ_HZ; - - // Initialize V3 music timer - _music_timer_ctr = _music_timer = 0; - _ticks_per_music_timer = 65535; + int i; // Initialize square generator _level = 0; _RNG = NG_PRESET; - set_pcjr(pcjr); - setMusicVolume(255); - - _mixer->playStream(Audio::Mixer::kPlainSoundType, &_soundHandle, this, -1, Audio::Mixer::kMaxChannelVolume, 0, DisposeAfterUse::NO, true); -} - -Player_V2::~Player_V2() { - Common::StackLock lock(_mutex); - _mixer->stopHandle(_soundHandle); -} - -void Player_V2::set_pcjr(bool pcjr) { - Common::StackLock lock(_mutex); - _pcjr = pcjr; if (_pcjr) { _decay = PCJR_DECAY; _update_step = (_sampleRate << FIXP_SHIFT) / (111860 * 2); - _freqs_table = pcjr_freq_table; } else { _decay = SPK_DECAY; _update_step = (_sampleRate << FIXP_SHIFT) / (1193000 * 2); - _freqs_table = spk_freq_table; } - /* adapt _decay to sample rate. It must be squared when - * sample rate doubles. - */ - int i; + // Adapt _decay to sample rate. It must be squared when + // sample rate doubles. for (i = 0; (_sampleRate << i) < 30000; i++) _decay = _decay * _decay / 65536; _timer_output = 0; for (i = 0; i < 4; i++) _timer_count[i] = 0; + + setMusicVolume(255); + + _mixer->playStream(Audio::Mixer::kPlainSoundType, &_soundHandle, this, -1, Audio::Mixer::kMaxChannelVolume, 0, DisposeAfterUse::NO, true); +} + +Player_V2::~Player_V2() { + Common::StackLock lock(_mutex); + _mixer->stopHandle(_soundHandle); } void Player_V2::setMusicVolume (int vol) { @@ -421,33 +97,6 @@ void Player_V2::setMusicVolume (int vol) { _volumetable[15] = 0; } -void Player_V2::chainSound(int nr, byte *data) { - int offset = _header_len + (_pcjr ? 10 : 2); - - _current_nr = nr; - _current_data = data; - - for (int i = 0; i < 4; i++) { - clear_channel(i); - - _channels[i].d.music_script_nr = nr; - if (data) { - _channels[i].d.next_cmd = READ_LE_UINT16(data + offset + 2 * i); - if (_channels[i].d.next_cmd) - _channels[i].d.time_left = 1; - } - } - _music_timer = 0; -} - -void Player_V2::chainNextSound() { - if (_next_nr) { - chainSound(_next_nr, _next_data); - _next_nr = 0; - _next_data = 0; - } -} - void Player_V2::stopAllSounds() { Common::StackLock lock(_mutex); @@ -476,11 +125,11 @@ void Player_V2::stopSound(int nr) { } void Player_V2::startSound(int nr) { + Common::StackLock lock(_mutex); + byte *data = _vm->getResourceAddress(rtSound, nr); assert(data); - Common::StackLock lock(_mutex); - int cprio = _current_data ? *(_current_data + _header_len) : 0; int prio = *(data + _header_len); int nprio = _next_data ? *(_next_data + _header_len) : 0; @@ -519,272 +168,11 @@ int Player_V2::getSoundStatus(int nr) const { return _current_nr == nr || _next_nr == nr; } - -void Player_V2::clear_channel(int i) { - ChannelInfo *channel = &_channels[i]; - memset(channel, 0, sizeof(ChannelInfo)); -} - -int Player_V2::getMusicTimer() { - if (_isV3Game) - return _music_timer; - else - return _channels[0].d.music_timer; -} - -void Player_V2::execute_cmd(ChannelInfo *channel) { - uint16 value; - int16 offset; - uint8 *script_ptr; - ChannelInfo * current_channel; - ChannelInfo * dest_channel; - - current_channel = channel; - - if (channel->d.next_cmd == 0) - goto check_stopped; - script_ptr = &_current_data[channel->d.next_cmd]; - - for (;;) { - uint8 opcode = *script_ptr++; - if (opcode >= 0xf8) { - switch (opcode) { - case 0xf8: // set hull curve - debug(7, "channels[%lu]: hull curve %2d", - (long)(channel - _channels), *script_ptr); - channel->d.hull_curve = hull_offsets[*script_ptr / 2]; - script_ptr++; - break; - - case 0xf9: // set freqmod curve - debug(7, "channels[%lu]: freqmod curve %2d", - (long)(channel - _channels), *script_ptr); - channel->d.freqmod_table = freqmod_offsets[*script_ptr / 4]; - channel->d.freqmod_modulo = freqmod_lengths[*script_ptr / 4]; - script_ptr++; - break; - - case 0xfd: // clear other channel - value = READ_LE_UINT16 (script_ptr) / sizeof (ChannelInfo); - debug(7, "clear channel %d", value); - script_ptr += 2; - // In Indy3, when traveling to Venice a command is - // issued to clear channel 4. So we introduce a 4th - // channel, which is never used. All OOB accesses are - // mapped to this channel. - // - // The original game had room for 8 channels, but only - // channels 0-3 are read, changes to other channels - // had no effect. - if (value >= ARRAYSIZE (_channels)) - value = 4; - channel = &_channels[value]; - // fall through - - case 0xfa: // clear current channel - if (opcode == 0xfa) - debug(7, "clear channel"); - channel->d.next_cmd = 0; - channel->d.base_freq = 0; - channel->d.freq_delta = 0; - channel->d.freq = 0; - channel->d.volume = 0; - channel->d.volume_delta = 0; - channel->d.inter_note_pause = 0; - channel->d.transpose = 0; - channel->d.hull_curve = 0; - channel->d.hull_offset = 0; - channel->d.hull_counter = 0; - channel->d.freqmod_table = 0; - channel->d.freqmod_offset = 0; - channel->d.freqmod_incr = 0; - channel->d.freqmod_multiplier = 0; - channel->d.freqmod_modulo = 0; - break; - - case 0xfb: // ret from subroutine - debug(7, "ret from sub"); - script_ptr = _retaddr; - break; - - case 0xfc: // call subroutine - offset = READ_LE_UINT16 (script_ptr); - debug(7, "subroutine %d", offset); - script_ptr += 2; - _retaddr = script_ptr; - script_ptr = _current_data + offset; - break; - - case 0xfe: // loop music - opcode = *script_ptr++; - offset = READ_LE_UINT16 (script_ptr); - script_ptr += 2; - debug(7, "loop if %d to %d", opcode, offset); - if (!channel->array[opcode / 2] || --channel->array[opcode/2]) - script_ptr += offset; - break; - - case 0xff: // set parameter - opcode = *script_ptr++; - value = READ_LE_UINT16 (script_ptr); - channel->array[opcode / 2] = value; - debug(7, "channels[%lu]: set param %2d = %5d", - (long)(channel - _channels), opcode, value); - script_ptr += 2; - if (opcode == 14) { - /* tempo var */ - _ticks_per_music_timer = 125; - } - if (opcode == 0) - goto end; - break; - } - } else { // opcode < 0xf8 - for (;;) { - int16 note, octave; - int is_last_note; - dest_channel = &_channels[(opcode >> 5) & 3]; - - if (!(opcode & 0x80)) { - - int tempo = channel->d.tempo; - if (!tempo) - tempo = 1; - channel->d.time_left = tempo * note_lengths[opcode & 0x1f]; - - note = *script_ptr++; - is_last_note = note & 0x80; - note &= 0x7f; - if (note == 0x7f) { - debug(8, "channels[%lu]: pause %d", - (long)(channel - _channels), channel->d.time_left); - goto end; - } - } else { - - channel->d.time_left = ((opcode & 7) << 8) | *script_ptr++; - - if ((opcode & 0x10)) { - debug(8, "channels[%lu]: pause %d", - (long)(channel - _channels), channel->d.time_left); - goto end; - } - - is_last_note = 0; - note = (*script_ptr++) & 0x7f; - } - - debug(8, "channels[%lu]: @%04lx note: %3d+%d len: %2d hull: %d mod: %d/%d/%d %s", - (long)(dest_channel - channel), (long)(script_ptr ? script_ptr - _current_data - 2 : 0), - note, (signed short) dest_channel->d.transpose, channel->d.time_left, - dest_channel->d.hull_curve, dest_channel->d.freqmod_table, - dest_channel->d.freqmod_incr,dest_channel->d.freqmod_multiplier, - is_last_note ? "last":""); - - uint16 myfreq; - dest_channel->d.time_left = channel->d.time_left; - dest_channel->d.note_length = - channel->d.time_left - dest_channel->d.inter_note_pause; - note += dest_channel->d.transpose; - while (note < 0) - note += 12; - octave = note / 12; - note = note % 12; - dest_channel->d.hull_offset = 0; - dest_channel->d.hull_counter = 1; - if (_pcjr && dest_channel == &_channels[3]) { - dest_channel->d.hull_curve = 196 + note * 12; - myfreq = 384 - 64 * octave; - } else { - myfreq = _freqs_table[note] >> octave; - } - dest_channel->d.freq = dest_channel->d.base_freq = myfreq; - if (is_last_note) - goto end; - opcode = *script_ptr++; - } - } - } - -end: - channel = current_channel; - if (channel->d.time_left) { - channel->d.next_cmd = script_ptr - _current_data; - return; - } - - channel->d.next_cmd = 0; - -check_stopped: - int i; - for (i = 0; i < 4; i++) { - if (_channels[i].d.time_left) - return; - } - - _current_nr = 0; - _current_data = 0; - chainNextSound(); - return; -} - -void Player_V2::next_freqs(ChannelInfo *channel) { - channel->d.volume += channel->d.volume_delta; - channel->d.base_freq += channel->d.freq_delta; - - channel->d.freqmod_offset += channel->d.freqmod_incr; - if (channel->d.freqmod_offset > channel->d.freqmod_modulo) - channel->d.freqmod_offset -= channel->d.freqmod_modulo; - channel->d.freq = - (int) (freqmod_table[channel->d.freqmod_table + (channel->d.freqmod_offset >> 4)]) - * (int) channel->d.freqmod_multiplier / 256 - + channel->d.base_freq; - - debug(9, "Freq: %d/%d, %d/%d/%d*%d %d", - channel->d.base_freq, (int16)channel->d.freq_delta, - channel->d.freqmod_table, channel->d.freqmod_offset, - channel->d.freqmod_incr, channel->d.freqmod_multiplier, - channel->d.freq); - - if (channel->d.note_length && !--channel->d.note_length) { - channel->d.hull_offset = 16; - channel->d.hull_counter = 1; - } - - if (!--channel->d.time_left) { - execute_cmd(channel); - } - -#if 0 - debug(9, "channels[%d]: freq %d hull %d/%d/%d", - channel - &_channels[0], channel->d.freq, - channel->d.hull_curve, channel->d.hull_offset, - channel->d.hull_counter); -#endif - - if (channel->d.hull_counter && !--channel->d.hull_counter) { - for (;;) { - const int16 *hull_ptr = hulls - + channel->d.hull_curve + channel->d.hull_offset / 2; - if (hull_ptr[1] == -1) { - channel->d.volume = hull_ptr[0]; - if (hull_ptr[0] == 0) - channel->d.volume_delta = 0; - channel->d.hull_offset += 4; - } else { - channel->d.volume_delta = hull_ptr[0]; - channel->d.hull_counter = hull_ptr[1]; - channel->d.hull_offset += 4; - break; - } - } - } -} - -void Player_V2::do_mix(int16 *data, uint len) { +int Player_V2::readBuffer(int16 *data, const int numSamples) { Common::StackLock lock(_mutex); uint step; + uint len = numSamples / 2; do { if (!(_next_tick >> FIXP_SHIFT)) { @@ -802,18 +190,8 @@ void Player_V2::do_mix(int16 *data, uint len) { data += 2 * step; _next_tick -= step << FIXP_SHIFT; } while (len -= step); -} -void Player_V2::nextTick() { - for (int i = 0; i < 4; i++) { - if (!_channels[i].d.time_left) - continue; - next_freqs(&_channels[i]); - } - if (_music_timer_ctr++ >= _ticks_per_music_timer) { - _music_timer_ctr = 0; - _music_timer++; - } + return numSamples; } void Player_V2::lowPassFilter(int16 *sample, uint len) { diff --git a/engines/scumm/player_v2.h b/engines/scumm/player_v2.h index 2c3e784928..6a0b3d6d5e 100644 --- a/engines/scumm/player_v2.h +++ b/engines/scumm/player_v2.h @@ -26,84 +26,35 @@ #ifndef SCUMM_PLAYER_V2_H #define SCUMM_PLAYER_V2_H -#include "common/scummsys.h" -#include "common/mutex.h" -#include "scumm/music.h" -#include "sound/audiostream.h" -#include "sound/mixer.h" +#include "scumm/player_v2base.h" namespace Scumm { -class ScummEngine; - -#include "common/pack-start.h" // START STRUCT PACKING - -struct channel_data { - uint16 time_left; // 00 - uint16 next_cmd; // 02 - uint16 base_freq; // 04 - uint16 freq_delta; // 06 - uint16 freq; // 08 - uint16 volume; // 10 - uint16 volume_delta; // 12 - uint16 tempo; // 14 - uint16 inter_note_pause; // 16 - uint16 transpose; // 18 - uint16 note_length; // 20 - uint16 hull_curve; // 22 - uint16 hull_offset; // 24 - uint16 hull_counter; // 26 - uint16 freqmod_table; // 28 - uint16 freqmod_offset; // 30 - uint16 freqmod_incr; // 32 - uint16 freqmod_multiplier; // 34 - uint16 freqmod_modulo; // 36 - uint16 unknown[4]; // 38 - 44 - uint16 music_timer; // 46 - uint16 music_script_nr; // 48 -} PACKED_STRUCT; - -#include "common/pack-end.h" // END STRUCT PACKING - - /** * Scumm V2 PC-Speaker MIDI driver. * This simulates the pc speaker sound, which is driven by the 8253 (square * wave generator) and a low-band filter. */ -class Player_V2 : public Audio::AudioStream, public MusicEngine { +class Player_V2 : public Player_V2Base { public: Player_V2(ScummEngine *scumm, Audio::Mixer *mixer, bool pcjr); virtual ~Player_V2(); + // MusicEngine API virtual void setMusicVolume(int vol); virtual void startSound(int sound); virtual void stopSound(int sound); virtual void stopAllSounds(); - virtual int getMusicTimer(); +// virtual int getMusicTimer(); virtual int getSoundStatus(int sound) const; // AudioStream API - int readBuffer(int16 *buffer, const int numSamples) { - do_mix(buffer, numSamples / 2); - return numSamples; - } + int readBuffer(int16 *buffer, const int numSamples); bool isStereo() const { return true; } bool endOfData() const { return false; } int getRate() const { return _sampleRate; } protected: - bool _isV3Game; - Audio::Mixer *_mixer; - Audio::SoundHandle _soundHandle; - ScummEngine *_vm; - - bool _pcjr; - int _header_len; - - uint32 _sampleRate; - uint32 _next_tick; - uint32 _tick_len; unsigned int _update_step; unsigned int _decay; int _level; @@ -113,218 +64,13 @@ protected: int _timer_count[4]; int _timer_output; - int _current_nr; - byte *_current_data; - int _next_nr; - byte *_next_data; - byte *_retaddr; - - Common::Mutex _mutex; - -private: - union ChannelInfo { - channel_data d; - uint16 array[sizeof(channel_data)/2]; - }; - - int _music_timer; - int _music_timer_ctr; - int _ticks_per_music_timer; - - const uint16 *_freqs_table; - - ChannelInfo _channels[5]; - protected: - virtual void nextTick(); - virtual void clear_channel(int i); - virtual void chainSound(int nr, byte *data); - virtual void chainNextSound(); - virtual void generateSpkSamples(int16 *data, uint len); virtual void generatePCjrSamples(int16 *data, uint len); void lowPassFilter(int16 *data, uint len); void squareGenerator(int channel, int freq, int vol, int noiseFeedback, int16 *sample, uint len); - -private: - void do_mix(int16 *buf, uint len); - - void set_pcjr(bool pcjr); - void execute_cmd(ChannelInfo *channel); - void next_freqs(ChannelInfo *channel); -}; - -/** - * Scumm V2 CMS/Gameblaster MIDI driver. - */ -class Player_V2CMS : public Audio::AudioStream, public MusicEngine { -public: - Player_V2CMS(ScummEngine *scumm, Audio::Mixer *mixer); - virtual ~Player_V2CMS(); - - virtual void setMusicVolume(int vol); - virtual void startSound(int sound); - virtual void stopSound(int sound); - virtual void stopAllSounds(); - virtual int getMusicTimer(); - virtual int getSoundStatus(int sound) const; - - // AudioStream API - int readBuffer(int16 *buffer, const int numSamples); - bool isStereo() const { return true; } - bool endOfData() const { return false; } - int getRate() const { return _sampleRate; } - -protected: - -#include "common/pack-start.h" // START STRUCT PACKING - struct Voice { - byte attack; - byte decay; - byte sustain; - byte release; - byte octadd; - int16 vibrato; - int16 vibrato2; - int16 noise; - } PACKED_STRUCT; - - struct Voice2 { - byte *amplitudeOutput; - byte *freqOutput; - byte *octaveOutput; - - uint8 channel; - int8 sustainLevel; - int8 attackRate; - uint8 maxAmpl; - int8 decayRate; - int8 sustainRate; - int8 releaseRate; - int8 releaseTime; - int8 vibratoRate; - int8 vibratoDepth; - - int8 curVibratoRate; - int8 curVibratoUnk; - - int8 unkVibratoRate; - int8 unkVibratoDepth; - - int8 unkRate; - int8 unkCount; - - int nextProcessState; - int8 curVolume; - int8 curOctave; - int8 curFreq; - - int8 octaveAdd; - - int8 playingNote; - Voice2 *nextVoice; - - byte chanNumber; - } PACKED_STRUCT; - - struct MusicChip { - byte ampl[4]; - byte freq[4]; - byte octave[2]; - } PACKED_STRUCT; -#include "common/pack-end.h" // END STRUCT PACKING - - Voice _cmsVoicesBase[16]; - Voice2 _cmsVoices[8]; - MusicChip _cmsChips[2]; - - int8 _tempo; - int8 _tempoSum; - byte _looping; - byte _octaveMask; - int16 _midiDelay; - Voice2 *_midiChannel[16]; - byte _midiChannelUse[16]; - byte *_midiData; - byte *_midiSongBegin; - - int _loadedMidiSong; - - byte _lastMidiCommand; - uint _outputTableReady; - byte _clkFrequenz; - byte _restart; - byte _curSno; - - void loadMidiData(byte *data, int sound); - void play(); - - void processChannel(Voice2 *channel); - void processRelease(Voice2 *channel); - void processAttack(Voice2 *channel); - void processDecay(Voice2 *channel); - void processSustain(Voice2 *channel); - void processVibrato(Voice2 *channel); - - void playMusicChips(const MusicChip *table); - void playNote(byte *&data); - void clearNote(byte *&data); - void offAllChannels(); - void playVoice(); - void processMidiData(uint ticks); - - Voice2 *getFreeVoice(); - Voice2 *getPlayVoice(byte param); - - // from Player_V2 -protected: - bool _isV3Game; - Audio::Mixer *_mixer; - Audio::SoundHandle _soundHandle; - ScummEngine *_vm; - - int _header_len; - - uint32 _sampleRate; - uint32 _next_tick; - uint32 _tick_len; - - int _timer_count[4]; - int _timer_output; - - int _current_nr; - byte *_current_data; - int _next_nr; - byte *_next_data; - byte *_retaddr; - - Common::Mutex _mutex; - -private: - union ChannelInfo { - channel_data d; - uint16 array[sizeof(channel_data)/2]; - }; - - int _music_timer; - int _music_timer_ctr; - int _ticks_per_music_timer; - - ChannelInfo _channels[5]; - -protected: - virtual void nextTick(); - virtual void clear_channel(int i); - virtual void chainSound(int nr, byte *data); - virtual void chainNextSound(); - -private: - void do_mix(int16 *buf, uint len); - - void execute_cmd(ChannelInfo *channel); - void next_freqs(ChannelInfo *channel); }; } // End of namespace Scumm diff --git a/engines/scumm/player_v2base.cpp b/engines/scumm/player_v2base.cpp new file mode 100644 index 0000000000..61c91aae85 --- /dev/null +++ b/engines/scumm/player_v2base.cpp @@ -0,0 +1,657 @@ +/* ScummVM - Graphic Adventure Engine + * + * ScummVM is the legal property of its developers, whose names + * are too numerous to list here. Please refer to the COPYRIGHT + * file distributed with this source distribution. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * $URL$ + * $Id$ + * + */ + +#include "scumm/player_v2base.h" +#include "scumm/scumm.h" + +#define FREQ_HZ 236 // Don't change! + +#define MAX_OUTPUT 0x7fff + +namespace Scumm { + +const uint8 note_lengths[] = { + 0, + 0, 0, 2, + 0, 3, 4, + 5, 6, 8, + 9, 12, 16, + 18, 24, 32, + 36, 48, 64, + 72, 96 +}; + +static const uint16 hull_offsets[] = { + 0, 12, 24, 36, 48, 60, + 72, 88, 104, 120, 136, 256, + 152, 164, 180 +}; + +static const int16 hulls[] = { + // hull 0 + 3, -1, 0, 0, 0, 0, 0, 0, + 0, -1, 0, 0, + // hull 1 (staccato) + 3, -1, 0, 32, 0, -1, 0, 0, + 0, -1, 0, 0, + // hull 2 (legato) + 3, -1, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, + // hull 3 (staccatissimo) + 3, -1, 0, 2, 0, -1, 0, 0, + 0, -1, 0, 0, + // hull 4 + 3, -1, 0, 6, 0, -1, 0, 0, + 0, -1, 0, 0, + // hull 5 + 3, -1, 0, 16, 0, -1, 0, 0, + 0, -1, 0, 0, + // hull 6 + (int16) 60000, -1, -1000, 20, 0, 0, 0, 0, + (int16) 40000, -1, -5000, 5, 0, -1, 0, 0, + // hull 7 + (int16) 50000, -1, 0, 8, 30000, -1, 0, 0, + 28000, -1, -5000, 5, 0, -1, 0, 0, + // hull 8 + (int16) 60000, -1, -2000, 16, 0, 0, 0, 0, + 28000, -1, -6000, 5, 0, -1, 0, 0, + // hull 9 + (int16) 55000, -1, 0, 8, (int16) 35000, -1, 0, 0, + (int16) 40000, -1, -2000, 10, 0, -1, 0, 0, + // hull 10 + (int16) 60000, -1, 0, 4, -2000, 8, 0, 0, + (int16) 40000, -1, -6000, 5, 0, -1, 0, 0, + // hull 12 + 0, -1, 150, 340, -150, 340, 0, -1, + 0, -1, 0, 0, + // hull 13 == 164 + 20000, -1, 4000, 7, 1000, 15, 0, 0, + (int16) 35000, -1, -2000, 15, 0, -1, 0, 0, + + // hull 14 == 180 + (int16) 35000, -1, 500, 20, 0, 0, 0, 0, + (int16) 45000, -1, -500, 60, 0, -1, 0, 0, + + // hull misc = 196 + (int16) 44000, -1, -4400, 10, 0, -1, 0, 0, + 0, -1, 0, 0, + + (int16) 53000, -1, -5300, 10, 0, -1, 0, 0, + 0, -1, 0, 0, + + (int16) 63000, -1, -6300, 10, 0, -1, 0, 0, + 0, -1, 0, 0, + + (int16) 44000, -1, -1375, 32, 0, -1, 0, 0, + 0, -1, 0, 0, + + (int16) 53000, -1, -1656, 32, 0, -1, 0, 0, + 0, -1, 0, 0, + + // hull 11 == 256 + (int16) 63000, -1, -1968, 32, 0, -1, 0, 0, + 0, -1, 0, 0, + + (int16) 44000, -1, - 733, 60, 0, -1, 0, 0, + 0, -1, 0, 0, + + (int16) 53000, -1, - 883, 60, 0, -1, 0, 0, + 0, -1, 0, 0, + + (int16) 63000, -1, -1050, 60, 0, -1, 0, 0, + 0, -1, 0, 0, + + (int16) 44000, -1, - 488, 90, 0, -1, 0, 0, + 0, -1, 0, 0, + + (int16) 53000, -1, - 588, 90, 0, -1, 0, 0, + 0, -1, 0, 0, + + (int16) 63000, -1, - 700, 90, 0, -1, 0, 0, + 0, -1, 0, 0 +}; + +static const uint16 freqmod_lengths[] = { + 0x1000, 0x1000, 0x20, 0x2000, 0x1000 +}; + +static const uint16 freqmod_offsets[] = { + 0, 0x100, 0x200, 0x302, 0x202 +}; + +static const int8 freqmod_table[0x502] = { + 0, 3, 6, 9, 12, 15, 18, 21, + 24, 27, 30, 33, 36, 39, 42, 45, + 48, 51, 54, 57, 59, 62, 65, 67, + 70, 73, 75, 78, 80, 82, 85, 87, + 89, 91, 94, 96, 98, 100, 102, 103, + 105, 107, 108, 110, 112, 113, 114, 116, + 117, 118, 119, 120, 121, 122, 123, 123, + 124, 125, 125, 126, 126, 126, 126, 126, + 126, 126, 126, 126, 126, 126, 125, 125, + 124, 123, 123, 122, 121, 120, 119, 118, + 117, 116, 114, 113, 112, 110, 108, 107, + 105, 103, 102, 100, 98, 96, 94, 91, + 89, 87, 85, 82, 80, 78, 75, 73, + 70, 67, 65, 62, 59, 57, 54, 51, + 48, 45, 42, 39, 36, 33, 30, 27, + 24, 21, 18, 15, 12, 9, 6, 3, + 0, -3, -6, -9, -12, -15, -18, -21, + -24, -27, -30, -33, -36, -39, -42, -45, + -48, -51, -54, -57, -59, -62, -65, -67, + -70, -73, -75, -78, -80, -82, -85, -87, + -89, -91, -94, -96, -98,-100,-102,-103, + -105,-107,-108,-110,-112,-113,-114,-116, + -117,-118,-119,-120,-121,-122,-123,-123, + -124,-125,-125,-126,-126,-126,-126,-126, + -126,-126,-126,-126,-126,-126,-125,-125, + -124,-123,-123,-122,-121,-120,-119,-118, + -117,-116,-114,-113,-112,-110,-108,-107, + -105,-103,-102,-100, -98, -96, -94, -91, + -89, -87, -85, -82, -80, -78, -75, -73, + -70, -67, -65, -62, -59, -57, -54, -51, + -48, -45, -42, -39, -36, -33, -30, -27, + -24, -21, -18, -15, -12, -9, -6, -3, + + 0, 1, 2, 3, 4, 5, 6, 7, + 8, 9, 10, 11, 12, 13, 14, 15, + 16, 17, 18, 19, 20, 21, 22, 23, + 24, 25, 26, 27, 28, 29, 30, 31, + 32, 33, 34, 35, 36, 37, 38, 39, + 40, 41, 42, 43, 44, 45, 46, 47, + 48, 49, 50, 51, 52, 53, 54, 55, + 56, 57, 58, 59, 60, 61, 62, 63, + 64, 65, 66, 67, 68, 69, 70, 71, + 72, 73, 74, 75, 76, 77, 78, 79, + 80, 81, 82, 83, 84, 85, 86, 87, + 88, 89, 90, 91, 92, 93, 94, 95, + 96, 97, 98, 99, 100, 101, 102, 103, + 104, 105, 106, 107, 108, 109, 110, 111, + 112, 113, 114, 115, 116, 117, 118, 119, + 120, 121, 122, 123, 124, 125, 126, 127, + -128,-127,-126,-125,-124,-123,-122,-121, + -120,-119,-118,-117,-116,-115,-114,-113, + -112,-111,-110,-109,-108,-107,-106,-105, + -104,-103,-102,-101,-100, -99, -98, -97, + -96, -95, -94, -93, -92, -91, -90, -89, + -88, -87, -86, -85, -84, -83, -82, -81, + -80, -79, -78, -77, -76, -75, -74, -73, + -72, -71, -70, -69, -68, -67, -66, -65, + -64, -63, -62, -61, -60, -59, -58, -57, + -56, -55, -54, -53, -52, -51, -50, -49, + -48, -47, -46, -45, -44, -43, -42, -41, + -40, -39, -38, -37, -36, -35, -34, -33, + -32, -31, -30, -29, -28, -27, -26, -25, + -24, -23, -22, -21, -20, -19, -18, -17, + -16, -15, -14, -13, -12, -11, -10, -9, + -8, -7, -6, -5, -4, -3, -2, -1, + + -120, 120, + + -120,-120,-120,-120,-120,-120,-120,-120, + -120,-120,-120,-120,-120,-120,-120,-120, + -120,-120,-120,-120,-120,-120,-120,-120, + -120,-120,-120,-120,-120,-120,-120,-120, + -120,-120,-120,-120,-120,-120,-120,-120, + -120,-120,-120,-120,-120,-120,-120,-120, + -120,-120,-120,-120,-120,-120,-120,-120, + -120,-120,-120,-120,-120,-120,-120,-120, + -120,-120,-120,-120,-120,-120,-120,-120, + -120,-120,-120,-120,-120,-120,-120,-120, + -120,-120,-120,-120,-120,-120,-120,-120, + -120,-120,-120,-120,-120,-120,-120,-120, + -120,-120,-120,-120,-120,-120,-120,-120, + -120,-120,-120,-120,-120,-120,-120,-120, + -120,-120,-120,-120,-120,-120,-120,-120, + -120,-120,-120,-120,-120,-120,-120,-120, + 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120, 120, 120, 120, 120, + + 41, 35, -66,-124, -31, 108, -42, -82, + 82,-112, 73, -15, -15, -69, -23, -21, + -77, -90, -37, 60,-121, 12, 62,-103, + 36, 94, 13, 28, 6, -73, 71, -34, + -77, 18, 77, -56, 67, -69,-117, -90, + 31, 3, 90, 125, 9, 56, 37, 31, + 93, -44, -53, -4,-106, -11, 69, 59, + 19, 13,-119, 10, 28, -37, -82, 50, + 32,-102, 80, -18, 64, 120, 54, -3, + 18, 73, 50, -10, -98, 125, 73, -36, + -83, 79, 20, -14, 68, 64, 102, -48, + 107, -60, 48, -73, 50, 59, -95, 34, + -10, 34,-111, -99, -31,-117, 31, -38, + -80, -54,-103, 2, -71, 114, -99, 73, + 44,-128, 126, -59,-103, -43, -23,-128, + -78, -22, -55, -52, 83, -65, 103, -42, + -65, 20, -42, 126, 45, -36,-114, 102, + -125, -17, 87, 73, 97, -1, 105,-113, + 97, -51, -47, 30, -99,-100, 22, 114, + 114, -26, 29, -16,-124, 79, 74, 119, + 2, -41, -24, 57, 44, 83, -53, -55, + 18, 30, 51, 116, -98, 12, -12, -43, + -44, -97, -44, -92, 89, 126, 53, -49, + 50, 34, -12, -52, -49, -45,-112, 45, + 72, -45,-113, 117, -26, -39, 29, 42, + -27, -64, -9, 43, 120,-127,-121, 68, + 14, 95, 80, 0, -44, 97,-115, -66, + 123, 5, 21, 7, 59, 51,-126, 31, + 24, 112,-110, -38, 100, 84, -50, -79, + -123, 62, 105, 21, -8, 70, 106, 4, + -106, 115, 14, -39, 22, 47, 103, 104, + -44, -9, 74, 74, -48, 87, 104, 118, + -6, 22, -69, 17, -83, -82, 36,-120, + 121, -2, 82, -37, 37, 67, -27, 60, + -12, 69, -45, -40, 40, -50, 11, -11, + -59, 96, 89, 61,-105, 39,-118, 89, + 118, 45, -48, -62, -55, -51, 104, -44, + 73, 106, 121, 37, 8, 97, 64, 20, + -79, 59, 106, -91, 17, 40, -63,-116, + -42, -87, 11,-121,-105,-116, 47, -15, + 21, 29,-102,-107, -63,-101, -31, -64, + 126, -23, -88,-102, -89,-122, -62, -75, + 84, -65,-102, -25, -39, 35, -47, 85, + -112, 56, 40, -47, -39, 108, -95, 102, + 94, 78, -31, 48,-100, -2, -39, 113, + -97, -30, -91, -30, 12,-101, -76, 71, + 101, 56, 42, 70,-119, -87,-126, 121, + 122, 118, 120, -62, 99, -79, 38, -33, + -38, 41, 109, 62, 98, -32,-106, 18, + 52, -65, 57, -90, 63,-119, 94, -15, + 109, 14, -29, 108, 40, -95, 30, 32, + 29, -53, -62, 3, 63, 65, 7,-124, + 15, 20, 5, 101, 27, 40, 97, -55, + -59, -25, 44,-114, 70, 54, 8, -36, + -13, -88,-115, -2, -66, -14, -21, 113, + -1, -96, -48, 59, 117, 6,-116, 126, + -121, 120, 115, 77, -48, -66,-126, -66, + -37, -62, 70, 65, 43,-116, -6, 48, + 127, 112, -16, -89, 84,-122, 50,-107, + -86, 91, 104, 19, 11, -26, -4, -11, + -54, -66, 125, -97,-119,-118, 65, 27, + -3, -72, 79, 104, -10, 114, 123, 20, + -103, -51, -45, 13, -16, 68, 58, -76, + -90, 102, 83, 51, 11, -53, -95, 16 +}; + +static const uint16 spk_freq_table[12] = { + 36484, 34436, 32503, 30679, 28957, 27332, + 25798, 24350, 22983, 21693, 20476, 19326 +}; + +static const uint16 pcjr_freq_table[12] = { + 65472, 61760, 58304, 55040, 52032, 49024, + 46272, 43648, 41216, 38912, 36736, 34624 +}; + + +Player_V2Base::Player_V2Base(ScummEngine *scumm, Audio::Mixer *mixer, bool pcjr) + : _vm(scumm), + _mixer(mixer), + _pcjr(pcjr), + _sampleRate(_mixer->getOutputRate()) { + + _isV3Game = (scumm->_game.version >= 3); + + _header_len = (scumm->_game.features & GF_OLD_BUNDLE) ? 4 : 6; + + // Initialize sound queue + _current_nr = _next_nr = 0; + _current_data = _next_data = 0; + + // Initialize channel code + for (int i = 0; i < 4; ++i) + clear_channel(i); + + _next_tick = 0; + _tick_len = (_sampleRate << FIXP_SHIFT) / FREQ_HZ; + + // Initialize V3 music timer + _music_timer_ctr = _music_timer = 0; + _ticks_per_music_timer = 65535; + + if (_pcjr) { + _freqs_table = pcjr_freq_table; + } else { + _freqs_table = spk_freq_table; + } +} + +Player_V2Base::~Player_V2Base() { +} + +void Player_V2Base::chainSound(int nr, byte *data) { + int offset = _header_len + (_pcjr ? 10 : 2); + + _current_nr = nr; + _current_data = data; + + for (int i = 0; i < 4; i++) { + clear_channel(i); + + _channels[i].d.music_script_nr = nr; + if (data) { + _channels[i].d.next_cmd = READ_LE_UINT16(data + offset + 2 * i); + if (_channels[i].d.next_cmd) { + _channels[i].d.time_left = 1; + } + } + } + _music_timer = 0; +} + +void Player_V2Base::chainNextSound() { + if (_next_nr) { + chainSound(_next_nr, _next_data); + _next_nr = 0; + _next_data = 0; + } +} + +// TODO: Merge stopAllSounds, stopSound() and startSound(), using some overriding +// perhaps? Player_V2CMS's implementations start like that of Player_V2 +// but then add some MIDI related stuff. + +void Player_V2Base::clear_channel(int i) { + ChannelInfo *channel = &_channels[i]; + memset(channel, 0, sizeof(ChannelInfo)); +} + +int Player_V2Base::getMusicTimer() { + if (_isV3Game) + return _music_timer; + else + return _channels[0].d.music_timer; +} + +void Player_V2Base::execute_cmd(ChannelInfo *channel) { + uint16 value; + int16 offset; + uint8 *script_ptr; + ChannelInfo * current_channel; + ChannelInfo * dest_channel; + + current_channel = channel; + + if (channel->d.next_cmd == 0) + goto check_stopped; + script_ptr = &_current_data[channel->d.next_cmd]; + + for (;;) { + uint8 opcode = *script_ptr++; + if (opcode >= 0xf8) { + switch (opcode) { + case 0xf8: // set hull curve + debug(7, "channels[%d]: hull curve %2d", + (uint)(channel - _channels), *script_ptr); + channel->d.hull_curve = hull_offsets[*script_ptr / 2]; + script_ptr++; + break; + + case 0xf9: // set freqmod curve + debug(7, "channels[%d]: freqmod curve %2d", + (uint)(channel - _channels), *script_ptr); + channel->d.freqmod_table = freqmod_offsets[*script_ptr / 4]; + channel->d.freqmod_modulo = freqmod_lengths[*script_ptr / 4]; + script_ptr++; + break; + + case 0xfd: // clear other channel + value = READ_LE_UINT16 (script_ptr) / sizeof (ChannelInfo); + debug(7, "clear channel %d", value); + script_ptr += 2; + // In Indy3, when traveling to Venice a command is + // issued to clear channel 4. So we introduce a 4th + // channel, which is never used. All OOB accesses are + // mapped to this channel. + // + // The original game had room for 8 channels, but only + // channels 0-3 are read, changes to other channels + // had no effect. + if (value >= ARRAYSIZE (_channels)) + value = 4; + channel = &_channels[value]; + // fall through + + case 0xfa: // clear current channel + if (opcode == 0xfa) + debug(7, "clear channel"); + channel->d.next_cmd = 0; + channel->d.base_freq = 0; + channel->d.freq_delta = 0; + channel->d.freq = 0; + channel->d.volume = 0; + channel->d.volume_delta = 0; + channel->d.inter_note_pause = 0; + channel->d.transpose = 0; + channel->d.hull_curve = 0; + channel->d.hull_offset = 0; + channel->d.hull_counter = 0; + channel->d.freqmod_table = 0; + channel->d.freqmod_offset = 0; + channel->d.freqmod_incr = 0; + channel->d.freqmod_multiplier = 0; + channel->d.freqmod_modulo = 0; + break; + + case 0xfb: // ret from subroutine + debug(7, "ret from sub"); + script_ptr = _retaddr; + break; + + case 0xfc: // call subroutine + offset = READ_LE_UINT16 (script_ptr); + debug(7, "subroutine %d", offset); + script_ptr += 2; + _retaddr = script_ptr; + script_ptr = _current_data + offset; + break; + + case 0xfe: // loop music + opcode = *script_ptr++; + offset = READ_LE_UINT16 (script_ptr); + script_ptr += 2; + debug(7, "loop if %d to %d", opcode, offset); + if (!channel->array[opcode / 2] || --channel->array[opcode/2]) + script_ptr += offset; + break; + + case 0xff: // set parameter + opcode = *script_ptr++; + value = READ_LE_UINT16 (script_ptr); + channel->array[opcode / 2] = value; + debug(7, "channels[%d]: set param %2d = %5d", + (uint)(channel - _channels), opcode, value); + script_ptr += 2; + if (opcode == 14) { + /* tempo var */ + _ticks_per_music_timer = 125; + } + if (opcode == 0) + goto end; + break; + } + } else { // opcode < 0xf8 + for (;;) { + int16 note, octave; + int is_last_note; + dest_channel = &_channels[(opcode >> 5) & 3]; + + if (!(opcode & 0x80)) { + + int tempo = channel->d.tempo; + if (!tempo) + tempo = 1; + channel->d.time_left = tempo * note_lengths[opcode & 0x1f]; + + note = *script_ptr++; + is_last_note = note & 0x80; + note &= 0x7f; + if (note == 0x7f) { + debug(8, "channels[%d]: pause %d", + (uint)(channel - _channels), channel->d.time_left); + goto end; + } + } else { + + channel->d.time_left = ((opcode & 7) << 8) | *script_ptr++; + + if ((opcode & 0x10)) { + debug(8, "channels[%d]: pause %d", + (uint)(channel - _channels), channel->d.time_left); + goto end; + } + + is_last_note = 0; + note = (*script_ptr++) & 0x7f; + } + + debug(8, "channels[%d]: @%04x note: %3d+%d len: %2d hull: %d mod: %d/%d/%d %s", + (uint)(dest_channel - channel), script_ptr ? (uint)(script_ptr - _current_data - 2) : 0, + note, (signed short) dest_channel->d.transpose, channel->d.time_left, + dest_channel->d.hull_curve, dest_channel->d.freqmod_table, + dest_channel->d.freqmod_incr,dest_channel->d.freqmod_multiplier, + is_last_note ? "last":""); + + uint16 myfreq; + dest_channel->d.time_left = channel->d.time_left; + dest_channel->d.note_length = + channel->d.time_left - dest_channel->d.inter_note_pause; + note += dest_channel->d.transpose; + while (note < 0) + note += 12; + octave = note / 12; + note = note % 12; + dest_channel->d.hull_offset = 0; + dest_channel->d.hull_counter = 1; + if (_pcjr && dest_channel == &_channels[3]) { + dest_channel->d.hull_curve = 196 + note * 12; + myfreq = 384 - 64 * octave; + } else { + myfreq = _freqs_table[note] >> octave; + } + dest_channel->d.freq = dest_channel->d.base_freq = myfreq; + if (is_last_note) + goto end; + opcode = *script_ptr++; + } + } + } + +end: + channel = current_channel; + if (channel->d.time_left) { + channel->d.next_cmd = script_ptr - _current_data; + return; + } + + channel->d.next_cmd = 0; + +check_stopped: + int i; + for (i = 0; i < 4; i++) { + if (_channels[i].d.time_left) + return; + } + + _current_nr = 0; + _current_data = 0; + chainNextSound(); +} + +void Player_V2Base::next_freqs(ChannelInfo *channel) { + channel->d.volume += channel->d.volume_delta; + channel->d.base_freq += channel->d.freq_delta; + + channel->d.freqmod_offset += channel->d.freqmod_incr; + if (channel->d.freqmod_offset > channel->d.freqmod_modulo) + channel->d.freqmod_offset -= channel->d.freqmod_modulo; + + channel->d.freq = + (int) (freqmod_table[channel->d.freqmod_table + (channel->d.freqmod_offset >> 4)]) + * (int) channel->d.freqmod_multiplier / 256 + + channel->d.base_freq; + + debug(9, "Freq: %d/%d, %d/%d/%d*%d %d", + channel->d.base_freq, (int16)channel->d.freq_delta, + channel->d.freqmod_table, channel->d.freqmod_offset, + channel->d.freqmod_incr, channel->d.freqmod_multiplier, + channel->d.freq); + + if (channel->d.note_length && !--channel->d.note_length) { + channel->d.hull_offset = 16; + channel->d.hull_counter = 1; + } + + if (!--channel->d.time_left) { + execute_cmd(channel); + } + + if (channel->d.hull_counter && !--channel->d.hull_counter) { + for (;;) { + const int16 *hull_ptr = hulls + + channel->d.hull_curve + channel->d.hull_offset / 2; + if (hull_ptr[1] == -1) { + channel->d.volume = hull_ptr[0]; + if (hull_ptr[0] == 0) + channel->d.volume_delta = 0; + channel->d.hull_offset += 4; + } else { + channel->d.volume_delta = hull_ptr[0]; + channel->d.hull_counter = hull_ptr[1]; + channel->d.hull_offset += 4; + break; + } + } + } +} + +void Player_V2Base::nextTick() { + for (int i = 0; i < 4; i++) { + if (!_channels[i].d.time_left) + continue; + next_freqs(&_channels[i]); + } + if (_music_timer_ctr++ >= _ticks_per_music_timer) { + _music_timer_ctr = 0; + _music_timer++; + } +} + + +} // End of namespace Scumm diff --git a/engines/scumm/player_v2base.h b/engines/scumm/player_v2base.h new file mode 100644 index 0000000000..7b90ae98cf --- /dev/null +++ b/engines/scumm/player_v2base.h @@ -0,0 +1,148 @@ +/* ScummVM - Graphic Adventure Engine + * + * ScummVM is the legal property of its developers, whose names + * are too numerous to list here. Please refer to the COPYRIGHT + * file distributed with this source distribution. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * $URL$ + * $Id$ + * + */ + +#ifndef SCUMM_PLAYER_V2BASE_H +#define SCUMM_PLAYER_V2BASE_H + +#include "common/scummsys.h" +#include "common/mutex.h" +#include "scumm/music.h" +#include "sound/audiostream.h" +#include "sound/mixer.h" + +namespace Scumm { + +class ScummEngine; + + +#include "common/pack-start.h" // START STRUCT PACKING + +struct channel_data { + uint16 time_left; // 00 + uint16 next_cmd; // 02 + uint16 base_freq; // 04 + uint16 freq_delta; // 06 + uint16 freq; // 08 + uint16 volume; // 10 + uint16 volume_delta; // 12 + uint16 tempo; // 14 + uint16 inter_note_pause; // 16 + uint16 transpose; // 18 + uint16 note_length; // 20 + uint16 hull_curve; // 22 + uint16 hull_offset; // 24 + uint16 hull_counter; // 26 + uint16 freqmod_table; // 28 + uint16 freqmod_offset; // 30 + uint16 freqmod_incr; // 32 + uint16 freqmod_multiplier; // 34 + uint16 freqmod_modulo; // 36 + uint16 unknown[4]; // 38 - 44 + uint16 music_timer; // 46 + uint16 music_script_nr; // 48 +} PACKED_STRUCT; + +#include "common/pack-end.h" // END STRUCT PACKING + +/** + * Common base class for Player_V2 and Player_V2CMS. + */ +class Player_V2Base : public Audio::AudioStream, public MusicEngine { +public: + Player_V2Base(ScummEngine *scumm, Audio::Mixer *mixer, bool pcjr); + virtual ~Player_V2Base(); + + // MusicEngine API +// virtual void setMusicVolume(int vol); +// virtual void startSound(int sound); +// virtual void stopSound(int sound); +// virtual void stopAllSounds(); + virtual int getMusicTimer(); +// virtual int getSoundStatus(int sound) const; + + // AudioStream API +/* + int readBuffer(int16 *buffer, const int numSamples) { + do_mix(buffer, numSamples / 2); + return numSamples; + } +*/ + bool isStereo() const { return true; } + bool endOfData() const { return false; } + int getRate() const { return _sampleRate; } + +protected: + enum { + FIXP_SHIFT = 16 + }; + + bool _isV3Game; + Audio::Mixer *_mixer; + Audio::SoundHandle _soundHandle; + ScummEngine *_vm; + + bool _pcjr; + int _header_len; + + const uint32 _sampleRate; + uint32 _next_tick; + uint32 _tick_len; + + int _current_nr; + byte *_current_data; + int _next_nr; + byte *_next_data; + byte *_retaddr; + + Common::Mutex _mutex; + + union ChannelInfo { + channel_data d; + uint16 array[sizeof(channel_data)/2]; + }; + + ChannelInfo _channels[5]; + +private: + int _music_timer; + int _music_timer_ctr; + int _ticks_per_music_timer; + + const uint16 *_freqs_table; + +protected: + virtual void nextTick(); + virtual void clear_channel(int i); + virtual void chainSound(int nr, byte *data); + virtual void chainNextSound(); + + void execute_cmd(ChannelInfo *channel); + void next_freqs(ChannelInfo *channel); +}; + + +} // End of namespace Scumm + +#endif diff --git a/engines/scumm/player_v2cms.cpp b/engines/scumm/player_v2cms.cpp index e3e7bc1901..7bec171173 100644 --- a/engines/scumm/player_v2cms.cpp +++ b/engines/scumm/player_v2cms.cpp @@ -23,689 +23,21 @@ * */ -#include "engines/engine.h" -#include "scumm/player_v2.h" +#include "scumm/player_v2cms.h" #include "scumm/scumm.h" #include "sound/mididrv.h" #include "sound/mixer.h" +#include "sound/softsynth/cms.h" namespace Scumm { -#define FREQ_HZ 236 // Don't change! - -#define FIXP_SHIFT 16 -#define MAX_OUTPUT 0x7fff - -#define NG_PRESET 0x0f35 /* noise generator preset */ -#define FB_WNOISE 0x12000 /* feedback for white noise */ -#define FB_PNOISE 0x08000 /* feedback for periodic noise */ - -// CMS/Gameblaster Emulation taken from DosBox - -#define LEFT 0x00 -#define RIGHT 0x01 -#define MAX_OUTPUT 0x7fff -#define MIN_OUTPUT -0x8000 -//#define CMS_BUFFER_SIZE 128 -#define CMS_RATE 22050 - #define PROCESS_ATTACK 1 #define PROCESS_RELEASE 2 #define PROCESS_SUSTAIN 3 #define PROCESS_DECAY 4 #define PROCESS_VIBRATO 5 -/* this structure defines a channel */ -struct saa1099_channel { - int frequency; /* frequency (0x00..0xff) */ - int freq_enable; /* frequency enable */ - int noise_enable; /* noise enable */ - int octave; /* octave (0x00..0x07) */ - int amplitude[2]; /* amplitude (0x00..0x0f) */ - int envelope[2]; /* envelope (0x00..0x0f or 0x10 == off) */ - - /* vars to simulate the square wave */ - double counter; - double freq; - int level; -}; - -/* this structure defines a noise channel */ -struct saa1099_noise { - /* vars to simulate the noise generator output */ - double counter; - double freq; - int level; /* noise polynomal shifter */ -}; - -/* this structure defines a SAA1099 chip */ -struct SAA1099 { - int stream; /* our stream */ - int noise_params[2]; /* noise generators parameters */ - int env_enable[2]; /* envelope generators enable */ - int env_reverse_right[2]; /* envelope reversed for right channel */ - int env_mode[2]; /* envelope generators mode */ - int env_bits[2]; /* non zero = 3 bits resolution */ - int env_clock[2]; /* envelope clock mode (non-zero external) */ - int env_step[2]; /* current envelope step */ - int all_ch_enable; /* all channels enable */ - int sync_state; /* sync all channels */ - int selected_reg; /* selected register */ - struct saa1099_channel channels[6]; /* channels */ - struct saa1099_noise noise[2]; /* noise generators */ -}; - -static byte envelope[8][64] = { - /* zero amplitude */ - { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, - /* maximum amplitude */ - {15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15, - 15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15, - 15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15, - 15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15, }, - /* single decay */ - {15,14,13,12,11,10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, - /* repetitive decay */ - {15,14,13,12,11,10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0, - 15,14,13,12,11,10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0, - 15,14,13,12,11,10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0, - 15,14,13,12,11,10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0 }, - /* single triangular */ - { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9,10,11,12,13,14,15, - 15,14,13,12,11,10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, - /* repetitive triangular */ - { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9,10,11,12,13,14,15, - 15,14,13,12,11,10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0, - 0, 1, 2, 3, 4, 5, 6, 7, 8, 9,10,11,12,13,14,15, - 15,14,13,12,11,10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0 }, - /* single attack */ - { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9,10,11,12,13,14,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, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, - /* repetitive attack */ - { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9,10,11,12,13,14,15, - 0, 1, 2, 3, 4, 5, 6, 7, 8, 9,10,11,12,13,14,15, - 0, 1, 2, 3, 4, 5, 6, 7, 8, 9,10,11,12,13,14,15, - 0, 1, 2, 3, 4, 5, 6, 7, 8, 9,10,11,12,13,14,15 } -}; - -static int amplitude_lookup[16] = { - 0*32767/16, 1*32767/16, 2*32767/16, 3*32767/16, - 4*32767/16, 5*32767/16, 6*32767/16, 7*32767/16, - 8*32767/16, 9*32767/16, 10*32767/16, 11*32767/16, - 12*32767/16, 13*32767/16, 14*32767/16, 15*32767/16 -}; - -class CMSEmulator { -public: - CMSEmulator(uint32 sampleRate) { - _sampleRate = sampleRate; - memset(_saa1099, 0, sizeof(SAA1099)*2); - } - - ~CMSEmulator() { } - - void portWrite(int port, int val); - void readBuffer(int16 *buffer, const int numSamples); -private: - uint32 _sampleRate; - - SAA1099 _saa1099[2]; - - void envelope(int chip, int ch); - void update(int chip, int16 *buffer, int length); - void portWriteIntern(int chip, int offset, int data); -}; - -void CMSEmulator::portWrite(int port, int val) { - switch (port) { - case 0x220: - portWriteIntern(0, 1, val); - break; - - case 0x221: - _saa1099[0].selected_reg = val & 0x1f; - if (_saa1099[0].selected_reg == 0x18 || _saa1099[0].selected_reg == 0x19) { - /* clock the envelope channels */ - if (_saa1099[0].env_clock[0]) envelope(0, 0); - if (_saa1099[0].env_clock[1]) envelope(0, 1); - } - break; - - case 0x222: - portWriteIntern(1, 1, val); - break; - - case 0x223: - _saa1099[1].selected_reg = val & 0x1f; - if (_saa1099[1].selected_reg == 0x18 || _saa1099[1].selected_reg == 0x19) { - /* clock the envelope channels */ - if (_saa1099[1].env_clock[0]) envelope(1, 0); - if (_saa1099[1].env_clock[1]) envelope(1, 1); - } - break; - - default: - warning("CMSEmulator got port: 0x%X", port); - break; - } -} - -void CMSEmulator::readBuffer(int16 *buffer, const int numSamples) { - update(0, &buffer[0], numSamples); - update(1, &buffer[0], numSamples); -} - -void CMSEmulator::envelope(int chip, int ch) { - SAA1099 *saa = &_saa1099[chip]; - if (saa->env_enable[ch]) { - int step, mode, mask; - mode = saa->env_mode[ch]; - /* step from 0..63 and then loop in steps 32..63 */ - step = saa->env_step[ch] = ((saa->env_step[ch] + 1) & 0x3f) | (saa->env_step[ch] & 0x20); - - mask = 15; - if (saa->env_bits[ch]) - mask &= ~1; /* 3 bit resolution, mask LSB */ - - saa->channels[ch*3+0].envelope[ LEFT] = - saa->channels[ch*3+1].envelope[ LEFT] = - saa->channels[ch*3+2].envelope[ LEFT] = Scumm::envelope[mode][step] & mask; - if (saa->env_reverse_right[ch] & 0x01) { - saa->channels[ch*3+0].envelope[RIGHT] = - saa->channels[ch*3+1].envelope[RIGHT] = - saa->channels[ch*3+2].envelope[RIGHT] = (15 - Scumm::envelope[mode][step]) & mask; - } else { - saa->channels[ch*3+0].envelope[RIGHT] = - saa->channels[ch*3+1].envelope[RIGHT] = - saa->channels[ch*3+2].envelope[RIGHT] = Scumm::envelope[mode][step] & mask; - } - } else { - /* envelope mode off, set all envelope factors to 16 */ - saa->channels[ch*3+0].envelope[ LEFT] = - saa->channels[ch*3+1].envelope[ LEFT] = - saa->channels[ch*3+2].envelope[ LEFT] = - saa->channels[ch*3+0].envelope[RIGHT] = - saa->channels[ch*3+1].envelope[RIGHT] = - saa->channels[ch*3+2].envelope[RIGHT] = 16; - } -} - -void CMSEmulator::update(int chip, int16 *buffer, int length) { - struct SAA1099 *saa = &_saa1099[chip]; - int j, ch; - - /* if the channels are disabled we're done */ - if (!saa->all_ch_enable) { - /* init output data */ - if (chip == 0) { - memset(buffer, 0, sizeof(int16)*length*2); - } - return; - } - - if (chip == 0) { - memset(buffer, 0, sizeof(int16)*length*2); - } - - for (ch = 0; ch < 2; ch++) { - switch (saa->noise_params[ch]) { - case 0: saa->noise[ch].freq = 31250.0 * 2; break; - case 1: saa->noise[ch].freq = 15625.0 * 2; break; - case 2: saa->noise[ch].freq = 7812.5 * 2; break; - case 3: saa->noise[ch].freq = saa->channels[ch * 3].freq; break; - } - } - - /* fill all data needed */ - for (j = 0; j < length; ++j) { - int output_l = 0, output_r = 0; - - /* for each channel */ - for (ch = 0; ch < 6; ch++) { - if (saa->channels[ch].freq == 0.0) - saa->channels[ch].freq = (double)((2 * 15625) << saa->channels[ch].octave) / - (511.0 - (double)saa->channels[ch].frequency); - - /* check the actual position in the square wave */ - saa->channels[ch].counter -= saa->channels[ch].freq; - while (saa->channels[ch].counter < 0) { - /* calculate new frequency now after the half wave is updated */ - saa->channels[ch].freq = (double)((2 * 15625) << saa->channels[ch].octave) / - (511.0 - (double)saa->channels[ch].frequency); - - saa->channels[ch].counter += _sampleRate; - saa->channels[ch].level ^= 1; - - /* eventually clock the envelope counters */ - if (ch == 1 && saa->env_clock[0] == 0) - envelope(chip, 0); - if (ch == 4 && saa->env_clock[1] == 0) - envelope(chip, 1); - } - - /* if the noise is enabled */ - if (saa->channels[ch].noise_enable) { - /* if the noise level is high (noise 0: chan 0-2, noise 1: chan 3-5) */ - if (saa->noise[ch/3].level & 1) { - /* subtract to avoid overflows, also use only half amplitude */ - output_l -= saa->channels[ch].amplitude[ LEFT] * saa->channels[ch].envelope[ LEFT] / 16 / 2; - output_r -= saa->channels[ch].amplitude[RIGHT] * saa->channels[ch].envelope[RIGHT] / 16 / 2; - } - } - - /* if the square wave is enabled */ - if (saa->channels[ch].freq_enable) { - /* if the channel level is high */ - if (saa->channels[ch].level & 1) { - output_l += saa->channels[ch].amplitude[ LEFT] * saa->channels[ch].envelope[ LEFT] / 16; - output_r += saa->channels[ch].amplitude[RIGHT] * saa->channels[ch].envelope[RIGHT] / 16; - } - } - } - - for (ch = 0; ch < 2; ch++) { - /* check the actual position in noise generator */ - saa->noise[ch].counter -= saa->noise[ch].freq; - while (saa->noise[ch].counter < 0) { - saa->noise[ch].counter += _sampleRate; - if (((saa->noise[ch].level & 0x4000) == 0) == ((saa->noise[ch].level & 0x0040) == 0) ) - saa->noise[ch].level = (saa->noise[ch].level << 1) | 1; - else - saa->noise[ch].level <<= 1; - } - } - /* write sound data to the buffer */ - buffer[j*2] += output_l / 6; - buffer[j*2+1] += output_r / 6; - } -} - -void CMSEmulator::portWriteIntern(int chip, int offset, int data) { - SAA1099 *saa = &_saa1099[chip]; - int reg = saa->selected_reg; - int ch; - - switch (reg) { - /* channel i amplitude */ - case 0x00: - case 0x01: - case 0x02: - case 0x03: - case 0x04: - case 0x05: - ch = reg & 7; - saa->channels[ch].amplitude[LEFT] = amplitude_lookup[data & 0x0f]; - saa->channels[ch].amplitude[RIGHT] = amplitude_lookup[(data >> 4) & 0x0f]; - break; - - /* channel i frequency */ - case 0x08: - case 0x09: - case 0x0a: - case 0x0b: - case 0x0c: - case 0x0d: - ch = reg & 7; - saa->channels[ch].frequency = data & 0xff; - break; - - /* channel i octave */ - case 0x10: - case 0x11: - case 0x12: - ch = (reg - 0x10) << 1; - saa->channels[ch + 0].octave = data & 0x07; - saa->channels[ch + 1].octave = (data >> 4) & 0x07; - break; - - /* channel i frequency enable */ - case 0x14: - saa->channels[0].freq_enable = data & 0x01; - saa->channels[1].freq_enable = data & 0x02; - saa->channels[2].freq_enable = data & 0x04; - saa->channels[3].freq_enable = data & 0x08; - saa->channels[4].freq_enable = data & 0x10; - saa->channels[5].freq_enable = data & 0x20; - break; - - /* channel i noise enable */ - case 0x15: - saa->channels[0].noise_enable = data & 0x01; - saa->channels[1].noise_enable = data & 0x02; - saa->channels[2].noise_enable = data & 0x04; - saa->channels[3].noise_enable = data & 0x08; - saa->channels[4].noise_enable = data & 0x10; - saa->channels[5].noise_enable = data & 0x20; - break; - - /* noise generators parameters */ - case 0x16: - saa->noise_params[0] = data & 0x03; - saa->noise_params[1] = (data >> 4) & 0x03; - break; - - /* envelope generators parameters */ - case 0x18: - case 0x19: - ch = reg - 0x18; - saa->env_reverse_right[ch] = data & 0x01; - saa->env_mode[ch] = (data >> 1) & 0x07; - saa->env_bits[ch] = data & 0x10; - saa->env_clock[ch] = data & 0x20; - saa->env_enable[ch] = data & 0x80; - /* reset the envelope */ - saa->env_step[ch] = 0; - break; - - /* channels enable & reset generators */ - case 0x1c: - saa->all_ch_enable = data & 0x01; - saa->sync_state = data & 0x02; - if (data & 0x02) { - int i; - /* Synch & Reset generators */ - for (i = 0; i < 6; i++) { - saa->channels[i].level = 0; - saa->channels[i].counter = 0.0; - } - } - break; - - default: /* Error! */ - error("CMS Unkown write to reg %x with %x",reg, data); - } -} - -#pragma mark - -#pragma mark - Player_V2CMS -#pragma mark - - -const uint8 note_lengths[] = { - 0, - 0, 0, 2, - 0, 3, 4, - 5, 6, 8, - 9, 12, 16, - 18, 24, 32, - 36, 48, 64, - 72, 96 -}; - -static const uint16 hull_offsets[] = { - 0, 12, 24, 36, 48, 60, - 72, 88, 104, 120, 136, 256, - 152, 164, 180 -}; - -static const int16 hulls[] = { - // hull 0 - 3, -1, 0, 0, 0, 0, 0, 0, - 0, -1, 0, 0, - // hull 1 (staccato) - 3, -1, 0, 32, 0, -1, 0, 0, - 0, -1, 0, 0, - // hull 2 (legato) - 3, -1, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, - // hull 3 (staccatissimo) - 3, -1, 0, 2, 0, -1, 0, 0, - 0, -1, 0, 0, - // hull 4 - 3, -1, 0, 6, 0, -1, 0, 0, - 0, -1, 0, 0, - // hull 5 - 3, -1, 0, 16, 0, -1, 0, 0, - 0, -1, 0, 0, - // hull 6 - (int16) 60000, -1, -1000, 20, 0, 0, 0, 0, - (int16) 40000, -1, -5000, 5, 0, -1, 0, 0, - // hull 7 - (int16) 50000, -1, 0, 8, 30000, -1, 0, 0, - 28000, -1, -5000, 5, 0, -1, 0, 0, - // hull 8 - (int16) 60000, -1, -2000, 16, 0, 0, 0, 0, - 28000, -1, -6000, 5, 0, -1, 0, 0, - // hull 9 - (int16) 55000, -1, 0, 8, (int16) 35000, -1, 0, 0, - (int16) 40000, -1, -2000, 10, 0, -1, 0, 0, - // hull 10 - (int16) 60000, -1, 0, 4, -2000, 8, 0, 0, - (int16) 40000, -1, -6000, 5, 0, -1, 0, 0, - // hull 12 - 0, -1, 150, 340, -150, 340, 0, -1, - 0, -1, 0, 0, - // hull 13 == 164 - 20000, -1, 4000, 7, 1000, 15, 0, 0, - (int16) 35000, -1, -2000, 15, 0, -1, 0, 0, - - // hull 14 == 180 - (int16) 35000, -1, 500, 20, 0, 0, 0, 0, - (int16) 45000, -1, -500, 60, 0, -1, 0, 0, - - // hull misc = 196 - (int16) 44000, -1, -4400, 10, 0, -1, 0, 0, - 0, -1, 0, 0, - - (int16) 53000, -1, -5300, 10, 0, -1, 0, 0, - 0, -1, 0, 0, - - (int16) 63000, -1, -6300, 10, 0, -1, 0, 0, - 0, -1, 0, 0, - - (int16) 44000, -1, -1375, 32, 0, -1, 0, 0, - 0, -1, 0, 0, - - (int16) 53000, -1, -1656, 32, 0, -1, 0, 0, - 0, -1, 0, 0, - - // hull 11 == 256 - (int16) 63000, -1, -1968, 32, 0, -1, 0, 0, - 0, -1, 0, 0, - - (int16) 44000, -1, - 733, 60, 0, -1, 0, 0, - 0, -1, 0, 0, - - (int16) 53000, -1, - 883, 60, 0, -1, 0, 0, - 0, -1, 0, 0, - - (int16) 63000, -1, -1050, 60, 0, -1, 0, 0, - 0, -1, 0, 0, - - (int16) 44000, -1, - 488, 90, 0, -1, 0, 0, - 0, -1, 0, 0, - - (int16) 53000, -1, - 588, 90, 0, -1, 0, 0, - 0, -1, 0, 0, - - (int16) 63000, -1, - 700, 90, 0, -1, 0, 0, - 0, -1, 0, 0 -}; - -static const uint16 freqmod_lengths[] = { - 0x1000, 0x1000, 0x20, 0x2000, 0x1000 -}; - -static const uint16 freqmod_offsets[] = { - 0, 0x100, 0x200, 0x302, 0x202 -}; - -static const int8 freqmod_table[0x502] = { - 0, 3, 6, 9, 12, 15, 18, 21, - 24, 27, 30, 33, 36, 39, 42, 45, - 48, 51, 54, 57, 59, 62, 65, 67, - 70, 73, 75, 78, 80, 82, 85, 87, - 89, 91, 94, 96, 98, 100, 102, 103, - 105, 107, 108, 110, 112, 113, 114, 116, - 117, 118, 119, 120, 121, 122, 123, 123, - 124, 125, 125, 126, 126, 126, 126, 126, - 126, 126, 126, 126, 126, 126, 125, 125, - 124, 123, 123, 122, 121, 120, 119, 118, - 117, 116, 114, 113, 112, 110, 108, 107, - 105, 103, 102, 100, 98, 96, 94, 91, - 89, 87, 85, 82, 80, 78, 75, 73, - 70, 67, 65, 62, 59, 57, 54, 51, - 48, 45, 42, 39, 36, 33, 30, 27, - 24, 21, 18, 15, 12, 9, 6, 3, - 0, -3, -6, -9, -12, -15, -18, -21, - -24, -27, -30, -33, -36, -39, -42, -45, - -48, -51, -54, -57, -59, -62, -65, -67, - -70, -73, -75, -78, -80, -82, -85, -87, - -89, -91, -94, -96, -98,-100,-102,-103, - -105,-107,-108,-110,-112,-113,-114,-116, - -117,-118,-119,-120,-121,-122,-123,-123, - -124,-125,-125,-126,-126,-126,-126,-126, - -126,-126,-126,-126,-126,-126,-125,-125, - -124,-123,-123,-122,-121,-120,-119,-118, - -117,-116,-114,-113,-112,-110,-108,-107, - -105,-103,-102,-100, -98, -96, -94, -91, - -89, -87, -85, -82, -80, -78, -75, -73, - -70, -67, -65, -62, -59, -57, -54, -51, - -48, -45, -42, -39, -36, -33, -30, -27, - -24, -21, -18, -15, -12, -9, -6, -3, - - 0, 1, 2, 3, 4, 5, 6, 7, - 8, 9, 10, 11, 12, 13, 14, 15, - 16, 17, 18, 19, 20, 21, 22, 23, - 24, 25, 26, 27, 28, 29, 30, 31, - 32, 33, 34, 35, 36, 37, 38, 39, - 40, 41, 42, 43, 44, 45, 46, 47, - 48, 49, 50, 51, 52, 53, 54, 55, - 56, 57, 58, 59, 60, 61, 62, 63, - 64, 65, 66, 67, 68, 69, 70, 71, - 72, 73, 74, 75, 76, 77, 78, 79, - 80, 81, 82, 83, 84, 85, 86, 87, - 88, 89, 90, 91, 92, 93, 94, 95, - 96, 97, 98, 99, 100, 101, 102, 103, - 104, 105, 106, 107, 108, 109, 110, 111, - 112, 113, 114, 115, 116, 117, 118, 119, - 120, 121, 122, 123, 124, 125, 126, 127, - -128,-127,-126,-125,-124,-123,-122,-121, - -120,-119,-118,-117,-116,-115,-114,-113, - -112,-111,-110,-109,-108,-107,-106,-105, - -104,-103,-102,-101,-100, -99, -98, -97, - -96, -95, -94, -93, -92, -91, -90, -89, - -88, -87, -86, -85, -84, -83, -82, -81, - -80, -79, -78, -77, -76, -75, -74, -73, - -72, -71, -70, -69, -68, -67, -66, -65, - -64, -63, -62, -61, -60, -59, -58, -57, - -56, -55, -54, -53, -52, -51, -50, -49, - -48, -47, -46, -45, -44, -43, -42, -41, - -40, -39, -38, -37, -36, -35, -34, -33, - -32, -31, -30, -29, -28, -27, -26, -25, - -24, -23, -22, -21, -20, -19, -18, -17, - -16, -15, -14, -13, -12, -11, -10, -9, - -8, -7, -6, -5, -4, -3, -2, -1, - - -120, 120, - - -120,-120,-120,-120,-120,-120,-120,-120, - -120,-120,-120,-120,-120,-120,-120,-120, - -120,-120,-120,-120,-120,-120,-120,-120, - -120,-120,-120,-120,-120,-120,-120,-120, - -120,-120,-120,-120,-120,-120,-120,-120, - -120,-120,-120,-120,-120,-120,-120,-120, - -120,-120,-120,-120,-120,-120,-120,-120, - -120,-120,-120,-120,-120,-120,-120,-120, - -120,-120,-120,-120,-120,-120,-120,-120, - -120,-120,-120,-120,-120,-120,-120,-120, - -120,-120,-120,-120,-120,-120,-120,-120, - -120,-120,-120,-120,-120,-120,-120,-120, - -120,-120,-120,-120,-120,-120,-120,-120, - -120,-120,-120,-120,-120,-120,-120,-120, - -120,-120,-120,-120,-120,-120,-120,-120, - -120,-120,-120,-120,-120,-120,-120,-120, - 120, 120, 120, 120, 120, 120, 120, 120, - 120, 120, 120, 120, 120, 120, 120, 120, - 120, 120, 120, 120, 120, 120, 120, 120, - 120, 120, 120, 120, 120, 120, 120, 120, - 120, 120, 120, 120, 120, 120, 120, 120, - 120, 120, 120, 120, 120, 120, 120, 120, - 120, 120, 120, 120, 120, 120, 120, 120, - 120, 120, 120, 120, 120, 120, 120, 120, - 120, 120, 120, 120, 120, 120, 120, 120, - 120, 120, 120, 120, 120, 120, 120, 120, - 120, 120, 120, 120, 120, 120, 120, 120, - 120, 120, 120, 120, 120, 120, 120, 120, - 120, 120, 120, 120, 120, 120, 120, 120, - 120, 120, 120, 120, 120, 120, 120, 120, - 120, 120, 120, 120, 120, 120, 120, 120, - 120, 120, 120, 120, 120, 120, 120, 120, - - 41, 35, -66,-124, -31, 108, -42, -82, - 82,-112, 73, -15, -15, -69, -23, -21, - -77, -90, -37, 60,-121, 12, 62,-103, - 36, 94, 13, 28, 6, -73, 71, -34, - -77, 18, 77, -56, 67, -69,-117, -90, - 31, 3, 90, 125, 9, 56, 37, 31, - 93, -44, -53, -4,-106, -11, 69, 59, - 19, 13,-119, 10, 28, -37, -82, 50, - 32,-102, 80, -18, 64, 120, 54, -3, - 18, 73, 50, -10, -98, 125, 73, -36, - -83, 79, 20, -14, 68, 64, 102, -48, - 107, -60, 48, -73, 50, 59, -95, 34, - -10, 34,-111, -99, -31,-117, 31, -38, - -80, -54,-103, 2, -71, 114, -99, 73, - 44,-128, 126, -59,-103, -43, -23,-128, - -78, -22, -55, -52, 83, -65, 103, -42, - -65, 20, -42, 126, 45, -36,-114, 102, - -125, -17, 87, 73, 97, -1, 105,-113, - 97, -51, -47, 30, -99,-100, 22, 114, - 114, -26, 29, -16,-124, 79, 74, 119, - 2, -41, -24, 57, 44, 83, -53, -55, - 18, 30, 51, 116, -98, 12, -12, -43, - -44, -97, -44, -92, 89, 126, 53, -49, - 50, 34, -12, -52, -49, -45,-112, 45, - 72, -45,-113, 117, -26, -39, 29, 42, - -27, -64, -9, 43, 120,-127,-121, 68, - 14, 95, 80, 0, -44, 97,-115, -66, - 123, 5, 21, 7, 59, 51,-126, 31, - 24, 112,-110, -38, 100, 84, -50, -79, - -123, 62, 105, 21, -8, 70, 106, 4, - -106, 115, 14, -39, 22, 47, 103, 104, - -44, -9, 74, 74, -48, 87, 104, 118, - -6, 22, -69, 17, -83, -82, 36,-120, - 121, -2, 82, -37, 37, 67, -27, 60, - -12, 69, -45, -40, 40, -50, 11, -11, - -59, 96, 89, 61,-105, 39,-118, 89, - 118, 45, -48, -62, -55, -51, 104, -44, - 73, 106, 121, 37, 8, 97, 64, 20, - -79, 59, 106, -91, 17, 40, -63,-116, - -42, -87, 11,-121,-105,-116, 47, -15, - 21, 29,-102,-107, -63,-101, -31, -64, - 126, -23, -88,-102, -89,-122, -62, -75, - 84, -65,-102, -25, -39, 35, -47, 85, - -112, 56, 40, -47, -39, 108, -95, 102, - 94, 78, -31, 48,-100, -2, -39, 113, - -97, -30, -91, -30, 12,-101, -76, 71, - 101, 56, 42, 70,-119, -87,-126, 121, - 122, 118, 120, -62, 99, -79, 38, -33, - -38, 41, 109, 62, 98, -32,-106, 18, - 52, -65, 57, -90, 63,-119, 94, -15, - 109, 14, -29, 108, 40, -95, 30, 32, - 29, -53, -62, 3, 63, 65, 7,-124, - 15, 20, 5, 101, 27, 40, 97, -55, - -59, -25, 44,-114, 70, 54, 8, -36, - -13, -88,-115, -2, -66, -14, -21, 113, - -1, -96, -48, 59, 117, 6,-116, 126, - -121, 120, 115, 77, -48, -66,-126, -66, - -37, -62, 70, 65, 43,-116, -6, 48, - 127, 112, -16, -89, 84,-122, 50,-107, - -86, 91, 104, 19, 11, -26, -4, -11, - -54, -66, 125, -97,-119,-118, 65, 27, - -3, -72, 79, 104, -10, 114, 123, 20, - -103, -51, -45, 13, -16, 68, 58, -76, - -90, 102, 83, 51, 11, -53, -95, 16 -}; +#define CMS_RATE 22050 static const byte freqTable[] = { 3, 10, 17, 24, 31, 38, 45, 51, @@ -797,50 +129,17 @@ static const byte releaseRate[] = { 36, 56, 80, 100, 120, 140, 160, 255 }; -static const uint16 pcjr_freq_table[12] = { - 65472, 61760, 58304, 55040, 52032, 49024, - 46272, 43648, 41216, 38912, 36736, 34624 -}; - static const byte volumeTable[] = { 0x00, 0x10, 0x10, 0x11, 0x11, 0x21, 0x22, 0x22, 0x33, 0x44, 0x55, 0x66, 0x88, 0xAA, 0xCC, 0xFF }; -static CMSEmulator *g_cmsEmu = 0; - -Player_V2CMS::Player_V2CMS(ScummEngine *scumm, Audio::Mixer *mixer) { +Player_V2CMS::Player_V2CMS(ScummEngine *scumm, Audio::Mixer *mixer) + : Player_V2Base(scumm, mixer, true) { int i; - _isV3Game = (scumm->_game.version >= 3); - _vm = scumm; - _mixer = mixer; -// debug("mixer rate: %d", _mixer->getOutputRate()); - _sampleRate = CMS_RATE; - - _header_len = (scumm->_game.features & GF_OLD_BUNDLE) ? 4 : 6; - - // Initialize sound queue - _current_nr = _next_nr = 0; - _current_data = _next_data = 0; - - // Initialize channel code - for (i = 0; i < 4; ++i) - clear_channel(i); - - _next_tick = 0; - _tick_len = (_sampleRate << FIXP_SHIFT) / FREQ_HZ; - - // Initialize V3 music timer - _music_timer_ctr = _music_timer = 0; - _ticks_per_music_timer = 65535; - setMusicVolume(255); - _timer_output = 0; - for (i = 0; i < 4; i++) - _timer_count[i] = 0; - memset(_cmsVoicesBase, 0, sizeof(Voice)*16); memset(_cmsVoices, 0, sizeof(Voice2)*8); memset(_cmsChips, 0, sizeof(MusicChip)*2); @@ -876,7 +175,7 @@ Player_V2CMS::Player_V2CMS(ScummEngine *scumm, Audio::Mixer *mixer) { _cmsVoices[7].octaveOutput = &(_cmsChips[1].octave[1]); // inits the CMS Emulator like in the original - g_cmsEmu = new CMSEmulator(_sampleRate); + _cmsEmu = new CMSEmulator(_sampleRate); static const byte cmsInitData[13*2] = { 0x1C, 0x02, 0x00, 0x00, 0x01, 0x00, 0x02, 0x00, 0x03, 0x00, 0x04, 0x00, 0x05, 0x00, @@ -886,8 +185,8 @@ Player_V2CMS::Player_V2CMS(ScummEngine *scumm, Audio::Mixer *mixer) { i = 0; for (int cmsPort = 0x220; i < 2; cmsPort += 2, ++i) { for (int off = 0; off < 13; ++off) { - g_cmsEmu->portWrite(cmsPort+1, cmsInitData[off*2]); - g_cmsEmu->portWrite(cmsPort, cmsInitData[off*2+1]); + _cmsEmu->portWrite(cmsPort+1, cmsInitData[off*2]); + _cmsEmu->portWrite(cmsPort, cmsInitData[off*2+1]); } } @@ -898,40 +197,12 @@ Player_V2CMS::~Player_V2CMS() { Common::StackLock lock(_mutex); _mixer->stopHandle(_soundHandle); - delete g_cmsEmu; + delete _cmsEmu; } void Player_V2CMS::setMusicVolume(int vol) { } -void Player_V2CMS::chainSound(int nr, byte *data) { - int offset = _header_len + 10; - - _current_nr = nr; - _current_data = data; - - for (int i = 0; i < 4; i++) { - clear_channel(i); - - _channels[i].d.music_script_nr = nr; - if (data) { - _channels[i].d.next_cmd = READ_LE_UINT16(data + offset + 2 * i); - if (_channels[i].d.next_cmd) { - _channels[i].d.time_left = 1; - } - } - } - _music_timer = 0; -} - -void Player_V2CMS::chainNextSound() { - if (_next_nr) { - chainSound(_next_nr, _next_data); - _next_nr = 0; - _next_data = 0; - } -} - void Player_V2CMS::stopAllSounds() { Common::StackLock lock(_mutex); @@ -1083,275 +354,6 @@ int Player_V2CMS::getSoundStatus(int nr) const { return _current_nr == nr || _next_nr == nr || _loadedMidiSong == nr; } - -void Player_V2CMS::clear_channel(int i) { - ChannelInfo *channel = &_channels[i]; - memset(channel, 0, sizeof(ChannelInfo)); -} - -int Player_V2CMS::getMusicTimer() { - if (_isV3Game) - return _music_timer; - else - return _channels[0].d.music_timer; -} - -void Player_V2CMS::execute_cmd(ChannelInfo *channel) { - uint16 value; - int16 offset; - uint8 *script_ptr; - ChannelInfo * current_channel; - ChannelInfo * dest_channel; - - current_channel = channel; - - if (channel->d.next_cmd == 0) - goto check_stopped; - script_ptr = &_current_data[channel->d.next_cmd]; - - for (;;) { - uint8 opcode = *script_ptr++; - if (opcode >= 0xf8) { - switch (opcode) { - case 0xf8: // set hull curve - debug(7, "channels[%d]: hull curve %2d", - (uint)(channel - _channels), *script_ptr); - channel->d.hull_curve = hull_offsets[*script_ptr / 2]; - script_ptr++; - break; - - case 0xf9: // set freqmod curve - debug(7, "channels[%d]: freqmod curve %2d", - (uint)(channel - _channels), *script_ptr); - channel->d.freqmod_table = freqmod_offsets[*script_ptr / 4]; - channel->d.freqmod_modulo = freqmod_lengths[*script_ptr / 4]; - script_ptr++; - break; - - case 0xfd: // clear other channel - value = READ_LE_UINT16 (script_ptr) / sizeof (ChannelInfo); - debug(7, "clear channel %d", value); - script_ptr += 2; - // In Indy3, when traveling to Venice a command is - // issued to clear channel 4. So we introduce a 4th - // channel, which is never used. All OOB accesses are - // mapped to this channel. - // - // The original game had room for 8 channels, but only - // channels 0-3 are read, changes to other channels - // had no effect. - if (value >= ARRAYSIZE (_channels)) - value = 4; - channel = &_channels[value]; - // fall through - - case 0xfa: // clear current channel - if (opcode == 0xfa) - debug(7, "clear channel"); - channel->d.next_cmd = 0; - channel->d.base_freq = 0; - channel->d.freq_delta = 0; - channel->d.freq = 0; - channel->d.volume = 0; - channel->d.volume_delta = 0; - channel->d.inter_note_pause = 0; - channel->d.transpose = 0; - channel->d.hull_curve = 0; - channel->d.hull_offset = 0; - channel->d.hull_counter = 0; - channel->d.freqmod_table = 0; - channel->d.freqmod_offset = 0; - channel->d.freqmod_incr = 0; - channel->d.freqmod_multiplier = 0; - channel->d.freqmod_modulo = 0; - break; - - case 0xfb: // ret from subroutine - debug(7, "ret from sub"); - script_ptr = _retaddr; - break; - - case 0xfc: // call subroutine - offset = READ_LE_UINT16 (script_ptr); - debug(7, "subroutine %d", offset); - script_ptr += 2; - _retaddr = script_ptr; - script_ptr = _current_data + offset; - break; - - case 0xfe: // loop music - opcode = *script_ptr++; - offset = READ_LE_UINT16 (script_ptr); - script_ptr += 2; - debug(7, "loop if %d to %d", opcode, offset); - if (!channel->array[opcode / 2] || --channel->array[opcode/2]) - script_ptr += offset; - break; - - case 0xff: // set parameter - opcode = *script_ptr++; - value = READ_LE_UINT16 (script_ptr); - channel->array[opcode / 2] = value; - debug(7, "channels[%d]: set param %2d = %5d", - (uint)(channel - _channels), opcode, value); - script_ptr += 2; - if (opcode == 14) { - /* tempo var */ - _ticks_per_music_timer = 125; - } - if (opcode == 0) - goto end; - break; - } - } else { // opcode < 0xf8 - for (;;) { - int16 note, octave; - int is_last_note; - dest_channel = &_channels[(opcode >> 5) & 3]; - - if (!(opcode & 0x80)) { - - int tempo = channel->d.tempo; - if (!tempo) - tempo = 1; - channel->d.time_left = tempo * note_lengths[opcode & 0x1f]; - - note = *script_ptr++; - is_last_note = note & 0x80; - note &= 0x7f; - if (note == 0x7f) { - debug(8, "channels[%d]: pause %d", - (uint)(channel - _channels), channel->d.time_left); - goto end; - } - } else { - - channel->d.time_left = ((opcode & 7) << 8) | *script_ptr++; - - if ((opcode & 0x10)) { - debug(8, "channels[%d]: pause %d", - (uint)(channel - _channels), channel->d.time_left); - goto end; - } - - is_last_note = 0; - note = (*script_ptr++) & 0x7f; - } - - debug(8, "channels[%d]: @%04x note: %3d+%d len: %2d hull: %d mod: %d/%d/%d %s", - (uint)(dest_channel - channel), script_ptr ? (uint)(script_ptr - _current_data - 2) : 0, - note, (signed short) dest_channel->d.transpose, channel->d.time_left, - dest_channel->d.hull_curve, dest_channel->d.freqmod_table, - dest_channel->d.freqmod_incr,dest_channel->d.freqmod_multiplier, - is_last_note ? "last":""); - - uint16 myfreq; - dest_channel->d.time_left = channel->d.time_left; - dest_channel->d.note_length = - channel->d.time_left - dest_channel->d.inter_note_pause; - note += dest_channel->d.transpose; - while (note < 0) - note += 12; - octave = note / 12; - note = note % 12; - dest_channel->d.hull_offset = 0; - dest_channel->d.hull_counter = 1; - if (dest_channel == &_channels[3]) { - dest_channel->d.hull_curve = 196 + note * 12; - myfreq = 384 - 64 * octave; - } else { - myfreq = pcjr_freq_table[note] >> octave; - } - dest_channel->d.freq = dest_channel->d.base_freq = myfreq; - if (is_last_note) - goto end; - opcode = *script_ptr++; - } - } - } - -end: - channel = current_channel; - if (channel->d.time_left) { - channel->d.next_cmd = script_ptr - _current_data; - return; - } - - channel->d.next_cmd = 0; - -check_stopped: - int i; - for (i = 0; i < 4; i++) { - if (_channels[i].d.time_left) - return; - } - - _current_nr = 0; - _current_data = 0; - chainNextSound(); - return; -} - -void Player_V2CMS::next_freqs(ChannelInfo *channel) { - channel->d.volume += channel->d.volume_delta; - channel->d.base_freq += channel->d.freq_delta; - - channel->d.freqmod_offset += channel->d.freqmod_incr; - if (channel->d.freqmod_offset != 0) - if (channel->d.freqmod_offset > channel->d.freqmod_modulo) - channel->d.freqmod_offset -= channel->d.freqmod_modulo; - - channel->d.freq = - (int) (freqmod_table[channel->d.freqmod_table + (channel->d.freqmod_offset >> 4)]) - * (int) channel->d.freqmod_multiplier / 256 - + channel->d.base_freq; - - debug(9, "Freq: %d/%d, %d/%d/%d*%d %d", - channel->d.base_freq, (int16)channel->d.freq_delta, - channel->d.freqmod_table, channel->d.freqmod_offset, - channel->d.freqmod_incr, channel->d.freqmod_multiplier, - channel->d.freq); - - if (channel->d.note_length && !--channel->d.note_length) { - channel->d.hull_offset = 16; - channel->d.hull_counter = 1; - } - - if (!--channel->d.time_left) { - execute_cmd(channel); - } - - if (channel->d.hull_counter && !--channel->d.hull_counter) { - for (;;) { - const int16 *hull_ptr = hulls - + channel->d.hull_curve + channel->d.hull_offset / 2; - if (hull_ptr[1] == -1) { - channel->d.volume = hull_ptr[0]; - if (hull_ptr[0] == 0) - channel->d.volume_delta = 0; - channel->d.hull_offset += 4; - } else { - channel->d.volume_delta = hull_ptr[0]; - channel->d.hull_counter = hull_ptr[1]; - channel->d.hull_offset += 4; - break; - } - } - } -} - -void Player_V2CMS::nextTick() { - for (int i = 0; i < 4; i++) { - if (!_channels[i].d.time_left) - continue; - next_freqs(&_channels[i]); - } - if (_music_timer_ctr++ >= _ticks_per_music_timer) { - _music_timer_ctr = 0; - _music_timer++; - } -} - void Player_V2CMS::processMidiData(uint ticks) { byte *currentData = _midiData; byte command = 0x00; @@ -1412,7 +414,7 @@ int Player_V2CMS::readBuffer(int16 *buffer, const int numSamples) { Common::StackLock lock(_mutex); uint step = 1; - int len = numSamples/2; + int len = numSamples / 2; // maybe this needs a complete rewrite do { @@ -1442,7 +444,7 @@ int Player_V2CMS::readBuffer(int16 *buffer, const int numSamples) { step = len; if (step > (_next_tick >> FIXP_SHIFT)) step = (_next_tick >> FIXP_SHIFT); - g_cmsEmu->readBuffer(buffer, step); + _cmsEmu->readBuffer(buffer, step); buffer += 2 * step; _next_tick -= step << FIXP_SHIFT; } while (len -= step); @@ -1486,27 +488,27 @@ void Player_V2CMS::playVoice() { void Player_V2CMS::processChannel(Voice2 *channel) { ++_outputTableReady; switch (channel->nextProcessState) { - case PROCESS_RELEASE: - processRelease(channel); + case PROCESS_RELEASE: + processRelease(channel); break; - case PROCESS_ATTACK: - processAttack(channel); + case PROCESS_ATTACK: + processAttack(channel); break; - case PROCESS_DECAY: - processDecay(channel); + case PROCESS_DECAY: + processDecay(channel); break; - case PROCESS_SUSTAIN: - processSustain(channel); + case PROCESS_SUSTAIN: + processSustain(channel); break; - case PROCESS_VIBRATO: - processVibrato(channel); + case PROCESS_VIBRATO: + processVibrato(channel); break; - default: + default: break; } } @@ -1588,8 +590,8 @@ void Player_V2CMS::offAllChannels() { for (int cmsPort = 0x220, i = 0; i < 2; cmsPort += 2, ++i) { for (int off = 0; off < 10; ++off) { - g_cmsEmu->portWrite(cmsPort+1, cmsOffData[off*2]); - g_cmsEmu->portWrite(cmsPort, cmsOffData[off*2+1]); + _cmsEmu->portWrite(cmsPort+1, cmsOffData[off*2]); + _cmsEmu->portWrite(cmsPort, cmsOffData[off*2+1]); } }*/ } @@ -1778,32 +780,32 @@ void Player_V2CMS::play() { // with the high nibble of the volumeReg value // the right channels amplitude is set // with the low value the left channels amplitude - g_cmsEmu->portWrite(0x221, 0); - g_cmsEmu->portWrite(0x220, volumeReg[0]); - g_cmsEmu->portWrite(0x221, 1); - g_cmsEmu->portWrite(0x220, volumeReg[1]); - g_cmsEmu->portWrite(0x221, 2); - g_cmsEmu->portWrite(0x220, volumeReg[2]); - g_cmsEmu->portWrite(0x221, 3); - g_cmsEmu->portWrite(0x220, volumeReg[3]); - g_cmsEmu->portWrite(0x221, 8); - g_cmsEmu->portWrite(0x220, freqReg[0]); - g_cmsEmu->portWrite(0x221, 9); - g_cmsEmu->portWrite(0x220, freqReg[1]); - g_cmsEmu->portWrite(0x221, 10); - g_cmsEmu->portWrite(0x220, freqReg[2]); - g_cmsEmu->portWrite(0x221, 11); - g_cmsEmu->portWrite(0x220, freqReg[3]); - g_cmsEmu->portWrite(0x221, 0x10); - g_cmsEmu->portWrite(0x220, octaveReg[0]); - g_cmsEmu->portWrite(0x221, 0x11); - g_cmsEmu->portWrite(0x220, octaveReg[1]); - g_cmsEmu->portWrite(0x221, 0x14); - g_cmsEmu->portWrite(0x220, freqEnable); - g_cmsEmu->portWrite(0x221, 0x15); - g_cmsEmu->portWrite(0x220, noiseEnable); - g_cmsEmu->portWrite(0x221, 0x16); - g_cmsEmu->portWrite(0x220, noiseGen); + _cmsEmu->portWrite(0x221, 0); + _cmsEmu->portWrite(0x220, volumeReg[0]); + _cmsEmu->portWrite(0x221, 1); + _cmsEmu->portWrite(0x220, volumeReg[1]); + _cmsEmu->portWrite(0x221, 2); + _cmsEmu->portWrite(0x220, volumeReg[2]); + _cmsEmu->portWrite(0x221, 3); + _cmsEmu->portWrite(0x220, volumeReg[3]); + _cmsEmu->portWrite(0x221, 8); + _cmsEmu->portWrite(0x220, freqReg[0]); + _cmsEmu->portWrite(0x221, 9); + _cmsEmu->portWrite(0x220, freqReg[1]); + _cmsEmu->portWrite(0x221, 10); + _cmsEmu->portWrite(0x220, freqReg[2]); + _cmsEmu->portWrite(0x221, 11); + _cmsEmu->portWrite(0x220, freqReg[3]); + _cmsEmu->portWrite(0x221, 0x10); + _cmsEmu->portWrite(0x220, octaveReg[0]); + _cmsEmu->portWrite(0x221, 0x11); + _cmsEmu->portWrite(0x220, octaveReg[1]); + _cmsEmu->portWrite(0x221, 0x14); + _cmsEmu->portWrite(0x220, freqEnable); + _cmsEmu->portWrite(0x221, 0x15); + _cmsEmu->portWrite(0x220, noiseEnable); + _cmsEmu->portWrite(0x221, 0x16); + _cmsEmu->portWrite(0x220, noiseGen); } void Player_V2CMS::playMusicChips(const MusicChip *table) { @@ -1811,30 +813,30 @@ void Player_V2CMS::playMusicChips(const MusicChip *table) { do { cmsPort += 2; - g_cmsEmu->portWrite(cmsPort+1, 0); - g_cmsEmu->portWrite(cmsPort, table->ampl[0]); - g_cmsEmu->portWrite(cmsPort+1, 1); - g_cmsEmu->portWrite(cmsPort, table->ampl[1]); - g_cmsEmu->portWrite(cmsPort+1, 2); - g_cmsEmu->portWrite(cmsPort, table->ampl[2]); - g_cmsEmu->portWrite(cmsPort+1, 3); - g_cmsEmu->portWrite(cmsPort, table->ampl[3]); - g_cmsEmu->portWrite(cmsPort+1, 8); - g_cmsEmu->portWrite(cmsPort, table->freq[0]); - g_cmsEmu->portWrite(cmsPort+1, 9); - g_cmsEmu->portWrite(cmsPort, table->freq[1]); - g_cmsEmu->portWrite(cmsPort+1, 10); - g_cmsEmu->portWrite(cmsPort, table->freq[2]); - g_cmsEmu->portWrite(cmsPort+1, 11); - g_cmsEmu->portWrite(cmsPort, table->freq[3]); - g_cmsEmu->portWrite(cmsPort+1, 0x10); - g_cmsEmu->portWrite(cmsPort, table->octave[0]); - g_cmsEmu->portWrite(cmsPort+1, 0x11); - g_cmsEmu->portWrite(cmsPort, table->octave[1]); - g_cmsEmu->portWrite(cmsPort+1, 0x14); - g_cmsEmu->portWrite(cmsPort, 0x3F); - g_cmsEmu->portWrite(cmsPort+1, 0x15); - g_cmsEmu->portWrite(cmsPort, 0x00); + _cmsEmu->portWrite(cmsPort+1, 0); + _cmsEmu->portWrite(cmsPort, table->ampl[0]); + _cmsEmu->portWrite(cmsPort+1, 1); + _cmsEmu->portWrite(cmsPort, table->ampl[1]); + _cmsEmu->portWrite(cmsPort+1, 2); + _cmsEmu->portWrite(cmsPort, table->ampl[2]); + _cmsEmu->portWrite(cmsPort+1, 3); + _cmsEmu->portWrite(cmsPort, table->ampl[3]); + _cmsEmu->portWrite(cmsPort+1, 8); + _cmsEmu->portWrite(cmsPort, table->freq[0]); + _cmsEmu->portWrite(cmsPort+1, 9); + _cmsEmu->portWrite(cmsPort, table->freq[1]); + _cmsEmu->portWrite(cmsPort+1, 10); + _cmsEmu->portWrite(cmsPort, table->freq[2]); + _cmsEmu->portWrite(cmsPort+1, 11); + _cmsEmu->portWrite(cmsPort, table->freq[3]); + _cmsEmu->portWrite(cmsPort+1, 0x10); + _cmsEmu->portWrite(cmsPort, table->octave[0]); + _cmsEmu->portWrite(cmsPort+1, 0x11); + _cmsEmu->portWrite(cmsPort, table->octave[1]); + _cmsEmu->portWrite(cmsPort+1, 0x14); + _cmsEmu->portWrite(cmsPort, 0x3F); + _cmsEmu->portWrite(cmsPort+1, 0x15); + _cmsEmu->portWrite(cmsPort, 0x00); ++table; } while ((cmsPort & 2) == 0); } diff --git a/engines/scumm/player_v2cms.h b/engines/scumm/player_v2cms.h new file mode 100644 index 0000000000..fd939d8505 --- /dev/null +++ b/engines/scumm/player_v2cms.h @@ -0,0 +1,166 @@ +/* ScummVM - Graphic Adventure Engine + * + * ScummVM is the legal property of its developers, whose names + * are too numerous to list here. Please refer to the COPYRIGHT + * file distributed with this source distribution. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * $URL$ + * $Id$ + * + */ + +#ifndef SCUMM_PLAYER_V2CMS_H +#define SCUMM_PLAYER_V2CMS_H + +#include "scumm/player_v2base.h" // for channel_data + +class CMSEmulator; + +namespace Scumm { + +/** + * Scumm V2 CMS/Gameblaster MIDI driver. + */ +class Player_V2CMS : public Player_V2Base { +public: + Player_V2CMS(ScummEngine *scumm, Audio::Mixer *mixer); + virtual ~Player_V2CMS(); + + // MusicEngine API + virtual void setMusicVolume(int vol); + virtual void startSound(int sound); + virtual void stopSound(int sound); + virtual void stopAllSounds(); +// virtual int getMusicTimer(); + virtual int getSoundStatus(int sound) const; + + // AudioStream API + int readBuffer(int16 *buffer, const int numSamples); + bool isStereo() const { return true; } + bool endOfData() const { return false; } + int getRate() const { return _sampleRate; } + +protected: + +#include "common/pack-start.h" // START STRUCT PACKING + struct Voice { + byte attack; + byte decay; + byte sustain; + byte release; + byte octadd; + int16 vibrato; + int16 vibrato2; + int16 noise; + } PACKED_STRUCT; + + struct Voice2 { + byte *amplitudeOutput; + byte *freqOutput; + byte *octaveOutput; + + uint8 channel; + int8 sustainLevel; + int8 attackRate; + uint8 maxAmpl; + int8 decayRate; + int8 sustainRate; + int8 releaseRate; + int8 releaseTime; + int8 vibratoRate; + int8 vibratoDepth; + + int8 curVibratoRate; + int8 curVibratoUnk; + + int8 unkVibratoRate; + int8 unkVibratoDepth; + + int8 unkRate; + int8 unkCount; + + int nextProcessState; + int8 curVolume; + int8 curOctave; + int8 curFreq; + + int8 octaveAdd; + + int8 playingNote; + Voice2 *nextVoice; + + byte chanNumber; + } PACKED_STRUCT; + + struct MusicChip { + byte ampl[4]; + byte freq[4]; + byte octave[2]; + } PACKED_STRUCT; +#include "common/pack-end.h" // END STRUCT PACKING + + Voice _cmsVoicesBase[16]; + Voice2 _cmsVoices[8]; + MusicChip _cmsChips[2]; + + int8 _tempo; + int8 _tempoSum; + byte _looping; + byte _octaveMask; + int16 _midiDelay; + Voice2 *_midiChannel[16]; + byte _midiChannelUse[16]; + byte *_midiData; + byte *_midiSongBegin; + + int _loadedMidiSong; + + byte _lastMidiCommand; + uint _outputTableReady; + byte _clkFrequenz; + byte _restart; + byte _curSno; + + void loadMidiData(byte *data, int sound); + void play(); + + void processChannel(Voice2 *channel); + void processRelease(Voice2 *channel); + void processAttack(Voice2 *channel); + void processDecay(Voice2 *channel); + void processSustain(Voice2 *channel); + void processVibrato(Voice2 *channel); + + void playMusicChips(const MusicChip *table); + void playNote(byte *&data); + void clearNote(byte *&data); + void offAllChannels(); + void playVoice(); + void processMidiData(uint ticks); + + Voice2 *getFreeVoice(); + Voice2 *getPlayVoice(byte param); + + // from Player_V2 +protected: + CMSEmulator *_cmsEmu; + +}; + +} // End of namespace Scumm + +#endif diff --git a/engines/scumm/resource.cpp b/engines/scumm/resource.cpp index d9a5e3414c..5aae59d987 100644 --- a/engines/scumm/resource.cpp +++ b/engines/scumm/resource.cpp @@ -1012,7 +1012,7 @@ void ResourceManager::freeResources() { void ScummEngine::loadPtrToResource(int type, int resindex, const byte *source) { byte *alloced; - int i, len; + int len; _res->nukeResource(type, resindex); @@ -1024,12 +1024,13 @@ void ScummEngine::loadPtrToResource(int type, int resindex, const byte *source) alloced = _res->createResource(type, resindex, len); if (!source) { - alloced[0] = fetchScriptByte(); - for (i = 1; i < len; i++) - alloced[i] = *_scriptPointer++; + // Need to refresh the script pointer, since createResource may + // have caused the script resource to expire. + refreshScriptPointer(); + memcpy(alloced, _scriptPointer, len); + _scriptPointer += len; } else { - for (i = 0; i < len; i++) - alloced[i] = source[i]; + memcpy(alloced, source, len); } } diff --git a/engines/scumm/room.cpp b/engines/scumm/room.cpp index 014787ec7e..02b2482e40 100644 --- a/engines/scumm/room.cpp +++ b/engines/scumm/room.cpp @@ -194,6 +194,11 @@ void ScummEngine::startScene(int room, Actor *a, int objectNr) { showActors(); _egoPositioned = false; + +#ifndef DISABLE_TOWNS_DUAL_LAYER_MODE + towns_resetPalCycleFields(); +#endif + runEntryScript(); if (_game.version >= 1 && _game.version <= 2) { runScript(5, 0, 0, 0); diff --git a/engines/scumm/saveload.cpp b/engines/scumm/saveload.cpp index ca48a2b86a..9cb603e27f 100644 --- a/engines/scumm/saveload.cpp +++ b/engines/scumm/saveload.cpp @@ -378,10 +378,10 @@ bool ScummEngine::loadState(int slot, bool compat) { return false; } - _engineStartTime = _system->getMillis() / 1000 - infos.playtime; + setTotalPlayTime(infos.playtime * 1000); } else { // start time counting - _engineStartTime = _system->getMillis() / 1000; + setTotalPlayTime(); } // Due to a bug in scummvm up to and including 0.3.0, save games could be saved @@ -797,7 +797,7 @@ void ScummEngine::saveInfos(Common::WriteStream* file) { // still save old format for older versions section.timeTValue = 0; - section.playtime = _system->getMillis() / 1000 - _engineStartTime; + section.playtime = getTotalPlayTime() / 1000; TimeDate curTime; _system->getTimeAndDate(curTime); @@ -1294,9 +1294,38 @@ void ScummEngine::saveOrLoad(Serializer *s) { // // Save/load palette data // - if (_16BitPalette) { + if (_16BitPalette && !(_game.platform == Common::kPlatformFMTowns && s->isLoading() && s->getVersion() < VER(82))) { s->saveLoadArrayOf(_16BitPalette, 512, sizeof(_16BitPalette[0]), sleUint16); } + +#ifndef DISABLE_TOWNS_DUAL_LAYER_MODE + // FM-Towns specific (extra palette data, color cycle data, etc.) + if (s->getVersion() >= VER(82)) { + const SaveLoadEntry townsFields[] = { + MKLINE(Common::Rect, left, sleInt16, VER(82)), + MKLINE(Common::Rect, top, sleInt16, VER(82)), + MKLINE(Common::Rect, right, sleInt16, VER(82)), + MKLINE(Common::Rect, bottom, sleInt16, VER(82)), + MKEND() + }; + + const SaveLoadEntry townsExtraEntries[] = { + MKLINE(ScummEngine, _townsOverrideShadowColor, sleUint8, VER(82)), + MKLINE(ScummEngine, _numCyclRects, sleUint8, VER(82)), + MKLINE(ScummEngine, _townsPaletteFlags, sleUint8, VER(82)), + MKLINE(ScummEngine, _townsClearLayerFlag, sleUint8, VER(82)), + MKLINE(ScummEngine, _townsActiveLayerFlags, sleUint8, VER(82)), + MKEND() + }; + + s->saveLoadArrayOf(_textPalette, 48, sizeof(_textPalette[0]), sleUint8); + s->saveLoadArrayOf(_cyclRects, 10, sizeof(_cyclRects[0]), townsFields); + s->saveLoadArrayOf(&_curStringRect, 1, sizeof(_curStringRect), townsFields); + s->saveLoadArrayOf(_townsCharsetColorMap, 16, sizeof(_townsCharsetColorMap[0]), sleUint8); + s->saveLoadEntries(this, townsExtraEntries); + } +#endif + if (_shadowPaletteSize) { s->saveLoadArrayOf(_shadowPalette, _shadowPaletteSize, 1, sleByte); // _roomPalette didn't show up until V21 save games @@ -1459,6 +1488,17 @@ void ScummEngine_v5::saveOrLoad(Serializer *s) { // This is probably only needed for Loom. s->saveLoadEntries(this, cursorEntries); + + // Reset cursors for old FM-Towns savegames saved with 256 color setting. + // Otherwise the cursor will be messed up when displayed in the new hi color setting. + if (_game.platform == Common::kPlatformFMTowns && _bytesPerPixelOutput == 2 && s->isLoading() && s->getVersion() < VER(82)) { + if (_game.id == GID_LOOM) { + redefineBuiltinCursorFromChar(1, 1); + redefineBuiltinCursorHotspot(1, 0, 0); + } else { + resetCursors(); + } + } } #ifdef ENABLE_SCUMM_7_8 diff --git a/engines/scumm/saveload.h b/engines/scumm/saveload.h index 44c8cc7d60..91e780bcd1 100644 --- a/engines/scumm/saveload.h +++ b/engines/scumm/saveload.h @@ -50,7 +50,7 @@ namespace Scumm { * only saves/loads those which are valid for the version of the savegame * which is being loaded/saved currently. */ -#define CURRENT_VER 81 +#define CURRENT_VER 84 /** * An auxillary macro, used to specify savegame versions. We use this instead diff --git a/engines/scumm/script.cpp b/engines/scumm/script.cpp index 223e9822e2..b6058d4d9a 100644 --- a/engines/scumm/script.cpp +++ b/engines/scumm/script.cpp @@ -339,7 +339,7 @@ void ScummEngine::runScriptNested(int script) { _currentScript = script; getScriptBaseAddress(); - getScriptEntryPoint(); + resetScriptPointer(); executeScript(); if (vm.numNestedScripts != 0) @@ -354,7 +354,7 @@ void ScummEngine::runScriptNested(int script) { slot->status != ssDead && slot->freezeCount == 0) { _currentScript = nest->slot; getScriptBaseAddress(); - getScriptEntryPoint(); + resetScriptPointer(); return; } } @@ -440,13 +440,27 @@ void ScummEngine::getScriptBaseAddress() { } } - -void ScummEngine::getScriptEntryPoint() { +void ScummEngine::resetScriptPointer() { if (_currentScript == 0xFF) return; _scriptPointer = _scriptOrgPointer + vm.slot[_currentScript].offs; } +/** + * This method checks whether the resource that contains the active script + * moved, and if so, updates the script pointer accordingly. + * + * The script resource may have moved because it might have been garbage + * collected by ResourceManager::expireResources. + */ +void ScummEngine::refreshScriptPointer() { + if (*_lastCodePtr + sizeof(MemBlkHeader) != _scriptOrgPointer) { + long oldoffs = _scriptPointer - _scriptOrgPointer; + getScriptBaseAddress(); + _scriptPointer = _scriptOrgPointer + oldoffs; + } +} + /** Execute a script - Read opcode, and execute it from the table */ void ScummEngine::executeScript() { int c; @@ -492,20 +506,12 @@ const char *ScummEngine::getOpcodeDesc(byte i) { } byte ScummEngine::fetchScriptByte() { - if (*_lastCodePtr + sizeof(MemBlkHeader) != _scriptOrgPointer) { - long oldoffs = _scriptPointer - _scriptOrgPointer; - getScriptBaseAddress(); - _scriptPointer = _scriptOrgPointer + oldoffs; - } + refreshScriptPointer(); return *_scriptPointer++; } uint ScummEngine::fetchScriptWord() { - if (*_lastCodePtr + sizeof(MemBlkHeader) != _scriptOrgPointer) { - long oldoffs = _scriptPointer - _scriptOrgPointer; - getScriptBaseAddress(); - _scriptPointer = _scriptOrgPointer + oldoffs; - } + refreshScriptPointer(); uint a = READ_LE_UINT16(_scriptPointer); _scriptPointer += 2; return a; @@ -516,11 +522,7 @@ int ScummEngine::fetchScriptWordSigned() { } uint ScummEngine::fetchScriptDWord() { - if (*_lastCodePtr + sizeof(MemBlkHeader) != _scriptOrgPointer) { - long oldoffs = _scriptPointer - _scriptOrgPointer; - getScriptBaseAddress(); - _scriptPointer = _scriptOrgPointer + oldoffs; - } + refreshScriptPointer(); uint a = READ_LE_UINT32(_scriptPointer); _scriptPointer += 4; return a; @@ -898,7 +900,7 @@ void ScummEngine::runAllScripts() { if (vm.slot[i].cycle == cycle && vm.slot[i].status == ssRunning && !vm.slot[i].didexec) { _currentScript = (byte)i; getScriptBaseAddress(); - getScriptEntryPoint(); + resetScriptPointer(); executeScript(); } } @@ -1231,22 +1233,26 @@ bool ScummEngine::isRoomScriptRunning(int script) const { void ScummEngine::copyScriptString(byte *dst) { int len = resStrLen(_scriptPointer) + 1; - while (len--) - *dst++ = fetchScriptByte(); + memcpy(dst, _scriptPointer, len); + _scriptPointer += len; + dst += len; *dst = 0; } -// -// Given a pointer to a Scumm string, this function returns the total byte length -// of the string data in that resource. To do so it has to understand certain -// special characters embedded into the string. The reason for this function is that -// sometimes this embedded data contains zero bytes, thus we can't just use strlen. -// -int ScummEngine::resStrLen(const byte *src) const { +/** + * Given a pointer to a Scumm string, this function returns the total + * byte length of the string data in that resource. To do so it has to + * understand certain special characters embedded into the string. The + * reason for this function is that sometimes this embedded data + * contains zero bytes, thus we can't just use strlen. + */ +int ScummEngine::resStrLen(const byte *src) { int num = 0; byte chr; - if (src == NULL) + if (src == NULL) { + refreshScriptPointer(); src = _scriptPointer; + } while ((chr = *src++) != 0) { num++; if (_game.heversion <= 71 && chr == 0xFF) { diff --git a/engines/scumm/script_v0.cpp b/engines/scumm/script_v0.cpp index cf44ee195e..d3f256c951 100644 --- a/engines/scumm/script_v0.cpp +++ b/engines/scumm/script_v0.cpp @@ -829,6 +829,8 @@ void ScummEngine_v0::o_setActorBitVar() { // This flag causes the actor to stop moving (used by script #158, Green Tentacle 'Oomph!') if (a->_miscflags & 0x40) a->stopActorMoving(); + if (a->_miscflags & 0x80) + a->setActorCostume(0); debug(0, "o_setActorBitVar(%d, %d, %d)", act, mask, mod); } @@ -987,7 +989,10 @@ void ScummEngine_v0::o_setOwnerOf() { void ScummEngine_v0::resetSentence(bool walking) { _activeVerb = 13; - if (!walking) { + // If the actor is walking, or the screen is a keypad (no sentence verbs/objects are drawn) + // Then reset all active objects (stops the radio crash, bug #3077966) + if (!walking || !(_userState & 32)) { + _v0ObjectFlag = 0; _activeInventory = 0; _activeObject = 0; _activeObject2 = 0; diff --git a/engines/scumm/script_v2.cpp b/engines/scumm/script_v2.cpp index bc8446d16f..d3a2272a39 100644 --- a/engines/scumm/script_v2.cpp +++ b/engines/scumm/script_v2.cpp @@ -1174,6 +1174,8 @@ void ScummEngine_v2::o2_walkActorToObject() { int obj; Actor *a; + _v0ObjectFlag = 0; + a = derefActor(getVarOrDirectByte(PARAM_1), "o2_walkActorToObject"); obj = getVarOrDirectWord(PARAM_2); if (whereIsObject(obj) != WIO_NOT_FOUND) { @@ -1182,6 +1184,7 @@ void ScummEngine_v2::o2_walkActorToObject() { AdjustBoxResult r = a->adjustXYToBeInBox(x, y); x = r.x; y = r.y; + a->startWalkActor(x, y, dir); } } diff --git a/engines/scumm/script_v4.cpp b/engines/scumm/script_v4.cpp index 6dc3004432..b6e5834acc 100644 --- a/engines/scumm/script_v4.cpp +++ b/engines/scumm/script_v4.cpp @@ -111,62 +111,17 @@ void ScummEngine_v4::o4_oldRoomEffect() { if ((_opcode & 0x1F) == 3) { a = getVarOrDirectWord(PARAM_1); -#if 1 if (_game.platform == Common::kPlatformFMTowns && _game.version == 3) { - // FIXME / TODO: OK the first thing to note is: at least in Zak256, - // maybe also in other games, this opcode does a bit more. I added - // some stubs here, but somebody with a full IDA or more knowledge - // about this will have to fill in the gaps. At least now we know - // that something is missing here :-) - if (a == 4) { - //printf("o5_oldRoomEffect ODDBALL: _opcode = 0x%x, a = 0x%x\n", _opcode, a); - // No idea what byte_2FCCF is, but it's a globale boolean flag. - // I only add it here as a temporary hack to make the pseudo code compile. - // Maybe it is just there as a reentry protection guard, given - // how it is used? It might also correspond to _screenEffectFlag. - int byte_2FCCF = 0; - - // For now, we force a redraw of the screen background. This - // way the Zak end credits seem to work mostly correct. - VirtScreen *vs = &_virtscr[kMainVirtScreen]; - restoreBackground(Common::Rect(0, vs->topline, vs->w, vs->topline + vs->h)); - vs->setDirtyRange(0, vs->h); - updateDirtyScreen(kMainVirtScreen); - - if (byte_2FCCF) { - // Here now "sub_1C44" is called, which sets byte_2FCCF to 0 then - // calls yet another sub (which also reads byte_2FCCF): - - byte_2FCCF = 0; - //call sub_0BB3 - - - // Now sub_085C is called. This is quite simply: it sets - // 0xF000 bytes. starting at 0x40000 to 0. No idea what that - // buffer is, maybe a screen buffer, though. Note that - // 0xF000 = 320*192. - // Maybe this is also the charset mask being cleaned? - - // call sub_085C - - - // And then sub_1C54 is called, which is almost identical to - // the above sub_1C44, only it sets byte_2FCCF to 1: - - byte_2FCCF = 1; - // call sub_0BB3 - - } else { - // Here only sub_085C is called (see comment above) - - // call sub_085C - } - return; - } + _textSurface.fillRect(Common::Rect(0, 0, _textSurface.w * _textSurfaceMultiplier, _textSurface.h * _textSurfaceMultiplier), 0); +#ifndef DISABLE_TOWNS_DUAL_LAYER_MODE + if (_townsScreen) + _townsScreen->clearLayer(1); #endif - + return; + } } + if (a) { _switchRoomEffect = (byte)(a & 0xFF); _switchRoomEffect2 = (byte)(a >> 8); diff --git a/engines/scumm/script_v5.cpp b/engines/scumm/script_v5.cpp index 8d986afc66..d3e1ba43ef 100644 --- a/engines/scumm/script_v5.cpp +++ b/engines/scumm/script_v5.cpp @@ -1617,7 +1617,7 @@ void ScummEngine_v5::o5_resourceRoutines() { void ScummEngine_v5::o5_roomOps() { int a = 0, b = 0, c, d, e; - const bool paramsBeforeOpcode = (_game.version == 3 && _game.platform != Common::kPlatformPCEngine); + const bool paramsBeforeOpcode = ((_game.version == 3) && (_game.platform != Common::kPlatformPCEngine)); if (paramsBeforeOpcode) { a = getVarOrDirectWord(PARAM_1); @@ -1712,26 +1712,58 @@ void ScummEngine_v5::o5_roomOps() { case 10: // SO_ROOM_FADE a = getVarOrDirectWord(PARAM_1); if (a) { + #ifndef DISABLE_TOWNS_DUAL_LAYER_MODE if (_game.platform == Common::kPlatformFMTowns) { switch (a) { - case 8: // compose kMainVirtScreen over a screen buffer - case 9: // call 0x110:0x20 _ax=0x601 _edx=2 - case 10: // call 0x110:0x20 _ax=0x601 _edx=3 - case 11: // clear screen 0x1C:0x45000 sizeof(640 * 320) - case 12: // call 0x110:0x20 _ax=0x601 _edx=0 - case 13: // call 0x110:0x20 _ax=0x601 _edx=1 - case 16: // enable clearing of a screen buffer in drawBitmap() - case 17: // disable clearing of a screen buffer in drawBitmap() - case 18: // clear a screen buffer + case 8: + towns_drawStripToScreen(&_virtscr[kMainVirtScreen], 0, _virtscr[kMainVirtScreen].topline, 0, 0, _virtscr[kMainVirtScreen].w, _virtscr[kMainVirtScreen].topline + _virtscr[kMainVirtScreen].h); + _townsScreen->update(); + return; + case 9: + _townsActiveLayerFlags = 2; + _townsScreen->toggleLayers(_townsActiveLayerFlags); + return; + case 10: + _townsActiveLayerFlags = 3; + _townsScreen->toggleLayers(_townsActiveLayerFlags); + return; + case 11: + _townsScreen->clearLayer(1); + return; + case 12: + _townsActiveLayerFlags = 0; + _townsScreen->toggleLayers(_townsActiveLayerFlags); + return; + case 13: + _townsActiveLayerFlags = 1; + _townsScreen->toggleLayers(_townsActiveLayerFlags); + return; + case 16: // enable clearing of layer 2 buffer in drawBitmap() + _townsPaletteFlags |= 2; + return; + case 17: // disable clearing of layer 2 buffer in drawBitmap() + _townsPaletteFlags &= ~2; + return; + case 18: // clear kMainVirtScreen layer 2 buffer + _textSurface.fillRect(Common::Rect(0, _virtscr[kMainVirtScreen].topline * _textSurfaceMultiplier, _textSurface.pitch, (_virtscr[kMainVirtScreen].topline + _virtscr[kMainVirtScreen].h) * _textSurfaceMultiplier), 0); case 19: // enable palette operations (palManipulate(), cyclePalette() etc.) + _townsPaletteFlags |= 1; + return; case 20: // disable palette operations - case 21: // disable clearing of screen 0x1C:0x5000 sizeof(640 * 320) in initScreens() - case 22: // enable clearing of screen 0x1C:0x5000 sizeof(640 * 320) in initScreens() + _townsPaletteFlags &= ~1; + return; + case 21: // disable clearing of layer 0 in initScreens() + _townsClearLayerFlag = 1; + return; + case 22: // enable clearing of layer 0 in initScreens() + _townsClearLayerFlag = 0; + return; case 30: - debug(0, "o5_roomOps: unhandled FM-TOWNS fadeEffect %d", a); + _townsOverrideShadowColor = 3; return; } } +#endif // DISABLE_TOWNS_DUAL_LAYER_MODE _switchRoomEffect = (byte)(a & 0xFF); _switchRoomEffect2 = (byte)(a >> 8); } else { @@ -1808,17 +1840,12 @@ void ScummEngine_v5::o5_roomOps() { Common::InSaveFile *file = _saveFileMan->openForLoading(filename); if (file != NULL) { byte *ptr; - int len = 256, cnt = 0; - ptr = (byte *)malloc(len); - while (ptr) { - int r = file->read(ptr + cnt, len - cnt); - cnt += r; - if (cnt < len) - break; - len *= 2; - ptr = (byte *)realloc(ptr, len); - } - ptr[cnt] = '\0'; + const int len = file->size(); + ptr = (byte *)malloc(len + 1); + assert(ptr); + int r = file->read(ptr, len); + assert(r == len); + ptr[len] = '\0'; loadPtrToResource(rtString, a, ptr); free(ptr); delete file; @@ -2124,6 +2151,7 @@ void ScummEngine_v5::o5_stringOps() { case 2: /* copystring */ a = getVarOrDirectByte(PARAM_1); b = getVarOrDirectByte(PARAM_2); + assert(a != b); _res->nukeResource(rtString, a); ptr = getResourceAddress(rtString, b); if (ptr) diff --git a/engines/scumm/script_v6.cpp b/engines/scumm/script_v6.cpp index c5841dfaf4..0226343df5 100644 --- a/engines/scumm/script_v6.cpp +++ b/engines/scumm/script_v6.cpp @@ -536,7 +536,18 @@ void ScummEngine_v6::o6_not() { } void ScummEngine_v6::o6_eq() { - push(pop() == pop()); + int a = pop(); + int b = pop(); + + // WORKAROUND: Forces the game version string set via script 1 to be used in both Macintosh and Windows versions, + // when checking for save game compatibility. Allows saved games to be shared between Macintosh and Windows versions. + // The scripts check VAR_PLATFORM (b) against the value (2) of the Macintosh platform (a). + if (_game.id == GID_BASEBALL2001 && (vm.slot[_currentScript].number == 291 || vm.slot[_currentScript].number == 292) && + a == 2 && b == 1) { + push(1); + } else { + push(a == b); + } } void ScummEngine_v6::o6_neq() { @@ -1285,7 +1296,7 @@ void ScummEngine_v6::o6_loadRoomWithEgo() { void ScummEngine_v6::o6_getRandomNumber() { int rnd; - rnd = _rnd.getRandomNumber(pop()); + rnd = _rnd.getRandomNumber(ABS(pop())); if (VAR_RANDOM_NR != 0xFF) VAR(VAR_RANDOM_NR) = rnd; push(rnd); diff --git a/engines/scumm/scumm-md5.h b/engines/scumm/scumm-md5.h index a25fac1a88..40eeba3663 100644 --- a/engines/scumm/scumm-md5.h +++ b/engines/scumm/scumm-md5.h @@ -1,5 +1,5 @@ /* - This file was generated by the md5table tool on Sun Jun 27 05:23:26 2010 + This file was generated by the md5table tool on Mon Oct 18 00:42:16 2010 DO NOT EDIT MANUALLY! */ @@ -117,7 +117,7 @@ static const MD5Table md5table[] = { { "2723fea3dae0cb47768c424b145ae0e7", "tentacle", "Floppy", "Floppy", 7932, Common::EN_ANY, Common::kPlatformPC }, { "27b2ef1653089fe5b897d9cc89ce784f", "balloon", "HE 80", "", -1, Common::RU_RUS, Common::kPlatformWindows }, { "27b3a4224ad63d5b04627595c1c1a025", "zak", "V2", "V2", -1, Common::IT_ITA, Common::kPlatformAmiga }, - { "28d24a33448fab6795850bc9f159a4a2", "atlantis", "", "Demo", 11170, Common::JA_JPN, Common::kPlatformFMTowns }, + { "28d24a33448fab6795850bc9f159a4a2", "atlantis", "FM-TOWNS", "Demo", 11170, Common::JA_JPN, Common::kPlatformFMTowns }, { "28ef68ee3ed76d7e2ee8ee13c15fbd5b", "loom", "EGA", "EGA", 5748, Common::EN_ANY, Common::kPlatformPC }, { "28f07458f1b6c24e118a1ea056827701", "lost", "HE 99", "", -1, Common::NL_NLD, Common::kPlatformUnknown }, { "2a208ffbcd0e83e86f4356e6f64aa6e1", "loom", "EGA", "EGA", -1, Common::ES_ESP, Common::kPlatformPC }, @@ -132,7 +132,7 @@ static const MD5Table md5table[] = { { "2d4acbdcfd8e374c9da8c2e7303a5cd0", "BluesBirthday", "", "Demo", -1, Common::EN_ANY, Common::kPlatformUnknown }, { "2d624d1b214f7faf0094daea65c6d1a6", "maniac", "Apple II", "", -1, Common::EN_ANY, Common::kPlatformApple2GS }, { "2d9d46f23cb07bbc90b8ad464d3e4ff8", "atlantis", "", "CD", -1, Common::EN_ANY, Common::kPlatformMacintosh }, - { "2e85f7aa054930c692a5b1bed1dfc295", "football2002", "", "Demo", -1, Common::EN_ANY, Common::kPlatformUnknown }, + { "2e85f7aa054930c692a5b1bed1dfc295", "football2002", "", "Patched", -1, Common::EN_ANY, Common::kPlatformUnknown }, { "2e8a1f76ea33bc5e04347646feee173d", "pajama3", "", "", -1, Common::DE_DEU, Common::kPlatformUnknown }, { "2fe369ad70f52a8cf7ad6077ee64f81a", "loom", "EGA", "EGA", -1, Common::DE_DEU, Common::kPlatformAmiga }, { "305d3dd57c96c65b017bc70c8c7cfb5e", "monkey", "CD", "CD", 8955, Common::DE_DEU, Common::kPlatformPC }, @@ -183,7 +183,7 @@ static const MD5Table md5table[] = { { "4167a92a1d46baa4f4127d918d561f88", "tentacle", "", "CD", 7932, Common::EN_ANY, Common::kPlatformUnknown }, { "41958e24d03181ff9a381a66d048a581", "ft", "", "", -1, Common::PT_BRA, Common::kPlatformUnknown }, { "425205754fa749f4f0b0dd9d09fa45fd", "football", "", "Demo", -1, Common::EN_ANY, Common::kPlatformUnknown }, - { "430bc518017b6fac046f58bab6baad5d", "monkey2", "", "", -1, Common::JA_JPN, Common::kPlatformFMTowns }, + { "430bc518017b6fac046f58bab6baad5d", "monkey2", "FM-TOWNS", "", -1, Common::JA_JPN, Common::kPlatformFMTowns }, { "439a7f4adf510489981ac52308e7d7a2", "maniac", "C64", "", -1, Common::DE_DEU, Common::kPlatformC64 }, { "45082a5c9f42ba14dacfe1fdeeba819d", "freddicove", "HE 100", "Demo", 18422, Common::EN_ANY, Common::kPlatformUnknown }, { "45152f7cf2ba8f43cf8a8ea2e740ae09", "monkey", "VGA", "VGA", 8357, Common::ES_ESP, Common::kPlatformPC }, @@ -206,7 +206,7 @@ static const MD5Table md5table[] = { { "4c4820518e16e1a0e3616a3b021a04f3", "catalog", "HE CUP", "Preview", 10927456, Common::DE_DEU, Common::kPlatformUnknown }, { "4cb9c3618f71668f8e4346c8f323fa82", "monkey2", "", "", 10700, Common::EN_ANY, Common::kPlatformMacintosh }, { "4ce2d5b355964bbcb5e5ce73236ef868", "freddicove", "HE 100", "", -1, Common::RU_RUS, Common::kPlatformWindows }, - { "4d34042713958b971cb139fba4658586", "atlantis", "", "", -1, Common::JA_JPN, Common::kPlatformFMTowns }, + { "4d34042713958b971cb139fba4658586", "atlantis", "FM-TOWNS", "", -1, Common::JA_JPN, Common::kPlatformFMTowns }, { "4dbff3787aedcd96b0b325f2d92d7ad9", "maze", "HE 100", "Updated", -1, Common::EN_USA, Common::kPlatformUnknown }, { "4dc780f1bc587a193ce8a97652791438", "loom", "EGA", "EGA", -1, Common::EN_ANY, Common::kPlatformAmiga }, { "4e5867848ee61bc30d157e2c94eee9b4", "PuttTime", "HE 90", "Demo", 18394, Common::EN_USA, Common::kPlatformUnknown }, @@ -501,7 +501,7 @@ static const MD5Table md5table[] = { { "c6907d44f1166941d982864cd42cdc89", "pajama2", "HE 99", "", -1, Common::DE_DEU, Common::kPlatformUnknown }, { "c782fbbe74a987c3df8ac73cd3e289ed", "freddi", "HE 73", "", -1, Common::SE_SWE, Common::kPlatformMacintosh }, { "c7890e038806df2bb5c0c8c6f1986ea2", "monkey", "VGA", "VGA", -1, Common::EN_ANY, Common::kPlatformPC }, - { "c7be10f775404fd9785a8b92a06d240c", "atlantis", "", "", 12030, Common::EN_ANY, Common::kPlatformFMTowns }, + { "c7be10f775404fd9785a8b92a06d240c", "atlantis", "FM-TOWNS", "", 12030, Common::EN_ANY, Common::kPlatformFMTowns }, { "c7c492a107ec520d7a7943037d0ca54a", "freddi", "HE 71", "Demo", -1, Common::NL_NLD, Common::kPlatformWindows }, { "c83079157ec765a28de445aec9768d60", "tentacle", "", "Demo", 7477, Common::EN_ANY, Common::kPlatformUnknown }, { "c8575e0b973ff1723aba6cd92c642db2", "puttrace", "HE 99", "Demo", -1, Common::FR_FRA, Common::kPlatformWindows }, @@ -552,7 +552,7 @@ static const MD5Table md5table[] = { { "d8323015ecb8b10bf53474f6e6b0ae33", "dig", "", "", 16304, Common::UNK_LANG, Common::kPlatformUnknown }, { "d917f311a448e3cc7239c31bddb00dd2", "samnmax", "", "CD", 9080, Common::EN_ANY, Common::kPlatformUnknown }, { "d9d0dd93d16ab4dec55cabc2b86bbd17", "samnmax", "", "Demo", 6478, Common::EN_ANY, Common::kPlatformPC }, - { "da09e666fc8f5b78d7b0ac65d1a3b56e", "monkey2", "", "", 11135, Common::EN_ANY, Common::kPlatformFMTowns }, + { "da09e666fc8f5b78d7b0ac65d1a3b56e", "monkey2", "FM-TOWNS", "", 11135, Common::EN_ANY, Common::kPlatformFMTowns }, { "da6269b18fcb08189c0aa9c95533cce2", "monkey", "CD", "CD", 8955, Common::IT_ITA, Common::kPlatformPC }, { "da669b20271b85182e9c17a2a37ea02e", "monkey2", "", "", -1, Common::DE_DEU, Common::kPlatformAmiga }, { "db21a6e338fe3b70c2723b6530865bf2", "PuttTime", "HE 85", "", -1, Common::FR_FRA, Common::kPlatformUnknown }, @@ -609,7 +609,7 @@ static const MD5Table md5table[] = { { "f237bf8a5ef9af78b2a6a4f3901da341", "pajama", "", "Demo", 18354, Common::EN_ANY, Common::kPlatformUnknown }, { "f27b1ba0eadaf2a6617b2b58192d1dbf", "samnmax", "Floppy", "Floppy", -1, Common::DE_DEU, Common::kPlatformPC }, { "f3d55aea441e260e9e9c7d2a187097e0", "puttzoo", "", "Demo", 14337, Common::EN_ANY, Common::kPlatformWindows }, - { "f40a7f495f59188ca57a9d1d50301bb6", "puttputt", "Demo", "Demo", -1, Common::EN_ANY, Common::kPlatformMacintosh }, + { "f40a7f495f59188ca57a9d1d50301bb6", "puttputt", "HE 60", "Demo", -1, Common::EN_ANY, Common::kPlatformMacintosh }, { "f5228b0cc1c19e6ea8268ba2eeb61f60", "freddi", "HE 73", "Demo", -1, Common::FR_FRA, Common::kPlatformWindows }, { "f73883f13b5a302749a5bad31d909780", "tentacle", "", "CD", -1, Common::DE_DEU, Common::kPlatformMacintosh }, { "f7711f9264d4d43c2a1518ec7c10a607", "pajama3", "", "", 79382, Common::EN_USA, Common::kPlatformUnknown }, diff --git a/engines/scumm/scumm.cpp b/engines/scumm/scumm.cpp index 75c507565c..42be33fff3 100644 --- a/engines/scumm/scumm.cpp +++ b/engines/scumm/scumm.cpp @@ -29,6 +29,7 @@ #include "common/events.h" #include "common/EventRecorder.h" #include "common/system.h" +#include "common/translation.h" #include "engines/util.h" @@ -60,6 +61,7 @@ #include "scumm/player_pce.h" #include "scumm/player_v1.h" #include "scumm/player_v2.h" +#include "scumm/player_v2cms.h" #include "scumm/player_v2a.h" #include "scumm/player_v3a.h" #include "scumm/player_v4a.h" @@ -135,7 +137,8 @@ ScummEngine::ScummEngine(OSystem *syst, const DetectorResult &dr) uint tmpVal; tmpStr[0] = dr.md5[2*i]; tmpStr[1] = dr.md5[2*i+1]; - sscanf(tmpStr, "%x", &tmpVal); + int res = sscanf(tmpStr, "%x", &tmpVal); + assert(res == 1); _gameMD5[i] = (byte)tmpVal; } @@ -144,6 +147,7 @@ ScummEngine::ScummEngine(OSystem *syst, const DetectorResult &dr) // Init all vars _v0ObjectIndex = false; _v0ObjectInInventory = false; + _v0ObjectFlag = 0; _imuse = NULL; _imuseDigital = NULL; _musicEngine = NULL; @@ -208,7 +212,6 @@ ScummEngine::ScummEngine(OSystem *syst, const DetectorResult &dr) _saveLoadSlot = 0; _lastSaveTime = 0; _saveTemporaryState = false; - memset(_saveLoadFileName, 0, sizeof(_saveLoadFileName)); memset(_saveLoadName, 0, sizeof(_saveLoadName)); memset(_localScriptOffsets, 0, sizeof(_localScriptOffsets)); _scriptPointer = NULL; @@ -257,7 +260,7 @@ ScummEngine::ScummEngine(OSystem *syst, const DetectorResult &dr) _switchRoomEffect2 = 0; _switchRoomEffect = 0; - _bytesPerPixel = 1; + _bytesPerPixelOutput = _bytesPerPixel = 1; _doEffect = false; _snapScroll = false; _currentLights = 0; @@ -277,6 +280,11 @@ ScummEngine::ScummEngine(OSystem *syst, const DetectorResult &dr) _hePalettes = NULL; _hePaletteSlot = 0; _16BitPalette = NULL; +#ifndef DISABLE_TOWNS_DUAL_LAYER_MODE + _townsScreen = 0; +#endif + _cjkFont = 0; + _cjkChar = 0; _shadowPalette = NULL; _shadowPaletteSize = 0; memset(_currentPalette, 0, sizeof(_currentPalette)); @@ -317,6 +325,15 @@ ScummEngine::ScummEngine(OSystem *syst, const DetectorResult &dr) _skipDrawObject = 0; +#ifndef DISABLE_TOWNS_DUAL_LAYER_MODE + _townsPaletteFlags = 0; + _townsClearLayerFlag = 1; + _townsActiveLayerFlags = 3; + memset(&_curStringRect, -1, sizeof(Common::Rect)); + memset(&_cyclRects, 0, 16 * sizeof(Common::Rect)); + _numCyclRects = 0; +#endif + // // Init all VARS to 0xFF // @@ -532,16 +549,21 @@ ScummEngine::ScummEngine(OSystem *syst, const DetectorResult &dr) _screenHeight = 200; } - _bytesPerPixel = (_game.features & GF_16BIT_COLOR) ? 2 : 1; + _bytesPerPixelOutput = _bytesPerPixel = (_game.features & GF_16BIT_COLOR) ? 2 : 1; + +#ifdef USE_RGB_COLOR +#ifndef DISABLE_TOWNS_DUAL_LAYER_MODE + if (_game.platform == Common::kPlatformFMTowns) + _bytesPerPixelOutput = 2; +#endif +#endif // Allocate gfx compositing buffer (not needed for V7/V8 games). if (_game.version < 7) - _compositeBuf = (byte *)malloc(_screenWidth * _screenHeight * _bytesPerPixel); + _compositeBuf = (byte *)malloc(_screenWidth * _screenHeight * _bytesPerPixelOutput); else _compositeBuf = 0; - _fmtownsBuf = 0; - _herculesBuf = 0; if (_renderMode == Common::kRenderHercA || _renderMode == Common::kRenderHercG) { _herculesBuf = (byte *)malloc(Common::kHercW * Common::kHercH); @@ -607,7 +629,13 @@ ScummEngine::~ScummEngine() { free(_compositeBuf); free(_herculesBuf); - free(_fmtownsBuf); + + free(_16BitPalette); + +#ifndef DISABLE_TOWNS_DUAL_LAYER_MODE + delete _townsScreen; +#endif + delete _cjkFont; delete _debugger; @@ -697,6 +725,10 @@ ScummEngine_v0::ScummEngine_v0(OSystem *syst, const DetectorResult &dr) _activeObject2Inv = false; _activeObjectObtained = false; _activeObject2Obtained = false; + + VAR_ACTIVE_ACTOR = 0xFF; + VAR_IS_SOUND_RUNNING = 0xFF; + VAR_ACTIVE_VERB = 0xFF; } ScummEngine_v6::ScummEngine_v6(OSystem *syst, const DetectorResult &dr) @@ -1111,16 +1143,29 @@ Common::Error ScummEngine::init() { screenWidth *= _textSurfaceMultiplier; screenHeight *= _textSurfaceMultiplier; } - if (_game.features & GF_16BIT_COLOR) { + if (_game.features & GF_16BIT_COLOR +#ifndef DISABLE_TOWNS_DUAL_LAYER_MODE + || _game.platform == Common::kPlatformFMTowns +#endif + ) { #ifdef USE_RGB_COLOR Graphics::PixelFormat format = Graphics::PixelFormat(2, 5, 5, 5, 0, 10, 5, 0, 0); initGraphics(screenWidth, screenHeight, screenWidth > 320, &format); if (format != _system->getScreenFormat()) return Common::kUnsupportedColorMode; #else - error("16bit color support is required for this game"); + if (_game.platform == Common::kPlatformFMTowns && _game.version == 3) { + warning("Starting game without the required 16bit color support.\nYou may experience color glitches"); + initGraphics(screenWidth, screenHeight, (screenWidth > 320)); + } else { + error("16bit color support is required for this game"); + } #endif } else { +#ifdef DISABLE_TOWNS_DUAL_LAYER_MODE + if (_game.platform == Common::kPlatformFMTowns && _game.version == 5) + error("This game requires dual graphics layer support which is disabled in this build"); +#endif initGraphics(screenWidth, screenHeight, (screenWidth > 320)); } } @@ -1236,13 +1281,8 @@ void ScummEngine::setupScumm() { _res->setHeapThreshold(400000, maxHeapThreshold); - if (_game.platform == Common::kPlatformFMTowns && _language == Common::JA_JPN) { - free(_fmtownsBuf); - _fmtownsBuf = (byte *)malloc(_screenWidth * _textSurfaceMultiplier * _screenHeight * _textSurfaceMultiplier); - } - free(_compositeBuf); - _compositeBuf = (byte *)malloc(_screenWidth * _textSurfaceMultiplier * _screenHeight * _textSurfaceMultiplier * _bytesPerPixel); + _compositeBuf = (byte *)malloc(_screenWidth * _textSurfaceMultiplier * _screenHeight * _textSurfaceMultiplier * _bytesPerPixelOutput); } #ifdef ENABLE_SCUMM_7_8 @@ -1320,6 +1360,24 @@ void ScummEngine::resetScumm() { debug(9, "resetScumm"); +#ifdef USE_RGB_COLOR + if (_game.features & GF_16BIT_COLOR +#ifndef DISABLE_TOWNS_DUAL_LAYER_MODE + || _game.platform == Common::kPlatformFMTowns +#endif + ) + _16BitPalette = (uint16 *)calloc(512, sizeof(uint16)); +#endif + +#ifndef DISABLE_TOWNS_DUAL_LAYER_MODE + if (_game.platform == Common::kPlatformFMTowns) { + delete _townsScreen; + _townsScreen = new TownsScreen(_system, _screenWidth * _textSurfaceMultiplier, _screenHeight * _textSurfaceMultiplier, _bytesPerPixelOutput); + _townsScreen->setupLayer(0, _screenWidth, _screenHeight, (_bytesPerPixelOutput == 2) ? 32767 : 256); + _townsScreen->setupLayer(1, _screenWidth * _textSurfaceMultiplier, _screenHeight * _textSurfaceMultiplier, 16, _textPalette); + } +#endif + if (_game.version == 0) { initScreens(8, 144); } else if ((_game.id == GID_MANIAC) && (_game.version <= 1) && !(_game.platform == Common::kPlatformNES)) { @@ -1512,8 +1570,6 @@ void ScummEngine_v3::resetScumm() { if (_game.id == GID_LOOM && _game.platform == Common::kPlatformPCEngine) { - _16BitPalette = (uint16 *)calloc(512, sizeof(uint16)); - // Load tile set and palette for the distaff byte *roomptr = getResourceAddress(rtRoom, 90); assert(roomptr); @@ -1591,6 +1647,10 @@ void ScummEngine_v90he::resetScumm() { _logicHE = new LogicHEsoccer(this); break; + case GID_BASEBALL2001: + _logicHE = new LogicHEbaseball2001(this); + break; + case GID_BASKETBALL: _logicHE = new LogicHEbasketball(this); break; @@ -1760,9 +1820,9 @@ void ScummEngine::setupMusic(int midi) { } else if (_game.platform == Common::kPlatform3DO && _game.heversion <= 62) { // 3DO versions use digital music and sound samples. } else if (_game.platform == Common::kPlatformFMTowns && (_game.version == 3 || _game.id == GID_MONKEY)) { - _musicEngine = _townsPlayer = new Player_Towns(this, _mixer); + _musicEngine = _townsPlayer = new Player_Towns_v1(this, _mixer); if (!_townsPlayer->init()) - error("Failed to initialize FM-Towns audio driver."); + error("Failed to initialize FM-Towns audio driver"); } else if (_game.version >= 3 && _game.heversion <= 62) { MidiDriver *nativeMidiDriver = 0; MidiDriver *adlibMidiDriver = 0; @@ -1777,7 +1837,16 @@ void ScummEngine::setupMusic(int midi) { adlibMidiDriver->property(MidiDriver::PROP_OLD_ADLIB, (_game.features & GF_SMALL_HEADER) ? 1 : 0); } - _musicEngine = _imuse = IMuse::create(_system, nativeMidiDriver, adlibMidiDriver); + _imuse = IMuse::create(_system, nativeMidiDriver, adlibMidiDriver); + + if (_game.platform == Common::kPlatformFMTowns) { + _musicEngine = _townsPlayer = new Player_Towns_v2(this, _imuse, _mixer, true); + if (!_townsPlayer->init()) + error("Failed to initialize FM-Towns audio driver"); + } else { + _musicEngine = _imuse; + } + if (_imuse) { _imuse->addSysexHandler (/*IMUSE_SYSEX_ID*/ 0x7D, @@ -1786,17 +1855,17 @@ void ScummEngine::setupMusic(int midi) { if (ConfMan.hasKey("tempo")) _imuse->property(IMuse::PROP_TEMPO_BASE, ConfMan.getInt("tempo")); // YM2162 driver can't handle midi->getPercussionChannel(), NULL shouldn't init MT-32/GM/GS - if ((midi != MDT_TOWNS) && (midi != MDT_NONE)) { + if (/*(midi != MDT_TOWNS) && (*/midi != MDT_NONE/*)*/) { _imuse->property(IMuse::PROP_NATIVE_MT32, _native_mt32); if (MidiDriver::getMusicType(dev) != MT_MT32) // MT-32 Emulation shouldn't be GM/GS initialized _imuse->property(IMuse::PROP_GS, _enable_gs); } - if (_game.heversion >= 60 || midi == MDT_TOWNS) { + if (_game.heversion >= 60 /*|| midi == MDT_TOWNS*/) { _imuse->property(IMuse::PROP_LIMIT_PLAYERS, 1); _imuse->property(IMuse::PROP_RECYCLE_PLAYERS, 1); } - if (midi == MDT_TOWNS) - _imuse->property(IMuse::PROP_DIRECT_PASSTHROUGH, 1); + /*if (midi == MDT_TOWNS) + _imuse->property(IMuse::PROP_DIRECT_PASSTHROUGH, 1);*/ } } } @@ -1858,7 +1927,7 @@ int ScummEngine::getTalkSpeed() { #pragma mark - Common::Error ScummEngine::go() { - _engineStartTime = _system->getMillis() / 1000; + setTotalPlayTime(); // If requested, load a save game instead of running the boot script if (_saveLoadFlag != 2 || !loadState(_saveLoadSlot, _saveTemporaryState)) { @@ -1923,6 +1992,12 @@ void ScummEngine::waitForTimer(int msec_delay) { while (!shouldQuit()) { _sound->updateCD(); // Loop CD Audio if needed parseEvents(); + +#ifndef DISABLE_TOWNS_DUAL_LAYER_MODE + if (_townsScreen) + _townsScreen->update(); +#endif + _system->updateScreen(); if (_system->getMillis() >= start_time + msec_delay) break; @@ -1930,6 +2005,12 @@ void ScummEngine::waitForTimer(int msec_delay) { } } +void ScummEngine_v0::scummLoop(int delta) { + VAR(VAR_IS_SOUND_RUNNING) = (_sound->_lastSound && _sound->isSoundRunning(_sound->_lastSound) != 0); + + ScummEngine::scummLoop(delta); +} + void ScummEngine::scummLoop(int delta) { if (_game.version >= 3) { VAR(VAR_TMR_1) += delta; @@ -2044,6 +2125,10 @@ load_game: goto load_game; } +#ifndef DISABLE_TOWNS_DUAL_LAYER_MODE + towns_processPalCycleField(); +#endif + if (_currentRoom == 0) { if (_game.version > 3) CHARSET_1(); @@ -2113,6 +2198,7 @@ void ScummEngine::scummLoop_updateScummVars() { VAR(VAR_CAMERA_POS_X) = camera._cur.x; VAR(VAR_CAMERA_POS_Y) = camera._cur.y; } else if (_game.platform == Common::kPlatformNES) { +#if 0 // WORKAROUND: // Since there are 2 2-stripes wide borders in MM NES screen, // we have to compensate for it here. This fixes paning effects. @@ -2122,6 +2208,7 @@ void ScummEngine::scummLoop_updateScummVars() { if (VAR(VAR_CAMERA_POS_X) < (camera._cur.x >> V12_X_SHIFT) + 2) VAR(VAR_CAMERA_POS_X) = (camera._cur.x >> V12_X_SHIFT) + 2; else +#endif VAR(VAR_CAMERA_POS_X) = (camera._cur.x >> V12_X_SHIFT); } else if (_game.version <= 2) { VAR(VAR_CAMERA_POS_X) = camera._cur.x >> V12_X_SHIFT; @@ -2167,14 +2254,14 @@ void ScummEngine::scummLoop_handleSaveLoad() { if (_saveLoadFlag == 1) { success = saveState(_saveLoadSlot, _saveTemporaryState); if (!success) - errMsg = "Failed to save game state to file:\n\n%s"; + errMsg = _("Failed to save game state to file:\n\n%s"); if (success && _saveTemporaryState && VAR_GAME_LOADED != 0xFF && _game.version <= 7) VAR(VAR_GAME_LOADED) = 201; } else { success = loadState(_saveLoadSlot, _saveTemporaryState); if (!success) - errMsg = "Failed to load game state from file:\n\n%s"; + errMsg = _("Failed to load game state from file:\n\n%s"); if (success && _saveTemporaryState && VAR_GAME_LOADED != 0xFF) VAR(VAR_GAME_LOADED) = (_game.version == 8) ? 1 : 203; @@ -2186,7 +2273,7 @@ void ScummEngine::scummLoop_handleSaveLoad() { } else if (_saveLoadFlag == 1 && _saveLoadSlot != 0 && !_saveTemporaryState) { // Display "Save successful" message, except for auto saves char buf[256]; - snprintf(buf, sizeof(buf), "Successfully saved game state in file:\n\n%s", filename.c_str()); + snprintf(buf, sizeof(buf), _("Successfully saved game state in file:\n\n%s"), filename.c_str()); GUI::TimedMessageDialog dialog(buf, 1500); runDialog(dialog); @@ -2418,10 +2505,6 @@ void ScummEngine::startManiac() { void ScummEngine::pauseEngineIntern(bool pause) { if (pause) { - // Record start of the pause, so that we can later - // adjust _engineStartTime accordingly. - _pauseStartTime = _system->getMillis(); - // Pause sound & video _oldSoundsPaused = _sound->_soundsPaused; _sound->pauseSounds(true); @@ -2429,14 +2512,16 @@ void ScummEngine::pauseEngineIntern(bool pause) { } else { // Update the screen to make it less likely that the player will see a // brief cursor palette glitch when the GUI is disabled. + +#ifndef DISABLE_TOWNS_DUAL_LAYER_MODE + if (_townsScreen) + _townsScreen->update(); +#endif + _system->updateScreen(); // Resume sound & video _sound->pauseSounds(_oldSoundsPaused); - - // Adjust engine start time - _engineStartTime += (_system->getMillis() - _pauseStartTime) / 1000; - _pauseStartTime = 0; } } diff --git a/engines/scumm/scumm.h b/engines/scumm/scumm.h index 1357bad8cf..0a513b6068 100644 --- a/engines/scumm/scumm.h +++ b/engines/scumm/scumm.h @@ -36,6 +36,7 @@ #include "common/rect.h" #include "common/str.h" #include "graphics/surface.h" +#include "graphics/sjis.h" #include "scumm/gfx.h" #include "scumm/detection.h" @@ -43,6 +44,17 @@ #include "sound/mididrv.h" +#ifdef __DS__ +/* This disables the dual layer mode which is used in FM-Towns versions + * of SCUMM games and which emulates the behaviour of the original code. + * The only purpose is code size reduction for certain backends. + * SCUMM 3 (FM-Towns) games will run in normal (DOS VGA) mode, which should + * work just fine in most situations. Some glitches might occur. SCUMM 5 games + * will not work without dual layer (and 16 bit color) support. + */ +#define DISABLE_TOWNS_DUAL_LAYER_MODE +#endif + namespace GUI { class Dialog; } @@ -55,10 +67,14 @@ namespace Common { /** * This is the namespace of the SCUMM engine. * - * Status of this engine: ??? + * Status of this engine: + * Complete support for all SCUMM based LucasArts adventures. + * Complete support for many Humongous Entertainment games, + * but for some of the newer ones, this is still work in progress. * - * Supported games: - * - ??? + * Games using this engine: + * - Classic 2D LucasArts adventures + * - numerous Humongous Entertainment games */ namespace Scumm { @@ -237,6 +253,7 @@ enum ScummGameId { GID_FUNSHOP, // Used for all three funshops GID_FOOTBALL, GID_SOCCER, + GID_BASEBALL2001, GID_BASKETBALL, GID_MOONBASE, GID_HECUP // CUP demos @@ -576,6 +593,7 @@ protected: bool _v0ObjectIndex; // V0 Use object index, instead of object number bool _v0ObjectInInventory; // V0 Use object number from inventory + byte _v0ObjectFlag; /* Global resource tables */ int _numVariables, _numBitVariables, _numLocalObjects; @@ -643,7 +661,7 @@ protected: byte _saveLoadFlag, _saveLoadSlot; uint32 _lastSaveTime; bool _saveTemporaryState; - char _saveLoadFileName[32]; + Common::String _saveLoadFileName; char _saveLoadName[32]; bool saveState(Common::OutSaveFile *out, bool writeHeader = true); @@ -682,9 +700,6 @@ protected: void saveInfos(Common::WriteStream* file); static bool loadInfos(Common::SeekableReadStream *file, InfoStuff *stuff); - int32 _engineStartTime; - int32 _pauseStartTime; - protected: /* Script VM - should be in Script class */ uint32 _localScriptOffsets[1024]; @@ -737,9 +752,10 @@ protected: void stopObjectScript(int script); void getScriptBaseAddress(); - void getScriptEntryPoint(); + void resetScriptPointer(); int getVerbEntrypoint(int obj, int entry); + void refreshScriptPointer(); byte fetchScriptByte(); virtual uint fetchScriptWord(); virtual int fetchScriptWordSigned(); @@ -759,7 +775,7 @@ protected: void endOverride(); void copyScriptString(byte *dst); - int resStrLen(const byte *src) const; + int resStrLen(const byte *src); void doSentence(int c, int b, int a); /* Should be in Resource class */ @@ -972,6 +988,7 @@ public: Common::RenderMode _renderMode; uint8 _bytesPerPixel; + uint8 _bytesPerPixelOutput; protected: ColorCycle _colorCycle[16]; // Palette cycles @@ -1044,6 +1061,7 @@ protected: void setRoomPalette(int pal, int room); void setPCEPaletteFromPtr(const byte *ptr); virtual void setPaletteFromPtr(const byte *ptr, int numcolor = -1); + virtual void setPalColor(int index, int r, int g, int b); void setDirtyColors(int min, int max); const byte *findPalInPals(const byte *pal, int index); @@ -1077,7 +1095,7 @@ protected: // Screen rendering byte *_compositeBuf; byte *_herculesBuf; - byte *_fmtownsBuf; + virtual void drawDirtyScreenParts(); void updateDirtyScreen(VirtScreenNumber slot); void drawStripToScreen(VirtScreen *vs, int x, int w, int t, int b); @@ -1221,7 +1239,7 @@ protected: void restoreCharsetBg(); void clearCharsetMask(); void clearTextSurface(); - + virtual void initCharset(int charset); virtual void printString(int m, const byte *msg); @@ -1397,6 +1415,40 @@ public: // Exists both in V7 and in V72HE: byte VAR_NUM_GLOBAL_OBJS; + + // FM-Towns specific +#ifndef DISABLE_TOWNS_DUAL_LAYER_MODE +public: + bool towns_isRectInStringBox(int x1, int y1, int x2, int y2); + byte _townsPaletteFlags; + byte _townsCharsetColorMap[16]; + Graphics::FontSJIS *_cjkFont; + uint16 _cjkChar; + +protected: + void towns_drawStripToScreen(VirtScreen *vs, int dstX, int dstY, int srcX, int srcY, int w, int h); +#ifdef USE_RGB_COLOR + void towns_setPaletteFromPtr(const byte *ptr, int numcolor = -1); + void towns_setTextPaletteFromPtr(const byte *ptr); +#endif + void towns_setupPalCycleField(int x1, int y1, int x2, int y2); + void towns_processPalCycleField(); + void towns_resetPalCycleFields(); + void towns_restoreCharsetBg(); + + Common::Rect _cyclRects[16]; + int _numCyclRects; + + Common::Rect _curStringRect; + + byte _townsOverrideShadowColor; + byte _textPalette[48]; + byte _townsClearLayerFlag; + byte _townsActiveLayerFlags; + static const uint8 _townsLayer2Mask[]; + + TownsScreen *_townsScreen; +#endif // DISABLE_TOWNS_DUAL_LAYER_MODE }; } // End of namespace Scumm diff --git a/engines/scumm/scumm_v0.h b/engines/scumm/scumm_v0.h index 5ef416f650..7b913f7750 100644 --- a/engines/scumm/scumm_v0.h +++ b/engines/scumm/scumm_v0.h @@ -62,6 +62,7 @@ protected: virtual void setupScummVars(); virtual void resetScummVars(); + virtual void scummLoop(int delta); virtual void decodeParseString(); virtual void processInput(); @@ -136,6 +137,10 @@ protected: void o_endCutscene(); void o_beginOverride(); void o_setOwnerOf(); + + byte VAR_ACTIVE_ACTOR; + byte VAR_IS_SOUND_RUNNING; + byte VAR_ACTIVE_VERB; }; diff --git a/engines/scumm/scumm_v2.h b/engines/scumm/scumm_v2.h index be623924f1..0c54308181 100644 --- a/engines/scumm/scumm_v2.h +++ b/engines/scumm/scumm_v2.h @@ -103,8 +103,6 @@ protected: virtual void setBuiltinCursor(int index); - virtual void runObject(int obj, int entry); - /* Version 2 script opcodes */ void o2_actorFromPos(); void o2_actorOps(); diff --git a/engines/scumm/smush/codec47.cpp b/engines/scumm/smush/codec47.cpp index 62bc0bb098..333fdabccf 100644 --- a/engines/scumm/smush/codec47.cpp +++ b/engines/scumm/smush/codec47.cpp @@ -301,9 +301,11 @@ void Codec47Decoder::makeTables47(int width) { int32 a, c, d; int16 tmp; - for (int l = 0; l < 512; l += 2) { + for (int l = 0; l < ARRAYSIZE(codec47_table); l += 2) { _table[l / 2] = (int16)(codec47_table[l + 1] * width + codec47_table[l]); } + // Note: _table[255] is never inited; but since only the first 0xF8 + // entries of it are used anyway, this doesn't matter. a = 0; c = 0; diff --git a/engines/scumm/sound.cpp b/engines/scumm/sound.cpp index 708faa5687..8fc6de8af9 100644 --- a/engines/scumm/sound.cpp +++ b/engines/scumm/sound.cpp @@ -78,6 +78,7 @@ Sound::Sound(ScummEngine *parent, Audio::Mixer *mixer) _curSoundPos(0), _currentCDSound(0), _currentMusic(0), + _lastSound(0), _soundsPaused(false), _sfxMode(0) { @@ -95,6 +96,7 @@ Sound::~Sound() { void Sound::addSoundToQueue(int sound, int heOffset, int heChannel, int heFlags) { if (_vm->VAR_LAST_SOUND != 0xFF) _vm->VAR(_vm->VAR_LAST_SOUND) = sound; + _lastSound = sound; // HE music resources are in separate file if (sound <= _vm->_numSounds) @@ -243,7 +245,7 @@ void Sound::playSound(int soundID) { _mixer->playStream(Audio::Mixer::kSFXSoundType, NULL, stream, soundID); } // Support for sampled sound effects in Monkey Island 1 and 2 - else if (READ_BE_UINT32(ptr) == MKID_BE('SBL ')) { + else if (_vm->_game.platform != Common::kPlatformFMTowns && READ_BE_UINT32(ptr) == MKID_BE('SBL ')) { debugC(DEBUG_SOUND, "Using SBL sound effect"); // SBL resources essentially contain VOC sound data. @@ -312,7 +314,7 @@ void Sound::playSound(int soundID) { sound = (byte *)malloc(size); memcpy(sound, ptr + 6, size); stream = Audio::makeRawStream(sound, size, rate, Audio::FLAG_UNSIGNED); - _mixer->playStream(Audio::Mixer::kSFXSoundType, NULL, stream, soundID); + _mixer->playStream(Audio::Mixer::kSFXSoundType, NULL, stream, soundID); } else if (_vm->_game.platform != Common::kPlatformFMTowns && READ_BE_UINT32(ptr) == MKID_BE('SOUN')) { if (_vm->_game.version != 3) @@ -789,6 +791,7 @@ void Sound::stopAllSounds() { } // Clear the (secondary) sound queue + _lastSound = 0; _soundQue2Pos = 0; memset(_soundQue2, 0, sizeof(_soundQue2)); diff --git a/engines/scumm/sound.h b/engines/scumm/sound.h index 401b1638cc..4fe46f32f0 100644 --- a/engines/scumm/sound.h +++ b/engines/scumm/sound.h @@ -91,6 +91,7 @@ public: bool _soundsPaused; byte _sfxMode; + uint _lastSound; public: Sound(ScummEngine *parent, Audio::Mixer *mixer); diff --git a/engines/scumm/string.cpp b/engines/scumm/string.cpp index 30281cb565..4b09547c8c 100644 --- a/engines/scumm/string.cpp +++ b/engines/scumm/string.cpp @@ -508,6 +508,11 @@ void ScummEngine::CHARSET_1() { if (_game.version >= 5) memcpy(_charsetColorMap, _charsetData[_charset->getCurID()], 4); +#ifndef DISABLE_TOWNS_DUAL_LAYER_MODE + if (_keepText && _game.platform == Common::kPlatformFMTowns) + memcpy(&_charset->_str, &_curStringRect, sizeof(Common::Rect)); +#endif + if (_talkDelay) return; @@ -539,7 +544,12 @@ void ScummEngine::CHARSET_1() { _nextTop = _string[0].ypos + _screenTop; #endif } else { - restoreCharsetBg(); +#ifndef DISABLE_TOWNS_DUAL_LAYER_MODE + if (_game.platform == Common::kPlatformFMTowns) + towns_restoreCharsetBg(); + else +#endif + restoreCharsetBg(); } } @@ -591,7 +601,12 @@ void ScummEngine::CHARSET_1() { } else if (!(_game.platform == Common::kPlatformFMTowns) && _string[0].height) { _nextTop += _string[0].height; } else { + bool useCJK = _useCJKMode; + // SCUMM5 FM-Towns doesn't use the height of the ROM font here. + if (_game.platform == Common::kPlatformFMTowns && _game.version == 5) + _useCJKMode = false; _nextTop += _charset->getFontHeight(); + _useCJKMode = useCJK; } if (_game.version > 3) { // FIXME: is this really needed? @@ -624,9 +639,7 @@ void ScummEngine::CHARSET_1() { #endif } else { if (c & 0x80 && _useCJKMode) { - if (_language == Common::JA_JPN && !checkSJISCode(c)) { - c = 0x20; //not in S-JIS - } else { + if (checkSJISCode(c)) { byte *buffer = _charsetBuffer + _charsetBufPos; c += *buffer++ * 256; //LE _charsetBufPos = buffer - _charsetBuffer; @@ -660,6 +673,11 @@ void ScummEngine::CHARSET_1() { } } +#ifndef DISABLE_TOWNS_DUAL_LAYER_MODE + if (_game.platform == Common::kPlatformFMTowns && (c == 0 || c == 2 || c == 3)) + memcpy(&_curStringRect, &_charset->_str, sizeof(Common::Rect)); +#endif + #ifdef ENABLE_SCUMM_7_8 if (_game.version >= 7 && subtitleLine != subtitleBuffer) { ((ScummEngine_v7 *)this)->addSubtitleToQueue(subtitleBuffer, subtitlePos, _charsetColor, _charset->getCurID()); @@ -978,11 +996,8 @@ void ScummEngine::drawString(int a, const byte *msg) { } } if (c & 0x80 && _useCJKMode) { - if (_language == Common::JA_JPN && !checkSJISCode(c)) { - c = 0x20; //not in S-JIS - } else { + if (checkSJISCode(c)) c += buf[i++] * 256; - } } _charset->printChar(c, true); _charset->_blitAlso = false; @@ -1006,6 +1021,7 @@ int ScummEngine::convertMessageToString(const byte *msg, byte *dst, int dstSize) uint num = 0; uint32 val; byte chr; + byte lastChr = 0; const byte *src; byte *end; byte transBuf[384]; @@ -1113,11 +1129,12 @@ int ScummEngine::convertMessageToString(const byte *msg, byte *dst, int dstSize) } else if (_game.id == GID_DIG && (chr == 1 || chr == 2 || chr == 3 || chr == 8)) { // Skip these characters } else { - if (!(chr == '@') || (_game.id == GID_CMI && _language == Common::ZH_TWN) || - (_game.id == GID_LOOM && _game.platform == Common::kPlatformPCEngine && _language == Common::JA_JPN)) - { + if ((chr != '@') || (_game.id == GID_CMI && _language == Common::ZH_TWN) || + (_game.id == GID_LOOM && _game.platform == Common::kPlatformPCEngine && _language == Common::JA_JPN) || + (_game.platform == Common::kPlatformFMTowns && _language == Common::JA_JPN && checkSJISCode(lastChr))) { *dst++ = chr; } + lastChr = chr; } // Check for a buffer overflow diff --git a/engines/scumm/vars.cpp b/engines/scumm/vars.cpp index d1d3ed63a4..5e6e96e413 100644 --- a/engines/scumm/vars.cpp +++ b/engines/scumm/vars.cpp @@ -116,10 +116,10 @@ void ScummEngine_v0::setupScummVars() { VAR_CAMERA_POS_X = 2; VAR_HAVE_MSG = 3; VAR_ROOM = 4; - //VAR_ACTIVE_ACTOR = 5; + VAR_ACTIVE_ACTOR = 5; VAR_OVERRIDE = 6; - //VAR_IS_SOUND_RUNNING = 8; - //VAR_ACTIVE_VERB = 9; + VAR_IS_SOUND_RUNNING = 8; + VAR_ACTIVE_VERB = 9; VAR_CHARCOUNT = 10; } diff --git a/engines/scumm/verbs.cpp b/engines/scumm/verbs.cpp index 5f8a6d9f52..c443f98bc6 100644 --- a/engines/scumm/verbs.cpp +++ b/engines/scumm/verbs.cpp @@ -167,11 +167,6 @@ void ScummEngine_v0::switchActor(int slot) { if (_currentMode == 0 || _currentMode == 1 || _currentMode == 2) return; - // verbs disabled for the current actor - ActorC64 *a = (ActorC64 *)derefActor(VAR(VAR_EGO), "switchActor"); - if (a->_miscflags & 0x40) - return; - VAR(VAR_EGO) = VAR(97 + slot); resetVerbs(); actorFollowCamera(VAR(VAR_EGO)); @@ -323,7 +318,7 @@ void ScummEngine_v2::checkV2MouseOver(Common::Point pos) { } } - if (new_box != _mouseOverBoxV2) { + if ((new_box != _mouseOverBoxV2) || (_game.version == 0)) { if (_mouseOverBoxV2 != -1) { rect = _mouseOverBoxesV2[_mouseOverBoxV2].rect; @@ -524,9 +519,8 @@ void ScummEngine_v2::handleMouseOver(bool updateInventory) { } void ScummEngine_v0::handleMouseOver(bool updateInventory) { - ScummEngine_v2::handleMouseOver(updateInventory); - drawSentence(); + ScummEngine_v2::handleMouseOver(updateInventory); } #ifdef ENABLE_HE @@ -727,7 +721,7 @@ void ScummEngine_v2::checkExecVerbs() { } void ScummEngine_v0::runObject(int obj, int entry) { - int prev = _v0ObjectInInventory; + bool prev = _v0ObjectInInventory; if (getVerbEntrypoint(obj, entry) == 0) { // If nothing was found, attempt to find the 'WHAT-IS' verb script @@ -745,30 +739,17 @@ void ScummEngine_v0::runObject(int obj, int entry) { runObjectScript(obj, entry, false, false, NULL); } else if (entry != 13 && entry != 15) { if (_activeVerb != 3) { - VAR(9) = entry; + VAR(VAR_ACTIVE_VERB) = entry; runScript(3, 0, 0, 0); // For some reasons, certain objects don't have a "give" script - } else if (VAR(5) > 0 && VAR(5) < 8) { + } else if (VAR(VAR_ACTIVE_ACTOR) > 0 && VAR(VAR_ACTIVE_ACTOR) < 8) { if (_activeInventory) - setOwnerOf(_activeInventory, VAR(5)); + setOwnerOf(_activeInventory, VAR(VAR_ACTIVE_ACTOR)); } } } -void ScummEngine_v2::runObject(int obj, int entry) { - if (getVerbEntrypoint(obj, entry) != 0) { - runObjectScript(obj, entry, false, false, NULL); - } else if (entry != 13 && entry != 15) { - VAR(9) = entry; - runScript(3, 0, 0, 0); - } - - _activeInventory = 0; - _activeObject = 0; - _activeVerb = 13; -} - bool ScummEngine_v0::verbMoveToActor(int actor) { Actor *a = derefActor(VAR(VAR_EGO), "verbMoveToActor"); Actor *a2 = derefActor(actor, "verbMoveToActor"); @@ -955,7 +936,7 @@ bool ScummEngine_v0::verbExec() { return true; } _v0ObjectInInventory = true; - VAR(5) = _activeActor; + VAR(VAR_ACTIVE_ACTOR) = _activeActor; runObject(_activeInventory , 3); _v0ObjectInInventory = false; @@ -991,6 +972,7 @@ bool ScummEngine_v0::verbExec() { // We acted on an inventory item if (_activeInventory && verbExecutes(_activeInventory, true) && _activeVerb != 3) { _v0ObjectInInventory = true; + _activeObject = _activeInventory; runObject(_activeInventory, _activeVerb); _verbExecuting = false; @@ -1049,7 +1031,7 @@ bool ScummEngine_v0::verbExec() { } void ScummEngine_v0::checkExecVerbs() { - Actor *a = derefActor(VAR(VAR_EGO), "checkExecVerbs"); + ActorC64 *a = (ActorC64 *)derefActor(VAR(VAR_EGO), "checkExecVerbs"); VirtScreen *zone = findVirtScreen(_mouse.y); // Is a verb currently executing @@ -1164,27 +1146,28 @@ void ScummEngine_v0::checkExecVerbs() { obj = 0; objIdx = 0; } + + if (a->_miscflags & 0x80) { + if (_activeVerb != 7 && over != 7) { + _activeVerb = 0; + over = 0; + } + } // Handle New Kid verb options if (_activeVerb == 7 || over == 7) { // Disable New-Kid (in the secret lab) if (_currentMode == 2 || _currentMode == 0) return; - - if (!(((ActorC64 *)a)->_miscflags & 0x80)) { - if (_activeVerb != 7) { - _activeVerb = over; - over = 0; - } - } - if (over) { + if (_activeVerb == 7 && over) { _activeVerb = 13; switchActor(_verbs[over].verbid - 1); return; } setNewKidVerbs(); + _activeVerb = 7; return; } @@ -1200,7 +1183,7 @@ void ScummEngine_v0::checkExecVerbs() { if (zone->number == kMainVirtScreen) { // Ignore verbs? - if (((ActorC64 *)a)->_miscflags & 0x40) { + if (a->_miscflags & 0x40) { resetSentence(false); return; } @@ -1464,9 +1447,14 @@ void ScummEngine::restoreVerbBG(int verb) { VerbSlot *vs; vs = &_verbs[verb]; + uint8 col = +#ifndef DISABLE_TOWNS_DUAL_LAYER_MODE + ((_game.platform == Common::kPlatformFMTowns) && (_game.id == GID_MONKEY2 || _game.id == GID_INDY4) && (vs->bkcolor == _townsOverrideShadowColor)) ? 0 : +#endif + vs->bkcolor; if (vs->oldRect.left != -1) { - restoreBackground(vs->oldRect, vs->bkcolor); + restoreBackground(vs->oldRect, col); vs->oldRect.left = -1; } } |