NorthstarLauncher/NorthstarDLL/logging.cpp

269 lines
6.3 KiB
C++

#include "pch.h"
#include "logging.h"
#include "convar.h"
#include "concommand.h"
#include "nsprefix.h"
#include "bitbuf.h"
#include "tier0.h"
#include "spdlog/sinks/basic_file_sink.h"
#include <iomanip>
#include <sstream>
AUTOHOOK_INIT()
ConVar* Cvar_spewlog_enable;
enum class SpewType_t
{
SPEW_MESSAGE = 0,
SPEW_WARNING,
SPEW_ASSERT,
SPEW_ERROR,
SPEW_LOG,
SPEW_TYPE_COUNT
};
const std::unordered_map<SpewType_t, const char*> PrintSpewTypes = {
{SpewType_t::SPEW_MESSAGE, "SPEW_MESSAGE"},
{SpewType_t::SPEW_WARNING, "SPEW_WARNING"},
{SpewType_t::SPEW_ASSERT, "SPEW_ASSERT"},
{SpewType_t::SPEW_ERROR, "SPEW_ERROR"},
{SpewType_t::SPEW_LOG, "SPEW_LOG"}};
// clang-format off
AUTOHOOK(EngineSpewFunc, engine.dll + 0x11CA80,
void, __fastcall, (void* pEngineServer, SpewType_t type, const char* format, va_list args))
// clang-format on
{
if (!Cvar_spewlog_enable->GetBool())
return;
const char* typeStr = PrintSpewTypes.at(type);
char formatted[2048] = {0};
bool bShouldFormat = true;
// because titanfall 2 is quite possibly the worst thing to yet exist, it sometimes gives invalid specifiers which will crash
// ttf2sdk had a way to prevent them from crashing but it doesnt work in debug builds
// so we use this instead
for (int i = 0; format[i]; i++)
{
if (format[i] == '%')
{
switch (format[i + 1])
{
// this is fucking awful lol
case 'd':
case 'i':
case 'u':
case 'x':
case 'X':
case 'f':
case 'F':
case 'g':
case 'G':
case 'a':
case 'A':
case 'c':
case 's':
case 'p':
case 'n':
case '%':
case '-':
case '+':
case ' ':
case '#':
case '*':
case '0':
case '1':
case '2':
case '3':
case '4':
case '5':
case '6':
case '7':
case '8':
case '9':
break;
default:
{
bShouldFormat = false;
break;
}
}
}
}
if (bShouldFormat)
vsnprintf(formatted, sizeof(formatted), format, args);
else
spdlog::warn("Failed to format {} \"{}\"", typeStr, format);
auto endpos = strlen(formatted);
if (formatted[endpos - 1] == '\n')
formatted[endpos - 1] = '\0'; // cut off repeated newline
spdlog::info("[SERVER {}] {}", typeStr, formatted);
}
// used for printing the output of status
// clang-format off
AUTOHOOK(Status_ConMsg, engine.dll + 0x15ABD0,
void,, (const char* text, ...))
// clang-format on
{
char formatted[2048];
va_list list;
va_start(list, text);
vsprintf_s(formatted, text, list);
va_end(list);
auto endpos = strlen(formatted);
if (formatted[endpos - 1] == '\n')
formatted[endpos - 1] = '\0'; // cut off repeated newline
spdlog::info(formatted);
}
// clang-format off
AUTOHOOK(CClientState_ProcessPrint, engine.dll + 0x1A1530,
bool,, (void* thisptr, uintptr_t msg))
// clang-format on
{
char* text = *(char**)(msg + 0x20);
auto endpos = strlen(text);
if (text[endpos - 1] == '\n')
text[endpos - 1] = '\0'; // cut off repeated newline
spdlog::info(text);
return true;
}
ConVar* Cvar_cl_showtextmsg;
class ICenterPrint
{
public:
virtual void ctor() = 0;
virtual void Clear(void) = 0;
virtual void ColorPrint(int r, int g, int b, int a, wchar_t* text) = 0;
virtual void ColorPrint(int r, int g, int b, int a, char* text) = 0;
virtual void Print(wchar_t* text) = 0;
virtual void Print(char* text) = 0;
virtual void SetTextColor(int r, int g, int b, int a) = 0;
};
ICenterPrint* pInternalCenterPrint = NULL;
enum class TextMsgPrintType_t
{
HUD_PRINTNOTIFY = 1,
HUD_PRINTCONSOLE,
HUD_PRINTTALK,
HUD_PRINTCENTER
};
// clang-format off
AUTOHOOK(TextMsg, client.dll + 0x198710,
void,, (BFRead* msg))
// clang-format on
{
TextMsgPrintType_t msg_dest = (TextMsgPrintType_t)msg->ReadByte();
char text[256];
msg->ReadString(text, sizeof(text));
if (!Cvar_cl_showtextmsg->GetBool())
return;
switch (msg_dest)
{
case TextMsgPrintType_t::HUD_PRINTCENTER:
pInternalCenterPrint->Print(text);
break;
default:
spdlog::warn("Unimplemented TextMsg type {}! printing to console", msg_dest);
[[fallthrough]];
case TextMsgPrintType_t::HUD_PRINTCONSOLE:
auto endpos = strlen(text);
if (text[endpos - 1] == '\n')
text[endpos - 1] = '\0'; // cut off repeated newline
spdlog::info(text);
break;
}
}
// clang-format off
AUTOHOOK(ConCommand_echo, engine.dll + 0x123680,
void,, (const CCommand& arg))
// clang-format on
{
if (arg.ArgC() >= 2)
spdlog::info("[echo] {}", arg.ArgS());
}
// This needs to be called after hooks are loaded so we can access the command line args
void CreateLogFiles()
{
if (strstr(GetCommandLineA(), "-disablelogs"))
{
spdlog::default_logger()->set_level(spdlog::level::off);
}
else
{
try
{
// todo: might be good to delete logs that are too old
time_t time = std::time(nullptr);
tm currentTime = *std::localtime(&time);
std::stringstream stream;
stream << std::put_time(&currentTime, (GetNorthstarPrefix() + "/logs/nslog%Y-%m-%d %H-%M-%S.txt").c_str());
spdlog::default_logger()->sinks().push_back(std::make_shared<spdlog::sinks::basic_file_sink_mt>(stream.str(), false));
spdlog::flush_on(spdlog::level::info);
}
catch (...)
{
spdlog::error("Failed creating log file!");
MessageBoxA(
0, "Failed creating log file! Make sure the profile directory is writable.", "Northstar Warning", MB_ICONWARNING | MB_OK);
}
}
}
void InitialiseLogging()
{
AllocConsole();
// Bind stdout to receive console output.
// these two lines are responsible for stuff to not show up in the console sometimes, from talking about it on discord
// apparently they were meant to make logging work when using -northstar, however from testing it seems that it doesnt
// work regardless of these two lines
// freopen("CONOUT$", "w", stdout);
// freopen("CONOUT$", "w", stderr);
spdlog::default_logger()->set_pattern("[%H:%M:%S] [%l] %v");
}
ON_DLL_LOAD_CLIENT_RELIESON("engine.dll", EngineSpewFuncHooks, ConVar, (CModule module))
{
AUTOHOOK_DISPATCH_MODULE(engine.dll)
Cvar_spewlog_enable = new ConVar("spewlog_enable", "1", FCVAR_NONE, "Enables/disables whether the engine spewfunc should be logged");
}
ON_DLL_LOAD_CLIENT_RELIESON("client.dll", ClientPrintHooks, ConVar, (CModule module))
{
AUTOHOOK_DISPATCH_MODULE(client.dll)
Cvar_cl_showtextmsg = new ConVar("cl_showtextmsg", "1", FCVAR_NONE, "Enable/disable text messages printing on the screen.");
pInternalCenterPrint = module.Offset(0x216E940).As<ICenterPrint*>();
}