From 222984d8513af328bf2017722cbcec22f05f6a28 Mon Sep 17 00:00:00 2001 From: Gregory Montoir Date: Mon, 28 Oct 2019 00:00:00 +0800 Subject: [PATCH] Import 0.4.4 --- Makefile | 2 +- README.txt | 6 +- cutscene.cpp | 61 +++++++++++--------- cutscene.h | 2 +- decode_mac.cpp | 141 +++++++++++++++++++++++++++++++++++++++++++++++ decode_mac.h | 23 ++++++++ game.cpp | 89 +++++++++++++++++++++++------- game.h | 10 +++- graphics.cpp | 15 ++--- intern.h | 34 ++++++------ main.cpp | 8 ++- menu.cpp | 19 ++----- menu.h | 1 + mixer.cpp | 10 ++++ mod_player.cpp | 22 ++------ piege.cpp | 4 +- resource.cpp | 4 ++ resource.h | 2 + resource_mac.cpp | 103 ++++++++++++++++++++++++++++++++++ resource_mac.h | 53 ++++++++++++++++++ sfx_player.cpp | 17 ++---- sfx_player.h | 4 +- staticres.cpp | 10 ++++ video.cpp | 19 +++---- video.h | 2 +- 25 files changed, 526 insertions(+), 135 deletions(-) create mode 100644 decode_mac.cpp create mode 100644 decode_mac.h create mode 100644 resource_mac.cpp create mode 100644 resource_mac.h diff --git a/Makefile b/Makefile index a3c6013..9e5d737 100644 --- a/Makefile +++ b/Makefile @@ -7,7 +7,7 @@ MODPLUG_LIBS := -lmodplug TREMOR_LIBS := -lvorbisidec -logg ZLIB_LIBS := -lz -CXXFLAGS += -O2 -Wall -MMD $(SDL_CFLAGS) -DUSE_MODPLUG -DUSE_STATIC_SCALER -DUSE_TREMOR -DUSE_ZLIB +CXXFLAGS += -Wall -Wpedantic -MMD $(SDL_CFLAGS) -DUSE_MODPLUG -DUSE_STATIC_SCALER -DUSE_TREMOR -DUSE_ZLIB SRCS = collision.cpp cutscene.cpp decode_mac.cpp dynlib.cpp file.cpp fs.cpp game.cpp graphics.cpp main.cpp \ menu.cpp mixer.cpp mod_player.cpp ogg_player.cpp piege.cpp resource.cpp resource_aba.cpp \ diff --git a/README.txt b/README.txt index a65fe8f..ebe078e 100644 --- a/README.txt +++ b/README.txt @@ -1,6 +1,6 @@ REminiscence README -Release version: 0.4.3 +Release version: 0.4.4 ------------------------------------------------------------------------------- @@ -46,6 +46,7 @@ These paths can be changed using command line switches : --widescreen=MODE 16:9 display --scaler=NAME@X Graphics scaler (default 'scale@3') --language=LANG Language (fr,en,de,sp,it,jp) + --autosave Save game state automatically The scaler option specifies the algorithm used to smoothen the image in addition to a scaling factor. External scalers are also supported, the suffix @@ -53,7 +54,7 @@ shall be used as the name. Eg. If you have scaler_xbrz.dll, you can pass '--scaler xbrz@2' to use that algorithm with a doubled window size (512x448). The widescreen option accepts two modes : - 'adjacent' : left and right rooms bitmaps will be drawn (default) + 'adjacent' : left and right rooms bitmaps will be drawn 'mirror' : the current room bitmap will be drawn mirrored In-game hotkeys : @@ -83,7 +84,6 @@ Credits: Delphine Software, obviously, for making another great game. Yaz0r, Pixel and gawd for sharing information they gathered on the game. -Nicolas Bondoux for sound fixes. Contact: diff --git a/cutscene.cpp b/cutscene.cpp index 7d7cea8..8c689d0 100644 --- a/cutscene.cpp +++ b/cutscene.cpp @@ -112,12 +112,12 @@ static bool isNewLineChar(uint8_t chr, Resource *res) { return chr == nl; } -uint16_t Cutscene::findTextSeparators(const uint8_t *p) { +uint16_t Cutscene::findTextSeparators(const uint8_t *p, int len) { uint8_t *q = _textSep; uint16_t ret = 0; uint16_t pos = 0; - for (; *p != 0xA && *p; ++p) { - if (isNewLineChar(*p, _res)) { + for (int i = 0; i < len && p[i] != 0xA; ++i) { + if (isNewLineChar(p[i], _res)) { *q++ = pos; if (pos > ret) { ret = pos; @@ -137,41 +137,43 @@ uint16_t Cutscene::findTextSeparators(const uint8_t *p) { void Cutscene::drawText(int16_t x, int16_t y, const uint8_t *p, uint16_t color, uint8_t *page, int textJustify) { debug(DBG_CUT, "Cutscene::drawText(x=%d, y=%d, c=%d, justify=%d)", x, y, color, textJustify); + int len = 0; if (_res->_type == kResourceTypeMac) { - warning("Unhandled Cutscene::drawText"); // TODO - return; + len = *p++; + } else { + len = strlen((const char *)p); } Video::drawCharFunc dcf = _vid->_drawChar; const uint8_t *fnt = (_res->_lang == LANG_JP) ? Video::_font8Jp : _res->_fnt; - uint16_t last_sep = 0; + uint16_t lastSep = 0; if (textJustify != kTextJustifyLeft) { - last_sep = findTextSeparators(p); + lastSep = findTextSeparators(p, len); if (textJustify != kTextJustifyCenter) { - last_sep = (_res->_lang == LANG_JP) ? 20 : 30; + lastSep = (_res->_lang == LANG_JP) ? 20 : 30; } } const uint8_t *sep = _textSep; y += 50; x += (_res->_lang == LANG_JP) ? 0 : 8; - int16_t yy = y; - int16_t xx = x; + int16_t yPos = y; + int16_t xPos = x; if (textJustify != kTextJustifyLeft) { - xx += ((last_sep - *sep++) / 2) * Video::CHAR_W; + xPos += ((lastSep - *sep++) / 2) * Video::CHAR_W; } - for (; *p != 0xA && *p; ++p) { - if (isNewLineChar(*p, _res)) { - yy += Video::CHAR_H; - xx = x; + for (int i = 0; i < len && p[i] != 0xA; ++i) { + if (isNewLineChar(p[i], _res)) { + yPos += Video::CHAR_H; + xPos = x; if (textJustify != kTextJustifyLeft) { - xx += ((last_sep - *sep++) / 2) * Video::CHAR_W; + xPos += ((lastSep - *sep++) / 2) * Video::CHAR_W; } - } else if (*p == 0x20) { - xx += Video::CHAR_W; - } else if (*p == 0x9) { + } else if (p[i] == 0x20) { + xPos += Video::CHAR_W; + } else if (p[i] == 0x9) { // ignore tab } else { - (_vid->*dcf)(page, _vid->_w, xx, yy, fnt, color, *p); - xx += Video::CHAR_W; + (_vid->*dcf)(page, _vid->_w, xPos, yPos, fnt, color, p[i]); + xPos += Video::CHAR_W; } } } @@ -415,9 +417,12 @@ void Cutscene::op_drawStringAtBottom() { } } - memset(_pageC + 179 * _vid->_w, 0xC0, 45 * _vid->_w); - memset(_page1 + 179 * _vid->_w, 0xC0, 45 * _vid->_w); - memset(_page0 + 179 * _vid->_w, 0xC0, 45 * _vid->_w); + 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); if (strId != 0xFFFF) { const uint8_t *str = _res->getCineString(strId); if (str) { @@ -878,7 +883,7 @@ void Cutscene::op_drawStringAtPos() { // '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, 256); + _stub->copyRect(0, 0, _vid->_w, _vid->_h, _page1, _vid->_w); _stub->updateScreen(0); } else { _stub->sleep(15); @@ -1059,9 +1064,7 @@ void Cutscene::unload() { void Cutscene::prepare() { _page0 = _vid->_frontLayer; _page1 = _vid->_tempLayer; - memset(_page1, 0, _vid->_layerSize); _pageC = _vid->_tempLayer2; - memset(_pageC, 0, _vid->_layerSize); _stub->_pi.dirMask = 0; _stub->_pi.enter = false; _stub->_pi.space = false; @@ -1080,6 +1083,10 @@ void Cutscene::prepare() { } void Cutscene::playCredits() { + if (_res->isMac()) { + warning("Cutscene::playCredits() unimplemented"); + return; + } _textCurPtr = _res->isAmiga() ? _creditsDataAmiga : _creditsDataDOS; _textBuf[0] = 0xA; _textCurBuf = _textBuf; diff --git a/cutscene.h b/cutscene.h index 972d962..04dac55 100644 --- a/cutscene.h +++ b/cutscene.h @@ -113,7 +113,7 @@ struct Cutscene { void updatePalette(); void setPalette(); void setRotationTransform(uint16_t a, uint16_t b, uint16_t c); - uint16_t findTextSeparators(const uint8_t *p); + 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 drawCreditsText(); diff --git a/decode_mac.cpp b/decode_mac.cpp new file mode 100644 index 0000000..cf18bf9 --- /dev/null +++ b/decode_mac.cpp @@ -0,0 +1,141 @@ + +#include +#include +#include +#include "decode_mac.h" +#include "file.h" + +uint8_t *decodeLzss(File &f, uint32_t &decodedSize) { + decodedSize = f.readUint32BE(); + uint8_t *dst = (uint8_t *)malloc(decodedSize); + uint32_t count = 0; + while (count < decodedSize) { + const int code = f.readByte(); + for (int i = 0; i < 8 && count < decodedSize; ++i) { + if ((code & (1 << i)) == 0) { + dst[count++] = f.readByte(); + } else { + int offset = f.readUint16BE(); + const int len = (offset >> 12) + 3; + offset &= 0xFFF; + for (int j = 0; j < len; ++j) { + dst[count + j] = dst[count - offset - 1 + j]; + } + count += len; + } + } + } + assert(count == decodedSize); + return dst; +} + +static void setPixel(int x, int y, int w, int h, uint8_t color, DecodeBuffer *buf) { + buf->setPixel(buf, x, y, w, h, color); +} + +void decodeC103(const uint8_t *a3, int w, int h, DecodeBuffer *buf) { + uint8_t d0; + int d3 = 0; + int d7 = 1; + int d6 = 0; + int d1 = 0; + static const uint32_t d5 = 0xFFF; + uint8_t a1[0x1000]; + + for (int y = 0; y < h; ++y) { + for (int x = 0; x < w; ++x) { + assert(d6 >= 0); + if (d6 == 0) { + int carry = d7 & 1; + d7 >>= 1; + if (d7 == 0) { + d7 = *a3++; + const int extended_bit = carry ? 0x80 : 0; + carry = d7 & 1; + d7 = extended_bit | (d7 >> 1); + } + if (!carry) { + d0 = *a3++; + a1[d3] = d0; + ++d3; + d3 &= d5; + setPixel(x, y, w, h, d0, buf); + continue; + } + d1 = READ_BE_UINT16(a3); a3 += 2; + d6 = d1; + d1 &= d5; + ++d1; + d1 = (d3 - d1) & d5; + d6 >>= 12; + d6 += 3; + } + d0 = a1[d1++]; + d1 &= d5; + a1[d3++] = d0; + d3 &= d5; + setPixel(x, y, w, h, d0, buf); + --d6; + } + } +} + +void decodeC211(const uint8_t *a3, int w, int h, DecodeBuffer *buf) { + struct { + const uint8_t *ptr; + int repeatCount; + } stack[512]; + int y = 0; + int x = 0; + int sp = 0; + + while (1) { + uint8_t d0 = *a3++; + if ((d0 & 0x80) != 0) { + ++y; + x = 0; + } + int d1 = d0 & 0x1F; + if (d1 == 0) { + d1 = READ_BE_UINT16(a3); a3 += 2; + } + const int carry_set = (d0 & 0x40) != 0; + d0 <<= 2; + if (!carry_set) { + if ((d0 & 0x80) == 0) { + --d1; + if (d1 == 0) { + assert(sp > 0); + --stack[sp - 1].repeatCount; + if (stack[sp - 1].repeatCount >= 0) { + a3 = stack[sp - 1].ptr; + } else { + --sp; + } + } else { + assert(sp < ARRAYSIZE(stack)); + stack[sp].ptr = a3; + stack[sp].repeatCount = d1; + ++sp; + } + } else { + x += d1; + } + } else { + if ((d0 & 0x80) == 0) { + if (d1 == 1) { + return; + } + const uint8_t color = *a3++; + for (int i = 0; i < d1; ++i) { + setPixel(x++, y, w, h, color, buf); + } + } else { + for (int i = 0; i < d1; ++i) { + setPixel(x++, y, w, h, *a3++, buf); + } + } + } + } +} + diff --git a/decode_mac.h b/decode_mac.h new file mode 100644 index 0000000..280b548 --- /dev/null +++ b/decode_mac.h @@ -0,0 +1,23 @@ + +#ifndef DECODE_MAC_H__ +#define DECODE_MAC_H__ + +#include +#include "file.h" + +uint8_t *decodeLzss(File &f, uint32_t &decodedSize); + +struct DecodeBuffer { + uint8_t *ptr; + int w, h, pitch; + int x, y; + bool xflip; + + void (*setPixel)(DecodeBuffer *buf, int src_x, int src_y, int src_w, int src_h, uint8_t color); + void *dataPtr; +}; + +void decodeC103(const uint8_t *a3, int w, int h, DecodeBuffer *buf); +void decodeC211(const uint8_t *a3, int w, int h, DecodeBuffer *buf); + +#endif diff --git a/game.cpp b/game.cpp index ea67cf3..3260715 100644 --- a/game.cpp +++ b/game.cpp @@ -14,7 +14,7 @@ #include "unpack.h" #include "util.h" -Game::Game(SystemStub *stub, FileSystem *fs, const char *savePath, int level, ResourceType ver, Language lang, WidescreenMode widescreenMode) +Game::Game(SystemStub *stub, FileSystem *fs, const char *savePath, int level, ResourceType ver, Language lang, WidescreenMode widescreenMode, bool autoSave) : _cut(&_res, stub, &_vid), _menu(&_res, stub, &_vid), _mix(fs, stub), _res(fs, ver, lang), _seq(stub, &_mix), _vid(&_res, stub), _stub(stub), _fs(fs), _savePath(savePath) { @@ -24,6 +24,7 @@ Game::Game(SystemStub *stub, FileSystem *fs, const char *savePath, int level, Re _currentLevel = _menu._level = level; _demoBin = -1; _widescreenMode = widescreenMode; + _autoSave = autoSave; } void Game::run() { @@ -150,6 +151,7 @@ void Game::run() { resetGameState(); _endLoop = false; _frameTimestamp = _stub->getTimeStamp(); + _saveTimestamp = _frameTimestamp; while (!_stub->_pi.quit && !_endLoop) { mainLoop(); if (_demoBin != -1 && _inp_demPos >= _res._demLen) { @@ -166,8 +168,8 @@ void Game::run() { _stub->_pi.shift = false; // clear widescreen borders if (_stub->hasWidescreen()) { - _stub->copyRectLeftBorder(Video::GAMESCREEN_W, Video::GAMESCREEN_H, 0); - _stub->copyRectRightBorder(Video::GAMESCREEN_W, Video::GAMESCREEN_H, 0); + _stub->copyRectLeftBorder(_vid._w, _vid._h, 0); + _stub->copyRectRightBorder(_vid._w, _vid._h, 0); } } } @@ -271,10 +273,34 @@ void Game::displayTitleScreenMac(int num) { Color c; c.r = c.g = c.b = 0; _stub->setPaletteEntry(0, &c); + } else if (num == Menu::kMacTitleScreen_Flashback) { + _vid.setTextPalette(); + _vid._charShadowColor = 0xE0; } _stub->copyRect(0, 0, _vid._w, _vid._h, _vid._frontLayer, _vid._w); _stub->updateScreen(0); while (1) { + if (num == Menu::kMacTitleScreen_Flashback) { + static const uint8_t selectedColor = 0xE4; + static const uint8_t defaultColor = 0xE8; + for (int i = 0; i < 7; ++i) { + const char *str = Menu::_levelNames[i]; + _vid.drawString(str, 24, 24 + i * 16, (_currentLevel == i) ? selectedColor : defaultColor); + } + if (_stub->_pi.dirMask & PlayerInput::DIR_UP) { + _stub->_pi.dirMask &= ~PlayerInput::DIR_UP; + if (_currentLevel > 0) { + --_currentLevel; + } + } + if (_stub->_pi.dirMask & PlayerInput::DIR_DOWN) { + _stub->_pi.dirMask &= ~PlayerInput::DIR_DOWN; + if (_currentLevel < 6) { + ++_currentLevel; + } + } + _vid.updateScreen(); + } _stub->processEvents(); if (_stub->_pi.quit) { break; @@ -324,10 +350,10 @@ void Game::mainLoop() { playCutscene(0x41); _endLoop = true; } else { - if (_validSaveState) { - if (!loadGameState(0)) { - _endLoop = true; - } + if (_autoSave && loadGameState(kAutoSaveSlot)) { + // autosave + } else if (_validSaveState && loadGameState(kIngameSaveSlot)) { + // ingame save } else { loadLevelData(); resetGameState(); @@ -393,6 +419,13 @@ void Game::mainLoop() { } } inp_handleSpecialKeys(); + if (_stub->getTimeStamp() - _saveTimestamp >= kAutoSaveIntervalMs) { + // do not save if we just died + if (_pgeLive[0].life > 0) { + saveGameState(kAutoSaveSlot); + _saveTimestamp = _stub->getTimeStamp(); + } + } } void Game::updateTiming() { @@ -474,7 +507,7 @@ void Game::playCutscene(int id) { _cut.play(); } } - if (_res._type == kResourceTypeMac) { + if (_res._type == kResourceTypeMac && !(id == 0x48 || id == 0x49)) { // continue or score screens // restore palette entries modified by the cutscene player (0xC and 0xD) Color palette[32]; _res.MAC_copyClut16(palette, 0, 0x37); @@ -533,8 +566,8 @@ void Game::drawCurrentInventoryItem() { void Game::showFinalScore() { if (_stub->hasWidescreen()) { - _stub->copyRectLeftBorder(Video::GAMESCREEN_W, Video::GAMESCREEN_H, 0); - _stub->copyRectRightBorder(Video::GAMESCREEN_W, Video::GAMESCREEN_H, 0); + _stub->copyRectLeftBorder(_vid._w, _vid._h, 0); + _stub->copyRectRightBorder(_vid._w, _vid._h, 0); } playCutscene(0x49); char buf[50]; @@ -697,8 +730,8 @@ bool Game::handleConfigPanel() { bool Game::handleContinueAbort() { if (_stub->hasWidescreen()) { - _stub->copyRectLeftBorder(Video::GAMESCREEN_W, Video::GAMESCREEN_H, 0); - _stub->copyRectRightBorder(Video::GAMESCREEN_W, Video::GAMESCREEN_H, 0); + _stub->copyRectLeftBorder(_vid._w, _vid._h, 0); + _stub->copyRectRightBorder(_vid._w, _vid._h, 0); } playCutscene(0x48); int timeout = 100; @@ -1362,7 +1395,7 @@ void Game::drawObjectFrame(const uint8_t *bankDataPtr, const uint8_t *dataPtr, i _vid.drawSpriteSub4(src, _vid._frontLayer + dst_offset, sprite_w, sprite_clipped_h, sprite_clipped_w, sprite_col_mask); } } - _vid.markBlockAsDirty(sprite_x, sprite_y, sprite_clipped_w, sprite_clipped_h); + _vid.markBlockAsDirty(sprite_x, sprite_y, sprite_clipped_w, sprite_clipped_h, _vid._layerScale); } void Game::decodeCharacterFrame(const uint8_t *dataPtr, uint8_t *dstPtr) { @@ -1499,7 +1532,7 @@ void Game::drawCharacter(const uint8_t *dataPtr, int16_t pos_x, int16_t pos_y, u _vid.drawSpriteSub4(src, _vid._frontLayer + dst_offset, sprite_w, sprite_clipped_h, sprite_clipped_w, sprite_col_mask); } } - _vid.markBlockAsDirty(pos_x, pos_y, sprite_clipped_w, sprite_clipped_h); + _vid.markBlockAsDirty(pos_x, pos_y, sprite_clipped_w, sprite_clipped_h, _vid._layerScale); } int Game::loadMonsterSprites(LivePGE *pge) { @@ -1617,14 +1650,30 @@ void Game::loadLevelMap() { } } _vid.PC_decodeMap(_currentLevel, _currentRoom); - if (_stub->hasWidescreen() && _widescreenMode == kWidescreenMirrorRoom) { - _stub->copyRectMirrorBorders(Video::GAMESCREEN_W, Video::GAMESCREEN_H, _vid._backLayer); - } break; case kResourceTypeMac: + if (_stub->hasWidescreen() && _widescreenMode == kWidescreenAdjacentRooms) { + const int leftRoom = _res._ctData[CT_LEFT_ROOM + _currentRoom]; + if (leftRoom > 0 && hasLevelMap(_currentLevel, leftRoom)) { + _vid.MAC_decodeMap(_currentLevel, leftRoom); + _stub->copyRectLeftBorder(_vid._w, _vid._h, _vid._backLayer); + } else { + _stub->copyRectLeftBorder(_vid._w, _vid._h, 0); + } + const int rightRoom = _res._ctData[CT_RIGHT_ROOM + _currentRoom]; + if (rightRoom > 0 && hasLevelMap(_currentLevel, rightRoom)) { + _vid.MAC_decodeMap(_currentLevel, rightRoom); + _stub->copyRectRightBorder(_vid._w, _vid._h, _vid._backLayer); + } else { + _stub->copyRectRightBorder(_vid._w, _vid._h, 0); + } + } _vid.MAC_decodeMap(_currentLevel, _currentRoom); break; } + if (_stub->hasWidescreen() && _widescreenMode == kWidescreenMirrorRoom) { + _stub->copyRectMirrorBorders(_vid._w, _vid._h, _vid._backLayer); + } } void Game::loadLevelData() { @@ -1806,7 +1855,7 @@ void Game::drawIcon(uint8_t iconNum, int16_t x, int16_t y, uint8_t colMask) { return; } _vid.drawSpriteSub1(buf, _vid._frontLayer + x + y * _vid._w, 16, 16, 16, colMask << 4); - _vid.markBlockAsDirty(x, y, 16, 16); + _vid.markBlockAsDirty(x, y, 16, 16, _vid._layerScale); } void Game::playSound(uint8_t sfxId, uint8_t softVol) { @@ -2006,7 +2055,7 @@ static const uint32_t TAG_FBSV = 0x46425356; bool Game::saveGameState(uint8_t slot) { bool success = false; - char stateFile[20]; + char stateFile[32]; makeGameStateName(slot, stateFile); File f; if (!f.open(stateFile, "zwb", _savePath)) { @@ -2033,7 +2082,7 @@ bool Game::saveGameState(uint8_t slot) { bool Game::loadGameState(uint8_t slot) { bool success = false; - char stateFile[20]; + char stateFile[32]; makeGameStateName(slot, stateFile); File f; if (!f.open(stateFile, "zrb", _savePath)) { diff --git a/game.h b/game.h index 410510d..0987b01 100644 --- a/game.h +++ b/game.h @@ -25,6 +25,12 @@ struct Game { typedef int (Game::*col_Callback1)(LivePGE *, LivePGE *, int16_t, int16_t); typedef int (Game::*col_Callback2)(LivePGE *, int16_t, int16_t, int16_t); + enum { + kIngameSaveSlot = 0, + kAutoSaveSlot = 255, + kAutoSaveIntervalMs = 30 * 1000 + }; + enum { CT_UP_ROOM = 0x00, CT_DOWN_ROOM = 0x40, @@ -87,8 +93,10 @@ struct Game { bool _endLoop; uint32_t _frameTimestamp; WidescreenMode _widescreenMode; + bool _autoSave; + uint32_t _saveTimestamp; - Game(SystemStub *, FileSystem *, const char *savePath, int level, ResourceType ver, Language lang, WidescreenMode widescreenMode); + Game(SystemStub *, FileSystem *, const char *savePath, int level, ResourceType ver, Language lang, WidescreenMode widescreenMode, bool autoSave); void run(); void displayTitleScreenAmiga(); diff --git a/graphics.cpp b/graphics.cpp index 0cc9a34..cf10620 100644 --- a/graphics.cpp +++ b/graphics.cpp @@ -211,21 +211,18 @@ void Graphics::fillArea(uint8_t color, bool hasAlpha) { if (x1 >= 0) { if (hasAlpha && color > 0xC7) { do { - int16_t x2 = *pts++; - if (x2 < _crw && x2 >= x1) { - int len = x2 - x1 + 1; - for (int i = 0; i < len; ++i) { - *(dst + x1 + i) |= color & 8; // XXX 0x88 - } + const int16_t x2 = MIN(_crw - 1, *pts++); + for (; x1 <= x2; ++x1) { + *(dst + x1) |= color & 8; } dst += _layerPitch; x1 = *pts++; } while (x1 >= 0); } else { do { - int16_t x2 = *pts++; - if (x2 < _crw && x2 >= x1) { - int len = x2 - x1 + 1; + const int16_t x2 = MIN(_crw - 1, *pts++); + if (x1 <= x2) { + const int len = x2 - x1 + 1; memset(dst + x1, color, len); } dst += _layerPitch; diff --git a/intern.h b/intern.h index 7959e20..799cedd 100644 --- a/intern.h +++ b/intern.h @@ -13,12 +13,6 @@ #include #include -#undef ABS -#define ABS(x) ((x)<0?-(x):(x)) -#undef MAX -#define MAX(x,y) ((x)>(y)?(x):(y)) -#undef MIN -#define MIN(x,y) ((x)<(y)?(x):(y)) #undef ARRAYSIZE #define ARRAYSIZE(a) (int)(sizeof(a)/sizeof(a[0])) @@ -42,16 +36,6 @@ inline uint32_t READ_LE_UINT32(const void *ptr) { return (b[3] << 24) | (b[2] << 16) | (b[1] << 8) | b[0]; } -inline int8_t ADDC_S8(int a, int b) { - a += b; - if (a < -128) { - a = -128; - } else if (a > 127) { - a = 127; - } - return a; -} - inline int16_t ADDC_S16(int a, int b) { a += b; if (a < -32768) { @@ -79,6 +63,24 @@ inline T CLIP(const T& val, const T& a, const T& b) { return val; } +#undef MIN +template +inline T MIN(T v1, T v2) { + return (v1 < v2) ? v1 : v2; +} + +#undef MAX +template +inline T MAX(T v1, T v2) { + return (v1 > v2) ? v1 : v2; +} + +#undef ABS +template +inline T ABS(T t) { + return (t < 0) ? -t : t; +} + enum Language { LANG_FR, LANG_EN, diff --git a/main.cpp b/main.cpp index baf733a..34cf66e 100644 --- a/main.cpp +++ b/main.cpp @@ -25,6 +25,7 @@ static const char *USAGE = " --widescreen=MODE 16:9 display\n" " --scaler=NAME@X Graphics scaler (default 'scale@3')\n" " --language=LANG Language (fr,en,de,sp,it,jp)\n" + " --autosave Save game state automatically\n" ; static int detectVersion(FileSystem *fs) { @@ -215,6 +216,7 @@ int main(int argc, char *argv[]) { const char *savePath = "."; int levelNum = 0; bool fullscreen = false; + bool autoSave = false; WidescreenMode widescreen = kWidescreenNone; ScalerParameters scalerParameters = ScalerParameters::defaults(); int forcedLanguage = -1; @@ -234,6 +236,7 @@ int main(int argc, char *argv[]) { { "scaler", required_argument, 0, 5 }, { "language", required_argument, 0, 6 }, { "widescreen", required_argument, 0, 7 }, + { "autosave", no_argument, 0, 8 }, { 0, 0, 0, 0 } }; int index; @@ -281,6 +284,9 @@ int main(int argc, char *argv[]) { case 7: widescreen = parseWidescreen(optarg); break; + case 8: + autoSave = true; + break; default: printf(USAGE, argv[0]); return 0; @@ -296,7 +302,7 @@ int main(int argc, char *argv[]) { } const Language language = (forcedLanguage == -1) ? detectLanguage(&fs) : (Language)forcedLanguage; SystemStub *stub = SystemStub_SDL_create(); - Game *g = new Game(stub, &fs, savePath, levelNum, (ResourceType)version, language, widescreen); + Game *g = new Game(stub, &fs, savePath, levelNum, (ResourceType)version, language, widescreen, autoSave); stub->init(g_caption, g->_vid._w, g->_vid._h, fullscreen, widescreen != kWidescreenNone, &scalerParameters); g->run(); delete g; diff --git a/menu.cpp b/menu.cpp index 349d6bf..4d6655f 100644 --- a/menu.cpp +++ b/menu.cpp @@ -84,7 +84,7 @@ void Menu::drawString2(const char *str, int16_t y, int16_t x) { } break; } - _vid->markBlockAsDirty(x * w, y * h, len * w, h); + _vid->markBlockAsDirty(x * w, y * h, len * w, h, _vid->_layerScale); } void Menu::loadPicture(const char *prefix) { @@ -201,7 +201,7 @@ bool Menu::handlePasswordScreen() { } _vid->PC_drawChar(0x20, 21, len + 15); - _vid->markBlockAsDirty(15 * Video::CHAR_W, 21 * Video::CHAR_H, (len + 1) * Video::CHAR_W, Video::CHAR_H); + _vid->markBlockAsDirty(15 * Video::CHAR_W, 21 * Video::CHAR_H, (len + 1) * Video::CHAR_W, Video::CHAR_H, _vid->_layerScale); _vid->updateScreen(); _stub->sleep(EVENTS_DELAY); _stub->processEvents(); @@ -254,24 +254,15 @@ bool Menu::handleLevelScreen() { int currentSkill = _skill; int currentLevel = _level; do { - static const char *levelTitles[] = { - "Titan / The Jungle", - "Titan / New Washington", - "Titan / Death Tower Show", - "Earth / Surface", - "Earth / Paradise Club", - "Planet Morphs / Surface", - "Planet Morphs / Inner Core" - }; for (int i = 0; i < 7; ++i) { - drawString(levelTitles[i], 7 + i * 2, 4, (currentLevel == i) ? 2 : 3); + drawString(_levelNames[i], 7 + i * 2, 4, (currentLevel == i) ? 2 : 3); } - _vid->markBlockAsDirty(4 * Video::CHAR_W, 7 * Video::CHAR_H, 192, 7 * Video::CHAR_H); + _vid->markBlockAsDirty(4 * Video::CHAR_W, 7 * Video::CHAR_H, 192, 7 * Video::CHAR_H, _vid->_layerScale); drawString(_res->getMenuString(LocaleData::LI_13_EASY), 23, 4, (currentSkill == 0) ? 2 : 3); drawString(_res->getMenuString(LocaleData::LI_14_NORMAL), 23, 14, (currentSkill == 1) ? 2 : 3); drawString(_res->getMenuString(LocaleData::LI_15_EXPERT), 23, 24, (currentSkill == 2) ? 2 : 3); - _vid->markBlockAsDirty(4 * Video::CHAR_W, 23 * Video::CHAR_H, 192, Video::CHAR_H); + _vid->markBlockAsDirty(4 * Video::CHAR_W, 23 * Video::CHAR_H, 192, Video::CHAR_H, _vid->_layerScale); _vid->updateScreen(); _stub->sleep(EVENTS_DELAY); diff --git a/menu.h b/menu.h index bbf0d58..27c236a 100644 --- a/menu.h +++ b/menu.h @@ -48,6 +48,7 @@ struct Menu { int opt; }; + static const char *_levelNames[]; static const char *_passwordsDOS[]; static const char *_passwordsFrAmiga[]; static const char *_passwordsEnAmiga[]; diff --git a/mixer.cpp b/mixer.cpp index f647416..0f06185 100644 --- a/mixer.cpp +++ b/mixer.cpp @@ -139,6 +139,15 @@ void Mixer::stopMusic() { } } +static void nr(int16_t *buf, int len) { + static int prev = 0; + for (int i = 0; i < len; ++i) { + const int vnr = buf[i] >> 1; + buf[i] = vnr + prev; + prev = vnr; + } +} + void Mixer::mix(int16_t *out, int len) { if (_premixHook) { if (!_premixHook(_premixHookData, out, len)) { @@ -160,6 +169,7 @@ void Mixer::mix(int16_t *out, int len) { } } } + nr(out, len); } void Mixer::mixCallback(void *param, int16_t *buf, int len) { diff --git a/mod_player.cpp b/mod_player.cpp index 8ff6786..189630d 100644 --- a/mod_player.cpp +++ b/mod_player.cpp @@ -156,8 +156,7 @@ struct ModPlayer_impl { void applyVibrato(int trackNum); void applyPortamento(int trackNum); void handleEffect(int trackNum, bool tick); - void mixSamples(int8_t *buf, int len); - bool mixS8(int8_t *buf, int len); + void mixSamples(int16_t *buf, int len); bool mix(int16_t *buf, int len); }; @@ -584,11 +583,11 @@ void ModPlayer_impl::handleTick() { } } -void ModPlayer_impl::mixSamples(int8_t *buf, int samplesLen) { +void ModPlayer_impl::mixSamples(int16_t *buf, int samplesLen) { for (int i = 0; i < NUM_TRACKS; ++i) { Track *tk = &_tracks[i]; if (tk->sample != 0 && tk->delayCounter == 0) { - int8_t *mixbuf = buf; + int16_t *mixbuf = buf; SampleInfo *si = tk->sample; int len = si->len << FRAC_BITS; int loopLen = si->repeatLen << FRAC_BITS; @@ -614,7 +613,7 @@ void ModPlayer_impl::mixSamples(int8_t *buf, int samplesLen) { } while (count--) { const int out = si->getPCM(pos >> FRAC_BITS); - *mixbuf = ADDC_S8(*mixbuf, out * tk->volume / 64); + *mixbuf = ADDC_S16(*mixbuf, (out * tk->volume / 64) << 8); ++mixbuf; pos += deltaPos; } @@ -624,7 +623,8 @@ void ModPlayer_impl::mixSamples(int8_t *buf, int samplesLen) { } } -bool ModPlayer_impl::mixS8(int8_t *buf, int len) { +bool ModPlayer_impl::mix(int16_t *buf, int len) { + memset(buf, 0, sizeof(int16_t) * len); if (_playing) { const int samplesPerTick = _mixingRate / (50 * _songTempo / 125); while (len != 0) { @@ -644,16 +644,6 @@ bool ModPlayer_impl::mixS8(int8_t *buf, int len) { } return _playing; } - -bool ModPlayer_impl::mix(int16_t *samples, int len) { - int8_t buf[len]; - memset(buf, 0, sizeof(buf)); - const bool ret = mixS8(buf, len); - for (int i = 0; i < len; ++i) { - samples[i] = buf[i] << 8; - } - return ret; -} #endif ModPlayer::ModPlayer(Mixer *mixer, FileSystem *fs) diff --git a/piege.cpp b/piege.cpp index 83b0827..9120b9e 100644 --- a/piege.cpp +++ b/piege.cpp @@ -325,7 +325,7 @@ int Game::pge_execute(LivePGE *live_pge, InitPGE *init_pge, const Object *obj) { ++live_pge->life; } if (obj->flags & 8) { - live_pge->life = 0xFFFF; + live_pge->life = -1; } if (live_pge->flags & 1) { @@ -1428,7 +1428,7 @@ int Game::pge_op_setCollisionState2(ObjectOpcodeArgs *args) { int Game::pge_op_saveState(ObjectOpcodeArgs *args) { _saveStateCompleted = true; - _validSaveState = saveGameState(0); + _validSaveState = saveGameState(kIngameSaveSlot); return 0xFFFF; } diff --git a/resource.cpp b/resource.cpp index d9b2d75..0768652 100644 --- a/resource.cpp +++ b/resource.cpp @@ -1679,6 +1679,10 @@ void Resource::MAC_loadCutsceneText() { _cine_off = 0; // offsets are prepended to _cine_txt } +void Resource::MAC_loadCreditsText() { + _credits = decodeResourceMacData("Credit strings", false); +} + void Resource::MAC_loadSounds() { static const int8_t table[NUM_SFXS] = { 0, -1, 1, 2, 3, 4, -1, 5, 6, 7, 8, 9, 10, 11, -1, 12, diff --git a/resource.h b/resource.h index 29db4c3..de063e4 100644 --- a/resource.h +++ b/resource.h @@ -177,6 +177,7 @@ struct Resource { uint8_t *_perso; uint8_t *_monster; uint8_t *_str; + uint8_t *_credits; Resource(FileSystem *fs, ResourceType type, Language lang); ~Resource(); @@ -335,6 +336,7 @@ struct Resource { void MAC_unloadCutscene(); void MAC_loadCutscene(const char *cutscene); void MAC_loadCutsceneText(); + void MAC_loadCreditsText(); void MAC_loadSounds(); int MAC_getPersoFrame(int anim) const { diff --git a/resource_mac.cpp b/resource_mac.cpp new file mode 100644 index 0000000..40b7a2d --- /dev/null +++ b/resource_mac.cpp @@ -0,0 +1,103 @@ + +#include +#include +#include +#include "fs.h" +#include "resource_mac.h" +#include "util.h" + +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) { + memset(&_map, 0, sizeof(_map)); + _f.open(filePath, "rb", fs); +} + +ResourceMac::~ResourceMac() { + if (_entries) { + for (int i = 0; i < _map.typesCount; ++i) { + free(_entries[i]); + } + free(_entries); + } + free(_types); +} + +void ResourceMac::load() { + const uint32_t sig = _f.readUint32BE(); + if (sig == 0x00051607) { // AppleDouble + debug(DBG_INFO, "Load Macintosh data from AppleDouble"); + _f.seek(24); + const int count = _f.readUint16BE(); + for (int i = 0; i < count; ++i) { + const int id = _f.readUint32BE(); + const int offset = _f.readUint32BE(); + const int length = _f.readUint32BE(); + if (id == 2) { // resource fork + loadResourceFork(offset, length); + break; + } + } + } else { // MacBinary + debug(DBG_INFO, "Load Macintosh data from MacBinary"); + _f.seek(83); + uint32_t dataSize = _f.readUint32BE(); + uint32_t resourceOffset = 128 + ((dataSize + 127) & ~127); + loadResourceFork(resourceOffset, dataSize); + } +} + +void ResourceMac::loadResourceFork(uint32_t resourceOffset, uint32_t dataSize) { + _f.seek(resourceOffset); + _dataOffset = resourceOffset + _f.readUint32BE(); + uint32_t mapOffset = resourceOffset + _f.readUint32BE(); + + _f.seek(mapOffset + 22); + _f.readUint16BE(); + _map.typesOffset = _f.readUint16BE(); + _map.namesOffset = _f.readUint16BE(); + _map.typesCount = _f.readUint16BE() + 1; + + _f.seek(mapOffset + _map.typesOffset + 2); + _types = (ResourceMacType *)calloc(_map.typesCount, sizeof(ResourceMacType)); + for (int i = 0; i < _map.typesCount; ++i) { + _f.read(_types[i].id, 4); + _types[i].count = _f.readUint16BE() + 1; + _types[i].startOffset = _f.readUint16BE(); + } + _entries = (ResourceMacEntry **)calloc(_map.typesCount, sizeof(ResourceMacEntry *)); + for (int i = 0; i < _map.typesCount; ++i) { + _f.seek(mapOffset + _map.typesOffset + _types[i].startOffset); + _entries[i] = (ResourceMacEntry *)calloc(_types[i].count, sizeof(ResourceMacEntry)); + for (int j = 0; j < _types[i].count; ++j) { + _entries[i][j].id = _f.readUint16BE(); + _entries[i][j].nameOffset = _f.readUint16BE(); + _entries[i][j].dataOffset = _f.readUint32BE() & 0x00FFFFFF; + _f.readUint32BE(); + } + for (int j = 0; j < _types[i].count; ++j) { + _entries[i][j].name[0] = '\0'; + if (_entries[i][j].nameOffset != 0xFFFF) { + _f.seek(mapOffset + _map.namesOffset + _entries[i][j].nameOffset); + const int len = _f.readByte(); + assert(len < kResourceMacEntryNameLength - 1); + _f.read(_entries[i][j].name, len); + _entries[i][j].name[len] = '\0'; + } + } + } +} + +const ResourceMacEntry *ResourceMac::findEntry(const char *name) const { + for (int type = 0; type < _map.typesCount; ++type) { + for (int i = 0; i < _types[type].count; ++i) { + if (strcmp(name, _entries[type][i].name) == 0) { + return &_entries[type][i]; + } + } + } + return 0; +} + diff --git a/resource_mac.h b/resource_mac.h new file mode 100644 index 0000000..35634e7 --- /dev/null +++ b/resource_mac.h @@ -0,0 +1,53 @@ + +#ifndef RESOURCE_MAC_H__ +#define RESOURCE_MAC_H__ + +#include +#include "file.h" + +struct ResourceMacMap { + uint16_t typesOffset; + uint16_t namesOffset; + uint16_t typesCount; +}; + +struct ResourceMacType { + unsigned char id[4]; + uint16_t count; + uint16_t startOffset; +}; + +enum { + kResourceMacEntryNameLength = 64 +}; + +struct ResourceMacEntry { + uint16_t id; + uint16_t nameOffset; + uint32_t dataOffset; + char name[kResourceMacEntryNameLength]; +}; + +struct ResourceMac { + + static const char *FILENAME1; + static const char *FILENAME2; + + File _f; + + uint32_t _dataOffset; + ResourceMacMap _map; + ResourceMacType *_types; + ResourceMacEntry **_entries; + + ResourceMac(const char *filePath, FileSystem *); + ~ResourceMac(); + + bool isOpen() const { return _entries != 0; } + void load(); + void loadResourceFork(uint32_t offset, uint32_t size); + const ResourceMacEntry *findEntry(const char *name) const; +}; + +#endif + diff --git a/sfx_player.cpp b/sfx_player.cpp index 5de5c98..8bf3ac2 100644 --- a/sfx_player.cpp +++ b/sfx_player.cpp @@ -97,11 +97,11 @@ void SfxPlayer::handleTick() { } } -void SfxPlayer::mixSamples(int8_t *buf, int samplesLen) { +void SfxPlayer::mixSamples(int16_t *buf, int samplesLen) { for (int i = 0; i < NUM_CHANNELS; ++i) { SampleInfo *si = &_samples[i]; if (si->data) { - int8_t *mixbuf = buf; + int16_t *mixbuf = buf; int len = si->len << FRAC_BITS; int loopLen = si->loopLen << FRAC_BITS; int loopPos = si->loopPos << FRAC_BITS; @@ -127,7 +127,7 @@ void SfxPlayer::mixSamples(int8_t *buf, int samplesLen) { } while (count--) { const int out = si->getPCM(pos >> FRAC_BITS); - *mixbuf = ADDC_S8(*mixbuf, out * si->vol / 64); + *mixbuf = ADDC_S16(*mixbuf, (out * si->vol / 64) << 8); ++mixbuf; pos += deltaPos; } @@ -137,7 +137,8 @@ void SfxPlayer::mixSamples(int8_t *buf, int samplesLen) { } } -bool SfxPlayer::mix(int8_t *buf, int len) { +bool SfxPlayer::mix(int16_t *buf, int len) { + memset(buf, 0, sizeof(int16_t) * len); if (_playing) { const int samplesPerTick = _mix->getSampleRate() / 50; while (len != 0) { @@ -159,11 +160,5 @@ bool SfxPlayer::mix(int8_t *buf, int len) { } bool SfxPlayer::mixCallback(void *param, int16_t *samples, int len) { - int8_t buf[len]; - memset(buf, 0, sizeof(buf)); - const bool ret = ((SfxPlayer *)param)->mix(buf, len); - for (int i = 0; i < len; ++i) { - samples[i] = buf[i] << 8; - } - return ret; + return ((SfxPlayer *)param)->mix(samples, len); } diff --git a/sfx_player.h b/sfx_player.h index a402f04..5929ff0 100644 --- a/sfx_player.h +++ b/sfx_player.h @@ -81,9 +81,9 @@ struct SfxPlayer { void stop(); void playSample(int channel, const uint8_t *sampleData, uint16_t period); void handleTick(); - void mixSamples(int8_t *samples, int samplesLen); + void mixSamples(int16_t *samples, int samplesLen); - bool mix(int8_t *buf, int len); + bool mix(int16_t *buf, int len); static bool mixCallback(void *param, int16_t *buf, int len); }; diff --git a/staticres.cpp b/staticres.cpp index 0c666d6..2c2558f 100644 --- a/staticres.cpp +++ b/staticres.cpp @@ -3223,6 +3223,16 @@ const uint8_t Game::_protectionPal[] = { 0x08, 0x88, 0x09, 0x99, 0x0A, 0xAA, 0x0B, 0xBB, 0x0C, 0xCC, 0x0D, 0xDD, 0x0E, 0xEE, 0x0F, 0xFF }; +const char *Menu::_levelNames[] { + "Titan / The Jungle", + "Titan / New Washington", + "Titan / Death Tower Show", + "Earth / Surface", + "Earth / Paradise Club", + "Planet Morphs / Surface", + "Planet Morphs / Inner Core" +}; + const char *Menu::_passwordsDOS[] = { "JAGUAR", "COMBEL", "ANTIC", "NOLAN", "ARTHUR", "SHIRYU", "RENDER", "BELUGA", // easy "BANTHA", "SHIVA", "KASYYK", "SARLAC", "MAENOC", "SULUST", "NEPTUN", "BELUGA", // normal diff --git a/video.cpp b/video.cpp index 0363c3a..020112b 100644 --- a/video.cpp +++ b/video.cpp @@ -49,12 +49,12 @@ Video::~Video() { free(_screenBlocks); } -void Video::markBlockAsDirty(int16_t x, int16_t y, uint16_t w, uint16_t h) { +void Video::markBlockAsDirty(int16_t x, int16_t y, uint16_t w, uint16_t h, int scale) { debug(DBG_VIDEO, "Video::markBlockAsDirty(%d, %d, %d, %d)", x, y, w, h); - int bx1 = _layerScale * x / SCREENBLOCK_W; - int by1 = _layerScale * y / SCREENBLOCK_H; - int bx2 = _layerScale * (x + w - 1) / SCREENBLOCK_W; - int by2 = _layerScale * (y + h - 1) / SCREENBLOCK_H; + int bx1 = scale * x / SCREENBLOCK_W; + int by1 = scale * y / SCREENBLOCK_H; + int bx2 = scale * (x + w - 1) / SCREENBLOCK_W; + int by2 = scale * (y + h - 1) / SCREENBLOCK_H; if (bx1 < 0) { bx1 = 0; } @@ -907,7 +907,7 @@ static uint8_t _MAC_fontShadowColor; void Video::MAC_drawStringChar(uint8_t *dst, int pitch, int x, int y, const uint8_t *src, uint8_t color, uint8_t chr) { DecodeBuffer buf; memset(&buf, 0, sizeof(buf)); - buf.ptr = _frontLayer; + buf.ptr = dst; buf.w = buf.pitch = _w; buf.h = _h; buf.x = x * _layerScale; @@ -931,7 +931,7 @@ const char *Video::drawString(const char *str, int16_t x, int16_t y, uint8_t col (this->*_drawChar)(_frontLayer, _w, x + len * CHAR_W, y, fnt, col, c); ++len; } - markBlockAsDirty(x, y, len * CHAR_W, CHAR_H); + markBlockAsDirty(x, y, len * CHAR_W, CHAR_H, _layerScale); return str - 1; } @@ -940,7 +940,7 @@ void Video::drawStringLen(const char *str, int len, int x, int y, uint8_t color) for (int i = 0; i < len; ++i) { (this->*_drawChar)(_frontLayer, _w, x + i * CHAR_W, y, fnt, color, str[i]); } - markBlockAsDirty(x, y, len * CHAR_W, CHAR_H); + markBlockAsDirty(x, y, len * CHAR_W, CHAR_H, _layerScale); } Color Video::AMIGA_convertColor(const uint16_t color, bool bgr) { // 4bits to 8bits @@ -1052,7 +1052,6 @@ void Video::MAC_drawSprite(int x, int y, const uint8_t *data, int frame, bool xf buf.setPixel = eraseBackground ? MAC_drawBuffer : MAC_drawBufferMask; fixOffsetDecodeBuffer(&buf, dataPtr); _res->MAC_decodeImageData(data, frame, &buf); - // divide by screen scale as the dirty blocks range is 256,224 - markBlockAsDirty(buf.x / _layerScale, buf.y / _layerScale, READ_BE_UINT16(dataPtr) / _layerScale, READ_BE_UINT16(dataPtr + 2) / _layerScale); + markBlockAsDirty(buf.x, buf.y, READ_BE_UINT16(dataPtr), READ_BE_UINT16(dataPtr + 2), 1); } } diff --git a/video.h b/video.h index a58b32e..3be685b 100644 --- a/video.h +++ b/video.h @@ -53,7 +53,7 @@ struct Video { Video(Resource *res, SystemStub *stub); ~Video(); - void markBlockAsDirty(int16_t x, int16_t y, uint16_t w, uint16_t h); + void markBlockAsDirty(int16_t x, int16_t y, uint16_t w, uint16_t h, int scale); void updateScreen(); void fullRefresh(); void fadeOut();