diff --git a/common/port.h b/common/port.h index 0c1a90f6..3aef3a17 100644 --- a/common/port.h +++ b/common/port.h @@ -126,6 +126,9 @@ GNU General Public License for more details. #define XASH_ALLOW_SAVERESTORE_OFFSETS #endif #endif //WIN32 +#ifndef XASH_LOW_MEMORY +#define XASH_LOW_MEMORY 0 +#endif #include #include diff --git a/common/xash3d_types.h b/common/xash3d_types.h index 1c82b353..f3de92e3 100644 --- a/common/xash3d_types.h +++ b/common/xash3d_types.h @@ -142,7 +142,13 @@ typedef void (*setpair_t)( const char *key, const void *value, void *buffer, voi // config strings are a general means of communication from // the server to all connected clients. // each config string can be at most CS_SIZE characters. +#if XASH_LOW_MEMORY == 0 #define MAX_QPATH 64 // max length of a game pathname +#elif XASH_LOW_MEMORY == 2 +#define MAX_QPATH 32 // should be enough for singleplayer +#elif XASH_LOW_MEMORY == 1 +#define MAX_QPATH 48 +#endif #define MAX_OSPATH 260 // max length of a filesystem pathname #define CS_SIZE 64 // size of one config string #define CS_TIME 16 // size of time string diff --git a/engine/client/cl_game.c b/engine/client/cl_game.c index 468542c0..8ffa78e0 100644 --- a/engine/client/cl_game.c +++ b/engine/client/cl_game.c @@ -1123,8 +1123,9 @@ void CL_InitEdicts( void ) Assert( clgame.entities == NULL ); if( !clgame.mempool ) return; // Host_Error without client - - CL_UPDATE_BACKUP = ( cl.maxclients == 1 ) ? SINGLEPLAYER_BACKUP : MULTIPLAYER_BACKUP; +#if XASH_LOW_MEMORY != 2 + CL_UPDATE_BACKUP = ( cl.maxclients <= 1 ) ? SINGLEPLAYER_BACKUP : MULTIPLAYER_BACKUP; +#endif 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 ); diff --git a/engine/client/cl_parse.c b/engine/client/cl_parse.c index 9fd165bc..da41fac2 100644 --- a/engine/client/cl_parse.c +++ b/engine/client/cl_parse.c @@ -21,9 +21,9 @@ GNU General Public License for more details. #include "shake.h" #include "hltv.h" #include "input.h" - +#if XASH_LOW_MEMORY != 2 int CL_UPDATE_BACKUP = SINGLEPLAYER_BACKUP; - +#endif /* =============== CL_UserMsgStub @@ -92,7 +92,7 @@ void CL_ParseSoundPacket( sizebuf_t *msg ) char sentenceName[32]; if( FBitSet( flags, SND_SEQUENCE )) - Q_snprintf( sentenceName, sizeof( sentenceName ), "!#%i", sound + MAX_SOUNDS ); + Q_snprintf( sentenceName, sizeof( sentenceName ), "!#%i", sound + MAX_SOUNDS_NONSENTENCE ); else Q_snprintf( sentenceName, sizeof( sentenceName ), "!%i", sound ); handle = S_RegisterSound( sentenceName ); @@ -156,7 +156,7 @@ void CL_ParseRestoreSoundPacket( sizebuf_t *msg ) char sentenceName[32]; if( flags & SND_SEQUENCE ) - Q_snprintf( sentenceName, sizeof( sentenceName ), "!%i", sound + MAX_SOUNDS ); + Q_snprintf( sentenceName, sizeof( sentenceName ), "!%i", sound + MAX_SOUNDS_NONSENTENCE ); else Q_snprintf( sentenceName, sizeof( sentenceName ), "!%i", sound ); handle = S_RegisterSound( sentenceName ); @@ -881,7 +881,7 @@ void CL_ParseServerData( sizebuf_t *msg ) cl.playernum = MSG_ReadByte( msg ); cl.maxclients = MSG_ReadByte( msg ); clgame.maxEntities = MSG_ReadWord( msg ); - clgame.maxEntities = bound( 600, clgame.maxEntities, MAX_EDICTS ); + clgame.maxEntities = bound( MIN_EDICTS, clgame.maxEntities, MAX_EDICTS ); clgame.maxModels = MSG_ReadWord( msg ); Q_strncpy( clgame.mapname, MSG_ReadString( msg ), MAX_STRING ); Q_strncpy( clgame.maptitle, MSG_ReadString( msg ), MAX_STRING ); @@ -1411,6 +1411,36 @@ void CL_ParseResource( sizebuf_t *msg ) if( MSG_ReadOneBit( msg )) MSG_ReadBytes( msg, pResource->rguc_reserved, sizeof( pResource->rguc_reserved )); + if( pResource->type == t_sound && pResource->nIndex > MAX_SOUNDS ) + { + Mem_Free( pResource ); + Host_Error( "bad sound index\n" ); + } + + if( pResource->type == t_model && pResource->nIndex > MAX_MODELS ) + { + Mem_Free( pResource ); + Host_Error( "bad model index\n" ); + } + + if( pResource->type == t_eventscript && pResource->nIndex > MAX_EVENTS ) + { + Mem_Free( pResource ); + Host_Error( "bad event index\n" ); + } + + if( pResource->type == t_generic && pResource->nIndex > MAX_CUSTOM ) + { + Mem_Free( pResource ); + Host_Error( "bad file index\n" ); + } + + if( pResource->type == t_decal && pResource->nIndex > MAX_DECALS ) + { + Mem_Free( pResource ); + Host_Error( "bad decal index\n" ); + } + CL_AddToResourceList( pResource, &cl.resourcesneeded ); } @@ -2394,7 +2424,7 @@ void CL_ParseLegacyServerData( sizebuf_t *msg ) cl.playernum = MSG_ReadByte( msg ); cl.maxclients = MSG_ReadByte( msg ); clgame.maxEntities = MSG_ReadWord( msg ); - clgame.maxEntities = bound( 600, clgame.maxEntities, 4096 ); + clgame.maxEntities = bound( 30, clgame.maxEntities, 4096 ); clgame.maxModels = 512; Q_strncpy( clgame.mapname, MSG_ReadString( msg ), MAX_STRING ); Q_strncpy( clgame.maptitle, MSG_ReadString( msg ), MAX_STRING ); @@ -2724,7 +2754,13 @@ void CL_LegacyUpdateUserinfo( sizebuf_t *msg ) } else memset( player, 0, sizeof( *player )); } - +#if XASH_LOW_MEMORY == 0 +#define MAX_LEGACY_RESOURCES 2048 +#elif XASH_LOW_MEMORY == 2 +#define MAX_LEGACY_RESOURCES 1 +#elif XASH_LOW_MEMORY == 1 +#define MAX_LEGACY_RESOURCES 512 +#endif /* ============== CL_ParseResourceList @@ -2738,17 +2774,20 @@ void CL_LegacyParseResourceList( sizebuf_t *msg ) static struct { int rescount; - int restype[MAX_RESOURCES]; - char resnames[MAX_RESOURCES][CS_SIZE]; + int restype[MAX_LEGACY_RESOURCES]; + char resnames[MAX_LEGACY_RESOURCES][MAX_QPATH]; } reslist; memset( &reslist, 0, sizeof( reslist )); reslist.rescount = MSG_ReadWord( msg ) - 1; + if( reslist.rescount > MAX_LEGACY_RESOURCES ) + Host_Error("MAX_RESOURCES reached\n"); + for( i = 0; i < reslist.rescount; i++ ) { reslist.restype[i] = MSG_ReadWord( msg ); - Q_strncpy( reslist.resnames[i], MSG_ReadString( msg ), CS_SIZE ); + Q_strncpy( reslist.resnames[i], MSG_ReadString( msg ), MAX_QPATH ); } if( CL_IsPlaybackDemo() ) diff --git a/engine/client/cl_view.c b/engine/client/cl_view.c index dd52b369..050bdaaf 100644 --- a/engine/client/cl_view.c +++ b/engine/client/cl_view.c @@ -486,6 +486,7 @@ void V_PostRender( void ) Con_DrawVersion(); Con_DrawDebug(); // must be last Touch_Draw(); + OSK_Draw(); S_ExtraUpdate(); } diff --git a/engine/client/client.h b/engine/client/client.h index 00c15e67..00b8b61c 100644 --- a/engine/client/client.h +++ b/engine/client/client.h @@ -96,7 +96,11 @@ typedef struct #define ANGLE_MASK (ANGLE_BACKUP - 1) #define CL_UPDATE_MASK (CL_UPDATE_BACKUP - 1) +#if XASH_LOW_MEMORY == 2 +#define CL_UPDATE_BACKUP SINGLEPLAYER_BACKUP +#else extern int CL_UPDATE_BACKUP; +#endif #define SIGNONS 2 // signon messages to receive before connected #define INVALID_HANDLE 0xFFFF // for XashXT cache system @@ -1115,6 +1119,26 @@ void SCR_RunCinematic( void ); void SCR_StopCinematic( void ); void CL_PlayVideo_f( void ); + +// +// keys.c +// +int Key_IsDown( int keynum ); +const char *Key_IsBind( int keynum ); +void Key_Event( int key, int down ); +void Key_Init( void ); +void Key_WriteBindings( file_t *f ); +const char *Key_GetBinding( int keynum ); +void Key_SetBinding( int keynum, const char *binding ); +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 ); +void OSK_Draw( void ); + extern rgba_t g_color_table[8]; #endif//CLIENT_H diff --git a/engine/client/console.c b/engine/client/console.c index 8699e48c..8d10de88 100644 --- a/engine/client/console.c +++ b/engine/client/console.c @@ -37,15 +37,19 @@ static qboolean g_utf8 = false; #define COLOR_DEFAULT '7' #define CON_HISTORY 64 #define MAX_DBG_NOTIFY 128 +#if XASH_LOW_MEMORY +#define CON_NUMFONTS 1 // do not load different font textures +#define CON_TEXTSIZE 32768 // max scrollback buffer characters in console (32 kb) +#define CON_MAXLINES 2048 // max scrollback buffer lines in console +#else #define CON_NUMFONTS 3 // maxfonts - +#define CON_TEXTSIZE 1048576 // max scrollback buffer characters in console (1 Mb) +#define CON_MAXLINES 16384 // max scrollback buffer lines in console +#endif #define CON_LINES( i ) (con.lines[(con.lines_first + (i)) % con.maxlines]) #define CON_LINES_COUNT con.lines_count #define CON_LINES_LAST() CON_LINES( CON_LINES_COUNT - 1 ) -#define CON_TEXTSIZE 1048576 // max scrollback buffer characters in console (1 Mb) -#define CON_MAXLINES 16384 // max scrollback buffer lines in console - // console color typeing rgba_t g_color_table[8] = { @@ -663,7 +667,7 @@ static void Con_LoadConchars( void ) int i, fontSize; // load all the console fonts - for( i = 0; i < 3; i++ ) + for( i = 0; i < CON_NUMFONTS; i++ ) Con_LoadConsoleFont( i, con.chars + i ); // select properly fontsize @@ -673,6 +677,9 @@ static void Con_LoadConchars( void ) fontSize = 2; else fontSize = 1; + if( fontSize > CON_NUMFONTS - 1 ) + fontSize = CON_NUMFONTS - 1; + // sets the current font con.lastUsedFont = con.curFont = &con.chars[fontSize]; } @@ -929,7 +936,7 @@ choose font size */ void Con_SetFont( int fontNum ) { - fontNum = bound( 0, fontNum, 2 ); + fontNum = bound( 0, fontNum, CON_NUMFONTS - 1 ); con.curFont = &con.chars[fontNum]; } @@ -1880,8 +1887,8 @@ void Con_DrawInput( int lines ) return; y = lines - ( con.curFont->charHeight * 2 ); - Con_DrawCharacter( 8, y, ']', g_color_table[7] ); - Field_DrawInputLine( 16, y, &con.input ); + Con_DrawCharacter( con.curFont->charWidths[' '], y, ']', g_color_table[7] ); + Field_DrawInputLine( con.curFont->charWidths[' ']*2, y, &con.input ); } /* @@ -2079,6 +2086,8 @@ void Con_DrawSolidConsole( int lines ) // draw the background ref.dllFuncs.GL_SetRenderMode( kRenderNormal ); ref.dllFuncs.Color4ub( 255, 255, 255, 255 ); // to prevent grab color from screenfade + if( refState.width * 3 / 4 < refState.height && lines >= refState.height ) + ref.dllFuncs.R_DrawStretchPic( 0, lines - refState.height, refState.width, refState.height - refState.width * 3 / 4, 0, 0, 1, 1, R_GetBuiltinTexture( REF_BLACK_TEXTURE) ); ref.dllFuncs.R_DrawStretchPic( 0, lines - refState.width * 3 / 4, refState.width, refState.width * 3 / 4, 0, 0, 1, 1, con.background ); if( !con.curFont || !host.allow_console ) @@ -2132,7 +2141,7 @@ void Con_DrawSolidConsole( int lines ) y -= Con_DrawConsoleLine( y, x ); // top of console buffer or console window - if( x == 0 || y < con.curFont->charHeight ) + if( x == 0 || y < con.curFont->charHeight ) break; x--; } @@ -2356,10 +2365,11 @@ INTERNAL RESOURCE */ void Con_VidInit( void ) { - Con_CheckResize(); - Con_LoadConchars(); - + Con_CheckResize(); +#if XASH_LOW_MEMORY + con.background = R_GetBuiltinTexture( REF_BLACK_TEXTURE ); +#else // loading console image if( host.allow_console ) { @@ -2434,6 +2444,7 @@ void Con_VidInit( void ) // missed console image will be replaced as gray background like X-Ray or Crysis if( con.background == R_GetBuiltinTexture( REF_DEFAULT_TEXTURE ) || con.background == 0 ) con.background = R_GetBuiltinTexture( REF_GRAY_TEXTURE ); +#endif } /* diff --git a/engine/client/keys.c b/engine/client/keys.c index b70d3b84..e2982dd3 100644 --- a/engine/client/keys.c +++ b/engine/client/keys.c @@ -140,6 +140,11 @@ keyname_t keynames[] = {NULL, 0, NULL }, }; +static void OSK_EnableTextInput( qboolean enable, qboolean force ); +static qboolean OSK_KeyEvent( int key, int down ); +static convar_t *osk_enable; +static convar_t *key_rotate; + /* =================== Key_IsDown @@ -514,6 +519,10 @@ void Key_Init( void ) // setup default binding. "unbindall" from config.cfg will be reset it for( kn = keynames; kn->name; kn++ ) Key_SetBinding( kn->keynum, kn->binding ); + + osk_enable = Cvar_Get( "osk_enable", "0", FCVAR_ARCHIVE, "enable built-in on-screen keyboard" ); + key_rotate = Cvar_Get( "key_rotate", "0", FCVAR_ARCHIVE, "rotate arrow keys (0-3)" ); + } /* @@ -586,6 +595,48 @@ static qboolean Key_IsAllowedAutoRepeat( int key ) } } +static int Key_Rotate( int key ) +{ + if( key_rotate->value == 1.0f ) // CW + { + if( key == K_UPARROW ) + key = K_LEFTARROW; + else if( key == K_LEFTARROW ) + key = K_DOWNARROW; + else if( key == K_RIGHTARROW ) + key = K_UPARROW; + else if( key == K_DOWNARROW ) + key = K_RIGHTARROW; + } + + else if( key_rotate->value == 3.0f ) // CCW + { + if( key == K_UPARROW ) + key = K_RIGHTARROW; + else if( key == K_LEFTARROW ) + key = K_UPARROW; + else if( key == K_RIGHTARROW ) + key = K_DOWNARROW; + else if( key == K_DOWNARROW ) + key = K_LEFTARROW; + } + + else if( key_rotate->value == 2.0f ) + { + if( key == K_UPARROW ) + key = K_DOWNARROW; + else if( key == K_LEFTARROW ) + key = K_RIGHTARROW; + else if( key == K_RIGHTARROW ) + key = K_LEFTARROW; + else if( key == K_DOWNARROW ) + key = K_UPARROW; + } + + return key; +} + + /* =================== Key_Event @@ -597,6 +648,11 @@ void Key_Event( int key, int down ) { const char *kb; + key = Key_Rotate( key ); + + if( OSK_KeyEvent( key, down ) ) + return; + // key was pressed before engine was run if( !keys[key].down && !down ) return; @@ -749,6 +805,11 @@ Key_EnableTextInput */ void Key_EnableTextInput( qboolean enable, qboolean force ) { + if( CVAR_TO_BOOL( osk_enable ) ) + { + OSK_EnableTextInput( enable, force ); + return; + } if( enable && ( !host.textmode || force ) ) Platform_EnableTextInput( true ); else if( !enable ) @@ -845,3 +906,286 @@ void CL_CharEvent( int key ) UI_CharEvent( key ); } } + + +/* On-screen keyboard: + * + * 4 lines with 13 buttons each + * Left trigger == backspace + * Right trigger == space + * Any button press is button press on keyboard + * + * Our layout: + * 0 1 2 3 4 5 6 7 8 9 10 11 12 + * +--+--+--+--+--+--+--+--+--+--+--+--+--+ + * |` |1 |2 |3 |4 |5 |6 |7 |8 |9 |0 |- |= | 0 + * +--+--+--+--+--+--+--+--+--+--+--+--+--+ + * |q |w |e |r |t |y |u |i |o |p |[ |] |\ | 1 + * +--+--+--+--+--+--+--+--+--+--+--+--+--+ + * |CL|a |s |d |f |g |h |j |k |l |; |' |BS| 2 + * +--+--+--+--+--+--+--+--+--+--+--+--+--+ + * |SH|z |x |c |v |b |n |m |, |. |/ |SP|EN| 3 + * +--+--+--+--+--+--+--+--+--+--+--+--+--+ + */ + +#define MAX_OSK_ROWS 13 +#define MAX_OSK_LINES 4 + +enum +{ + OSK_DEFAULT = 0, + OSK_UPPER, // on caps, shift + /* + OSK_RUSSIAN, + OSK_RUSSIAN_UPPER, + */ + OSK_LAST +}; + +enum +{ + OSK_TAB = 16, + OSK_SHIFT, + OSK_BACKSPACE, + OSK_ENTER, + OSK_SPECKEY_LAST +}; +static const char *osk_keylayout[][4] = +{ + { + "`1234567890-=", // 13 + "qwertyuiop[]\\", // 13 + "\x10" "asdfghjkl;'" "\x12", // 11 + caps on a left, enter on a right + "\x11" "zxcvbnm,./ " "\x13" // 10 + esc on left + shift on a left/right + }, + { + "~!@#$%^&*()_+", + "QWERTYUIOP{}|", + "\x10" "ASDFGHJKL:\"" "\x12", + "\x11" "ZXCVBNM<>? " "\x13" + } +}; + +struct osk_s +{ + qboolean enable; + int curlayout; + qboolean shift; + qboolean sending; + struct { + signed char x; + signed char y; + char val; + } curbutton; +} osk; + +static qboolean OSK_KeyEvent( int key, int down ) +{ + if( !osk.enable || !CVAR_TO_BOOL( osk_enable ) ) + return false; + + if( osk.sending ) + { + osk.sending = false; + return false; + } + + if( osk.curbutton.val == 0 ) + { + if( key == K_ENTER ) + { + osk.curbutton.val = osk_keylayout[osk.curlayout][osk.curbutton.y][osk.curbutton.x]; + return true; + } + return false; + } + + + switch ( key ) + { + case K_ENTER: + switch( osk.curbutton.val ) + { + case OSK_ENTER: + osk.sending = true; + Key_Event( K_ENTER, down ); + //osk_enable = false; // TODO: handle multiline + break; + case OSK_SHIFT: + if( !down ) + break; + + if( osk.curlayout & 1 ) + osk.curlayout--; + else + osk.curlayout++; + + osk.shift = osk.curbutton.val == OSK_SHIFT; + osk.curbutton.val = osk_keylayout[osk.curlayout][osk.curbutton.y][osk.curbutton.x]; + break; + case OSK_BACKSPACE: + Key_Event( K_BACKSPACE, down ); break; + case OSK_TAB: + Key_Event( K_TAB, down ); break; + default: + { + int ch; + + if( !down ) + { + if( osk.shift && osk.curlayout & 1 ) + osk.curlayout--; + + osk.shift = false; + osk.curbutton.val = osk_keylayout[osk.curlayout][osk.curbutton.y][osk.curbutton.x]; + break; + } + + if( !Q_stricmp( cl_charset->string, "utf-8" ) ) + ch = (unsigned char)osk.curbutton.val; + else + ch = Con_UtfProcessCharForce( (unsigned char)osk.curbutton.val ); + + if( !ch ) + break; + + Con_CharEvent( ch ); + if( cls.key_dest == key_menu ) + UI_CharEvent ( ch ); + + break; + } + } + break; + case K_UPARROW: + if( down && --osk.curbutton.y < 0 ) + { + osk.curbutton.y = MAX_OSK_LINES - 1; + osk.curbutton.val = 0; + return true; + } + break; + case K_DOWNARROW: + if( down && ++osk.curbutton.y >= MAX_OSK_LINES ) + { + osk.curbutton.y = 0; + osk.curbutton.val = 0; + return true; + } + break; + case K_LEFTARROW: + if( down && --osk.curbutton.x < 0 ) + osk.curbutton.x = MAX_OSK_ROWS - 1; + break; + case K_RIGHTARROW: + if( down && ++osk.curbutton.x >= MAX_OSK_ROWS ) + osk.curbutton.x = 0; + break; + default: + return false; + } + + osk.curbutton.val = osk_keylayout[osk.curlayout][osk.curbutton.y][osk.curbutton.x]; + return true; + +} + +/* +============= +Joy_EnableTextInput + +Enables built-in IME +============= +*/ +static void OSK_EnableTextInput( qboolean enable, qboolean force ) +{ + qboolean old = osk.enable; + + osk.enable = enable; + + if( osk.enable && (!old || force) ) + { + osk.curlayout = 0; + osk.curbutton.val = osk_keylayout[osk.curlayout][osk.curbutton.y][osk.curbutton.x]; + + } +} + +#define X_START 0.1347475f +#define Y_START 0.567f +#define X_STEP 0.05625 +#define Y_STEP 0.0825 + +/* +============ +Joy_DrawSymbolButton + +Draw button with symbol on it +============ +*/ +static void OSK_DrawSymbolButton( int symb, float x, float y, float width, float height ) +{ + char str[] = {symb & 255, 0}; + byte color[] = { 255, 255, 255, 255 }; + int x1 = x * refState.width, + y1 = y * refState.height, + w = width * refState.width, + h = height * refState.height; + + if( symb == osk.curbutton.val ) + { + ref.dllFuncs.FillRGBABlend( x1, y1, w, h, 255, 160, 0, 100 ); + } + + if( !symb || symb == ' ' || (symb >= OSK_TAB && symb < OSK_SPECKEY_LAST ) ) + return; + + Con_DrawCharacter( x1 + 1, y1, symb, color ); +} + +/* +============= +Joy_DrawSpecialButton + +Draw special button, like shift, enter or esc +============= +*/ +static void OSK_DrawSpecialButton( const char *name, float x, float y, float width, float height ) +{ + byte color[] = { 0, 255, 0, 255 }; + + Con_DrawString( x * refState.width, y * refState.height, name, color ); +} + + +/* +============= +Joy_DrawOnScreenKeyboard + +Draw on screen keyboard, if enabled +============= +*/ +void OSK_Draw( void ) +{ + const char **curlayout = osk_keylayout[osk.curlayout]; // shortcut :) + float x, y; + int i, j; + + if( !osk.enable || !CVAR_TO_BOOL(osk_enable) || !osk.curbutton.val ) + return; + + // draw keyboard + ref.dllFuncs.FillRGBABlend( X_START * refState.width, Y_START * refState.height, + X_STEP * MAX_OSK_ROWS * refState.width, + Y_STEP * MAX_OSK_LINES * refState.height, 100, 100, 100, 100 ); + + OSK_DrawSpecialButton( "-]", X_START, Y_START + Y_STEP * 2, X_STEP, Y_STEP ); + OSK_DrawSpecialButton( "<-", X_START + X_STEP * 12, Y_START + Y_STEP * 2, X_STEP, Y_STEP ); + + OSK_DrawSpecialButton( "sh", X_START, Y_START + Y_STEP * 3, X_STEP, Y_STEP ); + OSK_DrawSpecialButton( "en", X_START + X_STEP * 12, Y_START + Y_STEP * 3, X_STEP, Y_STEP ); + + for( y = Y_START, j = 0; j < MAX_OSK_LINES; j++, y += Y_STEP ) + for( x = X_START, i = 0; i < MAX_OSK_ROWS; i++, x += X_STEP ) + OSK_DrawSymbolButton( curlayout[j][i], x, y, X_STEP, Y_STEP ); +} diff --git a/engine/client/ref_common.c b/engine/client/ref_common.c index 41fb6da6..bb3e72b5 100644 --- a/engine/client/ref_common.c +++ b/engine/client/ref_common.c @@ -558,7 +558,7 @@ static void SetWidthAndHeightFromCommandLine( void ) return; } - R_SaveVideoMode( width, height ); + R_SaveVideoMode( width, height, width, height ); } static void SetFullscreenModeFromCommandLine( void ) diff --git a/engine/client/vid_common.c b/engine/client/vid_common.c index a2a315a7..f31312dd 100644 --- a/engine/client/vid_common.c +++ b/engine/client/vid_common.c @@ -31,6 +31,10 @@ glwstate_t glw_state; convar_t *window_xpos; convar_t *window_ypos; + +convar_t *vid_rotate; +convar_t *vid_scale; + /* ================= VID_StartupGamma @@ -62,19 +66,21 @@ void VID_InitDefaultResolution( void ) R_SaveVideoMode ================= */ -void R_SaveVideoMode( int w, int h ) +void R_SaveVideoMode( int w, int h , int render_w, int render_h ) { - refState.width = w; - refState.height = h; - host.window_center_x = w / 2; host.window_center_y = h / 2; Cvar_SetValue( "width", w ); Cvar_SetValue( "height", h ); + refState.width = render_w; + refState.height = render_h; + + host.renderinfo_changed = false; + // check for 4:3 or 5:4 - if( w * 3 != h * 4 && w * 4 != h * 5 ) + if( render_w * 3 != render_h * 4 && render_w * 4 != render_h * 5 ) refState.wideScreen = true; else refState.wideScreen = false; } @@ -175,6 +181,8 @@ void VID_Init( void ) vid_displayfrequency = Cvar_Get ( "vid_displayfrequency", "0", FCVAR_RENDERINFO|FCVAR_VIDRESTART, "fullscreen refresh rate" ); vid_fullscreen = Cvar_Get( "fullscreen", "0", FCVAR_RENDERINFO|FCVAR_VIDRESTART, "enable fullscreen mode" ); vid_highdpi = Cvar_Get( "vid_highdpi", "1", FCVAR_RENDERINFO|FCVAR_VIDRESTART, "enable High-DPI mode" ); + vid_rotate = Cvar_Get( "vid_rotate", "0", FCVAR_RENDERINFO|FCVAR_VIDRESTART, "screen rotation (0-3)" ); + vid_scale = Cvar_Get( "vid_scale", "1.0", FCVAR_RENDERINFO|FCVAR_VIDRESTART, "pixel scale" ); // a1ba: planned to be named vid_mode for compability // but supported mode list is filled by backends, so numbers are not portable any more diff --git a/engine/client/vid_common.h b/engine/client/vid_common.h index 842ae2fc..f95d2531 100644 --- a/engine/client/vid_common.h +++ b/engine/client/vid_common.h @@ -34,8 +34,11 @@ extern glwstate_t glw_state; extern convar_t *vid_fullscreen; extern convar_t *vid_displayfrequency; extern convar_t *vid_highdpi; +extern convar_t *vid_rotate; +extern convar_t *vid_scale; + extern convar_t *gl_wgl_msaa_samples; -void R_SaveVideoMode( int w, int h ); +void R_SaveVideoMode( int w, int h, int render_w, int render_h ); void VID_CheckChanges( void ); const char *VID_GetModeString( int vid_mode ); void VID_StartupGamma( void ); diff --git a/engine/common/common.h b/engine/common/common.h index 0b568410..33c9fee1 100644 --- a/engine/common/common.h +++ b/engine/common/common.h @@ -137,10 +137,18 @@ typedef enum #define CIN_MAIN 0 #define CIN_LOGO 1 - +#if XASH_LOW_MEMORY == 0 #define MAX_DECALS 512 // touching TE_DECAL messages, etc #define MAX_STATIC_ENTITIES 3096 // static entities that moved on the client when level is spawn +#elif XASH_LOW_MEMORY == 2 +#define MAX_DECALS 256 // touching TE_DECAL messages, etc +#define MAX_STATIC_ENTITIES 32 // static entities that moved on the client when level is spawn +#elif XASH_LOW_MEMORY == 1 +#define MAX_DECALS 512 // touching TE_DECAL messages, etc +#define MAX_STATIC_ENTITIES 128 // static entities that moved on the client when level is spawn +#endif + // filesystem flags #define FS_STATIC_PATH ( 1U << 0 ) // FS_ClearSearchPath will be ignore this path #define FS_NOWRITE_PATH ( 1U << 1 ) // default behavior - last added gamedir set as writedir. This flag disables it @@ -811,24 +819,6 @@ void HPAK_CheckIntegrity( const char *filename ); void HPAK_CheckSize( const char *filename ); void HPAK_FlushHostQueue( void ); -// -// keys.c -// -int Key_IsDown( int keynum ); -const char *Key_IsBind( int keynum ); -void Key_Event( int key, int down ); -void Key_Init( void ); -void Key_WriteBindings( file_t *f ); -const char *Key_GetBinding( int keynum ); -void Key_SetBinding( int keynum, const char *binding ); -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 ); - #include "avi/avi.h" // diff --git a/engine/common/filesystem.c b/engine/common/filesystem.c index aaf60591..0eaacbb3 100644 --- a/engine/common/filesystem.c +++ b/engine/common/filesystem.c @@ -1588,7 +1588,7 @@ void FS_ParseGenericGameInfo( gameinfo_t *GameInfo, const char *buf, const qbool else if( !Q_stricmp( token, isGameInfo ? "max_edicts" : "edicts" )) { pfile = COM_ParseFile( pfile, token ); - GameInfo->max_edicts = bound( 600, Q_atoi( token ), MAX_EDICTS ); + GameInfo->max_edicts = bound( MIN_EDICTS, Q_atoi( token ), MAX_EDICTS ); } // only for gameinfo else if( isGameInfo ) diff --git a/engine/common/net_ws.h b/engine/common/net_ws.h index 86b197ff..c2e4de33 100644 --- a/engine/common/net_ws.h +++ b/engine/common/net_ws.h @@ -29,8 +29,12 @@ typedef enum // Max length of a multicast message #define MAX_MULTICAST 8192 // some mods spamming for rain effect -#define MAX_INIT_MSG 0x20000 // max length of possible message +#if !XASH_LOW_MEMORY +#define MAX_INIT_MSG 0x20000 // max length of possible message +#else +#define MAX_INIT_MSG 0x8000 +#endif // net packets type #define NET_HEADER_OUTOFBANDPACKET -1 #define NET_HEADER_SPLITPACKET -2 diff --git a/engine/common/netchan.h b/engine/common/netchan.h index aa608719..6a321b55 100644 --- a/engine/common/netchan.h +++ b/engine/common/netchan.h @@ -78,17 +78,38 @@ GNU General Public License for more details. #define PORT_SERVER 27015 #define MULTIPLAYER_BACKUP 64 // how many data slots to use when in multiplayer (must be power of 2) -#define SINGLEPLAYER_BACKUP 16 // same for single player +#define SINGLEPLAYER_BACKUP 16 // same for single player #define CMD_BACKUP 64 // allow a lot of command backups for very fast systems #define CMD_MASK (CMD_BACKUP - 1) #define NUM_PACKET_ENTITIES 256 // 170 Mb for multiplayer with 32 players #define MAX_CUSTOM_BASELINES 64 - #define NET_LEGACY_EXT_SPLIT (1U<<1) #define NETSPLIT_BACKUP 8 #define NETSPLIT_BACKUP_MASK (NETSPLIT_BACKUP - 1) #define NETSPLIT_HEADER_SIZE 18 +#if XASH_LOW_MEMORY == 2 + #undef MULTIPLAYER_BACKUP + #undef SINGLEPLAYER_BACKUP + #undef NUM_PACKET_ENTITIES + #undef MAX_CUSTOM_BASELINES + #undef NET_MAX_FRAGMENT + #define MULTIPLAYER_BACKUP 4 // breaks protocol in legacy mode, new protocol status unknown + #define SINGLEPLAYER_BACKUP 4 + #define NUM_PACKET_ENTITIES 32 + #define MAX_CUSTOM_BASELINES 8 + #define NET_MAX_FRAGMENT 32768 +#elif XASH_LOW_MEMORY == 1 + #undef SINGLEPLAYER_BACKUP + #undef NUM_PACKET_ENTITIES + #undef MAX_CUSTOM_BASELINES + #undef NET_MAX_FRAGMENT + #define SINGLEPLAYER_BACKUP 4 + #define NUM_PACKET_ENTITIES 64 + #define MAX_CUSTOM_BASELINES 8 + #define NET_MAX_FRAGMENT 32768 +#endif + typedef struct netsplit_chain_packet_s { // bool vector diff --git a/engine/common/protocol.h b/engine/common/protocol.h index a3221c75..d43e6aec 100644 --- a/engine/common/protocol.h +++ b/engine/common/protocol.h @@ -119,12 +119,15 @@ GNU General Public License for more details. #define MAX_SOUND_BITS 11 #define MAX_SOUNDS (1<realsize += sizeof( memheader_t ) + size + sizeof( int ); - mem = (memheader_t *)malloc( sizeof( memheader_t ) + size + sizeof( int )); + mem = (memheader_t *)Q_malloc( sizeof( memheader_t ) + size + sizeof( int )); if( mem == NULL ) Sys_Error( "Mem_Alloc: out of memory (alloc at %s:%i)\n", filename, fileline ); mem->filename = filename; @@ -74,7 +83,8 @@ void *_Mem_Alloc( byte *poolptr, size_t size, qboolean clear, const char *filena mem->prev = NULL; pool->chain = mem; if( mem->next ) mem->next->prev = mem; - if( clear ) memset((void *)((byte *)mem + sizeof( memheader_t )), 0, mem->size ); + if( clear ) + memset((void *)((byte *)mem + sizeof( memheader_t )), 0, mem->size ); return (void *)((byte *)mem + sizeof( memheader_t )); } @@ -128,7 +138,7 @@ static void Mem_FreeBlock( memheader_t *mem, const char *filename, int fileline pool->totalsize -= mem->size; pool->realsize -= sizeof( memheader_t ) + mem->size + sizeof( int ); - free( mem ); + Q_free( mem ); } void _Mem_Free( void *data, const char *filename, int fileline ) @@ -166,7 +176,7 @@ byte *_Mem_AllocPool( const char *name, const char *filename, int fileline ) { mempool_t *pool; - pool = (mempool_t *)malloc( sizeof( mempool_t )); + pool = (mempool_t *)Q_malloc( sizeof( mempool_t )); if( pool == NULL ) Sys_Error( "Mem_AllocPool: out of memory (allocpool at %s:%i)\n", filename, fileline ); memset( pool, 0, sizeof( mempool_t )); @@ -203,7 +213,7 @@ void _Mem_FreePool( byte **poolptr, const char *filename, int fileline ) while( pool->chain ) Mem_FreeBlock( pool->chain, filename, fileline ); // free the pool itself memset( pool, 0xBF, sizeof( mempool_t )); - free( pool ); + Q_free( pool ); *poolptr = NULL; } } diff --git a/engine/platform/android/vid_android.c b/engine/platform/android/vid_android.c index a447f5cb..65904097 100644 --- a/engine/platform/android/vid_android.c +++ b/engine/platform/android/vid_android.c @@ -327,14 +327,35 @@ qboolean VID_SetMode( void ) rserr_t R_ChangeDisplaySettings( int width, int height, qboolean fullscreen ) { + int render_w, render_h; + uint rotate = vid_rotate->value; + Android_GetScreenRes(&width, &height); + render_w = width; + render_h = height; + Con_Reportf( "R_ChangeDisplaySettings: forced resolution to %dx%d)\n", width, height); - R_SaveVideoMode( width, height ); + if( ref.dllFuncs.R_SetDisplayTransform( rotate, 0, 0, vid_scale->value, vid_scale->value ) ) + { + if( rotate & 1 ) + { + int swap = render_w; - host.window_center_x = width / 2; - host.window_center_y = height / 2; + render_w = render_h; + render_h = swap; + } + + render_h /= vid_scale->value; + render_w /= vid_scale->value; + } + else + { + Con_Printf( S_WARN "failed to setup screen transform\n" ); + } + + R_SaveVideoMode( width, height, render_w, render_h ); refState.wideScreen = true; // V_AdjustFov will check for widescreen diff --git a/engine/platform/linux/in_evdev.c b/engine/platform/linux/in_evdev.c index 98b81f57..3405a60e 100644 --- a/engine/platform/linux/in_evdev.c +++ b/engine/platform/linux/in_evdev.c @@ -37,6 +37,8 @@ struct evdev_s qboolean shift; } evdev; +static convar_t *evdev_keydebug; + static int KeycodeFromEvdev(int keycode, int value) { switch (keycode) { @@ -119,6 +121,10 @@ static int KeycodeFromEvdev(int keycode, int value) case BTN_LEFT: return K_MOUSE1; case BTN_RIGHT: return K_MOUSE2; case BTN_MIDDLE: return K_MOUSE3; + case KEY_POWER: return K_ESCAPE; + case KEY_VOLUMEDOWN: return K_PGDN; + case KEY_VOLUMEUP: return K_PGUP; + case KEY_PLAYPAUSE: return K_ENTER; default: break; } @@ -216,7 +222,7 @@ void Evdev_Autodetect_f( void ) open: Q_strncpy( evdev.paths[evdev.devices], path, MAX_STRING ); evdev.fds[evdev.devices++] = fd; - Msg( "Opened device %s\n", path ); + Con_Printf( "Opened device %s\n", path ); #if XASH_INPUT == INPUT_EVDEV if( Sys_CheckParm( "-grab" ) ) ioctl( evdev.fds[i], EVIOCGRAB, (void*) 1 ); @@ -244,7 +250,7 @@ void Evdev_OpenDevice ( const char *path ) if ( evdev.devices >= MAX_EVDEV_DEVICES ) { - Msg( "Only %d devices supported!\n", MAX_EVDEV_DEVICES ); + Con_Printf( "Only %d devices supported!\n", MAX_EVDEV_DEVICES ); return; } @@ -256,7 +262,7 @@ void Evdev_OpenDevice ( const char *path ) { if( !Q_strncmp( evdev.paths[i], path, MAX_STRING ) ) { - Msg( "device %s already open!\n", path ); + Con_Printf( "device %s already open!\n", path ); return; } } @@ -267,7 +273,7 @@ void Evdev_OpenDevice ( const char *path ) Con_Reportf( S_ERROR "Could not open input device %s: %s\n", path, strerror( errno ) ); return; } - Msg( "Input device #%d: %s opened sucessfully\n", evdev.devices, path ); + Con_Printf( "Input device #%d: %s opened sucessfully\n", evdev.devices, path ); evdev.fds[evdev.devices] = ret; Q_strncpy( evdev.paths[evdev.devices++], path, MAX_STRING ); @@ -280,7 +286,7 @@ void Evdev_OpenDevice ( const char *path ) void Evdev_OpenDevice_f( void ) { if( Cmd_Argc() < 2 ) - Msg( S_USAGE "evdev_opendevice \n" ); + Con_Printf( S_USAGE "evdev_opendevice \n" ); Evdev_OpenDevice( Cmd_Argv( 1 ) ); } @@ -308,13 +314,13 @@ void Evdev_CloseDevice_f ( void ) if( i >= evdev.devices ) { - Msg( "Device %s is not open\n", arg ); + Con_Printf( "Device %s is not open\n", arg ); return; } close( evdev.fds[i] ); evdev.devices--; - Msg( "Device %s closed successfully\n", evdev.paths[i] ); + Con_Printf( "Device %s closed successfully\n", evdev.paths[i] ); for( ; i < evdev.devices; i++ ) { @@ -360,7 +366,11 @@ void IN_EvdevFrame ( void ) else if ( ( ev.type == EV_KEY ) && (cls.key_dest == key_game || XASH_INPUT == INPUT_EVDEV ) ) { int key = KeycodeFromEvdev( ev.code, ev.value ); - Key_Event ( key , ev.value ); + + if( CVAR_TO_BOOL(evdev_keydebug) ) + Con_Printf( "key %d %d %d\n", ev.code, key, ev.value ); + + Key_Event( key , ev.value ); if( evdev.chars && ev.value ) { @@ -452,6 +462,7 @@ void Evdev_Shutdown( void ) Cmd_RemoveCommand( "evdev_open" ); Cmd_RemoveCommand( "evdev_close" ); Cmd_RemoveCommand( "evdev_autodetect" ); + evdev_keydebug = Cvar_Get( "evdev_keydebug", "0", 0, "print key events to console" ); for( i = 0; i < evdev.devices; i++ ) { diff --git a/engine/platform/linux/vid_fbdev.c b/engine/platform/linux/vid_fbdev.c index 49e1f0cb..065a211c 100644 --- a/engine/platform/linux/vid_fbdev.c +++ b/engine/platform/linux/vid_fbdev.c @@ -39,7 +39,7 @@ void GL_SwapBuffers( void ) { } -void FB_GetScreenRes(int *x, int *y) +void FB_GetScreenRes( int *x, int *y ) { *x = fb.vinfo.xres; *y = fb.vinfo.yres; @@ -121,16 +121,34 @@ qboolean VID_SetMode( void ) rserr_t R_ChangeDisplaySettings( int width, int height, qboolean fullscreen ) { + int render_w, render_h; + uint rotate = vid_rotate->value; + FB_GetScreenRes( &width, &height ); - Con_Reportf( "R_ChangeDisplaySettings: forced resolution to %dx%d)\n", width, height); + render_w = width; + render_h = height; - R_SaveVideoMode( width, height ); + Con_Reportf( "R_ChangeDisplaySettings: forced resolution to %dx%d)\n", width, height ); - host.window_center_x = width / 2; - host.window_center_y = height / 2; + if( ref.dllFuncs.R_SetDisplayTransform( rotate, 0, 0, vid_scale->value, vid_scale->value ) ) + { + if( rotate & 1 ) + { + int swap = render_w; - refState.wideScreen = true; // V_AdjustFov will check for widescreen + render_w = render_h; + render_h = swap; + } + + render_h /= vid_scale->value; + render_w /= vid_scale->value; + } + else + { + Con_Printf( S_WARN "failed to setup screen transform\n" ); + } + R_SaveVideoMode( width, height, render_w, render_h ); return rserr_ok; } @@ -204,6 +222,13 @@ void SW_UnlockBuffer( void ) qboolean SW_CreateBuffer( int width, int height, uint *stride, uint *bpp, uint *r, uint *g, uint *b ) { + if( width > fb.vinfo.xres_virtual || height > fb.vinfo.yres_virtual ) + { + Con_Printf( S_ERROR "requested size %dx%d not fit to framebuffer size %dx%d\n", + width, height, fb.vinfo.xres_virtual, fb.vinfo.yres_virtual ); + return false; + } + *bpp = fb.vinfo.bits_per_pixel >> 3; *stride = fb.vinfo.xres_virtual; *r = FB_BF_TO_MASK(fb.vinfo.red); diff --git a/engine/platform/sdl/events.c b/engine/platform/sdl/events.c index 09e0a9d5..5da4341a 100644 --- a/engine/platform/sdl/events.c +++ b/engine/platform/sdl/events.c @@ -423,14 +423,11 @@ static void SDLash_EventFilter( SDL_Event *event ) VID_RestoreScreenResolution(); break; case SDL_WINDOWEVENT_RESIZED: - case SDL_WINDOWEVENT_MAXIMIZED: { - int w = VID_MIN_WIDTH, h = VID_MIN_HEIGHT; if( vid_fullscreen->value ) break; - SDL_GL_GetDrawableSize( host.hWnd, &w, &h ); - R_SaveVideoMode( w, h ); + VID_SaveWindowSize( event->window.data1, event->window.data2 ); SCR_VidInit(); // tell the client.dll what vid_mode has changed break; } diff --git a/engine/platform/sdl/events.h b/engine/platform/sdl/events.h index d5102331..452f7286 100644 --- a/engine/platform/sdl/events.h +++ b/engine/platform/sdl/events.h @@ -28,7 +28,7 @@ void GL_InitExtensions( void ); qboolean GL_CreateContext( void ); qboolean GL_UpdateContext( void ); qboolean GL_DeleteContext( void ); - +void VID_SaveWindowSize( int width, int height ); #endif // XASH_SDL #endif // KEYWRAPPER_H diff --git a/engine/platform/sdl/vid_sdl.c b/engine/platform/sdl/vid_sdl.c index cc97150c..7816a38e 100644 --- a/engine/platform/sdl/vid_sdl.c +++ b/engine/platform/sdl/vid_sdl.c @@ -449,7 +449,38 @@ qboolean GL_UpdateContext( void ) return true; } -qboolean VID_SetScreenResolution( int width, int height ) +void VID_SaveWindowSize( int width, int height ) +{ + int render_w = width, render_h = height; + uint rotate = vid_rotate->value; + + if( !glw_state.software ) + SDL_GL_GetDrawableSize( host.hWnd, &render_w, &render_h ); + else + SDL_RenderSetLogicalSize( sw.renderer, width, height ); + + if( ref.dllFuncs.R_SetDisplayTransform( rotate, 0, 0, vid_scale->value, vid_scale->value ) ) + { + if( rotate & 1 ) + { + int swap = render_w; + + render_w = render_h; + render_h = swap; + } + + render_h /= vid_scale->value; + render_w /= vid_scale->value; + } + else + { + Con_Printf( S_WARN "failed to setup screen transform\n" ); + } + + R_SaveVideoMode( width, height, render_w, render_h ); +} + +static qboolean VID_SetScreenResolution( int width, int height ) { SDL_DisplayMode want, got; Uint32 wndFlags = 0; @@ -479,9 +510,8 @@ qboolean VID_SetScreenResolution( int width, int height ) SDL_SetWindowGrab( host.hWnd, SDL_TRUE ); SDL_SetWindowSize( host.hWnd, got.w, got.h ); - SDL_GL_GetDrawableSize( host.hWnd, &got.w, &got.h ); + VID_SaveWindowSize( got.w, got.h ); - R_SaveVideoMode( got.w, got.h ); return true; } @@ -672,9 +702,9 @@ qboolean VID_CreateWindow( int width, int height, qboolean fullscreen ) if( !GL_UpdateContext( )) return false; - SDL_GL_GetDrawableSize( host.hWnd, &width, &height ); } - R_SaveVideoMode( width, height ); + + VID_SaveWindowSize( width, height ); return true; } @@ -906,11 +936,7 @@ rserr_t R_ChangeDisplaySettings( int width, int height, qboolean fullscreen ) #endif SDL_SetWindowBordered( host.hWnd, SDL_TRUE ); SDL_SetWindowSize( host.hWnd, width, height ); - if( !glw_state.software ) - SDL_GL_GetDrawableSize( host.hWnd, &width, &height ); - else - SDL_RenderSetLogicalSize(sw.renderer, width, height); - R_SaveVideoMode( width, height ); + VID_SaveWindowSize( width, height ); } return rserr_ok; diff --git a/engine/platform/swap/kmalloc.c b/engine/platform/swap/kmalloc.c new file mode 100644 index 00000000..6e205b4f --- /dev/null +++ b/engine/platform/swap/kmalloc.c @@ -0,0 +1,488 @@ +/* $NetBSD: malloc.c,v 1.8 1997/04/07 03:12:14 christos Exp $ */ + +/* + * Copyright (c) 1983 Regents of the University of California. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#if defined(LIBC_SCCS) && !defined(lint) +#if 0 +static char *sccsid = "from: @(#)malloc.c 5.11 (Berkeley) 2/23/91"; +#else +static char *rcsid = "$NetBSD: malloc.c,v 1.8 1997/04/07 03:12:14 christos Exp $"; +#endif +#endif /* LIBC_SCCS and not lint */ + +/* + * malloc.c (Caltech) 2/21/82 + * Chris Kingsley, kingsley@cit-20. + * + * This is a very fast storage allocator. It allocates blocks of a small + * number of different sizes, and keeps free lists of each size. Blocks that + * don't exactly fit are passed up to the next larger size. In this + * implementation, the available sizes are 2^n-4 (or 2^n-10) bytes long. + * This is designed for use in a virtual memory environment. + */ + +#include +#include +#include +#include "platform/swap/swap.h" +/* #define MSTATS 1 */ + +#ifndef _WIN32 +#include +#define NULL 0 +#else + +#define u_char unsigned char +#define u_long unsigned long +#define getpagesize() 4096 +#define caddr_t size_t +#define bcopy(a,b,c) memcpy(b,a,c) + +#endif + + +static void morecore(int); +/* + * The overhead on a block is at least 4 bytes. When free, this space + * contains a pointer to the next free block, and the bottom two bits must + * be zero. When in use, the first byte is set to MAGIC, and the second + * byte is the size index. The remaining bytes are for alignment. + * If range checking is enabled then a second word holds the size of the + * requested block, less 1, rounded up to a multiple of sizeof(RMAGIC). + * The order of elements is critical: ov_magic must overlay the low order + * bits of ov_next, and ov_magic can not be a valid ov_next bit pattern. + */ +union overhead { + union overhead *ov_next; /* when free */ + struct { + u_char ovu_magic; /* magic number */ + u_char ovu_index; /* bucket # */ +#ifdef RCHECK + u_short ovu_rmagic; /* range magic number */ + u_long ovu_size; /* actual block size */ +#endif + } ovu; +#define ov_magic ovu.ovu_magic +#define ov_index ovu.ovu_index +#define ov_rmagic ovu.ovu_rmagic +#define ov_size ovu.ovu_size +}; + +static int findbucket( union overhead *freep, +int srchlen); +#define MAGIC 0xef /* magic # on accounting info */ +#define RMAGIC 0x5555 /* magic # on range info */ + +#ifdef RCHECK +#define RSLOP sizeof (u_short) +#else +#define RSLOP 0 +#endif + +/* + * nextf[i] is the pointer to the next free block of size 2^(i+3). The + * smallest allocatable block is 8 bytes. The overhead information + * precedes the data area returned to the user. + */ +#define NBUCKETS 30 +static union overhead *nextf[NBUCKETS]; +/* extern char *sbrk(); */ + +static int pagesz; /* page size */ +static int pagebucket; /* page size bucket */ + +#ifdef MSTATS +/* + * nmalloc[i] is the difference between the number of mallocs and frees + * for a given block size. + */ +static unsigned int nmalloc[NBUCKETS]; +#include +#endif + +#if defined(DEBUG) || defined(RCHECK) +#define ASSERT(p) if (!(p)) botch(__STRING(p)) +#include /* */ +static +botch(s) + char *s; +{ + fprintf(stderr, "\r\nassertion botched: %s\r\n", s); + (void) fflush(stderr); /* just in case user buffered it */ + abort(); +} +#else +#define ASSERT(p) +#endif + +#include + +/* */ +/* malloc */ +/* malloc */ +void * SWAP_Malloc( size_t nbytes ) +{ + register union overhead *op; + register int bucket; + register long n; + register unsigned amt; + + /* + * First time malloc is called, setup page size and + * align break pointer so all data will be page aligned. + */ + if (pagesz == 0) { + pagesz = n = getpagesize(); + op = (union overhead *)SWAP_Sbrk( 0 ); + n = n - sizeof (*op) - ((long)op & (n - 1)); + if (n < 0) + n += pagesz; + if (n) { + if (SWAP_Sbrk(n) == (char *)-1) { + return (NULL); + } + } + bucket = 0; + amt = 8; + while (pagesz > amt) { + amt <<= 1; + bucket++; + } + pagebucket = bucket; + } + /* + * Convert amount of memory requested into closest block size + * stored in hash buckets which satisfies request. + * Account for space used per block for accounting. + */ + if (nbytes <= (n = pagesz - sizeof (*op) - RSLOP)) { +#ifndef RCHECK + amt = 8; /* size of first bucket */ + bucket = 0; +#else + amt = 16; /* size of first bucket */ + bucket = 1; +#endif + n = -((long)sizeof (*op) + RSLOP); + } else { + amt = pagesz; + bucket = pagebucket; + } + while (nbytes > amt + n) { + amt <<= 1; + if (amt == 0) { + return (NULL); + } + bucket++; + } + /* + * If nothing in hash bucket right now, + * request more memory from the system. + */ + if ((op = nextf[bucket]) == NULL) { + morecore(bucket); + if ((op = nextf[bucket]) == NULL) { + return (NULL); + } + } + /* remove from linked list */ + nextf[bucket] = op->ov_next; + op->ov_magic = MAGIC; + op->ov_index = bucket; +#ifdef MSTATS + nmalloc[bucket]++; +#endif +#ifdef RCHECK + /* + * Record allocated size of block and + * bound space with magic numbers. + */ + op->ov_size = (nbytes + RSLOP - 1) & ~(RSLOP - 1); + op->ov_rmagic = RMAGIC; + *(u_short *)((caddr_t)(op + 1) + op->ov_size) = RMAGIC; +#endif + return ((char *)(op + 1)); +} + +void * SWAP_Calloc(size_t nelem, size_t elsize) { + void * ptr = SWAP_Malloc (nelem * elsize); + // Zero out the malloc'd block. + memset (ptr, 0, nelem * elsize); + return ptr; +} + + +/* + * Allocate more memory to the indicated bucket. + */ +static void +morecore(int bucket) +{ + register union overhead *op; + register long sz; /* size of desired block */ + long amt; /* amount to allocate */ + int nblks; /* how many blocks we get */ + + /* + * sbrk_size <= 0 only for big, FLUFFY, requests (about + * 2^30 bytes on a VAX, I think) or for a negative arg. + */ + sz = 1 << (bucket + 3); +#ifdef DEBUG + ASSERT(sz > 0); +#else + if (sz <= 0) + return; +#endif + if (sz < pagesz) { + amt = pagesz; + nblks = amt / sz; + } else { + amt = sz + pagesz; + nblks = 1; + } + op = (union overhead *)SWAP_Sbrk(amt); + /* no more room! */ + if ((long)op == -1) + return; + /* + * Add new memory allocated to that on + * free list for this hash bucket. + */ + nextf[bucket] = op; + while (--nblks > 0) { + op->ov_next = (union overhead *)((caddr_t)op + sz); + op = (union overhead *)((caddr_t)op + sz); + } +} +/* */ +/* free */ +/* free */ +void SWAP_Free( void *cp) + +{ + register long size; + register union overhead *op; + + if (cp == NULL) + return; + op = (union overhead *)((caddr_t)cp - sizeof (union overhead)); +#ifdef DEBUG + ASSERT(op->ov_magic == MAGIC); /* make sure it was in use */ +#else + if (op->ov_magic != MAGIC) + return; /* sanity */ +#endif +#ifdef RCHECK + ASSERT(op->ov_rmagic == RMAGIC); + ASSERT(*(u_short *)((caddr_t)(op + 1) + op->ov_size) == RMAGIC); +#endif + size = op->ov_index; + ASSERT(size < NBUCKETS); + op->ov_next = nextf[size]; /* also clobbers ov_magic */ + nextf[size] = op; +#ifdef MSTATS + nmalloc[size]--; +#endif +} + + +/* EDB: added size lookup */ + +size_t SWAP_MallocUsableSize(void * cp) +{ + register long size; + register union overhead *op; + + if (cp == NULL) + return 0; + op = (union overhead *)((caddr_t)cp - sizeof (union overhead)); +#ifdef DEBUG + ASSERT(op->ov_magic == MAGIC); /* make sure it was in use */ +#else + if (op->ov_magic != MAGIC) + return 0; /* sanity */ +#endif +#ifdef RCHECK + ASSERT(op->ov_rmagic == RMAGIC); + ASSERT(*(u_short *)((caddr_t)(op + 1) + op->ov_size) == RMAGIC); +#endif + return op->ov_index; +} +/* End EDB */ + + +/* + * When a program attempts "storage compaction" as mentioned in the + * old malloc man page, it realloc's an already freed block. Usually + * this is the last block it freed; occasionally it might be farther + * back. We have to search all the free lists for the block in order + * to determine its bucket: 1st we make one pass thru the lists + * checking only the first block in each; if that fails we search + * ``realloc_srchlen'' blocks in each list for a match (the variable + * is extern so the caller can modify it). If that fails we just copy + * however many bytes was given to realloc() and hope it's not huge. + */ +int realloc_srchlen = 4; /* 4 should be plenty, -1 =>'s whole list */ + +/* */ +/* realloc */ +/* realloc */ +void * +SWAP_Realloc( void *cp, +size_t nbytes) + +{ + register u_long onb; + register long i; + union overhead *op; + char *res; + int was_alloced = 0; + + if (cp == NULL) + return (SWAP_Malloc(nbytes)); + if (nbytes == 0) { + SWAP_Free (cp); + return NULL; + } + op = (union overhead *)((caddr_t)cp - sizeof (union overhead)); + if (op->ov_magic == MAGIC) { + was_alloced++; + i = op->ov_index; + } else { + /* + * Already free, doing "compaction". + * + * Search for the old block of memory on the + * free list. First, check the most common + * case (last element free'd), then (this failing) + * the last ``realloc_srchlen'' items free'd. + * If all lookups fail, then assume the size of + * the memory block being realloc'd is the + * largest possible (so that all "nbytes" of new + * memory are copied into). Note that this could cause + * a memory fault if the old area was tiny, and the moon + * is gibbous. However, that is very unlikely. + */ + if ((i = findbucket(op, 1)) < 0 && + (i = findbucket(op, realloc_srchlen)) < 0) + i = NBUCKETS; + } + onb = 1 << (i + 3); + if (onb < pagesz) + onb -= sizeof (*op) + RSLOP; + else + onb += pagesz - sizeof (*op) - RSLOP; + /* avoid the copy if same size block */ + if (was_alloced) { + if (i) { + i = 1 << (i + 2); + if (i < pagesz) + i -= sizeof (*op) + RSLOP; + else + i += pagesz - sizeof (*op) - RSLOP; + } + if (nbytes <= onb && nbytes > i) { +#ifdef RCHECK + op->ov_size = (nbytes + RSLOP - 1) & ~(RSLOP - 1); + *(u_short *)((caddr_t)(op + 1) + op->ov_size) = RMAGIC; +#endif + return(cp); + } else + SWAP_Free(cp); + } + if ((res = SWAP_Malloc(nbytes)) == NULL) + return (NULL); + if (cp != res) /* common optimization if "compacting" */ + bcopy(cp, res, (nbytes < onb) ? nbytes : onb); + return (res); +} + +/* + * Search ``srchlen'' elements of each free list for a block whose + * header starts at ``freep''. If srchlen is -1 search the whole list. + * Return bucket number, or -1 if not found. + */ +static int +findbucket( union overhead *freep, +int srchlen) + +{ + register union overhead *p; + register int i, j; + + for (i = 0; i < NBUCKETS; i++) { + j = 0; + for (p = nextf[i]; p && j != srchlen; p = p->ov_next) { + if (p == freep) + return (i); + j++; + } + } + return (-1); +} + +#ifdef MSTATS +/* + * mstats - print out statistics about malloc + * + * Prints two lines of numbers, one showing the length of the free list + * for each size category, the second showing the number of mallocs - + * frees for each size category. + */ +/* */ +mstats(void) +{ + register int i, j; + register union overhead *p; + int totfree = 0, + totused = 0; + + fprintf(stderr, "Memory allocation statistics\nfree:\t"); + for (i = 0; i < NBUCKETS; i++) { + for (j = 0, p = nextf[i]; p; p = p->ov_next, j++) + ; + fprintf(stderr, " %d", j); + totfree += j * (1 << (i + 3)); + } + fprintf(stderr, "\nused:\t"); + for (i = 0; i < NBUCKETS; i++) { + fprintf(stderr, " %d", nmalloc[i]); + totused += nmalloc[i] * (1 << (i + 3)); + } + fprintf(stderr, "\n\tTotal in use: %d, total free: %d, total allocated: %d\n", + totused, totfree, totused + totfree); +} +#endif + diff --git a/engine/platform/swap/sbrk.c b/engine/platform/swap/sbrk.c new file mode 100644 index 00000000..76af1990 --- /dev/null +++ b/engine/platform/swap/sbrk.c @@ -0,0 +1,108 @@ +/* +sbrk.c - swap memory allocation +Copyright (C) 2019 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. +*/ +#define _GNU_SOURCE +#include +#include +#include +#include +#include +#include +#include +#include +#include +#define _GNU_SOURCE +#include +#include "platform/swap/swap.h" +#include "string.h" + +#ifndef XASH_DEFAULT_SWAP_PATH +#define XASH_DEFAULT_SWAP_PATH "/tmp/xash3d-swap" +#endif +#define PAGE_SIZE 4096 +static struct sbrk_state_s +{ + void *top; + int fd; + size_t size; + size_t prealloc; + pid_t pid; +} s; + +static void SWAP_Initialize(void) +{ + char *path; + char *prealloc = getenv("SWAP_SIZE"); + int fd; + + if( s.top ) + return; + + path = getenv("SWAP_PATH"); + if( !path ) + path = XASH_DEFAULT_SWAP_PATH; + fd = open( path, O_CREAT|O_RDWR, 0600 ); + + if( prealloc ) s.prealloc = atoi(prealloc); + else s.prealloc = 128*1024*1024; + s.prealloc &= ~(PAGE_SIZE - 1); + + s.fd = fd; + ftruncate( fd, s.prealloc ); + s.top = mmap( 0, s.prealloc, PROT_READ|PROT_WRITE, MAP_SHARED, fd, 0 ); + + // space will be freed on exit + //unlink(path); +} + +void *SWAP_Sbrk(size_t size) +{ + char buf[64]; + SWAP_Initialize(); + + if( size == 0 ) + return s.top; + else if( size > 0 ) + { + void *res; + + //write(1, buf, snprintf(buf, 32, "allocating %d\n", size) ); + res = s.top; + s.size += size; + s.top = res + size; + if( s.size + size > s.prealloc ) + return (void*)-1; + + memset( res, 0, size ); + return res; + + } + else + { + void *res = s.top; + + if( -size > s.size ) + res = (void*)-1; + else + { + s.top += size; + s.size += size; + //write(1, buf, snprintf(buf, 32, "freed %d\n", -size) ); + } + + return res; + } +} + + diff --git a/engine/platform/swap/swap.h b/engine/platform/swap/swap.h new file mode 100644 index 00000000..342ca8e9 --- /dev/null +++ b/engine/platform/swap/swap.h @@ -0,0 +1,21 @@ +/* +sbrk_swap.h - swap memory allocation +Copyright (C) 2019 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 +void *SWAP_Sbrk( size_t size ); +void *SWAP_Malloc( size_t size ); +void *SWAP_Calloc( size_t nelem, size_t size ); +void SWAP_Free( void *cp ); +void *SWAP_Realloc( void *cp, size_t size ); +size_t SWAP_MallocUsableSize( void * cp ); diff --git a/engine/ref_api.h b/engine/ref_api.h index 9c4b546e..09e5321d 100644 --- a/engine/ref_api.h +++ b/engine/ref_api.h @@ -88,6 +88,7 @@ typedef struct ref_globals_s // viewport width and height int width; int height; + qboolean fullScreen; qboolean wideScreen; @@ -211,6 +212,14 @@ enum REF_GL_CONTEXT_RESET_ISOLATION_FLAG = 0x0008 }; +typedef enum ref_screen_rotation_e +{ + REF_ROTATE_NONE = 0, + REF_ROTATE_CW = 1, + REF_ROTATE_UD = 2, + REF_ROTATE_CCW = 3, +} ref_screen_rotation_t; + typedef struct remap_info_s { unsigned short textures[MAX_SKINS];// alias textures @@ -435,6 +444,7 @@ typedef struct ref_interface_s // const char *(*R_GetInitError)( void ); void (*R_Shutdown)( void ); const char *(*R_GetConfigName)( void ); // returns config name without extension + qboolean (*R_SetDisplayTransform)( ref_screen_rotation_t rotate, int x, int y, float scale_x, float scale_y ); // only called for GL contexts void (*GL_SetupAttributes)( int safegl ); diff --git a/engine/server/server.h b/engine/server/server.h index 59f604ea..4058f7e5 100644 --- a/engine/server/server.h +++ b/engine/server/server.h @@ -32,7 +32,11 @@ GNU General Public License for more details. //============================================================================= #define SV_UPDATE_MASK (SV_UPDATE_BACKUP - 1) +#if XASH_LOW_MEMORY == 2 +#define SV_UPDATE_BACKUP SINGLEPLAYER_BACKUP +#else extern int SV_UPDATE_BACKUP; +#endif // hostflags #define SVF_SKIPLOCALHOST BIT( 0 ) diff --git a/engine/server/sv_game.c b/engine/server/sv_game.c index a1ac66ba..b6b9c9c5 100644 --- a/engine/server/sv_game.c +++ b/engine/server/sv_game.c @@ -2042,10 +2042,10 @@ int SV_BuildSoundMsg( sizebuf_t *msg, edict_t *ent, int chan, const char *sample { sound_idx = Q_atoi( sample + 1 ); - if( sound_idx >= MAX_SOUNDS ) + if( sound_idx >= MAX_SOUNDS_NONSENTENCE ) { SetBits( flags, SND_SENTENCE|SND_SEQUENCE ); - sound_idx -= MAX_SOUNDS; + sound_idx -= MAX_SOUNDS_NONSENTENCE; } else SetBits( flags, SND_SENTENCE ); } diff --git a/engine/server/sv_init.c b/engine/server/sv_init.c index d3a296fa..88847b37 100644 --- a/engine/server/sv_init.c +++ b/engine/server/sv_init.c @@ -17,9 +17,9 @@ GNU General Public License for more details. #include "server.h" #include "net_encode.h" #include "library.h" - +#if XASH_LOW_MEMORY != 2 int SV_UPDATE_BACKUP = SINGLEPLAYER_BACKUP; - +#endif server_t sv; // local server server_static_t svs; // persistant server info svgame_static_t svgame; // persistant game info @@ -752,7 +752,9 @@ void SV_SetupClients( void ) // feedback for cvar Cvar_FullSet( "maxplayers", va( "%d", svs.maxclients ), FCVAR_LATCH ); +#if XASH_LOW_MEMORY != 2 SV_UPDATE_BACKUP = ( svs.maxclients == 1 ) ? SINGLEPLAYER_BACKUP : MULTIPLAYER_BACKUP; +#endif svs.clients = Z_Realloc( svs.clients, sizeof( sv_client_t ) * svs.maxclients ); svs.num_client_entities = svs.maxclients * SV_UPDATE_BACKUP * NUM_PACKET_ENTITIES; diff --git a/engine/wscript b/engine/wscript index 345f8220..c7baf2f2 100644 --- a/engine/wscript +++ b/engine/wscript @@ -14,6 +14,10 @@ def options(opt): help = 'enable console input from stdin (always enabled for dedicated) [default: %default]') grp.add_option('--fbdev', action = 'store_true', dest = 'FBDEV_SW', default = False, help = 'build fbdev-only software-only engine') + grp.add_option('--disable-async-resolve', action = 'store_true', dest = 'NO_ASYNC_RESOLVE', default = False, + help = 'disable asynchronous name resolution') + grp.add_option('--enable-custom-swap', action = 'store_true', dest = 'CUSTOM_SWAP', default = False, + help = 'enable custom swap allocator. For devices with no swap support') opt.load('sdl2') @@ -28,7 +32,8 @@ def configure(conf): conf.check_cc(lib = i) elif conf.options.FBDEV_SW: conf.define('XASH_FBDEV', 1) - conf.check_cc(lib = 'asound') + conf.check_cc( lib = 'asound' ) + conf.check_cc( lib = 'rt' ) else: conf.load('sdl2') if not conf.env.HAVE_SDL2: @@ -38,8 +43,15 @@ def configure(conf): if conf.options.USE_SELECT == None: conf.options.USE_SELECT = conf.options.DEDICATED - conf.define_cond('SINGLE_BINARY', conf.env.SINGLE_BINARY) + if not conf.env.DEST_OS in ['win32', 'android'] and not conf.options.NO_ASYNC_RESOLVE: + conf.check_cc( lib='pthread' ) + + if conf.options.CUSTOM_SWAP: + conf.define('XASH_CUSTOM_SWAP',1) + conf.env.CUSTOM_SWAP = True + conf.define_cond('SINGLE_BINARY', conf.env.SINGLE_BINARY) + conf.define_cond('XASH_NO_ASYNC_NS_RESOLVE', conf.options.NO_ASYNC_RESOLVE) conf.define_cond('XASH_USE_SELECT', conf.options.USE_SELECT or conf.options.DEDICATED) conf.define_cond('SUPPORT_BSP2_FORMAT', conf.options.SUPPORT_BSP2_FORMAT) conf.define_cond('XASH_64BIT', conf.env.DEST_SIZEOF_VOID_P != 4) @@ -57,8 +69,9 @@ def build(bld): # basic build: dedicated only, no dependencies if bld.env.DEST_OS != 'win32': - libs += [ 'DL' , 'M' , 'RT', 'PTHREAD'] + libs += [ 'DL' , 'M' , 'RT', 'PTHREAD', 'ASOUND'] source += bld.path.ant_glob(['platform/posix/*.c']) + else: libs += ['USER32', 'SHELL32', 'GDI32', 'ADVAPI32', 'DBGHELP', 'PSAPI', 'WS2_32' ] source += bld.path.ant_glob(['platform/win32/*.c']) @@ -66,7 +79,8 @@ def build(bld): if bld.env.DEST_OS == 'linux': source += bld.path.ant_glob(['platform/linux/*.c']) - source += bld.path.ant_glob(['platform/stub/*.c']) + if bld.env.CUSTOM_SWAP: + source += bld.path.ant_glob(['platform/swap/*.c']) if bld.env.HAVE_SDL2: libs.append('SDL2') @@ -83,12 +97,6 @@ def build(bld): 'client/vgui/*.c', 'client/avi/*.c']) - if bld.env.DEST_OS == 'linux' and not bld.env.HAVE_SDL2: - libs.append('RT') - if not bld.env.DEDICATED: - libs.append('ASOUND') - - # HACK: public headers must be put before common, so we don't get wrong mathlib included includes = ['common', 'server', 'client', 'client/vgui', '.', '../public', '../common', '../pm_shared' ] diff --git a/ref_gl/gl_context.c b/ref_gl/gl_context.c index 35505a13..e356f7fd 100644 --- a/ref_gl/gl_context.c +++ b/ref_gl/gl_context.c @@ -308,6 +308,30 @@ void R_ProcessEntData( qboolean allocate ) gEngfuncs.drawFuncs->R_ProcessEntData( allocate ); } +qboolean R_SetDisplayTransform( ref_screen_rotation_t rotate, int offset_x, int offset_y, float scale_x, float scale_y ) +{ + qboolean ret = true; + if( rotate > 0 ) + { + gEngfuncs.Con_Printf("rotation transform not supported\n"); + ret = false; + } + + if( offset_x || offset_y ) + { + gEngfuncs.Con_Printf("offset transform not supported\n"); + ret = false; + } + + if( scale_x != 1.0f || scale_y != 1.0f ) + { + gEngfuncs.Con_Printf("scale transform not supported\n"); + ret = false; + } + + return ret; +} + static const char *R_GetConfigName( void ) { return "opengl"; @@ -318,6 +342,7 @@ ref_interface_t gReffuncs = R_Init, R_Shutdown, R_GetConfigName, + R_SetDisplayTransform, GL_SetupAttributes, GL_InitExtensions, diff --git a/wscript b/wscript index 86142fdd..45c7fd38 100644 --- a/wscript +++ b/wscript @@ -76,6 +76,9 @@ def options(opt): grp.add_option('--enable-poly-opt', action = 'store_true', dest = 'POLLY', default = False, help = 'enable polyhedral optimization if possible [default: %default]') + grp.add_option('--low-memory-mode', action = 'store', dest = 'LOW_MEMORY', default = 0, + help = 'enable low memory mode (only for devices have <128 ram)') + opt.load('subproject') opt.add_subproject(subdirs()) @@ -108,7 +111,7 @@ def configure(conf): conf.env.MSVC_TARGETS = ['x86'] # explicitly request x86 target for MSVC if sys.platform == 'win32': conf.load('msvc msvc_pdb msdev msvs') - conf.load('subproject xcompile compiler_c compiler_cxx gitversion clang_compilation_database strip_on_install waf_unit_test pthreads') + conf.load('subproject xcompile compiler_c compiler_cxx gitversion clang_compilation_database strip_on_install waf_unit_test') # Every static library must have fPIC if conf.env.DEST_OS != 'win32' and '-fPIC' in conf.env.CFLAGS_cshlib: @@ -262,7 +265,6 @@ def configure(conf): conf.env.SINGLE_BINARY = conf.options.SINGLE_BINARY or conf.env.DEDICATED if conf.env.DEST_OS != 'win32': - conf.check_pthread_flag() conf.check_cc(lib='dl', mandatory=False) if not conf.env.LIB_M: # HACK: already added in xcompile! @@ -301,6 +303,9 @@ def configure(conf): conf.define('XASH_BUILD_COMMIT', conf.env.GIT_VERSION if conf.env.GIT_VERSION else 'notset') + if conf.options.LOW_MEMORY: + conf.define('XASH_LOW_MEMORY', int(conf.options.LOW_MEMORY)) + for i in SUBDIRS: if not i.is_enabled(conf): continue