diff --git a/source-code/source/plugins/TLAC/Components/ScoreSaver.cpp b/source-code/source/plugins/TLAC/Components/ScoreSaver.cpp index 072faf2..c56eb5b 100644 --- a/source-code/source/plugins/TLAC/Components/ScoreSaver.cpp +++ b/source-code/source/plugins/TLAC/Components/ScoreSaver.cpp @@ -1,6 +1,7 @@ #include "ScoreSaver.h" #include "../Constants.h" #include "../framework.h" +#include "../Utilities/Operations.h" #include #include #include @@ -12,7 +13,7 @@ namespace TLAC::Components ScoreSaver::ScoreSaver() { std::string utf8path = TLAC::framework::GetModuleDirectory() + "/scores.ini"; - MultiByteToWideChar(CP_UTF8, 0, utf8path.c_str(), -1, ScoreSaver::configPath, 256); + MultiByteToWideChar(CP_UTF8, 0, utf8path.c_str(), -1, configPath, 256); } ScoreSaver::~ScoreSaver() @@ -24,9 +25,62 @@ namespace TLAC::Components return "score_saver"; } + char ScoreSaver::selHighScore[9] = "39393939"; + char ScoreSaver::selHighPct1[4] = "39"; + char ScoreSaver::selHighPct2[3] = "39"; void(__stdcall* ScoreSaver::divaInitResults)(void* cls) = (void(__stdcall*)(void* cls))RESULTS_INIT_ADDRESS; void ScoreSaver::Initialize(ComponentsManager*) { + // replace code that loads personal high scores on pv select screen with code that loads custom strings + + InjectCode((void*)0x1405bfb23, { // MOV R9, &ScoreSaver::selHighScore + 0x49, + 0xb9, + (uint8_t)((uint64_t)&selHighScore & 0xFF), + (uint8_t)(((uint64_t)&selHighScore >> 8) & 0xFF), + (uint8_t)(((uint64_t)&selHighScore >> 16) & 0xFF), + (uint8_t)(((uint64_t)&selHighScore >> 24) & 0xFF), + (uint8_t)(((uint64_t)&selHighScore >> 32) & 0xFF), + (uint8_t)(((uint64_t)&selHighScore >> 40) & 0xFF), + (uint8_t)(((uint64_t)&selHighScore >> 48) & 0xFF), + (uint8_t)(((uint64_t)&selHighScore >> 56) & 0xFF), + }); + InjectCode((void*)0x1405bfb2d, { 0x4c, 0x8d, 0x05, 0x28, 0x5e, 0x3e, 0x00 }); // LEA R8, [0x1409a595c] + InjectCode((void*)0x1405bfb34, { 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90 }); + + InjectCode((void*)0x1405bfc42, { // MOV RDI, &selHighPct1 + 0x48, + 0xbf, + (uint8_t)((uint64_t)&selHighPct1 & 0xFF), + (uint8_t)(((uint64_t)&selHighPct1 >> 8) & 0xFF), + (uint8_t)(((uint64_t)&selHighPct1 >> 16) & 0xFF), + (uint8_t)(((uint64_t)&selHighPct1 >> 24) & 0xFF), + (uint8_t)(((uint64_t)&selHighPct1 >> 32) & 0xFF), + (uint8_t)(((uint64_t)&selHighPct1 >> 40) & 0xFF), + (uint8_t)(((uint64_t)&selHighPct1 >> 48) & 0xFF), + (uint8_t)(((uint64_t)&selHighPct1 >> 56) & 0xFF), + }); + InjectCode((void*)0x1405bfc4c, { 0xeb, 0x09 }); // JMP 0x1405bfc57 + InjectCode((void*)0x1405bfc69, { 0x4c, 0x8b, 0xcf }); // MOV R9, RDI + InjectCode((void*)0x1405bfc6c, { 0x4c, 0x8d, 0x05, 0xe9, 0x5c, 0x3e, 0x00 }); // LEA R8, [0x1409a595c] + + InjectCode((void*)0x1405bfd98, { // MOV RSI, &selHighPct2 + 0x48, + 0xbe, + (uint8_t)((uint64_t)&selHighPct2 & 0xFF), + (uint8_t)(((uint64_t)&selHighPct2 >> 8) & 0xFF), + (uint8_t)(((uint64_t)&selHighPct2 >> 16) & 0xFF), + (uint8_t)(((uint64_t)&selHighPct2 >> 24) & 0xFF), + (uint8_t)(((uint64_t)&selHighPct2 >> 32) & 0xFF), + (uint8_t)(((uint64_t)&selHighPct2 >> 40) & 0xFF), + (uint8_t)(((uint64_t)&selHighPct2 >> 48) & 0xFF), + (uint8_t)(((uint64_t)&selHighPct2 >> 56) & 0xFF), + }); + InjectCode((void*)0x1405bfda2, { 0xeb, 0x0c }); // JMP 0x1405bfdb0 + InjectCode((void*)0x1405bfdc2, { 0x4c, 0x8b, 0xce }); // MOV R9, RSI + InjectCode((void*)0x1405bfdc5, { 0x4c, 0x8d, 0x05, 0x90, 0x5b, 0x3e, 0x00 }); // LEA R8, [0x1409a595c] + + DetourTransactionBegin(); DetourUpdateThread(GetCurrentThread()); DetourAttach(&(PVOID&)ScoreSaver::divaInitResults, (PVOID)(ScoreSaver::hookedInitResults)); @@ -38,7 +92,7 @@ namespace TLAC::Components divaInitResults(cls); int clearRank = *(int*)(RESULTS_BASE_ADDRESS + 0xe8); - int insurance = *(int*)RESULTS_INSURANCE_ADDRESS; + int insurance = *(int*)(GAME_INFO_ADDRESS + 0x14); if (clearRank < 2 || insurance !=0) return; @@ -48,6 +102,7 @@ namespace TLAC::Components 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); @@ -68,75 +123,154 @@ namespace TLAC::Components MultiByteToWideChar(CP_UTF8, 0, utf8song, -1, songName, 256); - WCHAR keyBase[16]; // needs to be big enough to store pv.999.diff.4 + WCHAR keyBase[32]; // needs to be big enough to store pv.999.diff.4.ex WCHAR key[32]; // needs to be big enough to store pv.999.diff.4.challengescore WCHAR val[32]; // needs to be big enough to store five <=four digit ints (with comma separators) const WCHAR section[] = L"scores"; - swprintf(keyBase, 16, L"pv.%03d.diff.%01d", pvNum, pvDifficulty); + 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, ScoreSaver::configPath); + int oldScore = GetPrivateProfileIntW(section, key, 0, configPath); if (score <= oldScore) return; swprintf(key, 32, L"%ls.%ls", keyBase, L"challengescore"); swprintf(val, 32, L"%d", challengeScore); - WritePrivateProfileStringW(section, key, val, ScoreSaver::configPath); + WritePrivateProfileStringW(section, key, val, configPath); swprintf(key, 32, L"%ls.%ls", keyBase, L"combo"); swprintf(val, 32, L"%d", combo); - WritePrivateProfileStringW(section, key, val, ScoreSaver::configPath); + WritePrivateProfileStringW(section, key, val, configPath); swprintf(key, 32, L"%ls.%ls", keyBase, L"holdscore"); swprintf(val, 32, L"%d", holdScore); - WritePrivateProfileStringW(section, key, val, ScoreSaver::configPath); + WritePrivateProfileStringW(section, key, val, configPath); swprintf(key, 32, L"%ls.%ls", keyBase, L"modifier"); swprintf(val, 32, L"%d", modifier); - WritePrivateProfileStringW(section, key, val, ScoreSaver::configPath); + WritePrivateProfileStringW(section, key, val, configPath); swprintf(key, 32, L"%ls.%ls", keyBase, L"name"); - WritePrivateProfileStringW(section, key, songName, ScoreSaver::configPath); + 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, ScoreSaver::configPath); + 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, ScoreSaver::configPath); + WritePrivateProfileStringW(section, key, val, configPath); swprintf(key, 32, L"%ls.%ls", keyBase, L"percent"); swprintf(val, 32, L"%d", percent); - WritePrivateProfileStringW(section, key, val, ScoreSaver::configPath); + WritePrivateProfileStringW(section, key, val, configPath); swprintf(key, 32, L"%ls.%ls", keyBase, L"rank"); swprintf(val, 32, L"%d", clearRank); - WritePrivateProfileStringW(section, key, val, ScoreSaver::configPath); + WritePrivateProfileStringW(section, key, val, configPath); swprintf(key, 32, L"%ls.%ls", keyBase, L"score"); swprintf(val, 32, L"%d", score); - WritePrivateProfileStringW(section, key, val, ScoreSaver::configPath); + WritePrivateProfileStringW(section, key, val, configPath); swprintf(key, 32, L"%ls.%ls", keyBase, L"slidescore"); swprintf(val, 32, L"%d", slideScore); - WritePrivateProfileStringW(section, key, val, ScoreSaver::configPath); + WritePrivateProfileStringW(section, key, val, configPath); swprintf(key, 32, L"%ls.%ls", keyBase, L"check"); swprintf(val, 32, L"%d", checkField); - WritePrivateProfileStringW(section, key, val, ScoreSaver::configPath); + WritePrivateProfileStringW(section, key, val, configPath); } void ScoreSaver::Update() { - return; + int pvNum = *(int*)SELPV_CURRENT_SONG_ADDRESS; + int diff = *(int*)(GAME_INFO_ADDRESS); + int diffIsEx = *(int*)(GAME_INFO_ADDRESS + 0x4); + + if (pvNum == currentPv && diff == currentDifficulty && diffIsEx == currentDifficultyIsEx) + return; + + currentPv = pvNum; + currentDifficulty = diff; + currentDifficultyIsEx = diffIsEx; + + + WCHAR keyBase[32]; // needs to be big enough to store pv.999.diff.4.ex + WCHAR key[32]; // needs to be big enough to store pv.999.diff.4.challengescore + + const WCHAR section[] = L"scores"; + + if (currentDifficultyIsEx == 0) + swprintf(keyBase, 32, L"pv.%03d.diff.%01d", currentPv, currentDifficulty); + else + swprintf(keyBase, 32, L"pv.%03d.diff.%01d.ex", currentPv, currentDifficulty); + + + 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 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"check"); + int expectedCheck = GetPrivateProfileIntW(section, key, -1, configPath); + + + if (expectedCheck == (int)(((short)score ^ ((short)cntHitTypes[0] << 16) ^ ((short)cntHitTypes[1]) ^ ((short)percent << 16) ^ ((short)combo)) * clearRank)) + { + if (score > 99999999) + score = 99999999; + + snprintf(selHighScore, sizeof(selHighScore), "%d", score); + snprintf(selHighPct1, sizeof(selHighPct1), "%d", percent / 100); + snprintf(selHighPct2, sizeof(selHighPct2), "%d", percent % 100); + } + else + { + snprintf(selHighScore, sizeof(selHighScore), "--------"); + snprintf(selHighPct1, sizeof(selHighPct1), "---"); + snprintf(selHighPct2, sizeof(selHighPct2), "--"); + } } void ScoreSaver::UpdateInput() { return; } + + void ScoreSaver::InjectCode(void* address, const std::vector 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); + } } diff --git a/source-code/source/plugins/TLAC/Components/ScoreSaver.h b/source-code/source/plugins/TLAC/Components/ScoreSaver.h index ee08765..1a647b9 100644 --- a/source-code/source/plugins/TLAC/Components/ScoreSaver.h +++ b/source-code/source/plugins/TLAC/Components/ScoreSaver.h @@ -2,6 +2,7 @@ #include "EmulatorComponent.h" #include "../Constants.h" #include +#include namespace TLAC::Components { @@ -20,7 +21,15 @@ namespace TLAC::Components private: static void(__stdcall* divaInitResults)(void* cls); static void hookedInitResults(void* cls); + static void InjectCode(void* address, const std::vector data); static WCHAR configPath[256]; + static char selHighScore[9]; + static char selHighPct1[4]; + static char selHighPct2[3]; + + int currentPv; + int currentDifficulty; + int currentDifficultyIsEx; }; } diff --git a/source-code/source/plugins/TLAC/Constants.h b/source-code/source/plugins/TLAC/Constants.h index 0d975f0..c26ca9a 100644 --- a/source-code/source/plugins/TLAC/Constants.h +++ b/source-code/source/plugins/TLAC/Constants.h @@ -90,10 +90,11 @@ constexpr uint64_t DOEXIT_ADDRESS = 0x000000014085B574; constexpr uint64_t RESULTS_INIT_ADDRESS = 0x000000014065BBC0; constexpr uint64_t RESULTS_BASE_ADDRESS = 0x000000014CC93830; -constexpr uint64_t RESULTS_INSURANCE_ADDRESS = 0x0000000141197E14; +constexpr uint64_t GAME_INFO_ADDRESS = 0x0000000141197E00; constexpr uint64_t CURRENT_SONG_NAME_ADDRESS = 0x0000000140D0A578; constexpr uint64_t CURRENT_SONG_ID_ADDRESS = 0x0000000140CDD8E0; +constexpr uint64_t SELPV_CURRENT_SONG_ADDRESS = 0x000000014CC12438; #define XINPUT_A 0x00 #define XINPUT_B 0x01