scummvm/engines/glk/scott/saga_draw.cpp
2022-06-23 20:11:01 +02:00

728 lines
19 KiB
C++

/* 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 3 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, see <http://www.gnu.org/licenses/>.
*
*/
/*
* Based on ScottFree interpreter version 1.14 developed by Swansea
* University Computer Society without disassembly of any other game
* drivers, only of game databases as permitted by EEC law (for purposes
* of compatibility).
*
* Licensed under GPLv2
*
* https://github.com/angstsmurf/spatterlight/tree/master/terps/scott
*/
#include "glk/scott/scott.h"
#include "glk/scott/definitions.h"
#include "glk/scott/resource.h"
#include "glk/scott/globals.h"
#include "glk/scott/saga_draw.h"
namespace Glk {
namespace Scott {
#define INVALID_COLOR 16
void rot90(uint8_t character[]) {
int32_t i, j;
uint8_t work2[8] = {0, 0, 0, 0, 0, 0, 0, 0};
for (i = 0; i < 8; i++)
for (j = 0; j < 8; j++)
if ((character[j] & (1 << i)) != 0)
work2[7 - i] += 1 << j;
for (i = 0; i < 8; i++)
character[i] = work2[i];
}
void rot180(uint8_t character[]) {
int32_t i, j;
uint8_t work2[8] = {0, 0, 0, 0, 0, 0, 0, 0};
for (i = 0; i < 8; i++)
for (j = 0; j < 8; j++)
if ((character[i] & (1 << j)) != 0)
work2[7 - i] += 1 << (7 - j);
for (i = 0; i < 8; i++)
character[i] = work2[i];
}
void rot270(uint8_t character[]) {
int32_t i, j;
uint8_t work2[8] = {0, 0, 0, 0, 0, 0, 0, 0};
for (i = 0; i < 8; i++)
for (j = 0; j < 8; j++)
if ((character[j] & (1 << i)) != 0)
work2[i] += 1 << (7 - j);
for (i = 0; i < 8; i++)
character[i] = work2[i];
}
void flip(uint8_t character[]) {
int32_t i, j;
uint8_t work2[8] = {0, 0, 0, 0, 0, 0, 0, 0};
for (i = 0; i < 8; i++)
for (j = 0; j < 8; j++)
if ((character[i] & (1 << j)) != 0)
work2[i] += 1 << (7 - j);
for (i = 0; i < 8; i++)
character[i] = work2[i];
}
void background(int32_t x, int32_t y, int32_t color) {
/* Draw the background */
rectFill(x * 8, y * 8, 8, 8, color);
}
void plotsprite(int32_t character, int32_t x, int32_t y, int32_t fg, int32_t bg) {
int32_t i, j;
background(x, y, bg);
for (i = 0; i < 8; i++) {
for (j = 0; j < 8; j++)
if ((_G(_screenchars)[character][i] & (1 << j)) != 0)
putPixel(x * 8 + j, y * 8 + i, fg);
}
}
void transform(int32_t character, int32_t flipMode, int32_t ptr) {
if (character > 255)
return;
uint8_t work[8];
int32_t i;
#ifdef DRAWDEBUG
debug("Plotting char: %d with flip: %02x (%s) at %d: %d,%d\n",
character, flip_mode, flipdescription[(flip_mode & 48) >> 4],
ptr, ptr % 0x20, ptr / 0x20);
#endif
// first copy the character into work
for (i = 0; i < 8; i++)
work[i] = _G(_sprite)[character][i];
// Now flip it
if ((flipMode & 0x30) == 0x10) {
rot90(work);
}
if ((flipMode & 0x30) == 0x20) {
rot180(work);
}
if ((flipMode & 0x30) == 0x30) {
rot270(work);
}
if ((flipMode & 0x40) != 0) {
flip(work);
}
flip(work);
// Now mask it onto the previous character
for (i = 0; i < 8; i++) {
if ((flipMode & 0x0c) == 12)
_G(_screenchars)[ptr][i] ^= work[i];
else if ((flipMode & 0x0c) == 8)
_G(_screenchars)[ptr][i] &= work[i];
else if ((flipMode & 0x0c) == 4)
_G(_screenchars)[ptr][i] |= work[i];
else
_G(_screenchars)[ptr][i] = work[i];
}
}
uint8_t *drawSagaPictureFromData(uint8_t *dataptr, int xSize, int ySize, int xOff, int yOff) {
int32_t offset = 0, cont = 0;
int32_t i, x, y, mask_mode;
uint8_t data, data2, old = 0;
int32_t ink[0x22][14], paper[0x22][14];
// uint8_t *origptr = dataptr;
int version = _G(_game)->_pictureFormatVersion;
offset = 0;
int32_t character = 0;
int32_t count;
do {
count = 1;
/* first handle mode */
data = *dataptr++;
if (data < 0x80) {
if (character > 127 && version > 2) {
data += 128;
}
character = data;
#ifdef DRAWDEBUG
debug("******* SOLO CHARACTER: %04x\n", character);
#endif
transform(character, 0, offset);
offset++;
if (offset > 767)
break;
} else {
// first check for a count
if ((data & 2) == 2) {
count = (*dataptr++) + 1;
}
// Next get character and plot (count) times
character = *dataptr++;
// Plot the initial character
if ((data & 1) == 1 && character < 128)
character += 128;
for (i = 0; i < count; i++)
transform(character, (data & 0x0c) ? (data & 0xf3) : data, offset + i);
// Now check for overlays
if ((data & 0xc) != 0) {
// We have overlays so grab each member of the stream and work out what
// to do with it
mask_mode = (data & 0xc);
data2 = *dataptr++;
old = data;
do {
cont = 0;
if (data2 < 0x80) {
if (version == 4 && (old & 1) == 1)
data2 += 128;
#ifdef DRAWDEBUG
debug("Plotting %d directly (overlay) at %d\n", data2, offset);
#endif
for (i = 0; i < count; i++)
transform(data2, old & 0x0c, offset + i);
} else {
character = *dataptr++;
if ((data2 & 1) == 1)
character += 128;
#ifdef DRAWDEBUG
debug("Plotting %d with flip %02x (%s) at %d %d\n", character, (data2 | mask_mode), flipdescription[((data2 | mask_mode) & 48) >> 4],
offset, count);
#endif
for (i = 0; i < count; i++)
transform(character, (data2 & 0xf3) | mask_mode, offset + i);
if ((data2 & 0x0c) != 0) {
mask_mode = (data2 & 0x0c);
old = data2;
cont = 1;
data2 = *dataptr++;
}
}
} while (cont != 0);
}
offset += count;
}
} while (offset < xSize * ySize);
y = 0;
x = 0;
uint8_t colour = 0;
// Note version 3 count is inverse it is repeat previous colour
// Whilst version0-2 count is repeat next character
while (y < ySize) {
if (dataptr > _G(_entireFile) && static_cast<size_t>(dataptr - _G(_entireFile)) > _G(_fileLength))
return dataptr - 1;
data = *dataptr++;
if ((data & 0x80)) {
count = (data & 0x7f) + 1;
if (version >= 3) {
count--;
} else {
colour = *dataptr++;
}
} else {
count = 1;
colour = data;
}
while (count > 0) {
// Now split up depending on which version we're using
// For version 3+
if (_G(_drawToBuffer))
_G(_buffer)[(yOff + y) * 32 + (xOff + x)][8] = colour;
else {
if (version > 2) {
if (x > 33)
return nullptr;
// ink is 0-2, screen is 3-5, 6 in bright flag
ink[x][y] = colour & 0x07;
paper[x][y] = (colour & 0x38) >> 3;
if ((colour & 0x40) == 0x40) {
paper[x][y] += 8;
ink[x][y] += 8;
}
} else {
if (x > 33)
return nullptr;
paper[x][y] = colour & 0x07;
ink[x][y] = ((colour & 0x70) >> 4);
if ((colour & 0x08) == 0x08 || version < 2) {
paper[x][y] += 8;
ink[x][y] += 8;
}
}
}
x++;
if (x == xSize) {
x = 0;
y++;
}
count--;
}
}
offset = 0;
int32_t xoff2;
for (y = 0; y < ySize; y++)
for (x = 0; x < xSize; x++) {
xoff2 = xOff;
if (version > 0 && version < 3)
xoff2 = xOff - 4;
if (_G(_drawToBuffer)) {
for (i = 0; i < 8; i++)
_G(_buffer)[(y + yOff) * 32 + x + xoff2][i] = _G(_screenchars)[offset][i];
} else {
plotsprite(offset, x + xoff2, y + yOff, remap(ink[x][y]), remap(paper[x][y]));
}
#ifdef DRAWDEBUG
debug("(gfx#:plotting %d,%d:paper=%s,ink=%s)\n", x + xoff2, y + yoff,
colortext(remap(paper[x][y])), colortext(remap(ink[x][y])));
#endif
offset++;
if (offset > 766)
break;
}
return dataptr;
}
void drawSagaPictureNumber(int pictureNumber) {
int numgraphics = _G(_game)->_numberOfPictures;
if (pictureNumber >= numgraphics) {
error("drawSagaPictureNumber: Invalid image number % d !Last image: % d", pictureNumber, numgraphics - 1);
return;
}
Image img = _G(_images)[pictureNumber];
if (img._imageData == nullptr)
return;
drawSagaPictureFromData(img._imageData, img._width, img._height, img._xOff, img._yOff);
}
void drawSagaPictureAtPos(int pictureNumber, int x, int y) {
Image img = _G(_images)[pictureNumber];
drawSagaPictureFromData(img._imageData, img._width, img._height, x, y);
}
void drawSagaPictureFromBuffer() {
for (int line = 0; line < 12; line++) {
for (int col = 0; col < 32; col++) {
uint8_t colour = _G(_buffer)[col + line * 32][8];
int paper = (colour >> 3) & 0x7;
paper += 8 * ((colour & 0x40) == 0x40);
paper = remap(paper);
int ink = (colour & 0x7);
ink += 8 * ((colour & 0x40) == 0x40);
ink = remap(ink);
background(col, line, paper);
for (int i = 0; i < 8; i++) {
if (_G(_buffer)[col + line * 32][i] == 0)
continue;
if (_G(_buffer)[col + line * 32][i] == 255) {
glui32 glk_color = (_G(_pal)[ink][0] << 16) | (_G(_pal)[ink][1] << 8) | _G(_pal)[ink][2];
g_scott->glk_window_fill_rect(_G(_graphics), glk_color, col * 8 * _G(_pixelSize) + _G(_xOffset),
(line * 8 + i) * _G(_pixelSize), 8 * _G(_pixelSize), _G(_pixelSize));
continue;
}
for (int j = 0; j < 8; j++)
if ((_G(_buffer)[col + line * 32][i] & (1 << j)) != 0) {
int ypos = line * 8 + i;
putPixel(col * 8 + j, ypos, ink);
}
}
}
}
}
void sagaSetup(size_t imgOffset) {
int32_t i, y;
Common::Array<uint16_t> imageOffsets(_G(_game)->_numberOfPictures);
if (_G(_palChosen) == NO_PALETTE) {
_G(_palChosen) = _G(_game)->_palette;
}
if (_G(_palChosen) == NO_PALETTE) {
error("sagaSetup: unknown palette");
}
definePalette();
int version = _G(_game)->_pictureFormatVersion;
int32_t CHAR_START = _G(_game)->_startOfCharacters + _G(_fileBaselineOffset);
int32_t OFFSET_TABLE_START = _G(_game)->_startOfImageData + _G(_fileBaselineOffset);
if (_G(_game)->_startOfImageData == FOLLOWS) {
OFFSET_TABLE_START = CHAR_START + 0x800;
}
int32_t DATA_OFFSET = _G(_game)->_imageAddressOffset + _G(_fileBaselineOffset);
if (imgOffset)
DATA_OFFSET = imgOffset;
uint8_t *pos;
int numgraphics = _G(_game)->_numberOfPictures;
pos = seekToPos(_G(_entireFile), CHAR_START);
#ifdef DRAWDEBUG
debug("Grabbing Character details\n");
debug("Character Offset: %04x\n", CHAR_START - _G(_fileBaselineOffset));
#endif
for (i = 0; i < 256; i++) {
for (y = 0; y < 8; y++) {
_G(_sprite)[i][y] = *(pos++);
}
}
_G(_images).resize(numgraphics);
Image *img = &_G(_images)[0];
pos = seekToPos(_G(_entireFile), OFFSET_TABLE_START);
pos = seekToPos(_G(_entireFile), OFFSET_TABLE_START);
for (i = 0; i < numgraphics; i++) {
if (_G(_game)->_pictureFormatVersion == 0) {
uint16_t address;
if (i < 11) {
address = _G(_game)->_startOfImageData + (i * 2);
} else if (i < 28) {
address = _G(_hulkItemImageOffsets) + (i - 10) * 2;
} else if (i < 34) {
address = _G(_hulkLookImageOffsets) + (i - 28) * 2;
} else {
address = _G(_hulkSpecialImageOffsets) + (i - 34) * 2;
}
address += _G(_fileBaselineOffset);
address = _G(_entireFile)[address] + _G(_entireFile)[address + 1] * 0x100;
imageOffsets[i] = address + _G(_hulkImageOffset);
} else {
imageOffsets[i] = *(pos++);
imageOffsets[i] += *(pos++) * 0x100;
}
}
for (int picture_number = 0; picture_number < numgraphics; picture_number++) {
pos = seekToPos(_G(_entireFile), imageOffsets[picture_number] + DATA_OFFSET);
if (pos == 0)
return;
img->_width = *(pos++);
if (img->_width > 32)
img->_width = 32;
img->_height = *(pos++);
if (img->_height > 12)
img->_height = 12;
if (version > 0) {
img->_xOff = *(pos++);
if (img->_xOff > 32)
img->_xOff = 4;
img->_yOff = *(pos++);
if (img->_yOff > 12)
img->_yOff = 0;
} else {
if (picture_number > 9 && picture_number < 28) {
img->_xOff = _G(_entireFile)[_G(_hulkCoordinates) + picture_number - 10 + _G(_fileBaselineOffset)];
img->_yOff = _G(_entireFile)[_G(_hulkCoordinates) + 18 + picture_number - 10 + _G(_fileBaselineOffset)];
} else {
img->_xOff = img->_yOff = 0;
}
}
img->_imageData = pos;
img++;
}
}
void putPixel(glsi32 x, glsi32 y, int32_t color) {
int yOffset = 0;
glui32 glk_color = ((_G(_pal)[color][0] << 16)) | ((_G(_pal)[color][1] << 8)) | (_G(_pal)[color][2]);
g_scott->glk_window_fill_rect(_G(_graphics), glk_color, x * _G(_pixelSize) + _G(_xOffset),
y * _G(_pixelSize) + yOffset, _G(_pixelSize), _G(_pixelSize));
}
void rectFill(int32_t x, int32_t y, int32_t width, int32_t height, int32_t color) {
int yOffset = 0;
int bufferpos = (y / 8) * 32 + (x / 8);
if (bufferpos >= 0xD80)
return;
_G(_buffer)[bufferpos][8] = _G(_buffer)[bufferpos][8] | (color << 3);
glui32 glk_color = ((_G(_pal)[color][0] << 16)) | ((_G(_pal)[color][1] << 8)) | (_G(_pal)[color][2]);
g_scott->glk_window_fill_rect(_G(_graphics), glk_color, x * _G(_pixelSize) + _G(_xOffset),
y * _G(_pixelSize) + yOffset, width * _G(_pixelSize), height * _G(_pixelSize));
}
void switchPalettes(int pal1, int pal2) {
uint8_t temp[3];
temp[0] = _G(_pal)[pal1][0];
temp[1] = _G(_pal)[pal1][1];
temp[2] = _G(_pal)[pal1][2];
_G(_pal)[pal1][0] = _G(_pal)[pal2][0];
_G(_pal)[pal1][1] = _G(_pal)[pal2][1];
_G(_pal)[pal1][2] = _G(_pal)[pal2][2];
_G(_pal)[pal2][0] = temp[0];
_G(_pal)[pal2][1] = temp[1];
_G(_pal)[pal2][2] = temp[2];
}
void setColor(int32_t index, RGB *colour) {
_G(_pal)[index][0] = (*colour)[0];
_G(_pal)[index][1] = (*colour)[1];
_G(_pal)[index][2] = (*colour)[2];
}
void definePalette() {
/* set up the palette */
if (_G(_palChosen) == VGA) {
RGB black = {0, 0, 0};
RGB blue = {0, 0, 255};
RGB red = {255, 0, 0};
RGB magenta = {255, 0, 255};
RGB green = {0, 255, 0};
RGB cyan = {0, 255, 255};
RGB yellow = {255, 255, 0};
RGB white = {255, 255, 255};
RGB brblack = {0, 0, 0};
RGB brblue = {0, 0, 255};
RGB brred = {255, 0, 0};
RGB brmagenta = {255, 0, 255};
RGB brgreen = {0, 255, 0};
RGB brcyan = {0, 255, 255};
RGB bryellow = {255, 255, 0};
RGB brwhite = {255, 255, 255};
setColor(0, &black);
setColor(1, &blue);
setColor(2, &red);
setColor(3, &magenta);
setColor(4, &green);
setColor(5, &cyan);
setColor(6, &yellow);
setColor(7, &white);
setColor(8, &brblack);
setColor(9, &brblue);
setColor(10, &brred);
setColor(11, &brmagenta);
setColor(12, &brgreen);
setColor(13, &brcyan);
setColor(14, &bryellow);
setColor(15, &brwhite);
} else if (_G(_palChosen) == ZX) {
/* corrected Sinclair ZX palette (pretty dull though) */
RGB black = {0, 0, 0};
RGB blue = {0, 0, 154};
RGB red = {154, 0, 0};
RGB magenta = {154, 0, 154};
RGB green = {0, 154, 0};
RGB cyan = {0, 154, 154};
RGB yellow = {154, 154, 0};
RGB white = {154, 154, 154};
RGB brblack = {0, 0, 0};
RGB brblue = {0, 0, 170};
RGB brred = {186, 0, 0};
RGB brmagenta = {206, 0, 206};
RGB brgreen = {0, 206, 0};
RGB brcyan = {0, 223, 223};
RGB bryellow = {239, 239, 0};
RGB brwhite = {255, 255, 255};
setColor(0, &black);
setColor(1, &blue);
setColor(2, &red);
setColor(3, &magenta);
setColor(4, &green);
setColor(5, &cyan);
setColor(6, &yellow);
setColor(7, &white);
setColor(8, &brblack);
setColor(9, &brblue);
setColor(10, &brred);
setColor(11, &brmagenta);
setColor(12, &brgreen);
setColor(13, &brcyan);
setColor(14, &bryellow);
setColor(15, &brwhite);
_G(_whiteColour) = 15;
_G(_blueColour) = 9;
_G(_diceColour) = 0xff0000;
} else if (_G(_palChosen) == ZXOPT) {
/* optimized but not realistic Sinclair ZX palette (SPIN emu) */
RGB black = {0, 0, 0};
RGB blue = {0, 0, 202};
RGB red = {202, 0, 0};
RGB magenta = {202, 0, 202};
RGB green = {0, 202, 0};
RGB cyan = {0, 202, 202};
RGB yellow = {202, 202, 0};
RGB white = {202, 202, 202};
/*
old David Lodge palette:
RGB black = { 0, 0, 0 };
RGB blue = { 0, 0, 214 };
RGB red = { 214, 0, 0 };
RGB magenta = { 214, 0, 214 };
RGB green = { 0, 214, 0 };
RGB cyan = { 0, 214, 214 };
RGB yellow = { 214, 214, 0 };
RGB white = { 214, 214, 214 };
*/
RGB brblack = {0, 0, 0};
RGB brblue = {0, 0, 255};
RGB brred = {255, 0, 20};
RGB brmagenta = {255, 0, 255};
RGB brgreen = {0, 255, 0};
RGB brcyan = {0, 255, 255};
RGB bryellow = {255, 255, 0};
RGB brwhite = {255, 255, 255};
setColor(0, &black);
setColor(1, &blue);
setColor(2, &red);
setColor(3, &magenta);
setColor(4, &green);
setColor(5, &cyan);
setColor(6, &yellow);
setColor(7, &white);
setColor(8, &brblack);
setColor(9, &brblue);
setColor(10, &brred);
setColor(11, &brmagenta);
setColor(12, &brgreen);
setColor(13, &brcyan);
setColor(14, &bryellow);
setColor(15, &brwhite);
_G(_whiteColour) = 15;
_G(_blueColour) = 9;
_G(_diceColour) = 0xff0000;
} else if ((_G(_palChosen) == C64A) || (_G(_palChosen) == C64B)) {
/* and now: C64 palette (pepto/VICE) */
RGB black = {0, 0, 0};
RGB white = {255, 255, 255};
RGB red = {191, 97, 72};
RGB cyan = {153, 230, 249};
RGB purple = {177, 89, 185};
RGB green = {121, 213, 112};
RGB blue = {95, 72, 233};
RGB yellow = {247, 255, 108};
RGB orange = {186, 134, 32};
RGB brown = {116, 105, 0};
RGB lred = {180, 105, 164};
RGB dgrey = {69, 69, 69};
RGB grey = {167, 167, 167};
RGB lgreen = {154, 210, 134};
RGB lblue = {162, 143, 255};
RGB lgrey = {150, 150, 150};
setColor(0, &black);
setColor(1, &white);
setColor(2, &red);
setColor(3, &cyan);
setColor(4, &purple);
setColor(5, &green);
setColor(6, &blue);
setColor(7, &yellow);
setColor(8, &orange);
setColor(9, &brown);
setColor(10, &lred);
setColor(11, &dgrey);
setColor(12, &grey);
setColor(13, &lgreen);
setColor(14, &lblue);
setColor(15, &lgrey);
_G(_whiteColour) = 1;
_G(_blueColour) = 6;
_G(_diceColour) = 0x5f48e9;
}
}
int32_t remap(int32_t color) {
int32_t mapcol;
if ((_G(_palChosen) == ZX) || (_G(_palChosen) == ZXOPT)) {
/* nothing to remap here; shows that the gfx were created on a ZX */
mapcol = (((color >= 0) && (color <= 15)) ? color : INVALID_COLOR);
} else if (_G(_palChosen) == C64A) {
/* remap A determined from Golden Baton, applies to S1/S3/S13 too (8col) */
int32_t c64remap[] = {0, 6, 2, 4, 5, 3, 7, 1, 8, 1, 1, 1, 7, 12, 8, 7};
mapcol = (((color >= 0) && (color <= 15)) ? c64remap[color] : INVALID_COLOR);
} else if (_G(_palChosen) == C64B) {
/* remap B determined from Spiderman (16col) */
int32_t c64remap[] = {0, 6, 9, 4, 5, 14, 8, 12, 0, 6, 2, 4, 5, 3, 7, 1};
mapcol = (((color >= 0) && (color <= 15)) ? c64remap[color] : INVALID_COLOR);
} else
mapcol = (((color >= 0) && (color <= 15)) ? color : INVALID_COLOR);
return (mapcol);
}
} // End of namespace Scott
} // End of namespace Glk