Import 0.4.0
This commit is contained in:
parent
be1f81f217
commit
bfef522ada
6
Makefile
6
Makefile
|
@ -9,9 +9,9 @@ ZLIB_LIBS := -lz
|
|||
|
||||
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 \
|
||||
mixer.cpp mod_player.cpp ogg_player.cpp piege.cpp resource.cpp resource_aba.cpp \
|
||||
scaler.cpp screenshot.cpp seq_player.cpp \
|
||||
SRCS = collision.cpp cutscene.cpp decode_mac.cpp dynlib.cpp file.cpp fs.cpp game.cpp graphics.cpp main.cpp \
|
||||
menu.cpp mixer.cpp mod_player.cpp ogg_player.cpp piege.cpp resource.cpp resource_aba.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
|
||||
|
||||
OBJS = $(SRCS:.cpp=.o)
|
||||
|
|
11
README.txt
11
README.txt
|
@ -1,6 +1,6 @@
|
|||
|
||||
REminiscence README
|
||||
Release version: 0.3.7
|
||||
Release version: 0.4.0
|
||||
-------------------------------------------------------------------------------
|
||||
|
||||
|
||||
|
@ -22,11 +22,15 @@ libraries are required.
|
|||
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,
|
||||
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'
|
||||
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
|
||||
[2] http://en.wikipedia.org/wiki/Flashback:_The_Quest_for_Identity
|
||||
[3] http://ramal.free.fr/fb_en.htm
|
||||
[4] https://www.exotica.org.uk/wiki/Flashback
|
||||
|
|
108
cutscene.cpp
108
cutscene.cpp
|
@ -11,12 +11,30 @@
|
|||
#include "util.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)
|
||||
: _res(res), _stub(stub), _vid(vid) {
|
||||
_patchedOffsetsTable = 0;
|
||||
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() {
|
||||
if (_stub->_pi.quit) {
|
||||
return;
|
||||
|
@ -57,7 +75,7 @@ void Cutscene::setPalette() {
|
|||
sync();
|
||||
updatePalette();
|
||||
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);
|
||||
}
|
||||
|
||||
|
@ -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) {
|
||||
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;
|
||||
const uint8_t *fnt = (_res->_lang == LANG_JP) ? Video::_font8Jp : _res->_fnt;
|
||||
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 xx = x;
|
||||
if (textJustify != kTextJustifyLeft) {
|
||||
xx += ((last_sep - *sep++) / 2) * 8;
|
||||
xx += ((last_sep - *sep++) / 2) * Video::CHAR_W;
|
||||
}
|
||||
for (; *p != 0xA && *p; ++p) {
|
||||
if (isNewLineChar(*p, _res)) {
|
||||
yy += 8;
|
||||
yy += Video::CHAR_H;
|
||||
xx = x;
|
||||
if (textJustify != kTextJustifyLeft) {
|
||||
xx += ((last_sep - *sep++) / 2) * 8;
|
||||
xx += ((last_sep - *sep++) / 2) * Video::CHAR_W;
|
||||
}
|
||||
} else if (*p == 0x20) {
|
||||
xx += 8;
|
||||
xx += Video::CHAR_W;
|
||||
} else if (*p == 0x9) {
|
||||
// ignore tab
|
||||
} else {
|
||||
uint8_t *dst = page + 256 * yy + xx;
|
||||
(_vid->*dcf)(dst, 256, fnt, color, *p);
|
||||
xx += 8;
|
||||
(_vid->*dcf)(page, _vid->_w, xx, yy, fnt, color, *p);
|
||||
xx += Video::CHAR_W;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -282,7 +303,7 @@ void Cutscene::op_waitForSync() {
|
|||
|
||||
void Cutscene::drawShape(const uint8_t *data, int16_t x, int16_t y) {
|
||||
debug(DBG_CUT, "Cutscene::drawShape()");
|
||||
_gfx._layer = _page1;
|
||||
_gfx.setLayer(_page1, _vid->_w);
|
||||
uint8_t numVertices = *data++;
|
||||
if (numVertices & 0x80) {
|
||||
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;
|
||||
uint16_t rx = 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);
|
||||
} else if (numVertices == 0) {
|
||||
Point pt;
|
||||
pt.x = READ_BE_UINT16(data) + x; data += 2;
|
||||
pt.y = READ_BE_UINT16(data) + y; data += 2;
|
||||
scalePoints(&pt, 1, _vid->_layerScale);
|
||||
_gfx.drawPoint(_primitiveColor, &pt);
|
||||
} else {
|
||||
Point *pt = _vertices;
|
||||
|
@ -319,6 +342,7 @@ void Cutscene::drawShape(const uint8_t *data, int16_t x, int16_t y) {
|
|||
++pt;
|
||||
}
|
||||
}
|
||||
scalePoints(_vertices, numVertices, _vid->_layerScale);
|
||||
_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
|
||||
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;
|
||||
setPalette();
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
memset(_pageC + 179 * 256, 0xC0, 45 * 256);
|
||||
memset(_page1 + 179 * 256, 0xC0, 45 * 256);
|
||||
memset(_page0 + 179 * 256, 0xC0, 45 * 256);
|
||||
memset(_pageC + 179 * _vid->_w, 0xC0, 45 * _vid->_w);
|
||||
memset(_page1 + 179 * _vid->_w, 0xC0, 45 * _vid->_w);
|
||||
memset(_page0 + 179 * _vid->_w, 0xC0, 45 * _vid->_w);
|
||||
if (strId != 0xFFFF) {
|
||||
const uint8_t *str = _res->getCineString(strId);
|
||||
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) {
|
||||
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++;
|
||||
if (numVertices & 0x80) {
|
||||
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;
|
||||
int16_t rx = _vertices[0].x - _vertices[2].x;
|
||||
int16_t ry = _vertices[0].y - _vertices[1].y;
|
||||
scalePoints(&po, 1, _vid->_layerScale);
|
||||
_gfx.drawEllipse(_primitiveColor, _hasAlphaColor, &po, rx, ry);
|
||||
} else if (numVertices == 0) {
|
||||
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_x16 = _shape_cur_x16;
|
||||
_shape_prev_y16 = _shape_cur_y16;
|
||||
scalePoints(&pt, 1, _vid->_layerScale);
|
||||
_gfx.drawPoint(_primitiveColor, &pt);
|
||||
} else {
|
||||
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_x16 = _shape_cur_x16;
|
||||
_shape_prev_y16 = _shape_cur_y16;
|
||||
scalePoints(_vertices, numVertices, _vid->_layerScale);
|
||||
_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) {
|
||||
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++;
|
||||
if (numVertices & 0x80) {
|
||||
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;
|
||||
int16_t rx = _vertices[0].x - _vertices[2].x;
|
||||
int16_t ry = _vertices[0].y - _vertices[1].y;
|
||||
scalePoints(&po, 1, _vid->_layerScale);
|
||||
_gfx.drawEllipse(_primitiveColor, _hasAlphaColor, &po, rx, ry);
|
||||
} else if (numVertices == 0) {
|
||||
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_x16 = _shape_cur_x16;
|
||||
_shape_prev_y16 = _shape_cur_y16;
|
||||
scalePoints(&pt, 1, _vid->_layerScale);
|
||||
_gfx.drawPoint(_primitiveColor, &pt);
|
||||
} else {
|
||||
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_x16 = _shape_cur_x16;
|
||||
_shape_prev_y16 = _shape_cur_y16;
|
||||
scalePoints(_vertices, numVertices + 1, _vid->_layerScale);
|
||||
_gfx.drawPolygon(_primitiveColor, _hasAlphaColor, _vertices, numVertices + 1);
|
||||
}
|
||||
}
|
||||
|
@ -905,10 +935,10 @@ void Cutscene::op_handleKeys() {
|
|||
}
|
||||
_varKey = 0;
|
||||
--n;
|
||||
_cmdPtr = _res->_cmd;
|
||||
_cmdPtr = getCommandData();
|
||||
n = READ_BE_UINT16(_cmdPtr + n * 2 + 2);
|
||||
}
|
||||
_cmdPtr = _cmdPtrBak = _res->_cmd + n + _startOffset;
|
||||
_cmdPtr = _cmdPtrBak = getCommandData() + n + _startOffset;
|
||||
}
|
||||
|
||||
uint8_t Cutscene::fetchNextCmdByte() {
|
||||
|
@ -932,14 +962,14 @@ void Cutscene::mainLoop(uint16_t offset) {
|
|||
}
|
||||
_newPal = false;
|
||||
_hasAlphaColor = false;
|
||||
uint8_t *p = _res->_cmd;
|
||||
const uint8_t *p = getCommandData();
|
||||
if (offset != 0) {
|
||||
offset = READ_BE_UINT16(p + (offset + 1) * 2);
|
||||
}
|
||||
_startOffset = (READ_BE_UINT16(p) + 1) * 2;
|
||||
_varKey = 0;
|
||||
_cmdPtr = _cmdPtrBak = p + _startOffset + offset;
|
||||
_polPtr = _res->_pol;
|
||||
_polPtr = getPolygonData();
|
||||
debug(DBG_CUT, "_startOffset = %d offset = %d", _startOffset, offset);
|
||||
|
||||
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_POL);
|
||||
break;
|
||||
case kResourceTypeMac:
|
||||
_res->MAC_loadCutscene(name);
|
||||
break;
|
||||
}
|
||||
_res->load_CINE();
|
||||
return _res->_cmd && _res->_pol;
|
||||
|
@ -1006,6 +1039,9 @@ void Cutscene::unload() {
|
|||
_res->unload(Resource::OT_CMD);
|
||||
_res->unload(Resource::OT_POL);
|
||||
break;
|
||||
case kResourceTypeMac:
|
||||
_res->MAC_unloadCutscene();
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1019,7 +1055,15 @@ void Cutscene::prepare() {
|
|||
_stub->_pi.shift = false;
|
||||
_interrupted = 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() {
|
||||
|
@ -1066,7 +1110,7 @@ void Cutscene::playText(const char *str) {
|
|||
const int y = (128 - lines * 8) / 2;
|
||||
memset(_page1, 0xC0, _vid->_layerSize);
|
||||
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);
|
||||
|
||||
while (!_stub->_pi.quit) {
|
||||
|
@ -1087,19 +1131,25 @@ void Cutscene::play() {
|
|||
prepare();
|
||||
uint16_t cutName = _offsetsTable[_id * 2 + 0];
|
||||
uint16_t cutOff = _offsetsTable[_id * 2 + 1];
|
||||
if (cutName == 0xFFFF && g_options.play_disabled_cutscenes) {
|
||||
if (cutName == 0xFFFF) {
|
||||
switch (_id) {
|
||||
case 19:
|
||||
cutName = 31; // SERRURE
|
||||
if (g_options.play_serrure_cutscene) {
|
||||
cutName = 31; // SERRURE
|
||||
}
|
||||
break;
|
||||
case 22:
|
||||
case 23:
|
||||
case 24:
|
||||
cutName = 12; // ASC
|
||||
if (g_options.play_asc_cutscene) {
|
||||
cutName = 12; // ASC
|
||||
}
|
||||
break;
|
||||
case 30:
|
||||
case 31:
|
||||
cutName = 14; // METRO
|
||||
if (g_options.play_metro_cutscene) {
|
||||
cutName = 14; // METRO
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
@ -1125,7 +1175,7 @@ void Cutscene::play() {
|
|||
mainLoop(cutOff);
|
||||
unload();
|
||||
}
|
||||
} else if (_id == 8 && g_options.play_stone_cutscene) {
|
||||
} else if (_id == 8 && g_options.play_caillou_cutscene) {
|
||||
playSet(_caillouSetData, 0x5E4);
|
||||
}
|
||||
_vid->fullRefresh();
|
||||
|
@ -1158,12 +1208,14 @@ void Cutscene::drawSetShape(const uint8_t *p, uint16_t offset, int x, int y, uin
|
|||
Point pt;
|
||||
pt.x = x + ix;
|
||||
pt.y = y + iy;
|
||||
scalePoints(&pt, 1, _vid->_layerScale);
|
||||
_gfx.drawEllipse(color, false, &pt, rx, ry);
|
||||
} else {
|
||||
for (int i = 0; i < verticesCount; ++i) {
|
||||
_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;
|
||||
}
|
||||
scalePoints(_vertices, verticesCount, _vid->_layerScale);
|
||||
_gfx.drawPolygon(color, false, _vertices, verticesCount);
|
||||
}
|
||||
}
|
||||
|
@ -1208,7 +1260,7 @@ void Cutscene::playSet(const uint8_t *p, int offset) {
|
|||
}
|
||||
|
||||
prepare();
|
||||
_gfx._layer = _page1;
|
||||
_gfx.setLayer(_page1, _vid->_w);
|
||||
|
||||
offset = 10;
|
||||
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->copyRect(0, 0, _vid->_w, _vid->_h, _page1, 256);
|
||||
_stub->copyRect(0, 0, _vid->_w, _vid->_h, _page1, _vid->_w);
|
||||
_stub->updateScreen(0);
|
||||
const int diff = 6 * TIMER_SLICE - (_stub->getTimeStamp() - timestamp);
|
||||
_stub->sleep((diff < 16) ? 16 : diff);
|
||||
|
|
|
@ -64,9 +64,9 @@ struct Cutscene {
|
|||
uint16_t _deathCutsceneId;
|
||||
bool _interrupted;
|
||||
bool _stop;
|
||||
uint8_t *_polPtr;
|
||||
uint8_t *_cmdPtr;
|
||||
uint8_t *_cmdPtrBak;
|
||||
const uint8_t *_polPtr;
|
||||
const uint8_t *_cmdPtr;
|
||||
const uint8_t *_cmdPtrBak;
|
||||
uint32_t _tstamp;
|
||||
uint8_t _frameDelay;
|
||||
bool _newPal;
|
||||
|
@ -105,6 +105,9 @@ struct Cutscene {
|
|||
|
||||
Cutscene(Resource *res, SystemStub *stub, Video *vid);
|
||||
|
||||
const uint8_t *getCommandData() const;
|
||||
const uint8_t *getPolygonData() const;
|
||||
|
||||
void sync();
|
||||
void copyPalette(const uint8_t *pal, uint16_t num);
|
||||
void updatePalette();
|
||||
|
|
437
game.cpp
437
game.cpp
|
@ -4,7 +4,8 @@
|
|||
* Copyright (C) 2005-2018 Gregory Montoir (cyx@users.sourceforge.net)
|
||||
*/
|
||||
|
||||
#include <ctime>
|
||||
#include <time.h>
|
||||
#include "decode_mac.h"
|
||||
#include "file.h"
|
||||
#include "fs.h"
|
||||
#include "game.h"
|
||||
|
@ -46,6 +47,10 @@ void Game::run() {
|
|||
_cut._patchedOffsetsTable = Cutscene::_ssiOffsetsTable;
|
||||
}
|
||||
break;
|
||||
case kResourceTypeMac:
|
||||
_res.MAC_loadClutData();
|
||||
_res.MAC_loadFontData();
|
||||
break;
|
||||
}
|
||||
|
||||
if (!g_options.bypass_protection) {
|
||||
|
@ -74,12 +79,15 @@ void Game::run() {
|
|||
_res.load_SPR_OFF("PERSO", _res._spr1);
|
||||
_res.load_FIB("GLOBAL");
|
||||
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) {
|
||||
if (_res._isDemo) {
|
||||
// do not present title screen and menus
|
||||
} else {
|
||||
if (presentMenu) {
|
||||
_mix.playMusic(1);
|
||||
switch (_res._type) {
|
||||
case kResourceTypeDOS:
|
||||
|
@ -111,6 +119,9 @@ void Game::run() {
|
|||
displayTitleScreenAmiga();
|
||||
_stub->setScreenSize(Video::GAMESCREEN_W, Video::GAMESCREEN_H);
|
||||
break;
|
||||
case kResourceTypeMac:
|
||||
// TODO:
|
||||
break;
|
||||
}
|
||||
if (_stub->_pi.quit) {
|
||||
break;
|
||||
|
@ -374,9 +385,12 @@ void Game::playCutscene(int id) {
|
|||
_mix.playMusic(Cutscene::_musicTable[_cut._id]);
|
||||
}
|
||||
_cut.play();
|
||||
if (id == 0xD && !_cut._interrupted && _res.isDOS()) {
|
||||
_cut._id = 0x4A;
|
||||
_cut.play();
|
||||
if (id == 0xD && !_cut._interrupted) {
|
||||
const bool extendedIntroduction = (_res._type == kResourceTypeDOS || _res._type == kResourceTypeMac);
|
||||
if (extendedIntroduction) {
|
||||
_cut._id = 0x4A;
|
||||
_cut.play();
|
||||
}
|
||||
}
|
||||
if (id == 0x3D) {
|
||||
_cut.playCredits();
|
||||
|
@ -430,11 +444,11 @@ void Game::showFinalScore() {
|
|||
playCutscene(0x49);
|
||||
char buf[50];
|
||||
snprintf(buf, sizeof(buf), "SCORE %08u", _score);
|
||||
_vid.drawString(buf, (256 - strlen(buf) * 8) / 2, 40, 0xE5);
|
||||
strcpy(buf, _menu._passwords[7][_skillLevel]);
|
||||
_vid.drawString(buf, (256 - strlen(buf) * 8) / 2, 16, 0xE7);
|
||||
_vid.drawString(buf, (Video::GAMESCREEN_W - strlen(buf) * Video::CHAR_W) / 2, 40, 0xE5);
|
||||
const char *str = _menu.getLevelPassword(7, _skillLevel);
|
||||
_vid.drawString(str, (Video::GAMESCREEN_W - strlen(str) * Video::CHAR_W) / 2, 16, 0xE7);
|
||||
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->processEvents();
|
||||
if (_stub->_pi.enter) {
|
||||
|
@ -446,9 +460,6 @@ void Game::showFinalScore() {
|
|||
}
|
||||
|
||||
bool Game::handleConfigPanel() {
|
||||
if (_res.isAmiga()) {
|
||||
return true;
|
||||
}
|
||||
const int x = 7;
|
||||
const int y = 10;
|
||||
const int w = 17;
|
||||
|
@ -461,33 +472,60 @@ bool Game::handleConfigPanel() {
|
|||
// the panel background is drawn using special characters from FB_TXT.FNT
|
||||
static const bool kUseDefaultFont = true;
|
||||
|
||||
// top-left rounded corder
|
||||
_vid.PC_drawChar(0x81, y, x, kUseDefaultFont);
|
||||
// top horizontal line
|
||||
for (int i = 1; i < w; ++i) {
|
||||
_vid.PC_drawChar(0x85, y, x + i, kUseDefaultFont);
|
||||
}
|
||||
// top-right rounded corner
|
||||
_vid.PC_drawChar(0x82, y, x + w, kUseDefaultFont);
|
||||
for (int j = 1; j < h; ++j) {
|
||||
// left vertical line
|
||||
_vid.PC_drawChar(0x86, y + j, x, kUseDefaultFont);
|
||||
switch (_res._type) {
|
||||
case kResourceTypeAmiga:
|
||||
// TODO
|
||||
return true;
|
||||
case kResourceTypeDOS:
|
||||
// top-left rounded corner
|
||||
_vid.PC_drawChar(0x81, y, x, kUseDefaultFont);
|
||||
// top-right rounded corner
|
||||
_vid.PC_drawChar(0x82, y, x + w, kUseDefaultFont);
|
||||
// bottom-left rounded corner
|
||||
_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) {
|
||||
_vid._charTransparentColor = 0xE2;
|
||||
_vid.PC_drawChar(0x20, y + j, x + i, kUseDefaultFont);
|
||||
_vid.PC_drawChar(0x85, y, x + i, kUseDefaultFont);
|
||||
_vid.PC_drawChar(0x88, y + h, x + i, kUseDefaultFont);
|
||||
}
|
||||
_vid._charTransparentColor = 0xFF;
|
||||
// right vertical line
|
||||
_vid.PC_drawChar(0x87, y + j, x + w, kUseDefaultFont);
|
||||
for (int j = 1; j < h; ++j) {
|
||||
_vid._charTransparentColor = 0xFF;
|
||||
// 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._charVar4 = 0xE5;
|
||||
|
@ -570,15 +608,15 @@ bool Game::handleContinueAbort() {
|
|||
while (timeout >= 0 && !_stub->_pi.quit) {
|
||||
const char *str;
|
||||
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);
|
||||
char buf[50];
|
||||
snprintf(buf, sizeof(buf), "%s : %d", str, timeout / 10);
|
||||
_vid.drawString(buf, 96, 88, 0xE3);
|
||||
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);
|
||||
_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);
|
||||
_vid.drawString(buf, 64, 154, 0xE3);
|
||||
if (_stub->_pi.dirMask & PlayerInput::DIR_UP) {
|
||||
|
@ -599,7 +637,7 @@ bool Game::handleContinueAbort() {
|
|||
_stub->_pi.enter = false;
|
||||
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);
|
||||
static const int COLOR_STEP = 8;
|
||||
static const int COLOR_MIN = 16;
|
||||
|
@ -642,7 +680,7 @@ bool Game::handleProtectionScreen() {
|
|||
int shapeNum = getRandomNumber() % 30;
|
||||
for (int16_t zoom = 2000; zoom != 0; zoom -= 100) {
|
||||
_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->sleep(30);
|
||||
}
|
||||
|
@ -654,10 +692,10 @@ bool Game::handleProtectionScreen() {
|
|||
do {
|
||||
codeText[len] = '\0';
|
||||
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];
|
||||
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();
|
||||
_stub->sleep(50);
|
||||
_stub->processEvents();
|
||||
|
@ -711,16 +749,8 @@ void Game::printLevelCode() {
|
|||
--_printLevelCodeCounter;
|
||||
if (_printLevelCodeCounter != 0) {
|
||||
char buf[32];
|
||||
const char *code = Menu::_passwords[_currentLevel][_skillLevel];
|
||||
if (_res.isAmiga()) {
|
||||
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);
|
||||
snprintf(buf, sizeof(buf), "CODE: %s", _menu.getLevelPassword(_currentLevel, _skillLevel));
|
||||
_vid.drawString(buf, (Video::GAMESCREEN_W - strlen(buf) * Video::CHAR_W) / 2, 16, 0xE7);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -728,7 +758,7 @@ void Game::printLevelCode() {
|
|||
void Game::printSaveStateCompleted() {
|
||||
if (_saveStateCompleted) {
|
||||
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;
|
||||
drawIcon(icon_num, 80, 8, 0xA);
|
||||
uint8_t txt_num = pge->init_PGE->text_num;
|
||||
const char *str = (const char *)_res.getTextString(_currentLevel, txt_num);
|
||||
_vid.drawString(str, (176 - strlen(str) * 8) / 2, 26, 0xE6);
|
||||
const uint8_t *str = _res.getTextString(_currentLevel, txt_num);
|
||||
drawString(str, 176, 26, 0xE6, true);
|
||||
if (icon_num == 2) {
|
||||
printSaveStateCompleted();
|
||||
return;
|
||||
|
@ -768,6 +798,11 @@ static int getLineLength(const uint8_t *str) {
|
|||
|
||||
void Game::drawStoryTexts() {
|
||||
if (_textToDisplay != 0xFFFF) {
|
||||
if (_res._type == kResourceTypeMac) {
|
||||
warning("Unhandled textToDisplay %d", _textToDisplay);
|
||||
_textToDisplay = 0xFFFF;
|
||||
return;
|
||||
}
|
||||
uint8_t textColor = 0xE8;
|
||||
const uint8_t *str = _res.getGameString(_textToDisplay);
|
||||
memcpy(_vid._tempLayer, _vid._frontLayer, _vid._layerSize);
|
||||
|
@ -797,7 +832,7 @@ void Game::drawStoryTexts() {
|
|||
int yPos = 26;
|
||||
while (1) {
|
||||
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;
|
||||
if (*str == 0 || *str == 0xB) {
|
||||
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() {
|
||||
if (!(_currentRoom & 0x80) && _currentRoom < 0x40) {
|
||||
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) {
|
||||
return;
|
||||
}
|
||||
assert(pge->anim_number < 1287);
|
||||
const uint8_t *dataPtr = _res._sprData[pge->anim_number];
|
||||
if (dataPtr == 0) {
|
||||
return;
|
||||
const uint8_t *dataPtr = 0;
|
||||
int8_t dw = 0, dh = 0;
|
||||
switch (_res._type) {
|
||||
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;
|
||||
switch (_res._type) {
|
||||
case kResourceTypeAmiga:
|
||||
|
@ -907,8 +966,10 @@ void Game::prepareAnimsHelper(LivePGE *pge, int16_t dx, int16_t dy) {
|
|||
h = dataPtr[3];
|
||||
dataPtr += 4;
|
||||
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;
|
||||
if (pge->flags & 2) {
|
||||
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);
|
||||
}
|
||||
} else {
|
||||
assert(pge->anim_number < _res._numSpc);
|
||||
const uint8_t *dataPtr = _res._spc + READ_BE_UINT16(_res._spc + pge->anim_number * 2);
|
||||
const uint8_t *dataPtr = 0;
|
||||
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 ypos = dy + pge->pos_y + 2;
|
||||
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);
|
||||
}
|
||||
break;
|
||||
case kResourceTypeMac:
|
||||
drawPiege(state);
|
||||
break;
|
||||
}
|
||||
} else {
|
||||
drawObject(state->dataPtr, state->x, state->y, pge->flags);
|
||||
drawPiege(state);
|
||||
}
|
||||
--state;
|
||||
} 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) {
|
||||
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);
|
||||
|
@ -1018,6 +1148,9 @@ void Game::drawObject(const uint8_t *dataPtr, int16_t x, int16_t y, uint8_t flag
|
|||
count = dataPtr[5];
|
||||
dataPtr += 6;
|
||||
break;
|
||||
case kResourceTypeMac:
|
||||
assert(0); // different graphics format
|
||||
break;
|
||||
}
|
||||
for (int i = 0; i < count; ++i) {
|
||||
drawObjectFrame(data, dataPtr, posx, posy, flags);
|
||||
|
@ -1052,6 +1185,9 @@ void Game::drawObjectFrame(const uint8_t *bankDataPtr, const uint8_t *dataPtr, i
|
|||
case kResourceTypeDOS:
|
||||
_vid.PC_decodeSpc(src, sprite_w, sprite_h, _res._scratchBuffer);
|
||||
break;
|
||||
case kResourceTypeMac:
|
||||
assert(0); // different graphics format
|
||||
break;
|
||||
}
|
||||
|
||||
src = _res._scratchBuffer;
|
||||
|
@ -1283,24 +1419,42 @@ int Game::loadMonsterSprites(LivePGE *pge) {
|
|||
_curMonsterFrame = mList[0];
|
||||
if (_curMonsterNum != mList[1]) {
|
||||
_curMonsterNum = mList[1];
|
||||
if (_res.isAmiga()) {
|
||||
_res.load(_monsterNames[1][_curMonsterNum], Resource::OT_SPM);
|
||||
static const uint8_t tab[4] = { 0, 8, 0, 8 };
|
||||
const int offset = _vid._mapPalSlot3 * 16 + tab[_curMonsterNum];
|
||||
for (int i = 0; i < 8; ++i) {
|
||||
_vid.setPaletteColorBE(0x50 + i, offset + i);
|
||||
switch (_res._type) {
|
||||
case kResourceTypeAmiga: {
|
||||
_res.load(_monsterNames[1][_curMonsterNum], Resource::OT_SPM);
|
||||
static const uint8_t tab[4] = { 0, 8, 0, 8 };
|
||||
const int offset = _vid._mapPalSlot3 * 16 + tab[_curMonsterNum];
|
||||
for (int i = 0; i < 8; ++i) {
|
||||
_vid.setPaletteColorBE(0x50 + i, offset + i);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
const char *name = _monsterNames[0][_curMonsterNum];
|
||||
_res.load(name, Resource::OT_SPRM);
|
||||
_res.load_SPR_OFF(name, _res._sprm);
|
||||
_vid.setPaletteSlotLE(5, _monsterPals[_curMonsterNum]);
|
||||
break;
|
||||
case kResourceTypeDOS: {
|
||||
const char *name = _monsterNames[0][_curMonsterNum];
|
||||
_res.load(name, Resource::OT_SPRM);
|
||||
_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;
|
||||
}
|
||||
|
||||
bool Game::hasLevelMap(int level, int room) const {
|
||||
if (_res._type == kResourceTypeMac) {
|
||||
return _res.MAC_hasLevelMap(level, room);
|
||||
}
|
||||
if (_res._map) {
|
||||
return READ_LE_UINT32(_res._map + room * 6) != 0;
|
||||
} else if (_res._lev) {
|
||||
|
@ -1347,6 +1501,29 @@ void Game::loadLevelMap() {
|
|||
}
|
||||
_vid.PC_setLevelPalettes();
|
||||
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_TBN);
|
||||
break;
|
||||
case kResourceTypeMac:
|
||||
_res.MAC_unloadLevelData();
|
||||
_res.MAC_loadLevelData(_currentLevel);
|
||||
break;
|
||||
}
|
||||
|
||||
_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:
|
||||
_vid.PC_decodeIcn(_res._icn, iconNum, buf);
|
||||
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);
|
||||
}
|
||||
|
||||
|
@ -1569,35 +1776,43 @@ void Game::handleInventory() {
|
|||
int current_line = 0;
|
||||
bool display_score = false;
|
||||
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_h = 16;
|
||||
do {
|
||||
int icon_x = 56;
|
||||
int icon_w = 9;
|
||||
do {
|
||||
drawIcon(icon_num, icon_x, icon_y, 0xF);
|
||||
++icon_num;
|
||||
icon_x += icon_spr_w;
|
||||
} while (--icon_w);
|
||||
icon_y += icon_spr_h;
|
||||
} while (--icon_h);
|
||||
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;
|
||||
switch (_res._type) {
|
||||
case kResourceTypeAmiga:
|
||||
case kResourceTypeDOS: {
|
||||
// draw inventory background
|
||||
int icon_h = 5;
|
||||
int icon_y = 140;
|
||||
int icon_num = 31;
|
||||
do {
|
||||
int icon_x = 56;
|
||||
int icon_w = 9;
|
||||
do {
|
||||
drawIcon(icon_num, icon_x, icon_y, 0xF);
|
||||
++icon_num;
|
||||
icon_x += icon_spr_w;
|
||||
} while (--icon_w);
|
||||
icon_y += icon_spr_h;
|
||||
} while (--icon_h);
|
||||
}
|
||||
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) {
|
||||
int icon_x_pos = 72;
|
||||
for (int i = 0; i < 4; ++i) {
|
||||
|
@ -1610,12 +1825,12 @@ void Game::handleInventory() {
|
|||
drawIcon(76, icon_x_pos, 157, 0xA);
|
||||
selected_pge = items[item_it].live_pge;
|
||||
uint8_t txt_num = items[item_it].init_pge->text_num;
|
||||
const char *str = (const char *)_res.getTextString(_currentLevel, txt_num);
|
||||
_vid.drawString(str, (256 - strlen(str) * 8) / 2, 189, 0xED);
|
||||
const uint8_t *str = _res.getTextString(_currentLevel, txt_num);
|
||||
drawString(str, Video::GAMESCREEN_W, 189, 0xED, true);
|
||||
if (items[item_it].init_pge->init_flags & 4) {
|
||||
char buf[10];
|
||||
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;
|
||||
|
@ -1629,9 +1844,9 @@ void Game::handleInventory() {
|
|||
} else {
|
||||
char buf[50];
|
||||
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));
|
||||
_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();
|
||||
|
|
2
game.h
2
game.h
|
@ -109,10 +109,12 @@ struct Game {
|
|||
void printSaveStateCompleted();
|
||||
void drawLevelTexts();
|
||||
void drawStoryTexts();
|
||||
void drawString(const uint8_t *p, int x, int y, uint8_t color, bool hcenter = false);
|
||||
void prepareAnims();
|
||||
void prepareAnimsHelper(LivePGE *pge, int16_t dx, int16_t dy);
|
||||
void drawAnims();
|
||||
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 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);
|
||||
|
|
17
graphics.cpp
17
graphics.cpp
|
@ -7,6 +7,11 @@
|
|||
#include "graphics.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) {
|
||||
debug(DBG_VIDEO, "Graphics::setClippingRect(%d, %d, %d, %d)", rx, ry, rw, rh);
|
||||
_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) {
|
||||
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) {
|
||||
*(_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) {
|
||||
debug(DBG_VIDEO, "Graphics::fillArea()");
|
||||
int16_t *pts = _areaPoints;
|
||||
uint8_t *dst = _layer + (_cry + *pts++) * 256 + _crx;
|
||||
uint8_t *dst = _layer + (_cry + *pts++) * _layerPitch + _crx;
|
||||
int16_t x1 = *pts++;
|
||||
if (x1 >= 0) {
|
||||
if (hasAlpha && color > 0xC7) {
|
||||
|
@ -213,7 +218,7 @@ void Graphics::fillArea(uint8_t color, bool hasAlpha) {
|
|||
*(dst + x1 + i) |= color & 8; // XXX 0x88
|
||||
}
|
||||
}
|
||||
dst += 256;
|
||||
dst += _layerPitch;
|
||||
x1 = *pts++;
|
||||
} while (x1 >= 0);
|
||||
} else {
|
||||
|
@ -223,7 +228,7 @@ void Graphics::fillArea(uint8_t color, bool hasAlpha) {
|
|||
int len = x2 - x1 + 1;
|
||||
memset(dst + x1, color, len);
|
||||
}
|
||||
dst += 256;
|
||||
dst += _layerPitch;
|
||||
x1 = *pts++;
|
||||
} while (x1 >= 0);
|
||||
}
|
||||
|
@ -341,8 +346,8 @@ void Graphics::drawPolygon(uint8_t color, bool hasAlpha, const Point *pts, uint8
|
|||
debug(DBG_VIDEO, "Graphics::drawPolygon()");
|
||||
assert(numPts * 4 < 0x100);
|
||||
|
||||
int16_t *apts1 = &_areaPoints[0x100];
|
||||
int16_t *apts2 = &_areaPoints[0x100 + numPts * 2];
|
||||
int16_t *apts1 = &_areaPoints[AREA_POINTS_SIZE];
|
||||
int16_t *apts2 = &_areaPoints[AREA_POINTS_SIZE + numPts * 2];
|
||||
|
||||
int16_t xmin, xmax, ymin, ymax;
|
||||
xmin = xmax = pts[0].x;
|
||||
|
|
|
@ -10,10 +10,13 @@
|
|||
#include "intern.h"
|
||||
|
||||
struct Graphics {
|
||||
static const int AREA_POINTS_SIZE = 256 * 2; // maxY * sizeof(Point) / sizeof(int16_t)
|
||||
uint8_t *_layer;
|
||||
int16_t _areaPoints[0x200];
|
||||
int _layerPitch;
|
||||
int16_t _areaPoints[AREA_POINTS_SIZE * 2];
|
||||
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 drawPoint(uint8_t color, const Point *pt);
|
||||
void drawLine(uint8_t color, const Point *pt1, const Point *pt2);
|
||||
|
|
9
intern.h
9
intern.h
|
@ -80,7 +80,8 @@ enum Language {
|
|||
|
||||
enum ResourceType {
|
||||
kResourceTypeAmiga,
|
||||
kResourceTypeDOS
|
||||
kResourceTypeDOS,
|
||||
kResourceTypeMac,
|
||||
};
|
||||
|
||||
enum Skill {
|
||||
|
@ -91,13 +92,15 @@ enum Skill {
|
|||
|
||||
struct Options {
|
||||
bool bypass_protection;
|
||||
bool play_disabled_cutscenes;
|
||||
bool enable_password_menu;
|
||||
bool fade_out_palette;
|
||||
bool use_tiledata;
|
||||
bool use_text_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 {
|
||||
|
|
14
main.cpp
14
main.cpp
|
@ -38,6 +38,7 @@ static int detectVersion(FileSystem *fs) {
|
|||
{ "LEVEL1.BNQ", kResourceTypeDOS, "DOS (Demo)" },
|
||||
{ "LEVEL1.LEV", kResourceTypeAmiga, "Amiga" },
|
||||
{ "DEMO.LEV", kResourceTypeAmiga, "Amiga (Demo)" },
|
||||
{ "FLASHBACK.BIN", kResourceTypeMac, "Macintosh" },
|
||||
{ 0, -1, 0 }
|
||||
};
|
||||
for (int i = 0; table[i].filename; ++i) {
|
||||
|
@ -80,24 +81,29 @@ const char *g_caption = "REminiscence";
|
|||
static void initOptions() {
|
||||
// defaults
|
||||
g_options.bypass_protection = true;
|
||||
g_options.play_disabled_cutscenes = false;
|
||||
g_options.enable_password_menu = false;
|
||||
g_options.fade_out_palette = true;
|
||||
g_options.use_text_cutscenes = false;
|
||||
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
|
||||
struct {
|
||||
const char *name;
|
||||
bool *value;
|
||||
} opts[] = {
|
||||
{ "bypass_protection", &g_options.bypass_protection },
|
||||
{ "play_disabled_cutscenes", &g_options.play_disabled_cutscenes },
|
||||
{ "enable_password_menu", &g_options.enable_password_menu },
|
||||
{ "fade_out_palette", &g_options.fade_out_palette },
|
||||
{ "use_tiledata", &g_options.use_tiledata },
|
||||
{ "use_text_cutscenes", &g_options.use_text_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 }
|
||||
};
|
||||
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;
|
||||
SystemStub *stub = SystemStub_SDL_create();
|
||||
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();
|
||||
delete g;
|
||||
stub->destroy();
|
||||
|
|
67
menu.cpp
67
menu.cpp
|
@ -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) {
|
||||
debug(DBG_MENU, "Menu::drawString2()");
|
||||
int i = 0;
|
||||
for (; str[i]; ++i) {
|
||||
_vid->PC_drawChar((uint8_t)str[i], y, x + i, true);
|
||||
int w = Video::CHAR_W;
|
||||
int h = Video::CHAR_H;
|
||||
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) {
|
||||
|
@ -216,7 +229,7 @@ bool Menu::handlePasswordScreen() {
|
|||
password[len] = '\0';
|
||||
for (int level = 0; level < 8; ++level) {
|
||||
for (int skill = 0; skill < 3; ++skill) {
|
||||
if (strcmp(_passwords[level][skill], password) == 0) {
|
||||
if (strcmp(getLevelPassword(level, skill), password) == 0) {
|
||||
_level = level;
|
||||
_skill = skill;
|
||||
return true;
|
||||
|
@ -328,17 +341,19 @@ void Menu::handleTitleScreen() {
|
|||
menuItems[menuItemsCount].str = LocaleData::LI_07_START;
|
||||
menuItems[menuItemsCount].opt = MENU_OPTION_ITEM_START;
|
||||
++menuItemsCount;
|
||||
if (g_options.enable_password_menu) {
|
||||
menuItems[menuItemsCount].str = LocaleData::LI_08_SKILL;
|
||||
menuItems[menuItemsCount].opt = MENU_OPTION_ITEM_SKILL;
|
||||
++menuItemsCount;
|
||||
menuItems[menuItemsCount].str = LocaleData::LI_09_PASSWORD;
|
||||
menuItems[menuItemsCount].opt = MENU_OPTION_ITEM_PASSWORD;
|
||||
++menuItemsCount;
|
||||
} else {
|
||||
menuItems[menuItemsCount].str = LocaleData::LI_06_LEVEL;
|
||||
menuItems[menuItemsCount].opt = MENU_OPTION_ITEM_LEVEL;
|
||||
++menuItemsCount;
|
||||
if (!_res->_isDemo) {
|
||||
if (g_options.enable_password_menu) {
|
||||
menuItems[menuItemsCount].str = LocaleData::LI_08_SKILL;
|
||||
menuItems[menuItemsCount].opt = MENU_OPTION_ITEM_SKILL;
|
||||
++menuItemsCount;
|
||||
menuItems[menuItemsCount].str = LocaleData::LI_09_PASSWORD;
|
||||
menuItems[menuItemsCount].opt = MENU_OPTION_ITEM_PASSWORD;
|
||||
++menuItemsCount;
|
||||
} else {
|
||||
menuItems[menuItemsCount].str = LocaleData::LI_06_LEVEL;
|
||||
menuItems[menuItemsCount].opt = MENU_OPTION_ITEM_LEVEL;
|
||||
++menuItemsCount;
|
||||
}
|
||||
}
|
||||
menuItems[menuItemsCount].str = LocaleData::LI_10_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
5
menu.h
|
@ -40,9 +40,10 @@ struct Menu {
|
|||
int opt;
|
||||
};
|
||||
|
||||
static const char *_passwords[8][3];
|
||||
static const char *_passwordsDOS[];
|
||||
static const char *_passwordsFrAmiga[];
|
||||
static const char *_passwordsEnAmiga[];
|
||||
static const char *_passwordsMac[];
|
||||
|
||||
Resource *_res;
|
||||
SystemStub *_stub;
|
||||
|
@ -72,6 +73,8 @@ struct Menu {
|
|||
bool handlePasswordScreen();
|
||||
bool handleLevelScreen();
|
||||
void handleTitleScreen();
|
||||
|
||||
const char *getLevelPassword(int level, int skill) const;
|
||||
};
|
||||
|
||||
#endif // MENU_H__
|
||||
|
|
312
resource.cpp
312
resource.cpp
|
@ -4,6 +4,7 @@
|
|||
* Copyright (C) 2005-2018 Gregory Montoir (cyx@users.sourceforge.net)
|
||||
*/
|
||||
|
||||
#include "decode_mac.h"
|
||||
#include "file.h"
|
||||
#include "fs.h"
|
||||
#include "resource.h"
|
||||
|
@ -17,6 +18,7 @@ Resource::Resource(FileSystem *fs, ResourceType ver, Language lang) {
|
|||
_lang = lang;
|
||||
_isDemo = false;
|
||||
_aba = 0;
|
||||
_mac = 0;
|
||||
_readUint16 = (_type == kResourceTypeDOS) ? READ_LE_UINT16 : READ_BE_UINT16;
|
||||
_readUint32 = (_type == kResourceTypeDOS) ? READ_LE_UINT32 : READ_BE_UINT32;
|
||||
_scratchBuffer = (uint8_t *)malloc(320 * 224 + 1024);
|
||||
|
@ -63,16 +65,30 @@ void Resource::init() {
|
|||
_aba = new ResourceAba(_fs);
|
||||
_aba->readEntries();
|
||||
_isDemo = true;
|
||||
} else if (!_fs->exists("LEVEL1.MAP")) { // fbdemofr (no cutscenes)
|
||||
}
|
||||
if (!fileExists("LEVEL1.MAP")) { // fbdemofr (no cutscenes)
|
||||
_isDemo = true;
|
||||
}
|
||||
break;
|
||||
case kResourceTypeMac:
|
||||
_mac = new ResourceMac(ResourceMac::FILENAME, _fs);
|
||||
_mac->loadMap();
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
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() {
|
||||
free(_tbn); _tbn = 0;
|
||||
free(_mbk); _mbk = 0;
|
||||
|
@ -96,6 +112,12 @@ void Resource::load_DEM(const char *filename) {
|
|||
if (_dem) {
|
||||
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);
|
||||
}
|
||||
break;
|
||||
case kResourceTypeMac:
|
||||
MAC_loadCutsceneText();
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -893,6 +918,10 @@ void Resource::decodeOBJ(const uint8_t *tmp, int size) {
|
|||
uint32_t offsets[256];
|
||||
int tmpOffset = 0;
|
||||
_numObjectNodes = 230;
|
||||
if (_type == kResourceTypeMac) {
|
||||
_numObjectNodes = _readUint16(tmp);
|
||||
tmpOffset += 2;
|
||||
}
|
||||
for (int i = 0; i < _numObjectNodes; ++i) {
|
||||
offsets[i] = _readUint32(tmp + tmpOffset); tmpOffset += 4;
|
||||
}
|
||||
|
@ -1292,6 +1321,9 @@ int Resource::getBankDataSize(uint16_t num) {
|
|||
len &= 0x7FFF;
|
||||
}
|
||||
break;
|
||||
case kResourceTypeMac:
|
||||
assert(0); // different graphics format
|
||||
break;
|
||||
}
|
||||
return len * 32;
|
||||
}
|
||||
|
@ -1337,3 +1369,281 @@ uint8_t *Resource::loadBankData(uint16_t num) {
|
|||
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
|
||||
}
|
||||
|
|
103
resource.h
103
resource.h
|
@ -9,7 +9,9 @@
|
|||
|
||||
#include "intern.h"
|
||||
#include "resource_aba.h"
|
||||
#include "resource_mac.h"
|
||||
|
||||
struct DecodeBuffer;
|
||||
struct File;
|
||||
struct FileSystem;
|
||||
|
||||
|
@ -108,6 +110,10 @@ struct Resource {
|
|||
NUM_SPRITES = 1287
|
||||
};
|
||||
|
||||
enum {
|
||||
kClutSize = 1024
|
||||
};
|
||||
|
||||
static const uint16_t _voicesOffsetsTable[];
|
||||
static const uint32_t _spmOffsetsTable[];
|
||||
static const char *_splNames[];
|
||||
|
@ -117,6 +123,7 @@ struct Resource {
|
|||
Language _lang;
|
||||
bool _isDemo;
|
||||
ResourceAba *_aba;
|
||||
ResourceMac *_mac;
|
||||
uint16_t (*_readUint16)(const void *);
|
||||
uint32_t (*_readUint32)(const void *);
|
||||
bool _hasSeqData;
|
||||
|
@ -162,6 +169,12 @@ struct Resource {
|
|||
int _bankBuffersCount;
|
||||
uint8_t *_dem;
|
||||
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();
|
||||
|
@ -172,6 +185,8 @@ struct Resource {
|
|||
bool isDOS() const { return _type == kResourceTypeDOS; }
|
||||
bool isAmiga() const { return _type == kResourceTypeAmiga; }
|
||||
|
||||
bool fileExists(const char *filename);
|
||||
|
||||
void clearLevelRes();
|
||||
void load_DEM(const char *filename);
|
||||
void load_FIB(const char *fileName);
|
||||
|
@ -213,10 +228,16 @@ struct Resource {
|
|||
void load_BNQ(File *pf);
|
||||
void load_SPM(File *f);
|
||||
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);
|
||||
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) {
|
||||
const uint8_t *p = 0;
|
||||
switch (level) {
|
||||
|
@ -246,29 +267,103 @@ struct Resource {
|
|||
}
|
||||
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);
|
||||
}
|
||||
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);
|
||||
}
|
||||
const uint8_t *getCineString(int num) {
|
||||
const uint8_t *getCineString(int num) const {
|
||||
if (_lang == LANG_JP) {
|
||||
const int offset = READ_BE_UINT16(LocaleData::_cineBinJP + num * 2);
|
||||
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) {
|
||||
const int offset = READ_BE_UINT16(_cine_off + num * 2);
|
||||
return _cine_txt + offset;
|
||||
}
|
||||
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] : "";
|
||||
}
|
||||
void clearBankData();
|
||||
int getBankDataSize(uint16_t num);
|
||||
uint8_t *findBankData(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__
|
||||
|
|
14
rs.cfg
14
rs.cfg
|
@ -1,9 +1,6 @@
|
|||
# display copy protection shapes
|
||||
bypass_protection=true
|
||||
|
||||
# enable playback of 'asc', 'metro' and 'serrure' cutscenes
|
||||
play_disabled_cutscenes=false
|
||||
|
||||
# use original password level selection menu screen
|
||||
enable_password_menu=false
|
||||
|
||||
|
@ -19,5 +16,14 @@ use_text_cutscenes=false
|
|||
# enable playback of .SEQ cutscenes (use polygonal if false)
|
||||
use_seq_cutscenes=true
|
||||
|
||||
# enable playback of 'ASC' cutscene
|
||||
play_asc_cutscene=true
|
||||
|
||||
# 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
|
||||
|
|
|
@ -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
|
||||
};
|
||||
|
||||
const char *Menu::_passwords[8][3] = {
|
||||
{ "JAGUAR", "BANTHA", "TOHOLD" },
|
||||
{ "COMBEL", "SHIVA", "PICOLO" },
|
||||
{ "ANTIC", "KASYYK", "FUGU" },
|
||||
{ "NOLAN", "SARLAC", "CAPSUL" },
|
||||
{ "ARTHUR", "MAENOC", "ZZZAP" },
|
||||
{ "SHIRYU", "SULUST", "MANIAC" },
|
||||
{ "RENDER", "NEPTUN", "NO WAY" },
|
||||
{ "BELUGA", "BELUGA", "BELUGA" }
|
||||
const char *Menu::_passwordsDOS[] = {
|
||||
"JAGUAR", "COMBEL", "ANTIC", "NOLAN", "ARTHUR", "SHIRYU", "RENDER", "BELUGA", // easy
|
||||
"BANTHA", "SHIVA", "KASYYK", "SARLAC", "MAENOC", "SULUST", "NEPTUN", "BELUGA", // normal
|
||||
"TOHOLD", "PICOLO", "FUGU", "CAPSUL", "ZZZAP", "MANIAC", "NO WAY", "BELUGA", // hard
|
||||
};
|
||||
|
||||
const char *Menu::_passwordsFrAmiga[] = {
|
||||
|
@ -3246,6 +3241,12 @@ const char *Menu::_passwordsEnAmiga[] = {
|
|||
"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[] = {
|
||||
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
|
||||
|
|
|
@ -234,7 +234,6 @@ void SystemStub_SDL::updateScreen(int shakeOffset) {
|
|||
r.x = 0;
|
||||
r.y = shakeOffset * _scaleFactor;
|
||||
SDL_GetRendererOutputSize(_renderer, &r.w, &r.h);
|
||||
r.h -= r.y;
|
||||
SDL_RenderCopy(_renderer, _texture, 0, &r);
|
||||
} else {
|
||||
SDL_RenderCopy(_renderer, _texture, 0, 0);
|
||||
|
|
139
video.cpp
139
video.cpp
|
@ -4,6 +4,7 @@
|
|||
* Copyright (C) 2005-2018 Gregory Montoir (cyx@users.sourceforge.net)
|
||||
*/
|
||||
|
||||
#include "decode_mac.h"
|
||||
#include "resource.h"
|
||||
#include "systemstub.h"
|
||||
#include "unpack.h"
|
||||
|
@ -12,8 +13,9 @@
|
|||
|
||||
Video::Video(Resource *res, SystemStub *stub)
|
||||
: _res(res), _stub(stub) {
|
||||
_w = GAMESCREEN_W;
|
||||
_h = GAMESCREEN_H;
|
||||
_layerScale = (_res->_type == kResourceTypeMac) ? 2 : 1; // Macintosh version is 512x448
|
||||
_w = GAMESCREEN_W * _layerScale;
|
||||
_h = GAMESCREEN_H * _layerScale;
|
||||
_layerSize = _w * _h;
|
||||
_frontLayer = (uint8_t *)calloc(1, _layerSize);
|
||||
_backLayer = (uint8_t *)calloc(1,_layerSize);
|
||||
|
@ -33,6 +35,9 @@ Video::Video(Resource *res, SystemStub *stub)
|
|||
case kResourceTypeDOS:
|
||||
_drawChar = &Video::PC_drawStringChar;
|
||||
break;
|
||||
case kResourceTypeMac:
|
||||
_drawChar = &Video::MAC_drawStringChar;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -63,7 +68,7 @@ void Video::updateScreen() {
|
|||
debug(DBG_VIDEO, "Video::updateScreen()");
|
||||
// _fullRefresh = true;
|
||||
if (_fullRefresh) {
|
||||
_stub->copyRect(0, 0, _w, _h, _frontLayer, 256);
|
||||
_stub->copyRect(0, 0, _w, _h, _frontLayer, _w);
|
||||
_stub->updateScreen(_shakeOffset);
|
||||
_fullRefresh = false;
|
||||
} else {
|
||||
|
@ -78,14 +83,14 @@ void Video::updateScreen() {
|
|||
++nh;
|
||||
} else if (nh != 0) {
|
||||
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;
|
||||
++count;
|
||||
}
|
||||
}
|
||||
if (nh != 0) {
|
||||
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;
|
||||
}
|
||||
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) {
|
||||
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;
|
||||
y *= 8;
|
||||
x *= 8;
|
||||
y *= CHAR_W;
|
||||
x *= CHAR_H;
|
||||
const uint8_t *src = fnt + (c - 32) * 32;
|
||||
uint8_t *dst = _frontLayer + x + 256 * y;
|
||||
for (int h = 0; h < 8; ++h) {
|
||||
uint8_t *dst = _frontLayer + x + _w * y;
|
||||
for (int h = 0; h < CHAR_H; ++h) {
|
||||
for (int i = 0; i < 4; ++i, ++src) {
|
||||
const uint8_t c1 = *src >> 4;
|
||||
if (c1 != 0) {
|
||||
|
@ -829,11 +834,12 @@ void Video::PC_drawChar(uint8_t c, int16_t y, int16_t x, bool forceDefaultFont)
|
|||
}
|
||||
++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);
|
||||
AMIGA_decodeIcn(src, chr - 32, _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);
|
||||
src += (chr - 32) * 8 * 4;
|
||||
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) {
|
||||
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;
|
||||
drawCharFunc dcf = _drawChar;
|
||||
int len = 0;
|
||||
uint8_t *dst = _frontLayer + y * 256 + x;
|
||||
while (1) {
|
||||
const uint8_t c = *str++;
|
||||
if (c == 0 || c == 0xB || c == 0xA) {
|
||||
break;
|
||||
}
|
||||
(this->*dcf)(dst, 256, fnt, col, c);
|
||||
dst += CHAR_W;
|
||||
(this->*_drawChar)(_frontLayer, _w, x + len * CHAR_W, y, fnt, col, c);
|
||||
++len;
|
||||
}
|
||||
markBlockAsDirty(x, y, len * 8, 8);
|
||||
markBlockAsDirty(x, y, len * CHAR_W, CHAR_H);
|
||||
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
|
||||
int r = (color & 0xF00) >> 8;
|
||||
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;
|
||||
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
15
video.h
|
@ -13,7 +13,7 @@ struct Resource;
|
|||
struct SystemStub;
|
||||
|
||||
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 {
|
||||
GAMESCREEN_W = 256,
|
||||
|
@ -35,6 +35,7 @@ struct Video {
|
|||
|
||||
int _w, _h;
|
||||
int _layerSize;
|
||||
int _layerScale; // 1 for Amiga/PC, 2 for Macintosh
|
||||
uint8_t *_frontLayer;
|
||||
uint8_t *_backLayer;
|
||||
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 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_drawStringChar(uint8_t *dst, int pitch, 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 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, 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);
|
||||
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);
|
||||
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__
|
||||
|
|
Loading…
Reference in New Issue