From 2dc61ca6270e0492216df38f4a5a7b0d2de6b7f2 Mon Sep 17 00:00:00 2001 From: Gregory Montoir Date: Sun, 29 Dec 2019 00:00:00 +0800 Subject: [PATCH] Import 0.4.6 --- CHANGES.txt | 10 +- Makefile | 8 +- README.txt | 7 +- cutscene.cpp | 6 +- cutscene.h | 2 +- dynlib.cpp | 59 ----------- dynlib.h | 16 --- file.cpp | 61 +++++++++++ file.h | 1 + game.cpp | 76 +++++++++++++- game.h | 8 +- main.cpp | 41 +------- menu.cpp | 3 - protection.cpp | 20 ++-- resource_aba.cpp | 14 +-- scaler.cpp | 15 --- seq_player.cpp | 1 + staticres.cpp | 2 +- systemstub.h | 5 +- systemstub_sdl.cpp | 254 +++++++++++++++++++++++++++++++++++---------- unpack.cpp | 64 ++++++------ 21 files changed, 411 insertions(+), 262 deletions(-) delete mode 100644 dynlib.cpp delete mode 100644 dynlib.h diff --git a/CHANGES.txt b/CHANGES.txt index 9e89886..71f63ea 100644 --- a/CHANGES.txt +++ b/CHANGES.txt @@ -1,3 +1,7 @@ +* release 0.4.6 + - added rewind to automatic saves + - fixed passwords and protection codes input + * release 0.4.5 - added low-pass filtering for in-game music - added support for 3DO background music (tunes/*.Cpc) @@ -72,7 +76,7 @@ * release 0.2.2 - added support for level background music - - added italian texts + - added Italian texts - fixed PC-CD SEQ cutscenes numbering - fixed several issues with Amiga data files @@ -111,7 +115,7 @@ - added input keys recording - reduced memory usage -* release 0.2.0 (2005/04/02) +* release 0.1.4 (2005/04/02) - added screen shaking (level 2) - added support for Amiga music (experimental) - fixed screen refresh after teleportation @@ -127,7 +131,7 @@ - added sound effects playback - added support for polygonal cutscenes - added support for final credits sequence - - fixed instructions screen display in english version + - fixed instructions screen display in English version * release 0.1.1 (2005/01/29) - added missing opcodes, game should now be completable diff --git a/Makefile b/Makefile index 171f44e..61ed283 100644 --- a/Makefile +++ b/Makefile @@ -2,24 +2,22 @@ SDL_CFLAGS := `sdl2-config --cflags` SDL_LIBS := `sdl2-config --libs` -DL_LIBS := -ldl MODPLUG_LIBS := -lmodplug TREMOR_LIBS := -lvorbisidec -logg ZLIB_LIBS := -lz -CXXFLAGS += -Wall -Wpedantic -MMD $(SDL_CFLAGS) -DUSE_MODPLUG -DUSE_STATIC_SCALER -DUSE_TREMOR -DUSE_ZLIB +CXXFLAGS += -Wall -Wpedantic -MMD $(SDL_CFLAGS) -DUSE_MODPLUG -DUSE_TREMOR -DUSE_ZLIB -SRCS = collision.cpp cpc_player.cpp cutscene.cpp decode_mac.cpp dynlib.cpp file.cpp fs.cpp game.cpp graphics.cpp main.cpp \ +SRCS = collision.cpp cpc_player.cpp cutscene.cpp decode_mac.cpp file.cpp fs.cpp game.cpp graphics.cpp main.cpp \ menu.cpp mixer.cpp mod_player.cpp ogg_player.cpp piege.cpp protection.cpp resource.cpp resource_aba.cpp \ resource_mac.cpp scaler.cpp screenshot.cpp seq_player.cpp \ sfx_player.cpp staticres.cpp systemstub_sdl.cpp unpack.cpp util.cpp video.cpp -SCALERS := scalers/scaler_nearest.cpp scalers/scaler_tv2x.cpp scalers/scaler_xbr.cpp OBJS = $(SRCS:.cpp=.o) $(SCALERS:.cpp=.o) DEPS = $(SRCS:.cpp=.d) $(SCALERS:.cpp=.d) -LIBS = $(SDL_LIBS) $(DL_LIBS) $(MODPLUG_LIBS) $(TREMOR_LIBS) $(ZLIB_LIBS) +LIBS = $(SDL_LIBS) $(MODPLUG_LIBS) $(TREMOR_LIBS) $(ZLIB_LIBS) rs: $(OBJS) $(CXX) $(LDFLAGS) -o $@ $(OBJS) $(LIBS) diff --git a/README.txt b/README.txt index 661981a..90fd00a 100644 --- a/README.txt +++ b/README.txt @@ -1,6 +1,6 @@ REminiscence README -Release version: 0.4.5 +Release version: 0.4.6 ------------------------------------------------------------------------------- @@ -50,8 +50,8 @@ These paths can be changed using command line switches : The scaler option specifies the algorithm used to smoothen the image in addition to a scaling factor. External scalers are also supported, the suffix -shall be used as the name. Eg. If you have scaler_xbrz.dll, you can pass -'--scaler xbrz@2' to use that algorithm with a doubled window size (512x448). +shall be used as the name. Eg. If you have scaler_xbr.dll, you can pass +'--scaler xbr@2' to use that algorithm with a doubled window size (512x448). The widescreen option accepts two modes : 'adjacent' : left and right rooms bitmaps will be drawn @@ -69,6 +69,7 @@ In-game hotkeys : Alt S write screenshot as .tga Ctrl S save game state Ctrl L load game state + Ctrl R rewind game state buffer (requires --autosave) Ctrl + and - change game state slot Function Keys change game screen scaler diff --git a/cutscene.cpp b/cutscene.cpp index 3b39fec..b4d658e 100644 --- a/cutscene.cpp +++ b/cutscene.cpp @@ -410,8 +410,8 @@ void Cutscene::op_setPalette() { } } -void Cutscene::op_drawStringAtBottom() { - debug(DBG_CUT, "Cutscene::op_drawStringAtBottom()"); +void Cutscene::op_drawCaptionText() { + debug(DBG_CUT, "Cutscene::op_drawCaptionText()"); uint16_t strId = fetchNextCmdWord(); if (!_creditsSequence) { @@ -1036,7 +1036,7 @@ bool Cutscene::load(uint16_t cutName) { // uint8_t *p = _res->_cmd + 0x322; if (memcmp(p, "\x00\x18\x00\x3a", 4) == 0) { - p[0] = 0x06 << 2; // op_drawStringAtBottom + p[0] = 0x06 << 2; // op_drawCaptionText p[1] = 0x00; p[2] = 0x3a; p[3] = 0x00; // op_markCurPos diff --git a/cutscene.h b/cutscene.h index 13c354d..aef0fc0 100644 --- a/cutscene.h +++ b/cutscene.h @@ -128,7 +128,7 @@ struct Cutscene { void op_waitForSync(); void op_drawShape(); void op_setPalette(); - void op_drawStringAtBottom(); + void op_drawCaptionText(); void op_nop(); void op_skip3(); void op_refreshAll(); diff --git a/dynlib.cpp b/dynlib.cpp deleted file mode 100644 index 766ef98..0000000 --- a/dynlib.cpp +++ /dev/null @@ -1,59 +0,0 @@ - -#ifdef _WIN32 -#define WIN32_LEAN_AND_MEAN -#include -#else -#include -#endif -#include -#include -#include "dynlib.h" - -#ifdef WIN32 -struct DynLib_impl { - HINSTANCE _dl; - DynLib_impl(const char *name) { - char dllname[MAXPATHLEN]; - snprintf(dllname, sizeof(dllname), "%s.dll", name); - _dl = LoadLibrary(dllname); - } - ~DynLib_impl() { - if (_dl) { - FreeLibrary(_dl); - _dl = 0; - } - } - void *getSymbol(const char *name) { - return (void *)GetProcAddress(_dl, name); - } -}; -#else -struct DynLib_impl { - void *_dl; - DynLib_impl(const char *name) { - char soname[MAXPATHLEN]; - snprintf(soname, sizeof(soname), "%s.so", name); - _dl = dlopen(soname, RTLD_LAZY); - } - ~DynLib_impl() { - if (_dl) { - dlclose(_dl); - } - } - void *getSymbol(const char *name) { - return dlsym(_dl, name); - } -}; -#endif - -DynLib::DynLib(const char *name) { - _impl = new DynLib_impl(name); -} - -DynLib::~DynLib() { - delete _impl; -} - -void *DynLib::getSymbol(const char *name) { - return _impl->getSymbol(name); -} diff --git a/dynlib.h b/dynlib.h deleted file mode 100644 index 6b4fe85..0000000 --- a/dynlib.h +++ /dev/null @@ -1,16 +0,0 @@ - -#ifndef DYNLIB_H__ -#define DYNLIB_H__ - -struct DynLib_impl; - -struct DynLib { - DynLib_impl *_impl; - - DynLib(const char *name); - ~DynLib(); - - void *getSymbol(const char *name); -}; - -#endif // DYNLIB_H__ diff --git a/file.cpp b/file.cpp index d9fd404..4942d83 100644 --- a/file.cpp +++ b/file.cpp @@ -197,6 +197,58 @@ struct AssetFile: File_impl { }; #endif +struct MemoryBufferFile: File_impl { + uint8_t *_ptr; + uint32_t _capacity, _offset, _len; + MemoryBufferFile(int initialCapacity) { + _capacity = initialCapacity; + _ptr = (uint8_t *)malloc(_capacity); + _offset = _len = 0; + } + ~MemoryBufferFile() { + free(_ptr); + } + bool open(const char *path, const char *mode) { + return false; + } + void close() { + } + uint32_t size() { + return _len; + } + uint32_t tell() { + return _offset; + } + void seek(int offs) { + _offset = offs; + } + uint32_t read(void *ptr, uint32_t len) { + int count = len; + if (_offset + count > _len) { + count = _len - _offset; + _ioErr = true; + } + if (count != 0) { + memcpy(ptr, _ptr + _offset, count); + _offset += count; + } + return count; + } + uint32_t write(const void *ptr, uint32_t len) { + int count = len; + while (_offset + count > _capacity) { + _capacity *= 2; + _ptr = (uint8_t *)realloc(_ptr, _capacity); + } + if (count != 0) { + memcpy(_ptr + _offset, ptr, count); + _offset += count; + } + _len = _offset; + return count; + } +}; + File::File() : _impl(0) { } @@ -264,6 +316,15 @@ bool File::open(const char *filename, const char *mode, const char *directory) { return _impl->open(path, mode); } +void File::openMemoryBuffer(int initialCapacity) { + if (_impl) { + _impl->close(); + delete _impl; + _impl = 0; + } + _impl = new MemoryBufferFile(initialCapacity); +} + void File::close() { if (_impl) { _impl->close(); diff --git a/file.h b/file.h index 9f741d7..1876cd4 100644 --- a/file.h +++ b/file.h @@ -20,6 +20,7 @@ struct File { bool open(const char *filename, const char *mode, FileSystem *fs); bool open(const char *filename, const char *mode, const char *directory); + void openMemoryBuffer(int initialCapacity); void close(); bool ioErr() const; uint32_t size(); diff --git a/game.cpp b/game.cpp index 759288d..959e5c3 100644 --- a/game.cpp +++ b/game.cpp @@ -25,6 +25,8 @@ Game::Game(SystemStub *stub, FileSystem *fs, const char *savePath, int level, Re _demoBin = -1; _widescreenMode = widescreenMode; _autoSave = autoSave; + _rewindPtr = -1; + _rewindLen = 0; } void Game::run() { @@ -159,6 +161,7 @@ void Game::run() { _vid._unkPalSlot1 = 0; _vid._unkPalSlot2 = 0; _score = 0; + clearStateRewind(); loadLevelData(); resetGameState(); _endLoop = false; @@ -195,7 +198,7 @@ void Game::displayTitleScreenAmiga() { if (!buf) { error("Failed to allocate screen buffer w=%d h=%d", kW, kH); } - static const int kAmigaColors[] = { + static const uint16_t kAmigaColors[] = { 0x000, 0x123, 0x012, 0x134, 0x433, 0x453, 0x046, 0x245, 0x751, 0x455, 0x665, 0x268, 0x961, 0x478, 0x677, 0x786, 0x17B, 0x788, 0xB84, 0xC92, 0x49C, 0xF00, 0x9A8, 0x9AA, @@ -384,11 +387,12 @@ void Game::mainLoop() { playCutscene(0x41); _endLoop = true; } else { - if (_autoSave && loadGameState(kAutoSaveSlot)) { + if (_autoSave && _rewindLen != 0 && loadGameState(kAutoSaveSlot)) { // autosave } else if (_validSaveState && loadGameState(kIngameSaveSlot)) { // ingame save } else { + clearStateRewind(); loadLevelData(); resetGameState(); } @@ -453,9 +457,9 @@ void Game::mainLoop() { } } inp_handleSpecialKeys(); - if (_stub->getTimeStamp() - _saveTimestamp >= kAutoSaveIntervalMs) { - // do not save if we just died - if (_pgeLive[0].life > 0) { + if (_autoSave && _stub->getTimeStamp() - _saveTimestamp >= kAutoSaveIntervalMs) { + // do not save if we died or about to + if (_pgeLive[0].life > 0 && _deathCutsceneCounter == 0) { saveGameState(kAutoSaveSlot); _saveTimestamp = _stub->getTimeStamp(); } @@ -594,6 +598,14 @@ void Game::inp_handleSpecialKeys() { } _stub->_pi.stateSlot = 0; } + if (_stub->_pi.rewind) { + if (_rewindLen != 0) { + loadStateRewind(); + } else { + debug(DBG_INFO, "Rewind buffer is empty"); + } + _stub->_pi.rewind = false; + } } void Game::drawCurrentInventoryItem() { @@ -1817,6 +1829,7 @@ uint16_t Game::getRandomNumber() { void Game::changeLevel() { _vid.fadeOut(); + clearStateRewind(); loadLevelData(); loadLevelMap(); _vid.setPalette0xF(); @@ -1981,6 +1994,9 @@ void Game::makeGameStateName(uint8_t slot, char *buf) { static const uint32_t TAG_FBSV = 0x46425356; bool Game::saveGameState(uint8_t slot) { + if (slot == kAutoSaveSlot) { + return saveStateRewind(); + } bool success = false; char stateFile[32]; makeGameStateName(slot, stateFile); @@ -2008,6 +2024,9 @@ bool Game::saveGameState(uint8_t slot) { } bool Game::loadGameState(uint8_t slot) { + if (slot == kAutoSaveSlot) { + return loadStateRewind(); + } bool success = false; char stateFile[32]; makeGameStateName(slot, stateFile); @@ -2177,6 +2196,53 @@ void Game::loadState(File *f) { resetGameState(); } +void Game::clearStateRewind() { + // debug(DBG_INFO, "Clear rewind state (count %d)", _rewindLen); + for (int i = 0; i < _rewindLen; ++i) { + int ptr = _rewindPtr - i; + if (ptr < 0) { + ptr += kRewindSize; + } + _rewindBuffer[ptr].close(); + } + _rewindPtr = -1; + _rewindLen = 0; +} + +bool Game::saveStateRewind() { + if (_rewindPtr == kRewindSize - 1) { + _rewindPtr = 0; + } else { + ++_rewindPtr; + } + static const int kGameStateSize = 16384; + File &f = _rewindBuffer[_rewindPtr]; + f.openMemoryBuffer(kGameStateSize); + saveState(&f); + if (_rewindLen < kRewindSize) { + ++_rewindLen; + } + // debug(DBG_INFO, "Save state for rewind (index %d, count %d, size %d)", _rewindPtr, _rewindLen, f.size()); + return !f.ioErr(); +} + +bool Game::loadStateRewind() { + const int ptr = _rewindPtr; + if (_rewindPtr == 0) { + _rewindPtr = kRewindSize - 1; + } else { + --_rewindPtr; + } + File &f = _rewindBuffer[ptr]; + f.seek(0); + loadState(&f); + if (_rewindLen > 0) { + --_rewindLen; + } + // debug(DBG_INFO, "Rewind state (index %d, count %d, size %d)", ptr, _rewindLen, f.size()); + return !f.ioErr(); +} + void AnimBuffers::addState(uint8_t stateNum, int16_t x, int16_t y, const uint8_t *dataPtr, LivePGE *pge, uint8_t w, uint8_t h) { debug(DBG_GAME, "AnimBuffers::addState() stateNum=%d x=%d y=%d dataPtr=%p pge=%p", stateNum, x, y, dataPtr, pge); assert(stateNum < 4); diff --git a/game.h b/game.h index 26e5b62..2f88ab5 100644 --- a/game.h +++ b/game.h @@ -27,8 +27,9 @@ struct Game { enum { kIngameSaveSlot = 0, + kRewindSize = 120, // 10mins (~2MB) kAutoSaveSlot = 255, - kAutoSaveIntervalMs = 120 * 1000 + kAutoSaveIntervalMs = 5 * 1000 }; enum { @@ -68,6 +69,8 @@ struct Game { SystemStub *_stub; FileSystem *_fs; const char *_savePath; + File _rewindBuffer[kRewindSize]; + int _rewindPtr, _rewindLen; const uint8_t *_stringsTable; const char **_textsTable; @@ -384,6 +387,9 @@ struct Game { bool loadGameState(uint8_t slot); void saveState(File *f); void loadState(File *f); + void clearStateRewind(); + bool saveStateRewind(); + bool loadStateRewind(); }; #endif // GAME_H__ diff --git a/main.cpp b/main.cpp index 02e8b66..6d5de4d 100644 --- a/main.cpp +++ b/main.cpp @@ -156,50 +156,13 @@ static void initOptions() { } static void parseScaler(char *name, ScalerParameters *scalerParameters) { - static const struct { - const char *name; - int type; - const Scaler *scaler; - } scalers[] = { - { "point", kScalerTypePoint, 0 }, - { "linear", kScalerTypeLinear, 0 }, - { "scale", kScalerTypeInternal, &_internalScaler }, -#ifdef USE_STATIC_SCALER - { "nearest", kScalerTypeInternal, &scaler_nearest }, - { "tv2x", kScalerTypeInternal, &scaler_tv2x }, - { "xbr", kScalerTypeInternal, &scaler_xbr }, -#endif - { 0, -1 } - }; - bool found = false; char *sep = strchr(name, '@'); if (sep) { *sep = 0; - } - for (int i = 0; scalers[i].name; ++i) { - if (strcmp(scalers[i].name, name) == 0) { - scalerParameters->type = (ScalerType)scalers[i].type; - scalerParameters->scaler = scalers[i].scaler; - found = true; - break; - } - } - if (!found) { - char libname[32]; - snprintf(libname, sizeof(libname), "scaler_%s", name); - const Scaler *scaler = findScaler(libname); - if (!scaler) { - warning("Scaler '%s' not found, using default", libname); - } else if (scaler->tag != SCALER_TAG) { - warning("Unexpected tag %d for scaler '%s'", scaler->tag, libname); - } else { - scalerParameters->type = kScalerTypeExternal; - scalerParameters->scaler = scaler; - } - } - if (sep) { scalerParameters->factor = atoi(sep + 1); } + strncpy(scalerParameters->name, name, sizeof(scalerParameters->name) - 1); + scalerParameters->name[sizeof(scalerParameters->name) - 1] = 0; } static WidescreenMode parseWidescreen(const char *mode) { diff --git a/menu.cpp b/menu.cpp index a7c6e31..50e4142 100644 --- a/menu.cpp +++ b/menu.cpp @@ -212,9 +212,6 @@ bool Menu::handlePasswordScreen() { if (c != 0) { _stub->_pi.lastChar = 0; if (len < 6) { - if (c >= 'a' && c <= 'z') { - c &= ~0x20; - } if ((c >= 'A' && c <= 'Z') || (c == 0x20)) { password[len] = c; ++len; diff --git a/protection.cpp b/protection.cpp index ff440cc..d020ca1 100644 --- a/protection.cpp +++ b/protection.cpp @@ -9,13 +9,11 @@ #include "systemstub.h" static uint8_t reverseBits(uint8_t ch) { - uint8_t r = 0; - for (int b = 0; b < 8; ++b) { - if (ch & (1 << b)) { - r |= (1 << (7 - b)); - } - } - return r; + static const uint8_t lut[] = { + 0x0, 0x8, 0x4, 0xC, 0x2, 0xA, 0x6, 0xE, + 0x1, 0x9, 0x5, 0xD, 0x3, 0xB, 0x7, 0xF + }; + return (lut[ch & 15] << 4) | lut[ch >> 4]; } static uint8_t decryptChar(uint8_t ch) { @@ -114,9 +112,6 @@ bool Game::handleProtectionScreenShape() { if (c != 0) { _stub->_pi.lastChar = 0; if (len < kCodeLen) { - if (c >= 'a' && c <= 'z') { - c &= ~0x20; - } if ((c >= 'A' && c <= 'Z') || (c >= '0' && c <= '9')) { codeText[len] = c; ++len; @@ -201,7 +196,7 @@ bool Game::handleProtectionScreenWords() { const uint8_t code = getRandomNumber() % kWordsCount; const uint8_t *protectionData = _protectionWordData + code * 18; - const char *kSecurityCodeText = "SECURITY CODE"; + static const char *kSecurityCodeText = "SECURITY CODE"; _vid.drawString(kSecurityCodeText, 72 + (114 - strlen(kSecurityCodeText) * 8) / 2, 158, 0xE4); char buf[16]; snprintf(buf, sizeof(buf), "PAGE %d", protectionData[0]); @@ -229,9 +224,6 @@ bool Game::handleProtectionScreenWords() { if (c != 0) { _stub->_pi.lastChar = 0; if (len < kCodeLen) { - if (c >= 'a' && c <= 'z') { - c &= ~0x20; - } if (c >= 'A' && c <= 'Z') { codeText[len] = c; ++len; diff --git a/resource_aba.cpp b/resource_aba.cpp index 5039589..68f2705 100644 --- a/resource_aba.cpp +++ b/resource_aba.cpp @@ -15,6 +15,10 @@ ResourceAba::~ResourceAba() { free(_entries); } +static int compareAbaEntry(const void *a, const void *b) { + return strcasecmp(((ResourceAbaEntry *)a)->name, ((ResourceAbaEntry *)b)->name); +} + void ResourceAba::readEntries() { if (_f.open(FILENAME, "rb", _fs)) { _entriesCount = _f.readUint16BE(); @@ -39,16 +43,14 @@ void ResourceAba::readEntries() { } nextOffset = _entries[i].offset + _entries[i].compressedSize; } + qsort(_entries, _entriesCount, sizeof(ResourceAbaEntry), compareAbaEntry); } } const ResourceAbaEntry *ResourceAba::findEntry(const char *name) const { - for (int i = 0; i < _entriesCount; ++i) { - if (strcasecmp(_entries[i].name, name) == 0) { - return &_entries[i]; - } - } - return 0; + ResourceAbaEntry tmp; + strcpy(tmp.name, name); + return (const ResourceAbaEntry *)bsearch(&tmp, _entries, _entriesCount, sizeof(ResourceAbaEntry), compareAbaEntry); } uint8_t *ResourceAba::loadEntry(const char *name, uint32_t *size) { diff --git a/scaler.cpp b/scaler.cpp index 8ecb14b..850943e 100644 --- a/scaler.cpp +++ b/scaler.cpp @@ -5,7 +5,6 @@ */ #include "scaler.h" -#include "dynlib.h" #include "util.h" static void scanline2x(uint32_t *dst0, uint32_t *dst1, const uint32_t *src0, const uint32_t *src1, const uint32_t *src2, int w) { @@ -282,17 +281,3 @@ const Scaler _internalScaler = { 2, 4, scaleNx, }; - -static DynLib *dynLib; - -static const char *kSoSym = "getScaler"; - -const Scaler *findScaler(const char *name) { - dynLib = new DynLib(name); - void *symbol = dynLib->getSymbol(kSoSym); - if (symbol) { - typedef const Scaler *(*GetScalerProc)(); - return ((GetScalerProc)symbol)(); - } - return 0; -} diff --git a/seq_player.cpp b/seq_player.cpp index 4d013a3..8fbe468 100644 --- a/seq_player.cpp +++ b/seq_player.cpp @@ -24,6 +24,7 @@ void SeqDemuxer::close() { for (int i = 0; i < kBuffersCount; ++i) { free(_buffers[i].data); } + memset(_buffers, 0, sizeof(_buffers)); } bool SeqDemuxer::readHeader() { diff --git a/staticres.cpp b/staticres.cpp index 3b1bd49..153e396 100644 --- a/staticres.cpp +++ b/staticres.cpp @@ -17,7 +17,7 @@ const Cutscene::OpcodeStub Cutscene::_opcodeTable[] = { /* 0x04 */ &Cutscene::op_setPalette, &Cutscene::op_markCurPos, - &Cutscene::op_drawStringAtBottom, + &Cutscene::op_drawCaptionText, &Cutscene::op_nop, /* 0x08 */ &Cutscene::op_skip3, diff --git a/systemstub.h b/systemstub.h index 20dd8de..6dd0802 100644 --- a/systemstub.h +++ b/systemstub.h @@ -35,6 +35,7 @@ struct PlayerInput { bool save; bool load; int stateSlot; + bool rewind; uint8_t dbgMask; bool quit; @@ -42,7 +43,7 @@ struct PlayerInput { struct ScalerParameters { ScalerType type; - const Scaler *scaler; + char name[32]; int factor; static ScalerParameters defaults(); @@ -55,7 +56,7 @@ struct SystemStub { virtual ~SystemStub() {} - virtual void init(const char *title, int w, int h, bool fullscreen, int widescreenMode, ScalerParameters *scalerParameters) = 0; + virtual void init(const char *title, int w, int h, bool fullscreen, int widescreenMode, const ScalerParameters *scalerParameters) = 0; virtual void destroy() = 0; virtual bool hasWidescreen() const = 0; diff --git a/systemstub_sdl.cpp b/systemstub_sdl.cpp index 8301a0d..8f41636 100644 --- a/systemstub_sdl.cpp +++ b/systemstub_sdl.cpp @@ -1,4 +1,3 @@ - /* * REminiscence - Flashback interpreter * Copyright (C) 2005-2019 Gregory Montoir (cyx@users.sourceforge.net) @@ -22,7 +21,7 @@ static const uint32_t kPixelFormat = SDL_PIXELFORMAT_RGB888; ScalerParameters ScalerParameters::defaults() { ScalerParameters params; params.type = kScalerTypeInternal; - params.scaler = &_internalScaler; + params.name[0] = 0; params.factor = _internalScaler.factorMin + (_internalScaler.factorMax - _internalScaler.factorMin) / 2; return params; } @@ -47,15 +46,16 @@ struct SystemStub_SDL : SystemStub { void *_audioCbData; int _screenshot; ScalerType _scalerType; - const Scaler *_scaler; int _scaleFactor; + const Scaler *_scaler; + void *_scalerSo; int _widescreenMode; SDL_Texture *_widescreenTexture; int _wideMargin; bool _enableWidescreen; virtual ~SystemStub_SDL() {} - virtual void init(const char *title, int w, int h, bool fullscreen, int widescreenMode, ScalerParameters *scalerParameters); + virtual void init(const char *title, int w, int h, bool fullscreen, int widescreenMode, const ScalerParameters *scalerParameters); virtual void destroy(); virtual bool hasWidescreen() const; virtual void setScreenSize(int w, int h); @@ -88,7 +88,8 @@ struct SystemStub_SDL : SystemStub { void prepareGraphics(); void cleanupGraphics(); void changeGraphics(bool fullscreen, int scaleFactor); - void changeScaler(int scaler); + void setScaler(const ScalerParameters *parameters); + void changeScaler(int scalerNum); void drawRect(int x, int y, int w, int h, uint8_t color); }; @@ -96,7 +97,7 @@ SystemStub *SystemStub_SDL_create() { return new SystemStub_SDL(); } -void SystemStub_SDL::init(const char *title, int w, int h, bool fullscreen, int widescreenMode, ScalerParameters *scalerParameters) { +void SystemStub_SDL::init(const char *title, int w, int h, bool fullscreen, int widescreenMode, const ScalerParameters *scalerParameters) { SDL_Init(SDL_INIT_VIDEO | SDL_INIT_AUDIO | SDL_INIT_JOYSTICK); SDL_ShowCursor(SDL_DISABLE); _caption = title; @@ -108,9 +109,13 @@ void SystemStub_SDL::init(const char *title, int w, int h, bool fullscreen, int _screenBuffer = 0; _fadeOnUpdateScreen = false; _fullscreen = fullscreen; - _scalerType = scalerParameters->type; - _scaler = scalerParameters->scaler; - _scaleFactor = _scaler ? CLIP(scalerParameters->factor, _scaler->factorMin, _scaler->factorMax) : 1; + _scalerType = kScalerTypeInternal; + _scaleFactor = 1; + _scaler = 0; + _scalerSo = 0; + if (scalerParameters->name[0]) { + setScaler(scalerParameters); + } memset(_rgbPalette, 0, sizeof(_rgbPalette)); memset(_darkPalette, 0, sizeof(_darkPalette)); _screenW = _screenH = 0; @@ -143,6 +148,10 @@ void SystemStub_SDL::destroy() { SDL_FreeFormat(_fmt); _fmt = 0; } + if (_scalerSo) { + SDL_UnloadObject(_scalerSo); + _scalerSo = 0; + } if (_controller) { SDL_GameControllerClose(_controller); _controller = 0; @@ -352,46 +361,107 @@ void SystemStub_SDL::copyWidescreenMirror(int w, int h, const uint8_t *buf) { } } -static uint32_t blurPixel(int x, int y, const uint8_t *src, const uint32_t *pal, int pitch, int w, int h, const SDL_PixelFormat *fmt) { - static const uint8_t blurMat[3 * 3] = { - 2, 4, 2, - 4, 8, 4, - 2, 4, 2 - }; - static const int blurMatSigma = 32 * 2; +static void blur_h(int radius, const uint32_t *src, int srcPitch, int w, int h, const SDL_PixelFormat *fmt, uint32_t *dst, int dstPitch) { - const uint32_t redBlueMask = fmt->Rmask | fmt->Bmask; - const uint32_t greenMask = fmt->Gmask; + const int count = 2 * radius + 1; - uint32_t redBlueBlurSum = 0; - uint32_t greenBlurSum = 0; + for (int y = 0; y < h; ++y) { - for (int v = 0; v < 3; ++v) { - const int ym = CLIP(y + v - 1, 0, h - 1); - for (int u = 0; u < 3; ++u) { - const int xm = CLIP(x + u - 1, 0, w - 1); - const uint32_t color = pal[src[ym * pitch + xm]]; - const int mul = blurMat[v * 3 + u]; - redBlueBlurSum += (color & redBlueMask) * mul; - greenBlurSum += (color & greenMask) * mul; + uint32_t r = 0; + uint32_t g = 0; + uint32_t b = 0; + + uint32_t color; + + for (int x = -radius; x <= radius; ++x) { + color = src[MAX(x, 0)]; + r += (color & fmt->Rmask) >> fmt->Rshift; + g += (color & fmt->Gmask) >> fmt->Gshift; + b += (color & fmt->Bmask) >> fmt->Bshift; } + dst[0] = ((r / count) << fmt->Rshift) | ((g / count) << fmt->Gshift) | ((b / count) << fmt->Bshift); + + for (int x = 1; x < w; ++x) { + color = src[MIN(x + radius, w - 1)]; + r += (color & fmt->Rmask) >> fmt->Rshift; + g += (color & fmt->Gmask) >> fmt->Gshift; + b += (color & fmt->Bmask) >> fmt->Bshift; + + color = src[MAX(x - radius - 1, 0)]; + r -= (color & fmt->Rmask) >> fmt->Rshift; + g -= (color & fmt->Gmask) >> fmt->Gshift; + b -= (color & fmt->Bmask) >> fmt->Bshift; + + dst[x] = ((r / count) << fmt->Rshift) | ((g / count) << fmt->Gshift) | ((b / count) << fmt->Bshift); + } + + src += srcPitch; + dst += dstPitch; + } +} + +static void blur_v(int radius, const uint32_t *src, int srcPitch, int w, int h, const SDL_PixelFormat *fmt, uint32_t *dst, int dstPitch) { + + const int count = 2 * radius + 1; + + for (int x = 0; x < w; ++x) { + + uint32_t r = 0; + uint32_t g = 0; + uint32_t b = 0; + + uint32_t color; + + for (int y = -radius; y <= radius; ++y) { + color = src[MAX(y, 0) * srcPitch]; + r += (color & fmt->Rmask) >> fmt->Rshift; + g += (color & fmt->Gmask) >> fmt->Gshift; + b += (color & fmt->Bmask) >> fmt->Bshift; + } + dst[0] = ((r / count) << fmt->Rshift) | ((g / count) << fmt->Gshift) | ((b / count) << fmt->Bshift); + + for (int y = 1; y < h; ++y) { + color = src[MIN(y + radius, h - 1) * srcPitch]; + r += (color & fmt->Rmask) >> fmt->Rshift; + g += (color & fmt->Gmask) >> fmt->Gshift; + b += (color & fmt->Bmask) >> fmt->Bshift; + + color = src[MAX(y - radius - 1, 0) * srcPitch]; + r -= (color & fmt->Rmask) >> fmt->Rshift; + g -= (color & fmt->Gmask) >> fmt->Gshift; + b -= (color & fmt->Bmask) >> fmt->Bshift; + + dst[y * dstPitch] = ((r / count) << fmt->Rshift) | ((g / count) << fmt->Gshift) | ((b / count) << fmt->Bshift); + } + + ++src; + ++dst; } - return ((redBlueBlurSum / blurMatSigma) & redBlueMask) | ((greenBlurSum / blurMatSigma) & greenMask); } void SystemStub_SDL::copyWidescreenBlur(int w, int h, const uint8_t *buf) { assert(w == _screenW && h == _screenH); - void *dst = 0; + void *ptr = 0; int pitch = 0; - if (SDL_LockTexture(_widescreenTexture, 0, &dst, &pitch) == 0) { + if (SDL_LockTexture(_widescreenTexture, 0, &ptr, &pitch) == 0) { assert((pitch & 3) == 0); - uint32_t *p = (uint32_t *)dst; - for (int y = 0; y < h; ++y) { - for (int x = 0; x < w; ++x) { - p[x] = blurPixel(x, y, buf, _rgbPalette, w, w, h, _fmt); + + uint32_t *src = (uint32_t *)malloc(w * h * sizeof(uint32_t)); + uint32_t *tmp = (uint32_t *)malloc(w * h * sizeof(uint32_t)); + uint32_t *dst = (uint32_t *)ptr; + + if (src && tmp) { + for (int i = 0; i < w * h; ++i) { + src[i] = _rgbPalette[buf[i]]; } - p += pitch / sizeof(uint32_t); + static const int radius = 8; + blur_h(radius, src, w, w, h, _fmt, tmp, w); + blur_v(radius, tmp, w, w, h, _fmt, dst, pitch / sizeof(uint32_t)); } + + free(src); + free(tmp); + SDL_UnlockTexture(_widescreenTexture); } } @@ -476,6 +546,23 @@ void SystemStub_SDL::processEvents() { } } +// only used for the protection codes and level passwords +static void setAsciiChar(PlayerInput &pi, const SDL_Keysym *key) { + if (key->sym >= SDLK_0 && key->sym <= SDLK_9) { + pi.lastChar = '0' + key->sym - SDLK_0; + } else if (key->sym >= SDLK_a && key->sym <= SDLK_z) { + pi.lastChar = 'A' + key->sym - SDLK_a; + } else if (key->scancode == SDL_SCANCODE_0) { + pi.lastChar = '0'; + } else if (key->scancode >= SDL_SCANCODE_1 && key->scancode <= SDL_SCANCODE_9) { + pi.lastChar = '1' + key->scancode - SDL_SCANCODE_1; + } else if (key->sym == SDLK_SPACE || key->sym == SDLK_KP_SPACE) { + pi.lastChar = ' '; + } else { + pi.lastChar = 0; + } +} + void SystemStub_SDL::processEvent(const SDL_Event &ev, bool &paused) { switch (ev.type) { case SDL_QUIT: @@ -681,6 +768,9 @@ void SystemStub_SDL::processEvent(const SDL_Event &ev, bool &paused) { case SDLK_l: _pi.load = true; break; + case SDLK_r: + _pi.rewind = true; + break; case SDLK_KP_PLUS: case SDLK_PAGEUP: _pi.stateSlot = 1; @@ -692,7 +782,7 @@ void SystemStub_SDL::processEvent(const SDL_Event &ev, bool &paused) { } break; } - _pi.lastChar = ev.key.keysym.sym; + setAsciiChar(_pi, &ev.key.keysym); switch (ev.key.keysym.sym) { case SDLK_LEFT: _pi.dirMask &= ~PlayerInput::DIR_LEFT; @@ -917,39 +1007,97 @@ void SystemStub_SDL::changeGraphics(bool fullscreen, int scaleFactor) { prepareGraphics(); } -void SystemStub_SDL::changeScaler(int scaler) { - ScalerParameters scalerParameters = ScalerParameters::defaults(); - switch (scaler) { +void SystemStub_SDL::setScaler(const ScalerParameters *parameters) { + static const struct { + const char *name; + int type; + const Scaler *scaler; + } scalers[] = { + { "point", kScalerTypePoint, 0 }, + { "linear", kScalerTypeLinear, 0 }, + { "scale", kScalerTypeInternal, &_internalScaler }, +#ifdef USE_STATIC_SCALER + { "nearest", kScalerTypeInternal, &scaler_nearest }, + { "tv2x", kScalerTypeInternal, &scaler_tv2x }, + { "xbr", kScalerTypeInternal, &scaler_xbr }, +#endif + { 0, -1 } + }; + bool found = false; + for (int i = 0; scalers[i].name; ++i) { + if (strcmp(scalers[i].name, parameters->name) == 0) { + _scalerType = (ScalerType)scalers[i].type; + _scaler = scalers[i].scaler; + found = true; + break; + } + } + if (!found) { +#ifdef _WIN32 + static const char *libSuffix = "dll"; +#else + static const char *libSuffix = "so"; +#endif + char libname[64]; + snprintf(libname, sizeof(libname), "scaler_%s.%s", parameters->name, libSuffix); + _scalerSo = SDL_LoadObject(libname); + if (!_scalerSo) { + warning("Scaler '%s' not found, using default", libname); + } else { + static const char *kSoSym = "getScaler"; + void *symbol = SDL_LoadFunction(_scalerSo, kSoSym); + if (!symbol) { + warning("Symbol '%s' not found in '%s'", kSoSym, libname); + } else { + typedef const Scaler *(*GetScalerProc)(); + const Scaler *scaler = ((GetScalerProc)symbol)(); + const int tag = scaler ? scaler->tag : 0; + if (tag != SCALER_TAG) { + warning("Unexpected tag %d for scaler '%s'", tag, libname); + } else { + _scalerType = kScalerTypeExternal; + _scaler = scaler; + } + } + } + } + _scaleFactor = _scaler ? CLIP(parameters->factor, _scaler->factorMin, _scaler->factorMax) : 1; +} + +void SystemStub_SDL::changeScaler(int scalerNum) { + ScalerType type = kScalerTypeInternal; + const Scaler *scaler = 0; + switch (scalerNum) { case 0: - scalerParameters.type = kScalerTypePoint; + type = kScalerTypePoint; break; case 1: - scalerParameters.type = kScalerTypeLinear; + type = kScalerTypeLinear; break; case 2: - scalerParameters.type = kScalerTypeInternal; - scalerParameters.scaler = &_internalScaler; + type = kScalerTypeInternal; + scaler = &_internalScaler; break; #ifdef USE_STATIC_SCALER case 3: - scalerParameters.type = kScalerTypeInternal; - scalerParameters.scaler = &scaler_nearest; + type = kScalerTypeInternal; + scaler = &scaler_nearest; break; case 4: - scalerParameters.type = kScalerTypeInternal; - scalerParameters.scaler = &scaler_tv2x; + type = kScalerTypeInternal; + scaler = &scaler_tv2x; break; case 5: - scalerParameters.type = kScalerTypeInternal; - scalerParameters.scaler = &scaler_xbr; + type = kScalerTypeInternal; + scaler = &scaler_xbr; break; #endif default: return; } - if (_scalerType != scalerParameters.type || scalerParameters.scaler != _scaler) { - _scalerType = scalerParameters.type; - _scaler = scalerParameters.scaler; + if (_scalerType != type || scaler != _scaler) { + _scalerType = type; + _scaler = scaler; if (_scalerType == kScalerTypeInternal || _scalerType == kScalerTypeExternal) { _scaleFactor = CLIP(_scaleFactor, _scaler->factorMin, _scaler->factorMax); } else { diff --git a/unpack.cpp b/unpack.cpp index ce93402..ada3a20 100644 --- a/unpack.cpp +++ b/unpack.cpp @@ -16,52 +16,48 @@ struct UnpackCtx { }; static bool nextBit(UnpackCtx *uc) { - bool carry = (uc->bits & 1) != 0; + bool bit = (uc->bits & 1) != 0; uc->bits >>= 1; if (uc->bits == 0) { // getnextlwd - uc->bits = READ_BE_UINT32(uc->src); uc->src -= 4; - uc->crc ^= uc->bits; - carry = (uc->bits & 1) != 0; - uc->bits = (1 << 31) | (uc->bits >> 1); + const uint32_t bits = READ_BE_UINT32(uc->src); uc->src -= 4; + uc->crc ^= bits; + bit = (bits & 1) != 0; + uc->bits = (1 << 31) | (bits >> 1); } - return carry; + return bit; } -static int getBits(UnpackCtx *uc, int count) { // rdd1bits - int bits = 0; +template +static uint32_t getBits(UnpackCtx *uc) { // rdd1bits + uint32_t bits = 0; for (int i = 0; i < count; ++i) { - bits <<= 1; - if (nextBit(uc)) { - bits |= 1; - } + bits |= (nextBit(uc) ? 1 : 0) << (count - 1 - i); } return bits; } -static void copyLiteral(UnpackCtx *uc, int bitsCount, int len) { // getd3chr - int count = getBits(uc, bitsCount) + len + 1; - uc->size -= count; +static void copyLiteral(UnpackCtx *uc, int len) { // getd3chr + uc->size -= len; if (uc->size < 0) { - count += uc->size; + len += uc->size; uc->size = 0; } - for (int i = 0; i < count; ++i) { - *(uc->dst - i) = (uint8_t)getBits(uc, 8); + for (int i = 0; i < len; ++i) { + *(uc->dst - i) = (uint8_t)getBits<8>(uc); } - uc->dst -= count; + uc->dst -= len; } -static void copyReference(UnpackCtx *uc, int bitsCount, int count) { // copyd3bytes - uc->size -= count; +static void copyReference(UnpackCtx *uc, int len, int offset) { // copyd3bytes + uc->size -= len; if (uc->size < 0) { - count += uc->size; + len += uc->size; uc->size = 0; } - const int offset = getBits(uc, bitsCount); - for (int i = 0; i < count; ++i) { + for (int i = 0; i < len; ++i) { *(uc->dst - i) = *(uc->dst - i + offset); } - uc->dst -= count; + uc->dst -= len; } bool bytekiller_unpack(uint8_t *dst, int dstSize, const uint8_t *src, int srcSize) { @@ -79,24 +75,26 @@ bool bytekiller_unpack(uint8_t *dst, int dstSize, const uint8_t *src, int srcSiz do { if (!nextBit(&uc)) { if (!nextBit(&uc)) { - copyLiteral(&uc, 3, 0); + copyLiteral(&uc, getBits<3>(&uc) + 1); } else { - copyReference(&uc, 8, 2); + copyReference(&uc, 2, getBits<8>(&uc)); } } else { - const int code = getBits(&uc, 2); + const int code = getBits<2>(&uc); switch (code) { case 3: - copyLiteral(&uc, 8, 8); + copyLiteral(&uc, getBits<8>(&uc) + 9); break; - case 2: - copyReference(&uc, 12, getBits(&uc, 8) + 1); + case 2: { + const int len = getBits<8>(&uc) + 1; + copyReference(&uc, len, getBits<12>(&uc)); + } break; case 1: - copyReference(&uc, 10, 4); + copyReference(&uc, 4, getBits<10>(&uc)); break; case 0: - copyReference(&uc, 9, 3); + copyReference(&uc, 3, getBits<9>(&uc)); break; } }