From faa91a0ffbe68f39446358b4ea860f4b111e8e45 Mon Sep 17 00:00:00 2001 From: somewhatlurker <52014015+somewhatlurker@users.noreply.github.com> Date: Sun, 13 Oct 2019 01:14:17 +1100 Subject: [PATCH 1/4] score saver: trying the vector-based cache again --- .../plugins/TLAC/Components/ScoreSaver.cpp | 145 ++++++++++++++---- .../plugins/TLAC/Components/ScoreSaver.h | 10 +- 2 files changed, 121 insertions(+), 34 deletions(-) diff --git a/source-code/source/plugins/TLAC/Components/ScoreSaver.cpp b/source-code/source/plugins/TLAC/Components/ScoreSaver.cpp index c684e44..d2ac3ce 100644 --- a/source-code/source/plugins/TLAC/Components/ScoreSaver.cpp +++ b/source-code/source/plugins/TLAC/Components/ScoreSaver.cpp @@ -23,6 +23,11 @@ namespace TLAC::Components ScoreSaver::~ScoreSaver() { + for (int diff = 0; diff < 4; diff++) + { + *(DivaScore**)(PLAYER_DATA_ADDRESS + diff * 0x18 + 0x5d0) = 0; + *(DivaScore**)(PLAYER_DATA_ADDRESS + diff * 0x18 + 0x5d8) = 0; + } } const char* ScoreSaver::GetDisplayName() @@ -35,13 +40,6 @@ namespace TLAC::Components // build the score cache UpdateScoreCache(); UpdateClearCounts(); - - // update score begin and end vars from game - for (int diff = 0; diff < 4; diff++) - { - *(DivaScore**)(PLAYER_DATA_ADDRESS + diff * 0x18 + 0x5d0) = &ScoreCache[diff][0][0]; - *(DivaScore**)(PLAYER_DATA_ADDRESS + diff * 0x18 + 0x5d8) = &ScoreCache[diff][1000][0]; // deliberately use 1000 to get past end of cache - } } bool(__stdcall* ScoreSaver::divaInitResults)(void* cls) = (bool(__stdcall*)(void* cls))RESULTS_INIT_ADDRESS; @@ -307,7 +305,7 @@ namespace TLAC::Components } } - UpdateSingleScoreCacheEntry(pvNum, pvDifficulty, pvDifficultyIsEx); + UpdateSingleScoreCacheEntry(pvNum, pvDifficulty, pvDifficultyIsEx, true); UpdateClearCounts(); return result; @@ -315,11 +313,38 @@ namespace TLAC::Components void ScoreSaver::Update() { - if (*(GameState*)CURRENT_GAME_STATE_ADDRESS == GS_GAME && *(SubGameState*)CURRENT_GAME_SUB_STATE_ADDRESS == SUB_SELECTOR && initThread.joinable()) + if (*(GameState*)CURRENT_GAME_STATE_ADDRESS == GS_GAME && *(SubGameState*)CURRENT_GAME_SUB_STATE_ADDRESS == SUB_SELECTOR) { - // it's actually fine to let the init happen in the background after reaching game state, but this is probably safer - printf("[ScoreSaver] Waiting for initialisation..."); - initThread.join(); + if (initThread.joinable()) + { + // it's actually fine to let the init happen in the background after reaching game state, + // but this is safer because it means the begin and end addresses aren't changed in a different thread + printf("[ScoreSaver] Waiting for initialisation..."); + initThread.join(); + } + + int pvNum = *(int*)SELPV_CURRENT_SONG_ADDRESS; + int diff = *(int*)(GAME_INFO_ADDRESS); + int diffIsEx = *(int*)(GAME_INFO_ADDRESS + 0x4); + byte insurance = *(byte*)(GAME_INFO_ADDRESS + 0x14); + + if (pvNum != currentPv || diff != currentDifficulty || diffIsEx != currentDifficultyIsEx || insurance == currentInsurance) + { + DivaScore* cachedScore = GetCachedScore(pvNum, diff, diffIsEx); + if (cachedScore == nullptr) + { + // create a score cache entry if none exists for current song + ScoreCache[diff].push_back(DivaScore(pvNum, diffIsEx)); + // update score begin and end vars from game + *(DivaScore**)(PLAYER_DATA_ADDRESS + diff * 0x18 + 0x5d0) = ScoreCache[diff].begin()._Ptr; + *(DivaScore**)(PLAYER_DATA_ADDRESS + diff * 0x18 + 0x5d8) = ScoreCache[diff].end()._Ptr; + } + + currentPv = pvNum; + currentDifficulty = diff; + currentDifficultyIsEx = diffIsEx; + currentInsurance = insurance; + } } } @@ -338,8 +363,27 @@ namespace TLAC::Components VirtualProtect(address, byteCount, oldProtect, nullptr); } - ScoreSaver::DivaScore ScoreSaver::ScoreCache[4][1000][2]; // 4 difficulties * 1000 pvs * extra or not - void ScoreSaver::UpdateSingleScoreCacheEntry(int pvNum, int diff, int exDiff) + std::vector ScoreSaver::ScoreCache[4] = { // * 4 difficulties + {}, + {}, + {}, + {} + }; + + ScoreSaver::DivaScore* ScoreSaver::GetCachedScore(int pvNum, int diff, int exDiff) + { + if (pvNum < 0 || diff < 0 || exDiff < 0 || pvNum > 999 || diff > 3 || exDiff > 1) + return nullptr; + + for (DivaScore &scoreinfo : ScoreCache[diff]) + { + if (scoreinfo.pvNum == pvNum && scoreinfo.exDifficulty == exDiff) + return &scoreinfo; + } + + return nullptr; + } + void ScoreSaver::UpdateSingleScoreCacheEntry(int pvNum, int diff, int exDiff, bool doDefaultsReset) { if (pvNum < 0 || diff < 0 || exDiff < 0 || pvNum > 999 || diff > 3 || exDiff > 1) return; @@ -392,13 +436,38 @@ namespace TLAC::Components modifiers = modifiers > 0 ? 1 << (modifiers - 1) : 0; } - DivaScore* cachedScore = &ScoreCache[diff][pvNum][exDiff]; - cachedScore->score = score; - cachedScore->percent = percent; - cachedScore->clearRank = allTimeRank; - if (modifiers & 1) cachedScore->optionA = 1; - if (modifiers & 2) cachedScore->optionB = 1; - if (modifiers & 4) cachedScore->optionC = 1; + DivaScore* cachedScore = GetCachedScore(pvNum, diff, exDiff); + if (cachedScore == nullptr) + { + ScoreCache[diff].push_back(DivaScore(pvNum, exDiff)); + cachedScore = GetCachedScore(pvNum, diff, exDiff); + + // update score begin and end vars from game + *(DivaScore**)(PLAYER_DATA_ADDRESS + diff * 0x18 + 0x5d0) = ScoreCache[diff].begin()._Ptr; + *(DivaScore**)(PLAYER_DATA_ADDRESS + diff * 0x18 + 0x5d8) = ScoreCache[diff].end()._Ptr; + } + if (cachedScore != nullptr) + { + cachedScore->score = score; + cachedScore->percent = percent; + cachedScore->clearRank = allTimeRank; + if (modifiers & 1) cachedScore->optionA = 1; + if (modifiers & 2) cachedScore->optionB = 1; + if (modifiers & 4) cachedScore->optionC = 1; + } + } + else if (doDefaultsReset) // reset to defaults if not valid + { + DivaScore* cachedScore = GetCachedScore(pvNum, diff, exDiff); + if (cachedScore != nullptr) + { + cachedScore->score = 0; + cachedScore->percent = 0; + cachedScore->clearRank = -1; + cachedScore->optionA = 0; + cachedScore->optionB = 0; + cachedScore->optionC = 0; + } } } @@ -443,10 +512,25 @@ namespace TLAC::Components allTimeRank = GetPrivateProfileIntW(section, key, -1, rival_configPath); } - DivaScore* cachedScore = &ScoreCache[diff][pvNum][exDiff]; - cachedScore->rival_clearRank = allTimeRank; - cachedScore->rival_score = score; - cachedScore->rival_percent = percent; + if (score > 0 || percent > 0) + { + DivaScore* cachedScore = GetCachedScore(pvNum, diff, exDiff); + if (cachedScore == nullptr) + { + ScoreCache[diff].push_back(DivaScore(pvNum, exDiff)); + cachedScore = GetCachedScore(pvNum, diff, exDiff); + + // update score begin and end vars from game + *(DivaScore**)(PLAYER_DATA_ADDRESS + diff * 0x18 + 0x5d0) = ScoreCache[diff].begin()._Ptr; + *(DivaScore**)(PLAYER_DATA_ADDRESS + diff * 0x18 + 0x5d8) = ScoreCache[diff].end()._Ptr; + } + if (cachedScore != nullptr) + { + cachedScore->rival_clearRank = allTimeRank; + cachedScore->rival_score = score; + cachedScore->rival_percent = percent; + } + } } void ScoreSaver::UpdateScoreCache() @@ -457,8 +541,7 @@ namespace TLAC::Components { for (int exDiff = 0; exDiff < 2; exDiff++) { - ScoreCache[diff][pvNum][exDiff] = DivaScore(pvNum, exDiff); - UpdateSingleScoreCacheEntry(pvNum, diff, exDiff); + UpdateSingleScoreCacheEntry(pvNum, diff, exDiff, false); UpdateSingleScoreCacheRivalEntry(pvNum, diff, exDiff); } } @@ -475,9 +558,8 @@ namespace TLAC::Components for (int diff = 0; diff < 4; diff++) { - for (int pvNum = 0; pvNum < 1000; pvNum++) + for (DivaScore &scoreinfo : ScoreCache[diff]) { - DivaScore &scoreinfo = ScoreCache[diff][pvNum][0]; if (scoreinfo.clearRank > 1 && scoreinfo.clearRank <= 5 && scoreinfo.exDifficulty == 0) // at least clear and no greater than perfect and not ex { counts[diff * 4 + scoreinfo.clearRank - 2] += 1; @@ -486,10 +568,9 @@ namespace TLAC::Components } // exex special case - for (int pvNum = 0; pvNum < 1000; pvNum++) + for (DivaScore &scoreinfo : ScoreCache[3]) { - DivaScore &scoreinfo = ScoreCache[3][pvNum][1]; - if (scoreinfo.clearRank > 1 && scoreinfo.clearRank <= 5 && scoreinfo.exDifficulty == 1) // at least clear and no greater than perfect and not ex + if (scoreinfo.clearRank > 1 && scoreinfo.clearRank <= 5 && scoreinfo.exDifficulty == 1) // at least clear and no greater than perfect and IS ex { counts[4 * 4 + scoreinfo.clearRank - 2] += 1; } diff --git a/source-code/source/plugins/TLAC/Components/ScoreSaver.h b/source-code/source/plugins/TLAC/Components/ScoreSaver.h index 89236c5..1e93e8c 100644 --- a/source-code/source/plugins/TLAC/Components/ScoreSaver.h +++ b/source-code/source/plugins/TLAC/Components/ScoreSaver.h @@ -99,11 +99,12 @@ namespace TLAC::Components }; - static DivaScore ScoreCache[4][1000][2]; // 4 difficulties * 1000 pvs * extra or not + static std::vector ScoreCache[4]; // * 4 difficulties static void UpdateScoreCache(); - static void UpdateSingleScoreCacheEntry(int pvNum, int diff, int exDiff); + static void UpdateSingleScoreCacheEntry(int pvNum, int diff, int exDiff, bool doDefaultsReset); static void UpdateSingleScoreCacheRivalEntry(int pvNum, int diff, int exDiff); + static DivaScore* GetCachedScore(int pvNum, int diff, int exDiff); static void UpdateClearCounts(); private: @@ -117,5 +118,10 @@ namespace TLAC::Components static WCHAR configPath[256]; static WCHAR rival_configPath[256]; static std::thread initThread; + + int currentPv; + int currentDifficulty; + int currentDifficultyIsEx; + byte currentInsurance; }; } From 400c17573c8d2d8ef01646b0aec7957d20325532 Mon Sep 17 00:00:00 2001 From: somewhatlurker <52014015+somewhatlurker@users.noreply.github.com> Date: Mon, 14 Oct 2019 01:45:52 +1100 Subject: [PATCH 2/4] score saver: detect SUB_GAME_SEL as in pv menu for update --- source-code/source/plugins/TLAC/Components/ScoreSaver.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/source-code/source/plugins/TLAC/Components/ScoreSaver.cpp b/source-code/source/plugins/TLAC/Components/ScoreSaver.cpp index d2ac3ce..e93df30 100644 --- a/source-code/source/plugins/TLAC/Components/ScoreSaver.cpp +++ b/source-code/source/plugins/TLAC/Components/ScoreSaver.cpp @@ -313,7 +313,7 @@ namespace TLAC::Components void ScoreSaver::Update() { - if (*(GameState*)CURRENT_GAME_STATE_ADDRESS == GS_GAME && *(SubGameState*)CURRENT_GAME_SUB_STATE_ADDRESS == SUB_SELECTOR) + if (*(GameState*)CURRENT_GAME_STATE_ADDRESS == GS_GAME && (*(SubGameState*)CURRENT_GAME_SUB_STATE_ADDRESS == SUB_SELECTOR || *(SubGameState*)CURRENT_GAME_SUB_STATE_ADDRESS == SUB_GAME_SEL)) { if (initThread.joinable()) { From f99769e6e9c05d65a454cde7f8e2673828328942 Mon Sep 17 00:00:00 2001 From: somewhatlurker <52014015+somewhatlurker@users.noreply.github.com> Date: Wed, 16 Oct 2019 10:58:27 +1100 Subject: [PATCH 3/4] score_saver: don't block game loading when scores aren't loaded -- just delay score loading --- .../plugins/TLAC/Components/ScoreSaver.cpp | 83 +++++++++++-------- .../plugins/TLAC/Components/ScoreSaver.h | 4 + 2 files changed, 53 insertions(+), 34 deletions(-) diff --git a/source-code/source/plugins/TLAC/Components/ScoreSaver.cpp b/source-code/source/plugins/TLAC/Components/ScoreSaver.cpp index e93df30..0133897 100644 --- a/source-code/source/plugins/TLAC/Components/ScoreSaver.cpp +++ b/source-code/source/plugins/TLAC/Components/ScoreSaver.cpp @@ -35,11 +35,13 @@ namespace TLAC::Components return "score_saver"; } + bool ScoreSaver::initCacheFinished = false; void ScoreSaver::initCache() { // build the score cache UpdateScoreCache(); UpdateClearCounts(); + initCacheFinished = true; } bool(__stdcall* ScoreSaver::divaInitResults)(void* cls) = (bool(__stdcall*)(void* cls))RESULTS_INIT_ADDRESS; @@ -306,44 +308,58 @@ namespace TLAC::Components } UpdateSingleScoreCacheEntry(pvNum, pvDifficulty, pvDifficultyIsEx, true); - UpdateClearCounts(); + if (initCacheFinished) // don't update clear counts if they're not ready yet + UpdateClearCounts(); + if (didInitialAddressUpdate) // don't set the addresses if they're not ready yet + FixScoreCacheAddresses(pvDifficulty); return result; } + bool ScoreSaver::didInitialAddressUpdate = false; void ScoreSaver::Update() { + // the below stuff is only verified for operating in menus if (*(GameState*)CURRENT_GAME_STATE_ADDRESS == GS_GAME && (*(SubGameState*)CURRENT_GAME_SUB_STATE_ADDRESS == SUB_SELECTOR || *(SubGameState*)CURRENT_GAME_SUB_STATE_ADDRESS == SUB_GAME_SEL)) { - if (initThread.joinable()) + if (!didInitialAddressUpdate) { - // it's actually fine to let the init happen in the background after reaching game state, - // but this is safer because it means the begin and end addresses aren't changed in a different thread - printf("[ScoreSaver] Waiting for initialisation..."); - initThread.join(); - } - - int pvNum = *(int*)SELPV_CURRENT_SONG_ADDRESS; - int diff = *(int*)(GAME_INFO_ADDRESS); - int diffIsEx = *(int*)(GAME_INFO_ADDRESS + 0x4); - byte insurance = *(byte*)(GAME_INFO_ADDRESS + 0x14); - - if (pvNum != currentPv || diff != currentDifficulty || diffIsEx != currentDifficultyIsEx || insurance == currentInsurance) - { - DivaScore* cachedScore = GetCachedScore(pvNum, diff, diffIsEx); - if (cachedScore == nullptr) + if (initCacheFinished) // check for initThread to be done { - // create a score cache entry if none exists for current song - ScoreCache[diff].push_back(DivaScore(pvNum, diffIsEx)); - // update score begin and end vars from game - *(DivaScore**)(PLAYER_DATA_ADDRESS + diff * 0x18 + 0x5d0) = ScoreCache[diff].begin()._Ptr; - *(DivaScore**)(PLAYER_DATA_ADDRESS + diff * 0x18 + 0x5d8) = ScoreCache[diff].end()._Ptr; + // fix the addresses here instead of doing it unsafely in initThread + FixScoreCacheAddresses(0); + FixScoreCacheAddresses(1); + FixScoreCacheAddresses(2); + FixScoreCacheAddresses(3); + didInitialAddressUpdate = true; } + } + else + { + // it probably doesn't really matter whether the initial address update is done first if I add some locks, + // but I feel better knowing that there won't be multiple things racing to modify the vector + // this does mean that with a lot of scores you can beat the loading into the game, thus not getting the new record banner etc. after a song.... + int pvNum = *(int*)SELPV_CURRENT_SONG_ADDRESS; + int diff = *(int*)(GAME_INFO_ADDRESS); + int diffIsEx = *(int*)(GAME_INFO_ADDRESS + 0x4); + byte insurance = *(byte*)(GAME_INFO_ADDRESS + 0x14); - currentPv = pvNum; - currentDifficulty = diff; - currentDifficultyIsEx = diffIsEx; - currentInsurance = insurance; + if (pvNum != currentPv || diff != currentDifficulty || diffIsEx != currentDifficultyIsEx || insurance == currentInsurance) + { + DivaScore* cachedScore = GetCachedScore(pvNum, diff, diffIsEx); + if (cachedScore == nullptr) + { + // create a score cache entry if none exists for current song + ScoreCache[diff].push_back(DivaScore(pvNum, diffIsEx)); + // update score begin and end vars from game + FixScoreCacheAddresses(diff); + } + + currentPv = pvNum; + currentDifficulty = diff; + currentDifficultyIsEx = diffIsEx; + currentInsurance = insurance; + } } } } @@ -441,10 +457,6 @@ namespace TLAC::Components { ScoreCache[diff].push_back(DivaScore(pvNum, exDiff)); cachedScore = GetCachedScore(pvNum, diff, exDiff); - - // update score begin and end vars from game - *(DivaScore**)(PLAYER_DATA_ADDRESS + diff * 0x18 + 0x5d0) = ScoreCache[diff].begin()._Ptr; - *(DivaScore**)(PLAYER_DATA_ADDRESS + diff * 0x18 + 0x5d8) = ScoreCache[diff].end()._Ptr; } if (cachedScore != nullptr) { @@ -519,10 +531,6 @@ namespace TLAC::Components { ScoreCache[diff].push_back(DivaScore(pvNum, exDiff)); cachedScore = GetCachedScore(pvNum, diff, exDiff); - - // update score begin and end vars from game - *(DivaScore**)(PLAYER_DATA_ADDRESS + diff * 0x18 + 0x5d0) = ScoreCache[diff].begin()._Ptr; - *(DivaScore**)(PLAYER_DATA_ADDRESS + diff * 0x18 + 0x5d8) = ScoreCache[diff].end()._Ptr; } if (cachedScore != nullptr) { @@ -533,6 +541,13 @@ namespace TLAC::Components } } + void ScoreSaver::FixScoreCacheAddresses(int diff) + { + // update score begin and end vars from game + *(DivaScore**)(PLAYER_DATA_ADDRESS + diff * 0x18 + 0x5d0) = ScoreCache[diff].begin()._Ptr; + *(DivaScore**)(PLAYER_DATA_ADDRESS + diff * 0x18 + 0x5d8) = ScoreCache[diff].end()._Ptr; + } + void ScoreSaver::UpdateScoreCache() { for (int pvNum = 0; pvNum < 1000; pvNum++) diff --git a/source-code/source/plugins/TLAC/Components/ScoreSaver.h b/source-code/source/plugins/TLAC/Components/ScoreSaver.h index 1e93e8c..dc58a0c 100644 --- a/source-code/source/plugins/TLAC/Components/ScoreSaver.h +++ b/source-code/source/plugins/TLAC/Components/ScoreSaver.h @@ -104,6 +104,7 @@ namespace TLAC::Components static void UpdateScoreCache(); static void UpdateSingleScoreCacheEntry(int pvNum, int diff, int exDiff, bool doDefaultsReset); static void UpdateSingleScoreCacheRivalEntry(int pvNum, int diff, int exDiff); + static void FixScoreCacheAddresses(int diff); // only call this from the main thread static DivaScore* GetCachedScore(int pvNum, int diff, int exDiff); static void UpdateClearCounts(); @@ -123,5 +124,8 @@ namespace TLAC::Components int currentDifficulty; int currentDifficultyIsEx; byte currentInsurance; + + static bool initCacheFinished; + static bool didInitialAddressUpdate; }; } From 3ee6484bb03e7e234a693a614e200da0a31da61b Mon Sep 17 00:00:00 2001 From: somewhatlurker <52014015+somewhatlurker@users.noreply.github.com> Date: Fri, 18 Oct 2019 19:05:20 +1100 Subject: [PATCH 4/4] score saver: fix insurance update check --- source-code/source/plugins/TLAC/Components/ScoreSaver.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/source-code/source/plugins/TLAC/Components/ScoreSaver.cpp b/source-code/source/plugins/TLAC/Components/ScoreSaver.cpp index 0133897..4b68d39 100644 --- a/source-code/source/plugins/TLAC/Components/ScoreSaver.cpp +++ b/source-code/source/plugins/TLAC/Components/ScoreSaver.cpp @@ -344,7 +344,7 @@ namespace TLAC::Components int diffIsEx = *(int*)(GAME_INFO_ADDRESS + 0x4); byte insurance = *(byte*)(GAME_INFO_ADDRESS + 0x14); - if (pvNum != currentPv || diff != currentDifficulty || diffIsEx != currentDifficultyIsEx || insurance == currentInsurance) + if (pvNum != currentPv || diff != currentDifficulty || diffIsEx != currentDifficultyIsEx || insurance != currentInsurance) { DivaScore* cachedScore = GetCachedScore(pvNum, diff, diffIsEx); if (cachedScore == nullptr)