diff --git a/common/render_api.h b/common/render_api.h index 59f5e6f5..cc55c540 100644 --- a/common/render_api.h +++ b/common/render_api.h @@ -116,6 +116,7 @@ typedef enum { GLES_WRAPPER_NONE = 0, // native GLES GLES_WRAPPER_NANOGL, // used on GLES platforms + GLES_WRAPPER_WES, // used on GLES platforms } gles_wrapper_t; // 30 bytes here @@ -229,7 +230,7 @@ typedef struct render_api_s void (*Host_Error)( const char *error, ... ); // cause Host Error void* ( *pfnGetModel )( int modelindex ); float (*pfnTime)( void ); // Sys_DoubleTime - void (*Cvar_Set)( char *name, char *value ); + void (*Cvar_Set)( const char *name, const char *value ); void (*S_FadeMusicVolume)( float fadePercent ); // fade background track (0-100 percents) void (*SetRandomSeed)( long lSeed ); // set custom seed for RANDOM_FLOAT\RANDOM_LONG for predictable random // ONLY ADD NEW FUNCTIONS TO THE END OF THIS STRUCT. INTERFACE VERSION IS FROZEN AT 37 @@ -263,4 +264,4 @@ typedef struct render_interface_s void (*R_ClearScene)( void ); } render_interface_t; -#endif//RENDER_API_H \ No newline at end of file +#endif//RENDER_API_H diff --git a/engine/client/cl_game.c b/engine/client/cl_game.c index a0ea8380..7b32838c 100644 --- a/engine/client/cl_game.c +++ b/engine/client/cl_game.c @@ -29,6 +29,9 @@ GNU General Public License for more details. #include "library.h" #include "vgui_draw.h" #include "sound.h" // SND_STOP_LOOPING +#ifdef XASH_SDL +#include +#endif #define MAX_LINELENGTH 80 #define MAX_TEXTCHANNELS 8 // must be power of two (GoldSrc uses 4 channels) @@ -1596,7 +1599,7 @@ pfnGetScreenInfo get actual screen info ============= */ -static int pfnGetScreenInfo( SCREENINFO *pscrinfo ) +int CL_GetScreenInfo( SCREENINFO *pscrinfo ) { // setup screen info clgame.scrInfo.iSize = sizeof( clgame.scrInfo ); @@ -2001,19 +2004,18 @@ static float pfnGetClientMaxspeed( void ) /* ============= -pfnGetMousePosition +CL_GetMousePosition ============= */ -static void pfnGetMousePosition( int *mx, int *my ) +void CL_GetMousePosition( int *mx, int *my ) { - POINT curpos; - - GetCursorPos( &curpos ); - ScreenToClient( host.hWnd, &curpos ); - - if( mx ) *mx = curpos.x; - if( my ) *my = curpos.y; +#ifdef XASH_SDL + SDL_GetMouseState( mx, my ); +#else + if( mx ) *mx = 0; + if( my ) *my = 0; +#endif } /* @@ -2723,7 +2725,10 @@ pfnGetMousePos */ void pfnGetMousePos( struct tagPOINT *ppt ) { - GetCursorPos( ppt ); + if( !ppt ) + return; + + CL_GetMousePosition( &ppt->x, &ppt->y ); } /* @@ -2734,7 +2739,9 @@ pfnSetMousePos */ void pfnSetMousePos( int mx, int my ) { - SetCursorPos( mx, my ); +#ifdef XASH_SDL + SDL_WarpMouseInWindow( host.hWnd, mx, my ); +#endif } /* @@ -2984,7 +2991,7 @@ int TriSpriteTexture( model_t *pSpriteModel, int frame ) if( gl_texturenum <= 0 || gl_texturenum > MAX_TEXTURES ) gl_texturenum = tr.defaultTexture; - GL_Bind( GL_TEXTURE0, gl_texturenum ); + GL_Bind( XASH_TEXTURE0, gl_texturenum ); return 1; } @@ -3665,7 +3672,7 @@ static cl_enginefunc_t gEngfuncs = SPR_DisableScissor, pfnSPR_GetList, CL_FillRGBA, - pfnGetScreenInfo, + CL_GetScreenInfo, pfnSetCrosshair, pfnCvar_RegisterClientVariable, Cvar_VariableValue, @@ -3702,7 +3709,7 @@ static cl_enginefunc_t gEngfuncs = pfnGetClientMaxspeed, COM_CheckParm, Key_Event, - pfnGetMousePosition, + CL_GetMousePosition, pfnIsNoClipping, CL_GetLocalPlayer, pfnGetViewModel, diff --git a/engine/client/cl_main.c b/engine/client/cl_main.c index b1992fac..f09498eb 100644 --- a/engine/client/cl_main.c +++ b/engine/client/cl_main.c @@ -21,6 +21,7 @@ GNU General Public License for more details. #include "input.h" #include "kbutton.h" #include "vgui_draw.h" +#include "library.h" #define MAX_TOTAL_CMDS 32 #define MAX_CMD_BUFFER 8000 @@ -63,6 +64,7 @@ convar_t *cl_cmdrate; convar_t *cl_interp; convar_t *cl_dlmax; convar_t *cl_lw; +convar_t *cl_charset; // // userinfo @@ -972,38 +974,6 @@ void CL_Drop( void ) CL_Disconnect(); } -/* -======================= -CL_GetCDKeyHash() - -Connections will now use a hashed cd key value -======================= -*/ -char *CL_GetCDKeyHash( void ) -{ - const char *keyBuffer; - static char szHashedKeyBuffer[256]; - int nKeyLength = 0; - byte digest[17]; // The MD5 Hash - MD5Context_t ctx; - - keyBuffer = Sys_GetMachineKey( &nKeyLength ); - - // now get the md5 hash of the key - memset( &ctx, 0, sizeof( ctx )); - memset( digest, 0, sizeof( digest )); - - MD5Init( &ctx ); - MD5Update( &ctx, (byte *)keyBuffer, nKeyLength ); - MD5Final( digest, &ctx ); - digest[16] = '\0'; - - memset( szHashedKeyBuffer, 0, 256 ); - Q_strncpy( szHashedKeyBuffer, MD5_Print( digest ), sizeof( szHashedKeyBuffer )); - - return szHashedKeyBuffer; -} - /* ======================= CL_SendConnectPacket @@ -1016,7 +986,7 @@ void CL_SendConnectPacket( void ) { char protinfo[MAX_INFO_STRING]; char *qport; - char *key; + const char *key; netadr_t adr; if( !NET_StringToAdr( cls.servername, &adr )) @@ -1028,7 +998,7 @@ void CL_SendConnectPacket( void ) if( adr.port == 0 ) adr.port = MSG_BigShort( PORT_SERVER ); qport = Cvar_VariableString( "net_qport" ); - key = CL_GetCDKeyHash(); + key = ID_GetMD5(); memset( protinfo, 0, sizeof( protinfo )); Info_SetValueForKey( protinfo, "uuid", key, sizeof( protinfo )); @@ -1771,8 +1741,9 @@ void CL_ConnectionlessPacket( netadr_t from, sizebuf_t *msg ) return; } - ShowWindow( host.hWnd, SW_RESTORE ); - SetForegroundWindow ( host.hWnd ); +#ifdef XASH_SDL + SDL_ShowWindow( host.hWnd ); +#endif args = MSG_ReadString( msg ); Cbuf_AddText( args ); Cbuf_AddText( "\n" ); @@ -1888,7 +1859,7 @@ void CL_ConnectionlessPacket( netadr_t from, sizebuf_t *msg ) nr->pfnFunc( &nr->resp ); // throw the list, now it will be stored in user area - prev = &((net_adrlist_t *)nr->resp.response); + prev = &nr->resp.response; while( 1 ) { @@ -2548,6 +2519,7 @@ void CL_InitLocal( void ) cl_solid_players = Cvar_Get( "cl_solid_players", "1", 0, "Make all players not solid (can't traceline them)" ); cl_interp = Cvar_Get( "ex_interp", "0.1", FCVAR_ARCHIVE, "Interpolate object positions starting this many seconds in past" ); cl_timeout = Cvar_Get( "cl_timeout", "60", 0, "connect timeout (in-seconds)" ); + cl_charset = Cvar_Get( "cl_charset", "utf-8", FCVAR_ARCHIVE, "1-byte charset to use (iconv style)" ); rcon_client_password = Cvar_Get( "rcon_password", "", 0, "remote control client password" ); rcon_address = Cvar_Get( "rcon_address", "", 0, "remote control address" ); @@ -2794,7 +2766,10 @@ void CL_Init( void ) // unreliable buffer. unsed for unreliable commands and voice stream MSG_Init( &cls.datagram, "cls.datagram", cls.datagram_buf, sizeof( cls.datagram_buf )); - if( !CL_LoadProgs( va( "%s/client.dll", GI->dll_path ))) + IN_TouchInit(); + + COM_ResetLibraryError(); + if( !CL_LoadProgs( va( "%s/%s", GI->dll_path, GI->client_lib))) Host_Error( "can't initialize client.dll\n" ); cls.initialized = true; diff --git a/engine/client/client.h b/engine/client/client.h index 10ac2e36..43af15b4 100644 --- a/engine/client/client.h +++ b/engine/client/client.h @@ -535,6 +535,8 @@ typedef struct long logo_xres; long logo_yres; float logo_length; + + qboolean use_text_api; } gameui_static_t; typedef struct @@ -688,6 +690,7 @@ extern convar_t *gl_showtextures; extern convar_t *cl_bmodelinterp; extern convar_t *cl_righthand; extern convar_t *cl_lw; // local weapons +extern convar_t *cl_charset; extern convar_t *cl_showevents; extern convar_t *scr_centertime; extern convar_t *scr_viewsize; @@ -816,10 +819,12 @@ model_t *CL_LoadModel( const char *modelname, int *index ); HSPRITE pfnSPR_Load( const char *szPicName ); HSPRITE pfnSPR_LoadExt( const char *szPicName, uint texFlags ); void PicAdjustSize( float *x, float *y, float *w, float *h ); +int CL_GetScreenInfo( SCREENINFO *pscrinfo ); void CL_FillRGBA( int x, int y, int width, int height, int r, int g, int b, int a ); void CL_PlayerTrace( float *start, float *end, int traceFlags, int ignore_pe, pmtrace_t *tr ); void CL_PlayerTraceExt( float *start, float *end, int traceFlags, int (*pfnIgnore)( physent_t *pe ), pmtrace_t *tr ); void CL_SetTraceHull( int hull ); +void CL_GetMousePosition( int *mx, int *my ); // TODO: move to input _inline cl_entity_t *CL_EDICT_NUM( int n ) { @@ -982,6 +987,10 @@ void Con_DrawDebug( void ); void Con_RunConsole( void ); void Con_DrawConsole( void ); void Con_DrawVersion( void ); +int Con_UtfProcessChar( int in ); +int Con_UtfProcessCharForce( int in ); +int Con_UtfMoveLeft( char *str, int pos ); +int Con_UtfMoveRight( char *str, int pos, int length ); void Con_DrawStringLen( const char *pText, int *length, int *height ); int Con_DrawString( int x, int y, const char *string, rgba_t setColor ); int Con_DrawCharacter( int x, int y, int number, rgba_t color ); @@ -994,6 +1003,10 @@ void Con_RestoreFont( void ); void Key_Console( int key ); void Key_Message( int key ); void Con_FastClose( void ); +void Con_Bottom( void ); +void Con_Top( void ); +void Con_PageDown( int lines ); +void Con_PageUp( int lines ); // // s_main.c @@ -1033,6 +1046,7 @@ qboolean UI_CreditsActive( void ); void UI_CharEvent( int key ); qboolean UI_MouseInRect( void ); qboolean UI_IsVisible( void ); +void UI_AddTouchButtonToList( const char *name, const char *texture, const char *command, unsigned char *color, int flags ); void pfnPIC_Set( HIMAGE hPic, int r, int g, int b, int a ); void pfnPIC_Draw( int x, int y, int width, int height, const wrect_t *prc ); void pfnPIC_DrawTrans( int x, int y, int width, int height, const wrect_t *prc ); diff --git a/engine/client/gl_local.h b/engine/client/gl_local.h index 9560ba01..11c62abc 100644 --- a/engine/client/gl_local.h +++ b/engine/client/gl_local.h @@ -441,27 +441,15 @@ void R_AliasInit( void ); // // gl_warp.c // -void R_InitSkyClouds( struct mip_s *mt, struct texture_s *tx, qboolean custom_palette ); +typedef struct mip_s mip_t; +void R_InitSkyClouds( mip_t *mt, struct texture_s *tx, qboolean custom_palette ); void R_AddSkyBoxSurface( msurface_t *fa ); void R_ClearSkyBox( void ); void R_DrawSkyBox( void ); void R_DrawClouds( void ); void EmitWaterPolys( msurface_t *warp, qboolean reverse ); -// -// gl_vidnt.c -// -#define GL_CheckForErrors() GL_CheckForErrors_( __FILE__, __LINE__ ) -void GL_CheckForErrors_( const char *filename, const int fileline ); -const char *VID_GetModeString( int vid_mode ); -void *GL_GetProcAddress( const char *name ); -void GL_UpdateSwapInterval( void ); -qboolean GL_DeleteContext( void ); -qboolean GL_Support( int r_ext ); -void VID_CheckChanges( void ); -int GL_MaxTextureUnits( void ); -qboolean R_Init( void ); -void R_Shutdown( void ); +#include "vid_common.h" // // renderer exports @@ -542,9 +530,12 @@ enum enum { GL_KEEP_UNIT = -1, - GL_TEXTURE0 = 0, - GL_TEXTURE1, // used in some cases - MAX_TEXTURE_UNITS = 32 // can't acess to all over units without GLSL or cg + XASH_TEXTURE0 = 0, + XASH_TEXTURE1, + XASH_TEXTURE2, + XASH_TEXTURE3, // g-cont. 4 units should be enough + XASH_TEXTURE4, // mittorn. bump+detail needs 5 for single-pass + MAX_TEXTURE_UNITS = 32 // can't access to all over units without GLSL or cg }; typedef enum @@ -565,7 +556,6 @@ typedef struct // list of supported extensions const char *extensions_string; - const char *wgl_extensions_string; byte extension[GL_EXTCOUNT]; int max_texture_units; @@ -587,11 +577,14 @@ typedef struct int alpha_bits; int depth_bits; int stencil_bits; + int msaasamples; gl_context_type_t context; gles_wrapper_t wrapper; qboolean softwareGammaUpdate; + int prev_width; + int prev_height; int prev_mode; } glconfig_t; @@ -614,9 +607,19 @@ typedef struct qboolean in2DMode; } glstate_t; +typedef enum +{ + SAFE_NO, + SAFE_NOACC, + SAFE_NODEPTH, + SAFE_NOATTRIB, + SAFE_DONTCARE +} safe_context_t; + typedef struct { void* context; // handle to GL rendering context + safe_context_t safe; int desktopBitsPixel; int desktopWidth; @@ -647,6 +650,9 @@ extern convar_t *gl_finish; extern convar_t *gl_nosort; extern convar_t *gl_clear; extern convar_t *gl_test; // cvar to testify new effects +extern convar_t *gl_msaa; +extern convar_t *gl_stencilbits; + extern convar_t *r_speeds; extern convar_t *r_fullbright; @@ -672,5 +678,11 @@ extern convar_t *vid_fullscreen; extern convar_t *vid_brightness; extern convar_t *vid_gamma; extern convar_t *vid_mode; +extern convar_t *vid_highdpi; + +extern convar_t *window_xpos; +extern convar_t *window_ypos; +extern convar_t *scr_width; +extern convar_t *scr_height; #endif//GL_LOCAL_H diff --git a/engine/client/gl_rmain.c b/engine/client/gl_rmain.c index 6798128a..369724d6 100644 --- a/engine/client/gl_rmain.c +++ b/engine/client/gl_rmain.c @@ -1115,11 +1115,11 @@ void R_EndFrame( void ) // flush any remaining 2D bits R_Set2DMode( false ); - if( !pwglSwapBuffers( glw_state.hDC )) - { - Con_Printf( S_ERROR "failed to swap buffers\n" ); - Host_NewInstance( va("#%s", GI->gamefolder ), "stopped" ); - } +#ifdef XASH_SDL + SDL_GL_SwapWindow( host.hWnd ); +#elif defined __ANDROID__ // For direct android backend + Android_SwapBuffers(); +#endif } /* @@ -1538,4 +1538,4 @@ qboolean R_InitRenderAPI( void ) // render interface is missed return true; -} \ No newline at end of file +} diff --git a/engine/client/vox.h b/engine/client/vox.h index d043dd9a..7391e2d9 100644 --- a/engine/client/vox.h +++ b/engine/client/vox.h @@ -41,7 +41,8 @@ typedef struct float length; } sentence_t; -void VOX_LoadWord( struct channel_s *pchan ); -void VOX_FreeWord( struct channel_s *pchan ); +typedef struct channel_s channel_t; +void VOX_LoadWord( channel_t *pchan ); +void VOX_FreeWord( channel_t *pchan ); -#endif \ No newline at end of file +#endif diff --git a/engine/common/common.h b/engine/common/common.h index 8f305608..873f24ad 100644 --- a/engine/common/common.h +++ b/engine/common/common.h @@ -208,6 +208,8 @@ typedef enum #define FS_Title() SI.GameInfo->title #define GameState (&host.game) +#define FORCE_DRAW_VERSION_TIME 5.0f // draw version for 5 seconds + #ifdef _DEBUG void DBG_AssertFunction( qboolean fExpr, const char* szExpr, const char* szFile, int szLine, const char* szMessage ); #define Assert( f ) DBG_AssertFunction( f, #f, __FILE__, __LINE__, NULL ) @@ -281,8 +283,19 @@ typedef struct gameinfo_s int max_tents; // min temp ents is 300, max is 2048 int max_beams; // min beams is 64, max beams is 512 int max_particles; // min particles is 4096, max particles is 32768 + + char game_dll_linux[64]; // custom path for game.dll + char game_dll_osx[64]; // custom path for game.dll + char client_lib[64]; // custom name of client library } gameinfo_t; +typedef enum +{ + GAME_NORMAL, + GAME_SINGLEPLAYER_ONLY, + GAME_MULTIPLAYER_ONLY +} gametype_t; + typedef struct sysinfo_s { string exeName; // exe.filename @@ -451,6 +464,7 @@ typedef struct host_parm_s qboolean mouse_visible; // vgui override cursor control qboolean shutdown_issued; // engine is shutting down qboolean force_draw_version; // used when fraps is loaded + float force_draw_version_time; qboolean write_to_clipboard; // put image to clipboard instead of disk qboolean apply_game_config; // when true apply only to game cvars and ignore all other commands qboolean config_executed; // a bit who indicated was config.cfg already executed e.g. from valve.rc @@ -478,6 +492,7 @@ typedef struct host_parm_s struct decallist_s *decalList; // used for keep decals, when renderer is restarted or changed int numdecals; + } host_parm_t; extern host_parm_t host; @@ -505,6 +520,7 @@ const char *COM_FileWithoutPath( const char *in ); byte *W_LoadLump( wfile_t *wad, const char *lumpname, size_t *lumpsizeptr, const char type ); void W_Close( wfile_t *wad ); byte *FS_LoadFile( const char *path, long *filesizeptr, qboolean gamedironly ); +byte *FS_LoadDirectFile( const char *path, long *filesizeptr ); qboolean FS_WriteFile( const char *filename, const void *data, long len ); qboolean COM_ParseVector( char **pfile, float *v, size_t size ); void COM_NormalizeAngles( vec3_t angles ); @@ -526,6 +542,7 @@ long FS_FileTime( const char *filename, qboolean gamedironly ); int FS_Print( file_t *file, const char *msg ); qboolean FS_Rename( const char *oldname, const char *newname ); qboolean FS_FileExists( const char *filename, qboolean gamedironly ); +qboolean FS_SysFileExists( const char *path, qboolean casesensitive ); qboolean FS_FileCopy( file_t *pOutput, file_t *pInput, int fileSize ); qboolean FS_Delete( const char *path ); int FS_UnGetc( file_t *file, byte c ); @@ -910,6 +927,7 @@ 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" @@ -1026,7 +1044,7 @@ void Cmd_Null_f( void ); // soundlib shared exports qboolean S_Init( void ); void S_Shutdown( void ); -void S_Activate( qboolean active, void *hInst ); +void S_Activate( qboolean active ); void S_StopSound( int entnum, int channel, const char *soundname ); int S_GetCurrentStaticSounds( soundlist_t *pout, int size ); void S_StopBackgroundTrack( void ); @@ -1037,6 +1055,13 @@ void BuildGammaTable( float gamma, float brightness ); byte LightToTexGamma( byte b ); byte TextureToGamma( byte b ); +// +// identification.c +// +void ID_Init( void ); +const char *ID_GetMD5( void ); +void GAME_EXPORT ID_SetCustomClientID( const char *id ); + #ifdef __cplusplus } #endif diff --git a/engine/common/host_state.c b/engine/common/host_state.c index dfbc7ed2..41069779 100644 --- a/engine/common/host_state.c +++ b/engine/common/host_state.c @@ -15,6 +15,10 @@ GNU General Public License for more details. #include "common.h" +#ifdef XASH_SDL +#include "platform/sdl/events.h" +#endif + void COM_InitHostState( void ) { memset( GameState, 0, sizeof( game_status_t )); @@ -114,6 +118,12 @@ void Host_ShutdownGame( void ) void Host_RunFrame( float time ) { +#if XASH_INPUT == INPUT_SDL + SDLash_RunEvents(); +#elif XASH_INPUT == INPUT_ANDROID + Android_RunEvents(); +#endif + // engine main frame Host_Frame( time ); @@ -178,4 +188,4 @@ void COM_Frame( float time ) if(( GameState->curstate == oldState ) || ( ++loopCount > 8 )) Sys_Error( "state infinity loop!\n" ); } -} \ No newline at end of file +} diff --git a/engine/common/mod_local.h b/engine/common/mod_local.h index 9f1ebeb1..e2a03229 100644 --- a/engine/common/mod_local.h +++ b/engine/common/mod_local.h @@ -175,6 +175,10 @@ void Mod_PrintWorldStats_f( void ); // // mod_studio.c // +typedef struct studiohdr_s studiohdr_t; +typedef struct mstudioseqdesc_s mstudioseqdesc_t; +typedef struct mstudiobone_s mstudiobone_t; +typedef struct mstudioanim_s mstudioanim_t; void Mod_InitStudioAPI( void ); void Mod_InitStudioHull( void ); void Mod_ResetStudioAPI( void ); @@ -184,13 +188,13 @@ void Mod_StudioGetAttachment( const edict_t *e, int iAttachment, float *org, flo void Mod_GetBonePosition( const edict_t *e, int iBone, float *org, float *ang ); hull_t *Mod_HullForStudio( model_t *m, float frame, int seq, vec3_t ang, vec3_t org, vec3_t size, byte *pcnt, byte *pbl, int *hitboxes, edict_t *ed ); void R_StudioSlerpBones( int numbones, vec4_t q1[], float pos1[][3], vec4_t q2[], float pos2[][3], float s ); -void R_StudioCalcBoneQuaternion( int frame, float s, void *pbone, void *panim, float *adj, vec4_t q ); -void R_StudioCalcBonePosition( int frame, float s, void *pbone, void *panim, vec3_t adj, vec3_t pos ); -void *R_StudioGetAnim( void *m_pStudioHeader, void *m_pSubModel, void *pseqdesc ); +void R_StudioCalcBoneQuaternion( int frame, float s, mstudiobone_t *pbone, mstudioanim_t *panim, float *adj, vec4_t q ); +void R_StudioCalcBonePosition( int frame, float s, mstudiobone_t *pbone, mstudioanim_t *panim, vec3_t adj, vec3_t pos ); +void *R_StudioGetAnim( studiohdr_t *m_pStudioHeader, model_t *m_pSubModel, mstudioseqdesc_t *pseqdesc ); void Mod_StudioComputeBounds( void *buffer, vec3_t mins, vec3_t maxs, qboolean ignore_sequences ); void Mod_StudioLoadTextures( model_t *mod, void *data ); void Mod_StudioUnloadTextures( void *data ); int Mod_HitgroupForStudioHull( int index ); void Mod_ClearStudioCache( void ); -#endif//MOD_LOCAL_H \ No newline at end of file +#endif//MOD_LOCAL_H diff --git a/engine/common/system.h b/engine/common/system.h index c59b3f87..8c59eb5a 100644 --- a/engine/common/system.h +++ b/engine/common/system.h @@ -92,7 +92,8 @@ void Sys_SetupCrashHandler( void ); void Sys_RestoreCrashHandler( void ); void Sys_SetClipboardData( const byte *buffer, size_t size ); #define Sys_GetParmFromCmdLine( parm, out ) _Sys_GetParmFromCmdLine( parm, out, sizeof( out )) -qboolean _Sys_GetParmFromCmdLine( char *parm, char *out, size_t size ); +qboolean _Sys_GetParmFromCmdLine( const char *parm, char *out, size_t size ); +qboolean Sys_GetIntFromCmdLine( const char *parm, int *out ); void Sys_ShellExecute( const char *path, const char *parms, qboolean exit ); void Sys_SendKeyEvents( void ); void Sys_Print( const char *pMsg ); diff --git a/engine/platform/sdl/events.c b/engine/platform/sdl/events.c new file mode 100644 index 00000000..c8d492ee --- /dev/null +++ b/engine/platform/sdl/events.c @@ -0,0 +1,676 @@ +/* +events.c - SDL event system handlers +Copyright (C) 2015-2017 a1batross + +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. +*/ + +#ifdef XASH_SDL +#include + +#include "common.h" +#include "keydefs.h" +#include "input.h" +#include "client.h" +#include "vgui_draw.h" +#include "events.h" +#include "sound.h" +#include "vid_common.h" +#include "gl_local.h" + +extern convar_t *vid_fullscreen; +extern convar_t *snd_mute_losefocus; +static int wheelbutton; +static SDL_Joystick *joy; +static SDL_GameController *gamecontroller; + +void R_ChangeDisplaySettingsFast( int w, int h ); + +/* +============= +SDLash_KeyEvent + +============= +*/ +static void SDLash_KeyEvent( SDL_KeyboardEvent key, int down ) +{ + int keynum = key.keysym.scancode; + qboolean numLock = SDL_GetModState() & KMOD_NUM; + + if( SDL_IsTextInputActive() && down ) + { + if( SDL_GetModState() & KMOD_CTRL ) + { + if( keynum >= SDL_SCANCODE_A && keynum <= SDL_SCANCODE_Z ) + { + keynum = keynum - SDL_SCANCODE_A + 1; + CL_CharEvent( keynum ); + } + + return; + } + } + +#define DECLARE_KEY_RANGE( min, max, repl ) \ + if( keynum >= (min) && keynum <= (max) ) \ + { \ + keynum = keynum - (min) + (repl); \ + } + + DECLARE_KEY_RANGE( SDL_SCANCODE_A, SDL_SCANCODE_Z, 'a' ) + else DECLARE_KEY_RANGE( SDL_SCANCODE_1, SDL_SCANCODE_9, '1' ) + else DECLARE_KEY_RANGE( SDL_SCANCODE_F1, SDL_SCANCODE_F12, K_F1 ) +#undef DECLARE_KEY_RANGE + else + { + switch( keynum ) + { + case SDL_SCANCODE_GRAVE: keynum = '`'; break; + case SDL_SCANCODE_0: keynum = '0'; break; + case SDL_SCANCODE_BACKSLASH: keynum = '\\'; break; + case SDL_SCANCODE_LEFTBRACKET: keynum = '['; break; + case SDL_SCANCODE_RIGHTBRACKET: keynum = ']'; break; + case SDL_SCANCODE_EQUALS: keynum = '='; break; + case SDL_SCANCODE_MINUS: keynum = '-'; break; + case SDL_SCANCODE_TAB: keynum = K_TAB; break; + case SDL_SCANCODE_RETURN: keynum = K_ENTER; break; + case SDL_SCANCODE_ESCAPE: keynum = K_ESCAPE; break; + case SDL_SCANCODE_SPACE: keynum = K_SPACE; break; + case SDL_SCANCODE_BACKSPACE: keynum = K_BACKSPACE; break; + case SDL_SCANCODE_UP: keynum = K_UPARROW; break; + case SDL_SCANCODE_LEFT: keynum = K_LEFTARROW; break; + case SDL_SCANCODE_DOWN: keynum = K_DOWNARROW; break; + case SDL_SCANCODE_RIGHT: keynum = K_RIGHTARROW; break; + case SDL_SCANCODE_LALT: + case SDL_SCANCODE_RALT: keynum = K_ALT; break; + case SDL_SCANCODE_LCTRL: + case SDL_SCANCODE_RCTRL: keynum = K_CTRL; break; + case SDL_SCANCODE_LSHIFT: + case SDL_SCANCODE_RSHIFT: keynum = K_SHIFT; break; + case SDL_SCANCODE_LGUI: + case SDL_SCANCODE_RGUI: keynum = K_WIN; break; + case SDL_SCANCODE_INSERT: keynum = K_INS; break; + case SDL_SCANCODE_DELETE: keynum = K_DEL; break; + case SDL_SCANCODE_PAGEDOWN: keynum = K_PGDN; break; + case SDL_SCANCODE_PAGEUP: keynum = K_PGUP; break; + case SDL_SCANCODE_HOME: keynum = K_HOME; break; + case SDL_SCANCODE_END: keynum = K_END; break; + case SDL_SCANCODE_KP_1: keynum = numLock ? '1' : K_KP_END; break; + case SDL_SCANCODE_KP_2: keynum = numLock ? '2' : K_KP_DOWNARROW; break; + case SDL_SCANCODE_KP_3: keynum = numLock ? '3' : K_KP_PGDN; break; + case SDL_SCANCODE_KP_4: keynum = numLock ? '4' : K_KP_LEFTARROW; break; + case SDL_SCANCODE_KP_5: keynum = numLock ? '5' : K_KP_5; break; + case SDL_SCANCODE_KP_6: keynum = numLock ? '6' : K_KP_RIGHTARROW; break; + case SDL_SCANCODE_KP_7: keynum = numLock ? '7' : K_KP_HOME; break; + case SDL_SCANCODE_KP_8: keynum = numLock ? '8' : K_KP_UPARROW; break; + case SDL_SCANCODE_KP_9: keynum = numLock ? '9' : K_KP_PGUP; break; + case SDL_SCANCODE_KP_0: keynum = numLock ? '0' : K_KP_INS; break; + case SDL_SCANCODE_KP_PERIOD: keynum = K_KP_DEL; break; + case SDL_SCANCODE_KP_ENTER: keynum = K_KP_ENTER; break; + case SDL_SCANCODE_KP_PLUS: keynum = K_KP_PLUS; break; + case SDL_SCANCODE_KP_MINUS: keynum = K_KP_MINUS; break; + case SDL_SCANCODE_KP_DIVIDE: keynum = K_KP_SLASH; break; + case SDL_SCANCODE_KP_MULTIPLY: keynum = '*'; break; + case SDL_SCANCODE_NUMLOCKCLEAR: keynum = K_KP_NUMLOCK; break; + case SDL_SCANCODE_CAPSLOCK: keynum = K_CAPSLOCK; break; + case SDL_SCANCODE_APPLICATION: keynum = K_WIN; break; // (compose key) ??? + case SDL_SCANCODE_SLASH: keynum = '/'; break; + case SDL_SCANCODE_PERIOD: keynum = '.'; break; + case SDL_SCANCODE_SEMICOLON: keynum = ';'; break; + case SDL_SCANCODE_APOSTROPHE: keynum = '\''; break; + case SDL_SCANCODE_COMMA: keynum = ','; break; + case SDL_SCANCODE_PRINTSCREEN: + { + host.force_draw_version = true; + host.force_draw_version_time = host.realtime + FORCE_DRAW_VERSION_TIME; + break; + } + // don't console spam on known functional buttons, but not used in engine + case SDL_SCANCODE_MUTE: + case SDL_SCANCODE_VOLUMEUP: + case SDL_SCANCODE_VOLUMEDOWN: + case SDL_SCANCODE_BRIGHTNESSDOWN: + case SDL_SCANCODE_BRIGHTNESSUP: + return; + case SDL_SCANCODE_UNKNOWN: + { + if( down ) MsgDev( D_INFO, "SDLash_KeyEvent: Unknown scancode\n" ); + return; + } + default: + if( down ) MsgDev( D_INFO, "SDLash_KeyEvent: Unknown key: %s = %i\n", SDL_GetScancodeName( keynum ), keynum ); + return; + } + } + + Key_Event( keynum, down ); +} + +/* +============= +SDLash_MouseEvent + +============= +*/ +static void SDLash_MouseEvent( SDL_MouseButtonEvent button ) +{ + int down = button.type == SDL_MOUSEBUTTONDOWN ? 1 : 0; + if( in_mouseinitialized && !m_ignore->value && button.which != SDL_TOUCH_MOUSEID ) + { + Key_Event( K_MOUSE1 - 1 + button.button, down ); + } +} + +/* +============= +SDLash_InputEvent + +============= +*/ +static void SDLash_InputEvent( SDL_TextInputEvent input ) +{ + int i; + + // Pass characters one by one to Con_CharEvent + for(i = 0; input.text[i]; ++i) + { + int ch; + + if( !Q_stricmp( cl_charset->string, "utf-8" ) ) + ch = (unsigned char)input.text[i]; + else + ch = Con_UtfProcessCharForce( (unsigned char)input.text[i] ); + + if( !ch ) + continue; + + CL_CharEvent( ch ); + } +} + +/* +============= +SDLash_EnableTextInput + +============= +*/ +void SDLash_EnableTextInput( int enable, qboolean force ) +{ + if( force ) + { + if( enable ) + SDL_StartTextInput(); + else + SDL_StopTextInput(); + } + else if( enable ) + { + if( !host.textmode ) + { + SDL_StartTextInput(); + } + host.textmode = true; + } + else + { + SDL_StopTextInput(); + host.textmode = false; + } +} + +/* +============= +SDLash_EventFilter + +============= +*/ +static void SDLash_EventFilter( SDL_Event *event ) +{ + static int mdown; + + if( wheelbutton ) + { + Key_Event( wheelbutton, false ); + wheelbutton = 0; + } + + switch ( event->type ) + { + /* Mouse events */ + case SDL_MOUSEMOTION: + if( !host.mouse_visible && event->motion.which != SDL_TOUCH_MOUSEID ) + IN_MouseEvent(); +#ifdef TOUCHEMU + if( mdown ) + IN_TouchEvent( event_motion, 0, + event->motion.x/scr_width->value, + event->motion.y/scr_height->value, + event->motion.xrel/scr_width->value, + event->motion.yrel/scr_height->value ); + SDL_ShowCursor( true ); +#endif + break; + + case SDL_MOUSEBUTTONUP: +#ifdef TOUCHEMU + mdown = 0; + IN_TouchEvent( event_up, 0, + event->button.x/scr_width->value, + event->button.y/scr_height->value, 0, 0); +#else + SDLash_MouseEvent( event->button ); +#endif + break; + case SDL_MOUSEBUTTONDOWN: +#ifdef TOUCHEMU + mdown = 1; + IN_TouchEvent( event_down, 0, + event->button.x/scr_width->value, + event->button.y/scr_height->value, 0, 0); +#else + SDLash_MouseEvent( event->button ); +#endif + break; + + case SDL_MOUSEWHEEL: + wheelbutton = event->wheel.y < 0 ? K_MWHEELDOWN : K_MWHEELUP; + Key_Event( wheelbutton, true ); + break; + + /* Keyboard events */ + case SDL_KEYDOWN: + SDLash_KeyEvent( event->key, 1 ); + break; + + case SDL_KEYUP: + SDLash_KeyEvent( event->key, 0 ); + break; + + + /* Touch events */ + case SDL_FINGERDOWN: + case SDL_FINGERUP: + case SDL_FINGERMOTION: + { + touchEventType type; + static int scale = 0; + float x, y, dx, dy; + + if( event->type == SDL_FINGERDOWN ) + type = event_down; + else if( event->type == SDL_FINGERUP ) + type = event_up ; + else if(event->type == SDL_FINGERMOTION ) + type = event_motion; + else break; + + /* + SDL sends coordinates in [0..width],[0..height] values + on some devices + */ + if( !scale ) + { + if( ( event->tfinger.x > 0 ) && ( event->tfinger.y > 0 ) ) + { + if( ( event->tfinger.x > 2 ) && ( event->tfinger.y > 2 ) ) + { + scale = 2; + MsgDev( D_INFO, "SDL reports screen coordinates, workaround enabled!\n"); + } + else + { + scale = 1; + } + } + } + + x = event->tfinger.x; + y = event->tfinger.y; + dx = event->tfinger.dx; + dy = event->tfinger.dy; + + if( scale == 2 ) + { + x /= (float)glState.width; + y /= (float)glState.height; + dx /= (float)glState.width; + dy /= (float)glState.height; + } + + IN_TouchEvent( type, event->tfinger.fingerId, x, y, dx, dy ); + break; + } + + + /* IME */ + case SDL_TEXTINPUT: + SDLash_InputEvent( event->text ); + break; + + /* Joystick events */ + case SDL_JOYAXISMOTION: + Joy_AxisMotionEvent( event->jaxis.which, event->jaxis.axis, event->jaxis.value ); + break; + + case SDL_JOYBALLMOTION: + Joy_BallMotionEvent( event->jball.which, event->jball.ball, event->jball.xrel, event->jball.yrel ); + break; + + case SDL_JOYHATMOTION: + Joy_HatMotionEvent( event->jhat.which, event->jhat.hat, event->jhat.value ); + break; + + case SDL_JOYBUTTONDOWN: + case SDL_JOYBUTTONUP: + Joy_ButtonEvent( event->jbutton.which, event->jbutton.button, event->jbutton.state ); + break; + + case SDL_JOYDEVICEADDED: + Joy_AddEvent( event->jdevice.which ); + break; + case SDL_JOYDEVICEREMOVED: + Joy_RemoveEvent( event->jdevice.which ); + break; + + /* GameController API */ + case SDL_CONTROLLERAXISMOTION: + if( event->caxis.axis == (Uint8)SDL_CONTROLLER_AXIS_INVALID ) + break; + + // Swap axis to follow default axis binding: + // LeftX, LeftY, RightX, RightY, TriggerRight, TriggerLeft + if( event->caxis.axis == SDL_CONTROLLER_AXIS_TRIGGERLEFT ) + event->caxis.axis = SDL_CONTROLLER_AXIS_TRIGGERRIGHT; + else if( event->caxis.axis == SDL_CONTROLLER_AXIS_TRIGGERRIGHT ) + event->caxis.axis = SDL_CONTROLLER_AXIS_TRIGGERLEFT; + + Joy_AxisMotionEvent( event->caxis.which, event->caxis.axis, event->caxis.value ); + break; + + case SDL_CONTROLLERBUTTONDOWN: + case SDL_CONTROLLERBUTTONUP: + { + static int sdlControllerButtonToEngine[] = + { + K_AUX16, // invalid + K_A_BUTTON, K_B_BUTTON, K_X_BUTTON, K_Y_BUTTON, + K_BACK_BUTTON, K_MODE_BUTTON, K_START_BUTTON, + K_LSTICK, K_RSTICK, + K_L1_BUTTON, K_R1_BUTTON, + K_UPARROW, K_DOWNARROW, K_LEFTARROW, K_RIGHTARROW + }; + + // TODO: Use joyinput funcs, for future multiple gamepads support + if( Joy_IsActive() ) + Key_Event( sdlControllerButtonToEngine[event->cbutton.button], event->cbutton.state ); + break; + } + + case SDL_CONTROLLERDEVICEADDED: + Joy_AddEvent( event->cdevice.which ); + break; + + case SDL_CONTROLLERDEVICEREMOVED: + Joy_RemoveEvent( event->cdevice.which ); + break; + + case SDL_QUIT: + Sys_Quit(); + break; + + case SDL_WINDOWEVENT: + if( event->window.windowID != SDL_GetWindowID( host.hWnd ) ) + return; + + if( ( host.status == HOST_SHUTDOWN ) || + ( host.type == HOST_DEDICATED ) ) + break; // no need to activate + switch( event->window.event ) + { + case SDL_WINDOWEVENT_MOVED: + if( !vid_fullscreen->value ) + { + Cvar_SetValue( "_window_xpos", (float)event->window.data1 ); + Cvar_SetValue( "_window_ypos", (float)event->window.data1 ); + } + break; + case SDL_WINDOWEVENT_RESTORED: + host.status = HOST_FRAME; + host.force_draw_version = true; + host.force_draw_version_time = host.realtime + 2; + if( vid_fullscreen->value ) + VID_SetMode(); + break; + case SDL_WINDOWEVENT_FOCUS_GAINED: + host.status = HOST_FRAME; + IN_ActivateMouse(true); + if( snd_mute_losefocus->value ) + { + S_Activate( true ); + } + host.force_draw_version = true; + host.force_draw_version_time = host.realtime + 2; + if( vid_fullscreen->value ) + VID_SetMode(); + break; + case SDL_WINDOWEVENT_MINIMIZED: + host.status = HOST_SLEEP; + VID_RestoreScreenResolution(); + break; + case SDL_WINDOWEVENT_FOCUS_LOST: + +#if TARGET_OS_IPHONE + { + // Keep running if ftp server enabled + void IOS_StartBackgroundTask( void ); + IOS_StartBackgroundTask(); + } +#endif + host.status = HOST_NOFOCUS; + IN_DeactivateMouse(); + if( snd_mute_losefocus->value ) + { + S_Activate( false ); + } + host.force_draw_version = true; + host.force_draw_version_time = host.realtime + 1; + VID_RestoreScreenResolution(); + break; + case SDL_WINDOWEVENT_CLOSE: + Sys_Quit(); + break; + case SDL_WINDOWEVENT_RESIZED: + if( vid_fullscreen->value ) break; + Cvar_SetValue( "vid_mode", VID_NOMODE ); // no mode + R_ChangeDisplaySettingsFast( event->window.data1, + event->window.data2 ); + break; + case SDL_WINDOWEVENT_MAXIMIZED: + { + int w, h; + if( vid_fullscreen->value ) break; + Cvar_SetValue( "vid_mode", VID_NOMODE ); // no mode + + SDL_GL_GetDrawableSize( host.hWnd, &w, &h ); + R_ChangeDisplaySettingsFast( w, h ); + break; + } + default: + break; + } + } +} + +/* +============= +SDLash_RunEvents + +============= +*/ +void SDLash_RunEvents( void ) +{ + SDL_Event event; + + while( !host.crashed && !host.shutdown_issued && SDL_PollEvent( &event ) ) + SDLash_EventFilter( &event ); +} + +/* +============= +SDLash_JoyInit_Old + +============= +*/ +static int SDLash_JoyInit_Old( int numjoy ) +{ + int num; + int i; + + MsgDev( D_INFO, "Joystick: SDL\n" ); + + if( SDL_WasInit( SDL_INIT_JOYSTICK ) != SDL_INIT_JOYSTICK && + SDL_InitSubSystem( SDL_INIT_JOYSTICK ) ) + { + MsgDev( D_INFO, "Failed to initialize SDL Joysitck: %s\n", SDL_GetError() ); + return 0; + } + + if( joy ) + { + SDL_JoystickClose( joy ); + } + + num = SDL_NumJoysticks(); + + if( num > 0 ) + MsgDev( D_INFO, "%i joysticks found:\n", num ); + else + { + MsgDev( D_INFO, "No joystick found.\n" ); + return 0; + } + + for( i = 0; i < num; i++ ) + MsgDev( D_INFO, "%i\t: %s\n", i, SDL_JoystickNameForIndex( i ) ); + + MsgDev( D_INFO, "Pass +set joy_index N to command line, where N is number, to select active joystick\n" ); + + joy = SDL_JoystickOpen( numjoy ); + + if( !joy ) + { + MsgDev( D_INFO, "Failed to select joystick: %s\n", SDL_GetError( ) ); + return 0; + } + + MsgDev( D_INFO, "Selected joystick: %s\n" + "\tAxes: %i\n" + "\tHats: %i\n" + "\tButtons: %i\n" + "\tBalls: %i\n", + SDL_JoystickName( joy ), SDL_JoystickNumAxes( joy ), SDL_JoystickNumHats( joy ), + SDL_JoystickNumButtons( joy ), SDL_JoystickNumBalls( joy ) ); + + SDL_GameControllerEventState( SDL_DISABLE ); + SDL_JoystickEventState( SDL_ENABLE ); + + return num; +} + +/* +============= +SDLash_JoyInit_New + +============= +*/ +static int SDLash_JoyInit_New( int numjoy ) +{ + int temp, num; + int i; + + MsgDev( D_INFO, "Joystick: SDL GameController API\n" ); + + if( SDL_WasInit( SDL_INIT_GAMECONTROLLER ) != SDL_INIT_GAMECONTROLLER && + SDL_InitSubSystem( SDL_INIT_GAMECONTROLLER ) ) + { + MsgDev( D_INFO, "Failed to initialize SDL GameController API: %s\n", SDL_GetError() ); + return 0; + } + + // chance to add mappings from file + SDL_GameControllerAddMappingsFromFile( "controllermappings.txt" ); + + if( gamecontroller ) + { + SDL_GameControllerClose( gamecontroller ); + } + + temp = SDL_NumJoysticks(); + num = 0; + + for( i = 0; i < temp; i++ ) + { + if( SDL_IsGameController( i )) + num++; + } + + if( num > 0 ) + MsgDev( D_INFO, "%i joysticks found:\n", num ); + else + { + MsgDev( D_INFO, "No joystick found.\n" ); + return 0; + } + + for( i = 0; i < num; i++ ) + MsgDev( D_INFO, "%i\t: %s\n", i, SDL_GameControllerNameForIndex( i ) ); + + MsgDev( D_INFO, "Pass +set joy_index N to command line, where N is number, to select active joystick\n" ); + + gamecontroller = SDL_GameControllerOpen( numjoy ); + + if( !gamecontroller ) + { + MsgDev( D_INFO, "Failed to select joystick: %s\n", SDL_GetError( ) ); + return 0; + } +// was added in SDL2-2.0.6, allow build with earlier versions just in case +#if SDL_MAJOR_VERSION > 2 || SDL_MINOR_VERSION > 0 || SDL_PATCHLEVEL >= 6 + MsgDev( D_INFO, "Selected joystick: %s (%i:%i:%i)\n", + SDL_GameControllerName( gamecontroller ), + SDL_GameControllerGetVendor( gamecontroller ), + SDL_GameControllerGetProduct( gamecontroller ), + SDL_GameControllerGetProductVersion( gamecontroller )); +#endif + SDL_GameControllerEventState( SDL_ENABLE ); + SDL_JoystickEventState( SDL_DISABLE ); + + return num; +} + +/* +============= +SDLash_JoyInit + +============= +*/ +int SDLash_JoyInit( int numjoy ) +{ + // SDL_Joystick is now an old API + // SDL_GameController is preferred + if( Sys_CheckParm( "-sdl_joy_old_api" ) ) + return SDLash_JoyInit_Old(numjoy); + + return SDLash_JoyInit_New(numjoy); +} + + +#endif // XASH_SDL diff --git a/engine/platform/sdl/events.h b/engine/platform/sdl/events.h new file mode 100644 index 00000000..663baae0 --- /dev/null +++ b/engine/platform/sdl/events.h @@ -0,0 +1,27 @@ +/* +events.h - SDL event system handlers +Copyright (C) 2015-2017 a1batross + +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. +*/ + +#pragma once +#ifndef KEYWRAPPER_H +#define KEYWRAPPER_H + +#ifdef XASH_SDL + +void SDLash_RunEvents( void ); +void SDLash_EnableTextInput( int enable, qboolean force ); +int SDLash_JoyInit( int numjoy ); // pass -1 to init every joystick + +#endif // XASH_SDL +#endif // KEYWRAPPER_H diff --git a/engine/studio.h b/engine/studio.h index 2442b397..c8cf431a 100644 --- a/engine/studio.h +++ b/engine/studio.h @@ -111,7 +111,7 @@ Studio models are position independent, so the cache manager can move them. #define STUDIO_HAS_BBOX 0x0004 #define STUDIO_HAS_CHROME 0x0008 // if any of the textures have chrome on them -typedef struct +typedef struct studiohdr_s { int ident; int version; @@ -199,7 +199,7 @@ typedef struct } studioseqhdr_t; // bones -typedef struct +typedef struct mstudiobone_s { char name[32]; // bone name for symbolic links int parent; // parent bone @@ -258,7 +258,7 @@ typedef struct } mstudioseqgroup_t; // sequence descriptions -typedef struct +typedef struct mstudioseqdesc_s { char label[32]; // sequence label @@ -324,7 +324,7 @@ typedef struct vec3_t vectors[3]; } mstudioattachment_t; -typedef struct +typedef struct mstudioanim_s { unsigned short offset[6]; } mstudioanim_t; @@ -410,4 +410,4 @@ typedef struct short s,t; // s,t position on skin } mstudiotrivert_t; -#endif//STUDIO_H \ No newline at end of file +#endif//STUDIO_H