diff --git a/Source/CMakeLists.txt b/Source/CMakeLists.txt index e07a6dedb..cef9d50cc 100644 --- a/Source/CMakeLists.txt +++ b/Source/CMakeLists.txt @@ -163,6 +163,7 @@ set(libdevilutionx_SRCS utils/pcx.cpp utils/sdl_bilinear_scale.cpp utils/sdl_thread.cpp + utils/str_cat.cpp utils/utf8.cpp) if(IOS) diff --git a/Source/DiabloUI/selgame.cpp b/Source/DiabloUI/selgame.cpp index 01457b056..c2c25be83 100644 --- a/Source/DiabloUI/selgame.cpp +++ b/Source/DiabloUI/selgame.cpp @@ -13,6 +13,7 @@ #include "options.h" #include "storm/storm_net.hpp" #include "utils/language.h" +#include "utils/str_cat.hpp" #include "utils/utf8.hpp" namespace devilution { @@ -254,7 +255,7 @@ void selgame_GameSelection_Focus(int value) break; default: // This should not occure, so no translations is needed - infoString.append(fmt::format("Speed: {}", gameInfo.gameData.nTickRate)); + infoString.append(StrCat("Speed: ", gameInfo.gameData.nTickRate)); break; } infoString += '\n'; diff --git a/Source/DiabloUI/selhero.cpp b/Source/DiabloUI/selhero.cpp index 67b2dfc90..0cdc25848 100644 --- a/Source/DiabloUI/selhero.cpp +++ b/Source/DiabloUI/selhero.cpp @@ -14,11 +14,12 @@ #include "DiabloUI/selyesno.h" #include "control.h" #include "controls/plrctrls.h" +#include "menu.h" #include "options.h" #include "pfile.h" #include "utils/language.h" +#include "utils/str_cat.hpp" #include "utils/utf8.hpp" -#include namespace devilution { @@ -78,12 +79,12 @@ void SelheroFree() void SelheroSetStats() { SELHERO_DIALOG_HERO_IMG->setSprite(UiGetHeroDialogSprite(static_cast(selhero_heroInfo.heroclass))); - CopyUtf8(textStats[0], fmt::format("{}", selhero_heroInfo.level), sizeof(textStats[0])); - CopyUtf8(textStats[1], fmt::format("{}", selhero_heroInfo.strength), sizeof(textStats[1])); - CopyUtf8(textStats[2], fmt::format("{}", selhero_heroInfo.magic), sizeof(textStats[2])); - CopyUtf8(textStats[3], fmt::format("{}", selhero_heroInfo.dexterity), sizeof(textStats[3])); - CopyUtf8(textStats[4], fmt::format("{}", selhero_heroInfo.vitality), sizeof(textStats[4])); - CopyUtf8(textStats[5], fmt::format("{}", selhero_heroInfo.saveNumber), sizeof(textStats[5])); + CopyUtf8(textStats[0], StrCat(selhero_heroInfo.level), sizeof(textStats[0])); + CopyUtf8(textStats[1], StrCat(selhero_heroInfo.strength), sizeof(textStats[1])); + CopyUtf8(textStats[2], StrCat(selhero_heroInfo.magic), sizeof(textStats[2])); + CopyUtf8(textStats[3], StrCat(selhero_heroInfo.dexterity), sizeof(textStats[3])); + CopyUtf8(textStats[4], StrCat(selhero_heroInfo.vitality), sizeof(textStats[4])); + CopyUtf8(textStats[5], StrCat(selhero_heroInfo.saveNumber), sizeof(textStats[5])); } UiArtTextButton *SELLIST_DIALOG_DELETE_BUTTON; diff --git a/Source/appfat.cpp b/Source/appfat.cpp index b9a1f7f7f..c29cea7c1 100644 --- a/Source/appfat.cpp +++ b/Source/appfat.cpp @@ -13,6 +13,7 @@ #include "storm/storm_net.hpp" #include "utils/language.h" #include "utils/sdl_thread.h" +#include "utils/str_cat.hpp" #include "utils/ui_fwd.h" namespace devilution { @@ -55,7 +56,7 @@ void app_fatal(string_view str) #ifdef _DEBUG void assert_fail(int nLineNo, const char *pszFile, const char *pszFail) { - app_fatal(fmt::format("assertion failed ({}:{})\n{}", pszFile, nLineNo, pszFail)); + app_fatal(StrCat("assertion failed (", pszFile, ":", nLineNo, ")\n", pszFail)); } #endif diff --git a/Source/control.cpp b/Source/control.cpp index 5f6e56a70..733f8238c 100644 --- a/Source/control.cpp +++ b/Source/control.cpp @@ -46,6 +46,7 @@ #include "utils/language.h" #include "utils/sdl_geometry.h" #include "utils/stdcompat/optional.hpp" +#include "utils/str_cat.hpp" #include "utils/string_or_view.hpp" #include "utils/utf8.hpp" @@ -528,10 +529,10 @@ void DrawFlaskValues(const Surface &out, Point pos, int currValue, int maxValue) DrawString(out, text, pos, color | UiFlags::KerningFitSpacing, 0); }; - std::string currText = fmt::format("{:d}", currValue); + std::string currText = StrCat(currValue); drawStringWithShadow(currText, pos - Displacement { GetLineWidth(currText, GameFont12) + 1, 0 }); drawStringWithShadow("/", pos); - drawStringWithShadow(fmt::format("{:d}", maxValue), pos + Displacement { GetLineWidth("/", GameFont12) + 1, 0 }); + drawStringWithShadow(StrCat(maxValue), pos + Displacement { GetLineWidth("/", GameFont12) + 1, 0 }); } void control_update_life_mana() @@ -1078,9 +1079,9 @@ void DrawGoldSplit(const Surface &out, int amount) // for the text entered by the player. DrawString(out, wrapped, { GetPanelPosition(UiPanels::Inventory, { dialogX + 31, 75 }), { 200, 50 } }, UiFlags::ColorWhitegold | UiFlags::AlignCenter, 1, 17); - std::string value = ""; + std::string value; if (amount > 0) { - value = fmt::format("{:d}", amount); + value = StrCat(amount); } // Even a ten digit amount of gold only takes up about half a line. There's no need to wrap or clip text here so we // use the Point form of DrawString. diff --git a/Source/controls/plrctrls.cpp b/Source/controls/plrctrls.cpp index bee41436c..93117b083 100644 --- a/Source/controls/plrctrls.cpp +++ b/Source/controls/plrctrls.cpp @@ -36,6 +36,7 @@ #include "towners.h" #include "track.h" #include "utils/log.hpp" +#include "utils/str_cat.hpp" #define SPLICONLENGTH 56 @@ -1469,7 +1470,7 @@ void LogControlDeviceAndModeChange(ControlTypes newControlDevice, ControlTypes n constexpr auto DebugChange = [](ControlTypes before, ControlTypes after) -> std::string { if (before == after) return std::string { ControlTypeToString(before) }; - return fmt::format("{} -> {}", ControlTypeToString(before), ControlTypeToString(after)); + return StrCat(ControlTypeToString(before), " -> ", ControlTypeToString(after)); }; LogVerbose("Control: device {}, mode {}", DebugChange(ControlDevice, newControlDevice), DebugChange(ControlMode, newControlMode)); } diff --git a/Source/debug.cpp b/Source/debug.cpp index f6709bd0e..75f3e419c 100644 --- a/Source/debug.cpp +++ b/Source/debug.cpp @@ -9,9 +9,6 @@ #include #include -#include -#include - #include "debug.h" #include "automap.h" @@ -31,6 +28,7 @@ #include "towners.h" #include "utils/language.h" #include "utils/log.hpp" +#include "utils/str_cat.hpp" namespace devilution { @@ -97,16 +95,11 @@ void PrintDebugMonster(int m) { auto &monster = Monsters[m]; - EventPlrMsg(fmt::format( - "Monster {:d} = {:s}\nX = {:d}, Y = {:d}\nEnemy = {:d}, HP = {:d}\nMode = {:d}, Var1 = {:d}", - m, - monster.name, - monster.position.tile.x, - monster.position.tile.y, - monster.enemy, - monster.hitPoints, - static_cast(monster.mode), - monster.var1), + EventPlrMsg(StrCat( + "Monster ", m, " = ", monster.name, + "\nX = ", monster.position.tile.x, ", Y = ", monster.position.tile.y, + "\nEnemy = ", monster.enemy, ", HP = ", monster.hitPoints, + "\nMode = ", static_cast(monster.mode), ", Var1 = ", monster.var1), UiFlags::ColorWhite); bool bActive = false; @@ -116,7 +109,7 @@ void PrintDebugMonster(int m) bActive = true; } - EventPlrMsg(fmt::format("Active List = {:d}, Squelch = {:d}", bActive ? 1 : 0, monster.activeForTicks), UiFlags::ColorWhite); + EventPlrMsg(StrCat("Active List = ", bActive ? 1 : 0, ", Squelch = ", monster.activeForTicks), UiFlags::ColorWhite); } void ProcessMessages() @@ -155,15 +148,14 @@ std::string DebugCmdHelp(const string_view parameter) ret.append(std::string(dbgCmd.text)); } return ret; - } else { - auto debugCmdIterator = std::find_if(DebugCmdList.begin(), DebugCmdList.end(), [&](const DebugCmdItem &elem) { return elem.text == parameter; }); - if (debugCmdIterator == DebugCmdList.end()) - return fmt::format("Debug command {} wasn't found", parameter); - auto &dbgCmdItem = *debugCmdIterator; - if (dbgCmdItem.requiredParameter.empty()) - return fmt::format("Description: {}\nParameters: No additional parameter needed.", dbgCmdItem.description); - return fmt::format("Description: {}\nParameters: {}", dbgCmdItem.description, dbgCmdItem.requiredParameter); } + auto debugCmdIterator = std::find_if(DebugCmdList.begin(), DebugCmdList.end(), [&](const DebugCmdItem &elem) { return elem.text == parameter; }); + if (debugCmdIterator == DebugCmdList.end()) + return StrCat("Debug command ", parameter, " wasn't found"); + auto &dbgCmdItem = *debugCmdIterator; + if (dbgCmdItem.requiredParameter.empty()) + return StrCat("Description: ", dbgCmdItem.description, "\nParameters: No additional parameter needed."); + return StrCat("Description: ", dbgCmdItem.description, "\nParameters: ", dbgCmdItem.requiredParameter); } std::string DebugCmdGiveGoldCheat(const string_view parameter) @@ -211,12 +203,12 @@ std::string DebugCmdWarpToLevel(const string_view parameter) Player &myPlayer = *MyPlayer; auto level = atoi(parameter.data()); if (level < 0 || level > (gbIsHellfire ? 24 : 16)) - return fmt::format("Level {} is not known. Do you want to write a mod?", level); + return StrCat("Level ", level, " is not known. Do you want to write a mod?"); if (!setlevel && myPlayer.isOnLevel(level)) - return fmt::format("I did nothing but fulfilled your wish. You are already at level {}.", level); + return StrCat("I did nothing but fulfilled your wish. You are already at level ", level, "."); StartNewLvl(MyPlayerId, (level != 21) ? interface_mode::WM_DIABNEXTLVL : interface_mode::WM_DIABTOWNWARP, level); - return fmt::format("Welcome to level {}.", level); + return StrCat("Welcome to level ", level, "."); } std::string DebugCmdLoadQuestMap(const string_view parameter) @@ -226,16 +218,16 @@ std::string DebugCmdLoadQuestMap(const string_view parameter) for (auto &quest : Quests) { if (quest._qslvl <= 0) continue; - ret.append(fmt::format(" {} ({})", quest._qslvl, QuestLevelNames[quest._qslvl])); + StrAppend(ret, " ", quest._qslvl, " (", QuestLevelNames[quest._qslvl], ")"); } return ret; } auto level = atoi(parameter.data()); if (level < 1) - return fmt::format("Map id must be 1 or higher", level); + return "Map id must be 1 or higher"; if (setlevel && setlvlnum == level) - return fmt::format("I did nothing but fulfilled your wish. You are already at mapid {}.", level); + return StrCat("I did nothing but fulfilled your wish. You are already at mapid .", level); for (auto &quest : Quests) { if (level != quest._qslvl) @@ -249,10 +241,10 @@ std::string DebugCmdLoadQuestMap(const string_view parameter) setlvltype = quest._qlvltype; StartNewLvl(MyPlayerId, WM_DIABSETLVL, level); - return fmt::format("Welcome to {}.", QuestLevelNames[level]); + return StrCat("Welcome to ", QuestLevelNames[level], "."); } - return fmt::format("Mapid {} is not known. Do you want to write a mod?", level); + return StrCat("Mapid ", level, " is not known. Do you want to write a mod?"); } std::string DebugCmdLoadMap(const string_view parameter) @@ -266,7 +258,7 @@ std::string DebugCmdLoadMap(const string_view parameter) for (std::string tmp; std::getline(paramsStream, tmp, ' ');) { switch (count) { case 0: - TestMapPath = fmt::format("{:s}.dun", tmp); + TestMapPath = StrCat(tmp, ".dun"); break; case 1: mapType = atoi(tmp.c_str()); @@ -308,7 +300,7 @@ std::string ExportDun(const string_view parameter) { std::ofstream dunFile; - std::string levelName = fmt::format("{}-{}.dun", currlevel, glSeedTbl[currlevel]); + std::string levelName = StrCat(currlevel, "-", glSeedTbl[currlevel], ".dun"); dunFile.open(levelName, std::ios::out | std::ios::app | std::ios::binary); @@ -369,7 +361,7 @@ std::string ExportDun(const string_view parameter) } dunFile.close(); - return fmt::format("{} saved. Happy mapping!", levelName); + return StrCat(levelName, " saved. Happy mapping!"); } std::unordered_map TownerShortNameToTownerId = { @@ -405,7 +397,7 @@ std::string DebugCmdVisitTowner(const string_view parameter) auto it = TownerShortNameToTownerId.find(parameter); if (it == TownerShortNameToTownerId.end()) - return fmt::format("{} is unknown. Perhaps he is a ninja?", parameter); + return StrCat(parameter, " is unknown. Perhaps he is a ninja?"); for (auto &towner : Towners) { if (towner._ttype != it->second) @@ -420,10 +412,10 @@ std::string DebugCmdVisitTowner(const string_view parameter) towner.position.y, 1); - return fmt::format("Say hello to {} from me.", parameter); + return StrCat("Say hello to ", parameter, " from me."); } - return fmt::format("Couldn't find {}.", parameter); + return StrCat("Couldn't find ", parameter, "."); } std::string DebugCmdResetLevel(const string_view parameter) @@ -436,7 +428,7 @@ std::string DebugCmdResetLevel(const string_view parameter) return "What level do you want to visit?"; auto level = atoi(singleParameter.c_str()); if (level < 0 || level > (gbIsHellfire ? 24 : 16)) - return fmt::format("Level {} is not known. Do you want to write an extension mod?", level); + return StrCat("Level ", level, " is not known. Do you want to write an extension mod?"); myPlayer._pLvlVisited[level] = false; if (std::getline(paramsStream, singleParameter, ' ')) { @@ -445,8 +437,8 @@ std::string DebugCmdResetLevel(const string_view parameter) } if (myPlayer.isOnLevel(level)) - return fmt::format("Level {} can't be cleaned, cause you still occupy it!", level); - return fmt::format("Level {} was restored and looks fabulous.", level); + return StrCat("Level ", level, " can't be cleaned, cause you still occupy it!"); + return StrCat("Level ", level, " was restored and looks fabulous."); } std::string DebugCmdGodMode(const string_view parameter) @@ -498,7 +490,7 @@ std::string DebugCmdQuest(const string_view parameter) for (auto &quest : Quests) { if (IsNoneOf(quest._qactive, QUEST_NOTAVAIL, QUEST_INIT)) continue; - ret.append(fmt::format(", {} ({})", quest._qidx, QuestsData[quest._qidx]._qlstr)); + StrAppend(ret, ", ", quest._qidx, " (", QuestsData[quest._qidx]._qlstr, ")"); } return ret; } @@ -518,16 +510,16 @@ std::string DebugCmdQuest(const string_view parameter) int questId = atoi(parameter.data()); if (questId >= MAXQUESTS) - return fmt::format("Quest {} is not known. Do you want to write a mod?", questId); + return StrCat("Quest ", questId, " is not known. Do you want to write a mod?"); auto &quest = Quests[questId]; if (IsNoneOf(quest._qactive, QUEST_NOTAVAIL, QUEST_INIT)) - return fmt::format("{} was already given.", QuestsData[questId]._qlstr); + return StrCat(QuestsData[questId]._qlstr, " was already given."); quest._qactive = QUEST_ACTIVE; quest._qlog = true; - return fmt::format("{} enabled.", QuestsData[questId]._qlstr); + return StrCat(QuestsData[questId]._qlstr, " enabled."); } std::string DebugCmdLevelUp(const string_view parameter) @@ -572,14 +564,14 @@ std::string DebugCmdChangeHealth(const string_view parameter) change = atoi(parameter.data()); if (change == 0) - return fmt::format("Health hasn't changed."); + return "Health hasn't changed."; int newHealth = myPlayer._pHitPoints + (change * 64); SetPlayerHitPoints(myPlayer, newHealth); if (newHealth <= 0) SyncPlrKill(MyPlayerId, 0); - return fmt::format("Health has changed."); + return "Health has changed."; } std::string DebugCmdChangeMana(const string_view parameter) @@ -591,14 +583,14 @@ std::string DebugCmdChangeMana(const string_view parameter) change = atoi(parameter.data()); if (change == 0) - return fmt::format("Mana hasn't changed."); + return "Mana hasn't changed."; int newMana = myPlayer._pMana + (change * 64); myPlayer._pMana = newMana; myPlayer._pManaBase = myPlayer._pMana + myPlayer._pMaxManaBase - myPlayer._pMaxMana; drawmanaflag = true; - return fmt::format("Mana has changed."); + return "Mana has changed."; } std::string DebugCmdGenerateUniqueItem(const string_view parameter) @@ -659,7 +651,7 @@ std::string DebugCmdShowGrid(const string_view parameter) std::string DebugCmdLevelSeed(const string_view parameter) { - return fmt::format("Seedinfo for level {}\nseed: {}\nMid1: {}\nMid2: {}\nMid3: {}\nEnd: {}", currlevel, glSeedTbl[currlevel], glMid1Seed[currlevel], glMid2Seed[currlevel], glMid3Seed[currlevel], glEndSeed[currlevel]); + return StrCat("Seedinfo for level ", currlevel, "\nseed: ", glSeedTbl[currlevel], "\nMid1: ", glMid1Seed[currlevel], "\nMid2: ", glMid2Seed[currlevel], "\nMid3: ", glMid3Seed[currlevel], "\nEnd: ", glEndSeed[currlevel]); } std::string DebugCmdSpawnUniqueMonster(const string_view parameter) @@ -732,7 +724,7 @@ std::string DebugCmdSpawnUniqueMonster(const string_view parameter) Monster *monster = AddMonster(pos, myPlayer._pdir, id, true); if (monster == nullptr) - return fmt::format("I could only summon {} Monsters. The rest strike for shorter working hours.", spawnedMonster); + return StrCat("I could only summon ", spawnedMonster, " Monsters. The rest strike for shorter working hours."); PrepareUniqueMonst(*monster, uniqueIndex, 0, 0, UniqueMonstersData[uniqueIndex]); ActiveMonsterCount--; monster->corpseId = 1; @@ -745,7 +737,7 @@ std::string DebugCmdSpawnUniqueMonster(const string_view parameter) }); if (!ret) - ret = fmt::format("I could only summon {} Monsters. The rest strike for shorter working hours.", spawnedMonster); + ret = StrCat("I could only summon ", spawnedMonster, " Monsters. The rest strike for shorter working hours."); return *ret; } @@ -817,7 +809,7 @@ std::string DebugCmdSpawnMonster(const string_view parameter) return {}; if (AddMonster(pos, myPlayer._pdir, id, true) == nullptr) - return fmt::format("I could only summon {} Monsters. The rest strike for shorter working hours.", spawnedMonster); + return StrCat("I could only summon ", spawnedMonster, " Monsters. The rest strike for shorter working hours."); spawnedMonster += 1; if (spawnedMonster >= count) @@ -827,7 +819,7 @@ std::string DebugCmdSpawnMonster(const string_view parameter) }); if (!ret) - ret = fmt::format("I could only summon {} Monsters. The rest strike for shorter working hours.", spawnedMonster); + ret = StrCat("I could only summon ", spawnedMonster, " Monsters. The rest strike for shorter working hours."); return *ret; } @@ -913,9 +905,9 @@ std::string DebugCmdItemInfo(const string_view parameter) pItem = &Items[pcursitem]; } if (pItem != nullptr) { - return fmt::format("Name: {}\nIDidx: {}\nSeed: {}\nCreateInfo: {}", pItem->_iIName, pItem->IDidx, pItem->_iSeed, pItem->_iCreateInfo); + return StrCat("Name: ", pItem->_iIName, "\nIDidx: ", pItem->IDidx, "\nSeed: ", pItem->_iSeed, "\nCreateInfo: ", pItem->_iCreateInfo); } - return fmt::format("Numitems: {}", ActiveItemCount); + return StrCat("Numitems: ", ActiveItemCount); } std::string DebugCmdQuestInfo(const string_view parameter) @@ -925,7 +917,7 @@ std::string DebugCmdQuestInfo(const string_view parameter) for (auto &quest : Quests) { if (IsNoneOf(quest._qactive, QUEST_NOTAVAIL, QUEST_INIT)) continue; - ret.append(fmt::format(" {} ({})", quest._qidx, QuestsData[quest._qidx]._qlstr)); + StrAppend(ret, ", ", quest._qidx, " (", QuestsData[quest._qidx]._qlstr, ")"); } return ret; } @@ -933,9 +925,9 @@ std::string DebugCmdQuestInfo(const string_view parameter) int questId = atoi(parameter.data()); if (questId >= MAXQUESTS) - return fmt::format("Quest {} is not known. Do you want to write a mod?", questId); + return StrCat("Quest ", questId, " is not known. Do you want to write a mod?"); auto &quest = Quests[questId]; - return fmt::format("\nQuest: {}\nActive: {} Var1: {} Var2: {}", QuestsData[quest._qidx]._qlstr, quest._qactive, quest._qvar1, quest._qvar2); + return StrCat("\nQuest: ", QuestsData[quest._qidx]._qlstr, "\nActive: ", quest._qactive, " Var1: ", quest._qvar1, " Var2: ", quest._qvar2); } std::string DebugCmdPlayerInfo(const string_view parameter) @@ -948,12 +940,11 @@ std::string DebugCmdPlayerInfo(const string_view parameter) return "Player is not active"; const Point target = player.GetTargetPosition(); - return fmt::format("Plr {} is {}\nLvl: {} Changing: {}\nTile.x: {} Tile.y: {} Target.x: {} Target.y: {}\nMode: {} destAction: {} walkpath[0]: {}\nInvincible:{} HitPoints:{}", - playerId, player._pName, - player.plrlevel, player._pLvlChanging, - player.position.tile.x, player.position.tile.y, target.x, target.y, - player._pmode, player.destAction, player.walkpath[0], - player._pInvincible ? 1 : 0, player._pHitPoints); + return StrCat("Plr ", playerId, " is ", player._pName, + "\nLvl: ", player.plrlevel, " Changing: ", player._pLvlChanging, + "\nTile.x: ", player.position.tile.x, " Tile.y: ", player.position.tile.y, " Target.x: ", target.x, " Target.y: ", target.y, + "\nMode: ", player._pmode, " destAction: ", player.destAction, " walkpath[0]: ", player.walkpath[0], + "\nInvincible:", player._pInvincible ? 1 : 0, " HitPoints:", player._pHitPoints); } std::string DebugCmdToggleFPS(const string_view parameter) @@ -1032,7 +1023,7 @@ void NextDebugMonster() if (DebugMonsterId == MaxMonsters) DebugMonsterId = 0; - EventPlrMsg(fmt::format("Current debug monster = {:d}", DebugMonsterId), UiFlags::ColorWhite); + EventPlrMsg(StrCat("Current debug monster = ", DebugMonsterId), UiFlags::ColorWhite); } void SetDebugLevelSeedInfos(uint32_t mid1Seed, uint32_t mid2Seed, uint32_t mid3Seed, uint32_t endSeed) @@ -1084,12 +1075,12 @@ bool GetDebugGridText(Point dungeonCoords, char *debugGridTextBuffer) Point megaCoords = dungeonCoords.worldToMega(); switch (SelectedDebugGridTextItem) { case DebugGridTextItem::coords: - *fmt::format_to(debugGridTextBuffer, FMT_COMPILE("{}:{}"), dungeonCoords.x, dungeonCoords.y) = '\0'; + *BufCopy(debugGridTextBuffer, dungeonCoords.x, ":", dungeonCoords.y) = '\0'; return true; case DebugGridTextItem::cursorcoords: if (dungeonCoords != cursPosition) return false; - *fmt::format_to(debugGridTextBuffer, FMT_COMPILE("{}:{}"), dungeonCoords.x, dungeonCoords.y) = '\0'; + *BufCopy(debugGridTextBuffer, dungeonCoords.x, ":", dungeonCoords.y) = '\0'; return true; case DebugGridTextItem::objectindex: { info = 0; @@ -1158,7 +1149,7 @@ bool GetDebugGridText(Point dungeonCoords, char *debugGridTextBuffer) } if (info == 0) return false; - *fmt::format_to(debugGridTextBuffer, FMT_COMPILE("{}"), info) = '\0'; + *BufCopy(debugGridTextBuffer, info) = '\0'; return true; } diff --git a/Source/discord/discord.cpp b/Source/discord/discord.cpp index 38dde97c5..cffb3b85a 100644 --- a/Source/discord/discord.cpp +++ b/Source/discord/discord.cpp @@ -19,6 +19,7 @@ #include "panels/charpanel.hpp" #include "player.h" #include "utils/language.h" +#include "utils/str_cat.hpp" namespace devilution { namespace discord_manager { @@ -92,7 +93,7 @@ std::string GetCharacterString() std::string GetDetailString() { - return fmt::format("{} - {}", GetCharacterString(), GetLocationString()); + return StrCat(GetCharacterString(), " - ", GetLocationString()); } std::string GetStateString() @@ -104,7 +105,7 @@ std::string GetStateString() std::string GetTooltipString() { - return fmt::format("{} - {}", MyPlayer->_pName, GetCharacterString()); + return StrCat(MyPlayer->_pName, " - ", GetCharacterString()); } std::string GetPlayerAssetString() diff --git a/Source/effects.cpp b/Source/effects.cpp index e3fdc5bf0..e230ec1b7 100644 --- a/Source/effects.cpp +++ b/Source/effects.cpp @@ -5,14 +5,13 @@ */ #include "effects.h" -#include - #include "engine/random.hpp" #include "engine/sound.h" #include "engine/sound_defs.hpp" #include "init.h" #include "player.h" #include "utils/stdcompat/algorithm.hpp" +#include "utils/str_cat.hpp" namespace devilution { @@ -1222,7 +1221,7 @@ void InitMonsterSND(int monst) if (MonstSndChar[i] != 's' || MonstersData[mtype].snd_special) { for (int j = 0; j < 2; j++) { char path[MAX_PATH]; - *fmt::format_to(path, FMT_COMPILE("{}{}{}.WAV"), MonstersData[mtype].sndfile, MonstSndChar[i], j + 1) = '\0'; + *BufCopy(path, MonstersData[mtype].sndfile, string_view(&MonstSndChar[i], 1), j + 1, ".WAV") = '\0'; LevelMonsterTypes[monst].sounds[i][j] = sound_file_load(path); } } diff --git a/Source/engine/load_file.hpp b/Source/engine/load_file.hpp index db9987e39..4cbf97d17 100644 --- a/Source/engine/load_file.hpp +++ b/Source/engine/load_file.hpp @@ -12,6 +12,7 @@ #include "engine/assets.hpp" #include "utils/static_vector.hpp" #include "utils/stdcompat/cstddef.hpp" +#include "utils/str_cat.hpp" namespace devilution { @@ -22,7 +23,7 @@ public: handle_ = OpenAsset(path); if (handle_ == nullptr) { if (!gbQuietMode) { - app_fatal(fmt::format("Failed to open file:\n{}\n\n{}", path, SDL_GetError())); + app_fatal(StrCat("Failed to open file:\n", path, "\n\n", SDL_GetError())); } } } @@ -60,7 +61,7 @@ void LoadFileInMem(const char *path, T *data) return; const std::size_t fileLen = file.Size(); if ((fileLen % sizeof(T)) != 0) - app_fatal(fmt::format("File size does not align with type\n{}", path)); + app_fatal(StrCat("File size does not align with type\n", path)); file.Read(reinterpret_cast(data), fileLen); } @@ -93,7 +94,7 @@ std::unique_ptr LoadFileInMem(const char *path, std::size_t *numRead = null return nullptr; const std::size_t fileLen = file.Size(); if ((fileLen % sizeof(T)) != 0) - app_fatal(fmt::format("File size does not align with type\n{}", path)); + app_fatal(StrCat("File size does not align with type\n", path)); if (numRead != nullptr) *numRead = fileLen / sizeof(T); diff --git a/Source/engine/render/scrollrt.cpp b/Source/engine/render/scrollrt.cpp index 77f51db80..c316a4593 100644 --- a/Source/engine/render/scrollrt.cpp +++ b/Source/engine/render/scrollrt.cpp @@ -5,8 +5,6 @@ */ #include "engine/render/scrollrt.h" -#include - #include "DiabloUI/ui_flags.hpp" #include "automap.h" #include "controls/plrctrls.h" @@ -43,6 +41,7 @@ #include "utils/display.h" #include "utils/endian.hpp" #include "utils/log.hpp" +#include "utils/str_cat.hpp" #ifdef _DEBUG #include "debug.h" @@ -1326,7 +1325,7 @@ void DrawFPS(const Surface &out) framesSinceLastUpdate = 0; static char buf[12] {}; - const char *end = fmt::format_to_n(buf, sizeof(buf), FMT_COMPILE("{} FPS"), framerate).out; + const char *end = BufCopy(buf, framerate, " FPS"); formatted = { buf, static_cast(end - buf) }; }; DrawString(out, formatted, Point { 8, 68 }, UiFlags::ColorRed); diff --git a/Source/engine/sound.cpp b/Source/engine/sound.cpp index d024455fe..2596a9bfe 100644 --- a/Source/engine/sound.cpp +++ b/Source/engine/sound.cpp @@ -21,6 +21,7 @@ #include "utils/stdcompat/algorithm.hpp" #include "utils/stdcompat/optional.hpp" #include "utils/stdcompat/shared_ptr_array.hpp" +#include "utils/str_cat.hpp" #include "utils/stubs.h" namespace devilution { @@ -76,7 +77,7 @@ bool LoadAudioFile(const char *path, bool stream, bool errorDialog, SoundSample auto waveFile = MakeArraySharedPtr(dwBytes); if (SDL_RWread(file, waveFile.get(), dwBytes, 1) == 0) { if (errorDialog) - ErrDlg("Failed to read file", fmt::format("{}: {}", path, SDL_GetError()), __FILE__, __LINE__); + ErrDlg("Failed to read file", StrCat(path, ": ", SDL_GetError()), __FILE__, __LINE__); return false; } int error = result.SetChunk(waveFile, dwBytes, isMp3); diff --git a/Source/inv.cpp b/Source/inv.cpp index df93373c0..18b4c763d 100644 --- a/Source/inv.cpp +++ b/Source/inv.cpp @@ -30,6 +30,7 @@ #include "utils/language.h" #include "utils/sdl_geometry.h" #include "utils/stdcompat/optional.hpp" +#include "utils/str_cat.hpp" #include "utils/utf8.hpp" namespace devilution { @@ -1219,7 +1220,7 @@ void DrawInvBelt(const Surface &out) if (AllItemsList[myPlayer.SpdList[i].IDidx].iUsable && myPlayer.SpdList[i]._itype != ItemType::Gold) { - DrawString(out, fmt::format("{:d}", i + 1), { position - Displacement { 0, 12 }, InventorySlotSizeInPixels }, UiFlags::ColorWhite | UiFlags::AlignRight); + DrawString(out, StrCat(i + 1), { position - Displacement { 0, 12 }, InventorySlotSizeInPixels }, UiFlags::ColorWhite | UiFlags::AlignRight); } } } @@ -1353,7 +1354,7 @@ bool AutoPlaceItemInInventory(Player &player, const Item &item, bool persistItem return false; } - app_fatal(fmt::format("Unknown item size: {}x{}", itemSize.width, itemSize.height)); + app_fatal(StrCat("Unknown item size: ", itemSize.width, "x", itemSize.height)); } bool AutoPlaceItemInInventorySlot(Player &player, int slotIndex, const Item &item, bool persistItem) diff --git a/Source/items.cpp b/Source/items.cpp index 7fa0e78d4..84728dee4 100644 --- a/Source/items.cpp +++ b/Source/items.cpp @@ -42,6 +42,7 @@ #include "utils/language.h" #include "utils/math.h" #include "utils/stdcompat/algorithm.hpp" +#include "utils/str_cat.hpp" #include "utils/utf8.hpp" namespace devilution { @@ -2297,7 +2298,7 @@ void InitItemGFX() int itemTypes = gbIsHellfire ? ITEMTYPES : 35; for (int i = 0; i < itemTypes; i++) { - *fmt::format_to(arglist, FMT_COMPILE(R"(Items\{}.CEL)"), ItemDropNames[i]) = '\0'; + *BufCopy(arglist, "Items\\", ItemDropNames[i], ".CEL") = '\0'; itemanims[i] = LoadCel(arglist, ItemAnimWidth); } memset(UniqueItemFlags, 0, sizeof(UniqueItemFlags)); @@ -4461,10 +4462,10 @@ std::string DebugSpawnItem(std::string itemName) std::uniform_int_distribution dist(0, INT_MAX); SetRndSeed(dist(BetterRng)); if (SDL_GetTicks() - begin > max_time) - return fmt::format("Item not found in {:d} seconds!", max_time / 1000); + return StrCat("Item not found in ", max_time / 1000, " seconds!"); if (i > max_iter) - return fmt::format("Item not found in {:d} tries!", max_iter); + return StrCat("Item not found in ", max_iter, " tries!"); fake_m.level = dist(BetterRng) % CF_LEVEL + 1; @@ -4487,7 +4488,7 @@ std::string DebugSpawnItem(std::string itemName) item._iIdentified = true; NetSendCmdPItem(false, CMD_DROPITEM, item.position, item); - return fmt::format("Item generated successfully - iterations: {:d}", i); + return StrCat("Item generated successfully - iterations: ", i); } std::string DebugSpawnUniqueItem(std::string itemName) @@ -4535,10 +4536,10 @@ std::string DebugSpawnUniqueItem(std::string itemName) int i = 0; for (uint32_t begin = SDL_GetTicks();; i++) { if (SDL_GetTicks() - begin > max_time) - return fmt::format("Item not found in {:d} seconds!", max_time / 1000); + return StrCat("Item not found in ", max_time / 1000, " seconds!"); if (i > max_iter) - return fmt::format("Item not found in {:d} tries!", max_iter); + return StrCat("Item not found in ", max_iter, " tries!"); Point bkp = item.position; item = {}; @@ -4564,7 +4565,7 @@ std::string DebugSpawnUniqueItem(std::string itemName) item._iIdentified = true; NetSendCmdPItem(false, CMD_DROPITEM, item.position, item); - return fmt::format("Item generated successfully - iterations: {:d}", i); + return StrCat("Item generated successfully - iterations: ", i); } #endif diff --git a/Source/levels/themes.cpp b/Source/levels/themes.cpp index a1e1a0f1a..dc5237d93 100644 --- a/Source/levels/themes.cpp +++ b/Source/levels/themes.cpp @@ -15,6 +15,7 @@ #include "monster.h" #include "objects.h" #include "quests.h" +#include "utils/str_cat.hpp" namespace devilution { @@ -1008,7 +1009,7 @@ void CreateThemeRooms() Theme_WeaponRack(i); break; case THEME_NONE: - app_fatal(fmt::format("Unknown theme type: {}", static_cast(themes[i].ttype))); + app_fatal(StrCat("Unknown theme type: ", static_cast(themes[i].ttype))); } } ApplyObjectLighting = false; diff --git a/Source/missiles.cpp b/Source/missiles.cpp index b9f763963..ccc88ffd0 100644 --- a/Source/missiles.cpp +++ b/Source/missiles.cpp @@ -23,6 +23,7 @@ #include "lighting.h" #include "monster.h" #include "spells.h" +#include "utils/str_cat.hpp" namespace devilution { @@ -2668,7 +2669,7 @@ void MI_LArrow(Missile &missile) eRst = MISR_FIRE; break; default: - app_fatal(fmt::format("wrong missile ID {}", static_cast(missile._mitype))); + app_fatal(StrCat("wrong missile ID ", static_cast(missile._mitype))); break; } SetMissAnim(missile, eAnim); diff --git a/Source/monster.cpp b/Source/monster.cpp index 038332849..87e1565c2 100644 --- a/Source/monster.cpp +++ b/Source/monster.cpp @@ -37,6 +37,7 @@ #include "utils/file_name_generator.hpp" #include "utils/language.h" #include "utils/stdcompat/string_view.hpp" +#include "utils/str_cat.hpp" #include "utils/utf8.hpp" #ifdef _DEBUG @@ -1614,7 +1615,7 @@ void MonsterTalk(Monster &monster) monster.flags |= MFLAG_QUEST_COMPLETE; } if (Quests[Q_LTBANNER]._qvar1 < 2) { - app_fatal(fmt::format("SS Talk = {}, Flags = {}", monster.talkMsg, monster.flags)); + app_fatal(StrCat("SS Talk = ", monster.talkMsg, ", Flags = ", monster.flags)); } } if (monster.uniqType - 1 == UMT_LACHDAN) { @@ -3282,7 +3283,7 @@ string_view GetMonsterTypeText(const MonsterData &monsterData) return _("Undead"); } - app_fatal(fmt::format("Unknown mMonstClass {}", static_cast(monsterData.mMonstClass))); + app_fatal(StrCat("Unknown mMonstClass ", static_cast(monsterData.mMonstClass))); } void ActivateSpawn(Monster &monster, Point position, Direction dir) @@ -3421,7 +3422,7 @@ bool UpdateModeStance(int monsterId) void InitTRNForUniqueMonster(Monster &monster) { char filestr[64]; - *fmt::format_to(filestr, FMT_COMPILE(R"(Monsters\Monsters\{}.TRN)"), UniqueMonstersData[monster.uniqType - 1].mTrnName) = '\0'; + *BufCopy(filestr, R"(Monsters\Monsters\)", UniqueMonstersData[monster.uniqType - 1].mTrnName, ".TRN") = '\0'; monster.uniqueMonsterTRN = LoadFileInMem(filestr); } diff --git a/Source/mpq/mpq_writer.cpp b/Source/mpq/mpq_writer.cpp index f9e8f7f92..7bfb9ac42 100644 --- a/Source/mpq/mpq_writer.cpp +++ b/Source/mpq/mpq_writer.cpp @@ -15,6 +15,7 @@ #include "utils/file_util.h" #include "utils/language.h" #include "utils/log.hpp" +#include "utils/str_cat.hpp" namespace devilution { @@ -336,7 +337,7 @@ MpqBlockEntry *MpqWriter::AddFile(const char *filename, MpqBlockEntry *block, ui uint32_t h2 = Hash(filename, 1); uint32_t h3 = Hash(filename, 2); if (GetHashIndex(h1, h2, h3) != HashEntryNotFound) - app_fatal(fmt::format("Hash collision between \"{}\" and existing file\n", filename)); + app_fatal(StrCat("Hash collision between \"", filename, "\" and existing file\n")); unsigned int hIdx = h1 & 0x7FF; bool hasSpace = false; diff --git a/Source/msg.cpp b/Source/msg.cpp index a92a1b01e..43e304314 100644 --- a/Source/msg.cpp +++ b/Source/msg.cpp @@ -36,6 +36,7 @@ #include "tmsg.h" #include "towners.h" #include "utils/language.h" +#include "utils/str_cat.hpp" #include "utils/utf8.hpp" namespace devilution { @@ -446,7 +447,7 @@ void DeltaImportData(_cmd_id cmd, DWORD recvOffset) src += DeltaImportObject(src, deltaLevel.object); DeltaImportMonster(src, deltaLevel.monster); } else { - app_fatal(fmt::format("Unkown network message type: {}", cmd)); + app_fatal(StrCat("Unkown network message type: ", cmd)); } sgbDeltaChunks++; diff --git a/Source/multi.cpp b/Source/multi.cpp index 95ec60507..d40974b89 100644 --- a/Source/multi.cpp +++ b/Source/multi.cpp @@ -28,6 +28,7 @@ #include "utils/language.h" #include "utils/stdcompat/cstddef.hpp" #include "utils/stdcompat/string_view.hpp" +#include "utils/str_cat.hpp" namespace devilution { @@ -372,7 +373,7 @@ void HandleEvents(_SNETEVENT *pEvt) case EVENT_TYPE_PLAYER_CREATE_GAME: { auto *gameData = (GameData *)pEvt->data; if (gameData->size != sizeof(GameData)) - app_fatal(fmt::format("Invalid size of game data: {}", gameData->size)); + app_fatal(StrCat("Invalid size of game data: ", gameData->size)); sgGameInitInfo = *gameData; sgbPlayerTurnBitTbl[pEvt->playerid] = true; break; @@ -405,7 +406,7 @@ void EventHandler(bool add) for (auto eventType : EventTypes) { if (add) { if (!SNetRegisterEventHandler(eventType, HandleEvents)) { - app_fatal(fmt::format("SNetRegisterEventHandler:\n{}", SDL_GetError())); + app_fatal(StrCat("SNetRegisterEventHandler:\n", SDL_GetError())); } } else { SNetUnregisterEventHandler(eventType); @@ -421,7 +422,7 @@ bool InitSingle(GameData *gameData) int unused = 0; if (!SNetCreateGame("local", "local", (char *)&sgGameInitInfo, sizeof(sgGameInitInfo), &unused)) { - app_fatal(fmt::format("SNetCreateGame1:\n{}", SDL_GetError())); + app_fatal(StrCat("SNetCreateGame1:\n", SDL_GetError())); } MyPlayerId = 0; diff --git a/Source/nthread.cpp b/Source/nthread.cpp index 18a119ba0..c85aa0a33 100644 --- a/Source/nthread.cpp +++ b/Source/nthread.cpp @@ -13,6 +13,7 @@ #include "storm/storm_net.hpp" #include "utils/sdl_mutex.h" #include "utils/sdl_thread.h" +#include "utils/str_cat.hpp" namespace devilution { @@ -70,7 +71,7 @@ void nthread_terminate_game(const char *pszFcn) return; } if (sErr != STORM_ERROR_GAME_TERMINATED && sErr != STORM_ERROR_NOT_IN_GAME) { - app_fatal(fmt::format("{}:\n{}", pszFcn, SDL_GetError())); + app_fatal(StrCat(pszFcn, ":\n", pszFcn)); } gbGameDestroyed = true; diff --git a/Source/objects.cpp b/Source/objects.cpp index 15a3884fe..b76a8314f 100644 --- a/Source/objects.cpp +++ b/Source/objects.cpp @@ -7,7 +7,7 @@ #include #include -#include +#include #include "DiabloUI/ui_flags.hpp" #include "automap.h" @@ -36,6 +36,7 @@ #include "track.h" #include "utils/language.h" #include "utils/log.hpp" +#include "utils/str_cat.hpp" #include "utils/utf8.hpp" namespace devilution { @@ -4087,7 +4088,7 @@ void LoadLevelObjects(bool filesLoaded[65]) ObjFileList[numobjfiles] = static_cast(i); char filestr[32]; - *fmt::format_to(filestr, FMT_COMPILE(R"(Objects\{}.CEL)"), ObjMasterLoadList[i]) = '\0'; + *BufCopy(filestr, "Objects\\", ObjMasterLoadList[i], ".CEL") = '\0'; pObjCels[numobjfiles] = LoadFileInMem(filestr); numobjfiles++; } diff --git a/Source/options.cpp b/Source/options.cpp index a122ef51b..0b14bf3c7 100644 --- a/Source/options.cpp +++ b/Source/options.cpp @@ -28,6 +28,7 @@ #include "utils/log.hpp" #include "utils/paths.h" #include "utils/stdcompat/algorithm.hpp" +#include "utils/str_cat.hpp" #include "utils/utf8.hpp" namespace devilution { @@ -515,7 +516,7 @@ string_view OptionEntryIntBase::GetListDescription(size_t index) const { if (entryNames.empty()) { for (auto value : entryValues) { - entryNames.push_back(fmt::format("{}", value)); + entryNames.push_back(StrCat(value)); } } return entryNames[index].data(); @@ -724,7 +725,7 @@ void OptionEntryResolution::CheckResolutionsAreInitialized() const sizes.erase(std::unique(sizes.begin(), sizes.end()), sizes.end()); for (auto &size : sizes) { - resolutions.emplace_back(size, fmt::format("{}x{}", size.width, size.height)); + resolutions.emplace_back(size, StrCat(size.width, "x", size.height)); } } @@ -1221,7 +1222,7 @@ KeymapperOptions::KeymapperOptions() keyIDToKeyName.emplace(c, std::string(1, c)); } for (int i = 0; i < 12; ++i) { - keyIDToKeyName.emplace(DVL_VK_F1 + i, fmt::format("F{}", i + 1)); + keyIDToKeyName.emplace(DVL_VK_F1 + i, StrCat("F", i + 1)); } keyIDToKeyName.emplace(DVL_VK_LMENU, "LALT"); diff --git a/Source/panels/charpanel.cpp b/Source/panels/charpanel.cpp index b8b64e4d9..85581debd 100644 --- a/Source/panels/charpanel.cpp +++ b/Source/panels/charpanel.cpp @@ -14,6 +14,7 @@ #include "utils/display.h" #include "utils/format_int.hpp" #include "utils/language.h" +#include "utils/str_cat.hpp" namespace devilution { @@ -108,7 +109,7 @@ StyledText GetResistInfo(int8_t resist) else if (resist >= MaxResistance) style = UiFlags::ColorWhitegold; - return { style, fmt::format("{:d}%", resist) }; + return { style, StrCat(resist, "%") }; } constexpr int LeftColumnLabelX = 88; @@ -129,11 +130,11 @@ PanelEntry panelEntries[] = { []() { return StyledText { UiFlags::ColorWhite, std::string(_(ClassStrTbl[static_cast(MyPlayer->_pClass)])) }; } }, { N_("Level"), { 57, 52 }, 57, 45, - []() { return StyledText { UiFlags::ColorWhite, fmt::format("{:d}", MyPlayer->_pLevel) }; } }, + []() { return StyledText { UiFlags::ColorWhite, StrCat(MyPlayer->_pLevel) }; } }, { N_("Experience"), { TopRightLabelX, 52 }, 99, 91, []() { int spacing = ((MyPlayer->_pExperience >= 1000000000) ? 0 : 1); - return StyledText { UiFlags::ColorWhite, fmt::format("{:s}", FormatInteger(MyPlayer->_pExperience)), spacing }; + return StyledText { UiFlags::ColorWhite, FormatInteger(MyPlayer->_pExperience), spacing }; } }, { N_("Next level"), { TopRightLabelX, 80 }, 99, 198, []() { @@ -141,54 +142,54 @@ PanelEntry panelEntries[] = { return StyledText { UiFlags::ColorWhitegold, std::string(_("None")) }; } int spacing = ((MyPlayer->_pNextExper >= 1000000000) ? 0 : 1); - return StyledText { UiFlags::ColorWhite, fmt::format("{:s}", FormatInteger(MyPlayer->_pNextExper)), spacing }; + return StyledText { UiFlags::ColorWhite, FormatInteger(MyPlayer->_pNextExper), spacing }; } }, { N_("Base"), { LeftColumnLabelX, /* set dynamically */ 0 }, 0, 44 }, { N_("Now"), { 135, /* set dynamically */ 0 }, 0, 44 }, { N_("Strength"), { LeftColumnLabelX, 135 }, 45, LeftColumnLabelWidth, - []() { return StyledText { GetBaseStatColor(CharacterAttribute::Strength), fmt::format("{:d}", MyPlayer->_pBaseStr) }; } }, + []() { return StyledText { GetBaseStatColor(CharacterAttribute::Strength), StrCat(MyPlayer->_pBaseStr) }; } }, { "", { 135, 135 }, 45, 0, - []() { return StyledText { GetCurrentStatColor(CharacterAttribute::Strength), fmt::format("{:d}", MyPlayer->_pStrength) }; } }, + []() { return StyledText { GetCurrentStatColor(CharacterAttribute::Strength), StrCat(MyPlayer->_pStrength) }; } }, { N_("Magic"), { LeftColumnLabelX, 163 }, 45, LeftColumnLabelWidth, - []() { return StyledText { GetBaseStatColor(CharacterAttribute::Magic), fmt::format("{:d}", MyPlayer->_pBaseMag) }; } }, + []() { return StyledText { GetBaseStatColor(CharacterAttribute::Magic), StrCat(MyPlayer->_pBaseMag) }; } }, { "", { 135, 163 }, 45, 0, - []() { return StyledText { GetCurrentStatColor(CharacterAttribute::Magic), fmt::format("{:d}", MyPlayer->_pMagic) }; } }, - { N_("Dexterity"), { LeftColumnLabelX, 191 }, 45, LeftColumnLabelWidth, []() { return StyledText { GetBaseStatColor(CharacterAttribute::Dexterity), fmt::format("{:d}", MyPlayer->_pBaseDex) }; } }, + []() { return StyledText { GetCurrentStatColor(CharacterAttribute::Magic), StrCat(MyPlayer->_pMagic) }; } }, + { N_("Dexterity"), { LeftColumnLabelX, 191 }, 45, LeftColumnLabelWidth, []() { return StyledText { GetBaseStatColor(CharacterAttribute::Dexterity), StrCat(MyPlayer->_pBaseDex) }; } }, { "", { 135, 191 }, 45, 0, - []() { return StyledText { GetCurrentStatColor(CharacterAttribute::Dexterity), fmt::format("{:d}", MyPlayer->_pDexterity) }; } }, - { N_("Vitality"), { LeftColumnLabelX, 219 }, 45, LeftColumnLabelWidth, []() { return StyledText { GetBaseStatColor(CharacterAttribute::Vitality), fmt::format("{:d}", MyPlayer->_pBaseVit) }; } }, + []() { return StyledText { GetCurrentStatColor(CharacterAttribute::Dexterity), StrCat(MyPlayer->_pDexterity) }; } }, + { N_("Vitality"), { LeftColumnLabelX, 219 }, 45, LeftColumnLabelWidth, []() { return StyledText { GetBaseStatColor(CharacterAttribute::Vitality), StrCat(MyPlayer->_pBaseVit) }; } }, { "", { 135, 219 }, 45, 0, - []() { return StyledText { GetCurrentStatColor(CharacterAttribute::Vitality), fmt::format("{:d}", MyPlayer->_pVitality) }; } }, + []() { return StyledText { GetCurrentStatColor(CharacterAttribute::Vitality), StrCat(MyPlayer->_pVitality) }; } }, { N_("Points to distribute"), { LeftColumnLabelX, 248 }, 45, LeftColumnLabelWidth, []() { MyPlayer->_pStatPts = std::min(CalcStatDiff(*MyPlayer), MyPlayer->_pStatPts); - return StyledText { UiFlags::ColorRed, (MyPlayer->_pStatPts > 0 ? fmt::format("{:d}", MyPlayer->_pStatPts) : "") }; + return StyledText { UiFlags::ColorRed, (MyPlayer->_pStatPts > 0 ? StrCat(MyPlayer->_pStatPts) : "") }; } }, { N_("Gold"), { TopRightLabelX, /* set dynamically */ 0 }, 0, 98 }, { "", { TopRightLabelX, 127 }, 99, 0, - []() { return StyledText { UiFlags::ColorWhite, fmt::format("{:s}", FormatInteger(MyPlayer->_pGold)) }; } }, + []() { return StyledText { UiFlags::ColorWhite, FormatInteger(MyPlayer->_pGold) }; } }, { N_("Armor class"), { RightColumnLabelX, 163 }, 57, RightColumnLabelWidth, - []() { return StyledText { GetValueColor(MyPlayer->_pIBonusAC), fmt::format("{:d}", MyPlayer->GetArmor()) }; } }, + []() { return StyledText { GetValueColor(MyPlayer->_pIBonusAC), StrCat(MyPlayer->GetArmor()) }; } }, { N_("To hit"), { RightColumnLabelX, 191 }, 57, RightColumnLabelWidth, - []() { return StyledText { GetValueColor(MyPlayer->_pIBonusToHit), fmt::format("{:d}%", (MyPlayer->InvBody[INVLOC_HAND_LEFT]._itype == ItemType::Bow ? MyPlayer->GetRangedToHit() : MyPlayer->GetMeleeToHit())) }; } }, + []() { return StyledText { GetValueColor(MyPlayer->_pIBonusToHit), StrCat(MyPlayer->InvBody[INVLOC_HAND_LEFT]._itype == ItemType::Bow ? MyPlayer->GetRangedToHit() : MyPlayer->GetMeleeToHit(), "%") }; } }, { N_("Damage"), { RightColumnLabelX, 219 }, 57, RightColumnLabelWidth, []() { std::pair dmg = GetDamage(); int spacing = ((dmg.first >= 100) ? -1 : 1); - return StyledText { GetValueColor(MyPlayer->_pIBonusDam), fmt::format("{:d}-{:d}", dmg.first, dmg.second), spacing }; + return StyledText { GetValueColor(MyPlayer->_pIBonusDam), StrCat(dmg.first, "-", dmg.second), spacing }; } }, { N_("Life"), { LeftColumnLabelX, 284 }, 45, LeftColumnLabelWidth, - []() { return StyledText { GetMaxHealthColor(), fmt::format("{:d}", MyPlayer->_pMaxHP >> 6) }; } }, + []() { return StyledText { GetMaxHealthColor(), StrCat(MyPlayer->_pMaxHP >> 6) }; } }, { "", { 135, 284 }, 45, 0, - []() { return StyledText { (MyPlayer->_pHitPoints != MyPlayer->_pMaxHP ? UiFlags::ColorRed : GetMaxHealthColor()), fmt::format("{:d}", MyPlayer->_pHitPoints >> 6) }; } }, + []() { return StyledText { (MyPlayer->_pHitPoints != MyPlayer->_pMaxHP ? UiFlags::ColorRed : GetMaxHealthColor()), StrCat(MyPlayer->_pHitPoints >> 6) }; } }, { N_("Mana"), { LeftColumnLabelX, 312 }, 45, LeftColumnLabelWidth, - []() { return StyledText { GetMaxManaColor(), fmt::format("{:d}", MyPlayer->_pMaxMana >> 6) }; } }, + []() { return StyledText { GetMaxManaColor(), StrCat(MyPlayer->_pMaxMana >> 6) }; } }, { "", { 135, 312 }, 45, 0, - []() { return StyledText { (MyPlayer->_pMana != MyPlayer->_pMaxMana ? UiFlags::ColorRed : GetMaxManaColor()), fmt::format("{:d}", MyPlayer->_pMana >> 6) }; } }, + []() { return StyledText { (MyPlayer->_pMana != MyPlayer->_pMaxMana ? UiFlags::ColorRed : GetMaxManaColor()), StrCat(MyPlayer->_pMana >> 6) }; } }, { N_("Resist magic"), { RightColumnLabelX, 256 }, 57, RightColumnLabelWidth, []() { return GetResistInfo(MyPlayer->_pMagResist); } }, diff --git a/Source/panels/spell_list.cpp b/Source/panels/spell_list.cpp index ff7cfdd3a..07b7e9706 100644 --- a/Source/panels/spell_list.cpp +++ b/Source/panels/spell_list.cpp @@ -12,6 +12,7 @@ #include "player.h" #include "spells.h" #include "utils/language.h" +#include "utils/str_cat.hpp" #include "utils/utf8.hpp" #define SPLROWICONLS 10 @@ -88,7 +89,7 @@ std::optional GetHotkeyName(spell_id spellId, spell_type spellType) for (size_t t = 0; t < NumHotkeys; t++) { if (myPlayer._pSplHotKey[t] != spellId || myPlayer._pSplTHotKey[t] != spellType) continue; - auto quickSpellActionKey = fmt::format("QuickSpell{}", t + 1); + auto quickSpellActionKey = StrCat("QuickSpell", t + 1); return sgOptions.Keymapper.KeyNameForAction(quickSpellActionKey); } return {}; diff --git a/Source/pfile.cpp b/Source/pfile.cpp index 95b5a6bfb..9569ae891 100644 --- a/Source/pfile.cpp +++ b/Source/pfile.cpp @@ -21,6 +21,8 @@ #include "utils/file_util.h" #include "utils/language.h" #include "utils/paths.h" +#include "utils/stdcompat/string_view.hpp" +#include "utils/str_cat.hpp" #include "utils/utf8.hpp" namespace devilution { @@ -37,50 +39,20 @@ namespace { /** List of character names for the character selection screen. */ char hero_names[MAX_CHARACTERS][PlayerNameLength]; -std::string GetSavePath(uint32_t saveNum, std::string savePrefix = "") +std::string GetSavePath(uint32_t saveNum, string_view savePrefix = {}) { - std::string path = paths::PrefPath(); - const char *ext = ".sv"; - if (gbIsHellfire) - ext = ".hsv"; - - path.append(savePrefix); - - if (gbIsSpawn) { - if (!gbIsMultiplayer) { - path.append("spawn_"); - } else { - path.append("share_"); - } - } else { - if (!gbIsMultiplayer) { - path.append("single_"); - } else { - path.append("multi_"); - } - } - - char saveNumStr[21]; - *fmt::format_to(saveNumStr, FMT_COMPILE("{}"), saveNum) = '\0'; - path.append(saveNumStr); - path.append(ext); - return path; + return StrCat(paths::PrefPath(), savePrefix, + gbIsSpawn + ? (gbIsMultiplayer ? "share_" : "spawn_") + : (gbIsMultiplayer ? "multi_" : "single_"), + saveNum, gbIsHellfire ? ".hsv" : ".sv"); } std::string GetStashSavePath() { - std::string path = paths::PrefPath(); - const char *ext = ".sv"; - if (gbIsHellfire) - ext = ".hsv"; - - if (gbIsSpawn) { - path.append("stash_spawn"); - } else { - path.append("stash"); - } - path.append(ext); - return path; + return StrCat(paths::PrefPath(), + gbIsSpawn ? "stash_spawn" : "stash", + gbIsHellfire ? ".hsv" : ".sv"); } bool GetSaveNames(uint8_t index, string_view prefix, char *out) @@ -316,19 +288,19 @@ void pfile_write_hero(bool writeGameData) void pfile_write_hero_demo(int demo) { - std::string savePath = GetSavePath(gSaveNumber, fmt::format("demo_{}_reference_", demo)); + std::string savePath = GetSavePath(gSaveNumber, StrCat("demo_", demo, "_reference_")); auto saveWriter = MpqWriter(savePath.c_str()); pfile_write_hero(saveWriter, true); } HeroCompareResult pfile_compare_hero_demo(int demo) { - std::string referenceSavePath = GetSavePath(gSaveNumber, fmt::format("demo_{}_reference_", demo)); + std::string referenceSavePath = GetSavePath(gSaveNumber, StrCat("demo_", demo, "_reference_")); if (!FileExists(referenceSavePath.c_str())) return HeroCompareResult::ReferenceNotFound; - std::string actualSavePath = GetSavePath(gSaveNumber, fmt::format("demo_{}_actual_", demo)); + std::string actualSavePath = GetSavePath(gSaveNumber, StrCat("demo_", demo, "_actual_")); { MpqWriter saveWriter(actualSavePath.c_str()); pfile_write_hero(saveWriter, true); diff --git a/Source/platform/ctr/messagebox.cpp b/Source/platform/ctr/messagebox.cpp index a0c2a84cc..b8b0515eb 100644 --- a/Source/platform/ctr/messagebox.cpp +++ b/Source/platform/ctr/messagebox.cpp @@ -1,8 +1,8 @@ #include <3ds.h> #include -#include #include "utils/sdl2_to_1_2_backports.h" +#include "utils/str_cat.hpp" int SDL_ShowSimpleMessageBox(Uint32 flags, const char *title, @@ -13,7 +13,7 @@ int SDL_ShowSimpleMessageBox(Uint32 flags, SDL_Log("%s", SDL_GetError()); bool init = !gspHasGpuRight(); - auto text = fmt::format("{}\n\n{}", title, message); + auto text = devilution::StrCat(title, "\n\n", message); if (init) gfxInitDefault(); diff --git a/Source/player.cpp b/Source/player.cpp index 72c6184af..0fc944584 100644 --- a/Source/player.cpp +++ b/Source/player.cpp @@ -38,6 +38,7 @@ #include "towners.h" #include "utils/language.h" #include "utils/log.hpp" +#include "utils/str_cat.hpp" #include "utils/utf8.hpp" namespace devilution { @@ -433,7 +434,7 @@ void ChangeOffset(Player &player) void StartAttack(int pnum, Direction d) { if ((DWORD)pnum >= MAX_PLRS) { - app_fatal(fmt::format("StartAttack: illegal player {}", pnum)); + app_fatal(StrCat("StartAttack: illegal player ", pnum)); } Player &player = Players[pnum]; @@ -465,7 +466,7 @@ void StartAttack(int pnum, Direction d) void StartRangeAttack(int pnum, Direction d, WorldTileCoord cx, WorldTileCoord cy) { if ((DWORD)pnum >= MAX_PLRS) { - app_fatal(fmt::format("StartRangeAttack: illegal player {}", pnum)); + app_fatal(StrCat("StartRangeAttack: illegal player ", pnum)); } Player &player = Players[pnum]; @@ -507,7 +508,7 @@ player_graphic GetPlayerGraphicForSpell(spell_id spellId) void StartSpell(int pnum, Direction d, WorldTileCoord cx, WorldTileCoord cy) { if ((DWORD)pnum >= MAX_PLRS) - app_fatal(fmt::format("StartSpell: illegal player {}", pnum)); + app_fatal(StrCat("StartSpell: illegal player ", pnum)); Player &player = Players[pnum]; if (player._pInvincible && player._pHitPoints == 0 && pnum == MyPlayerId) { @@ -649,7 +650,7 @@ void InitLevelChange(int pnum) bool DoWalk(int pnum, int variant) { if ((DWORD)pnum >= MAX_PLRS) { - app_fatal(fmt::format("PM_DoWalk: illegal player {}", pnum)); + app_fatal(StrCat("PM_DoWalk: illegal player ", pnum)); } Player &player = Players[pnum]; @@ -802,12 +803,12 @@ bool PlrHitMonst(int pnum, int monsterId, bool adjacentDamage = false) int hper = 0; if ((DWORD)monsterId >= MaxMonsters) { - app_fatal(fmt::format("PlrHitMonst: illegal monster {}", monsterId)); + app_fatal(StrCat("PlrHitMonst: illegal monster ", monsterId)); } auto &monster = Monsters[monsterId]; if ((DWORD)pnum >= MAX_PLRS) { - app_fatal(fmt::format("PlrHitMonst: illegal player {}", pnum)); + app_fatal(StrCat("PlrHitMonst: illegal player ", pnum)); } Player &player = Players[pnum]; @@ -984,7 +985,7 @@ bool PlrHitMonst(int pnum, int monsterId, bool adjacentDamage = false) bool PlrHitPlr(Player &attacker, int8_t p) { if ((DWORD)p >= MAX_PLRS) { - app_fatal(fmt::format("PlrHitPlr: illegal target player {}", p)); + app_fatal(StrCat("PlrHitPlr: illegal target player ", p)); } Player &target = Players[p]; @@ -1064,7 +1065,7 @@ bool PlrHitObj(int pnum, Object &targetObject) bool DoAttack(int pnum) { if ((DWORD)pnum >= MAX_PLRS) { - app_fatal(fmt::format("PM_DoAttack: illegal player {}", pnum)); + app_fatal(StrCat("PM_DoAttack: illegal player ", pnum)); } Player &player = Players[pnum]; @@ -1174,7 +1175,7 @@ bool DoAttack(int pnum) bool DoRangeAttack(int pnum) { if ((DWORD)pnum >= MAX_PLRS) { - app_fatal(fmt::format("PM_DoRangeAttack: illegal player {}", pnum)); + app_fatal(StrCat("PM_DoRangeAttack: illegal player ", pnum)); } Player &player = Players[pnum]; @@ -1274,7 +1275,7 @@ void DamageParryItem(Player &player) bool DoBlock(int pnum) { if ((DWORD)pnum >= MAX_PLRS) { - app_fatal(fmt::format("PM_DoBlock: illegal player {}", pnum)); + app_fatal(StrCat("PM_DoBlock: illegal player ", pnum)); } Player &player = Players[pnum]; @@ -1335,7 +1336,7 @@ void DamageArmor(Player &player) bool DoSpell(int pnum) { if ((DWORD)pnum >= MAX_PLRS) { - app_fatal(fmt::format("PM_DoSpell: illegal player {}", pnum)); + app_fatal(StrCat("PM_DoSpell: illegal player ", pnum)); } Player &player = Players[pnum]; @@ -1366,7 +1367,7 @@ bool DoSpell(int pnum) bool DoGotHit(int pnum) { if ((DWORD)pnum >= MAX_PLRS) { - app_fatal(fmt::format("PM_DoGotHit: illegal player {}", pnum)); + app_fatal(StrCat("PM_DoGotHit: illegal player ", pnum)); } Player &player = Players[pnum]; @@ -1437,7 +1438,7 @@ void TryDisarm(const Player &player, Object &object) void CheckNewPath(int pnum, bool pmWillBeCalled) { if ((DWORD)pnum >= MAX_PLRS) { - app_fatal(fmt::format("CheckNewPath: illegal player {}", pnum)); + app_fatal(StrCat("CheckNewPath: illegal player ", pnum)); } Player &player = Players[pnum]; @@ -1775,7 +1776,7 @@ bool PlrDeathModeOK(Player &player) void ValidatePlayer() { if ((DWORD)MyPlayerId >= MAX_PLRS) { - app_fatal(fmt::format("ValidatePlayer: illegal player {}", MyPlayerId)); + app_fatal(StrCat("ValidatePlayer: illegal player ", MyPlayerId)); } Player &myPlayer = *MyPlayer; @@ -2845,7 +2846,7 @@ void InitPlayer(Player &player, bool firstTime) void InitMultiView() { if ((DWORD)MyPlayerId >= MAX_PLRS) { - app_fatal(fmt::format("InitPlayer: illegal player {}", MyPlayerId)); + app_fatal(StrCat("InitPlayer: illegal player ", MyPlayerId)); } Player &myPlayer = *MyPlayer; @@ -2899,7 +2900,7 @@ void FixPlayerLocation(Player &player, Direction bDir) void StartStand(int pnum, Direction dir) { if ((DWORD)pnum >= MAX_PLRS) { - app_fatal(fmt::format("StartStand: illegal player {}", pnum)); + app_fatal(StrCat("StartStand: illegal player ", pnum)); } Player &player = Players[pnum]; @@ -2919,7 +2920,7 @@ void StartStand(int pnum, Direction dir) void StartPlrBlock(int pnum, Direction dir) { if ((DWORD)pnum >= MAX_PLRS) { - app_fatal(fmt::format("StartPlrBlock: illegal player {}", pnum)); + app_fatal(StrCat("StartPlrBlock: illegal player ", pnum)); } Player &player = Players[pnum]; @@ -2954,7 +2955,7 @@ void FixPlrWalkTags(const Player &player) void StartPlrHit(int pnum, int dam, bool forcehit) { if ((DWORD)pnum >= MAX_PLRS) { - app_fatal(fmt::format("StartPlrHit: illegal player {}", pnum)); + app_fatal(StrCat("StartPlrHit: illegal player ", pnum)); } Player &player = Players[pnum]; @@ -3006,7 +3007,7 @@ void StartPlayerKill(int pnum, int earflag) { if ((DWORD)pnum >= MAX_PLRS) { - app_fatal(fmt::format("StartPlayerKill: illegal player {}", pnum)); + app_fatal(StrCat("StartPlayerKill: illegal player ", pnum)); } Player &player = Players[pnum]; @@ -3214,7 +3215,7 @@ StartNewLvl(int pnum, interface_mode fom, int lvl) InitLevelChange(pnum); if ((DWORD)pnum >= MAX_PLRS) { - app_fatal(fmt::format("StartNewLvl: illegal player {}", pnum)); + app_fatal(StrCat("StartNewLvl: illegal player ", pnum)); } Player &player = Players[pnum]; Player &myPlayer = *MyPlayer; @@ -3254,7 +3255,7 @@ void RestartTownLvl(int pnum) { InitLevelChange(pnum); if ((DWORD)pnum >= MAX_PLRS) { - app_fatal(fmt::format("RestartTownLvl: illegal player {}", pnum)); + app_fatal(StrCat("RestartTownLvl: illegal player ", pnum)); } Player &player = Players[pnum]; @@ -3303,7 +3304,7 @@ void StartWarpLvl(int pnum, int pidx) void ProcessPlayers() { if ((DWORD)MyPlayerId >= MAX_PLRS) { - app_fatal(fmt::format("ProcessPlayers: illegal player {}", MyPlayerId)); + app_fatal(StrCat("ProcessPlayers: illegal player ", MyPlayerId)); } Player &myPlayer = *MyPlayer; @@ -3472,7 +3473,7 @@ void CheckPlrSpell(bool isShiftHeld, spell_id spellID, spell_type spellType) bool addflag = false; if ((DWORD)MyPlayerId >= MAX_PLRS) { - app_fatal(fmt::format("CheckPlrSpell: illegal player {}", MyPlayerId)); + app_fatal(StrCat("CheckPlrSpell: illegal player ", MyPlayerId)); } Player &myPlayer = *MyPlayer; @@ -3648,7 +3649,7 @@ void SyncInitPlrPos(int pnum) void SyncInitPlr(int pnum) { if ((DWORD)pnum >= MAX_PLRS) { - app_fatal(fmt::format("SyncInitPlr: illegal player {}", pnum)); + app_fatal(StrCat("SyncInitPlr: illegal player ", pnum)); } Player &player = Players[pnum]; @@ -3829,7 +3830,7 @@ enum { void PlayDungMsgs() { if ((DWORD)MyPlayerId >= MAX_PLRS) { - app_fatal(fmt::format("PlayDungMsgs: illegal player {}", MyPlayerId)); + app_fatal(StrCat("PlayDungMsgs: illegal player ", MyPlayerId)); } Player &myPlayer = *MyPlayer; diff --git a/Source/qol/monhealthbar.cpp b/Source/qol/monhealthbar.cpp index c3318f099..4490b8345 100644 --- a/Source/qol/monhealthbar.cpp +++ b/Source/qol/monhealthbar.cpp @@ -11,6 +11,7 @@ #include "cursor.h" #include "options.h" #include "utils/language.h" +#include "utils/str_cat.hpp" namespace devilution { namespace { @@ -117,7 +118,7 @@ void DrawMonsterHealthBar(const Surface &out) return 150; default: - app_fatal(fmt::format("Invalid monster class: {}", static_cast(monsterClass))); + app_fatal(StrCat("Invalid monster class: ", static_cast(monsterClass))); } }; @@ -142,7 +143,7 @@ void DrawMonsterHealthBar(const Surface &out) DrawString(out, monster.name, { position, { width, height } }, style); if (multiplier > 0) - DrawString(out, fmt::format("x{:d}", multiplier), { position, { width - 2, height } }, UiFlags::ColorWhite | UiFlags::AlignRight | UiFlags::VerticalCenter); + DrawString(out, StrCat("x", multiplier), { position, { width - 2, height } }, UiFlags::ColorWhite | UiFlags::AlignRight | UiFlags::VerticalCenter); if (monster.uniqType != 0 || MonsterKillCounts[monster.type().type] >= 15) { monster_resistance immunes[] = { IMMUNE_MAGIC, IMMUNE_FIRE, IMMUNE_LIGHTNING }; monster_resistance resists[] = { RESIST_MAGIC, RESIST_FIRE, RESIST_LIGHTNING }; diff --git a/Source/qol/stash.cpp b/Source/qol/stash.cpp index e3c4354cd..28dec9e58 100644 --- a/Source/qol/stash.cpp +++ b/Source/qol/stash.cpp @@ -18,6 +18,7 @@ #include "stores.h" #include "utils/format_int.hpp" #include "utils/language.h" +#include "utils/str_cat.hpp" #include "utils/utf8.hpp" namespace devilution { @@ -382,8 +383,8 @@ void DrawStash(const Surface &out) Point position = GetPanelPosition(UiPanels::Stash); UiFlags style = UiFlags::VerticalCenter | UiFlags::ColorWhite; - DrawString(out, fmt::format("{:d}", Stash.GetPage() + 1), { position + Displacement { 132, 0 }, { 57, 11 } }, UiFlags::AlignCenter | style); - DrawString(out, fmt::format("{:s}", FormatInteger(Stash.gold)), { position + Displacement { 122, 19 }, { 107, 13 } }, UiFlags::AlignRight | style); + DrawString(out, StrCat(Stash.GetPage() + 1), { position + Displacement { 132, 0 }, { 57, 11 } }, UiFlags::AlignCenter | style); + DrawString(out, FormatInteger(Stash.gold), { position + Displacement { 122, 19 }, { 107, 13 } }, UiFlags::AlignRight | style); } void CheckStashItem(Point mousePosition, bool isShiftHeld, bool isCtrlHeld) @@ -631,7 +632,7 @@ void DrawGoldWithdraw(const Surface &out, int amount) std::string value = ""; if (amount > 0) { - value = fmt::format("{:d}", amount); + value = StrCat(amount); } // Even a ten digit amount of gold only takes up about half a line. There's no need to wrap or clip text here so we // use the Point form of DrawString. diff --git a/Source/stores.cpp b/Source/stores.cpp index f3fb71446..efccb5809 100644 --- a/Source/stores.cpp +++ b/Source/stores.cpp @@ -24,6 +24,7 @@ #include "utils/format_int.hpp" #include "utils/language.h" #include "utils/stdcompat/string_view.hpp" +#include "utils/str_cat.hpp" #include "utils/utf8.hpp" namespace devilution { @@ -1022,7 +1023,7 @@ void StoreConfirm(Item &item) prompt = _("Are you sure you want to repair this item?"); break; default: - app_fatal(fmt::format("Unknown store dialog {}", static_cast(stextshold))); + app_fatal(StrCat("Unknown store dialog ", static_cast(stextshold))); } AddSText(0, 15, prompt, UiFlags::ColorWhite | UiFlags::AlignCenter, false); AddSText(0, 18, _("Yes"), UiFlags::ColorWhite | UiFlags::AlignCenter, true); @@ -2274,7 +2275,7 @@ void PrintSString(const Surface &out, int margin, int line, string_view text, Ui const Rectangle rect { { sx, sy }, { width, 0 } }; DrawString(out, text, rect, flags); if (price > 0) - DrawString(out, fmt::format("{:s}", FormatInteger(price)), rect, flags | UiFlags::AlignRight); + DrawString(out, FormatInteger(price), rect, flags | UiFlags::AlignRight); if (stextsel == line) { DrawSelector(out, rect, text, flags); diff --git a/Source/utils/display.cpp b/Source/utils/display.cpp index 783dd7f49..cb9ab1445 100644 --- a/Source/utils/display.cpp +++ b/Source/utils/display.cpp @@ -29,6 +29,7 @@ #include "utils/log.hpp" #include "utils/sdl_geometry.h" #include "utils/sdl_wrap.h" +#include "utils/str_cat.hpp" #ifdef USE_SDL1 #ifndef SDL1_VIDEO_MODE_BPP @@ -350,7 +351,7 @@ void ReinitializeTexture() if (renderer == nullptr) return; - auto quality = fmt::format("{}", static_cast(*sgOptions.Graphics.scaleQuality)); + auto quality = StrCat(static_cast(*sgOptions.Graphics.scaleQuality)); SDL_SetHint(SDL_HINT_RENDER_SCALE_QUALITY, quality.c_str()); texture = SDLWrap::CreateTexture(renderer, SDL_PIXELFORMAT_RGB888, SDL_TEXTUREACCESS_STREAMING, gnScreenWidth, gnScreenHeight); diff --git a/Source/utils/file_name_generator.hpp b/Source/utils/file_name_generator.hpp index 0cad6e374..59e5e6e41 100644 --- a/Source/utils/file_name_generator.hpp +++ b/Source/utils/file_name_generator.hpp @@ -3,9 +3,8 @@ #include #include -#include - #include "utils/stdcompat/string_view.hpp" +#include "utils/str_cat.hpp" namespace devilution { @@ -19,7 +18,7 @@ public: const char *operator()() const { - *Append(prefixEnd_, suffix_) = '\0'; + *BufCopy(prefixEnd_, suffix_) = '\0'; return buf_; } @@ -27,16 +26,10 @@ protected: static char *Append(char *buf, std::initializer_list strings) { for (string_view str : strings) - buf = Append(buf, str); + buf = BufCopy(buf, str); return buf; } - static char *Append(char *buf, string_view str) - { - memcpy(buf, str.data(), str.size()); - return buf + str.size(); - } - [[nodiscard]] string_view Suffix() const { return suffix_; @@ -76,7 +69,7 @@ public: const char *operator()(size_t i) const { - *Append(fmt::format_to(PrefixEnd(), "{}", static_cast(min_ + i)), Suffix()) = '\0'; + *BufCopy(PrefixEnd(), static_cast(min_ + i), Suffix()) = '\0'; return Buffer(); } @@ -97,7 +90,7 @@ public: : BaseFileNameGenerator(prefixes, suffix) , chars_(chars) { - *Append(PrefixEnd() + 1, Suffix()) = '\0'; + *BufCopy(PrefixEnd() + 1, Suffix()) = '\0'; } const char *operator()(size_t i) const diff --git a/Source/utils/format_int.cpp b/Source/utils/format_int.cpp index 68e98d8ad..1b6acba17 100644 --- a/Source/utils/format_int.cpp +++ b/Source/utils/format_int.cpp @@ -5,6 +5,7 @@ #include "utils/language.h" #include "utils/stdcompat/string_view.hpp" +#include "utils/str_cat.hpp" namespace devilution { @@ -14,7 +15,7 @@ std::string FormatInteger(int n) char buf[40]; char *begin = buf; - const char *end = fmt::format_to(buf, FMT_COMPILE("{}"), n); + const char *end = BufCopy(buf, n); const size_t len = end - begin; std::string out; diff --git a/Source/utils/log.hpp b/Source/utils/log.hpp index 23cc474c3..4958e31fa 100644 --- a/Source/utils/log.hpp +++ b/Source/utils/log.hpp @@ -5,6 +5,7 @@ #include #include "utils/stdcompat/string_view.hpp" +#include "utils/str_cat.hpp" #ifdef USE_SDL1 #include "sdl2_to_1_2_backports.h" @@ -52,7 +53,7 @@ std::string format(string_view fmt, Args &&...args) #if FMT_EXCEPTIONS // e.what() is undefined if exceptions are disabled, so we wrap the whole block // with an `FMT_EXCEPTIONS` check. - std::string error = fmt::format("Format error, fmt: {}, error: {}", fmt, e.what()); + std::string error = StrCat("Format error, fmt: ", fmt, " error: ", e.what()); SDL_LogCritical(SDL_LOG_CATEGORY_APPLICATION, "%s", error.c_str()); app_fatal(error); #endif diff --git a/Source/utils/str_cat.cpp b/Source/utils/str_cat.cpp new file mode 100644 index 000000000..f1d112a20 --- /dev/null +++ b/Source/utils/str_cat.cpp @@ -0,0 +1,28 @@ +#include "utils/str_cat.hpp" + +#include + +#include +#include + +namespace devilution { + +namespace { + +template +constexpr size_t MaxDecimalDigits = 241 * sizeof(T) / 100 + 1; + +} // namespace + +char *BufCopy(char *out, int value) +{ + return fmt::format_to(out, FMT_COMPILE("{}"), value); +} + +void StrAppend(std::string &out, int value) +{ + char buf[MaxDecimalDigits + 1]; + out.append(&buf[0], BufCopy(buf, value) - &buf[0]); +} + +} // namespace devilution diff --git a/Source/utils/str_cat.hpp b/Source/utils/str_cat.hpp new file mode 100644 index 000000000..a1e91fdb4 --- /dev/null +++ b/Source/utils/str_cat.hpp @@ -0,0 +1,82 @@ +#pragma once + +#include +#include +#include + +#include "utils/stdcompat/string_view.hpp" + +namespace devilution { + +/** + * @brief Writes the integer to the given buffer. + * @return char* end of the buffer + */ +char *BufCopy(char *out, int value); + +/** + * @brief Appends the integer to the given string. + */ +void StrAppend(std::string &out, int value); + +/** + * @brief Copies the given string_view to the given buffer. + */ +inline char *BufCopy(char *out, string_view value) +{ + std::memcpy(out, value.data(), value.size()); + return out + value.size(); +} + +/** + * @brief Copies the given string_view to the given string. + */ +inline void StrAppend(std::string &out, string_view value) +{ + out.append(value.data(), value.size()); +} + +/** + * @brief Appends the given C string to the given buffer. + * + * `str` must be a null-terminated C string, `out` will not be null terminated. + */ +inline char *BufCopy(char *out, const char *str) +{ + return BufCopy(out, string_view(str != nullptr ? str : "(nullptr)")); +} + +/** + * @brief Appends the given C string to the given string. + */ +inline void StrAppend(std::string &out, const char *str) +{ + if (str == nullptr) + out.append("(nullptr)"); + out.append(str, string_view(str).size()); +} + +template +inline typename std::enable_if<(sizeof...(Args) > 0), char *>::type +BufCopy(char *out, Arg &&arg, Args &&...args) +{ + return BufCopy(BufCopy(out, std::forward(arg)), std::forward(args)...); +} + +template +inline typename std::enable_if<(sizeof...(Args) > 0), void>::type +StrAppend(std::string &out, Arg &&arg, Args &&...args) +{ + StrAppend(out, std::forward(arg)); + StrAppend(out, std::forward(args)...); +} + +template +std::string StrCat(Args &&...args) +{ + std::string result; + StrAppend(result, std::forward(args)...); + return result; +} + +} // namespace devilution