mirror of https://github.com/PCSX2/pcsx2.git
Console: Remove WX rubbish and replace
This commit is contained in:
parent
efae58de52
commit
fa00069068
|
@ -1,524 +1,500 @@
|
|||
// SPDX-FileCopyrightText: 2002-2023 PCSX2 Dev Team
|
||||
// SPDX-FileCopyrightText: 2002-2024 PCSX2 Dev Team
|
||||
// SPDX-License-Identifier: LGPL-3.0+
|
||||
|
||||
#include "common/Threading.h"
|
||||
#include "common/TraceLog.h"
|
||||
#include "common/Console.h"
|
||||
#include "common/Assertions.h"
|
||||
#include "common/RedtapeWindows.h" // OutputDebugString
|
||||
#include "common/FileSystem.h"
|
||||
#include "common/SmallString.h"
|
||||
#include "common/Timer.h"
|
||||
|
||||
using namespace Threading;
|
||||
#include "fmt/format.h"
|
||||
|
||||
// thread-local console indentation setting.
|
||||
static thread_local int conlog_Indent(0);
|
||||
#include <mutex>
|
||||
#include <vector>
|
||||
|
||||
// thread-local console color storage.
|
||||
static thread_local ConsoleColors conlog_Color(DefaultConsoleColor);
|
||||
|
||||
#ifdef __POSIX__
|
||||
#ifdef _WIN32
|
||||
#include "common/RedtapeWindows.h"
|
||||
#else
|
||||
#include <unistd.h>
|
||||
|
||||
static FILE* stdout_fp = stdout;
|
||||
|
||||
static bool checkSupportsColor()
|
||||
{
|
||||
if (!isatty(fileno(stdout_fp)))
|
||||
return false;
|
||||
char* term = getenv("TERM");
|
||||
if (!term || (0 == strcmp(term, "dumb")))
|
||||
return false;
|
||||
return true; // Probably supports color
|
||||
}
|
||||
|
||||
static bool supports_color = checkSupportsColor();
|
||||
|
||||
void Console_SetStdout(FILE* fp)
|
||||
{
|
||||
stdout_fp = fp;
|
||||
}
|
||||
#endif
|
||||
|
||||
// This function re-assigns the console log writer(s) to the specified target. It makes sure
|
||||
// to flush any contents from the buffered console log (which typically accumulates due to
|
||||
// log suspension during log file/window re-init operations) into the new log.
|
||||
//
|
||||
// Important! Only Assert and Null console loggers are allowed during C++ startup init (when
|
||||
// the program or DLL first loads). Other log targets rely on the static buffer and a
|
||||
// threaded mutex lock, which are only valid after C++ initialization has finished.
|
||||
void Console_SetActiveHandler(const IConsoleWriter& writer, FILE* flushfp)
|
||||
using namespace std::string_view_literals;
|
||||
|
||||
// Dummy objects, need to get rid of them...
|
||||
ConsoleLogWriter<LOGLEVEL_INFO> Console;
|
||||
ConsoleLogWriter<LOGLEVEL_DEV> DevCon;
|
||||
|
||||
#ifdef _DEBUG
|
||||
ConsoleLogWriter<LOGLEVEL_DEBUG> DbgConWriter;
|
||||
#else
|
||||
NullLogWriter DbgConWriter;
|
||||
#endif
|
||||
|
||||
#define TIMESTAMP_FORMAT_STRING "[{:10.4f}] "
|
||||
#define TIMESTAMP_PRINTF_STRING "[%10.4f] "
|
||||
|
||||
namespace Log
|
||||
{
|
||||
pxAssertMsg(
|
||||
(writer.WriteRaw != NULL) && (writer.DoWriteLn != NULL) &&
|
||||
(writer.Newline != NULL) && (writer.SetTitle != NULL) &&
|
||||
(writer.DoSetColor != NULL),
|
||||
"Invalid IConsoleWriter object! All function pointer interfaces must be implemented.");
|
||||
static void WriteToConsole(LOGLEVEL level, ConsoleColors color, std::string_view message);
|
||||
static void WriteToDebug(LOGLEVEL level, ConsoleColors color, std::string_view message);
|
||||
static void WriteToFile(LOGLEVEL level, ConsoleColors color, std::string_view message);
|
||||
|
||||
Console = writer;
|
||||
DevConWriter = writer;
|
||||
static void UpdateMaxLevel();
|
||||
|
||||
#ifdef PCSX2_DEBUG
|
||||
DbgCon = writer;
|
||||
static void ExecuteCallbacks(LOGLEVEL level, ConsoleColors color, std::string_view message);
|
||||
|
||||
static Common::Timer::Value s_start_timestamp = Common::Timer::GetCurrentValue();
|
||||
|
||||
static LOGLEVEL s_max_level = LOGLEVEL_NONE;
|
||||
static LOGLEVEL s_console_level = LOGLEVEL_NONE;
|
||||
static LOGLEVEL s_debug_level = LOGLEVEL_NONE;
|
||||
static LOGLEVEL s_file_level = LOGLEVEL_NONE;
|
||||
static LOGLEVEL s_host_level = LOGLEVEL_NONE;
|
||||
static bool s_log_timestamps = true;
|
||||
|
||||
static FileSystem::ManagedCFilePtr s_file_handle;
|
||||
static std::string s_file_path;
|
||||
static std::mutex s_file_mutex;
|
||||
|
||||
static HostCallbackType s_host_callback;
|
||||
|
||||
#ifdef _WIN32
|
||||
static HANDLE s_hConsoleStdIn = NULL;
|
||||
static HANDLE s_hConsoleStdOut = NULL;
|
||||
static HANDLE s_hConsoleStdErr = NULL;
|
||||
#endif
|
||||
} // namespace Log
|
||||
|
||||
float Log::GetCurrentMessageTime()
|
||||
{
|
||||
return static_cast<float>(Common::Timer::ConvertValueToSeconds(Common::Timer::GetCurrentValue() - s_start_timestamp));
|
||||
}
|
||||
|
||||
__ri void Log::WriteToConsole(LOGLEVEL level, ConsoleColors color, std::string_view message)
|
||||
{
|
||||
static constexpr std::string_view s_ansi_color_codes[ConsoleColors_Count] = {
|
||||
"\033[0m"sv, // default
|
||||
"\033[30m\033[1m"sv, // black
|
||||
"\033[32m"sv, // green
|
||||
"\033[31m"sv, // red
|
||||
"\033[34m"sv, // blue
|
||||
"\033[35m"sv, // magenta
|
||||
"\033[35m"sv, // orange (FIXME)
|
||||
"\033[37m"sv, // gray
|
||||
"\033[36m"sv, // cyan
|
||||
"\033[33m"sv, // yellow
|
||||
"\033[37m"sv, // white
|
||||
"\033[30m\033[1m"sv, // strong black
|
||||
"\033[31m\033[1m"sv, // strong red
|
||||
"\033[32m\033[1m"sv, // strong green
|
||||
"\033[34m\033[1m"sv, // strong blue
|
||||
"\033[35m\033[1m"sv, // strong magenta
|
||||
"\033[35m\033[1m"sv, // strong orange (FIXME)
|
||||
"\033[37m\033[1m"sv, // strong gray
|
||||
"\033[36m\033[1m"sv, // strong cyan
|
||||
"\033[33m\033[1m"sv, // strong yellow
|
||||
"\033[37m\033[1m"sv, // strong white
|
||||
};
|
||||
|
||||
static constexpr size_t BUFFER_SIZE = 512;
|
||||
|
||||
SmallStackString<BUFFER_SIZE> buffer;
|
||||
buffer.reserve(32 + message.length());
|
||||
buffer.append(s_ansi_color_codes[color]);
|
||||
|
||||
if (s_log_timestamps)
|
||||
buffer.append_fmt(TIMESTAMP_FORMAT_STRING, Log::GetCurrentMessageTime());
|
||||
|
||||
buffer.append(message);
|
||||
buffer.append('\n');
|
||||
|
||||
#ifdef _WIN32
|
||||
const HANDLE hOutput = (level <= LOGLEVEL_WARNING) ? s_hConsoleStdErr : s_hConsoleStdOut;
|
||||
|
||||
// Convert to UTF-16 first so Unicode characters display correctly. NT is going to do it anyway...
|
||||
wchar_t wbuf[BUFFER_SIZE];
|
||||
wchar_t* wmessage_buf = wbuf;
|
||||
int wmessage_buflen = static_cast<int>(std::size(wbuf) - 1);
|
||||
if (buffer.length() >= std::size(wbuf))
|
||||
{
|
||||
wmessage_buflen = static_cast<int>(buffer.length());
|
||||
wmessage_buf = static_cast<wchar_t*>(std::malloc(static_cast<size_t>(wmessage_buflen) * sizeof(wchar_t)));
|
||||
if (!wmessage_buf)
|
||||
return;
|
||||
}
|
||||
|
||||
const int wmessage_size = MultiByteToWideChar(CP_UTF8, 0, buffer.data(), static_cast<int>(buffer.length()), wmessage_buf, wmessage_buflen);
|
||||
if (wmessage_size <= 0)
|
||||
goto cleanup;
|
||||
|
||||
DWORD chars_written;
|
||||
WriteConsoleW(hOutput, wmessage_buf, static_cast<DWORD>(wmessage_size), &chars_written, nullptr);
|
||||
|
||||
cleanup:
|
||||
if (wmessage_buf != wbuf)
|
||||
std::free(wmessage_buf);
|
||||
#else
|
||||
const int fd = (level <= LOGLEVEL_WARNING) ? STDERR_FILENO : STDOUT_FILENO;
|
||||
write(fd, buffer.data(), buffer.length());
|
||||
#endif
|
||||
}
|
||||
|
||||
// Writes text to the Visual Studio Output window (Microsoft Windows only).
|
||||
// On all other platforms this pipes to Stdout instead.
|
||||
static void MSW_OutputDebugString(const char* text)
|
||||
bool Log::IsConsoleOutputEnabled()
|
||||
{
|
||||
return (s_console_level > LOGLEVEL_NONE);
|
||||
}
|
||||
|
||||
void Log::SetConsoleOutputLevel(LOGLEVEL level)
|
||||
{
|
||||
if (s_console_level == level)
|
||||
return;
|
||||
|
||||
const bool was_enabled = (s_console_level > LOGLEVEL_NONE);
|
||||
const bool now_enabled = (level > LOGLEVEL_NONE);
|
||||
s_console_level = level;
|
||||
UpdateMaxLevel();
|
||||
|
||||
if (was_enabled == now_enabled)
|
||||
return;
|
||||
|
||||
// Worst that happens here is we write to a bad handle..
|
||||
|
||||
#if defined(_WIN32)
|
||||
static constexpr auto enable_virtual_terminal_processing = [](HANDLE hConsole) {
|
||||
DWORD old_mode;
|
||||
if (!GetConsoleMode(hConsole, &old_mode))
|
||||
return;
|
||||
|
||||
// already enabled?
|
||||
if (old_mode & ENABLE_VIRTUAL_TERMINAL_PROCESSING)
|
||||
return;
|
||||
|
||||
SetConsoleMode(hConsole, old_mode | ENABLE_VIRTUAL_TERMINAL_PROCESSING);
|
||||
};
|
||||
|
||||
// On windows, no console is allocated by default on a windows based application
|
||||
static bool console_was_allocated = false;
|
||||
static HANDLE old_stdin = NULL;
|
||||
static HANDLE old_stdout = NULL;
|
||||
static HANDLE old_stderr = NULL;
|
||||
|
||||
if (now_enabled)
|
||||
{
|
||||
old_stdin = GetStdHandle(STD_INPUT_HANDLE);
|
||||
old_stdout = GetStdHandle(STD_OUTPUT_HANDLE);
|
||||
old_stderr = GetStdHandle(STD_ERROR_HANDLE);
|
||||
|
||||
if (!old_stdout)
|
||||
{
|
||||
// Attach to the parent console if we're running from a command window
|
||||
if (!AttachConsole(ATTACH_PARENT_PROCESS) && !AllocConsole())
|
||||
return;
|
||||
|
||||
s_hConsoleStdIn = GetStdHandle(STD_INPUT_HANDLE);
|
||||
s_hConsoleStdOut = GetStdHandle(STD_OUTPUT_HANDLE);
|
||||
s_hConsoleStdErr = GetStdHandle(STD_ERROR_HANDLE);
|
||||
|
||||
enable_virtual_terminal_processing(s_hConsoleStdOut);
|
||||
enable_virtual_terminal_processing(s_hConsoleStdErr);
|
||||
|
||||
std::FILE* fp;
|
||||
freopen_s(&fp, "CONIN$", "r", stdin);
|
||||
freopen_s(&fp, "CONOUT$", "w", stdout);
|
||||
freopen_s(&fp, "CONOUT$", "w", stderr);
|
||||
|
||||
console_was_allocated = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
s_hConsoleStdIn = old_stdin;
|
||||
s_hConsoleStdOut = old_stdout;
|
||||
s_hConsoleStdErr = old_stderr;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (console_was_allocated)
|
||||
{
|
||||
console_was_allocated = false;
|
||||
|
||||
std::FILE* fp;
|
||||
freopen_s(&fp, "NUL:", "w", stderr);
|
||||
freopen_s(&fp, "NUL:", "w", stdout);
|
||||
freopen_s(&fp, "NUL:", "w", stdin);
|
||||
|
||||
SetStdHandle(STD_ERROR_HANDLE, old_stderr);
|
||||
SetStdHandle(STD_OUTPUT_HANDLE, old_stdout);
|
||||
SetStdHandle(STD_INPUT_HANDLE, old_stdin);
|
||||
|
||||
s_hConsoleStdIn = NULL;
|
||||
s_hConsoleStdOut = NULL;
|
||||
s_hConsoleStdErr = NULL;
|
||||
|
||||
FreeConsole();
|
||||
}
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
__ri void Log::WriteToDebug(LOGLEVEL level, ConsoleColors color, std::string_view message)
|
||||
{
|
||||
#ifdef _WIN32
|
||||
static bool hasDebugger = IsDebuggerPresent();
|
||||
if (hasDebugger)
|
||||
OutputDebugStringA(text);
|
||||
#else
|
||||
fputs(text, stdout_fp);
|
||||
fflush(stdout_fp);
|
||||
#endif
|
||||
}
|
||||
static constexpr size_t BUFFER_SIZE = 512;
|
||||
|
||||
|
||||
// --------------------------------------------------------------------------------------
|
||||
// ConsoleNull
|
||||
// --------------------------------------------------------------------------------------
|
||||
|
||||
static void ConsoleNull_SetTitle(const char* title) {}
|
||||
static void ConsoleNull_DoSetColor(ConsoleColors color) {}
|
||||
static void ConsoleNull_Newline() {}
|
||||
static void ConsoleNull_DoWrite(const char* fmt) {}
|
||||
static void ConsoleNull_DoWriteLn(const char* fmt) {}
|
||||
|
||||
const IConsoleWriter ConsoleWriter_Null =
|
||||
// Convert to UTF-16 first so Unicode characters display correctly. NT is going to do it anyway...
|
||||
wchar_t wbuf[BUFFER_SIZE];
|
||||
wchar_t* wmessage_buf = wbuf;
|
||||
int wmessage_buflen = static_cast<int>(std::size(wbuf) - 1);
|
||||
if (message.length() >= std::size(wbuf))
|
||||
{
|
||||
ConsoleNull_DoWrite,
|
||||
ConsoleNull_DoWriteLn,
|
||||
ConsoleNull_DoSetColor,
|
||||
|
||||
ConsoleNull_DoWrite,
|
||||
ConsoleNull_Newline,
|
||||
ConsoleNull_SetTitle,
|
||||
|
||||
0, // instance-level indentation (should always be 0)
|
||||
};
|
||||
|
||||
// --------------------------------------------------------------------------------------
|
||||
// Console_Stdout
|
||||
// --------------------------------------------------------------------------------------
|
||||
|
||||
#if defined(__POSIX__)
|
||||
static __fi const char* GetLinuxConsoleColor(ConsoleColors color)
|
||||
{
|
||||
switch (color)
|
||||
{
|
||||
case Color_Black:
|
||||
case Color_StrongBlack:
|
||||
return "\033[30m\033[1m";
|
||||
|
||||
case Color_Red:
|
||||
return "\033[31m";
|
||||
case Color_StrongRed:
|
||||
return "\033[31m\033[1m";
|
||||
|
||||
case Color_Green:
|
||||
return "\033[32m";
|
||||
case Color_StrongGreen:
|
||||
return "\033[32m\033[1m";
|
||||
|
||||
case Color_Yellow:
|
||||
return "\033[33m";
|
||||
case Color_StrongYellow:
|
||||
return "\033[33m\033[1m";
|
||||
|
||||
case Color_Blue:
|
||||
return "\033[34m";
|
||||
case Color_StrongBlue:
|
||||
return "\033[34m\033[1m";
|
||||
|
||||
// No orange, so use magenta.
|
||||
case Color_Orange:
|
||||
case Color_Magenta:
|
||||
return "\033[35m";
|
||||
case Color_StrongOrange:
|
||||
case Color_StrongMagenta:
|
||||
return "\033[35m\033[1m";
|
||||
|
||||
case Color_Cyan:
|
||||
return "\033[36m";
|
||||
case Color_StrongCyan:
|
||||
return "\033[36m\033[1m";
|
||||
|
||||
// Use 'white' instead of grey.
|
||||
case Color_Gray:
|
||||
case Color_White:
|
||||
return "\033[37m";
|
||||
case Color_StrongGray:
|
||||
case Color_StrongWhite:
|
||||
return "\033[37m\033[1m";
|
||||
|
||||
// On some other value being passed, clear any formatting.
|
||||
case Color_Default:
|
||||
default:
|
||||
return "\033[0m";
|
||||
wmessage_buflen = static_cast<int>(message.length());
|
||||
wmessage_buf = static_cast<wchar_t*>(std::malloc((static_cast<size_t>(wmessage_buflen) + 2) * sizeof(wchar_t)));
|
||||
if (!wmessage_buf)
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
int wmessage_size = 0;
|
||||
if (!message.empty()) [[likely]]
|
||||
{
|
||||
wmessage_size = MultiByteToWideChar(CP_UTF8, 0, message.data(), static_cast<int>(message.length()), wmessage_buf, wmessage_buflen);
|
||||
if (wmessage_size <= 0)
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
wmessage_buf[wmessage_size++] = L'\n';
|
||||
wmessage_buf[wmessage_size++] = 0;
|
||||
OutputDebugStringW(wmessage_buf);
|
||||
|
||||
cleanup:
|
||||
if (wmessage_buf != wbuf)
|
||||
std::free(wmessage_buf);
|
||||
#endif
|
||||
|
||||
// One possible default write action at startup and shutdown is to use the stdout.
|
||||
static void ConsoleStdout_DoWrite(const char* fmt)
|
||||
{
|
||||
MSW_OutputDebugString(fmt);
|
||||
}
|
||||
|
||||
// Default write action at startup and shutdown is to use the stdout.
|
||||
static void ConsoleStdout_DoWriteLn(const char* fmt)
|
||||
bool Log::IsDebugOutputEnabled()
|
||||
{
|
||||
MSW_OutputDebugString(fmt);
|
||||
MSW_OutputDebugString("\n");
|
||||
return (s_console_level > LOGLEVEL_NONE);
|
||||
}
|
||||
|
||||
static void ConsoleStdout_Newline()
|
||||
void Log::SetDebugOutputLevel(LOGLEVEL level)
|
||||
{
|
||||
MSW_OutputDebugString("\n");
|
||||
s_debug_level = level;
|
||||
UpdateMaxLevel();
|
||||
}
|
||||
|
||||
static void ConsoleStdout_DoSetColor(ConsoleColors color)
|
||||
__ri void Log::WriteToFile(LOGLEVEL level, ConsoleColors color, std::string_view message)
|
||||
{
|
||||
#if defined(__POSIX__)
|
||||
if (!supports_color)
|
||||
std::unique_lock lock(s_file_mutex);
|
||||
if (!s_file_handle) [[unlikely]]
|
||||
return;
|
||||
fprintf(stdout_fp, "\033[0m%s", GetLinuxConsoleColor(color));
|
||||
fflush(stdout_fp);
|
||||
#endif
|
||||
}
|
||||
|
||||
static void ConsoleStdout_SetTitle(const char* title)
|
||||
{
|
||||
#if defined(__POSIX__)
|
||||
if (supports_color)
|
||||
fputs("\033]0;", stdout_fp);
|
||||
fputs(title, stdout_fp);
|
||||
if (supports_color)
|
||||
fputs("\007", stdout_fp);
|
||||
#endif
|
||||
}
|
||||
|
||||
const IConsoleWriter ConsoleWriter_Stdout =
|
||||
if (!message.empty()) [[likely]]
|
||||
{
|
||||
ConsoleStdout_DoWrite, // Writes without newlines go to buffer to avoid error log spam.
|
||||
ConsoleStdout_DoWriteLn,
|
||||
ConsoleStdout_DoSetColor,
|
||||
|
||||
ConsoleNull_DoWrite, // writes from re-piped stdout are ignored here, lest we create infinite loop hell >_<
|
||||
ConsoleStdout_Newline,
|
||||
ConsoleStdout_SetTitle,
|
||||
0, // instance-level indentation (should always be 0)
|
||||
};
|
||||
|
||||
// --------------------------------------------------------------------------------------
|
||||
// ConsoleAssert
|
||||
// --------------------------------------------------------------------------------------
|
||||
|
||||
static void ConsoleAssert_DoWrite(const char* fmt)
|
||||
{
|
||||
pxFailRel("Console class has not been initialized");
|
||||
}
|
||||
|
||||
static void ConsoleAssert_DoWriteLn(const char* fmt)
|
||||
{
|
||||
pxFailRel("Console class has not been initialized");
|
||||
}
|
||||
|
||||
const IConsoleWriter ConsoleWriter_Assert =
|
||||
{
|
||||
ConsoleAssert_DoWrite,
|
||||
ConsoleAssert_DoWriteLn,
|
||||
ConsoleNull_DoSetColor,
|
||||
|
||||
ConsoleNull_DoWrite,
|
||||
ConsoleNull_Newline,
|
||||
ConsoleNull_SetTitle,
|
||||
|
||||
0, // instance-level indentation (should always be 0)
|
||||
};
|
||||
|
||||
// =====================================================================================================
|
||||
// IConsoleWriter (implementations)
|
||||
// =====================================================================================================
|
||||
// (all non-virtual members that do common work and then pass the result through DoWrite
|
||||
// or DoWriteLn)
|
||||
|
||||
// Parameters:
|
||||
// glob_indent - this parameter is used to specify a global indentation setting. It is used by
|
||||
// WriteLn function, but defaults to 0 for Warning and Error calls. Local indentation always
|
||||
// applies to all writes.
|
||||
std::string IConsoleWriter::_addIndentation(const std::string& src, int glob_indent = 0) const
|
||||
{
|
||||
const int indent = glob_indent + _imm_indentation;
|
||||
|
||||
std::string indentStr;
|
||||
for (int i = 0; i < indent; i++)
|
||||
indentStr += '\t';
|
||||
|
||||
std::string result;
|
||||
result.reserve(src.length() + 16 * indent);
|
||||
result.append(indentStr);
|
||||
result.append(src);
|
||||
|
||||
std::string::size_type pos = result.find('\n');
|
||||
while (pos != std::string::npos)
|
||||
{
|
||||
result.insert(pos + 1, indentStr);
|
||||
pos = result.find('\n', pos + 1);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
// Sets the indentation to be applied to all WriteLn's. The indentation is added to the
|
||||
// primary write, and to any newlines specified within the write. Note that this applies
|
||||
// to calls to WriteLn *only* -- calls to Write bypass the indentation parser.
|
||||
const IConsoleWriter& IConsoleWriter::SetIndent(int tabcount) const
|
||||
{
|
||||
conlog_Indent += tabcount;
|
||||
pxAssert(conlog_Indent >= 0);
|
||||
return *this;
|
||||
}
|
||||
|
||||
IConsoleWriter IConsoleWriter::Indent(int tabcount) const
|
||||
{
|
||||
IConsoleWriter retval = *this;
|
||||
retval._imm_indentation = tabcount;
|
||||
return retval;
|
||||
}
|
||||
|
||||
// Changes the active console color.
|
||||
// This color will be unset by calls to colored text methods
|
||||
// such as ErrorMsg and Notice.
|
||||
const IConsoleWriter& IConsoleWriter::SetColor(ConsoleColors color) const
|
||||
{
|
||||
// Ignore current color requests since, well, the current color is already set. ;)
|
||||
if (color == Color_Current)
|
||||
return *this;
|
||||
|
||||
pxAssertMsg((color > Color_Current) && (color < ConsoleColors_Count), "Invalid ConsoleColor specified.");
|
||||
|
||||
if (conlog_Color != color)
|
||||
DoSetColor(conlog_Color = color);
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
ConsoleColors IConsoleWriter::GetColor() const
|
||||
{
|
||||
return conlog_Color;
|
||||
}
|
||||
|
||||
// Restores the console color to default (usually black, or low-intensity white if the console uses a black background)
|
||||
const IConsoleWriter& IConsoleWriter::ClearColor() const
|
||||
{
|
||||
if (conlog_Color != DefaultConsoleColor)
|
||||
DoSetColor(conlog_Color = DefaultConsoleColor);
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
// --------------------------------------------------------------------------------------
|
||||
// ASCII/UTF8 (char*)
|
||||
// --------------------------------------------------------------------------------------
|
||||
|
||||
bool IConsoleWriter::FormatV(const char* fmt, va_list args) const
|
||||
{
|
||||
// TODO: Make this less rubbish
|
||||
if ((_imm_indentation + conlog_Indent) > 0)
|
||||
{
|
||||
DoWriteLn(_addIndentation(StringUtil::StdStringFromFormatV(fmt, args), conlog_Indent).c_str());
|
||||
if (s_log_timestamps)
|
||||
{
|
||||
std::fprintf(s_file_handle.get(), TIMESTAMP_PRINTF_STRING "%.*s\n", GetCurrentMessageTime(),
|
||||
static_cast<int>(message.size()), message.data());
|
||||
}
|
||||
else
|
||||
{
|
||||
std::fprintf(s_file_handle.get(), "%.*s\n", static_cast<int>(message.size()), message.data());
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
DoWriteLn(StringUtil::StdStringFromFormatV(fmt, args).c_str());
|
||||
if (s_log_timestamps)
|
||||
{
|
||||
std::fprintf(s_file_handle.get(), TIMESTAMP_PRINTF_STRING "\n", GetCurrentMessageTime());
|
||||
}
|
||||
else
|
||||
{
|
||||
std::fputc('\n', s_file_handle.get());
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
std::fflush(s_file_handle.get());
|
||||
}
|
||||
|
||||
bool IConsoleWriter::WriteLn(const char* fmt, ...) const
|
||||
bool Log::IsFileOutputEnabled()
|
||||
{
|
||||
va_list args;
|
||||
va_start(args, fmt);
|
||||
FormatV(fmt, args);
|
||||
va_end(args);
|
||||
|
||||
return false;
|
||||
return (s_file_level > LOGLEVEL_NONE);
|
||||
}
|
||||
|
||||
bool IConsoleWriter::WriteLn(ConsoleColors color, const char* fmt, ...) const
|
||||
bool Log::SetFileOutputLevel(LOGLEVEL level, std::string path)
|
||||
{
|
||||
va_list args;
|
||||
va_start(args, fmt);
|
||||
ConsoleColorScope cs(color);
|
||||
FormatV(fmt, args);
|
||||
va_end(args);
|
||||
std::unique_lock lock(s_file_mutex);
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
bool IConsoleWriter::Error(const char* fmt, ...) const
|
||||
{
|
||||
va_list args;
|
||||
va_start(args, fmt);
|
||||
ConsoleColorScope cs(Color_StrongRed);
|
||||
FormatV(fmt, args);
|
||||
va_end(args);
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
bool IConsoleWriter::Warning(const char* fmt, ...) const
|
||||
{
|
||||
va_list args;
|
||||
va_start(args, fmt);
|
||||
ConsoleColorScope cs(Color_StrongOrange);
|
||||
FormatV(fmt, args);
|
||||
va_end(args);
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
bool IConsoleWriter::WriteLn(ConsoleColors color, const std::string& str) const
|
||||
{
|
||||
ConsoleColorScope cs(color);
|
||||
return WriteLn(str);
|
||||
}
|
||||
|
||||
bool IConsoleWriter::WriteLn(const std::string& str) const
|
||||
{
|
||||
// TODO: Make this less rubbish
|
||||
if ((_imm_indentation + conlog_Indent) > 0)
|
||||
const bool was_enabled = (s_file_level > LOGLEVEL_NONE);
|
||||
const bool new_enabled = (level > LOGLEVEL_NONE && !path.empty());
|
||||
if (was_enabled != new_enabled || (new_enabled && path == s_file_path))
|
||||
{
|
||||
DoWriteLn(_addIndentation(str, conlog_Indent).c_str());
|
||||
if (new_enabled)
|
||||
{
|
||||
if (!s_file_handle || s_file_path != path)
|
||||
{
|
||||
s_file_handle.reset();
|
||||
s_file_handle = FileSystem::OpenManagedCFile(path.c_str(), "wb");
|
||||
if (s_file_handle)
|
||||
{
|
||||
s_file_path = std::move(path);
|
||||
}
|
||||
else
|
||||
{
|
||||
s_file_path = {};
|
||||
|
||||
if (IsConsoleOutputEnabled())
|
||||
WriteToConsole(LOGLEVEL_ERROR, Color_StrongRed, TinyString::from_fmt("Failed to open log file '{}'", path));
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
s_file_handle.reset();
|
||||
s_file_path = {};
|
||||
}
|
||||
}
|
||||
|
||||
s_file_level = s_file_handle ? level : LOGLEVEL_NONE;
|
||||
return IsFileOutputEnabled();
|
||||
}
|
||||
|
||||
std::FILE* Log::GetFileLogHandle()
|
||||
{
|
||||
std::unique_lock lock(s_file_mutex);
|
||||
return s_file_handle.get();
|
||||
}
|
||||
|
||||
bool Log::IsHostOutputEnabled()
|
||||
{
|
||||
return (s_host_level > LOGLEVEL_NONE);
|
||||
}
|
||||
|
||||
void Log::SetHostOutputLevel(LOGLEVEL level, HostCallbackType callback)
|
||||
{
|
||||
s_host_callback = callback;
|
||||
s_host_level = callback ? level : LOGLEVEL_NONE;
|
||||
UpdateMaxLevel();
|
||||
}
|
||||
|
||||
bool Log::AreTimestampsEnabled()
|
||||
{
|
||||
return s_log_timestamps;
|
||||
}
|
||||
|
||||
void Log::SetTimestampsEnabled(bool enabled)
|
||||
{
|
||||
s_log_timestamps = enabled;
|
||||
}
|
||||
|
||||
LOGLEVEL Log::GetMaxLevel()
|
||||
{
|
||||
return s_max_level;
|
||||
}
|
||||
|
||||
__ri void Log::UpdateMaxLevel()
|
||||
{
|
||||
s_max_level = std::max(s_console_level, std::max(s_debug_level, std::max(s_file_level, s_host_level)));
|
||||
}
|
||||
|
||||
void Log::ExecuteCallbacks(LOGLEVEL level, ConsoleColors color, std::string_view message)
|
||||
{
|
||||
// TODO: Cache the message time.
|
||||
|
||||
// Split newlines into separate messages.
|
||||
std::string_view::size_type start_pos = 0;
|
||||
if (std::string_view::size_type end_pos = message.find('\n'); end_pos != std::string::npos) [[unlikely]]
|
||||
{
|
||||
for (;;)
|
||||
{
|
||||
std::string_view message_line;
|
||||
if (start_pos != end_pos)
|
||||
message_line = message.substr(start_pos, (end_pos == std::string_view::npos) ? end_pos : end_pos - start_pos);
|
||||
|
||||
ExecuteCallbacks(level, color, message_line);
|
||||
|
||||
if (end_pos == std::string_view::npos)
|
||||
return;
|
||||
|
||||
start_pos = end_pos + 1;
|
||||
end_pos = message.find('\n', start_pos);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
pxAssert(level > LOGLEVEL_NONE);
|
||||
if (level <= s_console_level)
|
||||
WriteToConsole(level, color, message);
|
||||
|
||||
if (level <= s_debug_level)
|
||||
WriteToDebug(level, color, message);
|
||||
|
||||
if (level <= s_file_level)
|
||||
WriteToFile(level, color, message);
|
||||
|
||||
if (level <= s_host_level)
|
||||
{
|
||||
// double check in case of race here
|
||||
const HostCallbackType callback = s_host_callback;
|
||||
if (callback)
|
||||
s_host_callback(level, color, message);
|
||||
}
|
||||
}
|
||||
|
||||
void Log::Write(LOGLEVEL level, ConsoleColors color, std::string_view message)
|
||||
{
|
||||
if (level > s_max_level)
|
||||
return;
|
||||
|
||||
ExecuteCallbacks(level, color, message);
|
||||
}
|
||||
|
||||
void Log::Writef(LOGLEVEL level, ConsoleColors color, const char* format, ...)
|
||||
{
|
||||
std::va_list ap;
|
||||
va_start(ap, format);
|
||||
Writev(level, color, format, ap);
|
||||
va_end(ap);
|
||||
}
|
||||
|
||||
void Log::Writev(LOGLEVEL level, ConsoleColors color, const char* format, va_list ap)
|
||||
{
|
||||
if (level > s_max_level)
|
||||
return;
|
||||
|
||||
std::va_list ap_copy;
|
||||
va_copy(ap_copy, ap);
|
||||
|
||||
#ifdef _WIN32
|
||||
const u32 required_size = static_cast<u32>(_vscprintf(format, ap_copy));
|
||||
#else
|
||||
const u32 required_size = std::vsnprintf(nullptr, 0, format, ap_copy);
|
||||
#endif
|
||||
va_end(ap_copy);
|
||||
|
||||
if (required_size < 512)
|
||||
{
|
||||
char buffer[512];
|
||||
const int len = std::vsnprintf(buffer, std::size(buffer), format, ap);
|
||||
if (len > 0)
|
||||
ExecuteCallbacks(level, color, std::string_view(buffer, static_cast<size_t>(len)));
|
||||
}
|
||||
else
|
||||
{
|
||||
DoWriteLn(str.c_str());
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
bool IConsoleWriter::Error(const std::string& str) const
|
||||
{
|
||||
return WriteLn(Color_StrongRed, str);
|
||||
}
|
||||
|
||||
bool IConsoleWriter::Warning(const std::string& str) const
|
||||
{
|
||||
return WriteLn(Color_StrongOrange, str);
|
||||
}
|
||||
|
||||
// --------------------------------------------------------------------------------------
|
||||
// ConsoleColorScope / ConsoleIndentScope
|
||||
// --------------------------------------------------------------------------------------
|
||||
|
||||
ConsoleColorScope::ConsoleColorScope(ConsoleColors newcolor)
|
||||
{
|
||||
m_IsScoped = false;
|
||||
m_newcolor = newcolor;
|
||||
EnterScope();
|
||||
}
|
||||
|
||||
ConsoleColorScope::~ConsoleColorScope()
|
||||
{
|
||||
LeaveScope();
|
||||
}
|
||||
|
||||
void ConsoleColorScope::EnterScope()
|
||||
{
|
||||
if (!m_IsScoped)
|
||||
{
|
||||
m_old_color = Console.GetColor();
|
||||
Console.SetColor(m_newcolor);
|
||||
m_IsScoped = true;
|
||||
char* buffer = new char[required_size + 1];
|
||||
const int len = std::vsnprintf(buffer, required_size + 1, format, ap);
|
||||
if (len > 0)
|
||||
ExecuteCallbacks(level, color, std::string_view(buffer, static_cast<size_t>(len)));
|
||||
delete[] buffer;
|
||||
}
|
||||
}
|
||||
|
||||
void ConsoleColorScope::LeaveScope()
|
||||
void Log::WriteFmtArgs(LOGLEVEL level, ConsoleColors color, fmt::string_view fmt, fmt::format_args args)
|
||||
{
|
||||
m_IsScoped = m_IsScoped && (Console.SetColor(m_old_color), false);
|
||||
}
|
||||
|
||||
ConsoleIndentScope::ConsoleIndentScope(int tabs)
|
||||
{
|
||||
m_IsScoped = false;
|
||||
m_amount = tabs;
|
||||
EnterScope();
|
||||
}
|
||||
|
||||
ConsoleIndentScope::~ConsoleIndentScope()
|
||||
{
|
||||
LeaveScope();
|
||||
}
|
||||
|
||||
void ConsoleIndentScope::EnterScope()
|
||||
{
|
||||
m_IsScoped = m_IsScoped || (Console.SetIndent(m_amount), true);
|
||||
}
|
||||
|
||||
void ConsoleIndentScope::LeaveScope()
|
||||
{
|
||||
m_IsScoped = m_IsScoped && (Console.SetIndent(-m_amount), false);
|
||||
}
|
||||
|
||||
|
||||
ConsoleAttrScope::ConsoleAttrScope(ConsoleColors newcolor, int indent)
|
||||
{
|
||||
m_old_color = Console.GetColor();
|
||||
Console.SetIndent(m_tabsize = indent);
|
||||
Console.SetColor(newcolor);
|
||||
}
|
||||
|
||||
ConsoleAttrScope::~ConsoleAttrScope()
|
||||
{
|
||||
Console.SetColor(m_old_color);
|
||||
Console.SetIndent(-m_tabsize);
|
||||
}
|
||||
|
||||
|
||||
// --------------------------------------------------------------------------------------
|
||||
// Default Writer for C++ init / startup:
|
||||
// --------------------------------------------------------------------------------------
|
||||
// Currently all build types default to Stdout, which is very functional on Linux but not
|
||||
// always so useful on Windows (which itself lacks a proper stdout console without using
|
||||
// platform specific code). Under windows Stdout will attempt to write to the IDE Debug
|
||||
// console, if one is available (such as running pcsx2 via MSVC). If not available, then
|
||||
// the log message will pretty much be lost into the ether.
|
||||
//
|
||||
#define _DefaultWriter_ ConsoleWriter_Stdout
|
||||
|
||||
IConsoleWriter Console = _DefaultWriter_;
|
||||
IConsoleWriter DevConWriter = _DefaultWriter_;
|
||||
bool DevConWriterEnabled = false;
|
||||
|
||||
#ifdef PCSX2_DEBUG
|
||||
IConsoleWriter DbgConWriter = _DefaultWriter_;
|
||||
#endif
|
||||
|
||||
NullConsoleWriter NullCon = {};
|
||||
|
||||
// --------------------------------------------------------------------------------------
|
||||
// ConsoleLogSource (implementations)
|
||||
// --------------------------------------------------------------------------------------
|
||||
|
||||
// Writes to the console using the specified color. This overrides the default color setting
|
||||
// for this log.
|
||||
bool ConsoleLogSource::WriteV(ConsoleColors color, const char* fmt, va_list list) const
|
||||
{
|
||||
ConsoleColorScope cs(color);
|
||||
Console.WriteLn(StringUtil::StdStringFromFormatV(fmt, list));
|
||||
return false;
|
||||
}
|
||||
|
||||
// Writes to the console using the source's default color. Note that the source's default
|
||||
// color will always be used, thus ConsoleColorScope() will not be effectual unless the
|
||||
// console's default color is Color_Default.
|
||||
bool ConsoleLogSource::WriteV(const char* fmt, va_list list) const
|
||||
{
|
||||
WriteV(DefaultColor, fmt, list);
|
||||
return false;
|
||||
if (level > s_max_level)
|
||||
return;
|
||||
|
||||
fmt::memory_buffer buffer;
|
||||
fmt::vformat_to(std::back_inserter(buffer), fmt, args);
|
||||
|
||||
ExecuteCallbacks(level, color, std::string_view(buffer.data(), buffer.size()));
|
||||
}
|
||||
|
|
299
common/Console.h
299
common/Console.h
|
@ -1,16 +1,19 @@
|
|||
// SPDX-FileCopyrightText: 2002-2023 PCSX2 Dev Team
|
||||
// SPDX-FileCopyrightText: 2002-2024 PCSX2 Dev Team
|
||||
// SPDX-License-Identifier: LGPL-3.0+
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "Pcsx2Defs.h"
|
||||
|
||||
#include "fmt/core.h"
|
||||
|
||||
#include <cstdarg>
|
||||
#include <string>
|
||||
|
||||
// TODO: This whole thing needs to get ripped out.
|
||||
|
||||
enum ConsoleColors
|
||||
{
|
||||
Color_Current = -1,
|
||||
|
||||
Color_Default = 0,
|
||||
|
||||
Color_Black,
|
||||
|
@ -42,191 +45,145 @@ enum ConsoleColors
|
|||
ConsoleColors_Count
|
||||
};
|
||||
|
||||
static const ConsoleColors DefaultConsoleColor = Color_Default;
|
||||
|
||||
|
||||
// ----------------------------------------------------------------------------------------
|
||||
// IConsoleWriter -- For printing messages to the console.
|
||||
// ----------------------------------------------------------------------------------------
|
||||
// General ConsoleWrite Threading Guideline:
|
||||
// PCSX2 is a threaded environment and multiple threads can write to the console asynchronously.
|
||||
// Individual calls to ConsoleWriter APIs will be written in atomic fashion, however "partial"
|
||||
// logs may end up interrupted by logs on other threads. This is usually not a problem for
|
||||
// WriteLn, but may be undesirable for typical uses of Write. In cases where you want to
|
||||
// print multi-line blocks of uninterrupted logs, compound the entire log into a single large
|
||||
// string and issue that to WriteLn.
|
||||
//
|
||||
struct IConsoleWriter
|
||||
enum LOGLEVEL
|
||||
{
|
||||
// A direct console write, without tabbing or newlines. Useful to devs who want to do quick
|
||||
// logging of various junk; but should *not* be used in production code due.
|
||||
void(* WriteRaw)(const char* fmt);
|
||||
LOGLEVEL_NONE, // Silences all log traffic
|
||||
LOGLEVEL_ERROR,
|
||||
LOGLEVEL_WARNING,
|
||||
LOGLEVEL_INFO,
|
||||
LOGLEVEL_DEV,
|
||||
LOGLEVEL_DEBUG,
|
||||
LOGLEVEL_TRACE,
|
||||
|
||||
// WriteLn implementation for internal use only. Bypasses tabbing, prefixing, and other
|
||||
// formatting.
|
||||
void(* DoWriteLn)(const char* fmt);
|
||||
|
||||
// SetColor implementation for internal use only.
|
||||
void(* DoSetColor)(ConsoleColors color);
|
||||
|
||||
// Special implementation of DoWrite that's pretty much for MSVC use only.
|
||||
// All implementations should map to DoWrite, except Stdio which should map to Null.
|
||||
// (This avoids circular/recursive stdio output)
|
||||
void(* DoWriteFromStdout)(const char* fmt);
|
||||
|
||||
void(* Newline)();
|
||||
void(* SetTitle)(const char* title);
|
||||
|
||||
// internal value for indentation of individual lines. Use the Indent() member to invoke.
|
||||
int _imm_indentation;
|
||||
|
||||
// For internal use only.
|
||||
std::string _addIndentation(const std::string& src, int glob_indent) const;
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
// Public members; call these to print stuff to console!
|
||||
//
|
||||
// All functions always return false. Return value is provided only so that we can easily
|
||||
// disable logs at compile time using the "0&&action" macro trick.
|
||||
|
||||
ConsoleColors GetColor() const;
|
||||
const IConsoleWriter& SetColor(ConsoleColors color) const;
|
||||
const IConsoleWriter& ClearColor() const;
|
||||
const IConsoleWriter& SetIndent(int tabcount = 1) const;
|
||||
|
||||
IConsoleWriter Indent(int tabcount = 1) const;
|
||||
|
||||
bool FormatV(const char* fmt, va_list args) const;
|
||||
bool WriteLn(ConsoleColors color, const char* fmt, ...) const;
|
||||
bool WriteLn(const char* fmt, ...) const;
|
||||
bool Error(const char* fmt, ...) const;
|
||||
bool Warning(const char* fmt, ...) const;
|
||||
|
||||
bool WriteLn(ConsoleColors color, const std::string& str) const;
|
||||
bool WriteLn(const std::string& str) const;
|
||||
bool Error(const std::string& str) const;
|
||||
bool Warning(const std::string& str) const;
|
||||
LOGLEVEL_COUNT,
|
||||
};
|
||||
|
||||
// --------------------------------------------------------------------------------------
|
||||
// NullConsoleWriter
|
||||
// --------------------------------------------------------------------------------------
|
||||
// Used by Release builds for Debug and Devel writes (DbgCon / DevCon). Inlines to NOPs. :)
|
||||
//
|
||||
struct NullConsoleWriter
|
||||
// TODO: Move this elsewhere, add channels.
|
||||
|
||||
namespace Log
|
||||
{
|
||||
void WriteRaw(const char* fmt) {}
|
||||
void DoWriteLn(const char* fmt) {}
|
||||
void DoSetColor(ConsoleColors color) {}
|
||||
void DoWriteFromStdout(const char* fmt) {}
|
||||
void Newline() {}
|
||||
void SetTitle(const char* title) {}
|
||||
// log message callback type
|
||||
using HostCallbackType = void (*)(LOGLEVEL level, ConsoleColors color, std::string_view message);
|
||||
|
||||
// returns the time in seconds since the start of the process
|
||||
float GetCurrentMessageTime();
|
||||
|
||||
ConsoleColors GetColor() const { return Color_Current; }
|
||||
const NullConsoleWriter& SetColor(ConsoleColors color) const { return *this; }
|
||||
const NullConsoleWriter& ClearColor() const { return *this; }
|
||||
const NullConsoleWriter& SetIndent(int tabcount = 1) const { return *this; }
|
||||
// adds a standard console output
|
||||
bool IsConsoleOutputEnabled();
|
||||
void SetConsoleOutputLevel(LOGLEVEL level);
|
||||
|
||||
NullConsoleWriter Indent(int tabcount = 1) const { return NullConsoleWriter(); }
|
||||
// adds a debug console output
|
||||
bool IsDebugOutputEnabled();
|
||||
void SetDebugOutputLevel(LOGLEVEL level);
|
||||
|
||||
bool FormatV(const char* fmt, va_list args) const { return false; }
|
||||
bool WriteLn(ConsoleColors color, const char* fmt, ...) const { return false; }
|
||||
bool WriteLn(const char* fmt, ...) const { return false; }
|
||||
bool Error(const char* fmt, ...) const { return false; }
|
||||
bool Warning(const char* fmt, ...) const { return false; }
|
||||
// adds a file output
|
||||
bool IsFileOutputEnabled();
|
||||
bool SetFileOutputLevel(LOGLEVEL level, std::string path);
|
||||
|
||||
// returns the log file, this is really dangerous to use if it changes...
|
||||
std::FILE* GetFileLogHandle();
|
||||
|
||||
// adds host output
|
||||
bool IsHostOutputEnabled();
|
||||
void SetHostOutputLevel(LOGLEVEL level, HostCallbackType callback);
|
||||
|
||||
// sets logging timestamps
|
||||
bool AreTimestampsEnabled();
|
||||
void SetTimestampsEnabled(bool enabled);
|
||||
|
||||
// Returns the current global filtering level.
|
||||
LOGLEVEL GetMaxLevel();
|
||||
|
||||
// writes a message to the log
|
||||
void Write(LOGLEVEL level, ConsoleColors color, std::string_view message);
|
||||
void Writef(LOGLEVEL level, ConsoleColors color, const char* format, ...);
|
||||
void Writev(LOGLEVEL level, ConsoleColors color, const char* format, va_list ap);
|
||||
void WriteFmtArgs(LOGLEVEL level, ConsoleColors color, fmt::string_view fmt, fmt::format_args args);
|
||||
|
||||
template <typename... T>
|
||||
__fi static void Write(LOGLEVEL level, ConsoleColors color, fmt::format_string<T...> fmt, T&&... args)
|
||||
{
|
||||
// Avoid arg packing if filtered.
|
||||
if (level <= GetMaxLevel())
|
||||
return WriteFmtArgs(level, color, fmt, fmt::make_format_args(args...));
|
||||
}
|
||||
} // namespace Log
|
||||
|
||||
// Adapter classes to handle old code.
|
||||
template <LOGLEVEL level>
|
||||
struct ConsoleLogWriter
|
||||
{
|
||||
__fi static void Error(std::string_view str) { Log::Write(level, Color_StrongRed, str); }
|
||||
__fi static void Warning(std::string_view str) { Log::Write(level, Color_StrongOrange, str); }
|
||||
__fi static void WriteLn(std::string_view str) { Log::Write(level, Color_Default, str); }
|
||||
__fi static void WriteLn(ConsoleColors color, std::string_view str) { Log::Write(level, color, str); }
|
||||
__fi static void WriteLn() { Log::Write(level, Color_Default, std::string_view()); }
|
||||
__fi static void FormatV(const char* format, va_list ap) { Log::Writev(level, Color_Default, format, ap); }
|
||||
__fi static void FormatV(ConsoleColors color, const char* format, va_list ap) { Log::Writev(level, color, format, ap); }
|
||||
|
||||
#define MAKE_PRINTF_CONSOLE_WRITER(color) \
|
||||
do \
|
||||
{ \
|
||||
std::va_list ap; \
|
||||
va_start(ap, format); \
|
||||
Log::Writev(level, color, format, ap); \
|
||||
va_end(ap); \
|
||||
} while (0)
|
||||
|
||||
// clang-format off
|
||||
__fi static void Error(const char* format, ...) { MAKE_PRINTF_CONSOLE_WRITER(Color_StrongRed); }
|
||||
__fi static void Warning(const char* format, ...) { MAKE_PRINTF_CONSOLE_WRITER(Color_StrongOrange); }
|
||||
__fi static void WriteLn(const char* format, ...) { MAKE_PRINTF_CONSOLE_WRITER(Color_Default); }
|
||||
__fi static void WriteLn(ConsoleColors color, const char* format, ...) { MAKE_PRINTF_CONSOLE_WRITER(color); }
|
||||
// clang-format on
|
||||
|
||||
#undef MAKE_PRINTF_CONSOLE_WRITER
|
||||
|
||||
#define MAKE_FMT_CONSOLE_WRITER(color) do \
|
||||
{ \
|
||||
if (level <= Log::GetMaxLevel()) \
|
||||
Log::WriteFmtArgs(level, color, fmt, fmt::make_format_args(args...)); \
|
||||
} \
|
||||
while (0)
|
||||
|
||||
// clang-format off
|
||||
template<typename... T> __fi static void ErrorFmt(fmt::format_string<T...> fmt, T&&... args) { MAKE_FMT_CONSOLE_WRITER(Color_StrongRed); }
|
||||
template<typename... T> __fi static void WarningFmt(fmt::format_string<T...> fmt, T&&... args) { MAKE_FMT_CONSOLE_WRITER(Color_StrongOrange); }
|
||||
template<typename... T> __fi static void WriteLnFmt(fmt::format_string<T...> fmt, T&&... args) { MAKE_FMT_CONSOLE_WRITER(Color_Default); }
|
||||
template<typename... T> __fi static void WriteLnFmt(ConsoleColors color, fmt::format_string<T...> fmt, T&&... args) { MAKE_FMT_CONSOLE_WRITER(color); }
|
||||
// clang-format on
|
||||
|
||||
#undef MAKE_FMT_CONSOLE_WRITER
|
||||
};
|
||||
|
||||
// --------------------------------------------------------------------------------------
|
||||
// ConsoleIndentScope
|
||||
// --------------------------------------------------------------------------------------
|
||||
// Provides a scoped indentation of the IConsoleWriter interface for the current thread.
|
||||
// Any console writes performed from this scope will be indented by the specified number
|
||||
// of tab characters.
|
||||
//
|
||||
// Scoped Object Notes: Like most scoped objects, this is intended to be used as a stack
|
||||
// or temporary object only. Using it in a situation where the object's lifespan out-lives
|
||||
// a scope will almost certainly result in unintended /undefined side effects.
|
||||
//
|
||||
class ConsoleIndentScope
|
||||
struct NullLogWriter
|
||||
{
|
||||
DeclareNoncopyableObject(ConsoleIndentScope);
|
||||
// clang-format off
|
||||
__fi static bool Error(std::string_view str) { return false; }
|
||||
__fi static bool Warning(std::string_view str) { return false; }
|
||||
__fi static bool WriteLn(std::string_view str) { return false; }
|
||||
__fi static bool WriteLn(ConsoleColors color, std::string_view str) { return false; }
|
||||
__fi static bool WriteLn() { return false; }
|
||||
|
||||
protected:
|
||||
int m_amount;
|
||||
bool m_IsScoped;
|
||||
__fi static bool Error(const char* format, ...) { return false; }
|
||||
__fi static bool Warning(const char* format, ...) { return false; }
|
||||
__fi static bool WriteLn(const char* format, ...) { return false; }
|
||||
__fi static bool WriteLn(ConsoleColors color, const char* format, ...) { return false; }
|
||||
|
||||
public:
|
||||
// Constructor: The specified number of tabs will be appended to the current indentation
|
||||
// setting. The tabs will be unrolled when the object leaves scope or is destroyed.
|
||||
ConsoleIndentScope(int tabs = 1);
|
||||
virtual ~ConsoleIndentScope();
|
||||
void EnterScope();
|
||||
void LeaveScope();
|
||||
template<typename... T> __fi static bool ErrorFmt(fmt::format_string<T...> fmt, T&&... args) { return false; }
|
||||
template<typename... T> __fi static bool WarningFmt(fmt::format_string<T...> fmt, T&&... args) { return false; }
|
||||
template<typename... T> __fi static bool WriteLnFmt(fmt::format_string<T...> fmt, T&&... args) { return false; }
|
||||
template<typename... T> __fi static bool WriteLnFmt(ConsoleColors color, fmt::format_string<T...> fmt, T&&... args) { return false; }
|
||||
// clang-format on
|
||||
};
|
||||
|
||||
// --------------------------------------------------------------------------------------
|
||||
// ConsoleColorScope
|
||||
// --------------------------------------------------------------------------------------
|
||||
class ConsoleColorScope
|
||||
{
|
||||
DeclareNoncopyableObject(ConsoleColorScope);
|
||||
extern ConsoleLogWriter<LOGLEVEL_INFO> Console;
|
||||
extern ConsoleLogWriter<LOGLEVEL_DEV> DevCon;
|
||||
|
||||
protected:
|
||||
ConsoleColors m_newcolor;
|
||||
ConsoleColors m_old_color;
|
||||
bool m_IsScoped;
|
||||
|
||||
public:
|
||||
ConsoleColorScope(ConsoleColors newcolor);
|
||||
virtual ~ConsoleColorScope();
|
||||
void EnterScope();
|
||||
void LeaveScope();
|
||||
};
|
||||
|
||||
// --------------------------------------------------------------------------------------
|
||||
// ConsoleAttrScope
|
||||
// --------------------------------------------------------------------------------------
|
||||
// Applies both color and tab attributes in a single object constructor.
|
||||
//
|
||||
class ConsoleAttrScope
|
||||
{
|
||||
DeclareNoncopyableObject(ConsoleAttrScope);
|
||||
|
||||
protected:
|
||||
ConsoleColors m_old_color;
|
||||
int m_tabsize;
|
||||
|
||||
public:
|
||||
ConsoleAttrScope(ConsoleColors newcolor, int indent = 0);
|
||||
virtual ~ConsoleAttrScope();
|
||||
};
|
||||
|
||||
extern IConsoleWriter Console;
|
||||
|
||||
#if defined(__POSIX__)
|
||||
extern void Console_SetStdout(FILE* fp);
|
||||
#endif
|
||||
extern void Console_SetActiveHandler(const IConsoleWriter& writer, FILE* flushfp = NULL);
|
||||
|
||||
extern const IConsoleWriter ConsoleWriter_Null;
|
||||
extern const IConsoleWriter ConsoleWriter_Stdout;
|
||||
extern const IConsoleWriter ConsoleWriter_Assert;
|
||||
|
||||
extern NullConsoleWriter NullCon;
|
||||
|
||||
extern IConsoleWriter DevConWriter;
|
||||
extern bool DevConWriterEnabled;
|
||||
|
||||
#ifdef PCSX2_DEVBUILD
|
||||
#define DevCon DevConWriter
|
||||
#else
|
||||
#define DevCon DevConWriterEnabled&& DevConWriter
|
||||
#endif
|
||||
|
||||
#ifdef PCSX2_DEBUG
|
||||
extern IConsoleWriter DbgConWriter;
|
||||
#ifdef _DEBUG
|
||||
extern ConsoleLogWriter<LOGLEVEL_DEBUG> DbgConWriter;
|
||||
#define DbgCon DbgConWriter
|
||||
#else
|
||||
#define DbgCon 0 && NullCon
|
||||
extern NullLogWriter DbgConWriter;
|
||||
#define DbgCon 0 && DbgConWriter
|
||||
#endif
|
||||
|
|
|
@ -183,7 +183,15 @@ public:
|
|||
return false;
|
||||
}
|
||||
|
||||
bool WriteV(const char* fmt, va_list list) const;
|
||||
bool WriteV(const char* fmt, va_list list) const
|
||||
{
|
||||
Console.FormatV(fmt, list);
|
||||
return false;
|
||||
}
|
||||
|
||||
bool WriteV(ConsoleColors color, const char* fmt, va_list list) const;
|
||||
bool WriteV(ConsoleColors color, const char* fmt, va_list list) const
|
||||
{
|
||||
Console.FormatV(color, fmt, list);
|
||||
return false;
|
||||
}
|
||||
};
|
||||
|
|
|
@ -35,7 +35,6 @@
|
|||
#include "pcsx2/INISettingsInterface.h"
|
||||
#include "pcsx2/ImGui/ImGuiManager.h"
|
||||
#include "pcsx2/Input/InputManager.h"
|
||||
#include "pcsx2/LogSink.h"
|
||||
#include "pcsx2/MTGS.h"
|
||||
#include "pcsx2/SIO/Pad/Pad.h"
|
||||
#include "pcsx2/PerformanceMetrics.h"
|
||||
|
@ -442,7 +441,7 @@ void GSRunner::InitializeConsole()
|
|||
const char* var = std::getenv("PCSX2_NOCONSOLE");
|
||||
s_no_console = (var && StringUtil::FromChars<bool>(var).value_or(false));
|
||||
if (!s_no_console)
|
||||
LogSink::InitializeEarlyConsole();
|
||||
Log::SetConsoleOutputLevel(LOGLEVEL_DEBUG);
|
||||
}
|
||||
|
||||
bool GSRunner::ParseCommandLineArgs(int argc, char* argv[], VMBootParameters& params)
|
||||
|
@ -568,7 +567,7 @@ bool GSRunner::ParseCommandLineArgs(int argc, char* argv[], VMBootParameters& pa
|
|||
{
|
||||
// disable timestamps, since we want to be able to diff the logs
|
||||
Console.WriteLn("Logging to %s...", logfile);
|
||||
LogSink::SetFileLogPath(logfile);
|
||||
VMManager::Internal::SetFileLogPath(logfile);
|
||||
s_settings_interface.SetBoolValue("Logging", "EnableFileLogging", true);
|
||||
s_settings_interface.SetBoolValue("Logging", "EnableTimestamps", false);
|
||||
}
|
||||
|
@ -698,7 +697,6 @@ int main(int argc, char* argv[])
|
|||
|
||||
VMManager::Internal::CPUThreadShutdown();
|
||||
GSRunner::DestroyPlatformWindow();
|
||||
LogSink::CloseFileLog();
|
||||
|
||||
return EXIT_SUCCESS;
|
||||
}
|
||||
|
|
|
@ -27,7 +27,6 @@
|
|||
#include "pcsx2/GSDumpReplayer.h"
|
||||
#include "pcsx2/GameList.h"
|
||||
#include "pcsx2/Host.h"
|
||||
#include "pcsx2/LogSink.h"
|
||||
#include "pcsx2/MTGS.h"
|
||||
#include "pcsx2/PerformanceMetrics.h"
|
||||
#include "pcsx2/Recording/InputRecording.h"
|
||||
|
|
|
@ -26,7 +26,6 @@
|
|||
#include "pcsx2/ImGui/ImGuiManager.h"
|
||||
#include "pcsx2/ImGui/ImGuiOverlays.h"
|
||||
#include "pcsx2/Input/InputManager.h"
|
||||
#include "pcsx2/LogSink.h"
|
||||
#include "pcsx2/MTGS.h"
|
||||
#include "pcsx2/PerformanceMetrics.h"
|
||||
#include "pcsx2/SysForwardDefs.h"
|
||||
|
@ -64,6 +63,7 @@ EmuThread* g_emu_thread = nullptr;
|
|||
//////////////////////////////////////////////////////////////////////////
|
||||
namespace QtHost
|
||||
{
|
||||
static void InitializeEarlyConsole();
|
||||
static void PrintCommandLineVersion();
|
||||
static void PrintCommandLineHelp(const std::string_view& progname);
|
||||
static std::shared_ptr<VMBootParameters>& AutoBoot(std::shared_ptr<VMBootParameters>& autoboot);
|
||||
|
@ -1240,7 +1240,8 @@ bool QtHost::InitializeConfig()
|
|||
s_run_setup_wizard =
|
||||
s_run_setup_wizard || s_base_settings_interface->GetBoolValue("UI", "SetupWizardIncomplete", false);
|
||||
|
||||
LogSink::SetBlockSystemConsole(QtHost::InNoGUIMode());
|
||||
// TODO: -nogui console block?
|
||||
|
||||
VMManager::Internal::LoadStartupSettings();
|
||||
InstallTranslator(nullptr);
|
||||
return true;
|
||||
|
@ -1575,12 +1576,27 @@ static void SignalHandler(int signal)
|
|||
#endif
|
||||
}
|
||||
|
||||
#ifdef _WIN32
|
||||
|
||||
static BOOL WINAPI ConsoleCtrlHandler(DWORD dwCtrlType)
|
||||
{
|
||||
if (dwCtrlType != CTRL_C_EVENT)
|
||||
return FALSE;
|
||||
|
||||
SignalHandler(SIGTERM);
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
void QtHost::HookSignals()
|
||||
{
|
||||
std::signal(SIGINT, SignalHandler);
|
||||
std::signal(SIGTERM, SignalHandler);
|
||||
|
||||
#ifdef __linux__
|
||||
#if defined(_WIN32)
|
||||
SetConsoleCtrlHandler(ConsoleCtrlHandler, FALSE);
|
||||
#elif defined(__linux__)
|
||||
// Ignore SIGCHLD by default on Linux, since we kick off aplay asynchronously.
|
||||
struct sigaction sa_chld = {};
|
||||
sigemptyset(&sa_chld.sa_mask);
|
||||
|
@ -1589,9 +1605,14 @@ void QtHost::HookSignals()
|
|||
#endif
|
||||
}
|
||||
|
||||
void QtHost::InitializeEarlyConsole()
|
||||
{
|
||||
Log::SetConsoleOutputLevel(LOGLEVEL_DEBUG);
|
||||
}
|
||||
|
||||
void QtHost::PrintCommandLineVersion()
|
||||
{
|
||||
LogSink::InitializeEarlyConsole();
|
||||
InitializeEarlyConsole();
|
||||
std::fprintf(stderr, "%s\n", (GetAppNameAndVersion() + GetAppConfigSuffix()).toUtf8().constData());
|
||||
std::fprintf(stderr, "https://pcsx2.net/\n");
|
||||
std::fprintf(stderr, "\n");
|
||||
|
@ -1715,7 +1736,7 @@ bool QtHost::ParseCommandLineOptions(const QStringList& args, std::shared_ptr<VM
|
|||
}
|
||||
else if (CHECK_ARG_PARAM(QStringLiteral("-logfile")))
|
||||
{
|
||||
LogSink::SetFileLogPath((++it)->toStdString());
|
||||
VMManager::Internal::SetFileLogPath((++it)->toStdString());
|
||||
continue;
|
||||
}
|
||||
else if (CHECK_ARG(QStringLiteral("-bios")))
|
||||
|
@ -1736,7 +1757,7 @@ bool QtHost::ParseCommandLineOptions(const QStringList& args, std::shared_ptr<VM
|
|||
}
|
||||
else if (CHECK_ARG(QStringLiteral("-earlyconsolelog")))
|
||||
{
|
||||
LogSink::InitializeEarlyConsole();
|
||||
InitializeEarlyConsole();
|
||||
continue;
|
||||
}
|
||||
else if (CHECK_ARG(QStringLiteral("-bigpicture")))
|
||||
|
@ -1798,7 +1819,6 @@ bool QtHost::ParseCommandLineOptions(const QStringList& args, std::shared_ptr<VM
|
|||
// or disc, we don't want to actually start.
|
||||
if (autoboot && !autoboot->source_type.has_value() && autoboot->filename.empty() && autoboot->elf_override.empty())
|
||||
{
|
||||
LogSink::InitializeEarlyConsole();
|
||||
Console.Warning("Skipping autoboot due to no boot parameters.");
|
||||
autoboot.reset();
|
||||
}
|
||||
|
@ -1956,8 +1976,5 @@ shutdown_and_exit:
|
|||
if (s_base_settings_interface->IsDirty())
|
||||
s_base_settings_interface->Save();
|
||||
|
||||
// Ensure emulog is flushed.
|
||||
LogSink::CloseFileLog();
|
||||
|
||||
return result;
|
||||
}
|
||||
|
|
|
@ -476,7 +476,7 @@ static CDVDDiscType GetPS2ElfName(IsoReader& isor, std::string* name, std::strin
|
|||
if (value.empty() && (lineno == (lines.size() - 1)))
|
||||
{
|
||||
Console.Warning("(SYSTEM.CNF) Unusual or malformed entry in SYSTEM.CNF ignored:");
|
||||
Console.Indent().WriteLn(std::string(line));
|
||||
Console.WarningFmt(" {}", line);
|
||||
continue;
|
||||
}
|
||||
|
||||
|
|
|
@ -260,14 +260,12 @@ bool InputIsoFile::Open(std::string srcfile, Error* error, bool testOnly)
|
|||
|
||||
Console.WriteLn(Color_StrongBlue, "isoFile open ok: %s", m_filename.c_str());
|
||||
|
||||
ConsoleIndentScope indent;
|
||||
|
||||
Console.WriteLn("Image type = %s", nameFromType(m_type));
|
||||
//Console.WriteLn("Fileparts = %u", m_numparts); // Pointless print, it's 1 unless it says otherwise above
|
||||
DevCon.WriteLn("blocks = %u", m_blocks);
|
||||
DevCon.WriteLn("offset = %d", m_offset);
|
||||
DevCon.WriteLn("blocksize = %u", m_blocksize);
|
||||
DevCon.WriteLn("blockoffset = %d", m_blockofs);
|
||||
Console.WriteLn(" Image type = %s", nameFromType(m_type));
|
||||
//Console.WriteLn(" Fileparts = %u", m_numparts); // Pointless print, it's 1 unless it says otherwise above
|
||||
DevCon.WriteLn(" blocks = %u", m_blocks);
|
||||
DevCon.WriteLn(" offset = %d", m_offset);
|
||||
DevCon.WriteLn(" blocksize = %u", m_blocksize);
|
||||
DevCon.WriteLn(" blockoffset = %d", m_blockofs);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
|
|
@ -77,7 +77,6 @@ set(pcsx2Sources
|
|||
HwRead.cpp
|
||||
HwWrite.cpp
|
||||
LayeredSettingsInterface.cpp
|
||||
LogSink.cpp
|
||||
INISettingsInterface.cpp
|
||||
Interpreter.cpp
|
||||
IopBios.cpp
|
||||
|
@ -169,7 +168,6 @@ set(pcsx2Headers
|
|||
IopHw.h
|
||||
IopMem.h
|
||||
LayeredSettingsInterface.h
|
||||
LogSink.h
|
||||
PINE.h
|
||||
Mdec.h
|
||||
MTGS.h
|
||||
|
|
|
@ -417,7 +417,7 @@ void UpdateVSyncRate(bool force)
|
|||
Console.WriteLn(Color_Green, "(UpdateVSyncRate) Mode Changed to %s.", ReportVideoMode());
|
||||
|
||||
if (custom && video_mode_initialized)
|
||||
Console.Indent().WriteLn(Color_StrongGreen, "... with user configured refresh rate: %.02f Hz", vertical_frequency);
|
||||
Console.WriteLn(Color_StrongGreen, " ... with user configured refresh rate: %.02f Hz", vertical_frequency);
|
||||
|
||||
hsyncCounter.CycleT = (hsyncCounter.Mode == MODE_HBLANK) ? vSyncInfo.hBlank : vSyncInfo.hRender;
|
||||
vsyncCounter.CycleT = (vsyncCounter.Mode == MODE_GSBLANK) ?
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
// SPDX-FileCopyrightText: 2002-2023 PCSX2 Dev Team
|
||||
// SPDX-FileCopyrightText: 2002-2024 PCSX2 Dev Team
|
||||
// SPDX-License-Identifier: LGPL-3.0+
|
||||
|
||||
#pragma once
|
||||
|
@ -7,8 +7,10 @@
|
|||
#include "Config.h"
|
||||
#include "Memory.h"
|
||||
|
||||
#include <string>
|
||||
|
||||
// TODO: Purge emuLog and all this other nonsense, just go through Log with LOGLEVEL_TRACE.
|
||||
extern FILE *emuLog;
|
||||
extern std::string emuLogName;
|
||||
|
||||
extern char* disVU0MicroUF(u32 code, u32 pc);
|
||||
extern char* disVU0MicroLF(u32 code, u32 pc);
|
||||
|
@ -214,23 +216,34 @@ class ConsoleLogFromVM : public BaseTraceLogSource
|
|||
public:
|
||||
ConsoleLogFromVM( const TraceLogDescriptor* desc ) : _parent( desc ) {}
|
||||
|
||||
bool Write( const char* msg ) const
|
||||
bool Write(std::string_view msg)
|
||||
{
|
||||
ConsoleColorScope cs(conColor);
|
||||
Console.WriteRaw(msg);
|
||||
|
||||
// Buffered output isn't compatible with the testsuite. The end of test
|
||||
// doesn't always get flushed. Let's just flush all the output if EE/IOP
|
||||
// print anything.
|
||||
fflush(NULL);
|
||||
for (const char ch : msg)
|
||||
{
|
||||
if (ch == '\n')
|
||||
{
|
||||
if (!m_buffer.empty())
|
||||
{
|
||||
Console.WriteLn(conColor, m_buffer);
|
||||
m_buffer.clear();
|
||||
}
|
||||
}
|
||||
else if (ch < 0x20)
|
||||
{
|
||||
// Ignore control characters.
|
||||
// Otherwise you get fun bells going off.
|
||||
}
|
||||
else
|
||||
{
|
||||
m_buffer.push_back(ch);
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
bool Write(const std::string& msg) const
|
||||
{
|
||||
return Write(msg.c_str());
|
||||
}
|
||||
private:
|
||||
std::string m_buffer;
|
||||
};
|
||||
|
||||
// --------------------------------------------------------------------------------------
|
||||
|
|
|
@ -171,7 +171,7 @@ static bool OpenGSDevice(GSRendererType renderer, bool clear_state_on_fail, bool
|
|||
GSConfig.OsdShowGPU = GSConfig.OsdShowGPU && g_gs_device->SetGPUTimingEnabled(true);
|
||||
|
||||
Console.WriteLn(Color_StrongGreen, "%s Graphics Driver Info:", GSDevice::RenderAPIToString(new_api));
|
||||
Console.Indent().WriteLn(g_gs_device->GetDriverInfo());
|
||||
Console.WriteLn(g_gs_device->GetDriverInfo());
|
||||
|
||||
return true;
|
||||
}
|
||||
|
|
|
@ -115,7 +115,7 @@ void ReportIPU()
|
|||
Console.WriteLn("coded_block_pattern = 0x%x.", coded_block_pattern);
|
||||
Console.WriteLn("g_decoder = 0x%x.", &decoder);
|
||||
Console.WriteLn(ipu_cmd.desc());
|
||||
Console.Newline();
|
||||
Console.WriteLn();
|
||||
}
|
||||
|
||||
bool SaveStateBase::ipuFreeze()
|
||||
|
|
|
@ -1,427 +0,0 @@
|
|||
// SPDX-FileCopyrightText: 2002-2023 PCSX2 Dev Team
|
||||
// SPDX-License-Identifier: LGPL-3.0+
|
||||
|
||||
#include "DebugTools/Debug.h"
|
||||
#include "Host.h"
|
||||
#include "LogSink.h"
|
||||
|
||||
#include "common/Assertions.h"
|
||||
#include "common/Console.h"
|
||||
#include "common/FileSystem.h"
|
||||
#include "common/Path.h"
|
||||
#include "common/SettingsInterface.h"
|
||||
#include "common/StringUtil.h"
|
||||
#include "common/Timer.h"
|
||||
|
||||
#ifdef _WIN32
|
||||
#include "common/RedtapeWindows.h"
|
||||
#endif
|
||||
|
||||
#include <csignal>
|
||||
#include "fmt/core.h"
|
||||
|
||||
// Used on both Windows and Linux.
|
||||
#ifdef _WIN32
|
||||
static const wchar_t s_console_colors[][ConsoleColors_Count] = {
|
||||
#define CC(x) L##x
|
||||
#else
|
||||
static const char s_console_colors[][ConsoleColors_Count] = {
|
||||
#define CC(x) x
|
||||
#endif
|
||||
CC("\033[0m"), // default
|
||||
CC("\033[30m\033[1m"), // black
|
||||
CC("\033[32m"), // green
|
||||
CC("\033[31m"), // red
|
||||
CC("\033[34m"), // blue
|
||||
CC("\033[35m"), // magenta
|
||||
CC("\033[35m"), // orange (FIXME)
|
||||
CC("\033[37m"), // gray
|
||||
CC("\033[36m"), // cyan
|
||||
CC("\033[33m"), // yellow
|
||||
CC("\033[37m"), // white
|
||||
CC("\033[30m\033[1m"), // strong black
|
||||
CC("\033[31m\033[1m"), // strong red
|
||||
CC("\033[32m\033[1m"), // strong green
|
||||
CC("\033[34m\033[1m"), // strong blue
|
||||
CC("\033[35m\033[1m"), // strong magenta
|
||||
CC("\033[35m\033[1m"), // strong orange (FIXME)
|
||||
CC("\033[37m\033[1m"), // strong gray
|
||||
CC("\033[36m\033[1m"), // strong cyan
|
||||
CC("\033[33m\033[1m"), // strong yellow
|
||||
CC("\033[37m\033[1m") // strong white
|
||||
};
|
||||
#undef CC
|
||||
|
||||
static bool s_block_system_console = false;
|
||||
static Common::Timer::Value s_log_start_timestamp = Common::Timer::GetCurrentValue();
|
||||
static bool s_log_timestamps = false;
|
||||
static std::mutex s_log_mutex;
|
||||
|
||||
// Replacement for Console so we actually get output to our console window on Windows.
|
||||
#ifdef _WIN32
|
||||
|
||||
static bool s_debugger_attached = false;
|
||||
static bool s_console_handle_set = false;
|
||||
static bool s_console_allocated = false;
|
||||
static HANDLE s_console_handle = INVALID_HANDLE_VALUE;
|
||||
static HANDLE s_old_console_stdin = NULL;
|
||||
static HANDLE s_old_console_stdout = NULL;
|
||||
static HANDLE s_old_console_stderr = NULL;
|
||||
|
||||
static BOOL WINAPI ConsoleCtrlHandler(DWORD dwCtrlType)
|
||||
{
|
||||
Console.WriteLn("Handler %u", dwCtrlType);
|
||||
if (dwCtrlType != CTRL_C_EVENT)
|
||||
return FALSE;
|
||||
|
||||
::raise(SIGTERM);
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
static bool EnableVirtualTerminalProcessing(HANDLE hConsole)
|
||||
{
|
||||
if (hConsole == INVALID_HANDLE_VALUE)
|
||||
return false;
|
||||
|
||||
DWORD old_mode;
|
||||
if (!GetConsoleMode(hConsole, &old_mode))
|
||||
return false;
|
||||
|
||||
// already enabled?
|
||||
if (old_mode & ENABLE_VIRTUAL_TERMINAL_PROCESSING)
|
||||
return true;
|
||||
|
||||
return SetConsoleMode(hConsole, old_mode | ENABLE_VIRTUAL_TERMINAL_PROCESSING);
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
static void ConsoleQt_SetTitle(const char* title)
|
||||
{
|
||||
#ifdef _WIN32
|
||||
SetConsoleTitleW(StringUtil::UTF8StringToWideString(title).c_str());
|
||||
#else
|
||||
std::fprintf(stdout, "\033]0;%s\007", title);
|
||||
#endif
|
||||
}
|
||||
|
||||
static void ConsoleQt_DoSetColor(ConsoleColors color)
|
||||
{
|
||||
#ifdef _WIN32
|
||||
if (s_console_handle == INVALID_HANDLE_VALUE)
|
||||
return;
|
||||
|
||||
const wchar_t* colortext = s_console_colors[static_cast<u32>(color)];
|
||||
DWORD written;
|
||||
WriteConsoleW(s_console_handle, colortext, std::wcslen(colortext), &written, nullptr);
|
||||
#else
|
||||
const char* colortext = s_console_colors[static_cast<u32>(color)];
|
||||
std::fputs(colortext, stdout);
|
||||
#endif
|
||||
}
|
||||
|
||||
static void ConsoleQt_DoWrite(const char* fmt)
|
||||
{
|
||||
std::unique_lock lock(s_log_mutex);
|
||||
|
||||
#ifdef _WIN32
|
||||
if (s_console_handle != INVALID_HANDLE_VALUE || s_debugger_attached)
|
||||
{
|
||||
// TODO: Put this on the stack.
|
||||
std::wstring wfmt(StringUtil::UTF8StringToWideString(fmt));
|
||||
|
||||
if (s_debugger_attached)
|
||||
OutputDebugStringW(wfmt.c_str());
|
||||
|
||||
if (s_console_handle != INVALID_HANDLE_VALUE)
|
||||
{
|
||||
DWORD written;
|
||||
WriteConsoleW(s_console_handle, wfmt.c_str(), static_cast<DWORD>(wfmt.length()), &written, nullptr);
|
||||
}
|
||||
}
|
||||
#else
|
||||
std::fputs(fmt, stdout);
|
||||
#endif
|
||||
|
||||
if (emuLog)
|
||||
{
|
||||
std::fputs(fmt, emuLog);
|
||||
}
|
||||
}
|
||||
|
||||
static void ConsoleQt_DoWriteLn(const char* fmt)
|
||||
{
|
||||
std::unique_lock lock(s_log_mutex);
|
||||
|
||||
// find time since start of process, but save a syscall if we're not writing timestamps
|
||||
float message_time =
|
||||
s_log_timestamps ?
|
||||
static_cast<float>(Common::Timer::ConvertValueToSeconds(Common::Timer::GetCurrentValue() - s_log_start_timestamp)) :
|
||||
0.0f;
|
||||
|
||||
// split newlines up
|
||||
const char* start = fmt;
|
||||
do
|
||||
{
|
||||
const char* end = std::strchr(start, '\n');
|
||||
|
||||
std::string_view line;
|
||||
if (end)
|
||||
{
|
||||
line = std::string_view(start, end - start);
|
||||
start = end + 1;
|
||||
}
|
||||
else
|
||||
{
|
||||
line = std::string_view(start);
|
||||
start = nullptr;
|
||||
}
|
||||
|
||||
#ifdef _WIN32
|
||||
if (s_console_handle != INVALID_HANDLE_VALUE || s_debugger_attached)
|
||||
{
|
||||
// TODO: Put this on the stack.
|
||||
std::wstring wfmt(StringUtil::UTF8StringToWideString(line));
|
||||
|
||||
if (s_debugger_attached)
|
||||
{
|
||||
// VS already timestamps logs (at least with the productivity power tools).
|
||||
if (!wfmt.empty())
|
||||
OutputDebugStringW(wfmt.c_str());
|
||||
OutputDebugStringW(L"\n");
|
||||
}
|
||||
|
||||
if (s_console_handle != INVALID_HANDLE_VALUE)
|
||||
{
|
||||
DWORD written;
|
||||
if (s_log_timestamps)
|
||||
{
|
||||
wchar_t timestamp_text[128];
|
||||
const int timestamp_len = _swprintf(timestamp_text, L"[%10.4f] ", message_time);
|
||||
WriteConsoleW(s_console_handle, timestamp_text, static_cast<DWORD>(timestamp_len), &written, nullptr);
|
||||
}
|
||||
|
||||
if (!wfmt.empty())
|
||||
WriteConsoleW(s_console_handle, wfmt.c_str(), static_cast<DWORD>(wfmt.length()), &written, nullptr);
|
||||
|
||||
WriteConsoleW(s_console_handle, L"\n", 1, &written, nullptr);
|
||||
}
|
||||
}
|
||||
#else
|
||||
if (s_log_timestamps)
|
||||
{
|
||||
std::fprintf(stdout, "[%10.4f] %.*s\n", message_time, static_cast<int>(line.length()), line.data());
|
||||
}
|
||||
else
|
||||
{
|
||||
if (!line.empty())
|
||||
std::fwrite(line.data(), line.length(), 1, stdout);
|
||||
std::fputc('\n', stdout);
|
||||
}
|
||||
#endif
|
||||
|
||||
if (emuLog)
|
||||
{
|
||||
if (s_log_timestamps)
|
||||
{
|
||||
std::fprintf(emuLog, "[%10.4f] %.*s\n", message_time, static_cast<int>(line.length()), line.data());
|
||||
}
|
||||
else
|
||||
{
|
||||
std::fwrite(line.data(), line.length(), 1, emuLog);
|
||||
std::fputc('\n', emuLog);
|
||||
}
|
||||
}
|
||||
} while (start);
|
||||
}
|
||||
|
||||
static void ConsoleQt_Newline()
|
||||
{
|
||||
ConsoleQt_DoWriteLn("");
|
||||
}
|
||||
|
||||
static const IConsoleWriter ConsoleWriter_WinQt = {
|
||||
ConsoleQt_DoWrite,
|
||||
ConsoleQt_DoWriteLn,
|
||||
ConsoleQt_DoSetColor,
|
||||
|
||||
ConsoleQt_DoWrite,
|
||||
ConsoleQt_Newline,
|
||||
ConsoleQt_SetTitle,
|
||||
};
|
||||
|
||||
static void UpdateLoggingSinks(bool system_console, bool file_log)
|
||||
{
|
||||
#ifdef _WIN32
|
||||
const bool debugger_attached = IsDebuggerPresent();
|
||||
s_debugger_attached = debugger_attached;
|
||||
if (system_console)
|
||||
{
|
||||
if (!s_console_handle_set)
|
||||
{
|
||||
s_old_console_stdin = GetStdHandle(STD_INPUT_HANDLE);
|
||||
s_old_console_stdout = GetStdHandle(STD_OUTPUT_HANDLE);
|
||||
s_old_console_stderr = GetStdHandle(STD_ERROR_HANDLE);
|
||||
|
||||
bool handle_valid = (GetConsoleWindow() != NULL);
|
||||
if (!handle_valid)
|
||||
{
|
||||
s_console_allocated = AttachConsole(ATTACH_PARENT_PROCESS) || AllocConsole();
|
||||
handle_valid = (GetConsoleWindow() != NULL);
|
||||
}
|
||||
|
||||
if (handle_valid)
|
||||
{
|
||||
s_console_handle = GetStdHandle(STD_OUTPUT_HANDLE);
|
||||
if (s_console_handle != INVALID_HANDLE_VALUE)
|
||||
{
|
||||
s_console_handle_set = true;
|
||||
SetConsoleCtrlHandler(ConsoleCtrlHandler, TRUE);
|
||||
|
||||
// This gets us unix-style coloured output.
|
||||
EnableVirtualTerminalProcessing(GetStdHandle(STD_OUTPUT_HANDLE));
|
||||
EnableVirtualTerminalProcessing(GetStdHandle(STD_ERROR_HANDLE));
|
||||
|
||||
// Redirect stdout/stderr.
|
||||
std::FILE* fp;
|
||||
freopen_s(&fp, "CONIN$", "r", stdin);
|
||||
freopen_s(&fp, "CONOUT$", "w", stdout);
|
||||
freopen_s(&fp, "CONOUT$", "w", stderr);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// just in case it fails
|
||||
system_console = s_console_handle_set;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (s_console_handle_set)
|
||||
{
|
||||
s_console_handle_set = false;
|
||||
SetConsoleCtrlHandler(ConsoleCtrlHandler, FALSE);
|
||||
|
||||
// redirect stdout/stderr back to null.
|
||||
std::FILE* fp;
|
||||
freopen_s(&fp, "NUL:", "w", stderr);
|
||||
freopen_s(&fp, "NUL:", "w", stdout);
|
||||
freopen_s(&fp, "NUL:", "w", stdin);
|
||||
|
||||
// release console and restore state
|
||||
SetStdHandle(STD_INPUT_HANDLE, s_old_console_stdin);
|
||||
SetStdHandle(STD_OUTPUT_HANDLE, s_old_console_stdout);
|
||||
SetStdHandle(STD_ERROR_HANDLE, s_old_console_stderr);
|
||||
s_old_console_stdin = NULL;
|
||||
s_old_console_stdout = NULL;
|
||||
s_old_console_stderr = NULL;
|
||||
if (s_console_allocated)
|
||||
{
|
||||
s_console_allocated = false;
|
||||
FreeConsole();
|
||||
}
|
||||
}
|
||||
}
|
||||
#else
|
||||
const bool debugger_attached = false;
|
||||
#endif
|
||||
|
||||
if (file_log)
|
||||
{
|
||||
if (!emuLog)
|
||||
{
|
||||
if (emuLogName.empty())
|
||||
emuLogName = Path::Combine(EmuFolders::Logs, "emulog.txt");
|
||||
|
||||
emuLog = FileSystem::OpenCFile(emuLogName.c_str(), "wb");
|
||||
file_log = (emuLog != nullptr);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (emuLog)
|
||||
{
|
||||
std::fclose(emuLog);
|
||||
emuLog = nullptr;
|
||||
emuLogName = {};
|
||||
}
|
||||
}
|
||||
|
||||
// Discard logs completely if there's no sinks.
|
||||
if (debugger_attached || system_console || file_log)
|
||||
Console_SetActiveHandler(ConsoleWriter_WinQt);
|
||||
else
|
||||
Console_SetActiveHandler(ConsoleWriter_Null);
|
||||
}
|
||||
|
||||
void LogSink::SetFileLogPath(std::string path)
|
||||
{
|
||||
if (emuLogName == path)
|
||||
return;
|
||||
|
||||
emuLogName = std::move(path);
|
||||
|
||||
// reopen on change
|
||||
if (emuLog)
|
||||
{
|
||||
std::fclose(emuLog);
|
||||
if (!emuLogName.empty())
|
||||
emuLog = FileSystem::OpenCFile(emuLogName.c_str(), "wb");
|
||||
}
|
||||
}
|
||||
|
||||
void LogSink::CloseFileLog()
|
||||
{
|
||||
if (!emuLog)
|
||||
return;
|
||||
|
||||
std::fclose(emuLog);
|
||||
emuLog = nullptr;
|
||||
}
|
||||
|
||||
void LogSink::SetBlockSystemConsole(bool block)
|
||||
{
|
||||
s_block_system_console = block;
|
||||
}
|
||||
|
||||
void LogSink::InitializeEarlyConsole()
|
||||
{
|
||||
UpdateLoggingSinks(true, false);
|
||||
}
|
||||
|
||||
void LogSink::UpdateLogging(SettingsInterface& si)
|
||||
{
|
||||
const bool system_console_enabled = !s_block_system_console && si.GetBoolValue("Logging", "EnableSystemConsole", false);
|
||||
const bool file_logging_enabled = si.GetBoolValue("Logging", "EnableFileLogging", false);
|
||||
|
||||
s_log_timestamps = si.GetBoolValue("Logging", "EnableTimestamps", true);
|
||||
|
||||
const bool any_logging_sinks = system_console_enabled || file_logging_enabled;
|
||||
DevConWriterEnabled = any_logging_sinks && (IsDevBuild || si.GetBoolValue("Logging", "EnableVerbose", false));
|
||||
|
||||
const bool ee_console_enabled = any_logging_sinks && si.GetBoolValue("Logging", "EnableEEConsole", false);
|
||||
SysConsole.eeConsole.Enabled = ee_console_enabled;
|
||||
|
||||
SysConsole.iopConsole.Enabled = any_logging_sinks && si.GetBoolValue("Logging", "EnableIOPConsole", false);
|
||||
SysTrace.IOP.R3000A.Enabled = true;
|
||||
SysTrace.IOP.COP2.Enabled = true;
|
||||
SysTrace.IOP.Memory.Enabled = true;
|
||||
SysTrace.SIF.Enabled = true;
|
||||
|
||||
// Input Recording Logs
|
||||
SysConsole.recordingConsole.Enabled = any_logging_sinks && si.GetBoolValue("Logging", "EnableInputRecordingLogs", true);
|
||||
SysConsole.controlInfo.Enabled = any_logging_sinks && si.GetBoolValue("Logging", "EnableControllerLogs", false);
|
||||
|
||||
UpdateLoggingSinks(system_console_enabled, file_logging_enabled);
|
||||
}
|
||||
|
||||
void LogSink::SetDefaultLoggingSettings(SettingsInterface& si)
|
||||
{
|
||||
si.SetBoolValue("Logging", "EnableSystemConsole", false);
|
||||
si.SetBoolValue("Logging", "EnableFileLogging", false);
|
||||
si.SetBoolValue("Logging", "EnableTimestamps", true);
|
||||
si.SetBoolValue("Logging", "EnableVerbose", false);
|
||||
si.SetBoolValue("Logging", "EnableEEConsole", false);
|
||||
si.SetBoolValue("Logging", "EnableIOPConsole", false);
|
||||
si.SetBoolValue("Logging", "EnableInputRecordingLogs", true);
|
||||
si.SetBoolValue("Logging", "EnableControllerLogs", false);
|
||||
}
|
|
@ -1,27 +0,0 @@
|
|||
// SPDX-FileCopyrightText: 2002-2023 PCSX2 Dev Team
|
||||
// SPDX-License-Identifier: LGPL-3.0+
|
||||
|
||||
#pragma once
|
||||
|
||||
class SettingsInterface;
|
||||
|
||||
namespace LogSink
|
||||
{
|
||||
/// Overrides the filename used for the file log.
|
||||
void SetFileLogPath(std::string path);
|
||||
|
||||
/// Ensures file log is flushed and closed.
|
||||
void CloseFileLog();
|
||||
|
||||
/// Prevents the system console from being displayed.
|
||||
void SetBlockSystemConsole(bool block);
|
||||
|
||||
/// Updates the Console handler based on the current configuration.
|
||||
void UpdateLogging(SettingsInterface& si);
|
||||
|
||||
/// Initializes early console logging (for printing command line arguments).
|
||||
void InitializeEarlyConsole();
|
||||
|
||||
/// Stores default logging settings to the specified file.
|
||||
void SetDefaultLoggingSettings(SettingsInterface& si);
|
||||
}
|
|
@ -180,7 +180,7 @@ bool SysMemory::AllocateMemoryMap()
|
|||
void SysMemory::DumpMemoryMap()
|
||||
{
|
||||
#define DUMP_REGION(name, base, offset, size) \
|
||||
DevCon.WriteLn(Color_Gray, "%-32s @ 0x%016" PRIXPTR " -> 0x%016" PRIXPTR " %s", name, \
|
||||
DevCon.WriteLn(Color_Gray, " %-32s @ 0x%016" PRIXPTR " -> 0x%016" PRIXPTR " %s", name, \
|
||||
(uptr)(base + offset), (uptr)(base + offset + size), fmt::format("[{}mb]", size / _1mb).c_str());
|
||||
|
||||
DUMP_REGION("EE Main Memory", s_data_memory, HostMemoryMap::EEmemOffset, HostMemoryMap::EEmemSize);
|
||||
|
@ -227,8 +227,6 @@ bool SysMemory::Allocate()
|
|||
{
|
||||
DevCon.WriteLn(Color_StrongBlue, "Allocating host memory for virtual systems...");
|
||||
|
||||
ConsoleIndentScope indent(1);
|
||||
|
||||
if (!AllocateMemoryMap())
|
||||
return false;
|
||||
|
||||
|
@ -245,7 +243,6 @@ bool SysMemory::Allocate()
|
|||
void SysMemory::Reset()
|
||||
{
|
||||
DevCon.WriteLn(Color_StrongBlue, "Resetting host memory for virtual systems...");
|
||||
ConsoleIndentScope indent(1);
|
||||
|
||||
memReset();
|
||||
iopMemReset();
|
||||
|
@ -258,7 +255,6 @@ void SysMemory::Reset()
|
|||
void SysMemory::Release()
|
||||
{
|
||||
Console.WriteLn(Color_Blue, "Releasing host memory for virtual systems...");
|
||||
ConsoleIndentScope indent(1);
|
||||
|
||||
vtlb_Core_Free(); // Just to be sure... (calling order could result in it getting missed during Decommit).
|
||||
|
||||
|
|
|
@ -93,7 +93,6 @@ void MultipartFileReader::FindParts()
|
|||
return;
|
||||
|
||||
DevCon.WriteLn( Color_Blue, "isoFile: multi-part %s detected...", StringUtil::toUpper(curext).c_str() );
|
||||
ConsoleIndentScope indent;
|
||||
|
||||
int bsize = m_parts[0].reader->GetBlockSize();
|
||||
int blocks = m_parts[0].end;
|
||||
|
|
|
@ -7,6 +7,7 @@
|
|||
#include "common/ByteSwap.h"
|
||||
#include "common/FileSystem.h"
|
||||
#include "common/Path.h"
|
||||
#include "common/SmallString.h"
|
||||
#include "common/StringUtil.h"
|
||||
#include "common/ZipHelpers.h"
|
||||
|
||||
|
@ -99,9 +100,9 @@ namespace Patch
|
|||
bool operator==(const PatchCommand& p) const { return std::memcmp(this, &p, sizeof(*this)) == 0; }
|
||||
bool operator!=(const PatchCommand& p) const { return std::memcmp(this, &p, sizeof(*this)) != 0; }
|
||||
|
||||
std::string ToString() const
|
||||
SmallString ToString() const
|
||||
{
|
||||
return fmt::format("{},{},{},{:08x},{:x}", s_place_to_string[static_cast<u8>(placetopatch)],
|
||||
return SmallString::from_fmt("{},{},{},{:08x},{:x}", s_place_to_string[static_cast<u8>(placetopatch)],
|
||||
s_cpu_to_string[static_cast<u8>(cpu)], s_type_to_string[static_cast<u8>(type)], addr, data);
|
||||
}
|
||||
};
|
||||
|
@ -616,8 +617,8 @@ u32 Patch::EnablePatches(const PatchList& patches, const EnablePatchList& enable
|
|||
for (const PatchCommand& ip : p.patches)
|
||||
{
|
||||
// print the actual patch lines only in verbose mode (even in devel)
|
||||
if (DevConWriterEnabled)
|
||||
DevCon.Indent().WriteLn(ip.ToString());
|
||||
if (Log::GetMaxLevel() >= LOGLEVEL_DEV)
|
||||
DevCon.WriteLnFmt(" {}", ip.ToString());
|
||||
|
||||
s_active_patches.push_back(&ip);
|
||||
}
|
||||
|
|
|
@ -152,11 +152,10 @@ bool SaveStateBase::FreezeBios()
|
|||
|
||||
if (bioscheck != BiosChecksum)
|
||||
{
|
||||
Console.Newline();
|
||||
Console.Indent(1).Error( "Warning: BIOS Version Mismatch, savestate may be unstable!" );
|
||||
Console.Indent(2).Error(
|
||||
"Current BIOS: %s (crc=0x%08x)\n"
|
||||
"Savestate BIOS: %s (crc=0x%08x)\n",
|
||||
Console.Error("\n Warning: BIOS Version Mismatch, savestate may be unstable!");
|
||||
Console.Error(
|
||||
" Current BIOS: %s (crc=0x%08x)\n"
|
||||
" Savestate BIOS: %s (crc=0x%08x)\n",
|
||||
BiosDescription.c_str(), BiosChecksum,
|
||||
biosdesc, bioscheck
|
||||
);
|
||||
|
@ -355,7 +354,7 @@ static bool SysState_ComponentFreezeIn(zip_file_t* zf, SysState_Component comp)
|
|||
if (comp.freeze(FreezeAction::Size, &fP) != 0)
|
||||
fP.size = 0;
|
||||
|
||||
Console.Indent().WriteLn("Loading %s", comp.name);
|
||||
Console.WriteLn(" Loading %s", comp.name);
|
||||
|
||||
std::unique_ptr<u8[]> data;
|
||||
if (fP.size > 0)
|
||||
|
@ -394,7 +393,7 @@ static bool SysState_ComponentFreezeOut(SaveStateBase& writer, SysState_Componen
|
|||
const int size = fP.size;
|
||||
writer.PrepBlock(size);
|
||||
|
||||
Console.Indent().WriteLn("Saving %s", comp.name);
|
||||
Console.WriteLn(" Saving %s", comp.name);
|
||||
|
||||
fP.data = writer.GetBlockPtr();
|
||||
if (comp.freeze(FreezeAction::Save, &fP) != 0)
|
||||
|
|
|
@ -25,7 +25,6 @@
|
|||
using namespace R5900;
|
||||
|
||||
FILE* emuLog;
|
||||
std::string emuLogName;
|
||||
|
||||
SysTraceLogPack SysTrace;
|
||||
SysConsoleLogPack SysConsole;
|
||||
|
|
|
@ -21,7 +21,6 @@
|
|||
#include "ImGui/ImGuiOverlays.h"
|
||||
#include "Input/InputManager.h"
|
||||
#include "IopBios.h"
|
||||
#include "LogSink.h"
|
||||
#include "MTGS.h"
|
||||
#include "MTVU.h"
|
||||
#include "PINE.h"
|
||||
|
@ -79,6 +78,9 @@
|
|||
|
||||
namespace VMManager
|
||||
{
|
||||
static void SetDefaultLoggingSettings(SettingsInterface& si);
|
||||
static void UpdateLoggingSettings(SettingsInterface& si);
|
||||
|
||||
static void LogCPUCapabilities();
|
||||
static void InitializeCPUProviders();
|
||||
static void ShutdownCPUProviders();
|
||||
|
@ -145,6 +147,9 @@ static constexpr u32 SETTINGS_VERSION = 1;
|
|||
static std::unique_ptr<INISettingsInterface> s_game_settings_interface;
|
||||
static std::unique_ptr<INISettingsInterface> s_input_settings_interface;
|
||||
|
||||
static bool s_log_block_system_console = false;
|
||||
static bool s_log_force_file_log = false;
|
||||
|
||||
static std::atomic<VMState> s_state{VMState::Shutdown};
|
||||
static bool s_cpu_implementation_changed = false;
|
||||
static Threading::ThreadHandle s_vm_thread_handle;
|
||||
|
@ -411,6 +416,80 @@ void VMManager::Internal::CPUThreadShutdown()
|
|||
#ifdef _WIN32
|
||||
CoUninitialize();
|
||||
#endif
|
||||
|
||||
// Ensure emulog gets flushed.
|
||||
Log::SetFileOutputLevel(LOGLEVEL_NONE, std::string());
|
||||
}
|
||||
|
||||
void VMManager::Internal::SetFileLogPath(std::string path)
|
||||
{
|
||||
s_log_force_file_log = Log::SetFileOutputLevel(LOGLEVEL_DEBUG, std::move(path));
|
||||
emuLog = Log::GetFileLogHandle();
|
||||
}
|
||||
|
||||
void VMManager::Internal::SetBlockSystemConsole(bool block)
|
||||
{
|
||||
s_log_block_system_console = block;
|
||||
}
|
||||
|
||||
void VMManager::UpdateLoggingSettings(SettingsInterface& si)
|
||||
{
|
||||
#ifdef _DEBUG
|
||||
constexpr LOGLEVEL level = LOGLEVEL_DEBUG;
|
||||
#else
|
||||
const LOGLEVEL level = (IsDevBuild || si.GetBoolValue("Logging", "EnableVerbose", false)) ? LOGLEVEL_DEV : LOGLEVEL_INFO;
|
||||
#endif
|
||||
|
||||
const bool system_console_enabled = !s_log_block_system_console && si.GetBoolValue("Logging", "EnableSystemConsole", false);
|
||||
const bool log_window_enabled = !s_log_block_system_console && si.GetBoolValue("Logging", "EnableLogWindow", false);
|
||||
const bool file_logging_enabled = s_log_force_file_log || si.GetBoolValue("Logging", "EnableFileLogging", false);
|
||||
|
||||
if (system_console_enabled != Log::IsConsoleOutputEnabled())
|
||||
Log::SetConsoleOutputLevel(system_console_enabled ? level : LOGLEVEL_NONE);
|
||||
|
||||
if (file_logging_enabled != Log::IsFileOutputEnabled())
|
||||
{
|
||||
std::string path = Path::Combine(EmuFolders::Logs, "emulog.txt");
|
||||
Log::SetFileOutputLevel(file_logging_enabled ? level : LOGLEVEL_NONE, std::move(path));
|
||||
}
|
||||
|
||||
// Debug console only exists on Windows.
|
||||
#ifdef _WIN32
|
||||
const bool debug_console_enabled = IsDebuggerPresent() && si.GetBoolValue("Logging", "EnableDebugConsole", false);
|
||||
Log::SetDebugOutputLevel(debug_console_enabled ? level : LOGLEVEL_NONE);
|
||||
#else
|
||||
constexpr bool debug_console_enabled = false;
|
||||
#endif
|
||||
|
||||
const bool timestamps_enabled = si.GetBoolValue("Logging", "EnableTimestamps", true);
|
||||
Log::SetTimestampsEnabled(timestamps_enabled);
|
||||
|
||||
const bool any_logging_sinks = system_console_enabled || log_window_enabled || file_logging_enabled || debug_console_enabled;
|
||||
|
||||
const bool ee_console_enabled = any_logging_sinks && si.GetBoolValue("Logging", "EnableEEConsole", false);
|
||||
SysConsole.eeConsole.Enabled = ee_console_enabled;
|
||||
|
||||
SysConsole.iopConsole.Enabled = any_logging_sinks && si.GetBoolValue("Logging", "EnableIOPConsole", false);
|
||||
SysTrace.IOP.R3000A.Enabled = true;
|
||||
SysTrace.IOP.COP2.Enabled = true;
|
||||
SysTrace.IOP.Memory.Enabled = true;
|
||||
SysTrace.SIF.Enabled = true;
|
||||
|
||||
// Input Recording Logs
|
||||
SysConsole.recordingConsole.Enabled = any_logging_sinks && si.GetBoolValue("Logging", "EnableInputRecordingLogs", true);
|
||||
SysConsole.controlInfo.Enabled = any_logging_sinks && si.GetBoolValue("Logging", "EnableControllerLogs", false);
|
||||
}
|
||||
|
||||
void VMManager::SetDefaultLoggingSettings(SettingsInterface& si)
|
||||
{
|
||||
si.SetBoolValue("Logging", "EnableSystemConsole", false);
|
||||
si.SetBoolValue("Logging", "EnableFileLogging", false);
|
||||
si.SetBoolValue("Logging", "EnableTimestamps", true);
|
||||
si.SetBoolValue("Logging", "EnableVerbose", false);
|
||||
si.SetBoolValue("Logging", "EnableEEConsole", false);
|
||||
si.SetBoolValue("Logging", "EnableIOPConsole", false);
|
||||
si.SetBoolValue("Logging", "EnableInputRecordingLogs", true);
|
||||
si.SetBoolValue("Logging", "EnableControllerLogs", false);
|
||||
}
|
||||
|
||||
bool VMManager::Internal::CheckSettingsVersion()
|
||||
|
@ -426,7 +505,9 @@ void VMManager::Internal::LoadStartupSettings()
|
|||
SettingsInterface* bsi = Host::Internal::GetBaseSettingsLayer();
|
||||
EmuFolders::LoadConfig(*bsi);
|
||||
EmuFolders::EnsureFoldersExist();
|
||||
LogSink::UpdateLogging(*bsi);
|
||||
|
||||
// We need to create the console window early, otherwise it appears behind the main window.
|
||||
UpdateLoggingSettings(*bsi);
|
||||
|
||||
#ifdef ENABLE_RAINTEGRATION
|
||||
// RAIntegration switch must happen before the UI is created.
|
||||
|
@ -455,7 +536,7 @@ void VMManager::SetDefaultSettings(
|
|||
si.SetBoolValue("EmuCore", "EnableFastBoot", true);
|
||||
|
||||
SetHardwareDependentDefaultSettings(si);
|
||||
LogSink::SetDefaultLoggingSettings(si);
|
||||
SetDefaultLoggingSettings(si);
|
||||
}
|
||||
if (controllers)
|
||||
{
|
||||
|
@ -482,7 +563,7 @@ void VMManager::LoadSettings()
|
|||
Host::LoadSettings(*si, lock);
|
||||
InputManager::ReloadSources(*si, lock);
|
||||
InputManager::ReloadBindings(*si, *Host::GetSettingsInterfaceForBindings());
|
||||
LogSink::UpdateLogging(*si);
|
||||
UpdateLoggingSettings(*si);
|
||||
|
||||
if (HasValidOrInitializingVM())
|
||||
{
|
||||
|
@ -2152,26 +2233,22 @@ void VMManager::LogCPUCapabilities()
|
|||
SVN_REV);
|
||||
}
|
||||
|
||||
Console.WriteLn("Savestate version: 0x%x", g_SaveVersion);
|
||||
Console.Newline();
|
||||
Console.WriteLnFmt("Savestate version: 0x{:x}\n", g_SaveVersion);
|
||||
|
||||
Console.WriteLn(Color_StrongBlack, "Host Machine Init:");
|
||||
|
||||
Console.Indent().WriteLn(
|
||||
"Operating System = %s\n"
|
||||
"Physical RAM = %u MB",
|
||||
Console.WriteLnFmt(
|
||||
" Operating System = {}\n"
|
||||
" Physical RAM = {} MB",
|
||||
GetOSVersionString(),
|
||||
GetPhysicalMemory() / _1mb);
|
||||
|
||||
GetOSVersionString().c_str(),
|
||||
(u32)(GetPhysicalMemory() / _1mb));
|
||||
|
||||
Console.Indent().WriteLn("Processor = %s", cpuinfo_get_package(0)->name);
|
||||
Console.Indent().WriteLn("Core Count = %u cores", cpuinfo_get_cores_count());
|
||||
Console.Indent().WriteLn("Thread Count = %u threads", cpuinfo_get_processors_count());
|
||||
|
||||
Console.Newline();
|
||||
Console.WriteLnFmt(" Processor = {}", cpuinfo_get_package(0)->name);
|
||||
Console.WriteLnFmt(" Core Count = {} cores", cpuinfo_get_cores_count());
|
||||
Console.WriteLnFmt(" Thread Count = {} threads", cpuinfo_get_processors_count());
|
||||
Console.WriteLn();
|
||||
|
||||
std::string features;
|
||||
|
||||
if (cpuinfo_has_x86_avx())
|
||||
features += "AVX ";
|
||||
if (cpuinfo_has_x86_avx2())
|
||||
|
@ -2180,9 +2257,8 @@ void VMManager::LogCPUCapabilities()
|
|||
StringUtil::StripWhitespace(&features);
|
||||
|
||||
Console.WriteLn(Color_StrongBlack, "x86 Features Detected:");
|
||||
Console.Indent().WriteLn("%s", features.c_str());
|
||||
|
||||
Console.Newline();
|
||||
Console.WriteLnFmt(" {}", features);
|
||||
Console.WriteLn();
|
||||
}
|
||||
|
||||
|
||||
|
|
|
@ -222,6 +222,12 @@ namespace VMManager
|
|||
/// Loads early settings. Call once on startup.
|
||||
void LoadStartupSettings();
|
||||
|
||||
/// Overrides the filename used for the file log.
|
||||
void SetFileLogPath(std::string path);
|
||||
|
||||
/// Prevents the system console from being displayed.
|
||||
void SetBlockSystemConsole(bool block);
|
||||
|
||||
/// Initializes common host state, called on the CPU thread.
|
||||
bool CPUThreadInitialize();
|
||||
|
||||
|
|
|
@ -230,7 +230,6 @@
|
|||
<ClCompile Include="Input\XInputSource.cpp" />
|
||||
<ClCompile Include="IopGte.cpp" />
|
||||
<ClCompile Include="LayeredSettingsInterface.cpp" />
|
||||
<ClCompile Include="LogSink.cpp" />
|
||||
<ClCompile Include="PINE.cpp" />
|
||||
<ClCompile Include="FW.cpp" />
|
||||
<ClCompile Include="PerformanceMetrics.cpp" />
|
||||
|
@ -578,7 +577,6 @@
|
|||
<ClInclude Include="IopGte.h" />
|
||||
<ClInclude Include="IPU\mpeg2_vlc.h" />
|
||||
<ClInclude Include="LayeredSettingsInterface.h" />
|
||||
<ClInclude Include="LogSink.h" />
|
||||
<ClInclude Include="PINE.h" />
|
||||
<ClInclude Include="FW.h" />
|
||||
<ClInclude Include="PerformanceMetrics.h" />
|
||||
|
|
|
@ -1331,9 +1331,6 @@
|
|||
<ClCompile Include="Host.cpp">
|
||||
<Filter>System</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="LogSink.cpp">
|
||||
<Filter>Misc</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="windows\Optimus.cpp">
|
||||
<Filter>Misc</Filter>
|
||||
</ClCompile>
|
||||
|
@ -2253,9 +2250,6 @@
|
|||
<ClInclude Include="Host.h">
|
||||
<Filter>System</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="LogSink.h">
|
||||
<Filter>Misc</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="GameList.h">
|
||||
<Filter>Misc</Filter>
|
||||
</ClInclude>
|
||||
|
|
|
@ -2083,7 +2083,6 @@ static void memory_protect_recompiled_code(u32 startpc, u32 size)
|
|||
xJC(DispatchPageReset);
|
||||
|
||||
// note: clearcnt is measured per-page, not per-block!
|
||||
ConsoleColorScope cs(Color_Gray);
|
||||
eeRecPerfLog.Write("Manual block @ %08X : size =%3d page/offs = 0x%05X/0x%03X inpgsz = %d clearcnt = %d",
|
||||
startpc, size, inpage_ptr >> 12, inpage_ptr & 0xfff, inpage_sz, manual_counter[inpage_ptr >> 12]);
|
||||
}
|
||||
|
|
|
@ -370,9 +370,9 @@ void VifUnpackSSE_Init()
|
|||
nVifGen(a, b, c);
|
||||
|
||||
DevCon.WriteLn("Unpack function generation complete. Generated function statistics:");
|
||||
DevCon.Indent().WriteLn(
|
||||
"Reserved buffer : %zu bytes @ 0x%016" PRIXPTR "\n"
|
||||
"x86 code generated : %zu bytes\n",
|
||||
DevCon.WriteLn(
|
||||
" Reserved buffer : %zu bytes @ 0x%016" PRIXPTR "\n"
|
||||
" x86 code generated : %zu bytes\n",
|
||||
SysMemory::GetVIFUnpackRecEnd() - SysMemory::GetVIFUnpackRec(),
|
||||
SysMemory::GetVIFUnpackRec(),
|
||||
xGetPtr() - SysMemory::GetVIFUnpackRec()
|
||||
|
|
|
@ -31,36 +31,6 @@ static constexpr size_t kInputBufSize = ((size_t)1 << 18);
|
|||
static constexpr ISzAlloc g_Alloc = {SzAlloc, SzFree};
|
||||
#endif
|
||||
|
||||
static std::FILE* s_file_console_stream;
|
||||
static constexpr IConsoleWriter s_file_console_writer = {
|
||||
[](const char* fmt) { // WriteRaw
|
||||
std::fputs(fmt, s_file_console_stream);
|
||||
std::fflush(s_file_console_stream);
|
||||
},
|
||||
[](const char* fmt) { // DoWriteLn
|
||||
std::fputs(fmt, s_file_console_stream);
|
||||
std::fputc('\n', s_file_console_stream);
|
||||
std::fflush(s_file_console_stream);
|
||||
},
|
||||
[](ConsoleColors) { // DoSetColor
|
||||
},
|
||||
[](const char* fmt) { // DoWriteFromStdout
|
||||
std::fputs(fmt, s_file_console_stream);
|
||||
std::fflush(s_file_console_stream);
|
||||
},
|
||||
[]() { // Newline
|
||||
std::fputc('\n', s_file_console_stream);
|
||||
std::fflush(s_file_console_stream);
|
||||
},
|
||||
[](const char*) { // SetTitle
|
||||
}};
|
||||
|
||||
static void CloseConsoleFile()
|
||||
{
|
||||
if (s_file_console_stream)
|
||||
std::fclose(s_file_console_stream);
|
||||
}
|
||||
|
||||
Updater::Updater(ProgressCallback* progress)
|
||||
: m_progress(progress)
|
||||
{
|
||||
|
@ -74,16 +44,11 @@ Updater::~Updater()
|
|||
|
||||
void Updater::SetupLogging(ProgressCallback* progress, const std::string& destination_directory)
|
||||
{
|
||||
const std::string log_path(Path::Combine(destination_directory, "updater.log"));
|
||||
s_file_console_stream = FileSystem::OpenCFile(log_path.c_str(), "w");
|
||||
if (!s_file_console_stream)
|
||||
{
|
||||
progress->DisplayFormattedModalError("Failed to open log file '%s'", log_path.c_str());
|
||||
return;
|
||||
}
|
||||
Log::SetDebugOutputLevel(LOGLEVEL_DEBUG);
|
||||
|
||||
Console_SetActiveHandler(s_file_console_writer);
|
||||
std::atexit(CloseConsoleFile);
|
||||
std::string log_path = Path::Combine(destination_directory, "updater.log");
|
||||
if (!Log::SetFileOutputLevel(LOGLEVEL_DEBUG, std::move(log_path)))
|
||||
progress->DisplayFormattedModalError("Failed to open log file '%s'", log_path.c_str());
|
||||
}
|
||||
|
||||
bool Updater::Initialize(std::string destination_directory)
|
||||
|
|
Loading…
Reference in New Issue