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 * release 0.4.6
- added rewind to automatic saves - added rewind to automatic saves
- fixed passwords and protection codes input - fixed passwords and protection codes input

View File

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

View File

@ -71,11 +71,11 @@ void Cutscene::updatePalette() {
} }
} }
void Cutscene::setPalette() { void Cutscene::updateScreen() {
sync(); sync();
updatePalette(); updatePalette();
SWAP(_page0, _page1); SWAP(_frontPage, _backPage);
_stub->copyRect(0, 0, _vid->_w, _vid->_h, _page0, _vid->_w); _stub->copyRect(0, 0, _vid->_w, _vid->_h, _frontPage, _vid->_w);
_stub->updateScreen(0); _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) { if (_clearScreen == 0) {
memcpy(_page1, _pageC, _vid->_layerSize); memcpy(_backPage, _auxPage, _vid->_layerSize);
} else { } else {
memset(_page1, 0xC0, _vid->_layerSize); memset(_backPage, 0xC0, _vid->_layerSize);
} }
} }
@ -230,7 +230,7 @@ void Cutscene::drawCreditsText() {
} else { } else {
_creditsTextCounter -= 10; _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; _cmdPtrBak = _cmdPtr;
drawCreditsText(); drawCreditsText();
_frameDelay = 5; _frameDelay = 5;
setPalette(); updateScreen();
swapLayers(); clearBackPage();
_creditsSlowText = 0; _creditsSlowText = 0;
} }
@ -283,7 +283,7 @@ void Cutscene::op_refreshScreen() {
debug(DBG_CUT, "Cutscene::op_refreshScreen()"); debug(DBG_CUT, "Cutscene::op_refreshScreen()");
_clearScreen = fetchNextCmdByte(); _clearScreen = fetchNextCmdByte();
if (_clearScreen != 0) { if (_clearScreen != 0) {
swapLayers(); clearBackPage();
_creditsSlowText = 0; _creditsSlowText = 0;
} }
} }
@ -298,11 +298,11 @@ void Cutscene::op_waitForSync() {
if (_textBuf == _textCurBuf) { if (_textBuf == _textCurBuf) {
_creditsTextCounter = _res->isAmiga() ? 60 : 20; _creditsTextCounter = _res->isAmiga() ? 60 : 20;
} }
memcpy(_page1, _page0, _vid->_layerSize); memcpy(_backPage, _frontPage, _vid->_layerSize);
drawCreditsText(); drawCreditsText();
setPalette(); updateScreen();
} while (--n); } while (--n);
swapLayers(); clearBackPage();
_creditsSlowText = 0; _creditsSlowText = 0;
} else { } else {
_frameDelay = fetchNextCmdByte() * 4; _frameDelay = fetchNextCmdByte() * 4;
@ -312,7 +312,7 @@ void Cutscene::op_waitForSync() {
void Cutscene::drawShape(const uint8_t *data, int16_t x, int16_t y) { void Cutscene::drawShape(const uint8_t *data, int16_t x, int16_t y) {
debug(DBG_CUT, "Cutscene::drawShape()"); debug(DBG_CUT, "Cutscene::drawShape()");
_gfx.setLayer(_page1, _vid->_w); _gfx.setLayer(_backPage, _vid->_w);
uint8_t numVertices = *data++; uint8_t numVertices = *data++;
if (numVertices & 0x80) { if (numVertices & 0x80) {
Point pt; Point pt;
@ -393,10 +393,12 @@ void Cutscene::op_drawShape() {
drawShape(primitiveVertices, x + dx, y + dy); drawShape(primitiveVertices, x + dx, y + dy);
} }
if (_clearScreen != 0) { if (_clearScreen != 0) {
memcpy(_pageC, _page1, _vid->_layerSize); memcpy(_auxPage, _backPage, _vid->_layerSize);
} }
} }
static int _paletteNum = -1;
void Cutscene::op_setPalette() { void Cutscene::op_setPalette() {
debug(DBG_CUT, "Cutscene::op_setPalette()"); debug(DBG_CUT, "Cutscene::op_setPalette()");
uint8_t num = fetchNextCmdByte(); uint8_t num = fetchNextCmdByte();
@ -408,6 +410,7 @@ void Cutscene::op_setPalette() {
_palBuf[0x20] = 0x0F; _palBuf[0x20] = 0x0F;
_palBuf[0x21] = 0xFF; _palBuf[0x21] = 0xFF;
} }
_paletteNum = num;
} }
void Cutscene::op_drawCaptionText() { void Cutscene::op_drawCaptionText() {
@ -419,7 +422,7 @@ void Cutscene::op_drawCaptionText() {
if (_id == 0x39 && strId == 0xFFFF) { if (_id == 0x39 && strId == 0xFFFF) {
if ((_res->isDOS() && (_cmdPtr - _cmdPtrBak) == 0x10) || (_res->isAmiga() && (_cmdPtr - getCommandData()) == 0x9F3)) { if ((_res->isDOS() && (_cmdPtr - _cmdPtrBak) == 0x10) || (_res->isAmiga() && (_cmdPtr - getCommandData()) == 0x9F3)) {
_frameDelay = 100; _frameDelay = 100;
setPalette(); updateScreen();
return; return;
} }
} }
@ -427,14 +430,14 @@ void Cutscene::op_drawCaptionText() {
const int h = 45 * _vid->_layerScale; const int h = 45 * _vid->_layerScale;
const int y = Video::GAMESCREEN_H * _vid->_layerScale - h; const int y = Video::GAMESCREEN_H * _vid->_layerScale - h;
memset(_pageC + y * _vid->_w, 0xC0, h * _vid->_w); memset(_auxPage + y * _vid->_w, 0xC0, h * _vid->_w);
memset(_page1 + y * _vid->_w, 0xC0, h * _vid->_w); memset(_backPage + y * _vid->_w, 0xC0, h * _vid->_w);
memset(_page0 + y * _vid->_w, 0xC0, h * _vid->_w); memset(_frontPage + y * _vid->_w, 0xC0, h * _vid->_w);
if (strId != 0xFFFF) { if (strId != 0xFFFF) {
const uint8_t *str = _res->getCineString(strId); const uint8_t *str = _res->getCineString(strId);
if (str) { if (str) {
drawText(0, 129, str, 0xEF, _page1, kTextJustifyAlign); drawText(0, 129, str, 0xEF, _backPage, kTextJustifyAlign);
drawText(0, 129, str, 0xEF, _pageC, kTextJustifyAlign); drawText(0, 129, str, 0xEF, _auxPage, kTextJustifyAlign);
} }
} }
} }
@ -452,15 +455,15 @@ void Cutscene::op_skip3() {
void Cutscene::op_refreshAll() { void Cutscene::op_refreshAll() {
debug(DBG_CUT, "Cutscene::op_refreshAll()"); debug(DBG_CUT, "Cutscene::op_refreshAll()");
_frameDelay = 5; _frameDelay = 5;
setPalette(); updateScreen();
swapLayers(); clearBackPage();
_creditsSlowText = 0xFF; _creditsSlowText = 0xFF;
op_handleKeys(); 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) { void Cutscene::drawShapeScale(const uint8_t *data, int16_t zoom, int16_t b, int16_t c, int16_t d, int16_t e, int16_t f, int16_t g) {
debug(DBG_CUT, "Cutscene::drawShapeScale(%d, %d, %d, %d, %d, %d, %d)", zoom, b, c, d, e, f, g); debug(DBG_CUT, "Cutscene::drawShapeScale(%d, %d, %d, %d, %d, %d, %d)", zoom, b, c, d, e, f, g);
_gfx.setLayer(_page1, _vid->_w); _gfx.setLayer(_backPage, _vid->_w);
uint8_t numVertices = *data++; uint8_t numVertices = *data++;
if (numVertices & 0x80) { if (numVertices & 0x80) {
int16_t x, y; int16_t x, y;
@ -631,7 +634,7 @@ void Cutscene::op_drawShapeScale() {
_hasAlphaColor = (verticesOffset & 0x4000) != 0; _hasAlphaColor = (verticesOffset & 0x4000) != 0;
uint8_t color = *shapeData++; uint8_t color = *shapeData++;
if (_clearScreen == 0) { if (_clearScreen == 0) {
color += 0x10; // 2nd pal buf color += 0x10; // 2nd paletter buffer
} }
_primitiveColor = 0xC0 + color; _primitiveColor = 0xC0 + color;
drawShapeScale(p, zoom, dx, dy, x, y, 0, 0); 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) { void Cutscene::drawShapeScaleRotate(const uint8_t *data, int16_t zoom, int16_t b, int16_t c, int16_t d, int16_t e, int16_t f, int16_t g) {
debug(DBG_CUT, "Cutscene::drawShapeScaleRotate(%d, %d, %d, %d, %d, %d, %d)", zoom, b, c, d, e, f, g); debug(DBG_CUT, "Cutscene::drawShapeScaleRotate(%d, %d, %d, %d, %d, %d, %d)", zoom, b, c, d, e, f, g);
_gfx.setLayer(_page1, _vid->_w); _gfx.setLayer(_backPage, _vid->_w);
uint8_t numVertices = *data++; uint8_t numVertices = *data++;
if (numVertices & 0x80) { if (numVertices & 0x80) {
int16_t x, y, ix, iy; int16_t x, y, ix, iy;
@ -724,7 +727,7 @@ void Cutscene::drawShapeScaleRotate(const uint8_t *data, int16_t zoom, int16_t b
_gfx.drawPoint(_primitiveColor, &pt); _gfx.drawPoint(_primitiveColor, &pt);
} else { } else {
int16_t x, y, a, shape_last_x, shape_last_y; int16_t x, y, a, shape_last_x, shape_last_y;
Point tempVertices[40]; Point tempVertices[MAX_VERTICES];
_shape_cur_x = b + READ_BE_UINT16(data); data += 2; _shape_cur_x = b + READ_BE_UINT16(data); data += 2;
x = _shape_cur_x; x = _shape_cur_x;
_shape_cur_y = c + READ_BE_UINT16(data); data += 2; _shape_cur_y = c + READ_BE_UINT16(data); data += 2;
@ -856,7 +859,7 @@ void Cutscene::op_drawShapeScaleRotate() {
_hasAlphaColor = (verticesOffset & 0x4000) != 0; _hasAlphaColor = (verticesOffset & 0x4000) != 0;
uint8_t color = *shapeData++; uint8_t color = *shapeData++;
if (_clearScreen == 0) { if (_clearScreen == 0) {
color += 0x10; // 2nd pal buf color += 0x10; // 2nd palette buffer
} }
_primitiveColor = 0xC0 + color; _primitiveColor = 0xC0 + color;
drawShapeScaleRotate(p, zoom, dx, dy, x, y, 0, 0); drawShapeScaleRotate(p, zoom, dx, dy, x, y, 0, 0);
@ -864,19 +867,86 @@ void Cutscene::op_drawShapeScaleRotate() {
} }
} }
void Cutscene::op_drawCreditsText() { static const uint16_t memoSetPos[] = {
debug(DBG_CUT, "Cutscene::op_drawCreditsText()"); 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; _creditsSlowText = 0xFF;
if (_textCurBuf == _textBuf) { if (_textCurBuf == _textBuf) {
++_creditsTextCounter; ++_creditsTextCounter;
} }
memcpy(_page1, _page0, _vid->_layerSize); memcpy(_backPage, _frontPage, _vid->_layerSize);
_frameDelay = 10; _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() { void Cutscene::op_drawTextAtPos() {
debug(DBG_CUT, "Cutscene::op_drawStringAtPos()"); debug(DBG_CUT, "Cutscene::op_drawTextAtPos()");
uint16_t strId = fetchNextCmdWord(); uint16_t strId = fetchNextCmdWord();
if (strId != 0xFFFF) { if (strId != 0xFFFF) {
int16_t x = (int8_t)fetchNextCmdByte() * 8; int16_t x = (int8_t)fetchNextCmdByte() * 8;
@ -885,12 +955,12 @@ void Cutscene::op_drawStringAtPos() {
const uint8_t *str = _res->getCineString(strId & 0xFFF); const uint8_t *str = _res->getCineString(strId & 0xFFF);
if (str) { if (str) {
uint8_t color = 0xD0 + (strId >> 0xC); 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 // 'voyage' - cutscene script redraws the string to refresh the screen
if (_id == 0x34 && (strId & 0xFFF) == 0x45) { if (_id == 0x34 && (strId & 0xFFF) == 0x45) {
if ((_cmdPtr - _cmdPtrBak) == 0xA) { 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); _stub->updateScreen(0);
} else { } else {
_stub->sleep(15); _stub->sleep(15);
@ -995,6 +1065,10 @@ void Cutscene::mainLoop(uint16_t num) {
_polPtr = getPolygonData(); _polPtr = getPolygonData();
debug(DBG_CUT, "_baseOffset = %d offset = %d", _baseOffset, offset); 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) { while (!_stub->_pi.quit && !_interrupted && !_stop) {
uint8_t op = fetchNextCmdByte(); uint8_t op = fetchNextCmdByte();
debug(DBG_CUT, "Cutscene::play() opcode = 0x%X (%d)", op, (op >> 2)); debug(DBG_CUT, "Cutscene::play() opcode = 0x%X (%d)", op, (op >> 2));
@ -1071,9 +1145,9 @@ void Cutscene::unload() {
} }
void Cutscene::prepare() { void Cutscene::prepare() {
_page0 = _vid->_frontLayer; _frontPage = _vid->_frontLayer;
_page1 = _vid->_tempLayer; _backPage = _vid->_tempLayer;
_pageC = _vid->_tempLayer2; _auxPage = _vid->_tempLayer2;
_stub->_pi.dirMask = 0; _stub->_pi.dirMask = 0;
_stub->_pi.enter = false; _stub->_pi.enter = false;
_stub->_pi.space = false; _stub->_pi.space = false;
@ -1138,9 +1212,9 @@ void Cutscene::playText(const char *str) {
} }
} }
const int y = (128 - lines * 8) / 2; const int y = (128 - lines * 8) / 2;
memset(_page1, 0xC0, _vid->_layerSize); memset(_backPage, 0xC0, _vid->_layerSize);
drawText(0, y, (const uint8_t *)str, 0xC1, _page1, kTextJustifyAlign); drawText(0, y, (const uint8_t *)str, 0xC1, _backPage, kTextJustifyAlign);
_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); _stub->updateScreen(0);
while (!_stub->_pi.quit) { 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; const int count = READ_BE_UINT16(p + offset); offset += 2;
for (int i = 0; i < count - 1; ++i) { for (int i = 0; i < count - 1; ++i) {
offset += 5; // shape_marker offset += 5; // shape_marker
@ -1303,14 +1377,14 @@ void Cutscene::playSet(const uint8_t *p, int offset) {
} }
prepare(); prepare();
_gfx.setLayer(_page1, _vid->_w); _gfx.setLayer(_backPage, _vid->_w);
offset = 10; offset = 10;
const int frames = READ_BE_UINT16(p + offset); offset += 2; const int frames = READ_BE_UINT16(p + offset); offset += 2;
for (int i = 0; i < frames && !_stub->_pi.quit && !_interrupted; ++i) { for (int i = 0; i < frames && !_stub->_pi.quit && !_interrupted; ++i) {
const uint32_t timestamp = _stub->getTimeStamp(); 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 shapeBg = READ_BE_UINT16(p + offset); offset += 2;
const int count = 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->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); _stub->updateScreen(0);
const int diff = 6 * TIMER_SLICE - (_stub->getTimeStamp() - timestamp); const int diff = 6 * TIMER_SLICE - (_stub->getTimeStamp() - timestamp);
_stub->sleep((diff < 16) ? 16 : diff); _stub->sleep((diff < 16) ? 16 : diff);

View File

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

View File

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

36
game.h
View File

@ -145,9 +145,9 @@ struct Game {
// pieges // pieges
bool _pge_playAnimSound; bool _pge_playAnimSound;
GroupPGE _pge_groups[256]; MessagePGE _pge_messages[256];
GroupPGE *_pge_groupsTable[256]; MessagePGE *_pge_messagesTable[256]; // indexed by pge number
GroupPGE *_pge_nextFreeGroup; MessagePGE *_pge_nextFreeMessage;
LivePGE *_pge_liveTable2[256]; // active pieges list (index = pge number) LivePGE *_pge_liveTable2[256]; // active pieges list (index = pge number)
LivePGE *_pge_liveTable1[256]; // pieges list by room (index = room) LivePGE *_pge_liveTable1[256]; // pieges list by room (index = room)
LivePGE _pgeLive[256]; LivePGE _pgeLive[256];
@ -160,12 +160,12 @@ struct Game {
uint16_t _pge_compareVar1; uint16_t _pge_compareVar1;
uint16_t _pge_compareVar2; uint16_t _pge_compareVar2;
void pge_resetGroups(); void pge_resetMessages();
void pge_removeFromGroup(uint8_t idx); void pge_clearMessages(uint8_t pge_index);
int pge_isInGroup(LivePGE *pge_dst, uint16_t group_id, uint16_t counter); int pge_hasMessageData(LivePGE *pge, uint16_t msg_num, uint16_t counter) const;
void pge_loadForCurrentLevel(uint16_t idx); void pge_loadForCurrentLevel(uint16_t idx);
void pge_process(LivePGE *pge); 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_playAnimSound(LivePGE *pge, uint16_t arg2);
void pge_setupAnim(LivePGE *pge); void pge_setupAnim(LivePGE *pge);
int pge_execute(LivePGE *live_pge, InitPGE *init_pge, const Object *obj); 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_collides0o0u(ObjectOpcodeArgs *args);
int pge_op_collides2o2u(ObjectOpcodeArgs *args); int pge_op_collides2o2u(ObjectOpcodeArgs *args);
int pge_op_collides2u2o(ObjectOpcodeArgs *args); int pge_op_collides2u2o(ObjectOpcodeArgs *args);
int pge_op_isInGroup(ObjectOpcodeArgs *args); int pge_hasPiegeSentMessage(ObjectOpcodeArgs *args);
int pge_op_updateGroup0(ObjectOpcodeArgs *args); int pge_op_sendMessageData0(ObjectOpcodeArgs *args);
int pge_op_updateGroup1(ObjectOpcodeArgs *args); int pge_op_sendMessageData1(ObjectOpcodeArgs *args);
int pge_op_updateGroup2(ObjectOpcodeArgs *args); int pge_op_sendMessageData2(ObjectOpcodeArgs *args);
int pge_op_updateGroup3(ObjectOpcodeArgs *args); int pge_op_sendMessageData3(ObjectOpcodeArgs *args);
int pge_op_isPiegeDead(ObjectOpcodeArgs *args); int pge_op_isPiegeDead(ObjectOpcodeArgs *args);
int pge_op_collides1u2o(ObjectOpcodeArgs *args); int pge_op_collides1u2o(ObjectOpcodeArgs *args);
int pge_op_collides1u1o(ObjectOpcodeArgs *args); int pge_op_collides1u1o(ObjectOpcodeArgs *args);
@ -230,10 +230,10 @@ struct Game {
int pge_op_isInpMod(ObjectOpcodeArgs *args); int pge_op_isInpMod(ObjectOpcodeArgs *args);
int pge_op_setCollisionState1(ObjectOpcodeArgs *args); int pge_op_setCollisionState1(ObjectOpcodeArgs *args);
int pge_op_setCollisionState0(ObjectOpcodeArgs *args); int pge_op_setCollisionState0(ObjectOpcodeArgs *args);
int pge_op_isInGroup1(ObjectOpcodeArgs *args); int pge_hasMessageData0(ObjectOpcodeArgs *args);
int pge_op_isInGroup2(ObjectOpcodeArgs *args); int pge_hasMessageData1(ObjectOpcodeArgs *args);
int pge_op_isInGroup3(ObjectOpcodeArgs *args); int pge_hasMessageData2(ObjectOpcodeArgs *args);
int pge_op_isInGroup4(ObjectOpcodeArgs *args); int pge_hasMessageData3(ObjectOpcodeArgs *args);
int pge_o_unk0x3C(ObjectOpcodeArgs *args); int pge_o_unk0x3C(ObjectOpcodeArgs *args);
int pge_o_unk0x3D(ObjectOpcodeArgs *args); int pge_o_unk0x3D(ObjectOpcodeArgs *args);
int pge_op_setPiegeCounter(ObjectOpcodeArgs *args); int pge_op_setPiegeCounter(ObjectOpcodeArgs *args);
@ -280,7 +280,7 @@ struct Game {
int pge_op_setCollisionState2(ObjectOpcodeArgs *args); int pge_op_setCollisionState2(ObjectOpcodeArgs *args);
int pge_op_saveState(ObjectOpcodeArgs *args); int pge_op_saveState(ObjectOpcodeArgs *args);
int pge_o_unk0x6A(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_o_unk0x6C(ObjectOpcodeArgs *args);
int pge_op_isCollidingObject(ObjectOpcodeArgs *args); int pge_op_isCollidingObject(ObjectOpcodeArgs *args);
int pge_o_unk0x6E(ObjectOpcodeArgs *args); int pge_o_unk0x6E(ObjectOpcodeArgs *args);
@ -319,7 +319,7 @@ struct Game {
void pge_addToInventory(LivePGE *pge1, LivePGE *pge2, LivePGE *pge3); void pge_addToInventory(LivePGE *pge1, LivePGE *pge2, LivePGE *pge3);
int pge_updateCollisionState(LivePGE *pge, int16_t pge_dy, uint8_t var8); 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); 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); void pge_removeFromInventory(LivePGE *pge1, LivePGE *pge2, LivePGE *pge3);
int pge_ZOrderByAnimY(LivePGE *pge1, LivePGE *pge2, uint8_t comp, uint8_t comp2); 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); 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_serrure_cutscene;
bool play_carte_cutscene; bool play_carte_cutscene;
bool play_gamesaved_sound; bool play_gamesaved_sound;
bool restore_memo_cutscene;
}; };
struct Color { struct Color {
@ -171,7 +172,7 @@ struct InitPGE {
int16_t pos_y; int16_t pos_y;
uint16_t obj_node_number; uint16_t obj_node_number;
uint16_t life; uint16_t life;
int16_t counter_values[4]; // messages int16_t counter_values[4]; // data
uint8_t object_type; uint8_t object_type;
uint8_t init_room; uint8_t init_room;
uint8_t room_location; uint8_t room_location;
@ -181,7 +182,7 @@ struct InitPGE {
uint8_t object_id; uint8_t object_id;
uint8_t skill; uint8_t skill;
uint8_t mirror_x; uint8_t mirror_x;
uint8_t flags; uint8_t flags; // 1:xflip 4:active
uint8_t unk1C; // collidable, collision_data_len uint8_t unk1C; // collidable, collision_data_len
uint16_t text_num; uint16_t text_num;
}; };
@ -206,10 +207,10 @@ struct LivePGE {
InitPGE *init_PGE; InitPGE *init_PGE;
}; };
struct GroupPGE { struct MessagePGE {
GroupPGE *next_entry; MessagePGE *next_entry;
uint16_t index; uint16_t index; // src_pge
uint16_t group_id; uint16_t msg_num;
}; };
struct Object { struct Object {

View File

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

View File

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

View File

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

200
piege.cpp
View File

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

View File

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

View File

@ -318,6 +318,7 @@ struct Resource {
uint8_t *findBankData(uint16_t num); uint8_t *findBankData(uint16_t num);
uint8_t *loadBankData(uint16_t num); uint8_t *loadBankData(uint16_t num);
uint8_t *decodeResourceMacText(const char *name, const char *suffix);
uint8_t *decodeResourceMacData(const char *name, bool decompressLzss); uint8_t *decodeResourceMacData(const char *name, bool decompressLzss);
void MAC_decodeImageData(const uint8_t *ptr, int i, DecodeBuffer *dst); void MAC_decodeImageData(const uint8_t *ptr, int i, DecodeBuffer *dst);
void MAC_decodeDataCLUT(const uint8_t *ptr); 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"; const char *ResourceMac::FILENAME2 = "Flashback.rsrc";
ResourceMac::ResourceMac(const char *filePath, FileSystem *fs) 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)); memset(&_map, 0, sizeof(_map));
_f.open(filePath, "rb", fs); _f.open(filePath, "rb", fs);
} }
@ -66,6 +66,9 @@ void ResourceMac::loadResourceFork(uint32_t resourceOffset, uint32_t dataSize) {
_f.read(_types[i].id, 4); _f.read(_types[i].id, 4);
_types[i].count = _f.readUint16BE() + 1; _types[i].count = _f.readUint16BE() + 1;
_types[i].startOffset = _f.readUint16BE(); _types[i].startOffset = _f.readUint16BE();
if (_sndIndex < 0 && memcmp(_types[i].id, "snd ", 4) == 0) {
_sndIndex = i;
}
} }
_entries = (ResourceMacEntry **)calloc(_map.typesCount, sizeof(ResourceMacEntry *)); _entries = (ResourceMacEntry **)calloc(_map.typesCount, sizeof(ResourceMacEntry *));
for (int i = 0; i < _map.typesCount; ++i) { for (int i = 0; i < _map.typesCount; ++i) {

View File

@ -39,6 +39,7 @@ struct ResourceMac {
ResourceMacMap _map; ResourceMacMap _map;
ResourceMacType *_types; ResourceMacType *_types;
ResourceMacEntry **_entries; ResourceMacEntry **_entries;
int _sndIndex;
ResourceMac(const char *filePath, FileSystem *); ResourceMac(const char *filePath, FileSystem *);
~ResourceMac(); ~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 'Game saved' sample when saving with level checkpoints (as in the 3DO version)
play_gamesaved_sound=true play_gamesaved_sound=true
# restore content from 'MEMO' cutscene
restore_memo_cutscene=true

View File

@ -2,39 +2,28 @@
#include "screenshot.h" #include "screenshot.h"
#include "file.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 kTgaImageTypeUncompressedTrueColor 2
#define kTgaImageTypeRunLengthEncodedTrueColor 10 #define kTgaImageTypeRunLengthEncodedTrueColor 10
#define kTgaDirectionTop (1 << 5) #define kTgaDirectionTop (1 << 5)
static const int TGA_HEADER_SIZE = 18;
void saveTGA(const char *filename, const uint8_t *rgba, int w, int h) { void saveTGA(const char *filename, const uint8_t *rgba, int w, int h) {
static const uint8_t kImageType = kTgaImageTypeRunLengthEncodedTrueColor; 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; File f;
if (f.open(filename, "wb", ".")) { 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) { if (kImageType == kTgaImageTypeUncompressedTrueColor) {
for (int i = 0; i < w * h; ++i) { for (int i = 0; i < w * h; ++i) {
f.writeByte(rgba[0]); f.writeByte(rgba[0]);

View File

@ -25,8 +25,8 @@ const Cutscene::OpcodeStub Cutscene::_opcodeTable[] = {
&Cutscene::op_drawShapeScale, &Cutscene::op_drawShapeScale,
&Cutscene::op_drawShapeScaleRotate, &Cutscene::op_drawShapeScaleRotate,
/* 0x0C */ /* 0x0C */
&Cutscene::op_drawCreditsText, &Cutscene::op_copyScreen,
&Cutscene::op_drawStringAtPos, &Cutscene::op_drawTextAtPos,
&Cutscene::op_handleKeys &Cutscene::op_handleKeys
}; };
@ -3535,12 +3535,12 @@ const Game::pge_OpcodeProc Game::_pge_opcodeTable[] = {
/* 0x20 */ /* 0x20 */
&Game::pge_op_collides2o2u, &Game::pge_op_collides2o2u,
&Game::pge_op_collides2u2o, &Game::pge_op_collides2u2o,
&Game::pge_op_isInGroup, &Game::pge_hasPiegeSentMessage,
&Game::pge_op_updateGroup0, &Game::pge_op_sendMessageData0,
/* 0x24 */ /* 0x24 */
&Game::pge_op_updateGroup1, &Game::pge_op_sendMessageData1,
&Game::pge_op_updateGroup2, &Game::pge_op_sendMessageData2,
&Game::pge_op_updateGroup3, &Game::pge_op_sendMessageData3,
&Game::pge_op_isPiegeDead, &Game::pge_op_isPiegeDead,
/* 0x28 */ /* 0x28 */
&Game::pge_op_collides1u2o, &Game::pge_op_collides1u2o,
@ -3563,10 +3563,10 @@ const Game::pge_OpcodeProc Game::_pge_opcodeTable[] = {
&Game::pge_op_setCollisionState1, &Game::pge_op_setCollisionState1,
&Game::pge_op_setCollisionState0, &Game::pge_op_setCollisionState0,
/* 0x38 */ /* 0x38 */
&Game::pge_op_isInGroup1, &Game::pge_hasMessageData0,
&Game::pge_op_isInGroup2, &Game::pge_hasMessageData1,
&Game::pge_op_isInGroup3, &Game::pge_hasMessageData2,
&Game::pge_op_isInGroup4, &Game::pge_hasMessageData3,
/* 0x3C */ /* 0x3C */
&Game::pge_o_unk0x3C, &Game::pge_o_unk0x3C,
&Game::pge_o_unk0x3D, &Game::pge_o_unk0x3D,
@ -3626,7 +3626,7 @@ const Game::pge_OpcodeProc Game::_pge_opcodeTable[] = {
&Game::pge_op_setCollisionState2, &Game::pge_op_setCollisionState2,
&Game::pge_op_saveState, &Game::pge_op_saveState,
&Game::pge_o_unk0x6A, &Game::pge_o_unk0x6A,
&Game::pge_op_isInGroupSlice, &Game::pge_isToggleable,
/* 0x6C */ /* 0x6C */
&Game::pge_o_unk0x6C, &Game::pge_o_unk0x6C,
&Game::pge_op_isCollidingObject, &Game::pge_op_isCollidingObject,
@ -6187,6 +6187,89 @@ const uint8_t Cutscene::_caillouSetData[] = {
0x06, 0x66, 0x04, 0x44, 0x02, 0x22, 0x04, 0x66, 0x00 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[] = { const uint8_t Menu::_flagEn16x12[] = {
0x73, 0x00, 0x19, 0x8e, 0x00, 0x00, 0x82, 0x91, 0x9d, 0x4e, 0x4f, 0xad, 0x00, 0x00, 0x89, 0x00, 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, 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) { 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) { if (fullscreen == _fullscreen && factor == _scaleFactor) {
// no change // no change
return; return;

10
util.h
View File

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