/* 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 . * */ /* * 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(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 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