diff --git a/Makefile b/Makefile index e304ef8..93d085f 100644 --- a/Makefile +++ b/Makefile @@ -1,12 +1,12 @@ -SDL_CFLAGS = `sdl-config --cflags` -SDL_LIBS = `sdl-config --libs` -MODPLUG_LIBS = -lmodplug -# TREMOR_LIBS = -lvorbisidec -logg -ZLIB_LIBS = -lz +SDL_CFLAGS := `sdl2-config --cflags` +SDL_LIBS := `sdl2-config --libs` -CXX := clang++ -CXXFLAGS := -Wall -MMD $(SDL_CFLAGS) -DUSE_MODPLUG -DUSE_ZLIB # -DUSE_TREMOR +MODPLUG_LIBS := -lmodplug +TREMOR_LIBS := -lvorbisidec -logg +ZLIB_LIBS := -lz + +CXXFLAGS += -Wall -MMD $(SDL_CFLAGS) -DUSE_MODPLUG -DUSE_TREMOR -DUSE_ZLIB SRCS = collision.cpp cutscene.cpp file.cpp fs.cpp game.cpp graphics.cpp main.cpp menu.cpp \ mixer.cpp mod_player.cpp ogg_player.cpp piege.cpp resource.cpp resource_aba.cpp \ diff --git a/README.txt b/README.txt index d159aef..ee28cdd 100644 --- a/README.txt +++ b/README.txt @@ -1,6 +1,6 @@ REminiscence README -Release version: 0.3.2 +Release version: 0.3.3 ------------------------------------------------------------------------------- @@ -24,30 +24,9 @@ Data Files: You will need the original files of the PC (DOS or CD) or Amiga release. -To hear background music during polygonal cutscenes with the PC version, -you'll need to copy the .mod files of the Amiga version : - - mod.flashback-ascenseur - mod.flashback-ceinturea - mod.flashback-chute - mod.flashback-desintegr - mod.flashback-donneobjt - mod.flashback-fin - mod.flashback-fin2 - mod.flashback-game_over - mod.flashback-holocube - mod.flashback-introb - mod.flashback-jungle - mod.flashback-logo - mod.flashback-memoire - mod.flashback-missionca - mod.flashback-options1 - mod.flashback-options2 - mod.flashback-reunion - mod.flashback-taxi - mod.flashback-teleport2 - mod.flashback-teleporta - mod.flashback-voyage +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. To hear voice during in-game dialogues, you'll need to copy the 'VOICE.VCE' file from the SegaCD version to the DATA directory. @@ -87,7 +66,6 @@ Debug hotkeys : Ctrl F toggle fast mode Ctrl I Conrad 'infinite' life Ctrl B toggle display of updated dirty blocks - Ctrl M mirror mode (right - left swapped) Credits: diff --git a/cutscene.cpp b/cutscene.cpp index a4952ca..364db4d 100644 --- a/cutscene.cpp +++ b/cutscene.cpp @@ -33,7 +33,7 @@ void Cutscene::sync() { } void Cutscene::copyPalette(const uint8_t *pal, uint16_t num) { - uint8_t *dst = (uint8_t *)_palBuf; + uint8_t *dst = _palBuf; if (num != 0) { dst += 0x20; } @@ -1091,10 +1091,11 @@ void Cutscene::play() { } } } - if (g_options.use_text_cutscenes && _res->_lang == LANG_FR) { - for (int i = 0; _frTextsTable[i].str; ++i) { - if (_id == _frTextsTable[i].num) { - playText(_frTextsTable[i].str); + if (g_options.use_text_cutscenes) { + const Text *textsTable = (_res->_lang == LANG_FR) ? _frTextsTable : _enTextsTable; + for (int i = 0; textsTable[i].str; ++i) { + if (_id == textsTable[i].num) { + playText(textsTable[i].str); break; } } diff --git a/cutscene.h b/cutscene.h index e890eb4..34ce937 100644 --- a/cutscene.h +++ b/cutscene.h @@ -40,6 +40,7 @@ struct Cutscene { static const uint8_t _musicTable[]; static const uint8_t _protectionShapeData[]; static const Text _frTextsTable[]; + static const Text _enTextsTable[]; Graphics _gfx; Resource *_res; diff --git a/file.cpp b/file.cpp index 0962a10..c60bda1 100644 --- a/file.cpp +++ b/file.cpp @@ -11,6 +11,10 @@ #ifdef USE_ZLIB #include "zlib.h" #endif +#ifdef USE_RWOPS +#include +#include +#endif struct File_impl { bool _ioErr; @@ -128,6 +132,62 @@ struct GzipFile : File_impl { }; #endif +#ifdef USE_RWOPS +struct AssetFile: File_impl { + SDL_RWops *_rw; + AssetFile() : _rw(0) {} + bool open(const char *path, const char *mode) { + _ioErr = false; + _rw = SDL_RWFromFile(path, "rb"); + if (!_rw) { + // try uppercase + char fixedPath[MAXPATHLEN]; + { + int i = 0; + for (; path[i] && i < MAXPATHLEN - 1; ++i) { + fixedPath[i] = path[i]; + if (fixedPath[i] >= 'a' && fixedPath[i] <= 'z') { + fixedPath[i] += 'A' - 'a'; + } + } + fixedPath[i] = 0; + } + _rw = SDL_RWFromFile(fixedPath, "rb"); + } + return _rw != 0; + } + void close() { + if (_rw) { + SDL_RWclose(_rw); + _rw = 0; + } + } + uint32_t size() { + if (_rw) { + return SDL_RWsize(_rw); + } + return 0; + } + void seek(int32_t off) { + if (_rw) { + SDL_RWseek(_rw, off, RW_SEEK_SET); + } + } + uint32_t read(void *ptr, uint32_t len) { + if (_rw) { + const int count = SDL_RWread(_rw, ptr, 1, len); + if (count != len) { + _ioErr = true; + } + } + return 0; + } + uint32_t write(void *ptr, uint32_t len) { + _ioErr = true; + return 0; + } +}; +#endif File::File() : _impl(0) { @@ -155,6 +215,23 @@ bool File::open(const char *filename, const char *mode, FileSystem *fs) { free(path); return ret; } +#ifdef USE_RWOPS + if (mode[0] == 'r') { + _impl = new AssetFile; + return _impl->open(filename, mode); + } else if (mode[0] == 'w') { + bool ret = false; + char *prefPath = SDL_GetPrefPath("org.cyxdown", "fb"); + if (prefPath) { + char path[MAXPATHLEN]; + snprintf(path, sizeof(path), "%s/%s", prefPath, filename); + _impl = new StdioFile; + ret = _impl->open(path, mode); + SDL_free(prefPath); + } + return ret; + } +#endif return false; } diff --git a/fs.cpp b/fs.cpp index 0087f47..b5c4b0f 100644 --- a/fs.cpp +++ b/fs.cpp @@ -12,6 +12,9 @@ #include #include #endif +#ifdef USE_RWOPS +#include +#endif #include "fs.h" #include "util.h" @@ -47,18 +50,26 @@ struct FileSystem_impl { debug(DBG_FILE, "Found %d files and %d directories", _filesCount, _dirsCount); } - char *findPath(const char *name) const { + int findPathIndex(const char *name) const { for (int i = 0; i < _filesCount; ++i) { if (strcasecmp(_filesList[i].name, name) == 0) { - const char *dir = _dirsList[_filesList[i].dir]; - const int len = strlen(dir) + 1 + strlen(_filesList[i].name) + 1; - char *p = (char *)malloc(len); - if (p) { - snprintf(p, len, "%s/%s", dir, _filesList[i].name); - } - return p; + return i; } } + return -1; + } + + char *getPath(const char *name) const { + const int i = findPathIndex(name); + if (i >= 0) { + const char *dir = _dirsList[_filesList[i].dir]; + const int len = strlen(dir) + 1 + strlen(_filesList[i].name) + 1; + char *p = (char *)malloc(len); + if (p) { + snprintf(p, len, "%s/%s", dir, _filesList[i].name); + } + return p; + } return 0; } @@ -146,13 +157,19 @@ FileSystem::~FileSystem() { } char *FileSystem::findPath(const char *filename) const { - return _impl->findPath(filename); + return _impl->getPath(filename); } bool FileSystem::exists(const char *filename) const { - char *path = findPath(filename); - if (path) { - free(path); + if (_impl->findPathIndex(filename) >= 0) { + return true; } - return path != 0; +#ifdef USE_RWOPS + SDL_RWops *rw = SDL_RWFromFile(filename, "rb"); + if (rw) { + SDL_RWclose(rw); + return true; + } +#endif + return false; } diff --git a/game.cpp b/game.cpp index 3692c8b..8278a69 100644 --- a/game.cpp +++ b/game.cpp @@ -28,7 +28,7 @@ void Game::run() { _randSeed = time(0); if (_demoBin != -1) { - if (_demoBin < (int)ARRAYSIZE(_demoInputs)) { + if (_demoBin < ARRAYSIZE(_demoInputs)) { const char *fn = _demoInputs[_demoBin].name; debug(DBG_INFO, "Loading inputs from '%s'", fn); _res.load_DEM(fn); @@ -50,7 +50,9 @@ void Game::run() { break; case kResourceTypeDOS: _res.load("FB_TXT", Resource::OT_FNT); - _res._hasSeqData = _fs->exists("INTRO.SEQ"); + if (g_options.use_seq_cutscenes) { + _res._hasSeqData = _fs->exists("INTRO.SEQ"); + } if (_fs->exists("logosssi.cmd")) { _cut._patchedOffsetsTable = Cutscene::_ssiOffsetsTable; } @@ -91,10 +93,12 @@ void Game::run() { if (_demoBin != -1) { _currentLevel = _demoInputs[_demoBin].level; _randSeed = 0; + } else if (_res._isDemo) { + // do not present title screen and menus } else { + _mix.playMusic(1); switch (_res._type) { case kResourceTypeDOS: - _mix.playMusic(1); _menu.handleTitleScreen(); if (_menu._selectedOption == Menu::MENU_OPTION_ITEM_QUIT || _stub->_pi.quit) { _stub->_pi.quit = true; @@ -148,11 +152,10 @@ void Game::displayTitleScreenAmiga() { _res.load_CMP_menu(FILENAME, _res._memBuf); static const int kW = 320; static const int kH = 224; - uint8_t *buf = (uint8_t *)malloc(kW * kH); + uint8_t *buf = (uint8_t *)calloc(kW * kH, 1); if (!buf) { error("Failed to allocate screen buffer w=%d h=%d", kW, kH); } - _vid.AMIGA_decodeCmp(_res._memBuf + 6, buf); static const int kAmigaColors[] = { 0x000, 0x123, 0x012, 0x134, 0x433, 0x453, 0x046, 0x245, 0x751, 0x455, 0x665, 0x268, 0x961, 0x478, 0x677, 0x786, @@ -166,7 +169,14 @@ void Game::displayTitleScreenAmiga() { _stub->setScreenSize(kW, kH); _stub->copyRect(0, 0, kW, kH, buf, kW); _stub->updateScreen(0); + _vid.AMIGA_decodeCmp(_res._memBuf + 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); + } while (1) { _stub->processEvents(); if (_stub->_pi.quit) { @@ -243,6 +253,9 @@ void Game::mainLoop() { } } if (oldLevel != _currentLevel) { + if (_res._isDemo) { + _currentLevel = oldLevel; + } changeLevel(); _pge_opTempVar1 = 0; return; @@ -598,7 +611,9 @@ bool Game::handleContinueAbort() { bool Game::handleProtectionScreen() { bool valid = true; _cut.prepare(); - _cut.copyPalette(_protectionPal, 0); + const int palOffset = _res.isAmiga() ? 32 : 0; + _cut.copyPalette(_protectionPal + palOffset, 0); + _cut.updatePalette(); _cut._gfx.setClippingRect(64, 48, 128, 128); @@ -622,10 +637,10 @@ bool Game::handleProtectionScreen() { do { codeText[len] = '\0'; memcpy(_vid._frontLayer, _vid._tempLayer, _vid._layerSize); - _menu.drawString("PROTECTION", 2, 11, 5); + _vid.drawString("PROTECTION", 11 * 8, 2 * 8, _menu._charVar2); char buf[20]; snprintf(buf, sizeof(buf), "CODE %d : %s", codeNum + 1, codeText); - _menu.drawString(buf, 23, 8, 5); + _vid.drawString(buf, 8 * 8, 23 * 8, _menu._charVar2); _vid.updateScreen(); _stub->sleep(50); _stub->processEvents(); @@ -679,7 +694,11 @@ void Game::printLevelCode() { --_printLevelCodeCounter; if (_printLevelCodeCounter != 0) { char buf[32]; - snprintf(buf, sizeof(buf), "CODE: %s", _menu._passwords[_currentLevel][_skillLevel]); + const char *code = Menu::_passwords[_currentLevel][_skillLevel]; + if (_res.isAmiga() && _res._lang == LANG_FR) { + code = Menu::_passwordsFrAmiga[_skillLevel * 7 + _currentLevel]; + } + snprintf(buf, sizeof(buf), "CODE: %s", code); _vid.drawString(buf, (_vid._w - strlen(buf) * 8) / 2, 16, 0xE7); } } @@ -1506,16 +1525,30 @@ void Game::handleInventory() { int icon_h = 5; int icon_y = 140; int icon_num = 31; + static const int icon_spr_w = 16; + static const int icon_spr_h = 16; do { int icon_x = 56; int icon_w = 9; do { drawIcon(icon_num, icon_x, icon_y, 0xF); ++icon_num; - icon_x += 16; + icon_x += icon_spr_w; } while (--icon_w); - icon_y += 16; + icon_y += icon_spr_h; } while (--icon_h); + if (_res._type == kResourceTypeAmiga) { + // draw outline rectangle + static const uint8_t outline_color = 0xE7; + uint8_t *p = _vid._frontLayer + 140 * Video::GAMESCREEN_W + 56; + memset(p + 1, outline_color, 9 * icon_spr_w - 2); + p += Video::GAMESCREEN_W; + for (int y = 1; y < 5 * icon_spr_h - 1; ++y) { + p[0] = p[9 * icon_spr_w - 1] = outline_color; + p += Video::GAMESCREEN_W; + } + memset(p + 1, outline_color, 9 * icon_spr_w - 2); + } if (!display_score) { int icon_x_pos = 72; diff --git a/intern.h b/intern.h index 0d2b044..e41511b 100644 --- a/intern.h +++ b/intern.h @@ -7,24 +7,20 @@ #ifndef INTERN_H__ #define INTERN_H__ -#include -#include -#include -#include +#include +#include +#include +#include #include -#ifndef ABS +#undef ABS #define ABS(x) ((x)<0?-(x):(x)) -#endif -#ifndef MAX +#undef MAX #define MAX(x,y) ((x)>(y)?(x):(y)) -#endif -#ifndef MIN +#undef MIN #define MIN(x,y) ((x)<(y)?(x):(y)) -#endif -#ifndef ARRAYSIZE -#define ARRAYSIZE(a) (sizeof(a)/sizeof(a[0])) -#endif +#undef ARRAYSIZE +#define ARRAYSIZE(a) (int)(sizeof(a)/sizeof(a[0])) inline uint16_t READ_BE_UINT16(const void *ptr) { const uint8_t *b = (const uint8_t *)ptr; @@ -93,6 +89,7 @@ struct Options { bool fade_out_palette; bool use_tiledata; bool use_text_cutscenes; + bool use_seq_cutscenes; }; struct Color { diff --git a/main.cpp b/main.cpp index fedc59c..d22d563 100644 --- a/main.cpp +++ b/main.cpp @@ -4,6 +4,7 @@ * Copyright (C) 2005-2015 Gregory Montoir (cyx@users.sourceforge.net) */ +#include #include #include #include @@ -19,7 +20,7 @@ static const char *USAGE = "Usage: %s [OPTIONS]...\n" " --datapath=PATH Path to data files (default 'DATA')\n" " --savepath=PATH Path to save files (default '.')\n" - " --levelnum=NUM Level to start from (default '0')\n" + " --levelnum=NUM Start to level, bypass introduction\n" " --fullscreen Fullscreen display\n" " --scaler=INDEX Graphics scaler\n" " --language=LANG Language (fr,en,de,sp,it)\n" @@ -36,7 +37,7 @@ static int detectVersion(FileSystem *fs) { { "LEVEL1.MAP", kResourceTypeDOS, "DOS" }, { "LEVEL1.LEV", kResourceTypeAmiga, "Amiga" }, { "DEMO.LEV", kResourceTypeAmiga, "Amiga (Demo)" }, - { 0, -1 } + { 0, -1, 0 } }; for (int i = 0; table[i].filename; ++i) { File f; @@ -82,6 +83,7 @@ static void initOptions() { g_options.enable_password_menu = false; g_options.fade_out_palette = true; g_options.use_text_cutscenes = false; + g_options.use_seq_cutscenes = true; // read configuration file struct { const char *name; @@ -93,6 +95,7 @@ static void initOptions() { { "fade_out_palette", &g_options.fade_out_palette }, { "use_tiledata", &g_options.use_tiledata }, { "use_text_cutscenes", &g_options.use_text_cutscenes }, + { "use_seq_cutscenes", &g_options.use_seq_cutscenes }, { 0, 0 } }; static const char *filename = "rs.cfg"; @@ -126,7 +129,6 @@ static void initOptions() { static const int DEFAULT_SCALER = SCALER_SCALE_3X; -#undef main int main(int argc, char *argv[]) { const char *dataPath = "DATA"; const char *savePath = "."; diff --git a/menu.h b/menu.h index 45b4372..7b89cbe 100644 --- a/menu.h +++ b/menu.h @@ -40,6 +40,7 @@ struct Menu { }; static const char *_passwords[8][3]; + static const char *_passwordsFrAmiga[]; Resource *_res; SystemStub *_stub; diff --git a/piege.cpp b/piege.cpp index a784d6c..8b7fb73 100644 --- a/piege.cpp +++ b/piege.cpp @@ -488,9 +488,6 @@ void Game::pge_addToCurrentRoomList(LivePGE *pge, uint8_t room) { void Game::pge_getInput() { inp_update(); _inp_lastKeysHit = _stub->_pi.dirMask; - if (_stub->_pi.mirrorMode && (_inp_lastKeysHit & 0xC)) { - _inp_lastKeysHit ^= 0xC; // invert left/right - } if ((_inp_lastKeysHit & 0xC) && (_inp_lastKeysHit & 0x3)) { const uint8_t mask = (_inp_lastKeysHit & 0xF0) | (_inp_lastKeysHitLeftRight & 0xF); _pge_inpKeysMask = mask; diff --git a/resource.cpp b/resource.cpp index 648afd3..076a09d 100644 --- a/resource.cpp +++ b/resource.cpp @@ -949,6 +949,7 @@ void Resource::load_PGE(File *f) { _pgeNum = f->readUint16LE(); memset(_pgeInit, 0, sizeof(_pgeInit)); debug(DBG_RES, "_pgeNum=%d", _pgeNum); + assert(_pgeNum <= ARRAYSIZE(_pgeInit)); for (uint16_t i = 0; i < _pgeNum; ++i) { InitPGE *pge = &_pgeInit[i]; pge->type = f->readUint16LE(); @@ -979,6 +980,7 @@ void Resource::decodePGE(const uint8_t *p, int size) { _pgeNum = _readUint16(p); p += 2; memset(_pgeInit, 0, sizeof(_pgeInit)); debug(DBG_RES, "len=%d _pgeNum=%d", size, _pgeNum); + assert(_pgeNum <= ARRAYSIZE(_pgeInit)); for (uint16_t i = 0; i < _pgeNum; ++i) { InitPGE *pge = &_pgeInit[i]; pge->type = _readUint16(p); p += 2; diff --git a/rs.cfg b/rs.cfg index 0d04e7f..c790ffe 100644 --- a/rs.cfg +++ b/rs.cfg @@ -13,5 +13,8 @@ fade_out_palette=true # use .BNQ & .LEV datafiles (tile based rendering) for backgrounds (instead of .MAP) use_tiledata=false -# display text instead of playing the polygon cutscenes (french only) +# display text instead of playing the polygon cutscenes use_text_cutscenes=false + +# enable playback of .SEQ cutscenes (use polygonal if false) +use_seq_cutscenes=true diff --git a/scaler.h b/scaler.h index 9ab183c..6ed973d 100644 --- a/scaler.h +++ b/scaler.h @@ -19,7 +19,7 @@ enum { SCALER_SCALE_3X, SCALER_POINT_4X, SCALER_SCALE_4X, - NUM_SCALERS = 7 + NUM_SCALERS }; struct Scaler { diff --git a/staticres.cpp b/staticres.cpp index 7684688..916629d 100644 --- a/staticres.cpp +++ b/staticres.cpp @@ -836,6 +836,29 @@ const Cutscene::Text Cutscene::_frTextsTable[] = { { -1, 0 } }; +const Cutscene::Text Cutscene::_enTextsTable[] = { + { 1, "YOU PICK UP THE HOLOCUBE" }, + { 2, "YOU PICK UP THE KEY" }, + { 3, "YOU PICK UP THE GUN" }, + { 5, "YOUR SHIELD IS RECHARGED" }, + { 10, "YOU PICK UP THE CREDIT CARD" }, + { 14, "THE CARTRIDGE IS RECHARGED" }, + { 15, "YOU PICK UP THE CARTRIDGE" }, + { 16, "YOU PICK UP THE TELEPORTER" }, + { 18, "YOU PICK UP THE I.D. CARD" }, + { 21, "YOU GIVE HIM THE TELEPORTER" }, + { 32, "THE RECEPTIONIST GIVES YOU||A PACKAGE" }, + { 33, "YOU GIVE THE PARCEL" }, + { 34, "THE GOVERNOR GIVES YOU||A WORKING PERMIT" }, + { 35, "THE FORGER GIVES YOU||A FAKE I.D. CARD" }, + { 36, "YOU PICK UP THE FUSE" }, + { 43, "YOU GIVE HIM||YOUR PAPERS" }, + { 44, "YOU GIVE HIM MONEY" }, + { 49, "HE GIVES YOU||AN ANTI-G BELT" }, + { 60, "YOU PICK UP THE TELE RECEPTER" }, + { -1, 0} +}; + const Demo Game::_demoInputs[] = { { "demo1.bin", 0, 0x33, 0x60, 0x46 }, { "demo51.bin", 5, 0x00, 0x60, 0xD6 }, @@ -2700,8 +2723,12 @@ const uint8_t Game::_protectionCodeData[] = { }; const uint8_t Game::_protectionPal[] = { - 0x00, 0x00, 0x00, 0x42, 0x00, 0x63, 0x00, 0x00, 0x0F, 0xFF, 0x0F, 0xF0, 0x07, 0x77, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 + // DOS + 0x00, 0x00, 0x00, 0x42, 0x00, 0x63, 0x00, 0x00, 0x0F, 0xFF, 0x0F, 0xF0, 0x07, 0x77, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + // Amiga + 0x00, 0x00, 0x03, 0x04, 0x06, 0x05, 0x00, 0x00, 0x04, 0x44, 0x05, 0x55, 0x06, 0x66, 0x07, 0x77, + 0x08, 0x88, 0x09, 0x99, 0x0A, 0xAA, 0x0B, 0xBB, 0x0C, 0xCC, 0x0D, 0xDD, 0x0E, 0xEE, 0x0F, 0xFF }; const char *Menu::_passwords[8][3] = { @@ -2715,6 +2742,12 @@ const char *Menu::_passwords[8][3] = { { "BELUGA", "BELUGA", "BELUGA" } }; +const char *Menu::_passwordsFrAmiga[] = { + "BACK", "LOUP", "CINE", "GOOD", "SPIZ", "BIOS", "HALL", // easy + "PLAY", "TOIT", "ZAPP", "LYNX", "SCSI", "GARY", "PONT", // normal + "CLOP", "CARA", "CALE", "FONT", "HASH", "FIBO", "TIPS", // hard +}; + const uint8_t Video::_conradPal1[] = { 0x00, 0x00, 0xCC, 0x0C, 0x8F, 0x08, 0x7E, 0x07, 0x6C, 0x06, 0x5B, 0x05, 0x4A, 0x04, 0x63, 0x09, 0x52, 0x07, 0x41, 0x06, 0x30, 0x06, 0x76, 0x0C, 0x14, 0x09, 0x25, 0x0B, 0x88, 0x08, 0xFF, 0x0F diff --git a/systemstub.h b/systemstub.h index a76fbf2..dd2cf90 100644 --- a/systemstub.h +++ b/systemstub.h @@ -35,8 +35,6 @@ struct PlayerInput { bool load; int stateSlot; - bool mirrorMode; - uint8_t dbgMask; bool quit; }; diff --git a/systemstub_sdl.cpp b/systemstub_sdl.cpp index ee5ae6c..c8db672 100644 --- a/systemstub_sdl.cpp +++ b/systemstub_sdl.cpp @@ -19,6 +19,7 @@ struct SystemStub_SDL : SystemStub { SDL_Window *_window; SDL_Renderer *_renderer; SDL_Texture *_texture; + int _texW, _texH; SDL_GameController *_controller; #else SDL_Surface *_surface; @@ -125,10 +126,7 @@ void SystemStub_SDL::setScreenSize(int w, int h) { if (_screenW == w && _screenH == h) { return; } - free(_screenBuffer); - _screenBuffer = 0; - free(_fadeScreenBuffer); - _fadeScreenBuffer = 0; + cleanupGraphics(); // allocate some extra bytes for the scaling routines const int screenBufferSize = (w + 2) * (h + 2) * sizeof(uint16_t); _screenBuffer = (uint16_t *)calloc(1, screenBufferSize); @@ -190,7 +188,7 @@ void SystemStub_SDL::copyRect(int x, int y, int w, int h, const uint8_t *buf, in } SDL_Rect *br = &_blitRects[_numBlitRects]; - br->x = _pi.mirrorMode ? _screenW - (x + w) : x; + br->x = x; br->y = y; br->w = w; br->h = h; @@ -199,22 +197,12 @@ void SystemStub_SDL::copyRect(int x, int y, int w, int h, const uint8_t *buf, in uint16_t *p = _screenBuffer + (br->y + 1) * _screenW + (br->x + 1); buf += y * pitch + x; - if (_pi.mirrorMode) { - while (h--) { - for (int i = 0; i < w; ++i) { - p[i] = _pal[buf[w - 1 - i]]; - } - p += _screenW; - buf += pitch; - } - } else { - while (h--) { - for (int i = 0; i < w; ++i) { - p[i] = _pal[buf[i]]; - } - p += _screenW; - buf += pitch; + while (h--) { + for (int i = 0; i < w; ++i) { + p[i] = _pal[buf[i]]; } + p += _screenW; + buf += pitch; } if (_pi.dbgMask & PlayerInput::DF_DBLOCKS) { drawRect(br, 0xE7, _screenBuffer + _screenW + 1, _screenW * 2); @@ -244,7 +232,16 @@ static uint16_t blendPixel16(uint16_t colorSrc, uint16_t colorDst, uint32_t mask void SystemStub_SDL::updateScreen(int shakeOffset) { #if SDL_VERSION_ATLEAST(2, 0, 0) - SDL_UpdateTexture(_texture, 0, _screenBuffer + _screenW + 1, _screenW * sizeof(uint16_t)); + if (_texW != _screenW || _texH != _screenH) { + void *dst = 0; + int pitch = 0; + if (SDL_LockTexture(_texture, 0, &dst, &pitch) == 0) { + (*_scalers[_scaler].proc)((uint16_t *)dst, pitch, _screenBuffer + _screenW + 1, _screenW, _screenW, _screenH); + SDL_UnlockTexture(_texture); + } + } else { + SDL_UpdateTexture(_texture, 0, _screenBuffer + _screenW + 1, _screenW * sizeof(uint16_t)); + } SDL_RenderClear(_renderer); if (_fadeOnUpdateScreen) { SDL_SetRenderDrawBlendMode(_renderer, SDL_BLENDMODE_BLEND); @@ -582,9 +579,6 @@ void SystemStub_SDL::processEvent(const SDL_Event &ev, bool &paused) { _pi.dbgMask ^= PlayerInput::DF_DBLOCKS; } else if (ev.key.keysym.sym == SDLK_i) { _pi.dbgMask ^= PlayerInput::DF_SETLIFE; - } else if (ev.key.keysym.sym == SDLK_m) { - _pi.mirrorMode = !_pi.mirrorMode; - flipGraphics(); } else if (ev.key.keysym.sym == SDLK_s) { _pi.save = true; } else if (ev.key.keysym.sym == SDLK_l) { @@ -690,9 +684,13 @@ void SystemStub_SDL::prepareGraphics() { case SCALER_SCALE_3X: case SCALER_SCALE_4X: SDL_SetHint(SDL_HINT_RENDER_SCALE_QUALITY, "1"); + _texW = _screenW * _scalers[_scaler].factor; + _texH = _screenH * _scalers[_scaler].factor; break; default: SDL_SetHint(SDL_HINT_RENDER_SCALE_QUALITY, "0"); // nearest pixel sampling + _texW = _screenW; + _texH = _screenH; break; } const int windowW = _screenW * _scalers[_scaler].factor; @@ -705,7 +703,7 @@ void SystemStub_SDL::prepareGraphics() { _renderer = SDL_CreateRenderer(_window, -1, SDL_RENDERER_ACCELERATED); SDL_RenderSetLogicalSize(_renderer, windowW, windowH); static const uint32_t kPixelFormat = SDL_PIXELFORMAT_RGB565; - _texture = SDL_CreateTexture(_renderer, kPixelFormat, SDL_TEXTUREACCESS_STREAMING, _screenW, _screenH); + _texture = SDL_CreateTexture(_renderer, kPixelFormat, SDL_TEXTUREACCESS_STREAMING, _texW, _texH); _fmt = SDL_AllocFormat(kPixelFormat); #else SDL_WM_SetCaption(_caption, NULL); diff --git a/util.cpp b/util.cpp index e1c977c..68c3219 100644 --- a/util.cpp +++ b/util.cpp @@ -8,7 +8,11 @@ #define WIN32_LEAN_AND_MEAN #include #endif -#include +#ifdef __ANDROID__ +#define LOG_TAG "FbJni" +#include +#endif +#include #include "util.h" @@ -21,8 +25,11 @@ void debug(uint16_t cm, const char *msg, ...) { va_start(va, msg); vsprintf(buf, msg, va); va_end(va); - printf("%s\n", buf); + fprintf(stdout, "%s\n", buf); fflush(stdout); +#ifdef __ANDROID__ + __android_log_print(ANDROID_LOG_INFO, LOG_TAG, "%s", buf); +#endif } } @@ -35,6 +42,9 @@ void error(const char *msg, ...) { fprintf(stderr, "ERROR: %s!\n", buf); #ifdef _WIN32 MessageBox(0, buf, g_caption, MB_ICONERROR); +#endif +#ifdef __ANDROID__ + __android_log_print(ANDROID_LOG_ERROR, LOG_TAG, "%s", buf); #endif exit(-1); } @@ -46,5 +56,8 @@ void warning(const char *msg, ...) { vsnprintf(buf, sizeof(buf), msg, va); va_end(va); fprintf(stderr, "WARNING: %s!\n", buf); +#ifdef __ANDROID__ + __android_log_print(ANDROID_LOG_WARN, LOG_TAG, "%s", buf); +#endif } diff --git a/video.cpp b/video.cpp index c2a01a1..b5b0da6 100644 --- a/video.cpp +++ b/video.cpp @@ -558,8 +558,8 @@ static void decodeLevHelper(uint8_t *dst, const uint8_t *src, int offset10, int const bool yflip = (d3 & (1 << 12)) != 0; const bool xflip = (d3 & (1 << 11)) != 0; int mask = 0; - if ((d3 < (1 << 15)) == 0) { - mask = 0x80; + if ((d3 & 0x8000) != 0) { + mask = 0x80 + ((d3 >> 6) & 0x10); } if (isPC) { PC_drawTile(dst + y * 256 + x, a2, mask, xflip, yflip, -1); @@ -586,8 +586,8 @@ static void decodeLevHelper(uint8_t *dst, const uint8_t *src, int offset10, int int mask = 0; if ((d3 & 0x6000) != 0 && sgdBuf) { mask = 0x10; - } else if ((d3 < (1 << 15)) == 0) { - mask = 0x80; + } else if ((d3 & 0x8000) != 0) { + mask = 0x80 + ((d3 >> 6) & 0x10); } if (isPC) { PC_drawTile(dst + y * 256 + x, a2, mask, xflip, yflip, 0); @@ -663,13 +663,14 @@ void Video::AMIGA_decodeLev(int level, int room) { // background setPaletteSlotBE(0x0, _mapPalSlot1); // objects - setPaletteSlotBE(0x1, (level == 0 || level == 1) ? _mapPalSlot3 : _mapPalSlot2); + setPaletteSlotBE(0x1, (level == 0) ? _mapPalSlot3 : _mapPalSlot2); setPaletteSlotBE(0x2, _mapPalSlot3); setPaletteSlotBE(0x3, _mapPalSlot3); // conrad setPaletteSlotBE(0x4, _mapPalSlot3); // foreground setPaletteSlotBE(0x8, _mapPalSlot1); + setPaletteSlotBE(0x9, (level == 0) ? _mapPalSlot1 : _mapPalSlot3); // inventory setPaletteSlotBE(0xA, _mapPalSlot3); } @@ -803,11 +804,8 @@ void Video::PC_drawChar(uint8_t c, int16_t y, int16_t x) { const uint8_t *src = _res->_fnt + (c - 32) * 32; uint8_t *dst = _frontLayer + x + 256 * y; for (int h = 0; h < 8; ++h) { - for (int i = 0; i < 4; ++i) { - uint8_t c1 = (*src & 0xF0) >> 4; - uint8_t c2 = (*src & 0x0F) >> 0; - ++src; - + for (int i = 0; i < 4; ++i, ++src) { + const uint8_t c1 = *src >> 4; if (c1 != 0) { if (c1 != 2) { *dst = _charFrontColor; @@ -818,7 +816,7 @@ void Video::PC_drawChar(uint8_t c, int16_t y, int16_t x) { *dst = _charTransparentColor; } ++dst; - + const uint8_t c2 = *src & 15; if (c2 != 0) { if (c2 != 2) { *dst = _charFrontColor;