aboutsummaryrefslogtreecommitdiff
path: root/engines/adl/display.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'engines/adl/display.cpp')
-rw-r--r--engines/adl/display.cpp520
1 files changed, 520 insertions, 0 deletions
diff --git a/engines/adl/display.cpp b/engines/adl/display.cpp
new file mode 100644
index 0000000000..6342504bc3
--- /dev/null
+++ b/engines/adl/display.cpp
@@ -0,0 +1,520 @@
+/* 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 "common/stream.h"
+#include "common/rect.h"
+#include "common/system.h"
+#include "common/str.h"
+#include "common/config-manager.h"
+
+#include "graphics/surface.h"
+#include "graphics/palette.h"
+#include "graphics/thumbnail.h"
+
+#include "engines/util.h"
+
+#include "adl/display.h"
+
+namespace Adl {
+
+// This implements the Apple II "Hi-Res" display mode
+
+#define DISPLAY_PITCH (DISPLAY_WIDTH / 7)
+#define DISPLAY_SIZE (DISPLAY_PITCH * DISPLAY_HEIGHT)
+
+#define TEXT_WIDTH 40
+#define TEXT_HEIGHT 24
+#define TEXT_BUF_SIZE (TEXT_WIDTH * TEXT_HEIGHT)
+
+#define COLOR_PALETTE_ENTRIES 8
+static const byte colorPalette[COLOR_PALETTE_ENTRIES * 3] = {
+ 0x00, 0x00, 0x00,
+ 0xff, 0xff, 0xff,
+ 0xc7, 0x34, 0xff,
+ 0x38, 0xcb, 0x00,
+ 0x00, 0x00, 0x00,
+ 0xff, 0xff, 0xff,
+ 0x0d, 0xa1, 0xff,
+ 0xf2, 0x5e, 0x00
+};
+
+// Corresponding color in second palette
+#define PAL2(X) ((X) | 0x04)
+
+// Alternate color for odd pixel rows (for scanlines)
+#define ALTCOL(X) ((X) | 0x08)
+
+// Green monochrome palette
+#define MONO_PALETTE_ENTRIES 2
+static const byte monoPalette[MONO_PALETTE_ENTRIES * 3] = {
+ 0x00, 0x00, 0x00,
+ 0x00, 0xc0, 0x01
+};
+
+// Uppercase-only Apple II font (manually created).
+static const 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() :
+ _mode(DISPLAY_MODE_TEXT),
+ _cursorPos(0),
+ _showCursor(false) {
+
+ initGraphics(DISPLAY_WIDTH * 2, DISPLAY_HEIGHT * 2, true);
+
+ _monochrome = !ConfMan.getBool("color");
+ _scanlines = ConfMan.getBool("scanlines");
+
+ if (_monochrome)
+ g_system->getPaletteManager()->setPalette(monoPalette, 0, MONO_PALETTE_ENTRIES);
+ else
+ g_system->getPaletteManager()->setPalette(colorPalette, 0, COLOR_PALETTE_ENTRIES);
+
+ showScanlines(_scanlines);
+
+ _frameBuf = new byte[DISPLAY_SIZE];
+ memset(_frameBuf, 0, DISPLAY_SIZE);
+ _frameBufSurface = new Graphics::Surface;
+ // We need 2x scaling to properly render the half-pixel shift
+ // of the second palette
+ _frameBufSurface->create(DISPLAY_WIDTH * 2, DISPLAY_HEIGHT * 2, Graphics::PixelFormat::createFormatCLUT8());
+
+ _textBuf = new byte[TEXT_BUF_SIZE];
+ memset(_textBuf, APPLECHAR(' '), TEXT_BUF_SIZE);
+ _textBufSurface = new Graphics::Surface;
+ // For ease of copying, also use 2x scaling here
+ _textBufSurface->create(DISPLAY_WIDTH * 2, DISPLAY_HEIGHT * 2, Graphics::PixelFormat::createFormatCLUT8());
+
+ createFont();
+}
+
+Display::~Display() {
+ delete[] _frameBuf;
+ _frameBufSurface->free();
+ delete _frameBufSurface;
+
+ delete[] _textBuf;
+ _textBufSurface->free();
+ delete _textBufSurface;
+
+ _font->free();
+ delete _font;
+}
+
+void Display::setMode(DisplayMode mode) {
+ _mode = mode;
+
+ if (_mode == DISPLAY_MODE_TEXT || _mode == DISPLAY_MODE_MIXED)
+ updateTextScreen();
+ if (_mode == DISPLAY_MODE_HIRES || _mode == DISPLAY_MODE_MIXED)
+ updateHiResScreen();
+}
+
+void Display::updateTextScreen() {
+ updateTextSurface();
+
+ if (_mode == DISPLAY_MODE_TEXT)
+ g_system->copyRectToScreen(_textBufSurface->getPixels(), _textBufSurface->pitch, 0, 0, _textBufSurface->w, _textBufSurface->h);
+ else if (_mode == DISPLAY_MODE_MIXED)
+ g_system->copyRectToScreen(_textBufSurface->getBasePtr(0, _textBufSurface->h - 4 * 8 * 2), _textBufSurface->pitch, 0, _textBufSurface->h - 4 * 8 * 2, _textBufSurface->w, 4 * 8 * 2);
+
+ g_system->updateScreen();
+}
+
+void Display::updateHiResScreen() {
+ updateHiResSurface();
+
+ if (_mode == DISPLAY_MODE_HIRES)
+ g_system->copyRectToScreen(_frameBufSurface->getPixels(), _frameBufSurface->pitch, 0, 0, _frameBufSurface->w, _frameBufSurface->h);
+ else if (_mode == DISPLAY_MODE_MIXED)
+ g_system->copyRectToScreen(_frameBufSurface->getPixels(), _frameBufSurface->pitch, 0, 0, _frameBufSurface->w, _frameBufSurface->h - 4 * 8 * 2);
+
+ g_system->updateScreen();
+}
+
+bool Display::saveThumbnail(Common::WriteStream &out) {
+ if (_scanlines) {
+ showScanlines(false);
+ g_system->updateScreen();
+ }
+
+ bool retval = Graphics::saveThumbnail(out);
+
+ if (_scanlines) {
+ showScanlines(true);
+ g_system->updateScreen();
+ }
+
+ return retval;
+}
+
+void Display::loadFrameBuffer(Common::ReadStream &stream) {
+ byte *dst = _frameBuf;
+ for (uint j = 0; j < 8; ++j) {
+ for (uint i = 0; i < 8; ++i) {
+ stream.read(dst, DISPLAY_PITCH);
+ dst += DISPLAY_PITCH * 64;
+ stream.read(dst, DISPLAY_PITCH);
+ dst += DISPLAY_PITCH * 64;
+ stream.read(dst, DISPLAY_PITCH);
+ stream.readUint32LE();
+ stream.readUint32LE();
+ dst -= DISPLAY_PITCH * 120;
+ }
+ dst -= DISPLAY_PITCH * 63;
+ }
+
+ if (stream.eos() || stream.err())
+ error("Failed to read frame buffer");
+}
+
+void Display::putPixel(const Common::Point &p, byte color) {
+ byte offset = p.x / 7;
+
+ if (offset & 1) {
+ byte c = color << 1;
+ if (c >= 0x40 && c < 0xc0)
+ color ^= 0x7f;
+ }
+
+ byte *b = _frameBuf + p.y * DISPLAY_PITCH + offset;
+ color ^= *b;
+ color &= 1 << (p.x % 7);
+ *b ^= color;
+}
+
+void Display::clear(byte color) {
+ byte val = 0;
+
+ byte c = color << 1;
+ if (c >= 0x40 && c < 0xc0)
+ val = 0x7f;
+
+ for (uint i = 0; i < DISPLAY_SIZE; ++i) {
+ _frameBuf[i] = color;
+ color ^= val;
+ }
+}
+
+void Display::home() {
+ memset(_textBuf, APPLECHAR(' '), TEXT_BUF_SIZE);
+ _cursorPos = 0;
+}
+
+void Display::moveCursorForward() {
+ ++_cursorPos;
+
+ if (_cursorPos >= TEXT_BUF_SIZE)
+ scrollUp();
+}
+
+void Display::moveCursorBackward() {
+ if (_cursorPos > 0)
+ --_cursorPos;
+}
+
+void Display::moveCursorTo(const Common::Point &pos) {
+ _cursorPos = pos.y * TEXT_WIDTH + pos.x;
+
+ if (_cursorPos >= TEXT_BUF_SIZE)
+ error("Cursor position (%i, %i) out of bounds", pos.x, pos.y);
+}
+
+void Display::printString(const Common::String &str) {
+ Common::String::const_iterator c;
+ for (c = str.begin(); c != str.end(); ++c) {
+ byte b = *c;
+
+ if (*c == APPLECHAR('\r'))
+ _cursorPos = (_cursorPos / TEXT_WIDTH + 1) * TEXT_WIDTH;
+ else if (b < 0x80 || b >= 0xa0) {
+ setCharAtCursor(b);
+ ++_cursorPos;
+ }
+
+ if (_cursorPos == TEXT_BUF_SIZE)
+ scrollUp();
+ }
+
+ updateTextScreen();
+}
+
+void Display::printAsciiString(const Common::String &str) {
+ Common::String aStr;
+
+ Common::String::const_iterator it;
+ for (it = str.begin(); it != str.end(); ++it)
+ aStr += APPLECHAR(*it);
+
+ printString(aStr);
+}
+
+void Display::setCharAtCursor(byte c) {
+ _textBuf[_cursorPos] = c;
+}
+
+void Display::showCursor(bool enable) {
+ _showCursor = enable;
+}
+
+void Display::showScanlines(bool enable) {
+ byte pal[COLOR_PALETTE_ENTRIES * 3] = { };
+
+ if (enable)
+ g_system->getPaletteManager()->setPalette(pal, COLOR_PALETTE_ENTRIES, COLOR_PALETTE_ENTRIES);
+ else {
+ g_system->getPaletteManager()->grabPalette(pal, 0, COLOR_PALETTE_ENTRIES);
+ g_system->getPaletteManager()->setPalette(pal, COLOR_PALETTE_ENTRIES, COLOR_PALETTE_ENTRIES);
+ }
+}
+
+static byte processColorBits(uint16 &bits, bool &odd, bool secondPal) {
+ byte color = 0;
+
+ switch (bits & 0x7) {
+ case 0x3: // 011 (white)
+ case 0x6: // 110
+ case 0x7: // 111
+ color = 1;
+ break;
+ case 0x2: // 010 (color)
+ color = 2 + odd;
+ break;
+ case 0x5: // 101 (color)
+ color = 2 + !odd;
+ }
+
+ if (secondPal)
+ color = PAL2(color);
+
+ odd = !odd;
+ bits >>= 1;
+
+ return color;
+}
+
+static void renderPixelRowColor(byte *dst, byte *src) {
+ uint16 bits = (src[0] & 0x7f) << 1;
+ byte pal = src[0] >> 7;
+
+ if (pal != 0)
+ *dst++ = 0;
+
+ bool odd = false;
+
+ for (uint i = 0; i < DISPLAY_PITCH; ++i) {
+ if (i != DISPLAY_PITCH - 1) {
+ bits |= (src[i + 1] & 0x7f) << 8;
+ pal |= (src[i + 1] >> 7) << 1;
+ }
+
+ // For the first 6 bits in the block we draw two pixels
+ for (uint j = 0; j < 6; ++j) {
+ byte color = processColorBits(bits, odd, pal & 1);
+ *dst++ = color;
+ *dst++ = color;
+ }
+
+ // Last bit of the block, draw one, two or three pixels
+ byte color = processColorBits(bits, odd, pal & 1);
+
+ // Draw the first pixel
+ *dst++ = color;
+
+ switch (pal) {
+ case 0x0:
+ case 0x3:
+ // If palette stays the same, draw a second pixel
+ *dst++ = color;
+ break;
+ case 0x2:
+ // If we're moving from first to second palette,
+ // draw a second pixel, and a third in the second
+ // palette.
+ *dst++ = color;
+ *dst++ = PAL2(color);
+ }
+
+ pal >>= 1;
+ }
+}
+
+static void renderPixelRowMono(byte *dst, byte *src) {
+ byte pal = src[0] >> 7;
+
+ if (pal != 0)
+ *dst++ = 0;
+
+ for (uint i = 0; i < DISPLAY_PITCH; ++i) {
+ if (i != DISPLAY_PITCH - 1)
+ pal |= (src[i + 1] >> 7) << 1;
+
+ for (uint j = 0; j < 6; ++j) {
+ bool color = src[i] & (1 << j);
+ *dst++ = color;
+ *dst++ = color;
+ }
+
+ bool color = src[i] & (1 << 6);
+
+ *dst++ = color;
+
+ switch (pal) {
+ case 0x0:
+ case 0x3:
+ *dst++ = color;
+ break;
+ case 0x2:
+ *dst++ = color;
+ *dst++ = color;
+ }
+
+ pal >>= 1;
+ }
+}
+
+static void copyEvenSurfaceRows(Graphics::Surface &surf) {
+ byte *src = (byte *)surf.getPixels();
+
+ for (uint y = 0; y < surf.h / 2; ++y) {
+ byte *dst = src + surf.pitch;
+ for (uint x = 0; x < surf.w; ++x)
+ dst[x] = ALTCOL(src[x]);
+ src += surf.pitch * 2;
+ }
+}
+
+void Display::updateHiResSurface() {
+ byte *src = _frameBuf;
+ byte *dst = (byte *)_frameBufSurface->getPixels();
+
+ for (uint i = 0; i < DISPLAY_HEIGHT; ++i) {
+ if (_monochrome)
+ renderPixelRowMono(dst, src);
+ else
+ renderPixelRowColor(dst, src);
+ src += DISPLAY_PITCH;
+ dst += _frameBufSurface->pitch * 2;
+ }
+
+ copyEvenSurfaceRows(*_frameBufSurface);
+}
+
+void Display::updateTextSurface() {
+ for (uint row = 0; row < 24; ++row)
+ for (uint col = 0; col < TEXT_WIDTH; ++col) {
+ uint charPos = row * TEXT_WIDTH + col;
+ char c = _textBuf[row * TEXT_WIDTH + col];
+
+ if (charPos == _cursorPos && _showCursor)
+ c = (c & 0x3f) | 0x40;
+
+ 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::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;
+ }
+ }
+
+ 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 += 2) {
+ for (uint col = 0; col < _font->w; ++col)
+ bufInv[col] = (buf[col] ? 0 : 1);
+
+ buf += _font->pitch * 2;
+ bufInv += _font->pitch * 2;
+ }
+
+ copyEvenSurfaceRows(*_font);
+}
+
+void Display::scrollUp() {
+ memmove(_textBuf, _textBuf + TEXT_WIDTH, TEXT_BUF_SIZE - TEXT_WIDTH);
+ memset(_textBuf + TEXT_BUF_SIZE - TEXT_WIDTH, APPLECHAR(' '), TEXT_WIDTH);
+ if (_cursorPos >= TEXT_WIDTH)
+ _cursorPos -= TEXT_WIDTH;
+}
+
+} // End of namespace Adl