diff --git a/README.txt b/README.txt index 590940a..3749006 100644 --- a/README.txt +++ b/README.txt @@ -1,6 +1,6 @@ REminiscence README -Release version: 0.3.5 +Release version: 0.3.6 ------------------------------------------------------------------------------- diff --git a/game.cpp b/game.cpp index 952ce3d..5404acb 100644 --- a/game.cpp +++ b/game.cpp @@ -13,7 +13,7 @@ #include "unpack.h" #include "util.h" -Game::Game(SystemStub *stub, FileSystem *fs, const char *savePath, int level, int demo, ResourceType ver, Language lang) +Game::Game(SystemStub *stub, FileSystem *fs, const char *savePath, int level, 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) { @@ -21,23 +21,12 @@ Game::Game(SystemStub *stub, FileSystem *fs, const char *savePath, int level, in _inp_demPos = 0; _skillLevel = _menu._skill = 1; _currentLevel = _menu._level = level; - _demoBin = demo; + _demoBin = -1; } void Game::run() { _randSeed = time(0); - if (_demoBin != -1) { - if (_demoBin < 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(); @@ -69,10 +58,8 @@ void Game::run() { _mix.init(); _mix._mod._isAmiga = _res.isAmiga(); - if (_demoBin == -1) { - playCutscene(0x40); - playCutscene(0x0D); - } + playCutscene(0x40); + playCutscene(0x0D); switch (_res._type) { case kResourceTypeAmiga: @@ -90,10 +77,7 @@ void Game::run() { } while (!_stub->_pi.quit) { - if (_demoBin != -1) { - _currentLevel = _demoInputs[_demoBin].level; - _randSeed = 0; - } else if (_res._isDemo) { + if (_res._isDemo) { // do not present title screen and menus } else { _mix.playMusic(1); @@ -104,6 +88,21 @@ void Game::run() { _stub->_pi.quit = true; break; } + if (_menu._selectedOption == Menu::MENU_OPTION_ITEM_DEMO) { + _demoBin = (_demoBin + 1) % ARRAYSIZE(_demoInputs); + const char *fn = _demoInputs[_demoBin].name; + debug(DBG_DEMO, "Loading inputs from '%s'", fn); + _res.load_DEM(fn); + if (_res._demLen == 0) { + continue; + } + _skillLevel = 1; + _currentLevel = _demoInputs[_demoBin].level; + _randSeed = 0; + _mix.stopMusic(); + break; + } + _demoBin = -1; _skillLevel = _menu._skill; _currentLevel = _menu._level; _mix.stopMusic(); @@ -135,10 +134,17 @@ void Game::run() { while (!_stub->_pi.quit && !_endLoop) { mainLoop(); if (_demoBin != -1 && _inp_demPos >= _res._demLen) { - debug(DBG_INFO, "End of demo"); - _stub->_pi.quit = true; + debug(DBG_DEMO, "End of demo"); + // exit level + _demoBin = -1; + _endLoop = true; } } + // flush inputs + _stub->_pi.dirMask = 0; + _stub->_pi.enter = false; + _stub->_pi.space = false; + _stub->_pi.shift = false; } } @@ -149,7 +155,7 @@ void Game::run() { void Game::displayTitleScreenAmiga() { static const char *FILENAME = "present.cmp"; - _res.load_CMP_menu(FILENAME, _res._memBuf); + _res.load_CMP_menu(FILENAME, _res._scratchBuffer); static const int kW = 320; static const int kH = 224; uint8_t *buf = (uint8_t *)calloc(kW * kH, 1); @@ -169,7 +175,7 @@ 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); + _vid.AMIGA_decodeCmp(_res._scratchBuffer + 6, buf); free(buf); for (int h = 0; h < kH / 2; h += 2) { const int y = kH / 2 - h; @@ -261,7 +267,7 @@ void Game::mainLoop() { return; } if (_loadMap) { - if (_currentRoom == 0xFF) { + if (_currentRoom == 0xFF || !hasLevelMap(_currentLevel, _pgeLive[0].room_location)) { _cut._id = 6; _deathCutsceneCounter = 1; } else { @@ -288,7 +294,7 @@ void Game::mainLoop() { } if (_stub->_pi.escape) { _stub->_pi.escape = false; - if (handleConfigPanel()) { + if (_demoBin != -1 || handleConfigPanel()) { _endLoop = true; return; } @@ -382,7 +388,7 @@ void Game::playCutscene(int id) { bool Game::playCutsceneSeq(const char *name) { File f; if (f.open(name, "rb", _fs)) { - _seq.setBackBuffer(_res._memBuf); + _seq.setBackBuffer(_res._scratchBuffer); _seq.play(&f); _vid.fullRefresh(); return true; @@ -967,13 +973,13 @@ void Game::drawAnimBuffer(uint8_t stateNum, AnimBufferState *state) { } switch (_res._type) { case kResourceTypeAmiga: - _vid.AMIGA_decodeSpm(state->dataPtr, _res._memBuf); - drawCharacter(_res._memBuf, state->x, state->y, state->h, state->w, pge->flags); + _vid.AMIGA_decodeSpm(state->dataPtr, _res._scratchBuffer); + drawCharacter(_res._scratchBuffer, state->x, state->y, state->h, state->w, pge->flags); break; case kResourceTypeDOS: if (!(state->dataPtr[-2] & 0x80)) { - decodeCharacterFrame(state->dataPtr, _res._memBuf); - drawCharacter(_res._memBuf, state->x, state->y, state->h, state->w, pge->flags); + decodeCharacterFrame(state->dataPtr, _res._scratchBuffer); + drawCharacter(_res._scratchBuffer, state->x, state->y, state->h, state->w, pge->flags); } else { drawCharacter(state->dataPtr, state->x, state->y, state->h, state->w, pge->flags); } @@ -1041,14 +1047,14 @@ void Game::drawObjectFrame(const uint8_t *bankDataPtr, const uint8_t *dataPtr, i switch (_res._type) { case kResourceTypeAmiga: - _vid.AMIGA_decodeSpc(src, sprite_w, sprite_h, _res._memBuf); + _vid.AMIGA_decodeSpc(src, sprite_w, sprite_h, _res._scratchBuffer); break; case kResourceTypeDOS: - _vid.PC_decodeSpc(src, sprite_w, sprite_h, _res._memBuf); + _vid.PC_decodeSpc(src, sprite_w, sprite_h, _res._scratchBuffer); break; } - src = _res._memBuf; + src = _res._scratchBuffer; bool sprite_mirror_x = false; int16_t sprite_clipped_w; if (sprite_x >= 0) { @@ -1294,6 +1300,15 @@ int Game::loadMonsterSprites(LivePGE *pge) { return 0xFFFF; } +bool Game::hasLevelMap(int level, int room) const { + if (_res._map) { + return READ_LE_UINT32(_res._map + room * 6) != 0; + } else if (_res._lev) { + return READ_BE_UINT32(_res._lev + room * 4) != 0; + } + return false; +} + void Game::loadLevelMap() { debug(DBG_GAME, "Game::loadLevelMap() room=%d", _currentRoom); _currentIcon = 0xFF; @@ -1441,6 +1456,7 @@ void Game::loadLevelData() { _pgeLive[0].room_location = _demoInputs[_demoBin].room; _pgeLive[0].pos_x = _demoInputs[_demoBin].x; _pgeLive[0].pos_y = _demoInputs[_demoBin].y; + _inp_demPos = 0; } else { _inp_demPos = 1; } @@ -1669,7 +1685,7 @@ void Game::handleInventory() { void Game::inp_update() { _stub->processEvents(); - if (_inp_demPos < _res._demLen) { + if (_demoBin != -1 && _inp_demPos < _res._demLen) { const int keymask = _res._dem[_inp_demPos++]; _stub->_pi.dirMask = keymask & 0xF; _stub->_pi.enter = (keymask & 0x10) != 0; diff --git a/game.h b/game.h index 1944922..39d4539 100644 --- a/game.h +++ b/game.h @@ -87,7 +87,7 @@ struct Game { bool _endLoop; uint32_t _frameTimestamp; - Game(SystemStub *, FileSystem *, const char *savePath, int level, int demo, ResourceType ver, Language lang); + Game(SystemStub *, FileSystem *, const char *savePath, int level, ResourceType ver, Language lang); void run(); void displayTitleScreenAmiga(); @@ -96,6 +96,7 @@ struct Game { void updateTiming(); void playCutscene(int id = -1); bool playCutsceneSeq(const char *name); + bool hasLevelMap(int level, int room) const; void loadLevelMap(); void loadLevelData(); void drawIcon(uint8_t iconNum, int16_t x, int16_t y, uint8_t colMask); diff --git a/main.cpp b/main.cpp index 195f5d7..4bcd7a1 100644 --- a/main.cpp +++ b/main.cpp @@ -174,7 +174,6 @@ int main(int argc, char *argv[]) { bool fullscreen = false; ScalerParameters scalerParameters = ScalerParameters::defaults(); int forcedLanguage = -1; - int demoNum = -1; if (argc == 2) { // data path as the only command line argument struct stat st; @@ -190,7 +189,6 @@ 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; @@ -235,9 +233,6 @@ int main(int argc, char *argv[]) { } } break; - case 7: - demoNum = atoi(optarg); - break; default: printf(USAGE, argv[0]); return 0; @@ -253,7 +248,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, demoNum, (ResourceType)version, language); + Game *g = new Game(stub, &fs, savePath, levelNum, (ResourceType)version, language); stub->init(g_caption, Video::GAMESCREEN_W, Video::GAMESCREEN_H, fullscreen, &scalerParameters); g->run(); delete g; diff --git a/menu.cpp b/menu.cpp index a199fde..08d91b3 100644 --- a/menu.cpp +++ b/menu.cpp @@ -73,16 +73,16 @@ void Menu::drawString2(const char *str, int16_t y, int16_t x) { void Menu::loadPicture(const char *prefix) { debug(DBG_MENU, "Menu::loadPicture('%s')", prefix); - _res->load_MAP_menu(prefix, _res->_memBuf); + _res->load_MAP_menu(prefix, _res->_scratchBuffer); for (int i = 0; i < 4; ++i) { for (int y = 0; y < 224; ++y) { for (int x = 0; x < 64; ++x) { - _vid->_frontLayer[i + x * 4 + 256 * y] = _res->_memBuf[0x3800 * i + x + 64 * y]; + _vid->_frontLayer[i + x * 4 + 256 * y] = _res->_scratchBuffer[0x3800 * i + x + 64 * y]; } } } - _res->load_PAL_menu(prefix, _res->_memBuf); - _stub->setPalette(_res->_memBuf, 256); + _res->load_PAL_menu(prefix, _res->_scratchBuffer); + _stub->setPalette(_res->_scratchBuffer, 256); } void Menu::handleInfoScreen() { @@ -309,7 +309,7 @@ void Menu::handleTitleScreen() { _charVar4 = 0; _charVar5 = 0; - static const int MAX_MENU_ITEMS = 5; + static const int MAX_MENU_ITEMS = 6; Item menuItems[MAX_MENU_ITEMS]; int menuItemsCount = 0; @@ -331,6 +331,9 @@ void Menu::handleTitleScreen() { menuItems[menuItemsCount].str = LocaleData::LI_10_INFO; menuItems[menuItemsCount].opt = MENU_OPTION_ITEM_INFO; ++menuItemsCount; + menuItems[menuItemsCount].str = LocaleData::LI_23_DEMO; + menuItems[menuItemsCount].opt = MENU_OPTION_ITEM_DEMO; + ++menuItemsCount; menuItems[menuItemsCount].str = LocaleData::LI_11_QUIT; menuItems[menuItemsCount].opt = MENU_OPTION_ITEM_QUIT; ++menuItemsCount; @@ -406,6 +409,9 @@ void Menu::handleTitleScreen() { _currentScreen = SCREEN_INFO; handleInfoScreen(); break; + case MENU_OPTION_ITEM_DEMO: + quitLoop = true; + break; case MENU_OPTION_ITEM_QUIT: quitLoop = true; break; diff --git a/menu.h b/menu.h index 554d65e..292dd01 100644 --- a/menu.h +++ b/menu.h @@ -20,6 +20,7 @@ struct Menu { MENU_OPTION_ITEM_PASSWORD, MENU_OPTION_ITEM_LEVEL, MENU_OPTION_ITEM_INFO, + MENU_OPTION_ITEM_DEMO, MENU_OPTION_ITEM_QUIT }; enum { diff --git a/mod_player.cpp b/mod_player.cpp index dc1eb7f..bdfa1f3 100644 --- a/mod_player.cpp +++ b/mod_player.cpp @@ -30,6 +30,7 @@ struct ModPlayer_impl { _settings.mBits = 16; _settings.mFrequency = rate; _settings.mResamplingMode = MODPLUG_RESAMPLE_FIR; + _settings.mLoopCount = -1; ModPlug_SetSettings(&_settings); } @@ -58,7 +59,15 @@ struct ModPlayer_impl { _repeatIntro = false; } const int count = ModPlug_Read(_mf, buf, len * sizeof(int16_t)); - return count > 0; + // setting mLoopCount to non-zero does not trigger any looping in + // my test and ModPlug_Read returns 0. + // looking at the libmodplug-0.8.8 tarball, it seems the variable + // m_nRepeatCount is commented in sndmix.cpp. Not sure how if this + // is a known bug, we workaround it here. + if (count == 0) { + ModPlug_SeekOrder(_mf, 0); + } + return true; } return false; } @@ -570,7 +579,8 @@ void ModPlayer_impl::handleTick() { } if (_currentPatternOrder == _modInfo.numPatterns) { debug(DBG_MOD, "ModPlayer::handleEffect() _currentPatternOrder == _modInfo.numPatterns"); - _playing = false; +// _playing = false; + _currentPatternOrder = 0; } } diff --git a/piege.cpp b/piege.cpp index 8b7fb73..ab149b0 100644 --- a/piege.cpp +++ b/piege.cpp @@ -193,7 +193,7 @@ set_anim: uint8_t _dl = pge->anim_seq; const uint8_t *anim_frame = anim_data + 6 + _dl * 4; while (_dh > _dl) { - if (READ_LE_UINT16(anim_frame) != 0xFFFF) { + if (_res._readUint16(anim_frame) != 0xFFFF) { if (_pge_currentPiegeFacingDir) { pge->pos_x -= (int8_t)anim_frame[2]; } else { diff --git a/resource.cpp b/resource.cpp index 4c5bfb0..6bf5a38 100644 --- a/resource.cpp +++ b/resource.cpp @@ -19,8 +19,8 @@ Resource::Resource(FileSystem *fs, ResourceType ver, Language lang) { _aba = 0; _readUint16 = (_type == kResourceTypeDOS) ? READ_LE_UINT16 : READ_BE_UINT16; _readUint32 = (_type == kResourceTypeDOS) ? READ_LE_UINT32 : READ_BE_UINT32; - _memBuf = (uint8_t *)malloc(320 * 224 + 1024); - if (!_memBuf) { + _scratchBuffer = (uint8_t *)malloc(320 * 224 + 1024); + if (!_scratchBuffer) { error("Unable to allocate temporary memory buffer"); } static const int kBankDataSize = 0x7000; @@ -40,7 +40,7 @@ Resource::~Resource() { free(_tab); free(_spc); free(_spr1); - free(_memBuf); + free(_scratchBuffer); free(_cmd); free(_pol); free(_cine_off); diff --git a/resource.h b/resource.h index 67f01a3..3de9763 100644 --- a/resource.h +++ b/resource.h @@ -37,6 +37,7 @@ struct LocaleData { LI_20_LOAD_GAME, LI_21_SAVE_GAME, LI_22_SAVE_SLOT, + LI_23_DEMO, LI_NUM }; @@ -144,7 +145,7 @@ struct Resource { uint8_t *_bnq; uint16_t _numObjectNodes; ObjectNode *_objectNodesMap[255]; - uint8_t *_memBuf; + uint8_t *_scratchBuffer; SoundFx *_sfxList; uint8_t _numSfx; uint8_t *_cmd; diff --git a/staticres.cpp b/staticres.cpp index 984eef4..077aa83 100644 --- a/staticres.cpp +++ b/staticres.cpp @@ -2261,7 +2261,7 @@ const uint8_t LocaleData::_stringsTableJP[] = { 0x0a, 0x61, 0x7a, 0x73, 0x20, 0x7d, 0x73, 0x6a, 0xcf, 0x92, 0x00 }; -const char *LocaleData::_textsTableFR[] = { +const char *LocaleData::_textsTableFR[LocaleData::LI_NUM] = { "CONTINUER OU ABANDONNER ?", "TEMPS", "CONTINUER", @@ -2283,10 +2283,11 @@ const char *LocaleData::_textsTableFR[] = { "ABANDONNER", "CHARGER", "SAUVEGARDER", - "PARTIE" + "PARTIE", + "DEMO" }; -const char *LocaleData::_textsTableEN[] = { +const char *LocaleData::_textsTableEN[LocaleData::LI_NUM] = { "CONTINUE OR ABORT THIS GAME ?", "TIME", "CONTINUE", @@ -2308,10 +2309,11 @@ const char *LocaleData::_textsTableEN[] = { "ABORT GAME", "LOAD GAME", "SAVE GAME", - "SLOT" + "SLOT", + "DEMO" }; -const char *LocaleData::_textsTableDE[] = { +const char *LocaleData::_textsTableDE[LocaleData::LI_NUM] = { "WEITERSPIELEN ODER ABBRECHEN ?", "ZEIT : ", "WEITERSPIELEN", @@ -2333,10 +2335,11 @@ const char *LocaleData::_textsTableDE[] = { "SPIEL ABBRECHEN", "LADEN", "SPEICHERN", - "SPIEL" + "SPIEL", + "DEMO" }; -const char *LocaleData::_textsTableSP[] = { +const char *LocaleData::_textsTableSP[LocaleData::LI_NUM] = { "CONTINUAR O TERMINAR JUEGO ?", "TIEMPO", "SEGUIR", @@ -2358,10 +2361,11 @@ const char *LocaleData::_textsTableSP[] = { "PARAR JUEGO", "CARGAR DATOS", "GUARDAR DATOS", - "JUEGO" + "JUEGO", + "DEMO" }; -const char *LocaleData::_textsTableIT[] = { +const char *LocaleData::_textsTableIT[LocaleData::LI_NUM] = { "CONTINUA O ABBANDONA GIOCO", "TEMPO", "CONTINUA", @@ -2383,7 +2387,8 @@ const char *LocaleData::_textsTableIT[] = { "ESCI DAL GIOCO", "CARICA IL GIOCO", "SALVA IL GIOCO", - "SLOT" + "SLOT", + "DEMO" }; const uint8_t LocaleData::_level1TbnJP[] = { diff --git a/systemstub_sdl.cpp b/systemstub_sdl.cpp index 34e5ccd..0ddd9d6 100644 --- a/systemstub_sdl.cpp +++ b/systemstub_sdl.cpp @@ -611,6 +611,8 @@ void SystemStub_SDL::prepareGraphics() { int flags = 0; if (_fullscreen) { flags |= SDL_WINDOW_FULLSCREEN_DESKTOP; + } else { + flags |= SDL_WINDOW_RESIZABLE; } _window = SDL_CreateWindow(_caption, SDL_WINDOWPOS_CENTERED, SDL_WINDOWPOS_CENTERED, windowW, windowH, flags); SDL_Surface *icon = SDL_LoadBMP(kIconBmp); diff --git a/util.h b/util.h index dcfe899..e2a89ae 100644 --- a/util.h +++ b/util.h @@ -22,7 +22,8 @@ enum { DBG_CUT = 1 << 9, DBG_MOD = 1 << 10, DBG_SFX = 1 << 11, - DBG_FILE = 1 << 12 + DBG_FILE = 1 << 12, + DBG_DEMO = 1 << 13 }; extern uint16_t g_debugMask; diff --git a/video.cpp b/video.cpp index 5667a56..25623a0 100644 --- a/video.cpp +++ b/video.cpp @@ -188,7 +188,7 @@ void Video::PC_decodeLev(int level, int room) { _res->clearBankData(); } -static void PC_decodeMapHelper(int sz, const uint8_t *src, uint8_t *dst) { +static void PC_decodeMapPlane(int sz, const uint8_t *src, uint8_t *dst) { const uint8_t *end = src + sz; while (src < end) { int16_t code = (int8_t)*src++; @@ -212,6 +212,7 @@ void Video::PC_decodeMap(int level, int room) { if (off == 0) { error("Invalid room %d", room); } + // int size = READ_LE_UINT16(_res->_map + room * 6 + 4); bool packed = true; if (off < 0) { off = -off; @@ -226,19 +227,18 @@ void Video::PC_decodeMap(int level, int room) { // workaround for wrong palette colors (fire) _mapPalSlot4 = 5; } + static const int kPlaneSize = 256 * 224 / 4; if (packed) { - uint8_t *vid = _frontLayer; for (int i = 0; i < 4; ++i) { const int sz = READ_LE_UINT16(p); p += 2; - PC_decodeMapHelper(sz, p, _res->_memBuf); p += sz; - memcpy(vid, _res->_memBuf, 256 * 56); - vid += 256 * 56; + PC_decodeMapPlane(sz, p, _res->_scratchBuffer); p += sz; + memcpy(_frontLayer + i * kPlaneSize, _res->_scratchBuffer, kPlaneSize); } } else { for (int i = 0; i < 4; ++i) { for (int y = 0; y < 224; ++y) { for (int x = 0; x < 64; ++x) { - _frontLayer[i + x * 4 + 256 * y] = p[256 * 56 * i + x + 64 * y]; + _frontLayer[i + x * 4 + 256 * y] = p[kPlaneSize * i + x + 64 * y]; } } } @@ -601,7 +601,7 @@ static void decodeLevHelper(uint8_t *dst, const uint8_t *src, int offset10, int } void Video::AMIGA_decodeLev(int level, int room) { - uint8_t *tmp = _res->_memBuf; + uint8_t *tmp = _res->_scratchBuffer; const int offset = READ_BE_UINT32(_res->_lev + room * 4); if (!delphine_unpack(tmp, _res->_lev, offset)) { error("Bad CRC for level %d room %d", level, room); @@ -835,8 +835,8 @@ void Video::PC_drawChar(uint8_t c, int16_t y, int16_t x, bool forceDefaultFont) void Video::AMIGA_drawStringChar(uint8_t *dst, int pitch, const uint8_t *src, uint8_t color, uint8_t chr) { assert(chr >= 32); - AMIGA_decodeIcn(src, chr - 32, _res->_memBuf); - src = _res->_memBuf; + AMIGA_decodeIcn(src, chr - 32, _res->_scratchBuffer); + src = _res->_scratchBuffer; for (int y = 0; y < 8; ++y) { for (int x = 0; x < 8; ++x) { if (src[x] != 0) {