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
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)

View File

@ -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

View File

@ -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);

View File

@ -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
View File

@ -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
View File

@ -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);

View File

@ -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;

View File

@ -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);

View File

@ -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 {

View File

@ -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();

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) {
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
View File

@ -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__

View File

@ -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
}

View File

@ -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
View File

@ -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

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
};
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

View File

@ -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
View File

@ -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
View File

@ -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__