Updated platform files to ioq3 codebase

This commit is contained in:
L 2023-01-31 00:53:24 +01:00
parent 0d074c09e8
commit dc36511b5a
16 changed files with 2248 additions and 884 deletions

View file

@ -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})

View file

@ -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) {

View file

@ -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 );

View file

@ -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;
}

View file

@ -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 )

View file

@ -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 <io.h>
#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 <stdint.h>
#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 );

View file

@ -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 );

View file

@ -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;

View file

@ -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 <unistd.h>
#include <signal.h>
#include <termios.h>
@ -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; i<TTY_con.cursor; i++)
{
write(1, TTY_con.buffer+i, 1);
size = write(STDOUT_FILENO, TTY_con.buffer+i, 1);
}
}
}
@ -165,12 +172,12 @@ void CON_Shutdown( void )
{
if (ttycon_on)
{
CON_Back(); // Delete "]"
tcsetattr (0, TCSADRAIN, &TTY_tc);
CON_Hide();
tcsetattr (STDIN_FILENO, TCSADRAIN, &TTY_tc);
}
// Restore blocking to stdin reads
fcntl( 0, F_SETFL, fcntl( 0, F_GETFL, 0 ) & ~O_NONBLOCK );
// Restore blocking to stdin reads
fcntl(STDIN_FILENO, F_SETFL, fcntl(STDIN_FILENO, F_GETFL, 0) & ~O_NONBLOCK);
}
/*
@ -181,6 +188,11 @@ Hist_Add
void Hist_Add(field_t *field)
{
int i;
// Don't save blank lines in history.
if (!field->cursor)
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++;
}
}

View file

@ -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( );
}

View file

@ -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 <windows.h>
# else
# include <unistd.h>
# endif
# include <stdio.h>
# include <string.h>
#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. */
}

View file

@ -20,6 +20,8 @@ Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
===========================================================================
*/
#pragma once
#ifdef DEDICATED
# ifdef _WIN32
# include <windows.h>
@ -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 <SDL.h>
# include <SDL_loadso.h>
# 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);

View file

@ -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 <SDL_version.h>
#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 );

File diff suppressed because it is too large Load diff

View file

@ -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 <signal.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <errno.h>
@ -34,10 +35,24 @@ Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
#include <sys/time.h>
#include <pwd.h>
#include <libgen.h>
#include <fcntl.h>
#include <fenv.h>
#include <sys/wait.h>
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;
}

View file

@ -36,19 +36,60 @@ Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
#include <conio.h>
#include <wincrypt.h>
#include <shlobj.h>
#include <psapi.h>
#include <float.h>
#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);
}