aboutsummaryrefslogtreecommitdiff
path: root/engines/scumm
diff options
context:
space:
mode:
authorJohannes Schickel2010-10-13 03:57:44 +0000
committerJohannes Schickel2010-10-13 03:57:44 +0000
commit75e8452b6e6a2bf4fb2f588aa00b428a60d873b5 (patch)
treef29541d55309487a94bd1d38e8b53bb3dde9aec6 /engines/scumm
parent48ee83b88957dab86bc763e9ef21a70179fa8679 (diff)
parente9f50882ea5b6beeefa994040be9d3bab6a1f107 (diff)
downloadscummvm-rg350-75e8452b6e6a2bf4fb2f588aa00b428a60d873b5.tar.gz
scummvm-rg350-75e8452b6e6a2bf4fb2f588aa00b428a60d873b5.tar.bz2
scummvm-rg350-75e8452b6e6a2bf4fb2f588aa00b428a60d873b5.zip
OPENGL: Merged from trunk, from rev 52105 to 53396.
This includes an rather hacky attempt to merge all the recent gp2x backend changes into the branch. I suppose the gp2x backend and probably all new backends, i.e. gph, dingux etc., might not compile anymore. Since I have no way of testing those it would be nice if porters could look into getting those up to speed in this branch. svn-id: r53399
Diffstat (limited to 'engines/scumm')
-rw-r--r--engines/scumm/actor.cpp7
-rw-r--r--engines/scumm/charset.cpp161
-rw-r--r--engines/scumm/charset.h16
-rw-r--r--engines/scumm/cursor.cpp33
-rw-r--r--engines/scumm/detection.cpp4
-rw-r--r--engines/scumm/detection_tables.h16
-rw-r--r--engines/scumm/dialogs.cpp17
-rw-r--r--engines/scumm/dialogs.h3
-rw-r--r--engines/scumm/gfx.cpp283
-rw-r--r--engines/scumm/gfx.h63
-rw-r--r--engines/scumm/gfx_towns.cpp522
-rw-r--r--engines/scumm/he/wiz_he.cpp2
-rw-r--r--engines/scumm/help.cpp2
-rw-r--r--engines/scumm/imuse/imuse_player.cpp4
-rw-r--r--engines/scumm/midiparser_eup.cpp222
-rw-r--r--engines/scumm/module.mk3
-rw-r--r--engines/scumm/object.cpp2
-rw-r--r--engines/scumm/palette.cpp73
-rw-r--r--engines/scumm/player_sid.cpp24
-rw-r--r--engines/scumm/player_towns.cpp748
-rw-r--r--engines/scumm/player_towns.h181
-rw-r--r--engines/scumm/player_v2cms.cpp385
-rw-r--r--engines/scumm/room.cpp5
-rw-r--r--engines/scumm/saveload.cpp51
-rw-r--r--engines/scumm/saveload.h2
-rw-r--r--engines/scumm/script.cpp19
-rw-r--r--engines/scumm/script_v2.cpp1
-rw-r--r--engines/scumm/script_v3.cpp40
-rw-r--r--engines/scumm/script_v4.cpp64
-rw-r--r--engines/scumm/script_v5.cpp119
-rw-r--r--engines/scumm/scumm-md5.h12
-rw-r--r--engines/scumm/scumm.cpp158
-rw-r--r--engines/scumm/scumm.h51
-rw-r--r--engines/scumm/scumm_v0.h5
-rw-r--r--engines/scumm/scumm_v2.h2
-rw-r--r--engines/scumm/scumm_v3.h2
-rw-r--r--engines/scumm/sound.cpp101
-rw-r--r--engines/scumm/sound.h1
-rw-r--r--engines/scumm/string.cpp17
-rw-r--r--engines/scumm/vars.cpp6
-rw-r--r--engines/scumm/verbs.cpp72
41 files changed, 2445 insertions, 1054 deletions
diff --git a/engines/scumm/actor.cpp b/engines/scumm/actor.cpp
index a06939dc51..1bcb065b25 100644
--- a/engines/scumm/actor.cpp
+++ b/engines/scumm/actor.cpp
@@ -2159,7 +2159,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();
}
}
diff --git a/engines/scumm/charset.cpp b/engines/scumm/charset.cpp
index fa4804ce7d..5b33fee742 100644
--- a/engines/scumm/charset.cpp
+++ b/engines/scumm/charset.cpp
@@ -52,6 +52,9 @@ void ScummEngine::loadCJKFont() {
_newLineCharacter = 0;
if (_game.version <= 5 && _game.platform == Common::kPlatformFMTowns && _language == Common::JA_JPN) { // FM-TOWNS v3 / v5 Kanji
+#ifdef DISABLE_TOWNS_DUAL_LAYER_MODE
+ error("FM-Towns Kanji font drawing requires dual graphics layer support which is disabled in this build");
+#endif
int numChar = 256 * 32;
_2byteWidth = 16;
_2byteHeight = 16;
@@ -453,21 +456,51 @@ 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, 9, 10, 9, 10, 9, 10, 0, 0 };
+ static const uint8 sjisFontHeightM2[] = { 8, 8, 9, 9, 9, 8, 9, 9, 9, 8 };
+ static const uint8 sjisFontHeightI4[] = { 8, 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 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->_game.platform == Common::kPlatformFMTowns) {
+ if (_vm->_useCJKMode) {
+ if ((chr & 0xff00) == 0xfd00) {
+ chr &= 0xff;
+ } else if (chr >= 256) {
+ spacing = 9;
+ } else if (chr >= 128) {
+ spacing = 5;
+ }
+
+ if (spacing) {
+ static const uint8 sjisWidthM1[] = { 0, 1, 0, 0, 0, 0, 0, 0, 0, 0 };
+ static const uint8 sjisWidthM2[] = { 0, 1, 1, 1, 1, 0, 1, 1, 1, 0 };
+ static const uint8 sjisWidthI4[] = { 0, 0, 1, 1, 1, 0, 0, 0, 0, 0 };
+ const uint8 *wtbl = (_vm->_game.id == GID_MONKEY) ? sjisWidthM1 : ((_vm->_game.id == GID_INDY4) ? sjisWidthI4 : sjisWidthM2);
+ spacing += wtbl[_curId];
+ }
+ }
+ } else if (chr >= 0x80 && _vm->_useCJKMode) {
+ 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;
@@ -476,7 +509,7 @@ int CharsetRendererClassic::getCharWidth(byte chr) {
int CharsetRenderer::getStringWidth(int arg, const byte *text) {
int pos = 0;
int width = 1;
- byte chr;
+ uint16 chr;
int oldID = getCurID();
int code = (_vm->_game.heversion >= 80) ? 127 : 64;
@@ -534,12 +567,18 @@ 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 ((chr >= 0x80 && chr <= 0x9f) || (chr >= 0xe0 && chr <= 0xfd))
+ chr = (chr << 8) | text[pos++];
+ } else if (chr & 0x80) {
+ pos++;
+ width += _vm->_2byteWidth;
+ continue;
+ }
}
+ width += getCharWidth(chr);
}
setCurID(oldID);
@@ -631,7 +670,7 @@ void CharsetRenderer::addLinebreaks(int a, byte *str, int pos, int maxwidth) {
setCurID(oldID);
}
-int CharsetRendererV3::getCharWidth(byte chr) {
+int CharsetRendererV3::getCharWidth(uint16 chr) {
if (chr & 0x80 && _vm->_useCJKMode)
return _vm->_2byteWidth / 2;
int spacing = 0;
@@ -655,6 +694,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();
@@ -672,7 +719,12 @@ void CharsetRendererPCE::setColor(byte color) {
void CharsetRendererCommon::enableShadow(bool enable) {
if (enable) {
if (_vm->_game.platform == Common::kPlatformFMTowns) {
- _shadowColor = 8;
+ _shadowColor =
+#ifndef DISABLE_TOWNS_DUAL_LAYER_MODE
+ _vm->_game.version == 5 ? _vm->_townsCharsetColorMap[0] : 0x88;
+#else
+ 8;
+#endif
_shadowMode = kFMTOWNSShadowMode;
} else {
_shadowColor = 0;
@@ -683,7 +735,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 +775,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 +795,12 @@ 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 || (_vm->_game.id == GID_LOOM && !is2byte)) &&
+#endif
+ (ignoreCharsetMask || !vs->hasTwoBuffers)) {
dst = vs->getPixels(_left, drawTop);
drawBits1(*vs, dst, charPtr, drawTop, origWidth, origHeight, vs->bytesPerPixel);
} else {
@@ -801,6 +857,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)),
@@ -836,6 +915,10 @@ void CharsetRendererClassic::printChar(int chr, bool ignoreCharsetMask) {
_vm->_charsetColorMap[1] = _color;
+#ifndef DISABLE_TOWNS_DUAL_LAYER_MODE
+ processTownsCharsetColors(_bytesPerPixel);
+#endif
+
if (is2byte) {
enableShadow(true);
charPtr = _vm->get2byteCharPtr(chr);
@@ -851,7 +934,7 @@ void CharsetRendererClassic::printChar(int chr, bool ignoreCharsetMask) {
width = charPtr[0];
height = charPtr[1];
-
+
if (_disableOffsX) {
offsX = 0;
} else {
@@ -866,8 +949,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 +988,13 @@ 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;
}
@@ -961,7 +1050,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 {
@@ -1064,13 +1157,18 @@ 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 =
+#ifndef DISABLE_TOWNS_DUAL_LAYER_MODE
+ (_vm->_game.platform == Common::kPlatformFMTowns) ? _vm->_townsCharsetColorMap :
+#endif
+ _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 +1185,11 @@ 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 =
+#ifndef DISABLE_TOWNS_DUAL_LAYER_MODE
+ (_vm->_game.platform == Common::kPlatformFMTowns && _vm->_game.version == 5) ? _vm->_townsCharsetColorMap[1] :
+#endif
+ _color;
for (y = 0; y < height && y + drawTop < s.h; y++) {
for (x = 0; x < width; x++) {
@@ -1108,7 +1211,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;
@@ -1191,7 +1294,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);
}
diff --git a/engines/scumm/charset.h b/engines/scumm/charset.h
index dca254669b..b5fc7b1b15 100644
--- a/engines/scumm/charset.h
+++ b/engines/scumm/charset.h
@@ -80,12 +80,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(); }
@@ -130,7 +134,7 @@ 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);
};
class CharsetRendererNES : public CharsetRendererCommon {
@@ -147,7 +151,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,7 +165,7 @@ 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
@@ -185,7 +189,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 +208,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/detection.cpp b/engines/scumm/detection.cpp
index 9721c75677..9010cb84c3 100644
--- a/engines/scumm/detection.cpp
+++ b/engines/scumm/detection.cpp
@@ -401,8 +401,8 @@ static void composeFileHashMap(const Common::FSList &fslist, DescMap &fileMD5Map
continue;
bool matched = false;
- for (const char *glob = *globs; *glob; glob++)
- if (file->getName().matchString(glob, true)) {
+ for (const char **glob = globs; *glob; glob++)
+ if (file->getName().matchString(*glob, true)) {
matched = true;
break;
}
diff --git a/engines/scumm/detection_tables.h b/engines/scumm/detection_tables.h
index f275b1c93f..4428185774 100644
--- a/engines/scumm/detection_tables.h
+++ b/engines/scumm/detection_tables.h
@@ -186,6 +186,7 @@ using Common::GUIO_NONE;
using Common::GUIO_NOLAUNCHLOAD;
using Common::GUIO_NOMIDI;
using Common::GUIO_NOSPEECH;
+using Common::GUIO_MIDITOWNS;
// The following table contains information about variants of our various
// games. We index into it with help of md5table (from scumm-md5.h), to find
@@ -217,19 +218,20 @@ static const GameSettings gameVariantsTable[] = {
{"zak", "V1", "v1", GID_ZAK, 1, 0, MDT_PCSPK | MDT_PCJR, 0, UNK, GUIO_NOSPEECH | GUIO_NOMIDI},
{"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},
+ {"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},
- {"indy3", "FM-TOWNS", 0, GID_INDY3, 3, 0, MDT_TOWNS, GF_OLD256 | GF_FEW_LOCALS | GF_AUDIOTRACKS, Common::kPlatformFMTowns, GUIO_NOSPEECH | GUIO_NOMIDI},
+ {"indy3", "FM-TOWNS", 0, GID_INDY3, 3, 0, MDT_TOWNS, GF_OLD256 | GF_FEW_LOCALS | GF_AUDIOTRACKS, Common::kPlatformFMTowns, GUIO_NOSPEECH | GUIO_NOMIDI | GUIO_MIDITOWNS},
{"loom", "EGA", "ega", GID_LOOM, 3, 0, MDT_PCSPK | MDT_PCJR | MDT_CMS | MDT_ADLIB | MDT_MIDI | MDT_PREFER_MT32, 0, UNK, GUIO_NOSPEECH},
{"loom", "No AdLib", "ega", GID_LOOM, 3, 0, MDT_PCSPK | MDT_PCJR | MDT_CMS, 0, UNK, GUIO_NOSPEECH | GUIO_NOMIDI},
#ifdef USE_RGB_COLOR
{"loom", "PC-Engine", 0, GID_LOOM, 3, 0, MDT_NONE, GF_AUDIOTRACKS | GF_OLD256 | GF_16BIT_COLOR, Common::kPlatformPCEngine, GUIO_NOSPEECH | GUIO_NOMIDI},
#endif
- {"loom", "FM-TOWNS", 0, GID_LOOM, 3, 0, MDT_TOWNS, GF_AUDIOTRACKS | GF_OLD256, Common::kPlatformFMTowns, GUIO_NOSPEECH | GUIO_NOMIDI},
+ {"loom", "FM-TOWNS", 0, GID_LOOM, 3, 0, MDT_TOWNS, GF_AUDIOTRACKS | GF_OLD256, Common::kPlatformFMTowns, GUIO_NOSPEECH | GUIO_NOMIDI | GUIO_MIDITOWNS},
{"loom", "VGA", "vga", GID_LOOM, 4, 0, MDT_NONE, GF_AUDIOTRACKS, Common::kPlatformPC, GUIO_NOSPEECH | GUIO_NOMIDI},
{"pass", 0, 0, GID_PASS, 4, 0, MDT_PCSPK | MDT_PCJR | MDT_ADLIB, GF_16COLOR, Common::kPlatformPC, GUIO_NOSPEECH | GUIO_NOMIDI},
@@ -239,13 +241,15 @@ static const GameSettings gameVariantsTable[] = {
{"monkey", "No AdLib", "ega", GID_MONKEY_EGA, 4, 0, MDT_PCSPK | MDT_PCJR, GF_16COLOR, Common::kPlatformAtariST, GUIO_NOSPEECH | GUIO_NOMIDI},
{"monkey", "Demo", "ega", GID_MONKEY_EGA, 4, 0, MDT_PCSPK | MDT_PCJR | MDT_ADLIB, GF_16COLOR, Common::kPlatformPC, GUIO_NOSPEECH | GUIO_NOMIDI},
{"monkey", "CD", 0, GID_MONKEY, 5, 0, MDT_ADLIB, GF_AUDIOTRACKS, UNK, GUIO_NOSPEECH | GUIO_NOMIDI},
- {"monkey", "FM-TOWNS", 0, GID_MONKEY, 5, 0, MDT_ADLIB, GF_AUDIOTRACKS, Common::kPlatformFMTowns, GUIO_NOSPEECH | GUIO_NOMIDI},
+ {"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/dialogs.cpp b/engines/scumm/dialogs.cpp
index 1e0bf6d4be..a48e54c05a 100644
--- a/engines/scumm/dialogs.cpp
+++ b/engines/scumm/dialogs.cpp
@@ -473,14 +473,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/gfx.cpp b/engines/scumm/gfx.cpp
index 7b0d4909d6..e7c81bd418 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;
@@ -1028,6 +1014,11 @@ void ScummEngine::restoreBackground(Common::Rect rect, byte backColor) {
if (rect.left > vs->w)
return;
+#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
+
// Convert 'rect' to local (virtual screen) coordinates
rect.top -= vs->topline;
rect.bottom -= vs->topline;
@@ -1047,10 +1038,26 @@ 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);
+#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;
@@ -1794,18 +1848,16 @@ bool Gdi::drawStrip(byte *dstPtr, VirtScreen *vs, int x, int y, const int width,
if (stripnr * 2 + 2 < smapLen) {
offset = READ_LE_UINT16(smap_ptr + stripnr * 2 + 2);
}
- assertRange(0, offset, smapLen-1, "screen strip");
} else if (_vm->_game.features & GF_SMALL_HEADER) {
smapLen = READ_LE_UINT32(smap_ptr);
if (stripnr * 4 + 4 < smapLen)
offset = READ_LE_UINT32(smap_ptr + stripnr * 4 + 4);
- assertRange(0, offset, smapLen-1, "screen strip");
} else {
smapLen = READ_BE_UINT32(smap_ptr);
if (stripnr * 4 + 8 < smapLen)
offset = READ_LE_UINT32(smap_ptr + stripnr * 4 + 8);
- assertRange(0, offset, smapLen-1, "screen strip");
}
+ assertRange(0, offset, smapLen-1, "screen strip");
return decompressBitmap(dstPtr, vs->pitch, smap_ptr + offset, height);
}
@@ -1902,7 +1954,7 @@ void Gdi::decodeMask(int x, int y, const int width, const int height,
z_plane_ptr = zplane_list[i] + offs;
if (tmsk_ptr) {
- const byte *tmsk = tmsk_ptr + READ_LE_UINT16(tmsk_ptr + 8);
+ const byte *tmsk = tmsk_ptr + READ_LE_UINT16(tmsk_ptr + stripnr * 2 + 8);
decompressTMSK(mask_ptr, tmsk, z_plane_ptr, height);
} else if (transpStrip && (flag & dbAllowMaskOr)) {
decompressMaskImgOr(mask_ptr, z_plane_ptr, height);
@@ -3655,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);
@@ -3858,15 +3914,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) {
@@ -3906,23 +3959,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;
@@ -3931,21 +3982,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;
@@ -3954,21 +4005,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;
@@ -3977,21 +4029,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/wiz_he.cpp b/engines/scumm/he/wiz_he.cpp
index 1ac7e98689..361a3bc165 100644
--- a/engines/scumm/he/wiz_he.cpp
+++ b/engines/scumm/he/wiz_he.cpp
@@ -2088,7 +2088,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/help.cpp b/engines/scumm/help.cpp
index d9bdf51d19..e15c4a5592 100644
--- a/engines/scumm/help.cpp
+++ b/engines/scumm/help.cpp
@@ -254,7 +254,7 @@ void ScummHelp::updateStrings(byte gameId, byte version, Common::Platform platfo
case 4:
title = "Other game controls:";
if (version <= 2) {
- ADD_TEXT("Inventory: (not yet implemented)");
+ ADD_TEXT("Inventory:");
ADD_BIND("u", "Scroll list up");
ADD_BIND("j", "Scroll list down");
ADD_BIND("i", "Upper left item");
diff --git a/engines/scumm/imuse/imuse_player.cpp b/engines/scumm/imuse/imuse_player.cpp
index 73aec472e4..a90915e438 100644
--- a/engines/scumm/imuse/imuse_player.cpp
+++ b/engines/scumm/imuse/imuse_player.cpp
@@ -47,7 +47,6 @@ namespace Scumm {
#define PERCUSSION_CHANNEL 9
extern MidiParser *MidiParser_createRO();
-extern MidiParser *MidiParser_createEUP();
uint16 Player::_active_notes[128];
@@ -193,9 +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
- _parser = MidiParser_createEUP();
} else if (!memcmp(ptr, "FORM", 4)) {
// Humongous Games XMIDI resource
_parser = MidiParser::createParser_XMIDI();
diff --git a/engines/scumm/midiparser_eup.cpp b/engines/scumm/midiparser_eup.cpp
deleted file mode 100644
index 592d43f7fe..0000000000
--- a/engines/scumm/midiparser_eup.cpp
+++ /dev/null
@@ -1,222 +0,0 @@
-/* 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 "sound/midiparser.h"
-#include "sound/mididrv.h"
-#include "common/util.h"
-
-namespace Scumm {
-
-/**
- * The FM-TOWNS Euphony version of MidiParser.
- */
-class MidiParser_EUP : public MidiParser {
-protected:
- byte _instruments[6][50]; // Two extra bytes for SysEx ID and channel #
- byte *_instr_to_channel;
- struct {
- byte *enable;
- int8 *channel;
- int8 *volume;
- int8 *transpose;
- } _presets;
- bool _loop;
- byte _presend; // Tracks which startup implied events have been sent.
- uint32 _base_tick; // Events times are relative to this base.
-
-protected:
- void parseNextEvent (EventInfo &info);
- void resetTracking();
-
-public:
- bool loadMusic (byte *data, uint32 size);
-};
-
-
-
-//////////////////////////////////////////////////
-//
-// MidiParser_EUP implementation
-//
-//////////////////////////////////////////////////
-
-void MidiParser_EUP::parseNextEvent (EventInfo &info) {
- byte *pos = _position._play_pos;
-
- // FIXME: The presend is for sending init events
- // that aren't actually in the stream. This would
- // be for, e.g., instrument setup. Right now, we
- // don't actually use the instruments specified
- // in the music header. We're sending fixed GM
- // program changes to get a reasonable "one-size-
- // fits-all" sound until we actually support the
- // FM synthesis capabilities of FM-TOWNS.
- for (; _presend < 12; ++_presend) {
- if (_instr_to_channel[_presend >> 1] >= 16)
- continue;
- info.start = pos;
- info.delta = 0;
- if (_presend & 1) {
- byte *data = &_instruments[_presend >> 1][0];
- data[1] = _instr_to_channel[_presend >> 1];
- info.event = 0xF0;
- info.ext.data = data;
- info.length = 48;
- } else {
- info.event = 0xB0 | (_presend >> 1);
- info.basic.param1 = 121;
- info.basic.param2 = 0;
- }
- ++_presend;
- return;
- }
-
- while (true) {
- byte cmd = *pos;
- if ((cmd & 0xF0) == 0x90) {
- byte preset = pos[1];
- byte channel = _presets.channel[preset];
- if (channel >= 16)
- channel = cmd & 0x0F;
- uint16 tick = (pos[2] | ((uint16) pos[3] << 7)) + _base_tick;
- int note = (int) pos[4] + _presets.transpose[preset];
- int volume = (int) pos[5];
- // HACK: Loom-Towns distaff tracks seem to
- // contain zero-volume note events, so change
- // those to full volume.
- if (!volume)
- volume = 127;
- volume += _presets.volume[preset];
- if (volume > 127)
- volume = 127;
- else if (volume < 0)
- volume = 0;
- pos += 6;
- if (_presets.enable[preset]) {
- uint16 duration = pos[1] | (pos[2] << 4);
- info.start = pos;
- uint32 last = _position._last_event_tick;
- info.delta = (tick < last) ? 0 : (tick - last);
- info.event = 0x90 | channel;
- info.length = duration;
- info.basic.param1 = note;
- info.basic.param2 = volume;
- pos += 6;
- break;
- }
- pos += 6;
- } else if (cmd == 0xF2) {
- // This is a "measure marker" of sorts.
- // It advances the "base time", to which
- // all event times are relative.
- _base_tick += (pos[3] << 7) | pos[2];
- pos += 6;
- } else if (cmd == 0xF8) {
- // TODO: Implement this.
- pos += 6;
- } else if (cmd == 0xFD || cmd == 0xFE) {
- // End of track.
- if (_loop && false) {
- // TODO: Implement this.
- } else {
- info.start = pos;
- uint32 last = _position._last_event_tick;
- info.delta = (_base_tick < last) ? 0 : (_base_tick - last);
- info.event = 0xFF;
- info.length = 0;
- info.ext.type = 0x2F;
- info.ext.data = pos;
- break;
- }
- } else {
- error("Unknown Euphony music event 0x%02X", (int) cmd);
- memset(&info, 0, sizeof(info));
- pos = 0;
- break;
- }
- }
- _position._play_pos = pos;
-}
-
-bool MidiParser_EUP::loadMusic (byte *data, uint32 size) {
- unloadMusic();
- byte *pos = data;
- int i;
-
- if (memcmp(pos, "SO", 2)) {
- error("'SO' header expected but found '%c%c' instead.", pos[0], pos[1]);
- return false;
- }
-
- byte numInstruments = pos[16];
- pos += 16 + 2;
- for (i = 0; i < numInstruments; ++i) {
- _instruments[i][0] = 0x7C;
- memcpy (&_instruments[i][2], pos, 48);
- pos += 48;
- }
-
- // Load the prest pointers
- _presets.enable = pos;
- pos += 32;
- _presets.channel = (int8 *) pos;
- pos += 32;
- _presets.volume = (int8 *) pos;
- pos += 32;
- _presets.transpose = (int8 *) pos;
- pos += 32;
-
- pos += 8; // Unknown bytes
- _instr_to_channel = pos; // Instrument-to-channel mapping
- pos += 6;
- pos += 4; // Skip the music size for now.
- pos++; // Unknown byte
- byte tempo = *pos++;
- _loop = (*pos++ != 1);
- pos++; // Unknown byte
-
- _num_tracks = 1;
- _ppqn = 120;
- _tracks[0] = pos;
-
- // Note that we assume the original data passed in
- // will persist beyond this call, i.e. we do NOT
- // copy the data to our own buffer. Take warning....
- resetTracking();
- setTempo (1000000 * 60 / tempo);
- setTrack (0);
- return true;
-}
-
-void MidiParser_EUP::resetTracking() {
- MidiParser::resetTracking();
- _presend = 0;
- _base_tick = 0;
-}
-
-MidiParser *MidiParser_createEUP() { return new MidiParser_EUP; }
-
-} // End of namespace Scumm
diff --git a/engines/scumm/module.mk b/engines/scumm/module.mk
index e5f0745dd6..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 \
@@ -29,7 +30,6 @@ MODULE_OBJS := \
imuse/sysex_samnmax.o \
imuse/sysex_scumm.o \
input.o \
- midiparser_eup.o \
midiparser_ro.o \
object.o \
palette.o \
@@ -37,6 +37,7 @@ MODULE_OBJS := \
player_nes.o \
player_pce.o \
player_sid.o \
+ player_towns.o \
player_v1.o \
player_v2.o \
player_v2a.o \
diff --git a/engines/scumm/object.cpp b/engines/scumm/object.cpp
index c6ac53b862..e2b68f8d3b 100644
--- a/engines/scumm/object.cpp
+++ b/engines/scumm/object.cpp
@@ -714,7 +714,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() {
diff --git a/engines/scumm/palette.cpp b/engines/scumm/palette.cpp
index 09da1b47c4..5c0c58595b 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 {
@@ -56,11 +57,17 @@ uint16 ScummEngine::get16BitColor(uint8 r, uint8 g, uint8 b) {
void ScummEngine::resetPalette() {
static const byte tableC64Palette[] = {
+#if 1 // VICE-based palette. See bug #2847001
+ 0x00, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0x7E, 0x35, 0x2B, 0x6E, 0xB7, 0xC1,
+ 0x7F, 0x3B, 0xA6, 0x5C, 0xA0, 0x35, 0x33, 0x27, 0x99, 0xCB, 0xD7, 0x65,
+ 0x85, 0x53, 0x1C, 0x50, 0x3C, 0x00, 0xB4, 0x6B, 0x61, 0x4A, 0x4A, 0x4A,
+ 0x75, 0x75, 0x75, 0xA3, 0xE7, 0x7C, 0x70, 0x64, 0xD6, 0xA3, 0xA3, 0xA3,
+#else
0x00, 0x00, 0x00, 0xFD, 0xFE, 0xFC, 0xBE, 0x1A, 0x24, 0x30, 0xE6, 0xC6,
0xB4, 0x1A, 0xE2, 0x1F, 0xD2, 0x1E, 0x21, 0x1B, 0xAE, 0xDF, 0xF6, 0x0A,
0xB8, 0x41, 0x04, 0x6A, 0x33, 0x04, 0xFE, 0x4A, 0x57, 0x42, 0x45, 0x40,
0x70, 0x74, 0x6F, 0x59, 0xFE, 0x59, 0x5F, 0x53, 0xFE, 0xA4, 0xA7, 0xA2,
-
+#endif
// 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.
@@ -133,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?
@@ -192,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);
}
@@ -459,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);
@@ -500,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);
@@ -967,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);
}
@@ -1063,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_sid.cpp b/engines/scumm/player_sid.cpp
index 3f7b65cbb4..35671e094b 100644
--- a/engines/scumm/player_sid.cpp
+++ b/engines/scumm/player_sid.cpp
@@ -92,8 +92,8 @@ static const uint16 FREQ_TBL[97] = {
static const int SONG_CHANNEL_OFFSET[3] = { 6, 8, 10 };
static const int RES_ID_CHANNEL[3] = { 3, 4, 5 };
-#define LOBYTE(a) ((a) & 0xFF)
-#define HIBYTE(a) (((a) >> 8) & 0xFF)
+#define LOBYTE_(a) ((a) & 0xFF)
+#define HIBYTE_(a) (((a) >> 8) & 0xFF)
#define GETBIT(var, pos) ((var) & (1<<(pos)))
@@ -176,14 +176,14 @@ void Player_SID::handleMusicBuffer() { // $33cd
l_chanBuf[19] = phaseBit[channel];
l_chanBuf[10] |= 0x01; // attack phase
}
- l_chanBuf[11] = LOBYTE(l_freq);
- l_chanBuf[12] = HIBYTE(l_freq);
+ l_chanBuf[11] = LOBYTE_(l_freq);
+ l_chanBuf[12] = HIBYTE_(l_freq);
releasePhase[channel] = false;
}
// set counter value for frequency update (freqDeltaCounter)
- l_chanBuf[13] = LOBYTE(curStepSum);
- l_chanBuf[14] = HIBYTE(curStepSum);
+ l_chanBuf[13] = LOBYTE_(curStepSum);
+ l_chanBuf[14] = HIBYTE_(curStepSum);
_soundQueue[channel] = RES_ID_CHANNEL[channel];
processSongData(channel);
@@ -340,8 +340,8 @@ void Player_SID::processSongData(int channel) { // $4939
if (songFileOrChanBufData == NULL) { // chanBuf (4C1C)
/*
// TODO: do we need this?
- LOBYTE(vec20[channel]) = 0;
- LOBYTE(songPosPtr[channel]) = LOBYTE(songFileOrChanBufOffset[channel]);
+ LOBYTE_(vec20[channel]) = 0;
+ LOBYTE_(songPosPtr[channel]) = LOBYTE_(songFileOrChanBufOffset[channel]);
*/
releaseResourceUnk(channel);
return;
@@ -551,8 +551,8 @@ void Player_SID::setSIDFreqAS(int channel) { // $4be6
if (swapVarLoaded)
return;
int reg = SID_REG_OFFSET[channel];
- SID_Write(reg, LOBYTE(freqReg[channel])); // freq/pulseWidth voice 1/2/3
- SID_Write(reg+1, HIBYTE(freqReg[channel]));
+ SID_Write(reg, LOBYTE_(freqReg[channel])); // freq/pulseWidth voice 1/2/3
+ SID_Write(reg+1, HIBYTE_(freqReg[channel]));
if (channel < 3) {
SID_Write(reg+5, attackReg[channel]); // attack
SID_Write(reg+6, sustainReg[channel]); // sustain
@@ -839,8 +839,8 @@ void Player_SID::useSwapVars(int channel) { // $5342
SID_Write(24, SIDReg24);
// filter freq.
- SID_Write(21, LOBYTE(freqReg[3]));
- SID_Write(22, HIBYTE(freqReg[3]));
+ SID_Write(21, LOBYTE_(freqReg[3]));
+ SID_Write(22, HIBYTE_(freqReg[3]));
} else {
SIDReg23 = SIDReg23Stuff & BITMASK_INV[channel];
SID_Write(23, SIDReg23);
diff --git a/engines/scumm/player_towns.cpp b/engines/scumm/player_towns.cpp
new file mode 100644
index 0000000000..06f97fd671
--- /dev/null
+++ b/engines/scumm/player_towns.cpp
@@ -0,0 +1,748 @@
+/* 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/sound.h"
+#include "scumm/player_towns.h"
+
+namespace Scumm {
+
+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;
+ _cdaVolLeft = _cdaVolRight = 0;
+
+ _eupVolLeft = _eupVolRight = 0;
+ _eupLooping = false;
+
+ if (_vm->_game.version == 3) {
+ _soundOverride = new SoundOvrParameters[_numSoundMax];
+ memset(_soundOverride, 0, _numSoundMax * sizeof(SoundOvrParameters));
+ }
+
+ _driver = new TownsEuphonyDriver(mixer);
+}
+
+Player_Towns_v1::~Player_Towns_v1() {
+ delete _driver;
+ delete[] _soundOverride;
+}
+
+bool Player_Towns_v1::init() {
+ if (!_driver)
+ return false;
+
+ if (!_driver->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.
+ _intf->setSoundEffectChanMask(-1);
+
+ setVolumeCD(255, 255);
+
+ return true;
+}
+
+void Player_Towns_v1::setMusicVolume(int vol) {
+ _driver->setMusicVolume(vol);
+}
+
+void Player_Towns_v1::startSound(int sound) {
+ uint8 *ptr = _vm->getResourceAddress(rtSound, sound);
+ if (_vm->_game.version != 3)
+ ptr += 2;
+
+ int type = ptr[13];
+
+ if (type == 0) {
+ 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);
+ }
+
+ if (_vm->_game.version == 3)
+ _soundOverride[sound].vLeft = _soundOverride[sound].vRight = _soundOverride[sound].note = 0;
+}
+
+void Player_Towns_v1::stopSound(int sound) {
+ if (sound == 0 || sound == _cdaCurrentSound) {
+ _cdaCurrentSound = 0;
+ _vm->_sound->stopCD();
+ _vm->_sound->stopCDTimer();
+ }
+
+ if (sound != 0 && sound == _eupCurrentSound) {
+ _eupCurrentSound = 0;
+ _eupLooping = false;
+ _driver->stopParser();
+ }
+
+ stopPcmTrack(sound);
+}
+
+void Player_Towns_v1::stopAllSounds() {
+ _cdaCurrentSound = 0;
+ _vm->_sound->stopCD();
+ _vm->_sound->stopCDTimer();
+
+ _eupCurrentSound = 0;
+ _eupLooping = false;
+ _driver->stopParser();
+
+ stopPcmTrack(0);
+}
+
+int Player_Towns_v1::getSoundStatus(int sound) const {
+ if (sound == _cdaCurrentSound)
+ return _vm->_sound->pollCD();
+ if (sound == _eupCurrentSound)
+ return _driver->parserIsPlaying() ? 1 : 0;
+ return Player_Towns::getSoundStatus(sound);
+}
+
+int32 Player_Towns_v1::doCommand(int numargs, int args[]) {
+ int32 res = 0;
+
+ switch (args[0]) {
+ case 2:
+ _driver->intf()->callback(73, 0);
+ break;
+
+ case 3:
+ restartLoopingSounds();
+ break;
+
+ case 8:
+ startSound(args[1]);
+ break;
+
+ case 9:
+ _vm->_sound->stopSound(args[1]);
+ break;
+
+ case 11:
+ stopPcmTrack(0);
+ break;
+
+ case 14:
+ startSoundEx(args[1], args[2], args[3], args[4]);
+ break;
+
+ case 15:
+ stopSoundSuspendLooping(args[1]);
+ break;
+
+ default:
+ warning("Player_Towns_v1::doCommand: Unknown command %d", args[0]);
+ break;
+ }
+
+ return res;
+}
+
+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_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_v1::setSoundNote(int sound, int note) {
+ if (_soundOverride && sound > 0 && sound < _numSoundMax)
+ _soundOverride[sound].note = note;
+}
+
+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_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()
+ };
+
+ ser->saveLoadEntries(this, cdEntries);
+
+ if (!_eupLooping && !_driver->parserIsPlaying())
+ _eupCurrentSound = 0;
+
+ static const SaveLoadEntry eupEntries[] = {
+ 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);
+
+ Player_Towns::saveLoadWithSerializer(ser);
+}
+
+void Player_Towns_v1::restoreAfterLoad() {
+ setVolumeCD(_cdaVolLeft, _cdaVolRight);
+
+ if (_cdaCurrentSoundTemp) {
+ uint8 *ptr = _vm->getResourceAddress(rtSound, _cdaCurrentSoundTemp) + 6;
+ if (_vm->_game.version != 3)
+ ptr += 2;
+
+ if (ptr[7] == 2) {
+ playCdaTrack(_cdaCurrentSoundTemp, ptr, true);
+ _cdaCurrentSound = _cdaCurrentSoundTemp;
+ _cdaNumLoops = _cdaNumLoopsTemp;
+ }
+ }
+
+ if (_eupCurrentSound) {
+ uint8 *ptr = _vm->getResourceAddress(rtSound, _eupCurrentSound) + 6;
+ if (_vm->_game.version != 3)
+ ptr += 2;
+
+ if (ptr[7] == 1) {
+ setSoundVolume(_eupCurrentSound, _eupVolLeft, _eupVolRight);
+ playEuphonyTrack(_eupCurrentSound, ptr);
+ }
+ }
+
+ Player_Towns::restoreAfterLoad();
+}
+
+void Player_Towns_v1::restartLoopingSounds() {
+ if (_cdaNumLoops && !_cdaForceRestart)
+ _cdaForceRestart = 1;
+
+ for (int i = 1; i < 9; i++) {
+ if (!_pcmCurrentSound[i].paused)
+ continue;
+
+ _pcmCurrentSound[i].paused = 0;
+
+ uint8 *ptr = _vm->getResourceAddress(rtSound, _pcmCurrentSound[i].index);
+ if (!ptr)
+ continue;
+ ptr += 24;
+
+ int c = 1;
+ while (_pcmCurrentSound[i].chan != c) {
+ ptr = ptr + READ_LE_UINT32(&ptr[12]) + 32;
+ c++;
+ }
+
+ _driver->playSoundEffect(i + 0x3f, _pcmCurrentSound[i].note, _pcmCurrentSound[i].velo, ptr);
+ }
+
+ _driver->intf()->callback(73, 1);
+}
+
+void Player_Towns_v1::startSoundEx(int sound, int velo, int pan, int note) {
+ uint8 *ptr = _vm->getResourceAddress(rtSound, sound) + 2;
+
+ if (pan > 99)
+ pan = 99;
+
+ 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;
+
+ if (!velo)
+ velo = 1;
+
+ pan = pan ? (((pan << 7) - pan) + 50) / 100 : 64;
+
+ playPcmTrack(sound, ptr + 6, velo ? velo : ptr[14] >> 1, pan, note ? note : ptr[50], pri);
+
+ } else if (ptr[13] == 2) {
+ int volLeft = velo;
+ int volRight = velo;
+
+ if (pan < 50)
+ volRight = ((pan * 2 + 1) * velo + 50) / 100;
+ else if (pan > 50)
+ volLeft = (((99 - pan) * 2 + 1) * velo + 50) / 100;
+
+ setVolumeCD(volLeft, volRight);
+
+ if (!_cdaForceRestart && sound == _cdaCurrentSound)
+ return;
+
+ playCdaTrack(sound, ptr + 6, true);
+ }
+}
+
+void Player_Towns_v1::stopSoundSuspendLooping(int sound) {
+ if (!sound) {
+ return;
+ } else if (sound == _cdaCurrentSound) {
+ if (_cdaNumLoops && _cdaForceRestart)
+ _cdaForceRestart = 1;
+ } else {
+ for (int i = 1; i < 9; i++) {
+ if (sound == _pcmCurrentSound[i].index) {
+ if (!_driver->soundEffectIsPlaying(i + 0x3f))
+ continue;
+ _driver->stopSoundEffect(i + 0x3f);
+ if (_pcmCurrentSound[i].looping)
+ _pcmCurrentSound[i].paused = 1;
+ else
+ _pcmCurrentSound[i].index = 0;
+ }
+ }
+ }
+}
+
+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;
+
+ for (int i = 0; i < 32; i++)
+ _driver->chanEnable(i, *src++);
+ for (int i = 0; i < 32; i++)
+ _driver->chanMode(i, 0xff);
+ for (int i = 0; i < 32; i++)
+ _driver->chanOrdr(i, *src++);
+ for (int i = 0; i < 32; i++)
+ _driver->chanVolumeShift(i, *src++);
+ for (int i = 0; i < 32; i++)
+ _driver->chanNoteShift(i, *src++);
+
+ src += 8;
+ for (int i = 0; i < 6; i++)
+ _driver->assignChannel(i, *src++);
+
+ for (int i = 0; i < data[14]; i++) {
+ _driver->loadInstrument(i, i, pos + i * 48);
+ _driver->intf()->callback(4, i, i);
+ }
+
+ _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;
+
+ for (int i = 0; i < 6; i++)
+ _driver->chanVolume(i, lvl);
+
+ uint32 trackSize = READ_LE_UINT32(src);
+ src += 4;
+ uint8 startTick = *src++;
+
+ _driver->setMusicTempo(*src++);
+ _driver->startMusicTrack(trackData, trackSize, startTick);
+
+ _eupLooping = (*src != 1) ? 1 : 0;
+ _driver->setMusicLoop(_eupLooping != 0);
+ _driver->continueParsing();
+ _eupCurrentSound = sound;
+}
+
+void Player_Towns_v1::playCdaTrack(int sound, const uint8 *data, bool skipTrackVelo) {
+ const uint8 *ptr = data;
+
+ if (!sound)
+ return;
+
+ if (!skipTrackVelo) {
+ 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)
+ return;
+
+ ptr += 16;
+
+ int track = ptr[0];
+ _cdaNumLoops = ptr[1];
+ int start = (ptr[2] * 60 + ptr[3]) * 75 + ptr[4];
+ int end = (ptr[5] * 60 + ptr[6]) * 75 + ptr[7];
+
+ _vm->_sound->playCDTrack(track, _cdaNumLoops == 0xff ? -1 : _cdaNumLoops, start, end <= start ? 0 : end - start);
+ _cdaForceRestart = 0;
+ _cdaCurrentSound = sound;
+}
+
+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;
+}
+
+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
new file mode 100644
index 0000000000..e5023d25c2
--- /dev/null
+++ b/engines/scumm/player_towns.h
@@ -0,0 +1,181 @@
+/* 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_TOWNS_H
+#define SCUMM_PLAYER_TOWNS_H
+
+#include "scumm/scumm.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, 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 startSound(int sound);
+ void stopSound(int sound);
+ void stopAllSounds();
+
+ int getSoundStatus(int sound) const;
+ int getCurrentCdaSound() { return _cdaCurrentSound; }
+ int getCurrentCdaVolume() { return (_cdaVolLeft + _cdaVolRight + 1) >> 1; }
+
+ int32 doCommand(int numargs, int args[]);
+
+ void setVolumeCD(int left, int right);
+ void setSoundVolume(int sound, int left, int right);
+ void setSoundNote(int sound, int note);
+
+ void saveLoadWithSerializer(Serializer *ser);
+ void restoreAfterLoad();
+
+ TownsEuphonyDriver *driver() { return _driver; }
+
+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 playCdaTrack(int sound, const uint8 *data, bool skipTrackVelo = false);
+
+ struct SoundOvrParameters {
+ uint8 vLeft;
+ uint8 vRight;
+ uint8 note;
+ };
+
+ SoundOvrParameters *_soundOverride;
+
+ uint8 _cdaVolLeft;
+ uint8 _cdaVolRight;
+
+ uint8 _eupCurrentSound;
+ uint8 _eupLooping;
+ uint8 _eupVolLeft;
+ uint8 _eupVolRight;
+
+ uint8 _cdaCurrentSound;
+ uint8 _cdaNumLoops;
+ uint8 _cdaForceRestart;
+
+ uint8 _cdaCurrentSoundTemp;
+ uint8 _cdaNumLoopsTemp;
+
+ TownsEuphonyDriver *_driver;
+};
+
+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
+
+#endif
diff --git a/engines/scumm/player_v2cms.cpp b/engines/scumm/player_v2cms.cpp
index e3e7bc1901..ba60a31061 100644
--- a/engines/scumm/player_v2cms.cpp
+++ b/engines/scumm/player_v2cms.cpp
@@ -28,6 +28,7 @@
#include "scumm/scumm.h"
#include "sound/mididrv.h"
#include "sound/mixer.h"
+#include "sound/softsynth/cms.h"
namespace Scumm {
@@ -40,395 +41,13 @@ namespace Scumm {
#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 -
+#define CMS_RATE 22050
const uint8 note_lengths[] = {
0,
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 81762d87a8..b100e15604 100644
--- a/engines/scumm/saveload.cpp
+++ b/engines/scumm/saveload.cpp
@@ -32,6 +32,7 @@
#include "scumm/charset.h"
#include "scumm/imuse_digi/dimuse.h"
#include "scumm/imuse/imuse.h"
+#include "player_towns.h"
#include "scumm/he/intern_he.h"
#include "scumm/object.h"
#include "scumm/resource.h"
@@ -446,6 +447,9 @@ bool ScummEngine::loadState(int slot, bool compat) {
// Update volume settings
syncSoundSettings();
+ if (_townsPlayer && (hdr.ver >= VER(81)))
+ _townsPlayer->restoreAfterLoad();
+
// Init NES costume data
if (_game.platform == Common::kPlatformNES) {
if (hdr.ver < VER(47))
@@ -1289,9 +1293,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
@@ -1393,6 +1426,11 @@ void ScummEngine::saveOrLoad(Serializer *s) {
_imuse->save_or_load(s, this);
}
+
+ // Save/load FM-Towns audio status
+ if (_townsPlayer)
+ _townsPlayer->saveLoadWithSerializer(s);
+
//
// Save/load the charset renderer state
//
@@ -1449,6 +1487,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 fafb6b383f..d33ece7f6a 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 80
+#define CURRENT_VER 83
/**
* 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 29f0c025d2..223e9822e2 100644
--- a/engines/scumm/script.cpp
+++ b/engines/scumm/script.cpp
@@ -708,25 +708,6 @@ void ScummEngine::writeVar(uint var, int value) {
error("Illegal varbits (w)");
}
-void ScummEngine_v5::getResultPos() {
- int a;
-
- _resultVarNumber = fetchScriptWord();
- if (_resultVarNumber & 0x2000) {
- a = fetchScriptWord();
- if (a & 0x2000) {
- _resultVarNumber += readVar(a & ~0x2000);
- } else {
- _resultVarNumber += a & 0xFFF;
- }
- _resultVarNumber &= ~0x2000;
- }
-}
-
-void ScummEngine_v5::setResult(int value) {
- writeVar(_resultVarNumber, value);
-}
-
void ScummEngine::push(int a) {
assert(_scummStackPos >= 0 && _scummStackPos < ARRAYSIZE(_vmStack));
_vmStack[_scummStackPos++] = a;
diff --git a/engines/scumm/script_v2.cpp b/engines/scumm/script_v2.cpp
index d93e2ea3e4..bc8446d16f 100644
--- a/engines/scumm/script_v2.cpp
+++ b/engines/scumm/script_v2.cpp
@@ -640,7 +640,6 @@ void ScummEngine_v2::o2_waitForActor() {
}
void ScummEngine_v2::o2_waitForMessage() {
-
if (VAR(VAR_HAVE_MSG)) {
_scriptPointer--;
o5_breakHere();
diff --git a/engines/scumm/script_v3.cpp b/engines/scumm/script_v3.cpp
index 7d2efdb9a8..176eefdeef 100644
--- a/engines/scumm/script_v3.cpp
+++ b/engines/scumm/script_v3.cpp
@@ -24,6 +24,7 @@
*/
#include "scumm/scumm_v3.h"
+#include "scumm/actor.h"
namespace Scumm {
@@ -36,6 +37,11 @@ void ScummEngine_v3::setupOpcodes() {
OPCODE(0x30, o3_setBoxFlags);
OPCODE(0xb0, o3_setBoxFlags);
}
+
+ OPCODE(0x3b, o3_waitForActor);
+ OPCODE(0xbb, o3_waitForActor);
+
+ OPCODE(0x4c, o3_waitForSentence);
}
void ScummEngine_v3::o3_setBoxFlags() {
@@ -46,4 +52,38 @@ void ScummEngine_v3::o3_setBoxFlags() {
setBoxFlags(a, b);
}
+void ScummEngine_v3::o3_waitForActor() {
+ // This opcode was a NOP in LOOM. Also, we cannot directly use
+ // o2_waitForActor because there the _scriptPointer is different (it
+ // assumes that getVar reads only a single byte, which is correct
+ // for v2 but not for v3). Of course we could copy the code here to
+ // o2_waitForActor and then merge the two, but right now I am
+ // keeping this as it is.
+ if (_game.id == GID_INDY3) {
+ const byte *oldaddr = _scriptPointer - 1;
+ Actor *a = derefActor(getVarOrDirectByte(PARAM_1), "o3_waitForActor");
+ if (a->_moving) {
+ _scriptPointer = oldaddr;
+ o5_breakHere();
+ }
+ }
+}
+
+void ScummEngine_v3::o3_waitForSentence() {
+ // FIXME/TODO: Can we merge this with o2_waitForSentence? I think
+ // the code here is actually incorrect, and the code in
+ // o2_waitForSentence correct, but somebody should check the
+ // disassembly for Indy and Loom.
+ if (_sentenceNum) {
+ if (_sentence[_sentenceNum - 1].freezeCount && !isScriptInUse(VAR(VAR_SENTENCE_SCRIPT)))
+ return;
+ } else if (!isScriptInUse(VAR(VAR_SENTENCE_SCRIPT)))
+ return;
+
+ _scriptPointer--;
+ o5_breakHere();
+}
+
+
+
} // End of namespace Scumm
diff --git a/engines/scumm/script_v4.cpp b/engines/scumm/script_v4.cpp
index 01927b02e7..b6e5834acc 100644
--- a/engines/scumm/script_v4.cpp
+++ b/engines/scumm/script_v4.cpp
@@ -60,6 +60,11 @@ void ScummEngine_v4::setupOpcodes() {
OPCODE(0x22, o4_saveLoadGame);
OPCODE(0xa2, o4_saveLoadGame);
+
+ // Disable some opcodes which are unused in v4.
+ _opcodes[0x3b].setProc(0, 0);
+ _opcodes[0x4c].setProc(0, 0);
+ _opcodes[0xbb].setProc(0, 0);
}
void ScummEngine_v4::o4_ifState() {
@@ -106,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 5c20e0dfd3..ea903fc108 100644
--- a/engines/scumm/script_v5.cpp
+++ b/engines/scumm/script_v5.cpp
@@ -29,6 +29,7 @@
#include "scumm/scumm_v3.h"
#include "scumm/scumm_v5.h"
#include "scumm/sound.h"
+#include "scumm/player_towns.h"
#include "scumm/util.h"
#include "scumm/verbs.h"
@@ -377,6 +378,25 @@ int ScummEngine_v5::getVarOrDirectWord(byte mask) {
return fetchScriptWordSigned();
}
+void ScummEngine_v5::getResultPos() {
+ int a;
+
+ _resultVarNumber = fetchScriptWord();
+ if (_resultVarNumber & 0x2000) {
+ a = fetchScriptWord();
+ if (a & 0x2000) {
+ _resultVarNumber += readVar(a & ~0x2000);
+ } else {
+ _resultVarNumber += a & 0xFFF;
+ }
+ _resultVarNumber &= ~0x2000;
+ }
+}
+
+void ScummEngine_v5::setResult(int value) {
+ writeVar(_resultVarNumber, value);
+}
+
void ScummEngine_v5::jumpRelative(bool cond) {
// We explicitly call ScummEngine::fetchScriptWord()
// to make this method work also in v0, which overloads
@@ -980,17 +1000,6 @@ void ScummEngine_v5::o5_getActorRoom() {
void ScummEngine_v5::o5_getActorScale() {
Actor *a;
- // INDY3 uses this opcode for waitForActor
- if (_game.id == GID_INDY3) {
- const byte *oldaddr = _scriptPointer - 1;
- a = derefActor(getVarOrDirectByte(PARAM_1), "o5_getActorScale (wait)");
- if (a->_moving) {
- _scriptPointer = oldaddr;
- o5_breakHere();
- }
- return;
- }
-
getResultPos();
int act = getVarOrDirectByte(PARAM_1);
a = derefActor(act, "o5_getActorScale");
@@ -1587,21 +1596,18 @@ void ScummEngine_v5::o5_resourceRoutines() {
debug(0, "o5_resourceRoutines %d not yet handled (script %d)", op, vm.slot[_currentScript].number);
break;
case 35:
- // TODO: Might be used to set CD volume in FM-TOWNS Loom
- foo = getVarOrDirectByte(PARAM_2);
- debug(0, "o5_resourceRoutines %d not yet handled (script %d)", op, vm.slot[_currentScript].number);
+ if (_townsPlayer)
+ _townsPlayer->setVolumeCD(getVarOrDirectByte(PARAM_2), resid);
break;
case 36:
- // TODO: Sets the loudness of a sound resource. Used in Indy3 and Zak.
foo = getVarOrDirectByte(PARAM_2);
bar = fetchScriptByte();
- debug(0, "o5_resourceRoutines %d not yet handled (script %d)", op, vm.slot[_currentScript].number);
+ if (_townsPlayer)
+ _townsPlayer->setSoundVolume(resid, foo, bar);
break;
case 37:
- // TODO: Sets the pitch of a sound resource (pitch = foo - center semitones.
- // "center" is at 0x32 in the sfx resource (always 0x3C in zak256, but sometimes different in Indy3).
- foo = getVarOrDirectByte(PARAM_2);
- debug(0, "o5_resourceRoutines %d not yet handled (script %d)", op, vm.slot[_currentScript].number);
+ if (_townsPlayer)
+ _townsPlayer->setSoundNote(resid, getVarOrDirectByte(PARAM_2));
break;
default:
@@ -1611,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);
@@ -1706,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 {
@@ -1972,7 +2010,7 @@ void ScummEngine_v5::o5_startMusic() {
result = _sound->getCurrentCDSound();
break;
case 0xFF:
- // TODO: Might return current CD volume in FM-TOWNS Loom. See also bug #805691.
+ result = _townsPlayer->getCurrentCdaVolume();
break;
default:
// TODO: return track length in seconds. We'll have to extend Sound and OSystem for this.
@@ -2025,19 +2063,6 @@ void ScummEngine_v5::o5_isSoundRunning() {
void ScummEngine_v5::o5_soundKludge() {
int items[16];
-
- if (_game.features & GF_SMALL_HEADER) { // Is WaitForSentence in SCUMM V3
- if (_sentenceNum) {
- if (_sentence[_sentenceNum - 1].freezeCount && !isScriptInUse(VAR(VAR_SENTENCE_SCRIPT)))
- return;
- } else if (!isScriptInUse(VAR(VAR_SENTENCE_SCRIPT)))
- return;
-
- _scriptPointer--;
- o5_breakHere();
- return;
- }
-
int num = getWordVararg(items);
_sound->soundKludge(items, num);
}
diff --git a/engines/scumm/scumm-md5.h b/engines/scumm/scumm-md5.h
index a25fac1a88..b141b2d758 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 Sat Oct 02 14:51:12 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 },
@@ -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 50901b8f9e..2f25aeefba 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"
@@ -48,6 +49,7 @@
#include "scumm/imuse_digi/dimuse.h"
#include "scumm/smush/smush_mixer.h"
#include "scumm/smush/smush_player.h"
+#include "scumm/player_towns.h"
#include "scumm/insane/insane.h"
#include "scumm/he/animation_he.h"
#include "scumm/he/intern_he.h"
@@ -146,6 +148,7 @@ ScummEngine::ScummEngine(OSystem *syst, const DetectorResult &dr)
_imuse = NULL;
_imuseDigital = NULL;
_musicEngine = NULL;
+ _townsPlayer = NULL;
_verbs = NULL;
_objs = NULL;
_sound = NULL;
@@ -255,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;
@@ -275,6 +278,9 @@ ScummEngine::ScummEngine(OSystem *syst, const DetectorResult &dr)
_hePalettes = NULL;
_hePaletteSlot = 0;
_16BitPalette = NULL;
+#ifndef DISABLE_TOWNS_DUAL_LAYER_MODE
+ _townsScreen = 0;
+#endif
_shadowPalette = NULL;
_shadowPaletteSize = 0;
memset(_currentPalette, 0, sizeof(_currentPalette));
@@ -315,6 +321,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
//
@@ -530,16 +545,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);
@@ -605,7 +625,12 @@ ScummEngine::~ScummEngine() {
free(_compositeBuf);
free(_herculesBuf);
- free(_fmtownsBuf);
+
+ free(_16BitPalette);
+
+#ifndef DISABLE_TOWNS_DUAL_LAYER_MODE
+ delete _townsScreen;
+#endif
delete _debugger;
@@ -695,6 +720,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)
@@ -1109,16 +1138,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));
}
}
@@ -1234,13 +1276,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
@@ -1318,6 +1355,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)) {
@@ -1510,8 +1565,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);
@@ -1757,6 +1810,10 @@ void ScummEngine::setupMusic(int midi) {
_musicEngine = new Player_V2CMS(this, _mixer);
} 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_v1(this, _mixer);
+ if (!_townsPlayer->init())
+ error("Failed to initialize FM-Towns audio driver");
} else if (_game.version >= 3 && _game.heversion <= 62) {
MidiDriver *nativeMidiDriver = 0;
MidiDriver *adlibMidiDriver = 0;
@@ -1771,7 +1828,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,
@@ -1780,17 +1846,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);*/
}
}
}
@@ -1802,10 +1868,23 @@ void ScummEngine::syncSoundSettings() {
int soundVolumeSfx = ConfMan.getInt("sfx_volume");
int soundVolumeSpeech = ConfMan.getInt("speech_volume");
+ bool mute = false;
+
+ if (ConfMan.hasKey("mute")) {
+ mute = ConfMan.getBool("mute");
+
+ if (mute)
+ soundVolumeMusic = soundVolumeSfx = soundVolumeSpeech = 0;
+ }
+
if (_musicEngine) {
_musicEngine->setMusicVolume(soundVolumeMusic);
}
+ if (_townsPlayer) {
+ _townsPlayer->setSfxVolume(soundVolumeSfx);
+ }
+
_mixer->setVolumeForSoundType(Audio::Mixer::kSFXSoundType, soundVolumeSfx);
_mixer->setVolumeForSoundType(Audio::Mixer::kMusicSoundType, soundVolumeMusic);
_mixer->setVolumeForSoundType(Audio::Mixer::kSpeechSoundType, soundVolumeSpeech);
@@ -1904,6 +1983,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;
@@ -1911,6 +1996,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;
@@ -2025,6 +2116,10 @@ load_game:
goto load_game;
}
+#ifndef DISABLE_TOWNS_DUAL_LAYER_MODE
+ towns_processPalCycleField();
+#endif
+
if (_currentRoom == 0) {
if (_game.version > 3)
CHARSET_1();
@@ -2094,11 +2189,18 @@ 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.
// Fixes bug #1328120: "MANIACNES: Screen width incorrect, camera halts sometimes"
- VAR(VAR_CAMERA_POS_X) = (camera._cur.x >> V12_X_SHIFT) + 2;
+ // But do not do it when only scrolling right to left, since otherwise Ed will not show
+ // up on the doorbell (Bug #3039004)
+ 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;
} else {
@@ -2143,14 +2245,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;
@@ -2162,7 +2264,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);
@@ -2405,6 +2507,12 @@ 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
diff --git a/engines/scumm/scumm.h b/engines/scumm/scumm.h
index 8c3df21238..aef5cfbec7 100644
--- a/engines/scumm/scumm.h
+++ b/engines/scumm/scumm.h
@@ -43,6 +43,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;
}
@@ -70,6 +81,7 @@ class CharsetRenderer;
class IMuse;
class IMuseDigital;
class MusicEngine;
+class Player_Towns;
class ScummEngine;
class ScummDebugger;
class Serializer;
@@ -426,6 +438,7 @@ public:
IMuse *_imuse;
IMuseDigital *_imuseDigital;
MusicEngine *_musicEngine;
+ Player_Towns *_townsPlayer;
Sound *_sound;
VerbSlot *_verbs;
@@ -970,6 +983,7 @@ public:
Common::RenderMode _renderMode;
uint8 _bytesPerPixel;
+ uint8 _bytesPerPixelOutput;
protected:
ColorCycle _colorCycle[16]; // Palette cycles
@@ -1042,6 +1056,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);
@@ -1075,7 +1090,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);
@@ -1219,7 +1234,7 @@ protected:
void restoreCharsetBg();
void clearCharsetMask();
void clearTextSurface();
-
+
virtual void initCharset(int charset);
virtual void printString(int m, const byte *msg);
@@ -1395,6 +1410,38 @@ 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];
+
+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/scumm_v3.h b/engines/scumm/scumm_v3.h
index 767069d1bc..abe75cd64d 100644
--- a/engines/scumm/scumm_v3.h
+++ b/engines/scumm/scumm_v3.h
@@ -50,6 +50,8 @@ protected:
/* Version 3 script opcodes */
void o3_setBoxFlags();
+ void o3_waitForActor();
+ void o3_waitForSentence();
};
/**
diff --git a/engines/scumm/sound.cpp b/engines/scumm/sound.cpp
index 99ab1b23b7..af7bc5f295 100644
--- a/engines/scumm/sound.cpp
+++ b/engines/scumm/sound.cpp
@@ -31,6 +31,7 @@
#include "scumm/file.h"
#include "scumm/imuse/imuse.h"
#include "scumm/imuse_digi/dimuse.h"
+#include "scumm/player_towns.h"
#include "scumm/scumm.h"
#include "scumm/sound.h"
#include "scumm/util.h"
@@ -76,6 +77,7 @@ Sound::Sound(ScummEngine *parent, Audio::Mixer *mixer)
_curSoundPos(0),
_currentCDSound(0),
_currentMusic(0),
+ _lastSound(0),
_soundsPaused(false),
_sfxMode(0) {
@@ -93,6 +95,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)
@@ -149,9 +152,10 @@ void Sound::processSoundQueues() {
data[0] >> 8, data[0] & 0xFF,
data[1], data[2], data[3], data[4], data[5], data[6], data[7]);
- if (_vm->_imuse) {
+ if (_vm->_townsPlayer)
+ _vm->VAR(_vm->VAR_SOUNDRESULT) = (short)_vm->_townsPlayer->doCommand(num, data);
+ else if (_vm->_imuse)
_vm->VAR(_vm->VAR_SOUNDRESULT) = (short)_vm->_imuse->doCommand(num, data);
- }
}
}
_soundQuePos = 0;
@@ -240,7 +244,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.
@@ -309,91 +313,30 @@ 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 && _vm->_game.version == 3) || READ_BE_UINT32(ptr) == MKID_BE('SOUN') || READ_BE_UINT32(ptr) == MKID_BE('TOWS')) {
-
- bool tows = READ_BE_UINT32(ptr) == MKID_BE('TOWS');
- if (_vm->_game.version == 3) {
- size = READ_LE_UINT32(ptr);
- } else {
- size = READ_BE_UINT32(ptr + 4) - 2;
- if (tows)
- size += 8;
+ else if (_vm->_game.platform != Common::kPlatformFMTowns && READ_BE_UINT32(ptr) == MKID_BE('SOUN')) {
+ if (_vm->_game.version != 3)
ptr += 2;
- }
- rate = 11025;
int type = *(ptr + 0x0D);
- int numInstruments;
-
- if (tows)
- type = 0;
- switch (type) {
- case 0: // Sound effect
- numInstruments = *(ptr + 0x14);
- if (tows)
- numInstruments = 1;
- ptr += 0x16;
- size -= 0x16;
-
- while (numInstruments--) {
- int waveSize = READ_LE_UINT32(ptr + 0x0C);
- int loopStart = READ_LE_UINT32(ptr + 0x10) * 2;
- int loopEnd = READ_LE_UINT32(ptr + 0x14) - 1;
- rate = READ_LE_UINT32(ptr + 0x18) * 1000 / 0x62;
- ptr += 0x20;
- size -= 0x20;
- if (size < waveSize) {
- warning("Wrong wave size in sound #%i: %i", soundID, waveSize);
- waveSize = size;
- }
- sound = (byte *)malloc(waveSize);
- for (int x = 0; x < waveSize; x++) {
- byte b = *ptr++;
- if (b < 0x80)
- sound[x] = 0x7F - b;
- else
- sound[x] = b;
- }
- size -= waveSize;
-
- if (loopEnd > 0) {
- Audio::SeekableAudioStream *s = Audio::makeRawStream(sound, waveSize, rate, Audio::FLAG_UNSIGNED);
- stream = new Audio::SubLoopingAudioStream(s, 0, Audio::Timestamp(0, loopStart, rate), Audio::Timestamp(0, loopEnd, rate));
- } else {
- stream = Audio::makeRawStream(sound, waveSize, rate, Audio::FLAG_UNSIGNED);
- }
- _mixer->playStream(Audio::Mixer::kSFXSoundType, NULL, stream, soundID, 255, 0);
- }
- break;
- case 1:
- // Music (Euphony format)
- if (_vm->_musicEngine)
- _vm->_musicEngine->startSound(soundID);
- break;
- case 2: // CD track resource
+ if (type == 2) {
+ // CD track resource
ptr += 0x16;
-
- if (soundID == _currentCDSound && pollCD() == 1) {
+ if (soundID == _currentCDSound && pollCD() == 1)
return;
- }
-
- {
- int track = ptr[0];
- int loops = ptr[1];
- int start = (ptr[2] * 60 + ptr[3]) * 75 + ptr[4];
- int end = (ptr[5] * 60 + ptr[6]) * 75 + ptr[7];
- playCDTrack(track, loops == 0xff ? -1 : loops, start, end <= start ? 0 : end - start);
- }
+ int track = ptr[0];
+ int loops = ptr[1];
+ int start = (ptr[2] * 60 + ptr[3]) * 75 + ptr[4];
+ int end = (ptr[5] * 60 + ptr[6]) * 75 + ptr[7];
+ playCDTrack(track, loops == 0xff ? -1 : loops, start, end <= start ? 0 : end - start);
_currentCDSound = soundID;
- break;
- default:
+ } else {
// All other sound types are ignored
- break;
+ warning("Scumm::Sound::playSound: encountered audio resoure with chunk type 'SOUN' and sound type %d", type);
}
}
else if ((_vm->_game.id == GID_LOOM) && (_vm->_game.platform == Common::kPlatformMacintosh)) {
@@ -479,6 +422,9 @@ void Sound::playSound(int soundID) {
if (_vm->_musicEngine) {
_vm->_musicEngine->startSound(soundID);
}
+
+ if (_vm->_townsPlayer)
+ _currentCDSound = _vm->_townsPlayer->getCurrentCdaSound();
}
}
@@ -844,6 +790,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..5eb2b9d159 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();
}
}
@@ -660,6 +670,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());
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 95fba8ee7e..77181c0b55 100644
--- a/engines/scumm/verbs.cpp
+++ b/engines/scumm/verbs.cpp
@@ -651,6 +651,50 @@ void ScummEngine_v2::checkExecVerbs() {
}
}
+ // Simulate inventory picking and scrolling keys
+ int object = -1;
+
+ switch (_mouseAndKeyboardStat) {
+ case 'u': // arrow up
+ if (_inventoryOffset >= 2) {
+ _inventoryOffset -= 2;
+ redrawV2Inventory();
+ }
+ return;
+ case 'j': // arrow down
+ if (_inventoryOffset + 4 < getInventoryCount(_scummVars[VAR_EGO])) {
+ _inventoryOffset += 2;
+ redrawV2Inventory();
+ }
+ return;
+ case 'i': // object
+ object = 0;
+ break;
+ case 'o':
+ object = 1;
+ break;
+ case 'k':
+ object = 2;
+ break;
+ case 'l':
+ object = 3;
+ break;
+ }
+
+ if (object != -1) {
+ object = findInventory(_scummVars[VAR_EGO], object + 1 + _inventoryOffset);
+
+ if (object > 0) {
+ if (_game.version == 0) {
+ _activeInventory = object;
+
+ } else {
+ runInputScript(kInventoryClickArea, object, 0);
+ }
+ }
+ return;
+ }
+
// Generic keyboard input
runInputScript(kKeyClickArea, _mouseAndKeyboardStat, 1);
} else if (_mouseAndKeyboardStat & MBS_MOUSE_MASK) {
@@ -701,30 +745,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");
@@ -911,7 +942,7 @@ bool ScummEngine_v0::verbExec() {
return true;
}
_v0ObjectInInventory = true;
- VAR(5) = _activeActor;
+ VAR(VAR_ACTIVE_ACTOR) = _activeActor;
runObject(_activeInventory , 3);
_v0ObjectInInventory = false;
@@ -1420,9 +1451,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;
}
}