diff options
Diffstat (limited to 'engines/adl/display.cpp')
-rw-r--r-- | engines/adl/display.cpp | 611 |
1 files changed, 611 insertions, 0 deletions
diff --git a/engines/adl/display.cpp b/engines/adl/display.cpp new file mode 100644 index 0000000000..02b8d51b7f --- /dev/null +++ b/engines/adl/display.cpp @@ -0,0 +1,611 @@ +/* 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. + * + */ + +#include "adl/display.h" +#include "common/stream.h" +#include "common/rect.h" +#include "graphics/surface.h" +#include "common/system.h" +#include "common/str.h" +#include "common/events.h" +#include "common/rect.h" +#include "common/array.h" +#include "engines/engine.h" + +namespace Adl { + +static byte font[64][5] = { + { 0x7c, 0x82, 0xba, 0xb2, 0x9c }, { 0xf8, 0x24, 0x22, 0x24, 0xf8 }, // @A + { 0xfe, 0x92, 0x92, 0x92, 0x6c }, { 0x7c, 0x82, 0x82, 0x82, 0x44 }, // BC + { 0xfe, 0x82, 0x82, 0x82, 0x7c }, { 0xfe, 0x92, 0x92, 0x92, 0x82 }, // DE + { 0xfe, 0x12, 0x12, 0x12, 0x02 }, { 0x7c, 0x82, 0x82, 0xa2, 0xe2 }, // FG + { 0xfe, 0x10, 0x10, 0x10, 0xfe }, { 0x00, 0x82, 0xfe, 0x82, 0x00 }, // HI + { 0x40, 0x80, 0x80, 0x80, 0x7e }, { 0xfe, 0x10, 0x28, 0x44, 0x82 }, // JK + { 0xfe, 0x80, 0x80, 0x80, 0x80 }, { 0xfe, 0x04, 0x18, 0x04, 0xfe }, // LM + { 0xfe, 0x08, 0x10, 0x20, 0xfe }, { 0x7c, 0x82, 0x82, 0x82, 0x7c }, // NO + { 0xfe, 0x12, 0x12, 0x12, 0x0c }, { 0x7c, 0x82, 0xa2, 0x42, 0xbc }, // PQ + { 0xfe, 0x12, 0x32, 0x52, 0x8c }, { 0x4c, 0x92, 0x92, 0x92, 0x64 }, // RS + { 0x02, 0x02, 0xfe, 0x02, 0x02 }, { 0x7e, 0x80, 0x80, 0x80, 0x7e }, // TU + { 0x3e, 0x40, 0x80, 0x40, 0x3e }, { 0xfe, 0x40, 0x30, 0x40, 0xfe }, // VW + { 0xc6, 0x28, 0x10, 0x28, 0xc6 }, { 0x06, 0x08, 0xf0, 0x08, 0x06 }, // XY + { 0xc2, 0xa2, 0x92, 0x8a, 0x86 }, { 0xfe, 0xfe, 0x82, 0x82, 0x82 }, // Z[ + { 0x04, 0x08, 0x10, 0x20, 0x40 }, { 0x82, 0x82, 0x82, 0xfe, 0xfe }, // \] + { 0x20, 0x10, 0x08, 0x10, 0x20 }, { 0x80, 0x80, 0x80, 0x80, 0x80 }, // ^_ + { 0x00, 0x00, 0x00, 0x00, 0x00 }, { 0x00, 0x00, 0xbe, 0x00, 0x00 }, // ! + { 0x00, 0x0e, 0x00, 0x0e, 0x00 }, { 0x28, 0xfe, 0x28, 0xfe, 0x28 }, // "# + { 0x48, 0x54, 0xfe, 0x54, 0x24 }, { 0x46, 0x26, 0x10, 0xc8, 0xc4 }, // $% + { 0x6c, 0x92, 0xac, 0x40, 0xa0 }, { 0x00, 0x00, 0x0e, 0x00, 0x00 }, // &' + { 0x38, 0x44, 0x82, 0x00, 0x00 }, { 0x00, 0x00, 0x82, 0x44, 0x38 }, // () + { 0x44, 0x28, 0xfe, 0x28, 0x44 }, { 0x10, 0x10, 0x7c, 0x10, 0x10 }, // *+ + { 0x00, 0x80, 0x60, 0x00, 0x00 }, { 0x10, 0x10, 0x10, 0x10, 0x10 }, // ,- + { 0x00, 0x00, 0x80, 0x00, 0x00 }, { 0x40, 0x20, 0x10, 0x08, 0x04 }, // ./ + { 0x7c, 0xa2, 0x92, 0x8a, 0x7c }, { 0x00, 0x84, 0xfe, 0x80, 0x00 }, // 01 + { 0xc4, 0xa2, 0x92, 0x92, 0x8c }, { 0x42, 0x82, 0x92, 0x9a, 0x66 }, // 23 + { 0x30, 0x28, 0x24, 0xfe, 0x20 }, { 0x4e, 0x8a, 0x8a, 0x8a, 0x72 }, // 45 + { 0x78, 0x94, 0x92, 0x92, 0x62 }, { 0x02, 0xe2, 0x12, 0x0a, 0x06 }, // 67 + { 0x6c, 0x92, 0x92, 0x92, 0x6c }, { 0x8c, 0x92, 0x92, 0x52, 0x3c }, // 89 + { 0x00, 0x00, 0x28, 0x00, 0x00 }, { 0x00, 0x80, 0x68, 0x00, 0x00 }, // :; + { 0x10, 0x28, 0x44, 0x82, 0x00 }, { 0x28, 0x28, 0x28, 0x28, 0x28 }, // <= + { 0x00, 0x82, 0x44, 0x28, 0x10 }, { 0x04, 0x02, 0xb2, 0x0a, 0x04 } // >? +}; + +Display::Display() : + _scanlines(false), + _cursorPos(0), + _mode(kModeText) { + _frameBuf = new byte[kFrameBufSize]; + _frameBufSurface = new Graphics::Surface; + _frameBufSurface->create(kWidth * 2, kHeight * 2, Graphics::PixelFormat::createFormatCLUT8()); + + _textBuf = new byte[kTextBufSize]; + memset(_textBuf, ' ' | 0x80, kTextBufSize); + _textBufSurface = new Graphics::Surface; + _textBufSurface->create(kWidth * 2, kHeight * 2, Graphics::PixelFormat::createFormatCLUT8()); + + createFont(); + + struct PixelPos rel = getPixelPos(0, 191); + struct PixelPos absy; + for (int i = 191; i >= 0; --i) { + absy = getPixelPos(0, i); + if (absy.rowAddr != rel.rowAddr) + debug("%i: %04x %04x", i, absy.rowAddr, rel.rowAddr); + moveY(rel, false); + } + absy = getPixelPos(0, 191); + if (absy.rowAddr != rel.rowAddr) + debug("%i: %04x %04x", 191, absy.rowAddr, rel.rowAddr); + + rel = getPixelPos(0, 0); + for (int i = 0; i < 192; ++i) { + absy = getPixelPos(0, i); + if (absy.rowAddr != rel.rowAddr) + debug("%i: %04x %04x", i, absy.rowAddr, rel.rowAddr); + moveY(rel, true); + } + absy = getPixelPos(0, 0); + if (absy.rowAddr != rel.rowAddr) + debug("%i: %04x %04x", 191, absy.rowAddr, rel.rowAddr); +} + +Display::~Display() { + delete[] _frameBuf; + _frameBufSurface->free(); + delete _frameBufSurface; + + delete[] _textBuf; + _textBufSurface->free(); + delete _textBufSurface; + + _font->free(); + delete _font; +} + +void Display::loadFrameBuffer(Common::ReadStream &stream) { + stream.read(_frameBuf, kFrameBufSize); +} + +void Display::decodeScanline(byte *dst, int pitch, byte *src) { + // TODO: shift secondPal by half a pixel + + bool prevOn = false; + + for (uint j = 0; j < 39; ++j) { + bool secondPal = src[j] & 0x80; + byte cur = src[j]; + byte next = 0; + if (j != 39) + next = src[j + 1]; + + for (uint k = 0; k < 7; ++k) { + bool curOn = cur & (1 << k); + bool nextOn; + + if (k != 6) + nextOn = cur & (1 << (k + 1)); + else + nextOn = next & 1; + + byte color; + if (curOn == prevOn || curOn == nextOn) + color = curOn ? 1 : 0; + else { + if (secondPal) + color = (curOn == ((j + k) % 2) ? 5 : 4); + else + color = (curOn == ((j + k) % 2) ? 3 : 2); + } + + dst[0] = color; + dst[1] = color; + + if (!_scanlines) { + dst[pitch] = color; + dst[pitch + 1] = color; + } + + dst += 2; + prevOn = curOn; + } + } +} + +Display::PixelPos Display::getPixelPos(byte x, byte y) { + PixelPos pixelPos; + + // FIXME: check X, Y range + + byte offsetL = y & 0xc0; + offsetL |= offsetL >> 2; + byte offsetH = y; + y <<= 2; + offsetH <<= 1; + offsetH |= y >> 7; + y <<= 1; + offsetH <<= 1; + offsetH |= y >> 7; + y <<= 1; + offsetL >>= 1; + offsetL |= y & 0x80; + y <<= 1; + offsetH = offsetH & 0x1f; + pixelPos.rowAddr = (offsetH << 8) | offsetL; + pixelPos.byteOffset = x / 7; + pixelPos.bitMask = 0x80 | (1 << x % 7); + + return pixelPos; +} + +byte Display::getPixelColor(byte offset, byte color) { + if (offset & 1) { + byte c = color << 1; + if (c >= 0x40 && c < 0xc0) + return color ^ 0x7f; + } + + return color; +} + +void Display::decodeFrameBuffer() { + byte *src = _frameBuf; + int pitch = _frameBufSurface->pitch; + for (int j = 0; j < 8; ++j) { + for (int i = 0; i < 8; ++i) { + byte *dst = (byte *)_frameBufSurface->getPixels() + pitch * 2 * (i * 8 + j); + decodeScanline(dst, pitch, src); + src += 40; + dst += pitch * 2 * 64; + decodeScanline(dst, pitch, src); + src += 40; + dst += pitch * 2 * 64; + decodeScanline(dst, pitch, src); + src += 48; + dst += pitch * 2 * 64; + } + } +} + +void Display::drawPixel(byte x, byte y, byte color) { + PixelPos p = getPixelPos(x, y); + byte c = getPixelColor(p.byteOffset, color); + byte *b = _frameBuf + p.rowAddr + p.byteOffset; + c ^= *b; + c &= p.bitMask; + c ^= *b; + *b = c; +} + +void Display::moveX(PixelPos &p, byte &color, bool left) { + if (left) { + byte bit = p.bitMask; + bool b = bit & 1; + bit >>= 1; + if (!b) { + bit ^= 0xc0; + p.bitMask = bit; + return; + } + --p.byteOffset; + if (p.byteOffset & 0x80) + p.byteOffset = 39; + p.bitMask = 0xc0; + } else { + byte bit = p.bitMask; + bit <<= 1; + bit ^= 0x80; + if (bit & 0x80) { + p.bitMask = bit; + return; + } + p.bitMask = 0x81; + ++p.byteOffset; + if (p.byteOffset == 40) + p.byteOffset = 0; + } + + color = getPixelColor(p.byteOffset, color); +} + +void Display::moveY(PixelPos &p, bool down) { + if (!down) { + if (p.rowAddr & 0x1c00) + p.rowAddr -= 0x400; + else if (p.rowAddr & 0x380) + p.rowAddr += 0x1b80; + else { + p.rowAddr += 0x1f58; + if (!(p.rowAddr & 0x80)) + p.rowAddr += 0x78; // Wrap around + } + } else { + p.rowAddr += 0x400; + if (p.rowAddr & 0x1c00) + return; + else if ((p.rowAddr & 0x380) != 0x380) + p.rowAddr -= 0x1f80; + else { + p.rowAddr -= 0x2358; + if ((p.rowAddr & 0x78) == 0x78) + p.rowAddr -= 0x78; // Wrap around + } + } +} + +void Display::drawNextPixel(Display::PixelPos &p, byte &color, byte bits, byte quadrant) { + if (bits & 4) { + byte b = (_frameBuf[p.rowAddr + p.byteOffset] ^ color) & p.bitMask; + _frameBuf[p.rowAddr + p.byteOffset] ^= b; + } + + bits += quadrant; + + if (bits & 1) + moveX(p, color, bits & 2); + else + moveY(p, bits & 2); +} + +void Display::drawRightAngles(Common::Array<byte> &rightAngles, Common::Point p, byte rotation, byte scaling, byte color) { + const byte stepping[] = { + 0xff, 0xfe, 0xfa, 0xf4, 0xec, 0xe1, 0xd4, 0xc5, + 0xb4, 0xa1, 0x8d, 0x78, 0x61, 0x49, 0x31, 0x18, + 0xff + }; + + PixelPos pos = getPixelPos(p.x, p.y); + byte c = getPixelColor(pos.byteOffset, color); + + byte quadrant = rotation >> 4; + rotation &= 0xf; + byte xStep = stepping[rotation]; + byte yStep = stepping[(rotation ^ 0xf) + 1] + 1; + + for (uint i = 0; i < rightAngles.size(); ++i) { + byte b = rightAngles[i]; + + do { + byte xFrac = 0x80; + byte yFrac = 0x80; + for (uint j = 0; j < scaling; ++j) { + if (xFrac + xStep + 1 > 255) + drawNextPixel(pos, c, b, quadrant); + xFrac += xStep + 1; + if (yFrac + yStep > 255) + drawNextPixel(pos, c, b, quadrant + 1); + yFrac += yStep; + } + b >>= 3; + } while (b != 0); + } +} + +void Display::drawLine(Common::Point p1, Common::Point p2, byte color) { + PixelPos p = getPixelPos(p1.x, p1.y); + byte c = getPixelColor(p.byteOffset, color); + + int16 deltaX = p2.x - p1.x; + byte dir = deltaX >> 8; + + if (deltaX < 0) + deltaX = -deltaX; + + int16 err = deltaX; + + int16 deltaY = p2.y - p1.y - 1; + dir >>= 1; + if (deltaY >= 0) { + deltaY = -deltaY - 2; + dir |= 0x80; + } + + int16 steps = deltaY - deltaX; + + err += deltaY + 1; + + while (1) { + byte *b = _frameBuf + p.rowAddr + p.byteOffset; + byte d = *b; + d ^= c; + d &= p.bitMask; + d ^= *b; + *b = d; + + if (++steps == 0) + return; + + if (err < 0) { + moveY(p, dir & 0x80); + err += deltaX; + } else { + moveX(p, c, dir & 0x40); + err += deltaY + 1; + } + } +} + +void Display::clear(byte color) { + for (uint i = 0; i < kFrameBufSize; ++i) + _frameBuf[i] = getPixelColor(i & 1, color); +} + +void Display::updateTextSurface() { + for (uint row = 0; row < 24; ++row) + for (uint col = 0; col < 40; ++col) { + char c = _textBuf[row * 40 + col]; + + Common::Rect r(7 * 2, 8 * 2); + r.translate(((c & 0x3f) % 16) * 7 * 2, (c & 0x3f) / 16 * 8 * 2); + + if (!(c & 0x80)) { + if (!(c & 0x40) || ((g_system->getMillis() / 270) & 1)) + r.translate(0, 4 * 8 * 2); + } + + _textBufSurface->copyRectToSurface(*_font, col * 7 * 2, row * 8 * 2, r); + } +} + +void Display::printString(const Common::String &str) { + Common::String::const_iterator it; + for (it = str.begin(); it != str.end(); ++it) { + byte b = *it; + + if (b == ('\r' | 0x80)) + _cursorPos = (_cursorPos / 40 + 1) * 40; + else if (b < 0x80 || b >= 0xa0) + _textBuf[_cursorPos++] = b; + + if (_cursorPos == kTextBufSize) { + memmove(_textBuf, _textBuf + 40, kTextBufSize - 40); + memset(_textBuf + kTextBufSize - 40, ' ' | 0x80, 40); + _cursorPos -= 40; + } + } + + updateTextSurface(); +} + +void Display::printASCIIString(const Common::String &str) { + Common::String aStr; + + Common::String::const_iterator it; + for (it = str.begin(); it != str.end(); ++it) + aStr += *it | 0x80; + + printString(aStr); +} + +void Display::drawChar(byte c, int x, int y) { + byte *buf = (byte *)_font->getPixels() + y * _font->pitch + x; + + for (uint row = 0; row < 8; ++row) { + for (uint col = 1; col < 6; ++col) + if (font[c][col - 1] & (1 << row)) { + buf[col * 2] = 1; + buf[col * 2 + 1] = 1; + + if (!_scanlines) { + buf[_font->pitch + col * 2] = 1; + buf[_font->pitch + col * 2 + 1] = 1; + } + } + + buf += 2 * _font->pitch; + } +} + +void Display::createFont() { + _font = new Graphics::Surface; + _font->create(16 * 7 * 2, 4 * 8 * 2 * 2, Graphics::PixelFormat::createFormatCLUT8()); + + for (uint i = 0; i < 4; ++i) + for (uint j = 0; j < 16; ++j) + drawChar(i * 16 + j, j * 7 * 2, i * 8 * 2); + + // Create inverted font + byte *buf = (byte *)_font->getPixels(); + byte *bufInv = buf + (_font->h / 2) * _font->pitch; + + for (uint row = 0; row < _font->h / 2; ++row) { + if (!_scanlines || !(row & 1)) + for (uint col = 0; col < _font->w; ++col) + bufInv[col] = buf[col] ? 0 : 1; + + buf += _font->pitch; + bufInv += _font->pitch; + } +} + +void Display::updateScreen() { + if (_mode == kModeText) { + g_system->copyRectToScreen(_textBufSurface->getPixels(), _textBufSurface->pitch, 0, 0, _textBufSurface->w, _textBufSurface->h); + } else if (_mode == kModeHires) { + g_system->copyRectToScreen(_frameBufSurface->getPixels(), _frameBufSurface->pitch, 0, 0, _frameBufSurface->w, _frameBufSurface->h); + } else { + g_system->copyRectToScreen(_frameBufSurface->getPixels(), _frameBufSurface->pitch, 0, 0, _frameBufSurface->w, _frameBufSurface->h - 4 * 8 * 2); + g_system->copyRectToScreen(_textBufSurface->getBasePtr(0, _textBufSurface->h - 4 * 8 * 2), _textBufSurface->pitch, 0, _textBufSurface->h - 4 * 8 * 2, _textBufSurface->w, 4 * 8 * 2); + } +} + +Common::String Display::inputString(byte prompt) { + Common::String s; + + if (prompt > 0) + printString(Common::String(prompt)); + + while (1) { + byte b = inputKey(); + + if (g_engine->shouldQuit()) + return 0; + + if (b == 0) + continue; + + if (b == ('\r' | 0x80)) { + s += b; + printString(Common::String(b)); + return s; + } + + if (b < 0xa0) { + switch (b) { + case Common::KEYCODE_BACKSPACE | 0x80: + if (!s.empty()) { + --_cursorPos; + _textBuf[_cursorPos] = ' ' | 0x80; + s.deleteLastChar(); + } + break; + }; + } else { + s += b; + printString(Common::String(b)); + } + } +} + +byte Display::convertKey(uint16 ascii) { + ascii = toupper(ascii); + + if (ascii >= 0x80) + return 0; + + ascii |= 0x80; + + if (ascii >= 0x80 && ascii <= 0xe0) + return ascii; + + return 0; +} + +byte Display::inputKey() { + Common::EventManager *ev = g_system->getEventManager(); + + byte orgChar = _textBuf[_cursorPos]; + _textBuf[_cursorPos] = (orgChar & 0x3f) | 0x40; + + byte key = 0; + + while (!g_engine->shouldQuit() && key == 0) { + Common::Event event; + if (ev->pollEvent(event)) { + if (event.type != Common::EVENT_KEYDOWN) + continue; + + if (event.kbd.flags & Common::KBD_CTRL) { + if (event.kbd.keycode == Common::KEYCODE_q) + g_engine->quitGame(); + continue; + } + + switch (event.kbd.keycode) { + case Common::KEYCODE_BACKSPACE: + case Common::KEYCODE_RETURN: + key = convertKey(event.kbd.keycode); + break; + default: + if (event.kbd.ascii >= 0x20 && event.kbd.ascii < 0x80) + key = convertKey(event.kbd.ascii); + }; + } + + updateTextSurface(); + updateScreen(); + g_system->updateScreen(); + g_system->delayMillis(16); + } + + _textBuf[_cursorPos] = orgChar; + return key; +} + +void Display::delay(uint32 ms) { + Common::EventManager *ev = g_system->getEventManager(); + + uint32 start = g_system->getMillis(); + + while (!g_engine->shouldQuit() && g_system->getMillis() - start < ms) { + Common::Event event; + if (ev->pollEvent(event)) { + if (event.type == Common::EVENT_KEYDOWN && (event.kbd.flags & Common::KBD_CTRL)) { + switch(event.kbd.keycode) { + case Common::KEYCODE_q: + g_engine->quitGame(); + break; + default: + break; + } + } + } + updateScreen(); + g_system->updateScreen(); + g_system->delayMillis(16); + } +} + +void Display::home() { + memset(_textBuf, ' ' | 0x80, kTextBufSize); + _cursorPos = 0; +} + +} // End of namespace Adl |