#pragma once #include #ifdef USE_SDL3 #include #else #include #endif #include #include #include #include "utils/str_cat.hpp" #ifdef USE_SDL1 #include "utils/sdl2_to_1_2_backports.h" #endif namespace devilution { // Local definition to fix compilation issue due to header conflict. [[noreturn]] extern void app_fatal(std::string_view); 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, }; constexpr auto defaultCategory = LogCategory::Application; 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, }; namespace detail { template std::string format(std::string_view fmt, Args &&...args) { FMT_TRY { return fmt::format(fmt::runtime(fmt), std::forward(args)...); } #if FMT_EXCEPTIONS FMT_CATCH(const fmt::format_error &e) { // e.what() is undefined if exceptions are disabled, so we wrap it // with an `FMT_EXCEPTIONS` check. std::string error = e.what(); #else FMT_CATCH(const fmt::format_error &) { std::string error = "unknown (FMT_EXCEPTIONS disabled)"; #endif std::string fullError = StrCat("Format error, fmt: ", fmt, " error: ", error); SDL_LogCritical(SDL_LOG_CATEGORY_APPLICATION, "%s", error.c_str()); app_fatal(error); } } } // namespace detail inline void Log(std::string_view str) { SDL_Log("%.*s", static_cast(str.size()), str.data()); } template void Log(std::string_view fmt, Args &&...args) { auto str = detail::format(fmt, std::forward(args)...); SDL_Log("%s", str.c_str()); } inline void LogVerbose(LogCategory category, std::string_view str) { SDL_LogVerbose(static_cast(category), "%.*s", static_cast(str.size()), str.data()); } inline bool IsLogLevel(LogCategory category, SDL_LogPriority priority) { #ifdef USE_SDL3 return SDL_GetLogPriority(static_cast(category)) <= priority; #else return SDL_LogGetPriority(static_cast(category)) <= priority; #endif } template void LogVerbose(LogCategory category, std::string_view fmt, Args &&...args) { if (!IsLogLevel(category, SDL_LOG_PRIORITY_VERBOSE)) return; auto str = detail::format(fmt, std::forward(args)...); SDL_LogVerbose(static_cast(category), "%s", str.c_str()); } template void LogVerbose(std::string_view fmt, Args &&...args) { LogVerbose(defaultCategory, fmt, std::forward(args)...); } inline void LogDebug(LogCategory category, std::string_view str) { SDL_LogDebug(static_cast(category), "%.*s", static_cast(str.size()), str.data()); } template void LogDebug(LogCategory category, std::string_view fmt, Args &&...args) { if (!IsLogLevel(category, SDL_LOG_PRIORITY_DEBUG)) return; auto str = detail::format(fmt, std::forward(args)...); SDL_LogDebug(static_cast(category), "%s", str.c_str()); } template void LogDebug(std::string_view fmt, Args &&...args) { LogDebug(defaultCategory, fmt, std::forward(args)...); } inline void LogInfo(LogCategory category, std::string_view str) { SDL_LogInfo(static_cast(category), "%.*s", static_cast(str.size()), str.data()); } template void LogInfo(LogCategory category, std::string_view fmt, Args &&...args) { auto str = detail::format(fmt, std::forward(args)...); SDL_LogInfo(static_cast(category), "%s", str.c_str()); } template void LogInfo(std::string_view fmt, Args &&...args) { LogInfo(defaultCategory, fmt, std::forward(args)...); } inline void LogWarn(LogCategory category, std::string_view str) { SDL_LogWarn(static_cast(category), "%.*s", static_cast(str.size()), str.data()); } template void LogWarn(LogCategory category, std::string_view fmt, Args &&...args) { auto str = detail::format(fmt, std::forward(args)...); SDL_LogWarn(static_cast(category), "%s", str.c_str()); } template void LogWarn(std::string_view fmt, Args &&...args) { LogWarn(defaultCategory, fmt, std::forward(args)...); } inline void LogError(LogCategory category, std::string_view str) { SDL_LogError(static_cast(category), "%.*s", static_cast(str.size()), str.data()); } template void LogError(LogCategory category, std::string_view fmt, Args &&...args) { auto str = detail::format(fmt, std::forward(args)...); SDL_LogError(static_cast(category), "%s", str.c_str()); } template void LogError(std::string_view fmt, Args &&...args) { LogError(defaultCategory, fmt, std::forward(args)...); } inline void LogCritical(LogCategory category, std::string_view str) { SDL_LogCritical(static_cast(category), "%.*s", static_cast(str.size()), str.data()); } template void LogCritical(LogCategory category, std::string_view fmt, Args &&...args) { auto str = detail::format(fmt, std::forward(args)...); SDL_LogCritical(static_cast(category), "%s", str.c_str()); } template void LogCritical(std::string_view fmt, Args &&...args) { LogCritical(defaultCategory, fmt, std::forward(args)...); } inline void LogMessageV(LogCategory category, LogPriority priority, std::string_view str) { SDL_LogMessage(static_cast(category), static_cast(priority), "%.*s", static_cast(str.size()), str.data()); } template void LogMessageV(LogCategory category, LogPriority priority, std::string_view fmt, Args &&...args) { auto str = detail::format(fmt, std::forward(args)...); SDL_LogMessageV(static_cast(category), static_cast(priority), "%s", str.c_str()); } template void LogMessageV(std::string_view fmt, Args &&...args) { LogMessageV(defaultCategory, fmt, std::forward(args)...); } } // namespace devilution