diff options
Diffstat (limited to 'engines/scumm')
| -rw-r--r-- | engines/scumm/actor.cpp | 5 | ||||
| -rw-r--r-- | engines/scumm/charset.cpp | 57 | ||||
| -rw-r--r-- | engines/scumm/charset.h | 1 | ||||
| -rw-r--r-- | engines/scumm/cursor.cpp | 23 | ||||
| -rw-r--r-- | engines/scumm/detection_tables.h | 7 | ||||
| -rw-r--r-- | engines/scumm/gfx.cpp | 233 | ||||
| -rw-r--r-- | engines/scumm/gfx.h | 61 | ||||
| -rw-r--r-- | engines/scumm/gfx_towns.cpp | 508 | ||||
| -rw-r--r-- | engines/scumm/module.mk | 1 | ||||
| -rw-r--r-- | engines/scumm/palette.cpp | 53 | ||||
| -rw-r--r-- | engines/scumm/room.cpp | 3 | ||||
| -rw-r--r-- | engines/scumm/saveload.cpp | 40 | ||||
| -rw-r--r-- | engines/scumm/saveload.h | 2 | ||||
| -rw-r--r-- | engines/scumm/script_v4.cpp | 57 | ||||
| -rw-r--r-- | engines/scumm/script_v5.cpp | 56 | ||||
| -rw-r--r-- | engines/scumm/scumm-md5.h | 10 | ||||
| -rw-r--r-- | engines/scumm/scumm.cpp | 66 | ||||
| -rw-r--r-- | engines/scumm/scumm.h | 36 | ||||
| -rw-r--r-- | engines/scumm/string.cpp | 11 | ||||
| -rw-r--r-- | engines/scumm/verbs.cpp | 3 | 
20 files changed, 1005 insertions, 228 deletions
| diff --git a/engines/scumm/actor.cpp b/engines/scumm/actor.cpp index a06939dc51..7c1f401e67 100644 --- a/engines/scumm/actor.cpp +++ b/engines/scumm/actor.cpp @@ -2159,7 +2159,10 @@ void ScummEngine::stopTalk() {  		((ScummEngine_v7 *)this)->clearSubtitleQueue();  #endif  	} else { -		restoreCharsetBg(); +		if (_game.platform == Common::kPlatformFMTowns) +			towns_restoreCharsetBg(); +		else +			restoreCharsetBg();  	}  } diff --git a/engines/scumm/charset.cpp b/engines/scumm/charset.cpp index fa4804ce7d..38b85f6fbd 100644 --- a/engines/scumm/charset.cpp +++ b/engines/scumm/charset.cpp @@ -655,6 +655,12 @@ void CharsetRendererV3::setColor(byte color) {  	} else  		useShadow = false; +	if (_vm->_game.platform == Common::kPlatformFMTowns) { +		_color = (_color & 0x0f) | ((_color & 0x0f) << 4); +		if (_color == 0) +			_color = 0x88; +	} +  	enableShadow(useShadow);  	translateColor(); @@ -672,7 +678,7 @@ void CharsetRendererPCE::setColor(byte color) {  void CharsetRendererCommon::enableShadow(bool enable) {  	if (enable) {  		if (_vm->_game.platform == Common::kPlatformFMTowns) { -			_shadowColor = 8; +			_shadowColor = _vm->_game.version == 5 ? _vm->_townsCharsetColorMap[0] : 0x88;  			_shadowMode = kFMTOWNSShadowMode;  		} else {  			_shadowColor = 0; @@ -683,7 +689,6 @@ void CharsetRendererCommon::enableShadow(bool enable) {  	}  } -  void CharsetRendererV3::printChar(int chr, bool ignoreCharsetMask) {  	// WORKAROUND for bug #1509509: Indy3 Mac does not show black  	// characters (such as in the grail diary) if ignoreCharsetMask @@ -724,8 +729,8 @@ void CharsetRendererV3::printChar(int chr, bool ignoreCharsetMask) {  	origHeight = height;  	if (_shadowMode != kNoShadowMode) { -		width++; -		height++; +		width += _vm->_textSurfaceMultiplier; +		height += _vm->_textSurfaceMultiplier;  	}  	if (_firstChar) { @@ -744,7 +749,8 @@ void CharsetRendererV3::printChar(int chr, bool ignoreCharsetMask) {  		_hasMask = true;  		_textScreenID = vs->number;  	} -	if ((ignoreCharsetMask || !vs->hasTwoBuffers) && !(_vm->_useCJKMode && _vm->_textSurfaceMultiplier == 2)) { + +	if ((_vm->_game.platform != Common::kPlatformFMTowns || (_vm->_game.id == GID_LOOM && !is2byte)) && (ignoreCharsetMask || !vs->hasTwoBuffers)) {  		dst = vs->getPixels(_left, drawTop);  		drawBits1(*vs, dst, charPtr, drawTop, origWidth, origHeight, vs->bytesPerPixel);  	} else { @@ -801,6 +807,27 @@ void CharsetRenderer::translateColor() {  	}  } +void CharsetRenderer::processTownsCharsetColors(uint8 bytesPerPixel) { +	if (_vm->_game.platform == Common::kPlatformFMTowns) { +		for (int i = 0; i < (1 << bytesPerPixel); i++) { +			uint8 c = _vm->_charsetColorMap[i]; +						 +			if (c > 16) { +				uint8 t = (_vm->_currentPalette[c * 3] < 32) ? 4 : 12; +				t |= ((_vm->_currentPalette[c * 3 + 1] < 32) ? 2 : 10); +				t |= ((_vm->_currentPalette[c * 3 + 1] < 32) ? 1 : 9); +				c = t; +			} +			 +			if (c == 0) +				c = _vm->_townsOverrideShadowColor; +			 +			c = ((c & 0x0f) << 4) | (c & 0x0f); +			_vm->_townsCharsetColorMap[i] = c; +		} +	} +} +  void CharsetRenderer::saveLoadWithSerializer(Serializer *ser) {  	static const SaveLoadEntry charsetRendererEntries[] = {  		MKLINE_OLD(CharsetRenderer, _curId, sleByte, VER(73), VER(73)), @@ -836,6 +863,8 @@ void CharsetRendererClassic::printChar(int chr, bool ignoreCharsetMask) {  	_vm->_charsetColorMap[1] = _color; +	processTownsCharsetColors(_bytesPerPixel); +  	if (is2byte) {  		enableShadow(true);  		charPtr = _vm->get2byteCharPtr(chr); @@ -851,7 +880,7 @@ void CharsetRendererClassic::printChar(int chr, bool ignoreCharsetMask) {  		width = charPtr[0];  		height = charPtr[1]; - +	  		if (_disableOffsX) {  			offsX = 0;  		} else { @@ -866,8 +895,8 @@ void CharsetRendererClassic::printChar(int chr, bool ignoreCharsetMask) {  	origHeight = height;  	if (_shadowMode != kNoShadowMode) { -		width++; -		height++; +		width += _vm->_textSurfaceMultiplier; +		height += _vm->_textSurfaceMultiplier;  	}  	if (_firstChar) {  		_str.left = 0; @@ -905,7 +934,9 @@ void CharsetRendererClassic::printChar(int chr, bool ignoreCharsetMask) {  	_vm->markRectAsDirty(vs->number, _left, _left + width, drawTop, drawTop + height); -	if (!ignoreCharsetMask) { +	// This check for kPlatformFMTowns and kMainVirtScreen is at least required for the chat with +	// the navigator's head in front of the ghost ship in Monkey Island 1 +	if (!ignoreCharsetMask || (_vm->_game.platform == Common::kPlatformFMTowns && vs->number == kMainVirtScreen)) {  		_hasMask = true;  		_textScreenID = vs->number;  	} @@ -961,7 +992,7 @@ void CharsetRendererClassic::printCharIntern(bool is2byte, const byte *charPtr,  	} else {  		Graphics::Surface dstSurface;  		Graphics::Surface backSurface; -		if ((ignoreCharsetMask || !vs->hasTwoBuffers) && !(_vm->_useCJKMode && _vm->_textSurfaceMultiplier == 2)) { +		if (_vm->_game.platform != Common::kPlatformFMTowns && (ignoreCharsetMask || !vs->hasTwoBuffers) && !(_vm->_useCJKMode && _vm->_textSurfaceMultiplier == 2)) {  			dstSurface = *vs;  			dstPtr = vs->getPixels(_left, drawTop);  		} else { @@ -1064,13 +1095,14 @@ void CharsetRendererClassic::drawBitsN(const Graphics::Surface &s, byte *dst, co  	assert(bpp == 1 || bpp == 2 || bpp == 4 || bpp == 8);  	bits = *src++;  	numbits = 8; +	byte *cmap = (_vm->_game.platform == Common::kPlatformFMTowns) ? _vm->_townsCharsetColorMap : _vm->_charsetColorMap;  	for (y = 0; y < height && y + drawTop < s.h; y++) {  		for (x = 0; x < width; x++) {  			color = (bits >> (8 - bpp)) & 0xFF;  			if (color && y + drawTop >= 0) { -				*dst = _vm->_charsetColorMap[color]; +				*dst = cmap[color];  			}  			dst++;  			bits <<= bpp; @@ -1087,6 +1119,7 @@ void CharsetRendererClassic::drawBitsN(const Graphics::Surface &s, byte *dst, co  void CharsetRendererCommon::drawBits1(const Graphics::Surface &s, byte *dst, const byte *src, int drawTop, int width, int height, uint8 bitDepth) {  	int y, x;  	byte bits = 0; +	uint8 col = (_vm->_game.platform == Common::kPlatformFMTowns && _vm->_game.version == 5) ? _vm->_townsCharsetColorMap[1] : _color;  	for (y = 0; y < height && y + drawTop < s.h; y++) {  		for (x = 0; x < width; x++) { @@ -1108,7 +1141,7 @@ void CharsetRendererCommon::drawBits1(const Graphics::Surface &s, byte *dst, con  						if (_shadowMode != kFMTOWNSShadowMode)  							*(dst + s.pitch + 1) = _shadowColor;  					} -					*dst = _color; +					*dst = col;  				}  			}  			dst += bitDepth; diff --git a/engines/scumm/charset.h b/engines/scumm/charset.h index dca254669b..e072f3ed5f 100644 --- a/engines/scumm/charset.h +++ b/engines/scumm/charset.h @@ -79,6 +79,7 @@ public:  	int getStringWidth(int a, const byte *str);  	void addLinebreaks(int a, byte *str, int pos, int maxwidth);  	void translateColor(); +	void processTownsCharsetColors(uint8 bytesPerPixel);  	virtual void setCurID(int32 id) = 0;  	int getCurID() { return _curId; } diff --git a/engines/scumm/cursor.cpp b/engines/scumm/cursor.cpp index b1f8f2ae2b..63a7b1c40e 100644 --- a/engines/scumm/cursor.cpp +++ b/engines/scumm/cursor.cpp @@ -554,11 +554,14 @@ void ScummEngine_v5::setBuiltinCursor(int idx) {  	uint16 color;  	const uint16 *src = _cursorImages[_currentCursor]; -	if (_bytesPerPixel == 2) { +	if (_bytesPerPixelOutput == 2) {  		if (_game.id == GID_LOOM && _game.platform == Common::kPlatformPCEngine) {  			byte r, g, b;  			colorPCEToRGB(default_pce_cursor_colors[idx], &r, &g, &b);  			color = get16BitColor(r, g, b); +		} else if (_game.platform == Common::kPlatformFMTowns) { +			byte *palEntry = &_textPalette[default_cursor_colors[idx] * 3]; +			color = get16BitColor(palEntry[0], palEntry[1], palEntry[2]);  		} else {  			color = _16BitPalette[default_cursor_colors[idx]];  		} @@ -570,18 +573,20 @@ void ScummEngine_v5::setBuiltinCursor(int idx) {  		memset(_grabbedCursor, 0xFF, sizeof(_grabbedCursor));  	} -	_cursor.hotspotX = _cursorHotspots[2 * _currentCursor]; -	_cursor.hotspotY = _cursorHotspots[2 * _currentCursor + 1]; -	_cursor.width = 16; -	_cursor.height = 16; +	_cursor.hotspotX = _cursorHotspots[2 * _currentCursor] * _textSurfaceMultiplier; +	_cursor.hotspotY = _cursorHotspots[2 * _currentCursor + 1] * _textSurfaceMultiplier; +	_cursor.width = 16 * _textSurfaceMultiplier; +	_cursor.height = 16 * _textSurfaceMultiplier; + +	int scl = (_game.platform == Common::kPlatformFMTowns) ? (_bytesPerPixelOutput * _textSurfaceMultiplier) : 1;  	for (i = 0; i < 16; i++) {  		for (j = 0; j < 16; j++) {  			if (src[i] & (1 << j)) { -				if (_bytesPerPixel == 2) -					WRITE_UINT16(_grabbedCursor + 32 * i + (15 - j) * 2, color); -				else -					_grabbedCursor[16 * i + 15 - j] = color; +				byte *dst1 = _grabbedCursor + 16 * scl * i * _textSurfaceMultiplier + (15 - j) * scl; +				byte *dst2 = (_textSurfaceMultiplier == 2) ? dst1 + 16 * scl : dst1; +				for (int b = 0; b < scl; b++) +					*dst1++ = *dst2++ = color;  			}  		}  	} diff --git a/engines/scumm/detection_tables.h b/engines/scumm/detection_tables.h index 98fab9468a..4428185774 100644 --- a/engines/scumm/detection_tables.h +++ b/engines/scumm/detection_tables.h @@ -220,6 +220,7 @@ static const GameSettings gameVariantsTable[] = {  	{"zak", "V2",       "v2", GID_ZAK, 2, 0, MDT_PCSPK | MDT_PCJR, 0, UNK, GUIO_NOSPEECH | GUIO_NOMIDI},  	{"zak", "FM-TOWNS",    0, GID_ZAK, 3, 0, MDT_TOWNS, GF_OLD256 | GF_AUDIOTRACKS, Common::kPlatformFMTowns, GUIO_NOSPEECH | GUIO_NOMIDI | GUIO_MIDITOWNS}, +  	{"indy3", "EGA",      "ega", GID_INDY3, 3, 0, MDT_PCSPK | MDT_PCJR | MDT_CMS | MDT_ADLIB, 0, UNK, GUIO_NOSPEECH | GUIO_NOMIDI},  	{"indy3", "No AdLib", "ega", GID_INDY3, 3, 0, MDT_PCSPK | MDT_PCJR,             0, UNK, GUIO_NOSPEECH | GUIO_NOMIDI},  	{"indy3", "VGA",      "vga", GID_INDY3, 3, 0, MDT_PCSPK | MDT_PCJR | MDT_ADLIB, GF_OLD256 | GF_FEW_LOCALS,                  Common::kPlatformPC, GUIO_NOSPEECH | GUIO_NOMIDI}, @@ -243,10 +244,12 @@ static const GameSettings gameVariantsTable[] = {  	{"monkey", "FM-TOWNS",     0, GID_MONKEY,     5, 0, MDT_TOWNS,                        GF_AUDIOTRACKS, Common::kPlatformFMTowns, GUIO_NOSPEECH | GUIO_NOMIDI | GUIO_MIDITOWNS},  	{"monkey", "SEGA",         0, GID_MONKEY,     5, 0, MDT_NONE,                         GF_AUDIOTRACKS, Common::kPlatformSegaCD, GUIO_NOSPEECH | GUIO_NOMIDI}, -	{"monkey2",  0, 0, GID_MONKEY2,  5, 0, MDT_ADLIB | MDT_MIDI | MDT_PREFER_MT32, 0, UNK, GUIO_NOSPEECH}, +	{"monkey2",  "", 0, GID_MONKEY2,  5, 0, MDT_ADLIB | MDT_MIDI | MDT_PREFER_MT32, 0, UNK, GUIO_NOSPEECH}, +	{"monkey2", "FM-TOWNS", 0, GID_MONKEY2,  5, 0, MDT_TOWNS | MDT_ADLIB | MDT_MIDI | MDT_PREFER_MT32, 0, Common::kPlatformFMTowns, GUIO_NOSPEECH}, -	{"atlantis", "" , 0, GID_INDY4,    5, 0, MDT_ADLIB | MDT_MIDI | MDT_PREFER_MT32, 0, UNK, GUIO_NONE}, +	{"atlantis", "", 0, GID_INDY4,    5, 0, MDT_ADLIB | MDT_MIDI | MDT_PREFER_MT32, 0, UNK, GUIO_NONE},  	{"atlantis", "Floppy", 0, GID_INDY4,    5, 0, MDT_ADLIB | MDT_MIDI | MDT_PREFER_MT32, 0, UNK, GUIO_NOSPEECH}, +	{"atlantis", "FM-TOWNS", 0, GID_INDY4,    5, 0, MDT_TOWNS | MDT_ADLIB | MDT_MIDI | MDT_PREFER_MT32, 0, Common::kPlatformFMTowns, GUIO_NONE},  	{"tentacle", "", 0, GID_TENTACLE, 6, 0, MDT_ADLIB | MDT_MIDI | MDT_PREFER_GM, GF_USE_KEY, UNK, GUIO_NONE},  	{"tentacle", "Floppy", 0, GID_TENTACLE, 6, 0, MDT_ADLIB | MDT_MIDI | MDT_PREFER_GM, GF_USE_KEY, UNK, GUIO_NOSPEECH}, diff --git a/engines/scumm/gfx.cpp b/engines/scumm/gfx.cpp index 3b8d9c296a..2ee54337b8 100644 --- a/engines/scumm/gfx.cpp +++ b/engines/scumm/gfx.cpp @@ -51,7 +51,6 @@ static void copy8Col(byte *dst, int dstPitch, const byte *src, int height, uint8  static void clear8Col(byte *dst, int dstPitch, int height, uint8 bitDepth);  static void ditherHerc(byte *src, byte *hercbuf, int srcPitch, int *x, int *y, int *width, int *height); -static void scale2x(byte *dst, int dstPitch, const byte *src, int srcPitch, int w, int h);  struct StripTable {  	int offsets[160]; @@ -323,6 +322,16 @@ void ScummEngine::initScreens(int b, int h) {  		_res->nukeResource(rtBuffer, i + 5);  	} +	if (_townsScreen) { +		if (!_townsClearLayerFlag && (h - b != _virtscr[kMainVirtScreen].h)) +			_townsScreen->clearLayer(0); + +		if (_game.id == GID_MONKEY2 || _game.id == GID_INDY4) { +			_textSurface.fillRect(Common::Rect(0, 0, _textSurface.w * _textSurfaceMultiplier, _textSurface.h * _textSurfaceMultiplier), 0); +			_townsScreen->clearLayer(1); +		} +	} +  	if (!getResourceAddress(rtBuffer, 4)) {  		// Since the size of screen 3 is fixed, there is no need to reallocate  		// it if its size changed. @@ -611,16 +620,7 @@ void ScummEngine::drawStripToScreen(VirtScreen *vs, int x, int width, int top, i  	int m = _textSurfaceMultiplier;  	int vsPitch;  	int pitch = vs->pitch; - -	if (_useCJKMode && _textSurfaceMultiplier == 2) { -		scale2x(_fmtownsBuf, _screenWidth * m, (const byte *)src, vs->pitch,  width, height); -		src = _fmtownsBuf; - -		vsPitch = _screenWidth * m - width * m; - -	} else { -		vsPitch = vs->pitch - width * vs->bytesPerPixel; -	} +	vsPitch = vs->pitch - width * vs->bytesPerPixel;  	if (_game.version < 7) { @@ -643,7 +643,10 @@ void ScummEngine::drawStripToScreen(VirtScreen *vs, int x, int width, int top, i  #ifdef USE_ARM_GFX_ASM  		asmDrawStripToScreen(height, width, text, src, _compositeBuf, vs->pitch, width, _textSurface.pitch);  #else -		if (_bytesPerPixel == 2) { +		if (_game.platform == Common::kPlatformFMTowns) { +			towns_drawStripToScreen(vs, x, y, x, top, width, height); +			return;	 +		} else if (_bytesPerPixelOutput == 2) {  			const byte *srcPtr = (const byte *)src;  			const byte *textPtr = (byte *)_textSurface.getBasePtr(x * m, y * m);  			byte *dstPtr = _compositeBuf; @@ -824,28 +827,6 @@ void ditherHerc(byte *src, byte *hercbuf, int srcPitch, int *x, int *y, int *wid  	*height = dsty - *y;  } -void scale2x(byte *dst, int dstPitch, const byte *src, int srcPitch, int w, int h) { -	/* dst and dstPitch should both be even. So the use of (void *) in -	 * the following casts to avoid the unnecessary warning is valid. */ -	uint16 *dstL1 = (uint16 *)(void *)dst; -	uint16 *dstL2 = (uint16 *)(void *)(dst + dstPitch); - -	const int dstAdd = dstPitch - w; -	const int srcAdd = srcPitch - w; - -	while (h--) { -		for (int x = 0; x < w; ++x) { -			uint16 col = *src++; -			col |= col << 8; -			*dstL1++ = col; -			*dstL2++ = col; -		} -		dstL1 += dstAdd; dstL2 += dstAdd; -		src += srcAdd; -	} -} - -  #pragma mark -  #pragma mark --- Background buffers & charset mask ---  #pragma mark - @@ -1017,7 +998,7 @@ void ScummEngine::restoreBackground(Common::Rect rect, byte backColor) {  	VirtScreen *vs;  	byte *screenBuf; -	if (rect.top < 0) + 	if (rect.top < 0)  		rect.top = 0;  	if (rect.left >= rect.right || rect.top >= rect.bottom)  		return; @@ -1028,6 +1009,9 @@ void ScummEngine::restoreBackground(Common::Rect rect, byte backColor) {  	if (rect.left > vs->w)  		return; +	if (_game.platform == Common::kPlatformFMTowns && _game.id == GID_MONKEY && vs->number == kVerbVirtScreen && rect.bottom <= 154) +		rect.right = 320; +	  	// Convert 'rect' to local (virtual screen) coordinates  	rect.top -= vs->topline;  	rect.bottom -= vs->topline; @@ -1047,10 +1031,21 @@ void ScummEngine::restoreBackground(Common::Rect rect, byte backColor) {  	if (vs->hasTwoBuffers && _currentRoom != 0 && isLightOn()) {  		blit(screenBuf, vs->pitch, vs->getBackPixels(rect.left, rect.top), vs->pitch, width, height, vs->bytesPerPixel);  		if (vs->number == kMainVirtScreen && _charset->_hasMask) { -			byte *mask = (byte *)_textSurface.getBasePtr(rect.left, rect.top - _screenTop); -			fill(mask, _textSurface.pitch, CHARSET_MASK_TRANSPARENCY, width, height, _textSurface.bytesPerPixel); +			if (_game.platform == Common::kPlatformFMTowns) { +				byte *mask = (byte *)_textSurface.getBasePtr(rect.left * _textSurfaceMultiplier, (rect.top + vs->topline) * _textSurfaceMultiplier); +				fill(mask, _textSurface.pitch, 0, width * _textSurfaceMultiplier, height * _textSurfaceMultiplier, _textSurface.bytesPerPixel); +			} else { +				byte *mask = (byte *)_textSurface.getBasePtr(rect.left, rect.top - _screenTop); +				fill(mask, _textSurface.pitch, CHARSET_MASK_TRANSPARENCY, width * _textSurfaceMultiplier, height * _textSurfaceMultiplier, _textSurface.bytesPerPixel); +			}  		}  	} else { +		if (_game.platform == Common::kPlatformFMTowns) { +			backColor |= (backColor << 4); +			byte *mask = (byte *)_textSurface.getBasePtr(rect.left * _textSurfaceMultiplier, (rect.top + vs->topline) * _textSurfaceMultiplier); +			fill(mask, _textSurface.pitch, backColor, width * _textSurfaceMultiplier, height * _textSurfaceMultiplier, _textSurface.bytesPerPixel); +		} +			  		if (_game.features & GF_16BIT_COLOR)  			fill(screenBuf, vs->pitch, _16BitPalette[backColor], width, height, vs->bytesPerPixel);  		else @@ -1102,7 +1097,10 @@ void ScummEngine::clearCharsetMask() {  }  void ScummEngine::clearTextSurface() { -	fill((byte*)_textSurface.pixels,  _textSurface.pitch,  CHARSET_MASK_TRANSPARENCY,  _textSurface.w, _textSurface.h, _textSurface.bytesPerPixel); +	if (_townsScreen) +		_townsScreen->fillLayerRect(1, 0, 0, _textSurface.w, _textSurface.h, 0); + +	fill((byte*)_textSurface.pixels,  _textSurface.pitch,  _game.platform == Common::kPlatformFMTowns ? 0 : CHARSET_MASK_TRANSPARENCY,  _textSurface.w, _textSurface.h, _textSurface.bytesPerPixel);  }  byte *ScummEngine::getMaskBuffer(int x, int y, int z) { @@ -1256,13 +1254,25 @@ void ScummEngine::drawBox(int x, int y, int x2, int y2, int color) {  	backbuff = vs->getPixels(x, y);  	bgbuff = vs->getBackPixels(x, y); -	if (color == -1) { -		if (vs->number != kMainVirtScreen) -			error("can only copy bg to main window"); -		blit(backbuff, vs->pitch, bgbuff, vs->pitch, width, height, vs->bytesPerPixel); -		if (_charset->_hasMask) { -			byte *mask = (byte *)_textSurface.getBasePtr(x * _textSurfaceMultiplier, (y - _screenTop) * _textSurfaceMultiplier); -			fill(mask, _textSurface.pitch, CHARSET_MASK_TRANSPARENCY, width * _textSurfaceMultiplier, height * _textSurfaceMultiplier, _textSurface.bytesPerPixel); +	// A check for -1 might be wrong in all cases since o5_drawBox() in its current form +	// is definitely not capable of passing a parameter of -1 (color range is 0 - 255). +	// Just to make sure I don't break anything I restrict the code change to FM-Towns +	// version 5 games where this change is necessary to fix certain long standing bugs. +	if (color == -1 || (color >= 254 && _game.platform == Common::kPlatformFMTowns && (_game.id == GID_MONKEY2 || _game.id == GID_INDY4))) { +		if (_game.platform == Common::kPlatformFMTowns) { +			if (color == 254) { +				color = color; +				towns_setupPalCycleField(x, y, x2, y2); +			} +		} else { +			if (vs->number != kMainVirtScreen) +				error("can only copy bg to main window"); + +			blit(backbuff, vs->pitch, bgbuff, vs->pitch, width, height, vs->bytesPerPixel); +			if (_charset->_hasMask) { +				byte *mask = (byte *)_textSurface.getBasePtr(x * _textSurfaceMultiplier, (y - _screenTop) * _textSurfaceMultiplier); +				fill(mask, _textSurface.pitch, CHARSET_MASK_TRANSPARENCY, width * _textSurfaceMultiplier, height * _textSurfaceMultiplier, _textSurface.bytesPerPixel); +			}  		}  	} else if (_game.heversion >= 72) {  		// Flags are used for different methods in HE games @@ -1293,10 +1303,20 @@ void ScummEngine::drawBox(int x, int y, int x2, int y2, int color) {  			fill(backbuff, vs->pitch, flags, width, height, vs->bytesPerPixel);  		}  	} else { -		if (_game.features & GF_16BIT_COLOR) +		if (_game.features & GF_16BIT_COLOR) {  			fill(backbuff, vs->pitch, _16BitPalette[color], width, height, vs->bytesPerPixel); -		else +		} else { +			if (_game.platform == Common::kPlatformFMTowns) { +				color = ((color & 0x0f) << 4) | (color & 0x0f); +				byte *mask = (byte *)_textSurface.getBasePtr(x * _textSurfaceMultiplier, (y - _screenTop + vs->topline) * _textSurfaceMultiplier); +				fill(mask, _textSurface.pitch, color, width * _textSurfaceMultiplier, height * _textSurfaceMultiplier, _textSurface.bytesPerPixel); +				 +				if (_game.id == GID_MONKEY2 || _game.id == GID_INDY4) +					return; +			} +			  			fill(backbuff, vs->pitch, color, width, height, vs->bytesPerPixel); +		}  	}  } @@ -1703,6 +1723,11 @@ void Gdi::drawBitmap(const byte *ptr, VirtScreen *vs, int x, const int y, const  		warning("Gdi::drawBitmap, strip drawn to %d below window bottom %d", y + height, vs->h);  	} +	if (_vm->_townsPaletteFlags & 2) { +		int cx = (x - _vm->_screenStartStrip) << 3; +		_vm->_textSurface.fillRect(Common::Rect(cx * _vm->_textSurfaceMultiplier, y * _vm->_textSurfaceMultiplier, (cx  + width - 1) * _vm->_textSurfaceMultiplier, (y + height - 1) * _vm->_textSurfaceMultiplier), 0); +	} +  	_vertStripNextInc = height * vs->pitch - 1 * vs->bytesPerPixel;  	_objectMode = (flag & dbObjectMode) == dbObjectMode; @@ -3662,6 +3687,9 @@ void ScummEngine::fadeOut(int effect) {  		// Fill screen 0 with black  		memset(vs->getPixels(0, 0), 0, vs->pitch * vs->h); +		if (_game.version == 3 && _game.platform == Common::kPlatformFMTowns) +			_textSurface.fillRect(Common::Rect(0, vs->topline * _textSurfaceMultiplier, _textSurface.pitch, (vs->topline + vs->h) * _textSurfaceMultiplier), 0); +  		// Fade to black with the specified effect, if any.  		switch (effect) {  		case 1: @@ -3856,15 +3884,10 @@ void ScummEngine::dissolveEffect(int width, int height) {  		x = offsets[i] % vs->pitch;  		y = offsets[i] / vs->pitch; -		if (_useCJKMode && _textSurfaceMultiplier == 2) { -			int m = _textSurfaceMultiplier; -			byte *dst = _fmtownsBuf + x * m + y * m * _screenWidth * m; -			scale2x(dst, _screenWidth * m, vs->getPixels(x, y), vs->pitch,  width, height); - -			_system->copyRectToScreen(dst, _screenWidth * m, x * m, (y + vs->topline) * m, width * m, height * m); -		} else { +		if (_game.platform == Common::kPlatformFMTowns) +			towns_drawStripToScreen(vs, x, y + vs->topline, x, y, width, height); +		else  			_system->copyRectToScreen(vs->getPixels(x, y), vs->pitch, x, y + vs->topline, width, height); -		}  		if (++blits >= blits_before_refresh) { @@ -3904,23 +3927,19 @@ void ScummEngine::scrollEffect(int dir) {  		y = 1 + step;  		while (y < vs->h) {  			moveScreen(0, -step, vs->h); - -			src = vs->getPixels(0, y - step); -			if (_useCJKMode && m == 2) { -				int x1 = 0, y1 = vs->h - step; -				byte *dst = _fmtownsBuf + x1 * m + y1 * m * _screenWidth * m; -				scale2x(dst, _screenWidth * m, src, vs->pitch, vs->w, step); -				src = dst; -				vsPitch = _screenWidth * 2; +			 +			if (_townsScreen) { +				towns_drawStripToScreen(vs, 0, vs->topline + vs->h - step, 0, y - step, vs->w, step); +			} else { +				src = vs->getPixels(0, y - step); +				_system->copyRectToScreen(src, +					vsPitch, +					0, (vs->h - step) * m, +					vs->w * m, step * m); +				_system->updateScreen();  			} - -			_system->copyRectToScreen(src, -				vsPitch, -				0 * m, (vs->h - step) * m, -				vs->w * m, step * m); -			_system->updateScreen(); +			  			waitForTimer(delay); -  			y += step;  		}  		break; @@ -3929,21 +3948,19 @@ void ScummEngine::scrollEffect(int dir) {  		y = 1 + step;  		while (y < vs->h) {  			moveScreen(0, step, vs->h); -			src = vs->getPixels(0, vs->h - y); -			if (_useCJKMode && m == 2) { -				int x1 = 0, y1 = 0; -				byte *dst = _fmtownsBuf + x1 * m + y1 * m * _screenWidth * m; -				scale2x(dst, _screenWidth * m, src, vs->pitch, vs->w, step); -				src = dst; -				vsPitch = _screenWidth * 2; + +			if (_townsScreen) { +				towns_drawStripToScreen(vs, 0, vs->topline, 0, vs->h - y, vs->w, step); +			} else { +				src = vs->getPixels(0, vs->h - y); +				_system->copyRectToScreen(src, +					vsPitch, +					0, 0, +					vs->w * m, step * m); +				_system->updateScreen();  			} -			_system->copyRectToScreen(src, -				vsPitch, -				0, 0, -				vs->w * m, step * m); -			_system->updateScreen(); +			  			waitForTimer(delay); -  			y += step;  		}  		break; @@ -3952,21 +3969,19 @@ void ScummEngine::scrollEffect(int dir) {  		x = 1 + step;  		while (x < vs->w) {  			moveScreen(-step, 0, vs->h); -			src = vs->getPixels(x - step, 0); -			if (_useCJKMode && m == 2) { -				int x1 = vs->w - step, y1 = 0; -				byte *dst = _fmtownsBuf + x1 * m + y1 * m * _screenWidth * m; -				scale2x(dst, _screenWidth * m, src, vs->pitch, step, vs->h); -				src = dst; -				vsPitch = _screenWidth * 2; +			 +			if (_townsScreen) { +				towns_drawStripToScreen(vs, vs->w - step, vs->topline, x - step, 0, step, vs->h); +			} else { +				src = vs->getPixels(x - step, 0); +				_system->copyRectToScreen(src, +					vsPitch, +					(vs->w - step) * m, 0, +					step * m, vs->h * m); +				_system->updateScreen();  			} -			_system->copyRectToScreen(src, -				vsPitch, -				(vs->w - step) * m, 0, -				step * m, vs->h * m); -			_system->updateScreen(); -			waitForTimer(delay); +			waitForTimer(delay);  			x += step;  		}  		break; @@ -3975,21 +3990,19 @@ void ScummEngine::scrollEffect(int dir) {  		x = 1 + step;  		while (x < vs->w) {  			moveScreen(step, 0, vs->h); -			src = vs->getPixels(vs->w - x, 0); -			if (_useCJKMode && m == 2) { -				int x1 = 0, y1 = 0; -				byte *dst = _fmtownsBuf + x1 * m + y1 * m * _screenWidth * m; -				scale2x(dst, _screenWidth * m, src, vs->pitch, step, vs->h); -				src = dst; -				vsPitch = _screenWidth * 2; -			} -			_system->copyRectToScreen(src, -				vsPitch, -				0, 0, -				step, vs->h); -			_system->updateScreen(); -			waitForTimer(delay); +			if (_townsScreen) { +				towns_drawStripToScreen(vs, 0, vs->topline, vs->w - x, 0, step, vs->h); +			} else { +				src = vs->getPixels(vs->w - x, 0); +				_system->copyRectToScreen(src, +					vsPitch, +					0, 0, +					step, vs->h); +				_system->updateScreen(); +			}	 + +			waitForTimer(delay);  			x += step;  		}  		break; diff --git a/engines/scumm/gfx.h b/engines/scumm/gfx.h index cdb473a67c..b4b6309f51 100644 --- a/engines/scumm/gfx.h +++ b/engines/scumm/gfx.h @@ -26,6 +26,9 @@  #ifndef SCUMM_GFX_H  #define SCUMM_GFX_H +#include "common/system.h" +#include "common/list.h" +  #include "graphics/surface.h"  namespace Scumm { @@ -421,6 +424,64 @@ public:  };  #endif +// Helper class for FM-Towns output (required for specific hardware effects like +// switching graphics layers on and off). + +class TownsScreen { +public: +	TownsScreen(OSystem *system, int width, int height, int bpp); +	~TownsScreen(); + +	void setupLayer(int layer, int width, int height, int numCol, void *srcPal = 0); +	void clearLayer(int layer); +	void fillLayerRect(int layer, int x, int y, int w, int h, int col); +	//void copyRectToLayer(int layer, int x, int y, int w, int h, const uint8 *src); +	 +	uint8 *getLayerPixels(int layer, int x, int y); +	int getLayerPitch(int layer); +	int getLayerHeight(int layer); +	int getLayerBpp(int layer); +	int getLayerScaleW(int layer); +	int getLayerScaleH(int layer); +	 +	void addDirtyRect(int x, int y, int w, int h); +	void toggleLayers(int flag); +	void update(); + +private: +	void updateOutputBuffer(); +	void outputToScreen(); +	uint16 calc16BitColor(const uint8 *palEntry); + +	struct TownsScreenLayer { +		uint8 *pixels; +		uint8 *palette; +		int pitch; +		int height; +		int bpp; +		int numCol; +		uint8 scaleW; +		uint8 scaleH; +		bool onBottom; +		bool enabled; +		bool ready; + +		uint16 *bltInternX; +		uint8 **bltInternY; +	} _layers[2]; +	 +	uint8 *_outBuffer; + +	int _height; +	int _width; +	int _pitch; +	int _bpp; +	 +	int _numDirtyRects; +	Common::List<Common::Rect> _dirtyRects;	 +	OSystem *_system; +}; +  } // End of namespace Scumm  #endif diff --git a/engines/scumm/gfx_towns.cpp b/engines/scumm/gfx_towns.cpp new file mode 100644 index 0000000000..602f6984b4 --- /dev/null +++ b/engines/scumm/gfx_towns.cpp @@ -0,0 +1,508 @@ +/* ScummVM - Graphic Adventure Engine + * + * ScummVM is the legal property of its developers, whose names + * are too numerous to list here. Please refer to the COPYRIGHT + * file distributed with this source distribution. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the + * GNU General Public License for more details. + + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * $URL$ + * $Id$ + * + */ + +#include "common/endian.h" + +#include "scumm/scumm.h" +#include "scumm/charset.h" +#include "scumm/util.h" +#include "scumm/resource.h" + +namespace Scumm { + +void ScummEngine::towns_drawStripToScreen(VirtScreen *vs, int dstX, int dstY, int srcX, int srcY, int width, int height) { +	if (width <= 0 || height <= 0) +		return; + +	assert(_textSurface.pixels); + +	int m = _textSurfaceMultiplier; + +	uint8 *src1 = vs->getPixels(srcX, srcY); +	uint8 *src2 = (uint8*)_textSurface.getBasePtr(srcX * m, (srcY + vs->topline - _screenTop) * m); +	uint8 *dst1 = _townsScreen->getLayerPixels(0, dstX, dstY); +	uint8 *dst2 = _townsScreen->getLayerPixels(1, dstX * m, dstY * m); + +	int dp1 = _townsScreen->getLayerPitch(0) - width * _townsScreen->getLayerBpp(0); +	int dp2 = _townsScreen->getLayerPitch(1) - width * m * _townsScreen->getLayerBpp(1); +	int sp1 = vs->pitch - (width * vs->bytesPerPixel); +	int sp2 = _textSurface.pitch - width * m; +				 +	if (vs->number == kMainVirtScreen) { +		for (int h = 0; h < height; ++h) { +			if (_bytesPerPixelOutput == 2) { +				for (int w = 0; w < width; ++w) { +					WRITE_UINT16(dst1, _16BitPalette[*src1++]); +					dst1 += _bytesPerPixelOutput; +				} + +				src1 += sp1; +				dst1 += dp1; +			} else { +				memcpy(dst1, src1, width); +				src1 += vs->pitch; +				dst1 += _townsScreen->getLayerPitch(0); +			} +			 +			for (int sH = 0; sH < m; ++sH) { +				memcpy(dst2, src2, width * m); +				src2 += _textSurface.pitch; +				dst2 += _townsScreen->getLayerPitch(1); +			} +		}					 +	} else { +		dst1 = dst2; +		for (int h = 0; h < height; ++h) { +			for (int w = 0; w < width; ++w) { +				uint8 t = (*src1++) & 0x0f; +				memset(dst1, (t << 4) | t, m); +				dst1 += m; +			} + +			dst1 = dst2; +			uint8 *src3 = src2; +			 +			if (m == 2) { +				dst2 += _townsScreen->getLayerPitch(1); +				src3 += _townsScreen->getLayerPitch(1); +			} + +			for (int w = 0; w < width * m; ++w) { +				*dst2++ = (*src3 | (*dst1 & _townsLayer2Mask[*src3])); +				*dst1 = (*src2 | (*dst1 & _townsLayer2Mask[*src2])); +				src2++; +				src3++; +				dst1++; +			} + +			src1 += sp1;			 +			src2 = src3 + sp2; +			dst1 = dst2 + dp2; +			dst2 += dp2; +		} +	} + +	_townsScreen->addDirtyRect(dstX * m, dstY * m, width * m, height * m); +} + +bool ScummEngine::towns_isRectInStringBox(int x1, int y1, int x2, int y2) { +	if (_game.platform == Common::kPlatformFMTowns && _charset->_hasMask && y1 <= _curStringRect.bottom && x1 <= _curStringRect.right && y2 >= _curStringRect.top && x2 >= _curStringRect.left) +		return true; +	return false; +} + +void ScummEngine::towns_restoreCharsetBg() { +	if (_curStringRect.left != -1) { +		restoreBackground(_curStringRect, 0); +		_curStringRect.left = -1; +		_charset->_hasMask = false; +		_nextLeft = _string[0].xpos; +	} + +	_nextLeft = _string[0].xpos; +	_nextTop = _string[0].ypos; +} + +#ifdef USE_RGB_COLOR +void ScummEngine::towns_setPaletteFromPtr(const byte *ptr, int numcolor) { +	setPaletteFromPtr(ptr, numcolor); + +	if (_game.version == 5) +		towns_setTextPaletteFromPtr(_currentPalette); + +	_townsOverrideShadowColor = 1; +	int m = 48; +	for (int i = 1; i < 16; ++i) { +		int val = _currentPalette[i * 3] + _currentPalette[i * 3 + 1] + _currentPalette[i * 3 + 2]; +		if (m > val) { +			_townsOverrideShadowColor = i; +			m = val; +		} +	} +} + +void ScummEngine::towns_setTextPaletteFromPtr(const byte *ptr) { +	memcpy(_textPalette, ptr, 48); +} +#endif + +void ScummEngine::towns_setupPalCycleField(int x1, int y1, int x2, int y2) { +	if (_numCyclRects >= 10) +		return; +	_cyclRects[_numCyclRects].left = x1; +	_cyclRects[_numCyclRects].top = y1; +	_cyclRects[_numCyclRects].right = x2; +	_cyclRects[_numCyclRects].bottom = y2; +	_numCyclRects++; +	_townsPaletteFlags |= 1; +} + +void ScummEngine::towns_processPalCycleField() { +	for (int i = 0; i < _numCyclRects; i++) { +		int x1 = _cyclRects[i].left - _virtscr[kMainVirtScreen].xstart; +		int x2 = _cyclRects[i].right - _virtscr[kMainVirtScreen].xstart; +		if (x1 < 0) +			x1 = 0; +		if (x2 > 320) +			x2 = 320; +		if (x2 > 0) +			markRectAsDirty(kMainVirtScreen, x1, x2, _cyclRects[i].top, _cyclRects[i].bottom); +	} +} + +void ScummEngine::towns_resetPalCycleFields() { +	_numCyclRects = 0; +	_townsPaletteFlags &= ~1; +} + +const uint8 ScummEngine::_townsLayer2Mask[] = { +	0xFF, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, +	0x0F, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +	0x0F, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +	0x0F, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +	0x0F, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +	0x0F, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +	0x0F, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +	0x0F, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +	0x0F, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +	0x0F, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +	0x0F, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +	0x0F, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +	0x0F, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +	0x0F, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +	0x0F, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +	0x0F, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 +}; + +#define DIRTY_RECTS_MAX 20 +#define FULL_REDRAW (DIRTY_RECTS_MAX + 1) + +TownsScreen::TownsScreen(OSystem *system, int width, int height, int bpp) : +	_system(system), _width(width), _height(height), _bpp(bpp), _pitch(width * bpp) { +	memset(&_layers[0], 0, sizeof(TownsScreenLayer)); +	memset(&_layers[1], 0, sizeof(TownsScreenLayer)); +	_outBuffer = new byte[_pitch * _height]; +	memset(_outBuffer, 0, _pitch * _height); + +	setupLayer(0, width, height, 256); +} + +TownsScreen::~TownsScreen() { +	delete[] _layers[0].pixels; +	delete[] _layers[1].pixels; +	delete[] _layers[0].bltInternX; +	delete[] _layers[1].bltInternX; +	delete[] _layers[0].bltInternY; +	delete[] _layers[1].bltInternY; +	delete[] _outBuffer; +	_dirtyRects.clear(); +} + +void TownsScreen::setupLayer(int layer, int width, int height, int numCol, void *pal) { +	if (layer < 0 || layer > 1) +		return; + +	TownsScreenLayer *l = &_layers[layer]; + +	if (numCol >> 15) +		error("TownsScreen::setupLayer(): No more than 32767 colors supported."); + +	if (width > _width || height > _height) +		error("TownsScreen::setupLayer(): Layer width/height must be equal or less than screen width/height"); +	 +	l->scaleW = _width / width; +	l->scaleH = _height / height; + +	if ((float)l->scaleW !=	((float)_width / (float)width) || (float)l->scaleH != ((float)_height / (float)height)) +		error("TownsScreen::setupLayer(): Layer width/height must be equal or an EXACT half, third, etc. of screen width/height.\n More complex aspect ratio scaling is not supported."); + +	if (width <= 0 || height <= 0 || numCol < 16) +		error("TownsScreen::setupLayer(): Invalid width/height/number of colors setting."); + +	l->height = height; +	l->numCol = numCol; +	l->bpp = ((numCol - 1) & 0xff00) ? 2 : 1; +	l->pitch = width * l->bpp; +	l->palette = (uint8*)pal; + +	if (l->palette && _bpp == 1) +		warning("TownsScreen::setupLayer(): Layer palette usage requires 15 bit graphics setting.\nLayer palette will be ignored."); + +	delete[] l->pixels; +	l->pixels = new uint8[l->pitch * l->height]; +	assert(l->pixels); +	memset(l->pixels, 0, l->pitch * l->height); + +	// build offset tables to speed up merging/scaling layers +	delete[] l->bltInternX; +	l->bltInternX = new uint16[_width]; +	for (int i = 0; i < _width; ++i) +		l->bltInternX[i] = (i / l->scaleW) * l->bpp; + +	delete[] l->bltInternY; +	l->bltInternY = new uint8*[_height]; +	for (int i = 0; i < _height; ++i) +		l->bltInternY[i] = l->pixels + (i / l->scaleH) * l->pitch; + +	l->enabled = true; +	l->onBottom = (!layer || !_layers[0].enabled); +	l->ready = true; +} +	 +void TownsScreen::clearLayer(int layer) { +	if (layer < 0 || layer > 1) +		return; + +	TownsScreenLayer *l = &_layers[layer]; +	if (!l->ready) +		return; + +	memset(l->pixels, 0, l->pitch * l->height); +	_dirtyRects.push_back(Common::Rect(_width - 1, _height - 1)); +	_numDirtyRects = FULL_REDRAW; +} + + +void TownsScreen::fillLayerRect(int layer, int x, int y, int w, int h, int col) { +	if (layer < 0 || layer > 1 || w <= 0 || h <= 0) +		return; + +	TownsScreenLayer *l = &_layers[layer]; +	if (!l->ready) +		return; + +	assert(x >= 0 && y >= 0 && ((x + w) * l->bpp) <= (l->pitch) && (y + h) <= (l->height)); + +	uint8 *pos = l->pixels + y * l->pitch + x * l->bpp; +	 +	for (int i = 0; i < h; ++i) { +		if (l->bpp == 2) { +			for (int ii = 0; ii < w; ++ii) {			 +				WRITE_UINT16(pos, col); +				pos += 2; +			} +			pos += (l->pitch - w * 2); +		} else { +			memset(pos, col, w); +			pos += l->pitch; +		} +	} +	addDirtyRect(x * l->scaleW, y * l->scaleH, w * l->scaleW, h * l->scaleH); +} + +uint8 *TownsScreen::getLayerPixels(int layer, int x, int y) { +	if (layer < 0 || layer > 1) +		return 0; + +	TownsScreenLayer *l = &_layers[layer]; +	if (!l->ready) +		return 0; + +	return l->pixels + y * l->pitch + x * l->bpp; +} + +int TownsScreen::getLayerPitch(int layer) { +	if (layer >= 0 && layer < 2) +		return _layers[layer].pitch; +	return 0; +} + +int TownsScreen::getLayerHeight(int layer) { +	if (layer >= 0 && layer < 2) +		return _layers[layer].height; +	return 0; +} + +int TownsScreen::getLayerBpp(int layer) { +	if (layer >= 0 && layer < 2) +		return _layers[layer].bpp; +	return 0; +} + +int TownsScreen::getLayerScaleW(int layer) { +	if (layer >= 0 && layer < 2) +		return _layers[layer].scaleW; +	return 0; +} + +int TownsScreen::getLayerScaleH(int layer) { +	if (layer >= 0 && layer < 2) +		return _layers[layer].scaleH; +	return 0; +} + +void TownsScreen::addDirtyRect(int x, int y, int w, int h) { +	if (w <= 0 || h <= 0 || _numDirtyRects > DIRTY_RECTS_MAX) +		return;	 +	 +	if (_numDirtyRects == DIRTY_RECTS_MAX) { +		// full redraw +		_dirtyRects.clear(); +		_dirtyRects.push_back(Common::Rect(_width - 1, _height - 1)); +		_numDirtyRects++; +		return; +	} + +	int x2 = x + w - 1; +	int y2 = y + h - 1; + +	assert(x >= 0 && y >= 0 && x2 <= _width && y2 <= _height); + +	bool skip = false; +	for (Common::List<Common::Rect>::iterator r = _dirtyRects.begin(); r != _dirtyRects.end(); ++r) { +		// Try to merge new rect with an existing rect (only once, since trying to merge +		// more than one overlapping rect would be causing more overhead than doing any good). +		if (x > r->left && x < r->right && y > r->top && y < r->bottom) { +			x = r->left; +			y = r->top; +			skip = true; +		} +		 +		if (x2 > r->left && x2 < r->right && y > r->top && y < r->bottom) { +			x2 = r->right; +			y = r->top; +			skip = true; +		} +		 +		if (x2 > r->left && x2 < r->right && y2 > r->top && y2 < r->bottom) { +			x2 = r->right; +			y2 = r->bottom; +			skip = true; +		} +		 +		if (x > r->left && x < r->right && y2 > r->top && y2 < r->bottom) { +			x = r->left; +			y2 = r->bottom; +			skip = true; +		}		 +				 +		if (skip) { +			r->left = x; +			r->top = y; +			r->right = x2; +			r->bottom = y2; +			break; +		} +	} + +	if (!skip) { +		_dirtyRects.push_back(Common::Rect(x, y, x2, y2)); +		_numDirtyRects++; +	} +} + +void TownsScreen::toggleLayers(int flag) { +	if (flag < 0 || flag > 3) +		return; + +	for (int i = 0; i < 2; ++i) { +		_layers[i].enabled = (flag & (i + 1)) ? true : false; +		_layers[i].onBottom = (!i || !_layers[0].enabled); +	} + +	_dirtyRects.clear(); +	_dirtyRects.push_back(Common::Rect(_width - 1, _height - 1)); +	_numDirtyRects = FULL_REDRAW; + +	memset(_outBuffer, 0, _pitch * _height); +	updateOutputBuffer(); +	outputToScreen(); + +	_system->updateScreen(); +} + +void TownsScreen::update() { +	updateOutputBuffer(); +	outputToScreen(); +} + +void TownsScreen::updateOutputBuffer() { +	for (Common::List<Common::Rect>::iterator r = _dirtyRects.begin(); r != _dirtyRects.end(); ++r) { +		for (int i = 0; i < 2; i++) { + +			TownsScreenLayer *l = &_layers[i]; +			if (!l->enabled || !l->ready) +				continue; + +			uint8 *dst = _outBuffer + r->top * _pitch + r->left * _bpp; +			int ptch = _pitch - (r->right - r->left + 1) * _bpp; + +			for (int y = r->top; y <= r->bottom; ++y) { +				if (l->bpp == _bpp && l->scaleW == 1 && l->onBottom) { +					memcpy(dst, l->bltInternY[y] + l->bltInternX[r->left], (r->right + 1 - r->left) * _bpp); +					dst += _pitch; + +				} else if (_bpp == 2) { +					for (int x = r->left; x <= r->right; ++x) { +						uint8 *src = l->bltInternY[y] + l->bltInternX[x]; +						if (l->bpp == 1) { +							uint8 col = *src; +							if (col || l->onBottom) { +								if (l->numCol == 16) +									col = (col >> 4) & (col & 0x0f); +								WRITE_LE_UINT16(dst, calc16BitColor(&l->palette[col * 3])); +							} +						} else { +							WRITE_LE_UINT16(dst, READ_LE_UINT16(src)); +						} +						dst += 2; +					} +					dst += ptch; + +				} else { +					for (int x = r->left; x <= r->right; ++x) { +						uint8 col = *(l->bltInternY[y] + l->bltInternX[x]); +						if (col || l->onBottom) { +							if (l->numCol == 16) +								col = (col >> 4) & (col & 0x0f);						 +							*dst = col; +						} +						dst++; +					} +					dst += ptch; +				}				 +			} +		} +	} +} + +void TownsScreen::outputToScreen() { +	for (Common::List<Common::Rect>::iterator i = _dirtyRects.begin(); i != _dirtyRects.end(); ++i) +		_system->copyRectToScreen(_outBuffer + i->top * _pitch + i->left * _bpp, _pitch, i->left, i->top, i->right - i->left + 1, i->bottom - i->top + 1); +	_dirtyRects.clear(); +	_numDirtyRects = 0; +} + +uint16 TownsScreen::calc16BitColor(const uint8 *palEntry) { +	uint16 ar = (palEntry[0] & 0xf8) << 7; +	uint16 ag = (palEntry[1] & 0xf8) << 2; +	uint16 ab = (palEntry[2] >> 3); +	uint16 col = ar | ag | ab; +	return col; +} + +#undef DIRTY_RECTS_MAX +#undef FULL_REDRAW + +} // End of namespace Scumm diff --git a/engines/scumm/module.mk b/engines/scumm/module.mk index 14d1f5fdd4..cd89f5ffad 100644 --- a/engines/scumm/module.mk +++ b/engines/scumm/module.mk @@ -16,6 +16,7 @@ MODULE_OBJS := \  	dialogs.o \  	file.o \  	file_nes.o \ +	gfx_towns.o \  	gfx.o \  	he/resource_he.o \  	he/script_v60he.o \ diff --git a/engines/scumm/palette.cpp b/engines/scumm/palette.cpp index 7659e5fe1a..38577cd0c4 100644 --- a/engines/scumm/palette.cpp +++ b/engines/scumm/palette.cpp @@ -30,6 +30,7 @@  #include "scumm/scumm_v6.h"  #include "scumm/scumm_v8.h"  #include "scumm/util.h" +#include "scumm/charset.h"  namespace Scumm { @@ -139,6 +140,22 @@ void ScummEngine::resetPalette() {  		0x00, 0x00, 0x00, 	0x00, 0xFF, 0x00  	}; +#ifdef USE_RGB_COLOR +	static const byte tableTownsV3Palette[] = { +		0x00, 0x00, 0x00,	0x00, 0x00, 0xA0,	0x00, 0xA0, 0x00,	0x00, 0xA0, 0xA0, +		0xA0, 0x00, 0x00,	0xA0, 0x00, 0xA0,	0xA0, 0x60, 0x00,	0xA0, 0xA0, 0xA0, +		0x60, 0x60, 0x60,	0x60, 0x60, 0xE0,	0x00, 0xE0, 0x00,	0x00, 0xE0, 0xE0, +		0xE0, 0x80, 0x80,	0xE0, 0x00, 0xE0,	0xE0, 0xE0, 0x00,	0xE0, 0xE0, 0xE0 +	}; + +	static const byte tableTownsLoomPalette[] = { +		0x00, 0x00, 0x00,	0x00, 0x00, 0xAB,	0x00, 0xAB, 0x00,	0x00, 0xAB, 0xAB, +		0xAB, 0x00, 0x00,	0x69, 0x29, 0x45,	0x8C, 0x4D, 0x14,	0xAB, 0xAB, 0xAB, +		0x57, 0x3F, 0x57,	0x57, 0x57, 0xFF,	0x57, 0xFF, 0x57,	0x57, 0xFF, 0xFF, +		0xFF, 0x57, 0x57,	0xD6, 0x94, 0x40,	0xFF, 0xFF, 0x57,	0xFF, 0xFF, 0xFF +	}; +#endif +  	if (_game.version <= 1) {  		if (_game.platform == Common::kPlatformApple2GS) {  			// TODO: unique palette? @@ -198,6 +215,17 @@ void ScummEngine::resetPalette() {  			// else we initialise and then lock down the first 16 colors.  			if (_renderMode != Common::kRenderEGA)  				setPaletteFromTable(tableAmigaMIPalette, sizeof(tableAmigaMIPalette) / 3); +		} else if (_game.platform == Common::kPlatformFMTowns) { +			if (_game.id == GID_INDY4 || _game.id == GID_MONKEY2) +				_townsClearLayerFlag = 0; +#ifdef USE_RGB_COLOR +			else if (_game.id == GID_LOOM) +				towns_setTextPaletteFromPtr(tableTownsLoomPalette);  +			else if (_game.version == 3) +				towns_setTextPaletteFromPtr(tableTownsV3Palette); +#endif +			 +			_townsScreen->toggleLayers(_townsActiveLayerFlags);  		}  		setDirtyColors(0, 255);  	} @@ -465,6 +493,9 @@ void ScummEngine::cyclePalette() {  	int valueToAdd;  	int i, j; +	if (_game.platform == Common::kPlatformFMTowns && (!_townsPaletteFlags & 1)) +		return; +  	valueToAdd = VAR(VAR_TIMER);  	if (valueToAdd < VAR(VAR_TIMER_NEXT))  		valueToAdd = VAR(VAR_TIMER_NEXT); @@ -506,6 +537,9 @@ void ScummEngine::moveMemInPalRes(int start, int end, byte direction) {  }  void ScummEngine::palManipulateInit(int resID, int start, int end, int time) { +	if (_game.platform == Common::kPlatformFMTowns && (!_townsPaletteFlags & 1)) +		return; +  	byte *string1 = getStringAddress(resID);  	byte *string2 = getStringAddress(resID + 1);  	byte *string3 = getStringAddress(resID + 2); @@ -973,6 +1007,10 @@ void ScummEngine::setCurrentPalette(int palindex) {  	pals = getPalettePtr(_curPalIndex, _roomResource);  	if (_game.id == GID_LOOM && _game.platform == Common::kPlatformPCEngine) {  		setPCEPaletteFromPtr(pals); +#ifdef USE_RGB_COLOR +	} else if (_game.platform == Common::kPlatformFMTowns) { +		towns_setPaletteFromPtr(pals); +#endif  	} else {  		setPaletteFromPtr(pals);  	} @@ -1069,10 +1107,21 @@ void ScummEngine::updatePalette() {  		}  	} -	_system->setPalette(palette_colors, first, num); -  	_palDirtyMax = -1;  	_palDirtyMin = 256; + +#ifdef USE_RGB_COLOR +	if (_game.platform == Common::kPlatformFMTowns) { +		p = palette_colors; +		for (i = first; i < first + num; ++i) { +			_16BitPalette[i] = get16BitColor(p[0], p[1], p[2]); +			p += 4; +		} +		return; +	} +#endif + +	_system->setPalette(palette_colors, first, num);	  }  } // End of namespace Scumm diff --git a/engines/scumm/room.cpp b/engines/scumm/room.cpp index 014787ec7e..35d6d25548 100644 --- a/engines/scumm/room.cpp +++ b/engines/scumm/room.cpp @@ -194,6 +194,9 @@ void ScummEngine::startScene(int room, Actor *a, int objectNr) {  	showActors();  	_egoPositioned = false; + +	towns_resetPalCycleFields(); +  	runEntryScript();  	if (_game.version >= 1 && _game.version <= 2) {  		runScript(5, 0, 0, 0); diff --git a/engines/scumm/saveload.cpp b/engines/scumm/saveload.cpp index ca48a2b86a..a1747db690 100644 --- a/engines/scumm/saveload.cpp +++ b/engines/scumm/saveload.cpp @@ -1294,9 +1294,36 @@ void ScummEngine::saveOrLoad(Serializer *s) {  	//  	// Save/load palette data  	// -	if (_16BitPalette) { +	if (_16BitPalette && !(_game.platform == Common::kPlatformFMTowns && s->isLoading() && s->getVersion() < VER(82))) {  		s->saveLoadArrayOf(_16BitPalette, 512, sizeof(_16BitPalette[0]), sleUint16);  	} + +	// FM-Towns specific (extra palette data, color cycle data, etc.) +	if (s->getVersion() >= VER(82)) { +		const SaveLoadEntry townsFields[] = { +			MKLINE(Common::Rect, left, sleInt16, VER(82)), +			MKLINE(Common::Rect, top, sleInt16, VER(82)), +			MKLINE(Common::Rect, right, sleInt16, VER(82)), +			MKLINE(Common::Rect, bottom, sleInt16, VER(82)), +			MKEND() +		}; + +		const SaveLoadEntry townsExtraEntries[] = { +			MKLINE(ScummEngine, _townsOverrideShadowColor, sleUint8, VER(82)), +			MKLINE(ScummEngine, _numCyclRects, sleUint8, VER(82)), +			MKLINE(ScummEngine, _townsPaletteFlags, sleUint8, VER(82)), +			MKLINE(ScummEngine, _townsClearLayerFlag, sleUint8, VER(82)), +			MKLINE(ScummEngine, _townsActiveLayerFlags, sleUint8, VER(82)), +			MKEND() +		}; + +		s->saveLoadArrayOf(_textPalette, 48, sizeof(_textPalette[0]), sleUint8);		 +		s->saveLoadArrayOf(_cyclRects, 10, sizeof(_cyclRects[0]), townsFields); +		s->saveLoadArrayOf(&_curStringRect, 1, sizeof(_curStringRect), townsFields); +		s->saveLoadArrayOf(_townsCharsetColorMap, 16, sizeof(_townsCharsetColorMap[0]), sleUint8);		 +		s->saveLoadEntries(this, townsExtraEntries); +	} +  	if (_shadowPaletteSize) {  		s->saveLoadArrayOf(_shadowPalette, _shadowPaletteSize, 1, sleByte);  		// _roomPalette didn't show up until V21 save games @@ -1459,6 +1486,17 @@ void ScummEngine_v5::saveOrLoad(Serializer *s) {  	// This is probably only needed for Loom.  	s->saveLoadEntries(this, cursorEntries); + +	// Reset cursors for old FM-Towns savegames saved with 256 color setting. +	// Otherwise the cursor will be messed up when displayed in the new hi color setting. +	if (_game.platform == Common::kPlatformFMTowns && _bytesPerPixelOutput == 2 && s->isLoading() && s->getVersion() < VER(82)) { +		if (_game.id == GID_LOOM) { +			redefineBuiltinCursorFromChar(1, 1); +			redefineBuiltinCursorHotspot(1, 0, 0); +		} else { +			resetCursors(); +		} +	}  }  #ifdef ENABLE_SCUMM_7_8 diff --git a/engines/scumm/saveload.h b/engines/scumm/saveload.h index 44c8cc7d60..aaa79f38b6 100644 --- a/engines/scumm/saveload.h +++ b/engines/scumm/saveload.h @@ -50,7 +50,7 @@ namespace Scumm {   * only saves/loads those which are valid for the version of the savegame   * which is being loaded/saved currently.   */ -#define CURRENT_VER 81 +#define CURRENT_VER 82  /**   * An auxillary macro, used to specify savegame versions. We use this instead diff --git a/engines/scumm/script_v4.cpp b/engines/scumm/script_v4.cpp index 6dc3004432..8e1feb7388 100644 --- a/engines/scumm/script_v4.cpp +++ b/engines/scumm/script_v4.cpp @@ -111,62 +111,15 @@ void ScummEngine_v4::o4_oldRoomEffect() {  	if ((_opcode & 0x1F) == 3) {  		a = getVarOrDirectWord(PARAM_1); -#if 1  		if (_game.platform == Common::kPlatformFMTowns && _game.version == 3) { -			// FIXME / TODO: OK the first thing to note is: at least in Zak256, -			// maybe also in other games, this opcode does a bit more. I added -			// some stubs here, but somebody with a full IDA or more knowledge -			// about this will have to fill in the gaps. At least now we know -			// that something is missing here :-) -  			if (a == 4) { -				//printf("o5_oldRoomEffect ODDBALL: _opcode = 0x%x, a = 0x%x\n", _opcode, a); -				// No idea what byte_2FCCF is, but it's a globale boolean flag. -				// I only add it here as a temporary hack to make the pseudo code compile. -				// Maybe it is just there as a reentry protection guard, given -				// how it is used? It might also correspond to _screenEffectFlag. -				int byte_2FCCF = 0; - -				// For now, we force a redraw of the screen background. This -				// way the Zak end credits seem to work mostly correct. -				VirtScreen *vs = &_virtscr[kMainVirtScreen]; -				restoreBackground(Common::Rect(0, vs->topline, vs->w, vs->topline + vs->h)); -				vs->setDirtyRange(0, vs->h); -				updateDirtyScreen(kMainVirtScreen); - -				if (byte_2FCCF) { -					// Here now "sub_1C44" is called, which sets byte_2FCCF to 0 then -					// calls yet another sub (which also reads byte_2FCCF): - -					byte_2FCCF = 0; -					//call sub_0BB3 - - -					// Now sub_085C is called. This is quite simply: it sets -					// 0xF000 bytes. starting at 0x40000 to 0. No idea what that -					// buffer is, maybe a screen buffer, though. Note that -					// 0xF000 = 320*192. -					// Maybe this is also the charset mask being cleaned? - -					// call sub_085C - - -					// And then sub_1C54 is called, which is almost identical to -					// the above sub_1C44, only it sets byte_2FCCF to 1: - -					byte_2FCCF = 1; -					// call sub_0BB3 - -				} else { -					// Here only sub_085C is called (see comment above) - -					// call sub_085C -				} -			return; +				_textSurface.fillRect(Common::Rect(0, 0, _textSurface.w * _textSurfaceMultiplier, _textSurface.h * _textSurfaceMultiplier), 0); +				if (_townsScreen) +					_townsScreen->clearLayer(1); +				return;  			} -#endif -  		} +  		if (a) {  			_switchRoomEffect = (byte)(a & 0xFF);  			_switchRoomEffect2 = (byte)(a >> 8); diff --git a/engines/scumm/script_v5.cpp b/engines/scumm/script_v5.cpp index 8d986afc66..56c9f11445 100644 --- a/engines/scumm/script_v5.cpp +++ b/engines/scumm/script_v5.cpp @@ -1617,7 +1617,7 @@ void ScummEngine_v5::o5_resourceRoutines() {  void ScummEngine_v5::o5_roomOps() {  	int a = 0, b = 0, c, d, e; -	const bool paramsBeforeOpcode = (_game.version == 3 && _game.platform != Common::kPlatformPCEngine); +	const bool paramsBeforeOpcode = (_game.version == 3 && (_game.platform != Common::kPlatformPCEngine || _game.platform != Common::kPlatformFMTowns));  	if (paramsBeforeOpcode) {  		a = getVarOrDirectWord(PARAM_1); @@ -1714,21 +1714,51 @@ void ScummEngine_v5::o5_roomOps() {  		if (a) {  			if (_game.platform == Common::kPlatformFMTowns) {  				switch (a) { -				case 8: // compose kMainVirtScreen over a screen buffer -				case 9: // call 0x110:0x20 _ax=0x601 _edx=2 -				case 10: // call 0x110:0x20 _ax=0x601 _edx=3 -				case 11: // clear screen 0x1C:0x45000 sizeof(640 * 320) -				case 12: // call 0x110:0x20 _ax=0x601 _edx=0 -				case 13: // call 0x110:0x20 _ax=0x601 _edx=1 -				case 16: // enable clearing of a screen buffer in drawBitmap() -				case 17: // disable clearing of a screen buffer in drawBitmap() -				case 18: // clear a screen buffer +				case 8: +					towns_drawStripToScreen(&_virtscr[kMainVirtScreen], 0, _virtscr[kMainVirtScreen].topline, 0, 0, _virtscr[kMainVirtScreen].w, _virtscr[kMainVirtScreen].topline + _virtscr[kMainVirtScreen].h); +					_townsScreen->update(); +					return; +				case 9: +					_townsActiveLayerFlags = 2; +					_townsScreen->toggleLayers(_townsActiveLayerFlags); +					return; +				case 10: +					_townsActiveLayerFlags = 3; +					_townsScreen->toggleLayers(_townsActiveLayerFlags); +					return; +				case 11: +					_townsScreen->clearLayer(1); +					return; +				case 12: +					_townsActiveLayerFlags = 0; +					_townsScreen->toggleLayers(_townsActiveLayerFlags); +					return; +				case 13: +					_townsActiveLayerFlags = 1; +					_townsScreen->toggleLayers(_townsActiveLayerFlags); +					return; +				case 16: // enable clearing of layer 2 buffer in drawBitmap() +					_townsPaletteFlags |= 2; +					return; +				case 17: // disable clearing of layer 2 buffer in drawBitmap() +					_townsPaletteFlags &= ~2; +					return; +				case 18: // clear kMainVirtScreen layer 2 buffer +					_textSurface.fillRect(Common::Rect(0, _virtscr[kMainVirtScreen].topline * _textSurfaceMultiplier, _textSurface.pitch, (_virtscr[kMainVirtScreen].topline + _virtscr[kMainVirtScreen].h) * _textSurfaceMultiplier), 0);  				case 19: // enable palette operations (palManipulate(), cyclePalette() etc.) +					_townsPaletteFlags |= 1; +					return;  				case 20: // disable palette operations -				case 21: // disable clearing of screen 0x1C:0x5000 sizeof(640 * 320) in initScreens() -				case 22: // enable clearing of screen 0x1C:0x5000 sizeof(640 * 320) in initScreens() +					_townsPaletteFlags &= ~1; +					return; +				case 21: // disable clearing of layer 0 in initScreens() +					_townsClearLayerFlag = 1; +					return; +				case 22: // enable clearing of layer 0 in initScreens() +					_townsClearLayerFlag = 0; +					return;  				case 30: -					debug(0, "o5_roomOps: unhandled FM-TOWNS fadeEffect %d", a); +					_townsOverrideShadowColor = 3;  					return;  				}  			} diff --git a/engines/scumm/scumm-md5.h b/engines/scumm/scumm-md5.h index a25fac1a88..47520e8f54 100644 --- a/engines/scumm/scumm-md5.h +++ b/engines/scumm/scumm-md5.h @@ -117,7 +117,7 @@ static const MD5Table md5table[] = {  	{ "2723fea3dae0cb47768c424b145ae0e7", "tentacle", "Floppy", "Floppy", 7932, Common::EN_ANY, Common::kPlatformPC },  	{ "27b2ef1653089fe5b897d9cc89ce784f", "balloon", "HE 80", "", -1, Common::RU_RUS, Common::kPlatformWindows },  	{ "27b3a4224ad63d5b04627595c1c1a025", "zak", "V2", "V2", -1, Common::IT_ITA, Common::kPlatformAmiga }, -	{ "28d24a33448fab6795850bc9f159a4a2", "atlantis", "", "Demo", 11170, Common::JA_JPN, Common::kPlatformFMTowns }, +	{ "28d24a33448fab6795850bc9f159a4a2", "atlantis", "FM-TOWNS", "Demo", 11170, Common::JA_JPN, Common::kPlatformFMTowns },  	{ "28ef68ee3ed76d7e2ee8ee13c15fbd5b", "loom", "EGA", "EGA", 5748, Common::EN_ANY, Common::kPlatformPC },  	{ "28f07458f1b6c24e118a1ea056827701", "lost", "HE 99", "", -1, Common::NL_NLD, Common::kPlatformUnknown },  	{ "2a208ffbcd0e83e86f4356e6f64aa6e1", "loom", "EGA", "EGA", -1, Common::ES_ESP, Common::kPlatformPC }, @@ -183,7 +183,7 @@ static const MD5Table md5table[] = {  	{ "4167a92a1d46baa4f4127d918d561f88", "tentacle", "", "CD", 7932, Common::EN_ANY, Common::kPlatformUnknown },  	{ "41958e24d03181ff9a381a66d048a581", "ft", "", "", -1, Common::PT_BRA, Common::kPlatformUnknown },  	{ "425205754fa749f4f0b0dd9d09fa45fd", "football", "", "Demo", -1, Common::EN_ANY, Common::kPlatformUnknown }, -	{ "430bc518017b6fac046f58bab6baad5d", "monkey2", "", "", -1, Common::JA_JPN, Common::kPlatformFMTowns }, +	{ "430bc518017b6fac046f58bab6baad5d", "monkey2", "FM-TOWNS", "", -1, Common::JA_JPN, Common::kPlatformFMTowns },  	{ "439a7f4adf510489981ac52308e7d7a2", "maniac", "C64", "", -1, Common::DE_DEU, Common::kPlatformC64 },  	{ "45082a5c9f42ba14dacfe1fdeeba819d", "freddicove", "HE 100", "Demo", 18422, Common::EN_ANY, Common::kPlatformUnknown },  	{ "45152f7cf2ba8f43cf8a8ea2e740ae09", "monkey", "VGA", "VGA", 8357, Common::ES_ESP, Common::kPlatformPC }, @@ -206,7 +206,7 @@ static const MD5Table md5table[] = {  	{ "4c4820518e16e1a0e3616a3b021a04f3", "catalog", "HE CUP", "Preview", 10927456, Common::DE_DEU, Common::kPlatformUnknown },  	{ "4cb9c3618f71668f8e4346c8f323fa82", "monkey2", "", "", 10700, Common::EN_ANY, Common::kPlatformMacintosh },  	{ "4ce2d5b355964bbcb5e5ce73236ef868", "freddicove", "HE 100", "", -1, Common::RU_RUS, Common::kPlatformWindows }, -	{ "4d34042713958b971cb139fba4658586", "atlantis", "", "", -1, Common::JA_JPN, Common::kPlatformFMTowns }, +	{ "4d34042713958b971cb139fba4658586", "atlantis", "FM-TOWNS", "", -1, Common::JA_JPN, Common::kPlatformFMTowns },  	{ "4dbff3787aedcd96b0b325f2d92d7ad9", "maze", "HE 100", "Updated", -1, Common::EN_USA, Common::kPlatformUnknown },  	{ "4dc780f1bc587a193ce8a97652791438", "loom", "EGA", "EGA", -1, Common::EN_ANY, Common::kPlatformAmiga },  	{ "4e5867848ee61bc30d157e2c94eee9b4", "PuttTime", "HE 90", "Demo", 18394, Common::EN_USA, Common::kPlatformUnknown }, @@ -501,7 +501,7 @@ static const MD5Table md5table[] = {  	{ "c6907d44f1166941d982864cd42cdc89", "pajama2", "HE 99", "", -1, Common::DE_DEU, Common::kPlatformUnknown },  	{ "c782fbbe74a987c3df8ac73cd3e289ed", "freddi", "HE 73", "", -1, Common::SE_SWE, Common::kPlatformMacintosh },  	{ "c7890e038806df2bb5c0c8c6f1986ea2", "monkey", "VGA", "VGA", -1, Common::EN_ANY, Common::kPlatformPC }, -	{ "c7be10f775404fd9785a8b92a06d240c", "atlantis", "", "", 12030, Common::EN_ANY, Common::kPlatformFMTowns }, +	{ "c7be10f775404fd9785a8b92a06d240c", "atlantis", "FM-TOWNS", "", 12030, Common::EN_ANY, Common::kPlatformFMTowns },  	{ "c7c492a107ec520d7a7943037d0ca54a", "freddi", "HE 71", "Demo", -1, Common::NL_NLD, Common::kPlatformWindows },  	{ "c83079157ec765a28de445aec9768d60", "tentacle", "", "Demo", 7477, Common::EN_ANY, Common::kPlatformUnknown },  	{ "c8575e0b973ff1723aba6cd92c642db2", "puttrace", "HE 99", "Demo", -1, Common::FR_FRA, Common::kPlatformWindows }, @@ -552,7 +552,7 @@ static const MD5Table md5table[] = {  	{ "d8323015ecb8b10bf53474f6e6b0ae33", "dig", "", "", 16304, Common::UNK_LANG, Common::kPlatformUnknown },  	{ "d917f311a448e3cc7239c31bddb00dd2", "samnmax", "", "CD", 9080, Common::EN_ANY, Common::kPlatformUnknown },  	{ "d9d0dd93d16ab4dec55cabc2b86bbd17", "samnmax", "", "Demo", 6478, Common::EN_ANY, Common::kPlatformPC }, -	{ "da09e666fc8f5b78d7b0ac65d1a3b56e", "monkey2", "", "", 11135, Common::EN_ANY, Common::kPlatformFMTowns }, +	{ "da09e666fc8f5b78d7b0ac65d1a3b56e", "monkey2", "FM-TOWNS", "", 11135, Common::EN_ANY, Common::kPlatformFMTowns },  	{ "da6269b18fcb08189c0aa9c95533cce2", "monkey", "CD", "CD", 8955, Common::IT_ITA, Common::kPlatformPC },  	{ "da669b20271b85182e9c17a2a37ea02e", "monkey2", "", "", -1, Common::DE_DEU, Common::kPlatformAmiga },  	{ "db21a6e338fe3b70c2723b6530865bf2", "PuttTime", "HE 85", "", -1, Common::FR_FRA, Common::kPlatformUnknown }, diff --git a/engines/scumm/scumm.cpp b/engines/scumm/scumm.cpp index 0f1b43cedb..4cb4b282e8 100644 --- a/engines/scumm/scumm.cpp +++ b/engines/scumm/scumm.cpp @@ -258,7 +258,7 @@ ScummEngine::ScummEngine(OSystem *syst, const DetectorResult &dr)  	_switchRoomEffect2 = 0;  	_switchRoomEffect = 0; -	_bytesPerPixel = 1; +	_bytesPerPixelOutput = _bytesPerPixel = 1;  	_doEffect = false;  	_snapScroll = false;  	_currentLights = 0; @@ -278,6 +278,7 @@ ScummEngine::ScummEngine(OSystem *syst, const DetectorResult &dr)  	_hePalettes = NULL;  	_hePaletteSlot = 0;  	_16BitPalette = NULL; +	_townsScreen = 0;  	_shadowPalette = NULL;  	_shadowPaletteSize = 0;  	memset(_currentPalette, 0, sizeof(_currentPalette)); @@ -318,6 +319,13 @@ ScummEngine::ScummEngine(OSystem *syst, const DetectorResult &dr)  	_skipDrawObject = 0; +	_townsPaletteFlags = 0; +	_townsClearLayerFlag = 1; +	_townsActiveLayerFlags = 3; +	memset(&_curStringRect, -1, sizeof(Common::Rect)); +	memset(&_cyclRects, 0, 16 * sizeof(Common::Rect)); +	_numCyclRects = 0; +	  	//  	// Init all VARS to 0xFF  	// @@ -533,16 +541,19 @@ ScummEngine::ScummEngine(OSystem *syst, const DetectorResult &dr)  		_screenHeight = 200;  	} -	_bytesPerPixel = (_game.features & GF_16BIT_COLOR) ? 2 : 1; +	_bytesPerPixelOutput = _bytesPerPixel = (_game.features & GF_16BIT_COLOR) ? 2 : 1; + +#ifdef USE_RGB_COLOR +	if (_game.platform == Common::kPlatformFMTowns) +		_bytesPerPixelOutput = 2; +#endif  	// Allocate gfx compositing buffer (not needed for V7/V8 games).  	if (_game.version < 7) -		_compositeBuf = (byte *)malloc(_screenWidth * _screenHeight * _bytesPerPixel); +		_compositeBuf = (byte *)malloc(_screenWidth * _screenHeight * _bytesPerPixelOutput);  	else  		_compositeBuf = 0; -	_fmtownsBuf = 0; -  	_herculesBuf = 0;  	if (_renderMode == Common::kRenderHercA || _renderMode == Common::kRenderHercG) {  		_herculesBuf = (byte *)malloc(Common::kHercW * Common::kHercH); @@ -608,7 +619,10 @@ ScummEngine::~ScummEngine() {  	free(_compositeBuf);  	free(_herculesBuf); -	free(_fmtownsBuf); + +	free(_16BitPalette); + +	delete _townsScreen;  	delete _debugger; @@ -1116,14 +1130,19 @@ Common::Error ScummEngine::init() {  			screenWidth *= _textSurfaceMultiplier;  			screenHeight *= _textSurfaceMultiplier;  		} -		if (_game.features & GF_16BIT_COLOR) { +		if (_game.features & GF_16BIT_COLOR || _game.platform == Common::kPlatformFMTowns) {  #ifdef USE_RGB_COLOR  			Graphics::PixelFormat format = Graphics::PixelFormat(2, 5, 5, 5, 0, 10, 5, 0, 0);  			initGraphics(screenWidth, screenHeight, screenWidth > 320, &format);  			if (format != _system->getScreenFormat())  				return Common::kUnsupportedColorMode;  #else -			error("16bit color support is required for this game"); +			if (_game.platform == Common::kPlatformFMTowns) { +				warning("Starting game without the required 16bit color support.\nYou will experience severe color glitches"); +				initGraphics(screenWidth, screenHeight, (screenWidth > 320)); +			} else { +				error("16bit color support is required for this game"); +			}  #endif  		} else {  			initGraphics(screenWidth, screenHeight, (screenWidth > 320)); @@ -1241,13 +1260,8 @@ void ScummEngine::setupScumm() {  	_res->setHeapThreshold(400000, maxHeapThreshold); -	if (_game.platform == Common::kPlatformFMTowns && _language == Common::JA_JPN) { -		free(_fmtownsBuf); -		_fmtownsBuf = (byte *)malloc(_screenWidth * _textSurfaceMultiplier * _screenHeight * _textSurfaceMultiplier); -	} -  	free(_compositeBuf); -	_compositeBuf = (byte *)malloc(_screenWidth * _textSurfaceMultiplier * _screenHeight * _textSurfaceMultiplier * _bytesPerPixel); +	_compositeBuf = (byte *)malloc(_screenWidth * _textSurfaceMultiplier * _screenHeight * _textSurfaceMultiplier * _bytesPerPixelOutput);  }  #ifdef ENABLE_SCUMM_7_8 @@ -1325,6 +1339,18 @@ void ScummEngine::resetScumm() {  	debug(9, "resetScumm"); +#ifdef USE_RGB_COLOR +	if (_game.features & GF_16BIT_COLOR || _game.platform == Common::kPlatformFMTowns) +		_16BitPalette = (uint16 *)calloc(512, sizeof(uint16)); +#endif + +	if (_game.platform == Common::kPlatformFMTowns) { +		delete _townsScreen; +		_townsScreen = new TownsScreen(_system, _screenWidth * _textSurfaceMultiplier, _screenHeight * _textSurfaceMultiplier, _bytesPerPixelOutput); +		_townsScreen->setupLayer(0, _screenWidth, _screenHeight, (_bytesPerPixelOutput == 2) ? 32767 : 256); +		_townsScreen->setupLayer(1, _screenWidth * _textSurfaceMultiplier, _screenHeight * _textSurfaceMultiplier, 16, _textPalette); +	} +  	if (_game.version == 0) {  		initScreens(8, 144);  	} else if ((_game.id == GID_MANIAC) && (_game.version <= 1) && !(_game.platform == Common::kPlatformNES)) { @@ -1517,8 +1543,6 @@ void ScummEngine_v3::resetScumm() {  	if (_game.id == GID_LOOM && _game.platform == Common::kPlatformPCEngine) { -		_16BitPalette = (uint16 *)calloc(512, sizeof(uint16)); -  		// Load tile set and palette for the distaff  		byte *roomptr = getResourceAddress(rtRoom, 90);  		assert(roomptr); @@ -1928,6 +1952,10 @@ void ScummEngine::waitForTimer(int msec_delay) {  	while (!shouldQuit()) {  		_sound->updateCD(); // Loop CD Audio if needed  		parseEvents(); + +		if (_townsScreen) +			_townsScreen->update(); +  		_system->updateScreen();  		if (_system->getMillis() >= start_time + msec_delay)  			break; @@ -2055,6 +2083,8 @@ load_game:  		goto load_game;  	} +	towns_processPalCycleField(); +  	if (_currentRoom == 0) {  		if (_game.version > 3)  			CHARSET_1(); @@ -2442,6 +2472,10 @@ void ScummEngine::pauseEngineIntern(bool pause) {  	} else {  		// Update the screen to make it less likely that the player will see a  		// brief cursor palette glitch when the GUI is disabled. + +		if (_townsScreen) +			_townsScreen->update(); +  		_system->updateScreen();  		// Resume sound & video diff --git a/engines/scumm/scumm.h b/engines/scumm/scumm.h index 1357bad8cf..d161c698c4 100644 --- a/engines/scumm/scumm.h +++ b/engines/scumm/scumm.h @@ -972,6 +972,7 @@ public:  	Common::RenderMode _renderMode;  	uint8 _bytesPerPixel; +	uint8 _bytesPerPixelOutput;  protected:  	ColorCycle _colorCycle[16];	// Palette cycles @@ -1044,6 +1045,7 @@ protected:  	void setRoomPalette(int pal, int room);  	void setPCEPaletteFromPtr(const byte *ptr);  	virtual void setPaletteFromPtr(const byte *ptr, int numcolor = -1); +  	virtual void setPalColor(int index, int r, int g, int b);  	void setDirtyColors(int min, int max);  	const byte *findPalInPals(const byte *pal, int index); @@ -1077,7 +1079,7 @@ protected:  	// Screen rendering  	byte *_compositeBuf;  	byte *_herculesBuf; -	byte *_fmtownsBuf; +	  	virtual void drawDirtyScreenParts();  	void updateDirtyScreen(VirtScreenNumber slot);  	void drawStripToScreen(VirtScreen *vs, int x, int w, int t, int b); @@ -1221,7 +1223,7 @@ protected:  	void restoreCharsetBg();  	void clearCharsetMask();  	void clearTextSurface(); - +	  	virtual void initCharset(int charset);  	virtual void printString(int m, const byte *msg); @@ -1397,6 +1399,36 @@ public:  	// Exists both in V7 and in V72HE:  	byte VAR_NUM_GLOBAL_OBJS; + +	// FM-Towns specific +public: +	bool towns_isRectInStringBox(int x1, int y1, int x2, int y2); +	byte _townsPaletteFlags; +	byte _townsCharsetColorMap[16]; + +protected: +	void towns_drawStripToScreen(VirtScreen *vs, int dstX, int dstY, int srcX, int srcY, int w, int h); +#ifdef USE_RGB_COLOR +	void towns_setPaletteFromPtr(const byte *ptr, int numcolor = -1); +	void towns_setTextPaletteFromPtr(const byte *ptr); +#endif +	void towns_setupPalCycleField(int x1, int y1, int x2, int y2); +	void towns_processPalCycleField(); +	void towns_resetPalCycleFields(); +	void towns_restoreCharsetBg(); + +	Common::Rect _cyclRects[16]; +	int _numCyclRects; +	 +	Common::Rect _curStringRect; + +	byte _townsOverrideShadowColor;	 +	byte _textPalette[48]; +	byte _townsClearLayerFlag; +	byte _townsActiveLayerFlags; +	static const uint8 _townsLayer2Mask[]; + +	TownsScreen *_townsScreen;  };  } // End of namespace Scumm diff --git a/engines/scumm/string.cpp b/engines/scumm/string.cpp index 30281cb565..b1e645be3e 100644 --- a/engines/scumm/string.cpp +++ b/engines/scumm/string.cpp @@ -508,6 +508,9 @@ void ScummEngine::CHARSET_1() {  	if (_game.version >= 5)  		memcpy(_charsetColorMap, _charsetData[_charset->getCurID()], 4); +	if (_keepText && _game.platform == Common::kPlatformFMTowns) +		memcpy(&_charset->_str, &_curStringRect, sizeof(Common::Rect)); +  	if (_talkDelay)  		return; @@ -539,7 +542,10 @@ void ScummEngine::CHARSET_1() {  			_nextTop = _string[0].ypos + _screenTop;  #endif  		} else { -			restoreCharsetBg(); +			if (_game.platform == Common::kPlatformFMTowns) +				towns_restoreCharsetBg(); +			else +				restoreCharsetBg();  		}  	} @@ -660,6 +666,9 @@ void ScummEngine::CHARSET_1() {  		}  	} +	if (_game.platform == Common::kPlatformFMTowns && (c == 0 || c == 2 || c == 3)) +		memcpy(&_curStringRect, &_charset->_str, sizeof(Common::Rect)); +  #ifdef ENABLE_SCUMM_7_8  	if (_game.version >= 7 && subtitleLine != subtitleBuffer) {  		((ScummEngine_v7 *)this)->addSubtitleToQueue(subtitleBuffer, subtitlePos, _charsetColor, _charset->getCurID()); diff --git a/engines/scumm/verbs.cpp b/engines/scumm/verbs.cpp index 25a59e0317..5135fd94a2 100644 --- a/engines/scumm/verbs.cpp +++ b/engines/scumm/verbs.cpp @@ -1451,9 +1451,10 @@ void ScummEngine::restoreVerbBG(int verb) {  	VerbSlot *vs;  	vs = &_verbs[verb]; +	uint8 col = ((_game.platform == Common::kPlatformFMTowns) && (_game.id == GID_MONKEY2 || _game.id == GID_INDY4) && (vs->bkcolor == _townsOverrideShadowColor)) ? 0 : vs->bkcolor;  	if (vs->oldRect.left != -1) { -		restoreBackground(vs->oldRect, vs->bkcolor); +		restoreBackground(vs->oldRect, col);  		vs->oldRect.left = -1;  	}  } | 
