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