aboutsummaryrefslogtreecommitdiff
path: root/engines/cine/gfx.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'engines/cine/gfx.cpp')
-rw-r--r--engines/cine/gfx.cpp1404
1 files changed, 1203 insertions, 201 deletions
diff --git a/engines/cine/gfx.cpp b/engines/cine/gfx.cpp
index 2730a92e62..47446f2410 100644
--- a/engines/cine/gfx.cpp
+++ b/engines/cine/gfx.cpp
@@ -27,6 +27,7 @@
#include "cine/bg.h"
#include "cine/bg_list.h"
#include "cine/various.h"
+#include "cine/pal.h"
#include "common/endian.h"
#include "common/system.h"
@@ -35,18 +36,8 @@
namespace Cine {
-uint16 c_palette[256];
-byte colorMode256 = 0;
-byte palette256[256 * 3];
-byte newPalette[256 * 3];
-byte newColorMode = 0;
-byte ctColorMode = 0;
-byte bgColorMode = 0;
-
-byte *screenBuffer;
-byte *page1Raw;
-byte *page2Raw;
byte *page3Raw;
+FWRenderer *renderer = NULL;
static const byte mouseCursorNormal[] = {
0x00, 0x00, 0x40, 0x00, 0x60, 0x00, 0x70, 0x00,
@@ -96,28 +87,1197 @@ static const byte cursorPalette[] = {
0xff, 0xff, 0xff, 0xff
};
-void gfxInit() {
- screenBuffer = (byte *)malloc(320 * 200);
- page1Raw = (byte *)malloc(320 * 200);
- page2Raw = (byte *)malloc(320 * 200);
- page3Raw = (byte *)malloc(320 * 200);
- if (!screenBuffer || !page1Raw || !page2Raw || !page3Raw) {
- error("Unable to allocate offscreen buffers");
+/*! \brief Initialize renderer
+ */
+FWRenderer::FWRenderer() : _background(NULL), _palette(NULL), _cmd(""),
+ _cmdY(0), _messageBg(0), _backBuffer(new byte[_screenSize]),
+ _activeLowPal(NULL), _changePal(0) {
+
+ assert(_backBuffer);
+
+ memset(_backBuffer, 0, _screenSize);
+ memset(_bgName, 0, sizeof (_bgName));
+}
+
+/* \brief Destroy renderer
+ */
+FWRenderer::~FWRenderer() {
+ delete[] _background;
+ delete[] _palette;
+ delete[] _backBuffer;
+ delete[] _activeLowPal;
+}
+
+/* \brief Reset renderer state
+ */
+void FWRenderer::clear() {
+ delete[] _background;
+ delete[] _palette;
+ delete[] _activeLowPal;
+
+ _background = NULL;
+ _palette = NULL;
+ _activeLowPal = NULL;
+
+ memset(_backBuffer, 0, _screenSize);
+
+ _cmd = "";
+ _cmdY = 0;
+ _messageBg = 0;
+ _changePal = 0;
+}
+
+/*! \brief Draw 1bpp sprite using selected color
+ * \param obj Object info
+ * \param fillColor Sprite color
+ */
+void FWRenderer::fillSprite(const objectStruct &obj, uint8 color) {
+ const byte *data = animDataTable[obj.frame].data();
+ int x, y, width, height;
+
+ x = obj.x;
+ y = obj.y;
+ width = animDataTable[obj.frame]._realWidth;
+ height = animDataTable[obj.frame]._height;
+
+ gfxFillSprite(data, width, height, _backBuffer, x, y, color);
+}
+
+/*! \brief Draw 1bpp sprite using selected color on background
+ * \param obj Object info
+ * \param fillColor Sprite color
+ */
+void FWRenderer::incrustMask(const objectStruct &obj, uint8 color) {
+ const byte *data = animDataTable[obj.frame].data();
+ int x, y, width, height;
+
+ x = obj.x;
+ y = obj.y;
+ width = animDataTable[obj.frame]._realWidth;
+ height = animDataTable[obj.frame]._height;
+
+ gfxFillSprite(data, width, height, _background, x, y, color);
+}
+
+/*! \brief Draw color sprite using with external mask
+ * \param obj Object info
+ * \param mask External mask
+ */
+void FWRenderer::drawMaskedSprite(const objectStruct &obj, const byte *mask) {
+ const byte *data = animDataTable[obj.frame].data();
+ int x, y, width, height;
+
+ x = obj.x;
+ y = obj.y;
+ width = animDataTable[obj.frame]._realWidth;
+ height = animDataTable[obj.frame]._height;
+
+ assert(mask);
+
+ drawSpriteRaw(data, mask, width, height, _backBuffer, x, y);
+}
+
+/*! \brief Draw color sprite
+ * \param obj Object info
+ */
+void FWRenderer::drawSprite(const objectStruct &obj) {
+ const byte *mask = animDataTable[obj.frame].mask();
+ drawMaskedSprite(obj, mask);
+}
+
+/*! \brief Draw color sprite on background
+ * \param obj Object info
+ */
+void FWRenderer::incrustSprite(const objectStruct &obj) {
+ const byte *data = animDataTable[obj.frame].data();
+ const byte *mask = animDataTable[obj.frame].mask();
+ int x, y, width, height;
+
+ x = obj.x;
+ y = obj.y;
+ width = animDataTable[obj.frame]._realWidth;
+ height = animDataTable[obj.frame]._height;
+
+ assert(mask);
+
+ drawSpriteRaw(data, mask, width, height, _background, x, y);
+}
+
+/*! \brief Draw command box on screen
+ */
+void FWRenderer::drawCommand() {
+ unsigned int i;
+ int x = 10, y = _cmdY;
+
+ drawPlainBox(x, y, 301, 11, 0);
+ drawBorder(x - 1, y - 1, 302, 12, 2);
+
+ x += 2;
+ y += 2;
+
+ for (i = 0; i < _cmd.size(); i++) {
+ x = drawChar(_cmd[i], x, y);
+ }
+}
+
+/*! \brief Draw message in a box
+ * \param str Message to draw
+ * \param x Top left message box corner coordinate
+ * \param y Top left message box corner coordinate
+ * \param width Message box width
+ * \param color Message box background color
+ */
+void FWRenderer::drawMessage(const char *str, int x, int y, int width, byte color) {
+ int i, tx, ty, tw;
+ int line = 0, words = 0, cw = 0;
+ int space = 0, extraSpace = 0;
+
+ drawPlainBox(x, y, width, 4, color);
+ tx = x + 4;
+ ty = str[0] ? y - 5 : y + 4;
+ tw = width - 8;
+
+ for (i = 0; str[i]; i++, line--) {
+ // Fit line of text into textbox
+ if (!line) {
+ while (str[i] == ' ') i++;
+ line = fitLine(str + i, tw, words, cw);
+
+ if ( str[i + line] != '\0' && str[i + line] != 0x7C && words) {
+ space = (tw - cw) / words;
+ extraSpace = (tw - cw) % words;
+ } else {
+ space = 5;
+ extraSpace = 0;
+ }
+
+ ty += 9;
+ drawPlainBox(x, ty, width, 9, color);
+ tx = x + 4;
+ }
+
+ // draw characters
+ if (str[i] == ' ') {
+ tx += space + extraSpace;
+
+ if (extraSpace) {
+ extraSpace = 0;
+ }
+ } else {
+ tx = drawChar(str[i], tx, ty);
+ }
+ }
+
+ ty += 9;
+ drawPlainBox(x, ty, width, 4, color);
+ drawDoubleBorder(x, y, width, ty - y + 4, 2);
+}
+
+/*! \brief Draw rectangle on screen
+ * \param x Top left corner coordinate
+ * \param y Top left corner coordinate
+ * \param width Rectangle width
+ * \param height Rectangle height
+ * \param color Fill color
+ */
+void FWRenderer::drawPlainBox(int x, int y, int width, int height, byte color) {
+ int i;
+ byte *dest = _backBuffer + y * 320 + x;
+
+ if (width < 0) {
+ x += width;
+ width = -width;
}
- memset(page1Raw, 0, 320 * 200);
- memset(page2Raw, 0, 320 * 200);
- memset(page3Raw, 0, 320 * 200);
- memset(additionalBgTable, 0, sizeof(additionalBgTable));
- additionalBgTable[0] = page2Raw;
- additionalBgTable[8] = page3Raw;
+ if (height < 0) {
+ y += height;
+ height = -height;
+ }
+
+ for (i = 0; i < height; i++) {
+ memset(dest + i * 320, color, width);
+ }
+}
+
+/*! \brief Draw empty rectangle
+ * \param x Top left corner coordinate
+ * \param y Top left corner coordinate
+ * \param width Rectangle width
+ * \param height Rectangle height
+ * \param color Line color
+ */
+void FWRenderer::drawBorder(int x, int y, int width, int height, byte color) {
+ drawLine(x, y, width, 1, color);
+ drawLine(x, y + height, width, 1, color);
+ drawLine(x, y, 1, height, color);
+ drawLine(x + width, y, 1, height + 1, color);
+}
+
+/*! \brief Draw empty 2 color rectangle (inner line color is black)
+ * \param x Top left corner coordinate
+ * \param y Top left corner coordinate
+ * \param width Rectangle width
+ * \param height Rectangle height
+ * \param color Outter line color
+ */
+void FWRenderer::drawDoubleBorder(int x, int y, int width, int height, byte color) {
+ drawBorder(x + 1, y + 1, width - 2, height - 2, 0);
+ drawBorder(x, y, width, height, color);
+}
+
+/*! \brief Draw text character on screen
+ * \param character Character to draw
+ * \param x Character coordinate
+ * \param y Character coordinate
+ */
+int FWRenderer::drawChar(char character, int x, int y) {
+ int width, idx;
+
+ if (character == ' ') {
+ x += 5;
+ } else if ((width = fontParamTable[(unsigned char)character].characterWidth)) {
+ idx = fontParamTable[(unsigned char)character].characterIdx;
+ drawSpriteRaw(textTable[idx][0], textTable[idx][1], 16, 8, _backBuffer, x, y);
+ x += width + 1;
+ }
+
+ return x;
+}
+
+/*! \brief Draw Line
+ * \param x Line end coordinate
+ * \param y Line end coordinate
+ * \param width Horizontal line length
+ * \param height Vertical line length
+ * \param color Line color
+ * \note Either width or height must be equal to 1
+ */
+void FWRenderer::drawLine(int x, int y, int width, int height, byte color) {
+ // this line is a special case of rectangle ;-)
+ drawPlainBox(x, y, width, height, color);
+}
+
+/*! \brief Hide invisible parts of the sprite
+ * \param[in,out] mask Mask to be updated
+ * \param it Overlay info from overlayList
+ */
+void FWRenderer::remaskSprite(byte *mask, Common::List<overlay>::iterator it) {
+ AnimData &sprite = animDataTable[objectTable[it->objIdx].frame];
+ int x, y, width, height, idx;
+ int mx, my, mw, mh;
+
+ x = objectTable[it->objIdx].x;
+ y = objectTable[it->objIdx].y;
+ width = sprite._realWidth;
+ height = sprite._height;
+
+ for (++it; it != overlayList.end(); ++it) {
+ if (it->type != 5) {
+ continue;
+ }
+
+ idx = ABS(objectTable[it->objIdx].frame);
+ mx = objectTable[it->objIdx].x;
+ my = objectTable[it->objIdx].y;
+ mw = animDataTable[idx]._realWidth;
+ mh = animDataTable[idx]._height;
+
+ gfxUpdateSpriteMask(mask, x, y, width, height, animDataTable[idx].data(), mx, my, mw, mh);
+ }
+}
+
+/*! \brief Draw background to backbuffer
+ */
+void FWRenderer::drawBackground() {
+ assert(_background);
+ memcpy(_backBuffer, _background, _screenSize);
+}
+
+/*! \brief Draw one overlay
+ * \param it Overlay info
+ */
+void FWRenderer::renderOverlay(const Common::List<overlay>::iterator &it) {
+ int idx, len, width;
+ objectStruct *obj;
+ AnimData *sprite;
+ byte *mask;
+
+ switch (it->type) {
+ // color sprite
+ case 0:
+ sprite = animDataTable + objectTable[it->objIdx].frame;
+ len = sprite->_realWidth * sprite->_height;
+ mask = new byte[len];
+ memcpy(mask, sprite->mask(), len);
+ remaskSprite(mask, it);
+ drawMaskedSprite(objectTable[it->objIdx], mask);
+ delete[] mask;
+ break;
+
+ // game message
+ case 2:
+ if (it->objIdx >= messageTable.size()) {
+ return;
+ }
+
+ _messageLen += messageTable[it->objIdx].size();
+ drawMessage(messageTable[it->objIdx].c_str(), it->x, it->y, it->width, it->color);
+ break;
+
+ // action failure message
+ case 3:
+ idx = it->objIdx * 4 + g_cine->_rnd.getRandomNumber(3);
+ len = strlen(failureMessages[idx]);
+ _messageLen += len;
+ width = 6 * len + 20;
+ width = width > 300 ? 300 : width;
+
+ drawMessage(failureMessages[idx], (320 - width) / 2, 80, width, 4);
+ break;
+
+ // bitmap
+ case 4:
+ assert(it->objIdx < NUM_MAX_OBJECT);
+ obj = objectTable + it->objIdx;
+
+ if (obj->frame < 0) {
+ return;
+ }
+
+ if (!animDataTable[obj->frame].data()) {
+ return;
+ }
+
+ fillSprite(*obj);
+ break;
+ }
}
-void gfxDestroy() {
- free(screenBuffer);
- free(page1Raw);
- free(page2Raw);
- free(page3Raw);
+/*! \brief Draw overlays
+ */
+void FWRenderer::drawOverlays() {
+ Common::List<overlay>::iterator it;
+
+ for (it = overlayList.begin(); it != overlayList.end(); ++it) {
+ renderOverlay(it);
+ }
+}
+
+/*! \brief Draw another frame
+ */
+void FWRenderer::drawFrame() {
+ drawBackground();
+ drawOverlays();
+
+ if (!_cmd.empty()) {
+ drawCommand();
+ }
+
+ if (_changePal) {
+ refreshPalette();
+ }
+
+ blit();
+}
+
+/*! \brief Update screen
+ */
+void FWRenderer::blit() {
+ g_system->copyRectToScreen(_backBuffer, 320, 0, 0, 320, 200);
+}
+
+/*! \brief Set player command string
+ * \param cmd New command string
+ */
+void FWRenderer::setCommand(const char *cmd) {
+ _cmd = cmd;
+}
+
+/*! \brief Refresh current palette
+ */
+void FWRenderer::refreshPalette() {
+ int i;
+ byte pal[16*4];
+
+ assert(_activeLowPal);
+
+ for (i = 0; i < 16; i++) {
+ // This seems to match the output from DOSbox.
+ pal[i * 4 + 2] = ((_activeLowPal[i] & 0x00f) >> 0) * 32;
+ pal[i * 4 + 1] = ((_activeLowPal[i] & 0x0f0) >> 4) * 32;
+ pal[i * 4 + 0] = ((_activeLowPal[i] & 0xf00) >> 8) * 32;
+ pal[i * 4 + 3] = 0;
+ }
+
+ g_system->setPalette(pal, 0, 16);
+ _changePal = 0;
+}
+
+/*! \brief Load palette of current background
+ */
+void FWRenderer::reloadPalette() {
+ assert(_palette);
+
+ if (!_activeLowPal) {
+ _activeLowPal = new uint16[_lowPalSize];
+ }
+
+ assert(_activeLowPal);
+
+ memcpy(_activeLowPal, _palette, _lowPalSize * sizeof (uint16));
+ _changePal = 1;
+}
+
+/*! \brief Load background into renderer
+ * \param bg Raw background data
+ */
+void FWRenderer::loadBg16(const byte *bg, const char *name) {
+ int i;
+
+ if (!_background) {
+ _background = new byte[_screenSize];
+ }
+
+ if (!_palette) {
+ _palette = new uint16[_lowPalSize];
+ }
+
+ assert(_background && _palette);
+
+ strcpy(_bgName, name);
+
+ for (i = 0; i < _lowPalSize; i++, bg += 2) {
+ _palette[i] = READ_BE_UINT16(bg);
+ }
+
+ gfxConvertSpriteToRaw(_background, bg, 160, 200);
+}
+
+/*! \brief Placeholder for Operation Stealth implementation
+ */
+void FWRenderer::loadBg16(const byte *bg, const char *name, unsigned int idx) {
+ error("Future Wars renderer doesn't support multiple backgrounds");
+}
+
+/*! \brief Placeholder for Operation Stealth implementation
+ */
+void FWRenderer::loadCt16(const byte *ct, const char *name) {
+ error("Future Wars renderer doesn't support multiple backgrounds");
+}
+
+/*! \brief Placeholder for Operation Stealth implementation
+ */
+void FWRenderer::loadBg256(const byte *bg, const char *name) {
+ error("Future Wars renderer doesn't support 256 color mode");
+}
+
+/*! \brief Placeholder for Operation Stealth implementation
+ */
+void FWRenderer::loadBg256(const byte *bg, const char *name, unsigned int idx) {
+ error("Future Wars renderer doesn't support multiple backgrounds");
+}
+
+/*! \brief Placeholder for Operation Stealth implementation
+ */
+void FWRenderer::loadCt256(const byte *ct, const char *name) {
+ error("Future Wars renderer doesn't support multiple backgrounds");
+}
+
+/*! \brief Placeholder for Operation Stealth implementation
+ */
+void FWRenderer::selectBg(unsigned int idx) {
+ error("Future Wars renderer doesn't support multiple backgrounds");
+}
+
+/*! \brief Placeholder for Operation Stealth implementation
+ */
+void FWRenderer::selectScrollBg(unsigned int idx) {
+ error("Future Wars renderer doesn't support multiple backgrounds");
+}
+
+/*! \brief Placeholder for Operation Stealth implementation
+ */
+void FWRenderer::setScroll(unsigned int shift) {
+ error("Future Wars renderer doesn't support multiple backgrounds");
+}
+
+/*! \brief Placeholder for Operation Stealth implementation
+ */
+void FWRenderer::removeBg(unsigned int idx) {
+ error("Future Wars renderer doesn't support multiple backgrounds");
+}
+
+void FWRenderer::saveBg(Common::OutSaveFile &fHandle) {
+ fHandle.write(_bgName, 13);
+}
+
+/*! \brief Restore active and backup palette from save
+ * \param fHandle Savefile open for reading
+ */
+void FWRenderer::restorePalette(Common::InSaveFile &fHandle) {
+ int i;
+
+ if (!_palette) {
+ _palette = new uint16[_lowPalSize];
+ }
+
+ if (!_activeLowPal) {
+ _activeLowPal = new uint16[_lowPalSize];
+ }
+
+ assert(_palette && _activeLowPal);
+
+ for (i = 0; i < _lowPalSize; i++) {
+ _activeLowPal[i] = fHandle.readUint16BE();
+ }
+
+ for (i = 0; i < _lowPalSize; i++) {
+ _palette[i] = fHandle.readUint16BE();
+ }
+
+ _changePal = 1;
+}
+
+/*! \brief Write active and backup palette to save
+ * \param fHandle Savefile open for writing
+ */
+void FWRenderer::savePalette(Common::OutSaveFile &fHandle) {
+ int i;
+
+ assert(_palette && _activeLowPal);
+
+ for (i = 0; i < _lowPalSize; i++) {
+ fHandle.writeUint16BE(_activeLowPal[i]);
+ }
+
+ for (i = 0; i < _lowPalSize; i++) {
+ fHandle.writeUint16BE(_palette[i]);
+ }
+}
+
+/*! \brief Rotate active palette
+ * \param a First color to rotate
+ * \param b Last color to rotate
+ * \param c Possibly rotation step, must be equal to 1 at the moment
+ */
+void FWRenderer::rotatePalette(int a, int b, int c) {
+ palRotate(_activeLowPal, a, b, c);
+ refreshPalette();
+}
+
+/*! \brief Copy part of backup palette to active palette and transform
+ * \param first First color to transform
+ * \param last Last color to transform
+ * \param r Red channel transformation
+ * \param g Green channel transformation
+ * \param b Blue channel transformation
+ */
+void FWRenderer::transformPalette(int first, int last, int r, int g, int b) {
+ if (!_activeLowPal) {
+ _activeLowPal = new uint16[_lowPalSize];
+ memset(_activeLowPal, 0, _lowPalSize * sizeof (uint16));
+ }
+
+ transformPaletteRange(_activeLowPal, _palette, first, last, r, g, b);
+ refreshPalette();
+}
+
+/*! \brief Draw menu box, one item per line with possible highlight
+ * \param items Menu items
+ * \param height Item count
+ * \param x Top left menu corner coordinate
+ * \param y Top left menu corner coordinate
+ * \param width Menu box width
+ * \param selected Index of highlighted item (no highlight if less than 0)
+ */
+void FWRenderer::drawMenu(const CommandeType *items, unsigned int height, int x, int y, int width, int selected) {
+ int tx, ty, th = height * 9 + 10;
+ unsigned int i, j;
+
+ if (x + width > 319) {
+ x = 319 - width;
+ }
+
+ if (y + th > 199) {
+ y = 199 - th;
+ }
+
+ drawPlainBox(x, y, width, 4, _messageBg);
+
+ ty = y + 4;
+
+ for (i = 0; i < height; i++, ty += 9) {
+ drawPlainBox(x, ty, width, 9, (int)i == selected ? 0 : _messageBg);
+ tx = x + 4;
+
+ for (j = 0; items[i][j]; j++) {
+ tx = drawChar(items[i][j], tx, ty);
+ }
+ }
+
+ drawPlainBox(x, ty, width, 4, _messageBg);
+ drawDoubleBorder(x, y, width, ty - y + 4, 2);
+}
+
+/*! \brief Draw text input box
+ * \param info Input box message
+ * \param input Text entered in the input area
+ * \param cursor Cursor position in the input area
+ * \param x Top left input box corner coordinate
+ * \param y Top left input box corner coordinate
+ * \param width Input box width
+ */
+void FWRenderer::drawInputBox(const char *info, const char *input, int cursor, int x, int y, int width) {
+ int i, tx, ty, tw;
+ int line = 0, words = 0, cw = 0;
+ int space = 0, extraSpace = 0;
+
+ drawPlainBox(x, y, width, 4, _messageBg);
+ tx = x + 4;
+ ty = info[0] ? y - 5 : y + 4;
+ tw = width - 8;
+
+ // input box info message
+ for (i = 0; info[i]; i++, line--) {
+ // fit line of text
+ if (!line) {
+ line = fitLine(info + i, tw, words, cw);
+
+ if ( info[i + line] != '\0' && words) {
+ space = (tw - cw) / words;
+ extraSpace = (tw - cw) % words;
+ } else {
+ space = 5;
+ extraSpace = 0;
+ }
+
+ ty += 9;
+ drawPlainBox(x, ty, width, 9, _messageBg);
+ tx = x + 4;
+ }
+
+ // draw characters
+ if (info[i] == ' ') {
+ tx += space + extraSpace;
+
+ if (extraSpace) {
+ extraSpace = 0;
+ }
+ } else {
+ tx = drawChar(info[i], tx, ty);
+ }
+ }
+
+ // input area background
+ ty += 9;
+ drawPlainBox(x, ty, width, 9, _messageBg);
+ drawPlainBox(x + 16, ty - 1, width - 32, 9, 0);
+ tx = x + 20;
+
+ // text in input area
+ for (i = 0; input[i]; i++) {
+ tx = drawChar(input[i], tx, ty);
+
+ if (cursor == i + 2) {
+ drawLine(tx, ty - 1, 1, 9, 2);
+ }
+ }
+
+ if (!input[0] || cursor == 1) {
+ drawLine(x + 20, ty - 1, 1, 9, 2);
+ }
+
+ ty += 9;
+ drawPlainBox(x, ty, width, 4, _messageBg);
+ drawDoubleBorder(x, y, width, ty - y + 4, 2);
+}
+
+/*! \brief Fade to black
+ */
+void FWRenderer::fadeToBlack() {
+ // FIXME: _activeLowPal is invalid when starting Operation Stealth
+ // Adding this sanity check fixes a crash when the game
+ // starts, but I'm not sure if this is the best place to check it
+ if (!_activeLowPal) {
+ warning("_activeLowPal is invalid");
+ return;
+ }
+
+ assert(_activeLowPal);
+
+ for (int i = 0; i < 8; i++) {
+ for (int j = 0; j < 16; j++) {
+ _activeLowPal[j] = transformColor(_activeLowPal[j], -1, -1, -1);
+ }
+
+ refreshPalette();
+ g_system->updateScreen();
+ g_system->delayMillis(50);
+ }
+}
+
+/*! \brief Initialize Operation Stealth renderer
+ */
+OSRenderer::OSRenderer() : _activeHiPal(NULL), _currentBg(0), _scrollBg(0),
+ _bgShift(0) {
+
+ int i;
+ for (i = 0; i < 9; i++) {
+ _bgTable[i].bg = NULL;
+ _bgTable[i].lowPal = NULL;
+ _bgTable[i].hiPal = NULL;
+ memset(_bgTable[i].name, 0, sizeof (_bgTable[i].name));
+ }
+}
+
+/*! \brief Destroy Operation Stealth renderer
+ */
+OSRenderer::~OSRenderer() {
+ delete[] _activeHiPal;
+
+ for (int i = 0; i < 9; i++) {
+ delete[] _bgTable[i].bg;
+ delete[] _bgTable[i].lowPal;
+ delete[] _bgTable[i].hiPal;
+ }
+}
+
+/*! \brief Reset Operation Stealth renderer state
+ */
+void OSRenderer::clear() {
+ delete[] _activeHiPal;
+ _activeHiPal = NULL;
+
+ for (int i = 0; i < 9; i++) {
+ delete[] _bgTable[i].bg;
+ delete[] _bgTable[i].lowPal;
+ delete[] _bgTable[i].hiPal;
+
+ _bgTable[i].bg = NULL;
+ _bgTable[i].lowPal = NULL;
+ _bgTable[i].hiPal = NULL;
+ memset(_bgTable[i].name, 0, sizeof (_bgTable[i].name));
+ }
+
+ _currentBg = 0;
+ _scrollBg = 0;
+ _bgShift = 0;
+
+ FWRenderer::clear();
+}
+
+/*! \brief Draw 1bpp sprite using selected color on backgrounds
+ * \param obj Object info
+ * \param fillColor Sprite color
+ */
+void OSRenderer::incrustMask(const objectStruct &obj, uint8 color) {
+ const byte *data = animDataTable[obj.frame].data();
+ int x, y, width, height, i;
+
+ x = obj.x;
+ y = obj.y;
+ width = animDataTable[obj.frame]._realWidth;
+ height = animDataTable[obj.frame]._height;
+
+ for (i = 0; i < 8; i++) {
+ if (!_bgTable[i].bg) {
+ continue;
+ }
+
+ gfxFillSprite(data, width, height, _bgTable[i].bg, x, y, color);
+ }
+}
+
+/*! \brief Draw color sprite
+ * \param obj Object info
+ */
+void OSRenderer::drawSprite(const objectStruct &obj) {
+ const byte *data = animDataTable[obj.frame].data();
+ int x, y, width, height, transColor;
+
+ x = obj.x;
+ y = obj.y;
+ transColor = obj.part;
+ width = animDataTable[obj.frame]._realWidth;
+ height = animDataTable[obj.frame]._height;
+
+ drawSpriteRaw2(data, transColor, width, height, _backBuffer, x, y);
+}
+
+/*! \brief Draw color sprite
+ * \param obj Object info
+ */
+void OSRenderer::incrustSprite(const objectStruct &obj) {
+ const byte *data = animDataTable[obj.frame].data();
+ int x, y, width, height, transColor, i;
+
+ x = obj.x;
+ y = obj.y;
+ transColor = obj.part;
+ width = animDataTable[obj.frame]._realWidth;
+ height = animDataTable[obj.frame]._height;
+
+ for (i = 0; i < 8; i++) {
+ if (!_bgTable[i].bg) {
+ continue;
+ }
+
+ drawSpriteRaw2(data, transColor, width, height, _bgTable[i].bg, x, y);
+ }
+}
+
+/*! \brief Draw text character on screen
+ * \param character Character to draw
+ * \param x Character coordinate
+ * \param y Character coordinate
+ */
+int OSRenderer::drawChar(char character, int x, int y) {
+ int width, idx;
+
+ if (character == ' ') {
+ x += 5;
+ } else if ((width = fontParamTable[(unsigned char)character].characterWidth)) {
+ idx = fontParamTable[(unsigned char)character].characterIdx;
+ drawSpriteRaw2(textTable[idx][0], 0, 16, 8, _backBuffer, x, y);
+ x += width + 1;
+ }
+
+ return x;
+}
+
+/*! \brief Draw background to backbuffer
+ */
+void OSRenderer::drawBackground() {
+ byte *main;
+
+ main = _bgTable[_currentBg].bg;
+ assert(main);
+
+ if (!_bgShift) {
+ memcpy(_backBuffer, main, _screenSize);
+ } else {
+ byte *scroll = _bgTable[_scrollBg].bg;
+ int mainShift = _bgShift * _screenWidth;
+ int mainSize = _screenSize - mainShift;
+
+ assert(scroll);
+
+ memcpy(_backBuffer, main + mainShift, mainSize);
+ memcpy(_backBuffer + mainSize, scroll, mainShift);
+ }
+}
+
+/*! \brief Draw one overlay
+ * \param it Overlay info
+ */
+void OSRenderer::renderOverlay(const Common::List<overlay>::iterator &it) {
+ int len;
+ objectStruct *obj;
+ AnimData *sprite;
+ byte *mask;
+
+ switch (it->type) {
+ // color sprite
+ case 0:
+ sprite = animDataTable + objectTable[it->objIdx].frame;
+ len = sprite->_realWidth * sprite->_height;
+ mask = new byte[len];
+ generateMask(sprite->data(), mask, len, objectTable[it->objIdx].part);
+ remaskSprite(mask, it);
+ drawMaskedSprite(objectTable[it->objIdx], mask);
+ delete[] mask;
+ break;
+
+ // masked background
+ case 20:
+ assert(it->objIdx < NUM_MAX_OBJECT);
+ obj = objectTable + it->objIdx;
+ sprite = animDataTable + obj->frame;
+
+ if (obj->frame < 0 || it->x > 8 || !_bgTable[it->x].bg || sprite->_bpp != 1) {
+ break;
+ }
+
+ maskBgOverlay(_bgTable[it->x].bg, sprite->data(), sprite->_realWidth, sprite->_height, _backBuffer, obj->x, obj->y);
+ break;
+
+ // something else
+ default:
+ FWRenderer::renderOverlay(it);
+ break;
+ }
+}
+
+/*! \brief Refresh current palette
+ */
+void OSRenderer::refreshPalette() {
+ if (!_activeHiPal) {
+ FWRenderer::refreshPalette();
+ return;
+ }
+
+ int i;
+ byte pal[256*4];
+
+ for (i = 0; i < 256; i++) {
+ pal[i * 4 + 0] = _activeHiPal[i * 3 + 0];
+ pal[i * 4 + 1] = _activeHiPal[i * 3 + 1];
+ pal[i * 4 + 2] = _activeHiPal[i * 3 + 2];
+ pal[i * 4 + 3] = 0;
+ }
+
+ g_system->setPalette(pal, 0, 256);
+ _changePal = 0;
+}
+
+/*! \brief Load palette of current background
+ */
+void OSRenderer::reloadPalette() {
+ // selected background in plane takeoff scene has swapped colors 12
+ // and 14, shift background has it right
+ palBg *bg = _bgShift ? &_bgTable[_scrollBg] : &_bgTable[_currentBg];
+
+ assert(bg->lowPal || bg->hiPal);
+
+ if (bg->lowPal) {
+ if (!_activeLowPal) {
+ _activeLowPal = new uint16[_lowPalSize];
+ }
+
+ assert(_activeLowPal);
+
+ delete[] _activeHiPal;
+ _activeHiPal = NULL;
+
+ memcpy(_activeLowPal, bg->lowPal, _lowPalSize * sizeof (uint16));
+ } else {
+ if (!_activeHiPal) {
+ _activeHiPal = new byte[_hiPalSize];
+ }
+
+ assert(_activeHiPal);
+
+ delete[] _activeLowPal;
+ _activeLowPal = NULL;
+
+ memcpy(_activeHiPal, bg->hiPal, _hiPalSize);
+ }
+ _changePal = 1;
+}
+
+/*! \brief Rotate active palette
+ * \param a First color to rotate
+ * \param b Last color to rotate
+ * \param c Possibly rotation step, must be equal to 1 at the moment
+ */
+void OSRenderer::rotatePalette(int a, int b, int c) {
+ if (_activeLowPal) {
+ FWRenderer::rotatePalette(a, b, c);
+ return;
+ }
+
+ palRotate(_activeHiPal, a, b, c);
+ refreshPalette();
+}
+
+/*! \brief Copy part of backup palette to active palette and transform
+ * \param first First color to transform
+ * \param last Last color to transform
+ * \param r Red channel transformation
+ * \param g Green channel transformation
+ * \param b Blue channel transformation
+ */
+void OSRenderer::transformPalette(int first, int last, int r, int g, int b) {
+ palBg *bg = _bgShift ? &_bgTable[_scrollBg] : &_bgTable[_currentBg];
+
+ if (!bg->lowPal) {
+ if (!_activeHiPal) {
+ _activeHiPal = new byte[_hiPalSize];
+ memset(_activeHiPal, 0, _hiPalSize);
+ }
+
+ delete[] _activeLowPal;
+ _activeLowPal = NULL;
+
+ transformPaletteRange(_activeHiPal, bg->hiPal, first, last, r, g, b);
+ } else {
+ if (!_activeLowPal) {
+ _activeLowPal = new uint16[_lowPalSize];
+ memset(_activeLowPal, 0, _lowPalSize * sizeof (uint16));
+ }
+
+ delete[] _activeHiPal;
+ _activeHiPal = NULL;
+
+ transformPaletteRange(_activeLowPal, bg->lowPal, first, last, r, g, b);
+ }
+
+ refreshPalette();
+}
+
+/*! \brief Load 16 color background into renderer
+ * \param bg Raw background data
+ * \param name Background filename
+ */
+void OSRenderer::loadBg16(const byte *bg, const char *name) {
+ loadBg16(bg, name, 0);
+}
+
+/*! \brief Load 16 color background into renderer
+ * \param bg Raw background data
+ * \param name Background filename
+ * \param pos Background index
+ */
+void OSRenderer::loadBg16(const byte *bg, const char *name, unsigned int idx) {
+ int i;
+ assert(idx < 9);
+
+ if (!_bgTable[idx].bg) {
+ _bgTable[idx].bg = new byte[_screenSize];
+ }
+
+ if (!_bgTable[idx].lowPal) {
+ _bgTable[idx].lowPal = new uint16[_lowPalSize];
+ }
+
+ assert(_bgTable[idx].bg && _bgTable[idx].lowPal);
+
+ delete[] _bgTable[idx].hiPal;
+ _bgTable[idx].hiPal = NULL;
+
+ strcpy(_bgTable[idx].name, name);
+
+ for (i = 0; i < _lowPalSize; i++, bg += 2) {
+ _bgTable[idx].lowPal[i] = READ_BE_UINT16(bg);
+ }
+
+ gfxConvertSpriteToRaw(_bgTable[idx].bg, bg, 160, 200);
+}
+
+/*! \brief Load 16 color CT data as background into renderer
+ * \param ct Raw CT data
+ * \param name Background filename
+ */
+void OSRenderer::loadCt16(const byte *ct, const char *name) {
+ loadBg16(ct, name, 8);
+}
+
+/*! \brief Load 256 color background into renderer
+ * \param bg Raw background data
+ * \param name Background filename
+ */
+void OSRenderer::loadBg256(const byte *bg, const char *name) {
+ loadBg256(bg, name, 0);
+}
+
+/*! \brief Load 256 color background into renderer
+ * \param bg Raw background data
+ * \param name Background filename
+ * \param pos Background index
+ */
+void OSRenderer::loadBg256(const byte *bg, const char *name, unsigned int idx) {
+ assert(idx < 9);
+
+ if (!_bgTable[idx].bg) {
+ _bgTable[idx].bg = new byte[_screenSize];
+ }
+
+ if (!_bgTable[idx].hiPal) {
+ _bgTable[idx].hiPal = new byte[_hiPalSize];
+ }
+
+ assert(_bgTable[idx].bg && _bgTable[idx].hiPal);
+
+ delete[] _bgTable[idx].lowPal;
+ _bgTable[idx].lowPal = NULL;
+
+ strcpy(_bgTable[idx].name, name);
+ memcpy(_bgTable[idx].hiPal, bg, _hiPalSize);
+ memcpy(_bgTable[idx].bg, bg + _hiPalSize, _screenSize);
+}
+
+/*! \brief Load 256 color CT data as background into renderer
+ * \param ct Raw CT data
+ * \param name Background filename
+ */
+void OSRenderer::loadCt256(const byte *ct, const char *name) {
+ loadBg256(ct, name, 8);
+}
+
+/*! \brief Select active background and load its palette
+ * \param idx Background index
+ */
+void OSRenderer::selectBg(unsigned int idx) {
+ assert(idx < 9 && _bgTable[idx].bg);
+ assert(_bgTable[idx].lowPal || _bgTable[idx].hiPal);
+
+ _currentBg = idx;
+ reloadPalette();
+}
+
+/*! \brief Select scroll background
+ * \param idx Scroll background index
+ */
+void OSRenderer::selectScrollBg(unsigned int idx) {
+ assert(idx < 9);
+
+ if (_bgTable[idx].bg) {
+ _scrollBg = idx;
+ }
+ reloadPalette();
+}
+
+/*! \brief Set background scroll
+ * \param shift Background scroll in pixels
+ */
+void OSRenderer::setScroll(unsigned int shift) {
+ assert(shift <= 200);
+
+ _bgShift = shift;
+}
+
+/*! \brief Unload background from renderer
+ * \param idx Background to unload
+ */
+void OSRenderer::removeBg(unsigned int idx) {
+ assert(idx > 0 && idx < 9);
+
+ if (_currentBg == idx) {
+ _currentBg = 0;
+ }
+
+ if (_scrollBg == idx) {
+ _scrollBg = 0;
+ }
+
+ delete[] _bgTable[idx].bg;
+ delete[] _bgTable[idx].lowPal;
+ delete[] _bgTable[idx].hiPal;
+ _bgTable[idx].bg = NULL;
+ _bgTable[idx].lowPal = NULL;
+ _bgTable[idx].hiPal = NULL;
+ memset(_bgTable[idx].name, 0, sizeof (_bgTable[idx].name));
+}
+
+/*! \brief Fade to black
+ * \bug Operation Stealth sometimes seems to fade to black using
+ * transformPalette resulting in double fadeout
+ */
+void OSRenderer::fadeToBlack() {
+ if (!_activeHiPal) {
+ FWRenderer::fadeToBlack();
+ return;
+ }
+
+ for (int i = 0; i < 8; i++) {
+ for (int j = 0; j < _hiPalSize; j++) {
+ _activeHiPal[j] = CLIP(_activeHiPal[j] - 32, 0, 255);
+ }
+
+ refreshPalette();
+ g_system->updateScreen();
+ g_system->delayMillis(50);
+ }
}
void setMouseCursor(int cursor) {
@@ -147,21 +1307,6 @@ void setMouseCursor(int cursor) {
}
}
-static uint16 transformColor(uint16 baseColor, int8 r, int8 g, int8 b) {
- int8 oriR = CLIP( (baseColor & 0x007) + r, 0, 7);
- int8 oriG = CLIP(((baseColor & 0x070) >> 4) + g, 0, 7);
- int8 oriB = CLIP(((baseColor & 0x700) >> 8) + b, 0, 7);
-
- return oriR | (oriG << 4) | (oriB << 8);
-}
-
-void transformPaletteRange(byte startColor, byte stopColor, int8 r, int8 g, int8 b) {
- for (byte i = startColor; i <= stopColor; i++) {
- c_palette[i] = transformColor(tempPalette[i], b, g, r);
- }
- //gfxFlipPage(page2);
-}
-
void gfxFillSprite(const byte *spritePtr, uint16 width, uint16 height, byte *page, int16 x, int16 y, uint8 fillColor) {
int16 i, j;
@@ -170,17 +1315,12 @@ void gfxFillSprite(const byte *spritePtr, uint16 width, uint16 height, byte *pag
destPtr += i * 320;
for (j = 0; j < width; j++) {
- if (x + j >= 0 && x + j < 320 && i + y >= 0
- && i + y < 200) {
- if (!*(spritePtr++)) {
- *(destPtr++) = fillColor;
- } else {
- destPtr++;
- }
- } else {
- destPtr++;
- spritePtr++;
+ if (x + j >= 0 && x + j < 320 && i + y >= 0 && i + y < 200 && !*spritePtr) {
+ *destPtr = fillColor;
}
+
+ destPtr++;
+ spritePtr++;
}
}
}
@@ -360,53 +1500,7 @@ void gfxConvertSpriteToRaw(byte *dst, const byte *src, uint16 w, uint16 h) {
}
}
-void gfxCopyRawPage(byte *source, byte *dest) {
- memcpy(dest, source, 320 * 200);
-}
-
-void gfxFlipRawPage(byte *frontBuffer) {
- byte *page = frontBuffer;
- int x, y, i;
- byte *pixels = (byte *) screenBuffer;
- byte c;
- byte pal[256 * 4];
-
- for (y = 0; y < 200; y++) {
- for (x = 0; x < 320; x++) {
- c = *(page++);
-
- if (!colorMode256) {
- c = c & 15;
- }
-
- pixels[x + 0 + y * 320] = c;
- }
- }
-
- if (colorMode256) {
- for (i = 0; i < 256; i++) {
- pal[i * 4 + 0] = palette256[i * 3 + 0];
- pal[i * 4 + 1] = palette256[i * 3 + 1];
- pal[i * 4 + 2] = palette256[i * 3 + 2];
- pal[i * 4 + 3] = 0;
- }
- g_system->setPalette(pal, 0, 256);
- } else {
- for (i = 0; i < 16; i++) {
- // This seems to match the output from DOSbox.
- pal[i * 4 + 2] = ((c_palette[i] & 0x00f) >> 0) * 32;
- pal[i * 4 + 1] = ((c_palette[i] & 0x0f0) >> 4) * 32;
- pal[i * 4 + 0] = ((c_palette[i] & 0xf00) >> 8) * 32;
- pal[i * 4 + 3] = 0;
- }
- g_system->setPalette(pal, 0, 16);
- }
-
- g_system->copyRectToScreen(screenBuffer, 320, 0, 0, 320, 200);
-}
-
-void drawSpriteRaw(const byte *spritePtr, const byte *maskPtr, int16 width, int16 height,
- byte *page, int16 x, int16 y) {
+void drawSpriteRaw(const byte *spritePtr, const byte *maskPtr, int16 width, int16 height, byte *page, int16 x, int16 y) {
int16 i, j;
// FIXME: Is it a bug if maskPtr == NULL?
@@ -417,9 +1511,8 @@ void drawSpriteRaw(const byte *spritePtr, const byte *maskPtr, int16 width, int1
byte *destPtr = page + x + y * 320;
destPtr += i * 320;
- for (j = 0; j < width * 8; j++) {
- if (((g_cine->getGameType() == Cine::GType_FW && (!maskPtr || !(*maskPtr))) || (g_cine->getGameType() == Cine::GType_OS)) && (x + j >= 0
- && x + j < 320 && i + y >= 0 && i + y < 200)) {
+ for (j = 0; j < width; j++) {
+ if ((!maskPtr || !(*maskPtr)) && x + j >= 0 && x + j < 320 && i + y >= 0 && i + y < 200) {
*(destPtr++) = *(spritePtr++);
} else {
destPtr++;
@@ -432,21 +1525,19 @@ void drawSpriteRaw(const byte *spritePtr, const byte *maskPtr, int16 width, int1
}
}
-void drawSpriteRaw2(const byte *spritePtr, byte transColor, int16 width, int16 height,
- byte *page, int16 x, int16 y) {
+void drawSpriteRaw2(const byte *spritePtr, byte transColor, int16 width, int16 height, byte *page, int16 x, int16 y) {
int16 i, j;
for (i = 0; i < height; i++) {
byte *destPtr = page + x + y * 320;
destPtr += i * 320;
- for (j = 0; j < width * 8; j++) {
- if ((*(spritePtr) != transColor) && (x + j >= 0 && x + j < 320 && i + y >= 0 && i + y < 200)) {
- *(destPtr++) = *(spritePtr++);
- } else {
- destPtr++;
- spritePtr++;
+ for (j = 0; j < width; j++) {
+ if ((*spritePtr != transColor) && (x + j >= 0 && x + j < 320 && i + y >= 0 && i + y < 200)) {
+ *destPtr = *spritePtr;
}
+ destPtr++;
+ spritePtr++;
}
}
}
@@ -499,93 +1590,4 @@ void maskBgOverlay(const byte *bgPtr, const byte *maskPtr, int16 width, int16 he
}
}
-/*! \todo Fix rendering to prevent fadein artifacts
- */
-void fadeFromBlack() {
- int i, j;
- int r, g, b, tr, tg, tb;
- if (newColorMode == 2) {
- colorMode256 = 1;
- memset(palette256, 0, 256*3);
- } else if (newColorMode == 1) {
- colorMode256 = 0;
- memset(c_palette, 0, 16 * sizeof(uint16));
- }
-
- for (i = 0; i < 8; i++ ) {
- gfxFlipRawPage(page1Raw);
- g_system->updateScreen();
- g_system->delayMillis(50);
-
- if (colorMode256) {
- for (j = 0; j < 256*3; j++) {
- r = palette256[j] + (newPalette[j] + 7) / 8;
- palette256[j] = CLIP(r, 0, (int)newPalette[j]);
- }
- } else {
- for (j = 0; j < 16; j++) {
- r = c_palette[j] & 0xf;
- g = (c_palette[j] & 0xf0) >> 4;
- b = (c_palette[j] & 0xf00) >> 8;
-
- tr = tempPalette[j] & 0xf;
- tg = (tempPalette[j] & 0xf0) >> 4;
- tb = (tempPalette[j] & 0xf00) >> 8;
-
- r = CLIP(r + (tr + 7) / 8, 0, tr);
- g = CLIP(g + (tg + 7) / 8, 0, tg);
- b = CLIP(b + (tb + 7) / 8, 0, tb);
-
- c_palette[j] = r | (g << 4) | (b << 8);
- }
-
- }
- }
-
- if (colorMode256) {
- memcpy(palette256, newPalette, 256*3);
- } else {
- memcpy(c_palette, tempPalette, sizeof(uint16) * 16);
- }
-}
-
-void fadeToBlack() {
- for (int i = 0; i < 8; i++) {
- if (colorMode256) {
- for (int j = 0; j < 256*3; j++) {
- palette256[j] = CLIP(palette256[j] - 32, 0, 255);
- }
- } else {
- for (int j = 0; j < 16; j++) {
- c_palette[j] = transformColor(c_palette[j], -1, -1, -1);
- }
- }
- gfxFlipRawPage(page1Raw);
- g_system->updateScreen();
- g_system->delayMillis(50);
- }
-}
-
-void blitRawScreen(byte *frontBuffer) {
- gfxFlipRawPage(frontBuffer);
-}
-
-void flip(void) {
- blitRawScreen(page1Raw);
- if (fadeRequired) {
- if (newColorMode == 3) {
- newColorMode = ctColorMode + 1;
- }
-
- if (newColorMode == 2) {
- colorMode256 = 1;
- memcpy(palette256, newPalette, 256*3);
- } else {
- colorMode256 = 0;
- memcpy(c_palette, tempPalette, sizeof(uint16) * 16);
- }
- fadeRequired = false;
- }
-}
-
} // End of namespace Cine