From f3a401b5f679c665dc8b2bdf5ec3d2296fae8c07 Mon Sep 17 00:00:00 2001 From: Jmgr Date: Sat, 17 Apr 2021 19:01:12 +0100 Subject: [PATCH] Add the fmt library as a dependency and add some examples Attempt at fixing missing functions on some platforms --- 3rdParty/libfmt/CMakeLists.txt | 18 +++++++ CMakeLists.txt | 14 ++++++ Source/DiabloUI/selgame.cpp | 11 ++-- Source/debug.cpp | 3 ++ Source/init.cpp | 1 + Source/stores.cpp | 6 ++- Source/utils/format.hpp | 57 +++++++++++++++++++++ Source/utils/log.hpp | 91 ++++++++++++++++++++++++++++++++++ 8 files changed, 195 insertions(+), 6 deletions(-) create mode 100644 3rdParty/libfmt/CMakeLists.txt create mode 100644 Source/utils/format.hpp create mode 100644 Source/utils/log.hpp diff --git a/3rdParty/libfmt/CMakeLists.txt b/3rdParty/libfmt/CMakeLists.txt new file mode 100644 index 000000000..cd9bf8803 --- /dev/null +++ b/3rdParty/libfmt/CMakeLists.txt @@ -0,0 +1,18 @@ +if(NOT DEVILUTIONX_SYSTEM_LIBFMT) + include(FetchContent_MakeAvailableExcludeFromAll) + + add_definitions(-D_POSIX_C_SOURCE=200809L) + + if(DEVILUTIONX_STATIC_LIBFMT) + set(BUILD_SHARED_LIBS OFF) + else() + set(BUILD_SHARED_LIBS ON) + endif() + include(FetchContent) + FetchContent_Declare(fmt + GIT_REPOSITORY https://github.com/fmtlib/fmt.git + GIT_TAG 7bdf0628b1276379886c7f6dda2cef2b3b374f0b + ) + FetchContent_MakeAvailableExcludeFromAll(fmt) + set(fmt_SOURCE_DIR ${fmt_SOURCE_DIR} PARENT_SCOPE) +endif() diff --git a/CMakeLists.txt b/CMakeLists.txt index 9555690d3..ae23f5733 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -43,6 +43,10 @@ option(DEVILUTIONX_SYSTEM_LIBSODIUM "Use system-provided libsodium" ON) cmake_dependent_option(DEVILUTIONX_STATIC_LIBSODIUM "Link static libsodium" OFF "DEVILUTIONX_SYSTEM_LIBSODIUM AND NOT DIST" ON) +option(DEVILUTIONX_SYSTEM_LIBFMT "Use system-provided libfmt" OFF) +cmake_dependent_option(DEVILUTIONX_STATIC_LIBFMT "Link static libfmt" OFF + "DEVILUTIONX_STATIC_LIBFMT AND NOT DIST" ON) + if(NOT VERSION_NUM) include(CMake/git.cmake) get_git_tag(VERSION_NUM) @@ -169,6 +173,13 @@ if(NOT NONET) endif() endif() +if(DEVILUTIONX_SYSTEM_LIBFMT) + set(fmt_USE_STATIC_LIBS ${DEVILUTIONX_STATIC_LIBFMT}) + find_package(fmt REQUIRED) +else() + add_subdirectory(3rdParty/libfmt) +endif() + if(USE_SDL1) find_package(SDL REQUIRED) find_package(SDL_ttf REQUIRED) @@ -456,6 +467,7 @@ target_include_directories(${BIN_TARGET} PRIVATE 3rdParty/asio/include 3rdParty/Radon/Radon/include 3rdParty/libsmacker + ${fmt_SOURCE_DIR}/include ${CMAKE_CURRENT_BINARY_DIR}) if(NOT N3DS) @@ -473,6 +485,8 @@ if(NOT NONET) target_link_libraries(${BIN_TARGET} PRIVATE sodium) endif() +target_link_libraries(${BIN_TARGET} PRIVATE fmt) + genex_for_option(DEBUG) target_compile_definitions(${BIN_TARGET} PUBLIC "$<${DEBUG_GENEX}:_DEBUG>") target_compile_definitions(${BIN_TARGET} PRIVATE ASIO_STANDALONE) diff --git a/Source/DiabloUI/selgame.cpp b/Source/DiabloUI/selgame.cpp index 388fc2c10..352799e19 100644 --- a/Source/DiabloUI/selgame.cpp +++ b/Source/DiabloUI/selgame.cpp @@ -11,6 +11,8 @@ #include "options.h" #include "storm/storm.h" +#include + namespace devilution { char selgame_Label[32]; @@ -410,10 +412,11 @@ static bool IsGameCompatible(GameData *data) if (data->programid != GAME_ID) { UiSelOkDialog(title, "The host is running a different game than you.", false); } else { - char msg[64]; - sprintf(msg, "Your version %s does not match the host %d.%d.%d.", PROJECT_VERSION, PROJECT_VERSION_MAJOR, PROJECT_VERSION_MINOR, PROJECT_VERSION_PATCH); - - UiSelOkDialog(title, msg, false); + // An example of using a format with references. Note the references that allow a localized string to change the order of the inserted parameters. + // Note that named insertions would probably be better here, but this is just to provide an example. + auto msg = fmt::format("Your version {0} does not match the host {1}.{2}.{3}.", PROJECT_VERSION, PROJECT_VERSION_MAJOR, PROJECT_VERSION_MINOR, PROJECT_VERSION_PATCH); + + UiSelOkDialog(title, msg.c_str(), false); } LoadBackgroundArt("ui_art\\selgame.pcx"); diff --git a/Source/debug.cpp b/Source/debug.cpp index 67bfae771..b8087e54d 100644 --- a/Source/debug.cpp +++ b/Source/debug.cpp @@ -7,6 +7,9 @@ #include "cursor.h" #include "inv.h" #include "spells.h" +#include "utils/format.hpp" + +#include namespace devilution { diff --git a/Source/init.cpp b/Source/init.cpp index 40869c1fd..2e05c66a3 100644 --- a/Source/init.cpp +++ b/Source/init.cpp @@ -14,6 +14,7 @@ #include "storm/storm.h" #include "utils/paths.h" #include "utils/ui_fwd.h" +#include "utils/log.hpp" #ifdef __vita__ // increase default allowed heap size on Vita diff --git a/Source/stores.cpp b/Source/stores.cpp index 0bbd46b5e..bbf7cf340 100644 --- a/Source/stores.cpp +++ b/Source/stores.cpp @@ -6,6 +6,7 @@ #include "stores.h" #include +#include #include "cursor.h" #include "init.h" @@ -270,8 +271,9 @@ void S_StartSBuy() stextsize = true; stextscrl = true; stextsval = 0; - sprintf(tempstr, "I have these items for sale: Your gold: %i", plr[myplr]._pGold); - AddSText(0, 1, true, tempstr, COL_GOLD, false); + // An example of a named format insertion. + auto str = fmt::format("I have these items for sale: Your gold: {gold}", fmt::arg("gold", plr[myplr]._pGold)); + AddSText(0, 1, true, str.c_str(), COL_GOLD, false); AddSLine(3); AddSLine(21); S_ScrollSBuy(stextsval); diff --git a/Source/utils/format.hpp b/Source/utils/format.hpp new file mode 100644 index 000000000..bd09087c0 --- /dev/null +++ b/Source/utils/format.hpp @@ -0,0 +1,57 @@ +#pragma once + +#include "../player.h" + +#include + +namespace fmt { + +template <> +struct formatter : formatter { + template + auto format(devilution::PLR_MODE mode, FormatContext &ctx) + { + string_view name = "unknown"; + switch (mode) { + case devilution::PM_STAND: + name = "stand"; + break; + case devilution::PM_WALK: + name = "walk (N, NW, or NE)"; + break; + case devilution::PM_WALK2: + name = "walk (S, SW, or SE)"; + break; + case devilution::PM_WALK3: + name = "walk (W or E)"; + break; + case devilution::PM_ATTACK: + name = "attack"; + break; + case devilution::PM_RATTACK: + name = "ranged attack"; + break; + case devilution::PM_BLOCK: + name = "block"; + break; + case devilution::PM_GOTHIT: + name = "got hit"; + break; + case devilution::PM_DEATH: + name = "death"; + break; + case devilution::PM_SPELL: + name = "spell"; + break; + case devilution::PM_NEWLVL: + name = "new level"; + break; + case devilution::PM_QUIT: + name = "quit"; + break; + } + return formatter::format(name, ctx); + } +}; + +} diff --git a/Source/utils/log.hpp b/Source/utils/log.hpp new file mode 100644 index 000000000..4034c19b6 --- /dev/null +++ b/Source/utils/log.hpp @@ -0,0 +1,91 @@ +#pragma once + +#include "fmt/core.h" +#include "fmt/ranges.h" + +#include + +#ifdef USE_SDL1 +#include "sdl2_to_1_2_backports.h" +#endif + +namespace devilution { + +enum class LogCategory { + Application = SDL_LOG_CATEGORY_APPLICATION, + Error = SDL_LOG_CATEGORY_ERROR, + Assert = SDL_LOG_CATEGORY_ASSERT, + System = SDL_LOG_CATEGORY_SYSTEM, + Audio = SDL_LOG_CATEGORY_AUDIO, + Video = SDL_LOG_CATEGORY_VIDEO, + Render = SDL_LOG_CATEGORY_RENDER, + Input = SDL_LOG_CATEGORY_INPUT, + Test = SDL_LOG_CATEGORY_TEST, +}; + +enum class LogPriority { + Verbose = SDL_LOG_PRIORITY_VERBOSE, + Debug = SDL_LOG_PRIORITY_DEBUG, + Info = SDL_LOG_PRIORITY_INFO, + Warn = SDL_LOG_PRIORITY_WARN, + Error = SDL_LOG_PRIORITY_ERROR, + Critical = SDL_LOG_PRIORITY_CRITICAL, +}; + +template +void Log(const char *fmt, Args &&... args) +{ + auto str = fmt::format(fmt, std::forward(args)...); + SDL_Log("%s", str.c_str()); +} + +template +void LogVerbose(LogCategory category, const char *fmt, Args &&... args) +{ + auto str = fmt::format(fmt, std::forward(args)...); + SDL_LogVerbose(static_cast(category), "%s", str.c_str()); +} + +template +void LogDebug(LogCategory category, const char *fmt, Args &&... args) +{ + auto str = fmt::format(fmt, std::forward(args)...); + SDL_LogDebug(static_cast(category), "%s", str.c_str()); +} + +template +void LogInfo(LogCategory category, const char *fmt, Args &&... args) +{ + auto str = fmt::format(fmt, std::forward(args)...); + SDL_LogInfo(static_cast(category), "%s", str.c_str()); +} + +template +void LogWarn(LogCategory category, const char *fmt, Args &&... args) +{ + auto str = fmt::format(fmt, std::forward(args)...); + SDL_LogWarn(static_cast(category), "%s", str.c_str()); +} + +template +void LogError(LogCategory category, const char *fmt, Args &&... args) +{ + auto str = fmt::format(fmt, std::forward(args)...); + SDL_LogError(static_cast(category), "%s", str.c_str()); +} + +template +void LogCritical(LogCategory category, const char *fmt, Args &&... args) +{ + auto str = fmt::format(fmt, std::forward(args)...); + SDL_LogCritical(static_cast(category), "%s", str.c_str()); +} + +template +void LogMessageV(LogCategory category, LogPriority priority, const char *fmt, Args &&... args) +{ + auto str = fmt::format(fmt, std::forward(args)...); + SDL_LogMessageV(static_cast(category), static_cast(priority), "%s", str.c_str()); +} + +} // namespace devilution