diff options
Diffstat (limited to 'engines/scumm')
-rw-r--r-- | engines/scumm/actor.cpp | 5 | ||||
-rw-r--r-- | engines/scumm/charset.cpp | 57 | ||||
-rw-r--r-- | engines/scumm/charset.h | 1 | ||||
-rw-r--r-- | engines/scumm/cursor.cpp | 23 | ||||
-rw-r--r-- | engines/scumm/detection_tables.h | 7 | ||||
-rw-r--r-- | engines/scumm/gfx.cpp | 233 | ||||
-rw-r--r-- | engines/scumm/gfx.h | 61 | ||||
-rw-r--r-- | engines/scumm/gfx_towns.cpp | 508 | ||||
-rw-r--r-- | engines/scumm/module.mk | 1 | ||||
-rw-r--r-- | engines/scumm/palette.cpp | 53 | ||||
-rw-r--r-- | engines/scumm/room.cpp | 3 | ||||
-rw-r--r-- | engines/scumm/saveload.cpp | 40 | ||||
-rw-r--r-- | engines/scumm/saveload.h | 2 | ||||
-rw-r--r-- | engines/scumm/script_v4.cpp | 57 | ||||
-rw-r--r-- | engines/scumm/script_v5.cpp | 56 | ||||
-rw-r--r-- | engines/scumm/scumm-md5.h | 10 | ||||
-rw-r--r-- | engines/scumm/scumm.cpp | 66 | ||||
-rw-r--r-- | engines/scumm/scumm.h | 36 | ||||
-rw-r--r-- | engines/scumm/string.cpp | 11 | ||||
-rw-r--r-- | engines/scumm/verbs.cpp | 3 |
20 files changed, 1005 insertions, 228 deletions
diff --git a/engines/scumm/actor.cpp b/engines/scumm/actor.cpp index a06939dc51..7c1f401e67 100644 --- a/engines/scumm/actor.cpp +++ b/engines/scumm/actor.cpp @@ -2159,7 +2159,10 @@ void ScummEngine::stopTalk() { ((ScummEngine_v7 *)this)->clearSubtitleQueue(); #endif } else { - restoreCharsetBg(); + if (_game.platform == Common::kPlatformFMTowns) + towns_restoreCharsetBg(); + else + restoreCharsetBg(); } } diff --git a/engines/scumm/charset.cpp b/engines/scumm/charset.cpp index fa4804ce7d..38b85f6fbd 100644 --- a/engines/scumm/charset.cpp +++ b/engines/scumm/charset.cpp @@ -655,6 +655,12 @@ void CharsetRendererV3::setColor(byte color) { } else useShadow = false; + if (_vm->_game.platform == Common::kPlatformFMTowns) { + _color = (_color & 0x0f) | ((_color & 0x0f) << 4); + if (_color == 0) + _color = 0x88; + } + enableShadow(useShadow); translateColor(); @@ -672,7 +678,7 @@ void CharsetRendererPCE::setColor(byte color) { void CharsetRendererCommon::enableShadow(bool enable) { if (enable) { if (_vm->_game.platform == Common::kPlatformFMTowns) { - _shadowColor = 8; + _shadowColor = _vm->_game.version == 5 ? _vm->_townsCharsetColorMap[0] : 0x88; _shadowMode = kFMTOWNSShadowMode; } else { _shadowColor = 0; @@ -683,7 +689,6 @@ void CharsetRendererCommon::enableShadow(bool enable) { } } - 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 @@ -724,8 +729,8 @@ void CharsetRendererV3::printChar(int chr, bool ignoreCharsetMask) { origHeight = height; if (_shadowMode != kNoShadowMode) { - width++; - height++; + width += _vm->_textSurfaceMultiplier; + height += _vm->_textSurfaceMultiplier; } if (_firstChar) { @@ -744,7 +749,8 @@ void CharsetRendererV3::printChar(int chr, bool ignoreCharsetMask) { _hasMask = true; _textScreenID = vs->number; } - if ((ignoreCharsetMask || !vs->hasTwoBuffers) && !(_vm->_useCJKMode && _vm->_textSurfaceMultiplier == 2)) { + + if ((_vm->_game.platform != Common::kPlatformFMTowns || (_vm->_game.id == GID_LOOM && !is2byte)) && (ignoreCharsetMask || !vs->hasTwoBuffers)) { dst = vs->getPixels(_left, drawTop); drawBits1(*vs, dst, charPtr, drawTop, origWidth, origHeight, vs->bytesPerPixel); } else { @@ -801,6 +807,27 @@ void CharsetRenderer::translateColor() { } } +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; + } + } +} + void CharsetRenderer::saveLoadWithSerializer(Serializer *ser) { static const SaveLoadEntry charsetRendererEntries[] = { MKLINE_OLD(CharsetRenderer, _curId, sleByte, VER(73), VER(73)), @@ -836,6 +863,8 @@ void CharsetRendererClassic::printChar(int chr, bool ignoreCharsetMask) { _vm->_charsetColorMap[1] = _color; + processTownsCharsetColors(_bytesPerPixel); + if (is2byte) { enableShadow(true); charPtr = _vm->get2byteCharPtr(chr); @@ -851,7 +880,7 @@ void CharsetRendererClassic::printChar(int chr, bool ignoreCharsetMask) { width = charPtr[0]; height = charPtr[1]; - + if (_disableOffsX) { offsX = 0; } else { @@ -866,8 +895,8 @@ void CharsetRendererClassic::printChar(int chr, bool ignoreCharsetMask) { origHeight = height; if (_shadowMode != kNoShadowMode) { - width++; - height++; + width += _vm->_textSurfaceMultiplier; + height += _vm->_textSurfaceMultiplier; } if (_firstChar) { _str.left = 0; @@ -905,7 +934,9 @@ 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 || (_vm->_game.platform == Common::kPlatformFMTowns && vs->number == kMainVirtScreen)) { _hasMask = true; _textScreenID = vs->number; } @@ -961,7 +992,7 @@ 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 (_vm->_game.platform != Common::kPlatformFMTowns && (ignoreCharsetMask || !vs->hasTwoBuffers) && !(_vm->_useCJKMode && _vm->_textSurfaceMultiplier == 2)) { dstSurface = *vs; dstPtr = vs->getPixels(_left, drawTop); } else { @@ -1064,13 +1095,14 @@ void CharsetRendererClassic::drawBitsN(const Graphics::Surface &s, byte *dst, co assert(bpp == 1 || bpp == 2 || bpp == 4 || bpp == 8); bits = *src++; numbits = 8; + byte *cmap = (_vm->_game.platform == Common::kPlatformFMTowns) ? _vm->_townsCharsetColorMap : _vm->_charsetColorMap; 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]; } dst++; bits <<= bpp; @@ -1087,6 +1119,7 @@ void CharsetRendererClassic::drawBitsN(const Graphics::Surface &s, byte *dst, co void CharsetRendererCommon::drawBits1(const Graphics::Surface &s, byte *dst, const byte *src, int drawTop, int width, int height, uint8 bitDepth) { int y, x; byte bits = 0; + uint8 col = (_vm->_game.platform == Common::kPlatformFMTowns && _vm->_game.version == 5) ? _vm->_townsCharsetColorMap[1] : _color; for (y = 0; y < height && y + drawTop < s.h; y++) { for (x = 0; x < width; x++) { @@ -1108,7 +1141,7 @@ void CharsetRendererCommon::drawBits1(const Graphics::Surface &s, byte *dst, con if (_shadowMode != kFMTOWNSShadowMode) *(dst + s.pitch + 1) = _shadowColor; } - *dst = _color; + *dst = col; } } dst += bitDepth; diff --git a/engines/scumm/charset.h b/engines/scumm/charset.h index dca254669b..e072f3ed5f 100644 --- a/engines/scumm/charset.h +++ b/engines/scumm/charset.h @@ -79,6 +79,7 @@ public: int getStringWidth(int a, const byte *str); void addLinebreaks(int a, byte *str, int pos, int maxwidth); void translateColor(); + void processTownsCharsetColors(uint8 bytesPerPixel); virtual void setCurID(int32 id) = 0; int getCurID() { return _curId; } diff --git a/engines/scumm/cursor.cpp b/engines/scumm/cursor.cpp index b1f8f2ae2b..63a7b1c40e 100644 --- a/engines/scumm/cursor.cpp +++ b/engines/scumm/cursor.cpp @@ -554,11 +554,14 @@ 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); + } else if (_game.platform == Common::kPlatformFMTowns) { + byte *palEntry = &_textPalette[default_cursor_colors[idx] * 3]; + color = get16BitColor(palEntry[0], palEntry[1], palEntry[2]); } else { color = _16BitPalette[default_cursor_colors[idx]]; } @@ -570,18 +573,20 @@ 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 = (_game.platform == Common::kPlatformFMTowns) ? (_bytesPerPixelOutput * _textSurfaceMultiplier) : 1; 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; + for (int b = 0; b < scl; b++) + *dst1++ = *dst2++ = color; } } } diff --git a/engines/scumm/detection_tables.h b/engines/scumm/detection_tables.h index 98fab9468a..4428185774 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}, diff --git a/engines/scumm/gfx.cpp b/engines/scumm/gfx.cpp index 3b8d9c296a..2ee54337b8 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,16 @@ void ScummEngine::initScreens(int b, int h) { _res->nukeResource(rtBuffer, i + 5); } + if (_townsScreen) { + if (!_townsClearLayerFlag && (h - b != _virtscr[kMainVirtScreen].h)) + _townsScreen->clearLayer(0); + + if (_game.id == GID_MONKEY2 || _game.id == GID_INDY4) { + _textSurface.fillRect(Common::Rect(0, 0, _textSurface.w * _textSurfaceMultiplier, _textSurface.h * _textSurfaceMultiplier), 0); + _townsScreen->clearLayer(1); + } + } + 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 +620,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 +643,10 @@ 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) { + if (_game.platform == Common::kPlatformFMTowns) { + towns_drawStripToScreen(vs, x, y, x, top, width, height); + return; + } else if (_bytesPerPixelOutput == 2) { const byte *srcPtr = (const byte *)src; const byte *textPtr = (byte *)_textSurface.getBasePtr(x * m, y * m); byte *dstPtr = _compositeBuf; @@ -824,28 +827,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 +998,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; @@ -1028,6 +1009,9 @@ void ScummEngine::restoreBackground(Common::Rect rect, byte backColor) { if (rect.left > vs->w) return; + if (_game.platform == Common::kPlatformFMTowns && _game.id == GID_MONKEY && vs->number == kVerbVirtScreen && rect.bottom <= 154) + rect.right = 320; + // Convert 'rect' to local (virtual screen) coordinates rect.top -= vs->topline; rect.bottom -= vs->topline; @@ -1047,10 +1031,21 @@ void ScummEngine::restoreBackground(Common::Rect rect, byte backColor) { 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); + 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 { + byte *mask = (byte *)_textSurface.getBasePtr(rect.left, rect.top - _screenTop); + fill(mask, _textSurface.pitch, CHARSET_MASK_TRANSPARENCY, width * _textSurfaceMultiplier, height * _textSurfaceMultiplier, _textSurface.bytesPerPixel); + } } } else { + 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); + } + if (_game.features & GF_16BIT_COLOR) fill(screenBuf, vs->pitch, _16BitPalette[backColor], width, height, vs->bytesPerPixel); else @@ -1102,7 +1097,10 @@ void ScummEngine::clearCharsetMask() { } void ScummEngine::clearTextSurface() { - fill((byte*)_textSurface.pixels, _textSurface.pitch, CHARSET_MASK_TRANSPARENCY, _textSurface.w, _textSurface.h, _textSurface.bytesPerPixel); + if (_townsScreen) + _townsScreen->fillLayerRect(1, 0, 0, _textSurface.w, _textSurface.h, 0); + + fill((byte*)_textSurface.pixels, _textSurface.pitch, _game.platform == Common::kPlatformFMTowns ? 0 : CHARSET_MASK_TRANSPARENCY, _textSurface.w, _textSurface.h, _textSurface.bytesPerPixel); } byte *ScummEngine::getMaskBuffer(int x, int y, int z) { @@ -1256,13 +1254,25 @@ 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 || (color >= 254 && _game.platform == Common::kPlatformFMTowns && (_game.id == GID_MONKEY2 || _game.id == GID_INDY4))) { + if (_game.platform == Common::kPlatformFMTowns) { + if (color == 254) { + color = color; + towns_setupPalCycleField(x, y, x2, y2); + } + } else { + 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 +1303,20 @@ 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 { + 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) + return; + } + fill(backbuff, vs->pitch, color, width, height, vs->bytesPerPixel); + } } } @@ -1703,6 +1723,11 @@ 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); } + 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); + } + _vertStripNextInc = height * vs->pitch - 1 * vs->bytesPerPixel; _objectMode = (flag & dbObjectMode) == dbObjectMode; @@ -3662,6 +3687,9 @@ void ScummEngine::fadeOut(int effect) { // Fill screen 0 with black memset(vs->getPixels(0, 0), 0, vs->pitch * vs->h); + if (_game.version == 3 && _game.platform == Common::kPlatformFMTowns) + _textSurface.fillRect(Common::Rect(0, vs->topline * _textSurfaceMultiplier, _textSurface.pitch, (vs->topline + vs->h) * _textSurfaceMultiplier), 0); + // Fade to black with the specified effect, if any. switch (effect) { case 1: @@ -3856,15 +3884,10 @@ 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 { + if (_game.platform == Common::kPlatformFMTowns) + towns_drawStripToScreen(vs, x, y + vs->topline, x, y, width, height); + else _system->copyRectToScreen(vs->getPixels(x, y), vs->pitch, x, y + vs->topline, width, height); - } if (++blits >= blits_before_refresh) { @@ -3904,23 +3927,19 @@ 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; + + if (_townsScreen) { + towns_drawStripToScreen(vs, 0, vs->topline + vs->h - step, 0, y - step, vs->w, step); + } else { + 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 +3948,19 @@ 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; + + if (_townsScreen) { + towns_drawStripToScreen(vs, 0, vs->topline, 0, vs->h - y, vs->w, step); + } else { + 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 +3969,19 @@ 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; + + if (_townsScreen) { + towns_drawStripToScreen(vs, vs->w - step, vs->topline, x - step, 0, step, vs->h); + } else { + 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 +3990,19 @@ 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); + if (_townsScreen) { + towns_drawStripToScreen(vs, 0, vs->topline, vs->w - x, 0, step, vs->h); + } else { + 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..b4b6309f51 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,64 @@ public: }; #endif +// 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; + } _layers[2]; + + uint8 *_outBuffer; + + int _height; + int _width; + int _pitch; + int _bpp; + + int _numDirtyRects; + Common::List<Common::Rect> _dirtyRects; + OSystem *_system; +}; + } // 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..602f6984b4 --- /dev/null +++ b/engines/scumm/gfx_towns.cpp @@ -0,0 +1,508 @@ +/* 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" + +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) { + for (int h = 0; h < height; ++h) { + if (_bytesPerPixelOutput == 2) { + for (int w = 0; w < width; ++w) { + WRITE_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[] _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; + + 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) { + WRITE_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; + + 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); + WRITE_LE_UINT16(dst, calc16BitColor(&l->palette[col * 3])); + } + } else { + WRITE_LE_UINT16(dst, READ_LE_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 diff --git a/engines/scumm/module.mk b/engines/scumm/module.mk index 14d1f5fdd4..cd89f5ffad 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 \ diff --git a/engines/scumm/palette.cpp b/engines/scumm/palette.cpp index 7659e5fe1a..38577cd0c4 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 { @@ -139,6 +140,22 @@ void ScummEngine::resetPalette() { 0x00, 0x00, 0x00, 0x00, 0xFF, 0x00 }; +#ifdef USE_RGB_COLOR + 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 + if (_game.version <= 1) { if (_game.platform == Common::kPlatformApple2GS) { // TODO: unique palette? @@ -198,6 +215,17 @@ void ScummEngine::resetPalette() { // else we initialise and then lock down the first 16 colors. if (_renderMode != Common::kRenderEGA) setPaletteFromTable(tableAmigaMIPalette, sizeof(tableAmigaMIPalette) / 3); + } 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); } setDirtyColors(0, 255); } @@ -465,6 +493,9 @@ void ScummEngine::cyclePalette() { int valueToAdd; int i, j; + if (_game.platform == Common::kPlatformFMTowns && (!_townsPaletteFlags & 1)) + return; + valueToAdd = VAR(VAR_TIMER); if (valueToAdd < VAR(VAR_TIMER_NEXT)) valueToAdd = VAR(VAR_TIMER_NEXT); @@ -506,6 +537,9 @@ void ScummEngine::moveMemInPalRes(int start, int end, byte direction) { } void ScummEngine::palManipulateInit(int resID, int start, int end, int time) { + if (_game.platform == Common::kPlatformFMTowns && (!_townsPaletteFlags & 1)) + return; + byte *string1 = getStringAddress(resID); byte *string2 = getStringAddress(resID + 1); byte *string3 = getStringAddress(resID + 2); @@ -973,6 +1007,10 @@ void ScummEngine::setCurrentPalette(int palindex) { pals = getPalettePtr(_curPalIndex, _roomResource); if (_game.id == GID_LOOM && _game.platform == Common::kPlatformPCEngine) { setPCEPaletteFromPtr(pals); +#ifdef USE_RGB_COLOR + } else if (_game.platform == Common::kPlatformFMTowns) { + towns_setPaletteFromPtr(pals); +#endif } else { setPaletteFromPtr(pals); } @@ -1069,10 +1107,21 @@ void ScummEngine::updatePalette() { } } - _system->setPalette(palette_colors, first, num); - _palDirtyMax = -1; _palDirtyMin = 256; + +#ifdef USE_RGB_COLOR + 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 + + _system->setPalette(palette_colors, first, num); } } // End of namespace Scumm diff --git a/engines/scumm/room.cpp b/engines/scumm/room.cpp index 014787ec7e..35d6d25548 100644 --- a/engines/scumm/room.cpp +++ b/engines/scumm/room.cpp @@ -194,6 +194,9 @@ void ScummEngine::startScene(int room, Actor *a, int objectNr) { showActors(); _egoPositioned = false; + + towns_resetPalCycleFields(); + 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..a1747db690 100644 --- a/engines/scumm/saveload.cpp +++ b/engines/scumm/saveload.cpp @@ -1294,9 +1294,36 @@ 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); } + + // 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); + } + if (_shadowPaletteSize) { s->saveLoadArrayOf(_shadowPalette, _shadowPaletteSize, 1, sleByte); // _roomPalette didn't show up until V21 save games @@ -1459,6 +1486,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..aaa79f38b6 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 82 /** * An auxillary macro, used to specify savegame versions. We use this instead diff --git a/engines/scumm/script_v4.cpp b/engines/scumm/script_v4.cpp index 6dc3004432..8e1feb7388 100644 --- a/engines/scumm/script_v4.cpp +++ b/engines/scumm/script_v4.cpp @@ -111,62 +111,15 @@ 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); + if (_townsScreen) + _townsScreen->clearLayer(1); + return; } -#endif - } + 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..56c9f11445 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 || _game.platform != Common::kPlatformFMTowns)); if (paramsBeforeOpcode) { a = getVarOrDirectWord(PARAM_1); @@ -1714,21 +1714,51 @@ void ScummEngine_v5::o5_roomOps() { if (a) { 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; } } diff --git a/engines/scumm/scumm-md5.h b/engines/scumm/scumm-md5.h index a25fac1a88..47520e8f54 100644 --- a/engines/scumm/scumm-md5.h +++ b/engines/scumm/scumm-md5.h @@ -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 }, @@ -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 }, diff --git a/engines/scumm/scumm.cpp b/engines/scumm/scumm.cpp index 0f1b43cedb..4cb4b282e8 100644 --- a/engines/scumm/scumm.cpp +++ b/engines/scumm/scumm.cpp @@ -258,7 +258,7 @@ ScummEngine::ScummEngine(OSystem *syst, const DetectorResult &dr) _switchRoomEffect2 = 0; _switchRoomEffect = 0; - _bytesPerPixel = 1; + _bytesPerPixelOutput = _bytesPerPixel = 1; _doEffect = false; _snapScroll = false; _currentLights = 0; @@ -278,6 +278,7 @@ ScummEngine::ScummEngine(OSystem *syst, const DetectorResult &dr) _hePalettes = NULL; _hePaletteSlot = 0; _16BitPalette = NULL; + _townsScreen = 0; _shadowPalette = NULL; _shadowPaletteSize = 0; memset(_currentPalette, 0, sizeof(_currentPalette)); @@ -318,6 +319,13 @@ ScummEngine::ScummEngine(OSystem *syst, const DetectorResult &dr) _skipDrawObject = 0; + _townsPaletteFlags = 0; + _townsClearLayerFlag = 1; + _townsActiveLayerFlags = 3; + memset(&_curStringRect, -1, sizeof(Common::Rect)); + memset(&_cyclRects, 0, 16 * sizeof(Common::Rect)); + _numCyclRects = 0; + // // Init all VARS to 0xFF // @@ -533,16 +541,19 @@ 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 + if (_game.platform == Common::kPlatformFMTowns) + _bytesPerPixelOutput = 2; +#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); @@ -608,7 +619,10 @@ ScummEngine::~ScummEngine() { free(_compositeBuf); free(_herculesBuf); - free(_fmtownsBuf); + + free(_16BitPalette); + + delete _townsScreen; delete _debugger; @@ -1116,14 +1130,19 @@ Common::Error ScummEngine::init() { screenWidth *= _textSurfaceMultiplier; screenHeight *= _textSurfaceMultiplier; } - if (_game.features & GF_16BIT_COLOR) { + if (_game.features & GF_16BIT_COLOR || _game.platform == Common::kPlatformFMTowns) { #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) { + warning("Starting game without the required 16bit color support.\nYou will experience severe color glitches"); + initGraphics(screenWidth, screenHeight, (screenWidth > 320)); + } else { + error("16bit color support is required for this game"); + } #endif } else { initGraphics(screenWidth, screenHeight, (screenWidth > 320)); @@ -1241,13 +1260,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 @@ -1325,6 +1339,18 @@ void ScummEngine::resetScumm() { debug(9, "resetScumm"); +#ifdef USE_RGB_COLOR + if (_game.features & GF_16BIT_COLOR || _game.platform == Common::kPlatformFMTowns) + _16BitPalette = (uint16 *)calloc(512, sizeof(uint16)); +#endif + + 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); + } + if (_game.version == 0) { initScreens(8, 144); } else if ((_game.id == GID_MANIAC) && (_game.version <= 1) && !(_game.platform == Common::kPlatformNES)) { @@ -1517,8 +1543,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); @@ -1928,6 +1952,10 @@ void ScummEngine::waitForTimer(int msec_delay) { while (!shouldQuit()) { _sound->updateCD(); // Loop CD Audio if needed parseEvents(); + + if (_townsScreen) + _townsScreen->update(); + _system->updateScreen(); if (_system->getMillis() >= start_time + msec_delay) break; @@ -2055,6 +2083,8 @@ load_game: goto load_game; } + towns_processPalCycleField(); + if (_currentRoom == 0) { if (_game.version > 3) CHARSET_1(); @@ -2442,6 +2472,10 @@ 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. + + if (_townsScreen) + _townsScreen->update(); + _system->updateScreen(); // Resume sound & video diff --git a/engines/scumm/scumm.h b/engines/scumm/scumm.h index 1357bad8cf..d161c698c4 100644 --- a/engines/scumm/scumm.h +++ b/engines/scumm/scumm.h @@ -972,6 +972,7 @@ public: Common::RenderMode _renderMode; uint8 _bytesPerPixel; + uint8 _bytesPerPixelOutput; protected: ColorCycle _colorCycle[16]; // Palette cycles @@ -1044,6 +1045,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 +1079,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 +1223,7 @@ protected: void restoreCharsetBg(); void clearCharsetMask(); void clearTextSurface(); - + virtual void initCharset(int charset); virtual void printString(int m, const byte *msg); @@ -1397,6 +1399,36 @@ public: // Exists both in V7 and in V72HE: byte VAR_NUM_GLOBAL_OBJS; + + // FM-Towns specific +public: + bool towns_isRectInStringBox(int x1, int y1, int x2, int y2); + byte _townsPaletteFlags; + byte _townsCharsetColorMap[16]; + +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; }; } // End of namespace Scumm diff --git a/engines/scumm/string.cpp b/engines/scumm/string.cpp index 30281cb565..b1e645be3e 100644 --- a/engines/scumm/string.cpp +++ b/engines/scumm/string.cpp @@ -508,6 +508,9 @@ void ScummEngine::CHARSET_1() { if (_game.version >= 5) memcpy(_charsetColorMap, _charsetData[_charset->getCurID()], 4); + if (_keepText && _game.platform == Common::kPlatformFMTowns) + memcpy(&_charset->_str, &_curStringRect, sizeof(Common::Rect)); + if (_talkDelay) return; @@ -539,7 +542,10 @@ void ScummEngine::CHARSET_1() { _nextTop = _string[0].ypos + _screenTop; #endif } else { - restoreCharsetBg(); + if (_game.platform == Common::kPlatformFMTowns) + towns_restoreCharsetBg(); + else + restoreCharsetBg(); } } @@ -660,6 +666,9 @@ void ScummEngine::CHARSET_1() { } } + if (_game.platform == Common::kPlatformFMTowns && (c == 0 || c == 2 || c == 3)) + memcpy(&_curStringRect, &_charset->_str, sizeof(Common::Rect)); + #ifdef ENABLE_SCUMM_7_8 if (_game.version >= 7 && subtitleLine != subtitleBuffer) { ((ScummEngine_v7 *)this)->addSubtitleToQueue(subtitleBuffer, subtitlePos, _charsetColor, _charset->getCurID()); diff --git a/engines/scumm/verbs.cpp b/engines/scumm/verbs.cpp index 25a59e0317..5135fd94a2 100644 --- a/engines/scumm/verbs.cpp +++ b/engines/scumm/verbs.cpp @@ -1451,9 +1451,10 @@ void ScummEngine::restoreVerbBG(int verb) { VerbSlot *vs; vs = &_verbs[verb]; + uint8 col = ((_game.platform == Common::kPlatformFMTowns) && (_game.id == GID_MONKEY2 || _game.id == GID_INDY4) && (vs->bkcolor == _townsOverrideShadowColor)) ? 0 : vs->bkcolor; if (vs->oldRect.left != -1) { - restoreBackground(vs->oldRect, vs->bkcolor); + restoreBackground(vs->oldRect, col); vs->oldRect.left = -1; } } |