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 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]. 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: Data Files:
----------- -----------
You will need the original files of the PC (DOS or CD), Amiga or Macintosh 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 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, To hear music during polygonal cutscenes with the PC version, you need to copy
you need to copy the music/ directory of the Amiga version or use the .mod the music/ directory of the Amiga version or use the .mod fileset from
fileset from unexotica [4]. unexotica [4].
To hear voice during in-game dialogues, you'll need to copy the 'VOICE.VCE' For speech with in-game dialogues, you need to copy the 'VOICE.VCE' file from
file from the SegaCD version to the DATA directory. the SegaCD version to the DATA directory.
Running: Running:
-------- --------
By default, the engine will try to load the game data files from the 'DATA' By default, the engine tries to load the game data files from the 'DATA'
directory (as the original game did). The savestates are saved in the current directory, as the original game executable did. The savestates are saved in the
directory. These paths can be changed using command line switches : current directory.
These paths can be changed using command line switches :
Usage: rs [OPTIONS]... Usage: rs [OPTIONS]...
--datapath=PATH Path to data files (default 'DATA') --datapath=PATH Path to data files (default 'DATA')
--savepath=PATH Path to save files (default '.') --savepath=PATH Path to save files (default '.')
--levelnum=NUM Level to start from (default '0') --levelnum=NUM Level to start from (default '0')
--fullscreen Fullscreen display --fullscreen Fullscreen display
--widescreen 16:9 display
--scaler=NAME@X Graphics scaler (default 'scale@3') --scaler=NAME@X Graphics scaler (default 'scale@3')
--language=LANG Language (fr,en,de,sp,it,jp) --language=LANG Language (fr,en,de,sp,it,jp)
The scaler option specifies the algorithm used to smoothen the image in The scaler option specifies the algorithm used to smoothen the image in
addition to a scaling factor. External scalers are also supported, the addition to a scaling factor. External scalers are also supported, the suffix
suffix shall be used as the name. Eg. If you have scaler_xbrz.dll, you can shall be used as the name. Eg. If you have scaler_xbrz.dll, you can pass
pass '--scaler xbrz@2' to use that algorithm with a window size 512x448. '--scaler xbrz@2' to use that algorithm with a doubled window size (512x448).
In-game hotkeys : In-game hotkeys :

View File

@ -1048,7 +1048,9 @@ void Cutscene::unload() {
void Cutscene::prepare() { void Cutscene::prepare() {
_page0 = _vid->_frontLayer; _page0 = _vid->_frontLayer;
_page1 = _vid->_tempLayer; _page1 = _vid->_tempLayer;
memset(_page1, 0, _vid->_layerSize);
_pageC = _vid->_tempLayer2; _pageC = _vid->_tempLayer2;
memset(_pageC, 0, _vid->_layerSize);
_stub->_pi.dirMask = 0; _stub->_pi.dirMask = 0;
_stub->_pi.enter = false; _stub->_pi.enter = false;
_stub->_pi.space = false; _stub->_pi.space = false;

253
game.cpp
View File

@ -157,6 +157,11 @@ void Game::run() {
_stub->_pi.enter = false; _stub->_pi.enter = false;
_stub->_pi.space = false; _stub->_pi.space = false;
_stub->_pi.shift = 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); _stub->updateScreen(0);
_vid.AMIGA_decodeCmp(_res._scratchBuffer + 6, buf); _vid.AMIGA_decodeCmp(_res._scratchBuffer + 6, buf);
free(buf); free(buf);
for (int h = 0; h < kH / 2; h += 2) { int h = 0;
const int y = kH / 2 - h;
_stub->copyRect(0, y, kW, h * 2, buf, kW);
_stub->updateScreen(0);
_stub->sleep(30);
}
while (1) { 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(); _stub->processEvents();
if (_stub->_pi.quit) { if (_stub->_pi.quit) {
break; break;
@ -293,7 +299,9 @@ void Game::mainLoop() {
drawAnims(); drawAnims();
drawCurrentInventoryItem(); drawCurrentInventoryItem();
drawLevelTexts(); drawLevelTexts();
printLevelCode(); if (g_options.enable_password_menu) {
printLevelCode();
}
if (_blinkingConradCounter != 0) { if (_blinkingConradCounter != 0) {
--_blinkingConradCounter; --_blinkingConradCounter;
} }
@ -393,6 +401,15 @@ void Game::playCutscene(int id) {
_cut.play(); _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) { if (id == 0x3D) {
_cut.playCredits(); _cut.playCredits();
} }
@ -442,6 +459,10 @@ void Game::drawCurrentInventoryItem() {
} }
void Game::showFinalScore() { 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); playCutscene(0x49);
char buf[50]; char buf[50];
snprintf(buf, sizeof(buf), "SCORE %08u", _score); snprintf(buf, sizeof(buf), "SCORE %08u", _score);
@ -534,16 +555,16 @@ bool Game::handleConfigPanel() {
_menu._charVar2 = 0xEE; _menu._charVar2 = 0xEE;
_vid.fullRefresh(); _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 }; uint8_t colors[] = { 2, 3, 3, 3 };
int current = 0; int current = 0;
while (!_stub->_pi.quit) { while (!_stub->_pi.quit) {
_menu.drawString(_res.getMenuString(LocaleData::LI_18_RESUME_GAME), y + 2, 9, colors[0]); _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_19_ABORT_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_20_LOAD_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_21_SAVE_GAME), y + 8, 9, colors[3]);
char buf[30]; 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); _menu.drawString(buf, y + 10, 9, 1);
_vid.updateScreen(); _vid.updateScreen();
@ -598,6 +619,10 @@ bool Game::handleConfigPanel() {
} }
bool Game::handleContinueAbort() { 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); playCutscene(0x48);
int timeout = 100; int timeout = 100;
int current_color = 0; int current_color = 0;
@ -799,46 +824,77 @@ static int getLineLength(const uint8_t *str) {
void Game::drawStoryTexts() { void Game::drawStoryTexts() {
if (_textToDisplay != 0xFFFF) { if (_textToDisplay != 0xFFFF) {
if (_res._type == kResourceTypeMac) {
warning("Unhandled textToDisplay %d", _textToDisplay);
_textToDisplay = 0xFFFF;
return;
}
uint8_t textColor = 0xE8; uint8_t textColor = 0xE8;
const uint8_t *str = _res.getGameString(_textToDisplay); const uint8_t *str = _res.getGameString(_textToDisplay);
memcpy(_vid._tempLayer, _vid._frontLayer, _vid._layerSize); memcpy(_vid._tempLayer, _vid._frontLayer, _vid._layerSize);
int textSpeechSegment = 0; int textSpeechSegment = 0;
int textSegmentsCount = 0;
while (!_stub->_pi.quit) { while (!_stub->_pi.quit) {
drawIcon(_currentInventoryIconNum, 80, 8, 0xA); drawIcon(_currentInventoryIconNum, 80, 8, 0xA);
if (*str == 0xFF) { int yPos = 26;
if (_res._lang == LANG_JP) { if (_res._type == kResourceTypeMac) {
if (textSegmentsCount == 0) {
textSegmentsCount = *str++;
}
int len = *str++;
if (*str == '@') {
switch (str[1]) { switch (str[1]) {
case 0: case '1':
textColor = 0xE9; textColor = 0xE9;
break; break;
case 1: case '2':
textColor = 0xEB; textColor = 0xEB;
break; break;
default: default:
warning("Unhandled JP color code 0x%x", str[1]); warning("Unhandled MAC text color code 0x%x", str[1]);
break; break;
} }
str += 2; str += 2;
} else { len -= 2;
textColor = str[1];
// str[2] is an unused color (possibly the shadow)
str += 3;
} }
} for (; len > 0; yPos += 8) {
int yPos = 26; const uint8_t *next = (const uint8_t *)memchr(str, 0x7C, len);
while (1) { if (!next) {
const int len = getLineLength(str); _vid.drawStringLen((const char *)str, len, (176 - len * Video::CHAR_W) / 2, yPos, textColor);
str = (const uint8_t *)_vid.drawString((const char *)str, (176 - len * Video::CHAR_W) / 2, yPos, textColor); // point 'str' to beginning of next text segment
yPos += 8; str += len;
if (*str == 0 || *str == 0xB) { break;
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; MixerChunk chunk;
_res.load_VCE(_textToDisplay, textSpeechSegment++, &chunk.data, &chunk.len); _res.load_VCE(_textToDisplay, textSpeechSegment++, &chunk.data, &chunk.len);
@ -858,10 +914,16 @@ void Game::drawStoryTexts() {
free(chunk.data); free(chunk.data);
} }
_stub->_pi.backspace = false; _stub->_pi.backspace = false;
if (*str == 0) { if (_res._type == kResourceTypeMac) {
break; if (textSpeechSegment == textSegmentsCount) {
break;
}
} else {
if (*str == 0) {
break;
}
++str;
} }
++str;
memcpy(_vid._frontLayer, _vid._tempLayer, _vid._layerSize); memcpy(_vid._frontLayer, _vid._tempLayer, _vid._layerSize);
} }
_textToDisplay = 0xFFFF; _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) { void Game::drawPiege(AnimBufferState *state) {
LivePGE *pge = state->pge; LivePGE *pge = state->pge;
switch (_res._type) { switch (_res._type) {
@ -1082,43 +1135,18 @@ void Game::drawPiege(AnimBufferState *state) {
case kResourceTypeDOS: case kResourceTypeDOS:
drawObject(state->dataPtr, state->x, state->y, pge->flags); drawObject(state->dataPtr, state->x, state->y, pge->flags);
break; break;
case kResourceTypeMac: { case kResourceTypeMac:
DecodeBuffer buf; if (pge->flags & 8) {
memset(&buf, 0, sizeof(buf)); _vid.MAC_drawSprite(state->x, state->y, _res._spc, pge->anim_number, (pge->flags & 2) != 0, _eraseBackground);
buf.xflip = (pge->flags & 2); } else if (pge->index == 0) {
buf.ptr = _vid._frontLayer; if (pge->anim_number == 0x386) {
buf.w = buf.pitch = _vid._w; break;
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));
}
} }
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; break;
} }
@ -1495,35 +1523,26 @@ void Game::loadLevelMap() {
_vid.AMIGA_decodeLev(_currentLevel, _currentRoom); _vid.AMIGA_decodeLev(_currentLevel, _currentRoom);
break; break;
case kResourceTypeDOS: case kResourceTypeDOS:
if (_res._map) { if (_stub->hasWidescreen()) { // draw adjacent rooms
_vid.PC_decodeMap(_currentLevel, _currentRoom); const int leftRoom = _res._ctData[CT_LEFT_ROOM + _currentRoom];
_vid.PC_setLevelPalettes(); if (leftRoom > 0 && hasLevelMap(_currentLevel, leftRoom)) {
} else if (_res._lev) { _vid.PC_decodeMap(_currentLevel, leftRoom);
_vid.PC_decodeLev(_currentLevel, _currentRoom); _stub->copyRectLeftBorder(Video::GAMESCREEN_W, Video::GAMESCREEN_H, _vid._backLayer);
} } else {
break; _stub->copyRectLeftBorder(Video::GAMESCREEN_W, Video::GAMESCREEN_H, 0);
case kResourceTypeMac: }
{ const int rightRoom = _res._ctData[CT_RIGHT_ROOM + _currentRoom];
DecodeBuffer buf; if (rightRoom > 0 && hasLevelMap(_currentLevel, rightRoom)) {
memset(&buf, 0, sizeof(buf)); _vid.PC_decodeMap(_currentLevel, rightRoom);
buf.ptr = _vid._frontLayer; _stub->copyRectRightBorder(Video::GAMESCREEN_W, Video::GAMESCREEN_H, _vid._backLayer);
buf.w = buf.pitch = _vid._w; } else {
buf.h = _vid._h; _stub->copyRectRightBorder(Video::GAMESCREEN_W, Video::GAMESCREEN_H, 0);
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]);
}
} }
} }
_vid.PC_decodeMap(_currentLevel, _currentRoom);
break;
case kResourceTypeMac:
_vid.MAC_decodeMap(_currentLevel, _currentRoom);
break; break;
} }
} }
@ -1703,19 +1722,7 @@ void Game::drawIcon(uint8_t iconNum, int16_t x, int16_t y, uint8_t colMask) {
iconNum = 34; iconNum = 34;
break; break;
} }
{ _vid.MAC_drawSprite(x, y, _res._icn, iconNum, false, true);
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));
}
return; return;
} }
_vid.drawSpriteSub1(buf, _vid._frontLayer + x + y * _vid._w, 16, 16, 16, colMask << 4); _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; 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 { enum Language {
LANG_FR, LANG_FR,
LANG_EN, LANG_EN,
@ -93,6 +103,7 @@ enum Skill {
struct Options { struct Options {
bool bypass_protection; bool bypass_protection;
bool enable_password_menu; bool enable_password_menu;
bool enable_language_selection;
bool fade_out_palette; bool fade_out_palette;
bool use_tiledata; bool use_tiledata;
bool use_text_cutscenes; bool use_text_cutscenes;

View File

@ -22,6 +22,7 @@ static const char *USAGE =
" --savepath=PATH Path to save files (default '.')\n" " --savepath=PATH Path to save files (default '.')\n"
" --levelnum=NUM Start to level, bypass introduction\n" " --levelnum=NUM Start to level, bypass introduction\n"
" --fullscreen Fullscreen display\n" " --fullscreen Fullscreen display\n"
" --widescreen 16:9 display\n"
" --scaler=NAME@X Graphics scaler (default 'scale@3')\n" " --scaler=NAME@X Graphics scaler (default 'scale@3')\n"
" --language=LANG Language (fr,en,de,sp,it,jp)\n" " --language=LANG Language (fr,en,de,sp,it,jp)\n"
; ;
@ -39,6 +40,7 @@ static int detectVersion(FileSystem *fs) {
{ "LEVEL1.LEV", kResourceTypeAmiga, "Amiga" }, { "LEVEL1.LEV", kResourceTypeAmiga, "Amiga" },
{ "DEMO.LEV", kResourceTypeAmiga, "Amiga (Demo)" }, { "DEMO.LEV", kResourceTypeAmiga, "Amiga (Demo)" },
{ "FLASHBACK.BIN", kResourceTypeMac, "Macintosh" }, { "FLASHBACK.BIN", kResourceTypeMac, "Macintosh" },
{ "FLASHBACK.RSRC", kResourceTypeMac, "Macintosh" },
{ 0, -1, 0 } { 0, -1, 0 }
}; };
for (int i = 0; table[i].filename; ++i) { for (int i = 0; table[i].filename; ++i) {
@ -82,6 +84,7 @@ static void initOptions() {
// defaults // defaults
g_options.bypass_protection = true; g_options.bypass_protection = true;
g_options.enable_password_menu = false; g_options.enable_password_menu = false;
g_options.enable_language_selection = false;
g_options.fade_out_palette = true; g_options.fade_out_palette = true;
g_options.use_text_cutscenes = false; g_options.use_text_cutscenes = false;
g_options.use_seq_cutscenes = true; g_options.use_seq_cutscenes = true;
@ -96,6 +99,7 @@ static void initOptions() {
} opts[] = { } opts[] = {
{ "bypass_protection", &g_options.bypass_protection }, { "bypass_protection", &g_options.bypass_protection },
{ "enable_password_menu", &g_options.enable_password_menu }, { "enable_password_menu", &g_options.enable_password_menu },
{ "enable_language_selection", &g_options.enable_language_selection },
{ "fade_out_palette", &g_options.fade_out_palette }, { "fade_out_palette", &g_options.fade_out_palette },
{ "use_tiledata", &g_options.use_tiledata }, { "use_tiledata", &g_options.use_tiledata },
{ "use_text_cutscenes", &g_options.use_text_cutscenes }, { "use_text_cutscenes", &g_options.use_text_cutscenes },
@ -180,6 +184,7 @@ int main(int argc, char *argv[]) {
const char *savePath = "."; const char *savePath = ".";
int levelNum = 0; int levelNum = 0;
bool fullscreen = false; bool fullscreen = false;
bool widescreen = false;
ScalerParameters scalerParameters = ScalerParameters::defaults(); ScalerParameters scalerParameters = ScalerParameters::defaults();
int forcedLanguage = -1; int forcedLanguage = -1;
if (argc == 2) { if (argc == 2) {
@ -197,6 +202,7 @@ int main(int argc, char *argv[]) {
{ "fullscreen", no_argument, 0, 4 }, { "fullscreen", no_argument, 0, 4 },
{ "scaler", required_argument, 0, 5 }, { "scaler", required_argument, 0, 5 },
{ "language", required_argument, 0, 6 }, { "language", required_argument, 0, 6 },
{ "widescreen", no_argument, 0, 7 },
{ 0, 0, 0, 0 } { 0, 0, 0, 0 }
}; };
int index; int index;
@ -241,6 +247,9 @@ int main(int argc, char *argv[]) {
} }
} }
break; break;
case 7:
widescreen = true;
break;
default: default:
printf(USAGE, argv[0]); printf(USAGE, argv[0]);
return 0; return 0;
@ -257,7 +266,7 @@ int main(int argc, char *argv[]) {
const Language language = (forcedLanguage == -1) ? detectLanguage(&fs) : (Language)forcedLanguage; const Language language = (forcedLanguage == -1) ? detectLanguage(&fs) : (Language)forcedLanguage;
SystemStub *stub = SystemStub_SDL_create(); SystemStub *stub = SystemStub_SDL_create();
Game *g = new Game(stub, &fs, savePath, levelNum, (ResourceType)version, language); 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(); g->run();
delete g; delete g;
stub->destroy(); 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); _res->load_PAL_menu(prefix, _res->_scratchBuffer);
_stub->setPalette(_res->_scratchBuffer, 256); _stub->setPalette(_res->_scratchBuffer, 256);
} }
@ -197,7 +198,7 @@ bool Menu::handlePasswordScreen() {
} }
_vid->PC_drawChar(0x20, 21, len + 15); _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(); _vid->updateScreen();
_stub->sleep(EVENTS_DELAY); _stub->sleep(EVENTS_DELAY);
_stub->processEvents(); _stub->processEvents();
@ -262,12 +263,12 @@ bool Menu::handleLevelScreen() {
for (int i = 0; i < 7; ++i) { for (int i = 0; i < 7; ++i) {
drawString(levelTitles[i], 7 + i * 2, 4, (currentLevel == i) ? 2 : 3); 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_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_14_NORMAL), 23, 14, (currentSkill == 1) ? 2 : 3);
drawString(_res->getMenuString(LocaleData::LI_15_EXPERT), 23, 24, (currentSkill == 2) ? 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(); _vid->updateScreen();
_stub->sleep(EVENTS_DELAY); _stub->sleep(EVENTS_DELAY);
@ -372,7 +373,30 @@ void Menu::handleTitleScreen() {
bool quitLoop = false; bool quitLoop = false;
int currentEntry = 0; 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) { while (!quitLoop) {
int selectedItem = -1;
int previousLanguage = currentLanguage;
if (_nextScreen == SCREEN_TITLE) { if (_nextScreen == SCREEN_TITLE) {
_vid->fadeOut(); _vid->fadeOut();
loadPicture("menu1"); loadPicture("menu1");
@ -383,16 +407,25 @@ void Menu::handleTitleScreen() {
_currentScreen = _nextScreen; _currentScreen = _nextScreen;
_nextScreen = -1; _nextScreen = -1;
} }
int selectedItem = -1;
const int yPos = 26 - menuItemsCount * 2; if (g_options.enable_language_selection) {
for (int i = 0; i < menuItemsCount; ++i) { if (_stub->_pi.dirMask & PlayerInput::DIR_LEFT) {
drawString(_res->getMenuString(menuItems[i].str), yPos + i * 2, 20, (i == currentEntry) ? 2 : 3); _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) { if (_stub->_pi.dirMask & PlayerInput::DIR_UP) {
_stub->_pi.dirMask &= ~PlayerInput::DIR_UP; _stub->_pi.dirMask &= ~PlayerInput::DIR_UP;
if (currentEntry != 0) { if (currentEntry != 0) {
@ -413,7 +446,6 @@ void Menu::handleTitleScreen() {
_stub->_pi.enter = false; _stub->_pi.enter = false;
selectedItem = currentEntry; selectedItem = currentEntry;
} }
if (selectedItem != -1) { if (selectedItem != -1) {
_selectedOption = menuItems[selectedItem].opt; _selectedOption = menuItems[selectedItem].opt;
switch (_selectedOption) { switch (_selectedOption) {
@ -444,7 +476,33 @@ void Menu::handleTitleScreen() {
break; break;
} }
_nextScreen = SCREEN_TITLE; _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) { if (_stub->_pi.quit) {
break; break;
} }

7
menu.h
View File

@ -45,6 +45,13 @@ struct Menu {
static const char *_passwordsEnAmiga[]; static const char *_passwordsEnAmiga[];
static const char *_passwordsMac[]; 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; Resource *_res;
SystemStub *_stub; SystemStub *_stub;
Video *_vid; Video *_vid;

View File

@ -71,8 +71,12 @@ void Resource::init() {
} }
break; break;
case kResourceTypeMac: case kResourceTypeMac:
_mac = new ResourceMac(ResourceMac::FILENAME, _fs); if (_fs->exists(ResourceMac::FILENAME1)) {
_mac->loadMap(); _mac = new ResourceMac(ResourceMac::FILENAME1, _fs);
} else if (_fs->exists(ResourceMac::FILENAME2)) {
_mac = new ResourceMac(ResourceMac::FILENAME2, _fs);
}
_mac->load();
break; break;
} }
} }
@ -80,6 +84,17 @@ void Resource::init() {
void Resource::fini() { 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) { bool Resource::fileExists(const char *filename) {
if (_fs->exists(filename)) { if (_fs->exists(filename)) {
return true; return true;
@ -367,6 +382,9 @@ void Resource::load_CINE() {
case kResourceTypeDOS: case kResourceTypeDOS:
if (_cine_off == 0) { if (_cine_off == 0) {
snprintf(_entryName, sizeof(_entryName), "%sCINE.BIN", prefix); snprintf(_entryName, sizeof(_entryName), "%sCINE.BIN", prefix);
if (!_fs->exists(_entryName)) {
strcpy(_entryName, "ENGCINE.BIN");
}
File f; File f;
if (f.open(_entryName, "rb", _fs)) { if (f.open(_entryName, "rb", _fs)) {
int len = f.size(); int len = f.size();
@ -389,6 +407,9 @@ void Resource::load_CINE() {
} }
if (_cine_txt == 0) { if (_cine_txt == 0) {
snprintf(_entryName, sizeof(_entryName), "%sCINE.TXT", prefix); snprintf(_entryName, sizeof(_entryName), "%sCINE.TXT", prefix);
if (!_fs->exists(_entryName)) {
strcpy(_entryName, "ENGCINE.TXT");
}
File f; File f;
if (f.open(_entryName, "rb", _fs)) { if (f.open(_entryName, "rb", _fs)) {
int len = f.size(); 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() { void Resource::load_TEXT() {
_stringsTable = 0; _stringsTable = 0;
switch (_lang) { switch (_lang) {
@ -502,7 +530,7 @@ void Resource::unload(int objType) {
_pol = 0; _pol = 0;
break; break;
default: default:
error("Unimplemented Resource::load() type %d", objType); error("Unimplemented Resource::unload() type %d", objType);
break; break;
} }
} }

View File

@ -183,6 +183,8 @@ struct Resource {
void init(); void init();
void fini(); void fini();
void setLanguage(Language lang);
bool isDOS() const { return _type == kResourceTypeDOS; } bool isDOS() const { return _type == kResourceTypeDOS; }
bool isAmiga() const { return _type == kResourceTypeAmiga; } bool isAmiga() const { return _type == kResourceTypeAmiga; }
@ -197,6 +199,7 @@ struct Resource {
void load_CMP_menu(const char *fileName, uint8_t *dstPtr); void load_CMP_menu(const char *fileName, uint8_t *dstPtr);
void load_SPR_OFF(const char *fileName, uint8_t *sprData); void load_SPR_OFF(const char *fileName, uint8_t *sprData);
void load_CINE(); void load_CINE();
void free_CINE();
void load_TEXT(); void load_TEXT();
void free_TEXT(); void free_TEXT();
void unload(int objType); void unload(int objType);

3
rs.cfg
View File

@ -4,6 +4,9 @@ bypass_protection=true
# use original password level selection menu screen # use original password level selection menu screen
enable_password_menu=false 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 palette to black for screen transition (use blending if false)
fade_out_palette=false fade_out_palette=false

View File

@ -8,85 +8,239 @@
#include "dynlib.h" #include "dynlib.h"
#include "util.h" #include "util.h"
static void scale2x(uint32_t *dst, int dstPitch, const uint32_t *src, int srcPitch, int w, int h) { static void scanline2x(uint32_t *dst0, uint32_t *dst1, const uint32_t *src0, const uint32_t *src1, const uint32_t *src2, int w) {
const int dstPitch2 = dstPitch * 2; uint32_t B, D, E, F, H;
for (int y = 0; y < h; ++y) {
uint32_t *p = dst; // ABC
for (int x = 0; x < w; ++x, p += 2) { // DEF
const uint32_t E = *(src + x); // GHI
const uint32_t B = (y == 0) ? E : *(src + x - srcPitch);
const uint32_t D = (x == 0) ? E : *(src + x - 1); int x = 0;
const uint32_t F = (x == w - 1) ? E : *(src + x + 1);
const uint32_t H = (y == h - 1) ? E : *(src + x + srcPitch); // first pixel (D == E)
if (B != H && D != F) { B = *(src0 + x);
*(p) = D == B ? D : E; E = *(src1 + x);
*(p + 1) = B == F ? F : E; D = E;
*(p + dstPitch) = D == H ? D : E; F = *(src1 + x + 1);
*(p + dstPitch + 1) = H == F ? F : E; H = *(src2 + x);
} else { if (B != H && D != F) {
*(p) = E; dst0[0] = D == B ? D : E;
*(p + 1) = E; dst0[1] = B == F ? F : E;
*(p + dstPitch) = E; dst1[0] = D == H ? D : E;
*(p + dstPitch + 1) = 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; 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) { 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 dstPitch2 = dstPitch * 2;
const int dstPitch3 = dstPitch * 3; const int dstPitch3 = dstPitch * 3;
for (int y = 0; y < h; ++y) {
uint32_t *p = dst; const uint32_t *src0, *src1, *src2;
for (int x = 0; x < w; ++x, p += 3) {
const uint32_t E = *(src + x); // y == 0
const uint32_t B = (y == 0) ? E : *(src + x - srcPitch); src0 = src;
const uint32_t D = (x == 0) ? E : *(src + x - 1); src1 = src;
const uint32_t F = (x == w - 1) ? E : *(src + x + 1); src2 = src + srcPitch;
const uint32_t H = (y == h - 1) ? E : *(src + x + srcPitch); scanline3x(dst, dst + dstPitch, dst + dstPitch2, src0, src1, src2, w);
uint32_t A, C; dst += dstPitch3;
if (y == 0) {
A = D; // center
C = F; src0 = src;
} else { src1 = src + srcPitch;
A = (x == 0) ? B : *(src + x - srcPitch - 1); src2 = src + srcPitch * 2;
C = (x == w - 1) ? B : *(src + x - srcPitch + 1); for (int y = 1; y < h - 1; ++y) {
} scanline3x(dst, dst + dstPitch, dst + dstPitch2, src0, src1, src2, w);
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;
}
}
dst += dstPitch3; 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) { 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, 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 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 ~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 void destroy() = 0;
virtual bool hasWidescreen() const = 0;
virtual void setScreenSize(int w, int h) = 0; virtual void setScreenSize(int w, int h) = 0;
virtual void setPalette(const uint8_t *pal, int n) = 0; virtual void setPalette(const uint8_t *pal, int n) = 0;
virtual void setPaletteEntry(int i, const Color *c) = 0; virtual void setPaletteEntry(int i, const Color *c) = 0;
virtual void getPaletteEntry(int i, Color *c) = 0; virtual void getPaletteEntry(int i, Color *c) = 0;
virtual void setOverscanColor(int i) = 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 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 fadeScreen() = 0;
virtual void updateScreen(int shakeOffset) = 0; virtual void updateScreen(int shakeOffset) = 0;

View File

@ -39,6 +39,7 @@ struct SystemStub_SDL : SystemStub {
bool _fullscreen; bool _fullscreen;
uint8_t _overscanColor; uint8_t _overscanColor;
uint32_t _rgbPalette[256]; uint32_t _rgbPalette[256];
uint32_t _darkPalette[256];
int _screenW, _screenH; int _screenW, _screenH;
SDL_Joystick *_joystick; SDL_Joystick *_joystick;
bool _fadeOnUpdateScreen; bool _fadeOnUpdateScreen;
@ -48,16 +49,23 @@ struct SystemStub_SDL : SystemStub {
ScalerType _scalerType; ScalerType _scalerType;
const Scaler *_scaler; const Scaler *_scaler;
int _scaleFactor; int _scaleFactor;
bool _widescreen;
SDL_Texture *_wideTexture;
int _wideMargin;
virtual ~SystemStub_SDL() {} 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 void destroy();
virtual bool hasWidescreen() const;
virtual void setScreenSize(int w, int h); virtual void setScreenSize(int w, int h);
virtual void setPalette(const uint8_t *pal, int n); virtual void setPalette(const uint8_t *pal, int n);
virtual void setPaletteEntry(int i, const Color *c); virtual void setPaletteEntry(int i, const Color *c);
virtual void getPaletteEntry(int i, Color *c); virtual void getPaletteEntry(int i, Color *c);
virtual void setOverscanColor(int i); virtual void setOverscanColor(int i);
virtual void copyRect(int x, int y, int w, int h, const uint8_t *buf, int pitch); 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 fadeScreen();
virtual void updateScreen(int shakeOffset); virtual void updateScreen(int shakeOffset);
virtual void processEvents(); virtual void processEvents();
@ -69,6 +77,7 @@ struct SystemStub_SDL : SystemStub {
virtual void lockAudio(); virtual void lockAudio();
virtual void unlockAudio(); virtual void unlockAudio();
void setPaletteColor(int color, int r, int g, int b);
void processEvent(const SDL_Event &ev, bool &paused); void processEvent(const SDL_Event &ev, bool &paused);
void prepareGraphics(); void prepareGraphics();
void cleanupGraphics(); void cleanupGraphics();
@ -80,7 +89,7 @@ SystemStub *SystemStub_SDL_create() {
return new SystemStub_SDL(); 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_Init(SDL_INIT_VIDEO | SDL_INIT_AUDIO | SDL_INIT_JOYSTICK);
SDL_ShowCursor(SDL_DISABLE); SDL_ShowCursor(SDL_DISABLE);
_caption = title; _caption = title;
@ -94,9 +103,13 @@ void SystemStub_SDL::init(const char *title, int w, int h, bool fullscreen, Scal
_fullscreen = fullscreen; _fullscreen = fullscreen;
_scalerType = scalerParameters->type; _scalerType = scalerParameters->type;
_scaler = scalerParameters->scaler; _scaler = scalerParameters->scaler;
_scaleFactor = scalerParameters->factor; _scaleFactor = CLIP(scalerParameters->factor, _scaler->factorMin, _scaler->factorMax);
memset(_rgbPalette, 0, sizeof(_rgbPalette)); memset(_rgbPalette, 0, sizeof(_rgbPalette));
memset(_darkPalette, 0, sizeof(_darkPalette));
_screenW = _screenH = 0; _screenW = _screenH = 0;
_widescreen = widescreen;
_wideTexture = 0;
_wideMargin = 0;
setScreenSize(w, h); setScreenSize(w, h);
_joystick = 0; _joystick = 0;
_controller = 0; _controller = 0;
@ -125,6 +138,10 @@ void SystemStub_SDL::destroy() {
SDL_Quit(); SDL_Quit();
} }
bool SystemStub_SDL::hasWidescreen() const {
return _widescreen;
}
void SystemStub_SDL::setScreenSize(int w, int h) { void SystemStub_SDL::setScreenSize(int w, int h) {
if (_screenW == w && _screenH == h) { if (_screenW == w && _screenH == h) {
return; return;
@ -140,18 +157,21 @@ void SystemStub_SDL::setScreenSize(int w, int h) {
prepareGraphics(); 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) { void SystemStub_SDL::setPalette(const uint8_t *pal, int n) {
assert(n <= 256); assert(n <= 256);
for (int i = 0; i < n; ++i) { for (int i = 0; i < n; ++i) {
uint8_t r = pal[i * 3 + 0]; setPaletteColor(i, pal[0], pal[1], pal[2]);
uint8_t g = pal[i * 3 + 1]; pal += 3;
uint8_t b = pal[i * 3 + 2];
_rgbPalette[i] = SDL_MapRGB(_fmt, r, g, b);
} }
} }
void SystemStub_SDL::setPaletteEntry(int i, const Color *c) { 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) { 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() { void SystemStub_SDL::fadeScreen() {
_fadeOnUpdateScreen = true; _fadeOnUpdateScreen = true;
} }
@ -213,30 +293,38 @@ void SystemStub_SDL::updateScreen(int shakeOffset) {
SDL_UpdateTexture(_texture, 0, _screenBuffer, _screenW * sizeof(uint32_t)); SDL_UpdateTexture(_texture, 0, _screenBuffer, _screenW * sizeof(uint32_t));
} }
SDL_RenderClear(_renderer); SDL_RenderClear(_renderer);
if (_fadeOnUpdateScreen) { if (_widescreen) {
SDL_SetRenderDrawBlendMode(_renderer, SDL_BLENDMODE_BLEND); // borders / background screen
SDL_RenderCopy(_renderer, _wideTexture, 0, 0);
// game screen
SDL_Rect r; SDL_Rect r;
r.x = r.y = 0; r.y = shakeOffset * _scaleFactor;
SDL_GetRendererOutputSize(_renderer, &r.w, &r.h); SDL_RenderGetLogicalSize(_renderer, &r.w, &r.h);
for (int i = 1; i <= 16; ++i) { r.x = (r.w - _texW) / 2;
SDL_SetRenderDrawColor(_renderer, 0, 0, 0, 256 - i * 16); r.w = _texW;
SDL_RenderCopy(_renderer, _texture, 0, 0); SDL_RenderCopy(_renderer, _texture, 0, &r);
SDL_RenderFillRect(_renderer, &r); } else {
SDL_RenderPresent(_renderer); if (_fadeOnUpdateScreen) {
SDL_Delay(30); 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; SDL_Rect r;
r.x = 0; r.x = 0;
r.y = shakeOffset * _scaleFactor; r.y = shakeOffset * _scaleFactor;
SDL_GetRendererOutputSize(_renderer, &r.w, &r.h); SDL_RenderGetLogicalSize(_renderer, &r.w, &r.h);
SDL_RenderCopy(_renderer, _texture, 0, &r); SDL_RenderCopy(_renderer, _texture, 0, &r);
} else {
SDL_RenderCopy(_renderer, _texture, 0, 0);
} }
SDL_RenderPresent(_renderer); SDL_RenderPresent(_renderer);
} }
@ -600,7 +688,7 @@ void SystemStub_SDL::prepareGraphics() {
SDL_SetHint(SDL_HINT_RENDER_SCALE_QUALITY, "0"); // nearest pixel sampling SDL_SetHint(SDL_HINT_RENDER_SCALE_QUALITY, "0"); // nearest pixel sampling
break; break;
case kScalerTypeLinear: case kScalerTypeLinear:
SDL_SetHint(SDL_HINT_RENDER_SCALE_QUALITY, "1"); SDL_SetHint(SDL_HINT_RENDER_SCALE_QUALITY, "1"); // linear filtering
break; break;
case kScalerTypeInternal: case kScalerTypeInternal:
case kScalerTypeExternal: case kScalerTypeExternal:
@ -609,14 +697,17 @@ void SystemStub_SDL::prepareGraphics() {
_texH *= _scaleFactor; _texH *= _scaleFactor;
break; break;
} }
const int windowW = _screenW * _scaleFactor; int windowW = _screenW * _scaleFactor;
const int windowH = _screenH * _scaleFactor; int windowH = _screenH * _scaleFactor;
int flags = 0; int flags = 0;
if (_fullscreen) { if (_fullscreen) {
flags |= SDL_WINDOW_FULLSCREEN_DESKTOP; flags |= SDL_WINDOW_FULLSCREEN_DESKTOP;
} else { } else {
flags |= SDL_WINDOW_RESIZABLE; flags |= SDL_WINDOW_RESIZABLE;
} }
if (_widescreen) {
windowW = windowH * 16 / 9;
}
_window = SDL_CreateWindow(_caption, SDL_WINDOWPOS_CENTERED, SDL_WINDOWPOS_CENTERED, windowW, windowH, flags); _window = SDL_CreateWindow(_caption, SDL_WINDOWPOS_CENTERED, SDL_WINDOWPOS_CENTERED, windowW, windowH, flags);
SDL_Surface *icon = SDL_LoadBMP(kIconBmp); SDL_Surface *icon = SDL_LoadBMP(kIconBmp);
if (icon) { if (icon) {
@ -627,6 +718,13 @@ void SystemStub_SDL::prepareGraphics() {
SDL_RenderSetLogicalSize(_renderer, windowW, windowH); SDL_RenderSetLogicalSize(_renderer, windowW, windowH);
_texture = SDL_CreateTexture(_renderer, kPixelFormat, SDL_TEXTUREACCESS_STREAMING, _texW, _texH); _texture = SDL_CreateTexture(_renderer, kPixelFormat, SDL_TEXTUREACCESS_STREAMING, _texW, _texH);
_fmt = SDL_AllocFormat(kPixelFormat); _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() { void SystemStub_SDL::cleanupGraphics() {
@ -634,6 +732,14 @@ void SystemStub_SDL::cleanupGraphics() {
free(_screenBuffer); free(_screenBuffer);
_screenBuffer = 0; _screenBuffer = 0;
} }
if (_texture) {
SDL_DestroyTexture(_texture);
_texture = 0;
}
if (_wideTexture) {
SDL_DestroyTexture(_wideTexture);
_wideTexture = 0;
}
if (_renderer) { if (_renderer) {
SDL_DestroyRenderer(_renderer); SDL_DestroyRenderer(_renderer);
_renderer = 0; _renderer = 0;
@ -649,18 +755,21 @@ void SystemStub_SDL::cleanupGraphics() {
} }
void SystemStub_SDL::changeGraphics(bool fullscreen, int scaleFactor) { void SystemStub_SDL::changeGraphics(bool fullscreen, int scaleFactor) {
int factor = scaleFactor; int factor = CLIP(scaleFactor, _scaler->factorMin, _scaler->factorMax);
if (factor < _scaler->factorMin) {
factor = _scaler->factorMin;
} else if (factor > _scaler->factorMax) {
factor = _scaler->factorMax;
}
if (fullscreen == _fullscreen && factor == _scaleFactor) { if (fullscreen == _fullscreen && factor == _scaleFactor) {
// no change // no change
return; return;
} }
_fullscreen = fullscreen; _fullscreen = fullscreen;
_scaleFactor = factor; _scaleFactor = factor;
if (_texture) {
SDL_DestroyTexture(_texture);
_texture = 0;
}
if (_wideTexture) {
SDL_DestroyTexture(_wideTexture);
_wideTexture = 0;
}
if (_renderer) { if (_renderer) {
SDL_DestroyRenderer(_renderer); SDL_DestroyRenderer(_renderer);
_renderer = 0; _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) { 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); 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 = _layerScale * x / SCREENBLOCK_W;
int bx1 = x / SCREENBLOCK_W; int by1 = _layerScale * y / SCREENBLOCK_H;
int by1 = y / SCREENBLOCK_H; int bx2 = _layerScale * (x + w - 1) / SCREENBLOCK_W;
int bx2 = (x + w - 1) / SCREENBLOCK_W; int by2 = _layerScale * (y + h - 1) / SCREENBLOCK_H;
int by2 = (y + h - 1) / SCREENBLOCK_H; if (bx1 < 0) {
assert(bx2 < _w / SCREENBLOCK_W && by2 < _h / SCREENBLOCK_H); 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 (; by1 <= by2; ++by1) {
for (int i = bx1; i <= bx2; ++i) { for (int i = bx1; i <= bx2; ++i) {
_screenBlocks[by1 * (_w / SCREENBLOCK_W) + i] = 2; _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) { void Video::PC_decodeMap(int level, int room) {
debug(DBG_VIDEO, "Video::PC_decodeMap(%d)", room); debug(DBG_VIDEO, "Video::PC_decodeMap(%d)", room);
if (!_res->_map) {
assert(_res->_lev);
PC_decodeLev(level, room);
return;
}
assert(room < 0x40); assert(room < 0x40);
int32_t off = READ_LE_UINT32(_res->_map + room * 6); int32_t off = READ_LE_UINT32(_res->_map + room * 6);
if (off == 0) { if (off == 0) {
@ -249,6 +264,7 @@ void Video::PC_decodeMap(int level, int room) {
} }
} }
memcpy(_backLayer, _frontLayer, _layerSize); memcpy(_backLayer, _frontLayer, _layerSize);
PC_setLevelPalettes();
} }
void Video::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_decodeMap(int level, int room) {
} DecodeBuffer buf;
memset(&buf, 0, sizeof(buf));
void Video::MAC_markBlockAsDirty(int x, int y, int w, int h) { buf.ptr = _frontLayer;
if (x < 0) { buf.w = buf.pitch = _w;
w += x; buf.h = _h;
x = 0; 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) { 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; 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); 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); static Color AMIGA_convertColor(const uint16_t color, bool bgr = false);
void MAC_decodeMap(int level, int room); 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_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_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); 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_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__ #endif // VIDEO_H__