diff --git a/.clangd b/.clangd index aa4ff06..98e1662 100644 --- a/.clangd +++ b/.clangd @@ -8,4 +8,4 @@ Diagnostics: Suppress: - unused-includes InlayHints: - Designators: false \ No newline at end of file + Designators: false diff --git a/Makefile b/Makefile index d6a1498..7230ebc 100644 --- a/Makefile +++ b/Makefile @@ -48,7 +48,6 @@ else else ifeq ($(shell uname -s),macOS) ifeq ($(AZAHAR),1) -# TODO: triple check with a mac user CITRA_DIR := ~/Library/Application Support/Azahar/sdmc else CITRA_DIR := ~/Library/Application Support/Citra/sdmc diff --git a/include/CTRPF.hpp b/include/CTRPF.hpp new file mode 100644 index 0000000..547c381 --- /dev/null +++ b/include/CTRPF.hpp @@ -0,0 +1,13 @@ +#ifndef CTRPF_WRAPPER_H +#define CTRPF_WRAPPER_H + +#include + +namespace CTRPF = CTRPluginFramework; + +using CTRPF::Process; +using CTRPF::OSD; + +constexpr auto Format = CTRPF::Utils::Format; + +#endif diff --git a/include/Config.hpp b/include/Config.hpp index ce159c7..dac1a1b 100644 --- a/include/Config.hpp +++ b/include/Config.hpp @@ -2,22 +2,22 @@ #define CONFIG_H #include <3ds.h> -#include +#include "CTRPF.hpp" #include #include struct Config { - typedef std::map map; + typedef std::map TickflowMap; - map tickflows; + TickflowMap tickflows; Config(); - Config(map map); - static Config* FromFile(std::string fname); + Config(Config::TickflowMap map); + static Config FromFile(std::string fname); }; -extern Config* config; +extern Config config; extern int configResult; #endif \ No newline at end of file diff --git a/include/Unicode.h b/include/Helpers/ButtonSymbols.h similarity index 100% rename from include/Unicode.h rename to include/Helpers/ButtonSymbols.h diff --git a/include/Megamix.hpp b/include/Megamix.hpp index e8dc485..b8a1c59 100644 --- a/include/Megamix.hpp +++ b/include/Megamix.hpp @@ -7,7 +7,6 @@ #define MEGAMIX_MODS_PATH "/spicerack/mods/" #define MEGAMIX_CONFIG_PATH MEGAMIX_BIN_PATH "saltwater.cfg" -#include "Saltwater.hpp" #include "Megamix/Region.hpp" #include "Megamix/Hooks.hpp" #include "Megamix/Patches.hpp" diff --git a/include/Megamix/BTKS.hpp b/include/Megamix/BTKS.hpp index 0ff9c72..ba5e07e 100644 --- a/include/Megamix/BTKS.hpp +++ b/include/Megamix/BTKS.hpp @@ -3,6 +3,7 @@ #include #include + #include "Megamix/Types.hpp" namespace Megamix { @@ -19,8 +20,8 @@ namespace Megamix { char* tickflow = nullptr; char* strings = nullptr; std::map tempos = std::map(); - u32 tickflowSize; - u32 stringSize; + u32 tickflowSize = 0; + u32 stringSize = 0; int LoadFile(std::string filename); void Unload(); diff --git a/include/Megamix/Commands.hpp b/include/Megamix/Commands.hpp index 50a1ae3..2e75ab4 100644 --- a/include/Megamix/Commands.hpp +++ b/include/Megamix/Commands.hpp @@ -13,15 +13,7 @@ namespace Megamix{ DisplayCondvar = 0x300 }; - void tickflowCommandsHookWrapper(); - - extern "C" int tickflowCommandsHook(CTickflow* self, u32 cmd_num, u32 arg0, u32* args); - - void input_cmd(CTickflow* self, u32 arg0, u32* args); - 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 msbtWithNum(CTickflow* self, u32 arg0, u32* args); + void tickflowCommands(); } #endif diff --git a/include/Megamix/Error.hpp b/include/Megamix/Error.hpp index 41ca5dc..820cb71 100644 --- a/include/Megamix/Error.hpp +++ b/include/Megamix/Error.hpp @@ -2,13 +2,7 @@ #define MEGAMIX_ERROR_HPP #include -#include - -#define CALL_STACK_SIZE 5 - -using CTRPluginFramework::Process; - -struct Void{}; +#include "CTRPF.hpp" namespace Megamix { std::string ErrorMessage(int code); @@ -53,6 +47,7 @@ namespace Megamix { u32 statusRegisterA; // IFSR, DFSR, FPEXC u32 statusRegisterB; // FAR, FPINST + static constexpr int CALL_STACK_SIZE = 5; u32 callStack[CALL_STACK_SIZE]; }; diff --git a/include/Megamix/Hooks.hpp b/include/Megamix/Hooks.hpp index e8c8c62..9a40ced 100644 --- a/include/Megamix/Hooks.hpp +++ b/include/Megamix/Hooks.hpp @@ -1,16 +1,25 @@ #ifndef MEGAMIX_HOOKS_H #define MEGAMIX_HOOKS_H -#include "types.h" +#include <3ds/types.h> namespace Megamix::Hooks { - void TickflowHooks(); - void TempoHooks(); - void RegionHooks(); - void CommandHook(); - void DisableAllHooks(); + void initTickflowHooks(); + void initTempoHooks(); + void initRegionHooks(); + void initCommandHooks(); + + void disableTickflowHooks(); + void disableTempoHooks(); + void disableRegionHooks(); + void disableCommandHooks(); + inline void disableAllHooks() { + disableTickflowHooks(); + disableTempoHooks(); + disableRegionHooks(); + disableCommandHooks(); + } - template T StubbedFunction(); template void StubFunction(u32 address); } diff --git a/include/Megamix/Region.hpp b/include/Megamix/Region.hpp index f2fc9f3..faa7fbc 100644 --- a/include/Megamix/Region.hpp +++ b/include/Megamix/Region.hpp @@ -5,15 +5,11 @@ #include #include -#include "types.h" - -#include +#include <3ds/types.h> #include "Megamix/Error.hpp" #include "Megamix/Types.hpp" -extern u8 region; - namespace Megamix { struct GameInterface { u32 gameCode; @@ -101,7 +97,7 @@ namespace Megamix { // TODO: if we ever add extra game revisions, add check for those - std::expected initGameInterface(u32 gameCode); + std::expected initGameInterface(u32 gameCode); inline bool isJP() { return pointers->gameCode == 0x155a00; } @@ -117,6 +113,27 @@ namespace Megamix { namespace Game { + enum class GameVariant: u8 { + JPRev0, + USRev0, + EURev0, + KRRev0, + UNK = (u8)-1, + }; + + inline GameVariant getRegion() { + if (isJP()) + return GameVariant::JPRev0; + else if (isUS()) + return GameVariant::USRev0; + else if (isEU()) + return GameVariant::EURev0; + else if (isKR()) + return GameVariant::KRRev0; + else + return GameVariant::UNK; + } + inline u32 _textEnd () { return pointers->textEnd; } inline u32 _rodataEnd() { return pointers->rodataEnd; } inline u32 _dataEnd() { return pointers->dataEnd; } diff --git a/include/Megamix/Types.hpp b/include/Megamix/Types.hpp index ced1826..5fec1f3 100644 --- a/include/Megamix/Types.hpp +++ b/include/Megamix/Types.hpp @@ -3,7 +3,10 @@ #include -#include "types.h" +#include <3ds/types.h> +#include "CTRPF.hpp" + +// TODO: split this file - potentially move into Game? namespace Megamix { struct GameDef { @@ -46,7 +49,7 @@ namespace Megamix { struct TempoTable { u32 id1; u32 id2; - u8 unk8; + u8 isStreamed; u16 unkA; Tempo* pos; }; @@ -139,7 +142,7 @@ namespace Megamix { RvlManjuL = 0x37, RvlMuscleL = 0x38, RvlRapL = 0x39, - RvlRecieveL = 0x3a, + RvlReceiveL = 0x3a, RvlRobotL = 0x3b, RvlRocketL = 0x3c, RvlRotationL = 0x3d, @@ -216,12 +219,12 @@ namespace Megamix { u32 lowIndex; MuseumRow(std::array gameIndices, const char* titleId, u32 highIndex, u32 lowIndex) { - if (gameIndices[0] == 0x101) this->columnCount = 0; - else if (gameIndices[1] == 0x101) this->columnCount = 1; - else if (gameIndices[2] == 0x101) this->columnCount = 2; - else if (gameIndices[3] == 0x101) this->columnCount = 3; - else if (gameIndices[4] == 0x101) this->columnCount = 4; - else this->columnCount = 5; + if (gameIndices[0] == 0x101) columnCount = 0; + else if (gameIndices[1] == 0x101) columnCount = 1; + else if (gameIndices[2] == 0x101) columnCount = 2; // broken in vanilla + else if (gameIndices[3] == 0x101) columnCount = 3; + else if (gameIndices[4] == 0x101) columnCount = 4; + else columnCount = 5; this->gameIndices = gameIndices; this->titleId = titleId; @@ -237,24 +240,24 @@ namespace Megamix { u8 a; Color8() { - this->r = 0; - this->g = 0; - this->b = 0; - this->a = 0; + r = 0; + g = 0; + b = 0; + a = 0; } Color8(u8 r, u8 g, u8 b, u8 a) { - this->r = r; - this->g = g; - this->b = b; - this->a = a; + r = r; + g = g; + b = b; + a = a; } Color8(u32 color) { - this->r = color >> 24; - this->g = color >> 16; - this->b = color >> 8; - this->a = color; + r = color >> 24; + g = color >> 16; + b = color >> 8; + a = color; } }; @@ -265,8 +268,8 @@ namespace Megamix { Color8 unk3; MuseumRowColor(Color8 background, Color8 edgeFade) { - this->unk1 = {0x00, 0x00, 0x00, 0x00}; - this->unk3 = {0xFF, 0xFF, 0xFF, 0x6E}; + unk1 = {0x00, 0x00, 0x00, 0x00}; + unk3 = {0xFF, 0xFF, 0xFF, 0x6E}; this->background = background; this->edgeFade = edgeFade; @@ -523,8 +526,8 @@ namespace Megamix { u8 field12_0x2a; u8 field13_0x2b; u32 field14_0x2c; - u16 locale[9]; - u16 sublocale[9]; + char16_t locale[9]; + char16_t sublocale[9]; }; struct FileInfo { diff --git a/include/Saltwater.hpp b/include/Saltwater.hpp index 2295a5f..e2ccf47 100644 --- a/include/Saltwater.hpp +++ b/include/Saltwater.hpp @@ -1,7 +1,9 @@ #ifndef SALTWATER_H #define SALTWATER_H +#include "external/plgldr.h" #include <3ds.h> +#include // Saltwater version @@ -19,7 +21,6 @@ #ifdef RELEASE - #define VERSION "v" STRC(VERSION_MAJOR) "." STRC(VERSION_MINOR) "." STRC(VERSION_PATCH) #else @@ -29,17 +30,15 @@ #endif -// Params struct -extern "C" { - struct SaltwaterParams { - u16 barista; - bool rhmpatch; - bool plgldr; - bool mod_loaded_msg; - bool extra_rows; - u32 null[30]; - }; -} +struct SaltwaterParams { + u16 barista; + bool rhmpatch; + bool plgldr; + bool mod_loaded_msg; + bool extra_rows; + u32 null[30]; +}; +static_assert(sizeof(SaltwaterParams) == sizeof(PluginLoadParameters::config)); extern SaltwaterParams params; diff --git a/include/Stuff.hpp b/include/Stuff.hpp index a17e580..058f8c1 100644 --- a/include/Stuff.hpp +++ b/include/Stuff.hpp @@ -1,25 +1,30 @@ -#include +#include "CTRPF.hpp" + +#include "Config.hpp" #include "Megamix.hpp" -using CTRPluginFramework::Utils; using Megamix::BTKS; +#define CHAR4_LE(char1, char2, char3, char4) ( \ + ((u32)(char4) << 24) | ((u32)(char3) << 16) | \ + ((u32)(char2) << 8) | ((u32)(char1) << 0) \ +) + namespace Stuff { - static inline std::string FileMapToString(std::map in) { + static inline std::string FileMapToString(Config::TickflowMap in) { std::string out = "{"; for (auto item = in.cbegin(); item != in.cend(); ++item) { - out += Utils::Format("%X: %s, ", item->first, item->second.c_str()); + out += Format("%X: %s, ", item->first, item->second.c_str()); } out += "}"; return out; } static inline std::string TempoMapToString(std::map in) { - std::string out = "{"; for (auto item = in.cbegin(); item != in.cend(); ++item) { - out += Utils::Format("%X: Tempo of size %d, ", item->first, ((int*)item->second->pos)[-1]); + out += Format("%X: Tempo of size %d, ", item->first, ((int*)item->second->pos)[-1]); } out += "}"; return out; diff --git a/include/csvc.h b/include/csvc.h index 1acbdc8..36f341c 100644 --- a/include/csvc.h +++ b/include/csvc.h @@ -20,7 +20,7 @@ extern "C" { #endif -#include +#include <3ds/types.h> #define PA_RWX(add) (add == 0 ? 0 : (add < 0x30000000 ? (u32)((add) | (1u << 31)) : add)) #define PA_FROM_VA(addr) PA_RWX(svcConvertVAToPA((void *)addr, false)) diff --git a/include/types.h b/include/types.h index b6aa614..0a5e341 100644 --- a/include/types.h +++ b/include/types.h @@ -6,6 +6,8 @@ #ifndef TYPES_H #define TYPES_H +// ctrpf REQUIRES having this file, don't @ me + #include #include #include diff --git a/src/Config.cpp b/src/Config.cpp index e92f641..4c2e6b5 100644 --- a/src/Config.cpp +++ b/src/Config.cpp @@ -1,8 +1,9 @@ #include <3ds.h> -#include +#include "CTRPF.hpp" #include "Megamix.hpp" #include "Config.hpp" +#include "Stuff.hpp" #include @@ -10,39 +11,44 @@ using CTRPluginFramework::File; int configResult; +Config config; + Config::Config() { tickflows = {}; } -Config::Config(map tf) { +Config::Config(TickflowMap tf) { tickflows = tf; } -Config* Config::FromFile(std::string fname) { +Config Config::FromFile(std::string fname) { File file(fname, File::Mode::READ); - char* contents = new char[4]; - configResult = file.Read(contents, 4); - if (configResult || file.GetSize() < 4) return new Config(); - if (configResult == 0 && strcmp(contents, "SCF\2")) { - map tfmap = {}; + u32 magic; + configResult = file.Read(&magic, 4); + if (configResult || file.GetSize() < 4) return Config(); + + if (configResult == 0 && magic == CHAR4_LE('S', 'C', 'F', '\2')) { + TickflowMap tfmap = {}; + while (true) { - u16* index = new u16; - configResult = file.Read(index, 2); - if (configResult) return new Config(); - if (*index == 0xC000) break; + u16 index; + configResult = file.Read(&index, 2); + if (configResult) return Config(); + if (index == 0xC000) break; - u16* strlen = new u16; - configResult = file.Read(strlen, 2); - if (configResult) return new Config(); + u16 strlen; + configResult = file.Read(&strlen, 2); + if (configResult) return Config(); - char* str = new char[*strlen + 1]; - configResult = file.Read(str, *strlen); - if (configResult) return new Config(); - str[*strlen] = 0; + std::string str; + str.resize(strlen); + configResult = file.Read(str.data(), strlen); + if (configResult) return Config(); + str[strlen] = 0; - tfmap[*index] = std::string(str); + tfmap[index] = str; } - return new Config(tfmap); + return Config(tfmap); } - return new Config(); + return Config(); } \ No newline at end of file diff --git a/src/Megamix/BTKS.cpp b/src/Megamix/BTKS.cpp index 4168f94..65c84c5 100644 --- a/src/Megamix/BTKS.cpp +++ b/src/Megamix/BTKS.cpp @@ -1,8 +1,10 @@ #include <3ds.h> -#include +#include "CTRPF.hpp" #include #include "Megamix.hpp" +#include "Saltwater.hpp" +#include "Stuff.hpp" using CTRPluginFramework::File; using CTRPluginFramework::OSD; @@ -10,86 +12,83 @@ using CTRPluginFramework::OSD; namespace Megamix { BTKS btks; + struct SectionHeader { + u32 magic; + u32 size; + }; + + struct BTKSHeader : SectionHeader { + u32 revision; + u32 headerSize; + u32 numSections; + u32 tickflowVariant; + }; + int BTKS::LoadFile(const std::string filename) { - this->Unload(); + Unload(); if (params.mod_loaded_msg) OSD::Notify("Loading BTKS file..."); File file(MEGAMIX_MODS_PATH + filename + ".btk", File::Mode::READ); u32 result; - char* magicBuf = new char[8]; - magicBuf[4] = 0; // terminator - u32* intBuf = new u32[1]; - //Header - result = file.Read(magicBuf, 4); // Magic + // Read header + // ------------------- + + BTKSHeader header; + result = file.Read(&header, sizeof(header)); if (result) return result; - if (strcmp(magicBuf,"BTKS")) { + + // sanity checks on the header + + if (header.magic != CHAR4_LE('B', 'T', 'K', 'S')) { return -6; // Not a BTKS file } - result = file.Read(intBuf, 4); // Filesize - not that useful rn - if (result) return result; - int filesize = *intBuf; - - result = file.Read(intBuf, 4); // Format version - supported: rev2 - if (result) return result; - int version = *intBuf; - if (version != 2) + // rev2 is the only supported revision + if (header.revision != 2) return -7; // Unsupported version - // Since the only supported version is rev2 we can expect it to always be that - - result = file.Read(intBuf, 4); // Header size - expected: 0x18 - if (result) return result; - int headerEnd = *intBuf; - - result = file.Read(intBuf, 4); // Number of sections - expected: 3 or 4 - if (result) return result; - int numSections = *intBuf; - - result = file.Read(intBuf, 4); // Tickflow format - supported: 0 (US/EU/KR) - if (result) return result; - if (!((isJP() && *intBuf == 0) || (isJP() && *intBuf == 1))) + if (isJP() ? (header.tickflowVariant != 1) : (header.tickflowVariant != 0)) return -13; // Unsupported Tickflow format - // Seek to end of header - result = file.Seek(headerEnd, File::SeekPos::SET); + // Seek to end of header (in case there's some non-standard stuff idfk) + result = file.Seek(header.headerSize, File::SeekPos::SET); if (result) return result; - // Section code! This is where things get more complicated + // Read sections + // ------------------- Pointer* pointers = nullptr; int numPointers = 0; - for (int i = 0; i < numSections; i++) { - result = file.Read(magicBuf, 4); // Section magic + for (u32 i = 0; i < header.numSections; i++) { + SectionHeader secHeader; + result = file.Read(&secHeader, sizeof(secHeader)); // Section magic if (result) return result; + // TODO: separate each individual section into a function for readability + // Tickflow section - if (!strcmp(magicBuf, "FLOW")) { - result = file.Read(intBuf, 4); // Section size - if (result) return result; - tickflowSize = *intBuf - 0xC; - tickflow = new char[tickflowSize]; - result = file.Read(intBuf, 4); // Position of start sub + if (secHeader.magic == CHAR4_LE('F', 'L', 'O', 'W')) { + if (tickflowSize != 0) { + // TODO: error - repeated FLOW section + } + tickflowSize = secHeader.size - 0xC; + result = file.Read(&start, 4); // Position of start sub if (result) return result; - start = *intBuf; - if (tickflowSize > 0x100000) + if (tickflowSize > 0x100000) // 1MiB return -12; //file too big - tickflow = (char*) malloc(tickflowSize); + tickflow = new char[tickflowSize]; result = file.Read(tickflow, tickflowSize); // Tickflow data if (result) return result; } // Pointer section - else if (!strcmp(magicBuf, "PTRO")) { - result = file.Read(intBuf, 4); // Section size - if (result) return result; - int extraBytes = *intBuf - 0x08; - result = file.Read(intBuf, 4); // Number of pointers + else if (secHeader.magic == CHAR4_LE('P', 'T', 'R', 'O')) { + int extraBytes = secHeader.size - 0x08; + result = file.Read(&numPointers, 4); // Number of pointers if (result) return result; - numPointers = *intBuf; extraBytes -= 5 * numPointers; pointers = new Pointer[numPointers]; for (int i = 0; i < numPointers; i++) { @@ -98,49 +97,50 @@ namespace Megamix { } file.Read(nullptr, extraBytes); // Extra bytes that may be there for whatever reason } - else if (!strcmp(magicBuf, "STRD")) { - result = file.Read(intBuf, 4); // Section size - if (result) return result; - stringSize = *intBuf - 0x08; - if (stringSize > 0x80000) + else if (secHeader.magic == CHAR4_LE('S', 'T', 'R', 'D')) { + stringSize = secHeader.size - 0x08; + if (stringSize > 0x80000) // 500KiB return -12; //file too big strings = (char*) malloc(stringSize); result = file.Read(strings, stringSize); } - else if (!strcmp(magicBuf, "TMPO")) { - result = file.Read(intBuf, 4); // Section size + else if (secHeader.magic == CHAR4_LE('T', 'M', 'P', 'O')) { + int extraBytes = secHeader.size - 0xC; + u32 tempoAmount; + result = file.Read(&tempoAmount, 4); if (result) return result; - int extraBytes = *intBuf - 0xC; - result = file.Read(intBuf, 4); - if (result) return result; - u32 tempoAmount = *intBuf; + + // TODO: restructure this entire code for (u32 i = 0; i < tempoAmount; i++) { - result = file.Read(intBuf, 4); + u32 id; + result = file.Read(&id, 4); if (result) return result; - u32 id = *intBuf; - result = file.Read(intBuf, 4); + + u32 dataSize; + result = file.Read(&dataSize, 4); if (result) return result; - u32 dataSize = *intBuf; - result = file.Read(intBuf, 4); + + // TODO: this stinks + u32 isStreamed_; + result = file.Read(&isStreamed_, 4); if (result) return result; - bool is_streamed = *(bool*)intBuf; + bool isStreamed = isStreamed_ != 0; + Tempo* data = new Tempo[dataSize]; for (u32 i = 0; i < dataSize; i++) { - float* floatBuf = new float; - result = file.Read(floatBuf, 4); + result = file.Read(&data[i].beats, 4); + if (result) return result; + result = file.Read(&data[i].time, 4); if (result) return result; - data[i].beats = *floatBuf; - result = file.Read(intBuf, 4); + result = file.Read(&data[i].flag8, 2); if (result) return result; - data[i].time = *intBuf; - result = file.Read(intBuf, 4); + result = file.Read(&data[i].flagA, 2); if (result) return result; - data[i].flag8 = ((u16*)intBuf)[0]; - data[i].flagA = ((u16*)intBuf)[1]; } - if (is_streamed && data->flag8 != 1) { + // recalculate tempo data for streamed tempos; this is done on game load for vanilla tempos + if (isStreamed && data->flag8 != 1) { u32 time = data->time; float beats = data->beats; u32 time_added = time + 2000; @@ -151,8 +151,9 @@ namespace Megamix { TempoTable* tempo = new TempoTable; tempo->id1 = tempo->id2 = id; - tempo->unk8 = (u8)is_streamed; - tempo->unkA = 0x7D00; //seems to always be this? + tempo->isStreamed = (u8)isStreamed; + tempo->unkA = 32000; // sample rate? + tempo->pos = data; tempos[id] = tempo; @@ -167,14 +168,17 @@ namespace Megamix { if (tickflow == nullptr || strings == nullptr) { return -11; //missing required section } + + // Handle relocations + // ------------------- for (int i = 0; i < numPointers; i++) { Pointer pointer = pointers[i]; - if (pointer.pointerType == 0) - *(u32*)((u32)tickflow + pointer.pointerPos) += (u32)(strings); - else if (pointer.pointerType == 1) - *(u32*)((u32)tickflow + pointer.pointerPos) += (u32)(tickflow); - else + if (pointer.pointerType == 0) { + *(u32*)&tickflow[pointer.pointerPos] += (u32)(strings); + } else if (pointer.pointerType == 1) { + *(u32*)&tickflow[pointer.pointerPos] += (u32)(tickflow); + } else return -10; //unknown pointer type } start = (u32)tickflow + start; @@ -186,12 +190,12 @@ namespace Megamix { return 0; } + // TODO: more descriptive name void BTKS::Unload() { loaded = false; delete[] tickflow; delete[] strings; - //this may not be required? but just in case for (auto tempo: tempos) { delete[] tempo.second->pos; delete tempo.second; diff --git a/src/Megamix/Commands.cpp b/src/Megamix/Commands.cpp index 0c2a0fc..a1cea27 100644 --- a/src/Megamix/Commands.cpp +++ b/src/Megamix/Commands.cpp @@ -1,23 +1,28 @@ #include <3ds.h> -#include +#include "CTRPF.hpp" -#include "Megamix.hpp" +#include -using CTRPluginFramework::OSD; -using CTRPluginFramework::Utils; +#include "Megamix.hpp" +#include "Saltwater.hpp" namespace Megamix { + void input_cmd(CTickflow* self, u32 arg0, u32* args); + 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 msbtWithNum(CTickflow* self, u32 arg0, u32* args); - void tickflowCommandsHookWrapper() { + void tickflowCommands() { asm( "mov r0, r6\n" - "bl tickflowCommandsHook\n" + "bl tickflowCommandsHookImpl\n" "bx r0\n" ); } - extern "C" __attribute__((used)) - int tickflowCommandsHook(CTickflow* self, u32 cmd_num, u32 arg0, u32* args){ + extern "C" __attribute__((used)) + int tickflowCommandsHookImpl(CTickflow* self, u32 cmd_num, u32 arg0, u32* args){ switch(cmd_num){ // Necessary, as our hook overrides case 0. case 0: @@ -83,23 +88,22 @@ namespace Megamix { if(saveLanguage == 1){ self->condvar = 0; } else { - wchar_t sublocale[5]; - utf16_to_utf32((u32*)sublocale, Game::gFileManager()->sublocale, 4); - sublocale[4] = '\0'; - std::wstring localews(sublocale); - if(localews.find(L"JP") != (unsigned int)-1){ + std::u16string sublocale(Game::gFileManager()->sublocale); + + sublocale = sublocale.substr(2, 2); + if (sublocale == u"JP") { self->condvar = 0; - } else if (localews.find(L"EN") != (unsigned int)-1){ + } else if (sublocale == u"EN") { self->condvar = 1; - } else if (localews.find(L"FR") != (unsigned int)-1) { + } else if (sublocale == u"FR") { self->condvar = 2; - } else if (localews.find(L"GE") != (unsigned int)-1) { + } else if (sublocale == u"GE") { self->condvar = 3; - } else if (localews.find(L"IT") != (unsigned int)-1) { + } else if (sublocale == u"IT") { self->condvar = 4; - } else if (localews.find(L"SP") != (unsigned int)-1) { + } else if (sublocale == u"SP") { self->condvar = 5; - } else if (localews.find(L"KR") != (unsigned int)-1) { + } else if (sublocale == u"KR") { self->condvar = 6; } else { self->condvar = -1; @@ -122,11 +126,11 @@ namespace Megamix { void displayCondvar(CTickflow* self, u32 arg0, u32* args) { // Keeping that just in case // Screen bottomScreen = OSD::GetBottomScreen(); - // bottomScreen.Draw("Condvar:"+Utils::Format("0x%08x",self->condvar), 0, 0, Color::White, Color::Black); + // bottomScreen.Draw("Condvar:"+Format("0x%08x",self->condvar), 0, 0, Color::White, Color::Black); if(arg0 == 0) { - OSD::Notify("Condvar:"+Utils::Format("0x%08x",self->condvar)); + OSD::Notify("Condvar:"+Format("0x%08x",self->condvar)); } else if (arg0 == 1){ - OSD::Notify("Condvar:"+Utils::Format("%08d",self->condvar)); + OSD::Notify("Condvar:"+Format("%08d",self->condvar)); } } diff --git a/src/Megamix/Error.cpp b/src/Megamix/Error.cpp index c1eba06..f55193d 100644 --- a/src/Megamix/Error.cpp +++ b/src/Megamix/Error.cpp @@ -1,28 +1,23 @@ +#include "Megamix/Error.hpp" #include <3ds.h> -#include +#include "CTRPF.hpp" #include #include "Megamix.hpp" +#include "Saltwater.hpp" -using CTRPluginFramework::Process; -using CTRPluginFramework::Utils; -using CTRPluginFramework::Screen; -using CTRPluginFramework::Color; -using CTRPluginFramework::Controller; -using CTRPluginFramework::Key; -using CTRPluginFramework::OSD; -using CTRPluginFramework::File; -using CTRPluginFramework::Directory; +using CTRPF::Directory; +using CTRPF::File; +using CTRPF::Controller; +using CTRPF::Key; extern const u32 _TEXT_END; extern const u32 _start; -//TODO: add enum -//TODO: exceptions? +//TODO: switch error system to something that does not suck ass namespace Megamix { - u8 errorImg[] = {}; - + // TODO: move all the strings to headers for translation reasons std::string ErrorMessage(int code) { switch (code) { // CTRPF errors @@ -58,7 +53,7 @@ namespace Megamix { return "Unsupported Tickflow format"; default: - return Utils::Format("Unknown error code %08x", code); + return Format("Unknown error code %08x", code); } } @@ -95,33 +90,45 @@ namespace Megamix { u32 fsr_status = (info->fsr & 0xf); switch (info->type) { case ERRF_EXCEPTION_PREFETCH_ABORT: - return Utils::Format("0-%02d-%s-%08X", fsr_status, MemSection(regs->pc), regs->pc); + return Format("0-%02d-%s-%08X", fsr_status, MemSection(regs->pc), regs->pc); case ERRF_EXCEPTION_DATA_ABORT: fsr_status += ((info->fsr >> 10) & 1) * 0x10; - return Utils::Format("1-%02d-%s-%07X-%08X", fsr_status, MemSection(info->far), regs->pc, info->far); + return Format("1-%02d-%s-%07X-%08X", fsr_status, MemSection(info->far), regs->pc, info->far); case ERRF_EXCEPTION_VFP: - return Utils::Format("2-%07X", regs->pc); + return Format("2-%07X", regs->pc); case ERRF_EXCEPTION_UNDEFINED: - return Utils::Format("3-%07X", regs->pc); + return Format("3-%07X", regs->pc); default: return "INVALID"; } } - static bool render = true; - static bool dumped = false; - static bool full_info = false; - static bool faded = false; - - static std::string dump_location = ""; // also serves as error message if dump_error is true - static CrashInfo crash; - static bool dump_error = false; + // this has to be static because GetCrashData runs every frame for some reason + static struct { + bool render; + bool dumped; + bool full_info; + bool faded; + + std::string dump_location; + CrashInfo crash; + bool dump_error; + } s_crash_data = { + .render = true, + .dumped = false, + .full_info = false, + .faded = false, + + .dump_location = "", + .crash = {}, + .dump_error = false, + }; namespace ErrorScreen { static CrashInfo GetCrashData(ERRF_ExceptionInfo* info, CpuRegisters* regs) { CrashInfo crash; crash.info.type = CrashType::Extended; - crash.info.region = region; + crash.info.region = (u8)Game::getRegion(); crash.info.excType = info->type; #ifdef RELEASE @@ -163,7 +170,7 @@ namespace Megamix { // TODO: get call stack u32 stack_offset = 0; - for (int i = 0; i < CALL_STACK_SIZE; i++) { + for (int i = 0; i < ShortCrashInfo::CALL_STACK_SIZE; i++) { while ((u32)stack >= 0x06000000 || (u32)(stack + stack_offset) < 0x01000000) { u32 val = *(u32*)(regs->sp + stack_offset); if ((val >= 0x0010000 && val < Game::_textEnd() || (val >= (u32)_start && val < _TEXT_END))) { @@ -178,28 +185,30 @@ namespace Megamix { return crash; } + // TODO: move all the strings to headers for translation reasons static void InfoScreen(ERRF_ExceptionInfo* info, CpuRegisters* regs) { - Screen screen = OSD::GetTopScreen(); - if (!faded) { + CTRPF::Screen screen = OSD::GetTopScreen(); + if (!s_crash_data.faded) { screen.Fade(0.3); - faded = true; + s_crash_data.faded = true; } - screen.DrawRect(16, 16, 368, 208, Color(0, 0, 0)); + screen.DrawRect(16, 16, 368, 208, CTRPF::Color(0, 0, 0)); u32 posY = 20; - posY = screen.Draw("Oh, no!", 20, posY, Color(255, 0, 0)); - posY = screen.Draw(Utils::Format("Error %s", CrashCode(info, regs).c_str()), 20, posY, Color(255, 127, 127)); + posY = screen.Draw("Oh, no!", 20, posY, CTRPF::Color(255, 0, 0)); + posY = screen.Draw(Format("Error %s", CrashCode(info, regs).c_str()), 20, posY, CTRPF::Color(255, 127, 127)); posY += 10; posY = screen.Draw("Something went wrong!", 20, posY); posY = screen.Draw("But don't panic, report the error to the SpiceRack", 20, posY); posY = screen.Draw("Discord server (discord.gg/xAKFPaERRG)", 20, posY); posY += 10; - if (dump_location != "") { - if (dump_error) - posY = screen.Draw(std::string("Error while saving dump: ").append(dump_location), 20, posY); - else - posY = screen.Draw(std::string("Crash dump saved to ").append(dump_location), 20, posY); + if (s_crash_data.dump_location.empty()) { + if (s_crash_data.dump_error) { + posY = screen.Draw(std::string("Error while saving dump: ").append(s_crash_data.dump_location), 20, posY); + } else { + posY = screen.Draw(std::string("Crash dump saved to ").append(s_crash_data.dump_location), 20, posY); + } posY += 10; } else { posY = screen.Draw("> Press A to dump crash (WIP)", 20, posY); @@ -211,32 +220,32 @@ namespace Megamix { } static void DevScreen(ERRF_ExceptionInfo* info, CpuRegisters* regs) { - Screen screen = OSD::GetTopScreen(); - screen.DrawRect(16, 16, 368, 208, Color(0, 0, 0)); + CTRPF::Screen screen = OSD::GetTopScreen(); + screen.DrawRect(16, 16, 368, 208, CTRPF::Color(0, 0, 0)); u32 posY = 20; - posY = screen.Draw(Utils::Format("Debug info (%s)", Game::_regionName()), 20, posY); - posY = screen.Draw(Utils::Format("@ %08x -> %08x (@ PC -> LR)", regs->pc, regs->lr), 20, posY); - posY = screen.Draw(Utils::Format("Exception type %d", info->type), 20, posY); + posY = screen.Draw(Format("Debug info (%s)", Game::_regionName()), 20, posY); + posY = screen.Draw(Format("@ %08x -> %08x (@ PC -> LR)", regs->pc, regs->lr), 20, posY); + posY = screen.Draw(Format("Exception type %d", info->type), 20, posY); posY += 10; posY = screen.Draw("Call stack:", 20, posY); - for (int i = 0; i < CALL_STACK_SIZE; i++) { - posY = screen.Draw(Utils::Format(" - %08x", crash.info.callStack[i]), 20, posY); + for (int i = 0; i < ShortCrashInfo::CALL_STACK_SIZE; i++) { + posY = screen.Draw(Format(" - %08x", s_crash_data.crash.info.callStack[i]), 20, posY); } posY += 10; - posY = screen.Draw(Utils::Format("r0 = %08x r1 = %08x", crash.registers[0], crash.registers[1]), 20, posY); - posY = screen.Draw(Utils::Format("r2 = %08x r3 = %08x", crash.registers[2], crash.registers[3]), 20, posY); - - if (dump_location != "") { - if (dump_error) - posY = screen.Draw(std::string("Error while saving dump: ").append(dump_location), 20, posY); + posY = screen.Draw(Format("r0 = %08x r1 = %08x", s_crash_data.crash.registers[0], s_crash_data.crash.registers[1]), 20, posY); + posY = screen.Draw(Format("r2 = %08x r3 = %08x", s_crash_data.crash.registers[2], s_crash_data.crash.registers[3]), 20, posY); + + if (s_crash_data.dump_location != "") { + if (s_crash_data.dump_error) + posY = screen.Draw(std::string("Error while saving dump: ").append(s_crash_data.dump_location), 20, posY); else - posY = screen.Draw(std::string("Crash dump saved to ").append(dump_location), 20, posY); + posY = screen.Draw(std::string("Crash dump saved to ").append(s_crash_data.dump_location), 20, posY); posY += 10; } else { posY = screen.Draw("> Press A to dump crash", 20, posY); @@ -260,7 +269,7 @@ namespace Megamix { int num = 0; std::string path; for (;; num++) { - path = Utils::Format(MEGAMIX_CRASH_PATH "swcrash_%05d.swd", num); + path = Format(MEGAMIX_CRASH_PATH "swcrash_%05d.swd", num); if (!File::Exists(path)) break; } @@ -274,26 +283,26 @@ namespace Megamix { res = file.Write("SELCRAH\0", 8); if (res) return res; - res = file.Write(&crash, sizeof(crash)); + res = file.Write(&s_crash_data.crash, sizeof(s_crash_data.crash)); if (res) return res; res = file.Close(); if (res) return res; - dump_location = path; + s_crash_data.dump_location = path; return 0; } Process::ExceptionCallbackState CrashHandler(ERRF_ExceptionInfo* info, CpuRegisters* regs) { - if (!dumped) { - crash = ErrorScreen::GetCrashData(info, regs); - dumped = true; + if (!s_crash_data.dumped) { + s_crash_data.crash = ErrorScreen::GetCrashData(info, regs); + s_crash_data.dumped = true; } - if (render) { - render = false; - if (full_info) { + if (s_crash_data.render) { + s_crash_data.render = false; + if (s_crash_data.full_info) { ErrorScreen::DevScreen(info, regs); } else { ErrorScreen::InfoScreen(info, regs); @@ -301,21 +310,23 @@ namespace Megamix { } Controller::Update(); + if (Controller::IsKeyPressed(Key::B)) { return Process::ExceptionCallbackState::EXCB_RETURN_HOME; - } else { - if (Controller::IsKeyPressed(Key::A) && dump_location == "") { - int result = SaveCrashDump(); - if (result != 0) { - dump_error = true; - dump_location = ErrorMessage(result); - } - render = true; - } else if (Controller::IsKeyPressed(Key::Y)) { - render = true; - full_info = !full_info; + } + + if (Controller::IsKeyPressed(Key::A) && s_crash_data.dump_location == "") { + int result = SaveCrashDump(); + if (result != 0) { + s_crash_data.dump_error = true; + s_crash_data.dump_location = ErrorMessage(result); } - return Process::ExceptionCallbackState::EXCB_LOOP; + s_crash_data.render = true; + } else if (Controller::IsKeyPressed(Key::Y)) { + s_crash_data.render = true; + s_crash_data.full_info = !s_crash_data.full_info; } + return Process::ExceptionCallbackState::EXCB_LOOP; + } } diff --git a/src/Megamix/Hooks.cpp b/src/Megamix/Hooks.cpp index fb9e78b..0a215b8 100644 --- a/src/Megamix/Hooks.cpp +++ b/src/Megamix/Hooks.cpp @@ -1,5 +1,5 @@ #include <3ds.h> -#include +#include "CTRPF.hpp" #include "external/rt.h" @@ -28,36 +28,36 @@ namespace Megamix::Hooks { RT_HOOK tickflowCommandsHook; void* getTickflowOffset(int index) { - if (config->tickflows.contains(index)) { - int result = Megamix::btks.LoadFile(config->tickflows[index]); + if (config.tickflows.contains(index)) { + int result = Megamix::btks.LoadFile(config.tickflows[index]); if (!result) { return (void*)(Megamix::btks.start); } else { - OSD::Notify(CTRPluginFramework::Utils::Format("Error: %s", Megamix::ErrorMessage(result).c_str())); + OSD::Notify(Format("Error: %s", Megamix::ErrorMessage(result).c_str())); } } return Game::gGameTable()[index].tfStart; // og code } void* getGateTickflowOffset(int index) { - if (config->tickflows.contains(index + 0x100)) { - int result = Megamix::btks.LoadFile(config->tickflows[index + 0x100]); + if (config.tickflows.contains(index + 0x100)) { + int result = Megamix::btks.LoadFile(config.tickflows[index + 0x100]); if (!result) { return (void*)(Megamix::btks.start); } else { - OSD::Notify(CTRPluginFramework::Utils::Format("Error: %s", Megamix::ErrorMessage(result).c_str())); + OSD::Notify(Format("Error: %s", Megamix::ErrorMessage(result).c_str())); } } return Game::gGateTable()[index].tfStart; // og code } void* getGatePracticeTickflowOffset(int index) { - if (config->tickflows.contains((index >> 2) + 0x110)) { - int result = Megamix::btks.LoadFile(config->tickflows[(index >> 2) + 0x110]); + if (config.tickflows.contains((index >> 2) + 0x110)) { + int result = Megamix::btks.LoadFile(config.tickflows[(index >> 2) + 0x110]); if (!result) { return (void*)(Megamix::btks.start); } else { - OSD::Notify(CTRPluginFramework::Utils::Format("Error: %s", Megamix::ErrorMessage(result).c_str())); + OSD::Notify(Format("Error: %s", Megamix::ErrorMessage(result).c_str())); } } return Game::gGateTable()[index].tfGatePractice; // og code @@ -138,8 +138,9 @@ namespace Megamix::Hooks { return RegionMegamix::UNK; } + // --- - void TickflowHooks() { + void initTickflowHooks() { rtInitHook(&tickflowHook, GHooks::tickflow(), (u32)getTickflowOffset); rtEnableHook(&tickflowHook); rtInitHook(&gateHook, GHooks::gate(), (u32)getGateTickflowOffset); @@ -148,7 +149,7 @@ namespace Megamix::Hooks { rtEnableHook(&gatePracHook); } - void TempoHooks() { + void initTempoHooks() { rtInitHook(&tempoStrmHook, GHooks::strmTempo(), (u32)getTempoStrm); rtEnableHook(&tempoStrmHook); rtInitHook(&tempoSeqHook, GHooks::seqTempo(), (u32)getTempoSeq); @@ -157,7 +158,7 @@ namespace Megamix::Hooks { rtEnableHook(&tempoAllHook); } - void RegionHooks() { + void initRegionHooks() { if (!Megamix::isJP()){ rtInitHook(®ionFSHook, GHooks::megamixRegionCode(), (u32)getRegionMegamix); rtEnableHook(®ionFSHook); @@ -166,19 +167,28 @@ namespace Megamix::Hooks { rtEnableHook(®ionOtherHook); } - void CommandHook() { - rtInitHook(&tickflowCommandsHook, Game::hTickflowCmds::hook(), (u32)tickflowCommandsHookWrapper); + void initCommandHooks() { + rtInitHook(&tickflowCommandsHook, Game::hTickflowCmds::hook(), (u32)tickflowCommands); rtEnableHook(&tickflowCommandsHook); } - void DisableAllHooks() { + void disableTickflowHooks() { rtDisableHook(&tickflowHook); rtDisableHook(&gateHook); + } + + void disableTempoHooks() { rtDisableHook(&tempoStrmHook); rtDisableHook(&tempoSeqHook); rtDisableHook(&tempoAllHook); + } + + void disableRegionHooks() { rtDisableHook(®ionFSHook); rtDisableHook(®ionOtherHook); + } + + void disableCommandHooks() { rtDisableHook(&tickflowCommandsHook); } diff --git a/src/Megamix/Patches.cpp b/src/Megamix/Patches.cpp index 01408c1..c52e264 100644 --- a/src/Megamix/Patches.cpp +++ b/src/Megamix/Patches.cpp @@ -4,12 +4,39 @@ #include #include <3ds.h> -#include +#include "CTRPF.hpp" #include "Megamix.hpp" #include "Config.hpp" +// TODO: these currently have no way to unpatch. should we not do that? + namespace Megamix::Patches { + // see specified sections in the arm A-profile reference manual + namespace BuildInstr { + // register is 4 bits, value is 12 bits + // see F5.1.35 + static constexpr u32 cmp_immediate(u32 reg, u32 value) { + // 1110 == no condition + const u32 cond = 0b1110 << 28; + const u32 cmp_imm_base = 0b0000'00110'10'1 << 20; + reg <<= 16; + + return cond | cmp_imm_base | reg | value; + } + + // register is 4 bits, value is 12 bits + // see F5.1.122 + static constexpr u32 mov_immediate(u32 reg, u32 value) { + // 1110 == no condition + const u32 cond = 0b1110 << 28; + const u32 cmp_imm_base = 0b0000'00111'01'0 << 20; + reg <<= 12; + + return cond | cmp_imm_base | reg | value; + } + } + std::vector museumRows { /* 0 */ MuseumRow({ RvlKarate0, NtrRobotS, RvlBadmintonS, CtrStepS, None }, "stage_gr00", 0, 0), /* 1 */ MuseumRow({ AgbHairS, NtrChorusS, RvlMuscleS, CtrFruitbasketS, None }, "stage_gr01", 0, 1), @@ -37,7 +64,7 @@ namespace Megamix::Patches { /* 23 */ MuseumRow({ AgbHoppingL, AgbNightWalkL, AgbQuizL, None, None }, "bonus_AGB", 0, 0), /* 24 */ MuseumRow({ NtrBoxShowL, NtrShortLiveL, RvlKarate2, None, None }, "bonus_NTR", 0, 0), /* 25 */ MuseumRow({ RvlAssembleL, RvlDateL, RvlFishingL, None, None }, "bonus_RVL0", 0, 0), - /* 26 */ MuseumRow({ RvlForkL, RvlRapL, RvlRecieveL, None, None }, "bonus_RVL1", 0, 0), + /* 26 */ MuseumRow({ RvlForkL, RvlRapL, RvlReceiveL, None, None }, "bonus_RVL1", 0, 0), /* 27 */ MuseumRow({ RvlRobotL, RvlRotationL, RvlSamuraiL, None, None }, "bonus_RVL2", 0, 0), /* 28 */ MuseumRow({ RvlSortL, RvlWatchL, RvlKarate3, None, None }, "bonus_RVL3", 0, 0), }; @@ -74,17 +101,6 @@ namespace Megamix::Patches { /* 28 */ MuseumRowColor(0x78500AFF, 0x643C3200), }; - // see section F5.1.35 in the arm A-profile reference manual - // register is 4 bits, value is 12 bits - constexpr u32 make_cmp_immediate_instruction(u32 reg, u32 value) { - // 1110 == no condition - const u32 cond = 0b1110 << 28; - const u32 cmp_imm_base = 0b0000'00110'10'1 << 20; - reg <<= 16; - - return cond | cmp_imm_base | reg | value; - } - std::optional SlotIdToMuseumGameId(u16 gameId) { if (gameId < 0x100) { return gameId; @@ -111,7 +127,7 @@ namespace Megamix::Patches { // not all slots are valid museum games u32 validGameIds = 0; - for (auto &[key, _] : config->tickflows) { + for (auto &[key, _] : config.tickflows) { if (SlotIdToMuseumGameId(key).has_value()) { validGameIds += 1; } @@ -119,7 +135,7 @@ namespace Megamix::Patches { // museum don't support rows with only 2 games if (validGameIds == 2) { - for (auto &pair : config->tickflows) { + for (auto &pair : config.tickflows) { std::optional id = SlotIdToMuseumGameId(pair.first); if (!id.has_value()) { continue; @@ -131,7 +147,7 @@ namespace Megamix::Patches { std::array newRowIds { None, None, None, None, None }; size_t newRowLength = 0; - for (auto &pair : config->tickflows) { + for (auto &pair : config.tickflows) { std::optional id = SlotIdToMuseumGameId(pair.first); if (!id.has_value()) { continue; @@ -195,14 +211,14 @@ namespace Megamix::Patches { } u32 compare_r1_instruction = // cmp r1, MUSEUM_ROW_COUNT - make_cmp_immediate_instruction(1, museumRows.size()); + BuildInstr::cmp_immediate(1, museumRows.size()); for (auto address : Game::pMuseumRows::r1Cmps()) { Process::Patch(address, compare_r1_instruction); } u32 compare_r8_instruction = // cmp r8, MUSEUM_ROW_COUNT - make_cmp_immediate_instruction(8, museumRows.size()); + BuildInstr::cmp_immediate(8, museumRows.size()); for (auto address : Game::pMuseumRows::r8Cmps()) { Process::Patch(address, compare_r8_instruction); @@ -215,20 +231,9 @@ namespace Megamix::Patches { } } - // see section F5.1.122 in the arm A-profile reference manual - // register is 4 bits, value is 12 bits - constexpr u32 make_mov_immediate_instruction(u32 reg, u32 value) { - // 1110 == no condition - const u32 cond = 0b1110 << 28; - const u32 cmp_imm_base = 0b0000'00111'01'0 << 20; - reg <<= 12; - - return cond | cmp_imm_base | reg | value; - } - void PatchRetryRemix() { u32 instr = // mov r2, #0xE - make_mov_immediate_instruction(2, 0xE); + BuildInstr::mov_immediate(2, 0xE); for (auto loc: Game::Patches::ptrsToRetryRemix()){ Process::Patch(loc, instr); diff --git a/src/Megamix/Region.cpp b/src/Megamix/Region.cpp index c2a7781..9b97c01 100644 --- a/src/Megamix/Region.cpp +++ b/src/Megamix/Region.cpp @@ -1,12 +1,10 @@ #include <3ds.h> -#include +#include "CTRPF.hpp" #include "Megamix.hpp" #include -u8 region; - #define THUMB_CALL_ADDR(pos) ((pos) | 1) namespace Megamix { @@ -206,7 +204,7 @@ namespace Megamix { const GameInterface* pointers = nullptr; - std::expected initGameInterface(u32 gameCode) { + std::expected initGameInterface(u32 gameCode) { switch (gameCode) { case 0x155a00: pointers = &jpCode; diff --git a/src/main.cpp b/src/main.cpp index 19d6d32..ac83aa1 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -1,23 +1,18 @@ #include <3ds.h> -#include +#include "CTRPF.hpp" #include "csvc.h" #include "external/plgldr.h" #include "Megamix.hpp" #include "Config.hpp" - +#include "Saltwater.hpp" #include "Stuff.hpp" -Config* config; -using Megamix::btks; - const char* version = VERSION; SaltwaterParams params; -// tired of typing these names -namespace ctrpf = CTRPluginFramework; namespace CTRPluginFramework { void PatchProcess(FwkSettings &settings); void OnProcessExit(void); @@ -57,7 +52,7 @@ static void ToggleTouchscreenForceOn(void) { if(R_FAILED(svcMapProcessMemoryEx(CUR_PROCESS_HANDLE, 0x14000000, processHandle, (u32)startAddress, textTotalSize))) goto exit; - found = (u32 *)Utils::Search(0x14000000, (u32)textTotalSize, pattern); + found = (u32 *)CTRPF::Utils::Search(0x14000000, (u32)textTotalSize, pattern); if (found != nullptr) { @@ -73,28 +68,29 @@ static void ToggleTouchscreenForceOn(void) { // This function is called before main and before the game starts // Useful to do code edits safely -void ctrpf::PatchProcess(ctrpf::FwkSettings&) { +void CTRPF::PatchProcess(CTRPF::FwkSettings&) { ToggleTouchscreenForceOn(); - // le params :D - params = *(SaltwaterParams*)ctrpf::FwkSettings::Header->config; + // plugin params - these are used for shorter types (bools usually) + params = *reinterpret_cast(CTRPF::FwkSettings::Header->config); if (params.rhmpatch) { - // move RHMPatch back to where it was - ctrpf::File::Rename( + // barista moved RHMPatch's code.ips, move it back to where it was + CTRPF::File::Rename( "/luma/titles/000400000018a400/code.old.ips", "/luma/titles/000400000018a400/code.ips" ); } + if (params.plgldr) { - // disable plugin loader + // had to specifically turn on the plgldr, turn it off to not cause any issues plgLdrInit(); PLGLDR__SetPluginLoaderState(false); plgLdrExit(); } // Init region and config - auto region_res = Megamix::initGameInterface(ctrpf::Process::GetTitleID()); + auto region_res = Megamix::initGameInterface(CTRPF::Process::GetTitleID()); if (!region_res.has_value()) { Megamix::panic("what the hell how did you get this\nyou're running saltwater on something that isn't megamix\nluma3ds shouldn't allow this, please report this bug!"); } @@ -102,14 +98,14 @@ void ctrpf::PatchProcess(ctrpf::FwkSettings&) { config = Config::FromFile(MEGAMIX_CONFIG_PATH); // Start hooks, apply patches - Megamix::Hooks::TickflowHooks(); - Megamix::Hooks::RegionHooks(); + Megamix::Hooks::initTickflowHooks(); + Megamix::Hooks::initRegionHooks(); Megamix::Patches::PatchRetryRemix(); if (!Megamix::isJP()) { //TODO: find out how to make the tempo hooks JP-compatible - Megamix::Hooks::TempoHooks(); + Megamix::Hooks::initTempoHooks(); //TODO: find out how to make the tickflow commands hook JP-compatible - Megamix::Hooks::CommandHook(); + Megamix::Hooks::initCommandHooks(); } if (!Megamix::isJP() && params.extra_rows) { @@ -120,40 +116,39 @@ void ctrpf::PatchProcess(ctrpf::FwkSettings&) { // This function is called when the process exits // Useful to save settings, undo patchs or clean up things -void ctrpf::OnProcessExit(void) { - Megamix::Hooks::DisableAllHooks(); // Probably not needed, but still +void CTRPF::OnProcessExit(void) { + Megamix::Hooks::disableAllHooks(); // Probably not needed, but still ToggleTouchscreenForceOn(); - delete config; } #ifndef RELEASE -void InitMenu(ctrpf::PluginMenu &menu) { - menu += new ctrpf::MenuEntry("Config values", nullptr, [](ctrpf::MenuEntry*) { - ctrpf::MessageBox("Settings", Utils::Format( +void InitMenu(CTRPF::PluginMenu &menu) { + menu += new CTRPF::MenuEntry("Config values", nullptr, [](CTRPF::MenuEntry*) { + CTRPF::MessageBox("Settings", Format( "Result: %d", configResult ))(); }); - menu += new ctrpf::MenuEntry("Tickflow contents", nullptr, [](ctrpf::MenuEntry*) { - ctrpf::MessageBox("Map shit", Stuff::FileMapToString(config->tickflows))(); + menu += new CTRPF::MenuEntry("Tickflow contents", nullptr, [](CTRPF::MenuEntry*) { + CTRPF::MessageBox("Map shit", Stuff::FileMapToString(config.tickflows))(); }); - menu += new ctrpf::MenuEntry("Tempo contents (do this w a loaded btks)", nullptr, [](ctrpf::MenuEntry*) { - ctrpf::MessageBox("Map shit", Stuff::TempoMapToString(btks.tempos))(); + menu += new CTRPF::MenuEntry("Tempo contents (do this w a loaded btks)", nullptr, [](CTRPF::MenuEntry*) { + CTRPF::MessageBox("Map shit", Stuff::TempoMapToString(Megamix::btks.tempos))(); }); - menu += new ctrpf::MenuEntry("Force a crash (prefetch)", nullptr, [](ctrpf::MenuEntry*) { + menu += new CTRPF::MenuEntry("Force a crash (prefetch)", nullptr, [](CTRPF::MenuEntry*) { ((void(*)(void))nullptr)(); }); - menu += new ctrpf::MenuEntry("Force a crash (data)", nullptr, [](ctrpf::MenuEntry*) { + menu += new CTRPF::MenuEntry("Force a crash (data)", nullptr, [](CTRPF::MenuEntry*) { *(volatile int*)nullptr = 100; }); } #endif -int ctrpf::main(void) { +int CTRPF::main(void) { // Crash handler Process::exceptionCallback = Megamix::CrashHandler; @@ -164,7 +159,7 @@ int ctrpf::main(void) { #ifdef RELEASE Process::WaitForExit(); #else - PluginMenu *menu = new PluginMenu(Utils::Format("Saltwater %s debug", VERSION), "", 1); + PluginMenu *menu = new PluginMenu(Format("Saltwater %s debug", VERSION), "", 1); // Synnchronize the menu with frame event menu->SynchronizeWithFrame(true);