diff --git a/include/Megamix/Commands.hpp b/include/Megamix/Commands.hpp index 50a1ae3..778c379 100644 --- a/include/Megamix/Commands.hpp +++ b/include/Megamix/Commands.hpp @@ -8,6 +8,7 @@ namespace Megamix{ InputCheck = 0x200, VersionNumber = 0x201, LanguageCheck = 0x202, + EndlessSave = 0x205, MSBTWithNum = 0x206, DisplayCondvar = 0x300 @@ -21,6 +22,7 @@ namespace Megamix{ void versionCheck(CTickflow* self, u32 arg0, u32* args); void languageCheck(CTickflow* self, u32 arg0, u32* args); void displayCondvar(CTickflow* self, u32 arg0, u32* args); + void endlessSave(CTickflow* self, u32 arg0, u32* args); void msbtWithNum(CTickflow* self, u32 arg0, u32* args); } diff --git a/include/Megamix/Region.hpp b/include/Megamix/Region.hpp index f2fc9f3..897b4e5 100644 --- a/include/Megamix/Region.hpp +++ b/include/Megamix/Region.hpp @@ -12,7 +12,7 @@ #include "Megamix/Error.hpp" #include "Megamix/Types.hpp" -extern u8 region; +extern u8 region; //TODO: remove namespace Megamix { struct GameInterface { @@ -61,9 +61,28 @@ namespace Megamix { // some common globals / managers + typedef u16 (*GetGateScoreSignature) (CSaveData* self, GateGameIndex index, s32 file); + typedef void (*SetGateScoreSignature) (CSaveData* self, GateGameIndex index, u16 score, s32 file); + CSaveData** saveData; + GetGateScoreSignature getGateScore; + SetGateScoreSignature setGateScore; + CInputManager** inputManager; CFileManager** fileManager; + UnkStruct0054ef10** unk0054ef10; + + typedef void (*SaveDataSignature) (CSaveManager*); + + CSaveManager** saveManager; + SaveDataSignature saveGame; + + CBlackBarManager** blackbarLayout; + + // endless score saving + + typedef bool (*IsGateGameValidSignature)(GateGameIndex slot); + IsGateGameValidSignature isGateGameValid; // tickflow commands u32 tickflowCommandsHook; @@ -72,7 +91,6 @@ namespace Megamix { // MSBT printf - CBlackBarManager** blackbarLayout; typedef int (*SWPrintfSignature) (char16_t* buffer, size_t size, const char16_t* format, ...); typedef u32 (*SetTextBoxStringSignature) (Megamix::TextBox *, const char16_t *, u32); @@ -131,9 +149,12 @@ namespace Megamix { inline CSaveData* gSaveData() { return *pointers->saveData; } inline CInputManager* gInputManager() { return *pointers->inputManager; } inline CFileManager* gFileManager() { return *pointers->fileManager; } - + inline UnkStruct0054ef10* D_0054ef10() { return *pointers->unk0054ef10; } + inline CSaveManager* gSaveManager() { return *pointers->saveManager; } inline CBlackBarManager* gBlackbarLayout() { return *pointers->blackbarLayout; } + inline bool isGateGameValid(GateGameIndex slot) { return pointers->isGateGameValid(slot); } + // see cpp file for impl details of this - tldr it's not good extern int swprintf(char16_t* buffer, size_t size, const char16_t* format, ...); diff --git a/include/Megamix/Types.hpp b/include/Megamix/Types.hpp index ced1826..2ce9ebb 100644 --- a/include/Megamix/Types.hpp +++ b/include/Megamix/Types.hpp @@ -312,6 +312,58 @@ namespace Megamix { //Save File //----------------- + + enum class GateGameIndex: u8 { + Game = 0xC, + AgbVirus = 0, + NtrCoinToss = 4, + RvlSword = 8, + CtrChicken = 0xC, + + Difficulty = 0x3, + Easy = 0, + Medium = 1, + Hard = 2, + Endless = 3, + + AgbVirusEasy = AgbVirus | Easy, + AgbVirusMedium = AgbVirus | Medium, + AgbVirusHard = AgbVirus | Hard, + AgbVirusEndless = AgbVirus | Endless, + NtrCoinTossEasy = NtrCoinToss | Easy, + NtrCoinTossMedium = NtrCoinToss | Medium, + NtrCoinTossHard = NtrCoinToss | Hard, + NtrCoinTossEndless = NtrCoinToss | Endless, + RvlSwordEasy = RvlSword | Easy, + RvlSwordMedium = RvlSword | Medium, + RvlSwordHard = RvlSword | Hard, + RvlSwordEndless = RvlSword | Endless, + CtrChickenEasy = CtrChicken | Easy, + CtrChickenMedium = CtrChicken | Medium, + CtrChickenHard = CtrChicken | Hard, + CtrChickenEndless = CtrChicken | Endless, + + Invalid = 0x11, + }; + + inline GateGameIndex operator&(GateGameIndex lhs, GateGameIndex rhs) { + return (GateGameIndex)((u8)lhs & (u8)rhs); + } + + enum class GateGameRank : u8 { + Unplayed = 0, + Failed = 1, + Beaten = 2, + Invalid = 4, + }; + + // Not really sure what this is, and it's undocumented + struct UnkStruct0054ef10 { + u8 padding[0x4c]; + GateGameIndex currentGateSlot; + GateGameRank currentGateState; + }; + enum class EGameRank : u8 { Locked = 0, Unplayed = 1, @@ -371,6 +423,14 @@ namespace Megamix { u8 cSaveData_sub1[0x1c20]; struct CSaveFileData fileData[4]; u32 currentFile; + + u16 getGateScore(GateGameIndex index, s32 file); + void setGateScore(Megamix::GateGameIndex index, u16 score, s32 file); + }; + + // TODO + struct CSaveManager { + void saveGame(); // defined in Region }; //----------------- @@ -504,7 +564,7 @@ namespace Megamix { //----------------- - //File Manager + // File Manager //----------------- struct CFileManager { @@ -547,6 +607,10 @@ namespace Megamix { s32 id; }; + //----------------- + // TextBox and its derivates + //----------------- + struct TextBox { u8 parent[0xa4]; char16_t* textBuf; diff --git a/src/Megamix/Commands.cpp b/src/Megamix/Commands.cpp index 0c2a0fc..83f6d57 100644 --- a/src/Megamix/Commands.cpp +++ b/src/Megamix/Commands.cpp @@ -33,6 +33,9 @@ namespace Megamix { case LanguageCheck: languageCheck(self, arg0, args); break; + case EndlessSave: + endlessSave(self, arg0, args); + break; case MSBTWithNum: msbtWithNum(self, arg0, args); break; @@ -107,6 +110,25 @@ namespace Megamix { } } + void endlessSave(CTickflow* self, u32 arg0, u32* args) { + using enum Megamix::GateGameIndex; + + if (arg0 != 0) return; + + // alternatively, load the current slot loaded with the tickflow hook into a global, and use that instead + // that way we can avoid the UB on non-gate slots + GateGameIndex slot = Game::D_0054ef10()->currentGateSlot; + if ((slot & Difficulty) != Endless || !Game::isGateGameValid(slot)) + return; + + u32 oldScore = Game::gSaveData()->getGateScore(slot, -1); + + if (oldScore < self->condvar && self->condvar <= 0xFFFF) { + Game::gSaveData()->setGateScore(slot, self->condvar, -1); + Game::gSaveManager()->saveGame(); + } + } + void msbtWithNum(CTickflow* self, u32 arg0, u32* args) { if (arg0 != 0) return; diff --git a/src/Megamix/Region.cpp b/src/Megamix/Region.cpp index c2a7781..5b3401b 100644 --- a/src/Megamix/Region.cpp +++ b/src/Megamix/Region.cpp @@ -1,7 +1,9 @@ +#include "Megamix/Region.hpp" #include <3ds.h> #include #include "Megamix.hpp" +#include "Megamix/Types.hpp" #include @@ -46,15 +48,25 @@ namespace Megamix { .regionBHookPos= 0x11932c, // TODO: any of these could have been made incompatible by version differences - .saveData= (CSaveData**)GameInterface::UNIMPLEMENTED, + .saveData= (CSaveData**)GameInterface::UNIMPLEMENTED, + .getGateScore = (GameInterface::GetGateScoreSignature)GameInterface::UNIMPLEMENTED, + .setGateScore = (GameInterface::SetGateScoreSignature)GameInterface::UNIMPLEMENTED, + .inputManager= (CInputManager**)GameInterface::UNIMPLEMENTED, .fileManager= (CFileManager**)GameInterface::UNIMPLEMENTED, + .unk0054ef10 = (UnkStruct0054ef10**)GameInterface::UNIMPLEMENTED, + + .saveManager = (CSaveManager**)GameInterface::UNIMPLEMENTED, + .saveGame = (GameInterface::SaveDataSignature)GameInterface::UNIMPLEMENTED, + + .blackbarLayout= (CBlackBarManager**)0x526404, + + .isGateGameValid= (GameInterface::IsGateGameValidSignature)0x2569d8, .tickflowCommandsHook= 0x25e054, .tickflowCommandsCmd0= 0x25e338, .tickflowCommandsReturn= 0x262eac, - .blackbarLayout= (CBlackBarManager**)0x526404, .swprintfFunc= (GameInterface::SWPrintfSignature)THUMB_CALL_ADDR(0x100914), .setTextBoxStringFunc= (GameInterface::SetTextBoxStringSignature)0x3204f8, }; @@ -96,14 +108,24 @@ namespace Megamix { .regionBHookPos= 0x119560, .saveData= (CSaveData**)0x54d350, + .getGateScore = (GameInterface::GetGateScoreSignature)0x261a6c, + .setGateScore = (GameInterface::SetGateScoreSignature)0x2366c0, + .inputManager= (CInputManager**)0x54eed0, .fileManager= (CFileManager**)0x54eedc, + .unk0054ef10 = (UnkStruct0054ef10**)0x54ef10, + + .saveManager = (CSaveManager**)0x54ef28, + .saveGame = (GameInterface::SaveDataSignature)0x28bf14, + + .blackbarLayout= (CBlackBarManager**)0x52f3f8, + + .isGateGameValid= (GameInterface::IsGateGameValidSignature)0x255550, .tickflowCommandsHook= 0x25c3c0, .tickflowCommandsCmd0= 0x25c6c0, .tickflowCommandsReturn= 0x2613cc, - .blackbarLayout= (CBlackBarManager**)0x52f3f8, .swprintfFunc= (GameInterface::SWPrintfSignature)THUMB_CALL_ADDR(0x28a2d0), .setTextBoxStringFunc= (GameInterface::SetTextBoxStringSignature)0x31fcd8, }; @@ -139,15 +161,25 @@ namespace Megamix { .regionAHookPos= 0x28c070, .regionBHookPos= 0x119560, - .saveData= (CSaveData**)0x54d448, + .saveData= (CSaveData**)0x54d448, + .getGateScore = (GameInterface::GetGateScoreSignature)0x261a6c, + .setGateScore = (GameInterface::SetGateScoreSignature)0x2366c0, + .inputManager= (CInputManager**)0x54efc8, .fileManager= (CFileManager**)0x54efd4, + .unk0054ef10 = (UnkStruct0054ef10**)0x54f008, + + .saveManager = (CSaveManager**)0x54f020, + .saveGame = (GameInterface::SaveDataSignature)0x28bf14, + + .blackbarLayout= (CBlackBarManager**)0x52f3f8, + + .isGateGameValid= (GameInterface::IsGateGameValidSignature)0x255550, .tickflowCommandsHook= 0x25c3c0, .tickflowCommandsCmd0= 0x25c6c0, .tickflowCommandsReturn= 0x2613cc, - .blackbarLayout= (CBlackBarManager**)0x52f3f8, .swprintfFunc= (GameInterface::SWPrintfSignature)THUMB_CALL_ADDR(0x28a2d0), .setTextBoxStringFunc= (GameInterface::SetTextBoxStringSignature)0x31fcd8, }; @@ -188,15 +220,25 @@ namespace Megamix { .regionAHookPos= 0x28c048, .regionBHookPos= 0x119560, - .saveData= (CSaveData**)0x54d448, + .saveData= (CSaveData**)0x54d448, + .getGateScore = (GameInterface::GetGateScoreSignature)0x261a44, + .setGateScore = (GameInterface::SetGateScoreSignature)0x236698, + .inputManager= (CInputManager**)0x54efc8, .fileManager= (CFileManager**)0x54efd4, + .unk0054ef10 = (UnkStruct0054ef10**)0x54f008, + + .saveManager = (CSaveManager**)0x54f020, + .saveGame = (GameInterface::SaveDataSignature)0x28beec, + + .blackbarLayout= (CBlackBarManager**)0x52f3f8, + + .isGateGameValid= (GameInterface::IsGateGameValidSignature)0x255528, .tickflowCommandsHook= 0x25c398, .tickflowCommandsCmd0= 0x25c698, .tickflowCommandsReturn= 0x2613a4, - .blackbarLayout= (CBlackBarManager**)0x52f3f8, .swprintfFunc= (GameInterface::SWPrintfSignature)THUMB_CALL_ADDR(0x28a2a8), .setTextBoxStringFunc= (GameInterface::SetTextBoxStringSignature)0x31fcd8, }; @@ -274,3 +316,17 @@ extern "C" { return Megamix::pointers->swprintfFunc; } } + +// definitions from type stuff cause, lol + +void Megamix::CSaveManager::saveGame() { + pointers->saveGame(this); +} + +u16 Megamix::CSaveData::getGateScore(Megamix::GateGameIndex index, s32 file) { + return pointers->getGateScore(this, index, file); +} + +void Megamix::CSaveData::setGateScore(Megamix::GateGameIndex index, u16 score, s32 file) { + pointers->setGateScore(this, index, score, file); +} \ No newline at end of file diff --git a/src/main.cpp b/src/main.cpp index 19d6d32..cb8dc3d 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -108,7 +108,7 @@ void ctrpf::PatchProcess(ctrpf::FwkSettings&) { if (!Megamix::isJP()) { //TODO: find out how to make the tempo hooks JP-compatible Megamix::Hooks::TempoHooks(); - //TODO: find out how to make the tickflow commands hook JP-compatible + //TODO: find out how to make the tickflow commands hook (and each individual command) JP-compatible Megamix::Hooks::CommandHook(); }