diff --git a/README.md b/README.md index 92cf987ad..c4a59a6a3 100644 --- a/README.md +++ b/README.md @@ -13,6 +13,7 @@ A "BIOS" ROM image is required to to start the emulator and to play games. You c ## Latest News +- 2020/09/25: Cheat support added for libretro core. - 2020/09/23: Game covers added to Qt frontend (see [Adding Game Covers](https://github.com/stenzek/duckstation/wiki/Adding-Game-Covers)). - 2020/09/19: Memory card importer/editor added to Qt frontend. - 2020/09/13: Support for chaining post processing shaders added. diff --git a/src/core/cheats.cpp b/src/core/cheats.cpp index 41ccac317..bc9d9c17b 100644 --- a/src/core/cheats.cpp +++ b/src/core/cheats.cpp @@ -196,49 +196,55 @@ bool CheatList::LoadFromLibretroFile(const char* filename) CheatCode cc; cc.description = *desc; cc.enabled = StringUtil::FromChars(*enable).value_or(false); - - const char* current_ptr = code->c_str(); - while (current_ptr) - { - char* end_ptr; - CheatCode::Instruction inst; - inst.first = static_cast(std::strtoul(current_ptr, &end_ptr, 16)); - current_ptr = end_ptr; - if (end_ptr) - { - if (*end_ptr != ' ') - { - Log_WarningPrintf("Malformed code '%s'", code->c_str()); - break; - } - - end_ptr++; - inst.second = static_cast(std::strtoul(current_ptr, &end_ptr, 16)); - current_ptr = end_ptr; - - if (end_ptr) - { - if (*end_ptr != '+') - { - Log_WarningPrintf("Malformed code '%s'", code->c_str()); - break; - } - - end_ptr++; - current_ptr = end_ptr; - } - - cc.instructions.push_back(inst); - } - } - - m_codes.push_back(std::move(cc)); + if (ParseLibretroCheat(&cc, code->c_str())) + m_codes.push_back(std::move(cc)); } Log_InfoPrintf("Loaded %zu cheats from '%s' (libretro format)", m_codes.size(), filename); return !m_codes.empty(); } +bool CheatList::ParseLibretroCheat(CheatCode* cc, const char* line) +{ + const char* current_ptr = line; + while (current_ptr) + { + char* end_ptr; + CheatCode::Instruction inst; + inst.first = static_cast(std::strtoul(current_ptr, &end_ptr, 16)); + current_ptr = end_ptr; + if (end_ptr) + { + if (*end_ptr != ' ') + { + Log_WarningPrintf("Malformed code '%s'", line); + break; + } + + end_ptr++; + inst.second = static_cast(std::strtoul(current_ptr, &end_ptr, 16)); + if (end_ptr && *end_ptr == '\0') + end_ptr = nullptr; + + if (end_ptr) + { + if (*end_ptr != '+') + { + Log_WarningPrintf("Malformed code '%s'", line); + break; + } + + end_ptr++; + } + + current_ptr = end_ptr; + cc->instructions.push_back(inst); + } + } + + return !cc->instructions.empty(); +} + void CheatList::Apply() { for (const CheatCode& code : m_codes) @@ -253,6 +259,20 @@ void CheatList::AddCode(CheatCode cc) m_codes.push_back(std::move(cc)); } +void CheatList::SetCode(u32 index, CheatCode cc) +{ + if (index > m_codes.size()) + return; + + if (index == m_codes.size()) + { + m_codes.push_back(std::move(cc)); + return; + } + + m_codes[index] = std::move(cc); +} + void CheatList::RemoveCode(u32 i) { m_codes.erase(m_codes.begin() + i); diff --git a/src/core/cheats.h b/src/core/cheats.h index 02e4d5ae9..abf317f39 100644 --- a/src/core/cheats.h +++ b/src/core/cheats.h @@ -71,6 +71,7 @@ public: ALWAYS_INLINE bool IsCodeEnabled(u32 index) const { return m_codes[index].enabled; } void AddCode(CheatCode cc); + void SetCode(u32 index, CheatCode cc); void RemoveCode(u32 i); u32 GetEnabledCodeCount() const; @@ -79,6 +80,7 @@ public: void SetCodeEnabled(u32 index, bool state); static std::optional DetectFileFormat(const char* filename); + static bool ParseLibretroCheat(CheatCode* cc, const char* line); bool LoadFromFile(const char* filename, Format format); bool LoadFromPCSXRFile(const char* filename); diff --git a/src/duckstation-libretro/libretro_host_interface.cpp b/src/duckstation-libretro/libretro_host_interface.cpp index fa4039d29..e4c5df5c2 100644 --- a/src/duckstation-libretro/libretro_host_interface.cpp +++ b/src/duckstation-libretro/libretro_host_interface.cpp @@ -6,6 +6,7 @@ #include "common/string_util.h" #include "core/analog_controller.h" #include "core/bus.h" +#include "core/cheats.h" #include "core/digital_controller.h" #include "core/gpu.h" #include "core/system.h" @@ -369,6 +370,29 @@ size_t LibretroHostInterface::retro_get_memory_size(unsigned id) } } +void LibretroHostInterface::retro_cheat_reset() +{ + System::SetCheatList(nullptr); +} + +void LibretroHostInterface::retro_cheat_set(unsigned index, bool enabled, const char* code) +{ + CheatList* cl = System::GetCheatList(); + if (!cl) + { + System::SetCheatList(std::make_unique()); + cl = System::GetCheatList(); + } + + CheatCode cc; + cc.description = StringUtil::StdStringFromFormat("Cheat%u", index); + cc.enabled = true; + if (!CheatList::ParseLibretroCheat(&cc, code)) + Log_ErrorPrintf("Failed to parse cheat %u '%s'", index, code); + + cl->SetCode(index, std::move(cc)); +} + bool LibretroHostInterface::AcquireHostDisplay() { // start in software mode, switch to hardware later diff --git a/src/duckstation-libretro/libretro_host_interface.h b/src/duckstation-libretro/libretro_host_interface.h index e4cb649c1..133598e3a 100644 --- a/src/duckstation-libretro/libretro_host_interface.h +++ b/src/duckstation-libretro/libretro_host_interface.h @@ -40,6 +40,8 @@ public: bool retro_unserialize(const void* data, size_t size); void* retro_get_memory_data(unsigned id); size_t retro_get_memory_size(unsigned id); + void retro_cheat_reset(); + void retro_cheat_set(unsigned index, bool enabled, const char* code); protected: bool AcquireHostDisplay() override; diff --git a/src/duckstation-libretro/main.cpp b/src/duckstation-libretro/main.cpp index 848fd53b3..ed400766d 100644 --- a/src/duckstation-libretro/main.cpp +++ b/src/duckstation-libretro/main.cpp @@ -79,12 +79,14 @@ RETRO_API bool retro_unserialize(const void* data, size_t size) RETRO_API void retro_cheat_reset(void) { - Log_ErrorPrintf("retro_cheat_reset()"); + Log_InfoPrint("retro_cheat_reset()"); + g_libretro_host_interface.retro_cheat_reset(); } RETRO_API void retro_cheat_set(unsigned index, bool enabled, const char* code) { - Log_ErrorPrintf("retro_cheat_set(%u, %u, %s)", index, enabled, code); + Log_InfoPrintf("retro_cheat_set(%u, %u, %s)", index, enabled, code); + g_libretro_host_interface.retro_cheat_set(index, enabled, code); } RETRO_API bool retro_load_game(const struct retro_game_info* game)