Import 0.4.0

This commit is contained in:
Gregory Montoir 2018-02-11 00:00:00 +08:00
parent be1f81f217
commit bfef522ada
19 changed files with 1075 additions and 218 deletions

View File

@ -9,9 +9,9 @@ ZLIB_LIBS := -lz
CXXFLAGS += -Wall -MMD $(SDL_CFLAGS) -DUSE_MODPLUG -DUSE_TREMOR -DUSE_ZLIB CXXFLAGS += -Wall -MMD $(SDL_CFLAGS) -DUSE_MODPLUG -DUSE_TREMOR -DUSE_ZLIB
SRCS = collision.cpp cutscene.cpp dynlib.cpp file.cpp fs.cpp game.cpp graphics.cpp main.cpp menu.cpp \ SRCS = collision.cpp cutscene.cpp decode_mac.cpp dynlib.cpp file.cpp fs.cpp game.cpp graphics.cpp main.cpp \
mixer.cpp mod_player.cpp ogg_player.cpp piege.cpp resource.cpp resource_aba.cpp \ menu.cpp mixer.cpp mod_player.cpp ogg_player.cpp piege.cpp resource.cpp resource_aba.cpp \
scaler.cpp screenshot.cpp seq_player.cpp \ resource_mac.cpp scaler.cpp screenshot.cpp seq_player.cpp \
sfx_player.cpp staticres.cpp systemstub_sdl.cpp unpack.cpp util.cpp video.cpp sfx_player.cpp staticres.cpp systemstub_sdl.cpp unpack.cpp util.cpp video.cpp
OBJS = $(SRCS:.cpp=.o) OBJS = $(SRCS:.cpp=.o)

View File

@ -1,6 +1,6 @@
REminiscence README REminiscence README
Release version: 0.3.7 Release version: 0.4.0
------------------------------------------------------------------------------- -------------------------------------------------------------------------------
@ -22,11 +22,15 @@ libraries are required.
Data Files: Data Files:
----------- -----------
You will need the original files of the PC (DOS or CD) or Amiga release. You will need the original files of the PC (DOS or CD), Amiga or Macintosh
release.
For the Macintosh release, the resource fork must dumped as a file named
'FLASHBACK.BIN'. It shall contain 38 distinct data types.
To have background music during polygonal cutscenes with the PC version, To have background music during polygonal cutscenes with the PC version,
you need to copy the music/ directory of the Amiga version or use the .mod you need to copy the music/ directory of the Amiga version or use the .mod
fileset from unexotica. fileset from unexotica [4].
To hear voice during in-game dialogues, you'll need to copy the 'VOICE.VCE' To hear voice during in-game dialogues, you'll need to copy the 'VOICE.VCE'
file from the SegaCD version to the DATA directory. file from the SegaCD version to the DATA directory.
@ -93,3 +97,4 @@ URLs:
[1] http://www.mobygames.com/game/flashback-the-quest-for-identity [1] http://www.mobygames.com/game/flashback-the-quest-for-identity
[2] http://en.wikipedia.org/wiki/Flashback:_The_Quest_for_Identity [2] http://en.wikipedia.org/wiki/Flashback:_The_Quest_for_Identity
[3] http://ramal.free.fr/fb_en.htm [3] http://ramal.free.fr/fb_en.htm
[4] https://www.exotica.org.uk/wiki/Flashback

View File

@ -11,12 +11,30 @@
#include "util.h" #include "util.h"
#include "video.h" #include "video.h"
static void scalePoints(Point *pt, int count, int scale) {
if (scale != 1) {
while (count-- > 0) {
pt->x *= scale;
pt->y *= scale;
++pt;
}
}
}
Cutscene::Cutscene(Resource *res, SystemStub *stub, Video *vid) Cutscene::Cutscene(Resource *res, SystemStub *stub, Video *vid)
: _res(res), _stub(stub), _vid(vid) { : _res(res), _stub(stub), _vid(vid) {
_patchedOffsetsTable = 0; _patchedOffsetsTable = 0;
memset(_palBuf, 0, sizeof(_palBuf)); memset(_palBuf, 0, sizeof(_palBuf));
} }
const uint8_t *Cutscene::getCommandData() const {
return _res->_cmd;
}
const uint8_t *Cutscene::getPolygonData() const {
return _res->_pol;
}
void Cutscene::sync() { void Cutscene::sync() {
if (_stub->_pi.quit) { if (_stub->_pi.quit) {
return; return;
@ -57,7 +75,7 @@ void Cutscene::setPalette() {
sync(); sync();
updatePalette(); updatePalette();
SWAP(_page0, _page1); SWAP(_page0, _page1);
_stub->copyRect(0, 0, _vid->_w, _vid->_h, _page0, 256); _stub->copyRect(0, 0, _vid->_w, _vid->_h, _page0, _vid->_w);
_stub->updateScreen(0); _stub->updateScreen(0);
} }
@ -119,6 +137,10 @@ uint16_t Cutscene::findTextSeparators(const uint8_t *p) {
void Cutscene::drawText(int16_t x, int16_t y, const uint8_t *p, uint16_t color, uint8_t *page, int textJustify) { void Cutscene::drawText(int16_t x, int16_t y, const uint8_t *p, uint16_t color, uint8_t *page, int textJustify) {
debug(DBG_CUT, "Cutscene::drawText(x=%d, y=%d, c=%d, justify=%d)", x, y, color, textJustify); debug(DBG_CUT, "Cutscene::drawText(x=%d, y=%d, c=%d, justify=%d)", x, y, color, textJustify);
if (_res->_type == kResourceTypeMac) {
warning("Unhandled Cutscene::drawText"); // TODO
return;
}
Video::drawCharFunc dcf = _vid->_drawChar; Video::drawCharFunc dcf = _vid->_drawChar;
const uint8_t *fnt = (_res->_lang == LANG_JP) ? Video::_font8Jp : _res->_fnt; const uint8_t *fnt = (_res->_lang == LANG_JP) ? Video::_font8Jp : _res->_fnt;
uint16_t last_sep = 0; uint16_t last_sep = 0;
@ -134,23 +156,22 @@ void Cutscene::drawText(int16_t x, int16_t y, const uint8_t *p, uint16_t color,
int16_t yy = y; int16_t yy = y;
int16_t xx = x; int16_t xx = x;
if (textJustify != kTextJustifyLeft) { if (textJustify != kTextJustifyLeft) {
xx += ((last_sep - *sep++) / 2) * 8; xx += ((last_sep - *sep++) / 2) * Video::CHAR_W;
} }
for (; *p != 0xA && *p; ++p) { for (; *p != 0xA && *p; ++p) {
if (isNewLineChar(*p, _res)) { if (isNewLineChar(*p, _res)) {
yy += 8; yy += Video::CHAR_H;
xx = x; xx = x;
if (textJustify != kTextJustifyLeft) { if (textJustify != kTextJustifyLeft) {
xx += ((last_sep - *sep++) / 2) * 8; xx += ((last_sep - *sep++) / 2) * Video::CHAR_W;
} }
} else if (*p == 0x20) { } else if (*p == 0x20) {
xx += 8; xx += Video::CHAR_W;
} else if (*p == 0x9) { } else if (*p == 0x9) {
// ignore tab // ignore tab
} else { } else {
uint8_t *dst = page + 256 * yy + xx; (_vid->*dcf)(page, _vid->_w, xx, yy, fnt, color, *p);
(_vid->*dcf)(dst, 256, fnt, color, *p); xx += Video::CHAR_W;
xx += 8;
} }
} }
} }
@ -282,7 +303,7 @@ void Cutscene::op_waitForSync() {
void Cutscene::drawShape(const uint8_t *data, int16_t x, int16_t y) { void Cutscene::drawShape(const uint8_t *data, int16_t x, int16_t y) {
debug(DBG_CUT, "Cutscene::drawShape()"); debug(DBG_CUT, "Cutscene::drawShape()");
_gfx._layer = _page1; _gfx.setLayer(_page1, _vid->_w);
uint8_t numVertices = *data++; uint8_t numVertices = *data++;
if (numVertices & 0x80) { if (numVertices & 0x80) {
Point pt; Point pt;
@ -290,11 +311,13 @@ void Cutscene::drawShape(const uint8_t *data, int16_t x, int16_t y) {
pt.y = READ_BE_UINT16(data) + y; data += 2; pt.y = READ_BE_UINT16(data) + y; data += 2;
uint16_t rx = READ_BE_UINT16(data); data += 2; uint16_t rx = READ_BE_UINT16(data); data += 2;
uint16_t ry = READ_BE_UINT16(data); data += 2; uint16_t ry = READ_BE_UINT16(data); data += 2;
scalePoints(&pt, 1, _vid->_layerScale);
_gfx.drawEllipse(_primitiveColor, _hasAlphaColor, &pt, rx, ry); _gfx.drawEllipse(_primitiveColor, _hasAlphaColor, &pt, rx, ry);
} else if (numVertices == 0) { } else if (numVertices == 0) {
Point pt; Point pt;
pt.x = READ_BE_UINT16(data) + x; data += 2; pt.x = READ_BE_UINT16(data) + x; data += 2;
pt.y = READ_BE_UINT16(data) + y; data += 2; pt.y = READ_BE_UINT16(data) + y; data += 2;
scalePoints(&pt, 1, _vid->_layerScale);
_gfx.drawPoint(_primitiveColor, &pt); _gfx.drawPoint(_primitiveColor, &pt);
} else { } else {
Point *pt = _vertices; Point *pt = _vertices;
@ -319,6 +342,7 @@ void Cutscene::drawShape(const uint8_t *data, int16_t x, int16_t y) {
++pt; ++pt;
} }
} }
scalePoints(_vertices, numVertices, _vid->_layerScale);
_gfx.drawPolygon(_primitiveColor, _hasAlphaColor, _vertices, numVertices); _gfx.drawPolygon(_primitiveColor, _hasAlphaColor, _vertices, numVertices);
} }
} }
@ -384,16 +408,16 @@ void Cutscene::op_drawStringAtBottom() {
// 'espions' - ignore last call, allows caption to be displayed longer on the screen // 'espions' - ignore last call, allows caption to be displayed longer on the screen
if (_id == 0x39 && strId == 0xFFFF) { if (_id == 0x39 && strId == 0xFFFF) {
if ((_res->isDOS() && (_cmdPtr - _cmdPtrBak) == 0x10) || (_res->isAmiga() && (_cmdPtr - _res->_cmd) == 0x9F3)) { if ((_res->isDOS() && (_cmdPtr - _cmdPtrBak) == 0x10) || (_res->isAmiga() && (_cmdPtr - getCommandData()) == 0x9F3)) {
_frameDelay = 100; _frameDelay = 100;
setPalette(); setPalette();
return; return;
} }
} }
memset(_pageC + 179 * 256, 0xC0, 45 * 256); memset(_pageC + 179 * _vid->_w, 0xC0, 45 * _vid->_w);
memset(_page1 + 179 * 256, 0xC0, 45 * 256); memset(_page1 + 179 * _vid->_w, 0xC0, 45 * _vid->_w);
memset(_page0 + 179 * 256, 0xC0, 45 * 256); memset(_page0 + 179 * _vid->_w, 0xC0, 45 * _vid->_w);
if (strId != 0xFFFF) { if (strId != 0xFFFF) {
const uint8_t *str = _res->getCineString(strId); const uint8_t *str = _res->getCineString(strId);
if (str) { if (str) {
@ -424,7 +448,7 @@ void Cutscene::op_refreshAll() {
void Cutscene::drawShapeScale(const uint8_t *data, int16_t zoom, int16_t b, int16_t c, int16_t d, int16_t e, int16_t f, int16_t g) { void Cutscene::drawShapeScale(const uint8_t *data, int16_t zoom, int16_t b, int16_t c, int16_t d, int16_t e, int16_t f, int16_t g) {
debug(DBG_CUT, "Cutscene::drawShapeScale(%d, %d, %d, %d, %d, %d, %d)", zoom, b, c, d, e, f, g); debug(DBG_CUT, "Cutscene::drawShapeScale(%d, %d, %d, %d, %d, %d, %d)", zoom, b, c, d, e, f, g);
_gfx._layer = _page1; _gfx.setLayer(_page1, _vid->_w);
uint8_t numVertices = *data++; uint8_t numVertices = *data++;
if (numVertices & 0x80) { if (numVertices & 0x80) {
int16_t x, y; int16_t x, y;
@ -473,6 +497,7 @@ void Cutscene::drawShapeScale(const uint8_t *data, int16_t zoom, int16_t b, int1
po.y = _vertices[0].y + e + _shape_iy; po.y = _vertices[0].y + e + _shape_iy;
int16_t rx = _vertices[0].x - _vertices[2].x; int16_t rx = _vertices[0].x - _vertices[2].x;
int16_t ry = _vertices[0].y - _vertices[1].y; int16_t ry = _vertices[0].y - _vertices[1].y;
scalePoints(&po, 1, _vid->_layerScale);
_gfx.drawEllipse(_primitiveColor, _hasAlphaColor, &po, rx, ry); _gfx.drawEllipse(_primitiveColor, _hasAlphaColor, &po, rx, ry);
} else if (numVertices == 0) { } else if (numVertices == 0) {
Point pt; Point pt;
@ -495,6 +520,7 @@ void Cutscene::drawShapeScale(const uint8_t *data, int16_t zoom, int16_t b, int1
_shape_prev_y = _shape_cur_y; _shape_prev_y = _shape_cur_y;
_shape_prev_x16 = _shape_cur_x16; _shape_prev_x16 = _shape_cur_x16;
_shape_prev_y16 = _shape_cur_y16; _shape_prev_y16 = _shape_cur_y16;
scalePoints(&pt, 1, _vid->_layerScale);
_gfx.drawPoint(_primitiveColor, &pt); _gfx.drawPoint(_primitiveColor, &pt);
} else { } else {
Point *pt = _vertices; Point *pt = _vertices;
@ -540,6 +566,7 @@ void Cutscene::drawShapeScale(const uint8_t *data, int16_t zoom, int16_t b, int1
_shape_prev_y = _shape_cur_y; _shape_prev_y = _shape_cur_y;
_shape_prev_x16 = _shape_cur_x16; _shape_prev_x16 = _shape_cur_x16;
_shape_prev_y16 = _shape_cur_y16; _shape_prev_y16 = _shape_cur_y16;
scalePoints(_vertices, numVertices, _vid->_layerScale);
_gfx.drawPolygon(_primitiveColor, _hasAlphaColor, _vertices, numVertices); _gfx.drawPolygon(_primitiveColor, _hasAlphaColor, _vertices, numVertices);
} }
} }
@ -603,7 +630,7 @@ void Cutscene::op_drawShapeScale() {
void Cutscene::drawShapeScaleRotate(const uint8_t *data, int16_t zoom, int16_t b, int16_t c, int16_t d, int16_t e, int16_t f, int16_t g) { void Cutscene::drawShapeScaleRotate(const uint8_t *data, int16_t zoom, int16_t b, int16_t c, int16_t d, int16_t e, int16_t f, int16_t g) {
debug(DBG_CUT, "Cutscene::drawShapeScaleRotate(%d, %d, %d, %d, %d, %d, %d)", zoom, b, c, d, e, f, g); debug(DBG_CUT, "Cutscene::drawShapeScaleRotate(%d, %d, %d, %d, %d, %d, %d)", zoom, b, c, d, e, f, g);
_gfx._layer = _page1; _gfx.setLayer(_page1, _vid->_w);
uint8_t numVertices = *data++; uint8_t numVertices = *data++;
if (numVertices & 0x80) { if (numVertices & 0x80) {
int16_t x, y, ix, iy; int16_t x, y, ix, iy;
@ -654,6 +681,7 @@ void Cutscene::drawShapeScaleRotate(const uint8_t *data, int16_t zoom, int16_t b
po.y = _vertices[0].y + e + _shape_iy; po.y = _vertices[0].y + e + _shape_iy;
int16_t rx = _vertices[0].x - _vertices[2].x; int16_t rx = _vertices[0].x - _vertices[2].x;
int16_t ry = _vertices[0].y - _vertices[1].y; int16_t ry = _vertices[0].y - _vertices[1].y;
scalePoints(&po, 1, _vid->_layerScale);
_gfx.drawEllipse(_primitiveColor, _hasAlphaColor, &po, rx, ry); _gfx.drawEllipse(_primitiveColor, _hasAlphaColor, &po, rx, ry);
} else if (numVertices == 0) { } else if (numVertices == 0) {
Point pt; Point pt;
@ -680,6 +708,7 @@ void Cutscene::drawShapeScaleRotate(const uint8_t *data, int16_t zoom, int16_t b
_shape_prev_y = _shape_cur_y; _shape_prev_y = _shape_cur_y;
_shape_prev_x16 = _shape_cur_x16; _shape_prev_x16 = _shape_cur_x16;
_shape_prev_y16 = _shape_cur_y16; _shape_prev_y16 = _shape_cur_y16;
scalePoints(&pt, 1, _vid->_layerScale);
_gfx.drawPoint(_primitiveColor, &pt); _gfx.drawPoint(_primitiveColor, &pt);
} else { } else {
int16_t x, y, a, shape_last_x, shape_last_y; int16_t x, y, a, shape_last_x, shape_last_y;
@ -758,6 +787,7 @@ void Cutscene::drawShapeScaleRotate(const uint8_t *data, int16_t zoom, int16_t b
_shape_prev_y = _shape_cur_y; _shape_prev_y = _shape_cur_y;
_shape_prev_x16 = _shape_cur_x16; _shape_prev_x16 = _shape_cur_x16;
_shape_prev_y16 = _shape_cur_y16; _shape_prev_y16 = _shape_cur_y16;
scalePoints(_vertices, numVertices + 1, _vid->_layerScale);
_gfx.drawPolygon(_primitiveColor, _hasAlphaColor, _vertices, numVertices + 1); _gfx.drawPolygon(_primitiveColor, _hasAlphaColor, _vertices, numVertices + 1);
} }
} }
@ -905,10 +935,10 @@ void Cutscene::op_handleKeys() {
} }
_varKey = 0; _varKey = 0;
--n; --n;
_cmdPtr = _res->_cmd; _cmdPtr = getCommandData();
n = READ_BE_UINT16(_cmdPtr + n * 2 + 2); n = READ_BE_UINT16(_cmdPtr + n * 2 + 2);
} }
_cmdPtr = _cmdPtrBak = _res->_cmd + n + _startOffset; _cmdPtr = _cmdPtrBak = getCommandData() + n + _startOffset;
} }
uint8_t Cutscene::fetchNextCmdByte() { uint8_t Cutscene::fetchNextCmdByte() {
@ -932,14 +962,14 @@ void Cutscene::mainLoop(uint16_t offset) {
} }
_newPal = false; _newPal = false;
_hasAlphaColor = false; _hasAlphaColor = false;
uint8_t *p = _res->_cmd; const uint8_t *p = getCommandData();
if (offset != 0) { if (offset != 0) {
offset = READ_BE_UINT16(p + (offset + 1) * 2); offset = READ_BE_UINT16(p + (offset + 1) * 2);
} }
_startOffset = (READ_BE_UINT16(p) + 1) * 2; _startOffset = (READ_BE_UINT16(p) + 1) * 2;
_varKey = 0; _varKey = 0;
_cmdPtr = _cmdPtrBak = p + _startOffset + offset; _cmdPtr = _cmdPtrBak = p + _startOffset + offset;
_polPtr = _res->_pol; _polPtr = getPolygonData();
debug(DBG_CUT, "_startOffset = %d offset = %d", _startOffset, offset); debug(DBG_CUT, "_startOffset = %d offset = %d", _startOffset, offset);
while (!_stub->_pi.quit && !_interrupted && !_stop) { while (!_stub->_pi.quit && !_interrupted && !_stop) {
@ -992,6 +1022,9 @@ bool Cutscene::load(uint16_t cutName) {
_res->load(name, Resource::OT_CMD); _res->load(name, Resource::OT_CMD);
_res->load(name, Resource::OT_POL); _res->load(name, Resource::OT_POL);
break; break;
case kResourceTypeMac:
_res->MAC_loadCutscene(name);
break;
} }
_res->load_CINE(); _res->load_CINE();
return _res->_cmd && _res->_pol; return _res->_cmd && _res->_pol;
@ -1006,6 +1039,9 @@ void Cutscene::unload() {
_res->unload(Resource::OT_CMD); _res->unload(Resource::OT_CMD);
_res->unload(Resource::OT_POL); _res->unload(Resource::OT_POL);
break; break;
case kResourceTypeMac:
_res->MAC_unloadCutscene();
break;
} }
} }
@ -1019,7 +1055,15 @@ void Cutscene::prepare() {
_stub->_pi.shift = false; _stub->_pi.shift = false;
_interrupted = false; _interrupted = false;
_stop = false; _stop = false;
_gfx.setClippingRect(8, 50, 240, 128); const int w = 240;
const int h = 128;
const int x = (Video::GAMESCREEN_W - w) / 2;
const int y = 50;
const int sw = w * _vid->_layerScale;
const int sh = h * _vid->_layerScale;
const int sx = x * _vid->_layerScale;
const int sy = y * _vid->_layerScale;
_gfx.setClippingRect(sx, sy, sw, sh);
} }
void Cutscene::playCredits() { void Cutscene::playCredits() {
@ -1066,7 +1110,7 @@ void Cutscene::playText(const char *str) {
const int y = (128 - lines * 8) / 2; const int y = (128 - lines * 8) / 2;
memset(_page1, 0xC0, _vid->_layerSize); memset(_page1, 0xC0, _vid->_layerSize);
drawText(0, y, (const uint8_t *)str, 0xC1, _page1, kTextJustifyAlign); drawText(0, y, (const uint8_t *)str, 0xC1, _page1, kTextJustifyAlign);
_stub->copyRect(0, 0, _vid->_w, _vid->_h, _page1, 256); _stub->copyRect(0, 0, _vid->_w, _vid->_h, _page1, _vid->_w);
_stub->updateScreen(0); _stub->updateScreen(0);
while (!_stub->_pi.quit) { while (!_stub->_pi.quit) {
@ -1087,19 +1131,25 @@ void Cutscene::play() {
prepare(); prepare();
uint16_t cutName = _offsetsTable[_id * 2 + 0]; uint16_t cutName = _offsetsTable[_id * 2 + 0];
uint16_t cutOff = _offsetsTable[_id * 2 + 1]; uint16_t cutOff = _offsetsTable[_id * 2 + 1];
if (cutName == 0xFFFF && g_options.play_disabled_cutscenes) { if (cutName == 0xFFFF) {
switch (_id) { switch (_id) {
case 19: case 19:
cutName = 31; // SERRURE if (g_options.play_serrure_cutscene) {
cutName = 31; // SERRURE
}
break; break;
case 22: case 22:
case 23: case 23:
case 24: case 24:
cutName = 12; // ASC if (g_options.play_asc_cutscene) {
cutName = 12; // ASC
}
break; break;
case 30: case 30:
case 31: case 31:
cutName = 14; // METRO if (g_options.play_metro_cutscene) {
cutName = 14; // METRO
}
break; break;
} }
} }
@ -1125,7 +1175,7 @@ void Cutscene::play() {
mainLoop(cutOff); mainLoop(cutOff);
unload(); unload();
} }
} else if (_id == 8 && g_options.play_stone_cutscene) { } else if (_id == 8 && g_options.play_caillou_cutscene) {
playSet(_caillouSetData, 0x5E4); playSet(_caillouSetData, 0x5E4);
} }
_vid->fullRefresh(); _vid->fullRefresh();
@ -1158,12 +1208,14 @@ void Cutscene::drawSetShape(const uint8_t *p, uint16_t offset, int x, int y, uin
Point pt; Point pt;
pt.x = x + ix; pt.x = x + ix;
pt.y = y + iy; pt.y = y + iy;
scalePoints(&pt, 1, _vid->_layerScale);
_gfx.drawEllipse(color, false, &pt, rx, ry); _gfx.drawEllipse(color, false, &pt, rx, ry);
} else { } else {
for (int i = 0; i < verticesCount; ++i) { for (int i = 0; i < verticesCount; ++i) {
_vertices[i].x = x + (int16_t)READ_BE_UINT16(p + offset); offset += 2; _vertices[i].x = x + (int16_t)READ_BE_UINT16(p + offset); offset += 2;
_vertices[i].y = y + (int16_t)READ_BE_UINT16(p + offset); offset += 2; _vertices[i].y = y + (int16_t)READ_BE_UINT16(p + offset); offset += 2;
} }
scalePoints(_vertices, verticesCount, _vid->_layerScale);
_gfx.drawPolygon(color, false, _vertices, verticesCount); _gfx.drawPolygon(color, false, _vertices, verticesCount);
} }
} }
@ -1208,7 +1260,7 @@ void Cutscene::playSet(const uint8_t *p, int offset) {
} }
prepare(); prepare();
_gfx._layer = _page1; _gfx.setLayer(_page1, _vid->_w);
offset = 10; offset = 10;
const int frames = READ_BE_UINT16(p + offset); offset += 2; const int frames = READ_BE_UINT16(p + offset); offset += 2;
@ -1262,7 +1314,7 @@ void Cutscene::playSet(const uint8_t *p, int offset) {
_stub->setPaletteEntry(0xC0 + j, &c); _stub->setPaletteEntry(0xC0 + j, &c);
} }
_stub->copyRect(0, 0, _vid->_w, _vid->_h, _page1, 256); _stub->copyRect(0, 0, _vid->_w, _vid->_h, _page1, _vid->_w);
_stub->updateScreen(0); _stub->updateScreen(0);
const int diff = 6 * TIMER_SLICE - (_stub->getTimeStamp() - timestamp); const int diff = 6 * TIMER_SLICE - (_stub->getTimeStamp() - timestamp);
_stub->sleep((diff < 16) ? 16 : diff); _stub->sleep((diff < 16) ? 16 : diff);

View File

@ -64,9 +64,9 @@ struct Cutscene {
uint16_t _deathCutsceneId; uint16_t _deathCutsceneId;
bool _interrupted; bool _interrupted;
bool _stop; bool _stop;
uint8_t *_polPtr; const uint8_t *_polPtr;
uint8_t *_cmdPtr; const uint8_t *_cmdPtr;
uint8_t *_cmdPtrBak; const uint8_t *_cmdPtrBak;
uint32_t _tstamp; uint32_t _tstamp;
uint8_t _frameDelay; uint8_t _frameDelay;
bool _newPal; bool _newPal;
@ -105,6 +105,9 @@ struct Cutscene {
Cutscene(Resource *res, SystemStub *stub, Video *vid); Cutscene(Resource *res, SystemStub *stub, Video *vid);
const uint8_t *getCommandData() const;
const uint8_t *getPolygonData() const;
void sync(); void sync();
void copyPalette(const uint8_t *pal, uint16_t num); void copyPalette(const uint8_t *pal, uint16_t num);
void updatePalette(); void updatePalette();

437
game.cpp
View File

@ -4,7 +4,8 @@
* Copyright (C) 2005-2018 Gregory Montoir (cyx@users.sourceforge.net) * Copyright (C) 2005-2018 Gregory Montoir (cyx@users.sourceforge.net)
*/ */
#include <ctime> #include <time.h>
#include "decode_mac.h"
#include "file.h" #include "file.h"
#include "fs.h" #include "fs.h"
#include "game.h" #include "game.h"
@ -46,6 +47,10 @@ void Game::run() {
_cut._patchedOffsetsTable = Cutscene::_ssiOffsetsTable; _cut._patchedOffsetsTable = Cutscene::_ssiOffsetsTable;
} }
break; break;
case kResourceTypeMac:
_res.MAC_loadClutData();
_res.MAC_loadFontData();
break;
} }
if (!g_options.bypass_protection) { if (!g_options.bypass_protection) {
@ -74,12 +79,15 @@ void Game::run() {
_res.load_SPR_OFF("PERSO", _res._spr1); _res.load_SPR_OFF("PERSO", _res._spr1);
_res.load_FIB("GLOBAL"); _res.load_FIB("GLOBAL");
break; break;
case kResourceTypeMac:
_res.MAC_loadIconData();
_res.MAC_loadPersoData();
break;
} }
bool presentMenu = (_res._type == kResourceTypeAmiga) || (_res._type == kResourceTypeDOS && _res.fileExists("MENU1.MAP"));
while (!_stub->_pi.quit) { while (!_stub->_pi.quit) {
if (_res._isDemo) { if (presentMenu) {
// do not present title screen and menus
} else {
_mix.playMusic(1); _mix.playMusic(1);
switch (_res._type) { switch (_res._type) {
case kResourceTypeDOS: case kResourceTypeDOS:
@ -111,6 +119,9 @@ void Game::run() {
displayTitleScreenAmiga(); displayTitleScreenAmiga();
_stub->setScreenSize(Video::GAMESCREEN_W, Video::GAMESCREEN_H); _stub->setScreenSize(Video::GAMESCREEN_W, Video::GAMESCREEN_H);
break; break;
case kResourceTypeMac:
// TODO:
break;
} }
if (_stub->_pi.quit) { if (_stub->_pi.quit) {
break; break;
@ -374,9 +385,12 @@ void Game::playCutscene(int id) {
_mix.playMusic(Cutscene::_musicTable[_cut._id]); _mix.playMusic(Cutscene::_musicTable[_cut._id]);
} }
_cut.play(); _cut.play();
if (id == 0xD && !_cut._interrupted && _res.isDOS()) { if (id == 0xD && !_cut._interrupted) {
_cut._id = 0x4A; const bool extendedIntroduction = (_res._type == kResourceTypeDOS || _res._type == kResourceTypeMac);
_cut.play(); if (extendedIntroduction) {
_cut._id = 0x4A;
_cut.play();
}
} }
if (id == 0x3D) { if (id == 0x3D) {
_cut.playCredits(); _cut.playCredits();
@ -430,11 +444,11 @@ void Game::showFinalScore() {
playCutscene(0x49); playCutscene(0x49);
char buf[50]; char buf[50];
snprintf(buf, sizeof(buf), "SCORE %08u", _score); snprintf(buf, sizeof(buf), "SCORE %08u", _score);
_vid.drawString(buf, (256 - strlen(buf) * 8) / 2, 40, 0xE5); _vid.drawString(buf, (Video::GAMESCREEN_W - strlen(buf) * Video::CHAR_W) / 2, 40, 0xE5);
strcpy(buf, _menu._passwords[7][_skillLevel]); const char *str = _menu.getLevelPassword(7, _skillLevel);
_vid.drawString(buf, (256 - strlen(buf) * 8) / 2, 16, 0xE7); _vid.drawString(str, (Video::GAMESCREEN_W - strlen(str) * Video::CHAR_W) / 2, 16, 0xE7);
while (!_stub->_pi.quit) { while (!_stub->_pi.quit) {
_stub->copyRect(0, 0, _vid._w, _vid._h, _vid._frontLayer, 256); _stub->copyRect(0, 0, _vid._w, _vid._h, _vid._frontLayer, _vid._w);
_stub->updateScreen(0); _stub->updateScreen(0);
_stub->processEvents(); _stub->processEvents();
if (_stub->_pi.enter) { if (_stub->_pi.enter) {
@ -446,9 +460,6 @@ void Game::showFinalScore() {
} }
bool Game::handleConfigPanel() { bool Game::handleConfigPanel() {
if (_res.isAmiga()) {
return true;
}
const int x = 7; const int x = 7;
const int y = 10; const int y = 10;
const int w = 17; const int w = 17;
@ -461,33 +472,60 @@ bool Game::handleConfigPanel() {
// the panel background is drawn using special characters from FB_TXT.FNT // the panel background is drawn using special characters from FB_TXT.FNT
static const bool kUseDefaultFont = true; static const bool kUseDefaultFont = true;
// top-left rounded corder switch (_res._type) {
_vid.PC_drawChar(0x81, y, x, kUseDefaultFont); case kResourceTypeAmiga:
// top horizontal line // TODO
for (int i = 1; i < w; ++i) { return true;
_vid.PC_drawChar(0x85, y, x + i, kUseDefaultFont); case kResourceTypeDOS:
} // top-left rounded corner
// top-right rounded corner _vid.PC_drawChar(0x81, y, x, kUseDefaultFont);
_vid.PC_drawChar(0x82, y, x + w, kUseDefaultFont); // top-right rounded corner
for (int j = 1; j < h; ++j) { _vid.PC_drawChar(0x82, y, x + w, kUseDefaultFont);
// left vertical line // bottom-left rounded corner
_vid.PC_drawChar(0x86, y + j, x, kUseDefaultFont); _vid.PC_drawChar(0x83, y + h, x, kUseDefaultFont);
// bottom-right rounded corner
_vid.PC_drawChar(0x84, y + h, x + w, kUseDefaultFont);
// horizontal lines
for (int i = 1; i < w; ++i) { for (int i = 1; i < w; ++i) {
_vid._charTransparentColor = 0xE2; _vid.PC_drawChar(0x85, y, x + i, kUseDefaultFont);
_vid.PC_drawChar(0x20, y + j, x + i, kUseDefaultFont); _vid.PC_drawChar(0x88, y + h, x + i, kUseDefaultFont);
} }
_vid._charTransparentColor = 0xFF; for (int j = 1; j < h; ++j) {
// right vertical line _vid._charTransparentColor = 0xFF;
_vid.PC_drawChar(0x87, y + j, x + w, kUseDefaultFont); // left vertical line
_vid.PC_drawChar(0x86, y + j, x, kUseDefaultFont);
// right vertical line
_vid.PC_drawChar(0x87, y + j, x + w, kUseDefaultFont);
_vid._charTransparentColor = 0xE2;
for (int i = 1; i < w; ++i) {
_vid.PC_drawChar(0x20, y + j, x + i, kUseDefaultFont);
}
}
break;
case kResourceTypeMac:
// top-left rounded corner
_vid.MAC_drawStringChar(_vid._frontLayer, _vid._w, Video::CHAR_W * x, Video::CHAR_H * y, _res._fnt, _vid._charFrontColor, 0x81);
// top-right rounded corner
_vid.MAC_drawStringChar(_vid._frontLayer, _vid._w, Video::CHAR_W * (x + w), Video::CHAR_H * y, _res._fnt, _vid._charFrontColor, 0x82);
// bottom-left rounded corner
_vid.MAC_drawStringChar(_vid._frontLayer, _vid._w, Video::CHAR_W * x, Video::CHAR_H * (y + h), _res._fnt, _vid._charFrontColor, 0x83);
// bottom-right rounded corner
_vid.MAC_drawStringChar(_vid._frontLayer, _vid._w, Video::CHAR_W * (x + w), Video::CHAR_H * (y + h), _res._fnt, _vid._charFrontColor, 0x84);
// horizontal lines
for (int i = 1; i < w; ++i) {
_vid.MAC_drawStringChar(_vid._frontLayer, _vid._w, Video::CHAR_W * (x + i), Video::CHAR_H * y, _res._fnt, _vid._charFrontColor, 0x85);
_vid.MAC_drawStringChar(_vid._frontLayer, _vid._w, Video::CHAR_W * (x + i), Video::CHAR_H * (y + h), _res._fnt, _vid._charFrontColor, 0x88);
}
// vertical lines
for (int i = 1; i < h; ++i) {
_vid.MAC_drawStringChar(_vid._frontLayer, _vid._w, Video::CHAR_W * x, Video::CHAR_H * (y + i), _res._fnt, _vid._charFrontColor, 0x86);
_vid.MAC_drawStringChar(_vid._frontLayer, _vid._w, Video::CHAR_W * (x + w), Video::CHAR_H * (y + i), _res._fnt, _vid._charFrontColor, 0x87);
for (int j = 1; j < w; ++j) {
_vid.MAC_fillRect(Video::CHAR_W * (x + j), Video::CHAR_H * (y + i), Video::CHAR_W, Video::CHAR_H, 0xE2);
}
}
break;
} }
// bottom-left rounded corner
_vid.PC_drawChar(0x83, y + h, x, kUseDefaultFont);
// bottom horizontal line
for (int i = 1; i < w; ++i) {
_vid.PC_drawChar(0x88, y + h, x + i, kUseDefaultFont);
}
// bottom-right rounded corner
_vid.PC_drawChar(0x84, y + h, x + w, kUseDefaultFont);
_menu._charVar3 = 0xE4; _menu._charVar3 = 0xE4;
_menu._charVar4 = 0xE5; _menu._charVar4 = 0xE5;
@ -570,15 +608,15 @@ bool Game::handleContinueAbort() {
while (timeout >= 0 && !_stub->_pi.quit) { while (timeout >= 0 && !_stub->_pi.quit) {
const char *str; const char *str;
str = _res.getMenuString(LocaleData::LI_01_CONTINUE_OR_ABORT); str = _res.getMenuString(LocaleData::LI_01_CONTINUE_OR_ABORT);
_vid.drawString(str, (256 - strlen(str) * 8) / 2, 64, 0xE3); _vid.drawString(str, (Video::GAMESCREEN_W - strlen(str) * Video::CHAR_W) / 2, 64, 0xE3);
str = _res.getMenuString(LocaleData::LI_02_TIME); str = _res.getMenuString(LocaleData::LI_02_TIME);
char buf[50]; char buf[50];
snprintf(buf, sizeof(buf), "%s : %d", str, timeout / 10); snprintf(buf, sizeof(buf), "%s : %d", str, timeout / 10);
_vid.drawString(buf, 96, 88, 0xE3); _vid.drawString(buf, 96, 88, 0xE3);
str = _res.getMenuString(LocaleData::LI_03_CONTINUE); str = _res.getMenuString(LocaleData::LI_03_CONTINUE);
_vid.drawString(str, (256 - strlen(str) * 8) / 2, 104, colors[0]); _vid.drawString(str, (Video::GAMESCREEN_W - strlen(str) * Video::CHAR_W) / 2, 104, colors[0]);
str = _res.getMenuString(LocaleData::LI_04_ABORT); str = _res.getMenuString(LocaleData::LI_04_ABORT);
_vid.drawString(str, (256 - strlen(str) * 8) / 2, 112, colors[1]); _vid.drawString(str, (Video::GAMESCREEN_W - strlen(str) * Video::CHAR_W) / 2, 112, colors[1]);
snprintf(buf, sizeof(buf), "SCORE %08u", _score); snprintf(buf, sizeof(buf), "SCORE %08u", _score);
_vid.drawString(buf, 64, 154, 0xE3); _vid.drawString(buf, 64, 154, 0xE3);
if (_stub->_pi.dirMask & PlayerInput::DIR_UP) { if (_stub->_pi.dirMask & PlayerInput::DIR_UP) {
@ -599,7 +637,7 @@ bool Game::handleContinueAbort() {
_stub->_pi.enter = false; _stub->_pi.enter = false;
return (current_color == 0); return (current_color == 0);
} }
_stub->copyRect(0, 0, _vid._w, _vid._h, _vid._frontLayer, 256); _stub->copyRect(0, 0, _vid._w, _vid._h, _vid._frontLayer, _vid._w);
_stub->updateScreen(0); _stub->updateScreen(0);
static const int COLOR_STEP = 8; static const int COLOR_STEP = 8;
static const int COLOR_MIN = 16; static const int COLOR_MIN = 16;
@ -642,7 +680,7 @@ bool Game::handleProtectionScreen() {
int shapeNum = getRandomNumber() % 30; int shapeNum = getRandomNumber() % 30;
for (int16_t zoom = 2000; zoom != 0; zoom -= 100) { for (int16_t zoom = 2000; zoom != 0; zoom -= 100) {
_cut.drawProtectionShape(shapeNum, zoom); _cut.drawProtectionShape(shapeNum, zoom);
_stub->copyRect(0, 0, _vid._w, _vid._h, _vid._tempLayer, 256); _stub->copyRect(0, 0, _vid._w, _vid._h, _vid._tempLayer, _vid._w);
_stub->updateScreen(0); _stub->updateScreen(0);
_stub->sleep(30); _stub->sleep(30);
} }
@ -654,10 +692,10 @@ bool Game::handleProtectionScreen() {
do { do {
codeText[len] = '\0'; codeText[len] = '\0';
memcpy(_vid._frontLayer, _vid._tempLayer, _vid._layerSize); memcpy(_vid._frontLayer, _vid._tempLayer, _vid._layerSize);
_vid.drawString("PROTECTION", 11 * 8, 2 * 8, _menu._charVar2); _vid.drawString("PROTECTION", 11 * Video::CHAR_W, 2 * Video::CHAR_H, _menu._charVar2);
char buf[20]; char buf[20];
snprintf(buf, sizeof(buf), "CODE %d : %s", codeNum + 1, codeText); snprintf(buf, sizeof(buf), "CODE %d : %s", codeNum + 1, codeText);
_vid.drawString(buf, 8 * 8, 23 * 8, _menu._charVar2); _vid.drawString(buf, 8 * Video::CHAR_W, 23 * Video::CHAR_H, _menu._charVar2);
_vid.updateScreen(); _vid.updateScreen();
_stub->sleep(50); _stub->sleep(50);
_stub->processEvents(); _stub->processEvents();
@ -711,16 +749,8 @@ void Game::printLevelCode() {
--_printLevelCodeCounter; --_printLevelCodeCounter;
if (_printLevelCodeCounter != 0) { if (_printLevelCodeCounter != 0) {
char buf[32]; char buf[32];
const char *code = Menu::_passwords[_currentLevel][_skillLevel]; snprintf(buf, sizeof(buf), "CODE: %s", _menu.getLevelPassword(_currentLevel, _skillLevel));
if (_res.isAmiga()) { _vid.drawString(buf, (Video::GAMESCREEN_W - strlen(buf) * Video::CHAR_W) / 2, 16, 0xE7);
if (_res._lang == LANG_FR) {
code = Menu::_passwordsFrAmiga[_skillLevel * 7 + _currentLevel];
} else {
code = Menu::_passwordsEnAmiga[_skillLevel * 7 + _currentLevel];
}
}
snprintf(buf, sizeof(buf), "CODE: %s", code);
_vid.drawString(buf, (_vid._w - strlen(buf) * 8) / 2, 16, 0xE7);
} }
} }
} }
@ -728,7 +758,7 @@ void Game::printLevelCode() {
void Game::printSaveStateCompleted() { void Game::printSaveStateCompleted() {
if (_saveStateCompleted) { if (_saveStateCompleted) {
const char *str = _res.getMenuString(LocaleData::LI_05_COMPLETED); const char *str = _res.getMenuString(LocaleData::LI_05_COMPLETED);
_vid.drawString(str, (176 - strlen(str) * 8) / 2, 34, 0xE6); _vid.drawString(str, (176 - strlen(str) * Video::CHAR_W) / 2, 34, 0xE6);
} }
} }
@ -744,8 +774,8 @@ void Game::drawLevelTexts() {
uint8_t icon_num = obj - 1; uint8_t icon_num = obj - 1;
drawIcon(icon_num, 80, 8, 0xA); drawIcon(icon_num, 80, 8, 0xA);
uint8_t txt_num = pge->init_PGE->text_num; uint8_t txt_num = pge->init_PGE->text_num;
const char *str = (const char *)_res.getTextString(_currentLevel, txt_num); const uint8_t *str = _res.getTextString(_currentLevel, txt_num);
_vid.drawString(str, (176 - strlen(str) * 8) / 2, 26, 0xE6); drawString(str, 176, 26, 0xE6, true);
if (icon_num == 2) { if (icon_num == 2) {
printSaveStateCompleted(); printSaveStateCompleted();
return; return;
@ -768,6 +798,11 @@ static int getLineLength(const uint8_t *str) {
void Game::drawStoryTexts() { void Game::drawStoryTexts() {
if (_textToDisplay != 0xFFFF) { if (_textToDisplay != 0xFFFF) {
if (_res._type == kResourceTypeMac) {
warning("Unhandled textToDisplay %d", _textToDisplay);
_textToDisplay = 0xFFFF;
return;
}
uint8_t textColor = 0xE8; uint8_t textColor = 0xE8;
const uint8_t *str = _res.getGameString(_textToDisplay); const uint8_t *str = _res.getGameString(_textToDisplay);
memcpy(_vid._tempLayer, _vid._frontLayer, _vid._layerSize); memcpy(_vid._tempLayer, _vid._frontLayer, _vid._layerSize);
@ -797,7 +832,7 @@ void Game::drawStoryTexts() {
int yPos = 26; int yPos = 26;
while (1) { while (1) {
const int len = getLineLength(str); const int len = getLineLength(str);
str = (const uint8_t *)_vid.drawString((const char *)str, (176 - len * 8) / 2, yPos, textColor); str = (const uint8_t *)_vid.drawString((const char *)str, (176 - len * Video::CHAR_W) / 2, yPos, textColor);
yPos += 8; yPos += 8;
if (*str == 0 || *str == 0xB) { if (*str == 0 || *str == 0xB) {
break; break;
@ -832,6 +867,21 @@ void Game::drawStoryTexts() {
} }
} }
void Game::drawString(const uint8_t *p, int x, int y, uint8_t color, bool hcenter) {
const char *str = (const char *)p;
int len = 0;
if (_res._type == kResourceTypeMac) {
len = *p;
++str;
} else {
len = strlen(str);
}
if (hcenter) {
x = (x - len * Video::CHAR_W) / 2;
}
_vid.drawStringLen(str, len, x, y, color);
}
void Game::prepareAnims() { void Game::prepareAnims() {
if (!(_currentRoom & 0x80) && _currentRoom < 0x40) { if (!(_currentRoom & 0x80) && _currentRoom < 0x40) {
int8_t pge_room; int8_t pge_room;
@ -889,13 +939,22 @@ void Game::prepareAnimsHelper(LivePGE *pge, int16_t dx, int16_t dy) {
if (pge->index != 0 && loadMonsterSprites(pge) == 0) { if (pge->index != 0 && loadMonsterSprites(pge) == 0) {
return; return;
} }
assert(pge->anim_number < 1287); const uint8_t *dataPtr = 0;
const uint8_t *dataPtr = _res._sprData[pge->anim_number]; int8_t dw = 0, dh = 0;
if (dataPtr == 0) { switch (_res._type) {
return; case kResourceTypeAmiga:
case kResourceTypeDOS:
assert(pge->anim_number < 1287);
dataPtr = _res._sprData[pge->anim_number];
if (dataPtr == 0) {
return;
}
dw = (int8_t)dataPtr[0];
dh = (int8_t)dataPtr[1];
break;
case kResourceTypeMac:
break;
} }
const int8_t dw = (int8_t)dataPtr[0];
const int8_t dh = (int8_t)dataPtr[1];
uint8_t w = 0, h = 0; uint8_t w = 0, h = 0;
switch (_res._type) { switch (_res._type) {
case kResourceTypeAmiga: case kResourceTypeAmiga:
@ -907,8 +966,10 @@ void Game::prepareAnimsHelper(LivePGE *pge, int16_t dx, int16_t dy) {
h = dataPtr[3]; h = dataPtr[3];
dataPtr += 4; dataPtr += 4;
break; break;
case kResourceTypeMac:
break;
} }
const int16_t ypos = dy + pge->pos_y - dh + 2; int16_t ypos = dy + pge->pos_y - dh + 2;
int16_t xpos = dx + pge->pos_x - dw; int16_t xpos = dx + pge->pos_x - dw;
if (pge->flags & 2) { if (pge->flags & 2) {
xpos = dw + dx + pge->pos_x; xpos = dw + dx + pge->pos_x;
@ -932,8 +993,16 @@ void Game::prepareAnimsHelper(LivePGE *pge, int16_t dx, int16_t dy) {
_animBuffers.addState(0, xpos, ypos, dataPtr, pge, w, h); _animBuffers.addState(0, xpos, ypos, dataPtr, pge, w, h);
} }
} else { } else {
assert(pge->anim_number < _res._numSpc); const uint8_t *dataPtr = 0;
const uint8_t *dataPtr = _res._spc + READ_BE_UINT16(_res._spc + pge->anim_number * 2); switch (_res._type) {
case kResourceTypeAmiga:
case kResourceTypeDOS:
assert(pge->anim_number < _res._numSpc);
dataPtr = _res._spc + READ_BE_UINT16(_res._spc + pge->anim_number * 2);
break;
case kResourceTypeMac:
break;
}
const int16_t xpos = dx + pge->pos_x + 8; const int16_t xpos = dx + pge->pos_x + 8;
const int16_t ypos = dy + pge->pos_y + 2; const int16_t ypos = dy + pge->pos_y + 2;
if (pge->init_PGE->object_type == 11) { if (pge->init_PGE->object_type == 11) {
@ -984,15 +1053,76 @@ void Game::drawAnimBuffer(uint8_t stateNum, AnimBufferState *state) {
drawCharacter(state->dataPtr, state->x, state->y, state->h, state->w, pge->flags); drawCharacter(state->dataPtr, state->x, state->y, state->h, state->w, pge->flags);
} }
break; break;
case kResourceTypeMac:
drawPiege(state);
break;
} }
} else { } else {
drawObject(state->dataPtr, state->x, state->y, pge->flags); drawPiege(state);
} }
--state; --state;
} while (--numAnims != 0); } while (--numAnims != 0);
} }
} }
static void fixOffsetDecodeBuffer(DecodeBuffer *buf, const uint8_t *dataPtr) {
if (buf->xflip) {
buf->x += (int16_t)READ_BE_UINT16(dataPtr + 4) - READ_BE_UINT16(dataPtr) - 1;
} else {
buf->x -= (int16_t)READ_BE_UINT16(dataPtr + 4);
}
buf->y -= (int16_t)READ_BE_UINT16(dataPtr + 6);
}
void Game::drawPiege(AnimBufferState *state) {
LivePGE *pge = state->pge;
switch (_res._type) {
case kResourceTypeAmiga:
case kResourceTypeDOS:
drawObject(state->dataPtr, state->x, state->y, pge->flags);
break;
case kResourceTypeMac: {
DecodeBuffer buf;
memset(&buf, 0, sizeof(buf));
buf.xflip = (pge->flags & 2);
buf.ptr = _vid._frontLayer;
buf.w = buf.pitch = _vid._w;
buf.h = _vid._h;
buf.x = state->x * _vid._layerScale;
buf.y = state->y * _vid._layerScale;
buf.setPixel = _eraseBackground ? Video::MAC_drawBuffer : Video::MAC_drawBufferMask;
if (pge->flags & 8) {
const uint8_t *dataPtr = _res.MAC_getImageData(_res._spc, pge->anim_number);
if (dataPtr) {
fixOffsetDecodeBuffer(&buf, dataPtr);
_res.MAC_decodeImageData(_res._spc, pge->anim_number, &buf);
_vid.MAC_markBlockAsDirty(buf.x, buf.y, READ_BE_UINT16(dataPtr), READ_BE_UINT16(dataPtr + 2));
}
} else if (pge->index == 0) {
if (pge->anim_number == 0x386) {
return;
}
const int frame = _res.MAC_getPersoFrame(pge->anim_number);
const uint8_t *dataPtr = _res.MAC_getImageData(_res._perso, frame);
if (dataPtr) {
fixOffsetDecodeBuffer(&buf, dataPtr);
_res.MAC_decodeImageData(_res._perso, frame, &buf);
_vid.MAC_markBlockAsDirty(buf.x, buf.y, READ_BE_UINT16(dataPtr), READ_BE_UINT16(dataPtr + 2));
}
} else {
const int frame = _res.MAC_getMonsterFrame(pge->anim_number);
const uint8_t *dataPtr = _res.MAC_getImageData(_res._monster, frame);
if (dataPtr) {
fixOffsetDecodeBuffer(&buf, dataPtr);
_res.MAC_decodeImageData(_res._monster, frame, &buf);
_vid.MAC_markBlockAsDirty(buf.x, buf.y, READ_BE_UINT16(dataPtr), READ_BE_UINT16(dataPtr + 2));
}
}
}
break;
}
}
void Game::drawObject(const uint8_t *dataPtr, int16_t x, int16_t y, uint8_t flags) { void Game::drawObject(const uint8_t *dataPtr, int16_t x, int16_t y, uint8_t flags) {
debug(DBG_GAME, "Game::drawObject() dataPtr[]=0x%X dx=%d dy=%d", dataPtr[0], (int8_t)dataPtr[1], (int8_t)dataPtr[2]); debug(DBG_GAME, "Game::drawObject() dataPtr[]=0x%X dx=%d dy=%d", dataPtr[0], (int8_t)dataPtr[1], (int8_t)dataPtr[2]);
assert(dataPtr[0] < 0x4A); assert(dataPtr[0] < 0x4A);
@ -1018,6 +1148,9 @@ void Game::drawObject(const uint8_t *dataPtr, int16_t x, int16_t y, uint8_t flag
count = dataPtr[5]; count = dataPtr[5];
dataPtr += 6; dataPtr += 6;
break; break;
case kResourceTypeMac:
assert(0); // different graphics format
break;
} }
for (int i = 0; i < count; ++i) { for (int i = 0; i < count; ++i) {
drawObjectFrame(data, dataPtr, posx, posy, flags); drawObjectFrame(data, dataPtr, posx, posy, flags);
@ -1052,6 +1185,9 @@ void Game::drawObjectFrame(const uint8_t *bankDataPtr, const uint8_t *dataPtr, i
case kResourceTypeDOS: case kResourceTypeDOS:
_vid.PC_decodeSpc(src, sprite_w, sprite_h, _res._scratchBuffer); _vid.PC_decodeSpc(src, sprite_w, sprite_h, _res._scratchBuffer);
break; break;
case kResourceTypeMac:
assert(0); // different graphics format
break;
} }
src = _res._scratchBuffer; src = _res._scratchBuffer;
@ -1283,24 +1419,42 @@ int Game::loadMonsterSprites(LivePGE *pge) {
_curMonsterFrame = mList[0]; _curMonsterFrame = mList[0];
if (_curMonsterNum != mList[1]) { if (_curMonsterNum != mList[1]) {
_curMonsterNum = mList[1]; _curMonsterNum = mList[1];
if (_res.isAmiga()) { switch (_res._type) {
_res.load(_monsterNames[1][_curMonsterNum], Resource::OT_SPM); case kResourceTypeAmiga: {
static const uint8_t tab[4] = { 0, 8, 0, 8 }; _res.load(_monsterNames[1][_curMonsterNum], Resource::OT_SPM);
const int offset = _vid._mapPalSlot3 * 16 + tab[_curMonsterNum]; static const uint8_t tab[4] = { 0, 8, 0, 8 };
for (int i = 0; i < 8; ++i) { const int offset = _vid._mapPalSlot3 * 16 + tab[_curMonsterNum];
_vid.setPaletteColorBE(0x50 + i, offset + i); for (int i = 0; i < 8; ++i) {
_vid.setPaletteColorBE(0x50 + i, offset + i);
}
} }
} else { break;
const char *name = _monsterNames[0][_curMonsterNum]; case kResourceTypeDOS: {
_res.load(name, Resource::OT_SPRM); const char *name = _monsterNames[0][_curMonsterNum];
_res.load_SPR_OFF(name, _res._sprm); _res.load(name, Resource::OT_SPRM);
_vid.setPaletteSlotLE(5, _monsterPals[_curMonsterNum]); _res.load_SPR_OFF(name, _res._sprm);
_vid.setPaletteSlotLE(5, _monsterPals[_curMonsterNum]);
}
break;
case kResourceTypeMac: {
Color palette[256];
_res.MAC_loadMonsterData(_monsterNames[0][_curMonsterNum], palette);
static const int kMonsterPalette = 5;
for (int i = 0; i < 16; ++i) {
const int color = kMonsterPalette * 16 + i;
_stub->setPaletteEntry(color, &palette[color]);
}
}
break;
} }
} }
return 0xFFFF; return 0xFFFF;
} }
bool Game::hasLevelMap(int level, int room) const { bool Game::hasLevelMap(int level, int room) const {
if (_res._type == kResourceTypeMac) {
return _res.MAC_hasLevelMap(level, room);
}
if (_res._map) { if (_res._map) {
return READ_LE_UINT32(_res._map + room * 6) != 0; return READ_LE_UINT32(_res._map + room * 6) != 0;
} else if (_res._lev) { } else if (_res._lev) {
@ -1347,6 +1501,29 @@ void Game::loadLevelMap() {
} }
_vid.PC_setLevelPalettes(); _vid.PC_setLevelPalettes();
break; break;
case kResourceTypeMac:
{
DecodeBuffer buf;
memset(&buf, 0, sizeof(buf));
buf.ptr = _vid._frontLayer;
buf.w = buf.pitch = _vid._w;
buf.h = _vid._h;
buf.setPixel = Video::MAC_drawBuffer;
_res.MAC_loadLevelRoom(_currentLevel, _currentRoom, &buf);
memcpy(_vid._backLayer, _vid._frontLayer, _vid._layerSize);
Color roomPalette[256];
_res.MAC_setupRoomClut(_currentLevel, _currentRoom, roomPalette);
for (int j = 0; j < 16; ++j) {
if (j == 5 || j == 7 || j == 14 || j == 15) {
continue;
}
for (int i = 0; i < 16; ++i) {
const int color = j * 16 + i;
_stub->setPaletteEntry(color, &roomPalette[color]);
}
}
}
break;
} }
} }
@ -1425,6 +1602,10 @@ void Game::loadLevelData() {
_res.load(lvl->name2, Resource::OT_ANI); _res.load(lvl->name2, Resource::OT_ANI);
_res.load(lvl->name2, Resource::OT_TBN); _res.load(lvl->name2, Resource::OT_TBN);
break; break;
case kResourceTypeMac:
_res.MAC_unloadLevelData();
_res.MAC_loadLevelData(_currentLevel);
break;
} }
_cut._id = lvl->cutscene_id; _cut._id = lvl->cutscene_id;
@ -1509,8 +1690,34 @@ void Game::drawIcon(uint8_t iconNum, int16_t x, int16_t y, uint8_t colMask) {
case kResourceTypeDOS: case kResourceTypeDOS:
_vid.PC_decodeIcn(_res._icn, iconNum, buf); _vid.PC_decodeIcn(_res._icn, iconNum, buf);
break; break;
case kResourceTypeMac:
switch (iconNum) {
case 76: // cursor
iconNum = 32;
break;
case 77: // up
iconNum = 33;
break;
case 78: // down
iconNum = 34;
break;
}
{
const uint8_t *dataPtr = _res.MAC_getImageData(_res._icn, iconNum);
DecodeBuffer buf;
memset(&buf, 0, sizeof(buf));
buf.ptr = _vid._frontLayer;
buf.w = buf.pitch = _vid._w;
buf.h = _vid._h;
buf.x = x * _vid._layerScale;
buf.y = y * _vid._layerScale;
buf.setPixel = Video::MAC_drawBuffer;
_res.MAC_decodeImageData(_res._icn, iconNum, &buf);
_vid.MAC_markBlockAsDirty(buf.x, buf.y, READ_BE_UINT16(dataPtr), READ_BE_UINT16(dataPtr + 2));
}
return;
} }
_vid.drawSpriteSub1(buf, _vid._frontLayer + x + y * 256, 16, 16, 16, colMask << 4); _vid.drawSpriteSub1(buf, _vid._frontLayer + x + y * _vid._w, 16, 16, 16, colMask << 4);
_vid.markBlockAsDirty(x, y, 16, 16); _vid.markBlockAsDirty(x, y, 16, 16);
} }
@ -1569,35 +1776,43 @@ void Game::handleInventory() {
int current_line = 0; int current_line = 0;
bool display_score = false; bool display_score = false;
while (!_stub->_pi.backspace && !_stub->_pi.quit) { while (!_stub->_pi.backspace && !_stub->_pi.quit) {
// draw inventory background
int icon_h = 5;
int icon_y = 140;
int icon_num = 31;
static const int icon_spr_w = 16; static const int icon_spr_w = 16;
static const int icon_spr_h = 16; static const int icon_spr_h = 16;
do { switch (_res._type) {
int icon_x = 56; case kResourceTypeAmiga:
int icon_w = 9; case kResourceTypeDOS: {
do { // draw inventory background
drawIcon(icon_num, icon_x, icon_y, 0xF); int icon_h = 5;
++icon_num; int icon_y = 140;
icon_x += icon_spr_w; int icon_num = 31;
} while (--icon_w); do {
icon_y += icon_spr_h; int icon_x = 56;
} while (--icon_h); int icon_w = 9;
if (_res._type == kResourceTypeAmiga) { do {
// draw outline rectangle drawIcon(icon_num, icon_x, icon_y, 0xF);
static const uint8_t outline_color = 0xE7; ++icon_num;
uint8_t *p = _vid._frontLayer + 140 * Video::GAMESCREEN_W + 56; icon_x += icon_spr_w;
memset(p + 1, outline_color, 9 * icon_spr_w - 2); } while (--icon_w);
p += Video::GAMESCREEN_W; icon_y += icon_spr_h;
for (int y = 1; y < 5 * icon_spr_h - 1; ++y) { } while (--icon_h);
p[0] = p[9 * icon_spr_w - 1] = outline_color;
p += Video::GAMESCREEN_W;
} }
memset(p + 1, outline_color, 9 * icon_spr_w - 2); if (_res._type == kResourceTypeAmiga) {
// draw outline rectangle
static const uint8_t outline_color = 0xE7;
uint8_t *p = _vid._frontLayer + 140 * Video::GAMESCREEN_W + 56;
memset(p + 1, outline_color, 9 * icon_spr_w - 2);
p += Video::GAMESCREEN_W;
for (int y = 1; y < 5 * icon_spr_h - 1; ++y) {
p[0] = p[9 * icon_spr_w - 1] = outline_color;
p += Video::GAMESCREEN_W;
}
memset(p + 1, outline_color, 9 * icon_spr_w - 2);
}
break;
case kResourceTypeMac:
drawIcon(31, 56, 140, 0xF);
break;
} }
if (!display_score) { if (!display_score) {
int icon_x_pos = 72; int icon_x_pos = 72;
for (int i = 0; i < 4; ++i) { for (int i = 0; i < 4; ++i) {
@ -1610,12 +1825,12 @@ void Game::handleInventory() {
drawIcon(76, icon_x_pos, 157, 0xA); drawIcon(76, icon_x_pos, 157, 0xA);
selected_pge = items[item_it].live_pge; selected_pge = items[item_it].live_pge;
uint8_t txt_num = items[item_it].init_pge->text_num; uint8_t txt_num = items[item_it].init_pge->text_num;
const char *str = (const char *)_res.getTextString(_currentLevel, txt_num); const uint8_t *str = _res.getTextString(_currentLevel, txt_num);
_vid.drawString(str, (256 - strlen(str) * 8) / 2, 189, 0xED); drawString(str, Video::GAMESCREEN_W, 189, 0xED, true);
if (items[item_it].init_pge->init_flags & 4) { if (items[item_it].init_pge->init_flags & 4) {
char buf[10]; char buf[10];
snprintf(buf, sizeof(buf), "%d", selected_pge->life); snprintf(buf, sizeof(buf), "%d", selected_pge->life);
_vid.drawString(buf, (256 - strlen(buf) * 8) / 2, 197, 0xED); _vid.drawString(buf, (Video::GAMESCREEN_W - strlen(buf) * Video::CHAR_W) / 2, 197, 0xED);
} }
} }
icon_x_pos += 32; icon_x_pos += 32;
@ -1629,9 +1844,9 @@ void Game::handleInventory() {
} else { } else {
char buf[50]; char buf[50];
snprintf(buf, sizeof(buf), "SCORE %08u", _score); snprintf(buf, sizeof(buf), "SCORE %08u", _score);
_vid.drawString(buf, (114 - strlen(buf) * 8) / 2 + 72, 158, 0xE5); _vid.drawString(buf, (114 - strlen(buf) * Video::CHAR_W) / 2 + 72, 158, 0xE5);
snprintf(buf, sizeof(buf), "%s:%s", _res.getMenuString(LocaleData::LI_06_LEVEL), _res.getMenuString(LocaleData::LI_13_EASY + _skillLevel)); snprintf(buf, sizeof(buf), "%s:%s", _res.getMenuString(LocaleData::LI_06_LEVEL), _res.getMenuString(LocaleData::LI_13_EASY + _skillLevel));
_vid.drawString(buf, (114 - strlen(buf) * 8) / 2 + 72, 166, 0xE5); _vid.drawString(buf, (114 - strlen(buf) * Video::CHAR_W) / 2 + 72, 166, 0xE5);
} }
_vid.updateScreen(); _vid.updateScreen();

2
game.h
View File

@ -109,10 +109,12 @@ struct Game {
void printSaveStateCompleted(); void printSaveStateCompleted();
void drawLevelTexts(); void drawLevelTexts();
void drawStoryTexts(); void drawStoryTexts();
void drawString(const uint8_t *p, int x, int y, uint8_t color, bool hcenter = false);
void prepareAnims(); void prepareAnims();
void prepareAnimsHelper(LivePGE *pge, int16_t dx, int16_t dy); void prepareAnimsHelper(LivePGE *pge, int16_t dx, int16_t dy);
void drawAnims(); void drawAnims();
void drawAnimBuffer(uint8_t stateNum, AnimBufferState *state); void drawAnimBuffer(uint8_t stateNum, AnimBufferState *state);
void drawPiege(AnimBufferState *state);
void drawObject(const uint8_t *dataPtr, int16_t x, int16_t y, uint8_t flags); void drawObject(const uint8_t *dataPtr, int16_t x, int16_t y, uint8_t flags);
void drawObjectFrame(const uint8_t *bankDataPtr, const uint8_t *dataPtr, int16_t x, int16_t y, uint8_t flags); void drawObjectFrame(const uint8_t *bankDataPtr, const uint8_t *dataPtr, int16_t x, int16_t y, uint8_t flags);
void decodeCharacterFrame(const uint8_t *dataPtr, uint8_t *dstPtr); void decodeCharacterFrame(const uint8_t *dataPtr, uint8_t *dstPtr);

View File

@ -7,6 +7,11 @@
#include "graphics.h" #include "graphics.h"
#include "util.h" #include "util.h"
void Graphics::setLayer(uint8_t *layer, int pitch) {
_layer = layer;
_layerPitch = pitch;
}
void Graphics::setClippingRect(int16_t rx, int16_t ry, int16_t rw, int16_t rh) { void Graphics::setClippingRect(int16_t rx, int16_t ry, int16_t rw, int16_t rh) {
debug(DBG_VIDEO, "Graphics::setClippingRect(%d, %d, %d, %d)", rx, ry, rw, rh); debug(DBG_VIDEO, "Graphics::setClippingRect(%d, %d, %d, %d)", rx, ry, rw, rh);
_crx = rx; _crx = rx;
@ -18,7 +23,7 @@ void Graphics::setClippingRect(int16_t rx, int16_t ry, int16_t rw, int16_t rh) {
void Graphics::drawPoint(uint8_t color, const Point *pt) { void Graphics::drawPoint(uint8_t color, const Point *pt) {
debug(DBG_VIDEO, "Graphics::drawPoint() col=0x%X x=%d, y=%d", color, pt->x, pt->y); debug(DBG_VIDEO, "Graphics::drawPoint() col=0x%X x=%d, y=%d", color, pt->x, pt->y);
if (pt->x >= 0 && pt->x < _crw && pt->y >= 0 && pt->y < _crh) { if (pt->x >= 0 && pt->x < _crw && pt->y >= 0 && pt->y < _crh) {
*(_layer + (pt->y + _cry) * 256 + pt->x + _crx) = color; *(_layer + (pt->y + _cry) * _layerPitch + pt->x + _crx) = color;
} }
} }
@ -201,7 +206,7 @@ void Graphics::drawEllipse(uint8_t color, bool hasAlpha, const Point *pt, int16_
void Graphics::fillArea(uint8_t color, bool hasAlpha) { void Graphics::fillArea(uint8_t color, bool hasAlpha) {
debug(DBG_VIDEO, "Graphics::fillArea()"); debug(DBG_VIDEO, "Graphics::fillArea()");
int16_t *pts = _areaPoints; int16_t *pts = _areaPoints;
uint8_t *dst = _layer + (_cry + *pts++) * 256 + _crx; uint8_t *dst = _layer + (_cry + *pts++) * _layerPitch + _crx;
int16_t x1 = *pts++; int16_t x1 = *pts++;
if (x1 >= 0) { if (x1 >= 0) {
if (hasAlpha && color > 0xC7) { if (hasAlpha && color > 0xC7) {
@ -213,7 +218,7 @@ void Graphics::fillArea(uint8_t color, bool hasAlpha) {
*(dst + x1 + i) |= color & 8; // XXX 0x88 *(dst + x1 + i) |= color & 8; // XXX 0x88
} }
} }
dst += 256; dst += _layerPitch;
x1 = *pts++; x1 = *pts++;
} while (x1 >= 0); } while (x1 >= 0);
} else { } else {
@ -223,7 +228,7 @@ void Graphics::fillArea(uint8_t color, bool hasAlpha) {
int len = x2 - x1 + 1; int len = x2 - x1 + 1;
memset(dst + x1, color, len); memset(dst + x1, color, len);
} }
dst += 256; dst += _layerPitch;
x1 = *pts++; x1 = *pts++;
} while (x1 >= 0); } while (x1 >= 0);
} }
@ -341,8 +346,8 @@ void Graphics::drawPolygon(uint8_t color, bool hasAlpha, const Point *pts, uint8
debug(DBG_VIDEO, "Graphics::drawPolygon()"); debug(DBG_VIDEO, "Graphics::drawPolygon()");
assert(numPts * 4 < 0x100); assert(numPts * 4 < 0x100);
int16_t *apts1 = &_areaPoints[0x100]; int16_t *apts1 = &_areaPoints[AREA_POINTS_SIZE];
int16_t *apts2 = &_areaPoints[0x100 + numPts * 2]; int16_t *apts2 = &_areaPoints[AREA_POINTS_SIZE + numPts * 2];
int16_t xmin, xmax, ymin, ymax; int16_t xmin, xmax, ymin, ymax;
xmin = xmax = pts[0].x; xmin = xmax = pts[0].x;

View File

@ -10,10 +10,13 @@
#include "intern.h" #include "intern.h"
struct Graphics { struct Graphics {
static const int AREA_POINTS_SIZE = 256 * 2; // maxY * sizeof(Point) / sizeof(int16_t)
uint8_t *_layer; uint8_t *_layer;
int16_t _areaPoints[0x200]; int _layerPitch;
int16_t _areaPoints[AREA_POINTS_SIZE * 2];
int16_t _crx, _cry, _crw, _crh; int16_t _crx, _cry, _crw, _crh;
void setLayer(uint8_t *layer, int pitch);
void setClippingRect(int16_t vx, int16_t vy, int16_t vw, int16_t vh); void setClippingRect(int16_t vx, int16_t vy, int16_t vw, int16_t vh);
void drawPoint(uint8_t color, const Point *pt); void drawPoint(uint8_t color, const Point *pt);
void drawLine(uint8_t color, const Point *pt1, const Point *pt2); void drawLine(uint8_t color, const Point *pt1, const Point *pt2);

View File

@ -80,7 +80,8 @@ enum Language {
enum ResourceType { enum ResourceType {
kResourceTypeAmiga, kResourceTypeAmiga,
kResourceTypeDOS kResourceTypeDOS,
kResourceTypeMac,
}; };
enum Skill { enum Skill {
@ -91,13 +92,15 @@ enum Skill {
struct Options { struct Options {
bool bypass_protection; bool bypass_protection;
bool play_disabled_cutscenes;
bool enable_password_menu; bool enable_password_menu;
bool fade_out_palette; bool fade_out_palette;
bool use_tiledata; bool use_tiledata;
bool use_text_cutscenes; bool use_text_cutscenes;
bool use_seq_cutscenes; bool use_seq_cutscenes;
bool play_stone_cutscene; bool play_asc_cutscene;
bool play_caillou_cutscene;
bool play_metro_cutscene;
bool play_serrure_cutscene;
}; };
struct Color { struct Color {

View File

@ -38,6 +38,7 @@ static int detectVersion(FileSystem *fs) {
{ "LEVEL1.BNQ", kResourceTypeDOS, "DOS (Demo)" }, { "LEVEL1.BNQ", kResourceTypeDOS, "DOS (Demo)" },
{ "LEVEL1.LEV", kResourceTypeAmiga, "Amiga" }, { "LEVEL1.LEV", kResourceTypeAmiga, "Amiga" },
{ "DEMO.LEV", kResourceTypeAmiga, "Amiga (Demo)" }, { "DEMO.LEV", kResourceTypeAmiga, "Amiga (Demo)" },
{ "FLASHBACK.BIN", kResourceTypeMac, "Macintosh" },
{ 0, -1, 0 } { 0, -1, 0 }
}; };
for (int i = 0; table[i].filename; ++i) { for (int i = 0; table[i].filename; ++i) {
@ -80,24 +81,29 @@ const char *g_caption = "REminiscence";
static void initOptions() { static void initOptions() {
// defaults // defaults
g_options.bypass_protection = true; g_options.bypass_protection = true;
g_options.play_disabled_cutscenes = false;
g_options.enable_password_menu = false; g_options.enable_password_menu = false;
g_options.fade_out_palette = true; g_options.fade_out_palette = true;
g_options.use_text_cutscenes = false; g_options.use_text_cutscenes = false;
g_options.use_seq_cutscenes = true; g_options.use_seq_cutscenes = true;
g_options.play_asc_cutscene = false;
g_options.play_caillou_cutscene = false;
g_options.play_metro_cutscene = false;
g_options.play_serrure_cutscene = false;
// read configuration file // read configuration file
struct { struct {
const char *name; const char *name;
bool *value; bool *value;
} opts[] = { } opts[] = {
{ "bypass_protection", &g_options.bypass_protection }, { "bypass_protection", &g_options.bypass_protection },
{ "play_disabled_cutscenes", &g_options.play_disabled_cutscenes },
{ "enable_password_menu", &g_options.enable_password_menu }, { "enable_password_menu", &g_options.enable_password_menu },
{ "fade_out_palette", &g_options.fade_out_palette }, { "fade_out_palette", &g_options.fade_out_palette },
{ "use_tiledata", &g_options.use_tiledata }, { "use_tiledata", &g_options.use_tiledata },
{ "use_text_cutscenes", &g_options.use_text_cutscenes }, { "use_text_cutscenes", &g_options.use_text_cutscenes },
{ "use_seq_cutscenes", &g_options.use_seq_cutscenes }, { "use_seq_cutscenes", &g_options.use_seq_cutscenes },
{ "play_stone_cutscene", &g_options.play_stone_cutscene }, { "play_asc_cutscene", &g_options.play_asc_cutscene },
{ "play_caillou_cutscene", &g_options.play_caillou_cutscene },
{ "play_metro_cutscene", &g_options.play_metro_cutscene },
{ "play_serrure_cutscene", &g_options.play_serrure_cutscene },
{ 0, 0 } { 0, 0 }
}; };
static const char *filename = "rs.cfg"; static const char *filename = "rs.cfg";
@ -251,7 +257,7 @@ int main(int argc, char *argv[]) {
const Language language = (forcedLanguage == -1) ? detectLanguage(&fs) : (Language)forcedLanguage; const Language language = (forcedLanguage == -1) ? detectLanguage(&fs) : (Language)forcedLanguage;
SystemStub *stub = SystemStub_SDL_create(); SystemStub *stub = SystemStub_SDL_create();
Game *g = new Game(stub, &fs, savePath, levelNum, (ResourceType)version, language); Game *g = new Game(stub, &fs, savePath, levelNum, (ResourceType)version, language);
stub->init(g_caption, Video::GAMESCREEN_W, Video::GAMESCREEN_H, fullscreen, &scalerParameters); stub->init(g_caption, g->_vid._w, g->_vid._h, fullscreen, &scalerParameters);
g->run(); g->run();
delete g; delete g;
stub->destroy(); stub->destroy();

View File

@ -64,11 +64,24 @@ void Menu::drawString(const char *str, int16_t y, int16_t x, uint8_t color) {
void Menu::drawString2(const char *str, int16_t y, int16_t x) { void Menu::drawString2(const char *str, int16_t y, int16_t x) {
debug(DBG_MENU, "Menu::drawString2()"); debug(DBG_MENU, "Menu::drawString2()");
int i = 0; int w = Video::CHAR_W;
for (; str[i]; ++i) { int h = Video::CHAR_H;
_vid->PC_drawChar((uint8_t)str[i], y, x + i, true); int len = 0;
switch (_res->_type) {
case kResourceTypeDOS:
for (; str[len]; ++len) {
_vid->PC_drawChar((uint8_t)str[len], y, x + len, true);
}
break;
case kResourceTypeMac:
for (; str[len]; ++len) {
_vid->MAC_drawStringChar(_vid->_frontLayer, _vid->_w, Video::CHAR_W * (x + len), Video::CHAR_H * y, _res->_fnt, _vid->_charFrontColor, (uint8_t)str[len]);
}
w *= _vid->_layerScale;
h *= _vid->_layerScale;
break;
} }
_vid->markBlockAsDirty(x * 8, y * 8, i * 8, 8); _vid->markBlockAsDirty(x * w, y * h, len * w, h);
} }
void Menu::loadPicture(const char *prefix) { void Menu::loadPicture(const char *prefix) {
@ -216,7 +229,7 @@ bool Menu::handlePasswordScreen() {
password[len] = '\0'; password[len] = '\0';
for (int level = 0; level < 8; ++level) { for (int level = 0; level < 8; ++level) {
for (int skill = 0; skill < 3; ++skill) { for (int skill = 0; skill < 3; ++skill) {
if (strcmp(_passwords[level][skill], password) == 0) { if (strcmp(getLevelPassword(level, skill), password) == 0) {
_level = level; _level = level;
_skill = skill; _skill = skill;
return true; return true;
@ -328,17 +341,19 @@ void Menu::handleTitleScreen() {
menuItems[menuItemsCount].str = LocaleData::LI_07_START; menuItems[menuItemsCount].str = LocaleData::LI_07_START;
menuItems[menuItemsCount].opt = MENU_OPTION_ITEM_START; menuItems[menuItemsCount].opt = MENU_OPTION_ITEM_START;
++menuItemsCount; ++menuItemsCount;
if (g_options.enable_password_menu) { if (!_res->_isDemo) {
menuItems[menuItemsCount].str = LocaleData::LI_08_SKILL; if (g_options.enable_password_menu) {
menuItems[menuItemsCount].opt = MENU_OPTION_ITEM_SKILL; menuItems[menuItemsCount].str = LocaleData::LI_08_SKILL;
++menuItemsCount; menuItems[menuItemsCount].opt = MENU_OPTION_ITEM_SKILL;
menuItems[menuItemsCount].str = LocaleData::LI_09_PASSWORD; ++menuItemsCount;
menuItems[menuItemsCount].opt = MENU_OPTION_ITEM_PASSWORD; menuItems[menuItemsCount].str = LocaleData::LI_09_PASSWORD;
++menuItemsCount; menuItems[menuItemsCount].opt = MENU_OPTION_ITEM_PASSWORD;
} else { ++menuItemsCount;
menuItems[menuItemsCount].str = LocaleData::LI_06_LEVEL; } else {
menuItems[menuItemsCount].opt = MENU_OPTION_ITEM_LEVEL; menuItems[menuItemsCount].str = LocaleData::LI_06_LEVEL;
++menuItemsCount; menuItems[menuItemsCount].opt = MENU_OPTION_ITEM_LEVEL;
++menuItemsCount;
}
} }
menuItems[menuItemsCount].str = LocaleData::LI_10_INFO; menuItems[menuItemsCount].str = LocaleData::LI_10_INFO;
menuItems[menuItemsCount].opt = MENU_OPTION_ITEM_INFO; menuItems[menuItemsCount].opt = MENU_OPTION_ITEM_INFO;
@ -435,3 +450,23 @@ void Menu::handleTitleScreen() {
} }
} }
} }
const char *Menu::getLevelPassword(int level, int skill) const {
switch (_res->_type) {
case kResourceTypeAmiga:
if (level < 7) {
if (_res->_lang == LANG_FR) {
return _passwordsFrAmiga[skill * 7 + level];
} else {
return _passwordsEnAmiga[skill * 7 + level];
}
}
break;
case kResourceTypeMac:
return _passwordsMac[skill * 8 + level];
case kResourceTypeDOS:
// default
break;
}
return _passwordsDOS[skill * 8 + level];
}

5
menu.h
View File

@ -40,9 +40,10 @@ struct Menu {
int opt; int opt;
}; };
static const char *_passwords[8][3]; static const char *_passwordsDOS[];
static const char *_passwordsFrAmiga[]; static const char *_passwordsFrAmiga[];
static const char *_passwordsEnAmiga[]; static const char *_passwordsEnAmiga[];
static const char *_passwordsMac[];
Resource *_res; Resource *_res;
SystemStub *_stub; SystemStub *_stub;
@ -72,6 +73,8 @@ struct Menu {
bool handlePasswordScreen(); bool handlePasswordScreen();
bool handleLevelScreen(); bool handleLevelScreen();
void handleTitleScreen(); void handleTitleScreen();
const char *getLevelPassword(int level, int skill) const;
}; };
#endif // MENU_H__ #endif // MENU_H__

View File

@ -4,6 +4,7 @@
* Copyright (C) 2005-2018 Gregory Montoir (cyx@users.sourceforge.net) * Copyright (C) 2005-2018 Gregory Montoir (cyx@users.sourceforge.net)
*/ */
#include "decode_mac.h"
#include "file.h" #include "file.h"
#include "fs.h" #include "fs.h"
#include "resource.h" #include "resource.h"
@ -17,6 +18,7 @@ Resource::Resource(FileSystem *fs, ResourceType ver, Language lang) {
_lang = lang; _lang = lang;
_isDemo = false; _isDemo = false;
_aba = 0; _aba = 0;
_mac = 0;
_readUint16 = (_type == kResourceTypeDOS) ? READ_LE_UINT16 : READ_BE_UINT16; _readUint16 = (_type == kResourceTypeDOS) ? READ_LE_UINT16 : READ_BE_UINT16;
_readUint32 = (_type == kResourceTypeDOS) ? READ_LE_UINT32 : READ_BE_UINT32; _readUint32 = (_type == kResourceTypeDOS) ? READ_LE_UINT32 : READ_BE_UINT32;
_scratchBuffer = (uint8_t *)malloc(320 * 224 + 1024); _scratchBuffer = (uint8_t *)malloc(320 * 224 + 1024);
@ -63,16 +65,30 @@ void Resource::init() {
_aba = new ResourceAba(_fs); _aba = new ResourceAba(_fs);
_aba->readEntries(); _aba->readEntries();
_isDemo = true; _isDemo = true;
} else if (!_fs->exists("LEVEL1.MAP")) { // fbdemofr (no cutscenes) }
if (!fileExists("LEVEL1.MAP")) { // fbdemofr (no cutscenes)
_isDemo = true; _isDemo = true;
} }
break; break;
case kResourceTypeMac:
_mac = new ResourceMac(ResourceMac::FILENAME, _fs);
_mac->loadMap();
break;
} }
} }
void Resource::fini() { void Resource::fini() {
} }
bool Resource::fileExists(const char *filename) {
if (_fs->exists(filename)) {
return true;
} else if (_aba) {
return _aba->findEntry(filename) != 0;
}
return false;
}
void Resource::clearLevelRes() { void Resource::clearLevelRes() {
free(_tbn); _tbn = 0; free(_tbn); _tbn = 0;
free(_mbk); _mbk = 0; free(_mbk); _mbk = 0;
@ -96,6 +112,12 @@ void Resource::load_DEM(const char *filename) {
if (_dem) { if (_dem) {
f.read(_dem, _demLen); f.read(_dem, _demLen);
} }
} else if (_aba) {
uint32_t size;
_dem = _aba->loadEntry(filename, &size);
if (_dem) {
_demLen = size;
}
} }
} }
@ -386,6 +408,9 @@ void Resource::load_CINE() {
error("Cannot load '%s'", _entryName); error("Cannot load '%s'", _entryName);
} }
break; break;
case kResourceTypeMac:
MAC_loadCutsceneText();
break;
} }
} }
@ -893,6 +918,10 @@ void Resource::decodeOBJ(const uint8_t *tmp, int size) {
uint32_t offsets[256]; uint32_t offsets[256];
int tmpOffset = 0; int tmpOffset = 0;
_numObjectNodes = 230; _numObjectNodes = 230;
if (_type == kResourceTypeMac) {
_numObjectNodes = _readUint16(tmp);
tmpOffset += 2;
}
for (int i = 0; i < _numObjectNodes; ++i) { for (int i = 0; i < _numObjectNodes; ++i) {
offsets[i] = _readUint32(tmp + tmpOffset); tmpOffset += 4; offsets[i] = _readUint32(tmp + tmpOffset); tmpOffset += 4;
} }
@ -1292,6 +1321,9 @@ int Resource::getBankDataSize(uint16_t num) {
len &= 0x7FFF; len &= 0x7FFF;
} }
break; break;
case kResourceTypeMac:
assert(0); // different graphics format
break;
} }
return len * 32; return len * 32;
} }
@ -1337,3 +1369,281 @@ uint8_t *Resource::loadBankData(uint16_t num) {
return bankData; return bankData;
} }
uint8_t *Resource::decodeResourceMacData(const char *name, bool decompressLzss) {
_resourceMacDataSize = 0;
uint8_t *data = 0;
const ResourceMacEntry *entry = _mac->findEntry(name);
if (entry) {
_mac->_f.seek(_mac->_dataOffset + entry->dataOffset);
_resourceMacDataSize = _mac->_f.readUint32BE();
if (decompressLzss) {
data = decodeLzss(_mac->_f, _resourceMacDataSize);
} else {
data = (uint8_t *)malloc(_resourceMacDataSize);
if (data) {
_mac->_f.read(data, _resourceMacDataSize);
}
}
} else {
error("Resource '%s' not found", name);
}
return data;
}
void Resource::MAC_decodeImageData(const uint8_t *ptr, int i, DecodeBuffer *dst) {
const uint8_t *basePtr = ptr;
const uint16_t sig = READ_BE_UINT16(ptr); ptr += 2;
assert(sig == 0xC211 || sig == 0xC103);
const int count = READ_BE_UINT16(ptr); ptr += 2;
assert(i < count);
ptr += 4;
const uint32_t offset = READ_BE_UINT32(ptr + i * 4);
if (offset != 0) {
ptr = basePtr + offset;
const int w = READ_BE_UINT16(ptr); ptr += 2;
const int h = READ_BE_UINT16(ptr); ptr += 2;
switch (sig) {
case 0xC211:
decodeC211(ptr + 4, w, h, dst);
break;
case 0xC103:
decodeC103(ptr, w, h, dst);
break;
}
}
}
void Resource::MAC_decodeDataCLUT(const uint8_t *ptr) {
ptr += 6; // seed+flags
_clutSize = READ_BE_UINT16(ptr); ptr += 2;
assert(_clutSize < kClutSize);
for (int i = 0; i < _clutSize; ++i) {
const int index = READ_BE_UINT16(ptr); ptr += 2;
assert(i == index);
// ignore lower bits
_clut[i].r = ptr[0]; ptr += 2;
_clut[i].g = ptr[0]; ptr += 2;
_clut[i].b = ptr[0]; ptr += 2;
}
}
void Resource::MAC_loadClutData() {
uint8_t *ptr = decodeResourceMacData("Flashback colors", false);
if (ptr) {
MAC_decodeDataCLUT(ptr);
free(ptr);
}
}
void Resource::MAC_loadFontData() {
_fnt = decodeResourceMacData("Font", true);
}
void Resource::MAC_loadIconData() {
_icn = decodeResourceMacData("Icons", true);
}
void Resource::MAC_loadPersoData() {
_perso = decodeResourceMacData("Person", true);
}
void Resource::MAC_loadMonsterData(const char *name, Color *clut) {
static const struct {
const char *id;
const char *name;
int index;
} data[] = {
{ "junky", "Junky", 0x32 },
{ "mercenai", "Mercenary", 0x34 },
{ "replican", "Replicant", 0x35 },
{ "glue", "Alien", 0x36 },
{ 0, 0, 0 }
};
free(_monster);
_monster = 0;
for (int i = 0; data[i].id; ++i) {
if (strcmp(data[i].id, name) == 0) {
_monster = decodeResourceMacData(data[i].name, true);
assert(_monster);
MAC_copyClut16(clut, 5, data[i].index);
break;
}
}
}
void Resource::MAC_loadTitleImage(int i, DecodeBuffer *buf) {
char name[64];
snprintf(name, sizeof(name), "Title %d", i);
uint8_t *ptr = decodeResourceMacData(name, (i == 6));
if (ptr) {
MAC_decodeImageData(ptr, 0, buf);
free(ptr);
}
}
void Resource::MAC_unloadLevelData() {
free(_ani);
_ani = 0;
ObjectNode *prevNode = 0;
for (int i = 0; i < _numObjectNodes; ++i) {
if (prevNode != _objectNodesMap[i]) {
free(_objectNodesMap[i]);
prevNode = _objectNodesMap[i];
}
}
_numObjectNodes = 0;
free(_tbn);
_tbn = 0;
free(_str);
_str = 0;
}
static const int _macLevelColorOffsets[] = { 24, 28, 36, 40, 44 }; // red palette: 32
static const char *_macLevelNumbers[] = { "1", "2", "3", "4-1", "4-2", "5-1", "5-2" };
void Resource::MAC_loadLevelData(int level) {
char name[64];
// .PGE
snprintf(name, sizeof(name), "Level %s objects", _macLevelNumbers[level]);
uint8_t *ptr = decodeResourceMacData(name, true);
if (ptr) {
decodePGE(ptr, _resourceMacDataSize);
free(ptr);
} else {
error("Failed to load '%s'", name);
}
// .ANI
snprintf(name, sizeof(name), "Level %s sequences", _macLevelNumbers[level]);
_ani = decodeResourceMacData(name, true);
if (_ani) {
assert(READ_BE_UINT16(_ani) == 0x48D);
} else {
error("Failed to load '%s'", name);
}
// .OBJ
snprintf(name, sizeof(name), "Level %s conditions", _macLevelNumbers[level]);
ptr = decodeResourceMacData(name, true);
if (ptr) {
assert(READ_BE_UINT16(ptr) == 0xE6);
decodeOBJ(ptr, _resourceMacDataSize);
free(ptr);
} else {
error("Failed to load '%s'", name);
}
// .CT
snprintf(name, sizeof(name), "Level %c map", _macLevelNumbers[level][0]);
ptr = decodeResourceMacData(name, true);
if (ptr) {
assert(_resourceMacDataSize == 0x1D00);
memcpy(_ctData, ptr, _resourceMacDataSize);
free(ptr);
} else {
error("Failed to load '%s'", name);
}
// .SPC
snprintf(name, sizeof(name), "Objects %c", _macLevelNumbers[level][0]);
_spc = decodeResourceMacData(name, true);
// .TBN
snprintf(name, sizeof(name), "Level %s names", _macLevelNumbers[level]);
_tbn = decodeResourceMacData(name, false);
_str = decodeResourceMacData("Flashback strings", false);
}
void Resource::MAC_loadLevelRoom(int level, int i, DecodeBuffer *dst) {
char name[64];
snprintf(name, sizeof(name), "Level %c Room %d", _macLevelNumbers[level][0], i);
uint8_t *ptr = decodeResourceMacData(name, true);
if (ptr) {
MAC_decodeImageData(ptr, 0, dst);
free(ptr);
}
}
void Resource::MAC_clearClut16(Color *clut, uint8_t dest) {
memset(&clut[dest * 16], 0, 16 * sizeof(Color));
}
void Resource::MAC_copyClut16(Color *clut, uint8_t dest, uint8_t src) {
memcpy(&clut[dest * 16], &_clut[src * 16], 16 * sizeof(Color));
}
void Resource::MAC_setupRoomClut(int level, int room, Color *clut) {
const int num = _macLevelNumbers[level][0] - '1';
int offset = _macLevelColorOffsets[num];
if (level == 1) {
switch (room) {
case 27:
case 28:
case 29:
case 30:
case 35:
case 36:
case 37:
case 45:
case 46:
offset = 32;
break;
}
}
for (int i = 0; i < 4; ++i) {
MAC_copyClut16(clut, i, offset + i);
MAC_copyClut16(clut, 8 + i, offset + i);
}
MAC_copyClut16(clut, 4, 0x30);
// 5 is monster palette
MAC_clearClut16(clut, 6);
MAC_copyClut16(clut, 0xA, _macLevelColorOffsets[0] + 2);
MAC_copyClut16(clut, 0xC, 0x37);
MAC_copyClut16(clut, 0xD, 0x38);
}
const uint8_t *Resource::MAC_getImageData(const uint8_t *ptr, int i) {
const uint8_t *basePtr = ptr;
const uint16_t sig = READ_BE_UINT16(ptr); ptr += 2;
assert(sig == 0xC211);
const int count = READ_BE_UINT16(ptr); ptr += 2;
assert(i < count);
ptr += 4;
const uint32_t offset = READ_BE_UINT32(ptr + i * 4);
return (offset != 0) ? basePtr + offset : 0;
}
bool Resource::MAC_hasLevelMap(int level, int room) const {
char name[64];
snprintf(name, sizeof(name), "Level %c Room %d", _macLevelNumbers[level][0], room);
return _mac->findEntry(name) != 0;
}
static void stringLowerCase(char *p) {
while (*p) {
if (*p >= 'A' && *p <= 'Z') {
*p += 'a' - 'A';
}
++p;
}
}
void Resource::MAC_unloadCutscene() {
free(_cmd);
_cmd = 0;
free(_pol);
_pol = 0;
}
void Resource::MAC_loadCutscene(const char *cutscene) {
char name[32];
free(_cmd);
snprintf(name, sizeof(name), "%s movie", cutscene);
stringLowerCase(name);
_cmd = decodeResourceMacData(name, true);
free(_pol);
snprintf(name, sizeof(name), "%s polygons", cutscene);
stringLowerCase(name);
_pol = decodeResourceMacData(name, true);
}
void Resource::MAC_loadCutsceneText() {
_cine_txt = decodeResourceMacData("Movie strings", false);
_cine_off = 0; // offsets are prepended to _cine_txt
}

View File

@ -9,7 +9,9 @@
#include "intern.h" #include "intern.h"
#include "resource_aba.h" #include "resource_aba.h"
#include "resource_mac.h"
struct DecodeBuffer;
struct File; struct File;
struct FileSystem; struct FileSystem;
@ -108,6 +110,10 @@ struct Resource {
NUM_SPRITES = 1287 NUM_SPRITES = 1287
}; };
enum {
kClutSize = 1024
};
static const uint16_t _voicesOffsetsTable[]; static const uint16_t _voicesOffsetsTable[];
static const uint32_t _spmOffsetsTable[]; static const uint32_t _spmOffsetsTable[];
static const char *_splNames[]; static const char *_splNames[];
@ -117,6 +123,7 @@ struct Resource {
Language _lang; Language _lang;
bool _isDemo; bool _isDemo;
ResourceAba *_aba; ResourceAba *_aba;
ResourceMac *_mac;
uint16_t (*_readUint16)(const void *); uint16_t (*_readUint16)(const void *);
uint32_t (*_readUint32)(const void *); uint32_t (*_readUint32)(const void *);
bool _hasSeqData; bool _hasSeqData;
@ -162,6 +169,12 @@ struct Resource {
int _bankBuffersCount; int _bankBuffersCount;
uint8_t *_dem; uint8_t *_dem;
int _demLen; int _demLen;
uint32_t _resourceMacDataSize;
int _clutSize;
Color _clut[kClutSize];
uint8_t *_perso;
uint8_t *_monster;
uint8_t *_str;
Resource(FileSystem *fs, ResourceType type, Language lang); Resource(FileSystem *fs, ResourceType type, Language lang);
~Resource(); ~Resource();
@ -172,6 +185,8 @@ struct Resource {
bool isDOS() const { return _type == kResourceTypeDOS; } bool isDOS() const { return _type == kResourceTypeDOS; }
bool isAmiga() const { return _type == kResourceTypeAmiga; } bool isAmiga() const { return _type == kResourceTypeAmiga; }
bool fileExists(const char *filename);
void clearLevelRes(); void clearLevelRes();
void load_DEM(const char *filename); void load_DEM(const char *filename);
void load_FIB(const char *fileName); void load_FIB(const char *fileName);
@ -213,10 +228,16 @@ struct Resource {
void load_BNQ(File *pf); void load_BNQ(File *pf);
void load_SPM(File *f); void load_SPM(File *f);
const uint8_t *getAniData(int num) const { const uint8_t *getAniData(int num) const {
if (_type == kResourceTypeMac) {
const int count = READ_BE_UINT16(_ani);
assert(num < count);
const int offset = READ_BE_UINT16(_ani + 2 + num * 2);
return _ani + offset;
}
const int offset = _readUint16(_ani + 2 + num * 2); const int offset = _readUint16(_ani + 2 + num * 2);
return _ani + 2 + offset; return _ani + 2 + offset;
} }
const uint8_t *getTextString(int level, int num) { const uint8_t *getTextString(int level, int num) const {
if (_lang == LANG_JP) { if (_lang == LANG_JP) {
const uint8_t *p = 0; const uint8_t *p = 0;
switch (level) { switch (level) {
@ -246,29 +267,103 @@ struct Resource {
} }
return p + READ_LE_UINT16(p + num * 2); return p + READ_LE_UINT16(p + num * 2);
} }
if (_type == kResourceTypeMac) {
const int count = READ_BE_UINT16(_tbn);
assert(num < count);
const int offset = READ_BE_UINT16(_tbn + 2 + num * 2);
return _tbn + offset;
}
return _tbn + _readUint16(_tbn + num * 2); return _tbn + _readUint16(_tbn + num * 2);
} }
const uint8_t *getGameString(int num) { const uint8_t *getGameString(int num) const {
if (_type == kResourceTypeMac) {
const int count = READ_BE_UINT16(_str);
assert(num < count);
const int offset = READ_BE_UINT16(_str + 2 + num * 2);
return _str + offset;
}
return _stringsTable + READ_LE_UINT16(_stringsTable + num * 2); return _stringsTable + READ_LE_UINT16(_stringsTable + num * 2);
} }
const uint8_t *getCineString(int num) { const uint8_t *getCineString(int num) const {
if (_lang == LANG_JP) { if (_lang == LANG_JP) {
const int offset = READ_BE_UINT16(LocaleData::_cineBinJP + num * 2); const int offset = READ_BE_UINT16(LocaleData::_cineBinJP + num * 2);
return LocaleData::_cineTxtJP + offset; return LocaleData::_cineTxtJP + offset;
} }
if (_type == kResourceTypeMac) {
const int count = READ_BE_UINT16(_cine_txt);
assert(num < count);
const int offset = READ_BE_UINT16(_cine_txt + 2 + num * 2);
return _cine_txt + offset;
}
if (_cine_off) { if (_cine_off) {
const int offset = READ_BE_UINT16(_cine_off + num * 2); const int offset = READ_BE_UINT16(_cine_off + num * 2);
return _cine_txt + offset; return _cine_txt + offset;
} }
return (num >= 0 && num < NUM_CUTSCENE_TEXTS) ? _cineStrings[num] : 0; return (num >= 0 && num < NUM_CUTSCENE_TEXTS) ? _cineStrings[num] : 0;
} }
const char *getMenuString(int num) { const char *getMenuString(int num) const {
return (num >= 0 && num < LocaleData::LI_NUM) ? _textsTable[num] : ""; return (num >= 0 && num < LocaleData::LI_NUM) ? _textsTable[num] : "";
} }
void clearBankData(); void clearBankData();
int getBankDataSize(uint16_t num); int getBankDataSize(uint16_t num);
uint8_t *findBankData(uint16_t num); uint8_t *findBankData(uint16_t num);
uint8_t *loadBankData(uint16_t num); uint8_t *loadBankData(uint16_t num);
uint8_t *decodeResourceMacData(const char *name, bool decompressLzss);
void MAC_decodeImageData(const uint8_t *ptr, int i, DecodeBuffer *dst);
void MAC_decodeDataCLUT(const uint8_t *ptr);
void MAC_loadClutData();
void MAC_loadFontData();
void MAC_loadIconData();
void MAC_loadPersoData();
void MAC_loadMonsterData(const char *name, Color *clut);
void MAC_loadTitleImage(int i, DecodeBuffer *buf);
void MAC_unloadLevelData();
void MAC_loadLevelData(int level);
void MAC_loadLevelRoom(int level, int i, DecodeBuffer *dst);
void MAC_clearClut16(Color *clut, uint8_t dest);
void MAC_copyClut16(Color *clut, uint8_t dest, uint8_t src);
void MAC_setupRoomClut(int level, int room, Color *clut);
const uint8_t *MAC_getImageData(const uint8_t *ptr, int i);
bool MAC_hasLevelMap(int level, int room) const;
void MAC_unloadCutscene();
void MAC_loadCutscene(const char *cutscene);
void MAC_loadCutsceneText();
int MAC_getPersoFrame(int anim) const {
static const int data[] = {
0x000, 0x22E,
0x28E, 0x2E9,
0x4E9, 0x506,
-1
};
int offset = 0;
for (int i = 0; data[i] != -1; i += 2) {
if (anim >= data[i] && anim <= data[i + 1]) {
return offset + anim - data[i];
}
const int count = data[i + 1] + 1 - data[i];
offset += count;
}
assert(0);
return 0;
}
int MAC_getMonsterFrame(int anim) const {
static const int data[] = {
0x22F, 0x28D, // junky - 94
0x2EA, 0x385, // mercenai - 156
0x387, 0x42F, // replican - 169
0x430, 0x4E8, // glue - 185
-1
};
for (int i = 0; data[i] != -1; i += 2) {
if (anim >= data[i] && anim <= data[i + 1]) {
return anim - data[i];
}
}
assert(0);
return 0;
}
}; };
#endif // RESOURCE_H__ #endif // RESOURCE_H__

14
rs.cfg
View File

@ -1,9 +1,6 @@
# display copy protection shapes # display copy protection shapes
bypass_protection=true bypass_protection=true
# enable playback of 'asc', 'metro' and 'serrure' cutscenes
play_disabled_cutscenes=false
# use original password level selection menu screen # use original password level selection menu screen
enable_password_menu=false enable_password_menu=false
@ -19,5 +16,14 @@ use_text_cutscenes=false
# enable playback of .SEQ cutscenes (use polygonal if false) # enable playback of .SEQ cutscenes (use polygonal if false)
use_seq_cutscenes=true use_seq_cutscenes=true
# enable playback of 'ASC' cutscene
play_asc_cutscene=true
# enable playback of 'CAILLOU-F.SET' cutscene # enable playback of 'CAILLOU-F.SET' cutscene
play_stone_cutscene=true play_caillou_cutscene=true
# enable playback of 'METRO' cutscene
play_metro_cutscene=false
# enable playback of 'SERRURE' cutscene
play_serrure_cutscene=true

View File

@ -3223,15 +3223,10 @@ const uint8_t Game::_protectionPal[] = {
0x08, 0x88, 0x09, 0x99, 0x0A, 0xAA, 0x0B, 0xBB, 0x0C, 0xCC, 0x0D, 0xDD, 0x0E, 0xEE, 0x0F, 0xFF 0x08, 0x88, 0x09, 0x99, 0x0A, 0xAA, 0x0B, 0xBB, 0x0C, 0xCC, 0x0D, 0xDD, 0x0E, 0xEE, 0x0F, 0xFF
}; };
const char *Menu::_passwords[8][3] = { const char *Menu::_passwordsDOS[] = {
{ "JAGUAR", "BANTHA", "TOHOLD" }, "JAGUAR", "COMBEL", "ANTIC", "NOLAN", "ARTHUR", "SHIRYU", "RENDER", "BELUGA", // easy
{ "COMBEL", "SHIVA", "PICOLO" }, "BANTHA", "SHIVA", "KASYYK", "SARLAC", "MAENOC", "SULUST", "NEPTUN", "BELUGA", // normal
{ "ANTIC", "KASYYK", "FUGU" }, "TOHOLD", "PICOLO", "FUGU", "CAPSUL", "ZZZAP", "MANIAC", "NO WAY", "BELUGA", // hard
{ "NOLAN", "SARLAC", "CAPSUL" },
{ "ARTHUR", "MAENOC", "ZZZAP" },
{ "SHIRYU", "SULUST", "MANIAC" },
{ "RENDER", "NEPTUN", "NO WAY" },
{ "BELUGA", "BELUGA", "BELUGA" }
}; };
const char *Menu::_passwordsFrAmiga[] = { const char *Menu::_passwordsFrAmiga[] = {
@ -3246,6 +3241,12 @@ const char *Menu::_passwordsEnAmiga[] = {
"MINE", "YOUR", "NEST", "LINE", "LISA", "MARY", "MICE", // hard "MINE", "YOUR", "NEST", "LINE", "LISA", "MARY", "MICE", // hard
}; };
const char *Menu::_passwordsMac[] = {
"QUENCH", "GHOST", "LEGEND", "SPHERE", "BULLET", "DISRUPT", "TRAUMA", "OPAQUE", // easy
"HICK", "FRGO", "JERK", "KOIK", "KIMO", "LEDUX", "MORDO", "OPAQUE", // normal
"CURIOUS", "IMPACT", "LETHAL", "PERSIST", "MORTAL", "VERDICT", "KNIGHT", "OPAQUE", // hard
};
const uint8_t Video::_conradPal1[] = { const uint8_t Video::_conradPal1[] = {
0x00, 0x00, 0xCC, 0x0C, 0x8F, 0x08, 0x7E, 0x07, 0x6C, 0x06, 0x5B, 0x05, 0x4A, 0x04, 0x63, 0x09, 0x00, 0x00, 0xCC, 0x0C, 0x8F, 0x08, 0x7E, 0x07, 0x6C, 0x06, 0x5B, 0x05, 0x4A, 0x04, 0x63, 0x09,
0x52, 0x07, 0x41, 0x06, 0x30, 0x06, 0x76, 0x0C, 0x14, 0x09, 0x25, 0x0B, 0x88, 0x08, 0xFF, 0x0F 0x52, 0x07, 0x41, 0x06, 0x30, 0x06, 0x76, 0x0C, 0x14, 0x09, 0x25, 0x0B, 0x88, 0x08, 0xFF, 0x0F

View File

@ -234,7 +234,6 @@ void SystemStub_SDL::updateScreen(int shakeOffset) {
r.x = 0; r.x = 0;
r.y = shakeOffset * _scaleFactor; r.y = shakeOffset * _scaleFactor;
SDL_GetRendererOutputSize(_renderer, &r.w, &r.h); SDL_GetRendererOutputSize(_renderer, &r.w, &r.h);
r.h -= r.y;
SDL_RenderCopy(_renderer, _texture, 0, &r); SDL_RenderCopy(_renderer, _texture, 0, &r);
} else { } else {
SDL_RenderCopy(_renderer, _texture, 0, 0); SDL_RenderCopy(_renderer, _texture, 0, 0);

139
video.cpp
View File

@ -4,6 +4,7 @@
* Copyright (C) 2005-2018 Gregory Montoir (cyx@users.sourceforge.net) * Copyright (C) 2005-2018 Gregory Montoir (cyx@users.sourceforge.net)
*/ */
#include "decode_mac.h"
#include "resource.h" #include "resource.h"
#include "systemstub.h" #include "systemstub.h"
#include "unpack.h" #include "unpack.h"
@ -12,8 +13,9 @@
Video::Video(Resource *res, SystemStub *stub) Video::Video(Resource *res, SystemStub *stub)
: _res(res), _stub(stub) { : _res(res), _stub(stub) {
_w = GAMESCREEN_W; _layerScale = (_res->_type == kResourceTypeMac) ? 2 : 1; // Macintosh version is 512x448
_h = GAMESCREEN_H; _w = GAMESCREEN_W * _layerScale;
_h = GAMESCREEN_H * _layerScale;
_layerSize = _w * _h; _layerSize = _w * _h;
_frontLayer = (uint8_t *)calloc(1, _layerSize); _frontLayer = (uint8_t *)calloc(1, _layerSize);
_backLayer = (uint8_t *)calloc(1,_layerSize); _backLayer = (uint8_t *)calloc(1,_layerSize);
@ -33,6 +35,9 @@ Video::Video(Resource *res, SystemStub *stub)
case kResourceTypeDOS: case kResourceTypeDOS:
_drawChar = &Video::PC_drawStringChar; _drawChar = &Video::PC_drawStringChar;
break; break;
case kResourceTypeMac:
_drawChar = &Video::MAC_drawStringChar;
break;
} }
} }
@ -63,7 +68,7 @@ void Video::updateScreen() {
debug(DBG_VIDEO, "Video::updateScreen()"); debug(DBG_VIDEO, "Video::updateScreen()");
// _fullRefresh = true; // _fullRefresh = true;
if (_fullRefresh) { if (_fullRefresh) {
_stub->copyRect(0, 0, _w, _h, _frontLayer, 256); _stub->copyRect(0, 0, _w, _h, _frontLayer, _w);
_stub->updateScreen(_shakeOffset); _stub->updateScreen(_shakeOffset);
_fullRefresh = false; _fullRefresh = false;
} else { } else {
@ -78,14 +83,14 @@ void Video::updateScreen() {
++nh; ++nh;
} else if (nh != 0) { } else if (nh != 0) {
int16_t x = (i - nh) * SCREENBLOCK_W; int16_t x = (i - nh) * SCREENBLOCK_W;
_stub->copyRect(x, j * SCREENBLOCK_H, nh * SCREENBLOCK_W, SCREENBLOCK_H, _frontLayer, 256); _stub->copyRect(x, j * SCREENBLOCK_H, nh * SCREENBLOCK_W, SCREENBLOCK_H, _frontLayer, _w);
nh = 0; nh = 0;
++count; ++count;
} }
} }
if (nh != 0) { if (nh != 0) {
int16_t x = (i - nh) * SCREENBLOCK_W; int16_t x = (i - nh) * SCREENBLOCK_W;
_stub->copyRect(x, j * SCREENBLOCK_H, nh * SCREENBLOCK_W, SCREENBLOCK_H, _frontLayer, 256); _stub->copyRect(x, j * SCREENBLOCK_H, nh * SCREENBLOCK_W, SCREENBLOCK_H, _frontLayer, _w);
++count; ++count;
} }
p += _w / SCREENBLOCK_W; p += _w / SCREENBLOCK_W;
@ -800,11 +805,11 @@ void Video::drawSpriteSub6(const uint8_t *src, uint8_t *dst, int pitch, int h, i
void Video::PC_drawChar(uint8_t c, int16_t y, int16_t x, bool forceDefaultFont) { void Video::PC_drawChar(uint8_t c, int16_t y, int16_t x, bool forceDefaultFont) {
debug(DBG_VIDEO, "Video::PC_drawChar(0x%X, %d, %d)", c, y, x); debug(DBG_VIDEO, "Video::PC_drawChar(0x%X, %d, %d)", c, y, x);
const uint8_t *fnt = (_res->_lang == LANG_JP && !forceDefaultFont) ? _font8Jp : _res->_fnt; const uint8_t *fnt = (_res->_lang == LANG_JP && !forceDefaultFont) ? _font8Jp : _res->_fnt;
y *= 8; y *= CHAR_W;
x *= 8; x *= CHAR_H;
const uint8_t *src = fnt + (c - 32) * 32; const uint8_t *src = fnt + (c - 32) * 32;
uint8_t *dst = _frontLayer + x + 256 * y; uint8_t *dst = _frontLayer + x + _w * y;
for (int h = 0; h < 8; ++h) { for (int h = 0; h < CHAR_H; ++h) {
for (int i = 0; i < 4; ++i, ++src) { for (int i = 0; i < 4; ++i, ++src) {
const uint8_t c1 = *src >> 4; const uint8_t c1 = *src >> 4;
if (c1 != 0) { if (c1 != 0) {
@ -829,11 +834,12 @@ void Video::PC_drawChar(uint8_t c, int16_t y, int16_t x, bool forceDefaultFont)
} }
++dst; ++dst;
} }
dst += 256 - 8; dst += _w - CHAR_W;
} }
} }
void Video::AMIGA_drawStringChar(uint8_t *dst, int pitch, const uint8_t *src, uint8_t color, uint8_t chr) { void Video::AMIGA_drawStringChar(uint8_t *dst, int pitch, int x, int y, const uint8_t *src, uint8_t color, uint8_t chr) {
dst += y * pitch + x;
assert(chr >= 32); assert(chr >= 32);
AMIGA_decodeIcn(src, chr - 32, _res->_scratchBuffer); AMIGA_decodeIcn(src, chr - 32, _res->_scratchBuffer);
src = _res->_scratchBuffer; src = _res->_scratchBuffer;
@ -848,7 +854,8 @@ void Video::AMIGA_drawStringChar(uint8_t *dst, int pitch, const uint8_t *src, ui
} }
} }
void Video::PC_drawStringChar(uint8_t *dst, int pitch, const uint8_t *src, uint8_t color, uint8_t chr) { void Video::PC_drawStringChar(uint8_t *dst, int pitch, int x, int y, const uint8_t *src, uint8_t color, uint8_t chr) {
dst += y * pitch + x;
assert(chr >= 32); assert(chr >= 32);
src += (chr - 32) * 8 * 4; src += (chr - 32) * 8 * 4;
for (int y = 0; y < 8; ++y) { for (int y = 0; y < 8; ++y) {
@ -869,25 +876,45 @@ void Video::PC_drawStringChar(uint8_t *dst, int pitch, const uint8_t *src, uint8
} }
} }
void Video::MAC_drawStringChar(uint8_t *dst, int pitch, int x, int y, const uint8_t *src, uint8_t color, uint8_t chr) {
DecodeBuffer buf;
memset(&buf, 0, sizeof(buf));
buf.ptr = _frontLayer;
buf.w = buf.pitch = _w;
buf.h = _h;
buf.x = x * _layerScale;
buf.y = y * _layerScale;
buf.setPixel = Video::MAC_drawBufferFont;
_charFrontColor = color;
buf.dataPtr = this;
assert(chr >= 32);
_res->MAC_decodeImageData(_res->_fnt, chr - 32, &buf);
}
const char *Video::drawString(const char *str, int16_t x, int16_t y, uint8_t col) { const char *Video::drawString(const char *str, int16_t x, int16_t y, uint8_t col) {
debug(DBG_VIDEO, "Video::drawString('%s', %d, %d, 0x%X)", str, x, y, col); debug(DBG_VIDEO, "Video::drawString('%s', %d, %d, 0x%X)", str, x, y, col);
const uint8_t *fnt = (_res->_lang == LANG_JP) ? _font8Jp : _res->_fnt; const uint8_t *fnt = (_res->_lang == LANG_JP) ? _font8Jp : _res->_fnt;
drawCharFunc dcf = _drawChar;
int len = 0; int len = 0;
uint8_t *dst = _frontLayer + y * 256 + x;
while (1) { while (1) {
const uint8_t c = *str++; const uint8_t c = *str++;
if (c == 0 || c == 0xB || c == 0xA) { if (c == 0 || c == 0xB || c == 0xA) {
break; break;
} }
(this->*dcf)(dst, 256, fnt, col, c); (this->*_drawChar)(_frontLayer, _w, x + len * CHAR_W, y, fnt, col, c);
dst += CHAR_W;
++len; ++len;
} }
markBlockAsDirty(x, y, len * 8, 8); markBlockAsDirty(x, y, len * CHAR_W, CHAR_H);
return str - 1; return str - 1;
} }
void Video::drawStringLen(const char *str, int len, int x, int y, uint8_t color) {
const uint8_t *fnt = (_res->_lang == LANG_JP) ? _font8Jp : _res->_fnt;
for (int i = 0; i < len; ++i) {
(this->*_drawChar)(_frontLayer, _w, x + i * CHAR_W, y, fnt, color, str[i]);
}
markBlockAsDirty(x, y, len * CHAR_W, CHAR_H);
}
Color Video::AMIGA_convertColor(const uint16_t color, bool bgr) { // 4bits to 8bits Color Video::AMIGA_convertColor(const uint16_t color, bool bgr) { // 4bits to 8bits
int r = (color & 0xF00) >> 8; int r = (color & 0xF00) >> 8;
int g = (color & 0xF0) >> 4; int g = (color & 0xF0) >> 4;
@ -901,3 +928,81 @@ Color Video::AMIGA_convertColor(const uint16_t color, bool bgr) { // 4bits to 8b
c.b = (b << 4) | b; c.b = (b << 4) | b;
return c; return c;
} }
void Video::MAC_decodeMap(int level, int room) {
}
void Video::MAC_markBlockAsDirty(int x, int y, int w, int h) {
if (x < 0) {
w += x;
x = 0;
}
if (x + w > _w) {
w = _w - x;
}
if (w <= 0) {
return;
}
if (y < 0) {
h += y;
y = 0;
}
if (y + h > _h) {
h = _h - y;
}
if (h <= 0) {
return;
}
markBlockAsDirty(x, y, w, h);
}
void Video::MAC_drawBuffer(DecodeBuffer *buf, int src_x, int src_y, int src_w, int src_h, uint8_t color) {
const int y = buf->y + src_y;
if (y >= 0 && y < buf->h) {
const int x = buf->xflip ? (buf->x + (src_w - 1 - src_x)) : (buf->x + src_x);
if (x >= 0 && x < buf->w) {
const int offset = y * buf->pitch + x;
buf->ptr[offset] = color;
}
}
}
void Video::MAC_drawBufferMask(DecodeBuffer *buf, int src_x, int src_y, int src_w, int src_h, uint8_t color) {
const int y = buf->y + src_y;
if (y >= 0 && y < buf->h) {
const int x = buf->xflip ? (buf->x + (src_w - 1 - src_x)) : (buf->x + src_x);
if (x >= 0 && x < buf->w) {
const int offset = y * buf->pitch + x;
if ((buf->ptr[offset] & 0x80) == 0) {
buf->ptr[offset] = color;
}
}
}
}
void Video::MAC_drawBufferFont(DecodeBuffer *buf, int src_x, int src_y, int src_w, int src_h, uint8_t color) {
const int y = buf->y + src_y;
if (y >= 0 && y < buf->h) {
const int x = buf->x + src_x;
if (x >= 0 && x < buf->w) {
const Video *vid = (Video *)buf->dataPtr;
const int offset = y * buf->pitch + x;
switch (color) {
case 0xC0:
buf->ptr[offset] = vid->_charShadowColor;
break;
case 0xC1:
buf->ptr[offset] = vid->_charFrontColor;
break;
}
}
}
}
void Video::MAC_fillRect(int x, int y, int w, int h, uint8_t color) {
uint8_t *p = _frontLayer + y * _layerScale * _w + x * _layerScale;
for (int j = 0; j < h * _layerScale; ++j) {
memset(p, color, w * _layerScale);
p += _w;
}
}

15
video.h
View File

@ -13,7 +13,7 @@ struct Resource;
struct SystemStub; struct SystemStub;
struct Video { struct Video {
typedef void (Video::*drawCharFunc)(uint8_t *, int, const uint8_t *, uint8_t, uint8_t); typedef void (Video::*drawCharFunc)(uint8_t *, int, int, int, const uint8_t *, uint8_t, uint8_t);
enum { enum {
GAMESCREEN_W = 256, GAMESCREEN_W = 256,
@ -35,6 +35,7 @@ struct Video {
int _w, _h; int _w, _h;
int _layerSize; int _layerSize;
int _layerScale; // 1 for Amiga/PC, 2 for Macintosh
uint8_t *_frontLayer; uint8_t *_frontLayer;
uint8_t *_backLayer; uint8_t *_backLayer;
uint8_t *_tempLayer; uint8_t *_tempLayer;
@ -79,10 +80,18 @@ struct Video {
void drawSpriteSub5(const uint8_t *src, uint8_t *dst, int pitch, int h, int w, uint8_t colMask); void drawSpriteSub5(const uint8_t *src, uint8_t *dst, int pitch, int h, int w, uint8_t colMask);
void drawSpriteSub6(const uint8_t *src, uint8_t *dst, int pitch, int h, int w, uint8_t colMask); void drawSpriteSub6(const uint8_t *src, uint8_t *dst, int pitch, int h, int w, uint8_t colMask);
void PC_drawChar(uint8_t c, int16_t y, int16_t x, bool forceDefaultFont = false); void PC_drawChar(uint8_t c, int16_t y, int16_t x, bool forceDefaultFont = false);
void PC_drawStringChar(uint8_t *dst, int pitch, const uint8_t *src, uint8_t color, uint8_t chr); void PC_drawStringChar(uint8_t *dst, int pitch, int x, int y, const uint8_t *src, uint8_t color, uint8_t chr);
void AMIGA_drawStringChar(uint8_t *dst, int pitch, const uint8_t *src, uint8_t color, uint8_t chr); void AMIGA_drawStringChar(uint8_t *dst, int pitch, int x, int y, const uint8_t *src, uint8_t color, uint8_t chr);
void MAC_drawStringChar(uint8_t *dst, int pitch, int x, int y, const uint8_t *src, uint8_t color, uint8_t chr);
const char *drawString(const char *str, int16_t x, int16_t y, uint8_t col); const char *drawString(const char *str, int16_t x, int16_t y, uint8_t col);
void drawStringLen(const char *str, int len, int x, int y, uint8_t color);
static Color AMIGA_convertColor(const uint16_t color, bool bgr = false); static Color AMIGA_convertColor(const uint16_t color, bool bgr = false);
void MAC_decodeMap(int level, int room);
void MAC_markBlockAsDirty(int x, int y, int w, int h);
static void MAC_drawBuffer(DecodeBuffer *buf, int src_x, int src_y, int src_w, int src_h, uint8_t color);
static void MAC_drawBufferMask(DecodeBuffer *buf, int src_x, int src_y, int src_w, int src_h, uint8_t color);
static void MAC_drawBufferFont(DecodeBuffer *buf, int src_x, int src_y, int src_w, int src_h, uint8_t color);
void MAC_fillRect(int x, int y, int w, int h, uint8_t color);
}; };
#endif // VIDEO_H__ #endif // VIDEO_H__