// engine_stubs.cpp // #include "precompiled.h" #include #include #include class idCommonStub : public idCommon { public: idCommonStub(); virtual ~idCommonStub(); virtual void Init(int argc, const char** argv, const char* cmdline); virtual void Shutdown(void); virtual void Quit(void); virtual bool IsInitialized(void) const; virtual void Frame(void); virtual void GUIFrame(bool execCmd, bool network); virtual void StartupVariable(const char* match, bool once); #ifdef QUAKE4 virtual int GetUserCmdHz(void) const; virtual int GetUserCmdMSec(void) const; virtual int GetFrameTime(void) const; virtual bool IsRenderableGameFrame(void) const; virtual void SetRenderableGameFrame(bool in); virtual const char* GetErrorMessage(void) const; virtual bool IsToolActive(void) const; #ifdef _RV_MEM_SYS_SUPPORT virtual void InitHeaps(void); virtual void ShutdownHeaps(void); #endif virtual void RunAlwaysThinkGUIs(int time); virtual bool DoingDeclValidation(void); virtual void SetCrashReportAutoSendString(const char* psString); #endif virtual void InitTool(const toolFlag_t tool, const idDict* dict); virtual void ActivateTool(bool active); virtual void WriteConfigToFile(const char* filename); virtual void WriteFlaggedCVarsToFile(const char* filename, int flags, const char* setCmd); #ifdef QUAKE4 virtual void BeginRedirect(char* buffer, int buffersize, void (*flush)(const char*), bool rcon = false); #else virtual void BeginRedirect(char* buffer, int buffersize, void (*flush)(const char*)); #endif virtual void EndRedirect(void); virtual void SetRefreshOnPrint(bool set); virtual void Printf(const char* fmt, ...) id_attribute((format(printf, 2, 3))); virtual void VPrintf(const char* fmt, va_list arg); virtual void DPrintf(const char* fmt, ...) id_attribute((format(printf, 2, 3))); virtual void Warning(const char* fmt, ...) id_attribute((format(printf, 2, 3))); virtual void DWarning(const char* fmt, ...) id_attribute((format(printf, 2, 3))); virtual void PrintWarnings(void); virtual void ClearWarnings(const char* reason); virtual void Error(const char* fmt, ...) id_attribute((format(printf, 2, 3))); virtual void FatalError(const char* fmt, ...) id_attribute((format(printf, 2, 3))); #ifdef QUAKE4 virtual void DumpWarnings(void); #endif virtual const idLangDict* GetLanguageDict(void); #ifdef QUAKE4 virtual const char* GetLocalizedString(const char* token, int langIndex = -1); virtual const idLangKeyValue* GetLocalizedString(int index, int langIndex = -1); virtual int GetNumLanguages(void) const; virtual int GetNumLocalizedStrings(void) const; virtual const char* GetLanguage(int index) const; virtual bool LanguageHasVO(int index) const; #endif virtual const char* KeysFromBinding(const char* bind); virtual const char* BindingFromKey(const char* key); virtual int ButtonState(int key); virtual int KeyState(int key); #ifdef QUAKE4 virtual int GetRModeForMachineSpec(int machineSpec) const; virtual void SetDesiredMachineSpec(int machineSpec); virtual bool IsRCon(void) const; #endif #ifdef PREY virtual void MaterialKeyForBinding(const char* binding, char* materialName, char* keyText, bool* wideKey); #endif virtual float Get_com_engineHz_latched(void); virtual int64_t Get_com_engineHz_numerator(void); virtual int64_t Get_com_engineHz_denominator(void); private: void OutputString(const char* prefix, const char* text); void VOutputf(const char* prefix, const char* fmt, va_list args); private: bool initialized; bool quitRequested; bool refreshOnPrint; #ifdef QUAKE4 bool renderableGameFrame; bool toolActive; char errorMessage[4096]; #endif char* redirectBuffer; int redirectBufferSize; int redirectBufferUsed; void (*redirectFlush)(const char*); }; idCommonStub commonLocal; idCommon* common = &commonLocal; idCommonStub::idCommonStub() { initialized = false; quitRequested = false; refreshOnPrint = false; #ifdef QUAKE4 renderableGameFrame = true; toolActive = false; errorMessage[0] = '\0'; #endif redirectBuffer = NULL; redirectBufferSize = 0; redirectBufferUsed = 0; redirectFlush = NULL; } idCommonStub::~idCommonStub() { Shutdown(); } void idCommonStub::OutputString(const char* prefix, const char* text) { if (!text) { text = ""; } char finalBuffer[8192]; if (prefix && prefix[0]) { idStr::snPrintf(finalBuffer, sizeof(finalBuffer), "%s%s", prefix, text); } else { idStr::snPrintf(finalBuffer, sizeof(finalBuffer), "%s", text); } finalBuffer[sizeof(finalBuffer) - 1] = '\0'; if (redirectBuffer && redirectBufferSize > 0) { const int len = (int)strlen(finalBuffer); const int remaining = redirectBufferSize - redirectBufferUsed - 1; if (remaining > 0) { const int copyLen = len < remaining ? len : remaining; memcpy(redirectBuffer + redirectBufferUsed, finalBuffer, copyLen); redirectBufferUsed += copyLen; redirectBuffer[redirectBufferUsed] = '\0'; } if (redirectFlush) { redirectFlush(finalBuffer); } } fputs(finalBuffer, stdout); fflush(stdout); #ifdef _WIN32 OutputDebugStringA(finalBuffer); #endif } void idCommonStub::VOutputf(const char* prefix, const char* fmt, va_list args) { if (!fmt) { OutputString(prefix, "\n"); return; } char buffer[8192]; idStr::vsnPrintf(buffer, sizeof(buffer), fmt, args); buffer[sizeof(buffer) - 1] = '\0'; OutputString(prefix, buffer); } void idCommonStub::Init(int argc, const char** argv, const char* cmdline) { initialized = true; quitRequested = false; Printf("idCommonStub::Init argc=%d cmdline=\"%s\"\n", argc, cmdline ? cmdline : ""); } void idCommonStub::Shutdown(void) { if (initialized) { Printf("idCommonStub::Shutdown\n"); } initialized = false; } void idCommonStub::Quit(void) { quitRequested = true; Printf("idCommonStub::Quit\n"); } bool idCommonStub::IsInitialized(void) const { return initialized; } void idCommonStub::Frame(void) { // Stub. } void idCommonStub::GUIFrame(bool execCmd, bool network) { // Stub. } void idCommonStub::StartupVariable(const char* match, bool once) { // Stub. } #ifdef QUAKE4 int idCommonStub::GetUserCmdHz(void) const { return 60; } int idCommonStub::GetUserCmdMSec(void) const { return 16; } int idCommonStub::GetFrameTime(void) const { return 16; } bool idCommonStub::IsRenderableGameFrame(void) const { return renderableGameFrame; } void idCommonStub::SetRenderableGameFrame(bool in) { renderableGameFrame = in; } const char* idCommonStub::GetErrorMessage(void) const { return errorMessage; } bool idCommonStub::IsToolActive(void) const { return toolActive; } #ifdef _RV_MEM_SYS_SUPPORT void idCommonStub::InitHeaps(void) { // Stub. } void idCommonStub::ShutdownHeaps(void) { // Stub. } #endif void idCommonStub::RunAlwaysThinkGUIs(int time) { // Stub. } bool idCommonStub::DoingDeclValidation(void) { return false; } void idCommonStub::SetCrashReportAutoSendString(const char* psString) { // Stub. } #endif void idCommonStub::InitTool(const toolFlag_t tool, const idDict* dict) { #ifdef QUAKE4 toolActive = true; #endif Printf("idCommonStub::InitTool\n"); } void idCommonStub::ActivateTool(bool active) { #ifdef QUAKE4 toolActive = active; #endif Printf("idCommonStub::ActivateTool active=%d\n", active ? 1 : 0); } void idCommonStub::WriteConfigToFile(const char* filename) { Printf("idCommonStub::WriteConfigToFile \"%s\"\n", filename ? filename : ""); } void idCommonStub::WriteFlaggedCVarsToFile(const char* filename, int flags, const char* setCmd) { Printf("idCommonStub::WriteFlaggedCVarsToFile \"%s\" flags=%d setCmd=\"%s\"\n", filename ? filename : "", flags, setCmd ? setCmd : ""); } #ifdef QUAKE4 void idCommonStub::BeginRedirect(char* buffer, int buffersize, void (*flush)(const char*), bool rcon) { #else void idCommonStub::BeginRedirect(char* buffer, int buffersize, void (*flush)(const char*)) { #endif redirectBuffer = buffer; redirectBufferSize = buffersize; redirectBufferUsed = 0; redirectFlush = flush; if (redirectBuffer && redirectBufferSize > 0) { redirectBuffer[0] = '\0'; } #ifdef QUAKE4 Printf("idCommonStub::BeginRedirect rcon=%d\n", rcon ? 1 : 0); #else Printf("idCommonStub::BeginRedirect\n"); #endif } void idCommonStub::EndRedirect(void) { if (redirectFlush && redirectBuffer) { redirectFlush(redirectBuffer); } redirectBuffer = NULL; redirectBufferSize = 0; redirectBufferUsed = 0; redirectFlush = NULL; } void idCommonStub::SetRefreshOnPrint(bool set) { refreshOnPrint = set; } void idCommonStub::Printf(const char* fmt, ...) { va_list args; va_start(args, fmt); VPrintf(fmt, args); va_end(args); } void idCommonStub::VPrintf(const char* fmt, va_list arg) { VOutputf("", fmt, arg); } void idCommonStub::DPrintf(const char* fmt, ...) { va_list args; va_start(args, fmt); VOutputf("DEBUG: ", fmt, args); va_end(args); } void idCommonStub::Warning(const char* fmt, ...) { va_list args; va_start(args, fmt); VOutputf("WARNING: ", fmt, args); va_end(args); } void idCommonStub::DWarning(const char* fmt, ...) { va_list args; va_start(args, fmt); VOutputf("DWARNING: ", fmt, args); va_end(args); } void idCommonStub::PrintWarnings(void) { Printf("idCommonStub::PrintWarnings: no queued warnings.\n"); } void idCommonStub::ClearWarnings(const char* reason) { Printf("idCommonStub::ClearWarnings reason=\"%s\"\n", reason ? reason : ""); } void idCommonStub::Error(const char* fmt, ...) { va_list args; va_start(args, fmt); VOutputf("ERROR: ", fmt, args); va_end(args); #ifdef QUAKE4 va_list args2; va_start(args2, fmt); idStr::vsnPrintf(errorMessage, sizeof(errorMessage), fmt ? fmt : "", args2); errorMessage[sizeof(errorMessage) - 1] = '\0'; va_end(args2); #endif // Stub behavior: do not throw unless you want this to behave like real common. // throw idException( "idCommonStub::Error" ); } void idCommonStub::FatalError(const char* fmt, ...) { va_list args; va_start(args, fmt); VOutputf("FATAL: ", fmt, args); va_end(args); #ifdef QUAKE4 va_list args2; va_start(args2, fmt); idStr::vsnPrintf(errorMessage, sizeof(errorMessage), fmt ? fmt : "", args2); errorMessage[sizeof(errorMessage) - 1] = '\0'; va_end(args2); #endif #ifdef _WIN32 MessageBeep(MB_ICONERROR); #endif // Stub behavior: do not exit unless you want this to behave like real common. // Sys_Quit(); } #ifdef QUAKE4 void idCommonStub::DumpWarnings(void) { Printf("idCommonStub::DumpWarnings: no queued warnings.\n"); } #endif const idLangDict* idCommonStub::GetLanguageDict(void) { return NULL; } #ifdef QUAKE4 const char* idCommonStub::GetLocalizedString(const char* token, int langIndex) { return token ? token : ""; } const idLangKeyValue* idCommonStub::GetLocalizedString(int index, int langIndex) { return NULL; } int idCommonStub::GetNumLanguages(void) const { return 1; } int idCommonStub::GetNumLocalizedStrings(void) const { return 0; } const char* idCommonStub::GetLanguage(int index) const { return "english"; } bool idCommonStub::LanguageHasVO(int index) const { return false; } #endif const char* idCommonStub::KeysFromBinding(const char* bind) { return ""; } const char* idCommonStub::BindingFromKey(const char* key) { return ""; } int idCommonStub::ButtonState(int key) { return 0; } int idCommonStub::KeyState(int key) { return 0; } #ifdef QUAKE4 int idCommonStub::GetRModeForMachineSpec(int machineSpec) const { return -1; } void idCommonStub::SetDesiredMachineSpec(int machineSpec) { Printf("idCommonStub::SetDesiredMachineSpec %d\n", machineSpec); } bool idCommonStub::IsRCon(void) const { return false; } #endif #ifdef PREY void idCommonStub::MaterialKeyForBinding(const char* binding, char* materialName, char* keyText, bool* wideKey) { if (materialName) { materialName[0] = '\0'; } if (keyText) { keyText[0] = '\0'; } if (wideKey) { *wideKey = false; } } #endif float idCommonStub::Get_com_engineHz_latched(void) { return 60.0f; } int64_t idCommonStub::Get_com_engineHz_numerator(void) { return 60; } int64_t idCommonStub::Get_com_engineHz_denominator(void) { return 1; } class idDeclManagerStub : public idDeclManager { public: idDeclManagerStub() { initialized = false; checksum = 0; } virtual ~idDeclManagerStub(void) { Shutdown(); } private: bool initialized; int checksum; void StubPrint(const char* fmt, ...) const { char buffer[4096]; va_list args; va_start(args, fmt); #if defined(_WIN32) _vsnprintf_s(buffer, sizeof(buffer), _TRUNCATE, fmt ? fmt : "", args); #else vsnprintf(buffer, sizeof(buffer), fmt ? fmt : "", args); buffer[sizeof(buffer) - 1] = '\0'; #endif va_end(args); if (common) { common->Printf("%s", buffer); } else { fputs(buffer, stdout); fflush(stdout); #ifdef _WIN32 OutputDebugStringA(buffer); #endif } } public: virtual void Init(void) { initialized = true; StubPrint("idDeclManagerStub::Init\n"); } virtual void Shutdown(void) { if (initialized) { StubPrint("idDeclManagerStub::Shutdown\n"); } initialized = false; } virtual void Reload(bool force) { StubPrint("idDeclManagerStub::Reload force=%d\n", force ? 1 : 0); } virtual void BeginLevelLoad(void) { StubPrint("idDeclManagerStub::BeginLevelLoad\n"); } virtual void EndLevelLoad(void) { StubPrint("idDeclManagerStub::EndLevelLoad\n"); } virtual void RegisterDeclType(const char* typeName, declType_t type, idDecl* (*allocator)(void)) { StubPrint("idDeclManagerStub::RegisterDeclType typeName=\"%s\" type=%d\n", typeName ? typeName : "", (int)type); } virtual void RegisterDeclFolder(const char* folder, const char* extension, declType_t defaultType) { StubPrint( "idDeclManagerStub::RegisterDeclFolder folder=\"%s\" extension=\"%s\" defaultType=%d\n", folder ? folder : "", extension ? extension : "", (int)defaultType ); } virtual int GetChecksum(void) const { return checksum; } virtual int GetNumDeclTypes(void) const { return 0; } virtual const char* GetDeclNameFromType(declType_t type) const { return ""; } virtual declType_t GetDeclTypeFromName(const char* typeName) const { return DECL_MAX_TYPES; } virtual const idDecl* FindType(declType_t type, const char* name, bool makeDefault = true) { StubPrint( "idDeclManagerStub::FindType type=%d name=\"%s\" makeDefault=%d\n", (int)type, name ? name : "", makeDefault ? 1 : 0 ); return NULL; } #ifdef QUAKE4 virtual const idDecl* FindType(declType_t type, const char* name, bool makeDefault, bool noCaching) { StubPrint( "idDeclManagerStub::FindType type=%d name=\"%s\" makeDefault=%d noCaching=%d\n", (int)type, name ? name : "", makeDefault ? 1 : 0, noCaching ? 1 : 0 ); return FindType(type, name, makeDefault); } #endif virtual const idDecl* FindDeclWithoutParsing(declType_t type, const char* name, bool makeDefault = true) { StubPrint( "idDeclManagerStub::FindDeclWithoutParsing type=%d name=\"%s\" makeDefault=%d\n", (int)type, name ? name : "", makeDefault ? 1 : 0 ); return NULL; } virtual void ReloadFile(const char* filename, bool force) { StubPrint( "idDeclManagerStub::ReloadFile filename=\"%s\" force=%d\n", filename ? filename : "", force ? 1 : 0 ); } virtual int GetNumDecls(declType_t type) { return 0; } virtual const idDecl* DeclByIndex(declType_t type, int index, bool forceParse = true) { StubPrint( "idDeclManagerStub::DeclByIndex type=%d index=%d forceParse=%d\n", (int)type, index, forceParse ? 1 : 0 ); return NULL; } virtual void ListType(const idCmdArgs& args, declType_t type) { StubPrint("idDeclManagerStub::ListType type=%d\n", (int)type); } virtual void PrintType(const idCmdArgs& args, declType_t type) { StubPrint("idDeclManagerStub::PrintType type=%d\n", (int)type); } virtual bool CreateDeclFile(const char* fileName, declType_t defaultType) { StubPrint( "idDeclManagerStub::CreateDeclFile fileName=\"%s\" defaultType=%d\n", fileName ? fileName : "", (int)defaultType ); return false; } virtual idDecl* CreateNewDecl(declType_t type, const char* name, const char* fileName, bool save = true) { StubPrint( "idDeclManagerStub::CreateNewDecl type=%d name=\"%s\" fileName=\"%s\" save=%d\n", (int)type, name ? name : "", fileName ? fileName : "", save ? 1 : 0 ); return NULL; } virtual bool SetDeclText(declType_t type, const char* name, const char* text, bool parse = true, bool save = false) { StubPrint( "idDeclManagerStub::SetDeclText type=%d name=\"%s\" parse=%d save=%d\n", (int)type, name ? name : "", parse ? 1 : 0, save ? 1 : 0 ); return false; } virtual bool SaveDecl(declType_t type, const char* name) { StubPrint( "idDeclManagerStub::SaveDecl type=%d name=\"%s\"\n", (int)type, name ? name : "" ); return false; } virtual bool DeleteDecl(declType_t type, const char* name, bool deleteSourceText = true) { StubPrint( "idDeclManagerStub::DeleteDecl type=%d name=\"%s\" deleteSourceText=%d\n", (int)type, name ? name : "", deleteSourceText ? 1 : 0 ); return false; } virtual bool RenameDecl(declType_t type, const char* oldName, const char* newName) { StubPrint( "idDeclManagerStub::RenameDecl type=%d oldName=\"%s\" newName=\"%s\"\n", (int)type, oldName ? oldName : "", newName ? newName : "" ); return false; } virtual void MediaPrint(const char* fmt, ...) id_attribute((format(printf, 2, 3))) { char buffer[4096]; va_list args; va_start(args, fmt); #if defined(_WIN32) _vsnprintf_s(buffer, sizeof(buffer), _TRUNCATE, fmt ? fmt : "", args); #else vsnprintf(buffer, sizeof(buffer), fmt ? fmt : "", args); buffer[sizeof(buffer) - 1] = '\0'; #endif va_end(args); StubPrint("MEDIA: %s", buffer); } virtual void WritePrecacheCommands(idFile* f) { StubPrint("idDeclManagerStub::WritePrecacheCommands\n"); } #ifdef QUAKE4 virtual void ParseGuides(void) { StubPrint("idDeclManagerStub::ParseGuides\n"); } virtual void ShutdownGuides(void) { StubPrint("idDeclManagerStub::ShutdownGuides\n"); } virtual bool EvaluateGuide(idStr& name, idLexer* src, idStr& definition) { StubPrint("idDeclManagerStub::EvaluateGuide name=\"%s\"\n", name.c_str()); return false; } virtual bool EvaluateInlineGuide(idStr& name, idStr& definition) { StubPrint("idDeclManagerStub::EvaluateInlineGuide name=\"%s\"\n", name.c_str()); return false; } #endif virtual const idMaterial* FindMaterial(const char* name, bool makeDefault = true) { StubPrint( "idDeclManagerStub::FindMaterial name=\"%s\" makeDefault=%d\n", name ? name : "", makeDefault ? 1 : 0 ); return NULL; } virtual const idDeclSkin* FindSkin(const char* name, bool makeDefault = true) { StubPrint( "idDeclManagerStub::FindSkin name=\"%s\" makeDefault=%d\n", name ? name : "", makeDefault ? 1 : 0 ); return NULL; } virtual const idSoundShader* FindSound(const char* name, bool makeDefault = true) { StubPrint( "idDeclManagerStub::FindSound name=\"%s\" makeDefault=%d\n", name ? name : "", makeDefault ? 1 : 0 ); return NULL; } virtual const idMaterial* MaterialByIndex(int index, bool forceParse = true) { StubPrint( "idDeclManagerStub::MaterialByIndex index=%d forceParse=%d\n", index, forceParse ? 1 : 0 ); return NULL; } virtual const idDeclSkin* SkinByIndex(int index, bool forceParse = true) { StubPrint( "idDeclManagerStub::SkinByIndex index=%d forceParse=%d\n", index, forceParse ? 1 : 0 ); return NULL; } virtual const idSoundShader* SoundByIndex(int index, bool forceParse = true) { StubPrint( "idDeclManagerStub::SoundByIndex index=%d forceParse=%d\n", index, forceParse ? 1 : 0 ); return NULL; } virtual const class idDeclTable* FindTable(const char* name, bool makeDefault = true) { StubPrint( "idDeclManagerStub::FindTable name=\"%s\" makeDefault=%d\n", name ? name : "", makeDefault ? 1 : 0 ); return NULL; } #ifdef QUAKE4 virtual const class rvDeclMatType* FindMaterialType(const char* name, bool makeDefault = true) { StubPrint( "idDeclManagerStub::FindMaterialType name=\"%s\" makeDefault=%d\n", name ? name : "", makeDefault ? 1 : 0 ); return NULL; } virtual const class rvDeclLipSync* FindLipSync(const char* name, bool makeDefault = true) { StubPrint( "idDeclManagerStub::FindLipSync name=\"%s\" makeDefault=%d\n", name ? name : "", makeDefault ? 1 : 0 ); return NULL; } virtual const class rvDeclPlayback* FindPlayback(const char* name, bool makeDefault = true) { StubPrint( "idDeclManagerStub::FindPlayback name=\"%s\" makeDefault=%d\n", name ? name : "", makeDefault ? 1 : 0 ); return NULL; } virtual const class rvDeclEffect* FindEffect(const char* name, bool makeDefault = true) { StubPrint( "idDeclManagerStub::FindEffect name=\"%s\" makeDefault=%d\n", name ? name : "", makeDefault ? 1 : 0 ); return NULL; } virtual const class rvDeclMatType* MaterialTypeByIndex(int index, bool forceParse = true) { StubPrint( "idDeclManagerStub::MaterialTypeByIndex index=%d forceParse=%d\n", index, forceParse ? 1 : 0 ); return NULL; } virtual const class rvDeclLipSync* LipSyncByIndex(int index, bool forceParse = true) { StubPrint( "idDeclManagerStub::LipSyncByIndex index=%d forceParse=%d\n", index, forceParse ? 1 : 0 ); return NULL; } virtual const class rvDeclPlayback* PlaybackByIndex(int index, bool forceParse = true) { StubPrint( "idDeclManagerStub::PlaybackByIndex index=%d forceParse=%d\n", index, forceParse ? 1 : 0 ); return NULL; } virtual const class rvDeclEffect* EffectByIndex(int index, bool forceParse = true) { StubPrint( "idDeclManagerStub::EffectByIndex index=%d forceParse=%d\n", index, forceParse ? 1 : 0 ); return NULL; } #endif }; idDeclManagerStub declManagerLocal; idDeclManager* declManager = &declManagerLocal; #ifndef MAX_OSPATH #define MAX_OSPATH 256 #endif #ifndef MAX_SYS_STUB_EVENTS #define MAX_SYS_STUB_EVENTS 64 #endif #ifndef MAX_SYS_STUB_CRITICAL_SECTIONS #define MAX_SYS_STUB_CRITICAL_SECTIONS 64 #endif #ifndef MAX_SYS_STUB_THREADS #define MAX_SYS_STUB_THREADS 64 #endif /* =============================================================================== Globals expected by other code =============================================================================== */ idSys* sys = NULL; idEventLoop* eventLoop = NULL; int g_thread_count = 0; static xthreadInfo* sys_stub_thread_storage[MAX_SYS_STUB_THREADS]; xthreadInfo* g_threads[MAX_THREADS]; /* =============================================================================== com_developer If this constructor does not match your branch, change only this one line. Doom 3-style branches usually support: idCVar( name, value, flags, description ) =============================================================================== */ #ifndef CVAR_BOOL #define CVAR_BOOL 0 #endif #ifndef CVAR_SYSTEM #define CVAR_SYSTEM 0 #endif idCVar com_developer( "developer", "0", CVAR_SYSTEM | CVAR_BOOL, "Stubbed developer cvar" ); /* =============================================================================== Debug/helper functions =============================================================================== */ static void SysStub_DebugPrint(const char* text) { if (!text) { return; } #ifdef _WIN32 OutputDebugStringA(text); #endif fputs(text, stdout); fflush(stdout); } static void SysStub_NormalizeSlashes(char* path) { if (!path) { return; } for (char* p = path; *p; p++) { if (*p == '\\') { *p = '/'; } } } static void SysStub_CopyString(char* dst, int dstSize, const char* src) { if (!dst || dstSize <= 0) { return; } if (!src) { src = ""; } #ifdef _WIN32 strncpy_s(dst, dstSize, src, _TRUNCATE); #else strncpy(dst, src, dstSize - 1); dst[dstSize - 1] = '\0'; #endif } static bool SysStub_StringEndsWith(const char* text, const char* suffix) { if (!text || !suffix) { return false; } const size_t textLen = strlen(text); const size_t suffixLen = strlen(suffix); if (suffixLen > textLen) { return false; } #ifdef _WIN32 return _stricmp(text + textLen - suffixLen, suffix) == 0; #else return strcasecmp(text + textLen - suffixLen, suffix) == 0; #endif } static const char* SysStub_GetExePathFull(void) { static char exePath[MAX_OSPATH]; exePath[0] = '\0'; #ifdef _WIN32 GetModuleFileNameA(NULL, exePath, sizeof(exePath)); exePath[sizeof(exePath) - 1] = '\0'; #else ssize_t len = readlink("/proc/self/exe", exePath, sizeof(exePath) - 1); if (len > 0) { exePath[len] = '\0'; } else if (getcwd(exePath, sizeof(exePath)) == NULL) { strcpy(exePath, "."); } #endif SysStub_NormalizeSlashes(exePath); if (exePath[0] == '\0') { strcpy(exePath, "."); } return exePath; } static const char* SysStub_GetExeDirectory(void) { static char exeDir[MAX_OSPATH]; SysStub_CopyString(exeDir, sizeof(exeDir), SysStub_GetExePathFull()); SysStub_NormalizeSlashes(exeDir); char* lastSlash = strrchr(exeDir, '/'); if (lastSlash) { *lastSlash = '\0'; } if (exeDir[0] == '\0') { strcpy(exeDir, "."); } return exeDir; } static bool SysStub_IsDirectory(const char* path) { if (!path || !path[0]) { return false; } #ifdef _WIN32 DWORD attribs = GetFileAttributesA(path); if (attribs == INVALID_FILE_ATTRIBUTES) { return false; } return (attribs & FILE_ATTRIBUTE_DIRECTORY) != 0; #else struct stat st; if (stat(path, &st) != 0) { return false; } return S_ISDIR(st.st_mode); #endif } /* =============================================================================== Sys_FileTimeStamp Required linker symbol: __int64 __cdecl Sys_FileTimeStamp( struct _iobuf * ) =============================================================================== */ int64_t Sys_FileTimeStamp(FILE* fp) { if (!fp) { return 0; } #ifdef _WIN32 const int fd = _fileno(fp); if (fd < 0) { return 0; } HANDLE hFile = (HANDLE)_get_osfhandle(fd); if (hFile == INVALID_HANDLE_VALUE) { return 0; } FILETIME writeTime; if (!GetFileTime(hFile, NULL, NULL, &writeTime)) { return 0; } ULARGE_INTEGER uli; uli.LowPart = writeTime.dwLowDateTime; uli.HighPart = writeTime.dwHighDateTime; // FILETIME is 100ns ticks since Jan 1 1601. // Unix timestamp is seconds since Jan 1 1970. const uint64_t windowsTicks = uli.QuadPart; const uint64_t unixTicks = windowsTicks - 116444736000000000ULL; return (int64_t)(unixTicks / 10000000ULL); #else const int fd = fileno(fp); if (fd < 0) { return 0; } struct stat st; if (fstat(fd, &st) != 0) { return 0; } return (int64_t)st.st_mtime; #endif } /* =============================================================================== Paths =============================================================================== */ const char* Sys_EXEPath(void) { return SysStub_GetExeDirectory(); } const char* Sys_DefaultBasePath(void) { return SysStub_GetExeDirectory(); } const char* Sys_DefaultSavePath(void) { return SysStub_GetExeDirectory(); } const char* Sys_DefaultCDPath(void) { return ""; } /* =============================================================================== Sys_Mkdir Recursive mkdir. =============================================================================== */ void Sys_Mkdir(const char* path) { if (!path || !path[0]) { return; } char temp[MAX_OSPATH * 4]; SysStub_CopyString(temp, sizeof(temp), path); SysStub_NormalizeSlashes(temp); const int len = (int)strlen(temp); if (len <= 0) { return; } // Do not try to create "C:" by itself. for (char* p = temp; *p; p++) { if (*p != '/') { continue; } // Skip drive root, for example C:/. if (p == temp + 2 && temp[1] == ':') { continue; } // Skip leading slash. if (p == temp) { continue; } *p = '\0'; if (temp[0] && !SysStub_IsDirectory(temp)) { #ifdef _WIN32 _mkdir(temp); #else mkdir(temp, 0755); #endif } *p = '/'; } if (!SysStub_IsDirectory(temp)) { #ifdef _WIN32 _mkdir(temp); #else mkdir(temp, 0755); #endif } } /* =============================================================================== Sys_ListFiles Real directory listing. Returns count added. Behavior: - extension NULL / empty: lists everything except "." and ".." - extension "/" : lists folders only - extension ".ext" : lists files ending in .ext - extension "ext" : also treated like ".ext" =============================================================================== */ int Sys_ListFiles(const char* directory, const char* extension, idList& list) { if (!directory || !directory[0]) { return 0; } const int startCount = list.Num(); const bool listFoldersOnly = extension && strcmp(extension, "/") == 0; const bool hasExtensionFilter = extension && extension[0] && !listFoldersOnly; char normalizedExtension[128]; normalizedExtension[0] = '\0'; if (hasExtensionFilter) { if (extension[0] == '.') { SysStub_CopyString(normalizedExtension, sizeof(normalizedExtension), extension); } else { #ifdef _WIN32 _snprintf_s(normalizedExtension, sizeof(normalizedExtension), _TRUNCATE, ".%s", extension); #else snprintf(normalizedExtension, sizeof(normalizedExtension), ".%s", extension); normalizedExtension[sizeof(normalizedExtension) - 1] = '\0'; #endif } } #ifdef _WIN32 char searchPath[MAX_OSPATH * 4]; SysStub_CopyString(searchPath, sizeof(searchPath), directory); SysStub_NormalizeSlashes(searchPath); const int searchLen = (int)strlen(searchPath); if (searchLen > 0 && searchPath[searchLen - 1] != '/') { strcat_s(searchPath, sizeof(searchPath), "/"); } strcat_s(searchPath, sizeof(searchPath), "*"); WIN32_FIND_DATAA findData; HANDLE hFind = FindFirstFileA(searchPath, &findData); if (hFind == INVALID_HANDLE_VALUE) { return 0; } do { const char* name = findData.cFileName; if (!strcmp(name, ".") || !strcmp(name, "..")) { continue; } const bool isDirectory = (findData.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) != 0; if (listFoldersOnly) { if (!isDirectory) { continue; } } else { if (isDirectory) { continue; } if (hasExtensionFilter && !SysStub_StringEndsWith(name, normalizedExtension)) { continue; } } list.Append(idStr(name)); } while (FindNextFileA(hFind, &findData)); FindClose(hFind); #else DIR* dir = opendir(directory); if (!dir) { return 0; } struct dirent* entry; while ((entry = readdir(dir)) != NULL) { const char* name = entry->d_name; if (!strcmp(name, ".") || !strcmp(name, "..")) { continue; } char fullPath[MAX_OSPATH * 4]; snprintf(fullPath, sizeof(fullPath), "%s/%s", directory, name); fullPath[sizeof(fullPath) - 1] = '\0'; const bool isDirectory = SysStub_IsDirectory(fullPath); if (listFoldersOnly) { if (!isDirectory) { continue; } } else { if (isDirectory) { continue; } if (hasExtensionFilter && !SysStub_StringEndsWith(name, normalizedExtension)) { continue; } } list.Append(idStr(name)); } closedir(dir); #endif return list.Num() - startCount; } /* =============================================================================== Critical sections =============================================================================== */ #ifdef _WIN32 static CRITICAL_SECTION sys_stub_criticalSections[MAX_SYS_STUB_CRITICAL_SECTIONS]; static bool sys_stub_criticalSectionsInitialized[MAX_SYS_STUB_CRITICAL_SECTIONS]; static void SysStub_InitCriticalSection(int index) { if (index < 0 || index >= MAX_SYS_STUB_CRITICAL_SECTIONS) { return; } if (!sys_stub_criticalSectionsInitialized[index]) { InitializeCriticalSection(&sys_stub_criticalSections[index]); sys_stub_criticalSectionsInitialized[index] = true; } } #endif void Sys_EnterCriticalSection(int index) { #ifdef _WIN32 if (index < 0 || index >= MAX_SYS_STUB_CRITICAL_SECTIONS) { return; } SysStub_InitCriticalSection(index); EnterCriticalSection(&sys_stub_criticalSections[index]); #else (void)index; #endif } void Sys_LeaveCriticalSection(int index) { #ifdef _WIN32 if (index < 0 || index >= MAX_SYS_STUB_CRITICAL_SECTIONS) { return; } SysStub_InitCriticalSection(index); LeaveCriticalSection(&sys_stub_criticalSections[index]); #else (void)index; #endif } /* =============================================================================== Events =============================================================================== */ #ifdef _WIN32 static HANDLE sys_stub_events[MAX_SYS_STUB_EVENTS]; static HANDLE SysStub_GetEvent(int index) { if (index < 0 || index >= MAX_SYS_STUB_EVENTS) { return NULL; } if (!sys_stub_events[index]) { sys_stub_events[index] = CreateEventA( NULL, FALSE, // auto reset FALSE, // initially not signaled NULL ); } return sys_stub_events[index]; } #endif void Sys_WaitForEvent(int index) { #ifdef _WIN32 HANDLE eventHandle = SysStub_GetEvent(index); if (!eventHandle) { return; } WaitForSingleObject(eventHandle, INFINITE); #else (void)index; #endif } void Sys_TriggerEvent(int index) { #ifdef _WIN32 HANDLE eventHandle = SysStub_GetEvent(index); if (!eventHandle) { return; } SetEvent(eventHandle); #else (void)index; #endif } /* =============================================================================== Thread creation Required linker symbol: void Sys_CreateThread( unsigned int (__cdecl*)(void *), void *, enum xthreadPriority, struct xthreadInfo &, char const *, struct xthreadInfo ** const, int * ) =============================================================================== */ #ifdef _WIN32 struct sysStubThreadStart_t { unsigned int (*function)(void*); void* parameter; }; static unsigned __stdcall SysStub_ThreadTrampoline(void* arg) { sysStubThreadStart_t* start = (sysStubThreadStart_t*)arg; unsigned int result = 0; if (start && start->function) { result = start->function(start->parameter); } delete start; return result; } #endif void Sys_CreateThread( unsigned int (*function)(void*), void* parameter, xthreadPriority priority, xthreadInfo& info, const char* name, xthreadInfo** const threads, int* thread_count ) { #ifdef _WIN32 memset(&info, 0, sizeof(info)); sysStubThreadStart_t* start = new sysStubThreadStart_t; start->function = function; start->parameter = parameter; unsigned int threadId = 0; HANDLE handle = (HANDLE)_beginthreadex( NULL, 0, SysStub_ThreadTrampoline, start, 0, &threadId ); if (!handle) { delete start; SysStub_DebugPrint("Sys_CreateThread failed\n"); return; } // This assumes your xthreadInfo has these common Doom 3 fields. // If your struct differs, comment these out or adjust names. info.threadHandle = (int)handle; info.threadId = threadId; info.name = name; switch (priority) { case THREAD_NORMAL: SetThreadPriority(handle, THREAD_PRIORITY_NORMAL); break; default: SetThreadPriority(handle, THREAD_PRIORITY_NORMAL); break; } if (threads && thread_count) { if (*thread_count >= 0 && *thread_count < MAX_SYS_STUB_THREADS) { threads[*thread_count] = &info; (*thread_count)++; } } g_thread_count++; char buffer[512]; #ifdef _WIN32 _snprintf_s( buffer, sizeof(buffer), _TRUNCATE, "Sys_CreateThread created \"%s\" id=%u\n", name ? name : "", threadId ); #else snprintf(buffer, sizeof(buffer), "Sys_CreateThread created\n"); #endif SysStub_DebugPrint(buffer); #else (void)function; (void)parameter; (void)priority; (void)info; (void)name; (void)threads; (void)thread_count; #endif } /* =============================================================================== idEventLoop::JournalLevel Stubbed to 0 so file system reads do not try to journal. =============================================================================== */ int idEventLoop::JournalLevel(void) const { return 0; }