diff --git a/CMakeLists.txt b/CMakeLists.txt index d88af384..d209e7e0 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -106,6 +106,10 @@ set(SOURCES_COMMON "code/qcommon/tiki_main.cpp" "code/qcommon/tiki_script.cpp" "code/qcommon/unzip.c" + # Main stuff + "code/sys/sys_main.c" + "code/sys/sys_autoupdater.c" + "code/sys/con_log.c" ) file(GLOB_RECURSE SOURCES_SKEL "code/tiki/*.cpp" "code/skeletor/*.cpp") @@ -124,7 +128,7 @@ set(SOURCES_APP ${SOURCES_SHARED} ${SOURCES_COMMON} ${SOURCES_SKEL}) file(GLOB_RECURSE SOURCES_SERVER "code/server/*.c" "code/server/*.cpp") set(SOURCES_SERVER_APP ${SOURCES_APP} ${SOURCES_SERVER}) -add_executable(omohaaded ${SOURCES_SERVER_APP} ${SOURCES_PLATFORM_SPECIFIC} "code/null/null_client.c" "code/null/null_input.c" "code/null/null_snddma.c" "code/sys/sys_main.c" "code/sys/con_log.c") +add_executable(omohaaded ${SOURCES_SERVER_APP} ${SOURCES_PLATFORM_SPECIFIC} "code/null/null_client.c" "code/null/null_input.c" "code/null/null_snddma.c") set_property(TARGET omohaaded PROPERTY CXX_STANDARD 11) target_compile_features(omohaaded PUBLIC cxx_constexpr) target_compile_definitions(omohaaded PRIVATE NO_SCRIPTENGINE DEDICATED TARGET_GAME_TYPE=${TARGET_GAME_TYPE}) diff --git a/code/null/null_main.c b/code/null/null_main.c index 38a322b6..30703497 100644 --- a/code/null/null_main.c +++ b/code/null/null_main.c @@ -83,7 +83,8 @@ int Sys_Milliseconds (void) { return 0; } -void Sys_Mkdir (char *path) { +qboolean Sys_Mkdir (const char *path) { + return qfalse; } char *Sys_FindFirst (char *path, unsigned musthave, unsigned canthave) { diff --git a/code/qcommon/common.cpp b/code/qcommon/common.cpp index 9b5d94d6..02c2f355 100644 --- a/code/qcommon/common.cpp +++ b/code/qcommon/common.cpp @@ -99,6 +99,7 @@ cvar_t *com_cameraMode; cvar_t *com_ansiColor; cvar_t *com_unfocused; cvar_t *com_minimized; +cvar_t *com_homepath; cvar_t *precache; // com_speeds times @@ -291,7 +292,7 @@ void QDECL Com_Error( int code, const char *fmt, ... ) { lastErrorTime = currentTime; if ( com_errorEntered ) { - SyScriptError( "recursive error after: %s", com_errorMessage ); + Sys_Error( "recursive error after: %s", com_errorMessage ); } com_errorEntered = qtrue; @@ -336,7 +337,7 @@ void QDECL Com_Error( int code, const char *fmt, ... ) { Com_Shutdown (); - SyScriptError ("%s", com_errorMessage); + Sys_Error("%s", com_errorMessage); } @@ -1266,7 +1267,7 @@ void Com_Init( char *commandLine ) { Com_Printf( "%s %s %s\n", PRODUCT_VERSION_FULL, PLATFORM_STRING, __DATE__ ); if ( setjmp (abortframe) ) { - SyScriptError ("Error during initialization"); + Sys_Error("Error during initialization"); } // prepare enough of the subsystems to handle @@ -1291,6 +1292,8 @@ void Com_Init( char *commandLine ) { // done early so bind command exists CL_InitKeyCommands(); + com_homepath = Cvar_Get("com_homepath", "", CVAR_INIT|CVAR_ROM); + FS_InitFilesystem (); Com_InitJournaling(); @@ -1404,7 +1407,7 @@ void Com_Init( char *commandLine ) { } } - s = va( "%s %s %s", PRODUCT_VERSION_FULL, PLATFORM_STRING, __DATE__ ); + s = va( "%s %s %s", PRODUCT_VERSION_FULL, PLATFORM_STRING, PRODUCT_DATE ); com_version = Cvar_Get( "version", s, CVAR_ROM | CVAR_SERVERINFO ); com_shortversion = Cvar_Get( "shortversion", TARGET_GAME_VERSION, CVAR_ROM ); diff --git a/code/qcommon/files.cpp b/code/qcommon/files.cpp index dcf95356..0aa76ac6 100644 --- a/code/qcommon/files.cpp +++ b/code/qcommon/files.cpp @@ -245,8 +245,7 @@ cvar_t *fs_basepath; static cvar_t *fs_basegame; static cvar_t *fs_gamedirvar; static cvar_t *fs_restrict; -static cvar_t *fs_userpath; -static cvar_t *fs_outputpath; +static cvar_t *fs_homepath; static cvar_t *fs_copyfiles; static cvar_t *fs_filedir; static searchpath_t *fs_searchpaths; @@ -671,7 +670,7 @@ qboolean FS_FileExists( const char *file ) FILE *f; char *testpath; - testpath = FS_BuildOSPath( fs_outputpath->string, fs_gamedir, file ); + testpath = FS_BuildOSPath( fs_homepath->string, fs_gamedir, file ); f = fopen( testpath, "rb" ); if (f) { @@ -693,7 +692,7 @@ qboolean FS_SV_FileExists( const char *file ) FILE *f; char *testpath; - testpath = FS_BuildOSPath( fs_outputpath->string, file, ""); + testpath = FS_BuildOSPath( fs_homepath->string, file, ""); testpath[strlen(testpath)-1] = '\0'; f = fopen( testpath, "rb" ); @@ -718,7 +717,7 @@ fileHandle_t FS_SV_FOpenFileWrite( const char *filename ) { Com_Error( ERR_FATAL, "Filesystem call made without initialization\n" ); } - ospath = FS_BuildOSPath( fs_outputpath->string, filename, "" ); + ospath = FS_BuildOSPath( fs_homepath->string, filename, "" ); ospath[strlen(ospath)-1] = '\0'; f = FS_HandleForFile(); @@ -769,20 +768,20 @@ int FS_SV_FOpenFileRead( const char *filename, fileHandle_t *fp ) { S_ClearSoundBuffer(); // search homepath - ospath = FS_BuildOSPath( fs_outputpath->string, filename, "" ); + ospath = FS_BuildOSPath( fs_homepath->string, filename, "" ); // remove trailing slash ospath[strlen(ospath)-1] = '\0'; if ( fs_debug->integer ) { - Com_Printf( "FS_SV_FOpenFileRead (fs_outputpath): %s\n", ospath ); + Com_Printf( "FS_SV_FOpenFileRead (fs_homepath): %s\n", ospath ); } fsh[f].handleFiles.file.o = fopen( ospath, "rb" ); fsh[f].handleSync = qfalse; if (!fsh[f].handleFiles.file.o) { - // If fs_outputpath == fs_basepath, don't bother - if (Q_stricmp(fs_outputpath->string,fs_basepath->string)) + // If fs_homepath == fs_basepath, don't bother + if (Q_stricmp(fs_homepath->string,fs_basepath->string)) { // search basepath ospath = FS_BuildOSPath( fs_basepath->string, filename, "" ); @@ -827,8 +826,8 @@ void FS_SV_Rename( const char *from, const char *to ) { // don't let sound stutter S_ClearSoundBuffer(); - from_ospath = FS_BuildOSPath( fs_outputpath->string, from, "" ); - to_ospath = FS_BuildOSPath( fs_outputpath->string, to, "" ); + from_ospath = FS_BuildOSPath( fs_homepath->string, from, "" ); + to_ospath = FS_BuildOSPath( fs_homepath->string, to, "" ); from_ospath[strlen(from_ospath)-1] = '\0'; to_ospath[strlen(to_ospath)-1] = '\0'; @@ -861,8 +860,8 @@ void FS_Rename( const char *from, const char *to ) { // don't let sound stutter S_ClearSoundBuffer(); - from_ospath = FS_BuildOSPath( fs_outputpath->string, fs_gamedir, from ); - to_ospath = FS_BuildOSPath( fs_outputpath->string, fs_gamedir, to ); + from_ospath = FS_BuildOSPath( fs_homepath->string, fs_gamedir, from ); + to_ospath = FS_BuildOSPath( fs_homepath->string, fs_gamedir, to ); if ( fs_debug->integer ) { Com_Printf( "FS_Rename: %s --> %s\n", from_ospath, to_ospath ); @@ -923,7 +922,7 @@ fileHandle_t FS_FOpenFileWrite( const char *filename ) { f = FS_HandleForFile(); fsh[f].zipFile = qfalse; - ospath = FS_BuildOSPath( fs_outputpath->string, fs_gamedir, filename ); + ospath = FS_BuildOSPath( fs_homepath->string, fs_gamedir, filename ); if ( fs_debug->integer ) { Com_Printf( "FS_FOpenFileWrite: %s\n", ospath ); @@ -962,7 +961,7 @@ fileHandle_t FS_FOpenTextFileWrite( const char *filename ) { Q_strncpyz( fsh[f].name, filename, sizeof( fsh[f].name ) ); - ospath = FS_BuildOSPath( fs_outputpath->string, fs_gamedir, filename ); + ospath = FS_BuildOSPath( fs_homepath->string, fs_gamedir, filename ); if ( fs_debug->integer ) { Com_Printf( "FS_FOpenFileWrite: %s\n", ospath ); @@ -1002,7 +1001,7 @@ fileHandle_t FS_FOpenFileAppend( const char *filename ) { // don't let sound stutter S_ClearSoundBuffer(); - ospath = FS_BuildOSPath( fs_outputpath->string, fs_gamedir, filename ); + ospath = FS_BuildOSPath( fs_homepath->string, fs_gamedir, filename ); if ( fs_debug->integer ) { Com_Printf( "FS_FOpenFileAppend: %s\n", ospath ); @@ -1084,7 +1083,7 @@ void FS_DeleteFile( const char *filename ) Com_Error( 0, "Filesystem call made without initialization\n" ); } - ospath = FS_BuildOSPath( fs_outputpath->string, fs_gamedir, filename ); + ospath = FS_BuildOSPath( fs_homepath->string, fs_gamedir, filename ); if( fs_debug->integer ) { Com_Printf( "FS_DeleteFile: %s\n", ospath ); @@ -1130,7 +1129,6 @@ Used for streaming data out of either a separate file or a ZIP file. =========== */ -extern "C" qboolean com_fullyInitialized; int FS_FOpenFileRead( const char *filename, fileHandle_t *file, qboolean uniqueFILE, qboolean quiet ) { searchpath_t *search; @@ -1288,7 +1286,7 @@ int FS_FOpenFileRead( const char *filename, fileHandle_t *file, qboolean uniqueF continue; } } - + dir = search->dir; netpath = FS_BuildOSPath( dir->path, dir->gamedir, filename ); @@ -2397,7 +2395,7 @@ int FS_GetModList( char *listbuf, int bufsize ) { nMods = 0; nPotential = nTotal = 0; - pFiles0 = Sys_ListFiles( fs_outputpath->string, NULL, NULL, &dummy, qtrue ); + pFiles0 = Sys_ListFiles( fs_homepath->string, NULL, NULL, &dummy, qtrue ); pFiles1 = Sys_ListFiles( fs_basepath->string, NULL, NULL, &dummy, qtrue ); // we searched for mods in the three paths // it is likely that we have duplicate names now, which we will cleanup below @@ -2437,7 +2435,7 @@ int FS_GetModList( char *listbuf, int bufsize ) { /* try on home path */ if ( nPaks <= 0 ) { - path = FS_BuildOSPath( fs_outputpath->string, name, "" ); + path = FS_BuildOSPath( fs_homepath->string, name, "" ); nPaks = 0; pPaks = Sys_ListFiles( path, ".pk3", NULL, &nPaks, qfalse ); Sys_FreeFileList( pPaks ); @@ -3072,13 +3070,20 @@ FS_Startup */ static void FS_Startup( const char *gameName ) { + const char* homePath; + if( !silentStart ) { Com_Printf( "----- FS_Startup -----\n" ); } fs_debug = Cvar_Get( "fs_debug", "0", 0 ); fs_copyfiles = Cvar_Get( "fs_copyfiles", "0", CVAR_INIT ); - fs_basepath = Cvar_Get ("fs_basepath", Sys_DefaultBasePath(), CVAR_INIT ); + fs_basepath = Cvar_Get("fs_basepath", Sys_DefaultInstallPath(), CVAR_INIT); + homePath = Sys_DefaultHomePath(); + if (!homePath || !homePath[0]) { + homePath = fs_basepath->string; + } + fs_homepath = Cvar_Get("fs_homepath", homePath, CVAR_INIT | CVAR_ROM); fs_gamedirvar = Cvar_Get( "fs_game", "", CVAR_INIT | CVAR_SYSTEMINFO ); fs_restrict = Cvar_Get( "fs_restrict", "", CVAR_INIT ); @@ -3097,14 +3102,7 @@ static void FS_Startup( const char *gameName ) Cmd_AddCommand ("fdir", FS_NewDir_f ); Cmd_AddCommand ("touchFile", FS_TouchFile_f ); - fs_userpath = Cvar_Get( "fs_userpath", Sys_DefaultUserPath(), CVAR_INIT ); - fs_outputpath = Cvar_Get( "fs_outputpath", Sys_DefaultOutputPath(), CVAR_INIT ); - - Sys_Mkdir( fs_userpath->string ); - Sys_Mkdir( fs_outputpath->string ); - - FS_AddGameDirectory( fs_userpath->string, gameName ); - + Sys_Mkdir(fs_homepath->string); if( !silentStart ) { // print the current search paths @@ -3593,8 +3591,7 @@ void FS_InitFilesystem( void ) { Com_StartupVariable( "fs_game" ); Com_StartupVariable( "fs_copyfiles" ); Com_StartupVariable( "fs_restrict" ); - Com_StartupVariable( "fs_userpath" ); - Com_StartupVariable( "fs_outputpath" ); + Com_StartupVariable( "fs_homepath" ); // try to start up normally FS_Startup( BASEGAME ); @@ -3893,3 +3890,8 @@ void FS_GetRelativeFilename( const char *currentDirectory, const char *absoluteF // copy the rest of the filename into the result string strcpy( &out[ rfMarker ], &absoluteFilename[ afMarker ] ); } + +const char* FS_GetCurrentGameDir() +{ + return fs_gamedirvar->string; +} diff --git a/code/qcommon/q_shared.c b/code/qcommon/q_shared.c index e4ad985b..6629ad1c 100644 --- a/code/qcommon/q_shared.c +++ b/code/qcommon/q_shared.c @@ -220,6 +220,30 @@ void COM_StripExtension( const char *in, char *out, int destsize ) { out[length] = 0; } +/* +============ +COM_CompareExtension + +string compare the end of the strings and return qtrue if strings match +============ +*/ +qboolean COM_CompareExtension(const char* in, const char* ext) +{ + int inlen, extlen; + + inlen = strlen(in); + extlen = strlen(ext); + + if (extlen <= inlen) + { + in += inlen - extlen; + + if (!Q_stricmp(in, ext)) + return qtrue; + } + + return qfalse; +} /* ================== @@ -1040,34 +1064,86 @@ int Com_HexStrToInt( const char *str ) ============================================================================ */ -int Q_isprint( int c ) +int Q_isprint(int c) { - if ( c >= 0x20 && c <= 0x7E ) - return ( 1 ); - return ( 0 ); + if (c >= 0x20 && c <= 0x7E) + return (1); + return (0); } -int Q_islower( int c ) +int Q_islower(int c) { if (c >= 'a' && c <= 'z') - return ( 1 ); - return ( 0 ); + return (1); + return (0); } -int Q_isupper( int c ) +int Q_isupper(int c) { if (c >= 'A' && c <= 'Z') - return ( 1 ); - return ( 0 ); + return (1); + return (0); } -int Q_isalpha( int c ) +int Q_isalpha(int c) { if ((c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z')) - return ( 1 ); - return ( 0 ); + return (1); + return (0); } +qboolean Q_isanumber(const char* s) +{ + char* p; + double d; + + if (*s == '\0') + return qfalse; + + d = strtod(s, &p); + + return *p == '\0'; +} + +qboolean Q_isintegral(float f) +{ + return (int)f == f; +} + +#ifdef _WIN32 +/* +============= +Q_vsnprintf + +Special wrapper function for Microsoft's broken _vsnprintf() function. +MinGW comes with its own vsnprintf() which is not broken. mingw-w64 +however, uses Microsoft's broken _vsnprintf() function. +============= +*/ + +int Q_vsnprintf(char* str, size_t size, const char* format, va_list ap) +{ + int retval; + + retval = _vsnprintf(str, size, format, ap); + + if (retval < 0 || retval == size) + { + // Microsoft doesn't adhere to the C99 standard of vsnprintf, + // which states that the return value must be the number of + // bytes written if the output string had sufficient length. + // + // Obviously we cannot determine that value from Microsoft's + // implementation, so we have no choice but to return size. + + str[size - 1] = '\0'; + return size; + } + + return retval; +} +#endif + char* Q_strrchr( const char* string, int c ) { char cc = c; @@ -1325,24 +1401,18 @@ char *Q_CleanStr( char *string ) { } -void QDECL Com_sprintf( char *dest, int size, const char *fmt, ...) { - size_t len; +int QDECL Com_sprintf( char *dest, int size, const char *fmt, ...) { + int len; va_list argptr; - char bigbuffer[32000]; // big, but small enough to fit in PPC stack - va_start (argptr,fmt); - len = vsprintf (bigbuffer,fmt,argptr); - va_end (argptr); - if ( len >= sizeof( bigbuffer ) ) { - Com_Error( ERR_FATAL, "Com_sprintf: overflowed bigbuffer" ); - } - if (len >= size) { - Com_Printf ("Com_sprintf: overflow of %i in %i\n", len, size); -#ifdef _DEBUG - __debugbreak(); -#endif - } - Q_strncpyz (dest, bigbuffer, size ); + va_start(argptr, fmt); + len = Q_vsnprintf(dest, size, fmt, argptr); + va_end(argptr); + + if (len >= size) + Com_Printf("Com_sprintf: Output length %d too short, require %d bytes.\n", size, len + 1); + + return len; } void Com_BackslashToSlash( char *str ) diff --git a/code/qcommon/q_shared.h b/code/qcommon/q_shared.h index b67b2f5c..373ae625 100644 --- a/code/qcommon/q_shared.h +++ b/code/qcommon/q_shared.h @@ -31,6 +31,7 @@ extern "C" { #define PRODUCT_NAME "OpenMoHAA" #define PRODUCT_VERSION "0.49-alpha" +#define PRODUCT_DATE __DATE__ #if TARGET_GAME_TYPE == 1 // Team Assault @@ -39,6 +40,10 @@ extern "C" { // The version string must be equal or above 2.0 to be able to connect to spearhead servers #define TARGET_GAME_VERSION "2.41" #define TARGET_GAME_PROTOCOL 17 + + #define HOMEPATH_NAME_UNIX ".mohta" + #define HOMEPATH_NAME_WIN "mohta" + #define HOMEPATH_NAME_MACOSX HOMEPATH_NAME_WIN #elif TARGET_GAME_TYPE == 2 // Team Tactics #define BASEGAME "maintt" @@ -46,6 +51,10 @@ extern "C" { // The version string must be equal or above 2.0 to be able to connect to breakthrough servers #define TARGET_GAME_VERSION "2.41" #define TARGET_GAME_PROTOCOL 17 + + #define HOMEPATH_NAME_UNIX ".mohtt" + #define HOMEPATH_NAME_WIN "mohtt" + #define HOMEPATH_NAME_MACOSX HOMEPATH_NAME_WIN #else // The default: the base game (no expansion) @@ -54,6 +63,10 @@ extern "C" { // The version string must be below 1.12, otherwise it's not possible to connect #define TARGET_GAME_VERSION "1.12" #define TARGET_GAME_PROTOCOL 8 + + #define HOMEPATH_NAME_UNIX ".moh" + #define HOMEPATH_NAME_WIN "moh" + #define HOMEPATH_NAME_MACOSX HOMEPATH_NAME_WIN #endif #define PRODUCT_NAME_FULL PRODUCT_NAME ": " PRODUCT_EXTENSION @@ -141,28 +154,25 @@ extern "C" { #ifdef _MSC_VER #include -#ifndef _STDINT - typedef __int64 int64_t; - typedef __int32 int32_t; - typedef __int16 int16_t; - typedef __int8 int8_t; - typedef unsigned __int64 uint64_t; - typedef unsigned __int32 uint32_t; - typedef unsigned __int16 uint16_t; - typedef unsigned __int8 uint8_t; -#endif - - // vsnprintf is ISO/IEC 9899:1999 - // abstracting this to make it portable - int Q_vsnprintf( char *str, size_t size, const char *format, va_list ap ); +typedef __int64 int64_t; +typedef __int32 int32_t; +typedef __int16 int16_t; +typedef __int8 int8_t; +typedef unsigned __int64 uint64_t; +typedef unsigned __int32 uint32_t; +typedef unsigned __int16 uint16_t; +typedef unsigned __int8 uint8_t; #else #include - -#define Q_vsnprintf vsnprintf #endif -#define HAVE_STDINT_H -#define _HAVE_STDINT_H 1 +#ifdef _WIN32 + // vsnprintf is ISO/IEC 9899:1999 + // abstracting this to make it portable +int Q_vsnprintf(char* str, size_t size, const char* format, va_list ap); +#else +#define Q_vsnprintf vsnprintf +#endif #endif @@ -1097,6 +1107,7 @@ float Com_Clamp( float min, float max, float value ); const char *COM_SkipPath( const char *pathname ); const char *COM_GetExtension( const char *name ); void COM_StripExtension(const char *in, char *out, int destsize); +qboolean COM_CompareExtension(const char* in, const char* ext); void COM_DefaultExtension( char *path, int maxSize, const char *extension ); void COM_BeginParseSession( const char *name ); @@ -1147,7 +1158,7 @@ void Parse3DMatrix (char **buf_p, int z, int y, int x, float *m); int Com_HexStrToInt( const char *str ); -void QDECL Com_sprintf (char *dest, int size, const char *fmt, ...) __attribute__ ((format (printf, 3, 4))); +int QDECL Com_sprintf (char *dest, int size, const char *fmt, ...) __attribute__ ((format (printf, 3, 4))); char *Com_SkipTokens( char *s, int numTokens, char *sep ); char *Com_SkipCharset( char *s, char *sep ); diff --git a/code/qcommon/qcommon.h b/code/qcommon/qcommon.h index f183654c..fd9b9655 100644 --- a/code/qcommon/qcommon.h +++ b/code/qcommon/qcommon.h @@ -647,7 +647,7 @@ qboolean FS_FileExists( const char *file ); char *FS_BuildOSPath( const char *base, const char *game, const char *qpath ); -qboolean FS_CreatePath( char *OSPath ); +qboolean FS_CreatePath(char *OSPath); int FS_LoadStack( void ); @@ -767,6 +767,7 @@ void FS_Remove( const char *osPath ); void FS_FilenameCompletion( const char *dir, const char *ext, qboolean stripExt, void(*callback)(const char *s) ); +const char* FS_GetCurrentGameDir(); void FS_GetRelativeFilename( const char *currentDirectory, const char *absoluteFilename, char *out, size_t destlen ); extern char fs_gamedir[]; @@ -1098,6 +1099,7 @@ extern cvar_t *com_cameraMode; extern cvar_t *com_ansiColor; extern cvar_t *com_unfocused; extern cvar_t *com_minimized; +extern cvar_t *com_homepath; extern cvar_t *com_altivec; // both client and server must agree to pause @@ -1116,6 +1118,7 @@ extern int com_frameTime; extern int com_frameMsec; extern qboolean com_errorEntered; +extern qboolean com_fullyInitialized; extern fileHandle_t com_journalFile; extern fileHandle_t com_journalDataFile; @@ -1299,6 +1302,14 @@ void SV_Frame( int msec ); void SV_PacketEvent( netadr_t from, msg_t *msg ); qboolean SV_GameCommand( void ); +// +// input interface +// +void IN_Init(void* windowData); +void IN_Frame(void); +void IN_Shutdown(void); +void IN_Restart(void); + void Com_Pause(); void Com_Unpause(); void Com_FakePause(); @@ -1350,10 +1361,10 @@ sysEvent_t Com_GetSystemEvent( void ); void Sys_Init (void); // general development dll loading for virtual machine testing -void * QDECL Sys_LoadDll( const char *name, char *fqpath , intptr_t (QDECL **entryPoint)(int, ...), - intptr_t (QDECL *systemcalls)(intptr_t, ...) ); void Sys_UnloadDll( void *dllHandle ); +qboolean Sys_DllExtension(const char* name); + void Sys_UnloadGame( void ); void *Sys_GetGameAPI( void *parms ); @@ -1369,7 +1380,7 @@ void *Sys_GetBotLibAPI( void *parms ); char *Sys_GetCurrentUser( void ); -void QDECL SyScriptError( const char *error, ...) __attribute__ ((format (printf, 1, 2))); +void QDECL Sys_Error( const char *error, ...) __attribute__ ((format (printf, 1, 2))); void Sys_Quit (void); char *Sys_GetClipboardData( void ); // note that this isn't journaled... @@ -1399,7 +1410,7 @@ qboolean Sys_StringToAdr( const char *s, netadr_t *a ); qboolean Sys_IsLANAddress (netadr_t adr); void Sys_ShowIP(void); -void Sys_Mkdir( const char *path ); +qboolean Sys_Mkdir( const char *path ); char *Sys_Cwd( void ); void Sys_SetDefaultInstallPath(const char *path); char *Sys_DefaultInstallPath( void ); diff --git a/code/sys/con_log.c b/code/sys/con_log.c index 40a6d3c7..205734e7 100644 --- a/code/sys/con_log.c +++ b/code/sys/con_log.c @@ -26,16 +26,16 @@ Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA #define MAX_LOG 32768 -static char consoleLog[ MAX_LOG ]; -static size_t writePos = 0; -static size_t readPos = 0; +static char consoleLog[ MAX_LOG ]; +static unsigned int writePos = 0; +static unsigned int readPos = 0; /* ================== CON_LogSize ================== */ -size_t CON_LogSize( void ) +unsigned int CON_LogSize( void ) { if( readPos <= writePos ) return writePos - readPos; @@ -48,7 +48,7 @@ size_t CON_LogSize( void ) CON_LogFree ================== */ -static size_t CON_LogFree( void ) +static unsigned int CON_LogFree( void ) { return MAX_LOG - CON_LogSize( ) - 1; } @@ -58,11 +58,11 @@ static size_t CON_LogFree( void ) CON_LogWrite ================== */ -size_t CON_LogWrite( const char *in ) +unsigned int CON_LogWrite( const char *in ) { - size_t length = strlen( in ); - size_t firstChunk; - size_t secondChunk; + unsigned int length = (unsigned int)strlen( in ); + unsigned int firstChunk; + unsigned int secondChunk; while( CON_LogFree( ) < length && CON_LogSize( ) > 0 ) { @@ -101,10 +101,10 @@ size_t CON_LogWrite( const char *in ) CON_LogRead ================== */ -size_t CON_LogRead( char *out, size_t outSize ) +unsigned int CON_LogRead( char *out, unsigned int outSize ) { - size_t firstChunk; - size_t secondChunk; + unsigned int firstChunk; + unsigned int secondChunk; if( CON_LogSize( ) < outSize ) outSize = CON_LogSize( ); @@ -121,7 +121,7 @@ size_t CON_LogRead( char *out, size_t outSize ) } Com_Memcpy( out, consoleLog + readPos, firstChunk ); - Com_Memcpy( out + firstChunk, out, secondChunk ); + Com_Memcpy( out + firstChunk, consoleLog, secondChunk ); readPos = ( readPos + outSize ) % MAX_LOG; diff --git a/code/sys/con_tty.c b/code/sys/con_tty.c index c2e553aa..58f178ad 100644 --- a/code/sys/con_tty.c +++ b/code/sys/con_tty.c @@ -24,6 +24,10 @@ Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA #include "../qcommon/qcommon.h" #include "sys_local.h" +#ifndef DEDICATED +#include "../client/client.h" +#endif + #include #include #include @@ -40,9 +44,12 @@ called before and after a stdout or stderr output ============================================================= */ +extern qboolean stdinIsATTY; +static qboolean stdin_active; // general flag to tell about tty console mode static qboolean ttycon_on = qfalse; static int ttycon_hide = 0; +static int ttycon_show_overdue = 0; // some key codes that the terminal may be using, initialised on start up static int TTY_erase; @@ -58,19 +65,13 @@ static field_t TTY_con; static field_t ttyEditLines[ CON_HISTORY ]; static int hist_current = -1, hist_count = 0; -/* -================== -CON_FlushIn - -Flush stdin, I suspect some terminals are sending a LOT of shit -FIXME relevant? -================== -*/ -static void CON_FlushIn( void ) -{ - char key; - while (read(0, &key, 1)!=-1); -} +#ifndef DEDICATED +// Don't use "]" as it would be the same as in-game console, +// this makes it clear where input came from. +#define TTY_CONSOLE_PROMPT "tty]" +#else +#define TTY_CONSOLE_PROMPT "]" +#endif /* ================== @@ -86,12 +87,14 @@ send "\b \b" static void CON_Back( void ) { char key; + size_t UNUSED_VAR size; + key = '\b'; - write(1, &key, 1); + size = write(STDOUT_FILENO, &key, 1); key = ' '; - write(1, &key, 1); + size = write(STDOUT_FILENO, &key, 1); key = '\b'; - write(1, &key, 1); + size = write(STDOUT_FILENO, &key, 1); } /* @@ -119,7 +122,10 @@ static void CON_Hide( void ) CON_Back(); } } - CON_Back(); // Delete "]" + // Delete prompt + for (i = strlen(TTY_CONSOLE_PROMPT); i > 0; i--) { + CON_Back(); + } ttycon_hide++; } } @@ -142,12 +148,13 @@ static void CON_Show( void ) ttycon_hide--; if (ttycon_hide == 0) { - write( 1, "]", 1 ); + size_t UNUSED_VAR size; + size = write(STDOUT_FILENO, TTY_CONSOLE_PROMPT, strlen(TTY_CONSOLE_PROMPT)); if (TTY_con.cursor) { for (i=0; icursor) + return; + assert(hist_count <= CON_HISTORY); assert(hist_count >= 0); assert(hist_current >= -1); @@ -241,6 +253,19 @@ field_t *Hist_Next( void ) return &(ttyEditLines[hist_current]); } +/* +================== +CON_SigCont +Reinitialize console input after receiving SIGCONT, as on Linux the terminal seems to lose all +set attributes if user did CTRL+Z and then does fg again. +================== +*/ + +void CON_SigCont(int signum) +{ + CON_Init(); +} + /* ================== CON_Init @@ -257,18 +282,22 @@ void CON_Init( void ) signal(SIGTTIN, SIG_IGN); signal(SIGTTOU, SIG_IGN); - // Make stdin reads non-blocking - fcntl( 0, F_SETFL, fcntl( 0, F_GETFL, 0 ) | O_NONBLOCK ); + // If SIGCONT is received, reinitialize console + signal(SIGCONT, CON_SigCont); - if (isatty(STDIN_FILENO)!=1) + // Make stdin reads non-blocking + fcntl(STDIN_FILENO, F_SETFL, fcntl(STDIN_FILENO, F_GETFL, 0) | O_NONBLOCK ); + + if (!stdinIsATTY) { - Com_Printf( "stdin is not a tty, tty console mode disabled\n"); + Com_Printf("tty console mode disabled\n"); ttycon_on = qfalse; + stdin_active = qtrue; return; } Field_Clear(&TTY_con); - tcgetattr (0, &TTY_tc); + tcgetattr (STDIN_FILENO, &TTY_tc); TTY_erase = TTY_tc.c_cc[VERASE]; TTY_eof = TTY_tc.c_cc[VEOF]; tc = TTY_tc; @@ -279,8 +308,7 @@ void CON_Init( void ) characters EOF, EOL, EOL2, ERASE, KILL, REPRINT, STATUS, and WERASE, and buffers by lines. ISIG: when any of the characters INTR, QUIT, SUSP, or - DSUSP are received, generate the corresponding sig­ - nal + DSUSP are received, generate the corresponding signal */ tc.c_lflag &= ~(ECHO | ICANON); @@ -291,8 +319,10 @@ void CON_Init( void ) tc.c_iflag &= ~(ISTRIP | INPCK); tc.c_cc[VMIN] = 1; tc.c_cc[VTIME] = 0; - tcsetattr (0, TCSADRAIN, &tc); + tcsetattr (STDIN_FILENO, TCSADRAIN, &tc); ttycon_on = qtrue; + ttycon_hide = 1; // Mark as hidden, so prompt is shown in CON_Show + CON_Show(); } /* @@ -303,14 +333,15 @@ CON_Input char *CON_Input( void ) { // we use this when sending back commands - static char text[256]; + static char text[MAX_EDIT_LINE]; int avail; char key; field_t *history; + size_t UNUSED_VAR size; - if( ttycon_on ) + if(ttycon_on) { - avail = read(0, &key, 1); + avail = read(STDIN_FILENO, &key, 1); if (avail != -1) { // we have something @@ -331,13 +362,43 @@ char *CON_Input( void ) { if (key == '\n') { +#ifndef DEDICATED + // if not in the game explicitly prepend a slash if needed + if (clc.state != CA_ACTIVE && con_autochat->integer && TTY_con.cursor && + TTY_con.buffer[0] != '/' && TTY_con.buffer[0] != '\\') + { + memmove(TTY_con.buffer + 1, TTY_con.buffer, sizeof(TTY_con.buffer) - 1); + TTY_con.buffer[0] = '\\'; + TTY_con.cursor++; + } + + if (TTY_con.buffer[0] == '/' || TTY_con.buffer[0] == '\\') { + Q_strncpyz(text, TTY_con.buffer + 1, sizeof(text)); + } else if (TTY_con.cursor) { + if (con_autochat->integer) { + Com_sprintf(text, sizeof(text), "cmd say %s", TTY_con.buffer); + } else { + Q_strncpyz(text, TTY_con.buffer, sizeof(text)); + } + } else { + text[0] = '\0'; + } + // push it in history Hist_Add(&TTY_con); - strcpy(text, TTY_con.buffer); + CON_Hide(); + Com_Printf("%s%s\n", TTY_CONSOLE_PROMPT, TTY_con.buffer); + Field_Clear(&TTY_con); + CON_Show(); +#else + // push it in history + Hist_Add(&TTY_con); + Q_strncpyz(text, TTY_con.buffer, sizeof(text)); Field_Clear(&TTY_con); key = '\n'; - write(1, &key, 1); - write( 1, "]", 1 ); + size = write(STDOUT_FILENO, &key, 1); + size = write(STDOUT_FILENO, TTY_CONSOLE_PROMPT, strlen(TTY_CONSOLE_PROMPT)); +#endif return text; } if (key == '\t') @@ -347,13 +408,13 @@ char *CON_Input( void ) CON_Show(); return NULL; } - avail = read(0, &key, 1); + avail = read(STDIN_FILENO, &key, 1); if (avail != -1) { // VT 100 keys if (key == '[' || key == 'O') { - avail = read(0, &key, 1); + avail = read(STDIN_FILENO, &key, 1); if (avail != -1) { switch (key) @@ -366,7 +427,7 @@ char *CON_Input( void ) TTY_con = *history; CON_Show(); } - CON_FlushIn(); + tcflush(STDIN_FILENO, TCIFLUSH); return NULL; break; case 'B': @@ -380,7 +441,7 @@ char *CON_Input( void ) Field_Clear(&TTY_con); } CON_Show(); - CON_FlushIn(); + tcflush(STDIN_FILENO, TCIFLUSH); return NULL; break; case 'C': @@ -392,41 +453,34 @@ char *CON_Input( void ) } } Com_DPrintf("droping ISCTL sequence: %d, TTY_erase: %d\n", key, TTY_erase); - CON_FlushIn(); + tcflush(STDIN_FILENO, TCIFLUSH); return NULL; } + if (TTY_con.cursor >= sizeof(text) - 1) + return NULL; // push regular character TTY_con.buffer[TTY_con.cursor] = key; - TTY_con.cursor++; + TTY_con.cursor++; // next char will always be '\0' // print the current line (this is differential) - write(1, &key, 1); + size = write(STDOUT_FILENO, &key, 1); } return NULL; } - else + else if (stdin_active) { int len; fd_set fdset; struct timeval timeout; - static qboolean stdin_active; - - if (!com_dedicated || !com_dedicated->value) - return NULL; - - if (!stdin_active) - return NULL; FD_ZERO(&fdset); - FD_SET(0, &fdset); // stdin + FD_SET(STDIN_FILENO, &fdset); // stdin timeout.tv_sec = 0; timeout.tv_usec = 0; - if (select (1, &fdset, NULL, NULL, &timeout) == -1 || !FD_ISSET(0, &fdset)) - { + if(select (STDIN_FILENO + 1, &fdset, NULL, NULL, &timeout) == -1 || !FD_ISSET(STDIN_FILENO, &fdset)) return NULL; - } - len = read (0, text, sizeof(text)); + len = read(STDIN_FILENO, text, sizeof(text)); if (len == 0) { // eof! stdin_active = qfalse; @@ -439,6 +493,7 @@ char *CON_Input( void ) return text; } + return NULL; } /* @@ -448,6 +503,9 @@ CON_Print */ void CON_Print( const char *msg ) { + if (!msg[0]) + return; + CON_Hide( ); if( com_ansiColor && com_ansiColor->integer ) @@ -455,5 +513,25 @@ void CON_Print( const char *msg ) else fputs( msg, stderr ); - CON_Show( ); + if (!ttycon_on) { + // CON_Hide didn't do anything. + return; + } + + // Only print prompt when msg ends with a newline, otherwise the console + // might get garbled when output does not fit on one line. + if (msg[strlen(msg) - 1] == '\n') { + CON_Show(); + + // Run CON_Show the number of times it was deferred. + while (ttycon_show_overdue > 0) { + CON_Show(); + ttycon_show_overdue--; + } + } + else + { + // Defer calling CON_Show + ttycon_show_overdue++; + } } diff --git a/code/sys/con_win32.c b/code/sys/con_win32.c index 80285309..e273ff05 100644 --- a/code/sys/con_win32.c +++ b/code/sys/con_win32.c @@ -28,6 +28,7 @@ Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA #define QCONSOLE_HISTORY 32 static WORD qconsole_attrib; +static WORD qconsole_backgroundAttrib; // saved console status static DWORD qconsole_orig_mode; @@ -36,15 +37,50 @@ static CONSOLE_CURSOR_INFO qconsole_orig_cursorinfo; // cmd history static char qconsole_history[ QCONSOLE_HISTORY ][ MAX_EDIT_LINE ]; static int qconsole_history_pos = -1; +static int qconsole_history_lines = 0; static int qconsole_history_oldest = 0; // current edit buffer static char qconsole_line[ MAX_EDIT_LINE ]; -static size_t qconsole_linelen = 0; +static int qconsole_linelen = 0; +static qboolean qconsole_drawinput = qtrue; +static int qconsole_cursor; static HANDLE qconsole_hout; static HANDLE qconsole_hin; +/* +================== +CON_ColorCharToAttrib + +Convert Quake color character to Windows text attrib +================== +*/ +static WORD CON_ColorCharToAttrib( char color ) { + WORD attrib; + + if ( color == COLOR_WHITE ) + { + // use console's foreground and background colors + attrib = qconsole_attrib; + } + else + { + float *rgba = g_color_table[ ColorIndex( color ) ]; + + // set foreground color + attrib = ( rgba[0] >= 0.5 ? FOREGROUND_RED : 0 ) | + ( rgba[1] >= 0.5 ? FOREGROUND_GREEN : 0 ) | + ( rgba[2] >= 0.5 ? FOREGROUND_BLUE : 0 ) | + ( rgba[3] >= 0.5 ? FOREGROUND_INTENSITY : 0 ); + + // use console's background color + attrib |= qconsole_backgroundAttrib; + } + + return attrib; +} + /* ================== CON_CtrlHandler @@ -73,6 +109,9 @@ static void CON_HistAdd( void ) Q_strncpyz( qconsole_history[ qconsole_history_oldest ], qconsole_line, sizeof( qconsole_history[ qconsole_history_oldest ] ) ); + if( qconsole_history_lines < QCONSOLE_HISTORY ) + qconsole_history_lines++; + if( qconsole_history_oldest >= QCONSOLE_HISTORY - 1 ) qconsole_history_oldest = 0; else @@ -94,13 +133,14 @@ static void CON_HistPrev( void ) ( QCONSOLE_HISTORY - 1 ) : ( qconsole_history_pos - 1 ); // don' t allow looping through history - if( pos == qconsole_history_oldest ) + if( pos == qconsole_history_oldest || pos >= qconsole_history_lines ) return; qconsole_history_pos = pos; Q_strncpyz( qconsole_line, qconsole_history[ qconsole_history_pos ], sizeof( qconsole_line ) ); qconsole_linelen = strlen( qconsole_line ); + qconsole_cursor = qconsole_linelen; } /* @@ -112,14 +152,20 @@ static void CON_HistNext( void ) { int pos; + // don' t allow looping through history + if( qconsole_history_pos == qconsole_history_oldest ) + return; + pos = ( qconsole_history_pos >= QCONSOLE_HISTORY - 1 ) ? 0 : ( qconsole_history_pos + 1 ); // clear the edit buffer if they try to advance to a future command if( pos == qconsole_history_oldest ) { + qconsole_history_pos = pos; qconsole_line[ 0 ] = '\0'; qconsole_linelen = 0; + qconsole_cursor = qconsole_linelen; return; } @@ -127,6 +173,7 @@ static void CON_HistNext( void ) Q_strncpyz( qconsole_line, qconsole_history[ qconsole_history_pos ], sizeof( qconsole_line ) ); qconsole_linelen = strlen( qconsole_line ); + qconsole_cursor = qconsole_linelen; } @@ -141,13 +188,15 @@ static void CON_Show( void ) COORD writeSize = { MAX_EDIT_LINE, 1 }; COORD writePos = { 0, 0 }; SMALL_RECT writeArea = { 0, 0, 0, 0 }; + COORD cursorPos; int i; CHAR_INFO line[ MAX_EDIT_LINE ]; + WORD attrib; GetConsoleScreenBufferInfo( qconsole_hout, &binfo ); // if we're in the middle of printf, don't bother writing the buffer - if( binfo.dwCursorPosition.X != 0 ) + if( !qconsole_drawinput ) return; writeArea.Left = 0; @@ -155,15 +204,23 @@ static void CON_Show( void ) writeArea.Bottom = binfo.dwCursorPosition.Y; writeArea.Right = MAX_EDIT_LINE; + // set color to white + attrib = CON_ColorCharToAttrib( COLOR_WHITE ); + // build a space-padded CHAR_INFO array for( i = 0; i < MAX_EDIT_LINE; i++ ) { if( i < qconsole_linelen ) + { + if( i + 1 < qconsole_linelen && Q_IsColorString( qconsole_line + i ) ) + attrib = CON_ColorCharToAttrib( *( qconsole_line + i + 1 ) ); + line[ i ].Char.AsciiChar = qconsole_line[ i ]; + } else line[ i ].Char.AsciiChar = ' '; - line[ i ].Attributes = qconsole_attrib; + line[ i ].Attributes = attrib; } if( qconsole_linelen > binfo.srWindow.Right ) @@ -177,8 +234,37 @@ static void CON_Show( void ) WriteConsoleOutput( qconsole_hout, line, writeSize, writePos, &writeArea ); } + + // set curor position + cursorPos.Y = binfo.dwCursorPosition.Y; + cursorPos.X = qconsole_cursor < qconsole_linelen + ? qconsole_cursor + : qconsole_linelen > binfo.srWindow.Right + ? binfo.srWindow.Right + : qconsole_linelen; + + SetConsoleCursorPosition( qconsole_hout, cursorPos ); } +/* +================== +CON_Hide +================== +*/ +static void CON_Hide( void ) +{ + int realLen; + + realLen = qconsole_linelen; + + // remove input line from console output buffer + qconsole_linelen = 0; + CON_Show( ); + + qconsole_linelen = realLen; +} + + /* ================== CON_Shutdown @@ -186,8 +272,10 @@ CON_Shutdown */ void CON_Shutdown( void ) { + CON_Hide( ); SetConsoleMode( qconsole_hin, qconsole_orig_mode ); SetConsoleCursorInfo( qconsole_hout, &qconsole_orig_cursorinfo ); + SetConsoleTextAttribute( qconsole_hout, qconsole_attrib ); CloseHandle( qconsole_hout ); CloseHandle( qconsole_hin ); } @@ -199,7 +287,6 @@ CON_Init */ void CON_Init( void ) { - CONSOLE_CURSOR_INFO curs; CONSOLE_SCREEN_BUFFER_INFO info; int i; @@ -224,18 +311,16 @@ void CON_Init( void ) GetConsoleScreenBufferInfo( qconsole_hout, &info ); qconsole_attrib = info.wAttributes; + qconsole_backgroundAttrib = qconsole_attrib & (BACKGROUND_BLUE|BACKGROUND_GREEN|BACKGROUND_RED|BACKGROUND_INTENSITY); SetConsoleTitle(CLIENT_WINDOW_TITLE " Dedicated Server Console"); - // make cursor invisible - GetConsoleCursorInfo( qconsole_hout, &qconsole_orig_cursorinfo ); - curs.dwSize = 1; - curs.bVisible = FALSE; - SetConsoleCursorInfo( qconsole_hout, &curs ); - // initialize history for( i = 0; i < QCONSOLE_HISTORY; i++ ) qconsole_history[ i ][ 0 ] = '\0'; + + // set text color to white + SetConsoleTextAttribute( qconsole_hout, CON_ColorCharToAttrib( COLOR_WHITE ) ); } /* @@ -281,6 +366,7 @@ char *CON_Input( void ) if( key == VK_RETURN ) { newlinepos = i; + qconsole_cursor = 0; break; } else if( key == VK_UP ) @@ -293,6 +379,34 @@ char *CON_Input( void ) CON_HistNext(); break; } + else if( key == VK_LEFT ) + { + qconsole_cursor--; + if ( qconsole_cursor < 0 ) + { + qconsole_cursor = 0; + } + break; + } + else if( key == VK_RIGHT ) + { + qconsole_cursor++; + if ( qconsole_cursor > qconsole_linelen ) + { + qconsole_cursor = qconsole_linelen; + } + break; + } + else if( key == VK_HOME ) + { + qconsole_cursor = 0; + break; + } + else if( key == VK_END ) + { + qconsole_cursor = qconsole_linelen; + break; + } else if( key == VK_TAB ) { field_t f; @@ -303,6 +417,7 @@ char *CON_Input( void ) Q_strncpyz( qconsole_line, f.buffer, sizeof( qconsole_line ) ); qconsole_linelen = strlen( qconsole_line ); + qconsole_cursor = qconsole_linelen; break; } @@ -312,15 +427,33 @@ char *CON_Input( void ) if( key == VK_BACK ) { - size_t pos = ( qconsole_linelen > 0 ) ? - qconsole_linelen - 1 : 0; + if ( qconsole_cursor > 0 ) + { + int newlen = ( qconsole_linelen > 0 ) ? qconsole_linelen - 1 : 0; + if ( qconsole_cursor < qconsole_linelen ) + { + memmove( qconsole_line + qconsole_cursor - 1, + qconsole_line + qconsole_cursor, + qconsole_linelen - qconsole_cursor ); + } - qconsole_line[ pos ] = '\0'; - qconsole_linelen = pos; + qconsole_line[ newlen ] = '\0'; + qconsole_linelen = newlen; + qconsole_cursor--; + } } else if( c ) { - qconsole_line[ qconsole_linelen++ ] = c; + if ( qconsole_linelen > qconsole_cursor ) + { + memmove( qconsole_line + qconsole_cursor + 1, + qconsole_line + qconsole_cursor, + qconsole_linelen - qconsole_cursor ); + } + + qconsole_line[ qconsole_cursor++ ] = c; + + qconsole_linelen++; qconsole_line[ qconsole_linelen ] = '\0'; } } @@ -338,15 +471,74 @@ char *CON_Input( void ) return NULL; } - CON_HistAdd(); - Com_Printf( "%s\n", qconsole_line ); - qconsole_linelen = 0; CON_Show(); + CON_HistAdd(); + Com_Printf( "%s\n", qconsole_line ); + return qconsole_line; } +/* +================= +CON_WindowsColorPrint + +Set text colors based on Q3 color codes +================= +*/ +void CON_WindowsColorPrint( const char *msg ) +{ + static char buffer[ MAXPRINTMSG ]; + int length = 0; + + while( *msg ) + { + qconsole_drawinput = ( *msg == '\n' ); + + if( Q_IsColorString( msg ) || *msg == '\n' ) + { + // First empty the buffer + if( length > 0 ) + { + buffer[ length ] = '\0'; + fputs( buffer, stderr ); + length = 0; + } + + if( *msg == '\n' ) + { + // Reset color and then add the newline + SetConsoleTextAttribute( qconsole_hout, CON_ColorCharToAttrib( COLOR_WHITE ) ); + fputs( "\n", stderr ); + msg++; + } + else + { + // Set the color + SetConsoleTextAttribute( qconsole_hout, CON_ColorCharToAttrib( *( msg + 1 ) ) ); + msg += 2; + } + } + else + { + if( length >= MAXPRINTMSG - 1 ) + break; + + buffer[ length ] = *msg; + length++; + msg++; + } + } + + // Empty anything still left in the buffer + if( length > 0 ) + { + buffer[ length ] = '\0'; + fputs( buffer, stderr ); + } +} + /* ================== CON_Print @@ -354,7 +546,9 @@ CON_Print */ void CON_Print( const char *msg ) { - fputs( msg, stderr ); + CON_Hide( ); + + CON_WindowsColorPrint( msg ); CON_Show( ); } diff --git a/code/sys/sys_autoupdater.c b/code/sys/sys_autoupdater.c new file mode 100644 index 00000000..2f59e071 --- /dev/null +++ b/code/sys/sys_autoupdater.c @@ -0,0 +1,86 @@ +/* +The code in this file is in the public domain. The rest of ioquake3 +is licensed under the GPLv2. Do not mingle code, please! +*/ + +#ifdef USE_AUTOUPDATER +# ifndef AUTOUPDATER_BIN +# error The build system should have defined AUTOUPDATER_BIN +# endif + +# ifdef _WIN32 +# define WIN32_LEAN_AND_MEAN 1 +# include +# else +# include +# endif + +# include +# include +#endif + +void Sys_LaunchAutoupdater(int argc, char **argv) +{ +#ifdef USE_AUTOUPDATER + #ifdef _WIN32 + { + /* We don't need the Unix pipe() tapdance here because Windows lets children wait on parent processes. */ + PROCESS_INFORMATION procinfo; + STARTUPINFO startinfo; + char cmdline[128]; + memset(&procinfo, '\0', sizeof (procinfo)); + memset(&startinfo, '\0', sizeof (startinfo)); + startinfo.cb = sizeof (startinfo); + sprintf(cmdline, "" AUTOUPDATER_BIN " --waitpid %u", (unsigned int) GetCurrentProcessId()); + + if (CreateProcessA(AUTOUPDATER_BIN, cmdline, NULL, NULL, FALSE, CREATE_NO_WINDOW, NULL, NULL, &startinfo, &procinfo)) + { + /* close handles now so child cleans up immediately if nothing to do */ + CloseHandle(procinfo.hProcess); + CloseHandle(procinfo.hThread); + } + } + #else + int updater_pipes[2]; + if (pipe(updater_pipes) == 0) + { + pid_t pid = fork(); + if (pid == -1) /* failure, oh well. */ + { + close(updater_pipes[0]); + close(updater_pipes[1]); + } + else if (pid == 0) /* child process */ + { + close(updater_pipes[1]); /* don't need write end. */ + if (dup2(updater_pipes[0], 3) != -1) + { + char pidstr[64]; + char *ptr = strrchr(argv[0], '/'); + if (ptr) + *ptr = '\0'; + if (chdir(argv[0]) == -1) { + _exit(1); /* oh well. */ + } + #ifdef __APPLE__ + if (chdir("../..") == -1) { /* put this at base of app bundle so paths make sense later. */ + _exit(1); /* oh well. */ + } + #endif + snprintf(pidstr, sizeof (pidstr), "%lld", (long long) getppid()); + execl(AUTOUPDATER_BIN, AUTOUPDATER_BIN, "--waitpid", pidstr, NULL); + } + _exit(0); /* oh well. */ + } + else /* parent process */ + { + /* leave the write end open until we terminate so updater can block on it. */ + close(updater_pipes[0]); + } + } + #endif +#endif + + (void) argc; (void) argv; /* possibly unused. Pacify compilers. */ +} + diff --git a/code/sys/sys_loadlib.h b/code/sys/sys_loadlib.h index a7e854c6..3580b7b7 100644 --- a/code/sys/sys_loadlib.h +++ b/code/sys/sys_loadlib.h @@ -20,6 +20,8 @@ Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA =========================================================================== */ +#pragma once + #ifdef DEDICATED # ifdef _WIN32 # include @@ -33,12 +35,19 @@ Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA # define Sys_UnloadLibrary(h) dlclose(h) # define Sys_LoadFunction(h,fn) dlsym(h,fn) # define Sys_LibraryError() dlerror() -#endif +# endif #else -# include "SDL.h" -# include "SDL_loadso.h" +# ifdef USE_LOCAL_HEADERS +# include "SDL.h" +# include "SDL_loadso.h" +# else +# include +# include +# endif # define Sys_LoadLibrary(f) SDL_LoadObject(f) # define Sys_UnloadLibrary(h) SDL_UnloadObject(h) # define Sys_LoadFunction(h,fn) SDL_LoadFunction(h,fn) # define Sys_LibraryError() SDL_GetError() #endif + +void * QDECL Sys_LoadDll(const char *name, qboolean useSystemLib); diff --git a/code/sys/sys_local.h b/code/sys/sys_local.h index f1dc9de0..cd7fbe7f 100644 --- a/code/sys/sys_local.h +++ b/code/sys/sys_local.h @@ -23,20 +23,22 @@ Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA #include "../qcommon/q_shared.h" #include "../qcommon/qcommon.h" -#ifdef __cplusplus -extern "C" { +#ifndef DEDICATED +#ifdef USE_LOCAL_HEADERS +# include "SDL_version.h" +#else +# include #endif // Require a minimum version of SDL -#define MINSDL_MAJOR 1 -#define MINSDL_MINOR 2 -#define MINSDL_PATCH 7 - -// Input subsystem -void IN_Init( void *windowData ); -void IN_Frame( void ); -void IN_Shutdown( void ); -void IN_Restart( void ); +#define MINSDL_MAJOR 2 +#define MINSDL_MINOR 0 +#if SDL_VERSION_ATLEAST( 2, 0, 5 ) +#define MINSDL_PATCH 5 +#else +#define MINSDL_PATCH 0 +#endif +#endif // Console void CON_Shutdown( void ); @@ -44,18 +46,21 @@ void CON_Init( void ); char *CON_Input( void ); void CON_Print( const char *message ); -size_t CON_LogSize( void ); -size_t CON_LogWrite( const char *in ); -size_t CON_LogRead( char *out, size_t outSize ); +unsigned int CON_LogSize( void ); +unsigned int CON_LogWrite( const char *in ); +unsigned int CON_LogRead( char *out, unsigned int outSize ); -#ifdef MACOS_X +#ifdef __APPLE__ char *Sys_StripAppBundle( char *pwd ); #endif -void Sys_SigHandler( int signal ); +void Sys_GLimpSafeInit( void ); +void Sys_GLimpInit( void ); +void Sys_PlatformInit( void ); +void Sys_PlatformExit( void ); +void Sys_SigHandler( int signal ) __attribute__ ((noreturn)); void Sys_ErrorDialog( const char *error ); void Sys_AnsiColorPrint( const char *msg ); -#ifdef __cplusplus -} -#endif +int Sys_PID( void ); +qboolean Sys_PIDIsRunning( int pid ); diff --git a/code/sys/sys_main.c b/code/sys/sys_main.c index bee6cb62..3a45ae0a 100644 --- a/code/sys/sys_main.c +++ b/code/sys/sys_main.c @@ -30,16 +30,6 @@ Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA #include #include #include -#include -#include "../client/snd_public.h" - -#ifdef WIN32 -#include -#define GetCurrentDir _getcwd -#else -#include -#define GetCurrentDir getcwd -#endif #ifndef DEDICATED #ifdef USE_LOCAL_HEADERS @@ -60,6 +50,9 @@ Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA static char binaryPath[ MAX_OSPATH ] = { 0 }; static char installPath[ MAX_OSPATH ] = { 0 }; +static void* game_library = NULL; +static void* cgame_library = NULL; + /* ================= Sys_SetBinaryPath @@ -122,7 +115,15 @@ Restart the input subsystem */ void Sys_In_Restart_f( void ) { - IN_Restart(); +#ifndef DEDICATED + if( !SDL_WasInit( SDL_INIT_VIDEO ) ) + { + Com_Printf( "in_restart: Cannot restart input while video is shutdown\n" ); + return; + } +#endif + + IN_Restart( ); } /* @@ -137,6 +138,145 @@ char *Sys_ConsoleInput(void) return CON_Input( ); } +/* +================== +Sys_GetClipboardData +================== +*/ +char *Sys_GetClipboardData(void) +{ +#ifdef DEDICATED + return NULL; +#else + char *data = NULL; + char *cliptext; + + if ( ( cliptext = SDL_GetClipboardText() ) != NULL ) { + if ( cliptext[0] != '\0' ) { + size_t bufsize = strlen( cliptext ) + 1; + + data = Z_Malloc( bufsize ); + Q_strncpyz( data, cliptext, bufsize ); + + // find first listed char and set to '\0' + strtok( data, "\n\r\b" ); + } + SDL_free( cliptext ); + } + return data; +#endif +} + +#ifdef DEDICATED +# define PID_FILENAME PRODUCT_NAME "_server.pid" +#else +# define PID_FILENAME PRODUCT_NAME ".pid" +#endif + +/* +================= +Sys_PIDFileName +================= +*/ +static char *Sys_PIDFileName( const char *gamedir ) +{ + const char *homePath = Cvar_VariableString( "fs_homepath" ); + + if( *homePath != '\0' ) + return (char*)va( "%s/%s/%s", homePath, gamedir, PID_FILENAME ); + + return NULL; +} + +/* +================= +Sys_RemovePIDFile +================= +*/ +void Sys_RemovePIDFile( const char *gamedir ) +{ + const char *pidFile = Sys_PIDFileName( gamedir ); + + if( pidFile != NULL ) + remove( pidFile ); +} + +/* +================= +Sys_WritePIDFile + +Return qtrue if there is an existing stale PID file +================= +*/ +static qboolean Sys_WritePIDFile( const char *gamedir ) +{ + char *pidFile = Sys_PIDFileName( gamedir ); + FILE *f; + qboolean stale = qfalse; + + if( pidFile == NULL ) + return qfalse; + + // First, check if the pid file is already there + if( ( f = fopen( pidFile, "r" ) ) != NULL ) + { + char pidBuffer[ 64 ] = { 0 }; + int pid; + size_t len; + + len = fread( pidBuffer, sizeof( char ), sizeof( pidBuffer ) - 1, f ); + fclose( f ); + + if(len > 0) + { + pid = atoi( pidBuffer ); + if( !Sys_PIDIsRunning( pid ) ) + stale = qtrue; + } + else + stale = qtrue; + } + + if( FS_CreatePath( pidFile ) ) { + return 0; + } + + if( ( f = fopen( pidFile, "w" ) ) != NULL ) + { + fprintf( f, "%d", Sys_PID( ) ); + fclose( f ); + } + else + Com_Printf( S_COLOR_YELLOW "Couldn't write %s.\n", pidFile ); + + return stale; +} + +/* +================= +Sys_InitPIDFile +================= +*/ +void Sys_InitPIDFile( const char *gamedir ) { + if( Sys_WritePIDFile( gamedir ) ) { +#ifndef DEDICATED + char message[1024]; + char modName[MAX_OSPATH]; + + FS_GetModDescription( gamedir, modName, sizeof ( modName ) ); + Q_CleanStr( modName ); + + Com_sprintf( message, sizeof (message), "The last time %s ran, " + "it didn't exit properly. This may be due to inappropriate video " + "settings. Would you like to start with \"safe\" video settings?", modName ); + + if( Sys_Dialog( DT_YES_NO, message, "Abnormal Exit" ) == DR_YES ) { + Cvar_Set( "com_abnormalExit", "1" ); + } +#endif + } +} + /* ================= Sys_Exit @@ -144,7 +284,7 @@ Sys_Exit Single exit point (regular exit or in case of error) ================= */ -void Sys_Exit( int ex ) +static __attribute__ ((noreturn)) void Sys_Exit( int exitCode ) { CON_Shutdown( ); @@ -152,13 +292,17 @@ void Sys_Exit( int ex ) SDL_Quit( ); #endif -#ifdef NDEBUG - exit( ex ); -#else - // Cause a backtrace on error exits - assert( ex == 0 ); - exit( ex ); -#endif + if( exitCode < 2 && com_fullyInitialized ) + { + // Normal exit + Sys_RemovePIDFile( FS_GetCurrentGameDir() ); + } + + NET_Shutdown( ); + + Sys_PlatformExit( ); + + exit( exitCode ); } /* @@ -168,7 +312,6 @@ Sys_Quit */ void Sys_Quit( void ) { - CL_Shutdown( ); Sys_Exit( 0 ); } @@ -182,10 +325,12 @@ cpuFeatures_t Sys_GetProcessorFeatures( void ) cpuFeatures_t features = 0; #ifndef DEDICATED - if( SDL_HasRDTSC( ) ) features |= CF_RDTSC; - if( SDL_HasMMX( ) ) features |= CF_MMX; - if( SDL_HasSSE( ) ) features |= CF_SSE; - if( SDL_HasSSE2( ) ) features |= CF_SSE2; + if( SDL_HasRDTSC( ) ) features |= CF_RDTSC; + if( SDL_Has3DNow( ) ) features |= CF_3DNOW; + if( SDL_HasMMX( ) ) features |= CF_MMX; + if( SDL_HasSSE( ) ) features |= CF_SSE; + if( SDL_HasSSE2( ) ) features |= CF_SSE2; + if( SDL_HasAltiVec( ) ) features |= CF_ALTIVEC; #endif return features; @@ -285,31 +430,30 @@ void Sys_Print( const char *msg ) /* ================= -SyScriptError +Sys_Error ================= */ -void SyScriptError( const char *error, ... ) +void Sys_Error( const char *error, ... ) { va_list argptr; char string[1024]; - CL_Shutdown (); - va_start (argptr,error); Q_vsnprintf (string, sizeof(string), error, argptr); va_end (argptr); - //SyScriptErrorDialog( string ); + Sys_ErrorDialog( string ); - Sys_Exit( 1 ); + Sys_Exit( 3 ); } +#if 0 /* ================= Sys_Warn ================= */ -void Sys_Warn( char *warning, ... ) +static __attribute__ ((format (printf, 1, 2))) void Sys_Warn( char *warning, ... ) { va_list argptr; char string[1024]; @@ -320,6 +464,7 @@ void Sys_Warn( char *warning, ... ) CON_Print( va( "Warning: %s", string ) ); } +#endif /* ============ @@ -354,355 +499,127 @@ void Sys_UnloadDll( void *dllHandle ) Sys_UnloadLibrary(dllHandle); } -static void *cgame_library; -static void *game_library; - -/* -================= -Sys_UnloadGame -================= -*/ -void Sys_UnloadGame( void ) -{ - Com_Printf( "------ Unloading Game ------\n" ); - - if( game_library ) { - Sys_UnloadLibrary( game_library ); - } - - game_library = NULL; -} - -/* -================= -Sys_GetGameAPI -================= -*/ -void *Sys_GetGameAPI( void *parms ) -{ - void *( *GetGameAPI ) ( void * ); - const char *basepath; - const char *cdpath; - const char *gamedir; - const char *homepath; -#ifdef MACOS_X - const char *apppath; -#endif - const char *fn; - const char *gamename = "game" ARCH_STRING DLL_EXT; - - if( game_library ) - Com_Error( ERR_FATAL, "Sys_GetGameAPI without calling Sys_UnloadGame" ); - - // check the current debug directory first for development purposes - homepath = Cvar_VariableString( "fs_homepath" ); - basepath = Cvar_VariableString( "fs_basepath" ); - cdpath = Cvar_VariableString( "fs_cdpath" ); - gamedir = Cvar_VariableString( "fs_game" ); -#ifdef MACOS_X - apppath = Cvar_VariableString( "fs_apppath" ); -#endif - - fn = FS_BuildOSPath( basepath, gamedir, gamename ); - - game_library = Sys_LoadLibrary( fn ); - - //First try in mod directories. basepath -> homepath -> cdpath - if( !game_library ) { - if( homepath[ 0 ] ) { - Com_Printf( "Sys_GetGameAPI(%s) failed: \"%s\"\n", fn, Sys_LibraryError() ); - fn = FS_BuildOSPath( homepath, gamedir, gamename ); - game_library = Sys_LoadLibrary( fn ); - } - } -#ifdef MACOS_X - if( !game_library ) { - if( apppath[ 0 ] ) { - Com_Printf( "Sys_GetGameAPI(%s) failed: \"%s\"\n", fn, Sys_LibraryError() ); - fn = FS_BuildOSPath( apppath, gamedir, gamename ); - game_library = Sys_LoadLibrary( fn ); - } - } -#endif - if( !game_library ) { - if( cdpath[ 0 ] ) { - Com_Printf( "Sys_GetGameAPI(%s) failed: \"%s\"\n", fn, Sys_LibraryError() ); - fn = FS_BuildOSPath( cdpath, gamedir, gamename ); - game_library = Sys_LoadLibrary( fn ); - } - } - - //Now try in base. basepath -> homepath -> cdpath - if( !game_library ) { - Com_Printf( "Sys_GetGameAPI(%s) failed: \"%s\"\n", fn, Sys_LibraryError() ); - fn = FS_BuildOSPath( basepath, PRODUCT_NAME, gamename ); - game_library = Sys_LoadLibrary( fn ); - } - - if( !game_library ) { - if( homepath[ 0 ] ) { - Com_Printf( "Sys_GetGameAPI(%s) failed: \"%s\"\n", fn, Sys_LibraryError() ); - fn = FS_BuildOSPath( homepath, PRODUCT_NAME, gamename ); - game_library = Sys_LoadLibrary( fn ); - } - } -#ifdef MACOS_X - if( !game_library ) { - if( apppath[ 0 ] ) { - Com_Printf( "Sys_GetGameAPI(%s) failed: \"%s\"\n", fn, Sys_LibraryError() ); - fn = FS_BuildOSPath( apppath, OPENJKGAME, gamename ); - game_library = Sys_LoadLibrary( fn ); - } - } -#endif - if( !game_library ) { - if( cdpath[ 0 ] ) { - Com_Printf( "Sys_GetGameAPI(%s) failed: \"%s\"\n", fn, Sys_LibraryError() ); - fn = FS_BuildOSPath( cdpath, PRODUCT_NAME, gamename ); - game_library = Sys_LoadLibrary( fn ); - } - } - - //Still couldn't find it. - if( !game_library ) { - Com_Printf( "Sys_GetGameAPI(%s) failed: \"%s\"\n", fn, Sys_LibraryError() ); - Com_Error( ERR_FATAL, "Couldn't load game" ); - } - - Com_Printf( "Sys_GetGameAPI(%s): succeeded ...\n", fn ); - GetGameAPI = ( void *( *)( void * ) )Sys_LoadFunction( game_library, "GetGameAPI" ); - - if( !GetGameAPI ) - { - Sys_UnloadGame(); - return NULL; - } - - return GetGameAPI( parms ); -} - -/* -================= -Sys_UnloadCGame -================= -*/ -void Sys_UnloadCGame( void ) -{ - Com_Printf( "------ Unloading ClientGame ------\n" ); - - if( cgame_library ) { - Sys_UnloadLibrary( cgame_library ); - } - - cgame_library = NULL; -} - -/* -================= -Sys_GetCGameAPI -================= -*/ -void *Sys_GetCGameAPI( void *parms ) -{ - void *( *GetCGameAPI ) ( void * ); - const char *basepath; - const char *cdpath; - const char *gamedir; - const char *homepath; -#ifdef MACOS_X - const char *apppath; -#endif - const char *fn; - const char *gamename = "cgame" ARCH_STRING DLL_EXT; - - if( cgame_library ) - Com_Error( ERR_FATAL, "Sys_GetCGameAPI without calling Sys_UnloadCGame" ); - - // check the current debug directory first for development purposes - homepath = Cvar_VariableString( "fs_homepath" ); - basepath = Cvar_VariableString( "fs_basepath" ); - cdpath = Cvar_VariableString( "fs_cdpath" ); - gamedir = Cvar_VariableString( "fs_game" ); -#ifdef MACOS_X - apppath = Cvar_VariableString( "fs_apppath" ); -#endif - - fn = FS_BuildOSPath( basepath, gamedir, gamename ); - - cgame_library = Sys_LoadLibrary( fn ); - - //First try in mod directories. basepath -> homepath -> cdpath - if( !cgame_library ) { - if( homepath[ 0 ] ) { - Com_Printf( "Sys_GetCGameAPI(%s) failed: \"%s\"\n", fn, Sys_LibraryError() ); - fn = FS_BuildOSPath( homepath, gamedir, gamename ); - cgame_library = Sys_LoadLibrary( fn ); - } - } -#ifdef MACOS_X - if( !cgame_library ) { - if( apppath[ 0 ] ) { - Com_Printf( "Sys_GetCGameAPI(%s) failed: \"%s\"\n", fn, Sys_LibraryError() ); - fn = FS_BuildOSPath( apppath, gamedir, gamename ); - cgame_library = Sys_LoadLibrary( fn ); - } - } -#endif - if( !cgame_library ) { - if( cdpath[ 0 ] ) { - Com_Printf( "Sys_GetCGameAPI(%s) failed: \"%s\"\n", fn, Sys_LibraryError() ); - fn = FS_BuildOSPath( cdpath, gamedir, gamename ); - cgame_library = Sys_LoadLibrary( fn ); - } - } - - //Now try in base. basepath -> homepath -> cdpath - if( !cgame_library ) { - Com_Printf( "Sys_GetCGameAPI(%s) failed: \"%s\"\n", fn, Sys_LibraryError() ); - fn = FS_BuildOSPath( basepath, PRODUCT_NAME, gamename ); - cgame_library = Sys_LoadLibrary( fn ); - } - - if( !cgame_library ) { - if( homepath[ 0 ] ) { - Com_Printf( "Sys_GetCGameAPI(%s) failed: \"%s\"\n", fn, Sys_LibraryError() ); - fn = FS_BuildOSPath( homepath, PRODUCT_NAME, gamename ); - cgame_library = Sys_LoadLibrary( fn ); - } - } -#ifdef MACOS_X - if( !cgame_library ) { - if( apppath[ 0 ] ) { - Com_Printf( "Sys_GetCGameAPI(%s) failed: \"%s\"\n", fn, Sys_LibraryError() ); - fn = FS_BuildOSPath( apppath, OPENJKGAME, gamename ); - cgame_library = Sys_LoadLibrary( fn ); - } - } -#endif - if( !cgame_library ) { - if( cdpath[ 0 ] ) { - Com_Printf( "Sys_GetCGameAPI(%s) failed: \"%s\"\n", fn, Sys_LibraryError() ); - fn = FS_BuildOSPath( cdpath, PRODUCT_NAME, gamename ); - cgame_library = Sys_LoadLibrary( fn ); - } - } - - //Still couldn't find it. - if( !cgame_library ) { - Com_Printf( "Sys_GetCGameAPI(%s) failed: \"%s\"\n", fn, Sys_LibraryError() ); - Com_Error( ERR_FATAL, "Couldn't load game" ); - } - - Com_Printf( "Sys_GetCGameAPI(%s): succeeded ...\n", fn ); - GetCGameAPI = ( void *( *)( void * ) )Sys_LoadFunction( cgame_library, "GetCGameAPI" ); - - if( !GetCGameAPI ) - { - Sys_UnloadCGame(); - return NULL; - } - - return GetCGameAPI( parms ); -} - -/* -================= -Sys_TryLibraryLoad -================= -*/ -static void* Sys_TryLibraryLoad(const char* base, const char* gamedir, const char* fname, char* fqpath ) -{ - void* libHandle; - char* fn; - - *fqpath = 0; - - fn = FS_BuildOSPath( base, gamedir, fname ); - Com_Printf( "Sys_LoadDll(%s)... \n", fn ); - - libHandle = Sys_LoadLibrary(fn); - - if(!libHandle) { - Com_Printf( "Sys_LoadDll(%s) failed:\n\"%s\"\n", fn, Sys_LibraryError() ); - return NULL; - } - - Com_Printf ( "Sys_LoadDll(%s): succeeded ...\n", fn ); - Q_strncpyz ( fqpath , fn , MAX_QPATH ) ; - - return libHandle; -} - /* ================= Sys_LoadDll -Used to load a development dll instead of a virtual machine -#1 look down current path -#2 look in fs_homepath -#3 look in fs_basepath +First try to load library name from system library path, +from executable path, then fs_basepath. ================= */ -void *Sys_LoadDll( const char *name, char *fqpath , - intptr_t (**entryPoint)(int, ...), - intptr_t (*systemcalls)(intptr_t, ...) ) + +void *Sys_LoadDll(const char *name, qboolean useSystemLib) { - void *libHandle; - void (*dllEntry)( intptr_t (*syscallptr)(intptr_t, ...) ); - char fname[MAX_OSPATH]; - char *basepath; - char *homepath; -#ifdef MACOS_X - char *apppath; -#endif - char *pwdpath; - char *gamedir; + void *dllhandle = NULL; - assert( name ); - - Q_snprintf (fname, sizeof(fname), "%s" ARCH_STRING "opm" DLL_EXT, name); - - // TODO: use fs_searchpaths from files.c - pwdpath = Sys_Cwd(); - basepath = Cvar_VariableString( "fs_basepath" ); - homepath = Cvar_VariableString( "fs_homepath" ); -#ifdef MACOS_X - apppath = Cvar_VariableString( "fs_apppath" ); -#endif - gamedir = Cvar_VariableString( "fs_game" ); - - libHandle = Sys_TryLibraryLoad(pwdpath, gamedir, fname, fqpath); - - if(!libHandle && homepath) - libHandle = Sys_TryLibraryLoad(homepath, gamedir, fname, fqpath); - - if(!libHandle && basepath) - libHandle = Sys_TryLibraryLoad(basepath, gamedir, fname, fqpath); -#ifdef MACOS_X - if(!libHandle && apppath) - libHandle = Sys_TryLibraryLoad(apppath, gamedir, fname, fqpath); -#endif - - if(!libHandle) { - Com_Printf ( "Sys_LoadDll(%s) failed to load library\n", name ); + if(!Sys_DllExtension(name)) + { + Com_Printf("Refusing to attempt to load library \"%s\": Extension not allowed.\n", name); return NULL; } - dllEntry = Sys_LoadFunction( libHandle, "dllEntry" ); - *entryPoint = Sys_LoadFunction( libHandle, "vmMain" ); - - if ( !*entryPoint || !dllEntry ) + if(useSystemLib) { - Com_Printf ( "Sys_LoadDll(%s) failed to find vmMain function:\n\"%s\" !\n", name, Sys_LibraryError( ) ); + Com_Printf("Trying to load \"%s\"...\n", name); + dllhandle = Sys_LoadLibrary(name); + } + + if(!dllhandle) + { + const char *topDir; + char libPath[MAX_OSPATH]; + int len; + + topDir = Sys_BinaryPath(); + + if(!*topDir) + topDir = "."; + + len = Com_sprintf(libPath, sizeof(libPath), "%s%c%s", topDir, PATH_SEP, name); + if(len < sizeof(libPath)) + { + Com_Printf("Trying to load \"%s\" from \"%s\"...\n", name, topDir); + dllhandle = Sys_LoadLibrary(libPath); + } + else + { + Com_Printf("Skipping trying to load \"%s\" from \"%s\", file name is too long.\n", name, topDir); + } + + if(!dllhandle) + { + const char *basePath = Cvar_VariableString("fs_basepath"); + + if(!basePath || !*basePath) + basePath = "."; + + if(FS_FilenameCompare(topDir, basePath)) + { + len = Com_sprintf(libPath, sizeof(libPath), "%s%c%s", basePath, PATH_SEP, name); + if(len < sizeof(libPath)) + { + Com_Printf("Trying to load \"%s\" from \"%s\"...\n", name, basePath); + dllhandle = Sys_LoadLibrary(libPath); + } + else + { + Com_Printf("Skipping trying to load \"%s\" from \"%s\", file name is too long.\n", name, basePath); + } + } + + if(!dllhandle) + Com_Printf("Loading \"%s\" failed\n", name); + } + } + + return dllhandle; +} + +/* +================= +Sys_LoadGameDll + +Used to load a development dll instead of a virtual machine +================= +*/ +void *Sys_LoadGameDll(const char *name, const char* entryPointName, void* imports, void** pLibHandle) +{ + void *libHandle; + void* (*GetGameAPI)(void* imports); + + assert(name); + + if(!Sys_DllExtension(name)) + { + Com_Printf("Refusing to attempt to load library \"%s\": Extension not allowed.\n", name); + return NULL; + } + + Com_Printf( "Loading DLL file: %s\n", name); + libHandle = Sys_LoadLibrary(name); + + if(!libHandle) + { + Com_Printf("Sys_LoadGameDll(%s) failed:\n\"%s\"\n", name, Sys_LibraryError()); + return NULL; + } + + GetGameAPI = Sys_LoadFunction(libHandle, entryPointName); + + if (!GetGameAPI) + { + Com_Printf ( "Sys_LoadGameDll(%s) failed to find %s function:\n\"%s\" !\n", name, entryPointName, Sys_LibraryError( ) ); Sys_UnloadLibrary(libHandle); return NULL; } - Com_Printf ( "Sys_LoadDll(%s) found vmMain function at %p\n", name, *entryPoint ); - dllEntry( systemcalls ); + Com_Printf("Sys_LoadGameDll(%s) found %s function at %p\n", name, entryPointName, GetGameAPI); + if (pLibHandle) { + *pLibHandle = libHandle; + } - return libHandle; + return GetGameAPI(imports); } /* @@ -717,19 +634,19 @@ void Sys_ParseArgs( int argc, char **argv ) if( !strcmp( argv[1], "--version" ) || !strcmp( argv[1], "-v" ) ) { - const char* date = __DATE__; + const char* date = PRODUCT_DATE; #ifdef DEDICATED fprintf( stdout, PRODUCT_VERSION " dedicated server (%s)\n", date ); #else fprintf( stdout, PRODUCT_VERSION " client (%s)\n", date ); #endif - Sys_Exit(0); + Sys_Exit( 0 ); } } } #ifndef DEFAULT_BASEDIR -# ifdef MACOS_X +# ifdef __APPLE__ # define DEFAULT_BASEDIR Sys_StripAppBundle(Sys_BinaryPath()) # else # define DEFAULT_BASEDIR Sys_BinaryPath() @@ -753,14 +670,263 @@ void Sys_SigHandler( int signal ) else { signalcaught = qtrue; - fprintf( stderr, "Received signal %d, exiting...\n", signal ); #ifndef DEDICATED - CL_Shutdown(); + CL_Shutdown(va("Received signal %d", signal), qtrue, qtrue); #endif - SV_Shutdown( "Signal caught" ); + SV_Shutdown(va("Received signal %d", signal) ); } - Sys_Exit( 0 ); // Exit with 0 to avoid recursive signals + if( signal == SIGTERM || signal == SIGINT ) + Sys_Exit( 1 ); + else + Sys_Exit( 2 ); +} +/* +================= +Sys_UnloadGame +================= +*/ +void Sys_UnloadGame(void) +{ + Com_Printf("------ Unloading Game ------\n"); + + if (game_library) { + Sys_UnloadLibrary(game_library); + } + + game_library = NULL; +} + +/* +================= +Sys_GetGameAPI +================= +*/ +void* Sys_GetGameAPI(void* parms) +{ + void* (*GetGameAPI) (void*); + const char* basepath; + const char* cdpath; + const char* gamedir; + const char* homepath; +#ifdef MACOS_X + const char* apppath; +#endif + const char* fn; + const char* gamename = "game" ARCH_STRING DLL_EXT; + + if (game_library) + Com_Error(ERR_FATAL, "Sys_GetGameAPI without calling Sys_UnloadGame"); + + // check the current debug directory first for development purposes + homepath = Cvar_VariableString("fs_homepath"); + basepath = Cvar_VariableString("fs_basepath"); + cdpath = Cvar_VariableString("fs_cdpath"); + gamedir = Cvar_VariableString("fs_game"); +#ifdef MACOS_X + apppath = Cvar_VariableString("fs_apppath"); +#endif + + fn = FS_BuildOSPath(basepath, gamedir, gamename); + + game_library = Sys_LoadLibrary(fn); + + //First try in mod directories. basepath -> homepath -> cdpath + if (!game_library) { + if (homepath[0]) { + Com_Printf("Sys_GetGameAPI(%s) failed: \"%s\"\n", fn, Sys_LibraryError()); + fn = FS_BuildOSPath(homepath, gamedir, gamename); + game_library = Sys_LoadLibrary(fn); + } + } +#ifdef MACOS_X + if (!game_library) { + if (apppath[0]) { + Com_Printf("Sys_GetGameAPI(%s) failed: \"%s\"\n", fn, Sys_LibraryError()); + fn = FS_BuildOSPath(apppath, gamedir, gamename); + game_library = Sys_LoadLibrary(fn); + } + } +#endif + if (!game_library) { + if (cdpath[0]) { + Com_Printf("Sys_GetGameAPI(%s) failed: \"%s\"\n", fn, Sys_LibraryError()); + fn = FS_BuildOSPath(cdpath, gamedir, gamename); + game_library = Sys_LoadLibrary(fn); + } + } + + //Now try in base. basepath -> homepath -> cdpath + if (!game_library) { + Com_Printf("Sys_GetGameAPI(%s) failed: \"%s\"\n", fn, Sys_LibraryError()); + fn = FS_BuildOSPath(basepath, PRODUCT_NAME, gamename); + game_library = Sys_LoadLibrary(fn); + } + + if (!game_library) { + if (homepath[0]) { + Com_Printf("Sys_GetGameAPI(%s) failed: \"%s\"\n", fn, Sys_LibraryError()); + fn = FS_BuildOSPath(homepath, PRODUCT_NAME, gamename); + game_library = Sys_LoadLibrary(fn); + } + } +#ifdef MACOS_X + if (!game_library) { + if (apppath[0]) { + Com_Printf("Sys_GetGameAPI(%s) failed: \"%s\"\n", fn, Sys_LibraryError()); + fn = FS_BuildOSPath(apppath, OPENJKGAME, gamename); + game_library = Sys_LoadLibrary(fn); + } + } +#endif + if (!game_library) { + if (cdpath[0]) { + Com_Printf("Sys_GetGameAPI(%s) failed: \"%s\"\n", fn, Sys_LibraryError()); + fn = FS_BuildOSPath(cdpath, PRODUCT_NAME, gamename); + game_library = Sys_LoadLibrary(fn); + } + } + + //Still couldn't find it. + if (!game_library) { + Com_Printf("Sys_GetGameAPI(%s) failed: \"%s\"\n", fn, Sys_LibraryError()); + Com_Error(ERR_FATAL, "Couldn't load game"); + } + + Com_Printf("Sys_GetGameAPI(%s): succeeded ...\n", fn); + GetGameAPI = (void* (*)(void*))Sys_LoadFunction(game_library, "GetGameAPI"); + + if (!GetGameAPI) + { + Sys_UnloadGame(); + return NULL; + } + + return GetGameAPI(parms); +} + +/* +================= +Sys_UnloadCGame +================= +*/ +void Sys_UnloadCGame(void) +{ + Com_Printf("------ Unloading ClientGame ------\n"); + + if (cgame_library) { + Sys_UnloadLibrary(cgame_library); + } + + cgame_library = NULL; +} + +/* +================= +Sys_GetCGameAPI +================= +*/ +void* Sys_GetCGameAPI(void* parms) +{ + void* (*GetCGameAPI) (void*); + const char* basepath; + const char* cdpath; + const char* gamedir; + const char* homepath; +#ifdef MACOS_X + const char* apppath; +#endif + const char* fn; + const char* gamename = "cgame" ARCH_STRING DLL_EXT; + + if (cgame_library) + Com_Error(ERR_FATAL, "Sys_GetCGameAPI without calling Sys_UnloadCGame"); + + // check the current debug directory first for development purposes + homepath = Cvar_VariableString("fs_homepath"); + basepath = Cvar_VariableString("fs_basepath"); + cdpath = Cvar_VariableString("fs_cdpath"); + gamedir = Cvar_VariableString("fs_game"); +#ifdef MACOS_X + apppath = Cvar_VariableString("fs_apppath"); +#endif + + fn = FS_BuildOSPath(basepath, gamedir, gamename); + + cgame_library = Sys_LoadLibrary(fn); + + //First try in mod directories. basepath -> homepath -> cdpath + if (!cgame_library) { + if (homepath[0]) { + Com_Printf("Sys_GetCGameAPI(%s) failed: \"%s\"\n", fn, Sys_LibraryError()); + fn = FS_BuildOSPath(homepath, gamedir, gamename); + cgame_library = Sys_LoadLibrary(fn); + } + } +#ifdef MACOS_X + if (!cgame_library) { + if (apppath[0]) { + Com_Printf("Sys_GetCGameAPI(%s) failed: \"%s\"\n", fn, Sys_LibraryError()); + fn = FS_BuildOSPath(apppath, gamedir, gamename); + cgame_library = Sys_LoadLibrary(fn); + } + } +#endif + if (!cgame_library) { + if (cdpath[0]) { + Com_Printf("Sys_GetCGameAPI(%s) failed: \"%s\"\n", fn, Sys_LibraryError()); + fn = FS_BuildOSPath(cdpath, gamedir, gamename); + cgame_library = Sys_LoadLibrary(fn); + } + } + + //Now try in base. basepath -> homepath -> cdpath + if (!cgame_library) { + Com_Printf("Sys_GetCGameAPI(%s) failed: \"%s\"\n", fn, Sys_LibraryError()); + fn = FS_BuildOSPath(basepath, PRODUCT_NAME, gamename); + cgame_library = Sys_LoadLibrary(fn); + } + + if (!cgame_library) { + if (homepath[0]) { + Com_Printf("Sys_GetCGameAPI(%s) failed: \"%s\"\n", fn, Sys_LibraryError()); + fn = FS_BuildOSPath(homepath, PRODUCT_NAME, gamename); + cgame_library = Sys_LoadLibrary(fn); + } + } +#ifdef MACOS_X + if (!cgame_library) { + if (apppath[0]) { + Com_Printf("Sys_GetCGameAPI(%s) failed: \"%s\"\n", fn, Sys_LibraryError()); + fn = FS_BuildOSPath(apppath, OPENJKGAME, gamename); + cgame_library = Sys_LoadLibrary(fn); + } + } +#endif + if (!cgame_library) { + if (cdpath[0]) { + Com_Printf("Sys_GetCGameAPI(%s) failed: \"%s\"\n", fn, Sys_LibraryError()); + fn = FS_BuildOSPath(cdpath, PRODUCT_NAME, gamename); + cgame_library = Sys_LoadLibrary(fn); + } + } + + //Still couldn't find it. + if (!cgame_library) { + Com_Printf("Sys_GetCGameAPI(%s) failed: \"%s\"\n", fn, Sys_LibraryError()); + Com_Error(ERR_FATAL, "Couldn't load game"); + } + + Com_Printf("Sys_GetCGameAPI(%s): succeeded ...\n", fn); + GetCGameAPI = (void* (*)(void*))Sys_LoadFunction(cgame_library, "GetCGameAPI"); + + if (!GetCGameAPI) + { + Sys_UnloadCGame(); + return NULL; + } + + return GetCGameAPI(parms); } /* @@ -772,7 +938,9 @@ int main( int argc, char **argv ) { int i; char commandLine[ MAX_STRING_CHARS ] = { 0 }; - char szPath[ MAX_QPATH ]; + + extern void Sys_LaunchAutoupdater(int argc, char **argv); + Sys_LaunchAutoupdater(argc, argv); #ifndef DEDICATED // SDL version check @@ -792,7 +960,7 @@ int main( int argc, char **argv ) XSTRING(MINSDL_PATCH) if( SDL_VERSIONNUM( ver.major, ver.minor, ver.patch ) < - SDL_VERSIONNUM( MINSDL_MAJOR, MINSDL_MINOR, MINSDL_PATCH ) ) + SDL_VERSIONNUM( MINSDL_MAJOR, MINSDL_MINOR, MINSDL_PATCH ) ) { Sys_Dialog( DT_ERROR, va( "SDL version " MINSDL_VERSION " or greater is required, " "but only version %d.%d.%d was found. You may be able to obtain a more recent copy " @@ -802,47 +970,48 @@ int main( int argc, char **argv ) } #endif + Sys_PlatformInit( ); + + // Set the initial time base + Sys_Milliseconds( ); + +#ifdef __APPLE__ + // This is passed if we are launched by double-clicking + if ( argc >= 2 && Q_strncmp ( argv[1], "-psn", 4 ) == 0 ) + argc = 1; +#endif + Sys_ParseArgs( argc, argv ); - GetCurrentDir( szPath, sizeof( szPath ) ); - Sys_SetBinaryPath( szPath ); + Sys_SetBinaryPath( Sys_Dirname( argv[ 0 ] ) ); Sys_SetDefaultInstallPath( DEFAULT_BASEDIR ); - SetProgramPath( DEFAULT_BASEDIR ); // Concatenate the command line for passing to Com_Init for( i = 1; i < argc; i++ ) { + const qboolean containsSpaces = strchr(argv[i], ' ') != NULL; + if (containsSpaces) + Q_strcat( commandLine, sizeof( commandLine ), "\"" ); + Q_strcat( commandLine, sizeof( commandLine ), argv[ i ] ); + + if (containsSpaces) + Q_strcat( commandLine, sizeof( commandLine ), "\"" ); + Q_strcat( commandLine, sizeof( commandLine ), " " ); } - Com_Init( commandLine ); - Sys_InitLocalization(); - NET_Init( ); - CON_Init( ); - -#ifndef _DEBUG - -#ifndef _WIN32 - // Windows doesn't have these signals - // see CON_CtrlHandler() in con_win32.c - signal( SIGHUP, Sys_SigHandler ); - signal( SIGQUIT, Sys_SigHandler ); - signal( SIGTRAP, Sys_SigHandler ); - signal( SIGIOT, Sys_SigHandler ); - signal( SIGBUS, Sys_SigHandler ); -#endif + Com_Init( commandLine ); + NET_Init( ); signal( SIGILL, Sys_SigHandler ); signal( SIGFPE, Sys_SigHandler ); signal( SIGSEGV, Sys_SigHandler ); signal( SIGTERM, Sys_SigHandler ); - -#endif + signal( SIGINT, Sys_SigHandler ); while( 1 ) { - IN_Frame( ); Com_Frame( ); } diff --git a/code/sys/sys_unix.c b/code/sys/sys_unix.c index ce678ffc..dfb0c0df 100644 --- a/code/sys/sys_unix.c +++ b/code/sys/sys_unix.c @@ -24,6 +24,7 @@ Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA #include "../qcommon/qcommon.h" #include "sys_local.h" +#include #include #include #include @@ -34,10 +35,24 @@ Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA #include #include #include +#include +#include +#include + +qboolean stdinIsATTY; // Used to determine where to store user-specific files static char homePath[ MAX_OSPATH ] = { 0 }; +// Used to store the Steam Quake 3 installation path +static char steamPath[ MAX_OSPATH ] = { 0 }; + +// Used to store the GOG Quake 3 installation path +static char gogPath[ MAX_OSPATH ] = { 0 }; + +// Used to store the Microsoft Store Quake 3 installation path +static char microsoftStorePath[MAX_OSPATH] = { 0 }; + /* ================== Sys_DefaultHomePath @@ -47,30 +62,79 @@ char *Sys_DefaultHomePath(void) { char *p; - if( !*homePath ) + if( !*homePath && com_homepath != NULL ) { if( ( p = getenv( "HOME" ) ) != NULL ) { - Q_strncpyz( homePath, p, sizeof( homePath ) ); -#ifdef MACOS_X - Q_strcat( homePath, sizeof( homePath ), "/Library/Application Support/OpenMoHAA" ); + Com_sprintf(homePath, sizeof(homePath), "%s%c", p, PATH_SEP); +#ifdef __APPLE__ + Q_strcat(homePath, sizeof(homePath), + "Library/Application Support/"); + + if(com_homepath->string[0]) + Q_strcat(homePath, sizeof(homePath), com_homepath->string); + else + Q_strcat(homePath, sizeof(homePath), HOMEPATH_NAME_MACOSX); #else - Q_strcat( homePath, sizeof( homePath ), "/.openmohaa" ); + if(com_homepath->string[0]) + Q_strcat(homePath, sizeof(homePath), com_homepath->string); + else + Q_strcat(homePath, sizeof(homePath), HOMEPATH_NAME_UNIX); #endif - if( mkdir( homePath, 0777 ) ) - { - if( errno != EEXIST ) - { - Com_Error( "Unable to create directory \"%s\", error is %s(%d)\n", - homePath, strerror( errno ), errno ); - } - } } } return homePath; } +/* +================ +Sys_SteamPath +================ +*/ +char *Sys_SteamPath( void ) +{ + // Disabled since Steam doesn't let you install Quake 3 on Mac/Linux +#if 0 //#ifdef STEAMPATH_NAME + char *p; + + if( ( p = getenv( "HOME" ) ) != NULL ) + { +#ifdef __APPLE__ + char *steamPathEnd = "/Library/Application Support/Steam/SteamApps/common/" STEAMPATH_NAME; +#else + char *steamPathEnd = "/.steam/steam/SteamApps/common/" STEAMPATH_NAME; +#endif + Com_sprintf(steamPath, sizeof(steamPath), "%s%s", p, steamPathEnd); + } +#endif + + return steamPath; +} + +/* +================ +Sys_GogPath +================ +*/ +char *Sys_GogPath( void ) +{ + // GOG also doesn't let you install Quake 3 on Mac/Linux + return gogPath; +} + +/* +================ +Sys_MicrosoftStorePath +================ +*/ +char* Sys_MicrosoftStorePath(void) +{ + // Microsoft Store doesn't exist on Mac/Linux + return microsoftStorePath; +} + + /* ================ Sys_Milliseconds @@ -78,8 +142,7 @@ Sys_Milliseconds */ /* base time in seconds, that's our origin timeval:tv_sec is an int: - assuming this wraps every 0x7fffffff - ~68 years since the Epoch (1970) - we're safe till 2038 - using unsigned long data type to work right with Sys_XTimeToSysTime */ + assuming this wraps every 0x7fffffff - ~68 years since the Epoch (1970) - we're safe till 2038 */ unsigned long sys_timeBase = 0; /* current time in ms, using sys_timeBase as origin NOTE: sys_timeBase*1000 + curtime -> ms since the Epoch @@ -104,31 +167,6 @@ int Sys_Milliseconds (void) return curtime; } -#if !id386 -/* -================== -fastftol -================== -*/ -long fastftol( float f ) -{ - return (long)f; -} - -/* -================== -Sys_SnapVector -================== -*/ -void Sys_SnapVector( float *v ) -{ - v[0] = rint(v[0]); - v[1] = rint(v[1]); - v[2] = rint(v[2]); -} -#endif - - /* ================== Sys_RandomBytes @@ -142,7 +180,9 @@ qboolean Sys_RandomBytes( byte *string, int len ) if( !fp ) return qfalse; - if( !fread( string, sizeof( byte ), len, fp ) ) + setvbuf( fp, NULL, _IONBF, 0 ); // don't buffer reads from /dev/urandom + + if( fread( string, sizeof( byte ), len, fp ) != len ) { fclose( fp ); return qfalse; @@ -167,16 +207,6 @@ char *Sys_GetCurrentUser( void ) return p->pw_name; } -/* -================== -Sys_GetClipboardData -================== -*/ -char *Sys_GetClipboardData(void) -{ - return NULL; -} - #define MEM_THRESHOLD 96*1024*1024 /* @@ -211,14 +241,64 @@ const char *Sys_Dirname( char *path ) return dirname( path ); } +/* +============== +Sys_FOpen +============== +*/ +FILE *Sys_FOpen( const char *ospath, const char *mode ) { + struct stat buf; + + // check if path exists and is a directory + if ( !stat( ospath, &buf ) && S_ISDIR( buf.st_mode ) ) + return NULL; + + return fopen( ospath, mode ); +} + /* ================== Sys_Mkdir ================== */ -void Sys_Mkdir( const char *path ) +qboolean Sys_Mkdir( const char *path ) { - mkdir( path, 0777 ); + int result = mkdir( path, 0750 ); + + if( result != 0 ) + return errno == EEXIST; + + return qtrue; +} + +/* +================== +Sys_Mkfifo +================== +*/ +FILE *Sys_Mkfifo( const char *ospath ) +{ + FILE *fifo; + int result; + int fn; + struct stat buf; + + // if file already exists AND is a pipefile, remove it + if( !stat( ospath, &buf ) && S_ISFIFO( buf.st_mode ) ) + FS_Remove( ospath ); + + result = mkfifo( ospath, 0600 ); + if( result != 0 ) + return NULL; + + fifo = fopen( ospath, "w+" ); + if( fifo ) + { + fn = fileno( fifo ); + fcntl( fn, F_SETFL, O_NONBLOCK ); + } + + return fifo; } /* @@ -230,7 +310,10 @@ char *Sys_Cwd( void ) { static char cwd[MAX_OSPATH]; - getcwd( cwd, sizeof( cwd ) - 1 ); + char *result = getcwd( cwd, sizeof( cwd ) - 1 ); + if( result != cwd ) + return NULL; + cwd[MAX_OSPATH-1] = 0; return cwd; @@ -369,9 +452,9 @@ char **Sys_ListFiles( const char *directory, const char *extension, char *filter continue; if (*extension) { - if ( strlen( d->d_name ) < strlen( extension ) || + if ( strlen( d->d_name ) < extLen || Q_stricmp( - d->d_name + strlen( d->d_name ) - strlen( extension ), + d->d_name + strlen( d->d_name ) - extLen, extension ) ) { continue; // didn't match } @@ -423,59 +506,44 @@ void Sys_FreeFileList( char **list ) Z_Free( list ); } -#ifdef MACOS_X -/* -================= -Sys_StripAppBundle - -Discovers if passed dir is suffixed with the directory structure of a Mac OS X -.app bundle. If it is, the .app directory structure is stripped off the end and -the result is returned. If not, dir is returned untouched. -================= -*/ -char *Sys_StripAppBundle( char *dir ) -{ - static char cwd[MAX_OSPATH]; - - Q_strncpyz(cwd, dir, sizeof(cwd)); - if(strcmp(Sys_Basename(cwd), "MacOS")) - return dir; - Q_strncpyz(cwd, Sys_Dirname(cwd), sizeof(cwd)); - if(strcmp(Sys_Basename(cwd), "Contents")) - return dir; - Q_strncpyz(cwd, Sys_Dirname(cwd), sizeof(cwd)); - if(!strstr(Sys_Basename(cwd), ".app")) - return dir; - Q_strncpyz(cwd, Sys_Dirname(cwd), sizeof(cwd)); - return cwd; -} -#endif // MACOS_X - - /* ================== Sys_Sleep -Block execution for msec or until input is recieved. +Block execution for msec or until input is received. ================== */ void Sys_Sleep( int msec ) { - fd_set fdset; + if( msec == 0 ) + return; - FD_ZERO(&fdset); - FD_SET(fileno(stdin), &fdset); - if( msec < 0 ) + if( stdinIsATTY ) { - select((fileno(stdin) + 1), &fdset, NULL, NULL, NULL); + fd_set fdset; + + FD_ZERO(&fdset); + FD_SET(STDIN_FILENO, &fdset); + if( msec < 0 ) + { + select(STDIN_FILENO + 1, &fdset, NULL, NULL, NULL); + } + else + { + struct timeval timeout; + + timeout.tv_sec = msec/1000; + timeout.tv_usec = (msec%1000)*1000; + select(STDIN_FILENO + 1, &fdset, NULL, NULL, &timeout); + } } else { - struct timeval timeout; + // With nothing to select() on, we can't wait indefinitely + if( msec < 0 ) + msec = 10; - timeout.tv_sec = msec/1000; - timeout.tv_usec = (msec%1000)*1000; - select((fileno(stdin) + 1), &fdset, NULL, NULL, &timeout); + usleep( msec * 1000 ); } } @@ -490,21 +558,421 @@ void Sys_ErrorDialog( const char *error ) { char buffer[ 1024 ]; unsigned int size; - fileHandle_t f; + int f = -1; + const char *homepath = Cvar_VariableString( "fs_homepath" ); + const char *gamedir = Cvar_VariableString( "fs_game" ); const char *fileName = "crashlog.txt"; + char *dirpath = FS_BuildOSPath( homepath, gamedir, ""); + char *ospath = FS_BuildOSPath( homepath, gamedir, fileName ); Sys_Print( va( "%s\n", error ) ); - // Write console log to file - f = FS_FOpenFileWrite( fileName ); - if( !f ) +#ifndef DEDICATED + Sys_Dialog( DT_ERROR, va( "%s. See \"%s\" for details.", error, ospath ), "Error" ); +#endif + + // Make sure the write path for the crashlog exists... + + if(!Sys_Mkdir(homepath)) + { + Com_Printf("ERROR: couldn't create path '%s' for crash log.\n", homepath); + return; + } + + if(!Sys_Mkdir(dirpath)) + { + Com_Printf("ERROR: couldn't create path '%s' for crash log.\n", dirpath); + return; + } + + // We might be crashing because we maxed out the Quake MAX_FILE_HANDLES, + // which will come through here, so we don't want to recurse forever by + // calling FS_FOpenFileWrite()...use the Unix system APIs instead. + f = open( ospath, O_CREAT | O_TRUNC | O_WRONLY, 0640 ); + if( f == -1 ) { Com_Printf( "ERROR: couldn't open %s\n", fileName ); return; } - while( ( size = CON_LogRead( buffer, sizeof( buffer ) ) ) > 0 ) - FS_Write( buffer, size, f ); + // We're crashing, so we don't care much if write() or close() fails. + while( ( size = CON_LogRead( buffer, sizeof( buffer ) ) ) > 0 ) { + if( write( f, buffer, size ) != size ) { + Com_Printf( "ERROR: couldn't fully write to %s\n", fileName ); + break; + } + } - FS_FCloseFile( f ); + close( f ); +} + +#ifndef __APPLE__ +static char execBuffer[ 1024 ]; +static char *execBufferPointer; +static char *execArgv[ 16 ]; +static int execArgc; + +/* +============== +Sys_ClearExecBuffer +============== +*/ +static void Sys_ClearExecBuffer( void ) +{ + execBufferPointer = execBuffer; + Com_Memset( execArgv, 0, sizeof( execArgv ) ); + execArgc = 0; +} + +/* +============== +Sys_AppendToExecBuffer +============== +*/ +static void Sys_AppendToExecBuffer( const char *text ) +{ + size_t size = sizeof( execBuffer ) - ( execBufferPointer - execBuffer ); + int length = strlen( text ) + 1; + + if( length > size || execArgc >= ARRAY_LEN( execArgv ) ) + return; + + Q_strncpyz( execBufferPointer, text, size ); + execArgv[ execArgc++ ] = execBufferPointer; + + execBufferPointer += length; +} + +/* +============== +Sys_Exec +============== +*/ +static int Sys_Exec( void ) +{ + pid_t pid = fork( ); + + if( pid < 0 ) + return -1; + + if( pid ) + { + // Parent + int exitCode; + + wait( &exitCode ); + + return WEXITSTATUS( exitCode ); + } + else + { + // Child + execvp( execArgv[ 0 ], execArgv ); + + // Failed to execute + exit( -1 ); + + return -1; + } +} + +/* +============== +Sys_ZenityCommand +============== +*/ +static void Sys_ZenityCommand( dialogType_t type, const char *message, const char *title ) +{ + Sys_ClearExecBuffer( ); + Sys_AppendToExecBuffer( "zenity" ); + + switch( type ) + { + default: + case DT_INFO: Sys_AppendToExecBuffer( "--info" ); break; + case DT_WARNING: Sys_AppendToExecBuffer( "--warning" ); break; + case DT_ERROR: Sys_AppendToExecBuffer( "--error" ); break; + case DT_YES_NO: + Sys_AppendToExecBuffer( "--question" ); + Sys_AppendToExecBuffer( "--ok-label=Yes" ); + Sys_AppendToExecBuffer( "--cancel-label=No" ); + break; + + case DT_OK_CANCEL: + Sys_AppendToExecBuffer( "--question" ); + Sys_AppendToExecBuffer( "--ok-label=OK" ); + Sys_AppendToExecBuffer( "--cancel-label=Cancel" ); + break; + } + + Sys_AppendToExecBuffer( va( "--text=%s", message ) ); + Sys_AppendToExecBuffer( va( "--title=%s", title ) ); +} + +/* +============== +Sys_KdialogCommand +============== +*/ +static void Sys_KdialogCommand( dialogType_t type, const char *message, const char *title ) +{ + Sys_ClearExecBuffer( ); + Sys_AppendToExecBuffer( "kdialog" ); + + switch( type ) + { + default: + case DT_INFO: Sys_AppendToExecBuffer( "--msgbox" ); break; + case DT_WARNING: Sys_AppendToExecBuffer( "--sorry" ); break; + case DT_ERROR: Sys_AppendToExecBuffer( "--error" ); break; + case DT_YES_NO: Sys_AppendToExecBuffer( "--warningyesno" ); break; + case DT_OK_CANCEL: Sys_AppendToExecBuffer( "--warningcontinuecancel" ); break; + } + + Sys_AppendToExecBuffer( message ); + Sys_AppendToExecBuffer( va( "--title=%s", title ) ); +} + +/* +============== +Sys_XmessageCommand +============== +*/ +static void Sys_XmessageCommand( dialogType_t type, const char *message, const char *title ) +{ + Sys_ClearExecBuffer( ); + Sys_AppendToExecBuffer( "xmessage" ); + Sys_AppendToExecBuffer( "-buttons" ); + + switch( type ) + { + default: Sys_AppendToExecBuffer( "OK:0" ); break; + case DT_YES_NO: Sys_AppendToExecBuffer( "Yes:0,No:1" ); break; + case DT_OK_CANCEL: Sys_AppendToExecBuffer( "OK:0,Cancel:1" ); break; + } + + Sys_AppendToExecBuffer( "-center" ); + Sys_AppendToExecBuffer( message ); +} + +/* +============== +Sys_Dialog + +Display a *nix dialog box +============== +*/ +dialogResult_t Sys_Dialog( dialogType_t type, const char *message, const char *title ) +{ + typedef enum + { + NONE = 0, + ZENITY, + KDIALOG, + XMESSAGE, + NUM_DIALOG_PROGRAMS + } dialogCommandType_t; + typedef void (*dialogCommandBuilder_t)( dialogType_t, const char *, const char * ); + + const char *session = getenv( "DESKTOP_SESSION" ); + qboolean tried[ NUM_DIALOG_PROGRAMS ] = { qfalse }; + dialogCommandBuilder_t commands[ NUM_DIALOG_PROGRAMS ] = { NULL }; + dialogCommandType_t preferredCommandType = NONE; + int i; + + commands[ ZENITY ] = &Sys_ZenityCommand; + commands[ KDIALOG ] = &Sys_KdialogCommand; + commands[ XMESSAGE ] = &Sys_XmessageCommand; + + // This may not be the best way + if( !Q_stricmp( session, "gnome" ) ) + preferredCommandType = ZENITY; + else if( !Q_stricmp( session, "kde" ) ) + preferredCommandType = KDIALOG; + + for( i = NONE + 1; i < NUM_DIALOG_PROGRAMS; i++ ) + { + if( preferredCommandType != NONE && preferredCommandType != i ) + continue; + + if( !tried[ i ] ) + { + int exitCode; + + commands[ i ]( type, message, title ); + exitCode = Sys_Exec( ); + + if( exitCode >= 0 ) + { + switch( type ) + { + case DT_YES_NO: return exitCode ? DR_NO : DR_YES; + case DT_OK_CANCEL: return exitCode ? DR_CANCEL : DR_OK; + default: return DR_OK; + } + } + + tried[ i ] = qtrue; + + // The preference failed, so start again in order + if( preferredCommandType != NONE ) + { + preferredCommandType = NONE; + i = NONE + 1; + } + } + } + + Com_DPrintf( S_COLOR_YELLOW "WARNING: failed to show a dialog\n" ); + return DR_OK; +} +#endif + +/* +============== +Sys_GLimpSafeInit + +Unix specific "safe" GL implementation initialisation +============== +*/ +void Sys_GLimpSafeInit( void ) +{ + // NOP +} + +/* +============== +Sys_GLimpInit + +Unix specific GL implementation initialisation +============== +*/ +void Sys_GLimpInit( void ) +{ + // NOP +} + +void Sys_SetFloatEnv(void) +{ + // rounding toward nearest + fesetround(FE_TONEAREST); +} + +/* +============== +Sys_PlatformInit + +Unix specific initialisation +============== +*/ +void Sys_PlatformInit( void ) +{ + const char* term = getenv( "TERM" ); + + signal( SIGHUP, Sys_SigHandler ); + signal( SIGQUIT, Sys_SigHandler ); + signal( SIGTRAP, Sys_SigHandler ); + signal( SIGABRT, Sys_SigHandler ); + signal( SIGBUS, Sys_SigHandler ); + + Sys_SetFloatEnv(); + + stdinIsATTY = isatty( STDIN_FILENO ) && + !( term && ( !strcmp( term, "raw" ) || !strcmp( term, "dumb" ) ) ); +} + +/* +============== +Sys_PlatformExit + +Unix specific deinitialisation +============== +*/ +void Sys_PlatformExit( void ) +{ +} + +/* +============== +Sys_SetEnv + +set/unset environment variables (empty value removes it) +============== +*/ + +void Sys_SetEnv(const char *name, const char *value) +{ + if(value && *value) + setenv(name, value, 1); + else + unsetenv(name); +} + +/* +============== +Sys_PID +============== +*/ +int Sys_PID( void ) +{ + return getpid( ); +} + +/* +============== +Sys_PIDIsRunning +============== +*/ +qboolean Sys_PIDIsRunning( size_t pid ) +{ + return kill( pid, 0 ) == 0; +} + +/* +================= +Sys_DllExtension + +Check if filename should be allowed to be loaded as a DLL. +================= +*/ +qboolean Sys_DllExtension( const char *name ) { + const char *p; + char c = 0; + + if ( COM_CompareExtension( name, DLL_EXT ) ) { + return qtrue; + } + +#ifdef __APPLE__ + // Allow system frameworks without dylib extensions + // i.e., /System/Library/Frameworks/OpenAL.framework/OpenAL + if ( strncmp( name, "/System/Library/Frameworks/", 27 ) == 0 ) { + return qtrue; + } +#endif + + // Check for format of filename.so.1.2.3 + p = strstr( name, DLL_EXT "." ); + + if ( p ) { + p += strlen( DLL_EXT ); + + // Check if .so is only followed for periods and numbers. + while ( *p ) { + c = *p; + + if ( !isdigit( c ) && c != '.' ) { + return qfalse; + } + + p++; + } + + // Don't allow filename to end in a period. file.so., file.so.0., etc + if ( c != '.' ) { + return qtrue; + } + } + + return qfalse; } diff --git a/code/sys/sys_win32.c b/code/sys/sys_win32.c index d1cbf0d4..763215f2 100644 --- a/code/sys/sys_win32.c +++ b/code/sys/sys_win32.c @@ -36,19 +36,60 @@ Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA #include #include #include +#include +#include + +#ifndef KEY_WOW64_32KEY +#define KEY_WOW64_32KEY 0x0200 +#endif // Used to determine where to store user-specific files static char homePath[ MAX_OSPATH ] = { 0 }; -static char programpath[ MAX_OSPATH ] = { 0 }; + +// Used to store the Steam Quake 3 installation path +static char steamPath[ MAX_OSPATH ] = { 0 }; + +// Used to store the GOG Quake 3 installation path +static char gogPath[ MAX_OSPATH ] = { 0 }; + +// Used to store the Microsoft Store Quake 3 installation path +static char microsoftStorePath[MAX_OSPATH] = { 0 }; + +#ifndef DEDICATED +static UINT timerResolution = 0; +#endif /* ================ -RecoverLostAutodialData +Sys_SetFPUCW +Set FPU control word to default value ================ */ -void RecoverLostAutodialData( void ) + +#ifndef _RC_CHOP +// mingw doesn't seem to have these defined :( + + #define _MCW_EM 0x0008001fU + #define _MCW_RC 0x00000300U + #define _MCW_PC 0x00030000U + #define _RC_NEAR 0x00000000U + #define _PC_53 0x00010000U + + unsigned int _controlfp(unsigned int new, unsigned int mask); +#endif + +#define FPUCWMASK1 (_MCW_RC | _MCW_EM) +#define FPUCW (_RC_NEAR | _MCW_EM | _PC_53) + +#if idx64 +#define FPUCWMASK (FPUCWMASK1) +#else +#define FPUCWMASK (FPUCWMASK1 | _MCW_PC) +#endif + +void Sys_SetFloatEnv(void) { - // FIXME: stub + _controlfp(FPUCW, FPUCWMASK); } /* @@ -60,19 +101,16 @@ char *Sys_DefaultHomePath( void ) { TCHAR szPath[MAX_PATH]; FARPROC qSHGetFolderPath; - HMODULE shfolder; + HMODULE shfolder = LoadLibrary("shfolder.dll"); - return NULL; - shfolder = LoadLibrary("shfolder.dll"); - - if( !*homePath ) + if(shfolder == NULL) { - if(shfolder == NULL) - { - Com_Printf("Unable to load SHFolder.dll\n"); - return NULL; - } + Com_Printf("Unable to load SHFolder.dll\n"); + return NULL; + } + if(!*homePath && com_homepath) + { qSHGetFolderPath = GetProcAddress(shfolder, "SHGetFolderPathA"); if(qSHGetFolderPath == NULL) { @@ -88,70 +126,148 @@ char *Sys_DefaultHomePath( void ) FreeLibrary(shfolder); return NULL; } - Q_strncpyz( homePath, szPath, sizeof( homePath ) ); - Q_strcat( homePath, sizeof( homePath ), "\\OpenMoHAA" ); - FreeLibrary(shfolder); - if( !CreateDirectory( homePath, NULL ) ) - { - if( GetLastError() != ERROR_ALREADY_EXISTS ) - { - Com_Printf("Unable to create directory \"%s\"\n", homePath ); - return NULL; - } - } + + Com_sprintf(homePath, sizeof(homePath), "%s%c", szPath, PATH_SEP); + + if(com_homepath->string[0]) + Q_strcat(homePath, sizeof(homePath), com_homepath->string); + else + Q_strcat(homePath, sizeof(homePath), HOMEPATH_NAME_WIN); } + FreeLibrary(shfolder); return homePath; } /* ================ -SetProgramPath +Sys_SteamPath ================ */ -void SetProgramPath( const char *path ) +char *Sys_SteamPath( void ) { - char *p; +#if defined(STEAMPATH_NAME) || defined(STEAMPATH_APPID) + HKEY steamRegKey; + DWORD pathLen = MAX_OSPATH; + qboolean finishPath = qfalse; - Q_strncpyz( programpath, path, sizeof( programpath ) ); +#ifdef STEAMPATH_APPID + // Assuming Steam is a 32-bit app + if (!steamPath[0] && !RegOpenKeyEx(HKEY_LOCAL_MACHINE, "SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Uninstall\\Steam App " STEAMPATH_APPID, 0, KEY_QUERY_VALUE | KEY_WOW64_32KEY, &steamRegKey)) + { + pathLen = MAX_OSPATH; + if (RegQueryValueEx(steamRegKey, "InstallLocation", NULL, NULL, (LPBYTE)steamPath, &pathLen)) + steamPath[0] = '\0'; - p = strrchr( programpath, '/' ); - if( p ) { - *p = 0; + RegCloseKey(steamRegKey); } +#endif + +#ifdef STEAMPATH_NAME + if (!steamPath[0] && !RegOpenKeyEx(HKEY_CURRENT_USER, "Software\\Valve\\Steam", 0, KEY_QUERY_VALUE, &steamRegKey)) + { + pathLen = MAX_OSPATH; + if (RegQueryValueEx(steamRegKey, "SteamPath", NULL, NULL, (LPBYTE)steamPath, &pathLen)) + if (RegQueryValueEx(steamRegKey, "InstallPath", NULL, NULL, (LPBYTE)steamPath, &pathLen)) + steamPath[0] = '\0'; + + if (steamPath[0]) + finishPath = qtrue; + + RegCloseKey(steamRegKey); + } +#endif + + if (steamPath[0]) + { + if (pathLen == MAX_OSPATH) + pathLen--; + + steamPath[pathLen] = '\0'; + + if (finishPath) + Q_strcat(steamPath, MAX_OSPATH, "\\SteamApps\\common\\" STEAMPATH_NAME ); + } +#endif + + return steamPath; } /* ================ -Sys_DefaultBasePath +Sys_GogPath ================ */ -char *Sys_DefaultBasePath( void ) +char *Sys_GogPath( void ) { - static char basepath[ 256 ]; +#ifdef GOGPATH_ID + HKEY gogRegKey; + DWORD pathLen = MAX_OSPATH; - Q_strncpyz( basepath, programpath, sizeof( basepath ) ); - return basepath; + if (!gogPath[0] && !RegOpenKeyEx(HKEY_LOCAL_MACHINE, "SOFTWARE\\GOG.com\\Games\\" GOGPATH_ID, 0, KEY_QUERY_VALUE | KEY_WOW64_32KEY, &gogRegKey)) + { + pathLen = MAX_OSPATH; + if (RegQueryValueEx(gogRegKey, "PATH", NULL, NULL, (LPBYTE)gogPath, &pathLen)) + gogPath[0] = '\0'; + + RegCloseKey(gogRegKey); + } + + if (gogPath[0]) + { + if (pathLen == MAX_OSPATH) + pathLen--; + + gogPath[pathLen] = '\0'; + } +#endif + + return gogPath; } /* ================ -Sys_DefaultUserPath +Sys_MicrosoftStorePath ================ */ -char *Sys_DefaultUserPath( void ) +char* Sys_MicrosoftStorePath(void) { - return Sys_DefaultBasePath(); -} +#ifdef MSSTORE_PATH + if (!microsoftStorePath[0]) + { + TCHAR szPath[MAX_PATH]; + FARPROC qSHGetFolderPath; + HMODULE shfolder = LoadLibrary("shfolder.dll"); -/* -================ -Sys_DefaultUserPath -================ -*/ -char *Sys_DefaultOutputPath( void ) -{ - return Sys_DefaultUserPath(); + if(shfolder == NULL) + { + Com_Printf("Unable to load SHFolder.dll\n"); + return microsoftStorePath; + } + + qSHGetFolderPath = GetProcAddress(shfolder, "SHGetFolderPathA"); + if(qSHGetFolderPath == NULL) + { + Com_Printf("Unable to find SHGetFolderPath in SHFolder.dll\n"); + FreeLibrary(shfolder); + return microsoftStorePath; + } + + if( !SUCCEEDED( qSHGetFolderPath( NULL, CSIDL_PROGRAM_FILES, + NULL, 0, szPath ) ) ) + { + Com_Printf("Unable to detect CSIDL_PROGRAM_FILES\n"); + FreeLibrary(shfolder); + return microsoftStorePath; + } + + FreeLibrary(shfolder); + + // default: C:\Program Files\ModifiableWindowsApps\Quake 3\EN + Com_sprintf(microsoftStorePath, sizeof(microsoftStorePath), "%s%cModifiableWindowsApps%c%s%cEN", szPath, PATH_SEP, PATH_SEP, MSSTORE_PATH, PATH_SEP); + } +#endif + return microsoftStorePath; } /* @@ -174,40 +290,6 @@ int Sys_Milliseconds (void) return sys_curtime; } -#ifndef __GNUC__ //see snapvectora.s -/* -================ -Sys_SnapVector -================ -*/ -void Sys_SnapVector( float *v ) -{ -#ifndef _WIN64 - int i; - float f; - - f = *v; - __asm fld f; - __asm fistp i; - *v = i; - v++; - f = *v; - __asm fld f; - __asm fistp i; - *v = i; - v++; - f = *v; - __asm fld f; - __asm fistp i; - *v = i; -#else - v[ 0 ] = rint( v[ 0 ] ); - v[ 1 ] = rint( v[ 1 ] ); - v[ 2 ] = rint( v[ 2 ] ); -#endif -} -#endif - /* ================ Sys_RandomBytes @@ -252,33 +334,6 @@ char *Sys_GetCurrentUser( void ) return s_userName; } -/* -================ -Sys_GetClipboardData -================ -*/ -char *Sys_GetClipboardData( void ) -{ - char *data = NULL; - char *cliptext; - - if ( OpenClipboard( NULL ) != 0 ) { - HANDLE hClipboardData; - - if ( ( hClipboardData = GetClipboardData( CF_TEXT ) ) != 0 ) { - if ( ( cliptext = GlobalLock( hClipboardData ) ) != 0 ) { - data = Z_Malloc( GlobalSize( hClipboardData ) + 1 ); - Q_strncpyz( data, cliptext, GlobalSize( hClipboardData ) ); - GlobalUnlock( hClipboardData ); - - strtok( data, "\n\r\b" ); - } - } - CloseClipboard(); - } - return data; -} - #define MEM_THRESHOLD 96*1024*1024 /* @@ -293,26 +348,6 @@ qboolean Sys_LowPhysicalMemory( void ) return (stat.dwTotalPhys <= MEM_THRESHOLD) ? qtrue : qfalse; } -/* -================== -SetNormalThreadPriority -================== -*/ -void SetNormalThreadPriority( void ) -{ - SetThreadPriority( GetCurrentThread(), THREAD_PRIORITY_NORMAL ); -} - -/* -================== -SetBelowNormalThreadPriority -================== -*/ -void SetBelowNormalThreadPriority( void ) -{ - SetThreadPriority( GetCurrentThread(), THREAD_PRIORITY_BELOW_NORMAL ); -} - /* ============== Sys_Basename @@ -321,7 +356,7 @@ Sys_Basename const char *Sys_Basename( char *path ) { static char base[ MAX_OSPATH ] = { 0 }; - intptr_t length; + int length; length = strlen( path ) - 1; @@ -351,7 +386,7 @@ Sys_Dirname const char *Sys_Dirname( char *path ) { static char dir[ MAX_OSPATH ] = { 0 }; - intptr_t length; + int length; Q_strncpyz( dir, path, sizeof( dir ) ); length = strlen( dir ) - 1; @@ -364,14 +399,48 @@ const char *Sys_Dirname( char *path ) return dir; } +/* +============== +Sys_FOpen +============== +*/ +FILE *Sys_FOpen( const char *ospath, const char *mode ) { + size_t length; + + // Windows API ignores all trailing spaces and periods which can get around Quake 3 file system restrictions. + length = strlen( ospath ); + if ( length == 0 || ospath[length-1] == ' ' || ospath[length-1] == '.' ) { + return NULL; + } + + return fopen( ospath, mode ); +} + /* ============== Sys_Mkdir ============== */ -void Sys_Mkdir( const char *path ) +qboolean Sys_Mkdir( const char *path ) { - _mkdir (path); + if( !CreateDirectory( path, NULL ) ) + { + if( GetLastError( ) != ERROR_ALREADY_EXISTS ) + return qfalse; + } + + return qtrue; +} + +/* +================== +Sys_Mkfifo +Noop on windows because named pipes do not function the same way +================== +*/ +FILE *Sys_Mkfifo( const char *ospath ) +{ + return NULL; } /* @@ -403,7 +472,7 @@ DIRECTORY SCANNING Sys_ListFilteredFiles ============== */ -void Sys_ListFilteredFiles( const char *basedir, char *subdirs, char *filter, const char **list, int *numfiles ) +void Sys_ListFilteredFiles( const char *basedir, char *subdirs, char *filter, char **list, int *numfiles ) { char search[MAX_OSPATH], newsubdirs[MAX_OSPATH]; char filename[MAX_OSPATH]; @@ -490,9 +559,10 @@ char **Sys_ListFiles( const char *directory, const char *extension, char *filter char **listCopy; char *list[MAX_FOUND_FILES]; struct _finddata_t findinfo; - intptr_t findhandle; + intptr_t findhandle; int flag; int i; + int extLen; if (filter) { @@ -526,6 +596,8 @@ char **Sys_ListFiles( const char *directory, const char *extension, char *filter flag = _A_SUBDIR; } + extLen = strlen( extension ); + Com_sprintf( search, sizeof(search), "%s\\*%s", directory, extension ); // search @@ -539,6 +611,14 @@ char **Sys_ListFiles( const char *directory, const char *extension, char *filter do { if ( (!wantsubs && flag ^ ( findinfo.attrib & _A_SUBDIR )) || (wantsubs && findinfo.attrib & _A_SUBDIR) ) { + if (*extension) { + if ( strlen( findinfo.name ) < extLen || + Q_stricmp( + findinfo.name + strlen( findinfo.name ) - extLen, + extension ) ) { + continue; // didn't match + } + } if ( nfiles == MAX_FOUND_FILES - 1 ) { break; } @@ -604,28 +684,39 @@ void Sys_FreeFileList( char **list ) ============== Sys_Sleep -Block execution for msec or until input is recieved. +Block execution for msec or until input is received. ============== */ void Sys_Sleep( int msec ) { + if( msec == 0 ) + return; + +#ifdef DEDICATED if( msec < 0 ) WaitForSingleObject( GetStdHandle( STD_INPUT_HANDLE ), INFINITE ); else WaitForSingleObject( GetStdHandle( STD_INPUT_HANDLE ), msec ); +#else + // Client Sys_Sleep doesn't support waiting on stdin + if( msec < 0 ) + return; + + Sleep( msec ); +#endif } /* ============== -SyScriptErrorDialog +Sys_ErrorDialog Display an error message ============== */ -void SyScriptErrorDialog( const char *error ) +void Sys_ErrorDialog( const char *error ) { - if( MessageBox( NULL, va( "%s. Copy console log to clipboard?", error ), - NULL, MB_YESNO|MB_ICONERROR ) == IDYES ) + if( Sys_Dialog( DT_YES_NO, va( "%s. Copy console log to clipboard?", error ), + "Error" ) == DR_YES ) { HGLOBAL memoryHandle; char *clipMemory; @@ -637,7 +728,7 @@ void SyScriptErrorDialog( const char *error ) { char *p = clipMemory; char buffer[ 1024 ]; - size_t size; + unsigned int size; while( ( size = CON_LogRead( buffer, sizeof( buffer ) ) ) > 0 ) { @@ -656,39 +747,10 @@ void SyScriptErrorDialog( const char *error ) } } -/* -============== -Sys_CloseMutex -============== -*/ -void Sys_CloseMutex( void ) -{ - // FIXME: stub -} - -/* -============== -Sys_ShowConsole -============== -*/ -void Sys_ShowConsole( int visLevel, qboolean quitOnClose ) -{ - // FIXME: stub -} - -/* -============== -Sys_PumpMessageLoop -============== -*/ -void Sys_PumpMessageLoop( void ) -{ - // FIXME: stub -} - /* ============== Sys_Dialog + Display a win32 dialog box ============== */ @@ -716,14 +778,185 @@ dialogResult_t Sys_Dialog( dialogType_t type, const char *message, const char *t } } +/* +============== +Sys_GLimpSafeInit + +Windows specific "safe" GL implementation initialisation +============== +*/ +void Sys_GLimpSafeInit( void ) +{ +} + +/* +============== +Sys_GLimpInit + +Windows specific GL implementation initialisation +============== +*/ +void Sys_GLimpInit( void ) +{ +} + +/* +============== +Sys_PlatformInit + +Windows specific initialisation +============== +*/ +void Sys_PlatformInit( void ) +{ +#ifndef DEDICATED + TIMECAPS ptc; +#endif + + Sys_SetFloatEnv(); + +#ifndef DEDICATED + if(timeGetDevCaps(&ptc, sizeof(ptc)) == MMSYSERR_NOERROR) + { + timerResolution = ptc.wPeriodMin; + + if(timerResolution > 1) + { + Com_Printf("Warning: Minimum supported timer resolution is %ums " + "on this system, recommended resolution 1ms\n", timerResolution); + } + + timeBeginPeriod(timerResolution); + } + else + timerResolution = 0; +#endif +} + +/* +============== +Sys_PlatformExit + +Windows specific initialisation +============== +*/ +void Sys_PlatformExit( void ) +{ +#ifndef DEDICATED + if(timerResolution) + timeEndPeriod(timerResolution); +#endif +} + +/* +============== +Sys_SetEnv + +set/unset environment variables (empty value removes it) +============== +*/ +void Sys_SetEnv(const char *name, const char *value) +{ + if(value) + _putenv(va("%s=%s", name, value)); + else + _putenv(va("%s=", name)); +} + +/* +============== +Sys_PID +============== +*/ +int Sys_PID( void ) +{ + return GetCurrentProcessId( ); +} + +/* +============== +Sys_PIDIsRunning +============== +*/ +qboolean Sys_PIDIsRunning( int pid ) +{ + DWORD processes[ 1024 ]; + DWORD numBytes, numProcesses; + int i; + + if( !EnumProcesses( processes, sizeof( processes ), &numBytes ) ) + return qfalse; // Assume it's not running + + numProcesses = numBytes / sizeof( DWORD ); + + // Search for the pid + for( i = 0; i < numProcesses; i++ ) + { + if( processes[ i ] == pid ) + return qtrue; + } + + return qfalse; +} + +/* +================= +Sys_DllExtension + +Check if filename should be allowed to be loaded as a DLL. +================= +*/ +qboolean Sys_DllExtension( const char *name ) { + return COM_CompareExtension( name, DLL_EXT ); +} +/* +================ +RecoverLostAutodialData +================ +*/ +void RecoverLostAutodialData(void) +{ + // FIXME: stub +} + +/* +============== +Sys_CloseMutex +============== +*/ +void Sys_CloseMutex(void) +{ + // FIXME: stub +} + +/* +============== +Sys_ShowConsole +============== +*/ +void Sys_ShowConsole(int visLevel, qboolean quitOnClose) +{ + // FIXME: stub +} + +/* +============== +Sys_PumpMessageLoop +============== +*/ +void Sys_PumpMessageLoop(void) +{ + // FIXME: stub +} + /* ============== SaveRegistryInfo ============== */ -qboolean SaveRegistryInfo( qboolean user, const char *pszName, void *pvBuf, long lSize ) +qboolean SaveRegistryInfo(qboolean user, const char* pszName, void* pvBuf, long lSize) { - STUB_DESC( "not implemented" ); + STUB_DESC("not implemented"); return qfalse; } @@ -732,9 +965,9 @@ qboolean SaveRegistryInfo( qboolean user, const char *pszName, void *pvBuf, long LoadRegistryInfo ============== */ -qboolean LoadRegistryInfo( qboolean user, const char *pszName, void *pvBuf, long *plSize ) +qboolean LoadRegistryInfo(qboolean user, const char* pszName, void* pvBuf, long* plSize) { - STUB_DESC( "not implemented" ); + STUB_DESC("not implemented"); return qfalse; } @@ -743,9 +976,9 @@ qboolean LoadRegistryInfo( qboolean user, const char *pszName, void *pvBuf, long IsFirstRun ============== */ -qboolean IsFirstRun( void ) +qboolean IsFirstRun(void) { - STUB_DESC( "wtf" ); + STUB_DESC("wtf"); return qfalse; } @@ -754,9 +987,9 @@ qboolean IsFirstRun( void ) IsNewConfig ============== */ -qboolean IsNewConfig( void ) +qboolean IsNewConfig(void) { - STUB_DESC( "wtf" ); + STUB_DESC("wtf"); return qfalse; } @@ -765,9 +998,9 @@ qboolean IsNewConfig( void ) IsSafeMode ============== */ -qboolean IsSafeMode( void ) +qboolean IsSafeMode(void) { - STUB_DESC( "wtf" ); + STUB_DESC("wtf"); return qfalse; } @@ -776,7 +1009,7 @@ qboolean IsSafeMode( void ) ClearNewConfigFlag ============== */ -void ClearNewConfigFlag( void ) +void ClearNewConfigFlag(void) { } @@ -785,7 +1018,7 @@ void ClearNewConfigFlag( void ) Sys_GetWholeClipboard ============== */ -const char *Sys_GetWholeClipboard( void ) +const char* Sys_GetWholeClipboard(void) { return NULL; } @@ -795,6 +1028,26 @@ const char *Sys_GetWholeClipboard( void ) Sys_SetClipboard ============== */ -void Sys_SetClipboard( const char *contents ) +void Sys_SetClipboard(const char* contents) { } + +/* +================== +SetNormalThreadPriority +================== +*/ +void SetNormalThreadPriority(void) +{ + SetThreadPriority(GetCurrentThread(), THREAD_PRIORITY_NORMAL); +} + +/* +================== +SetBelowNormalThreadPriority +================== +*/ +void SetBelowNormalThreadPriority(void) +{ + SetThreadPriority(GetCurrentThread(), THREAD_PRIORITY_BELOW_NORMAL); +}