diff --git a/README.txt b/README.txt index 931b0d3..d159aef 100644 --- a/README.txt +++ b/README.txt @@ -1,6 +1,6 @@ REminiscence README -Release version: 0.3.1 +Release version: 0.3.2 ------------------------------------------------------------------------------- @@ -23,8 +23,6 @@ Data Files: ----------- You will need the original files of the PC (DOS or CD) or Amiga release. -If you have a version distributed by SSI, you'll have to rename the files -and drop the 'ssi' suffix (ie. logosssi.cmd -> logos.cmd). To hear background music during polygonal cutscenes with the PC version, you'll need to copy the .mod files of the Amiga version : @@ -83,8 +81,6 @@ In-game hotkeys : Ctrl S save game state Ctrl L load game state Ctrl + and - change game state slot - Ctrl R toggle input keys record - Ctrl P toggle input keys replay Debug hotkeys : diff --git a/collision.cpp b/collision.cpp index f65a8ba..7769d94 100644 --- a/collision.cpp +++ b/collision.cpp @@ -40,7 +40,7 @@ void Game::col_clearState() { } void Game::col_preparePiegeState(LivePGE *pge) { - debug(DBG_COL, "Game::col_preparePiegeState() pge_num=%d", pge - &_pgeLive[0]); + debug(DBG_COL, "Game::col_preparePiegeState() pge_num=%ld", pge - &_pgeLive[0]); CollisionSlot *ct_slot1, *ct_slot2; if (pge->init_PGE->unk1C == 0) { pge->collision_slot = 0xFF; diff --git a/cutscene.h b/cutscene.h index 1658df1..e890eb4 100644 --- a/cutscene.h +++ b/cutscene.h @@ -31,6 +31,7 @@ struct Cutscene { static const char *_namesTable[]; static const uint16_t _offsetsTable[]; static const uint8_t _amigaDemoOffsetsTable[]; + static const uint8_t _ssiOffsetsTable[]; static const uint16_t _cosTable[]; static const uint16_t _sinTable[]; static const uint8_t _creditsDataDOS[]; diff --git a/game.cpp b/game.cpp index 5ab7bb1..3692c8b 100644 --- a/game.cpp +++ b/game.cpp @@ -13,31 +13,47 @@ #include "unpack.h" #include "util.h" -Game::Game(SystemStub *stub, FileSystem *fs, const char *savePath, int level, ResourceType ver, Language lang) +Game::Game(SystemStub *stub, FileSystem *fs, const char *savePath, int level, int demo, ResourceType ver, Language lang) : _cut(&_res, stub, &_vid), _menu(&_res, stub, &_vid), _mix(fs, stub), _res(fs, ver, lang), _seq(stub, &_mix), _vid(&_res, stub), _stub(stub), _fs(fs), _savePath(savePath) { _stateSlot = 1; - _inp_demo = 0; - _inp_record = false; - _inp_replay = false; + _inp_demPos = 0; _skillLevel = _menu._skill = 1; _currentLevel = _menu._level = level; + _demoBin = demo; } void Game::run() { _randSeed = time(0); + if (_demoBin != -1) { + if (_demoBin < (int)ARRAYSIZE(_demoInputs)) { + const char *fn = _demoInputs[_demoBin].name; + debug(DBG_INFO, "Loading inputs from '%s'", fn); + _res.load_DEM(fn); + } + if (_res._demLen == 0) { + return; + } + } + _res.init(); _res.load_TEXT(); switch (_res._type) { case kResourceTypeAmiga: _res.load("FONT8", Resource::OT_FNT, "SPR"); + if (_res._isDemo) { + _cut._patchedOffsetsTable = Cutscene::_amigaDemoOffsetsTable; + } break; case kResourceTypeDOS: _res.load("FB_TXT", Resource::OT_FNT); _res._hasSeqData = _fs->exists("INTRO.SEQ"); + if (_fs->exists("logosssi.cmd")) { + _cut._patchedOffsetsTable = Cutscene::_ssiOffsetsTable; + } break; } @@ -51,8 +67,10 @@ void Game::run() { _mix.init(); _mix._mod._isAmiga = _res.isAmiga(); - playCutscene(0x40); - playCutscene(0x0D); + if (_demoBin == -1) { + playCutscene(0x40); + playCutscene(0x0D); + } switch (_res._type) { case kResourceTypeAmiga: @@ -70,25 +88,30 @@ void Game::run() { } while (!_stub->_pi.quit) { - 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; + if (_demoBin != -1) { + _currentLevel = _demoInputs[_demoBin].level; + _randSeed = 0; + } else { + 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; + break; + } + _skillLevel = _menu._skill; + _currentLevel = _menu._level; + _mix.stopMusic(); + break; + case kResourceTypeAmiga: + displayTitleScreenAmiga(); + _stub->setScreenSize(Video::GAMESCREEN_W, Video::GAMESCREEN_H); + break; + } + if (_stub->_pi.quit) { break; } - _skillLevel = _menu._skill; - _currentLevel = _menu._level; - _mix.stopMusic(); - break; - case kResourceTypeAmiga: - displayTitleScreenAmiga(); - _stub->setScreenSize(Video::GAMESCREEN_W, Video::GAMESCREEN_H); - break; - } - if (_stub->_pi.quit) { - break; } if (_currentLevel == 7) { _vid.fadeOut(); @@ -107,6 +130,10 @@ void Game::run() { _frameTimestamp = _stub->getTimeStamp(); while (!_stub->_pi.quit && !_endLoop) { mainLoop(); + if (_demoBin != -1 && _inp_demPos >= _res._demLen) { + debug(DBG_INFO, "End of demo"); + _stub->_pi.quit = true; + } } } } @@ -370,52 +397,6 @@ void Game::inp_handleSpecialKeys() { } _stub->_pi.stateSlot = 0; } - if (_stub->_pi.inpRecord || _stub->_pi.inpReplay) { - bool replay = false; - bool record = false; - char demoFile[20]; - makeGameDemoName(demoFile); - if (_inp_demo) { - _inp_demo->close(); - delete _inp_demo; - } - _inp_demo = new File; - if (_stub->_pi.inpRecord) { - if (_inp_record) { - debug(DBG_INFO, "Stop recording input keys"); - } else { - if (_inp_demo->open(demoFile, "zwb", _savePath)) { - debug(DBG_INFO, "Recording input keys"); - static const uint32_t TAG_FBDM = 0x4642444D; - _inp_demo->writeUint32BE(TAG_FBDM); - _inp_demo->writeUint16BE(0); - _inp_demo->writeUint32BE(_randSeed); - record = true; - } else { - warning("Unable to save demo file '%s'", demoFile); - } - } - } - if (_stub->_pi.inpReplay) { - if (_inp_replay) { - debug(DBG_INFO, "Stop replaying input keys"); - } else { - if (_inp_demo->open(demoFile, "zrb", _savePath)) { - debug(DBG_INFO, "Replaying input keys"); - _inp_demo->readUint32BE(); - _inp_demo->readUint16BE(); - _randSeed = _inp_demo->readUint32BE(); - replay = true; - } else { - warning("Unable to open demo file '%s'", demoFile); - } - } - } - _inp_record = record; - _inp_replay = replay; - _stub->_pi.inpReplay = false; - _stub->_pi.inpRecord = false; - } } void Game::drawCurrentInventoryItem() { @@ -838,7 +819,7 @@ void Game::prepareAnims() { } void Game::prepareAnimsHelper(LivePGE *pge, int16_t dx, int16_t dy) { - debug(DBG_GAME, "Game::prepareAnimsHelper() dx=0x%X dy=0x%X pge_num=%d pge->flags=0x%X pge->anim_number=0x%X", dx, dy, pge - &_pgeLive[0], pge->flags, pge->anim_number); + debug(DBG_GAME, "Game::prepareAnimsHelper() dx=0x%X dy=0x%X pge_num=%ld pge->flags=0x%X pge->anim_number=0x%X", dx, dy, pge - &_pgeLive[0], pge->flags, pge->anim_number); if (!(pge->flags & 8)) { if (pge->index != 0 && loadMonsterSprites(pge) == 0) { return; @@ -980,7 +961,7 @@ void Game::drawObject(const uint8_t *dataPtr, int16_t x, int16_t y, uint8_t flag } void Game::drawObjectFrame(const uint8_t *bankDataPtr, const uint8_t *dataPtr, int16_t x, int16_t y, uint8_t flags) { - debug(DBG_GAME, "Game::drawObjectFrame(0x%X, %d, %d, 0x%X)", dataPtr, x, y, flags); + debug(DBG_GAME, "Game::drawObjectFrame(%p, %d, %d, 0x%X)", dataPtr, x, y, flags); const uint8_t *src = bankDataPtr + dataPtr[0] * 32; int16_t sprite_y = y + dataPtr[2]; @@ -1111,8 +1092,7 @@ void Game::decodeCharacterFrame(const uint8_t *dataPtr, uint8_t *dstPtr) { } void Game::drawCharacter(const uint8_t *dataPtr, int16_t pos_x, int16_t pos_y, uint8_t a, uint8_t b, uint8_t flags) { - debug(DBG_GAME, "Game::drawCharacter(0x%X, %d, %d, 0x%X, 0x%X, 0x%X)", dataPtr, pos_x, pos_y, a, b, flags); - + debug(DBG_GAME, "Game::drawCharacter(%p, %d, %d, 0x%X, 0x%X, 0x%X)", dataPtr, pos_x, pos_y, a, b, flags); bool var16 = false; // sprite_mirror_y if (b & 0x40) { b &= 0xBF; @@ -1197,7 +1177,7 @@ void Game::drawCharacter(const uint8_t *dataPtr, int16_t pos_x, int16_t pos_y, u uint32_t dst_offset = 256 * pos_y + pos_x; uint8_t sprite_col_mask = ((flags & 0x60) == 0x60) ? 0x50 : 0x40; - debug(DBG_GAME, "dst_offset=0x%X src_offset=0x%X", dst_offset, src - dataPtr); + debug(DBG_GAME, "dst_offset=0x%X src_offset=%ld", dst_offset, src - dataPtr); if (!(flags & 2)) { if (var16) { @@ -1302,7 +1282,6 @@ void Game::loadLevelData() { switch (_res._type) { case kResourceTypeAmiga: if (_res._isDemo) { - _cut._patchedOffsetsTable = Cutscene::_amigaDemoOffsetsTable; static const char *fname1 = "demo"; static const char *fname2 = "demof"; _res.load(fname1, Resource::OT_MBK); @@ -1397,6 +1376,17 @@ void Game::loadLevelData() { pge_loadForCurrentLevel(n); } + if (_demoBin != -1) { + _cut._id = -1; + if (_demoInputs[_demoBin].room != 255) { + _pgeLive[0].room_location = _demoInputs[_demoBin].room; + _pgeLive[0].pos_x = _demoInputs[_demoBin].x; + _pgeLive[0].pos_y = _demoInputs[_demoBin].y; + } else { + _inp_demPos = 1; + } + } + for (uint16_t i = 0; i < _res._pgeNum; ++i) { if (_res._pgeInit[i].skill <= _skillLevel) { LivePGE *pge = &_pgeLive[i]; @@ -1466,7 +1456,7 @@ void Game::playSound(uint8_t sfxId, uint8_t softVol) { uint16_t Game::getRandomNumber() { uint32_t n = _randSeed * 2; - if (_randSeed > n) { + if (((int32_t)_randSeed) >= 0) { n ^= 0x1D872B41; } _randSeed = n; @@ -1614,44 +1604,17 @@ void Game::handleInventory() { } void Game::inp_update() { - if (_inp_replay && _inp_demo) { - uint8_t keymask = _inp_demo->readByte(); - if (_inp_demo->ioErr()) { - _inp_replay = false; - } else { - _stub->_pi.dirMask = keymask & 0xF; - _stub->_pi.enter = (keymask & 0x10) != 0; - _stub->_pi.space = (keymask & 0x20) != 0; - _stub->_pi.shift = (keymask & 0x40) != 0; - _stub->_pi.quit = (keymask & 0x80) != 0; - } - } _stub->processEvents(); - if (_inp_record && _inp_demo) { - uint8_t keymask = _stub->_pi.dirMask; - if (_stub->_pi.enter) { - keymask |= 0x10; - } - if (_stub->_pi.space) { - keymask |= 0x20; - } - if (_stub->_pi.shift) { - keymask |= 0x40; - } - if (_stub->_pi.quit) { - keymask |= 0x80; - } - _inp_demo->writeByte(keymask); - if (_inp_demo->ioErr()) { - _inp_record = false; - } + if (_inp_demPos < _res._demLen) { + const int keymask = _res._dem[_inp_demPos++]; + _stub->_pi.dirMask = keymask & 0xF; + _stub->_pi.enter = (keymask & 0x10) != 0; + _stub->_pi.space = (keymask & 0x20) != 0; + _stub->_pi.shift = (keymask & 0x40) != 0; + _stub->_pi.backspace = (keymask & 0x80) != 0; } } -void Game::makeGameDemoName(char *buf) { - sprintf(buf, "rs-level%d.demo", _currentLevel + 1); -} - void Game::makeGameStateName(uint8_t slot, char *buf) { sprintf(buf, "rs-level%d-%02d.state", _currentLevel + 1, slot); } @@ -1856,7 +1819,7 @@ void Game::loadState(File *f) { } 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=0x%X pge=0x%X", stateNum, x, y, dataPtr, pge); + debug(DBG_GAME, "AnimBuffers::addState() stateNum=%d x=%d y=%d dataPtr=%p pge=%p", stateNum, x, y, dataPtr, pge); assert(stateNum < 4); AnimBufferState *state = _states[stateNum]; state->x = x; diff --git a/game.h b/game.h index 68b8f96..321085b 100644 --- a/game.h +++ b/game.h @@ -32,6 +32,7 @@ struct Game { CT_LEFT_ROOM = 0xC0 }; + static const Demo _demoInputs[3]; static const Level _gameLevels[]; static const uint16_t _scoreTable[]; static const uint8_t _monsterListLevel1[]; @@ -63,6 +64,7 @@ struct Game { const char **_textsTable; uint8_t _currentLevel; uint8_t _skillLevel; + int _demoBin; uint32_t _score; uint8_t _currentRoom; uint8_t _currentIcon; @@ -85,7 +87,7 @@ struct Game { bool _endLoop; uint32_t _frameTimestamp; - Game(SystemStub *, FileSystem *, const char *savePath, int level, ResourceType ver, Language lang); + Game(SystemStub *, FileSystem *, const char *savePath, int level, int demo, ResourceType ver, Language lang); void run(); void displayTitleScreenAmiga(); @@ -351,9 +353,7 @@ struct Game { // input uint8_t _inp_lastKeysHit; uint8_t _inp_lastKeysHitLeftRight; - bool _inp_replay; - bool _inp_record; - File *_inp_demo; + int _inp_demPos; void inp_handleSpecialKeys(); void inp_update(); @@ -363,7 +363,6 @@ struct Game { uint8_t _stateSlot; bool _validSaveState; - void makeGameDemoName(char *buf); void makeGameStateName(uint8_t slot, char *buf); bool saveGameState(uint8_t slot); bool loadGameState(uint8_t slot); diff --git a/intern.h b/intern.h index 0387fb1..0d2b044 100644 --- a/intern.h +++ b/intern.h @@ -106,6 +106,13 @@ struct Point { int16_t y; }; +struct Demo { + const char *name; + int level; + int room; + int x, y; +}; + struct Level { const char *name; const char *name2; diff --git a/main.cpp b/main.cpp index afe7576..fedc59c 100644 --- a/main.cpp +++ b/main.cpp @@ -134,6 +134,7 @@ int main(int argc, char *argv[]) { int scaler = DEFAULT_SCALER; bool fullscreen = false; int forcedLanguage = -1; + int demoNum = -1; if (argc == 2) { // data path as the only command line argument struct stat st; @@ -149,6 +150,7 @@ int main(int argc, char *argv[]) { { "fullscreen", no_argument, 0, 4 }, { "scaler", required_argument, 0, 5 }, { "language", required_argument, 0, 6 }, + { "playdemo", required_argument, 0, 7 }, { 0, 0, 0, 0 } }; int index; @@ -195,6 +197,9 @@ int main(int argc, char *argv[]) { } } break; + case 7: + demoNum = atoi(optarg); + break; default: printf(USAGE, argv[0]); return 0; @@ -210,7 +215,7 @@ int main(int argc, char *argv[]) { } const Language language = (forcedLanguage == -1) ? detectLanguage(&fs) : (Language)forcedLanguage; SystemStub *stub = SystemStub_SDL_create(); - Game *g = new Game(stub, &fs, savePath, levelNum, (ResourceType)version, language); + Game *g = new Game(stub, &fs, savePath, levelNum, demoNum, (ResourceType)version, language); stub->init(g_caption, Video::GAMESCREEN_W, Video::GAMESCREEN_H, scaler, fullscreen); g->run(); delete g; diff --git a/piege.cpp b/piege.cpp index 4ee52e6..a784d6c 100644 --- a/piege.cpp +++ b/piege.cpp @@ -113,7 +113,7 @@ void Game::pge_loadForCurrentLevel(uint16_t idx) { } void Game::pge_process(LivePGE *pge) { - debug(DBG_PGE, "Game::pge_process() pge_num=%d", pge - &_pgeLive[0]); + debug(DBG_PGE, "Game::pge_process() pge_num=%ld", pge - &_pgeLive[0]); _pge_playAnimSound = true; _pge_currentPiegeFacingDir = (pge->flags & 1) != 0; _pge_currentPiegeRoom = pge->room_location; @@ -226,7 +226,7 @@ void Game::pge_playAnimSound(LivePGE *pge, uint16_t arg2) { } void Game::pge_setupAnim(LivePGE *pge) { - debug(DBG_PGE, "Game::pge_setupAnim() pgeNum=%d", pge - &_pgeLive[0]); + debug(DBG_PGE, "Game::pge_setupAnim() pgeNum=%ld", pge - &_pgeLive[0]); const uint8_t *anim_data = _res.getAniData(pge->obj_type); if (_res._readUint16(anim_data) < pge->anim_seq) { pge->anim_seq = 0; @@ -254,7 +254,7 @@ void Game::pge_setupAnim(LivePGE *pge) { } int Game::pge_execute(LivePGE *live_pge, InitPGE *init_pge, const Object *obj) { - debug(DBG_PGE, "Game::pge_execute() pge_num=%d op1=0x%X op2=0x%X op3=0x%X", live_pge - &_pgeLive[0], obj->opcode1, obj->opcode2, obj->opcode3); + debug(DBG_PGE, "Game::pge_execute() pge_num=%ld op1=0x%X op2=0x%X op3=0x%X", live_pge - &_pgeLive[0], obj->opcode1, obj->opcode2, obj->opcode3); pge_OpcodeProc op; ObjectOpcodeArgs args; if (obj->opcode1) { @@ -378,7 +378,7 @@ void Game::pge_setupDefaultAnim(LivePGE *pge) { pge->flags |= 8; } pge->anim_number = _res._readUint16(anim_frame) & 0x7FFF; - debug(DBG_PGE, "Game::pge_setupDefaultAnim() pgeNum=%d pge->flags=0x%X pge->anim_number=0x%X pge->anim_seq=0x%X", pge - &_pgeLive[0], pge->flags, pge->anim_number, pge->anim_seq); + debug(DBG_PGE, "Game::pge_setupDefaultAnim() pgeNum=%ld pge->flags=0x%X pge->anim_number=0x%X pge->anim_seq=0x%X", pge - &_pgeLive[0], pge->flags, pge->anim_number, pge->anim_seq); } } @@ -464,7 +464,7 @@ void Game::pge_setupOtherPieges(LivePGE *pge, InitPGE *init_pge) { } void Game::pge_addToCurrentRoomList(LivePGE *pge, uint8_t room) { - debug(DBG_PGE, "Game::pge_addToCurrentRoomList() pgeNum=%d room=%d", pge - &_pgeLive[0], room); + debug(DBG_PGE, "Game::pge_addToCurrentRoomList() pgeNum=%ld room=%d", pge - &_pgeLive[0], room); if (room != pge->room_location) { LivePGE *cur_pge = _pge_liveTable1[room]; LivePGE *prev_pge = 0; diff --git a/resource.cpp b/resource.cpp index 7c0c002..648afd3 100644 --- a/resource.cpp +++ b/resource.cpp @@ -84,6 +84,19 @@ void Resource::clearLevelRes() { free_OBJ(); } +void Resource::load_DEM(const char *filename) { + free(_dem); _dem = 0; + _demLen = 0; + File f; + if (f.open(filename, "rb", _fs)) { + _demLen = f.size(); + _dem = (uint8_t *)malloc(_demLen); + if (_dem) { + f.read(_dem, _demLen); + } + } +} + void Resource::load_FIB(const char *fileName) { debug(DBG_RES, "Resource::load_FIB('%s')", fileName); static const uint8_t fibonacciTable[] = { diff --git a/resource.h b/resource.h index 1d7c09a..bea1513 100644 --- a/resource.h +++ b/resource.h @@ -148,6 +148,8 @@ struct Resource { uint8_t *_bankDataTail; BankSlot _bankBuffers[NUM_BANK_BUFFERS]; int _bankBuffersCount; + uint8_t *_dem; + int _demLen; Resource(FileSystem *fs, ResourceType type, Language lang); ~Resource(); @@ -159,6 +161,7 @@ struct Resource { bool isAmiga() const { return _type == kResourceTypeAmiga; } void clearLevelRes(); + void load_DEM(const char *filename); void load_FIB(const char *fileName); void load_SPL_demo(); void load_MAP_menu(const char *fileName, uint8_t *dstPtr); diff --git a/staticres.cpp b/staticres.cpp index db5aff5..7684688 100644 --- a/staticres.cpp +++ b/staticres.cpp @@ -64,7 +64,8 @@ const char *Cutscene::_namesTable[] = { "INTRO2", "SERRURE", "HOLOCUBE", - "CHUTE2" + "CHUTE2", + "LOGOSSSI", }; const uint16_t Cutscene::_offsetsTable[] = { @@ -92,6 +93,11 @@ const uint8_t Cutscene::_amigaDemoOffsetsTable[] = { 255 }; +const uint8_t Cutscene::_ssiOffsetsTable[] = { + 64, 34, 0, /* LOGOSSSI */ + 255 +}; + const uint16_t Cutscene::_cosTable[] = { 0x0100, 0x00FF, 0x00FF, 0x00FF, 0x00FF, 0x00FF, 0x00FE, 0x00FE, 0x00FD, 0x00FC, 0x00FC, 0x00FB, 0x00FA, 0x00F9, 0x00F8, 0x00F7, @@ -830,6 +836,12 @@ const Cutscene::Text Cutscene::_frTextsTable[] = { { -1, 0 } }; +const Demo Game::_demoInputs[] = { + { "demo1.bin", 0, 0x33, 0x60, 0x46 }, + { "demo51.bin", 5, 0x00, 0x60, 0xD6 }, + { "demo3.bin" , 2, 0xFF, 0x00, 0x00 } +}; + const Level Game::_gameLevels[] = { { "level1", "level1", "level1", 0x00, 1, 3 }, { "level2", "level2", "level2", 0x2F, 1, 4 }, diff --git a/systemstub.h b/systemstub.h index ed7e5ad..a76fbf2 100644 --- a/systemstub.h +++ b/systemstub.h @@ -35,9 +35,6 @@ struct PlayerInput { bool load; int stateSlot; - bool inpRecord; - bool inpReplay; - bool mirrorMode; uint8_t dbgMask; diff --git a/systemstub_sdl.cpp b/systemstub_sdl.cpp index 4438abe..ee5ae6c 100644 --- a/systemstub_sdl.cpp +++ b/systemstub_sdl.cpp @@ -10,6 +10,8 @@ #include "util.h" static const int kAudioHz = 22050; + +static const int kJoystickIndex = 0; static const int kJoystickCommitValue = 3200; struct SystemStub_SDL : SystemStub { @@ -17,6 +19,7 @@ struct SystemStub_SDL : SystemStub { SDL_Window *_window; SDL_Renderer *_renderer; SDL_Texture *_texture; + SDL_GameController *_controller; #else SDL_Surface *_surface; #endif @@ -83,15 +86,34 @@ void SystemStub_SDL::init(const char *title, int w, int h, int scaler, bool full memset(_pal, 0, sizeof(_pal)); _screenW = _screenH = 0; setScreenSize(w, h); - _joystick = NULL; + _joystick = 0; +#if SDL_VERSION_ATLEAST(2, 0, 0) + _controller = 0; if (SDL_NumJoysticks() > 0) { - _joystick = SDL_JoystickOpen(0); + SDL_GameControllerAddMappingsFromFile("gamecontrollerdb.txt"); + if (SDL_IsGameController(kJoystickIndex)) { + _controller = SDL_GameControllerOpen(kJoystickIndex); + } + if (!_controller) { + _joystick = SDL_JoystickOpen(kJoystickIndex); + } } +#else + if (SDL_NumJoysticks() > 0) { + _joystick = SDL_JoystickOpen(kJoystickIndex); + } +#endif _screenshot = 1; } void SystemStub_SDL::destroy() { cleanupGraphics(); +#if SDL_VERSION_ATLEAST(2, 0, 0) + if (_controller) { + SDL_GameControllerClose(_controller); + _controller = 0; + } +#endif if (_joystick) { SDL_JoystickClose(_joystick); _joystick = 0; @@ -141,7 +163,7 @@ void SystemStub_SDL::setOverscanColor(int i) { } void SystemStub_SDL::copyRect(int x, int y, int w, int h, const uint8_t *buf, int pitch) { - if (_numBlitRects >= ARRAYSIZE(_blitRects)) { + if (_numBlitRects >= (int)ARRAYSIZE(_blitRects)) { warning("SystemStub_SDL::copyRect() Too many blit rects, you may experience graphical glitches"); } else { // extend the dirty region by 1 pixel for scalers accessing 'outer' pixels @@ -201,13 +223,16 @@ void SystemStub_SDL::copyRect(int x, int y, int w, int h, const uint8_t *buf, in } void SystemStub_SDL::fadeScreen() { - const int fadeScreenBufferSize = _screenH * _screenW * sizeof(uint16_t); + const int bufferSize = _screenH * _screenW * sizeof(uint16_t); if (!_fadeScreenBuffer) { - _fadeScreenBuffer = (uint16_t *)malloc(fadeScreenBufferSize); - assert(_fadeScreenBuffer); + _fadeScreenBuffer = (uint16_t *)malloc(bufferSize); + if (!_fadeScreenBuffer) { + warning("SystemStub_SDL::fadeScreen() Unable to allocate buffer size %d", bufferSize); + return; + } } _fadeOnUpdateScreen = true; - memcpy(_fadeScreenBuffer, _screenBuffer + _screenW + 1, fadeScreenBufferSize); + memcpy(_fadeScreenBuffer, _screenBuffer + _screenW + 1, bufferSize); } static uint16_t blendPixel16(uint16_t colorSrc, uint16_t colorDst, uint32_t mask, int step) { @@ -219,9 +244,23 @@ 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) - // _fadeOnUpdateScreen SDL_UpdateTexture(_texture, 0, _screenBuffer + _screenW + 1, _screenW * sizeof(uint16_t)); SDL_RenderClear(_renderer); + if (_fadeOnUpdateScreen) { + SDL_SetRenderDrawBlendMode(_renderer, SDL_BLENDMODE_BLEND); + SDL_Rect r; + r.x = r.y = 0; + SDL_GetRendererOutputSize(_renderer, &r.w, &r.h); + for (int i = 1; i <= 16; ++i) { + SDL_SetRenderDrawColor(_renderer, 0, 0, 0, 256 - i * 16); + SDL_RenderCopy(_renderer, _texture, 0, 0); + SDL_RenderFillRect(_renderer, &r); + SDL_RenderPresent(_renderer); + SDL_Delay(30); + } + _fadeOnUpdateScreen = false; + return; + } if (shakeOffset != 0) { SDL_Rect r; r.x = 0; @@ -375,41 +414,111 @@ void SystemStub_SDL::processEvent(const SDL_Event &ev, bool &paused) { } break; case SDL_JOYBUTTONDOWN: - if (_joystick) { - switch (ev.jbutton.button) { - case 0: - _pi.space = true; - break; - case 1: - _pi.shift = true; - break; - case 2: - _pi.enter = true; - break; - case 3: - _pi.backspace = true; - break; - } - } - break; case SDL_JOYBUTTONUP: if (_joystick) { + const bool pressed = (ev.jbutton.state == SDL_PRESSED); switch (ev.jbutton.button) { case 0: - _pi.space = false; + _pi.space = pressed; break; case 1: - _pi.shift = false; + _pi.shift = pressed; break; case 2: - _pi.enter = false; + _pi.enter = pressed; break; case 3: - _pi.backspace = false; + _pi.backspace = pressed; break; } } break; +#if SDL_VERSION_ATLEAST(2, 0, 0) + case SDL_CONTROLLERAXISMOTION: + if (_controller) { + switch (ev.caxis.axis) { + case SDL_CONTROLLER_AXIS_LEFTX: + case SDL_CONTROLLER_AXIS_RIGHTX: + if (ev.caxis.value < -kJoystickCommitValue) { + _pi.dirMask |= PlayerInput::DIR_LEFT; + } else { + _pi.dirMask &= ~PlayerInput::DIR_LEFT; + } + if (ev.caxis.value > kJoystickCommitValue) { + _pi.dirMask |= PlayerInput::DIR_RIGHT; + } else { + _pi.dirMask &= ~PlayerInput::DIR_RIGHT; + } + break; + case SDL_CONTROLLER_AXIS_LEFTY: + case SDL_CONTROLLER_AXIS_RIGHTY: + if (ev.caxis.value < -kJoystickCommitValue) { + _pi.dirMask |= PlayerInput::DIR_UP; + } else { + _pi.dirMask &= ~PlayerInput::DIR_UP; + } + if (ev.caxis.value > kJoystickCommitValue) { + _pi.dirMask |= PlayerInput::DIR_DOWN; + } else { + _pi.dirMask &= ~PlayerInput::DIR_DOWN; + } + break; + } + } + break; + case SDL_CONTROLLERBUTTONDOWN: + case SDL_CONTROLLERBUTTONUP: + if (_controller) { + const bool pressed = (ev.cbutton.state == SDL_PRESSED); + switch (ev.cbutton.button) { + case SDL_CONTROLLER_BUTTON_A: + _pi.enter = pressed; + break; + case SDL_CONTROLLER_BUTTON_B: + _pi.space = pressed; + break; + case SDL_CONTROLLER_BUTTON_X: + _pi.shift = pressed; + break; + case SDL_CONTROLLER_BUTTON_Y: + _pi.backspace = pressed; + break; + case SDL_CONTROLLER_BUTTON_BACK: + case SDL_CONTROLLER_BUTTON_START: + _pi.escape = pressed; + break; + case SDL_CONTROLLER_BUTTON_DPAD_UP: + if (pressed) { + _pi.dirMask |= PlayerInput::DIR_UP; + } else { + _pi.dirMask &= ~PlayerInput::DIR_UP; + } + break; + case SDL_CONTROLLER_BUTTON_DPAD_DOWN: + if (pressed) { + _pi.dirMask |= PlayerInput::DIR_DOWN; + } else { + _pi.dirMask &= ~PlayerInput::DIR_DOWN; + } + break; + case SDL_CONTROLLER_BUTTON_DPAD_LEFT: + if (pressed) { + _pi.dirMask |= PlayerInput::DIR_LEFT; + } else { + _pi.dirMask &= ~PlayerInput::DIR_LEFT; + } + break; + case SDL_CONTROLLER_BUTTON_DPAD_RIGHT: + if (pressed) { + _pi.dirMask |= PlayerInput::DIR_RIGHT; + } else { + _pi.dirMask &= ~PlayerInput::DIR_RIGHT; + } + break; + } + } + break; +#endif case SDL_KEYUP: switch (ev.key.keysym.sym) { case SDLK_LEFT: @@ -484,10 +593,6 @@ void SystemStub_SDL::processEvent(const SDL_Event &ev, bool &paused) { _pi.stateSlot = 1; } else if (ev.key.keysym.sym == SDLK_KP_MINUS || ev.key.keysym.sym == SDLK_PAGEDOWN) { _pi.stateSlot = -1; - } else if (ev.key.keysym.sym == SDLK_r) { - _pi.inpRecord = true; - } else if (ev.key.keysym.sym == SDLK_p) { - _pi.inpReplay = true; } } _pi.lastChar = ev.key.keysym.sym; diff --git a/util.h b/util.h index ed896a1..dcfe899 100644 --- a/util.h +++ b/util.h @@ -27,8 +27,8 @@ enum { extern uint16_t g_debugMask; -extern void debug(uint16_t cm, const char *msg, ...); -extern void error(const char *msg, ...); -extern void warning(const char *msg, ...); +extern void debug(uint16_t cm, const char *msg, ...); // __attribute__((__format__(__printf__, 2, 3))) +extern void error(const char *msg, ...); // __attribute__((__format__(__printf__, 1, 2))) +extern void warning(const char *msg, ...); // __attribute__((__format__(__printf__, 1, 2))) #endif // UTIL_H__