diff --git a/CHANGES.txt b/CHANGES.txt index 71f63ea..875eba7 100644 --- a/CHANGES.txt +++ b/CHANGES.txt @@ -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 diff --git a/README.txt b/README.txt index 90fd00a..e1d9b8a 100644 --- a/README.txt +++ b/README.txt @@ -1,6 +1,6 @@ REminiscence README -Release version: 0.4.6 +Release version: 0.4.7 ------------------------------------------------------------------------------- diff --git a/collision.cpp b/collision.cpp index 60907a6..e2a7d96 100644 --- a/collision.cpp +++ b/collision.cpp @@ -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; diff --git a/cutscene.cpp b/cutscene.cpp index b4d658e..7849249 100644 --- a/cutscene.cpp +++ b/cutscene.cpp @@ -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); diff --git a/cutscene.h b/cutscene.h index aef0fc0..1540292 100644 --- a/cutscene.h +++ b/cutscene.h @@ -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); }; diff --git a/game.cpp b/game.cpp index 959e5c3..33dd647 100644 --- a/game.cpp +++ b/game.cpp @@ -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); } diff --git a/game.h b/game.h index 2f88ab5..c95ec15 100644 --- a/game.h +++ b/game.h @@ -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); diff --git a/intern.h b/intern.h index 458217c..a2118fc 100644 --- a/intern.h +++ b/intern.h @@ -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 { diff --git a/main.cpp b/main.cpp index 6d5de4d..a9c325a 100644 --- a/main.cpp +++ b/main.cpp @@ -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"; diff --git a/mixer.cpp b/mixer.cpp index 2bbd7af..4a32442 100644 --- a/mixer.cpp +++ b/mixer.cpp @@ -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; } diff --git a/mod_player.cpp b/mod_player.cpp index 7ee290c..a26b72d 100644 --- a/mod_player.cpp +++ b/mod_player.cpp @@ -40,6 +40,7 @@ struct ModPlayer_impl { if (data) { f->read(data, size); _mf = ModPlug_Load(data, size); + free(data); } return _mf != 0; } diff --git a/piege.cpp b/piege.cpp index ce5e46f..d27663c 100644 --- a/piege.cpp +++ b/piege.cpp @@ -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; } diff --git a/resource.cpp b/resource.cpp index 0cfbb25..b93e970 100644 --- a/resource.cpp +++ b/resource.cpp @@ -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); diff --git a/resource.h b/resource.h index c11dcee..b705d78 100644 --- a/resource.h +++ b/resource.h @@ -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); diff --git a/resource_mac.cpp b/resource_mac.cpp index 40b7a2d..cdca14d 100644 --- a/resource_mac.cpp +++ b/resource_mac.cpp @@ -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) { diff --git a/resource_mac.h b/resource_mac.h index 35634e7..eabcef1 100644 --- a/resource_mac.h +++ b/resource_mac.h @@ -39,6 +39,7 @@ struct ResourceMac { ResourceMacMap _map; ResourceMacType *_types; ResourceMacEntry **_entries; + int _sndIndex; ResourceMac(const char *filePath, FileSystem *); ~ResourceMac(); diff --git a/rs.cfg b/rs.cfg index 42f3cd3..bdb992a 100644 --- a/rs.cfg +++ b/rs.cfg @@ -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 diff --git a/screenshot.cpp b/screenshot.cpp index 29d1937..2b56622 100644 --- a/screenshot.cpp +++ b/screenshot.cpp @@ -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]); diff --git a/staticres.cpp b/staticres.cpp index 153e396..56c1a50 100644 --- a/staticres.cpp +++ b/staticres.cpp @@ -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, diff --git a/systemstub_sdl.cpp b/systemstub_sdl.cpp index 8f41636..407d18e 100644 --- a/systemstub_sdl.cpp +++ b/systemstub_sdl.cpp @@ -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; diff --git a/util.h b/util.h index 4bb5226..2eca991 100644 --- a/util.h +++ b/util.h @@ -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__