diff --git a/.gitignore b/.gitignore index be2a5c8..a37f717 100644 --- a/.gitignore +++ b/.gitignore @@ -1,7 +1,9 @@ build *.3gx *.elf +*.kate-swp .vscode .cache -compile_commands.json \ No newline at end of file +3gxtool +compile_commands.json diff --git a/include/Megamix/Hooks.hpp b/include/Megamix/Hooks.hpp index e8c8c62..77f4546 100644 --- a/include/Megamix/Hooks.hpp +++ b/include/Megamix/Hooks.hpp @@ -8,10 +8,13 @@ namespace Megamix::Hooks { void TempoHooks(); void RegionHooks(); void CommandHook(); + void ScoringHook(); void DisableAllHooks(); template T StubbedFunction(); template void StubFunction(u32 address); } +extern std::vector debugScoreArray; + #endif diff --git a/include/Megamix/Region.hpp b/include/Megamix/Region.hpp index f2fc9f3..acc5770 100644 --- a/include/Megamix/Region.hpp +++ b/include/Megamix/Region.hpp @@ -27,6 +27,9 @@ namespace Megamix { u32 dataEnd; u32 bssEnd; + // scoring crap + u32 scoringHookPos; + // game definitions GameDef* gameTable; @@ -151,6 +154,8 @@ namespace Megamix { inline u32 seqTempo() { return pointers->seqTempoHookPos; } inline u32 allTempo() { return pointers->allTempoHookPos; } + inline u32 scoring() { return pointers->scoringHookPos; } + inline u32 megamixRegionCode() { return pointers->regionAHookPos; } inline u32 sdkRegionCode() { return pointers->regionBHookPos; } } @@ -193,4 +198,4 @@ namespace Megamix { } } -#endif \ No newline at end of file +#endif diff --git a/include/Megamix/Types.hpp b/include/Megamix/Types.hpp index ced1826..3e0d0f5 100644 --- a/include/Megamix/Types.hpp +++ b/include/Megamix/Types.hpp @@ -560,6 +560,44 @@ namespace Megamix { TextBox* textBox; }; + struct CResultManager{ + int* _vtable; + u8 mAmtCategories; + u8 field_0x5; + u8 mCommentTargetLowPrio; + u8 field_0x7; + s32 mCommentTargetHighPrio; + u32* mAmtHit; + u32* mAmtBarely; + u32* mAmtMiss; + s32 mAmtPenalties; + s32 field10_0x1c; + u32* mMaxWeight; + u32* field12_0x24; + u32* mScoreWeight; + u32 mPenalty; + u32* field15_0x30; + u32* field16_0x34; + u32 mOkThreshold; + u32 mHiThereshold; + u32* points; + u32* field20_0x44; + u32* mCommentMsbtFile; + char** mCommentHiEntry; + char** mCommentNgEntry; + s32 mSafeComment; + char* mSafeHiEntry; + char* mSafeOkEntry; + char* mSafeNgEntry; + s32 mMsbtFile; + char* mResultCaptionString; + u8 field_0x6c; + u8 field_0x6d; + u8 field_0x6e; + u8 field_0x64; + u32 mSkillStarInputNum; + }; + } // namespace Megamix #endif diff --git a/src/Megamix/Commands.cpp b/src/Megamix/Commands.cpp index 0c2a0fc..8891038 100644 --- a/src/Megamix/Commands.cpp +++ b/src/Megamix/Commands.cpp @@ -16,7 +16,7 @@ namespace Megamix { ); } - extern "C" __attribute__((used)) + extern "C" __attribute__((used)) int tickflowCommandsHook(CTickflow* self, u32 cmd_num, u32 arg0, u32* args){ switch(cmd_num){ // Necessary, as our hook overrides case 0. diff --git a/src/Megamix/Hooks.cpp b/src/Megamix/Hooks.cpp index fb9e78b..a30a911 100644 --- a/src/Megamix/Hooks.cpp +++ b/src/Megamix/Hooks.cpp @@ -10,6 +10,8 @@ using CTRPluginFramework::OSD; using Megamix::TempoTable; +std::vector debugScoreArray; + // c++ is stupid namespace GHooks = Megamix::Game::Hooks; @@ -27,6 +29,8 @@ namespace Megamix::Hooks { RT_HOOK tickflowCommandsHook; + RT_HOOK scoringFunctionHook; + void* getTickflowOffset(int index) { if (config->tickflows.contains(index)) { int result = Megamix::btks.LoadFile(config->tickflows[index]); @@ -138,6 +142,79 @@ namespace Megamix::Hooks { return RegionMegamix::UNK; } + int decideFinalScore(Megamix::CResultManager *arg1) { + u8 amtCategories = arg1->mAmtCategories; + u32* amtHit = arg1->mAmtHit; + u32* amtBarely = arg1->mAmtBarely; + u32* amtMiss = arg1->mAmtMiss; + float catScore10000 = 0.0f; + float totalWeight = 0.0f; + float scoreDivWeight = 0.0f; + float amtInputs = 0.0f; + float result = 0.0f; + int finalResult = 0; + float curScoreWeight = 0.0f; + int validCategories = 0; + float distributedWeight = 0.0f; + float distributedAmtInputs = 0.0f; + float distributedScore = 0.0f; + + debugScoreArray.clear(); + + if (amtCategories != 0) { + for (int g = 0; g < 7; g++) { + amtInputs = (int)(amtHit[g] + amtBarely[g] + amtMiss[g]); + if(amtInputs != 0){ + validCategories += 1; + } + } + + distributedWeight = (float)(arg1->mScoreWeight[7]) / (float)(validCategories); + distributedAmtInputs = (float)(amtHit[7] + amtBarely[7] + amtMiss[7]) / (float)(validCategories); + if (arg1->points[7] < 1) { + distributedScore = (float)(arg1->points[7] + 1) / (float)(validCategories); + } else { + distributedScore = (float)(arg1->points[7]) / (float)(validCategories); + } + + for (int i = 0; amtCategories > i; i++) { + if (i != 7) { + amtInputs = (float)(amtHit[i] + amtBarely[i] + amtMiss[i]); + if (amtInputs > 0) { + if ((float)(amtHit[i] + amtBarely[i] + amtMiss[i]) < 1) { + amtInputs = 0; + } + else { + amtInputs += distributedAmtInputs; + if (arg1->points[i] < 1) { + catScore10000 = ((arg1->points[i] + distributedScore + 1) * 10000) / (amtInputs * arg1->mMaxWeight[i]); + } else { + catScore10000 = ((arg1->points[i] + distributedScore) * 10000) / (amtInputs * arg1->mMaxWeight[i]); + } + } + + debugScoreArray.push_back(catScore10000); + + curScoreWeight = (float)(arg1->mScoreWeight[i]) + distributedWeight; + totalWeight += curScoreWeight; + scoreDivWeight += curScoreWeight/catScore10000; + } + } + } + + if (totalWeight > 0.0f) { + result = totalWeight/scoreDivWeight; + finalResult = (int)(result - (arg1->mAmtPenalties * arg1->mPenalty)); + if (finalResult > 10000) { + finalResult = 10000; + } + if (finalResult > 0) { + return finalResult; + } + } + } + return 0; + } void TickflowHooks() { rtInitHook(&tickflowHook, GHooks::tickflow(), (u32)getTickflowOffset); @@ -171,6 +248,11 @@ namespace Megamix::Hooks { rtEnableHook(&tickflowCommandsHook); } + void ScoringHook(){ + rtInitHook(&scoringFunctionHook, GHooks::scoring(), (int)decideFinalScore); + rtEnableHook(&scoringFunctionHook); + } + void DisableAllHooks() { rtDisableHook(&tickflowHook); rtDisableHook(&gateHook); @@ -179,9 +261,12 @@ namespace Megamix::Hooks { rtDisableHook(&tempoAllHook); rtDisableHook(®ionFSHook); rtDisableHook(®ionOtherHook); + rtDisableHook(&scoringFunctionHook); rtDisableHook(&tickflowCommandsHook); } + + template T StubbedFunction() { return {}; diff --git a/src/Megamix/Region.cpp b/src/Megamix/Region.cpp index c2a7781..0dc284e 100644 --- a/src/Megamix/Region.cpp +++ b/src/Megamix/Region.cpp @@ -22,6 +22,8 @@ namespace Megamix { .dataEnd= 0x540754, .bssEnd= 0x5ce1f0, + .scoringHookPos= GameInterface::UNIMPLEMENTED, + .gameTable= (GameDef*)0x522498, .gateTable= (GateGameDef*)0x525488, .tickflowHookPos= 0x25a1b4, @@ -69,6 +71,8 @@ namespace Megamix { .dataEnd= 0x54f074, .bssEnd= 0x5dc2f0, + .scoringHookPos= 0x276264, + .gameTable= (GameDef*)0x52b498, .gateTable= (GateGameDef*)0x52e488, .tickflowHookPos= 0x258df4, @@ -118,6 +122,8 @@ namespace Megamix { .dataEnd= 0x54f16c, .bssEnd= 0x5dc2f0, + .scoringHookPos= GameInterface::UNIMPLEMENTED, + .gameTable= (GameDef*)0x52b498, .gateTable= (GateGameDef*)0x52e488, .tickflowHookPos= 0x258df4, @@ -162,6 +168,8 @@ namespace Megamix { .dataEnd= 0x54f16c, //TODO: check .bssEnd= 0x5dc2f0, + .scoringHookPos= GameInterface::UNIMPLEMENTED, + .gameTable= (GameDef*)0x52b498, .gateTable= (GateGameDef*)0x52e488, .tickflowHookPos= 0x258dcc, diff --git a/src/main.cpp b/src/main.cpp index 19d6d32..f1c1d36 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -103,6 +103,9 @@ void ctrpf::PatchProcess(ctrpf::FwkSettings&) { // Start hooks, apply patches Megamix::Hooks::TickflowHooks(); + if(Megamix::isUS()) { + Megamix::Hooks::ScoringHook(); + } Megamix::Hooks::RegionHooks(); Megamix::Patches::PatchRetryRemix(); if (!Megamix::isJP()) { @@ -135,6 +138,16 @@ void InitMenu(ctrpf::PluginMenu &menu) { ))(); }); + menu += new ctrpf::MenuEntry("Category Scores", nullptr, [](ctrpf::MenuEntry*) { + std::string debugScoreOutput = ""; + + for(float i : debugScoreArray) { + debugScoreOutput += std::to_string(i) + "\n"; + } + + ctrpf::MessageBox("Category Scores", debugScoreOutput)(); + }); + menu += new ctrpf::MenuEntry("Tickflow contents", nullptr, [](ctrpf::MenuEntry*) { ctrpf::MessageBox("Map shit", Stuff::FileMapToString(config->tickflows))(); });