diff --git a/engine/cdll_int.h b/engine/cdll_int.h index dd011e78..c7b04fcf 100644 --- a/engine/cdll_int.h +++ b/engine/cdll_int.h @@ -27,6 +27,16 @@ extern "C" { #endif #include "const.h" +#include + +#define MAX_ALIAS_NAME 32 + +typedef struct cmdalias_s +{ + struct cmdalias_s *next; + char name[MAX_ALIAS_NAME]; + char *value; +} cmdalias_t; // this file is included by both the engine and the client-dll, // so make sure engine declarations aren't done twice @@ -96,10 +106,12 @@ typedef struct hud_player_info_s char *model; short topcolor; short bottomcolor; + + uint64_t m_nSteamID; } hud_player_info_t; -typedef struct event_args_s event_args_t; -typedef struct screenfade_s screenfade_t; +struct screenfade_s; +struct tagPOINT; typedef struct cl_enginefuncs_s { @@ -197,15 +209,15 @@ typedef struct cl_enginefuncs_s void (*pfnPlaybackEvent)( int flags, const struct edict_s *pInvoker, unsigned short eventindex, float delay, float *origin, float *angles, float fparam1, float fparam2, int iparam1, int iparam2, int bparam1, int bparam2 ); void (*pfnWeaponAnim)( int iAnim, int body ); float (*pfnRandomFloat)( float flLow, float flHigh ); - long (*pfnRandomLong)( long lLow, long lHigh ); - void (*pfnHookEvent)( char *name, void ( *pfnEvent )( event_args_t *args )); + int (*pfnRandomLong)( int lLow, int lHigh ); + void (*pfnHookEvent)( char *name, void ( *pfnEvent )( struct event_args_s *args )); int (*Con_IsVisible) (); const char *(*pfnGetGameDirectory)( void ); struct cvar_s *(*pfnGetCvarPointer)( const char *szName ); const char *(*Key_LookupBinding)( const char *pBinding ); const char *(*pfnGetLevelName)( void ); - void (*pfnGetScreenFade)( screenfade_t *fade ); - void (*pfnSetScreenFade)( screenfade_t *fade ); + void (*pfnGetScreenFade)( struct screenfade_s *fade ); + void (*pfnSetScreenFade)( struct screenfade_s *fade ); void* (*VGui_GetPanel)( ); void (*VGui_ViewportPaintBackground)( int extents[4] ); @@ -251,6 +263,44 @@ typedef struct cl_enginefuncs_s void (*pfnGetMousePos)( struct tagPOINT *ppt ); void (*pfnSetMousePos)( int x, int y ); void (*pfnSetMouseEnable)( qboolean fEnable ); + + // undocumented interface starts here + struct cvar_s* (*pfnGetFirstCvarPtr)( void ); + void* (*pfnGetFirstCmdFunctionHandle)( void ); + void* (*pfnGetNextCmdFunctionHandle)( void *cmdhandle ); + const char* (*pfnGetCmdFunctionName)( void *cmdhandle ); + float (*pfnGetClientOldTime)( void ); + float (*pfnGetGravity)( void ); + struct model_s* (*pfnGetModelByIndex)( int index ); + void (*pfnSetFilterMode)( int mode ); // same as gl_texsort in original Quake + void (*pfnSetFilterColor)( float red, float green, float blue ); + void (*pfnSetFilterBrightness)( float brightness ); + void *(*pfnSequenceGet)( const char *fileName, const char *entryName ); + void (*pfnSPR_DrawGeneric)( int frame, int x, int y, const wrect_t *prc, int blendsrc, int blenddst, int width, int height ); + void *(*pfnSequencePickSentence)( const char *groupName, int pickMethod, int *entryPicked ); + int (*pfnDrawString)( int x, int y, const char *str, int r, int g, int b ); + int (*pfnDrawStringReverse)( int x, int y, const char *str, int r, int g, int b ); + const char *(*LocalPlayerInfo_ValueForKey)( const char* key ); + int (*pfnVGUI2DrawCharacter)( int x, int y, int ch, unsigned int font ); + int (*pfnVGUI2DrawCharacterAdditive)( int x, int y, int ch, int r, int g, int b, unsigned int font ); + unsigned int (*pfnGetApproxWavePlayLen)( char *filename ); + void* (*GetCareerGameUI)( void ); // g-cont. !!!! potential crash-point! + void (*Cvar_Set)( char *name, char *value ); + int (*pfnIsPlayingCareerMatch)( void ); + void (*pfnPlaySoundVoiceByName)( char *szSound, float volume, int pitch ); + void (*pfnPrimeMusicStream)( char *filename, int looping ); + double (*pfnSys_FloatTime)( void ); + + // decay funcs + void (*pfnProcessTutorMessageDecayBuffer)( int *buffer, int buflen ); + void (*pfnConstructTutorMessageDecayBuffer)( int *buffer, int buflen ); + void (*pfnResetTutorMessageDecayData)( void ); + + void (*pfnPlaySoundByNameAtPitch)( char *szSound, float volume, int pitch ); + void (*pfnFillRGBABlend)( int x, int y, int width, int height, int r, int g, int b, int a ); + int (*pfnGetAppID)( void ); + cmdalias_t *(*pfnGetAliases)( void ); + void (*pfnVguiWrap2_GetMouseDelta)( int *x, int *y ); } cl_enginefunc_t; #define CLDLL_INTERFACE_VERSION 7 diff --git a/engine/client/cl_game.c b/engine/client/cl_game.c index e2008490..3c76bd82 100644 --- a/engine/client/cl_game.c +++ b/engine/client/cl_game.c @@ -2772,6 +2772,281 @@ void pfnSetMouseEnable( qboolean fEnable ) { } + +/* +============= +pfnGetServerTime + +============= +*/ +static float GAME_EXPORT pfnGetClientOldTime( void ) +{ + return cl.oldtime; +} + +/* +============= +pfnGetGravity + +============= +*/ +static float GAME_EXPORT pfnGetGravity( void ) +{ + return clgame.movevars.gravity; +} + +/* +============= +pfnEnableTexSort + +TODO: implement +============= +*/ +static void GAME_EXPORT pfnEnableTexSort( int enable ) +{ +} + +/* +============= +pfnSetLightmapColor + +TODO: implement +============= +*/ +static void GAME_EXPORT pfnSetLightmapColor( float red, float green, float blue ) +{ +} + +/* +============= +pfnSetLightmapScale + +TODO: implement +============= +*/ +static void GAME_EXPORT pfnSetLightmapScale( float scale ) +{ +} + +/* +============= +pfnSPR_DrawGeneric + +============= +*/ +static void GAME_EXPORT pfnSPR_DrawGeneric( int frame, int x, int y, const wrect_t *prc, int blendsrc, int blenddst, int width, int height ) +{ + pglEnable( GL_BLEND ); + pglBlendFunc( blendsrc, blenddst ); // g-cont. are params is valid? + SPR_DrawGeneric( frame, x, y, width, height, prc ); +} + +/* +============= +LocalPlayerInfo_ValueForKey + +============= +*/ +static const char *GAME_EXPORT LocalPlayerInfo_ValueForKey( const char* key ) +{ + return Info_ValueForKey( CL_Userinfo(), key ); +} + +/* +============= +pfnVGUI2DrawCharacter + +============= +*/ +static int GAME_EXPORT pfnVGUI2DrawCharacter( int x, int y, int number, unsigned int font ) +{ + if( !cls.creditsFont.valid ) + return 0; + + number &= 255; + + number = Con_UtfProcessChar( number ); + + if( number < 32 ) return 0; + if( y < -clgame.scrInfo.iCharHeight ) + return 0; + + clgame.ds.adjust_size = true; + gameui.ds.gl_texturenum = cls.creditsFont.hFontTexture; + pfnPIC_DrawAdditive( x, y, -1, -1, &cls.creditsFont.fontRc[number] ); + clgame.ds.adjust_size = false; + + return clgame.scrInfo.charWidths[number]; +} + +/* +============= +pfnVGUI2DrawCharacterAdditive + +============= +*/ +static int GAME_EXPORT pfnVGUI2DrawCharacterAdditive( int x, int y, int ch, int r, int g, int b, unsigned int font ) +{ + /// TODO: fix UTF-8 +#if 0 + if( !hud_utf8->integer ) + ch = Con_UtfProcessChar( ch ); +#endif + + return pfnDrawCharacter( x, y, ch, r, g, b ); +} + +/* +============= +pfnDrawString + +============= +*/ +static int GAME_EXPORT pfnDrawString( int x, int y, const char *str, int r, int g, int b ) +{ + Con_UtfProcessChar(0); + + // draw the string until we hit the null character or a newline character + for ( ; *str != 0 && *str != '\n'; str++ ) + { + x += pfnVGUI2DrawCharacterAdditive( x, y, (unsigned char)*str, r, g, b, 0 ); + } + + return x; +} + +/* +============= +pfnDrawStringReverse + +============= +*/ +static int GAME_EXPORT pfnDrawStringReverse( int x, int y, const char *str, int r, int g, int b ) +{ + // find the end of the string + char *szIt; + for( szIt = (char*)str; *szIt != 0; szIt++ ) + x -= clgame.scrInfo.charWidths[ (unsigned char) *szIt ]; + pfnDrawString( x, y, str, r, g, b ); + return x; +} + +/* +============= +GetCareerGameInterface + +============= +*/ +static void *GAME_EXPORT GetCareerGameInterface( void ) +{ + Msg( "^1Career GameInterface called!\n" ); + return NULL; +} + +/* +============= +pfnPlaySoundVoiceByName + +============= +*/ +static void GAME_EXPORT pfnPlaySoundVoiceByName( char *filename, float volume, int pitch ) +{ + int hSound = S_RegisterSound( filename ); + + S_StartSound( NULL, cl.viewentity, CHAN_NETWORKVOICE_END + 1, hSound, volume, 1.0, pitch, SND_STOP_LOOPING ); +} + +/* +============= +pfnMP3_InitStream + +============= +*/ +static void GAME_EXPORT pfnMP3_InitStream( char *filename, int looping ) +{ + if( !filename ) + { + S_StopBackgroundTrack(); + return; + } + + if( looping ) + { + S_StartBackgroundTrack( filename, filename, 0, false ); + } + else + { + S_StartBackgroundTrack( filename, NULL, 0, false ); + } +} + +/* +============= +pfnPlaySoundByNameAtPitch + +============= +*/ +static void GAME_EXPORT pfnPlaySoundByNameAtPitch( char *filename, float volume, int pitch ) +{ + int hSound = S_RegisterSound( filename ); + S_StartSound( NULL, cl.viewentity, CHAN_ITEM, hSound, volume, 1.0, pitch, SND_STOP_LOOPING ); +} + +/* +============= +pfnFillRGBABlend + +============= +*/ +void GAME_EXPORT CL_FillRGBABlend( int x, int y, int w, int h, int r, int g, int b, int a ) +{ + r = bound( 0, r, 255 ); + g = bound( 0, g, 255 ); + b = bound( 0, b, 255 ); + a = bound( 0, a, 255 ); + + SPR_AdjustSize( (float *)&x, (float *)&y, (float *)&w, (float *)&h ); + + pglDisable( GL_TEXTURE_2D ); + pglEnable( GL_BLEND ); + pglTexEnvi( GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE ); + pglBlendFunc( GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA ); + pglColor4f( r / 255.0f, g / 255.0f, b / 255.0f, a / 255.0f ); + + pglBegin( GL_QUADS ); + pglVertex2f( x, y ); + pglVertex2f( x + w, y ); + pglVertex2f( x + w, y + h ); + pglVertex2f( x, y + h ); + pglEnd (); + + pglColor3f( 1.0f, 1.0f, 1.0f ); + pglEnable( GL_TEXTURE_2D ); + pglDisable( GL_BLEND ); +} + +/* +============= +pfnGetAppID + +============= +*/ +static int GAME_EXPORT pfnGetAppID( void ) +{ + return 70; // Half-Life AppID +} + +/* +============= +pfnVguiWrap2_GetMouseDelta + +TODO: implement +============= +*/ +static void GAME_EXPORT pfnVguiWrap2_GetMouseDelta( int *x, int *y ) +{ +} + /* ============= pfnParseFile @@ -3779,6 +4054,39 @@ static cl_enginefunc_t gEngfuncs = pfnGetMousePos, pfnSetMousePos, pfnSetMouseEnable, + Cvar_GetList, + (void*)Cmd_GetFirstFunctionHandle, + (void*)Cmd_GetNextFunctionHandle, + (void*)Cmd_GetName, + pfnGetClientOldTime, + pfnGetGravity, + Mod_Handle, + pfnEnableTexSort, + pfnSetLightmapColor, + pfnSetLightmapScale, + pfnSequenceGet, + pfnSPR_DrawGeneric, + pfnSequencePickSentence, + pfnDrawString, + pfnDrawStringReverse, + LocalPlayerInfo_ValueForKey, + pfnVGUI2DrawCharacter, + pfnVGUI2DrawCharacterAdditive, + (void*)Sound_GetApproxWavePlayLen, + GetCareerGameInterface, + (void*)Cvar_Set, + pfnIsCareerMatch, + pfnPlaySoundVoiceByName, + pfnMP3_InitStream, + Sys_DoubleTime, + pfnProcessTutorMessageDecayBuffer, + pfnConstructTutorMessageDecayBuffer, + pfnResetTutorMessageDecayData, + pfnPlaySoundByNameAtPitch, + CL_FillRGBABlend, + pfnGetAppID, + Cmd_AliasGetList, + pfnVguiWrap2_GetMouseDelta, }; void CL_UnloadProgs( void ) diff --git a/engine/common/cmd.c b/engine/common/cmd.c index 07fa3d62..882f123c 100644 --- a/engine/common/cmd.c +++ b/engine/common/cmd.c @@ -21,13 +21,6 @@ GNU General Public License for more details. #define MAX_CMD_LINE 2048 #define MAX_ALIAS_NAME 32 -typedef struct cmdalias_s -{ - struct cmdalias_s *next; - char name[MAX_ALIAS_NAME]; - char *value; -} cmdalias_t; - typedef struct { byte *data; @@ -49,6 +42,7 @@ int cmd_condlevel; ============================================================================= */ + /* ============ Cbuf_Init @@ -498,6 +492,56 @@ char *Cmd_Args( void ) return cmd_args; } + +/* +=========================== + +Client exports + +=========================== +*/ + + +/* +============ +Cmd_AliasGetList +============ +*/ +cmdalias_t *GAME_EXPORT Cmd_AliasGetList( void ) +{ + return cmd_alias; +} + +/* +============ +Cmd_GetList +============ +*/ +cmd_t *GAME_EXPORT Cmd_GetFirstFunctionHandle( void ) +{ + return cmd_functions; +} + +/* +============ +Cmd_GetNext +============ +*/ +cmd_t *GAME_EXPORT Cmd_GetNextFunctionHandle( cmd_t *cmd ) +{ + return (cmd) ? cmd->next : NULL; +} + +/* +============ +Cmd_GetName +============ +*/ +char *GAME_EXPORT Cmd_GetName( cmd_t *cmd ) +{ + return cmd->name; +} + /* ============ Cmd_TokenizeString @@ -1153,4 +1197,4 @@ void Cmd_Init( void ) Cmd_AddCommand( "unalias", Cmd_UnAlias_f, "remove a script function" ); Cmd_AddCommand( "if", Cmd_If_f, "compare and set condition bits" ); Cmd_AddCommand( "else", Cmd_Else_f, "invert condition bit" ); -} \ No newline at end of file +} diff --git a/engine/common/common.c b/engine/common/common.c index b1fc6e10..7bc7b26e 100644 --- a/engine/common/common.c +++ b/engine/common/common.c @@ -1321,3 +1321,110 @@ qboolean COM_IsSafeFileToDownload( const char *filename ) return true; } + +/* +====================== + +COMMON EXPORT STUBS + +====================== +*/ + + +/* +============= +pfnSequenceGet + +used by CS:CZ +============= +*/ +void *GAME_EXPORT pfnSequenceGet( const char *fileName, const char *entryName ) +{ + Msg( "Sequence_Get: file %s, entry %s\n", fileName, entryName ); + + + return Sequence_Get( fileName, entryName ); +} + +/* +============= +pfnSequencePickSentence + +used by CS:CZ +============= +*/ +void *GAME_EXPORT pfnSequencePickSentence( const char *groupName, int pickMethod, int *picked ) +{ + Msg( "Sequence_PickSentence: group %s, pickMethod %i\n", groupName, pickMethod ); + + return Sequence_PickSentence( groupName, pickMethod, picked ); + +} + +/* +============= +pfnIsCareerMatch + +used by CS:CZ (client stub) +============= +*/ +int GAME_EXPORT GAME_EXPORT pfnIsCareerMatch( void ) +{ + return 0; +} + +/* +============= +pfnRegisterTutorMessageShown + +only exists in PlayStation version +============= +*/ +void GAME_EXPORT pfnRegisterTutorMessageShown( int mid ) +{ +} + +/* +============= +pfnGetTimesTutorMessageShown + +only exists in PlayStation version +============= +*/ +int GAME_EXPORT pfnGetTimesTutorMessageShown( int mid ) +{ + return 0; +} + +/* +============= +pfnProcessTutorMessageDecayBuffer + +only exists in PlayStation version +============= +*/ +void GAME_EXPORT pfnProcessTutorMessageDecayBuffer( int *buffer, int bufferLength ) +{ +} + +/* +============= +pfnConstructTutorMessageDecayBuffer + +only exists in PlayStation version +============= +*/ +void GAME_EXPORT pfnConstructTutorMessageDecayBuffer( int *buffer, int bufferLength ) +{ +} + +/* +============= +pfnResetTutorMessageDecayData + +only exists in PlayStation version +============= +*/ +void GAME_EXPORT pfnResetTutorMessageDecayData( void ) +{ +} diff --git a/engine/common/common.h b/engine/common/common.h index cfe1eef8..3d6cb30f 100644 --- a/engine/common/common.h +++ b/engine/common/common.h @@ -122,6 +122,7 @@ typedef char string[MAX_STRING]; typedef struct file_s file_t; // normal file typedef struct wfile_s wfile_t; // wad file typedef struct stream_s stream_t; // sound stream for background music playing +typedef off_t fs_offset_t; typedef void (*setpair_t)( const char *key, const char *value, void *buffer, void *numpairs ); @@ -768,6 +769,7 @@ long FS_SetStreamPos( stream_t *stream, long newpos ); long FS_GetStreamPos( stream_t *stream ); void FS_FreeStream( stream_t *stream ); qboolean Sound_Process( wavdata_t **wav, int rate, int width, uint flags ); +uint Sound_GetApproxWavePlayLen( const char *filepath ); // // build.c @@ -860,6 +862,19 @@ int pfnNumberOfEntities( void ); int pfnIsInGame( void ); float pfnTime( void ); +// CS:CS engfuncs (stubs) +void *pfnSequenceGet( const char *fileName, const char *entryName ); +void *pfnSequencePickSentence( const char *groupName, int pickMethod, int *picked ); +int pfnIsCareerMatch( void ); + +// Decay engfuncs (stubs) +int pfnGetTimesTutorMessageShown( int mid ); +void pfnRegisterTutorMessageShown( int mid ); +void pfnConstructTutorMessageDecayBuffer( int *buffer, int buflen ); +void pfnProcessTutorMessageDecayBuffer( int *buffer, int bufferLength ); +void pfnResetTutorMessageDecayData( void ); + + /* ============================================================== @@ -952,6 +967,10 @@ byte *COM_LoadFile( const char *filename, int usehunk, int *pLength ); qboolean CL_GetDemoComment( const char *demoname, char *comment ); void COM_AddAppDirectoryToSearchPath( const char *pszBaseDir, const char *appName ); int COM_ExpandFilename( const char *fileName, char *nameOutBuffer, int nameOutBufferSize ); +struct cmd_s *Cmd_GetFirstFunctionHandle( void ); +struct cmd_s *Cmd_GetNextFunctionHandle( struct cmd_s *cmd ); +struct cmdalias_s *Cmd_AliasGetList( void ); +char *Cmd_GetName( struct cmd_s *cmd ); struct pmtrace_s *PM_TraceLine( float *start, float *end, int flags, int usehull, int ignore_pe ); void SV_StartSound( edict_t *ent, int chan, const char *sample, float vol, float attn, int flags, int pitch ); void SV_StartMusic( const char *curtrack, const char *looptrack, long position ); diff --git a/engine/common/cvar.c b/engine/common/cvar.c index c01b3c31..264b3a16 100644 --- a/engine/common/cvar.c +++ b/engine/common/cvar.c @@ -19,6 +19,17 @@ GNU General Public License for more details. convar_t *cvar_vars = NULL; // head of list convar_t *cmd_scripting; +/* +============ +Cvar_GetList +============ +*/ +cvar_t *GAME_EXPORT Cvar_GetList( void ) +{ + return (cvar_t *)cvar_vars; +} + + /* ============ Cvar_FindVar @@ -889,4 +900,4 @@ void Cvar_Init( void ) Cmd_AddCommand( "toggle", Cvar_Toggle_f, "toggles a console variable's values (use for more info)" ); Cmd_AddCommand( "reset", Cvar_Reset_f, "reset any type variable to initial value" ); Cmd_AddCommand( "cvarlist", Cvar_List_f, "display all console variables beginning with the specified prefix" ); -} \ No newline at end of file +} diff --git a/engine/common/cvar.h b/engine/common/cvar.h index e190b109..45015798 100644 --- a/engine/common/cvar.h +++ b/engine/common/cvar.h @@ -48,6 +48,7 @@ typedef struct convar_s #define CVAR_DEFINE_AUTO( cv, cvstr, cvflags, cvdesc ) convar_t cv = { #cv, cvstr, cvflags, 0.0f, (void *)CVAR_SENTINEL, cvdesc } #define CVAR_TO_BOOL( x ) ((x) && ((x)->value != 0.0f) ? true : false ) +cvar_t *Cvar_GetList( void ); #define Cvar_FindVar( name ) Cvar_FindVarExt( name, 0 ) convar_t *Cvar_FindVarExt( const char *var_name, int ignore_group ); void Cvar_RegisterVariable( convar_t *var ); @@ -68,4 +69,4 @@ qboolean Cvar_Command( void ); void Cvar_Init( void ); void Cvar_Unlink( int group ); -#endif//CVAR_H \ No newline at end of file +#endif//CVAR_H diff --git a/engine/common/mod_local.h b/engine/common/mod_local.h index 47f9e207..5281e103 100644 --- a/engine/common/mod_local.h +++ b/engine/common/mod_local.h @@ -152,6 +152,7 @@ model_t *Mod_ForName( const char *name, qboolean crash, qboolean trackCRC ); qboolean Mod_ValidateCRC( const char *name, CRC32_t crc ); void Mod_NeedCRC( const char *name, qboolean needCRC ); void Mod_FreeUnused( void ); +model_t *Mod_Handle( int handle ); // // mod_bmodel.c diff --git a/engine/common/model.c b/engine/common/model.c index d899f01f..ea480fcb 100644 --- a/engine/common/model.c +++ b/engine/common/model.c @@ -601,4 +601,21 @@ void Mod_NeedCRC( const char *name, qboolean needCRC ) if( needCRC ) SetBits( p->flags, FCRC_SHOULD_CHECKSUM ); else ClearBits( p->flags, FCRC_SHOULD_CHECKSUM ); -} \ No newline at end of file +} + + +/* +================== +Mod_Handle + +================== +*/ +model_t *GAME_EXPORT Mod_Handle( int handle ) +{ + if( handle < 0 || handle >= MAX_MODELS ) + { + MsgDev( D_NOTE, "Mod_Handle: bad handle #%i\n", handle ); + return NULL; + } + return &mod_known[handle]; +} diff --git a/engine/common/sequence.c b/engine/common/sequence.c new file mode 100644 index 00000000..955b104c --- /dev/null +++ b/engine/common/sequence.c @@ -0,0 +1,1781 @@ +/* +sequence.c - scripted sequences for CS:CZDS +Copyright (C) 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. +*/ + +#include "common.h" +#include "eiface.h" +#include "sequence.h" + +sequenceCommandLine_s g_fileScopeDefaults; +sequenceCommandLine_s g_blockScopeDefaults; +sequenceEntry_s *g_sequenceList = NULL; +sentenceGroupEntry_s *g_sentenceGroupList = NULL; +qboolean g_sequenceParseFileIsGlobal; +unsigned int g_nonGlobalSentences = 0; +char g_sequenceParseFileName[MAX_STRING]; +int g_lineNum = 1; +char *g_scan = NULL; +char *g_lineScan = NULL; + +const sequenceCommandMapping_s g_sequenceCommandMappingTable[] = +{ + {SEQUENCE_COMMAND_PAUSE, "pause", SEQUENCE_TYPE_COMMAND}, + {SEQUENCE_COMMAND_TEXT, "text", SEQUENCE_TYPE_COMMAND}, + {SEQUENCE_COMMAND_SOUND, "sound", SEQUENCE_TYPE_COMMAND}, + {SEQUENCE_COMMAND_FIRETARGETS, "firetargets", SEQUENCE_TYPE_COMMAND}, + {SEQUENCE_COMMAND_KILLTARGETS, "killtargets", SEQUENCE_TYPE_COMMAND}, + {SEQUENCE_COMMAND_GOSUB, "gosub", SEQUENCE_TYPE_COMMAND}, + {SEQUENCE_COMMAND_SENTENCE, "sentence", SEQUENCE_TYPE_COMMAND}, + {SEQUENCE_COMMAND_REPEAT, "repeat", SEQUENCE_TYPE_COMMAND}, + {SEQUENCE_COMMAND_SETDEFAULTS, "setdefaults", SEQUENCE_TYPE_COMMAND}, + {SEQUENCE_COMMAND_MODIFIER, "modifier", SEQUENCE_TYPE_COMMAND}, + {SEQUENCE_COMMAND_POSTMODIFIER, "postmodifier", SEQUENCE_TYPE_COMMAND}, + {SEQUENCE_COMMAND_NOOP, "noop", SEQUENCE_TYPE_COMMAND}, + {SEQUENCE_MODIFIER_EFFECT, "effect", SEQUENCE_TYPE_MODIFIER}, + {SEQUENCE_MODIFIER_POSITION, "position", SEQUENCE_TYPE_MODIFIER}, + {SEQUENCE_MODIFIER_COLOR, "color", SEQUENCE_TYPE_MODIFIER}, + {SEQUENCE_MODIFIER_COLOR2, "color2", SEQUENCE_TYPE_MODIFIER}, + {SEQUENCE_MODIFIER_FADEIN, "fadein", SEQUENCE_TYPE_MODIFIER}, + {SEQUENCE_MODIFIER_FADEOUT, "fadeout", SEQUENCE_TYPE_MODIFIER}, + {SEQUENCE_MODIFIER_HOLDTIME, "holdtime", SEQUENCE_TYPE_MODIFIER}, + {SEQUENCE_MODIFIER_FXTIME, "fxtime", SEQUENCE_TYPE_MODIFIER}, + {SEQUENCE_MODIFIER_SPEAKER, "speaker", SEQUENCE_TYPE_MODIFIER}, + {SEQUENCE_MODIFIER_LISTENER, "listener", SEQUENCE_TYPE_MODIFIER}, + {SEQUENCE_MODIFIER_TEXTCHANNEL, "channel", SEQUENCE_TYPE_MODIFIER} +}; + +/* +============= +Sequence_GetCommandEnumForName + +============= +*/ +sequenceCommandEnum_e Sequence_GetCommandEnumForName( const char *commandName, sequenceCommandType_e type ) +{ + int i; + + for( i = 0; i < ARRAYSIZE( g_sequenceCommandMappingTable ); i++ ) + { + const sequenceCommandMapping_s *mapping = g_sequenceCommandMappingTable + i; + + if( mapping->commandType == type && !Q_stricmp( mapping->commandName, commandName ) ) + return mapping->commandEnum; + } + return SEQUENCE_COMMAND_ERROR; +} + +/* +============= +Sequence_ResetDefaults + +============= +*/ +void Sequence_ResetDefaults( sequenceCommandLine_s *destination, sequenceCommandLine_s *source ) +{ + if( !source ) + { + static client_textmessage_t defaultClientMessage = + { + 0, // effect + -1, -1, -1, -1, // rgba1 + -1, -1, -1, -1, // rgba2 + 0.5, 0.5, // xy + 0.2, 0.2, // fade-in/out + 1.6, // holdtime + 1.0, // fxtime + NULL, NULL // pName, pMessage + }; + + destination->clientMessage = defaultClientMessage; + destination->textChannel = 0; + destination->delay = 0; + destination->repeatCount = 0; + destination->nextCommandLine = NULL; + destination->soundFileName = NULL; + destination->speakerName = NULL; + destination->listenerName = NULL; + return; + } + + destination->clientMessage = source->clientMessage; + destination->clientMessage.pName = NULL; + destination->clientMessage.pMessage = NULL; + destination->textChannel = source->textChannel; + destination->delay = source->delay; + destination->repeatCount = source->repeatCount; + destination->nextCommandLine = NULL; + destination->soundFileName = NULL; + + Z_Free( destination->speakerName ); + destination->speakerName = copystring( source->speakerName ); + + Z_Free( destination->listenerName ); + destination->listenerName = copystring( source->listenerName ); +} + +/* +============= +Sequence_WriteDefaults + +============= +*/ +void Sequence_WriteDefaults( sequenceCommandLine_s *source, sequenceCommandLine_s *destination ) +{ + if( !destination ) + MsgDev( D_ERROR, "Attempt to bake defaults into a non-existant command." ); + + if( !source ) + MsgDev( D_ERROR, "Attempt to bake defaults from a non-existant command." ); + + if( source->modifierBitField & SEQUENCE_MODIFIER_EFFECT_BIT ) + { + destination->clientMessage.effect = source->clientMessage.effect; + } + + if( source->modifierBitField & SEQUENCE_MODIFIER_POSITION_BIT ) + { + destination->clientMessage.x = source->clientMessage.x; + destination->clientMessage.y = source->clientMessage.y; + } + + if( source->modifierBitField & SEQUENCE_MODIFIER_COLOR_BIT ) + { + destination->clientMessage.r1 = source->clientMessage.r1; + destination->clientMessage.g1 = source->clientMessage.g1; + destination->clientMessage.b1 = source->clientMessage.b1; + destination->clientMessage.a1 = source->clientMessage.a1; + } + + if( source->modifierBitField & SEQUENCE_MODIFIER_COLOR2_BIT ) + { + destination->clientMessage.r2 = source->clientMessage.r2; + destination->clientMessage.g2 = source->clientMessage.g2; + destination->clientMessage.b2 = source->clientMessage.b2; + destination->clientMessage.a2 = source->clientMessage.a2; + } + + if( source->modifierBitField & SEQUENCE_MODIFIER_FADEIN_BIT ) + { + destination->clientMessage.fadein = source->clientMessage.fadein; + } + + if( source->modifierBitField & SEQUENCE_MODIFIER_FADEOUT_BIT ) + { + destination->clientMessage.fadeout = source->clientMessage.fadeout; + } + + if( source->modifierBitField & SEQUENCE_MODIFIER_HOLDTIME_BIT ) + { + destination->clientMessage.holdtime = source->clientMessage.holdtime; + } + + if( source->modifierBitField & SEQUENCE_MODIFIER_FXTIME_BIT ) + { + destination->clientMessage.fxtime = source->clientMessage.fxtime; + } + + if( source->modifierBitField & SEQUENCE_MODIFIER_SPEAKER_BIT ) + { + Z_Free( destination->speakerName ); + destination->speakerName = copystring( source->speakerName ); + } + + if( source->modifierBitField & SEQUENCE_MODIFIER_LISTENER_BIT ) + { + Z_Free( destination->listenerName ); + destination->listenerName = copystring( source->listenerName ); + } + + if( source->modifierBitField & SEQUENCE_MODIFIER_TEXTCHANNEL_BIT ) + { + destination->textChannel = source->textChannel; + } +} + +/* +============= +Sequence_BakeDefaults + +============= +*/ +void Sequence_BakeDefaults( sequenceCommandLine_s *destination, sequenceCommandLine_s *source ) +{ + char *saveName, *saveMessage; + + if( !destination ) + MsgDev( D_ERROR, "Attempt to bake defaults into a non-existant command." ); + + if( !source ) + MsgDev( D_ERROR, "Attempt to bake defaults from a non-existant command." ); + + saveName= destination->clientMessage.pName; + saveMessage = destination->clientMessage.pMessage; + + destination->clientMessage = source->clientMessage; + + destination->clientMessage.pName = saveName; + destination->clientMessage.pMessage = saveMessage; + + destination->textChannel = source->textChannel; + + Z_Free( destination->speakerName ); + destination->speakerName = copystring( source->speakerName ); + + Z_Free( destination->listenerName ); + destination->listenerName = copystring( source->listenerName ); +} + +/* +============= +Sequence_SkipWhitespace + +============= +*/ +qboolean Sequence_SkipWhitespace( void ) +{ + qboolean newLine = false; + + for( ; isspace( *g_scan ); g_scan++ ) + { + if( *g_scan == '\n' ) + { + g_lineScan = g_scan + 1; + g_lineNum++; + + newLine = true; + } + } + + return newLine; +} + +/* +============= +Sequence_IsNameValueChar + +============= +*/ +qboolean Sequence_IsNameValueChar( char ch ) +{ + if( isalnum( ch ) ) + return true; + + switch( ch ) + { + case '.': + case '-': + case '_': + case '/': + case '\\': + return true; + } + + return false; +} + +/* +============= +Sequence_IsSymbol + +============= +*/ +qboolean Sequence_IsSymbol( char ch ) +{ + switch( ch ) + { + case '"': + case '#': + case '$': + case '%': + case ',': + case '=': + case '@': + case '{': + case '}': + return true; + } + + return false; +} + +/* +============= +Sequence_GetNameValueString + +============= +*/ +size_t Sequence_GetNameValueString( char *token, size_t len ) +{ + char *p; + + Sequence_SkipWhitespace( ); + + if( !Sequence_IsNameValueChar( *g_scan ) ) + { + if( *g_scan == '#' || *g_scan == '$' ) + MsgDev( D_ERROR, "Parsing error on line %d of %s.seq: cannot have more than one '%c' per line; '%c' must be at the beginning of the line ONLY\n", g_lineNum, g_sequenceParseFileName, *g_scan, *g_scan ); + else + MsgDev( D_ERROR, "Parsing error on line %d of %s.seq: expected name/value, found illegal character '%c'\n", g_lineNum, g_sequenceParseFileName, *g_scan ); + } + + for( p = token; Sequence_IsNameValueChar( *g_scan ) && len; p++, g_scan++, len-- ) + { + *p = *g_scan; + } + + *p = 0; + + return p - token; +} + +/* +============= +Sequence_GetSymbol + +============= +*/ +char Sequence_GetSymbol( void ) +{ + char ch; + + Sequence_SkipWhitespace( ); + + ch = *g_scan; + + if( ch ) + g_scan++; + + return ch; +} + +/* +============= +Sequence_ValidateNameValueString + +============= +*/ +void Sequence_ValidateNameValueString( char *token ) +{ + char *scan; + + for( scan = token; *scan; scan++ ) + { + if( !Sequence_IsNameValueChar( *scan ) ) + MsgDev( D_ERROR, "Parsing error on line %d of %s.seq: name/value string \"%s\" had illegal character '%c'\n", g_lineNum, g_sequenceParseFileName, token, *scan ); + } +} + +/* +============= +Sequence_GetToken + +============= +*/ +size_t Sequence_GetToken( char *token, size_t size ) +{ + Sequence_SkipWhitespace( ); + + if( Sequence_IsNameValueChar( *g_scan ) ) + { + return Sequence_GetNameValueString( token, size ); + } + + if( !Sequence_IsSymbol( *g_scan ) ) + MsgDev( D_ERROR, "Parsing error on line %d of %s.seq: expected token, found '%c' instead\n", g_lineNum, g_sequenceParseFileName, *g_scan ); + + token[0] = *g_scan++; + token[1] = 0; + g_scan++; + + return 1; // only one symbol has copied to token +} + +/* +============= +Sequence_GetLine + +============= +*/ +size_t Sequence_GetLine( char *line, int lineMaxLen ) +{ + int lineLen; + char *read; + char *write = line; + + Sequence_SkipWhitespace( ); + + read = Q_strchr( g_scan, '\n' ); + + if( !read ) + MsgDev( D_ERROR, "Syntax Error on line %d of %s.seq: expected sentence definition or '}', found End-Of-File!\n", g_lineNum, g_sequenceParseFileName ); + + lineLen = read - g_scan; + + if( lineLen >= lineMaxLen ) + MsgDev( D_ERROR, "Syntax Error on line %d of %s.seq: line was too long (was %d chars; max is %d chars)\n", g_lineNum, g_sequenceParseFileName, lineLen, lineMaxLen - 1 ); + + Q_strncpy( write, g_scan, lineLen ); + write[lineLen] = 0; + g_scan = read; + + return lineLen; +} + +/* +============= +Sequence_StripComments + +============= +*/ +void Sequence_StripComments( char *buffer, int *pBufSize ) +{ + char *eof = buffer + *pBufSize; + char *read = buffer; + char *write = buffer; + + for( ; read < eof; ) + { + if( !*read ) + break; + + if( *read == '/' ) + { + // skip one line comments // + if( read[1] == '/' ) + { + read += 2; + + while( *read ) + { + if( *read == '\n' ) + break; + + if( *read == '\r' ) + break; + + read++; + } + + continue; + } + + // skip multiline /* */ + if( read[1] == '*' ) + { + read += 2; + + while( *read && read[1] ) + { + if( *read == '*' && read[1] == '/' ) + { + read += 2; + break; + } + + if( *read == '\n' || *read == '\r' ) + *write++ = *read; + + read++; + } + + continue; + } + } + + *write++ = *read++; + } + + *write = 0; +} + +/* +============= +Sequence_ReadInt + +============= +*/ +int Sequence_ReadInt( void ) +{ + char str[MAX_STRING]; + + Sequence_SkipWhitespace( ); + Sequence_GetNameValueString( str, MAX_STRING ); + + return Q_atoi( str ); +} + +/* +============= +Sequence_ReadFloat + +============= +*/ +float Sequence_ReadFloat( void ) +{ + char str[MAX_STRING]; + + Sequence_SkipWhitespace( ); + Sequence_GetNameValueString( str, MAX_STRING ); + + return Q_atof( str ); +} + +/* +============= +Sequence_ReadFloat + +============= +*/ +void Sequence_ReadString( char **dest, char *string, size_t len ) +{ + Sequence_SkipWhitespace( ); + Sequence_GetNameValueString( string, len ); + + if( dest ) *dest = copystring( string ); +} + +/* +============= +Sequence_ReadQuotedString + +============= +*/ +void Sequence_ReadQuotedString( char **dest, char *str, size_t len ) +{ + char *write, ch; + + Sequence_SkipWhitespace( ); + + ch = Sequence_GetSymbol( ); + if( ch != '\"' ) + MsgDev( D_ERROR, "Parsing error on or before line %d of %s.seq: expected quote (\"), found '%c' instead\n", g_lineNum, g_sequenceParseFileName, ch ); + + for( write = str; *g_scan && len; write++, g_scan++, len-- ) + { + if( *g_scan == '\"' ) + break; + + if( *g_scan == '\n' ) + g_lineNum++; + + *write = *g_scan; + } + + *write = 0; + g_scan++; + + if( dest ) *dest = copystring( str ); +} + +/* +============= +Sequence_ConfirmCarriageReturnOrSymbol + +============= +*/ +qboolean Sequence_ConfirmCarriageReturnOrSymbol( char symbol ) +{ + if( Sequence_SkipWhitespace( ) ) + return true; + return *g_scan == symbol; +} + + +/* +============= +Sequence_IsCommandAModifier + +============= +*/ +qboolean Sequence_IsCommandAModifier( sequenceCommandEnum_e commandEnum ) +{ + int i; + + for( i = 0; i < ARRAYSIZE( g_sequenceCommandMappingTable ); i++ ) + { + if( g_sequenceCommandMappingTable[i].commandEnum == commandEnum ) + return ( g_sequenceCommandMappingTable[i].commandType == SEQUENCE_TYPE_MODIFIER ); + } + + MsgDev( D_ERROR, "Internal error caused by line %d of %s.seq: unknown command enum = %d\n", g_lineNum, g_sequenceParseFileName, commandEnum ); + return false; +} + +/* +============= +Sequence_ReadCommandData + +============= +*/ +void Sequence_ReadCommandData( sequenceCommandEnum_e commandEnum, sequenceCommandLine_s *defaults ) +{ + char temp[1024]; + + if( commandEnum >= SEQUENCE_MODIFIER_EFFECT || commandEnum <= SEQUENCE_MODIFIER_TEXTCHANNEL ) + defaults->modifierBitField |= BIT( SEQUENCE_MODIFIER_EFFECT - SEQUENCE_COMMAND_NOOP ); + + switch( commandEnum ) + { + case SEQUENCE_COMMAND_PAUSE: + defaults->delay = Sequence_ReadFloat( ); + break; + + case SEQUENCE_COMMAND_FIRETARGETS: + Sequence_ReadQuotedString( &defaults->fireTargetNames, temp, sizeof( temp ) ); + break; + + case SEQUENCE_COMMAND_KILLTARGETS: + Sequence_ReadQuotedString( &defaults->killTargetNames, temp, sizeof( temp ) ); + break; + + case SEQUENCE_COMMAND_TEXT: + Sequence_ReadQuotedString( &defaults->clientMessage.pMessage, temp, sizeof( temp ) ); + break; + + case SEQUENCE_COMMAND_SOUND: + Sequence_ReadString( &defaults->soundFileName, temp, sizeof( temp ) ); + break; + + case SEQUENCE_COMMAND_GOSUB: + Sequence_ReadString( &defaults->clientMessage.pName, temp, sizeof( temp ) ); + break; + + case SEQUENCE_COMMAND_SENTENCE: + Sequence_ReadString( &defaults->sentenceName, temp, sizeof( temp ) ); + break; + + case SEQUENCE_COMMAND_REPEAT: + defaults->repeatCount = Sequence_ReadInt( ); + break; + + case SEQUENCE_MODIFIER_EFFECT: + defaults->clientMessage.effect = Sequence_ReadInt( ); + break; + + case SEQUENCE_MODIFIER_POSITION: + defaults->clientMessage.x = Sequence_ReadFloat( ); + defaults->clientMessage.y = Sequence_ReadFloat( ); + break; + + case SEQUENCE_MODIFIER_COLOR: + defaults->clientMessage.r1 = Sequence_ReadInt( ); + defaults->clientMessage.g1 = Sequence_ReadInt( ); + defaults->clientMessage.b1 = Sequence_ReadInt( ); + defaults->clientMessage.a1 = 255; + break; + + case SEQUENCE_MODIFIER_COLOR2: + defaults->clientMessage.r2 = Sequence_ReadInt( ); + defaults->clientMessage.g2 = Sequence_ReadInt( ); + defaults->clientMessage.b2 = Sequence_ReadInt( ); + defaults->clientMessage.a2 = 255; + break; + + case SEQUENCE_MODIFIER_FADEIN: + defaults->clientMessage.fadein = Sequence_ReadFloat( ); + break; + + case SEQUENCE_MODIFIER_FADEOUT: + defaults->clientMessage.fadeout = Sequence_ReadFloat( ); + break; + + case SEQUENCE_MODIFIER_HOLDTIME: + defaults->clientMessage.holdtime = Sequence_ReadFloat( ); + break; + + case SEQUENCE_MODIFIER_FXTIME: + defaults->clientMessage.fxtime = Sequence_ReadFloat( ); + break; + + case SEQUENCE_MODIFIER_SPEAKER: + Sequence_ReadString( &defaults->speakerName, temp, sizeof( temp ) ); + break; + + case SEQUENCE_MODIFIER_LISTENER: + Sequence_ReadString( &defaults->listenerName, temp, sizeof( temp ) ); + break; + + case SEQUENCE_MODIFIER_TEXTCHANNEL: + defaults->textChannel = Sequence_ReadInt( ); + break; + + default: + MsgDev( D_ERROR, "Internal error caused by line %d of %s.seq: unknown command enum = %d\n", g_lineNum, g_sequenceParseFileName, commandEnum ); + } +} + +/* +============= +Sequence_ParseModifier + +============= +*/ +char Sequence_ParseModifier( sequenceCommandLine_s *defaults ) +{ + char modifierName[MAX_STRING]; + char delimiter; + sequenceCommandEnum_e modifierEnum; + + Sequence_GetNameValueString( modifierName, MAX_STRING ); + modifierEnum = Sequence_GetCommandEnumForName( modifierName, SEQUENCE_TYPE_MODIFIER ); + + if( modifierEnum == SEQUENCE_COMMAND_ERROR ) + MsgDev( D_ERROR, "Parsing error on line %d of %s.seq: unknown modifier \"%s\"\n", g_lineNum, g_sequenceParseFileName, modifierName ); + + if( !Sequence_IsCommandAModifier( modifierEnum ) ) + MsgDev( D_ERROR, "Parsing error on line %d of %s.seq: \"%s\" is a #command, not a $modifier\n", g_lineNum, g_sequenceParseFileName, modifierName ); + + delimiter = Sequence_GetSymbol( ); + + if( delimiter != '=' ) + MsgDev( D_ERROR, "Parsing error on or after line %d of %s.seq: after modifier \"%s\", expected '=', found '%c'\n", g_lineNum, g_sequenceParseFileName, modifierName, delimiter ); + + Sequence_ReadCommandData( modifierEnum, defaults ); + + if( !Sequence_ConfirmCarriageReturnOrSymbol( ',' ) ) + MsgDev( D_ERROR, "Parsing error on line %d of %s.seq: after value(s) for modifier \"%s\", expected ',' or End-Of-Line; found '%c'\n", g_lineNum, g_sequenceParseFileName, modifierName, *g_scan ); + + return Sequence_GetSymbol( ); +} + +/* +============= +Sequence_AddCommandLineToEntry + +============= +*/ +void Sequence_AddCommandLineToEntry( sequenceCommandLine_s *commandLine, sequenceEntry_s *entry ) +{ + sequenceCommandLine_s *scan; + + if( entry->firstCommand ) + { + for( scan = entry->firstCommand; scan->nextCommandLine; scan = scan->nextCommandLine ); + scan->nextCommandLine = commandLine; + } + else entry->firstCommand = commandLine; + + commandLine->nextCommandLine = NULL; +} + +/* +============= +Sequence_ParseModifierLine + +============= +*/ +char Sequence_ParseModifierLine( sequenceEntry_s *entry, sequenceCommandType_e modifierType ) +{ + sequenceCommandLine_s *newCommandLine; + char delimiter = ','; + + while( delimiter == ',' ) + { + switch( modifierType ) + { + case SEQUENCE_TYPE_COMMAND: + newCommandLine = Z_Malloc( sizeof( sequenceCommandLine_s ) ); + memset( newCommandLine, 0, sizeof( sequenceCommandLine_s ) ); + newCommandLine->commandType = SEQUENCE_COMMAND_MODIFIER; + Sequence_AddCommandLineToEntry( newCommandLine, entry ); + delimiter = Sequence_ParseModifier( newCommandLine ); + break; + + case SEQUENCE_TYPE_MODIFIER: + delimiter = Sequence_ParseModifier( &g_fileScopeDefaults ); + break; + } + } + + return delimiter; +} + +/* +============= +Sequence_ParseCommand + +============= +*/ +char Sequence_ParseCommand( sequenceCommandLine_s *newCommandLine ) +{ + char commandName[MAX_STRING], ch; + sequenceCommandEnum_e commandEnum; + sequenceCommandLine_s *modifierCommandLine; + + Sequence_GetNameValueString( commandName, MAX_STRING ); + commandEnum = Sequence_GetCommandEnumForName( commandName, SEQUENCE_TYPE_COMMAND ); + + if( commandEnum == SEQUENCE_COMMAND_ERROR ) + MsgDev( D_ERROR, "Parsing error on line %d of %s.seq: unknown command \"%s\"\n", g_lineNum, g_sequenceParseFileName, commandName ); + + if( Sequence_IsCommandAModifier( commandEnum ) ) + { + modifierCommandLine = Z_Malloc( sizeof( sequenceCommandLine_s ) ); + memset( modifierCommandLine, 0, sizeof( sequenceCommandLine_s ) ); + modifierCommandLine->commandType = SEQUENCE_COMMAND_POSTMODIFIER; + + for( ; newCommandLine->nextCommandLine; newCommandLine = newCommandLine->nextCommandLine ); + + newCommandLine->nextCommandLine = modifierCommandLine; + newCommandLine = modifierCommandLine; + } + + ch = Sequence_GetSymbol( ); + if( ch != '=' ) + MsgDev( D_ERROR, "Parsing error on or before line %d of %s.seq: after command \"%s\", expected '=', found '%c'\n", + g_lineNum, g_sequenceParseFileName, commandName, ch ); + + Sequence_ReadCommandData( commandEnum, newCommandLine ); + return Sequence_GetSymbol( ); +} + +/* +============= +Sequence_ParseCommandLine + +============= +*/ +char Sequence_ParseCommandLine( sequenceEntry_s *entry ) +{ + char symbol; + sequenceCommandLine_s *newCommandLine; + + newCommandLine = Z_Malloc( sizeof( sequenceCommandLine_s ) ); + memset( newCommandLine, 0, sizeof( sequenceCommandLine_s ) ); + + Sequence_ResetDefaults( newCommandLine, &g_blockScopeDefaults ); + Sequence_AddCommandLineToEntry( newCommandLine, entry ); + + symbol = Sequence_ParseCommand( newCommandLine ); + + while( symbol == ',' ) + { + symbol = Sequence_ParseCommand( newCommandLine ); + } + + return symbol; +} + +/* +============= +Sequence_ParseMacro + +============= +*/ +char Sequence_ParseMacro( sequenceEntry_s *entry ) +{ + char symbol; + sequenceCommandLine_s *newCommandLine; + + newCommandLine = Z_Malloc( sizeof( sequenceCommandLine_s ) ); + memset( newCommandLine, 0, sizeof( sequenceCommandLine_s ) ); + + Sequence_ResetDefaults( newCommandLine, &g_blockScopeDefaults ); + Sequence_AddCommandLineToEntry( newCommandLine, entry ); + Sequence_ReadCommandData( SEQUENCE_COMMAND_GOSUB, newCommandLine ); + + symbol = Sequence_GetSymbol( ); + + while( symbol == ',' ) + { + symbol = Sequence_ParseCommand( newCommandLine ); + } + + return symbol; +} + +/* +============= +Sequence_ParseLine + +============= +*/ +char Sequence_ParseLine( char start, sequenceEntry_s *entry ) +{ + char end = '\0'; + + switch( start ) + { + case '#': + end = Sequence_ParseCommandLine( entry ); + break; + + case '$': + end = Sequence_ParseModifierLine( entry, SEQUENCE_TYPE_MODIFIER ); + break; + + case '@': + end = Sequence_ParseMacro( entry ); + break; + + default: + MsgDev( D_ERROR, "Parsing error on line %d of %s.seq: line must begin with either '#' (command) or '$' (modifier); found '%c'\n", g_lineNum, g_sequenceParseFileName, start ); + } + + return end; +} + +/* +============= +Sequence_CalcEntryDuration + +============= +*/ +float Sequence_CalcEntryDuration( sequenceEntry_s *entry ) +{ + float duration; + sequenceCommandLine_s *cmd; + + duration = 0; + + for( cmd = entry->firstCommand; cmd; cmd = cmd->nextCommandLine ) + duration += cmd->delay; + + return duration; +} + +/* +============= +Sequence_DoesEntryContainInfiniteLoop + +============= +*/ +qboolean Sequence_DoesEntryContainInfiniteLoop( sequenceEntry_s *entry ) +{ + sequenceCommandLine_s *cmd; + + for( cmd = entry->firstCommand; cmd; cmd = cmd->nextCommandLine ) + { + if( cmd->repeatCount < 0 ) + return true; + } + + return false; +} + +/* +============= +Sequence_IsEntrySafe + +============= +*/ +qboolean Sequence_IsEntrySafe( sequenceEntry_s *entry ) +{ + float duration; + sequenceCommandLine_s *cmd; + + duration = 0; + + for( cmd = entry->firstCommand; cmd; cmd = cmd->nextCommandLine ) + { + duration += cmd->delay; + + if( cmd->repeatCount < 0 ) + { + if( duration <= 0 ) + return false; + } + } + + return true; +} + +/* +============= +Sequence_CreateDefaultsCommand + +============= +*/ +void Sequence_CreateDefaultsCommand( sequenceEntry_s *entry ) +{ + sequenceCommandLine_s *cmd; + + cmd = Z_Malloc( sizeof( sequenceCommandLine_s ) ); + memset( cmd, 0, sizeof( sequenceCommandLine_s ) ); + + Sequence_ResetDefaults( cmd, &g_fileScopeDefaults ); + cmd->commandType = SEQUENCE_COMMAND_SETDEFAULTS; + cmd->modifierBitField = SEQUENCE_MODIFIER_EFFECT_BIT | + SEQUENCE_MODIFIER_POSITION_BIT | + SEQUENCE_MODIFIER_COLOR_BIT | + SEQUENCE_MODIFIER_COLOR2_BIT | + SEQUENCE_MODIFIER_FADEIN_BIT | + SEQUENCE_MODIFIER_FADEOUT_BIT | + SEQUENCE_MODIFIER_HOLDTIME_BIT | + SEQUENCE_MODIFIER_FXTIME_BIT; + + Sequence_AddCommandLineToEntry( cmd, entry ); +} + + +/* +============= +Sequence_ParseEntry + +============= +*/ +char Sequence_ParseEntry( void ) +{ + char symbol; + char token[MAX_STRING]; + sequenceEntry_s *entry; + + Sequence_GetNameValueString( token, MAX_STRING ); + symbol = Sequence_GetSymbol( ); + + if( symbol != '{' ) + MsgDev( D_ERROR, "Parsing error on line %d of %s.seq: expected '{' to start a\n new entry block; found '%c' instead!", g_lineNum, g_sequenceParseFileName, symbol ); + + entry = Z_Malloc( sizeof( sequenceEntry_s ) ); + Sequence_ResetDefaults( &g_blockScopeDefaults, &g_fileScopeDefaults ); + entry->entryName = copystring( token ); + entry->fileName = copystring( g_sequenceParseFileName ); + entry->isGlobal = g_sequenceParseFileIsGlobal; + entry->firstCommand = NULL; + Sequence_CreateDefaultsCommand( entry ); + + symbol = Sequence_GetSymbol( ); + + while( symbol != '}' ) + { + symbol = Sequence_ParseLine( symbol, entry ); + } + + if( !Sequence_IsEntrySafe( entry ) ) + MsgDev( D_ERROR, "Logic error in file %s.seq before line %d: execution of entry \"%%%s\" would cause an infinite loop!", g_sequenceParseFileName, g_lineNum, entry->entryName ); + + entry->nextEntry = g_sequenceList; + g_sequenceList = entry; + + return Sequence_GetSymbol( ); +} + +/* +============= +Sequence_FindSentenceGroup + +============= +*/ +sentenceGroupEntry_s *Sequence_FindSentenceGroup( const char *groupName ) +{ + sentenceGroupEntry_s *groupEntry; + + for( groupEntry = g_sentenceGroupList; groupEntry; groupEntry = groupEntry->nextEntry ) + { + if( !Q_stricmp( groupEntry->groupName, groupName ) ) + return groupEntry; + } + + return NULL; +} + +/* +============= +Sequence_GetSentenceByIndex + +============= +*/ +sentenceEntry_s *Sequence_GetSentenceByIndex( unsigned int index ) +{ + sentenceEntry_s *sentenceEntry; + sentenceGroupEntry_s *groupEntry; + unsigned int sentenceCount=0; + + for( groupEntry = g_sentenceGroupList; groupEntry; groupEntry = groupEntry->nextEntry ) + { + sentenceCount += groupEntry->numSentences; + + if( index < sentenceCount ) + { + for( sentenceEntry = groupEntry->firstSentence; sentenceEntry; sentenceEntry = sentenceEntry->nextEntry ) + { + if( sentenceEntry->index == index ) + return sentenceEntry; + } + } + } + + return NULL; +} + + +/* +============= +Sequence_PickSentence + +============= +*/ +sentenceEntry_s *Sequence_PickSentence( const char *groupName, int pickMethod, int *picked ) +{ + sentenceEntry_s *sentenceEntry; + sentenceGroupEntry_s *groupEntry; + unsigned int pickedIdx; + unsigned int entryIdx; + + groupEntry = Sequence_FindSentenceGroup( groupName ); + + if( groupEntry ) + { + pickedIdx = COM_RandomLong( 0, groupEntry->numSentences - 1 ); + sentenceEntry = groupEntry->firstSentence; + + for( entryIdx = pickedIdx; entryIdx; entryIdx-- ) + sentenceEntry = sentenceEntry->nextEntry; + } + else + { + pickedIdx = 0; + sentenceEntry = NULL; + } + + if( picked ) + *picked = pickedIdx; + + return sentenceEntry; +} + +/* +============= +Sequence_AddSentenceGroup + +============= +*/ +sentenceGroupEntry_s *Sequence_AddSentenceGroup( char *groupName ) +{ + sentenceGroupEntry_s *entry, *last; + + entry = Z_Malloc( sizeof( sentenceGroupEntry_s ) ); + entry->numSentences = 0; + entry->firstSentence = NULL; + entry->nextEntry = NULL; + entry->groupName = copystring( groupName ); + + if( g_sentenceGroupList ) + { + for( last = g_sentenceGroupList; last->nextEntry; last = last->nextEntry ); + last->nextEntry = entry; + } + else + { + g_sentenceGroupList = entry; + } + + return entry; +} + +/* +============= +Sequence_AddSentenceToGroup + +============= +*/ +void Sequence_AddSentenceToGroup( char *groupName, char *data ) +{ + sentenceEntry_s *entry, *last; + sentenceGroupEntry_s *group; + + group = Sequence_FindSentenceGroup( groupName ); + + if( !group ) + { + group = Sequence_AddSentenceGroup( groupName ); + + if( !group ) + MsgDev( D_ERROR, "Unable to allocate sentence group %s at line %d in file %s.seq", groupName, g_lineNum, g_sequenceParseFileName ); + } + + entry = Z_Malloc( sizeof( sentenceEntry_s ) ); + entry->nextEntry = NULL; + entry->data = copystring( data ); + entry->index = g_nonGlobalSentences; + entry->isGlobal = g_sequenceParseFileIsGlobal; + + group->numSentences++; + g_nonGlobalSentences++; + + if( group->firstSentence ) + { + for( last = group->firstSentence; last->nextEntry; last = last->nextEntry ); + + last->nextEntry = entry; + } + else + { + group->firstSentence = entry; + } +} + +/* +============= +Sequence_ParseSentenceLine + +============= +*/ +qboolean Sequence_ParseSentenceLine( void ) +{ + char data[1024]; + char fullgroup[64]; + char groupName[64]; + char *c; + int lastCharacterPos; + size_t len; + + len = Sequence_GetToken( fullgroup, sizeof( fullgroup ) ); + + if( *fullgroup == '}' ) + return true; + + c = fullgroup + len; + + while( !isalpha( *c ) && *c != '_' ) + c--; + + c += 1; + + if( *c ) + *c = 0; + + strcpy( groupName, fullgroup ); + + len = Sequence_GetLine( data, sizeof( data ) ); + lastCharacterPos = len - 1; + + if( data[lastCharacterPos] == '\n' || data[lastCharacterPos] == '\r' ) + data[lastCharacterPos] = 0; + + Sequence_AddSentenceToGroup( groupName, data ); + return false; +} + +/* +============== +Sequence_ParseSentenceBlock + +============== +*/ +char Sequence_ParseSentenceBlock( void ) +{ + qboolean end = false; + char ch = Sequence_GetSymbol( ); + if( ch != '{' ) + MsgDev( D_ERROR, "Parsing error on line %d of %s.seq: expected '{' to start a\n new sentence block; found '%c' instead!", g_lineNum, g_sequenceParseFileName, ch ); + + while( !end ) + { + end = Sequence_ParseSentenceLine( ); + } + + return Sequence_GetSymbol( ); +} + +/* +============== +Sequence_ParseGlobalDataBlock + +============== +*/ +char Sequence_ParseGlobalDataBlock( void ) +{ + char token[MAX_STRING]; + + Sequence_GetNameValueString( token, MAX_STRING ); + + if( Q_stricmp( token, "Sentences" ) ) + MsgDev( D_ERROR, "Syntax error in file %s.seq on line %d: found global data block symbol '!' with unknown data type \"%s\"", g_sequenceParseFileName, g_lineNum, token ); + + return Sequence_ParseSentenceBlock( ); +} + +/* +============== +Sequence_GetEntryForName + +============== +*/ +sequenceEntry_s *Sequence_GetEntryForName( const char *entryName ) +{ + sequenceEntry_s *scan; + + for( scan = g_sequenceList; scan; scan = scan->nextEntry ) + { + if( !Q_stricmp( entryName, scan->entryName ) ) + return scan; + } + + return NULL; +} + +/* +============== +Sequence_CopyCommand + +============== +*/ +sequenceCommandLine_s *Sequence_CopyCommand( sequenceCommandLine_s *commandOrig ) +{ + sequenceCommandLine_s *commandCopy; + + commandCopy = Z_Malloc( sizeof( sequenceCommandLine_s ) ); + + commandCopy->commandType = commandOrig->commandType; + commandCopy->clientMessage = commandOrig->clientMessage; + commandCopy->clientMessage.pMessage = copystring( commandOrig->clientMessage.pMessage ); + commandCopy->clientMessage.pName = copystring( commandOrig->clientMessage.pName ); + commandCopy->speakerName = copystring( commandOrig->speakerName ); + commandCopy->listenerName = copystring( commandOrig->listenerName ); + commandCopy->soundFileName = copystring( commandOrig->soundFileName ); + commandCopy->sentenceName = copystring( commandOrig->sentenceName ); + commandCopy->fireTargetNames = copystring( commandOrig->fireTargetNames ); + commandCopy->killTargetNames = copystring( commandOrig->killTargetNames ); + commandCopy->delay = commandOrig->delay; + commandCopy->repeatCount = commandOrig->repeatCount; + commandCopy->textChannel = commandOrig->textChannel; + commandCopy->modifierBitField = commandOrig->modifierBitField; + commandCopy->nextCommandLine = NULL; + + return commandCopy; +} + +/* +============== +Sequence_CopyCommandList + +============== +*/ +sequenceCommandLine_s *Sequence_CopyCommandList( sequenceCommandLine_s *list ) +{ + sequenceCommandLine_s *scan, *copy, *new, *prev; + + copy = NULL; + prev = NULL; + + for( scan = list; scan; scan = scan->nextCommandLine ) + { + if( scan->commandType != SEQUENCE_COMMAND_SETDEFAULTS ) + { + new = Sequence_CopyCommand( scan ); + + if( prev ) + { + prev->nextCommandLine = new; + prev = new; + } + else + { + prev = new; + copy = new; + } + } + } + + return copy; +} + +/* +============== +Sequence_ExpandGosubsForEntry + +============== +*/ +qboolean Sequence_ExpandGosubsForEntry( sequenceEntry_s *entry ) +{ + sequenceCommandLine_s *cmd, *copyList, *scan; + sequenceEntry_s *gosubEntry; + qboolean foundGosubs = false; + + for( cmd = entry->firstCommand; cmd; cmd = cmd->nextCommandLine ) + { + if( !cmd->clientMessage.pName ) + continue; + + if( !Q_stricmp( cmd->clientMessage.pName, entry->entryName ) ) + MsgDev( D_ERROR, "Error in %s.seq: entry \"%s\" gosubs itself!\n", entry->fileName, entry->entryName ); + + gosubEntry = Sequence_GetEntryForName( cmd->clientMessage.pName ); + + if( !gosubEntry ) + MsgDev( D_ERROR, "Error in %s.seq: Gosub in entry \"%s\" specified unknown entry \"%s\"\n", entry->fileName, entry->entryName, cmd->clientMessage.pName ); + + foundGosubs = true; + copyList = Sequence_CopyCommandList( gosubEntry->firstCommand ); + + if( copyList ) + { + for( scan = copyList->nextCommandLine; scan; scan = scan->nextCommandLine ); + + scan->nextCommandLine = cmd->nextCommandLine; + + Z_Free( cmd->clientMessage.pName ); + cmd->clientMessage.pName = NULL; + cmd = scan; + } + else + { + Z_Free( cmd->clientMessage.pName ); + cmd->clientMessage.pName = NULL; + } + } + + return !foundGosubs; +} + +/* +============== +Sequence_ExpandAllGosubs + +============== +*/ +void Sequence_ExpandAllGosubs( void ) +{ + sequenceEntry_s *scan; + qboolean isComplete = true; + + while( !isComplete ) + { + for( scan = g_sequenceList; scan; scan = scan->nextEntry ) + { + isComplete = Sequence_ExpandGosubsForEntry( scan ); + } + } +} + +/* +============== +Sequence_FlattenEntry + +============== +*/ +void Sequence_FlattenEntry( sequenceEntry_s *entry ) +{ + sequenceCommandLine_s *cmd, *last = NULL; + + for( cmd = entry->firstCommand; cmd; cmd = cmd->nextCommandLine ) + { + switch( cmd->commandType ) + { + case SEQUENCE_COMMAND_SETDEFAULTS: + Sequence_WriteDefaults( cmd, &g_blockScopeDefaults ); + cmd->commandType = SEQUENCE_COMMAND_NOOP; + break; + + case SEQUENCE_COMMAND_MODIFIER: + Sequence_WriteDefaults( cmd, &g_blockScopeDefaults ); + break; + + case SEQUENCE_COMMAND_POSTMODIFIER: + Sequence_WriteDefaults( cmd, last ); + break; + + default: + Sequence_BakeDefaults( cmd, &g_blockScopeDefaults ); + last = cmd; + } + } +} + +/* +============== +Sequence_FlattenAllEntries + +============== +*/ +void Sequence_FlattenAllEntries( void ) +{ + sequenceEntry_s *entry; + + for( entry = g_sequenceList; entry; entry = entry->nextEntry ) + Sequence_FlattenEntry( entry ); +} + +/* +============== +Sequence_ParseBuffer + +============== +*/ +void Sequence_ParseBuffer( byte *buffer, int bufferSize ) +{ + char symbol; + + g_lineNum = 1; + g_scan = buffer; + g_lineScan = g_scan; + + Sequence_StripComments( buffer, &bufferSize ); + Sequence_ResetDefaults( &g_fileScopeDefaults, NULL ); + + symbol = Sequence_GetSymbol( ); + + while( symbol ) + { + switch( symbol ) + { + case '$': + do + symbol = Sequence_ParseModifier( &g_fileScopeDefaults ); + while( symbol == ',' ); + break; + + case '%': + symbol = Sequence_ParseEntry( ); + break; + + case '!': + symbol = Sequence_ParseGlobalDataBlock( ); + break; + + default: + MsgDev( D_ERROR, "Parsing error on line %d of %s.seq: At file scope, lines must begin with '$' (modifier) or '%%' (entry block) or '!' (sentence / global data block); found '%c'\n", g_lineNum, g_sequenceParseFileName, symbol ); + } + } + + Sequence_ExpandAllGosubs( ); + Sequence_FlattenAllEntries( ); +} + +/* +============== +Sequence_ParseFile + +============== +*/ +void Sequence_ParseFile( const char *fileName, qboolean isGlobal ) +{ + byte *buffer; + fs_offset_t bufSize = 0; + + Q_strcpy( g_sequenceParseFileName, fileName ); + g_sequenceParseFileIsGlobal = isGlobal; + + buffer = FS_LoadFile( va("sequences/%s.seq", fileName ), &bufSize, true ); + + if( !buffer ) + return; + + MsgDev( D_INFO, "reading sequence file: %s\n", fileName ); + + Sequence_ParseBuffer( buffer, bufSize ); + + Mem_Free( buffer ); +} + +/* +============== +Sequence_Init + +============== +*/ +void Sequence_Init( void ) +{ + Sequence_ParseFile( "global", true ); +} + +/* +============== +SequenceGet + +============== +*/ +sequenceEntry_s *Sequence_Get( const char *fileName, const char *entryName ) +{ + sequenceEntry_s *scan; + + for( scan = g_sequenceList; scan; scan = scan->nextEntry ) + { + if( ( !fileName || !Q_stricmp( fileName, scan->fileName ) ) && // a1ba: add filename check, even if originally it is ignored + !Q_stricmp( entryName, scan->entryName ) ) + return scan; + } + + return NULL; +} + +/* +============== +Sequence_FreeCommand + +============== +*/ +void Sequence_FreeCommand( sequenceCommandLine_s *kill ) +{ + Z_Free( kill->fireTargetNames ); + Z_Free( kill->speakerName ); + Z_Free( kill->listenerName ); + Z_Free( kill->soundFileName ); + Z_Free( kill->sentenceName ); + Z_Free( kill->clientMessage.pName ); + Z_Free( kill->clientMessage.pMessage ); +} + +/* +============== +Sequence_FreeEntry + +============== +*/ +void Sequence_FreeEntry( sequenceEntry_s *kill ) +{ + sequenceCommandLine_s *dead; + + Z_Free( kill->entryName ); + Z_Free( kill->fileName ); + + for( dead = kill->firstCommand; dead; dead = dead->nextCommandLine ) + { + kill->firstCommand = dead->nextCommandLine; + Sequence_FreeCommand( dead ); + } + + Z_Free( kill ); +} + +/* +============== +Sequence_FreeSentence + +============== +*/ +void Sequence_FreeSentence( sentenceEntry_s *sentenceEntry ) +{ + Z_Free( sentenceEntry->data ); + Z_Free( sentenceEntry ); +} + +/* +============== +Sequence_FreeSentenceGroup + +============== +*/ +void Sequence_FreeSentenceGroup( sentenceGroupEntry_s *groupEntry ) +{ + Z_Free( groupEntry->groupName ); + Z_Free( groupEntry ); +} + +/* +============== +Sequence_FreeSentenceGroupEntries + +============== +*/ +void Sequence_FreeSentenceGroupEntries( sentenceGroupEntry_s *groupEntry, qboolean purgeGlobals ) +{ + sentenceEntry_s *sentenceEntry; + sentenceEntry_s *deadSentence; + sentenceEntry_s *prevSentence; + + sentenceEntry = groupEntry->firstSentence; + prevSentence = NULL; + + while( sentenceEntry ) + { + if( !sentenceEntry->isGlobal || purgeGlobals ) + { + if( prevSentence ) + prevSentence->nextEntry = sentenceEntry->nextEntry; + else + groupEntry->firstSentence = sentenceEntry->nextEntry; + + groupEntry->numSentences--; + g_nonGlobalSentences--; + + deadSentence = sentenceEntry; + sentenceEntry = sentenceEntry->nextEntry; + + Sequence_FreeSentence( deadSentence ); + } + else + { + prevSentence = sentenceEntry; + sentenceEntry = sentenceEntry->nextEntry; + } + } +} + +/* +============== +Sequence_PurgeEntries + +============== +*/ +void Sequence_PurgeEntries( qboolean purgeGlobals ) +{ + sequenceEntry_s *scan; + sequenceEntry_s *dead; + sequenceEntry_s *prev; + sentenceGroupEntry_s *groupEntry; + sentenceGroupEntry_s *deadGroup; + sentenceGroupEntry_s *prevGroup; + + dead = NULL; + prev = NULL; + + for( scan = g_sequenceList; scan; ) + { + if( !scan->isGlobal || purgeGlobals ) + { + if( prev ) + prev->nextEntry = scan->nextEntry; + else + g_sequenceList = scan->nextEntry; + + dead = scan; + scan = scan->nextEntry; + Sequence_FreeEntry( dead ); + } + else + { + prev = scan; + scan = scan->nextEntry; + } + } + + groupEntry = g_sentenceGroupList; + prevGroup = NULL; + + while( groupEntry ) + { + Sequence_FreeSentenceGroupEntries( groupEntry, purgeGlobals ); + + if( groupEntry->numSentences ) + { + prevGroup = groupEntry; + groupEntry = groupEntry->nextEntry; + } + else + { + if( prevGroup ) + prevGroup->nextEntry = groupEntry->nextEntry; + else + g_sentenceGroupList = groupEntry->nextEntry; + + deadGroup = groupEntry; + groupEntry = groupEntry->nextEntry; + Sequence_FreeSentenceGroup( deadGroup ); + } + } +} + +/* +============== +Sequence_OnLevelLoad + +============== +*/ +void Sequence_OnLevelLoad( const char *mapName ) +{ + Sequence_PurgeEntries( false ); + Sequence_ParseFile( mapName, false ); +} diff --git a/engine/common/soundlib/snd_utils.c b/engine/common/soundlib/snd_utils.c index 382752e7..552d04fb 100644 --- a/engine/common/soundlib/snd_utils.c +++ b/engine/common/soundlib/snd_utils.c @@ -93,6 +93,49 @@ byte *Sound_Copy( size_t size ) return out; } +uint GAME_EXPORT Sound_GetApproxWavePlayLen( const char *filepath ) +{ + file_t *f; + wavehdr_t wav; + size_t filesize; + float seconds; + uint samples; + + f = FS_Open( filepath, "rb", false ); + if( !f ) return 0; + + if( FS_Read( f, &wav, sizeof( wav )) != sizeof( wav )) + { + FS_Close( f ); + return 0; + } + + filesize = FS_FileLength( f ); + filesize -= ( sizeof( wavehdr_t ) + sizeof( chunkhdr_t )); + + FS_Close( f ); + + // is real wav file ? + if( wav.riff_id != RIFFHEADER || wav.wave_id != WAVEHEADER || wav.fmt_id != FORMHEADER ) + return 0; + + if( wav.wFormatTag != 1 ) + return 0; + + if( wav.nChannels != 1 && wav.nChannels != 2 ) + return 0; + + if( wav.nBitsPerSample != 8 && wav.nBitsPerSample != 16 ) + return 0; + + // calc samplecount + seconds = (float)filesize / wav.nAvgBytesPerSec / wav.nChannels; + samples = (uint)(( wav.nSamplesPerSec * wav.nChannels ) * seconds ); + + // g-cont. this function returns samplecount or time in milliseconds ??? + return (uint)(seconds * 1000); +} + /* ================ Sound_ConvertToSigned @@ -238,4 +281,4 @@ qboolean Sound_Process( wavdata_t **wav, int rate, int width, uint flags ) *wav = snd; return false; -} \ No newline at end of file +} diff --git a/engine/sequence.h b/engine/sequence.h new file mode 100644 index 00000000..dd1cbc41 --- /dev/null +++ b/engine/sequence.h @@ -0,0 +1,205 @@ +//--------------------------------------------------------------------------- +// +// S c r i p t e d S e q u e n c e s +// +//--------------------------------------------------------------------------- +#ifndef _INCLUDE_SEQUENCE_H_ +#define _INCLUDE_SEQUENCE_H_ + + +#ifndef _DEF_BYTE_ +//typedef unsigned char byte; +#endif + +#ifndef CDLL_INT_H +//--------------------------------------------------------------------------- +// client_textmessage_t +//--------------------------------------------------------------------------- +typedef struct client_textmessage_s +{ + int effect; + byte r1, g1, b1, a1; // 2 colors for effects + byte r2, g2, b2, a2; + float x; + float y; + float fadein; + float fadeout; + float holdtime; + float fxtime; + char *pName; + char *pMessage; +} client_textmessage_t; +#endif + + +//--------------------------------------------------------------------------- +// sequenceCommandEnum_e +// +// Enumerated sequence command types. +//--------------------------------------------------------------------------- +enum sequenceCommandEnum_ +{ + SEQUENCE_COMMAND_ERROR = -1, + SEQUENCE_COMMAND_PAUSE = 0, + SEQUENCE_COMMAND_FIRETARGETS, + SEQUENCE_COMMAND_KILLTARGETS, + SEQUENCE_COMMAND_TEXT, + SEQUENCE_COMMAND_SOUND, + SEQUENCE_COMMAND_GOSUB, + SEQUENCE_COMMAND_SENTENCE, + SEQUENCE_COMMAND_REPEAT, + SEQUENCE_COMMAND_SETDEFAULTS, + SEQUENCE_COMMAND_MODIFIER, + SEQUENCE_COMMAND_POSTMODIFIER, + SEQUENCE_COMMAND_NOOP, + + SEQUENCE_MODIFIER_EFFECT, + SEQUENCE_MODIFIER_POSITION, + SEQUENCE_MODIFIER_COLOR, + SEQUENCE_MODIFIER_COLOR2, + SEQUENCE_MODIFIER_FADEIN, + SEQUENCE_MODIFIER_FADEOUT, + SEQUENCE_MODIFIER_HOLDTIME, + SEQUENCE_MODIFIER_FXTIME, + SEQUENCE_MODIFIER_SPEAKER, + SEQUENCE_MODIFIER_LISTENER, + SEQUENCE_MODIFIER_TEXTCHANNEL, +}; +typedef enum sequenceCommandEnum_ sequenceCommandEnum_e; + +//-------------------------------------------------------------------------- +// sequenceDefaultBits_e +// +// Enumerated list of possible modifiers for a command. This enumeration +// is used in a bitarray controlling what modifiers are specified for a command. +//--------------------------------------------------------------------------- +enum sequenceModifierBits +{ + SEQUENCE_MODIFIER_EFFECT_BIT = (1 << 1), + SEQUENCE_MODIFIER_POSITION_BIT = (1 << 2), + SEQUENCE_MODIFIER_COLOR_BIT = (1 << 3), + SEQUENCE_MODIFIER_COLOR2_BIT = (1 << 4), + SEQUENCE_MODIFIER_FADEIN_BIT = (1 << 5), + SEQUENCE_MODIFIER_FADEOUT_BIT = (1 << 6), + SEQUENCE_MODIFIER_HOLDTIME_BIT = (1 << 7), + SEQUENCE_MODIFIER_FXTIME_BIT = (1 << 8), + SEQUENCE_MODIFIER_SPEAKER_BIT = (1 << 9), + SEQUENCE_MODIFIER_LISTENER_BIT = (1 << 10), + SEQUENCE_MODIFIER_TEXTCHANNEL_BIT = (1 << 11), +}; +typedef enum sequenceModifierBits sequenceModifierBits_e ; + + +//--------------------------------------------------------------------------- +// sequenceCommandType_e +// +// Typeerated sequence command types. +//--------------------------------------------------------------------------- +enum sequenceCommandType_ +{ + SEQUENCE_TYPE_COMMAND, + SEQUENCE_TYPE_MODIFIER, +}; +typedef enum sequenceCommandType_ sequenceCommandType_e; + + +//--------------------------------------------------------------------------- +// sequenceCommandMapping_s +// +// A mapping of a command enumerated-value to its name. +//--------------------------------------------------------------------------- +typedef struct sequenceCommandMapping_ sequenceCommandMapping_s; +struct sequenceCommandMapping_ +{ + sequenceCommandEnum_e commandEnum; + const char* commandName; + sequenceCommandType_e commandType; +}; + + +//--------------------------------------------------------------------------- +// sequenceCommandLine_s +// +// Structure representing a single command (usually 1 line) from a +// .SEQ file entry. +//--------------------------------------------------------------------------- +typedef struct sequenceCommandLine_ sequenceCommandLine_s; +struct sequenceCommandLine_ +{ + int commandType; // Specifies the type of command + client_textmessage_t clientMessage; // Text HUD message struct + char* speakerName; // Targetname of speaking entity + char* listenerName; // Targetname of entity being spoken to + char* soundFileName; // Name of sound file to play + char* sentenceName; // Name of sentences.txt to play + char* fireTargetNames; // List of targetnames to fire + char* killTargetNames; // List of targetnames to remove + float delay; // Seconds 'till next command + int repeatCount; // If nonzero, reset execution pointer to top of block (N times, -1 = infinite) + int textChannel; // Display channel on which text message is sent + int modifierBitField; // Bit field to specify what clientmessage fields are valid + sequenceCommandLine_s* nextCommandLine; // Next command (linked list) +}; + + +//--------------------------------------------------------------------------- +// sequenceEntry_s +// +// Structure representing a single command (usually 1 line) from a +// .SEQ file entry. +//--------------------------------------------------------------------------- +typedef struct sequenceEntry_ sequenceEntry_s; +struct sequenceEntry_ +{ + char* fileName; // Name of sequence file without .SEQ extension + char* entryName; // Name of entry label in file + sequenceCommandLine_s* firstCommand; // Linked list of commands in entry + sequenceEntry_s* nextEntry; // Next loaded entry + qboolean isGlobal; // Is entry retained over level transitions? +}; + + + +//--------------------------------------------------------------------------- +// sentenceEntry_s +// Structure representing a single sentence of a group from a .SEQ +// file entry. Sentences are identical to entries in sentences.txt, but +// can be unique per level and are loaded/unloaded with the level. +//--------------------------------------------------------------------------- +typedef struct sentenceEntry_ sentenceEntry_s; +struct sentenceEntry_ +{ + char* data; // sentence data (ie "We have hostiles" ) + sentenceEntry_s* nextEntry; // Next loaded entry + qboolean isGlobal; // Is entry retained over level transitions? + unsigned int index; // this entry's position in the file. +}; + +//-------------------------------------------------------------------------- +// sentenceGroupEntry_s +// Structure representing a group of sentences found in a .SEQ file. +// A sentence group is defined by all sentences with the same name, ignoring +// the number at the end of the sentence name. Groups enable a sentence +// to be picked at random across a group. +//-------------------------------------------------------------------------- +typedef struct sentenceGroupEntry_ sentenceGroupEntry_s; +struct sentenceGroupEntry_ +{ + char* groupName; // name of the group (ie CT_ALERT ) + unsigned int numSentences; // number of sentences in group + sentenceEntry_s* firstSentence; // head of linked list of sentences in group + sentenceGroupEntry_s* nextEntry; // next loaded group +}; + +//--------------------------------------------------------------------------- +// Function declarations +//--------------------------------------------------------------------------- +sequenceEntry_s* Sequence_Get( const char* fileName, const char* entryName ); +void Sequence_ParseFile( const char* fileName, qboolean isGlobal ); +void Sequence_OnLevelLoad( const char* mapName ); +sentenceEntry_s* Sequence_PickSentence( const char *groupName, int pickMethod, int *picked ); +void Sequence_Init( void ); +void Sequence_PurgeEntries( qboolean purgeGlobals ); +sentenceEntry_s *Sequence_GetSentenceByIndex( unsigned int index ); + +#endif // _INCLUDE_SEQUENCE_H_