diff --git a/Source/CMakeLists.txt b/Source/CMakeLists.txt index 49d1a8477..d9bc5b8e6 100644 --- a/Source/CMakeLists.txt +++ b/Source/CMakeLists.txt @@ -161,6 +161,7 @@ set(libdevilutionx_SRCS utils/sdl_bilinear_scale.cpp utils/sdl_thread.cpp utils/str_cat.cpp + utils/str_case.cpp utils/surface_to_clx.cpp utils/utf8.cpp) diff --git a/Source/control.cpp b/Source/control.cpp index 3c3aa45ab..4944cdc40 100644 --- a/Source/control.cpp +++ b/Source/control.cpp @@ -49,6 +49,7 @@ #include "utils/log.hpp" #include "utils/sdl_geometry.h" #include "utils/stdcompat/optional.hpp" +#include "utils/str_case.hpp" #include "utils/str_cat.hpp" #include "utils/string_or_view.hpp" #include "utils/utf8.hpp" @@ -442,11 +443,9 @@ std::string TextCmdInspect(const string_view parameter) return ret; } - std::string param { parameter.data() }; - std::transform(param.begin(), param.end(), param.begin(), [](unsigned char c) { return std::tolower(c); }); + const std::string param = AsciiStrToLower(parameter); for (auto &player : Players) { - std::string playerName { player._pName }; - std::transform(playerName.begin(), playerName.end(), playerName.begin(), [](unsigned char c) { return std::tolower(c); }); + const std::string playerName = AsciiStrToLower(player._pName); if (playerName.find(param) != std::string::npos) { InspectPlayer = &player; StrAppend(ret, _("Inspecting player: ")); diff --git a/Source/debug.cpp b/Source/debug.cpp index 5e1eff9f2..189d82f32 100644 --- a/Source/debug.cpp +++ b/Source/debug.cpp @@ -32,6 +32,7 @@ #include "utils/file_util.h" #include "utils/language.h" #include "utils/log.hpp" +#include "utils/str_case.hpp" #include "utils/str_cat.hpp" #include "utils/str_split.hpp" @@ -669,14 +670,13 @@ std::string DebugCmdSpawnUniqueMonster(const string_view parameter) return "Monster name cannot be empty. Duh."; name.pop_back(); // remove last space - std::transform(name.begin(), name.end(), name.begin(), [](unsigned char c) { return std::tolower(c); }); + AsciiStrToLower(name); int mtype = -1; UniqueMonsterType uniqueIndex = UniqueMonsterType::None; for (size_t i = 0; UniqueMonstersData[i].mtype != MT_INVALID; i++) { auto mondata = UniqueMonstersData[i]; - std::string monsterName(mondata.mName); - std::transform(monsterName.begin(), monsterName.end(), monsterName.begin(), [](unsigned char c) { return std::tolower(c); }); + const std::string monsterName = AsciiStrToLower(mondata.mName); if (monsterName.find(name) == std::string::npos) continue; mtype = mondata.mtype; @@ -757,14 +757,13 @@ std::string DebugCmdSpawnMonster(const string_view parameter) return "Monster name cannot be empty. Duh."; name.pop_back(); // remove last space - std::transform(name.begin(), name.end(), name.begin(), [](unsigned char c) { return std::tolower(c); }); + AsciiStrToLower(name); int mtype = -1; for (int i = 0; i < NUM_MTYPES; i++) { auto mondata = MonstersData[i]; - std::string monsterName(mondata.name); - std::transform(monsterName.begin(), monsterName.end(), monsterName.begin(), [](unsigned char c) { return std::tolower(c); }); + const std::string monsterName = AsciiStrToLower(mondata.name); if (monsterName.find(name) == std::string::npos) continue; mtype = i; @@ -990,7 +989,7 @@ std::string DebugCmdSearchMonster(const string_view parameter) std::string name; AppendStrView(name, parameter); - std::transform(name.begin(), name.end(), name.begin(), [](unsigned char c) { return std::tolower(c); }); + AsciiStrToLower(name); SearchMonsters.push_back(name); return "We will find this bastard!"; @@ -1005,7 +1004,7 @@ std::string DebugCmdSearchItem(const string_view parameter) std::string name; AppendStrView(name, parameter); - std::transform(name.begin(), name.end(), name.begin(), [](unsigned char c) { return std::tolower(c); }); + AsciiStrToLower(name); SearchItems.push_back(name); return "Are you greedy? Anyway I will help you."; @@ -1020,7 +1019,7 @@ std::string DebugCmdSearchObject(const string_view parameter) std::string name; AppendStrView(name, parameter); - std::transform(name.begin(), name.end(), name.begin(), [](unsigned char c) { return std::tolower(c); }); + AsciiStrToLower(name); SearchObjects.push_back(name); return "I will look for the pyramids. Oh sorry, I'm looking for what you want, of course."; @@ -1251,11 +1250,9 @@ bool IsDebugAutomapHighlightNeeded() bool ShouldHighlightDebugAutomapTile(Point position) { auto matchesSearched = [](const string_view name, const std::vector &searchedNames) { - std::string nameToLower; - StrAppend(nameToLower, name); - std::transform(nameToLower.begin(), nameToLower.end(), nameToLower.begin(), [](unsigned char c) { return std::tolower(c); }); + const std::string lowercaseName = AsciiStrToLower(name); for (const auto &searchedName : searchedNames) { - if (nameToLower.find(searchedName) != std::string::npos) { + if (lowercaseName.find(searchedName) != std::string::npos) { return true; } } diff --git a/Source/items.cpp b/Source/items.cpp index 7fc4c77f4..518b685d7 100644 --- a/Source/items.cpp +++ b/Source/items.cpp @@ -46,6 +46,7 @@ #include "utils/log.hpp" #include "utils/math.h" #include "utils/stdcompat/algorithm.hpp" +#include "utils/str_case.hpp" #include "utils/str_cat.hpp" #include "utils/utf8.hpp" @@ -4611,7 +4612,7 @@ std::string DebugSpawnItem(std::string itemName) const int max_time = 3000; const int max_iter = 1000000; - std::transform(itemName.begin(), itemName.end(), itemName.begin(), [](unsigned char c) { return std::tolower(c); }); + AsciiStrToLower(itemName); Item testItem; @@ -4635,8 +4636,7 @@ std::string DebugSpawnItem(std::string itemName) testItem = {}; SetupAllItems(*MyPlayer, testItem, idx, AdvanceRndSeed(), monsterLevel, 1, false, false, false); - std::string tmp(testItem._iIName); - std::transform(tmp.begin(), tmp.end(), tmp.begin(), [](unsigned char c) { return std::tolower(c); }); + std::string tmp = AsciiStrToLower(testItem._iIName); if (tmp.find(itemName) != std::string::npos) break; } @@ -4656,7 +4656,7 @@ std::string DebugSpawnUniqueItem(std::string itemName) if (ActiveItemCount >= MAXITEMS) return "No space to generate the item!"; - std::transform(itemName.begin(), itemName.end(), itemName.begin(), [](unsigned char c) { return std::tolower(c); }); + AsciiStrToLower(itemName); UniqueItem uniqueItem; bool foundUnique = false; int uniqueIndex = 0; @@ -4664,8 +4664,7 @@ std::string DebugSpawnUniqueItem(std::string itemName) if (!IsUniqueAvailable(j)) break; - std::string tmp(UniqueItems[j].UIName); - std::transform(tmp.begin(), tmp.end(), tmp.begin(), [](unsigned char c) { return std::tolower(c); }); + const std::string tmp = AsciiStrToLower(UniqueItems[j].UIName); if (tmp.find(itemName) != std::string::npos) { itemName = tmp; uniqueItem = UniqueItems[j]; @@ -4718,8 +4717,7 @@ std::string DebugSpawnUniqueItem(std::string itemName) if (testItem._iMagical != ITEM_QUALITY_UNIQUE) continue; - std::string tmp(testItem._iIName); - std::transform(tmp.begin(), tmp.end(), tmp.begin(), [](unsigned char c) { return std::tolower(c); }); + const std::string tmp = AsciiStrToLower(testItem._iIName); if (tmp.find(itemName) != std::string::npos) break; return "Impossible to generate!"; diff --git a/Source/towners.cpp b/Source/towners.cpp index a877d009a..37cd6779a 100644 --- a/Source/towners.cpp +++ b/Source/towners.cpp @@ -11,6 +11,7 @@ #include "minitext.h" #include "stores.h" #include "utils/language.h" +#include "utils/str_case.hpp" namespace devilution { namespace { @@ -937,7 +938,7 @@ void UpdateCowFarmerAnimAfterQuestComplete() bool DebugTalkToTowner(std::string targetName) { SetupTownStores(); - std::transform(targetName.begin(), targetName.end(), targetName.begin(), [](unsigned char c) { return std::tolower(c); }); + AsciiStrToLower(targetName); Player &myPlayer = *MyPlayer; for (auto &townerData : TownersData) { if (!IsTownerPresent(townerData.type)) @@ -948,8 +949,7 @@ bool DebugTalkToTowner(std::string targetName) Towner fakeTowner; townerData.init(fakeTowner, townerData); fakeTowner.position = myPlayer.position.tile; - std::string npcName(fakeTowner.name); - std::transform(npcName.begin(), npcName.end(), npcName.begin(), [](unsigned char c) { return std::tolower(c); }); + const std::string npcName = AsciiStrToLower(fakeTowner.name); if (npcName.find(targetName) != std::string::npos) { townerData.talk(myPlayer, fakeTowner); return true; diff --git a/Source/utils/str_case.cpp b/Source/utils/str_case.cpp new file mode 100644 index 000000000..4ca9e2083 --- /dev/null +++ b/Source/utils/str_case.cpp @@ -0,0 +1,13 @@ +#include "utils/str_case.hpp" + +namespace devilution { + +void AsciiStrToLower(std::string &str) +{ + for (char &c : str) { // NOLINT(readability-identifier-length) + if (c >= 'A' && c <= 'Z') + c += ('a' - 'A'); + } +} + +} // namespace devilution diff --git a/Source/utils/str_case.hpp b/Source/utils/str_case.hpp new file mode 100644 index 000000000..59564c4ab --- /dev/null +++ b/Source/utils/str_case.hpp @@ -0,0 +1,18 @@ +#pragma once + +#include + +#include "utils/stdcompat/string_view.hpp" + +namespace devilution { + +void AsciiStrToLower(std::string &str); + +[[nodiscard]] inline std::string AsciiStrToLower(string_view str) +{ + std::string copy { str.data(), str.size() }; + AsciiStrToLower(copy); + return copy; +} + +} // namespace devilution