From 7a169c90f676ead7de6aa2624ac257ff5e85c10e Mon Sep 17 00:00:00 2001 From: Martin Kiewitz Date: Sun, 28 Feb 2016 11:23:31 +0100 Subject: AGI: Hercules rendering for game screen --- engines/agi/agi.cpp | 19 +++---- engines/agi/font.cpp | 17 +++++++ engines/agi/graphics.cpp | 127 ++++++++++++++++++++++++++++++++++++++++++++++- engines/agi/graphics.h | 1 + engines/agi/palette.h | 16 ++++++ engines/agi/text.cpp | 10 ++++ 6 files changed, 177 insertions(+), 13 deletions(-) diff --git a/engines/agi/agi.cpp b/engines/agi/agi.cpp index 6773398271..6e63cd3e71 100644 --- a/engines/agi/agi.cpp +++ b/engines/agi/agi.cpp @@ -283,18 +283,7 @@ void AgiBase::initRenderMode() { switch (platform) { case Common::kPlatformDOS: - switch (configRenderMode) { - case Common::kRenderCGA: - _renderMode = Common::kRenderCGA; - break; - // Hercules is not supported atm - //case Common::kRenderHercA: - //case Common::kRenderHercG: - // _renderMode = Common::kRenderHercG; - // break; - default: - break; - } + // Keep EGA break; case Common::kPlatformAmiga: _renderMode = Common::kRenderAmiga; @@ -323,6 +312,12 @@ void AgiBase::initRenderMode() { case Common::kRenderVGA: _renderMode = Common::kRenderVGA; break; + case Common::kRenderHercG: + _renderMode = Common::kRenderHercG; + break; + case Common::kRenderHercA: + _renderMode = Common::kRenderHercA; + break; case Common::kRenderAmiga: _renderMode = Common::kRenderAmiga; break; diff --git a/engines/agi/font.cpp b/engines/agi/font.cpp index c453ee5aa1..5e6ba1e8ce 100644 --- a/engines/agi/font.cpp +++ b/engines/agi/font.cpp @@ -624,6 +624,16 @@ void GfxFont::init() { if (ConfMan.getBool("herculesfont")) { // User wants, that we use Hercules hires font, try to load it loadFontHercules(); + } else { + switch (_vm->_renderMode) { + case Common::kRenderHercA: + case Common::kRenderHercG: + // Render mode is Hercules, we try to load Hercules hires font + loadFontHercules(); + break; + default: + break; + } } if (!_fontData) { @@ -650,6 +660,8 @@ void GfxFont::init() { } } break; + case Common::kRenderHercA: + case Common::kRenderHercG: case Common::kRenderCGA: case Common::kRenderEGA: case Common::kRenderVGA: @@ -699,6 +711,11 @@ void GfxFont::overwriteSaveRestoreDialogCharacter() { // Overwrite extended character set (0x80-0xFF) with Russian characters void GfxFont::overwriteExtendedWithRussianSet() { + if (_fontIsHires) { + // TODO: Implement overwriting hires font characters too + return; + } + if (!_fontDataAllocated) { // nothing allocated, we need to allocate space ourselves to be able to modify an internal font _fontDataAllocated = (uint8 *)calloc(256, 8); diff --git a/engines/agi/graphics.cpp b/engines/agi/graphics.cpp index 5fa652c5ff..6d3563a451 100644 --- a/engines/agi/graphics.cpp +++ b/engines/agi/graphics.cpp @@ -69,6 +69,8 @@ GfxMgr::GfxMgr(AgiBase *vm, GfxFont *font) : _vm(vm), _font(font) { * @see deinit_video() */ int GfxMgr::initVideo() { + bool forceHires = false; + // Set up palettes initPalette(_paletteTextMode, PALETTE_EGA); @@ -82,6 +84,14 @@ int GfxMgr::initVideo() { case Common::kRenderVGA: initPalette(_paletteGfxMode, PALETTE_VGA, 256, 8); break; + case Common::kRenderHercG: + initPalette(_paletteGfxMode, PALETTE_HERCULES_GREEN, 2, 8); + forceHires = true; + break; + case Common::kRenderHercA: + initPalette(_paletteGfxMode, PALETTE_HERCULES_AMBER, 2, 8); + forceHires = true; + break; case Common::kRenderAmiga: if (!ConfMan.getBool("altamigapalette")) { // Set the correct Amiga palette depending on AGI interpreter version @@ -137,7 +147,7 @@ int GfxMgr::initVideo() { //bool forcedUpscale = true; - if (_font->isFontHires()) { + if (_font->isFontHires() || forceHires) { // Upscaling enable _upscaledHires = DISPLAY_UPSCALED_640x400; _displayScreenWidth = 640; @@ -154,6 +164,8 @@ int GfxMgr::initVideo() { case Common::kRenderEGA: case Common::kRenderCGA: case Common::kRenderVGA: + case Common::kRenderHercG: + case Common::kRenderHercA: initMouseCursor(&_mouseCursor, MOUSECURSOR_SCI, 11, 16, 0, 0); initMouseCursor(&_mouseCursorBusy, MOUSECURSOR_SCI_BUSY, 15, 16, 7, 8); break; @@ -500,6 +512,10 @@ void GfxMgr::render_Block(int16 x, int16 y, int16 width, int16 height, bool copy return; switch (_vm->_renderMode) { + case Common::kRenderHercG: + case Common::kRenderHercA: + render_BlockHercules(x, y, width, height, copyToScreen); + break; case Common::kRenderCGA: render_BlockCGA(x, y, width, height, copyToScreen); break; @@ -654,6 +670,106 @@ void GfxMgr::render_BlockCGA(int16 x, int16 y, int16 width, int16 height, bool c } } +static const uint8 herculesColorMapping[] = { + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x88, 0x00, 0x00, 0x00, 0x22, 0x00, 0x00, 0x00, + 0x80, 0x10, 0x02, 0x20, 0x01, 0x08, 0x40, 0x04, + 0xAA, 0x00, 0xAA, 0x00, 0xAA, 0x00, 0xAA, 0x00, + 0x22, 0x88, 0x22, 0x88, 0x22, 0x88, 0x22, 0x88, + 0x88, 0x00, 0x88, 0x00, 0x88, 0x00, 0x88, 0x00, + 0x11, 0x22, 0x44, 0x88, 0x11, 0x22, 0x44, 0x88, + 0x55, 0xAA, 0x55, 0xAA, 0x55, 0xAA, 0x55, 0xAA, + 0x22, 0x00, 0x88, 0x00, 0x22, 0x00, 0x88, 0x00, + 0xD7, 0xFF, 0x7D, 0xFF, 0xD7, 0xFF, 0x7D, 0xFF, + 0xDD, 0x55, 0x77, 0xAA, 0xDD, 0x55, 0x77, 0xAA, + 0x7F, 0xEF, 0xFD, 0xDF, 0xFE, 0xF7, 0xBF, 0xFB, + 0xAA, 0xFF, 0xAA, 0xFF, 0xAA, 0xFF, 0xAA, 0xFF, + 0x77, 0xBB, 0xDD, 0xEE, 0x77, 0xBB, 0xDD, 0xEE, + 0x77, 0xFF, 0xFF, 0xFF, 0xDD, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF +}; + +// Sierra actually seems to have rendered the whole screen all the time +void GfxMgr::render_BlockHercules(int16 x, int16 y, int16 width, int16 height, bool copyToScreen) { + uint32 offsetVisual = SCRIPT_WIDTH * y + x; + uint32 offsetDisplay = getDisplayOffsetToGameScreenPos(x, y); + int16 remainingWidth = width; + int16 remainingHeight = height; + byte curColor = 0; + int16 displayWidth = width * (2 + _displayWidthMulAdjust); + + assert(_upscaledHires == DISPLAY_UPSCALED_640x400); + + uint16 lookupOffset1 = (y * 2 & 0x07); + uint16 lookupOffset2 = 0; + bool getUpperNibble = false; + byte herculesColors1 = 0; + byte herculesColors2 = 0; + + while (remainingHeight) { + remainingWidth = width; + + lookupOffset1 = (lookupOffset1 + 0) & 0x07; + lookupOffset2 = (lookupOffset1 + 1) & 0x07; + + getUpperNibble = (x & 1) ? false : true; + while (remainingWidth) { + curColor = _activeScreen[offsetVisual++] & 0x0F; + + if (getUpperNibble) { + herculesColors1 = herculesColorMapping[curColor * 8 + lookupOffset1] & 0x0F; + herculesColors2 = herculesColorMapping[curColor * 8 + lookupOffset2] & 0x0F; + } else { + herculesColors1 = herculesColorMapping[curColor * 8 + lookupOffset1] >> 4; + herculesColors2 = herculesColorMapping[curColor * 8 + lookupOffset2] >> 4; + } + getUpperNibble ^= true; + + _displayScreen[offsetDisplay + 0] = (herculesColors1 & 0x08) ? 1 : 0; + _displayScreen[offsetDisplay + 1] = (herculesColors1 & 0x04) ? 1 : 0; + _displayScreen[offsetDisplay + 2] = (herculesColors1 & 0x02) ? 1 : 0; + _displayScreen[offsetDisplay + 3] = (herculesColors1 & 0x01) ? 1 : 0; + + _displayScreen[offsetDisplay + _displayScreenWidth + 0] = (herculesColors2 & 0x08) ? 1 : 0; + _displayScreen[offsetDisplay + _displayScreenWidth + 1] = (herculesColors2 & 0x04) ? 1 : 0; + _displayScreen[offsetDisplay + _displayScreenWidth + 2] = (herculesColors2 & 0x02) ? 1 : 0; + _displayScreen[offsetDisplay + _displayScreenWidth + 3] = (herculesColors2 & 0x01) ? 1 : 0; + + offsetDisplay += 4; + remainingWidth--; + } + + lookupOffset1 += 2; + + offsetVisual += SCRIPT_WIDTH - width; + offsetDisplay += _displayScreenWidth - displayWidth; + offsetDisplay += _displayScreenWidth;; + + remainingHeight--; + } +} + +// Table used for at least Manhunter 2, it renders 2 lines -> 3 lines instead of 4 +// Manhunter 1 is shipped with a broken Hercules font +// King's Quest 4 aborts right at the start, when Hercules rendering is active +#if 0 +static const uint8 herculesCoordinateOffset[] = { + 0x00, 0x01, 0x03, 0x04, 0x06, 0x07, 0x01, 0x02, + 0x04, 0x05, 0x07, 0x00, 0x02, 0x03, 0x05, 0x06 +}; + +static const uint8 herculesColorMapping[] = { + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x40, 0x00, 0x02, 0x00, 0x40, 0x00, 0x08, 0x00, + 0x80, 0x10, 0x02, 0x20, 0x01, 0x08, 0x40, 0x04, 0xAA, 0x00, 0xAA, 0x00, 0xAA, 0x00, 0xAA, 0x00, + 0x22, 0x88, 0x22, 0x88, 0x22, 0x88, 0x22, 0x88, 0x88, 0x00, 0x88, 0x00, 0x88, 0x00, 0x88, 0x00, + 0x11, 0x22, 0x44, 0x88, 0x11, 0x22, 0x44, 0x88, 0x55, 0xAA, 0x55, 0xAA, 0x55, 0xAA, 0x55, 0xAA, + 0x22, 0x00, 0x88, 0x00, 0x22, 0x00, 0x88, 0x00, 0xD7, 0xFF, 0x7D, 0xFF, 0xD7, 0xFF, 0x7D, 0xFF, + 0xDD, 0x55, 0x77, 0xAA, 0xDD, 0x55, 0x77, 0xAA, 0x7F, 0xEF, 0xFD, 0xDF, 0xFE, 0xF7, 0xBF, 0xFB, + 0xAA, 0xFF, 0xAA, 0xFF, 0xAA, 0xFF, 0xAA, 0xFF, 0x77, 0xBB, 0xDD, 0xEE, 0x77, 0xBB, 0xDD, 0xEE, + 0x7F, 0xEF, 0xFB, 0xBF, 0xEF, 0xFE, 0xBF, 0xFD, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF +}; +#endif + void GfxMgr::transition_Amiga() { uint16 screenPos = 1; uint32 screenStepPos = 1; @@ -876,6 +992,10 @@ void GfxMgr::drawBox(int16 x, int16 y, int16 width, int16 height, byte backgroun drawDisplayRect(x, +1, y + height, -2, width, -2, 0, 1, 0); drawDisplayRect(x, +1, y, +1, 0, 1, height, -2, 0); break; + case Common::kRenderHercA: + case Common::kRenderHercG: + lineColor = 0; // change linecolor to black + // supposed to fall through case Common::kRenderCGA: case Common::kRenderEGA: case Common::kRenderVGA: @@ -895,6 +1015,11 @@ void GfxMgr::drawDisplayRect(int16 x, int16 y, int16 width, int16 height, byte c case Common::kRenderCGA: drawDisplayRectCGA(x, y, width, height, color); break; + case Common::kRenderHercG: + case Common::kRenderHercA: + if (color) + color = 1; // change any color except black to green/amber + // supposed to fall through case Common::kRenderEGA: default: drawDisplayRectEGA(x, y, width, height, color); diff --git a/engines/agi/graphics.h b/engines/agi/graphics.h index c523b61a8f..1cb595cdfa 100644 --- a/engines/agi/graphics.h +++ b/engines/agi/graphics.h @@ -180,6 +180,7 @@ public: private: void render_BlockEGA(int16 x, int16 y, int16 width, int16 height, bool copyToScreen); void render_BlockCGA(int16 x, int16 y, int16 width, int16 height, bool copyToScreen); + void render_BlockHercules(int16 x, int16 y, int16 width, int16 height, bool copyToScreen); public: void transition_Amiga(); diff --git a/engines/agi/palette.h b/engines/agi/palette.h index e0db81ed7b..40c31da425 100644 --- a/engines/agi/palette.h +++ b/engines/agi/palette.h @@ -59,6 +59,22 @@ static const uint8 PALETTE_CGA[4 * 3] = { 0xff, 0xff, 0xff }; +/** + * 2 color Hercules (green) palette. Using 8-bit RGB values. + */ +static const uint8 PALETTE_HERCULES_GREEN[2 * 3] = { + 0x00, 0x00, 0x00, // black + 0x00, 0xdc, 0x28 // green +}; + +/** + * 2 color Hercules (amber) palette. Using 8-bit RGB values. + */ +static const uint8 PALETTE_HERCULES_AMBER[2 * 3] = { + 0x00, 0x00, 0x00, // black + 0xdc, 0xb4, 0x00 // amber +}; + /** * Atari ST AGI palette. * Used by all of the tested Atari ST AGI games diff --git a/engines/agi/text.cpp b/engines/agi/text.cpp index 31f364d856..18254d88f8 100644 --- a/engines/agi/text.cpp +++ b/engines/agi/text.cpp @@ -179,6 +179,16 @@ void TextMgr::charAttrib_Set(byte foreground, byte background) { _textAttrib.combinedBackground = 0; } break; + case Common::kRenderHercA: + case Common::kRenderHercG: + if (background) { + _textAttrib.combinedForeground = 0; + _textAttrib.combinedBackground = 1; + } else { + _textAttrib.combinedForeground = 1; + _textAttrib.combinedBackground = 0; + } + break; default: // EGA-handling: if (background) { -- cgit v1.2.3