Restore gamelog and consolelog properly

This commit is contained in:
Diavolo 2022-08-07 11:38:06 +02:00
parent ac12467a49
commit b706e21381
No known key found for this signature in database
GPG Key ID: FA77F074E98D98A5
14 changed files with 1075 additions and 155 deletions

View File

@ -0,0 +1,84 @@
#include <std_include.hpp>
#include "game/game.hpp"
#include "scoped_critical_section.hpp"
namespace game::engine
{
scoped_critical_section::scoped_critical_section(const native::CriticalSection s, const native::ScopedCriticalSectionType type)
: s_(s), is_scoped_release_(false)
{
if (type == native::SCOPED_CRITSECT_NORMAL)
{
Sys_EnterCriticalSection(this->s_);
this->has_ownership_ = true;
}
else
{
if (type == native::SCOPED_CRITSECT_TRY)
{
this->has_ownership_ = Sys_TryEnterCriticalSection(this->s_);
}
else
{
if (type == native::SCOPED_CRITSECT_RELEASE)
{
Sys_LeaveCriticalSection(this->s_);
this->is_scoped_release_ = true;
}
this->has_ownership_ = false;
}
}
}
scoped_critical_section::~scoped_critical_section()
{
if (!this->has_ownership_ || this->is_scoped_release_)
{
if (!this->has_ownership_ && this->is_scoped_release_)
{
Sys_EnterCriticalSection(this->s_);
}
}
else
{
Sys_LeaveCriticalSection(this->s_);
}
}
void scoped_critical_section::enter_crit_sect()
{
assert(!this->has_ownership_);
this->has_ownership_ = true;
Sys_EnterCriticalSection(this->s_);
}
void scoped_critical_section::leave_crit_sect()
{
assert(this->has_ownership_);
this->has_ownership_ = false;
Sys_LeaveCriticalSection(this->s_);
}
bool scoped_critical_section::try_enter_crit_sect()
{
assert(!this->has_ownership_);
const auto result = Sys_TryEnterCriticalSection(this->s_);
this->has_ownership_ = result;
return result;
}
bool scoped_critical_section::has_ownership() const
{
return this->has_ownership_;
}
bool scoped_critical_section::is_scoped_release() const
{
return this->is_scoped_release_;
}
}

View File

@ -0,0 +1,23 @@
#pragma once
namespace game::engine
{
class scoped_critical_section
{
public:
scoped_critical_section(native::CriticalSection s, native::ScopedCriticalSectionType type);
~scoped_critical_section();
void enter_crit_sect();
void leave_crit_sect();
bool try_enter_crit_sect();
[[nodiscard]] bool has_ownership() const;
[[nodiscard]] bool is_scoped_release() const;
private:
native::CriticalSection s_;
bool has_ownership_;
bool is_scoped_release_;
};
}

View File

@ -13,7 +13,6 @@ namespace game
DB_LoadXAssets_t DB_LoadXAssets;
Dvar_RegisterBool_t Dvar_RegisterBool;
Dvar_RegisterInt_t Dvar_RegisterInt;
Dvar_RegisterString_t Dvar_RegisterString;
Dvar_SetIntByName_t Dvar_SetIntByName;
@ -39,7 +38,7 @@ namespace game
Sys_ShowConsole_t Sys_ShowConsole;
Sys_Error_t Sys_Error;
Sys_IsServerThread_t Sys_IsServerThread;
Sys_Milliseconds_t Sys_Milliseconds;
VM_Notify_t VM_Notify;
@ -73,6 +72,8 @@ namespace game
Com_Quit_f_t Com_Quit_f;
FS_Printf_t FS_Printf;
player_die_t player_die;
decltype(longjmp)* _longjmp;
@ -107,6 +108,22 @@ namespace game
float* com_codeTimeScale;
RTL_CRITICAL_SECTION* s_criticalSection;
int* logfile;
searchpath_s** fs_searchpaths;
char* fs_gamedir;
fileHandleData_t* fsh;
int* com_fileAccessed;
// DS does not have MJPEG thread
unsigned int (*threadId)[THREAD_CONTEXT_COUNT];
int* initialized_0;
int* sys_timeBase;
unsigned __int64* sys_counterBase;
namespace mp
{
SV_GetGuid_t SV_GetGuid;
@ -193,11 +210,9 @@ namespace game
{
return find_variable_dedicated(parentId, name);
}
else
{
return reinterpret_cast<unsigned int(*)(unsigned int, unsigned int)> //
(SELECT_VALUE(0x4C4E70, 0x5651F0, 0x0))(parentId, name);
}
return reinterpret_cast<unsigned int(*)(unsigned int, unsigned int)> //
(SELECT_VALUE(0x4C4E70, 0x5651F0, 0x0))(parentId, name);
}
__declspec(naked) VariableValue get_entity_field_value_dedicated(unsigned int classnum, int entnum, int _offset)
@ -221,11 +236,9 @@ namespace game
{
return get_entity_field_value_dedicated(classnum, entnum, offset);
}
else
{
return reinterpret_cast<VariableValue(*)(unsigned int, int, int)> //
(SELECT_VALUE(0x530E30, 0x56AF20, 0x0))(classnum, entnum, offset);
}
return reinterpret_cast<VariableValue(*)(unsigned int, int, int)> //
(SELECT_VALUE(0x530E30, 0x56AF20, 0x0))(classnum, entnum, offset);
}
void* MT_Alloc(const int numBytes, const int type)
@ -312,6 +325,27 @@ namespace game
flags, dvar_value, domain, description);
}
const dvar_t* Dvar_RegisterInt(const char* dvarName, int value,
int min, int max, unsigned __int16 flags, const char* description)
{
if (!is_dedi())
{
return reinterpret_cast<const dvar_t*(*)(const char*, int, int, int, unsigned __int16, const char*)>
(SELECT_VALUE(0x48CD40, 0x5BEA40, 0x0))(dvarName, value, min, max, flags, description);
}
DvarLimits domain;
DvarValue dvar_value;
domain.integer.min = min;
domain.integer.max = max;
dvar_value.integer = value;
return dvar_register_variant_dedicated(dvarName, DVAR_TYPE_INT,
flags, dvar_value, domain, description);
}
void IncInParam()
{
Scr_ClearOutParams();
@ -368,10 +402,8 @@ namespace game
{
return scr_instanceFunctions[index];
}
else
{
return scr_globalFunctions[index];
}
return scr_globalFunctions[index];
}
__declspec(naked) void scr_notify_id_multiplayer(unsigned int id, unsigned int string_value,
@ -444,11 +476,9 @@ namespace game
{
return scr_set_object_field_dedicated(classnum, entnum, offset);
}
else
{
return reinterpret_cast<int(*)(unsigned int, int, int)> //
(SELECT_VALUE(0x42CAD0, 0x52BCC0, 0x0))(classnum, entnum, offset);
}
return reinterpret_cast<int(*)(unsigned int, int, int)> //
(SELECT_VALUE(0x42CAD0, 0x52BCC0, 0x0))(classnum, entnum, offset);
}
__declspec(naked) void scr_add_string_dedicated(const char* value)
@ -529,7 +559,8 @@ namespace game
{
return dedi::svs_clients[clientNum].bIsTestClient;
}
else if (is_mp())
if (is_mp())
{
return mp::svs_clients[clientNum].bIsTestClient;
}
@ -667,6 +698,238 @@ namespace game
reinterpret_cast<void(*)(LocalClientNum_t, const char*, int)>(0x4228A0)(localClientNum, msg, flags);
}
}
void Sys_EnterCriticalSection(CriticalSection critSect)
{
assert(static_cast<unsigned int>(critSect) <
static_cast<unsigned int>(CRITSECT_COUNT));
EnterCriticalSection(&s_criticalSection[critSect]);
}
void Sys_LeaveCriticalSection(CriticalSection critSect)
{
assert(static_cast<unsigned int>(critSect) <
static_cast<unsigned int>(CRITSECT_COUNT));
LeaveCriticalSection(&s_criticalSection[critSect]);
}
bool Sys_TryEnterCriticalSection(CriticalSection critSect)
{
assert(static_cast<unsigned int>(critSect) <
static_cast<unsigned int>(CRITSECT_COUNT));
return TryEnterCriticalSection(&s_criticalSection[critSect]) != FALSE;
}
bool Sys_IsMainThread()
{
const auto id = GetCurrentThreadId();
assert(id);
return id == *threadId[THREAD_CONTEXT_MAIN];
}
bool Sys_IsDatabaseThread()
{
const auto id = GetCurrentThreadId();
assert(id);
return id == *threadId[THREAD_CONTEXT_DATABASE];
}
bool Sys_IsStreamThread()
{
const auto id = GetCurrentThreadId();
assert(id);
return id == *threadId[THREAD_CONTEXT_STREAM];
}
bool Sys_IsRenderThread()
{
const auto id = GetCurrentThreadId();
assert(id);
return id == *threadId[THREAD_CONTEXT_BACKEND];
}
bool Sys_IsServerThread()
{
const auto id = GetCurrentThreadId();
assert(id);
return id == *threadId[THREAD_CONTEXT_SERVER];
}
void fs_fclose_file_dedicated(int h)
{
static DWORD func = 0x524910;
__asm
{
pushad
mov eax, h
call func
popad
}
}
void FS_FCloseFile(int h)
{
if (is_dedi())
{
fs_fclose_file_dedicated(h);
}
else
{
reinterpret_cast<void(*)(int)>
(SELECT_VALUE(0x415160, 0x5AF170, 0x0))(h);
}
}
bool FS_Initialized()
{
return (*fs_searchpaths != nullptr);
}
void* fs_handle_for_file_dedicated(FsThread thread)
{
static DWORD func = 0x5245F0;
void* result = nullptr;
__asm
{
pushad
mov edi, thread
call func
mov result, eax
popad
}
return result;
}
void* FS_HandleForFile(FsThread thread)
{
if (is_dedi())
{
return fs_handle_for_file_dedicated(thread);
}
return reinterpret_cast<void*(*)(FsThread)>
(SELECT_VALUE(0x46B1C0, 0x5AEE50, 0x0))(thread);
}
int fs_fopen_file_read_for_thread_singleplayer(const char* filename, int* file, FsThread thread)
{
static DWORD func = 0x627F20;
auto result = 0;
__asm
{
pushad
mov eax, file
mov edx, filename
push thread
call func
add esp, 0x4
mov result, eax
popad
}
return result;
}
int fs_fopen_file_read_for_thread_multiplayer(const char* filename, int* file, FsThread thread)
{
static DWORD func = 0x5B1990;
auto result = 0;
__asm
{
pushad
mov edx, filename
push thread
push file
call func
add esp, 0x8
mov result, eax
popad
}
return result;
}
int fs_fopen_file_read_for_thread_dedicated(const char* filename, int* file, FsThread thread)
{
static DWORD func = 0x524E30;
auto result = 0;
__asm
{
pushad
mov edx, filename
push thread
push file
call func
add esp, 0x8
mov result, eax
popad
}
return result;
}
int FS_FOpenFileReadForThread(const char* filename, int* file, FsThread thread)
{
if (is_sp())
{
return fs_fopen_file_read_for_thread_singleplayer(filename, file, thread);
}
if (is_mp())
{
return fs_fopen_file_read_for_thread_multiplayer(filename, file, thread);
}
return fs_fopen_file_read_for_thread_dedicated(filename, file, thread);
}
int fs_create_path_directory(char* OSPath)
{
static DWORD func = 0x5247D0;
auto result = 0;
__asm
{
pushad
mov edi, OSPath
call func
mov result, eax
popad
}
return result;
}
int FS_CreatePath(char* OSPath)
{
if (is_dedi())
{
return fs_create_path_directory(OSPath);
}
return reinterpret_cast<int(*)(char*)>
(SELECT_VALUE(0x4F5AB0, 0x5AF060, 0x0))(OSPath);
}
void FS_CheckFileSystemStarted()
{
assert(*fs_searchpaths);
}
}
launcher::mode mode = launcher::mode::none;
@ -708,7 +971,6 @@ namespace game
native::DB_LoadXAssets = native::DB_LoadXAssets_t(SELECT_VALUE(0x48A8E0, 0x4CD020, 0x44F770));
native::Dvar_RegisterBool = native::Dvar_RegisterBool_t(SELECT_VALUE(0x4914D0, 0x5BE9F0, 0x0));
native::Dvar_RegisterInt = native::Dvar_RegisterInt_t(SELECT_VALUE(0x48CD40, 0x5BEA40, 0x0));
native::Dvar_RegisterString = native::Dvar_RegisterString_t(SELECT_VALUE(0x5197F0, 0x5BEC90, 0x0));
native::Dvar_SetIntByName = native::Dvar_SetIntByName_t(SELECT_VALUE(0x5396B0, 0x5BF560, 0x0));
@ -735,8 +997,8 @@ namespace game
native::Scr_GetString = native::Scr_GetString_t(SELECT_VALUE(0x497530, 0x56A3D0, 0x0));
native::Sys_ShowConsole = native::Sys_ShowConsole_t(SELECT_VALUE(0x470AF0, 0x5CF590, 0));
native::Sys_Error = native::Sys_Error_t(SELECT_VALUE(0x490D90, 0x5CC3B0, 0x539590));
native::Sys_Milliseconds = native::Sys_Milliseconds_t(SELECT_VALUE(0x4A1610, 0x5CE740, 0x53B900));
native::VM_Notify = native::VM_Notify_t(SELECT_VALUE(0x610200, 0x569720, 0x4EF450));
@ -756,8 +1018,6 @@ namespace game
native::SV_SendServerCommand = native::SV_SendServerCommand_t(SELECT_VALUE(0x4F6990, 0x575DE0, 0x4FD5A0));
native::mp::SV_GetGuid = native::mp::SV_GetGuid_t(0x573990);
native::Sys_IsServerThread = native::Sys_IsServerThread_t(SELECT_VALUE(0x4CC5A0, 0x55F9A0, 0x0));
native::sp::IsServerRunning = native::sp::IsServerRunning_t(0x45D310);
native::XUIDToString = native::XUIDToString_t(SELECT_VALUE(0x4FAA30, 0x55CC20, 0x0));
@ -781,6 +1041,8 @@ namespace game
native::Com_Quit_f = native::Com_Quit_f_t(SELECT_VALUE(0x4F48B0, 0x5556B0, 0x4D95B0));
native::FS_Printf = native::FS_Printf_t(SELECT_VALUE(0x421E90, 0x5AF7C0, 0x5255A0));
native::player_die = native::player_die_t(SELECT_VALUE(0x0, 0x503460, 0x47F4D0));
native::_longjmp = reinterpret_cast<decltype(longjmp)*>(SELECT_VALUE(0x73AC20, 0x7363BC, 0x655558));
@ -823,5 +1085,20 @@ namespace game
native::deferredQueue = reinterpret_cast<native::DeferredQueue*>(SELECT_VALUE(0x0, 0x1D55438, 0x0));
native::com_codeTimeScale = reinterpret_cast<float*>(SELECT_VALUE(0x1769F1C, 0x1CEF554, 0x1B9CEC0));
native::s_criticalSection = reinterpret_cast<RTL_CRITICAL_SECTION*>(SELECT_VALUE(0x1CD5638, 0x5A91048, 0x593FF98));
native::logfile = reinterpret_cast<int*>(SELECT_VALUE(0x176B534, 0x1CF0B78, 0x1B9E4C8));
native::fs_searchpaths = reinterpret_cast<native::searchpath_s**>(SELECT_VALUE(0x1C2FE78, 0x59BA858, 0x62F4F60));
native::fs_gamedir = reinterpret_cast<char*>(SELECT_VALUE(0x1C2B220, 0x59A98F8, 0x585A4D8));
native::fsh = reinterpret_cast<native::fileHandleData_t*>(SELECT_VALUE(0x1C2B540, 0x59B5F20, 0x5866AF8));
native::com_fileAccessed = reinterpret_cast<int*>(SELECT_VALUE(0x1C2B328, 0x59A9A04, 0x585A5E0));
native::threadId = reinterpret_cast<unsigned(*)[native::THREAD_CONTEXT_COUNT]>(SELECT_VALUE(0x18576C8, 0x1D6E448, 0x1C14BDC));
native::initialized_0 = reinterpret_cast<int*>(SELECT_VALUE(0x1CE1CA0, 0x5AA3058, 0x62F4F9C));
native::sys_timeBase = reinterpret_cast<int*>(SELECT_VALUE(0x1CE1C98, 0x5AA3050, 0x5950CE4));
native::sys_counterBase = reinterpret_cast<unsigned __int64*>(SELECT_VALUE(0x1CE1C90, 0x5AA3048, 0x5950CE8));
}
}

View File

@ -27,10 +27,6 @@ namespace game
unsigned __int16 flags, const char* description);
extern Dvar_RegisterBool_t Dvar_RegisterBool;
typedef const dvar_t* (*Dvar_RegisterInt_t)(const char* dvarName, int value,
int min, int max, unsigned __int16 flags, const char* description);
extern Dvar_RegisterInt_t Dvar_RegisterInt;
typedef const dvar_t* (*Dvar_RegisterString_t)(const char* dvarName, const char* value,
unsigned __int16 flags, const char* description);
extern Dvar_RegisterString_t Dvar_RegisterString;
@ -83,8 +79,8 @@ namespace game
typedef void (*Sys_Error_t)(const char* error, ...);
extern Sys_Error_t Sys_Error;
typedef bool (*Sys_IsServerThread_t)();
extern Sys_IsServerThread_t Sys_IsServerThread;
typedef int (*Sys_Milliseconds_t)();
extern Sys_Milliseconds_t Sys_Milliseconds;
typedef void (*VM_Notify_t)(unsigned int notifyListOwnerId, unsigned int stringValue, VariableValue* top);
extern VM_Notify_t VM_Notify;
@ -150,6 +146,9 @@ namespace game
typedef void (*Com_Quit_f_t)();
extern Com_Quit_f_t Com_Quit_f;
typedef void (*FS_Printf_t)(int h, const char* fmt, ...);
extern FS_Printf_t FS_Printf;
typedef void (*player_die_t)(gentity_s* self, const gentity_s* inflictor, gentity_s* attacker, int damage, int meansOfDeath, const Weapon* iWeapon, bool isAlternate, const float* vDir, const hitLocation_t hitLoc, int psTimeOffset);
extern player_die_t player_die;
@ -188,6 +187,21 @@ namespace game
extern float* com_codeTimeScale;
extern RTL_CRITICAL_SECTION* s_criticalSection;
extern int* logfile;
extern searchpath_s** fs_searchpaths;
extern char* fs_gamedir;
extern fileHandleData_t* fsh;
extern int* com_fileAccessed;
extern unsigned int(*threadId)[THREAD_CONTEXT_COUNT];
extern int* initialized_0;
extern int* sys_timeBase;
extern unsigned __int64* sys_counterBase;
// PM Global Definitions & Functions
constexpr auto JUMP_LAND_SLOWDOWN_TIME = 1800;
@ -230,6 +244,7 @@ namespace game
dvar_t* Dvar_FindVar(const char* dvarName);
const dvar_t* Dvar_RegisterFloat(const char* dvarName, float value, float min, float max, unsigned __int16 flags, const char* description);
const dvar_t* Dvar_RegisterInt(const char* dvarName, int value, int min, int max, unsigned __int16 flags, const char* description);
const float* Scr_AllocVector(const float* v);
void Scr_ClearOutParams();
@ -259,6 +274,22 @@ namespace game
void TeleportPlayer(gentity_s* player, float* origin, float* angles);
void CG_GameMessage(LocalClientNum_t localClientNum, const char* msg, int flags = 0);
void Sys_EnterCriticalSection(CriticalSection critSect);
void Sys_LeaveCriticalSection(CriticalSection critSect);
bool Sys_TryEnterCriticalSection(CriticalSection critSect);
bool Sys_IsMainThread();
bool Sys_IsDatabaseThread();
bool Sys_IsStreamThread();
bool Sys_IsRenderThread();
bool Sys_IsServerThread();
void FS_FCloseFile(int h);
bool FS_Initialized();
void* FS_HandleForFile(FsThread thread);
int FS_FOpenFileReadForThread(const char* filename, int* file, FsThread thread);
int FS_CreatePath(char* OSPath);
void FS_CheckFileSystemStarted();
}
bool is_mp();

View File

@ -1197,6 +1197,112 @@ namespace game
static_assert(sizeof(level_locals_t) == 0x3080);
enum CriticalSection
{
CRITSECT_CONSOLE = 0x0,
CRITSECT_CBUF = 0x1F,
CRITSECT_COUNT = 0x27,
};
enum ScopedCriticalSectionType
{
SCOPED_CRITSECT_NORMAL = 0x0,
SCOPED_CRITSECT_DISABLED = 0x1,
SCOPED_CRITSECT_RELEASE = 0x2,
SCOPED_CRITSECT_TRY = 0x3,
};
struct fileInIwd_s
{
unsigned int pos;
char* name;
fileInIwd_s* next;
};
struct iwd_t
{
char iwdFilename[256];
char iwdBasename[256];
char iwdGamename[256];
char* handle;
int checksum;
int pure_checksum;
volatile int hasOpenFile;
int numfiles;
char referenced;
unsigned int hashSize;
fileInIwd_s** hashTable;
fileInIwd_s* buildBuffer;
};
struct directory_t
{
char path[256];
char gamedir[256];
};
struct searchpath_s
{
searchpath_s* next;
iwd_t* iwd;
directory_t* dir;
int bLocalized;
int ignore;
int ignorePureCheck;
int language;
};
enum
{
THREAD_CONTEXT_MAIN,
THREAD_CONTEXT_BACKEND,
THREAD_CONTEXT_WORKER0,
THREAD_CONTEXT_WORKER1,
THREAD_CONTEXT_SERVER,
THREAD_CONTEXT_CINEMATIC,
THREAD_CONTEXT_DATABASE,
THREAD_CONTEXT_STREAM,
THREAD_CONTEXT_STATS_WRITE,
THREAD_CONTEXT_MJPEG,
THREAD_CONTEXT_COUNT,
};
enum FsThread
{
FS_THREAD_MAIN = 0x0,
FS_THREAD_STREAM = 0x1,
FS_THREAD_DATABASE = 0x2,
FS_THREAD_BACKEND = 0x3,
FS_THREAD_SERVER = 0x4,
FS_THREAD_COUNT = 0x5,
FS_THREAD_INVALID = 0x6,
};
union qfile_gus
{
_iobuf* o;
char* z;
};
struct qfile_us
{
qfile_gus file;
int iwdIsClone;
};
struct fileHandleData_t
{
qfile_us handleFiles;
int handleSync;
int fileSize;
int zipFilePos;
iwd_t* zipFile;
int streamed;
char name[256];
};
static_assert(sizeof(fileHandleData_t) == 0x11C);
namespace mp
{
struct client_t

View File

@ -57,5 +57,5 @@ private:
#define REGISTER_MODULE(name) \
namespace \
{ \
static module_loader::installer<name> $_##name; \
static module_loader::installer<name> __module; \
}

View File

@ -5,7 +5,7 @@
#include <utils/hook.hpp>
#include "log_file.hpp"
#include "game_log.hpp"
static void notify_on_say(game::native::gentity_s* ent, int mode, const char* message)
{
@ -22,11 +22,11 @@ static void notify_on_say(game::native::gentity_s* ent, int mode, const char* me
if (mode == 0)
{
log_file::g_log_printf("say;%s;%d;%s;%s\n", guid, ent_num, name, message + 1);
game_log::g_log_printf("say;%s;%d;%s;%s\n", guid, ent_num, name, message + 1);
}
else
{
log_file::g_log_printf("sayteam;%s;%d;%s;%s\n", guid, ent_num, name, message + 1);
game_log::g_log_printf("sayteam;%s;%d;%s;%s\n", guid, ent_num, name, message + 1);
}
}

View File

@ -2,7 +2,9 @@
#include <loader/module_loader.hpp>
#include "game/game.hpp"
#include "scheduler.hpp"
#include "log_file.hpp"
class console final : public module
{
@ -11,9 +13,9 @@ public:
{
ShowWindow(GetConsoleWindow(), SW_HIDE);
_pipe(this->handles_, 1024, _O_TEXT);
_dup2(this->handles_[1], 1);
_dup2(this->handles_[1], 2);
(void)_pipe(this->handles_, 1024, _O_TEXT);
(void)_dup2(this->handles_[1], 1);
(void)_dup2(this->handles_[1], 2);
//setvbuf(stdout, nullptr, _IONBF, 0);
//setvbuf(stderr, nullptr, _IONBF, 0);
@ -89,6 +91,7 @@ private:
static void log_message(const std::string& message)
{
OutputDebugStringA(message.data());
log_file::info("%s", message.data());
game::native::Conbuf_AppendText(message.data());
}

273
src/module/file_system.cpp Normal file
View File

@ -0,0 +1,273 @@
#include <std_include.hpp>
#include <loader/module_loader.hpp>
#include <utils/hook.hpp>
#include "game/game.hpp"
#include "file_system.hpp"
static utils::hook::detour sys_default_install_path_hook;
static const game::native::dvar_t** fs_homepath;
static const game::native::dvar_t** fs_debug;
static FILE* file_for_handle(int f)
{
assert(!game::native::fsh[f].zipFile);
assert(game::native::fsh[f].handleFiles.file.o);
return game::native::fsh[f].handleFiles.file.o;
}
static unsigned int file_write(const void* ptr, unsigned int len, FILE* stream)
{
return std::fwrite(ptr, 1, len, stream);
}
static FILE* file_open_append_text(const char* filename)
{
FILE* file;
const auto err = fopen_s(&file, filename, "at");
if (err == 0)
{
return file;
}
printf("Couldn't open file: %s\n", filename);
return nullptr;
}
static void replace_separators(char* path)
{
char* src, * dst;
bool was_sep = false;
for (src = path, dst = path; *src; ++src)
{
if (*src == '/' || *src == '\\')
{
if (!was_sep)
{
was_sep = true;
*dst++ = '\\';
}
}
else
{
was_sep = false;
*dst++ = *src;
}
}
*dst = 0;
}
static void build_os_path_for_thread(const char* base, const char* game, const char* qpath, char* ospath, game::native::FsThread thread)
{
assert(base);
assert(qpath);
assert(ospath);
if (!game)
{
game = "";
}
else if (!game[0])
{
game = game::native::fs_gamedir;
}
auto len_base = std::strlen(base);
auto len_game = std::strlen(game);
auto len_qpath = std::strlen(qpath);
if (len_game + 1 + len_base + len_qpath + 1 >= 256)
{
if (thread)
{
*ospath = '\0';
return;
}
game::native::Com_Error(game::native::ERR_FATAL, "\x15" "FS_BuildOSPath: os path length exceeded\n");
}
std::memcpy(ospath, base, len_base);
ospath[len_base] = '/';
std::memcpy(&ospath[len_base + 1], game, len_game);
ospath[len_base + 1 + len_game] = '/';
std::memcpy(ospath + len_base + 2 + len_game, qpath, len_qpath + 1);
replace_separators(ospath);
}
static game::native::FsThread get_current_thread()
{
if (game::native::Sys_IsMainThread())
{
return game::native::FS_THREAD_MAIN;
}
if (game::native::Sys_IsDatabaseThread())
{
return game::native::FS_THREAD_DATABASE;
}
if (game::native::Sys_IsStreamThread())
{
return game::native::FS_THREAD_STREAM;
}
if (game::native::Sys_IsRenderThread())
{
return game::native::FS_THREAD_BACKEND;
}
if (game::native::Sys_IsServerThread())
{
return game::native::FS_THREAD_SERVER;
}
return game::native::FS_THREAD_INVALID;
}
static void* handle_for_file_current_thread()
{
return game::native::FS_HandleForFile(get_current_thread());
}
static int open_file_append(const char* filename)
{
char ospath[MAX_PATH]{};
game::native::FS_CheckFileSystemStarted();
const auto* basepath = (*fs_homepath)->current.string;
build_os_path_for_thread(basepath, game::native::fs_gamedir, filename, ospath, game::native::FS_THREAD_MAIN);
if ((*fs_debug)->current.integer)
{
printf("FS_FOpenFileAppend: %s\n", ospath);
}
if (game::native::FS_CreatePath(ospath))
{
return 0;
}
auto* f = file_open_append_text(ospath);
if (!f)
{
return 0;
}
auto h = reinterpret_cast<int>(handle_for_file_current_thread());
game::native::fsh[h].zipFile = nullptr;
strncpy_s(game::native::fsh[h].name, filename, _TRUNCATE);
game::native::fsh[h].handleFiles.file.o = f;
game::native::fsh[h].handleSync = 0;
if (!game::native::fsh[h].handleFiles.file.o)
{
game::native::FS_FCloseFile(h);
h = 0;
}
return h;
}
static const char* sys_default_install_path_stub()
{
static auto current_path = std::filesystem::current_path().string();
return current_path.data();
}
int file_system::open_file_by_mode(const char* qpath, int* f, game::native::fsMode_t mode)
{
auto r = 6969;
auto sync = 0;
switch (mode)
{
case game::native::FS_READ:
*game::native::com_fileAccessed = TRUE;
r = game::native::FS_FOpenFileReadForThread(qpath, f, game::native::FS_THREAD_MAIN);
break;
case game::native::FS_APPEND_SYNC:
sync = 1;
case game::native::FS_APPEND:
*f = open_file_append(qpath);
r = 0;
if (!*f )
{
r = -1;
}
break;
default:
game::native::Com_Error(game::native::ERR_FATAL, "\x15" "FSH_FOpenFile: bad mode");
break;
}
if (!f)
{
return r;
}
if (*f)
{
game::native::fsh[*f].fileSize = r;
game::native::fsh[*f].streamed = 0;
}
game::native::fsh[*f].handleSync = sync;
return r;
}
int file_system::write(const char* buffer, int len, int h)
{
game::native::FS_CheckFileSystemStarted();
if (!h)
{
return 0;
}
auto* f = file_for_handle(h);
auto* buf = const_cast<char*>(buffer);
auto remaining = len;
auto tries = 0;
while (remaining)
{
auto block = remaining;
auto written = static_cast<int>(file_write(buf, block, f));
if (!written)
{
if (tries)
{
return 0;
}
tries = 1;
}
if (written == -1)
{
return 0;
}
remaining -= written;
buf += written;
}
if (game::native::fsh[h].handleSync)
{
std::fflush(f);
}
return len;
}
void file_system::post_load()
{
fs_homepath = reinterpret_cast<const game::native::dvar_t**>(
SELECT_VALUE(0x1C2B538, 0x59ADD18, 0x585E8F0));
fs_debug = reinterpret_cast<const game::native::dvar_t**>(
SELECT_VALUE(0x1C2B32C, 0x59A9A08, 0x585A5E4));
// Make open-iw5 work outside of the game directory
sys_default_install_path_hook.create(SELECT_VALUE(0x487E50, 0x5C4A80, 0x535F80), &sys_default_install_path_stub);
}
REGISTER_MODULE(file_system)

View File

@ -0,0 +1,10 @@
#pragma once
class file_system final : public module
{
public:
void post_load() override;
static int open_file_by_mode(const char* qpath, int* f, game::native::fsMode_t mode);
static int write(const char* buffer, int len, int h);
};

138
src/module/game_log.cpp Normal file
View File

@ -0,0 +1,138 @@
#include <std_include.hpp>
#include <loader/module_loader.hpp>
#include <utils/hook.hpp>
#include "game/game.hpp"
#include "game_log.hpp"
#include "scheduler.hpp"
#include "file_system.hpp"
const game::native::dvar_t* game_log::g_log;
const game::native::dvar_t* game_log::g_logSync;
int game_log::log_file = 0;
void game_log::g_log_printf(const char* fmt, ...)
{
char buf[1024] = {0};
char out[1024] = {0};
va_list va;
va_start(va, fmt);
vsnprintf_s(buf, _TRUNCATE, fmt, va);
va_end(va);
if (!log_file)
{
return;
}
const auto time = game::native::level->time / 1000;
const auto len = sprintf_s(out, "%3i:%i%i %s", time / 60, time % 60 / 10, time % 60 % 10, buf);
file_system::write(out, len, log_file);
}
void game_log::gscr_log_print()
{
char buf[1024] = {0};
std::size_t out_chars = 0;
for (std::size_t i = 0; i < game::native::Scr_GetNumParam(); ++i)
{
const auto* value = game::native::Scr_GetString(i);
const auto len = std::strlen(value);
out_chars += len;
if (out_chars >= sizeof(buf))
{
// Do not overflow the buffer
break;
}
strncat_s(buf, value, _TRUNCATE);
}
g_log_printf("%s", buf);
}
void game_log::g_init_game_stub()
{
printf("------- Game Initialization -------\n");
printf("gamename: %s\n", reinterpret_cast<const char*>(0x7FFC68));
printf("gamedate: %s\n", __DATE__);
const auto* log = g_log->current.string;
if (*log == '\0')
{
printf("Not logging to disk.\n");
}
else
{
file_system::open_file_by_mode(log, &log_file, game::native::FS_APPEND_SYNC);
if (!log_file)
{
printf("WARNING: Couldn't open logfile: %s\n", log);
}
else
{
printf("Logging to disk: '%s'.\n", log);
g_log_printf("------------------------------------------------------------\n");
g_log_printf("InitGame\n");
}
}
utils::hook::invoke<void>(0x5C2800);
}
void game_log::g_shutdown_game_stub(int free_scripts)
{
printf("==== ShutdownGame (%d) ====\n", free_scripts);
if (log_file)
{
g_log_printf("ShutdownGame:\n");
g_log_printf("------------------------------------------------------------\n");
game::native::FS_FCloseFile(log_file);
log_file = 0;
}
utils::hook::invoke<void>(0x50C100, free_scripts);
}
void game_log::exit_level_stub()
{
g_log_printf("ExitLevel: executed\n");
}
void game_log::post_load()
{
if (!game::is_mp())
{
return;
}
utils::hook::set<void(*)()>(0x8AC858, gscr_log_print);
utils::hook(0x50D135, g_init_game_stub, HOOK_CALL).install()->quick();
utils::hook(0x573C82, g_shutdown_game_stub, HOOK_CALL).install()->quick();
utils::hook(0x573D3A, g_shutdown_game_stub, HOOK_CALL).install()->quick();
utils::hook(0x50D5F4, exit_level_stub, HOOK_JUMP).install()->quick();
scheduler::once([]
{
g_log = game::native::Dvar_RegisterString("g_log", "games_mp.log",
game::native::DVAR_ARCHIVE, "Log file name");
g_logSync = game::native::Dvar_RegisterBool("g_logSync", false,
game::native::DVAR_NONE, "Enable synchronous logging");
}, scheduler::pipeline::main);
}
REGISTER_MODULE(game_log)

25
src/module/game_log.hpp Normal file
View File

@ -0,0 +1,25 @@
#pragma once
class game_log final : public module
{
public:
static_assert(offsetof(game::native::level_locals_t, time) == 0x4A8);
void post_load() override;
static void g_log_printf(const char* fmt, ...);
private:
static const game::native::dvar_t* g_log;
static const game::native::dvar_t* g_logSync;
static int log_file;
static void gscr_log_print();
static void g_init_game_stub();
static void g_shutdown_game_stub(int free_scripts);
static void exit_level_stub();
};

View File

@ -1,140 +1,94 @@
#include <std_include.hpp>
#include <loader/module_loader.hpp>
#include <utils/hook.hpp>
#include "game/game.hpp"
#include "game/engine/scoped_critical_section.hpp"
#include "log_file.hpp"
#include "scheduler.hpp"
#include "file_system.hpp"
const game::native::dvar_t* log_file::g_log;
const game::native::dvar_t* log_file::g_logSync;
const char* log_file::log_file_name;
FILE* log_file::game_log_fsh = nullptr;
int log_file::opening_qconsole = 0;
int log_file::com_console_log_open_failed = 0;
void log_file::g_log_printf(const char* fmt, ...)
const game::native::dvar_t* log_file::com_logfile;
void log_file::com_open_log_file()
{
char buf[1024] = {0};
char out[1024] = {0};
time_t aclock;
char time_buffer[32]{};
va_list va;
va_start(va, fmt);
vsnprintf_s(buf, _TRUNCATE, fmt, va);
va_end(va);
if (game::native::Sys_IsMainThread() && !opening_qconsole)
{
opening_qconsole = 1;
tm new_time{};
if (game_log_fsh == nullptr)
_time64(&aclock);
_localtime64_s(&new_time, &aclock);
file_system::open_file_by_mode(log_file_name, game::native::logfile, game::native::FS_APPEND_SYNC);
asctime_s(time_buffer, sizeof(time_buffer), &new_time);
info("logfile opened on %s\n", time_buffer);
opening_qconsole = 0;
com_console_log_open_failed = *game::native::logfile == 0;
}
}
void log_file::com_log_print_message(const char* msg)
{
char print_buffer[0x40]{};
game::engine::scoped_critical_section crit_sect_lock(game::native::CRITSECT_CONSOLE, game::native::SCOPED_CRITSECT_NORMAL);
if (!game::native::FS_Initialized())
{
return;
}
_snprintf_s(out, _TRUNCATE, "%3i:%i%i %s",
game::native::level->time / 1000 / 60,
game::native::level->time / 1000 % 60 / 10,
game::native::level->time / 1000 % 60 % 10,
buf);
fprintf(game_log_fsh, "%s", out);
fflush(game_log_fsh);
}
void log_file::gscr_log_print()
{
char buf[1024] = {0};
std::size_t out_chars = 0;
for (std::size_t i = 0; i < game::native::Scr_GetNumParam(); ++i)
if (!*game::native::logfile)
{
const auto* value = game::native::Scr_GetString(i);
const auto len = std::strlen(value);
out_chars += len;
if (out_chars >= sizeof(buf))
{
// Do not overflow the buffer
break;
}
strncat_s(buf, value, _TRUNCATE);
com_open_log_file();
}
g_log_printf("%s", buf);
}
void log_file::g_init_game_stub()
{
printf("------- Game Initialization -------\n");
printf("gamename: %s\n", reinterpret_cast<const char*>(0x7FFC68));
printf("gamedate: %s\n", __DATE__);
const auto* log = g_log->current.string;
if (*log == '\0')
if (*game::native::logfile)
{
printf("Not logging to disk.\n");
}
else
{
game_log_fsh = _fsopen(log, "a", _SH_DENYWR);
if (game_log_fsh == nullptr)
static auto log_next_time_stamp = true;
if (log_next_time_stamp)
{
printf("WARNING: Couldn't open logfile: %s\n", log);
const auto len = sprintf_s(print_buffer, "[%10i] ", game::native::Sys_Milliseconds());
file_system::write(print_buffer, len, *game::native::logfile);
}
else
{
printf("Logging to disk: '%s'.\n", log);
g_log_printf("------------------------------------------------------------\n");
g_log_printf("InitGame\n");
}
}
utils::hook::invoke<void>(0x5C2800);
}
void log_file::g_shutdown_game_stub(int free_scripts)
{
printf("==== ShutdownGame (%d) ====\n", free_scripts);
if (game_log_fsh != nullptr)
{
g_log_printf("ShutdownGame:\n");
g_log_printf("------------------------------------------------------------\n");
fclose(game_log_fsh);
game_log_fsh = nullptr;
log_next_time_stamp = std::strchr(msg, 10) != nullptr;
file_system::write(msg, static_cast<int>(std::strlen(msg)), *game::native::logfile);
}
utils::hook::invoke<void>(0x50C100, free_scripts);
}
void log_file::exit_level_stub()
void log_file::info(const char* fmt, ...)
{
g_log_printf("ExitLevel: executed\n");
char msg[0x1000]{};
va_list argptr;
va_start(argptr, fmt);
vsnprintf_s(msg, _TRUNCATE, fmt, argptr);
va_end(argptr);
if (com_logfile && com_logfile->current.integer)
{
com_log_print_message(msg);
}
}
void log_file::post_load()
{
if (!game::is_mp())
{
return;
}
// The game closes the logfile handle in Com_Quit_f
utils::hook::set<void(*)()>(0x8AC858, gscr_log_print);
com_logfile = game::native::Dvar_RegisterInt("logfile", 1,
0, 2, 0, "Write to log file - 0 = disabled, 1 = async file write, 2 = Sync every write");
utils::hook(0x50D135, g_init_game_stub, HOOK_CALL).install()->quick();
utils::hook(0x573C82, g_shutdown_game_stub, HOOK_CALL).install()->quick();
utils::hook(0x573D3A, g_shutdown_game_stub, HOOK_CALL).install()->quick();
utils::hook(0x50D5F4, exit_level_stub, HOOK_JUMP).install()->quick();
scheduler::once([]
{
g_log = game::native::Dvar_RegisterString("g_log", "games_mp.log",
game::native::DVAR_ARCHIVE, "Log file name");
g_logSync = game::native::Dvar_RegisterBool("g_logSync", false,
game::native::DVAR_NONE, "Enable synchronous logging");
}, scheduler::pipeline::main);
log_file_name = SELECT_VALUE("console_sp.log", "console_mp.log", "console_mp_dedicated.log");
}
REGISTER_MODULE(log_file)

View File

@ -3,23 +3,19 @@
class log_file final : public module
{
public:
static_assert(offsetof(game::native::level_locals_t, time) == 0x4A8);
void post_load() override;
static void g_log_printf(const char* fmt, ...);
static void info(const char* fmt, ...);
private:
static const game::native::dvar_t* g_log;
static const game::native::dvar_t* g_logSync;
static const char* log_file_name;
static FILE* game_log_fsh;
static int opening_qconsole;
static int com_console_log_open_failed;
static void gscr_log_print();
static const game::native::dvar_t* com_logfile;
static void g_init_game_stub();
static void com_open_log_file();
static void g_shutdown_game_stub(int free_scripts);
static void exit_level_stub();
static void com_log_print_message(const char* msg);
};