From 1d20dd4d0fa45749f87512c702339472be4db7a2 Mon Sep 17 00:00:00 2001 From: Matthieu Milan Date: Mon, 4 May 2020 19:29:43 +0200 Subject: [PATCH] Import hode 0.2.9 --- CHANGES.txt | 9 +- README.txt | 2 +- RELEASES.yaml | 25 +++ defs.h | 3 +- fileio.cpp | 36 ++-- fileio.h | 6 +- fs_posix.cpp | 1 + game.cpp | 267 ++++++++++++++----------- game.h | 8 +- intern.h | 22 ++- level1_rock.cpp | 41 ++-- level2_fort.cpp | 4 +- level3_pwr1.cpp | 2 +- level5_lava.cpp | 2 +- level7_lar1.cpp | 4 +- level8_lar2.cpp | 9 +- level9_dark.cpp | 2 +- main.cpp | 37 +++- mdec.cpp | 2 +- menu.cpp | 505 +++++++++++++++++++++++++++++++++++++----------- menu.h | 21 +- mixer.cpp | 9 +- mixer.h | 4 - monsters.cpp | 14 +- paf.cpp | 127 ++++++------ paf.h | 14 +- random.cpp | 14 +- random.h | 3 +- resource.cpp | 311 ++++++++++++++++++++--------- resource.h | 49 ++++- sound.cpp | 23 +-- system.h | 6 +- system_psp.cpp | 417 +++++++++++++++++++++++++++++++++++++++ system_sdl2.cpp | 84 ++++++-- system_wii.cpp | 485 ++++++++++++++++++++++++++++++++++++++++++++++ util.cpp | 16 +- util.h | 4 + video.cpp | 96 +++++---- video.h | 21 +- 39 files changed, 2116 insertions(+), 589 deletions(-) create mode 100644 system_psp.cpp create mode 100644 system_wii.cpp diff --git a/CHANGES.txt b/CHANGES.txt index ff9fd96..a0f742b 100644 --- a/CHANGES.txt +++ b/CHANGES.txt @@ -1,6 +1,13 @@ +* release 0.2.9 + - added PSP and Wii platforms specific code + - added support for PC demo v1.0 data files + - fixed screen state for 'fort' screens 16 and 17 + - fixed checkpoint for 'pwr1' screen 21 + - fixed PAF animation glitches with last frame + * release 0.2.8 - added PSX background overlays (MDEC) - - fixed crash playing paf animation #3 with Italian PC data files + - fixed crash playing PAF animation #3 with Italian PC data files * release 0.2.7 - added 'projection' submenu diff --git a/README.txt b/README.txt index fe9fa07..4c04f9b 100644 --- a/README.txt +++ b/README.txt @@ -1,6 +1,6 @@ hode README -Release version: 0.2.8 +Release version: 0.2.9 ------------------------------------------------------------------------------- diff --git a/RELEASES.yaml b/RELEASES.yaml index 96a441a..5e99e7f 100644 --- a/RELEASES.yaml +++ b/RELEASES.yaml @@ -7,6 +7,7 @@ - fort_hod.lvl: 82456ed6e29780b5b8031a67ba3dddb8da813c19 - fort_hod.mst: ce55095902ade9f1d8a198f271d0946d6228b90d - fort_hod.sss: 6ff572b553d93040c9cad74891db05b2cc8267c6 + - hod.dem: 5d40f812b6ed1385b7e502527911023f28136341 - hod.paf: 6df823a778ed0df275217692fd814a7408b725cb - hodwin32.exe: 02ae85c179f175c5a42fadb22ba9edaf4f62be14 securom: true @@ -99,6 +100,7 @@ - fort_hod.lvl: 82456ed6e29780b5b8031a67ba3dddb8da813c19 - fort_hod.mst: ce55095902ade9f1d8a198f271d0946d6228b90d - fort_hod.sss: 6ff572b553d93040c9cad74891db05b2cc8267c6 + - hod.dem: 5d40f812b6ed1385b7e502527911023f28136341 - hod.paf: fe75aff52c879aa5cc84a87babbca2ce0affec23 - hodwin32.exe: eb0f032f343e2c27d60291c79ae667034a0c7389 securom: false @@ -137,6 +139,28 @@ - rock_hod.mst: 5b49637348f6c6a2737a64cacf01f9bcef8e83f6 - rock_hod.sss: 55c841f6c091c2ae68f0636c075c66405d00cc6a - setup.dat: 5cb36223a3fc5561d9c9d945fe3261cd188b5665 +- name: Heart of Darkness Demo V1.0 (Win32) + files: + - hod_demo.paf: c1b46680b9e8596d72f998fb2b56d723a28a06af + - hodwin32.exe: c649d4365a2c9f6280bc4deb50a7c3ffb73baaad + securom: false + version_info: + - Comments: Demo 1 level + - CompanyName: Amazing Studio - 9 rue d' Enhgien - 75010 Paris FRANCE - Email: hod@amazingstudio.com + - FileDescription: Heart Of Darkness + - FileVersion: 0, 9, 8, 2 + - InternalName: Heart Of Darkness + - LegalCopyright: (c) 1998 by Amazing Studio & Infogrames + - LegalTrademarks: by Amazing Studio - All rights reserved + - OriginalFilename: Heart Of Darkness + - PrivateBuild: 0 + - ProductName: Heart Of Darkness - Windows 95 / 98 & NT - DirectX 3.x or greater + - ProductVersion: 0, 9, 8, 2 + - SpecialBuild: VC5.0 Sp3 + - rock_hod.lvl: c514afa2137b434cdd8da0024c1e4f20bff1cdb7 + - rock_hod.mst: fa3fd22005a859abdae3b6d8b4f5ecb061a16e37 + - rock_hod.sss: 855d5b4b4fec7c8d8d64575d8a1ce5ee27d8e2d5 + - setup.dat: 56fd54d28074657b947e165134f044d9a91cef47 - name: Heart of Darkness Demo V1.2 (Win32) files: - hod_demo.paf: 50a0942256b17ed26f3b0f59f4cf7570a6eb8688 @@ -223,6 +247,7 @@ - dark_hod.mst: a97ac13edde17fd33cb06e9907139949c8b79cdb - fort_hod.lvl: e94a21bc4f779dba658ef5ab45f1ad15880913d8 - fort_hod.mst: f7423c3d277650ac36378f09f59034cec28b6b34 + - hod.dem: ba5060778a9f5391a8fa9a4928473cf7797df15a - isld_hod.lvl: c0fbd02deea136d57b60bc8a7c3df69741491335 - isld_hod.mst: 9ff92346444a66833eb2f8a1b3853ad788d1777b - lar1_hod.lvl: 575dde976affeedfa5f1637a34d5efdda3218778 diff --git a/defs.h b/defs.h index 596f39c..5a4114a 100644 --- a/defs.h +++ b/defs.h @@ -60,7 +60,7 @@ struct SetupConfig { uint8_t difficulty; uint8_t stereo; uint8_t volume; - uint8_t currentLevel; + uint8_t lastLevelNum; } players[4]; // sizeof == 52 uint8_t unkD0; uint8_t currentPlayer; // 0xD1 @@ -230,7 +230,6 @@ struct Sprite { const uint8_t *bitmapBits; Sprite *nextPtr; uint16_t num; - uint16_t flags; uint16_t w, h; }; diff --git a/fileio.cpp b/fileio.cpp index 792b3aa..0b2e9d9 100644 --- a/fileio.cpp +++ b/fileio.cpp @@ -7,7 +7,13 @@ #include "fileio.h" #include "util.h" -static const bool kCheckSectorFileCrc = true; +static const bool kCheckSectorFileCrc = false; + +#ifdef PSP +static const bool kSeekAbsolutePosition = true; +#else +static const bool kSeekAbsolutePosition = false; +#endif File::File() : _fp(0) { @@ -20,11 +26,15 @@ void File::setFp(FILE *fp) { _fp = fp; } -void File::seekAlign(int pos) { +void File::seekAlign(uint32_t pos) { fseek(_fp, pos, SEEK_SET); } void File::seek(int pos, int whence) { + if (kSeekAbsolutePosition && whence == SEEK_CUR) { + pos += ftell(_fp); + whence = SEEK_SET; + } fseek(_fp, pos, whence); } @@ -50,9 +60,6 @@ uint32_t File::readUint32() { return READ_LE_UINT32(buf); } -void File::flush() { -} - SectorFile::SectorFile() { memset(_buf, 0, sizeof(_buf)); _bufPos = 2044; @@ -93,13 +100,14 @@ void SectorFile::refillBuffer(uint8_t *ptr) { } } -void SectorFile::seekAlign(int pos) { +void SectorFile::seekAlign(uint32_t pos) { pos += (pos / 2048) * 4; - const int alignPos = (pos / 2048) * 2048; - fseek(_fp, alignPos, SEEK_SET); - refillBuffer(); - const int skipCount = pos - alignPos; - _bufPos += skipCount; + const long alignPos = pos & ~2047; + if (alignPos != (ftell(_fp) - 2048)) { + fseek(_fp, alignPos, SEEK_SET); + refillBuffer(); + } + _bufPos = pos - alignPos; } void SectorFile::seek(int pos, int whence) { @@ -148,9 +156,3 @@ int SectorFile::read(uint8_t *ptr, int size) { } return 0; } - -void SectorFile::flush() { - const int currentPos = ftell(_fp); - assert((currentPos & 2047) == 0); - _bufPos = 2044; -} diff --git a/fileio.h b/fileio.h index 0667cc2..44d7fc9 100644 --- a/fileio.h +++ b/fileio.h @@ -17,13 +17,12 @@ struct File { void setFp(FILE *fp); - virtual void seekAlign(int pos); + virtual void seekAlign(uint32_t pos); virtual void seek(int pos, int whence); virtual int read(uint8_t *ptr, int size); uint8_t readByte(); uint16_t readUint16(); uint32_t readUint32(); - virtual void flush(); void skipByte() { seek(1, SEEK_CUR); } void skipUint16() { seek(2, SEEK_CUR); } @@ -42,10 +41,9 @@ struct SectorFile : File { SectorFile(); void refillBuffer(uint8_t *ptr = 0); - virtual void seekAlign(int pos); + virtual void seekAlign(uint32_t pos); virtual void seek(int pos, int whence); virtual int read(uint8_t *ptr, int size); - virtual void flush(); }; int fioAlignSizeTo2048(int size); diff --git a/fs_posix.cpp b/fs_posix.cpp index ebc749c..e255e91 100644 --- a/fs_posix.cpp +++ b/fs_posix.cpp @@ -8,6 +8,7 @@ #include "util.h" static const char *_suffixes[] = { + "hod.dem", "setup.dat", "setup.dax", ".paf", diff --git a/game.cpp b/game.cpp index 1c6a56c..b46fa9e 100644 --- a/game.cpp +++ b/game.cpp @@ -25,6 +25,7 @@ Game::Game(const char *dataPath, const char *savePath, uint32_t cheats) _rnd.setSeed(); _video = new Video(); _cheats = cheats; + _playDemo = false; _frameMs = kFrameTimeStamp; _difficulty = 1; @@ -144,7 +145,7 @@ void Game::fadeScreenPalette() { } _video->_displayPaletteBuffer[i] = color; } - _video->_paletteNeedRefresh = true; + _video->_paletteChanged = true; } void Game::shakeScreen() { @@ -196,7 +197,7 @@ void Game::transformShadowLayer(int delta) { const int offset = x + *src++; *dst++ = _video->_frontLayer[y * Video::W + offset]; } - memset(dst, 0xC4, 6); + memset(dst, Video::CLEAR_COLOR, 6); dst += 6; src += 6; } else { @@ -244,7 +245,7 @@ void Game::unloadTransformLayerData() { void Game::decodeShadowScreenMask(LvlBackgroundData *lvl) { uint8_t *dst = _video->_shadowScreenMaskBuffer; for (int i = lvl->currentShadowId; i < lvl->shadowCount; ++i) { - uint8_t *src = lvl->backgroundMaskTable[i]; + const uint8_t *src = lvl->backgroundMaskTable[i]; if (src) { const int decodedSize = decodeLZW(src + 2, dst); @@ -264,12 +265,12 @@ void Game::decodeShadowScreenMask(LvlBackgroundData *lvl) { debug(kDebug_GAME, "shadow screen mask #%d pos %d,%d dim %d,%d size %d", i, x, y, w, h, decodedSize); const int size = w * h; - src = _shadowScreenMasksTable[i].projectionDataPtr + 2; + uint8_t *p = _shadowScreenMasksTable[i].projectionDataPtr + 2; for (int j = 1; j < size; ++j) { - const int16_t offset = (int16_t)READ_LE_UINT16(src - 2) + (int16_t)READ_LE_UINT16(src); - // fprintf(stdout, "shadow #%d offset #%d 0x%x 0x%x\n", i, j, READ_LE_UINT16(src), offset); - WRITE_LE_UINT16(src, offset); - src += 2; + const int16_t offset = (int16_t)READ_LE_UINT16(p - 2) + (int16_t)READ_LE_UINT16(p); + // fprintf(stdout, "shadow #%d offset #%d 0x%x 0x%x\n", i, j, READ_LE_UINT16(p), offset); + WRITE_LE_UINT16(p, offset); + p += 2; } const int shadowPaletteSize = decodedSize - 20 - w * h * sizeof(uint16_t); @@ -327,7 +328,14 @@ void Game::setupBackgroundBitmap() { for (int i = 0; i < 256 * 3; ++i) { _video->_displayPaletteBuffer[i] = pal[i] << 8; } - _video->_paletteNeedRefresh = true; + _video->_paletteChanged = true; +} + +void Game::addToSpriteList(Sprite *spr) { + _spritesNextPtr = spr->nextPtr; + const int index = spr->num & 0x1F; + spr->nextPtr = _typeSpritesList[index]; + _typeSpritesList[index] = spr; } void Game::addToSpriteList(LvlObject *ptr) { @@ -370,10 +378,7 @@ void Game::addToSpriteList(LvlObject *ptr) { spr->w = ptr->width; spr->h = ptr->height; spr->bitmapBits = ptr->bitmapBits; - _spritesNextPtr = spr->nextPtr; - index = (ptr->flags2 & 31); - spr->nextPtr = _typeSpritesList[index]; - _typeSpritesList[index] = spr; + addToSpriteList(spr); } } } @@ -464,15 +469,6 @@ void Game::setupScreenMask(uint8_t num) { memcpy(p, _screenTempMaskBuffer + i * 32, 32); p += 512; } - if (0) { - fprintf(stdout, "screen %d mask %d\n", num, mask); - for (int y = 0; y < 24; ++y) { - for (int x = 0; x < 32; ++x) { - fprintf(stdout, "%02d ", _screenTempMaskBuffer[y * 32 + x]); - } - fputc('\n', stdout); - } - } } if (_res->_currentScreenResourceNum == num) { setupScreenPosTable(num); @@ -930,23 +926,6 @@ void Game::preloadLevelScreenData(uint8_t num, uint8_t prev) { } } } - if (0) { - const uint8_t leftScreen = _res->_screensGrid[num][kPosLeftScreen]; - if (leftScreen != kNoScreen && !_res->isLvlBackgroundDataLoaded(leftScreen)) { - _res->loadLvlScreenBackgroundData(leftScreen); - } - const uint8_t rightScreen = _res->_screensGrid[num][kPosRightScreen]; - if (rightScreen != kNoScreen && !_res->isLvlBackgroundDataLoaded(rightScreen)) { - _res->loadLvlScreenBackgroundData(rightScreen); - } - for (unsigned int i = 0; i < kMaxScreens; ++i) { - if (_res->_resLevelData0x2B88SizeTable[i] != 0) { - if (i != num && i != leftScreen && i != rightScreen) { - _res->unloadLvlScreenBackgroundData(i); - } - } - } - } } void Game::setLvlObjectPosRelativeToObject(LvlObject *ptr1, int num1, LvlObject *ptr2, int num2) { @@ -1294,7 +1273,7 @@ void Game::setupAndyLvlObject() { } } -void Game::updateScreenHelper(int num) { +void Game::setupScreenLvlObjects(int num) { _res->_screensState[num].s2 = 1; for (LvlObject *ptr = _screenLvlObjectsList[num]; ptr; ptr = ptr->nextPtr) { switch (ptr->type) { @@ -1353,7 +1332,7 @@ void Game::updateScreenHelper(int num) { ptr->callbackFuncPtr = &Game::objectUpdate_rock_case4; break; default: - warning("updateScreenHelper unimplemented for level %d, state %d", _currentLevel, ptr->objectUpdateType); + warning("setupScreenLvlObjects unimplemented for level %d, state %d", _currentLevel, ptr->objectUpdateType); break; } } else { @@ -1366,7 +1345,7 @@ void Game::updateScreenHelper(int num) { ptr->callbackFuncPtr = &Game::objectUpdate_rock_case3; break; default: - warning("updateScreenHelper unimplemented for level %d, state %d", _currentLevel, ptr->objectUpdateType); + warning("setupScreenLvlObjects unimplemented for level %d, state %d", _currentLevel, ptr->objectUpdateType); break; } } @@ -1386,7 +1365,7 @@ void Game::resetDisplay() { _snd_masterVolume = kDefaultSoundVolume; // _plyConfigTable[_plyConfigNumber].soundVolume; } -void Game::updateScreen(uint8_t num) { +void Game::setupScreen(uint8_t num) { uint8_t i, prev; if (num == kNoScreen) { @@ -1394,10 +1373,10 @@ void Game::updateScreen(uint8_t num) { } prev = _res->_currentScreenResourceNum; _res->_currentScreenResourceNum = num; - updateScreenHelper(num); + setupScreenLvlObjects(num); callLevel_preScreenUpdate(num); if (_res->_screensState[num].s0 >= _res->_screensState[num].s1) { - --_res->_screensState[num].s1; + _res->_screensState[num].s0 = _res->_screensState[num].s1 - 1; } callLevel_postScreenUpdate(num); i = _res->_screensGrid[num][kPosTopScreen]; @@ -1408,7 +1387,7 @@ void Game::updateScreen(uint8_t num) { } i = _res->_screensGrid[num][kPosRightScreen]; if (i != kNoScreen && _res->_resLevelData0x2B88SizeTable[i] != 0 && prev != i) { - updateScreenHelper(i); + setupScreenLvlObjects(i); callLevel_preScreenUpdate(i); setupScreenMask(i); callLevel_postScreenUpdate(i); @@ -1421,7 +1400,7 @@ void Game::updateScreen(uint8_t num) { } i = _res->_screensGrid[num][kPosLeftScreen]; if (i != kNoScreen && _res->_resLevelData0x2B88SizeTable[i] != 0 && prev != i) { - updateScreenHelper(i); + setupScreenLvlObjects(i); callLevel_preScreenUpdate(i); setupScreenMask(i); callLevel_postScreenUpdate(i); @@ -1472,7 +1451,7 @@ void Game::restartLevel() { if (_andyObject->screenNum != screenNum) { preloadLevelScreenData(_andyObject->screenNum, kNoScreen); } - updateScreen(_andyObject->screenNum); + setupScreen(_andyObject->screenNum); } void Game::playAndyFallingCutscene(int type) { @@ -1991,18 +1970,16 @@ void Game::drawPlasmaCannon() { int y1 = _plasmaCannonPosY[index]; index += 4; do { - _video->_drawLine.color = 0xA9; int x2 = _plasmaCannonXPointsTable1[index]; int y2 = _plasmaCannonYPointsTable1[index]; if (_plasmaCannonDirection == 1) { - _video->drawLine(x1 - 1, y1, x2 - 1, y2); - _video->drawLine(x1 + 1, y1, x2 + 1, y2); + _video->drawLine(x1 - 1, y1, x2 - 1, y2, 0xA9); + _video->drawLine(x1 + 1, y1, x2 + 1, y2, 0xA9); } else { - _video->drawLine(x1, y1 - 1, x2, y2 - 1); - _video->drawLine(x1, y1 + 1, x2, y2 + 1); + _video->drawLine(x1, y1 - 1, x2, y2 - 1, 0xA9); + _video->drawLine(x1, y1 + 1, x2, y2 + 1, 0xA9); } - _video->_drawLine.color = 0xA6; - _video->drawLine(x1, y1, x2, y2); + _video->drawLine(x1, y1, x2, y2, 0xA6); x1 = x2; y1 = y2; index += 4; @@ -2018,9 +1995,17 @@ void Game::drawScreen() { // redraw background animation sprites LvlBackgroundData *dat = &_res->_resLvlScreenBackgroundDataTable[_res->_currentScreenResourceNum]; - for (Sprite *spr = _typeSpritesList[0]; spr; spr = spr->nextPtr) { - if ((spr->num & 0x1F) == 0) { - _video->decodeSPR(spr->bitmapBits, _video->_backgroundLayer, spr->xPos, spr->yPos, 0, spr->w, spr->h); + if (_res->_isPsx) { + for (Sprite *spr = _typeSpritesList[0]; spr; spr = spr->nextPtr) { + assert((spr->num & 0x1F) == 0); + assert(spr->w == 0xFFFF && spr->h == 0xFFFF); + _video->decodeBackgroundOverlayPsx(spr->bitmapBits); + } + } else { + for (Sprite *spr = _typeSpritesList[0]; spr; spr = spr->nextPtr) { + if ((spr->num & 0x1F) == 0) { + _video->decodeSPR(spr->bitmapBits, _video->_backgroundLayer, spr->xPos, spr->yPos, 0, spr->w, spr->h); + } } } memset(_video->_shadowLayer, 0, Video::W * Video::H + 1); @@ -2091,10 +2076,13 @@ void Game::drawScreen() { } void Game::mainLoop(int level, int checkpoint, bool levelChanged) { - if (_loadingScreenEnabled) { - displayLoadingScreen(); - } - if (_resumeGame) { + if (_playDemo && _res->loadHodDem()) { + _rnd._rndSeed = _res->_dem.randSeed; + level = _res->_dem.level; + checkpoint = _res->_dem.checkpoint; + _difficulty = _res->_dem.difficulty; + _res->_demOffset = 0; + } else if (_resumeGame) { const int num = _setupConfig.currentPlayer; level = _setupConfig.players[num].levelNum; if (level > kLvl_dark) { @@ -2120,7 +2108,8 @@ void Game::mainLoop(int level, int checkpoint, bool levelChanged) { clearSoundObjects(); _mix._lock(0); _mstAndyCurrentScreenNum = -1; - _rnd.initTable(); + const int rounds = _playDemo ? _res->_dem.randRounds : ((g_system->getTimeStamp() & 15) + 1); + _rnd.initTable(rounds); const int screenNum = _level->getCheckpointData(checkpoint)->screenNum; if (_mstDisabled) { _specialAnimMask = 0; @@ -2257,22 +2246,21 @@ LvlObject *Game::updateAnimatedLvlObjectType0(LvlObject *ptr) { playSound(ptr->currentSound, ptr, 0, 3); ptr->currentSound = 0xFFFF; } - if (isPsx) { - _video->decodeBackgroundOverlayPsx(data); - } else { - Sprite *spr = _spritesNextPtr; - if (spr && READ_LE_UINT16(data + 2) > 8) { + Sprite *spr = _spritesNextPtr; + if (spr && READ_LE_UINT16(data + 2) > 8) { + if (isPsx) { + assert((ptr->flags2 & 0x1F) == 0); + spr->bitmapBits = data; + spr->w = spr->h = 0xFFFF; + } else { spr->xPos = data[0]; spr->yPos = data[1]; spr->w = READ_LE_UINT16(data + 4); spr->h = READ_LE_UINT16(data + 6); spr->bitmapBits = data + 8; - spr->num = ptr->flags2; - const int index = spr->num & 0x1F; - _spritesNextPtr = spr->nextPtr; - spr->nextPtr = _typeSpritesList[index]; - _typeSpritesList[index] = spr; } + spr->num = ptr->flags2; + addToSpriteList(spr); } } int16_t soundNum = -1; @@ -2324,22 +2312,21 @@ LvlObject *Game::updateAnimatedLvlObjectType0(LvlObject *ptr) { } data = vg->currentSpriteData + soundDataLen; if (_res->_currentScreenResourceNum == ptr->screenNum) { - if (isPsx) { - _video->decodeBackgroundOverlayPsx(data); - } else { - Sprite *spr = _spritesNextPtr; - if (spr && READ_LE_UINT16(data + 2) > 8) { + Sprite *spr = _spritesNextPtr; + if (spr && READ_LE_UINT16(data + 2) > 8) { + if (isPsx) { + assert((ptr->flags2 & 0x1F) == 0); + spr->bitmapBits = data; + spr->w = spr->h = 0xFFFF; + } else { spr->w = READ_LE_UINT16(data + 4); spr->h = READ_LE_UINT16(data + 6); spr->bitmapBits = data + 8; spr->xPos = data[0]; spr->yPos = data[1]; - _spritesNextPtr = spr->nextPtr; - spr->num = ptr->flags2; - const int index = spr->num & 0x1F; - spr->nextPtr = _typeSpritesList[index]; - _typeSpritesList[index] = spr; } + spr->num = ptr->flags2; + addToSpriteList(spr); } } ptr->objectUpdateType = 1; @@ -2387,7 +2374,7 @@ LvlObject *Game::updateAnimatedLvlObjectType1(LvlObject *ptr) { playSound(ptr->currentSound, 0, 0, 3); ptr->currentSound = 0xFFFF; } - uint8_t *data = (uint8_t *)getLvlObjectDataPtr(ptr, kObjectDataTypeLvlBackgroundSound); + const uint8_t *data = (const uint8_t *)getLvlObjectDataPtr(ptr, kObjectDataTypeLvlBackgroundSound); Sprite *spr = _spritesNextPtr; if (spr && READ_LE_UINT16(data + 2) > 8) { spr->w = READ_LE_UINT16(data + 4); @@ -2395,11 +2382,8 @@ LvlObject *Game::updateAnimatedLvlObjectType1(LvlObject *ptr) { spr->bitmapBits = data + 8; spr->xPos = data[0]; spr->yPos = data[1]; - _spritesNextPtr = spr->nextPtr; spr->num = ptr->flags2; - const int index = spr->num & 0x1F; - spr->nextPtr = _typeSpritesList[index]; - _typeSpritesList[index] = spr; + addToSpriteList(spr); } } } @@ -2435,27 +2419,24 @@ LvlObject *Game::updateAnimatedLvlObjectType2(LvlObject *ptr) { return o; } if (_currentScreen == ptr->screenNum) { - const uint8_t *vf = ptr->bitmapBits; + const uint8_t *bitmap = ptr->bitmapBits; LvlObjectData *dat = ptr->levelData0x2988; LvlAnimHeader *ah = (LvlAnimHeader *)(dat->animsInfoData + kLvlAnimHdrOffset) + ptr->anim; LvlAnimSeqHeader *ash = (LvlAnimSeqHeader *)(dat->animsInfoData + ah->seqOffset) + ptr->frame; - int vd = (ptr->flags1 >> 4) & 0xFF; - int vc = (ash->flags1 >> 4) & 0xFF; - vc = (((vc ^ vd) & 3) << 14) | ptr->flags2; + const int f1 = (ptr->flags1 >> 4) & 3; + const int f2 = (ash->flags1 >> 4) & 3; + const int num = ((f1 ^ f2) << 14) | ptr->flags2; Sprite *spr = _spritesNextPtr; - if (spr && vf) { + if (spr && bitmap) { spr->yPos = ptr->yPos; spr->xPos = ptr->xPos; spr->w = ptr->width; spr->h = ptr->height; - spr->bitmapBits = vf; - spr->num = vc; - const int index = spr->num & 0x1F; - _spritesNextPtr = spr->nextPtr; - spr->nextPtr = _typeSpritesList[index]; - _typeSpritesList[index] = spr; + spr->bitmapBits = bitmap; + spr->num = num; + addToSpriteList(spr); } } if (ptr->spriteNum <= 15 || ptr->dataPtr == 0) { @@ -2702,12 +2683,18 @@ void Game::levelMainLoop() { _directionKeyMask = 0; _actionKeyMask = 0; updateInput(); - _andyObject->directionKeyMask = _directionKeyMask; - _andyObject->actionKeyMask = _actionKeyMask; - _video->fillBackBuffer(); + if (_playDemo && _res->_demOffset < _res->_dem.keyMaskLen) { + _andyObject->actionKeyMask = _res->_dem.actionKeyMask[_res->_demOffset]; + _andyObject->directionKeyMask = _res->_dem.directionKeyMask[_res->_demOffset]; + ++_res->_demOffset; + } else { + _andyObject->directionKeyMask = _directionKeyMask; + _andyObject->actionKeyMask = _actionKeyMask; + } + _video->clearBackBuffer(); if (_andyObject->screenNum != _res->_currentScreenResourceNum) { preloadLevelScreenData(_andyObject->screenNum, _res->_currentScreenResourceNum); - updateScreen(_andyObject->screenNum); + setupScreen(_andyObject->screenNum); } else if (_fadePalette && _levelRestartCounter == 0) { restartLevel(); } else { @@ -2743,9 +2730,9 @@ void Game::levelMainLoop() { if (_res->_sssHdr.infosDataCount != 0) { // sound thread signaling } - if (_video->_paletteNeedRefresh) { - _video->_paletteNeedRefresh = false; - _video->refreshGamePalette(_video->_displayPaletteBuffer); + if (_video->_paletteChanged) { + _video->_paletteChanged = false; + _video->updateGamePalette(_video->_displayPaletteBuffer); g_system->copyRectWidescreen(Video::W, Video::H, _video->_backgroundLayer, _video->_palette); } drawScreen(); @@ -2838,7 +2825,7 @@ void Game::callLevel_terminate() { } void Game::displayLoadingScreen() { - if (_res->loadDatLoadingImage(_video->_frontLayer, _video->_palette)) { + if (_loadingScreenEnabled && _res->loadDatLoadingImage(_video->_frontLayer, _video->_palette)) { g_system->setPalette(_video->_palette, 256, 6); g_system->copyRect(0, 0, Video::W, Video::H, _video->_frontLayer, 256); g_system->updateScreen(false); @@ -2881,7 +2868,7 @@ int Game::displayHintScreen(int num, int pause) { } g_system->sleep(30); } while (!g_system->inp.quit && !g_system->inp.keyReleased(SYS_INP_JUMP)); - _video->_paletteNeedRefresh = true; + _video->_paletteChanged = true; } unmuteSound(); return confirmQuit && quit == kQuitYes; @@ -4754,7 +4741,7 @@ void Game::updateWormHoleSprites() { int xOffset = 0; for (int j = 0; j < 11; ++j) { uint8_t _al = (*flags >> (j * 2)) & 3; - if (_al != 0 && _spritesNextPtr != 0) { + if (_al != 0 && _spritesNextPtr) { const int xPos = spr->xPos + xOffset + 12; const int yPos = spr->yPos + yOffset + 16; if (rect_contains(spr->rect1_x1, spr->rect1_y1, spr->rect1_x2, spr->rect1_y2, xPos, yPos)) { @@ -4776,10 +4763,7 @@ void Game::updateWormHoleSprites() { spr->h = tmp.height; spr->bitmapBits = tmp.bitmapBits; spr->num = tmp.flags2 & 0x3FFF; - const int index = spr->num & 0x1F; - _spritesNextPtr = spr->nextPtr; - spr->nextPtr = _typeSpritesList[index]; - _typeSpritesList[index] = spr; + addToSpriteList(spr); } } xOffset += 24; @@ -4809,8 +4793,8 @@ void Game::saveSetupCfg() { _setupConfig.players[num].cutscenesMask = _paf->_playedMask; _setupConfig.players[num].difficulty = _difficulty; _setupConfig.players[num].volume = _snd_masterVolume; - if (_currentLevel > _setupConfig.players[num].currentLevel) { - _setupConfig.players[num].currentLevel = _currentLevel; + if (_currentLevel > _setupConfig.players[num].lastLevelNum) { + _setupConfig.players[num].lastLevelNum = _currentLevel; } _res->writeSetupCfg(&_setupConfig); } @@ -4823,8 +4807,57 @@ void Game::captureScreenshot() { FILE *fp = _fs.openSaveFile(name, true); if (fp) { saveBMP(fp, _video->_frontLayer, _video->_palette, Video::W, Video::H); - fclose(fp); + _fs.closeFile(fp); + } + if (_cheats != 0) { + snprintf(name, sizeof(name), "screenshot-%03d-background.bmp", screenshot); + fp = _fs.openSaveFile(name, true); + if (fp) { + saveBMP(fp, _video->_backgroundLayer, _video->_palette, Video::W, Video::H); + _fs.closeFile(fp); + } + snprintf(name, sizeof(name), "screenshot-%03d-shadow.bmp", screenshot); + fp = _fs.openSaveFile(name, true); + if (fp) { + saveBMP(fp, _video->_shadowLayer, _video->_palette, Video::W, Video::H); + _fs.closeFile(fp); + } + snprintf(name, sizeof(name), "screenshot-%03d-palette.bmp", screenshot); + fp = _fs.openSaveFile(name, true); + if (fp) { + static const int kPaletteRectSize = 8; + uint8_t paletteBuffer[8 * 256 * 8]; + for (int x = 0; x < 256; ++x) { + const int xOffset = x * kPaletteRectSize; + for (int y = 0; y < kPaletteRectSize; ++y) { + memset(paletteBuffer + xOffset + y * 256 * kPaletteRectSize, x, kPaletteRectSize); + } + } + saveBMP(fp, paletteBuffer, _video->_palette, 256 * kPaletteRectSize, kPaletteRectSize); + _fs.closeFile(fp); + } + snprintf(name, sizeof(name), "screenshot-%03d-postable.txt", screenshot); + fp = _fs.openSaveFile(name, true); + if (fp) { + for (int y = 0; y < 24; ++y) { + for (int x = 0; x < 32; ++x) { + fprintf(fp, "%02x ", _screenPosTable[4][y * 32 + x]); + } + fputc('\n', fp); + } + _fs.closeFile(fp); + } + snprintf(name, sizeof(name), "screenshot-%03d-mask.txt", screenshot); + fp = _fs.openSaveFile(name, true); + if (fp) { + for (int y = 0; y < 24; ++y) { + for (int x = 0; x < 32; ++x) { + fprintf(fp, "%02x ", _screenTempMaskBuffer[y * 32 + x]); + } + fputc('\n', fp); + } + _fs.closeFile(fp); + } } - ++screenshot; } diff --git a/game.h b/game.h index d1a3121..952c9f0 100644 --- a/game.h +++ b/game.h @@ -97,6 +97,7 @@ struct Game { bool _loadingScreenEnabled; SetupConfig _setupConfig; + bool _playDemo; bool _resumeGame; LvlObject *_screenLvlObjectsList[kMaxScreens]; // LvlObject linked list for each screen @@ -257,6 +258,7 @@ struct Game { void playSound(int num, LvlObject *ptr, int a, int b); void removeSound(LvlObject *ptr); void setupBackgroundBitmap(); + void addToSpriteList(Sprite *spr); void addToSpriteList(LvlObject *ptr); int16_t calcScreenMaskDy(int16_t xPos, int16_t yPos, int num); void setupScreenPosTable(uint8_t num); @@ -289,9 +291,9 @@ struct Game { void removeLvlObject2(LvlObject *o); void setAndySprite(int num); void setupAndyLvlObject(); - void updateScreenHelper(int num); + void setupScreenLvlObjects(int num); void resetDisplay(); - void updateScreen(uint8_t num); + void setupScreen(uint8_t num); void resetScreen(); void restartLevel(); void playAndyFallingCutscene(int type); @@ -376,7 +378,7 @@ struct Game { // level1_rock.cpp int objectUpdate_rock_case0(LvlObject *o); - void objectUpdate_rock_helper(LvlObject *ptr, uint8_t *p); + void objectUpdate_rockShadow(LvlObject *ptr, uint8_t *p); bool plasmaCannonHit(LvlObject *ptr); int objectUpdate_rock_case1(LvlObject *o); int objectUpdate_rock_case2(LvlObject *o); diff --git a/intern.h b/intern.h index 6615a49..f3d1c2f 100644 --- a/intern.h +++ b/intern.h @@ -12,16 +12,25 @@ #include #include -#ifdef _WIN32 +#if defined(_WIN32) || defined(PSP) #define le16toh(x) x #define le32toh(x) x #define htole16(x) x #define htole32(x) x static const bool kByteSwapData = false; // no byteswap needed on little endian #else +#if defined(WII) // big endian +#include +#define le16toh(x) __bswap16(x) +#define le32toh(x) __bswap32(x) +#define htole16(x) __bswap16(x) +#define htole32(x) __bswap32(x) +static const bool kByteSwapData = true; +#else #include static const bool kByteSwapData = (__BYTE_ORDER == __BIG_ENDIAN); #endif +#endif #define ARRAYSIZE(a) (sizeof(a)/sizeof(a[0])) #define PACKED __attribute__((packed)) @@ -91,9 +100,14 @@ inline void SWAP(T &a, T &b) { T tmp = a; a = b; b = tmp; } -inline int merge_bits(int dbit, int sbit, int mask) { - return ((sbit ^ dbit) & mask) ^ dbit; -// return (dbit & ~mask) | (sbit & mask); +inline uint32_t merge_bits(uint32_t dst, uint32_t src, uint32_t mask) { +// return (dst & ~mask) | (src & mask); + return ((src ^ dst) & mask) ^ dst; +} + +inline bool compare_bits(uint32_t a, uint32_t b, uint32_t mask) { +// return (a & mask) == (b & mask); + return ((a ^ b) & mask) == 0; } inline bool rect_contains(int left, int top, int right, int bottom, int x, int y) { diff --git a/level1_rock.cpp b/level1_rock.cpp index e4e1a44..7782c3d 100644 --- a/level1_rock.cpp +++ b/level1_rock.cpp @@ -162,7 +162,7 @@ void Level_rock::postScreenUpdate_rock_screen9() { if (!_paf->_skipCutscenes) { _paf->play(1); _res->_resLvlScreenBackgroundDataTable[9].currentBackgroundId = 1; - _video->_paletteNeedRefresh = true; + _video->_paletteChanged = true; } if (_checkpoint == 4) { _checkpoint = 5; @@ -174,7 +174,7 @@ void Level_rock::postScreenUpdate_rock_screen9() { _andyObject->anim = 232; _andyObject->frame = 0; _g->setupLvlObjectBitmap(_andyObject); - _g->updateScreen(_andyObject->screenNum); + _g->setupScreen(_andyObject->screenNum); } break; case 1: @@ -257,6 +257,7 @@ void Level_rock::postScreenUpdate_rock_screen16() { } void Level_rock::postScreenUpdate_rock_screen18() { + LvlObject *o; switch (_res->_screensState[18].s0) { case 0: if (_andyObject->yPos + _andyObject->height < 162) { @@ -273,16 +274,19 @@ void Level_rock::postScreenUpdate_rock_screen18() { } break; case 2: + o = _g->findLvlObject(2, 0, 18); ++_screenCounterTable[18]; - if (_screenCounterTable[18] == 29) { - LvlObject *o = _g->findLvlObject(2, 0, 18); + if (_res->_version >= Resource::V1_2) { o->actionKeyMask = 1; - } else if (_screenCounterTable[18] == 43) { + } else if (_screenCounterTable[18] == 29) { + o->actionKeyMask = 1; + break; + } + if (_screenCounterTable[18] == 43) { _g->setShakeScreen(2, 5); _res->_resLvlScreenBackgroundDataTable[18].currentMaskId = 1; _g->setupScreenMask(18); - } else if (_screenCounterTable[18] < 57) { - LvlObject *o = _g->findLvlObject(2, 0, 18); + } else if (_screenCounterTable[18] < (_res->_version >= Resource::V1_2 ? 51 : 57)) { if ((o->flags0 & 0x1F) != 11 || (_andyObject->flags0 & 0x1F) == 11) { break; } @@ -334,7 +338,7 @@ void Level_rock::postScreenUpdate_rock_screen19() { if (!_paf->_skipCutscenes) { _paf->play(2); _paf->unload(2); - if (false /* _isDemo */ && !_paf->_skipCutscenes) { + if (_res->_isDemo && !_paf->_skipCutscenes) { _paf->play(21); } } @@ -419,9 +423,9 @@ static const uint8_t _level1OpHelper1KeyMaskTable[112] = { 8, 4, 8, 4, 8, 4, 8, 4, 8, 4, 8, 0, 8, 0, 4, 0 }; -void Game::objectUpdate_rock_helper(LvlObject *ptr, uint8_t *p) { +void Game::objectUpdate_rockShadow(LvlObject *ptr, uint8_t *p) { const bool sameScreen = (_andyObject->screenNum == ptr->screenNum); - int i = (_andyObject->width / 2 + _andyObject->xPos + (_andyObject->xPos & 7)) / 8; + int i = (_andyObject->width / 2 + _andyObject->xPos) / 8; if (i < 0 || ptr->screenNum != _res->_currentScreenResourceNum) { i = 0; } else if (i > 31) { @@ -451,7 +455,7 @@ void Game::objectUpdate_rock_helper(LvlObject *ptr, uint8_t *p) { } } if ((ptr->directionKeyMask & 4) != 0) { - if (ptr->anim != 1 || ptr->frame != p[64]) { + if (o->anim != 1 || o->frame != p[64]) { ptr->directionKeyMask &= ~4; } } @@ -478,9 +482,9 @@ bool Game::plasmaCannonHit(LvlObject *ptr) { if (ptr->bitmapBits) { int dx = 0; if (ptr->screenNum == _currentLeftScreen) { - dx = -256; + dx = -Video::W; } else if (ptr->screenNum == _currentRightScreen) { - dx = 256; + dx = Video::W; } else if (ptr->screenNum != _currentScreen) { return false; } @@ -503,7 +507,7 @@ int Game::objectUpdate_rock_case1(LvlObject *o) { 0x0C, 0x07, 0x00, 0x00 }; if (_level->_screenCounterTable[2] == 0) { - objectUpdate_rock_helper(o, data); + objectUpdate_rockShadow(o, data); if ((o->flags0 & 0x3FF) == 0x4B) { _level->_screenCounterTable[2] = 1; } @@ -522,7 +526,7 @@ int Game::objectUpdate_rock_case2(LvlObject *o) { 0x06, 0x0B, 0x00, 0x00 }; if (_level->_screenCounterTable[3] == 0) { - objectUpdate_rock_helper(o, data); + objectUpdate_rockShadow(o, data); if ((o->flags0 & 0x3FF) == 0x4B) { _level->_screenCounterTable[3] = 1; } @@ -619,7 +623,12 @@ void Level_rock::preScreenUpdate_rock_screen4() { break; } _res->_resLvlScreenBackgroundDataTable[4].currentBackgroundId = num; - _res->_resLvlScreenBackgroundDataTable[4].currentShadowId = num; + // bugfix: glitch when re-entering screen 4 (state 1) from screen 3. + // drawScreen() calls applyShadowColors() from 0 to shadowsCount. Setting + // currentShadowId to 1 will result in screen 3 shadow mask (index #0) to + // be applied where it shouldn't. Another fix would be iterating from + // currentShadowId to shadowsCount, similar to decodeShadowScreenMask. + // _res->_resLvlScreenBackgroundDataTable[4].currentShadowId = num; _res->_resLvlScreenBackgroundDataTable[4].currentMaskId = num; if (_res->_currentScreenResourceNum == 4 && _checkpoint == 1) { _checkpoint = 2; diff --git a/level2_fort.cpp b/level2_fort.cpp index f15a6f0..cf3310e 100644 --- a/level2_fort.cpp +++ b/level2_fort.cpp @@ -150,7 +150,7 @@ void Level_fort::postScreenUpdate_fort_screen8() { void Level_fort::postScreenUpdate_fort_screen16() { if (_res->_currentScreenResourceNum == 16) { - if (_res->_screensState[16].s0 == 1) { + if (_res->_screensState[16].s0 != 1) { AndyLvlObjectData *data = (AndyLvlObjectData *)_g->getLvlObjectDataPtr(_andyObject, kObjectDataTypeAndy); BoundingBox b = { 150, 0, 187, 60 }; if (!_g->clipBoundingBox(&b, &data->boundingBox)) { @@ -183,7 +183,7 @@ void Level_fort::postScreenUpdate_fort_screen17() { if (_res->_screensState[17].s0 == 1) { ++_screenCounterTable[17]; if (_screenCounterTable[17] == 68) { - _screenCounterTable[17] = 0; + _res->_screensState[17].s0 = 0; } } } diff --git a/level3_pwr1.cpp b/level3_pwr1.cpp index 5726005..52d718a 100644 --- a/level3_pwr1.cpp +++ b/level3_pwr1.cpp @@ -394,7 +394,7 @@ void Level_pwr1::preScreenUpdate_pwr1_screen15() { } void Level_pwr1::preScreenUpdate_pwr1_screen21() { - if (_res->_currentScreenResourceNum == 15) { + if (_res->_currentScreenResourceNum == 21) { if (_checkpoint == 4) { _checkpoint = 5; } diff --git a/level5_lava.cpp b/level5_lava.cpp index 9565fbf..a7f8079 100644 --- a/level5_lava.cpp +++ b/level5_lava.cpp @@ -396,7 +396,7 @@ void Level_lava::postScreenUpdate_lava_screen10() { _paf->play(7); _paf->unload(7); _video->clearPalette(); - _g->updateScreen(_andyObject->screenNum); + _g->setupScreen(_andyObject->screenNum); } } } diff --git a/level7_lar1.cpp b/level7_lar1.cpp index 26d5556..c8db812 100644 --- a/level7_lar1.cpp +++ b/level7_lar1.cpp @@ -367,7 +367,7 @@ void Level_lar1::postScreenUpdate_lar1_screen14() { _paf->unload(12); } _video->clearPalette(); - _g->updateScreen(_andyObject->screenNum); + _g->setupScreen(_andyObject->screenNum); } break; } @@ -416,7 +416,7 @@ void Level_lar1::postScreenUpdate_lar1_screen19() { } _video->clearPalette(); ++_screenCounterTable[19]; // bugfix: conditioned with _pafSkipCutscenes - _g->updateScreen(_andyObject->screenNum); + _g->setupScreen(_andyObject->screenNum); Game::_lar1_maskData[12 * 6 + 1] = 0; Game::_lar1_maskData[13 * 6 + 1] = 0; _andyObject->xPos = 204; diff --git a/level8_lar2.cpp b/level8_lar2.cpp index 74228de..2ce5354 100644 --- a/level8_lar2.cpp +++ b/level8_lar2.cpp @@ -190,7 +190,7 @@ void Level_lar2::postScreenUpdate_lar2_screen4() { _video->clearPalette(); } _g->_currentLevelCheckpoint = _checkpoint; // bugfix: conditioned with _pafSkipCutscenes - _g->updateScreen(_andyObject->screenNum); + _g->setupScreen(_andyObject->screenNum); } LvlObject *o = _g->findLvlObject(2, 0, 4); _g->updateGatesLar(o, _lar2_gatesData + 8, 2); @@ -214,7 +214,7 @@ void Level_lar2::postScreenUpdate_lar2_screen6() { _video->clearPalette(); } _g->_currentLevelCheckpoint = _checkpoint; // bugfix: conditioned with _pafSkipCutscenes - _g->updateScreen(_andyObject->screenNum); + _g->setupScreen(_andyObject->screenNum); } } else if (_checkpoint == 3) { if (_res->_screensState[6].s0 != 0) { @@ -224,7 +224,7 @@ void Level_lar2::postScreenUpdate_lar2_screen6() { _video->clearPalette(); } _res->_screensState[6].s0 = 0; // bugfix: conditioned with _pafSkipCutscenes - _g->updateScreen(_andyObject->screenNum); + _g->setupScreen(_andyObject->screenNum); } } } @@ -239,7 +239,7 @@ void Level_lar2::postScreenUpdate_lar2_screen7() { _video->clearPalette(); } _res->_screensState[7].s0 = 1; // bugfix: conditioned with _pafSkipCutscenes - _g->updateScreen(_andyObject->screenNum); + _g->setupScreen(_andyObject->screenNum); } } } @@ -306,7 +306,6 @@ void Level_lar2::postScreenUpdate_lar2_screen12() { o->objectUpdateType = 7; } } - } } } diff --git a/level9_dark.cpp b/level9_dark.cpp index 7201652..46f5df4 100644 --- a/level9_dark.cpp +++ b/level9_dark.cpp @@ -46,7 +46,7 @@ void Level_dark::postScreenUpdate_dark_screen0() { _paf->unload(21); } _video->clearPalette(); - _video->fillBackBuffer(); + _video->clearBackBuffer(); _g->_endLevel = true; } } diff --git a/main.cpp b/main.cpp index fea8fec..014fe70 100644 --- a/main.cpp +++ b/main.cpp @@ -3,7 +3,12 @@ * Copyright (C) 2009-2011 Gregory Montoir (cyx@users.sourceforge.net) */ +#if !defined(PSP) && !defined(WII) #include +#endif +#if defined(WII) +#include +#endif #include #include @@ -51,7 +56,6 @@ static void mixAudio(void *userdata, int16_t *buf, int len) { static void setupAudio(Game *g) { g->_mix._lock = lockAudio; - g->_mix.init(g_system->getOutputSampleRate()); AudioCallback cb; cb.proc = mixAudio; cb.userdata = g; @@ -129,6 +133,23 @@ int main(int argc, char *argv[]) { g_debugMask = 0; //kDebug_GAME | kDebug_RESOURCE | kDebug_SOUND | kDebug_MONSTER; int cheats = 0; +#ifdef WII + fatInitDefault(); + static const char *pathsWII[] = { + "sd:/hode", + "usb:/hode", + 0 + }; + for (int i = 0; pathsWII[i]; ++i) { + struct stat st; + if (stat(pathsWII[i], &st) == 0 && S_ISDIR(st.st_mode)) { + dataPath = strdup(pathsWII[i]); + savePath = strdup(pathsWII[i]); + break; + } + } +#else +#if !defined(PSP) if (argc == 2) { // data path as the only command line argument struct stat st; @@ -186,6 +207,8 @@ int main(int argc, char *argv[]) { return -1; } } +#endif +#endif Game *g = new Game(dataPath ? dataPath : _defaultDataPath, savePath ? savePath : _defaultSavePath, cheats); ini_parse(_configIni, handleConfigIni, g); if (_runBenchmark) { @@ -199,6 +222,7 @@ int main(int argc, char *argv[]) { g->loadSetupCfg(resume); bool runGame = true; g->_video->init(isPsx); + g->displayLoadingScreen(); if (_runMenu && resume && !isPsx) { Menu *m = new Menu(g, g->_paf, g->_res, g->_video); runGame = m->mainLoop(); @@ -207,21 +231,28 @@ int main(int argc, char *argv[]) { if (runGame && !g_system->inp.quit) { bool levelChanged = false; do { + g->displayLoadingScreen(); g->mainLoop(level, checkpoint, levelChanged); // do not save progress when game is started from a specific level/checkpoint if (resume) { g->saveSetupCfg(); } - level += 1; + if (g->_res->_isDemo) { + break; + } + level = g->_currentLevel + 1; checkpoint = 0; levelChanged = true; } while (!g_system->inp.quit && level < kLvl_test); } g_system->stopAudio(); - g->_mix.fini(); g_system->destroy(); delete g; free(dataPath); free(savePath); +#ifdef WII + fatUnmount("sd:/"); + fatUnmount("usb:/"); +#endif return 0; } diff --git a/mdec.cpp b/mdec.cpp index 2634032..5e8731a 100644 --- a/mdec.cpp +++ b/mdec.cpp @@ -149,7 +149,7 @@ static void idct(float *dequantData, float *result) { for (int i = 0; i < 8; ++i) { p += u[i] * _idct8x8[y][i]; } - result[y * 8 + x] = p; + result[y * 8 + x] = p; } } } diff --git a/menu.cpp b/menu.cpp index 2f15f65..5266b41 100644 --- a/menu.cpp +++ b/menu.cpp @@ -27,6 +27,33 @@ enum { kMenu_Cutscenes = 17 }; +enum { + kSound_0x60 = 0x60 / 8, + kSound_0x70 = 0x70 / 8, + kSound_0x78 = 0x78 / 8, + kSound_0x80 = 0x80 / 8, + kSound_0x88 = 0x88 / 8, + kSound_0x90 = 0x90 / 8, + kSound_0x98 = 0x98 / 8, + kSound_0xA0 = 0xA0 / 8 +}; + +enum { + kSettingNum_Controls = 0, + kSettingNum_Difficulty = 1, + kSettingNum_Sound = 2, + kSettingNum_Confirm = 3 +}; + +enum { + kSoundNum_Stereo = 0, + kSoundNum_Volume = 1, + kSoundNum_Confirm = 2, + kSoundNum_Test = 3, + kSoundNum_Cancel = 4, + kSoundNum_Reset = 5 +}; + static void setDefaultsSetupCfg(SetupConfig *config, int num) { assert(num >= 0 && num < 4); memset(config->players[num].progress, 0, 10); @@ -40,8 +67,8 @@ static void setDefaultsSetupCfg(SetupConfig *config, int num) { config->players[num].controls[0xC] = 0x48; config->players[num].difficulty = 1; config->players[num].stereo = 1; - config->players[num].volume = 128; - config->players[num].currentLevel = 0; + config->players[num].volume = Game::kDefaultSoundVolume; + config->players[num].lastLevelNum = 0; } static bool isEmptySetupCfg(SetupConfig *config, int num) { @@ -72,17 +99,28 @@ static uint32_t readBitmapsGroup(int count, DatBitmapsGroup *bitmapsGroup, uint3 int size; if (paletteSize == 0) { // PSX size = le32toh(bitmapsGroup[i].offset); - bitmapsGroup[i].offset = ptrOffset - baseOffset; } else { size = bitmapsGroup[i].w * bitmapsGroup[i].h; - bitmapsGroup[i].offset = ptrOffset - baseOffset; } + bitmapsGroup[i].offset = ptrOffset - baseOffset; bitmapsGroup[i].palette = bitmapsGroup[i].offset + size; ptrOffset += size + paletteSize; } return ptrOffset - baseOffset; } +static uint32_t readSoundData(uint8_t *soundData, uint32_t soundDataSize) { + const int soundListsCount = READ_LE_UINT32(soundData + 4); + const uint8_t *listData = soundData + 8 + soundListsCount * 8; + for (int i = 0; i < soundListsCount; ++i) { + WRITE_LE_UINT32(soundData + 8 + i * 8, listData - soundData); + const int count = READ_LE_UINT32(soundData + 8 + i * 8 + 4); + listData += count * sizeof(uint16_t); + } + assert((uint32_t)(listData - soundData) == soundDataSize); + return soundDataSize; +} + void Menu::loadData() { _g->_mix._lock(1); _res->loadDatMenuBuffers(); @@ -139,11 +177,11 @@ void Menu::loadData() { } _soundData = ptr + ptrOffset; + readSoundData(_res->_menuBuffer1 + ptrOffset, _res->_datHdr.soundDataSize); ptrOffset += _res->_datHdr.soundDataSize; } else if (version == 11) { - ptr = _res->_menuBuffer1; hdrOffset = 4; ptrOffset = 4 + (2 + kOptionsCount) * sizeof(DatBitmap); @@ -221,6 +259,7 @@ void Menu::loadData() { ptrOffset += size; _soundData = ptr + ptrOffset; + readSoundData(_res->_menuBuffer0 + ptrOffset, _res->_datHdr.soundDataSize); ptrOffset += _res->_datHdr.soundDataSize; } @@ -233,11 +272,10 @@ void Menu::loadData() { const int iconsCount = _res->_datHdr.iconsCount; ptrOffset += iconsCount * sizeof(DatSpritesGroup); _iconsSpritesData = ptr + ptrOffset; - const uint32_t baseOffset = ptrOffset; for (int i = 0; i < iconsCount; ++i) { _iconsSprites[i].size = le32toh(_iconsSprites[i].size); _iconsSprites[i].count = le16toh(_iconsSprites[i].count); - _iconsSprites[i].firstFrameOffset = ptrOffset - baseOffset; + _iconsSprites[i].firstFrameOffset = ptr + ptrOffset - _iconsSpritesData; ptrOffset += _iconsSprites[i].size; } @@ -246,12 +284,11 @@ void Menu::loadData() { _optionsButtonSpritesData = ptr + ptrOffset; hdrOffset = ptrOffset; ptrOffset += _optionsButtonSpritesCount * 20; - const uint32_t baseOffset = ptrOffset; for (int i = 0; i < _optionsButtonSpritesCount; ++i) { - WRITE_LE_UINT32(_res->_menuBuffer0 + hdrOffset, ptrOffset - baseOffset); DatSpritesGroup *spritesGroup = (DatSpritesGroup *)(ptr + hdrOffset + 4); spritesGroup->size = le32toh(spritesGroup->size); spritesGroup->count = le16toh(spritesGroup->count); + spritesGroup->firstFrameOffset = ptr + ptrOffset - _optionsButtonSpritesData; hdrOffset += 20; ptrOffset += spritesGroup->size; } @@ -286,51 +323,26 @@ void Menu::loadData() { } } -int Menu::getSoundNum(int num) const { - assert((num & 7) == 0); - num /= 8; - if (_soundData) { - const int count = READ_LE_UINT32(_soundData + 4); - const uint8_t *p = _soundData + 8 + count * 8; - for (int i = 0; i < count; ++i) { - const int count2 = READ_LE_UINT32(_soundData + 8 + i * 8 + 4); - if (i == num) { - assert(count2 != 0); - return (int16_t)READ_LE_UINT16(p); - } - p += count2 * 2; - } - // sound not found - assert((uint32_t)(p - _soundData) == _res->_datHdr.soundDataSize); +int Menu::getSoundNum(int num, int index) const { + const int soundListsCount = READ_LE_UINT32(_soundData + 4); + if (num < soundListsCount) { + const int count = READ_LE_UINT32(_soundData + 8 + num * 8 + 4); + assert(index < count); + const uint8_t *data = _soundData + READ_LE_UINT32(_soundData + 8 + num * 8); + return (int16_t)READ_LE_UINT16(data + index * 2); } return -1; } void Menu::playSound(int num) { num = getSoundNum(num); + debug(kDebug_MENU, "playSound %d", num); if (num != -1) { _g->playSound(num, 0, 0, 5); } } -void Menu::drawSprite(const DatSpritesGroup *spriteGroup, const uint8_t *ptr, uint32_t num) { - ptr += spriteGroup->firstFrameOffset; - for (uint32_t i = 0; i < spriteGroup->count; ++i) { - const uint16_t size = READ_LE_UINT16(ptr + 2); - if (num == i) { - if (_res->_isPsx) { - _video->decodeBackgroundOverlayPsx(ptr); - } else { - _video->decodeSPR(ptr + 8, _video->_frontLayer, ptr[0], ptr[1], 0, READ_LE_UINT16(ptr + 4), READ_LE_UINT16(ptr + 6)); - } - break; - } - ptr += size + 2; - } -} - -void Menu::drawSpritePos(const DatSpritesGroup *spriteGroup, const uint8_t *ptr, int x, int y, uint32_t num) { - assert(x != 0 || y != 0); +void Menu::drawSprite(const DatSpritesGroup *spriteGroup, const uint8_t *ptr, uint32_t num, int x, int y) { ptr += spriteGroup->firstFrameOffset; for (uint32_t i = 0; i < spriteGroup->count; ++i) { const uint16_t size = READ_LE_UINT16(ptr + 2); @@ -338,6 +350,12 @@ void Menu::drawSpritePos(const DatSpritesGroup *spriteGroup, const uint8_t *ptr, if (_res->_isPsx) { _video->decodeBackgroundOverlayPsx(ptr); } else { + if (x < 0) { + x = ptr[0]; + } + if (y < 0) { + y = ptr[1]; + } _video->decodeSPR(ptr + 8, _video->_frontLayer, x, y, 0, READ_LE_UINT16(ptr + 4), READ_LE_UINT16(ptr + 6)); } break; @@ -346,23 +364,22 @@ void Menu::drawSpritePos(const DatSpritesGroup *spriteGroup, const uint8_t *ptr, } } -void Menu::drawSpriteNextFrame(DatSpritesGroup *spriteGroup, int num, int x, int y) { - const uint8_t *ptr = _iconsSpritesData; +void Menu::drawSpriteAnim(DatSpritesGroup *spriteGroup, const uint8_t *ptr, uint32_t num) { if (spriteGroup[num].num == 0) { spriteGroup[num].currentFrameOffset = spriteGroup[num].firstFrameOffset; } ptr += spriteGroup[num].currentFrameOffset; if (_res->_isPsx) { - _video->decodeBackgroundOverlayPsx(ptr, x, y); + _video->decodeBackgroundOverlayPsx(ptr); } else { - _video->decodeSPR(ptr + 8, _video->_frontLayer, ptr[0] + x, ptr[1] + y, 0, READ_LE_UINT16(ptr + 4), READ_LE_UINT16(ptr + 6)); + _video->decodeSPR(ptr + 8, _video->_frontLayer, ptr[0], ptr[1], 0, READ_LE_UINT16(ptr + 4), READ_LE_UINT16(ptr + 6)); } ++spriteGroup[num].num; if (spriteGroup[num].num < spriteGroup[num].count) { const uint16_t size = READ_LE_UINT16(ptr + 2); spriteGroup[num].currentFrameOffset += size + 2; } else { - spriteGroup[num].num = 0; + spriteGroup[num].num = 0; // restart from frame #0 spriteGroup[num].currentFrameOffset = spriteGroup[num].firstFrameOffset; } } @@ -375,6 +392,24 @@ void Menu::refreshScreen(bool updatePalette) { g_system->updateScreen(false); } +void Menu::pafCallback(int frameNum, const uint8_t *frameData) { + if (_currentOptionButtonSound != 0) { + const int num = getSoundNum(_currentOptionButtonSound, frameNum); + if (num != -1) { + _g->playSound(num, 0, 0, 5); + } + } + if (_currentOptionButtonSprite && frameNum == _currentOptionButtonSprite->num) { + memcpy(_video->_frontLayer, frameData, Video::W * Video::H); + drawSpriteAnim(_currentOptionButtonSprite, _optionsButtonSpritesData, 0); + g_system->copyRect(0, 0, Video::W, Video::H, _video->_frontLayer, Video::W); + } +} + +static void menuPafCallback(void *userdata, int frame, const uint8_t *buffer) { + ((Menu *)userdata)->pafCallback(frame, buffer); +} + bool Menu::mainLoop() { bool ret = false; loadData(); @@ -386,8 +421,15 @@ bool Menu::mainLoop() { } else if (option == kTitleScreen_Play) { return true; } else if (option == kTitleScreen_Options) { + PafCallback pafCb; + pafCb.proc = menuPafCallback; + pafCb.userdata = this; + _paf->setCallback(&pafCb); + playSound(kSound_0xA0); handleOptions(); debug(kDebug_MENU, "optionNum %d", _optionNum); + _g->resetSound(); + _paf->setCallback(0); if (_optionNum == kMenu_NewGame + 1 || _optionNum == kMenu_CurrentGame + 1 || _optionNum == kMenu_ResumeGame) { ret = true; break; @@ -421,18 +463,18 @@ int Menu::handleTitleScreen() { g_system->processEvents(); if (g_system->inp.keyReleased(SYS_INP_UP)) { if (currentOption > firstOption) { - playSound(0x70); + playSound(kSound_0x70); --currentOption; } } if (g_system->inp.keyReleased(SYS_INP_DOWN)) { if (currentOption < lastOption) { - playSound(0x70); + playSound(kSound_0x70); ++currentOption; } } if (g_system->inp.keyReleased(SYS_INP_SHOOT) || g_system->inp.keyReleased(SYS_INP_JUMP)) { - playSound(0x78); + playSound(kSound_0x78); break; } drawTitleScreen(currentOption); @@ -530,7 +572,7 @@ void Menu::drawPlayerProgress(int state, int cursor) { int player = 0; for (int y = 96; y < 164; y += 17) { if (isEmptySetupCfg(_config, player)) { - drawSpritePos(_playerSprites, (const uint8_t *)&_playerSprites[1], 82, y - 3, 3); // empty + drawSprite(_playerSprites, (const uint8_t *)&_playerSprites[1], 3, 82, y - 3); // empty } else { int levelNum = _config->players[player].levelNum; int checkpointNum; @@ -564,7 +606,7 @@ void Menu::drawPlayerProgress(int state, int cursor) { } if (cursor > 0) { if (cursor <= 4) { // highlight one player - drawSpritePos(_playerSprites, (const uint8_t *)&_playerSprites[1], 2, cursor * 17 + 74, 8); + drawSprite(_playerSprites, (const uint8_t *)&_playerSprites[1], 8, 2, cursor * 17 + 74); drawSprite(_playerSprites, (const uint8_t *)&_playerSprites[1], 6); } else if (cursor == 5) { // cancel drawSprite(_playerSprites, (const uint8_t *)&_playerSprites[1], 7); @@ -587,9 +629,9 @@ void Menu::handleAssignPlayer() { g_system->processEvents(); if (g_system->inp.keyReleased(SYS_INP_SHOOT) || g_system->inp.keyReleased(SYS_INP_JUMP)) { if (state != 0 && cursor == 5) { - playSound(0x80); + playSound(kSound_0x80); } else { - playSound(0x78); + playSound(kSound_0x78); } if (state == 0) { // return to title screen @@ -622,14 +664,14 @@ void Menu::handleAssignPlayer() { if (cursor != 0 && state < 3) { if (g_system->inp.keyReleased(SYS_INP_UP)) { if (cursor > 1) { - playSound(0x70); + playSound(kSound_0x70); --cursor; setCurrentPlayer(cursor - 1); } } if (g_system->inp.keyReleased(SYS_INP_DOWN)) { if (cursor < 5) { - playSound(0x70); + playSound(kSound_0x70); ++cursor; setCurrentPlayer((cursor == 5) ? _config->currentPlayer : (cursor - 1)); } @@ -637,13 +679,13 @@ void Menu::handleAssignPlayer() { } else { if (g_system->inp.keyReleased(SYS_INP_LEFT)) { if (state == 1 || state == 2 || state == 5) { - playSound(0x70); + playSound(kSound_0x70); --state; } } if (g_system->inp.keyReleased(SYS_INP_RIGHT)) { if (state == 0 || state == 1 || state == 4) { - playSound(0x70); + playSound(kSound_0x70); ++state; } } @@ -668,7 +710,7 @@ void Menu::updateBitmapsCircularList(const DatBitmapsGroup *bitmapsGroup, const if (bitmapsGroup == _cutscenesBitmaps) { for (int i = 0; i < 3; ++i) { const int num = _bitmapCircularListIndex[i]; - if (num != -1) { + if (num != -1 && num < _cutsceneIndexesCount) { _bitmapCircularListIndex[i] = _cutsceneIndexes[num]; } } @@ -700,16 +742,16 @@ void Menu::drawBitmapsCircularList(const DatBitmapsGroup *bitmapsGroup, const ui void Menu::drawCheckpointScreen() { decodeLZW(_optionsBitmapData[_optionNum], _video->_frontLayer); drawBitmapsCircularList(_checkpointsBitmaps[_levelNum], _checkpointsBitmapsData[_levelNum], _checkpointNum, _lastLevelCheckpointNum[_levelNum], false); - drawSpriteNextFrame(_iconsSprites, 5, 0, 0); - drawSpritePos(&_iconsSprites[0], _iconsSpritesData, 119, 108, (_checkpointNum + 1) / 10); - drawSpritePos(&_iconsSprites[0], _iconsSpritesData, 127, 108, (_checkpointNum + 1) % 10); + drawSpriteAnim(_iconsSprites, _iconsSpritesData, 5); + drawSprite(&_iconsSprites[0], _iconsSpritesData, (_checkpointNum + 1) / 10, 119, 108); + drawSprite(&_iconsSprites[0], _iconsSpritesData, (_checkpointNum + 1) % 10, 127, 108); const int num = _loadCheckpointButtonState; if (num > 1 && num < 7) { drawSprite(&_iconsSprites[9], _iconsSpritesData, num - 2); } else { - drawSpriteNextFrame(_iconsSprites, (num != 0) ? 8 : 7, 0, 0); + drawSpriteAnim(_iconsSprites, _iconsSpritesData, (num != 0) ? 8 : 7); } - drawSpriteNextFrame(_iconsSprites, 6, 0, 0); + drawSpriteAnim(_iconsSprites, _iconsSpritesData, 6); refreshScreen(false); } @@ -718,28 +760,238 @@ void Menu::drawLevelScreen() { drawSprite(&_iconsSprites[1], _iconsSpritesData, _levelNum); DatBitmapsGroup *bitmap = &_levelsBitmaps[_levelNum]; drawBitmap(bitmap, _levelsBitmapsData + bitmap->offset, 23, 10, bitmap->w, bitmap->h, 192); - drawSpriteNextFrame(_iconsSprites, 4, 0, 0); - drawSpriteNextFrame(_iconsSprites, (_loadLevelButtonState != 0) ? 3 : 2, 0, 0); + drawSpriteAnim(_iconsSprites, _iconsSpritesData, 4); + drawSpriteAnim(_iconsSprites, _iconsSpritesData, (_loadLevelButtonState != 0) ? 3 : 2); refreshScreen(false); } void Menu::drawCutsceneScreen() { decodeLZW(_optionsBitmapData[_optionNum], _video->_frontLayer); drawBitmapsCircularList(_cutscenesBitmaps, _cutscenesBitmapsData, _cutsceneNum, _cutsceneIndexesCount, false); - drawSpriteNextFrame(_iconsSprites, 10, 0, 0); - drawSpritePos(&_iconsSprites[0], _iconsSpritesData, 119, 108, (_cutsceneNum + 1) / 10); - drawSpritePos(&_iconsSprites[0], _iconsSpritesData, 127, 108, (_cutsceneNum + 1) % 10); + drawSpriteAnim(_iconsSprites, _iconsSpritesData, 10); + drawSprite(&_iconsSprites[0], _iconsSpritesData, (_cutsceneNum + 1) / 10, 119, 108); + drawSprite(&_iconsSprites[0], _iconsSpritesData, (_cutsceneNum + 1) % 10, 127, 108); const int num = _loadCutsceneButtonState; if (num > 1 && num < 7) { drawSprite(&_iconsSprites[14], _iconsSpritesData, num - 2); } else { - drawSpriteNextFrame(_iconsSprites, (num != 0) ? 13 : 12, 0, 0); + drawSpriteAnim(_iconsSprites, _iconsSpritesData, (num != 0) ? 13 : 12); } - drawSpriteNextFrame(_iconsSprites, 11, 0, 0); + drawSpriteAnim(_iconsSprites, _iconsSpritesData, 11); refreshScreen(false); } +void Menu::drawSettingsScreen() { + decodeLZW(_optionsBitmapData[_optionNum], _video->_frontLayer); + drawSpriteAnim(_iconsSprites, _iconsSpritesData, 0x2A); + drawSpriteAnim(_iconsSprites, _iconsSpritesData, (_settingNum == kSettingNum_Controls) ? 0x27 : 0x24); + drawSpriteAnim(_iconsSprites, _iconsSpritesData, (_settingNum == kSettingNum_Difficulty) ? 0x26 : 0x23); + drawSpriteAnim(_iconsSprites, _iconsSpritesData, (_settingNum == kSettingNum_Sound) ? 0x28 : 0x25); + drawSprite(&_iconsSprites[0x29], _iconsSpritesData, (_settingNum == kSettingNum_Confirm) ? 1 : 0); + refreshScreen(true); +} + void Menu::handleSettingsScreen(int num) { + const uint8_t *data = &_optionData[num * 8]; + num = data[5]; + if (num == 0) { + if (_settingNum == kSettingNum_Controls) { + playSound(kSound_0x78); + _condMask = 0x10; + } else if (_settingNum == kSettingNum_Difficulty) { + playSound(kSound_0x78); + _condMask = 0x20; + } else if (_settingNum == kSettingNum_Sound) { + playSound(kSound_0x78); + _condMask = 0x40; + } else if (_settingNum == kSettingNum_Confirm) { + playSound(kSound_0x78); + _condMask = 0x80; + } + return; + } else if (num == 1) { + if (_settingNum != kSettingNum_Confirm && _settingNum > 0 && 0) { // 'controls' not implemented + playSound(kSound_0x70); + --_settingNum; + _iconsSprites[0x27].num = 0; + _iconsSprites[0x26].num = 0; + _iconsSprites[0x28].num = 0; + } + } else if (num == 2) { + if (_settingNum != kSettingNum_Confirm && _settingNum < 2 && 0) { // 'volume' not implemented + playSound(kSound_0x70); + ++_settingNum; + _iconsSprites[0x27].num = 0; + _iconsSprites[0x26].num = 0; + _iconsSprites[0x28].num = 0; + } + } else if (num == 3) { + if (_settingNum == kSettingNum_Confirm) { + playSound(kSound_0x70); + _settingNum = kSettingNum_Difficulty; + _iconsSprites[0x26].num = 0; + } + } else if (num == 4) { + if (_settingNum != kSettingNum_Confirm) { + playSound(kSound_0x70); + } + _settingNum = kSettingNum_Confirm; + } + drawSettingsScreen(); + _condMask = 8; + g_system->sleep(30); +} + +void Menu::drawDifficultyScreen() { + decodeLZW(_optionsBitmapData[_optionNum], _video->_frontLayer); + for (int i = 0; i < 3; ++i) { + if (i != _difficultyNum) { + drawSprite(&_iconsSprites[0xF], _iconsSpritesData, i * 2); + } + } + drawSprite(&_iconsSprites[0xF], _iconsSpritesData, _difficultyNum * 2 + 1); + refreshScreen(true); +} + +void Menu::handleDifficultyScreen(int num) { + const uint8_t *data = &_optionData[num * 8]; + num = data[5]; + if (num == 0) { + playSound(kSound_0x78); + _config->players[_config->currentPlayer].difficulty = _g->_difficulty = _difficultyNum; + _condMask = 0x80; + } else if (num == 1) { + if (_difficultyNum > 0) { + playSound(kSound_0x70); + --_difficultyNum; + } + } else if (num == 2) { + if (_difficultyNum < 2) { + playSound(kSound_0x70); + ++_difficultyNum; + } + } + drawDifficultyScreen(); + g_system->sleep(30); +} + +void Menu::drawSoundScreen() { + decodeLZW(_optionsBitmapData[_optionNum], _video->_frontLayer); + drawSprite(&_iconsSprites[0x12], _iconsSpritesData, (_soundNum == kSoundNum_Stereo) ? 1 : 0); + drawSprite(&_iconsSprites[0x12], _iconsSpritesData, (_soundNum == kSoundNum_Volume) ? 3 : 2); + drawSprite(&_iconsSprites[0x12], _iconsSpritesData, (_soundNum == kSoundNum_Confirm) ? 5 : 4); + drawSprite(&_iconsSprites[0x12], _iconsSpritesData, (_soundNum == kSoundNum_Test) ? 7 : 6); + drawSprite(&_iconsSprites[0x12], _iconsSpritesData, (_soundNum == kSoundNum_Cancel) ? 9 : 8); + drawSprite(&_iconsSprites[0x12], _iconsSpritesData, (_soundNum == kSoundNum_Reset) ? 11 : 10); + // volume bar + const int w = ((_g->_snd_masterVolume * 3) << 5) >> 7; + for (int y = 0; y < 15; ++y) { + memset(_video->_frontLayer + 18807 + 256 * y, 0xE0, w); + } + drawSprite(&_iconsSprites[0x12], _iconsSpritesData, 21); + if (_soundNum == kSoundNum_Test) { +// drawSprite(&_iconsSprites[0x12], _iconsSpritesData, _soundTestNum); + } + if (_g->_snd_masterVolume != 0) { + if (_config->players[_config->currentPlayer].stereo) { + drawSprite(&_iconsSprites[0x12], _iconsSpritesData, 13); + drawSprite(&_iconsSprites[0x12], _iconsSpritesData, 16); + } else { + drawSprite(&_iconsSprites[0x12], _iconsSpritesData, 14); + drawSprite(&_iconsSprites[0x12], _iconsSpritesData, 15); + } + } + if (0) { // (soundUnk1 != 0) && (_soundCounter & 1) + if (0) { // soundUnk1 == 1 + drawSprite(&_iconsSprites[0x12], _iconsSpritesData, 18); + drawSprite(&_iconsSprites[0x12], _iconsSpritesData, 19); + } else { + drawSprite(&_iconsSprites[0x12], _iconsSpritesData, 17); + drawSprite(&_iconsSprites[0x12], _iconsSpritesData, 20); + } + } else { + drawSprite(&_iconsSprites[0x12], _iconsSpritesData, 17); + drawSprite(&_iconsSprites[0x12], _iconsSpritesData, 19); + } + refreshScreen(true); +} + +void Menu::handleSoundScreen(int num) { + const uint8_t *data = &_optionData[num * 8]; + num = data[5]; + if (num == 0) { + if (_soundNum == kSoundNum_Confirm) { + playSound(kSound_0x78); + _config->players[_config->currentPlayer].volume = _g->_snd_masterVolume; + _condMask = 0x80; + } else if (_soundNum == kSoundNum_Test) { + playSound(kSound_0x60); + // ... + } else if (_soundNum == kSoundNum_Cancel) { + playSound(kSound_0x80); + _config->players[_config->currentPlayer].volume = _g->_snd_masterVolume = _soundVolume; + _condMask = 0x80; + } else if (_soundNum == kSoundNum_Reset) { + playSound(kSound_0x88); + _config->players[_config->currentPlayer].volume = _g->_snd_masterVolume = Game::kDefaultSoundVolume; + } + } else if (num == 1) { + if (_soundNum == 0) { + // ... + } else if (_soundNum == kSoundNum_Volume) { + if (_g->_snd_masterVolume > 0) { + playSound(kSound_0x90); + --_g->_snd_masterVolume; + _config->players[_config->currentPlayer].volume = _g->_snd_masterVolume; + _condMask = 8; + } + } else if (_soundNum == 3) { + playSound(kSound_0x70); + _soundNum = 4; + } else if (_soundNum == 4) { + playSound(kSound_0x70); + _soundNum = 2; + } + } else if (num == 2) { + if (_soundNum == 0) { + // ... + } else if (_soundNum == kSoundNum_Volume) { + if (_g->_snd_masterVolume < 128) { + playSound(kSound_0x90); + ++_g->_snd_masterVolume; + _config->players[_config->currentPlayer].volume = _g->_snd_masterVolume; + _condMask = 8; + } + } else if (_soundNum == 2) { + playSound(kSound_0x70); + _soundNum = 4; + } else if (_soundNum == 4) { + if (_g->_snd_masterVolume != 0) { + playSound(kSound_0x70); + _soundNum = 3; + } + } + } else if (num == 3) { + if (_soundNum != kSoundNum_Volume) { + playSound(kSound_0x70); + } + if ((_soundNum >= 2 && _soundNum <= 4) || _soundNum == 5) { + _soundNum = 1; + } + } else if (num == 4) { + if (_soundNum != 5) { + playSound(kSound_0x70); + } + if (_soundNum == 0) { + _soundNum = 1; + } else if (_soundNum == 1) { + _soundNum = 4; + } else if (_soundNum >= 2 && _soundNum <= 4) { + _soundNum = 5; + } + } + drawSoundScreen(); + g_system->sleep(30); } void Menu::changeToOption(int num) { @@ -747,11 +999,16 @@ void Menu::changeToOption(int num) { const int button = data[6]; if (button != 0xFF) { assert(button < _optionsButtonSpritesCount); - _currentOptionButton = _optionsButtonSpritesData + button * 20; + _currentOptionButtonSound = READ_LE_UINT32(_optionsButtonSpritesData + button * 20); + _currentOptionButtonSprite = (DatSpritesGroup *)(_optionsButtonSpritesData + button * 20 + 4); + _currentOptionButtonSprite->num = 0; // start from frame #0 } else { - _currentOptionButton = 0; + _currentOptionButtonSound = 0; + _currentOptionButtonSprite = 0; + } + if (!_paf->_skipCutscenes) { + _paf->play(data[5]); } - _paf->play(data[5]); if (_optionNum == kMenu_NewGame + 1) { _config->players[_config->currentPlayer].levelNum = 0; _config->players[_config->currentPlayer].checkpointNum = 0; @@ -760,7 +1017,6 @@ void Menu::changeToOption(int num) { // _config->players[_config->currentPlayer].checkpointNum = _g->_currentLevelCheckpoint; } else if (_optionNum == kMenu_Load + 1) { _loadLevelButtonState = 0; - _levelNum = 0; memcpy(_paletteBuffer, _optionsBitmapData[5] + _optionsBitmapSize[5], 768); memcpy(_paletteBuffer + 192 * 3, _levelsBitmapsData + _levelsBitmaps[_levelNum].palette, 64 * 3); g_system->setPalette(_paletteBuffer, 256, 6); @@ -773,7 +1029,8 @@ void Menu::changeToOption(int num) { g_system->setPalette(_paletteBuffer, 256, 6); drawCheckpointScreen(); } else if (_optionNum == kMenu_Settings + 1) { - memcpy(_paletteBuffer, _optionsBitmapData[0] + _optionsBitmapSize[0], 768); + _settingNum = 1; + memcpy(_paletteBuffer, _optionsBitmapData[_optionNum] + _optionsBitmapSize[_optionNum], 768); handleSettingsScreen(5); } else if (_optionNum == kMenu_Cutscenes + 1) { _loadCutsceneButtonState = 0; @@ -791,25 +1048,25 @@ void Menu::handleLoadLevel(int num) { num = data[5]; if (num == 16) { if (_loadLevelButtonState != 0) { - playSound(0x70); + playSound(kSound_0x70); } _loadLevelButtonState = 0; } else if (num == 17) { if (_loadLevelButtonState == 0) { - playSound(0x70); + playSound(kSound_0x70); } _loadLevelButtonState = 1; } else if (num == 18) { if (_loadLevelButtonState != 0) { - playSound(0x80); + playSound(kSound_0x80); _condMask = 0x80; } else { - playSound(0x78); + playSound(kSound_0x78); _condMask = 0x10; } } else if (num == 19) { if (_lastLevelNum > 1) { - playSound(0x70); + playSound(kSound_0x70); ++_levelNum; _checkpointNum = 0; if (_levelNum >= _lastLevelNum) { @@ -821,7 +1078,7 @@ void Menu::handleLoadLevel(int num) { } } else if (num == 20) { if (_lastLevelNum > 1) { - playSound(0x70); + playSound(kSound_0x70); --_levelNum; _checkpointNum = 0; if (_levelNum < 0) { @@ -840,20 +1097,20 @@ void Menu::handleLoadCheckpoint(int num) { num = data[5]; if (num == 11) { if (_loadCheckpointButtonState != 0) { - playSound(0x70); + playSound(kSound_0x70); _loadCheckpointButtonState = 0; } } else if (num == 12) { if (_loadCheckpointButtonState == 0) { - playSound(0x70); + playSound(kSound_0x70); _loadCheckpointButtonState = 1; } } else if (num == 13) { if (_loadCheckpointButtonState != 0) { - playSound(0x80); + playSound(kSound_0x80); _condMask = 0x80; } else { - playSound(0x78); + playSound(kSound_0x78); _loadCheckpointButtonState = 2; _condMask = 0x08; if (_levelNum == 7 && _checkpointNum == 11) { @@ -868,7 +1125,7 @@ void Menu::handleLoadCheckpoint(int num) { } } else if (num == 14) { if (_lastLevelCheckpointNum[_levelNum] > 2 || _loadCheckpointButtonState == 0) { - playSound(0x70); + playSound(kSound_0x70); ++_checkpointNum; if (_checkpointNum >= _lastLevelCheckpointNum[_levelNum]) { _checkpointNum = 0; @@ -876,7 +1133,7 @@ void Menu::handleLoadCheckpoint(int num) { } } else if (num == 15) { if (_lastLevelCheckpointNum[_levelNum] > 2 || _loadCheckpointButtonState == 1) { - playSound(0x70); + playSound(kSound_0x70); --_checkpointNum; if (_checkpointNum < 0) { _checkpointNum = _lastLevelCheckpointNum[_levelNum] - 1; @@ -891,33 +1148,35 @@ void Menu::handleLoadCutscene(int num) { num = data[5]; if (num == 6) { if (_loadCutsceneButtonState != 0) { - playSound(0x70); + playSound(kSound_0x70); _loadCutsceneButtonState = 0; } } else if (num == 7) { if (_loadCutsceneButtonState == 0) { - playSound(0x70); + playSound(kSound_0x70); _loadCutsceneButtonState = 1; } } else if (num == 8) { if (_loadCutsceneButtonState != 0) { - playSound(0x80); + playSound(kSound_0x80); _condMask = 0x80; } else { - playSound(0x78); + playSound(kSound_0x78); _loadCutsceneButtonState = 2; - const int num = _cutscenesBitmaps[_cutsceneIndexes[_cutsceneNum]].data; - _paf->play(num); - if (num == kPafAnimation_end) { - _paf->play(kPafAnimation_cinema); + if (!_paf->_skipCutscenes) { + const int num = _cutscenesBitmaps[_cutsceneIndexes[_cutsceneNum]].data; + _paf->play(num); + if (num == kPafAnimation_end) { + _paf->play(kPafAnimation_cinema); + } } - playSound(0x98); - playSound(0xA0); + playSound(kSound_0x98); + playSound(kSound_0xA0); _loadCutsceneButtonState = 0; } } else if (num == 9) { if (_cutsceneIndexesCount > 2 || _loadCutsceneButtonState == 0) { - playSound(0x70); + playSound(kSound_0x70); ++_cutsceneNum; if (_cutsceneNum >= _cutsceneIndexesCount) { _cutsceneNum = 0; @@ -925,7 +1184,7 @@ void Menu::handleLoadCutscene(int num) { } } else if (num == 10) { if (_cutsceneIndexesCount > 2 || _loadCutsceneButtonState == 1) { - playSound(0x70); + playSound(kSound_0x70); --_cutsceneNum; if (_cutsceneNum < 0) { _cutsceneNum = _cutsceneIndexesCount - 1; @@ -937,10 +1196,6 @@ void Menu::handleLoadCutscene(int num) { static bool matchInput(uint8_t menu, uint8_t type, uint8_t mask, const PlayerInput &inp, uint8_t optionMask) { if (type != 0) { - if (menu == kMenu_Settings) { - // not implemented yet - return false; - } if ((mask & 1) != 0 && inp.keyReleased(SYS_INP_RUN)) { return true; } @@ -971,10 +1226,18 @@ static bool matchInput(uint8_t menu, uint8_t type, uint8_t mask, const PlayerInp } void Menu::handleOptions() { - _lastLevelNum = _config->players[_config->currentPlayer].currentLevel + 1; - if (_lastLevelNum >= _res->_datHdr.levelsCount) { + _lastLevelNum = _config->players[_config->currentPlayer].lastLevelNum + 1; + if (_lastLevelNum > _res->_datHdr.levelsCount) { _lastLevelNum = _res->_datHdr.levelsCount; } + _levelNum = _config->players[_config->currentPlayer].levelNum; + if (_levelNum > kLvl_dark) { + _levelNum = kLvl_dark; + } + if (_levelNum == kLvl_dark) { + _levelNum = 7; + _checkpointNum = 11; + } _cutsceneIndexesCount = 0; const uint32_t playedCutscenesMask = _config->players[_config->currentPlayer].cutscenesMask; for (int i = 0; i < _res->_datHdr.cutscenesCount; ++i) { @@ -1010,18 +1273,42 @@ void Menu::handleOptions() { continue; } const uint8_t *data = &_optionData[num * 8]; + const int prevOptionNum = _optionNum; _optionNum = data[3]; debug(kDebug_MENU, "handleOptions option %d code %d", _optionNum, data[4]); switch (data[4]) { case 0: + if (prevOptionNum != _optionNum) { + _iconsSprites[0x2A].num = 0; + _iconsSprites[0x27].num = 0; + _iconsSprites[0x26].num = 0; + _iconsSprites[0x28].num = 0; + memcpy(_paletteBuffer, _optionsBitmapData[_optionNum] + _optionsBitmapSize[_optionNum], 768); + } handleSettingsScreen(num); break; + // case 1: // controls + case 4: + if (prevOptionNum != _optionNum) { + memcpy(_paletteBuffer, _optionsBitmapData[_optionNum] + _optionsBitmapSize[_optionNum], 768); + _difficultyNum = _config->players[_config->currentPlayer].difficulty; + } + handleDifficultyScreen(num); + break; + case 5: + if (prevOptionNum != _optionNum) { + memcpy(_paletteBuffer, _optionsBitmapData[_optionNum] + _optionsBitmapSize[_optionNum], 768); + _soundNum = 2; + _soundVolume = _g->_snd_masterVolume; + } + handleSoundScreen(num); + break; case 6: - playSound(0x70); + playSound(kSound_0x70); changeToOption(num); break; case 7: - playSound(0x78); + playSound(kSound_0x78); changeToOption(num); break; case 8: diff --git a/menu.h b/menu.h index 1d55219..d0bbc51 100644 --- a/menu.h +++ b/menu.h @@ -66,7 +66,8 @@ struct Menu { const uint8_t *_iconsSpritesData; int _optionsButtonSpritesCount; const uint8_t *_optionsButtonSpritesData; - const uint8_t *_currentOptionButton; + DatSpritesGroup *_currentOptionButtonSprite; + int _currentOptionButtonSound; const uint8_t *_digitsData; const uint8_t *_optionData; @@ -84,6 +85,10 @@ struct Menu { int _cutsceneNum; uint8_t _loadCutsceneButtonState; int _cutsceneIndexes[kCutsceneIndexesCount]; + int _settingNum; + int _difficultyNum; + int _soundNum; + uint8_t _soundVolume; Menu(Game *g, PafPlayer *paf, Resource *res, Video *video); @@ -91,12 +96,13 @@ struct Menu { void loadData(); - int getSoundNum(int num) const; + int getSoundNum(int num, int index = 0) const; void playSound(int num); - void drawSprite(const DatSpritesGroup *spriteGroup, const uint8_t *ptr, uint32_t num); - void drawSpritePos(const DatSpritesGroup *spriteGroup, const uint8_t *ptr, int x, int y, uint32_t num); - void drawSpriteNextFrame(DatSpritesGroup *spriteGroup, int num, int x, int y); + void drawSprite(const DatSpritesGroup *spriteGroup, const uint8_t *ptr, uint32_t num, int x = -1, int y = -1); + void drawSpriteAnim(DatSpritesGroup *spriteGroup, const uint8_t *ptr, uint32_t num); + + void pafCallback(int frameNum, const uint8_t *frameData); void refreshScreen(bool updatePalette); bool mainLoop(); @@ -114,7 +120,12 @@ struct Menu { void drawCheckpointScreen(); void drawLevelScreen(); void drawCutsceneScreen(); + void drawSettingsScreen(); void handleSettingsScreen(int num); + void drawDifficultyScreen(); + void handleDifficultyScreen(int num); + void drawSoundScreen(); + void handleSoundScreen(int num); void changeToOption(int num); void handleLoadLevel(int num); void handleLoadCheckpoint(int num); diff --git a/mixer.cpp b/mixer.cpp index 8a23e08..15e983d 100644 --- a/mixer.cpp +++ b/mixer.cpp @@ -7,18 +7,11 @@ static void nullMixerLock(int lock) { Mixer::Mixer() : _lock(nullMixerLock) { -} - -Mixer::~Mixer() { -} - -void Mixer::init(int rate) { - _rate = rate; memset(_mixingQueue, 0, sizeof(_mixingQueue)); _mixingQueueSize = 0; } -void Mixer::fini() { +Mixer::~Mixer() { } void Mixer::queue(const int16_t *ptr, const int16_t *end, int panType, int panL, int panR, bool stereo) { diff --git a/mixer.h b/mixer.h index ee21d27..b6dc124 100644 --- a/mixer.h +++ b/mixer.h @@ -18,7 +18,6 @@ struct Mixer { static const int kPcmChannels = 32; void (*_lock)(int); - int _rate; MixerChannel _mixingQueue[kPcmChannels]; int _mixingQueueSize; @@ -26,9 +25,6 @@ struct Mixer { Mixer(); ~Mixer(); - void init(int rate); - void fini(); - void queue(const int16_t *ptr, const int16_t *end, int panType, int panL, int panR, bool stereo); void mix(int16_t *buf, int len); diff --git a/monsters.cpp b/monsters.cpp index 8edbdbc..c24e7b4 100644 --- a/monsters.cpp +++ b/monsters.cpp @@ -2911,7 +2911,9 @@ void Game::mstUpdateRefPos() { AndyShootData *p = _andyShootsTable; for (LvlObject *o = _lvlObjectsList0; o; o = o->nextPtr) { p->o = o; - assert(o->dataPtr); + if (!o->dataPtr) { + continue; + } ShootLvlObjectData *ptr = (ShootLvlObjectData *)getLvlObjectDataPtr(o, kObjectDataTypeShoot); p->shootObjectData = ptr; if (ptr->unk3 == 0x80) { @@ -3168,7 +3170,6 @@ Task *Game::findFreeTask() { Task *Game::createTask(const uint8_t *codeData) { Task *t = findFreeTask(); if (t) { - memset(t, 0, sizeof(Task)); resetTask(t, codeData); t->prevPtr = 0; t->nextPtr = _tasksList; @@ -3220,7 +3221,6 @@ void Game::updateTask(Task *t, int num, const uint8_t *codeData) { if (codeData) { t = findFreeTask(); if (t) { - memset(t, 0, sizeof(Task)); resetTask(t, codeData); t->prevPtr = 0; t->nextPtr = _tasksList; @@ -3234,7 +3234,7 @@ void Game::updateTask(Task *t, int num, const uint8_t *codeData) { } void Game::resetTask(Task *t, const uint8_t *codeData) { - debug(kDebug_MONSTER, "resetTask t %p offset 0x%04x", t, codeData - _res->_mstCodeData); + debug(kDebug_MONSTER, "resetTask t %p offset 0x%04x monster1 %p monster2 %p", t, codeData - _res->_mstCodeData, t->monster1, t->monster2); assert(codeData); t->state |= 2; t->codeData = codeData; @@ -5578,7 +5578,7 @@ int Game::mstOp56_specialAction(Task *t, int code, int num) { if (op204Data->arg3 != 6 && o) { LvlObject *tmpObject = t->monster1->o16; const uint8_t flags = getLvlObjectFlag(op204Data->arg3 & 255, tmpObject, _andyObject); - _specialAnimMask = ((flags & 3) << 4) | (_specialAnimMask & 0xFFCF); + _specialAnimMask = ((flags & 3) << 4) | (_specialAnimMask & ~0x30); // _specialAnimScreenNum = tmpObject->screenNum; _specialAnimLvlObject = tmpObject; _mstOriginPosX = op204Data->arg1 & 0xFFFF; @@ -5757,7 +5757,7 @@ int Game::mstOp56_specialAction(Task *t, int code, int num) { o = _andyObject; } const uint8_t flags = getLvlObjectFlag(op204Data->arg2 & 255, o, _andyObject); - _andyObject->flags1 = ((flags & 3) << 4) | (_andyObject->flags1 & 0xFFCF); + _andyObject->flags1 = ((flags & 3) << 4) | (_andyObject->flags1 & ~0x30); const int x3 = _andyObject->posTable[3].x; const int y3 = _andyObject->posTable[3].y; setupLvlObjectBitmap(_andyObject); @@ -6796,7 +6796,6 @@ void Game::mstOp67_addMonster(Task *currentTask, int x1, int x2, int y1, int y2, removeLvlObject2(o); return; } - memset(t, 0, sizeof(Task)); resetTask(t, kUndefinedMonsterByteCode); t->monster2 = mo; t->monster1 = 0; @@ -6818,7 +6817,6 @@ void Game::mstOp67_addMonster(Task *currentTask, int x1, int x2, int y1, int y2, removeLvlObject2(o); return; } - memset(t, 0, sizeof(Task)); resetTask(t, kUndefinedMonsterByteCode); t->monster1 = m; t->monster2 = 0; diff --git a/paf.cpp b/paf.cpp index a1e2848..6b52e78 100644 --- a/paf.cpp +++ b/paf.cpp @@ -34,10 +34,8 @@ static void closePaf(FileSystem *fs, File *f) { } PafPlayer::PafPlayer(FileSystem *fs) - : _skipCutscenes(false), _fs(fs) { - if (!openPaf(_fs, &_file)) { - _skipCutscenes = true; - } + : _fs(fs) { + _skipCutscenes = !openPaf(_fs, &_file); _videoNum = -1; memset(&_pafHdr, 0, sizeof(_pafHdr)); memset(_pageBuffers, 0, sizeof(_pageBuffers)); @@ -45,6 +43,7 @@ PafPlayer::PafPlayer(FileSystem *fs) _demuxVideoFrameBlocks = 0; _audioQueue = _audioQueueTail = 0; _playedMask = 0; + memset(&_pafCb, 0, sizeof(_pafCb)); } PafPlayer::~PafPlayer() { @@ -131,6 +130,7 @@ bool PafPlayer::readPafHeader() { warning("readPafHeader() Unexpected signature"); return false; } + _pafHdr.frameRate = READ_LE_UINT32(_bufferBlock + 0x88); _pafHdr.startOffset = READ_LE_UINT32(_bufferBlock + 0xA4); _pafHdr.preloadFrameBlocksCount = READ_LE_UINT32(_bufferBlock + 0x9C); _pafHdr.readBufferSize = READ_LE_UINT32(_bufferBlock + 0x98); @@ -177,6 +177,7 @@ void PafPlayer::decodeVideoFrame(const uint8_t *src) { memset(_pageBuffers[i], 0, kPageBufferSize); } memset(_paletteBuffer, 0, sizeof(_paletteBuffer)); + _paletteChanged = true; _currentPageBuffer = 0; } if (code & 0x40) { @@ -185,6 +186,7 @@ void PafPlayer::decodeVideoFrame(const uint8_t *src) { assert(index * 3 + count <= 768); src += 2; memcpy(_paletteBuffer + index * 3, src, count); + _paletteChanged = true; src += count; } switch (code & 0xF) { @@ -254,11 +256,10 @@ static const char *updateSequences[] = { "\x02\x04\x05\x07\x05\x07" }; -uint8_t *PafPlayer::getVideoPageOffset(uint8_t a, uint8_t b) { - const int x = b & 0x7F; - const int y = ((a & 0x3F) << 1) | ((b >> 7) & 1); - const int page = (a & 0xC0) >> 6; - return _pageBuffers[page] + (y * kVideoWidth + x) * 2; +uint8_t *PafPlayer::getVideoPageOffset(uint16_t val) { + const int x = val & 0x7F; val >>= 7; + const int y = val & 0x7F; val >>= 7; + return _pageBuffers[val] + (y * kVideoWidth + x) * 2; } void PafPlayer::decodeVideoFrameOp0(const uint8_t *base, const uint8_t *src, uint8_t code) { @@ -271,8 +272,8 @@ void PafPlayer::decodeVideoFrameOp0(const uint8_t *base, const uint8_t *src, uin src += 4 - align; } } - do { - uint8_t *dst = getVideoPageOffset(src[0], src[1]); + for (int i = 0; i < count; ++i) { + uint8_t *dst = getVideoPageOffset((src[0] << 8) | src[1]); uint32_t offset = (src[1] & 0x7F) * 2; uint32_t end = READ_LE_UINT16(src + 2); src += 4; end += offset; @@ -285,24 +286,18 @@ void PafPlayer::decodeVideoFrameOp0(const uint8_t *base, const uint8_t *src, uin } dst += 4; } while (offset < end); - } while (--count != 0); + } } uint8_t *dst = _pageBuffers[_currentPageBuffer]; - count = 0; - do { - const uint8_t *src2 = getVideoPageOffset(src[0], src[1]); src += 2; - pafCopy4x4v(dst, src2); - ++count; - if ((count & 0x3F) == 0) { - dst += kVideoWidth * 3; + for (int y = 0; y < kVideoHeight; y += 4, dst += kVideoWidth * 3) { + for (int x = 0; x < kVideoWidth; x += 4, dst += 4) { + const uint8_t *src2 = getVideoPageOffset((src[0] << 8) | src[1]); src += 2; + pafCopy4x4v(dst, src2); } - dst += 4; - } while (count < kVideoWidth * kVideoHeight / 16); + } const uint32_t opcodesSize = READ_LE_UINT16(src); src += 4; - - const char *opcodes; const uint8_t *opcodesData = src; src += opcodesSize; @@ -312,37 +307,36 @@ void PafPlayer::decodeVideoFrameOp0(const uint8_t *base, const uint8_t *src, uin dst = _pageBuffers[_currentPageBuffer]; for (int y = 0; y < kVideoHeight; y += 4, dst += kVideoWidth * 3) { - for (int x = 0; x < kVideoWidth; x += 4, dst += 4) { - if ((x & 4) == 0) { - opcodes = updateSequences[*opcodesData >> 4]; - } else { - opcodes = updateSequences[*opcodesData & 15]; - ++opcodesData; - } - while (*opcodes) { - uint32_t offset = kVideoWidth * 2; - const int code = *opcodes++; - switch (code) { - case 2: - offset = 0; - case 3: - color = *src++; - case 4: - mask = *src++; - pafCopyColorMask(mask >> 4, dst + offset, color); - offset += kVideoWidth; - pafCopyColorMask(mask & 15, dst + offset, color); - break; - case 5: - offset = 0; - case 6: - src2 = getVideoPageOffset(src[0], src[1]); src += 2; - case 7: - mask = *src++; - pafCopySrcMask(mask >> 4, dst + offset, src2 + offset); - offset += kVideoWidth; - pafCopySrcMask(mask & 15, dst + offset, src2 + offset); - break; + for (int x = 0; x < kVideoWidth; x += 8) { + uint8_t updateIndex = *opcodesData++; + for (int i = 0; i < 2; ++i, dst += 4) { + const char *opcodes = updateSequences[updateIndex >> 4]; + updateIndex <<= 4; + while (*opcodes) { + uint32_t offset = kVideoWidth * 2; + const int code = *opcodes++; + switch (code) { + case 2: + offset = 0; + case 3: + color = *src++; + case 4: + mask = *src++; + pafCopyColorMask(mask >> 4, dst + offset, color); + offset += kVideoWidth; + pafCopyColorMask(mask & 15, dst + offset, color); + break; + case 5: + offset = 0; + case 6: + src2 = getVideoPageOffset((src[0] << 8) | src[1]); src += 2; + case 7: + mask = *src++; + pafCopySrcMask(mask >> 4, dst + offset, src2 + offset); + offset += kVideoWidth; + pafCopySrcMask(mask & 15, dst + offset, src2 + offset); + break; + } } } } @@ -382,7 +376,7 @@ void PafPlayer::decodeVideoFrameOp4(const uint8_t *src) { } static void decodeAudioFrame2205(const uint8_t *src, int len, int16_t *dst) { - const int offset = 256 * sizeof(int16_t); + static const int offset = 256 * sizeof(int16_t); for (int i = 0; i < len * 2; ++i) { // stereo dst[i] = READ_LE_UINT16(src + src[offset + i] * sizeof(int16_t)); } @@ -466,6 +460,7 @@ void PafPlayer::mainLoop() { memset(_pageBuffers[i], 0, kVideoWidth * kVideoHeight); } memset(_paletteBuffer, 0, sizeof(_paletteBuffer)); + _paletteChanged = true; _currentPageBuffer = 0; int currentFrameBlock = 0; @@ -480,9 +475,10 @@ void PafPlayer::mainLoop() { const uint32_t framesPerSec = (_demuxAudioFrameBlocks != 0) ? kFramesPerSec : 15; uint32_t frameTime = g_system->getTimeStamp() + 1000 / framesPerSec; + uint32_t blocksCountForFrame = _pafHdr.preloadFrameBlocksCount; for (int i = 0; i < (int)_pafHdr.framesCount; ++i) { // read buffering blocks - uint32_t blocksCountForFrame = (i == 0) ? _pafHdr.preloadFrameBlocksCount : _pafHdr.frameBlocksCountTable[i - 1]; + blocksCountForFrame += _pafHdr.frameBlocksCountTable[i]; while (blocksCountForFrame != 0) { _file.read(_bufferBlock, _pafHdr.readBufferSize); const uint32_t dstOffset = _pafHdr.frameBlocksOffsetTable[currentFrameBlock] & ~(1 << 31); @@ -499,8 +495,15 @@ void PafPlayer::mainLoop() { } // decode video data decodeVideoFrame(_demuxVideoFrameBlocks + _pafHdr.framesOffsetTable[i]); - g_system->setPalette(_paletteBuffer, 256, 6); - g_system->copyRect(0, 0, kVideoWidth, kVideoHeight, _pageBuffers[_currentPageBuffer], kVideoWidth); + + if (_pafCb.proc) { + _pafCb.proc(_pafCb.userdata, i, _pageBuffers[_currentPageBuffer]); + } else { + g_system->copyRect(0, 0, kVideoWidth, kVideoHeight, _pageBuffers[_currentPageBuffer], kVideoWidth); + } + if (_paletteChanged) { + g_system->setPalette(_paletteBuffer, 256, 6); + } g_system->updateScreen(false); g_system->processEvents(); if (g_system->inp.quit || g_system->inp.keyPressed(SYS_INP_ESC)) { @@ -523,3 +526,11 @@ void PafPlayer::mainLoop() { unload(); } + +void PafPlayer::setCallback(const PafCallback *pafCb) { + if (pafCb) { + _pafCb = *pafCb; + } else { + memset(&_pafCb, 0, sizeof(_pafCb)); + } +} diff --git a/paf.h b/paf.h index 5c69cd9..3afc709 100644 --- a/paf.h +++ b/paf.h @@ -20,6 +20,7 @@ struct PafHeader { uint32_t readBufferSize; int32_t maxVideoFrameBlocksCount; int32_t maxAudioFrameBlocksCount; + int32_t frameRate; }; // names taken from the PSX filenames @@ -34,7 +35,7 @@ enum { kPafAnimation_vicious = 7, kPafAnimation_together = 8, kPafAnimation_power = 9, - kPafAniamtion_back = 10, + kPafAnimation_back = 10, kPafAnimation_dogfree1 = 11, kPafAnimation_dogfree2 = 12, kPafAnimation_meteor = 13, @@ -59,6 +60,11 @@ struct PafAudioQueue { PafAudioQueue *next; }; +struct PafCallback { + void (*proc)(void *userdata, int num, const uint8_t *frame); + void *userdata; +}; + struct PafPlayer { enum { @@ -81,6 +87,7 @@ struct PafPlayer { int _currentPageBuffer; uint8_t *_pageBuffers[4]; uint8_t _paletteBuffer[256 * 3]; + bool _paletteChanged; uint8_t _bufferBlock[kBufferBlockSize]; uint8_t *_demuxVideoFrameBlocks; uint8_t *_demuxAudioFrameBlocks; @@ -89,6 +96,7 @@ struct PafPlayer { PafAudioQueue *_audioQueue, *_audioQueueTail; uint32_t _flushAudioSize; uint32_t _playedMask; + PafCallback _pafCb; PafPlayer(FileSystem *fs); ~PafPlayer(); @@ -101,7 +109,7 @@ struct PafPlayer { uint32_t *readPafHeaderTable(int count); void decodeVideoFrame(const uint8_t *src); - uint8_t *getVideoPageOffset(uint8_t a, uint8_t b); + uint8_t *getVideoPageOffset(uint16_t val); void decodeVideoFrameOp0(const uint8_t *base, const uint8_t *src, uint8_t code); void decodeVideoFrameOp1(const uint8_t *src); void decodeVideoFrameOp2(const uint8_t *src); @@ -111,6 +119,8 @@ struct PafPlayer { void mix(int16_t *buf, int samples); void mainLoop(); + + void setCallback(const PafCallback *pafCb); }; #endif // PAF_PLAYER_H__ diff --git a/random.cpp b/random.cpp index 3ba570e..fe2c021 100644 --- a/random.cpp +++ b/random.cpp @@ -22,14 +22,16 @@ void Random::initMstTable() { } } -void Random::initTable() { +void Random::initTable(int rounds) { for (int i = 0; i < 100; ++i) { _rndRandomTable[i] = i + 1; } - for (int i = 0; i < 256; ++i) { - const int a = update() % 100; - const int b = update() % 100; - SWAP(_rndRandomTable[a], _rndRandomTable[b]); + for (int j = 0; j < rounds; ++j) { + for (int i = 0; i < 256; ++i) { + const int a = update() % 100; + const int b = update() % 100; + SWAP(_rndRandomTable[a], _rndRandomTable[b]); + } } _rndRandomTableIndex = 0; } @@ -44,7 +46,7 @@ uint32_t Random::update() { } uint8_t Random::getNextNumber() { - uint8_t num = _rndRandomTable[_rndRandomTableIndex]; + const uint8_t num = _rndRandomTable[_rndRandomTableIndex]; ++_rndRandomTableIndex; if (_rndRandomTableIndex == 100) { _rndRandomTableIndex = 0; diff --git a/random.h b/random.h index 7ae70df..2eecdcc 100644 --- a/random.h +++ b/random.h @@ -16,8 +16,7 @@ struct Random { void setSeed(); void initMstTable(); - void initTable(); - void init(); + void initTable(int rounds = 1); uint32_t update(); uint8_t getNextNumber(); void resetMst(uint8_t *p); diff --git a/resource.cpp b/resource.cpp index 7b36aaf..2f395a8 100644 --- a/resource.cpp +++ b/resource.cpp @@ -10,12 +10,21 @@ #include "resource.h" #include "util.h" +// load and uncompress .sss pcm on level start +static const bool kPreloadSssPcm = true; + +static const bool kPreloadLvlBackgroundData = true; + +static const bool kCheckSssBytecode = false; + // menu settings and player progress static const char *_setupCfg = "setup.cfg"; static const char *_setupDat = "SETUP.DAT"; static const char *_setupDax = "SETUP.DAX"; +static const char *_hodDem = "HOD.DEM"; + static const char *_prefixes[] = { "rock", "fort", @@ -33,6 +42,7 @@ static bool openDat(FileSystem *fs, const char *name, File *f) { FILE *fp = fs->openAssetFile(name); if (fp) { f->setFp(fp); + f->seek(0, SEEK_SET); return true; } return false; @@ -60,7 +70,7 @@ static int readBytesAlign(File *f, uint8_t *buf, int len) { } Resource::Resource(FileSystem *fs) - : _fs(fs), _isPsx(false) { + : _fs(fs), _isPsx(false), _isDemo(false), _version(V1_1) { memset(_screensGrid, 0, sizeof(_screensGrid)); memset(_screensBasePos, 0, sizeof(_screensBasePos)); @@ -70,6 +80,7 @@ Resource::Resource(FileSystem *fs) _resLevelData0x470CTable = 0; _resLevelData0x470CTablePtrHdr = 0; _resLevelData0x470CTablePtrData = 0; + _lvlSssOffset = 0; // sprites @@ -91,21 +102,51 @@ Resource::Resource(FileSystem *fs) _lvlFile = new SectorFile; _mstFile = new SectorFile; _sssFile = new SectorFile; + // from v1.2, game data files are 'sector aligned' + _version = V1_2; } else { _datFile = new File; _lvlFile = new File; _mstFile = new File; _sssFile = new File; + // detect if this is version 1.0 by reading the size of the first screen background using the v1.1 offset + char filename[32]; + snprintf(filename, sizeof(filename), "%s_HOD.LVL", _prefixes[0]); + if (openDat(_fs, filename, _lvlFile)) { + _lvlFile->seek(0x2B88, SEEK_SET); + _lvlFile->skipUint32(); + const int size = _lvlFile->readUint32(); + if (size == 0) { + _version = V1_0; + } + closeDat(_fs, _lvlFile); + } } + // detect if this is a demo version by trying to open the second level data files + char filename[32]; + snprintf(filename, sizeof(filename), "%s_HOD.LVL", _prefixes[1]); + if (openDat(_fs, filename, _lvlFile)) { + closeDat(_fs, _lvlFile); + } else { + _isDemo = true; + } + debug(kDebug_RESOURCE, "psx %d demo %d version %d", _isPsx, _isDemo, _version); memset(&_datHdr, 0, sizeof(_datHdr)); memset(&_lvlHdr, 0, sizeof(_lvlHdr)); memset(&_mstHdr, 0, sizeof(_mstHdr)); memset(&_sssHdr, 0, sizeof(_sssHdr)); + _lvlSpritesOffset = 0x288 + 96 * (_version == V1_0 ? 96 : 104); + _lvlBackgroundsOffset = _lvlSpritesOffset + 32 * 16; + _lvlMasksOffset = _lvlBackgroundsOffset + kMaxScreens * (16 + 160); + _loadingImageBuffer = 0; _fontBuffer = 0; _menuBuffer0 = 0; _menuBuffer1 = 0; + + memset(&_dem, 0, sizeof(_dem)); + _demOffset = 0; } Resource::~Resource() { @@ -164,7 +205,8 @@ void Resource::loadSetupDat() { for (int i = 0; i < hintsCount; ++i) { _datHdr.hintsImageSizeTable[i] = _datFile->readUint32(); } - _datFile->seek(2048, SEEK_SET); // align to the next sector + _datFile->seek(2048, SEEK_SET); // align to next sector + _loadingImageBuffer = (uint8_t *)malloc(_datHdr.loadingImageSize); if (_loadingImageBuffer) { _datFile->read(_loadingImageBuffer, _datHdr.loadingImageSize); @@ -205,7 +247,9 @@ bool Resource::loadDatHintImage(int num, uint8_t *dst, uint8_t *pal) { assert(size == 256 * 192); _datFile->seek(offset, SEEK_SET); _datFile->read(dst, size); - _datFile->flush(); + if (_datHdr.version == 11) { + _datFile->seek(offset + fioAlignSizeTo2048(size), SEEK_SET); // align to next sector + } _datFile->read(pal, 768); return true; } @@ -229,15 +273,14 @@ void Resource::loadDatMenuBuffers() { _datFile->seek(_datHdr.sssOffset, SEEK_SET); loadSssData(_datFile, _datHdr.sssOffset); - uint32_t baseOffset = _menuBuffersOffset; + const uint32_t baseOffset = _menuBuffersOffset; _datFile->seek(baseOffset, SEEK_SET); _menuBuffer1 = (uint8_t *)malloc(_datHdr.bufferSize1); if (_menuBuffer1) { _datFile->read(_menuBuffer1, _datHdr.bufferSize1); } if (_datHdr.version == 11) { - baseOffset += fioAlignSizeTo2048(_datHdr.bufferSize1); // align to the next sector - _datFile->seek(baseOffset, SEEK_SET); + _datFile->seek(baseOffset + fioAlignSizeTo2048(_datHdr.bufferSize1), SEEK_SET); // align to next sector } _menuBuffer0 = (uint8_t *)malloc(_datHdr.bufferSize0); if (_menuBuffer0) { @@ -434,16 +477,22 @@ static uint32_t resFixPointersLevelData0x2988(uint8_t *src, uint8_t *ptr, LvlObj return (dat->framesCount + dat->coordsCount) * sizeof(uint32_t); } -void Resource::loadLvlSpriteData(int num) { +void Resource::loadLvlSpriteData(int num, const uint8_t *buf) { assert((unsigned int)num < kMaxSpriteTypes); - static const uint32_t baseOffset = 0x2988; + static const uint32_t baseOffset = _lvlSpritesOffset; - uint8_t buf[4 * 3]; - _lvlFile->seekAlign(baseOffset + num * 16); - _lvlFile->read(buf, sizeof(buf)); + uint8_t header[3 * sizeof(uint32_t)]; + if (!buf) { + _lvlFile->seekAlign(baseOffset + num * 16); + _lvlFile->read(header, sizeof(header)); + buf = header; + } const uint32_t offset = READ_LE_UINT32(&buf[0]); const uint32_t size = READ_LE_UINT32(&buf[4]); + if (size == 0) { + return; + } const uint32_t readSize = READ_LE_UINT32(&buf[8]); assert(readSize <= size); uint8_t *ptr = (uint8_t *)malloc(size); @@ -473,7 +522,7 @@ const uint8_t *Resource::getLvlScreenPosDataPtr(int num) const { } void Resource::loadLvlScreenMaskData() { - _lvlFile->seekAlign(0x4708); + _lvlFile->seekAlign(_lvlMasksOffset); const uint32_t offset = _lvlFile->readUint32(); const uint32_t size = _lvlFile->readUint32(); _resLevelData0x470CTable = (uint8_t *)malloc(size); @@ -526,7 +575,9 @@ void Resource::loadLvlData(File *fp) { } _lvlFile->seekAlign(0x288); static const int kSizeOfLvlObject = 96; - for (int i = 0; i < (0x2988 - 0x288) / kSizeOfLvlObject; ++i) { + const int lvlObjectsCount = (_lvlSpritesOffset - 0x288) / kSizeOfLvlObject; + debug(kDebug_RESOURCE, "Resource::loadLvlData() lvlObjectsCount %d", lvlObjectsCount); + for (int i = 0; i < lvlObjectsCount; ++i) { LvlObject *dat = &_resLvlScreenObjectDataTable[i]; uint8_t buf[kSizeOfLvlObject]; _lvlFile->read(buf, kSizeOfLvlObject); @@ -535,12 +586,27 @@ void Resource::loadLvlData(File *fp) { loadLvlScreenMaskData(); - memset(_resLevelData0x2B88SizeTable, 0, sizeof(_resLevelData0x2B88SizeTable)); memset(_resLevelData0x2988SizeTable, 0, sizeof(_resLevelData0x2988SizeTable)); memset(_resLevelData0x2988PtrTable, 0, sizeof(_resLevelData0x2988PtrTable)); + _lvlFile->seekAlign(_lvlSpritesOffset); + uint8_t spr[kMaxSpriteTypes * 16]; + assert(_lvlHdr.spritesCount <= kMaxSpriteTypes); + _lvlFile->read(spr, _lvlHdr.spritesCount * 16); for (int i = 0; i < _lvlHdr.spritesCount; ++i) { - loadLvlSpriteData(i); + loadLvlSpriteData(i, spr + i * 16); + } + + memset(_resLevelData0x2B88SizeTable, 0, sizeof(_resLevelData0x2B88SizeTable)); + + if (kPreloadLvlBackgroundData) { + _lvlFile->seekAlign(_lvlBackgroundsOffset); + uint8_t buf[kMaxScreens * 16]; + assert(_lvlHdr.screensCount <= kMaxScreens); + _lvlFile->read(buf, _lvlHdr.screensCount * 16); + for (unsigned int i = 0; i < _lvlHdr.screensCount; ++i) { + loadLvlScreenBackgroundData(i, buf + i * 16); + } } } @@ -616,16 +682,22 @@ static uint32_t resFixPointersLevelData0x2B88(const uint8_t *src, uint8_t *ptr, return offsetsSize; } -void Resource::loadLvlScreenBackgroundData(int num) { +void Resource::loadLvlScreenBackgroundData(int num, const uint8_t *buf) { assert((unsigned int)num < kMaxScreens); - static const uint32_t baseOffset = 0x2B88; + static const uint32_t baseOffset = _lvlBackgroundsOffset; - uint8_t buf[4 * 3]; - _lvlFile->seekAlign(baseOffset + num * 16); - _lvlFile->read(buf, sizeof(buf)); + uint8_t header[3 * sizeof(uint32_t)]; + if (!buf) { + _lvlFile->seekAlign(baseOffset + num * 16); + _lvlFile->read(header, sizeof(header)); + buf = header; + } const uint32_t offset = READ_LE_UINT32(&buf[0]); const uint32_t size = READ_LE_UINT32(&buf[4]); + if (size == 0) { + return; + } const uint32_t readSize = READ_LE_UINT32(&buf[8]); assert(readSize <= size); uint8_t *ptr = (uint8_t *)malloc(size); @@ -725,11 +797,6 @@ void Resource::loadSssData(File *fp, const uint32_t baseOffset) { assert(fp == _sssFile || fp == _datFile || fp == _lvlFile); if (_sssHdr.bufferSize != 0) { - const int count = MIN(_sssHdr.pcmCount, _sssHdr.preloadPcmCount); - for (int i = 0; i < count; ++i) { - free(_sssPcmTable[i].ptr); - _sssPcmTable[i].ptr = 0; - } unloadSssData(); _sssHdr.bufferSize = 0; _sssHdr.infosDataCount = 0; @@ -762,6 +829,8 @@ void Resource::loadSssData(File *fp, const uint32_t baseOffset) { const int bufferSize = _sssHdr.bufferSize + _sssHdr.filtersDataCount * 52 + _sssHdr.banksDataCount * 56; debug(kDebug_RESOURCE, "bufferSize %d", bufferSize); + const bool preloadPcm = (fp == _datFile) || (kPreloadSssPcm && !_isPsx); + // fp->flush(); fp->seek(baseOffset + 2048, SEEK_SET); // align to the next sector @@ -828,15 +897,16 @@ void Resource::loadSssData(File *fp, const uint32_t baseOffset) { bytesRead += _sssHdr.preloadData3Count * 4; _sssPreload1Table.allocate(_sssHdr.preloadData1Count); - const bool is16Bits = (_sssHdr.version == 12); + const int ptrSize = (_sssHdr.version == 12) ? 2 : 1; for (int i = 0; i < _sssHdr.preloadData1Count; ++i) { - const int count = is16Bits ? fp->readUint16() : fp->readByte(); + const int count = (ptrSize == 2) ? fp->readUint16() : fp->readByte(); debug(kDebug_RESOURCE, "sssPreloadData1 #%d count %d", i, count); _sssPreload1Table[i].count = count; - const int tableSize = is16Bits ? count * 2 : count; + _sssPreload1Table[i].ptrSize = ptrSize; + const int tableSize = ptrSize * count; _sssPreload1Table[i].ptr = (uint8_t *)malloc(tableSize); fp->read(_sssPreload1Table[i].ptr, tableSize); - bytesRead += tableSize + (is16Bits ? 2 : 1); + bytesRead += tableSize + ptrSize; } for (int i = 0; i < _sssHdr.preloadData2Count; ++i) { const int count = fp->readByte(); @@ -869,7 +939,7 @@ void Resource::loadSssData(File *fp, const uint32_t baseOffset) { static const int kSizeOfPreloadInfoData_V10 = 32; for (int i = 0; i < _sssHdr.preloadInfoCount; ++i) { const int count = _sssPreloadInfosData[i].count; - _sssPreloadInfosData[i].data = (SssPreloadInfoData *)malloc(count * sizeof(SssPreloadInfoData)); + _sssPreloadInfosData[i].data = (SssPreloadInfoData *)calloc(count, sizeof(SssPreloadInfoData)); for (int j = 0; j < count; ++j) { SssPreloadInfoData *preloadInfoData = &_sssPreloadInfosData[i].data[j]; preloadInfoData->pcmBlockOffset = fp->readUint16(); @@ -896,23 +966,35 @@ void Resource::loadSssData(File *fp, const uint32_t baseOffset) { } } else if (_sssHdr.version == 6) { static const int kSizeOfPreloadInfoData_V6 = 68; + assert((unsigned int)_sssHdr.preloadInfoCount < kMaxScreens); + uint8_t buffer[kMaxScreens * kSizeOfPreloadInfoData_V6]; + for (int i = 0; i < _sssHdr.preloadInfoCount; ++i) { const int count = _sssPreloadInfosData[i].count; - uint8_t *p = (uint8_t *)malloc(kSizeOfPreloadInfoData_V6 * count); - fp->read(p, kSizeOfPreloadInfoData_V6 * count); + _sssPreloadInfosData[i].data = (SssPreloadInfoData *)calloc(count, sizeof(SssPreloadInfoData)); + + fp->read(buffer, kSizeOfPreloadInfoData_V6 * count); bytesRead += kSizeOfPreloadInfoData_V6 * count; + for (int j = 0; j < count; ++j) { - static const int8_t offsets[8] = { 0x2C, 0x30, 0x34, 0x04, 0x08, 0x0C, 0x10, 0x14 }; - static const int8_t lengths[8] = { 2, 1, 1, 2, 2, 1, 1, 1 }; - for (int k = 0; k < 8; ++k) { - const int len = READ_LE_UINT32(p + j * kSizeOfPreloadInfoData_V6 + offsets[k]) * lengths[k]; + SssPreloadInfoData *preloadInfoData = &_sssPreloadInfosData[i].data[j]; + preloadInfoData->screenNum = buffer[j * kSizeOfPreloadInfoData_V6]; + + // 0x2C is pcm preload list + preloadInfoData->preload1Data_V6.count = READ_LE_UINT32(buffer + j * kSizeOfPreloadInfoData_V6 + 0x2C); + preloadInfoData->preload1Data_V6.ptrSize = 2; + const int preload1DataLen = ((preloadInfoData->preload1Data_V6.count * 2) + 3) & ~3; + preloadInfoData->preload1Data_V6.ptr = (uint8_t *)malloc(preload1DataLen); + bytesRead += fp->read(preloadInfoData->preload1Data_V6.ptr, preload1DataLen); + + static const int8_t offsets[7] = { 0x30, 0x34, 0x04, 0x08, 0x0C, 0x10, 0x14 }; + static const int8_t lengths[7] = { 1, 1, 2, 2, 1, 1, 1 }; + for (int k = 0; k < 7; ++k) { + const int len = READ_LE_UINT32(buffer + j * kSizeOfPreloadInfoData_V6 + offsets[k]) * lengths[k]; bytesRead += skipBytesAlign(fp, len); } } - free(p); } - _sssPreloadInfosData.deallocate(); - _sssHdr.preloadInfoCount = 0; } _sssPcmTable.allocate(_sssHdr.pcmCount); @@ -933,6 +1015,15 @@ void Resource::loadSssData(File *fp, const uint32_t baseOffset) { _sssPcmTable[i].offset += sssPcmOffset; sssPcmOffset += _sssPcmTable[i].totalSize; } + if (_isPsx) { + assert(_sssPcmTable[i].strideSize == 512); + _sssPcmTable[i].pcmSize = (_sssPcmTable[i].totalSize / 16) * 56 * sizeof(int16_t); + } else { + assert(_sssPcmTable[i].strideSize == 2276 || _sssPcmTable[i].strideSize == 4040); + _sssPcmTable[i].pcmSize = (_sssPcmTable[i].totalSize - 256 * sizeof(int16_t) * _sssPcmTable[i].strideCount) * sizeof(int16_t); + } + } else { + _sssPcmTable[i].pcmSize = 0; } bytesRead += 20; } @@ -955,17 +1046,19 @@ void Resource::loadSssData(File *fp, const uint32_t baseOffset) { } const int lutSize = _sssHdr.banksDataCount * sizeof(uint32_t); + // allocate structures but skip read as tables are initialized in clearSoundObjects() + fp->seek(lutSize * 3 * 3, SEEK_CUR); + bytesRead += lutSize * 3 * 3; for (int i = 0; i < 3; ++i) { - // allocate structures but skip read as tables are initialized in clearSoundObjects() _sssGroup1[i] = (uint32_t *)malloc(lutSize); _sssGroup2[i] = (uint32_t *)malloc(lutSize); _sssGroup3[i] = (uint32_t *)malloc(lutSize); - fp->seek(lutSize * 3, SEEK_CUR); - bytesRead += lutSize * 3; } // _sssPreloadedPcmTotalSize = 0; - checkSssCode(_sssCodeData, _sssHdr.codeSize); + if (kCheckSssBytecode) { + checkSssCode(_sssCodeData, _sssHdr.codeSize); + } for (int i = 0; i < _sssHdr.banksDataCount; ++i) { if (_sssBanksData[i].count != 0) { // const int num = _sssBanksData[i].firstSampleIndex; @@ -979,11 +1072,12 @@ void Resource::loadSssData(File *fp, const uint32_t baseOffset) { if (bufferSize != bytesRead) { error("Unexpected number of bytes read %d (%d)", bytesRead, bufferSize); } - // preload PCM (_sssHdr.preloadPcmCount or setup.dat) - if (fp == _datFile) { + if (preloadPcm) { fp->seek(_sssPcmTable[0].offset, SEEK_SET); for (int i = 0; i < _sssHdr.pcmCount; ++i) { - loadSssPcm(fp, &_sssPcmTable[i]); + if (_sssPcmTable[i].pcmSize != 0) { + loadSssPcm(fp, &_sssPcmTable[i]); + } } } for (int i = 0; i < _sssHdr.banksDataCount; ++i) { @@ -1015,6 +1109,13 @@ void Resource::unloadSssData() { _sssPreload1Table[i].ptr = 0; } for (unsigned int i = 0; i < _sssPreloadInfosData.count; ++i) { + if (_sssHdr.version == 6) { + for (unsigned int j = 0; j < _sssPreloadInfosData[i].count; ++j) { + SssPreloadInfoData *preloadInfoData = &_sssPreloadInfosData[i].data[j]; + free(preloadInfoData->preload1Data_V6.ptr); + preloadInfoData->preload1Data_V6.ptr = 0; + } + } free(_sssPreloadInfosData[i].data); _sssPreloadInfosData[i].data = 0; } @@ -1073,61 +1174,55 @@ static void decodeSssSpuAdpcmUnit(const uint8_t *src, int16_t *dst) { // src: 16 const int s1 = (t1 << shift) + ((_pcmL0 * K0_1024[filter] + _pcmL1 * K1_1024[filter] + 512) >> 10); _pcmL1 = _pcmL0; _pcmL0 = s1; - dst[0] = dst[1] = CLIP(_pcmL0, -32768, 32767); + dst[0] = (_pcmL1 + _pcmL0) >> 1; + dst[1] = CLIP(_pcmL0, -32768, 32767); dst += 2; const int t2 = sext8(b >> 4, 4); const int s2 = (t2 << shift) + ((_pcmL0 * K0_1024[filter] + _pcmL1 * K1_1024[filter] + 512) >> 10); _pcmL1 = _pcmL0; _pcmL0 = s2; - dst[0] = dst[1] = CLIP(_pcmL0, -32768, 32767); + dst[0] = (_pcmL1 + _pcmL0) >> 1; + dst[1] = CLIP(_pcmL0, -32768, 32767); dst += 2; } } -uint32_t Resource::getSssPcmSize(const SssPcm *pcm) const { - if (_isPsx) { - assert(pcm->strideSize == 512); - const uint32_t size = pcm->strideCount * 512; - return (size / 16) * 56 * sizeof(int16_t); - } - return (pcm->strideSize - 256 * sizeof(int16_t)) * pcm->strideCount * sizeof(int16_t); -} - void Resource::loadSssPcm(File *fp, SssPcm *pcm) { assert(!pcm->ptr); - if (pcm->totalSize != 0) { - const uint32_t decompressedSize = getSssPcmSize(pcm); - debug(kDebug_SOUND, "Loading PCM %p decompressedSize %d", pcm, decompressedSize); - int16_t *p = (int16_t *)malloc(decompressedSize); - if (!p) { - warning("Failed to allocate %d bytes for PCM", decompressedSize); - return; - } - pcm->ptr = p; - if (_isPsx) { - for (int i = 0; i < pcm->strideCount; ++i) { - uint8_t buf[512]; - fp->read(buf, sizeof(buf)); - _pcmL1 = _pcmL0 = 0; - for (int j = 0; j < 512; j += 16) { - decodeSssSpuAdpcmUnit(buf + j, p); - p += 56; - } + const uint32_t decompressedSize = pcm->pcmSize; + debug(kDebug_SOUND, "Loading PCM %p decompressedSize %d", pcm, decompressedSize); + int16_t *p = (int16_t *)malloc(decompressedSize); + if (!p) { + warning("Failed to allocate %d bytes for PCM", decompressedSize); + return; + } + pcm->ptr = p; + if (_isPsx) { + assert(pcm->strideSize == 512); + uint8_t strideBuffer[512]; + for (int i = 0; i < pcm->strideCount; ++i) { + fp->read(strideBuffer, sizeof(strideBuffer)); + _pcmL1 = _pcmL0 = 0; + for (unsigned int j = 0; j < 512; j += 16) { + decodeSssSpuAdpcmUnit(strideBuffer + j, p); + p += 56; } - return; } + } else { if (fp != _datFile) { fp->seek(pcm->offset, SEEK_SET); } + const uint32_t strideSize = pcm->strideSize; + assert(strideSize == 2276 || strideSize == 4040); + uint8_t strideBuffer[4040]; // maximum stride size for (int i = 0; i < pcm->strideCount; ++i) { - uint8_t samples[256 * sizeof(int16_t)]; - fp->read(samples, sizeof(samples)); - for (unsigned int j = 256 * sizeof(int16_t); j < pcm->strideSize; ++j) { - *p++ = READ_LE_UINT16(samples + fp->readByte() * sizeof(int16_t)); + fp->read(strideBuffer, strideSize); + for (unsigned int j = 256 * sizeof(int16_t); j < strideSize; ++j) { + *p++ = READ_LE_UINT16(strideBuffer + strideBuffer[j] * sizeof(int16_t)); } } - assert((p - pcm->ptr) * sizeof(int16_t) == decompressedSize); } + assert((p - pcm->ptr) * sizeof(int16_t) == decompressedSize); } void Resource::clearSssGroup3() { @@ -1162,16 +1257,18 @@ void Resource::preloadSssPcmList(const SssPreloadInfoData *preloadInfoData) { } _lvlFile->seek(offset * 2048, SEEK_SET); fp = _lvlFile; + } else if (kPreloadSssPcm) { + return; } - const uint8_t num = preloadInfoData->preload1Index; - const SssPreloadList *preloadList = &_sssPreload1Table[num]; - const bool is16Bits = (_sssHdr.version == 12); + const SssPreloadList *preloadList = (_sssHdr.version == 6) ? &preloadInfoData->preload1Data_V6 : &_sssPreload1Table[preloadInfoData->preload1Index]; for (int i = 0; i < preloadList->count; ++i) { - const int num = is16Bits ? READ_LE_UINT16(preloadList->ptr + i * 2) : preloadList->ptr[i]; - if (!_sssPcmTable[num].ptr) { - loadSssPcm(fp, &_sssPcmTable[num]); - } else if (_isPsx) { - fp->seek(_sssPcmTable[num].strideCount * 512, SEEK_CUR); + const int num = (preloadList->ptrSize == 2) ? READ_LE_UINT16(preloadList->ptr + i * 2) : preloadList->ptr[i]; + if (_sssPcmTable[num].pcmSize != 0) { + if (!_sssPcmTable[num].ptr) { + loadSssPcm(fp, &_sssPcmTable[num]); + } else if (_isPsx) { + fp->seek(_sssPcmTable[num].strideCount * 512, SEEK_CUR); + } } } } @@ -1902,7 +1999,7 @@ static void persistSetupCfg(FILE *fp, T *config) { } persistUint8(fp, config->players[i].stereo); persistUint8(fp, config->players[i].volume); - persistUint8(fp, config->players[i].currentLevel); + persistUint8(fp, config->players[i].lastLevelNum); } persistUint8(fp, config->unkD0); persistUint8(fp, config->currentPlayer); @@ -1918,6 +2015,40 @@ static void persistSetupCfg(FILE *fp, T *config) { } } +static const uint32_t _demTag = 0x31434552; // 'REC1' + +bool Resource::loadHodDem() { + bool ret = false; + File f; + if (openDat(_fs, _hodDem, &f)) { + const uint32_t tag = f.readUint32(); + if (tag == _demTag) { + f.skipUint32(); + _dem.randSeed = f.readUint32(); + _dem.keyMaskLen = f.readUint32(); + _dem.level = f.readByte(); + _dem.checkpoint = f.readByte(); + _dem.difficulty = f.readByte(); + _dem.randRounds = f.readByte(); + f.skipUint32(); + f.skipUint32(); + _dem.actionKeyMask = (uint8_t *)malloc(_dem.keyMaskLen); + f.read(_dem.actionKeyMask, _dem.keyMaskLen); + _dem.directionKeyMask = (uint8_t *)malloc(_dem.keyMaskLen); + f.read(_dem.directionKeyMask, _dem.keyMaskLen); + ret = true; + } + closeDat(_fs, &f); + } + return ret; +} + +void Resource::unloadHodDem() { + free(_dem.actionKeyMask); + free(_dem.directionKeyMask); + memset(&_dem, 0, sizeof(_dem)); +} + bool Resource::writeSetupCfg(const SetupConfig *config) { FILE *fp = _fs->openSaveFile(_setupCfg, true); if (fp) { diff --git a/resource.h b/resource.h index a858b7c..7d56433 100644 --- a/resource.h +++ b/resource.h @@ -446,7 +446,8 @@ struct SssSample { struct SssPreloadList { int count; - uint8_t *ptr; // uint16_t for v12 + int ptrSize; + uint8_t *ptr; // uint16_t for v6 or v12, uint8_t for v10 }; struct SssPreloadInfoData { @@ -457,6 +458,7 @@ struct SssPreloadInfoData { uint8_t preload1Index; uint8_t preload2Index; uint32_t unk1C; + SssPreloadList preload1Data_V6; }; // sizeof == 32 (v10,v12) 68 (v6) struct SssPreloadInfo { @@ -485,6 +487,7 @@ struct SssPcm { uint32_t strideSize; // 12 uint16_t strideCount; // 16 uint16_t flags; // 18 1:stereo + uint32_t pcmSize; // decompressed PCM size in bytes }; struct SssUnk6 { @@ -492,6 +495,17 @@ struct SssUnk6 { uint32_t mask; // 10 }; +struct Dem { + uint32_t randSeed; + uint32_t keyMaskLen; + uint8_t level; + uint8_t checkpoint; + uint8_t difficulty; + uint8_t randRounds; + uint8_t *actionKeyMask; + uint8_t *directionKeyMask; +}; + template struct ResStruct { T *ptr; @@ -528,6 +542,11 @@ struct ResStruct { struct FileSystem; struct Resource { + enum { + V1_0, + V1_1, + V1_2 // 1.2 or newer + }; FileSystem *_fs; @@ -541,6 +560,8 @@ struct Resource { File *_sssFile; bool _isPsx; + bool _isDemo; + int _version; uint8_t *_loadingImageBuffer; uint8_t *_fontBuffer; @@ -548,22 +569,28 @@ struct Resource { uint8_t *_menuBuffer1; uint32_t _menuBuffersOffset; + Dem _dem; + uint32_t _demOffset; + uint8_t _currentScreenResourceNum; uint8_t _screensGrid[kMaxScreens][4]; LvlScreenVector _screensBasePos[kMaxScreens]; LvlScreenState _screensState[kMaxScreens]; - uint8_t *_resLevelData0x470CTable; + uint8_t *_resLevelData0x470CTable; // masks uint8_t *_resLevelData0x470CTablePtrHdr; uint8_t *_resLevelData0x470CTablePtrData; - uint32_t _lvlSssOffset; - uint32_t _resLevelData0x2B88SizeTable[kMaxScreens]; - uint32_t _resLevelData0x2988SizeTable[kMaxSpriteTypes]; - LvlObjectData _resLevelData0x2988Table[kMaxScreens]; + uint32_t _lvlSpritesOffset; + uint32_t _lvlBackgroundsOffset; + uint32_t _lvlMasksOffset; + uint32_t _lvlSssOffset; // .sss offset (PSX) + uint32_t _resLevelData0x2988SizeTable[kMaxSpriteTypes]; // sprites + LvlObjectData _resLevelData0x2988Table[kMaxSpriteTypes]; LvlObjectData *_resLevelData0x2988PtrTable[kMaxSpriteTypes]; + uint8_t *_resLvlSpriteDataPtrTable[kMaxSpriteTypes]; + uint32_t _resLevelData0x2B88SizeTable[kMaxScreens]; // backgrounds LvlBackgroundData _resLvlScreenBackgroundDataTable[kMaxScreens]; uint8_t *_resLvlScreenBackgroundDataPtrTable[kMaxScreens]; - uint8_t *_resLvlSpriteDataPtrTable[kMaxSpriteTypes]; LvlObject _resLvlScreenObjectDataTable[104]; LvlObject _dummyObject; // (LvlObject *)0xFFFFFFFF @@ -633,11 +660,11 @@ struct Resource { void loadLvlScreenObjectData(LvlObject *dat, const uint8_t *src); void loadLvlData(File *fp); void unloadLvlData(); - void loadLvlSpriteData(int num); + void loadLvlSpriteData(int num, const uint8_t *buf = 0); const uint8_t *getLvlScreenMaskDataPtr(int num) const; const uint8_t *getLvlScreenPosDataPtr(int num) const; void loadLvlScreenMaskData(); - void loadLvlScreenBackgroundData(int num); + void loadLvlScreenBackgroundData(int num, const uint8_t *buf = 0); void unloadLvlScreenBackgroundData(int num); bool isLvlSpriteDataLoaded(int num) const; bool isLvlBackgroundDataLoaded(int num) const; @@ -651,7 +678,6 @@ struct Resource { void unloadSssData(); void checkSssCode(const uint8_t *buf, int size) const; void loadSssPcm(File *fp, SssPcm *pcm); - uint32_t getSssPcmSize(const SssPcm *pcm) const; void clearSssGroup3(); void resetSssFilters(); void preloadSssPcmList(const SssPreloadInfoData *preloadInfoData); @@ -661,6 +687,9 @@ struct Resource { const MstScreenArea *findMstCodeForPos(int num, int xPos, int yPos) const; void flagMstCodeForPos(int num, uint8_t value); + bool loadHodDem(); + void unloadHodDem(); + bool writeSetupCfg(const SetupConfig *config); bool readSetupCfg(SetupConfig *config); }; diff --git a/sound.cpp b/sound.cpp index c85d3b5..6e9eb12 100644 --- a/sound.cpp +++ b/sound.cpp @@ -62,7 +62,7 @@ static bool compareSssGroup(uint32_t flags_a, uint32_t flags_b) { return false; } // we can instead simply compare masked integers - return (flags_a & 0xFFF00FFF) == (flags_b & 0xFFF00FFF); + return compare_bits(flags_a, flags_b, 0xFFF00FFF); } // returns the active samples for the table/source/bank @@ -337,7 +337,7 @@ void Game::sssOp4_removeSounds(uint32_t flags) { const uint32_t mask = 1 << (flags >> 24); *getSssGroupPtr(_res, 1, flags) &= ~mask; for (SssObject *so = _sssObjectsList1; so; so = so->nextPtr) { - if (((so->flags1 ^ flags) & 0xFFFF0FFF) == 0) { // (a & m) == (b & m) + if (compare_bits(so->flags1, flags, 0xFFFF0FFF)) { so->codeDataStage3 = 0; if (so->codeDataStage4 == 0) { removeSoundObjectFromList(so); @@ -347,7 +347,7 @@ void Game::sssOp4_removeSounds(uint32_t flags) { } } for (SssObject *so = _sssObjectsList2; so; so = so->nextPtr) { - if (((so->flags1 ^ flags) & 0xFFFF0FFF) == 0) { + if (compare_bits(so->flags1, flags, 0xFFFF0FFF)) { so->codeDataStage3 = 0; if (so->codeDataStage4 == 0) { removeSoundObjectFromList(so); @@ -770,8 +770,7 @@ SssObject *Game::createSoundObject(int bankIndex, int sampleIndex, uint32_t flag return 0; } const int firstSampleIndex = bank->firstSampleIndex; - assert(firstSampleIndex >= 0 && firstSampleIndex < _res->_sssHdr.samplesDataCount); - SssSample *sample = &_res->_sssSamplesData[firstSampleIndex]; + const SssSample *sample = &_res->_sssSamplesData[firstSampleIndex]; int framesCount = 0; for (int i = 0; i < bank->count; ++i) { if (sample->pcm != 0xFFFF) { @@ -833,14 +832,8 @@ SssObject *Game::startSoundObject(int bankIndex, int sampleIndex, uint32_t flags SssBank *bank = &_res->_sssBanksData[bankIndex]; const int sampleNum = bank->firstSampleIndex + sampleIndex; debug(kDebug_SOUND, "startSoundObject sample %d", sampleNum); - assert(sampleNum >= 0 && sampleNum < _res->_sssHdr.samplesDataCount); - SssSample *sample = &_res->_sssSamplesData[sampleNum]; - - // original preloads PCM when changing screens + const SssSample *sample = &_res->_sssSamplesData[sampleNum]; SssPcm *pcm = &_res->_sssPcmTable[sample->pcm]; - if (!pcm->ptr && !_res->_isPsx) { - _res->loadSssPcm(_res->_sssFile, pcm); - } if (sample->framesCount != 0) { SssFilter *filter = &_res->_sssFilters[bank->sssFilter]; @@ -1122,7 +1115,7 @@ void Game::expireSoundObjects(uint32_t flags) { *getSssGroupPtr(_res, 1, flags) &= ~mask; *getSssGroupPtr(_res, 2, flags) &= ~mask; for (SssObject *so = _sssObjectsList1; so; so = so->nextPtr) { - if (((so->flags0 ^ flags) & 0xFFFF0FFF) == 0) { + if (compare_bits(so->flags0, flags, 0xFFFF0FFF)) { so->codeDataStage3 = 0; if (so->codeDataStage4 == 0) { removeSoundObjectFromList(so); @@ -1132,7 +1125,7 @@ void Game::expireSoundObjects(uint32_t flags) { } } for (SssObject *so = _sssObjectsList2; so; so = so->nextPtr) { - if (((so->flags0 ^ flags) & 0xFFFF0FFF) == 0) { + if (compare_bits(so->flags0, flags, 0xFFFF0FFF)) { so->codeDataStage3 = 0; if (so->codeDataStage4 == 0) { removeSoundObjectFromList(so); @@ -1196,7 +1189,7 @@ void Game::queueSoundObjectsPcmStride() { if (so->currentPcmPtr < ptr) { continue; } - const uint32_t pcmSize = _res->getSssPcmSize(pcm) / sizeof(int16_t); + const uint32_t pcmSize = pcm->pcmSize / sizeof(int16_t); const int16_t *end = ptr + pcmSize; if (so->currentPcmPtr >= end) { continue; diff --git a/system.h b/system.h index 2695f14..80796a3 100644 --- a/system.h +++ b/system.h @@ -28,13 +28,10 @@ struct PlayerInput { bool keyReleased(int keyMask) const { return (prevMask & keyMask) == keyMask && (mask & keyMask) == 0; } - void flush() { - prevMask = mask = 0; - } }; struct AudioCallback { - void (*proc)(void *param, int16_t *stream, int len); + void (*proc)(void *param, int16_t *stream, int len); // 22khz void *userdata; }; @@ -63,7 +60,6 @@ struct System { virtual void startAudio(AudioCallback callback) = 0; virtual void stopAudio() = 0; - virtual uint32_t getOutputSampleRate() = 0; virtual void lockAudio() = 0; virtual void unlockAudio() = 0; virtual AudioCallback setAudioCallback(AudioCallback callback) = 0; diff --git a/system_psp.cpp b/system_psp.cpp new file mode 100644 index 0000000..d41dded --- /dev/null +++ b/system_psp.cpp @@ -0,0 +1,417 @@ + +#include +#include +#include +#include +#include +#include +#include +#include "system.h" + +struct System_PSP : System { + + uint32_t _buttons; + int _shakeDx, _shakeDy; + bool _stretchScreen; + AudioCallback _audioCb; + int _audioChannel; + SceUID _audioMutex; + uint32_t _vramOffset; + + System_PSP(); + virtual ~System_PSP(); + virtual void init(const char *title, int w, int h, bool fullscreen, bool widescreen, bool yuv); + virtual void destroy(); + virtual void setScaler(const char *name, int multiplier); + virtual void setGamma(float gamma); + virtual void setPalette(const uint8_t *pal, int n, int depth); + virtual void copyRect(int x, int y, int w, int h, const uint8_t *buf, int pitch); + virtual void copyYuv(int w, int h, const uint8_t *y, int ypitch, const uint8_t *u, int upitch, const uint8_t *v, int vpitch); + virtual void fillRect(int x, int y, int w, int h, uint8_t color); + virtual void copyRectWidescreen(int w, int h, const uint8_t *buf, const uint8_t *pal); + virtual void shakeScreen(int dx, int dy); + virtual void updateScreen(bool drawWidescreen); + virtual void processEvents(); + virtual void sleep(int duration); + virtual uint32_t getTimeStamp(); + virtual void startAudio(AudioCallback callback); + virtual void stopAudio(); + virtual void lockAudio(); + virtual void unlockAudio(); + virtual AudioCallback setAudioCallback(AudioCallback callback); +}; + +static System_PSP system_psp; +System *const g_system = &system_psp; + +// static const int AUDIO_FREQ = 44100; +static const int AUDIO_SAMPLES_COUNT = 2048; + +static const int SCREEN_W = 480; +static const int SCREEN_H = 272; +static const int SCREEN_PITCH = 512; + +static const int GAME_W = 256; +static const int GAME_H = 192; + +static const int BLUR_TEX_W = 16; +static const int BLUR_TEX_H = 16; + +static uint32_t WHITE_COLOR = GU_RGBA(255, 255, 255, 255); + +static uint32_t __attribute__((aligned(16))) _dlist[1024]; +static uint32_t __attribute__((aligned(16))) _clut[256]; +static uint8_t __attribute__((aligned(16))) _texture[256 * 256]; +static uint32_t __attribute__((aligned(16))) _clut2[256]; +static uint8_t __attribute__((aligned(16))) _texture2[256 * 256]; + +PSP_MODULE_INFO("Heart of Darkness", 0, 2, 8); +PSP_MAIN_THREAD_ATTR(THREAD_ATTR_USER); + +struct Vertex { + uint16_t u, v; + int16_t x, y, z; +}; + +static void initVertex2D(Vertex *vertices, int w, int h, int targetW, int targetH) { + vertices[0].u = 0; vertices[1].u = w; + vertices[0].v = 0; vertices[1].v = h; + vertices[0].x = 0; vertices[1].x = targetW; + vertices[0].y = 0; vertices[1].y = targetH; + vertices[0].z = 0; vertices[1].z = 0; +} + +void System_printLog(FILE *fp, const char *s) { + if (fp == stderr) { + static bool firstOpen = false; + if (!firstOpen) { + fp = fopen("stderr.txt", "w"); + firstOpen = true; + } else { + fp = fopen("stderr.txt", "a"); + } + } else if (fp == stdout) { + static bool firstOpen = false; + if (!firstOpen) { + fp = fopen("stdout.txt", "w"); + firstOpen = true; + } else { + fp = fopen("stdout.txt", "a"); + } + } else { + return; + } + fprintf(fp, "%s\n", s); + fclose(fp); +} + +void System_fatalError(const char *s) { + sceKernelExitGame(); +} + +static int exitCallback(int arg1, int arg2, void *common) { + g_system->inp.quit = true; + return 0; +} + +static int callbackThread(SceSize args, void *argp) { + const int cb = sceKernelCreateCallback("Exit Callback", exitCallback, NULL); + sceKernelRegisterExitCallback(cb); + sceKernelSleepThreadCB(); + return 0; +} + +System_PSP::System_PSP() { +} + +System_PSP::~System_PSP() { +} + +void System_PSP::init(const char *title, int w, int h, bool fullscreen, bool widescreen, bool yuv) { + + memset(&inp, 0, sizeof(inp)); + memset(&pad, 0, sizeof(pad)); + _buttons = 0; + _shakeDx = _shakeDy = 0; + _stretchScreen = false; // keep 4:3 AR + + memset(&_audioCb, 0, sizeof(_audioCb)); + _audioChannel = -1; + _audioMutex = 0; + + const int th = sceKernelCreateThread("update_thread", callbackThread, 0x11, 0xFA0, 0, 0); + if (th >= 0) { + sceKernelStartThread(th, 0, 0); + } + + sceKernelDcacheWritebackAll(); + + sceCtrlSetSamplingCycle(0); + sceCtrlSetSamplingMode(PSP_CTRL_MODE_ANALOG); + + sceGuInit(); + sceGuStart(GU_DIRECT, _dlist); + + const int fbSize = SCREEN_PITCH * SCREEN_H * sizeof(uint32_t); // rgba + const int zbSize = SCREEN_PITCH * SCREEN_H * sizeof(uint16_t); // 16 bits + _vramOffset = 0; + sceGuDrawBuffer(GU_PSM_8888, (void *)_vramOffset, SCREEN_PITCH); _vramOffset += fbSize; + sceGuDispBuffer(SCREEN_W, SCREEN_H, (void *)_vramOffset, SCREEN_PITCH); _vramOffset += fbSize; + sceGuDepthBuffer((void *)_vramOffset, SCREEN_PITCH); _vramOffset += zbSize; + + sceGuOffset(2048 - (SCREEN_W / 2), 2048 - (SCREEN_H / 2)); + sceGuViewport(2048, 2048, SCREEN_W, SCREEN_H); + sceGuScissor(0, 0, SCREEN_W, SCREEN_H); + sceGuEnable(GU_SCISSOR_TEST); + sceGuEnable(GU_TEXTURE_2D); + sceGuClear(GU_COLOR_BUFFER_BIT | GU_DEPTH_BUFFER_BIT); + + sceGuFinish(); + sceGuSync(GU_SYNC_WHAT_DONE, GU_SYNC_FINISH); + + sceDisplayWaitVblankStart(); + sceGuDisplay(GU_TRUE); +} + +void System_PSP::destroy() { + sceGuTerm(); + sceKernelExitGame(); +} + +void System_PSP::setScaler(const char *name, int multiplier) { +} + +void System_PSP::setGamma(float gamma) { +} + +void System_PSP::setPalette(const uint8_t *pal, int n, int depth) { + const int shift = 8 - depth; + for (int i = 0; i < n; ++i) { + int r = pal[i * 3]; + int g = pal[i * 3 + 1]; + int b = pal[i * 3 + 2]; + if (shift != 0) { + r = (r << shift) | (r >> depth); + g = (g << shift) | (g >> depth); + b = (b << shift) | (b >> depth); + } + _clut[i] = GU_RGBA(r, g, b, 255); + } + sceKernelDcacheWritebackRange(_clut, sizeof(_clut)); +} + +void System_PSP::copyRect(int x, int y, int w, int h, const uint8_t *buf, int pitch) { + uint8_t *p = _texture + y * GAME_W + x; + if (w == GAME_W && w == pitch) { + memcpy(p, buf, w * h); + } else { + for (int i = 0; i < h; ++i) { + memcpy(p, buf, w); + p += GAME_W; + buf += pitch; + } + } + sceKernelDcacheWritebackRange(_texture, sizeof(_texture)); +} + +void System_PSP::copyYuv(int w, int h, const uint8_t *y, int ypitch, const uint8_t *u, int upitch, const uint8_t *v, int vpitch) { +} + +void System_PSP::fillRect(int x, int y, int w, int h, uint8_t color) { + uint8_t *p = _texture + y * GAME_W + x; + if (w == GAME_W) { + memset(p, color, w * h); + } else { + for (int i = 0; i < h; ++i) { + memset(p, color, w); + p += GAME_W; + } + } + sceKernelDcacheWritebackRange(_texture, sizeof(_texture)); +} + +void System_PSP::copyRectWidescreen(int w, int h, const uint8_t *buf, const uint8_t *pal) { + for (int i = 0; i < 256; ++i) { + _clut2[i] = GU_RGBA(pal[i * 3], pal[i * 3 + 1], pal[i * 3 + 2], 255); + } + sceKernelDcacheWritebackRange(_clut2, sizeof(_clut2)); + memcpy(_texture2, buf, w * h); + sceKernelDcacheWritebackRange(_texture2, sizeof(_texture2)); + + sceGuStart(GU_DIRECT, _dlist); + + sceGuDrawBufferList(GU_PSM_8888, (void *)_vramOffset, BLUR_TEX_W); + sceGuOffset(2048 - (BLUR_TEX_W / 2), 2048 - (BLUR_TEX_H / 2)); + sceGuViewport(2048, 2048, BLUR_TEX_W, BLUR_TEX_H); + sceGuClearColor(WHITE_COLOR); + sceGuClear(GU_COLOR_BUFFER_BIT); + + sceGuClutMode(GU_PSM_8888, 0, 0xFF, 0); + sceGuClutLoad(256 / 8, _clut2); + sceGuTexMode(GU_PSM_T8, 0, 0, 0); + sceGuTexImage(0, 256, 256, 256, _texture2); + sceGuTexFunc(GU_TFX_REPLACE, GU_TCC_RGB); + sceGuTexFilter(GU_LINEAR, GU_LINEAR); + + Vertex *vertices = (Vertex *)sceGuGetMemory(2 * sizeof(struct Vertex)); + initVertex2D(vertices, GAME_W, GAME_H, BLUR_TEX_W, BLUR_TEX_H); + sceGuDrawArray(GU_SPRITES, GU_TEXTURE_16BIT | GU_VERTEX_16BIT | GU_TRANSFORM_2D, 2, 0, vertices); + + sceGuFinish(); +} + +void System_PSP::shakeScreen(int dx, int dy) { + _shakeDx = dx; + _shakeDy = dy; +} + +void System_PSP::updateScreen(bool drawWidescreen) { + + sceGuStart(GU_DIRECT, _dlist); + + sceGuClearColor(0); + sceGuClear(GU_COLOR_BUFFER_BIT); + + if (!_stretchScreen && drawWidescreen) { + + sceGuTexMode(GU_PSM_8888, 0, 0, 0); + sceGuTexImage(0, BLUR_TEX_W, BLUR_TEX_H, BLUR_TEX_W, (uint8_t *)sceGeEdramGetAddr() + _vramOffset); + sceGuTexFunc(GU_TFX_REPLACE, GU_TCC_RGB); + sceGuTexFilter(GU_LINEAR, GU_LINEAR); + + Vertex *vertices = (Vertex *)sceGuGetMemory(2 * sizeof(Vertex)); + initVertex2D(vertices, BLUR_TEX_W, BLUR_TEX_H, SCREEN_W, SCREEN_H); + sceGuDrawArray(GU_SPRITES, GU_TEXTURE_16BIT | GU_VERTEX_16BIT | GU_TRANSFORM_2D, 2, 0, vertices); + } + + sceGuClutMode(GU_PSM_8888, 0, 0xFF, 0); + sceGuClutLoad(256 / 8, _clut); + sceGuTexMode(GU_PSM_T8, 0, 0, 0); + sceGuTexImage(0, 256, 256, 256, _texture); + sceGuTexFunc(GU_TFX_REPLACE, GU_TCC_RGB); + sceGuTexFilter(GU_LINEAR, GU_LINEAR); + + Vertex *vertices = (Vertex *)sceGuGetMemory(2 * sizeof(Vertex)); + initVertex2D(vertices, GAME_W, GAME_H, SCREEN_W, SCREEN_H); + vertices[0].x += _shakeDx; + vertices[1].x += _shakeDx; + vertices[0].y += _shakeDy; + vertices[1].y += _shakeDy; + if (!_stretchScreen) { + const int w = (SCREEN_H * GAME_W / GAME_H); + const int dx = (SCREEN_W - w) / 2; + vertices[0].x += dx; + vertices[1].x -= dx; + } + sceGuDrawArray(GU_SPRITES, GU_TEXTURE_16BIT | GU_VERTEX_16BIT | GU_TRANSFORM_2D, 2, 0, vertices); + + sceGuFinish(); + sceGuSync(GU_SYNC_WHAT_DONE, GU_SYNC_FINISH); + + sceDisplayWaitVblankStart(); + sceGuSwapBuffers(); +} + +void System_PSP::processEvents() { + inp.prevMask = inp.mask; + inp.mask = 0; + + static const struct { + int psp; + int sys; + } mapping[] = { + { PSP_CTRL_UP, SYS_INP_UP }, + { PSP_CTRL_RIGHT, SYS_INP_RIGHT }, + { PSP_CTRL_DOWN, SYS_INP_DOWN }, + { PSP_CTRL_LEFT, SYS_INP_LEFT }, + { PSP_CTRL_CROSS, SYS_INP_JUMP }, + { PSP_CTRL_SQUARE, SYS_INP_RUN }, + { PSP_CTRL_CIRCLE, SYS_INP_SHOOT }, + { PSP_CTRL_TRIANGLE, SYS_INP_SHOOT | SYS_INP_RUN }, + { PSP_CTRL_START, SYS_INP_ESC }, + { 0, 0 } + }; + SceCtrlData data; + sceCtrlPeekBufferPositive(&data, 1); + for (int i = 0; mapping[i].psp != 0; ++i) { + if (data.Buttons & mapping[i].psp) { + inp.mask |= mapping[i].sys; + } + } + static const int lxMargin = 64; + if (data.Lx < 127 - lxMargin) { + inp.mask |= SYS_INP_LEFT; + } else if (data.Lx > 127 + lxMargin) { + inp.mask |= SYS_INP_RIGHT; + } + static const int lyMargin = 64; + if (data.Ly < 127 - lyMargin) { + inp.mask |= SYS_INP_UP; + } else if (data.Ly > 127 + lyMargin) { + inp.mask |= SYS_INP_DOWN; + } + + const uint32_t mask = data.Buttons ^ _buttons; + if ((data.Buttons & PSP_CTRL_LTRIGGER) & mask) { + _stretchScreen = !_stretchScreen; + } + if ((data.Buttons & PSP_CTRL_RTRIGGER) & mask) { + } + _buttons = data.Buttons; +} + +void System_PSP::sleep(int duration) { + sceKernelDelayThread(duration * 1000); +} + +uint32_t System_PSP::getTimeStamp() { + struct timeval tv; + sceKernelLibcGettimeofday(&tv, 0); + return tv.tv_sec * 1000 + tv.tv_usec / 1000; +} + +static void audioCallback(void *buf, unsigned int samples, void *userdata) { // 44100hz S16 stereo + int16_t buf22khz[samples]; + memset(buf22khz, 0, sizeof(buf22khz)); + system_psp.lockAudio(); + (system_psp._audioCb.proc)(system_psp._audioCb.userdata, buf22khz, samples); + system_psp.unlockAudio(); + uint32_t *buf44khz = (uint32_t *)buf; + static int16_t prev; + for (unsigned int i = 0; i < samples; ++i) { + const int16_t current = buf22khz[i]; + buf44khz[i] = (current << 16) | (((prev + current) >> 1) & 0xFFFF); + prev = current; + } +} + +void System_PSP::startAudio(AudioCallback callback) { + // sceAudioSetFrequency(AUDIO_FREQ); + pspAudioInit(); + _audioCb = callback; + _audioMutex = sceKernelCreateSema("audio_lock", 0, 1, 1, 0); + _audioChannel = sceAudioChReserve(PSP_AUDIO_NEXT_CHANNEL, AUDIO_SAMPLES_COUNT, PSP_AUDIO_FORMAT_STEREO); + pspAudioSetChannelCallback(_audioChannel, audioCallback, 0); +} + +void System_PSP::stopAudio() { + sceAudioChRelease(_audioChannel); + sceKernelDeleteSema(_audioMutex); + pspAudioEnd(); +} + +void System_PSP::lockAudio() { + sceKernelWaitSema(_audioMutex, 1, 0); +} + +void System_PSP::unlockAudio() { + sceKernelSignalSema(_audioMutex, 1); +} + +AudioCallback System_PSP::setAudioCallback(AudioCallback callback) { + AudioCallback cb = _audioCb; + lockAudio(); + _audioCb = callback; + unlockAudio(); + return cb; +} diff --git a/system_sdl2.cpp b/system_sdl2.cpp index 55b847f..0e24341 100644 --- a/system_sdl2.cpp +++ b/system_sdl2.cpp @@ -74,7 +74,6 @@ struct System_SDL2 : System { virtual void startAudio(AudioCallback callback); virtual void stopAudio(); - virtual uint32_t getOutputSampleRate(); virtual void lockAudio(); virtual void unlockAudio(); virtual AudioCallback setAudioCallback(AudioCallback callback); @@ -88,6 +87,11 @@ struct System_SDL2 : System { static System_SDL2 system_sdl2; System *const g_system = &system_sdl2; +void System_fatalError(const char *s) { + SDL_ShowSimpleMessageBox(SDL_MESSAGEBOX_ERROR, "Heart of Darkness", s, system_sdl2._window); + exit(-1); +} + System_SDL2::System_SDL2() : _offscreenLut(0), _offscreenRgb(0), _window(0), _renderer(0), _texture(0), _backgroundTexture(0), _fmt(0), _widescreenTexture(0), @@ -133,7 +137,7 @@ void System_SDL2::init(const char *title, int w, int h, bool fullscreen, bool wi } _joystick = SDL_JoystickOpen(i); if (_joystick) { - fprintf(stdout, "Using joystick '%s'", SDL_JoystickName(_joystick)); + fprintf(stdout, "Using joystick '%s'\n", SDL_JoystickName(_joystick)); break; } } @@ -326,10 +330,14 @@ void System_SDL2::setPalette(const uint8_t *pal, int n, int depth) { void System_SDL2::copyRect(int x, int y, int w, int h, const uint8_t *buf, int pitch) { assert(x >= 0 && x + w <= _screenW && y >= 0 && y + h <= _screenH); - for (int i = 0; i < h; ++i) { - memcpy(_offscreenLut + y * _screenW + x, buf, w); - buf += pitch; - ++y; + if (w == pitch && w == _screenW) { + memcpy(_offscreenLut + y * _screenW + x, buf, w * h); + } else { + for (int i = 0; i < h; ++i) { + memcpy(_offscreenLut + y * _screenW + x, buf, w); + buf += pitch; + ++y; + } } } @@ -341,9 +349,13 @@ void System_SDL2::copyYuv(int w, int h, const uint8_t *y, int ypitch, const uint void System_SDL2::fillRect(int x, int y, int w, int h, uint8_t color) { assert(x >= 0 && x + w <= _screenW && y >= 0 && y + h <= _screenH); - for (int i = 0; i < h; ++i) { - memset(_offscreenLut + y * _screenW + x, color, w); - ++y; + if (w == _screenW) { + memset(_offscreenLut + y * _screenW + x, color, w * h); + } else { + for (int i = 0; i < h; ++i) { + memset(_offscreenLut + y * _screenW + x, color, w); + ++y; + } } } @@ -440,6 +452,21 @@ void System_SDL2::processEvents() { inp.screenshot = true; } break; + case SDL_JOYDEVICEADDED: + if (!_joystick) { + _joystick = SDL_JoystickOpen(ev.jdevice.which); + if (_joystick) { + fprintf(stdout, "Using joystick '%s'\n", SDL_JoystickName(_joystick)); + } + } + break; + case SDL_JOYDEVICEREMOVED: + if (_joystick == SDL_JoystickFromInstanceID(ev.jdevice.which)) { + fprintf(stdout, "Removed joystick '%s'\n", SDL_JoystickName(_joystick)); + SDL_JoystickClose(_joystick); + _joystick = 0; + } + break; case SDL_JOYHATMOTION: if (_joystick) { pad.mask &= ~(SYS_INP_UP | SYS_INP_DOWN | SYS_INP_LEFT | SYS_INP_RIGHT); @@ -505,9 +532,31 @@ void System_SDL2::processEvents() { pad.mask &= ~SYS_INP_SHOOT; } break; + case 3: + if (pressed) { + pad.mask |= SYS_INP_SHOOT | SYS_INP_RUN; + } else { + pad.mask &= ~(SYS_INP_SHOOT | SYS_INP_RUN); + } + break; } } break; + case SDL_CONTROLLERDEVICEADDED: + if (!_controller) { + _controller = SDL_GameControllerOpen(ev.cdevice.which); + if (_controller) { + fprintf(stdout, "Using controller '%s'\n", SDL_GameControllerName(_controller)); + } + } + break; + case SDL_CONTROLLERDEVICEREMOVED: + if (_controller == SDL_GameControllerFromInstanceID(ev.cdevice.which)) { + fprintf(stdout, "Removed controller '%s'\n", SDL_GameControllerName(_controller)); + SDL_GameControllerClose(_controller); + _controller = 0; + } + break; case SDL_CONTROLLERAXISMOTION: if (_controller) { switch (ev.caxis.axis) { @@ -567,6 +616,11 @@ void System_SDL2::processEvents() { } break; case SDL_CONTROLLER_BUTTON_Y: + if (pressed) { + pad.mask |= SYS_INP_SHOOT | SYS_INP_RUN; + } else { + pad.mask &= ~(SYS_INP_SHOOT | SYS_INP_RUN); + } break; case SDL_CONTROLLER_BUTTON_BACK: case SDL_CONTROLLER_BUTTON_START: @@ -606,7 +660,6 @@ void System_SDL2::processEvents() { case SDL_QUIT: inp.quit = true; break; - } } updateKeys(&inp); @@ -621,9 +674,8 @@ uint32_t System_SDL2::getTimeStamp() { } static void mixAudioS16(void *param, uint8_t *buf, int len) { - System_SDL2 *stub = (System_SDL2 *)param; memset(buf, 0, len); - stub->_audioCb.proc(stub->_audioCb.userdata, (int16_t *)buf, len / 2); + system_sdl2._audioCb.proc(system_sdl2._audioCb.userdata, (int16_t *)buf, len / 2); } void System_SDL2::startAudio(AudioCallback callback) { @@ -647,10 +699,6 @@ void System_SDL2::stopAudio() { SDL_CloseAudio(); } -uint32_t System_SDL2::getOutputSampleRate() { - return kAudioHz; -} - void System_SDL2::lockAudio() { SDL_LockAudio(); } @@ -701,7 +749,7 @@ void System_SDL2::setupDefaultKeyMappings() { addKeyMapping(SDL_SCANCODE_RETURN, SYS_INP_JUMP); addKeyMapping(SDL_SCANCODE_LCTRL, SYS_INP_RUN); addKeyMapping(SDL_SCANCODE_F, SYS_INP_RUN); -// addKeyMapping(SDL_SCANCODE_LALT, SYS_INP_JUMP); + addKeyMapping(SDL_SCANCODE_LALT, SYS_INP_JUMP); addKeyMapping(SDL_SCANCODE_G, SYS_INP_JUMP); addKeyMapping(SDL_SCANCODE_LSHIFT, SYS_INP_SHOOT); addKeyMapping(SDL_SCANCODE_H, SYS_INP_SHOOT); @@ -742,7 +790,7 @@ void System_SDL2::prepareScaledGfx(const char *caption, bool fullscreen, bool wi } _renderer = SDL_CreateRenderer(_window, -1, SDL_RENDERER_ACCELERATED); SDL_RenderSetLogicalSize(_renderer, windowW, windowH); - SDL_SetHint(SDL_HINT_RENDER_SCALE_QUALITY, "1"); + SDL_SetHint(SDL_HINT_RENDER_SCALE_QUALITY, (_scaler == &scaler_nearest) ? "0" : "1"); const int pixelFormat = yuv ? SDL_PIXELFORMAT_RGBA8888 : SDL_PIXELFORMAT_RGB888; _texture = SDL_CreateTexture(_renderer, pixelFormat, SDL_TEXTUREACCESS_STREAMING, _texW, _texH); diff --git a/system_wii.cpp b/system_wii.cpp new file mode 100644 index 0000000..c83d09a --- /dev/null +++ b/system_wii.cpp @@ -0,0 +1,485 @@ + +#include +#include + +#include +#include +#include +#include +#include +#include + +#include "system.h" + +struct System_Wii : System { + + int _shakeDx, _shakeDy; + uint64_t _startTime; + AudioCallback _audioCb; + mutex_t _audioMutex; + lwpq_t _audioQueue; + lwp_t _audioThread; + bool _audioOut; + GXRModeObj *_rmodeObj; + GXTlutObj _tlutObj; + GXTexObj _texObj; + int _projTop, _projLeft; + int _gamma; + + System_Wii(); + virtual ~System_Wii(); + virtual void init(const char *title, int w, int h, bool fullscreen, bool widescreen, bool yuv); + virtual void destroy(); + virtual void setScaler(const char *name, int multiplier); + virtual void setGamma(float gamma); + virtual void setPalette(const uint8_t *pal, int n, int depth); + virtual void copyRect(int x, int y, int w, int h, const uint8_t *buf, int pitch); + virtual void copyYuv(int w, int h, const uint8_t *y, int ypitch, const uint8_t *u, int upitch, const uint8_t *v, int vpitch); + virtual void fillRect(int x, int y, int w, int h, uint8_t color); + virtual void copyRectWidescreen(int w, int h, const uint8_t *buf, const uint8_t *pal); + virtual void shakeScreen(int dx, int dy); + virtual void updateScreen(bool drawWidescreen); + virtual void processEvents(); + virtual void sleep(int duration); + virtual uint32_t getTimeStamp(); + virtual void startAudio(AudioCallback callback); + virtual void stopAudio(); + virtual void lockAudio(); + virtual void unlockAudio(); + virtual AudioCallback setAudioCallback(AudioCallback callback); + + void setupVideo(); + + void initGX(); + void finiGX(); + void drawTextureGX(int x, int y); +}; + +static System_Wii system_wii; +System *const g_system = &system_wii; + +static uint32_t __attribute__((aligned(16))) _fifo[256 * 1024]; +static uint32_t *_xfb[2]; +static int _current_fb; + +static const int GAME_W = 256; +static const int GAME_H = 192; + +static uint16_t __attribute__((aligned(32))) _clut[256]; +static uint8_t __attribute__((aligned(32))) _texture[GAME_W * GAME_H]; + +static const int DMA_BUFFER_SAMPLES = 512; +static const int DMA_BUFFER_SIZE = DMA_BUFFER_SAMPLES * 2 * sizeof(int16_t); // stereo s16 + +static const int AUDIO_THREAD_PRIORITY = 80; + +static const int AUDIO_BUFFER_SAMPLES = DMA_BUFFER_SAMPLES * 22050 / 32000; +static const int AUDIO_BUFFER_SIZE = AUDIO_BUFFER_SAMPLES * 2 * sizeof(int16_t); // stereo s16 + +static uint8_t __attribute__((aligned(32))) _audioThreadStack[16384]; +static int16_t __attribute__((aligned(32))) _resample[DMA_BUFFER_SAMPLES * 2]; +static uint8_t __attribute__((aligned(32))) _dma[2][DMA_BUFFER_SIZE]; +static int _current_dma; + +void System_fatalError(const char *s) { + GXRModeObj *rmodeObj = system_wii._rmodeObj; + if (!rmodeObj) { + VIDEO_Init(); + system_wii.setupVideo(); + rmodeObj = system_wii._rmodeObj; + } + console_init(_xfb[_current_fb], 20, 20, rmodeObj->fbWidth, rmodeObj->xfbHeight, rmodeObj->fbWidth * VI_DISPLAY_PIX_SZ); + fputs(s, stdout); + usleep(10 * 1000 * 1000); +} + +void System_printLog(FILE *fp, const char *s) { +} + +System_Wii::System_Wii() { + _rmodeObj = 0; +} + +System_Wii::~System_Wii() { +} + +void System_Wii::init(const char *title, int w, int h, bool fullscreen, bool widescreen, bool yuv) { + memset(&inp, 0, sizeof(inp)); + memset(&pad, 0, sizeof(pad)); + + _shakeDx = _shakeDy = 0; + _startTime = gettime(); + + memset(&_audioCb, 0, sizeof(_audioCb)); + _audioMutex = 0; + _audioThread = LWP_THREAD_NULL; + _audioOut = false; + + AUDIO_Init(0); + + _gamma = GX_GM_1_0; + + if (!_rmodeObj) { + VIDEO_Init(); + setupVideo(); + } + + PAD_Init(); + WUPC_Init(); + WPAD_Init(); + WiiDRC_Init(); + + initGX(); +} + +void System_Wii::destroy() { + WUPC_Shutdown(); + WPAD_Shutdown(); + + finiGX(); + + VIDEO_SetBlack(TRUE); + VIDEO_Flush(); + free(MEM_K1_TO_K0(_xfb[0])); + _xfb[0] = 0; + free(MEM_K1_TO_K0(_xfb[1])); + _xfb[1] = 0; +} + +void System_Wii::setScaler(const char *name, int multiplier) { +} + +void System_Wii::setGamma(float gamma) { + if (gamma < 1.7f) { + _gamma = GX_GM_1_0; + } else if (gamma < 2.2f) { + _gamma = GX_GM_1_7; + } else { + _gamma = GX_GM_2_2; + } +} + +void System_Wii::setPalette(const uint8_t *pal, int n, int depth) { + const int shift = 8 - depth; + for (int i = 0; i < n; ++i) { + int r = pal[i * 3]; + int g = pal[i * 3 + 1]; + int b = pal[i * 3 + 2]; + if (shift != 0) { + r = (r << shift) | (r >> depth); + g = (g << shift) | (g >> depth); + b = (b << shift) | (b >> depth); + } + _clut[i] = ((r & 0xF8) << 8) | ((g & 0xFC) << 3) | (b >> 3); + } + DCFlushRange(_clut, sizeof(_clut)); + GX_LoadTlut(&_tlutObj, GX_TLUT0); +} + +void System_Wii::copyRect(int x, int y, int w, int h, const uint8_t *buf, int pitch) { + uint64_t *dst = (uint64_t *)_texture + y * GAME_W + x; + uint64_t *src = (uint64_t *)buf; + assert((pitch & 7) == 0); + pitch >>= 3; + w >>= 3; + for (int y = 0; y < h; y += 4) { + for (int x = 0; x < w; ++x) { + for (int i = 0; i < 4; ++i) { + *dst++ = src[pitch * i + x]; + } + } + src += pitch * 4; + } + DCFlushRange(_texture, sizeof(_texture)); + GX_LoadTexObj(&_texObj, GX_TEXMAP0); +} + +void System_Wii::copyYuv(int w, int h, const uint8_t *y, int ypitch, const uint8_t *u, int upitch, const uint8_t *v, int vpitch) { +} + +void System_Wii::fillRect(int x, int y, int w, int h, uint8_t color) { + uint8_t *p = _texture + y * GAME_W + x; + if (w == GAME_W) { + memset(p, color, w * h); + } else { + for (int y = 0; y < h; ++y) { + memset(p, color, w); + p += GAME_W; + } + } + DCFlushRange(_texture, sizeof(_texture)); + GX_LoadTexObj(&_texObj, GX_TEXMAP0); +} + +void System_Wii::copyRectWidescreen(int w, int h, const uint8_t *buf, const uint8_t *pal) { +} + +void System_Wii::shakeScreen(int dx, int dy) { + _shakeDx = dx; + _shakeDy = dy; +} + +void System_Wii::updateScreen(bool drawWidescreen) { + + GX_InvalidateTexAll(); + drawTextureGX(_shakeDx, _shakeDy); + GX_DrawDone(); + + _current_fb ^= 1; + GX_SetZMode(GX_TRUE, GX_LEQUAL, GX_TRUE); + GX_SetColorUpdate(GX_TRUE); + GX_CopyDisp(_xfb[_current_fb], GX_TRUE); + GX_Flush(); + + VIDEO_SetNextFramebuffer(_xfb[_current_fb]); + VIDEO_Flush(); + VIDEO_WaitVSync(); +} + +void System_Wii::processEvents() { + inp.prevMask = inp.mask; + inp.mask = 0; + + if (SYS_ResetButtonDown()) { + inp.quit = true; + } + + static const struct { + int pad; + int wpad; + int wdrc; + int sys; + } mapping[] = { + { PAD_BUTTON_UP, (WPAD_CLASSIC_BUTTON_UP | WPAD_BUTTON_RIGHT), WIIDRC_BUTTON_UP, SYS_INP_UP }, + { PAD_BUTTON_RIGHT, (WPAD_CLASSIC_BUTTON_RIGHT | WPAD_BUTTON_DOWN), WIIDRC_BUTTON_RIGHT, SYS_INP_RIGHT }, + { PAD_BUTTON_DOWN, (WPAD_CLASSIC_BUTTON_DOWN | WPAD_BUTTON_LEFT), WIIDRC_BUTTON_DOWN, SYS_INP_DOWN }, + { PAD_BUTTON_LEFT, (WPAD_CLASSIC_BUTTON_LEFT | WPAD_BUTTON_UP), WIIDRC_BUTTON_LEFT, SYS_INP_LEFT }, + { PAD_BUTTON_A, (WPAD_CLASSIC_BUTTON_A | WPAD_BUTTON_A), WIIDRC_BUTTON_A, SYS_INP_JUMP }, + { PAD_BUTTON_B, (WPAD_CLASSIC_BUTTON_B | WPAD_BUTTON_B), WIIDRC_BUTTON_B, SYS_INP_RUN }, + { PAD_BUTTON_X, (WPAD_CLASSIC_BUTTON_X | WPAD_BUTTON_1), WIIDRC_BUTTON_X, SYS_INP_SHOOT }, + { PAD_BUTTON_Y, (WPAD_CLASSIC_BUTTON_Y | WPAD_BUTTON_2), WIIDRC_BUTTON_Y, SYS_INP_SHOOT | SYS_INP_RUN }, + { PAD_BUTTON_START, (WPAD_CLASSIC_BUTTON_HOME | WPAD_BUTTON_HOME), WIIDRC_BUTTON_HOME, SYS_INP_ESC }, + { 0, 0, 0 } + }; + PAD_ScanPads(); + const uint32_t padMask = PAD_ButtonsDown(0) | PAD_ButtonsHeld(0); + WUPC_UpdateButtonStats(); + uint32_t wpadMask = WUPC_ButtonsDown(0) | WUPC_ButtonsHeld(0); + WPAD_ScanPads(); + wpadMask |= WPAD_ButtonsDown(0) | WPAD_ButtonsHeld(0); + WiiDRC_ScanPads(); + const uint32_t wdrcMask = WiiDRC_ButtonsDown() | WiiDRC_ButtonsHeld(); + for (int i = 0; mapping[i].pad != 0; ++i) { + if ((mapping[i].pad & padMask) != 0 || (mapping[i].wpad & wpadMask) != 0 || (mapping[i].wdrc & wdrcMask) != 0) { + inp.mask |= mapping[i].sys; + } + } + const int x = PAD_StickX(0); + if (x > 64) { + inp.mask |= SYS_INP_RIGHT; + } else if (x < -64) { + inp.mask |= SYS_INP_LEFT; + } + const int y = PAD_StickY(0); + if (y > 64) { + inp.mask |= SYS_INP_UP; + } else if (y < -64) { + inp.mask |= SYS_INP_DOWN; + } +} + +void System_Wii::sleep(int duration) { + usleep(duration * 1000); +} + +uint32_t System_Wii::getTimeStamp() { + const uint64_t ticks = diff_ticks(_startTime, gettime()); + return ticks_to_millisecs(ticks); +} + +static void *audioThread(void *arg) { + while (system_wii._audioOut) { + + memset(_dma[_current_dma], 0, DMA_BUFFER_SIZE); + + int16_t buf22khz[AUDIO_BUFFER_SAMPLES * 2]; // stereo + memset(buf22khz, 0, sizeof(buf22khz)); + system_wii.lockAudio(); + (system_wii._audioCb.proc)(system_wii._audioCb.userdata, buf22khz, AUDIO_BUFFER_SAMPLES * 2); + system_wii.unlockAudio(); + + // point resampling + static const int fracBits = 16; + static const uint32_t step = (22050 << fracBits) / 32000; + uint32_t len = 0; + for (uint32_t pos = 0; len < DMA_BUFFER_SAMPLES * 2; pos += step) { + const int16_t *p = buf22khz + MIN((pos >> fracBits), AUDIO_BUFFER_SAMPLES - 1) * 2; + _resample[len++] = p[0]; + _resample[len++] = p[1]; + } + assert(len * sizeof(int16_t) == DMA_BUFFER_SIZE); + + memcpy(_dma[_current_dma], _resample, DMA_BUFFER_SIZE); + DCFlushRange(_dma[_current_dma], DMA_BUFFER_SIZE); + + LWP_ThreadSleep(system_wii._audioQueue); + } + return 0; +} + +static void dmaCallback() { + _current_dma ^= 1; + AUDIO_InitDMA((uint32_t)_dma[_current_dma], DMA_BUFFER_SIZE); + LWP_ThreadSignal(system_wii._audioQueue); +} + +void System_Wii::startAudio(AudioCallback callback) { + _audioCb = callback; + LWP_MutexInit(&_audioMutex, FALSE); + LWP_InitQueue(&_audioQueue); + _audioOut = true; + AUDIO_SetDSPSampleRate(AI_SAMPLERATE_32KHZ); + LWP_CreateThread(&_audioThread, audioThread, 0, _audioThreadStack, sizeof(_audioThreadStack), AUDIO_THREAD_PRIORITY); + AUDIO_RegisterDMACallback(dmaCallback); + dmaCallback(); + AUDIO_InitDMA((uint32_t)_dma[_current_dma], DMA_BUFFER_SIZE); + AUDIO_StartDMA(); +} + +void System_Wii::stopAudio() { + AUDIO_StopDMA(); + AUDIO_RegisterDMACallback(0); + _audioOut = false; + LWP_ThreadSignal(_audioQueue); + LWP_JoinThread(_audioThread, 0); + LWP_MutexDestroy(_audioMutex); + LWP_CloseQueue(_audioQueue); +} + +void System_Wii::lockAudio() { + LWP_MutexLock(_audioMutex); +} + +void System_Wii::unlockAudio() { + LWP_MutexUnlock(_audioMutex); +} + +AudioCallback System_Wii::setAudioCallback(AudioCallback callback) { + lockAudio(); + AudioCallback cb = _audioCb; + _audioCb = callback; + unlockAudio(); + return cb; +} + +void System_Wii::setupVideo() { + _rmodeObj = VIDEO_GetPreferredMode(0); + _rmodeObj->viWidth = 640; + _rmodeObj->viXOrigin = (VI_MAX_WIDTH_NTSC - _rmodeObj->viWidth) / 2; + VIDEO_Configure(_rmodeObj); + + _xfb[0] = (uint32_t *)SYS_AllocateFramebuffer(_rmodeObj); + _xfb[1] = (uint32_t *)SYS_AllocateFramebuffer(_rmodeObj); + DCInvalidateRange(_xfb[0], VIDEO_GetFrameBufferSize(_rmodeObj)); + DCInvalidateRange(_xfb[1], VIDEO_GetFrameBufferSize(_rmodeObj)); + _xfb[0] = (uint32_t *)MEM_K0_TO_K1(_xfb[0]); + _xfb[1] = (uint32_t *)MEM_K0_TO_K1(_xfb[1]); + + VIDEO_ClearFrameBuffer(_rmodeObj, _xfb[0], COLOR_BLACK); + VIDEO_ClearFrameBuffer(_rmodeObj, _xfb[1], COLOR_BLACK); + VIDEO_SetNextFramebuffer(_xfb[0]); + _current_fb = 0; + + VIDEO_SetBlack(FALSE); + VIDEO_Flush(); + VIDEO_WaitVSync(); + if (_rmodeObj->viTVMode & VI_NON_INTERLACE) { + VIDEO_WaitVSync(); + } +} + +void System_Wii::initGX() { + memset(_fifo, 0, sizeof(_fifo)); + GX_Init(_fifo, sizeof(_fifo)); + + GXColor background = { 0, 0, 0, 0xFF }; + GX_SetCopyClear(background, GX_MAX_Z24); + + GX_SetViewport(0, 0, _rmodeObj->fbWidth, _rmodeObj->efbHeight, 0, 1); + const int xfbHeight = GX_SetDispCopyYScale(GX_GetYScaleFactor(_rmodeObj->efbHeight, _rmodeObj->xfbHeight)); + GX_SetScissor(0, 0, _rmodeObj->fbWidth, _rmodeObj->efbHeight); + + GX_SetDispCopySrc(0, 0, _rmodeObj->fbWidth, _rmodeObj->efbHeight); + GX_SetDispCopyDst(_rmodeObj->fbWidth, xfbHeight); + + GX_SetCopyFilter(_rmodeObj->aa, _rmodeObj->sample_pattern, GX_TRUE, _rmodeObj->vfilter); + GX_SetFieldMode(_rmodeObj->field_rendering, ((_rmodeObj->viHeight == 2 * _rmodeObj->xfbHeight) ? GX_ENABLE : GX_DISABLE)); + GX_SetPixelFmt(_rmodeObj->aa ? GX_PF_RGB565_Z16 : GX_PF_RGB8_Z24, GX_ZC_LINEAR); + GX_SetDispCopyGamma(_gamma); + GX_SetCullMode(GX_CULL_NONE); + + Mtx m; + const int h = GAME_H; + const int top = 0; + const int w = GAME_W * 11 / 10; + const int left = 0; + guOrtho(m, top, h - top, left, w - left, 0, 1); + GX_LoadProjectionMtx(m, GX_ORTHOGRAPHIC); + _projTop = (h - GAME_H) / 2; + _projLeft = (w - GAME_W) / 2; + + GX_InvVtxCache(); + GX_InvalidateTexAll(); + + GX_SetVtxAttrFmt(GX_VTXFMT0, GX_VA_POS, GX_POS_XYZ, GX_S16, 0); + GX_SetVtxAttrFmt(GX_VTXFMT0, GX_VA_CLR0, GX_CLR_RGBA, GX_RGBA8, 0); + GX_SetVtxAttrFmt(GX_VTXFMT0, GX_VA_TEX0, GX_TEX_ST, GX_F32, 0); + + GX_ClearVtxDesc(); + GX_SetVtxDesc(GX_VA_PTNMTXIDX, GX_PNMTX0); + GX_SetVtxDesc(GX_VA_TEX0MTXIDX, GX_TEXMTX0); + GX_SetVtxDesc(GX_VA_POS, GX_DIRECT); + GX_SetVtxDesc(GX_VA_CLR0, GX_DIRECT); + GX_SetVtxDesc(GX_VA_TEX0, GX_DIRECT); + + GX_InitTlutObj(&_tlutObj, _clut, GX_TL_RGB565, 256); + GX_InitTexObjCI(&_texObj, _texture, GAME_W, GAME_H, GX_TF_CI8, GX_CLAMP, GX_CLAMP, GX_FALSE, GX_TLUT0); + GX_InitTexObjFilterMode(&_texObj, GX_LIN_MIP_NEAR, GX_LINEAR); // GX_NEAR, GX_NEAR); +} + +void System_Wii::finiGX() { + GX_AbortFrame(); + GX_Flush(); +} + +void System_Wii::drawTextureGX(int x, int y) { + Mtx m; + guMtxIdentity(m); + guMtxTrans(m, x, y, 0); + GX_LoadPosMtxImm(m, GX_PNMTX0); + + const int x1 = _projLeft; + const int y1 = _projTop; + const int x2 = x1 + GAME_W; + const int y2 = y1 + GAME_H; + + GX_Begin(GX_QUADS, GX_VTXFMT0, 4); + + GX_Position3s16(x1, y1, 0); + GX_Color1u32(0xFFFFFFFF); + GX_TexCoord2f32(0, 0); + + GX_Position3s16(x2, y1, 0); + GX_Color1u32(0xFFFFFFFF); + GX_TexCoord2f32(1, 0); + + GX_Position3s16(x2, y2, 0); + GX_Color1u32(0xFFFFFFFF); + GX_TexCoord2f32(1, 1); + + GX_Position3s16(x1, y2, 0); + GX_Color1u32(0xFFFFFFFF); + GX_TexCoord2f32(0, 1); + + GX_End(); +} diff --git a/util.cpp b/util.cpp index 1c6f8da..dbc01a6 100644 --- a/util.cpp +++ b/util.cpp @@ -7,8 +7,10 @@ #define LOG_TAG "HodJni" #include #endif +#include #include -#include "util.h" +extern void System_printLog(FILE *, const char *s); +extern void System_fatalError(const char *s); int g_debugMask; @@ -23,6 +25,9 @@ void debug(int mask, const char *msg, ...) { fflush(stdout); #ifdef __ANDROID__ __android_log_print(ANDROID_LOG_INFO, LOG_TAG, "%s", buf); +#endif +#ifdef PSP + System_printLog(stdout, buf); #endif } } @@ -37,7 +42,10 @@ void error(const char *msg, ...) { #ifdef __ANDROID__ __android_log_print(ANDROID_LOG_ERROR, LOG_TAG, "%s", buf); #endif - exit(-1); +#ifdef PSP + System_printLog(stderr, buf); +#endif + System_fatalError(buf); } void warning(const char *msg, ...) { @@ -50,5 +58,7 @@ void warning(const char *msg, ...) { #ifdef __ANDROID__ __android_log_print(ANDROID_LOG_WARN, LOG_TAG, "%s", buf); #endif +#ifdef PSP + System_printLog(stderr, buf); +#endif } - diff --git a/util.h b/util.h index 1518f2b..6edbbea 100644 --- a/util.h +++ b/util.h @@ -25,4 +25,8 @@ void debug(int mask, const char *msg, ...); void error(const char *msg, ...); void warning(const char *msg, ...); +#ifdef NDEBUG +#define debug(x, ...) +#endif + #endif // UTIL_H__ diff --git a/video.cpp b/video.cpp index f98002f..8616043 100644 --- a/video.cpp +++ b/video.cpp @@ -15,24 +15,20 @@ Video::Video() { _drawLine.y1 = 0; _drawLine.x2 = W - 1; _drawLine.y2 = H - 1; - _drawLine.pitch = W; _shadowLayer = (uint8_t *)malloc(W * H + 1); // projectionData offset can be equal to W * H _frontLayer = (uint8_t *)malloc(W * H); _backgroundLayer = (uint8_t *)malloc(W * H); - _spr.pitch = W; - _spr.x = 0; - _spr.y = 0; - _spr.w = W; - _spr.h = H; if (kUseShadowColorLut) { _shadowColorLookupTable = (uint8_t *)malloc(256 * 256); // shadowLayer, frontLayer } else { _shadowColorLookupTable = 0; } _shadowScreenMaskBuffer = (uint8_t *)malloc(256 * 192 * 2 + 256 * 4); + for (int i = 144; i < 256; ++i) { + _shadowColorLut[i] = i; + } _transformShadowBuffer = 0; _transformShadowLayerDelta = 0; - _fillColor = 0xC4; memset(&_mdec, 0, sizeof(_mdec)); } @@ -49,12 +45,12 @@ Video::~Video() { void Video::init(bool mdec) { if (mdec) { - const int w = (W + 15) & ~15; - const int h = (H + 15) & ~15; + static const int w = (W + 15) & ~15; + static const int h = (H + 15) & ~15; + static const int w2 = w / 2; + static const int h2 = h / 2; _mdec.planes[kOutputPlaneY].ptr = (uint8_t *)malloc(w * h); _mdec.planes[kOutputPlaneY].pitch = w; - const int w2 = w / 2; - const int h2 = h / 2; _mdec.planes[kOutputPlaneCb].ptr = (uint8_t *)malloc(w2 * h2); _mdec.planes[kOutputPlaneCb].pitch = w2; _mdec.planes[kOutputPlaneCr].ptr = (uint8_t *)malloc(w2 * h2); @@ -66,8 +62,7 @@ static int colorBrightness(int r, int g, int b) { return (r + g * 2) * 19 + b * 7; } -void Video::refreshGamePalette(const uint16_t *pal) { - _refreshPalette = true; +void Video::updateGamePalette(const uint16_t *pal) { for (int i = 0; i < 256 * 3; ++i) { _palette[i] = pal[i] >> 8; } @@ -77,50 +72,52 @@ void Video::refreshGamePalette(const uint16_t *pal) { void Video::updateGameDisplay(uint8_t *buf) { g_system->copyRect(0, 0, W, H, buf, 256); if (_mdec.planes[kOutputPlaneY].ptr) { - updateYuvDisplay(); + updateYuvDisplay(&_mdec); } } -void Video::updateYuvDisplay() { +void Video::updateYuvDisplay(MdecOutput *mdec) { g_system->copyYuv(Video::W, Video::H, _mdec.planes[0].ptr, _mdec.planes[0].pitch, _mdec.planes[1].ptr, _mdec.planes[1].pitch, _mdec.planes[2].ptr, _mdec.planes[2].pitch); } +void Video::copyYuvBackBuffer() { +} + void Video::updateScreen() { g_system->updateScreen(true); } -void Video::fillBackBuffer() { - g_system->fillRect(0, 0, W, H, _fillColor); +void Video::clearBackBuffer() { + g_system->fillRect(0, 0, W, H, CLEAR_COLOR); } void Video::clearPalette() { memset(_palette, 0, sizeof(_palette)); - _refreshPalette = true; g_system->setPalette(_palette, 256); } void Video::decodeSPR(const uint8_t *src, uint8_t *dst, int x, int y, uint8_t flags, uint16_t spr_w, uint16_t spr_h) { - if (y >= _spr.h) { + if (y >= H) { return; - } else if (y < _spr.y) { + } else if (y < 0) { flags |= kSprClipTop; } const int y2 = y + spr_h - 1; - if (y2 < _spr.y) { + if (y2 < 0) { return; - } else if (y2 >= _spr.h) { + } else if (y2 >= H) { flags |= kSprClipBottom; } - if (x >= _spr.w) { + if (x >= W) { return; - } else if (x < _spr.x) { + } else if (x < 0) { flags |= kSprClipLeft; } const int x2 = x + spr_w - 1; - if (x2 < _spr.x) { + if (x2 < 0) { return; - } else if (x2 >= _spr.w) { + } else if (x2 >= W) { flags |= kSprClipRight; } @@ -132,11 +129,11 @@ void Video::decodeSPR(const uint8_t *src, uint8_t *dst, int x, int y, uint8_t fl } const int xOrig = x; while (1) { - uint8_t *p = dst + y * _spr.pitch + x; + uint8_t *p = dst + y * W + x; int code = *src++; int count = code & 0x3F; int clippedCount = count; - if (y < _spr.y || y >= _spr.h) { + if (y < 0 || y >= H) { clippedCount = 0; } switch (code >> 6) { @@ -146,14 +143,14 @@ void Video::decodeSPR(const uint8_t *src, uint8_t *dst, int x, int y, uint8_t fl x += count; } else if (flags & kSprHorizFlip) { for (int i = 0; i < clippedCount; ++i) { - if (x - i >= _spr.x && x - i < _spr.w) { + if (x - i >= 0 && x - i < W) { p[-i] = src[i]; } } x -= count; } else { for (int i = 0; i < clippedCount; ++i) { - if (x + i >= _spr.x && x + i < _spr.w) { + if (x + i >= 0 && x + i < W) { p[i] = src[i]; } } @@ -168,14 +165,14 @@ void Video::decodeSPR(const uint8_t *src, uint8_t *dst, int x, int y, uint8_t fl x += count; } else if (flags & kSprHorizFlip) { for (int i = 0; i < clippedCount; ++i) { - if (x - i >= _spr.x && x - i < _spr.w) { + if (x - i >= 0 && x - i < W) { p[-i] = code; } } x -= count; } else { for (int i = 0; i < clippedCount; ++i) { - if (x + i >= _spr.x && x + i < _spr.w) { + if (x + i >= 0 && x + i < W) { p[i] = code; } } @@ -282,7 +279,7 @@ bool Video::clipLineCoords(int &x1, int &y1, int &x2, int &y2) { return false; } -void Video::drawLine(int x1, int y1, int x2, int y2) { +void Video::drawLine(int x1, int y1, int x2, int y2, uint8_t color) { if (clipLineCoords(x1, y1, x2, y2)) { return; } @@ -290,7 +287,7 @@ void Video::drawLine(int x1, int y1, int x2, int y2) { assert(y1 >= _drawLine.y1 && y1 <= _drawLine.y2); assert(x2 >= _drawLine.x1 && x2 <= _drawLine.x2); assert(y2 >= _drawLine.y1 && y2 <= _drawLine.y2); - int dstPitch = _drawLine.pitch; + int dstPitch = W; int dx = x2 - x1; if (dx == 0) { int dy = y2 - y1; @@ -298,9 +295,9 @@ void Video::drawLine(int x1, int y1, int x2, int y2) { y1 += dy; dy = -dy; } - uint8_t *dst = _frontLayer + y1 * _drawLine.pitch + x1; + uint8_t *dst = _frontLayer + y1 * W + x1; for (int i = 0; i <= dy; ++i) { - *dst = _drawLine.color; + *dst = color; dst += dstPitch; } return; @@ -310,10 +307,10 @@ void Video::drawLine(int x1, int y1, int x2, int y2) { dx = -dx; SWAP(y1, y2); } - uint8_t *dst = _frontLayer + y1 * _drawLine.pitch + x1; + uint8_t *dst = _frontLayer + y1 * W + x1; int dy = y2 - y1; if (dy == 0) { - memset(dst, _drawLine.color, dx); + memset(dst, color, dx); return; } if (dy < 0) { @@ -327,7 +324,7 @@ void Video::drawLine(int x1, int y1, int x2, int y2) { const int stepInc = dy * 2; step -= stepInc; for (int i = 0; i <= dy; ++i) { - *dst = _drawLine.color; + *dst = color; step += dx; if (step >= 0) { step -= stepInc; @@ -340,7 +337,7 @@ void Video::drawLine(int x1, int y1, int x2, int y2) { const int stepInc = dy * 2; step -= stepInc; for (int i = 0; i <= dy; ++i) { - *dst = _drawLine.color; + *dst = color; step += dx; if (step >= 0) { step -= stepInc; @@ -362,14 +359,11 @@ void Video::applyShadowColors(int x, int y, int src_w, int src_h, int dst_pitch, // src2 == shadowPalette dst2 += y * dst_pitch + x; - src2 = _shadowColorLookupTable; for (int j = 0; j < src_h; ++j) { for (int i = 0; i < src_w; ++i) { int offset = READ_LE_UINT16(src1); src1 += 2; assert(offset <= W * H); - if (!src2) { // no LUT - dst2[i] = lookupColor(_shadowLayer[offset], dst2[i], _shadowColorLut); - } else { + if (kUseShadowColorLut) { // build lookup offset // msb : _shadowLayer[ _projectionData[ (x, y) ] ] // lsb : _frontLayer[ (x, y) ] @@ -378,7 +372,9 @@ void Video::applyShadowColors(int x, int y, int src_w, int src_h, int dst_pitch, // lookup color matrix // if msb < 144 : _frontLayer.color // if msb >= 144 : if _frontLayer.color < 144 ? shadowPalette[ _frontLayer.color ] : _frontLayer.color - dst2[i] = src2[offset]; + dst2[i] = _shadowColorLookupTable[offset]; + } else { + dst2[i] = lookupColor(_shadowLayer[offset], dst2[i], _shadowColorLut); } } dst2 += dst_pitch; @@ -386,8 +382,8 @@ void Video::applyShadowColors(int x, int y, int src_w, int src_h, int dst_pitch, } void Video::buildShadowColorLookupTable(const uint8_t *src, uint8_t *dst) { - assert(dst == _shadowColorLookupTable); - if (_shadowColorLookupTable) { + if (kUseShadowColorLut) { + assert(dst == _shadowColorLookupTable); // 256x256 // 0..143 : 0..255 // 144..255 : src[0..143] 144..255 @@ -404,10 +400,7 @@ void Video::buildShadowColorLookupTable(const uint8_t *src, uint8_t *dst) { } } } - memcpy(_shadowColorLut, src, 144); - for (int i = 144; i < 256; ++i) { - _shadowColorLut[i] = i; - } + memcpy(_shadowColorLut, src, 144); // indexes 144-256 are not remapped if (0) { // lookup[a * 256 + b] // @@ -490,6 +483,7 @@ void Video::decodeBackgroundPsx(const uint8_t *src, const int size, int w, int h _mdec.w = w; _mdec.h = h; decodeMDEC(src, size, 0, 0, w, h, &_mdec); + copyYuvBackBuffer(); } void Video::decodeBackgroundOverlayPsx(const uint8_t *src, int x, int y) { diff --git a/video.h b/video.h index 55d00c9..25507e2 100644 --- a/video.h +++ b/video.h @@ -20,6 +20,7 @@ enum { struct Video { enum { + CLEAR_COLOR = 0xC4, W = 256, H = 192 }; @@ -28,8 +29,7 @@ struct Video { uint8_t _palette[256 * 3]; uint16_t _displayPaletteBuffer[256 * 3]; - bool _paletteNeedRefresh; - bool _refreshPalette; + bool _paletteChanged; bool _displayShadowLayer; uint8_t *_shadowLayer; uint8_t *_frontLayer; @@ -39,22 +39,14 @@ struct Video { uint8_t *_shadowScreenMaskBuffer; uint8_t *_transformShadowBuffer; uint8_t _transformShadowLayerDelta; - uint8_t _fillColor; uint8_t _shadowColorLut[256]; const uint8_t *_font; struct { int x1, y1; int x2, y2; - int pitch; - uint8_t color; } _drawLine; - struct { - int pitch; - int x, y, w, h; - } _spr; - MdecOutput _mdec; Video(); @@ -62,17 +54,18 @@ struct Video { void init(bool mdec); - void refreshGamePalette(const uint16_t *pal); + void updateGamePalette(const uint16_t *pal); void updateGameDisplay(uint8_t *buf); - void updateYuvDisplay(); + void updateYuvDisplay(MdecOutput *mdec); + void copyYuvBackBuffer(); void updateScreen(); - void fillBackBuffer(); + void clearBackBuffer(); void clearPalette(); static void decodeRLE(const uint8_t *src, uint8_t *dst, int size); void decodeSPR(const uint8_t *src, uint8_t *dst, int x, int y, uint8_t flags, uint16_t spr_w, uint16_t spr_h); int computeLineOutCode(int x, int y); bool clipLineCoords(int &x1, int &y1, int &x2, int &y2); - void drawLine(int x1, int y1, int x2, int y2); + void drawLine(int x1, int y1, int x2, int y2, uint8_t color); void applyShadowColors(int x, int y, int src_w, int src_h, int dst_pitch, int src_pitch, uint8_t *dst1, uint8_t *dst2, uint8_t *src1, uint8_t *src2); void buildShadowColorLookupTable(const uint8_t *src, uint8_t *dst);