mirror of
https://github.com/KytyPS5/KytyPS5.git
synced 2026-10-20 02:47:02 +02:00
8cecaf4e30
Preserve native Windows paths for filesystem operations, command-line options, and game icons. Complete Unicode handling in recursive file listings and file logging, restore unmounting by host directory, and extend regression coverage. Original implementation by Augusto Domingos (@AghastyGD); retain all five of his PR commits and include the reviewed follow-up fixes. Closes #714 Co-authored-by: Augusto Domingos <augustodomingosvasco@gmail.com>
196 lines
4.8 KiB
C++
196 lines
4.8 KiB
C++
|
|
#include "common/logging/log.h"
|
|
|
|
#include "common/assert.h"
|
|
#include "common/emulatorConfig.h"
|
|
|
|
#include <cstdio>
|
|
#include <filesystem>
|
|
#include <fmt/format.h>
|
|
#include <memory>
|
|
#include <mutex>
|
|
#include <spdlog/formatter.h>
|
|
#include <spdlog/logger.h>
|
|
#include <spdlog/sinks/basic_file_sink.h>
|
|
#include <spdlog/sinks/null_sink.h>
|
|
#include <spdlog/sinks/stdout_sinks.h>
|
|
#include <string_view>
|
|
|
|
namespace {
|
|
|
|
class RawFormatter final: public spdlog::formatter {
|
|
public:
|
|
void format(const spdlog::details::log_msg& msg, spdlog::memory_buf_t& dest) override {
|
|
const std::string_view payload(msg.payload.data(), msg.payload.size());
|
|
dest.append(payload.data(), payload.data() + payload.size());
|
|
}
|
|
|
|
std::unique_ptr<spdlog::formatter> clone() const override {
|
|
return std::make_unique<RawFormatter>();
|
|
}
|
|
};
|
|
|
|
std::shared_ptr<spdlog::logger> MakeLogger(std::string name, spdlog::sink_ptr sink) {
|
|
sink->set_formatter(std::make_unique<RawFormatter>());
|
|
|
|
auto logger = std::make_shared<spdlog::logger>(std::move(name), std::move(sink));
|
|
logger->set_level(spdlog::level::trace);
|
|
return logger;
|
|
}
|
|
|
|
std::shared_ptr<spdlog::logger> MakeFileLogger(std::string name,
|
|
const std::filesystem::path& path) {
|
|
const auto parent = path.parent_path();
|
|
if (!parent.empty()) {
|
|
std::filesystem::create_directories(parent);
|
|
}
|
|
|
|
auto sink =
|
|
std::make_shared<spdlog::sinks::basic_file_sink_mt>(path.native(), true);
|
|
return MakeLogger(std::move(name), std::move(sink));
|
|
}
|
|
|
|
static bool HasStyle(fmt::text_style style) {
|
|
return style != fmt::text_style {};
|
|
}
|
|
|
|
void WriteStdout(std::string_view text, fmt::text_style style = {}) {
|
|
if (HasStyle(style)) {
|
|
fmt::print(stdout, style, "{}", text);
|
|
} else {
|
|
std::fwrite(text.data(), 1, text.size(), stdout);
|
|
}
|
|
std::fflush(stdout);
|
|
}
|
|
|
|
} // namespace
|
|
|
|
namespace Log {
|
|
|
|
static bool g_initialized = false;
|
|
static Direction g_direction = Direction::Console;
|
|
static std::filesystem::path g_output_file;
|
|
static std::mutex g_logger_mutex;
|
|
static std::shared_ptr<spdlog::logger> g_logger;
|
|
|
|
void Flush() {
|
|
if (g_logger != nullptr) {
|
|
g_logger->flush();
|
|
}
|
|
}
|
|
|
|
static void SetupLogger() {
|
|
std::lock_guard lock(g_logger_mutex);
|
|
Flush();
|
|
g_logger.reset();
|
|
|
|
switch (g_direction) {
|
|
case Direction::Silent:
|
|
g_logger = MakeLogger("kyty", std::make_shared<spdlog::sinks::null_sink_mt>());
|
|
break;
|
|
case Direction::Console:
|
|
g_logger = MakeLogger("kyty", std::make_shared<spdlog::sinks::stdout_sink_mt>());
|
|
break;
|
|
case Direction::File:
|
|
if (!g_output_file.empty()) {
|
|
g_logger = MakeFileLogger("kyty", g_output_file);
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
|
|
static std::shared_ptr<spdlog::logger> GetLogger() {
|
|
std::lock_guard lock(g_logger_mutex);
|
|
return g_logger;
|
|
}
|
|
|
|
static void WriteImpl(std::string_view text, fmt::text_style style = {}) {
|
|
if (text.empty()) {
|
|
return;
|
|
}
|
|
|
|
if (!g_initialized) {
|
|
WriteStdout(text, style);
|
|
return;
|
|
}
|
|
|
|
if (g_direction == Direction::Silent) {
|
|
return;
|
|
}
|
|
|
|
if (auto logger = GetLogger()) {
|
|
if (g_direction == Direction::Console && HasStyle(style)) {
|
|
const auto styled = fmt::format(style, "{}", text);
|
|
logger->log(spdlog::level::info, spdlog::string_view_t(styled.data(), styled.size()));
|
|
} else {
|
|
logger->log(spdlog::level::info, spdlog::string_view_t(text.data(), text.size()));
|
|
}
|
|
}
|
|
}
|
|
|
|
void WriteToConsoleAndLog(std::string_view text) {
|
|
WriteImpl(text);
|
|
if (g_initialized && g_direction != Direction::Console) {
|
|
WriteStdout(text);
|
|
}
|
|
Flush();
|
|
}
|
|
|
|
void WriteFatal(std::string_view text) {
|
|
if (g_direction == Direction::Silent || !g_initialized) {
|
|
WriteStdout(text);
|
|
} else {
|
|
WriteStdout(text);
|
|
WriteImpl(text);
|
|
}
|
|
Flush();
|
|
}
|
|
|
|
void WriteFatal(fmt::text_style style, std::string_view text) {
|
|
if (g_direction == Direction::Silent || !g_initialized) {
|
|
WriteStdout(text, style);
|
|
} else {
|
|
WriteStdout(text, style);
|
|
WriteImpl(text, style);
|
|
}
|
|
Flush();
|
|
}
|
|
|
|
void Initialize() {
|
|
g_initialized = true;
|
|
switch (Config::GetPrintfDirection()) {
|
|
case Config::LogDirection::Silent: g_direction = Direction::Silent; break;
|
|
case Config::LogDirection::Console: g_direction = Direction::Console; break;
|
|
case Config::LogDirection::File: g_direction = Direction::File; break;
|
|
}
|
|
g_output_file =
|
|
(g_direction == Direction::File ? Config::GetPrintfOutputFile() : std::filesystem::path {});
|
|
SetupLogger();
|
|
}
|
|
|
|
void Shutdown() {
|
|
Flush();
|
|
std::lock_guard lock(g_logger_mutex);
|
|
g_logger.reset();
|
|
}
|
|
|
|
Direction GetDirection() {
|
|
EXIT_IF(!g_initialized);
|
|
return g_direction;
|
|
}
|
|
|
|
bool IsSilent() {
|
|
// Before init LOGF must keep writing to stdout, so report non-silent.
|
|
return g_initialized && g_direction == Direction::Silent;
|
|
}
|
|
|
|
void Write(std::string_view text) {
|
|
WriteImpl(text);
|
|
}
|
|
|
|
void Write(fmt::text_style style, std::string_view text) {
|
|
WriteImpl(text, style);
|
|
}
|
|
|
|
} // namespace Log
|