Added Flashback game clone named REminiscence, intially ported by Lubomyr
This commit is contained in:
@@ -0,0 +1,411 @@
|
||||
/* REminiscence - Flashback interpreter
|
||||
* Copyright (C) 2005-2007 Gregory Montoir
|
||||
*
|
||||
* 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 "resource.h"
|
||||
#include "systemstub.h"
|
||||
#include "video.h"
|
||||
|
||||
|
||||
Video::Video(Resource *res, SystemStub *stub)
|
||||
: _res(res), _stub(stub) {
|
||||
_frontLayer = (uint8 *)malloc(GAMESCREEN_W * GAMESCREEN_H);
|
||||
memset(_frontLayer, 0, GAMESCREEN_W * GAMESCREEN_H);
|
||||
_backLayer = (uint8 *)malloc(GAMESCREEN_W * GAMESCREEN_H);
|
||||
memset(_backLayer, 0, GAMESCREEN_W * GAMESCREEN_H);
|
||||
_tempLayer = (uint8 *)malloc(GAMESCREEN_W * GAMESCREEN_H);
|
||||
memset(_tempLayer, 0, GAMESCREEN_W * GAMESCREEN_H);
|
||||
_tempLayer2 = (uint8 *)malloc(GAMESCREEN_W * GAMESCREEN_H);
|
||||
memset(_tempLayer2, 0, GAMESCREEN_W * GAMESCREEN_H);
|
||||
_screenBlocks = (uint8 *)malloc((GAMESCREEN_W / SCREENBLOCK_W) * (GAMESCREEN_H / SCREENBLOCK_H));
|
||||
memset(_screenBlocks, 0, (GAMESCREEN_W / SCREENBLOCK_W) * (GAMESCREEN_H / SCREENBLOCK_H));
|
||||
_fullRefresh = true;
|
||||
_shakeOffset = 0;
|
||||
_charFrontColor = 0;
|
||||
_charTransparentColor = 0;
|
||||
_charShadowColor = 0;
|
||||
}
|
||||
|
||||
Video::~Video() {
|
||||
free(_frontLayer);
|
||||
free(_backLayer);
|
||||
free(_tempLayer);
|
||||
free(_tempLayer2);
|
||||
free(_screenBlocks);
|
||||
}
|
||||
|
||||
void Video::markBlockAsDirty(int16 x, int16 y, uint16 w, uint16 h) {
|
||||
debug(DBG_VIDEO, "Video::markBlockAsDirty(%d, %d, %d, %d)", x, y, w, h);
|
||||
assert(x >= 0 && x + w <= GAMESCREEN_W && y >= 0 && y + h <= GAMESCREEN_H);
|
||||
int bx1 = x / SCREENBLOCK_W;
|
||||
int by1 = y / SCREENBLOCK_H;
|
||||
int bx2 = (x + w - 1) / SCREENBLOCK_W;
|
||||
int by2 = (y + h - 1) / SCREENBLOCK_H;
|
||||
assert(bx2 < GAMESCREEN_W / SCREENBLOCK_W && by2 < GAMESCREEN_H / SCREENBLOCK_H);
|
||||
for (; by1 <= by2; ++by1) {
|
||||
for (int i = bx1; i <= bx2; ++i) {
|
||||
_screenBlocks[by1 * (GAMESCREEN_W / SCREENBLOCK_W) + i] = 2;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void Video::updateScreen() {
|
||||
debug(DBG_VIDEO, "Video::updateScreen()");
|
||||
// _fullRefresh = true;
|
||||
if (_fullRefresh) {
|
||||
_stub->copyRect(0, 0, Video::GAMESCREEN_W, Video::GAMESCREEN_H, _frontLayer, 256);
|
||||
_stub->updateScreen(_shakeOffset);
|
||||
_fullRefresh = false;
|
||||
} else {
|
||||
int i, j;
|
||||
int count = 0;
|
||||
uint8 *p = _screenBlocks;
|
||||
for (j = 0; j < GAMESCREEN_H / SCREENBLOCK_H; ++j) {
|
||||
uint16 nh = 0;
|
||||
for (i = 0; i < GAMESCREEN_W / SCREENBLOCK_W; ++i) {
|
||||
if (p[i] != 0) {
|
||||
--p[i];
|
||||
++nh;
|
||||
} else if (nh != 0) {
|
||||
int16 x = (i - nh) * SCREENBLOCK_W;
|
||||
_stub->copyRect(x, j * SCREENBLOCK_H, nh * SCREENBLOCK_W, SCREENBLOCK_H, _frontLayer, 256);
|
||||
nh = 0;
|
||||
++count;
|
||||
}
|
||||
}
|
||||
if (nh != 0) {
|
||||
int16 x = (i - nh) * SCREENBLOCK_W;
|
||||
_stub->copyRect(x, j * SCREENBLOCK_H, nh * SCREENBLOCK_W, SCREENBLOCK_H, _frontLayer, 256);
|
||||
++count;
|
||||
}
|
||||
p += GAMESCREEN_W / SCREENBLOCK_W;
|
||||
}
|
||||
if (count != 0) {
|
||||
_stub->updateScreen(_shakeOffset);
|
||||
}
|
||||
}
|
||||
if (_shakeOffset != 0) {
|
||||
_shakeOffset = 0;
|
||||
_fullRefresh = true;
|
||||
}
|
||||
}
|
||||
|
||||
void Video::fullRefresh() {
|
||||
debug(DBG_VIDEO, "Video::fullRefresh()");
|
||||
_fullRefresh = true;
|
||||
memset(_screenBlocks, 0, (GAMESCREEN_W / SCREENBLOCK_W) * (GAMESCREEN_H / SCREENBLOCK_H));
|
||||
}
|
||||
|
||||
void Video::fadeOut() {
|
||||
debug(DBG_VIDEO, "Video::fadeOut()");
|
||||
for (int step = 16; step >= 0; --step) {
|
||||
for (int c = 0; c < 256; ++c) {
|
||||
Color col;
|
||||
_stub->getPaletteEntry(c, &col);
|
||||
col.r = col.r * step >> 4;
|
||||
col.g = col.g * step >> 4;
|
||||
col.b = col.b * step >> 4;
|
||||
_stub->setPaletteEntry(c, &col);
|
||||
}
|
||||
fullRefresh();
|
||||
updateScreen();
|
||||
_stub->sleep(50);
|
||||
}
|
||||
}
|
||||
|
||||
void Video::setPaletteSlotBE(int palSlot, int palNum) {
|
||||
debug(DBG_VIDEO, "Video::setPaletteSlotBE()");
|
||||
const uint8 *p = _res->_pal + palNum * 0x20;
|
||||
for (int i = 0; i < 16; ++i) {
|
||||
uint16 color = READ_BE_UINT16(p); p += 2;
|
||||
uint8 t = (color == 0) ? 0 : 3;
|
||||
Color c;
|
||||
c.r = ((color & 0x00F) << 2) | t;
|
||||
c.g = ((color & 0x0F0) >> 2) | t;
|
||||
c.b = ((color & 0xF00) >> 6) | t;
|
||||
_stub->setPaletteEntry(palSlot * 0x10 + i, &c);
|
||||
}
|
||||
}
|
||||
|
||||
void Video::setPaletteSlotLE(int palSlot, const uint8 *palData) {
|
||||
debug(DBG_VIDEO, "Video::setPaletteSlotLE()");
|
||||
for (int i = 0; i < 16; ++i) {
|
||||
uint16 color = READ_LE_UINT16(palData); palData += 2;
|
||||
Color c;
|
||||
c.b = (color & 0x00F) << 2;
|
||||
c.g = (color & 0x0F0) >> 2;
|
||||
c.r = (color & 0xF00) >> 6;
|
||||
_stub->setPaletteEntry(palSlot * 0x10 + i, &c);
|
||||
}
|
||||
}
|
||||
|
||||
void Video::setTextPalette() {
|
||||
debug(DBG_VIDEO, "Video::setTextPalette()");
|
||||
const uint8 *p = _textPal;
|
||||
for (int i = 0; i < 16; ++i) {
|
||||
uint16 color = READ_LE_UINT16(p); p += 2;
|
||||
Color c;
|
||||
c.b = (color & 0x00F) << 2;
|
||||
c.g = (color & 0x0F0) >> 2;
|
||||
c.r = (color & 0xF00) >> 6;
|
||||
_stub->setPaletteEntry(0xE0 + i, &c);
|
||||
}
|
||||
}
|
||||
|
||||
void Video::setPalette0xF() {
|
||||
debug(DBG_VIDEO, "Video::setPalette0xF()");
|
||||
const uint8 *p = _palSlot0xF;
|
||||
for (int i = 0; i < 16; ++i) {
|
||||
Color c;
|
||||
c.r = *p++ >> 2;
|
||||
c.g = *p++ >> 2;
|
||||
c.b = *p++ >> 2;
|
||||
_stub->setPaletteEntry(0xF0 + i, &c);
|
||||
}
|
||||
}
|
||||
|
||||
void Video::copyLevelMap(uint16 room) {
|
||||
debug(DBG_VIDEO, "Video::copyLevelMap(%d)", room);
|
||||
assert(room < 0x40);
|
||||
int32 off = READ_LE_UINT32(_res->_map + room * 6);
|
||||
if (off == 0) {
|
||||
error("Invalid room %d", room);
|
||||
}
|
||||
bool packed = true;
|
||||
if (off < 0) {
|
||||
off = -off;
|
||||
packed = false;
|
||||
}
|
||||
const uint8 *p = _res->_map + off;
|
||||
_mapPalSlot1 = *p++;
|
||||
_mapPalSlot2 = *p++;
|
||||
_mapPalSlot3 = *p++;
|
||||
_mapPalSlot4 = *p++;
|
||||
if (packed) {
|
||||
uint8 *vid = _frontLayer;
|
||||
for (int i = 0; i < 4; ++i) {
|
||||
uint16 sz = READ_LE_UINT16(p); p += 2;
|
||||
decodeLevelMap(sz, p, _res->_memBuf); p += sz;
|
||||
memcpy(vid, _res->_memBuf, 256 * 56);
|
||||
vid += 256 * 56;
|
||||
}
|
||||
} else {
|
||||
for (int i = 0; i < 4; ++i) {
|
||||
for (int y = 0; y < 224; ++y) {
|
||||
for (int x = 0; x < 64; ++x) {
|
||||
_frontLayer[i + x * 4 + 256 * y] = p[256 * 56 * i + x + 64 * y];
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
memcpy(_backLayer, _frontLayer, Video::GAMESCREEN_W * Video::GAMESCREEN_H);
|
||||
}
|
||||
|
||||
void Video::decodeLevelMap(uint16 sz, const uint8 *src, uint8 *dst) {
|
||||
debug(DBG_VIDEO, "Video::decodeLevelMap() sz = 0x%X", sz);
|
||||
const uint8 *end = src + sz;
|
||||
while (src < end) {
|
||||
int16 code = (int8)*src++;
|
||||
if (code < 0) {
|
||||
int len = 1 - code;
|
||||
memset(dst, *src++, len);
|
||||
dst += len;
|
||||
} else {
|
||||
++code;
|
||||
memcpy(dst, src, code);
|
||||
src += code;
|
||||
dst += code;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void Video::setLevelPalettes() {
|
||||
debug(DBG_VIDEO, "Video::setLevelPalettes()");
|
||||
if (_unkPalSlot2 == 0) {
|
||||
_unkPalSlot2 = _mapPalSlot3;
|
||||
}
|
||||
if (_unkPalSlot1 == 0) {
|
||||
_unkPalSlot1 = _mapPalSlot3;
|
||||
}
|
||||
setPaletteSlotBE(0x0, _mapPalSlot1);
|
||||
setPaletteSlotBE(0x1, _mapPalSlot2);
|
||||
setPaletteSlotBE(0x2, _mapPalSlot3);
|
||||
setPaletteSlotBE(0x3, _mapPalSlot4);
|
||||
if (_unkPalSlot1 == _mapPalSlot3) {
|
||||
setPaletteSlotLE(4, _conradPal1);
|
||||
} else {
|
||||
setPaletteSlotLE(4, _conradPal2);
|
||||
}
|
||||
// slot 5 is monster palette
|
||||
setPaletteSlotBE(0x8, _mapPalSlot1);
|
||||
setPaletteSlotBE(0x9, _mapPalSlot2);
|
||||
setPaletteSlotBE(0xA, _unkPalSlot2);
|
||||
setPaletteSlotBE(0xB, _mapPalSlot4);
|
||||
// slots 0xC and 0xD are cutscene palettes
|
||||
setTextPalette();
|
||||
}
|
||||
|
||||
void Video::drawSpriteSub1(const uint8 *src, uint8 *dst, int pitch, int h, int w, uint8 colMask) {
|
||||
debug(DBG_VIDEO, "Video::drawSpriteSub1(0x%X, 0x%X, 0x%X, 0x%X)", pitch, w, h, colMask);
|
||||
while (h--) {
|
||||
for (int i = 0; i < w; ++i) {
|
||||
if (src[i] != 0) {
|
||||
dst[i] = src[i] | colMask;
|
||||
}
|
||||
}
|
||||
src += pitch;
|
||||
dst += 256;
|
||||
}
|
||||
}
|
||||
|
||||
void Video::drawSpriteSub2(const uint8 *src, uint8 *dst, int pitch, int h, int w, uint8 colMask) {
|
||||
debug(DBG_VIDEO, "Video::drawSpriteSub2(0x%X, 0x%X, 0x%X, 0x%X)", pitch, w, h, colMask);
|
||||
while (h--) {
|
||||
for (int i = 0; i < w; ++i) {
|
||||
if (src[-i] != 0) {
|
||||
dst[i] = src[-i] | colMask;
|
||||
}
|
||||
}
|
||||
src += pitch;
|
||||
dst += 256;
|
||||
}
|
||||
}
|
||||
|
||||
void Video::drawSpriteSub3(const uint8 *src, uint8 *dst, int pitch, int h, int w, uint8 colMask) {
|
||||
debug(DBG_VIDEO, "Video::drawSpriteSub3(0x%X, 0x%X, 0x%X, 0x%X)", pitch, w, h, colMask);
|
||||
while (h--) {
|
||||
for (int i = 0; i < w; ++i) {
|
||||
if (src[i] != 0 && !(dst[i] & 0x80)) {
|
||||
dst[i] = src[i] | colMask;
|
||||
}
|
||||
}
|
||||
src += pitch;
|
||||
dst += 256;
|
||||
}
|
||||
}
|
||||
|
||||
void Video::drawSpriteSub4(const uint8 *src, uint8 *dst, int pitch, int h, int w, uint8 colMask) {
|
||||
debug(DBG_VIDEO, "Video::drawSpriteSub4(0x%X, 0x%X, 0x%X, 0x%X)", pitch, w, h, colMask);
|
||||
while (h--) {
|
||||
for (int i = 0; i < w; ++i) {
|
||||
if (src[-i] != 0 && !(dst[i] & 0x80)) {
|
||||
dst[i] = src[-i] | colMask;
|
||||
}
|
||||
}
|
||||
src += pitch;
|
||||
dst += 256;
|
||||
}
|
||||
}
|
||||
|
||||
void Video::drawSpriteSub5(const uint8 *src, uint8 *dst, int pitch, int h, int w, uint8 colMask) {
|
||||
debug(DBG_VIDEO, "Video::drawSpriteSub5(0x%X, 0x%X, 0x%X, 0x%X)", pitch, w, h, colMask);
|
||||
while (h--) {
|
||||
for (int i = 0; i < w; ++i) {
|
||||
if (src[i * pitch] != 0 && !(dst[i] & 0x80)) {
|
||||
dst[i] = src[i * pitch] | colMask;
|
||||
}
|
||||
}
|
||||
++src;
|
||||
dst += 256;
|
||||
}
|
||||
}
|
||||
|
||||
void Video::drawSpriteSub6(const uint8 *src, uint8 *dst, int pitch, int h, int w, uint8 colMask) {
|
||||
debug(DBG_VIDEO, "Video::drawSpriteSub6(0x%X, 0x%X, 0x%X, 0x%X)", pitch, w, h, colMask);
|
||||
while (h--) {
|
||||
for (int i = 0; i < w; ++i) {
|
||||
if (src[-i * pitch] != 0 && !(dst[i] & 0x80)) {
|
||||
dst[i] = src[-i * pitch] | colMask;
|
||||
}
|
||||
}
|
||||
++src;
|
||||
dst += 256;
|
||||
}
|
||||
}
|
||||
|
||||
void Video::drawChar(uint8 c, int16 y, int16 x) {
|
||||
debug(DBG_VIDEO, "Video::drawChar(0x%X, %d, %d)", c, y, x);
|
||||
y *= 8;
|
||||
x *= 8;
|
||||
const uint8 *src = _res->_fnt + (c - 32) * 32;
|
||||
uint8 *dst = _frontLayer + x + 256 * y;
|
||||
for (int h = 0; h < 8; ++h) {
|
||||
for (int i = 0; i < 4; ++i) {
|
||||
uint8 c1 = (*src & 0xF0) >> 4;
|
||||
uint8 c2 = (*src & 0x0F) >> 0;
|
||||
++src;
|
||||
|
||||
if (c1 != 0) {
|
||||
if (c1 != 2) {
|
||||
*dst = _charFrontColor;
|
||||
} else {
|
||||
*dst = _charShadowColor;
|
||||
}
|
||||
} else if (_charTransparentColor != 0xFF) {
|
||||
*dst = _charTransparentColor;
|
||||
}
|
||||
++dst;
|
||||
|
||||
if (c2 != 0) {
|
||||
if (c2 != 2) {
|
||||
*dst = _charFrontColor;
|
||||
} else {
|
||||
*dst = _charShadowColor;
|
||||
}
|
||||
} else if (_charTransparentColor != 0xFF) {
|
||||
*dst = _charTransparentColor;
|
||||
}
|
||||
++dst;
|
||||
}
|
||||
dst += 256 - 8;
|
||||
}
|
||||
}
|
||||
|
||||
const char *Video::drawString(const char *str, int16 x, int16 y, uint8 col) {
|
||||
debug(DBG_VIDEO, "Video::drawString('%s', %d, %d, 0x%X)", str, x, y, col);
|
||||
int len = 0;
|
||||
int offset = y * 256 + x;
|
||||
uint8 *dst = _frontLayer + offset;
|
||||
while (1) {
|
||||
uint8 c = *str++;
|
||||
if (c == 0 || c == 0xB || c == 0xA) {
|
||||
break;
|
||||
}
|
||||
uint8 *dst_char = dst;
|
||||
const uint8 *src = _res->_fnt + (c - 32) * 32;
|
||||
for (int h = 0; h < 8; ++h) {
|
||||
for (int w = 0; w < 4; ++w) {
|
||||
uint8 c1 = (*src & 0xF0) >> 4;
|
||||
uint8 c2 = (*src & 0x0F) >> 0;
|
||||
++src;
|
||||
if (c1 != 0) {
|
||||
*dst_char = (c1 == 0xF) ? col : (0xE0 + c1);
|
||||
}
|
||||
++dst_char;
|
||||
if (c2 != 0) {
|
||||
*dst_char = (c2 == 0xF) ? col : (0xE0 + c2);
|
||||
}
|
||||
++dst_char;
|
||||
}
|
||||
dst_char += 256 - 8;
|
||||
}
|
||||
dst += 8; // character width
|
||||
++len;
|
||||
}
|
||||
markBlockAsDirty(x, y, len * 8, 8);
|
||||
return str - 1;
|
||||
}
|
||||
Reference in New Issue
Block a user