mirror of
https://github.com/FWGS/xash3d-fwgs.git
synced 2026-08-05 03:24:56 +08:00
Compare commits
69 Commits
continuous
...
continuous
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
8c9b62cb03 | ||
|
|
9ff6eed0bf | ||
|
|
b6196c3c95 | ||
|
|
1ee2186f72 | ||
|
|
6099d5eadd | ||
|
|
3d68e13585 | ||
|
|
83b5edea88 | ||
|
|
9d2a85f11f | ||
|
|
93d3df81ee | ||
|
|
2a7f13151a | ||
|
|
0c4f6ae61d | ||
|
|
b7150c3208 | ||
|
|
d6ebff81e2 | ||
|
|
ce4148f351 | ||
|
|
08c2632c14 | ||
|
|
03eac904c1 | ||
|
|
62f3354f4e | ||
|
|
d5b6eff0c0 | ||
|
|
c9ee844a24 | ||
|
|
687c9b004f | ||
|
|
14e79f04ac | ||
|
|
5d6cf48687 | ||
|
|
88949a9ee6 | ||
|
|
2d1e76199b | ||
|
|
a5d78ecfd7 | ||
|
|
3576e474e6 | ||
|
|
d50927f21f | ||
|
|
c6e3146290 | ||
|
|
9811988cce | ||
|
|
48bdd4238e | ||
|
|
fd2b7e9197 | ||
|
|
0e42e5b9e3 | ||
|
|
65750f0cf3 | ||
|
|
6469b0a5f5 | ||
|
|
4f98187e9a | ||
|
|
74c19a1557 | ||
|
|
cfebee6ea8 | ||
|
|
bd0d6644e0 | ||
|
|
f41450127a | ||
|
|
70897af362 | ||
|
|
f1d7101ad8 | ||
|
|
ac9d960eef | ||
|
|
0c471aecb1 | ||
|
|
72d0d70587 | ||
|
|
bec526a77e | ||
|
|
4d1119391a | ||
|
|
1dd622fcbb | ||
|
|
af4b64983a | ||
|
|
e751092555 | ||
|
|
ce06b1d3d7 | ||
|
|
a834f4996f | ||
|
|
fdcf267fe3 | ||
|
|
7f2ebd1b5e | ||
|
|
68b2d9f269 | ||
|
|
3f1eed9f59 | ||
|
|
0637dbaab1 | ||
|
|
85388e97c1 | ||
|
|
de961eb45e | ||
|
|
3d60770b84 | ||
|
|
93c5853356 | ||
|
|
98e587285b | ||
|
|
2aa13c8347 | ||
|
|
53344771fd | ||
|
|
2373743920 | ||
|
|
90a3497afe | ||
|
|
339aebb08c | ||
|
|
b4afe390d6 | ||
|
|
ca0c5f929a | ||
|
|
48cc526c7e |
2
.github/workflows/c-cpp.yml
vendored
2
.github/workflows/c-cpp.yml
vendored
@@ -66,7 +66,7 @@ jobs:
|
||||
targetos: apple
|
||||
targetarch: amd64
|
||||
env:
|
||||
SDL_VERSION: 2.30.9
|
||||
SDL_VERSION: 2.30.10
|
||||
GH_CPU_ARCH: ${{ matrix.targetarch }}
|
||||
GH_CROSSCOMPILING: ${{ matrix.cross }}
|
||||
steps:
|
||||
|
||||
2
3rdparty/mainui
vendored
2
3rdparty/mainui
vendored
Submodule 3rdparty/mainui updated: c92a06e979...734a6d1ca2
@@ -5,5 +5,10 @@ This can be useful to test engine in Wine without using virtual machines or dual
|
||||
0. Clone and install https://github.com/mstorsjo/msvc-wine (you can skip CMake part)
|
||||
1. Set environment variable MSVC_WINE_PATH to the path to installed MSVC toolchain
|
||||
2. Pre-load wine: `wineserver -k; wineserver -p; wine64 wineboot`
|
||||
3. Run `./waf configure -T <build-type> --enable-wine-msvc --sdl2=../SDL2_VC`. Configuration step will take more time than usual.
|
||||
3. Run `PKGCONFIG=/bin/false ./waf configure -T <build-type> --enable-wine-msvc --sdl2=../SDL2_VC`. Configuration step will take more time than usual.
|
||||
4. .. other typical steps to build from console ...
|
||||
|
||||
> [!NOTE]
|
||||
> Notice the usage of PKGCONFIG=/bin/false here. We're disabling pkg-config so we don't accidentally pull
|
||||
> system-wide dependencies and force building them from source. In future builds we might set custom
|
||||
> directory to pull dependencies from, like ffmpeg...
|
||||
|
||||
@@ -85,6 +85,9 @@ These strings are specific to Xash3D FWGS.
|
||||
As Xash3D accidentally supports GoldSrc games, it also supports parsing liblist.gam.\
|
||||
Xash3D will use this file if gameinfo.txt is absent, or if its modification timestamp is older than liblist.gam.
|
||||
|
||||
> [!NOTE]
|
||||
> Starting from January 2025, Xash3D FWGS doesn't automatically generate gameinfo.txt from liblist.gam. The key conversion table still remains but if you wish to use gameinfo.txt instead of liblist.gam, you can execute `fs_make_gameinfo` in console.
|
||||
|
||||
For game creators who plan supporting only Xash3D, using this file is not recommended.
|
||||
|
||||
The table below defines conversion rules from liblist.gam to gameinfo.txt. Some keys' interpretation does differ from `gameinfo.txt`, in this case a note will be left. If `liblist.gam` key isn't present in this table, it's ignored.
|
||||
|
||||
@@ -38,6 +38,9 @@ Uploaded to github by Oleg Cherkasky - https://github.com/gunrunners-paradise/Ct
|
||||
## Deathmatch Classic
|
||||
Available in Valve's Half-Life repository - https://github.com/ValveSoftware/halflife/tree/master/dmc
|
||||
|
||||
## Earth Special Forces
|
||||
Alpha 2.0 - https://www.gamers-desire.de/details/2830
|
||||
|
||||
## ESHQ
|
||||
Official github repository - https://github.com/adslbarxatov/xash3d-for-ESHQ
|
||||
|
||||
|
||||
@@ -232,6 +232,7 @@ typedef struct dll_info_s
|
||||
{
|
||||
const char *name; // name of library
|
||||
const dllfunc_t *fcts; // list of dll exports
|
||||
const size_t num_fcts;
|
||||
qboolean crash; // crash if dll not found
|
||||
void *link; // hinstance of loading library
|
||||
} dll_info_t;
|
||||
|
||||
@@ -30,10 +30,9 @@ static dllfunc_t msvfw_funcs[] =
|
||||
{ "DrawDibOpen", (void **) &pDrawDibOpen },
|
||||
{ "DrawDibDraw", (void **) &pDrawDibDraw },
|
||||
{ "DrawDibClose", (void **) &pDrawDibClose },
|
||||
{ NULL, NULL }
|
||||
};
|
||||
|
||||
dll_info_t msvfw_dll = { "msvfw32.dll", msvfw_funcs, false };
|
||||
dll_info_t msvfw_dll = { "msvfw32.dll", msvfw_funcs, ARRAYSIZE( msvfw_funcs ), false };
|
||||
|
||||
// msacm32.dll exports
|
||||
static MMRESULT (_stdcall *pacmStreamOpen)( LPHACMSTREAM, HACMDRIVER, LPWAVEFORMATEX, LPWAVEFORMATEX, LPWAVEFILTER, DWORD, DWORD, DWORD );
|
||||
@@ -51,10 +50,9 @@ static dllfunc_t msacm_funcs[] =
|
||||
{ "acmStreamConvert", (void **) &pacmStreamConvert },
|
||||
{ "acmStreamSize", (void **) &pacmStreamSize },
|
||||
{ "acmStreamClose", (void **) &pacmStreamClose },
|
||||
{ NULL, NULL }
|
||||
};
|
||||
|
||||
dll_info_t msacm_dll = { "msacm32.dll", msacm_funcs, false };
|
||||
dll_info_t msacm_dll = { "msacm32.dll", msacm_funcs, ARRAYSIZE( msacm_funcs ), false };
|
||||
|
||||
// avifil32.dll exports
|
||||
static int (_stdcall *pAVIStreamInfo)( PAVISTREAM pavi, AVISTREAMINFO *psi, LONG lSize );
|
||||
@@ -88,10 +86,9 @@ static dllfunc_t avifile_funcs[] =
|
||||
{ "AVIStreamRelease", (void **) &pAVIStreamRelease },
|
||||
{ "AVIStreamStart", (void **) &pAVIStreamStart },
|
||||
{ "AVIStreamTimeToSample", (void **) &pAVIStreamTimeToSample },
|
||||
{ NULL, NULL }
|
||||
};
|
||||
|
||||
dll_info_t avifile_dll = { "avifil32.dll", avifile_funcs, false };
|
||||
dll_info_t avifile_dll = { "avifil32.dll", avifile_funcs, ARRAYSIZE( avifile_funcs ), false };
|
||||
|
||||
typedef struct movie_state_s
|
||||
{
|
||||
|
||||
@@ -157,7 +157,7 @@ void CL_StartupDemoHeader( void )
|
||||
{
|
||||
CL_CloseDemoHeader();
|
||||
|
||||
cls.demoheader = FS_Open( "demoheader.tmp", "w+b", true );
|
||||
cls.demoheader = FS_Open( "demoheader.tmp", "w+bm", true );
|
||||
|
||||
if( !cls.demoheader )
|
||||
{
|
||||
@@ -724,7 +724,7 @@ static void CL_DemoStartPlayback( int mode )
|
||||
{
|
||||
// NOTE: at this point demo is still valid
|
||||
CL_Disconnect();
|
||||
Host_ShutdownServer();
|
||||
SV_Shutdown( "Server was killed due to demo playback start\n" );
|
||||
|
||||
Con_FastClose();
|
||||
UI_SetActiveMenu( false );
|
||||
|
||||
@@ -76,7 +76,6 @@ static const dllfunc_t cdll_exports[] =
|
||||
{ "IN_ClearStates", (void **)&clgame.dllFuncs.IN_ClearStates },
|
||||
{ "V_CalcRefdef", (void **)&clgame.dllFuncs.pfnCalcRefdef },
|
||||
{ "KB_Find", (void **)&clgame.dllFuncs.KB_Find },
|
||||
{ NULL, NULL }
|
||||
};
|
||||
|
||||
// optional exports
|
||||
@@ -91,7 +90,6 @@ static const dllfunc_t cdll_new_exports[] = // allowed only in SDK 2.3 and high
|
||||
{ "IN_ClientTouchEvent", (void **)&clgame.dllFuncs.pfnTouchEvent}, // Xash3D FWGS ext
|
||||
{ "IN_ClientMoveEvent", (void **)&clgame.dllFuncs.pfnMoveEvent}, // Xash3D FWGS ext
|
||||
{ "IN_ClientLookEvent", (void **)&clgame.dllFuncs.pfnLookEvent}, // Xash3D FWGS ext
|
||||
{ NULL, NULL }
|
||||
};
|
||||
|
||||
static void pfnSPR_DrawHoles( int frame, int x, int y, const wrect_t *prc );
|
||||
@@ -976,7 +974,7 @@ static void CL_ClearUserMessage( char *pszName, int svc_num )
|
||||
int i;
|
||||
|
||||
for( i = 0; i < MAX_USER_MESSAGES && clgame.msg[i].name[0]; i++ )
|
||||
if( ( clgame.msg[i].number == svc_num ) && Q_strcmp( clgame.msg[i].name, pszName ) )
|
||||
if( ( clgame.msg[i].number == svc_num ) && Q_stricmp( clgame.msg[i].name, pszName ) )
|
||||
clgame.msg[i].number = 0;
|
||||
}
|
||||
|
||||
@@ -1046,7 +1044,7 @@ void CL_InitEdicts( int maxclients )
|
||||
cls.num_client_entities = CL_UPDATE_BACKUP * NUM_PACKET_ENTITIES;
|
||||
cls.packet_entities = Mem_Realloc( clgame.mempool, cls.packet_entities, sizeof( entity_state_t ) * cls.num_client_entities );
|
||||
clgame.entities = Mem_Calloc( clgame.mempool, sizeof( cl_entity_t ) * clgame.maxEntities );
|
||||
clgame.static_entities = Mem_Calloc( clgame.mempool, sizeof( cl_entity_t ) * MAX_STATIC_ENTITIES );
|
||||
clgame.static_entities = NULL; // will be initialized later
|
||||
clgame.numStatics = 0;
|
||||
|
||||
if(( clgame.maxRemapInfos - 1 ) != clgame.maxEntities )
|
||||
@@ -1765,6 +1763,17 @@ static cvar_t *GAME_EXPORT pfnCvar_RegisterClientVariable( const char *szName, c
|
||||
return (cvar_t *)Cvar_Get( szName, szValue, flags|FCVAR_CLIENTDLL, Cvar_BuildAutoDescription( szName, flags|FCVAR_CLIENTDLL ));
|
||||
}
|
||||
|
||||
static int GAME_EXPORT Cmd_AddClientCommand( const char *cmd_name, xcommand_t function )
|
||||
{
|
||||
int flags = CMD_CLIENTDLL;
|
||||
|
||||
// a1ba: try to mitigate outdated client.dll vulnerabilities
|
||||
if( !Q_stricmp( cmd_name, "motd_write" ))
|
||||
flags |= CMD_PRIVILEGED;
|
||||
|
||||
return Cmd_AddCommandEx( cmd_name, function, "client command", flags, __func__ );
|
||||
}
|
||||
|
||||
/*
|
||||
=============
|
||||
pfnHookUserMsg
|
||||
@@ -2698,17 +2707,6 @@ static const char *pfnGetGameDirectory( void )
|
||||
return szGetGameDir;
|
||||
}
|
||||
|
||||
/*
|
||||
=============
|
||||
Key_LookupBinding
|
||||
|
||||
=============
|
||||
*/
|
||||
static const char *Key_LookupBinding( const char *pBinding )
|
||||
{
|
||||
return Key_KeynumToString( Key_GetKey( pBinding ));
|
||||
}
|
||||
|
||||
/*
|
||||
=============
|
||||
pfnGetLevelName
|
||||
@@ -4007,9 +4005,10 @@ void CL_UnloadProgs( void )
|
||||
qboolean CL_LoadProgs( const char *name )
|
||||
{
|
||||
static playermove_t gpMove;
|
||||
const dllfunc_t *func;
|
||||
CL_EXPORT_FUNCS GetClientAPI; // single export
|
||||
qboolean critical_exports = true;
|
||||
CL_EXPORT_FUNCS GetClientAPI; // single export
|
||||
qboolean valid_single_export = false;
|
||||
qboolean missed_exports = false;
|
||||
int i;
|
||||
|
||||
if( clgame.hInstance ) CL_UnloadProgs();
|
||||
|
||||
@@ -4020,36 +4019,19 @@ qboolean CL_LoadProgs( const char *name )
|
||||
clgame.mempool = Mem_AllocPool( "Client Edicts Zone" );
|
||||
clgame.entities = NULL;
|
||||
|
||||
|
||||
// a1ba: we need to check if client.dll has direct dependency on SDL2
|
||||
// and if so, disable relative mouse mode
|
||||
#if XASH_WIN32 && !XASH_64BIT
|
||||
if( ( clgame.client_dll_uses_sdl = COM_CheckLibraryDirectDependency( name, OS_LIB_PREFIX "SDL2." OS_LIB_EXT, false ) ) )
|
||||
{
|
||||
Con_Printf( S_NOTE "%s uses SDL2 for mouse input\n", name );
|
||||
}
|
||||
else
|
||||
{
|
||||
Con_Printf( S_NOTE "%s uses Windows API for mouse input\n", name );
|
||||
}
|
||||
#else
|
||||
// this doesn't mean other platforms uses SDL2 in any case
|
||||
// it just helps input code to stay platform-independent
|
||||
clgame.client_dll_uses_sdl = true;
|
||||
clgame.client_dll_uses_sdl = COM_CheckLibraryDirectDependency( name, OS_LIB_PREFIX "SDL2." OS_LIB_EXT, false );
|
||||
Con_Printf( S_NOTE "%s uses %s for mouse input\n", name, clgame.client_dll_uses_sdl ? "SDL2" : "Windows API" );
|
||||
#endif
|
||||
|
||||
// NOTE: important stuff!
|
||||
// vgui must startup BEFORE loading client.dll to avoid get error ERROR_NOACESS
|
||||
// during LoadLibrary
|
||||
// vgui must startup BEFORE loading client.dll to avoid get error ERROR_NOACESS during LoadLibrary
|
||||
if( !GI->internal_vgui_support && VGui_LoadProgs( NULL ))
|
||||
{
|
||||
VGui_Startup( refState.width, refState.height );
|
||||
}
|
||||
else
|
||||
{
|
||||
// we failed to load vgui_support, but let's probe client.dll for support anyway
|
||||
GI->internal_vgui_support = true;
|
||||
}
|
||||
GI->internal_vgui_support = true; // we failed to load vgui_support, but let's probe client.dll for support anyway
|
||||
|
||||
clgame.hInstance = COM_LoadLibrary( name, false, false );
|
||||
|
||||
@@ -4058,13 +4040,10 @@ qboolean CL_LoadProgs( const char *name )
|
||||
|
||||
// delayed vgui initialization for internal support
|
||||
if( GI->internal_vgui_support && VGui_LoadProgs( clgame.hInstance ))
|
||||
{
|
||||
VGui_Startup( refState.width, refState.height );
|
||||
}
|
||||
|
||||
// clear exports
|
||||
for( func = cdll_exports; func && func->name; func++ )
|
||||
*func->func = NULL;
|
||||
ClearExports( cdll_exports, ARRAYSIZE( cdll_exports ));
|
||||
|
||||
// trying to get single export
|
||||
if(( GetClientAPI = (void *)COM_GetProcAddress( clgame.hInstance, "GetClientAPI" )) != NULL )
|
||||
@@ -4082,56 +4061,44 @@ qboolean CL_LoadProgs( const char *name )
|
||||
CL_GetSecuredClientAPI( GetClientAPI );
|
||||
}
|
||||
|
||||
if ( GetClientAPI != NULL )
|
||||
{
|
||||
// check critical functions again
|
||||
for( func = cdll_exports; func && func->name; func++ )
|
||||
{
|
||||
if( func->func == NULL )
|
||||
break; // BAH critical function was missed
|
||||
}
|
||||
if( GetClientAPI != NULL ) // check critical functions again
|
||||
valid_single_export = ValidateExports( cdll_exports, ARRAYSIZE( cdll_exports ));
|
||||
|
||||
// because all the exports are loaded through function 'F"
|
||||
if( !func || !func->name )
|
||||
critical_exports = false;
|
||||
}
|
||||
|
||||
for( func = cdll_exports; func && func->name != NULL; func++ )
|
||||
for( i = 0; i < ARRAYSIZE( cdll_exports ); i++ )
|
||||
{
|
||||
if( *func->func != NULL )
|
||||
continue; // already get through 'F'
|
||||
if( *(cdll_exports[i].func) != NULL )
|
||||
continue; // already gott through 'F' or 'GetClientAPI'
|
||||
|
||||
// functions are cleared before all the extensions are evaluated
|
||||
if(( *func->func = (void *)COM_GetProcAddress( clgame.hInstance, func->name )) == NULL )
|
||||
if(( *(cdll_exports[i].func) = (void *)COM_GetProcAddress( clgame.hInstance, cdll_exports[i].name )) == NULL )
|
||||
{
|
||||
Con_Reportf( "%s: failed to get address of %s proc\n", __func__, func->name );
|
||||
Con_Reportf( S_ERROR "%s: failed to get address of %s proc\n", __func__, cdll_exports[i].name );
|
||||
|
||||
if( critical_exports )
|
||||
{
|
||||
COM_FreeLibrary( clgame.hInstance );
|
||||
clgame.hInstance = NULL;
|
||||
return false;
|
||||
}
|
||||
// print all not found exports at once, for debug
|
||||
missed_exports = true;
|
||||
}
|
||||
}
|
||||
|
||||
if( missed_exports )
|
||||
{
|
||||
COM_FreeLibrary( clgame.hInstance );
|
||||
clgame.hInstance = NULL;
|
||||
return false;
|
||||
}
|
||||
|
||||
// it may be loaded through 'GetClientAPI' so we don't need to clear them
|
||||
if( critical_exports )
|
||||
{
|
||||
// clear new exports
|
||||
for( func = cdll_new_exports; func && func->name; func++ )
|
||||
*func->func = NULL;
|
||||
}
|
||||
if( !valid_single_export )
|
||||
ClearExports( cdll_new_exports, ARRAYSIZE( cdll_new_exports ));
|
||||
|
||||
for( func = cdll_new_exports; func && func->name != NULL; func++ )
|
||||
for( i = 0; i < ARRAYSIZE( cdll_new_exports ); i++ )
|
||||
{
|
||||
if( *func->func != NULL )
|
||||
continue; // already get through 'F'
|
||||
if( *(cdll_new_exports[i].func) != NULL )
|
||||
continue; // already gott through 'F' or 'GetClientAPI'
|
||||
|
||||
// functions are cleared before all the extensions are evaluated
|
||||
// NOTE: new exports can be missed without stop the engine
|
||||
if(( *func->func = (void *)COM_GetProcAddress( clgame.hInstance, func->name )) == NULL )
|
||||
Con_Reportf( "%s: failed to get address of %s proc\n", __func__, func->name );
|
||||
if(( *(cdll_new_exports[i].func) = (void *)COM_GetProcAddress( clgame.hInstance, cdll_new_exports[i].name )) == NULL )
|
||||
Con_Reportf( S_WARN "%s: failed to get address of %s proc\n", __func__, cdll_new_exports[i].name );
|
||||
}
|
||||
|
||||
if( !clgame.dllFuncs.pfnInitialize( &gEngfuncs, CLDLL_INTERFACE_VERSION ))
|
||||
|
||||
@@ -694,6 +694,11 @@ static cvar_t *GAME_EXPORT pfnCvar_RegisterGameUIVariable( const char *szName, c
|
||||
return (cvar_t *)Cvar_Get( szName, szValue, flags|FCVAR_GAMEUIDLL, Cvar_BuildAutoDescription( szName, flags|FCVAR_GAMEUIDLL ));
|
||||
}
|
||||
|
||||
static int GAME_EXPORT Cmd_AddGameUICommand( const char *cmd_name, xcommand_t function )
|
||||
{
|
||||
return Cmd_AddCommandEx( cmd_name, function, "gameui command", CMD_GAMEUIDLL, __func__ );
|
||||
}
|
||||
|
||||
/*
|
||||
=============
|
||||
pfnClientCmd
|
||||
@@ -1109,7 +1114,7 @@ static void GAME_EXPORT UI_ShellExecute( const char *path, const char *parms, in
|
||||
Platform_ShellExecute( path, parms );
|
||||
|
||||
if( shouldExit )
|
||||
Sys_Quit();
|
||||
Sys_Quit( __func__ );
|
||||
}
|
||||
|
||||
/*
|
||||
|
||||
@@ -965,7 +965,7 @@ CL_Quit_f
|
||||
void CL_Quit_f( void )
|
||||
{
|
||||
CL_Disconnect();
|
||||
Sys_Quit();
|
||||
Sys_Quit( "command" );
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -1401,7 +1401,8 @@ static void CL_Connect_f( void )
|
||||
Q_strncpy( server, Cmd_Argv( 1 ), sizeof( server ));
|
||||
|
||||
// if running a local server, kill it and reissue
|
||||
if( SV_Active( )) Host_ShutdownServer();
|
||||
if( SV_Active( ))
|
||||
SV_Shutdown( "Server was killed due to connection to remote server\n" );
|
||||
NET_Config( true, !cl_nat.value ); // allow remote
|
||||
|
||||
Con_Printf( "server %s\n", server );
|
||||
@@ -3326,6 +3327,22 @@ static void CL_Escape_f( void )
|
||||
else UI_SetActiveMenu( true );
|
||||
}
|
||||
|
||||
static void CL_ListMessages_f( void )
|
||||
{
|
||||
int i;
|
||||
|
||||
Con_Printf( "num size name\n" );
|
||||
for( i = 0; i < MAX_USER_MESSAGES; i++ )
|
||||
{
|
||||
if( !COM_CheckStringEmpty( clgame.msg[i].name ))
|
||||
break;
|
||||
|
||||
Con_Printf( "%3d\t%3d\t%s\n", clgame.msg[i].number, clgame.msg[i].size, clgame.msg[i].name );
|
||||
}
|
||||
|
||||
Con_Printf( "Total %i messages\n", i );
|
||||
}
|
||||
|
||||
/*
|
||||
=================
|
||||
CL_InitLocal
|
||||
@@ -3483,6 +3500,7 @@ static void CL_InitLocal( void )
|
||||
Cmd_AddCommand( "richpresence_gamemode", Cmd_Null_f, "compatibility command, does nothing" );
|
||||
Cmd_AddCommand( "richpresence_update", Cmd_Null_f, "compatibility command, does nothing" );
|
||||
|
||||
Cmd_AddCommand( "cl_list_messages", CL_ListMessages_f, "list registered user messages" );
|
||||
}
|
||||
|
||||
//============================================================================
|
||||
|
||||
@@ -311,10 +311,13 @@ static client entity
|
||||
static void CL_ParseStaticEntity( sizebuf_t *msg )
|
||||
{
|
||||
int i, newnum;
|
||||
entity_state_t from, to;
|
||||
const entity_state_t from = { 0 };
|
||||
entity_state_t to;
|
||||
cl_entity_t *ent;
|
||||
|
||||
memset( &from, 0, sizeof( from ));
|
||||
if( !clgame.static_entities )
|
||||
clgame.static_entities = Mem_Calloc( clgame.mempool, sizeof( cl_entity_t ) * MAX_STATIC_ENTITIES );
|
||||
|
||||
newnum = MSG_ReadUBitLong( msg, MAX_ENTITY_BITS );
|
||||
MSG_ReadDeltaEntity( msg, &from, &to, 0, DELTA_STATIC, cl.mtime[0] );
|
||||
|
||||
@@ -1322,11 +1325,11 @@ void CL_ParseLightStyle( sizebuf_t *msg, connprotocol_t proto )
|
||||
{
|
||||
int style;
|
||||
const char *s;
|
||||
float f = 0.0f;
|
||||
float f = cl.mtime[0];
|
||||
|
||||
style = MSG_ReadByte( msg );
|
||||
s = MSG_ReadString( msg );
|
||||
if( proto != PROTO_GOLDSRC )
|
||||
if( proto != PROTO_GOLDSRC && proto != PROTO_QUAKE )
|
||||
f = MSG_ReadFloat( msg );
|
||||
|
||||
CL_SetLightstyle( style, s, f );
|
||||
@@ -1424,37 +1427,31 @@ register new user message or update existing
|
||||
void CL_RegisterUserMessage( sizebuf_t *msg, connprotocol_t proto )
|
||||
{
|
||||
char *pszName;
|
||||
int svc_num, size, bits;
|
||||
|
||||
svc_num = MSG_ReadByte( msg );
|
||||
char szName[17];
|
||||
int size;
|
||||
int svc_num = MSG_ReadByte( msg );
|
||||
|
||||
if( proto == PROTO_LEGACY || proto == PROTO_GOLDSRC )
|
||||
{
|
||||
size = MSG_ReadByte( msg );
|
||||
bits = 8;
|
||||
if( size == UINT8_MAX )
|
||||
size = -1;
|
||||
}
|
||||
else
|
||||
{
|
||||
size = MSG_ReadWord( msg );
|
||||
bits = 16;
|
||||
if( size == UINT16_MAX )
|
||||
size = -1;
|
||||
}
|
||||
|
||||
if( proto == PROTO_GOLDSRC )
|
||||
{
|
||||
static char szName[17];
|
||||
|
||||
MSG_ReadBytes( msg, szName, sizeof( szName ) - 1 );
|
||||
szName[16] = 0;
|
||||
|
||||
pszName = szName;
|
||||
}
|
||||
else pszName = MSG_ReadString( msg );
|
||||
|
||||
// important stuff
|
||||
if( size == ( BIT( bits ) - 1 ) )
|
||||
size = -1;
|
||||
svc_num = bound( 0, svc_num, 255 );
|
||||
|
||||
CL_LinkUserMessage( pszName, svc_num, size );
|
||||
}
|
||||
|
||||
@@ -1593,17 +1590,23 @@ collect pings and packet lossage from clients
|
||||
*/
|
||||
void CL_UpdateUserPings( sizebuf_t *msg )
|
||||
{
|
||||
int i, slot;
|
||||
player_info_t *player;
|
||||
|
||||
for( i = 0; i < MAX_CLIENTS; i++ )
|
||||
// a1ba: there was a MAX_PLAYERS check but it doesn't make sense
|
||||
// because pings message always ends by null bit
|
||||
while( 1 )
|
||||
{
|
||||
if( !MSG_ReadOneBit( msg )) break; // end of message
|
||||
int slot;
|
||||
player_info_t *player;
|
||||
|
||||
if( !MSG_ReadOneBit( msg ))
|
||||
break; // end of message
|
||||
|
||||
slot = MSG_ReadUBitLong( msg, MAX_CLIENT_BITS );
|
||||
|
||||
if( slot >= MAX_CLIENTS )
|
||||
if( unlikely( slot >= MAX_CLIENTS ))
|
||||
{
|
||||
Host_Error( "%s: svc_pings > MAX_CLIENTS\n", __func__ );
|
||||
return;
|
||||
}
|
||||
|
||||
player = &cl.players[slot];
|
||||
player->ping = MSG_ReadUBitLong( msg, 12 );
|
||||
|
||||
@@ -33,10 +33,12 @@ static client entity
|
||||
static void CL_LegacyParseStaticEntity( sizebuf_t *msg )
|
||||
{
|
||||
int i;
|
||||
entity_state_t state;
|
||||
entity_state_t state = { 0 };
|
||||
cl_entity_t *ent;
|
||||
|
||||
memset( &state, 0, sizeof( state ));
|
||||
if( !clgame.static_entities )
|
||||
clgame.static_entities = Mem_Calloc( clgame.mempool, sizeof( cl_entity_t ) * MAX_STATIC_ENTITIES );
|
||||
|
||||
state.modelindex = MSG_ReadShort( msg );
|
||||
state.sequence = MSG_ReadByte( msg );
|
||||
state.frame = MSG_ReadByte( msg );
|
||||
|
||||
@@ -615,7 +615,7 @@ void CL_ParseGoldSrcServerMessage( sizebuf_t *msg )
|
||||
case svc_setangle:
|
||||
CL_ParseSetAngle( msg );
|
||||
break;
|
||||
case svc_goldsrc_serverinfo:
|
||||
case svc_serverdata:
|
||||
Cbuf_Execute(); // make sure any stuffed commands are done
|
||||
CL_ParseServerData( msg, PROTO_GOLDSRC );
|
||||
break;
|
||||
@@ -625,7 +625,7 @@ void CL_ParseGoldSrcServerMessage( sizebuf_t *msg )
|
||||
case svc_updateuserinfo:
|
||||
CL_UpdateUserinfo( msg, PROTO_GOLDSRC );
|
||||
break;
|
||||
case svc_goldsrc_deltadescription:
|
||||
case svc_deltatable:
|
||||
Delta_ParseTableField_GS( msg );
|
||||
break;
|
||||
case svc_clientdata:
|
||||
@@ -687,7 +687,7 @@ void CL_ParseGoldSrcServerMessage( sizebuf_t *msg )
|
||||
case svc_addangle:
|
||||
CL_ParseAddAngle( msg );
|
||||
break;
|
||||
case svc_goldsrc_newusermsg:
|
||||
case svc_usermessage:
|
||||
CL_RegisterUserMessage( msg, PROTO_GOLDSRC );
|
||||
break;
|
||||
case svc_packetentities:
|
||||
@@ -709,7 +709,7 @@ void CL_ParseGoldSrcServerMessage( sizebuf_t *msg )
|
||||
CL_ParseResourceList( msg, PROTO_GOLDSRC );
|
||||
MSG_EndBitWriting( msg );
|
||||
break;
|
||||
case svc_goldsrc_newmovevars:
|
||||
case svc_deltamovevars:
|
||||
CL_ParseNewMovevars( msg );
|
||||
break;
|
||||
case svc_resourcerequest:
|
||||
@@ -749,10 +749,10 @@ void CL_ParseGoldSrcServerMessage( sizebuf_t *msg )
|
||||
Con_Reportf( S_ERROR "%s: svc_goldsrc_timescale: implement me!\n", __func__ );
|
||||
MSG_ReadFloat( msg );
|
||||
break;
|
||||
case svc_goldsrc_sendcvarvalue:
|
||||
case svc_querycvarvalue:
|
||||
CL_ParseCvarValue( msg, false, PROTO_GOLDSRC );
|
||||
break;
|
||||
case svc_goldsrc_sendcvarvalue2:
|
||||
case svc_querycvarvalue2:
|
||||
CL_ParseCvarValue( msg, true, PROTO_GOLDSRC );
|
||||
break;
|
||||
case svc_exec:
|
||||
|
||||
@@ -628,11 +628,12 @@ CL_ParseStaticEntity
|
||||
*/
|
||||
static void CL_ParseQuakeStaticEntity( sizebuf_t *msg )
|
||||
{
|
||||
entity_state_t state;
|
||||
entity_state_t state = { 0 };
|
||||
cl_entity_t *ent;
|
||||
int i;
|
||||
|
||||
memset( &state, 0, sizeof( state ));
|
||||
if( !clgame.static_entities )
|
||||
clgame.static_entities = Mem_Calloc( clgame.mempool, sizeof( cl_entity_t ) * MAX_STATIC_ENTITIES );
|
||||
|
||||
state.modelindex = MSG_ReadByte( msg );
|
||||
state.frame = MSG_ReadByte( msg );
|
||||
@@ -969,9 +970,7 @@ void CL_ParseQuakeMessage( sizebuf_t *msg )
|
||||
CL_ParseQuakeServerInfo( msg );
|
||||
break;
|
||||
case svc_lightstyle:
|
||||
param1 = MSG_ReadByte( msg );
|
||||
str = MSG_ReadString( msg );
|
||||
CL_SetLightstyle( param1, str, cl.mtime[0] );
|
||||
CL_ParseLightStyle( msg, PROTO_QUAKE );
|
||||
break;
|
||||
case svc_updatename:
|
||||
param1 = MSG_ReadByte( msg );
|
||||
|
||||
@@ -124,7 +124,7 @@ void SCR_DrawPos( void )
|
||||
if( cls.state != ca_active || !cl_showpos.value || cl.background )
|
||||
return;
|
||||
|
||||
ent = CL_EDICT_NUM( cl.playernum + 1 );
|
||||
ent = CL_GetLocalPlayer();
|
||||
speed = VectorLength( cl.simvel );
|
||||
|
||||
Q_snprintf( msg, MAX_SYSPATH,
|
||||
|
||||
@@ -2427,18 +2427,18 @@ static void CL_ClearLightStyles( void )
|
||||
|
||||
void CL_SetLightstyle( int style, const char *s, float f )
|
||||
{
|
||||
int i, k;
|
||||
lightstyle_t *ls;
|
||||
float val1, val2;
|
||||
int i;
|
||||
lightstyle_t *ls;
|
||||
|
||||
Assert( s != NULL );
|
||||
Assert( style >= 0 && style < MAX_LIGHTSTYLES );
|
||||
if( unlikely( style < 0 || style >= MAX_LIGHTSTYLES ))
|
||||
{
|
||||
Con_Printf( S_WARN "%s: ignored invalid lightstyle id %d\n", style );
|
||||
return;
|
||||
}
|
||||
|
||||
ls = &cl.lightstyles[style];
|
||||
|
||||
Q_strncpy( ls->pattern, s, sizeof( ls->pattern ));
|
||||
|
||||
ls->length = Q_strlen( s );
|
||||
ls->length = Q_strncpy( ls->pattern, s, sizeof( ls->pattern ));
|
||||
ls->time = f; // set local time
|
||||
|
||||
for( i = 0; i < ls->length; i++ )
|
||||
@@ -2448,10 +2448,10 @@ void CL_SetLightstyle( int style, const char *s, float f )
|
||||
|
||||
// check for allow interpolate
|
||||
// NOTE: fast flickering styles looks ugly when interpolation is running
|
||||
for( k = 0; k < (ls->length - 1); k++ )
|
||||
for( i = 0; i < ( ls->length - 1 ); i++ )
|
||||
{
|
||||
val1 = ls->map[(k+0) % ls->length];
|
||||
val2 = ls->map[(k+1) % ls->length];
|
||||
float val1 = ls->map[( i + 0 ) % ls->length];
|
||||
float val2 = ls->map[( i + 1 ) % ls->length];
|
||||
|
||||
if( fabs( val1 - val2 ) > STYLE_LERPING_THRESHOLD )
|
||||
{
|
||||
@@ -2471,8 +2471,8 @@ DLIGHT MANAGEMENT
|
||||
|
||||
==============================================================
|
||||
*/
|
||||
dlight_t cl_dlights[MAX_DLIGHTS];
|
||||
dlight_t cl_elights[MAX_ELIGHTS];
|
||||
static dlight_t cl_dlights[MAX_DLIGHTS];
|
||||
static dlight_t cl_elights[MAX_ELIGHTS];
|
||||
|
||||
/*
|
||||
================
|
||||
|
||||
@@ -495,7 +495,9 @@ typedef struct
|
||||
|
||||
cl_entity_t viewent; // viewmodel
|
||||
|
||||
#if XASH_WIN32
|
||||
qboolean client_dll_uses_sdl;
|
||||
#endif
|
||||
} clgame_static_t;
|
||||
|
||||
typedef struct
|
||||
@@ -881,10 +883,12 @@ static inline qboolean CL_IsThirdPerson( void )
|
||||
|
||||
static inline cl_entity_t *CL_GetLocalPlayer( void )
|
||||
{
|
||||
cl_entity_t *player;
|
||||
cl_entity_t *player = CL_GetEntityByIndex( cl.playernum + 1 );
|
||||
|
||||
player = CL_EDICT_NUM( cl.playernum + 1 );
|
||||
Assert( player != NULL );
|
||||
// HACKHACK: GoldSrc doesn't do this, but some mods actually check it for null pointer
|
||||
// this is a lesser evil than changing semantics of HUD_VidInit and call it after entities are allocated
|
||||
if( !player )
|
||||
Con_Printf( S_WARN "%s: client entities are not initialized yet! Returning NULL...\n", __func__ );
|
||||
|
||||
return player;
|
||||
}
|
||||
@@ -1195,10 +1199,9 @@ void Key_Init( void );
|
||||
void Key_WriteBindings( file_t *f );
|
||||
const char *Key_GetBinding( int keynum );
|
||||
void Key_SetBinding( int keynum, const char *binding );
|
||||
const char *Key_LookupBinding( const char *pBinding );
|
||||
void Key_ClearStates( void );
|
||||
const char *Key_KeynumToString( int keynum );
|
||||
int Key_StringToKeynum( const char *str );
|
||||
int Key_GetKey( const char *binding );
|
||||
void Key_EnumCmds_f( void );
|
||||
void Key_SetKeyDest( int key_dest );
|
||||
void Key_EnableTextInput( qboolean enable, qboolean force );
|
||||
|
||||
@@ -1101,7 +1101,7 @@ void Touch_Init( void )
|
||||
Touch_AddDefaultButton( "duck", "touch_default/crouch", "+duck", 0.880000, 0.757428, 1.000000, 0.984656, color, 2, 1, 0 );
|
||||
Touch_AddDefaultButton( "tduck", "touch_default/tduck", ";+duck", 0.560000, 0.833171, 0.620000, 0.946785, color, 2, 1, 0 );
|
||||
Touch_AddDefaultButton( "edit", "touch_default/settings", "touch_enableedit", 0.420000, 0.000000, 0.500000, 0.151486, color, 2, 1, 32 );
|
||||
Touch_AddDefaultButton( "menu", "touch_default/menu", "escape", 0.000000, 0.833171, 0.080000, 0.984656, color, 2, 1, 0 );
|
||||
Touch_AddDefaultButton( "menu", "touch_default/menu", "cancelselect", 0.000000, 0.833171, 0.080000, 0.984656, color, 2, 1, 0 );
|
||||
Touch_AddDefaultButton( "spray", "touch_default/spray", "impulse 201", 0.680000, 0.000000, 0.760000, 0.142222, color, 2, 1, 8 );
|
||||
Touch_AddDefaultButton( "voicechat", "touch_default/microphone", "+voicerecord", 0.780000, 0.817778, 0.860000, 0.960000, color, 2, 1, 8 );
|
||||
|
||||
|
||||
@@ -633,8 +633,6 @@ Called every frame, even if not generating commands
|
||||
*/
|
||||
void Host_InputFrame( void )
|
||||
{
|
||||
Sys_SendKeyEvents ();
|
||||
|
||||
IN_Commands();
|
||||
|
||||
IN_MouseMove();
|
||||
|
||||
@@ -34,13 +34,13 @@ typedef struct keyname_s
|
||||
const char *binding; // default bind
|
||||
} keyname_t;
|
||||
|
||||
enginekey_t keys[256];
|
||||
static enginekey_t keys[256];
|
||||
|
||||
keyname_t keynames[] =
|
||||
static const keyname_t keynames[] =
|
||||
{
|
||||
{"TAB", K_TAB, "" },
|
||||
{"ENTER", K_ENTER, "" },
|
||||
{"ESCAPE", K_ESCAPE, "escape" }, // hardcoded
|
||||
{"ESCAPE", K_ESCAPE, "cancelselect" }, // hardcoded
|
||||
{"SPACE", K_SPACE, "+jump" },
|
||||
{"BACKSPACE", K_BACKSPACE, "" },
|
||||
{"UPARROW", K_UPARROW, "+forward" },
|
||||
@@ -106,7 +106,7 @@ keyname_t keynames[] =
|
||||
{"Y_BUTTON", K_Y_BUTTON, "impulse 100"}, // Flashlight
|
||||
{"BACK", K_BACK_BUTTON, "pause"}, // Menu
|
||||
{"MODE", K_MODE_BUTTON, ""},
|
||||
{"START", K_START_BUTTON, "escape"},
|
||||
{"START", K_START_BUTTON, "cancelselect"},
|
||||
{"STICK1", K_LSTICK, "+speed"},
|
||||
{"STICK2", K_RSTICK, "+duck"},
|
||||
{"L1_BUTTON", K_L1_BUTTON, "+duck"},
|
||||
@@ -139,7 +139,6 @@ keyname_t keynames[] =
|
||||
|
||||
// raw semicolon seperates commands
|
||||
{"SEMICOLON", ';', "" },
|
||||
{NULL, 0, NULL },
|
||||
};
|
||||
|
||||
static void OSK_EnableTextInput( qboolean enable, qboolean force );
|
||||
@@ -172,48 +171,25 @@ the K_* names are matched up.
|
||||
to be configured even if they don't have defined names.
|
||||
===================
|
||||
*/
|
||||
int Key_StringToKeynum( const char *str )
|
||||
static int Key_StringToKeynum( const char *str )
|
||||
{
|
||||
keyname_t *kn;
|
||||
int i;
|
||||
|
||||
if( !str || !str[0] ) return -1;
|
||||
if( !str[1] ) return str[0];
|
||||
if( !str || !str[0] )
|
||||
return -1;
|
||||
|
||||
if( !str[1] )
|
||||
return str[0];
|
||||
|
||||
// check for hex code
|
||||
if( str[0] == '0' && str[1] == 'x' && Q_strlen( str ) == 4 )
|
||||
{
|
||||
int n1, n2;
|
||||
|
||||
n1 = str[2];
|
||||
if( n1 >= '0' && n1 <= '9' )
|
||||
{
|
||||
n1 -= '0';
|
||||
}
|
||||
else if( n1 >= 'a' && n1 <= 'f' )
|
||||
{
|
||||
n1 = n1 - 'a' + 10;
|
||||
}
|
||||
else n1 = 0;
|
||||
|
||||
n2 = str[3];
|
||||
if( n2 >= '0' && n2 <= '9' )
|
||||
{
|
||||
n2 -= '0';
|
||||
}
|
||||
else if( n2 >= 'a' && n2 <= 'f' )
|
||||
{
|
||||
n2 = n2 - 'a' + 10;
|
||||
}
|
||||
else n2 = 0;
|
||||
|
||||
return n1 * 16 + n2;
|
||||
}
|
||||
return COM_Nibble( str[2] ) << 4 | COM_Nibble( str[3] );
|
||||
|
||||
// scan for a text match
|
||||
for( kn = keynames; kn->name; kn++ )
|
||||
for( i = 0; i < ARRAYSIZE( keynames ); i++ )
|
||||
{
|
||||
if( !Q_stricmp( str, kn->name ))
|
||||
return kn->keynum;
|
||||
if( !Q_stricmp( str, keynames[i].name ))
|
||||
return keynames[i].keynum;
|
||||
}
|
||||
|
||||
return -1;
|
||||
@@ -229,12 +205,14 @@ given keynum.
|
||||
*/
|
||||
const char *Key_KeynumToString( int keynum )
|
||||
{
|
||||
keyname_t *kn;
|
||||
static char tinystr[5];
|
||||
int i, j;
|
||||
|
||||
if ( keynum == -1 ) return "<KEY NOT FOUND>";
|
||||
if ( keynum < 0 || keynum > 255 ) return "<OUT OF RANGE>";
|
||||
if( keynum == -1 )
|
||||
return "<KEY NOT FOUND>";
|
||||
|
||||
if( keynum < 0 || keynum > 255 )
|
||||
return "<OUT OF RANGE>";
|
||||
|
||||
// check for printable ascii (don't use quote)
|
||||
if( keynum > 32 && keynum < 127 && keynum != '"' && keynum != ';' && keynum != K_SCROLLOCK )
|
||||
@@ -245,10 +223,10 @@ const char *Key_KeynumToString( int keynum )
|
||||
}
|
||||
|
||||
// check for a key string
|
||||
for( kn = keynames; kn->name; kn++ )
|
||||
for( i = 0; i < ARRAYSIZE( keynames ); i++ )
|
||||
{
|
||||
if( keynum == kn->keynum )
|
||||
return kn->name;
|
||||
if( keynum == keynames[i].keynum )
|
||||
return keynames[i].name;
|
||||
}
|
||||
|
||||
// make a hex string
|
||||
@@ -301,7 +279,7 @@ const char *Key_GetBinding( int keynum )
|
||||
Key_GetKey
|
||||
===================
|
||||
*/
|
||||
int Key_GetKey( const char *pBinding )
|
||||
static int Key_GetKey( const char *pBinding )
|
||||
{
|
||||
int i, len;
|
||||
const char *p;
|
||||
@@ -327,6 +305,17 @@ int Key_GetKey( const char *pBinding )
|
||||
return -1;
|
||||
}
|
||||
|
||||
/*
|
||||
=============
|
||||
Key_LookupBinding
|
||||
|
||||
=============
|
||||
*/
|
||||
const char *Key_LookupBinding( const char *pBinding )
|
||||
{
|
||||
return Key_KeynumToString( Key_GetKey( pBinding ));
|
||||
}
|
||||
|
||||
/*
|
||||
===================
|
||||
Key_Unbind_f
|
||||
@@ -350,6 +339,12 @@ static void Key_Unbind_f( void )
|
||||
return;
|
||||
}
|
||||
|
||||
if( b == K_ESCAPE )
|
||||
{
|
||||
Con_Printf( "Can't unbind ESCAPE key\n" );
|
||||
return;
|
||||
}
|
||||
|
||||
Key_SetBinding( b, "" );
|
||||
}
|
||||
|
||||
@@ -369,8 +364,8 @@ static void Key_Unbindall_f( void )
|
||||
}
|
||||
|
||||
// set some defaults
|
||||
Key_SetBinding( K_ESCAPE, "escape" );
|
||||
Key_SetBinding( K_START_BUTTON, "escape" );
|
||||
Key_SetBinding( K_ESCAPE, "cancelselect" );
|
||||
Key_SetBinding( K_START_BUTTON, "cancelselect" );
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -380,7 +375,6 @@ Key_Reset_f
|
||||
*/
|
||||
static void Key_Reset_f( void )
|
||||
{
|
||||
keyname_t *kn;
|
||||
int i;
|
||||
|
||||
// clear all keys first
|
||||
@@ -391,8 +385,8 @@ static void Key_Reset_f( void )
|
||||
}
|
||||
|
||||
// apply default values
|
||||
for( kn = keynames; kn->name; kn++ )
|
||||
Key_SetBinding( kn->keynum, kn->binding );
|
||||
for( i = 0; i < ARRAYSIZE( keynames ); i++ )
|
||||
Key_SetBinding( keynames[i].keynum, keynames[i].binding );
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -500,7 +494,7 @@ Key_Init
|
||||
*/
|
||||
void Key_Init( void )
|
||||
{
|
||||
keyname_t *kn;
|
||||
int i;
|
||||
|
||||
// register our functions
|
||||
Cmd_AddRestrictedCommand( "bind", Key_Bind_f, "binds a command to the specified key in bindmap" );
|
||||
@@ -511,7 +505,8 @@ void Key_Init( void )
|
||||
Cmd_AddCommand( "makehelp", Key_EnumCmds_f, "write help.txt that contains all console cvars and cmds" );
|
||||
|
||||
// setup default binding. "unbindall" from config.cfg will be reset it
|
||||
for( kn = keynames; kn->name; kn++ ) Key_SetBinding( kn->keynum, kn->binding );
|
||||
for( i = 0; i < ARRAYSIZE( keynames ); i++ )
|
||||
Key_SetBinding( keynames[i].keynum, keynames[i].binding );
|
||||
|
||||
Cvar_RegisterVariable( &osk_enable );
|
||||
Cvar_RegisterVariable( &key_rotate );
|
||||
@@ -748,18 +743,24 @@ void GAME_EXPORT Key_Event( int key, int down )
|
||||
|
||||
if( cls.key_dest == key_menu )
|
||||
{
|
||||
// only non printable keys passed
|
||||
// classic Xash3D menus don't have an extension that tells engine
|
||||
// to enable text input
|
||||
if( !gameui.use_extended_api )
|
||||
Key_EnableTextInput( true, false );
|
||||
//pass printable chars for old menus
|
||||
if( !gameui.use_extended_api && !host.textmode && down && ( key >= 32 ) && ( key <= 'z' ) )
|
||||
{
|
||||
if( Key_IsDown( K_SHIFT ) )
|
||||
// we don't know if menu wants text input or not
|
||||
// enable it unconditionally
|
||||
Key_EnableTextInput( true, false );
|
||||
|
||||
// pass this key to the menu, if printable
|
||||
if( !host.textmode && down && ( key >= 32 ) && ( key <= 'z' ))
|
||||
{
|
||||
key += 'A' - 'a';
|
||||
if( Key_IsDown( K_SHIFT ))
|
||||
key += 'A' - 'a';
|
||||
|
||||
UI_CharEvent( key );
|
||||
}
|
||||
UI_CharEvent( key );
|
||||
}
|
||||
|
||||
UI_KeyEvent( key, down );
|
||||
return;
|
||||
}
|
||||
@@ -855,11 +856,12 @@ void GAME_EXPORT Key_ClearStates( void )
|
||||
int i;
|
||||
|
||||
// don't clear keys during changelevel
|
||||
if( cls.changelevel ) return;
|
||||
if( cls.changelevel )
|
||||
return;
|
||||
|
||||
for( i = 0; i < 256; i++ )
|
||||
{
|
||||
if( keys[i].down )
|
||||
if( keys[i].down && i < K_MOUSE1 && i > K_MOUSE5 )
|
||||
Key_Event( i, false );
|
||||
|
||||
keys[i].down = 0;
|
||||
@@ -867,6 +869,9 @@ void GAME_EXPORT Key_ClearStates( void )
|
||||
keys[i].gamedown = 0;
|
||||
}
|
||||
|
||||
for( i = K_MOUSE1; i < K_MOUSE5; i++ ) // from K_MOUSE1 to K_MOUSE5
|
||||
IN_MouseEvent( i - K_MOUSE1, false );
|
||||
|
||||
if( clgame.hInstance )
|
||||
clgame.dllFuncs.IN_ClearStates();
|
||||
}
|
||||
@@ -909,17 +914,16 @@ A helper function if platform input doesn't support text mode properly
|
||||
*/
|
||||
int Key_ToUpper( int keynum )
|
||||
{
|
||||
keynum = Q_toupper( keynum );
|
||||
if( keynum == '-' )
|
||||
keynum = '_';
|
||||
return '_';
|
||||
if( keynum == '=' )
|
||||
keynum = '+';
|
||||
return '+';
|
||||
if( keynum == ';' )
|
||||
keynum = ':';
|
||||
return ':';
|
||||
if( keynum == '\'' )
|
||||
keynum = '"';
|
||||
return '"';
|
||||
|
||||
return keynum;
|
||||
return Q_toupper( keynum );
|
||||
}
|
||||
|
||||
/* On-screen keyboard:
|
||||
|
||||
@@ -181,6 +181,11 @@ static void pfnCvar_FullSet( const char *var_name, const char *value, int flags
|
||||
Cvar_FullSet( var_name, value, flags | FCVAR_REFDLL );
|
||||
}
|
||||
|
||||
static int Cmd_AddRefCommand( const char *cmd_name, xcommand_t function, const char *description )
|
||||
{
|
||||
return Cmd_AddCommandEx( cmd_name, function, description, CMD_REFDLL, __func__ );
|
||||
}
|
||||
|
||||
static void pfnStudioEvent( const mstudioevent_t *event, const cl_entity_t *e )
|
||||
{
|
||||
clgame.dllFuncs.pfnStudioEvent( event, e );
|
||||
|
||||
@@ -1944,7 +1944,8 @@ qboolean S_Init( void )
|
||||
Cmd_AddCommand( "play2", S_Play2_f, "playing a group of specified sound files" ); // nehahra stuff
|
||||
Cmd_AddCommand( "playvol", S_PlayVol_f, "playing a specified sound file with specified volume" );
|
||||
Cmd_AddCommand( "stopsound", S_StopSound_f, "stop all sounds" );
|
||||
Cmd_AddCommand( "music", S_Music_f, "starting a background track" );
|
||||
// HLU SDK have command with the same name
|
||||
Cmd_AddCommandWithFlags( "music", S_Music_f, "starting a background track", CMD_OVERRIDABLE );
|
||||
Cmd_AddCommand( "soundlist", S_SoundList_f, "display loaded sounds" );
|
||||
Cmd_AddCommand( "s_info", S_SoundInfo_f, "print sound system information" );
|
||||
Cmd_AddCommand( "s_fade", S_SoundFade_f, "fade all sounds then stop all" );
|
||||
@@ -1987,7 +1988,8 @@ void S_Shutdown( void )
|
||||
Cmd_RemoveCommand( "play" );
|
||||
Cmd_RemoveCommand( "playvol" );
|
||||
Cmd_RemoveCommand( "stopsound" );
|
||||
Cmd_RemoveCommand( "music" );
|
||||
if( Cmd_Exists( "music" ))
|
||||
Cmd_RemoveCommand( "music" );
|
||||
Cmd_RemoveCommand( "soundlist" );
|
||||
Cmd_RemoveCommand( "s_info" );
|
||||
Cmd_RemoveCommand( "s_fade" );
|
||||
|
||||
@@ -25,20 +25,29 @@ GNU General Public License for more details.
|
||||
|
||||
#define VGUI_MAX_TEXTURES 1024
|
||||
|
||||
typedef struct vgui_reusable_texture_s
|
||||
{
|
||||
int gl_texturenum;
|
||||
byte hash[16];
|
||||
} vgui_reusable_texture_t;
|
||||
|
||||
typedef struct vgui_static_s
|
||||
{
|
||||
qboolean initialized;
|
||||
VGUI_DefaultCursor cursor;
|
||||
vguiapi_t dllFuncs;
|
||||
|
||||
int textures[VGUI_MAX_TEXTURES];
|
||||
vgui_reusable_texture_t *textures;
|
||||
int texture_id;
|
||||
int max_textures;
|
||||
int bound_texture;
|
||||
byte color[4];
|
||||
qboolean enable_texture;
|
||||
|
||||
HINSTANCE hInstance;
|
||||
|
||||
poolhandle_t mempool;
|
||||
|
||||
enum VGUI_KeyCode virtualKeyTrans[256];
|
||||
} vgui_static_t;
|
||||
|
||||
@@ -49,9 +58,16 @@ static CVAR_DEFINE_AUTO( vgui_utf8, "0", FCVAR_ARCHIVE, "enable utf-8 support fo
|
||||
|
||||
static void GAME_EXPORT VGUI_DrawInit( void )
|
||||
{
|
||||
memset( vgui.textures, 0, sizeof( vgui.textures ));
|
||||
if( vgui.mempool )
|
||||
Mem_EmptyPool( vgui.mempool );
|
||||
else vgui.mempool = Mem_AllocPool( "VGui Support Pool" );
|
||||
|
||||
vgui.textures = NULL;
|
||||
|
||||
memset( vgui.color, 0, sizeof( vgui.color ));
|
||||
vgui.texture_id = vgui.bound_texture = 0;
|
||||
vgui.texture_id = 0;
|
||||
vgui.bound_texture = 0;
|
||||
vgui.max_textures = 0;
|
||||
vgui.enable_texture = true;
|
||||
}
|
||||
|
||||
@@ -60,28 +76,79 @@ static void GAME_EXPORT VGUI_DrawShutdown( void )
|
||||
int i;
|
||||
|
||||
for( i = 1; i < vgui.texture_id; i++ )
|
||||
ref.dllFuncs.GL_FreeTexture( vgui.textures[i] );
|
||||
ref.dllFuncs.GL_FreeTexture( vgui.textures[i].gl_texturenum );
|
||||
|
||||
Mem_FreePool( &vgui.mempool );
|
||||
vgui.textures = NULL;
|
||||
|
||||
memset( vgui.color, 0, sizeof( vgui.color ));
|
||||
vgui.texture_id = 0;
|
||||
vgui.bound_texture = 0;
|
||||
vgui.max_textures = 0;
|
||||
}
|
||||
|
||||
static int GAME_EXPORT VGUI_GenerateTexture( void )
|
||||
{
|
||||
if( ++vgui.texture_id >= VGUI_MAX_TEXTURES )
|
||||
Host_Error( "%s: VGUI_MAX_TEXTURES limit exceeded\n", __func__ );
|
||||
// allocate new
|
||||
if( vgui.texture_id + 1 >= vgui.max_textures )
|
||||
{
|
||||
if( vgui.max_textures + VGUI_MAX_TEXTURES >= VGUI_MAX_TEXTURES * VGUI_MAX_TEXTURES )
|
||||
{
|
||||
// in theory it might look up texture that hasn't been bound for a while and
|
||||
// reuse that but it will eventually overwrite some important textures anyway
|
||||
Con_Printf( S_ERROR "%s: Refusing resizing VGUI textures array due to memory leak\n", __func__ );
|
||||
return vgui.texture_id;
|
||||
}
|
||||
|
||||
return vgui.texture_id;
|
||||
vgui.max_textures += VGUI_MAX_TEXTURES;
|
||||
|
||||
// this potentially might leak memory if VGUI is used incorrectly!
|
||||
// (like in Cry of Fear)
|
||||
vgui.textures = Mem_Realloc( vgui.mempool, vgui.textures, sizeof( *vgui.textures ) * vgui.max_textures );
|
||||
|
||||
// warn mod developer
|
||||
if( vgui.max_textures >= VGUI_MAX_TEXTURES * 4 )
|
||||
Con_Printf( S_ERROR "%s: Potential memory leak in VGUI code is detected!\n", __func__ );
|
||||
}
|
||||
|
||||
return ++vgui.texture_id;
|
||||
}
|
||||
|
||||
static void GAME_EXPORT VGUI_UploadTexture( int id, const char *buffer, int width, int height )
|
||||
{
|
||||
rgbdata_t r_image = { 0 };
|
||||
char texName[32];
|
||||
MD5Context_t ctx;
|
||||
byte hash[16];
|
||||
|
||||
if( id <= 0 || id >= VGUI_MAX_TEXTURES )
|
||||
if( id <= 0 || id >= vgui.max_textures || width <= 0 || height <= 0 )
|
||||
{
|
||||
Con_DPrintf( S_ERROR "%s: bad texture %i. Ignored\n", __func__, id );
|
||||
return;
|
||||
}
|
||||
|
||||
// need to do this as some mods tend to upload same texture over and over
|
||||
// exhausing engine-wide limit on textures and leaking vram
|
||||
MD5Init( &ctx );
|
||||
MD5Update( &ctx, buffer, width * height * 4 );
|
||||
MD5Final( hash, &ctx );
|
||||
|
||||
// it's a new texture, try to find a copy
|
||||
if( vgui.textures[id].gl_texturenum == 0 )
|
||||
{
|
||||
int i;
|
||||
|
||||
for( i = 1; i < vgui.texture_id; i++ )
|
||||
{
|
||||
if( vgui.textures[i].gl_texturenum != 0 && !memcmp( vgui.textures[i].hash, hash, sizeof( hash )))
|
||||
{
|
||||
// copy data to new texture id
|
||||
vgui.textures[id] = vgui.textures[i];
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Q_snprintf( texName, sizeof( texName ), "*vgui%i", id );
|
||||
|
||||
r_image.width = width;
|
||||
@@ -91,51 +158,29 @@ static void GAME_EXPORT VGUI_UploadTexture( int id, const char *buffer, int widt
|
||||
r_image.flags = IMAGE_HAS_COLOR|IMAGE_HAS_ALPHA;
|
||||
r_image.buffer = (byte*)buffer;
|
||||
|
||||
vgui.textures[id] = GL_LoadTextureInternal( texName, &r_image, TF_IMAGE );
|
||||
vgui.textures[id].gl_texturenum = GL_LoadTextureInternal( texName, &r_image, TF_IMAGE );
|
||||
memcpy( vgui.textures[id].hash, hash, sizeof( hash ));
|
||||
}
|
||||
|
||||
static void GAME_EXPORT VGUI_CreateTexture( int id, int width, int height )
|
||||
{
|
||||
rgbdata_t r_image = { 0 };
|
||||
char texName[32];
|
||||
// nothing uses it, it can be removed
|
||||
Host_Error( "%s: deprecated\n", __func__ );
|
||||
|
||||
if( id <= 0 || id >= VGUI_MAX_TEXTURES )
|
||||
{
|
||||
Con_DPrintf( S_ERROR "%s: bad texture %i. Ignored\n", __func__, id );
|
||||
return;
|
||||
}
|
||||
|
||||
Q_snprintf( texName, sizeof( texName ), "*vgui%i", id );
|
||||
|
||||
r_image.width = width;
|
||||
r_image.height = height;
|
||||
r_image.type = PF_RGBA_32;
|
||||
r_image.size = width * height * 4;
|
||||
r_image.flags = IMAGE_HAS_COLOR|IMAGE_HAS_ALPHA;
|
||||
r_image.buffer = NULL;
|
||||
|
||||
vgui.textures[id] = GL_LoadTextureInternal( texName, &r_image, TF_IMAGE );
|
||||
vgui.bound_texture = id;
|
||||
}
|
||||
|
||||
static void GAME_EXPORT VGUI_UploadTextureBlock( int id, int drawX, int drawY, const byte *rgba, int blockWidth, int blockHeight )
|
||||
{
|
||||
if( id <= 0 || id >= VGUI_MAX_TEXTURES || vgui.textures[id] == 0 )
|
||||
{
|
||||
Con_DPrintf( S_ERROR "%s: bad texture %i. Ignored\n", __func__, id );
|
||||
return;
|
||||
}
|
||||
|
||||
ref.dllFuncs.VGUI_UploadTextureBlock( drawX, drawY, rgba, blockWidth, blockHeight );
|
||||
vgui.bound_texture = id;
|
||||
// nothing uses it, it can be removed
|
||||
Host_Error( "%s: deprecated\n", __func__ );
|
||||
}
|
||||
|
||||
static void GAME_EXPORT VGUI_BindTexture( int id )
|
||||
{
|
||||
if( id <= 0 || id >= VGUI_MAX_TEXTURES || !vgui.textures[id] )
|
||||
if( id <= 0 || id >= vgui.max_textures || !vgui.textures[id].gl_texturenum )
|
||||
id = 1; // NOTE: same as bogus index 2700 in GoldSrc
|
||||
|
||||
ref.dllFuncs.GL_Bind( XASH_TEXTURE0, vgui.textures[id] );
|
||||
ref.dllFuncs.GL_Bind( XASH_TEXTURE0, vgui.textures[id].gl_texturenum );
|
||||
vgui.bound_texture = id;
|
||||
}
|
||||
|
||||
@@ -144,7 +189,7 @@ static void GAME_EXPORT VGUI_GetTextureSizes( int *w, int *h )
|
||||
int texnum;
|
||||
|
||||
if( vgui.bound_texture )
|
||||
texnum = vgui.textures[vgui.bound_texture];
|
||||
texnum = vgui.textures[vgui.bound_texture].gl_texturenum;
|
||||
else
|
||||
texnum = R_GetBuiltinTexture( REF_DEFAULT_TEXTURE );
|
||||
|
||||
@@ -187,7 +232,7 @@ static void GAME_EXPORT VGUI_DrawQuad( const vpoint_t *ul, const vpoint_t *lr )
|
||||
t2 = lr->coord[1];
|
||||
|
||||
ref.dllFuncs.Color4ub( vgui.color[0], vgui.color[1], vgui.color[2], vgui.color[3] );
|
||||
ref.dllFuncs.R_DrawStretchPic( x, y, w, h, s1, t1, s2, t2, vgui.textures[vgui.bound_texture] );
|
||||
ref.dllFuncs.R_DrawStretchPic( x, y, w, h, s1, t1, s2, t2, vgui.textures[vgui.bound_texture].gl_texturenum );
|
||||
}
|
||||
else
|
||||
{
|
||||
|
||||
@@ -142,28 +142,33 @@ void Cbuf_AddFilteredText( const char *text )
|
||||
Cbuf_InsertText
|
||||
|
||||
Adds command text immediately after the current command
|
||||
Adds a \n to the text
|
||||
============
|
||||
*/
|
||||
static void Cbuf_InsertTextToBuffer( cmdbuf_t *buf, const char *text )
|
||||
static void Cbuf_InsertTextToBuffer( cmdbuf_t *buf, const char *text, size_t len, size_t requested_len )
|
||||
{
|
||||
int l = Q_strlen( text );
|
||||
|
||||
if(( buf->cursize + l ) >= buf->maxsize )
|
||||
if(( buf->cursize + requested_len ) >= buf->maxsize )
|
||||
{
|
||||
Con_Reportf( S_WARN "%s: overflow\n", __func__ );
|
||||
}
|
||||
else
|
||||
{
|
||||
memmove( buf->data + l, buf->data, buf->cursize );
|
||||
memcpy( buf->data, text, l );
|
||||
buf->cursize += l;
|
||||
memmove( buf->data + len, buf->data, buf->cursize );
|
||||
memcpy( buf->data, text, len );
|
||||
buf->cursize += len;
|
||||
}
|
||||
}
|
||||
|
||||
void Cbuf_InsertTextLen( const char *text, size_t len, size_t requested_len )
|
||||
{
|
||||
// sometimes we need to insert more data than we have
|
||||
// but also prevent overflow
|
||||
Cbuf_InsertTextToBuffer( &cmd_text, text, len, requested_len );
|
||||
}
|
||||
|
||||
void Cbuf_InsertText( const char *text )
|
||||
{
|
||||
Cbuf_InsertTextToBuffer( &cmd_text, text );
|
||||
size_t l = Q_strlen( text );
|
||||
Cbuf_InsertTextToBuffer( &cmd_text, text, l, l );
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -263,10 +268,16 @@ Cbuf_Execute
|
||||
void Cbuf_Execute( void )
|
||||
{
|
||||
Cbuf_ExecuteCommandsFromBuffer( &cmd_text, true, -1 );
|
||||
// a1ba: unlimited commands for filtered buffer per frame
|
||||
|
||||
// a1ba: goldsrc limits unprivileged commands per frame to 1 here
|
||||
// I don't see any sense in restricting that at this moment
|
||||
// but in future we may limit this
|
||||
Cbuf_ExecuteCommandsFromBuffer( &filteredcmd_text, false, -1 );
|
||||
|
||||
// a1ba: there is little to no sense limit privileged commands in
|
||||
// local game, as client runs server code anyway
|
||||
// do this for singleplayer only though, to make it easier to catch
|
||||
// possible bugs during local multiplayer testing
|
||||
Cbuf_ExecuteCommandsFromBuffer( &filteredcmd_text, SV_Active() && SV_GetMaxClients() == 1, -1 );
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -512,14 +523,14 @@ static void Cmd_UnAlias_f ( void )
|
||||
|
||||
=============================================================================
|
||||
*/
|
||||
typedef struct cmd_s
|
||||
struct cmd_s
|
||||
{
|
||||
struct cmd_s *next;
|
||||
char *name;
|
||||
xcommand_t function;
|
||||
int flags;
|
||||
char *desc;
|
||||
} cmd_t;
|
||||
};
|
||||
|
||||
static int cmd_argc;
|
||||
static const char *cmd_args = NULL;
|
||||
@@ -670,8 +681,7 @@ void Cmd_TokenizeString( const char *text )
|
||||
Cmd_AddCommandEx
|
||||
============
|
||||
*/
|
||||
static int Cmd_AddCommandEx( const char *funcname, const char *cmd_name, xcommand_t function,
|
||||
const char *cmd_desc, int iFlags )
|
||||
int Cmd_AddCommandEx( const char *cmd_name, xcommand_t function, const char *cmd_desc, int iFlags, const char *funcname )
|
||||
{
|
||||
cmd_t *cmd, *cur, *prev;
|
||||
|
||||
@@ -688,11 +698,28 @@ static int Cmd_AddCommandEx( const char *funcname, const char *cmd_name, xcomman
|
||||
return 0;
|
||||
}
|
||||
|
||||
// fail if the command already exists
|
||||
if( Cmd_Exists( cmd_name ))
|
||||
// fail if the command already exists and cannot be overriden
|
||||
cmd = Cmd_Exists( cmd_name );
|
||||
if( cmd )
|
||||
{
|
||||
Con_DPrintf( S_ERROR "%s: %s already defined\n", funcname, cmd_name );
|
||||
return 0;
|
||||
// some mods register commands that share the name with some engine's commands
|
||||
// when they aren't critical to keep engine running, we can let mods to override them
|
||||
// unfortunately, we lose original command this way
|
||||
if( FBitSet( cmd->flags, CMD_OVERRIDABLE ))
|
||||
{
|
||||
Mem_Free( cmd->desc );
|
||||
cmd->desc = copystring( cmd_desc );
|
||||
cmd->function = function;
|
||||
cmd->flags = iFlags;
|
||||
|
||||
Con_DPrintf( S_WARN "%s: %s already defined but is allowed to be overriden\n", funcname, cmd_name );
|
||||
return 1;
|
||||
}
|
||||
else
|
||||
{
|
||||
Con_DPrintf( "%s%s: %s already defined\n", cmd->function == function ? S_WARN : S_ERROR, funcname, cmd_name );
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
// use a small malloc to avoid zone fragmentation
|
||||
@@ -716,75 +743,6 @@ static int Cmd_AddCommandEx( const char *funcname, const char *cmd_name, xcomman
|
||||
return 1;
|
||||
}
|
||||
|
||||
/*
|
||||
============
|
||||
Cmd_AddCommand
|
||||
============
|
||||
*/
|
||||
void Cmd_AddCommand( const char *cmd_name, xcommand_t function, const char *cmd_desc )
|
||||
{
|
||||
Cmd_AddCommandEx( __func__, cmd_name, function, cmd_desc, 0 );
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
============
|
||||
Cmd_AddRestrictedCommand
|
||||
============
|
||||
*/
|
||||
void Cmd_AddRestrictedCommand( const char *cmd_name, xcommand_t function, const char *cmd_desc )
|
||||
{
|
||||
Cmd_AddCommandEx( __func__, cmd_name, function, cmd_desc, CMD_PRIVILEGED );
|
||||
}
|
||||
|
||||
/*
|
||||
============
|
||||
Cmd_AddServerCommand
|
||||
============
|
||||
*/
|
||||
void GAME_EXPORT Cmd_AddServerCommand( const char *cmd_name, xcommand_t function )
|
||||
{
|
||||
Cmd_AddCommandEx( __func__, cmd_name, function, "server command", CMD_SERVERDLL );
|
||||
}
|
||||
|
||||
/*
|
||||
============
|
||||
Cmd_AddClientCommand
|
||||
============
|
||||
*/
|
||||
int GAME_EXPORT Cmd_AddClientCommand( const char *cmd_name, xcommand_t function )
|
||||
{
|
||||
int flags = CMD_CLIENTDLL;
|
||||
|
||||
// a1ba: try to mitigate outdated client.dll vulnerabilities
|
||||
if( !Q_stricmp( cmd_name, "motd_write" ))
|
||||
{
|
||||
flags |= CMD_PRIVILEGED;
|
||||
}
|
||||
|
||||
return Cmd_AddCommandEx( __func__, cmd_name, function, "client command", flags );
|
||||
}
|
||||
|
||||
/*
|
||||
============
|
||||
Cmd_AddGameUICommand
|
||||
============
|
||||
*/
|
||||
int GAME_EXPORT Cmd_AddGameUICommand( const char *cmd_name, xcommand_t function )
|
||||
{
|
||||
return Cmd_AddCommandEx( __func__, cmd_name, function, "gameui command", CMD_GAMEUIDLL );
|
||||
}
|
||||
|
||||
/*
|
||||
============
|
||||
Cmd_AddRefCommand
|
||||
============
|
||||
*/
|
||||
int Cmd_AddRefCommand( const char *cmd_name, xcommand_t function, const char *description )
|
||||
{
|
||||
return Cmd_AddCommandEx( __func__, cmd_name, function, description, CMD_REFDLL );
|
||||
}
|
||||
|
||||
/*
|
||||
============
|
||||
Cmd_RemoveCommand
|
||||
@@ -853,19 +811,18 @@ void Cmd_LookupCmds( void *buffer, void *ptr, setpair_t callback )
|
||||
Cmd_Exists
|
||||
============
|
||||
*/
|
||||
qboolean Cmd_Exists( const char *cmd_name )
|
||||
cmd_t *Cmd_Exists( const char *cmd_name )
|
||||
{
|
||||
#if defined(XASH_HASHED_VARS)
|
||||
return BaseCmd_Find( HM_CMD, cmd_name ) != NULL;
|
||||
return BaseCmd_Find( HM_CMD, cmd_name );
|
||||
#else
|
||||
cmd_t *cmd;
|
||||
|
||||
for( cmd = cmd_functions; cmd; cmd = cmd->next )
|
||||
{
|
||||
if( !Q_strcmp( cmd_name, cmd->name ))
|
||||
return true;
|
||||
return cmd;
|
||||
}
|
||||
return false;
|
||||
return NULL;
|
||||
#endif
|
||||
}
|
||||
|
||||
@@ -1058,9 +1015,10 @@ static void Cmd_ExecuteStringWithPrivilegeCheck( const char *text, qboolean isPr
|
||||
|
||||
if( a )
|
||||
{
|
||||
size_t len = Q_strlen( a->value );
|
||||
Cbuf_InsertTextToBuffer(
|
||||
isPrivileged ? &cmd_text : &filteredcmd_text,
|
||||
a->value );
|
||||
a->value, len, len );
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -612,7 +612,7 @@ COM_Nibble
|
||||
Returns the 4 bit nibble for a hex character
|
||||
==================
|
||||
*/
|
||||
static byte COM_Nibble( char c )
|
||||
byte COM_Nibble( char c )
|
||||
{
|
||||
if(( c >= '0' ) && ( c <= '9' ))
|
||||
{
|
||||
|
||||
@@ -362,12 +362,13 @@ typedef struct host_parm_s
|
||||
|
||||
extern host_parm_t host;
|
||||
|
||||
#define CMD_SERVERDLL BIT( 0 ) // added by server.dll
|
||||
#define CMD_CLIENTDLL BIT( 1 ) // added by client.dll
|
||||
#define CMD_GAMEUIDLL BIT( 2 ) // added by GameUI.dll
|
||||
#define CMD_PRIVILEGED BIT( 3 ) // only available in privileged mode
|
||||
#define CMD_FILTERABLE BIT( 4 ) // filtered in unprivileged mode if cl_filterstuffcmd is 1
|
||||
#define CMD_REFDLL BIT( 5 ) // added by ref.dll
|
||||
#define CMD_SERVERDLL BIT( 0 ) // added by server.dll
|
||||
#define CMD_CLIENTDLL BIT( 1 ) // added by client.dll
|
||||
#define CMD_GAMEUIDLL BIT( 2 ) // added by GameUI.dll
|
||||
#define CMD_PRIVILEGED BIT( 3 ) // only available in privileged mode
|
||||
#define CMD_FILTERABLE BIT( 4 ) // filtered in unprivileged mode if cl_filterstuffcmd is 1
|
||||
#define CMD_REFDLL BIT( 5 ) // added by ref.dll
|
||||
#define CMD_OVERRIDABLE BIT( 6 ) // can be removed by DLLs if name matches
|
||||
|
||||
typedef void (*xcommand_t)( void );
|
||||
|
||||
@@ -418,11 +419,13 @@ byte *FS_LoadDirectFile( const char *path, fs_offset_t *filesizeptr )
|
||||
//
|
||||
// cmd.c
|
||||
//
|
||||
typedef struct cmd_s cmd_t;
|
||||
void Cbuf_Clear( void );
|
||||
void Cbuf_AddText( const char *text );
|
||||
void Cbuf_AddTextf( const char *text, ... ) FORMAT_CHECK( 1 );
|
||||
void Cbuf_AddFilteredText( const char *text );
|
||||
void Cbuf_InsertText( const char *text );
|
||||
void Cbuf_InsertTextLen( const char *text, size_t len, size_t requested_len );
|
||||
void Cbuf_ExecStuffCmds( void );
|
||||
void Cbuf_Execute (void);
|
||||
qboolean Cmd_CurrentCommandIsPrivileged( void );
|
||||
@@ -431,14 +434,25 @@ const char *Cmd_Args( void ) RETURNS_NONNULL;
|
||||
const char *Cmd_Argv( int arg ) RETURNS_NONNULL;
|
||||
void Cmd_Init( void );
|
||||
void Cmd_Unlink( int group );
|
||||
void Cmd_AddCommand( const char *cmd_name, xcommand_t function, const char *cmd_desc );
|
||||
void Cmd_AddRestrictedCommand( const char *cmd_name, xcommand_t function, const char *cmd_desc );
|
||||
void Cmd_AddServerCommand( const char *cmd_name, xcommand_t function );
|
||||
int Cmd_AddClientCommand( const char *cmd_name, xcommand_t function );
|
||||
int Cmd_AddGameUICommand( const char *cmd_name, xcommand_t function );
|
||||
int Cmd_AddRefCommand( const char *cmd_name, xcommand_t function, const char *description );
|
||||
int Cmd_AddCommandEx( const char *cmd_name, xcommand_t function, const char *cmd_desc, int flags, const char *funcname );
|
||||
|
||||
static inline int Cmd_AddCommand( const char *cmd_name, xcommand_t function, const char *cmd_desc )
|
||||
{
|
||||
return Cmd_AddCommandEx( cmd_name, function, cmd_desc, 0, __func__ );
|
||||
}
|
||||
|
||||
static inline int Cmd_AddRestrictedCommand( const char *cmd_name, xcommand_t function, const char *cmd_desc )
|
||||
{
|
||||
return Cmd_AddCommandEx( cmd_name, function, cmd_desc, CMD_PRIVILEGED, __func__ );
|
||||
}
|
||||
|
||||
static inline int Cmd_AddCommandWithFlags( const char *cmd_name, xcommand_t function, const char *cmd_desc, int flags )
|
||||
{
|
||||
return Cmd_AddCommandEx( cmd_name, function, cmd_desc, flags, __func__ );
|
||||
}
|
||||
|
||||
void Cmd_RemoveCommand( const char *cmd_name );
|
||||
qboolean Cmd_Exists( const char *cmd_name );
|
||||
cmd_t *Cmd_Exists( const char *cmd_name );
|
||||
void Cmd_LookupCmds( void *buffer, void *ptr, setpair_t callback );
|
||||
int Cmd_ListMaps( search_t *t , char *lastmapname, size_t len );
|
||||
void Cmd_TokenizeString( const char *text );
|
||||
@@ -534,7 +548,7 @@ qboolean Sound_SupportedFileFormat( const char *fileext );
|
||||
typedef void( *pfnChangeGame )( const char *progname );
|
||||
|
||||
qboolean Host_IsQuakeCompatible( void );
|
||||
void EXPORT Host_Shutdown( void );
|
||||
void Host_ShutdownWithReason( const char *reason );
|
||||
int EXPORT Host_Main( int argc, char **argv, const char *progname, int bChangeGame, pfnChangeGame func );
|
||||
void Host_EndGame( qboolean abort, const char *message, ... ) FORMAT_CHECK( 2 );
|
||||
void Host_AbortCurrentFrame( void ) NORETURN;
|
||||
@@ -542,7 +556,6 @@ void Host_WriteServerConfig( const char *name );
|
||||
void Host_WriteOpenGLConfig( void );
|
||||
void Host_WriteVideoConfig( void );
|
||||
void Host_WriteConfig( void );
|
||||
void Host_ShutdownServer( void );
|
||||
void Host_Error( const char *error, ... ) FORMAT_CHECK( 1 );
|
||||
void Host_ValidateEngineFeatures( uint32_t mask, uint32_t features );
|
||||
void Host_Frame( double time );
|
||||
@@ -586,6 +599,7 @@ qboolean SV_Active( void );
|
||||
*/
|
||||
char *COM_MemFgets( byte *pMemFile, int fileSize, int *filePos, char *pBuffer, int bufferSize );
|
||||
void COM_HexConvert( const char *pszInput, int nInputLength, byte *pOutput );
|
||||
byte COM_Nibble( char c );
|
||||
int COM_SaveFile( const char *filename, const void *data, int len );
|
||||
byte *COM_LoadFileForMe( const char *filename, int *pLength ) MALLOC_LIKE( free, 1 );
|
||||
qboolean COM_IsSafeFileToDownload( const char *filename );
|
||||
|
||||
@@ -1040,7 +1040,6 @@ static const autocomplete_list_t cmd_list[] =
|
||||
{ "map", 1, Cmd_GetMapList },
|
||||
{ "cd", 1, Cmd_GetCDList },
|
||||
{ "mp3", 1, Cmd_GetCDList },
|
||||
{ NULL }, // termiantor
|
||||
};
|
||||
|
||||
/*
|
||||
@@ -1073,12 +1072,12 @@ for various cmds
|
||||
*/
|
||||
static qboolean Cmd_AutocompleteName( const char *source, int arg, char *buffer, size_t bufsize )
|
||||
{
|
||||
const autocomplete_list_t *list;
|
||||
int i;
|
||||
|
||||
for( list = cmd_list; list->name; list++ )
|
||||
for( i = 0; i < ARRAYSIZE( cmd_list ); i++ )
|
||||
{
|
||||
if( list->arg == arg && Cmd_CheckName( list->name ))
|
||||
return list->func( source, buffer, bufsize );
|
||||
if( cmd_list[i].arg == arg && Cmd_CheckName( cmd_list[i].name ))
|
||||
return cmd_list[i].func( source, buffer, bufsize );
|
||||
}
|
||||
|
||||
return false;
|
||||
|
||||
@@ -1,558 +0,0 @@
|
||||
/*
|
||||
crashhandler.c - advanced crashhandler
|
||||
Copyright (C) 2016 Mittorn
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
*/
|
||||
#ifndef _GNU_SOURCE
|
||||
#define _GNU_SOURCE
|
||||
#endif
|
||||
|
||||
#include "common.h"
|
||||
|
||||
/*
|
||||
================
|
||||
Sys_Crash
|
||||
|
||||
Crash handler, called from system
|
||||
================
|
||||
*/
|
||||
#if XASH_WIN32
|
||||
|
||||
#if DBGHELP
|
||||
|
||||
#pragma comment( lib, "dbghelp" )
|
||||
|
||||
#include <winnt.h>
|
||||
#include <dbghelp.h>
|
||||
#include <psapi.h>
|
||||
#include <time.h>
|
||||
|
||||
#ifndef XASH_SDL
|
||||
typedef ULONG_PTR DWORD_PTR, *PDWORD_PTR;
|
||||
#endif
|
||||
|
||||
static int Sys_ModuleName( HANDLE process, char *name, void *address, int len )
|
||||
{
|
||||
DWORD_PTR baseAddress = 0;
|
||||
static HMODULE *moduleArray;
|
||||
static unsigned int moduleCount;
|
||||
LPBYTE moduleArrayBytes;
|
||||
DWORD bytesRequired;
|
||||
int i;
|
||||
|
||||
if( len < 3 )
|
||||
return 0;
|
||||
|
||||
if( !moduleArray && EnumProcessModules( process, NULL, 0, &bytesRequired ) )
|
||||
{
|
||||
if ( bytesRequired )
|
||||
{
|
||||
moduleArrayBytes = (LPBYTE)LocalAlloc( LPTR, bytesRequired );
|
||||
|
||||
if( moduleArrayBytes && EnumProcessModules( process, (HMODULE *)moduleArrayBytes, bytesRequired, &bytesRequired ) )
|
||||
{
|
||||
moduleCount = bytesRequired / sizeof( HMODULE );
|
||||
moduleArray = (HMODULE *)moduleArrayBytes;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for( i = 0; i < moduleCount; i++ )
|
||||
{
|
||||
MODULEINFO info;
|
||||
GetModuleInformation( process, moduleArray[i], &info, sizeof(MODULEINFO) );
|
||||
|
||||
if( ( address > info.lpBaseOfDll ) &&
|
||||
( (DWORD64)address < (DWORD64)info.lpBaseOfDll + (DWORD64)info.SizeOfImage ) )
|
||||
return GetModuleBaseName( process, moduleArray[i], name, len );
|
||||
}
|
||||
return Q_snprintf( name, len, "???" );
|
||||
}
|
||||
|
||||
static void Sys_StackTrace( PEXCEPTION_POINTERS pInfo )
|
||||
{
|
||||
char message[8192]; // match *nix Sys_Crash
|
||||
int len = 0;
|
||||
size_t i;
|
||||
HANDLE process = GetCurrentProcess();
|
||||
HANDLE thread = GetCurrentThread();
|
||||
IMAGEHLP_LINE64 line;
|
||||
DWORD dline = 0;
|
||||
DWORD options;
|
||||
CONTEXT context;
|
||||
STACKFRAME64 stackframe;
|
||||
DWORD image;
|
||||
|
||||
context = *pInfo->ContextRecord;
|
||||
|
||||
options = SymGetOptions();
|
||||
options |= SYMOPT_DEBUG;
|
||||
options |= SYMOPT_LOAD_LINES;
|
||||
SymSetOptions( options );
|
||||
|
||||
SymInitialize( process, NULL, TRUE );
|
||||
|
||||
ZeroMemory( &stackframe, sizeof( STACKFRAME64 ));
|
||||
|
||||
#ifdef _M_IX86
|
||||
image = IMAGE_FILE_MACHINE_I386;
|
||||
stackframe.AddrPC.Offset = context.Eip;
|
||||
stackframe.AddrPC.Mode = AddrModeFlat;
|
||||
stackframe.AddrFrame.Offset = context.Ebp;
|
||||
stackframe.AddrFrame.Mode = AddrModeFlat;
|
||||
stackframe.AddrStack.Offset = context.Esp;
|
||||
stackframe.AddrStack.Mode = AddrModeFlat;
|
||||
#elif _M_X64
|
||||
image = IMAGE_FILE_MACHINE_AMD64;
|
||||
stackframe.AddrPC.Offset = context.Rip;
|
||||
stackframe.AddrPC.Mode = AddrModeFlat;
|
||||
stackframe.AddrFrame.Offset = context.Rsp;
|
||||
stackframe.AddrFrame.Mode = AddrModeFlat;
|
||||
stackframe.AddrStack.Offset = context.Rsp;
|
||||
stackframe.AddrStack.Mode = AddrModeFlat;
|
||||
#elif _M_IA64
|
||||
image = IMAGE_FILE_MACHINE_IA64;
|
||||
stackframe.AddrPC.Offset = context.StIIP;
|
||||
stackframe.AddrPC.Mode = AddrModeFlat;
|
||||
stackframe.AddrFrame.Offset = context.IntSp;
|
||||
stackframe.AddrFrame.Mode = AddrModeFlat;
|
||||
stackframe.AddrBStore.Offset = context.RsBSP;
|
||||
stackframe.AddrBStore.Mode = AddrModeFlat;
|
||||
stackframe.AddrStack.Offset = context.IntSp;
|
||||
stackframe.AddrStack.Mode = AddrModeFlat;
|
||||
#elif _M_ARM
|
||||
image = IMAGE_FILE_MACHINE_ARMNT;
|
||||
stackframe.AddrPC.Offset = context.Pc;
|
||||
stackframe.AddrPC.Mode = AddrModeFlat;
|
||||
stackframe.AddrFrame.Offset = context.R11;
|
||||
stackframe.AddrFrame.Mode = AddrModeFlat;
|
||||
stackframe.AddrStack.Offset = context.Sp;
|
||||
stackframe.AddrStack.Mode = AddrModeFlat;
|
||||
#elif _M_ARM64
|
||||
image = IMAGE_FILE_MACHINE_ARM64;
|
||||
stackframe.AddrPC.Offset = context.Pc;
|
||||
stackframe.AddrPC.Mode = AddrModeFlat;
|
||||
stackframe.AddrFrame.Offset = context.Fp;
|
||||
stackframe.AddrFrame.Mode = AddrModeFlat;
|
||||
stackframe.AddrStack.Offset = context.Sp;
|
||||
stackframe.AddrStack.Mode = AddrModeFlat;
|
||||
#elif
|
||||
#error
|
||||
#endif
|
||||
|
||||
len = Q_snprintf( message, sizeof( message ), "Ver: " XASH_ENGINE_NAME " " XASH_VERSION " (build %i-%s, %s-%s)\n",
|
||||
Q_buildnum(), Q_buildcommit(), Q_buildos(), Q_buildarch() );
|
||||
|
||||
len += Q_snprintf( message + len, 1024 - len, "Sys_Crash: address %p, code %p\n",
|
||||
pInfo->ExceptionRecord->ExceptionAddress, (void*)pInfo->ExceptionRecord->ExceptionCode );
|
||||
if( SymGetLineFromAddr64( process, (DWORD64)pInfo->ExceptionRecord->ExceptionAddress, &dline, &line ) )
|
||||
{
|
||||
len += Q_snprintf(message + len, 1024 - len, "Exception: %s:%d:%d\n",
|
||||
(char*)line.FileName, (int)line.LineNumber, (int)dline);
|
||||
}
|
||||
if( SymGetLineFromAddr64( process, stackframe.AddrPC.Offset, &dline, &line ) )
|
||||
{
|
||||
len += Q_snprintf(message + len, 1024 - len,"PC: %s:%d:%d\n",
|
||||
(char*)line.FileName, (int)line.LineNumber, (int)dline);
|
||||
}
|
||||
if( SymGetLineFromAddr64( process, stackframe.AddrFrame.Offset, &dline, &line ) )
|
||||
{
|
||||
len += Q_snprintf(message + len, 1024 - len,"Frame: %s:%d:%d\n",
|
||||
(char*)line.FileName, (int)line.LineNumber, (int)dline);
|
||||
}
|
||||
for( i = 0; i < 25; i++ )
|
||||
{
|
||||
char buffer[sizeof(SYMBOL_INFO) + MAX_SYM_NAME * sizeof(TCHAR)];
|
||||
PSYMBOL_INFO symbol = (PSYMBOL_INFO)buffer;
|
||||
BOOL result = StackWalk64(
|
||||
image, process, thread,
|
||||
&stackframe, &context, NULL,
|
||||
SymFunctionTableAccess64, SymGetModuleBase64, NULL);
|
||||
DWORD64 displacement = 0;
|
||||
|
||||
if( !result )
|
||||
break;
|
||||
|
||||
symbol->SizeOfStruct = sizeof(SYMBOL_INFO);
|
||||
symbol->MaxNameLen = MAX_SYM_NAME;
|
||||
|
||||
len += Q_snprintf( message + len, 1024 - len, "% 2d %p",
|
||||
i, (void*)stackframe.AddrPC.Offset );
|
||||
if( SymFromAddr( process, stackframe.AddrPC.Offset, &displacement, symbol ) )
|
||||
{
|
||||
len += Q_snprintf( message + len, 1024 - len, " %s ", symbol->Name );
|
||||
}
|
||||
if( SymGetLineFromAddr64( process, stackframe.AddrPC.Offset, &dline, &line ) )
|
||||
{
|
||||
len += Q_snprintf(message + len, 1024 - len,"(%s:%d:%d) ",
|
||||
(char*)line.FileName, (int)line.LineNumber, (int)dline);
|
||||
}
|
||||
len += Q_snprintf( message + len, 1024 - len, "(");
|
||||
len += Sys_ModuleName( process, message + len, (void*)stackframe.AddrPC.Offset, 1024 - len );
|
||||
len += Q_snprintf( message + len, 1024 - len, ")\n");
|
||||
}
|
||||
|
||||
#if XASH_SDL == 2
|
||||
if( host.type != HOST_DEDICATED ) // let system to restart server automaticly
|
||||
SDL_ShowSimpleMessageBox( SDL_MESSAGEBOX_ERROR, "Sys_Crash", message, host.hWnd );
|
||||
#endif
|
||||
|
||||
Sys_PrintLog( message );
|
||||
|
||||
SymCleanup( process );
|
||||
}
|
||||
|
||||
static void Sys_GetProcessName( char *processName, size_t bufferSize )
|
||||
{
|
||||
char fullpath[MAX_PATH];
|
||||
|
||||
GetModuleBaseName( GetCurrentProcess(), NULL, fullpath, sizeof( fullpath ) - 1 );
|
||||
COM_FileBase( fullpath, processName, bufferSize );
|
||||
}
|
||||
|
||||
static void Sys_GetMinidumpFileName( const char *processName, char *mdmpFileName, size_t bufferSize )
|
||||
{
|
||||
time_t currentUtcTime = time( NULL );
|
||||
struct tm *currentLocalTime = localtime( ¤tUtcTime );
|
||||
|
||||
Q_snprintf( mdmpFileName, bufferSize, "%s_%s_crash_%d%.2d%.2d_%.2d%.2d%.2d.mdmp",
|
||||
processName,
|
||||
Q_buildcommit(),
|
||||
currentLocalTime->tm_year + 1900,
|
||||
currentLocalTime->tm_mon + 1,
|
||||
currentLocalTime->tm_mday,
|
||||
currentLocalTime->tm_hour,
|
||||
currentLocalTime->tm_min,
|
||||
currentLocalTime->tm_sec);
|
||||
}
|
||||
|
||||
static qboolean Sys_WriteMinidump(PEXCEPTION_POINTERS exceptionInfo, MINIDUMP_TYPE minidumpType)
|
||||
{
|
||||
HRESULT errorCode;
|
||||
string processName;
|
||||
string mdmpFileName;
|
||||
MINIDUMP_EXCEPTION_INFORMATION minidumpInfo;
|
||||
|
||||
Sys_GetProcessName( processName, sizeof( processName ));
|
||||
Sys_GetMinidumpFileName( processName, mdmpFileName, sizeof( mdmpFileName ));
|
||||
|
||||
SetLastError( NOERROR );
|
||||
HANDLE fileHandle = CreateFile(
|
||||
mdmpFileName, GENERIC_WRITE, FILE_SHARE_WRITE,
|
||||
NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);
|
||||
|
||||
errorCode = HRESULT_FROM_WIN32( GetLastError( ));
|
||||
if( !SUCCEEDED( errorCode )) {
|
||||
CloseHandle( fileHandle );
|
||||
return false;
|
||||
}
|
||||
|
||||
minidumpInfo.ThreadId = GetCurrentThreadId();
|
||||
minidumpInfo.ExceptionPointers = exceptionInfo;
|
||||
minidumpInfo.ClientPointers = FALSE;
|
||||
|
||||
qboolean status = MiniDumpWriteDump(
|
||||
GetCurrentProcess(), GetCurrentProcessId(), fileHandle,
|
||||
minidumpType, &minidumpInfo, NULL, NULL);
|
||||
|
||||
CloseHandle( fileHandle );
|
||||
return status;
|
||||
}
|
||||
|
||||
#endif /* DBGHELP */
|
||||
|
||||
LPTOP_LEVEL_EXCEPTION_FILTER oldFilter;
|
||||
static long _stdcall Sys_Crash( PEXCEPTION_POINTERS pInfo )
|
||||
{
|
||||
// save config
|
||||
if( host.status != HOST_CRASHED )
|
||||
{
|
||||
// check to avoid recursive call
|
||||
host.crashed = true;
|
||||
|
||||
#ifdef XASH_SDL
|
||||
SDL_SetWindowGrab( host.hWnd, SDL_FALSE );
|
||||
#endif // XASH_SDL
|
||||
|
||||
#if DBGHELP
|
||||
Sys_StackTrace( pInfo );
|
||||
#else
|
||||
Sys_Warn( "Sys_Crash: call %p at address %p", pInfo->ExceptionRecord->ExceptionAddress, pInfo->ExceptionRecord->ExceptionCode );
|
||||
#endif
|
||||
|
||||
if( host.type == HOST_NORMAL )
|
||||
CL_Crashed(); // tell client about crash
|
||||
else host.status = HOST_CRASHED;
|
||||
|
||||
#if DBGHELP
|
||||
if( Sys_CheckParm( "-minidumps" ))
|
||||
{
|
||||
int minidumpFlags = (
|
||||
MiniDumpWithDataSegs |
|
||||
MiniDumpWithCodeSegs |
|
||||
MiniDumpWithHandleData |
|
||||
MiniDumpWithFullMemory |
|
||||
MiniDumpWithFullMemoryInfo |
|
||||
MiniDumpWithIndirectlyReferencedMemory |
|
||||
MiniDumpWithThreadInfo |
|
||||
MiniDumpWithModuleHeaders);
|
||||
|
||||
if( !Sys_WriteMinidump( pInfo, (MINIDUMP_TYPE)minidumpFlags )) {
|
||||
// fallback method, create minidump with minimal info in it
|
||||
Sys_WriteMinidump( pInfo, MiniDumpWithDataSegs );
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
if( host_developer.value <= 0 )
|
||||
{
|
||||
// no reason to call debugger in release build - just exit
|
||||
Sys_Quit();
|
||||
return EXCEPTION_CONTINUE_EXECUTION;
|
||||
}
|
||||
|
||||
// all other states keep unchanged to let debugger find bug
|
||||
Sys_DestroyConsole();
|
||||
}
|
||||
|
||||
if( oldFilter )
|
||||
return oldFilter( pInfo );
|
||||
return EXCEPTION_CONTINUE_EXECUTION;
|
||||
}
|
||||
|
||||
void Sys_SetupCrashHandler( void )
|
||||
{
|
||||
SetErrorMode( SEM_FAILCRITICALERRORS ); // no abort/retry/fail errors
|
||||
oldFilter = SetUnhandledExceptionFilter( Sys_Crash );
|
||||
}
|
||||
|
||||
void Sys_RestoreCrashHandler( void )
|
||||
{
|
||||
// restore filter
|
||||
if( oldFilter ) SetUnhandledExceptionFilter( oldFilter );
|
||||
}
|
||||
|
||||
#elif XASH_FREEBSD || XASH_NETBSD || XASH_OPENBSD || XASH_ANDROID || XASH_LINUX
|
||||
// Posix signal handler
|
||||
#ifndef XASH_OPENBSD
|
||||
#include <ucontext.h>
|
||||
#endif
|
||||
#include <signal.h>
|
||||
#include <sys/mman.h>
|
||||
#include "library.h"
|
||||
|
||||
#define STACK_BACKTRACE_STR "Stack backtrace:\n"
|
||||
#define STACK_DUMP_STR "Stack dump:\n"
|
||||
|
||||
#define STACK_BACKTRACE_STR_LEN ( sizeof( STACK_BACKTRACE_STR ) - 1 )
|
||||
#define STACK_DUMP_STR_LEN ( sizeof( STACK_DUMP_STR ) - 1 )
|
||||
#define ALIGN( x, y ) (((uintptr_t) ( x ) + (( y ) - 1 )) & ~(( y ) - 1 ))
|
||||
|
||||
static struct sigaction oldFilter;
|
||||
|
||||
static int Sys_PrintFrame( char *buf, int len, int i, void *addr )
|
||||
{
|
||||
Dl_info dlinfo;
|
||||
if( len <= 0 )
|
||||
return 0; // overflow
|
||||
|
||||
if( dladdr( addr, &dlinfo ))
|
||||
{
|
||||
if( dlinfo.dli_sname )
|
||||
return Q_snprintf( buf, len, "%2d: %p <%s+%lu> (%s)\n", i, addr, dlinfo.dli_sname,
|
||||
(unsigned long)addr - (unsigned long)dlinfo.dli_saddr, dlinfo.dli_fname ); // print symbol, module and address
|
||||
|
||||
return Q_snprintf( buf, len, "%2d: %p (%s)\n", i, addr, dlinfo.dli_fname ); // print module and address
|
||||
}
|
||||
|
||||
return Q_snprintf( buf, len, "%2d: %p\n", i, addr ); // print only address
|
||||
}
|
||||
|
||||
static void Sys_Crash( int signal, siginfo_t *si, void *context)
|
||||
{
|
||||
void *pc = NULL, **bp = NULL, **sp = NULL; // this must be set for every OS!
|
||||
char message[8192];
|
||||
int len, logfd, i = 0;
|
||||
|
||||
#if XASH_OPENBSD
|
||||
struct sigcontext *ucontext = (struct sigcontext*)context;
|
||||
#else
|
||||
ucontext_t *ucontext = (ucontext_t*)context;
|
||||
#endif
|
||||
|
||||
#if XASH_AMD64
|
||||
#if XASH_FREEBSD
|
||||
pc = (void*)ucontext->uc_mcontext.mc_rip;
|
||||
bp = (void**)ucontext->uc_mcontext.mc_rbp;
|
||||
sp = (void**)ucontext->uc_mcontext.mc_rsp;
|
||||
#elif XASH_NETBSD
|
||||
pc = (void*)ucontext->uc_mcontext.__gregs[_REG_RIP];
|
||||
bp = (void**)ucontext->uc_mcontext.__gregs[_REG_RBP];
|
||||
sp = (void**)ucontext->uc_mcontext.__gregs[_REG_RSP];
|
||||
#elif XASH_OPENBSD
|
||||
pc = (void*)ucontext->sc_rip;
|
||||
bp = (void**)ucontext->sc_rbp;
|
||||
sp = (void**)ucontext->sc_rsp;
|
||||
#else
|
||||
pc = (void*)ucontext->uc_mcontext.gregs[REG_RIP];
|
||||
bp = (void**)ucontext->uc_mcontext.gregs[REG_RBP];
|
||||
sp = (void**)ucontext->uc_mcontext.gregs[REG_RSP];
|
||||
#endif
|
||||
#elif XASH_X86
|
||||
#if XASH_FREEBSD
|
||||
pc = (void*)ucontext->uc_mcontext.mc_eip;
|
||||
bp = (void**)ucontext->uc_mcontext.mc_ebp;
|
||||
sp = (void**)ucontext->uc_mcontext.mc_esp;
|
||||
#elif XASH_NETBSD
|
||||
pc = (void*)ucontext->uc_mcontext.__gregs[_REG_EIP];
|
||||
bp = (void**)ucontext->uc_mcontext.__gregs[_REG_EBP];
|
||||
sp = (void**)ucontext->uc_mcontext.__gregs[_REG_ESP];
|
||||
#elif XASH_OPENBSD
|
||||
pc = (void*)ucontext->sc_eip;
|
||||
bp = (void**)ucontext->sc_ebp;
|
||||
sp = (void**)ucontext->sc_esp;
|
||||
#else
|
||||
pc = (void*)ucontext->uc_mcontext.gregs[REG_EIP];
|
||||
bp = (void**)ucontext->uc_mcontext.gregs[REG_EBP];
|
||||
sp = (void**)ucontext->uc_mcontext.gregs[REG_ESP];
|
||||
#endif
|
||||
#elif XASH_ARM && XASH_64BIT
|
||||
pc = (void*)ucontext->uc_mcontext.pc;
|
||||
bp = (void*)ucontext->uc_mcontext.regs[29];
|
||||
sp = (void*)ucontext->uc_mcontext.sp;
|
||||
#elif XASH_ARM
|
||||
pc = (void*)ucontext->uc_mcontext.arm_pc;
|
||||
bp = (void*)ucontext->uc_mcontext.arm_fp;
|
||||
sp = (void*)ucontext->uc_mcontext.arm_sp;
|
||||
#endif
|
||||
|
||||
// safe actions first, stack and memory may be corrupted
|
||||
len = Q_snprintf( message, sizeof( message ), "Ver: " XASH_ENGINE_NAME " " XASH_VERSION " (build %i-%s, %s-%s)\n",
|
||||
Q_buildnum(), Q_buildcommit(), Q_buildos(), Q_buildarch() );
|
||||
|
||||
#if !XASH_FREEBSD && !XASH_NETBSD && !XASH_OPENBSD
|
||||
len += Q_snprintf( message + len, sizeof( message ) - len, "Crash: signal %d errno %d with code %d at %p %p\n", signal, si->si_errno, si->si_code, si->si_addr, si->si_ptr );
|
||||
#else
|
||||
len += Q_snprintf( message + len, sizeof( message ) - len, "Crash: signal %d errno %d with code %d at %p\n", signal, si->si_errno, si->si_code, si->si_addr );
|
||||
#endif
|
||||
|
||||
write( STDERR_FILENO, message, len );
|
||||
|
||||
// flush buffers before writing directly to descriptors
|
||||
fflush( stdout );
|
||||
fflush( stderr );
|
||||
|
||||
// now get log fd and write trace directly to log
|
||||
logfd = Sys_LogFileNo();
|
||||
write( logfd, message, len );
|
||||
|
||||
if( pc && bp && sp )
|
||||
{
|
||||
size_t pagesize = sysconf( _SC_PAGESIZE );
|
||||
|
||||
// try to print backtrace
|
||||
write( STDERR_FILENO, STACK_BACKTRACE_STR, STACK_BACKTRACE_STR_LEN );
|
||||
write( logfd, STACK_BACKTRACE_STR, STACK_BACKTRACE_STR_LEN );
|
||||
Q_strncpy( message + len, STACK_BACKTRACE_STR, sizeof( message ) - len );
|
||||
len += STACK_BACKTRACE_STR_LEN;
|
||||
|
||||
// false on success, true on failure
|
||||
#define try_allow_read(pointer, pagesize) \
|
||||
((mprotect( (char *)ALIGN( (pointer), (pagesize) ), (pagesize), PROT_READ | PROT_WRITE | PROT_EXEC ) == -1) && \
|
||||
( mprotect( (char *)ALIGN( (pointer), (pagesize) ), (pagesize), PROT_READ | PROT_EXEC ) == -1) && \
|
||||
( mprotect( (char *)ALIGN( (pointer), (pagesize) ), (pagesize), PROT_READ | PROT_WRITE ) == -1) && \
|
||||
( mprotect( (char *)ALIGN( (pointer), (pagesize) ), (pagesize), PROT_READ ) == -1))
|
||||
|
||||
do
|
||||
{
|
||||
int line = Sys_PrintFrame( message + len, sizeof( message ) - len, ++i, pc);
|
||||
write( STDERR_FILENO, message + len, line );
|
||||
write( logfd, message + len, line );
|
||||
len += line;
|
||||
//if( !dladdr(bp,0) ) break; // only when bp is in module
|
||||
if( try_allow_read( bp, pagesize ) )
|
||||
break;
|
||||
if( try_allow_read( bp[0], pagesize ) )
|
||||
break;
|
||||
pc = bp[1];
|
||||
bp = (void**)bp[0];
|
||||
}
|
||||
while( bp && i < 128 );
|
||||
|
||||
// try to print stack
|
||||
write( STDERR_FILENO, STACK_DUMP_STR, STACK_DUMP_STR_LEN );
|
||||
write( logfd, STACK_DUMP_STR, STACK_DUMP_STR_LEN );
|
||||
Q_strncpy( message + len, STACK_DUMP_STR, sizeof( message ) - len );
|
||||
len += STACK_DUMP_STR_LEN;
|
||||
|
||||
if( !try_allow_read( sp, pagesize ) )
|
||||
{
|
||||
for( i = 0; i < 32; i++ )
|
||||
{
|
||||
int line = Sys_PrintFrame( message + len, sizeof( message ) - len, i, sp[i] );
|
||||
write( STDERR_FILENO, message + len, line );
|
||||
write( logfd, message + len, line );
|
||||
len += line;
|
||||
}
|
||||
}
|
||||
|
||||
#undef try_allow_read
|
||||
}
|
||||
|
||||
// put MessageBox as Sys_Error
|
||||
Msg( "%s\n", message );
|
||||
#ifdef XASH_SDL
|
||||
SDL_SetWindowGrab( host.hWnd, SDL_FALSE );
|
||||
#endif
|
||||
host.crashed = true;
|
||||
Platform_MessageBox( "Xash Error", message, false );
|
||||
|
||||
// log saved, now we can try to save configs and close log correctly, it may crash
|
||||
if( host.type == HOST_NORMAL )
|
||||
CL_Crashed();
|
||||
host.status = HOST_CRASHED;
|
||||
|
||||
|
||||
Sys_Quit();
|
||||
}
|
||||
|
||||
void Sys_SetupCrashHandler( void )
|
||||
{
|
||||
struct sigaction act = { 0 };
|
||||
act.sa_sigaction = Sys_Crash;
|
||||
act.sa_flags = SA_SIGINFO | SA_ONSTACK;
|
||||
sigaction( SIGSEGV, &act, &oldFilter );
|
||||
sigaction( SIGABRT, &act, &oldFilter );
|
||||
sigaction( SIGBUS, &act, &oldFilter );
|
||||
sigaction( SIGILL, &act, &oldFilter );
|
||||
}
|
||||
|
||||
void Sys_RestoreCrashHandler( void )
|
||||
{
|
||||
sigaction( SIGSEGV, &oldFilter, NULL );
|
||||
sigaction( SIGABRT, &oldFilter, NULL );
|
||||
sigaction( SIGBUS, &oldFilter, NULL );
|
||||
sigaction( SIGILL, &oldFilter, NULL );
|
||||
}
|
||||
|
||||
#else
|
||||
|
||||
void Sys_SetupCrashHandler( void )
|
||||
{
|
||||
// stub
|
||||
}
|
||||
|
||||
void Sys_RestoreCrashHandler( void )
|
||||
{
|
||||
// stub
|
||||
}
|
||||
#endif
|
||||
@@ -84,6 +84,11 @@ static void FS_Path_f_( void )
|
||||
FS_Path_f();
|
||||
}
|
||||
|
||||
static void FS_MakeGameInfo_f( void )
|
||||
{
|
||||
g_fsapi.MakeGameInfo();
|
||||
}
|
||||
|
||||
static const fs_interface_t fs_memfuncs =
|
||||
{
|
||||
Con_Printf,
|
||||
@@ -272,6 +277,7 @@ void FS_Init( const char *basedir )
|
||||
Cmd_AddRestrictedCommand( "fs_rescan", FS_Rescan_f, "rescan filesystem search pathes" );
|
||||
Cmd_AddRestrictedCommand( "fs_path", FS_Path_f_, "show filesystem search pathes" );
|
||||
Cmd_AddRestrictedCommand( "fs_clearpaths", FS_ClearPaths_f, "clear filesystem search pathes" );
|
||||
Cmd_AddRestrictedCommand( "fs_make_gameinfo", FS_MakeGameInfo_f, "create gameinfo.txt for current running game" );
|
||||
|
||||
if( !Sys_GetParmFromCmdLine( "-dll", host.gamedll ))
|
||||
host.gamedll[0] = 0;
|
||||
|
||||
@@ -209,7 +209,7 @@ static void Sys_PrintUsage( const char *exename )
|
||||
fprintf( stderr, usage_str, exename );
|
||||
#endif
|
||||
|
||||
Sys_Quit();
|
||||
Sys_Quit( NULL );
|
||||
}
|
||||
|
||||
static void Sys_PrintBugcompUsage( const char *exename )
|
||||
@@ -234,12 +234,7 @@ static void Sys_PrintBugcompUsage( const char *exename )
|
||||
fprintf( stderr, usage_str, exename );
|
||||
#endif
|
||||
|
||||
Sys_Quit();
|
||||
}
|
||||
|
||||
void Host_ShutdownServer( void )
|
||||
{
|
||||
SV_Shutdown( "Server was killed\n" );
|
||||
Sys_Quit( NULL );
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -423,8 +418,7 @@ static void Host_Exec_f( void )
|
||||
{
|
||||
string cfgpath;
|
||||
byte *f;
|
||||
char *txt;
|
||||
fs_offset_t len;
|
||||
fs_offset_t len;
|
||||
|
||||
if( Cmd_Argc() != 2 )
|
||||
{
|
||||
@@ -479,20 +473,29 @@ static void Host_Exec_f( void )
|
||||
return;
|
||||
}
|
||||
|
||||
// len is fs_offset_t, which can be larger than size_t
|
||||
if( len >= SIZE_MAX )
|
||||
{
|
||||
Con_Reportf( "%s: %s is too long\n", __func__, Cmd_Argv( 1 ));
|
||||
return;
|
||||
}
|
||||
|
||||
if( !Q_stricmp( "config.cfg", cfgpath ))
|
||||
host.config_executed = true;
|
||||
|
||||
// adds \n\0 at end of the file
|
||||
txt = Z_Calloc( len + 2 );
|
||||
memcpy( txt, f, len );
|
||||
txt[len] = '\n';
|
||||
txt[len + 1] = '\0';
|
||||
Mem_Free( f );
|
||||
|
||||
if( !host.apply_game_config )
|
||||
Con_Printf( "execing %s\n", Cmd_Argv( 1 ));
|
||||
Cbuf_InsertText( txt );
|
||||
Mem_Free( txt );
|
||||
|
||||
// adds \n at end of the file
|
||||
// FS_LoadFile always null terminates
|
||||
if( f[len - 1] != '\n' )
|
||||
{
|
||||
Cbuf_InsertTextLen( f, len, len + 1 );
|
||||
Cbuf_InsertTextLen( "\n", 1, 1 );
|
||||
}
|
||||
else Cbuf_InsertTextLen( f, len, len );
|
||||
|
||||
Mem_Free( f );
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -595,7 +598,7 @@ static void Host_GetCommands( void )
|
||||
{
|
||||
char *cmd;
|
||||
|
||||
while( ( cmd = Sys_Input() ) )
|
||||
while( ( cmd = Platform_Input() ) )
|
||||
{
|
||||
Cbuf_AddText( cmd );
|
||||
Cbuf_Execute();
|
||||
@@ -814,11 +817,11 @@ void GAME_EXPORT Host_Error( const char *error, ... )
|
||||
}
|
||||
else
|
||||
{
|
||||
Con_Printf( "%s: %s", __func__, hosterror1 );
|
||||
if( host.allow_console )
|
||||
{
|
||||
UI_SetActiveMenu( false );
|
||||
Key_SetKeyDest( key_console );
|
||||
Con_Printf( "%s: %s", __func__, hosterror1 );
|
||||
}
|
||||
else Platform_MessageBox( "Host Error", hosterror1, true );
|
||||
}
|
||||
@@ -841,7 +844,7 @@ void GAME_EXPORT Host_Error( const char *error, ... )
|
||||
COM_InitHostState();
|
||||
Cbuf_Clear();
|
||||
|
||||
Host_ShutdownServer();
|
||||
SV_Shutdown( "Server was killed due to an error\n" );
|
||||
CL_Drop(); // drop clients
|
||||
|
||||
// recreate world if needs
|
||||
@@ -920,7 +923,7 @@ static void Host_RunTests( int stage )
|
||||
#endif
|
||||
Msg( "Done! %d passed, %d failed\n", tests_stats.passed, tests_stats.failed );
|
||||
error_on_exit = tests_stats.failed > 0 ? EXIT_FAILURE : EXIT_SUCCESS;
|
||||
Sys_Quit();
|
||||
Sys_Quit( NULL );
|
||||
}
|
||||
}
|
||||
#endif
|
||||
@@ -1167,6 +1170,11 @@ static void Host_FreeCommon( void )
|
||||
FS_Shutdown();
|
||||
}
|
||||
|
||||
static void Sys_Quit_f( void )
|
||||
{
|
||||
Sys_Quit( "command" );
|
||||
}
|
||||
|
||||
/*
|
||||
=================
|
||||
Host_Main
|
||||
@@ -1241,8 +1249,8 @@ int EXPORT Host_Main( int argc, char **argv, const char *progname, int bChangeGa
|
||||
// disable texture replacements for dedicated
|
||||
Cvar_FullSet( "host_allow_materials", "0", FCVAR_READ_ONLY );
|
||||
|
||||
Cmd_AddRestrictedCommand( "quit", Sys_Quit, "quit the game" );
|
||||
Cmd_AddRestrictedCommand( "exit", Sys_Quit, "quit the game" );
|
||||
Cmd_AddRestrictedCommand( "quit", Sys_Quit_f, "quit the game" );
|
||||
Cmd_AddRestrictedCommand( "exit", Sys_Quit_f, "quit the game" );
|
||||
}
|
||||
else Cmd_AddRestrictedCommand( "minimize", Host_Minimize_f, "minimize main window to tray" );
|
||||
|
||||
@@ -1325,20 +1333,34 @@ int EXPORT Host_Main( int argc, char **argv, const char *progname, int bChangeGa
|
||||
return 0;
|
||||
}
|
||||
|
||||
void EXPORT Host_Shutdown( void );
|
||||
void EXPORT Host_Shutdown( void )
|
||||
{
|
||||
Host_ShutdownWithReason( "launcher shutdown" );
|
||||
}
|
||||
|
||||
/*
|
||||
=================
|
||||
Host_Shutdown
|
||||
=================
|
||||
*/
|
||||
void EXPORT Host_Shutdown( void )
|
||||
void Host_ShutdownWithReason( const char *reason )
|
||||
{
|
||||
qboolean error = host.status == HOST_ERR_FATAL;
|
||||
|
||||
if( host.shutdown_issued ) return;
|
||||
if( host.shutdown_issued )
|
||||
return;
|
||||
|
||||
host.shutdown_issued = true;
|
||||
|
||||
if( host.status != HOST_ERR_FATAL ) host.status = HOST_SHUTDOWN; // prepare host to normal shutdown
|
||||
if( !host.change_game ) Q_strncpy( host.finalmsg, "Server shutdown", sizeof( host.finalmsg ));
|
||||
if( reason != NULL )
|
||||
Con_Printf( S_NOTE "Issuing host shutdown due to reason \"%s\"\n", reason );
|
||||
|
||||
if( host.status != HOST_ERR_FATAL )
|
||||
host.status = HOST_SHUTDOWN; // prepare host to normal shutdown
|
||||
|
||||
if( !host.change_game )
|
||||
Q_strncpy( host.finalmsg, "Server shutdown", sizeof( host.finalmsg ));
|
||||
|
||||
#if !XASH_DEDICATED
|
||||
if( host.type == HOST_NORMAL && !error )
|
||||
|
||||
@@ -15,6 +15,7 @@ GNU General Public License for more details.
|
||||
|
||||
#include <math.h>
|
||||
#include "imagelib.h"
|
||||
#include "eiface.h" // ARRAYSIZE
|
||||
|
||||
// global image variables
|
||||
imglib_t image;
|
||||
@@ -67,7 +68,6 @@ static const cubepack_t load_cubemap[] =
|
||||
{ "3Ds Sky1", skybox_qv1 },
|
||||
{ "3Ds Sky2", skybox_qv2 },
|
||||
{ "3Ds Cube", cubemap_v1 },
|
||||
{ NULL, NULL },
|
||||
};
|
||||
|
||||
// soul of ImageLib - table of image format constants
|
||||
@@ -297,9 +297,8 @@ rgbdata_t *FS_LoadImage( const char *filename, const byte *buffer, size_t size )
|
||||
{
|
||||
const char *ext = COM_FileExtension( filename );
|
||||
string loadname;
|
||||
int i;
|
||||
int i, j;
|
||||
const loadpixformat_t *extfmt;
|
||||
const cubepack_t *cmap;
|
||||
|
||||
Q_strncpy( loadname, filename, sizeof( loadname ));
|
||||
Image_Reset(); // clear old image
|
||||
@@ -317,8 +316,10 @@ rgbdata_t *FS_LoadImage( const char *filename, const byte *buffer, size_t size )
|
||||
return ImagePack();
|
||||
|
||||
// check all cubemap sides with package suffix
|
||||
for( cmap = load_cubemap; cmap && cmap->type; cmap++ )
|
||||
for( j = 0; j < ARRAYSIZE( load_cubemap ); j++ )
|
||||
{
|
||||
const cubepack_t *cmap = &load_cubemap[j];
|
||||
|
||||
for( i = 0; i < 6; i++ )
|
||||
{
|
||||
if( Image_ProbeLoad( extfmt, loadname, cmap->type[i].suf, cmap->type[i].hint ))
|
||||
|
||||
@@ -37,11 +37,7 @@ static char **szArgv;
|
||||
|
||||
static void Sys_ChangeGame( const char *progname )
|
||||
{
|
||||
// a1ba: may never be called within engine
|
||||
// if platform supports execv() function
|
||||
Q_strncpy( szGameDir, progname, sizeof( szGameDir ));
|
||||
Host_Shutdown( );
|
||||
exit( Host_Main( szArgc, szArgv, szGameDir, 1, &Sys_ChangeGame ) );
|
||||
// stub
|
||||
}
|
||||
|
||||
static int Sys_Start( void )
|
||||
|
||||
@@ -385,10 +385,7 @@ void NET_SaveMasters( void )
|
||||
master_t *m;
|
||||
|
||||
if( !ml.modified )
|
||||
{
|
||||
Con_Reportf( "Master server list not changed\n" );
|
||||
return;
|
||||
}
|
||||
|
||||
f = FS_Open( "xashcomm.lst", "w", true );
|
||||
|
||||
|
||||
@@ -101,20 +101,14 @@ const char *const svc_legacy_strings[svc_lastmsg+1] =
|
||||
const char *const svc_goldsrc_strings[svc_lastmsg+1] =
|
||||
{
|
||||
[svc_goldsrc_version] = "svc_goldsrc_version",
|
||||
[svc_goldsrc_serverinfo] = "svc_goldsrc_serverinfo",
|
||||
[svc_goldsrc_deltadescription] = "svc_goldsrc_deltadescription",
|
||||
[svc_goldsrc_stopsound] = "svc_goldsrc_stopsound",
|
||||
[svc_goldsrc_damage] = "svc_goldsrc_damage",
|
||||
[svc_goldsrc_killedmonster] = "svc_goldsrc_killedmonster",
|
||||
[svc_goldsrc_foundsecret] = "svc_goldsrc_foundsecret",
|
||||
[svc_goldsrc_spawnstaticsound] = "svc_goldsrc_spawnstaticsound",
|
||||
[svc_goldsrc_decalname] = "svc_goldsrc_decalname",
|
||||
[svc_goldsrc_newusermsg] = "svc_goldsrc_newusermsg",
|
||||
[svc_goldsrc_newmovevars] = "svc_goldsrc_newmovevars",
|
||||
[svc_goldsrc_sendextrainfo] = "svc_goldsrc_sendextrainfo",
|
||||
[svc_goldsrc_timescale] = "svc_goldsrc_timescale",
|
||||
[svc_goldsrc_sendcvarvalue] = "svc_goldsrc_sendcvarvalue",
|
||||
[svc_goldsrc_sendcvarvalue2] = "svc_goldsrc_sendcvarvalue2",
|
||||
};
|
||||
|
||||
const char *const svc_quake_strings[svc_lastmsg+1] =
|
||||
|
||||
@@ -39,8 +39,6 @@ GNU General Public License for more details.
|
||||
#define DT_SIGNED BIT( 8 ) // sign modificator
|
||||
#define DT_SIGNED_GS BIT( 31 ) // GoldSrc-specific sign modificator
|
||||
|
||||
#define NUM_FIELDS( x ) ((sizeof( x ) / sizeof( x[0] )) - 1)
|
||||
|
||||
// helper macroses
|
||||
#define ENTS_DEF( x ) #x, offsetof( entity_state_t, x ), sizeof( ((entity_state_t *)0)->x )
|
||||
#define UCMD_DEF( x ) #x, offsetof( usercmd_t, x ), sizeof( ((usercmd_t *)0)->x )
|
||||
@@ -71,7 +69,6 @@ static const delta_field_t cmd_fields[] =
|
||||
{ UCMD_DEF( impact_position[0] ) },
|
||||
{ UCMD_DEF( impact_position[1] ) },
|
||||
{ UCMD_DEF( impact_position[2] ) },
|
||||
{ NULL },
|
||||
};
|
||||
|
||||
static const delta_field_t pm_fields[] =
|
||||
@@ -107,7 +104,6 @@ static const delta_field_t pm_fields[] =
|
||||
{ PHYS_DEF( skydir_y ) },
|
||||
{ PHYS_DEF( skydir_z ) },
|
||||
{ PHYS_DEF( skyangle ) },
|
||||
{ NULL },
|
||||
};
|
||||
|
||||
static const delta_field_t ev_fields[] =
|
||||
@@ -130,7 +126,6 @@ static const delta_field_t ev_fields[] =
|
||||
{ EVNT_DEF( iparam2 ) },
|
||||
{ EVNT_DEF( bparam1 ) },
|
||||
{ EVNT_DEF( bparam2 ) },
|
||||
{ NULL },
|
||||
};
|
||||
|
||||
static const delta_field_t wd_fields[] =
|
||||
@@ -157,7 +152,6 @@ static const delta_field_t wd_fields[] =
|
||||
{ WPDT_DEF( fuser2 ) },
|
||||
{ WPDT_DEF( fuser3 ) },
|
||||
{ WPDT_DEF( fuser4 ) },
|
||||
{ NULL },
|
||||
};
|
||||
|
||||
static const delta_field_t cd_fields[] =
|
||||
@@ -218,7 +212,6 @@ static const delta_field_t cd_fields[] =
|
||||
{ CLDT_DEF( vuser4[0] ) },
|
||||
{ CLDT_DEF( vuser4[1] ) },
|
||||
{ CLDT_DEF( vuser4[2] ) },
|
||||
{ NULL },
|
||||
};
|
||||
|
||||
static const delta_field_t ent_fields[] =
|
||||
@@ -314,7 +307,6 @@ static const delta_field_t ent_fields[] =
|
||||
{ ENTS_DEF( vuser4[0] ) },
|
||||
{ ENTS_DEF( vuser4[1] ) },
|
||||
{ ENTS_DEF( vuser4[2] ) },
|
||||
{ NULL },
|
||||
};
|
||||
|
||||
static const delta_field_t meta_fields[] =
|
||||
@@ -326,7 +318,6 @@ static const delta_field_t meta_fields[] =
|
||||
{ DESC_DEF( significant_bits ), },
|
||||
{ DESC_DEF( premultiply ), },
|
||||
{ DESC_DEF( postmultiply ), },
|
||||
{ NULL },
|
||||
};
|
||||
|
||||
#if XASH_ENGINE_TESTS
|
||||
@@ -362,24 +353,22 @@ static const delta_field_t test_fields[] =
|
||||
{ TEST_DEF( dt_short_unsigned ) },
|
||||
{ TEST_DEF( dt_byte_signed ) },
|
||||
{ TEST_DEF( dt_byte_unsigned ) },
|
||||
{ NULL },
|
||||
};
|
||||
#endif
|
||||
|
||||
static delta_info_t dt_info[] =
|
||||
{
|
||||
[DT_EVENT_T] = { "event_t", ev_fields, NUM_FIELDS( ev_fields ) },
|
||||
[DT_MOVEVARS_T] = { "movevars_t", pm_fields, NUM_FIELDS( pm_fields ) },
|
||||
[DT_USERCMD_T] = { "usercmd_t", cmd_fields, NUM_FIELDS( cmd_fields ) },
|
||||
[DT_CLIENTDATA_T] = { "clientdata_t", cd_fields, NUM_FIELDS( cd_fields ) },
|
||||
[DT_WEAPONDATA_T] = { "weapon_data_t", wd_fields, NUM_FIELDS( wd_fields ) },
|
||||
[DT_ENTITY_STATE_T] = { "entity_state_t", ent_fields, NUM_FIELDS( ent_fields ) },
|
||||
[DT_ENTITY_STATE_PLAYER_T] = { "entity_state_player_t", ent_fields, NUM_FIELDS( ent_fields ) },
|
||||
[DT_CUSTOM_ENTITY_STATE_T] = { "custom_entity_state_t", ent_fields, NUM_FIELDS( ent_fields ) },
|
||||
[DT_EVENT_T] = { "event_t", ev_fields, ARRAYSIZE( ev_fields ) },
|
||||
[DT_MOVEVARS_T] = { "movevars_t", pm_fields, ARRAYSIZE( pm_fields ) },
|
||||
[DT_USERCMD_T] = { "usercmd_t", cmd_fields, ARRAYSIZE( cmd_fields ) },
|
||||
[DT_CLIENTDATA_T] = { "clientdata_t", cd_fields, ARRAYSIZE( cd_fields ) },
|
||||
[DT_WEAPONDATA_T] = { "weapon_data_t", wd_fields, ARRAYSIZE( wd_fields ) },
|
||||
[DT_ENTITY_STATE_T] = { "entity_state_t", ent_fields, ARRAYSIZE( ent_fields ) },
|
||||
[DT_ENTITY_STATE_PLAYER_T] = { "entity_state_player_t", ent_fields, ARRAYSIZE( ent_fields ) },
|
||||
[DT_CUSTOM_ENTITY_STATE_T] = { "custom_entity_state_t", ent_fields, ARRAYSIZE( ent_fields ) },
|
||||
#if XASH_ENGINE_TESTS
|
||||
[DT_DELTA_TEST_STRUCT_T] = { "delta_test_struct_t", test_fields, NUM_FIELDS( test_fields ) },
|
||||
[DT_DELTA_TEST_STRUCT_T] = { "delta_test_struct_t", test_fields, ARRAYSIZE( test_fields ) },
|
||||
#endif
|
||||
[DT_STRUCT_COUNT] = { NULL },
|
||||
};
|
||||
|
||||
// meta description is special, it cannot be overriden
|
||||
@@ -387,9 +376,9 @@ static const delta_info_t dt_goldsrc_meta =
|
||||
{
|
||||
.pName = "goldsrc_delta_t",
|
||||
.pInfo = meta_fields,
|
||||
.maxFields = NUM_FIELDS( meta_fields ),
|
||||
.numFields = NUM_FIELDS( meta_fields ),
|
||||
.pFields = (delta_t[NUM_FIELDS( meta_fields )])
|
||||
.maxFields = ARRAYSIZE( meta_fields ),
|
||||
.numFields = ARRAYSIZE( meta_fields ),
|
||||
.pFields = (delta_t[ARRAYSIZE( meta_fields )])
|
||||
{
|
||||
{
|
||||
DESC_DEF( fieldType ),
|
||||
@@ -451,7 +440,7 @@ static delta_info_t *Delta_FindStruct( const char *name )
|
||||
if( !COM_CheckString( name ))
|
||||
return NULL;
|
||||
|
||||
for( i = 0; i < NUM_FIELDS( dt_info ); i++ )
|
||||
for( i = 0; i < ARRAYSIZE( dt_info ); i++ )
|
||||
{
|
||||
if( !Q_stricmp( dt_info[i].pName, name ))
|
||||
return &dt_info[i];
|
||||
@@ -465,7 +454,7 @@ static delta_info_t *Delta_FindStruct( const char *name )
|
||||
|
||||
static int Delta_NumTables( void )
|
||||
{
|
||||
return NUM_FIELDS( dt_info );
|
||||
return ARRAYSIZE( dt_info );
|
||||
}
|
||||
|
||||
static delta_info_t *Delta_FindStructByIndex( int index )
|
||||
@@ -480,7 +469,7 @@ static delta_info_t *Delta_FindStructByEncoder( const char *encoderName )
|
||||
if( !COM_CheckString( encoderName ) )
|
||||
return NULL;
|
||||
|
||||
for( i = 0; i < NUM_FIELDS( dt_info ); i++ )
|
||||
for( i = 0; i < ARRAYSIZE( dt_info ); i++ )
|
||||
{
|
||||
if( !Q_stricmp( dt_info[i].funcName, encoderName ))
|
||||
return &dt_info[i];
|
||||
@@ -495,7 +484,7 @@ static delta_info_t *Delta_FindStructByDelta( const delta_t *pFields )
|
||||
|
||||
if( !pFields ) return NULL;
|
||||
|
||||
for( i = 0; i < NUM_FIELDS( dt_info ); i++ )
|
||||
for( i = 0; i < ARRAYSIZE( dt_info ); i++ )
|
||||
{
|
||||
if( dt_info[i].pFields == pFields )
|
||||
return &dt_info[i];
|
||||
@@ -518,29 +507,32 @@ static void Delta_CustomEncode( delta_info_t *dt, const void *from, const void *
|
||||
dt->userCallback( dt->pFields, from, to );
|
||||
}
|
||||
|
||||
static delta_field_t *Delta_FindFieldInfo( const delta_field_t *pInfo, const char *fieldName )
|
||||
static const delta_field_t *Delta_FindFieldInfo( const delta_field_t *pInfo, const char *fieldName, int maxFields )
|
||||
{
|
||||
int i;
|
||||
|
||||
if( !fieldName || !*fieldName )
|
||||
return NULL;
|
||||
|
||||
for( ; pInfo->name; pInfo++ )
|
||||
for( i = 0; i < maxFields; i++ )
|
||||
{
|
||||
if( !Q_strcmp( pInfo->name, fieldName ))
|
||||
return (delta_field_t *)pInfo;
|
||||
if( !Q_strcmp( pInfo[i].name, fieldName ))
|
||||
return &pInfo[i];
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static int Delta_IndexForFieldInfo( const delta_field_t *pInfo, const char *fieldName )
|
||||
static int Delta_IndexForFieldInfo( const delta_field_t *pInfo, const char *fieldName, int maxFields )
|
||||
{
|
||||
int i;
|
||||
|
||||
if( !fieldName || !*fieldName )
|
||||
return -1;
|
||||
|
||||
for( i = 0; pInfo->name; i++, pInfo++ )
|
||||
for( i = 0; i < maxFields; i++ )
|
||||
{
|
||||
if( !Q_strcmp( pInfo->name, fieldName ))
|
||||
if( !Q_strcmp( pInfo[i].name, fieldName ))
|
||||
return i;
|
||||
}
|
||||
return -1;
|
||||
@@ -548,7 +540,7 @@ static int Delta_IndexForFieldInfo( const delta_field_t *pInfo, const char *fiel
|
||||
|
||||
static qboolean Delta_AddField( delta_info_t *dt, const char *pName, int flags, int bits, float mul, float post_mul )
|
||||
{
|
||||
delta_field_t *pFieldInfo;
|
||||
const delta_field_t *pFieldInfo;
|
||||
delta_t *pField;
|
||||
int i;
|
||||
|
||||
@@ -567,7 +559,7 @@ static qboolean Delta_AddField( delta_info_t *dt, const char *pName, int flags,
|
||||
}
|
||||
|
||||
// find field description
|
||||
pFieldInfo = Delta_FindFieldInfo( dt->pInfo, pName );
|
||||
pFieldInfo = Delta_FindFieldInfo( dt->pInfo, pName, dt->maxFields );
|
||||
if( !pFieldInfo )
|
||||
{
|
||||
Con_DPrintf( S_ERROR "%s: couldn't find description for %s->%s\n", __func__, dt->pName, pName );
|
||||
@@ -610,7 +602,7 @@ static void Delta_WriteTableField( sizebuf_t *msg, int tableIndex, const delta_t
|
||||
dt = Delta_FindStructByIndex( tableIndex );
|
||||
Assert( dt && dt->bInitialized );
|
||||
|
||||
nameIndex = Delta_IndexForFieldInfo( dt->pInfo, pField->name );
|
||||
nameIndex = Delta_IndexForFieldInfo( dt->pInfo, pField->name, dt->maxFields );
|
||||
Assert( nameIndex >= 0 && nameIndex < dt->maxFields );
|
||||
|
||||
MSG_BeginServerCmd( msg, svc_deltatable );
|
||||
@@ -681,10 +673,10 @@ void Delta_ParseTableField( sizebuf_t *msg )
|
||||
Delta_AddField( dt, pName, flags, bits, mul, post_mul );
|
||||
}
|
||||
|
||||
static qboolean Delta_ParseField( char **delta_script, const delta_field_t *pInfo, delta_t *pField, qboolean bPost )
|
||||
static qboolean Delta_ParseField( char **delta_script, const delta_info_t *dt, delta_t *pField, qboolean bPost )
|
||||
{
|
||||
const delta_field_t *pFieldInfo;
|
||||
string token;
|
||||
delta_field_t *pFieldInfo;
|
||||
char *oldpos;
|
||||
|
||||
*delta_script = COM_ParseFile( *delta_script, token, sizeof( token ));
|
||||
@@ -701,7 +693,7 @@ static qboolean Delta_ParseField( char **delta_script, const delta_field_t *pInf
|
||||
return false;
|
||||
}
|
||||
|
||||
pFieldInfo = Delta_FindFieldInfo( pInfo, token );
|
||||
pFieldInfo = Delta_FindFieldInfo( dt->pInfo, token, dt->maxFields );
|
||||
if( !pFieldInfo )
|
||||
{
|
||||
Con_DPrintf( S_ERROR "%s: unable to find field %s\n", __func__, token );
|
||||
@@ -825,13 +817,11 @@ static void Delta_ParseTable( char **delta_script, delta_info_t *dt, const char
|
||||
{
|
||||
string token;
|
||||
delta_t *pField;
|
||||
const delta_field_t *pInfo;
|
||||
|
||||
// allocate the delta-structures
|
||||
if( !dt->pFields ) dt->pFields = (delta_t *)Z_Calloc( dt->maxFields * sizeof( delta_t ));
|
||||
|
||||
pField = dt->pFields;
|
||||
pInfo = dt->pInfo;
|
||||
dt->numFields = 0;
|
||||
|
||||
// assume we have handled '{'
|
||||
@@ -841,12 +831,12 @@ static void Delta_ParseTable( char **delta_script, delta_info_t *dt, const char
|
||||
|
||||
if( !Q_strcmp( token, "DEFINE_DELTA" ))
|
||||
{
|
||||
if( Delta_ParseField( delta_script, pInfo, &pField[dt->numFields], false ))
|
||||
if( Delta_ParseField( delta_script, dt, &pField[dt->numFields], false ))
|
||||
dt->numFields++;
|
||||
}
|
||||
else if( !Q_strcmp( token, "DEFINE_DELTA_POST" ))
|
||||
{
|
||||
if( Delta_ParseField( delta_script, pInfo, &pField[dt->numFields], true ))
|
||||
if( Delta_ParseField( delta_script, dt, &pField[dt->numFields], true ))
|
||||
dt->numFields++;
|
||||
}
|
||||
else if( token[0] == '}' )
|
||||
@@ -963,7 +953,7 @@ void Delta_Init( void )
|
||||
Delta_AddField( dt, "skyvec_z", DT_FLOAT|DT_SIGNED, 16, 32.0f, 1.0f );
|
||||
Delta_AddField( dt, "wateralpha", DT_FLOAT|DT_SIGNED, 16, 32.0f, 1.0f );
|
||||
Delta_AddField( dt, "fog_settings", DT_INTEGER, 32, 1.0f, 1.0f );
|
||||
dt->numFields = NUM_FIELDS( pm_fields ) - 4;
|
||||
dt->numFields = ARRAYSIZE( pm_fields ) - 4;
|
||||
|
||||
// now done
|
||||
dt->bInitialized = true;
|
||||
@@ -976,7 +966,7 @@ void Delta_InitClient( void )
|
||||
// already initalized
|
||||
if( delta_init ) return;
|
||||
|
||||
for( i = 0; i < NUM_FIELDS( dt_info ); i++ )
|
||||
for( i = 0; i < ARRAYSIZE( dt_info ); i++ )
|
||||
{
|
||||
if( dt_info[i].numFields > 0 )
|
||||
{
|
||||
@@ -994,7 +984,7 @@ void Delta_Shutdown( void )
|
||||
|
||||
if( !delta_init ) return;
|
||||
|
||||
for( i = 0; i < NUM_FIELDS( dt_info ); i++ )
|
||||
for( i = 0; i < ARRAYSIZE( dt_info ); i++ )
|
||||
{
|
||||
dt_info[i].numFields = 0;
|
||||
dt_info[i].customEncode = CUSTOM_NONE;
|
||||
|
||||
@@ -46,7 +46,6 @@ enum
|
||||
#if XASH_ENGINE_TESTS
|
||||
DT_DELTA_TEST_STRUCT_T,
|
||||
#endif
|
||||
DT_STRUCT_COUNT
|
||||
};
|
||||
|
||||
// struct info (filled by engine)
|
||||
|
||||
@@ -324,20 +324,14 @@ extern const char *const clc_strings[clc_lastmsg+1];
|
||||
#define PROTOCOL_GOLDSRC_VERSION 48
|
||||
|
||||
#define svc_goldsrc_version svc_changing
|
||||
#define svc_goldsrc_serverinfo svc_serverdata
|
||||
#define svc_goldsrc_deltadescription svc_deltatable
|
||||
#define svc_goldsrc_stopsound svc_resource
|
||||
#define svc_goldsrc_damage svc_restoresound
|
||||
#define svc_goldsrc_killedmonster 27
|
||||
#define svc_goldsrc_foundsecret 28
|
||||
#define svc_goldsrc_spawnstaticsound 29
|
||||
#define svc_goldsrc_decalname svc_bspdecal
|
||||
#define svc_goldsrc_newusermsg svc_usermessage
|
||||
#define svc_goldsrc_newmovevars svc_deltamovevars
|
||||
#define svc_goldsrc_sendextrainfo 54
|
||||
#define svc_goldsrc_timescale 55
|
||||
#define svc_goldsrc_sendcvarvalue svc_querycvarvalue
|
||||
#define svc_goldsrc_sendcvarvalue2 svc_querycvarvalue2
|
||||
|
||||
#define clc_goldsrc_hltv clc_requestcvarvalue // 9
|
||||
#define clc_goldsrc_requestcvarvalue clc_requestcvarvalue2 // 10
|
||||
|
||||
@@ -14,10 +14,7 @@ GNU General Public License for more details.
|
||||
*/
|
||||
|
||||
#include "common.h"
|
||||
#if XASH_WIN32
|
||||
#define STDOUT_FILENO 1
|
||||
#include <io.h>
|
||||
#elif XASH_ANDROID
|
||||
#if XASH_ANDROID
|
||||
#include <android/log.h>
|
||||
#endif
|
||||
#include <string.h>
|
||||
@@ -25,20 +22,13 @@ GNU General Public License for more details.
|
||||
#if XASH_IRIX
|
||||
#include <sys/time.h>
|
||||
#endif
|
||||
#include "xash3d_mathlib.h"
|
||||
|
||||
// do not waste precious CPU cycles on mobiles or low memory devices
|
||||
#if !XASH_WIN32 && !XASH_MOBILE_PLATFORM && !XASH_LOW_MEMORY
|
||||
#define XASH_COLORIZE_CONSOLE true
|
||||
// use with caution, running engine in Qt Creator may cause a freeze in read() call
|
||||
// I have never encountered this bug anywhere else, so still enable by default
|
||||
#define XASH_USE_SELECT 1
|
||||
#define XASH_COLORIZE_CONSOLE 1
|
||||
#else
|
||||
#define XASH_COLORIZE_CONSOLE false
|
||||
#endif
|
||||
|
||||
#if XASH_USE_SELECT
|
||||
// non-blocking console input
|
||||
#include <sys/select.h>
|
||||
#define XASH_COLORIZE_CONSOLE 0
|
||||
#endif
|
||||
|
||||
typedef struct {
|
||||
@@ -51,41 +41,6 @@ typedef struct {
|
||||
|
||||
static LogData s_ld;
|
||||
|
||||
char *Sys_Input( void )
|
||||
{
|
||||
#if XASH_USE_SELECT
|
||||
if( Host_IsDedicated( ))
|
||||
{
|
||||
fd_set rfds;
|
||||
static char line[1024];
|
||||
static int len;
|
||||
struct timeval tv;
|
||||
tv.tv_sec = 0;
|
||||
tv.tv_usec = 0;
|
||||
FD_ZERO( &rfds );
|
||||
FD_SET( 0, &rfds); // stdin
|
||||
while( select( 1, &rfds, NULL, NULL, &tv ) > 0 )
|
||||
{
|
||||
if( read( 0, &line[len], 1 ) != 1 )
|
||||
break;
|
||||
if( line[len] == '\n' || len > 1022 )
|
||||
{
|
||||
line[ ++len ] = 0;
|
||||
len = 0;
|
||||
return line;
|
||||
}
|
||||
len++;
|
||||
tv.tv_sec = 0;
|
||||
tv.tv_usec = 0;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
#if XASH_WIN32
|
||||
return Wcon_Input();
|
||||
#endif
|
||||
return NULL;
|
||||
}
|
||||
|
||||
void Sys_DestroyConsole( void )
|
||||
{
|
||||
// last text message into console or log
|
||||
@@ -194,8 +149,8 @@ void Sys_CloseLog( void )
|
||||
}
|
||||
}
|
||||
|
||||
#if XASH_COLORIZE_CONSOLE == true
|
||||
static void Sys_WriteEscapeSequenceForColorcode( int fd, int c )
|
||||
#if XASH_COLORIZE_CONSOLE
|
||||
static qboolean Sys_WriteEscapeSequenceForColorcode( int fd, int c )
|
||||
{
|
||||
static const char *q3ToAnsi[ 8 ] =
|
||||
{
|
||||
@@ -210,19 +165,26 @@ static void Sys_WriteEscapeSequenceForColorcode( int fd, int c )
|
||||
};
|
||||
const char *esc = q3ToAnsi[c];
|
||||
|
||||
if( c == 7 )
|
||||
write( fd, esc, 4 );
|
||||
else write( fd, esc, 7 );
|
||||
return write( fd, esc, c == 7 ? 4 : 7 ) < 0 ? false : true;
|
||||
}
|
||||
#else
|
||||
static void Sys_WriteEscapeSequenceForColorcode( int fd, int c ) {}
|
||||
static qboolean Sys_WriteEscapeSequenceForColorcode( int fd, int c )
|
||||
{
|
||||
return true;
|
||||
}
|
||||
#endif
|
||||
|
||||
static void Sys_PrintLogfile( const int fd, const char *logtime, const char *msg, const qboolean colorize )
|
||||
static void Sys_PrintLogfile( const int fd, const char *logtime, size_t logtime_len, const char *msg, const int colorize )
|
||||
{
|
||||
const char *p = msg;
|
||||
|
||||
write( fd, logtime, Q_strlen( logtime ) );
|
||||
if( logtime_len != 0 )
|
||||
{
|
||||
if( write( fd, logtime, logtime_len ) < 0 )
|
||||
{
|
||||
// not critical for us
|
||||
}
|
||||
}
|
||||
|
||||
while( p && *p )
|
||||
{
|
||||
@@ -262,7 +224,7 @@ static void Sys_PrintLogfile( const int fd, const char *logtime, const char *msg
|
||||
Sys_WriteEscapeSequenceForColorcode( fd, 7 );
|
||||
}
|
||||
|
||||
static void Sys_PrintStdout( const char *logtime, const char *msg )
|
||||
static void Sys_PrintStdout( const char *logtime, size_t logtime_len, const char *msg )
|
||||
{
|
||||
#if XASH_MOBILE_PLATFORM
|
||||
static char buf[MAX_PRINT_MSG];
|
||||
@@ -294,7 +256,7 @@ static void Sys_PrintStdout( const char *logtime, const char *msg )
|
||||
#endif
|
||||
|
||||
#elif !XASH_WIN32 // Wcon does the job
|
||||
Sys_PrintLogfile( STDOUT_FILENO, logtime, msg, XASH_COLORIZE_CONSOLE );
|
||||
Sys_PrintLogfile( STDOUT_FILENO, logtime, logtime_len, msg, XASH_COLORIZE_CONSOLE );
|
||||
Sys_FlushStdout();
|
||||
#endif
|
||||
}
|
||||
@@ -305,33 +267,44 @@ void Sys_PrintLog( const char *pMsg )
|
||||
const struct tm *crt_tm;
|
||||
char logtime[32] = "";
|
||||
static char lastchar;
|
||||
size_t len;
|
||||
qboolean print_time = true;
|
||||
size_t len, logtime_len = 0;
|
||||
|
||||
time( &crt_time );
|
||||
crt_tm = localtime( &crt_time );
|
||||
if( !lastchar || lastchar == '\n' )
|
||||
{
|
||||
if( time( &crt_time ) >= 0 )
|
||||
{
|
||||
crt_tm = localtime( &crt_time );
|
||||
if( crt_tm == NULL )
|
||||
print_time = false;
|
||||
}
|
||||
}
|
||||
else print_time = false;
|
||||
|
||||
if( !lastchar || lastchar == '\n')
|
||||
strftime( logtime, sizeof( logtime ), "[%H:%M:%S] ", crt_tm ); //short time
|
||||
if( print_time )
|
||||
{
|
||||
logtime_len = strftime( logtime, sizeof( logtime ), "[%H:%M:%S] ", crt_tm ); // short time
|
||||
logtime_len = Q_min( logtime_len, sizeof( logtime ) - 1 ); // just in case
|
||||
}
|
||||
|
||||
// spew to stdout
|
||||
Sys_PrintStdout( logtime, pMsg );
|
||||
Sys_PrintStdout( logtime, logtime_len, pMsg );
|
||||
|
||||
len = Q_strlen( pMsg );
|
||||
|
||||
if( !s_ld.logfile )
|
||||
{
|
||||
// save last char to detect when line was not ended
|
||||
lastchar = len > 0 ? pMsg[len - 1] : 0;
|
||||
return;
|
||||
}
|
||||
|
||||
if( !lastchar || lastchar == '\n')
|
||||
strftime( logtime, sizeof( logtime ), "[%Y:%m:%d|%H:%M:%S] ", crt_tm ); //full time
|
||||
|
||||
// save last char to detect when line was not ended
|
||||
lastchar = len > 0 ? pMsg[len - 1] : 0;
|
||||
|
||||
Sys_PrintLogfile( s_ld.logfileno, logtime, pMsg, false );
|
||||
if( !s_ld.logfile )
|
||||
return;
|
||||
|
||||
if( print_time )
|
||||
{
|
||||
logtime_len = strftime( logtime, sizeof( logtime ), "[%Y:%m:%d|%H:%M:%S] ", crt_tm ); //full time
|
||||
logtime_len = Q_min( logtime_len, sizeof( logtime ) - 1 ); // just in case
|
||||
}
|
||||
|
||||
Sys_PrintLogfile( s_ld.logfileno, logtime, logtime_len, pMsg, false );
|
||||
Sys_FlushLogfile();
|
||||
}
|
||||
|
||||
|
||||
@@ -234,28 +234,12 @@ qboolean Sys_GetIntFromCmdLine( const char* argName, int *out )
|
||||
return true;
|
||||
}
|
||||
|
||||
void Sys_SendKeyEvents( void )
|
||||
{
|
||||
#if XASH_WIN32
|
||||
MSG msg;
|
||||
|
||||
while( PeekMessage( &msg, NULL, 0, 0, PM_NOREMOVE ))
|
||||
{
|
||||
if( !GetMessage( &msg, NULL, 0, 0 ))
|
||||
Sys_Quit ();
|
||||
|
||||
TranslateMessage( &msg );
|
||||
DispatchMessage( &msg );
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
//=======================================================================
|
||||
// DLL'S MANAGER SYSTEM
|
||||
//=======================================================================
|
||||
qboolean Sys_LoadLibrary( dll_info_t *dll )
|
||||
{
|
||||
const dllfunc_t *func;
|
||||
size_t i;
|
||||
string errorstring;
|
||||
|
||||
// check errors
|
||||
@@ -267,12 +251,8 @@ qboolean Sys_LoadLibrary( dll_info_t *dll )
|
||||
|
||||
Con_Reportf( "%s: Loading %s", __func__, dll->name );
|
||||
|
||||
if( dll->fcts )
|
||||
{
|
||||
// lookup export table
|
||||
for( func = dll->fcts; func && func->name != NULL; func++ )
|
||||
*func->func = NULL;
|
||||
}
|
||||
if( dll->fcts ) // lookup export table
|
||||
ClearExports( dll->fcts, dll->num_fcts );
|
||||
|
||||
if( !dll->link )
|
||||
dll->link = COM_LoadLibrary( dll->name, false, true ); // environment pathes
|
||||
@@ -285,9 +265,10 @@ qboolean Sys_LoadLibrary( dll_info_t *dll )
|
||||
}
|
||||
|
||||
// Get the function adresses
|
||||
for( func = dll->fcts; func && func->name != NULL; func++ )
|
||||
for( i = 0; i < dll->num_fcts; i++ )
|
||||
{
|
||||
if( !( *func->func = Sys_GetProcAddress( dll, func->name )))
|
||||
const dllfunc_t *func = &dll->fcts[i];
|
||||
if( !( *func->func = COM_GetProcAddress( dll->link, func->name )))
|
||||
{
|
||||
Q_snprintf( errorstring, sizeof( errorstring ), "Sys_LoadLibrary: %s missing or invalid function (%s)\n", dll->name, func->name );
|
||||
goto error;
|
||||
@@ -300,19 +281,11 @@ error:
|
||||
Con_Reportf( " - failed\n" );
|
||||
Sys_FreeLibrary( dll ); // trying to free
|
||||
if( dll->crash ) Sys_Error( "%s", errorstring );
|
||||
else Con_Reportf( S_ERROR "%s", errorstring );
|
||||
else Con_Reportf( S_ERROR "%s", errorstring );
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
void* Sys_GetProcAddress( dll_info_t *dll, const char* name )
|
||||
{
|
||||
if( !dll || !dll->link ) // invalid desc
|
||||
return NULL;
|
||||
|
||||
return (void *)COM_GetProcAddress( dll->link, name );
|
||||
}
|
||||
|
||||
qboolean Sys_FreeLibrary( dll_info_t *dll )
|
||||
{
|
||||
// invalid desc or alredy freed
|
||||
@@ -330,6 +303,8 @@ qboolean Sys_FreeLibrary( dll_info_t *dll )
|
||||
COM_FreeLibrary( dll->link );
|
||||
dll->link = NULL;
|
||||
|
||||
ClearExports( dll->fcts, dll->num_fcts );
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -437,7 +412,7 @@ void Sys_Error( const char *error, ... )
|
||||
Sys_WaitForQuit();
|
||||
}
|
||||
|
||||
Sys_Quit();
|
||||
Sys_Quit( "caught an error" );
|
||||
}
|
||||
|
||||
#if XASH_EMSCRIPTEN
|
||||
@@ -460,9 +435,9 @@ void my_exit(int ret)
|
||||
Sys_Quit
|
||||
================
|
||||
*/
|
||||
void Sys_Quit( void )
|
||||
void Sys_Quit( const char *reason )
|
||||
{
|
||||
Host_Shutdown();
|
||||
Host_ShutdownWithReason( reason );
|
||||
exit( error_on_exit );
|
||||
}
|
||||
|
||||
@@ -565,7 +540,7 @@ qboolean Sys_NewInstance( const char *gamedir )
|
||||
// just restart the entire thing
|
||||
printf( "envSetNextLoad exe: `%s`\n", exe );
|
||||
printf( "envSetNextLoad argv:\n`%s`\n", newargs );
|
||||
Host_Shutdown( );
|
||||
Host_ShutdownWithReason( "changing game" );
|
||||
envSetNextLoad( exe, newargs );
|
||||
exit( 0 );
|
||||
#else
|
||||
@@ -603,7 +578,7 @@ qboolean Sys_NewInstance( const char *gamedir )
|
||||
#if XASH_PSVITA
|
||||
// under normal circumstances it's always going to be the same path
|
||||
exe = strdup( "app0:/eboot.bin" );
|
||||
Host_Shutdown( );
|
||||
Host_ShutdownWithReason( "changing game" );
|
||||
sceAppMgrLoadExec( exe, newargs, NULL );
|
||||
#else
|
||||
exelen = wai_getExecutablePath( NULL, 0, NULL );
|
||||
@@ -611,7 +586,7 @@ qboolean Sys_NewInstance( const char *gamedir )
|
||||
wai_getExecutablePath( exe, exelen, NULL );
|
||||
exe[exelen] = 0;
|
||||
|
||||
Host_Shutdown();
|
||||
Host_ShutdownWithReason( "changing game" );
|
||||
|
||||
execv( exe, newargs );
|
||||
#endif
|
||||
|
||||
@@ -49,21 +49,17 @@ int Sys_CheckParm( const char *parm );
|
||||
void Sys_Warn( const char *format, ... ) FORMAT_CHECK( 1 );
|
||||
void Sys_Error( const char *error, ... ) FORMAT_CHECK( 1 );
|
||||
qboolean Sys_LoadLibrary( dll_info_t *dll );
|
||||
void* Sys_GetProcAddress( dll_info_t *dll, const char* name );
|
||||
qboolean Sys_FreeLibrary( dll_info_t *dll );
|
||||
void Sys_ParseCommandLine( int argc, char **argv );
|
||||
void Sys_SetupCrashHandler( void );
|
||||
void Sys_RestoreCrashHandler( void );
|
||||
void Sys_DebugBreak( void );
|
||||
#define Sys_GetParmFromCmdLine( parm, out ) _Sys_GetParmFromCmdLine( parm, out, sizeof( out ))
|
||||
qboolean _Sys_GetParmFromCmdLine( const char *parm, char *out, size_t size );
|
||||
qboolean Sys_GetIntFromCmdLine( const char *parm, int *out );
|
||||
void Sys_SendKeyEvents( void );
|
||||
void Sys_Print( const char *pMsg );
|
||||
void Sys_PrintLog( const char *pMsg );
|
||||
void Sys_InitLog( void );
|
||||
void Sys_CloseLog( void );
|
||||
void Sys_Quit( void ) NORETURN;
|
||||
void Sys_Quit( const char *reason ) NORETURN;
|
||||
qboolean Sys_NewInstance( const char *gamedir );
|
||||
void *Sys_GetNativeObject( const char *obj );
|
||||
|
||||
|
||||
@@ -34,6 +34,7 @@ long pathconf( const char *path, int name ) { return -1; }
|
||||
|
||||
// part of libunwind; required by any dynamic lib that uses C++ exceptions
|
||||
extern void *_Unwind_Resume;
|
||||
extern void *_Unwind_GetIPInfo;
|
||||
|
||||
// these are macros in our libc, so we need to wrap them
|
||||
static int tolower_fn( int c ) { return tolower( c ); }
|
||||
@@ -55,6 +56,7 @@ static const solder_export_t aux_exports[] =
|
||||
SOLDER_EXPORT_SYMBOL( strchrnul ),
|
||||
SOLDER_EXPORT_SYMBOL( stpcpy ),
|
||||
SOLDER_EXPORT_SYMBOL( _Unwind_Resume ),
|
||||
SOLDER_EXPORT_SYMBOL( _Unwind_GetIPInfo ),
|
||||
};
|
||||
|
||||
const solder_export_t *__solder_aux_exports = aux_exports;
|
||||
|
||||
@@ -51,6 +51,7 @@ void IOS_LaunchDialog( void );
|
||||
#if XASH_POSIX
|
||||
void Posix_Daemonize( void );
|
||||
void Posix_SetupSigtermHandling( void );
|
||||
char *Posix_Input( void );
|
||||
#endif
|
||||
|
||||
#if XASH_SDL
|
||||
@@ -177,6 +178,20 @@ static inline void Platform_Sleep( int msec )
|
||||
#endif
|
||||
}
|
||||
|
||||
#if XASH_WIN32 || XASH_FREEBSD || XASH_NETBSD || XASH_OPENBSD || XASH_ANDROID || XASH_LINUX
|
||||
void Sys_SetupCrashHandler( void );
|
||||
void Sys_RestoreCrashHandler( void );
|
||||
#else
|
||||
static inline void Sys_SetupCrashHandler( void )
|
||||
{
|
||||
}
|
||||
|
||||
static inline void Sys_RestoreCrashHandler( void )
|
||||
{
|
||||
}
|
||||
#endif
|
||||
|
||||
|
||||
/*
|
||||
==============================================================================
|
||||
|
||||
@@ -227,6 +242,17 @@ static inline void Platform_SetTimer( float time )
|
||||
#endif
|
||||
}
|
||||
|
||||
static inline char *Platform_Input( void )
|
||||
{
|
||||
#if XASH_WIN32
|
||||
return Wcon_Input();
|
||||
#elif XASH_POSIX && !XASH_MOBILE_PLATFORM && !XASH_LOW_MEMORY
|
||||
return Posix_Input();
|
||||
#else
|
||||
return NULL;
|
||||
#endif
|
||||
}
|
||||
|
||||
/*
|
||||
==============================================================================
|
||||
|
||||
|
||||
52
engine/platform/posix/con_posix.c
Normal file
52
engine/platform/posix/con_posix.c
Normal file
@@ -0,0 +1,52 @@
|
||||
/*
|
||||
con_posix.c - reading from stdin
|
||||
Copyright (C) 2024 Flying With Gauss
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
*/
|
||||
|
||||
#include "platform/platform.h"
|
||||
|
||||
#if !XASH_MOBILE_PLATFORM && !XASH_LOW_MEMORY
|
||||
// use with caution, running engine in Qt Creator may cause a freeze in read() call
|
||||
// I have never encountered this bug anywhere else, so still enable by default
|
||||
#include <sys/select.h>
|
||||
|
||||
char *Posix_Input( void )
|
||||
{
|
||||
fd_set rfds;
|
||||
static char line[1024];
|
||||
static int len;
|
||||
struct timeval tv = { 0 };
|
||||
|
||||
if( !Host_IsDedicated( ))
|
||||
return NULL;
|
||||
|
||||
FD_ZERO( &rfds );
|
||||
FD_SET( 0, &rfds); // stdin
|
||||
while( select( 1, &rfds, NULL, NULL, &tv ) > 0 )
|
||||
{
|
||||
if( read( 0, &line[len], 1 ) != 1 )
|
||||
break;
|
||||
if( line[len] == '\n' || len > ( sizeof( line ) - 2 ))
|
||||
{
|
||||
line[ ++len ] = 0;
|
||||
len = 0;
|
||||
return line;
|
||||
}
|
||||
len++;
|
||||
tv.tv_sec = 0;
|
||||
tv.tv_usec = 0;
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
#endif // !XASH_MOBILE_PLATFORM && !XASH_LOW_MEMORY
|
||||
222
engine/platform/posix/crash_posix.c
Normal file
222
engine/platform/posix/crash_posix.c
Normal file
@@ -0,0 +1,222 @@
|
||||
/*
|
||||
crashhandler.c - advanced crashhandler
|
||||
Copyright (C) 2016 Mittorn
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
*/
|
||||
|
||||
#include "common.h"
|
||||
|
||||
// while this is mostly POSIX-compatible functions
|
||||
// the contents of ucontext_t is platform-dependent
|
||||
// before adding new OS here, check platform.h
|
||||
#if XASH_FREEBSD || XASH_NETBSD || XASH_OPENBSD || XASH_ANDROID || XASH_LINUX
|
||||
#ifndef XASH_OPENBSD
|
||||
#include <ucontext.h>
|
||||
#endif
|
||||
#include <signal.h>
|
||||
#include <sys/mman.h>
|
||||
#include "library.h"
|
||||
|
||||
#define STACK_BACKTRACE_STR "Stack backtrace:\n"
|
||||
#define STACK_DUMP_STR "Stack dump:\n"
|
||||
|
||||
#define STACK_BACKTRACE_STR_LEN ( sizeof( STACK_BACKTRACE_STR ) - 1 )
|
||||
#define STACK_DUMP_STR_LEN ( sizeof( STACK_DUMP_STR ) - 1 )
|
||||
#define ALIGN( x, y ) (((uintptr_t) ( x ) + (( y ) - 1 )) & ~(( y ) - 1 ))
|
||||
|
||||
static struct sigaction oldFilter;
|
||||
|
||||
static int Sys_PrintFrame( char *buf, int len, int i, void *addr )
|
||||
{
|
||||
Dl_info dlinfo;
|
||||
if( len <= 0 )
|
||||
return 0; // overflow
|
||||
|
||||
if( dladdr( addr, &dlinfo ))
|
||||
{
|
||||
if( dlinfo.dli_sname )
|
||||
return Q_snprintf( buf, len, "%2d: %p <%s+%lu> (%s)\n", i, addr, dlinfo.dli_sname,
|
||||
(unsigned long)addr - (unsigned long)dlinfo.dli_saddr, dlinfo.dli_fname ); // print symbol, module and address
|
||||
|
||||
return Q_snprintf( buf, len, "%2d: %p (%s)\n", i, addr, dlinfo.dli_fname ); // print module and address
|
||||
}
|
||||
|
||||
return Q_snprintf( buf, len, "%2d: %p\n", i, addr ); // print only address
|
||||
}
|
||||
|
||||
static void Sys_Crash( int signal, siginfo_t *si, void *context)
|
||||
{
|
||||
void *pc = NULL, **bp = NULL, **sp = NULL; // this must be set for every OS!
|
||||
char message[8192];
|
||||
int len, logfd, i = 0;
|
||||
|
||||
#if XASH_OPENBSD
|
||||
struct sigcontext *ucontext = (struct sigcontext*)context;
|
||||
#else
|
||||
ucontext_t *ucontext = (ucontext_t*)context;
|
||||
#endif
|
||||
|
||||
#if XASH_AMD64
|
||||
#if XASH_FREEBSD
|
||||
pc = (void*)ucontext->uc_mcontext.mc_rip;
|
||||
bp = (void**)ucontext->uc_mcontext.mc_rbp;
|
||||
sp = (void**)ucontext->uc_mcontext.mc_rsp;
|
||||
#elif XASH_NETBSD
|
||||
pc = (void*)ucontext->uc_mcontext.__gregs[_REG_RIP];
|
||||
bp = (void**)ucontext->uc_mcontext.__gregs[_REG_RBP];
|
||||
sp = (void**)ucontext->uc_mcontext.__gregs[_REG_RSP];
|
||||
#elif XASH_OPENBSD
|
||||
pc = (void*)ucontext->sc_rip;
|
||||
bp = (void**)ucontext->sc_rbp;
|
||||
sp = (void**)ucontext->sc_rsp;
|
||||
#else
|
||||
pc = (void*)ucontext->uc_mcontext.gregs[REG_RIP];
|
||||
bp = (void**)ucontext->uc_mcontext.gregs[REG_RBP];
|
||||
sp = (void**)ucontext->uc_mcontext.gregs[REG_RSP];
|
||||
#endif
|
||||
#elif XASH_X86
|
||||
#if XASH_FREEBSD
|
||||
pc = (void*)ucontext->uc_mcontext.mc_eip;
|
||||
bp = (void**)ucontext->uc_mcontext.mc_ebp;
|
||||
sp = (void**)ucontext->uc_mcontext.mc_esp;
|
||||
#elif XASH_NETBSD
|
||||
pc = (void*)ucontext->uc_mcontext.__gregs[_REG_EIP];
|
||||
bp = (void**)ucontext->uc_mcontext.__gregs[_REG_EBP];
|
||||
sp = (void**)ucontext->uc_mcontext.__gregs[_REG_ESP];
|
||||
#elif XASH_OPENBSD
|
||||
pc = (void*)ucontext->sc_eip;
|
||||
bp = (void**)ucontext->sc_ebp;
|
||||
sp = (void**)ucontext->sc_esp;
|
||||
#else
|
||||
pc = (void*)ucontext->uc_mcontext.gregs[REG_EIP];
|
||||
bp = (void**)ucontext->uc_mcontext.gregs[REG_EBP];
|
||||
sp = (void**)ucontext->uc_mcontext.gregs[REG_ESP];
|
||||
#endif
|
||||
#elif XASH_ARM && XASH_64BIT
|
||||
pc = (void*)ucontext->uc_mcontext.pc;
|
||||
bp = (void*)ucontext->uc_mcontext.regs[29];
|
||||
sp = (void*)ucontext->uc_mcontext.sp;
|
||||
#elif XASH_ARM
|
||||
pc = (void*)ucontext->uc_mcontext.arm_pc;
|
||||
bp = (void*)ucontext->uc_mcontext.arm_fp;
|
||||
sp = (void*)ucontext->uc_mcontext.arm_sp;
|
||||
#endif
|
||||
|
||||
// safe actions first, stack and memory may be corrupted
|
||||
len = Q_snprintf( message, sizeof( message ), "Ver: " XASH_ENGINE_NAME " " XASH_VERSION " (build %i-%s, %s-%s)\n",
|
||||
Q_buildnum(), Q_buildcommit(), Q_buildos(), Q_buildarch() );
|
||||
|
||||
#if !XASH_FREEBSD && !XASH_NETBSD && !XASH_OPENBSD
|
||||
len += Q_snprintf( message + len, sizeof( message ) - len, "Crash: signal %d errno %d with code %d at %p %p\n", signal, si->si_errno, si->si_code, si->si_addr, si->si_ptr );
|
||||
#else
|
||||
len += Q_snprintf( message + len, sizeof( message ) - len, "Crash: signal %d errno %d with code %d at %p\n", signal, si->si_errno, si->si_code, si->si_addr );
|
||||
#endif
|
||||
|
||||
write( STDERR_FILENO, message, len );
|
||||
|
||||
// flush buffers before writing directly to descriptors
|
||||
fflush( stdout );
|
||||
fflush( stderr );
|
||||
|
||||
// now get log fd and write trace directly to log
|
||||
logfd = Sys_LogFileNo();
|
||||
write( logfd, message, len );
|
||||
|
||||
if( pc && bp && sp )
|
||||
{
|
||||
size_t pagesize = sysconf( _SC_PAGESIZE );
|
||||
|
||||
// try to print backtrace
|
||||
write( STDERR_FILENO, STACK_BACKTRACE_STR, STACK_BACKTRACE_STR_LEN );
|
||||
write( logfd, STACK_BACKTRACE_STR, STACK_BACKTRACE_STR_LEN );
|
||||
Q_strncpy( message + len, STACK_BACKTRACE_STR, sizeof( message ) - len );
|
||||
len += STACK_BACKTRACE_STR_LEN;
|
||||
|
||||
// false on success, true on failure
|
||||
#define try_allow_read(pointer, pagesize) \
|
||||
(( mprotect( (char *)ALIGN( (pointer), (pagesize) ), (pagesize), PROT_READ | PROT_WRITE | PROT_EXEC ) == -1 ) && \
|
||||
( mprotect( (char *)ALIGN( (pointer), (pagesize) ), (pagesize), PROT_READ | PROT_EXEC ) == -1 ) && \
|
||||
( mprotect( (char *)ALIGN( (pointer), (pagesize) ), (pagesize), PROT_READ | PROT_WRITE ) == -1 ) && \
|
||||
( mprotect( (char *)ALIGN( (pointer), (pagesize) ), (pagesize), PROT_READ ) == -1 ))
|
||||
|
||||
do
|
||||
{
|
||||
int line = Sys_PrintFrame( message + len, sizeof( message ) - len, ++i, pc);
|
||||
write( STDERR_FILENO, message + len, line );
|
||||
write( logfd, message + len, line );
|
||||
len += line;
|
||||
//if( !dladdr(bp,0) ) break; // only when bp is in module
|
||||
if( try_allow_read( bp, pagesize ) )
|
||||
break;
|
||||
if( try_allow_read( bp[0], pagesize ) )
|
||||
break;
|
||||
pc = bp[1];
|
||||
bp = (void**)bp[0];
|
||||
}
|
||||
while( bp && i < 128 );
|
||||
|
||||
// try to print stack
|
||||
write( STDERR_FILENO, STACK_DUMP_STR, STACK_DUMP_STR_LEN );
|
||||
write( logfd, STACK_DUMP_STR, STACK_DUMP_STR_LEN );
|
||||
Q_strncpy( message + len, STACK_DUMP_STR, sizeof( message ) - len );
|
||||
len += STACK_DUMP_STR_LEN;
|
||||
|
||||
if( !try_allow_read( sp, pagesize ) )
|
||||
{
|
||||
for( i = 0; i < 32; i++ )
|
||||
{
|
||||
int line = Sys_PrintFrame( message + len, sizeof( message ) - len, i, sp[i] );
|
||||
write( STDERR_FILENO, message + len, line );
|
||||
write( logfd, message + len, line );
|
||||
len += line;
|
||||
}
|
||||
}
|
||||
|
||||
#undef try_allow_read
|
||||
}
|
||||
|
||||
// put MessageBox as Sys_Error
|
||||
Msg( "%s\n", message );
|
||||
#ifdef XASH_SDL
|
||||
SDL_SetWindowGrab( host.hWnd, SDL_FALSE );
|
||||
#endif
|
||||
host.crashed = true;
|
||||
Platform_MessageBox( "Xash Error", message, false );
|
||||
|
||||
// log saved, now we can try to save configs and close log correctly, it may crash
|
||||
if( host.type == HOST_NORMAL )
|
||||
CL_Crashed();
|
||||
host.status = HOST_CRASHED;
|
||||
|
||||
Sys_Quit( "crashed" );
|
||||
}
|
||||
|
||||
void Sys_SetupCrashHandler( void )
|
||||
{
|
||||
struct sigaction act = { 0 };
|
||||
act.sa_sigaction = Sys_Crash;
|
||||
act.sa_flags = SA_SIGINFO | SA_ONSTACK;
|
||||
sigaction( SIGSEGV, &act, &oldFilter );
|
||||
sigaction( SIGABRT, &act, &oldFilter );
|
||||
sigaction( SIGBUS, &act, &oldFilter );
|
||||
sigaction( SIGILL, &act, &oldFilter );
|
||||
}
|
||||
|
||||
void Sys_RestoreCrashHandler( void )
|
||||
{
|
||||
sigaction( SIGSEGV, &oldFilter, NULL );
|
||||
sigaction( SIGABRT, &oldFilter, NULL );
|
||||
sigaction( SIGBUS, &oldFilter, NULL );
|
||||
sigaction( SIGILL, &oldFilter, NULL );
|
||||
}
|
||||
|
||||
#endif // XASH_FREEBSD || XASH_NETBSD || XASH_OPENBSD || XASH_ANDROID || XASH_LINUX
|
||||
@@ -148,7 +148,9 @@ void Posix_Daemonize( void )
|
||||
|
||||
static void Posix_SigtermCallback( int signal )
|
||||
{
|
||||
Sys_Quit();
|
||||
string reason;
|
||||
Con_Printf( reason, sizeof( reason ), "caught signal %d", signal );
|
||||
Sys_Quit( reason );
|
||||
}
|
||||
|
||||
void Posix_SetupSigtermHandling( void )
|
||||
|
||||
@@ -396,8 +396,12 @@ static void SDLash_ActiveEvent( int gain )
|
||||
}
|
||||
#endif
|
||||
host.status = HOST_NOFOCUS;
|
||||
|
||||
if( cls.key_dest == key_game )
|
||||
{
|
||||
Key_ClearStates();
|
||||
IN_DeactivateMouse();
|
||||
}
|
||||
|
||||
host.force_draw_version_time = host.realtime + 2.0;
|
||||
VID_RestoreScreenResolution();
|
||||
@@ -498,7 +502,7 @@ static void SDLash_EventHandler( SDL_Event *event )
|
||||
break;
|
||||
|
||||
case SDL_QUIT:
|
||||
Sys_Quit();
|
||||
Sys_Quit( "caught SDL_QUIT" );
|
||||
break;
|
||||
#if SDL_VERSION_ATLEAST( 2, 0, 0 )
|
||||
case SDL_MOUSEWHEEL:
|
||||
|
||||
@@ -99,13 +99,43 @@ qboolean SNDDMA_Init( void )
|
||||
{
|
||||
SDL_AudioSpec desired, obtained;
|
||||
int samplecount;
|
||||
const char *driver = NULL;
|
||||
|
||||
// Modders often tend to use proprietary crappy solutions
|
||||
// like FMOD to play music, sometimes even with versions outdated by a few decades!
|
||||
//
|
||||
// As these bullshit sound engines prefer to use DirectSound, we ask SDL2 to do
|
||||
// the same. Why you might ask? If SDL2 uses another audio API, like WASAPI on
|
||||
// more modern versions of Windows, it breaks the logic inside Windows, and the whole
|
||||
// application could hang in WaitFor{Single,Multiple}Object function, either called by
|
||||
// SDL2 if FMOD was shut down first, or deep in dsound.dll->fmod.dll if SDL2 audio
|
||||
// was shut down first.
|
||||
//
|
||||
// I honestly don't know who is the real culprit here: FMOD, HL modders, Windows, SDL2
|
||||
// or us.
|
||||
//
|
||||
// Also, fun note, GoldSrc seems doesn't use SDL2 for sound stuff at all, as nothing
|
||||
// reference SDL audio functions there. It's probably has DirectSound backend, that's
|
||||
// why modders never stumble upon this bug.
|
||||
#if XASH_WIN32
|
||||
driver = "directsound";
|
||||
|
||||
if( SDL_getenv( "SDL_AUDIODRIVER" ))
|
||||
driver = NULL; // let SDL2 and user decide
|
||||
|
||||
SDL_SetHint( SDL_HINT_AUDIODRIVER, driver );
|
||||
#endif // XASH_WIN32
|
||||
|
||||
// even if we don't have PA
|
||||
// we still can safely set env variables
|
||||
SDL_setenv( "PULSE_PROP_application.name", GI->title, 1 );
|
||||
SDL_setenv( "PULSE_PROP_media.role", "game", 1 );
|
||||
|
||||
if( SDL_Init( SDL_INIT_AUDIO ))
|
||||
// reinitialize SDL with our driver just in case
|
||||
if( SDL_WasInit( SDL_INIT_AUDIO ))
|
||||
SDL_QuitSubSystem( SDL_INIT_AUDIO );
|
||||
|
||||
if( SDL_InitSubSystem( SDL_INIT_AUDIO ))
|
||||
{
|
||||
Con_Reportf( S_ERROR "Audio: SDL: %s \n", SDL_GetError( ) );
|
||||
return false;
|
||||
@@ -212,7 +242,7 @@ void SNDDMA_Shutdown( void )
|
||||
}
|
||||
|
||||
#if !XASH_EMSCRIPTEN
|
||||
if( SDL_WasInit( SDL_INIT_AUDIO ) )
|
||||
if( SDL_WasInit( SDL_INIT_AUDIO ))
|
||||
SDL_QuitSubSystem( SDL_INIT_AUDIO );
|
||||
#endif
|
||||
|
||||
|
||||
331
engine/platform/win32/crash_win.c
Normal file
331
engine/platform/win32/crash_win.c
Normal file
@@ -0,0 +1,331 @@
|
||||
/*
|
||||
crashhandler.c - advanced crashhandler
|
||||
Copyright (C) 2016 Mittorn
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
*/
|
||||
|
||||
#include "platform/platform.h"
|
||||
|
||||
#define DBGHELP 1 // we always enable dbghelp.dll on Windows targets
|
||||
|
||||
#if DBGHELP
|
||||
|
||||
#include <winnt.h>
|
||||
#include <dbghelp.h>
|
||||
#include <psapi.h>
|
||||
#include <time.h>
|
||||
|
||||
#ifndef XASH_SDL
|
||||
typedef ULONG_PTR DWORD_PTR, *PDWORD_PTR;
|
||||
#endif
|
||||
|
||||
static int Sys_ModuleName( HANDLE process, char *name, void *address, int len )
|
||||
{
|
||||
DWORD_PTR baseAddress = 0;
|
||||
static HMODULE *moduleArray;
|
||||
static unsigned int moduleCount;
|
||||
LPBYTE moduleArrayBytes;
|
||||
DWORD bytesRequired;
|
||||
int i;
|
||||
|
||||
if( len < 3 )
|
||||
return 0;
|
||||
|
||||
if( !moduleArray && EnumProcessModules( process, NULL, 0, &bytesRequired ) )
|
||||
{
|
||||
if ( bytesRequired )
|
||||
{
|
||||
moduleArrayBytes = (LPBYTE)LocalAlloc( LPTR, bytesRequired );
|
||||
|
||||
if( moduleArrayBytes && EnumProcessModules( process, (HMODULE *)moduleArrayBytes, bytesRequired, &bytesRequired ) )
|
||||
{
|
||||
moduleCount = bytesRequired / sizeof( HMODULE );
|
||||
moduleArray = (HMODULE *)moduleArrayBytes;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for( i = 0; i < moduleCount; i++ )
|
||||
{
|
||||
MODULEINFO info;
|
||||
GetModuleInformation( process, moduleArray[i], &info, sizeof(MODULEINFO) );
|
||||
|
||||
if( ( address > info.lpBaseOfDll ) &&
|
||||
( (DWORD64)address < (DWORD64)info.lpBaseOfDll + (DWORD64)info.SizeOfImage ) )
|
||||
return GetModuleBaseName( process, moduleArray[i], name, len );
|
||||
}
|
||||
return Q_snprintf( name, len, "???" );
|
||||
}
|
||||
|
||||
static void Sys_StackTrace( PEXCEPTION_POINTERS pInfo )
|
||||
{
|
||||
char message[8192]; // match *nix Sys_Crash
|
||||
int len = 0;
|
||||
size_t i;
|
||||
HANDLE process = GetCurrentProcess();
|
||||
HANDLE thread = GetCurrentThread();
|
||||
IMAGEHLP_LINE64 line;
|
||||
DWORD dline = 0;
|
||||
DWORD options;
|
||||
CONTEXT context;
|
||||
STACKFRAME64 stackframe;
|
||||
DWORD image;
|
||||
|
||||
context = *pInfo->ContextRecord;
|
||||
|
||||
options = SymGetOptions();
|
||||
options |= SYMOPT_DEBUG;
|
||||
options |= SYMOPT_LOAD_LINES;
|
||||
SymSetOptions( options );
|
||||
|
||||
SymInitialize( process, NULL, TRUE );
|
||||
|
||||
ZeroMemory( &stackframe, sizeof( STACKFRAME64 ));
|
||||
|
||||
#ifdef _M_IX86
|
||||
image = IMAGE_FILE_MACHINE_I386;
|
||||
stackframe.AddrPC.Offset = context.Eip;
|
||||
stackframe.AddrPC.Mode = AddrModeFlat;
|
||||
stackframe.AddrFrame.Offset = context.Ebp;
|
||||
stackframe.AddrFrame.Mode = AddrModeFlat;
|
||||
stackframe.AddrStack.Offset = context.Esp;
|
||||
stackframe.AddrStack.Mode = AddrModeFlat;
|
||||
#elif _M_X64
|
||||
image = IMAGE_FILE_MACHINE_AMD64;
|
||||
stackframe.AddrPC.Offset = context.Rip;
|
||||
stackframe.AddrPC.Mode = AddrModeFlat;
|
||||
stackframe.AddrFrame.Offset = context.Rsp;
|
||||
stackframe.AddrFrame.Mode = AddrModeFlat;
|
||||
stackframe.AddrStack.Offset = context.Rsp;
|
||||
stackframe.AddrStack.Mode = AddrModeFlat;
|
||||
#elif _M_IA64
|
||||
image = IMAGE_FILE_MACHINE_IA64;
|
||||
stackframe.AddrPC.Offset = context.StIIP;
|
||||
stackframe.AddrPC.Mode = AddrModeFlat;
|
||||
stackframe.AddrFrame.Offset = context.IntSp;
|
||||
stackframe.AddrFrame.Mode = AddrModeFlat;
|
||||
stackframe.AddrBStore.Offset = context.RsBSP;
|
||||
stackframe.AddrBStore.Mode = AddrModeFlat;
|
||||
stackframe.AddrStack.Offset = context.IntSp;
|
||||
stackframe.AddrStack.Mode = AddrModeFlat;
|
||||
#elif _M_ARM
|
||||
image = IMAGE_FILE_MACHINE_ARMNT;
|
||||
stackframe.AddrPC.Offset = context.Pc;
|
||||
stackframe.AddrPC.Mode = AddrModeFlat;
|
||||
stackframe.AddrFrame.Offset = context.R11;
|
||||
stackframe.AddrFrame.Mode = AddrModeFlat;
|
||||
stackframe.AddrStack.Offset = context.Sp;
|
||||
stackframe.AddrStack.Mode = AddrModeFlat;
|
||||
#elif _M_ARM64
|
||||
image = IMAGE_FILE_MACHINE_ARM64;
|
||||
stackframe.AddrPC.Offset = context.Pc;
|
||||
stackframe.AddrPC.Mode = AddrModeFlat;
|
||||
stackframe.AddrFrame.Offset = context.Fp;
|
||||
stackframe.AddrFrame.Mode = AddrModeFlat;
|
||||
stackframe.AddrStack.Offset = context.Sp;
|
||||
stackframe.AddrStack.Mode = AddrModeFlat;
|
||||
#elif
|
||||
#error
|
||||
#endif
|
||||
|
||||
len = Q_snprintf( message, sizeof( message ), "Ver: " XASH_ENGINE_NAME " " XASH_VERSION " (build %i-%s, %s-%s)\n",
|
||||
Q_buildnum(), Q_buildcommit(), Q_buildos(), Q_buildarch() );
|
||||
|
||||
len += Q_snprintf( message + len, 1024 - len, "Sys_Crash: address %p, code %p\n",
|
||||
pInfo->ExceptionRecord->ExceptionAddress, (void*)pInfo->ExceptionRecord->ExceptionCode );
|
||||
if( SymGetLineFromAddr64( process, (DWORD64)pInfo->ExceptionRecord->ExceptionAddress, &dline, &line ) )
|
||||
{
|
||||
len += Q_snprintf(message + len, 1024 - len, "Exception: %s:%d:%d\n",
|
||||
(char*)line.FileName, (int)line.LineNumber, (int)dline);
|
||||
}
|
||||
if( SymGetLineFromAddr64( process, stackframe.AddrPC.Offset, &dline, &line ) )
|
||||
{
|
||||
len += Q_snprintf(message + len, 1024 - len,"PC: %s:%d:%d\n",
|
||||
(char*)line.FileName, (int)line.LineNumber, (int)dline);
|
||||
}
|
||||
if( SymGetLineFromAddr64( process, stackframe.AddrFrame.Offset, &dline, &line ) )
|
||||
{
|
||||
len += Q_snprintf(message + len, 1024 - len,"Frame: %s:%d:%d\n",
|
||||
(char*)line.FileName, (int)line.LineNumber, (int)dline);
|
||||
}
|
||||
for( i = 0; i < 25; i++ )
|
||||
{
|
||||
char buffer[sizeof(SYMBOL_INFO) + MAX_SYM_NAME * sizeof(TCHAR)];
|
||||
PSYMBOL_INFO symbol = (PSYMBOL_INFO)buffer;
|
||||
BOOL result = StackWalk64(
|
||||
image, process, thread,
|
||||
&stackframe, &context, NULL,
|
||||
SymFunctionTableAccess64, SymGetModuleBase64, NULL);
|
||||
DWORD64 displacement = 0;
|
||||
|
||||
if( !result )
|
||||
break;
|
||||
|
||||
symbol->SizeOfStruct = sizeof(SYMBOL_INFO);
|
||||
symbol->MaxNameLen = MAX_SYM_NAME;
|
||||
|
||||
len += Q_snprintf( message + len, 1024 - len, "% 2d %p",
|
||||
i, (void*)stackframe.AddrPC.Offset );
|
||||
if( SymFromAddr( process, stackframe.AddrPC.Offset, &displacement, symbol ) )
|
||||
{
|
||||
len += Q_snprintf( message + len, 1024 - len, " %s ", symbol->Name );
|
||||
}
|
||||
if( SymGetLineFromAddr64( process, stackframe.AddrPC.Offset, &dline, &line ) )
|
||||
{
|
||||
len += Q_snprintf(message + len, 1024 - len,"(%s:%d:%d) ",
|
||||
(char*)line.FileName, (int)line.LineNumber, (int)dline);
|
||||
}
|
||||
len += Q_snprintf( message + len, 1024 - len, "(");
|
||||
len += Sys_ModuleName( process, message + len, (void*)stackframe.AddrPC.Offset, 1024 - len );
|
||||
len += Q_snprintf( message + len, 1024 - len, ")\n");
|
||||
}
|
||||
|
||||
#if XASH_SDL == 2
|
||||
if( host.type != HOST_DEDICATED ) // let system to restart server automaticly
|
||||
SDL_ShowSimpleMessageBox( SDL_MESSAGEBOX_ERROR, "Sys_Crash", message, host.hWnd );
|
||||
#endif
|
||||
|
||||
Sys_PrintLog( message );
|
||||
|
||||
SymCleanup( process );
|
||||
}
|
||||
|
||||
static void Sys_GetProcessName( char *processName, size_t bufferSize )
|
||||
{
|
||||
char fullpath[MAX_PATH];
|
||||
|
||||
GetModuleBaseName( GetCurrentProcess(), NULL, fullpath, sizeof( fullpath ) - 1 );
|
||||
COM_FileBase( fullpath, processName, bufferSize );
|
||||
}
|
||||
|
||||
static void Sys_GetMinidumpFileName( const char *processName, char *mdmpFileName, size_t bufferSize )
|
||||
{
|
||||
time_t currentUtcTime = time( NULL );
|
||||
struct tm *currentLocalTime = localtime( ¤tUtcTime );
|
||||
|
||||
Q_snprintf( mdmpFileName, bufferSize, "%s_%s_crash_%d%.2d%.2d_%.2d%.2d%.2d.mdmp",
|
||||
processName,
|
||||
Q_buildcommit(),
|
||||
currentLocalTime->tm_year + 1900,
|
||||
currentLocalTime->tm_mon + 1,
|
||||
currentLocalTime->tm_mday,
|
||||
currentLocalTime->tm_hour,
|
||||
currentLocalTime->tm_min,
|
||||
currentLocalTime->tm_sec);
|
||||
}
|
||||
|
||||
static qboolean Sys_WriteMinidump(PEXCEPTION_POINTERS exceptionInfo, MINIDUMP_TYPE minidumpType)
|
||||
{
|
||||
HRESULT errorCode;
|
||||
string processName;
|
||||
string mdmpFileName;
|
||||
MINIDUMP_EXCEPTION_INFORMATION minidumpInfo;
|
||||
|
||||
Sys_GetProcessName( processName, sizeof( processName ));
|
||||
Sys_GetMinidumpFileName( processName, mdmpFileName, sizeof( mdmpFileName ));
|
||||
|
||||
SetLastError( NOERROR );
|
||||
HANDLE fileHandle = CreateFile(
|
||||
mdmpFileName, GENERIC_WRITE, FILE_SHARE_WRITE,
|
||||
NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);
|
||||
|
||||
errorCode = HRESULT_FROM_WIN32( GetLastError( ));
|
||||
if( !SUCCEEDED( errorCode )) {
|
||||
CloseHandle( fileHandle );
|
||||
return false;
|
||||
}
|
||||
|
||||
minidumpInfo.ThreadId = GetCurrentThreadId();
|
||||
minidumpInfo.ExceptionPointers = exceptionInfo;
|
||||
minidumpInfo.ClientPointers = FALSE;
|
||||
|
||||
qboolean status = MiniDumpWriteDump(
|
||||
GetCurrentProcess(), GetCurrentProcessId(), fileHandle,
|
||||
minidumpType, &minidumpInfo, NULL, NULL);
|
||||
|
||||
CloseHandle( fileHandle );
|
||||
return status;
|
||||
}
|
||||
|
||||
#endif /* DBGHELP */
|
||||
|
||||
static LPTOP_LEVEL_EXCEPTION_FILTER oldFilter;
|
||||
|
||||
static long _stdcall Sys_Crash( PEXCEPTION_POINTERS pInfo )
|
||||
{
|
||||
// save config
|
||||
if( host.status != HOST_CRASHED )
|
||||
{
|
||||
// check to avoid recursive call
|
||||
host.crashed = true;
|
||||
|
||||
#ifdef XASH_SDL
|
||||
SDL_SetWindowGrab( host.hWnd, SDL_FALSE );
|
||||
#endif // XASH_SDL
|
||||
|
||||
#if DBGHELP
|
||||
Sys_StackTrace( pInfo );
|
||||
#else
|
||||
Sys_Warn( "Sys_Crash: call %p at address %p", pInfo->ExceptionRecord->ExceptionAddress, pInfo->ExceptionRecord->ExceptionCode );
|
||||
#endif
|
||||
|
||||
if( host.type == HOST_NORMAL )
|
||||
CL_Crashed(); // tell client about crash
|
||||
else host.status = HOST_CRASHED;
|
||||
|
||||
#if DBGHELP
|
||||
if( Sys_CheckParm( "-minidumps" ))
|
||||
{
|
||||
int minidumpFlags = (
|
||||
MiniDumpWithDataSegs |
|
||||
MiniDumpWithCodeSegs |
|
||||
MiniDumpWithHandleData |
|
||||
MiniDumpWithFullMemory |
|
||||
MiniDumpWithFullMemoryInfo |
|
||||
MiniDumpWithIndirectlyReferencedMemory |
|
||||
MiniDumpWithThreadInfo |
|
||||
MiniDumpWithModuleHeaders);
|
||||
|
||||
if( !Sys_WriteMinidump( pInfo, (MINIDUMP_TYPE)minidumpFlags )) {
|
||||
// fallback method, create minidump with minimal info in it
|
||||
Sys_WriteMinidump( pInfo, MiniDumpWithDataSegs );
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
if( host_developer.value <= 0 )
|
||||
{
|
||||
// no reason to call debugger in release build - just exit
|
||||
Sys_Quit( "crashed" );
|
||||
return EXCEPTION_CONTINUE_EXECUTION;
|
||||
}
|
||||
|
||||
// all other states keep unchanged to let debugger find bug
|
||||
Sys_DestroyConsole();
|
||||
}
|
||||
|
||||
if( oldFilter )
|
||||
return oldFilter( pInfo );
|
||||
return EXCEPTION_CONTINUE_EXECUTION;
|
||||
}
|
||||
|
||||
void Sys_SetupCrashHandler( void )
|
||||
{
|
||||
SetErrorMode( SEM_FAILCRITICALERRORS ); // no abort/retry/fail errors
|
||||
oldFilter = SetUnhandledExceptionFilter( Sys_Crash );
|
||||
}
|
||||
|
||||
void Sys_RestoreCrashHandler( void )
|
||||
{
|
||||
// restore filter
|
||||
if( oldFilter ) SetUnhandledExceptionFilter( oldFilter );
|
||||
}
|
||||
@@ -55,3 +55,4 @@ void Platform_MessageBox( const char *title, const char *message, qboolean paren
|
||||
MessageBox( parentMainWindow ? host.hWnd : NULL, message, title, MB_OK|MB_SETFOREGROUND|MB_ICONSTOP );
|
||||
}
|
||||
#endif // XASH_MESSAGEBOX == MSGBOX_WIN32
|
||||
|
||||
|
||||
@@ -593,7 +593,7 @@ qboolean SV_CheckID( const char *id );
|
||||
// sv_frame.c
|
||||
//
|
||||
void SV_InactivateClients( void );
|
||||
int SV_FindBestBaselineForStatic( int index, entity_state_t **baseline, entity_state_t *to );
|
||||
int SV_FindBestBaseline( int index, entity_state_t **baseline, entity_state_t *to, client_frame_t *frame, qboolean player );
|
||||
void SV_SkipUpdates( void );
|
||||
|
||||
//
|
||||
|
||||
@@ -3022,7 +3022,6 @@ static const ucmd_t ucmds[] =
|
||||
{ "disconnect", SV_Disconnect_f },
|
||||
{ "userinfo", SV_UpdateUserinfo_f },
|
||||
{ "_sv_build_info", SV_SendBuildInfo_f },
|
||||
{ NULL, NULL }
|
||||
};
|
||||
|
||||
static const ucmd_t enttoolscmds[] =
|
||||
@@ -3032,7 +3031,6 @@ static const ucmd_t enttoolscmds[] =
|
||||
{ "ent_fire", SV_EntFire_f },
|
||||
{ "ent_create", SV_EntCreate_f },
|
||||
{ "ent_getvars", SV_EntGetVars_f },
|
||||
{ NULL, NULL }
|
||||
};
|
||||
|
||||
/*
|
||||
@@ -3042,25 +3040,30 @@ SV_ExecuteUserCommand
|
||||
*/
|
||||
static void SV_ExecuteClientCommand( sv_client_t *cl, const char *s )
|
||||
{
|
||||
const ucmd_t *u;
|
||||
int i;
|
||||
|
||||
Cmd_TokenizeString( s );
|
||||
|
||||
for( u = ucmds; u->name; u++ )
|
||||
for( i = 0; i < ARRAYSIZE( ucmds ); i++ )
|
||||
{
|
||||
const ucmd_t *u = &ucmds[i];
|
||||
if( !Q_strcmp( Cmd_Argv( 0 ), u->name ))
|
||||
{
|
||||
if( !u->func( cl ))
|
||||
Con_Printf( "'%s' is not valid from the console\n", u->name );
|
||||
else Con_Reportf( "ucmd->%s()\n", u->name );
|
||||
break;
|
||||
else
|
||||
Con_Reportf( "ucmd->%s()\n", u->name );
|
||||
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if( !u->name && sv_enttools_enable.value > 0.0f && !sv.background )
|
||||
if( sv_enttools_enable.value > 0.0f && !sv.background )
|
||||
{
|
||||
for( u = enttoolscmds; u->name; u++ )
|
||||
for( i = 0; i < ARRAYSIZE( enttoolscmds ); i++ )
|
||||
{
|
||||
const ucmd_t *u = &enttoolscmds[i];
|
||||
|
||||
if( !Q_strcmp( Cmd_Argv( 0 ), u->name ))
|
||||
{
|
||||
Con_Reportf( "enttools->%s(): %s\n", u->name, s );
|
||||
@@ -3070,12 +3073,12 @@ static void SV_ExecuteClientCommand( sv_client_t *cl, const char *s )
|
||||
if( u->func )
|
||||
u->func( cl );
|
||||
|
||||
break;
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if( !u->name && sv.state == ss_active )
|
||||
if( sv.state == ss_active )
|
||||
{
|
||||
qboolean fullupdate = !Q_strcmp( Cmd_Argv( 0 ), "fullupdate" );
|
||||
|
||||
|
||||
@@ -846,7 +846,7 @@ Kick everyone off, possibly in preparation for a new game
|
||||
*/
|
||||
static void SV_KillServer_f( void )
|
||||
{
|
||||
Host_ShutdownServer();
|
||||
SV_Shutdown( "Server was killed due to shutdownserver command\n" );
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -954,6 +954,22 @@ static void Rcon_Redirect_f( void )
|
||||
Msg( "Redirection enabled for next %d lines\n", lines );
|
||||
}
|
||||
|
||||
static void SV_ListMessages_f( void )
|
||||
{
|
||||
int i;
|
||||
|
||||
Con_Printf( "num size name\n" );
|
||||
for( i = 1; i < MAX_USER_MESSAGES; i++ )
|
||||
{
|
||||
if( !COM_CheckStringEmpty( svgame.msg[i].name ))
|
||||
break;
|
||||
|
||||
Con_Printf( "%3d\t%3d\t%s\n", svgame.msg[i].number, svgame.msg[i].size, svgame.msg[i].name );
|
||||
}
|
||||
|
||||
Con_Printf( "Total %i messages\n", i - 1 );
|
||||
}
|
||||
|
||||
/*
|
||||
==================
|
||||
SV_InitHostCommands
|
||||
@@ -1006,6 +1022,7 @@ void SV_InitOperatorCommands( void )
|
||||
Cmd_AddCommand( "logaddress", SV_SetLogAddress_f, "sets address and port for remote logging host" );
|
||||
Cmd_AddCommand( "log", SV_ServerLog_f, "enables logging to file" );
|
||||
Cmd_AddCommand( "str64stats", SV_PrintStr64Stats_f, "print engine pool string statistics" );
|
||||
Cmd_AddCommand( "sv_list_messages", SV_ListMessages_f, "list registered user messages" );
|
||||
|
||||
if( host.type == HOST_NORMAL )
|
||||
{
|
||||
|
||||
@@ -178,9 +178,10 @@ Encode a client frame onto the network channel
|
||||
SV_FindBestBaseline
|
||||
|
||||
trying to deltas with previous entities
|
||||
set frame to NULL to check for static entities
|
||||
=============
|
||||
*/
|
||||
static int SV_FindBestBaseline( sv_client_t *cl, int index, entity_state_t **baseline, entity_state_t *to, client_frame_t *frame, qboolean player )
|
||||
int SV_FindBestBaseline( int index, entity_state_t **baseline, entity_state_t *to, client_frame_t *frame, qboolean player )
|
||||
{
|
||||
int bestBitCount;
|
||||
int i, bitCount;
|
||||
@@ -193,7 +194,13 @@ static int SV_FindBestBaseline( sv_client_t *cl, int index, entity_state_t **bas
|
||||
for( i = index - 1; bestBitCount > 0 && i >= 0 && ( index - i ) < ( MAX_CUSTOM_BASELINES - 1 ); i-- )
|
||||
{
|
||||
// don't worry about underflow in circular buffer
|
||||
entity_state_t *test = &svs.packet_entities[(frame->first_entity+i) % svs.num_client_entities];
|
||||
entity_state_t *test;
|
||||
|
||||
// if set, then it's normal entity
|
||||
if( frame != NULL )
|
||||
test = &svs.packet_entities[(frame->first_entity+i) % svs.num_client_entities];
|
||||
else
|
||||
test = &svs.static_entities[i];
|
||||
|
||||
if( to->entityType == test->entityType )
|
||||
{
|
||||
@@ -209,44 +216,12 @@ static int SV_FindBestBaseline( sv_client_t *cl, int index, entity_state_t **bas
|
||||
|
||||
// using delta from previous entity as baseline for current
|
||||
if( index != bestfound )
|
||||
*baseline = &svs.packet_entities[(frame->first_entity+bestfound) % svs.num_client_entities];
|
||||
return index - bestfound;
|
||||
}
|
||||
|
||||
/*
|
||||
=============
|
||||
SV_FindBestBaselineForStatic
|
||||
|
||||
trying to deltas with previous static entities
|
||||
=============
|
||||
*/
|
||||
int SV_FindBestBaselineForStatic( int index, entity_state_t **baseline, entity_state_t *to )
|
||||
{
|
||||
int bestBitCount;
|
||||
int i, bitCount;
|
||||
int bestfound, j;
|
||||
|
||||
bestBitCount = j = Delta_TestBaseline( *baseline, to, false, sv.time );
|
||||
bestfound = index;
|
||||
|
||||
// lookup backward for previous 64 states and try to interpret current delta as baseline
|
||||
for( i = index - 1; bestBitCount > 0 && i >= 0 && ( index - i ) < ( MAX_CUSTOM_BASELINES - 1 ); i-- )
|
||||
{
|
||||
// don't worry about underflow in circular buffer
|
||||
entity_state_t *test = &svs.static_entities[i];
|
||||
|
||||
bitCount = Delta_TestBaseline( test, to, false, sv.time );
|
||||
|
||||
if( bitCount < bestBitCount )
|
||||
{
|
||||
bestBitCount = bitCount;
|
||||
bestfound = i;
|
||||
}
|
||||
if( frame != NULL )
|
||||
*baseline = &svs.packet_entities[(frame->first_entity+bestfound) % svs.num_client_entities];
|
||||
else
|
||||
*baseline = &svs.static_entities[bestfound];
|
||||
}
|
||||
|
||||
// using delta from previous entity as baseline for current
|
||||
if( index != bestfound )
|
||||
*baseline = &svs.static_entities[bestfound];
|
||||
return index - bestfound;
|
||||
}
|
||||
|
||||
@@ -347,7 +322,7 @@ static void SV_EmitPacketEntities( sv_client_t *cl, client_frame_t *to, sizebuf_
|
||||
// trying to reduce message by select optimal baseline
|
||||
if( !sv_instancedbaseline.value || !sv.num_instanced || sv.last_valid_baseline > newnum )
|
||||
{
|
||||
offset = SV_FindBestBaseline( cl, newindex, &baseline, newent, to, player );
|
||||
offset = SV_FindBestBaseline( newindex, &baseline, newent, to, player );
|
||||
}
|
||||
else
|
||||
{
|
||||
|
||||
@@ -578,7 +578,7 @@ qboolean SV_CreateStaticEntity( sizebuf_t *msg, int index )
|
||||
state->number = 0;
|
||||
|
||||
// trying to compress with previous delta's
|
||||
offset = SV_FindBestBaselineForStatic( index, &baseline, state );
|
||||
offset = SV_FindBestBaseline( index, &baseline, state, NULL, false );
|
||||
|
||||
MSG_BeginServerCmd( msg, svc_spawnstatic );
|
||||
MSG_WriteDeltaEntity( baseline, state, msg, true, DELTA_STATIC, sv.time, offset );
|
||||
@@ -1565,21 +1565,6 @@ edict_t *SV_FindGlobalEntity( string_t classname, string_t globalname )
|
||||
return pent;
|
||||
}
|
||||
|
||||
/*
|
||||
==============
|
||||
pfnGetEntityIllum
|
||||
|
||||
returns averaged lightvalue for entity
|
||||
==============
|
||||
*/
|
||||
static int GAME_EXPORT pfnGetEntityIllum( edict_t* pEnt )
|
||||
{
|
||||
if( !SV_IsValidEdict( pEnt ))
|
||||
return -1;
|
||||
|
||||
return SV_LightForEntity( pEnt );
|
||||
}
|
||||
|
||||
/*
|
||||
=================
|
||||
pfnFindEntityInSphere
|
||||
@@ -2450,10 +2435,17 @@ pfnLightStyle
|
||||
*/
|
||||
static void GAME_EXPORT pfnLightStyle( int style, const char* val )
|
||||
{
|
||||
if( style < 0 ) style = 0;
|
||||
if( style < 0 )
|
||||
style = 0;
|
||||
|
||||
if( style >= MAX_LIGHTSTYLES )
|
||||
{
|
||||
Host_Error( "%s: style: %i >= %d", __func__, style, MAX_LIGHTSTYLES );
|
||||
if( sv.loadgame ) return; // don't let the world overwrite our restored styles
|
||||
return;
|
||||
}
|
||||
|
||||
if( sv.loadgame )
|
||||
return; // don't let the world overwrite our restored styles
|
||||
|
||||
SV_SetLightStyle( style, val, 0.0f ); // set correct style
|
||||
}
|
||||
@@ -3520,7 +3512,7 @@ static int GAME_EXPORT pfnRegUserMsg( const char *pszName, int iSize )
|
||||
{
|
||||
// see if already registered
|
||||
if( !Q_strcmp( svgame.msg[i].name, pszName ))
|
||||
return svc_lastmsg + i; // offset
|
||||
return svgame.msg[i].number;
|
||||
}
|
||||
|
||||
if( i == MAX_USER_MESSAGES )
|
||||
@@ -4446,6 +4438,11 @@ static void GAME_EXPORT pfnGetPlayerStats( const edict_t *pClient, int *ping, in
|
||||
if( ping ) *ping = cl->latency * 1000;
|
||||
}
|
||||
|
||||
static void GAME_EXPORT Cmd_AddServerCommand( const char *cmd_name, xcommand_t function )
|
||||
{
|
||||
Cmd_AddCommandEx( cmd_name, function, "server command", CMD_SERVERDLL, __func__ );
|
||||
}
|
||||
|
||||
/*
|
||||
=============
|
||||
pfnForceUnmodified
|
||||
@@ -4677,7 +4674,7 @@ static enginefuncs_t gEngfuncs =
|
||||
pfnChangeYaw,
|
||||
pfnChangePitch,
|
||||
SV_FindEntityByString,
|
||||
pfnGetEntityIllum,
|
||||
SV_LightForEntity,
|
||||
pfnFindEntityInSphere,
|
||||
pfnFindClientInPVS,
|
||||
pfnEntitiesInPVS,
|
||||
|
||||
@@ -805,7 +805,8 @@ static void SV_SetupClients( void )
|
||||
if( !changed_maxclients ) return; // nothing to change
|
||||
|
||||
// if clients count was changed we need to run full shutdown procedure
|
||||
if( svs.maxclients ) Host_ShutdownServer();
|
||||
if( svs.maxclients )
|
||||
SV_Shutdown( "Server was killed due to maxclients change\n" );
|
||||
|
||||
// copy the actual value from cvar
|
||||
svs.maxclients = (int)sv_maxclients.value;
|
||||
|
||||
@@ -1784,21 +1784,23 @@ static void SV_Physics_Entity( edict_t *ent )
|
||||
|
||||
static void SV_RunLightStyles( void )
|
||||
{
|
||||
int i, ofs;
|
||||
lightstyle_t *ls;
|
||||
float scale;
|
||||
|
||||
scale = 1.0f;
|
||||
int i;
|
||||
|
||||
// run lightstyles animation
|
||||
for( i = 0, ls = sv.lightstyles; i < MAX_LIGHTSTYLES; i++, ls++ )
|
||||
for( i = 0; i < MAX_LIGHTSTYLES; i++ )
|
||||
{
|
||||
lightstyle_t *ls = &sv.lightstyles[i];
|
||||
int ofs;
|
||||
|
||||
ls->time += sv.frametime;
|
||||
ofs = (ls->time * 10);
|
||||
|
||||
if( ls->length == 0 ) ls->value = scale; // disable this light
|
||||
else if( ls->length == 1 ) ls->value = ( ls->map[0] / 12.0f ) * scale;
|
||||
else ls->value = ( ls->map[ofs % ls->length] / 12.0f ) * scale;
|
||||
if( ls->length == 0 )
|
||||
ls->value = 1.0f; // disable this light
|
||||
else if( ls->length == 1 )
|
||||
ls->value = ls->map[0] / 12.0f;
|
||||
else
|
||||
ls->value = ls->map[ofs % ls->length] / 12.0f;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -141,7 +141,8 @@ static TYPEDESCRIPTION gSaveClient[] =
|
||||
DEFINE_ARRAY( SAVE_CLIENT, introTrack, FIELD_CHARACTER, 64 ),
|
||||
DEFINE_ARRAY( SAVE_CLIENT, mainTrack, FIELD_CHARACTER, 64 ),
|
||||
DEFINE_FIELD( SAVE_CLIENT, trackPosition, FIELD_INTEGER ),
|
||||
DEFINE_FIELD( SAVE_CLIENT, viewentity, FIELD_SHORT ),
|
||||
// mods based on HLU SDK disallow usage of FIELD_SHORT
|
||||
DEFINE_ARRAY( SAVE_CLIENT, viewentity, FIELD_CHARACTER, sizeof( short )),
|
||||
DEFINE_FIELD( SAVE_CLIENT, wateralpha, FIELD_FLOAT ),
|
||||
DEFINE_FIELD( SAVE_CLIENT, wateramp, FIELD_FLOAT ),
|
||||
};
|
||||
@@ -150,7 +151,8 @@ static TYPEDESCRIPTION gDecalEntry[] =
|
||||
{
|
||||
DEFINE_FIELD( decallist_t, position, FIELD_VECTOR ),
|
||||
DEFINE_ARRAY( decallist_t, name, FIELD_CHARACTER, 64 ),
|
||||
DEFINE_FIELD( decallist_t, entityIndex, FIELD_SHORT ),
|
||||
// mods based on HLU SDK disallow usage of FIELD_SHORT
|
||||
DEFINE_ARRAY( decallist_t, entityIndex, FIELD_CHARACTER, sizeof( short )),
|
||||
DEFINE_FIELD( decallist_t, depth, FIELD_CHARACTER ),
|
||||
DEFINE_FIELD( decallist_t, flags, FIELD_CHARACTER ),
|
||||
DEFINE_FIELD( decallist_t, scale, FIELD_FLOAT ),
|
||||
@@ -158,6 +160,7 @@ static TYPEDESCRIPTION gDecalEntry[] =
|
||||
DEFINE_ARRAY( decallist_t, studio_state, FIELD_CHARACTER, sizeof( modelstate_t )),
|
||||
};
|
||||
|
||||
// Can use any FIELD type here because only Xash3D games will spawn static entities
|
||||
static TYPEDESCRIPTION gStaticEntry[] =
|
||||
{
|
||||
DEFINE_FIELD( entity_state_t, messagenum, FIELD_MODELNAME ), // HACKHACK: store model into messagenum
|
||||
@@ -200,7 +203,8 @@ static TYPEDESCRIPTION gStaticEntry[] =
|
||||
static TYPEDESCRIPTION gSoundEntry[] =
|
||||
{
|
||||
DEFINE_ARRAY( soundlist_t, name, FIELD_CHARACTER, 64 ),
|
||||
DEFINE_FIELD( soundlist_t, entnum, FIELD_SHORT ),
|
||||
// mods based on HLU SDK disallow usage of FIELD_SHORT
|
||||
DEFINE_ARRAY( soundlist_t, entnum, FIELD_CHARACTER, sizeof( short )),
|
||||
DEFINE_FIELD( soundlist_t, origin, FIELD_VECTOR ),
|
||||
DEFINE_FIELD( soundlist_t, volume, FIELD_FLOAT ),
|
||||
DEFINE_FIELD( soundlist_t, attenuation, FIELD_FLOAT ),
|
||||
@@ -1602,6 +1606,10 @@ static int LoadGameState( char const *level, qboolean changelevel )
|
||||
pSaveData = LoadSaveData( level );
|
||||
if( !pSaveData ) return 0; // couldn't load the file
|
||||
|
||||
// must set mapname before calling into DLL
|
||||
Q_strncpy( sv.name, level, sizeof( sv.name ));
|
||||
svgame.globals->mapname = MAKE_STRING( sv.name );
|
||||
|
||||
ParseSaveTables( pSaveData, &header, true );
|
||||
EntityPatchRead( pSaveData, level );
|
||||
|
||||
@@ -1609,8 +1617,6 @@ static int LoadGameState( char const *level, qboolean changelevel )
|
||||
sv.loadgame = sv.paused = true;
|
||||
|
||||
Cvar_SetValue( "skill", header.skillLevel );
|
||||
Q_strncpy( sv.name, header.mapName, sizeof( sv.name ));
|
||||
svgame.globals->mapname = MAKE_STRING( sv.name );
|
||||
Cvar_Set( "sv_skyname", header.skyName );
|
||||
|
||||
// restore sky parms
|
||||
|
||||
@@ -1508,23 +1508,16 @@ trace_t SV_MoveToss( edict_t *tossent, edict_t *ignore )
|
||||
===============================================================================
|
||||
*/
|
||||
|
||||
static vec3_t sv_pointColor;
|
||||
|
||||
/*
|
||||
=================
|
||||
SV_RecursiveLightPoint
|
||||
=================
|
||||
*/
|
||||
static qboolean SV_RecursiveLightPoint( model_t *model, mnode_t *node, const vec3_t start, const vec3_t end )
|
||||
static qboolean SV_RecursiveLightPoint( model_t *model, mnode_t *node, const vec3_t start, const vec3_t end, vec3_t point_color )
|
||||
{
|
||||
float front, back, scale, frac;
|
||||
int i, map, side, size;
|
||||
float ds, dt, s, t;
|
||||
int sample_size;
|
||||
msurface_t *surf;
|
||||
mextrasurf_t *info;
|
||||
color24 *lm;
|
||||
vec3_t mid;
|
||||
float front, back, frac;
|
||||
int i, side;
|
||||
vec3_t mid;
|
||||
|
||||
// didn't hit anything
|
||||
if( !node || node->contents < 0 )
|
||||
@@ -1536,27 +1529,28 @@ static qboolean SV_RecursiveLightPoint( model_t *model, mnode_t *node, const vec
|
||||
|
||||
side = front < 0.0f;
|
||||
if(( back < 0.0f ) == side )
|
||||
return SV_RecursiveLightPoint( model, node->children[side], start, end );
|
||||
return SV_RecursiveLightPoint( model, node->children[side], start, end, point_color );
|
||||
|
||||
frac = front / ( front - back );
|
||||
|
||||
VectorLerp( start, frac, end, mid );
|
||||
|
||||
// co down front side
|
||||
if( SV_RecursiveLightPoint( model, node->children[side], start, mid ))
|
||||
if( SV_RecursiveLightPoint( model, node->children[side], start, mid, point_color ))
|
||||
return true; // hit something
|
||||
|
||||
if(( back < 0.0f ) == side )
|
||||
return false;// didn't hit anything
|
||||
return false; // didn't hit anything
|
||||
|
||||
// check for impact on this node
|
||||
surf = model->surfaces + node->firstsurface;
|
||||
|
||||
for( i = 0; i < node->numsurfaces; i++, surf++ )
|
||||
for( i = 0; i < node->numsurfaces; i++ )
|
||||
{
|
||||
int smax, tmax;
|
||||
|
||||
info = surf->info;
|
||||
const msurface_t *surf = &model->surfaces[node->firstsurface + i];
|
||||
const mextrasurf_t *info = surf->info;
|
||||
int smax, tmax, map, size;
|
||||
int sample_size;
|
||||
float ds, dt, s, t;
|
||||
const color24 *lm;
|
||||
|
||||
if( FBitSet( surf->flags, SURF_DRAWTILED ))
|
||||
continue; // no lightmaps
|
||||
@@ -1582,26 +1576,27 @@ static qboolean SV_RecursiveLightPoint( model_t *model, mnode_t *node, const vec
|
||||
ds /= sample_size;
|
||||
dt /= sample_size;
|
||||
|
||||
VectorClear( sv_pointColor );
|
||||
VectorClear( point_color );
|
||||
|
||||
lm = surf->samples + Q_rint( dt ) * smax + Q_rint( ds );
|
||||
size = smax * tmax;
|
||||
|
||||
for( map = 0; map < MAXLIGHTMAPS && surf->styles[map] != 255; map++ )
|
||||
{
|
||||
scale = sv.lightstyles[surf->styles[map]].value;
|
||||
float scale = sv.lightstyles[surf->styles[map]].value;
|
||||
|
||||
sv_pointColor[0] += lm->r * scale;
|
||||
sv_pointColor[1] += lm->g * scale;
|
||||
sv_pointColor[2] += lm->b * scale;
|
||||
point_color[0] += lm->r * scale;
|
||||
point_color[1] += lm->g * scale;
|
||||
point_color[2] += lm->b * scale;
|
||||
|
||||
lm += size; // skip to next lightmap
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
// go down back side
|
||||
return SV_RecursiveLightPoint( model, node->children[!side], mid, end );
|
||||
return SV_RecursiveLightPoint( model, node->children[!side], mid, end, point_color );
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -1615,10 +1610,8 @@ void SV_SetLightStyle( int style, const char* s, float f )
|
||||
{
|
||||
int j, k;
|
||||
|
||||
Q_strncpy( sv.lightstyles[style].pattern, s, sizeof( sv.lightstyles[0].pattern ));
|
||||
j = Q_strncpy( sv.lightstyles[style].pattern, s, sizeof( sv.lightstyles[0].pattern ));
|
||||
sv.lightstyles[style].time = f;
|
||||
|
||||
j = Q_strlen( s );
|
||||
sv.lightstyles[style].length = j;
|
||||
|
||||
for( k = 0; k < j; k++ )
|
||||
@@ -1642,12 +1635,16 @@ grab the ambient lighting color for current point
|
||||
*/
|
||||
int SV_LightForEntity( edict_t *pEdict )
|
||||
{
|
||||
vec3_t start, end;
|
||||
vec3_t point_color = { 1.0f, 1.0f, 1.0f };
|
||||
vec3_t start, end;
|
||||
|
||||
if( !SV_IsValidEdict( pEdict ))
|
||||
return -1;
|
||||
|
||||
if( FBitSet( pEdict->v.effects, EF_FULLBRIGHT ) || !sv.worldmodel->lightdata )
|
||||
return 255;
|
||||
|
||||
// player has more precision light level that come from client-side
|
||||
// player has more precise light level that come from client-side
|
||||
if( FBitSet( pEdict->v.flags, FL_CLIENT ))
|
||||
return pEdict->v.light_level;
|
||||
|
||||
@@ -1657,9 +1654,8 @@ int SV_LightForEntity( edict_t *pEdict )
|
||||
if( FBitSet( pEdict->v.effects, EF_INVLIGHT ))
|
||||
end[2] = start[2] + world.size[2];
|
||||
else end[2] = start[2] - world.size[2];
|
||||
VectorSet( sv_pointColor, 1.0f, 1.0f, 1.0f );
|
||||
|
||||
SV_RecursiveLightPoint( sv.worldmodel, sv.worldmodel->nodes, start, end );
|
||||
SV_RecursiveLightPoint( sv.worldmodel, sv.worldmodel->nodes, start, end, point_color );
|
||||
|
||||
return VectorAvg( sv_pointColor );
|
||||
return VectorAvg( point_color );
|
||||
}
|
||||
|
||||
@@ -170,9 +170,9 @@ typedef struct vguiapi_s
|
||||
void (*SetupDrawingImage)( int *pColor );
|
||||
void (*BindTexture)( int id );
|
||||
void (*EnableTexture)( qboolean enable );
|
||||
void (*CreateTexture)( int id, int width, int height );
|
||||
void (*Reserved0)( int id, int width, int height );
|
||||
void (*UploadTexture)( int id, const char *buffer, int width, int height );
|
||||
void (*UploadTextureBlock)( int id, int drawX, int drawY, const byte *rgba, int blockWidth, int blockHeight );
|
||||
void (*Reserved1)( int id, int drawX, int drawY, const byte *rgba, int blockWidth, int blockHeight );
|
||||
void (*DrawQuad)( const vpoint_t *ul, const vpoint_t *lr );
|
||||
void (*GetTextureSizes)( int *width, int *height );
|
||||
int (*GenerateTexture)( void );
|
||||
|
||||
@@ -130,7 +130,6 @@ def configure(conf):
|
||||
conf.define_cond('XASH_CUSTOM_SWAP', conf.options.CUSTOM_SWAP)
|
||||
conf.define_cond('XASH_ENABLE_MAIN', conf.env.DISABLE_LAUNCHER)
|
||||
conf.define_cond('XASH_NO_ASYNC_NS_RESOLVE', conf.options.NO_ASYNC_RESOLVE)
|
||||
conf.define_cond('DBGHELP', conf.env.DEST_OS == 'win32')
|
||||
conf.define_cond('PSAPI_VERSION', conf.env.DEST_OS == 'win32') # will be defined as 1
|
||||
|
||||
for refdll in conf.refdlls:
|
||||
|
||||
@@ -134,7 +134,7 @@ static void FS_PopulateDirEntries( dir_t *dir, const char *path )
|
||||
}
|
||||
|
||||
stringlistinit( &list );
|
||||
listdirectory( &list, path );
|
||||
listdirectory( &list, path, false );
|
||||
if( !list.numstrings )
|
||||
{
|
||||
dir->numentries = DIRENTRY_EMPTY_DIRECTORY;
|
||||
@@ -225,7 +225,7 @@ static int FS_MaybeUpdateDirEntries( dir_t *dir, const char *path, const char *e
|
||||
int ret;
|
||||
|
||||
stringlistinit( &list );
|
||||
listdirectory( &list, path );
|
||||
listdirectory( &list, path, false );
|
||||
|
||||
if( list.numstrings == 0 ) // empty directory
|
||||
{
|
||||
@@ -421,7 +421,7 @@ static void FS_Search_DIR( searchpath_t *search, stringlist_t *list, const char
|
||||
}
|
||||
|
||||
stringlistinit( &dirlist );
|
||||
listdirectory( &dirlist, netpath );
|
||||
listdirectory( &dirlist, netpath, false );
|
||||
|
||||
Q_strncpy( temp, basepath, sizeof( temp ));
|
||||
|
||||
|
||||
@@ -16,6 +16,8 @@ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
*/
|
||||
|
||||
#define _GNU_SOURCE 1
|
||||
|
||||
#include "build.h"
|
||||
#include <fcntl.h>
|
||||
#include <sys/types.h>
|
||||
@@ -32,6 +34,9 @@ GNU General Public License for more details.
|
||||
#endif
|
||||
#include <stdio.h>
|
||||
#include <stdarg.h>
|
||||
#if HAVE_MEMFD_CREATE
|
||||
#include <sys/mman.h>
|
||||
#endif
|
||||
#include "port.h"
|
||||
#include "defaults.h"
|
||||
#include "const.h"
|
||||
@@ -220,18 +225,13 @@ static void listlowercase( stringlist_t *list )
|
||||
}
|
||||
#endif
|
||||
|
||||
void listdirectory( stringlist_t *list, const char *path )
|
||||
void listdirectory( stringlist_t *list, const char *path, qboolean dirs_only )
|
||||
{
|
||||
#if XASH_WIN32
|
||||
char pattern[4096];
|
||||
struct _finddata_t n_file;
|
||||
intptr_t hFile;
|
||||
#else
|
||||
DIR *dir;
|
||||
struct dirent *entry;
|
||||
#endif
|
||||
struct _finddata_t n_file;
|
||||
intptr_t hFile;
|
||||
|
||||
#if XASH_WIN32
|
||||
Q_snprintf( pattern, sizeof( pattern ), "%s/*", path );
|
||||
|
||||
// ask for the directory listing handle
|
||||
@@ -242,15 +242,33 @@ void listdirectory( stringlist_t *list, const char *path )
|
||||
stringlistappend( list, n_file.name );
|
||||
// iterate through the directory
|
||||
while( _findnext( hFile, &n_file ) == 0 )
|
||||
{
|
||||
if( dirs_only && !FBitSet( n_file.attrib, _A_SUBDIR ))
|
||||
continue;
|
||||
|
||||
stringlistappend( list, n_file.name );
|
||||
}
|
||||
_findclose( hFile );
|
||||
#else
|
||||
if( !( dir = opendir( path ) ) )
|
||||
DIR *dir;
|
||||
struct dirent *entry;
|
||||
|
||||
dir = opendir( path );
|
||||
|
||||
if( !dir )
|
||||
return;
|
||||
|
||||
// iterate through the directory
|
||||
while( ( entry = readdir( dir ) ))
|
||||
while(( entry = readdir( dir )))
|
||||
{
|
||||
#if HAVE_DIRENT_D_TYPE
|
||||
if( dirs_only && entry->d_type != DT_DIR && entry->d_type != DT_UNKNOWN )
|
||||
continue;
|
||||
#endif
|
||||
|
||||
stringlistappend( list, entry->d_name );
|
||||
}
|
||||
|
||||
closedir( dir );
|
||||
#endif
|
||||
|
||||
@@ -391,7 +409,7 @@ void FS_AddGameDirectory( const char *dir, uint flags )
|
||||
int i;
|
||||
|
||||
stringlistinit( &list );
|
||||
listdirectory( &list, dir );
|
||||
listdirectory( &list, dir, false );
|
||||
stringlistsort( &list );
|
||||
|
||||
for( archive = g_archives; archive->ext; archive++ )
|
||||
@@ -503,12 +521,13 @@ FS_WriteGameInfo
|
||||
assume GameInfo is valid
|
||||
================
|
||||
*/
|
||||
static void FS_WriteGameInfo( const char *filepath, gameinfo_t *GameInfo )
|
||||
static qboolean FS_WriteGameInfo( const char *filepath, gameinfo_t *GameInfo )
|
||||
{
|
||||
file_t *f = FS_Open( filepath, "w", false ); // we in binary-mode
|
||||
int i, write_ambients = false;
|
||||
|
||||
if( !f ) Sys_Error( "%s: can't write %s\n", __func__, filepath ); // may be disk-space is out?
|
||||
if( !f )
|
||||
return false;
|
||||
|
||||
FS_Printf( f, "// generated by " XASH_ENGINE_NAME " " XASH_VERSION "-%s (%s-%s)\n\n\n", Q_buildcommit(), Q_buildos(), Q_buildarch() );
|
||||
|
||||
@@ -632,25 +651,50 @@ static void FS_WriteGameInfo( const char *filepath, gameinfo_t *GameInfo )
|
||||
FS_Printf( f, "demomap\t\t\"%s\"\n", GameInfo->demomap );
|
||||
|
||||
FS_Close( f ); // all done
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static void FS_InitGameInfo( gameinfo_t *GameInfo, const char *gamedir )
|
||||
static void FS_MakeGameInfo( void )
|
||||
{
|
||||
if( FS_WriteGameInfo( "gameinfo.txt", FI.GameInfo ))
|
||||
Con_Printf( "Successfully generated %s/gameinfo.txt\n", FI.GameInfo->gamefolder );
|
||||
else
|
||||
Con_Printf( S_ERROR "Can't open %s/gameinfo.txt for write\n", FI.GameInfo->gamefolder );
|
||||
}
|
||||
|
||||
static void FS_InitGameInfo( gameinfo_t *GameInfo, const char *gamedir, qboolean quake, time_t mtime )
|
||||
{
|
||||
memset( GameInfo, 0, sizeof( *GameInfo ));
|
||||
|
||||
// filesystem info
|
||||
Q_strncpy( GameInfo->title, "New Game", sizeof( GameInfo->title ));
|
||||
GameInfo->mtime = mtime;
|
||||
Q_strncpy( GameInfo->gamefolder, gamedir, sizeof( GameInfo->gamefolder ));
|
||||
Q_strncpy( GameInfo->basedir, fs_basedir, sizeof( GameInfo->basedir ));
|
||||
Q_strncpy( GameInfo->sp_entity, "info_player_start", sizeof( GameInfo->sp_entity ));
|
||||
Q_strncpy( GameInfo->mp_entity, "info_player_deathmatch", sizeof( GameInfo->mp_entity ));
|
||||
Q_strncpy( GameInfo->startmap, "newmap", sizeof( GameInfo->startmap ));
|
||||
Q_strncpy( GameInfo->dll_path, "cl_dlls", sizeof( GameInfo->dll_path ));
|
||||
Q_strncpy( GameInfo->game_dll, "dlls/hl.dll", sizeof( GameInfo->game_dll ));
|
||||
Q_strncpy( GameInfo->game_dll_linux, "dlls/hl.so", sizeof( GameInfo->game_dll_linux ));
|
||||
Q_strncpy( GameInfo->game_dll_osx, "dlls/hl.dylib", sizeof( GameInfo->game_dll_osx ));
|
||||
Q_strncpy( GameInfo->iconpath, "game.ico", sizeof( GameInfo->iconpath ));
|
||||
|
||||
if( quake )
|
||||
{
|
||||
Q_strncpy( GameInfo->basedir, "id1", sizeof( GameInfo->basedir ));
|
||||
Q_strncpy( GameInfo->title, gamedir, sizeof( GameInfo->title ));
|
||||
Q_strncpy( GameInfo->startmap, "start", sizeof( GameInfo->startmap ));
|
||||
Q_strncpy( GameInfo->dll_path, "bin", sizeof( GameInfo->dll_path ));
|
||||
Q_strncpy( GameInfo->game_dll, "bin/progs.dll", sizeof( GameInfo->game_dll ));
|
||||
Q_strncpy( GameInfo->game_dll_linux, "bin/progs.so", sizeof( GameInfo->game_dll_linux ));
|
||||
Q_strncpy( GameInfo->game_dll_osx, "bin/progs.dylib", sizeof( GameInfo->game_dll_osx ));
|
||||
}
|
||||
else
|
||||
{
|
||||
Q_strncpy( GameInfo->basedir, fs_basedir, sizeof( GameInfo->basedir ));
|
||||
Q_strncpy( GameInfo->title, "New Game", sizeof( GameInfo->title ));
|
||||
Q_strncpy( GameInfo->startmap, "newmap", sizeof( GameInfo->startmap ));
|
||||
Q_strncpy( GameInfo->dll_path, "cl_dlls", sizeof( GameInfo->dll_path ));
|
||||
Q_strncpy( GameInfo->game_dll, "dlls/hl.dll", sizeof( GameInfo->game_dll ));
|
||||
Q_strncpy( GameInfo->game_dll_linux, "dlls/hl.so", sizeof( GameInfo->game_dll_linux ));
|
||||
Q_strncpy( GameInfo->game_dll_osx, "dlls/hl.dylib", sizeof( GameInfo->game_dll_osx ));
|
||||
}
|
||||
|
||||
GameInfo->max_edicts = DEFAULT_MAX_EDICTS; // default value if not specified
|
||||
GameInfo->max_tents = 500;
|
||||
GameInfo->max_beams = 128;
|
||||
@@ -935,36 +979,19 @@ static void FS_ParseGenericGameInfo( gameinfo_t *GameInfo, const char *buf, cons
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
================
|
||||
FS_CreateDefaultGameInfo
|
||||
================
|
||||
*/
|
||||
static void FS_CreateDefaultGameInfo( const char *filename )
|
||||
{
|
||||
gameinfo_t defGI;
|
||||
|
||||
FS_InitGameInfo( &defGI, fs_basedir );
|
||||
|
||||
// make simple gameinfo.txt
|
||||
FS_WriteGameInfo( filename, &defGI );
|
||||
}
|
||||
|
||||
/*
|
||||
================
|
||||
FS_ParseLiblistGam
|
||||
================
|
||||
*/
|
||||
static qboolean FS_ParseLiblistGam( const char *filename, const char *gamedir, gameinfo_t *GameInfo )
|
||||
static qboolean FS_ParseLiblistGam( const char *filename, const char *gamedir, gameinfo_t *GameInfo, time_t mtime )
|
||||
{
|
||||
char *afile;
|
||||
char *afile = FS_LoadDirectFile( filename, NULL );
|
||||
|
||||
if( !GameInfo ) return false;
|
||||
afile = (char *)FS_LoadDirectFile( filename, NULL );
|
||||
if( !afile ) return false;
|
||||
|
||||
FS_InitGameInfo( GameInfo, gamedir );
|
||||
if( !afile )
|
||||
return false;
|
||||
|
||||
FS_InitGameInfo( GameInfo, gamedir, false, mtime );
|
||||
FS_ParseGenericGameInfo( GameInfo, afile, false );
|
||||
|
||||
Mem_Free( afile );
|
||||
@@ -977,18 +1004,15 @@ static qboolean FS_ParseLiblistGam( const char *filename, const char *gamedir, g
|
||||
FS_ConvertGameInfo
|
||||
================
|
||||
*/
|
||||
static qboolean FS_ConvertGameInfo( const char *gamedir, const char *gameinfo_path, const char *liblist_path )
|
||||
static qboolean FS_ConvertGameInfo( const char *gamedir, const char *gameinfo_path, const char *liblist_path, gameinfo_t *gi, time_t liblist_mtime )
|
||||
{
|
||||
gameinfo_t GameInfo;
|
||||
memset( gi, 0, sizeof( *gi ));
|
||||
|
||||
memset( &GameInfo, 0, sizeof( GameInfo ));
|
||||
|
||||
if( FS_ParseLiblistGam( liblist_path, gamedir, &GameInfo ))
|
||||
// liblist.gam to gameinfo.txt conversion is deprecated, only support for RwDir!
|
||||
if( FS_ParseLiblistGam( liblist_path, gamedir, gi, liblist_mtime ))
|
||||
{
|
||||
Con_DPrintf( "Convert %s to %s\n", liblist_path, gameinfo_path );
|
||||
FS_WriteGameInfo( gameinfo_path, &GameInfo );
|
||||
|
||||
return true;
|
||||
return FS_WriteGameInfo( gameinfo_path, gi );
|
||||
}
|
||||
|
||||
return false;
|
||||
@@ -999,16 +1023,14 @@ static qboolean FS_ConvertGameInfo( const char *gamedir, const char *gameinfo_pa
|
||||
FS_ReadGameInfo
|
||||
================
|
||||
*/
|
||||
static qboolean FS_ReadGameInfo( const char *filepath, const char *gamedir, gameinfo_t *GameInfo )
|
||||
static qboolean FS_ReadGameInfo( const char *filename, const char *gamedir, gameinfo_t *GameInfo, time_t mtime )
|
||||
{
|
||||
char *afile;
|
||||
char *afile = FS_LoadDirectFile( filename, NULL );
|
||||
|
||||
afile = (char *)FS_LoadFile( filepath, NULL, false );
|
||||
if( !afile )
|
||||
return false;
|
||||
|
||||
FS_InitGameInfo( GameInfo, gamedir );
|
||||
|
||||
FS_InitGameInfo( GameInfo, gamedir, false, mtime );
|
||||
FS_ParseGenericGameInfo( GameInfo, afile, true );
|
||||
|
||||
Mem_Free( afile );
|
||||
@@ -1024,20 +1046,32 @@ Checks if game directory resembles Quake Engine game directory
|
||||
(some of checks may as well work with Xash gamedirs, it's not a bug)
|
||||
================
|
||||
*/
|
||||
static qboolean FS_CheckForQuakeGameDir( const char *gamedir, qboolean direct )
|
||||
static qboolean FS_CheckForQuakeGameDir( const char *gamedir )
|
||||
{
|
||||
// if directory contain config.cfg or progs.dat it's 100% gamedir
|
||||
// quake mods probably always archived but can missed config.cfg before first running
|
||||
const char *files[] = { "config.cfg", "progs.dat", "pak0.pak" };
|
||||
// if directory contain quake.rc or progs.dat it's 100% quake gamedir
|
||||
// quake mods probably always archived, so check pak0.pak too
|
||||
const char *files[] = { "progs.dat", "quake.rc" };
|
||||
char buf[MAX_SYSPATH];
|
||||
int i;
|
||||
|
||||
// try to read pak0.pak first, most quake mods are archived
|
||||
if( Q_snprintf( buf, sizeof( buf ), "%s/pak0.pak", gamedir ) > 0 )
|
||||
{
|
||||
if( FS_SysFileExists( buf ))
|
||||
{
|
||||
if( FS_CheckForQuakePak( buf, files, sizeof( files ) / sizeof( files[0] )))
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
// search it in the filesystem
|
||||
for( i = 0; i < sizeof( files ) / sizeof( files[0] ); i++ )
|
||||
{
|
||||
char buf[MAX_VA_STRING];
|
||||
|
||||
Q_snprintf( buf, sizeof( buf ), "%s/%s", gamedir, files[i] );
|
||||
if( direct ? FS_SysFileExists( buf ) : FS_FileExists( buf, false ))
|
||||
return true;
|
||||
if( Q_snprintf( buf, sizeof( buf ), "%s/%s", gamedir, files[i] ) > 0 )
|
||||
{
|
||||
if( FS_SysFileExists( buf ))
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
@@ -1050,7 +1084,7 @@ FS_CheckForXashGameDir
|
||||
Checks if game directory resembles Xash3D game directory
|
||||
===============
|
||||
*/
|
||||
static qboolean FS_CheckForXashGameDir( const char *gamedir, qboolean direct )
|
||||
static qboolean FS_CheckForXashGameDir( const char *gamedir )
|
||||
{
|
||||
// if directory contain gameinfo.txt or liblist.gam it's 100% gamedir
|
||||
const char *files[] = { "gameinfo.txt", "liblist.gam" };
|
||||
@@ -1058,15 +1092,16 @@ static qboolean FS_CheckForXashGameDir( const char *gamedir, qboolean direct )
|
||||
|
||||
for( i = 0; i < sizeof( files ) / sizeof( files[0] ); i++ )
|
||||
{
|
||||
char buf[MAX_SYSPATH];
|
||||
char buf[MAX_SYSPATH];
|
||||
|
||||
Q_snprintf( buf, sizeof( buf ), "%s/%s", gamedir, files[i] );
|
||||
if( direct ? FS_SysFileExists( buf ) : FS_FileExists( buf, false ))
|
||||
return true;
|
||||
if( Q_snprintf( buf, sizeof( buf ), "%s/%s", gamedir, files[i] ) > 0 )
|
||||
{
|
||||
if( FS_SysFileExists( buf ))
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -1074,86 +1109,74 @@ static qboolean FS_CheckForXashGameDir( const char *gamedir, qboolean direct )
|
||||
FS_ParseGameInfo
|
||||
================
|
||||
*/
|
||||
static qboolean FS_ParseGameInfo( const char *gamedir, gameinfo_t *GameInfo )
|
||||
static qboolean FS_ParseGameInfo( const char *gamedir, gameinfo_t *GameInfo, qboolean rodir )
|
||||
{
|
||||
string liblist_path, gameinfo_path;
|
||||
string default_gameinfo_path;
|
||||
qboolean haveUpdate = false;
|
||||
char liblist_path[MAX_SYSPATH];
|
||||
char gameinfo_path[MAX_SYSPATH];
|
||||
char gamedir_path[MAX_SYSPATH];
|
||||
time_t liblist_mtime = -1;
|
||||
time_t gameinfo_mtime = -1;
|
||||
|
||||
Q_snprintf( default_gameinfo_path, sizeof( default_gameinfo_path ), "%s/gameinfo.txt", fs_basedir );
|
||||
Q_snprintf( gameinfo_path, sizeof( gameinfo_path ), "%s/gameinfo.txt", gamedir );
|
||||
Q_snprintf( liblist_path, sizeof( liblist_path ), "%s/liblist.gam", gamedir );
|
||||
if( rodir )
|
||||
Q_snprintf( gamedir_path, sizeof( gamedir_path ), "%s/%s", fs_rodir, gamedir );
|
||||
else
|
||||
Q_snprintf( gamedir_path, sizeof( gamedir_path ), "%s/%s", fs_rootdir, gamedir );
|
||||
|
||||
// here goes some RoDir magic...
|
||||
if( COM_CheckStringEmpty( fs_rodir ))
|
||||
if( !FS_CheckForXashGameDir( gamedir_path ))
|
||||
{
|
||||
string gameinfo_ro, liblist_ro;
|
||||
fs_offset_t roLibListTime, roGameInfoTime, rwGameInfoTime;
|
||||
|
||||
FS_AllowDirectPaths( true );
|
||||
|
||||
Q_snprintf( gameinfo_ro, sizeof( gameinfo_ro ), "%s/%s/gameinfo.txt", fs_rodir, gamedir );
|
||||
Q_snprintf( liblist_ro, sizeof( liblist_ro ), "%s/%s/liblist.gam", fs_rodir, gamedir );
|
||||
|
||||
roLibListTime = FS_SysFileTime( liblist_ro );
|
||||
roGameInfoTime = FS_SysFileTime( gameinfo_ro );
|
||||
rwGameInfoTime = FS_SysFileTime( gameinfo_path );
|
||||
|
||||
// if rodir's liblist.gam newer than rwdir's gameinfo.txt, then convert it
|
||||
if( roLibListTime > rwGameInfoTime )
|
||||
// check if we need to generate gameinfo for Quake
|
||||
if( FS_CheckForQuakeGameDir( gamedir_path ))
|
||||
{
|
||||
haveUpdate = FS_ConvertGameInfo( gamedir, gameinfo_path, liblist_ro );
|
||||
}
|
||||
// if rodir's gameinfo.txt newer than rwdir's gameinfo.txt, just copy the file
|
||||
else if( roGameInfoTime > rwGameInfoTime )
|
||||
{
|
||||
file_t *ro, *rw;
|
||||
fs_offset_t ro_size;
|
||||
|
||||
// read & write as binary to copy the exact file
|
||||
ro = FS_SysOpen( gameinfo_ro, "rb" );
|
||||
rw = FS_SysOpen( gameinfo_path, "wb" );
|
||||
|
||||
FS_Seek( ro, 0, SEEK_END );
|
||||
ro_size = FS_Tell( ro );
|
||||
FS_Seek( ro, 0, SEEK_SET );
|
||||
|
||||
FS_FileCopy( rw, ro, ro_size );
|
||||
|
||||
FS_Close( rw );
|
||||
FS_Close( ro );
|
||||
|
||||
haveUpdate = true;
|
||||
// just generate stub gameinfo in memory
|
||||
FS_InitGameInfo( GameInfo, gamedir, true, -1 );
|
||||
GameInfo->rodir = rodir;
|
||||
return true;
|
||||
}
|
||||
|
||||
FS_AllowDirectPaths( false );
|
||||
// don't add empty or addon directories
|
||||
return false;
|
||||
}
|
||||
|
||||
// do not update gameinfo.txt, if it was just copied from rodir's
|
||||
// if user change liblist.gam update the gameinfo.txt
|
||||
if( !haveUpdate && FS_FileTime( liblist_path, false ) > FS_FileTime( gameinfo_path, false ))
|
||||
FS_ConvertGameInfo( gamedir, gameinfo_path, liblist_path );
|
||||
Q_snprintf( gameinfo_path, sizeof( gameinfo_path ), "%s/gameinfo.txt", gamedir_path );
|
||||
Q_snprintf( liblist_path, sizeof( liblist_path ), "%s/liblist.gam", gamedir_path );
|
||||
|
||||
// force to create gameinfo for specified game if missing
|
||||
if(( FS_CheckForQuakeGameDir( gamedir, false ) || !Q_stricmp( fs_gamedir, gamedir )) && !FS_FileExists( gameinfo_path, false ))
|
||||
liblist_mtime = FS_SysFileTime( liblist_path );
|
||||
gameinfo_mtime = FS_SysFileTime( gameinfo_path );
|
||||
|
||||
// in this function we never write new files for RoDir compatibility
|
||||
// since RoDir is only FWGS feature, do gameinfo.txt conversion only
|
||||
// for RwDir for those who worked with original Xash3D
|
||||
if( !rodir )
|
||||
{
|
||||
gameinfo_t tmpGameInfo;
|
||||
memset( &tmpGameInfo, 0, sizeof( tmpGameInfo ));
|
||||
|
||||
if( FS_ReadGameInfo( default_gameinfo_path, gamedir, &tmpGameInfo ))
|
||||
// !!!only if we have both liblist.gam and gameinfo.txt try to convert liblist.gam to gameinfo.txt if it's newer!!!
|
||||
if( liblist_mtime >= 0 && gameinfo_mtime >= 0 && liblist_mtime > gameinfo_mtime )
|
||||
{
|
||||
// now we have copy of game info from basedir but needs to change gamedir
|
||||
Con_DPrintf( "Convert %s to %s\n", default_gameinfo_path, gameinfo_path );
|
||||
Q_strncpy( tmpGameInfo.gamefolder, gamedir, sizeof( tmpGameInfo.gamefolder ));
|
||||
FS_WriteGameInfo( gameinfo_path, &tmpGameInfo );
|
||||
if( FS_ConvertGameInfo( gamedir, gameinfo_path, liblist_path, GameInfo, liblist_mtime ))
|
||||
return true;
|
||||
}
|
||||
else FS_CreateDefaultGameInfo( gameinfo_path );
|
||||
}
|
||||
|
||||
if( !GameInfo || !FS_FileExists( gameinfo_path, false ))
|
||||
return false; // no dest
|
||||
// can we parse gameinfo.txt?
|
||||
if( gameinfo_mtime >= 0 )
|
||||
{
|
||||
if( FS_ReadGameInfo( gameinfo_path, gamedir, GameInfo, gameinfo_mtime ))
|
||||
{
|
||||
GameInfo->rodir = rodir;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return FS_ReadGameInfo( gameinfo_path, gamedir, GameInfo );
|
||||
// can we parse liblist.gam?
|
||||
if( liblist_mtime >= 0 )
|
||||
{
|
||||
if( FS_ParseLiblistGam( liblist_path, gamedir, GameInfo, liblist_mtime ))
|
||||
{
|
||||
GameInfo->rodir = rodir;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -1192,7 +1215,7 @@ void FS_AddGameHierarchy( const char *dir, uint flags )
|
||||
}
|
||||
}
|
||||
|
||||
if( COM_CheckStringEmpty( fs_rodir ) )
|
||||
if( COM_CheckStringEmpty( fs_rodir ))
|
||||
{
|
||||
// append new flags to rodir, except FS_GAMEDIR_PATH and FS_CUSTOM_PATH
|
||||
uint new_flags = FS_NOWRITE_PATH | (flags & (~FS_GAMEDIR_PATH|FS_CUSTOM_PATH));
|
||||
@@ -1261,10 +1284,12 @@ void FS_LoadGameInfo( const char *rootfolder )
|
||||
// lock uplevel of gamedir for read\write
|
||||
FS_AllowDirectPaths( false );
|
||||
|
||||
if( rootfolder ) Q_strncpy( fs_gamedir, rootfolder, sizeof( fs_gamedir ));
|
||||
if( rootfolder )
|
||||
Q_strncpy( fs_gamedir, rootfolder, sizeof( fs_gamedir ));
|
||||
|
||||
Con_Reportf( "%s( %s )\n", __func__, fs_gamedir );
|
||||
|
||||
// clear any old pathes
|
||||
// clear any old paths
|
||||
FS_ClearSearchPath();
|
||||
|
||||
// validate gamedir
|
||||
@@ -1279,6 +1304,14 @@ void FS_LoadGameInfo( const char *rootfolder )
|
||||
|
||||
FI.GameInfo = FI.games[i];
|
||||
|
||||
if( FI.GameInfo->rodir )
|
||||
{
|
||||
// ensure we have directory in rwdir
|
||||
char buf[MAX_SYSPATH + MAX_QPATH + 2]; // and have plenty of space
|
||||
Q_snprintf( buf, sizeof( buf ), "%s/%s/", fs_rootdir, FI.GameInfo->gamefolder );
|
||||
FS_CreatePath( buf );
|
||||
}
|
||||
|
||||
FS_Rescan(); // create new filesystem
|
||||
}
|
||||
|
||||
@@ -1339,7 +1372,7 @@ static qboolean FS_FindLibrary( const char *dllname, qboolean directpath, fs_dll
|
||||
{
|
||||
string fixedname;
|
||||
searchpath_t *search;
|
||||
int index, start = 0, i, len;
|
||||
int index, start = 0, len;
|
||||
|
||||
// check for bad exports
|
||||
if( !COM_CheckString( dllname ))
|
||||
@@ -1474,6 +1507,33 @@ static void *Sys_GetNativeObjectStub( const char *object )
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static void FS_ValidateDirectories( const char *path, qboolean *has_base_dir, qboolean *has_game_dir )
|
||||
{
|
||||
stringlist_t dirs;
|
||||
int i;
|
||||
|
||||
stringlistinit( &dirs );
|
||||
listdirectory( &dirs, path, true );
|
||||
stringlistsort( &dirs );
|
||||
|
||||
for( i = 0; i < dirs.numstrings; i++ )
|
||||
{
|
||||
if( !FS_SysFolderExists( dirs.strings[i] ))
|
||||
continue;
|
||||
|
||||
if( !Q_stricmp( fs_basedir, dirs.strings[i] ))
|
||||
*has_base_dir = true;
|
||||
|
||||
if( !Q_stricmp( fs_gamedir, dirs.strings[i] ))
|
||||
*has_game_dir = true;
|
||||
|
||||
if( *has_base_dir && *has_game_dir )
|
||||
break;
|
||||
}
|
||||
|
||||
stringlistfreecontents( &dirs );
|
||||
}
|
||||
|
||||
/*
|
||||
================
|
||||
FS_Init
|
||||
@@ -1481,11 +1541,9 @@ FS_Init
|
||||
*/
|
||||
qboolean FS_InitStdio( qboolean unused_set_to_true, const char *rootdir, const char *basedir, const char *gamedir, const char *rodir )
|
||||
{
|
||||
stringlist_t dirs;
|
||||
qboolean hasBaseDir = false;
|
||||
qboolean hasGameDir = false;
|
||||
int i;
|
||||
char buf[MAX_VA_STRING];
|
||||
stringlist_t dirs;
|
||||
int i, rodir_num_games;
|
||||
char buf[MAX_SYSPATH];
|
||||
|
||||
FS_InitMemory();
|
||||
|
||||
@@ -1498,63 +1556,40 @@ qboolean FS_InitStdio( qboolean unused_set_to_true, const char *rootdir, const c
|
||||
Q_strncpy( fs_basedir, basedir, sizeof( fs_basedir ));
|
||||
Q_strncpy( fs_rodir, rodir, sizeof( fs_rodir ));
|
||||
|
||||
// add readonly directories first
|
||||
if( COM_CheckStringEmpty( fs_rodir ))
|
||||
// validate user input
|
||||
if( COM_CheckStringEmpty( fs_rodir ) && !Q_stricmp( fs_rodir, fs_rootdir ))
|
||||
{
|
||||
if( !Q_stricmp( fs_rodir, fs_rootdir ))
|
||||
Sys_Error( "RoDir and default rootdir can't point to same directory!" );
|
||||
return false;
|
||||
}
|
||||
|
||||
// look for game directories in RwDir first
|
||||
// this whole check only required to fallback to base directory
|
||||
// (i.e. default game directory hardcoded in the executable file)
|
||||
{
|
||||
qboolean has_base_dir = false;
|
||||
qboolean has_game_dir = false;
|
||||
|
||||
FS_ValidateDirectories( "./", &has_base_dir, &has_game_dir );
|
||||
|
||||
if( !has_game_dir )
|
||||
{
|
||||
Sys_Error( "RoDir and default rootdir can't point to same directory!" );
|
||||
return false;
|
||||
}
|
||||
// look for game directories in RoDir now
|
||||
if( COM_CheckStringEmpty( fs_rodir ))
|
||||
FS_ValidateDirectories( fs_rodir, &has_base_dir, &has_game_dir );
|
||||
|
||||
stringlistinit( &dirs );
|
||||
listdirectory( &dirs, fs_rodir );
|
||||
stringlistsort( &dirs );
|
||||
|
||||
for( i = 0; i < dirs.numstrings; i++ )
|
||||
{
|
||||
char roPath[MAX_SYSPATH];
|
||||
|
||||
Q_snprintf( roPath, sizeof( roPath ), "%s/%s/", fs_rodir, dirs.strings[i] );
|
||||
|
||||
// check if it's a directory
|
||||
if( !FS_SysFolderExists( roPath ))
|
||||
continue;
|
||||
|
||||
// check if it's gamedir
|
||||
if( FS_CheckForXashGameDir( roPath, true ) || FS_CheckForQuakeGameDir( roPath, true ))
|
||||
if( !has_game_dir )
|
||||
{
|
||||
char rwPath[MAX_SYSPATH];
|
||||
Con_Printf( S_ERROR "game directory \"%s\" not exist\n", fs_gamedir );
|
||||
|
||||
Q_snprintf( rwPath, sizeof( rwPath ), "%s/%s/", fs_rootdir, dirs.strings[i] );
|
||||
FS_CreatePath( rwPath );
|
||||
// revert to base game directory
|
||||
if( has_base_dir )
|
||||
Q_strncpy( fs_gamedir, fs_basedir, sizeof( fs_gamedir ));
|
||||
}
|
||||
}
|
||||
|
||||
stringlistfreecontents( &dirs );
|
||||
}
|
||||
|
||||
// validate directories
|
||||
stringlistinit( &dirs );
|
||||
listdirectory( &dirs, "./" );
|
||||
stringlistsort( &dirs );
|
||||
|
||||
for( i = 0; i < dirs.numstrings; i++ )
|
||||
{
|
||||
if( !Q_stricmp( fs_basedir, dirs.strings[i] ))
|
||||
hasBaseDir = true;
|
||||
|
||||
if( !Q_stricmp( fs_gamedir, dirs.strings[i] ))
|
||||
hasGameDir = true;
|
||||
}
|
||||
|
||||
if( !hasGameDir )
|
||||
{
|
||||
Con_Printf( S_ERROR "game directory \"%s\" not exist\n", fs_gamedir );
|
||||
if( hasBaseDir ) Q_strncpy( fs_gamedir, fs_basedir, sizeof( fs_gamedir ));
|
||||
}
|
||||
|
||||
// build list of game directories here
|
||||
// now start building first level of directory hierarchy
|
||||
if( COM_CheckStringEmpty( fs_rodir ))
|
||||
{
|
||||
Q_snprintf( buf, sizeof( buf ), "%s/", fs_rodir );
|
||||
@@ -1562,15 +1597,62 @@ qboolean FS_InitStdio( qboolean unused_set_to_true, const char *rootdir, const c
|
||||
}
|
||||
FS_AddGameDirectory( "./", FS_STATIC_PATH );
|
||||
|
||||
// but scan rodir for games first
|
||||
if( COM_CheckStringEmpty( fs_rodir ))
|
||||
{
|
||||
stringlistinit( &dirs );
|
||||
listdirectory( &dirs, fs_rodir, true );
|
||||
stringlistsort( &dirs );
|
||||
|
||||
for( i = 0; i < dirs.numstrings; i++ )
|
||||
{
|
||||
if( !FS_SysFolderExists( dirs.strings[i] ))
|
||||
continue;
|
||||
|
||||
if( FI.games[FI.numgames] == NULL )
|
||||
FI.games[FI.numgames] = Mem_Malloc( fs_mempool, sizeof( *FI.games[FI.numgames] ));
|
||||
|
||||
if( FS_ParseGameInfo( dirs.strings[i], FI.games[FI.numgames], true ))
|
||||
FI.numgames++;
|
||||
}
|
||||
|
||||
stringlistfreecontents( &dirs );
|
||||
}
|
||||
|
||||
rodir_num_games = FI.numgames;
|
||||
|
||||
stringlistinit( &dirs );
|
||||
listdirectory( &dirs, "./", true );
|
||||
stringlistsort( &dirs );
|
||||
|
||||
for( i = 0; i < dirs.numstrings; i++ )
|
||||
{
|
||||
int j;
|
||||
|
||||
if( !FS_SysFolderExists( dirs.strings[i] ))
|
||||
continue;
|
||||
|
||||
if( FI.games[FI.numgames] == NULL )
|
||||
FI.games[FI.numgames] = (gameinfo_t *)Mem_Calloc( fs_mempool, sizeof( gameinfo_t ));
|
||||
// update gameinfo from rwdir, if it's newer
|
||||
// with rodir we should never create new gameinfos anymore,
|
||||
// so this only catches possible user changes
|
||||
for( j = 0; j < rodir_num_games; j++ )
|
||||
{
|
||||
if( !Q_stricmp( dirs.strings[i], FI.games[j]->gamefolder ))
|
||||
{
|
||||
gameinfo_t gi;
|
||||
if( FS_ParseGameInfo( dirs.strings[i], &gi, false ))
|
||||
{
|
||||
if( gi.mtime > FI.games[j]->mtime )
|
||||
*FI.games[j] = gi;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if( FS_ParseGameInfo( dirs.strings[i], FI.games[FI.numgames] ))
|
||||
if( FI.games[FI.numgames] == NULL )
|
||||
FI.games[FI.numgames] = Mem_Calloc( fs_mempool, sizeof( *FI.games[FI.numgames] ));
|
||||
|
||||
if( FS_ParseGameInfo( dirs.strings[i], FI.games[FI.numgames], false ))
|
||||
FI.numgames++; // added
|
||||
}
|
||||
|
||||
@@ -1666,9 +1748,10 @@ Internal function used to create a file_t and open the relevant non-packed file
|
||||
*/
|
||||
file_t *FS_SysOpen( const char *filepath, const char *mode )
|
||||
{
|
||||
file_t *file;
|
||||
int mod, opt;
|
||||
uint ind;
|
||||
file_t *file;
|
||||
int mod, opt, fd = -1;
|
||||
qboolean memfile = false;
|
||||
uint ind;
|
||||
|
||||
// Parse the mode string
|
||||
switch( mode[0] )
|
||||
@@ -1708,30 +1791,52 @@ file_t *FS_SysOpen( const char *filepath, const char *mode )
|
||||
}
|
||||
}
|
||||
|
||||
file = (file_t *)Mem_Calloc( fs_mempool, sizeof( *file ));
|
||||
file->filetime = FS_SysFileTime( filepath );
|
||||
file->ungetc = EOF;
|
||||
// the 'm' flag let's user to create temporary file in memory
|
||||
// through so-called "anonymous files"
|
||||
if( Q_strchr( mode, 'm' ))
|
||||
{
|
||||
#if HAVE_MEMFD_CREATE
|
||||
#ifndef MFD_NOEXEC_SEAL
|
||||
#define MFD_NOEXEC_SEAL 8U
|
||||
#endif
|
||||
fd = memfd_create( filepath, MFD_CLOEXEC | MFD_NOEXEC_SEAL );
|
||||
|
||||
// through fcntl() and MFD_ALLOW_SEALING we could enforce
|
||||
// read-write flags but we don't really care about them yet
|
||||
if( fd < 0 )
|
||||
Con_Printf( S_ERROR "%s: can't create anonymous file %s: %s", __func__, filepath, strerror( errno ));
|
||||
else memfile = true;
|
||||
#endif
|
||||
// if it's unsupported, we can open it on disk
|
||||
}
|
||||
|
||||
if( fd < 0 )
|
||||
{
|
||||
#if XASH_WIN32
|
||||
file->handle = _wopen( FS_PathToWideChar( filepath ), mod | opt, 0666 );
|
||||
fd = _wopen( FS_PathToWideChar( filepath ), mod | opt, 0666 );
|
||||
#else
|
||||
file->handle = open( filepath, mod|opt, 0666 );
|
||||
fd = open( filepath, mod|opt, 0666 );
|
||||
#endif
|
||||
}
|
||||
|
||||
#if !XASH_WIN32
|
||||
if( file->handle < 0 )
|
||||
FS_BackupFileName( file, filepath, mod|opt );
|
||||
#endif
|
||||
|
||||
if( file->handle < 0 )
|
||||
if( fd < 0 )
|
||||
{
|
||||
if( errno != ENOENT )
|
||||
Con_Printf( S_ERROR "%s: can't open file %s: %s\n", __func__, filepath, strerror( errno ));
|
||||
|
||||
Mem_Free( file );
|
||||
return NULL;
|
||||
}
|
||||
|
||||
file = (file_t *)Mem_Calloc( fs_mempool, sizeof( *file ));
|
||||
file->filetime = memfile ? 0 : FS_SysFileTime( filepath );
|
||||
file->ungetc = EOF;
|
||||
file->handle = fd;
|
||||
|
||||
#if !XASH_WIN32
|
||||
if( !memfile )
|
||||
FS_BackupFileName( file, filepath, mod|opt );
|
||||
#endif
|
||||
|
||||
file->searchpath = NULL;
|
||||
file->real_length = lseek( file->handle, 0, SEEK_END );
|
||||
|
||||
@@ -3159,6 +3264,8 @@ const fs_api_t g_api =
|
||||
FS_LoadFileFromArchive,
|
||||
|
||||
FS_GetRootDirectory,
|
||||
|
||||
FS_MakeGameInfo,
|
||||
};
|
||||
|
||||
int EXPORT GetFSAPI( int version, fs_api_t *api, fs_globals_t **globals, fs_interface_t *engfuncs );
|
||||
|
||||
@@ -123,6 +123,9 @@ typedef struct gameinfo_s
|
||||
qboolean animated_title;
|
||||
|
||||
char demomap[MAX_QPATH];
|
||||
|
||||
qboolean rodir; // if true, parsed from rodir
|
||||
int64_t mtime;
|
||||
} gameinfo_t;
|
||||
|
||||
typedef struct fs_dllinfo_t
|
||||
@@ -226,6 +229,8 @@ typedef struct fs_api_t
|
||||
|
||||
// gets current root directory, set by InitStdio
|
||||
qboolean (*GetRootDirectory)( char *path, size_t size );
|
||||
|
||||
void (*MakeGameInfo)( void );
|
||||
} fs_api_t;
|
||||
|
||||
typedef struct fs_interface_t
|
||||
|
||||
@@ -205,7 +205,7 @@ void stringlistinit( stringlist_t *list );
|
||||
void stringlistfreecontents( stringlist_t *list );
|
||||
void stringlistappend( stringlist_t *list, const char *text );
|
||||
void stringlistsort( stringlist_t *list );
|
||||
void listdirectory( stringlist_t *list, const char *path );
|
||||
void listdirectory( stringlist_t *list, const char *path, qboolean dirs_only );
|
||||
|
||||
// filesystem ops
|
||||
int FS_FileExists( const char *filename, int gamedironly );
|
||||
@@ -230,6 +230,7 @@ qboolean FS_FullPathToRelativePath( char *dst, const char *src, size_t size );
|
||||
//
|
||||
// pak.c
|
||||
//
|
||||
qboolean FS_CheckForQuakePak( const char *pakfile, const char *files[], size_t num_files );
|
||||
searchpath_t *FS_AddPak_Fullpath( const char *pakfile, int flags );
|
||||
|
||||
//
|
||||
|
||||
@@ -105,6 +105,8 @@ static pack_t *FS_LoadPackPAK( const char *packfile, int *error )
|
||||
|
||||
// TODO: use FS_Open to allow PK3 to be included into other archives
|
||||
// Currently, it doesn't work with rodir due to FS_FindFile logic
|
||||
// when it will use FS_Open, check that FS_CheckForQuakePak correctly
|
||||
// detects Quake gamedirs in RoDir
|
||||
packhandle = FS_SysOpen( packfile, "rb" );
|
||||
|
||||
if( packhandle == NULL )
|
||||
@@ -364,3 +366,45 @@ searchpath_t *FS_AddPak_Fullpath( const char *pakfile, int flags )
|
||||
|
||||
return search;
|
||||
}
|
||||
|
||||
/*
|
||||
================
|
||||
FS_CheckForQuakePak
|
||||
|
||||
To generate fake gameinfo for Quake directory, we need to parse pak0.pak
|
||||
and find progs.dat in it
|
||||
================
|
||||
*/
|
||||
qboolean FS_CheckForQuakePak( const char *pakfile, const char *files[], size_t num_files )
|
||||
{
|
||||
qboolean is_quake = false;
|
||||
pack_t *pak;
|
||||
int i;
|
||||
|
||||
pak = FS_LoadPackPAK( pakfile, NULL );
|
||||
if( !pak )
|
||||
return false;
|
||||
|
||||
for( i = 0; i < num_files; i++ )
|
||||
{
|
||||
int j;
|
||||
|
||||
for( j = 0; j < pak->numfiles; j++ )
|
||||
{
|
||||
if( Q_strchr( pak->files[j].name, '/' ))
|
||||
continue; // exclude subdirectories
|
||||
|
||||
if( !Q_stricmp( pak->files[j].name, files[i] ))
|
||||
{
|
||||
is_quake = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if( is_quake )
|
||||
break;
|
||||
}
|
||||
|
||||
Mem_Free( pak );
|
||||
return is_quake;
|
||||
}
|
||||
|
||||
@@ -1,5 +1,14 @@
|
||||
#!/usr/bin/env python
|
||||
|
||||
MEMFD_CREATE_TEST = '''#define _GNU_SOURCE
|
||||
#include <sys/mman.h>
|
||||
int main(int argc, char **argv) { return memfd_create(argv[0], 0); }'''
|
||||
|
||||
DIRENT_D_TYPE_TEST = '''#define _GNU_SOURCE
|
||||
#include <dirent.h>
|
||||
int main(int argc, char **argv) { struct dirent entry; entry.d_type = DT_DIR; return 0; }
|
||||
'''
|
||||
|
||||
def options(opt):
|
||||
pass
|
||||
|
||||
@@ -11,6 +20,12 @@ def configure(conf):
|
||||
elif conf.env.cxxshlib_PATTERN.startswith('lib'): # remove lib prefix for other systems than Android
|
||||
conf.env.cxxshlib_PATTERN = conf.env.cxxshlib_PATTERN[3:]
|
||||
|
||||
if conf.check_cc(fragment=MEMFD_CREATE_TEST, msg='Checking for memfd_create', mandatory=False):
|
||||
conf.define('HAVE_MEMFD_CREATE', 1)
|
||||
|
||||
if conf.check_cc(fragment=DIRENT_D_TYPE_TEST, msg='Checking for d_type field in struct dirent', mandatory=False):
|
||||
conf.define('HAVE_DIRENT_D_TYPE', 1)
|
||||
|
||||
def build(bld):
|
||||
bld(name = 'filesystem_includes', export_includes = '.')
|
||||
|
||||
|
||||
@@ -171,7 +171,7 @@ _inline int Sys_Start( void )
|
||||
if( Xash_Shutdown )
|
||||
changeGame = Sys_ChangeGame;
|
||||
|
||||
ret = Xash_Main( szArgc, szArgv, szGameDir, 0, changeGame );
|
||||
ret = Xash_Main( szArgc, szArgv, szGameDir, 0, XASH_DISABLE_MENU_CHANGEGAME ? NULL : changeGame );
|
||||
|
||||
Sys_UnloadEngine();
|
||||
|
||||
|
||||
@@ -18,7 +18,7 @@ def configure(conf):
|
||||
if conf.env.DEST_OS == 'win32':
|
||||
conf.load('winres')
|
||||
|
||||
conf.define_cond('XASH_DISABLE_MENU_CHANGEGAME', conf.options.DISABLE_MENU_CHANGEGAME)
|
||||
conf.define('XASH_DISABLE_MENU_CHANGEGAME', conf.options.DISABLE_MENU_CHANGEGAME)
|
||||
|
||||
def build(bld):
|
||||
source = ['game.cpp']
|
||||
|
||||
@@ -663,21 +663,6 @@ void COM_RemoveLineFeed( char *str, size_t bufsize )
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
============
|
||||
COM_FixSlashes
|
||||
|
||||
Changes all '\' characters into '/' characters, in place.
|
||||
============
|
||||
*/
|
||||
void COM_FixSlashes( char *pname )
|
||||
{
|
||||
while(( pname = Q_strchr( pname, '\\' )))
|
||||
{
|
||||
*pname = '/';
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
============
|
||||
COM_PathSlashFix
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
/*
|
||||
crtlib.h - internal stdlib
|
||||
Copyright (C) 2011 Uncle Mike
|
||||
Copyright (C) Free Software Foundation, Inc
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
@@ -95,7 +96,6 @@ void COM_ExtractFilePath( const char *path, char *dest );
|
||||
const char *COM_FileWithoutPath( const char *in );
|
||||
void COM_StripExtension( char *path );
|
||||
void COM_RemoveLineFeed( char *str, size_t bufsize );
|
||||
void COM_FixSlashes( char *pname );
|
||||
void COM_PathSlashFix( char *path );
|
||||
// return 0 on empty or null string, 1 otherwise
|
||||
#define COM_CheckString( string ) ( ( !string || !*string ) ? 0 : 1 )
|
||||
@@ -105,6 +105,12 @@ char *COM_ParseFileSafe( char *data, char *token, const int size, unsigned int f
|
||||
int matchpattern( const char *in, const char *pattern, qboolean caseinsensitive );
|
||||
int matchpattern_with_separator( const char *in, const char *pattern, qboolean caseinsensitive, const char *separators, qboolean wildcard_least_one );
|
||||
|
||||
//
|
||||
// dllhelpers.c
|
||||
//
|
||||
void ClearExports( const dllfunc_t *funcs, size_t num_funcs );
|
||||
qboolean ValidateExports( const dllfunc_t *funcs, size_t num_funcs );
|
||||
|
||||
static inline char Q_toupper( const char in )
|
||||
{
|
||||
char out;
|
||||
@@ -169,63 +175,62 @@ static inline char *Q_strstr( const char *s1, const char *s2 )
|
||||
}
|
||||
|
||||
// libc extensions, be careful what to enable or what not
|
||||
static inline size_t Q_strnlen( const char *str, size_t size )
|
||||
{
|
||||
#if HAVE_STRNLEN
|
||||
return strnlen( str, size );
|
||||
#else
|
||||
const char *p = (const char *)memchr( str, 0, size );
|
||||
return p ? p - str : size;
|
||||
#endif
|
||||
}
|
||||
|
||||
static inline size_t Q_strncpy( char *dst, const char *src, size_t size )
|
||||
{
|
||||
#if HAVE_STRLCPY
|
||||
if( unlikely( !dst || !src || !size ))
|
||||
return 0;
|
||||
#if HAVE_STRLCPY
|
||||
return strlcpy( dst, src, size );
|
||||
#else
|
||||
size_t len;
|
||||
if( unlikely( !dst || !src || !size ))
|
||||
return 0;
|
||||
|
||||
len = strlen( src );
|
||||
if( len + 1 > size ) // check if truncate
|
||||
{
|
||||
memcpy( dst, src, size - 1 );
|
||||
dst[size - 1] = 0;
|
||||
}
|
||||
else memcpy( dst, src, len + 1 );
|
||||
size_t len = strlen( src );
|
||||
|
||||
return len; // count does not include NULL
|
||||
if( len >= size ) // check if truncate
|
||||
{
|
||||
memcpy( dst, src, size );
|
||||
dst[size - 1] = 0;
|
||||
}
|
||||
else memcpy( dst, src, len + 1 );
|
||||
|
||||
return len; // count does not include NULL
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
static inline size_t Q_strncat( char *dst, const char *src, size_t size )
|
||||
{
|
||||
#if HAVE_STRLCAT
|
||||
if( unlikely( !dst || !src || !size ))
|
||||
return 0;
|
||||
#if HAVE_STRLCAT
|
||||
return strlcat( dst, src, size );
|
||||
#else
|
||||
char *d = dst;
|
||||
const char *s = src;
|
||||
size_t n = size;
|
||||
size_t dlen;
|
||||
|
||||
if( unlikely( !dst || !src || !size ))
|
||||
return 0;
|
||||
|
||||
// find the end of dst and adjust bytes left but don't go past end
|
||||
while( n-- != 0 && *d != '\0' ) d++;
|
||||
dlen = d - dst;
|
||||
n = size - dlen;
|
||||
|
||||
if( n == 0 ) return( dlen + Q_strlen( s ));
|
||||
|
||||
while( *s != '\0' )
|
||||
{
|
||||
if( n != 1 )
|
||||
{
|
||||
*d++ = *s;
|
||||
n--;
|
||||
}
|
||||
s++;
|
||||
}
|
||||
size_t slen = strlen( src );
|
||||
size_t dlen = Q_strnlen( dst, size );
|
||||
|
||||
*d = '\0';
|
||||
return( dlen + ( s - src )); // count does not include NULL
|
||||
if( dlen != size )
|
||||
{
|
||||
size_t copy = size - dlen - 1;
|
||||
|
||||
if( copy > slen )
|
||||
copy = slen;
|
||||
|
||||
memcpy( &dst[dlen], src, copy );
|
||||
dst[dlen + copy] = 0;
|
||||
}
|
||||
|
||||
return dlen + slen;
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
@@ -314,6 +319,19 @@ static inline int Q_splitstr( char *str, int delim, void *userdata,
|
||||
return ret;
|
||||
}
|
||||
|
||||
/*
|
||||
============
|
||||
COM_FixSlashes
|
||||
|
||||
Changes all '\' characters into '/' characters, in place.
|
||||
============
|
||||
*/
|
||||
static inline void COM_FixSlashes( char *pname )
|
||||
{
|
||||
while(( pname = Q_strchr( pname, '\\' )))
|
||||
*pname = '/';
|
||||
}
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
36
public/dllhelpers.c
Normal file
36
public/dllhelpers.c
Normal file
@@ -0,0 +1,36 @@
|
||||
/*
|
||||
dllhelpers.c - dll exports helpers
|
||||
Copyright (C) 2024 Alibek Omarov
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
*/
|
||||
#include "crtlib.h"
|
||||
|
||||
void ClearExports( const dllfunc_t *funcs, size_t num_funcs )
|
||||
{
|
||||
size_t i;
|
||||
|
||||
for( i = 0; i < num_funcs; i++ )
|
||||
*(funcs[i].func) = NULL;
|
||||
}
|
||||
|
||||
qboolean ValidateExports( const dllfunc_t *funcs, size_t num_funcs )
|
||||
{
|
||||
size_t i;
|
||||
|
||||
for( i = 0; i < num_funcs; i++ )
|
||||
{
|
||||
if( *(funcs[i].func) == NULL )
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
@@ -29,6 +29,10 @@ int main(int argc, char **argv) { return strlcpy(argv[1], argv[2], 10); }'''
|
||||
STRLCAT_TEST = '''#include <string.h>
|
||||
int main(int argc, char **argv) { return strlcat(argv[1], argv[2], 10); }'''
|
||||
|
||||
STRNLEN_TEST = '''#include <string.h>
|
||||
int main(int argc, char **argv) { return (int)strnlen(argv[0], 10); }
|
||||
'''
|
||||
|
||||
ALLOCA_TEST = '''#include <%s>
|
||||
int main(void) { alloca(1); return 0; }'''
|
||||
|
||||
@@ -106,6 +110,7 @@ def configure(conf):
|
||||
check_libc_extension(STRCHRNUL_TEST, 'strchrnul', 'HAVE_STRCHRNUL')
|
||||
check_libc_extension(STRLCPY_TEST, 'strlcpy', 'HAVE_STRLCPY')
|
||||
check_libc_extension(STRLCAT_TEST, 'strlcat', 'HAVE_STRLCAT')
|
||||
check_libc_extension(STRNLEN_TEST, 'strnlen', 'HAVE_STRNLEN')
|
||||
|
||||
# kill temporary uselib
|
||||
del conf.env.DEFINES_export
|
||||
|
||||
@@ -20,6 +20,7 @@ GNU General Public License for more details.
|
||||
#if HAVE_TGMATH_H
|
||||
#include <tgmath.h>
|
||||
#endif
|
||||
#include <string.h>
|
||||
|
||||
#include "build.h"
|
||||
#include "xash3d_types.h"
|
||||
@@ -141,15 +142,6 @@ CONSTANTS GLOBALS
|
||||
// a1ba: we never return pointers to these globals
|
||||
// so help compiler optimize constants away
|
||||
#define vec3_origin ((vec3_t){ 0.0f, 0.0f, 0.0f })
|
||||
#define m_matrix3x4_identity ((matrix3x4) { \
|
||||
{ 1.0f, 0.0f, 0.0f, 0.0f }, \
|
||||
{ 0.0f, 1.0f, 0.0f, 0.0f }, \
|
||||
{ 0.0f, 0.0f, 1.0f, 0.0f }} )
|
||||
#define m_matrix4x4_identity ((matrix4x4) { \
|
||||
{ 1.0f, 0.0f, 0.0f, 0.0f }, \
|
||||
{ 0.0f, 1.0f, 0.0f, 0.0f }, \
|
||||
{ 0.0f, 0.0f, 1.0f, 0.0f }, \
|
||||
{ 0.0f, 0.0f, 0.0f, 1.0f }} )
|
||||
|
||||
extern const int boxpnt[6][4];
|
||||
extern const float m_bytenormals[NUMVERTEXNORMALS][3];
|
||||
@@ -191,7 +183,11 @@ int BoxOnPlaneSide( const vec3_t emins, const vec3_t emaxs, const mplane_t *p );
|
||||
//
|
||||
// matrixlib.c
|
||||
//
|
||||
#define Matrix3x4_LoadIdentity( mat ) Matrix3x4_Copy( mat, m_matrix3x4_identity )
|
||||
static inline void Matrix3x4_LoadIdentity( matrix3x4 m )
|
||||
{
|
||||
memset( m, 0, sizeof( *m ));
|
||||
m[0][0] = m[1][1] = m[2][2] = 1.0f;
|
||||
}
|
||||
#define Matrix3x4_Copy( out, in ) memcpy( out, in, sizeof( matrix3x4 ))
|
||||
void Matrix3x4_VectorTransform( const matrix3x4 in, const float v[3], float out[3] );
|
||||
void Matrix3x4_VectorITransform( const matrix3x4 in, const float v[3], float out[3] );
|
||||
@@ -203,7 +199,11 @@ void Matrix3x4_CreateFromEntity( matrix3x4 out, const vec3_t angles, const vec3_
|
||||
void Matrix3x4_TransformAABB( const matrix3x4 world, const vec3_t mins, const vec3_t maxs, vec3_t absmin, vec3_t absmax );
|
||||
void Matrix3x4_AnglesFromMatrix( const matrix3x4 in, vec3_t out );
|
||||
|
||||
#define Matrix4x4_LoadIdentity( mat ) Matrix4x4_Copy( mat, m_matrix4x4_identity )
|
||||
static inline void Matrix4x4_LoadIdentity( matrix4x4 m )
|
||||
{
|
||||
memset( m, 0, sizeof( *m ));
|
||||
m[0][0] = m[1][1] = m[2][2] = m[3][3] = 1.0f;
|
||||
}
|
||||
#define Matrix4x4_Copy( out, in ) memcpy( out, in, sizeof( matrix4x4 ))
|
||||
void Matrix4x4_VectorTransform( const matrix4x4 in, const float v[3], float out[3] );
|
||||
void Matrix4x4_VectorITransform( const matrix4x4 in, const float v[3], float out[3] );
|
||||
|
||||
@@ -502,11 +502,11 @@ void R_ClearSkyBox( void );
|
||||
void R_DrawSkyBox( void );
|
||||
void R_DrawClouds( void );
|
||||
void R_UnloadSkybox( void );
|
||||
void EmitWaterPolys( msurface_t *warp, qboolean reverse );
|
||||
void EmitWaterPolys( msurface_t *warp, qboolean reverse, qboolean ripples );
|
||||
void R_InitRipples( void );
|
||||
void R_ResetRipples( void );
|
||||
void R_AnimateRipples( void );
|
||||
void R_UploadRipples( texture_t *image );
|
||||
qboolean R_UploadRipples( const texture_t *image );
|
||||
|
||||
//#include "vid_common.h"
|
||||
|
||||
@@ -561,8 +561,6 @@ void GL_CheckForErrors_( const char *filename, const int fileline );
|
||||
const char *GL_ErrorString( int err );
|
||||
qboolean GL_Support( int r_ext );
|
||||
int GL_MaxTextureUnits( void );
|
||||
qboolean GL_CheckExtension( const char *name, const dllfunc_t *funcs, const char *cvarname, int r_ext, float minver );
|
||||
void GL_SetExtension( int r_ext, int enable );
|
||||
|
||||
//
|
||||
// gl_triapi.c
|
||||
|
||||
@@ -38,7 +38,6 @@ CVAR_DEFINE_AUTO( r_ripple, "0", FCVAR_GLCONFIG, "enable software-like water tex
|
||||
CVAR_DEFINE_AUTO( r_ripple_updatetime, "0.05", FCVAR_GLCONFIG, "how fast ripple simulation is" );
|
||||
CVAR_DEFINE_AUTO( r_ripple_spawntime, "0.1", FCVAR_GLCONFIG, "how fast new ripples spawn" );
|
||||
|
||||
|
||||
DEFINE_ENGINE_SHARED_CVAR_LIST()
|
||||
|
||||
poolhandle_t r_temppool;
|
||||
@@ -54,208 +53,199 @@ glwstate_t glw_state;
|
||||
#define GL_CALL( x ) #x, (void**)&p##x
|
||||
#endif
|
||||
|
||||
static dllfunc_t opengl_110funcs[] =
|
||||
static const dllfunc_t opengl_110funcs[] =
|
||||
{
|
||||
{ GL_CALL( glClearColor ) },
|
||||
{ GL_CALL( glClear ) },
|
||||
{ GL_CALL( glAlphaFunc ) },
|
||||
{ GL_CALL( glBlendFunc ) },
|
||||
{ GL_CALL( glCullFace ) },
|
||||
{ GL_CALL( glDrawBuffer ) },
|
||||
{ GL_CALL( glReadBuffer ) },
|
||||
{ GL_CALL( glAccum ) },
|
||||
{ GL_CALL( glEnable ) },
|
||||
{ GL_CALL( glDisable ) },
|
||||
{ GL_CALL( glEnableClientState ) },
|
||||
{ GL_CALL( glDisableClientState ) },
|
||||
{ GL_CALL( glGetBooleanv ) },
|
||||
{ GL_CALL( glGetDoublev ) },
|
||||
{ GL_CALL( glGetFloatv ) },
|
||||
{ GL_CALL( glGetIntegerv ) },
|
||||
{ GL_CALL( glGetError ) },
|
||||
{ GL_CALL( glGetString ) },
|
||||
{ GL_CALL( glFinish ) },
|
||||
{ GL_CALL( glFlush ) },
|
||||
{ GL_CALL( glClearDepth ) },
|
||||
{ GL_CALL( glDepthFunc ) },
|
||||
{ GL_CALL( glDepthMask ) },
|
||||
{ GL_CALL( glDepthRange ) },
|
||||
{ GL_CALL( glFrontFace ) },
|
||||
{ GL_CALL( glDrawElements ) },
|
||||
{ GL_CALL( glDrawArrays ) },
|
||||
{ GL_CALL( glColorMask ) },
|
||||
{ GL_CALL( glIndexPointer ) },
|
||||
{ GL_CALL( glVertexPointer ) },
|
||||
{ GL_CALL( glNormalPointer ) },
|
||||
{ GL_CALL( glColorPointer ) },
|
||||
{ GL_CALL( glTexCoordPointer ) },
|
||||
{ GL_CALL( glArrayElement ) },
|
||||
{ GL_CALL( glColor3f ) },
|
||||
{ GL_CALL( glColor3fv ) },
|
||||
{ GL_CALL( glColor4f ) },
|
||||
{ GL_CALL( glColor4fv ) },
|
||||
{ GL_CALL( glColor3ub ) },
|
||||
{ GL_CALL( glColor4ub ) },
|
||||
{ GL_CALL( glColor4ubv ) },
|
||||
{ GL_CALL( glTexCoord1f ) },
|
||||
{ GL_CALL( glTexCoord2f ) },
|
||||
{ GL_CALL( glTexCoord3f ) },
|
||||
{ GL_CALL( glTexCoord4f ) },
|
||||
{ GL_CALL( glTexCoord1fv ) },
|
||||
{ GL_CALL( glTexCoord2fv ) },
|
||||
{ GL_CALL( glTexCoord3fv ) },
|
||||
{ GL_CALL( glTexCoord4fv ) },
|
||||
{ GL_CALL( glTexGenf ) },
|
||||
{ GL_CALL( glTexGenfv ) },
|
||||
{ GL_CALL( glTexGeni ) },
|
||||
{ GL_CALL( glVertex2f ) },
|
||||
{ GL_CALL( glVertex3f ) },
|
||||
{ GL_CALL( glVertex3fv ) },
|
||||
{ GL_CALL( glNormal3f ) },
|
||||
{ GL_CALL( glNormal3fv ) },
|
||||
{ GL_CALL( glBegin ) },
|
||||
{ GL_CALL( glEnd ) },
|
||||
{ GL_CALL( glLineWidth ) },
|
||||
{ GL_CALL( glPointSize ) },
|
||||
{ GL_CALL( glMatrixMode ) },
|
||||
{ GL_CALL( glOrtho ) },
|
||||
{ GL_CALL( glRasterPos2f ) },
|
||||
{ GL_CALL( glFrustum ) },
|
||||
{ GL_CALL( glViewport ) },
|
||||
{ GL_CALL( glPushMatrix ) },
|
||||
{ GL_CALL( glPopMatrix ) },
|
||||
{ GL_CALL( glPushAttrib ) },
|
||||
{ GL_CALL( glPopAttrib ) },
|
||||
{ GL_CALL( glLoadIdentity ) },
|
||||
{ GL_CALL( glLoadMatrixd ) },
|
||||
{ GL_CALL( glLoadMatrixf ) },
|
||||
{ GL_CALL( glMultMatrixd ) },
|
||||
{ GL_CALL( glMultMatrixf ) },
|
||||
{ GL_CALL( glRotated ) },
|
||||
{ GL_CALL( glRotatef ) },
|
||||
{ GL_CALL( glScaled ) },
|
||||
{ GL_CALL( glScalef ) },
|
||||
{ GL_CALL( glTranslated ) },
|
||||
{ GL_CALL( glTranslatef ) },
|
||||
{ GL_CALL( glReadPixels ) },
|
||||
{ GL_CALL( glDrawPixels ) },
|
||||
{ GL_CALL( glStencilFunc ) },
|
||||
{ GL_CALL( glStencilMask ) },
|
||||
{ GL_CALL( glStencilOp ) },
|
||||
{ GL_CALL( glClearStencil ) },
|
||||
{ GL_CALL( glIsEnabled ) },
|
||||
{ GL_CALL( glIsList ) },
|
||||
{ GL_CALL( glIsTexture ) },
|
||||
{ GL_CALL( glTexEnvf ) },
|
||||
{ GL_CALL( glTexEnvfv ) },
|
||||
{ GL_CALL( glTexEnvi ) },
|
||||
{ GL_CALL( glTexParameterf ) },
|
||||
{ GL_CALL( glTexParameterfv ) },
|
||||
{ GL_CALL( glTexParameteri ) },
|
||||
{ GL_CALL( glHint ) },
|
||||
{ GL_CALL( glPixelStoref ) },
|
||||
{ GL_CALL( glPixelStorei ) },
|
||||
{ GL_CALL( glGenTextures ) },
|
||||
{ GL_CALL( glDeleteTextures ) },
|
||||
{ GL_CALL( glBindTexture ) },
|
||||
{ GL_CALL( glTexImage1D ) },
|
||||
{ GL_CALL( glTexImage2D ) },
|
||||
{ GL_CALL( glTexSubImage1D ) },
|
||||
{ GL_CALL( glTexSubImage2D ) },
|
||||
{ GL_CALL( glCopyTexImage1D ) },
|
||||
{ GL_CALL( glCopyTexImage2D ) },
|
||||
{ GL_CALL( glCopyTexSubImage1D ) },
|
||||
{ GL_CALL( glCopyTexSubImage2D ) },
|
||||
{ GL_CALL( glScissor ) },
|
||||
{ GL_CALL( glGetTexImage ) },
|
||||
{ GL_CALL( glGetTexEnviv ) },
|
||||
{ GL_CALL( glPolygonOffset ) },
|
||||
{ GL_CALL( glPolygonMode ) },
|
||||
{ GL_CALL( glPolygonStipple ) },
|
||||
{ GL_CALL( glClipPlane ) },
|
||||
{ GL_CALL( glGetClipPlane ) },
|
||||
{ GL_CALL( glShadeModel ) },
|
||||
{ GL_CALL( glGetTexLevelParameteriv ) },
|
||||
{ GL_CALL( glGetTexLevelParameterfv ) },
|
||||
{ GL_CALL( glFogfv ) },
|
||||
{ GL_CALL( glFogf ) },
|
||||
{ GL_CALL( glFogi ) },
|
||||
{ NULL , NULL }
|
||||
{ GL_CALL( glClearColor ) },
|
||||
{ GL_CALL( glClear ) },
|
||||
{ GL_CALL( glAlphaFunc ) },
|
||||
{ GL_CALL( glBlendFunc ) },
|
||||
{ GL_CALL( glCullFace ) },
|
||||
{ GL_CALL( glDrawBuffer ) },
|
||||
{ GL_CALL( glReadBuffer ) },
|
||||
{ GL_CALL( glAccum ) },
|
||||
{ GL_CALL( glEnable ) },
|
||||
{ GL_CALL( glDisable ) },
|
||||
{ GL_CALL( glEnableClientState ) },
|
||||
{ GL_CALL( glDisableClientState ) },
|
||||
{ GL_CALL( glGetBooleanv ) },
|
||||
{ GL_CALL( glGetDoublev ) },
|
||||
{ GL_CALL( glGetFloatv ) },
|
||||
{ GL_CALL( glGetIntegerv ) },
|
||||
{ GL_CALL( glGetError ) },
|
||||
{ GL_CALL( glGetString ) },
|
||||
{ GL_CALL( glFinish ) },
|
||||
{ GL_CALL( glFlush ) },
|
||||
{ GL_CALL( glClearDepth ) },
|
||||
{ GL_CALL( glDepthFunc ) },
|
||||
{ GL_CALL( glDepthMask ) },
|
||||
{ GL_CALL( glDepthRange ) },
|
||||
{ GL_CALL( glFrontFace ) },
|
||||
{ GL_CALL( glDrawElements ) },
|
||||
{ GL_CALL( glDrawArrays ) },
|
||||
{ GL_CALL( glColorMask ) },
|
||||
{ GL_CALL( glIndexPointer ) },
|
||||
{ GL_CALL( glVertexPointer ) },
|
||||
{ GL_CALL( glNormalPointer ) },
|
||||
{ GL_CALL( glColorPointer ) },
|
||||
{ GL_CALL( glTexCoordPointer ) },
|
||||
{ GL_CALL( glArrayElement ) },
|
||||
{ GL_CALL( glColor3f ) },
|
||||
{ GL_CALL( glColor3fv ) },
|
||||
{ GL_CALL( glColor4f ) },
|
||||
{ GL_CALL( glColor4fv ) },
|
||||
{ GL_CALL( glColor3ub ) },
|
||||
{ GL_CALL( glColor4ub ) },
|
||||
{ GL_CALL( glColor4ubv ) },
|
||||
{ GL_CALL( glTexCoord1f ) },
|
||||
{ GL_CALL( glTexCoord2f ) },
|
||||
{ GL_CALL( glTexCoord3f ) },
|
||||
{ GL_CALL( glTexCoord4f ) },
|
||||
{ GL_CALL( glTexCoord1fv ) },
|
||||
{ GL_CALL( glTexCoord2fv ) },
|
||||
{ GL_CALL( glTexCoord3fv ) },
|
||||
{ GL_CALL( glTexCoord4fv ) },
|
||||
{ GL_CALL( glTexGenf ) },
|
||||
{ GL_CALL( glTexGenfv ) },
|
||||
{ GL_CALL( glTexGeni ) },
|
||||
{ GL_CALL( glVertex2f ) },
|
||||
{ GL_CALL( glVertex3f ) },
|
||||
{ GL_CALL( glVertex3fv ) },
|
||||
{ GL_CALL( glNormal3f ) },
|
||||
{ GL_CALL( glNormal3fv ) },
|
||||
{ GL_CALL( glBegin ) },
|
||||
{ GL_CALL( glEnd ) },
|
||||
{ GL_CALL( glLineWidth ) },
|
||||
{ GL_CALL( glPointSize ) },
|
||||
{ GL_CALL( glMatrixMode ) },
|
||||
{ GL_CALL( glOrtho ) },
|
||||
{ GL_CALL( glRasterPos2f ) },
|
||||
{ GL_CALL( glFrustum ) },
|
||||
{ GL_CALL( glViewport ) },
|
||||
{ GL_CALL( glPushMatrix ) },
|
||||
{ GL_CALL( glPopMatrix ) },
|
||||
{ GL_CALL( glPushAttrib ) },
|
||||
{ GL_CALL( glPopAttrib ) },
|
||||
{ GL_CALL( glLoadIdentity ) },
|
||||
{ GL_CALL( glLoadMatrixd ) },
|
||||
{ GL_CALL( glLoadMatrixf ) },
|
||||
{ GL_CALL( glMultMatrixd ) },
|
||||
{ GL_CALL( glMultMatrixf ) },
|
||||
{ GL_CALL( glRotated ) },
|
||||
{ GL_CALL( glRotatef ) },
|
||||
{ GL_CALL( glScaled ) },
|
||||
{ GL_CALL( glScalef ) },
|
||||
{ GL_CALL( glTranslated ) },
|
||||
{ GL_CALL( glTranslatef ) },
|
||||
{ GL_CALL( glReadPixels ) },
|
||||
{ GL_CALL( glDrawPixels ) },
|
||||
{ GL_CALL( glStencilFunc ) },
|
||||
{ GL_CALL( glStencilMask ) },
|
||||
{ GL_CALL( glStencilOp ) },
|
||||
{ GL_CALL( glClearStencil ) },
|
||||
{ GL_CALL( glIsEnabled ) },
|
||||
{ GL_CALL( glIsList ) },
|
||||
{ GL_CALL( glIsTexture ) },
|
||||
{ GL_CALL( glTexEnvf ) },
|
||||
{ GL_CALL( glTexEnvfv ) },
|
||||
{ GL_CALL( glTexEnvi ) },
|
||||
{ GL_CALL( glTexParameterf ) },
|
||||
{ GL_CALL( glTexParameterfv ) },
|
||||
{ GL_CALL( glTexParameteri ) },
|
||||
{ GL_CALL( glHint ) },
|
||||
{ GL_CALL( glPixelStoref ) },
|
||||
{ GL_CALL( glPixelStorei ) },
|
||||
{ GL_CALL( glGenTextures ) },
|
||||
{ GL_CALL( glDeleteTextures ) },
|
||||
{ GL_CALL( glBindTexture ) },
|
||||
{ GL_CALL( glTexImage1D ) },
|
||||
{ GL_CALL( glTexImage2D ) },
|
||||
{ GL_CALL( glTexSubImage1D ) },
|
||||
{ GL_CALL( glTexSubImage2D ) },
|
||||
{ GL_CALL( glCopyTexImage1D ) },
|
||||
{ GL_CALL( glCopyTexImage2D ) },
|
||||
{ GL_CALL( glCopyTexSubImage1D ) },
|
||||
{ GL_CALL( glCopyTexSubImage2D ) },
|
||||
{ GL_CALL( glScissor ) },
|
||||
{ GL_CALL( glGetTexImage ) },
|
||||
{ GL_CALL( glGetTexEnviv ) },
|
||||
{ GL_CALL( glPolygonOffset ) },
|
||||
{ GL_CALL( glPolygonMode ) },
|
||||
{ GL_CALL( glPolygonStipple ) },
|
||||
{ GL_CALL( glClipPlane ) },
|
||||
{ GL_CALL( glGetClipPlane ) },
|
||||
{ GL_CALL( glShadeModel ) },
|
||||
{ GL_CALL( glGetTexLevelParameteriv ) },
|
||||
{ GL_CALL( glGetTexLevelParameterfv ) },
|
||||
{ GL_CALL( glFogfv ) },
|
||||
{ GL_CALL( glFogf ) },
|
||||
{ GL_CALL( glFogi ) },
|
||||
};
|
||||
|
||||
static dllfunc_t debugoutputfuncs[] =
|
||||
static const dllfunc_t debugoutputfuncs[] =
|
||||
{
|
||||
{ GL_CALL( glDebugMessageControlARB ) },
|
||||
{ GL_CALL( glDebugMessageInsertARB ) },
|
||||
{ GL_CALL( glDebugMessageCallbackARB ) },
|
||||
{ GL_CALL( glGetDebugMessageLogARB ) },
|
||||
{ NULL , NULL }
|
||||
{ GL_CALL( glDebugMessageControlARB ) },
|
||||
{ GL_CALL( glDebugMessageInsertARB ) },
|
||||
{ GL_CALL( glDebugMessageCallbackARB ) },
|
||||
{ GL_CALL( glGetDebugMessageLogARB ) },
|
||||
};
|
||||
|
||||
static dllfunc_t multitexturefuncs[] =
|
||||
static const dllfunc_t multitexturefuncs[] =
|
||||
{
|
||||
{ GL_CALL( glMultiTexCoord1f ) },
|
||||
{ GL_CALL( glMultiTexCoord2f ) },
|
||||
{ GL_CALL( glMultiTexCoord3f ) },
|
||||
{ GL_CALL( glMultiTexCoord4f ) },
|
||||
{ GL_CALL( glActiveTexture ) },
|
||||
{ GL_CALL( glActiveTextureARB ) },
|
||||
{ GL_CALL( glClientActiveTexture ) },
|
||||
{ GL_CALL( glClientActiveTextureARB ) },
|
||||
{ NULL , NULL }
|
||||
{ GL_CALL( glMultiTexCoord1f ) },
|
||||
{ GL_CALL( glMultiTexCoord2f ) },
|
||||
{ GL_CALL( glMultiTexCoord3f ) },
|
||||
{ GL_CALL( glMultiTexCoord4f ) },
|
||||
{ GL_CALL( glActiveTexture ) },
|
||||
{ GL_CALL( glActiveTextureARB ) },
|
||||
{ GL_CALL( glClientActiveTexture ) },
|
||||
{ GL_CALL( glClientActiveTextureARB ) },
|
||||
};
|
||||
|
||||
static dllfunc_t texture3dextfuncs[] =
|
||||
static const dllfunc_t texture3dextfuncs[] =
|
||||
{
|
||||
{ GL_CALL( glTexImage3D ) },
|
||||
{ GL_CALL( glTexSubImage3D ) },
|
||||
{ GL_CALL( glCopyTexSubImage3D ) },
|
||||
{ NULL , NULL }
|
||||
{ GL_CALL( glTexImage3D ) },
|
||||
{ GL_CALL( glTexSubImage3D ) },
|
||||
{ GL_CALL( glCopyTexSubImage3D ) },
|
||||
};
|
||||
|
||||
static dllfunc_t texturecompressionfuncs[] =
|
||||
static const dllfunc_t texturecompressionfuncs[] =
|
||||
{
|
||||
{ GL_CALL( glCompressedTexImage3DARB ) },
|
||||
{ GL_CALL( glCompressedTexImage2DARB ) },
|
||||
{ GL_CALL( glCompressedTexImage1DARB ) },
|
||||
{ GL_CALL( glCompressedTexSubImage3DARB ) },
|
||||
{ GL_CALL( glCompressedTexSubImage2DARB ) },
|
||||
{ GL_CALL( glCompressedTexSubImage1DARB ) },
|
||||
{ GL_CALL( glGetCompressedTexImage ) },
|
||||
{ NULL , NULL }
|
||||
{ GL_CALL( glCompressedTexImage3DARB ) },
|
||||
{ GL_CALL( glCompressedTexImage2DARB ) },
|
||||
{ GL_CALL( glCompressedTexImage1DARB ) },
|
||||
{ GL_CALL( glCompressedTexSubImage3DARB ) },
|
||||
{ GL_CALL( glCompressedTexSubImage2DARB ) },
|
||||
{ GL_CALL( glCompressedTexSubImage1DARB ) },
|
||||
{ GL_CALL( glGetCompressedTexImage ) },
|
||||
};
|
||||
|
||||
static dllfunc_t vbofuncs[] =
|
||||
static const dllfunc_t vbofuncs[] =
|
||||
{
|
||||
{ GL_CALL( glBindBufferARB ) },
|
||||
{ GL_CALL( glDeleteBuffersARB ) },
|
||||
{ GL_CALL( glGenBuffersARB ) },
|
||||
{ GL_CALL( glIsBufferARB ) },
|
||||
{ GL_CALL( glBindBufferARB ) },
|
||||
{ GL_CALL( glDeleteBuffersARB ) },
|
||||
{ GL_CALL( glGenBuffersARB ) },
|
||||
{ GL_CALL( glIsBufferARB ) },
|
||||
#if !XASH_GLES
|
||||
{ GL_CALL( glMapBufferARB ) },
|
||||
{ GL_CALL( glUnmapBufferARB ) },
|
||||
{ GL_CALL( glMapBufferARB ) },
|
||||
{ GL_CALL( glUnmapBufferARB ) },
|
||||
#endif
|
||||
{ GL_CALL( glBufferDataARB ) },
|
||||
{ GL_CALL( glBufferSubDataARB ) },
|
||||
{ NULL, NULL }
|
||||
{ GL_CALL( glBufferDataARB ) },
|
||||
{ GL_CALL( glBufferSubDataARB ) },
|
||||
};
|
||||
|
||||
static dllfunc_t multisampletexfuncs[] =
|
||||
static const dllfunc_t multisampletexfuncs[] =
|
||||
{
|
||||
{ GL_CALL(glTexImage2DMultisample) },
|
||||
{ NULL, NULL }
|
||||
{ GL_CALL(glTexImage2DMultisample) },
|
||||
};
|
||||
|
||||
static dllfunc_t drawrangeelementsfuncs[] =
|
||||
static const dllfunc_t drawrangeelementsfuncs[] =
|
||||
{
|
||||
{ GL_CALL( glDrawRangeElements ) },
|
||||
{ NULL, NULL }
|
||||
};
|
||||
|
||||
static dllfunc_t drawrangeelementsextfuncs[] =
|
||||
static const dllfunc_t drawrangeelementsextfuncs[] =
|
||||
{
|
||||
{ GL_CALL( glDrawRangeElementsEXT ) },
|
||||
{ NULL, NULL }
|
||||
};
|
||||
|
||||
|
||||
@@ -263,85 +253,80 @@ static dllfunc_t drawrangeelementsextfuncs[] =
|
||||
// still need resolve some ext dynamicly, and mangling beginend wrappers will help only with LTO
|
||||
// anyway this will not work with gl-wes/nanogl, we do not link to libGLESv2, so skip this now
|
||||
#if !XASH_GL_STATIC
|
||||
static dllfunc_t mapbufferrangefuncs[] =
|
||||
static const dllfunc_t mapbufferrangefuncs[] =
|
||||
{
|
||||
{ GL_CALL( glMapBufferRange ) },
|
||||
{ GL_CALL( glFlushMappedBufferRange ) },
|
||||
#if XASH_GLES
|
||||
{ GL_CALL( glUnmapBufferARB ) },
|
||||
#endif
|
||||
{ NULL, NULL }
|
||||
};
|
||||
|
||||
static dllfunc_t drawrangeelementsbasevertexfuncs[] =
|
||||
static const dllfunc_t drawrangeelementsbasevertexfuncs[] =
|
||||
{
|
||||
{ GL_CALL( glDrawRangeElementsBaseVertex ) },
|
||||
{ NULL, NULL }
|
||||
};
|
||||
|
||||
static dllfunc_t bufferstoragefuncs[] =
|
||||
static const dllfunc_t bufferstoragefuncs[] =
|
||||
{
|
||||
{ GL_CALL( glBufferStorage ) },
|
||||
{ NULL, NULL }
|
||||
};
|
||||
|
||||
static dllfunc_t shaderobjectsfuncs[] =
|
||||
static const dllfunc_t shaderobjectsfuncs[] =
|
||||
{
|
||||
{ GL_CALL( glDeleteObjectARB ) },
|
||||
{ GL_CALL( glGetHandleARB ) },
|
||||
{ GL_CALL( glDetachObjectARB ) },
|
||||
{ GL_CALL( glCreateShaderObjectARB ) },
|
||||
{ GL_CALL( glShaderSourceARB ) },
|
||||
{ GL_CALL( glCompileShaderARB ) },
|
||||
{ GL_CALL( glCreateProgramObjectARB ) },
|
||||
{ GL_CALL( glAttachObjectARB ) },
|
||||
{ GL_CALL( glLinkProgramARB ) },
|
||||
{ GL_CALL( glUseProgramObjectARB ) },
|
||||
{ GL_CALL( glValidateProgramARB ) },
|
||||
{ GL_CALL( glUniform1fARB ) },
|
||||
{ GL_CALL( glUniform2fARB ) },
|
||||
{ GL_CALL( glUniform3fARB ) },
|
||||
{ GL_CALL( glUniform4fARB ) },
|
||||
{ GL_CALL( glUniform1iARB ) },
|
||||
{ GL_CALL( glUniform2iARB ) },
|
||||
{ GL_CALL( glUniform3iARB ) },
|
||||
{ GL_CALL( glUniform4iARB ) },
|
||||
{ GL_CALL( glUniform1fvARB ) },
|
||||
{ GL_CALL( glUniform2fvARB ) },
|
||||
{ GL_CALL( glUniform3fvARB ) },
|
||||
{ GL_CALL( glUniform4fvARB ) },
|
||||
{ GL_CALL( glUniform1ivARB ) },
|
||||
{ GL_CALL( glUniform2ivARB ) },
|
||||
{ GL_CALL( glUniform3ivARB ) },
|
||||
{ GL_CALL( glUniform4ivARB ) },
|
||||
{ GL_CALL( glUniformMatrix2fvARB ) },
|
||||
{ GL_CALL( glUniformMatrix3fvARB ) },
|
||||
{ GL_CALL( glUniformMatrix4fvARB ) },
|
||||
{ GL_CALL( glGetObjectParameterfvARB ) },
|
||||
{ GL_CALL( glGetObjectParameterivARB ) },
|
||||
{ GL_CALL( glGetInfoLogARB ) },
|
||||
{ GL_CALL( glGetAttachedObjectsARB ) },
|
||||
{ GL_CALL( glGetUniformLocationARB ) },
|
||||
{ GL_CALL( glGetActiveUniformARB ) },
|
||||
{ GL_CALL( glGetUniformfvARB ) },
|
||||
{ GL_CALL( glGetUniformivARB ) },
|
||||
{ GL_CALL( glGetShaderSourceARB ) },
|
||||
{ GL_CALL( glVertexAttribPointerARB ) },
|
||||
{ GL_CALL( glEnableVertexAttribArrayARB ) },
|
||||
{ GL_CALL( glDisableVertexAttribArrayARB ) },
|
||||
{ GL_CALL( glBindAttribLocationARB ) },
|
||||
{ GL_CALL( glGetActiveAttribARB ) },
|
||||
{ GL_CALL( glGetAttribLocationARB ) },
|
||||
{ GL_CALL( glVertexAttrib2fARB ) },
|
||||
{ GL_CALL( glVertexAttrib2fvARB ) },
|
||||
// { GL_CALL( glVertexAttrib3fv ) },
|
||||
// { GL_CALL( glVertexAttrib4f ) },
|
||||
// { GL_CALL( glVertexAttrib4fv ) },
|
||||
// { GL_CALL( glVertexAttrib4ubv ) },
|
||||
{ NULL, NULL }
|
||||
{ GL_CALL( glDeleteObjectARB ) },
|
||||
{ GL_CALL( glGetHandleARB ) },
|
||||
{ GL_CALL( glDetachObjectARB ) },
|
||||
{ GL_CALL( glCreateShaderObjectARB ) },
|
||||
{ GL_CALL( glShaderSourceARB ) },
|
||||
{ GL_CALL( glCompileShaderARB ) },
|
||||
{ GL_CALL( glCreateProgramObjectARB ) },
|
||||
{ GL_CALL( glAttachObjectARB ) },
|
||||
{ GL_CALL( glLinkProgramARB ) },
|
||||
{ GL_CALL( glUseProgramObjectARB ) },
|
||||
{ GL_CALL( glValidateProgramARB ) },
|
||||
{ GL_CALL( glUniform1fARB ) },
|
||||
{ GL_CALL( glUniform2fARB ) },
|
||||
{ GL_CALL( glUniform3fARB ) },
|
||||
{ GL_CALL( glUniform4fARB ) },
|
||||
{ GL_CALL( glUniform1iARB ) },
|
||||
{ GL_CALL( glUniform2iARB ) },
|
||||
{ GL_CALL( glUniform3iARB ) },
|
||||
{ GL_CALL( glUniform4iARB ) },
|
||||
{ GL_CALL( glUniform1fvARB ) },
|
||||
{ GL_CALL( glUniform2fvARB ) },
|
||||
{ GL_CALL( glUniform3fvARB ) },
|
||||
{ GL_CALL( glUniform4fvARB ) },
|
||||
{ GL_CALL( glUniform1ivARB ) },
|
||||
{ GL_CALL( glUniform2ivARB ) },
|
||||
{ GL_CALL( glUniform3ivARB ) },
|
||||
{ GL_CALL( glUniform4ivARB ) },
|
||||
{ GL_CALL( glUniformMatrix2fvARB ) },
|
||||
{ GL_CALL( glUniformMatrix3fvARB ) },
|
||||
{ GL_CALL( glUniformMatrix4fvARB ) },
|
||||
{ GL_CALL( glGetObjectParameterfvARB ) },
|
||||
{ GL_CALL( glGetObjectParameterivARB ) },
|
||||
{ GL_CALL( glGetInfoLogARB ) },
|
||||
{ GL_CALL( glGetAttachedObjectsARB ) },
|
||||
{ GL_CALL( glGetUniformLocationARB ) },
|
||||
{ GL_CALL( glGetActiveUniformARB ) },
|
||||
{ GL_CALL( glGetUniformfvARB ) },
|
||||
{ GL_CALL( glGetUniformivARB ) },
|
||||
{ GL_CALL( glGetShaderSourceARB ) },
|
||||
{ GL_CALL( glVertexAttribPointerARB ) },
|
||||
{ GL_CALL( glEnableVertexAttribArrayARB ) },
|
||||
{ GL_CALL( glDisableVertexAttribArrayARB ) },
|
||||
{ GL_CALL( glBindAttribLocationARB ) },
|
||||
{ GL_CALL( glGetActiveAttribARB ) },
|
||||
{ GL_CALL( glGetAttribLocationARB ) },
|
||||
{ GL_CALL( glVertexAttrib2fARB ) },
|
||||
{ GL_CALL( glVertexAttrib2fvARB ) },
|
||||
//{ GL_CALL( glVertexAttrib3fv ) },
|
||||
//{ GL_CALL( glVertexAttrib4f ) },
|
||||
//{ GL_CALL( glVertexAttrib4fv ) },
|
||||
//{ GL_CALL( glVertexAttrib4ubv ) },
|
||||
};
|
||||
|
||||
|
||||
/*
|
||||
==================
|
||||
Even if *ARB functions may work in GL driver in Core context,
|
||||
@@ -354,90 +339,86 @@ Commented out lines left there intentionally to prevent usage on core/gles
|
||||
==================
|
||||
*/
|
||||
|
||||
static dllfunc_t shaderobjectsfuncs_gles[] =
|
||||
static const dllfunc_t shaderobjectsfuncs_gles[] =
|
||||
{
|
||||
{ "glDeleteShader" , (void **)&pglDeleteObjectARB },
|
||||
//{ "glGetHandleARB" , (void **)&pglGetHandleARB },
|
||||
{ "glDetachShader" , (void **)&pglDetachObjectARB },
|
||||
{ "glCreateShader" , (void **)&pglCreateShaderObjectARB },
|
||||
{ "glShaderSource" , (void **)&pglShaderSourceARB },
|
||||
{ "glCompileShader" , (void **)&pglCompileShaderARB },
|
||||
{ "glCreateProgram" , (void **)&pglCreateProgramObjectARB },
|
||||
{ "glAttachShader" , (void **)&pglAttachObjectARB },
|
||||
{ "glLinkProgram" , (void **)&pglLinkProgramARB },
|
||||
{ "glUseProgram" , (void **)&pglUseProgramObjectARB },
|
||||
{ "glValidateProgram" , (void **)&pglValidateProgramARB },
|
||||
{ "glUniform1f" , (void **)&pglUniform1fARB },
|
||||
{ "glUniform2f" , (void **)&pglUniform2fARB },
|
||||
{ "glUniform3f" , (void **)&pglUniform3fARB },
|
||||
{ "glUniform4f" , (void **)&pglUniform4fARB },
|
||||
{ "glUniform1i" , (void **)&pglUniform1iARB },
|
||||
{ "glUniform2i" , (void **)&pglUniform2iARB },
|
||||
{ "glUniform3i" , (void **)&pglUniform3iARB },
|
||||
{ "glUniform4i" , (void **)&pglUniform4iARB },
|
||||
{ "glUniform1f" , (void **)&pglUniform1fvARB },
|
||||
{ "glUniform2fv" , (void **)&pglUniform2fvARB },
|
||||
{ "glUniform3fv" , (void **)&pglUniform3fvARB },
|
||||
{ "glUniform4fv" , (void **)&pglUniform4fvARB },
|
||||
{ "glUniform1iv" , (void **)&pglUniform1ivARB },
|
||||
{ "glUniform2iv" , (void **)&pglUniform2ivARB },
|
||||
{ "glUniform3iv" , (void **)&pglUniform3ivARB },
|
||||
{ "glUniform4iv" , (void **)&pglUniform4ivARB },
|
||||
{ "glUniformMatrix2fv" , (void **)&pglUniformMatrix2fvARB },
|
||||
{ "glUniformMatrix3fv" , (void **)&pglUniformMatrix3fvARB },
|
||||
{ "glUniformMatrix4fv" , (void **)&pglUniformMatrix4fvARB },
|
||||
// { "glGetShaderfv" , (void **)&pglGetObjectParameterfvARB }, // missing in ES2?
|
||||
{ "glGetShaderiv" , (void **)&pglGetObjectParameterivARB },
|
||||
{ "glGetShaderInfoLog" , (void **)&pglGetInfoLogARB },
|
||||
// { "glGetAttachedObjects" , (void **)&pglGetAttachedObjectsARB }, // missing in ES2?
|
||||
{ "glGetUniformLocation" , (void **)&pglGetUniformLocationARB },
|
||||
{ "glGetActiveUniform" , (void **)&pglGetActiveUniformARB },
|
||||
{ "glGetUniformfv" , (void **)&pglGetUniformfvARB },
|
||||
{ "glGetUniformiv" , (void **)&pglGetUniformivARB },
|
||||
{ "glGetShaderSource" , (void **)&pglGetShaderSourceARB },
|
||||
{ "glVertexAttribPointer" , (void **)&pglVertexAttribPointerARB },
|
||||
{ "glEnableVertexAttribArray" , (void **)&pglEnableVertexAttribArrayARB },
|
||||
{ "glDisableVertexAttribArray" , (void **)&pglDisableVertexAttribArrayARB },
|
||||
{ "glBindAttribLocation" , (void **)&pglBindAttribLocationARB },
|
||||
{ "glGetActiveAttrib" , (void **)&pglGetActiveAttribARB },
|
||||
{ "glGetAttribLocation" , (void **)&pglGetAttribLocationARB },
|
||||
{ "glVertexAttrib2f" , (void **)&pglVertexAttrib2fARB },
|
||||
{ "glVertexAttrib2fv" , (void **)&pglVertexAttrib2fvARB },
|
||||
{ "glVertexAttrib3fv" , (void **)&pglVertexAttrib3fvARB },
|
||||
{ "glDeleteShader" , (void **)&pglDeleteObjectARB },
|
||||
//{ "glGetHandleARB" , (void **)&pglGetHandleARB },
|
||||
{ "glDetachShader" , (void **)&pglDetachObjectARB },
|
||||
{ "glCreateShader" , (void **)&pglCreateShaderObjectARB },
|
||||
{ "glShaderSource" , (void **)&pglShaderSourceARB },
|
||||
{ "glCompileShader" , (void **)&pglCompileShaderARB },
|
||||
{ "glCreateProgram" , (void **)&pglCreateProgramObjectARB },
|
||||
{ "glAttachShader" , (void **)&pglAttachObjectARB },
|
||||
{ "glLinkProgram" , (void **)&pglLinkProgramARB },
|
||||
{ "glUseProgram" , (void **)&pglUseProgramObjectARB },
|
||||
{ "glValidateProgram" , (void **)&pglValidateProgramARB },
|
||||
{ "glUniform1f" , (void **)&pglUniform1fARB },
|
||||
{ "glUniform2f" , (void **)&pglUniform2fARB },
|
||||
{ "glUniform3f" , (void **)&pglUniform3fARB },
|
||||
{ "glUniform4f" , (void **)&pglUniform4fARB },
|
||||
{ "glUniform1i" , (void **)&pglUniform1iARB },
|
||||
{ "glUniform2i" , (void **)&pglUniform2iARB },
|
||||
{ "glUniform3i" , (void **)&pglUniform3iARB },
|
||||
{ "glUniform4i" , (void **)&pglUniform4iARB },
|
||||
{ "glUniform1f" , (void **)&pglUniform1fvARB },
|
||||
{ "glUniform2fv" , (void **)&pglUniform2fvARB },
|
||||
{ "glUniform3fv" , (void **)&pglUniform3fvARB },
|
||||
{ "glUniform4fv" , (void **)&pglUniform4fvARB },
|
||||
{ "glUniform1iv" , (void **)&pglUniform1ivARB },
|
||||
{ "glUniform2iv" , (void **)&pglUniform2ivARB },
|
||||
{ "glUniform3iv" , (void **)&pglUniform3ivARB },
|
||||
{ "glUniform4iv" , (void **)&pglUniform4ivARB },
|
||||
{ "glUniformMatrix2fv" , (void **)&pglUniformMatrix2fvARB },
|
||||
{ "glUniformMatrix3fv" , (void **)&pglUniformMatrix3fvARB },
|
||||
{ "glUniformMatrix4fv" , (void **)&pglUniformMatrix4fvARB },
|
||||
//{ "glGetShaderfv" , (void **)&pglGetObjectParameterfvARB }, // missing in ES2?
|
||||
{ "glGetShaderiv" , (void **)&pglGetObjectParameterivARB },
|
||||
{ "glGetShaderInfoLog" , (void **)&pglGetInfoLogARB },
|
||||
//{ "glGetAttachedObjects" , (void **)&pglGetAttachedObjectsARB }, // missing in ES2?
|
||||
{ "glGetUniformLocation" , (void **)&pglGetUniformLocationARB },
|
||||
{ "glGetActiveUniform" , (void **)&pglGetActiveUniformARB },
|
||||
{ "glGetUniformfv" , (void **)&pglGetUniformfvARB },
|
||||
{ "glGetUniformiv" , (void **)&pglGetUniformivARB },
|
||||
{ "glGetShaderSource" , (void **)&pglGetShaderSourceARB },
|
||||
{ "glVertexAttribPointer" , (void **)&pglVertexAttribPointerARB },
|
||||
{ "glEnableVertexAttribArray" , (void **)&pglEnableVertexAttribArrayARB },
|
||||
{ "glDisableVertexAttribArray" , (void **)&pglDisableVertexAttribArrayARB },
|
||||
{ "glBindAttribLocation" , (void **)&pglBindAttribLocationARB },
|
||||
{ "glGetActiveAttrib" , (void **)&pglGetActiveAttribARB },
|
||||
{ "glGetAttribLocation" , (void **)&pglGetAttribLocationARB },
|
||||
{ "glVertexAttrib2f" , (void **)&pglVertexAttrib2fARB },
|
||||
{ "glVertexAttrib2fv" , (void **)&pglVertexAttrib2fvARB },
|
||||
{ "glVertexAttrib3fv" , (void **)&pglVertexAttrib3fvARB },
|
||||
|
||||
// Core/GLES only
|
||||
{ GL_CALL( glGetProgramiv ) },
|
||||
{ GL_CALL( glDeleteProgram ) },
|
||||
{ GL_CALL( glGetProgramInfoLog ) },
|
||||
//{ "glVertexAttrib4f" , (void **)&pglVertexAttrib4fARB },
|
||||
//{ "glVertexAttrib4fv" , (void **)&pglVertexAttrib4fvARB },
|
||||
//{ "glVertexAttrib4ubv" , (void **)&pglVertexAttrib4ubvARB },
|
||||
{ NULL, NULL }
|
||||
// Core/GLES only
|
||||
{ GL_CALL( glGetProgramiv ) },
|
||||
{ GL_CALL( glDeleteProgram ) },
|
||||
{ GL_CALL( glGetProgramInfoLog ) },
|
||||
//{ "glVertexAttrib4f" , (void **)&pglVertexAttrib4fARB },
|
||||
//{ "glVertexAttrib4fv" , (void **)&pglVertexAttrib4fvARB },
|
||||
//{ "glVertexAttrib4ubv" , (void **)&pglVertexAttrib4ubvARB },
|
||||
};
|
||||
|
||||
static dllfunc_t vaofuncs[] =
|
||||
static const dllfunc_t vaofuncs[] =
|
||||
{
|
||||
{ GL_CALL( glBindVertexArray ) },
|
||||
{ GL_CALL( glDeleteVertexArrays ) },
|
||||
{ GL_CALL( glGenVertexArrays ) },
|
||||
{ GL_CALL( glIsVertexArray ) },
|
||||
{ NULL, NULL }
|
||||
{ GL_CALL( glBindVertexArray ) },
|
||||
{ GL_CALL( glDeleteVertexArrays ) },
|
||||
{ GL_CALL( glGenVertexArrays ) },
|
||||
{ GL_CALL( glIsVertexArray ) },
|
||||
};
|
||||
|
||||
static dllfunc_t multitexturefuncs_es[] =
|
||||
static const dllfunc_t multitexturefuncs_es[] =
|
||||
{
|
||||
{ GL_CALL( glActiveTexture ) },
|
||||
{ GL_CALL( glActiveTextureARB ) },
|
||||
{ GL_CALL( glClientActiveTexture ) },
|
||||
{ GL_CALL( glClientActiveTextureARB ) },
|
||||
{ NULL , NULL }
|
||||
{ GL_CALL( glActiveTexture ) },
|
||||
{ GL_CALL( glActiveTextureARB ) },
|
||||
{ GL_CALL( glClientActiveTexture ) },
|
||||
{ GL_CALL( glClientActiveTextureARB ) },
|
||||
};
|
||||
|
||||
static dllfunc_t multitexturefuncs_es2[] =
|
||||
static const dllfunc_t multitexturefuncs_es2[] =
|
||||
{
|
||||
{ GL_CALL( glActiveTexture ) },
|
||||
{ GL_CALL( glActiveTextureARB ) },
|
||||
{ NULL , NULL }
|
||||
{ GL_CALL( glActiveTexture ) },
|
||||
{ GL_CALL( glActiveTextureARB ) },
|
||||
};
|
||||
|
||||
#endif // !XASH_GL_STATIC
|
||||
@@ -476,7 +457,7 @@ static void APIENTRY GL_DebugOutput( GLuint source, GLuint type, GLuint id, GLui
|
||||
GL_SetExtension
|
||||
=================
|
||||
*/
|
||||
void GL_SetExtension( int r_ext, int enable )
|
||||
static void GL_SetExtension( int r_ext, int enable )
|
||||
{
|
||||
if( r_ext >= 0 && r_ext < GL_EXTCOUNT )
|
||||
glConfig.extension[r_ext] = enable ? GL_TRUE : GL_FALSE;
|
||||
@@ -514,12 +495,12 @@ int GL_MaxTextureUnits( void )
|
||||
GL_CheckExtension
|
||||
=================
|
||||
*/
|
||||
qboolean GL_CheckExtension( const char *name, const dllfunc_t *funcs, const char *cvarname, int r_ext, float minver )
|
||||
static qboolean GL_CheckExtension( const char *name, const dllfunc_t *funcs, size_t num_funcs, const char *cvarname, int r_ext, float minver )
|
||||
{
|
||||
const dllfunc_t *func;
|
||||
cvar_t *parm = NULL;
|
||||
const char *extensions_string;
|
||||
char desc[MAX_VA_STRING];
|
||||
size_t i;
|
||||
cvar_t *parm = NULL;
|
||||
const char *extensions_string;
|
||||
char desc[MAX_VA_STRING];
|
||||
float glver = (float)glConfig.version_major + glConfig.version_minor / 10.0f;
|
||||
|
||||
gEngfuncs.Con_Reportf( "%s: %s ", __func__, name );
|
||||
@@ -550,17 +531,16 @@ qboolean GL_CheckExtension( const char *name, const dllfunc_t *funcs, const char
|
||||
|
||||
#if !XASH_GL_STATIC
|
||||
// clear exports
|
||||
for( func = funcs; func && func->name; func++ )
|
||||
*func->func = NULL;
|
||||
ClearExports( funcs, num_funcs );
|
||||
|
||||
for( func = funcs; func && func->name; func++ )
|
||||
for( i = 0; i < num_funcs; i++ )
|
||||
{
|
||||
// functions are cleared before all the extensions are evaluated
|
||||
if(( *func->func = (void *)gEngfuncs.GL_GetProcAddress( func->name )) == NULL )
|
||||
if(( *(funcs[i].func) = (void *)gEngfuncs.GL_GetProcAddress( funcs[i].name )) == NULL )
|
||||
{
|
||||
string name;
|
||||
char *end;
|
||||
size_t i = 0;
|
||||
size_t j = 0;
|
||||
#if XASH_GLES
|
||||
const char *suffixes[] = { "", "EXT", "OES" };
|
||||
#else
|
||||
@@ -568,7 +548,7 @@ qboolean GL_CheckExtension( const char *name, const dllfunc_t *funcs, const char
|
||||
#endif
|
||||
|
||||
// HACK: fix ARB names
|
||||
Q_strncpy( name, func->name, sizeof( name ));
|
||||
Q_strncpy( name, funcs[i].name, sizeof( name ));
|
||||
if(( end = Q_strstr( name, "ARB" )))
|
||||
{
|
||||
*end = '\0';
|
||||
@@ -576,21 +556,21 @@ qboolean GL_CheckExtension( const char *name, const dllfunc_t *funcs, const char
|
||||
else // I need Q_strstrnul
|
||||
{
|
||||
end = name + Q_strlen( name );
|
||||
i++; // skip empty suffix
|
||||
j++; // skip empty suffix
|
||||
}
|
||||
|
||||
for( ; i < sizeof( suffixes ) / sizeof( suffixes[0] ); i++ )
|
||||
for( ; j < sizeof( suffixes ) / sizeof( suffixes[0] ); j++ )
|
||||
{
|
||||
void *f;
|
||||
|
||||
Q_strncat( name, suffixes[i], sizeof( name ));
|
||||
Q_strncat( name, suffixes[j], sizeof( name ));
|
||||
|
||||
if(( f = gEngfuncs.GL_GetProcAddress( name )))
|
||||
{
|
||||
// GL_GetProcAddress prints errors about missing functions, so tell user that we found it with different name
|
||||
gEngfuncs.Con_Printf( S_NOTE "found %s\n", name );
|
||||
|
||||
*func->func = f;
|
||||
*(funcs[i].func) = f;
|
||||
break;
|
||||
}
|
||||
else
|
||||
@@ -600,7 +580,7 @@ qboolean GL_CheckExtension( const char *name, const dllfunc_t *funcs, const char
|
||||
}
|
||||
|
||||
// not found...
|
||||
if( i == sizeof( suffixes ) / sizeof( suffixes[0] ))
|
||||
if( j == sizeof( suffixes ) / sizeof( suffixes[0] ))
|
||||
{
|
||||
GL_SetExtension( r_ext, false );
|
||||
}
|
||||
@@ -798,14 +778,14 @@ static void GL_InitExtensionsGLES( void )
|
||||
switch( extid )
|
||||
{
|
||||
case GL_ARB_VERTEX_BUFFER_OBJECT_EXT:
|
||||
GL_CheckExtension( "vertex_buffer_object", vbofuncs, "gl_vertex_buffer_object", extid, 1.0 );
|
||||
GL_CheckExtension( "vertex_buffer_object", vbofuncs, ARRAYSIZE( vbofuncs ), "gl_vertex_buffer_object", extid, 1.0 );
|
||||
break;
|
||||
case GL_ARB_MULTITEXTURE:
|
||||
if( !GL_CheckExtension( "multitexture", multitexturefuncs, "gl_arb_multitexture", GL_ARB_MULTITEXTURE, 1.0 ) && glConfig.wrapper == GLES_WRAPPER_NONE )
|
||||
if( !GL_CheckExtension( "multitexture", multitexturefuncs, ARRAYSIZE( multitexturefuncs ), "gl_arb_multitexture", GL_ARB_MULTITEXTURE, 1.0 ) && glConfig.wrapper == GLES_WRAPPER_NONE )
|
||||
{
|
||||
#if !XASH_GL_STATIC
|
||||
if( !GL_CheckExtension( "multitexture_es1", multitexturefuncs_es, "gl_arb_multitexture", GL_ARB_MULTITEXTURE, 1.0 )
|
||||
&& !GL_CheckExtension( "multitexture_es2", multitexturefuncs_es2, "gl_arb_multitexture", GL_ARB_MULTITEXTURE, 2.0 ))
|
||||
if( !GL_CheckExtension( "multitexture_es1", multitexturefuncs_es, ARRAYSIZE( multitexturefuncs_es ), "gl_arb_multitexture", GL_ARB_MULTITEXTURE, 1.0 )
|
||||
&& !GL_CheckExtension( "multitexture_es2", multitexturefuncs_es2, ARRAYSIZE( multitexturefuncs_es2 ), "gl_arb_multitexture", GL_ARB_MULTITEXTURE, 2.0 ))
|
||||
break;
|
||||
#endif
|
||||
}
|
||||
@@ -823,48 +803,48 @@ static void GL_InitExtensionsGLES( void )
|
||||
glConfig.max_texture_coords = glConfig.max_teximage_units = glConfig.max_texture_units;
|
||||
break;
|
||||
case GL_TEXTURE_CUBEMAP_EXT:
|
||||
if( GL_CheckExtension( "GL_OES_texture_cube_map", NULL, "gl_texture_cubemap", extid, 0 ))
|
||||
if( GL_CheckExtension( "GL_OES_texture_cube_map", NULL, 0, "gl_texture_cubemap", extid, 0 ))
|
||||
pglGetIntegerv( GL_MAX_CUBE_MAP_TEXTURE_SIZE_ARB, &glConfig.max_cubemap_size );
|
||||
break;
|
||||
case GL_ANISOTROPY_EXT:
|
||||
glConfig.max_texture_anisotropy = 0.0f;
|
||||
if( GL_CheckExtension( "GL_EXT_texture_filter_anisotropic", NULL, "gl_ext_anisotropic_filter", extid, 0 ))
|
||||
if( GL_CheckExtension( "GL_EXT_texture_filter_anisotropic", NULL, 0, "gl_ext_anisotropic_filter", extid, 0 ))
|
||||
pglGetFloatv( GL_MAX_TEXTURE_MAX_ANISOTROPY_EXT, &glConfig.max_texture_anisotropy );
|
||||
break;
|
||||
case GL_TEXTURE_LOD_BIAS:
|
||||
if( GL_CheckExtension( "GL_EXT_texture_lod_bias", NULL, "gl_texture_mipmap_biasing", extid, 0 ))
|
||||
if( GL_CheckExtension( "GL_EXT_texture_lod_bias", NULL, 0, "gl_texture_mipmap_biasing", extid, 0 ))
|
||||
pglGetFloatv( GL_MAX_TEXTURE_LOD_BIAS_EXT, &glConfig.max_texture_lod_bias );
|
||||
break;
|
||||
case GL_ARB_TEXTURE_NPOT_EXT:
|
||||
GL_CheckExtension( "GL_OES_texture_npot", NULL, "gl_texture_npot", extid, 0 );
|
||||
GL_CheckExtension( "GL_OES_texture_npot", NULL, 0, "gl_texture_npot", extid, 0 );
|
||||
break;
|
||||
#if !XASH_GL_STATIC
|
||||
case GL_SHADER_OBJECTS_EXT:
|
||||
GL_CheckExtension( "ES2 Shaders", shaderobjectsfuncs_gles, "gl_shaderobjects", extid, 2.0 );
|
||||
GL_CheckExtension( "ES2 Shaders", shaderobjectsfuncs_gles, ARRAYSIZE( shaderobjectsfuncs_gles ), "gl_shaderobjects", extid, 2.0 );
|
||||
break;
|
||||
case GL_ARB_VERTEX_ARRAY_OBJECT_EXT:
|
||||
if( !GL_CheckExtension( "GL_OES_vertex_array_object", vaofuncs, "gl_vertex_array_object", extid, 3.0 ))
|
||||
GL_CheckExtension( "GL_EXT_vertex_array_object", vaofuncs, "gl_vertex_array_object", extid, 3.0 );
|
||||
if( !GL_CheckExtension( "GL_OES_vertex_array_object", vaofuncs, ARRAYSIZE( vaofuncs ), "gl_vertex_array_object", extid, 3.0 ))
|
||||
GL_CheckExtension( "GL_EXT_vertex_array_object", vaofuncs, ARRAYSIZE( vaofuncs ), "gl_vertex_array_object", extid, 3.0 );
|
||||
break;
|
||||
case GL_DRAW_RANGEELEMENTS_EXT:
|
||||
if( !GL_CheckExtension( "GL_EXT_draw_range_elements", drawrangeelementsfuncs, "gl_drawrangeelements", extid, 3.0 ))
|
||||
GL_CheckExtension( "GL_OES_draw_range_elements", drawrangeelementsfuncs, "gl_drawrangeelements", extid, 3.0 );
|
||||
if( !GL_CheckExtension( "GL_EXT_draw_range_elements", drawrangeelementsfuncs, ARRAYSIZE( drawrangeelementsfuncs ), "gl_drawrangeelements", extid, 3.0 ))
|
||||
GL_CheckExtension( "GL_OES_draw_range_elements", drawrangeelementsfuncs, ARRAYSIZE( drawrangeelementsfuncs ), "gl_drawrangeelements", extid, 3.0 );
|
||||
break;
|
||||
case GL_DRAW_RANGE_ELEMENTS_BASE_VERTEX_EXT:
|
||||
if( !GL_CheckExtension( "GL_OES_draw_elements_base_vertex", drawrangeelementsbasevertexfuncs, "gl_drawrangeelementsbasevertex", GL_DRAW_RANGE_ELEMENTS_BASE_VERTEX_EXT, 0 ))
|
||||
GL_CheckExtension( "GL_EXT_draw_elements_base_vertex", drawrangeelementsbasevertexfuncs, "gl_drawrangeelementsbasevertex", GL_DRAW_RANGE_ELEMENTS_BASE_VERTEX_EXT, 3.2 );
|
||||
if( !GL_CheckExtension( "GL_OES_draw_elements_base_vertex", drawrangeelementsbasevertexfuncs, ARRAYSIZE( drawrangeelementsbasevertexfuncs ), "gl_drawrangeelementsbasevertex", GL_DRAW_RANGE_ELEMENTS_BASE_VERTEX_EXT, 0 ))
|
||||
GL_CheckExtension( "GL_EXT_draw_elements_base_vertex", drawrangeelementsbasevertexfuncs, ARRAYSIZE( drawrangeelementsbasevertexfuncs ), "gl_drawrangeelementsbasevertex", GL_DRAW_RANGE_ELEMENTS_BASE_VERTEX_EXT, 3.2 );
|
||||
break;
|
||||
case GL_MAP_BUFFER_RANGE_EXT:
|
||||
GL_CheckExtension( "GL_EXT_map_buffer_range", mapbufferrangefuncs, "gl_map_buffer_range", GL_MAP_BUFFER_RANGE_EXT , 3.0);
|
||||
GL_CheckExtension( "GL_EXT_map_buffer_range", mapbufferrangefuncs, ARRAYSIZE( mapbufferrangefuncs ), "gl_map_buffer_range", GL_MAP_BUFFER_RANGE_EXT , 3.0);
|
||||
break;
|
||||
case GL_BUFFER_STORAGE_EXT:
|
||||
GL_CheckExtension( "GL_EXT_buffer_storage", bufferstoragefuncs, "gl_buffer_storage", GL_BUFFER_STORAGE_EXT, 0);
|
||||
GL_CheckExtension( "GL_EXT_buffer_storage", bufferstoragefuncs, ARRAYSIZE( bufferstoragefuncs ), "gl_buffer_storage", GL_BUFFER_STORAGE_EXT, 0);
|
||||
break;
|
||||
|
||||
#endif
|
||||
case GL_DEBUG_OUTPUT:
|
||||
if( glw_state.extended )
|
||||
GL_CheckExtension( "GL_KHR_debug", debugoutputfuncs, "gl_debug_output", extid, 0 );
|
||||
GL_CheckExtension( "GL_KHR_debug", debugoutputfuncs, ARRAYSIZE( debugoutputfuncs ), "gl_debug_output", extid, 0 );
|
||||
else
|
||||
GL_SetExtension( extid, false );
|
||||
break;
|
||||
@@ -918,7 +898,7 @@ static void GL_InitExtensionsBigGL( void )
|
||||
|
||||
// multitexture
|
||||
glConfig.max_texture_units = glConfig.max_texture_coords = glConfig.max_teximage_units = 1;
|
||||
if( GL_CheckExtension( "GL_ARB_multitexture", multitexturefuncs, "gl_arb_multitexture", GL_ARB_MULTITEXTURE, 1.3f ))
|
||||
if( GL_CheckExtension( "GL_ARB_multitexture", multitexturefuncs, ARRAYSIZE( multitexturefuncs ), "gl_arb_multitexture", GL_ARB_MULTITEXTURE, 1.3f ))
|
||||
{
|
||||
pglGetIntegerv( GL_MAX_TEXTURE_UNITS_ARB, &glConfig.max_texture_units );
|
||||
}
|
||||
@@ -927,7 +907,7 @@ static void GL_InitExtensionsBigGL( void )
|
||||
GL_SetExtension( GL_ARB_MULTITEXTURE, false );
|
||||
|
||||
// 3d texture support
|
||||
if( GL_CheckExtension( "GL_EXT_texture3D", texture3dextfuncs, "gl_texture_3d", GL_TEXTURE_3D_EXT, 2.0f ))
|
||||
if( GL_CheckExtension( "GL_EXT_texture3D", texture3dextfuncs, ARRAYSIZE( texture3dextfuncs ), "gl_texture_3d", GL_TEXTURE_3D_EXT, 2.0f ))
|
||||
{
|
||||
pglGetIntegerv( GL_MAX_3D_TEXTURE_SIZE, &glConfig.max_3d_texture_size );
|
||||
|
||||
@@ -939,25 +919,25 @@ static void GL_InitExtensionsBigGL( void )
|
||||
}
|
||||
|
||||
// 2d texture array support
|
||||
if( GL_CheckExtension( "GL_EXT_texture_array", texture3dextfuncs, "gl_texture_2d_array", GL_TEXTURE_ARRAY_EXT, 0 ))
|
||||
if( GL_CheckExtension( "GL_EXT_texture_array", texture3dextfuncs, ARRAYSIZE( texture3dextfuncs ), "gl_texture_2d_array", GL_TEXTURE_ARRAY_EXT, 0 ))
|
||||
pglGetIntegerv( GL_MAX_ARRAY_TEXTURE_LAYERS_EXT, &glConfig.max_2d_texture_layers );
|
||||
|
||||
// cubemaps support
|
||||
if( GL_CheckExtension( "GL_ARB_texture_cube_map", NULL, "gl_texture_cubemap", GL_TEXTURE_CUBEMAP_EXT, 0 ))
|
||||
if( GL_CheckExtension( "GL_ARB_texture_cube_map", NULL, 0, "gl_texture_cubemap", GL_TEXTURE_CUBEMAP_EXT, 0 ))
|
||||
{
|
||||
pglGetIntegerv( GL_MAX_CUBE_MAP_TEXTURE_SIZE_ARB, &glConfig.max_cubemap_size );
|
||||
|
||||
// check for seamless cubemaps too
|
||||
GL_CheckExtension( "GL_ARB_seamless_cube_map", NULL, "gl_texture_cubemap_seamless", GL_ARB_SEAMLESS_CUBEMAP, 0 );
|
||||
GL_CheckExtension( "GL_ARB_seamless_cube_map", NULL, 0, "gl_texture_cubemap_seamless", GL_ARB_SEAMLESS_CUBEMAP, 0 );
|
||||
}
|
||||
|
||||
GL_CheckExtension( "GL_ARB_texture_non_power_of_two", NULL, "gl_texture_npot", GL_ARB_TEXTURE_NPOT_EXT, 0 );
|
||||
GL_CheckExtension( "GL_ARB_texture_compression", texturecompressionfuncs, "gl_texture_dxt_compression", GL_TEXTURE_COMPRESSION_EXT, 0 );
|
||||
if( !GL_CheckExtension( "GL_EXT_texture_edge_clamp", NULL, "gl_clamp_to_edge", GL_CLAMPTOEDGE_EXT, 2.0 )) // present in ES2
|
||||
GL_CheckExtension( "GL_SGIS_texture_edge_clamp", NULL, "gl_clamp_to_edge", GL_CLAMPTOEDGE_EXT, 0 );
|
||||
GL_CheckExtension( "GL_ARB_texture_non_power_of_two", NULL, 0, "gl_texture_npot", GL_ARB_TEXTURE_NPOT_EXT, 0 );
|
||||
GL_CheckExtension( "GL_ARB_texture_compression", texturecompressionfuncs, ARRAYSIZE( texturecompressionfuncs ), "gl_texture_dxt_compression", GL_TEXTURE_COMPRESSION_EXT, 0 );
|
||||
if( !GL_CheckExtension( "GL_EXT_texture_edge_clamp", NULL, 0, "gl_clamp_to_edge", GL_CLAMPTOEDGE_EXT, 2.0 )) // present in ES2
|
||||
GL_CheckExtension( "GL_SGIS_texture_edge_clamp", NULL, 0, "gl_clamp_to_edge", GL_CLAMPTOEDGE_EXT, 0 );
|
||||
|
||||
glConfig.max_texture_anisotropy = 0.0f;
|
||||
if( GL_CheckExtension( "GL_EXT_texture_filter_anisotropic", NULL, "gl_texture_anisotropic_filter", GL_ANISOTROPY_EXT, 0 ))
|
||||
if( GL_CheckExtension( "GL_EXT_texture_filter_anisotropic", NULL, 0, "gl_texture_anisotropic_filter", GL_ANISOTROPY_EXT, 0 ))
|
||||
pglGetFloatv( GL_MAX_TEXTURE_MAX_ANISOTROPY_EXT, &glConfig.max_texture_anisotropy );
|
||||
|
||||
#if XASH_WIN32 // Win32 only drivers?
|
||||
@@ -965,30 +945,30 @@ static void GL_InitExtensionsBigGL( void )
|
||||
if( glConfig.hardware_type != GLHW_INTEL )
|
||||
#endif
|
||||
{
|
||||
if( GL_CheckExtension( "GL_EXT_texture_lod_bias", NULL, "gl_texture_mipmap_biasing", GL_TEXTURE_LOD_BIAS, 1.4 ))
|
||||
if( GL_CheckExtension( "GL_EXT_texture_lod_bias", NULL, 0, "gl_texture_mipmap_biasing", GL_TEXTURE_LOD_BIAS, 1.4 ))
|
||||
pglGetFloatv( GL_MAX_TEXTURE_LOD_BIAS_EXT, &glConfig.max_texture_lod_bias );
|
||||
}
|
||||
|
||||
GL_CheckExtension( "GL_ARB_texture_border_clamp", NULL, NULL, GL_CLAMP_TEXBORDER_EXT, 2.0 ); // present in ES2
|
||||
GL_CheckExtension( "GL_ARB_texture_border_clamp", NULL, 0, NULL, GL_CLAMP_TEXBORDER_EXT, 2.0 ); // present in ES2
|
||||
|
||||
GL_CheckExtension( "GL_ARB_depth_texture", NULL, NULL, GL_DEPTH_TEXTURE, 1.4 ); // missing in gles, check GL_OES_depth_texture
|
||||
GL_CheckExtension( "GL_ARB_texture_float", NULL, "gl_texture_float", GL_ARB_TEXTURE_FLOAT_EXT, 0 );
|
||||
GL_CheckExtension( "GL_ARB_depth_buffer_float", NULL, "gl_texture_depth_float", GL_ARB_DEPTH_FLOAT_EXT, 0 );
|
||||
GL_CheckExtension( "GL_EXT_gpu_shader4", NULL, NULL, GL_EXT_GPU_SHADER4, 0 ); // don't confuse users
|
||||
GL_CheckExtension( "GL_ARB_vertex_buffer_object", vbofuncs, "gl_vertex_buffer_object", GL_ARB_VERTEX_BUFFER_OBJECT_EXT, 2.0 );
|
||||
GL_CheckExtension( "GL_ARB_texture_multisample", multisampletexfuncs, "gl_texture_multisample", GL_TEXTURE_MULTISAMPLE, 0 );
|
||||
GL_CheckExtension( "GL_ARB_texture_compression_bptc", NULL, "gl_texture_bptc_compression", GL_ARB_TEXTURE_COMPRESSION_BPTC, 0 );
|
||||
GL_CheckExtension( "GL_ARB_depth_texture", NULL, 0, NULL, GL_DEPTH_TEXTURE, 1.4 ); // missing in gles, check GL_OES_depth_texture
|
||||
GL_CheckExtension( "GL_ARB_texture_float", NULL, 0, "gl_texture_float", GL_ARB_TEXTURE_FLOAT_EXT, 0 );
|
||||
GL_CheckExtension( "GL_ARB_depth_buffer_float", NULL, 0, "gl_texture_depth_float", GL_ARB_DEPTH_FLOAT_EXT, 0 );
|
||||
GL_CheckExtension( "GL_EXT_gpu_shader4", NULL, 0, NULL, GL_EXT_GPU_SHADER4, 0 ); // don't confuse users
|
||||
GL_CheckExtension( "GL_ARB_vertex_buffer_object", vbofuncs, ARRAYSIZE( vbofuncs ), "gl_vertex_buffer_object", GL_ARB_VERTEX_BUFFER_OBJECT_EXT, 2.0 );
|
||||
GL_CheckExtension( "GL_ARB_texture_multisample", multisampletexfuncs, ARRAYSIZE( multisampletexfuncs ), "gl_texture_multisample", GL_TEXTURE_MULTISAMPLE, 0 );
|
||||
GL_CheckExtension( "GL_ARB_texture_compression_bptc", NULL, 0, "gl_texture_bptc_compression", GL_ARB_TEXTURE_COMPRESSION_BPTC, 0 );
|
||||
#if !XASH_GL_STATIC
|
||||
if( glConfig.context == CONTEXT_TYPE_GL_CORE )
|
||||
GL_CheckExtension( "shader_objects", shaderobjectsfuncs_gles, "gl_shaderobjects", GL_SHADER_OBJECTS_EXT, 2.0 );
|
||||
GL_CheckExtension( "shader_objects", shaderobjectsfuncs_gles, ARRAYSIZE( shaderobjectsfuncs_gles ), "gl_shaderobjects", GL_SHADER_OBJECTS_EXT, 2.0 );
|
||||
else
|
||||
GL_CheckExtension( "GL_ARB_shader_objects", shaderobjectsfuncs, "gl_shaderobjects", GL_SHADER_OBJECTS_EXT, 2.0 );
|
||||
GL_CheckExtension( "GL_ARB_vertex_array_object", vaofuncs, "gl_vertex_array_object", GL_ARB_VERTEX_ARRAY_OBJECT_EXT, 3.0 );
|
||||
GL_CheckExtension( "GL_ARB_buffer_storage", bufferstoragefuncs, "gl_buffer_storage", GL_BUFFER_STORAGE_EXT, 4.4);
|
||||
GL_CheckExtension( "GL_ARB_map_buffer_range", mapbufferrangefuncs, "gl_map_buffer_range", GL_MAP_BUFFER_RANGE_EXT , 3.0);
|
||||
GL_CheckExtension( "GL_ARB_draw_elements_base_vertex", drawrangeelementsbasevertexfuncs, "gl_drawrangeelementsbasevertex", GL_DRAW_RANGE_ELEMENTS_BASE_VERTEX_EXT, 3.2 );
|
||||
GL_CheckExtension( "GL_ARB_shader_objects", shaderobjectsfuncs, ARRAYSIZE( shaderobjectsfuncs ), "gl_shaderobjects", GL_SHADER_OBJECTS_EXT, 2.0 );
|
||||
GL_CheckExtension( "GL_ARB_vertex_array_object", vaofuncs, ARRAYSIZE( vaofuncs ), "gl_vertex_array_object", GL_ARB_VERTEX_ARRAY_OBJECT_EXT, 3.0 );
|
||||
GL_CheckExtension( "GL_ARB_buffer_storage", bufferstoragefuncs, ARRAYSIZE( bufferstoragefuncs ), "gl_buffer_storage", GL_BUFFER_STORAGE_EXT, 4.4);
|
||||
GL_CheckExtension( "GL_ARB_map_buffer_range", mapbufferrangefuncs, ARRAYSIZE( mapbufferrangefuncs ), "gl_map_buffer_range", GL_MAP_BUFFER_RANGE_EXT , 3.0);
|
||||
GL_CheckExtension( "GL_ARB_draw_elements_base_vertex", drawrangeelementsbasevertexfuncs, ARRAYSIZE( drawrangeelementsbasevertexfuncs ), "gl_drawrangeelementsbasevertex", GL_DRAW_RANGE_ELEMENTS_BASE_VERTEX_EXT, 3.2 );
|
||||
#endif
|
||||
if( GL_CheckExtension( "GL_ARB_shading_language_100", NULL, NULL, GL_SHADER_GLSL100_EXT, 2.0 ))
|
||||
if( GL_CheckExtension( "GL_ARB_shading_language_100", NULL, 0, NULL, GL_SHADER_GLSL100_EXT, 2.0 ))
|
||||
{
|
||||
pglGetIntegerv( GL_MAX_TEXTURE_COORDS_ARB, &glConfig.max_texture_coords );
|
||||
pglGetIntegerv( GL_MAX_TEXTURE_IMAGE_UNITS_ARB, &glConfig.max_teximage_units );
|
||||
@@ -1009,11 +989,11 @@ static void GL_InitExtensionsBigGL( void )
|
||||
}
|
||||
|
||||
// rectangle textures support
|
||||
GL_CheckExtension( "GL_ARB_texture_rectangle", NULL, "gl_texture_rectangle", GL_TEXTURE_2D_RECT_EXT, 0 );
|
||||
GL_CheckExtension( "GL_ARB_texture_rectangle", NULL, 0, "gl_texture_rectangle", GL_TEXTURE_2D_RECT_EXT, 0 );
|
||||
|
||||
if( !GL_CheckExtension( "glDrawRangeElements", drawrangeelementsfuncs, "gl_drawrangeelements", GL_DRAW_RANGEELEMENTS_EXT, 0 ) )
|
||||
if( !GL_CheckExtension( "glDrawRangeElements", drawrangeelementsfuncs, ARRAYSIZE( drawrangeelementsfuncs ), "gl_drawrangeelements", GL_DRAW_RANGEELEMENTS_EXT, 0 ) )
|
||||
{
|
||||
if( GL_CheckExtension( "glDrawRangeElementsEXT", drawrangeelementsextfuncs,
|
||||
if( GL_CheckExtension( "glDrawRangeElementsEXT", drawrangeelementsextfuncs, ARRAYSIZE( drawrangeelementsextfuncs ),
|
||||
"gl_drawrangelements", GL_DRAW_RANGEELEMENTS_EXT, 0 ))
|
||||
{
|
||||
#if !XASH_GL_STATIC
|
||||
@@ -1024,7 +1004,7 @@ static void GL_InitExtensionsBigGL( void )
|
||||
|
||||
// this won't work without extended context
|
||||
if( glw_state.extended )
|
||||
GL_CheckExtension( "GL_ARB_debug_output", debugoutputfuncs, "gl_debug_output", GL_DEBUG_OUTPUT, 0 );
|
||||
GL_CheckExtension( "GL_ARB_debug_output", debugoutputfuncs, ARRAYSIZE( debugoutputfuncs ), "gl_debug_output", GL_DEBUG_OUTPUT, 0 );
|
||||
|
||||
#if XASH_PSVITA
|
||||
// not all GL1.1 functions are implemented in vitaGL, but there's enough
|
||||
@@ -1047,7 +1027,7 @@ void GL_InitExtensions( void )
|
||||
GL_OnContextCreated();
|
||||
|
||||
// initialize gl extensions
|
||||
GL_CheckExtension( "OpenGL 1.1.0", opengl_110funcs, NULL, GL_OPENGL_110, 1.0 );
|
||||
GL_CheckExtension( "OpenGL 1.1.0", opengl_110funcs, ARRAYSIZE( opengl_110funcs ), NULL, GL_OPENGL_110, 1.0 );
|
||||
|
||||
// get our various GL strings
|
||||
glConfig.vendor_string = (const char *)pglGetString( GL_VENDOR );
|
||||
|
||||
@@ -1317,10 +1317,8 @@ static void R_RenderBrushPoly( msurface_t *fa, int cull_type )
|
||||
|
||||
if( FBitSet( fa->flags, SURF_DRAWTURB ))
|
||||
{
|
||||
R_UploadRipples( t );
|
||||
|
||||
// warp texture, no lightmaps
|
||||
EmitWaterPolys( fa, (cull_type == CULL_BACKSIDE));
|
||||
EmitWaterPolys( fa, cull_type == CULL_BACKSIDE, R_UploadRipples( t ));
|
||||
return;
|
||||
}
|
||||
else GL_Bind( XASH_TEXTURE0, t->gl_texturenum );
|
||||
@@ -1500,11 +1498,8 @@ void R_DrawWaterSurfaces( void )
|
||||
if( !FBitSet( s->flags, SURF_DRAWTURB ))
|
||||
continue;
|
||||
|
||||
// set modulate mode explicitly
|
||||
R_UploadRipples( t );
|
||||
|
||||
for( ; s; s = s->texturechain )
|
||||
EmitWaterPolys( s, false );
|
||||
EmitWaterPolys( s, false, R_UploadRipples( t ));
|
||||
|
||||
t->texturechain = NULL;
|
||||
}
|
||||
|
||||
@@ -81,7 +81,6 @@ static struct
|
||||
uint32_t texture[RIPPLES_TEXSIZE];
|
||||
int gl_texturenum;
|
||||
int rippletexturenum;
|
||||
float texturescale; // not all textures are 128x128, scale the texcoords down
|
||||
} g_ripple;
|
||||
|
||||
|
||||
@@ -565,7 +564,7 @@ EmitWaterPolys
|
||||
Does a water warp on the pre-fragmented glpoly_t chain
|
||||
=============
|
||||
*/
|
||||
void EmitWaterPolys( msurface_t *warp, qboolean reverse )
|
||||
void EmitWaterPolys( msurface_t *warp, qboolean reverse, qboolean ripples )
|
||||
{
|
||||
float *v, nv, waveHeight;
|
||||
float s, t, os, ot;
|
||||
@@ -609,15 +608,15 @@ void EmitWaterPolys( msurface_t *warp, qboolean reverse )
|
||||
os = v[3];
|
||||
ot = v[4];
|
||||
|
||||
if( !r_ripple.value )
|
||||
if( !ripples )
|
||||
{
|
||||
s = os + r_turbsin[(int)((ot * 0.125f + gp_cl->time) * TURBSCALE) & 255];
|
||||
t = ot + r_turbsin[(int)((os * 0.125f + gp_cl->time) * TURBSCALE) & 255];
|
||||
}
|
||||
else
|
||||
{
|
||||
s = os / g_ripple.texturescale;
|
||||
t = ot / g_ripple.texturescale;
|
||||
s = os;
|
||||
t = ot;
|
||||
}
|
||||
|
||||
s *= ( 1.0f / SUBDIVIDE_SIZE );
|
||||
@@ -710,17 +709,6 @@ static void R_RunRipplesAnimation( const short *oldbuf, short *pbuf )
|
||||
}
|
||||
}
|
||||
|
||||
static int MostSignificantBit( unsigned int v )
|
||||
{
|
||||
#if __GNUC__
|
||||
return 31 - __builtin_clz( v );
|
||||
#else
|
||||
int i;
|
||||
for( i = 0, v >>= 1; v; v >>= 1, i++ );
|
||||
return i;
|
||||
#endif
|
||||
}
|
||||
|
||||
void R_AnimateRipples( void )
|
||||
{
|
||||
double frametime = gp_cl->time - g_ripple.time;
|
||||
@@ -750,67 +738,72 @@ void R_AnimateRipples( void )
|
||||
R_RunRipplesAnimation( g_ripple.oldbuf, g_ripple.curbuf );
|
||||
}
|
||||
|
||||
void R_UploadRipples( texture_t *image )
|
||||
qboolean R_UploadRipples( const texture_t *image )
|
||||
{
|
||||
gl_texture_t *glt;
|
||||
uint32_t *pixels;
|
||||
int wbits, wmask, wshft;
|
||||
const gl_texture_t *glt;
|
||||
const uint32_t *pixels;
|
||||
int y;
|
||||
int width, height, size;
|
||||
|
||||
// discard unuseful textures
|
||||
if( !r_ripple.value || image->width > RIPPLES_CACHEWIDTH || image->width != image->height )
|
||||
if( !r_ripple.value )
|
||||
{
|
||||
GL_Bind( XASH_TEXTURE0, image->gl_texturenum );
|
||||
return;
|
||||
return false;
|
||||
}
|
||||
|
||||
// discard unuseful textures
|
||||
glt = R_GetTexture( image->gl_texturenum );
|
||||
if( !glt || !glt->original || !glt->original->buffer || !FBitSet( glt->flags, TF_EXPAND_SOURCE ))
|
||||
{
|
||||
GL_Bind( XASH_TEXTURE0, image->gl_texturenum );
|
||||
return;
|
||||
return false;
|
||||
}
|
||||
|
||||
GL_Bind( XASH_TEXTURE0, g_ripple.rippletexturenum );
|
||||
|
||||
// no updates this frame
|
||||
if( !g_ripple.update && image->gl_texturenum == g_ripple.gl_texturenum )
|
||||
return;
|
||||
return true;
|
||||
|
||||
g_ripple.gl_texturenum = image->gl_texturenum;
|
||||
if( r_ripple.value == 1.0f )
|
||||
{
|
||||
g_ripple.texturescale = Q_max( 1.0f, image->width / 64.0f );
|
||||
}
|
||||
else
|
||||
{
|
||||
g_ripple.texturescale = 1.0f;
|
||||
}
|
||||
|
||||
size = r_ripple.value == 1.0f ? 64 : RIPPLES_CACHEWIDTH;
|
||||
|
||||
pixels = (uint32_t *)glt->original->buffer;
|
||||
wbits = MostSignificantBit( image->width );
|
||||
wshft = 7 - wbits;
|
||||
wmask = image->width - 1;
|
||||
// try to preserve aspect ratio
|
||||
width = height = RIPPLES_CACHEWIDTH; // always render at maximum size
|
||||
if( image->width > image->height )
|
||||
height = (float)image->height / image->width * width;
|
||||
else if( image->width < image->height )
|
||||
width = (float)image->width / image->height * height;
|
||||
|
||||
for( y = 0; y < image->height; y++ )
|
||||
pixels = (const uint32_t *)glt->original->buffer;
|
||||
|
||||
for( y = 0; y < height; y++ )
|
||||
{
|
||||
int ry = y << ( 7 + wshft );
|
||||
int ry = (float)y / height * size;
|
||||
int x;
|
||||
|
||||
for( x = 0; x < image->width; x++ )
|
||||
for( x = 0; x < width; x++ )
|
||||
{
|
||||
int rx = x << wshft;
|
||||
int val = g_ripple.curbuf[ry + rx] >> 4;
|
||||
int rx = (float)x / width * size;
|
||||
int val = g_ripple.curbuf[ry * RIPPLES_CACHEWIDTH + rx] / 16;
|
||||
|
||||
int py = (y - val) & wmask;
|
||||
int px = (x + val) & wmask;
|
||||
int p = ( py << wbits ) + px;
|
||||
// transform it to texture space and get nice tiling effect
|
||||
int rpy = ( y - val ) % height;
|
||||
int rpx = ( x + val ) % width;
|
||||
|
||||
g_ripple.texture[(y << wbits) + x] = pixels[p];
|
||||
int py = (float)rpy / height * image->height;
|
||||
int px = (float)rpx / width * image->width;
|
||||
|
||||
if( py < 0 ) py = image->height + py;
|
||||
if( px < 0 ) px = image->width + px;
|
||||
|
||||
g_ripple.texture[y * width + x] = pixels[py * image->width + px];
|
||||
}
|
||||
}
|
||||
|
||||
pglTexImage2D( GL_TEXTURE_2D, 0, GL_RGBA, image->width, image->width, 0,
|
||||
pglTexImage2D( GL_TEXTURE_2D, 0, glt->format, width, height, 0,
|
||||
GL_RGBA, GL_UNSIGNED_BYTE, g_ripple.texture );
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -17,8 +17,10 @@ fi
|
||||
|
||||
if [ "$ARCH" = "i386" ]; then
|
||||
cp SDL2_VC/lib/x86/SDL2.dll . # Install SDL2
|
||||
cp SDL2_VC/lib/x86/SDL2.pdb .
|
||||
elif [ "$ARCH" = "amd64" ]; then
|
||||
cp SDL2_VC/lib/x64/SDL2.dll .
|
||||
cp SDL2_VC/lib/x64/SDL2.pdb .
|
||||
else
|
||||
die
|
||||
fi
|
||||
|
||||
@@ -79,7 +79,7 @@ static void FS_CreatePath( char *path )
|
||||
}
|
||||
}
|
||||
|
||||
void usage( const char *arg0 )
|
||||
static void usage( const char *arg0 )
|
||||
{
|
||||
printf( "%s: <action> [option...] <file>\n", arg0 );
|
||||
puts( "XAR is a simple frontend to Xash3D FWGS's filesystem_stdio library" );
|
||||
|
||||
2
wscript
2
wscript
@@ -392,8 +392,10 @@ def configure(conf):
|
||||
|
||||
conf.env.TESTS = conf.options.TESTS
|
||||
conf.env.ENABLE_UTILS = conf.options.ENABLE_UTILS
|
||||
conf.env.ENABLE_XAR = conf.options.ENABLE_XAR
|
||||
conf.env.ENABLE_FUZZER = conf.options.ENABLE_FUZZER
|
||||
conf.env.DEDICATED = conf.options.DEDICATED
|
||||
conf.env.SUPPORT_BSP2_FORMAT = conf.options.SUPPORT_BSP2_FORMAT
|
||||
|
||||
conf.define_cond('SUPPORT_BSP2_FORMAT', conf.options.SUPPORT_BSP2_FORMAT)
|
||||
conf.define_cond('SUPPORT_HL25_EXTENDED_STRUCTS', conf.options.SUPPORT_HL25_EXTENDED_STRUCTS)
|
||||
|
||||
Reference in New Issue
Block a user