From 83bee9ddb6760f9d6d6ba5c44984a2bcc991c46d Mon Sep 17 00:00:00 2001 From: g-cont Date: Tue, 6 Feb 2018 00:00:00 +0300 Subject: [PATCH] 06 Feb 2018 --- common/bspfile.h | 13 +- common/ivoicetweak.h | 2 - common/render_api.h | 2 + engine/cdll_exp.h | 4 +- engine/cdll_int.h | 49 - engine/client/cl_events.c | 2 +- engine/client/cl_frame.c | 28 +- engine/client/cl_game.c | 371 +--- engine/client/cl_parse.c | 51 +- engine/client/client.h | 9 +- engine/client/gl_local.h | 5 +- engine/client/gl_mirror.c | 2 +- engine/client/gl_rmain.c | 10 +- engine/client/gl_rsurf.c | 9 +- engine/client/gl_warp.c | 22 +- engine/common/cmd.c | 77 +- engine/common/common.c | 107 +- engine/common/common.h | 55 +- engine/common/con_utils.c | 98 +- engine/common/crclib.c | 15 +- engine/common/cvar.c | 10 - engine/common/cvar.h | 1 - engine/common/filesystem.c | 430 +--- engine/common/host.c | 2 +- engine/common/mod_bmodel.c | 2990 ++++++++++++++++++++++++++++ engine/common/mod_local.h | 76 +- engine/common/model.c | 2792 +------------------------- engine/common/net_chan.c | 2 +- engine/common/protocol.h | 10 +- engine/common/soundlib/snd_utils.c | 43 - engine/eiface.h | 17 - engine/engine.dsp | 4 + engine/server/server.h | 8 +- engine/server/sv_client.c | 43 - engine/server/sv_game.c | 195 +- engine/server/sv_world.c | 2 +- 36 files changed, 3323 insertions(+), 4233 deletions(-) create mode 100644 engine/common/mod_bmodel.c diff --git a/common/bspfile.h b/common/bspfile.h index b5a6393d..c4305d60 100644 --- a/common/bspfile.h +++ b/common/bspfile.h @@ -96,6 +96,7 @@ BRUSH MODELS #define MAX_MAP_MIPTEX 0x2000000 // 32 Mb internal textures data #define MAX_MAP_LIGHTING 0x2000000 // 32 Mb lightmap raw data (can contain deluxemaps) #define MAX_MAP_VISIBILITY 0x1000000 // 16 Mb visdata +#define MAX_MAP_FACEINFO 8192 // can be increased but not needs #define MAX_TOTAL_CLIPNODES 524288 // quake lump ordering @@ -217,7 +218,7 @@ typedef struct float maxs[3]; int firstface; int numfaces; // counting both sides -} dnode2_t; +} dnode32_t; // leaf 0 is the generic CONTENTS_SOLID leaf, used for all solid areas // all other leafs need visibility info @@ -247,7 +248,7 @@ typedef struct int nummarksurfaces; byte ambient_level[NUM_AMBIENTS]; -} dleaf2_t; +} dleaf32_t; typedef struct { @@ -259,7 +260,7 @@ typedef struct { int planenum; int children[2]; // negative numbers are contents -} dclipnode2_t; +} dclipnode32_t; typedef struct { @@ -278,7 +279,7 @@ typedef struct } dfaceinfo_t; typedef word dmarkface_t; // leaf marksurfaces indexes -typedef int dmarkface2_t; // leaf marksurfaces indexes +typedef int dmarkface32_t; // leaf marksurfaces indexes typedef int dsurfedge_t; // map surfedges @@ -292,7 +293,7 @@ typedef struct typedef struct { int v[2]; // vertex numbers -} dedge2_t; +} dedge32_t; typedef struct { @@ -320,6 +321,6 @@ typedef struct // lighting info byte styles[LM_STYLES]; int lightofs; // start of [numstyles*surfsize] samples -} dface2_t; +} dface32_t; #endif//BSPFILE_H \ No newline at end of file diff --git a/common/ivoicetweak.h b/common/ivoicetweak.h index 9c7b6963..e4a65668 100644 --- a/common/ivoicetweak.h +++ b/common/ivoicetweak.h @@ -33,8 +33,6 @@ typedef struct IVoiceTweak_s // Get/set control values. void (*SetControlFloat)( VoiceTweakControl iControl, float value ); float (*GetControlFloat)( VoiceTweakControl iControl ); - - int (*GetSpeakingVolume)( void ); } IVoiceTweak; #endif//IVOICETWEAK_H \ No newline at end of file diff --git a/common/render_api.h b/common/render_api.h index 890d6cf4..97005b16 100644 --- a/common/render_api.h +++ b/common/render_api.h @@ -226,6 +226,8 @@ typedef struct render_api_s // find in files char **(*pfnGetFilesList)( const char *pattern, int *numFiles, int gamedironly ); unsigned long (*pfnFileBufferCRC32)( const void *buffer, const int length ); + void* ( *pfnGetModel )( int modelindex ); + float (*pfnTime)( void ); // ONLY ADD NEW FUNCTIONS TO THE END OF THIS STRUCT. INTERFACE VERSION IS FROZEN AT 35 } render_api_t; diff --git a/engine/cdll_exp.h b/engine/cdll_exp.h index 07227057..e4622cb3 100644 --- a/engine/cdll_exp.h +++ b/engine/cdll_exp.h @@ -34,7 +34,7 @@ typedef struct cldll_func_s void (*IN_Accumulate)( void ); void (*CL_CreateMove)( float frametime, struct usercmd_s *cmd, int active ); int (*CL_IsThirdPerson)( void ); - void (*CL_CameraOffset)( float *ofs ); + void (*CL_CameraOffset)( float *ofs ); // unused void *(*KB_Find)( const char *name ); void (*CAM_Think)( void ); // camera stuff void (*pfnCalcRefdef)( ref_params_t *pparams ); @@ -59,8 +59,6 @@ typedef struct cldll_func_s void (*pfnDirectorMessage)( int iSize, void *pbuf ); int (*pfnGetStudioModelInterface)( int version, struct r_studio_interface_s **ppinterface, struct engine_studio_api_s *pstudio ); void (*pfnChatInputPosition)( int *x, int *y ); - int (*pfnGetPlayerTeam)( int playerIndex ); - void *(*pfnGetClientFactory)( void ); // Xash3D extension int (*pfnGetRenderInterface)( int version, render_api_t *renderfuncs, render_interface_t *callback ); void (*pfnClipMoveToEntity)( struct physent_s *pe, const vec3_t start, vec3_t mins, vec3_t maxs, const vec3_t end, struct pmtrace_s *tr ); diff --git a/engine/cdll_int.h b/engine/cdll_int.h index 904d7fff..69776599 100644 --- a/engine/cdll_int.h +++ b/engine/cdll_int.h @@ -28,15 +28,6 @@ extern "C" { #include "const.h" -#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 @@ -105,8 +96,6 @@ typedef struct hud_player_info_s char *model; short topcolor; short bottomcolor; - - unsigned __int64 m_nSteamID; } hud_player_info_t; typedef struct cl_enginefuncs_s @@ -259,44 +248,6 @@ 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 (*pfnVGUI2_DrawString)( int x, int y, const char *str, int r, int g, int b ); - int (*pfnVGUI2_DrawStringReverse)( int x, int y, const char *str, int r, int g, int b ); - const char *(*LocalPlayerInfo_ValueForKey)( const char* key ); - int (*pfnVGUI2_DrawCharacter)( int x, int y, int ch, unsigned int font ); - int (*pfnVGUI2_DrawCharacterAdditive)( int x, int y, int ch, int r, int g, int b, unsigned int font ); - unsigned int (*pfnGetApproxWavePlayLen)( char *filename ); - void* (*pfnVGUI2_GetCareerGameUI)( void ); // g-cont. !!!! potential crash-point! - void (*Cvar_Set)( char *name, char *value ); - int (*pfnVGUI2_IsPlayingCareerMatch)( 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 (*pfnVGUI2_GetMouseDelta)( int *x, int *y ); } cl_enginefunc_t; #define CLDLL_INTERFACE_VERSION 7 diff --git a/engine/client/cl_events.c b/engine/client/cl_events.c index ef87580b..2de11480 100644 --- a/engine/client/cl_events.c +++ b/engine/client/cl_events.c @@ -184,7 +184,7 @@ void CL_RegisterEvent( int lastnum, const char *szEvName, pfnEventHook func ) ev = clgame.events[lastnum]; // NOTE: ev->index will be set later - Q_strncpy( ev->name, szEvName, CS_SIZE ); + Q_strncpy( ev->name, szEvName, MAX_QPATH ); ev->func = func; } diff --git a/engine/client/cl_frame.c b/engine/client/cl_frame.c index 49379cb5..c4748f46 100644 --- a/engine/client/cl_frame.c +++ b/engine/client/cl_frame.c @@ -288,17 +288,14 @@ void CL_ProcessEntityUpdate( cl_entity_t *ent ) CL_UpdatePositions( ent ); } - if( !FBitSet( host.features, ENGINE_COMPUTE_STUDIO_LERP )) + if( ent->player && !FBitSet( host.features, ENGINE_COMPUTE_STUDIO_LERP )) { // g-cont. it should be done for all the players? // FIXME: probably this cause problems with flahslight and mirror reflection // but it's used to reduce player body pitch... - if( ent->player ) - { - if( world.has_mirrors && gl_allow_mirrors->value && RP_LOCALCLIENT( ent ) && !cl.local.thirdperson ) - ent->curstate.angles[PITCH] /= 3.0f; - else ent->curstate.angles[PITCH] /= -3.0f; - } + if( FBitSet( world.flags, FWORLD_HAS_MIRRORS ) && gl_allow_mirrors->value && RP_LOCALCLIENT( ent ) && !cl.local.thirdperson ) + ent->curstate.angles[PITCH] /= 3.0f; + else ent->curstate.angles[PITCH] /= -3.0f; } VectorCopy( ent->curstate.origin, ent->origin ); @@ -911,7 +908,16 @@ qboolean CL_AddVisibleEntity( cl_entity_t *ent, int entityType ) // check for adding this entity if( !clgame.dllFuncs.pfnAddEntity( entityType, ent, ent->model->name )) - return false; + return true; + + if( entityType == ET_PLAYER && RP_LOCALCLIENT( ent )) + { + if( !CL_IsThirdPerson( )) + { + if( !gl_allow_mirrors->value || !FBitSet( world.flags, FWORLD_HAS_MIRRORS )) + return false; + } + } if( entityType == ET_BEAM ) { @@ -986,12 +992,6 @@ void CL_LinkPlayers( frame_t *frame ) if( state->messagenum != cl.parsecount ) continue; // not present this frame - if( !CL_IsThirdPerson() && ( i == cl.viewentity - 1 )) - { - if( !gl_allow_mirrors->value || !world.has_mirrors ) - continue; - } - if( !state->modelindex || FBitSet( state->effects, EF_NODRAW )) continue; diff --git a/engine/client/cl_game.c b/engine/client/cl_game.c index 2c120de0..62f89c27 100644 --- a/engine/client/cl_game.c +++ b/engine/client/cl_game.c @@ -87,7 +87,6 @@ static dllfunc_t cdll_new_exports[] = // allowed only in SDK 2.3 and higher { "HUD_VoiceStatus", (void **)&clgame.dllFuncs.pfnVoiceStatus }, { "HUD_ChatInputPosition", (void **)&clgame.dllFuncs.pfnChatInputPosition }, { "HUD_GetRenderInterface", (void **)&clgame.dllFuncs.pfnGetRenderInterface }, // Xash3D ext -{ "HUD_GetPlayerTeam", (void **)&clgame.dllFuncs.pfnGetPlayerTeam }, { "HUD_ClipMoveToEntity", (void **)&clgame.dllFuncs.pfnClipMoveToEntity }, // Xash3D ext { NULL, NULL } }; @@ -115,18 +114,6 @@ cl_entity_t *CL_GetEntityByIndex( int index ) return CL_EDICT_NUM( index ); } -/* -==================== -CL_GetServerTime - -don't clamped time that come from server -==================== -*/ -float CL_GetServerTime( void ) -{ - return cl.mtime[0]; -} - /* ==================== CL_IsThirdPerson @@ -260,18 +247,6 @@ int CL_PointContents( const vec3_t p ) return cont; } -/* -==================== -StudioEvent - -Event callback for studio models -==================== -*/ -void CL_StudioEvent( struct mstudioevent_s *event, cl_entity_t *pEdict ) -{ - clgame.dllFuncs.pfnStudioEvent( event, pEdict ); -} - /* ============= CL_AdjustXPos @@ -2675,11 +2650,7 @@ pfnGetMousePos */ void pfnGetMousePos( struct tagPOINT *ppt ) { - ASSERT( ppt != NULL ); - - // find mouse movement GetCursorPos( ppt ); - ScreenToClient( host.hWnd, ppt ); } /* @@ -2690,79 +2661,17 @@ pfnSetMousePos */ void pfnSetMousePos( int mx, int my ) { - POINT pt; - - pt.x = mx; - pt.y = my; - - ClientToScreen( host.hWnd, &pt ); - SetCursorPos( pt.x, pt.y ); + SetCursorPos( mx, my ); } /* ============= pfnSetMouseEnable +legacy of dinput code ============= */ void pfnSetMouseEnable( qboolean fEnable ) -{ - if( fEnable ) IN_ActivateMouse( false ); - else IN_DeactivateMouse(); -} - -/* -============= -pfnGetServerTime - -============= -*/ -float pfnGetClientOldTime( void ) -{ - return cl.oldtime; -} - -/* -============= -pfnGetGravity - -============= -*/ -float pfnGetGravity( void ) -{ - return clgame.movevars.gravity; -} - -/* -============= -pfnEnableTexSort - -TODO: implement -============= -*/ -void pfnEnableTexSort( int enable ) -{ -} - -/* -============= -pfnSetLightmapColor - -TODO: implement -============= -*/ -void pfnSetLightmapColor( float red, float green, float blue ) -{ -} - -/* -============= -pfnSetLightmapScale - -TODO: implement -============= -*/ -void pfnSetLightmapScale( float scale ) { } @@ -2784,201 +2693,6 @@ char *pfnParseFile( char *data, char *token ) return out; } -/* -============= -pfnSPR_DrawGeneric - -============= -*/ -void 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 ); -} - -/* -============= -pfnDrawString - -============= -*/ -int pfnDrawString( int x, int y, const char *str, int r, int g, int b ) -{ - // draw the string until we hit the null character or a newline character - for( ; *str != 0 && *str != '\n'; str++ ) - x += pfnDrawCharacter( x, y, (byte)*str, r, g, b ); - - return x; -} - -/* -============= -pfnDrawStringReverse - -============= -*/ -int pfnDrawStringReverse( int x, int y, const char *str, int r, int g, int b ) -{ - char *szIt; - - // find the end of the string - for( szIt = (char *)str; *szIt != 0; szIt++ ) - x -= clgame.scrInfo.charWidths[(byte)*szIt]; - pfnDrawString( x, y, str, r, g, b ); - - return x; -} - -/* -============= -LocalPlayerInfo_ValueForKey - -============= -*/ -const char *LocalPlayerInfo_ValueForKey( const char* key ) -{ - return Info_ValueForKey( cls.userinfo, key ); -} - -/* -============= -pfnVGUI2DrawCharacter - -============= -*/ -int pfnVGUI2DrawCharacter( int x, int y, int ch, unsigned int font ) -{ - return 0; -} - -/* -============= -pfnVGUI2DrawCharacterAdditive - -============= -*/ -int pfnVGUI2DrawCharacterAdditive( int x, int y, int ch, int r, int g, int b, unsigned int font ) -{ - return 0; -} - -/* -============= -GetCareerGameInterface - -============= -*/ -void *GetCareerGameInterface( void ) -{ - Msg( "^1Career GameInterface called!\n" ); - return NULL; -} - -/* -============= -pfnPlaySoundVoiceByName - -============= -*/ -void pfnPlaySoundVoiceByName( char *filename, float volume, int pitch ) -{ - int hSound = S_RegisterSound( filename ); - S_StartSound( NULL, cl.viewentity, CHAN_AUTO, hSound, volume, ATTN_NORM, pitch, SND_STOP_LOOPING ); -} - -/* -============= -pfnMP3_InitStream - -============= -*/ -void pfnMP3_InitStream( char *filename, int looping ) -{ - if( !filename ) - { - S_StopBackgroundTrack(); - return; - } - - if( looping ) - { - S_StartBackgroundTrack( filename, filename, 0 ); - } - else - { - S_StartBackgroundTrack( filename, NULL, 0 ); - } -} - -/* -============= -pfnPlaySoundByNameAtPitch - -============= -*/ -void pfnPlaySoundByNameAtPitch( char *filename, float volume, int pitch ) -{ - int hSound = S_RegisterSound( filename ); - S_StartSound( NULL, cl.viewentity, CHAN_STATIC, hSound, volume, ATTN_NORM, pitch, SND_STOP_LOOPING ); -} - -/* -============= -CL_FillRGBABlend - -============= -*/ -void 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 - -============= -*/ -int pfnGetAppID( void ) -{ - return 130; // borrowed from SDLash3D -} - -/* -============= -pfnVguiWrap2_GetMouseDelta - -============= -*/ -void pfnVguiWrap2_GetMouseDelta( int *x, int *y ) -{ - if( x ) *x = 0; - if( y ) *y = 0; -} - /* ================= TriApi implementation @@ -3176,6 +2890,7 @@ void TriCullFace( int mode ) clgame.ds.cullMode = GL_NONE; break; } + GL_Cull( clgame.ds.cullMode ); } @@ -3404,23 +3119,6 @@ void NetAPI_InitNetworking( void ) NET_Config( true ); // allow remote } -int Net_GetPacketLoss( void ) -{ - int packet_loss = 0; - - if( cls.state == ca_active ) - { - packet_loss = bound( 0, (int)cls.packet_loss, 100 ); - - if ( packet_loss < 0 ) - packet_loss = 0; - if ( packet_loss > 100 ) - packet_loss = 100; - } - - return packet_loss; -} - /* ================= NetAPI_InitNetworking @@ -3660,6 +3358,7 @@ void NetAPI_SetValueForKey( char *s, const char *key, const char *value, int max ================= IVoiceTweak implementation +TODO: implement ================= */ /* @@ -3670,7 +3369,6 @@ Voice_StartVoiceTweakMode */ int Voice_StartVoiceTweakMode( void ) { - // TODO: implement return 0; } @@ -3682,7 +3380,6 @@ Voice_EndVoiceTweakMode */ void Voice_EndVoiceTweakMode( void ) { - // TODO: implement } /* @@ -3693,7 +3390,6 @@ Voice_SetControlFloat */ void Voice_SetControlFloat( VoiceTweakControl iControl, float value ) { - // TODO: implement } /* @@ -3704,22 +3400,9 @@ Voice_GetControlFloat */ float Voice_GetControlFloat( VoiceTweakControl iControl ) { - // TODO: implement return 1.0f; } -/* -================= -Voice_GetSpeakingVolume - -================= -*/ -int Voice_GetSpeakingVolume( void ) -{ - // TODO: implement - return 255; -} - // shared between client and server triangleapi_t gTriApi = { @@ -3743,7 +3426,7 @@ triangleapi_t gTriApi = TriLightAtPoint, TriColor4fRendermode, TriFogParams, - R_LightVec, + R_LightVec, // Xash3D added }; static efx_api_t gEfxApi = @@ -3848,7 +3531,7 @@ static event_api_t gEventApi = pfnTraceTexture, pfnStopAllSounds, pfnKillEvents, - CL_EventIndex, + CL_EventIndex, // Xash3D added CL_IndexEvent, CL_PlayerTraceExt, CL_SoundFromIndex, @@ -3887,7 +3570,6 @@ static IVoiceTweak gVoiceApi = Voice_EndVoiceTweakMode, Voice_SetControlFloat, Voice_GetControlFloat, - Voice_GetSpeakingVolume, }; // engine callbacks @@ -3994,39 +3676,6 @@ static cl_enginefunc_t gEngfuncs = pfnGetMousePos, pfnSetMousePos, pfnSetMouseEnable, - Cvar_GetListHead, - Cmd_GetFirstFunctionHandle, - Cmd_GetNextFunctionHandle, - Cmd_GetName, - pfnGetClientOldTime, - pfnGetGravity, - Mod_Handle, - pfnEnableTexSort, - pfnSetLightmapColor, - pfnSetLightmapScale, - pfnSequenceGet, - pfnSPR_DrawGeneric, - pfnSequencePickSentence, - pfnDrawString, - pfnDrawStringReverse, - LocalPlayerInfo_ValueForKey, - pfnVGUI2DrawCharacter, - pfnVGUI2DrawCharacterAdditive, - Sound_GetApproxWavePlayLen, - GetCareerGameInterface, - Cvar_Set, - pfnIsCareerMatch, - pfnPlaySoundVoiceByName, - pfnMP3_InitStream, - Sys_DoubleTime, - pfnProcessTutorMessageDecayBuffer, - pfnConstructTutorMessageDecayBuffer, - pfnResetTutorMessageDecayData, - pfnPlaySoundByNameAtPitch, - CL_FillRGBABlend, - pfnGetAppID, - Cmd_AliasGetList, - pfnVguiWrap2_GetMouseDelta, }; void CL_UnloadProgs( void ) @@ -4061,7 +3710,7 @@ qboolean CL_LoadProgs( const char *name ) { static playermove_t gpMove; const dllfunc_t *func; - CL_EXPORT_FUNCS F; // export 'F' + CL_EXPORT_FUNCS GetClientAPI; // single export qboolean critical_exports = true; if( clgame.hInstance ) CL_UnloadProgs(); @@ -4085,12 +3734,12 @@ qboolean CL_LoadProgs( const char *name ) *func->func = NULL; // trying to get single export named 'F' - if(( F = (void *)Com_GetProcAddress( clgame.hInstance, "F" )) != NULL ) + if(( GetClientAPI = (void *)Com_GetProcAddress( clgame.hInstance, "GetClientAPI" )) != NULL ) { MsgDev( D_NOTE, "CL_LoadProgs: found single callback export\n" ); // trying to fill interface now - F( &clgame.dllFuncs ); + GetClientAPI( &clgame.dllFuncs ); // check critical functions again for( func = cdll_exports; func && func->name; func++ ) @@ -4123,7 +3772,7 @@ qboolean CL_LoadProgs( const char *name ) } } - // it may be loaded through 'F' so we don't need to clear them + // it may be loaded through 'GetClientAPI' so we don't need to clear them if( critical_exports ) { // clear new exports diff --git a/engine/client/cl_parse.c b/engine/client/cl_parse.c index 81be6fba..3a2b0fb4 100644 --- a/engine/client/cl_parse.c +++ b/engine/client/cl_parse.c @@ -443,6 +443,10 @@ void CL_ParseMovevars( sizebuf_t *msg ) MSG_ReadDeltaMovevars( msg, &clgame.oldmovevars, &clgame.movevars ); + // water alpha is not allowed + if( !FBitSet( world.flags, FWORLD_WATERALPHA )) + clgame.movevars.wateralpha = 1.0f; + // update sky if changed if( Q_strcmp( clgame.oldmovevars.skyName, clgame.movevars.skyName ) && cl.video_prepped ) R_SetupSky( clgame.movevars.skyName ); @@ -1486,45 +1490,6 @@ void CL_ParseScreenFade( sizebuf_t *msg ) } } -/* -============== -CL_ParseCvarValue - -Find the client cvar value -and sent it back to the server -============== -*/ -void CL_ParseCvarValue( sizebuf_t *msg ) -{ - const char *cvarName = MSG_ReadString( msg ); - convar_t *cvar = Cvar_FindVar( cvarName ); - - // build the answer - MSG_BeginClientCmd( &cls.netchan.message, clc_requestcvarvalue ); - MSG_WriteString( &cls.netchan.message, cvar ? cvar->string : "Not Found" ); -} - -/* -============== -CL_ParseCvarValue2 - -Find the client cvar value -and sent it back to the server -============== -*/ -void CL_ParseCvarValue2( sizebuf_t *msg ) -{ - int requestID = MSG_ReadLong( msg ); - const char *cvarName = MSG_ReadString( msg ); - convar_t *cvar = Cvar_FindVar( cvarName ); - - // build the answer - MSG_BeginClientCmd( &cls.netchan.message, clc_requestcvarvalue2 ); - MSG_WriteLong( &cls.netchan.message, requestID ); - MSG_WriteString( &cls.netchan.message, cvarName ); - MSG_WriteString( &cls.netchan.message, cvar ? cvar->string : "Not Found" ); -} - /* ============== CL_DispatchUserMessage @@ -1702,8 +1667,6 @@ void CL_ParseServerMessage( sizebuf_t *msg, qboolean normal_message ) cmd = MSG_ReadServerCmd( msg ); - if( cmd == svc_querycvarvalue2 ) Msg( "buffer left: %d bytes\n", MSG_GetNumBytesLeft( msg )); - // record command for debugging spew on parse problem CL_Parse_RecordCommand( cmd, bufStart ); @@ -1903,12 +1866,6 @@ void CL_ParseServerMessage( sizebuf_t *msg, qboolean normal_message ) case svc_studiodecal: CL_ParseStudioDecal( msg ); break; - case svc_querycvarvalue: - CL_ParseCvarValue( msg ); - break; - case svc_querycvarvalue2: - CL_ParseCvarValue2( msg ); - break; default: CL_ParseUserMessage( msg, cmd ); cl.frames[cl.parsecountmod].graphdata.usr += MSG_GetNumBytesRead( msg ) - bufStart; diff --git a/engine/client/client.h b/engine/client/client.h index b2858bed..3e516ba8 100644 --- a/engine/client/client.h +++ b/engine/client/client.h @@ -238,9 +238,9 @@ typedef struct int maxclients; int num_custombeams; // server beams count - char model_precache[MAX_MODELS][CS_SIZE]; - char sound_precache[MAX_SOUNDS][CS_SIZE]; - char event_precache[MAX_EVENTS][CS_SIZE]; + char model_precache[MAX_MODELS][MAX_QPATH]; + char sound_precache[MAX_SOUNDS][MAX_QPATH]; + char event_precache[MAX_EVENTS][MAX_QPATH]; lightstyle_t lightstyles[MAX_LIGHTSTYLES]; short sound_index[MAX_SOUNDS]; @@ -302,7 +302,7 @@ typedef void (*pfnEventHook)( event_args_t *args ); typedef struct { - char name[CS_SIZE]; + char name[MAX_QPATH]; word index; // event index pfnEventHook func; // user-defined function } cl_user_event_t; @@ -757,7 +757,6 @@ HSPRITE pfnSPR_Load( const char *szPicName ); HSPRITE pfnSPR_LoadExt( const char *szPicName, uint texFlags ); void PicAdjustSize( float *x, float *y, float *w, float *h ); void CL_FillRGBA( int x, int y, int width, int height, int r, int g, int b, int a ); -void CL_FillRGBABlend( 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 ); diff --git a/engine/client/gl_local.h b/engine/client/gl_local.h index a4fb5769..5f52118e 100644 --- a/engine/client/gl_local.h +++ b/engine/client/gl_local.h @@ -26,7 +26,7 @@ GNU General Public License for more details. extern byte *r_temppool; -#define BLOCK_SIZE world.block_size // lightmap blocksize +#define BLOCK_SIZE tr.block_size // lightmap blocksize #define BLOCK_SIZE_DEFAULT 128 // for keep backward compatibility #define BLOCK_SIZE_MAX 1024 @@ -220,6 +220,7 @@ typedef struct byte visbytes[(MAX_MAP_LEAFS+7)/8]; // member custom PVS int lightstylevalue[MAX_LIGHTSTYLES]; // value 0 - 65536 + int block_size; // lightmap blocksize double frametime; // special frametime for multipass rendering (will set to 0 on a nextview) float blend; // global blend value @@ -450,7 +451,7 @@ void R_AliasInit( void ); // // gl_warp.c // -void R_InitSky( struct mip_s *mt, struct texture_s *tx, qboolean custom_palette ); +void R_InitSkyClouds( struct mip_s *mt, struct texture_s *tx, qboolean custom_palette ); void R_AddSkyBoxSurface( msurface_t *fa ); void R_ClearSkyBox( void ); void R_DrawSkyBox( void ); diff --git a/engine/client/gl_mirror.c b/engine/client/gl_mirror.c index c09c826c..6e5ae386 100644 --- a/engine/client/gl_mirror.c +++ b/engine/client/gl_mirror.c @@ -565,7 +565,7 @@ Build mirror chains for this frame */ void R_FindMirrors( void ) { - if( !world.has_mirrors || RI.drawOrtho || !RI.drawWorld || RI.onlyClientDraw || !cl.worldmodel ) + if( !FBitSet( world.flags, FWORLD_HAS_MIRRORS ) || RI.drawOrtho || !RI.drawWorld || RI.onlyClientDraw || !cl.worldmodel ) return; // NOTE: we already has initial params at this point like vieworg, viewangles diff --git a/engine/client/gl_rmain.c b/engine/client/gl_rmain.c index 1ccfc906..39909062 100644 --- a/engine/client/gl_rmain.c +++ b/engine/client/gl_rmain.c @@ -1170,7 +1170,7 @@ static int GL_RenderGetParm( int parm, int arg ) arg = bound( 0, arg, MAX_LIGHTMAPS - 1 ); return tr.lightmapTextures[arg]; case PARM_SKY_SPHERE: - return world.sky_sphere && !world.custom_skybox; + return FBitSet( world.flags, FWORLD_SKYSPHERE ) && !FBitSet( world.flags, FWORLD_CUSTOM_SKYBOX ); case PARM_WIDESCREEN: return glState.wideScreen; case PARM_FULLSCREEN: @@ -1180,7 +1180,7 @@ static int GL_RenderGetParm( int parm, int arg ) case PARM_SCREEN_HEIGHT: return glState.height; case PARM_MAP_HAS_MIRRORS: - return world.has_mirrors; + return FBitSet( world.flags, FWORLD_HAS_MIRRORS ); case PARM_CLIENT_INGAME: return CL_IsInGame(); case PARM_MAX_ENTITIES: @@ -1202,7 +1202,7 @@ static int GL_RenderGetParm( int parm, int arg ) arg = bound( 0, arg, MAX_LIGHTSTYLES - 1 ); return tr.lightstylevalue[arg]; case PARM_MAP_HAS_DELUXE: - return (world.deluxedata != NULL); + return FBitSet( world.flags, FWORLD_HAS_DELUXEMAP ); case PARM_MAX_IMAGE_UNITS: return GL_MaxTextureUnits(); case PARM_CLIENT_ACTIVE: @@ -1222,7 +1222,7 @@ static int GL_RenderGetParm( int parm, int arg ) case PARM_STENCIL_ACTIVE: return glState.stencilEnabled; case PARM_WATER_ALPHA: - return world.water_alpha; + return FBitSet( world.flags, FWORLD_WATERALPHA ); } return 0; } @@ -1466,6 +1466,8 @@ static render_api_t gRenderAPI = R_Mem_Free, pfnGetFilesList, pfnFileBufferCRC32, + Mod_Handle, + pfnTime, }; /* diff --git a/engine/client/gl_rsurf.c b/engine/client/gl_rsurf.c index 5ba572d3..ec5693a3 100644 --- a/engine/client/gl_rsurf.c +++ b/engine/client/gl_rsurf.c @@ -1265,7 +1265,7 @@ void R_DrawTextureChains( void ) RI.currententity = clgame.entities; RI.currentmodel = RI.currententity->model; - if( world.sky_sphere && !world.custom_skybox ) + if( FBitSet( world.flags, FWORLD_SKYSPHERE ) && !FBitSet( world.flags, FWORLD_CUSTOM_SKYBOX )) { pglDisable( GL_TEXTURE_2D ); pglColor3f( 1.0f, 1.0f, 1.0f ); @@ -1275,7 +1275,7 @@ void R_DrawTextureChains( void ) for( s = skychain; s != NULL; s = s->texturechain ) R_AddSkyBoxSurface( s ); - if( world.sky_sphere && !world.custom_skybox ) + if( FBitSet( world.flags, FWORLD_SKYSPHERE ) && !FBitSet( world.flags, FWORLD_CUSTOM_SKYBOX )) { pglEnable( GL_TEXTURE_2D ); if( skychain ) @@ -2147,6 +2147,11 @@ void GL_BuildLightmaps( void ) memset( tr.mirror_entities, 0, sizeof( tr.mirror_entities )); memset( tr.mirrorTextures, 0, sizeof( tr.mirrorTextures )); memset( &RI, 0, sizeof( RI )); + + // update the lightmap blocksize + if( FBitSet( host.features, ENGINE_LARGE_LIGHTMAPS )) + tr.block_size = BLOCK_SIZE_MAX; + else tr.block_size = BLOCK_SIZE_DEFAULT; skychain = NULL; diff --git a/engine/client/gl_warp.c b/engine/client/gl_warp.c index 1842c9ba..f106a6d2 100644 --- a/engine/client/gl_warp.c +++ b/engine/client/gl_warp.c @@ -322,7 +322,7 @@ void R_AddSkyBoxSurface( msurface_t *fa ) } } - if( world.sky_sphere && fa->polys && !world.custom_skybox ) + if( FBitSet( world.flags, FWORLD_SKYSPHERE ) && fa->polys && !FBitSet( world.flags, FWORLD_CUSTOM_SKYBOX )) { glpoly_t *p = fa->polys; @@ -366,7 +366,7 @@ void R_UnloadSkybox( void ) tr.skyboxbasenum = 5800; // set skybox base (to let some mods load hi-res skyboxes) memset( tr.skyboxTextures, 0, sizeof( tr.skyboxTextures )); - world.custom_skybox = false; + ClearBits( world.flags, FWORLD_CUSTOM_SKYBOX ); } /* @@ -488,7 +488,7 @@ void R_SetupSky( const char *skyboxname ) if( i == 6 ) { - world.custom_skybox = true; + SetBits( world.flags, FWORLD_CUSTOM_SKYBOX ); return; // loaded } @@ -676,12 +676,12 @@ void R_DrawClouds( void ) /* ============= -R_InitSky +R_InitSkyClouds A sky texture is 256*128, with the right side being a masked overlay ============== */ -void R_InitSky( mip_t *mt, texture_t *tx, qboolean custom_palette ) +void R_InitSkyClouds( mip_t *mt, texture_t *tx, qboolean custom_palette ) { rgbdata_t r_temp, *r_sky; uint *trans, *rgba; @@ -690,6 +690,9 @@ void R_InitSky( mip_t *mt, texture_t *tx, qboolean custom_palette ) int i, j, p; char texname[32]; + if( !glw_state.initialized ) + return; + Q_snprintf( texname, sizeof( texname ), "%s%s.mip", ( mt->offsets[0] > 0 ) ? "#" : "", tx->name ); if( mt->offsets[0] > 0 ) @@ -709,15 +712,15 @@ void R_InitSky( mip_t *mt, texture_t *tx, qboolean custom_palette ) if( !r_sky || !r_sky->palette || r_sky->type != PF_INDEXED_32 || r_sky->height == 0 ) { MsgDev( D_ERROR, "R_InitSky: unable to load sky texture %s\n", tx->name ); - FS_FreeImage( r_sky ); + if( r_sky ) FS_FreeImage( r_sky ); return; } // make an average value for the back to avoid // a fringe on the top level trans = Mem_Alloc( r_temppool, r_sky->height * r_sky->height * sizeof( *trans )); - r = g = b = 0; + for( i = 0; i < r_sky->width >> 1; i++ ) { for( j = 0; j < r_sky->height; j++ ) @@ -742,9 +745,9 @@ void R_InitSky( mip_t *mt, texture_t *tx, qboolean custom_palette ) r_temp.height = r_sky->height; r_temp.type = PF_RGBA_32; r_temp.flags = IMAGE_HAS_COLOR; - r_temp.palette = NULL; - r_temp.buffer = (byte *)trans; r_temp.size = r_temp.width * r_temp.height * 4; + r_temp.buffer = (byte *)trans; + r_temp.palette = NULL; // load it in tr.solidskyTexture = GL_LoadTextureInternal( "solid_sky", &r_temp, TF_NOMIPMAP, false ); @@ -754,6 +757,7 @@ void R_InitSky( mip_t *mt, texture_t *tx, qboolean custom_palette ) for( j = 0; j < r_sky->height; j++ ) { p = r_sky->buffer[i * r_sky->width + j]; + if( p == 0 ) { trans[(i * r_sky->height) + j] = transpix; diff --git a/engine/common/cmd.c b/engine/common/cmd.c index 1f05d452..e226a2a0 100644 --- a/engine/common/cmd.c +++ b/engine/common/cmd.c @@ -19,20 +19,28 @@ GNU General Public License for more details. #define MAX_CMD_BUFFER 32768 #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; - int cursize; - int maxsize; + byte *data; + int cursize; + int maxsize; } cmdbuf_t; -qboolean cmd_wait; -cmdbuf_t cmd_text; -byte cmd_text_buf[MAX_CMD_BUFFER]; -cmdalias_t *cmd_alias; -uint cmd_condition; -int cmd_condlevel; +qboolean cmd_wait; +cmdbuf_t cmd_text; +byte cmd_text_buf[MAX_CMD_BUFFER]; +cmdalias_t *cmd_alias; +uint cmd_condition; +int cmd_condlevel; /* ============================================================================= @@ -73,7 +81,7 @@ void *Cbuf_GetSpace( cmdbuf_t *buf, int length ) { void *data; - if( buf->cursize + length > buf->maxsize ) + if(( buf->cursize + length ) > buf->maxsize ) { buf->cursize = 0; Host_Error( "Cbuf_GetSpace: overflow\n" ); @@ -96,7 +104,7 @@ void Cbuf_AddText( const char *text ) { int l = Q_strlen( text ); - if( cmd_text.cursize + l >= cmd_text.maxsize ) + if(( cmd_text.cursize + l ) >= cmd_text.maxsize ) { MsgDev( D_WARN, "Cbuf_AddText: overflow\n" ); } @@ -118,7 +126,7 @@ void Cbuf_InsertText( const char *text ) { int l = Q_strlen( text ); - if( cmd_text.cursize + l >= cmd_text.maxsize ) + if(( cmd_text.cursize + l ) >= cmd_text.maxsize ) { MsgDev( D_WARN, "Cbuf_InsertText: overflow\n" ); } @@ -285,8 +293,8 @@ Cmd_StuffCmds_f Adds command line parameters as script statements Commands lead with a +, and continue until a - or another + -xash -dev 3 +map c1a0d -xash -nosound -game bshift +hl.exe -dev 3 +map c1a0d +hl.exe -nosound -game bshift =============== */ void Cmd_StuffCmds_f( void ) @@ -490,47 +498,6 @@ char *Cmd_Args( void ) return cmd_args; } -/* -============ -Cmd_AliasGetList -============ -*/ -struct cmdalias_s *Cmd_AliasGetList( void ) -{ - return cmd_alias; -} - -/* -============ -Cmd_GetList -============ -*/ -struct cmd_s *Cmd_GetFirstFunctionHandle( void ) -{ - return cmd_functions; -} - -/* -============ -Cmd_GetNext -============ -*/ -struct cmd_s *Cmd_GetNextFunctionHandle( struct cmd_s *cmd ) -{ - return (cmd) ? cmd->next : NULL; -} - - -/* -============ -Cmd_GetName -============ -*/ -char *Cmd_GetName( struct cmd_s *cmd ) -{ - return cmd->name; -} - /* ============ Cmd_TokenizeString diff --git a/engine/common/common.c b/engine/common/common.c index a9a4a771..1cb4c25a 100644 --- a/engine/common/common.c +++ b/engine/common/common.c @@ -752,6 +752,17 @@ int COM_CompareFileTime( const char *filename1, const char *filename2, int *iCom return bRet; } +/* +============= +pfnTime + +============= +*/ +float pfnTime( void ) +{ + return (float)Sys_DoubleTime(); +} + /* ============= pfnGetGameDir @@ -762,100 +773,4 @@ void pfnGetGameDir( char *szGetGameDir ) { if( !szGetGameDir ) return; Q_sprintf( szGetGameDir, "%s/%s", host.rootdir, GI->gamedir ); -} - -/* -============= -pfnSequenceGet - -used by CS:CZ -============= -*/ -void *pfnSequenceGet( const char *fileName, const char *entryName ) -{ - Msg( "Sequence_Get: file %s, entry %s\n", fileName, entryName ); - return NULL; -} - -/* -============= -pfnSequencePickSentence - -used by CS:CZ -============= -*/ -void *pfnSequencePickSentence( const char *groupName, int pickMethod, int *picked ) -{ - Msg( "Sequence_PickSentence: group %s, pickMethod %i\n", groupName, pickMethod ); - *picked = 0; - - return NULL; -} - -/* -============= -pfnIsCareerMatch - -used by CS:CZ (client stub) -============= -*/ -int pfnIsCareerMatch( void ) -{ - return 0; -} - -/* -============= -pfnRegisterTutorMessageShown - -only exists in PlayStation version -============= -*/ -void pfnRegisterTutorMessageShown( int mid ) -{ -} - -/* -============= -pfnGetTimesTutorMessageShown - -only exists in PlayStation version -============= -*/ -int pfnGetTimesTutorMessageShown( int mid ) -{ - return 0; -} - -/* -============= -pfnProcessTutorMessageDecayBuffer - -only exists in PlayStation version -============= -*/ -void pfnProcessTutorMessageDecayBuffer( int *buffer, int bufferLength ) -{ -} - -/* -============= -pfnConstructTutorMessageDecayBuffer - -only exists in PlayStation version -============= -*/ -void pfnConstructTutorMessageDecayBuffer( int *buffer, int bufferLength ) -{ -} - -/* -============= -pfnResetTutorMessageDecayData - -only exists in PlayStation version -============= -*/ -void pfnResetTutorMessageDecayData( void ) -{ } \ No newline at end of file diff --git a/engine/common/common.h b/engine/common/common.h index 48e0b512..abb124c1 100644 --- a/engine/common/common.h +++ b/engine/common/common.h @@ -39,6 +39,7 @@ extern "C" { #define MAX_LOCALINFO_STRING 32768 // localinfo used on server and not sended to the clients #define MAX_SYSPATH 1024 // system filepath #define MAX_PRINT_MSG 8192 // how many symbols can handle single call of Msg or MsgDev +#define MAX_TOKEN 2048 // parse token length #define MAX_MODS 512 // environment games that engine can keep visible #define EXPORT __declspec( dllexport ) #define BIT( n ) (1<<( n )) @@ -169,27 +170,27 @@ internal shared gameinfo structure (readonly for engine parts) typedef struct gameinfo_s { // filesystem info - char gamefolder[64]; // used for change game '-game x' - char basedir[64]; // base game directory (like 'id1' for Quake or 'valve' for Half-Life) - char gamedir[64]; // game directory (can be match with basedir, used as game dir and as write path) - char falldir[64]; // used as second basedir - char startmap[64]; // map to start singleplayer game - char trainmap[64]; // map to start hazard course (if specified) + char gamefolder[MAX_QPATH]; // used for change game '-game x' + char basedir[MAX_QPATH]; // base game directory (like 'id1' for Quake or 'valve' for Half-Life) + char gamedir[MAX_QPATH]; // game directory (can be match with basedir, used as game dir and as write path) + char falldir[MAX_QPATH]; // used as second basedir + char startmap[MAX_QPATH];// map to start singleplayer game + char trainmap[MAX_QPATH];// map to start hazard course (if specified) char title[64]; // Game Main Title float version; // game version (optional) // .dll pathes - char dll_path[64]; // e.g. "bin" or "cl_dlls" - char game_dll[64]; // custom path for game.dll + char dll_path[MAX_QPATH]; // e.g. "bin" or "cl_dlls" + char game_dll[MAX_QPATH]; // custom path for game.dll // .ico path - char iconpath[64]; // "game.ico" by default + char iconpath[MAX_QPATH]; // "game.ico" by default // about mod info string game_url; // link to a developer's site string update_url; // link to updates page - char type[64]; // single, toolkit, multiplayer etc - char date[64]; + char type[MAX_QPATH]; // single, toolkit, multiplayer etc + char date[MAX_QPATH]; size_t size; int gamemode; @@ -199,7 +200,7 @@ typedef struct gameinfo_s char sp_entity[32]; // e.g. info_player_start char mp_entity[32]; // e.g. info_player_deathmatch - char ambientsound[NUM_AMBIENTS][64]; // quake ambient sounds + char ambientsound[NUM_AMBIENTS][MAX_QPATH]; // quake ambient sounds int max_edicts; // min edicts is 600, max edicts is 4096 int max_tents; // min temp ents is 300, max is 2048 @@ -273,7 +274,7 @@ typedef struct host_redirect_s typedef struct { - char name[64]; + char name[MAX_QPATH]; short entnum; vec3_t origin; float volume; @@ -311,7 +312,7 @@ typedef struct host_parm_s uint framecount; // global framecount // list of unique decal indexes - char draw_decals[MAX_DECALS][CS_SIZE]; + char draw_decals[MAX_DECALS][MAX_QPATH]; vec3_t player_mins[MAX_MAP_HULLS]; // 4 hulls allowed vec3_t player_maxs[MAX_MAP_HULLS]; // 4 hulls allowed @@ -344,7 +345,7 @@ typedef struct host_parm_s qboolean renderinfo_changed; char rootdir[256]; // member root directory - char gamefolder[64]; // it's a default gamefolder + char gamefolder[MAX_QPATH]; // it's a default gamefolder byte *imagepool; // imagelib mempool byte *soundpool; // soundlib mempool @@ -379,10 +380,8 @@ void FS_DefaultExtension( char *path, const char *extension ); void FS_ExtractFilePath( const char *path, char *dest ); const char *FS_GetDiskPath( const char *name, qboolean gamedironly ); const char *FS_FileWithoutPath( const char *in ); -wfile_t *W_Open( const char *filename, const char *mode, int *errorcode ); byte *W_LoadLump( wfile_t *wad, const char *lumpname, size_t *lumpsizeptr, const char type ); void W_Close( wfile_t *wad ); -file_t *FS_OpenFile( const char *path, long *filesizeptr, qboolean gamedironly ); byte *FS_LoadFile( const char *path, long *filesizeptr, qboolean gamedironly ); qboolean FS_WriteFile( const char *filename, const void *data, long len ); qboolean COM_ParseVector( char **pfile, float *v, size_t size ); @@ -412,7 +411,6 @@ long FS_Tell( file_t *file ); qboolean FS_Eof( file_t *file ); int FS_Close( file_t *file ); int FS_Getc( file_t *file ); -qboolean FS_Eof( file_t *file ); long FS_FileLength( file_t *f ); /* @@ -625,7 +623,6 @@ 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 @@ -708,18 +705,7 @@ void Con_DPrintf( char *fmt, ... ); void Con_Printf( char *szFmt, ... ); int pfnNumberOfEntities( void ); int pfnIsInGame( 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 ); +float pfnTime( void ); /* ============================================================== @@ -803,14 +789,12 @@ qboolean CL_IsThirdPerson( void ); qboolean CL_IsIntermission( void ); qboolean CL_Initialized( void ); char *CL_Userinfo( void ); -float CL_GetServerTime( void ); float CL_GetLerpFrac( void ); void CL_CharEvent( int key ); qboolean CL_DisableVisibility( void ); int CL_PointContents( const vec3_t point ); char *COM_ParseFile( char *data, char *token ); byte *COM_LoadFile( const char *filename, int usehunk, int *pLength ); -void CL_StudioEvent( struct mstudioevent_s *event, struct cl_entity_s *ent ); qboolean CL_GetComment( const char *demoname, char *comment ); void COM_AddAppDirectoryToSearchPath( const char *pszBaseDir, const char *appName ); int COM_ExpandFilename( const char *fileName, char *nameOutBuffer, int nameOutBufferSize ); @@ -890,15 +874,10 @@ void Cmd_AutoComplete( char *complete_string ); void COM_SetRandomSeed( long lSeed ); long COM_RandomLong( long lMin, long lMax ); float COM_RandomFloat( float fMin, float fMax ); -void TrimSpace( const char *source, char *dest ); const byte *GL_TextureData( unsigned int texnum ); void GL_FreeImage( const char *name ); void VID_InitDefaultResolution( void ); void UI_SetActiveMenu( qboolean fActive ); -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 ); void Cmd_Null_f( void ); extern const char *svc_strings[256]; extern const char *clc_strings[11]; diff --git a/engine/common/con_utils.c b/engine/common/con_utils.c index dcc93836..29e56066 100644 --- a/engine/common/con_utils.c +++ b/engine/common/con_utils.c @@ -69,13 +69,13 @@ qboolean Cmd_GetMapList( const char *s, char *completedname, int length ) for( i = 0, nummaps = 0; i < t->numfilenames; i++ ) { - char entfilename[CS_SIZE]; - int ver = -1, mapver = -1, lumpofs = 0, lumplen = 0; + char entfilename[MAX_QPATH]; const char *ext = FS_FileExtension( t->filenames[i] ); + int ver = -1, lumpofs = 0, lumplen = 0; char *ents = NULL, *pfile; + qboolean validmap = false; int version = 0; - qboolean gearbox = false; - + if( Q_stricmp( ext, "bsp" )) continue; Q_strncpy( message, "^1error^7", sizeof( message )); f = FS_Open( t->filenames[i], "rb", con_gamemaps->value ); @@ -89,30 +89,17 @@ qboolean Cmd_GetMapList( const char *s, char *completedname, int length ) FS_Read( f, buf, sizeof( buf )); header = (dheader_t *)buf; ver = header->version; - - switch( ver ) + + // check all the lumps and some other errors + if( Mod_TestBmodelLumps( t->filenames[i], buf, true )) { - case Q1BSP_VERSION: - case HLBSP_VERSION: - case QBSP2_VERSION: - if( header->lumps[LUMP_ENTITIES].fileofs <= 1024 && !(header->lumps[LUMP_ENTITIES].filelen % sizeof(dplane_t))) - { - lumpofs = header->lumps[LUMP_PLANES].fileofs; - lumplen = header->lumps[LUMP_PLANES].filelen; - gearbox = true; - } - else - { - lumpofs = header->lumps[LUMP_ENTITIES].fileofs; - lumplen = header->lumps[LUMP_ENTITIES].filelen; - gearbox = false; - } - break; + lumpofs = header->lumps[LUMP_ENTITIES].fileofs; + lumplen = header->lumps[LUMP_ENTITIES].filelen; + ver = header->version; } hdrext = (dextrahdr_t *)((byte *)buf + sizeof( dheader_t )); - if( hdrext->id == IDEXTRAHEADER ) - version = hdrext->version; + if( hdrext->id == IDEXTRAHEADER ) version = hdrext->version; Q_strncpy( entfilename, t->filenames[i], sizeof( entfilename )); FS_StripExtension( entfilename ); @@ -132,24 +119,18 @@ qboolean Cmd_GetMapList( const char *s, char *completedname, int length ) // means there is no title, so clear the message string now char token[2048]; - message[0] = 0; + message[0] = 0; // remove 'error' pfile = ents; while(( pfile = COM_ParseFile( pfile, token )) != NULL ) { if( !Q_strcmp( token, "{" )) continue; - else if(!Q_strcmp( token, "}" )) break; - else if(!Q_strcmp( token, "message" )) + else if( !Q_strcmp( token, "}" )) break; + else if( !Q_strcmp( token, "message" )) { // get the message contents pfile = COM_ParseFile( pfile, message ); } - else if(!Q_strcmp( token, "mapversion" )) - { - // get the message contents - pfile = COM_ParseFile( pfile, token ); - mapver = Q_atoi( token ); - } } Mem_Free( ents ); } @@ -161,19 +142,19 @@ qboolean Cmd_GetMapList( const char *s, char *completedname, int length ) switch( ver ) { case Q1BSP_VERSION: - if( mapver == 220 ) Q_strncpy( buf, "Half-Life Alpha", sizeof( buf )); - else Q_strncpy( buf, "Quake", sizeof( buf )); + Q_strncpy( buf, "Quake", sizeof( buf )); break; case QBSP2_VERSION: Q_strncpy( buf, "Darkplaces BSP2", sizeof( buf )); break; case HLBSP_VERSION: - if( gearbox ) Q_strncpy( buf, "Blue-Shift", sizeof( buf )); - else if( version == 1 ) Q_strncpy( buf, "XashXT old format", sizeof( buf )); - else if( version == 2 ) Q_strncpy( buf, "Paranoia 2: Savior", sizeof( buf )); - else if( version == 3 ) Q_strncpy( buf, "not supported", sizeof( buf )); - else if( version == 4 ) Q_strncpy( buf, "Half-Life extended", sizeof( buf )); - else Q_strncpy( buf, "Half-Life", sizeof( buf )); + switch( version ) + { + case 1: Q_strncpy( buf, "XashXT old format", sizeof( buf )); break; + case 2: Q_strncpy( buf, "Paranoia 2: Savior", sizeof( buf )); break; + case 4: Q_strncpy( buf, "Half-Life extended", sizeof( buf )); break; + default: Q_strncpy( buf, "Half-Life", sizeof( buf )); break; + } break; default: Q_strncpy( buf, "??", sizeof( buf )); break; } @@ -706,7 +687,6 @@ qboolean Cmd_CheckMapsList_R( qboolean fRefresh, qboolean onlyingamedir ) // mod doesn't contain any maps (probably this is a bot) return Cmd_CheckMapsList_R( fRefresh, false ); } - return false; } @@ -715,7 +695,7 @@ qboolean Cmd_CheckMapsList_R( qboolean fRefresh, qboolean onlyingamedir ) for( i = 0; i < t->numfilenames; i++ ) { char *ents = NULL, *pfile; - int ver = -1, lumpofs = 0, lumplen = 0; + int lumpofs = 0, lumplen = 0; string mapname, message, entfilename; if( Q_stricmp( FS_FileExtension( t->filenames[i] ), "bsp" )) @@ -731,27 +711,19 @@ qboolean Cmd_CheckMapsList_R( qboolean fRefresh, qboolean onlyingamedir ) memset( buf, 0, MAX_SYSPATH ); FS_Read( f, buf, MAX_SYSPATH ); - ver = *(uint *)buf; - - switch( ver ) + header = (dheader_t *)buf; + + // check all the lumps and some other errors + if( !Mod_TestBmodelLumps( t->filenames[i], buf, true )) { - case Q1BSP_VERSION: - case HLBSP_VERSION: - case QBSP2_VERSION: - header = (dheader_t *)buf; - if( header->lumps[LUMP_ENTITIES].fileofs <= 1024 ) - { - lumpofs = header->lumps[LUMP_PLANES].fileofs; - lumplen = header->lumps[LUMP_PLANES].filelen; - } - else - { - lumpofs = header->lumps[LUMP_ENTITIES].fileofs; - lumplen = header->lumps[LUMP_ENTITIES].filelen; - } - break; + FS_Close( f ); + continue; } + // after call Mod_TestBmodelLumps we gurantee what map is valid + lumpofs = header->lumps[LUMP_ENTITIES].fileofs; + lumplen = header->lumps[LUMP_ENTITIES].filelen; + Q_strncpy( entfilename, t->filenames[i], sizeof( entfilename )); FS_StripExtension( entfilename ); FS_DefaultExtension( entfilename, ".ent" ); @@ -760,7 +732,7 @@ qboolean Cmd_CheckMapsList_R( qboolean fRefresh, qboolean onlyingamedir ) if( !ents && lumplen >= 10 ) { FS_Seek( f, lumpofs, SEEK_SET ); - ents = (char *)Mem_Alloc( host.mempool, lumplen + 1 ); + ents = Z_Malloc( lumplen + 1 ); FS_Read( f, ents, lumplen ); } @@ -768,7 +740,7 @@ qboolean Cmd_CheckMapsList_R( qboolean fRefresh, qboolean onlyingamedir ) { // if there are entities to parse, a missing message key just // means there is no title, so clear the message string now - char token[2048]; + char token[MAX_TOKEN]; qboolean worldspawn = true; Q_strncpy( message, "No Title", MAX_STRING ); diff --git a/engine/common/crclib.c b/engine/common/crclib.c index cb20237d..2778f22f 100644 --- a/engine/common/crclib.c +++ b/engine/common/crclib.c @@ -229,12 +229,11 @@ qboolean CRC32_File( dword *crcvalue, const char *filename ) qboolean CRC32_MapFile( dword *crcvalue, const char *filename, qboolean multiplayer ) { - file_t *f; - dheader_t *header; char headbuf[256], buffer[1024]; int i, num_bytes, lumplen; - qboolean blue_shift = false; int version, hdr_size; + dheader_t *header; + file_t *f; if( !crcvalue ) return false; @@ -276,18 +275,10 @@ qboolean CRC32_MapFile( dword *crcvalue, const char *filename, qboolean multipla return false; } - ASSERT( crcvalue != NULL ); CRC32_Init( crcvalue ); - // check for Blue-Shift maps - if( header->lumps[LUMP_ENTITIES].fileofs <= 1024 && (header->lumps[LUMP_ENTITIES].filelen % sizeof( dplane_t )) == 0 ) - blue_shift = true; - - for( i = 0; i < HEADER_LUMPS; i++ ) + for( i = LUMP_PLANES; i < HEADER_LUMPS; i++ ) { - if( blue_shift && i == LUMP_PLANES ) continue; - else if( i == LUMP_ENTITIES ) continue; - lumplen = header->lumps[i].filelen; FS_Seek( f, header->lumps[i].fileofs, SEEK_SET ); diff --git a/engine/common/cvar.c b/engine/common/cvar.c index 1f5f1ba0..cf0a7aaa 100644 --- a/engine/common/cvar.c +++ b/engine/common/cvar.c @@ -19,16 +19,6 @@ GNU General Public License for more details. convar_t *cvar_vars; // head of list convar_t *cmd_scripting; -/* -============ -Cvar_GetListHead -============ -*/ -cvar_t *Cvar_GetListHead( void ) -{ - return (cvar_t *)cvar_vars; -} - /* ============ Cvar_FindVar diff --git a/engine/common/cvar.h b/engine/common/cvar.h index 5859bd5d..49516a27 100644 --- a/engine/common/cvar.h +++ b/engine/common/cvar.h @@ -62,7 +62,6 @@ void Cvar_WriteVariables( file_t *f, int group ); void Cvar_Reset( const char *var_name ); void Cvar_SetCheatState( void ); qboolean Cvar_Command( void ); -cvar_t *Cvar_GetListHead( void ); void Cvar_Init( void ); void Cvar_Unlink( int group ); diff --git a/engine/common/filesystem.c b/engine/common/filesystem.c index 405efbfe..5c989d79 100644 --- a/engine/common/filesystem.c +++ b/engine/common/filesystem.c @@ -79,7 +79,6 @@ typedef struct wfile_s int infotableofs; byte *mempool; // W_ReadLump temp buffers int numlumps; - int mode; file_t *handle; dlumpinfo_t *lumps; time_t filetime; @@ -118,6 +117,7 @@ static searchpath_t *FS_FindFile( const char *name, int *index, qboolean gamedir static dlumpinfo_t *W_FindLump( wfile_t *wad, const char *name, const char matchtype ); static dpackfile_t *FS_AddFileToPack( const char* name, pack_t *pack, long offset, long size ); static byte *W_LoadFile( const char *path, long *filesizeptr, qboolean gamedironly ); +static wfile_t *W_Open( const char *filename, int *errorcode ); static qboolean FS_SysFileExists( const char *path ); static qboolean FS_SysFolderExists( const char *path ); static long FS_SysFileTime( const char *filename ); @@ -313,7 +313,7 @@ static dpackfile_t *FS_AddFileToPack( const char *name, pack_t *pack, long offse diff = Q_stricmp( pack->files[middle].name, name ); // If we found the file, there's a problem - if( !diff ) MsgDev( D_WARN, "Package %s contains the file %s several times\n", pack->filename, name ); + if( !diff ) MsgDev( D_WARN, "package %s contains the file %s several times\n", pack->filename, name ); // If we're too far in the list if( diff > 0 ) right = middle - 1; @@ -549,7 +549,7 @@ static qboolean FS_AddWad_Fullpath( const char *wadfile, qboolean *already_loade } if( already_loaded ) *already_loaded = false; - if( !Q_stricmp( ext, "wad" )) wad = W_Open( wadfile, "rb", &errorcode ); + if( !Q_stricmp( ext, "wad" )) wad = W_Open( wadfile, &errorcode ); else MsgDev( D_ERROR, "\"%s\" doesn't have a wad extension\n", wadfile ); if( wad ) @@ -1904,12 +1904,12 @@ Open a file. The syntax is the same as fopen */ file_t *FS_Open( const char *filepath, const char *mode, qboolean gamedironly ) { - if( host.type == HOST_NORMAL || host.type == HOST_DEDICATED ) - { - // some stupid mappers used leading '/' or '\' in path to models or sounds - if( filepath[0] == '/' || filepath[0] == '\\' ) filepath++; - if( filepath[0] == '/' || filepath[0] == '\\' ) filepath++; - } + // some stupid mappers used leading '/' or '\' in path to models or sounds + if( filepath[0] == '/' || filepath[0] == '\\' ) + filepath++; + + if( filepath[0] == '/' || filepath[0] == '\\' ) + filepath++; if( FS_CheckNastyPath( filepath, false )) return NULL; @@ -2314,25 +2314,6 @@ byte *FS_LoadFile( const char *path, long *filesizeptr, qboolean gamedironly ) return buf; } -/* -============ -FS_OpenFile - -Simply version of FS_Open -============ -*/ -file_t *FS_OpenFile( const char *path, long *filesizeptr, qboolean gamedironly ) -{ - file_t *file = FS_Open( path, "rb", gamedironly ); - - if( filesizeptr ) - { - if( file ) *filesizeptr = file->real_length; - else *filesizeptr = 0; - } - return file; -} - /* ============ FS_WriteFile @@ -2925,7 +2906,7 @@ void FS_InitMemory( void ) /* ============================================================================= -WADSYSTEM PRIVATE COMMON FUNCTIONS +WADSYSTEM PRIVATE ROUTINES ============================================================================= */ @@ -3204,36 +3185,6 @@ byte *W_ReadLump( wfile_t *wad, dlumpinfo_t *lump, long *lumpsizeptr ) return buf; } -/* -=========== -W_WriteLump - -compress and write lump -=========== -*/ -qboolean W_WriteLump( wfile_t *wad, dlumpinfo_t *lump, const void *data, size_t datasize ) -{ - if( !wad || !lump ) return false; - - if( !data || !datasize ) - { - MsgDev( D_WARN, "W_WriteLump: ignore blank lump %s - nothing to save\n", lump->name ); - return false; - } - - if( wad->mode == O_RDONLY ) - { - MsgDev( D_ERROR, "W_WriteLump: %s opened in readonly mode\n", wad->filename ); - return false; - } - - lump->size = lump->disksize = datasize; - - if( FS_Write( wad->handle, data, datasize ) == datasize ) - return true; - return false; -} - /* ============================================================================= @@ -3248,54 +3199,17 @@ W_Open open the wad for reading & writing =========== */ -wfile_t *W_Open( const char *filename, const char *mode, int *error ) +wfile_t *W_Open( const char *filename, int *error ) { - dwadinfo_t header; wfile_t *wad = (wfile_t *)Mem_Alloc( fs_mempool, sizeof( wfile_t )); - const char *comment = "Created by Xash3D Engine.\0"; - int i, ind, mod, opt, lumpcount; - size_t wadsize, lat_size; + int i, lumpcount; dlumpinfo_t *srclumps; - - // parse the mode string - switch( mode[0] ) - { - case 'r': - mod = O_RDONLY; - opt = 0; - break; - case 'w': - mod = O_WRONLY; - opt = O_CREAT|O_TRUNC; - break; - case 'a': - mod = O_WRONLY; - opt = O_CREAT; - break; - default: - MsgDev( D_ERROR, "W_Open(%s, %s): invalid mode\n", filename, mode ); - return NULL; - } - - for( ind = 1; mode[ind] != '\0'; ind++ ) - { - switch( mode[ind] ) - { - case '+': - mod = O_RDWR; - break; - case 'b': - opt |= O_BINARY; - break; - default: - MsgDev( D_ERROR, "W_Open: %s: unknown char in mode (%c)\n", filename, mode, mode[ind] ); - break; - } - } + size_t lat_size; + dwadinfo_t header; // NOTE: FS_Open is load wad file from the first pak in the list (while fs_ext_path is false) - if( fs_ext_path ) wad->handle = FS_Open( filename, mode, false ); - else wad->handle = FS_Open( FS_FileWithoutPath( filename ), mode, false ); + if( fs_ext_path ) wad->handle = FS_Open( filename, "rb", false ); + else wad->handle = FS_Open( FS_FileWithoutPath( filename ), "rb", false ); if( wad->handle == NULL ) { @@ -3310,125 +3224,93 @@ wfile_t *W_Open( const char *filename, const char *mode, int *error ) wad->filetime = FS_SysFileTime( filename ); wad->mempool = Mem_AllocPool( filename ); - wadsize = FS_FileLength( wad->handle ); - - // if the file is opened in "write", "append", or "read/write" mode - if( mod == O_WRONLY || !wadsize ) + if( FS_Read( wad->handle, &header, sizeof( dwadinfo_t )) != sizeof( dwadinfo_t )) { - dwadinfo_t hdr; - - wad->numlumps = 0; // blank wad - wad->lumps = NULL; // - wad->mode = O_WRONLY; - - // save space for header - hdr.ident = IDWAD3HEADER; - hdr.numlumps = wad->numlumps; - hdr.infotableofs = sizeof( dwadinfo_t ); - FS_Write( wad->handle, &hdr, sizeof( hdr )); - FS_Write( wad->handle, comment, Q_strlen( comment ) + 1 ); - wad->infotableofs = FS_Tell( wad->handle ); + MsgDev( D_ERROR, "W_Open: %s can't read header\n", filename ); + if( error ) *error = WAD_LOAD_BAD_HEADER; + W_Close( wad ); + return NULL; } - else if( mod == O_RDWR || mod == O_RDONLY ) + + if( header.ident != IDWAD2HEADER && header.ident != IDWAD3HEADER ) { - if( mod == O_RDWR ) - wad->mode = O_APPEND; - else wad->mode = O_RDONLY; + MsgDev( D_ERROR, "W_Open: %s is not a WAD2 or WAD3 file\n", filename ); + if( error ) *error = WAD_LOAD_BAD_HEADER; + W_Close( wad ); + return NULL; + } - if( FS_Read( wad->handle, &header, sizeof( dwadinfo_t )) != sizeof( dwadinfo_t )) - { - MsgDev( D_ERROR, "W_Open: %s can't read header\n", filename ); - if( error ) *error = WAD_LOAD_BAD_HEADER; - W_Close( wad ); - return NULL; - } + lumpcount = header.numlumps; - if( header.ident != IDWAD2HEADER && header.ident != IDWAD3HEADER ) - { - MsgDev( D_ERROR, "W_Open: %s is not a WAD2 or WAD3 file\n", filename ); - if( error ) *error = WAD_LOAD_BAD_HEADER; - W_Close( wad ); - return NULL; - } + if( lumpcount >= MAX_FILES_IN_WAD ) + { + MsgDev( D_WARN, "W_Open: %s is full (%i lumps)\n", filename, lumpcount ); + if( error ) *error = WAD_LOAD_TOO_MANY_FILES; + } + else if( lumpcount <= 0 ) + { + MsgDev( D_ERROR, "W_Open: %s has no lumps\n", filename ); + if( error ) *error = WAD_LOAD_NO_FILES; + W_Close( wad ); + return NULL; + } + else if( error ) *error = WAD_LOAD_OK; - lumpcount = header.numlumps; + wad->infotableofs = header.infotableofs; // save infotableofs position - if( lumpcount >= MAX_FILES_IN_WAD && wad->mode == O_APPEND ) - { - MsgDev( D_WARN, "W_Open: %s is full (%i lumps)\n", filename, lumpcount ); - if( error ) *error = WAD_LOAD_TOO_MANY_FILES; - wad->mode = O_RDONLY; // set read-only mode - } - else if( lumpcount <= 0 && wad->mode == O_RDONLY ) - { - MsgDev( D_ERROR, "W_Open: %s has no lumps\n", filename ); - if( error ) *error = WAD_LOAD_NO_FILES; - W_Close( wad ); - return NULL; - } - else if( error ) *error = WAD_LOAD_OK; + if( FS_Seek( wad->handle, wad->infotableofs, SEEK_SET ) == -1 ) + { + MsgDev( D_ERROR, "W_Open: %s can't find lump allocation table\n", filename ); + if( error ) *error = WAD_LOAD_BAD_FOLDERS; + W_Close( wad ); + return NULL; + } - wad->infotableofs = header.infotableofs; // save infotableofs position + lat_size = lumpcount * sizeof( dlumpinfo_t ); - if( FS_Seek( wad->handle, wad->infotableofs, SEEK_SET ) == -1 ) - { - MsgDev( D_ERROR, "W_Open: %s can't find lump allocation table\n", filename ); - if( error ) *error = WAD_LOAD_BAD_FOLDERS; - W_Close( wad ); - return NULL; - } + // NOTE: lumps table can be reallocated for O_APPEND mode + srclumps = (dlumpinfo_t *)Mem_Alloc( wad->mempool, lat_size ); - lat_size = lumpcount * sizeof( dlumpinfo_t ); - - // NOTE: lumps table can be reallocated for O_APPEND mode - srclumps = (dlumpinfo_t *)Mem_Alloc( wad->mempool, lat_size ); - - if( FS_Read( wad->handle, srclumps, lat_size ) != lat_size ) - { - MsgDev( D_ERROR, "W_ReadLumpTable: %s has corrupted lump allocation table\n", wad->filename ); - if( error ) *error = WAD_LOAD_CORRUPTED; - Mem_Free( srclumps ); - W_Close( wad ); - return NULL; - } - - // starting to add lumps - wad->lumps = (dlumpinfo_t *)Mem_Alloc( wad->mempool, lat_size ); - wad->numlumps = 0; - - // sort lumps for binary search - for( i = 0; i < lumpcount; i++ ) - { - char name[16]; - int k; - - // cleanup lumpname - Q_strnlwr( srclumps[i].name, name, sizeof( srclumps[i].name )); - - // check for '*' symbol issues (quake1) - k = Q_strlen( Q_strrchr( name, '*' )); - if( k ) name[Q_strlen( name ) - k] = '!'; - - // check for Quake 'conchars' issues (only lmp loader really allows to read this lame pic) - if( srclumps[i].type == 68 && !Q_stricmp( srclumps[i].name, "conchars" )) - srclumps[i].type = TYP_GFXPIC; - - // fixups bad image types (some quake wads) - if( srclumps[i].img_type < 0 || srclumps[i].img_type > IMG_DECAL_COLOR ) - srclumps[i].img_type = IMG_DIFFUSE; - - W_AddFileToWad( name, wad, &srclumps[i] ); - } - - // release source lumps + if( FS_Read( wad->handle, srclumps, lat_size ) != lat_size ) + { + MsgDev( D_ERROR, "W_ReadLumpTable: %s has corrupted lump allocation table\n", wad->filename ); + if( error ) *error = WAD_LOAD_CORRUPTED; Mem_Free( srclumps ); - - // if we are in append mode - we need started from infotableofs poisition - // overwrite lumptable as well, we have her copy in wad->lumps - if( wad->mode == O_APPEND ) - FS_Seek( wad->handle, wad->infotableofs, SEEK_SET ); + W_Close( wad ); + return NULL; } + // starting to add lumps + wad->lumps = (dlumpinfo_t *)Mem_Alloc( wad->mempool, lat_size ); + wad->numlumps = 0; + + // sort lumps for binary search + for( i = 0; i < lumpcount; i++ ) + { + char name[16]; + int k; + + // cleanup lumpname + Q_strnlwr( srclumps[i].name, name, sizeof( srclumps[i].name )); + + // check for '*' symbol issues (quake1) + k = Q_strlen( Q_strrchr( name, '*' )); + if( k ) name[Q_strlen( name ) - k] = '!'; + + // check for Quake 'conchars' issues (only lmp loader really allows to read this lame pic) + if( srclumps[i].type == 68 && !Q_stricmp( srclumps[i].name, "conchars" )) + srclumps[i].type = TYP_GFXPIC; + + // fixups bad image types (some quake wads) + if( srclumps[i].img_type < 0 || srclumps[i].img_type > IMG_DECAL_COLOR ) + srclumps[i].img_type = IMG_DIFFUSE; + + W_AddFileToWad( name, wad, &srclumps[i] ); + } + + // release source lumps + Mem_Free( srclumps ); + // and leave the file open return wad; } @@ -3444,28 +3326,9 @@ void W_Close( wfile_t *wad ) { if( !wad ) return; - if( wad->handle != NULL && ( wad->mode == O_APPEND || wad->mode == O_WRONLY )) - { - dwadinfo_t hdr; - long ofs; - - // write the lumpinfo - ofs = FS_Tell( wad->handle ); - FS_Write( wad->handle, wad->lumps, wad->numlumps * sizeof( dlumpinfo_t )); - - // write the header - hdr.ident = IDWAD3HEADER; - hdr.numlumps = wad->numlumps; - hdr.infotableofs = ofs; - - FS_Seek( wad->handle, 0, SEEK_SET ); - FS_Write( wad->handle, &hdr, sizeof( hdr )); - } - Mem_FreePool( &wad->mempool ); if( wad->handle != NULL ) FS_Close( wad->handle ); - Mem_Free( wad ); // free himself } @@ -3476,119 +3339,6 @@ FILESYSTEM IMPLEMENTATION ============================================================================= */ -/* -=========== -W_SaveLump - -write new or replace existed lump -=========== -*/ -size_t W_SaveFile( wfile_t *wad, const char *lump, const void *data, size_t datasize, char type, qboolean replace ) -{ - dlumpinfo_t *find, newlump; - size_t lat_size, oldpos; - char hint, lumpname[64]; - - if( !wad || !lump ) return -1; - - if( !data || !datasize ) - { - MsgDev( D_WARN, "W_SaveLump: ignore blank lump %s - nothing to save\n", lump ); - return -1; - } - - if( wad->mode == O_RDONLY ) - { - MsgDev( D_ERROR, "W_SaveLump: %s opened in readonly mode\n", wad->filename ); - return -1; - } - - if( wad->numlumps >= MAX_FILES_IN_WAD ) - { - MsgDev( D_ERROR, "W_SaveLump: %s is full\n", wad->filename ); - return -1; - } - - find = W_FindLump( wad, lump, type ); - - if( find != NULL && replace ) - { - if( FBitSet( find->attribs, ATTR_READONLY )) - { - // g-cont. i left this limitation as a protect of the replacement of compressed lumps - MsgDev( D_ERROR, "W_ReplaceLump: %s is read-only\n", find->name ); - return -1; - } - - if( datasize != find->disksize ) - { - MsgDev( D_ERROR, "W_ReplaceLump: %s [%s] should be [%s]\n", - lumpname, Q_memprint( datasize ), Q_memprint( find->disksize )); - return -1; - } - - oldpos = FS_Tell( wad->handle ); // don't forget restore original position - - if( FS_Seek( wad->handle, find->filepos, SEEK_SET ) == -1 ) - { - MsgDev( D_ERROR, "W_ReplaceLump: %s is corrupted\n", find->name ); - FS_Seek( wad->handle, oldpos, SEEK_SET ); - return -1; - } - - if( FS_Write( wad->handle, data, datasize ) != find->disksize ) - MsgDev( D_WARN, "W_ReplaceLump: %s probably replaced with errors\n", find->name ); - - // restore old position - FS_Seek( wad->handle, oldpos, SEEK_SET ); - - return wad->numlumps; - } - else - { - MsgDev( D_ERROR, "W_SaveLump: %s already exist\n", lump ); - return -1; - } - - // prepare lump name - Q_strncpy( lumpname, lump, sizeof( lumpname )); - - // extract image hint - hint = W_HintFromSuf( lumpname ); - - if( hint != IMG_DIFFUSE ) - lumpname[Q_strlen( lumpname ) - HINT_NAMELEN] = '\0'; // kill the suffix - - if( Q_strlen( lumpname ) >= WAD3_NAMELEN ) - { - // name is too long - MsgDev( D_ERROR, "W_SaveLump: %s more than %i symbols\n", lumpname, WAD3_NAMELEN ); - return -1; - } - - lat_size = sizeof( dlumpinfo_t ) * (wad->numlumps + 1); - - // reallocate lumptable - wad->lumps = (dlumpinfo_t *)Mem_Realloc( wad->mempool, wad->lumps, lat_size ); - - memset( &newlump, 0, sizeof( newlump )); - - // write header - Q_strnupr( lumpname, newlump.name, WAD3_NAMELEN ); - newlump.filepos = FS_Tell( wad->handle ); - newlump.attribs = ATTR_NONE; - newlump.img_type = hint; - newlump.type = type; - - if( !W_WriteLump( wad, &newlump, data, datasize )) - return -1; - - // record entry and re-sort table - W_AddFileToWad( lumpname, wad, &newlump ); - - return wad->numlumps; -} - /* =========== W_LoadFile diff --git a/engine/common/host.c b/engine/common/host.c index c240ff5b..8a56551d 100644 --- a/engine/common/host.c +++ b/engine/common/host.c @@ -348,7 +348,7 @@ Host_RegisterDecal */ qboolean Host_RegisterDecal( const char *name, int *count ) { - char shortname[CS_SIZE]; + char shortname[MAX_QPATH]; int i; if( !name || !*name ) diff --git a/engine/common/mod_bmodel.c b/engine/common/mod_bmodel.c new file mode 100644 index 00000000..ae6391a5 --- /dev/null +++ b/engine/common/mod_bmodel.c @@ -0,0 +1,2990 @@ +/* +mod_bmodel.c - loading & handling world and brushmodels +Copyright (C) 2016 Uncle Mike + +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 "mod_local.h" +#include "sprite.h" +#include "mathlib.h" +#include "alias.h" +#include "studio.h" +#include "wadfile.h" +#include "world.h" +#include "gl_local.h" +#include "features.h" +#include "client.h" +#include "server.h" // LUMP_ error codes + +typedef struct wadlist_s +{ + char wadnames[MAX_MAP_WADS][32]; + int count; +} wadlist_t; + +typedef struct leaflist_s +{ + int count; + int maxcount; + qboolean overflowed; + short *list; + vec3_t mins, maxs; + int topnode; // for overflows where each leaf can't be stored individually +} leaflist_t; + +typedef struct +{ + // generic lumps + dmodel_t *submodels; + size_t numsubmodels; + + dvertex_t *vertexes; + size_t numvertexes; + + dplane_t *planes; + size_t numplanes; + + union + { + dnode_t *nodes; + dnode32_t *nodes32; + }; + size_t numnodes; + + union + { + dleaf_t *leafs; + dleaf32_t *leafs32; + }; + int numleafs; + + union + { + dclipnode_t *clipnodes; + dclipnode32_t *clipnodes32; + }; + int numclipnodes; + + dtexinfo_t *texinfo; + size_t numtexinfo; + + union + { + dmarkface_t *markfaces; + dmarkface32_t *markfaces32; + }; + size_t nummarkfaces; + + dsurfedge_t *surfedges; + size_t numsurfedges; + + union + { + dedge_t *edges; + dedge32_t *edges32; + }; + size_t numedges; + + union + { + dface_t *surfaces; + dface32_t *surfaces32; + }; + size_t numsurfaces; + + dfaceinfo_t *faceinfo; + size_t numfaceinfo; + + // array lumps + byte *visdata; + size_t visdatasize; + + byte *lightdata; + size_t lightdatasize; + + byte *deluxdata; + size_t deluxdatasize; + + byte *shadowdata; + size_t shadowdatasize; + + byte *entdata; + size_t entdatasize; + + // lumps that required personal handler + dmiptexlump_t *textures; + size_t texdatasize; + + // intermediate arrays (pointers will lost after loading, bet keep the data) + color24 *deluxedata_out; // deluxemap data pointer + byte *shadowdata_out; // occlusion data pointer + dclipnode32_t *clipnodes_out; // temporary 32-bit array to hold clipnodes + + // misc stuff + wadlist_t wadlist; + int lightmap_samples; // samples per lightmap (1 or 3) + int version; // model version + qboolean isworld; +} dbspmodel_t; + +typedef struct +{ + const char *lumpname; + size_t entrysize; + size_t maxcount; + size_t count; +} mlumpstat_t; + +typedef struct +{ + char name[64]; // just for debug + + // count errors and warnings + int numerrors; + int numwarnings; +} loadstat_t; + +#define CHECK_OVERFLOW BIT( 0 ) // if some of lumps will be overflowed this non fatal for us. But some lumps are critical. mark them +#define USE_EXTRAHEADER BIT( 1 ) + +#define LUMP_SAVESTATS BIT( 0 ) +#define LUMP_TESTONLY BIT( 1 ) +#define LUMP_SILENT BIT( 2 ) + +typedef struct +{ + const int lumpnumber; + const size_t mincount; + const size_t maxcount; + const int entrysize; + const int entrysize32; // alternative (-1 by default) + const char *loadname; + int flags; + const void **dataptr; + size_t *count; +} mlumpinfo_t; + +world_static_t world; +static dbspmodel_t srcmodel; +static loadstat_t loadstat; +static model_t *worldmodel; +static mlumpstat_t worldstats[HEADER_LUMPS+EXTRA_LUMPS]; +static mlumpinfo_t srclumps[HEADER_LUMPS] = +{ +{ LUMP_ENTITIES, 32, MAX_MAP_ENTSTRING, sizeof( byte ), -1, "entities", 0, (void **)&srcmodel.entdata, &srcmodel.entdatasize }, +{ LUMP_PLANES, 1, MAX_MAP_PLANES, sizeof( dplane_t ), -1, "planes", 0, (void **)&srcmodel.planes, &srcmodel.numplanes }, +{ LUMP_TEXTURES, 1, MAX_MAP_MIPTEX, sizeof( byte ), -1, "textures", 0, (void **)&srcmodel.textures, &srcmodel.texdatasize }, +{ LUMP_VERTEXES, 0, MAX_MAP_VERTS, sizeof( dvertex_t ), -1, "vertexes", 0, (void **)&srcmodel.vertexes, &srcmodel.numvertexes }, +{ LUMP_VISIBILITY, 0, MAX_MAP_VISIBILITY, sizeof( byte ), -1, "visibility", 0, (void **)&srcmodel.visdata, &srcmodel.visdatasize }, +{ LUMP_NODES, 1, MAX_MAP_NODES, sizeof( dnode_t ), sizeof( dnode32_t ), "nodes", CHECK_OVERFLOW, (void **)&srcmodel.nodes, &srcmodel.numnodes }, +{ LUMP_TEXINFO, 0, MAX_MAP_TEXINFO, sizeof( dtexinfo_t ), -1, "texinfo", CHECK_OVERFLOW, (void **)&srcmodel.texinfo, &srcmodel.numtexinfo }, +{ LUMP_FACES, 0, MAX_MAP_FACES, sizeof( dface_t ), sizeof( dface32_t ), "faces", CHECK_OVERFLOW, (void **)&srcmodel.surfaces, &srcmodel.numsurfaces }, +{ LUMP_LIGHTING, 0, MAX_MAP_LIGHTING, sizeof( byte ), -1, "lightmaps", 0, (void **)&srcmodel.lightdata, &srcmodel.lightdatasize }, +{ LUMP_CLIPNODES, 0, MAX_MAP_CLIPNODES, sizeof( dclipnode_t ), sizeof( dclipnode32_t ), "clipnodes", 0, (void **)&srcmodel.clipnodes, &srcmodel.numclipnodes }, +{ LUMP_LEAFS, 1, MAX_MAP_LEAFS, sizeof( dleaf_t ), sizeof( dleaf32_t ), "leafs", CHECK_OVERFLOW, (void **)&srcmodel.leafs, &srcmodel.numleafs }, +{ LUMP_MARKSURFACES, 0, MAX_MAP_MARKSURFACES, sizeof( dmarkface_t ), sizeof( dmarkface32_t ), "markfaces", 0, (void **)&srcmodel.markfaces, &srcmodel.nummarkfaces }, +{ LUMP_EDGES, 0, MAX_MAP_EDGES, sizeof( dedge_t ), sizeof( dedge32_t ), "edges", 0, (void **)&srcmodel.edges, &srcmodel.numedges }, +{ LUMP_SURFEDGES, 0, MAX_MAP_SURFEDGES, sizeof( dsurfedge_t ), -1, "surfedges", 0, (void **)&srcmodel.surfedges, &srcmodel.numsurfedges }, +{ LUMP_MODELS, 1, MAX_MAP_MODELS, sizeof( dmodel_t ), -1, "models", CHECK_OVERFLOW, (void **)&srcmodel.submodels, &srcmodel.numsubmodels }, +}; + +static mlumpinfo_t extlumps[EXTRA_LUMPS] = +{ +{ LUMP_LIGHTVECS, 0, MAX_MAP_LIGHTING, sizeof( byte ), -1, "deluxmaps", USE_EXTRAHEADER, (void **)&srcmodel.deluxdata, &srcmodel.deluxdatasize }, +{ LUMP_FACEINFO, 0, MAX_MAP_FACEINFO, sizeof( dfaceinfo_t ), -1, "faceinfos", CHECK_OVERFLOW|USE_EXTRAHEADER, (void **)&srcmodel.faceinfo, &srcmodel.numfaceinfo }, +{ LUMP_SHADOWMAP, 0, MAX_MAP_LIGHTING / 3, sizeof( byte ), -1, "shadowmap", USE_EXTRAHEADER, (void **)&srcmodel.shadowdata, &srcmodel.shadowdatasize }, +}; + +/* +=============================================================================== + + MAP PROCESSING + +=============================================================================== +*/ +/* +================= +Mod_LoadLump + +generic loader +================= +*/ +static void Mod_LoadLump( const byte *in, mlumpinfo_t *info, mlumpstat_t *stat, int flags ) +{ + int version = ((dheader_t *)in)->version; + size_t numelems, real_entrysize; + char msg1[32], msg2[32]; + dlump_t *l = NULL; + + if( FBitSet( info->flags, USE_EXTRAHEADER )) + { + dextrahdr_t *header = (dextrahdr_t *)((byte *)in + sizeof( dheader_t )); + if( header->id != IDEXTRAHEADER || header->version != EXTRA_VERSION ) + return; + l = &header->lumps[info->lumpnumber]; + } + else + { + dheader_t *header = (dheader_t *)in; + l = &header->lumps[info->lumpnumber]; + } + + // lump is unused by engine for some reasons ? + if( !l || info->entrysize <= 0 || info->maxcount <= 0 ) + return; + + real_entrysize = info->entrysize; // default + + // analyze real entrysize + if( version == QBSP2_VERSION && info->entrysize32 > 0 ) + { + // always use alternate entrysize for BSP2 + real_entrysize = info->entrysize32; + } + else if( info->lumpnumber == LUMP_CLIPNODES && version != Q1BSP_VERSION ) + { + // never run this check for BSP29 because Arguire QBSP 'broken' clipnodes! + if(( l->filelen % info->entrysize ) || ( l->filelen / info->entrysize ) >= MAX_MAP_CLIPNODES ) + { + real_entrysize = info->entrysize32; + SetBits( flags, LUMP_SILENT ); // shut up warning + } + } + + // bmodels not required the visibility + if( !world.loading && info->lumpnumber == LUMP_VISIBILITY ) + SetBits( flags, LUMP_SILENT ); // shut up warning + + // fill the stats for world + if( FBitSet( flags, LUMP_SAVESTATS )) + { + stat->lumpname = info->loadname; + stat->entrysize = real_entrysize; + stat->maxcount = info->maxcount; + if( real_entrysize != 0 ) + stat->count = l->filelen / real_entrysize; + } + + Q_strncpy( msg1, info->loadname, sizeof( msg1 )); + Q_strncpy( msg2, info->loadname, sizeof( msg2 )); + msg2[0] = Q_toupper( msg2[0] ); // first letter in cap + + // lump is not present + if( l->filelen <= 0 ) + { + // don't warn about extra lumps - it's optional + if( !FBitSet( info->flags, USE_EXTRAHEADER )) + { + // some data array that may be optional + if( real_entrysize == sizeof( byte )) + { + if( !FBitSet( flags, LUMP_SILENT )) + MsgDev( D_WARN, "map ^2%s^7 has no %s\n", loadstat.name, msg1 ); + loadstat.numwarnings++; + } + else if( info->mincount > 0 ) + { + // it has the mincount and the lump is completely missed! + if( !FBitSet( flags, LUMP_SILENT )) + MsgDev( D_ERROR, "map ^2%s^7 has no %s\n", loadstat.name, msg1 ); + loadstat.numerrors++; + } + } + return; + } + + if( l->filelen % real_entrysize ) + { + if( !FBitSet( flags, LUMP_SILENT )) + MsgDev( D_ERROR, "Mod_Load%s: funny lump size\n", msg2 ); + loadstat.numerrors++; + return; + } + + numelems = l->filelen / real_entrysize; + + if( numelems < info->mincount ) + { + // it has the mincount and it's smaller than this limit + if( !FBitSet( flags, LUMP_SILENT )) + MsgDev( D_ERROR, "map ^2%s^7 has no %s\n", loadstat.name, msg1 ); + loadstat.numerrors++; + return; + } + + if( numelems > info->maxcount ) + { + // it has the maxcount and it's overflowed + if( FBitSet( info->flags, CHECK_OVERFLOW )) + { + if( !FBitSet( flags, LUMP_SILENT )) + MsgDev( D_ERROR, "map ^2%s^7 has too many %s\n", loadstat.name, msg1 ); + loadstat.numerrors++; + return; + } + else + { + // just throw warning + if( !FBitSet( flags, LUMP_SILENT )) + MsgDev( D_WARN, "map ^2%s^7 has too many %s\n", loadstat.name, msg1 ); + loadstat.numwarnings++; + } + } + + if( FBitSet( flags, LUMP_TESTONLY )) + return; // don't fill the intermediate struct + + // all checks are passed, store pointers + if( info->dataptr ) *info->dataptr = (void *)(in + l->fileofs); + if( info->count ) *info->count = numelems; +} + +/* +================ +Mod_ArrayUsage +================ +*/ +static int Mod_ArrayUsage( const char *szItem, int items, int maxitems, int itemsize ) +{ + float percentage = maxitems ? (items * 100.0f / maxitems) : 0.0f; + + Msg( "%-12s %7i/%-7i %8i/%-8i (%4.1f%%)", szItem, items, maxitems, items * itemsize, maxitems * itemsize, percentage ); + + if( percentage > 99.9f ) + Msg( "^1SIZE OVERFLOW!!!^7\n" ); + else if( percentage > 95.0f ) + Msg( "^3SIZE DANGER!^7\n" ); + else if( percentage > 80.0f ) + Msg( "^2VERY FULL!^7\n" ); + else Msg( "\n" ); + + return items * itemsize; +} + +/* +================ +Mod_GlobUsage +================ +*/ +static int Mod_GlobUsage( const char *szItem, int itemstorage, int maxstorage ) +{ + float percentage = maxstorage ? (itemstorage * 100.0f / maxstorage) : 0.0f; + + Msg( "%-15s %-12s %8i/%-8i (%4.1f%%)", szItem, "[variable]", itemstorage, maxstorage, percentage ); + + if( percentage > 99.9f ) + Msg( "^1SIZE OVERFLOW!!!^7\n" ); + else if( percentage > 95.0f ) + Msg( "^3SIZE DANGER!^7\n" ); + else if( percentage > 80.0f ) + Msg( "^2VERY FULL!^7\n" ); + else Msg( "\n" ); + + return itemstorage; +} + +/* +============= +Mod_PrintWorldStats_f + +Dumps info about world +============= +*/ +void Mod_PrintWorldStats_f( void ) +{ + int i, totalmemory = 0; + model_t *w = worldmodel; + + if( !w || !w->numsubmodels ) + { + Msg( "No map loaded\n" ); + return; + } + + Msg( "\n" ); + Msg( "Object names Objects/Maxobjs Memory / Maxmem Fullness\n" ); + Msg( "------------ --------------- --------------- --------\n" ); + + for( i = 0; i < ARRAYSIZE( worldstats ); i++ ) + { + mlumpstat_t *stat = &worldstats[i]; + + if( !stat->lumpname || !stat->maxcount || !stat->count ) + continue; // unused or lump is empty + + if( stat->entrysize == sizeof( byte )) + totalmemory += Mod_GlobUsage( stat->lumpname, stat->count, stat->maxcount ); + else totalmemory += Mod_ArrayUsage( stat->lumpname, stat->count, stat->maxcount, stat->entrysize ); + } + + Msg( "=== Total BSP file data space used: %s ===\n", Q_memprint( totalmemory )); + Msg( "World size ( %g %g %g ) units\n", world.size[0], world.size[1], world.size[2] ); + Msg( "Supports transparency world water: %s\n", FBitSet( world.flags, FWORLD_WATERALPHA ) ? "Yes" : "No" ); + Msg( "Lighting: %s\n", FBitSet( w->flags, MODEL_COLORED_LIGHTING ) ? "colored" : "monochrome" ); + Msg( "original name: ^1%s\n", worldmodel->name ); + Msg( "internal name: %s\n", (world.message[0]) ? va( "^2%s", world.message ) : "none" ); + Msg( "map compiler: %s\n", (world.compiler[0]) ? va( "^3%s", world.compiler ) : "unknown" ); +} + +/* +=============================================================================== + + COMMON ROUTINES + +=============================================================================== +*/ +/* +=================== +Mod_DecompressVis +=================== +*/ +static void Mod_DecompressVis( const byte *in, const byte *inend, byte *out, byte *outend ) +{ + byte *outstart = out; + int c; + + while( out < outend ) + { + if( in == inend ) + { + MsgDev( D_WARN, "Mod_DecompressVis: input underrun (decompressed %i of %i output bytes)\n", + (int)(out - outstart), (int)(outend - outstart)); + return; + } + + c = *in++; + + if( c ) + { + *out++ = c; + } + else + { + if( in == inend ) + { + MsgDev( D_NOTE, "Mod_DecompressVis: input underrun (during zero-run) (decompressed %i of %i output bytes)\n", + (int)(out - outstart), (int)(outend - outstart)); + return; + } + + for( c = *in++; c > 0; c-- ) + { + if( out == outend ) + { + MsgDev( D_NOTE, "Mod_DecompressVis: output overrun (decompressed %i of %i output bytes)\n", + (int)(out - outstart), (int)(outend - outstart)); + return; + } + *out++ = 0; + } + } + } +} + +/* +================== +Mod_PointInLeaf + +================== +*/ +mleaf_t *Mod_PointInLeaf( const vec3_t p, mnode_t *node ) +{ + ASSERT( node != NULL ); + + while( 1 ) + { + if( node->contents < 0 ) + return (mleaf_t *)node; + node = node->children[PlaneDiff( p, node->plane ) <= 0]; + } + + // never reached + return NULL; +} + +/* +================== +Mod_GetPVSForPoint + +Returns PVS data for a given point +NOTE: can return NULL +================== +*/ +byte *Mod_GetPVSForPoint( const vec3_t p ) +{ + mnode_t *node; + mleaf_t *leaf = NULL; + + ASSERT( worldmodel != NULL ); + + node = worldmodel->nodes; + + while( 1 ) + { + if( node->contents < 0 ) + { + leaf = (mleaf_t *)node; + break; // we found a leaf + } + node = node->children[PlaneDiff( p, node->plane ) <= 0]; + } + + if( leaf && leaf->cluster >= 0 ) + return world.visdata + leaf->cluster * world.visbytes; + return NULL; +} + +/* +================== +Mod_FatPVS_RecursiveBSPNode + +================== +*/ +static void Mod_FatPVS_RecursiveBSPNode( const vec3_t org, float radius, byte *visbuffer, int visbytes, mnode_t *node ) +{ + int i; + + while( node->contents >= 0 ) + { + float d = PlaneDiff( org, node->plane ); + + if( d > radius ) + node = node->children[0]; + else if( d < -radius ) + node = node->children[1]; + else + { + // go down both sides + Mod_FatPVS_RecursiveBSPNode( org, radius, visbuffer, visbytes, node->children[0] ); + node = node->children[1]; + } + } + + // if this leaf is in a cluster, accumulate the vis bits + if(((mleaf_t *)node)->cluster >= 0 ) + { + byte *vis = world.visdata + ((mleaf_t *)node)->cluster * world.visbytes; + + for( i = 0; i < visbytes; i++ ) + visbuffer[i] |= vis[i]; + } +} + +/* +================== +Mod_FatPVS_RecursiveBSPNode + +Calculates a PVS that is the inclusive or of all leafs +within radius pixels of the given point. +================== +*/ +int Mod_FatPVS( const vec3_t org, float radius, byte *visbuffer, int visbytes, qboolean merge, qboolean fullvis ) +{ + mleaf_t *leaf = Mod_PointInLeaf( org, worldmodel->nodes ); + int bytes = world.visbytes; + + bytes = Q_min( bytes, visbytes ); + + // enable full visibility for some reasons + if( fullvis || !world.visclusters || !leaf || leaf->cluster < 0 ) + { + memset( visbuffer, 0xFF, bytes ); + return bytes; + } + + if( !merge ) memset( visbuffer, 0x00, bytes ); + + Mod_FatPVS_RecursiveBSPNode( org, radius, visbuffer, bytes, worldmodel->nodes ); + + return bytes; +} + +/* +====================================================================== + +LEAF LISTING + +====================================================================== +*/ +static void Mod_BoxLeafnums_r( leaflist_t *ll, mnode_t *node ) +{ + int sides; + + while( 1 ) + { + if( node->contents == CONTENTS_SOLID ) + return; + + if( node->contents < 0 ) + { + mleaf_t *leaf = (mleaf_t *)node; + + // it's a leaf! + if( ll->count >= ll->maxcount ) + { + ll->overflowed = true; + return; + } + + ll->list[ll->count++] = leaf->cluster; + return; + } + + sides = BOX_ON_PLANE_SIDE( ll->mins, ll->maxs, node->plane ); + + if( sides == 1 ) + { + node = node->children[0]; + } + else if( sides == 2 ) + { + node = node->children[1]; + } + else + { + // go down both + if( ll->topnode == -1 ) + ll->topnode = node - worldmodel->nodes; + Mod_BoxLeafnums_r( ll, node->children[0] ); + node = node->children[1]; + } + } +} + +/* +================== +Mod_BoxLeafnums +================== +*/ +static int Mod_BoxLeafnums( const vec3_t mins, const vec3_t maxs, short *list, int listsize, int *topnode ) +{ + leaflist_t ll; + + if( !worldmodel ) return 0; + + VectorCopy( mins, ll.mins ); + VectorCopy( maxs, ll.maxs ); + + ll.maxcount = listsize; + ll.overflowed = false; + ll.topnode = -1; + ll.list = list; + ll.count = 0; + + Mod_BoxLeafnums_r( &ll, worldmodel->nodes ); + + if( topnode ) *topnode = ll.topnode; + return ll.count; +} + +/* +============= +Mod_BoxVisible + +Returns true if any leaf in boxspace +is potentially visible +============= +*/ +qboolean Mod_BoxVisible( const vec3_t mins, const vec3_t maxs, const byte *visbits ) +{ + short leafList[MAX_BOX_LEAFS]; + int i, count; + + if( !visbits || !mins || !maxs ) + return true; + + count = Mod_BoxLeafnums( mins, maxs, leafList, MAX_BOX_LEAFS, NULL ); + + for( i = 0; i < count; i++ ) + { + if( CHECKVISBIT( visbits, leafList[i] )) + return true; + } + return false; +} + +/* +============= +Mod_HeadnodeVisible +============= +*/ +qboolean Mod_HeadnodeVisible( mnode_t *node, const byte *visbits, int *lastleaf ) +{ + if( !node || node->contents == CONTENTS_SOLID ) + return false; + + if( node->contents < 0 ) + { + if( !CHECKVISBIT( visbits, ((mleaf_t *)node)->cluster )) + return false; + + if( lastleaf ) + *lastleaf = ((mleaf_t *)node)->cluster; + return true; + } + + if( Mod_HeadnodeVisible( node->children[0], visbits, lastleaf )) + return true; + + if( Mod_HeadnodeVisible( node->children[1], visbits, lastleaf )) + return true; + + return false; +} + +/* +================== +Mod_AmbientLevels + +grab the ambient sound levels for current point +================== +*/ +void Mod_AmbientLevels( const vec3_t p, byte *pvolumes ) +{ + mleaf_t *leaf; + + if( !worldmodel || !p || !pvolumes ) + return; + + leaf = Mod_PointInLeaf( p, worldmodel->nodes ); + *(int *)pvolumes = *(int *)leaf->ambient_sound_level; +} + +/* +================= +Mod_FindModelOrigin + +routine to detect bmodels with origin-brush +================= +*/ +static void Mod_FindModelOrigin( const char *entities, const char *modelname, vec3_t origin ) +{ + char *pfile; + string keyname; + char token[2048]; + qboolean model_found; + qboolean origin_found; + + if( !entities || !modelname || !*modelname ) + return; + + if( !origin || !VectorIsNull( origin )) + return; + + pfile = (char *)entities; + + while(( pfile = COM_ParseFile( pfile, token )) != NULL ) + { + if( token[0] != '{' ) + Host_Error( "Mod_FindModelOrigin: found %s when expecting {\n", token ); + + model_found = origin_found = false; + VectorClear( origin ); + + while( 1 ) + { + // parse key + if(( pfile = COM_ParseFile( pfile, token )) == NULL ) + Host_Error( "Mod_FindModelOrigin: EOF without closing brace\n" ); + if( token[0] == '}' ) break; // end of desc + + Q_strncpy( keyname, token, sizeof( keyname )); + + // parse value + if(( pfile = COM_ParseFile( pfile, token )) == NULL ) + Host_Error( "Mod_FindModelOrigin: EOF without closing brace\n" ); + + if( token[0] == '}' ) + Host_Error( "Mod_FindModelOrigin: closing brace without data\n" ); + + if( !Q_stricmp( keyname, "model" ) && !Q_stricmp( modelname, token )) + model_found = true; + + if( !Q_stricmp( keyname, "origin" )) + { + Q_atov( origin, token, 3 ); + origin_found = true; + } + } + + if( model_found ) break; + } +} + +/* +================== +Mod_CheckWaterAlphaSupport + +converted maps potential may don't +support water transparency +================== +*/ +static qboolean Mod_CheckWaterAlphaSupport( dbspmodel_t *bmod ) +{ + mleaf_t *leaf; + int i, j; + const byte *pvs; + + if( bmod->visdatasize <= 0 ) + return true; + + // check all liquid leafs to see if they can see into empty leafs, if any + // can we can assume this map supports r_wateralpha + for( i = 0, leaf = loadmodel->leafs; i < loadmodel->numleafs; i++, leaf++ ) + { + if(( leaf->contents == CONTENTS_WATER || leaf->contents == CONTENTS_SLIME ) && leaf->cluster >= 0 ) + { + pvs = world.visdata + leaf->cluster * world.visbytes; + + for( j = 0; j < loadmodel->numleafs; j++ ) + { + if( CHECKVISBIT( pvs, loadmodel->leafs[j].cluster ) && loadmodel->leafs[j].contents == CONTENTS_EMPTY ) + return true; + } + } + } + + return false; +} + +/* +================== +Mod_SampleSizeForFace + +return the current lightmap resolution per face +================== +*/ +int Mod_SampleSizeForFace( msurface_t *surf ) +{ + if( !surf || !surf->texinfo ) + return LM_SAMPLE_SIZE; + + // world luxels has more priority + if( FBitSet( surf->texinfo->flags, TEX_WORLD_LUXELS )) + return 1; + + if( FBitSet( surf->texinfo->flags, TEX_EXTRA_LIGHTMAP )) + return LM_SAMPLE_EXTRASIZE; + + if( surf->texinfo->faceinfo ) + return surf->texinfo->faceinfo->texture_step; + + return LM_SAMPLE_SIZE; +} + +/* +================== +Mod_MakeNormalAxial + +remove jitter from near-axial normals +================== +*/ +static void Mod_MakeNormalAxial( vec3_t normal ) +{ + int i, type; + + for( type = 0; type < 3; type++ ) + { + if( fabs( normal[type] ) > 0.9999f ) + break; + } + + // make positive and pure axial + for( i = 0; i < 3 && type != 3; i++ ) + { + if( i == type ) + normal[i] = 1.0f; + else normal[i] = 0.0f; + } +} + +/* +================== +Mod_LightMatrixFromTexMatrix + +compute lightmap matrix based on texture matrix +================== +*/ +static void Mod_LightMatrixFromTexMatrix( const mtexinfo_t *tx, float lmvecs[2][4] ) +{ + float lmscale = LM_SAMPLE_SIZE; + int i, j; + + // this is can't be possible but who knews + if( FBitSet( tx->flags, TEX_EXTRA_LIGHTMAP )) + lmscale = LM_SAMPLE_EXTRASIZE; + + if( tx->faceinfo ) + lmscale = tx->faceinfo->texture_step; + + // copy texmatrix into lightmap matrix fisrt + for( i = 0; i < 2; i++ ) + { + for( j = 0; j < 4; j++ ) + { + lmvecs[i][j] = tx->vecs[i][j]; + } + } + + if( !FBitSet( tx->flags, TEX_WORLD_LUXELS )) + return; // just use texmatrix + + VectorNormalize( lmvecs[0] ); + VectorNormalize( lmvecs[1] ); + + if( FBitSet( tx->flags, TEX_AXIAL_LUXELS )) + { + Mod_MakeNormalAxial( lmvecs[0] ); + Mod_MakeNormalAxial( lmvecs[1] ); + } + + // put the lighting origin at center the of poly + VectorScale( lmvecs[0], (1.0 / lmscale), lmvecs[0] ); + VectorScale( lmvecs[1], -(1.0 / lmscale), lmvecs[1] ); + + lmvecs[0][3] = lmscale * 0.5; + lmvecs[1][3] = -lmscale * 0.5; +} + +/* +================= +Mod_CalcSurfaceExtents + +Fills in surf->texturemins[] and surf->extents[] +================= +*/ +static void Mod_CalcSurfaceExtents( msurface_t *surf ) +{ + float mins[2], maxs[2], val; + float lmmins[2], lmmaxs[2]; + int bmins[2], bmaxs[2]; + int i, j, e, sample_size; + mextrasurf_t *info = surf->info; + int facenum = surf - loadmodel->surfaces; + mtexinfo_t *tex; + mvertex_t *v; + + sample_size = Mod_SampleSizeForFace( surf ); + tex = surf->texinfo; + + Mod_LightMatrixFromTexMatrix( tex, info->lmvecs ); + + mins[0] = lmmins[0] = mins[1] = lmmins[1] = 999999; + maxs[0] = lmmaxs[0] = maxs[1] = lmmaxs[1] =-999999; + + for( i = 0; i < surf->numedges; i++ ) + { + e = loadmodel->surfedges[surf->firstedge + i]; + + if( e >= loadmodel->numedges || e <= -loadmodel->numedges ) + Host_Error( "Mod_CalcSurfaceExtents: bad edge\n" ); + + if( e >= 0 ) v = &loadmodel->vertexes[loadmodel->edges[e].v[0]]; + else v = &loadmodel->vertexes[loadmodel->edges[-e].v[1]]; + + for( j = 0; j < 2; j++ ) + { + val = DotProduct( v->position, surf->texinfo->vecs[j] ) + surf->texinfo->vecs[j][3]; + mins[j] = Q_min( val, mins[j] ); + maxs[j] = Q_max( val, maxs[j] ); + } + + for( j = 0; j < 2; j++ ) + { + val = DotProduct( v->position, info->lmvecs[j] ) + info->lmvecs[j][3]; + lmmins[j] = Q_min( val, lmmins[j] ); + lmmaxs[j] = Q_max( val, lmmaxs[j] ); + } + } + + for( i = 0; i < 2; i++ ) + { + bmins[i] = floor( mins[i] / sample_size ); + bmaxs[i] = ceil( maxs[i] / sample_size ); + + surf->texturemins[i] = bmins[i] * sample_size; + surf->extents[i] = (bmaxs[i] - bmins[i]) * sample_size; + + if( FBitSet( tex->flags, TEX_WORLD_LUXELS )) + { + lmmins[i] = floor( lmmins[i] ); + lmmaxs[i] = ceil( lmmaxs[i] ); + + info->lightmapmins[i] = lmmins[i]; + info->lightextents[i] = (lmmaxs[i] - lmmins[i]); + } + else + { + // just copy texturemins + info->lightmapmins[i] = surf->texturemins[i]; + info->lightextents[i] = surf->extents[i]; + } + + if( !FBitSet( tex->flags, TEX_SPECIAL ) && surf->extents[i] > 4096 ) + MsgDev( D_ERROR, "Bad surface extents %i\n", surf->extents[i] ); + } +} + +/* +================= +Mod_CalcSurfaceBounds + +fills in surf->mins and surf->maxs +================= +*/ +static void Mod_CalcSurfaceBounds( msurface_t *surf ) +{ + int i, e; + mvertex_t *v; + + ClearBounds( surf->info->mins, surf->info->maxs ); + + for( i = 0; i < surf->numedges; i++ ) + { + e = loadmodel->surfedges[surf->firstedge + i]; + + if( e >= loadmodel->numedges || e <= -loadmodel->numedges ) + Host_Error( "Mod_CalcSurfaceBounds: bad edge\n" ); + + if( e >= 0 ) v = &loadmodel->vertexes[loadmodel->edges[e].v[0]]; + else v = &loadmodel->vertexes[loadmodel->edges[-e].v[1]]; + AddPointToBounds( v->position, surf->info->mins, surf->info->maxs ); + } + + VectorAverage( surf->info->mins, surf->info->maxs, surf->info->origin ); +} + +/* +================= +Mod_SetParent +================= +*/ +static void Mod_SetParent( mnode_t *node, mnode_t *parent ) +{ + node->parent = parent; + + if( node->contents < 0 ) return; // it's leaf + Mod_SetParent( node->children[0], node ); + Mod_SetParent( node->children[1], node ); +} + +/* +================== +CountClipNodes_r +================== +*/ +static void CountClipNodes_r( dclipnode32_t *src, hull_t *hull, int nodenum ) +{ + // leaf? + if( nodenum < 0 ) return; + + if( hull->lastclipnode == MAX_MAP_CLIPNODES ) + Host_Error( "MAX_MAP_CLIPNODES limit exceeded\n" ); + hull->lastclipnode++; + + CountClipNodes_r( src, hull, src[nodenum].children[0] ); + CountClipNodes_r( src, hull, src[nodenum].children[1] ); +} + +/* +================== +RemapClipNodes_r +================== +*/ +static int RemapClipNodes_r( dclipnode32_t *srcnodes, hull_t *hull, int nodenum ) +{ + dclipnode32_t *src; + mclipnode_t *out; + int i, c; + + // leaf? + if( nodenum < 0 ) + return nodenum; + + // emit a clipnode + if( hull->lastclipnode == MAX_MAP_CLIPNODES ) + Host_Error( "MAX_MAP_CLIPNODES limit exceeded\n" ); + src = srcnodes + nodenum; + + c = hull->lastclipnode; + out = &hull->clipnodes[c]; + hull->lastclipnode++; + + out->planenum = src->planenum; + + for( i = 0; i < 2; i++ ) + out->children[i] = RemapClipNodes_r( srcnodes, hull, src->children[i] ); + + return c; +} + +/* +================= +Mod_MakeHull0 + +Duplicate the drawing hull structure as a clipping hull +================= +*/ +static void Mod_MakeHull0( void ) +{ + mnode_t *in, *child; + mclipnode_t *out; + hull_t *hull; + int i, j; + + hull = &loadmodel->hulls[0]; + hull->clipnodes = out = Mem_Alloc( loadmodel->mempool, loadmodel->numnodes * sizeof( *out )); + in = loadmodel->nodes; + + hull->firstclipnode = 0; + hull->lastclipnode = loadmodel->numnodes - 1; + hull->planes = loadmodel->planes; + + for( i = 0; i < loadmodel->numnodes; i++, out++, in++ ) + { + out->planenum = in->plane - loadmodel->planes; + + for( j = 0; j < 2; j++ ) + { + child = in->children[j]; + + if( child->contents < 0 ) + out->children[j] = child->contents; + else out->children[j] = child - loadmodel->nodes; + } + } +} + +/* +================= +Mod_SetupHull +================= +*/ +static void Mod_SetupHull( dbspmodel_t *bmod, model_t *mod, byte *mempool, int headnode, int hullnum ) +{ + hull_t *hull = &mod->hulls[hullnum]; + int count; + + // assume no hull + hull->firstclipnode = hull->lastclipnode = 0; + hull->planes = NULL; // hull is missed + + if(( headnode == -1 ) || ( hullnum != 1 && headnode == 0 )) + return; // hull missed + + if( headnode >= mod->numclipnodes ) + return; // ZHLT weird empty hulls + + switch( hullnum ) + { + case 1: + VectorCopy( host.player_mins[0], hull->clip_mins ); // copy human hull + VectorCopy( host.player_maxs[0], hull->clip_maxs ); + break; + case 2: + VectorCopy( host.player_mins[3], hull->clip_mins ); // copy large hull + VectorCopy( host.player_maxs[3], hull->clip_maxs ); + break; + case 3: + VectorCopy( host.player_mins[1], hull->clip_mins ); // copy head hull + VectorCopy( host.player_maxs[1], hull->clip_maxs ); + break; + default: + Host_Error( "Mod_SetupHull: bad hull number %i\n", hullnum ); + break; + } + + if( VectorIsNull( hull->clip_mins ) && VectorIsNull( hull->clip_maxs )) + return; // no hull specified + + CountClipNodes_r( bmod->clipnodes_out, hull, headnode ); + count = hull->lastclipnode; + + hull->clipnodes = (mclipnode_t *)Mem_Alloc( mempool, sizeof( mclipnode_t ) * hull->lastclipnode ); + hull->planes = mod->planes; // share planes + hull->lastclipnode = 0; // restart counting + + // remap clipnodes to 16-bit indexes + RemapClipNodes_r( bmod->clipnodes_out, hull, headnode ); + + // fit array to real count +// Msg( "%s hull%d, %i clipnodes (headnode %i)\n", mod->name, hullnum, hull->lastclipnode, headnode ); +} + +/* +================= +Mod_LoadColoredLighting +================= +*/ +static qboolean Mod_LoadColoredLighting( dbspmodel_t *bmod ) +{ + char modelname[64]; + char path[64]; + int iCompare; + size_t litdatasize; + byte *in; + + FS_FileBase( loadmodel->name, modelname ); + Q_snprintf( path, sizeof( path ), "maps/%s.lit", modelname ); + + // make sure what deluxemap is actual + if( !COM_CompareFileTime( path, loadmodel->name, &iCompare )) + return false; + + if( iCompare < 0 ) // this may happens if level-designer used -onlyents key for hlcsg + MsgDev( D_WARN, "Mod_LoadColoredLighting: %s probably is out of date\n", path ); + + in = FS_LoadFile( path, &litdatasize, false ); + + ASSERT( in != NULL ); + + if( *(uint *)in != IDDELUXEMAPHEADER || *((uint *)in + 1) != DELUXEMAP_VERSION ) + { + MsgDev( D_ERROR, "Mod_LoadColoredLighting: %s is not a lightmap file\n", path ); + Mem_Free( in ); + return false; + } + + // skip header bytes + litdatasize -= 8; + + MsgDev( D_INFO, "Mod_LoadColoredLighting: %s loaded\n", path ); + loadmodel->lightdata = Mem_Alloc( loadmodel->mempool, litdatasize ); + memcpy( loadmodel->lightdata, in + 8, litdatasize ); + SetBits( loadmodel->flags, MODEL_COLORED_LIGHTING ); + bmod->lightdatasize = litdatasize; + Mem_Free( in ); + + return true; +} + +/* +================= +Mod_LoadDeluxemap +================= +*/ +static void Mod_LoadDeluxemap( dbspmodel_t *bmod ) +{ + char modelname[64]; + size_t deluxdatasize; + char path[64]; + int iCompare; + byte *in; + + if( !FBitSet( host.features, ENGINE_LOAD_DELUXEDATA )) + return; + + FS_FileBase( loadmodel->name, modelname ); + Q_snprintf( path, sizeof( path ), "maps/%s.dlit", modelname ); + + // make sure what deluxemap is actual + if( !COM_CompareFileTime( path, loadmodel->name, &iCompare )) + return; + + if( iCompare < 0 ) // this may happens if level-designer used -onlyents key for hlcsg + MsgDev( D_WARN, "Mod_LoadDeluxemap: %s probably is out of date\n", path ); + + in = FS_LoadFile( path, &deluxdatasize, false ); + + ASSERT( in != NULL ); + + if( *(uint *)in != IDDELUXEMAPHEADER || *((uint *)in + 1) != DELUXEMAP_VERSION ) + { + MsgDev( D_ERROR, "Mod_LoadDeluxemap: %s is not a deluxemap file\n", path ); + Mem_Free( in ); + return; + } + + // skip header bytes + deluxdatasize -= 8; + + if( deluxdatasize != bmod->lightdatasize ) + { + MsgDev( D_ERROR, "Mod_LoadDeluxemap: %s has mismatched size (%i should be %i)\n", path, deluxdatasize, bmod->lightdatasize ); + Mem_Free( in ); + return; + } + + MsgDev( D_INFO, "Mod_LoadDeluxemap: %s loaded\n", path ); + bmod->deluxedata_out = Mem_Alloc( loadmodel->mempool, deluxdatasize ); + memcpy( bmod->deluxedata_out, in + 8, deluxdatasize ); + bmod->deluxdatasize = deluxdatasize; + Mem_Free( in ); +} + +/* +================= +Mod_SetupSubmodels + +duplicate the basic information +for embedded submodels +================= +*/ +static void Mod_SetupSubmodels( dbspmodel_t *bmod ) +{ + byte *mempool; + char *ents; + model_t *mod; + dmodel_t *bm; + int i, j; + + ents = loadmodel->entities; + mempool = loadmodel->mempool; + mod = loadmodel; + + loadmodel->numframes = 2; // regular and alternate animation + + // set up the submodels + for( i = 0; i < mod->numsubmodels; i++ ) + { + bm = &mod->submodels[i]; + + // hull 0 is just shared across all bmodels + mod->hulls[0].firstclipnode = bm->headnode[0]; + + // but hulls1-3 is build individually for a each given submodel + for( j = 1; j < MAX_MAP_HULLS; j++ ) + Mod_SetupHull( bmod, mod, mempool, bm->headnode[j], j ); + + mod->firstmodelsurface = bm->firstface; + mod->nummodelsurfaces = bm->numfaces; + + VectorCopy( bm->mins, mod->mins ); + VectorCopy( bm->maxs, mod->maxs ); + + mod->radius = RadiusFromBounds( mod->mins, mod->maxs ); + mod->numleafs = bm->visleafs; +// mod->flags = 0; + + if( i != 0 ) + { + Mod_FindModelOrigin( ents, va( "*%i", i ), bm->origin ); + + // HACKHACK: c2a1 issues + if( !Q_stricmp( loadmodel->name, "maps/c2a1.bsp" ) && ( i == 11 )) + SetBits( mod->flags, MODEL_HAS_ORIGIN ); + + // flag 2 is indicated model with origin brush! + if( !VectorIsNull( bm->origin )) + SetBits( mod->flags, MODEL_HAS_ORIGIN ); + } + + for( j = 0; i != 0 && j < mod->nummodelsurfaces; j++ ) + { + msurface_t *surf = mod->surfaces + mod->firstmodelsurface + j; + + if( surf->flags & SURF_CONVEYOR ) + mod->flags |= MODEL_CONVEYOR; + + if( surf->flags & SURF_TRANSPARENT ) + mod->flags |= MODEL_TRANSPARENT; + + // kill water backplanes for submodels (half-life rules) + if( surf->flags & SURF_DRAWTURB ) + { + mod->flags |= MODEL_LIQUID; + + if( surf->plane->type == PLANE_Z ) + { + // kill bottom plane too + if( surf->info->mins[2] == bm->mins[2] + 1.0f ) + surf->flags |= SURF_WATERCSG; + } + else + { + // kill side planes + surf->flags |= SURF_WATERCSG; + } + } + } + + if( i < mod->numsubmodels - 1 ) + { + char name[8]; + + // duplicate the basic information + Q_snprintf( name, sizeof( name ), "*%i", i + 1 ); + loadmodel = Mod_FindName( name, true ); + *loadmodel = *mod; + Q_strncpy( loadmodel->name, name, sizeof( loadmodel->name )); + loadmodel->mempool = NULL; + mod = loadmodel; + } + } + + Mem_Free( bmod->clipnodes_out ); +} + +/* +=============================================================================== + + MAP LOADING + +=============================================================================== +*/ +/* +================= +Mod_LoadSubmodels +================= +*/ +static void Mod_LoadSubmodels( dbspmodel_t *bmod ) +{ + dmodel_t *in, *out; + int oldmaxfaces; + int i, j; + + // allocate extradata for each dmodel_t + out = Mem_Alloc( loadmodel->mempool, bmod->numsubmodels * sizeof( *out )); + + loadmodel->numsubmodels = bmod->numsubmodels; + loadmodel->submodels = out; + in = bmod->submodels; + + if( bmod->isworld ) + world.max_surfaces = 0; + oldmaxfaces = world.max_surfaces; + + for( i = 0; i < bmod->numsubmodels; i++, in++, out++ ) + { + for( j = 0; j < 3; j++ ) + { + // spread the mins / maxs by a unit + out->mins[j] = in->mins[j] - 1.0f; + out->maxs[j] = in->maxs[j] + 1.0f; + out->origin[j] = in->origin[j]; + } + + for( j = 0; j < MAX_MAP_HULLS; j++ ) + out->headnode[j] = in->headnode[j]; + + out->visleafs = in->visleafs; + out->firstface = in->firstface; + out->numfaces = in->numfaces; + + if( i == 0 && bmod->isworld ) + continue; // skip the world to save mem + oldmaxfaces = Q_max( oldmaxfaces, out->numfaces ); + } + + // these array used to sort translucent faces in bmodels + if( oldmaxfaces > world.max_surfaces ) + { + world.draw_surfaces = (msurface_t **)Z_Realloc( world.draw_surfaces, oldmaxfaces * sizeof( msurface_t* )); + world.max_surfaces = oldmaxfaces; + } +} + +/* +================= +Mod_LoadEntities +================= +*/ +static void Mod_LoadEntities( dbspmodel_t *bmod ) +{ + char *pfile; + string keyname; + char token[MAX_TOKEN]; + char wadstring[2048]; + + // make sure what we really has terminator + loadmodel->entities = Mem_Alloc( loadmodel->mempool, bmod->entdatasize + 1 ); + memcpy( loadmodel->entities, bmod->entdata, bmod->entdatasize ); + if( !bmod->isworld ) return; + + pfile = (char *)loadmodel->entities; + world.compiler[0] = '\0'; + world.message[0] = '\0'; + bmod->wadlist.count = 0; + + // parse all the wads for loading textures in right ordering + while(( pfile = COM_ParseFile( pfile, token )) != NULL ) + { + if( token[0] != '{' ) + Host_Error( "Mod_LoadEntities: found %s when expecting {\n", token ); + + while( 1 ) + { + // parse key + if(( pfile = COM_ParseFile( pfile, token )) == NULL ) + Host_Error( "Mod_LoadEntities: EOF without closing brace\n" ); + if( token[0] == '}' ) break; // end of desc + + Q_strncpy( keyname, token, sizeof( keyname )); + + // parse value + if(( pfile = COM_ParseFile( pfile, token )) == NULL ) + Host_Error( "Mod_LoadEntities: EOF without closing brace\n" ); + + if( token[0] == '}' ) + Host_Error( "Mod_LoadEntities: closing brace without data\n" ); + + if( !Q_stricmp( keyname, "wad" )) + { + char *pszWadFile; + + Q_strncpy( wadstring, token, MAX_TOKEN - 2 ); + wadstring[MAX_TOKEN - 2] = 0; + + if( !Q_strchr( wadstring, ';' )) + Q_strcat( wadstring, ";" ); + + // parse wad pathes + for( pszWadFile = strtok( wadstring, ";" ); pszWadFile != NULL; pszWadFile = strtok( NULL, ";" )) + { + COM_FixSlashes( pszWadFile ); + FS_FileBase( pszWadFile, token ); + + // make sure what wad is really exist + if( FS_FileExists( va( "%s.wad", token ), false )) + { + int num = bmod->wadlist.count++; + Q_strncpy( bmod->wadlist.wadnames[num], token, sizeof( bmod->wadlist.wadnames[0] )); + } + + if( bmod->wadlist.count >= MAX_MAP_WADS ) + break; // too many wads... + } + } + else if( !Q_stricmp( keyname, "message" )) + Q_strncpy( world.message, token, sizeof( world.message )); + else if( !Q_stricmp( keyname, "compiler" ) || !Q_stricmp( keyname, "_compiler" )) + Q_strncpy( world.compiler, token, sizeof( world.compiler )); + } + return; // all done + } +} + +/* +================= +Mod_LoadPlanes +================= +*/ +static void Mod_LoadPlanes( dbspmodel_t *bmod ) +{ + dplane_t *in; + mplane_t *out; + int i, j; + + in = bmod->planes; + loadmodel->planes = out = Mem_Alloc( loadmodel->mempool, bmod->numplanes * sizeof( *out )); + loadmodel->numplanes = bmod->numplanes; + + for( i = 0; i < bmod->numplanes; i++, in++, out++ ) + { + for( j = 0; j < 3; j++ ) + { + out->normal[j] = in->normal[j]; + + if( out->normal[j] < 0.0f ) + SetBits( out->signbits, BIT( j )); + } + + if( VectorLength( out->normal ) < 0.5f ) + Host_Error( "Mod_LoadPlanes: bad normal for plane #%i\n", i ); + + out->dist = in->dist; + out->type = in->type; + } +} + +/* +================= +Mod_LoadVertexes +================= +*/ +static void Mod_LoadVertexes( dbspmodel_t *bmod ) +{ + dvertex_t *in; + mvertex_t *out; + int i; + + in = bmod->vertexes; + out = loadmodel->vertexes = Mem_Alloc( loadmodel->mempool, bmod->numvertexes * sizeof( mvertex_t )); + loadmodel->numvertexes = bmod->numvertexes; + + if( bmod->isworld ) ClearBounds( world.mins, world.maxs ); + + for( i = 0; i < bmod->numvertexes; i++, in++, out++ ) + { + if( bmod->isworld ) + AddPointToBounds( in->point, world.mins, world.maxs ); + VectorCopy( in->point, out->position ); + } + + if( !bmod->isworld ) return; + + VectorSubtract( world.maxs, world.mins, world.size ); + + for( i = 0; i < 3; i++ ) + { + // spread the mins / maxs by a pixel + world.mins[i] -= 1.0f; + world.maxs[i] += 1.0f; + } +} + +/* +================= +Mod_LoadEdges +================= +*/ +static void Mod_LoadEdges( dbspmodel_t *bmod ) +{ + medge_t *out; + int i; + + loadmodel->edges = out = Mem_Alloc( loadmodel->mempool, bmod->numedges * sizeof( medge_t )); + loadmodel->numedges = bmod->numedges; + + if( bmod->version == QBSP2_VERSION ) + { + dedge32_t *in = (dedge32_t *)bmod->edges32; + + for( i = 0; i < bmod->numedges; i++, in++, out++ ) + { + out->v[0] = in->v[0]; + out->v[1] = in->v[1]; + } + } + else + { + dedge_t *in = (dedge_t *)bmod->edges; + + for( i = 0; i < bmod->numedges; i++, in++, out++ ) + { + out->v[0] = (word)in->v[0]; + out->v[1] = (word)in->v[1]; + } + } +} + +/* +================= +Mod_LoadSurfEdges +================= +*/ +static void Mod_LoadSurfEdges( dbspmodel_t *bmod ) +{ + loadmodel->surfedges = Mem_Alloc( loadmodel->mempool, bmod->numsurfedges * sizeof( dsurfedge_t )); + memcpy( loadmodel->surfedges, bmod->surfedges, bmod->numsurfedges * sizeof( dsurfedge_t )); + loadmodel->numsurfedges = bmod->numsurfedges; +} + +/* +================= +Mod_LoadMarkSurfaces +================= +*/ +static void Mod_LoadMarkSurfaces( dbspmodel_t *bmod ) +{ + msurface_t **out; + int i; + + loadmodel->marksurfaces = out = Mem_Alloc( loadmodel->mempool, bmod->nummarkfaces * sizeof( *out )); + loadmodel->nummarksurfaces = bmod->nummarkfaces; + + if( bmod->version == QBSP2_VERSION ) + { + dmarkface32_t *in = bmod->markfaces32; + + for( i = 0; i < bmod->nummarkfaces; i++, in++ ) + { + if( *in < 0 || *in >= loadmodel->numsurfaces ) + Host_Error( "Mod_LoadMarkFaces: bad surface number in '%s'\n", loadmodel->name ); + out[i] = loadmodel->surfaces + *in; + } + } + else + { + dmarkface_t *in = bmod->markfaces; + + for( i = 0; i < bmod->nummarkfaces; i++, in++ ) + { + if( *in < 0 || *in >= loadmodel->numsurfaces ) + Host_Error( "Mod_LoadMarkFaces: bad surface number in '%s'\n", loadmodel->name ); + out[i] = loadmodel->surfaces + *in; + } + } +} + +/* +================= +Mod_LoadTextures +================= +*/ +static void Mod_LoadTextures( dbspmodel_t *bmod ) +{ + dmiptexlump_t *in; + texture_t *tx, *tx2; + texture_t *anims[10]; + texture_t *altanims[10]; + int num, max, altmax; + qboolean custom_palette; + char texname[64]; + imgfilter_t *filter; + mip_t *mt; + int i, j; + + if( bmod->isworld ) + { + // release old sky layers first + GL_FreeTexture( tr.solidskyTexture ); + GL_FreeTexture( tr.alphaskyTexture ); + tr.solidskyTexture = 0; + tr.alphaskyTexture = 0; + } + + if( !bmod->texdatasize ) + { + // no textures + loadmodel->textures = NULL; + return; + } + + in = bmod->textures; + loadmodel->textures = (texture_t **)Mem_Alloc( loadmodel->mempool, in->nummiptex * sizeof( texture_t* )); + loadmodel->numtextures = in->nummiptex; + + for( i = 0; i < loadmodel->numtextures; i++ ) + { + if( in->dataofs[i] == -1 ) + { + // create default texture (some mods requires this) + tx = Mem_Alloc( loadmodel->mempool, sizeof( *tx )); + loadmodel->textures[i] = tx; + + Q_strncpy( tx->name, "*default", sizeof( tx->name )); + tx->gl_texturenum = tr.defaultTexture; + tx->width = tx->height = 16; + continue; // missed + } + + mt = (mip_t *)((byte *)in + in->dataofs[i] ); + + if( !mt->name[0] ) + { + MsgDev( D_WARN, "unnamed texture in %s\n", loadstat.name ); + Q_snprintf( mt->name, sizeof( mt->name ), "miptex_%i", i ); + } + + tx = Mem_Alloc( loadmodel->mempool, sizeof( *tx )); + loadmodel->textures[i] = tx; + + // convert to lowercase + Q_strncpy( tx->name, mt->name, sizeof( tx->name )); + Q_strnlwr( tx->name, tx->name, sizeof( tx->name )); + filter = R_FindTexFilter( tx->name ); // grab texture filter + custom_palette = false; + + tx->width = mt->width; + tx->height = mt->height; + + if( mt->offsets[0] > 0 ) + { + int size = (int)sizeof( mip_t ) + ((mt->width * mt->height * 85)>>6); + int next_dataofs, remaining; + + // compute next dataofset to determine allocated miptex sapce + for( j = i + 1; j < loadmodel->numtextures; j++ ) + { + next_dataofs = in->dataofs[j]; + if( next_dataofs != -1 ) break; + } + + if( j == loadmodel->numtextures ) + next_dataofs = bmod->texdatasize; + + // NOTE: imagelib detect miptex version by size + // 770 additional bytes is indicated custom palette + remaining = next_dataofs - (in->dataofs[i] + size); + if( remaining >= 770 ) custom_palette = true; + } + + // check for multi-layered sky texture (quake1 specific) + if( bmod->isworld && !Q_strncmp( mt->name, "sky", 3 ) && (( mt->width / mt->height ) == 2 )) + { + R_InitSkyClouds( mt, tx, custom_palette ); // load quake sky + + if( tr.solidskyTexture && tr.alphaskyTexture ) + SetBits( world.flags, FWORLD_SKYSPHERE ); + continue; + } + + // texture loading order: + // 1. from wad + // 2. internal from map + + // trying wad texture (force while r_wadtextures is 1) + if(( r_wadtextures->value && bmod->wadlist.count > 0 ) || ( mt->offsets[0] <= 0 )) + { + Q_snprintf( texname, sizeof( texname ), "%s.mip", mt->name ); + + // check wads in reverse order + for( j = bmod->wadlist.count - 1; j >= 0; j-- ) + { + char *texpath = va( "%s.wad/%s", bmod->wadlist.wadnames[j], texname ); + + if( FS_FileExists( texpath, false )) + { + tx->gl_texturenum = GL_LoadTexture( texpath, NULL, 0, 0, filter ); + break; + } + } + } + + // wad failed, so use internal texture (if present) + if( mt->offsets[0] > 0 && !tx->gl_texturenum ) + { + // NOTE: imagelib detect miptex version by size + // 770 additional bytes is indicated custom palette + int size = (int)sizeof( mip_t ) + ((mt->width * mt->height * 85)>>6); + + if( custom_palette ) size += sizeof( short ) + 768; + Q_snprintf( texname, sizeof( texname ), "#%s.mip", mt->name ); + tx->gl_texturenum = GL_LoadTexture( texname, (byte *)mt, size, 0, filter ); + } + + // if texture is completely missed + if( !tx->gl_texturenum ) + { + MsgDev( D_ERROR, "couldn't load %s.mip\n", mt->name ); + tx->gl_texturenum = tr.defaultTexture; + } + + // check for luma texture + if( FBitSet( R_GetTexture( tx->gl_texturenum )->flags, TF_HAS_LUMA )) + { + Q_snprintf( texname, sizeof( texname ), "#%s_luma.mip", mt->name ); + + if( mt->offsets[0] > 0 ) + { + // NOTE: imagelib detect miptex version by size + // 770 additional bytes is indicated custom palette + int size = (int)sizeof( mip_t ) + ((mt->width * mt->height * 85)>>6); + + if( custom_palette ) size += sizeof( short ) + 768; + tx->fb_texturenum = GL_LoadTexture( texname, (byte *)mt, size, TF_MAKELUMA, NULL ); + } + else + { + size_t srcSize = 0; + byte *src = NULL; + + // NOTE: we can't loading it from wad as normal because _luma texture doesn't exist + // and not be loaded. But original texture is already loaded and can't be modified + // So load original texture manually and convert it to luma + + // check wads in reverse order + for( j = bmod->wadlist.count - 1; j >= 0; j-- ) + { + char *texpath = va( "%s.wad/%s.mip", bmod->wadlist.wadnames[j], tx->name ); + + if( FS_FileExists( texpath, false )) + { + src = FS_LoadFile( texpath, &srcSize, false ); + break; + } + } + + // okay, loading it from wad or hi-res version + tx->fb_texturenum = GL_LoadTexture( texname, src, srcSize, TF_MAKELUMA, NULL ); + if( src ) Mem_Free( src ); + } + } + } + + // sequence the animations and detail textures + for( i = 0; i < loadmodel->numtextures; i++ ) + { + tx = loadmodel->textures[i]; + + if( !tx || ( tx->name[0] != '-' && tx->name[0] != '+' )) + continue; + + if( tx->anim_next ) + continue; // already sequenced + + // find the number of frames in the animation + memset( anims, 0, sizeof( anims )); + memset( altanims, 0, sizeof( altanims )); + + max = tx->name[1]; + altmax = 0; + + if( max >= '0' && max <= '9' ) + { + max -= '0'; + altmax = 0; + anims[max] = tx; + max++; + } + else if( max >= 'a' && max <= 'j' ) + { + altmax = max - 'a'; + max = 0; + altanims[altmax] = tx; + altmax++; + } + else MsgDev( D_ERROR, "Mod_LoadTextures: bad animating texture %s\n", tx->name ); + + for( j = i + 1; j < loadmodel->numtextures; j++ ) + { + tx2 = loadmodel->textures[j]; + + if( !tx2 || ( tx2->name[0] != '-' && tx2->name[0] != '+' )) + continue; + + if( Q_strcmp( tx2->name + 2, tx->name + 2 )) + continue; + + num = tx2->name[1]; + + if( num >= '0' && num <= '9' ) + { + num -= '0'; + anims[num] = tx2; + if( num + 1 > max ) + max = num + 1; + } + else if( num >= 'a' && num <= 'j' ) + { + num = num - 'a'; + altanims[num] = tx2; + if( num + 1 > altmax ) + altmax = num + 1; + } + else MsgDev( D_ERROR, "Mod_LoadTextures: bad animating texture %s\n", tx->name ); + } + + // link them all together + for( j = 0; j < max; j++ ) + { + tx2 = anims[j]; + + if( !tx2 ) + { + MsgDev( D_ERROR, "Mod_LoadTextures: missing frame %i of %s\n", j, tx->name ); + tx->anim_total = 0; + break; + } + + tx2->anim_total = max * ANIM_CYCLE; + tx2->anim_min = j * ANIM_CYCLE; + tx2->anim_max = (j + 1) * ANIM_CYCLE; + tx2->anim_next = anims[(j + 1) % max]; + if( altmax ) tx2->alternate_anims = altanims[0]; + } + + for( j = 0; j < altmax; j++ ) + { + tx2 = altanims[j]; + + if( !tx2 ) + { + MsgDev( D_ERROR, "Mod_LoadTextures: missing frame %i of %s\n", j, tx->name ); + tx->anim_total = 0; + break; + } + + tx2->anim_total = altmax * ANIM_CYCLE; + tx2->anim_min = j * ANIM_CYCLE; + tx2->anim_max = (j+1) * ANIM_CYCLE; + tx2->anim_next = altanims[(j + 1) % altmax]; + if( max ) tx2->alternate_anims = anims[0]; + } + } +} + +/* +================= +Mod_LoadTexInfo +================= +*/ +static void Mod_LoadTexInfo( dbspmodel_t *bmod ) +{ + mfaceinfo_t *fout, *faceinfo; + int i, j, miptex; + dfaceinfo_t *fin; + mtexinfo_t *out; + dtexinfo_t *in; + + // trying to load faceinfo + faceinfo = fout = Mem_Alloc( loadmodel->mempool, bmod->numfaceinfo * sizeof( *fout )); + fin = bmod->faceinfo; + + for( i = 0; i < bmod->numfaceinfo; i++, fin++, fout++ ) + { + Q_strncpy( fout->landname, fin->landname, sizeof( fout->landname )); + fout->texture_step = fin->texture_step; + fout->max_extent = fin->max_extent; + fout->groupid = fin->groupid; + } + + loadmodel->texinfo = out = Mem_Alloc( loadmodel->mempool, bmod->numtexinfo * sizeof( *out )); + loadmodel->numtexinfo = bmod->numtexinfo; + in = bmod->texinfo; + + for( i = 0; i < bmod->numtexinfo; i++, in++, out++ ) + { + for( j = 0; j < 8; j++ ) + out->vecs[0][j] = in->vecs[0][j]; + + miptex = in->miptex; + if( miptex < 0 || miptex > loadmodel->numtextures ) + { + MsgDev( D_WARN, "Mod_LoadTexInfo: bad miptex number %i in '%s'\n", miptex, loadmodel->name ); + miptex = 0; + } + + out->texture = loadmodel->textures[miptex]; + out->flags = in->flags; + + // make sure what faceinfo is really exist + if( faceinfo != NULL && in->faceinfo != -1 && in->faceinfo < bmod->numfaceinfo ) + out->faceinfo = &faceinfo[in->faceinfo]; + } +} + +/* +================= +Mod_LoadSurfaces +================= +*/ +static void Mod_LoadSurfaces( dbspmodel_t *bmod ) +{ + int test_lightsize = -1; + int next_lightofs = -1; + int prev_lightofs = -1; + int i, j, lightofs; + mextrasurf_t *info; + msurface_t *out; + + loadmodel->surfaces = out = Mem_Alloc( loadmodel->mempool, bmod->numsurfaces * sizeof( msurface_t )); + info = Mem_Alloc( loadmodel->mempool, bmod->numsurfaces * sizeof( mextrasurf_t )); + loadmodel->numsurfaces = bmod->numsurfaces; + + // predict samplecount based on bspversion + if( bmod->version == Q1BSP_VERSION || bmod->version == QBSP2_VERSION ) + bmod->lightmap_samples = 1; + else bmod->lightmap_samples = 3; + + for( i = 0; i < bmod->numsurfaces; i++, out++, info++ ) + { + texture_t *tex; + + // setup crosslinks between two parts of msurface_t + out->info = info; + info->surf = out; + + if( bmod->version == QBSP2_VERSION ) + { + dface32_t *in = &bmod->surfaces32[i]; + + if(( in->firstedge + in->numedges ) > loadmodel->numsurfedges ) + { + MsgDev( D_ERROR, "bad surface %i from %i\n", i, bmod->numsurfaces ); + continue; + } + + out->firstedge = in->firstedge; + out->numedges = in->numedges; + if( in->side ) SetBits( out->flags, SURF_PLANEBACK ); + out->plane = loadmodel->planes + in->planenum; + out->texinfo = loadmodel->texinfo + in->texinfo; + + for( j = 0; j < MAXLIGHTMAPS; j++ ) + out->styles[j] = in->styles[j]; + lightofs = in->lightofs; + } + else + { + dface_t *in = &bmod->surfaces[i]; + + if(( in->firstedge + in->numedges ) > loadmodel->numsurfedges ) + { + MsgDev( D_ERROR, "bad surface %i from %i\n", i, bmod->numsurfaces ); + continue; + } + + out->firstedge = in->firstedge; + out->numedges = in->numedges; + if( in->side ) SetBits( out->flags, SURF_PLANEBACK ); + out->plane = loadmodel->planes + in->planenum; + out->texinfo = loadmodel->texinfo + in->texinfo; + + for( j = 0; j < MAXLIGHTMAPS; j++ ) + out->styles[j] = in->styles[j]; + lightofs = in->lightofs; + } + + tex = out->texinfo->texture; + + if( !Q_strncmp( tex->name, "sky", 3 )) + SetBits( out->flags, SURF_DRAWTILED|SURF_DRAWSKY ); + + if(( tex->name[0] == '*' && Q_stricmp( tex->name, "*default" )) || tex->name[0] == '!' ) + SetBits( out->flags, SURF_DRAWTURB|SURF_DRAWTILED|SURF_NOCULL ); + + if( !FBitSet( host.features, ENGINE_QUAKE_COMPATIBLE )) + { + if( !Q_strncmp( tex->name, "water", 5 ) || !Q_strnicmp( tex->name, "laser", 5 )) + SetBits( out->flags, SURF_DRAWTURB|SURF_DRAWTILED|SURF_NOCULL ); + } + + if( !Q_strncmp( tex->name, "scroll", 6 )) + SetBits( out->flags, SURF_CONVEYOR ); + + // g-cont. added a combined conveyor-transparent + if( !Q_strncmp( tex->name, "{scroll", 7 )) + SetBits( out->flags, SURF_CONVEYOR|SURF_TRANSPARENT ); + + // g-cont this texture comes from decals.wad he-he + // support !reflect for reflected water + if( !Q_strcmp( tex->name, "reflect1" ) || !Q_strncmp( tex->name, "!reflect", 8 )) + { + SetBits( world.flags, FWORLD_HAS_MIRRORS ); + SetBits( out->flags, SURF_REFLECT ); + } + + if( tex->name[0] == '{' ) + SetBits( out->flags, SURF_TRANSPARENT ); + + if( FBitSet( out->texinfo->flags, TEX_SPECIAL )) + SetBits( out->flags, SURF_DRAWTILED ); + + Mod_CalcSurfaceBounds( out ); + Mod_CalcSurfaceExtents( out ); + + // grab the second sample to detect colored lighting + if( test_lightsize > 0 && lightofs != -1 ) + { + if( lightofs > prev_lightofs && lightofs < next_lightofs ) + next_lightofs = lightofs; + } + + // grab the first sample to determine lightmap size + if( lightofs != -1 && test_lightsize == -1 ) + { + int sample_size = Mod_SampleSizeForFace( out ); + int smax = (info->lightextents[0] / sample_size) + 1; + int tmax = (info->lightextents[1] / sample_size) + 1; + int lightstyles = 0; + + test_lightsize = smax * tmax; + // count styles to right compute test_lightsize + for( j = 0; j < MAXLIGHTMAPS && out->styles[j] != 255; j++ ) + lightstyles++; + + test_lightsize *= lightstyles; + prev_lightofs = lightofs; + next_lightofs = 99999999; + } + + if( FBitSet( out->flags, SURF_DRAWTURB )) + GL_SubdivideSurface( out ); // cut up polygon for warps + } + + // now we have enough data to trying determine samplecount per lightmap pixel + if( test_lightsize > 0 && prev_lightofs != -1 && next_lightofs != -1 && next_lightofs != 99999999 ) + { + float samples = (float)(next_lightofs - prev_lightofs) / (float)test_lightsize; + + if( samples != (int)samples ) + { + test_lightsize = (test_lightsize + 3) & ~3; // align datasize and try again + samples = (float)(next_lightofs - prev_lightofs) / (float)test_lightsize; + } + + if( samples == 1 || samples == 3 ) + { + bmod->lightmap_samples = (int)samples; + MsgDev( D_REPORT, "lighting: %s\n", (bmod->lightmap_samples == 1) ? "monochrome" : "colored" ); + bmod->lightmap_samples = Q_max( bmod->lightmap_samples, 1 ); // avoid division by zero + } + else MsgDev( D_WARN, "lighting invalid samplecount: %g, defaulting to %i\n", samples, bmod->lightmap_samples ); + } +} + +/* +================= +Mod_LoadNodes +================= +*/ +static void Mod_LoadNodes( dbspmodel_t *bmod ) +{ + mnode_t *out; + int i, j, p; + + loadmodel->nodes = out = (mnode_t *)Mem_Alloc( loadmodel->mempool, bmod->numnodes * sizeof( *out )); + loadmodel->numnodes = bmod->numnodes; + + for( i = 0; i < loadmodel->numnodes; i++, out++ ) + { + if( bmod->version == QBSP2_VERSION ) + { + dnode32_t *in = &bmod->nodes32[i]; + + for( j = 0; j < 3; j++ ) + { + out->minmaxs[j+0] = in->mins[j]; + out->minmaxs[j+3] = in->maxs[j]; + } + + p = in->planenum; + out->plane = loadmodel->planes + p; + out->firstsurface = in->firstface; + out->numsurfaces = in->numfaces; + + for( j = 0; j < 2; j++ ) + { + p = in->children[j]; + if( p >= 0 ) out->children[j] = loadmodel->nodes + p; + else out->children[j] = (mnode_t *)(loadmodel->leafs + ( -1 - p )); + } + } + else + { + dnode_t *in = &bmod->nodes[i]; + + for( j = 0; j < 3; j++ ) + { + out->minmaxs[j+0] = in->mins[j]; + out->minmaxs[j+3] = in->maxs[j]; + } + + p = in->planenum; + out->plane = loadmodel->planes + p; + out->firstsurface = in->firstface; + out->numsurfaces = in->numfaces; + + for( j = 0; j < 2; j++ ) + { + p = in->children[j]; + if( p >= 0 ) out->children[j] = loadmodel->nodes + p; + else out->children[j] = (mnode_t *)(loadmodel->leafs + ( -1 - p )); + } + } + } + + // sets nodes and leafs + Mod_SetParent( loadmodel->nodes, NULL ); +} + +/* +================= +Mod_LoadLeafs +================= +*/ +static void Mod_LoadLeafs( dbspmodel_t *bmod ) +{ + mleaf_t *out; + int i, j, p; + + loadmodel->leafs = out = (mleaf_t *)Mem_Alloc( loadmodel->mempool, bmod->numleafs * sizeof( *out )); + loadmodel->numleafs = bmod->numleafs; + + if( bmod->isworld ) + { + // get visleafs from the submodel data + world.visclusters = loadmodel->submodels[0].visleafs; + world.visbytes = (world.visclusters + 7) >> 3; + world.visdata = (byte *)Mem_Alloc( loadmodel->mempool, world.visclusters * world.visbytes ); + world.fatbytes = (world.visclusters + 31) >> 3; + + // enable full visibility as default + memset( world.visdata, 0xFF, world.visclusters * world.visbytes ); + } + + for( i = 0; i < bmod->numleafs; i++, out++ ) + { + if( bmod->version == QBSP2_VERSION ) + { + dleaf32_t *in = &bmod->leafs32[i]; + + for( j = 0; j < 3; j++ ) + { + out->minmaxs[j+0] = in->mins[j]; + out->minmaxs[j+3] = in->maxs[j]; + } + + out->contents = in->contents; + p = in->visofs; + + for( j = 0; j < 4; j++ ) + out->ambient_sound_level[j] = in->ambient_level[j]; + + out->firstmarksurface = loadmodel->marksurfaces + in->firstmarksurface; + out->nummarksurfaces = in->nummarksurfaces; + } + else + { + dleaf_t *in = &bmod->leafs[i]; + + for( j = 0; j < 3; j++ ) + { + out->minmaxs[j+0] = in->mins[j]; + out->minmaxs[j+3] = in->maxs[j]; + } + + out->contents = in->contents; + p = in->visofs; + + for( j = 0; j < 4; j++ ) + out->ambient_sound_level[j] = in->ambient_level[j]; + + out->firstmarksurface = loadmodel->marksurfaces + in->firstmarksurface; + out->nummarksurfaces = in->nummarksurfaces; + } + + if( bmod->isworld ) + { + out->cluster = ( i - 1 ); // solid leaf 0 has no visdata + + if( out->cluster >= world.visclusters ) + out->cluster = -1; + + // ignore visofs errors on leaf 0 (solid) + if( p >= 0 && out->cluster >= 0 && loadmodel->visdata ) + { + if( p < bmod->visdatasize ) + { + byte *inrow = loadmodel->visdata + p; + byte *inrowend = loadmodel->visdata + bmod->visdatasize; + byte *outrow = world.visdata + out->cluster * world.visbytes; + byte *outrowend = world.visdata + (out->cluster + 1) * world.visbytes; + + Mod_DecompressVis( inrow, inrowend, outrow, outrowend ); + } + else MsgDev( D_WARN, "Mod_LoadLeafs: invalid visofs for leaf #%i\n", i ); + } + } + else out->cluster = -1; // no visclusters on bmodels + + if( p == -1 ) out->compressed_vis = NULL; + else out->compressed_vis = loadmodel->visdata + p; + + // gl underwater warp + if( out->contents != CONTENTS_EMPTY ) + { + for( j = 0; j < out->nummarksurfaces; j++ ) + { + // underwater surfaces can't have reflection (perfomance) + SetBits( out->firstmarksurface[j]->flags, SURF_UNDERWATER ); + ClearBits( out->firstmarksurface[j]->flags, SURF_REFLECT ); + } + } + } + + if( bmod->isworld && loadmodel->leafs[0].contents != CONTENTS_SOLID ) + Host_Error( "Mod_LoadLeafs: Map %s has leaf 0 is not CONTENTS_SOLID\n", loadmodel->name ); + + // do some final things for world + if( bmod->isworld && Mod_CheckWaterAlphaSupport( bmod )) + SetBits( world.flags, FWORLD_WATERALPHA ); +} + +/* +================= +Mod_LoadClipnodes +================= +*/ +static void Mod_LoadClipnodes( dbspmodel_t *bmod ) +{ + dclipnode32_t *out; + int i; + + bmod->clipnodes_out = out = (dclipnode32_t *)Mem_Alloc( loadmodel->mempool, bmod->numclipnodes * sizeof( *out )); + + if(( bmod->version == QBSP2_VERSION ) || ( bmod->version == HLBSP_VERSION && bmod->numclipnodes >= MAX_MAP_CLIPNODES )) + { + dclipnode32_t *in = bmod->clipnodes32; + + for( i = 0; i < bmod->numclipnodes; i++, out++, in++ ) + { + out->planenum = in->planenum; + out->children[0] = in->children[0]; + out->children[1] = in->children[1]; + } + } + else + { + dclipnode_t *in = bmod->clipnodes; + + for( i = 0; i < bmod->numclipnodes; i++, out++, in++ ) + { + out->planenum = in->planenum; + + out->children[0] = (unsigned short)in->children[0]; + out->children[1] = (unsigned short)in->children[1]; + + // Arguire QBSP 'broken' clipnodes + if( out->children[0] >= bmod->numclipnodes ) + out->children[0] -= 65536; + if( out->children[1] >= bmod->numclipnodes ) + out->children[1] -= 65536; + } + } + + // FIXME: fill loadmodel->clipnodes? + loadmodel->numclipnodes = bmod->numclipnodes; +} + +/* +================= +Mod_LoadVisibility +================= +*/ +static void Mod_LoadVisibility( dbspmodel_t *bmod ) +{ + loadmodel->visdata = Mem_Alloc( loadmodel->mempool, bmod->visdatasize ); + memcpy( loadmodel->visdata, bmod->visdata, bmod->visdatasize ); +} + +/* +================= +Mod_LoadLightVecs +================= +*/ +static void Mod_LoadLightVecs( dbspmodel_t *bmod ) +{ + if( bmod->deluxdatasize != bmod->lightdatasize ) + { + if( bmod->deluxdatasize > 0 ) + MsgDev( D_ERROR, "Mod_LoadLightVecs: has mismatched size (%i should be %i)\n", bmod->deluxdatasize, bmod->lightdatasize ); + else Mod_LoadDeluxemap( bmod ); // old method + return; + } + + bmod->deluxedata_out = Mem_Alloc( loadmodel->mempool, bmod->deluxdatasize ); + memcpy( bmod->deluxedata_out, bmod->deluxdata, bmod->deluxdatasize ); + MsgDev( D_INFO, "Mod_LoadLightVecs: loaded\n" ); +} + +/* +================= +Mod_LoadShadowmap +================= +*/ +static void Mod_LoadShadowmap( dbspmodel_t *bmod ) +{ + if( bmod->shadowdatasize != ( bmod->lightdatasize / 3 )) + { + if( bmod->shadowdatasize > 0 ) + MsgDev( D_ERROR, "Mod_LoadShadowmap: has mismatched size (%i should be %i)\n", bmod->shadowdatasize, bmod->lightdatasize / 3 ); + return; + } + + bmod->shadowdata_out = Mem_Alloc( loadmodel->mempool, bmod->shadowdatasize ); + memcpy( bmod->shadowdata_out, bmod->shadowdata, bmod->shadowdatasize ); + MsgDev( D_INFO, "Mod_LoadShadowmap: loaded\n" ); +} + +/* +================= +Mod_LoadLighting +================= +*/ +static void Mod_LoadLighting( dbspmodel_t *bmod ) +{ + int i, lightofs; + msurface_t *surf; + color24 *out; + byte *in; + + if( !bmod->lightdatasize ) + return; + + switch( bmod->lightmap_samples ) + { + case 1: + if( !Mod_LoadColoredLighting( bmod )) + { + loadmodel->lightdata = out = (color24 *)Mem_Alloc( loadmodel->mempool, bmod->lightdatasize * sizeof( color24 )); + in = bmod->lightdata; + + // expand the white lighting data + for( i = 0; i < bmod->lightdatasize; i++, out++ ) + out->r = out->g = out->b = *in++; + } + break; + case 3: // load colored lighting + loadmodel->lightdata = Mem_Alloc( loadmodel->mempool, bmod->lightdatasize ); + memcpy( loadmodel->lightdata, bmod->lightdata, bmod->lightdatasize ); + SetBits( loadmodel->flags, MODEL_COLORED_LIGHTING ); + break; + default: + Host_Error( "Mod_LoadLighting: bad lightmap sample count %i\n", bmod->lightmap_samples ); + break; + } + + // not supposed to be load ? + if( FBitSet( host.features, ENGINE_LOAD_DELUXEDATA )) + { + Mod_LoadLightVecs( bmod ); + Mod_LoadShadowmap( bmod ); + + if( bmod->isworld && bmod->deluxdatasize ) + SetBits( world.flags, FWORLD_HAS_DELUXEMAP ); + } + + surf = loadmodel->surfaces; + + // setup lightdata pointers + for( i = 0; i < loadmodel->numsurfaces; i++, surf++ ) + { + if( bmod->version == QBSP2_VERSION ) + lightofs = bmod->surfaces32[i].lightofs; + else lightofs = bmod->surfaces[i].lightofs; + + if( loadmodel->lightdata && lightofs != -1 ) + { + int offset = (lightofs / bmod->lightmap_samples); + + // NOTE: we divide offset by three because lighting and deluxemap keep their pointers + // into three-bytes structs and shadowmap just monochrome + surf->samples = loadmodel->lightdata + offset; + + // if deluxemap is present setup it too + if( bmod->deluxedata_out ) + surf->info->deluxemap = bmod->deluxedata_out + offset; + + // will be used by mods + if( bmod->shadowdata_out ) + surf->info->shadowmap = bmod->shadowdata_out + offset; + } + } +} + +/* +================= +Mod_LoadBmodelLumps + +loading and processing bmodel +================= +*/ +qboolean Mod_LoadBmodelLumps( const byte *mod_base, qboolean isworld ) +{ + dheader_t *header = (dheader_t *)mod_base; + dextrahdr_t *extrahdr = (dextrahdr_t *)((byte *)mod_base + sizeof( dheader_t )); + dbspmodel_t *bmod = &srcmodel; + int i; + + // always reset the intermediate struct + memset( bmod, 0, sizeof( dbspmodel_t )); + memset( &loadstat, 0, sizeof( loadstat_t )); + + Q_strncpy( loadstat.name, loadmodel->name, sizeof( loadstat.name )); + +#ifndef SUPPORT_BSP2_FORMAT + if( header->version == QBSP2_VERSION ) + { + MsgDev( D_ERROR, "%s can't be loaded in this build. Please rebuild engine with enabled SUPPORT_BSP2_FORMAT\n", loadmodel->name ); + return false; + } +#endif + switch( header->version ) + { + case Q1BSP_VERSION: + case HLBSP_VERSION: + case QBSP2_VERSION: + break; + default: + MsgDev( D_ERROR, "%s has wrong version number (%i should be %i)\n", loadmodel->name, header->version, HLBSP_VERSION ); + loadstat.numerrors++; + return false; + } + + bmod->version = header->version; // share up global + if( isworld ) world.flags = 0; // clear world settings + bmod->isworld = isworld; + + // loading base lumps + for( i = 0; i < ARRAYSIZE( srclumps ); i++ ) + Mod_LoadLump( mod_base, &srclumps[i], &worldstats[i], isworld ? LUMP_SAVESTATS : 0 ); + + // loading extralumps + for( i = 0; i < ARRAYSIZE( extlumps ); i++ ) + Mod_LoadLump( mod_base, &extlumps[i], &worldstats[ARRAYSIZE( srclumps ) + i], isworld ? LUMP_SAVESTATS : 0 ); + + if( loadstat.numerrors ) + { + MsgDev( D_INFO, "Mod_Load%s: %i error(s), %i warning(s)\n", isworld ? "World" : "Brush", loadstat.numerrors, loadstat.numwarnings ); + return false; // there were errors, we can't load this map + } + else if( loadstat.numwarnings ) + MsgDev( D_INFO, "Mod_Load%s: %i warning(s)\n", isworld ? "World" : "Brush", loadstat.numwarnings ); + + // load into heap + Mod_LoadEntities( bmod ); + Mod_LoadPlanes( bmod ); + Mod_LoadSubmodels( bmod ); + Mod_LoadVertexes( bmod ); + Mod_LoadEdges( bmod ); + Mod_LoadSurfEdges( bmod ); + Mod_LoadTextures( bmod ); + Mod_LoadVisibility( bmod ); + Mod_LoadTexInfo( bmod ); + Mod_LoadSurfaces( bmod ); + Mod_LoadLighting( bmod ); + Mod_LoadMarkSurfaces( bmod ); + Mod_LoadLeafs( bmod ); + Mod_LoadNodes( bmod ); + Mod_LoadClipnodes( bmod ); + + // preform some post-initalization + Mod_MakeHull0 (); + Mod_SetupSubmodels( bmod ); + + if( bmod->wadlist.count > 0 ) + Msg( "totally used %i wads\n", bmod->wadlist.count ); + + return true; +} + +/* +================= +Mod_TestBmodelLumps + +check for possible errors +================= +*/ +qboolean Mod_TestBmodelLumps( const char *name, const byte *mod_base, qboolean silent ) +{ + dheader_t *header = (dheader_t *)mod_base; + int i, flags = LUMP_TESTONLY; + + // always reset the intermediate struct + memset( &loadstat, 0, sizeof( loadstat_t )); + + // store the name to correct show errors and warnings + Q_strncpy( loadstat.name, name, sizeof( loadstat.name )); + if( silent ) SetBits( flags, LUMP_SILENT ); + +#ifndef SUPPORT_BSP2_FORMAT + if( header->version == QBSP2_VERSION ) + { + if( !FBitSet( flags, LUMP_SILENT )) + MsgDev( D_ERROR, "%s can't be loaded in this build. Please rebuild engine with enabled SUPPORT_BSP2_FORMAT\n", name ); + return false; + } +#endif + switch( header->version ) + { + case Q1BSP_VERSION: + case HLBSP_VERSION: + case QBSP2_VERSION: + break; + default: + // don't early out: let me analyze errors + if( !FBitSet( flags, LUMP_SILENT )) + MsgDev( D_ERROR, "%s has wrong version number (%i should be %i)\n", name, header->version, HLBSP_VERSION ); + loadstat.numerrors++; + break; + } + + // loading base lumps + for( i = 0; i < ARRAYSIZE( srclumps ); i++ ) + Mod_LoadLump( mod_base, &srclumps[i], &worldstats[i], flags ); + + // loading extralumps + for( i = 0; i < ARRAYSIZE( extlumps ); i++ ) + Mod_LoadLump( mod_base, &extlumps[i], &worldstats[ARRAYSIZE( srclumps ) + i], flags ); + + if( loadstat.numerrors ) + { + if( !FBitSet( flags, LUMP_SILENT )) + MsgDev( D_INFO, "Mod_LoadWorld: %i error(s), %i warning(s)\n", loadstat.numerrors, loadstat.numwarnings ); + return false; // there were errors, we can't load this map + } + else if( loadstat.numwarnings ) + { + if( !FBitSet( flags, LUMP_SILENT )) + MsgDev( D_INFO, "Mod_LoadWorld: %i warning(s)\n", loadstat.numwarnings ); + } + + return true; +} + +/* +================= +Mod_LoadBrushModel +================= +*/ +void Mod_LoadBrushModel( model_t *mod, const void *buffer, qboolean *loaded ) +{ + if( loaded ) *loaded = false; + + loadmodel->mempool = Mem_AllocPool( va( "^2%s^7", loadmodel->name )); + loadmodel->type = mod_brush; + + // loading all the lumps into heap + if( !Mod_LoadBmodelLumps( buffer, world.loading )) + return; // there were errors + + if( world.loading ) worldmodel = mod; + + if( loaded ) *loaded = true; // all done +} + +/* +================= +Mod_UnloadBrushModel + +Release all uploaded textures +================= +*/ +void Mod_UnloadBrushModel( model_t *mod ) +{ + texture_t *tx; + int i; + + ASSERT( mod != NULL ); + + if( mod->type != mod_brush ) + return; // not a bmodel + + if( mod->name[0] != '*' ) + { + for( i = 0; i < mod->numtextures; i++ ) + { + tx = mod->textures[i]; + if( !tx || tx->gl_texturenum == tr.defaultTexture ) + continue; // free slot + + GL_FreeTexture( tx->gl_texturenum ); // main texture + GL_FreeTexture( tx->fb_texturenum ); // luma texture + } + Mem_FreePool( &mod->mempool ); + } + + memset( mod, 0, sizeof( *mod )); +} + +/* +================== +Mod_CheckLump + +check lump for existing +================== +*/ +int Mod_CheckLump( const char *filename, const int lump, int *lumpsize ) +{ + file_t *f = FS_Open( filename, "rb", true ); + byte buffer[sizeof( dheader_t ) + sizeof( dextrahdr_t )]; + size_t prefetch_size = sizeof( buffer ); + dextrahdr_t *extrahdr; + dheader_t *header; + + if( !f ) return LUMP_LOAD_COULDNT_OPEN; + + if( FS_Read( f, buffer, prefetch_size ) != prefetch_size ) + { + FS_Close( f ); + return LUMP_LOAD_BAD_HEADER; + } + + header = (dheader_t *)buffer; + + if( header->version != HLBSP_VERSION ) + { + FS_Close( f ); + return LUMP_LOAD_BAD_VERSION; + } + + extrahdr = (dextrahdr_t *)((byte *)buffer + sizeof( dheader_t )); + + if( extrahdr->id != IDEXTRAHEADER || extrahdr->version != EXTRA_VERSION ) + { + FS_Close( f ); + return LUMP_LOAD_NO_EXTRADATA; + } + + if( lump < 0 || lump >= EXTRA_LUMPS ) + { + FS_Close( f ); + return LUMP_LOAD_INVALID_NUM; + } + + if( extrahdr->lumps[lump].filelen <= 0 ) + { + FS_Close( f ); + return LUMP_LOAD_NOT_EXIST; + } + + if( lumpsize ) + *lumpsize = extrahdr->lumps[lump].filelen; + + FS_Close( f ); + + return LUMP_LOAD_OK; +} + +/* +================== +Mod_ReadLump + +reading random lump by user request +================== +*/ +int Mod_ReadLump( const char *filename, const int lump, void **lumpdata, int *lumpsize ) +{ + file_t *f = FS_Open( filename, "rb", true ); + byte buffer[sizeof( dheader_t ) + sizeof( dextrahdr_t )]; + size_t prefetch_size = sizeof( buffer ); + dextrahdr_t *extrahdr; + dheader_t *header; + byte *data; + int length; + + if( !f ) return LUMP_LOAD_COULDNT_OPEN; + + if( FS_Read( f, buffer, prefetch_size ) != prefetch_size ) + { + FS_Close( f ); + return LUMP_LOAD_BAD_HEADER; + } + + header = (dheader_t *)buffer; + + if( header->version != HLBSP_VERSION ) + { + FS_Close( f ); + return LUMP_LOAD_BAD_VERSION; + } + + extrahdr = (dextrahdr_t *)((byte *)buffer + sizeof( dheader_t )); + + if( extrahdr->id != IDEXTRAHEADER || extrahdr->version != EXTRA_VERSION ) + { + FS_Close( f ); + return LUMP_LOAD_NO_EXTRADATA; + } + + if( lump < 0 || lump >= EXTRA_LUMPS ) + { + FS_Close( f ); + return LUMP_LOAD_INVALID_NUM; + } + + if( extrahdr->lumps[lump].filelen <= 0 ) + { + FS_Close( f ); + return LUMP_LOAD_NOT_EXIST; + } + + data = malloc( extrahdr->lumps[lump].filelen + 1 ); + length = extrahdr->lumps[lump].filelen; + + if( !data ) + { + FS_Close( f ); + return LUMP_LOAD_MEM_FAILED; + } + + FS_Seek( f, extrahdr->lumps[lump].fileofs, SEEK_SET ); + + if( FS_Read( f, data, length ) != length ) + { + free( data ); + FS_Close( f ); + return LUMP_LOAD_CORRUPTED; + } + + data[length] = 0; // write term + FS_Close( f ); + + if( lumpsize ) + *lumpsize = length; + *lumpdata = data; + + return LUMP_LOAD_OK; +} + +/* +================== +Mod_SaveLump + +writing lump by user request +only empty lumps is allows +================== +*/ +int Mod_SaveLump( const char *filename, const int lump, void *lumpdata, int lumpsize ) +{ + file_t *f = FS_Open( filename, "e+b", true ); + byte buffer[sizeof( dheader_t ) + sizeof( dextrahdr_t )]; + size_t prefetch_size = sizeof( buffer ); + dextrahdr_t *extrahdr; + dheader_t *header; + + if( !f ) return LUMP_SAVE_COULDNT_OPEN; + + if( !lumpdata || lumpsize <= 0 ) + return LUMP_SAVE_NO_DATA; + + if( FS_Read( f, buffer, prefetch_size ) != prefetch_size ) + { + FS_Close( f ); + return LUMP_SAVE_BAD_HEADER; + } + + header = (dheader_t *)buffer; + + if( header->version != HLBSP_VERSION ) + { + FS_Close( f ); + return LUMP_SAVE_BAD_VERSION; + } + + extrahdr = (dextrahdr_t *)((byte *)buffer + sizeof( dheader_t )); + + if( extrahdr->id != IDEXTRAHEADER || extrahdr->version != EXTRA_VERSION ) + { + FS_Close( f ); + return LUMP_SAVE_NO_EXTRADATA; + } + + if( lump < 0 || lump >= EXTRA_LUMPS ) + { + FS_Close( f ); + return LUMP_SAVE_INVALID_NUM; + } + + if( extrahdr->lumps[lump].filelen != 0 ) + { + FS_Close( f ); + return LUMP_SAVE_ALREADY_EXIST; + } + + FS_Seek( f, 0, SEEK_END ); + + // will be saved later + extrahdr->lumps[lump].fileofs = FS_Tell( f ); + extrahdr->lumps[lump].filelen = lumpsize; + + if( FS_Write( f, lumpdata, lumpsize ) != lumpsize ) + { + FS_Close( f ); + return LUMP_SAVE_CORRUPTED; + } + + // update the header + FS_Seek( f, sizeof( dheader_t ), SEEK_SET ); + + if( FS_Write( f, extrahdr, sizeof( dextrahdr_t )) != sizeof( dextrahdr_t )) + { + FS_Close( f ); + return LUMP_SAVE_CORRUPTED; + } + + FS_Close( f ); + return LUMP_SAVE_OK; +} \ No newline at end of file diff --git a/engine/common/mod_local.h b/engine/common/mod_local.h index 73a5dc09..51b999df 100644 --- a/engine/common/mod_local.h +++ b/engine/common/mod_local.h @@ -43,12 +43,14 @@ GNU General Public License for more details. #define LM_SAMPLE_SIZE 16 #define LM_SAMPLE_EXTRASIZE 8 +#define MAX_MAP_WADS 256 // max wads that can be referenced per one map + #define CHECKVISBIT( vis, b ) ((b) >= 0 ? (byte)((vis)[(b) >> 3] & (1 << ((b) & 7))) : (byte)false ) #define SETVISBIT( vis, b )( void ) ((b) >= 0 ? (byte)((vis)[(b) >> 3] |= (1 << ((b) & 7))) : (byte)false ) #define CLEARVISBIT( vis, b )( void ) ((b) >= 0 ? (byte)((vis)[(b) >> 3] &= ~(1 << ((b) & 7))) : (byte)false ) -#define REFPVS_RADIUS 2.0f // radius for rendering -#define FATPVS_RADIUS 8.0f // FatPVS use radius smaller than the FatPHS +#define REFPVS_RADIUS 2.0f // radius for rendering +#define FATPVS_RADIUS 8.0f // FatPVS use radius smaller than the FatPHS #define FATPHS_RADIUS 16.0f // model flags (stored in model_t->flags) @@ -60,45 +62,28 @@ GNU General Public License for more details. #define MODEL_CLIENT BIT( 30 ) // client sprite -typedef struct wadlist_s -{ - char wadnames[256][32]; - int count; -} wadlist_t; - -typedef struct leaflist_s -{ - int count; - int maxcount; - qboolean overflowed; - short *list; - vec3_t mins, maxs; - int topnode; // for overflows where each leaf can't be stored individually -} leaflist_t; +// goes into world.flags +#define FWORLD_SKYSPHERE BIT( 0 ) +#define FWORLD_CUSTOM_SKYBOX BIT( 1 ) +#define FWORLD_WATERALPHA BIT( 2 ) +#define FWORLD_HAS_DELUXEMAP BIT( 3 ) +#define FWORLD_HAS_MIRRORS BIT( 4 ) typedef struct { - int mapversion; // map version (an key-value in worldspawn settings) uint checksum; // current map checksum int load_sequence; // increace each map change - msurface_t **draw_surfaces; // used for sorting translucent surfaces - int max_surfaces; // max surfaces per submodel (for all models) qboolean loading; // true if worldmodel is loading - qboolean sky_sphere; // true when quake sky-sphere is used - qboolean has_mirrors; // one or more brush models contain reflective textures - qboolean custom_skybox; // if sky_sphere is active and custom skybox set - qboolean water_alpha; // allow translucency water - int block_size; // lightmap blocksize - int lightmap_samples; // samples per pixel - color24 *deluxedata; // deluxemap data pointer - byte *shadowdata; // occlusion data pointer + int flags; // misc flags + + // mapstats info char message[2048]; // just for debug char compiler[256]; // map compiler - dclipnode2_t *clipnodes; // temporary 32-bit array to hold clipnodes - int numclipnodes; // may be exceeds 32768 - int numnodes; // worldcount of nodes + // translucent sorted array + msurface_t **draw_surfaces; // used for sorting translucent surfaces + int max_surfaces; // max surfaces per submodel (for all models) // visibility info byte *visdata; // uncompressed visdata @@ -106,15 +91,7 @@ typedef struct size_t fatbytes; // fatpvs size int visclusters; // num visclusters - // world stats - size_t visdatasize; // actual size of the visdata - size_t litdatasize; // actual size of the lightdata - size_t vecdatasize; // actual size of the deluxdata - size_t occdatasize; // actual size of the shadowdata - size_t entdatasize; // actual size of the entity string - size_t texdatasize; // actual size of the textures lump - size_t clipnodesize; // sizeof dclipnode_t struct - + // world bounds vec3_t mins; // real accuracy world bounds vec3_t maxs; vec3_t size; @@ -124,6 +101,7 @@ extern world_static_t world; extern byte *com_studiocache; extern model_t *loadmodel; extern convar_t *mod_studiocache; +extern convar_t *r_wadtextures; // // model.c @@ -132,12 +110,10 @@ void Mod_Init( void ); void Mod_ClearAll( qboolean keep_playermodel ); void Mod_Shutdown( void ); void Mod_ClearUserData( void ); -void Mod_PrintBSPFileSizes( void ); void Mod_GetBounds( int handle, vec3_t mins, vec3_t maxs ); void Mod_GetFrames( int handle, int *numFrames ); void Mod_LoadWorld( const char *name, uint *checksum, qboolean multiplayer ); int Mod_FrameCount( model_t *mod ); -void Mod_FreeUnused( void ); void *Mod_Calloc( int number, size_t size ); void *Mod_CacheCheck( struct cache_user_s *c ); void Mod_LoadCacheFile( const char *path, struct cache_user_s *cu ); @@ -147,19 +123,27 @@ model_t *Mod_FindName( const char *name, qboolean create ); model_t *Mod_LoadModel( model_t *mod, qboolean world ); model_t *Mod_ForName( const char *name, qboolean world ); qboolean Mod_RegisterModel( const char *name, int index ); -mleaf_t *Mod_PointInLeaf( const vec3_t p, mnode_t *node ); +modtype_t Mod_GetType( int handle ); +model_t *Mod_Handle( int handle ); +void Mod_FreeUnused( void ); + +// +// mod_bmodel.c +// +void Mod_LoadBrushModel( model_t *mod, const void *buffer, qboolean *loaded ); +qboolean Mod_TestBmodelLumps( const char *name, const byte *mod_base, qboolean silent ); qboolean Mod_HeadnodeVisible( mnode_t *node, const byte *visbits, int *lastleaf ); -int Mod_BoxLeafnums( const vec3_t mins, const vec3_t maxs, short *list, int listsize, int *lastleaf ); int Mod_FatPVS( const vec3_t org, float radius, byte *visbuffer, int visbytes, qboolean merge, qboolean fullvis ); qboolean Mod_BoxVisible( const vec3_t mins, const vec3_t maxs, const byte *visbits ); int Mod_CheckLump( const char *filename, const int lump, int *lumpsize ); int Mod_ReadLump( const char *filename, const int lump, void **lumpdata, int *lumpsize ); int Mod_SaveLump( const char *filename, const int lump, void *lumpdata, int lumpsize ); +mleaf_t *Mod_PointInLeaf( const vec3_t p, mnode_t *node ); void Mod_AmbientLevels( const vec3_t p, byte *pvolumes ); int Mod_SampleSizeForFace( msurface_t *surf ); byte *Mod_GetPVSForPoint( const vec3_t p ); -modtype_t Mod_GetType( int handle ); -model_t *Mod_Handle( int handle ); +void Mod_UnloadBrushModel( model_t *mod ); +void Mod_PrintWorldStats_f( void ); // // mod_studio.c diff --git a/engine/common/model.c b/engine/common/model.c index 64ea613e..8e117e7e 100644 --- a/engine/common/model.c +++ b/engine/common/model.c @@ -25,22 +25,13 @@ GNU General Public License for more details. #include "client.h" #include "server.h" // LUMP_ error codes -world_static_t world; - -byte *mod_base; -byte *com_studiocache; // cache for submodels static model_t *com_models[MAX_MODELS]; // shared replacement modeltable static model_t cm_models[MAX_MODELS]; static int cm_nummodels = 0; -static byte visdata[MAX_MAP_LEAFS/8]; // intermediate buffer -static int bmodel_version; // global stuff to detect bsp version -char modelname[64]; // short model name (without path and ext) +byte *com_studiocache; // cache for submodels convar_t *mod_studiocache; convar_t *r_wadtextures; -static wadlist_t wadlist; - model_t *loadmodel; -model_t *worldmodel; /* =============================================================================== @@ -49,106 +40,12 @@ model_t *worldmodel; =============================================================================== */ -/* -================ -Mod_ArrayUsage -================ -*/ -int Mod_ArrayUsage( const char *szItem, int items, int maxitems, int itemsize ) -{ - float percentage = maxitems ? (items * 100.0f / maxitems) : 0.0f; - - Msg( "%-12s %7i/%-7i %7i/%-7i (%4.1f%%)", szItem, items, maxitems, items * itemsize, maxitems * itemsize, percentage ); - - if( percentage > 99.9f ) - Msg( "^1SIZE OVERFLOW!!!^7\n" ); - else if( percentage > 95.0f ) - Msg( "^3SIZE DANGER!^7\n" ); - else if( percentage > 80.0f ) - Msg( "^2VERY FULL!^7\n" ); - else Msg( "\n" ); - - return items * itemsize; -} - -/* -================ -Mod_GlobUsage -================ -*/ -int Mod_GlobUsage( const char *szItem, int itemstorage, int maxstorage ) -{ - float percentage = maxstorage ? (itemstorage * 100.0f / maxstorage) : 0.0f; - - Msg( "%-12s [variable] %7i/%-7i (%4.1f%%)", szItem, itemstorage, maxstorage, percentage ); - - if( percentage > 99.9f ) - Msg( "^1SIZE OVERFLOW!!!^7\n" ); - else if( percentage > 95.0f ) - Msg( "^3SIZE DANGER!^7\n" ); - else if( percentage > 80.0f ) - Msg( "^2VERY FULL!^7\n" ); - else Msg( "\n" ); - - return itemstorage; -} - -/* -============= -Mod_PrintBSPFileSizes - -Dumps info about current file -============= -*/ -void Mod_PrintBSPFileSizes_f( void ) -{ - int totalmemory = 0; - model_t *w = worldmodel; - - if( !w || !w->numsubmodels ) - { - Msg( "No map loaded\n" ); - return; - } - - Msg( "\n" ); - Msg( "Object names Objects/Maxobjs Memory / Maxmem Fullness\n" ); - Msg( "------------ --------------- --------------- --------\n" ); - - totalmemory += Mod_ArrayUsage( "models", w->numsubmodels, MAX_MAP_MODELS, sizeof( dmodel_t )); - totalmemory += Mod_ArrayUsage( "planes", w->numplanes, MAX_MAP_PLANES, sizeof( dplane_t )); - totalmemory += Mod_ArrayUsage( "vertexes", w->numvertexes, MAX_MAP_VERTS, sizeof( dvertex_t )); - totalmemory += Mod_ArrayUsage( "nodes", world.numnodes, MAX_MAP_NODES, sizeof( dnode_t )); - totalmemory += Mod_ArrayUsage( "texinfos", w->numtexinfo, MAX_MAP_TEXINFO, sizeof( dtexinfo_t )); - totalmemory += Mod_ArrayUsage( "faces", w->numsurfaces, MAX_MAP_FACES, sizeof( dface_t )); - totalmemory += Mod_ArrayUsage( "clipnodes", w->numclipnodes, MAX_TOTAL_CLIPNODES, world.clipnodesize ); - totalmemory += Mod_ArrayUsage( "leaves", w->numleafs, MAX_MAP_LEAFS, sizeof( dleaf_t )); - totalmemory += Mod_ArrayUsage( "marksurfaces", w->nummarksurfaces, MAX_MAP_MARKSURFACES, sizeof( dmarkface_t )); - totalmemory += Mod_ArrayUsage( "surfedges", w->numsurfedges, MAX_MAP_SURFEDGES, sizeof( dsurfedge_t )); - totalmemory += Mod_ArrayUsage( "edges", w->numedges, MAX_MAP_EDGES, sizeof( dedge_t )); - - totalmemory += Mod_GlobUsage( "texdata", world.texdatasize, MAX_MAP_MIPTEX ); - totalmemory += Mod_GlobUsage( "lightdata", world.litdatasize, MAX_MAP_LIGHTING ); - totalmemory += Mod_GlobUsage( "deluxemap", world.vecdatasize, MAX_MAP_LIGHTING ); - totalmemory += Mod_GlobUsage( "shadowmap", world.occdatasize, MAX_MAP_LIGHTING / 3 ); - totalmemory += Mod_GlobUsage( "visdata", world.visdatasize, MAX_MAP_VISIBILITY ); - totalmemory += Mod_GlobUsage( "entdata", world.entdatasize, MAX_MAP_ENTSTRING ); - - Msg( "=== Total BSP file data space used: %s ===\n", Q_memprint( totalmemory )); - Msg( "World size ( %g %g %g ) units\n", world.size[0], world.size[1], world.size[2] ); - Msg( "Supports transparency world water: %s\n", world.water_alpha ? "Yes" : "No" ); - Msg( "Lighting: %s\n", FBitSet( w->flags, MODEL_COLORED_LIGHTING ) ? "colored" : "monochrome" ); - Msg( "original name: ^1%s\n", worldmodel->name ); - Msg( "internal name: %s\n", (world.message[0]) ? va( "^2%s", world.message ) : "none" ); - Msg( "map compiler: %s\n", (world.compiler[0]) ? va( "^3%s", world.compiler ) : "unknown" ); -} - /* ================ Mod_Modellist_f ================ */ -void Mod_Modellist_f( void ) +static void Mod_Modellist_f( void ) { int i, nummodels; model_t *mod; @@ -170,446 +67,6 @@ void Mod_Modellist_f( void ) Msg( "\n" ); } -/* -=================== -Mod_DecompressVis -=================== -*/ -static void Mod_DecompressVis( const byte *in, const byte *inend, byte *out, byte *outend ) -{ - byte *outstart = out; - int c; - - while( out < outend ) - { - if( in == inend ) - { - MsgDev( D_WARN, "Mod_DecompressVis: input underrun (decompressed %i of %i output bytes)\n", - (int)(out - outstart), (int)(outend - outstart)); - return; - } - - c = *in++; - - if( c ) - { - *out++ = c; - } - else - { - if( in == inend ) - { - MsgDev( D_NOTE, "Mod_DecompressVis: input underrun (during zero-run) (decompressed %i of %i output bytes)\n", - (int)(out - outstart), (int)(outend - outstart)); - return; - } - - for( c = *in++; c > 0; c-- ) - { - if( out == outend ) - { - MsgDev( D_NOTE, "Mod_DecompressVis: output overrun (decompressed %i of %i output bytes)\n", - (int)(out - outstart), (int)(outend - outstart)); - return; - } - *out++ = 0; - } - } - } -} - -/* -================== -Mod_PointInLeaf - -================== -*/ -mleaf_t *Mod_PointInLeaf( const vec3_t p, mnode_t *node ) -{ - ASSERT( node != NULL ); - - while( 1 ) - { - if( node->contents < 0 ) - return (mleaf_t *)node; - node = node->children[PlaneDiff( p, node->plane ) <= 0]; - } - - // never reached - return NULL; -} - -/* -================== -Mod_GetPVSForPoint - -Returns PVS data for a given point -NOTE: can return NULL -================== -*/ -byte *Mod_GetPVSForPoint( const vec3_t p ) -{ - mnode_t *node; - mleaf_t *leaf = NULL; - - ASSERT( worldmodel != NULL ); - - node = worldmodel->nodes; - - while( 1 ) - { - if( node->contents < 0 ) - { - leaf = (mleaf_t *)node; - break; // we found a leaf - } - node = node->children[PlaneDiff( p, node->plane ) <= 0]; - } - - if( leaf && leaf->cluster >= 0 ) - return world.visdata + leaf->cluster * world.visbytes; - return NULL; -} - -/* -================== -Mod_FatPVS_RecursiveBSPNode - -================== -*/ -static void Mod_FatPVS_RecursiveBSPNode( const vec3_t org, float radius, byte *visbuffer, int visbytes, mnode_t *node ) -{ - int i; - - while( node->contents >= 0 ) - { - float d = PlaneDiff( org, node->plane ); - - if( d > radius ) - node = node->children[0]; - else if( d < -radius ) - node = node->children[1]; - else - { - // go down both sides - Mod_FatPVS_RecursiveBSPNode( org, radius, visbuffer, visbytes, node->children[0] ); - node = node->children[1]; - } - } - - // if this leaf is in a cluster, accumulate the vis bits - if(((mleaf_t *)node)->cluster >= 0 ) - { - byte *vis = world.visdata + ((mleaf_t *)node)->cluster * world.visbytes; - - for( i = 0; i < visbytes; i++ ) - visbuffer[i] |= vis[i]; - } -} - -/* -================== -Mod_FatPVS_RecursiveBSPNode - -Calculates a PVS that is the inclusive or of all leafs -within radius pixels of the given point. -================== -*/ -int Mod_FatPVS( const vec3_t org, float radius, byte *visbuffer, int visbytes, qboolean merge, qboolean fullvis ) -{ - mleaf_t *leaf = Mod_PointInLeaf( org, worldmodel->nodes ); - int bytes = world.visbytes; - - bytes = Q_min( bytes, visbytes ); - - // enable full visibility for some reasons - if( fullvis || !world.visclusters || !leaf || leaf->cluster < 0 ) - { - memset( visbuffer, 0xFF, bytes ); - return bytes; - } - - if( !merge ) memset( visbuffer, 0x00, bytes ); - - Mod_FatPVS_RecursiveBSPNode( org, radius, visbuffer, bytes, worldmodel->nodes ); - - return bytes; -} - -/* -====================================================================== - -LEAF LISTING - -====================================================================== -*/ -static void Mod_BoxLeafnums_r( leaflist_t *ll, mnode_t *node ) -{ - int sides; - - while( 1 ) - { - if( node->contents == CONTENTS_SOLID ) - return; - - if( node->contents < 0 ) - { - mleaf_t *leaf = (mleaf_t *)node; - - // it's a leaf! - if( ll->count >= ll->maxcount ) - { - ll->overflowed = true; - return; - } - - ll->list[ll->count++] = leaf->cluster; - return; - } - - sides = BOX_ON_PLANE_SIDE( ll->mins, ll->maxs, node->plane ); - - if( sides == 1 ) - { - node = node->children[0]; - } - else if( sides == 2 ) - { - node = node->children[1]; - } - else - { - // go down both - if( ll->topnode == -1 ) - ll->topnode = node - worldmodel->nodes; - Mod_BoxLeafnums_r( ll, node->children[0] ); - node = node->children[1]; - } - } -} - -/* -================== -Mod_BoxLeafnums -================== -*/ -int Mod_BoxLeafnums( const vec3_t mins, const vec3_t maxs, short *list, int listsize, int *topnode ) -{ - leaflist_t ll; - - if( !worldmodel ) return 0; - - VectorCopy( mins, ll.mins ); - VectorCopy( maxs, ll.maxs ); - - ll.maxcount = listsize; - ll.overflowed = false; - ll.topnode = -1; - ll.list = list; - ll.count = 0; - - Mod_BoxLeafnums_r( &ll, worldmodel->nodes ); - - if( topnode ) *topnode = ll.topnode; - return ll.count; -} - -/* -============= -Mod_BoxVisible - -Returns true if any leaf in boxspace -is potentially visible -============= -*/ -qboolean Mod_BoxVisible( const vec3_t mins, const vec3_t maxs, const byte *visbits ) -{ - short leafList[MAX_BOX_LEAFS]; - int i, count; - - if( !visbits || !mins || !maxs ) - return true; - - count = Mod_BoxLeafnums( mins, maxs, leafList, MAX_BOX_LEAFS, NULL ); - - for( i = 0; i < count; i++ ) - { - if( CHECKVISBIT( visbits, leafList[i] )) - return true; - } - return false; -} - -/* -============= -Mod_HeadnodeVisible -============= -*/ -qboolean Mod_HeadnodeVisible( mnode_t *node, const byte *visbits, int *lastleaf ) -{ - if( !node || node->contents == CONTENTS_SOLID ) - return false; - - if( node->contents < 0 ) - { - if( !CHECKVISBIT( visbits, ((mleaf_t *)node)->cluster )) - return false; - - if( lastleaf ) - *lastleaf = ((mleaf_t *)node)->cluster; - return true; - } - - if( Mod_HeadnodeVisible( node->children[0], visbits, lastleaf )) - return true; - - if( Mod_HeadnodeVisible( node->children[1], visbits, lastleaf )) - return true; - - return false; -} - -/* -================== -Mod_AmbientLevels - -grab the ambient sound levels for current point -================== -*/ -void Mod_AmbientLevels( const vec3_t p, byte *pvolumes ) -{ - mleaf_t *leaf; - - if( !worldmodel || !p || !pvolumes ) - return; - - leaf = Mod_PointInLeaf( p, worldmodel->nodes ); - *(int *)pvolumes = *(int *)leaf->ambient_sound_level; -} - -/* -================== -Mod_SampleSizeForFace - -return the current lightmap resolution per face -================== -*/ -int Mod_SampleSizeForFace( msurface_t *surf ) -{ - if( !surf || !surf->texinfo ) - return LM_SAMPLE_SIZE; - - // world luxels has more priority - if( FBitSet( surf->texinfo->flags, TEX_WORLD_LUXELS )) - return 1; - - if( FBitSet( surf->texinfo->flags, TEX_EXTRA_LIGHTMAP )) - return LM_SAMPLE_EXTRASIZE; - - if( surf->texinfo->faceinfo ) - return surf->texinfo->faceinfo->texture_step; - - return LM_SAMPLE_SIZE; -} - -static void MakeAxial( vec3_t normal ) -{ - int i, type; - - for( type = 0; type < 3; type++ ) - { - if( fabs( normal[type] ) > 0.9999f ) - break; - } - - // make positive and pure axial - for( i = 0; i < 3 && type != 3; i++ ) - { - if( i == type ) - normal[i] = 1.0f; - else normal[i] = 0.0f; - } -} - -/* -================== -Mod_SampleSizeForFace - -return the current lightmap resolution per face -================== -*/ -static void Mod_LightMatrixFromTexMatrix( const mtexinfo_t *tx, float lmvecs[2][4] ) -{ - float lmscale = LM_SAMPLE_SIZE; - int i, j; - - if( tx->faceinfo ) - lmscale = tx->faceinfo->texture_step; - - // copy texmatrix into lightmap matrix fisrt - for( i = 0; i < 2; i++ ) - { - for( j = 0; j < 4; j++ ) - { - lmvecs[i][j] = tx->vecs[i][j]; - } - } - - if( !FBitSet( tx->flags, TEX_WORLD_LUXELS )) - return; // just use texmatrix - - VectorNormalize( lmvecs[0] ); - VectorNormalize( lmvecs[1] ); - - if( FBitSet( tx->flags, TEX_AXIAL_LUXELS )) - { - MakeAxial( lmvecs[0] ); - MakeAxial( lmvecs[1] ); - } - - // put the lighting origin at center the poly - VectorScale( lmvecs[0], (1.0 / lmscale), lmvecs[0] ); - VectorScale( lmvecs[1], -(1.0 / lmscale), lmvecs[1] ); - - lmvecs[0][3] = lmscale * 0.5; - lmvecs[1][3] = -lmscale * 0.5; -} - -/* -================== -Mod_CheckWaterAlphaSupport - -converted maps potential may don't -support water transparency -================== -*/ -static qboolean Mod_CheckWaterAlphaSupport( void ) -{ - mleaf_t *leaf; - int i, j; - const byte *pvs; - - if( world.visdatasize <= 0 ) return true; - - // check all liquid leafs to see if they can see into empty leafs, if any - // can we can assume this map supports r_wateralpha - for( i = 0, leaf = loadmodel->leafs; i < loadmodel->numleafs; i++, leaf++ ) - { - if(( leaf->contents == CONTENTS_WATER || leaf->contents == CONTENTS_SLIME ) && leaf->cluster >= 0 ) - { - pvs = world.visdata + leaf->cluster * world.visbytes; - - for( j = 0; j < loadmodel->numleafs; j++ ) - { - if( CHECKVISBIT( pvs, loadmodel->leafs[j].cluster ) && loadmodel->leafs[j].contents == CONTENTS_EMPTY ) - return true; - } - } - } - - return false; -} - /* ================ Mod_FreeUserData @@ -677,14 +134,13 @@ static void Mod_FreeModel( model_t *mod ) =============================================================================== */ - void Mod_Init( void ) { com_studiocache = Mem_AllocPool( "Studio Cache" ); mod_studiocache = Cvar_Get( "r_studiocache", "1", FCVAR_ARCHIVE, "enables studio cache for speedup tracing hitboxes" ); r_wadtextures = Cvar_Get( "r_wadtextures", "0", 0, "completely ignore textures in the bsp-file if enabled" ); - Cmd_AddCommand( "mapstats", Mod_PrintBSPFileSizes_f, "show stats for currently loaded map" ); + Cmd_AddCommand( "mapstats", Mod_PrintWorldStats_f, "show stats for currently loaded map" ); Cmd_AddCommand( "modellist", Mod_Modellist_f, "display loaded models list" ); Mod_ResetStudioAPI (); @@ -727,2008 +183,10 @@ void Mod_Shutdown( void ) /* =============================================================================== - MAP LOADING + MODELS MANAGEMENT =============================================================================== */ -/* -================= -Mod_LoadSubmodels -================= -*/ -static void Mod_LoadSubmodels( const dlump_t *l ) -{ - dmodel_t *in; - dmodel_t *out; - int i, j, count; - - in = (void *)(mod_base + l->fileofs); - if( l->filelen % sizeof( *in )) Host_Error( "Mod_LoadBModel: funny lump size\n" ); - count = l->filelen / sizeof( *in ); - - if( count < 1 ) Host_Error( "Map %s without models\n", loadmodel->name ); - if( count > MAX_MAP_MODELS ) Host_Error( "Map %s has too many models\n", loadmodel->name ); - - // allocate extradata - out = Mem_Alloc( loadmodel->mempool, count * sizeof( *out )); - loadmodel->submodels = out; - loadmodel->numsubmodels = count; - if( world.loading ) world.max_surfaces = 0; - - for( i = 0; i < count; i++, in++, out++ ) - { - for( j = 0; j < 3; j++ ) - { - // spread the mins / maxs by a unit - out->mins[j] = in->mins[j] - 1.0f; - out->maxs[j] = in->maxs[j] + 1.0f; - out->origin[j] = in->origin[j]; // new P2 compillers fills the origin field - } - - for( j = 0; j < MAX_MAP_HULLS; j++ ) - out->headnode[j] = in->headnode[j]; - - out->visleafs = in->visleafs; - out->firstface = in->firstface; - out->numfaces = in->numfaces; - - if( i == 0 || !world.loading ) - continue; // skip the world - - world.max_surfaces = Q_max( world.max_surfaces, out->numfaces ); - } - - if( world.loading ) - world.draw_surfaces = Mem_Alloc( loadmodel->mempool, world.max_surfaces * sizeof( msurface_t* )); -} - -/* -================= -Mod_LoadTextures -================= -*/ -static void Mod_LoadTextures( const dlump_t *l ) -{ - dmiptexlump_t *in; - texture_t *tx, *tx2; - texture_t *anims[10]; - texture_t *altanims[10]; - int num, max, altmax; - qboolean custom_palette; - char texname[64]; - imgfilter_t *filter; - mip_t *mt; - int i, j; - - if( world.loading ) - { - // release old sky layers first - GL_FreeTexture( tr.solidskyTexture ); - GL_FreeTexture( tr.alphaskyTexture ); - tr.solidskyTexture = tr.alphaskyTexture = 0; - world.texdatasize = l->filelen; - world.custom_skybox = false; - world.has_mirrors = false; - world.sky_sphere = false; - } - - if( !l->filelen ) - { - // no textures - loadmodel->textures = NULL; - return; - } - - in = (void *)(mod_base + l->fileofs); - - loadmodel->numtextures = in->nummiptex; - loadmodel->textures = (texture_t **)Mem_Alloc( loadmodel->mempool, loadmodel->numtextures * sizeof( texture_t* )); - - for( i = 0; i < loadmodel->numtextures; i++ ) - { - if( in->dataofs[i] == -1 ) - { - // create default texture (some mods requires this) - tx = Mem_Alloc( loadmodel->mempool, sizeof( *tx )); - loadmodel->textures[i] = tx; - - Q_strncpy( tx->name, "*default", sizeof( tx->name )); - tx->gl_texturenum = tr.defaultTexture; - tx->width = tx->height = 16; - continue; // missed - } - - mt = (mip_t *)((byte *)in + in->dataofs[i] ); - - if( !mt->name[0] ) - { - MsgDev( D_WARN, "unnamed texture in %s\n", loadmodel->name ); - Q_snprintf( mt->name, sizeof( mt->name ), "miptex_%i", i ); - } - - tx = Mem_Alloc( loadmodel->mempool, sizeof( *tx )); - loadmodel->textures[i] = tx; - - // convert to lowercase - Q_strnlwr( mt->name, mt->name, sizeof( mt->name )); - Q_strncpy( tx->name, mt->name, sizeof( tx->name )); - filter = R_FindTexFilter( tx->name ); // grab texture filter - custom_palette = false; - - tx->width = mt->width; - tx->height = mt->height; - - if( mt->offsets[0] > 0 ) - { - int size = (int)sizeof( mip_t ) + ((mt->width * mt->height * 85)>>6); - int next_dataofs, remaining; - - // compute next dataofset to determine allocated miptex sapce - for( j = i + 1; j < loadmodel->numtextures; j++ ) - { - next_dataofs = in->dataofs[j]; - if( next_dataofs != -1 ) break; - } - - if( j == loadmodel->numtextures ) - next_dataofs = l->filelen; - - // NOTE: imagelib detect miptex version by size - // 770 additional bytes is indicated custom palette - remaining = next_dataofs - (in->dataofs[i] + size); - if( remaining >= 770 ) custom_palette = true; - } - - // check for multi-layered sky texture - if( world.loading && !Q_strncmp( mt->name, "sky", 3 ) && mt->width == 256 && mt->height == 128 ) - { - R_InitSky( mt, tx, custom_palette ); // load quake sky - - if( tr.solidskyTexture && tr.alphaskyTexture ) - world.sky_sphere = true; - } - else - { - // texture loading order: - // 1. from wad - // 2. internal from map - - // trying wad texture (force while r_wadtextures is 1) - if( r_wadtextures->value || mt->offsets[0] <= 0 ) - { - Q_snprintf( texname, sizeof( texname ), "%s.mip", mt->name ); - - // check wads in reverse order - for( j = wadlist.count - 1; j >= 0; j-- ) - { - char *texpath = va( "%s.wad/%s", wadlist.wadnames[j], texname ); - - if( FS_FileExists( texpath, false )) - { - tx->gl_texturenum = GL_LoadTexture( texpath, NULL, 0, 0, filter ); - break; - } - } - } - - // wad failed, so use internal texture (if present) - if( mt->offsets[0] > 0 && !tx->gl_texturenum ) - { - // NOTE: imagelib detect miptex version by size - // 770 additional bytes is indicated custom palette - int size = (int)sizeof( mip_t ) + ((mt->width * mt->height * 85)>>6); - - if( custom_palette ) size += sizeof( short ) + 768; - Q_snprintf( texname, sizeof( texname ), "#%s.mip", mt->name ); - tx->gl_texturenum = GL_LoadTexture( texname, (byte *)mt, size, 0, filter ); - } - } - - // set the emo-texture for missed - if( !tx->gl_texturenum ) tx->gl_texturenum = tr.defaultTexture; - - // check for luma texture - if( FBitSet( R_GetTexture( tx->gl_texturenum )->flags, TF_HAS_LUMA )) - { - Q_snprintf( texname, sizeof( texname ), "#%s_luma.mip", mt->name ); - - if( mt->offsets[0] > 0 ) - { - // NOTE: imagelib detect miptex version by size - // 770 additional bytes is indicated custom palette - int size = (int)sizeof( mip_t ) + ((mt->width * mt->height * 85)>>6); - - if( custom_palette ) size += sizeof( short ) + 768; - tx->fb_texturenum = GL_LoadTexture( texname, (byte *)mt, size, TF_MAKELUMA, NULL ); - } - else - { - size_t srcSize = 0; - byte *src = NULL; - - // NOTE: we can't loading it from wad as normal because _luma texture doesn't exist - // and not be loaded. But original texture is already loaded and can't be modified - // So load original texture manually and convert it to luma - - // check wads in reverse order - for( j = wadlist.count - 1; j >= 0; j-- ) - { - char *texpath = va( "%s.wad/%s.mip", wadlist.wadnames[j], tx->name ); - - if( FS_FileExists( texpath, false )) - { - src = FS_LoadFile( texpath, &srcSize, false ); - break; - } - } - - // okay, loading it from wad or hi-res version - tx->fb_texturenum = GL_LoadTexture( texname, src, srcSize, TF_MAKELUMA, NULL ); - if( src ) Mem_Free( src ); - } - } - } - - // sequence the animations and detail textures - for( i = 0; i < loadmodel->numtextures; i++ ) - { - tx = loadmodel->textures[i]; - - if( !tx || ( tx->name[0] != '-' && tx->name[0] != '+' )) - continue; - - if( tx->anim_next ) - continue; // already sequenced - - // find the number of frames in the animation - memset( anims, 0, sizeof( anims )); - memset( altanims, 0, sizeof( altanims )); - - max = tx->name[1]; - altmax = 0; - - if( max >= '0' && max <= '9' ) - { - max -= '0'; - altmax = 0; - anims[max] = tx; - max++; - } - else if( max >= 'a' && max <= 'j' ) - { - altmax = max - 'a'; - max = 0; - altanims[altmax] = tx; - altmax++; - } - else MsgDev( D_ERROR, "Mod_LoadTextures: bad animating texture %s\n", tx->name ); - - for( j = i + 1; j < loadmodel->numtextures; j++ ) - { - tx2 = loadmodel->textures[j]; - - if( !tx2 || ( tx2->name[0] != '-' && tx2->name[0] != '+' )) - continue; - - if( Q_strcmp( tx2->name + 2, tx->name + 2 )) - continue; - - num = tx2->name[1]; - - if( num >= '0' && num <= '9' ) - { - num -= '0'; - anims[num] = tx2; - if( num + 1 > max ) - max = num + 1; - } - else if( num >= 'a' && num <= 'j' ) - { - num = num - 'a'; - altanims[num] = tx2; - if( num + 1 > altmax ) - altmax = num + 1; - } - else MsgDev( D_ERROR, "Mod_LoadTextures: bad animating texture %s\n", tx->name ); - } - - // link them all together - for( j = 0; j < max; j++ ) - { - tx2 = anims[j]; - - if( !tx2 ) - { - MsgDev( D_ERROR, "Mod_LoadTextures: missing frame %i of %s\n", j, tx->name ); - tx->anim_total = 0; - break; - } - - tx2->anim_total = max * ANIM_CYCLE; - tx2->anim_min = j * ANIM_CYCLE; - tx2->anim_max = (j + 1) * ANIM_CYCLE; - tx2->anim_next = anims[(j + 1) % max]; - if( altmax ) tx2->alternate_anims = altanims[0]; - } - - for( j = 0; j < altmax; j++ ) - { - tx2 = altanims[j]; - - if( !tx2 ) - { - MsgDev( D_ERROR, "Mod_LoadTextures: missing frame %i of %s\n", j, tx->name ); - tx->anim_total = 0; - break; - } - - tx2->anim_total = altmax * ANIM_CYCLE; - tx2->anim_min = j * ANIM_CYCLE; - tx2->anim_max = (j+1) * ANIM_CYCLE; - tx2->anim_next = altanims[(j + 1) % altmax]; - if( max ) tx2->alternate_anims = anims[0]; - } - } -} - -/* -================= -Mod_LoadTexInfo -================= -*/ -static mfaceinfo_t *Mod_LoadFaceInfo( const dlump_t *l, int *numfaceinfo ) -{ - dfaceinfo_t *in; - mfaceinfo_t *out, *faceinfo; - int i, count; - - in = (void *)(mod_base + l->fileofs); - if( l->filelen % sizeof( *in )) - Host_Error( "Mod_LoadFaceInfo: funny lump size in %s\n", loadmodel->name ); - - count = l->filelen / sizeof( *in ); - faceinfo = out = Mem_Alloc( loadmodel->mempool, count * sizeof( *out )); - - for( i = 0; i < count; i++, in++, out++ ) - { - Q_strncpy( out->landname, in->landname, sizeof( out->landname )); - out->texture_step = in->texture_step; - out->max_extent = in->max_extent; - out->groupid = in->groupid; - } - - *numfaceinfo = count; - - return faceinfo; -} - -/* -================= -Mod_LoadTexInfo -================= -*/ -static void Mod_LoadTexInfo( const dlump_t *l, dextrahdr_t *extrahdr ) -{ - dtexinfo_t *in; - mtexinfo_t *out; - int miptex; - mfaceinfo_t *fi = NULL; - int fi_count; - int i, j, count; - - if( extrahdr != NULL ) - fi = Mod_LoadFaceInfo( &extrahdr->lumps[LUMP_FACEINFO], &fi_count ); - - in = (void *)(mod_base + l->fileofs); - if( l->filelen % sizeof( *in )) - Host_Error( "Mod_LoadTexInfo: funny lump size in %s\n", loadmodel->name ); - - count = l->filelen / sizeof( *in ); - out = Mem_Alloc( loadmodel->mempool, count * sizeof( *out )); - - loadmodel->texinfo = out; - loadmodel->numtexinfo = count; - - for( i = 0; i < count; i++, in++, out++ ) - { - for( j = 0; j < 8; j++ ) - out->vecs[0][j] = in->vecs[0][j]; - - miptex = in->miptex; - if( miptex < 0 || miptex > loadmodel->numtextures ) - Host_Error( "Mod_LoadTexInfo: bad miptex number %i in '%s'\n", miptex, loadmodel->name ); - - out->texture = loadmodel->textures[miptex]; - out->flags = in->flags; - - // make sure what faceinfo is really exist - if( extrahdr != NULL && in->faceinfo != -1 && in->faceinfo < fi_count ) - out->faceinfo = &fi[in->faceinfo]; - } -} - -/* -================= -Mod_LoadDeluxemap -================= -*/ -static void Mod_LoadDeluxemap( void ) -{ - char path[64]; - int iCompare; - byte *in; - - if( !world.loading ) return; // only world can have deluxedata - - if( !( host.features & ENGINE_LOAD_DELUXEDATA )) - { - world.deluxedata = NULL; - world.vecdatasize = 0; - return; - } - - Q_snprintf( path, sizeof( path ), "maps/%s.dlit", modelname ); - - // make sure what deluxemap is actual - if( !COM_CompareFileTime( path, loadmodel->name, &iCompare )) - { - world.deluxedata = NULL; - world.vecdatasize = 0; - return; - } - - if( iCompare < 0 ) // this may happens if level-designer used -onlyents key for hlcsg - MsgDev( D_WARN, "Mod_LoadDeluxemap: %s probably is out of date\n", path ); - - in = FS_LoadFile( path, &world.vecdatasize, false ); - - ASSERT( in != NULL ); - - if( *(uint *)in != IDDELUXEMAPHEADER || *((uint *)in + 1) != DELUXEMAP_VERSION ) - { - MsgDev( D_ERROR, "Mod_LoadDeluxemap: %s is not a deluxemap file\n", path ); - world.deluxedata = NULL; - world.vecdatasize = 0; - Mem_Free( in ); - return; - } - - // skip header bytes - world.vecdatasize -= 8; - - if( world.vecdatasize != world.litdatasize ) - { - MsgDev( D_ERROR, "Mod_LoadDeluxemap: %s has mismatched size (%i should be %i)\n", path, world.vecdatasize, world.litdatasize ); - world.deluxedata = NULL; - world.vecdatasize = 0; - Mem_Free( in ); - return; - } - - MsgDev( D_INFO, "Mod_LoadDeluxemap: %s loaded\n", path ); - world.deluxedata = Mem_Alloc( loadmodel->mempool, world.vecdatasize ); - memcpy( world.deluxedata, in + 8, world.vecdatasize ); - Mem_Free( in ); -} - -/* -================= -Mod_LoadLightVecs -================= -*/ -static void Mod_LoadLightVecs( const dlump_t *l ) -{ - byte *in; - - in = (void *)(mod_base + l->fileofs); - world.vecdatasize = l->filelen; - - if( world.vecdatasize != world.litdatasize ) - { - if( world.vecdatasize > 0 ) - MsgDev( D_ERROR, "Mod_LoadLightVecs: has mismatched size (%i should be %i)\n", world.vecdatasize, world.litdatasize ); - world.deluxedata = NULL; - world.vecdatasize = 0; - return; - } - - world.deluxedata = Mem_Alloc( loadmodel->mempool, world.vecdatasize ); - memcpy( world.deluxedata, in, world.vecdatasize ); - MsgDev( D_INFO, "Mod_LoadLightVecs: loaded\n" ); -} - -/* -================= -Mod_LoadShadowmap -================= -*/ -static void Mod_LoadShadowmap( const dlump_t *l ) -{ - byte *in; - - in = (void *)(mod_base + l->fileofs); - world.occdatasize = l->filelen; - - if( world.occdatasize != ( world.litdatasize / 3 )) - { - if( world.occdatasize > 0 ) - MsgDev( D_ERROR, "Mod_LoadShadowmap: has mismatched size (%i should be %i)\n", world.occdatasize, world.litdatasize / 3 ); - world.shadowdata = NULL; - world.occdatasize = 0; - return; - } - - world.shadowdata = Mem_Alloc( loadmodel->mempool, world.occdatasize ); - memcpy( world.shadowdata, in, world.occdatasize ); - MsgDev( D_INFO, "Mod_LoadShadowmap: loaded\n" ); -} - -/* -================= -Mod_LoadColoredLighting -================= -*/ -static qboolean Mod_LoadColoredLighting( void ) -{ - char path[64]; - int iCompare; - size_t litdatasize; - byte *in; - - Q_snprintf( path, sizeof( path ), "maps/%s.lit", modelname ); - - // make sure what deluxemap is actual - if( !COM_CompareFileTime( path, loadmodel->name, &iCompare )) - return false; - - if( iCompare < 0 ) // this may happens if level-designer used -onlyents key for hlcsg - MsgDev( D_WARN, "Mod_LoadColoredLighting: %s probably is out of date\n", path ); - - in = FS_LoadFile( path, &litdatasize, false ); - - ASSERT( in != NULL ); - - if( *(uint *)in != IDDELUXEMAPHEADER || *((uint *)in + 1) != DELUXEMAP_VERSION ) - { - MsgDev( D_ERROR, "Mod_LoadColoredLighting: %s is not a lightmap file\n", path ); - Mem_Free( in ); - return false; - } - - // skip header bytes - litdatasize -= 8; - - MsgDev( D_INFO, "Mod_LoadColoredLighting: %s loaded\n", path ); - loadmodel->lightdata = Mem_Alloc( loadmodel->mempool, litdatasize ); - memcpy( loadmodel->lightdata, in + 8, litdatasize ); - if( world.loading ) world.litdatasize = litdatasize; - SetBits( loadmodel->flags, MODEL_COLORED_LIGHTING ); - Mem_Free( in ); - - return true; -} - -/* -================= -Mod_LoadLighting -================= -*/ -static void Mod_LoadLighting( const dlump_t *l, const dlump_t *faces, dextrahdr_t *extrahdr ) -{ - int i, count; - dface_t *in16; - dface2_t *in32; - msurface_t *surf; - byte d, *in; - int lightofs; - color24 *out; - - if( !l->filelen ) - { - if( world.loading ) - { - MsgDev( D_WARN, "map ^2%s^7 has no lighting\n", loadmodel->name ); - loadmodel->lightdata = NULL; - world.deluxedata = NULL; - world.vecdatasize = 0; - world.litdatasize = 0; - } - return; - } - in = (void *)(mod_base + l->fileofs); - if( world.loading ) world.litdatasize = l->filelen; - - switch( world.lightmap_samples ) - { - case 1: - if( !Mod_LoadColoredLighting( )) - { - // expand the white lighting data - loadmodel->lightdata = (color24 *)Mem_Alloc( loadmodel->mempool, l->filelen * sizeof( color24 )); - out = loadmodel->lightdata; - - for( i = 0; i < l->filelen; i++, out++ ) - { - d = *in++; - out->r = d; - out->g = d; - out->b = d; - } - } - break; - case 3: // load colored lighting - loadmodel->lightdata = Mem_Alloc( loadmodel->mempool, l->filelen ); - memcpy( loadmodel->lightdata, in, l->filelen ); - SetBits( loadmodel->flags, MODEL_COLORED_LIGHTING ); - break; - default: - Host_Error( "Mod_LoadLighting: bad lightmap sample count %i\n", world.lightmap_samples ); - break; - } - - if( world.loading ) - { - // only world can have deluxedata (FIXME: what about quake models?) - // not supposed to be load ? - if( FBitSet( host.features, ENGINE_LOAD_DELUXEDATA )) - { - // try to loading deluxemap too - if( extrahdr != NULL ) - Mod_LoadLightVecs( &extrahdr->lumps[LUMP_LIGHTVECS] ); - else Mod_LoadDeluxemap (); // old method - - // try to loading occlusionmap too - if( extrahdr != NULL ) - Mod_LoadShadowmap( &extrahdr->lumps[LUMP_SHADOWMAP] ); - } - else - { - world.deluxedata = NULL; - world.vecdatasize = 0; - world.shadowdata = NULL; - world.occdatasize = 0; - } - } - - // setup lightdata pointers - if( bmodel_version == QBSP2_VERSION ) - { - in32 = (void *)(mod_base + faces->fileofs); - count = faces->filelen / sizeof( *in32 ); - in16 = NULL; - } - else - { - in16 = (void *)(mod_base + faces->fileofs); - count = faces->filelen / sizeof( *in16 ); - in32 = NULL; - } - - surf = loadmodel->surfaces; - - for( i = 0; i < count; i++, surf++ ) - { - if( bmodel_version == QBSP2_VERSION ) - { - lightofs = in32->lightofs; - in32++; - } - else - { - lightofs = in16->lightofs; - in16++; - } - - if( loadmodel->lightdata && lightofs != -1 ) - { - int offset = (lightofs / world.lightmap_samples); - - // NOTE: we divide offset by three because lighting and deluxemap keep their pointers - // into three-bytes structs and shadowmap just monochrome - surf->samples = loadmodel->lightdata + offset; - - // if deluxemap is present setup it too - if( world.deluxedata ) - surf->info->deluxemap = world.deluxedata + offset; - - // will be used by mods - if( world.shadowdata ) - surf->info->shadowmap = world.shadowdata + offset; - } - } -} - -/* -================= -Mod_CalcSurfaceExtents - -Fills in surf->texturemins[] and surf->extents[] -================= -*/ -static void Mod_CalcSurfaceExtents( msurface_t *surf ) -{ - float mins[2], maxs[2], val; - float lmmins[2], lmmaxs[2]; - int bmins[2], bmaxs[2]; - int i, j, e, sample_size; - mextrasurf_t *info = surf->info; - int facenum = surf - loadmodel->surfaces; - mtexinfo_t *tex; - mvertex_t *v; - - sample_size = Mod_SampleSizeForFace( surf ); - tex = surf->texinfo; - - Mod_LightMatrixFromTexMatrix( tex, info->lmvecs ); - - mins[0] = lmmins[0] = mins[1] = lmmins[1] = 999999; - maxs[0] = lmmaxs[0] = maxs[1] = lmmaxs[1] =-999999; - - for( i = 0; i < surf->numedges; i++ ) - { - e = loadmodel->surfedges[surf->firstedge + i]; - - if( e >= loadmodel->numedges || e <= -loadmodel->numedges ) - Host_Error( "Mod_CalcSurfaceExtents: bad edge\n" ); - - if( e >= 0 ) v = &loadmodel->vertexes[loadmodel->edges[e].v[0]]; - else v = &loadmodel->vertexes[loadmodel->edges[-e].v[1]]; - - for( j = 0; j < 2; j++ ) - { - val = DotProduct( v->position, surf->texinfo->vecs[j] ) + surf->texinfo->vecs[j][3]; - mins[j] = Q_min( val, mins[j] ); - maxs[j] = Q_max( val, maxs[j] ); - } - - for( j = 0; j < 2; j++ ) - { - val = DotProduct( v->position, info->lmvecs[j] ) + info->lmvecs[j][3]; - lmmins[j] = Q_min( val, lmmins[j] ); - lmmaxs[j] = Q_max( val, lmmaxs[j] ); - } - } - - for( i = 0; i < 2; i++ ) - { - bmins[i] = floor( mins[i] / sample_size ); - bmaxs[i] = ceil( maxs[i] / sample_size ); - - surf->texturemins[i] = bmins[i] * sample_size; - surf->extents[i] = (bmaxs[i] - bmins[i]) * sample_size; - - if( FBitSet( tex->flags, TEX_WORLD_LUXELS )) - { - lmmins[i] = floor( lmmins[i] ); - lmmaxs[i] = ceil( lmmaxs[i] ); - - info->lightmapmins[i] = lmmins[i]; - info->lightextents[i] = (lmmaxs[i] - lmmins[i]); - } - else - { - // just copy texturemins - info->lightmapmins[i] = surf->texturemins[i]; - info->lightextents[i] = surf->extents[i]; - } - - if( !FBitSet( tex->flags, TEX_SPECIAL ) && surf->extents[i] > 4096 ) - MsgDev( D_ERROR, "Bad surface extents %i\n", surf->extents[i] ); - } -} - -/* -================= -Mod_CalcSurfaceBounds - -fills in surf->mins and surf->maxs -================= -*/ -static void Mod_CalcSurfaceBounds( msurface_t *surf ) -{ - int i, e; - mvertex_t *v; - - ClearBounds( surf->info->mins, surf->info->maxs ); - - for( i = 0; i < surf->numedges; i++ ) - { - e = loadmodel->surfedges[surf->firstedge + i]; - - if( e >= loadmodel->numedges || e <= -loadmodel->numedges ) - Host_Error( "Mod_CalcSurfaceBounds: bad edge\n" ); - - if( e >= 0 ) v = &loadmodel->vertexes[loadmodel->edges[e].v[0]]; - else v = &loadmodel->vertexes[loadmodel->edges[-e].v[1]]; - AddPointToBounds( v->position, surf->info->mins, surf->info->maxs ); - } - - VectorAverage( surf->info->mins, surf->info->maxs, surf->info->origin ); -} - -/* -================= -Mod_LoadSurfaces -================= -*/ -static void Mod_LoadSurfaces( const dlump_t *l ) -{ - dface_t *in16; - dface2_t *in32; - msurface_t *out; - mextrasurf_t *info; - int test_lightsize = -1; - int next_lightofs = -1; - int prev_lightofs = -1; - int i, j, count; - int lightofs; - - if( bmodel_version == QBSP2_VERSION ) - { - in32 = (void *)(mod_base + l->fileofs); - if( l->filelen % sizeof( *in32 )) - Host_Error( "Mod_LoadSurfaces: funny lump size in '%s'\n", loadmodel->name ); - count = l->filelen / sizeof( *in32 ); - in16 = NULL; - } - else - { - in16 = (void *)(mod_base + l->fileofs); - if( l->filelen % sizeof( *in16 )) - Host_Error( "Mod_LoadSurfaces: funny lump size in '%s'\n", loadmodel->name ); - count = l->filelen / sizeof( *in16 ); - in32 = NULL; - } - - loadmodel->numsurfaces = count; - loadmodel->surfaces = out = Mem_Alloc( loadmodel->mempool, count * sizeof( msurface_t )); - info = Mem_Alloc( loadmodel->mempool, count * sizeof( mextrasurf_t )); - - // predict samplecount based on bspversion - if( bmodel_version == Q1BSP_VERSION || bmodel_version == QBSP2_VERSION ) - world.lightmap_samples = 1; - else world.lightmap_samples = 3; - - for( i = 0; i < count; i++, out++, info++ ) - { - texture_t *tex; - - // setup crosslinks between two parts of msurface_t - out->info = info; - info->surf = out; - - if( bmodel_version == QBSP2_VERSION ) - { - if(( in32->firstedge + in32->numedges ) > loadmodel->numsurfedges ) - { - MsgDev( D_ERROR, "Bad surface %i from %i\n", i, count ); - continue; - } - - out->firstedge = in32->firstedge; - out->numedges = in32->numedges; - out->flags = 0; - - if( in32->side ) out->flags |= SURF_PLANEBACK; - out->plane = loadmodel->planes + in32->planenum; - out->texinfo = loadmodel->texinfo + in32->texinfo; - - for( j = 0; j < MAXLIGHTMAPS; j++ ) - out->styles[j] = in32->styles[j]; - lightofs = in32->lightofs; - in32++; - } - else - { - if(( in16->firstedge + in16->numedges ) > loadmodel->numsurfedges ) - { - MsgDev( D_ERROR, "Bad surface %i from %i\n", i, count ); - continue; - } - - out->firstedge = in16->firstedge; - out->numedges = in16->numedges; - out->flags = 0; - - if( in16->side ) out->flags |= SURF_PLANEBACK; - out->plane = loadmodel->planes + in16->planenum; - out->texinfo = loadmodel->texinfo + in16->texinfo; - - for( j = 0; j < MAXLIGHTMAPS; j++ ) - out->styles[j] = in16->styles[j]; - lightofs = in16->lightofs; - in16++; - } - - tex = out->texinfo->texture; - - if( !Q_strncmp( tex->name, "sky", 3 )) - out->flags |= (SURF_DRAWTILED|SURF_DRAWSKY); - - if(( tex->name[0] == '*' && Q_stricmp( tex->name, "*default" )) || tex->name[0] == '!' ) - out->flags |= (SURF_DRAWTURB|SURF_DRAWTILED|SURF_NOCULL); - - if( !FBitSet( host.features, ENGINE_QUAKE_COMPATIBLE )) - { - if( !Q_strncmp( tex->name, "water", 5 ) || !Q_strnicmp( tex->name, "laser", 5 )) - out->flags |= (SURF_DRAWTURB|SURF_DRAWTILED|SURF_NOCULL); - } - - if( !Q_strncmp( tex->name, "scroll", 6 )) - out->flags |= SURF_CONVEYOR; - - // g-cont. added a combined conveyor-transparent - if( !Q_strncmp( tex->name, "{scroll", 7 )) - out->flags |= SURF_CONVEYOR|SURF_TRANSPARENT; - - // g-cont this texture comes from decals.wad he-he - // support !reflect for reflected water - if( !Q_strcmp( tex->name, "reflect1" ) || !Q_strncmp( tex->name, "!reflect", 8 )) - { - out->flags |= SURF_REFLECT; - world.has_mirrors = true; - } - - if( tex->name[0] == '{' ) - out->flags |= SURF_TRANSPARENT; - - if( out->texinfo->flags & TEX_SPECIAL ) - out->flags |= SURF_DRAWTILED; - - Mod_CalcSurfaceBounds( out ); - Mod_CalcSurfaceExtents( out ); - - // grab the second sample to detect colored lighting - if( test_lightsize > 0 && lightofs != -1 ) - { - if( lightofs > prev_lightofs && lightofs < next_lightofs ) - next_lightofs = lightofs; - } - - // grab the first sample to determine lightmap size - if( lightofs != -1 && test_lightsize == -1 ) - { - int sample_size = Mod_SampleSizeForFace( out ); - int smax = (info->lightextents[0] / sample_size) + 1; - int tmax = (info->lightextents[1] / sample_size) + 1; - int lightstyles = 0; - - test_lightsize = smax * tmax; - // count styles to right compute test_lightsize - for( j = 0; j < MAXLIGHTMAPS && out->styles[j] != 255; j++ ) - lightstyles++; - - test_lightsize *= lightstyles; - prev_lightofs = lightofs; - next_lightofs = 99999999; - } - - if( out->flags & SURF_DRAWTURB ) - GL_SubdivideSurface( out ); // cut up polygon for warps - } - - // now we have enough data to trying determine samplecount per lightmap pixel - if( test_lightsize > 0 && prev_lightofs != -1 && next_lightofs != -1 && next_lightofs != 99999999 ) - { - float samples = (float)(next_lightofs - prev_lightofs) / (float)test_lightsize; - - if( samples != (int)samples ) - { - test_lightsize = (test_lightsize + 3) & ~3; // align datasize and try again - samples = (float)(next_lightofs - prev_lightofs) / (float)test_lightsize; - } - - if( samples == 1 || samples == 3 ) - { - world.lightmap_samples = (int)samples; - MsgDev( D_REPORT, "lighting: %s\n", (world.lightmap_samples == 1) ? "monochrome" : "colored" ); - world.lightmap_samples = Q_max( world.lightmap_samples, 1 ); // avoid division by zero - } - else MsgDev( D_WARN, "lighting invalid samplecount: %g, defaulting to %i\n", samples, world.lightmap_samples ); - } -} - -/* -================= -Mod_LoadVertexes -================= -*/ -static void Mod_LoadVertexes( const dlump_t *l ) -{ - dvertex_t *in; - mvertex_t *out; - int i, count; - - in = (void *)( mod_base + l->fileofs ); - if( l->filelen % sizeof( *in )) - Host_Error( "Mod_LoadVertexes: funny lump size in %s\n", loadmodel->name ); - count = l->filelen / sizeof( *in ); - - loadmodel->numvertexes = count; - out = loadmodel->vertexes = Mem_Alloc( loadmodel->mempool, count * sizeof( mvertex_t )); - - if( world.loading ) ClearBounds( world.mins, world.maxs ); - - for( i = 0; i < count; i++, in++, out++ ) - { - VectorCopy( in->point, out->position ); - if( world.loading ) AddPointToBounds( in->point, world.mins, world.maxs ); - } - - if( !world.loading ) return; - - VectorSubtract( world.maxs, world.mins, world.size ); - - for( i = 0; i < 3; i++ ) - { - // spread the mins / maxs by a pixel - world.mins[i] -= 1.0f; - world.maxs[i] += 1.0f; - } -} - -/* -================= -Mod_LoadEdges -================= -*/ -static void Mod_LoadEdges( const dlump_t *l ) -{ - medge_t *out; - int i, count; - - if( bmodel_version == QBSP2_VERSION ) - { - dedge2_t *in = (void *)( mod_base + l->fileofs ); - - if( l->filelen % sizeof( *in )) - Host_Error( "Mod_LoadEdges: funny lump size in %s\n", loadmodel->name ); - - count = l->filelen / sizeof( *in ); - loadmodel->edges = out = Mem_Alloc( loadmodel->mempool, count * sizeof( medge_t )); - loadmodel->numedges = count; - - for( i = 0; i < count; i++, in++, out++ ) - { - out->v[0] = in->v[0]; - out->v[1] = in->v[1]; - } - } - else - { - dedge_t *in = (void *)( mod_base + l->fileofs ); - - if( l->filelen % sizeof( *in )) - Host_Error( "Mod_LoadEdges: funny lump size in %s\n", loadmodel->name ); - - count = l->filelen / sizeof( *in ); - loadmodel->edges = out = Mem_Alloc( loadmodel->mempool, count * sizeof( medge_t )); - loadmodel->numedges = count; - - for( i = 0; i < count; i++, in++, out++ ) - { - out->v[0] = (word)in->v[0]; - out->v[1] = (word)in->v[1]; - } - } -} - -/* -================= -Mod_LoadSurfEdges -================= -*/ -static void Mod_LoadSurfEdges( const dlump_t *l ) -{ - dsurfedge_t *in; - int count; - - in = (void *)( mod_base + l->fileofs ); - if( l->filelen % sizeof( *in )) - Host_Error( "Mod_LoadSurfEdges: funny lump size in %s\n", loadmodel->name ); - - count = l->filelen / sizeof( dsurfedge_t ); - loadmodel->surfedges = Mem_Alloc( loadmodel->mempool, count * sizeof( dsurfedge_t )); - loadmodel->numsurfedges = count; - - memcpy( loadmodel->surfedges, in, count * sizeof( dsurfedge_t )); -} - -/* -================= -Mod_LoadMarkSurfaces -================= -*/ -static void Mod_LoadMarkSurfaces( const dlump_t *l ) -{ - dmarkface_t *in16; - dmarkface2_t *in32; - int i, j, count; - msurface_t **out; - - if( bmodel_version == QBSP2_VERSION ) - { - in32 = (void *)(mod_base + l->fileofs); - if( l->filelen % sizeof( *in32 )) - Host_Error( "Mod_LoadMarkFaces: funny lump size in %s\n", loadmodel->name ); - count = l->filelen / sizeof( *in32 ); - in16 = NULL; - } - else - { - in16 = (void *)(mod_base + l->fileofs); - if( l->filelen % sizeof( *in16 )) - Host_Error( "Mod_LoadMarkFaces: funny lump size in %s\n", loadmodel->name ); - count = l->filelen / sizeof( *in16 ); - in32 = NULL; - } - - loadmodel->marksurfaces = out = Mem_Alloc( loadmodel->mempool, count * sizeof( *out )); - loadmodel->nummarksurfaces = count; - - for( i = 0; i < count; i++ ) - { - if( bmodel_version == QBSP2_VERSION ) - j = in32[i]; - else j = in16[i]; - - if( j < 0 || j >= loadmodel->numsurfaces ) - Host_Error( "Mod_LoadMarkFaces: bad surface number in '%s'\n", loadmodel->name ); - out[i] = loadmodel->surfaces + j; - } -} - -/* -================= -Mod_SetParent -================= -*/ -static void Mod_SetParent( mnode_t *node, mnode_t *parent ) -{ - node->parent = parent; - - if( node->contents < 0 ) return; // it's leaf - if( world.loading ) world.numnodes++; - - Mod_SetParent( node->children[0], node ); - Mod_SetParent( node->children[1], node ); -} - -/* -================= -Mod_LoadNodes -================= -*/ -static void Mod_LoadNodes( const dlump_t *l ) -{ - dnode_t *in16; - dnode2_t *in32; - mnode_t *out; - int i, j, p; - - if( bmodel_version == QBSP2_VERSION ) - { - in32 = (void *)(mod_base + l->fileofs); - if( l->filelen % sizeof( *in32 )) Host_Error( "Mod_LoadNodes: funny lump size\n" ); - loadmodel->numnodes = l->filelen / sizeof( *in32 ); - in16 = NULL; - } - else - { - in16 = (void *)(mod_base + l->fileofs); - if( l->filelen % sizeof( *in16 )) Host_Error( "Mod_LoadNodes: funny lump size\n" ); - loadmodel->numnodes = l->filelen / sizeof( *in16 ); - in32 = NULL; - } - - if( loadmodel->numnodes < 1 ) Host_Error( "Map %s has no nodes\n", loadmodel->name ); - out = loadmodel->nodes = (mnode_t *)Mem_Alloc( loadmodel->mempool, loadmodel->numnodes * sizeof( *out )); - - for( i = 0; i < loadmodel->numnodes; i++, out++ ) - { - if( bmodel_version == QBSP2_VERSION ) - { - for( j = 0; j < 3; j++ ) - { - out->minmaxs[j+0] = in32->mins[j]; - out->minmaxs[j+3] = in32->maxs[j]; - } - - p = in32->planenum; - out->plane = loadmodel->planes + p; - out->firstsurface = in32->firstface; - out->numsurfaces = in32->numfaces; - - for( j = 0; j < 2; j++ ) - { - p = in32->children[j]; - if( p >= 0 ) out->children[j] = loadmodel->nodes + p; - else out->children[j] = (mnode_t *)(loadmodel->leafs + ( -1 - p )); - } - in32++; - } - else - { - for( j = 0; j < 3; j++ ) - { - out->minmaxs[j+0] = in16->mins[j]; - out->minmaxs[j+3] = in16->maxs[j]; - } - - p = in16->planenum; - out->plane = loadmodel->planes + p; - out->firstsurface = in16->firstface; - out->numsurfaces = in16->numfaces; - - for( j = 0; j < 2; j++ ) - { - p = in16->children[j]; - if( p >= 0 ) out->children[j] = loadmodel->nodes + p; - else out->children[j] = (mnode_t *)(loadmodel->leafs + ( -1 - p )); - } - in16++; - } - } - - if( world.loading ) - world.numnodes = 0; - - // sets nodes and leafs - Mod_SetParent( loadmodel->nodes, NULL ); -} - -/* -================= -Mod_LoadLeafs -================= -*/ -static void Mod_LoadLeafs( const dlump_t *l ) -{ - dleaf_t *in16; - dleaf2_t *in32; - mleaf_t *out; - int i, j, p, count; - - if( bmodel_version == QBSP2_VERSION ) - { - in32 = (void *)(mod_base + l->fileofs); - if( l->filelen % sizeof( *in32 )) Host_Error( "Mod_LoadLeafs: funny lump size\n" ); - count = l->filelen / sizeof( *in32 ); - in16 = NULL; - } - else - { - in16 = (void *)(mod_base + l->fileofs); - if( l->filelen % sizeof( *in16 )) Host_Error( "Mod_LoadLeafs: funny lump size\n" ); - count = l->filelen / sizeof( *in16 ); - in32 = NULL; - } - - if( count < 1 ) Host_Error( "Map %s has no leafs\n", loadmodel->name ); - out = (mleaf_t *)Mem_Alloc( loadmodel->mempool, count * sizeof( *out )); - - loadmodel->leafs = out; - loadmodel->numleafs = count; - - if( world.loading ) - { - // get visleafs from the submodel data - world.visclusters = loadmodel->submodels[0].visleafs; - world.visbytes = (world.visclusters + 7) >> 3; - world.visdata = (byte *)Mem_Alloc( loadmodel->mempool, world.visclusters * world.visbytes ); - - // enable full visibility as default - memset( world.visdata, 0xFF, world.visclusters * world.visbytes ); - } - - for( i = 0; i < count; i++, out++ ) - { - if( bmodel_version == QBSP2_VERSION ) - { - for( j = 0; j < 3; j++ ) - { - out->minmaxs[j+0] = in32->mins[j]; - out->minmaxs[j+3] = in32->maxs[j]; - } - - out->contents = in32->contents; - p = in32->visofs; - - for( j = 0; j < 4; j++ ) - out->ambient_sound_level[j] = in32->ambient_level[j]; - - out->firstmarksurface = loadmodel->marksurfaces + in32->firstmarksurface; - out->nummarksurfaces = in32->nummarksurfaces; - in32++; - } - else - { - for( j = 0; j < 3; j++ ) - { - out->minmaxs[j+0] = in16->mins[j]; - out->minmaxs[j+3] = in16->maxs[j]; - } - - out->contents = in16->contents; - p = in16->visofs; - - for( j = 0; j < 4; j++ ) - out->ambient_sound_level[j] = in16->ambient_level[j]; - - out->firstmarksurface = loadmodel->marksurfaces + in16->firstmarksurface; - out->nummarksurfaces = in16->nummarksurfaces; - in16++; - } - - if( world.loading ) - { - out->cluster = ( i - 1 ); // solid leaf 0 has no visdata - if( out->cluster >= world.visclusters ) - out->cluster = -1; - - // ignore visofs errors on leaf 0 (solid) - if( p >= 0 && out->cluster >= 0 && loadmodel->visdata ) - { - if( p < world.visdatasize ) - { - byte *inrow = loadmodel->visdata + p; - byte *inrowend = loadmodel->visdata + world.visdatasize; - byte *outrow = world.visdata + out->cluster * world.visbytes; - byte *outrowend = world.visdata + (out->cluster + 1) * world.visbytes; - - Mod_DecompressVis( inrow, inrowend, outrow, outrowend ); - } - else MsgDev( D_WARN, "Mod_LoadLeafs: invalid visofs for leaf #%i\n", i ); - } - } - else out->cluster = -1; // no visclusters on bmodels - - if( p == -1 ) out->compressed_vis = NULL; - else out->compressed_vis = loadmodel->visdata + p; - - // gl underwater warp - if( out->contents != CONTENTS_EMPTY ) - { - for( j = 0; j < out->nummarksurfaces; j++ ) - { - // underwater surfaces can't have reflection (perfomance) - out->firstmarksurface[j]->flags |= SURF_UNDERWATER; - out->firstmarksurface[j]->flags &= ~SURF_REFLECT; - } - } - } - - if( loadmodel->leafs[0].contents != CONTENTS_SOLID ) - Host_Error( "Mod_LoadLeafs: Map %s has leaf 0 is not CONTENTS_SOLID\n", loadmodel->name ); - - // do some final things for world - if( world.loading ) - { - // store size of fat pvs - world.fatbytes = (world.visclusters + 31) >> 3; - world.water_alpha = Mod_CheckWaterAlphaSupport(); - } -} - -/* -================= -Mod_LoadPlanes -================= -*/ -static void Mod_LoadPlanes( const dlump_t *l ) -{ - dplane_t *in; - mplane_t *out; - int i, j, count; - - in = (void *)(mod_base + l->fileofs); - if( l->filelen % sizeof( *in )) Host_Error( "Mod_LoadPlanes: funny lump size\n" ); - count = l->filelen / sizeof( *in ); - - if( count < 1 ) Host_Error( "Map %s has no planes\n", loadmodel->name ); - out = (mplane_t *)Mem_Alloc( loadmodel->mempool, count * sizeof( *out )); - - loadmodel->planes = out; - loadmodel->numplanes = count; - - for( i = 0; i < count; i++, in++, out++ ) - { - for( j = 0; j < 3; j++ ) - { - out->normal[j] = in->normal[j]; - if( out->normal[j] < 0.0f ) - out->signbits |= 1<normal )) - MsgDev( D_ERROR, "Mod_LoadPlanes: zero normal for plane #%i\n", i ); - - out->dist = in->dist; - out->type = in->type; - } -} - -/* -================= -Mod_LoadVisibility -================= -*/ -static void Mod_LoadVisibility( const dlump_t *l ) -{ - // bmodels has no visibility - if( !world.loading ) return; - - if( !l->filelen ) - { - MsgDev( D_WARN, "map ^2%s^7 has no visibility\n", loadmodel->name ); - loadmodel->visdata = NULL; - world.visdatasize = 0; - return; - } - - loadmodel->visdata = Mem_Alloc( loadmodel->mempool, l->filelen ); - memcpy( loadmodel->visdata, (void *)(mod_base + l->fileofs), l->filelen ); - world.visdatasize = l->filelen; // save it for PHS allocation -} - -/* -================= -Mod_LoadEntities -================= -*/ -static void Mod_LoadEntities( const dlump_t *l ) -{ - char *pfile; - string keyname; - char token[2048]; - char wadstring[2048]; - - // make sure what we really has terminator - loadmodel->entities = Mem_Alloc( loadmodel->mempool, l->filelen + 1 ); - memcpy( loadmodel->entities, mod_base + l->fileofs, l->filelen ); - if( !world.loading ) return; - - world.entdatasize = l->filelen; - pfile = (char *)loadmodel->entities; - world.compiler[0] = '\0'; - world.message[0] = '\0'; - wadlist.count = 0; - - // parse all the wads for loading textures in right ordering - while(( pfile = COM_ParseFile( pfile, token )) != NULL ) - { - if( token[0] != '{' ) - Host_Error( "Mod_LoadEntities: found %s when expecting {\n", token ); - - while( 1 ) - { - // parse key - if(( pfile = COM_ParseFile( pfile, token )) == NULL ) - Host_Error( "Mod_LoadEntities: EOF without closing brace\n" ); - if( token[0] == '}' ) break; // end of desc - - Q_strncpy( keyname, token, sizeof( keyname )); - - // parse value - if(( pfile = COM_ParseFile( pfile, token )) == NULL ) - Host_Error( "Mod_LoadEntities: EOF without closing brace\n" ); - - if( token[0] == '}' ) - Host_Error( "Mod_LoadEntities: closing brace without data\n" ); - - if( !Q_stricmp( keyname, "wad" )) - { - char *pszWadFile; - - Q_strncpy( wadstring, token, 2046 ); - wadstring[2046] = 0; - - if( !Q_strchr( wadstring, ';' )) - Q_strcat( wadstring, ";" ); - - // parse wad pathes - for( pszWadFile = strtok( wadstring, ";" ); pszWadFile != NULL; pszWadFile = strtok( NULL, ";" )) - { - COM_FixSlashes( pszWadFile ); - FS_FileBase( pszWadFile, wadlist.wadnames[wadlist.count++] ); - if( wadlist.count >= 256 ) break; // too many wads... - } - } - else if( !Q_stricmp( keyname, "mapversion" )) - world.mapversion = Q_atoi( token ); - else if( !Q_stricmp( keyname, "message" )) - Q_strncpy( world.message, token, sizeof( world.message )); - else if( !Q_stricmp( keyname, "compiler" ) || !Q_stricmp( keyname, "_compiler" )) - Q_strncpy( world.compiler, token, sizeof( world.compiler )); - } - return; // all done - } -} - -/* -================== -CountClipNodes_r -================== -*/ -static void CountClipNodes_r( dclipnode2_t *src, hull_t *hull, int nodenum ) -{ - // leaf? - if( nodenum < 0 ) return; - - hull->lastclipnode++; - - CountClipNodes_r( src, hull, src[nodenum].children[0] ); - CountClipNodes_r( src, hull, src[nodenum].children[1] ); -} - -/* -================== -RemapClipNodes_r -================== -*/ -static int RemapClipNodes_r( dclipnode2_t *srcnodes, hull_t *hull, int nodenum ) -{ - dclipnode2_t *src; - mclipnode_t *out; - int i, c; - - // leaf? - if( nodenum < 0 ) - { - return nodenum; - } - - // emit a clipnode - if( hull->lastclipnode == MAX_MAP_CLIPNODES ) - Host_Error( "MAX_MAP_CLIPNODES limit exceeded\n" ); - - src = srcnodes + nodenum; - - c = hull->lastclipnode; - out = &hull->clipnodes[c]; - hull->lastclipnode++; - - out->planenum = src->planenum; - - for( i = 0; i < 2; i++ ) - out->children[i] = RemapClipNodes_r( srcnodes, hull, src->children[i] ); - - return c; -} - -/* -================= -Mod_SetupHull -================= -*/ -static void Mod_SetupHull( model_t *mod, byte *mempool, int headnode, int hullnum ) -{ - hull_t *hull = &mod->hulls[hullnum]; - int count; - - // assume no hull - hull->firstclipnode = hull->lastclipnode = 0; - hull->planes = NULL; // hull is missed - - if(( headnode == -1 ) || ( hullnum != 1 && headnode == 0 )) - return; // hull missed - - if( headnode >= world.numclipnodes ) - return; // ZHLT weird empty hulls - - switch( hullnum ) - { - case 1: - VectorCopy( host.player_mins[0], hull->clip_mins ); // copy human hull - VectorCopy( host.player_maxs[0], hull->clip_maxs ); - break; - case 2: - VectorCopy( host.player_mins[3], hull->clip_mins ); // copy large hull - VectorCopy( host.player_maxs[3], hull->clip_maxs ); - break; - case 3: - VectorCopy( host.player_mins[1], hull->clip_mins ); // copy head hull - VectorCopy( host.player_maxs[1], hull->clip_maxs ); - break; - default: - Host_Error( "Mod_SetupHull: bad hull number %i\n", hullnum ); - break; - } - - if( VectorIsNull( hull->clip_mins ) && VectorIsNull( hull->clip_maxs )) - return; // no hull specified - - CountClipNodes_r( world.clipnodes, hull, headnode ); - count = hull->lastclipnode; - - hull->clipnodes = (mclipnode_t *)Mem_Alloc( mempool, sizeof( mclipnode_t ) * hull->lastclipnode ); - hull->planes = mod->planes; // share planes - hull->lastclipnode = 0; // restart counting - - // remap clipnodes to 16-bit indexes - RemapClipNodes_r( world.clipnodes, hull, headnode ); - - // fit array to real count -// Msg( "%s hull%d, %i clipnodes (headnode %i)\n", mod->name, hullnum, hull->lastclipnode, headnode ); -} - -/* -================= -Mod_LoadClipnodes -================= -*/ -static void Mod_LoadClipnodes( const dlump_t *l ) -{ - dclipnode_t *in16; - dclipnode2_t *in32; - dclipnode2_t *out; - qboolean extended = false; - int i, count; - - if( bmodel_version == QBSP2_VERSION ) - extended = true; - else if( bmodel_version != Q1BSP_VERSION ) - { - if(( l->filelen % sizeof( *in16 )) || ( l->filelen / sizeof( *in16 )) >= MAX_MAP_CLIPNODES ) - extended = true; - } - - if( extended ) - { - in32 = (void *)(mod_base + l->fileofs); - if( world.loading ) world.clipnodesize = sizeof( dclipnode2_t ); - if( l->filelen % sizeof( *in32 )) Host_Error( "Mod_LoadClipnodes: funny lump size\n" ); - count = l->filelen / sizeof( *in32 ); - in16 = NULL; - } - else - { - in16 = (void *)(mod_base + l->fileofs); - if( world.loading ) world.clipnodesize = sizeof( dclipnode_t ); - if( l->filelen % sizeof( *in16 )) Host_Error( "Mod_LoadClipnodes: funny lump size\n" ); - count = l->filelen / sizeof( *in16 ); - in32 = NULL; - } - - out = (dclipnode2_t *)Mem_Alloc( loadmodel->mempool, count * sizeof( *out )); - world.numclipnodes = count; - world.clipnodes = out; - - if( extended ) - { - for( i = 0; i < count; i++, out++, in32++ ) - { - out->planenum = in32->planenum; - out->children[0] = in32->children[0]; - out->children[1] = in32->children[1]; - } - } - else - { - for( i = 0; i < count; i++, out++, in16++ ) - { - out->planenum = in16->planenum; - - out->children[0] = (unsigned short)in16->children[0]; - out->children[1] = (unsigned short)in16->children[1]; - - // Arguire QBSP 'broken' clipnodes - if( out->children[0] >= count ) - out->children[0] -= 65536; - if( out->children[1] >= count ) - out->children[1] -= 65536; - } - } - - // FIXME: fill loadmodel->clipnodes? - loadmodel->numclipnodes = count; -} - -/* -================= -Mod_FindModelOrigin - -routine to detect bmodels with origin-brush -================= -*/ -static void Mod_FindModelOrigin( const char *entities, const char *modelname, vec3_t origin ) -{ - char *pfile; - string keyname; - char token[2048]; - qboolean model_found; - qboolean origin_found; - - if( !entities || !modelname || !*modelname ) - return; - - if( !origin || !VectorIsNull( origin )) - return; - - pfile = (char *)entities; - - while(( pfile = COM_ParseFile( pfile, token )) != NULL ) - { - if( token[0] != '{' ) - Host_Error( "Mod_FindModelOrigin: found %s when expecting {\n", token ); - - model_found = origin_found = false; - VectorClear( origin ); - - while( 1 ) - { - // parse key - if(( pfile = COM_ParseFile( pfile, token )) == NULL ) - Host_Error( "Mod_FindModelOrigin: EOF without closing brace\n" ); - if( token[0] == '}' ) break; // end of desc - - Q_strncpy( keyname, token, sizeof( keyname )); - - // parse value - if(( pfile = COM_ParseFile( pfile, token )) == NULL ) - Host_Error( "Mod_FindModelOrigin: EOF without closing brace\n" ); - - if( token[0] == '}' ) - Host_Error( "Mod_FindModelOrigin: closing brace without data\n" ); - - if( !Q_stricmp( keyname, "model" ) && !Q_stricmp( modelname, token )) - model_found = true; - - if( !Q_stricmp( keyname, "origin" )) - { - Q_atov( origin, token, 3 ); - origin_found = true; - } - } - - if( model_found ) break; - } -} - -/* -================= -Mod_MakeHull0 - -Duplicate the drawing hull structure as a clipping hull -================= -*/ -static void Mod_MakeHull0( void ) -{ - mnode_t *in, *child; - mclipnode_t *out; - hull_t *hull; - int i, j, count; - - hull = &loadmodel->hulls[0]; - - in = loadmodel->nodes; - count = loadmodel->numnodes; - out = Mem_Alloc( loadmodel->mempool, count * sizeof( *out )); - - hull->clipnodes = out; - hull->firstclipnode = 0; - hull->lastclipnode = count - 1; - hull->planes = loadmodel->planes; - - for( i = 0; i < count; i++, out++, in++ ) - { - out->planenum = in->plane - loadmodel->planes; - - for( j = 0; j < 2; j++ ) - { - child = in->children[j]; - - if( child->contents < 0 ) - out->children[j] = child->contents; - else out->children[j] = child - loadmodel->nodes; - } - } -} - -/* -================= -Mod_UnloadBrushModel - -Release all uploaded textures -================= -*/ -void Mod_UnloadBrushModel( model_t *mod ) -{ - texture_t *tx; - int i; - - ASSERT( mod != NULL ); - - if( mod->type != mod_brush ) - return; // not a bmodel - - if( mod->name[0] != '*' ) - { - for( i = 0; i < mod->numtextures; i++ ) - { - tx = mod->textures[i]; - if( !tx || tx->gl_texturenum == tr.defaultTexture ) - continue; // free slot - - GL_FreeTexture( tx->gl_texturenum ); // main texture - GL_FreeTexture( tx->fb_texturenum ); // luma texture - } - - Mem_FreePool( &mod->mempool ); - } - - memset( mod, 0, sizeof( *mod )); -} - -/* -================= -Mod_LoadBrushModel -================= -*/ -static void Mod_LoadBrushModel( model_t *mod, const void *buffer, qboolean *loaded ) -{ - int i, j; - char *ents; - dheader_t *header; - dextrahdr_t *extrahdr; - byte *mempool; - dmodel_t *bm; - - if( loaded ) *loaded = false; - header = (dheader_t *)buffer; - loadmodel->type = mod_brush; - extrahdr = (dextrahdr_t *)((byte *)buffer + sizeof( dheader_t )); - i = header->version; - -#ifndef SUPPORT_BSP2_FORMAT - if( header->version == QBSP2_VERSION ) - { - MsgDev( D_ERROR, "%s can't be loaded in this build. Please rebuild engine with enabled SUPPORT_BSP2_FORMAT\n", loadmodel->name ); - return; - } -#endif - switch( i ) - { - case Q1BSP_VERSION: - case HLBSP_VERSION: - case QBSP2_VERSION: - break; - default: - MsgDev( D_ERROR, "%s has wrong version number (%i should be %i)\n", loadmodel->name, i, HLBSP_VERSION ); - return; - } - - loadmodel->numframes = 2; // g-cont. why is it necessary? - bmodel_version = i; // share tp global - - // swap all the lumps - mod_base = (byte *)header; - loadmodel->mempool = Mem_AllocPool( va( "^2%s^7", loadmodel->name )); - - // make sure what extrahdr is valid - if( extrahdr->id != IDEXTRAHEADER || extrahdr->version != EXTRA_VERSION ) - extrahdr = NULL; // no extra header - - // load into heap - if( header->lumps[LUMP_ENTITIES].fileofs <= 1024 && (header->lumps[LUMP_ENTITIES].filelen % sizeof( dplane_t )) == 0 ) - { - // blue-shift swapped lumps - Mod_LoadEntities( &header->lumps[LUMP_PLANES] ); - Mod_LoadPlanes( &header->lumps[LUMP_ENTITIES] ); - } - else - { - // normal half-life lumps - Mod_LoadEntities( &header->lumps[LUMP_ENTITIES] ); - Mod_LoadPlanes( &header->lumps[LUMP_PLANES] ); - } - - Mod_LoadSubmodels( &header->lumps[LUMP_MODELS] ); - Mod_LoadVertexes( &header->lumps[LUMP_VERTEXES] ); - Mod_LoadEdges( &header->lumps[LUMP_EDGES] ); - Mod_LoadSurfEdges( &header->lumps[LUMP_SURFEDGES] ); - Mod_LoadTextures( &header->lumps[LUMP_TEXTURES] ); - Mod_LoadVisibility( &header->lumps[LUMP_VISIBILITY] ); - Mod_LoadTexInfo( &header->lumps[LUMP_TEXINFO], extrahdr ); - Mod_LoadSurfaces( &header->lumps[LUMP_FACES] ); - Mod_LoadLighting( &header->lumps[LUMP_LIGHTING], &header->lumps[LUMP_FACES], extrahdr ); - Mod_LoadMarkSurfaces( &header->lumps[LUMP_MARKSURFACES] ); - Mod_LoadLeafs( &header->lumps[LUMP_LEAFS] ); - Mod_LoadNodes( &header->lumps[LUMP_NODES] ); - Mod_LoadClipnodes( &header->lumps[LUMP_CLIPNODES] ); - - Mod_MakeHull0 (); - - ents = loadmodel->entities; - mempool = loadmodel->mempool; - - // set up the submodels - for( i = 0; i < mod->numsubmodels; i++ ) - { - bm = &mod->submodels[i]; - - // hull 0 is just shared across all bmodels - mod->hulls[0].firstclipnode = bm->headnode[0]; - - // but hulls1-3 is build individually for a each given submodel - for( j = 1; j < MAX_MAP_HULLS; j++ ) - Mod_SetupHull( mod, mempool, bm->headnode[j], j ); - - mod->firstmodelsurface = bm->firstface; - mod->nummodelsurfaces = bm->numfaces; - - VectorCopy( bm->mins, mod->mins ); - VectorCopy( bm->maxs, mod->maxs ); - - mod->radius = RadiusFromBounds( mod->mins, mod->maxs ); - mod->numleafs = bm->visleafs; -// mod->flags = 0; - - if( i != 0 ) - { - Mod_FindModelOrigin( ents, va( "*%i", i ), bm->origin ); - - // HACKHACK: c2a1 issues - if( !Q_stricmp( loadmodel->name, "maps/c2a1.bsp" ) && ( i == 11 )) - SetBits( mod->flags, MODEL_HAS_ORIGIN ); - - // flag 2 is indicated model with origin brush! - if( !VectorIsNull( bm->origin )) - SetBits( mod->flags, MODEL_HAS_ORIGIN ); - } - - for( j = 0; i != 0 && j < mod->nummodelsurfaces; j++ ) - { - msurface_t *surf = mod->surfaces + mod->firstmodelsurface + j; - - if( surf->flags & SURF_CONVEYOR ) - mod->flags |= MODEL_CONVEYOR; - - if( surf->flags & SURF_TRANSPARENT ) - mod->flags |= MODEL_TRANSPARENT; - - // kill water backplanes for submodels (half-life rules) - if( surf->flags & SURF_DRAWTURB ) - { - mod->flags |= MODEL_LIQUID; - - if( surf->plane->type == PLANE_Z ) - { - // kill bottom plane too - if( surf->info->mins[2] == bm->mins[2] + 1.0f ) - surf->flags |= SURF_WATERCSG; - } - else - { - // kill side planes - surf->flags |= SURF_WATERCSG; - } - } - } - - if( i < mod->numsubmodels - 1 ) - { - char name[8]; - - // duplicate the basic information - Q_snprintf( name, sizeof( name ), "*%i", i + 1 ); - loadmodel = Mod_FindName( name, true ); - *loadmodel = *mod; - Q_strncpy( loadmodel->name, name, sizeof( loadmodel->name )); - loadmodel->mempool = NULL; - mod = loadmodel; - } - } - - // delete temporary array - if( world.clipnodes ) - Mem_Free( world.clipnodes ); - world.clipnodes = NULL; - world.numclipnodes = 0; - - if( loaded ) *loaded = true; // all done -} - /* ================== Mod_FindName @@ -2788,9 +246,9 @@ Loads a model into the cache */ model_t *Mod_LoadModel( model_t *mod, qboolean crash ) { - byte *buf; char tempname[64]; qboolean loaded; + byte *buf; if( !mod ) { @@ -2819,8 +277,6 @@ model_t *Mod_LoadModel( model_t *mod, qboolean crash ) return NULL; } - FS_FileBase( mod->name, modelname ); - MsgDev( D_NOTE, "Mod_LoadModel: %s\n", mod->name ); mod->needload = world.load_sequence; // register mod mod->type = mod_bad; @@ -2915,15 +371,10 @@ void Mod_LoadWorld( const char *name, uint *checksum, qboolean multiplayer ) memset( com_models, 0, sizeof( com_models )); com_models[1] = cm_models; // make link to world - // update the lightmap blocksize - if( host.features & ENGINE_LARGE_LIGHTMAPS ) - world.block_size = BLOCK_SIZE_MAX; - else world.block_size = BLOCK_SIZE_DEFAULT; - if( !Q_stricmp( cm_models[0].name, name )) { // recalc the checksum in force-mode - CRC32_MapFile( &world.checksum, worldmodel->name, multiplayer ); + CRC32_MapFile( &world.checksum, cm_models[0].name, multiplayer ); // singleplayer mode: server already loaded map if( checksum ) *checksum = world.checksum; @@ -2948,8 +399,8 @@ void Mod_LoadWorld( const char *name, uint *checksum, qboolean multiplayer ) // load the newmap world.loading = true; - worldmodel = Mod_ForName( name, true ); - CRC32_MapFile( &world.checksum, worldmodel->name, multiplayer ); + Mod_ForName( name, true ); + CRC32_MapFile( &world.checksum, cm_models[0].name, multiplayer ); world.loading = false; if( checksum ) *checksum = world.checksum; @@ -2975,6 +426,13 @@ void Mod_FreeUnused( void ) } } +/* +=============================================================================== + + MODEL ROUTINES + +=============================================================================== +*/ /* =================== Mod_GetType @@ -3180,224 +638,4 @@ model_t *Mod_Handle( int handle ) return NULL; } return com_models[handle]; -} - -/* -================== -Mod_CheckLump - -check lump for existing -================== -*/ -int Mod_CheckLump( const char *filename, const int lump, int *lumpsize ) -{ - file_t *f = FS_Open( filename, "rb", true ); - byte buffer[sizeof( dheader_t ) + sizeof( dextrahdr_t )]; - size_t prefetch_size = sizeof( buffer ); - dextrahdr_t *extrahdr; - dheader_t *header; - - if( !f ) return LUMP_LOAD_COULDNT_OPEN; - - if( FS_Read( f, buffer, prefetch_size ) != prefetch_size ) - { - FS_Close( f ); - return LUMP_LOAD_BAD_HEADER; - } - - header = (dheader_t *)buffer; - - if( header->version != HLBSP_VERSION ) - { - FS_Close( f ); - return LUMP_LOAD_BAD_VERSION; - } - - extrahdr = (dextrahdr_t *)((byte *)buffer + sizeof( dheader_t )); - - if( extrahdr->id != IDEXTRAHEADER || extrahdr->version != EXTRA_VERSION ) - { - FS_Close( f ); - return LUMP_LOAD_NO_EXTRADATA; - } - - if( lump < 0 || lump >= EXTRA_LUMPS ) - { - FS_Close( f ); - return LUMP_LOAD_INVALID_NUM; - } - - if( extrahdr->lumps[lump].filelen <= 0 ) - { - FS_Close( f ); - return LUMP_LOAD_NOT_EXIST; - } - - if( lumpsize ) - *lumpsize = extrahdr->lumps[lump].filelen; - - FS_Close( f ); - - return LUMP_LOAD_OK; -} - -/* -================== -Mod_ReadLump - -reading random lump by user request -================== -*/ -int Mod_ReadLump( const char *filename, const int lump, void **lumpdata, int *lumpsize ) -{ - file_t *f = FS_Open( filename, "rb", true ); - byte buffer[sizeof( dheader_t ) + sizeof( dextrahdr_t )]; - size_t prefetch_size = sizeof( buffer ); - dextrahdr_t *extrahdr; - dheader_t *header; - byte *data; - int length; - - if( !f ) return LUMP_LOAD_COULDNT_OPEN; - - if( FS_Read( f, buffer, prefetch_size ) != prefetch_size ) - { - FS_Close( f ); - return LUMP_LOAD_BAD_HEADER; - } - - header = (dheader_t *)buffer; - - if( header->version != HLBSP_VERSION ) - { - FS_Close( f ); - return LUMP_LOAD_BAD_VERSION; - } - - extrahdr = (dextrahdr_t *)((byte *)buffer + sizeof( dheader_t )); - - if( extrahdr->id != IDEXTRAHEADER || extrahdr->version != EXTRA_VERSION ) - { - FS_Close( f ); - return LUMP_LOAD_NO_EXTRADATA; - } - - if( lump < 0 || lump >= EXTRA_LUMPS ) - { - FS_Close( f ); - return LUMP_LOAD_INVALID_NUM; - } - - if( extrahdr->lumps[lump].filelen <= 0 ) - { - FS_Close( f ); - return LUMP_LOAD_NOT_EXIST; - } - - data = malloc( extrahdr->lumps[lump].filelen + 1 ); - length = extrahdr->lumps[lump].filelen; - - if( !data ) - { - FS_Close( f ); - return LUMP_LOAD_MEM_FAILED; - } - - FS_Seek( f, extrahdr->lumps[lump].fileofs, SEEK_SET ); - - if( FS_Read( f, data, length ) != length ) - { - free( data ); - FS_Close( f ); - return LUMP_LOAD_CORRUPTED; - } - - data[length] = 0; // write term - FS_Close( f ); - - if( lumpsize ) - *lumpsize = length; - *lumpdata = data; - - return LUMP_LOAD_OK; -} - -/* -================== -Mod_SaveLump - -writing lump by user request -only empty lumps is allows -================== -*/ -int Mod_SaveLump( const char *filename, const int lump, void *lumpdata, int lumpsize ) -{ - file_t *f = FS_Open( filename, "e+b", true ); - byte buffer[sizeof( dheader_t ) + sizeof( dextrahdr_t )]; - size_t prefetch_size = sizeof( buffer ); - dextrahdr_t *extrahdr; - dheader_t *header; - - if( !f ) return LUMP_SAVE_COULDNT_OPEN; - - if( !lumpdata || lumpsize <= 0 ) - return LUMP_SAVE_NO_DATA; - - if( FS_Read( f, buffer, prefetch_size ) != prefetch_size ) - { - FS_Close( f ); - return LUMP_SAVE_BAD_HEADER; - } - - header = (dheader_t *)buffer; - - if( header->version != HLBSP_VERSION ) - { - FS_Close( f ); - return LUMP_SAVE_BAD_VERSION; - } - - extrahdr = (dextrahdr_t *)((byte *)buffer + sizeof( dheader_t )); - - if( extrahdr->id != IDEXTRAHEADER || extrahdr->version != EXTRA_VERSION ) - { - FS_Close( f ); - return LUMP_SAVE_NO_EXTRADATA; - } - - if( lump < 0 || lump >= EXTRA_LUMPS ) - { - FS_Close( f ); - return LUMP_SAVE_INVALID_NUM; - } - - if( extrahdr->lumps[lump].filelen != 0 ) - { - FS_Close( f ); - return LUMP_SAVE_ALREADY_EXIST; - } - - FS_Seek( f, 0, SEEK_END ); - - // will be saved later - extrahdr->lumps[lump].fileofs = FS_Tell( f ); - extrahdr->lumps[lump].filelen = lumpsize; - - if( FS_Write( f, lumpdata, lumpsize ) != lumpsize ) - { - FS_Close( f ); - return LUMP_SAVE_CORRUPTED; - } - - // update the header - FS_Seek( f, sizeof( dheader_t ), SEEK_SET ); - - if( FS_Write( f, extrahdr, sizeof( dextrahdr_t )) != sizeof( dextrahdr_t )) - { - FS_Close( f ); - return LUMP_SAVE_CORRUPTED; - } - - FS_Close( f ); - return LUMP_SAVE_OK; } \ No newline at end of file diff --git a/engine/common/net_chan.c b/engine/common/net_chan.c index 00c4b376..e29606aa 100644 --- a/engine/common/net_chan.c +++ b/engine/common/net_chan.c @@ -955,7 +955,7 @@ Netchan_CopyFileFragments qboolean Netchan_CopyFileFragments( netchan_t *chan, sizebuf_t *msg ) { fragbuf_t *p, *n; - char filename[CS_SIZE]; + char filename[MAX_QPATH]; int nsize; byte *buffer; int pos; diff --git a/engine/common/protocol.h b/engine/common/protocol.h index 7c2891d8..48de7c1f 100644 --- a/engine/common/protocol.h +++ b/engine/common/protocol.h @@ -76,8 +76,8 @@ GNU General Public License for more details. #define svc_eventindex 54 // [index][eventname] // reserved #define svc_resourcelocation 56 // [string] -#define svc_querycvarvalue 57 // [string] -#define svc_querycvarvalue2 58 // [string][long] (context) +// reserved +// reserved #define svc_lastmsg 58 // start user messages at this point // client to server @@ -90,8 +90,8 @@ GNU General Public License for more details. // reserved #define clc_fileconsistency 7 #define clc_voicedata 8 -#define clc_requestcvarvalue 9 -#define clc_requestcvarvalue2 10 +// reserved +// reserved #define clc_lastmsg 10 // end client messages #define MAX_VISIBLE_PACKET_BITS 11 // 2048 visible entities per frame (hl1 has 256) @@ -174,7 +174,7 @@ typedef struct { int rescount; int restype[MAX_RESOURCES]; - char resnames[MAX_RESOURCES][CS_SIZE]; + char resnames[MAX_RESOURCES][MAX_QPATH]; } resourcelist_t; #endif//PROTOCOL_H \ No newline at end of file diff --git a/engine/common/soundlib/snd_utils.c b/engine/common/soundlib/snd_utils.c index b138b290..a640d7ce 100644 --- a/engine/common/soundlib/snd_utils.c +++ b/engine/common/soundlib/snd_utils.c @@ -93,49 +93,6 @@ byte *Sound_Copy( size_t size ) return out; } -uint 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 diff --git a/engine/eiface.h b/engine/eiface.h index 64984b10..6163920e 100644 --- a/engine/eiface.h +++ b/engine/eiface.h @@ -258,21 +258,6 @@ typedef struct enginefuncs_s qboolean (*pfnVoice_SetClientListening)(int iReceiver, int iSender, qboolean bListen); const char *(*pfnGetPlayerAuthId) ( edict_t *e ); - - void *(*pfnSequenceGet)( const char *fileName, const char *entryName ); - void *(*pfnSequencePickSentence)( const char *groupName, int pickMethod, int *picked ); - int (*pfnGetFileSize)( char *filename ); - unsigned int (*pfnGetApproxWavePlayLen)( const char *filepath ); - int (*pfnIsCareerMatch)( void ); - int (*pfnGetLocalizedStringLength)( const char *label ); - void (*pfnRegisterTutorMessageShown)( int mid ); - int (*pfnGetTimesTutorMessageShown)( int mid ); - void (*pfnProcessTutorMessageDecayBuffer)( int *buffer, int bufferLength ); - void (*pfnConstructTutorMessageDecayBuffer)( int *buffer, int bufferLength ); - void (*pfnResetTutorMessageDecayData)( void ); - void (*pfnQueryClientCvarValue)( const edict_t *player, const char *cvarName ); - void (*pfnQueryClientCvarValue2)( const edict_t *player, const char *cvarName, int requestID ); - int (*CheckParm)( char *parm, char **ppnext ); } enginefuncs_t; // ONLY ADD NEW FUNCTIONS TO THE END OF THIS STRUCT. INTERFACE VERSION IS FROZEN AT 138 @@ -483,8 +468,6 @@ typedef struct void (*pfnOnFreeEntPrivateData)( edict_t *pEnt ); void (*pfnGameShutdown)(void); int (*pfnShouldCollide)( edict_t *pentTouched, edict_t *pentOther ); - void (*pfnCvarValue)( const edict_t *pEnt, const char *value ); - void (*pfnCvarValue2)( const edict_t *pEnt, int requestID, const char *cvarName, const char *value ); } NEW_DLL_FUNCTIONS; typedef int (*NEW_DLL_FUNCTIONS_FN)( NEW_DLL_FUNCTIONS *pFunctionTable, int *interfaceVersion ); diff --git a/engine/engine.dsp b/engine/engine.dsp index f711bb54..44c8b246 100644 --- a/engine/engine.dsp +++ b/engine/engine.dsp @@ -383,6 +383,10 @@ SOURCE=.\common\matrixlib.c # End Source File # Begin Source File +SOURCE=.\common\mod_bmodel.c +# End Source File +# Begin Source File + SOURCE=.\common\mod_studio.c # End Source File # Begin Source File diff --git a/engine/server/server.h b/engine/server/server.h index 98881413..1c2792ae 100644 --- a/engine/server/server.h +++ b/engine/server/server.h @@ -137,10 +137,10 @@ typedef struct server_s double lastchecktime; int lastcheck; // number of last checked client - char model_precache[MAX_MODELS][CS_SIZE]; - char sound_precache[MAX_SOUNDS][CS_SIZE]; - char files_precache[MAX_CUSTOM][CS_SIZE]; - char event_precache[MAX_EVENTS][CS_SIZE]; + char model_precache[MAX_MODELS][MAX_QPATH]; + char sound_precache[MAX_SOUNDS][MAX_QPATH]; + char files_precache[MAX_CUSTOM][MAX_QPATH]; + char event_precache[MAX_EVENTS][MAX_QPATH]; sv_static_entity_t static_entities[MAX_STATIC_ENTITIES]; int num_static_entities; diff --git a/engine/server/sv_client.c b/engine/server/sv_client.c index e421ae10..65665954 100644 --- a/engine/server/sv_client.c +++ b/engine/server/sv_client.c @@ -30,8 +30,6 @@ const char *clc_strings[11] = "clc_unused6", "clc_fileconsistency", "clc_voicedata", - "clc_requestcvarvalue", - "clc_requestcvarvalue2", }; typedef struct ucmd_s @@ -2367,41 +2365,6 @@ void SV_ParseResourceList( sv_client_t *cl, sizebuf_t *msg ) Netchan_FragSend( &cl->netchan ); } -/* -=================== -SV_ParseCvarValue - -Parse a requested value from client cvar -=================== -*/ -void SV_ParseCvarValue( sv_client_t *cl, sizebuf_t *msg ) -{ - const char *value = MSG_ReadString( msg ); - - if( svgame.dllFuncs2.pfnCvarValue != NULL ) - svgame.dllFuncs2.pfnCvarValue( cl->edict, value ); - MsgDev( D_REPORT, "Cvar query response: name:%s, value:%s\n", cl->name, value ); -} - -/* -=================== -SV_ParseCvarValue2 - -Parse a requested value from client cvar -=================== -*/ -void SV_ParseCvarValue2( sv_client_t *cl, sizebuf_t *msg ) -{ - string name, value; - int requestID = MSG_ReadLong( msg ); - Q_strcpy( name, MSG_ReadString( msg )); - Q_strcpy( value, MSG_ReadString( msg )); - - if( svgame.dllFuncs2.pfnCvarValue2 != NULL ) - svgame.dllFuncs2.pfnCvarValue2( cl->edict, requestID, name, value ); - MsgDev( D_REPORT, "Cvar query response: name:%s, request ID %d, cvar:%s, value:%s\n", cl->name, requestID, name, value ); -} - /* =================== SV_ExecuteClientMessage @@ -2473,12 +2436,6 @@ void SV_ExecuteClientMessage( sv_client_t *cl, sizebuf_t *msg ) case clc_resourcelist: SV_ParseResourceList( cl, msg ); break; - case clc_requestcvarvalue: - SV_ParseCvarValue( cl, msg ); - break; - case clc_requestcvarvalue2: - SV_ParseCvarValue2( cl, msg ); - break; default: MsgDev( D_ERROR, "SV_ReadClientMessage: clc_bad\n" ); SV_DropClient( cl ); diff --git a/engine/server/sv_game.c b/engine/server/sv_game.c index 47543ef7..a275111e 100644 --- a/engine/server/sv_game.c +++ b/engine/server/sv_game.c @@ -576,38 +576,27 @@ void SV_WriteEntityPatch( const char *filename ) dheader_t *header; int ver = -1, lumpofs = 0, lumplen = 0; byte buf[MAX_SYSPATH]; // 1 kb + string bspfilename; file_t *f; - - f = FS_Open( va( "maps/%s.bsp", filename ), "rb", false ); + + Q_strncpy( bspfilename, va( "maps/%s.bsp", filename ), sizeof( bspfilename )); + f = FS_Open( bspfilename, "rb", false ); if( !f ) return; memset( buf, 0, MAX_SYSPATH ); FS_Read( f, buf, MAX_SYSPATH ); - ver = *(uint *)buf; - - switch( ver ) + header = (dheader_t *)buf; + + // check all the lumps and some other errors + if( !Mod_TestBmodelLumps( bspfilename, buf, true )) { - case Q1BSP_VERSION: - case HLBSP_VERSION: - case QBSP2_VERSION: - header = (dheader_t *)buf; - if( header->lumps[LUMP_ENTITIES].fileofs <= 1024 && (header->lumps[LUMP_ENTITIES].filelen % sizeof( dplane_t )) == 0 ) - { - // Blue-Shift ordering - lumpofs = header->lumps[LUMP_PLANES].fileofs; - lumplen = header->lumps[LUMP_PLANES].filelen; - } - else - { - lumpofs = header->lumps[LUMP_ENTITIES].fileofs; - lumplen = header->lumps[LUMP_ENTITIES].filelen; - } - break; - default: FS_Close( f ); return; } + lumpofs = header->lumps[LUMP_ENTITIES].fileofs; + lumplen = header->lumps[LUMP_ENTITIES].filelen; + if( lumplen >= 10 ) { char *entities = NULL; @@ -642,7 +631,7 @@ char *SV_ReadEntityScript( const char *filename, int *flags ) *flags = 0; - Q_strncpy( bspfilename, va( "maps/%s.bsp", filename ), sizeof( entfilename )); + Q_strncpy( bspfilename, va( "maps/%s.bsp", filename ), sizeof( bspfilename )); f = FS_Open( bspfilename, "rb", false ); if( !f ) return NULL; @@ -650,32 +639,20 @@ char *SV_ReadEntityScript( const char *filename, int *flags ) memset( buf, 0, MAX_SYSPATH ); FS_Read( f, buf, MAX_SYSPATH ); - ver = *(uint *)buf; - - switch( ver ) + header = (dheader_t *)buf; + + // check all the lumps and some other errors + if( !Mod_TestBmodelLumps( bspfilename, buf, (host.developer <= 2) ? true : false )) { - case Q1BSP_VERSION: - case HLBSP_VERSION: - case QBSP2_VERSION: - header = (dheader_t *)buf; - if( header->lumps[LUMP_ENTITIES].fileofs <= 1024 && (header->lumps[LUMP_ENTITIES].filelen % sizeof( dplane_t )) == 0 ) - { - // Blue-Shift ordering - lumpofs = header->lumps[LUMP_PLANES].fileofs; - lumplen = header->lumps[LUMP_PLANES].filelen; - } - else - { - lumpofs = header->lumps[LUMP_ENTITIES].fileofs; - lumplen = header->lumps[LUMP_ENTITIES].filelen; - } - break; - default: *flags |= MAP_INVALID_VERSION; FS_Close( f ); return NULL; } + // after call Mod_TestBmodelLumps we gurantee what map is valid + lumpofs = header->lumps[LUMP_ENTITIES].fileofs; + lumplen = header->lumps[LUMP_ENTITIES].filelen; + // check for entfile too Q_strncpy( entfilename, va( "maps/%s.ent", filename ), sizeof( entfilename )); @@ -692,7 +669,7 @@ char *SV_ReadEntityScript( const char *filename, int *flags ) if( !ents && lumplen >= 10 ) { FS_Seek( f, lumpofs, SEEK_SET ); - ents = (char *)Z_Malloc( lumplen + 1 ); + ents = Z_Malloc( lumplen + 1 ); FS_Read( f, ents, lumplen ); } @@ -3407,17 +3384,6 @@ void pfnSetView( const edict_t *pClient, const edict_t *pViewent ) MSG_WriteWord( &client->netchan.message, NUM_FOR_EDICT( pViewent )); } -/* -============= -pfnTime - -============= -*/ -float pfnTime( void ) -{ - return (float)Sys_DoubleTime(); -} - /* ============= pfnStaticDecal @@ -4362,111 +4328,6 @@ const char *pfnGetPlayerAuthId( edict_t *e ) return result; } - -/* -============= -pfnGetFileSize - -returns the filesize in bytes -============= -*/ -int pfnGetFileSize( char *filename ) -{ - return FS_FileSize( filename, false ); -} - -/* -============= -pfnGetLocalizedStringLength - -used by CS:CZ (client stub) -============= -*/ -int pfnGetLocalizedStringLength( const char *label ) -{ - return 0; -} - -/* -============= -pfnQueryClientCvarValue - -request client cvar value -============= -*/ -void pfnQueryClientCvarValue( const edict_t *player, const char *cvarName ) -{ - sv_client_t *cl; - - if( !cvarName || !*cvarName ) - { - MsgDev( D_ERROR, "QueryClientCvarValue: NULL cvar name!\n" ); - return; - } - - if(( cl = SV_ClientFromEdict( player, true )) != NULL ) - { - MSG_BeginServerCmd( &cl->netchan.message, svc_querycvarvalue ); - MSG_WriteString( &cl->netchan.message, cvarName ); - } - else - { - if( svgame.dllFuncs2.pfnCvarValue ) - svgame.dllFuncs2.pfnCvarValue( player, "Bad Player" ); - MsgDev( D_ERROR, "QueryClientCvarValue: tried to send to a non-client!\n" ); - } -} - -/* -============= -pfnQueryClientCvarValue2 - -request client cvar value (bugfixed) -============= -*/ -void pfnQueryClientCvarValue2( const edict_t *player, const char *cvarName, int requestID ) -{ - sv_client_t *cl; - - if( !cvarName || !*cvarName ) - { - MsgDev( D_ERROR, "QueryClientCvarValue: NULL cvar name!\n" ); - return; - } - - if(( cl = SV_ClientFromEdict( player, true )) != NULL ) - { - MSG_BeginServerCmd( &cl->netchan.message, svc_querycvarvalue2 ); - MSG_WriteLong( &cl->netchan.message, requestID ); - MSG_WriteString( &cl->netchan.message, cvarName ); - } - else - { - if( svgame.dllFuncs2.pfnCvarValue2 ) - svgame.dllFuncs2.pfnCvarValue2( player, requestID, cvarName, "Bad Player" ); - MsgDev( D_ERROR, "QueryClientCvarValue: tried to send to a non-client!\n" ); - } -} - -/* -============= -pfnCheckParm - -============= -*/ -static int pfnCheckParm( char *parm, char **ppnext ) -{ - int i = Sys_CheckParm( parm ); - - if( ppnext != NULL ) - { - if( i > 0 && i < host.argc - 1 ) - *ppnext = (char*)host.argv[i + 1]; - else *ppnext = NULL; - } - - return i; -} // engine callbacks static enginefuncs_t gEngfuncs = @@ -4615,20 +4476,6 @@ static enginefuncs_t gEngfuncs = pfnVoice_GetClientListening, pfnVoice_SetClientListening, pfnGetPlayerAuthId, - pfnSequenceGet, - pfnSequencePickSentence, - pfnGetFileSize, - Sound_GetApproxWavePlayLen, - pfnIsCareerMatch, - pfnGetLocalizedStringLength, - pfnRegisterTutorMessageShown, - pfnGetTimesTutorMessageShown, - pfnProcessTutorMessageDecayBuffer, - pfnConstructTutorMessageDecayBuffer, - pfnResetTutorMessageDecayData, - pfnQueryClientCvarValue, - pfnQueryClientCvarValue2, - pfnCheckParm, }; /* diff --git a/engine/server/sv_world.c b/engine/server/sv_world.c index 5fb134e0..27c7f1aa 100644 --- a/engine/server/sv_world.c +++ b/engine/server/sv_world.c @@ -241,7 +241,7 @@ hull_t *SV_HullForBsp( edict_t *ent, const vec3_t mins, const vec3_t maxs, vec3_ hull = &model->hulls[COM_RandomLong( 0, 0 )]; #endif // FIXME: find a better method to detect quake-maps? - if( world.sky_sphere || world.lightmap_samples == 1 ) + if( FBitSet( world.flags, FWORLD_SKYSPHERE )) { // alternate hull select for quake maps if( size[0] < 3.0f || ent->v.solid == SOLID_PORTAL )