mirror of
https://github.com/shadps4-emu/shadPS4.git
synced 2026-04-29 23:41:19 -06:00
Spdlog migration (#4069)
* spdlog migration * gitmodule instead of cmake
This commit is contained in:
parent
ed553054c6
commit
854b291c63
5
.gitmodules
vendored
5
.gitmodules
vendored
@ -136,3 +136,8 @@
|
||||
[submodule "externals/ImGuiFileDialog"]
|
||||
path = externals/ImGuiFileDialog
|
||||
url = https://github.com/shadexternals/ImGuiFileDialog.git
|
||||
[submodule "externals/spdlog"]
|
||||
path = externals/spdlog
|
||||
url = https://github.com/gabime/spdlog.git
|
||||
shallow = true
|
||||
branch = v2.x
|
||||
|
||||
@ -19,6 +19,8 @@ endif()
|
||||
|
||||
project(shadPS4 CXX C ASM ${ADDITIONAL_LANGUAGES})
|
||||
|
||||
include(FetchContent)
|
||||
|
||||
# Forcing PIE makes sure that the base address is high enough so that it doesn't clash with the PS4 memory.
|
||||
if(UNIX AND NOT APPLE)
|
||||
set(CMAKE_POSITION_INDEPENDENT_CODE TRUE)
|
||||
@ -701,16 +703,11 @@ set(DEV_TOOLS src/core/devtools/layer.cpp
|
||||
src/core/devtools/widget/text_editor.h
|
||||
)
|
||||
|
||||
set(COMMON src/common/logging/backend.cpp
|
||||
src/common/logging/backend.h
|
||||
src/common/logging/filter.cpp
|
||||
src/common/logging/filter.h
|
||||
set(COMMON src/common/logging/classes.h
|
||||
src/common/logging/formatter.h
|
||||
src/common/logging/log_entry.h
|
||||
src/common/logging/log.cpp
|
||||
src/common/logging/log.h
|
||||
src/common/logging/text_formatter.cpp
|
||||
src/common/logging/text_formatter.h
|
||||
src/common/logging/types.h
|
||||
src/common/logging/thread_name_formatter.h
|
||||
src/common/aes.h
|
||||
src/common/alignment.h
|
||||
src/common/arch.h
|
||||
@ -1149,7 +1146,7 @@ create_target_directory_groups(shadps4)
|
||||
|
||||
target_link_libraries(shadps4 PRIVATE magic_enum::magic_enum fmt::fmt toml11::toml11 tsl::robin_map xbyak::xbyak Tracy::TracyClient RenderDoc::API FFmpeg::ffmpeg Dear_ImGui ImGuiFileDialog gcn half::half ZLIB::ZLIB PNG::PNG minimp3)
|
||||
target_link_libraries(shadps4 PRIVATE Boost::headers GPUOpen::VulkanMemoryAllocator LibAtrac9 sirit Vulkan::Headers xxHash::xxhash Zydis::Zydis glslang::glslang SDL3::SDL3 pugixml::pugixml)
|
||||
target_link_libraries(shadps4 PRIVATE stb::headers lfreist-hwinfo::hwinfo nlohmann_json::nlohmann_json miniz::miniz fdk-aac CLI11::CLI11 OpenAL::OpenAL Cpp_Httplib)
|
||||
target_link_libraries(shadps4 PRIVATE stb::headers lfreist-hwinfo::hwinfo nlohmann_json::nlohmann_json miniz::miniz fdk-aac CLI11::CLI11 OpenAL::OpenAL Cpp_Httplib spdlog::spdlog)
|
||||
|
||||
if (${CMAKE_SYSTEM_NAME} STREQUAL "FreeBSD")
|
||||
target_link_libraries(shadps4 PRIVATE "/usr/lib/libusb.so")
|
||||
|
||||
@ -58,30 +58,38 @@ You can configure the emulator by editing the `config.toml` file found in the `u
|
||||
<details>
|
||||
<summary>Some configuration entries worth changing</summary>
|
||||
|
||||
- `[General]`
|
||||
|
||||
- `logType`: Configures logging synchronization (`sync`/`async`)
|
||||
- By default, the emulator logs messages asynchronously for better performance. Some log messages may end up being received out-of-order.
|
||||
- It can be beneficial to set this to `sync` in order for the log to accurately maintain message order, at the cost of performance.
|
||||
- When communicating about issues with games and the log messages aren't clear due to potentially confusing order, set this to `sync` and send that log as well.
|
||||
- `logFilter`: Sets the logging category for various logging classes.
|
||||
- Format: `<class>:<level> ...`
|
||||
- Multiple classes can be set by separating them with a space. (example: `Render:Warning Debug:Critical Lib.Pad:Error`)
|
||||
- `[Log]`
|
||||
- `sync`: Log synchronously (`true`/`false`)
|
||||
- By default `true`, the emulator logs messages synchronously to respect the order.
|
||||
- It can be beneficial to set this to `false` for better performance.
|
||||
- When communicating about issues with games and the log messages aren't clear due to potentially confusing order, set this to `true` and send that log instead.
|
||||
- `filter`: Sets the logging category for various logging classes.
|
||||
- Format: `<class>=<level>,...`
|
||||
- Multiple classes can be set by separating them with a comma. (example: `Render=warning,Debug=critical,Lib.Pad=error`)
|
||||
- Sub-classes can be specified in the same format as seen in the console/log (such as `Core.Linker`).
|
||||
- All classes and sub-classes can be set by specifying a `*` symbol. (example: `Kernel.*:Critical`)
|
||||
- Valid log levels: `Trace, Debug, Info, Warning, Error, Critical` - in this order, setting a level silences all levels preceding it and logs every level after it.
|
||||
- Valid log levels: `trace, debug, info, warning, error, critical` - in this order, setting a level silences all levels preceding it and logs every level after it.
|
||||
- Examples:
|
||||
- If the log is being spammed with messages coming from Lib.Pad, you can use `Lib.Pad:Critical` to only log critical-level messages.
|
||||
- If you'd like to mute everything, but still want to receive messages from Vulkan rendering: `*:Critical Render.Vulkan:Info`
|
||||
- `isIdenticalLogGrouped`: Group same logs in one line with a counter (`true`/`false`)
|
||||
- By default, the emulator will not rewrite the same line, and instead add a counter.
|
||||
- If the log is being spammed with messages coming from Lib.Pad, you can use `Lib.Pad=critical` to only log critical-level messages.
|
||||
- If you'd like to mute everything, but still want to receive messages from Vulkan rendering: `off,Render.Vulkan=info` (if you want critical at least `critical,Render.Vulkan=info`)
|
||||
- `skipDuplicate`: Skip same lines with a `Skipped N duplicate messages..` message (`true`/`false`)
|
||||
- By default, the emulator will skip same lines for `maxSkipDuration` milliseconds.
|
||||
- `append`: Append log to the existing file (`true`/`false`)
|
||||
- By default, the emulator will overwrite the log file. (it can also be by-passed with CLI `--log-append`)
|
||||
- `separate`: Write log to `log/{GAME ID}.log` instead of `log/shad_log.txt` (`true`/`false`)
|
||||
- By default, the emulator use `log/shad_log.txt`.
|
||||
- `maxSkipDuration`: Amount of time in which identical lines will not be logged (milliseconds).
|
||||
- By default, 5'000 milliseconds.
|
||||
- `sizeLimit`: Size limit for log files (bytes).
|
||||
- By default, 100 MB.
|
||||
- `type`: Choose between `wincolor` (WriteConsole*) and `msvc` (OutputDebugString*) - only for Windows.
|
||||
- By default, `wincolor`.
|
||||
|
||||
- `Fullscreen`: Display the game in a full screen borderless window.
|
||||
|
||||
- `[GPU]`
|
||||
- `dumpShaders`: Dump shaders that are loaded by the emulator. Dump path: `../user/shader/dumps`
|
||||
- `nullGpu`: Disables rendering.
|
||||
- `screenWidth` and `screenHeight`: Configures the game window width and height.
|
||||
- `Fullscreen`: Display the game in a full screen borderless window.
|
||||
|
||||
- `[Vulkan]`
|
||||
- `validation`-related settings: Use when debugging Vulkan.
|
||||
|
||||
5
externals/CMakeLists.txt
vendored
5
externals/CMakeLists.txt
vendored
@ -21,6 +21,11 @@ if (NOT TARGET Boost::headers)
|
||||
add_subdirectory(ext-boost)
|
||||
endif()
|
||||
|
||||
# spdlog
|
||||
set(SPDLOG_NO_EXCEPTIONS ON)
|
||||
set(SPDLOG_DISABLE_GLOBAL_LOGGER ON)
|
||||
add_subdirectory(spdlog)
|
||||
|
||||
# fmtlib
|
||||
if (NOT TARGET fmt::fmt)
|
||||
add_subdirectory(fmt)
|
||||
|
||||
1
externals/spdlog
vendored
Submodule
1
externals/spdlog
vendored
Submodule
@ -0,0 +1 @@
|
||||
Subproject commit b8944a4bcd478ee03375c9c50dc8d6c741f43f7b
|
||||
@ -3,7 +3,6 @@
|
||||
|
||||
#include "common/arch.h"
|
||||
#include "common/assert.h"
|
||||
#include "common/logging/backend.h"
|
||||
|
||||
#if defined(ARCH_X86_64)
|
||||
#define Crash() __asm__ __volatile__("int $3")
|
||||
@ -14,14 +13,12 @@
|
||||
#endif
|
||||
|
||||
void assert_fail_impl() {
|
||||
Common::Log::Stop();
|
||||
std::fflush(stdout);
|
||||
Common::Log::Shutdown();
|
||||
Crash();
|
||||
}
|
||||
|
||||
[[noreturn]] void unreachable_impl() {
|
||||
Common::Log::Stop();
|
||||
std::fflush(stdout);
|
||||
Common::Log::Shutdown();
|
||||
Crash();
|
||||
throw std::runtime_error("Unreachable code");
|
||||
}
|
||||
|
||||
@ -55,6 +55,10 @@ std::optional<T> get_optional(const toml::value& v, const std::string& key) {
|
||||
if (it->second.is_integer()) {
|
||||
return static_cast<u32>(toml::get<unsigned int>(it->second));
|
||||
}
|
||||
} else if constexpr (std::is_same_v<T, unsigned long long>) {
|
||||
if (it->second.is_integer()) {
|
||||
return static_cast<u32>(toml::get<unsigned long long>(it->second));
|
||||
}
|
||||
} else if constexpr (std::is_same_v<T, double>) {
|
||||
if (it->second.is_floating()) {
|
||||
return toml::get<double>(it->second);
|
||||
@ -139,9 +143,6 @@ static ConfigEntry<int> extraDmemInMbytes(0);
|
||||
static ConfigEntry<bool> isPSNSignedIn(false);
|
||||
static ConfigEntry<bool> isTrophyPopupDisabled(false);
|
||||
static ConfigEntry<double> trophyNotificationDuration(6.0);
|
||||
static ConfigEntry<string> logFilter("");
|
||||
static ConfigEntry<string> logType("sync");
|
||||
static ConfigEntry<bool> isIdenticalLogGrouped(true);
|
||||
static ConfigEntry<string> userName("shadPS4");
|
||||
static ConfigEntry<bool> isShowSplash(false);
|
||||
static ConfigEntry<string> isSideTrophy("right");
|
||||
@ -150,6 +151,19 @@ static bool enableDiscordRPC = false;
|
||||
static std::filesystem::path sys_modules_path = {};
|
||||
static std::filesystem::path fonts_path = {};
|
||||
|
||||
// Log
|
||||
static ConfigEntry<bool> logAppend(false);
|
||||
static ConfigEntry<bool> logEnable(true);
|
||||
static ConfigEntry<string> logFilter("");
|
||||
static ConfigEntry<u32> logMaxSkipDuration(5'000);
|
||||
static ConfigEntry<bool> logSeparate(false);
|
||||
static ConfigEntry<unsigned long long> logSizeLimit(100_MB);
|
||||
static ConfigEntry<bool> logSkipDuplicate(true);
|
||||
static ConfigEntry<bool> logSync(true);
|
||||
#ifdef _WIN32
|
||||
static ConfigEntry<string> logType("wincolor");
|
||||
#endif
|
||||
|
||||
// Input
|
||||
static ConfigEntry<int> cursorState(HideCursorState::Idle);
|
||||
static ConfigEntry<int> cursorHideTimeout(5); // 5 seconds (default)
|
||||
@ -202,9 +216,7 @@ static ConfigEntry<bool> pipelineCacheArchive(false);
|
||||
// Debug
|
||||
static ConfigEntry<bool> isDebugDump(false);
|
||||
static ConfigEntry<bool> isShaderDebug(false);
|
||||
static ConfigEntry<bool> isSeparateLogFilesEnabled(false);
|
||||
static ConfigEntry<bool> showFpsCounter(false);
|
||||
static ConfigEntry<bool> logEnabled(true);
|
||||
|
||||
// GUI
|
||||
static std::vector<GameInstallDir> settings_install_dirs = {};
|
||||
@ -277,10 +289,69 @@ int* GetControllerCustomColor() {
|
||||
return controllerCustomColorRGB;
|
||||
}
|
||||
|
||||
bool getLoggingEnabled() {
|
||||
return logEnabled.get();
|
||||
// Log
|
||||
bool isLogAppend() {
|
||||
return logAppend.get();
|
||||
}
|
||||
|
||||
bool isLogEnable() {
|
||||
return logEnable.get();
|
||||
}
|
||||
|
||||
void setLogEnable(bool enable, bool is_game_specific) {
|
||||
logEnable.set(enable, is_game_specific);
|
||||
}
|
||||
|
||||
bool getLogEnable() {
|
||||
return logEnable.get();
|
||||
}
|
||||
|
||||
string getLogFilter() {
|
||||
return logFilter.get();
|
||||
}
|
||||
|
||||
void setLogFilter(const string& type, bool is_game_specific) {
|
||||
logFilter.set(type, is_game_specific);
|
||||
}
|
||||
|
||||
u32 getLogMaxSkipDuration() {
|
||||
return logMaxSkipDuration.get();
|
||||
}
|
||||
|
||||
bool getLogSeparate() {
|
||||
return logSeparate.get();
|
||||
}
|
||||
|
||||
void setLogSeparate(bool enabled, bool is_game_specific) {
|
||||
logSeparate.set(enabled, is_game_specific);
|
||||
}
|
||||
|
||||
unsigned long long getLogSizeLimit() {
|
||||
return logSizeLimit.get();
|
||||
}
|
||||
|
||||
bool getLogSkipDuplicate() {
|
||||
return logSkipDuplicate.get();
|
||||
}
|
||||
|
||||
void setLogSkipDuplicate(bool enable, bool is_game_specific) {
|
||||
logSkipDuplicate.set(enable, is_game_specific);
|
||||
}
|
||||
|
||||
bool isLogSync() {
|
||||
return logSync.get();
|
||||
}
|
||||
|
||||
#ifdef _WIN32
|
||||
string getLogType() {
|
||||
return logType.get();
|
||||
}
|
||||
|
||||
void setLogType(const string& type, bool is_game_specific) {
|
||||
logType.set(type, is_game_specific);
|
||||
}
|
||||
#endif
|
||||
|
||||
void SetControllerCustomColor(int r, int b, int g) {
|
||||
controllerCustomColorRGB[0] = r;
|
||||
controllerCustomColorRGB[1] = b;
|
||||
@ -388,18 +459,6 @@ s32 getGpuId() {
|
||||
return gpuId.get();
|
||||
}
|
||||
|
||||
string getLogFilter() {
|
||||
return logFilter.get();
|
||||
}
|
||||
|
||||
string getLogType() {
|
||||
return logType.get();
|
||||
}
|
||||
|
||||
bool groupIdenticalLogs() {
|
||||
return isIdenticalLogGrouped.get();
|
||||
}
|
||||
|
||||
string getUserName() {
|
||||
return userName.get();
|
||||
}
|
||||
@ -480,10 +539,6 @@ void setShowFpsCounter(bool enable, bool is_game_specific) {
|
||||
showFpsCounter.set(enable, is_game_specific);
|
||||
}
|
||||
|
||||
bool isLoggingEnabled() {
|
||||
return logEnabled.get();
|
||||
}
|
||||
|
||||
u32 vblankFreq() {
|
||||
if (vblankFrequency.get() < 60) {
|
||||
vblankFrequency = 60;
|
||||
@ -563,10 +618,6 @@ void setDebugDump(bool enable, bool is_game_specific) {
|
||||
isDebugDump.set(enable, is_game_specific);
|
||||
}
|
||||
|
||||
void setLoggingEnabled(bool enable, bool is_game_specific) {
|
||||
logEnabled.set(enable, is_game_specific);
|
||||
}
|
||||
|
||||
void setCollectShaderForDebug(bool enable, bool is_game_specific) {
|
||||
isShaderDebug.set(enable, is_game_specific);
|
||||
}
|
||||
@ -695,22 +746,6 @@ void setDevKitConsole(bool enable, bool is_game_specific) {
|
||||
isDevKit.set(enable, is_game_specific);
|
||||
}
|
||||
|
||||
void setLogType(const string& type, bool is_game_specific) {
|
||||
logType.set(type, is_game_specific);
|
||||
}
|
||||
|
||||
void setIdenticalLogGrouped(bool enable, bool is_game_specific) {
|
||||
isIdenticalLogGrouped.set(enable, is_game_specific);
|
||||
}
|
||||
|
||||
void setLogFilter(const string& type, bool is_game_specific) {
|
||||
logFilter.set(type, is_game_specific);
|
||||
}
|
||||
|
||||
void setSeparateLogFilesEnabled(bool enabled, bool is_game_specific) {
|
||||
isSeparateLogFilesEnabled.set(enabled, is_game_specific);
|
||||
}
|
||||
|
||||
void setUserName(const string& name, bool is_game_specific) {
|
||||
userName.set(name, is_game_specific);
|
||||
}
|
||||
@ -804,10 +839,6 @@ u32 GetLanguage() {
|
||||
return m_language.get();
|
||||
}
|
||||
|
||||
bool getSeparateLogFilesEnabled() {
|
||||
return isSeparateLogFilesEnabled.get();
|
||||
}
|
||||
|
||||
bool getPSNSignedIn() {
|
||||
return isPSNSignedIn.get();
|
||||
}
|
||||
@ -900,9 +931,6 @@ void load(const std::filesystem::path& path, bool is_game_specific) {
|
||||
trophyNotificationDuration.setFromToml(general, "trophyNotificationDuration",
|
||||
is_game_specific);
|
||||
enableDiscordRPC = toml::find_or<bool>(general, "enableDiscordRPC", enableDiscordRPC);
|
||||
logFilter.setFromToml(general, "logFilter", is_game_specific);
|
||||
logType.setFromToml(general, "logType", is_game_specific);
|
||||
isIdenticalLogGrouped.setFromToml(general, "isIdenticalLogGrouped", is_game_specific);
|
||||
userName.setFromToml(general, "userName", is_game_specific);
|
||||
isShowSplash.setFromToml(general, "showSplash", is_game_specific);
|
||||
isSideTrophy.setFromToml(general, "sideTrophy", is_game_specific);
|
||||
@ -913,6 +941,22 @@ void load(const std::filesystem::path& path, bool is_game_specific) {
|
||||
fonts_path = toml::find_fs_path_or(general, "fontsPath", fonts_path);
|
||||
}
|
||||
|
||||
if (data.contains("Log")) {
|
||||
const toml::value& log = data.at("Log");
|
||||
|
||||
logAppend.setFromToml(log, "append", is_game_specific);
|
||||
logEnable.setFromToml(log, "enable", is_game_specific);
|
||||
logFilter.setFromToml(log, "filter", is_game_specific);
|
||||
logMaxSkipDuration.setFromToml(log, "maxSkipDuration", is_game_specific);
|
||||
logSeparate.setFromToml(log, "separate", is_game_specific);
|
||||
logSkipDuplicate.setFromToml(log, "skipDuplicate", is_game_specific);
|
||||
logSizeLimit.setFromToml(log, "sizeLimit", is_game_specific);
|
||||
logSync.setFromToml(log, "sync", is_game_specific);
|
||||
#ifdef _WIN32
|
||||
logType.setFromToml(log, "type", is_game_specific);
|
||||
#endif
|
||||
}
|
||||
|
||||
if (data.contains("Input")) {
|
||||
const toml::value& input = data.at("Input");
|
||||
|
||||
@ -979,10 +1023,8 @@ void load(const std::filesystem::path& path, bool is_game_specific) {
|
||||
const toml::value& debug = data.at("Debug");
|
||||
|
||||
isDebugDump.setFromToml(debug, "DebugDump", is_game_specific);
|
||||
isSeparateLogFilesEnabled.setFromToml(debug, "isSeparateLogFilesEnabled", is_game_specific);
|
||||
isShaderDebug.setFromToml(debug, "CollectShader", is_game_specific);
|
||||
showFpsCounter.setFromToml(debug, "showFpsCounter", is_game_specific);
|
||||
logEnabled.setFromToml(debug, "logEnabled", is_game_specific);
|
||||
current_version = toml::find_or<std::string>(debug, "ConfigVersion", current_version);
|
||||
}
|
||||
|
||||
@ -1033,8 +1075,8 @@ void load(const std::filesystem::path& path, bool is_game_specific) {
|
||||
|
||||
void sortTomlSections(toml::ordered_value& data) {
|
||||
toml::ordered_value ordered_data;
|
||||
std::vector<string> section_order = {"General", "Input", "Audio", "GPU", "Vulkan",
|
||||
"Debug", "Keys", "GUI", "Settings"};
|
||||
std::vector<string> section_order = {"General", "Log", "Input", "Audio", "GPU",
|
||||
"Vulkan", "Debug", "Keys", "GUI", "Settings"};
|
||||
|
||||
for (const auto& section : section_order) {
|
||||
if (data.contains(section)) {
|
||||
@ -1089,9 +1131,6 @@ void save(const std::filesystem::path& path, bool is_game_specific) {
|
||||
isTrophyPopupDisabled.setTomlValue(data, "General", "isTrophyPopupDisabled", is_game_specific);
|
||||
trophyNotificationDuration.setTomlValue(data, "General", "trophyNotificationDuration",
|
||||
is_game_specific);
|
||||
logFilter.setTomlValue(data, "General", "logFilter", is_game_specific);
|
||||
logType.setTomlValue(data, "General", "logType", is_game_specific);
|
||||
isIdenticalLogGrouped.setTomlValue(data, "General", "isIdenticalLogGrouped", is_game_specific);
|
||||
userName.setTomlValue(data, "General", "userName", is_game_specific);
|
||||
isShowSplash.setTomlValue(data, "General", "showSplash", is_game_specific);
|
||||
isSideTrophy.setTomlValue(data, "General", "sideTrophy", is_game_specific);
|
||||
@ -1103,6 +1142,18 @@ void save(const std::filesystem::path& path, bool is_game_specific) {
|
||||
isPSNSignedIn.setTomlValue(data, "General", "isPSNSignedIn", is_game_specific);
|
||||
isConnectedToNetwork.setTomlValue(data, "General", "isConnectedToNetwork", is_game_specific);
|
||||
|
||||
logAppend.setTomlValue(data, "Log", "append", is_game_specific);
|
||||
logEnable.setTomlValue(data, "Log", "enable", is_game_specific);
|
||||
logFilter.setTomlValue(data, "Log", "filter", is_game_specific);
|
||||
logMaxSkipDuration.setTomlValue(data, "Log", "maxSkipDuration", is_game_specific);
|
||||
logSeparate.setTomlValue(data, "Log", "separate", is_game_specific);
|
||||
logSizeLimit.setTomlValue(data, "Log", "sizeLimit", is_game_specific);
|
||||
logSkipDuplicate.setTomlValue(data, "Log", "skipDuplicate", is_game_specific);
|
||||
logSync.setTomlValue(data, "Log", "sync", is_game_specific);
|
||||
#ifdef _WIN32
|
||||
logType.setTomlValue(data, "Log", "type", is_game_specific);
|
||||
#endif
|
||||
|
||||
cursorState.setTomlValue(data, "Input", "cursorState", is_game_specific);
|
||||
cursorHideTimeout.setTomlValue(data, "Input", "cursorHideTimeout", is_game_specific);
|
||||
isMotionControlsEnabled.setTomlValue(data, "Input", "isMotionControlsEnabled",
|
||||
@ -1146,9 +1197,6 @@ void save(const std::filesystem::path& path, bool is_game_specific) {
|
||||
|
||||
isDebugDump.setTomlValue(data, "Debug", "DebugDump", is_game_specific);
|
||||
isShaderDebug.setTomlValue(data, "Debug", "CollectShader", is_game_specific);
|
||||
isSeparateLogFilesEnabled.setTomlValue(data, "Debug", "isSeparateLogFilesEnabled",
|
||||
is_game_specific);
|
||||
logEnabled.setTomlValue(data, "Debug", "logEnabled", is_game_specific);
|
||||
|
||||
m_language.setTomlValue(data, "Settings", "consoleLanguage", is_game_specific);
|
||||
|
||||
@ -1233,13 +1281,23 @@ void setDefaultValues(bool is_game_specific) {
|
||||
volumeSlider.set(100, is_game_specific);
|
||||
isTrophyPopupDisabled.set(false, is_game_specific);
|
||||
trophyNotificationDuration.set(6.0, is_game_specific);
|
||||
logFilter.set("", is_game_specific);
|
||||
logType.set("sync", is_game_specific);
|
||||
isIdenticalLogGrouped.set("isIdenticalLogGrouped", is_game_specific);
|
||||
userName.set("shadPS4", is_game_specific);
|
||||
isShowSplash.set(false, is_game_specific);
|
||||
isSideTrophy.set("right", is_game_specific);
|
||||
|
||||
// GS - Log
|
||||
logAppend.set(false, is_game_specific);
|
||||
logEnable.set(true, is_game_specific);
|
||||
logFilter.set("", is_game_specific);
|
||||
logMaxSkipDuration.set(5'000, is_game_specific);
|
||||
logSeparate.set(false, is_game_specific);
|
||||
logSkipDuplicate.set(true, is_game_specific);
|
||||
logSync.set(true, is_game_specific);
|
||||
logSizeLimit.set(100_MB, is_game_specific);
|
||||
#ifdef _WIN32
|
||||
logType.set("wincolor", is_game_specific);
|
||||
#endif
|
||||
|
||||
// GS - Input
|
||||
cursorState.set(HideCursorState::Idle, is_game_specific);
|
||||
cursorHideTimeout.set(5, is_game_specific);
|
||||
@ -1281,8 +1339,6 @@ void setDefaultValues(bool is_game_specific) {
|
||||
// GS - Debug
|
||||
isDebugDump.set(false, is_game_specific);
|
||||
isShaderDebug.set(false, is_game_specific);
|
||||
isSeparateLogFilesEnabled.set(false, is_game_specific);
|
||||
logEnabled.set(true, is_game_specific);
|
||||
|
||||
// GS - Settings
|
||||
m_language.set(1, is_game_specific);
|
||||
|
||||
@ -105,12 +105,29 @@ bool isPipelineCacheArchived();
|
||||
void setRdocEnabled(bool enable, bool is_game_specific = false);
|
||||
void setPipelineCacheEnabled(bool enable, bool is_game_specific = false);
|
||||
void setPipelineCacheArchived(bool enable, bool is_game_specific = false);
|
||||
std::string getLogType();
|
||||
void setLogType(const std::string& type, bool is_game_specific = false);
|
||||
bool groupIdenticalLogs();
|
||||
void setGroupIdenticalLogs(bool enable, bool is_game_specific = false);
|
||||
|
||||
// Log
|
||||
bool isLogAppend();
|
||||
void setLogAppend(bool enable, bool is_game_specific = false);
|
||||
bool getLogEnable();
|
||||
void setLogEnable(bool enable, bool is_game_specific = false);
|
||||
std::string getLogFilter();
|
||||
void setLogFilter(const std::string& type, bool is_game_specific = false);
|
||||
u32 getLogMaxSkipDuration();
|
||||
void setLogMaxSkipDuration(u32 duration, bool is_game_specific = false);
|
||||
bool getLogSeparateLogFilesEnabled();
|
||||
void setLogSeparateLogFilesEnabled(bool enabled, bool is_game_specific = false);
|
||||
unsigned long long getLogSizeLimit();
|
||||
void setLogSizeLimit(unsigned long long size, bool is_game_specific = false);
|
||||
bool getLogSkipDuplicate();
|
||||
void setLogSkipDuplicate(bool enable, bool is_game_specific = false);
|
||||
bool isLogSync();
|
||||
void setLogSync(bool sync, bool is_game_specific = false);
|
||||
#ifdef _WIN32
|
||||
std::string getLogType();
|
||||
void setLogType(const std::string& type, bool is_game_specific = false);
|
||||
#endif
|
||||
|
||||
double getTrophyNotificationDuration();
|
||||
void setTrophyNotificationDuration(double newTrophyNotificationDuration,
|
||||
bool is_game_specific = false);
|
||||
@ -122,8 +139,6 @@ void setPadSpkOutputDevice(std::string device, bool is_game_specific = false);
|
||||
std::string getMicDevice();
|
||||
void setCursorHideTimeout(int newcursorHideTimeout, bool is_game_specific = false);
|
||||
void setMicDevice(std::string device, bool is_game_specific = false);
|
||||
void setSeparateLogFilesEnabled(bool enabled, bool is_game_specific = false);
|
||||
bool getSeparateLogFilesEnabled();
|
||||
u32 GetLanguage();
|
||||
void setLanguage(u32 language, bool is_game_specific = false);
|
||||
void setUseSpecialPad(bool use);
|
||||
@ -148,8 +163,6 @@ std::string getDefaultControllerID();
|
||||
void setDefaultControllerID(std::string id);
|
||||
bool getBackgroundControllerInput();
|
||||
void setBackgroundControllerInput(bool enable, bool is_game_specific = false);
|
||||
bool getLoggingEnabled();
|
||||
void setLoggingEnabled(bool enable, bool is_game_specific = false);
|
||||
bool getFsrEnabled();
|
||||
void setFsrEnabled(bool enable, bool is_game_specific = false);
|
||||
bool getRcasEnabled();
|
||||
|
||||
@ -1,396 +0,0 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2014 Citra Emulator Project
|
||||
// SPDX-FileCopyrightText: Copyright 2026 shadPS4 Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#include <chrono>
|
||||
#include <filesystem>
|
||||
#include <mutex>
|
||||
#include <thread>
|
||||
|
||||
#include <fmt/format.h>
|
||||
|
||||
#ifdef _WIN32
|
||||
#include <windows.h> // For OutputDebugStringW
|
||||
#endif
|
||||
|
||||
#include "common/bounded_threadsafe_queue.h"
|
||||
#include "common/debug.h"
|
||||
#include "common/io_file.h"
|
||||
#include "common/logging/backend.h"
|
||||
#include "common/logging/log.h"
|
||||
#include "common/logging/log_entry.h"
|
||||
#include "common/logging/text_formatter.h"
|
||||
#include "common/path_util.h"
|
||||
#include "common/string_util.h"
|
||||
#include "common/thread.h"
|
||||
#include "core/emulator_settings.h"
|
||||
|
||||
namespace Common::Log {
|
||||
|
||||
using namespace Common::FS;
|
||||
|
||||
namespace {
|
||||
|
||||
/**
|
||||
* Backend that writes to stderr and with color
|
||||
*/
|
||||
class ColorConsoleBackend {
|
||||
public:
|
||||
explicit ColorConsoleBackend() = default;
|
||||
|
||||
~ColorConsoleBackend() = default;
|
||||
|
||||
void Write(const Entry& entry) {
|
||||
if (enabled.load(std::memory_order_relaxed)) {
|
||||
PrintColoredMessage(entry);
|
||||
}
|
||||
}
|
||||
|
||||
void Flush() {
|
||||
// stderr shouldn't be buffered
|
||||
}
|
||||
|
||||
void SetEnabled(bool enabled_) {
|
||||
enabled = enabled_;
|
||||
}
|
||||
|
||||
private:
|
||||
std::atomic_bool enabled{true};
|
||||
};
|
||||
|
||||
/**
|
||||
* Backend that writes to a file passed into the constructor
|
||||
*/
|
||||
class FileBackend {
|
||||
public:
|
||||
explicit FileBackend(const std::filesystem::path& filename, bool should_append = false)
|
||||
: file{filename, should_append ? FS::FileAccessMode::Append : FS::FileAccessMode::Create,
|
||||
FS::FileType::TextFile} {}
|
||||
|
||||
~FileBackend() = default;
|
||||
|
||||
void Write(const Entry& entry) {
|
||||
if (!enabled && entry.log_level != Level::Critical) {
|
||||
return;
|
||||
}
|
||||
|
||||
bytes_written += file.WriteString(FormatLogMessage(entry).append(1, '\n'));
|
||||
|
||||
// Prevent logs from exceeding a set maximum size in the event that log entries are spammed.
|
||||
const auto write_limit = 100_MB;
|
||||
const bool write_limit_exceeded = bytes_written > write_limit;
|
||||
if (entry.log_level >= Level::Error || write_limit_exceeded) {
|
||||
if (write_limit_exceeded) {
|
||||
// Stop writing after the write limit is exceeded.
|
||||
// Don't close the file so we can print a stacktrace if necessary
|
||||
enabled = false;
|
||||
}
|
||||
file.Flush();
|
||||
}
|
||||
}
|
||||
|
||||
void Flush() {
|
||||
file.Flush();
|
||||
}
|
||||
|
||||
private:
|
||||
Common::FS::IOFile file;
|
||||
bool enabled = true;
|
||||
std::size_t bytes_written = 0;
|
||||
};
|
||||
|
||||
#ifdef _WIN32
|
||||
/**
|
||||
* Backend that writes to Visual Studio's output window
|
||||
*/
|
||||
class DebuggerBackend {
|
||||
public:
|
||||
explicit DebuggerBackend() = default;
|
||||
|
||||
~DebuggerBackend() = default;
|
||||
|
||||
void Write(const Entry& entry) {
|
||||
::OutputDebugStringW(UTF8ToUTF16W(FormatLogMessage(entry).append(1, '\n')).c_str());
|
||||
}
|
||||
|
||||
void Flush() {}
|
||||
|
||||
void EnableForStacktrace() {}
|
||||
};
|
||||
#endif
|
||||
|
||||
bool initialization_in_progress_suppress_logging = true;
|
||||
|
||||
/**
|
||||
* Static state as a singleton.
|
||||
*/
|
||||
class Impl {
|
||||
public:
|
||||
static Impl& Instance() {
|
||||
if (!instance) {
|
||||
throw std::runtime_error("Using Logging instance before its initialization");
|
||||
}
|
||||
return *instance;
|
||||
}
|
||||
|
||||
static void Initialize(std::string_view log_file) {
|
||||
if (instance) {
|
||||
LOG_WARNING(Log, "Reinitializing logging backend");
|
||||
return;
|
||||
}
|
||||
const auto& log_dir = GetUserPath(PathType::LogDir);
|
||||
std::filesystem::create_directory(log_dir);
|
||||
Filter filter;
|
||||
filter.ParseFilterString(EmulatorSettings.GetLogFilter());
|
||||
const auto& log_file_path = log_file.empty() ? LOG_FILE : log_file;
|
||||
instance = std::unique_ptr<Impl, decltype(&Deleter)>(
|
||||
new Impl(log_dir / log_file_path, filter), Deleter);
|
||||
initialization_in_progress_suppress_logging = false;
|
||||
}
|
||||
|
||||
static void ResetInstance() {
|
||||
initialization_in_progress_suppress_logging = true;
|
||||
instance.reset();
|
||||
}
|
||||
|
||||
static bool IsActive() {
|
||||
return instance != nullptr;
|
||||
}
|
||||
|
||||
static void Start() {
|
||||
instance->StartBackendThread();
|
||||
}
|
||||
|
||||
static void Stop() {
|
||||
instance->StopBackendThread();
|
||||
}
|
||||
|
||||
static void SetAppend() {
|
||||
should_append = true;
|
||||
}
|
||||
|
||||
Impl(const Impl&) = delete;
|
||||
Impl& operator=(const Impl&) = delete;
|
||||
|
||||
Impl(Impl&&) = delete;
|
||||
Impl& operator=(Impl&&) = delete;
|
||||
|
||||
void SetGlobalFilter(const Filter& f) {
|
||||
filter = f;
|
||||
}
|
||||
|
||||
void SetColorConsoleBackendEnabled(bool enabled) {
|
||||
color_console_backend.SetEnabled(enabled);
|
||||
}
|
||||
|
||||
void PushEntry(Class log_class, Level log_level, const char* filename, unsigned int line_num,
|
||||
const char* function, const char* format, const fmt::format_args& args) {
|
||||
if (!filter.CheckMessage(log_class, log_level) || !EmulatorSettings.IsLogEnabled()) {
|
||||
return;
|
||||
}
|
||||
|
||||
const auto message = fmt::vformat(format, args);
|
||||
|
||||
// Propagate important log messages to the profiler
|
||||
if (IsProfilerConnected()) {
|
||||
const auto& msg_str = fmt::format("[{}] {}", GetLogClassName(log_class), message);
|
||||
switch (log_level) {
|
||||
case Level::Warning:
|
||||
TRACE_WARN(msg_str);
|
||||
break;
|
||||
case Level::Error:
|
||||
TRACE_ERROR(msg_str);
|
||||
break;
|
||||
case Level::Critical:
|
||||
TRACE_CRIT(msg_str);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
using std::chrono::duration_cast;
|
||||
using std::chrono::microseconds;
|
||||
using std::chrono::steady_clock;
|
||||
|
||||
if (EmulatorSettings.IsIdenticalLogGrouped()) {
|
||||
std::unique_lock entry_loc(_mutex);
|
||||
|
||||
if (_last_entry.message == message) {
|
||||
++_last_entry.counter;
|
||||
return;
|
||||
}
|
||||
|
||||
if (_last_entry.counter >= 2) {
|
||||
_last_entry.message += " x" + std::to_string(_last_entry.counter);
|
||||
}
|
||||
|
||||
if (_last_entry.counter >= 1) {
|
||||
if (EmulatorSettings.GetLogType() == "async") {
|
||||
message_queue.EmplaceWait(_last_entry);
|
||||
} else {
|
||||
ForEachBackend([this](auto& backend) { backend.Write(this->_last_entry); });
|
||||
std::fflush(stdout);
|
||||
}
|
||||
}
|
||||
|
||||
this->_last_entry = {
|
||||
.timestamp = duration_cast<microseconds>(steady_clock::now() - time_origin),
|
||||
.log_class = log_class,
|
||||
.log_level = log_level,
|
||||
.filename = filename,
|
||||
.line_num = line_num,
|
||||
.function = function,
|
||||
.message = message,
|
||||
.thread = Common::GetCurrentThreadName(),
|
||||
.counter = 1,
|
||||
};
|
||||
} else {
|
||||
const Entry entry = {
|
||||
.timestamp = duration_cast<microseconds>(steady_clock::now() - time_origin),
|
||||
.log_class = log_class,
|
||||
.log_level = log_level,
|
||||
.filename = filename,
|
||||
.line_num = line_num,
|
||||
.function = function,
|
||||
.message = message,
|
||||
.thread = Common::GetCurrentThreadName(),
|
||||
.counter = 1,
|
||||
};
|
||||
|
||||
if (EmulatorSettings.GetLogType() == "async") {
|
||||
message_queue.EmplaceWait(entry);
|
||||
} else {
|
||||
ForEachBackend([&entry](auto& backend) { backend.Write(entry); });
|
||||
std::fflush(stdout);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
Impl(const std::filesystem::path& file_backend_filename, const Filter& filter_)
|
||||
: filter{filter_}, file_backend{file_backend_filename, should_append} {}
|
||||
|
||||
~Impl() = default;
|
||||
|
||||
void StartBackendThread() {
|
||||
backend_thread = std::jthread([this](std::stop_token stop_token) {
|
||||
Common::SetCurrentThreadName("shadPS4:Log");
|
||||
Entry entry;
|
||||
const auto write_logs = [this, &entry]() {
|
||||
ForEachBackend([&entry](auto& backend) { backend.Write(entry); });
|
||||
};
|
||||
while (!stop_token.stop_requested()) {
|
||||
message_queue.PopWait(entry, stop_token);
|
||||
if (entry.filename != nullptr) {
|
||||
write_logs();
|
||||
}
|
||||
}
|
||||
// Drain the logging queue. Only writes out up to MAX_LOGS_TO_WRITE to prevent a
|
||||
// case where a system is repeatedly spamming logs even on close.
|
||||
int max_logs_to_write = filter.IsDebug() ? std::numeric_limits<s32>::max() : 100;
|
||||
while (max_logs_to_write-- && message_queue.TryPop(entry)) {
|
||||
write_logs();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
void StopBackendThread() {
|
||||
if (EmulatorSettings.IsIdenticalLogGrouped()) {
|
||||
// log last message
|
||||
if (_last_entry.counter >= 2) {
|
||||
_last_entry.message += " x" + std::to_string(_last_entry.counter);
|
||||
}
|
||||
|
||||
if (_last_entry.counter >= 1) {
|
||||
if (EmulatorSettings.GetLogType() == "async") {
|
||||
message_queue.EmplaceWait(_last_entry);
|
||||
} else {
|
||||
ForEachBackend([this](auto& backend) { backend.Write(this->_last_entry); });
|
||||
std::fflush(stdout);
|
||||
}
|
||||
}
|
||||
|
||||
this->_last_entry = {};
|
||||
}
|
||||
|
||||
backend_thread.request_stop();
|
||||
if (backend_thread.joinable()) {
|
||||
backend_thread.join();
|
||||
}
|
||||
|
||||
ForEachBackend([](auto& backend) { backend.Flush(); });
|
||||
}
|
||||
|
||||
void ForEachBackend(auto lambda) {
|
||||
#ifdef _WIN32
|
||||
lambda(debugger_backend);
|
||||
#endif
|
||||
lambda(color_console_backend);
|
||||
lambda(file_backend);
|
||||
}
|
||||
|
||||
static void Deleter(Impl* ptr) {
|
||||
delete ptr;
|
||||
}
|
||||
|
||||
static inline std::unique_ptr<Impl, decltype(&Deleter)> instance{nullptr, Deleter};
|
||||
static inline bool should_append{false};
|
||||
|
||||
Filter filter;
|
||||
#ifdef _WIN32
|
||||
DebuggerBackend debugger_backend{};
|
||||
#endif
|
||||
ColorConsoleBackend color_console_backend{};
|
||||
FileBackend file_backend;
|
||||
|
||||
MPSCQueue<Entry> message_queue{};
|
||||
std::chrono::steady_clock::time_point time_origin{std::chrono::steady_clock::now()};
|
||||
std::jthread backend_thread;
|
||||
Entry _last_entry;
|
||||
std::mutex _mutex;
|
||||
};
|
||||
} // namespace
|
||||
|
||||
void Initialize(std::string_view log_file) {
|
||||
Impl::Initialize(log_file.empty() ? LOG_FILE : log_file);
|
||||
}
|
||||
|
||||
bool IsActive() {
|
||||
return Impl::IsActive();
|
||||
}
|
||||
|
||||
void Start() {
|
||||
Impl::Start();
|
||||
}
|
||||
|
||||
void Stop() {
|
||||
Impl::Stop();
|
||||
}
|
||||
|
||||
void Denitializer() {
|
||||
Impl::Stop();
|
||||
Impl::ResetInstance();
|
||||
}
|
||||
|
||||
void SetGlobalFilter(const Filter& filter) {
|
||||
Impl::Instance().SetGlobalFilter(filter);
|
||||
}
|
||||
|
||||
void SetColorConsoleBackendEnabled(bool enabled) {
|
||||
Impl::Instance().SetColorConsoleBackendEnabled(enabled);
|
||||
}
|
||||
|
||||
void SetAppend() {
|
||||
Impl::SetAppend();
|
||||
}
|
||||
|
||||
void FmtLogMessageImpl(Class log_class, Level log_level, const char* filename,
|
||||
unsigned int line_num, const char* function, const char* format,
|
||||
const fmt::format_args& args) {
|
||||
if (!initialization_in_progress_suppress_logging) [[likely]] {
|
||||
Impl::Instance().PushEntry(log_class, log_level, filename, line_num, function, format,
|
||||
args);
|
||||
}
|
||||
}
|
||||
} // namespace Common::Log
|
||||
@ -1,34 +0,0 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2014 Citra Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <string_view>
|
||||
#include "common/logging/filter.h"
|
||||
|
||||
namespace Common::Log {
|
||||
|
||||
class Filter;
|
||||
|
||||
/// Initializes the logging system. This should be the first thing called in main.
|
||||
void Initialize(std::string_view log_file = "");
|
||||
|
||||
bool IsActive();
|
||||
|
||||
/// Starts the logging threads.
|
||||
void Start();
|
||||
|
||||
/// Explictily stops the logger thread and flushes the buffers
|
||||
void Stop();
|
||||
|
||||
/// Closes log files and stops the logger
|
||||
void Denitializer();
|
||||
|
||||
/// The global filter will prevent any messages from even being processed if they are filtered.
|
||||
void SetGlobalFilter(const Filter& filter);
|
||||
|
||||
void SetColorConsoleBackendEnabled(bool enabled);
|
||||
|
||||
void SetAppend();
|
||||
|
||||
} // namespace Common::Log
|
||||
112
src/common/logging/classes.h
Normal file
112
src/common/logging/classes.h
Normal file
@ -0,0 +1,112 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2025-2026 shadPS4 Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <array>
|
||||
|
||||
namespace Common::Log::Class {
|
||||
// clang-format off
|
||||
/// Listing all log classes, if you add here, dont forget ALL_LOGGERS
|
||||
constexpr auto Common = "Common"; ///< Library routines
|
||||
constexpr auto Common_Filesystem = "Common.Filesystem"; ///< Filesystem interface library
|
||||
constexpr auto Common_Memory = "Common.Memory"; ///< Memory mapping and management functions
|
||||
constexpr auto Config = "Config"; ///< Emulator configuration (including commandline)
|
||||
constexpr auto Core = "Core"; ///< LLE emulation core
|
||||
constexpr auto Core_Devices = "Core.Devices"; ///< Devices emulation
|
||||
constexpr auto Core_Linker = "Core.Linker"; ///< The module linker
|
||||
constexpr auto Debug = "Debug"; ///< Debugging tools
|
||||
constexpr auto Frontend = "Frontend"; ///< Emulator UI
|
||||
constexpr auto IPC = "IPC"; ///< IPC
|
||||
constexpr auto ImGui = "ImGui"; ///< ImGui
|
||||
constexpr auto Input = "Input"; ///< Input emulation
|
||||
constexpr auto Kernel = "Kernel"; ///< The HLE implementation of the PS4 kernel.
|
||||
constexpr auto Kernel_Event = "Kernel.Event"; ///< The event management implementation of the kernel.
|
||||
constexpr auto Kernel_Fs = "Kernel.Fs"; ///< The filesystem implementation of the kernel.
|
||||
constexpr auto Kernel_Pthread = "Kernel.Pthread"; ///< The pthread implementation of the kernel.
|
||||
constexpr auto Kernel_Sce = "Kernel.Sce"; ///< The Sony-specific interfaces provided by the kernel.
|
||||
constexpr auto Kernel_Vmm = "Kernel.Vmm"; ///< The virtual memory implementation of the kernel.
|
||||
constexpr auto KeyManager = "KeyManager"; ///< Key management system
|
||||
constexpr auto Lib = "Lib"; ///< HLE implementation of system library. Each major library should have its own subclass.
|
||||
constexpr auto Lib_Ajm = "Lib.Ajm"; ///< The LibSceAjm implementation.
|
||||
constexpr auto Lib_AppContent = "Lib.AppContent"; ///< The LibSceAppContent implementation.
|
||||
constexpr auto Lib_Audio3d = "Lib.Audio3d"; ///< The LibSceAudio3d implementation.
|
||||
constexpr auto Lib_AudioIn = "Lib.AudioIn"; ///< The LibSceAudioIn implementation.
|
||||
constexpr auto Lib_AudioOut = "Lib.AudioOut"; ///< The LibSceAudioOut implementation.
|
||||
constexpr auto Lib_AvPlayer = "Lib.AvPlayer"; ///< The LibSceAvPlayer implementation.
|
||||
constexpr auto Lib_Camera = "Lib.Camera"; ///< The LibCamera implementation.
|
||||
constexpr auto Lib_CommonDlg = "Lib.CommonDlg"; ///< The LibSceCommonDialog implementation.
|
||||
constexpr auto Lib_CompanionHttpd = "Lib.CompanionHttpd"; ///< The LibCompanionHttpd implementation.
|
||||
constexpr auto Lib_CompanionUtil = "Lib.CompanionUtil"; ///< The LibCompanionUtil implementation.
|
||||
constexpr auto Lib_DiscMap = "Lib.DiscMap"; ///< The LibSceDiscMap implementation.
|
||||
constexpr auto Lib_ErrorDialog = "Lib.ErrorDialog"; ///< The LibSceErrorDialog implementation.
|
||||
constexpr auto Lib_Fiber = "Lib.Fiber"; ///< The LibSceFiber implementation.
|
||||
constexpr auto Lib_Font = "Lib.Font"; ///< The libSceFont implementation.
|
||||
constexpr auto Lib_FontFt = "Lib.FontFt"; ///< The libSceFontFt implementation.
|
||||
constexpr auto Lib_GameLiveStreaming = "Lib.GameLiveStreaming"; ///< The LibSceGameLiveStreaming implementation
|
||||
constexpr auto Lib_GnmDriver = "Lib.GnmDriver"; ///< The LibSceGnmDriver implementation.
|
||||
constexpr auto Lib_Hmd = "Lib.Hmd"; ///< The LibSceHmd implementation.
|
||||
constexpr auto Lib_HmdSetupDialog = "Lib.HmdSetupDialog"; ///< The LibSceHmdSetupDialog implementation.
|
||||
constexpr auto Lib_Http = "Lib.Http"; ///< The LibSceHttp implementation.
|
||||
constexpr auto Lib_Http2 = "Lib.Http2"; ///< The LibSceHttp2 implementation.
|
||||
constexpr auto Lib_Ime = "Lib.Ime"; ///< The LibSceIme implementation
|
||||
constexpr auto Lib_ImeDialog = "Lib.ImeDialog"; ///< The LibSceImeDialog implementation.
|
||||
constexpr auto Lib_Jpeg = "Lib.Jpeg"; ///< The LibSceJpeg implementation.
|
||||
constexpr auto Lib_Kernel = "Lib.Kernel"; ///< The LibKernel implementation.
|
||||
constexpr auto Lib_LibcInternal = "Lib.LibcInternal"; ///< The LibcInternal implementation.
|
||||
constexpr auto Lib_Mouse = "Lib.Mouse"; ///< The LibSceMouse implementation
|
||||
constexpr auto Lib_Move = "Lib.Move"; ///< The LibSceMove implementation.
|
||||
constexpr auto Lib_MsgDlg = "Lib.MsgDlg"; ///< The LibSceMsgDialog implementation.
|
||||
constexpr auto Lib_Net = "Lib.Net"; ///< The LibSceNet implementation.
|
||||
constexpr auto Lib_NetCtl = "Lib.NetCtl"; ///< The LibSceNetCtl implementation.
|
||||
constexpr auto Lib_Ngs2 = "Lib.Ngs2"; ///< The LibSceNgs2 implementation.
|
||||
constexpr auto Lib_NpAuth = "Lib.NpAuth"; ///< The LibSceNpAuth implementation
|
||||
constexpr auto Lib_NpCommerce = "Lib.NpCommerce"; ///< The LibSceNpCommerce implementation
|
||||
constexpr auto Lib_NpCommon = "Lib.NpCommon"; ///< The LibSceNpCommon implementation
|
||||
constexpr auto Lib_NpManager = "Lib.NpManager"; ///< The LibSceNpManager implementation
|
||||
constexpr auto Lib_NpMatching2 = "Lib.NpMatching2"; ///< The LibSceNpMatching2 implementation
|
||||
constexpr auto Lib_NpPartner = "Lib.NpPartner"; ///< The LibSceNpPartner implementation
|
||||
constexpr auto Lib_NpParty = "Lib.NpParty"; ///< The LibSceNpParty implementation
|
||||
constexpr auto Lib_NpProfileDialog = "Lib.NpProfileDialog"; ///< The LibSceNpProfileDialog implementation
|
||||
constexpr auto Lib_NpScore = "Lib.NpScore"; ///< The LibSceNpScore implementation
|
||||
constexpr auto Lib_NpSnsFacebookDialog = "Lib.NpSnsFacebookDialog"; ///< The LibSceNpSnsFacebookDialog implementation
|
||||
constexpr auto Lib_NpTrophy = "Lib.NpTrophy"; ///< The LibSceNpTrophy implementation
|
||||
constexpr auto Lib_NpTus = "Lib.NpTus"; ///< The LibSceNpTus implementation
|
||||
constexpr auto Lib_NpWebApi = "Lib.NpWebApi"; ///< The LibSceWebApi implementation
|
||||
constexpr auto Lib_NpWebApi2 = "Lib.NpWebApi2"; ///< The LibSceWebApi2 implementation
|
||||
constexpr auto Lib_Pad = "Lib.Pad"; ///< The LibScePad implementation.
|
||||
constexpr auto Lib_PlayGo = "Lib.PlayGo"; ///< The LibScePlayGo implementation.
|
||||
constexpr auto Lib_PlayGoDialog = "Lib.PlayGoDialog"; ///< The LibScePlayGoDialog implementation.
|
||||
constexpr auto Lib_Png = "Lib.Png"; ///< The LibScePng implementation.
|
||||
constexpr auto Lib_Random = "Lib.Random"; ///< The LibSceRandom implementation.
|
||||
constexpr auto Lib_RazorCpu = "Lib.RazorCpu"; ///< The LibRazorCpu implementation.
|
||||
constexpr auto Lib_Remoteplay = "Lib.Remoteplay"; ///< The LibSceRemotePlay implementation
|
||||
constexpr auto Lib_Rtc = "Lib.Rtc"; ///< The LibSceRtc implementation.
|
||||
constexpr auto Lib_Rudp = "Lib.Rudp"; ///< The LibSceRudp implementation.
|
||||
constexpr auto Lib_SaveData = "Lib.SaveData"; ///< The LibSceSaveData implementation.
|
||||
constexpr auto Lib_SaveDataDialog = "Lib.SaveDataDialog"; ///< The LibSceSaveDataDialog implementation.
|
||||
constexpr auto Lib_Screenshot = "Lib.Screenshot"; ///< The LibSceScreenshot implementation
|
||||
constexpr auto Lib_SharePlay = "Lib.SharePlay"; ///< The LibSceSharePlay implemenation
|
||||
constexpr auto Lib_SigninDialog = "Lib.SigninDialog"; ///< The LibSigninDialog implementation.
|
||||
constexpr auto Lib_Ssl = "Lib.Ssl"; ///< The LibSceSsl implementation.
|
||||
constexpr auto Lib_Ssl2 = "Lib.Ssl2"; ///< The LibSceSsl2 implementation.
|
||||
constexpr auto Lib_SysModule = "Lib.SysModule"; ///< The LibSceSysModule implementation
|
||||
constexpr auto Lib_SystemGesture = "Lib.SystemGesture"; ///< The LibSceSystemGesture implementation.
|
||||
constexpr auto Lib_SystemService = "Lib.SystemService"; ///< The LibSceSystemService implementation.
|
||||
constexpr auto Lib_Usbd = "Lib.Usbd"; ///< The LibSceUsbd implementation.
|
||||
constexpr auto Lib_UserService = "Lib.UserService"; ///< The LibSceUserService implementation.
|
||||
constexpr auto Lib_Vdec2 = "Lib.Vdec2"; ///< The LibSceVideodec2 implementation.
|
||||
constexpr auto Lib_VideoOut = "Lib.VideoOut"; ///< The LibSceVideoOut implementation.
|
||||
constexpr auto Lib_Videodec = "Lib.Videodec"; ///< The LibSceVideodec implementation.
|
||||
constexpr auto Lib_Voice = "Lib.Voice"; ///< The LibSceVoice implementation.
|
||||
constexpr auto Lib_VrTracker = "Lib.VrTracker"; ///< The LibSceVrTracker implementation.
|
||||
constexpr auto Lib_WebBrowserDialog = "Lib.WebBrowserDialog"; ///< The LibSceWebBrowserDialog implementation
|
||||
constexpr auto Lib_Zlib = "Lib.Zlib"; ///< The LibSceZlib implementation.
|
||||
constexpr auto Loader = "Loader"; ///< ROM loader
|
||||
constexpr auto Log = "Log"; ///< Messages about the log system itself
|
||||
constexpr auto Render = "Render"; ///< Video Core
|
||||
constexpr auto Render_Recompiler = "Render.Recompiler"; ///< Shader recompiler
|
||||
constexpr auto Render_Vulkan = "Render.Vulkan"; ///< Vulkan backend
|
||||
constexpr auto Tty = "Tty"; ///< Debug output from emu
|
||||
// clang-format on
|
||||
} // namespace Common::Log::Class
|
||||
@ -1,246 +0,0 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2014 Citra Emulator Project
|
||||
// SPDX-FileCopyrightText: Copyright 2024-2026 shadPS4 Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#include <algorithm>
|
||||
|
||||
#include "common/assert.h"
|
||||
#include "common/logging/filter.h"
|
||||
|
||||
namespace Common::Log {
|
||||
namespace {
|
||||
template <typename It>
|
||||
Level GetLevelByName(const It begin, const It end) {
|
||||
for (u8 i = 0; i < static_cast<u8>(Level::Count); ++i) {
|
||||
const char* level_name = GetLevelName(static_cast<Level>(i));
|
||||
if (std::string_view(begin, end).compare(level_name) == 0) {
|
||||
return static_cast<Level>(i);
|
||||
}
|
||||
}
|
||||
return Level::Count;
|
||||
}
|
||||
|
||||
template <typename It>
|
||||
Class GetClassByName(const It begin, const It end) {
|
||||
for (u8 i = 0; i < static_cast<u8>(Class::Count); ++i) {
|
||||
const char* level_name = GetLogClassName(static_cast<Class>(i));
|
||||
if (std::string_view(begin, end).compare(level_name) == 0) {
|
||||
return static_cast<Class>(i);
|
||||
}
|
||||
}
|
||||
return Class::Count;
|
||||
}
|
||||
|
||||
template <typename Iterator>
|
||||
bool ParseFilterRule(Filter& instance, Iterator begin, Iterator end) {
|
||||
auto level_separator = std::find(begin, end, ':');
|
||||
if (level_separator == end) {
|
||||
LOG_ERROR(Log, "Invalid log filter. Must specify a log level after `:`: {}",
|
||||
std::string_view(begin, end));
|
||||
return false;
|
||||
}
|
||||
|
||||
const Level level = GetLevelByName(level_separator + 1, end);
|
||||
if (level == Level::Count) {
|
||||
LOG_ERROR(Log, "Unknown log level in filter: {}", std::string_view(begin, end));
|
||||
return false;
|
||||
}
|
||||
|
||||
if (std::string_view(begin, level_separator).compare("*") == 0) {
|
||||
instance.ResetAll(level);
|
||||
return true;
|
||||
}
|
||||
|
||||
const Class log_class = GetClassByName(begin, level_separator);
|
||||
if (log_class == Class::Count) {
|
||||
LOG_ERROR(Log, "Unknown log class in filter: {}", std::string(begin, end));
|
||||
return false;
|
||||
}
|
||||
|
||||
instance.SetClassLevel(log_class, level);
|
||||
return true;
|
||||
}
|
||||
} // Anonymous namespace
|
||||
|
||||
/// Macro listing all log classes. Code should define CLS and SUB as desired before invoking this.
|
||||
#define ALL_LOG_CLASSES() \
|
||||
CLS(Log) \
|
||||
CLS(Common) \
|
||||
SUB(Common, Filesystem) \
|
||||
SUB(Common, Memory) \
|
||||
CLS(KeyManager) \
|
||||
CLS(Core) \
|
||||
SUB(Core, Linker) \
|
||||
SUB(Core, Devices) \
|
||||
CLS(Config) \
|
||||
CLS(Debug) \
|
||||
CLS(Kernel) \
|
||||
SUB(Kernel, Pthread) \
|
||||
SUB(Kernel, Vmm) \
|
||||
SUB(Kernel, Fs) \
|
||||
SUB(Kernel, Event) \
|
||||
SUB(Kernel, Sce) \
|
||||
CLS(Lib) \
|
||||
SUB(Lib, LibcInternal) \
|
||||
SUB(Lib, Kernel) \
|
||||
SUB(Lib, Pad) \
|
||||
SUB(Lib, SystemGesture) \
|
||||
SUB(Lib, GnmDriver) \
|
||||
SUB(Lib, SystemService) \
|
||||
SUB(Lib, UserService) \
|
||||
SUB(Lib, VideoOut) \
|
||||
SUB(Lib, CommonDlg) \
|
||||
SUB(Lib, MsgDlg) \
|
||||
SUB(Lib, AudioOut) \
|
||||
SUB(Lib, AudioIn) \
|
||||
SUB(Lib, Net) \
|
||||
SUB(Lib, NetCtl) \
|
||||
SUB(Lib, SaveData) \
|
||||
SUB(Lib, SaveDataDialog) \
|
||||
SUB(Lib, Http) \
|
||||
SUB(Lib, Http2) \
|
||||
SUB(Lib, Ssl) \
|
||||
SUB(Lib, Ssl2) \
|
||||
SUB(Lib, SysModule) \
|
||||
SUB(Lib, Move) \
|
||||
SUB(Lib, NpAuth) \
|
||||
SUB(Lib, NpCommon) \
|
||||
SUB(Lib, NpCommerce) \
|
||||
SUB(Lib, NpManager) \
|
||||
SUB(Lib, NpMatching2) \
|
||||
SUB(Lib, NpScore) \
|
||||
SUB(Lib, NpTrophy) \
|
||||
SUB(Lib, NpTus) \
|
||||
SUB(Lib, NpWebApi) \
|
||||
SUB(Lib, NpWebApi2) \
|
||||
SUB(Lib, NpProfileDialog) \
|
||||
SUB(Lib, NpSnsFacebookDialog) \
|
||||
SUB(Lib, NpPartner) \
|
||||
SUB(Lib, Screenshot) \
|
||||
SUB(Lib, AppContent) \
|
||||
SUB(Lib, Rtc) \
|
||||
SUB(Lib, Rudp) \
|
||||
SUB(Lib, DiscMap) \
|
||||
SUB(Lib, Png) \
|
||||
SUB(Lib, Jpeg) \
|
||||
SUB(Lib, PlayGo) \
|
||||
SUB(Lib, PlayGoDialog) \
|
||||
SUB(Lib, Random) \
|
||||
SUB(Lib, Usbd) \
|
||||
SUB(Lib, Ajm) \
|
||||
SUB(Lib, ErrorDialog) \
|
||||
SUB(Lib, ImeDialog) \
|
||||
SUB(Lib, AvPlayer) \
|
||||
SUB(Lib, Ngs2) \
|
||||
SUB(Lib, Audio3d) \
|
||||
SUB(Lib, Ime) \
|
||||
SUB(Lib, GameLiveStreaming) \
|
||||
SUB(Lib, Remoteplay) \
|
||||
SUB(Lib, SharePlay) \
|
||||
SUB(Lib, Fiber) \
|
||||
SUB(Lib, Vdec2) \
|
||||
SUB(Lib, Videodec) \
|
||||
SUB(Lib, RazorCpu) \
|
||||
SUB(Lib, Mouse) \
|
||||
SUB(Lib, WebBrowserDialog) \
|
||||
SUB(Lib, NpParty) \
|
||||
SUB(Lib, Zlib) \
|
||||
SUB(Lib, Hmd) \
|
||||
SUB(Lib, Font) \
|
||||
SUB(Lib, FontFt) \
|
||||
SUB(Lib, HmdSetupDialog) \
|
||||
SUB(Lib, SigninDialog) \
|
||||
SUB(Lib, Camera) \
|
||||
SUB(Lib, CompanionHttpd) \
|
||||
SUB(Lib, CompanionUtil) \
|
||||
SUB(Lib, Voice) \
|
||||
SUB(Lib, VrTracker) \
|
||||
CLS(Frontend) \
|
||||
CLS(Render) \
|
||||
SUB(Render, Vulkan) \
|
||||
SUB(Render, Recompiler) \
|
||||
CLS(ImGui) \
|
||||
CLS(Input) \
|
||||
CLS(Tty) \
|
||||
CLS(Loader)
|
||||
|
||||
// GetClassName is a macro defined by Windows.h, grrr...
|
||||
const char* GetLogClassName(Class log_class) {
|
||||
switch (log_class) {
|
||||
#define CLS(x) \
|
||||
case Class::x: \
|
||||
return #x;
|
||||
#define SUB(x, y) \
|
||||
case Class::x##_##y: \
|
||||
return #x "." #y;
|
||||
ALL_LOG_CLASSES()
|
||||
#undef CLS
|
||||
#undef SUB
|
||||
case Class::Count:
|
||||
default:
|
||||
break;
|
||||
}
|
||||
UNREACHABLE();
|
||||
}
|
||||
|
||||
const char* GetLevelName(Level log_level) {
|
||||
#define LVL(x) \
|
||||
case Level::x: \
|
||||
return #x
|
||||
switch (log_level) {
|
||||
LVL(Trace);
|
||||
LVL(Debug);
|
||||
LVL(Info);
|
||||
LVL(Warning);
|
||||
LVL(Error);
|
||||
LVL(Critical);
|
||||
case Level::Count:
|
||||
default:
|
||||
break;
|
||||
}
|
||||
#undef LVL
|
||||
UNREACHABLE();
|
||||
}
|
||||
|
||||
Filter::Filter(Level default_level) {
|
||||
ResetAll(default_level);
|
||||
}
|
||||
|
||||
void Filter::ResetAll(Level level) {
|
||||
class_levels.fill(level);
|
||||
}
|
||||
|
||||
void Filter::SetClassLevel(Class log_class, Level level) {
|
||||
class_levels[static_cast<std::size_t>(log_class)] = level;
|
||||
}
|
||||
|
||||
void Filter::ParseFilterString(std::string_view filter_view) {
|
||||
auto clause_begin = filter_view.cbegin();
|
||||
while (clause_begin != filter_view.cend()) {
|
||||
auto clause_end = std::find(clause_begin, filter_view.cend(), ' ');
|
||||
|
||||
// If clause isn't empty
|
||||
if (clause_end != clause_begin) {
|
||||
ParseFilterRule(*this, clause_begin, clause_end);
|
||||
}
|
||||
|
||||
if (clause_end != filter_view.cend()) {
|
||||
// Skip over the whitespace
|
||||
++clause_end;
|
||||
}
|
||||
clause_begin = clause_end;
|
||||
}
|
||||
}
|
||||
|
||||
bool Filter::CheckMessage(Class log_class, Level level) const {
|
||||
return static_cast<u8>(level) >=
|
||||
static_cast<u8>(class_levels[static_cast<std::size_t>(log_class)]);
|
||||
}
|
||||
|
||||
bool Filter::IsDebug() const {
|
||||
return std::any_of(class_levels.begin(), class_levels.end(), [](const Level& l) {
|
||||
return static_cast<u8>(l) <= static_cast<u8>(Level::Debug);
|
||||
});
|
||||
}
|
||||
|
||||
} // namespace Common::Log
|
||||
@ -1,66 +0,0 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2014 Citra Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <array>
|
||||
#include <cstddef>
|
||||
#include <string_view>
|
||||
#include "common/logging/types.h"
|
||||
|
||||
namespace Common::Log {
|
||||
|
||||
/**
|
||||
* Returns the name of the passed log class as a C-string. Subclasses are separated by periods
|
||||
* instead of underscores as in the enumeration.
|
||||
*/
|
||||
const char* GetLogClassName(Class log_class);
|
||||
|
||||
/**
|
||||
* Returns the name of the passed log level as a C-string.
|
||||
*/
|
||||
const char* GetLevelName(Level log_level);
|
||||
|
||||
/**
|
||||
* Implements a log message filter which allows different log classes to have different minimum
|
||||
* severity levels. The filter can be changed at runtime and can be parsed from a string to allow
|
||||
* editing via the interface or loading from a configuration file.
|
||||
*/
|
||||
class Filter {
|
||||
public:
|
||||
/// Initializes the filter with all classes having `default_level` as the minimum level.
|
||||
explicit Filter(Level default_level = Level::Info);
|
||||
|
||||
/// Resets the filter so that all classes have `level` as the minimum displayed level.
|
||||
void ResetAll(Level level);
|
||||
|
||||
/// Sets the minimum level of `log_class` (and not of its subclasses) to `level`.
|
||||
void SetClassLevel(Class log_class, Level level);
|
||||
|
||||
/**
|
||||
* Parses a filter string and applies it to this filter.
|
||||
*
|
||||
* A filter string consists of a space-separated list of filter rules, each of the format
|
||||
* `<class>:<level>`. `<class>` is a log class name, with subclasses separated using periods.
|
||||
* `*` is allowed as a class name and will reset all filters to the specified level. `<level>`
|
||||
* a severity level name which will be set as the minimum logging level of the matched classes.
|
||||
* Rules are applied left to right, with each rule overriding previous ones in the sequence.
|
||||
*
|
||||
* A few examples of filter rules:
|
||||
* - `*:Info` -- Resets the level of all classes to Info.
|
||||
* - `Service:Info` -- Sets the level of Service to Info.
|
||||
* - `Service.FS:Trace` -- Sets the level of the Service.FS class to Trace.
|
||||
*/
|
||||
void ParseFilterString(std::string_view filter_view);
|
||||
|
||||
/// Matches class/level combination against the filter, returning true if it passed.
|
||||
bool CheckMessage(Class log_class, Level level) const;
|
||||
|
||||
/// Returns true if any logging classes are set to debug
|
||||
bool IsDebug() const;
|
||||
|
||||
private:
|
||||
std::array<Level, static_cast<std::size_t>(Class::Count)> class_levels;
|
||||
};
|
||||
|
||||
} // namespace Common::Log
|
||||
203
src/common/logging/log.cpp
Normal file
203
src/common/logging/log.cpp
Normal file
@ -0,0 +1,203 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2025-2026 shadPS4 Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#include <cstdlib>
|
||||
#include <string>
|
||||
|
||||
#include "common/assert.h"
|
||||
#include "common/config.h"
|
||||
#include "common/logging/log.h"
|
||||
#include "common/logging/thread_name_formatter.h"
|
||||
#include "common/types.h"
|
||||
#include "core/emulator_settings.h"
|
||||
|
||||
namespace Common::Log {
|
||||
bool g_should_append = false;
|
||||
|
||||
static std::shared_ptr<spdlog_stdout> g_console_sink;
|
||||
static std::shared_ptr<spdlog::sinks::basic_file_sink_mt> g_shad_file_sink;
|
||||
|
||||
std::unordered_map<std::string_view, std::shared_ptr<spdlog::logger>> ALL_LOGGERS{
|
||||
{Class::Common, nullptr},
|
||||
{Class::Common_Filesystem, nullptr},
|
||||
{Class::Common_Memory, nullptr},
|
||||
{Class::Config, nullptr},
|
||||
{Class::Core, nullptr},
|
||||
{Class::Core_Devices, nullptr},
|
||||
{Class::Core_Linker, nullptr},
|
||||
{Class::Debug, nullptr},
|
||||
{Class::Frontend, nullptr},
|
||||
{Class::IPC, nullptr},
|
||||
{Class::ImGui, nullptr},
|
||||
{Class::Input, nullptr},
|
||||
{Class::Kernel, nullptr},
|
||||
{Class::Kernel_Event, nullptr},
|
||||
{Class::Kernel_Fs, nullptr},
|
||||
{Class::Kernel_Pthread, nullptr},
|
||||
{Class::Kernel_Sce, nullptr},
|
||||
{Class::Kernel_Vmm, nullptr},
|
||||
{Class::KeyManager, nullptr},
|
||||
{Class::Lib, nullptr},
|
||||
{Class::Lib_Ajm, nullptr},
|
||||
{Class::Lib_AppContent, nullptr},
|
||||
{Class::Lib_Audio3d, nullptr},
|
||||
{Class::Lib_AudioIn, nullptr},
|
||||
{Class::Lib_AudioOut, nullptr},
|
||||
{Class::Lib_AvPlayer, nullptr},
|
||||
{Class::Lib_Camera, nullptr},
|
||||
{Class::Lib_CommonDlg, nullptr},
|
||||
{Class::Lib_CompanionHttpd, nullptr},
|
||||
{Class::Lib_CompanionUtil, nullptr},
|
||||
{Class::Lib_DiscMap, nullptr},
|
||||
{Class::Lib_ErrorDialog, nullptr},
|
||||
{Class::Lib_Fiber, nullptr},
|
||||
{Class::Lib_Font, nullptr},
|
||||
{Class::Lib_FontFt, nullptr},
|
||||
{Class::Lib_GameLiveStreaming, nullptr},
|
||||
{Class::Lib_GnmDriver, nullptr},
|
||||
{Class::Lib_Hmd, nullptr},
|
||||
{Class::Lib_HmdSetupDialog, nullptr},
|
||||
{Class::Lib_Http, nullptr},
|
||||
{Class::Lib_Http2, nullptr},
|
||||
{Class::Lib_Ime, nullptr},
|
||||
{Class::Lib_ImeDialog, nullptr},
|
||||
{Class::Lib_Jpeg, nullptr},
|
||||
{Class::Lib_Kernel, nullptr},
|
||||
{Class::Lib_LibcInternal, nullptr},
|
||||
{Class::Lib_Mouse, nullptr},
|
||||
{Class::Lib_Move, nullptr},
|
||||
{Class::Lib_MsgDlg, nullptr},
|
||||
{Class::Lib_Net, nullptr},
|
||||
{Class::Lib_NetCtl, nullptr},
|
||||
{Class::Lib_Ngs2, nullptr},
|
||||
{Class::Lib_NpAuth, nullptr},
|
||||
{Class::Lib_NpCommerce, nullptr},
|
||||
{Class::Lib_NpCommon, nullptr},
|
||||
{Class::Lib_NpManager, nullptr},
|
||||
{Class::Lib_NpMatching2, nullptr},
|
||||
{Class::Lib_NpPartner, nullptr},
|
||||
{Class::Lib_NpParty, nullptr},
|
||||
{Class::Lib_NpProfileDialog, nullptr},
|
||||
{Class::Lib_NpScore, nullptr},
|
||||
{Class::Lib_NpSnsFacebookDialog, nullptr},
|
||||
{Class::Lib_NpTrophy, nullptr},
|
||||
{Class::Lib_NpTus, nullptr},
|
||||
{Class::Lib_NpWebApi, nullptr},
|
||||
{Class::Lib_NpWebApi2, nullptr},
|
||||
{Class::Lib_Pad, nullptr},
|
||||
{Class::Lib_PlayGo, nullptr},
|
||||
{Class::Lib_PlayGoDialog, nullptr},
|
||||
{Class::Lib_Png, nullptr},
|
||||
{Class::Lib_Random, nullptr},
|
||||
{Class::Lib_RazorCpu, nullptr},
|
||||
{Class::Lib_Remoteplay, nullptr},
|
||||
{Class::Lib_Rtc, nullptr},
|
||||
{Class::Lib_Rudp, nullptr},
|
||||
{Class::Lib_SaveData, nullptr},
|
||||
{Class::Lib_SaveDataDialog, nullptr},
|
||||
{Class::Lib_Screenshot, nullptr},
|
||||
{Class::Lib_SharePlay, nullptr},
|
||||
{Class::Lib_SigninDialog, nullptr},
|
||||
{Class::Lib_Ssl, nullptr},
|
||||
{Class::Lib_Ssl2, nullptr},
|
||||
{Class::Lib_SysModule, nullptr},
|
||||
{Class::Lib_SystemGesture, nullptr},
|
||||
{Class::Lib_SystemService, nullptr},
|
||||
{Class::Lib_Usbd, nullptr},
|
||||
{Class::Lib_UserService, nullptr},
|
||||
{Class::Lib_Vdec2, nullptr},
|
||||
{Class::Lib_VideoOut, nullptr},
|
||||
{Class::Lib_Videodec, nullptr},
|
||||
{Class::Lib_Voice, nullptr},
|
||||
{Class::Lib_VrTracker, nullptr},
|
||||
{Class::Lib_WebBrowserDialog, nullptr},
|
||||
{Class::Lib_Zlib, nullptr},
|
||||
{Class::Loader, nullptr},
|
||||
{Class::Log, nullptr},
|
||||
{Class::Render, nullptr},
|
||||
{Class::Render_Recompiler, nullptr},
|
||||
{Class::Render_Vulkan, nullptr},
|
||||
{Class::Tty, nullptr},
|
||||
};
|
||||
|
||||
void Setup(std::string_view log_filename) {
|
||||
static bool already_registered = false;
|
||||
|
||||
if (!already_registered) {
|
||||
already_registered = true;
|
||||
std::atexit(Shutdown);
|
||||
std::at_quick_exit(Shutdown);
|
||||
}
|
||||
|
||||
#ifdef _WIN32
|
||||
if (EmulatorSettings.GetLogType() == "wincolor") {
|
||||
g_console_sink = std::make_shared<spdlog::sinks::wincolor_stdout_sink_mt>();
|
||||
} else {
|
||||
g_console_sink = std::make_shared<spdlog::sinks::msvc_sink_mt>();
|
||||
}
|
||||
#else
|
||||
g_console_sink = std::make_shared<spdlog_stdout>();
|
||||
#endif
|
||||
|
||||
g_console_sink->set_formatter(std::make_unique<thread_name_formatter>(UNLIMITED_SIZE));
|
||||
|
||||
g_shad_file_sink = std::make_shared<spdlog::sinks::basic_file_sink_mt>(
|
||||
(GetUserPath(Common::FS::PathType::LogDir) / log_filename).string(), !g_should_append);
|
||||
g_shad_file_sink->set_formatter(
|
||||
std::make_unique<thread_name_formatter>(EmulatorSettings.GetLogSizeLimit()));
|
||||
|
||||
std::initializer_list<spdlog::sink_ptr> sinks{g_console_sink, g_shad_file_sink};
|
||||
|
||||
std::initializer_list<spdlog::sink_ptr> async_sink{std::make_shared<spdlog::sinks::async_sink>(
|
||||
spdlog::sinks::async_sink::config{.sinks = sinks})};
|
||||
|
||||
std::initializer_list<spdlog::sink_ptr> dup_filter{
|
||||
std::make_shared<spdlog::sinks::dup_filter_sink_mt>(
|
||||
std::chrono::milliseconds(EmulatorSettings.GetLogMaxSkipDuration()),
|
||||
EmulatorSettings.IsLogSync() ? sinks : async_sink)};
|
||||
|
||||
spdlog::level default_log_level = spdlog::level::info;
|
||||
std::unordered_map<std::string, spdlog::level> log_level_per_class;
|
||||
|
||||
if (EmulatorSettings.IsLogEnable()) {
|
||||
for (const auto class_level : std::views::split(EmulatorSettings.GetLogFilter(), ',')) {
|
||||
const auto class_level_pair =
|
||||
std::views::split(class_level, '=') | std::ranges::to<std::vector<std::string>>();
|
||||
|
||||
if (class_level_pair.size() == 1) {
|
||||
default_log_level = spdlog::level_from_str(class_level_pair.front() |
|
||||
std::ranges::to<std::string>());
|
||||
} else {
|
||||
log_level_per_class[class_level_pair.front() | std::ranges::to<std::string>()] =
|
||||
spdlog::level_from_str(class_level_pair.back() |
|
||||
std::ranges::to<std::string>());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for (auto& [name, logger] : ALL_LOGGERS) {
|
||||
logger = std::make_shared<spdlog::logger>(
|
||||
std::string(name), EmulatorSettings.IsLogSkipDuplicate()
|
||||
? dup_filter
|
||||
: (EmulatorSettings.IsLogSync() ? sinks : async_sink));
|
||||
|
||||
if (EmulatorSettings.IsLogEnable()) {
|
||||
const auto level_it = log_level_per_class.find(std::string(name));
|
||||
|
||||
logger->set_level(level_it != log_level_per_class.end() ? level_it->second
|
||||
: default_log_level);
|
||||
} else {
|
||||
logger->set_level(spdlog::level::off);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void Shutdown() {
|
||||
for (auto& logger : ALL_LOGGERS | std::views::values) {
|
||||
logger.reset();
|
||||
}
|
||||
|
||||
g_shad_file_sink.reset();
|
||||
g_console_sink.reset();
|
||||
}
|
||||
} // namespace Common::Log
|
||||
@ -1,70 +1,58 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2014 Citra Emulator Project
|
||||
// SPDX-FileCopyrightText: Copyright 2025-2026 shadPS4 Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <algorithm>
|
||||
#include <array>
|
||||
#include <string_view>
|
||||
#include <unordered_map>
|
||||
#include <vector>
|
||||
#include <spdlog/details/fmt_helper.h>
|
||||
#include <spdlog/sinks/async_sink.h>
|
||||
#include <spdlog/sinks/basic_file_sink.h>
|
||||
#include <spdlog/sinks/dup_filter_sink.h>
|
||||
#include <spdlog/sinks/null_sink.h>
|
||||
#include <spdlog/sinks/stdout_color_sinks.h>
|
||||
|
||||
#include "common/logging/formatter.h"
|
||||
#include "common/logging/types.h"
|
||||
#ifdef _WIN32
|
||||
#include <spdlog/sinks/msvc_sink.h>
|
||||
#include <spdlog/sinks/wincolor_sink.h>
|
||||
using spdlog_stdout = spdlog::sinks::sink;
|
||||
#else
|
||||
using spdlog_stdout = spdlog::sinks::stdout_color_sink_mt;
|
||||
#endif
|
||||
#include <spdlog/spdlog.h>
|
||||
|
||||
#include "common/logging/classes.h"
|
||||
#include "common/path_util.h"
|
||||
|
||||
namespace Common::Log {
|
||||
extern bool g_should_append;
|
||||
extern std::unordered_map<std::string_view, std::shared_ptr<spdlog::logger>> ALL_LOGGERS;
|
||||
|
||||
constexpr const char* TrimSourcePath(std::string_view source) {
|
||||
const auto rfind = [source](const std::string_view match) {
|
||||
return source.rfind(match) == source.npos ? 0 : (source.rfind(match) + match.size());
|
||||
};
|
||||
auto idx = std::max({rfind("/"), rfind("\\")});
|
||||
return source.data() + idx;
|
||||
}
|
||||
void Setup(std::string_view log_filename);
|
||||
|
||||
/// Logs a message to the global logger, using fmt
|
||||
void FmtLogMessageImpl(Class log_class, Level log_level, const char* filename,
|
||||
unsigned int line_num, const char* function, const char* format,
|
||||
const fmt::format_args& args);
|
||||
|
||||
template <typename... Args>
|
||||
void FmtLogMessage(Class log_class, Level log_level, const char* filename, unsigned int line_num,
|
||||
const char* function, const char* format, const Args&... args) {
|
||||
FmtLogMessageImpl(log_class, log_level, filename, line_num, function, format,
|
||||
fmt::make_format_args(args...));
|
||||
}
|
||||
void Shutdown();
|
||||
|
||||
void Redirect(const std::string& name);
|
||||
} // namespace Common::Log
|
||||
|
||||
// Define the fmt lib macros
|
||||
#define LOG_GENERIC(log_class, log_level, ...) \
|
||||
Common::Log::FmtLogMessage(log_class, log_level, Common::Log::TrimSourcePath(__FILE__), \
|
||||
__LINE__, __func__, __VA_ARGS__)
|
||||
SPDLOG_LOGGER_CALL(Common::Log::ALL_LOGGERS[log_class], log_level, __VA_ARGS__)
|
||||
|
||||
#ifdef _DEBUG
|
||||
#define LOG_TRACE(log_class, ...) \
|
||||
Common::Log::FmtLogMessage(Common::Log::Class::log_class, Common::Log::Level::Trace, \
|
||||
Common::Log::TrimSourcePath(__FILE__), __LINE__, __func__, \
|
||||
__VA_ARGS__)
|
||||
LOG_GENERIC(Common::Log::Class::log_class, spdlog::level::trace, __VA_ARGS__)
|
||||
#else
|
||||
#define LOG_TRACE(log_class, fmt, ...) (void(0))
|
||||
#define LOG_TRACE(log_class, ...) (void(0))
|
||||
#endif
|
||||
|
||||
#define LOG_DEBUG(log_class, ...) \
|
||||
Common::Log::FmtLogMessage(Common::Log::Class::log_class, Common::Log::Level::Debug, \
|
||||
Common::Log::TrimSourcePath(__FILE__), __LINE__, __func__, \
|
||||
__VA_ARGS__)
|
||||
LOG_GENERIC(Common::Log::Class::log_class, spdlog::level::debug, __VA_ARGS__)
|
||||
#define LOG_INFO(log_class, ...) \
|
||||
Common::Log::FmtLogMessage(Common::Log::Class::log_class, Common::Log::Level::Info, \
|
||||
Common::Log::TrimSourcePath(__FILE__), __LINE__, __func__, \
|
||||
__VA_ARGS__)
|
||||
LOG_GENERIC(Common::Log::Class::log_class, spdlog::level::info, __VA_ARGS__)
|
||||
#define LOG_WARNING(log_class, ...) \
|
||||
Common::Log::FmtLogMessage(Common::Log::Class::log_class, Common::Log::Level::Warning, \
|
||||
Common::Log::TrimSourcePath(__FILE__), __LINE__, __func__, \
|
||||
__VA_ARGS__)
|
||||
LOG_GENERIC(Common::Log::Class::log_class, spdlog::level::warn, __VA_ARGS__)
|
||||
#define LOG_ERROR(log_class, ...) \
|
||||
Common::Log::FmtLogMessage(Common::Log::Class::log_class, Common::Log::Level::Error, \
|
||||
Common::Log::TrimSourcePath(__FILE__), __LINE__, __func__, \
|
||||
__VA_ARGS__)
|
||||
LOG_GENERIC(Common::Log::Class::log_class, spdlog::level::err, __VA_ARGS__)
|
||||
#define LOG_CRITICAL(log_class, ...) \
|
||||
Common::Log::FmtLogMessage(Common::Log::Class::log_class, Common::Log::Level::Critical, \
|
||||
Common::Log::TrimSourcePath(__FILE__), __LINE__, __func__, \
|
||||
__VA_ARGS__)
|
||||
LOG_GENERIC(Common::Log::Class::log_class, spdlog::level::critical, __VA_ARGS__)
|
||||
|
||||
@ -1,28 +0,0 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2014 Citra Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <chrono>
|
||||
|
||||
#include "common/logging/types.h"
|
||||
|
||||
namespace Common::Log {
|
||||
|
||||
/**
|
||||
* A log entry. Log entries are store in a structured format to permit more varied output
|
||||
* formatting on different frontends, as well as facilitating filtering and aggregation.
|
||||
*/
|
||||
struct Entry {
|
||||
std::chrono::microseconds timestamp;
|
||||
Class log_class{};
|
||||
Level log_level{};
|
||||
const char* filename = nullptr;
|
||||
u32 line_num = 0;
|
||||
std::string function;
|
||||
std::string message;
|
||||
std::string thread;
|
||||
u32 counter = 0;
|
||||
};
|
||||
|
||||
} // namespace Common::Log
|
||||
@ -1,110 +0,0 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2014 Citra Emulator Project
|
||||
// SPDX-FileCopyrightText: Copyright 2026 shadPS4 Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#include <array>
|
||||
#include <cstdio>
|
||||
|
||||
#ifdef _WIN32
|
||||
#include <windows.h>
|
||||
#endif
|
||||
|
||||
#include "common/assert.h"
|
||||
#include "common/logging/filter.h"
|
||||
#include "common/logging/log.h"
|
||||
#include "common/logging/log_entry.h"
|
||||
#include "common/logging/text_formatter.h"
|
||||
|
||||
namespace Common::Log {
|
||||
|
||||
std::string FormatLogMessage(const Entry& entry) {
|
||||
const u32 time_seconds = static_cast<u32>(entry.timestamp.count() / 1000000);
|
||||
const u32 time_fractional = static_cast<u32>(entry.timestamp.count() % 1000000);
|
||||
|
||||
const char* class_name = GetLogClassName(entry.log_class);
|
||||
const char* level_name = GetLevelName(entry.log_level);
|
||||
|
||||
return fmt::format("[{}] <{}> ({}) {}:{} {}: {}", class_name, level_name, entry.thread,
|
||||
entry.filename, entry.line_num, entry.function, entry.message);
|
||||
}
|
||||
|
||||
void PrintMessage(const Entry& entry) {
|
||||
const auto str = FormatLogMessage(entry).append(1, '\n');
|
||||
fputs(str.c_str(), stdout);
|
||||
}
|
||||
|
||||
void PrintColoredMessage(const Entry& entry) {
|
||||
#ifdef _WIN32
|
||||
HANDLE console_handle = GetStdHandle(STD_ERROR_HANDLE);
|
||||
if (console_handle == INVALID_HANDLE_VALUE) {
|
||||
return;
|
||||
}
|
||||
|
||||
CONSOLE_SCREEN_BUFFER_INFO original_info{};
|
||||
GetConsoleScreenBufferInfo(console_handle, &original_info);
|
||||
|
||||
WORD color = 0;
|
||||
switch (entry.log_level) {
|
||||
case Level::Trace: // Grey
|
||||
color = FOREGROUND_INTENSITY;
|
||||
break;
|
||||
case Level::Debug: // Cyan
|
||||
color = FOREGROUND_GREEN | FOREGROUND_BLUE;
|
||||
break;
|
||||
case Level::Info: // Bright gray
|
||||
color = FOREGROUND_RED | FOREGROUND_GREEN | FOREGROUND_BLUE;
|
||||
break;
|
||||
case Level::Warning: // Bright yellow
|
||||
color = FOREGROUND_RED | FOREGROUND_GREEN | FOREGROUND_INTENSITY;
|
||||
break;
|
||||
case Level::Error: // Bright red
|
||||
color = FOREGROUND_RED | FOREGROUND_INTENSITY;
|
||||
break;
|
||||
case Level::Critical: // Bright magenta
|
||||
color = FOREGROUND_RED | FOREGROUND_BLUE | FOREGROUND_INTENSITY;
|
||||
break;
|
||||
case Level::Count:
|
||||
UNREACHABLE();
|
||||
}
|
||||
|
||||
SetConsoleTextAttribute(console_handle, color);
|
||||
#else
|
||||
#define ESC "\x1b"
|
||||
const char* color = "";
|
||||
switch (entry.log_level) {
|
||||
case Level::Trace: // Grey
|
||||
color = ESC "[1;30m";
|
||||
break;
|
||||
case Level::Debug: // Cyan
|
||||
color = ESC "[0;36m";
|
||||
break;
|
||||
case Level::Info: // Bright gray
|
||||
color = ESC "[0;37m";
|
||||
break;
|
||||
case Level::Warning: // Bright yellow
|
||||
color = ESC "[1;33m";
|
||||
break;
|
||||
case Level::Error: // Bright red
|
||||
color = ESC "[1;31m";
|
||||
break;
|
||||
case Level::Critical: // Bright magenta
|
||||
color = ESC "[1;35m";
|
||||
break;
|
||||
case Level::Count:
|
||||
UNREACHABLE();
|
||||
}
|
||||
|
||||
fputs(color, stdout);
|
||||
#endif
|
||||
|
||||
PrintMessage(entry);
|
||||
|
||||
#ifdef _WIN32
|
||||
SetConsoleTextAttribute(console_handle, original_info.wAttributes);
|
||||
#else
|
||||
fputs(ESC "[0m", stdout);
|
||||
#undef ESC
|
||||
#endif
|
||||
}
|
||||
|
||||
} // namespace Common::Log
|
||||
@ -1,21 +0,0 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2014 Citra Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <string>
|
||||
|
||||
namespace Common::Log {
|
||||
|
||||
struct Entry;
|
||||
|
||||
/// Formats a log entry into the provided text buffer.
|
||||
std::string FormatLogMessage(const Entry& entry);
|
||||
|
||||
/// Formats and prints a log entry to stderr.
|
||||
void PrintMessage(const Entry& entry);
|
||||
|
||||
/// Prints the same message as `PrintMessage`, but colored according to the severity level.
|
||||
void PrintColoredMessage(const Entry& entry);
|
||||
|
||||
} // namespace Common::Log
|
||||
68
src/common/logging/thread_name_formatter.h
Normal file
68
src/common/logging/thread_name_formatter.h
Normal file
@ -0,0 +1,68 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2025-2026 shadPS4 Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <memory>
|
||||
#include <string_view>
|
||||
#include <spdlog/common.h>
|
||||
#include <spdlog/details/log_msg.h>
|
||||
#include <spdlog/formatter.h>
|
||||
|
||||
#include "common/thread.h"
|
||||
|
||||
namespace Common::Log {
|
||||
static constexpr unsigned long long UNLIMITED_SIZE = 0;
|
||||
|
||||
struct thread_name_formatter : spdlog::formatter {
|
||||
~thread_name_formatter() override = default;
|
||||
|
||||
thread_name_formatter(unsigned long long size_limit) : _size_limit(size_limit) {}
|
||||
|
||||
void format(const spdlog::details::log_msg& msg, spdlog::memory_buf_t& dest) override {
|
||||
if (_size_limit != UNLIMITED_SIZE && _current_size >= _size_limit) {
|
||||
return;
|
||||
}
|
||||
|
||||
msg.color_range_start = dest.size();
|
||||
|
||||
dest.push_back('[');
|
||||
spdlog::details::fmt_helper::append_string_view(msg.logger_name, dest);
|
||||
dest.push_back(']');
|
||||
dest.push_back(' ');
|
||||
dest.push_back('<');
|
||||
spdlog::details::fmt_helper::append_string_view(spdlog::to_string_view(msg.log_level),
|
||||
dest);
|
||||
dest.push_back('>');
|
||||
dest.push_back(' ');
|
||||
dest.push_back('(');
|
||||
spdlog::details::fmt_helper::append_string_view(GetCurrentThreadName(), dest);
|
||||
dest.push_back(')');
|
||||
dest.push_back(' ');
|
||||
spdlog::details::fmt_helper::append_string_view(msg.source.short_filename, dest);
|
||||
dest.push_back(':');
|
||||
spdlog::details::fmt_helper::append_int(msg.source.line, dest);
|
||||
dest.push_back(' ');
|
||||
spdlog::details::fmt_helper::append_string_view(
|
||||
std::string_view(msg.source.funcname).contains("(anonymous class)::operator()")
|
||||
? "lambda"
|
||||
: msg.source.funcname,
|
||||
dest);
|
||||
dest.push_back(':');
|
||||
dest.push_back(' ');
|
||||
spdlog::details::fmt_helper::append_string_view(msg.payload, dest);
|
||||
spdlog::details::fmt_helper::append_string_view(spdlog::details::os::default_eol, dest);
|
||||
|
||||
msg.color_range_end = dest.size();
|
||||
|
||||
_current_size += dest.size();
|
||||
}
|
||||
|
||||
std::unique_ptr<formatter> clone() const override {
|
||||
return std::make_unique<thread_name_formatter>(_size_limit);
|
||||
}
|
||||
|
||||
const unsigned long long _size_limit;
|
||||
unsigned long long _current_size = 0;
|
||||
};
|
||||
} // namespace Common::Log
|
||||
@ -1,136 +0,0 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2023 Citra Emulator Project
|
||||
// SPDX-FileCopyrightText: Copyright 2024-2026 shadPS4 Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "common/types.h"
|
||||
|
||||
namespace Common::Log {
|
||||
|
||||
/// Specifies the severity or level of detail of the log message.
|
||||
enum class Level : u8 {
|
||||
Trace, ///< Extremely detailed and repetitive debugging information that is likely to
|
||||
///< pollute logs.
|
||||
Debug, ///< Less detailed debugging information.
|
||||
Info, ///< Status information from important points during execution.
|
||||
Warning, ///< Minor or potential problems found during execution of a task.
|
||||
Error, ///< Major problems found during execution of a task that prevent it from being
|
||||
///< completed.
|
||||
Critical, ///< Major problems during execution that threaten the stability of the entire
|
||||
///< application.
|
||||
|
||||
Count, ///< Total number of logging levels
|
||||
};
|
||||
|
||||
/**
|
||||
* Specifies the sub-system that generated the log message.
|
||||
*
|
||||
* @note If you add a new entry here, also add a corresponding one to `ALL_LOG_CLASSES` in
|
||||
* filter.cpp.
|
||||
*/
|
||||
enum class Class : u8 {
|
||||
Log, ///< Messages about the log system itself
|
||||
Common, ///< Library routines
|
||||
Common_Filesystem, ///< Filesystem interface library
|
||||
Common_Memory, ///< Memory mapping and management functions
|
||||
KeyManager, ///< Key management system
|
||||
Core, ///< LLE emulation core
|
||||
Core_Linker, ///< The module linker
|
||||
Core_Devices, ///< Devices emulation
|
||||
Config, ///< Emulator configuration (including commandline)
|
||||
Debug, ///< Debugging tools
|
||||
Kernel, ///< The HLE implementation of the PS4 kernel.
|
||||
Kernel_Pthread, ///< The pthread implementation of the kernel.
|
||||
Kernel_Fs, ///< The filesystem implementation of the kernel.
|
||||
Kernel_Vmm, ///< The virtual memory implementation of the kernel.
|
||||
Kernel_Event, ///< The event management implementation of the kernel.
|
||||
Kernel_Sce, ///< The Sony-specific interfaces provided by the kernel.
|
||||
Lib, ///< HLE implementation of system library. Each major library
|
||||
///< should have its own subclass.
|
||||
Lib_LibcInternal, ///< The LibcInternal implementation.
|
||||
Lib_Kernel, ///< The LibKernel implementation.
|
||||
Lib_Pad, ///< The LibScePad implementation.
|
||||
Lib_SystemGesture, ///< The LibSceSystemGesture implementation.
|
||||
Lib_GnmDriver, ///< The LibSceGnmDriver implementation.
|
||||
Lib_SystemService, ///< The LibSceSystemService implementation.
|
||||
Lib_UserService, ///< The LibSceUserService implementation.
|
||||
Lib_VideoOut, ///< The LibSceVideoOut implementation.
|
||||
Lib_CommonDlg, ///< The LibSceCommonDialog implementation.
|
||||
Lib_MsgDlg, ///< The LibSceMsgDialog implementation.
|
||||
Lib_AudioOut, ///< The LibSceAudioOut implementation.
|
||||
Lib_AudioIn, ///< The LibSceAudioIn implementation.
|
||||
Lib_Move, ///< The LibSceMove implementation.
|
||||
Lib_Net, ///< The LibSceNet implementation.
|
||||
Lib_NetCtl, ///< The LibSceNetCtl implementation.
|
||||
Lib_SaveData, ///< The LibSceSaveData implementation.
|
||||
Lib_SaveDataDialog, ///< The LibSceSaveDataDialog implementation.
|
||||
Lib_Ssl, ///< The LibSceSsl implementation.
|
||||
Lib_Ssl2, ///< The LibSceSsl2 implementation.
|
||||
Lib_Http, ///< The LibSceHttp implementation.
|
||||
Lib_Http2, ///< The LibSceHttp2 implementation.
|
||||
Lib_SysModule, ///< The LibSceSysModule implementation
|
||||
Lib_NpCommon, ///< The LibSceNpCommon implementation
|
||||
Lib_NpCommerce, ///< The LibSceNpCommerce implementation
|
||||
Lib_NpAuth, ///< The LibSceNpAuth implementation
|
||||
Lib_NpManager, ///< The LibSceNpManager implementation
|
||||
Lib_NpMatching2, ///< The LibSceNpMatching2 implementation
|
||||
Lib_NpScore, ///< The LibSceNpScore implementation
|
||||
Lib_NpTrophy, ///< The LibSceNpTrophy implementation
|
||||
Lib_NpTus, ///< The LibSceNpTus implementation
|
||||
Lib_NpWebApi, ///< The LibSceWebApi implementation
|
||||
Lib_NpWebApi2, ///< The LibSceWebApi2 implementation
|
||||
Lib_NpProfileDialog, ///< The LibSceNpProfileDialog implementation
|
||||
Lib_NpSnsFacebookDialog, ///< The LibSceNpSnsFacebookDialog implementation
|
||||
Lib_Screenshot, ///< The LibSceScreenshot implementation
|
||||
Lib_AppContent, ///< The LibSceAppContent implementation.
|
||||
Lib_Rtc, ///< The LibSceRtc implementation.
|
||||
Lib_Rudp, ///< The LibSceRudp implementation.
|
||||
Lib_DiscMap, ///< The LibSceDiscMap implementation.
|
||||
Lib_Png, ///< The LibScePng implementation.
|
||||
Lib_Jpeg, ///< The LibSceJpeg implementation.
|
||||
Lib_PlayGo, ///< The LibScePlayGo implementation.
|
||||
Lib_PlayGoDialog, ///< The LibScePlayGoDialog implementation.
|
||||
Lib_Random, ///< The LibSceRandom implementation.
|
||||
Lib_Usbd, ///< The LibSceUsbd implementation.
|
||||
Lib_Ajm, ///< The LibSceAjm implementation.
|
||||
Lib_ErrorDialog, ///< The LibSceErrorDialog implementation.
|
||||
Lib_ImeDialog, ///< The LibSceImeDialog implementation.
|
||||
Lib_AvPlayer, ///< The LibSceAvPlayer implementation.
|
||||
Lib_Ngs2, ///< The LibSceNgs2 implementation.
|
||||
Lib_Audio3d, ///< The LibSceAudio3d implementation.
|
||||
Lib_Ime, ///< The LibSceIme implementation
|
||||
Lib_GameLiveStreaming, ///< The LibSceGameLiveStreaming implementation
|
||||
Lib_Remoteplay, ///< The LibSceRemotePlay implementation
|
||||
Lib_SharePlay, ///< The LibSceSharePlay implemenation
|
||||
Lib_Fiber, ///< The LibSceFiber implementation.
|
||||
Lib_Vdec2, ///< The LibSceVideodec2 implementation.
|
||||
Lib_Videodec, ///< The LibSceVideodec implementation.
|
||||
Lib_Voice, ///< The LibSceVoice implementation.
|
||||
Lib_RazorCpu, ///< The LibRazorCpu implementation.
|
||||
Lib_Mouse, ///< The LibSceMouse implementation
|
||||
Lib_WebBrowserDialog, ///< The LibSceWebBrowserDialog implementation
|
||||
Lib_NpParty, ///< The LibSceNpParty implementation
|
||||
Lib_NpPartner, ///< The LibSceNpPartner implementation
|
||||
Lib_Zlib, ///< The LibSceZlib implementation.
|
||||
Lib_Hmd, ///< The LibSceHmd implementation.
|
||||
Lib_HmdSetupDialog, ///< The LibSceHmdSetupDialog implementation.
|
||||
Lib_SigninDialog, ///< The LibSigninDialog implementation.
|
||||
Lib_Camera, ///< The LibCamera implementation.
|
||||
Lib_CompanionHttpd, ///< The LibCompanionHttpd implementation.
|
||||
Lib_CompanionUtil, ///< The LibCompanionUtil implementation.
|
||||
Lib_VrTracker, ///< The LibSceVrTracker implementation.
|
||||
Lib_Font, ///< The libSceFont implementation.
|
||||
Lib_FontFt, ///< The libSceFontFt implementation.
|
||||
Frontend, ///< Emulator UI
|
||||
Render, ///< Video Core
|
||||
Render_Vulkan, ///< Vulkan backend
|
||||
Render_Recompiler, ///< Shader recompiler
|
||||
ImGui, ///< ImGui
|
||||
Loader, ///< ROM loader
|
||||
Input, ///< Input emulation
|
||||
Tty, ///< Debug output from emu
|
||||
Count ///< Total number of logging classes
|
||||
};
|
||||
|
||||
} // namespace Common::Log
|
||||
@ -6,6 +6,7 @@
|
||||
#include "common/logging/log.h"
|
||||
#include "common/path_util.h"
|
||||
#include "common/scope_exit.h"
|
||||
#include "common/types.h"
|
||||
|
||||
#ifdef __APPLE__
|
||||
#include <CoreFoundation/CFBundle.h>
|
||||
|
||||
@ -12,6 +12,7 @@
|
||||
#include "text_editor.h"
|
||||
|
||||
#define IMGUI_DEFINE_MATH_OPERATORS
|
||||
#include "common/types.h"
|
||||
#include "imgui.h" // for imGui::GetCurrentWindow()
|
||||
|
||||
// TODO
|
||||
|
||||
@ -8,6 +8,7 @@
|
||||
#include <common/path_util.h>
|
||||
#include <common/scm_rev.h>
|
||||
#include <toml.hpp>
|
||||
#include "common/logging/formatter.h"
|
||||
#include "common/logging/log.h"
|
||||
#include "emulator_settings.h"
|
||||
#include "emulator_state.h"
|
||||
@ -55,6 +56,10 @@ std::optional<T> get_optional(const toml::value& v, const std::string& key) {
|
||||
if (it->second.is_integer()) {
|
||||
return static_cast<u32>(toml::get<unsigned int>(it->second));
|
||||
}
|
||||
} else if constexpr (std::is_same_v<T, unsigned long long>) {
|
||||
if (it->second.is_integer()) {
|
||||
return static_cast<long long>(toml::get<unsigned long long>(it->second));
|
||||
}
|
||||
} else if constexpr (std::is_same_v<T, double>) {
|
||||
if (it->second.is_floating()) {
|
||||
return toml::get<double>(it->second);
|
||||
@ -221,6 +226,7 @@ void EmulatorSettingsImpl::SetAddonInstallDir(const std::filesystem::path& dir)
|
||||
// ── Game-specific override management ────────────────────────────────
|
||||
void EmulatorSettingsImpl::ClearGameSpecificOverrides() {
|
||||
ClearGroupOverrides(m_general);
|
||||
ClearGroupOverrides(m_log);
|
||||
ClearGroupOverrides(m_debug);
|
||||
ClearGroupOverrides(m_input);
|
||||
ClearGroupOverrides(m_audio);
|
||||
@ -242,6 +248,8 @@ void EmulatorSettingsImpl::ResetGameSpecificValue(const std::string& key) {
|
||||
};
|
||||
if (tryGroup(m_general))
|
||||
return;
|
||||
if (tryGroup(m_log))
|
||||
return;
|
||||
if (tryGroup(m_debug))
|
||||
return;
|
||||
if (tryGroup(m_input))
|
||||
@ -268,6 +276,10 @@ bool EmulatorSettingsImpl::Save(const std::string& serial) {
|
||||
SaveGroupGameSpecific(m_general, generalObj);
|
||||
j["General"] = generalObj;
|
||||
|
||||
json logObj = json::object();
|
||||
SaveGroupGameSpecific(m_log, logObj);
|
||||
j["Log"] = logObj;
|
||||
|
||||
json debugObj = json::object();
|
||||
SaveGroupGameSpecific(m_debug, debugObj);
|
||||
j["Debug"] = debugObj;
|
||||
@ -305,6 +317,7 @@ bool EmulatorSettingsImpl::Save(const std::string& serial) {
|
||||
|
||||
json j;
|
||||
j["General"] = m_general;
|
||||
j["Log"] = m_log;
|
||||
j["Debug"] = m_debug;
|
||||
j["Input"] = m_input;
|
||||
j["Audio"] = m_audio;
|
||||
@ -366,6 +379,7 @@ bool EmulatorSettingsImpl::Load(const std::string& serial) {
|
||||
};
|
||||
|
||||
mergeGroup(m_general, "General");
|
||||
mergeGroup(m_log, "Log");
|
||||
mergeGroup(m_debug, "Debug");
|
||||
mergeGroup(m_input, "Input");
|
||||
mergeGroup(m_audio, "Audio");
|
||||
@ -446,6 +460,8 @@ bool EmulatorSettingsImpl::Load(const std::string& serial) {
|
||||
// time without ever touching the base values.
|
||||
if (gj.contains("General"))
|
||||
ApplyGroupOverrides(m_general, gj.at("General"), changed);
|
||||
if (gj.contains("Log"))
|
||||
ApplyGroupOverrides(m_log, gj.at("Log"), changed);
|
||||
if (gj.contains("Debug"))
|
||||
ApplyGroupOverrides(m_debug, gj.at("Debug"), changed);
|
||||
if (gj.contains("Input"))
|
||||
@ -469,6 +485,7 @@ bool EmulatorSettingsImpl::Load(const std::string& serial) {
|
||||
|
||||
void EmulatorSettingsImpl::SetDefaultValues() {
|
||||
m_general = GeneralSettings{};
|
||||
m_log = LogSettings{};
|
||||
m_debug = DebugSettings{};
|
||||
m_input = InputSettings{};
|
||||
m_audio = AudioSettings{};
|
||||
@ -502,9 +519,6 @@ bool EmulatorSettingsImpl::TransferSettings() {
|
||||
setFromToml(s.trophy_popup_disabled, general, "isTrophyPopupDisabled");
|
||||
setFromToml(s.trophy_notification_duration, general, "trophyNotificationDuration");
|
||||
setFromToml(s.discord_rpc_enabled, general, "enableDiscordRPC");
|
||||
setFromToml(s.log_filter, general, "logFilter");
|
||||
setFromToml(s.log_type, general, "logType");
|
||||
setFromToml(s.identical_log_grouped, general, "isIdenticalLogGrouped");
|
||||
setFromToml(s.show_splash, general, "showSplash");
|
||||
setFromToml(s.trophy_notification_side, general, "sideTrophy");
|
||||
setFromToml(s.connected_to_network, general, "isConnectedToNetwork");
|
||||
@ -514,6 +528,23 @@ bool EmulatorSettingsImpl::TransferSettings() {
|
||||
// setFromToml(s.defaultControllerID, general, "defaultControllerID");
|
||||
}
|
||||
|
||||
if (og_data.contains("Log")) {
|
||||
const toml::value& log = og_data.at("Log");
|
||||
auto& s = m_log;
|
||||
|
||||
setFromToml(s.append, log, "append");
|
||||
setFromToml(s.enable, log, "enable");
|
||||
setFromToml(s.filter, log, "filter");
|
||||
setFromToml(s.max_skip_duration, log, "maxSkipDuration");
|
||||
setFromToml(s.separate, log, "separate");
|
||||
setFromToml(s.size_limit, log, "sizeLimit");
|
||||
setFromToml(s.skip_duplicate, log, "skipDuplicate");
|
||||
setFromToml(s.sync, log, "sync");
|
||||
#ifdef _WIN32
|
||||
setFromToml(s.type, log, "type");
|
||||
#endif
|
||||
}
|
||||
|
||||
if (og_data.contains("Input")) {
|
||||
const toml::value& input = og_data.at("Input");
|
||||
auto& s = m_input;
|
||||
@ -584,9 +615,7 @@ bool EmulatorSettingsImpl::TransferSettings() {
|
||||
auto& s = m_debug;
|
||||
|
||||
setFromToml(s.debug_dump, debug, "DebugDump");
|
||||
setFromToml(s.separate_logging_enabled, debug, "isSeparateLogFilesEnabled");
|
||||
setFromToml(s.shader_collect, debug, "CollectShader");
|
||||
setFromToml(s.log_enabled, debug, "logEnabled");
|
||||
setFromToml(m_general.show_fps_counter, debug, "showFpsCounter");
|
||||
}
|
||||
|
||||
@ -689,6 +718,7 @@ std::vector<std::string> EmulatorSettingsImpl::GetAllOverrideableKeys() const {
|
||||
keys.push_back(item.key);
|
||||
};
|
||||
addGroup(m_general.GetOverrideableFields());
|
||||
addGroup(m_log.GetOverrideableFields());
|
||||
addGroup(m_debug.GetOverrideableFields());
|
||||
addGroup(m_input.GetOverrideableFields());
|
||||
addGroup(m_audio.GetOverrideableFields());
|
||||
|
||||
@ -183,10 +183,7 @@ struct GeneralSettings {
|
||||
Setting<bool> trophy_popup_disabled{false};
|
||||
Setting<double> trophy_notification_duration{6.0};
|
||||
Setting<std::string> trophy_notification_side{"right"};
|
||||
Setting<std::string> log_filter{""};
|
||||
Setting<std::string> log_type{"sync"};
|
||||
Setting<bool> show_splash{false};
|
||||
Setting<bool> identical_log_grouped{true};
|
||||
Setting<bool> connected_to_network{false};
|
||||
Setting<bool> discord_rpc_enabled{false};
|
||||
Setting<bool> show_fps_counter{false};
|
||||
@ -207,10 +204,6 @@ struct GeneralSettings {
|
||||
&GeneralSettings::trophy_popup_disabled),
|
||||
make_override<GeneralSettings>("trophy_notification_duration",
|
||||
&GeneralSettings::trophy_notification_duration),
|
||||
make_override<GeneralSettings>("log_filter", &GeneralSettings::log_filter),
|
||||
make_override<GeneralSettings>("log_type", &GeneralSettings::log_type),
|
||||
make_override<GeneralSettings>("identical_log_grouped",
|
||||
&GeneralSettings::identical_log_grouped),
|
||||
make_override<GeneralSettings>("show_splash", &GeneralSettings::show_splash),
|
||||
make_override<GeneralSettings>("trophy_notification_side",
|
||||
&GeneralSettings::trophy_notification_side),
|
||||
@ -218,35 +211,71 @@ struct GeneralSettings {
|
||||
&GeneralSettings::connected_to_network)};
|
||||
}
|
||||
};
|
||||
|
||||
NLOHMANN_DEFINE_TYPE_NON_INTRUSIVE(GeneralSettings, install_dirs, addon_install_dir, home_dir,
|
||||
sys_modules_dir, font_dir, volume_slider, neo_mode, dev_kit_mode,
|
||||
extra_dmem_in_mbytes, shad_net_enabled, trophy_popup_disabled,
|
||||
trophy_notification_duration, log_filter, log_type, show_splash,
|
||||
identical_log_grouped, trophy_notification_side,
|
||||
connected_to_network, discord_rpc_enabled, show_fps_counter,
|
||||
console_language, big_picture_scale, shadnet_server)
|
||||
trophy_notification_duration, show_splash,
|
||||
trophy_notification_side, connected_to_network,
|
||||
discord_rpc_enabled, show_fps_counter, console_language,
|
||||
big_picture_scale, shadnet_server)
|
||||
|
||||
// -------------------------------
|
||||
// Log settings
|
||||
// -------------------------------
|
||||
struct LogSettings {
|
||||
Setting<bool> append{false}; // specific
|
||||
Setting<bool> enable{true}; // specific
|
||||
Setting<std::string> filter{""};
|
||||
Setting<u32> max_skip_duration{5'000};
|
||||
Setting<bool> separate{false}; // specific
|
||||
Setting<unsigned long long> size_limit{100_MB};
|
||||
Setting<bool> skip_duplicate{true};
|
||||
Setting<bool> sync{true};
|
||||
#ifdef _WIN32
|
||||
Setting<std::string> type{"wincolor"};
|
||||
#endif
|
||||
|
||||
// return a vector of override descriptors (runtime, but tiny)
|
||||
std::vector<OverrideItem> GetOverrideableFields() const {
|
||||
return std::vector<OverrideItem>{
|
||||
make_override<LogSettings>("append", &LogSettings::append),
|
||||
make_override<LogSettings>("enable", &LogSettings::enable),
|
||||
make_override<LogSettings>("filter", &LogSettings::filter),
|
||||
make_override<LogSettings>("max_skip_duration", &LogSettings::max_skip_duration),
|
||||
make_override<LogSettings>("separate", &LogSettings::separate),
|
||||
make_override<LogSettings>("size_limit", &LogSettings::size_limit),
|
||||
make_override<LogSettings>("skip_duplicate", &LogSettings::skip_duplicate),
|
||||
make_override<LogSettings>("sync", &LogSettings::sync),
|
||||
#ifdef _WIN32
|
||||
make_override<LogSettings>("type", &LogSettings::type),
|
||||
#endif
|
||||
};
|
||||
}
|
||||
};
|
||||
#ifdef _WIN32
|
||||
NLOHMANN_DEFINE_TYPE_NON_INTRUSIVE(LogSettings, append, enable, filter, max_skip_duration, separate,
|
||||
size_limit, skip_duplicate, sync, type)
|
||||
#else
|
||||
NLOHMANN_DEFINE_TYPE_NON_INTRUSIVE(LogSettings, append, enable, filter, max_skip_duration, separate,
|
||||
size_limit, skip_duplicate, sync)
|
||||
#endif
|
||||
|
||||
// -------------------------------
|
||||
// Debug settings
|
||||
// -------------------------------
|
||||
struct DebugSettings {
|
||||
Setting<bool> separate_logging_enabled{false}; // specific
|
||||
Setting<bool> debug_dump{false}; // specific
|
||||
Setting<bool> shader_collect{false}; // specific
|
||||
Setting<bool> log_enabled{true}; // specific
|
||||
Setting<std::string> config_version{""}; // specific
|
||||
Setting<bool> debug_dump{false}; // specific
|
||||
Setting<bool> shader_collect{false}; // specific
|
||||
Setting<std::string> config_version{""}; // specific
|
||||
|
||||
std::vector<OverrideItem> GetOverrideableFields() const {
|
||||
return std::vector<OverrideItem>{
|
||||
make_override<DebugSettings>("debug_dump", &DebugSettings::debug_dump),
|
||||
make_override<DebugSettings>("shader_collect", &DebugSettings::shader_collect),
|
||||
make_override<DebugSettings>("separate_logging_enabled",
|
||||
&DebugSettings::separate_logging_enabled),
|
||||
make_override<DebugSettings>("log_enabled", &DebugSettings::log_enabled)};
|
||||
make_override<DebugSettings>("shader_collect", &DebugSettings::shader_collect)};
|
||||
}
|
||||
};
|
||||
NLOHMANN_DEFINE_TYPE_NON_INTRUSIVE(DebugSettings, separate_logging_enabled, debug_dump,
|
||||
shader_collect, log_enabled, config_version)
|
||||
NLOHMANN_DEFINE_TYPE_NON_INTRUSIVE(DebugSettings, debug_dump, shader_collect, config_version)
|
||||
|
||||
// -------------------------------
|
||||
// Input settings
|
||||
@ -466,6 +495,7 @@ public:
|
||||
|
||||
private:
|
||||
GeneralSettings m_general{};
|
||||
LogSettings m_log{};
|
||||
DebugSettings m_debug{};
|
||||
InputSettings m_input{};
|
||||
AudioSettings m_audio{};
|
||||
@ -556,9 +586,6 @@ public:
|
||||
SETTING_FORWARD(m_general, TrophyNotificationDuration, trophy_notification_duration)
|
||||
SETTING_FORWARD(m_general, TrophyNotificationSide, trophy_notification_side)
|
||||
SETTING_FORWARD_BOOL(m_general, ShowSplash, show_splash)
|
||||
SETTING_FORWARD_BOOL(m_general, IdenticalLogGrouped, identical_log_grouped)
|
||||
SETTING_FORWARD(m_general, LogFilter, log_filter)
|
||||
SETTING_FORWARD(m_general, LogType, log_type)
|
||||
SETTING_FORWARD_BOOL(m_general, ConnectedToNetwork, connected_to_network)
|
||||
SETTING_FORWARD_BOOL(m_general, DiscordRPCEnabled, discord_rpc_enabled)
|
||||
SETTING_FORWARD_BOOL(m_general, ShowFpsCounter, show_fps_counter)
|
||||
@ -566,6 +593,19 @@ public:
|
||||
SETTING_FORWARD(m_general, BigPictureScale, big_picture_scale)
|
||||
SETTING_FORWARD(m_general, ShadNetServer, shadnet_server)
|
||||
|
||||
// Log settings
|
||||
SETTING_FORWARD_BOOL(m_log, LogAppend, append)
|
||||
SETTING_FORWARD_BOOL(m_log, LogEnable, enable)
|
||||
SETTING_FORWARD(m_log, LogFilter, filter)
|
||||
SETTING_FORWARD(m_log, LogMaxSkipDuration, max_skip_duration)
|
||||
SETTING_FORWARD_BOOL(m_log, LogSeparate, separate)
|
||||
SETTING_FORWARD(m_log, LogSizeLimit, size_limit)
|
||||
SETTING_FORWARD_BOOL(m_log, LogSkipDuplicate, skip_duplicate)
|
||||
SETTING_FORWARD_BOOL(m_log, LogSync, sync)
|
||||
#ifdef _WIN32
|
||||
SETTING_FORWARD(m_log, LogType, type)
|
||||
#endif
|
||||
|
||||
// Audio settings
|
||||
SETTING_FORWARD(m_audio, AudioBackend, audio_backend)
|
||||
SETTING_FORWARD(m_audio, SDLMicDevice, sdl_mic_device)
|
||||
@ -576,10 +616,8 @@ public:
|
||||
SETTING_FORWARD(m_audio, OpenALPadSpkOutputDevice, openal_padSpk_output_device)
|
||||
|
||||
// Debug settings
|
||||
SETTING_FORWARD_BOOL(m_debug, SeparateLoggingEnabled, separate_logging_enabled)
|
||||
SETTING_FORWARD_BOOL(m_debug, DebugDump, debug_dump)
|
||||
SETTING_FORWARD_BOOL(m_debug, ShaderCollect, shader_collect)
|
||||
SETTING_FORWARD_BOOL(m_debug, LogEnabled, log_enabled)
|
||||
SETTING_FORWARD(m_debug, ConfigVersion, config_version)
|
||||
|
||||
// GPU Settings
|
||||
|
||||
@ -158,7 +158,7 @@ AjmJob AjmStatisticsJobFromBatchBuffer(u32 instance_id, AjmBatchBuffer batch_buf
|
||||
break;
|
||||
}
|
||||
default:
|
||||
UNREACHABLE_MSG("Unknown chunk: {}", header.ident);
|
||||
UNREACHABLE_MSG("Unknown chunk: {}", +header.ident);
|
||||
}
|
||||
}
|
||||
|
||||
@ -257,7 +257,7 @@ AjmJob AjmJobFromBatchBuffer(u32 instance_id, AjmBatchBuffer batch_buffer) {
|
||||
break;
|
||||
}
|
||||
default:
|
||||
UNREACHABLE_MSG("Unknown chunk: {}", header.ident);
|
||||
UNREACHABLE_MSG("Unknown chunk: {}", +header.ident);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -12,8 +12,8 @@
|
||||
namespace Libraries::Ime {
|
||||
|
||||
static std::queue<OrbisImeEvent> g_ime_events;
|
||||
static ImeState g_ime_state{};
|
||||
static ImeUi g_ime_ui;
|
||||
static std::unique_ptr<ImeState> g_ime_state;
|
||||
static std::unique_ptr<ImeUi> g_ime_ui;
|
||||
|
||||
class ImeHandler {
|
||||
public:
|
||||
@ -67,14 +67,14 @@ public:
|
||||
}*/
|
||||
|
||||
if (ime_mode) {
|
||||
g_ime_state = ImeState(&m_param.ime, &m_param.ime_ext);
|
||||
g_ime_ui = ImeUi(&g_ime_state, &m_param.ime, &m_param.ime_ext);
|
||||
g_ime_state = std::make_unique<ImeState>(&m_param.ime, &m_param.ime_ext);
|
||||
g_ime_ui = std::make_unique<ImeUi>(g_ime_state.get(), &m_param.ime, &m_param.ime_ext);
|
||||
|
||||
// Queue the Open event so it is delivered on next sceImeUpdate
|
||||
LOG_DEBUG(Lib_Ime, "IME Event queued: Open rect x={}, y={}, w={}, h={}",
|
||||
openEvent.param.rect.x, openEvent.param.rect.y, openEvent.param.rect.width,
|
||||
openEvent.param.rect.height);
|
||||
g_ime_state.SendEvent(&openEvent);
|
||||
g_ime_state->SendEvent(&openEvent);
|
||||
}
|
||||
}
|
||||
|
||||
@ -84,11 +84,11 @@ public:
|
||||
return Error::OK;
|
||||
}
|
||||
|
||||
std::unique_lock<std::mutex> lock{g_ime_state.queue_mutex};
|
||||
std::unique_lock<std::mutex> lock{g_ime_state->queue_mutex};
|
||||
|
||||
while (!g_ime_state.event_queue.empty()) {
|
||||
OrbisImeEvent event = g_ime_state.event_queue.front();
|
||||
g_ime_state.event_queue.pop();
|
||||
while (!g_ime_state->event_queue.empty()) {
|
||||
OrbisImeEvent event = g_ime_state->event_queue.front();
|
||||
g_ime_state->event_queue.pop();
|
||||
Execute(handler, &event, false);
|
||||
}
|
||||
|
||||
@ -118,7 +118,7 @@ public:
|
||||
LOG_WARNING(Lib_Ime, "ImeHandler::SetText received null text pointer");
|
||||
return Error::INVALID_ADDRESS;
|
||||
}
|
||||
g_ime_state.SetText(text, length);
|
||||
g_ime_state->SetText(text, length);
|
||||
return Error::OK;
|
||||
}
|
||||
|
||||
@ -127,7 +127,7 @@ public:
|
||||
LOG_WARNING(Lib_Ime, "ImeHandler::SetCaret received null caret pointer");
|
||||
return Error::INVALID_ADDRESS;
|
||||
}
|
||||
g_ime_state.SetCaret(caret->index);
|
||||
g_ime_state->SetCaret(caret->index);
|
||||
return Error::OK;
|
||||
}
|
||||
|
||||
@ -185,8 +185,8 @@ Error PS4_SYSV_ABI sceImeClose() {
|
||||
LOG_ERROR(Lib_Ime, "Failed to close IME handler, it is still open");
|
||||
return Error::INTERNAL;
|
||||
}
|
||||
g_ime_ui = ImeUi();
|
||||
g_ime_state = ImeState();
|
||||
g_ime_ui = std::make_unique<ImeUi>();
|
||||
g_ime_state = std::make_unique<ImeState>();
|
||||
|
||||
LOG_DEBUG(Lib_Ime, "IME closed successfully");
|
||||
return Error::OK;
|
||||
|
||||
@ -12,6 +12,7 @@
|
||||
#define MINIMP3_IMPLEMENTATION
|
||||
#include <minimp3.h>
|
||||
|
||||
#include "common/logging/formatter.h"
|
||||
#include "common/path_util.h"
|
||||
#include "core/emulator_settings.h"
|
||||
#include "core/libraries/np/trophy_ui.h"
|
||||
|
||||
@ -10,8 +10,8 @@
|
||||
#include "save_backup.h"
|
||||
#include "save_instance.h"
|
||||
|
||||
#include "common/logging/formatter.h"
|
||||
#include "common/logging/log.h"
|
||||
#include "common/logging/log_entry.h"
|
||||
#include "common/polyfill_thread.h"
|
||||
#include "common/thread.h"
|
||||
|
||||
|
||||
@ -11,7 +11,6 @@
|
||||
#include <hwinfo/hwinfo.h>
|
||||
|
||||
#include "common/debug.h"
|
||||
#include "common/logging/backend.h"
|
||||
#include "common/logging/log.h"
|
||||
#include "common/thread.h"
|
||||
#include "core/emulator_settings.h"
|
||||
@ -245,12 +244,11 @@ void Emulator::Run(std::filesystem::path file, std::vector<std::string> args,
|
||||
EmulatorSettings.Load(id);
|
||||
|
||||
// Initialize logging as soon as possible
|
||||
if (!id.empty() && EmulatorSettings.IsSeparateLoggingEnabled()) {
|
||||
Common::Log::Initialize(id + ".log");
|
||||
} else {
|
||||
Common::Log::Initialize();
|
||||
if (!id.empty() && EmulatorSettings.IsLogSeparate()) {
|
||||
Common::Log::Shutdown();
|
||||
Common::Log::Setup(id + ".log");
|
||||
}
|
||||
Common::Log::Start();
|
||||
|
||||
if (!std::filesystem::exists(file)) {
|
||||
LOG_CRITICAL(Loader, "eboot.bin does not exist: {}",
|
||||
std::filesystem::absolute(file).string());
|
||||
@ -266,12 +264,15 @@ void Emulator::Run(std::filesystem::path file, std::vector<std::string> args,
|
||||
LOG_INFO(Config, "Game-specific config used: {}",
|
||||
EmulatorState::GetInstance()->IsGameSpecifigConfigUsed());
|
||||
|
||||
LOG_INFO(Config, "General LogType: {}", EmulatorSettings.GetLogType());
|
||||
LOG_INFO(Config, "General isIdenticalLogGrouped: {}", EmulatorSettings.IsIdenticalLogGrouped());
|
||||
LOG_INFO(Config, "General isNeo: {}", EmulatorSettings.IsNeo());
|
||||
LOG_INFO(Config, "General isDevKit: {}", EmulatorSettings.IsDevKit());
|
||||
LOG_INFO(Config, "General isConnectedToNetwork: {}", EmulatorSettings.IsConnectedToNetwork());
|
||||
LOG_INFO(Config, "General isShadNetEnabled: {}", EmulatorSettings.IsShadNetEnabled());
|
||||
LOG_INFO(Config, "Log sync: {}", EmulatorSettings.IsLogSync());
|
||||
LOG_INFO(Config, "Log skipDuplicate: {}", EmulatorSettings.IsLogSkipDuplicate());
|
||||
#ifdef _WIN32
|
||||
LOG_INFO(Config, "Log type: {}", EmulatorSettings.GetLogType());
|
||||
#endif
|
||||
LOG_INFO(Config, "GPU isNullGpu: {}", EmulatorSettings.IsNullGPU());
|
||||
LOG_INFO(Config, "GPU readbacksMode: {}", EmulatorSettings.GetReadbacksMode());
|
||||
LOG_INFO(Config, "GPU readbackLinearImages: {}",
|
||||
@ -535,7 +536,7 @@ void Emulator::Restart(std::filesystem::path eboot_path,
|
||||
|
||||
LOG_INFO(Common, "Restarting the emulator with args: {}", fmt::join(args, " "));
|
||||
Libraries::SaveData::Backup::StopThread();
|
||||
Common::Log::Denitializer();
|
||||
Common::Log::Shutdown();
|
||||
|
||||
auto& ipc = IPC::Instance();
|
||||
|
||||
|
||||
@ -48,7 +48,6 @@ const std::map<std::string, int> languageMap = {{"Arabic", 21},
|
||||
{"Ukrainian", 30},
|
||||
{"Vietnamese", 28}};
|
||||
std::vector<std::string> languageOptions; // assigned from keys above
|
||||
const std::vector<std::string> logTypeOptions = {"sync", "async"};
|
||||
const std::vector<std::string> fullscreenModeOptions = {"Windowed", "Fullscreen",
|
||||
"Fullscreen (Borderless)"};
|
||||
const std::vector<std::string> audioBackendOptions = {"SDL", "OpenAL"};
|
||||
@ -88,9 +87,9 @@ int trophySideSetting;
|
||||
float trophyDurationSetting;
|
||||
|
||||
// Log tab
|
||||
bool logEnabledSetting;
|
||||
bool separateLogSetting;
|
||||
int logTypeSetting;
|
||||
bool logEnableSetting;
|
||||
bool logSeparateSetting;
|
||||
bool logSyncSetting;
|
||||
|
||||
// Experimental tab
|
||||
int readbacksModeSetting;
|
||||
@ -420,10 +419,10 @@ void LoadCategory(SettingsCategory category) {
|
||||
ImGui::TableSetupColumn("Name", ImGuiTableColumnFlags_WidthFixed, 500.0f * uiScale);
|
||||
ImGui::TableSetupColumn("Value");
|
||||
|
||||
AddSettingBool("Enable Logging", logEnabledSetting);
|
||||
if (logEnabledSetting) {
|
||||
AddSettingBool("Separate Log Files", separateLogSetting);
|
||||
AddSettingCombo("Log Type", logTypeSetting, logTypeOptions);
|
||||
AddSettingBool("Enable Logging", logEnableSetting);
|
||||
if (logEnableSetting) {
|
||||
AddSettingBool("Separate Log Files", logSeparateSetting);
|
||||
AddSettingBool("Log Sync", logSyncSetting);
|
||||
}
|
||||
|
||||
ImGui::EndTable();
|
||||
@ -538,9 +537,9 @@ void SaveSettings(std::string profile) {
|
||||
EmulatorSettings.SetTrophyNotificationDuration(static_cast<double>(trophyDurationSetting));
|
||||
|
||||
/////////// Log Tab
|
||||
EmulatorSettings.SetLogEnabled(logEnabledSetting, isSpecific);
|
||||
EmulatorSettings.SetLogType(logTypeOptions.at(logTypeSetting), isSpecific);
|
||||
EmulatorSettings.SetSeparateLoggingEnabled(separateLogSetting, isSpecific);
|
||||
EmulatorSettings.SetLogEnable(logEnableSetting, isSpecific);
|
||||
EmulatorSettings.SetLogSeparate(logSeparateSetting, isSpecific);
|
||||
EmulatorSettings.SetLogSync(logSyncSetting, isSpecific);
|
||||
|
||||
/////////// Experimental Tab
|
||||
if (isSpecific) {
|
||||
@ -609,9 +608,9 @@ void LoadSettings(std::string profile) {
|
||||
trophyDurationSetting = static_cast<float>(EmulatorSettings.GetTrophyNotificationDuration());
|
||||
|
||||
/////////// Log Tab
|
||||
logEnabledSetting = EmulatorSettings.IsLogEnabled();
|
||||
logTypeSetting = GetComboIndex(EmulatorSettings.GetLogType(), logTypeOptions);
|
||||
separateLogSetting = EmulatorSettings.IsSeparateLoggingEnabled();
|
||||
logEnableSetting = EmulatorSettings.IsLogEnable();
|
||||
logSeparateSetting = EmulatorSettings.IsLogSeparate();
|
||||
logSyncSetting = EmulatorSettings.IsLogSync();
|
||||
|
||||
/////////// Experimental Tab
|
||||
if (isSpecific) {
|
||||
|
||||
156
src/main.cpp
156
src/main.cpp
@ -13,7 +13,7 @@
|
||||
#include <core/emulator_state.h>
|
||||
#include "common/config.h"
|
||||
#include "common/key_manager.h"
|
||||
#include "common/logging/backend.h"
|
||||
#include "common/logging/log.h"
|
||||
#include "common/memory_patcher.h"
|
||||
#include "common/path_util.h"
|
||||
#include "core/debugger.h"
|
||||
@ -32,6 +32,81 @@ int main(int argc, char* argv[]) {
|
||||
SetConsoleOutputCP(CP_UTF8);
|
||||
#endif
|
||||
|
||||
CLI::App app{"shadPS4 Emulator CLI"};
|
||||
|
||||
// ---- CLI state ----
|
||||
std::optional<std::string> gamePath;
|
||||
std::vector<std::string> gameArgs;
|
||||
std::optional<std::filesystem::path> overrideRoot;
|
||||
std::optional<int> waitPid;
|
||||
bool waitForDebugger = false;
|
||||
|
||||
std::optional<std::string> fullscreenStr;
|
||||
bool ignoreGamePatch = false;
|
||||
bool showFps = false;
|
||||
bool configClean = false;
|
||||
bool configGlobal = false;
|
||||
bool bigPicture = false;
|
||||
|
||||
std::optional<std::filesystem::path> addGameFolder;
|
||||
std::optional<std::filesystem::path> setAddonFolder;
|
||||
std::optional<std::string> patchFile;
|
||||
|
||||
// ---- Options ----
|
||||
app.add_option("-g,--game", gamePath, "Game path or ID");
|
||||
app.add_option("-p,--patch", patchFile, "Patch file to apply");
|
||||
app.add_flag("-i,--ignore-game-patch", ignoreGamePatch,
|
||||
"Disable automatic loading of game patches");
|
||||
|
||||
app.add_flag("-b,--big-picture", bigPicture, "Start in Big Picture Mode");
|
||||
|
||||
// FULLSCREEN: behavior-identical
|
||||
app.add_option("-f,--fullscreen", fullscreenStr, "Fullscreen mode (true|false)");
|
||||
|
||||
app.add_option("--override-root", overrideRoot)->check(CLI::ExistingDirectory);
|
||||
|
||||
app.add_flag("--wait-for-debugger", waitForDebugger);
|
||||
app.add_option("--wait-for-pid", waitPid);
|
||||
|
||||
app.add_flag("--show-fps", showFps);
|
||||
app.add_flag("--config-clean", configClean);
|
||||
app.add_flag("--config-global", configGlobal);
|
||||
app.add_flag("--log-append", Common::Log::g_should_append);
|
||||
|
||||
app.add_option("--add-game-folder", addGameFolder)->check(CLI::ExistingDirectory);
|
||||
app.add_option("--set-addon-folder", setAddonFolder)->check(CLI::ExistingDirectory);
|
||||
|
||||
// ---- Capture args after `--` verbatim ----
|
||||
app.allow_extras();
|
||||
app.parse_complete_callback([&]() {
|
||||
const auto& extras = app.remaining();
|
||||
if (!extras.empty()) {
|
||||
gameArgs = extras;
|
||||
}
|
||||
});
|
||||
|
||||
// ---- No-args behavior ----
|
||||
if (argc == 1) {
|
||||
SDL_ShowSimpleMessageBox(SDL_MESSAGEBOX_INFORMATION, "shadPS4",
|
||||
"This is a CLI application. Please use the QTLauncher for a GUI:\n"
|
||||
"https://github.com/shadps4-emu/shadps4-qtlauncher/releases",
|
||||
nullptr);
|
||||
std::cout << app.help();
|
||||
return -1;
|
||||
}
|
||||
|
||||
try {
|
||||
app.parse(argc, argv);
|
||||
} catch (const CLI::ParseError& e) {
|
||||
return app.exit(e);
|
||||
}
|
||||
|
||||
if (waitPid)
|
||||
Core::Debugger::WaitForPid(*waitPid);
|
||||
|
||||
// Start default log
|
||||
Common::Log::Setup("shad_log.txt");
|
||||
|
||||
IPC::Instance().Init();
|
||||
|
||||
auto emu_state = std::make_shared<EmulatorState>();
|
||||
@ -60,75 +135,10 @@ int main(int argc, char* argv[]) {
|
||||
EmulatorSettingsImpl::SetInstance(emu_settings);
|
||||
emu_settings->Load();
|
||||
|
||||
CLI::App app{"shadPS4 Emulator CLI"};
|
||||
|
||||
// ---- CLI state ----
|
||||
std::optional<std::string> gamePath;
|
||||
std::vector<std::string> gameArgs;
|
||||
std::optional<std::filesystem::path> overrideRoot;
|
||||
std::optional<int> waitPid;
|
||||
bool waitForDebugger = false;
|
||||
|
||||
std::optional<std::string> fullscreenStr;
|
||||
bool ignoreGamePatch = false;
|
||||
bool showFps = false;
|
||||
bool configClean = false;
|
||||
bool configGlobal = false;
|
||||
bool logAppend = false;
|
||||
bool bigPicture = false;
|
||||
|
||||
std::optional<std::filesystem::path> addGameFolder;
|
||||
std::optional<std::filesystem::path> setAddonFolder;
|
||||
std::optional<std::string> patchFile;
|
||||
|
||||
// ---- Options ----
|
||||
app.add_option("-g,--game", gamePath, "Game path or ID");
|
||||
app.add_option("-p,--patch", patchFile, "Patch file to apply");
|
||||
app.add_flag("-i,--ignore-game-patch", ignoreGamePatch,
|
||||
"Disable automatic loading of game patches");
|
||||
|
||||
app.add_flag("-b,--big-picture", bigPicture, "Start in Big Picture Mode");
|
||||
|
||||
// FULLSCREEN: behavior-identical
|
||||
app.add_option("-f,--fullscreen", fullscreenStr, "Fullscreen mode (true|false)");
|
||||
|
||||
app.add_option("--override-root", overrideRoot)->check(CLI::ExistingDirectory);
|
||||
|
||||
app.add_flag("--wait-for-debugger", waitForDebugger);
|
||||
app.add_option("--wait-for-pid", waitPid);
|
||||
|
||||
app.add_flag("--show-fps", showFps);
|
||||
app.add_flag("--config-clean", configClean);
|
||||
app.add_flag("--config-global", configGlobal);
|
||||
app.add_flag("--log-append", logAppend);
|
||||
|
||||
app.add_option("--add-game-folder", addGameFolder)->check(CLI::ExistingDirectory);
|
||||
app.add_option("--set-addon-folder", setAddonFolder)->check(CLI::ExistingDirectory);
|
||||
|
||||
// ---- Capture args after `--` verbatim ----
|
||||
app.allow_extras();
|
||||
app.parse_complete_callback([&]() {
|
||||
const auto& extras = app.remaining();
|
||||
if (!extras.empty()) {
|
||||
gameArgs = extras;
|
||||
}
|
||||
});
|
||||
|
||||
// ---- No-args behavior ----
|
||||
if (argc == 1) {
|
||||
SDL_ShowSimpleMessageBox(SDL_MESSAGEBOX_INFORMATION, "shadPS4",
|
||||
"This is a CLI application. Please use the QTLauncher for a GUI:\n"
|
||||
"https://github.com/shadps4-emu/shadps4-qtlauncher/releases",
|
||||
nullptr);
|
||||
std::cout << app.help();
|
||||
return -1;
|
||||
}
|
||||
|
||||
try {
|
||||
app.parse(argc, argv);
|
||||
} catch (const CLI::ParseError& e) {
|
||||
return app.exit(e);
|
||||
}
|
||||
Common::Log::Shutdown();
|
||||
// Start configured log
|
||||
Common::Log::g_should_append |= EmulatorSettings.IsLogAppend();
|
||||
Common::Log::Setup("shad_log.txt");
|
||||
|
||||
// ---- Utility commands ----
|
||||
if (addGameFolder) {
|
||||
@ -190,9 +200,6 @@ int main(int argc, char* argv[]) {
|
||||
if (configGlobal)
|
||||
EmulatorSettings.SetConfigMode(ConfigMode::Global);
|
||||
|
||||
if (logAppend)
|
||||
Common::Log::SetAppend();
|
||||
|
||||
// ---- Resolve game path or ID ----
|
||||
std::filesystem::path ebootPath(*gamePath);
|
||||
if (!std::filesystem::exists(ebootPath)) {
|
||||
@ -211,9 +218,6 @@ int main(int argc, char* argv[]) {
|
||||
}
|
||||
}
|
||||
|
||||
if (waitPid)
|
||||
Core::Debugger::WaitForPid(*waitPid);
|
||||
|
||||
if (bigPicture) {
|
||||
BigPictureMode::Launch();
|
||||
} else {
|
||||
|
||||
@ -2,6 +2,7 @@
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#include "common/assert.h"
|
||||
#include "common/logging/formatter.h"
|
||||
#include "core/emulator_settings.h"
|
||||
#include "video_core/renderdoc.h"
|
||||
|
||||
|
||||
@ -36,20 +36,20 @@ static VKAPI_ATTR VkBool32 VKAPI_CALL DebugUtilsCallback(
|
||||
vk::DebugUtilsMessageSeverityFlagBitsEXT severity, vk::DebugUtilsMessageTypeFlagsEXT type,
|
||||
const vk::DebugUtilsMessengerCallbackDataEXT* callback_data, void* user_data) {
|
||||
|
||||
Common::Log::Level level{};
|
||||
spdlog::level level{};
|
||||
switch (severity) {
|
||||
case vk::DebugUtilsMessageSeverityFlagBitsEXT::eError:
|
||||
level = Common::Log::Level::Error;
|
||||
level = spdlog::level::err;
|
||||
break;
|
||||
case vk::DebugUtilsMessageSeverityFlagBitsEXT::eWarning:
|
||||
level = Common::Log::Level::Info;
|
||||
level = spdlog::level::info;
|
||||
break;
|
||||
case vk::DebugUtilsMessageSeverityFlagBitsEXT::eInfo:
|
||||
case vk::DebugUtilsMessageSeverityFlagBitsEXT::eVerbose:
|
||||
level = Common::Log::Level::Debug;
|
||||
level = spdlog::level::debug;
|
||||
break;
|
||||
default:
|
||||
level = Common::Log::Level::Info;
|
||||
level = spdlog::level::info;
|
||||
}
|
||||
|
||||
LOG_GENERIC(Common::Log::Class::Render_Vulkan, level, "{}: {}",
|
||||
|
||||
@ -415,7 +415,7 @@ std::tuple<ImageId, int, int> TextureCache::ResolveOverlap(const ImageInfo& imag
|
||||
cache_image.info.size.height, cache_image.info.size.depth, cache_image.info.pitch,
|
||||
cache_image.info.resources.levels, cache_image.info.resources.layers,
|
||||
cache_image.info.num_samples, static_cast<u32>(cache_image.info.tile_mode),
|
||||
cache_image.info.num_bits, cache_image.info.props.is_block,
|
||||
cache_image.info.num_bits, +cache_image.info.props.is_block,
|
||||
cache_image.info.guest_size, cache_image.tick_accessed_last, safe_to_delete,
|
||||
|
||||
// New image details
|
||||
|
||||
@ -19,11 +19,10 @@ set(SETTINGS_TEST_SOURCES
|
||||
${CMAKE_SOURCE_DIR}/src/common/assert.cpp
|
||||
${CMAKE_SOURCE_DIR}/src/common/error.cpp
|
||||
${CMAKE_SOURCE_DIR}/src/common/string_util.cpp
|
||||
${CMAKE_SOURCE_DIR}/src/common/logging/filter.cpp
|
||||
${CMAKE_SOURCE_DIR}/src/common/logging/text_formatter.cpp
|
||||
${CMAKE_SOURCE_DIR}/src/common/logging/log.cpp
|
||||
|
||||
# Stubs that replace dependencies
|
||||
stubs/log_stub.cpp
|
||||
stubs/common_stub.cpp
|
||||
stubs/scm_rev_stub.cpp
|
||||
stubs/sdl_stub.cpp
|
||||
|
||||
@ -43,7 +42,8 @@ target_link_libraries(shadps4_settings_test PRIVATE
|
||||
fmt::fmt
|
||||
nlohmann_json::nlohmann_json
|
||||
toml11::toml11
|
||||
SDL3::SDL3
|
||||
SDL3::SDL3
|
||||
spdlog::spdlog
|
||||
)
|
||||
|
||||
target_compile_features(shadps4_settings_test PRIVATE cxx_std_23)
|
||||
|
||||
10
tests/stubs/common_stub.cpp
Normal file
10
tests/stubs/common_stub.cpp
Normal file
@ -0,0 +1,10 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2026 shadPS4 Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#include <string>
|
||||
|
||||
namespace Common {
|
||||
|
||||
std::string GetCurrentThreadName() { return "shadPS4::Test"; }
|
||||
|
||||
} // namespace Common
|
||||
@ -1,27 +0,0 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2026 shadPS4 Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#include <cstdio>
|
||||
#include <string_view>
|
||||
#include <fmt/format.h>
|
||||
#include "common/logging/backend.h"
|
||||
#include "common/logging/filter.h"
|
||||
#include "common/logging/log.h"
|
||||
|
||||
namespace Common::Log {
|
||||
|
||||
void FmtLogMessageImpl(Class log_class, Level log_level, const char* filename,
|
||||
unsigned int line_num, const char* function, const char* format,
|
||||
const fmt::format_args& args) {
|
||||
}
|
||||
|
||||
void Initialize(std::string_view) {}
|
||||
bool IsActive() { return false; }
|
||||
void SetGlobalFilter(const Filter&) {}
|
||||
void SetColorConsoleBackendEnabled(bool) {}
|
||||
void Start() {}
|
||||
void Stop() {}
|
||||
void Denitializer() {}
|
||||
void SetAppend() {}
|
||||
|
||||
} // namespace Common::Log
|
||||
@ -54,7 +54,14 @@ static json ReadJson(const fs::path& p) {
|
||||
return j;
|
||||
}
|
||||
|
||||
class EmulatorSettingsTest : public ::testing::Test {
|
||||
class TestWrapper : public ::testing::Test {
|
||||
protected:
|
||||
TestWrapper() {
|
||||
Common::Log::Setup("shad_test.log");
|
||||
}
|
||||
};
|
||||
|
||||
class EmulatorSettingsTest : public TestWrapper {
|
||||
protected:
|
||||
void SetUp() override {
|
||||
temp_dir = std::make_unique<TempDir>();
|
||||
@ -286,7 +293,7 @@ TEST_F(EmulatorSettingsTest, SaveCreatesConfigJson) {
|
||||
TEST_F(EmulatorSettingsTest, SaveWritesAllExpectedSections) {
|
||||
ASSERT_TRUE(temp_settings->Save());
|
||||
json j = ReadJson(ConfigJson());
|
||||
for (const char* section : {"General", "Debug", "Input", "Audio", "GPU", "Vulkan"})
|
||||
for (const char* section : {"General", "Log", "Debug", "Input", "Audio", "GPU", "Vulkan"})
|
||||
EXPECT_TRUE(j.contains(section)) << "Missing section: " << section;
|
||||
}
|
||||
|
||||
@ -699,7 +706,6 @@ TEST_F(EmulatorSettingsTest, GetAllOverrideableKeysContainsRepresentativeKeys) {
|
||||
EXPECT_TRUE(has("pipeline_cache_enabled"));
|
||||
// Debug
|
||||
EXPECT_TRUE(has("debug_dump"));
|
||||
EXPECT_TRUE(has("log_enabled"));
|
||||
// Input
|
||||
EXPECT_TRUE(has("cursor_state"));
|
||||
// Audio
|
||||
|
||||
Loading…
Reference in New Issue
Block a user