From 7e7d4f7f1611cbd3bbcef2ddf07a425142785308 Mon Sep 17 00:00:00 2001 From: offtkp Date: Tue, 24 Oct 2023 16:52:08 +0300 Subject: [PATCH 1/5] Make cheats struct more versatile --- include/cheats.hpp | 7 ++++++- include/emulator.hpp | 1 + src/core/cheats.cpp | 42 ++++++++++++++++++++++++++++++++++++++++-- src/hydra_core.cpp | 37 +++++++++++++++++++++++++++++++++++-- 4 files changed, 82 insertions(+), 5 deletions(-) diff --git a/include/cheats.hpp b/include/cheats.hpp index c8d7c7630..d995a883c 100644 --- a/include/cheats.hpp +++ b/include/cheats.hpp @@ -12,17 +12,22 @@ class Memory; class Cheats { public: enum class CheatType { + None, ActionReplay, // CTRPF cheats // TODO: Other cheat devices and standards? }; struct Cheat { + bool enabled; CheatType type; std::vector instructions; }; Cheats(Memory& mem, HIDService& hid); - void addCheat(const Cheat& cheat); + uint32_t addCheat(const Cheat& cheat); + void removeCheat(uint32_t id); + void enableCheat(uint32_t id); + void disableCheat(uint32_t id); void reset(); void run(); diff --git a/include/emulator.hpp b/include/emulator.hpp index 121c6b8d5..df9303730 100644 --- a/include/emulator.hpp +++ b/include/emulator.hpp @@ -115,6 +115,7 @@ class Emulator { void deinitGraphicsContext() { gpu.deinitGraphicsContext(); } EmulatorConfig& getConfig() { return config; } + Cheats& getCheats() { return cheats; } ServiceManager& getServiceManager() { return kernel.getServiceManager(); } RendererType getRendererType() const { return config.rendererType; } Renderer* getRenderer() { return gpu.getRenderer(); } diff --git a/src/core/cheats.cpp b/src/core/cheats.cpp index 618460c5c..6841cdce6 100644 --- a/src/core/cheats.cpp +++ b/src/core/cheats.cpp @@ -7,9 +7,45 @@ void Cheats::reset() { ar.reset(); // Reset ActionReplay } -void Cheats::addCheat(const Cheat& cheat) { - cheats.push_back(cheat); +uint32_t Cheats::addCheat(const Cheat& cheat) { cheatsLoaded = true; + + // Find an empty slot if a cheat was previously removed + for (size_t i = 0; i < cheats.size(); i++) { + if (cheats[i].type == CheatType::None) { + cheats[i] = cheat; + return i; + } + } + + // Otherwise, just add a new slot + cheats.push_back(cheat); + return cheats.size() - 1; +} + +void Cheats::removeCheat(uint32_t id) { + if (id >= cheats.size()) return; + + // Not using std::erase because we don't want to invalidate cheat IDs + cheats[id].type = CheatType::None; + cheats[id].instructions.clear(); + + // Check if no cheats are loaded + for (const auto& cheat : cheats) { + if (cheat.type != CheatType::None) return; + } + + cheatsLoaded = false; +} + +void Cheats::enableCheat(uint32_t id) { + if (id >= cheats.size()) return; + cheats[id].enabled = true; +} + +void Cheats::disableCheat(uint32_t id) { + if (id >= cheats.size()) return; + cheats[id].enabled = false; } void Cheats::clear() { @@ -19,6 +55,8 @@ void Cheats::clear() { void Cheats::run() { for (const Cheat& cheat : cheats) { + if (!cheat.enabled) continue; + switch (cheat.type) { case CheatType::ActionReplay: { ar.runCheat(cheat.instructions); diff --git a/src/hydra_core.cpp b/src/hydra_core.cpp index 3ecf70194..1c711a02e 100644 --- a/src/hydra_core.cpp +++ b/src/hydra_core.cpp @@ -4,8 +4,13 @@ #include #include "hydra_icon.hpp" +#include "swap.hpp" -class HC_GLOBAL HydraCore final : public hydra::IBase, public hydra::IOpenGlRendered, public hydra::IFrontendDriven, public hydra::IInput { +class HC_GLOBAL HydraCore final : public hydra::IBase, + public hydra::IOpenGlRendered, + public hydra::IFrontendDriven, + public hydra::IInput, + public hydra::ICheat { HYDRA_CLASS public: HydraCore(); @@ -31,6 +36,12 @@ class HC_GLOBAL HydraCore final : public hydra::IBase, public hydra::IOpenGlRend void setPollInputCallback(void (*callback)()) override; void setCheckButtonCallback(int32_t (*callback)(uint32_t player, hydra::ButtonType button)) override; + // ICheat + uint32_t addCheat(const uint8_t* data, uint32_t size) override; + void removeCheat(uint32_t id) override; + void enableCheat(uint32_t id) override; + void disableCheat(uint32_t id) override; + std::unique_ptr emulator; RendererGL* renderer; void (*pollInputCallback)() = nullptr; @@ -122,7 +133,29 @@ void HydraCore::setGetProcAddress(void* function) { getProcAddress = function; } void HydraCore::setPollInputCallback(void (*callback)()) { pollInputCallback = callback; } void HydraCore::setCheckButtonCallback(int32_t (*callback)(uint32_t player, hydra::ButtonType button)) { checkButtonCallback = callback; } -HC_API hydra::IBase* createEmulator() { return new HydraCore(); } +uint32_t HydraCore::addCheat(const uint8_t* data, uint32_t size) { + if ((size % 8) != 0) return hydra::BAD_CHEAT; + + Cheats::Cheat cheat; + cheat.enabled = true; + cheat.type = Cheats::CheatType::ActionReplay; + + for (uint32_t i = 0; i < size; i += 8) { + uint32_t first_word = Common::swap32(*reinterpret_cast(data + i)); + uint32_t second_word = Common::swap32(*reinterpret_cast(data + i + 4)); + cheat.instructions.insert(cheat.instructions.end(), {first_word, second_word}); + } + + return emulator->getCheats().addCheat(cheat); +}; + +void HydraCore::removeCheat(uint32_t id) { emulator->getCheats().removeCheat(id); } + +void HydraCore::enableCheat(uint32_t id) { emulator->getCheats().enableCheat(id); } + +void HydraCore::disableCheat(uint32_t id) { emulator->getCheats().disableCheat(id); } + +HC_API hydra::IBase* createEmulator() { return new HydraCore; } HC_API void destroyEmulator(hydra::IBase* emulator) { delete emulator; } HC_API const char* getInfo(hydra::InfoType type) { From 8502cd92854985b3c52f6ff520153a5bdf8fbfd9 Mon Sep 17 00:00:00 2001 From: wheremyfoodat <44909372+wheremyfoodat@users.noreply.github.com> Date: Tue, 24 Oct 2023 22:07:45 +0300 Subject: [PATCH 2/5] Moar cheats --- include/cheats.hpp | 8 ++++---- src/core/cheats.cpp | 23 ++++++++++++++--------- src/hydra_core.cpp | 44 +++++++++++++++++++++++++------------------- 3 files changed, 43 insertions(+), 32 deletions(-) diff --git a/include/cheats.hpp b/include/cheats.hpp index d995a883c..1ffa83b71 100644 --- a/include/cheats.hpp +++ b/include/cheats.hpp @@ -24,10 +24,10 @@ class Cheats { }; Cheats(Memory& mem, HIDService& hid); - uint32_t addCheat(const Cheat& cheat); - void removeCheat(uint32_t id); - void enableCheat(uint32_t id); - void disableCheat(uint32_t id); + u32 addCheat(const Cheat& cheat); + void removeCheat(u32 id); + void enableCheat(u32 id); + void disableCheat(u32 id); void reset(); void run(); diff --git a/src/core/cheats.cpp b/src/core/cheats.cpp index 6841cdce6..c359d432c 100644 --- a/src/core/cheats.cpp +++ b/src/core/cheats.cpp @@ -7,7 +7,7 @@ void Cheats::reset() { ar.reset(); // Reset ActionReplay } -uint32_t Cheats::addCheat(const Cheat& cheat) { +u32 Cheats::addCheat(const Cheat& cheat) { cheatsLoaded = true; // Find an empty slot if a cheat was previously removed @@ -23,8 +23,10 @@ uint32_t Cheats::addCheat(const Cheat& cheat) { return cheats.size() - 1; } -void Cheats::removeCheat(uint32_t id) { - if (id >= cheats.size()) return; +void Cheats::removeCheat(u32 id) { + if (id >= cheats.size()) { + return; + } // Not using std::erase because we don't want to invalidate cheat IDs cheats[id].type = CheatType::None; @@ -38,14 +40,16 @@ void Cheats::removeCheat(uint32_t id) { cheatsLoaded = false; } -void Cheats::enableCheat(uint32_t id) { - if (id >= cheats.size()) return; - cheats[id].enabled = true; +void Cheats::enableCheat(u32 id) { + if (id < cheats.size()) { + cheats[id].enabled = true; + } } -void Cheats::disableCheat(uint32_t id) { - if (id >= cheats.size()) return; - cheats[id].enabled = false; +void Cheats::disableCheat(u32 id) { + if (id < cheats.size()) { + cheats[id].enabled = false; + } } void Cheats::clear() { @@ -63,6 +67,7 @@ void Cheats::run() { break; } + case CheatType::None: break; default: Helpers::panic("Unknown cheat device!"); } } diff --git a/src/hydra_core.cpp b/src/hydra_core.cpp index 1c711a02e..25cecafc8 100644 --- a/src/hydra_core.cpp +++ b/src/hydra_core.cpp @@ -30,17 +30,17 @@ class HC_GLOBAL HydraCore final : public hydra::IBase, // IFrontendDriven void runFrame() override; - uint16_t getFps() override; + u16 getFps() override; // IInput void setPollInputCallback(void (*callback)()) override; - void setCheckButtonCallback(int32_t (*callback)(uint32_t player, hydra::ButtonType button)) override; + void setCheckButtonCallback(s32 (*callback)(u32 player, hydra::ButtonType button)) override; // ICheat - uint32_t addCheat(const uint8_t* data, uint32_t size) override; - void removeCheat(uint32_t id) override; - void enableCheat(uint32_t id) override; - void disableCheat(uint32_t id) override; + u32 addCheat(const u8* data, u32 size) override; + void removeCheat(u32 id) override; + void enableCheat(u32 id) override; + void disableCheat(u32 id) override; std::unique_ptr emulator; RendererGL* renderer; @@ -104,7 +104,7 @@ void HydraCore::runFrame() { emulator->runFrame(); } -uint16_t HydraCore::getFps() { return 60; } +u16 HydraCore::getFps() { return 60; } void HydraCore::reset() { emulator->reset(Emulator::ReloadOption::Reload); } hydra::Size HydraCore::getNativeSize() { return {400, 480}; } @@ -131,29 +131,35 @@ void HydraCore::setFbo(unsigned handle) { renderer->setFBO(handle); } void HydraCore::setGetProcAddress(void* function) { getProcAddress = function; } void HydraCore::setPollInputCallback(void (*callback)()) { pollInputCallback = callback; } -void HydraCore::setCheckButtonCallback(int32_t (*callback)(uint32_t player, hydra::ButtonType button)) { checkButtonCallback = callback; } +void HydraCore::setCheckButtonCallback(s32 (*callback)(u32 player, hydra::ButtonType button)) { checkButtonCallback = callback; } -uint32_t HydraCore::addCheat(const uint8_t* data, uint32_t size) { - if ((size % 8) != 0) return hydra::BAD_CHEAT; +u32 HydraCore::addCheat(const u8* data, u32 size) { + // Every 3DS cheat is a multiple of 64 bits == 8 bytes + if ((size % 8) != 0) { + return hydra::BAD_CHEAT; + } Cheats::Cheat cheat; cheat.enabled = true; cheat.type = Cheats::CheatType::ActionReplay; - for (uint32_t i = 0; i < size; i += 8) { - uint32_t first_word = Common::swap32(*reinterpret_cast(data + i)); - uint32_t second_word = Common::swap32(*reinterpret_cast(data + i + 4)); - cheat.instructions.insert(cheat.instructions.end(), {first_word, second_word}); + for (u32 i = 0; i < size; i += 8) { + auto read32 = [](const u8* ptr) { + return (u32(ptr[3]) << 24) | (u32(ptr[2]) << 16) | (u32(ptr[1]) << 8) | u32(ptr[0]); + }; + + // Data is passed to us in big endian so we bswap + u32 firstWord = Common::swap32(read32(data + i)); + u32 secondWord = Common::swap32(read32(data + i + 4)); + cheat.instructions.insert(cheat.instructions.end(), {firstWord, secondWord}); } return emulator->getCheats().addCheat(cheat); }; -void HydraCore::removeCheat(uint32_t id) { emulator->getCheats().removeCheat(id); } - -void HydraCore::enableCheat(uint32_t id) { emulator->getCheats().enableCheat(id); } - -void HydraCore::disableCheat(uint32_t id) { emulator->getCheats().disableCheat(id); } +void HydraCore::removeCheat(u32 id) { emulator->getCheats().removeCheat(id); } +void HydraCore::enableCheat(u32 id) { emulator->getCheats().enableCheat(id); } +void HydraCore::disableCheat(u32 id) { emulator->getCheats().disableCheat(id); } HC_API hydra::IBase* createEmulator() { return new HydraCore; } HC_API void destroyEmulator(hydra::IBase* emulator) { delete emulator; } From f9228707854b71958d6804de63a7960757d18304 Mon Sep 17 00:00:00 2001 From: wheremyfoodat <44909372+wheremyfoodat@users.noreply.github.com> Date: Tue, 24 Oct 2023 22:10:40 +0300 Subject: [PATCH 3/5] format --- include/cheats.hpp | 3 +-- src/core/cheats.cpp | 14 +++++++------- src/hydra_core.cpp | 12 +++++------- 3 files changed, 13 insertions(+), 16 deletions(-) diff --git a/include/cheats.hpp b/include/cheats.hpp index 1ffa83b71..59e88e7a2 100644 --- a/include/cheats.hpp +++ b/include/cheats.hpp @@ -12,9 +12,8 @@ class Memory; class Cheats { public: enum class CheatType { - None, + None, // Cheat has been removed by the frontend or is invalid ActionReplay, // CTRPF cheats - // TODO: Other cheat devices and standards? }; struct Cheat { diff --git a/src/core/cheats.cpp b/src/core/cheats.cpp index c359d432c..83e7cdc4e 100644 --- a/src/core/cheats.cpp +++ b/src/core/cheats.cpp @@ -25,8 +25,8 @@ u32 Cheats::addCheat(const Cheat& cheat) { void Cheats::removeCheat(u32 id) { if (id >= cheats.size()) { - return; - } + return; + } // Not using std::erase because we don't want to invalidate cheat IDs cheats[id].type = CheatType::None; @@ -42,14 +42,14 @@ void Cheats::removeCheat(u32 id) { void Cheats::enableCheat(u32 id) { if (id < cheats.size()) { - cheats[id].enabled = true; - } + cheats[id].enabled = true; + } } void Cheats::disableCheat(u32 id) { if (id < cheats.size()) { - cheats[id].enabled = false; - } + cheats[id].enabled = false; + } } void Cheats::clear() { @@ -67,7 +67,7 @@ void Cheats::run() { break; } - case CheatType::None: break; + case CheatType::None: break; default: Helpers::panic("Unknown cheat device!"); } } diff --git a/src/hydra_core.cpp b/src/hydra_core.cpp index 25cecafc8..b7c084e20 100644 --- a/src/hydra_core.cpp +++ b/src/hydra_core.cpp @@ -135,20 +135,18 @@ void HydraCore::setCheckButtonCallback(s32 (*callback)(u32 player, hydra::Button u32 HydraCore::addCheat(const u8* data, u32 size) { // Every 3DS cheat is a multiple of 64 bits == 8 bytes - if ((size % 8) != 0) { - return hydra::BAD_CHEAT; - } + if ((size % 8) != 0) { + return hydra::BAD_CHEAT; + } Cheats::Cheat cheat; cheat.enabled = true; cheat.type = Cheats::CheatType::ActionReplay; for (u32 i = 0; i < size; i += 8) { - auto read32 = [](const u8* ptr) { - return (u32(ptr[3]) << 24) | (u32(ptr[2]) << 16) | (u32(ptr[1]) << 8) | u32(ptr[0]); - }; + auto read32 = [](const u8* ptr) { return (u32(ptr[3]) << 24) | (u32(ptr[2]) << 16) | (u32(ptr[1]) << 8) | u32(ptr[0]); }; - // Data is passed to us in big endian so we bswap + // Data is passed to us in big endian so we bswap u32 firstWord = Common::swap32(read32(data + i)); u32 secondWord = Common::swap32(read32(data + i + 4)); cheat.instructions.insert(cheat.instructions.end(), {firstWord, secondWord}); From 1c501aceef28e306ca11f40a2513785e561f5bba Mon Sep 17 00:00:00 2001 From: wheremyfoodat <44909372+wheremyfoodat@users.noreply.github.com> Date: Thu, 26 Oct 2023 12:12:05 +0300 Subject: [PATCH 4/5] Cheats: Default to enabled + CTRPF --- include/cheats.hpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/include/cheats.hpp b/include/cheats.hpp index 59e88e7a2..2be25827d 100644 --- a/include/cheats.hpp +++ b/include/cheats.hpp @@ -17,8 +17,8 @@ class Cheats { }; struct Cheat { - bool enabled; - CheatType type; + bool enabled = true; + CheatType type = CheatType::ActionReplay; std::vector instructions; }; @@ -37,4 +37,4 @@ class Cheats { ActionReplay ar; // An ActionReplay cheat machine for executing CTRPF codes std::vector cheats; bool cheatsLoaded = false; -}; \ No newline at end of file +}; From 8b6008e1964db5cf65221308be8667b525c217c4 Mon Sep 17 00:00:00 2001 From: wheremyfoodat <44909372+wheremyfoodat@users.noreply.github.com> Date: Mon, 30 Oct 2023 00:15:13 +0200 Subject: [PATCH 5/5] Fix new syntax --- src/hydra_core.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/hydra_core.cpp b/src/hydra_core.cpp index b7c084e20..d67ffe2fd 100644 --- a/src/hydra_core.cpp +++ b/src/hydra_core.cpp @@ -159,7 +159,7 @@ void HydraCore::removeCheat(u32 id) { emulator->getCheats().removeCheat(id); } void HydraCore::enableCheat(u32 id) { emulator->getCheats().enableCheat(id); } void HydraCore::disableCheat(u32 id) { emulator->getCheats().disableCheat(id); } -HC_API hydra::IBase* createEmulator() { return new HydraCore; } +HC_API hydra::IBase* createEmulator() { return new HydraCore(); } HC_API void destroyEmulator(hydra::IBase* emulator) { delete emulator; } HC_API const char* getInfo(hydra::InfoType type) {