Browse Source

Replace fmt with utils/str_cat in a few places

`fmt::format_to` is overkill for our simple formatting needs.
pull/8192/head
Gleb Mazovetskiy 6 months ago
parent
commit
3bcc869d85
  1. 2
      Source/CMakeLists.txt
  2. 6
      Source/capture.cpp
  3. 2
      Source/engine/assets.cpp
  4. 12
      Source/engine/demomode.cpp
  5. 10
      Source/engine/palette.cpp
  6. 2
      Source/items.cpp
  7. 3
      Source/loadsave.cpp
  8. 3
      Source/pfile.cpp
  9. 21
      Source/player.cpp
  10. 9
      Source/qol/chatlog.cpp
  11. 7
      Source/utils/sdl2_to_1_2_backports.cpp
  12. 12
      Source/utils/str_cat.cpp
  13. 46
      Source/utils/str_cat.hpp
  14. 7
      test/str_cat_test.cpp

2
Source/CMakeLists.txt

@ -797,8 +797,8 @@ if(USE_SDL1)
utils/sdl2_to_1_2_backports.cpp
)
target_link_dependencies(libdevilutionx_sdl2_to_1_2_backports PRIVATE
libdevilutionx_strings
libdevilutionx_utils_console
fmt::fmt
)
target_link_libraries(DevilutionX::SDL INTERFACE
libdevilutionx_sdl2_to_1_2_backports

6
Source/capture.cpp

@ -11,7 +11,6 @@
#include <SDL.h>
#include <expected.hpp>
#include <fmt/format.h>
#define DEVILUTIONX_SCREENSHOT_FORMAT_PCX 0
#define DEVILUTIONX_SCREENSHOT_FORMAT_PNG 1
@ -46,8 +45,9 @@ SDL_RWops *CaptureFile(std::string *dstPath)
const std::time_t tt = std::time(nullptr);
const std::tm *tm = std::localtime(&tt);
const std::string filename = tm != nullptr
? fmt::format("Screenshot from {:04}-{:02}-{:02} {:02}-{:02}-{:02}",
tm->tm_year + 1900, tm->tm_mon + 1, tm->tm_mday, tm->tm_hour, tm->tm_min, tm->tm_sec)
? StrCat("Screenshot from ",
LeftPad(tm->tm_year + 1900, 4, '0'), "-", LeftPad(tm->tm_mon + 1, 2, '0'), "-", LeftPad(tm->tm_mday, 2, '0'), "-",
LeftPad(tm->tm_hour, 2, '0'), "-", LeftPad(tm->tm_min, 2, '0'), "-", LeftPad(tm->tm_sec, 2, '0'))
: "Screenshot";
*dstPath = StrCat(paths::PrefPath(), filename, ext);
int i = 0;

2
Source/engine/assets.cpp

@ -367,7 +367,7 @@ std::vector<std::string> GetMPQSearchPaths()
std::string message;
for (std::size_t i = 0; i < paths.size(); ++i) {
message.append(fmt::format("\n{:6d}. '{}'", i + 1, paths[i]));
message.append(StrCat("\n", LeftPad(i + 1, 6, ' '), ". '", paths[i], "'"));
}
LogVerbose("MPQ search paths:{}", message);
}

12
Source/engine/demomode.cpp

@ -5,8 +5,6 @@
#include <limits>
#include <optional>
#include <fmt/format.h>
#ifdef USE_SDL1
#include "utils/sdl2_to_1_2_backports.h"
#endif
@ -180,7 +178,7 @@ void ReadSettings(FILE *in, uint8_t version) // NOLINT(readability-identifier-le
DemoSettings = {};
}
std::string message = fmt::format("\n{}={}x{}", _("Resolution"), DemoGraphicsWidth, DemoGraphicsHeight);
std::string message = StrCat("\n", _("Resolution"), "=", DemoGraphicsWidth, "x", DemoGraphicsHeight);
for (const auto &[key, value] : std::initializer_list<std::pair<std::string_view, bool>> {
{ _("Run in Town"), DemoSettings.runInTown },
{ _("Theo Quest"), DemoSettings.theoQuest },
@ -199,7 +197,7 @@ void ReadSettings(FILE *in, uint8_t version) // NOLINT(readability-identifier-le
{ _("Show Item Labels"), DemoSettings.showItemLabels },
{ _("Auto Refill Belt"), DemoSettings.autoRefillBelt },
{ _("Disable Crippling Shrines"), DemoSettings.disableCripplingShrines } }) {
fmt::format_to(std::back_inserter(message), "\n{}={:d}", key, value);
StrAppend(message, "\n", key, "=", value ? "1" : "0");
}
for (const auto &[key, value] : std::initializer_list<std::pair<std::string_view, uint8_t>> {
{ _("Heal Potion Pickup"), DemoSettings.numHealPotionPickup },
@ -208,7 +206,7 @@ void ReadSettings(FILE *in, uint8_t version) // NOLINT(readability-identifier-le
{ _("Full Mana Potion Pickup"), DemoSettings.numFullManaPotionPickup },
{ _("Rejuvenation Potion Pickup"), DemoSettings.numRejuPotionPickup },
{ _("Full Rejuvenation Potion Pickup"), DemoSettings.numFullRejuPotionPickup } }) {
fmt::format_to(std::back_inserter(message), "\n{}={}", key, value);
StrAppend(message, "\n", key, "=", static_cast<int>(value));
}
Log("{}", message);
}
@ -776,8 +774,8 @@ void RecordMessage(const SDL_Event &event, uint16_t modState)
|| event.wheel.x > std::numeric_limits<int16_t>::max()
|| event.wheel.y < std::numeric_limits<int16_t>::min()
|| event.wheel.y > std::numeric_limits<int16_t>::max()) {
app_fatal(fmt::format("Mouse wheel event x/y out of int16_t range. x={} y={}",
event.wheel.x, event.wheel.y));
app_fatal(StrCat("Mouse wheel event x/y out of int16_t range. x=",
event.wheel.x, " y=", event.wheel.y));
}
WriteLE16(DemoRecording, event.wheel.x);
WriteLE16(DemoRecording, event.wheel.y);

10
Source/engine/palette.cpp

@ -11,8 +11,6 @@
#include <cstring>
#include <span>
#include <fmt/core.h>
#include "engine/backbuffer_state.hpp"
#include "engine/demomode.h"
#include "engine/dx.h"
@ -24,6 +22,7 @@
#include "utils/display.h"
#include "utils/palette_blending.hpp"
#include "utils/sdl_compat.h"
#include "utils/str_cat.hpp"
namespace devilution {
@ -214,9 +213,12 @@ void LoadRndLvlPal(dungeon_type l)
if (!*GetOptions().Graphics.alternateNestArt) {
rv++;
}
*fmt::format_to(szFileName, R"(nlevels\l{0}data\l{0}base{1}.pal)", 6, rv) = '\0';
*BufCopy(szFileName, R"(nlevels\l6data\l6base)", rv, ".pal") = '\0';
} else {
*fmt::format_to(szFileName, R"(levels\l{0}data\l{0}_{1}.pal)", static_cast<int>(l), rv) = '\0';
char nbuf[3];
const char *end = BufCopy(nbuf, static_cast<int>(l));
const std::string_view n = std::string_view(nbuf, end - nbuf);
*BufCopy(szFileName, "levels\\l", n, "data\\l", n, "_", rv, ".pal") = '\0';
}
LoadPaletteAndInitBlending(szFileName);
}

2
Source/items.cpp

@ -3573,7 +3573,7 @@ void CornerstoneSave()
PackItem(id, CornerStone.item, (CornerStone.item.dwBuff & CF_HELLFIRE) != 0);
const auto *buffer = reinterpret_cast<uint8_t *>(&id);
for (size_t i = 0; i < sizeof(ItemPack); i++) {
fmt::format_to(&GetOptions().Hellfire.szItem[i * 2], "{:02X}", buffer[i]);
BufCopy(&GetOptions().Hellfire.szItem[i * 2], AsHexPad2(buffer[i], /*uppercase=*/true));
}
GetOptions().Hellfire.szItem[sizeof(GetOptions().Hellfire.szItem) - 1] = '\0';
} else {

3
Source/loadsave.cpp

@ -15,7 +15,6 @@
#include <SDL.h>
#include <ankerl/unordered_dense.h>
#include <expected.hpp>
#include <fmt/core.h>
#include "automap.h"
#include "codec.h"
@ -1049,7 +1048,7 @@ void GetLevelNames(std::string_view prefix, char *out)
suf = 'l';
num = currlevel;
}
*fmt::format_to(out, "{}{}{:02d}", prefix, suf, num) = '\0';
*BufCopy(out, prefix, std::string_view(&suf, 1), LeftPad(num, 2, '0')) = '\0';
}
void GetTempLevelNames(char *szTemp)

3
Source/pfile.cpp

@ -11,7 +11,6 @@
#include <ankerl/unordered_dense.h>
#include <expected.hpp>
#include <fmt/core.h>
#include "codec.h"
#include "engine/load_file.hpp"
@ -92,7 +91,7 @@ bool GetSaveNames(uint8_t index, std::string_view prefix, char *out)
return false;
}
*fmt::format_to(out, "{}{}{:02d}", prefix, suf, index) = '\0';
*BufCopy(out, prefix, std::string_view(&suf, 1), LeftPad(index, 2, '0')) = '\0';
return true;
}

21
Source/player.cpp

@ -1516,6 +1516,11 @@ uint16_t GetPlayerSpriteWidth(HeroClass cls, player_graphic graphic, PlayerWeapo
app_fatal("Invalid player_graphic");
}
void GetPlayerGraphicsPath(std::string_view path, std::string_view prefix, std::string_view type, char out[256])
{
*BufCopy(out, "plrgfx\\", path, "\\", prefix, "\\", prefix, type) = '\0';
}
} // namespace
void Player::CalcScrolls()
@ -2054,11 +2059,7 @@ ClxSprite GetPlayerPortraitSprite(Player &player)
const PlayerSpriteData &spriteData = GetPlayerSpriteDataForClass(cls);
const char *path = spriteData.classPath.c_str();
const char *szCel;
if (!inDungeon)
szCel = "st";
else
szCel = "as";
std::string_view szCel = inDungeon ? "as" : "st";
player_graphic graphic = player_graphic::Stand;
if (player._pHitPoints <= 0) {
@ -2068,9 +2069,9 @@ ClxSprite GetPlayerPortraitSprite(Player &player)
}
}
char prefix[3] = { spriteData.classChar, ArmourChar[player._pgfxnum >> 4], WepChar[static_cast<std::size_t>(animWeaponId)] };
const char prefixBuf[3] = { spriteData.classChar, ArmourChar[player._pgfxnum >> 4], WepChar[static_cast<std::size_t>(animWeaponId)] };
char pszName[256];
*fmt::format_to(pszName, R"(plrgfx\{0}\{1}\{1}{2})", path, std::string_view(prefix, 3), szCel) = 0;
GetPlayerGraphicsPath(path, std::string_view(prefixBuf, 3), szCel, pszName);
const std::string spritePath { pszName };
// Check to see if the sprite has updated.
@ -2110,7 +2111,7 @@ void LoadPlrGFX(Player &player, player_graphic graphic)
const PlayerSpriteData &spriteData = GetPlayerSpriteDataForClass(cls);
const char *path = spriteData.classPath.c_str();
const char *szCel;
std::string_view szCel;
switch (graphic) {
case player_graphic::Stand:
szCel = "as";
@ -2157,9 +2158,9 @@ void LoadPlrGFX(Player &player, player_graphic graphic)
app_fatal("PLR:2");
}
char prefix[3] = { spriteData.classChar, ArmourChar[player._pgfxnum >> 4], WepChar[static_cast<std::size_t>(animWeaponId)] };
const char prefixBuf[3] = { spriteData.classChar, ArmourChar[player._pgfxnum >> 4], WepChar[static_cast<std::size_t>(animWeaponId)] };
char pszName[256];
*fmt::format_to(pszName, R"(plrgfx\{0}\{1}\{1}{2})", path, std::string_view(prefix, 3), szCel) = 0;
GetPlayerGraphicsPath(path, std::string_view(prefixBuf, 3), szCel, pszName);
const uint16_t animationWidth = GetPlayerSpriteWidth(cls, graphic, animWeaponId);
animationData.sprites = LoadCl2Sheet(pszName, animationWidth);
std::optional<std::array<uint8_t, 256>> graphicTRN = GetPlayerGraphicTRN(pszName);

9
Source/qol/chatlog.cpp

@ -24,6 +24,7 @@
#include "minitext.h"
#include "stores.h"
#include "utils/language.h"
#include "utils/str_cat.hpp"
namespace devilution {
@ -119,8 +120,9 @@ void AddMessageToChatLog(std::string_view message, Player *player, UiFlags flags
MessageCounter++;
const time_t timeResult = time(nullptr);
const std::tm *localtimeResult = localtime(&timeResult);
std::string timestamp = localtimeResult != nullptr ? fmt::format("[#{:d}] {:02}:{:02}:{:02}", MessageCounter, localtimeResult->tm_hour, localtimeResult->tm_min, localtimeResult->tm_sec)
: fmt::format("[#{:d}] ", MessageCounter);
std::string timestamp = localtimeResult != nullptr
? StrCat("[#", MessageCounter, "] ", LeftPad(localtimeResult->tm_hour, 2, '0'), ":", LeftPad(localtimeResult->tm_min, 2, '0'), ":", LeftPad(localtimeResult->tm_sec, 2, '0'))
: StrCat("[#", MessageCounter, "] ");
const size_t oldSize = ChatLogLines.size();
if (player == nullptr) {
ChatLogLines.emplace_back(MultiColoredText { "{0} {1}", { { timestamp, UiFlags::ColorRed }, { std::string(message), flags } } });
@ -172,7 +174,8 @@ void DrawChatLog(const Surface &out)
const time_t timeResult = time(nullptr);
const std::tm *localtimeResult = localtime(&timeResult);
if (localtimeResult != nullptr) {
const std::string timestamp = fmt::format("{:02}:{:02}:{:02}", localtimeResult->tm_hour, localtimeResult->tm_min, localtimeResult->tm_sec);
const std::string timestamp = StrCat(
LeftPad(localtimeResult->tm_hour, 2, '0'), ":", LeftPad(localtimeResult->tm_min, 2, '0'), ":", LeftPad(localtimeResult->tm_sec, 2, '0'));
DrawString(out, timestamp, { { sx, sy + PaddingTop + blankLineHeight }, { ContentTextWidth, lineHeight } },
{ .flags = UiFlags::ColorWhitegold });
}

7
Source/utils/sdl2_to_1_2_backports.cpp

@ -15,9 +15,8 @@
#include <windows.h>
#endif
#include <fmt/format.h>
#include "utils/console.h"
#include "utils/str_cat.hpp"
#define DEFAULT_PRIORITY SDL_LOG_PRIORITY_CRITICAL
#define DEFAULT_ASSERT_PRIORITY SDL_LOG_PRIORITY_WARN
@ -919,9 +918,9 @@ extern "C" char *SDL_GetPrefPath(const char *org, const char *app)
}
if (*org) {
*fmt::format_to_n(retval, len - 1, "{}{}{}/{}", envr, append, org, app).out = '\0';
*devilution::BufCopy(retval, envr, append, org, "/", app) = '\0';
} else {
*fmt::format_to_n(retval, len - 1, "{}{}{}", envr, append, app).out = '\0';
*devilution::BufCopy(retval, envr, append, app) = '\0';
}
for (ptr = retval + 1; *ptr; ptr++) {

12
Source/utils/str_cat.cpp

@ -9,6 +9,7 @@ namespace devilution {
namespace {
[[nodiscard]] char HexDigit(uint8_t v) { return "0123456789abcdef"[v]; }
[[nodiscard]] char HexDigitUpper(uint8_t v) { return "0123456789ABCDEF"[v]; }
} // namespace
@ -26,8 +27,13 @@ char *BufCopy(char *out, unsigned long long value)
}
char *BufCopy(char *out, AsHexU8Pad2 value)
{
*out++ = HexDigit(value.value >> 4);
*out++ = HexDigit(value.value & 0xf);
if (value.uppercase) {
*out++ = HexDigitUpper(value.value >> 4);
*out++ = HexDigitUpper(value.value & 0xf);
} else {
*out++ = HexDigit(value.value >> 4);
*out++ = HexDigit(value.value & 0xf);
}
return out;
}
char *BufCopy(char *out, AsHexU16Pad2 value)
@ -36,7 +42,7 @@ char *BufCopy(char *out, AsHexU16Pad2 value)
if (value.value > 0xfff) {
out = BufCopy(out, AsHexU8Pad2 { static_cast<uint8_t>(value.value >> 8) });
} else {
*out++ = HexDigit(value.value >> 8);
*out++ = value.uppercase ? HexDigitUpper(value.value >> 8) : HexDigit(value.value >> 8);
}
}
return BufCopy(out, AsHexU8Pad2 { static_cast<uint8_t>(value.value & 0xff) });

46
Source/utils/str_cat.hpp

@ -10,21 +10,42 @@ namespace devilution {
struct AsHexU8Pad2 {
uint8_t value;
bool uppercase = false;
};
struct AsHexU16Pad2 {
uint16_t value;
bool uppercase = false;
};
/** @brief Maximum number of digits for a value of type T.
* If T is signed, also accounts for a potential '-'.
*/
template <typename T>
constexpr size_t MaxNumDigits = (8 * sizeof(T) * 28 / 93) + 1 + (std::is_signed_v<T> ? 1 : 0);
template <typename T>
struct LeftPadT {
T value;
unsigned length;
char padChar;
};
/**
* @brief Formats the value as a lowercase zero-padded hexadecimal with at least 2 hex digits (0-padded on the left).
*/
constexpr AsHexU8Pad2 AsHexPad2(uint8_t value) { return { value }; }
constexpr AsHexU8Pad2 AsHexPad2(uint8_t value, bool uppercase = false) { return { value, uppercase }; }
/**
* @brief Formats the value as a lowercase zero-padded hexadecimal with at least 2 hex digits (0-padded on the left).
*/
constexpr AsHexU16Pad2 AsHexPad2(uint16_t value) { return { value }; }
constexpr AsHexU16Pad2 AsHexPad2(uint16_t value, bool uppercase = false) { return { value, uppercase }; }
/**
* @brief Left-pads the value to the specified length with the specified character.
*/
template <typename T>
constexpr LeftPadT<T> LeftPad(T value, unsigned length, char padChar) { return { value, length, padChar }; }
/**
* @brief Writes the integer to the given buffer.
@ -65,6 +86,19 @@ inline char *BufCopy(char *out, unsigned short value)
char *BufCopy(char *out, AsHexU8Pad2 value);
char *BufCopy(char *out, AsHexU16Pad2 value);
template <typename T>
char *BufCopy(char *out, LeftPadT<T> value)
{
char buf[MaxNumDigits<T>];
const char *end = BufCopy(buf, value.value);
const std::string_view str = std::string_view(buf, end - buf);
for (size_t i = str.size(); i < value.length; ++i) {
*out++ = value.padChar;
}
std::memcpy(out, str.data(), str.size());
return out + str.size();
}
/**
* @brief Appends the integer to the given string.
*/
@ -102,6 +136,14 @@ inline void StrAppend(std::string &out, unsigned short value)
void StrAppend(std::string &out, AsHexU8Pad2 value);
void StrAppend(std::string &out, AsHexU16Pad2 value);
template <typename T>
void StrAppend(std::string &out, LeftPadT<T> value)
{
char buf[MaxNumDigits<T>];
const auto len = static_cast<size_t>(BufCopy(buf, value) - buf);
out.append(buf, len);
}
/**
* @brief Copies the given std::string_view to the given buffer.
*/

7
test/str_cat_test.cpp

@ -1,3 +1,5 @@
#include <cstdint>
#include <gtest/gtest.h>
#include "utils/str_cat.hpp"
@ -10,6 +12,11 @@ TEST(StrCatTest, StrCatBasicTest)
EXPECT_EQ(StrCat("a", "b", "c", 5), "abc5");
}
TEST(StrCatTest, LeftPadTest)
{
EXPECT_EQ(StrCat(LeftPad(static_cast<uint8_t>(5), 2, '0')), "05");
}
TEST(StrCatTest, BufCopyBasicTest)
{
char buf[5];

Loading…
Cancel
Save