diff --git a/README.txt b/README.txt index 74c5278..5211a89 100644 --- a/README.txt +++ b/README.txt @@ -1,6 +1,6 @@ REminiscence README -Release version: 0.4.1 +Release version: 0.4.2 ------------------------------------------------------------------------------- @@ -12,49 +12,45 @@ made by Delphine Software and released in 1992. More informations about the game can be found at [1], [2] and [3]. -Compiling: ----------- - -Update the defines in the Makefile if needed. The SDL, zlib and modplug -libraries are required. - - Data Files: ----------- You will need the original files of the PC (DOS or CD), Amiga or Macintosh -release. +release. Support for Amiga and Macintosh is still experimental. For the Macintosh release, the resource fork must dumped as a file named -'FLASHBACK.BIN'. It shall contain 38 distinct data types. +'FLASHBACK.BIN' (MacBinary) or 'FLASHBACK.RSRC' (AppleDouble). -To have background music during polygonal cutscenes with the PC version, -you need to copy the music/ directory of the Amiga version or use the .mod -fileset from unexotica [4]. +To hear music during polygonal cutscenes with the PC version, you need to copy +the music/ directory of the Amiga version or use the .mod fileset from +unexotica [4]. -To hear voice during in-game dialogues, you'll need to copy the 'VOICE.VCE' -file from the SegaCD version to the DATA directory. +For speech with in-game dialogues, you need to copy the 'VOICE.VCE' file from +the SegaCD version to the DATA directory. Running: -------- -By default, the engine will try to load the game data files from the 'DATA' -directory (as the original game did). The savestates are saved in the current -directory. These paths can be changed using command line switches : +By default, the engine tries to load the game data files from the 'DATA' +directory, as the original game executable did. The savestates are saved in the +current directory. + +These paths can be changed using command line switches : Usage: rs [OPTIONS]... --datapath=PATH Path to data files (default 'DATA') --savepath=PATH Path to save files (default '.') --levelnum=NUM Level to start from (default '0') --fullscreen Fullscreen display + --widescreen 16:9 display --scaler=NAME@X Graphics scaler (default 'scale@3') --language=LANG Language (fr,en,de,sp,it,jp) The scaler option specifies the algorithm used to smoothen the image in -addition to a scaling factor. External scalers are also supported, the -suffix 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 window size 512x448. +addition to a scaling factor. External scalers are also supported, the suffix +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). In-game hotkeys : diff --git a/cutscene.cpp b/cutscene.cpp index c028e61..3b1869d 100644 --- a/cutscene.cpp +++ b/cutscene.cpp @@ -1048,7 +1048,9 @@ 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; diff --git a/game.cpp b/game.cpp index 3e435f1..a75175c 100644 --- a/game.cpp +++ b/game.cpp @@ -157,6 +157,11 @@ void Game::run() { _stub->_pi.enter = false; _stub->_pi.space = false; _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); + } } } @@ -189,13 +194,14 @@ void Game::displayTitleScreenAmiga() { _stub->updateScreen(0); _vid.AMIGA_decodeCmp(_res._scratchBuffer + 6, buf); free(buf); - for (int h = 0; h < kH / 2; h += 2) { - const int y = kH / 2 - h; - _stub->copyRect(0, y, kW, h * 2, buf, kW); - _stub->updateScreen(0); - _stub->sleep(30); - } + int h = 0; while (1) { + if (h < kH / 2) { + const int y = kH / 2 - h; + _stub->copyRect(0, y, kW, h * 2, buf, kW); + _stub->updateScreen(0); + h += 2; + } _stub->processEvents(); if (_stub->_pi.quit) { break; @@ -293,7 +299,9 @@ void Game::mainLoop() { drawAnims(); drawCurrentInventoryItem(); drawLevelTexts(); - printLevelCode(); + if (g_options.enable_password_menu) { + printLevelCode(); + } if (_blinkingConradCounter != 0) { --_blinkingConradCounter; } @@ -393,6 +401,15 @@ void Game::playCutscene(int id) { _cut.play(); } } + if (_res._type == kResourceTypeMac) { + // restore palette entries modified by the cutscene player (0xC and 0xD) + Color palette[32]; + _res.MAC_copyClut16(palette, 0, 0x37); + _res.MAC_copyClut16(palette, 1, 0x38); + for (int i = 0; i < 32; ++i) { + _stub->setPaletteEntry(0xC0 + i, &palette[i]); + } + } if (id == 0x3D) { _cut.playCredits(); } @@ -442,6 +459,10 @@ 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); + } playCutscene(0x49); char buf[50]; snprintf(buf, sizeof(buf), "SCORE %08u", _score); @@ -534,16 +555,16 @@ bool Game::handleConfigPanel() { _menu._charVar2 = 0xEE; _vid.fullRefresh(); - enum { MENU_ITEM_LOAD = 1, MENU_ITEM_SAVE = 2, MENU_ITEM_ABORT = 3 }; + enum { MENU_ITEM_ABORT = 1, MENU_ITEM_LOAD = 2, MENU_ITEM_SAVE = 3 }; uint8_t colors[] = { 2, 3, 3, 3 }; int current = 0; while (!_stub->_pi.quit) { _menu.drawString(_res.getMenuString(LocaleData::LI_18_RESUME_GAME), y + 2, 9, colors[0]); - _menu.drawString(_res.getMenuString(LocaleData::LI_20_LOAD_GAME), y + 4, 9, colors[1]); - _menu.drawString(_res.getMenuString(LocaleData::LI_21_SAVE_GAME), y + 6, 9, colors[2]); - _menu.drawString(_res.getMenuString(LocaleData::LI_19_ABORT_GAME), y + 8, 9, colors[3]); + _menu.drawString(_res.getMenuString(LocaleData::LI_19_ABORT_GAME), y + 4, 9, colors[1]); + _menu.drawString(_res.getMenuString(LocaleData::LI_20_LOAD_GAME), y + 6, 9, colors[2]); + _menu.drawString(_res.getMenuString(LocaleData::LI_21_SAVE_GAME), y + 8, 9, colors[3]); char buf[30]; - snprintf(buf, sizeof(buf), "%s : %d-%02d", _res.getMenuString(LocaleData::LI_22_SAVE_SLOT), _currentLevel + 1, _stateSlot); + snprintf(buf, sizeof(buf), "%s < %02d >", _res.getMenuString(LocaleData::LI_22_SAVE_SLOT), _stateSlot); _menu.drawString(buf, y + 10, 9, 1); _vid.updateScreen(); @@ -598,6 +619,10 @@ 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); + } playCutscene(0x48); int timeout = 100; int current_color = 0; @@ -799,46 +824,77 @@ static int getLineLength(const uint8_t *str) { void Game::drawStoryTexts() { if (_textToDisplay != 0xFFFF) { - if (_res._type == kResourceTypeMac) { - warning("Unhandled textToDisplay %d", _textToDisplay); - _textToDisplay = 0xFFFF; - return; - } uint8_t textColor = 0xE8; const uint8_t *str = _res.getGameString(_textToDisplay); memcpy(_vid._tempLayer, _vid._frontLayer, _vid._layerSize); int textSpeechSegment = 0; + int textSegmentsCount = 0; while (!_stub->_pi.quit) { drawIcon(_currentInventoryIconNum, 80, 8, 0xA); - if (*str == 0xFF) { - if (_res._lang == LANG_JP) { + int yPos = 26; + if (_res._type == kResourceTypeMac) { + if (textSegmentsCount == 0) { + textSegmentsCount = *str++; + } + int len = *str++; + if (*str == '@') { switch (str[1]) { - case 0: + case '1': textColor = 0xE9; break; - case 1: + case '2': textColor = 0xEB; break; default: - warning("Unhandled JP color code 0x%x", str[1]); + warning("Unhandled MAC text color code 0x%x", str[1]); break; } str += 2; - } else { - textColor = str[1]; - // str[2] is an unused color (possibly the shadow) - str += 3; + len -= 2; } - } - int yPos = 26; - while (1) { - const int len = getLineLength(str); - str = (const uint8_t *)_vid.drawString((const char *)str, (176 - len * Video::CHAR_W) / 2, yPos, textColor); - yPos += 8; - if (*str == 0 || *str == 0xB) { - break; + for (; len > 0; yPos += 8) { + const uint8_t *next = (const uint8_t *)memchr(str, 0x7C, len); + if (!next) { + _vid.drawStringLen((const char *)str, len, (176 - len * Video::CHAR_W) / 2, yPos, textColor); + // point 'str' to beginning of next text segment + str += len; + break; + } + const int lineLength = next - str; + _vid.drawStringLen((const char *)str, lineLength, (176 - lineLength * Video::CHAR_W) / 2, yPos, textColor); + str = next + 1; + len -= lineLength + 1; + } + } else { + if (*str == 0xFF) { + if (_res._lang == LANG_JP) { + switch (str[1]) { + case 0: + textColor = 0xE9; + break; + case 1: + textColor = 0xEB; + break; + default: + warning("Unhandled JP text color code 0x%x", str[1]); + break; + } + str += 2; + } else { + textColor = str[1]; + // str[2] is an unused color (possibly the shadow) + str += 3; + } + } + while (1) { + const int len = getLineLength(str); + str = (const uint8_t *)_vid.drawString((const char *)str, (176 - len * Video::CHAR_W) / 2, yPos, textColor); + if (*str == 0 || *str == 0xB) { + break; + } + ++str; + yPos += 8; } - ++str; } MixerChunk chunk; _res.load_VCE(_textToDisplay, textSpeechSegment++, &chunk.data, &chunk.len); @@ -858,10 +914,16 @@ void Game::drawStoryTexts() { free(chunk.data); } _stub->_pi.backspace = false; - if (*str == 0) { - break; + if (_res._type == kResourceTypeMac) { + if (textSpeechSegment == textSegmentsCount) { + break; + } + } else { + if (*str == 0) { + break; + } + ++str; } - ++str; memcpy(_vid._frontLayer, _vid._tempLayer, _vid._layerSize); } _textToDisplay = 0xFFFF; @@ -1066,15 +1128,6 @@ void Game::drawAnimBuffer(uint8_t stateNum, AnimBufferState *state) { } } -static void fixOffsetDecodeBuffer(DecodeBuffer *buf, const uint8_t *dataPtr) { - if (buf->xflip) { - buf->x += (int16_t)READ_BE_UINT16(dataPtr + 4) - READ_BE_UINT16(dataPtr) - 1; - } else { - buf->x -= (int16_t)READ_BE_UINT16(dataPtr + 4); - } - buf->y -= (int16_t)READ_BE_UINT16(dataPtr + 6); -} - void Game::drawPiege(AnimBufferState *state) { LivePGE *pge = state->pge; switch (_res._type) { @@ -1082,43 +1135,18 @@ void Game::drawPiege(AnimBufferState *state) { case kResourceTypeDOS: drawObject(state->dataPtr, state->x, state->y, pge->flags); break; - case kResourceTypeMac: { - DecodeBuffer buf; - memset(&buf, 0, sizeof(buf)); - buf.xflip = (pge->flags & 2); - buf.ptr = _vid._frontLayer; - buf.w = buf.pitch = _vid._w; - buf.h = _vid._h; - buf.x = state->x * _vid._layerScale; - buf.y = state->y * _vid._layerScale; - buf.setPixel = _eraseBackground ? Video::MAC_drawBuffer : Video::MAC_drawBufferMask; - if (pge->flags & 8) { - const uint8_t *dataPtr = _res.MAC_getImageData(_res._spc, pge->anim_number); - if (dataPtr) { - fixOffsetDecodeBuffer(&buf, dataPtr); - _res.MAC_decodeImageData(_res._spc, pge->anim_number, &buf); - _vid.MAC_markBlockAsDirty(buf.x, buf.y, READ_BE_UINT16(dataPtr), READ_BE_UINT16(dataPtr + 2)); - } - } else if (pge->index == 0) { - if (pge->anim_number == 0x386) { - return; - } - const int frame = _res.MAC_getPersoFrame(pge->anim_number); - const uint8_t *dataPtr = _res.MAC_getImageData(_res._perso, frame); - if (dataPtr) { - fixOffsetDecodeBuffer(&buf, dataPtr); - _res.MAC_decodeImageData(_res._perso, frame, &buf); - _vid.MAC_markBlockAsDirty(buf.x, buf.y, READ_BE_UINT16(dataPtr), READ_BE_UINT16(dataPtr + 2)); - } - } else { - const int frame = _res.MAC_getMonsterFrame(pge->anim_number); - const uint8_t *dataPtr = _res.MAC_getImageData(_res._monster, frame); - if (dataPtr) { - fixOffsetDecodeBuffer(&buf, dataPtr); - _res.MAC_decodeImageData(_res._monster, frame, &buf); - _vid.MAC_markBlockAsDirty(buf.x, buf.y, READ_BE_UINT16(dataPtr), READ_BE_UINT16(dataPtr + 2)); - } + case kResourceTypeMac: + if (pge->flags & 8) { + _vid.MAC_drawSprite(state->x, state->y, _res._spc, pge->anim_number, (pge->flags & 2) != 0, _eraseBackground); + } else if (pge->index == 0) { + if (pge->anim_number == 0x386) { + break; } + const int frame = _res.MAC_getPersoFrame(pge->anim_number); + _vid.MAC_drawSprite(state->x, state->y, _res._perso, frame, (pge->flags & 2) != 0, _eraseBackground); + } else { + const int frame = _res.MAC_getMonsterFrame(pge->anim_number); + _vid.MAC_drawSprite(state->x, state->y, _res._monster, frame, (pge->flags & 2) != 0, _eraseBackground); } break; } @@ -1495,35 +1523,26 @@ void Game::loadLevelMap() { _vid.AMIGA_decodeLev(_currentLevel, _currentRoom); break; case kResourceTypeDOS: - if (_res._map) { - _vid.PC_decodeMap(_currentLevel, _currentRoom); - _vid.PC_setLevelPalettes(); - } else if (_res._lev) { - _vid.PC_decodeLev(_currentLevel, _currentRoom); - } - break; - case kResourceTypeMac: - { - DecodeBuffer buf; - memset(&buf, 0, sizeof(buf)); - buf.ptr = _vid._frontLayer; - buf.w = buf.pitch = _vid._w; - buf.h = _vid._h; - buf.setPixel = Video::MAC_drawBuffer; - _res.MAC_loadLevelRoom(_currentLevel, _currentRoom, &buf); - memcpy(_vid._backLayer, _vid._frontLayer, _vid._layerSize); - Color roomPalette[256]; - _res.MAC_setupRoomClut(_currentLevel, _currentRoom, roomPalette); - for (int j = 0; j < 16; ++j) { - if (j == 5 || j == 7 || j == 14 || j == 15) { - continue; - } - for (int i = 0; i < 16; ++i) { - const int color = j * 16 + i; - _stub->setPaletteEntry(color, &roomPalette[color]); - } + if (_stub->hasWidescreen()) { // draw adjacent rooms + const int leftRoom = _res._ctData[CT_LEFT_ROOM + _currentRoom]; + if (leftRoom > 0 && hasLevelMap(_currentLevel, leftRoom)) { + _vid.PC_decodeMap(_currentLevel, leftRoom); + _stub->copyRectLeftBorder(Video::GAMESCREEN_W, Video::GAMESCREEN_H, _vid._backLayer); + } else { + _stub->copyRectLeftBorder(Video::GAMESCREEN_W, Video::GAMESCREEN_H, 0); + } + const int rightRoom = _res._ctData[CT_RIGHT_ROOM + _currentRoom]; + if (rightRoom > 0 && hasLevelMap(_currentLevel, rightRoom)) { + _vid.PC_decodeMap(_currentLevel, rightRoom); + _stub->copyRectRightBorder(Video::GAMESCREEN_W, Video::GAMESCREEN_H, _vid._backLayer); + } else { + _stub->copyRectRightBorder(Video::GAMESCREEN_W, Video::GAMESCREEN_H, 0); } } + _vid.PC_decodeMap(_currentLevel, _currentRoom); + break; + case kResourceTypeMac: + _vid.MAC_decodeMap(_currentLevel, _currentRoom); break; } } @@ -1703,19 +1722,7 @@ void Game::drawIcon(uint8_t iconNum, int16_t x, int16_t y, uint8_t colMask) { iconNum = 34; break; } - { - const uint8_t *dataPtr = _res.MAC_getImageData(_res._icn, iconNum); - DecodeBuffer buf; - memset(&buf, 0, sizeof(buf)); - buf.ptr = _vid._frontLayer; - buf.w = buf.pitch = _vid._w; - buf.h = _vid._h; - buf.x = x * _vid._layerScale; - buf.y = y * _vid._layerScale; - buf.setPixel = Video::MAC_drawBuffer; - _res.MAC_decodeImageData(_res._icn, iconNum, &buf); - _vid.MAC_markBlockAsDirty(buf.x, buf.y, READ_BE_UINT16(dataPtr), READ_BE_UINT16(dataPtr + 2)); - } + _vid.MAC_drawSprite(x, y, _res._icn, iconNum, false, true); return; } _vid.drawSpriteSub1(buf, _vid._frontLayer + x + y * _vid._w, 16, 16, 16, colMask << 4); diff --git a/intern.h b/intern.h index 71a8d8a..9efae06 100644 --- a/intern.h +++ b/intern.h @@ -69,6 +69,16 @@ inline void SWAP(T &a, T &b) { b = tmp; } +template +inline T CLIP(const T& val, const T& a, const T& b) { + if (val < a) { + return a; + } else if (val > b) { + return b; + } + return val; +} + enum Language { LANG_FR, LANG_EN, @@ -93,6 +103,7 @@ enum Skill { struct Options { bool bypass_protection; bool enable_password_menu; + bool enable_language_selection; bool fade_out_palette; bool use_tiledata; bool use_text_cutscenes; diff --git a/main.cpp b/main.cpp index e937f2d..f400d0b 100644 --- a/main.cpp +++ b/main.cpp @@ -22,6 +22,7 @@ static const char *USAGE = " --savepath=PATH Path to save files (default '.')\n" " --levelnum=NUM Start to level, bypass introduction\n" " --fullscreen Fullscreen display\n" + " --widescreen 16:9 display\n" " --scaler=NAME@X Graphics scaler (default 'scale@3')\n" " --language=LANG Language (fr,en,de,sp,it,jp)\n" ; @@ -39,6 +40,7 @@ static int detectVersion(FileSystem *fs) { { "LEVEL1.LEV", kResourceTypeAmiga, "Amiga" }, { "DEMO.LEV", kResourceTypeAmiga, "Amiga (Demo)" }, { "FLASHBACK.BIN", kResourceTypeMac, "Macintosh" }, + { "FLASHBACK.RSRC", kResourceTypeMac, "Macintosh" }, { 0, -1, 0 } }; for (int i = 0; table[i].filename; ++i) { @@ -82,6 +84,7 @@ static void initOptions() { // defaults g_options.bypass_protection = true; g_options.enable_password_menu = false; + g_options.enable_language_selection = false; g_options.fade_out_palette = true; g_options.use_text_cutscenes = false; g_options.use_seq_cutscenes = true; @@ -96,6 +99,7 @@ static void initOptions() { } opts[] = { { "bypass_protection", &g_options.bypass_protection }, { "enable_password_menu", &g_options.enable_password_menu }, + { "enable_language_selection", &g_options.enable_language_selection }, { "fade_out_palette", &g_options.fade_out_palette }, { "use_tiledata", &g_options.use_tiledata }, { "use_text_cutscenes", &g_options.use_text_cutscenes }, @@ -180,6 +184,7 @@ int main(int argc, char *argv[]) { const char *savePath = "."; int levelNum = 0; bool fullscreen = false; + bool widescreen = false; ScalerParameters scalerParameters = ScalerParameters::defaults(); int forcedLanguage = -1; if (argc == 2) { @@ -197,6 +202,7 @@ int main(int argc, char *argv[]) { { "fullscreen", no_argument, 0, 4 }, { "scaler", required_argument, 0, 5 }, { "language", required_argument, 0, 6 }, + { "widescreen", no_argument, 0, 7 }, { 0, 0, 0, 0 } }; int index; @@ -241,6 +247,9 @@ int main(int argc, char *argv[]) { } } break; + case 7: + widescreen = true; + break; default: printf(USAGE, argv[0]); return 0; @@ -257,7 +266,7 @@ int main(int argc, char *argv[]) { const Language language = (forcedLanguage == -1) ? detectLanguage(&fs) : (Language)forcedLanguage; SystemStub *stub = SystemStub_SDL_create(); Game *g = new Game(stub, &fs, savePath, levelNum, (ResourceType)version, language); - stub->init(g_caption, g->_vid._w, g->_vid._h, fullscreen, &scalerParameters); + stub->init(g_caption, g->_vid._w, g->_vid._h, fullscreen, widescreen, &scalerParameters); g->run(); delete g; stub->destroy(); diff --git a/menu.cpp b/menu.cpp index 133d5bd..5fed812 100644 --- a/menu.cpp +++ b/menu.cpp @@ -94,6 +94,7 @@ void Menu::loadPicture(const char *prefix) { } } } + memcpy(_vid->_backLayer, _vid->_frontLayer, _vid->_layerSize); _res->load_PAL_menu(prefix, _res->_scratchBuffer); _stub->setPalette(_res->_scratchBuffer, 256); } @@ -197,7 +198,7 @@ bool Menu::handlePasswordScreen() { } _vid->PC_drawChar(0x20, 21, len + 15); - _vid->markBlockAsDirty(15 * 8, 21 * 8, (len + 1) * 8, 8); + _vid->markBlockAsDirty(15 * Video::CHAR_W, 21 * Video::CHAR_H, (len + 1) * Video::CHAR_W, Video::CHAR_H); _vid->updateScreen(); _stub->sleep(EVENTS_DELAY); _stub->processEvents(); @@ -262,12 +263,12 @@ bool Menu::handleLevelScreen() { for (int i = 0; i < 7; ++i) { drawString(levelTitles[i], 7 + i * 2, 4, (currentLevel == i) ? 2 : 3); } - _vid->markBlockAsDirty(4 * 8, 7 * 8, 192, 7 * 8); + _vid->markBlockAsDirty(4 * Video::CHAR_W, 7 * Video::CHAR_H, 192, 7 * Video::CHAR_H); 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 * 8, 23 * 8, 192, 8); + _vid->markBlockAsDirty(4 * Video::CHAR_W, 23 * Video::CHAR_H, 192, Video::CHAR_H); _vid->updateScreen(); _stub->sleep(EVENTS_DELAY); @@ -372,7 +373,30 @@ void Menu::handleTitleScreen() { bool quitLoop = false; int currentEntry = 0; + static const struct { + Language lang; + const uint8_t *bitmap16x12; + } languages[] = { + { LANG_EN, _flagEn16x12 }, + { LANG_FR, _flagFr16x12 }, + { LANG_DE, _flagDe16x12 }, + { LANG_SP, _flagSp16x12 }, + { LANG_IT, _flagIt16x12 }, + { LANG_JP, _flagJp16x12 }, + }; + int currentLanguage = 0; + for (int i = 0; i < ARRAYSIZE(languages); ++i) { + if (languages[i].lang == _res->_lang) { + currentLanguage = i; + break; + } + } + while (!quitLoop) { + + int selectedItem = -1; + int previousLanguage = currentLanguage; + if (_nextScreen == SCREEN_TITLE) { _vid->fadeOut(); loadPicture("menu1"); @@ -383,16 +407,25 @@ void Menu::handleTitleScreen() { _currentScreen = _nextScreen; _nextScreen = -1; } - int selectedItem = -1; - const int yPos = 26 - menuItemsCount * 2; - for (int i = 0; i < menuItemsCount; ++i) { - drawString(_res->getMenuString(menuItems[i].str), yPos + i * 2, 20, (i == currentEntry) ? 2 : 3); + + if (g_options.enable_language_selection) { + if (_stub->_pi.dirMask & PlayerInput::DIR_LEFT) { + _stub->_pi.dirMask &= ~PlayerInput::DIR_LEFT; + if (currentLanguage != 0) { + --currentLanguage; + } else { + currentLanguage = ARRAYSIZE(languages) - 1; + } + } + if (_stub->_pi.dirMask & PlayerInput::DIR_RIGHT) { + _stub->_pi.dirMask &= ~PlayerInput::DIR_RIGHT; + if (currentLanguage != ARRAYSIZE(languages) - 1) { + ++currentLanguage; + } else { + currentLanguage = 0; + } + } } - - _vid->updateScreen(); - _stub->sleep(EVENTS_DELAY); - _stub->processEvents(); - if (_stub->_pi.dirMask & PlayerInput::DIR_UP) { _stub->_pi.dirMask &= ~PlayerInput::DIR_UP; if (currentEntry != 0) { @@ -413,7 +446,6 @@ void Menu::handleTitleScreen() { _stub->_pi.enter = false; selectedItem = currentEntry; } - if (selectedItem != -1) { _selectedOption = menuItems[selectedItem].opt; switch (_selectedOption) { @@ -444,7 +476,33 @@ void Menu::handleTitleScreen() { break; } _nextScreen = SCREEN_TITLE; + continue; } + + if (previousLanguage != currentLanguage) { + _res->setLanguage(languages[currentLanguage].lang); + // clear previous language text + memcpy(_vid->_frontLayer, _vid->_backLayer, _vid->_layerSize); + } + + // draw the options + const int yPos = 26 - menuItemsCount * 2; + for (int i = 0; i < menuItemsCount; ++i) { + drawString(_res->getMenuString(menuItems[i].str), yPos + i * 2, 20, (i == currentEntry) ? 2 : 3); + } + + // draw the language flag in the top right corner + if (previousLanguage != currentLanguage) { + _stub->copyRect(0, 0, Video::GAMESCREEN_W, Video::GAMESCREEN_H, _vid->_frontLayer, Video::GAMESCREEN_W); + static const int flagW = 16; + static const int flagH = 12; + static const int flagX = Video::GAMESCREEN_W - flagW - 8; + static const int flagY = 8; + _stub->copyRectRgb24(flagX, flagY, flagW, flagH, languages[currentLanguage].bitmap16x12); + } + _vid->updateScreen(); + _stub->sleep(EVENTS_DELAY); + _stub->processEvents(); if (_stub->_pi.quit) { break; } diff --git a/menu.h b/menu.h index 64afef8..5974198 100644 --- a/menu.h +++ b/menu.h @@ -45,6 +45,13 @@ struct Menu { static const char *_passwordsEnAmiga[]; static const char *_passwordsMac[]; + static const uint8_t _flagEn16x12[]; + static const uint8_t _flagFr16x12[]; + static const uint8_t _flagDe16x12[]; + static const uint8_t _flagIt16x12[]; + static const uint8_t _flagJp16x12[]; + static const uint8_t _flagSp16x12[]; + Resource *_res; SystemStub *_stub; Video *_vid; diff --git a/resource.cpp b/resource.cpp index 651a1ad..247f627 100644 --- a/resource.cpp +++ b/resource.cpp @@ -71,8 +71,12 @@ void Resource::init() { } break; case kResourceTypeMac: - _mac = new ResourceMac(ResourceMac::FILENAME, _fs); - _mac->loadMap(); + if (_fs->exists(ResourceMac::FILENAME1)) { + _mac = new ResourceMac(ResourceMac::FILENAME1, _fs); + } else if (_fs->exists(ResourceMac::FILENAME2)) { + _mac = new ResourceMac(ResourceMac::FILENAME2, _fs); + } + _mac->load(); break; } } @@ -80,6 +84,17 @@ void Resource::init() { void Resource::fini() { } +void Resource::setLanguage(Language lang) { + if (_lang != lang) { + _lang = lang; + // reload global language specific data files + free_TEXT(); + load_TEXT(); + free_CINE(); + load_CINE(); + } +} + bool Resource::fileExists(const char *filename) { if (_fs->exists(filename)) { return true; @@ -367,6 +382,9 @@ void Resource::load_CINE() { case kResourceTypeDOS: if (_cine_off == 0) { snprintf(_entryName, sizeof(_entryName), "%sCINE.BIN", prefix); + if (!_fs->exists(_entryName)) { + strcpy(_entryName, "ENGCINE.BIN"); + } File f; if (f.open(_entryName, "rb", _fs)) { int len = f.size(); @@ -389,6 +407,9 @@ void Resource::load_CINE() { } if (_cine_txt == 0) { snprintf(_entryName, sizeof(_entryName), "%sCINE.TXT", prefix); + if (!_fs->exists(_entryName)) { + strcpy(_entryName, "ENGCINE.TXT"); + } File f; if (f.open(_entryName, "rb", _fs)) { int len = f.size(); @@ -416,6 +437,13 @@ void Resource::load_CINE() { } } +void Resource::free_CINE() { + free(_cine_off); + _cine_off = 0; + free(_cine_txt); + _cine_txt = 0; +} + void Resource::load_TEXT() { _stringsTable = 0; switch (_lang) { @@ -502,7 +530,7 @@ void Resource::unload(int objType) { _pol = 0; break; default: - error("Unimplemented Resource::load() type %d", objType); + error("Unimplemented Resource::unload() type %d", objType); break; } } diff --git a/resource.h b/resource.h index fbb2c5c..91f89a5 100644 --- a/resource.h +++ b/resource.h @@ -183,6 +183,8 @@ struct Resource { void init(); void fini(); + void setLanguage(Language lang); + bool isDOS() const { return _type == kResourceTypeDOS; } bool isAmiga() const { return _type == kResourceTypeAmiga; } @@ -197,6 +199,7 @@ struct Resource { void load_CMP_menu(const char *fileName, uint8_t *dstPtr); void load_SPR_OFF(const char *fileName, uint8_t *sprData); void load_CINE(); + void free_CINE(); void load_TEXT(); void free_TEXT(); void unload(int objType); diff --git a/rs.cfg b/rs.cfg index 08fc206..4202fe5 100644 --- a/rs.cfg +++ b/rs.cfg @@ -4,6 +4,9 @@ bypass_protection=true # use original password level selection menu screen enable_password_menu=false +# allow changing the language in title menu (left/right) +enable_language_selection=true + # fade palette to black for screen transition (use blending if false) fade_out_palette=false diff --git a/scaler.cpp b/scaler.cpp index 7df63d6..700190a 100644 --- a/scaler.cpp +++ b/scaler.cpp @@ -8,85 +8,239 @@ #include "dynlib.h" #include "util.h" -static void scale2x(uint32_t *dst, int dstPitch, const uint32_t *src, int srcPitch, int w, int h) { - const int dstPitch2 = dstPitch * 2; - for (int y = 0; y < h; ++y) { - uint32_t *p = dst; - for (int x = 0; x < w; ++x, p += 2) { - const uint32_t E = *(src + x); - const uint32_t B = (y == 0) ? E : *(src + x - srcPitch); - const uint32_t D = (x == 0) ? E : *(src + x - 1); - const uint32_t F = (x == w - 1) ? E : *(src + x + 1); - const uint32_t H = (y == h - 1) ? E : *(src + x + srcPitch); - if (B != H && D != F) { - *(p) = D == B ? D : E; - *(p + 1) = B == F ? F : E; - *(p + dstPitch) = D == H ? D : E; - *(p + dstPitch + 1) = H == F ? F : E; - } else { - *(p) = E; - *(p + 1) = E; - *(p + dstPitch) = E; - *(p + dstPitch + 1) = E; - } +static void scanline2x(uint32_t *dst0, uint32_t *dst1, const uint32_t *src0, const uint32_t *src1, const uint32_t *src2, int w) { + uint32_t B, D, E, F, H; + + // ABC + // DEF + // GHI + + int x = 0; + + // first pixel (D == E) + B = *(src0 + x); + E = *(src1 + x); + D = E; + F = *(src1 + x + 1); + H = *(src2 + x); + if (B != H && D != F) { + dst0[0] = D == B ? D : E; + dst0[1] = B == F ? F : E; + dst1[0] = D == H ? D : E; + dst1[1] = H == F ? F : E; + } else { + dst0[0] = E; + dst0[1] = E; + dst1[0] = E; + dst1[1] = E; + } + dst0 += 2; + dst1 += 2; + + // center pixels + E = F; + for (x = 1; x < w - 1; ++x) { + B = *(src0 + x); + F = *(src1 + x + 1); + H = *(src2 + x); + if (B != H && D != F) { + dst0[0] = D == B ? D : E; + dst0[1] = B == F ? F : E; + dst1[0] = D == H ? D : E; + dst1[1] = H == F ? F : E; + } else { + dst0[0] = E; + dst0[1] = E; + dst1[0] = E; + dst1[1] = E; } + D = E; E = F; + dst0 += 2; + dst1 += 2; + } + + // last pixel (F == E) + B = *(src0 + x); + H = *(src2 + x); + if (B != H && D != F) { + dst0[0] = D == B ? D : E; + dst0[1] = B == F ? F : E; + dst1[0] = D == H ? D : E; + dst1[1] = H == F ? F : E; + } else { + dst0[0] = E; + dst0[1] = E; + dst1[0] = E; + dst1[1] = E; + } +} + +static void scale2x(uint32_t *dst, int dstPitch, const uint32_t *src, int srcPitch, int w, int h) { + assert(w > 1 && h > 1); + const int dstPitch2 = dstPitch * 2; + + const uint32_t *src0, *src1, *src2; + + // y == 0 + src0 = src; + src1 = src; + src2 = src + srcPitch; + scanline2x(dst, dst + dstPitch, src0, src1, src2, w); + dst += dstPitch2; + + // center + src0 = src; + src1 = src + srcPitch; + src2 = src + srcPitch * 2; + for (int y = 1; y < h - 1; ++y) { + scanline2x(dst, dst + dstPitch, src0, src1, src2, w); dst += dstPitch2; - src += srcPitch; + + src0 += srcPitch; + src1 += srcPitch; + src2 += srcPitch; + } + + // y == h-1 + src2 = src1; + scanline2x(dst, dst + dstPitch, src0, src1, src2, w); +} + +static void scanline3x(uint32_t *dst0, uint32_t *dst1, uint32_t *dst2, const uint32_t *src0, const uint32_t *src1, const uint32_t *src2, int w) { + uint32_t A, B, C, D, E, F, G, H, I; + + // ABC + // DEF + // GHI + + int x = 0; + + // first pixel (A == B, D == E and G == H) + B = *(src0 + x); + A = B; + C = *(src0 + x + 1); + E = *(src1 + x); + D = E; + F = *(src1 + x + 1); + H = *(src2 + x); + G = H; + I = *(src2 + x + 1); + if (B != H && D != F) { + dst0[0] = D == B ? D : E; + dst0[1] = (E == B && E != C) || (B == F && E != A) ? B : E; + dst0[2] = B == F ? F : E; + dst1[0] = (D == B && E != G) || (D == B && E != A) ? D : E; + dst1[1] = E; + dst1[2] = (B == F && E != I) || (H == F && E != C) ? F : E; + dst2[0] = D == H ? D : E; + dst2[1] = (D == H && E != I) || (H == F && E != G) ? H : E; + dst2[2] = H == F ? F : E; + } else { + dst0[0] = E; + dst0[1] = E; + dst0[2] = E; + dst1[0] = E; + dst1[1] = E; + dst1[2] = E; + dst2[0] = E; + dst2[1] = E; + dst2[2] = E; + } + dst0 += 3; + dst1 += 3; + dst2 += 3; + + // center pixels + B = C; + E = F; + H = I; + for (x = 1; x < w - 1; ++x) { + C = *(src0 + x + 1); + F = *(src1 + x + 1); + I = *(src2 + x + 1); + if (B != H && D != F) { + dst0[0] = D == B ? D : E; + dst0[1] = (E == B && E != C) || (B == F && E != A) ? B : E; + dst0[2] = B == F ? F : E; + dst1[0] = (D == B && E != G) || (D == B && E != A) ? D : E; + dst1[1] = E; + dst1[2] = (B == F && E != I) || (H == F && E != C) ? F : E; + dst2[0] = D == H ? D : E; + dst2[1] = (D == H && E != I) || (H == F && E != G) ? H : E; + dst2[2] = H == F ? F : E; + } else { + dst0[0] = E; + dst0[1] = E; + dst0[2] = E; + dst1[0] = E; + dst1[1] = E; + dst1[2] = E; + dst2[0] = E; + dst2[1] = E; + dst2[2] = E; + } + A = B; B = C; + D = E; E = F; + G = H; H = I; + dst0 += 3; + dst1 += 3; + dst2 += 3; + } + + // last pixel (B == C, E == F and H == I) + if (B != H && D != F) { + dst0[0] = D == B ? D : E; + dst0[1] = (E == B && E != C) || (B == F && E != A) ? B : E; + dst0[2] = B == F ? F : E; + dst1[0] = (D == B && E != G) || (D == B && E != A) ? D : E; + dst1[1] = E; + dst1[2] = (B == F && E != I) || (H == F && E != C) ? F : E; + dst2[0] = D == H ? D : E; + dst2[1] = (D == H && E != I) || (H == F && E != G) ? H : E; + dst2[2] = H == F ? F : E; + } else { + dst0[0] = E; + dst0[1] = E; + dst0[2] = E; + dst1[0] = E; + dst1[1] = E; + dst1[2] = E; + dst2[0] = E; + dst2[1] = E; + dst2[2] = E; } } static void scale3x(uint32_t *dst, int dstPitch, const uint32_t *src, int srcPitch, int w, int h) { + assert(w > 1 && h > 1); const int dstPitch2 = dstPitch * 2; const int dstPitch3 = dstPitch * 3; - for (int y = 0; y < h; ++y) { - uint32_t *p = dst; - for (int x = 0; x < w; ++x, p += 3) { - const uint32_t E = *(src + x); - const uint32_t B = (y == 0) ? E : *(src + x - srcPitch); - const uint32_t D = (x == 0) ? E : *(src + x - 1); - const uint32_t F = (x == w - 1) ? E : *(src + x + 1); - const uint32_t H = (y == h - 1) ? E : *(src + x + srcPitch); - uint32_t A, C; - if (y == 0) { - A = D; - C = F; - } else { - A = (x == 0) ? B : *(src + x - srcPitch - 1); - C = (x == w - 1) ? B : *(src + x - srcPitch + 1); - } - uint32_t G, I; - if (y == h - 1) { - G = D; - I = F; - } else { - G = (x == 0) ? H : *(src + x + srcPitch - 1); - I = (x == w - 1) ? H : *(src + x + srcPitch + 1); - } - if (B != H && D != F) { - *(p) = D == B ? D : E; - *(p + 1) = (D == B && E != C) || (B == F && E != A) ? B : E; - *(p + 2) = B == F ? F : E; - *(p + dstPitch) = (D == B && E != G) || (D == B && E != A) ? D : E; - *(p + dstPitch + 1) = E; - *(p + dstPitch + 2) = (B == F && E != I) || (H == F && E != C) ? F : E; - *(p + dstPitch2) = D == H ? D : E; - *(p + dstPitch2 + 1) = (D == H && E != I) || (H == F && E != G) ? H : E; - *(p + dstPitch2 + 2) = H == F ? F : E; - } else { - *(p) = E; - *(p + 1) = E; - *(p + 2) = E; - *(p + dstPitch) = E; - *(p + dstPitch + 1) = E; - *(p + dstPitch + 2) = E; - *(p + dstPitch2) = E; - *(p + dstPitch2 + 1) = E; - *(p + dstPitch2 + 2) = E; - } - } + + const uint32_t *src0, *src1, *src2; + + // y == 0 + src0 = src; + src1 = src; + src2 = src + srcPitch; + scanline3x(dst, dst + dstPitch, dst + dstPitch2, src0, src1, src2, w); + dst += dstPitch3; + + // center + src0 = src; + src1 = src + srcPitch; + src2 = src + srcPitch * 2; + for (int y = 1; y < h - 1; ++y) { + scanline3x(dst, dst + dstPitch, dst + dstPitch2, src0, src1, src2, w); dst += dstPitch3; - src += srcPitch; + + src0 += srcPitch; + src1 += srcPitch; + src2 += srcPitch; } + + // y == h-1 + src2 = src1; + scanline3x(dst, dst + dstPitch, dst + dstPitch2, src0, src1, src2, w); } static void scale4x(uint32_t *dst, int dstPitch, const uint32_t *src, int srcPitch, int w, int h) { diff --git a/staticres.cpp b/staticres.cpp index 418af01..0c666d6 100644 --- a/staticres.cpp +++ b/staticres.cpp @@ -5490,3 +5490,237 @@ const uint8_t Cutscene::_caillouSetData[] = { 0x08, 0x64, 0x0c, 0x86, 0x0e, 0xa8, 0x08, 0xaa, 0x04, 0x66, 0x04, 0x48, 0x0c, 0xcc, 0x0a, 0xaa, 0x06, 0x66, 0x04, 0x44, 0x02, 0x22, 0x04, 0x66, 0x00 }; + +const uint8_t Menu::_flagEn16x12[] = { + 0x73, 0x00, 0x19, 0x8e, 0x00, 0x00, 0x82, 0x91, 0x9d, 0x4e, 0x4f, 0xad, 0x00, 0x00, 0x89, 0x00, + 0x00, 0x89, 0x82, 0x91, 0x9d, 0x8e, 0x00, 0x00, 0x8e, 0x00, 0x00, 0x82, 0x91, 0x9d, 0x00, 0x00, + 0x89, 0x2d, 0x2e, 0x9e, 0x82, 0x91, 0x9d, 0x82, 0x91, 0x9d, 0x8e, 0x00, 0x00, 0x74, 0x00, 0x19, + 0x8e, 0x00, 0x00, 0xef, 0x3d, 0x32, 0xf7, 0xa5, 0xa0, 0xfe, 0xfd, 0xfd, 0x93, 0x93, 0xe9, 0x37, + 0x37, 0xd6, 0xf2, 0xf2, 0xfc, 0xe8, 0x28, 0x28, 0xe8, 0x28, 0x28, 0xf2, 0xf2, 0xfc, 0x74, 0x75, + 0xe2, 0xf4, 0xf5, 0xfc, 0xf9, 0xbb, 0xb7, 0xf0, 0x45, 0x3b, 0xf0, 0x4e, 0x45, 0x8e, 0x00, 0x00, + 0x82, 0x91, 0x9d, 0xf2, 0xa7, 0xa6, 0xec, 0x43, 0x3a, 0xf0, 0x68, 0x60, 0xfc, 0xe8, 0xe7, 0xa7, + 0xa7, 0xed, 0xf2, 0xf2, 0xfc, 0xe8, 0x28, 0x28, 0xe8, 0x28, 0x28, 0xf9, 0xf9, 0xfe, 0xfc, 0xf2, + 0xf2, 0xf2, 0x7d, 0x77, 0xeb, 0x3b, 0x30, 0xf1, 0x95, 0x93, 0xf7, 0xef, 0xf5, 0x73, 0x73, 0xbe, + 0x03, 0x03, 0x8a, 0x99, 0x99, 0xea, 0xf6, 0xea, 0xef, 0xee, 0x85, 0x82, 0xe8, 0x40, 0x37, 0xf6, + 0xb7, 0xb4, 0xfb, 0xfb, 0xfe, 0xe8, 0x28, 0x28, 0xe8, 0x28, 0x28, 0xff, 0xfc, 0xfc, 0xe9, 0x48, + 0x40, 0xed, 0x73, 0x6f, 0xf5, 0xe0, 0xe4, 0xa1, 0xa1, 0xec, 0x47, 0x47, 0xd9, 0x00, 0x00, 0x89, + 0x82, 0x91, 0x9d, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xe8, 0x28, 0x28, 0xe8, 0x28, 0x28, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x82, 0x91, 0x9d, + 0x8e, 0x00, 0x00, 0xe6, 0x19, 0x19, 0xe6, 0x19, 0x19, 0xe6, 0x19, 0x19, 0xe6, 0x19, 0x19, 0xe6, + 0x19, 0x19, 0xe6, 0x19, 0x19, 0xe6, 0x19, 0x19, 0xe6, 0x19, 0x19, 0xe6, 0x19, 0x19, 0xe6, 0x19, + 0x19, 0xe6, 0x19, 0x19, 0xe6, 0x19, 0x19, 0xe6, 0x19, 0x19, 0xe6, 0x19, 0x19, 0x8e, 0x00, 0x00, + 0x8e, 0x00, 0x00, 0xe6, 0x19, 0x19, 0xe6, 0x19, 0x19, 0xe6, 0x19, 0x19, 0xe6, 0x19, 0x19, 0xe6, + 0x19, 0x19, 0xe6, 0x19, 0x19, 0xe6, 0x19, 0x19, 0xe6, 0x19, 0x19, 0xe6, 0x19, 0x19, 0xe6, 0x19, + 0x19, 0xe6, 0x19, 0x19, 0xe6, 0x19, 0x19, 0xe6, 0x19, 0x19, 0xe6, 0x19, 0x19, 0x8e, 0x00, 0x00, + 0x82, 0x91, 0x9d, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xe8, 0x28, 0x28, 0xe8, 0x28, 0x28, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x82, 0x91, 0x9d, + 0x00, 0x00, 0x89, 0x87, 0x87, 0xe6, 0xf1, 0xd6, 0xdb, 0xd8, 0x48, 0x45, 0xec, 0xa7, 0xa5, 0xf6, + 0xf8, 0xfc, 0xf8, 0xf8, 0xfe, 0xe8, 0x28, 0x28, 0xe8, 0x28, 0x28, 0xfe, 0xfe, 0xff, 0xf2, 0xc3, + 0xc1, 0xd7, 0x41, 0x3d, 0xee, 0xc4, 0xc8, 0x8f, 0x8f, 0xe8, 0x3f, 0x3f, 0xd8, 0x00, 0x00, 0x89, + 0x82, 0x91, 0x9d, 0xe8, 0xab, 0xad, 0xd2, 0x39, 0x35, 0xf4, 0xcf, 0xce, 0xe4, 0xe6, 0xf8, 0x56, + 0x57, 0xdc, 0xf2, 0xf2, 0xfc, 0xe8, 0x28, 0x28, 0xe8, 0x28, 0x28, 0xf4, 0xf4, 0xfd, 0xdb, 0xdc, + 0xf7, 0xf8, 0xe2, 0xe1, 0xd5, 0x44, 0x41, 0xe3, 0x8f, 0x90, 0xf7, 0xf6, 0xfc, 0x45, 0x45, 0xa9, + 0x8e, 0x00, 0x00, 0xd4, 0x4b, 0x48, 0xfa, 0xeb, 0xeb, 0xd0, 0xd1, 0xf5, 0x40, 0x40, 0xd8, 0x37, + 0x37, 0xd6, 0xf2, 0xf2, 0xfc, 0xe8, 0x28, 0x28, 0xe8, 0x28, 0x28, 0xf2, 0xf2, 0xfc, 0x3a, 0x3a, + 0xd6, 0xbd, 0xbe, 0xf1, 0xfd, 0xf6, 0xf6, 0xda, 0x67, 0x64, 0xd5, 0x53, 0x52, 0x8e, 0x00, 0x00, + 0x74, 0x00, 0x19, 0x8e, 0x00, 0x00, 0x94, 0x95, 0xce, 0x00, 0x00, 0x89, 0x00, 0x00, 0x89, 0x00, + 0x00, 0x89, 0x82, 0x91, 0x9d, 0x8e, 0x00, 0x00, 0x8e, 0x00, 0x00, 0x82, 0x91, 0x9d, 0x00, 0x00, + 0x89, 0x00, 0x00, 0x89, 0x6c, 0x6d, 0xbb, 0x82, 0x91, 0x9d, 0x8e, 0x00, 0x00, 0x74, 0x00, 0x19 +}; + +const uint8_t Menu::_flagFr16x12[] = { + 0x45, 0x2f, 0x9f, 0x46, 0x2f, 0x9e, 0x46, 0x2f, 0x9e, 0x46, 0x2f, 0x9e, 0x46, 0x2f, 0x9e, 0x82, + 0x91, 0x9d, 0x82, 0x91, 0x9d, 0x82, 0x91, 0x9d, 0x82, 0x91, 0x9d, 0x82, 0x91, 0x9d, 0x82, 0x91, + 0x9d, 0xa5, 0x0a, 0x0a, 0xa5, 0x0a, 0x0a, 0xa5, 0x0a, 0x0a, 0xa5, 0x0a, 0x0a, 0xa5, 0x0a, 0x0a, + 0x46, 0x2f, 0x9e, 0x66, 0x42, 0xf2, 0x66, 0x42, 0xf2, 0x66, 0x42, 0xf2, 0x66, 0x42, 0xf2, 0xf5, + 0xf8, 0xfb, 0xf5, 0xf8, 0xfb, 0xf5, 0xf8, 0xfb, 0xf5, 0xf8, 0xfb, 0xf5, 0xf8, 0xfb, 0xf5, 0xf8, + 0xfb, 0xe5, 0x1d, 0x1d, 0xe5, 0x1d, 0x1d, 0xe5, 0x1d, 0x1d, 0xe5, 0x1d, 0x1d, 0xa5, 0x0a, 0x0a, + 0x46, 0x2f, 0x9e, 0x66, 0x42, 0xf2, 0x66, 0x42, 0xf2, 0x66, 0x42, 0xf2, 0x66, 0x42, 0xf2, 0xf5, + 0xf8, 0xfb, 0xf5, 0xf8, 0xfb, 0xf5, 0xf8, 0xfb, 0xf5, 0xf8, 0xfb, 0xf5, 0xf8, 0xfb, 0xf5, 0xf8, + 0xfb, 0xe5, 0x1d, 0x1d, 0xe5, 0x1d, 0x1d, 0xe5, 0x1d, 0x1d, 0xe5, 0x1d, 0x1d, 0xa5, 0x0a, 0x0a, + 0x46, 0x2f, 0x9e, 0x66, 0x42, 0xf2, 0x66, 0x42, 0xf2, 0x66, 0x42, 0xf2, 0x66, 0x42, 0xf2, 0xf5, + 0xf8, 0xfb, 0xf5, 0xf8, 0xfb, 0xf5, 0xf8, 0xfb, 0xf5, 0xf8, 0xfb, 0xf5, 0xf8, 0xfb, 0xf5, 0xf8, + 0xfb, 0xe5, 0x1d, 0x1d, 0xe5, 0x1d, 0x1d, 0xe5, 0x1d, 0x1d, 0xe5, 0x1d, 0x1d, 0xa5, 0x0a, 0x0a, + 0x46, 0x2f, 0x9e, 0x66, 0x42, 0xf2, 0x66, 0x42, 0xf2, 0x66, 0x42, 0xf2, 0x66, 0x42, 0xf2, 0xf5, + 0xf8, 0xfb, 0xf5, 0xf8, 0xfb, 0xf5, 0xf8, 0xfb, 0xf5, 0xf8, 0xfb, 0xf5, 0xf8, 0xfb, 0xf5, 0xf8, + 0xfb, 0xe5, 0x1d, 0x1d, 0xe5, 0x1d, 0x1d, 0xe5, 0x1d, 0x1d, 0xe5, 0x1d, 0x1d, 0xa5, 0x0a, 0x0a, + 0x46, 0x2f, 0x9e, 0x66, 0x42, 0xf2, 0x66, 0x42, 0xf2, 0x66, 0x42, 0xf2, 0x66, 0x42, 0xf2, 0xf5, + 0xf8, 0xfb, 0xf5, 0xf8, 0xfb, 0xf5, 0xf8, 0xfb, 0xf5, 0xf8, 0xfb, 0xf5, 0xf8, 0xfb, 0xf5, 0xf8, + 0xfb, 0xe5, 0x1d, 0x1d, 0xe5, 0x1d, 0x1d, 0xe5, 0x1d, 0x1d, 0xe5, 0x1d, 0x1d, 0xa5, 0x0a, 0x0a, + 0x46, 0x2f, 0x9e, 0x66, 0x42, 0xf2, 0x66, 0x42, 0xf2, 0x66, 0x42, 0xf2, 0x66, 0x42, 0xf2, 0xf5, + 0xf8, 0xfb, 0xf5, 0xf8, 0xfb, 0xf5, 0xf8, 0xfb, 0xf5, 0xf8, 0xfb, 0xf5, 0xf8, 0xfb, 0xf5, 0xf8, + 0xfb, 0xe5, 0x1d, 0x1d, 0xe5, 0x1d, 0x1d, 0xe5, 0x1d, 0x1d, 0xe5, 0x1d, 0x1d, 0xa5, 0x0a, 0x0a, + 0x46, 0x2f, 0x9e, 0x66, 0x42, 0xf2, 0x66, 0x42, 0xf2, 0x66, 0x42, 0xf2, 0x66, 0x42, 0xf2, 0xf5, + 0xf8, 0xfb, 0xf5, 0xf8, 0xfb, 0xf5, 0xf8, 0xfb, 0xf5, 0xf8, 0xfb, 0xf5, 0xf8, 0xfb, 0xf5, 0xf8, + 0xfb, 0xe5, 0x1d, 0x1d, 0xe5, 0x1d, 0x1d, 0xe5, 0x1d, 0x1d, 0xe5, 0x1d, 0x1d, 0xa5, 0x0a, 0x0a, + 0x46, 0x2f, 0x9e, 0x66, 0x42, 0xf2, 0x66, 0x42, 0xf2, 0x66, 0x42, 0xf2, 0x66, 0x42, 0xf2, 0xf5, + 0xf8, 0xfb, 0xf5, 0xf8, 0xfb, 0xf5, 0xf8, 0xfb, 0xf5, 0xf8, 0xfb, 0xf5, 0xf8, 0xfb, 0xf5, 0xf8, + 0xfb, 0xe5, 0x1d, 0x1d, 0xe5, 0x1d, 0x1d, 0xe5, 0x1d, 0x1d, 0xe5, 0x1d, 0x1d, 0xa5, 0x0a, 0x0a, + 0x46, 0x2f, 0x9e, 0x66, 0x42, 0xf2, 0x66, 0x42, 0xf2, 0x66, 0x42, 0xf2, 0x66, 0x42, 0xf2, 0xf5, + 0xf8, 0xfb, 0xf5, 0xf8, 0xfb, 0xf5, 0xf8, 0xfb, 0xf5, 0xf8, 0xfb, 0xf5, 0xf8, 0xfb, 0xf5, 0xf8, + 0xfb, 0xe5, 0x1d, 0x1d, 0xe5, 0x1d, 0x1d, 0xe5, 0x1d, 0x1d, 0xe5, 0x1d, 0x1d, 0xa5, 0x0a, 0x0a, + 0x46, 0x2f, 0x9e, 0x66, 0x42, 0xf2, 0x66, 0x42, 0xf2, 0x66, 0x42, 0xf2, 0x66, 0x42, 0xf2, 0xf5, + 0xf8, 0xfb, 0xf5, 0xf8, 0xfb, 0xf5, 0xf8, 0xfb, 0xf5, 0xf8, 0xfb, 0xf5, 0xf8, 0xfb, 0xf5, 0xf8, + 0xfb, 0xe5, 0x1d, 0x1d, 0xe5, 0x1d, 0x1d, 0xe5, 0x1d, 0x1d, 0xe5, 0x1d, 0x1d, 0xa5, 0x0a, 0x0a, + 0x46, 0x2f, 0x9e, 0x46, 0x2f, 0x9e, 0x46, 0x2f, 0x9e, 0x46, 0x2f, 0x9e, 0x46, 0x2f, 0x9e, 0x82, + 0x91, 0x9d, 0x82, 0x91, 0x9d, 0x82, 0x91, 0x9d, 0x82, 0x91, 0x9d, 0x82, 0x91, 0x9d, 0x82, 0x91, + 0x9d, 0xa5, 0x0a, 0x0a, 0xa5, 0x0a, 0x0a, 0xa5, 0x0a, 0x0a, 0xa5, 0x0a, 0x0a, 0xa6, 0x0a, 0x0a +}; + +const uint8_t Menu::_flagDe16x12[] = { + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x27, 0x27, 0x27, 0x27, 0x27, 0x27, 0x27, 0x27, 0x27, 0x27, 0x27, 0x27, 0x27, + 0x27, 0x27, 0x27, 0x27, 0x27, 0x27, 0x27, 0x27, 0x27, 0x27, 0x27, 0x27, 0x27, 0x27, 0x27, 0x27, + 0x27, 0x27, 0x27, 0x27, 0x27, 0x27, 0x27, 0x27, 0x27, 0x27, 0x27, 0x27, 0x27, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x27, 0x27, 0x27, 0x27, 0x27, 0x27, 0x27, 0x27, 0x27, 0x27, 0x27, 0x27, 0x27, + 0x27, 0x27, 0x27, 0x27, 0x27, 0x27, 0x27, 0x27, 0x27, 0x27, 0x27, 0x27, 0x27, 0x27, 0x27, 0x27, + 0x27, 0x27, 0x27, 0x27, 0x27, 0x27, 0x27, 0x27, 0x27, 0x27, 0x27, 0x27, 0x27, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x27, 0x27, 0x27, 0x27, 0x27, 0x27, 0x27, 0x27, 0x27, 0x27, 0x27, 0x27, 0x27, + 0x27, 0x27, 0x27, 0x27, 0x27, 0x27, 0x27, 0x27, 0x27, 0x27, 0x27, 0x27, 0x27, 0x27, 0x27, 0x27, + 0x27, 0x27, 0x27, 0x27, 0x27, 0x27, 0x27, 0x27, 0x27, 0x27, 0x27, 0x27, 0x27, 0x00, 0x00, 0x00, + 0xba, 0x01, 0x00, 0xe3, 0x1d, 0x1c, 0xe3, 0x1d, 0x1c, 0xe3, 0x1d, 0x1c, 0xe3, 0x1d, 0x1c, 0xe3, + 0x1d, 0x1c, 0xe3, 0x1d, 0x1c, 0xe3, 0x1d, 0x1c, 0xe3, 0x1d, 0x1c, 0xe3, 0x1d, 0x1c, 0xe3, 0x1d, + 0x1c, 0xe3, 0x1d, 0x1c, 0xe3, 0x1d, 0x1c, 0xe3, 0x1d, 0x1c, 0xe3, 0x1d, 0x1c, 0xba, 0x01, 0x00, + 0xba, 0x01, 0x00, 0xe3, 0x1d, 0x1c, 0xe3, 0x1d, 0x1c, 0xe3, 0x1d, 0x1c, 0xe3, 0x1d, 0x1c, 0xe3, + 0x1d, 0x1c, 0xe3, 0x1d, 0x1c, 0xe3, 0x1d, 0x1c, 0xe3, 0x1d, 0x1c, 0xe3, 0x1d, 0x1c, 0xe3, 0x1d, + 0x1c, 0xe3, 0x1d, 0x1c, 0xe3, 0x1d, 0x1c, 0xe3, 0x1d, 0x1c, 0xe3, 0x1d, 0x1c, 0xba, 0x01, 0x00, + 0xba, 0x01, 0x00, 0xe3, 0x1d, 0x1c, 0xe3, 0x1d, 0x1c, 0xe3, 0x1d, 0x1c, 0xe3, 0x1d, 0x1c, 0xe3, + 0x1d, 0x1c, 0xe3, 0x1d, 0x1c, 0xe3, 0x1d, 0x1c, 0xe3, 0x1d, 0x1c, 0xe3, 0x1d, 0x1c, 0xe3, 0x1d, + 0x1c, 0xe3, 0x1d, 0x1c, 0xe3, 0x1d, 0x1c, 0xe3, 0x1d, 0x1c, 0xe3, 0x1d, 0x1c, 0xba, 0x01, 0x00, + 0xba, 0x01, 0x00, 0xe3, 0x1d, 0x1c, 0xe3, 0x1d, 0x1c, 0xe3, 0x1d, 0x1c, 0xe3, 0x1d, 0x1c, 0xe3, + 0x1d, 0x1c, 0xe3, 0x1d, 0x1c, 0xe3, 0x1d, 0x1c, 0xe3, 0x1d, 0x1c, 0xe3, 0x1d, 0x1c, 0xe3, 0x1d, + 0x1c, 0xe3, 0x1d, 0x1c, 0xe3, 0x1d, 0x1c, 0xe3, 0x1d, 0x1c, 0xe3, 0x1d, 0x1c, 0xba, 0x01, 0x00, + 0xca, 0x80, 0x00, 0xff, 0xd0, 0x18, 0xff, 0xd0, 0x18, 0xff, 0xd0, 0x18, 0xff, 0xd0, 0x18, 0xff, + 0xd0, 0x18, 0xff, 0xd0, 0x18, 0xff, 0xd0, 0x18, 0xff, 0xd0, 0x18, 0xff, 0xd0, 0x18, 0xff, 0xd0, + 0x18, 0xff, 0xd0, 0x18, 0xff, 0xd0, 0x18, 0xff, 0xd0, 0x18, 0xff, 0xd0, 0x18, 0xca, 0x80, 0x00, + 0xca, 0x80, 0x00, 0xff, 0xd0, 0x18, 0xff, 0xd0, 0x18, 0xff, 0xd0, 0x18, 0xff, 0xd0, 0x18, 0xff, + 0xd0, 0x18, 0xff, 0xd0, 0x18, 0xff, 0xd0, 0x18, 0xff, 0xd0, 0x18, 0xff, 0xd0, 0x18, 0xff, 0xd0, + 0x18, 0xff, 0xd0, 0x18, 0xff, 0xd0, 0x18, 0xff, 0xd0, 0x18, 0xff, 0xd0, 0x18, 0xca, 0x80, 0x00, + 0xca, 0x80, 0x00, 0xff, 0xd0, 0x18, 0xff, 0xd0, 0x18, 0xff, 0xd0, 0x18, 0xff, 0xd0, 0x18, 0xff, + 0xd0, 0x18, 0xff, 0xd0, 0x18, 0xff, 0xd0, 0x18, 0xff, 0xd0, 0x18, 0xff, 0xd0, 0x18, 0xff, 0xd0, + 0x18, 0xff, 0xd0, 0x18, 0xff, 0xd0, 0x18, 0xff, 0xd0, 0x18, 0xff, 0xd0, 0x18, 0xca, 0x80, 0x00, + 0xca, 0x80, 0x00, 0xca, 0x80, 0x00, 0xca, 0x80, 0x00, 0xca, 0x80, 0x00, 0xca, 0x80, 0x00, 0xca, + 0x80, 0x00, 0xca, 0x80, 0x00, 0xca, 0x80, 0x00, 0xca, 0x80, 0x00, 0xca, 0x80, 0x00, 0xca, 0x80, + 0x00, 0xca, 0x80, 0x00, 0xca, 0x80, 0x00, 0xca, 0x80, 0x00, 0xca, 0x80, 0x00, 0xca, 0x81, 0x00 +}; + +const uint8_t Menu::_flagIt16x12[] = { + 0x00, 0x7a, 0x4a, 0x00, 0x7a, 0x4a, 0x00, 0x7a, 0x4a, 0x00, 0x7a, 0x4a, 0x00, 0x7a, 0x4a, 0x82, + 0x91, 0x9d, 0x82, 0x91, 0x9d, 0x82, 0x91, 0x9d, 0x82, 0x91, 0x9d, 0x82, 0x91, 0x9d, 0x82, 0x91, + 0x9d, 0xab, 0x00, 0x07, 0xab, 0x00, 0x07, 0xab, 0x00, 0x07, 0xab, 0x00, 0x07, 0xaa, 0x00, 0x07, + 0x00, 0x7a, 0x4a, 0x2f, 0xb0, 0x77, 0x2f, 0xb0, 0x77, 0x2f, 0xb0, 0x77, 0x2f, 0xb0, 0x77, 0xf5, + 0xf8, 0xfb, 0xf5, 0xf8, 0xfb, 0xf5, 0xf8, 0xfb, 0xf5, 0xf8, 0xfb, 0xf5, 0xf8, 0xfb, 0xf5, 0xf8, + 0xfb, 0xf4, 0x43, 0x4a, 0xf4, 0x43, 0x4a, 0xf4, 0x43, 0x4a, 0xf4, 0x43, 0x4a, 0xab, 0x00, 0x07, + 0x00, 0x7a, 0x4a, 0x2f, 0xb0, 0x77, 0x2f, 0xb0, 0x77, 0x2f, 0xb0, 0x77, 0x2f, 0xb0, 0x77, 0xf5, + 0xf8, 0xfb, 0xf5, 0xf8, 0xfb, 0xf5, 0xf8, 0xfb, 0xf5, 0xf8, 0xfb, 0xf5, 0xf8, 0xfb, 0xf5, 0xf8, + 0xfb, 0xf4, 0x43, 0x4a, 0xf4, 0x43, 0x4a, 0xf4, 0x43, 0x4a, 0xf4, 0x43, 0x4a, 0xab, 0x00, 0x07, + 0x00, 0x7a, 0x4a, 0x2f, 0xb0, 0x77, 0x2f, 0xb0, 0x77, 0x2f, 0xb0, 0x77, 0x2f, 0xb0, 0x77, 0xf5, + 0xf8, 0xfb, 0xf5, 0xf8, 0xfb, 0xf5, 0xf8, 0xfb, 0xf5, 0xf8, 0xfb, 0xf5, 0xf8, 0xfb, 0xf5, 0xf8, + 0xfb, 0xf4, 0x43, 0x4a, 0xf4, 0x43, 0x4a, 0xf4, 0x43, 0x4a, 0xf4, 0x43, 0x4a, 0xab, 0x00, 0x07, + 0x00, 0x7a, 0x4a, 0x2f, 0xb0, 0x77, 0x2f, 0xb0, 0x77, 0x2f, 0xb0, 0x77, 0x2f, 0xb0, 0x77, 0xf5, + 0xf8, 0xfb, 0xf5, 0xf8, 0xfb, 0xf5, 0xf8, 0xfb, 0xf5, 0xf8, 0xfb, 0xf5, 0xf8, 0xfb, 0xf5, 0xf8, + 0xfb, 0xf4, 0x43, 0x4a, 0xf4, 0x43, 0x4a, 0xf4, 0x43, 0x4a, 0xf4, 0x43, 0x4a, 0xab, 0x00, 0x07, + 0x00, 0x7a, 0x4a, 0x2f, 0xb0, 0x77, 0x2f, 0xb0, 0x77, 0x2f, 0xb0, 0x77, 0x2f, 0xb0, 0x77, 0xf5, + 0xf8, 0xfb, 0xf5, 0xf8, 0xfb, 0xf5, 0xf8, 0xfb, 0xf5, 0xf8, 0xfb, 0xf5, 0xf8, 0xfb, 0xf5, 0xf8, + 0xfb, 0xf4, 0x43, 0x4a, 0xf4, 0x43, 0x4a, 0xf4, 0x43, 0x4a, 0xf4, 0x43, 0x4a, 0xab, 0x00, 0x07, + 0x00, 0x7a, 0x4a, 0x2f, 0xb0, 0x77, 0x2f, 0xb0, 0x77, 0x2f, 0xb0, 0x77, 0x2f, 0xb0, 0x77, 0xf5, + 0xf8, 0xfb, 0xf5, 0xf8, 0xfb, 0xf5, 0xf8, 0xfb, 0xf5, 0xf8, 0xfb, 0xf5, 0xf8, 0xfb, 0xf5, 0xf8, + 0xfb, 0xf4, 0x43, 0x4a, 0xf4, 0x43, 0x4a, 0xf4, 0x43, 0x4a, 0xf4, 0x43, 0x4a, 0xab, 0x00, 0x07, + 0x00, 0x7a, 0x4a, 0x2f, 0xb0, 0x77, 0x2f, 0xb0, 0x77, 0x2f, 0xb0, 0x77, 0x2f, 0xb0, 0x77, 0xf5, + 0xf8, 0xfb, 0xf5, 0xf8, 0xfb, 0xf5, 0xf8, 0xfb, 0xf5, 0xf8, 0xfb, 0xf5, 0xf8, 0xfb, 0xf5, 0xf8, + 0xfb, 0xf4, 0x43, 0x4a, 0xf4, 0x43, 0x4a, 0xf4, 0x43, 0x4a, 0xf4, 0x43, 0x4a, 0xab, 0x00, 0x07, + 0x00, 0x7a, 0x4a, 0x2f, 0xb0, 0x77, 0x2f, 0xb0, 0x77, 0x2f, 0xb0, 0x77, 0x2f, 0xb0, 0x77, 0xf5, + 0xf8, 0xfb, 0xf5, 0xf8, 0xfb, 0xf5, 0xf8, 0xfb, 0xf5, 0xf8, 0xfb, 0xf5, 0xf8, 0xfb, 0xf5, 0xf8, + 0xfb, 0xf4, 0x43, 0x4a, 0xf4, 0x43, 0x4a, 0xf4, 0x43, 0x4a, 0xf4, 0x43, 0x4a, 0xab, 0x00, 0x07, + 0x00, 0x7a, 0x4a, 0x2f, 0xb0, 0x77, 0x2f, 0xb0, 0x77, 0x2f, 0xb0, 0x77, 0x2f, 0xb0, 0x77, 0xf5, + 0xf8, 0xfb, 0xf5, 0xf8, 0xfb, 0xf5, 0xf8, 0xfb, 0xf5, 0xf8, 0xfb, 0xf5, 0xf8, 0xfb, 0xf5, 0xf8, + 0xfb, 0xf4, 0x43, 0x4a, 0xf4, 0x43, 0x4a, 0xf4, 0x43, 0x4a, 0xf4, 0x43, 0x4a, 0xab, 0x00, 0x07, + 0x00, 0x7a, 0x4a, 0x2f, 0xb0, 0x77, 0x2f, 0xb0, 0x77, 0x2f, 0xb0, 0x77, 0x2f, 0xb0, 0x77, 0xf5, + 0xf8, 0xfb, 0xf5, 0xf8, 0xfb, 0xf5, 0xf8, 0xfb, 0xf5, 0xf8, 0xfb, 0xf5, 0xf8, 0xfb, 0xf5, 0xf8, + 0xfb, 0xf4, 0x43, 0x4a, 0xf4, 0x43, 0x4a, 0xf4, 0x43, 0x4a, 0xf4, 0x43, 0x4a, 0xab, 0x00, 0x07, + 0x00, 0x7a, 0x49, 0x00, 0x7a, 0x4a, 0x00, 0x7a, 0x4a, 0x00, 0x7a, 0x4a, 0x00, 0x7a, 0x4a, 0x82, + 0x91, 0x9d, 0x82, 0x91, 0x9d, 0x82, 0x91, 0x9d, 0x82, 0x91, 0x9d, 0x82, 0x91, 0x9d, 0x82, 0x91, + 0x9d, 0xab, 0x00, 0x07, 0xab, 0x00, 0x07, 0xab, 0x00, 0x07, 0xab, 0x00, 0x07, 0xab, 0x00, 0x07 +}; + +const uint8_t Menu::_flagJp16x12[] = { + 0x82, 0x92, 0x9d, 0x82, 0x91, 0x9d, 0x82, 0x91, 0x9d, 0x82, 0x91, 0x9d, 0x82, 0x91, 0x9d, 0x82, + 0x91, 0x9d, 0x82, 0x91, 0x9d, 0x82, 0x91, 0x9d, 0x82, 0x91, 0x9d, 0x82, 0x91, 0x9d, 0x82, 0x91, + 0x9d, 0x82, 0x91, 0x9d, 0x82, 0x91, 0x9d, 0x82, 0x91, 0x9d, 0x82, 0x91, 0x9d, 0x83, 0x91, 0x9d, + 0x82, 0x91, 0x9d, 0xf5, 0xf8, 0xfb, 0xf5, 0xf8, 0xfb, 0xf5, 0xf8, 0xfb, 0xf5, 0xf8, 0xfb, 0xf5, + 0xf8, 0xfb, 0xf5, 0xf8, 0xfb, 0xf5, 0xf8, 0xfb, 0xf5, 0xf8, 0xfb, 0xf5, 0xf8, 0xfb, 0xf5, 0xf8, + 0xfb, 0xf5, 0xf8, 0xfb, 0xf5, 0xf8, 0xfb, 0xf5, 0xf8, 0xfb, 0xf5, 0xf8, 0xfb, 0x82, 0x91, 0x9d, + 0x82, 0x91, 0x9d, 0xf5, 0xf8, 0xfb, 0xf5, 0xf8, 0xfb, 0xf5, 0xf8, 0xfb, 0xf5, 0xf8, 0xfb, 0xf5, + 0xf8, 0xfb, 0xf5, 0xf8, 0xfb, 0xf5, 0xf8, 0xfb, 0xf5, 0xf8, 0xfb, 0xf5, 0xf8, 0xfb, 0xf5, 0xf8, + 0xfb, 0xf5, 0xf8, 0xfb, 0xf5, 0xf8, 0xfb, 0xf5, 0xf8, 0xfb, 0xf5, 0xf8, 0xfb, 0x82, 0x91, 0x9d, + 0x82, 0x91, 0x9d, 0xf5, 0xf8, 0xfb, 0xf5, 0xf8, 0xfb, 0xf5, 0xf8, 0xfb, 0xf5, 0xf8, 0xfb, 0xf4, + 0xf2, 0xf4, 0xdf, 0x70, 0x6d, 0xd1, 0x19, 0x13, 0xd1, 0x19, 0x13, 0xdf, 0x70, 0x6d, 0xf4, 0xf2, + 0xf4, 0xf5, 0xf8, 0xfb, 0xf5, 0xf8, 0xfb, 0xf5, 0xf8, 0xfb, 0xf5, 0xf8, 0xfb, 0x82, 0x91, 0x9d, + 0x82, 0x91, 0x9d, 0xf5, 0xf8, 0xfb, 0xf5, 0xf8, 0xfb, 0xf5, 0xf8, 0xfb, 0xf5, 0xf8, 0xfb, 0xe1, + 0x7c, 0x7a, 0xd0, 0x11, 0x0b, 0xd0, 0x11, 0x0b, 0xd0, 0x11, 0x0b, 0xd0, 0x11, 0x0b, 0xe1, 0x7c, + 0x7a, 0xf5, 0xf8, 0xfb, 0xf5, 0xf8, 0xfb, 0xf5, 0xf8, 0xfb, 0xf5, 0xf8, 0xfb, 0x82, 0x91, 0x9d, + 0x82, 0x91, 0x9d, 0xf5, 0xf8, 0xfb, 0xf5, 0xf8, 0xfb, 0xf5, 0xf8, 0xfb, 0xf5, 0xf8, 0xfb, 0xd3, + 0x26, 0x21, 0xd0, 0x11, 0x0b, 0xd0, 0x11, 0x0b, 0xd0, 0x11, 0x0b, 0xd0, 0x11, 0x0b, 0xd3, 0x26, + 0x20, 0xf5, 0xf8, 0xfb, 0xf5, 0xf8, 0xfb, 0xf5, 0xf8, 0xfb, 0xf5, 0xf8, 0xfb, 0x82, 0x91, 0x9d, + 0x82, 0x91, 0x9d, 0xf5, 0xf8, 0xfb, 0xf5, 0xf8, 0xfb, 0xf5, 0xf8, 0xfb, 0xf5, 0xf8, 0xfb, 0xd4, + 0x28, 0x23, 0xd0, 0x11, 0x0b, 0xd0, 0x11, 0x0b, 0xd0, 0x11, 0x0b, 0xd0, 0x11, 0x0b, 0xd4, 0x27, + 0x22, 0xf5, 0xf8, 0xfb, 0xf5, 0xf8, 0xfb, 0xf5, 0xf8, 0xfb, 0xf5, 0xf8, 0xfb, 0x82, 0x91, 0x9d, + 0x82, 0x91, 0x9d, 0xf5, 0xf8, 0xfb, 0xf5, 0xf8, 0xfb, 0xf5, 0xf8, 0xfb, 0xf5, 0xf8, 0xfb, 0xe3, + 0x85, 0x84, 0xd0, 0x11, 0x0b, 0xd0, 0x11, 0x0b, 0xd0, 0x11, 0x0b, 0xd0, 0x11, 0x0b, 0xe3, 0x85, + 0x84, 0xf5, 0xf8, 0xfb, 0xf5, 0xf8, 0xfb, 0xf5, 0xf8, 0xfb, 0xf5, 0xf8, 0xfb, 0x82, 0x91, 0x9d, + 0x82, 0x91, 0x9d, 0xf5, 0xf8, 0xfb, 0xf5, 0xf8, 0xfb, 0xf5, 0xf8, 0xfb, 0xf5, 0xf8, 0xfb, 0xf4, + 0xf4, 0xf7, 0xe1, 0x7d, 0x7b, 0xd4, 0x27, 0x22, 0xd4, 0x27, 0x22, 0xe1, 0x7d, 0x7b, 0xf4, 0xf4, + 0xf7, 0xf5, 0xf8, 0xfb, 0xf5, 0xf8, 0xfb, 0xf5, 0xf8, 0xfb, 0xf5, 0xf8, 0xfb, 0x82, 0x91, 0x9d, + 0x82, 0x91, 0x9d, 0xf5, 0xf8, 0xfb, 0xf5, 0xf8, 0xfb, 0xf5, 0xf8, 0xfb, 0xf5, 0xf8, 0xfb, 0xf5, + 0xf8, 0xfb, 0xf5, 0xf8, 0xfb, 0xf5, 0xf8, 0xfb, 0xf5, 0xf8, 0xfb, 0xf5, 0xf8, 0xfb, 0xf5, 0xf8, + 0xfb, 0xf5, 0xf8, 0xfb, 0xf5, 0xf8, 0xfb, 0xf5, 0xf8, 0xfb, 0xf5, 0xf8, 0xfb, 0x82, 0x91, 0x9d, + 0x82, 0x91, 0x9d, 0xf5, 0xf8, 0xfb, 0xf5, 0xf8, 0xfb, 0xf5, 0xf8, 0xfb, 0xf5, 0xf8, 0xfb, 0xf5, + 0xf8, 0xfb, 0xf5, 0xf8, 0xfb, 0xf5, 0xf8, 0xfb, 0xf5, 0xf8, 0xfb, 0xf5, 0xf8, 0xfb, 0xf5, 0xf8, + 0xfb, 0xf5, 0xf8, 0xfb, 0xf5, 0xf8, 0xfb, 0xf5, 0xf8, 0xfb, 0xf5, 0xf8, 0xfb, 0x82, 0x91, 0x9d, + 0x99, 0xa4, 0xaf, 0x82, 0x91, 0x9d, 0x82, 0x91, 0x9d, 0x82, 0x91, 0x9d, 0x82, 0x91, 0x9d, 0x82, + 0x91, 0x9d, 0x82, 0x91, 0x9d, 0x82, 0x91, 0x9d, 0x82, 0x91, 0x9d, 0x82, 0x91, 0x9d, 0x82, 0x91, + 0x9d, 0x82, 0x91, 0x9d, 0x82, 0x91, 0x9d, 0x82, 0x91, 0x9d, 0x82, 0x91, 0x9d, 0x98, 0xa4, 0xaf +}; + +const uint8_t Menu::_flagSp16x12[] = { + 0x8e, 0x03, 0x00, 0x8d, 0x03, 0x00, 0x8d, 0x03, 0x00, 0x8d, 0x03, 0x00, 0x8d, 0x03, 0x00, 0x8d, + 0x03, 0x00, 0x8d, 0x03, 0x00, 0x8d, 0x03, 0x00, 0x8d, 0x03, 0x00, 0x8d, 0x03, 0x00, 0x8d, 0x03, + 0x00, 0x8d, 0x03, 0x00, 0x8d, 0x03, 0x00, 0x8d, 0x03, 0x00, 0x8d, 0x03, 0x00, 0x8d, 0x03, 0x00, + 0x8d, 0x03, 0x00, 0xff, 0x2f, 0x29, 0xff, 0x2f, 0x29, 0xff, 0x2f, 0x29, 0xff, 0x2f, 0x29, 0xff, + 0x2f, 0x29, 0xff, 0x2f, 0x29, 0xff, 0x2f, 0x29, 0xff, 0x2f, 0x29, 0xff, 0x2f, 0x29, 0xff, 0x2f, + 0x29, 0xff, 0x2f, 0x29, 0xff, 0x2f, 0x29, 0xff, 0x2f, 0x29, 0xff, 0x2f, 0x29, 0x8d, 0x03, 0x00, + 0x8d, 0x03, 0x00, 0xff, 0x2f, 0x29, 0xff, 0x2f, 0x29, 0xff, 0x2f, 0x29, 0xff, 0x2f, 0x29, 0xff, + 0x2f, 0x29, 0xff, 0x2f, 0x29, 0xff, 0x2f, 0x29, 0xff, 0x2f, 0x29, 0xff, 0x2f, 0x29, 0xff, 0x2f, + 0x29, 0xff, 0x2f, 0x29, 0xff, 0x2f, 0x29, 0xff, 0x2f, 0x29, 0xff, 0x2f, 0x29, 0x8d, 0x03, 0x00, + 0xca, 0x80, 0x00, 0xff, 0xd4, 0x39, 0xff, 0xd4, 0x39, 0xd6, 0xad, 0x35, 0xcf, 0xac, 0x46, 0xb5, + 0x9a, 0x5b, 0xc1, 0xa3, 0x56, 0xcb, 0xab, 0x51, 0xe3, 0xbc, 0x42, 0xff, 0xd4, 0x39, 0xff, 0xd4, + 0x39, 0xff, 0xd4, 0x39, 0xff, 0xd4, 0x39, 0xff, 0xd4, 0x39, 0xff, 0xd4, 0x39, 0xca, 0x80, 0x00, + 0xca, 0x80, 0x00, 0xff, 0xd4, 0x39, 0xff, 0xd4, 0x39, 0xff, 0xd3, 0x39, 0xbf, 0x8e, 0x72, 0xe5, + 0x28, 0x1e, 0xea, 0xe5, 0xe0, 0xca, 0xbb, 0x9e, 0xfe, 0xd5, 0x44, 0xff, 0xd4, 0x39, 0xff, 0xd4, + 0x39, 0xff, 0xd4, 0x39, 0xff, 0xd4, 0x39, 0xff, 0xd4, 0x39, 0xff, 0xd4, 0x39, 0xca, 0x80, 0x00, + 0xca, 0x80, 0x00, 0xff, 0xd4, 0x39, 0xff, 0xd4, 0x39, 0xcf, 0xae, 0x4f, 0xe5, 0xde, 0xd5, 0xf4, + 0xba, 0xb6, 0xe5, 0x28, 0x1e, 0xdb, 0xd3, 0xc5, 0xd1, 0xb5, 0x67, 0xff, 0xd4, 0x39, 0xff, 0xd4, + 0x39, 0xff, 0xd4, 0x39, 0xff, 0xd4, 0x39, 0xff, 0xd4, 0x39, 0xff, 0xd4, 0x39, 0xca, 0x80, 0x00, + 0xca, 0x80, 0x00, 0xff, 0xd4, 0x39, 0xff, 0xd4, 0x39, 0xc1, 0xa4, 0x55, 0xe1, 0xda, 0xcf, 0xac, + 0x98, 0x7a, 0xaa, 0x78, 0x59, 0xc3, 0xb5, 0xad, 0xb8, 0x9a, 0x57, 0xff, 0xd4, 0x39, 0xff, 0xd4, + 0x39, 0xff, 0xd4, 0x39, 0xff, 0xd4, 0x39, 0xff, 0xd4, 0x39, 0xff, 0xd4, 0x39, 0xca, 0x80, 0x00, + 0xca, 0x80, 0x00, 0xff, 0xd4, 0x39, 0xff, 0xd4, 0x39, 0xf7, 0xcd, 0x3d, 0xdf, 0xc8, 0x84, 0xab, + 0x8f, 0x52, 0xc0, 0xa3, 0x56, 0xe3, 0xca, 0x7e, 0xfb, 0xd2, 0x41, 0xff, 0xd4, 0x39, 0xff, 0xd4, + 0x39, 0xff, 0xd4, 0x39, 0xff, 0xd4, 0x39, 0xff, 0xd4, 0x39, 0xff, 0xd4, 0x39, 0xca, 0x80, 0x00, + 0xca, 0x80, 0x00, 0xff, 0xd4, 0x39, 0xff, 0xd4, 0x39, 0xff, 0xd4, 0x39, 0xff, 0xd4, 0x39, 0xfb, + 0xd0, 0x3b, 0xff, 0xd4, 0x39, 0xff, 0xd4, 0x39, 0xff, 0xd4, 0x39, 0xff, 0xd4, 0x39, 0xff, 0xd4, + 0x39, 0xff, 0xd4, 0x39, 0xff, 0xd4, 0x39, 0xff, 0xd4, 0x39, 0xff, 0xd4, 0x39, 0xca, 0x80, 0x00, + 0x8d, 0x03, 0x00, 0xff, 0x2f, 0x29, 0xff, 0x2f, 0x29, 0xff, 0x2f, 0x29, 0xff, 0x2f, 0x29, 0xff, + 0x2f, 0x29, 0xff, 0x2f, 0x29, 0xff, 0x2f, 0x29, 0xff, 0x2f, 0x29, 0xff, 0x2f, 0x29, 0xff, 0x2f, + 0x29, 0xff, 0x2f, 0x29, 0xff, 0x2f, 0x29, 0xff, 0x2f, 0x29, 0xff, 0x2f, 0x29, 0x8d, 0x03, 0x00, + 0x8d, 0x03, 0x00, 0xff, 0x2f, 0x29, 0xff, 0x2f, 0x29, 0xff, 0x2f, 0x29, 0xff, 0x2f, 0x29, 0xff, + 0x2f, 0x29, 0xff, 0x2f, 0x29, 0xff, 0x2f, 0x29, 0xff, 0x2f, 0x29, 0xff, 0x2f, 0x29, 0xff, 0x2f, + 0x29, 0xff, 0x2f, 0x29, 0xff, 0x2f, 0x29, 0xff, 0x2f, 0x29, 0xff, 0x2f, 0x29, 0x8d, 0x03, 0x00, + 0x8d, 0x03, 0x00, 0x8d, 0x03, 0x00, 0x8d, 0x03, 0x00, 0x8d, 0x03, 0x00, 0x8d, 0x03, 0x00, 0x8d, + 0x03, 0x00, 0x8d, 0x03, 0x00, 0x8d, 0x03, 0x00, 0x8d, 0x03, 0x00, 0x8d, 0x03, 0x00, 0x8d, 0x03, + 0x00, 0x8d, 0x03, 0x00, 0x8d, 0x03, 0x00, 0x8d, 0x03, 0x00, 0x8d, 0x03, 0x00, 0x8e, 0x03, 0x00 +}; diff --git a/systemstub.h b/systemstub.h index 5d072e4..c1664fe 100644 --- a/systemstub.h +++ b/systemstub.h @@ -55,15 +55,19 @@ struct SystemStub { virtual ~SystemStub() {} - virtual void init(const char *title, int w, int h, bool fullscreen, ScalerParameters *scalerParameters) = 0; + virtual void init(const char *title, int w, int h, bool fullscreen, bool widescreen, ScalerParameters *scalerParameters) = 0; virtual void destroy() = 0; + virtual bool hasWidescreen() const = 0; virtual void setScreenSize(int w, int h) = 0; virtual void setPalette(const uint8_t *pal, int n) = 0; virtual void setPaletteEntry(int i, const Color *c) = 0; virtual void getPaletteEntry(int i, Color *c) = 0; virtual void setOverscanColor(int i) = 0; virtual void copyRect(int x, int y, int w, int h, const uint8_t *buf, int pitch) = 0; + virtual void copyRectRgb24(int x, int y, int w, int h, const uint8_t *rgb) = 0; + virtual void copyRectLeftBorder(int w, int h, const uint8_t *buf) = 0; + virtual void copyRectRightBorder(int w, int h, const uint8_t *buf) = 0; virtual void fadeScreen() = 0; virtual void updateScreen(int shakeOffset) = 0; diff --git a/systemstub_sdl.cpp b/systemstub_sdl.cpp index 6ed5551..8d61544 100644 --- a/systemstub_sdl.cpp +++ b/systemstub_sdl.cpp @@ -39,6 +39,7 @@ struct SystemStub_SDL : SystemStub { bool _fullscreen; uint8_t _overscanColor; uint32_t _rgbPalette[256]; + uint32_t _darkPalette[256]; int _screenW, _screenH; SDL_Joystick *_joystick; bool _fadeOnUpdateScreen; @@ -48,16 +49,23 @@ struct SystemStub_SDL : SystemStub { ScalerType _scalerType; const Scaler *_scaler; int _scaleFactor; + bool _widescreen; + SDL_Texture *_wideTexture; + int _wideMargin; virtual ~SystemStub_SDL() {} - virtual void init(const char *title, int w, int h, bool fullscreen, ScalerParameters *scalerParameters); + virtual void init(const char *title, int w, int h, bool fullscreen, bool widescreen, ScalerParameters *scalerParameters); virtual void destroy(); + virtual bool hasWidescreen() const; virtual void setScreenSize(int w, int h); virtual void setPalette(const uint8_t *pal, int n); virtual void setPaletteEntry(int i, const Color *c); virtual void getPaletteEntry(int i, Color *c); virtual void setOverscanColor(int i); virtual void copyRect(int x, int y, int w, int h, const uint8_t *buf, int pitch); + virtual void copyRectRgb24(int x, int y, int w, int h, const uint8_t *rgb); + virtual void copyRectLeftBorder(int w, int h, const uint8_t *buf); + virtual void copyRectRightBorder(int w, int h, const uint8_t *buf); virtual void fadeScreen(); virtual void updateScreen(int shakeOffset); virtual void processEvents(); @@ -69,6 +77,7 @@ struct SystemStub_SDL : SystemStub { virtual void lockAudio(); virtual void unlockAudio(); + void setPaletteColor(int color, int r, int g, int b); void processEvent(const SDL_Event &ev, bool &paused); void prepareGraphics(); void cleanupGraphics(); @@ -80,7 +89,7 @@ SystemStub *SystemStub_SDL_create() { return new SystemStub_SDL(); } -void SystemStub_SDL::init(const char *title, int w, int h, bool fullscreen, ScalerParameters *scalerParameters) { +void SystemStub_SDL::init(const char *title, int w, int h, bool fullscreen, bool widescreen, ScalerParameters *scalerParameters) { SDL_Init(SDL_INIT_VIDEO | SDL_INIT_AUDIO | SDL_INIT_JOYSTICK); SDL_ShowCursor(SDL_DISABLE); _caption = title; @@ -94,9 +103,13 @@ void SystemStub_SDL::init(const char *title, int w, int h, bool fullscreen, Scal _fullscreen = fullscreen; _scalerType = scalerParameters->type; _scaler = scalerParameters->scaler; - _scaleFactor = scalerParameters->factor; + _scaleFactor = CLIP(scalerParameters->factor, _scaler->factorMin, _scaler->factorMax); memset(_rgbPalette, 0, sizeof(_rgbPalette)); + memset(_darkPalette, 0, sizeof(_darkPalette)); _screenW = _screenH = 0; + _widescreen = widescreen; + _wideTexture = 0; + _wideMargin = 0; setScreenSize(w, h); _joystick = 0; _controller = 0; @@ -125,6 +138,10 @@ void SystemStub_SDL::destroy() { SDL_Quit(); } +bool SystemStub_SDL::hasWidescreen() const { + return _widescreen; +} + void SystemStub_SDL::setScreenSize(int w, int h) { if (_screenW == w && _screenH == h) { return; @@ -140,18 +157,21 @@ void SystemStub_SDL::setScreenSize(int w, int h) { prepareGraphics(); } +void SystemStub_SDL::setPaletteColor(int color, int r, int g, int b) { + _rgbPalette[color] = SDL_MapRGB(_fmt, r, g, b); + _darkPalette[color] = SDL_MapRGB(_fmt, r / 4, g / 4, b / 4); +} + void SystemStub_SDL::setPalette(const uint8_t *pal, int n) { assert(n <= 256); for (int i = 0; i < n; ++i) { - uint8_t r = pal[i * 3 + 0]; - uint8_t g = pal[i * 3 + 1]; - uint8_t b = pal[i * 3 + 2]; - _rgbPalette[i] = SDL_MapRGB(_fmt, r, g, b); + setPaletteColor(i, pal[0], pal[1], pal[2]); + pal += 3; } } void SystemStub_SDL::setPaletteEntry(int i, const Color *c) { - _rgbPalette[i] = SDL_MapRGB(_fmt, c->r, c->g, c->b); + setPaletteColor(i, c->r, c->g, c->b); } void SystemStub_SDL::getPaletteEntry(int i, Color *c) { @@ -196,6 +216,66 @@ void SystemStub_SDL::copyRect(int x, int y, int w, int h, const uint8_t *buf, in } } +void SystemStub_SDL::copyRectRgb24(int x, int y, int w, int h, const uint8_t *rgb) { + assert(x >= 0 && x + w <= _screenW && y >= 0 && y + h <= _screenH); + uint32_t *p = _screenBuffer + y * _screenW + x; + + for (int j = 0; j < h; ++j) { + for (int i = 0; i < w; ++i) { + p[i] = SDL_MapRGB(_fmt, rgb[0], rgb[1], rgb[2]); rgb += 3; + } + p += _screenW; + } + + if (_pi.dbgMask & PlayerInput::DF_DBLOCKS) { + drawRect(x, y, w, h, 0xE7); + } +} + +void SystemStub_SDL::copyRectLeftBorder(int w, int h, const uint8_t *buf) { + assert(w >= _wideMargin); + uint32_t *rgb = (uint32_t *)malloc(w * h * sizeof(uint32_t)); + if (rgb) { + if (buf) { + for (int i = 0; i < w * h; ++i) { + rgb[i] = _darkPalette[buf[i]]; + } + } else { + memset(rgb, 0, w * h * sizeof(uint32_t)); + } + const int xOffset = w - _wideMargin; + SDL_Rect r; + r.x = 0; + r.y = 0; + r.w = _wideMargin; + r.h = h; + SDL_UpdateTexture(_wideTexture, &r, rgb + xOffset, w * sizeof(uint32_t)); + free(rgb); + } +} + +void SystemStub_SDL::copyRectRightBorder(int w, int h, const uint8_t *buf) { + assert(w >= _wideMargin); + uint32_t *rgb = (uint32_t *)malloc(w * h * sizeof(uint32_t)); + if (rgb) { + if (buf) { + for (int i = 0; i < w * h; ++i) { + rgb[i] = _darkPalette[buf[i]]; + } + } else { + memset(rgb, 0, w * h * sizeof(uint32_t)); + } + const int xOffset = 0; + SDL_Rect r; + r.x = _wideMargin + _screenW; + r.y = 0; + r.w = _wideMargin; + r.h = h; + SDL_UpdateTexture(_wideTexture, &r, rgb + xOffset, w * sizeof(uint32_t)); + free(rgb); + } +} + void SystemStub_SDL::fadeScreen() { _fadeOnUpdateScreen = true; } @@ -213,30 +293,38 @@ void SystemStub_SDL::updateScreen(int shakeOffset) { SDL_UpdateTexture(_texture, 0, _screenBuffer, _screenW * sizeof(uint32_t)); } SDL_RenderClear(_renderer); - if (_fadeOnUpdateScreen) { - SDL_SetRenderDrawBlendMode(_renderer, SDL_BLENDMODE_BLEND); + if (_widescreen) { + // borders / background screen + SDL_RenderCopy(_renderer, _wideTexture, 0, 0); + // game screen SDL_Rect r; - r.x = r.y = 0; - SDL_GetRendererOutputSize(_renderer, &r.w, &r.h); - for (int i = 1; i <= 16; ++i) { - SDL_SetRenderDrawColor(_renderer, 0, 0, 0, 256 - i * 16); - SDL_RenderCopy(_renderer, _texture, 0, 0); - SDL_RenderFillRect(_renderer, &r); - SDL_RenderPresent(_renderer); - SDL_Delay(30); + r.y = shakeOffset * _scaleFactor; + SDL_RenderGetLogicalSize(_renderer, &r.w, &r.h); + r.x = (r.w - _texW) / 2; + r.w = _texW; + SDL_RenderCopy(_renderer, _texture, 0, &r); + } else { + if (_fadeOnUpdateScreen) { + SDL_SetRenderDrawBlendMode(_renderer, SDL_BLENDMODE_BLEND); + SDL_Rect r; + r.x = r.y = 0; + SDL_RenderGetLogicalSize(_renderer, &r.w, &r.h); + for (int i = 1; i <= 16; ++i) { + SDL_SetRenderDrawColor(_renderer, 0, 0, 0, 256 - i * 16); + SDL_RenderCopy(_renderer, _texture, 0, 0); + SDL_RenderFillRect(_renderer, &r); + SDL_RenderPresent(_renderer); + SDL_Delay(30); + } + _fadeOnUpdateScreen = false; + SDL_SetRenderDrawBlendMode(_renderer, SDL_BLENDMODE_NONE); + return; } - _fadeOnUpdateScreen = false; - SDL_SetRenderDrawBlendMode(_renderer, SDL_BLENDMODE_NONE); - return; - } - if (shakeOffset != 0) { SDL_Rect r; r.x = 0; r.y = shakeOffset * _scaleFactor; - SDL_GetRendererOutputSize(_renderer, &r.w, &r.h); + SDL_RenderGetLogicalSize(_renderer, &r.w, &r.h); SDL_RenderCopy(_renderer, _texture, 0, &r); - } else { - SDL_RenderCopy(_renderer, _texture, 0, 0); } SDL_RenderPresent(_renderer); } @@ -600,7 +688,7 @@ void SystemStub_SDL::prepareGraphics() { SDL_SetHint(SDL_HINT_RENDER_SCALE_QUALITY, "0"); // nearest pixel sampling break; case kScalerTypeLinear: - SDL_SetHint(SDL_HINT_RENDER_SCALE_QUALITY, "1"); + SDL_SetHint(SDL_HINT_RENDER_SCALE_QUALITY, "1"); // linear filtering break; case kScalerTypeInternal: case kScalerTypeExternal: @@ -609,14 +697,17 @@ void SystemStub_SDL::prepareGraphics() { _texH *= _scaleFactor; break; } - const int windowW = _screenW * _scaleFactor; - const int windowH = _screenH * _scaleFactor; + int windowW = _screenW * _scaleFactor; + int windowH = _screenH * _scaleFactor; int flags = 0; if (_fullscreen) { flags |= SDL_WINDOW_FULLSCREEN_DESKTOP; } else { flags |= SDL_WINDOW_RESIZABLE; } + if (_widescreen) { + windowW = windowH * 16 / 9; + } _window = SDL_CreateWindow(_caption, SDL_WINDOWPOS_CENTERED, SDL_WINDOWPOS_CENTERED, windowW, windowH, flags); SDL_Surface *icon = SDL_LoadBMP(kIconBmp); if (icon) { @@ -627,6 +718,13 @@ void SystemStub_SDL::prepareGraphics() { SDL_RenderSetLogicalSize(_renderer, windowW, windowH); _texture = SDL_CreateTexture(_renderer, kPixelFormat, SDL_TEXTUREACCESS_STREAMING, _texW, _texH); _fmt = SDL_AllocFormat(kPixelFormat); + if (_widescreen) { + const int w = _screenH * 16 / 9; + const int h = _screenH; + _wideTexture = SDL_CreateTexture(_renderer, kPixelFormat, SDL_TEXTUREACCESS_STREAMING, w, h); + // left and right borders + _wideMargin = (w - _screenW) / 2; + } } void SystemStub_SDL::cleanupGraphics() { @@ -634,6 +732,14 @@ void SystemStub_SDL::cleanupGraphics() { free(_screenBuffer); _screenBuffer = 0; } + if (_texture) { + SDL_DestroyTexture(_texture); + _texture = 0; + } + if (_wideTexture) { + SDL_DestroyTexture(_wideTexture); + _wideTexture = 0; + } if (_renderer) { SDL_DestroyRenderer(_renderer); _renderer = 0; @@ -649,18 +755,21 @@ void SystemStub_SDL::cleanupGraphics() { } void SystemStub_SDL::changeGraphics(bool fullscreen, int scaleFactor) { - int factor = scaleFactor; - if (factor < _scaler->factorMin) { - factor = _scaler->factorMin; - } else if (factor > _scaler->factorMax) { - factor = _scaler->factorMax; - } + int factor = CLIP(scaleFactor, _scaler->factorMin, _scaler->factorMax); if (fullscreen == _fullscreen && factor == _scaleFactor) { // no change return; } _fullscreen = fullscreen; _scaleFactor = factor; + if (_texture) { + SDL_DestroyTexture(_texture); + _texture = 0; + } + if (_wideTexture) { + SDL_DestroyTexture(_wideTexture); + _wideTexture = 0; + } if (_renderer) { SDL_DestroyRenderer(_renderer); _renderer = 0; diff --git a/video.cpp b/video.cpp index b4a41c6..aae4b80 100644 --- a/video.cpp +++ b/video.cpp @@ -51,12 +51,22 @@ Video::~Video() { void Video::markBlockAsDirty(int16_t x, int16_t y, uint16_t w, uint16_t h) { debug(DBG_VIDEO, "Video::markBlockAsDirty(%d, %d, %d, %d)", x, y, w, h); - assert(x >= 0 && x + w <= _w && y >= 0 && y + h <= _h); - int bx1 = x / SCREENBLOCK_W; - int by1 = y / SCREENBLOCK_H; - int bx2 = (x + w - 1) / SCREENBLOCK_W; - int by2 = (y + h - 1) / SCREENBLOCK_H; - assert(bx2 < _w / SCREENBLOCK_W && by2 < _h / SCREENBLOCK_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; + if (bx1 < 0) { + bx1 = 0; + } + if (bx2 > (_w / SCREENBLOCK_W) - 1) { + bx2 = (_w / SCREENBLOCK_W) - 1; + } + if (by1 < 0) { + by1 = 0; + } + if (by2 > (_h / SCREENBLOCK_H) - 1) { + by2 = (_h / SCREENBLOCK_H) - 1; + } for (; by1 <= by2; ++by1) { for (int i = bx1; i <= bx2; ++i) { _screenBlocks[by1 * (_w / SCREENBLOCK_W) + i] = 2; @@ -212,6 +222,11 @@ static void PC_decodeMapPlane(int sz, const uint8_t *src, uint8_t *dst) { void Video::PC_decodeMap(int level, int room) { debug(DBG_VIDEO, "Video::PC_decodeMap(%d)", room); + if (!_res->_map) { + assert(_res->_lev); + PC_decodeLev(level, room); + return; + } assert(room < 0x40); int32_t off = READ_LE_UINT32(_res->_map + room * 6); if (off == 0) { @@ -249,6 +264,7 @@ void Video::PC_decodeMap(int level, int room) { } } memcpy(_backLayer, _frontLayer, _layerSize); + PC_setLevelPalettes(); } void Video::PC_setLevelPalettes() { @@ -938,30 +954,25 @@ Color Video::AMIGA_convertColor(const uint16_t color, bool bgr) { // 4bits to 8b } void Video::MAC_decodeMap(int level, int room) { -} - -void Video::MAC_markBlockAsDirty(int x, int y, int w, int h) { - if (x < 0) { - w += x; - x = 0; + DecodeBuffer buf; + memset(&buf, 0, sizeof(buf)); + buf.ptr = _frontLayer; + buf.w = buf.pitch = _w; + buf.h = _h; + buf.setPixel = Video::MAC_drawBuffer; + _res->MAC_loadLevelRoom(level, room, &buf); + memcpy(_backLayer, _frontLayer, _layerSize); + Color roomPalette[256]; + _res->MAC_setupRoomClut(level, room, roomPalette); + for (int j = 0; j < 16; ++j) { + if (j == 5 || j == 7 || j == 14 || j == 15) { + continue; + } + for (int i = 0; i < 16; ++i) { + const int color = j * 16 + i; + _stub->setPaletteEntry(color, &roomPalette[color]); + } } - if (x + w > _w) { - w = _w - x; - } - if (w <= 0) { - return; - } - if (y < 0) { - h += y; - y = 0; - } - if (y + h > _h) { - h = _h - y; - } - if (h <= 0) { - return; - } - markBlockAsDirty(x, y, w, h); } void Video::MAC_drawBuffer(DecodeBuffer *buf, int src_x, int src_y, int src_w, int src_h, uint8_t color) { @@ -1014,3 +1025,31 @@ void Video::MAC_fillRect(int x, int y, int w, int h, uint8_t color) { p += _w; } } + +static void fixOffsetDecodeBuffer(DecodeBuffer *buf, const uint8_t *dataPtr) { + if (buf->xflip) { + buf->x += (int16_t)READ_BE_UINT16(dataPtr + 4) - READ_BE_UINT16(dataPtr) - 1; + } else { + buf->x -= (int16_t)READ_BE_UINT16(dataPtr + 4); + } + buf->y -= (int16_t)READ_BE_UINT16(dataPtr + 6); +} + +void Video::MAC_drawSprite(int x, int y, const uint8_t *data, int frame, bool xflip, bool eraseBackground) { + const uint8_t *dataPtr = _res->MAC_getImageData(data, frame); + if (dataPtr) { + DecodeBuffer buf; + memset(&buf, 0, sizeof(buf)); + buf.xflip = xflip; + buf.ptr = _frontLayer; + buf.w = buf.pitch = _w; + buf.h = _h; + buf.x = x * _layerScale; + buf.y = y * _layerScale; + 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); + } +} diff --git a/video.h b/video.h index 6226177..8526e43 100644 --- a/video.h +++ b/video.h @@ -87,11 +87,11 @@ struct Video { void drawStringLen(const char *str, int len, int x, int y, uint8_t color); static Color AMIGA_convertColor(const uint16_t color, bool bgr = false); void MAC_decodeMap(int level, int room); - void MAC_markBlockAsDirty(int x, int y, int w, int h); static void MAC_drawBuffer(DecodeBuffer *buf, int src_x, int src_y, int src_w, int src_h, uint8_t color); static void MAC_drawBufferMask(DecodeBuffer *buf, int src_x, int src_y, int src_w, int src_h, uint8_t color); static void MAC_drawBufferFont(DecodeBuffer *buf, int src_x, int src_y, int src_w, int src_h, uint8_t color); void MAC_fillRect(int x, int y, int w, int h, uint8_t color); + void MAC_drawSprite(int x, int y, const uint8_t *data, int frame, bool xflip, bool eraseBackground); }; #endif // VIDEO_H__