Port everything to CCommandLine

Before, we had to do a hack of capturing the command line using GetCommandLineA, and then checking if a certain argument is present. This was required due to how early the GameSDK dll was loaded (the g_CmdLine object was far from initialized in the engine). Due to the loader refactor, the commandline can be used directly after creation in the game's entry point (which is the time the SDK is getting loaded). Therefore, no copies of the command line are required anymore.
This commit contains the following changes:

- Correctly ordered the initialization, and deinitialization of systems (first init = last shutdown).
- Factored out command line string copy in favor of game's implementation.
- Factored the R5Reloaded emblem print into its own function.
- Removed 'SpdLog_PostInit()', we can now directly call DevMsg() once SpdLog_Init() has been called, the logger callback sink deals with the formatting of the output.
- Fixed a bug where the logger did not print the correct color for 'SYSTEM_WARNING' and 'SYSTEM_ERROR' in the external console.
- Fixed a bug where the command line did not work when the game wasn't launched with the '-launcher' parameter.
- Logs now equally appear on the external, and in-game console windows.
This commit is contained in:
Kawe Mazidjatari 2023-07-01 01:20:47 +02:00
parent dfb61aff74
commit 9e6b0e567f
15 changed files with 108 additions and 176 deletions

View File

@ -5,6 +5,7 @@
#include "core/logger.h"
#include "tier0/basetypes.h"
#include "tier0/crashhandler.h"
#include "tier0/commandline.h"
/*****************************************************************************/
#ifndef DEDICATED
#include "windows/id3dx.h"
@ -21,8 +22,10 @@
#define SDK_DEFAULT_CFG "cfg/startup_dedi_default.cfg"
#endif
bool g_bSdkInitialized = false;
//#############################################################################
// INITIALIZATION
// UTILITY
//#############################################################################
void Crash_Callback()
@ -33,6 +36,24 @@ void Crash_Callback()
// TODO[ AMOS ]: This is where we want to call backtrace from.
}
void Show_Emblem()
{
// Logged as 'SYSTEM_ERROR' for its red color.
for (size_t i = 0; i < SDK_ARRAYSIZE(R5R_EMBLEM); i++)
{
DevMsg(eDLL_T::SYSTEM_ERROR, "%s\n", R5R_EMBLEM[i]);
}
// Log the SDK's 'build_id' under the emblem.
DevMsg(eDLL_T::SYSTEM_ERROR, "+------------------------------------------------[%010d]-+\n",
g_SDKDll.GetNTHeaders()->FileHeader.TimeDateStamp);
DevMsg(eDLL_T::SYSTEM_ERROR, "\n");
}
//#############################################################################
// INITIALIZATION
//#############################################################################
void Tier0_Init()
{
#if !defined (DEDICATED)
@ -50,10 +71,10 @@ void Tier0_Init()
g_SDKDll = CModule("dedicated.dll");
#endif // !DEDICATED
// Setup logger callback sink.
g_CoreMsgVCallback = &EngineLoggerSink;
g_pCmdLine = g_GameDll.GetExportedSymbol("g_pCmdLine").RCast<CCommandLine*>();
g_CoreMsgVCallback = &EngineLoggerSink; // Setup logger callback sink.
// Setup crash callback.
g_pCmdLine->CreateCmdLine(GetCommandLineA());
g_CrashHandler->SetCrashCallback(&Crash_Callback);
}
@ -61,45 +82,35 @@ void SDK_Init()
{
Tier0_Init();
if (strstr(GetCommandLineA(), "-launcher"))
if (!CommandLine()->CheckParm("-launcher"))
{
g_svCmdLine = GetCommandLineA();
}
else
{
g_svCmdLine = LoadConfigFile(SDK_DEFAULT_CFG);
CommandLine()->AppendParametersFromFile(SDK_DEFAULT_CFG);
}
const bool bAnsiColor = CommandLine()->CheckParm("-ansicolor") ? true : false;
#ifndef DEDICATED
if (g_svCmdLine.find("-wconsole") != std::string::npos)
{
Console_Init();
}
#else
Console_Init();
if (CommandLine()->CheckParm("-wconsole"))
#endif // !DEDICATED
SpdLog_Init();
Winsock_Init(); // Initialize Winsock.
for (size_t i = 0; i < SDK_ARRAYSIZE(R5R_EMBLEM); i++)
{
spdlog::info("{:s}{:s}{:s}\n", g_svRedF, R5R_EMBLEM[i], g_svReset);
Console_Init(bAnsiColor);
}
// Log the SDK's 'build_id' under the emblem.
spdlog::info("{:s}+------------------------------------------------[{:010d}]-+{:s}\n",
g_svRedF, g_SDKDll.GetNTHeaders()->FileHeader.TimeDateStamp, g_svReset);
spdlog::info("\n");
SpdLog_Init(bAnsiColor);
Show_Emblem();
Winsock_Init(); // Initialize Winsock.
Systems_Init();
WinSys_Init();
WinSys_Init();
#ifndef DEDICATED
Input_Init();
#endif // !DEDICATED
curl_global_init(CURL_GLOBAL_ALL);
lzham_enable_fail_exceptions(true);
g_bSdkInitialized = true;
}
//#############################################################################
@ -110,26 +121,28 @@ void SDK_Shutdown()
{
static bool bShutDown = false;
assert(!bShutDown);
if (bShutDown)
{
spdlog::error("Recursive shutdown!\n");
return;
}
bShutDown = true;
spdlog::info("Shutdown GameSDK\n");
curl_global_cleanup();
Winsock_Shutdown();
Systems_Shutdown();
WinSys_Shutdown();
#ifndef DEDICATED
Input_Shutdown();
#endif // !DEDICATED
Console_Shutdown();
WinSys_Shutdown();
Systems_Shutdown();
Winsock_Shutdown();
SpdLog_Shutdown();
Console_Shutdown();
}
//#############################################################################

View File

@ -149,7 +149,7 @@
void Systems_Init()
{
spdlog::info("+-------------------------------------------------------------+\n");
DevMsg(eDLL_T::NONE, "+-------------------------------------------------------------+\n");
QuerySystemInfo();
DetourRegister();
@ -159,8 +159,8 @@ void Systems_Init()
DetourInit();
initTimer.End();
spdlog::info("+-------------------------------------------------------------+\n");
spdlog::info("{:16s} '{:10.6f}' seconds ('{:12d}' clocks)\n", "Detour->InitDB()",
DevMsg(eDLL_T::NONE, "+-------------------------------------------------------------+\n");
DevMsg(eDLL_T::NONE, "%-16s '%10.6f' seconds ('%12lu' clocks)\n", "Detour->InitDB()",
initTimer.GetDuration().GetSeconds(), initTimer.GetDuration().GetCycles());
initTimer.Start();
@ -187,9 +187,9 @@ void Systems_Init()
}
initTimer.End();
spdlog::info("{:16s} '{:10.6f}' seconds ('{:12d}' clocks)\n", "Detour->Attach()",
DevMsg(eDLL_T::NONE, "%-16s '%10.6f' seconds ('%12lu' clocks)\n", "Detour->Attach()",
initTimer.GetDuration().GetSeconds(), initTimer.GetDuration().GetCycles());
spdlog::info("+-------------------------------------------------------------+\n");
DevMsg(eDLL_T::NONE, "+-------------------------------------------------------------+\n");
ConVar_StaticInit();
}
@ -224,9 +224,9 @@ void Systems_Shutdown()
DetourTransactionCommit();
shutdownTimer.End();
spdlog::info("{:16s} '{:10.6f}' seconds ('{:12d}' clocks)\n", "Detour->Detach()",
DevMsg(eDLL_T::NONE, "%-16s '%10.6f' seconds ('%12lu' clocks)\n", "Detour->Detach()",
shutdownTimer.GetDuration().GetSeconds(), shutdownTimer.GetDuration().GetCycles());
spdlog::info("+-------------------------------------------------------------+\n");
DevMsg(eDLL_T::NONE, "+-------------------------------------------------------------+\n");
}
/////////////////////////////////////////////////////
@ -275,20 +275,20 @@ void QuerySystemInfo()
{
char szDeviceName[128];
wcstombs(szDeviceName, dd.DeviceString, sizeof(szDeviceName));
spdlog::info("{:25s}: '{:s}'\n", "GPU model identifier", szDeviceName);
DevMsg(eDLL_T::NONE, "%-25s: '%s'\n", "GPU model identifier", szDeviceName);
}
}
#endif // !DEDICATED
const CPUInformation& pi = GetCPUInformation();
spdlog::info("{:25s}: '{:s}'\n","CPU model identifier", pi.m_szProcessorBrand);
spdlog::info("{:25s}: '{:s}'\n","CPU vendor tag", pi.m_szProcessorID);
spdlog::info("{:25s}: '{:12d}' ('{:2d}' {:s})\n", "CPU core count", pi.m_nPhysicalProcessors, pi.m_nLogicalProcessors, "logical");
spdlog::info("{:25s}: '{:12d}' ({:12s})\n", "CPU core speed", pi.m_Speed, "Cycles");
spdlog::info("{:20s}{:s}: '{:12d}' (0x{:<10X})\n", "L1 cache", "(KiB)", pi.m_nL1CacheSizeKb, pi.m_nL1CacheDesc);
spdlog::info("{:20s}{:s}: '{:12d}' (0x{:<10X})\n", "L2 cache", "(KiB)", pi.m_nL2CacheSizeKb, pi.m_nL2CacheDesc);
spdlog::info("{:20s}{:s}: '{:12d}' (0x{:<10X})\n", "L3 cache", "(KiB)", pi.m_nL3CacheSizeKb, pi.m_nL3CacheDesc);
DevMsg(eDLL_T::NONE, "%-25s: '%s'\n","CPU model identifier", pi.m_szProcessorBrand);
DevMsg(eDLL_T::NONE, "%-25s: '%s'\n","CPU vendor tag", pi.m_szProcessorID);
DevMsg(eDLL_T::NONE, "%-25s: '%12hhu' ('%2hhu' %s)\n", "CPU core count", pi.m_nPhysicalProcessors, pi.m_nLogicalProcessors, "logical");
DevMsg(eDLL_T::NONE, "%-25s: '%12lld' (%-12s)\n", "CPU core speed", pi.m_Speed, "Cycles");
DevMsg(eDLL_T::NONE, "%-20s%s: '%12lu' (0x%-10X)\n", "L1 cache", "(KiB)", pi.m_nL1CacheSizeKb, pi.m_nL1CacheDesc);
DevMsg(eDLL_T::NONE, "%-20s%s: '%12lu' (0x%-10X)\n", "L2 cache", "(KiB)", pi.m_nL2CacheSizeKb, pi.m_nL2CacheDesc);
DevMsg(eDLL_T::NONE, "%-20s%s: '%12lu' (0x%-10X)\n", "L3 cache", "(KiB)", pi.m_nL3CacheSizeKb, pi.m_nL3CacheDesc);
MEMORYSTATUSEX statex{};
statex.dwLength = sizeof(statex);
@ -301,13 +301,13 @@ void QuerySystemInfo()
DWORDLONG availPhysical = (statex.ullAvailPhys / 1024) / 1024;
DWORDLONG availVirtual = (statex.ullAvailVirtual / 1024) / 1024;
spdlog::info("{:20s}{:s}: '{:12d}' ('{:9d}' {:s})\n", "Total system memory", "(MiB)", totalPhysical, totalVirtual, "virtual");
spdlog::info("{:20s}{:s}: '{:12d}' ('{:9d}' {:s})\n", "Avail system memory", "(MiB)", availPhysical, availVirtual, "virtual");
DevMsg(eDLL_T::NONE, "%-20s%s: '%12llu' ('%9llu' %s)\n", "Total system memory", "(MiB)", totalPhysical, totalVirtual, "virtual");
DevMsg(eDLL_T::NONE, "%-20s%s: '%12llu' ('%9llu' %s)\n", "Avail system memory", "(MiB)", availPhysical, availVirtual, "virtual");
}
else
{
spdlog::error("Unable to retrieve system memory information: {:s}\n",
std::system_category().message(static_cast<int>(::GetLastError())));
Error(eDLL_T::COMMON, NO_ERROR, "Unable to retrieve system memory information: %s\n",
std::system_category().message(static_cast<int>(::GetLastError())).c_str());
}
}
@ -337,12 +337,11 @@ void CheckCPU() // Respawn's engine and our SDK utilize POPCNT, SSE3 and SSSE3 (
void DetourInit() // Run the sigscan
{
LPSTR pCommandLine = GetCommandLineA();
bool bLogAdr = (strstr(pCommandLine, "-sig_toconsole") != nullptr);
const bool bLogAdr = CommandLine()->CheckParm("-sig_toconsole") ? true : false;
const bool bNoSmap = CommandLine()->CheckParm("-nosmap") ? true : false;
bool bInitDivider = false;
g_SigCache.SetDisabled((strstr(pCommandLine, "-nosmap") != nullptr));
g_SigCache.SetDisabled(bNoSmap);
g_SigCache.LoadCache(SIGDB_FILE);
for (const IDetour* pDetour : g_DetourVector)

View File

@ -14,3 +14,5 @@ void CheckCPU();
void DetourInit();
void DetourAddress();
void DetourRegister();
extern bool g_bSdkInitialized;

View File

@ -10,7 +10,7 @@ std::shared_ptr<spdlog::sinks::ostream_sink_st> g_LogSink;
//#############################################################################
// SPDLOG INIT
//#############################################################################
void SpdLog_Init(void)
void SpdLog_Init(const bool bAnsiColor)
{
static bool bInitialized = false;
@ -30,7 +30,7 @@ void SpdLog_Init(void)
g_LogSink = std::make_shared<spdlog::sinks::ostream_sink_st>(g_LogStream);
g_ImGuiLogger = std::make_shared<spdlog::logger>("game_console", g_LogSink);
spdlog::register_logger(g_ImGuiLogger); // in-game console logger.
g_ImGuiLogger->set_pattern("[0.000] %v");
g_ImGuiLogger->set_pattern("%v");
g_ImGuiLogger->set_level(spdlog::level::trace);
}
#endif // !NETCONSOLE
@ -45,14 +45,14 @@ void SpdLog_Init(void)
#endif // NETCONSOLE
// Determine if user wants ansi-color logging in the terminal.
if (g_svCmdLine.find("-ansicolor") != string::npos)
if (bAnsiColor)
{
g_TermLogger->set_pattern("[0.000] %v\u001b[0m");
g_TermLogger->set_pattern("%v\u001b[0m");
g_bSpdLog_UseAnsiClr = true;
}
else
{
g_TermLogger->set_pattern("[0.000] %v");
g_TermLogger->set_pattern("%v");
}
//g_TermLogger->set_level(spdlog::level::trace);
}
@ -91,25 +91,6 @@ void SpdLog_Create()
, fmt::format("{:s}\\{:s}", g_LogSessionDirectory, "filesystem.log"), SPDLOG_MAX_SIZE, SPDLOG_NUM_FILE)->set_pattern("[%Y-%m-%d %H:%M:%S.%e] %v");
}
//#############################################################################
// SPDLOG POST INIT
//#############################################################################
void SpdLog_PostInit()
{
#ifndef NETCONSOLE
spdlog::flush_every(std::chrono::seconds(5)); // Flush buffers every 5 seconds for every logger.
g_ImGuiLogger->set_pattern("%v");
#endif // !NETCONSOLE
if (g_svCmdLine.find("-ansicolor") != string::npos)
{
g_TermLogger->set_pattern("%v\u001b[0m");
g_bSpdLog_UseAnsiClr = true;
}
else { g_TermLogger->set_pattern("%v"); }
g_bSpdLog_PostInit = true;
}
//#############################################################################
// SPDLOG SHUTDOWN
//#############################################################################

View File

@ -14,7 +14,6 @@ constexpr int SPDLOG_MAX_SIZE = 10 * 1024 * 1024; // Sets number of bytes before
constexpr int SPDLOG_NUM_FILE = 512; // Sets number of files to rotate to.
inline bool g_bSpdLog_UseAnsiClr = false;
inline bool g_bSpdLog_PostInit = false;
extern std::shared_ptr<spdlog::logger> g_TermLogger;
extern std::shared_ptr<spdlog::logger> g_ImGuiLogger;
@ -24,7 +23,6 @@ extern std::shared_ptr<spdlog::logger> g_ImGuiLogger;
extern std::ostringstream g_LogStream;
extern std::shared_ptr<spdlog::sinks::ostream_sink_st> g_LogSink;
void SpdLog_Init(void);
void SpdLog_Init(const bool bAnsiColor);
void SpdLog_Create(void);
void SpdLog_PostInit(void);
void SpdLog_Shutdown(void);

View File

@ -1,5 +1,5 @@
#include "core/stdafx.h"
#include "tier0/utility.h"
#include "init.h"
#include "logdef.h"
#include "logger.h"
#ifndef DEDICATED
@ -95,10 +95,10 @@ const char* GetContextNameByIndex(eDLL_T context, const bool ansiColor = false)
case eDLL_T::VIDEO:
case eDLL_T::NETCON:
case eDLL_T::COMMON:
contextName = s_DllAnsiColor[index];
break;
case eDLL_T::SYSTEM_WARNING:
case eDLL_T::SYSTEM_ERROR:
contextName = s_DllAnsiColor[index];
break;
case eDLL_T::NONE:
default:
break;
@ -141,7 +141,7 @@ void EngineLoggerSink(LogType_t logType, LogLevel_t logLevel, eDLL_T context,
const UINT exitCode /*= NO_ERROR*/, const char* pszUptimeOverride /*= nullptr*/)
{
const char* pszUpTime = pszUptimeOverride ? pszUptimeOverride : Plat_GetProcessUpTime();
string message = g_bSpdLog_PostInit ? pszUpTime : "";
string message(pszUpTime);
const bool bToConsole = (logLevel >= LogLevel_t::LEVEL_CONSOLE);
const bool bUseColor = (bToConsole && g_bSpdLog_UseAnsiClr);
@ -288,15 +288,15 @@ void EngineLoggerSink(LogType_t logType, LogLevel_t logLevel, eDLL_T context,
#ifndef DEDICATED
g_ImGuiLogger->debug(message);
if (g_bSpdLog_PostInit)
{
const string logStreamBuf = g_LogStream.str();
g_pConsole->AddLog(ConLog_t(logStreamBuf, overlayColor));
const string logStreamBuf = g_LogStream.str();
g_pConsole->AddLog(ConLog_t(logStreamBuf, overlayColor));
if (logLevel >= LogLevel_t::LEVEL_NOTIFY) // Draw to mini console.
{
g_pOverlay->AddLog(overlayContext, logStreamBuf);
}
// We can only log to the in-game overlay console when the SDK has
// been fully initialized, due to the use of ConVar's.
if (g_bSdkInitialized && logLevel >= LogLevel_t::LEVEL_NOTIFY)
{
// Draw to mini console.
g_pOverlay->AddLog(overlayContext, logStreamBuf);
}
#endif // !DEDICATED
}

View File

@ -1,9 +1,5 @@
#pragma once
#define WIN32_LEAN_AND_MEAN // Exclude rarely-used stuff from Windows headers
// Windows Header Files
#include <windows.h>
__declspec(dllexport) void DummyExport()
{
// Required for detours.

View File

@ -15,8 +15,6 @@ const char* g_svYellowB = "";
const char* g_svReset = "";
std::string g_svCmdLine;
//-----------------------------------------------------------------------------
// Purpose: sets the global ansi escape sequences.
// If '-ansicolor' has not been passed to the sdk the char will be empty.

View File

@ -13,6 +13,4 @@ extern const char* g_svYellowB;
extern const char* g_svReset;
extern std::string g_svCmdLine;
void AnsiColors_Init();

View File

@ -14,10 +14,14 @@
int LauncherMain(HINSTANCE hInstance)
{
SpdLog_PostInit();
// Flush buffers every 5 seconds for every logger.
// Has to be done here, don't move this to SpdLog
// init, as this could cause deadlocks on certain
// compilers (VS2017)!!!
spdlog::flush_every(std::chrono::seconds(5));
int results = v_LauncherMain(hInstance);
spdlog::info("{:s} returned: {:s}\n", __FUNCTION__, ExitCodeToString(results));
DevMsg(eDLL_T::NONE, "%s returned: %s\n", __FUNCTION__, ExitCodeToString(results));
return results;
}
@ -73,66 +77,6 @@ void AppendSDKParametersPreInit()
CommandLine()->AppendParm("-nojoy", "");
CommandLine()->AppendParm("-nosendtable", "");
}
// Assume default configs if the game isn't launched with the SDKLauncher.
if (!CommandLine()->FindParm("-launcher"))
{
ParseAndApplyConfigFile(g_svCmdLine);
}
}
string LoadConfigFile(const char* svConfig)
{
fs::path cfgPath = fs::current_path() /= svConfig; // Get cfg path for default startup.
if (!FileExists(cfgPath))
{
// Load it from PLATFORM.
cfgPath = fs::current_path() /= string("platform/") + svConfig;
}
ifstream cfgFile(cfgPath);
if (!cfgFile)
{
spdlog::error("{:s}: '{:s}' does not exist!\n", __FUNCTION__, svConfig);
return "";
}
string svArguments;
stringstream ss;
ss << cfgFile.rdbuf();
svArguments = ss.str();
return svArguments;
}
void ParseAndApplyConfigFile(const string& svConfig)
{
stringstream ss(svConfig);
string svInput;
if (!svConfig.empty())
{
while (std::getline(ss, svInput, '\n'))
{
string::size_type nPos = svInput.find(' ');
if (!svInput.empty()
&& nPos > 0
&& nPos < svInput.size()
&& nPos != svInput.size())
{
string svValue = svInput.substr(nPos + 1);
string svArgument = svInput.erase(svInput.find(' '));
CommandLine()->AppendParm(svArgument.c_str(), svValue.c_str());
}
else
{
CommandLine()->AppendParm(svInput.c_str(), "");
}
}
}
}
const char* ExitCodeToString(int nCode)

View File

@ -13,8 +13,6 @@ inline auto v_RemoveSpuriousGameParameters = p_RemoveSpuriousGameParameters.RCas
#endif // !GAMEDLL_S0 || !GAMEDLL_S1
void AppendSDKParametersPreInit();
string LoadConfigFile(const char* svConfig);
void ParseAndApplyConfigFile(const string& svConfig);
const char* ExitCodeToString(int nCode);
///////////////////////////////////////////////////////////////////////////////

View File

@ -101,11 +101,11 @@ bool CNetCon::Shutdown(void)
//-----------------------------------------------------------------------------
void CNetCon::TermSetup(void)
{
g_svCmdLine = GetCommandLineA();
const char* pszCommandLine = GetCommandLineA();
const bool bEnableColor = strstr("-ansicolor", pszCommandLine) != nullptr;
SpdLog_Init();
SpdLog_PostInit();
Console_Init();
SpdLog_Init(bEnableColor);
Console_Init(bEnableColor);
}
//-----------------------------------------------------------------------------

View File

@ -66,9 +66,11 @@ enum class LogLevel_t
LEVEL_NOTIFY // Emit to in-game mini console
};
//-----------------------------------------------------------------------------
constexpr const char s_CommonAnsiColor[] = "\033[38;2;255;204;153m";
constexpr const char s_DefaultAnsiColor[]= "\033[38;2;204;204;204m";
constexpr const char* s_DllAnsiColor[12] =
constexpr const char s_CommonAnsiColor[] = "\033[38;2;255;204;153m";
constexpr const char s_WarningAnsiColor[] = "\033[38;2;255;255;000m";
constexpr const char s_ErrorAnsiColor[] = "\033[38;2;255;000;000m";
constexpr const char s_DefaultAnsiColor[] = "\033[38;2;204;204;204m";
constexpr const char* s_DllAnsiColor[14] =
{
"\033[38;2;059;120;218mNative(S):",
"\033[38;2;118;118;118mNative(C):",
@ -81,6 +83,8 @@ constexpr const char* s_DllAnsiColor[12] =
"\033[38;2;185;000;235mNative(V):",
"\033[38;2;204;204;204mNetcon(X):",
s_CommonAnsiColor,
s_WarningAnsiColor,
s_ErrorAnsiColor,
s_DefaultAnsiColor
};
//-----------------------------------------------------------------------------

View File

@ -67,8 +67,9 @@ void FlashConsoleBackground(int nFlashCount, int nFlashInterval, COLORREF color)
//-----------------------------------------------------------------------------
// Purpose: terminal window setup
// Input : bAnsiColor -
//-----------------------------------------------------------------------------
void Console_Init()
void Console_Init(const bool bAnsiColor)
{
#ifndef NETCONSOLE
///////////////////////////////////////////////////////////////////////////
@ -102,7 +103,7 @@ void Console_Init()
HANDLE hOutput = GetStdHandle(STD_OUTPUT_HANDLE);
DWORD dwMode = NULL;
if (g_svCmdLine.find("-ansicolor") != string::npos)
if (bAnsiColor)
{
GetConsoleMode(hOutput, &dwMode);
dwMode |= ENABLE_PROCESSED_OUTPUT | ENABLE_VIRTUAL_TERMINAL_PROCESSING;

View File

@ -3,5 +3,5 @@
void SetConsoleBackgroundColor(COLORREF color);
void FlashConsoleBackground(int nFlashCount, int nFlashInterval, COLORREF color);
void Console_Init();
void Console_Init(const bool bAnsiColor);
void Console_Shutdown();