diff --git a/pcsx2/Patch.cpp b/pcsx2/Patch.cpp index 6cf0cbf6ba..9910841df4 100644 --- a/pcsx2/Patch.cpp +++ b/pcsx2/Patch.cpp @@ -43,16 +43,14 @@ namespace Patch { - enum patch_cpu_type + enum patch_cpu_type : u8 { - NO_CPU, CPU_EE, CPU_IOP }; - enum patch_data_type + enum patch_data_type : u8 { - NO_TYPE, BYTE_T, SHORT_T, WORD_T, @@ -63,18 +61,42 @@ namespace Patch DOUBLE_BE_T }; + static constexpr std::array s_place_to_string = {{"0", "1", "2"}}; + static constexpr std::array s_cpu_to_string = {{"EE", "IOP"}}; + static constexpr std::array s_type_to_string = { + {"byte", "short", "word", "double", "extended", "beshort", "beword", "bedouble"}}; + + template + static inline std::optional LookupEnumName(const std::string_view& val, const ArrayType& arr) + { + for (size_t i = 0; i < arr.size(); i++) + { + if (val == arr[i]) + return static_cast(i); + } + return std::nullopt; + } + struct PatchCommand { - patch_data_type type; + patch_place_type placetopatch; patch_cpu_type cpu; - int placetopatch; + patch_data_type type; u32 addr; u64 data; + PatchCommand() { std::memset(this, 0, sizeof(*this)); } + bool operator==(const PatchCommand& p) const { return std::memcmp(this, &p, sizeof(*this)) == 0; } bool operator!=(const PatchCommand& p) const { return std::memcmp(this, &p, sizeof(*this)) != 0; } + + std::string ToString() const + { + return fmt::format("{},{},{},{:08x},{:x}", s_place_to_string[static_cast(placetopatch)], + s_cpu_to_string[static_cast(cpu)], s_type_to_string[static_cast(type)], addr, data); + } }; - static_assert(sizeof(PatchCommand) == 24, "IniPatch has no padding"); + static_assert(sizeof(PatchCommand) == 16, "IniPatch has no padding"); struct PatchGroup { @@ -97,8 +119,6 @@ namespace Patch namespace PatchFunc { - static void author(PatchGroup* group, const std::string_view& cmd, const std::string_view& param); - static void comment(PatchGroup* group, const std::string_view& cmd, const std::string_view& param); static void patch(PatchGroup* group, const std::string_view& cmd, const std::string_view& param); static void gsaspectratio(PatchGroup* group, const std::string_view& cmd, const std::string_view& param); static void gsinterlacemode(PatchGroup* group, const std::string_view& cmd, const std::string_view& param); @@ -150,31 +170,11 @@ namespace Patch static std::optional s_override_interlace_mode; static const PatchTextTable s_patch_commands[] = { - {0, "author", &Patch::PatchFunc::author}, - {0, "comment", &Patch::PatchFunc::comment}, {0, "patch", &Patch::PatchFunc::patch}, {0, "gsaspectratio", &Patch::PatchFunc::gsaspectratio}, {0, "gsinterlacemode", &Patch::PatchFunc::gsinterlacemode}, {0, nullptr, nullptr}, }; - - static const PatchTextTable s_type_commands[] = { - {BYTE_T, "byte", nullptr}, - {SHORT_T, "short", nullptr}, - {WORD_T, "word", nullptr}, - {DOUBLE_T, "double", nullptr}, - {EXTENDED_T, "extended", nullptr}, - {SHORT_BE_T, "beshort", nullptr}, - {WORD_BE_T, "beword", nullptr}, - {DOUBLE_BE_T, "bedouble", nullptr}, - {NO_TYPE, nullptr, nullptr}, - }; - - static const PatchTextTable s_cpu_commands[] = { - {CPU_EE, "EE", nullptr}, - {CPU_IOP, "IOP", nullptr}, - {NO_CPU, nullptr, nullptr}, - }; } // namespace Patch void Patch::TrimPatchLine(std::string& buffer) @@ -475,11 +475,17 @@ u32 Patch::EnablePatches(const PatchList& patches, const EnablePatchList& enable if (!p.name.empty() && std::find(enable_list.begin(), enable_list.end(), p.name) == enable_list.end()) continue; - if (!p.name.empty()) - Console.WriteLn(Color_Green, fmt::format("Enabled patch: '{}'", p.name)); + Console.WriteLn(Color_Green, fmt::format("Enabled patch: {}", + p.name.empty() ? std::string_view("") : std::string_view(p.name))); for (const PatchCommand& ip : p.patches) + { + // print the actual patch lines only in verbose mode (even in devel) + if (DevConWriterEnabled) + DevCon.Indent().WriteLn(ip.ToString()); + s_active_patches.push_back(&ip); + } if (p.override_aspect_ratio.has_value()) s_override_aspect_ratio = p.override_aspect_ratio; @@ -511,7 +517,7 @@ void Patch::ReloadPatches(std::string serial, u32 crc, bool force_reload_files, { const u32 patch_count = LoadPatchesFromString(&s_gamedb_patches, *patches); if (patch_count > 0) - Console.WriteLn(Color_Green, "(GameDB) Patches Loaded: %d", patch_count); + Console.WriteLn(Color_Green, fmt::format("Found {} game patches in GameDB.", patch_count)); } LoadDynamicPatches(game->dynaPatches); @@ -530,7 +536,7 @@ void Patch::ReloadPatches(bool force_reload_files, bool reload_enabled_list, boo s_patches_serial, s_patches_crc, false, [](const std::string& filename, const std::string& pnach_data) { const u32 patch_count = LoadPatchesFromString(&s_game_patches, pnach_data); if (patch_count > 0) - Console.WriteLn(Color_Green, fmt::format("Loaded {} game patches from {}.", patch_count, filename)); + Console.WriteLn(Color_Green, fmt::format("Found {} game patches in {}.", patch_count, filename)); }); s_cheat_patches.clear(); @@ -538,7 +544,7 @@ void Patch::ReloadPatches(bool force_reload_files, bool reload_enabled_list, boo s_patches_serial, s_patches_crc, true, [](const std::string& filename, const std::string& pnach_data) { const u32 patch_count = LoadPatchesFromString(&s_cheat_patches, pnach_data); if (patch_count > 0) - Console.WriteLn(Color_Green, fmt::format("Loaded {} cheats from {}.", patch_count, filename)); + Console.WriteLn(Color_Green, fmt::format("Found {} cheats in {}.", patch_count, filename)); }); } @@ -656,22 +662,8 @@ void Patch::UnloadPatches() } // PatchFunc Functions. -void Patch::PatchFunc::comment(PatchGroup* group, const std::string_view& cmd, const std::string_view& param) -{ - Console.WriteLn(fmt::format("Patch comment: {}", param)); -} - -void Patch::PatchFunc::author(PatchGroup* group, const std::string_view& cmd, const std::string_view& param) -{ - Console.WriteLn(fmt::format("Patch author: {}", param)); -} - void Patch::PatchFunc::patch(PatchGroup* group, const std::string_view& cmd, const std::string_view& param) { - // print the actual patch lines only in verbose mode (even in devel) - if (DevConWriterEnabled) - DevCon.WriteLn(fmt::format("{} {}", cmd, param)); - #define PATCH_ERROR(fstring, ...) \ Console.Error(fmt::format("(Patch) Error Parsing: {}={}: " fstring, cmd, param, __VA_ARGS__)) @@ -683,22 +675,18 @@ void Patch::PatchFunc::patch(PatchGroup* group, const std::string_view& cmd, con return; } - std::string_view placetopatch_end; - const std::optional placetopatch = StringUtil::FromChars(pieces[0], 10, &placetopatch_end); + std::string_view addr_end, data_end; + const std::optional placetopatch = LookupEnumName(pieces[0], s_place_to_string); + const std::optional cpu = LookupEnumName(pieces[1], s_cpu_to_string); + const std::optional addr = StringUtil::FromChars(pieces[2], 16, &addr_end); + const std::optional type = LookupEnumName(pieces[3], s_type_to_string); + const std::optional data = StringUtil::FromChars(pieces[4], 16, &data_end); - PatchCommand iPatch; - iPatch.placetopatch = StringUtil::FromChars(pieces[0]).value_or(PPT_END_MARKER); - - if (!placetopatch.has_value() || !placetopatch_end.empty() || - (iPatch.placetopatch = placetopatch.value()) >= PPT_END_MARKER) + if (!placetopatch.has_value()) { PATCH_ERROR("Invalid 'place' value '{}' (0 - once on startup, 1: continuously)", pieces[0]); return; } - - std::string_view addr_end, data_end; - const std::optional addr = StringUtil::FromChars(pieces[2], 16, &addr_end); - const std::optional data = StringUtil::FromChars(pieces[4], 16, &data_end); if (!addr.has_value() || !addr_end.empty()) { PATCH_ERROR("Malformed address '{}', a hex number without prefix (e.g. 0123ABCD) is expected", pieces[2]); @@ -709,24 +697,23 @@ void Patch::PatchFunc::patch(PatchGroup* group, const std::string_view& cmd, con PATCH_ERROR("Malformed data '{}', a hex number without prefix (e.g. 0123ABCD) is expected", pieces[4]); return; } - - iPatch.cpu = (patch_cpu_type)PatchTableExecute(group, pieces[1], std::string_view(), s_cpu_commands); - iPatch.addr = addr.value(); - iPatch.type = (patch_data_type)PatchTableExecute(group, pieces[3], std::string_view(), s_type_commands); - iPatch.data = data.value(); - - if (iPatch.cpu == 0) + if (!cpu.has_value()) { PATCH_ERROR("Unrecognized CPU Target: '%.*s'", pieces[1]); return; } - - if (iPatch.type == 0) + if (!type.has_value()) { PATCH_ERROR("Unrecognized Operand Size: '%.*s'", pieces[3]); return; } + PatchCommand iPatch; + iPatch.placetopatch = placetopatch.value(); + iPatch.cpu = cpu.value(); + iPatch.addr = addr.value(); + iPatch.type = type.value(); + iPatch.data = data.value(); group->patches.push_back(iPatch); #undef PATCH_ERROR diff --git a/pcsx2/Patch.h b/pcsx2/Patch.h index 9e9604ddd8..2d6642166b 100644 --- a/pcsx2/Patch.h +++ b/pcsx2/Patch.h @@ -55,7 +55,7 @@ namespace Patch // - There's no "place" value which indicates to apply both once on startup // and then also continuously, however such behavior can be achieved by // duplicating the line where one has a 0 place and the other has a 1 place. - enum patch_place_type + enum patch_place_type : u8 { PPT_ONCE_ON_LOAD = 0, PPT_CONTINUOUSLY = 1,