diff --git a/soh/soh/Enhancements/debugger/debugSaveEditor.cpp b/soh/soh/Enhancements/debugger/debugSaveEditor.cpp index 4e874976d7..5f40135134 100644 --- a/soh/soh/Enhancements/debugger/debugSaveEditor.cpp +++ b/soh/soh/Enhancements/debugger/debugSaveEditor.cpp @@ -11,8 +11,7 @@ #include "soh/Enhancements/randomizer/static_data.h" #include "soh/Enhancements/randomizer/item.h" #include "soh/Enhancements/randomizer/dungeon.h" -#include "soh/Enhancements/randomizer/randomizerEnums/RandomizerGet.h" -#include "soh/Enhancements/randomizer/randomizerEnums/RandomizerInf.h" +#include "soh/Enhancements/randomizer/randomizerEnumStrings.h" #include "soh/util.h" #include "soh/SohGui/ImGuiUtils.h" #include "soh/OTRGlobals.h" @@ -1368,13 +1367,24 @@ void DrawFlagArrayWithTooltips(const std::string& name, T& flags, Colors color, ImGui::PopID(); } +static const char* GetFlagDescription(const FlagTable& flagTable, uint16_t index) { + if (flagTable.flagTableType == RANDOMIZER_INF) { + std::string_view name = EnumToString(static_cast(index)); + return name.empty() ? "" : name.data(); + } + auto it = flagTable.flagDescriptions.find(index); + return it != flagTable.flagDescriptions.end() ? it->second : ""; +} + // Draw a flag bitfield as a grid of checkboxes void DrawFlagTableArray16(const FlagTable& flagTable, uint16_t row, uint16_t& flags) { ImGui::PushID((std::to_string(row) + flagTable.name).c_str()); for (int32_t flagIndex = 15; flagIndex >= 0; flagIndex--) { ImGui::SameLine(); ImGui::PushID(flagIndex); - bool hasDescription = !!flagTable.flagDescriptions.contains(row * 16 + flagIndex); + uint16_t index = static_cast(row * 16 + flagIndex); + const char* desc = GetFlagDescription(flagTable, index); + bool hasDescription = desc[0] != '\0'; uint32_t bitMask = 1 << flagIndex; ImVec4 themeColor = ColorValues.at(THEME_COLOR); ImVec4 colorDark = { themeColor.x * 0.4f, themeColor.y * 0.4f, themeColor.z * 0.4f, themeColor.z }; @@ -1396,9 +1406,7 @@ void DrawFlagTableArray16(const FlagTable& flagTable, uint16_t row, uint16_t& fl PopStyleCheckbox(); if (ImGui::IsItemHovered()) { ImGui::BeginTooltip(); - uint16_t index = row * 16 + flagIndex; if (hasDescription) { - const char* desc = flagTable.flagDescriptions.at(index); ImGui::Text("0x%02X: %s", index, UIWidgets::WrappedText(desc, 60).c_str()); } else { ImGui::Text("0x%02X: %s (Bit %d)", index, flagTable.name, flagIndex); @@ -1470,8 +1478,7 @@ static void DrawFlagTableSearchResults(const FlagTable& flagTable, ImGuiTextFilt for (int32_t flagIndex = 15; flagIndex >= 0; flagIndex--) { uint16_t index = static_cast(row * 16 + flagIndex); - auto descIt = flagTable.flagDescriptions.find(index); - const char* desc = descIt != flagTable.flagDescriptions.end() ? descIt->second : ""; + const char* desc = GetFlagDescription(flagTable, index); std::string searchable = spdlog::fmt_lib::format("0x{:02X} {}", index, desc); if (!filter.PassFilter(searchable.c_str())) { continue; @@ -1480,7 +1487,7 @@ static void DrawFlagTableSearchResults(const FlagTable& flagTable, ImGuiTextFilt hasMatches = true; ImGui::PushID(index); - bool hasDescription = descIt != flagTable.flagDescriptions.end(); + bool hasDescription = desc[0] != '\0'; uint32_t bitMask = 1 << flagIndex; ImVec4 themeColor = ColorValues.at(THEME_COLOR); ImVec4 colorDark = { themeColor.x * 0.4f, themeColor.y * 0.4f, themeColor.z * 0.4f, themeColor.z }; diff --git a/soh/soh/Enhancements/debugger/debugSaveEditor.h b/soh/soh/Enhancements/debugger/debugSaveEditor.h index f448be4a73..3a82149d37 100644 --- a/soh/soh/Enhancements/debugger/debugSaveEditor.h +++ b/soh/soh/Enhancements/debugger/debugSaveEditor.h @@ -359,14 +359,8 @@ const std::vector flagTables = { { 0x24, "Market Crowd Text Randomizer" }, { 0x30, "Entered the Market" }, } }, - { "Randomizer Inf Flags", - RANDOMIZER_INF, - (RAND_INF_MAX + 15) / 16, - { -#define RANDO_ENUM_ITEM(rand_inf) { rand_inf, #rand_inf }, -#include "soh/Enhancements/randomizer/randomizerEnums/RandomizerInf.h" -#undef RANDO_ENUM_ITEM - } }, + // Randomizer Inf flags are described by their enum name, see GetFlagDescription. + { "Randomizer Inf Flags", RANDOMIZER_INF, (RAND_INF_MAX + 15) / 16, {} }, }; const std::vector state1 = { diff --git a/soh/soh/Enhancements/randomizer/logic.cpp b/soh/soh/Enhancements/randomizer/logic.cpp index b70908eb94..e4d429710f 100644 --- a/soh/soh/Enhancements/randomizer/logic.cpp +++ b/soh/soh/Enhancements/randomizer/logic.cpp @@ -5,6 +5,7 @@ #include "../debugger/performanceTimer.h" #include "soh/OTRGlobals.h" #include "randomizer.h" +#include "randomizerEnumStrings.h" #include "dungeon.h" #include "SeedContext.h" #include "randomizer.h" @@ -322,8 +323,7 @@ bool Logic::HasItem(RandomizerGet itemName) { default: break; } - SPDLOG_ERROR("HasItem reached `return false;`. Missing case for RandomizerGet of {}", - static_cast(itemName)); + SPDLOG_ERROR("HasItem reached `return false;`. Missing case for RandomizerGet of {}", itemName); assert(false); return false; } @@ -486,7 +486,7 @@ bool Logic::ItemUseAllowed(RandomizerGet itemName) { case SCENE_FISHING_POND: return itemName == RG_FISHING_POLE; default: - SPDLOG_INFO("ItemUseAllowed reached `default` with item {} in Scene {}.", static_cast(itemName), + SPDLOG_INFO("ItemUseAllowed reached `default` with item {} in Scene {}.", itemName, static_cast(RegionTable(CurrentRegionKey)->scene)); return true; } @@ -676,8 +676,7 @@ bool Logic::CanUse(RandomizerGet itemName) { return true; default: - SPDLOG_INFO("CanUse reached `default` for {}. using HasItem is a minor Optimisation.", - static_cast(itemName)); + SPDLOG_INFO("CanUse reached `default` for {}. using HasItem is a minor Optimisation.", itemName); return true; } } diff --git a/soh/soh/Enhancements/randomizer/option.cpp b/soh/soh/Enhancements/randomizer/option.cpp index a3b3009a34..cb1df53679 100644 --- a/soh/soh/Enhancements/randomizer/option.cpp +++ b/soh/soh/Enhancements/randomizer/option.cpp @@ -1,6 +1,7 @@ #include #include "option.h" +#include "randomizerEnumStrings.h" #include "static_data.h" #include "soh/SohGui/SohMenu.h" #include "soh/Enhancements/Lang/Lang.h" @@ -70,18 +71,6 @@ RandomizerSettingKey Option::GetKey() const { #pragma region Lang -#define RANDO_ENUM_ITEM(enum) { enum, #enum }, - -std::unordered_map settingNames = { -#include "randomizerEnums/RandomizerSettingKey.h" -}; - -std::unordered_map trickNames = { -#include "randomizerEnums/RandomizerTrick.h" -}; - -#undef RANDO_ENUM_ITEM - const static std::string namePostfix = ".name"; const static std::string descriptionPostfix = ".description"; @@ -92,13 +81,13 @@ const static std::string empty = ""; const static std::string error = "[ERROR]"; static const std::string& MakeSettingName(RandomizerSettingKey key) { - std::string settingNamePart = settingNames[key].substr(4); + std::string settingNamePart(EnumToString(key).substr(4)); std::transform(settingNamePart.begin(), settingNamePart.end(), settingNamePart.begin(), ::tolower); return Lang::Translate((settingPrefix + settingNamePart + namePostfix).c_str()); } static const std::string& MakeSettingDescription(RandomizerSettingKey key) { - std::string settingNamePart = settingNames[key].substr(4); + std::string settingNamePart(EnumToString(key).substr(4)); std::transform(settingNamePart.begin(), settingNamePart.end(), settingNamePart.begin(), ::tolower); auto result = Lang::TryTranslate((settingPrefix + settingNamePart + descriptionPostfix).c_str()); if (std::holds_alternative>(result)) { @@ -112,13 +101,13 @@ static const std::string& MakeSettingDescription(RandomizerSettingKey key) { } static const std::string& MakeTrickName(RandomizerTrick key) { - std::string trickNamePart = trickNames[key].substr(3); + std::string trickNamePart(EnumToString(key).substr(3)); std::transform(trickNamePart.begin(), trickNamePart.end(), trickNamePart.begin(), ::tolower); return Lang::Translate((trickPrefix + trickNamePart + namePostfix).c_str()); } static const std::string& MakeTrickDescription(RandomizerTrick key) { - std::string trickNamePart = trickNames[key].substr(3); + std::string trickNamePart(EnumToString(key).substr(3)); std::transform(trickNamePart.begin(), trickNamePart.end(), trickNamePart.begin(), ::tolower); return Lang::Translate((trickPrefix + trickNamePart + descriptionPostfix).c_str()); } diff --git a/soh/soh/Enhancements/randomizer/randomizerEnumList.h b/soh/soh/Enhancements/randomizer/randomizerEnumList.h new file mode 100644 index 0000000000..a83339f4a5 --- /dev/null +++ b/soh/soh/Enhancements/randomizer/randomizerEnumList.h @@ -0,0 +1,12 @@ +// Deliberately has no include guard: define RANDO_ENUM_BEGIN/ITEM/END and include this to +// expand every enum again. +#include "randomizerEnums/LogicVal.h" +#include "randomizerEnums/RandomizerCheck.h" +#include "randomizerEnums/RandomizerGet.h" +#include "randomizerEnums/RandomizerHintTextKey.h" +#include "randomizerEnums/RandomizerInf.h" +#include "randomizerEnums/RandomizerMiscEnums.h" +#include "randomizerEnums/RandomizerOptions.h" +#include "randomizerEnums/RandomizerRegion.h" +#include "randomizerEnums/RandomizerSettingKey.h" +#include "randomizerEnums/RandomizerTrick.h" diff --git a/soh/soh/Enhancements/randomizer/randomizerEnumStrings.cpp b/soh/soh/Enhancements/randomizer/randomizerEnumStrings.cpp index 5269d217cd..439763d449 100644 --- a/soh/soh/Enhancements/randomizer/randomizerEnumStrings.cpp +++ b/soh/soh/Enhancements/randomizer/randomizerEnumStrings.cpp @@ -1,35 +1,26 @@ #include "randomizerEnumStrings.h" -// Redefine enum macros to generate string->enum maps for every enum. -#define RANDO_ENUM_BEGIN(EnumName) \ - template <> const std::unordered_map& GetStringToEnumMap() { \ - static const std::unordered_map map = [] { \ - std::unordered_map m; +static std::string NameOrValue(std::string_view name, std::string_view enumName, long long value) { + if (!name.empty()) { + return std::string(name); + } + return std::string(enumName) + '(' + std::to_string(value) + ')'; +} -#define RANDO_ENUM_ITEM(name, ...) m.emplace(#name, name); +#define RANDO_ENUM_BEGIN(EnumName) \ + std::string_view EnumToString(EnumName value) { \ + switch (value) { -#define RANDO_ENUM_END(EnumName) \ - return m; \ - } \ - (); \ - return map; \ +#define RANDO_ENUM_ITEM(name, ...) \ + case name: \ + return #name; + +#define RANDO_ENUM_END(EnumName) \ + } \ + return {}; \ + } \ + std::string format_as(EnumName value) { \ + return NameOrValue(EnumToString(value), #EnumName, (long long)value); \ } -#undef RANDO_ENUM_BEGIN -#undef RANDO_ENUM_ITEM -#undef RANDO_ENUM_END - -// Redefine enum macros to generate enum->string maps for every enum. -#define RANDO_ENUM_BEGIN(EnumName) \ - template <> const std::unordered_map& GetEnumToStringMap() { \ - static const std::unordered_map map = [] { \ - std::unordered_map m; - -#define RANDO_ENUM_ITEM(name, ...) m.emplace(name, #name); - -#define RANDO_ENUM_END(EnumName) \ - return m; \ - } \ - (); \ - return map; \ - } +#include "randomizerEnumList.h" diff --git a/soh/soh/Enhancements/randomizer/randomizerEnumStrings.h b/soh/soh/Enhancements/randomizer/randomizerEnumStrings.h index 11d3a87b39..29dbdf5926 100644 --- a/soh/soh/Enhancements/randomizer/randomizerEnumStrings.h +++ b/soh/soh/Enhancements/randomizer/randomizerEnumStrings.h @@ -1,29 +1,23 @@ #pragma once -#include +#include #include -#include -template const std::unordered_map& GetStringToEnumMap(); +#include "randomizerEnums.h" -template std::optional StringToEnum(const std::string_view& str) { - const auto& map = GetStringToEnumMap(); - auto it = map.find(str); - if (it != map.end()) { - return it->second; - } else { - return std::nullopt; - } -} - -template const std::unordered_map& GetEnumToStringMap(); - -template std::optional EnumToString(const T& value) { - const auto& map = GetEnumToStringMap(); - auto it = map.find(value); - if (it != map.end()) { - return it->second; - } else { - return std::nullopt; - } -} +// EnumToString gives an item's name, or an empty view for a value that isn't one of them. A +// name is a view over a string literal, so data() is NUL terminated and safe for %s. +// +// format_as is the hook fmt looks for: SPDLOG_ERROR("{}", RC_LINKS_POCKET) logs +// "RC_LINKS_POCKET" rather than a number, and a value with no name logs as +// "RandomizerCheck(...)". fmt finds it by argument dependent lookup, so including this +// header is all a caller has to do. +#define RANDO_ENUM_BEGIN(EnumName) \ + std::string_view EnumToString(EnumName value); \ + std::string format_as(EnumName value); +#define RANDO_ENUM_ITEM(...) +#define RANDO_ENUM_END(...) +#include "randomizerEnumList.h" +#undef RANDO_ENUM_BEGIN +#undef RANDO_ENUM_ITEM +#undef RANDO_ENUM_END diff --git a/soh/soh/Enhancements/randomizer/randomizerEnums.h b/soh/soh/Enhancements/randomizer/randomizerEnums.h index b5d4601e24..300b347084 100644 --- a/soh/soh/Enhancements/randomizer/randomizerEnums.h +++ b/soh/soh/Enhancements/randomizer/randomizerEnums.h @@ -10,16 +10,7 @@ // clang-format on #endif -#include "randomizerEnums/LogicVal.h" -#include "randomizerEnums/RandomizerCheck.h" -#include "randomizerEnums/RandomizerGet.h" -#include "randomizerEnums/RandomizerHintTextKey.h" -#include "randomizerEnums/RandomizerInf.h" -#include "randomizerEnums/RandomizerMiscEnums.h" -#include "randomizerEnums/RandomizerOptions.h" -#include "randomizerEnums/RandomizerRegion.h" -#include "randomizerEnums/RandomizerSettingKey.h" -#include "randomizerEnums/RandomizerTrick.h" +#include "randomizerEnumList.h" // Clean up only the defaults we defined. #ifdef RANDO_ENUM__CLEANUP diff --git a/soh/soh/Enhancements/randomizer/savefile.cpp b/soh/soh/Enhancements/randomizer/savefile.cpp index 3e4db83295..2fb9f30608 100644 --- a/soh/soh/Enhancements/randomizer/savefile.cpp +++ b/soh/soh/Enhancements/randomizer/savefile.cpp @@ -7,6 +7,7 @@ #include "soh/Enhancements/randomizer/dungeon.h" #include "soh/Enhancements/randomizer/logic.h" #include "soh/Enhancements/randomizer/randomizer.h" +#include "soh/Enhancements/randomizer/randomizerEnumStrings.h" extern "C" { #include "variables.h" @@ -326,7 +327,7 @@ void SetStartingItems() { Item_Give(NULL, ITEM_LETTER_RUTO); break; default: - SPDLOG_ERROR("[SetStartingItems] Unhandled value for bottleKey {}: {}", (int)bottleKey, bottle); + SPDLOG_ERROR("[SetStartingItems] Unhandled value for bottleKey {}: {}", bottleKey, bottle); assert(false); break; } diff --git a/soh/soh/Network/Anchor/Packets/SetCheckStatus.cpp b/soh/soh/Network/Anchor/Packets/SetCheckStatus.cpp index 7bd3d82862..4260ee3820 100644 --- a/soh/soh/Network/Anchor/Packets/SetCheckStatus.cpp +++ b/soh/soh/Network/Anchor/Packets/SetCheckStatus.cpp @@ -4,6 +4,7 @@ #include "soh/OTRGlobals.h" #include "soh/Enhancements/randomizer/randomizer_check_tracker.h" #include "soh/Enhancements/randomizer/randomizer.h" +#include "soh/Enhancements/randomizer/randomizerEnumStrings.h" static bool isResultOfHandling = false; @@ -41,7 +42,7 @@ void Anchor::HandlePacket_SetCheckStatus(nlohmann::json payload) { RandomizerCheck rc = payload.at("rc").get(); if (rc < 0 || rc >= RC_MAX) { - SPDLOG_ERROR("[Anchor] SET_CHECK_STATUS: rc {} out of range", (int)rc); + SPDLOG_ERROR("[Anchor] SET_CHECK_STATUS: {} out of range", rc); return; } RandomizerCheckStatus status = payload.at("status").get();