Import 0.4.7

This commit is contained in:
Gregory Montoir 2021-05-02 00:00:00 +08:00
parent 2dc61ca627
commit bc1337da63
21 changed files with 444 additions and 250 deletions

View File

@ -1,3 +1,7 @@
* release 0.4.7
- added detection for Macintosh CD version
- restored some content from MEMO cutscene
* release 0.4.6
- added rewind to automatic saves
- fixed passwords and protection codes input

View File

@ -1,6 +1,6 @@
REminiscence README
Release version: 0.4.6
Release version: 0.4.7
-------------------------------------------------------------------------------

View File

@ -332,7 +332,7 @@ int Game::col_detectHitCallback4(LivePGE *pge1, LivePGE *pge2, int16_t unk1, int
if (pge1->init_PGE->object_type == unk2) {
if ((pge1->flags & 1) != (pge2->flags & 1)) {
if (col_detectHitCallbackHelper(pge1, unk1) == 0) {
pge_updateGroup(pge2->index, pge1->index, unk1);
pge_sendMessage(pge2->index, pge1->index, unk1);
return 1;
}
}
@ -346,7 +346,7 @@ int Game::col_detectHitCallback5(LivePGE *pge1, LivePGE *pge2, int16_t unk1, int
if (pge1->init_PGE->object_type == unk2) {
if ((pge1->flags & 1) == (pge2->flags & 1)) {
if (col_detectHitCallbackHelper(pge1, unk1) == 0) {
pge_updateGroup(pge2->index, pge1->index, unk1);
pge_sendMessage(pge2->index, pge1->index, unk1);
return 1;
}
}
@ -355,33 +355,33 @@ int Game::col_detectHitCallback5(LivePGE *pge1, LivePGE *pge2, int16_t unk1, int
return 0;
}
int Game::col_detectHitCallbackHelper(LivePGE *pge, int16_t groupId) {
int Game::col_detectHitCallbackHelper(LivePGE *pge, int16_t msgNum) {
InitPGE *init_pge = pge->init_PGE;
assert(init_pge->obj_node_number < _res._numObjectNodes);
ObjectNode *on = _res._objectNodesMap[init_pge->obj_node_number];
Object *obj = &on->objects[pge->first_obj_number];
int i = pge->first_obj_number;
while (pge->obj_type == obj->type && on->last_obj_number > i) {
if (obj->opcode2 == 0x6B) { // pge_op_isInGroupSlice
if (obj->opcode2 == 0x6B) { // pge_isToggleable
if (obj->opcode_arg2 == 0) {
if (groupId == 1 || groupId == 2) return 0xFFFF;
if (msgNum == 1 || msgNum == 2) return 0xFFFF;
}
if (obj->opcode_arg2 == 1) {
if (groupId == 3 || groupId == 4) return 0xFFFF;
if (msgNum == 3 || msgNum == 4) return 0xFFFF;
}
} else if (obj->opcode2 == 0x22) { // pge_op_isInGroup
if (obj->opcode_arg2 == groupId) return 0xFFFF;
} else if (obj->opcode2 == 0x22) { // pge_hasPiegeSentMessage
if (obj->opcode_arg2 == msgNum) return 0xFFFF;
}
if (obj->opcode1 == 0x6B) { // pge_op_isInGroupSlice
if (obj->opcode1 == 0x6B) { // pge_isToggleable
if (obj->opcode_arg1 == 0) {
if (groupId == 1 || groupId == 2) return 0xFFFF;
if (msgNum == 1 || msgNum == 2) return 0xFFFF;
}
if (obj->opcode_arg1 == 1) {
if (groupId == 3 || groupId == 4) return 0xFFFF;
if (msgNum == 3 || msgNum == 4) return 0xFFFF;
}
} else if (obj->opcode1 == 0x22) { // pge_op_isInGroup
if (obj->opcode_arg1 == groupId) return 0xFFFF;
} else if (obj->opcode1 == 0x22) { // pge_hasPiegeSentMessage
if (obj->opcode_arg1 == msgNum) return 0xFFFF;
}
++obj;
++i;
@ -415,7 +415,7 @@ int Game::col_detectGunHitCallback2(LivePGE *pge1, LivePGE *pge2, int16_t arg4,
}
}
if (col_detectHitCallbackHelper(pge1, id) != 0) {
pge_updateGroup(pge2->index, pge1->index, id);
pge_sendMessage(pge2->index, pge1->index, id);
return 1;
}
}
@ -439,10 +439,9 @@ int Game::col_detectGunHitCallback3(LivePGE *pge1, LivePGE *pge2, int16_t arg4,
}
}
if (col_detectHitCallbackHelper(pge1, id) != 0) {
pge_updateGroup(pge2->index, pge1->index, id);
pge_sendMessage(pge2->index, pge1->index, id);
return 1;
}
}
}
return 0;

View File

@ -71,11 +71,11 @@ void Cutscene::updatePalette() {
}
}
void Cutscene::setPalette() {
void Cutscene::updateScreen() {
sync();
updatePalette();
SWAP(_page0, _page1);
_stub->copyRect(0, 0, _vid->_w, _vid->_h, _page0, _vid->_w);
SWAP(_frontPage, _backPage);
_stub->copyRect(0, 0, _vid->_w, _vid->_h, _frontPage, _vid->_w);
_stub->updateScreen(0);
}
@ -187,11 +187,11 @@ void Cutscene::drawText(int16_t x, int16_t y, const uint8_t *p, uint16_t color,
}
}
void Cutscene::swapLayers() {
void Cutscene::clearBackPage() {
if (_clearScreen == 0) {
memcpy(_page1, _pageC, _vid->_layerSize);
memcpy(_backPage, _auxPage, _vid->_layerSize);
} else {
memset(_page1, 0xC0, _vid->_layerSize);
memset(_backPage, 0xC0, _vid->_layerSize);
}
}
@ -230,7 +230,7 @@ void Cutscene::drawCreditsText() {
} else {
_creditsTextCounter -= 10;
}
drawText((_creditsTextPosX - 1) * 8, _creditsTextPosY * 8, _textBuf, 0xEF, _page1, kTextJustifyLeft);
drawText((_creditsTextPosX - 1) * 8, _creditsTextPosY * 8, _textBuf, 0xEF, _backPage, kTextJustifyLeft);
}
}
@ -274,8 +274,8 @@ void Cutscene::op_markCurPos() {
_cmdPtrBak = _cmdPtr;
drawCreditsText();
_frameDelay = 5;
setPalette();
swapLayers();
updateScreen();
clearBackPage();
_creditsSlowText = 0;
}
@ -283,7 +283,7 @@ void Cutscene::op_refreshScreen() {
debug(DBG_CUT, "Cutscene::op_refreshScreen()");
_clearScreen = fetchNextCmdByte();
if (_clearScreen != 0) {
swapLayers();
clearBackPage();
_creditsSlowText = 0;
}
}
@ -298,11 +298,11 @@ void Cutscene::op_waitForSync() {
if (_textBuf == _textCurBuf) {
_creditsTextCounter = _res->isAmiga() ? 60 : 20;
}
memcpy(_page1, _page0, _vid->_layerSize);
memcpy(_backPage, _frontPage, _vid->_layerSize);
drawCreditsText();
setPalette();
updateScreen();
} while (--n);
swapLayers();
clearBackPage();
_creditsSlowText = 0;
} else {
_frameDelay = fetchNextCmdByte() * 4;
@ -312,7 +312,7 @@ void Cutscene::op_waitForSync() {
void Cutscene::drawShape(const uint8_t *data, int16_t x, int16_t y) {
debug(DBG_CUT, "Cutscene::drawShape()");
_gfx.setLayer(_page1, _vid->_w);
_gfx.setLayer(_backPage, _vid->_w);
uint8_t numVertices = *data++;
if (numVertices & 0x80) {
Point pt;
@ -393,10 +393,12 @@ void Cutscene::op_drawShape() {
drawShape(primitiveVertices, x + dx, y + dy);
}
if (_clearScreen != 0) {
memcpy(_pageC, _page1, _vid->_layerSize);
memcpy(_auxPage, _backPage, _vid->_layerSize);
}
}
static int _paletteNum = -1;
void Cutscene::op_setPalette() {
debug(DBG_CUT, "Cutscene::op_setPalette()");
uint8_t num = fetchNextCmdByte();
@ -408,6 +410,7 @@ void Cutscene::op_setPalette() {
_palBuf[0x20] = 0x0F;
_palBuf[0x21] = 0xFF;
}
_paletteNum = num;
}
void Cutscene::op_drawCaptionText() {
@ -419,7 +422,7 @@ void Cutscene::op_drawCaptionText() {
if (_id == 0x39 && strId == 0xFFFF) {
if ((_res->isDOS() && (_cmdPtr - _cmdPtrBak) == 0x10) || (_res->isAmiga() && (_cmdPtr - getCommandData()) == 0x9F3)) {
_frameDelay = 100;
setPalette();
updateScreen();
return;
}
}
@ -427,14 +430,14 @@ void Cutscene::op_drawCaptionText() {
const int h = 45 * _vid->_layerScale;
const int y = Video::GAMESCREEN_H * _vid->_layerScale - h;
memset(_pageC + y * _vid->_w, 0xC0, h * _vid->_w);
memset(_page1 + y * _vid->_w, 0xC0, h * _vid->_w);
memset(_page0 + y * _vid->_w, 0xC0, h * _vid->_w);
memset(_auxPage + y * _vid->_w, 0xC0, h * _vid->_w);
memset(_backPage + y * _vid->_w, 0xC0, h * _vid->_w);
memset(_frontPage + y * _vid->_w, 0xC0, h * _vid->_w);
if (strId != 0xFFFF) {
const uint8_t *str = _res->getCineString(strId);
if (str) {
drawText(0, 129, str, 0xEF, _page1, kTextJustifyAlign);
drawText(0, 129, str, 0xEF, _pageC, kTextJustifyAlign);
drawText(0, 129, str, 0xEF, _backPage, kTextJustifyAlign);
drawText(0, 129, str, 0xEF, _auxPage, kTextJustifyAlign);
}
}
}
@ -452,15 +455,15 @@ void Cutscene::op_skip3() {
void Cutscene::op_refreshAll() {
debug(DBG_CUT, "Cutscene::op_refreshAll()");
_frameDelay = 5;
setPalette();
swapLayers();
updateScreen();
clearBackPage();
_creditsSlowText = 0xFF;
op_handleKeys();
}
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.setLayer(_page1, _vid->_w);
_gfx.setLayer(_backPage, _vid->_w);
uint8_t numVertices = *data++;
if (numVertices & 0x80) {
int16_t x, y;
@ -631,7 +634,7 @@ void Cutscene::op_drawShapeScale() {
_hasAlphaColor = (verticesOffset & 0x4000) != 0;
uint8_t color = *shapeData++;
if (_clearScreen == 0) {
color += 0x10; // 2nd pal buf
color += 0x10; // 2nd paletter buffer
}
_primitiveColor = 0xC0 + color;
drawShapeScale(p, zoom, dx, dy, x, y, 0, 0);
@ -642,7 +645,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.setLayer(_page1, _vid->_w);
_gfx.setLayer(_backPage, _vid->_w);
uint8_t numVertices = *data++;
if (numVertices & 0x80) {
int16_t x, y, ix, iy;
@ -724,7 +727,7 @@ void Cutscene::drawShapeScaleRotate(const uint8_t *data, int16_t zoom, int16_t b
_gfx.drawPoint(_primitiveColor, &pt);
} else {
int16_t x, y, a, shape_last_x, shape_last_y;
Point tempVertices[40];
Point tempVertices[MAX_VERTICES];
_shape_cur_x = b + READ_BE_UINT16(data); data += 2;
x = _shape_cur_x;
_shape_cur_y = c + READ_BE_UINT16(data); data += 2;
@ -856,7 +859,7 @@ void Cutscene::op_drawShapeScaleRotate() {
_hasAlphaColor = (verticesOffset & 0x4000) != 0;
uint8_t color = *shapeData++;
if (_clearScreen == 0) {
color += 0x10; // 2nd pal buf
color += 0x10; // 2nd palette buffer
}
_primitiveColor = 0xC0 + color;
drawShapeScaleRotate(p, zoom, dx, dy, x, y, 0, 0);
@ -864,19 +867,86 @@ void Cutscene::op_drawShapeScaleRotate() {
}
}
void Cutscene::op_drawCreditsText() {
debug(DBG_CUT, "Cutscene::op_drawCreditsText()");
static const uint16_t memoSetPos[] = {
2, 0xffca, 0x0010, 2, 0xffcb, 0x000f, 2, 0xffcd, 0x000e, 2, 0xffd0, 0x000d, 2, 0xffd3, 0x000c, 2, 0xffd7, 0x000b,
2, 0xffd9, 0x000a, 2, 0xffdb, 0x0009, 2, 0xffdd, 0x0008, 2, 0xffdd, 0x0008, 2, 0xffdd, 0x0008, 2, 0xffdd, 0x0008,
2, 0xffdd, 0x0008, 2, 0xffdd, 0x0008, 2, 0xffdd, 0x0008, 2, 0xffdd, 0x0008, 4, 0xffe2, 0xfffe, 2, 0xffdd, 0x0008,
4, 0xffe2, 0xfffe, 2, 0xffdd, 0x0008, 4, 0xffe2, 0xfffe, 2, 0xffdd, 0x0008, 4, 0xffe2, 0xfffe, 2, 0xffdd, 0x0008,
2, 0xffdd, 0x0008, 2, 0xffdd, 0x0008, 2, 0xffdd, 0x0008, 2, 0xffdd, 0x0008, 2, 0xffdd, 0x0008, 2, 0xffdd, 0x0008,
2, 0xffdc, 0x0008, 2, 0xffda, 0x0008, 2, 0xffd6, 0x0009, 2, 0xffd2, 0x000b, 2, 0xffce, 0x000e, 2, 0xffc9, 0x0010,
2, 0xffc7, 0x0012, 2, 0xffc8, 0x0013, 2, 0xffca, 0x0015, 2, 0xffce, 0x0014, 2, 0xffd1, 0x0013, 2, 0xffd4, 0x0012,
2, 0xffd6, 0x0011, 2, 0xffd8, 0x0011, 2, 0xffd8, 0x0011, 2, 0xffd8, 0x0011, 2, 0xffd8, 0x0011, 2, 0xffd8, 0x0011,
2, 0xffd8, 0x0011, 2, 0xffd8, 0x0011, 4, 0xffdc, 0x0009, 2, 0xffd8, 0x0011, 4, 0xffdc, 0x0009, 2, 0xffd8, 0x0011,
4, 0xffdc, 0x0009, 2, 0xffd8, 0x0011, 2, 0xffd8, 0x0011, 2, 0xffd8, 0x0011, 2, 0xffd8, 0x0011, 2, 0xffd8, 0x0011,
2, 0xffd8, 0x0011, 2, 0xffd7, 0x0011, 2, 0xffd6, 0x0011, 2, 0xffd3, 0x0011, 2, 0xffcd, 0x0012, 2, 0xffc7, 0x0014,
2, 0xffc1, 0x0016
};
static bool _drawMemoSetShapes;
static uint32_t _memoSetOffset;
static void readSetPalette(const uint8_t *p, uint16_t offset, uint16_t *palette);
static int findSetPaletteColor(const uint16_t color, const uint16_t *paletteBuffer) {
int index = -1;
int currentSum = 0;
for (int l = 0; l < 32; ++l) {
if (color == paletteBuffer[l]) {
return l;
}
const int dr = ((color >> 8) & 15) - ((paletteBuffer[l] >> 8) & 15);
const int dg = ((color >> 4) & 15) - ((paletteBuffer[l] >> 4) & 15);
const int db = (color & 15) - (paletteBuffer[l] & 15);
const int sum = dr * dr + dg * dg + db * db;
if (index < 0 || sum < currentSum) {
currentSum = sum;
index = l;
}
}
return index;
}
void Cutscene::op_copyScreen() {
debug(DBG_CUT, "Cutscene::op_copyScreen()");
_creditsSlowText = 0xFF;
if (_textCurBuf == _textBuf) {
++_creditsTextCounter;
}
memcpy(_page1, _page0, _vid->_layerSize);
memcpy(_backPage, _frontPage, _vid->_layerSize);
_frameDelay = 10;
setPalette();
const bool drawMemoShapes = _drawMemoSetShapes && (_paletteNum == 19 || _paletteNum == 23) && (_memoSetOffset + 3) <= sizeof(memoSetPos);
if (drawMemoShapes) {
uint16_t paletteBuffer[32];
for (int i = 0; i < 32; ++i) {
paletteBuffer[i] = READ_BE_UINT16(_palBuf + i * 2);
}
uint16_t tempPalette[16];
readSetPalette(_memoSetShape2Data, 0x462, tempPalette);
uint8_t paletteLut[32];
for (int k = 0; k < 16; ++k) {
const int index = findSetPaletteColor(tempPalette[k], paletteBuffer);
paletteLut[k] = 0xC0 + index;
}
_gfx.setLayer(_backPage, _vid->_w);
drawSetShape(_memoSetShape2Data, 0, (int16_t)memoSetPos[_memoSetOffset + 1], (int16_t)memoSetPos[_memoSetOffset + 2], paletteLut);
_memoSetOffset += 3;
if (memoSetPos[_memoSetOffset] == 4) {
drawSetShape(_memoSetShape4Data, 0, (int16_t)memoSetPos[_memoSetOffset + 1], (int16_t)memoSetPos[_memoSetOffset + 2], paletteLut);
_memoSetOffset += 3;
}
}
updateScreen();
if (drawMemoShapes) {
SWAP(_frontPage, _backPage);
}
}
void Cutscene::op_drawStringAtPos() {
debug(DBG_CUT, "Cutscene::op_drawStringAtPos()");
void Cutscene::op_drawTextAtPos() {
debug(DBG_CUT, "Cutscene::op_drawTextAtPos()");
uint16_t strId = fetchNextCmdWord();
if (strId != 0xFFFF) {
int16_t x = (int8_t)fetchNextCmdByte() * 8;
@ -885,12 +955,12 @@ void Cutscene::op_drawStringAtPos() {
const uint8_t *str = _res->getCineString(strId & 0xFFF);
if (str) {
uint8_t color = 0xD0 + (strId >> 0xC);
drawText(x, y, str, color, _page1, kTextJustifyCenter);
drawText(x, y, str, color, _backPage, kTextJustifyCenter);
}
// 'voyage' - cutscene script redraws the string to refresh the screen
if (_id == 0x34 && (strId & 0xFFF) == 0x45) {
if ((_cmdPtr - _cmdPtrBak) == 0xA) {
_stub->copyRect(0, 0, _vid->_w, _vid->_h, _page1, _vid->_w);
_stub->copyRect(0, 0, _vid->_w, _vid->_h, _backPage, _vid->_w);
_stub->updateScreen(0);
} else {
_stub->sleep(15);
@ -995,6 +1065,10 @@ void Cutscene::mainLoop(uint16_t num) {
_polPtr = getPolygonData();
debug(DBG_CUT, "_baseOffset = %d offset = %d", _baseOffset, offset);
_paletteNum = -1;
_drawMemoSetShapes = (_id == kCineMemo && g_options.restore_memo_cutscene);
_memoSetOffset = 0;
while (!_stub->_pi.quit && !_interrupted && !_stop) {
uint8_t op = fetchNextCmdByte();
debug(DBG_CUT, "Cutscene::play() opcode = 0x%X (%d)", op, (op >> 2));
@ -1071,9 +1145,9 @@ void Cutscene::unload() {
}
void Cutscene::prepare() {
_page0 = _vid->_frontLayer;
_page1 = _vid->_tempLayer;
_pageC = _vid->_tempLayer2;
_frontPage = _vid->_frontLayer;
_backPage = _vid->_tempLayer;
_auxPage = _vid->_tempLayer2;
_stub->_pi.dirMask = 0;
_stub->_pi.enter = false;
_stub->_pi.space = false;
@ -1138,9 +1212,9 @@ 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, _vid->_w);
memset(_backPage, 0xC0, _vid->_layerSize);
drawText(0, y, (const uint8_t *)str, 0xC1, _backPage, kTextJustifyAlign);
_stub->copyRect(0, 0, _vid->_w, _vid->_h, _backPage, _vid->_w);
_stub->updateScreen(0);
while (!_stub->_pi.quit) {
@ -1236,7 +1310,7 @@ static void readSetPalette(const uint8_t *p, uint16_t offset, uint16_t *palette)
}
}
void Cutscene::drawSetShape(const uint8_t *p, uint16_t offset, int x, int y, uint8_t *paletteLut) {
void Cutscene::drawSetShape(const uint8_t *p, uint16_t offset, int x, int y, const uint8_t *paletteLut) {
const int count = READ_BE_UINT16(p + offset); offset += 2;
for (int i = 0; i < count - 1; ++i) {
offset += 5; // shape_marker
@ -1303,14 +1377,14 @@ void Cutscene::playSet(const uint8_t *p, int offset) {
}
prepare();
_gfx.setLayer(_page1, _vid->_w);
_gfx.setLayer(_backPage, _vid->_w);
offset = 10;
const int frames = READ_BE_UINT16(p + offset); offset += 2;
for (int i = 0; i < frames && !_stub->_pi.quit && !_interrupted; ++i) {
const uint32_t timestamp = _stub->getTimeStamp();
memset(_page1, 0xC0, _vid->_layerSize);
memset(_backPage, 0xC0, _vid->_layerSize);
const int shapeBg = READ_BE_UINT16(p + offset); offset += 2;
const int count = READ_BE_UINT16(p + offset); offset += 2;
@ -1357,7 +1431,7 @@ void Cutscene::playSet(const uint8_t *p, int offset) {
_stub->setPaletteEntry(0xC0 + j, &c);
}
_stub->copyRect(0, 0, _vid->_w, _vid->_h, _page1, _vid->_w);
_stub->copyRect(0, 0, _vid->_w, _vid->_h, _backPage, _vid->_w);
_stub->updateScreen(0);
const int diff = 6 * TIMER_SLICE - (_stub->getTimeStamp() - timestamp);
_stub->sleep((diff < 16) ? 16 : diff);

View File

@ -18,6 +18,7 @@ struct Cutscene {
typedef void (Cutscene::*OpcodeStub)();
enum {
MAX_VERTICES = 128,
NUM_OPCODES = 15,
TIMER_SLICE = 15
};
@ -28,6 +29,10 @@ struct Cutscene {
kTextJustifyCenter = 2,
};
enum {
kCineMemo = 48
};
struct SetShape {
uint16_t offset;
uint16_t size;
@ -54,6 +59,8 @@ struct Cutscene {
static const Text _frTextsTable[];
static const Text _enTextsTable[];
static const uint8_t _caillouSetData[];
static const uint8_t _memoSetShape2Data[];
static const uint8_t _memoSetShape4Data[];
Graphics _gfx;
Resource *_res;
@ -77,7 +84,7 @@ struct Cutscene {
uint32_t _rotMat[4];
uint8_t _primitiveColor;
uint8_t _clearScreen;
Point _vertices[0x80];
Point _vertices[MAX_VERTICES];
bool _hasAlphaColor;
uint8_t _varKey;
int16_t _shape_ix;
@ -102,7 +109,7 @@ struct Cutscene {
uint8_t _creditsTextPosX;
uint8_t _creditsTextPosY;
int16_t _creditsTextCounter;
uint8_t *_page0, *_page1, *_pageC;
uint8_t *_frontPage, *_backPage, *_auxPage;
Cutscene(Resource *res, SystemStub *stub, Video *vid);
@ -112,11 +119,11 @@ struct Cutscene {
void sync();
void copyPalette(const uint8_t *pal, uint16_t num);
void updatePalette();
void setPalette();
void updateScreen();
void setRotationTransform(uint16_t a, uint16_t b, uint16_t c);
uint16_t findTextSeparators(const uint8_t *p, int len);
void drawText(int16_t x, int16_t y, const uint8_t *p, uint16_t color, uint8_t *page, int textJustify);
void swapLayers();
void clearBackPage();
void drawCreditsText();
void drawProtectionShape(uint8_t shapeNum, int16_t zoom);
void drawShape(const uint8_t *data, int16_t x, int16_t y);
@ -134,8 +141,8 @@ struct Cutscene {
void op_refreshAll();
void op_drawShapeScale();
void op_drawShapeScaleRotate();
void op_drawCreditsText();
void op_drawStringAtPos();
void op_copyScreen();
void op_drawTextAtPos();
void op_handleKeys();
uint8_t fetchNextCmdByte();
@ -148,7 +155,7 @@ struct Cutscene {
void playText(const char *str);
void play();
void drawSetShape(const uint8_t *p, uint16_t offset, int x, int y, uint8_t *paletteLut);
void drawSetShape(const uint8_t *p, uint16_t offset, int x, int y, const uint8_t *paletteLut);
void playSet(const uint8_t *p, int offset);
};

View File

@ -365,7 +365,7 @@ void Game::resetGameState() {
_deathCutsceneCounter = 0;
_saveStateCompleted = false;
_loadMap = true;
pge_resetGroups();
pge_resetMessages();
_blinkingConradCounter = 0;
_pge_processOBJ = false;
_pge_opTempVar1 = 0;
@ -1699,9 +1699,6 @@ void Game::loadLevelData() {
}
_cut._id = lvl->cutscene_id;
if (_res._isDemo && _currentLevel == 5) { // PC demo does not include TELEPORT.*
_cut._id = 0xFFFF;
}
_curMonsterNum = 0xFFFF;
_curMonsterFrame = 0;
@ -1741,7 +1738,7 @@ void Game::loadLevelData() {
_pge_liveTable1[pge->room_location] = pge;
}
}
pge_resetGroups();
pge_resetMessages();
_validSaveState = false;
_mix.playMusic(Mixer::MUSIC_TRACK + lvl->track);
@ -1811,8 +1808,10 @@ void Game::playSound(uint8_t num, uint8_t softVol) {
} else if (num >= 68 && num <= 75) {
// in-game music
_mix.playMusic(num);
} else if (num == 76) {
// metro
} else if (num == 77) {
// triggered when Conrad reaches a platform
// triggered when Conrad draw his gun
} else {
warning("Unknown sound num %d", num);
}

36
game.h
View File

@ -145,9 +145,9 @@ struct Game {
// pieges
bool _pge_playAnimSound;
GroupPGE _pge_groups[256];
GroupPGE *_pge_groupsTable[256];
GroupPGE *_pge_nextFreeGroup;
MessagePGE _pge_messages[256];
MessagePGE *_pge_messagesTable[256]; // indexed by pge number
MessagePGE *_pge_nextFreeMessage;
LivePGE *_pge_liveTable2[256]; // active pieges list (index = pge number)
LivePGE *_pge_liveTable1[256]; // pieges list by room (index = room)
LivePGE _pgeLive[256];
@ -160,12 +160,12 @@ struct Game {
uint16_t _pge_compareVar1;
uint16_t _pge_compareVar2;
void pge_resetGroups();
void pge_removeFromGroup(uint8_t idx);
int pge_isInGroup(LivePGE *pge_dst, uint16_t group_id, uint16_t counter);
void pge_resetMessages();
void pge_clearMessages(uint8_t pge_index);
int pge_hasMessageData(LivePGE *pge, uint16_t msg_num, uint16_t counter) const;
void pge_loadForCurrentLevel(uint16_t idx);
void pge_process(LivePGE *pge);
void pge_setupNextAnimFrame(LivePGE *pge, GroupPGE *le);
void pge_setupNextAnimFrame(LivePGE *pge, MessagePGE *le);
void pge_playAnimSound(LivePGE *pge, uint16_t arg2);
void pge_setupAnim(LivePGE *pge);
int pge_execute(LivePGE *live_pge, InitPGE *init_pge, const Object *obj);
@ -208,11 +208,11 @@ struct Game {
int pge_op_collides0o0u(ObjectOpcodeArgs *args);
int pge_op_collides2o2u(ObjectOpcodeArgs *args);
int pge_op_collides2u2o(ObjectOpcodeArgs *args);
int pge_op_isInGroup(ObjectOpcodeArgs *args);
int pge_op_updateGroup0(ObjectOpcodeArgs *args);
int pge_op_updateGroup1(ObjectOpcodeArgs *args);
int pge_op_updateGroup2(ObjectOpcodeArgs *args);
int pge_op_updateGroup3(ObjectOpcodeArgs *args);
int pge_hasPiegeSentMessage(ObjectOpcodeArgs *args);
int pge_op_sendMessageData0(ObjectOpcodeArgs *args);
int pge_op_sendMessageData1(ObjectOpcodeArgs *args);
int pge_op_sendMessageData2(ObjectOpcodeArgs *args);
int pge_op_sendMessageData3(ObjectOpcodeArgs *args);
int pge_op_isPiegeDead(ObjectOpcodeArgs *args);
int pge_op_collides1u2o(ObjectOpcodeArgs *args);
int pge_op_collides1u1o(ObjectOpcodeArgs *args);
@ -230,10 +230,10 @@ struct Game {
int pge_op_isInpMod(ObjectOpcodeArgs *args);
int pge_op_setCollisionState1(ObjectOpcodeArgs *args);
int pge_op_setCollisionState0(ObjectOpcodeArgs *args);
int pge_op_isInGroup1(ObjectOpcodeArgs *args);
int pge_op_isInGroup2(ObjectOpcodeArgs *args);
int pge_op_isInGroup3(ObjectOpcodeArgs *args);
int pge_op_isInGroup4(ObjectOpcodeArgs *args);
int pge_hasMessageData0(ObjectOpcodeArgs *args);
int pge_hasMessageData1(ObjectOpcodeArgs *args);
int pge_hasMessageData2(ObjectOpcodeArgs *args);
int pge_hasMessageData3(ObjectOpcodeArgs *args);
int pge_o_unk0x3C(ObjectOpcodeArgs *args);
int pge_o_unk0x3D(ObjectOpcodeArgs *args);
int pge_op_setPiegeCounter(ObjectOpcodeArgs *args);
@ -280,7 +280,7 @@ struct Game {
int pge_op_setCollisionState2(ObjectOpcodeArgs *args);
int pge_op_saveState(ObjectOpcodeArgs *args);
int pge_o_unk0x6A(ObjectOpcodeArgs *args);
int pge_op_isInGroupSlice(ObjectOpcodeArgs *args);
int pge_isToggleable(ObjectOpcodeArgs *args);
int pge_o_unk0x6C(ObjectOpcodeArgs *args);
int pge_op_isCollidingObject(ObjectOpcodeArgs *args);
int pge_o_unk0x6E(ObjectOpcodeArgs *args);
@ -319,7 +319,7 @@ struct Game {
void pge_addToInventory(LivePGE *pge1, LivePGE *pge2, LivePGE *pge3);
int pge_updateCollisionState(LivePGE *pge, int16_t pge_dy, uint8_t var8);
int pge_ZOrder(LivePGE *pge, int16_t num, pge_ZOrderCallback compare, uint16_t unk);
void pge_updateGroup(uint8_t idx, uint8_t unk1, int16_t unk2);
void pge_sendMessage(uint8_t src_pge_index, uint8_t dst_pge_index, int16_t num);
void pge_removeFromInventory(LivePGE *pge1, LivePGE *pge2, LivePGE *pge3);
int pge_ZOrderByAnimY(LivePGE *pge1, LivePGE *pge2, uint8_t comp, uint8_t comp2);
int pge_ZOrderByAnimYIfType(LivePGE *pge1, LivePGE *pge2, uint8_t comp, uint8_t comp2);

View File

@ -136,6 +136,7 @@ struct Options {
bool play_serrure_cutscene;
bool play_carte_cutscene;
bool play_gamesaved_sound;
bool restore_memo_cutscene;
};
struct Color {
@ -171,7 +172,7 @@ struct InitPGE {
int16_t pos_y;
uint16_t obj_node_number;
uint16_t life;
int16_t counter_values[4]; // messages
int16_t counter_values[4]; // data
uint8_t object_type;
uint8_t init_room;
uint8_t room_location;
@ -181,7 +182,7 @@ struct InitPGE {
uint8_t object_id;
uint8_t skill;
uint8_t mirror_x;
uint8_t flags;
uint8_t flags; // 1:xflip 4:active
uint8_t unk1C; // collidable, collision_data_len
uint16_t text_num;
};
@ -206,10 +207,10 @@ struct LivePGE {
InitPGE *init_PGE;
};
struct GroupPGE {
GroupPGE *next_entry;
uint16_t index;
uint16_t group_id;
struct MessagePGE {
MessagePGE *next_entry;
uint16_t index; // src_pge
uint16_t msg_num;
};
struct Object {

View File

@ -99,6 +99,7 @@ static void initOptions() {
g_options.play_serrure_cutscene = false;
g_options.play_carte_cutscene = false;
g_options.play_gamesaved_sound = false;
g_options.restore_memo_cutscene = true;
// read configuration file
struct {
const char *name;
@ -119,6 +120,7 @@ static void initOptions() {
{ "play_serrure_cutscene", &g_options.play_serrure_cutscene },
{ "play_carte_cutscene", &g_options.play_carte_cutscene },
{ "play_gamesaved_sound", &g_options.play_gamesaved_sound },
{ "restore_memo_cutscene", &g_options.restore_memo_cutscene },
{ 0, 0 }
};
static const char *filename = "rs.cfg";

View File

@ -91,7 +91,7 @@ void Mixer::playMusic(int num) {
debug(DBG_SND, "Mixer::playMusic(%d)", num);
if (num > MUSIC_TRACK && num != _musicTrack) {
if (_ogg.playTrack(num - MUSIC_TRACK)) {
_musicType = MT_OGG;
_backgroundMusicType = _musicType = MT_OGG;
_musicTrack = num;
return;
}

View File

@ -40,6 +40,7 @@ struct ModPlayer_impl {
if (data) {
f->read(data, size);
_mf = ModPlug_Load(data, size);
free(data);
}
return _mf != 0;
}

200
piege.cpp
View File

@ -10,46 +10,47 @@
#include "systemstub.h"
#include "util.h"
void Game::pge_resetGroups() {
memset(_pge_groupsTable, 0, sizeof(_pge_groupsTable));
GroupPGE *le = &_pge_groups[0];
_pge_nextFreeGroup = le;
void Game::pge_resetMessages() {
memset(_pge_messagesTable, 0, sizeof(_pge_messagesTable));
MessagePGE *le = &_pge_messages[0];
_pge_nextFreeMessage = le;
int n = 0xFF;
while (n--) {
le->next_entry = le + 1;
le->index = 0;
le->group_id = 0;
le->msg_num = 0;
++le;
}
le->next_entry = 0;
le->index = 0;
le->group_id = 0;
le->msg_num = 0;
}
void Game::pge_removeFromGroup(uint8_t idx) {
GroupPGE *le = _pge_groupsTable[idx];
void Game::pge_clearMessages(uint8_t pge_index) {
MessagePGE *le = _pge_messagesTable[pge_index];
if (le) {
_pge_groupsTable[idx] = 0;
GroupPGE *next = _pge_nextFreeGroup;
_pge_messagesTable[pge_index] = 0;
MessagePGE *next = _pge_nextFreeMessage;
while (le) {
GroupPGE *cur = le->next_entry;
MessagePGE *cur = le->next_entry;
le->next_entry = next;
le->index = 0;
le->group_id = 0;
le->msg_num = 0;
next = le;
le = cur;
}
_pge_nextFreeGroup = next;
_pge_nextFreeMessage = next;
}
}
int Game::pge_isInGroup(LivePGE *pge_dst, uint16_t group_id, uint16_t counter) {
int Game::pge_hasMessageData(LivePGE *pge, uint16_t msg_num, uint16_t counter) const {
assert(counter >= 1 && counter <= 4);
uint16_t c = pge_dst->init_PGE->counter_values[counter - 1];
GroupPGE *le = _pge_groupsTable[pge_dst->index];
uint16_t pge_src_index = pge->init_PGE->counter_values[counter - 1];
const MessagePGE *le = _pge_messagesTable[pge->index];
while (le) {
if (le->group_id == group_id && le->index == c)
if (le->msg_num == msg_num && le->index == pge_src_index) {
return 1;
}
le = le->next_entry;
}
return 0;
@ -117,7 +118,7 @@ void Game::pge_process(LivePGE *pge) {
_pge_playAnimSound = true;
_pge_currentPiegeFacingDir = (pge->flags & 1) != 0;
_pge_currentPiegeRoom = pge->room_location;
GroupPGE *le = _pge_groupsTable[pge->index];
MessagePGE *le = _pge_messagesTable[pge->index];
if (le) {
pge_setupNextAnimFrame(pge, le);
}
@ -129,7 +130,7 @@ void Game::pge_process(LivePGE *pge) {
Object *obj = &on->objects[pge->first_obj_number];
while (1) {
if (obj->type != pge->obj_type) {
pge_removeFromGroup(pge->index);
pge_clearMessages(pge->index);
return;
}
uint16_t _ax = pge_execute(pge, init_pge, obj);
@ -137,7 +138,7 @@ void Game::pge_process(LivePGE *pge) {
if (_currentLevel == 6 && (_currentRoom == 50 || _currentRoom == 51)) {
if (pge->index == 79 && _ax == 0xFFFF && obj->opcode1 == 0x60 && obj->opcode2 == 0 && obj->opcode3 == 0) {
if (col_getGridPos(&_pgeLive[79], 0) == col_getGridPos(&_pgeLive[0], 0)) {
pge_updateGroup(79, 0, 4);
pge_sendMessage(79, 0, 4);
}
}
}
@ -156,37 +157,37 @@ void Game::pge_process(LivePGE *pge) {
}
pge_setupAnim(pge);
++pge->anim_seq;
pge_removeFromGroup(pge->index);
pge_clearMessages(pge->index);
}
void Game::pge_setupNextAnimFrame(LivePGE *pge, GroupPGE *le) {
void Game::pge_setupNextAnimFrame(LivePGE *pge, MessagePGE *le) {
InitPGE *init_pge = pge->init_PGE;
assert(init_pge->obj_node_number < _res._numObjectNodes);
ObjectNode *on = _res._objectNodesMap[init_pge->obj_node_number];
Object *obj = &on->objects[pge->first_obj_number];
int i = pge->first_obj_number;
while (i < on->last_obj_number && pge->obj_type == obj->type) {
GroupPGE *next_le = le;
MessagePGE *next_le = le;
while (next_le) {
uint16_t groupId = next_le->group_id;
if (obj->opcode2 == 0x6B) { // pge_op_isInGroupSlice
uint16_t msgNum = next_le->msg_num;
if (obj->opcode2 == 0x6B) { // pge_isToggleable
if (obj->opcode_arg2 == 0) {
if (groupId == 1 || groupId == 2) goto set_anim;
if (msgNum == 1 || msgNum == 2) goto set_anim;
}
if (obj->opcode_arg2 == 1) {
if (groupId == 3 || groupId == 4) goto set_anim;
if (msgNum == 3 || msgNum == 4) goto set_anim;
}
} else if (groupId == obj->opcode_arg2) {
} else if (msgNum == obj->opcode_arg2) {
if (obj->opcode2 == 0x22 || obj->opcode2 == 0x6F) goto set_anim;
}
if (obj->opcode1 == 0x6B) { // pge_op_isInGroupSlice
if (obj->opcode1 == 0x6B) { // pge_isToggleable
if (obj->opcode_arg1 == 0) {
if (groupId == 1 || groupId == 2) goto set_anim;
if (msgNum == 1 || msgNum == 2) goto set_anim;
}
if (obj->opcode_arg1 == 1) {
if (groupId == 3 || groupId == 4) goto set_anim;
if (msgNum == 3 || msgNum == 4) goto set_anim;
}
} else if (groupId == obj->opcode_arg1) {
} else if (msgNum == obj->opcode_arg1) {
if (obj->opcode1 == 0x22 || obj->opcode1 == 0x6F) goto set_anim;
}
next_le = next_le->next_entry;
@ -796,10 +797,10 @@ int Game::pge_op_collides2u2o(ObjectOpcodeArgs *args) {
return 0;
}
int Game::pge_op_isInGroup(ObjectOpcodeArgs *args) {
GroupPGE *le = _pge_groupsTable[args->pge->index];
int Game::pge_hasPiegeSentMessage(ObjectOpcodeArgs *args) {
MessagePGE *le = _pge_messagesTable[args->pge->index];
while (le) {
if (le->group_id == args->a) {
if (le->msg_num == args->a) {
return 0xFFFF;
}
le = le->next_entry;
@ -807,27 +808,27 @@ int Game::pge_op_isInGroup(ObjectOpcodeArgs *args) {
return 0;
}
int Game::pge_op_updateGroup0(ObjectOpcodeArgs *args) {
int Game::pge_op_sendMessageData0(ObjectOpcodeArgs *args) {
LivePGE *pge = args->pge;
pge_updateGroup(pge->index, pge->init_PGE->counter_values[0], args->a);
pge_sendMessage(pge->index, pge->init_PGE->counter_values[0], args->a);
return 0xFFFF;
}
int Game::pge_op_updateGroup1(ObjectOpcodeArgs *args) {
int Game::pge_op_sendMessageData1(ObjectOpcodeArgs *args) {
LivePGE *pge = args->pge;
pge_updateGroup(pge->index, pge->init_PGE->counter_values[1], args->a);
pge_sendMessage(pge->index, pge->init_PGE->counter_values[1], args->a);
return 0xFFFF;
}
int Game::pge_op_updateGroup2(ObjectOpcodeArgs *args) {
int Game::pge_op_sendMessageData2(ObjectOpcodeArgs *args) {
LivePGE *pge = args->pge;
pge_updateGroup(pge->index, pge->init_PGE->counter_values[2], args->a);
pge_sendMessage(pge->index, pge->init_PGE->counter_values[2], args->a);
return 0xFFFF;
}
int Game::pge_op_updateGroup3(ObjectOpcodeArgs *args) {
int Game::pge_op_sendMessageData3(ObjectOpcodeArgs *args) {
LivePGE *pge = args->pge;
pge_updateGroup(pge->index, pge->init_PGE->counter_values[3], args->a);
pge_sendMessage(pge->index, pge->init_PGE->counter_values[3], args->a);
return 0xFFFF;
}
@ -888,7 +889,7 @@ int Game::pge_op_nop(ObjectOpcodeArgs *args) {
int Game::pge_op_pickupObject(ObjectOpcodeArgs *args) {
LivePGE *pge = col_findPiege(args->pge, 3);
if (pge) {
pge_updateGroup(args->pge->index, pge->index, args->a);
pge_sendMessage(args->pge->index, pge->index, args->a);
return 0xFFFF;
}
return 0;
@ -908,7 +909,7 @@ int Game::pge_op_copyPiege(ObjectOpcodeArgs *args) {
dst->pos_y = src->pos_y;
dst->room_location = src->room_location;
dst->flags &= 0xFE;
dst->flags &= ~1;
if (src->flags & 1) {
dst->flags |= 1;
}
@ -918,7 +919,7 @@ int Game::pge_op_copyPiege(ObjectOpcodeArgs *args) {
int Game::pge_op_removeItemFromInventory(ObjectOpcodeArgs *args) {
if (args->pge->current_inventory_PGE != 0xFF) {
pge_updateGroup(args->pge->index, args->pge->current_inventory_PGE, args->a);
pge_sendMessage(args->pge->index, args->pge->current_inventory_PGE, args->a);
}
return 1;
}
@ -960,20 +961,20 @@ int Game::pge_op_setCollisionState0(ObjectOpcodeArgs *args) {
return pge_updateCollisionState(args->pge, args->a, 0);
}
int Game::pge_op_isInGroup1(ObjectOpcodeArgs *args) {
return pge_isInGroup(args->pge, args->a, 1);
int Game::pge_hasMessageData0(ObjectOpcodeArgs *args) {
return pge_hasMessageData(args->pge, args->a, 1);
}
int Game::pge_op_isInGroup2(ObjectOpcodeArgs *args) {
return pge_isInGroup(args->pge, args->a, 2);
int Game::pge_hasMessageData1(ObjectOpcodeArgs *args) {
return pge_hasMessageData(args->pge, args->a, 2);
}
int Game::pge_op_isInGroup3(ObjectOpcodeArgs *args) {
return pge_isInGroup(args->pge, args->a, 3);
int Game::pge_hasMessageData2(ObjectOpcodeArgs *args) {
return pge_hasMessageData(args->pge, args->a, 3);
}
int Game::pge_op_isInGroup4(ObjectOpcodeArgs *args) {
return pge_isInGroup(args->pge, args->a, 4);
int Game::pge_hasMessageData3(ObjectOpcodeArgs *args) {
return pge_hasMessageData(args->pge, args->a, 4);
}
int Game::pge_o_unk0x3C(ObjectOpcodeArgs *args) {
@ -1170,7 +1171,7 @@ int Game::pge_o_unk0x47(ObjectOpcodeArgs *args) {
int Game::pge_o_unk0x48(ObjectOpcodeArgs *args) {
LivePGE *pge = col_findPiege(&_pgeLive[0], args->pge->init_PGE->counter_values[0]);
if (pge && pge->life == args->pge->life) {
pge_updateGroup(args->pge->index, pge->index, args->a);
pge_sendMessage(args->pge->index, pge->index, args->a);
return 1;
}
return 0;
@ -1364,9 +1365,9 @@ int Game::pge_o_unk0x5F(ObjectOpcodeArgs *args) {
}
int Game::pge_op_findAndCopyPiege(ObjectOpcodeArgs *args) {
GroupPGE *le = _pge_groupsTable[args->pge->index];
MessagePGE *le = _pge_messagesTable[args->pge->index];
while (le) {
if (le->group_id == args->a) {
if (le->msg_num == args->a) {
args->a = le->index;
args->b = 0;
pge_op_copyPiege(args);
@ -1429,7 +1430,7 @@ int Game::pge_op_setCollisionState2(ObjectOpcodeArgs *args) {
int Game::pge_op_saveState(ObjectOpcodeArgs *args) {
_saveStateCompleted = true;
_validSaveState = saveGameState(kIngameSaveSlot);
if (_validSaveState and g_options.play_gamesaved_sound) {
if (_validSaveState && g_options.play_gamesaved_sound) {
_mix.play(Resource::_gameSavedSoundData, Resource::_gameSavedSoundLen, 8000, Mixer::MAX_VOLUME);
}
return 0xFFFF;
@ -1544,20 +1545,20 @@ loc_0_15446:
return 0;
}
int Game::pge_op_isInGroupSlice(ObjectOpcodeArgs *args) {
int Game::pge_isToggleable(ObjectOpcodeArgs *args) {
LivePGE *pge = args->pge;
GroupPGE *le = _pge_groupsTable[pge->index];
MessagePGE *le = _pge_messagesTable[pge->index];
if (le) {
if (args->a == 0) {
do {
if (le->group_id == 1 || le->group_id == 2) {
if (le->msg_num == 1 || le->msg_num == 2) {
return 1;
}
le = le->next_entry;
} while (le);
} else {
do {
if (le->group_id == 3 || le->group_id == 4) {
if (le->msg_num == 3 || le->msg_num == 4) {
return 1;
}
le = le->next_entry;
@ -1571,7 +1572,7 @@ int Game::pge_o_unk0x6C(ObjectOpcodeArgs *args) {
LivePGE *pge = col_findPiege(&_pgeLive[0], args->pge->init_PGE->counter_values[0]);
if (pge) {
if (pge->life <= args->pge->life) {
pge_updateGroup(args->pge->index, pge->index, args->a);
pge_sendMessage(args->pge->index, pge->index, args->a);
return 1;
}
}
@ -1589,9 +1590,9 @@ int Game::pge_op_isCollidingObject(ObjectOpcodeArgs *args) {
// elevator
int Game::pge_o_unk0x6E(ObjectOpcodeArgs *args) {
GroupPGE *le = _pge_groupsTable[args->pge->index];
MessagePGE *le = _pge_messagesTable[args->pge->index];
while (le) {
if (args->a == le->group_id) {
if (args->a == le->msg_num) {
pge_updateInventory(&_pgeLive[le->index], args->pge);
return 0xFFFF;
}
@ -1603,10 +1604,10 @@ int Game::pge_o_unk0x6E(ObjectOpcodeArgs *args) {
int Game::pge_o_unk0x6F(ObjectOpcodeArgs *args) {
LivePGE *pge = args->pge;
GroupPGE *le = _pge_groupsTable[pge->index];
MessagePGE *le = _pge_messagesTable[pge->index];
while (le) {
if (args->a == le->group_id) {
pge_updateGroup(pge->index, le->index, 0xC);
if (args->a == le->msg_num) {
pge_sendMessage(pge->index, le->index, 0xC);
return 1;
}
le = le->next_entry;
@ -1617,7 +1618,7 @@ int Game::pge_o_unk0x6F(ObjectOpcodeArgs *args) {
int Game::pge_o_unk0x70(ObjectOpcodeArgs *args) {
uint8_t pge_num = args->pge->current_inventory_PGE;
while (pge_num != 0xFF) {
pge_updateGroup(args->pge->index, _pgeLive[pge_num].index, args->a);
pge_sendMessage(args->pge->index, _pgeLive[pge_num].index, args->a);
pge_num = _pgeLive[pge_num].next_inventory_PGE;
}
return 1;
@ -1626,9 +1627,9 @@ int Game::pge_o_unk0x70(ObjectOpcodeArgs *args) {
// elevator
int Game::pge_o_unk0x71(ObjectOpcodeArgs *args) {
LivePGE *pge = args->pge;
GroupPGE *le = _pge_groupsTable[pge->index];
MessagePGE *le = _pge_messagesTable[pge->index];
while (le) {
if (le->group_id == args->a) {
if (le->msg_num == args->a) {
pge_reorderInventory(args->pge);
return 1;
}
@ -1785,7 +1786,6 @@ int Game::pge_op_isFacingConrad(ObjectOpcodeArgs *args) {
return 0xFFFF;
}
}
}
}
return 0;
@ -1817,7 +1817,7 @@ int Game::pge_o_unk0x7C(ObjectOpcodeArgs *args) {
}
}
if (pge != 0) {
pge_updateGroup(args->pge->index, pge->index, args->a);
pge_sendMessage(args->pge->index, pge->index, args->a);
}
return 0;
}
@ -1876,13 +1876,21 @@ int Game::pge_op_setPiegePosModX(ObjectOpcodeArgs *args) {
return 0xFFFF;
}
// taxi, level4
// taxi and teleporter
int Game::pge_op_changeRoom(ObjectOpcodeArgs *args) {
InitPGE *init_pge_1 = args->pge->init_PGE;
assert(args->a >= 0 && args->a < 3);
const int16_t _ax = init_pge_1->counter_values[args->a];
if (_ax == 0 && !g_options.bypass_protection) {
warning("pge_op_changeRoom(): protection check");
if (_ax == 0 && !g_options.bypass_protection && !g_options.use_words_protection && !_res.isMac()) {
if (!handleProtectionScreenShape()) {
warning("Game::pge_op_changeRoom() protection check failed");
// when protection check fails, the changeRoom opcode is disabled,
// rendering the teleporter unusable.
//
// _pge_opcodeTable[0x82] = &Game::pge_op_nop;
// _pge_opTempVar1 = 0xFFFF;
// return;
}
}
const int16_t _bx = init_pge_1->counter_values[args->a + 1];
LivePGE *live_pge_1 = &_pgeLive[_bx];
@ -1897,7 +1905,7 @@ int Game::pge_op_changeRoom(ObjectOpcodeArgs *args) {
InitPGE *init_pge_2 = live_pge_2->init_PGE;
init_pge_1 = live_pge_1->init_PGE;
if (init_pge_2->obj_node_number == init_pge_1->obj_node_number) {
live_pge_2->flags &= 0xFE;
live_pge_2->flags &= ~1;
if (live_pge_1->flags & 1) {
live_pge_2->flags |= 1;
}
@ -2061,7 +2069,6 @@ int Game::pge_updateCollisionState(LivePGE *pge, int16_t pge_dy, uint8_t var8) {
CollisionSlot2 *slot1 = _col_slots2Next;
int16_t i = 255;
pge_pos_x = i;
if (_pge_currentPiegeFacingDir) {
i = pge_unk1C - 1;
grid_data -= i;
@ -2071,7 +2078,6 @@ int Game::pge_updateCollisionState(LivePGE *pge, int16_t pge_dy, uint8_t var8) {
slot1->data_size = pge_unk1C - 1;
assert(pge_unk1C < 0x70);
memset(grid_data, var8, pge_unk1C);
grid_data += pge_unk1C;
return 1;
} else {
++i;
@ -2126,36 +2132,36 @@ int Game::pge_ZOrder(LivePGE *pge, int16_t num, pge_ZOrderCallback compare, uint
return 0;
}
void Game::pge_updateGroup(uint8_t idx, uint8_t unk1, int16_t unk2) {
debug(DBG_GAME, "Game::pge_updateGroup() idx=0x%X unk1=0x%X unk2=0x%X", idx, unk1, unk2);
LivePGE *pge = &_pgeLive[unk1];
void Game::pge_sendMessage(uint8_t src_pge_index, uint8_t dst_pge_index, int16_t num) {
debug(DBG_GAME, "Game::pge_sendMessage() src=0x%X dst=0x%X num=0x%X", src_pge_index, dst_pge_index, num);
LivePGE *pge = &_pgeLive[dst_pge_index];
if (!(pge->flags & 4)) {
if (!(pge->init_PGE->flags & 1)) {
return;
}
pge->flags |= 4;
_pge_liveTable2[unk1] = pge;
_pge_liveTable2[dst_pge_index] = pge;
}
if (unk2 <= 4) {
if (num <= 4) {
uint8_t pge_room = pge->room_location;
pge = &_pgeLive[idx];
pge = &_pgeLive[src_pge_index];
if (pge_room != pge->room_location) {
return;
}
if (unk1 == 0 && _blinkingConradCounter != 0) {
if (dst_pge_index == 0 && _blinkingConradCounter != 0) {
return;
}
// XXX
}
GroupPGE *le = _pge_nextFreeGroup;
MessagePGE *le = _pge_nextFreeMessage;
if (le) {
// append to the list
_pge_nextFreeGroup = le->next_entry;
GroupPGE *_ax = _pge_groupsTable[unk1];
_pge_groupsTable[unk1] = le;
le->next_entry = _ax;
le->index = idx;
le->group_id = unk2;
_pge_nextFreeMessage = le->next_entry;
MessagePGE *next = _pge_messagesTable[dst_pge_index];
_pge_messagesTable[dst_pge_index] = le;
le->next_entry = next;
le->index = src_pge_index;
le->msg_num = num;
}
}
@ -2190,7 +2196,7 @@ int Game::pge_ZOrderByAnimYIfType(LivePGE *pge1, LivePGE *pge2, uint8_t comp, ui
int Game::pge_ZOrderIfIndex(LivePGE *pge1, LivePGE *pge2, uint8_t comp, uint8_t comp2) {
if (pge1->index != comp2) {
pge_updateGroup(pge2->index, pge1->index, comp);
pge_sendMessage(pge2->index, pge1->index, comp);
return 1;
}
return 0;
@ -2198,7 +2204,7 @@ int Game::pge_ZOrderIfIndex(LivePGE *pge1, LivePGE *pge2, uint8_t comp, uint8_t
int Game::pge_ZOrderByIndex(LivePGE *pge1, LivePGE *pge2, uint8_t comp, uint8_t comp2) {
if (pge1 != pge2) {
pge_updateGroup(pge2->index, pge1->index, comp);
pge_sendMessage(pge2->index, pge1->index, comp);
_pge_compareVar1 = 0xFFFF;
}
return 0;
@ -2221,7 +2227,7 @@ int Game::pge_ZOrderIfDifferentDirection(LivePGE *pge1, LivePGE *pge2, uint8_t c
if (pge1 != pge2) {
if ((pge1->flags & 1) != (pge2->flags & 1)) {
_pge_compareVar1 = 1;
pge_updateGroup(pge2->index, pge1->index, comp);
pge_sendMessage(pge2->index, pge1->index, comp);
if (pge2->index == 0) {
return 0xFFFF;
}
@ -2234,7 +2240,7 @@ int Game::pge_ZOrderIfSameDirection(LivePGE *pge1, LivePGE *pge2, uint8_t comp,
if (pge1 != pge2) {
if ((pge1->flags & 1) == (pge2->flags & 1)) {
_pge_compareVar2 = 1;
pge_updateGroup(pge2->index, pge1->index, comp);
pge_sendMessage(pge2->index, pge1->index, comp);
if (pge2->index == 0) {
return 0xFFFF;
}

View File

@ -66,8 +66,7 @@ void Resource::init() {
_aba = new ResourceAba(_fs);
_aba->readEntries();
_isDemo = true;
}
if (!fileExists("LEVEL1.MAP")) { // fbdemofr (no cutscenes)
} else if (!fileExists("LEVEL2.MAP")) { // fbdemofr (no cutscenes)
_isDemo = true;
}
break;
@ -742,6 +741,7 @@ void Resource::load(const char *objName, int objType, const char *ext) {
switch (objType) {
case OT_CMD:
case OT_POL:
case OT_CMP:
warning("Unable to load '%s' type %d", _entryName, objType);
return;
}
@ -1425,6 +1425,22 @@ uint8_t *Resource::loadBankData(uint16_t num) {
return bankData;
}
uint8_t *Resource::decodeResourceMacText(const char *name, const char *suffix) {
char buf[256];
snprintf(buf, sizeof(buf), "%s %s", name, suffix);
const ResourceMacEntry *entry = _mac->findEntry(buf);
if (entry) {
return decodeResourceMacData(buf, false);
} else { // CD version
if (strcmp(name, "Flashback") == 0) {
name = "Game";
}
const char *language = (_lang == LANG_FR) ? "French" : "English";
snprintf(buf, sizeof(buf), "%s %s %s", name, suffix, language);
return decodeResourceMacData(buf, false);
}
}
uint8_t *Resource::decodeResourceMacData(const char *name, bool decompressLzss) {
_resourceMacDataSize = 0;
uint8_t *data = 0;
@ -1600,10 +1616,10 @@ void Resource::MAC_loadLevelData(int level) {
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);
snprintf(name, sizeof(name), "Level %s", _macLevelNumbers[level]);
_tbn = decodeResourceMacText(name, "names");
_str = decodeResourceMacData("Flashback strings", false);
_str = decodeResourceMacText("Flashback", "strings");
}
void Resource::MAC_loadLevelRoom(int level, int i, DecodeBuffer *dst) {
@ -1700,12 +1716,12 @@ void Resource::MAC_loadCutscene(const char *cutscene) {
}
void Resource::MAC_loadCutsceneText() {
_cine_txt = decodeResourceMacData("Movie strings", false);
_cine_txt = decodeResourceMacText("Movie", "strings");
_cine_off = 0; // offsets are prepended to _cine_txt
}
void Resource::MAC_loadCreditsText() {
_credits = decodeResourceMacData("Credit strings", false);
_credits = decodeResourceMacText("Credit", "strings");
}
void Resource::MAC_loadSounds() {
@ -1722,12 +1738,13 @@ void Resource::MAC_loadSounds() {
return;
}
static const int kHeaderSize = 0x24;
static const int kSoundType = 4;
const int soundType = _mac->_sndIndex;
assert(soundType != -1);
for (int i = 0; i < NUM_SFXS; ++i) {
const int num = table[i];
if (num != -1) {
assert(num >= 0 && num < _mac->_types[kSoundType].count);
const ResourceMacEntry *entry = &_mac->_entries[kSoundType][num];
assert(num >= 0 && num < _mac->_types[soundType].count);
const ResourceMacEntry *entry = &_mac->_entries[soundType][num];
_mac->_f.seek(_mac->_dataOffset + entry->dataOffset);
int dataSize = _mac->_f.readUint32BE();
assert(dataSize > kHeaderSize);

View File

@ -318,6 +318,7 @@ struct Resource {
uint8_t *findBankData(uint16_t num);
uint8_t *loadBankData(uint16_t num);
uint8_t *decodeResourceMacText(const char *name, const char *suffix);
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);

View File

@ -10,7 +10,7 @@ const char *ResourceMac::FILENAME1 = "Flashback.bin";
const char *ResourceMac::FILENAME2 = "Flashback.rsrc";
ResourceMac::ResourceMac(const char *filePath, FileSystem *fs)
: _dataOffset(0), _types(0), _entries(0) {
: _dataOffset(0), _types(0), _entries(0), _sndIndex(-1) {
memset(&_map, 0, sizeof(_map));
_f.open(filePath, "rb", fs);
}
@ -66,6 +66,9 @@ void ResourceMac::loadResourceFork(uint32_t resourceOffset, uint32_t dataSize) {
_f.read(_types[i].id, 4);
_types[i].count = _f.readUint16BE() + 1;
_types[i].startOffset = _f.readUint16BE();
if (_sndIndex < 0 && memcmp(_types[i].id, "snd ", 4) == 0) {
_sndIndex = i;
}
}
_entries = (ResourceMacEntry **)calloc(_map.typesCount, sizeof(ResourceMacEntry *));
for (int i = 0; i < _map.typesCount; ++i) {

View File

@ -39,6 +39,7 @@ struct ResourceMac {
ResourceMacMap _map;
ResourceMacType *_types;
ResourceMacEntry **_entries;
int _sndIndex;
ResourceMac(const char *filePath, FileSystem *);
~ResourceMac();

3
rs.cfg
View File

@ -42,3 +42,6 @@ play_serrure_cutscene=true
# play 'Game saved' sample when saving with level checkpoints (as in the 3DO version)
play_gamesaved_sound=true
# restore content from 'MEMO' cutscene
restore_memo_cutscene=true

View File

@ -2,39 +2,28 @@
#include "screenshot.h"
#include "file.h"
static void TO_LE16(uint8_t *dst, uint16_t value) {
for (int i = 0; i < 2; ++i) {
dst[i] = value & 255;
value >>= 8;
}
}
#define kTgaImageTypeUncompressedTrueColor 2
#define kTgaImageTypeRunLengthEncodedTrueColor 10
#define kTgaDirectionTop (1 << 5)
static const int TGA_HEADER_SIZE = 18;
void saveTGA(const char *filename, const uint8_t *rgba, int w, int h) {
static const uint8_t kImageType = kTgaImageTypeRunLengthEncodedTrueColor;
uint8_t buffer[TGA_HEADER_SIZE];
buffer[0] = 0; // ID Length
buffer[1] = 0; // ColorMap Type
buffer[2] = kImageType;
TO_LE16(buffer + 3, 0); // ColorMap Start
TO_LE16(buffer + 5, 0); // ColorMap Length
buffer[7] = 0; // ColorMap Bits
TO_LE16(buffer + 8, 0); // X-origin
TO_LE16(buffer + 10, 0); // Y-origin
TO_LE16(buffer + 12, w); // Image Width
TO_LE16(buffer + 14, h); // Image Height
buffer[16] = 24; // Pixel Depth
buffer[17] = kTgaDirectionTop; // Descriptor
File f;
if (f.open(filename, "wb", ".")) {
f.write(buffer, sizeof(buffer));
f.writeByte(0); // ID Length
f.writeByte(0); // ColorMap Type
f.writeByte(kImageType);
f.writeUint16LE(0); // ColorMap Start
f.writeUint16LE(0); // ColorMap Length
f.writeByte(0); // ColorMap Bits
f.writeUint16LE(0); // X-origin
f.writeUint16LE(0); // Y-origin
f.writeUint16LE(w); // Image Width
f.writeUint16LE(h); // Image Height
f.writeByte(24); // Pixel Depth
f.writeByte(kTgaDirectionTop); // Descriptor
if (kImageType == kTgaImageTypeUncompressedTrueColor) {
for (int i = 0; i < w * h; ++i) {
f.writeByte(rgba[0]);

View File

@ -25,8 +25,8 @@ const Cutscene::OpcodeStub Cutscene::_opcodeTable[] = {
&Cutscene::op_drawShapeScale,
&Cutscene::op_drawShapeScaleRotate,
/* 0x0C */
&Cutscene::op_drawCreditsText,
&Cutscene::op_drawStringAtPos,
&Cutscene::op_copyScreen,
&Cutscene::op_drawTextAtPos,
&Cutscene::op_handleKeys
};
@ -3535,12 +3535,12 @@ const Game::pge_OpcodeProc Game::_pge_opcodeTable[] = {
/* 0x20 */
&Game::pge_op_collides2o2u,
&Game::pge_op_collides2u2o,
&Game::pge_op_isInGroup,
&Game::pge_op_updateGroup0,
&Game::pge_hasPiegeSentMessage,
&Game::pge_op_sendMessageData0,
/* 0x24 */
&Game::pge_op_updateGroup1,
&Game::pge_op_updateGroup2,
&Game::pge_op_updateGroup3,
&Game::pge_op_sendMessageData1,
&Game::pge_op_sendMessageData2,
&Game::pge_op_sendMessageData3,
&Game::pge_op_isPiegeDead,
/* 0x28 */
&Game::pge_op_collides1u2o,
@ -3563,10 +3563,10 @@ const Game::pge_OpcodeProc Game::_pge_opcodeTable[] = {
&Game::pge_op_setCollisionState1,
&Game::pge_op_setCollisionState0,
/* 0x38 */
&Game::pge_op_isInGroup1,
&Game::pge_op_isInGroup2,
&Game::pge_op_isInGroup3,
&Game::pge_op_isInGroup4,
&Game::pge_hasMessageData0,
&Game::pge_hasMessageData1,
&Game::pge_hasMessageData2,
&Game::pge_hasMessageData3,
/* 0x3C */
&Game::pge_o_unk0x3C,
&Game::pge_o_unk0x3D,
@ -3626,7 +3626,7 @@ const Game::pge_OpcodeProc Game::_pge_opcodeTable[] = {
&Game::pge_op_setCollisionState2,
&Game::pge_op_saveState,
&Game::pge_o_unk0x6A,
&Game::pge_op_isInGroupSlice,
&Game::pge_isToggleable,
/* 0x6C */
&Game::pge_o_unk0x6C,
&Game::pge_op_isCollidingObject,
@ -6187,6 +6187,89 @@ const uint8_t Cutscene::_caillouSetData[] = {
0x06, 0x66, 0x04, 0x44, 0x02, 0x22, 0x04, 0x66, 0x00
};
const uint8_t Cutscene::_memoSetShape2Data[] = {
0x00, 0x25, 0x00, 0x00, 0x00, 0x00, 0x01, 0x08, 0x00, 0x41, 0x00, 0x27, 0x0c, 0x0c, 0x00, 0x69,
0x00, 0x22, 0x00, 0x7c, 0x00, 0x31, 0x00, 0x7c, 0x00, 0x34, 0x00, 0x78, 0x00, 0x36, 0x00, 0x72,
0x00, 0x36, 0x00, 0x67, 0x00, 0x32, 0x00, 0x5d, 0x00, 0x20, 0x00, 0x64, 0x00, 0x20, 0x00, 0x00,
0x00, 0x00, 0x01, 0x10, 0x00, 0x92, 0x00, 0x23, 0x0c, 0x0c, 0x00, 0x75, 0x00, 0x4f, 0x00, 0x77,
0x00, 0x55, 0x00, 0x77, 0x00, 0x5a, 0x00, 0x74, 0x00, 0x70, 0x00, 0x47, 0x00, 0x70, 0x00, 0x32,
0x00, 0x4c, 0x00, 0x33, 0x00, 0x47, 0x00, 0x37, 0x00, 0x40, 0x00, 0x37, 0x00, 0x3a, 0x00, 0x39,
0x00, 0x35, 0x00, 0x4a, 0x00, 0x24, 0x00, 0x51, 0x00, 0x23, 0x00, 0x55, 0x00, 0x1a, 0x00, 0x5a,
0x00, 0x1a, 0x00, 0x5f, 0x00, 0x23, 0x00, 0x61, 0x00, 0x2e, 0x00, 0x00, 0x00, 0x00, 0x01, 0x0a,
0x00, 0xb5, 0x00, 0x2d, 0x0d, 0x0d, 0x00, 0x4d, 0x00, 0x27, 0x00, 0x4f, 0x00, 0x2e, 0x00, 0x53,
0x00, 0x3a, 0x00, 0x54, 0x00, 0x41, 0x00, 0x54, 0x00, 0x4f, 0x00, 0x50, 0x00, 0x3b, 0x00, 0x4c,
0x00, 0x31, 0x00, 0x4b, 0x00, 0x28, 0x00, 0x4c, 0x00, 0x24, 0x00, 0x4d, 0x00, 0x24, 0x00, 0x00,
0x00, 0x00, 0x01, 0x07, 0x00, 0x42, 0x00, 0x34, 0x0d, 0x0d, 0x00, 0x76, 0x00, 0x36, 0x00, 0x79,
0x00, 0x36, 0x00, 0x7b, 0x00, 0x35, 0x00, 0x79, 0x00, 0x37, 0x00, 0x73, 0x00, 0x37, 0x00, 0x63,
0x00, 0x2f, 0x00, 0x65, 0x00, 0x2d, 0x00, 0x00, 0x00, 0x00, 0x01, 0x08, 0x00, 0x8e, 0x00, 0x36,
0x0c, 0x0c, 0x00, 0x7a, 0x00, 0x3f, 0x00, 0x7b, 0x00, 0x40, 0x00, 0x7b, 0x00, 0x42, 0x00, 0x79,
0x00, 0x45, 0x00, 0x74, 0x00, 0x46, 0x00, 0x65, 0x00, 0x3f, 0x00, 0x63, 0x00, 0x2d, 0x00, 0x6f,
0x00, 0x39, 0x00, 0x00, 0x00, 0x00, 0x01, 0x08, 0x00, 0xa1, 0x00, 0x20, 0x0c, 0x0c, 0x00, 0x5e,
0x00, 0x23, 0x00, 0x68, 0x00, 0x3b, 0x00, 0x56, 0x00, 0x40, 0x00, 0x4e, 0x00, 0x26, 0x00, 0x4e,
0x00, 0x1c, 0x00, 0x51, 0x00, 0x18, 0x00, 0x55, 0x00, 0x17, 0x00, 0x59, 0x00, 0x19, 0x00, 0x00,
0x00, 0x00, 0x01, 0x0c, 0x00, 0x73, 0x00, 0x37, 0x0d, 0x0d, 0x00, 0x3d, 0x00, 0x41, 0x00, 0x41,
0x00, 0x4d, 0x00, 0x4a, 0x00, 0x62, 0x00, 0x40, 0x00, 0x5d, 0x00, 0x39, 0x00, 0x49, 0x00, 0x37,
0x00, 0x43, 0x00, 0x37, 0x00, 0x3b, 0x00, 0x38, 0x00, 0x36, 0x00, 0x3e, 0x00, 0x30, 0x00, 0x3b,
0x00, 0x35, 0x00, 0x3b, 0x00, 0x3a, 0x00, 0x3c, 0x00, 0x3c, 0x00, 0x00, 0x00, 0x00, 0x01, 0x02,
0x00, 0x54, 0x00, 0x27, 0x0d, 0x0d, 0x00, 0x64, 0x00, 0x32, 0x00, 0x5e, 0x00, 0x20, 0x00, 0x00,
0x00, 0x00, 0x01, 0x02, 0x00, 0x52, 0x00, 0x39, 0x0d, 0x0d, 0x00, 0x6b, 0x00, 0x40, 0x00, 0x64,
0x00, 0x32, 0x00, 0x00, 0x00, 0x00, 0x01, 0x02, 0x00, 0x63, 0x00, 0x2b, 0x0d, 0x0d, 0x00, 0x55,
0x00, 0x24, 0x00, 0x5a, 0x00, 0x24, 0x00, 0x00, 0x00, 0x00, 0x01, 0x02, 0x00, 0x68, 0x00, 0x2b,
0x0d, 0x0d, 0x00, 0x51, 0x00, 0x26, 0x00, 0x55, 0x00, 0x24, 0x00, 0x00, 0x00, 0x00, 0x01, 0x02,
0x00, 0x6c, 0x00, 0x27, 0x0d, 0x0d, 0x00, 0x4f, 0x00, 0x20, 0x00, 0x51, 0x00, 0x26, 0x00, 0x00,
0x00, 0x00, 0x01, 0x02, 0x00, 0x6e, 0x00, 0x24, 0x0d, 0x0d, 0x00, 0x4f, 0x00, 0x1d, 0x00, 0x4f,
0x00, 0x20, 0x00, 0x00, 0x00, 0x00, 0x01, 0x02, 0x00, 0x5f, 0x00, 0x35, 0x0d, 0x0d, 0x00, 0x56,
0x00, 0x2e, 0x00, 0x5e, 0x00, 0x2e, 0x00, 0x00, 0x00, 0x00, 0x01, 0x02, 0x00, 0x5e, 0x00, 0x35,
0x0d, 0x0d, 0x00, 0x5f, 0x00, 0x30, 0x00, 0x56, 0x00, 0x2e, 0x00, 0x00, 0x00, 0x00, 0x01, 0x05,
0x00, 0x4d, 0x00, 0x5a, 0x0c, 0x0c, 0x00, 0x4d, 0x00, 0x8c, 0x00, 0x47, 0x00, 0x70, 0x00, 0x73,
0x00, 0x67, 0x00, 0x77, 0x00, 0x75, 0x00, 0x83, 0x00, 0x8c, 0x00, 0x00, 0x00, 0x00, 0x01, 0x0a,
0x00, 0x6a, 0x00, 0x4c, 0x0d, 0x0d, 0x00, 0x53, 0x00, 0x8c, 0x00, 0x4c, 0x00, 0x8c, 0x00, 0x47,
0x00, 0x72, 0x00, 0x33, 0x00, 0x50, 0x00, 0x31, 0x00, 0x49, 0x00, 0x34, 0x00, 0x45, 0x00, 0x34,
0x00, 0x4b, 0x00, 0x37, 0x00, 0x4f, 0x00, 0x40, 0x00, 0x57, 0x00, 0x4f, 0x00, 0x76, 0x00, 0x00,
0x00, 0x00, 0x01, 0x04, 0x00, 0x53, 0x00, 0x34, 0x0d, 0x0d, 0x00, 0x63, 0x00, 0x2d, 0x00, 0x66,
0x00, 0x2d, 0x00, 0x6a, 0x00, 0x31, 0x00, 0x64, 0x00, 0x30, 0x00, 0x00, 0x00, 0x00, 0x01, 0x04,
0x00, 0x56, 0x00, 0x30, 0x0d, 0x0d, 0x00, 0x67, 0x00, 0x2d, 0x00, 0x65, 0x00, 0x2c, 0x00, 0x66,
0x00, 0x2c, 0x00, 0x67, 0x00, 0x29, 0x00, 0x00, 0x00, 0x00, 0x01, 0x02, 0x00, 0x4a, 0x00, 0x45,
0x0d, 0x0d, 0x00, 0x73, 0x00, 0x4b, 0x00, 0x6a, 0x00, 0x3e, 0x00, 0x00, 0x00, 0x00, 0x01, 0x03,
0x00, 0x51, 0x00, 0x40, 0x0d, 0x0d, 0x00, 0x69, 0x00, 0x3b, 0x00, 0x6c, 0x00, 0x39, 0x00, 0x6a,
0x00, 0x3d, 0x00, 0x00, 0x00, 0x00, 0x01, 0x08, 0x00, 0x42, 0x00, 0x47, 0x0d, 0x0d, 0x00, 0x7b,
0x00, 0x43, 0x00, 0x77, 0x00, 0x46, 0x00, 0x73, 0x00, 0x46, 0x00, 0x70, 0x00, 0x45, 0x00, 0x6c,
0x00, 0x40, 0x00, 0x73, 0x00, 0x44, 0x00, 0x77, 0x00, 0x44, 0x00, 0x7b, 0x00, 0x42, 0x00, 0x00,
0x00, 0x00, 0x01, 0x05, 0x00, 0x51, 0x00, 0x5f, 0x0d, 0x0d, 0x00, 0x77, 0x00, 0x75, 0x00, 0x7f,
0x00, 0x84, 0x00, 0x75, 0x00, 0x7a, 0x00, 0x73, 0x00, 0x75, 0x00, 0x75, 0x00, 0x6c, 0x00, 0x00,
0x00, 0x00, 0x01, 0x03, 0x00, 0x6d, 0x00, 0x3d, 0x0d, 0x0d, 0x00, 0x4f, 0x00, 0x37, 0x00, 0x49,
0x00, 0x37, 0x00, 0x4c, 0x00, 0x34, 0x00, 0x00, 0x00, 0x00, 0x01, 0x03, 0x00, 0x6d, 0x00, 0x40,
0x0d, 0x0d, 0x00, 0x4f, 0x00, 0x37, 0x00, 0x4b, 0x00, 0x39, 0x00, 0x4e, 0x00, 0x37, 0x00, 0x00,
0x00, 0x00, 0x01, 0x05, 0x00, 0x56, 0x00, 0x38, 0x0c, 0x0c, 0x00, 0x7a, 0x00, 0x4e, 0x00, 0x7a,
0x00, 0x52, 0x00, 0x79, 0x00, 0x53, 0x00, 0x6d, 0x00, 0x52, 0x00, 0x6a, 0x00, 0x45, 0x00, 0x00,
0x00, 0x00, 0x01, 0x02, 0x00, 0x5b, 0x00, 0x3b, 0x0d, 0x0d, 0x00, 0x75, 0x00, 0x4c, 0x00, 0x71,
0x00, 0x48, 0x00, 0x00, 0x00, 0x00, 0x01, 0x02, 0x00, 0x58, 0x00, 0x46, 0x0d, 0x0d, 0x00, 0x78,
0x00, 0x55, 0x00, 0x78, 0x00, 0x53, 0x00, 0x00, 0x00, 0x00, 0x01, 0x02, 0x00, 0x58, 0x00, 0x3f,
0x0d, 0x0d, 0x00, 0x78, 0x00, 0x54, 0x00, 0x75, 0x00, 0x4c, 0x00, 0x00, 0x00, 0x00, 0x01, 0x05,
0x00, 0x55, 0x00, 0x42, 0x0d, 0x0d, 0x00, 0x7b, 0x00, 0x51, 0x00, 0x79, 0x00, 0x53, 0x00, 0x77,
0x00, 0x51, 0x00, 0x7a, 0x00, 0x51, 0x00, 0x7b, 0x00, 0x4f, 0x00, 0x00, 0x00, 0x00, 0x01, 0x04,
0x00, 0x1a, 0x00, 0x34, 0x05, 0x05, 0x00, 0x50, 0x00, 0x34, 0x00, 0x53, 0x00, 0x40, 0x00, 0x2b,
0x00, 0x45, 0x00, 0x1a, 0x00, 0x34, 0x00, 0x00, 0x00, 0x00, 0x01, 0x04, 0x00, 0x66, 0x00, 0x34,
0x05, 0x05, 0x00, 0x66, 0x00, 0x34, 0x00, 0x85, 0x00, 0x34, 0x00, 0x87, 0x00, 0x37, 0x00, 0x6a,
0x00, 0x3c, 0x00, 0x00, 0x00, 0x00, 0x01, 0x04, 0x00, 0x25, 0x00, 0x41, 0x04, 0x04, 0x00, 0x2a,
0x00, 0x46, 0x00, 0x52, 0x00, 0x41, 0x00, 0x52, 0x00, 0x44, 0x00, 0x25, 0x00, 0x4b, 0x00, 0x00,
0x00, 0x00, 0x01, 0x04, 0x00, 0x14, 0x00, 0x34, 0x04, 0x04, 0x00, 0x19, 0x00, 0x34, 0x00, 0x2a,
0x00, 0x45, 0x00, 0x26, 0x00, 0x4b, 0x00, 0x14, 0x00, 0x39, 0x00, 0x00, 0x00, 0x00, 0x01, 0x04,
0x00, 0x6a, 0x00, 0x37, 0x04, 0x04, 0x00, 0x6a, 0x00, 0x3c, 0x00, 0x87, 0x00, 0x37, 0x00, 0x87,
0x00, 0x38, 0x00, 0x6c, 0x00, 0x3e, 0x00, 0x00, 0x00, 0x00, 0x01, 0x04, 0x00, 0x34, 0x00, 0x34,
0x03, 0x03, 0x00, 0x34, 0x00, 0x34, 0x00, 0x4c, 0x00, 0x34, 0x00, 0x4e, 0x00, 0x39, 0x00, 0x3b,
0x00, 0x3b, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, 0x00, 0x00,
0x0e, 0xa8, 0x00, 0x00, 0x02, 0x22, 0x04, 0x44, 0x06, 0x66, 0x08, 0x88, 0x0a, 0xaa, 0x00, 0x40,
0x00, 0x80, 0x00, 0xa0, 0x06, 0x42, 0x0c, 0x86, 0x0a, 0x64, 0x00, 0xe0, 0x0e, 0xe0, 0x00
};
const uint8_t Cutscene::_memoSetShape4Data[] = {
0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x01, 0xff, 0x00, 0x8f, 0x00, 0x3d, 0x0e, 0x0e, 0x00, 0x0a,
0x00, 0x0a, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, 0x00, 0x00,
0x0a, 0xa0, 0x00, 0x00, 0x02, 0x22, 0x04, 0x44, 0x06, 0x66, 0x08, 0x88, 0x0a, 0xaa, 0x00, 0x40,
0x00, 0x80, 0x00, 0xa0, 0x06, 0x42, 0x0c, 0x86, 0x0a, 0x64, 0x00, 0xe0, 0x0f, 0xf0, 0x00
};
const uint8_t Menu::_flagEn16x12[] = {
0x73, 0x00, 0x19, 0x8e, 0x00, 0x00, 0x82, 0x91, 0x9d, 0x4e, 0x4f, 0xad, 0x00, 0x00, 0x89, 0x00,
0x00, 0x89, 0x82, 0x91, 0x9d, 0x8e, 0x00, 0x00, 0x8e, 0x00, 0x00, 0x82, 0x91, 0x9d, 0x00, 0x00,

View File

@ -996,7 +996,7 @@ void SystemStub_SDL::cleanupGraphics() {
}
void SystemStub_SDL::changeGraphics(bool fullscreen, int scaleFactor) {
int factor = CLIP(scaleFactor, _scaler->factorMin, _scaler->factorMax);
const int factor = _scaler ? CLIP(scaleFactor, _scaler->factorMin, _scaler->factorMax) : scaleFactor;
if (fullscreen == _fullscreen && factor == _scaleFactor) {
// no change
return;

10
util.h
View File

@ -28,8 +28,12 @@ enum {
extern uint16_t g_debugMask;
extern void debug(uint16_t cm, const char *msg, ...); // __attribute__((__format__(__printf__, 2, 3)))
extern void error(const char *msg, ...); // __attribute__((__format__(__printf__, 1, 2)))
extern void warning(const char *msg, ...); // __attribute__((__format__(__printf__, 1, 2)))
extern void debug(uint16_t cm, const char *msg, ...);
extern void error(const char *msg, ...);
extern void warning(const char *msg, ...);
#ifdef NDEBUG
#define debug(x, ...)
#endif
#endif // UTIL_H__