Import 0.4.2

This commit is contained in:
Gregory Montoir 2018-03-19 00:00:00 +08:00
parent 7f1af541a6
commit a4c4ed8fef
16 changed files with 960 additions and 296 deletions

View File

@ -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 :

View File

@ -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;

253
game.cpp
View File

@ -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);

View File

@ -69,6 +69,16 @@ inline void SWAP(T &a, T &b) {
b = tmp;
}
template<typename T>
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;

View File

@ -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();

View File

@ -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;
}

7
menu.h
View File

@ -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;

View File

@ -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;
}
}

View File

@ -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);

3
rs.cfg
View File

@ -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

View File

@ -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) {

View File

@ -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
};

View File

@ -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;

View File

@ -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;

View File

@ -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);
}
}

View File

@ -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__