123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783 |
- #include "ScoreSaver.h"
- #include "GameState.h"
- #include "../Constants.h"
- #include "../framework.h"
- #include "../Utilities/Operations.h"
- #include <stdio.h>
- #include <windows.h>
- #include <detours.h>
- #include <algorithm>
- #pragma comment(lib, "detours.lib")
- namespace TLAC::Components
- {
- WCHAR ScoreSaver::configPath[256];
- WCHAR ScoreSaver::rival_configPath[256];
- WCHAR ScoreSaver::modules_configPath[256];
- WCHAR ScoreSaver::skins_configPath[256];
- WCHAR ScoreSaver::sfx_configPath[256];
- ScoreSaver::ScoreSaver()
- {
- std::string utf8path = TLAC::framework::GetModuleDirectory() + "/scores.ini";
- MultiByteToWideChar(CP_UTF8, 0, utf8path.c_str(), -1, configPath, 256);
- utf8path = TLAC::framework::GetModuleDirectory() + "/rivalscores.ini";
- MultiByteToWideChar(CP_UTF8, 0, utf8path.c_str(), -1, rival_configPath, 256);
- utf8path = TLAC::framework::GetModuleDirectory() + "/pv_equip/modules.ini";
- MultiByteToWideChar(CP_UTF8, 0, utf8path.c_str(), -1, modules_configPath, 256);
- utf8path = TLAC::framework::GetModuleDirectory() + "/pv_equip/skins.ini";
- MultiByteToWideChar(CP_UTF8, 0, utf8path.c_str(), -1, skins_configPath, 256);
- utf8path = TLAC::framework::GetModuleDirectory() + "/pv_equip/sfx.ini";
- MultiByteToWideChar(CP_UTF8, 0, utf8path.c_str(), -1, sfx_configPath, 256);
- }
- 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()
- {
- 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;
- std::thread ScoreSaver::initThread;
- void ScoreSaver::Initialize(ComponentsManager*)
- {
- DetourTransactionBegin();
- DetourUpdateThread(GetCurrentThread());
- DetourAttach(&(PVOID&)ScoreSaver::divaInitResults, (PVOID)(ScoreSaver::hookedInitResults));
- DetourTransactionCommit();
- initThread = std::thread(initCache);
- }
- bool ScoreSaver::checkExistingScoreValid(int pv, int difficulty, int isEx)
- {
- WCHAR keyBase[32]; // needs to be big enough to store pv.999.diff.3.ex
- WCHAR key[32]; // needs to be big enough to store pv.999.diff.3.alltimemodifiers
- const WCHAR section[] = L"scores";
- if (isEx == 0)
- swprintf(keyBase, 32, L"pv.%03d.diff.%01d", pv, difficulty);
- else
- swprintf(keyBase, 32, L"pv.%03d.diff.%01d.ex", pv, difficulty);
- swprintf(key, 32, L"%ls.%ls", keyBase, L"score");
- int score = GetPrivateProfileIntW(section, key, 0, configPath);
-
- swprintf(key, 32, L"%ls.%ls", keyBase, L"notecounts");
- WCHAR countsBufW[32];
- char countsBufA[32];
- int cntHitTypes[2] = { 0, 0 };
- GetPrivateProfileStringW(section, key, L"", countsBufW, 32, configPath);
- WideCharToMultiByte(CP_UTF8, 0, countsBufW, -1, countsBufA, 32, NULL, NULL);
- std::vector<std::string> countToks = TLAC::Utilities::Split(countsBufA, ",");
- if (countToks.size() >= 2)
- {
- cntHitTypes[0] = atoi(countToks[0].c_str());
- cntHitTypes[1] = atoi(countToks[1].c_str());
- }
- swprintf(key, 32, L"%ls.%ls", keyBase, L"percent");
- int percent = GetPrivateProfileIntW(section, key, 0, configPath);
- swprintf(key, 32, L"%ls.%ls", keyBase, L"combo");
- int combo = GetPrivateProfileIntW(section, key, 0, configPath);
- swprintf(key, 32, L"%ls.%ls", keyBase, L"rank");
- int clearRank = GetPrivateProfileIntW(section, key, 0, configPath);
- swprintf(key, 32, L"%ls.%ls", keyBase, L"alltimerank");
- int allTimeRank = GetPrivateProfileIntW(section, key, 0, configPath);
- swprintf(key, 32, L"%ls.%ls", keyBase, L"alltimemodifiers");
- int allTimeModifiers = GetPrivateProfileIntW(section, key, 0, configPath);
- swprintf(key, 32, L"%ls.%ls", keyBase, L"alltimepercent");
- int allTimePercent = GetPrivateProfileIntW(section, key, 0, configPath);
- swprintf(key, 32, L"%ls.%ls", keyBase, L"check");
- int expectedCheck = GetPrivateProfileIntW(section, key, -1, configPath);
- return expectedCheck == calculateCheck(score, cntHitTypes[0], cntHitTypes[1], percent, combo, clearRank, allTimeRank, allTimeModifiers, allTimePercent);
- }
- int ScoreSaver::calculateCheck(int score, int cntCools, int cntFines, int percent, int combo, int clearRank, int allTimeRank, int allTimeModifiers, int allTimePercent)
- {
- int checkField = ((short)score ^ ((short)cntCools << 16) ^ ((short)cntFines) ^ ((short)percent << 16) ^ ((short)combo)) * clearRank;
- checkField += (allTimeRank * clearRank); // first extension
- checkField ^= (allTimeModifiers * allTimePercent);
- return checkField;
- }
-
- bool ScoreSaver::hookedInitResults(void* cls)
- {
- bool result = divaInitResults(cls);
-
- int clearRank = *(int*)(RESULTS_BASE_ADDRESS + 0xe8);
- byte insurance = *(byte*)(GAME_INFO_ADDRESS + 0x14);
-
- if (insurance != 0)
- return result;
- // get the base for this specific set of results
- uint64_t resultBase = *(uint64_t*)(RESULTS_BASE_ADDRESS + 0x100);
-
- int pvNum = *(int*)(resultBase + 0x2c);
- int pvDifficulty = *(int*)(resultBase + 0x34);
- int pvDifficultyIsEx = *(int*)(resultBase + 0x44);
- int modifier = *(int*)(resultBase + 0x70);
-
- int* cntHitTypes = (int*)(resultBase + 0x158);
- int* pctHitTypes = (int*)(resultBase + 0x16c);
- int combo = *(int*)(resultBase + 0x180);
- int challengeScore = *(int*)(resultBase + 0x184);
- int holdScore = *(int*)(resultBase + 0x188);
- int score = *(int*)(resultBase + 0x18c);
- int percent = *(int*)(resultBase + 0x190);
- int slideScore = *(int*)(resultBase + 0x194);
-
- WCHAR songName[256];
- //std::string utf8song = *(std::string*)(CURRENT_SONG_NAME_ADDRESS);
- uint64_t songnamelen = *(uint64_t*)(CURRENT_SONG_NAME_ADDRESS + 0x18);
- char* utf8song = songnamelen < 0x10 ? (char*)CURRENT_SONG_NAME_ADDRESS : *(char**)CURRENT_SONG_NAME_ADDRESS;
- MultiByteToWideChar(CP_UTF8, 0, utf8song, -1, songName, 256);
- WCHAR keyBase[32]; // needs to be big enough to store pv.999.diff.3.ex
- WCHAR key[32]; // needs to be big enough to store pv.999.diff.3.alltimemodifiers
- WCHAR val[32]; // needs to be big enough to store five <=four digit ints (with comma separators)
- const WCHAR section[] = L"scores";
- if (pvDifficultyIsEx == 0)
- swprintf(keyBase, 32, L"pv.%03d.diff.%01d", pvNum, pvDifficulty);
- else
- swprintf(keyBase, 32, L"pv.%03d.diff.%01d.ex", pvNum, pvDifficulty);
- swprintf(key, 32, L"%ls.%ls", keyBase, L"score");
- int oldScore = GetPrivateProfileIntW(section, key, 0, configPath);
- swprintf(key, 32, L"%ls.%ls", keyBase, L"alltimerank");
- int oldAlltimeRank = GetPrivateProfileIntW(section, key, -1, configPath);
- if (oldAlltimeRank == -1) // fallback for old scores without alltimerank
- {
- swprintf(key, 32, L"%ls.%ls", keyBase, L"rank");
- oldAlltimeRank = GetPrivateProfileIntW(section, key, 0, configPath);
- }
- int allTimeRank = clearRank > oldAlltimeRank ? clearRank : oldAlltimeRank;
- swprintf(key, 32, L"%ls.%ls", keyBase, L"alltimemodifiers");
- int oldAlltimeModifiers = GetPrivateProfileIntW(section, key, -1, configPath);
- if (oldAlltimeModifiers == -1) // fallback for old scores without alltimemodifiers
- {
- swprintf(key, 32, L"%ls.%ls", keyBase, L"modifier");
- oldAlltimeModifiers = GetPrivateProfileIntW(section, key, 0, configPath);
- oldAlltimeModifiers = oldAlltimeModifiers > 0 ? 1 << (oldAlltimeModifiers - 1) : 0;
- }
- int allTimeModifiers = oldAlltimeModifiers | ((clearRank > 1 && modifier > 0) ? (1 << (modifier - 1)) : 0); // use clearRank to not update if didn't clear
- swprintf(key, 32, L"%ls.%ls", keyBase, L"alltimepercent");
- int oldAlltimePercent = GetPrivateProfileIntW(section, key, -1, configPath);
- if (oldAlltimePercent == -1) // fallback for old scores without alltimepercent
- {
- swprintf(key, 32, L"%ls.%ls", keyBase, L"percent");
- oldAlltimePercent = GetPrivateProfileIntW(section, key, 0, configPath);
- }
- int allTimePercent = percent > oldAlltimePercent ? percent : oldAlltimePercent;
- bool oldScoreValid = true;
- if (!checkExistingScoreValid(pvNum, pvDifficulty, pvDifficultyIsEx))
- {
- oldScore = 0; // ensure new score is written
- allTimeRank = clearRank; // don't trust old all-time clear rank, use a new one
- allTimeModifiers = ((clearRank > 1 && modifier > 0) ? (1 << (modifier - 1)) : 0); // don't trust old all-time modifiers, use a new one
- allTimePercent = percent; // don't trust old all-time percent, use a new one
- oldScoreValid = false;
- }
- if (clearRank > 1 && score > oldScore)
- {
- swprintf(key, 32, L"%ls.%ls", keyBase, L"alltimemodifiers");
- swprintf(val, 32, L"%d", allTimeModifiers);
- WritePrivateProfileStringW(section, key, val, configPath);
- swprintf(key, 32, L"%ls.%ls", keyBase, L"alltimepercent");
- swprintf(val, 32, L"%d", allTimePercent);
- WritePrivateProfileStringW(section, key, val, configPath);
- swprintf(key, 32, L"%ls.%ls", keyBase, L"alltimerank");
- swprintf(val, 32, L"%d", allTimeRank);
- WritePrivateProfileStringW(section, key, val, configPath);
- swprintf(key, 32, L"%ls.%ls", keyBase, L"challengescore");
- swprintf(val, 32, L"%d", challengeScore);
- WritePrivateProfileStringW(section, key, val, configPath);
- swprintf(key, 32, L"%ls.%ls", keyBase, L"combo");
- swprintf(val, 32, L"%d", combo);
- WritePrivateProfileStringW(section, key, val, configPath);
- swprintf(key, 32, L"%ls.%ls", keyBase, L"holdscore");
- swprintf(val, 32, L"%d", holdScore);
- WritePrivateProfileStringW(section, key, val, configPath);
- swprintf(key, 32, L"%ls.%ls", keyBase, L"modifier");
- swprintf(val, 32, L"%d", modifier);
- WritePrivateProfileStringW(section, key, val, configPath);
- swprintf(key, 32, L"%ls.%ls", keyBase, L"name");
- WritePrivateProfileStringW(section, key, songName, configPath);
- swprintf(key, 32, L"%ls.%ls", keyBase, L"notecounts");
- swprintf(val, 32, L"%d,%d,%d,%d,%d", cntHitTypes[0], cntHitTypes[1], cntHitTypes[2], cntHitTypes[3], cntHitTypes[4]);
- WritePrivateProfileStringW(section, key, val, configPath);
- swprintf(key, 32, L"%ls.%ls", keyBase, L"notepercents");
- swprintf(val, 32, L"%d,%d,%d,%d,%d", pctHitTypes[0], pctHitTypes[1], pctHitTypes[2], pctHitTypes[3], pctHitTypes[4]);
- WritePrivateProfileStringW(section, key, val, configPath);
- swprintf(key, 32, L"%ls.%ls", keyBase, L"percent");
- swprintf(val, 32, L"%d", percent);
- WritePrivateProfileStringW(section, key, val, configPath);
- swprintf(key, 32, L"%ls.%ls", keyBase, L"rank");
- swprintf(val, 32, L"%d", clearRank);
- WritePrivateProfileStringW(section, key, val, configPath);
- swprintf(key, 32, L"%ls.%ls", keyBase, L"score");
- swprintf(val, 32, L"%d", score);
- WritePrivateProfileStringW(section, key, val, configPath);
- swprintf(key, 32, L"%ls.%ls", keyBase, L"slidescore");
- swprintf(val, 32, L"%d", slideScore);
- WritePrivateProfileStringW(section, key, val, configPath);
- int checkField = calculateCheck(score, cntHitTypes[0], cntHitTypes[1], percent, combo, clearRank, allTimeRank, allTimeModifiers, allTimePercent);
- swprintf(key, 32, L"%ls.%ls", keyBase, L"check");
- swprintf(val, 32, L"%d", checkField);
- WritePrivateProfileStringW(section, key, val, configPath);
- }
- else // if (oldScoreValid)
- {
- if (allTimeRank != oldAlltimeRank) // first extension
- {
- swprintf(key, 32, L"%ls.%ls", keyBase, L"alltimerank");
- swprintf(val, 32, L"%d", allTimeRank);
- WritePrivateProfileStringW(section, key, val, configPath);
- swprintf(key, 32, L"%ls.%ls", keyBase, L"rank");
- int oldClearRank = GetPrivateProfileIntW(section, key, 0, configPath);
- swprintf(key, 32, L"%ls.%ls", keyBase, L"check");
- int oldCheck = GetPrivateProfileIntW(section, key, 0, configPath);
- swprintf(key, 32, L"%ls.%ls", keyBase, L"check");
- swprintf(val, 32, L"%d", oldCheck - (oldAlltimeRank * oldClearRank) + (allTimeRank * oldClearRank));
- WritePrivateProfileStringW(section, key, val, configPath);
- }
- if (allTimeModifiers != oldAlltimeModifiers || allTimePercent != oldAlltimePercent) // second extension
- {
- swprintf(key, 32, L"%ls.%ls", keyBase, L"alltimemodifiers");
- swprintf(val, 32, L"%d", allTimeModifiers);
- WritePrivateProfileStringW(section, key, val, configPath);
- swprintf(key, 32, L"%ls.%ls", keyBase, L"alltimepercent");
- swprintf(val, 32, L"%d", allTimePercent);
- WritePrivateProfileStringW(section, key, val, configPath);
- swprintf(key, 32, L"%ls.%ls", keyBase, L"check");
- int oldCheck = GetPrivateProfileIntW(section, key, 0, configPath);
- swprintf(key, 32, L"%ls.%ls", keyBase, L"check");
- swprintf(val, 32, L"%d", oldCheck ^ (oldAlltimeModifiers * oldAlltimePercent) ^ (allTimeModifiers * allTimePercent));
- WritePrivateProfileStringW(section, key, val, configPath);
- }
- }
- UpdateSingleScoreCacheEntry(pvNum, pvDifficulty, pvDifficultyIsEx, true);
- 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 (!didInitialAddressUpdate)
- {
- if (initCacheFinished) // check for initThread to be done
- {
- // 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);
- 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);
- }
- else
- {
- UpdateSingleScoreCacheModulesEntry;
- }
- currentPv = pvNum;
- currentDifficulty = diff;
- currentDifficultyIsEx = diffIsEx;
- currentInsurance = insurance;
- }
- ModuleCheck(pvNum, diff, diffIsEx);
- }
- }
- }
- void ScoreSaver::UpdateInput()
- {
- return;
- }
- void ScoreSaver::InjectCode(void* address, const std::vector<uint8_t> data)
- {
- const size_t byteCount = data.size() * sizeof(uint8_t);
- DWORD oldProtect;
- VirtualProtect(address, byteCount, PAGE_EXECUTE_READWRITE, &oldProtect);
- memcpy(address, data.data(), byteCount);
- VirtualProtect(address, byteCount, oldProtect, nullptr);
- }
- std::vector<ScoreSaver::DivaScore> 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;
- WCHAR keyBase[32]; // needs to be big enough to store pv.999.diff.3.ex
- WCHAR key[32]; // needs to be big enough to store pv.999.diff.3.alltimemodifiers
- const WCHAR section[] = L"scores";
- if (exDiff == 0)
- swprintf(keyBase, 32, L"pv.%03d.diff.%01d", pvNum, diff);
- else
- swprintf(keyBase, 32, L"pv.%03d.diff.%01d.ex", pvNum, diff);
-
- if (checkExistingScoreValid(pvNum, diff, exDiff))
- {
- swprintf(key, 32, L"%ls.%ls", keyBase, L"score");
- int score = GetPrivateProfileIntW(section, key, 0, configPath);
- if (score > 99999999)
- score = 99999999;
- swprintf(key, 32, L"%ls.%ls", keyBase, L"alltimepercent");
- int percent = GetPrivateProfileIntW(section, key, -1, configPath);
- if (percent == -1) // fallback for old scores without alltimepercent
- {
- swprintf(key, 32, L"%ls.%ls", keyBase, L"percent");
- percent = GetPrivateProfileIntW(section, key, 0, configPath);
- }
- swprintf(key, 32, L"%ls.%ls", keyBase, L"alltimerank");
- int allTimeRank = GetPrivateProfileIntW(section, key, -1, configPath);
- if (allTimeRank == -1) // fallback for old scores without alltimerank
- {
- swprintf(key, 32, L"%ls.%ls", keyBase, L"rank");
- allTimeRank = GetPrivateProfileIntW(section, key, -1, configPath);
- }
- swprintf(key, 32, L"%ls.%ls", keyBase, L"alltimemodifiers");
- int modifiers = GetPrivateProfileIntW(section, key, -1, configPath);
- if (modifiers == -1) // fallback for old scores without alltimemodifiers
- {
- swprintf(key, 32, L"%ls.%ls", keyBase, L"modifier");
- modifiers = GetPrivateProfileIntW(section, key, 0, configPath);
- modifiers = modifiers > 0 ? 1 << (modifiers - 1) : 0;
- }
- DivaScore* cachedScore = GetCachedScore(pvNum, diff, exDiff);
- if (cachedScore == nullptr)
- {
- ScoreCache[diff].push_back(DivaScore(pvNum, exDiff));
- cachedScore = GetCachedScore(pvNum, diff, exDiff);
- }
- 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;
- }
- }
- }
- void ScoreSaver::UpdateSingleScoreCacheRivalEntry(int pvNum, int diff, int exDiff)
- {
- if (pvNum < 0 || diff < 0 || exDiff < 0 || pvNum > 999 || diff > 3 || exDiff > 1)
- return;
- WCHAR keyBase[32]; // needs to be big enough to store pv.999.diff.3.ex
- WCHAR key[32]; // needs to be big enough to store pv.999.diff.3.alltimemodifiers
- const WCHAR section[] = L"scores";
- if (exDiff == 0)
- swprintf(keyBase, 32, L"pv.%03d.diff.%01d", pvNum, diff);
- else
- swprintf(keyBase, 32, L"pv.%03d.diff.%01d.ex", pvNum, diff);
- swprintf(key, 32, L"%ls.%ls", keyBase, L"score");
- int score = GetPrivateProfileIntW(section, key, 0, rival_configPath);
- if (score > 99999999)
- score = 99999999;
- swprintf(key, 32, L"%ls.%ls", keyBase, L"alltimepercent");
- int percent = GetPrivateProfileIntW(section, key, -1, rival_configPath);
- if (percent == -1) // fallback for old scores without alltimepercent
- {
- swprintf(key, 32, L"%ls.%ls", keyBase, L"percent");
- percent = GetPrivateProfileIntW(section, key, 0, rival_configPath);
- }
- swprintf(key, 32, L"%ls.%ls", keyBase, L"alltimerank");
- int allTimeRank = GetPrivateProfileIntW(section, key, -1, rival_configPath);
- if (allTimeRank == -1) // fallback for old scores without alltimerank
- {
- swprintf(key, 32, L"%ls.%ls", keyBase, L"rank");
- allTimeRank = GetPrivateProfileIntW(section, key, -1, rival_configPath);
- }
- 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);
- }
- if (cachedScore != nullptr)
- {
- cachedScore->rival_clearRank = allTimeRank;
- cachedScore->rival_score = score;
- cachedScore->rival_percent = percent;
- }
- }
- }
- void ScoreSaver::UpdateSingleScoreCacheModulesEntry(int pvNum, int diff, int exDiff)
- {
- if (pvNum < 0 || diff < 0 || exDiff < 0 || pvNum > 999 || diff > 3 || exDiff > 1)
- return;
- WCHAR keyBase[32]; // needs to be big enough to store pv.999
- WCHAR key[32]; // needs to be big enough to store pv.999.module5
- const WCHAR section[] = L"modules";
- swprintf(keyBase, 32, L"pv.%03d", pvNum);
- for (int i = 0; i < 6; ++i)
- {
- swprintf(key, 32, L"%ls.module%d", keyBase, i);
- int INImodule = GetPrivateProfileIntW(section, key, 0, modules_configPath);
- if (INImodule > 0)
- {
- DivaScore* cachedScore = GetCachedScore(pvNum, diff, exDiff);
- if (cachedScore == nullptr)
- {
- ScoreCache[diff].push_back(DivaScore(pvNum, exDiff));
- cachedScore = GetCachedScore(pvNum, diff, exDiff);
- }
- if (cachedScore != nullptr)
- {
- cachedScore->per_module_equip[i] = INImodule;
- }
- }
- }
- }
- void ScoreSaver::ModuleCheck(int pvNum, int diff, int exDiff)
- {
- if (pvNum < 0 || diff < 0 || exDiff < 0 || pvNum > 999 || diff > 3 || exDiff > 1)
- return;
- WCHAR keyBase[32]; // needs to be big enough to store pv.999
- WCHAR key[32]; // needs to be big enough to store pv.999.module5
- const WCHAR section[] = L"modules";
- swprintf(keyBase, 32, L"pv.%03d", pvNum);
- DivaScore* cachedScore = GetCachedScore(pvNum, diff, exDiff);
- for (int i = 0; i < 6; ++i)
- {
- swprintf(key, 32, L"%ls.module%d", keyBase, i);
- int INImodule = GetPrivateProfileIntW(section, key, 0, modules_configPath);
- if (INImodule != cachedScore->per_module_equip[i])
- {
- INImodule = *cachedScore->per_module_equip;
- WCHAR val[32];
- swprintf(val, 32, L"%d", cachedScore->per_module_equip[i]);
- WritePrivateProfileStringW(L"modules", key, val, modules_configPath);
- }
- }
-
- }
- void ScoreSaver::UpdateSingleScoreCacheSkinsEntry(int pvNum, int diff, int exDiff)
- {
- if (pvNum < 0 || diff < 0 || exDiff < 0 || pvNum > 999 || diff > 3 || exDiff > 1)
- return;
- WCHAR keyBase[32]; // needs to be big enough to store pv.999
- WCHAR key[32]; // needs to be big enough to store pv.999.module5
- const WCHAR section[] = L"skins";
- swprintf(keyBase, 32, L"pv.%03d", pvNum);
- swprintf(key, 32, L"%ls.skin", keyBase);
- int INISkin = GetPrivateProfileIntW(section, key, 0, skins_configPath);
- if (INISkin > -1)
- {
- DivaScore* cachedScore = GetCachedScore(pvNum, diff, exDiff);
- if (cachedScore == nullptr)
- {
- ScoreCache[diff].push_back(DivaScore(pvNum, exDiff));
- cachedScore = GetCachedScore(pvNum, diff, exDiff);
- }
- if (cachedScore != nullptr)
- {
- cachedScore->per_skin_equip = INISkin;
- }
- }
- }
- void ScoreSaver::UpdateSingleScoreCacheSFXEntry(int pvNum, int diff, int exDiff)
- {
- if (pvNum < 0 || diff < 0 || exDiff < 0 || pvNum > 999 || diff > 3 || exDiff > 1)
- return;
- WCHAR keyBase[32]; // needs to be big enough to store pv.999
- WCHAR key[32]; // needs to be big enough to store pv.999.module5
- const WCHAR section[] = L"SFX";
- swprintf(keyBase, 32, L"pv.%03d", pvNum);
- swprintf(key, 32, L"%ls.btn", keyBase);
- int INIBtn = GetPrivateProfileIntW(section, key, 0, sfx_configPath);
- if (INIBtn > 0)
- {
- DivaScore* cachedScore = GetCachedScore(pvNum, diff, exDiff);
- if (cachedScore == nullptr)
- {
- ScoreCache[diff].push_back(DivaScore(pvNum, exDiff));
- cachedScore = GetCachedScore(pvNum, diff, exDiff);
- }
- if (cachedScore != nullptr)
- {
- cachedScore->per_btn_se_equip = INIBtn;
- }
- }
- swprintf(keyBase, 32, L"pv.%03d", pvNum);
- swprintf(key, 32, L"%ls.chain", keyBase);
- int INIChain = GetPrivateProfileIntW(section, key, 0, sfx_configPath);
- if (INIChain > 0)
- {
- DivaScore* cachedScore = GetCachedScore(pvNum, diff, exDiff);
- if (cachedScore == nullptr)
- {
- ScoreCache[diff].push_back(DivaScore(pvNum, exDiff));
- cachedScore = GetCachedScore(pvNum, diff, exDiff);
- }
- if (cachedScore != nullptr)
- {
- cachedScore->per_chainslide_se_equip = INIChain;
- }
- }
- swprintf(keyBase, 32, L"pv.%03d", pvNum);
- swprintf(key, 32, L"%ls.slide", keyBase);
- int INISlide = GetPrivateProfileIntW(section, key, 0, sfx_configPath);
- if (INISlide > 0)
- {
- DivaScore* cachedScore = GetCachedScore(pvNum, diff, exDiff);
- if (cachedScore == nullptr)
- {
- ScoreCache[diff].push_back(DivaScore(pvNum, exDiff));
- cachedScore = GetCachedScore(pvNum, diff, exDiff);
- }
- if (cachedScore != nullptr)
- {
- cachedScore->per_slide_se_equip = INISlide;
- }
- }
- swprintf(keyBase, 32, L"pv.%03d", pvNum);
- swprintf(key, 32, L"%ls.touch", keyBase);
- int INITouch = GetPrivateProfileIntW(section, key, 0, sfx_configPath);
- if (INITouch > 0)
- {
- DivaScore* cachedScore = GetCachedScore(pvNum, diff, exDiff);
- if (cachedScore == nullptr)
- {
- ScoreCache[diff].push_back(DivaScore(pvNum, exDiff));
- cachedScore = GetCachedScore(pvNum, diff, exDiff);
- }
- if (cachedScore != nullptr)
- {
- cachedScore->per_slidertouch_se_equip = INITouch;
- }
- }
- }
- 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++)
- {
- for (int diff = 0; diff < 4; diff++)
- {
- for (int exDiff = 0; exDiff < 2; exDiff++)
- {
- UpdateSingleScoreCacheEntry(pvNum, diff, exDiff, false);
- UpdateSingleScoreCacheRivalEntry(pvNum, diff, exDiff);
- UpdateSingleScoreCacheModulesEntry(pvNum, diff, exDiff);
- UpdateSingleScoreCacheSkinsEntry(pvNum, diff, exDiff);
- UpdateSingleScoreCacheSFXEntry(pvNum, diff, exDiff);
- }
- }
- }
- }
- void ScoreSaver::UpdateClearCounts()
- {
- int* counts = (int*)SONG_CLEAR_COUNTS_ADDRESS;
- for (int i = 0; i < 20; i++)
- {
- counts[i] = 0;
- }
- for (int diff = 0; diff < 4; diff++)
- {
- for (DivaScore &scoreinfo : ScoreCache[diff])
- {
- 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;
- }
- }
- }
- // exex special case
- for (DivaScore &scoreinfo : ScoreCache[3])
- {
- 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;
- }
- }
- // perfects count as clears for <= excellent, etc
- for (int diff = 0; diff < 5; diff++)
- {
- counts[diff * 4 + 2] += counts[diff * 4 + 3];
- counts[diff * 4 + 1] += counts[diff * 4 + 2];
- counts[diff * 4 + 0] += counts[diff * 4 + 1];
- }
- }
- }
|