Restore gamelog and consolelog properly
This commit is contained in:
parent
ac12467a49
commit
b706e21381
84
src/game/engine/scoped_critical_section.cpp
Normal file
84
src/game/engine/scoped_critical_section.cpp
Normal 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_;
|
||||
}
|
||||
}
|
23
src/game/engine/scoped_critical_section.hpp
Normal file
23
src/game/engine/scoped_critical_section.hpp
Normal 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_;
|
||||
};
|
||||
}
|
@ -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));
|
||||
}
|
||||
}
|
||||
|
@ -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();
|
||||
|
@ -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
|
||||
|
@ -57,5 +57,5 @@ private:
|
||||
#define REGISTER_MODULE(name) \
|
||||
namespace \
|
||||
{ \
|
||||
static module_loader::installer<name> $_##name; \
|
||||
static module_loader::installer<name> __module; \
|
||||
}
|
||||
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -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
273
src/module/file_system.cpp
Normal 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)
|
10
src/module/file_system.hpp
Normal file
10
src/module/file_system.hpp
Normal 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
138
src/module/game_log.cpp
Normal 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
25
src/module/game_log.hpp
Normal 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();
|
||||
};
|
@ -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)
|
||||
|
@ -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);
|
||||
};
|
||||
|
Loading…
Reference in New Issue
Block a user