Compare commits

..

71 Commits

Author SHA1 Message Date
Alibek Omarov
bb734456f6 engine: client: avi: use typeof() to declare function pointers to ffmpeg API 2026-04-09 09:52:52 +05:00
Alibek Omarov
a219ca7371 ci: upgrade ffmpeg binary to 8.1 2026-04-09 09:20:25 +05:00
Oleg
c3b5e43a7a engine: client: avi: bump FFMPEG symbols 2026-04-09 09:14:04 +05:00
Alibek Omarov
e7597d2e84 Merge branch 'theevolk-feature-soundapi' of github.com:ShadowOfEVolk/xash3d-fwgs 2026-04-08 17:18:03 +05:00
TheEVolk
386ca1b9bb engine: client: make sound api const 2026-04-08 14:27:24 +03:00
TheEVolk
da3960c4f0 engine: client: move bitflag definitions to sound_api 2026-04-08 14:15:16 +03:00
TheEVolk
cd85762b09 engine: client: fix arm builds 2026-04-08 14:11:44 +03:00
TheEVolk
d92b3a730c engine: client: adapt sound api to snd globals 2026-04-08 14:02:10 +03:00
Alibek Omarov
ffa53f14e5 engine: platform: sdl2: implement continuous background recalibration for gyroscope 2026-04-08 11:31:01 +05:00
Alibek Omarov
b8bd906832 engine: menu_int: fix a typo 2026-04-08 11:19:13 +05:00
Alibek Omarov
1c73d8d6f4 engine: client: add joy_debug, tool that visualizes gamepad input 2026-04-08 11:13:07 +05:00
Alibek Omarov
2c3fdb5eee engine: client: in_joy: clear gyro data after each FinalizeMove, only take data when calibration status is set to calibrated 2026-04-08 10:30:32 +05:00
Alibek Omarov
4bbe52e3b4 engine: client: in_joy: fix engine-level axis rebinding 2026-04-08 10:22:12 +05:00
Alibek Omarov
2d241ed96b engine: platform: sdl2: fix calibraion never ending if it was failed, lower sample count requirements to 50%, fix gyro when using PS5 controller over Bluetooth 2026-04-08 10:22:12 +05:00
Alibek Omarov
ba4bce06b1 engine: platform: sdl2: swap pitch and yaw gamepads axis, following the fix in 626a7eb9108376a68c2fd140e1b5942c64ea1e66 2026-04-08 10:22:12 +05:00
Владислав Сухов
f7e933aea1 engine: client: add gyroscope aiming support
refactor: standardize input functions to use (pitch, yaw) parameter order
2026-04-08 10:22:12 +05:00
Alibek Omarov
dbcf77c9a9 engine: cleanup, disallow exec for multiplayer non-TFC games 2026-04-08 08:22:01 +05:00
w_sh1t
8a9aa2da6c engine: common: add wildcard support for exec command, add amount of frames to wait in wait command 2026-04-08 08:05:42 +05:00
Alibek Omarov
0e8143aefc ref: gl: remove duplicate line from if-else branches in R_MarkLeaves 2026-04-08 08:05:00 +05:00
TheEVolk
4345e9e6a6 Merge remote-tracking branch 'upstream/master' into theevolk-feature-soundapi 2026-04-07 18:35:39 +03:00
Alibek Omarov
e7fa45d5ca ref: gl: fix masked textures with VBO 2026-04-06 11:12:09 +05:00
Alibek Omarov
abda0a07c2 ref: gl: cache worldmodel pointer instead of accessing it through gp_cl pointer 2026-04-06 11:07:08 +05:00
Alibek Omarov
9d22827395 ref: gl: reorganize checks in R_CullSurface 2026-04-06 11:04:33 +05:00
Alibek Omarov
7b665b3763 ref: gl: track max height of a dynamic lightmap 2026-04-06 10:04:15 +05:00
Alibek Omarov
58692f397a ref: gl: reorganize R_CheckLightMap to exit early for dlighted surfaces 2026-04-06 09:40:19 +05:00
Alibek Omarov
2253fcc955 ref: gl: skip redundant positions in LM_AllocBlock 2026-04-06 09:05:47 +05:00
Alibek Omarov
cbbae0c9bf ref: gl: simplify backface culling checks in frustum class 2026-04-06 08:25:45 +05:00
Alibek Omarov
ef513f27ac engine: simplify node recursion functions, do not fetch both children at once if only one is used 2026-04-06 08:16:51 +05:00
Alibek Omarov
e8ae48fd3d ref: simplify node recursion functions, do not fetch both children at once if only one is used 2026-04-06 08:16:20 +05:00
Alibek Omarov
434e508186 engine: client: reserve some free space in channel_t and rawchan_t 2026-04-06 07:27:50 +05:00
Alibek Omarov
4a2b3e89b4 engine: client: disable NET_EXT_SPLITSIZE in local game, for slightly better performance 2026-04-06 07:14:06 +05:00
Alibek Omarov
6c3f8c7ec5 engine: mod_bmodel: allow nearest on quake skybox 2026-04-06 06:55:52 +05:00
Alibek Omarov
3fffe061d5 engine: client: switch to dynamically allocating vox words, freeing up 320 Kb of memory 2026-04-06 06:55:27 +05:00
Alibek Omarov
729c985eaf engine: client: consolidate dma, s_listener and bunch of separate sound globals into one snd global struct. Replace bit fields with bit flags in preparation for SoundAPI
Remove s_combine_sounds, it never worked great and was disabled by default.
2026-04-06 06:00:48 +05:00
Alibek Omarov
75fb6c5f34 engine: add Mem_Free2 that takes a pointer to pointer and then sets it to NULL 2026-04-06 05:14:33 +05:00
Alibek Omarov
8bc9b1865a engine: client: allow custom paths in cdaudio.txt 2026-04-05 07:03:46 +05:00
Alibek Omarov
d6993c47d6 engine: mod_bmodel: do not try to load visibility if there is none, like for external bmodels 2026-04-05 07:03:46 +05:00
Alibek Omarov
b8792e4084 filesystem: set qwrap as falldir for Quake games 2026-04-05 07:03:46 +05:00
Alibek Omarov
2234dd48bc engine: client: soundlib: use unsigned int in GetLittleLong to fix ubsan error 2026-04-05 07:03:46 +05:00
Alibek Omarov
c8693e1e89 engine: common: fix char array underflow, on Q_strlen() - 1 2026-04-05 07:03:46 +05:00
Alibek Omarov
3ace4a61d2 ref: gl: fix harmless NULL pointer dereference in alias model loading code 2026-04-05 07:03:46 +05:00
Alibek Omarov
bd3f865cf7 engine: mod_bmodel: only look for BSPX lumps if BSPX header was found 2026-04-05 07:03:46 +05:00
Alibek Omarov
b68e71abfd engine: net_chan: explicitly convert qboolean to uint before right shift to 31 bits 2026-04-05 07:03:46 +05:00
Alibek Omarov
6179bef160 engine: fix possible buffer overflow when looking for BSPX header 2026-04-05 07:03:46 +05:00
Alibek Omarov
983427ebb3 engine: client: allow coloring fixed font text 2026-04-05 07:03:46 +05:00
Alibek Omarov
1b2ab34745 engine: client: keep TF_NEAREST enabled for console font, if HUD font failed to load 2026-04-05 07:03:46 +05:00
Alibek Omarov
2713f03ec2 filesystem: fix Quake gamedir check if pak0.pak is written in uppercase, omitting caseinsensitive FS emulation as is VFS is not enabled at this stage 2026-04-05 07:03:46 +05:00
Alibek Omarov
238083c174 engine: client: move common server message handlers to CL_ParseCommonMessage and only HL specific ones to CL_ParseCommonHLMessage (previously CL_ParseCommonDLLMessage) 2026-04-05 07:03:46 +05:00
Alibek Omarov
78de3ee6d9 engine: client: initialize cl_textmessage at compile-time 2026-04-05 07:03:46 +05:00
Alibek Omarov
695081e079 3rdparty: library_suffix: update 2026-04-05 07:03:46 +05:00
TheEVolk
2c5e2e1ded engine: client: remove sound_t definition from sound.h 2026-04-04 18:30:14 +03:00
TheEVolk
d151c48d1f engine: client: fix api issues 2026-04-04 17:47:54 +03:00
TheEVolk
35998bce81 engine: client: add sound api 2026-04-04 16:08:31 +03:00
Alibek Omarov
a10520ca9e engine: host: add message when native game libraries weren't found but libraries for x86 windows, linux or macos were, telling that the mod probably require custom logic code 2026-04-04 17:37:48 +05:00
Alibek Omarov
cb4a03ded2 engine: platform: add Platform_LibraryExists and implement it for iOS and generally for everything else with FS_FileExists 2026-04-04 17:37:48 +05:00
Alibek Omarov
31a0d17de5 3rdparty: mainui: update 2026-04-04 17:36:21 +05:00
Alibek Omarov
5f765a35b9 ref: gl: show more detailed Host_Error instead of ASSERT in R_GetTexture 2026-04-04 17:36:05 +05:00
Alibek Omarov
5606346cc1 scripts: waifulib: xcompile: add Android NDK 30 2026-04-04 17:34:31 +05:00
Alibek Omarov
6faee8fd89 scripts: ios: call mkdir -p to not error out if the directory already exists (thanks, @ksagaming) 2026-04-04 17:34:07 +05:00
Alibek Omarov
255c9bcaed 3rdparty: mainui: update 2026-04-03 09:27:33 +05:00
Alibek Omarov
25a3fdbcec 3rdparty: mainui: update 2026-04-03 09:20:28 +05:00
Alibek Omarov
8e450d869b engine: common: add host.default_gamedir containing gamedir passed to us from game launcher 2026-04-03 04:09:25 +05:00
Alibek Omarov
e8ef7ad60c engine: server: throw Host_Error when game tries to call precache model with invalid argument, like GoldSrc 2026-04-03 01:43:23 +05:00
Alibek Omarov
216eedcdcd ref: gl: better wording about backface culling goldsrc bug [skip ci] 2026-04-03 01:28:04 +05:00
Alibek Omarov
fd55716633 ref: gl: change backface culling only if it wasn't disabled by mod developer beforehand 2026-04-03 01:23:55 +05:00
Alibek Omarov
0b920be381 engine: client: delay freeing up the sound channel when it becomes inaudible 2026-04-03 00:03:52 +05:00
Alibek Omarov
e6dfccb6ba engine: reorganize common.h header 2026-04-02 20:45:00 +05:00
Alibek Omarov
3129081050 engine: fix a typo USING_ASAN -> USE_ASAN 2026-04-02 02:16:50 +05:00
Alibek Omarov
00dac928c9 ref: replace checking PARM_QUAKE_COMPATIBLE with direct check of feature flag set 2026-04-02 02:13:27 +05:00
Alibek Omarov
3726256514 engine: simplify IsQuakeCompatible by enforcing QUAKE_COMPATIBLE feature flag when Quake demo starts 2026-04-02 02:11:40 +05:00
Alibek Omarov
5eb43505cb engine: platform: try to fix build on 32-bit arm in debug builds (r7 is used by frame pointer, so we push it into stack before using for a syscall) 2026-04-02 02:02:59 +05:00
74 changed files with 2025 additions and 1495 deletions

View File

@@ -69,7 +69,7 @@ jobs:
targetarch: amd64
env:
SDL_VERSION: 2.32.10
FFMPEG_VERSION: 7.1
FFMPEG_VERSION: 8.1
GH_CPU_ARCH: ${{ matrix.targetarch }}
GH_CPU_OS: ${{ matrix.targetos }}
GH_CROSSCOMPILING: ${{ matrix.cross }}

View File

@@ -612,9 +612,9 @@ static inline mnode_t *node_child( const mnode_t *n, int side, const model_t *mo
}
}
return n->children_[side];
return n->children_[side ? 1 : 0];
#else
return n->children_[side];
return n->children_[side ? 1 : 0];
#endif
}

185
common/sound_api.h Normal file
View File

@@ -0,0 +1,185 @@
/*
sound_api.h - Xash3D extension for client sound interface
Copyright (C) 2026 Xash3D FWGS Developers
This is free and unencumbered software released into the public domain.
Anyone is free to copy, modify, publish, use, compile, sell, or
distribute this software, either in source code form or as a compiled
binary, for any purpose, commercial or non-commercial, and by any
means.
In jurisdictions that recognize copyright laws, the author or authors
of this software dedicate any and all copyright interest in the
software to the public domain. We make this dedication for the benefit
of the public at large and to the detriment of our heirs and
successors. We intend this dedication to be an overt act of
relinquishment in perpetuity of all present and future rights to this
software under copyright law.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR
OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
OTHER DEALINGS IN THE SOFTWARE.
For more information, please refer to <http://unlicense.org/>
*/
#ifndef SOUND_API_H
#define SOUND_API_H
#include "xash3d_types.h"
// Experimental implementation, backward compatibility is not guaranteed.
#define CL_SOUND_INTERFACE_VERSION 1
#define FL_VOXWORD_IN_CACHE BIT( 0 ) // if set, it was loaded prior and shouldn't be freed
#define FL_CHAN_USE_LOOP BIT( 0 ) // don't loop default and local sounds
#define FL_CHAN_STATIC_SOUND BIT( 1 ) // use origin instead of fetching entnum's origin
#define FL_CHAN_LOCAL_SOUND BIT( 2 ) // it's a local menu sound (not looped, not paused)
#define FL_CHAN_SENTENCE_FINISHED BIT( 4 ) // if set, finished playing sentence
#define FL_CHAN_FINISHED BIT( 5 ) // if set, finished playing single word
typedef int sound_t;
typedef struct portable_samplepair_s
{
int left;
int right;
} portable_samplepair_t;
typedef struct sfx_s
{
char name[MAX_QPATH];
wavdata_t *cache;
int servercount;
uint hashValue;
struct sfx_s *hashNext;
} sfx_t;
typedef struct voxword_s
{
sfx_t *sfx;
uint16_t volume; // volume percent
uint16_t pitch; // pitch shift percent (keep large for extra chipmunk fun)
uint8_t timecompress; // percent of skipped data (speeds up playback without pitch shift)
uint8_t start; // percent at which playback starts
uint8_t end; // percent at which playback ends
uint8_t flags;
} voxword_t;
typedef struct channel_s
{
char name[16]; // keep sentence name
sfx_t *sfx; // sfx number
vec3_t origin; // only use if fixed_origin is set
float dist_mult; // distance multiplier (attenuation/clipK)
int entchannel; // sound channel (CHAN_STREAM, CHAN_VOICE, etc.)
uint flags;
short entnum; // entity soundsource
short master_vol; // 0-255 master volume
short leftvol; // 0-255 left volume
short rightvol; // 0-255 right volume
short basePitch; // base pitch percent (100% is normal pitch playback)
byte word_index;
// HACKHACK: count when this channel became inaudible
// to not free it when it could be respatialized soon
#define MAX_CHANNEL_INAUDIBLE_TIME 0.1f
float inauduble_free_time;
double sample;
double forced_end;
wavdata_t *data;
voxword_t *words; // dynamically allocated, (num_words + 1) entries, null sfx terminates
uintptr_t engine_reserved[8]; // only for engine developers
uintptr_t game_reserved[8]; // free space for game developers
} channel_t;
typedef struct rawchan_s
{
short entnum;
short master_vol;
short leftvol; // 0-255 left volume
short rightvol; // 0-255 right volume
float dist_mult; // distance multiplier (attenuation/clipK)
vec3_t origin; // only use if fixed_origin is set
volatile uint s_rawend;
float oldtime; // catch time jumps
uintptr_t engine_reserved[8]; // only for engine developers
uintptr_t game_reserved[8]; // free space for game developers
size_t max_samples; // buffer length
portable_samplepair_t rawsamples[]; // variable sized
} rawchan_t;
typedef struct snd_format_s
{
uint speed;
byte width;
byte channels;
} snd_format_t;
typedef struct snd_globals_s
{
// dma
const char *backend_name;
byte *buffer;
snd_format_t format;
qboolean initialized; // sound engine is active
int samples; // mono samples in buffer
int samplepos; // in mono samples
int paintedtime; // total samples that have been mixed at speed
int soundtime; // total samples that have been played out to hardware at dma speed
// listener (client, camera, etc)
vec3_t origin;
vec3_t forward, right, up;
int entnum;
qboolean streaming; // playing AVI-file
qboolean stream_paused; // pause only background track
// SoundAPI shared pointers
channel_t *const channels;
int max_channels;
int total_channels;
rawchan_t **const raw_channels;
int max_raw_channels;
sound_t ambient_sfx[NUM_AMBIENTS];
qboolean have_ambient_sfx;
} snd_globals_t;
// API from engine to client (client calls these)
typedef struct sound_api_s
{
qboolean (*CL_GetEntitySpatialization)( channel_t *ch );
sfx_t* (*S_GetSfxByHandle)( sound_t handle );
} sound_api_t;
// Callbacks from client to engine (engine calls these when custom sound is active)
typedef struct sound_interface_s
{
int version;
qboolean (*pfnS_Init)( snd_globals_t *globals );
void (*pfnS_Shutdown)( void );
void (*pfnS_UpdateSound)( void );
/* Full paint: endtime (sample pairs), dma buffer, paintedtime in/out. Client does mix + transfer to dma.buffer. */
void (*pfnS_PaintChannels)( int endtime );
void (*pfnS_UpdateChannel)( int ch_idx, const channel_t *ch, sound_t handle ); // ch=NULL -> channel freed
void (*pfnS_UpdateRawChannel)( int raw_idx, rawchan_t *ch ); // ch=NULL -> channel freed
void (*pfnS_Spatialize)( channel_t *ch );
void (*pfnS_FreeSound)( sfx_t *sfx, sound_t handle );
} sound_interface_t;
#endif // SOUND_API_H

View File

@@ -23,6 +23,12 @@ struct mstudioevent_s;
struct engine_studio_api_s;
struct r_studio_interface_s;
struct sound_api_s;
struct sound_interface_s;
typedef struct sound_api_s sound_api_t;
typedef struct sound_interface_s sound_interface_t;
// NOTE: ordering is important!
typedef struct cldll_func_s
{
@@ -74,6 +80,8 @@ typedef struct cldll_func_s
int (*pfnTouchEvent)( int type, int fingerID, float x, float y, float dx, float dy );
void (*pfnMoveEvent)( float forwardmove, float sidemove );
void (*pfnLookEvent)( float relyaw, float relpitch );
// Sound API
int (*pfnGetSoundInterface)( int version, const sound_api_t *api, sound_interface_t *callback );
} cldll_func_t;
#endif//CDLL_EXP_H

View File

@@ -255,9 +255,9 @@ static void AVI_StreamAudio( movie_state_t *Avi )
rawchan_t *ch = NULL;
// keep the same semantics, when S_RAW_SOUND_SOUNDTRACK doesn't play if S_StartStreaming wasn't enabled
qboolean disable_stream = Avi->entnum == S_RAW_SOUND_SOUNDTRACK ? !s_listener.streaming : false;
qboolean disable_stream = Avi->entnum == S_RAW_SOUND_SOUNDTRACK ? !snd.streaming : false;
if( !dma.initialized || disable_stream || cl.paused || !Avi->cached_audio )
if( !snd.initialized || disable_stream || cl.paused || !Avi->cached_audio )
return;
ch = S_FindRawChannel( Avi->entnum, true );
@@ -268,14 +268,14 @@ static void AVI_StreamAudio( movie_state_t *Avi )
ch->master_vol = Avi->volume;
ch->dist_mult = (Avi->attn / SND_CLIP_DISTANCE);
if( ch->s_rawend < soundtime )
ch->s_rawend = soundtime;
if( ch->s_rawend < snd.soundtime )
ch->s_rawend = snd.soundtime;
while( ch->s_rawend < soundtime + ch->max_samples )
while( ch->s_rawend < snd.soundtime + ch->max_samples )
{
size_t copy;
buffer_samples = ch->max_samples - (ch->s_rawend - soundtime);
buffer_samples = ch->max_samples - (ch->s_rawend - snd.soundtime);
file_samples = buffer_samples * ((float)Avi->rate / SOUND_DMA_SPEED);
if( file_samples <= 1 ) return; // no more samples need

View File

@@ -26,16 +26,82 @@ GNU General Public License for more details.
#if XASH_FFMPEG_DLOPEN
// using typeof gives more resilience to function declaration changes
// but it's only available in GCC and standardized in C23
#if defined(__GNUC__) || __STDC_VERSION__ >= 202311L
#define SUPPORTED_AVU_VERSION_MAJOR LIBAVUTIL_VERSION_MAJOR
#define SUPPORTED_AVF_VERSION_MAJOR LIBAVFORMAT_VERSION_MAJOR
#define SUPPORTED_AVC_VERSION_MAJOR LIBAVCODEC_VERSION_MAJOR
#define SUPPORTED_SWR_VERSION_MAJOR LIBSWRESAMPLE_VERSION_MAJOR
#define SUPPORTED_SWS_VERSION_MAJOR LIBSWSCALE_VERSION_MAJOR
#define F( func ) \
typedef typeof( func ) func##_t; \
func##_t *p##func
// libavutil
F( avutil_version );
F( av_frame_alloc );
F( av_frame_free );
F( av_frame_ref );
F( av_frame_unref );
F( av_strerror );
F( av_free );
F( av_get_bytes_per_sample );
F( av_get_media_type_string );
F( av_image_alloc );
// libavformat
F( avformat_version );
F( av_find_best_stream );
F( av_read_frame );
F( av_seek_frame );
F( avformat_close_input );
F( avformat_find_stream_info );
F( avformat_open_input );
// libavcodec
F( avcodec_version );
F( av_packet_alloc );
F( av_packet_free );
F( av_packet_unref );
F( avcodec_alloc_context3 );
F( avcodec_find_decoder );
F( avcodec_flush_buffers );
F( avcodec_free_context );
F( avcodec_open2 );
F( avcodec_parameters_to_context );
F( avcodec_receive_frame );
F( avcodec_send_packet );
// libswresample
F( swresample_version );
F( swr_alloc_set_opts2 );
F( swr_convert );
F( swr_free );
F( swr_init );
// libswscale
F( swscale_version );
F( sws_freeContext );
F( sws_getContext );
F( sws_scale );
#undef F
#else // !defined(__GNUC__) && __STDC_VERSION__ < 202311L
// the following symbols were taken from ffmpeg public headers
// on each major ffmpeg uprgade, they must be validated
// ffmpeg guarantees API and ABI compatibility between major versions
// so complain if this gets compiled against unsupported yet version
// the same check will be done in runtime to ensure compatibility
#define SUPPORTED_AVU_VERSION_MAJOR 59
#define SUPPORTED_AVF_VERSION_MAJOR 61
#define SUPPORTED_AVC_VERSION_MAJOR 61
#define SUPPORTED_SWR_VERSION_MAJOR 5
#define SUPPORTED_SWS_VERSION_MAJOR 8
#define SUPPORTED_AVU_VERSION_MAJOR 60
#define SUPPORTED_AVF_VERSION_MAJOR 62
#define SUPPORTED_AVC_VERSION_MAJOR 62
#define SUPPORTED_SWR_VERSION_MAJOR 6
#define SUPPORTED_SWS_VERSION_MAJOR 9
#if SUPPORTED_AVU_VERSION_MAJOR != LIBAVUTIL_VERSION_MAJOR
#error "unsupported libavutil version"
@@ -105,6 +171,8 @@ void (*psws_freeContext)( struct SwsContext *swsContext );
struct SwsContext *(*psws_getContext)( int srcW, int srcH, enum AVPixelFormat srcFormat, int dstW, int dstH, enum AVPixelFormat dstFormat, int flags, SwsFilter *srcFilter, SwsFilter *dstFilter, const double *param );
int (*psws_scale)( struct SwsContext *c, const uint8_t *const srcSlice[], const int srcStride[], int srcSliceY, int srcSliceH, uint8_t *const dst[], const int dstStride[] );
#endif // defined(__GNUC__) || __STDC_VERSION__ >= 202311L
#else // !XASH_FFMPEG_DLOPEN
#define SUPPORTED_AVU_VERSION_MAJOR LIBAVUTIL_VERSION_MAJOR

View File

@@ -70,11 +70,7 @@ void CL_SetFontRendermode( cl_font_t *font )
void CL_SetFontColor( cl_font_t *font, const rgba_t color )
{
// don't apply color to fixed fonts it's already colored
if( font->type != FONT_FIXED || REF_GET_PARM( PARM_TEX_GLFORMAT, font->hFontTexture ) == 0x8045 ) // GL_LUMINANCE8_ALPHA8
ref.dllFuncs.Color4ub( color[0], color[1], color[2], color[3] );
else
ref.dllFuncs.Color4ub( 255, 255, 255, color[3] );
ref.dllFuncs.Color4ub( color[0], color[1], color[2], color[3] );
}
qboolean Con_LoadFixedWidthFont( const char *fontname, cl_font_t *font, float scale, convar_t *rendermode, uint texFlags )

View File

@@ -1381,7 +1381,7 @@ qboolean CL_GetEntitySpatialization( channel_t *ch )
if( ch->entnum == 0 )
{
ch->staticsound = true;
SetBits( ch->flags, FL_CHAN_STATIC_SOUND );
return true; // static sound
}

View File

@@ -37,11 +37,20 @@ GNU General Public License for more details.
#include "platform/platform.h"
#define MAX_LINELENGTH 80
#define MAX_TEXTCHANNELS 8 // must be power of two (GoldSrc uses 4 channels)
#define TEXT_MSGNAME "TextMessage%i"
#define TEXT_MSGNAME "TextMessage"
static char cl_textbuffer[MAX_TEXTCHANNELS][2048];
static client_textmessage_t cl_textmessage[MAX_TEXTCHANNELS];
client_textmessage_t cl_textmessage[MAX_TEXTCHANNELS] =
{
{ .pName = "TextMessage0", .pMessage = cl_textbuffer[0] },
{ .pName = "TextMessage1", .pMessage = cl_textbuffer[1] },
{ .pName = "TextMessage2", .pMessage = cl_textbuffer[2] },
{ .pName = "TextMessage3", .pMessage = cl_textbuffer[3] },
{ .pName = "TextMessage4", .pMessage = cl_textbuffer[4] },
{ .pName = "TextMessage5", .pMessage = cl_textbuffer[5] },
{ .pName = "TextMessage6", .pMessage = cl_textbuffer[6] },
{ .pName = "TextMessage7", .pMessage = cl_textbuffer[7] },
};
static const dllfunc_t cdll_exports[] =
{
@@ -96,6 +105,7 @@ static const dllfunc_t cdll_new_exports[] = // allowed only in SDK 2.3 and high
{ "IN_ClientTouchEvent", (void **)&clgame.dllFuncs.pfnTouchEvent}, // Xash3D FWGS ext
{ "IN_ClientMoveEvent", (void **)&clgame.dllFuncs.pfnMoveEvent}, // Xash3D FWGS ext
{ "IN_ClientLookEvent", (void **)&clgame.dllFuncs.pfnLookEvent}, // Xash3D FWGS ext
{ "HUD_GetSoundInterface", (void **)&clgame.dllFuncs.pfnGetSoundInterface }, // Xash3D ext
};
static void pfnSPR_DrawHoles( int frame, int x, int y, const wrect_t *prc );
@@ -179,7 +189,13 @@ static void CL_InitCDAudio( const char *filename )
while(( pfile = COM_ParseFile( pfile, token, sizeof( token ))) != NULL )
{
if( !Q_stricmp( token, "blank" ))
{
clgame.cdtracks[c][0] = '\0';
}
else if( token[0] == '/' ) // allow custom path
{
Q_strncpy( clgame.cdtracks[c], &token[1], sizeof( clgame.cdtracks[c] ));
}
else
{
Q_snprintf( clgame.cdtracks[c], sizeof( clgame.cdtracks[c] ),
@@ -560,31 +576,18 @@ and hold them into permament memory pool
*/
static void CL_InitTitles( const char *filename )
{
fs_offset_t fileSize;
byte *pMemFile;
int i;
// initialize text messages (game_text)
for( i = 0; i < MAX_TEXTCHANNELS; i++ )
{
char name[MAX_VA_STRING];
Q_snprintf( name, sizeof( name ), TEXT_MSGNAME, i );
cl_textmessage[i].pName = copystringpool( clgame.mempool, name );
cl_textmessage[i].pMessage = cl_textbuffer[i];
}
// clear out any old data that's sitting around.
if( clgame.titles ) Mem_Free( clgame.titles );
Mem_Free( clgame.titles );
clgame.titles = NULL;
clgame.numTitles = 0;
pMemFile = FS_LoadFile( filename, &fileSize, false );
if( !pMemFile ) return;
fs_offset_t fileSize = 0;
char *pMemFile = (char *)FS_LoadFile( filename, &fileSize, false );
if( !pMemFile )
return;
clgame.titles = CL_TextMessageParse( clgame.mempool, (char *)pMemFile, fileSize, &clgame.numTitles );
clgame.titles = CL_TextMessageParse( clgame.mempool, pMemFile, fileSize, &clgame.numTitles );
Mem_Free( pMemFile );
}
@@ -653,54 +656,6 @@ void CL_ParseTextMessage( sizebuf_t *msg )
CL_HudMessage( text->pName );
}
/*
================
CL_ParseFinaleCutscene
show display finale or cutscene message
================
*/
void CL_ParseFinaleCutscene( sizebuf_t *msg, int level )
{
static int msgindex = 0;
client_textmessage_t *text;
int channel;
cl.intermission = level;
channel = msgindex;
msgindex = (msgindex + 1) & (MAX_TEXTCHANNELS - 1);
// grab message channel
text = &cl_textmessage[channel];
// NOTE: svc_finale and svc_cutscene has a
// predefined settings like Quake-style
text->x = -1.0f;
text->y = 0.15f;
text->effect = 2; // scan out effect
text->r1 = 245;
text->g1 = 245;
text->b1 = 245;
text->a1 = 0; // unused
text->r2 = 0;
text->g2 = 0;
text->b2 = 0;
text->a2 = 0;
text->fadein = 0.15f;
text->fadeout = 0.0f;
text->holdtime = 99999.0f;
text->fxtime = 0.0f;
// to prevent grab too long messages
Q_strncpy( (char *)text->pMessage, MSG_ReadString( msg ), 2048 );
if( *text->pMessage == '\0' )
return; // no real text
CL_HudMessage( text->pName );
}
/*
====================
CL_GetMaxlients
@@ -1974,13 +1929,12 @@ client_textmessage_t *CL_TextMessageGet( const char *pName )
int i;
// first check internal messages
for( i = 0; i < MAX_TEXTCHANNELS; i++ )
if( Q_strlen( pName ) == sizeof( TEXT_MSGNAME ) // including the digit
&& !Q_strncmp( pName, TEXT_MSGNAME, sizeof( TEXT_MSGNAME ) - 1 ))
{
char name[MAX_VA_STRING];
i = pName[sizeof( TEXT_MSGNAME ) - 1] - '0';
Q_snprintf( name, sizeof( name ), TEXT_MSGNAME, i );
if( !Q_strcmp( pName, name ))
if( i >= 0 && i < MAX_TEXTCHANNELS )
return cl_textmessage + i;
}
@@ -4095,14 +4049,14 @@ qboolean CL_LoadProgs( const char *name )
CL_InitCDAudio( "media/cdaudio.txt" );
CL_InitTitles( "titles.txt" );
CL_InitParticles ();
CL_InitViewBeams ();
CL_InitTempEnts ();
CL_InitParticles( );
CL_InitViewBeams( );
CL_InitTempEnts( );
if( !R_InitRenderAPI()) // Xash3D extension
if( !R_InitRenderAPI( )) // Xash3D extension
Con_Reportf( S_WARN "%s: couldn't get render API\n", __func__ );
if( !Mobile_Init() ) // Xash3D FWGS extension: mobile interface
if( !Mobile_Init( )) // Xash3D FWGS extension: mobile interface
Con_Reportf( S_WARN "%s: couldn't get mobility API\n", __func__ );
CL_InitEdicts( cl.maxclients ); // initailize local player and world

View File

@@ -1174,7 +1174,7 @@ static void CL_SendConnectPacket( connprotocol_t proto, int challenge )
else
{
const char *qport = Cvar_VariableString( "net_qport" );
int extensions = NET_EXT_SPLITSIZE;
int extensions = Host_IsLocalGame() ? 0 : NET_EXT_SPLITSIZE;
string key;
ID_GetMD5ForAddress( key, adr, sizeof( key ));
@@ -3678,8 +3678,6 @@ void CL_Init( void )
CL_InitLocal();
VID_Init(); // init video
S_Init(); // init sound
Voice_Init( VOICE_DEFAULT_CODEC, 3, true ); // init voice (do not open the device)
// unreliable buffer. unsed for unreliable commands and voice stream
MSG_Init( &cls.datagram, "cls.datagram", cls.datagram_buf, sizeof( cls.datagram_buf ));
@@ -3689,6 +3687,9 @@ void CL_Init( void )
if( !CL_LoadProgs( libpath ))
Host_Error( "can't initialize %s: %s\n", libpath, COM_GetLibraryError( ));
S_Init(); // init sound
Voice_Init( VOICE_DEFAULT_CODEC, 3, true ); // init voice (do not open the device)
ID_Init();
SteamBroker_Init();

View File

@@ -42,7 +42,7 @@ CL_ParseViewEntity
==================
*/
void CL_ParseViewEntity( sizebuf_t *msg )
static void CL_ParseViewEntity( sizebuf_t *msg )
{
cl.viewentity = MSG_ReadWord( msg );
@@ -161,7 +161,7 @@ CL_ParseSignon
==================
*/
void CL_ParseSignon( sizebuf_t *msg, connprotocol_t proto )
static void CL_ParseSignon( sizebuf_t *msg, connprotocol_t proto )
{
int i = MSG_ReadByte( msg );
@@ -209,7 +209,7 @@ CL_ParseParticles
==================
*/
void CL_ParseParticles( sizebuf_t *msg, connprotocol_t proto )
static void CL_ParseParticles( sizebuf_t *msg, connprotocol_t proto )
{
vec3_t org, dir;
int i, count, color;
@@ -362,7 +362,7 @@ CL_ParseSoundFade
==================
*/
void CL_ParseSoundFade( sizebuf_t *msg )
static void CL_ParseSoundFade( sizebuf_t *msg )
{
int fade_percent = MSG_ReadByte( msg );
int hold_time = MSG_ReadByte( msg );
@@ -616,7 +616,7 @@ CL_ParseCustomization
==================
*/
void CL_ParseCustomization( sizebuf_t *msg )
static void CL_ParseCustomization( sizebuf_t *msg )
{
customization_t *pExistingCustomization;
customization_t *pList;
@@ -704,7 +704,7 @@ CL_ParseResourceRequest
==================
*/
void CL_ParseResourceRequest( sizebuf_t *msg )
static void CL_ParseResourceRequest( sizebuf_t *msg )
{
byte buffer[MAX_INIT_MSG];
int i, arg, nStartIndex;
@@ -776,7 +776,7 @@ CL_ParseFileTransferFailed
==================
*/
void CL_ParseFileTransferFailed( sizebuf_t *msg )
static void CL_ParseFileTransferFailed( sizebuf_t *msg )
{
const char *name = MSG_ReadString( msg );
@@ -796,7 +796,7 @@ void CL_ParseFileTransferFailed( sizebuf_t *msg )
CL_ParseServerData
==================
*/
void CL_ParseServerData( sizebuf_t *msg, connprotocol_t proto )
static void CL_ParseServerData( sizebuf_t *msg, connprotocol_t proto )
{
char gamefolder[MAX_QPATH];
string mapfile;
@@ -1234,7 +1234,7 @@ void CL_ParseBaseline( sizebuf_t *msg, connprotocol_t proto )
CL_ParseLightStyle
================
*/
void CL_ParseLightStyle( sizebuf_t *msg, connprotocol_t proto )
static void CL_ParseLightStyle( sizebuf_t *msg, connprotocol_t proto )
{
int style;
const char *s;
@@ -1255,7 +1255,7 @@ CL_ParseSetAngle
set the view angle to this absolute value
================
*/
void CL_ParseSetAngle( sizebuf_t *msg )
static void CL_ParseSetAngle( sizebuf_t *msg )
{
MSG_ReadVec3Angles( msg, cl.viewangles );
}
@@ -1267,7 +1267,7 @@ CL_ParseAddAngle
add the view angle yaw
================
*/
void CL_ParseAddAngle( sizebuf_t *msg )
static void CL_ParseAddAngle( sizebuf_t *msg )
{
pred_viewangle_t *a;
float delta_yaw;
@@ -1296,7 +1296,7 @@ CL_ParseCrosshairAngle
offset crosshair angles
================
*/
void CL_ParseCrosshairAngle( sizebuf_t *msg )
static void CL_ParseCrosshairAngle( sizebuf_t *msg )
{
cl.crosshairangle[0] = MSG_ReadChar( msg ) * 0.2f;
cl.crosshairangle[1] = MSG_ReadChar( msg ) * 0.2f;
@@ -1310,7 +1310,7 @@ CL_ParseRestore
reading decals, etc.
================
*/
void CL_ParseRestore( sizebuf_t *msg )
static void CL_ParseRestore( sizebuf_t *msg )
{
string filename;
int i, mapCount;
@@ -1337,7 +1337,7 @@ CL_RegisterUserMessage
register new user message or update existing
================
*/
void CL_RegisterUserMessage( sizebuf_t *msg, connprotocol_t proto )
static void CL_RegisterUserMessage( sizebuf_t *msg, connprotocol_t proto )
{
char *pszName;
char szName[17];
@@ -1375,7 +1375,7 @@ CL_UpdateUserinfo
collect userinfo from all players
================
*/
void CL_UpdateUserinfo( sizebuf_t *msg, connprotocol_t proto )
static void CL_UpdateUserinfo( sizebuf_t *msg, connprotocol_t proto )
{
int slot, id;
qboolean active;
@@ -1898,7 +1898,7 @@ CL_ParseVoiceInit
==================
*/
void CL_ParseVoiceInit( sizebuf_t *msg )
static void CL_ParseVoiceInit( sizebuf_t *msg )
{
char *pszCodec = MSG_ReadString( msg );
int quality = MSG_ReadByte( msg );
@@ -1912,7 +1912,7 @@ CL_ParseVoiceData
==================
*/
void CL_ParseVoiceData( sizebuf_t *msg, connprotocol_t proto )
static void CL_ParseVoiceData( sizebuf_t *msg, connprotocol_t proto )
{
int size, idx, frames = 0;
byte received[VOICE_MAX_DATA_SIZE];
@@ -1962,7 +1962,7 @@ CL_ParseResLocation
==================
*/
void CL_ParseResLocation( sizebuf_t *msg )
static void CL_ParseResLocation( sizebuf_t *msg )
{
char *data = MSG_ReadString( msg );
char token[256];
@@ -1991,7 +1991,7 @@ sended from game.dll
normal client ignores any of HLTV messages
==============
*/
void CL_ParseHLTV( sizebuf_t *msg )
static void CL_ParseHLTV( sizebuf_t *msg )
{
switch( MSG_ReadByte( msg ))
{
@@ -2025,7 +2025,7 @@ spectator message (director)
sended from game.dll
==============
*/
void CL_ParseDirector( sizebuf_t *msg )
static void CL_ParseDirector( sizebuf_t *msg )
{
int iSize = MSG_ReadByte( msg );
byte pbuf[256];
@@ -2119,7 +2119,7 @@ Find the client cvar value
and sent it back to the server
==============
*/
void CL_ParseCvarValue( sizebuf_t *msg, const qboolean ext, const connprotocol_t proto )
static void CL_ParseCvarValue( sizebuf_t *msg, const qboolean ext, const connprotocol_t proto )
{
const char *cvarName, *response = NULL;
convar_t *cvar;
@@ -2181,7 +2181,7 @@ CL_ParseExec
Exec map/class specific configs
==============
*/
void CL_ParseExec( sizebuf_t *msg )
static void CL_ParseExec( sizebuf_t *msg )
{
qboolean is_class;
int class_idx;
@@ -2357,25 +2357,150 @@ ACTION MESSAGES
*/
/*
============
CL_ParseCommonDLLMessage
================
CL_ParseFinaleCutscene
parse a message which structure is enforced by DLL compatibility
it should always be the same regardless of protocol used
show display finale or cutscene message
================
*/
static void CL_ParseFinaleCutscene( sizebuf_t *msg, int level )
{
static int msgindex = 0;
client_textmessage_t *text;
int channel;
cl.intermission = level;
channel = msgindex;
msgindex = (msgindex + 1) & (MAX_TEXTCHANNELS - 1);
// grab message channel
text = &cl_textmessage[channel];
// NOTE: svc_finale and svc_cutscene has a
// predefined settings like Quake-style
text->x = -1.0f;
text->y = 0.15f;
text->effect = 2; // scan out effect
text->r1 = 245;
text->g1 = 245;
text->b1 = 245;
text->a1 = 0; // unused
text->r2 = 0;
text->g2 = 0;
text->b2 = 0;
text->a2 = 0;
text->fadein = 0.15f;
text->fadeout = 0.0f;
text->holdtime = 99999.0f;
text->fxtime = 0.0f;
// to prevent grab too long messages
Q_strncpy( (char *)text->pMessage, MSG_ReadString( msg ), 2048 );
if( *text->pMessage == '\0' )
return; // no real text
CL_HudMessage( text->pName );
}
/*
============
CL_ParseCommonMessage
parse a message which is the same across all supported protocols
============
*/
qboolean CL_ParseCommonDLLMessage( sizebuf_t *msg, connprotocol_t proto, int svc_num, int startoffset )
qboolean CL_ParseCommonMessage( sizebuf_t *msg, connprotocol_t proto, int svc_num, int startoffset )
{
int param1, param2;
switch( svc_num )
{
case svc_nop:
break;
case svc_setview:
CL_ParseViewEntity( msg );
break;
case svc_lightstyle:
CL_ParseLightStyle( msg, proto );
break;
case svc_intermission:
cl.intermission = 1;
break;
case svc_finale:
CL_ParseFinaleCutscene( msg, 2 );
break;
case svc_cutscene:
CL_ParseFinaleCutscene( msg, 3 );
break;
default:
return false;
}
return true;
}
/*
============
CL_ParseCommonHLMessage
parse a message which structure is enforced by DLL compatibility
it should always be the same regardless of HL-based protocol used
============
*/
qboolean CL_ParseCommonHLMessage( sizebuf_t *msg, connprotocol_t proto, int svc_num, int startoffset )
{
const char *s;
int param1, param2;
switch( svc_num )
{
case svc_bad:
Host_Error( "svc_bad\n" );
break;
case svc_time:
CL_ParseServerTime( msg, proto );
break;
case svc_print:
Con_Printf( "%s", MSG_ReadString( msg ));
break;
case svc_stufftext:
s = MSG_ReadString( msg );
if( cl_trace_stufftext.value )
{
size_t len = Q_strlen( s );
Con_Printf( "Stufftext: %s%c", s, len && s[len-1] == '\n' ? '\0' : '\n' );
}
#ifdef HACKS_RELATED_HLMODS
// disable Cry Of Fear antisave protection
if( !Q_strnicmp( s, "disconnect", 10 ) && cls.signon != SIGNONS )
break; // too early
#endif
Cbuf_AddFilteredText( s );
break;
case svc_setangle:
CL_ParseSetAngle( msg );
break;
case svc_serverdata:
Cbuf_Execute(); // make sure any stuffed commands are done
CL_ParseServerData( msg, proto );
break;
case svc_updateuserinfo:
CL_UpdateUserinfo( msg, proto );
break;
case svc_particle:
CL_ParseParticles( msg, proto );
break;
case svc_temp_entity:
CL_ParseTempEntity( msg, proto ); // need protocol because message header differs
cl.frames[cl.parsecountmod].graphdata.tentities += MSG_GetNumBytesRead( msg ) - startoffset;
break;
case svc_intermission:
cl.intermission = 1;
case svc_signonnum:
CL_ParseSignon( msg, proto );
break;
case svc_centerprint:
CL_CenterPrint( MSG_ReadString( msg ), 0.25f );
break;
case svc_cdtrack:
param1 = MSG_ReadByte( msg );
@@ -2384,6 +2509,9 @@ qboolean CL_ParseCommonDLLMessage( sizebuf_t *msg, connprotocol_t proto, int svc
param2 = bound( 1, param2, MAX_CDTRACKS ); // loopnum
S_StartBackgroundTrack( clgame.cdtracks[param1-1], clgame.cdtracks[param2-1], 0, false );
break;
case svc_restore:
CL_ParseRestore( msg );
break;
case svc_weaponanim:
param1 = MSG_ReadByte( msg ); // iAnim
param2 = MSG_ReadByte( msg ); // body
@@ -2393,9 +2521,56 @@ qboolean CL_ParseCommonDLLMessage( sizebuf_t *msg, connprotocol_t proto, int svc
param1 = MSG_ReadShort( msg );
Cvar_SetValue( "room_type", param1 );
break;
case svc_addangle:
CL_ParseAddAngle( msg );
break;
case svc_usermessage:
CL_RegisterUserMessage( msg, proto );
break;
case svc_choke:
cl.frames[cls.netchan.incoming_sequence & CL_UPDATE_MASK].choked = true;
cl.frames[cls.netchan.incoming_sequence & CL_UPDATE_MASK].receivedtime = -2.0;
break;
case svc_resourcerequest:
CL_ParseResourceRequest( msg );
break;
case svc_customization:
CL_ParseCustomization( msg );
break;
case svc_crosshairangle:
CL_ParseCrosshairAngle( msg );
break;
case svc_soundfade:
CL_ParseSoundFade( msg );
break;
case svc_filetxferfailed:
CL_ParseFileTransferFailed( msg );
break;
case svc_hltv:
CL_ParseHLTV( msg );
break;
case svc_director:
CL_ParseDirector( msg );
break;
case svc_voiceinit:
CL_ParseVoiceInit( msg );
break;
case svc_voicedata:
CL_ParseVoiceData( msg, proto );
cl.frames[cl.parsecountmod].graphdata.voicebytes += MSG_GetNumBytesRead( msg ) - startoffset;
break;
case svc_resourcelocation:
CL_ParseResLocation( msg );
break;
case svc_querycvarvalue:
CL_ParseCvarValue( msg, false, proto );
break;
case svc_querycvarvalue2:
CL_ParseCvarValue( msg, true, proto );
break;
case svc_exec:
CL_ParseExec( msg );
break;
default:
return false;
}
@@ -2415,7 +2590,6 @@ void CL_ParseServerMessage( sizebuf_t *msg )
size_t bufStart, playerbytes;
int cmd;
int old_background;
const char *s;
// parse the message
while( 1 )
@@ -2438,18 +2612,15 @@ void CL_ParseServerMessage( sizebuf_t *msg )
// record command for debugging spew on parse problem
CL_Parse_RecordCommand( cmd, bufStart );
if( CL_ParseCommonDLLMessage( msg, PROTO_CURRENT, cmd, bufStart ))
if( CL_ParseCommonMessage( msg, PROTO_CURRENT, cmd, bufStart ))
continue;
if( CL_ParseCommonHLMessage( msg, PROTO_CURRENT, cmd, bufStart ))
continue;
// other commands
switch( cmd )
{
case svc_bad:
Host_Error( "svc_bad\n" );
break;
case svc_nop:
// this does nothing
break;
case svc_disconnect:
CL_Drop ();
Host_AbortCurrentFrame ();
@@ -2497,48 +2668,10 @@ void CL_ParseServerMessage( sizebuf_t *msg )
cls.connect_retry = 0;
}
break;
case svc_setview:
CL_ParseViewEntity( msg );
break;
case svc_sound:
CL_ParseSoundPacket( msg, false );
cl.frames[cl.parsecountmod].graphdata.sound += MSG_GetNumBytesRead( msg ) - bufStart;
break;
case svc_time:
CL_ParseServerTime( msg, PROTO_CURRENT );
break;
case svc_print:
Con_Printf( "%s", MSG_ReadString( msg ));
break;
case svc_stufftext:
s = MSG_ReadString( msg );
if( cl_trace_stufftext.value )
{
size_t len = Q_strlen( s );
Con_Printf( "Stufftext: %s%c", s, len && s[len-1] == '\n' ? '\0' : '\n' );
}
#ifdef HACKS_RELATED_HLMODS
// disable Cry Of Fear antisave protection
if( !Q_strnicmp( s, "disconnect", 10 ) && cls.signon != SIGNONS )
break; // too early
#endif
Cbuf_AddFilteredText( s );
break;
case svc_setangle:
CL_ParseSetAngle( msg );
break;
case svc_serverdata:
Cbuf_Execute(); // make sure any stuffed commands are done
CL_ParseServerData( msg, PROTO_CURRENT );
break;
case svc_lightstyle:
CL_ParseLightStyle( msg, PROTO_CURRENT );
break;
case svc_updateuserinfo:
CL_UpdateUserinfo( msg, PROTO_CURRENT );
break;
case svc_deltatable:
Delta_ParseTableField( msg );
break;
@@ -2552,9 +2685,6 @@ void CL_ParseServerMessage( sizebuf_t *msg )
case svc_pings:
CL_UpdateUserPings( msg );
break;
case svc_particle:
CL_ParseParticles( msg, PROTO_CURRENT );
break;
case svc_restoresound:
CL_ParseSoundPacket( msg, true );
cl.frames[cl.parsecountmod].graphdata.sound += MSG_GetNumBytesRead( msg ) - bufStart;
@@ -2572,30 +2702,9 @@ void CL_ParseServerMessage( sizebuf_t *msg )
case svc_setpause:
cl.paused = ( MSG_ReadOneBit( msg ) != 0 );
break;
case svc_signonnum:
CL_ParseSignon( msg, PROTO_CURRENT );
break;
case svc_centerprint:
CL_CenterPrint( MSG_ReadString( msg ), 0.25f );
break;
case svc_finale:
CL_ParseFinaleCutscene( msg, 2 );
break;
case svc_restore:
CL_ParseRestore( msg );
break;
case svc_cutscene:
CL_ParseFinaleCutscene( msg, 3 );
break;
case svc_bspdecal:
CL_ParseStaticDecal( msg );
break;
case svc_addangle:
CL_ParseAddAngle( msg );
break;
case svc_usermessage:
CL_RegisterUserMessage( msg, PROTO_CURRENT );
break;
case svc_packetentities:
playerbytes = CL_ParsePacketEntities( msg, false, PROTO_CURRENT );
cl.frames[cl.parsecountmod].graphdata.players += playerbytes;
@@ -2606,53 +2715,12 @@ void CL_ParseServerMessage( sizebuf_t *msg )
cl.frames[cl.parsecountmod].graphdata.players += playerbytes;
cl.frames[cl.parsecountmod].graphdata.entities += MSG_GetNumBytesRead( msg ) - bufStart - playerbytes;
break;
case svc_choke:
cl.frames[cls.netchan.incoming_sequence & CL_UPDATE_MASK].choked = true;
cl.frames[cls.netchan.incoming_sequence & CL_UPDATE_MASK].receivedtime = -2.0;
break;
case svc_resourcelist:
CL_ParseResourceList( msg, PROTO_CURRENT );
break;
case svc_deltamovevars:
CL_ParseMovevars( msg );
break;
case svc_resourcerequest:
CL_ParseResourceRequest( msg );
break;
case svc_customization:
CL_ParseCustomization( msg );
break;
case svc_crosshairangle:
CL_ParseCrosshairAngle( msg );
break;
case svc_soundfade:
CL_ParseSoundFade( msg );
break;
case svc_filetxferfailed:
CL_ParseFileTransferFailed( msg );
break;
case svc_hltv:
CL_ParseHLTV( msg );
break;
case svc_voiceinit:
CL_ParseVoiceInit( msg );
break;
case svc_voicedata:
CL_ParseVoiceData( msg, PROTO_CURRENT );
cl.frames[cl.parsecountmod].graphdata.voicebytes += MSG_GetNumBytesRead( msg ) - bufStart;
break;
case svc_resourcelocation:
CL_ParseResLocation( msg );
break;
case svc_querycvarvalue:
CL_ParseCvarValue( msg, false, PROTO_CURRENT );
break;
case svc_querycvarvalue2:
CL_ParseCvarValue( msg, true, PROTO_CURRENT );
break;
case svc_exec:
CL_ParseExec( msg );
break;
default:
CL_ParseUserMessage( msg, cmd, PROTO_CURRENT );
cl.frames[cl.parsecountmod].graphdata.usr += MSG_GetNumBytesRead( msg ) - bufStart;

View File

@@ -569,22 +569,15 @@ void CL_ParseGoldSrcServerMessage( sizebuf_t *msg )
// record command for debugging spew on parse problem
CL_Parse_RecordCommand( cmd, bufStart );
if( CL_ParseCommonDLLMessage( msg, PROTO_GOLDSRC, cmd, bufStart ))
if( CL_ParseCommonMessage( msg, PROTO_GOLDSRC, cmd, bufStart ))
continue;
if( CL_ParseCommonHLMessage( msg, PROTO_GOLDSRC, cmd, bufStart ))
continue;
// other commands
switch( cmd )
{
case svc_bad:
Host_Error( "svc_bad\n" );
break;
case svc_nop:
case svc_spawnstatic:
case svc_goldsrc_damage:
case svc_goldsrc_killedmonster:
case svc_goldsrc_foundsecret:
// this does nothing
break;
case svc_disconnect:
s = MSG_ReadString( msg );
if( !COM_StringEmpty( s ))
@@ -603,47 +596,10 @@ void CL_ParseGoldSrcServerMessage( sizebuf_t *msg )
if( param1 != PROTOCOL_GOLDSRC_VERSION )
Host_Error( "Server use invalid protocol (%i should be %i)\n", param1, PROTOCOL_GOLDSRC_VERSION );
break;
case svc_setview:
CL_ParseViewEntity( msg );
break;
case svc_sound:
CL_ParseSoundPacketGS( msg );
cl.frames[cl.parsecountmod].graphdata.sound += MSG_GetNumBytesRead( msg ) - bufStart;
break;
case svc_time:
CL_ParseServerTime( msg, PROTO_GOLDSRC );
break;
case svc_print:
Con_Printf( "%s", MSG_ReadString( msg ));
break;
case svc_stufftext:
s = MSG_ReadString( msg );
if( cl_trace_stufftext.value )
{
size_t len = Q_strlen( s );
Con_Printf( "Stufftext: %s%c", s, len && s[len-1] == '\n' ? '\0' : '\n' );
}
#ifdef HACKS_RELATED_HLMODS
// disable Cry Of Fear antisave protection
if( !Q_strnicmp( s, "disconnect", 10 ) && cls.signon != SIGNONS )
break; // too early
#endif
Cbuf_AddFilteredText( s );
break;
case svc_setangle:
CL_ParseSetAngle( msg );
break;
case svc_serverdata:
Cbuf_Execute(); // make sure any stuffed commands are done
CL_ParseServerData( msg, PROTO_GOLDSRC );
break;
case svc_lightstyle:
CL_ParseLightStyle( msg, PROTO_GOLDSRC );
break;
case svc_updateuserinfo:
CL_UpdateUserinfo( msg, PROTO_GOLDSRC );
break;
case svc_deltatable:
Delta_ParseTableField_GS( msg );
break;
@@ -663,8 +619,9 @@ void CL_ParseGoldSrcServerMessage( sizebuf_t *msg )
CL_UpdateUserPings( msg );
MSG_EndBitWriting( msg );
break;
case svc_particle:
CL_ParseParticles( msg, PROTO_GOLDSRC );
case svc_goldsrc_damage:
case svc_spawnstatic:
// this does nothing
break;
case svc_event_reliable:
MSG_StartBitWriting( msg );
@@ -680,35 +637,18 @@ void CL_ParseGoldSrcServerMessage( sizebuf_t *msg )
case svc_setpause:
cl.paused = ( MSG_ReadByte( msg ) != 0 );
break;
case svc_signonnum:
CL_ParseSignon( msg, PROTO_GOLDSRC );
break;
case svc_centerprint:
CL_CenterPrint( MSG_ReadString( msg ), 0.25f );
case svc_goldsrc_killedmonster:
case svc_goldsrc_foundsecret:
// this does nothing
break;
case svc_goldsrc_spawnstaticsound:
CL_ParseSpawnStaticSound( msg );
break;
case svc_finale:
CL_ParseFinaleCutscene( msg, 2 );
break;
case svc_restore:
CL_ParseRestore( msg );
break;
case svc_cutscene:
CL_ParseFinaleCutscene( msg, 3 );
break;
case svc_goldsrc_decalname:
param1 = MSG_ReadByte( msg );
s = MSG_ReadString( msg );
Q_strncpy( host.draw_decals[param1], s, sizeof( host.draw_decals[param1] ));
break;
case svc_addangle:
CL_ParseAddAngle( msg );
break;
case svc_usermessage:
CL_RegisterUserMessage( msg, PROTO_GOLDSRC );
break;
case svc_packetentities:
playerbytes = CL_ParsePacketEntitiesGS( msg, false );
cl.frames[cl.parsecountmod].graphdata.players += playerbytes;
@@ -719,10 +659,6 @@ void CL_ParseGoldSrcServerMessage( sizebuf_t *msg )
cl.frames[cl.parsecountmod].graphdata.players += playerbytes;
cl.frames[cl.parsecountmod].graphdata.entities += MSG_GetNumBytesRead( msg ) - bufStart - playerbytes;
break;
case svc_choke:
cl.frames[cls.netchan.incoming_sequence & CL_UPDATE_MASK].choked = true;
cl.frames[cls.netchan.incoming_sequence & CL_UPDATE_MASK].receivedtime = -2.0;
break;
case svc_resourcelist:
MSG_StartBitWriting( msg );
CL_ParseResourceList( msg, PROTO_GOLDSRC );
@@ -731,34 +667,6 @@ void CL_ParseGoldSrcServerMessage( sizebuf_t *msg )
case svc_deltamovevars:
CL_ParseNewMovevars( msg );
break;
case svc_resourcerequest:
CL_ParseResourceRequest( msg );
break;
case svc_customization:
CL_ParseCustomization( msg );
break;
case svc_crosshairangle:
CL_ParseCrosshairAngle( msg );
break;
case svc_soundfade:
CL_ParseSoundFade( msg );
break;
case svc_filetxferfailed:
CL_ParseFileTransferFailed( msg );
break;
case svc_hltv:
CL_ParseHLTV( msg );
break;
case svc_voiceinit:
CL_ParseVoiceInit( msg );
break;
case svc_voicedata:
CL_ParseVoiceData( msg, PROTO_GOLDSRC );
cl.frames[cl.parsecountmod].graphdata.voicebytes += MSG_GetNumBytesRead( msg ) - bufStart;
break;
case svc_resourcelocation:
CL_ParseResLocation( msg );
break;
case svc_goldsrc_sendextrainfo:
CL_ParseExtraInfo( msg );
break;
@@ -768,15 +676,6 @@ void CL_ParseGoldSrcServerMessage( sizebuf_t *msg )
Con_Reportf( S_ERROR "%s: svc_goldsrc_timescale: implement me!\n", __func__ );
MSG_ReadFloat( msg );
break;
case svc_querycvarvalue:
CL_ParseCvarValue( msg, false, PROTO_GOLDSRC );
break;
case svc_querycvarvalue2:
CL_ParseCvarValue( msg, true, PROTO_GOLDSRC );
break;
case svc_exec:
CL_ParseExec( msg );
break;
default:
CL_ParseUserMessage( msg, cmd, PROTO_GOLDSRC );
cl.frames[cl.parsecountmod].graphdata.usr += MSG_GetNumBytesRead( msg ) - bufStart;

View File

@@ -213,6 +213,7 @@ static void CL_ParseQuakeServerInfo( sizebuf_t *msg )
clgame.maxEntities = bound( 600, clgame.maxEntities, MAX_EDICTS );
clgame.maxModels = MAX_MODELS;
Q_strncpy( clgame.maptitle, MSG_ReadString( msg ), sizeof( clgame.maptitle ));
Host_ValidateEngineFeatures( ENGINE_FEATURES_MASK, ENGINE_QUAKE_COMPATIBLE );
// Re-init hud video, especially if we changed game directories
clgame.dllFuncs.pfnVidInit();
@@ -929,12 +930,12 @@ void CL_ParseQuakeMessage( sizebuf_t *msg )
// record command for debugging spew on parse problem
CL_Parse_RecordCommand( cmd, bufStart );
if( CL_ParseCommonMessage( msg, PROTO_QUAKE, cmd, bufStart ))
continue;
// other commands
switch( cmd )
{
case svc_nop:
// this does nothing
break;
case svc_disconnect:
CL_DemoCompleted ();
break;
@@ -946,9 +947,6 @@ void CL_ParseQuakeMessage( sizebuf_t *msg )
if( param1 != PROTOCOL_VERSION_QUAKE )
Host_Error( "Server is protocol %i instead of %i\n", param1, PROTOCOL_VERSION_QUAKE );
break;
case svc_setview:
CL_ParseViewEntity( msg );
break;
case svc_sound:
CL_ParseQuakeSound( msg );
cl.frames[cl.parsecountmod].graphdata.sound += MSG_GetNumBytesRead( msg ) - bufStart;
@@ -973,9 +971,6 @@ void CL_ParseQuakeMessage( sizebuf_t *msg )
Cbuf_Execute(); // make sure any stuffed commands are done
CL_ParseQuakeServerInfo( msg );
break;
case svc_lightstyle:
CL_ParseLightStyle( msg, PROTO_QUAKE );
break;
case svc_updatename:
param1 = MSG_ReadByte( msg );
Q_strncpy( cl.players[param1].name, MSG_ReadString( msg ), sizeof( cl.players[0].name ));
@@ -1028,8 +1023,7 @@ void CL_ParseQuakeMessage( sizebuf_t *msg )
CL_ParseQuakeSignon( msg );
break;
case svc_centerprint:
str = MSG_ReadString( msg );
CL_DispatchUserMessage( "HudText", Q_strlen( str ) + 1, (void *)str );
CL_HudMessage( MSG_ReadString( msg ));
break;
case svc_killedmonster:
CL_DispatchQuakeMessage( "KillMonster" ); // just an event
@@ -1040,12 +1034,6 @@ void CL_ParseQuakeMessage( sizebuf_t *msg )
case svc_spawnstaticsound:
CL_ParseQuakeStaticSound( msg );
break;
case svc_intermission:
cl.intermission = 1;
break;
case svc_finale:
CL_ParseFinaleCutscene( msg, 2 );
break;
case svc_cdtrack:
param1 = MSG_ReadByte( msg );
param1 = bound( 0, param1, MAX_CDTRACKS - 1 ); // tracknum
@@ -1058,15 +1046,12 @@ void CL_ParseQuakeMessage( sizebuf_t *msg )
case svc_sellscreen:
Cmd_ExecuteString( "help" ); // open quake menu
break;
case svc_cutscene:
CL_ParseFinaleCutscene( msg, 3 );
case svc_showlmp:
CL_ParseNehahraShowLMP( msg );
break;
case svc_hidelmp:
CL_ParseNehahraHideLMP( msg );
break;
case svc_showlmp:
CL_ParseNehahraShowLMP( msg );
break;
case svc_skybox:
Q_strncpy( clgame.movevars.skyName, MSG_ReadString( msg ), sizeof( clgame.movevars.skyName ));
break;

View File

@@ -744,7 +744,7 @@ void SCR_LoadCreditsFont( void )
success = Con_LoadVariableWidthFont( "gfx/creditsfont.fnt", font, scale, &hud_fontrender, TF_FONT );
if( !success )
success = Con_LoadFixedWidthFont( "gfx/conchars", font, scale, &hud_fontrender, TF_FONT );
success = Con_LoadFixedWidthFont( "gfx/conchars", font, scale, &hud_fontrender, TF_FONT|TF_NEAREST );
// copy font size for client.dll
if( success )

View File

@@ -535,6 +535,7 @@ void V_PostRender( void )
SCR_DrawEnts();
SCR_DrawNetGraph();
SCR_DrawUserCmd();
Joy_DrawDebug();
SV_DrawOrthoTriangles();
CL_DrawDemoRecording();
CL_DrawHUD( CL_CHANGELEVEL );

View File

@@ -26,6 +26,7 @@ GNU General Public License for more details.
#include "pm_defs.h"
#include "ref_params.h"
#include "render_api.h"
#include "sound_api.h"
#include "cdll_exp.h"
#include "screenfade.h"
#include "protocol.h"
@@ -36,11 +37,6 @@ GNU General Public License for more details.
#include "voice.h"
#include "q_client.h"
// client sprite types
#define SPR_CLIENT 0 // client sprite for temp-entities or user-textures
#define SPR_HUDSPRITE 1 // hud sprite
#define SPR_MAPSPRITE 2 // contain overview.bmp that diced into frames 128x128
//=============================================================================
typedef struct netbandwithgraph_s
{
@@ -111,6 +107,8 @@ extern int CL_UPDATE_BACKUP;
#define MAX_EX_INTERP 0.1f
#define MAX_TEXTCHANNELS 8 // must be power of two (GoldSrc uses 4 channels)
#define CL_MIN_RESEND_TIME 1.5f // mininum time gap (in seconds) before a subsequent connection request is sent.
#define CL_MAX_RESEND_TIME 20.0f // max time. The cvar cl_resend is bounded by these.
@@ -310,6 +308,14 @@ typedef enum
CL_CHANGELEVEL, // draw 'loading' during changelevel
} scrstate_t;
// client sprite types
enum
{
SPR_CLIENT = 0, // client sprite for temp-entities or user-textures
SPR_HUDSPRITE, // hud sprite
SPR_MAPSPRITE, // contain overview.bmp that diced into frames 128x128
};
typedef struct
{
char name[32];
@@ -449,6 +455,7 @@ typedef struct
void *hInstance; // pointer to client.dll
cldll_func_t dllFuncs; // dll exported funcs
render_interface_t drawFuncs; // custom renderer support
sound_interface_t soundFuncs; // custom sound support
poolhandle_t mempool; // client edicts pool
string mapname; // map name
string maptitle; // display map title
@@ -731,10 +738,7 @@ extern convar_t cl_fixmodelinterpolationartifacts;
//=============================================================================
void CL_SetLightstyle( int style, const char* s, float f );
void CL_DecayLights( void );
dlight_t *CL_GetDynamicLight( int number );
dlight_t *CL_GetEntityLight( int number );
extern client_textmessage_t cl_textmessage[MAX_TEXTCHANNELS];
//=================================================
@@ -853,6 +857,7 @@ void CL_FreeEdicts( void );
void CL_ClearWorld( void );
void CL_DrawCenterPrint( void );
void CL_ClearSpriteTextures( void );
void CL_HudMessage( const char *pMessage );
void CL_CenterPrint( const char *text, float y );
client_textmessage_t *CL_TextMessageGet( const char *pName );
void NetAPI_CancelAllRequests( void );
@@ -923,45 +928,23 @@ static inline cl_entity_t *CL_GetLocalPlayer( void )
//
// cl_parse.c
//
void CL_ParseSetAngle( sizebuf_t *msg );
void CL_ParseServerData( sizebuf_t *msg, connprotocol_t proto );
void CL_ParseLightStyle( sizebuf_t *msg, connprotocol_t proto );
void CL_UpdateUserinfo( sizebuf_t *msg, connprotocol_t proto );
void CL_ParseResource( sizebuf_t *msg );
void CL_ParseClientData( sizebuf_t *msg, connprotocol_t proto );
void CL_UpdateUserPings( sizebuf_t *msg );
void CL_ParseParticles( sizebuf_t *msg, connprotocol_t proto );
void CL_ParseBaseline( sizebuf_t *msg, connprotocol_t proto );
void CL_ParseSignon( sizebuf_t *msg, connprotocol_t proto );
void CL_ParseRestore( sizebuf_t *msg );
void CL_ParseStaticDecal( sizebuf_t *msg );
void CL_ParseAddAngle( sizebuf_t *msg );
void CL_RegisterUserMessage( sizebuf_t *msg, connprotocol_t proto );
void CL_ParseResourceList( sizebuf_t *msg, connprotocol_t proto );
void CL_ParseMovevars( sizebuf_t *msg );
void CL_ParseResourceRequest( sizebuf_t *msg );
void CL_ParseCustomization( sizebuf_t *msg );
void CL_ParseCrosshairAngle( sizebuf_t *msg );
void CL_ParseSoundFade( sizebuf_t *msg );
void CL_ParseFileTransferFailed( sizebuf_t *msg );
void CL_ParseHLTV( sizebuf_t *msg );
void CL_ParseDirector( sizebuf_t *msg );
void CL_ParseVoiceInit( sizebuf_t *msg );
void CL_ParseVoiceData( sizebuf_t *msg, connprotocol_t proto );
void CL_ParseResLocation( sizebuf_t *msg );
void CL_ParseCvarValue( sizebuf_t *msg, const qboolean ext, const connprotocol_t proto );
void CL_ParseServerMessage( sizebuf_t *msg );
qboolean CL_ParseCommonDLLMessage( sizebuf_t *msg, connprotocol_t proto, int svc_num, int startoffset );
qboolean CL_ParseCommonMessage( sizebuf_t *msg, connprotocol_t proto, int svc_num, int startoffset );
qboolean CL_ParseCommonHLMessage( sizebuf_t *msg, connprotocol_t proto, int svc_num, int startoffset );
void CL_ParseTempEntity( sizebuf_t *msg, connprotocol_t proto );
qboolean CL_DispatchUserMessage( const char *pszName, int iSize, void *pbuf );
qboolean CL_RequestMissingResources( void );
void CL_RegisterResources( sizebuf_t *msg, connprotocol_t proto );
void CL_ParseViewEntity( sizebuf_t *msg );
void CL_ParseServerTime( sizebuf_t *msg, connprotocol_t proto );
void CL_ParseUserMessage( sizebuf_t *msg, int svc_num, connprotocol_t proto );
void CL_ParseFinaleCutscene( sizebuf_t *msg, int level );
void CL_ParseTextMessage( sizebuf_t *msg );
void CL_ParseExec( sizebuf_t *msg );
void CL_BatchResourceRequest( qboolean initialize );
int CL_EstimateNeededResources( void );
@@ -1083,6 +1066,10 @@ void R_AddEfrags( cl_entity_t *ent );
// cl_tent.c
//
struct particle_s;
void CL_SetLightstyle( int style, const char* s, float f );
void CL_DecayLights( void );
dlight_t *CL_GetDynamicLight( int number );
dlight_t *CL_GetEntityLight( int number );
void CL_WeaponAnim( int iAnim, int body );
void CL_ClearEffects( void );
void CL_ClearEfrags( void );

View File

@@ -1573,7 +1573,7 @@ void Key_Console( int key )
}
// command completion
if( key == K_TAB || key == K_L2_BUTTON )
if( key == K_TAB || key == K_L2_BUTTON || key == K_LTRIGGER )
{
Con_CompleteCommand( &con.input, true );
Con_Bottom();

View File

@@ -24,8 +24,6 @@ GNU General Public License for more details.
#define SHRT_MAX 0x7FFF
#endif
#define MAX_AXES JOY_AXIS_NULL
// index - axis num come from event
// value - inner axis
static engineAxis_t joyaxesmap[MAX_AXES] =
@@ -42,7 +40,9 @@ static struct joy_axis_s
{
short val;
short prevval;
short rawval; // value before deadzone zeroing, for debug display
} joyaxis[MAX_AXES] = { 0 };
static qboolean joy_initialized;
static CVAR_DEFINE_AUTO( joy_pitch, "100.0", FCVAR_ARCHIVE | FCVAR_FILTERABLE, "joystick pitch sensitivity" );
@@ -62,6 +62,15 @@ static CVAR_DEFINE_AUTO( joy_axis_binding, "sfpyrl", FCVAR_ARCHIVE | FCVAR_FILTE
CVAR_DEFINE_AUTO( joy_enable, "1", FCVAR_ARCHIVE | FCVAR_FILTERABLE, "enable joystick" );
static CVAR_DEFINE_AUTO( joy_have_gyro, "0", FCVAR_READ_ONLY, "tells whether current active gamepad has gyroscope or not" );
static CVAR_DEFINE_AUTO( joy_calibrated, "0", FCVAR_READ_ONLY, "tells whether current active gamepad gyroscope has been calibrated or not" );
static CVAR_DEFINE_AUTO( joy_gyro_pitch, "1.0", FCVAR_ARCHIVE | FCVAR_FILTERABLE, "gyroscope sensitivity for looking up and down" );
static CVAR_DEFINE_AUTO( joy_gyro_yaw, "1.0", FCVAR_ARCHIVE | FCVAR_FILTERABLE, "gyroscope sensitivity for turning left and right" );
static CVAR_DEFINE_AUTO( joy_gyro_roll, "0.0", FCVAR_ARCHIVE | FCVAR_FILTERABLE, "gyroscope sensitivity when tilting the device sideways" );
static CVAR_DEFINE_AUTO( joy_debug, "0", 0, "visualize gamepad axes and buttons" );
// stores the latest instantaneous gyroscope rotation rates (in rad/s) from platform input
static vec3_t joy_gyro_speed;
// last non-zero gyro speed, retained for debug display after joy_gyro_speed is cleared
static vec3_t joy_gyro_speed_display;
/*
============
@@ -228,6 +237,8 @@ static void Joy_ProcessStick( const engineAxis_t engineAxis, short value )
break;
}
joyaxis[engineAxis].rawval = value;
if( value < deadzone && value > -deadzone )
value = 0; // caught new event in deadzone, fill it with zero(no motion)
@@ -257,7 +268,12 @@ Axis events
*/
void Joy_AxisMotionEvent( engineAxis_t engineAxis, short value )
{
if( engineAxis >= JOY_AXIS_NULL )
if( engineAxis >= MAX_AXES )
return;
engineAxis = joyaxesmap[engineAxis];
if( engineAxis >= MAX_AXES )
return;
if( value == joyaxis[engineAxis].val )
@@ -278,7 +294,8 @@ Gyroscope events
*/
void Joy_GyroEvent( vec3_t data )
{
VectorCopy( data, joy_gyro_speed );
VectorCopy( data, joy_gyro_speed_display );
}
/*
@@ -290,7 +307,7 @@ Append movement from axis. Called everyframe
*/
void Joy_FinalizeMove( float *fw, float *side, float *dpitch, float *dyaw )
{
if( !Joy_IsActive() )
if( !Joy_IsActive( ))
return;
if( FBitSet( joy_axis_binding.flags, FCVAR_CHANGED ) )
@@ -308,7 +325,7 @@ void Joy_FinalizeMove( float *fw, float *side, float *dpitch, float *dyaw )
case 'p': joyaxesmap[i] = JOY_AXIS_PITCH; break;
case 'r': joyaxesmap[i] = JOY_AXIS_RT; break;
case 'l': joyaxesmap[i] = JOY_AXIS_LT; break;
default : joyaxesmap[i] = JOY_AXIS_NULL; break;
default : joyaxesmap[i] = MAX_AXES; break;
}
}
@@ -317,8 +334,193 @@ void Joy_FinalizeMove( float *fw, float *side, float *dpitch, float *dyaw )
*fw -= joy_forward.value * (float)joyaxis[JOY_AXIS_FWD ].val/(float)SHRT_MAX; // must be form -1.0 to 1.0
*side += joy_side.value * (float)joyaxis[JOY_AXIS_SIDE].val/(float)SHRT_MAX;
*dpitch -= joy_pitch.value * (float)joyaxis[JOY_AXIS_PITCH].val/(float)SHRT_MAX * host.realframetime;
*dyaw += joy_yaw.value * (float)joyaxis[JOY_AXIS_YAW ].val/(float)SHRT_MAX * host.realframetime;
*dpitch += joy_pitch.value * (float)joyaxis[JOY_AXIS_PITCH].val/(float)SHRT_MAX * host.realframetime;
*dyaw -= joy_yaw.value * (float)joyaxis[JOY_AXIS_YAW ].val/(float)SHRT_MAX * host.realframetime;
if( joy_have_gyro.value && (int)joy_calibrated.value == JOY_CALIBRATED )
{
float pitch_speed = joy_gyro_speed[0] * ( 180.0f / M_PI );
float yaw_speed = joy_gyro_speed[1] * ( 180.0f / M_PI );
float roll_speed = joy_gyro_speed[2] * ( 180.0f / M_PI );
*dpitch -= joy_gyro_pitch.value * pitch_speed * host.realframetime;
*dyaw += joy_gyro_yaw.value * yaw_speed * host.realframetime;
*dyaw += joy_gyro_roll.value * roll_speed * host.realframetime;
}
VectorClear( joy_gyro_speed );
}
/*
=============
Joy_DrawDebug
Draw gamepad axes and buttons for debugging (joy_debug 1)
=============
*/
void Joy_DrawDebug( void )
{
static const int buttons[] =
{
K_A_BUTTON, K_B_BUTTON, K_X_BUTTON, K_Y_BUTTON, 0,
K_L1_BUTTON, K_R1_BUTTON, 0,
K_L2_BUTTON, K_R2_BUTTON, 0,
K_BACK_BUTTON, K_MODE_BUTTON, K_START_BUTTON, 0,
K_LSTICK, K_RSTICK, 0,
// simulated buttons
K_DPAD_UP, K_DPAD_DOWN, K_DPAD_LEFT, K_DPAD_RIGHT, 0,
K_LTRIGGER, K_RTRIGGER, 0,
// additional buttons
K_C_BUTTON, K_Z_BUTTON, K_MISC_BUTTON, 0,
K_PADDLE1_BUTTON, K_PADDLE2_BUTTON, K_PADDLE3_BUTTON, K_PADDLE4_BUTTON, 0
};
static const char *const axisnames[MAX_AXES] = { "SIDE", "FWD", "PITCH", "YAW", "RT", "LT" };
// deadzone for sticks, threshold for triggers
static const convar_t *const axis_threshold[MAX_AXES] =
{
&joy_side_deadzone,
&joy_forward_deadzone,
&joy_pitch_deadzone,
&joy_yaw_deadzone,
&joy_rt_threshold,
&joy_lt_threshold,
};
cl_font_t *font = Con_GetCurFont();
if( !joy_debug.value || !Joy_IsActive( ))
return;
float x = 8;
float y = 100;
const float bar_w = 100;
const float halfbar_w = bar_w * 0.5f;
const float bar_h = font->charHeight - 2;
const float center = x + halfbar_w;
const rgba_t bar_backcolor = { 40, 40, 40, 180 };
const rgba_t bar_fillcolor = { 100, 200, 100, 200 };
// draw axes as labeled bars
for( int i = 0; i < MAX_AXES; i++ )
{
qboolean is_trigger = ( i >= JOY_AXIS_RT );
float fval = (float)joyaxis[i].val / SHRT_MAX;
float fprev = (float)joyaxis[i].prevval / SHRT_MAX;
float fthreshold = axis_threshold[i]->value / SHRT_MAX;
CL_DrawString( x, y, axisnames[i], g_color_table[7], font, 0 );
y += font->charHeight;
// background
ref.dllFuncs.FillRGBA( kRenderTransTexture, x, y, bar_w, bar_h, bar_backcolor[0], bar_backcolor[1], bar_backcolor[2], bar_backcolor[3] );
if( is_trigger )
{
// fill bar from the left for triggers
float filled = fval * bar_w;
if( filled > 0 )
ref.dllFuncs.FillRGBA( kRenderTransTexture, x, y, filled, bar_h, bar_fillcolor[0], bar_fillcolor[1], bar_fillcolor[2], bar_fillcolor[3] );
// fill threshold with red, and yellow edge marker
float thresh_x = x + fthreshold * bar_w;
ref.dllFuncs.FillRGBA( kRenderTransTexture, x, y, thresh_x - x, bar_h, 180, 40, 40, 120 );
ref.dllFuncs.FillRGBA( kRenderTransTexture, thresh_x, y, 1, bar_h, 255, 200, 0, 220 );
// prevval marker
float prev_x = x + fprev * bar_w;
ref.dllFuncs.FillRGBA( kRenderTransTexture, prev_x, y, 1, bar_h, 200, 200, 200, 180 );
}
else
{
// for sticks bar is filled from the center
float filled = fval * halfbar_w;
ref.dllFuncs.FillRGBA( kRenderTransTexture, center + Q_min( 0, filled ), y, fabs( filled ), bar_h, bar_fillcolor[0], bar_fillcolor[1], bar_fillcolor[2], bar_fillcolor[3] );
// fill deadzone with red, and yellow edge marker
float thresh_x = fthreshold * halfbar_w;
ref.dllFuncs.FillRGBA( kRenderTransTexture, center - thresh_x, y, thresh_x * 2, bar_h, 180, 40, 40, 120 );
ref.dllFuncs.FillRGBA( kRenderTransTexture, center + thresh_x, y, 1, bar_h, 255, 200, 0, 220 );
ref.dllFuncs.FillRGBA( kRenderTransTexture, center - thresh_x, y, 1, bar_h, 255, 200, 0, 220 );
// mark the center
ref.dllFuncs.FillRGBA( kRenderTransTexture, center, y, 1, bar_h, 180, 180, 180, 220 );
// now draw the actual value
float raw_x = center + ((float)joyaxis[i].rawval / SHRT_MAX * halfbar_w );
ref.dllFuncs.FillRGBA( kRenderTransTexture, raw_x, y, 1, bar_h, 255, 255, 100, 200 );
// prevval marker
float prev_x = center + fprev * halfbar_w;
ref.dllFuncs.FillRGBA( kRenderTransTexture, prev_x, y, 1, bar_h, 200, 200, 200, 180 );
}
y += bar_h + 2;
}
// draw gyro if available
if( joy_have_gyro.value )
{
static const char *gyronames[3] = { "GYRO P", "GYRO Y", "GYRO R" };
for( int i = 0; i < 3; i++ )
{
// clamp to +-5 rad/s for display
float fval = joy_gyro_speed_display[i] / 5.0f;
fval = bound( -1.0f, fval, 1.0f );
CL_DrawString( x, y, gyronames[i], g_color_table[7], font, 0 );
y += font->charHeight;
// background
ref.dllFuncs.FillRGBA( kRenderTransTexture, x, y, bar_w, bar_h, bar_backcolor[0], bar_backcolor[1], bar_backcolor[2], bar_backcolor[3] );
// for gyro bar is filled from the center
float filled = fval * halfbar_w;
ref.dllFuncs.FillRGBA( kRenderTransTexture, center + Q_min( 0, filled ), y, fabs( filled ), bar_h, bar_fillcolor[0], bar_fillcolor[1], bar_fillcolor[2], bar_fillcolor[3] );
// mark the center
ref.dllFuncs.FillRGBA( kRenderTransTexture, center, y, 1, bar_h, 180, 180, 180, 220 );
// gyro doesn't have deadzones or prevval like axes
y += bar_h + 2;
}
}
// draw buttons in a row
y += 4;
x = 8;
for( int i = 0; i < ARRAYSIZE( buttons ); i++ )
{
if( !buttons[i] )
{
x = 8;
y += font->charHeight;
continue;
}
qboolean pressed = Key_IsDown( buttons[i] );
int btn_w = 0;
rgba_t color;
string name;
Q_strncpy( name, Key_KeynumToString( buttons[i] ), sizeof( name ));
char *p = Q_strstr( name, "_BUTTON" );
if( p )
*p = 0;
MakeRGBA( color, pressed ? 0 : 255, pressed ? 255 : 0, 0, 255 );
CL_DrawString( x, y, name, color, font, 0 );
CL_DrawStringLen( font, name, &btn_w, NULL, 0 );
x += btn_w + 6;
}
}
static void Joy_CalibrateGyro_f( void )
@@ -367,6 +569,11 @@ void Joy_Init( void )
Cvar_RegisterVariable( &joy_calibrated );
Cvar_RegisterVariable( &joy_enable );
Cvar_RegisterVariable( &joy_gyro_pitch );
Cvar_RegisterVariable( &joy_gyro_yaw );
Cvar_RegisterVariable( &joy_gyro_roll );
Cvar_RegisterVariable( &joy_debug );
// renamed from -nojoy to -noenginejoy to not conflict with
// client.dll's joystick support
if( Sys_CheckParm( "-noenginejoy" ))

View File

@@ -2189,12 +2189,12 @@ int IN_TouchEvent( touchEventType type, int fingerID, float x, float y, float dx
return Touch_ControlsEvent( type, fingerID, x, y, dx, dy );
}
void Touch_GetMove( float *forward, float *side, float *yaw, float *pitch )
void Touch_GetMove( float *forward, float *side, float *pitch, float *yaw )
{
*forward += touch.forward;
*side += touch.side;
*yaw += touch.yaw;
*pitch += touch.pitch;
*yaw += touch.yaw;
touch.yaw = touch.pitch = 0;
}

View File

@@ -560,8 +560,8 @@ static void IN_CollectInput( float *forward, float *side, float *pitch, float *y
#endif
}
Joy_FinalizeMove( forward, side, yaw, pitch );
Touch_GetMove( forward, side, yaw, pitch );
Joy_FinalizeMove( forward, side, pitch, yaw );
Touch_GetMove( forward, side, pitch, yaw );
if( look_filter.value )
{

View File

@@ -71,7 +71,7 @@ static inline void Touch_AddDefaultButton( const char *name, const char *texture
static inline void Touch_WriteConfig( void ) { }
static inline void Touch_Init( void ) { }
static inline void Touch_Shutdown( void ) { }
static inline void Touch_GetMove( float * forward, float *side, float *yaw, float *pitch ) { }
static inline void Touch_GetMove( float * forward, float *side, float *pitch, float *yaw ) { }
static inline void Touch_ResetDefaultButtons( void ) { }
static inline int IN_TouchEvent( touchEventType type, int fingerID, float x, float y, float dx, float dy ) { return 0; }
static inline void Touch_KeyEvent( int key, int down ) { }
@@ -87,7 +87,7 @@ void Touch_AddDefaultButton( const char *name, const char *texturefile, const ch
void Touch_WriteConfig( void );
void Touch_Init( void );
void Touch_Shutdown( void );
void Touch_GetMove( float * forward, float *side, float *yaw, float *pitch );
void Touch_GetMove( float * forward, float *side, float *pitch, float *yaw );
void Touch_ResetDefaultButtons( void );
int IN_TouchEvent( touchEventType type, int fingerID, float x, float y, float dx, float dy );
void Touch_KeyEvent( int key, int down );
@@ -119,7 +119,7 @@ typedef enum engineAxis_e
JOY_AXIS_YAW,
JOY_AXIS_RT,
JOY_AXIS_LT,
JOY_AXIS_NULL
MAX_AXES,
} engineAxis_t;
typedef enum joy_calibration_state_s
@@ -136,6 +136,7 @@ void Joy_SetCalibrationState( joy_calibration_state_t state );
void Joy_AxisMotionEvent( engineAxis_t engineAxis, short value );
void Joy_GyroEvent( vec3_t data );
void Joy_FinalizeMove( float *fw, float *side, float *dpitch, float *dyaw );
void Joy_DrawDebug( void );
void Joy_Init( void );
void Joy_Shutdown( void );

View File

@@ -152,7 +152,7 @@ sfx_t *S_FindName( const char *pname, qboolean *pfInCache )
uint i, hash;
string name;
if( COM_StringEmptyOrNULL( pname ) || !dma.initialized )
if( COM_StringEmptyOrNULL( pname ) || !snd.initialized )
return NULL;
if( Q_strlen( pname ) >= sizeof( sfx->name ))
@@ -232,6 +232,12 @@ void S_FreeSound( sfx_t *sfx )
prev = &hashSfx->hashNext;
}
if( clgame.soundFuncs.pfnS_FreeSound )
{
clgame.soundFuncs.pfnS_FreeSound( sfx, sfx - s_knownSfx );
return;
}
if( sfx->cache )
FS_FreeSound( sfx->cache );
memset( sfx, 0, sizeof( *sfx ));
@@ -247,7 +253,7 @@ void S_BeginRegistration( void )
{
int i;
snd_ambient = false;
snd.have_ambient_sfx = false;
// check for automatic ambient sounds
for( i = 0; i < NUM_AMBIENTS; i++ )
@@ -255,8 +261,9 @@ void S_BeginRegistration( void )
if( !GI->ambientsound[i][0] )
continue; // empty slot
ambient_sfx[i] = S_RegisterSound( GI->ambientsound[i] );
if( ambient_sfx[i] ) snd_ambient = true; // allow auto-ambients
snd.ambient_sfx[i] = S_RegisterSound( GI->ambientsound[i] );
if( snd.ambient_sfx[i] )
snd.have_ambient_sfx = true; // allow auto-ambients
}
s_registering = true;
@@ -273,7 +280,7 @@ void S_EndRegistration( void )
sfx_t *sfx;
int i;
if( !s_registering || !dma.initialized )
if( !s_registering || !snd.initialized )
return;
// free any sounds not from this registration sequence
@@ -306,7 +313,7 @@ sound_t S_RegisterSound( const char *name )
{
sfx_t *sfx;
if( COM_StringEmptyOrNULL( name ) || !dma.initialized )
if( COM_StringEmptyOrNULL( name ) || !snd.initialized )
return -1;
if( S_TestSoundChar( name, '!' ))
@@ -330,7 +337,7 @@ sound_t S_RegisterSound( const char *name )
sfx_t *S_GetSfxByHandle( sound_t handle )
{
if( !dma.initialized )
if( !snd.initialized )
return NULL;
// create new sfx
@@ -369,7 +376,7 @@ void S_FreeSounds( void )
sfx_t *sfx;
int i;
if( !dma.initialized )
if( !snd.initialized )
return;
// stop all sounds

File diff suppressed because it is too large Load Diff

View File

@@ -120,7 +120,7 @@ static void S_MixAudio( portable_samplepair_t *pbuf, const int *pvol, const void
static int S_AdjustNumSamples( channel_t *chan, int num_samples, double rate, double timecompress_rate )
{
if( chan->finished )
if( FBitSet( chan->flags, FL_CHAN_FINISHED ))
return 0;
// if channel is set to end at specific sample,
@@ -132,7 +132,7 @@ static int S_AdjustNumSamples( channel_t *chan, int num_samples, double rate, do
if( end_sample >= chan->forced_end )
{
chan->finished = true;
SetBits( chan->flags, FL_CHAN_FINISHED );
return floor(( chan->forced_end - chan->sample ) / ( rate * timecompress_rate ));
}
}
@@ -153,7 +153,7 @@ static int S_MixChannelToBuffer( portable_samplepair_t *pbuf, channel_t *chan, i
// timecompress at 100% is skipping the entire sfx, so mark as finished and exit
if( timecompress >= 100 )
{
chan->finished = true;
SetBits( chan->flags, FL_CHAN_FINISHED );
return 0;
}
@@ -173,7 +173,7 @@ static int S_MixChannelToBuffer( portable_samplepair_t *pbuf, channel_t *chan, i
// get sample pointer and also amount of samples available
const void *audio = NULL;
int available = S_RetrieveAudioSamples( chan->sfx->cache, &audio, chan->sample, request_num_samples, chan->use_loop );
int available = S_RetrieveAudioSamples( chan->sfx->cache, &audio, chan->sample, request_num_samples, FBitSet( chan->flags, FL_CHAN_USE_LOOP ));
// no samples available, exit
if( !available )
@@ -196,7 +196,7 @@ static int S_MixChannelToBuffer( portable_samplepair_t *pbuf, channel_t *chan, i
// samples couldn't be retrieved, mark as finished
if( num_samples > 0 )
chan->finished = true;
SetBits( chan->flags, FL_CHAN_FINISHED );
// total amount of samples mixed
return offset - initial_offset;
@@ -206,10 +206,10 @@ static int VOX_MixChannelToBuffer( portable_samplepair_t *pbuf, channel_t *chan,
{
int offset = 0;
if( chan->sentence_finished )
if( FBitSet( chan->flags, FL_CHAN_SENTENCE_FINISHED ))
return 0;
while( num_samples > 0 && !chan->sentence_finished )
while( num_samples > 0 && !FBitSet( chan->flags, FL_CHAN_SENTENCE_FINISHED ))
{
int outputCount = S_MixChannelToBuffer( pbuf, chan, num_samples, out_rate, pitch, offset, chan->words[chan->word_index].timecompress );
@@ -217,13 +217,13 @@ static int VOX_MixChannelToBuffer( portable_samplepair_t *pbuf, channel_t *chan,
num_samples -= outputCount;
// if we finished load a next word
if( chan->finished )
if( FBitSet( chan->flags, FL_CHAN_FINISHED ))
{
VOX_FreeWord( chan );
chan->word_index++;
VOX_LoadWord( chan );
if( !chan->sentence_finished )
if( !FBitSet( chan->flags, FL_CHAN_SENTENCE_FINISHED ))
chan->sfx = chan->words[chan->word_index].sfx;
}
}
@@ -235,7 +235,7 @@ static int S_MixNormalChannels( portable_samplepair_t *dst, int end, int rate )
{
const qboolean local = Host_IsLocalGame();
const qboolean ingame = CL_IsInGame();
const int num_samples = ( end - paintedtime ) / ( SOUND_DMA_SPEED / rate );
const int num_samples = ( end - snd.paintedtime ) / ( SOUND_DMA_SPEED / rate );
// FWGS feature: make everybody sound like chipmunks when we're going fast
const float pitch_mult = ( sys_timescale.value + 1 ) / 2;
@@ -248,25 +248,25 @@ static int S_MixNormalChannels( portable_samplepair_t *dst, int end, int rate )
if( cl.background && cls.key_dest == key_console )
return num_mixed_channels; // no sounds in console with background map
for( int i = 0; i < total_channels; i++ )
for( int i = 0; i < snd.total_channels; i++ )
{
channel_t *ch = &channels[i];
channel_t *ch = &snd.channels[i];
if( !ch->sfx )
continue;
if( !cl.background )
{
if( cls.key_dest == key_console && ch->localsound )
if( cls.key_dest == key_console && FBitSet( ch->flags, FL_CHAN_LOCAL_SOUND ))
{
// play, playvol
}
else if(( cls.key_dest == key_menu || cl.paused ) && !ch->localsound && local )
else if(( cls.key_dest == key_menu || cl.paused ) && !FBitSet( ch->flags, FL_CHAN_LOCAL_SOUND ) && local )
{
// play only local sounds, keep pause for other
continue;
}
else if( cls.key_dest != key_menu && !ingame && !ch->staticsound )
else if( cls.key_dest != key_menu && !ingame && !FBitSet( ch->flags, FL_CHAN_STATIC_SOUND ))
{
// play only ambient sounds, keep pause for other
continue;
@@ -284,13 +284,20 @@ static int S_MixNormalChannels( portable_samplepair_t *dst, int end, int rate )
// if the sound is unaudible, skip it
// if it's also not looping, free it
if( ch->leftvol < 8 && ch->rightvol < 8 )
{
if( !FBitSet( sc->flags, SOUND_LOOPED ) || !ch->use_loop )
S_FreeChannel( ch );
{
if( !FBitSet( sc->flags, SOUND_LOOPED ) || !FBitSet( ch->flags, FL_CHAN_USE_LOOP ))
{
if( ch->inauduble_free_time == 0.0f )
ch->inauduble_free_time = host.realtime + MAX_CHANNEL_INAUDIBLE_TIME;
else if( ch->inauduble_free_time > host.realtime )
S_FreeChannel( ch );
}
continue;
}
ch->inauduble_free_time = 0.0f;
if( rate != sc->rate )
continue;
@@ -301,9 +308,9 @@ static int S_MixNormalChannels( portable_samplepair_t *dst, int end, int rate )
if( ent != NULL )
{
if( sc->width == 1 )
SND_MoveMouth8( &ent->mouth, ch->sample, sc, num_samples, ch->use_loop );
SND_MoveMouth8( &ent->mouth, ch->sample, sc, num_samples, FBitSet( ch->flags, FL_CHAN_USE_LOOP ));
else
SND_MoveMouth16( &ent->mouth, ch->sample, sc, num_samples, ch->use_loop );
SND_MoveMouth16( &ent->mouth, ch->sample, sc, num_samples, FBitSet( ch->flags, FL_CHAN_USE_LOOP ));
}
}
@@ -311,18 +318,18 @@ static int S_MixNormalChannels( portable_samplepair_t *dst, int end, int rate )
num_mixed_channels++;
if( ch->is_sentence )
if( ch->words )
{
VOX_MixChannelToBuffer( dst, ch, num_samples, rate, pitch );
if( ch->sentence_finished )
if( FBitSet( ch->flags, FL_CHAN_SENTENCE_FINISHED ))
S_FreeChannel( ch );
}
else
{
S_MixChannelToBuffer( dst, ch, num_samples, rate, pitch, 0, 0 );
if( ch->finished )
if( FBitSet( ch->flags, FL_CHAN_FINISHED ))
S_FreeChannel( ch );
}
}
@@ -368,7 +375,7 @@ static int S_MixNormalChannelsToRoombuffer( int end, int count )
// until there is no real usecase, let's keep it simple
int num_mixed_channels = S_MixNormalChannels( roombuffer, end, SOUND_11k );
if( dma.format.speed >= SOUND_22k )
if( snd.format.speed >= SOUND_22k )
{
if( num_mixed_channels > 0 )
S_UpsampleBuffer( roombuffer, count / ( SOUND_22k / SOUND_11k ));
@@ -376,7 +383,7 @@ static int S_MixNormalChannelsToRoombuffer( int end, int count )
num_mixed_channels += S_MixNormalChannels( roombuffer, end, SOUND_22k );
}
if( dma.format.speed >= SOUND_44k )
if( snd.format.speed >= SOUND_44k )
{
if( num_mixed_channels > 0 )
S_UpsampleBuffer( roombuffer, count / ( SOUND_44k / SOUND_22k ));
@@ -395,10 +402,10 @@ static int S_MixRawChannels( int end )
return 0;
// paint in the raw channels
for( size_t i = 0; i < ARRAYSIZE( raw_channels ); i++ )
for( size_t i = 0; i < snd.max_raw_channels; i++ )
{
// copy from the streaming sound source
rawchan_t *ch = raw_channels[i];
rawchan_t *ch = snd.raw_channels[i];
if( !ch )
continue;
@@ -427,7 +434,7 @@ static int S_MixRawChannels( int end )
uint stop = (end < ch->s_rawend) ? end : ch->s_rawend;
const uint mask = ch->max_samples - 1;
for( size_t i = 0, j = paintedtime; j < stop; i++, j++ )
for( size_t i = 0, j = snd.paintedtime; j < stop; i++, j++ )
{
pbuf[i].left += ( ch->rawsamples[j & mask].left * ch->leftvol ) >> 8;
pbuf[i].right += ( ch->rawsamples[j & mask].right * ch->rightvol ) >> 8;
@@ -436,8 +443,8 @@ static int S_MixRawChannels( int end )
if( ch->entnum > 0 )
{
cl_entity_t *ent = CL_GetEntityByIndex( ch->entnum );
int pos = paintedtime & ( ch->max_samples - 1 );
int count = bound( 0, ch->max_samples - pos, stop - paintedtime );
int pos = snd.paintedtime & ( ch->max_samples - 1 );
int count = bound( 0, ch->max_samples - pos, stop - snd.paintedtime );
if( ent )
SND_MoveMouthRaw( &ent->mouth, &ch->rawsamples[pos], count );
@@ -479,8 +486,8 @@ static void S_WriteLinearBlastStereo16( short *snd_out, const int *snd_p, size_t
static void S_TransferPaintBuffer( const portable_samplepair_t *src, int endtime )
{
const int *snd_p = (const int *)src;
const int sampleMask = ((dma.samples >> 1) - 1);
int lpaintedtime = paintedtime;
const int sampleMask = ((snd.samples >> 1) - 1);
int lpaintedtime = snd.paintedtime;
SNDDMA_BeginPainting ();
@@ -489,9 +496,9 @@ static void S_TransferPaintBuffer( const portable_samplepair_t *src, int endtime
// handle recirculating buffer issues
int lpos = lpaintedtime & sampleMask;
short *snd_out = (short *)dma.buffer + (lpos << 1);
short *snd_out = (short *)snd.buffer + (lpos << 1);
int snd_linear_count = (dma.samples>>1) - lpos;
int snd_linear_count = (snd.samples>>1) - lpos;
if( lpaintedtime + snd_linear_count > endtime )
snd_linear_count = endtime - lpaintedtime;
@@ -519,14 +526,14 @@ void S_PaintChannels( int endtime )
{
int gain = S_GetMasterVolume() * 256;
while( paintedtime < endtime )
while( snd.paintedtime < endtime )
{
// if paintbuffer is smaller than DMA buffer
int end = endtime;
if( end - paintedtime > PAINTBUFFER_SIZE )
end = paintedtime + PAINTBUFFER_SIZE;
if( end - snd.paintedtime > PAINTBUFFER_SIZE )
end = snd.paintedtime + PAINTBUFFER_SIZE;
const int num_samples = end - paintedtime;
const int num_samples = end - snd.paintedtime;
S_ClearBuffers( num_samples );
@@ -543,6 +550,6 @@ void S_PaintChannels( int endtime )
// transfer out according to DMA format
S_TransferPaintBuffer( paintbuffer, end );
paintedtime = end;
snd.paintedtime = end;
}
}

View File

@@ -92,7 +92,7 @@ void S_StartBackgroundTrack( const char *introTrack, const char *mainTrack, int
{
S_StopBackgroundTrack();
if( !dma.initialized ) return;
if( !snd.initialized ) return;
// check for special symbols
if( introTrack && *introTrack == '*' )
@@ -132,9 +132,9 @@ S_StopBackgroundTrack
*/
void S_StopBackgroundTrack( void )
{
s_listener.stream_paused = false;
snd.stream_paused = false;
if( !dma.initialized ) return;
if( !snd.initialized ) return;
if( !s_bgTrack.stream ) return;
FS_FreeStream( s_bgTrack.stream );
@@ -149,7 +149,7 @@ S_StreamSetPause
*/
void S_StreamSetPause( int pause )
{
s_listener.stream_paused = pause;
snd.stream_paused = pause;
}
/*
@@ -197,11 +197,11 @@ void S_StreamBackgroundTrack( void )
int r, fileBytes;
rawchan_t *ch = NULL;
if( !dma.initialized || !s_bgTrack.stream || s_listener.streaming )
if( !snd.initialized || !s_bgTrack.stream || snd.streaming )
return;
// don't bother playing anything if musicvolume is 0
if( !s_musicvolume.value || cl.paused || s_listener.stream_paused )
if( !s_musicvolume.value || cl.paused || snd.stream_paused )
return;
if( !cl.background )
@@ -218,14 +218,14 @@ void S_StreamBackgroundTrack( void )
Assert( ch != NULL );
// see how many samples should be copied into the raw buffer
if( ch->s_rawend < soundtime )
ch->s_rawend = soundtime;
if( ch->s_rawend < snd.soundtime )
ch->s_rawend = snd.soundtime;
while( ch->s_rawend < soundtime + ch->max_samples )
while( ch->s_rawend < snd.soundtime + ch->max_samples )
{
const stream_t *info = s_bgTrack.stream;
bufferSamples = ch->max_samples - (ch->s_rawend - soundtime);
bufferSamples = ch->max_samples - (ch->s_rawend - snd.soundtime);
// decide how much data needs to be read from the file
fileSamples = bufferSamples * ((float)info->rate / SOUND_DMA_SPEED );
@@ -283,9 +283,9 @@ S_StartStreaming
*/
void S_StartStreaming( void )
{
if( !dma.initialized ) return;
if( !snd.initialized ) return;
// begin streaming movie soundtrack
s_listener.streaming = true;
snd.streaming = true;
}
/*
@@ -295,6 +295,6 @@ S_StopStreaming
*/
void S_StopStreaming( void )
{
if( !dma.initialized ) return;
s_listener.streaming = false;
if( !snd.initialized ) return;
snd.streaming = false;
}

View File

@@ -140,7 +140,7 @@ static void S_TrimStartEndTimes( channel_t *ch, wavdata_t *wav, int start, int e
void VOX_LoadWord( channel_t *ch )
{
ch->sentence_finished = true;
SetBits( ch->flags, FL_CHAN_SENTENCE_FINISHED );
if( ch->word_index < 0 || ch->word_index >= CVOXWORDMAX )
return;
@@ -155,7 +155,7 @@ void VOX_LoadWord( channel_t *ch )
if( !data )
return;
ch->sentence_finished = false;
ClearBits( ch->flags, FL_CHAN_SENTENCE_FINISHED );
ch->data = data;
int start = word->start;
@@ -171,7 +171,7 @@ void VOX_FreeWord( channel_t *ch )
{
// TODO: don't set random fields to zero lol, was memset before
ch->sample = ch->forced_end = 0.0;
ch->finished = false;
ClearBits( ch->flags, FL_CHAN_FINISHED );
ch->data = NULL;
if( ch->word_index < 0 || ch->word_index >= CVOXWORDMAX )
@@ -179,7 +179,7 @@ void VOX_FreeWord( channel_t *ch )
voxword_t *word = &ch->words[ch->word_index];
if( !word->sfx || word->in_cache )
if( !word->sfx || FBitSet( word->flags, FL_VOXWORD_IN_CACHE ))
return;
FS_FreeSound( word->sfx->cache );
@@ -191,7 +191,7 @@ void VOX_SetChanVol( channel_t *ch )
{
voxword_t *word;
if( !ch->is_sentence || ch->sentence_finished )
if( !ch->words || FBitSet( ch->flags, FL_CHAN_SENTENCE_FINISHED ))
return;
word = &ch->words[ch->word_index];
@@ -207,7 +207,7 @@ float VOX_ModifyPitch( channel_t *ch, float pitch )
{
voxword_t *word;
if( !ch->is_sentence || ch->sentence_finished )
if( !ch->words || FBitSet( ch->flags, FL_CHAN_SENTENCE_FINISHED ))
return pitch;
word = &ch->words[ch->word_index];
@@ -356,10 +356,7 @@ static void VOX_MakeDefaultWordParams( voxword_t *voxword )
*voxword = (voxword_t) {
.volume = 100,
.pitch = 100,
.start = 0,
.end = 100,
.timecompress = 0,
.in_cache = false,
};
}
@@ -416,19 +413,24 @@ static qboolean VOX_ParseWordParams( char *psz, voxword_t *pvoxword, voxword_t *
i = Q_atoi( sznum );
switch( command )
{
case 'e': pvoxword->end = i; break;
case 'p': pvoxword->pitch = i; break;
case 's': pvoxword->start = i; break;
case 't': pvoxword->timecompress = i; break;
case 'v': pvoxword->volume = i; break;
case 'e':
pvoxword->end = bound( 0, i, 100 );
break;
case 'p':
pvoxword->pitch = bound( 0, i, UINT16_MAX );
break;
case 's':
pvoxword->start = bound( 0, i, 100 );
break;
case 't':
pvoxword->timecompress = bound( 0, i, 100 );
break;
case 'v':
pvoxword->volume = bound( 0, i, UINT16_MAX );
break;
}
}
// validate some of the parameters
pvoxword->start = bound( 0, pvoxword->start, 100 );
pvoxword->end = bound( 0, pvoxword->end, 100 );
pvoxword->timecompress = bound( 0, pvoxword->timecompress, 100 );
// no actual word but new defaults
if( Q_strlen( pszsave ) == 0 )
{
@@ -447,10 +449,18 @@ void VOX_LoadSound( channel_t *ch, const char *pszin )
int i, j;
int num_words;
voxword_t default_voxword;
voxword_t words_buf[CVOXWORDMAX + 1]; // local scratch: parsed words + null terminator
if( !pszin )
return;
// free any existing words from a previous sentence on this channel
if( ch->words )
{
VOX_FreeWord( ch );
Mem_Free2( &ch->words );
}
psz = VOX_LookupString( pszin );
if( !psz )
@@ -478,12 +488,13 @@ void VOX_LoadSound( channel_t *ch, const char *pszin )
num_words = VOX_ParseString( buffer, rgpparseword );
VOX_MakeDefaultWordParams( &default_voxword );
memset( words_buf, 0, sizeof( words_buf ));
for( i = 0, j = 0; i < num_words; i++ )
{
char pathbuffer[MAX_SYSPATH];
if( !VOX_ParseWordParams( rgpparseword[i], &ch->words[j], &default_voxword ))
if( !VOX_ParseWordParams( rgpparseword[i], &words_buf[j], &default_voxword ))
continue;
if( Q_snprintf( pathbuffer, sizeof( pathbuffer ), "%s%s", szpath, rgpparseword[i] ) < 0 )
@@ -493,17 +504,19 @@ void VOX_LoadSound( channel_t *ch, const char *pszin )
}
qboolean in_cache = false;
ch->words[j].sfx = S_FindName( pathbuffer, &in_cache );
ch->words[j].in_cache = in_cache;
words_buf[j].sfx = S_FindName( pathbuffer, &in_cache );
if( in_cache )
SetBits( words_buf[j].flags, FL_VOXWORD_IN_CACHE );
j++;
}
ch->words[j].sfx = NULL;
// words_buf[j].sfx is already NULL from the memset — null terminator
ch->words = Mem_Malloc( sndpool, ( j + 1 ) * sizeof( voxword_t ));
memcpy( ch->words, words_buf, ( j + 1 ) * sizeof( voxword_t ));
ch->sfx = ch->words[0].sfx;
ch->word_index = 0;
ch->is_sentence = true;
VOX_LoadWord( ch );
}

View File

@@ -19,6 +19,7 @@ GNU General Public License for more details.
extern poolhandle_t sndpool;
#include "xash3d_mathlib.h"
#include "sound_api.h"
// sound engine rate defines
#define SOUND_11k 11025 // 11khz sample rate
@@ -38,105 +39,6 @@ extern poolhandle_t sndpool;
#define CLIP16( x ) bound( SHRT_MIN + 8, x, SHRT_MAX - 8 )
typedef struct
{
int left;
int right;
} portable_samplepair_t;
typedef struct sfx_s
{
char name[MAX_QPATH];
wavdata_t *cache;
int servercount;
uint hashValue;
struct sfx_s *hashNext;
} sfx_t;
typedef struct voxword_s
{
sfx_t *sfx;
uint16_t volume; // volume percent
uint16_t pitch; // pitch shift percent (keep large for extra chipmunk fun)
uint16_t timecompress; // percent of skipped data (speeds up playback without pitch shift)
uint16_t start : 7; // percent at which playback starts
uint16_t end : 7; // percent at which playback ends
uint16_t in_cache : 1; // if set to 1, it was loaded prior and shouldn't be freed
} voxword_t;
typedef struct snd_format_s
{
uint speed;
byte width;
byte channels;
} snd_format_t;
typedef struct
{
snd_format_t format;
int samples; // mono samples in buffer
int samplepos; // in mono samples
qboolean initialized; // sound engine is active
byte *buffer;
const char *backendName;
} dma_t;
typedef struct rawchan_s
{
int entnum;
int master_vol;
int leftvol; // 0-255 left volume
int rightvol; // 0-255 right volume
float dist_mult; // distance multiplier (attenuation/clipK)
vec3_t origin; // only use if fixed_origin is set
volatile uint s_rawend;
float oldtime; // catch time jumps
size_t max_samples; // buffer length
portable_samplepair_t rawsamples[]; // variable sized
} rawchan_t;
typedef struct channel_s
{
char name[16]; // keep sentence name
sfx_t *sfx; // sfx number
int leftvol; // 0-255 left volume
int rightvol; // 0-255 right volume
int entnum; // entity soundsource
int entchannel; // sound channel (CHAN_STREAM, CHAN_VOICE, etc.)
vec3_t origin; // only use if fixed_origin is set
float dist_mult; // distance multiplier (attenuation/clipK)
int master_vol; // 0-255 master volume
byte use_loop : 1; // don't loop default and local sounds
byte staticsound : 1; // use origin instead of fetching entnum's origin
byte localsound : 1; // it's a local menu sound (not looped, not paused)
byte is_sentence : 1; // bit indicating vox sentence
byte sentence_finished : 1; // if set, finished playing sentence
byte finished : 1; // if set, finished playing single word
byte word_index;
short basePitch; // base pitch percent (100% is normal pitch playback)
voxword_t words[CVOXWORDMAX];
double sample;
double forced_end;
wavdata_t *data;
} channel_t;
typedef struct
{
vec3_t origin; // simorg + view_ofs
vec3_t forward;
vec3_t right;
vec3_t up;
int entnum;
qboolean streaming; // playing AVI-file
qboolean stream_paused; // pause only background track
} listener_t;
typedef int sound_t;
//====================================================================
#define MAX_DYNAMIC_CHANNELS (60 + NUM_AMBIENTS)
@@ -145,16 +47,8 @@ typedef int sound_t;
#define MAX_RAW_SAMPLES 16384
#define SND_CLIP_DISTANCE 1000.0f
extern sound_t ambient_sfx[NUM_AMBIENTS];
extern qboolean snd_ambient;
extern channel_t channels[MAX_CHANNELS];
extern rawchan_t *raw_channels[MAX_RAW_CHANNELS];
extern int total_channels;
extern int paintedtime;
extern int soundtime;
extern listener_t s_listener;
extern int idsp_room;
extern dma_t dma;
extern int idsp_room;
extern snd_globals_t snd;
extern convar_t s_musicvolume;
extern convar_t s_lerping;

View File

@@ -51,12 +51,12 @@ GetLittleLong
*/
static int GetLittleLong( void )
{
int val = 0;
uint val = 0;
val += (*(iff_dataPtr+0) << 0);
val += (*(iff_dataPtr+1) << 8);
val += (*(iff_dataPtr+2) <<16);
val += (*(iff_dataPtr+3) <<24);
val += ((uint)*(iff_dataPtr+0) << 0);
val += ((uint)*(iff_dataPtr+1) << 8);
val += ((uint)*(iff_dataPtr+2) <<16);
val += ((uint)*(iff_dataPtr+3) <<24);
iff_dataPtr += 4;
return val;

View File

@@ -28,7 +28,7 @@ typedef struct
int cursize;
} cmdbuf_t;
static qboolean cmd_wait;
static int cmd_wait;
static cmdbuf_t cmd_text;
static cmdbuf_t filteredcmd_text;
static cmdalias_t *cmd_alias;
@@ -56,6 +56,7 @@ void Cbuf_Clear( void )
{
memset( &cmd_text, 0, sizeof( cmd_text ));
memset( &filteredcmd_text, 0, sizeof( filteredcmd_text ));
cmd_wait = 0;
}
/*
@@ -174,6 +175,12 @@ static void Cbuf_ExecuteCommandsFromBuffer( cmdbuf_t *buf, qboolean isPrivileged
while( buf->cursize )
{
if( cmd_wait > 0 )
{
cmd_wait--;
break;
}
// limit amount of commands that can be issued
if( cmdsToExecute >= 0 )
{
@@ -238,14 +245,6 @@ static void Cbuf_ExecuteCommandsFromBuffer( cmdbuf_t *buf, qboolean isPrivileged
// execute the command line
Cmd_ExecuteStringWithPrivilegeCheck( line, isPrivileged );
if( cmd_wait )
{
// skip out while text still remains in buffer,
// leaving it for next frame
cmd_wait = false;
break;
}
}
}
@@ -367,7 +366,13 @@ bind g "cmd use rocket ; +attack ; wait ; -attack ; cmd use blaster"
*/
static void Cmd_Wait_f( void )
{
cmd_wait = true;
int frame = 1;
if( Cmd_Argc() > 1 )
{
frame = Q_atoi( Cmd_Argv( 1 ) );
frame = Q_max( 1, frame );
}
cmd_wait = frame;
}
/*
@@ -1234,26 +1239,60 @@ static void Cmd_MakePrivileged_f( void )
/*
===============
Cmd_Exec_f
Cmd_ExecScript
===============
*/
static void Cmd_Exec_f( void )
static void Cmd_ExecScript( const char *filename )
{
string cfgpath;
byte *f;
fs_offset_t len;
if( Cmd_Argc() != 2 )
f = FS_LoadFile( filename, &len, false );
if( !f )
{
Con_Printf( S_USAGE "exec <filename>\n" );
Con_Reportf( "couldn't exec %s\n", filename );
return;
}
Q_strncpy( cfgpath, Cmd_Argv( 1 ), sizeof( cfgpath ));
COM_DefaultExtension( cfgpath, ".cfg", sizeof( cfgpath )); // append as default
// len is fs_offset_t, which can be larger than size_t
if( len >= SIZE_MAX )
{
Con_Reportf( "%s: %s is too long\n", __func__, filename );
Mem_Free( f );
return;
}
#ifndef XASH_DEDICATED
if( !Cmd_CurrentCommandIsPrivileged() && !Q_stricmp( GI->gamefolder, "tfc" ))
if( f[len - 1] != '\n' )
{
Cbuf_InsertTextLen( f, len, len + 1 );
Cbuf_InsertTextLen( "\n", 1, 1 );
}
else Cbuf_InsertTextLen( f, len, len );
Mem_Free( f );
}
/*
===============
Cmd_UnprivilegedExec_f
===============
*/
static void Cmd_UnprivilegedExec_f( void )
{
string cfgpath;
qboolean allow = false;
if( Cmd_Argc() != 2 )
return;
Q_strncpy( cfgpath, Cmd_Argv( 1 ), sizeof( cfgpath ));
COM_DefaultExtension( cfgpath, ".cfg", sizeof( cfgpath ));
if( SV_GetMaxClients() == 1 )
allow = true;
#if !XASH_DEDICATED
if( !allow && !Q_stricmp( GI->gamefolder, "tfc" ))
{
const char *const unprivileged_whitelist[] =
{
@@ -1278,48 +1317,75 @@ static void Cmd_Exec_f( void )
break;
}
}
}
#endif
if( !allow )
if( allow )
Cmd_ExecScript( cfgpath );
else
Con_Printf( "exec %s: not privileged or in whitelist\n", cfgpath );
}
/*
===============
Cmd_Exec_f
===============
*/
static void Cmd_Exec_f( void )
{
string cfgpath;
// early exit for stuffcmd
if( !Cmd_CurrentCommandIsPrivileged())
{
Cmd_UnprivilegedExec_f();
return;
}
if( Cmd_Argc() != 2 )
{
Con_Printf( S_USAGE "exec <PATTERN>\n"
"PATTERN single file or wildcard pattern to match\n"
"Wildcards: * matches any characters, ? matches single character\n"
"Example: file.cfg - single file.cfg\n"
"\tdirectory/* - all .cfg files in directory\n"
"\tdirectory/???.cfg - all .cfg files in directory with 3 character names\n" );
return;
}
Q_strncpy( cfgpath, Cmd_Argv( 1 ), sizeof( cfgpath ));
COM_DefaultExtension( cfgpath, ".cfg", sizeof( cfgpath ));
if( Q_strpbrk( cfgpath, "*?" ))
{
search_t *search = FS_Search( cfgpath, true, false );
if( !search || !search->numfilenames )
{
Con_Printf( "exec %s: not privileged or in whitelist\n", cfgpath );
Con_Printf( "couldn't exec %s\n", Cmd_Argv( 1 ));
if( search ) Mem_Free( search );
return;
}
Con_Printf( "execing %d file%s from " S_GREEN "%s" S_DEFAULT "\n",
search->numfilenames, search->numfilenames > 1 ? "s" : "", Cmd_Argv( 1 ));
for( int i = 0; i < search->numfilenames; i++ )
Cmd_ExecScript( search->filenames[i] );
Mem_Free( search );
return;
}
#endif // XASH_DEDICATED
// don't execute game.cfg in singleplayer
if( SV_GetMaxClients() == 1 && !Q_stricmp( "game.cfg", cfgpath ))
return;
f = FS_LoadFile( cfgpath, &len, false );
if( !f )
{
Con_Reportf( "couldn't exec %s\n", Cmd_Argv( 1 ));
return;
}
// len is fs_offset_t, which can be larger than size_t
if( len >= SIZE_MAX )
{
Con_Reportf( "%s: %s is too long\n", __func__, Cmd_Argv( 1 ));
return;
}
if( !Q_stricmp( "config.cfg", cfgpath ))
host.config_executed = true;
Con_Printf( "execing " S_GREEN "%s" S_DEFAULT "\n", Cmd_Argv( 1 ));
// adds \n at end of the file
// FS_LoadFile always null terminates
if( f[len - 1] != '\n' )
{
Cbuf_InsertTextLen( f, len, len + 1 );
Cbuf_InsertTextLen( "\n", 1, 1 );
}
else Cbuf_InsertTextLen( f, len, len );
Mem_Free( f );
Cmd_ExecScript( cfgpath );
}
/*
@@ -1388,13 +1454,14 @@ void Cmd_Init( void )
cmd_pool = Mem_AllocPool( "Console Commands" );
cmd_functions = NULL;
cmd_condition = 0;
cmd_wait = 0;
cmd_alias = NULL;
cmd_args = NULL;
cmd_argc = 0;
// register our commands
Cmd_AddCommand( "echo", Cmd_Echo_f, "print a message to the console (useful in scripts)" );
Cmd_AddCommand( "wait", Cmd_Wait_f, "make script execution wait for some rendered frames" );
Cmd_AddRestrictedCommand( "wait", Cmd_Wait_f, "make script execution wait for some rendered frames" );
Cmd_AddRestrictedCommand( "cmdlist", Cmd_List_f, "display all console commands beginning with the specified prefix" );
Cmd_AddRestrictedCommand( "stuffcmds", Cmd_StuffCmds_f, "execute commandline parameters (must be present in .rc script)" );
Cmd_AddRestrictedCommand( "apropos", Cmd_Apropos_f, "lists all console variables/commands/aliases containing the specified string in the name or description" );

View File

@@ -961,7 +961,7 @@ static void Test_LZSS( void )
};
const char decompressed[] = "Do you like what you see?";
#ifdef USING_ASAN
#ifdef USE_ASAN
ASAN_POISON_MEMORY_REGION( poison1, sizeof( poison1 ));
ASAN_POISON_MEMORY_REGION( poison2, sizeof( poison2 ));
ASAN_POISON_MEMORY_REGION( poison3, sizeof( poison3 ));

View File

@@ -16,42 +16,48 @@ GNU General Public License for more details.
#ifndef COMMON_H
#define COMMON_H
#ifdef __cplusplus
extern "C" {
#endif
/*
===================================================================================================================================
Legend:
INTERNAL RESOURCE - function contain hardcoded path to resource that engine required (optional in most cases)
OBSOLETE, UNUSED - this function no longer used and leaved here for keep binary compatibility
TODO - some functionality not impemented but planned
FIXME - code doesn't working properly in some rare cases
HACKHACK - unexpected behavior on some input params (or something like)
BUGBUG - code doesn't working properly in most cases!
TESTTEST - this code may be unstable and needs to be more tested
g-cont: - notes from engine author
XASH SPECIFIC - sort of hack that works only in Xash3D not in GoldSrc
===================================================================================================================================
*/
#include "port.h"
#include "backends.h"
#include "defaults.h"
#include <stdio.h>
#include <stdlib.h> // rand, adbs
#include <stdarg.h> // va
#include "build.h"
#if !XASH_WIN32
#include <stddef.h> // size_t
#else
#include <sys/types.h> // off_t
#endif
#include "port.h"
#include "backends.h"
#include "defaults.h"
#include "library_suffix.h"
#include "enginefeatures.h"
#include "system.h"
#include "com_model.h"
#include "com_strings.h"
#include "crtlib.h"
#include "cvar.h"
#include "con_nprint.h"
#include "crclib.h"
#include "ref_api.h"
#include "com_image.h"
/*
===================================================================================================================================
Legend:
INTERNAL RESOURCE - function contain hardcoded path to resource that engine required (optional in most cases)
OBSOLETE, UNUSED - this function no longer used and leaved here for keep binary compatibility
TODO - some functionality not impemented but planned
FIXME - code doesn't working properly in some rare cases
HACKHACK - unexpected behavior on some input params (or something like)
BUGBUG - code doesn't working properly in most cases!
TESTTEST - this code may be unstable and needs to be more tested
g-cont: - notes from engine author
XASH SPECIFIC - sort of hack that works only in Xash3D not in GoldSrc
===================================================================================================================================
*/
// configuration
@@ -60,21 +66,19 @@ XASH SPECIFIC - sort of hack that works only in Xash3D not in GoldSrc
//
#if XASH_TIMER == TIMER_NULL
#error "Please select timer backend"
#endif
#endif // XASH_TIMER == TIMER_NULL
#if !XASH_DEDICATED
#if XASH_VIDEO == VIDEO_NULL
#error "Please select video backend"
#endif
#endif
#endif // XASH_VIDEO == VIDEO_NULL
#endif // !XASH_DEDICATED
#ifndef XASH_SDL
#if XASH_TIMER == TIMER_SDL || XASH_VIDEO == VIDEO_SDL || XASH_SOUND == SOUND_SDL || XASH_INPUT == INPUT_SDL
#error "SDL backends without XASH_SDL not allowed"
#endif
#endif
#if XASH_TIMER == TIMER_SDL || XASH_VIDEO == VIDEO_SDL || XASH_SOUND == SOUND_SDL || XASH_INPUT == INPUT_SDL
#error "SDL backends without XASH_SDL not allowed"
#endif // XASH_TIMER == TIMER_SDL || XASH_VIDEO == VIDEO_SDL || XASH_SOUND == SOUND_SDL || XASH_INPUT == INPUT_SDL
#endif // XASH_SDL
#define HACKS_RELATED_HLMODS // some HL-mods works differently under Xash and can't be fixed without some hacks at least at current time
@@ -97,19 +101,6 @@ typedef enum instance_e
#define Host_IsDedicated() ( host.type == HOST_DEDICATED )
#endif
#include "system.h"
#include "com_model.h"
#include "com_strings.h"
#include "crtlib.h"
#define FSCALLBACK_OVERRIDE_OPEN
#define FSCALLBACK_OVERRIDE_LOADFILE
#define FSCALLBACK_OVERRIDE_MALLOC_LIKE
#include "fscallback.h"
#include "cvar.h"
#include "con_nprint.h"
#include "crclib.h"
#include "ref_api.h"
// PERFORMANCE INFO
#define MIN_FPS 20.0f // host minimum fps value for maxfps.
#define MAX_FPS_SOFT 200.0f // soft limit for maxfps.
@@ -351,6 +342,9 @@ typedef struct host_parm_s
string gamedll;
string clientlib;
string menulib;
// default game directory, passed to us from game launcher
string default_gamedir;
} host_parm_t;
extern host_parm_t host;
@@ -387,6 +381,10 @@ void Mem_Stats_f( void );
#define Mem_Calloc( pool, size ) _Mem_Alloc( pool, size, true, __FILE__, __LINE__ )
#define Mem_Realloc( pool, ptr, size ) _Mem_Realloc( pool, ptr, size, true, __FILE__, __LINE__ )
#define Mem_Free( mem ) _Mem_Free( mem, __FILE__, __LINE__ )
#define Mem_Free2( ptr ) { \
_Mem_Free( *ptr, __FILE__, __LINE__ ); \
*ptr = NULL; }
#define Mem_AllocPool( name ) _Mem_AllocPool( name, __FILE__, __LINE__ )
#define Mem_FreePool( pool ) _Mem_FreePool( pool, __FILE__, __LINE__ )
#define Mem_EmptyPool( pool ) _Mem_EmptyPool( pool, __FILE__, __LINE__ )
@@ -396,7 +394,7 @@ void Mem_Stats_f( void );
//
// filesystem_engine.c
//
void FS_Init( const char *basedir );
void FS_Init( void );
void FS_Shutdown( void );
void *FS_GetNativeObject( const char *obj );
int FS_Close( file_t *file );
@@ -480,7 +478,6 @@ void Cmd_Escape( char *newCommand, const char *oldCommand, int len );
//
// imagelib
//
#include "com_image.h"
void Image_Setup( void );
void Image_Init( void );
@@ -564,7 +561,11 @@ qboolean Sound_SupportedFileFormat( const char *fileext );
//
typedef void( *pfnChangeGame )( const char *progname );
qboolean Host_IsQuakeCompatible( void );
static inline qboolean Host_IsQuakeCompatible( void )
{
return FBitSet( host.features, ENGINE_QUAKE_COMPATIBLE ) ? true : false;
}
void Host_ShutdownWithReason( const char *reason );
int EXPORT Host_Main( int argc, char **argv, const char *progname, int bChangeGame, pfnChangeGame func );
void Host_EndGame( qboolean abort, const char *message, ... ) FORMAT_CHECK( 2 );
@@ -934,7 +935,4 @@ void SoundList_Shutdown( void );
#error "common.h in ref_dll"
#endif
#ifdef __cplusplus
}
#endif
#endif//COMMON_H

View File

@@ -1199,6 +1199,9 @@ void Con_CompleteCommand( field_t *field, qboolean print_suggestions )
// setup the completion field
con.completionField = field;
if( COM_StringEmpty( con.completionField->buffer ))
return;
// only look at the first token for completion purposes
Cmd_TokenizeString( con.completionField->buffer );

View File

@@ -328,7 +328,7 @@ static qboolean FS_DetermineReadOnlyRootDirectory( char *out, size_t size )
FS_Init
================
*/
void FS_Init( const char *basedir )
void FS_Init( void )
{
string gamedir;
char rodir[MAX_OSPATH], rootdir[MAX_OSPATH];
@@ -352,7 +352,7 @@ void FS_Init( const char *basedir )
if( env )
Q_strncpy( gamedir, env, sizeof( gamedir ));
else
Q_strncpy( gamedir, basedir, sizeof( gamedir )); // gamedir == basedir
Q_strncpy( gamedir, host.default_gamedir, sizeof( gamedir )); // gamedir == basedir
}
FS_LoadProgs();
@@ -364,7 +364,7 @@ void FS_Init( const char *basedir )
// and this better be reworked at some point
g_fsapi.SetCurrentDirectory( rootdir );
if( !g_fsapi.InitStdio( true, rootdir, basedir, gamedir, rodir ))
if( !g_fsapi.InitStdio( true, rootdir, host.default_gamedir, gamedir, rodir ))
{
Sys_Error( "Can't init filesystem_stdio!\n" );
return;

View File

@@ -36,6 +36,8 @@ GNU General Public License for more details.
#include "enginefeatures.h"
#include "render_api.h" // decallist_t
#include "tests.h"
#include "library.h"
#include "platform/platform.h"
host_parm_t host; // host parms
static jmp_buf return_from_main_buf;
@@ -296,27 +298,6 @@ void Host_ValidateEngineFeatures( uint32_t mask, uint32_t features )
host.features = features;
}
/*
==============
Host_IsQuakeCompatible
==============
*/
qboolean Host_IsQuakeCompatible( void )
{
// feature set
if( FBitSet( host.features, ENGINE_QUAKE_COMPATIBLE ))
return true;
#if !XASH_DEDICATED
// quake demo playing
if( cls.demoplayback == DEMO_QUAKE1 )
return true;
#endif // XASH_DEDICATED
return false;
}
/*
================
Host_EndGame
@@ -882,6 +863,118 @@ static void Host_DetermineExecutableName( char *out, size_t size )
#endif
}
static qboolean Host_CollectX86Libraries( ECommonLibraryType lib_type,
const char *win_path, const char *lin_path, const char *osx_path,
char *found, size_t found_size )
{
string native_path;
qboolean has_any = false;
found[0] = 0;
COM_GetCommonLibraryPath( lib_type, native_path, sizeof( native_path ));
if( Platform_LibraryExists( native_path, true ))
return 0;
#if !( XASH_WIN32 && XASH_X86 )
if( !COM_StringEmpty( win_path ) && FS_FileExists( win_path, true ))
{
Q_strncat( found, "Windows (x86)", found_size );
has_any = true;
}
#endif
#if !( XASH_LINUX && !XASH_ANDROID && XASH_X86 )
if( !COM_StringEmpty( lin_path ) && FS_FileExists( lin_path, true ))
{
if( has_any )
Q_strncat( found, ", ", found_size );
Q_strncat( found, "GNU/Linux (x86)", found_size );
has_any = true;
}
#endif
#if !( XASH_APPLE && XASH_X86 )
if( !COM_StringEmpty( osx_path ) && FS_FileExists( osx_path, true ))
{
if( has_any )
Q_strncat( found, ", ", found_size );
Q_strncat( found, "macOS (x86)", found_size );
has_any = true;
}
#endif
return has_any;
}
static void Host_CheckGameLibraries( void )
{
// on Android, game libraries are loaded from APKs and are invisible to FS_FileExists,
// so the check cannot work there; iOS is handled via Platform_LibraryExists -> IOS_LibraryExists
#if !defined( XASH_INTERNAL_GAMELIBS ) && !XASH_ANDROID
struct
{
const char *name;
ECommonLibraryType type;
const char *override; // host.gamedll / host.clientlib / host.menulib
} libs[3] = {
{ "client", LIBRARY_CLIENT, host.clientlib },
{ "server", LIBRARY_SERVER, host.gamedll },
{ "menu", LIBRARY_GAMEUI, host.menulib },
};
char details[MAX_VA_STRING];
details[0] = 0;
for( int i = 0; i < ARRAYSIZE( libs ); i++ )
{
string found;
qboolean ret;
// if the user explicitly set a library path, trust them and skip the check
if( !COM_StringEmpty( libs[i].override ))
continue;
if( libs[i].type == LIBRARY_SERVER )
{
// missing server library is only critical when singleplayer is available
// mirrors silent mode in SV_InitGame
if( GI->gamemode != GAME_SINGLEPLAYER_ONLY )
continue;
ret = Host_CollectX86Libraries( libs[i].type,
GI->game_dll, GI->game_dll_linux, GI->game_dll_osx,
found, sizeof( found ));
}
else
{
string win, lin, osx;
Q_snprintf( win, sizeof( win ), "%s/%s.dll", GI->dll_path, libs[i].name );
Q_snprintf( lin, sizeof( lin ), "%s/%s.so", GI->dll_path, libs[i].name );
Q_snprintf( osx, sizeof( osx ), "%s/%s.dylib", GI->dll_path, libs[i].name );
ret = Host_CollectX86Libraries( libs[i].type,
win, lin, osx, found, sizeof( found ));
}
if( ret )
{
size_t len = Q_strlen( details );
Q_snprintf( details + len, sizeof( details ) - len, "- %s: %s\n", libs[i].name, found );
}
}
if( COM_StringEmpty( details ))
return;
Sys_Warn( "No native game libraries found for current platform (%s-%s),\n"
"but found libraries for other platforms:\n"
"%s"
"The game may fail to load or work incorrectly.\n"
"Consider using a mod version built for this platform.",
Q_buildos(), Q_buildarch(), details );
#endif // XASH_INTERNAL_GAMELIBS
}
/*
=================
Host_InitCommon
@@ -913,6 +1006,7 @@ static void Host_InitCommon( int argc, char **argv, const char *progname, qboole
host.config_executed = false;
host.status = HOST_INIT; // initialzation started
host.type = HOST_DEDICATED; // predict state
Q_strncpy( host.default_gamedir, basedir, sizeof( host.default_gamedir ));
#ifndef XASH_DEDICATED
if( !Sys_CheckParm( "-dedicated" ))
@@ -985,8 +1079,8 @@ static void Host_InitCommon( int argc, char **argv, const char *progname, qboole
#if XASH_DEDICATED
Platform_SetupSigtermHandling();
#endif
Platform_Init( Host_IsDedicated( ) || developer >= DEV_EXTENDED, basedir );
FS_Init( basedir );
Platform_Init( Host_IsDedicated( ) || developer >= DEV_EXTENDED );
FS_Init();
// print current developer level to simplify processing users feedback
if( developer > 0 )
@@ -1018,6 +1112,7 @@ static void Host_InitCommon( int argc, char **argv, const char *progname, qboole
#endif
FS_LoadGameInfo();
Host_CheckGameLibraries();
Cvar_PostFSInit();
Image_CheckPaletteQ1 ();

View File

@@ -2587,7 +2587,7 @@ static void Mod_InitSkyClouds( model_t *mod, const mip_t *mt, texture_t *tx, qbo
r_temp.palette = NULL;
// load it in
solidskyTexture = GL_LoadTextureInternal( "solid_sky", &r_temp, TF_NOMIPMAP );
solidskyTexture = GL_LoadTextureInternal( "solid_sky", &r_temp, TF_NOMIPMAP | TF_ALLOW_NEAREST );
for( i = 0; i < r_sky->width >> 1; i++ )
{
@@ -2610,7 +2610,7 @@ static void Mod_InitSkyClouds( model_t *mod, const mip_t *mt, texture_t *tx, qbo
r_temp.flags = IMAGE_HAS_COLOR|IMAGE_HAS_ALPHA;
// load it in
alphaskyTexture = GL_LoadTextureInternal( "alpha_sky", &r_temp, TF_NOMIPMAP );
alphaskyTexture = GL_LoadTextureInternal( "alpha_sky", &r_temp, TF_NOMIPMAP | TF_ALLOW_NEAREST );
// clean up
FS_FreeImage( r_sky );
@@ -3599,6 +3599,10 @@ Mod_LoadVisibility
*/
static void Mod_LoadVisibility( model_t *mod, dbspmodel_t *bmod )
{
// external bmodels have no visibility
if( !bmod->visdata || !bmod->visdatasize )
return;
mod->visdata = Mem_Malloc( mod->mempool, bmod->visdatasize );
memcpy( mod->visdata, bmod->visdata, bmod->visdatasize );
}
@@ -3793,7 +3797,7 @@ static fs_offset_t Mod_FindBSPX( const byte *mod_base, size_t bufferlen )
max_offset = ALIGN( max_offset, 4 ); // force 32-bit boundary
if( max_offset > bufferlen )
if( max_offset + sizeof( dbspx_hdr_t ) > bufferlen )
return -1;
bspx_header = (const dbspx_hdr_t *)( mod_base + max_offset );
@@ -3878,8 +3882,11 @@ static qboolean Mod_LoadBmodelLumps( model_t *mod, byte *mod_base, size_t buffer
Mod_LoadLump( mod_base, &extlumps[i], &worldstats[stat_index], flags, LOADLUMP_BSP30EXT, NULL );
// loading bspx lumps
for( i = 0; i < ARRAYSIZE( bspxlumps ); i++, stat_index++ )
Mod_LoadLump( mod_base, &bspxlumps[i], &worldstats[stat_index], flags, LOADLUMP_BSPX, mod_base + bspx_header_offset );
if( bspx_header_offset >= 0 )
{
for( i = 0; i < ARRAYSIZE( bspxlumps ); i++, stat_index++ )
Mod_LoadLump( mod_base, &bspxlumps[i], &worldstats[stat_index], flags, LOADLUMP_BSPX, mod_base + bspx_header_offset );
}
if( !bmod->isworld ) // a1ba: why world excluded here?
{

View File

@@ -1658,7 +1658,7 @@ void Netchan_TransmitBits( netchan_t *chan, int length, const byte *data )
MSG_Init( &send, "NetSend", send_buf, sizeof( send_buf ));
// prepare the packet header
w1 = chan->outgoing_sequence | (send_reliable << 31);
w1 = chan->outgoing_sequence | (((uint)send_reliable ) << 31);
w2 = chan->incoming_sequence | (chan->incoming_reliable_sequence << 31);
send_reliable_fragment = false;

View File

@@ -112,7 +112,6 @@ msurface_t *PM_RecursiveSurfCheck( model_t *mod, mnode_t *node, vec3_t p1, vec3_
int i, side;
msurface_t *surf;
vec3_t mid;
mnode_t *children[2];
int numsurfaces, firstsurface;
loc0:
@@ -122,17 +121,15 @@ loc0:
t1 = PlaneDiff( p1, node->plane );
t2 = PlaneDiff( p2, node->plane );
node_children( children, node, mod );
if( t1 >= -FRAC_EPSILON && t2 >= -FRAC_EPSILON )
{
node = children[0];
node = node_child( node, 0, mod );
goto loc0;
}
if( t1 < FRAC_EPSILON && t2 < FRAC_EPSILON )
{
node = children[1];
node = node_child( node, 1, mod );
goto loc0;
}
@@ -142,7 +139,7 @@ loc0:
VectorLerp( p1, frac, p2, mid );
if(( surf = PM_RecursiveSurfCheck( mod, children[side], p1, mid )) != NULL )
if(( surf = PM_RecursiveSurfCheck( mod, node_child( node, side, mod ), p1, mid )) != NULL )
return surf;
// walk through real faces
@@ -179,7 +176,7 @@ loc0:
return NULL; // through the fence
}
return PM_RecursiveSurfCheck( mod, children[side^1], mid, p2 );
return PM_RecursiveSurfCheck( mod, node_child( node, side^1, mod ), mid, p2 );
}
/*
@@ -234,7 +231,6 @@ static int PM_TestLine_r( model_t *mod, mnode_t *node, vec_t p1f, vec_t p2f, con
float frac, midf;
int i, r, side;
vec3_t mid;
mnode_t *children[2];
int numsurfaces, firstsurface;
loc0:
@@ -252,17 +248,15 @@ loc0:
front = PlaneDiff( start, node->plane );
back = PlaneDiff( stop, node->plane );
node_children( children, node, mod );
if( front >= -FRAC_EPSILON && back >= -FRAC_EPSILON )
{
node = children[0];
node = node_child( node, 0, mod );
goto loc0;
}
if( front < FRAC_EPSILON && back < FRAC_EPSILON )
{
node = children[1];
node = node_child( node, 1, mod );
goto loc0;
}
@@ -273,7 +267,7 @@ loc0:
VectorLerp( start, frac, stop, mid );
midf = p1f + ( p2f - p1f ) * frac;
r = PM_TestLine_r( mod, children[side], p1f, midf, start, mid, trace );
r = PM_TestLine_r( mod, node_child( node, side, mod ), p1f, midf, start, mid, trace );
if( r != CONTENTS_EMPTY )
{
@@ -322,7 +316,7 @@ loc0:
return contents;
}
return PM_TestLine_r( mod, children[!side], midf, p2f, mid, stop, trace );
return PM_TestLine_r( mod, node_child( node, !side, mod ), midf, p2f, mid, stop, trace );
}
int PM_TestLineExt( playermove_t *pmove, physent_t *ents, int numents, const vec3_t start, const vec3_t end, int flags )

View File

@@ -84,6 +84,9 @@ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
#define K_JOY3 205
#define K_JOY4 206
#define K_LTRIGGER K_JOY1
#define K_RTRIGGER K_JOY2
//
// aux keys are for multi-buttoned joysticks to generate so they can use
// the normal binding process

View File

@@ -222,7 +222,7 @@ typedef struct ui_extendedfuncs_s {
// new mods info
gameinfo2_t *(*pfnGetGameInfo)( int gi_version ); // might return NULL if gi_version is unsupported
gameinfo2_t *(*pfnGetModInfo)( int gi_version, int mod_index ); // continiously call it until it returns null
gameinfo2_t *(*pfnGetModInfo)( int gi_version, int mod_index ); // continuously call it until it returns null
// returns 1 if cvar has read-only flag
// or -1 if cvar not found

View File

@@ -21,9 +21,6 @@
#include <sys/stat.h>
#include "dlfcn.h"
#ifndef XASH_GAMEDIR
#define XASH_GAMEDIR "valve" // !!! Replace with your default (base) game directory !!!
#endif
#define XASHLIB "@rpath/libxash.dylib"
@@ -331,4 +328,4 @@ int IOS_GetArgs( char ***out )
{
*out = szArgv;
return szArgc;
}
}

View File

@@ -13,63 +13,83 @@ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
*/
#include <string.h>
#include <unistd.h>
#include <SDL.h>
#include <dlfcn.h>
#include <sys/stat.h>
#include "crtlib.h"
#include "fscallback.h"
#include "library.h"
#include "platform/ios/lib_ios.h"
#include <dlfcn.h>
#include "common.h"
#define EXT_LENGTH 5
const char *g_szLibrarySuffix;
static void *IOS_LoadLibraryInternal( const char *dllname )
{
void *pHandle;
string errorstring = "";
char path[MAX_SYSPATH];
Q_snprintf( path, MAX_SYSPATH, "%s%s", SDL_GetBasePath(), dllname );
Q_snprintf( path, sizeof( path ), "%s%s", SDL_GetBasePath(), dllname );
pHandle = dlopen( path, RTLD_LAZY );
if( !pHandle )
{
COM_PushLibraryError(errorstring);
COM_PushLibraryError(dlerror());
}
COM_PushLibraryError(dlerror( ));
return pHandle;
}
const char *g_szLibrarySuffix;
static qboolean IOS_LibraryExistsInternal( const char *name )
{
struct stat buf;
char path[MAX_SYSPATH];
Q_snprintf( path, sizeof( path ), "%s%s", SDL_GetBasePath(), name );
return stat( path, &buf ) == 0;
}
static const char *IOS_GetLibraryPostfix( void )
{
if( g_szLibrarySuffix )
return g_szLibrarySuffix;
return Q_strcmp( host.default_gamedir, FS_Gamedir( )) ? FS_Gamedir( ) : "";
}
static void IOS_PrepareGameLibraryPath( const char *dllname, char *out, size_t outsize )
{
string strippedname;
Q_strncpy( strippedname, dllname, sizeof( strippedname ));
COM_StripExtension( strippedname );
Q_snprintf( out, outsize, "%s_%s.dylib", strippedname, IOS_GetLibraryPostfix( ));
}
void *IOS_LoadLibrary( const char *dllname )
{
//Immediately load if the library is filesystem_stdio.dylib or we will crash when accessing gamedir
if ( !Q_strcmp( dllname, "filesystem_stdio.dylib" ) )
{
// filesystem_stdio is a special case, as engine won't work correctly without it
// but it's always located at known path
if( !Q_strcmp( dllname, "filesystem_stdio.dylib" ))
return IOS_LoadLibraryInternal( dllname );
}
string name;
string strippedname;
const char *postfix = g_szLibrarySuffix;
char *pHandle;
if( !postfix )
{
if ( Q_strcmp(FS_Gamedir(), "valve" ) )
{
postfix = FS_Gamedir( );
}
else postfix = "";
}
Q_strncpy( strippedname, dllname, sizeof( strippedname ) );
COM_StripExtension(strippedname);
Q_snprintf( name, MAX_STRING, "%s_%s.dylib", strippedname, postfix );
IOS_PrepareGameLibraryPath( dllname, name, sizeof( name ));
pHandle = IOS_LoadLibraryInternal( name );
if( pHandle )
return pHandle;
if( !pHandle )
pHandle = IOS_LoadLibraryInternal( dllname );
return IOS_LoadLibraryInternal( dllname );
return pHandle;
}
qboolean IOS_LibraryExists( const char *dllname )
{
string name;
IOS_PrepareGameLibraryPath( dllname, name, sizeof( name ));
return IOS_LibraryExistsInternal( name ) || IOS_LibraryExistsInternal( dllname );
}

View File

@@ -20,6 +20,7 @@ GNU General Public License for more details.
#define Platform_POSIX_LoadLibrary( x ) IOS_LoadLibrary(( x ))
void *IOS_LoadLibrary( const char *dllname );
qboolean IOS_LibraryExists( const char *name );
#endif // IOS_LIB_H
#endif // TARGET_OS_IPHONE

View File

@@ -105,8 +105,8 @@ qboolean SNDDMA_Init( void )
return false;
}
dma.format.speed = SOUND_DMA_SPEED;
r = dma.format.speed;
snd.format.speed = SOUND_DMA_SPEED;
r = snd.format.speed;
if( ( err = snd_pcm_hw_params_set_rate_near( s_alsa.pcm_handle, s_alsa.hw_params, &r, &dir ) ) < 0 )
{
@@ -121,14 +121,14 @@ qboolean SNDDMA_Init( void )
if( dir != 0 )
{
Con_Printf( "ALSA: rate %d not supported, using %d\n", SOUND_DMA_SPEED, r );
dma.format.speed = r;
snd.format.speed = r;
dir = 0;
}
}
dma.format.channels = 2;
snd.format.channels = 2;
if( ( err = snd_pcm_hw_params_set_channels(s_alsa.pcm_handle, s_alsa.hw_params, dma.format.channels ) ) < 0 )
if( ( err = snd_pcm_hw_params_set_channels(s_alsa.pcm_handle, s_alsa.hw_params, snd.format.channels ) ) < 0 )
{
Con_Printf( "ALSA: cannot set channels %d(%s)\n", 2, snd_strerror( err ) );
snd_pcm_hw_params_free( s_alsa.hw_params );
@@ -162,7 +162,7 @@ qboolean SNDDMA_Init( void )
}
else
{
// if period is NPOT it cannot be used as dma.samples in Xash3D
// if period is NPOT it cannot be used as snd.samples in Xash3D
// and need more space to send buffer partially
samples = 1;
while( samples < p * 4 )
@@ -184,15 +184,15 @@ qboolean SNDDMA_Init( void )
return false;
}
dma.buffer = Mem_Calloc( sndpool, samples * 2 ); //allocate pcm frame buffer
dma.samplepos = 0;
dma.samples = samples;
dma.format.width = 2;
dma.initialized = 1;
dma.backendName = "ALSA";
snd.buffer = Mem_Calloc( sndpool, samples * 2 ); //allocate pcm frame buffer
snd.samplepos = 0;
snd.samples = samples;
snd.format.width = 2;
snd.initialized = 1;
snd.backend_name = "ALSA";
snd_pcm_prepare( s_alsa.pcm_handle );
snd_pcm_writei( s_alsa.pcm_handle, dma.buffer, 2 * s_alsa.period_size );
snd_pcm_writei( s_alsa.pcm_handle, snd.buffer, 2 * s_alsa.period_size );
snd_pcm_start( s_alsa.pcm_handle );
return true;
@@ -208,14 +208,14 @@ Closes the ALSA pcm device and frees the dma buffer.
void SNDDMA_Shutdown(void)
{
Con_Printf( "Shutting down audio.\n" );
dma.initialized = false;
snd.initialized = false;
if( dma.buffer )
if( snd.buffer )
{
snd_pcm_drop( s_alsa.pcm_handle );
snd_pcm_close( s_alsa.pcm_handle );
Mem_Free( dma.buffer );
dma.buffer = NULL;
Mem_Free( snd.buffer );
snd.buffer = NULL;
}
}
@@ -240,38 +240,38 @@ void SNDDMA_Submit( void )
while( avail >= s_alsa.period_size )
{
int size = dma.samples << 1;
int pos = dma.samplepos << 1;
int size = snd.samples << 1;
int pos = snd.samplepos << 1;
unsigned long len = s_alsa.period_size * 4;
int wrapped = pos + len - size;
int w;
if( wrapped < 0 )
{
w = snd_pcm_writei( s_alsa.pcm_handle, dma.buffer + pos, len / 4 );
w = snd_pcm_writei( s_alsa.pcm_handle, snd.buffer + pos, len / 4 );
if( w < 0 )
{
snd_pcm_prepare(s_alsa.pcm_handle);
return;
}
dma.samplepos += len >> 1;
snd.samplepos += len >> 1;
}
else
{
int remaining = size - pos;
w = snd_pcm_writei( s_alsa.pcm_handle, dma.buffer + pos, remaining / 4 );
w = snd_pcm_writei( s_alsa.pcm_handle, snd.buffer + pos, remaining / 4 );
if( w < 0 )
{
snd_pcm_prepare(s_alsa.pcm_handle);
return;
}
w = snd_pcm_writei( s_alsa.pcm_handle, dma.buffer, wrapped / 4 );
w = snd_pcm_writei( s_alsa.pcm_handle, snd.buffer, wrapped / 4 );
if( w < 0 )
{
snd_pcm_prepare(s_alsa.pcm_handle);
return;
}
dma.samplepos = wrapped >> 1;
snd.samplepos = wrapped >> 1;
}
avail = snd_pcm_avail_update( s_alsa.pcm_handle );
@@ -282,11 +282,11 @@ void SNDDMA_Submit( void )
int s, w, frames;
void *start;
if( !dma.buffer )
if( !snd.buffer )
return;
s = dma.samplepos * 2;
start = (void *)&dma.buffer[s];
s = snd.samplepos * 2;
start = (void *)&snd.buffer[s];
frames = s_alsa.period_size / 2;
// write to card
if( ( w = snd_pcm_writei( s_alsa.pcm_handle, start, frames ) ) < 0)
@@ -296,10 +296,10 @@ void SNDDMA_Submit( void )
return;
}
dma.samplepos += w * 2; // mark progress
snd.samplepos += w * 2; // mark progress
if(dma.samplepos >= dma.samples)
dma.samplepos = 0; // wrap buffer
if(snd.samplepos >= snd.samples)
snd.samplepos = 0; // wrap buffer
}
}
@@ -324,7 +324,7 @@ between a deactivate and an activate.
*/
void SNDDMA_Activate( qboolean active )
{
if( !dma.initialized )
if( !snd.initialized )
return;
s_alsa.paused = !active;

View File

@@ -66,10 +66,15 @@ qboolean Platform_DebuggerPresent( void )
tracer_pid = (const byte*)Q_strstr( buf, TracerPid );
if( !tracer_pid )
return false;
//printf( "%s\n", tracer_pid );
while( *tracer_pid < '0' || *tracer_pid > '9' )
while(( *tracer_pid < '0' ) || ( *tracer_pid > '9' ))
{
if( *tracer_pid++ == '\n' )
return false;
}
//printf( "%s\n", tracer_pid );
return !!Q_atoi( (const char*)tracer_pid );
}

View File

@@ -18,6 +18,8 @@ GNU General Public License for more details.
#define PLATFORM_H
#include <errno.h>
#define FSCALLBACK_OVERRIDE_MALLOC_LIKE
#include "fscallback.h"
#include "common.h"
#include "system.h"
#include "defaults.h"
@@ -43,6 +45,7 @@ qboolean Platform_DebuggerPresent( void );
int IOS_GetArgs( char ***argv );
const char *IOS_GetDocsDir( void );
void IOS_LaunchDialog( void );
#include "platform/ios/lib_ios.h"
#endif // TARGET_OS_IOS
#if XASH_WIN32 || XASH_LINUX
@@ -58,7 +61,7 @@ char *Posix_Input( void );
#endif
#if XASH_SDL
void SDLash_Init( const char *basedir );
void SDLash_Init( void );
void SDLash_Shutdown( void );
void SDLash_NanoSleep( int nsec );
#endif
@@ -111,7 +114,7 @@ void Linux_SetTimer( float time );
int Linux_GetProcessID( void );
#endif
static inline void Platform_Init( qboolean con_showalways, const char *basedir )
static inline void Platform_Init( qboolean con_showalways )
{
#if XASH_POSIX
// daemonize as early as possible, because we need to close our file descriptors
@@ -119,7 +122,7 @@ static inline void Platform_Init( qboolean con_showalways, const char *basedir )
#endif
#if XASH_SDL
SDLash_Init( basedir );
SDLash_Init( );
#endif
#if XASH_ANDROID
@@ -207,6 +210,18 @@ static inline void Sys_RestoreCrashHandler( void )
}
#endif
static inline qboolean Platform_LibraryExists( const char *name, qboolean gamedironly )
{
#if XASH_IOS
return IOS_LibraryExists( name );
#elif XASH_ANDROID
// sorry, unimplemented
return false;
#else
return g_fsapi.FileExists( name, gamedironly );
#endif
}
/*
==============================================================================
@@ -389,14 +404,16 @@ qboolean VoiceCapture_Lock( qboolean lock );
pid_t raise_tid = Linux_GetProcessID(); \
int raise_sig = (x); \
__asm__ volatile ( \
"push {r7}\n\t" \
"mov r7,#268\n\t" \
"mov r0,%0\n\t" \
"mov r1,%1\n\t" \
"mov r2,%2\n\t" \
"svc 0\n\t" \
"pop {r7}\n\t" \
: \
: "r"(raise_pid), "r"(raise_tid), "r"(raise_sig) \
: "r0", "r1", "r2", "r7", "memory" \
: "r0", "r1", "r2", "memory" \
); \
} while( 0 )
#define INLINE_NANOSLEEP1() do \
@@ -404,13 +421,15 @@ qboolean VoiceCapture_Lock( qboolean lock );
struct timespec ns_t1 = {1, 0}; \
struct timespec ns_t2 = {0, 0}; \
__asm__ volatile ( \
"push {r7}\n\t" \
"mov r7,#162\n\t" \
"mov r0,%0\n\t" \
"mov r1,%1\n\t" \
"svc 0\n\t" \
"pop {r7}\n\t" \
: \
: "r"(&ns_t1), "r"(&ns_t2) \
: "r0", "r1", "r7", "memory" \
: "r0", "r1", "memory" \
); \
} while( 0 )
#elif XASH_LINUX && XASH_ARM && XASH_64BIT

View File

@@ -46,38 +46,38 @@ static char sdl_backend_name[32];
static void SDL_SoundCallback( void *userdata, Uint8 *stream, int len )
{
const int size = dma.samples << 1;
const int size = snd.samples << 1;
int pos;
int wrapped;
if( !dma.buffer )
if( !snd.buffer )
{
memset( stream, 0, len );
return;
}
pos = dma.samplepos << 1;
pos = snd.samplepos << 1;
if( pos >= size )
pos = dma.samplepos = 0;
pos = snd.samplepos = 0;
wrapped = pos + len - size;
if( wrapped < 0 )
{
memcpy( stream, dma.buffer + pos, len );
dma.samplepos += len >> 1;
memcpy( stream, snd.buffer + pos, len );
snd.samplepos += len >> 1;
}
else
{
int remaining = size - pos;
memcpy( stream, dma.buffer + pos, remaining );
memcpy( stream + remaining, dma.buffer, wrapped );
dma.samplepos = wrapped >> 1;
memcpy( stream, snd.buffer + pos, remaining );
memcpy( stream + remaining, snd.buffer, wrapped );
snd.samplepos = wrapped >> 1;
}
if( dma.samplepos >= size )
dma.samplepos = 0;
if( snd.samplepos >= size )
snd.samplepos = 0;
}
/*
@@ -131,20 +131,20 @@ qboolean SNDDMA_Init( void )
goto fail;
}
dma.format.speed = obtained.freq;
dma.format.channels = obtained.channels;
dma.format.width = 2;
snd.format.speed = obtained.freq;
snd.format.channels = obtained.channels;
snd.format.width = 2;
samplecount = s_samplecount.value;
if( !samplecount )
samplecount = 0x8000;
dma.samples = samplecount * obtained.channels;
dma.buffer = Mem_Calloc( sndpool, dma.samples * 2 );
dma.samplepos = 0;
snd.samples = samplecount * obtained.channels;
snd.buffer = Mem_Calloc( sndpool, snd.samples * 2 );
snd.samplepos = 0;
Con_Printf( "Using SDL audio driver: %s @ %d Hz\n", SDL_GetCurrentAudioDriver( ), obtained.freq );
Q_snprintf( sdl_backend_name, sizeof( sdl_backend_name ), "SDL" );
dma.initialized = true;
dma.backendName = sdl_backend_name;
snd.initialized = true;
snd.backend_name = sdl_backend_name;
SNDDMA_Activate( true );
@@ -191,7 +191,7 @@ Reset the sound device for exiting
void SNDDMA_Shutdown( void )
{
Con_Printf( "Shutting down audio.\n" );
dma.initialized = false;
snd.initialized = false;
if( sdl_dev )
{
@@ -203,10 +203,10 @@ void SNDDMA_Shutdown( void )
if( SDL_WasInit( SDL_INIT_AUDIO ))
SDL_QuitSubSystem( SDL_INIT_AUDIO );
if( dma.buffer )
if( snd.buffer )
{
Mem_Free( dma.buffer );
dma.buffer = NULL;
Mem_Free( snd.buffer );
snd.buffer = NULL;
}
}
@@ -220,7 +220,7 @@ between a deactivate and an activate.
*/
void SNDDMA_Activate( qboolean active )
{
if( !dma.initialized )
if( !snd.initialized )
return;
SDL_PauseAudioDevice( sdl_dev, !active );

View File

@@ -41,8 +41,8 @@ static const engineAxis_t g_axis_mapping[] =
{
JOY_AXIS_SIDE, // SDL_CONTROLLER_AXIS_LEFTX,
JOY_AXIS_FWD, // SDL_CONTROLLER_AXIS_LEFTY,
JOY_AXIS_PITCH, // SDL_CONTROLLER_AXIS_RIGHTX,
JOY_AXIS_YAW, // SDL_CONTROLLER_AXIS_RIGHTY,
JOY_AXIS_YAW, // SDL_CONTROLLER_AXIS_RIGHTX,
JOY_AXIS_PITCH, // SDL_CONTROLLER_AXIS_RIGHTY,
JOY_AXIS_LT, // SDL_CONTROLLER_AXIS_TRIGGERLEFT,
JOY_AXIS_RT, // SDL_CONTROLLER_AXIS_TRIGGERRIGHT,
};
@@ -52,14 +52,16 @@ static SDL_GameController *g_current_gamepad;
static SDL_GameController **g_gamepads;
static size_t g_num_gamepads;
#define CALIBRATION_TIME 10.0f
#define CALIBRATION_TIME 5.0f
static struct
{
float time;
vec3_t data;
vec3_t calibrated_values;
int samples;
float time;
vec3_t data;
vec3_t calibrated_values;
float data_rate;
int samples;
qboolean continuous; // true after first successful calibration
} gyrocal;
static void SDLash_RestartCalibration( void )
@@ -70,35 +72,69 @@ static void SDLash_RestartCalibration( void )
gyrocal.time = host.realtime + CALIBRATION_TIME;
Con_Printf( "Starting gyroscope calibration...\n" );
#if SDL_VERSION_ATLEAST( 2, 0, 16 )
gyrocal.data_rate = SDL_GameControllerGetSensorDataRate( g_current_gamepad, SDL_SENSOR_GYRO );
#endif
if( !gyrocal.data_rate )
gyrocal.data_rate = 10.0f;
Con_Printf( S_NOTE "Starting gyroscope calibration at %g data rate for %g seconds...\n", gyrocal.data_rate, CALIBRATION_TIME );
}
static void SDLash_FinalizeCalibration( void )
{
float data_rate = 10.0f; // let's say we're polling at 10Hz?
int min_samples;
#if SDL_VERSION_ATLEAST( 2, 0, 16 )
data_rate = SDL_GameControllerGetSensorDataRate( g_current_gamepad, SDL_SENSOR_GYRO );
if( !data_rate )
data_rate = 10.0f;
#endif
min_samples = Q_rint( CALIBRATION_TIME * data_rate * 0.75f );
int min_samples = Q_rint( CALIBRATION_TIME * gyrocal.data_rate * 0.5f );
// we waited for few seconds and got too few samples
if( gyrocal.samples <= min_samples )
{
Joy_SetCalibrationState( JOY_FAILED_TO_CALIBRATE );
return;
if( !gyrocal.continuous )
{
Joy_SetCalibrationState( JOY_FAILED_TO_CALIBRATE );
Con_Printf( S_ERROR "Calibration failed, got samples %d < %d\n", gyrocal.samples, min_samples );
gyrocal.time = 0.0f;
return;
}
// Con_Reportf( S_WARN "Continuous calibration: too few samples (%d < %d), retrying\n", gyrocal.samples, min_samples );
}
else
{
VectorScale( gyrocal.data, 1.0f / gyrocal.samples, gyrocal.calibrated_values );
Joy_SetCalibrationState( JOY_CALIBRATED );
if( !gyrocal.continuous )
Con_Printf( "Calibration done. Result: %f %f %f at %d samples\n", gyrocal.calibrated_values[0], gyrocal.calibrated_values[1], gyrocal.calibrated_values[2], gyrocal.samples );
// else
// Con_Reportf( "Continuous calibration done. Result: %f %f %f at %d samples\n", gyrocal.calibrated_values[0], gyrocal.calibrated_values[1], gyrocal.calibrated_values[2], gyrocal.samples );
gyrocal.continuous = true;
}
VectorScale( gyrocal.data, 1.0f / gyrocal.samples, gyrocal.calibrated_values );
Joy_SetCalibrationState( JOY_CALIBRATED );
// schedule next calibration window
VectorClear( gyrocal.data );
gyrocal.samples = 0;
gyrocal.time = host.realtime + CALIBRATION_TIME;
}
Con_Printf( "Calibration done. Result: %f %f %f\n", gyrocal.calibrated_values[0], gyrocal.calibrated_values[1], gyrocal.calibrated_values[2] );
static void SDLash_AccumulateCalibrationData( vec3_t data )
{
// for continuous background calibration only listen for noise
// by comparing it with calibrated values
//
// for first calibration this might be hurtful as device might
// output offset data
if( gyrocal.continuous )
{
vec3_t calibrated;
VectorSubtract( data, gyrocal.calibrated_values, calibrated );
gyrocal.time = 0.0f;
if( VectorLength( calibrated ) > 0.1f )
return;
}
VectorAdd( gyrocal.data, data, gyrocal.data );
gyrocal.samples++;
if( !gyrocal.continuous )
Joy_SetCalibrationState( JOY_CALIBRATING );
}
static void SDLash_GameControllerAddMappings( const char *name )
@@ -125,7 +161,8 @@ static void SDLash_SetActiveGameController( SDL_JoystickID id )
#if SDL_VERSION_ATLEAST( 2, 0, 14 )
// going to change active controller, disable gyro events in old
SDL_GameControllerSetSensorEnabled( g_current_gamepad, SDL_SENSOR_GYRO, SDL_FALSE );
if( g_current_gamepad )
SDL_GameControllerSetSensorEnabled( g_current_gamepad, SDL_SENSOR_GYRO, SDL_FALSE );
#endif // SDL_VERSION_ATLEAST( 2, 0, 14 )
g_current_gamepad_id = id;
@@ -243,12 +280,12 @@ static void SDLash_GameControllerSensorUpdate( SDL_ControllerSensorEvent sensor
{
if( host.realtime > gyrocal.time )
SDLash_FinalizeCalibration();
else
SDLash_AccumulateCalibrationData( sensor.data );
VectorAdd( gyrocal.data, sensor.data, gyrocal.data );
gyrocal.samples++;
Joy_SetCalibrationState( JOY_CALIBRATING );
return;
// block gyro events only during initial calibration
if( !gyrocal.continuous )
return;
}
VectorSubtract( sensor.data, gyrocal.calibrated_values, data );
@@ -335,6 +372,9 @@ int Platform_JoyInit( void )
{
int count, numJoysticks, i;
SDL_SetHint( SDL_HINT_JOYSTICK_HIDAPI_PS4_RUMBLE, "1" );
SDL_SetHint( SDL_HINT_JOYSTICK_HIDAPI_PS5_RUMBLE, "1" );
Con_Reportf( "Joystick: SDL GameController API\n" );
if( SDL_WasInit( SDL_INIT_GAMECONTROLLER ) != SDL_INIT_GAMECONTROLLER &&
SDL_InitSubSystem( SDL_INIT_GAMECONTROLLER ))

View File

@@ -39,32 +39,32 @@ static char sdl_backend_name[32];
static void SDL_SoundCallback( void *userdata, Uint8 *stream, int len )
{
const int size = dma.samples << 1;
const int size = snd.samples << 1;
int pos;
int wrapped;
pos = dma.samplepos << 1;
pos = snd.samplepos << 1;
if( pos >= size )
pos = dma.samplepos = 0;
pos = snd.samplepos = 0;
wrapped = pos + len - size;
if( wrapped < 0 )
{
memcpy( stream, dma.buffer + pos, len );
dma.samplepos += len >> 1;
memcpy( stream, snd.buffer + pos, len );
snd.samplepos += len >> 1;
}
else
{
int remaining = size - pos;
memcpy( stream, dma.buffer + pos, remaining );
memcpy( stream + remaining, dma.buffer, wrapped );
dma.samplepos = wrapped >> 1;
memcpy( stream, snd.buffer + pos, remaining );
memcpy( stream + remaining, snd.buffer, wrapped );
snd.samplepos = wrapped >> 1;
}
if( dma.samplepos >= size )
dma.samplepos = 0;
if( snd.samplepos >= size )
snd.samplepos = 0;
}
/*
@@ -148,22 +148,22 @@ qboolean SNDDMA_Init( void )
goto fail;
}
dma.format.speed = obtained.freq;
dma.format.channels = obtained.channels;
dma.format.width = 2;
snd.format.speed = obtained.freq;
snd.format.channels = obtained.channels;
snd.format.width = 2;
samplecount = s_samplecount.value;
if( !samplecount )
samplecount = 0x8000;
dma.samples = samplecount * obtained.channels;
dma.buffer = Mem_Calloc( sndpool, dma.samples * 2 );
dma.samplepos = 0;
snd.samples = samplecount * obtained.channels;
snd.buffer = Mem_Calloc( sndpool, snd.samples * 2 );
snd.samplepos = 0;
sdl_format = obtained.format;
Con_Printf( "Using SDL audio driver: %s @ %d Hz\n", SDL_GetCurrentAudioDriver( ), obtained.freq );
Q_snprintf( sdl_backend_name, sizeof( sdl_backend_name ), "SDL (%s)", SDL_GetCurrentAudioDriver( ));
dma.initialized = true;
dma.backendName = sdl_backend_name;
snd.initialized = true;
snd.backend_name = sdl_backend_name;
SNDDMA_Activate( true );
@@ -179,7 +179,7 @@ fail:
==============
SNDDMA_BeginPainting
Makes sure dma.buffer is valid
Makes sure snd.buffer is valid
===============
*/
void SNDDMA_BeginPainting( void )
@@ -210,7 +210,7 @@ Reset the sound device for exiting
void SNDDMA_Shutdown( void )
{
Con_Printf( "Shutting down audio.\n" );
dma.initialized = false;
snd.initialized = false;
if( sdl_dev )
{
@@ -221,10 +221,10 @@ void SNDDMA_Shutdown( void )
SDL_QuitSubSystem( SDL_INIT_AUDIO );
if( dma.buffer )
if( snd.buffer )
{
Mem_Free( dma.buffer );
dma.buffer = NULL;
Mem_Free( snd.buffer );
snd.buffer = NULL;
}
}
@@ -238,7 +238,7 @@ between a deactivate and an activate.
*/
void SNDDMA_Activate( qboolean active )
{
if( !dma.initialized )
if( !snd.initialized )
return;
SDL_PauseAudioDevice( sdl_dev, !active );

View File

@@ -79,20 +79,23 @@ static void SDLCALL SDLash_LogOutputFunction( void *userdata, int category, SDL_
case SDL_LOG_PRIORITY_INFO:
str = S_NOTE;
break;
default:
str = "";
break;
}
Con_Reportf( "%s" S_BLUE "SDL" S_DEFAULT ": [%s] %s\n", str, SDLash_CategoryToString( category ), message );
}
void SDLash_Init( const char *basedir )
void SDLash_Init( void )
{
#if XASH_APPLE
#if XASH_IOS
char *path = SDL_GetBasePath();
if( path != NULL )
{
char buf[MAX_VA_STRING];
Q_snprintf( buf, sizeof( buf ), "%s%s/extras.pk3", path, basedir );
Q_snprintf( buf, sizeof( buf ), "%s%s/extras.pk3", path, host.default_gamedir );
setenv( "XASH3D_EXTRAS_PAK1", buf, true );
}
#endif

View File

@@ -28,35 +28,35 @@ static char sdl_backend_name[32];
static void SDLash_OutputCallback( void *userdata, SDL_AudioStream *stream, int additional_amount, int len )
{
const int size = dma.samples << 1;
const int size = snd.samples << 1;
int pos;
int wrapped;
(void)userdata;
(void)additional_amount;
pos = dma.samplepos << 1;
pos = snd.samplepos << 1;
if( pos >= size )
pos = dma.samplepos = 0;
pos = snd.samplepos = 0;
wrapped = pos + len - size;
if( wrapped < 0 )
{
SDL_PutAudioStreamData( stream, dma.buffer + pos, len );
dma.samplepos += len >> 1;
SDL_PutAudioStreamData( stream, snd.buffer + pos, len );
snd.samplepos += len >> 1;
}
else
{
int remaining = size - pos;
SDL_PutAudioStreamData( stream, dma.buffer + pos, remaining );
SDL_PutAudioStreamData( stream, dma.buffer, wrapped );
dma.samplepos = wrapped >> 1;
SDL_PutAudioStreamData( stream, snd.buffer + pos, remaining );
SDL_PutAudioStreamData( stream, snd.buffer, wrapped );
snd.samplepos = wrapped >> 1;
}
if( dma.samplepos >= size )
dma.samplepos = 0;
if( snd.samplepos >= size )
snd.samplepos = 0;
}
/*
@@ -122,18 +122,18 @@ qboolean SNDDMA_Init( void )
return false;
}
dma.format.speed = SOUND_DMA_SPEED;
dma.format.channels = 2;
dma.format.width = 2;
snd.format.speed = SOUND_DMA_SPEED;
snd.format.channels = 2;
snd.format.width = 2;
int samplecount = s_samplecount.value;
if( !samplecount )
samplecount = 0x8000;
dma.samples = samplecount * dma.format.channels;
dma.buffer = Mem_Calloc( sndpool, dma.samples * dma.format.width );
dma.samplepos = 0;
dma.initialized = true;
snd.samples = samplecount * snd.format.channels;
snd.buffer = Mem_Calloc( sndpool, snd.samples * snd.format.width );
snd.samplepos = 0;
snd.initialized = true;
Q_snprintf( sdl_backend_name, sizeof( sdl_backend_name ), "SDL3 (%s)", SDL_GetCurrentAudioDriver( ));
dma.backendName = sdl_backend_name;
snd.backend_name = sdl_backend_name;
Con_Printf( "Using audio driver: %s @ %d Hz\n", sdl_backend_name, SOUND_DMA_SPEED );
@@ -178,7 +178,7 @@ Reset the sound device for exiting
void SNDDMA_Shutdown( void )
{
Con_Printf( "Shutting down audio.\n" );
dma.initialized = false;
snd.initialized = false;
if( out_stream )
{
@@ -189,10 +189,10 @@ void SNDDMA_Shutdown( void )
SDL_QuitSubSystem( SDL_INIT_AUDIO );
if( dma.buffer )
if( snd.buffer )
{
Mem_Free( dma.buffer );
dma.buffer = NULL;
Mem_Free( snd.buffer );
snd.buffer = NULL;
}
}
@@ -206,7 +206,7 @@ between a deactivate and an activate.
*/
void SNDDMA_Activate( qboolean active )
{
if( !dma.initialized )
if( !snd.initialized )
return;
if( active )

View File

@@ -30,8 +30,6 @@ so it can unlock and free the data block after it has been played.
=======================================================================
*/
dma_t dma;
void S_Activate( qboolean active )
{
}
@@ -86,11 +84,12 @@ Reset the sound device for exiting
void SNDDMA_Shutdown( void )
{
Con_Printf("Shutting down audio.\n");
dma.initialized = false;
snd.initialized = false;
if (dma.buffer) {
Z_Free(dma.buffer);
dma.buffer = NULL;
if( snd.buffer )
{
Mem_Free( snd.buffer );
snd.buffer = NULL;
}
}

View File

@@ -1284,6 +1284,12 @@ static int GAME_EXPORT pfnPrecacheModel( const char *s )
qboolean optional = false;
int i;
if( COM_StringEmptyOrNULL( s ))
{
Host_Error( "%s: NULL pointer or empty string as model name\n", __func__ );
return 0;
}
if( *s == '!' )
{
optional = true;

View File

@@ -1518,7 +1518,6 @@ static qboolean SV_RecursiveLightPoint( model_t *model, mnode_t *node, const vec
float front, back, frac;
int i, side;
vec3_t mid;
mnode_t *children[2];
int numsurfaces, firstsurface;
// didn't hit anything
@@ -1529,18 +1528,16 @@ static qboolean SV_RecursiveLightPoint( model_t *model, mnode_t *node, const vec
front = PlaneDiff( start, node->plane );
back = PlaneDiff( end, node->plane );
node_children( children, node, model );
side = front < 0.0f;
if(( back < 0.0f ) == side )
return SV_RecursiveLightPoint( model, children[side], start, end, point_color );
return SV_RecursiveLightPoint( model, node_child( node, side, model ), start, end, point_color );
frac = front / ( front - back );
VectorLerp( start, frac, end, mid );
// co down front side
if( SV_RecursiveLightPoint( model, children[side], start, mid, point_color ))
if( SV_RecursiveLightPoint( model, node_child( node, side, model ), start, mid, point_color ))
return true; // hit something
if(( back < 0.0f ) == side )
@@ -1602,7 +1599,7 @@ static qboolean SV_RecursiveLightPoint( model_t *model, mnode_t *node, const vec
}
// go down back side
return SV_RecursiveLightPoint( model, children[!side], mid, end, point_color );
return SV_RecursiveLightPoint( model, node_child( node, !side, model ), mid, end, point_color );
}
/*

View File

@@ -747,6 +747,7 @@ static void FS_InitGameInfo( gameinfo_t *GameInfo, const char *gamedir, qboolean
if( quake )
{
Q_strncpy( GameInfo->basedir, "id1", sizeof( GameInfo->basedir ));
Q_strncpy( GameInfo->falldir, "qwrap", sizeof( GameInfo->falldir ));
Q_strncpy( GameInfo->title, gamedir, sizeof( GameInfo->title ));
Q_strncpy( GameInfo->startmap, "start", sizeof( GameInfo->startmap ));
Q_strncpy( GameInfo->dll_path, "bin", sizeof( GameInfo->dll_path ));
@@ -1123,27 +1124,28 @@ static qboolean FS_CheckForQuakeGameDir( const char *gamedir )
{
// if directory contain quake.rc or progs.dat it's 100% quake gamedir
// quake mods probably always archived, so check pak0.pak too
const char *files[] = { "progs.dat", "quake.rc" };
char buf[MAX_SYSPATH];
const char *files[] = { "pak0.pak", "PAK0.PAK", "progs.dat", "quake.rc" };
int i;
// try to read pak0.pak first, most quake mods are archived
if( Q_snprintf( buf, sizeof( buf ), "%s/pak0.pak", gamedir ) > 0 )
{
if( FS_SysFileExists( buf ))
{
if( FS_CheckForQuakePak( buf, files, sizeof( files ) / sizeof( files[0] )))
return true;
}
}
// search it in the filesystem
for( i = 0; i < sizeof( files ) / sizeof( files[0] ); i++ )
{
char buf[MAX_SYSPATH];
if( Q_snprintf( buf, sizeof( buf ), "%s/%s", gamedir, files[i] ) > 0 )
{
if( FS_SysFileExists( buf ))
if( !FS_SysFileExists( buf ))
continue;
if( !Q_stricmp( COM_FileExtension( buf ), "pak" ))
{
if( FS_CheckForQuakePak( buf, &files[2], sizeof( files ) / sizeof( files[0] ) - 2 ))
return true;
}
else
{
return true;
}
}
}

View File

@@ -27,9 +27,6 @@ extern fs_globals_t *FI;
#define FS_Gamedir() GI->gamefolder
#define FS_Title() GI->title
#define FS_InitStdio (*g_fsapi.InitStdio)
#define FS_ShutdownStdio (*g_fsapi.ShutdownStdio)
// search path utils
#define FS_Rescan (*g_fsapi.Rescan)
#define FS_ClearSearchPath (*g_fsapi.ClearSearchPath)

View File

@@ -337,7 +337,6 @@ Mod_CreateSkinData
static rgbdata_t *Mod_CreateSkinData( model_t *mod, const byte *data, int width, int height )
{
static rgbdata_t skin;
char name[MAX_QPATH];
int i;
skin.width = width;
@@ -363,10 +362,8 @@ static rgbdata_t *Mod_CreateSkinData( model_t *mod, const byte *data, int width,
}
}
COM_FileBase( mod->name, name, sizeof( name ));
// for alias models only player can have remap textures
if( mod != NULL && !Q_stricmp( name, "player" ))
if( mod != NULL && !Q_stricmp( mod->name, "player" ))
{
texture_t *tx = NULL;
int i, size;

View File

@@ -86,7 +86,7 @@ static qboolean Mod_LooksLikeWaterTexture( const char *name )
if(( name[0] == '*' && Q_stricmp( name, REF_DEFAULT_TEXTURE )) || name[0] == '!' )
return true;
if( !ENGINE_GET_PARM( PARM_QUAKE_COMPATIBLE ))
if( !FBitSet( gp_host->features, ENGINE_QUAKE_COMPATIBLE ))
{
if( !Q_strncmp( name, "water", 5 ) || !Q_strnicmp( name, "laser", 5 ))
return true;

View File

@@ -67,20 +67,20 @@ int R_CullSurface( const msurface_t *surf, const gl_frustum_t *frustum, uint cli
{
cl_entity_t *e = RI.currententity;
if( !e || !surf || !surf->texinfo || !surf->texinfo->texture )
return CULL_OTHER;
if( r_nocull.value )
return CULL_VISIBLE;
// world surfaces can be culled by vis frame too
if( e == CL_GetEntityByIndex( 0 ) && surf->visframe != tr.framecount )
if( surf->visframe != tr.framecount && e == CL_GetEntityByIndex( 0 ))
return CULL_VISFRAME;
// only static ents can be culled by frustum
if( !R_StaticEntity( e )) frustum = NULL;
if( unlikely( !surf->texinfo || !surf->texinfo->texture ))
return CULL_OTHER;
if( !VectorIsNull( surf->plane->normal ))
if( unlikely( r_nocull.value ))
return CULL_VISIBLE;
// only static ents can be culled by frustum
if( !R_StaticEntity( e ))
frustum = NULL;
if( glState.faceCull != GL_NONE && !VectorIsNull( surf->plane->normal ))
{
float dist;
@@ -95,35 +95,22 @@ int R_CullSurface( const msurface_t *surf, const gl_frustum_t *frustum, uint cli
}
else dist = PlaneDiff( tr.modelorg, surf->plane );
if( FBitSet( surf->flags, SURF_PLANEBACK ))
dist = -dist;
if( glState.faceCull == GL_FRONT )
{
if( FBitSet( surf->flags, SURF_PLANEBACK ))
{
if( dist >= -BACKFACE_EPSILON )
return CULL_BACKSIDE; // wrong side
}
else
{
if( dist <= BACKFACE_EPSILON )
return CULL_BACKSIDE; // wrong side
}
if( dist <= BACKFACE_EPSILON )
return CULL_BACKSIDE;
}
else if( glState.faceCull == GL_BACK )
else // if( glState.faceCull == GL_BACK )
{
if( FBitSet( surf->flags, SURF_PLANEBACK ))
{
if( dist <= BACKFACE_EPSILON )
return CULL_BACKSIDE; // wrong side
}
else
{
if( dist >= -BACKFACE_EPSILON )
return CULL_BACKSIDE; // wrong side
}
if( dist >= -BACKFACE_EPSILON )
return CULL_BACKSIDE;
}
}
if( frustum && GL_FrustumCullBox( frustum, surf->info->mins, surf->info->maxs, clipflags ))
if( frustum && clipflags && GL_FrustumCullBox( frustum, surf->info->mins, surf->info->maxs, clipflags ))
return CULL_FRUSTUM;
return CULL_VISIBLE;

View File

@@ -697,11 +697,8 @@ static void R_DecalNodeSurfaces( model_t *model, mnode_t *node, decalinfo_t *dec
//-----------------------------------------------------------------------------
static void R_DecalNode( model_t *model, mnode_t *node, decalinfo_t *decalinfo )
{
mplane_t *splitplane;
float dist;
mnode_t *children[2];
Assert( node != NULL );
mplane_t *splitplane;
float dist;
if( node->contents < 0 )
{
@@ -711,31 +708,22 @@ static void R_DecalNode( model_t *model, mnode_t *node, decalinfo_t *decalinfo )
splitplane = node->plane;
dist = DotProduct( decalinfo->m_Position, splitplane->normal ) - splitplane->dist;
node_children( children, node, model );
// This is arbitrarily set to 10 right now. In an ideal world we'd have the
// exact surface but we don't so, this tells me which planes are "sort of
// close" to the gunshot -- the gunshot is actually 4 units in front of the
// wall (see dlls\weapons.cpp). We also need to check to see if the decal
// actually intersects the texture space of the surface, as this method tags
// parallel surfaces in the same node always.
// JAY: This still tags faces that aren't correct at edges because we don't
// have a surface normal
if( dist > decalinfo->m_Size )
{
R_DecalNode( model, children[0], decalinfo );
R_DecalNode( model, node_child( node, 0, model ), decalinfo );
}
else if( dist < -decalinfo->m_Size )
{
R_DecalNode( model, children[1], decalinfo );
R_DecalNode( model, node_child( node, 1, model ), decalinfo );
}
else
{
if( dist < DECAL_DISTANCE && dist > -DECAL_DISTANCE )
R_DecalNodeSurfaces( model, node, decalinfo );
R_DecalNode( model, children[0], decalinfo );
R_DecalNode( model, children[1], decalinfo );
R_DecalNode( model, node_child( node, 0, model ), decalinfo );
R_DecalNode( model, node_child( node, 1, model ), decalinfo );
}
}

View File

@@ -99,15 +99,14 @@ void GL_FrustumInitOrtho( gl_frustum_t *out, float xLeft, float xRight, float yT
// cull methods
qboolean GL_FrustumCullBox( const gl_frustum_t *out, const vec3_t mins, const vec3_t maxs, int userClipFlags )
{
int iClipFlags;
int iClipFlags = userClipFlags != 0 ? userClipFlags : out->clipFlags;
int i, bit;
if( r_nocull.value )
if( unlikely( r_nocull.value ))
return false;
if( userClipFlags != 0 )
iClipFlags = userClipFlags;
else iClipFlags = out->clipFlags;
if( !iClipFlags )
return false;
for( i = FRUSTUM_PLANES, bit = 1; i > 0; i--, bit <<= 1 )
{
@@ -160,15 +159,14 @@ qboolean GL_FrustumCullBox( const gl_frustum_t *out, const vec3_t mins, const ve
qboolean GL_FrustumCullSphere( const gl_frustum_t *out, const vec3_t center, float radius, int userClipFlags )
{
int iClipFlags;
int iClipFlags = userClipFlags != 0 ? userClipFlags : out->clipFlags;
int i, bit;
if( r_nocull.value )
if( unlikely( r_nocull.value ))
return false;
if( userClipFlags != 0 )
iClipFlags = userClipFlags;
else iClipFlags = out->clipFlags;
if( !iClipFlags )
return false;
for( i = FRUSTUM_PLANES, bit = 1; i > 0; i--, bit <<= 1 )
{

View File

@@ -46,7 +46,12 @@ acess to array elem
*/
gl_texture_t *R_GetTexture( unsigned int texnum )
{
Assert( texnum < MAX_TEXTURES );
if( texnum >= MAX_TEXTURES )
{
gEngfuncs.Host_Error( "%s: texnum (%d) >= MAX_TEXTURES (%d)", __func__, texnum, MAX_TEXTURES );
texnum = 0;
}
return &gl_textures[texnum];
}
@@ -837,7 +842,7 @@ static byte *GL_ApplyFilter( const byte *source, int width, int height )
byte *out = (byte *)source;
int i;
if( ENGINE_GET_PARM( PARM_QUAKE_COMPATIBLE ) || glConfig.max_multisamples > 1 )
if( FBitSet( gp_host->features, ENGINE_QUAKE_COMPATIBLE ) || glConfig.max_multisamples > 1 )
return in;
for( i = 0; source && i < width * height; i++, in += 4 )

View File

@@ -249,6 +249,7 @@ typedef struct
vec3_t modelorg; // relative to viewpoint
// get from engine
model_t *worldmodel;
world_static_t *world;
cl_entity_t *entities;
movevars_t *movevars;
@@ -770,7 +771,7 @@ static inline int GL_MaxTextureUnits( void )
return Q_min( glConfig.max_texture_units, MAX_TEXTURE_UNITS );
}
#define WORLDMODEL (gp_cl->models[1])
#define WORLDMODEL (tr.worldmodel)
//
// renderer cvars

View File

@@ -105,7 +105,6 @@ void R_MarkLights( const dlight_t *light, int bit, const mnode_t *node )
const float maxdist = light->radius * light->radius;
float dist;
int i;
mnode_t *children[2];
int firstsurface, numsurfaces;
start:
@@ -115,25 +114,25 @@ start:
dist = PlaneDiff( light->origin, node->plane );
node_children( children, node, RI.currentmodel );
if( dist > virtual_radius )
{
node = children[0];
node = node_child( node, 0, RI.currentmodel );
goto start;
}
if( dist < -virtual_radius )
{
node = children[1];
node = node_child( node, 1, RI.currentmodel );
goto start;
}
const float dist_sq = dist * dist;
// mark the polygons
firstsurface = node_firstsurface( node, RI.currentmodel );
numsurfaces = node_numsurfaces( node, RI.currentmodel );
for( i = 0; i < numsurfaces; i++ )
for( i = 0; i < numsurfaces && dist_sq < maxdist; i++ )
{
vec3_t impact;
float s, t, l;
@@ -160,7 +159,7 @@ start:
t = bound( 0, t, info->lightextents[1] );
t = l - t;
if( s * s + t * t + dist * dist >= maxdist )
if( s * s + t * t + dist_sq >= maxdist )
continue;
if( surf->dlightframe != tr.dlightframecount )
@@ -171,8 +170,8 @@ start:
else surf->dlightbits |= bit;
}
R_MarkLights( light, bit, children[0] );
R_MarkLights( light, bit, children[1] );
R_MarkLights( light, bit, node_child( node, 0, RI.currentmodel ));
R_MarkLights( light, bit, node_child( node, 1, RI.currentmodel ));
}
/*
@@ -236,7 +235,6 @@ static qboolean R_RecursiveLightPoint( model_t *model, mnode_t *node, float p1f,
mtexinfo_t *tex;
matrix3x4 tbn;
vec3_t mid;
mnode_t *children[2];
int firstsurface, numsurfaces;
// didn't hit anything
@@ -246,7 +244,6 @@ static qboolean R_RecursiveLightPoint( model_t *model, mnode_t *node, float p1f,
return false;
}
node_children( children, node, model );
firstsurface = node_firstsurface( node, model );
numsurfaces = node_numsurfaces( node, model );
@@ -256,7 +253,7 @@ static qboolean R_RecursiveLightPoint( model_t *model, mnode_t *node, float p1f,
side = front < 0;
if(( back < 0 ) == side )
return R_RecursiveLightPoint( model, children[side], p1f, p2f, cv, start, end );
return R_RecursiveLightPoint( model, node_child( node, side, model ), p1f, p2f, cv, start, end );
frac = front / ( front - back );
@@ -264,7 +261,7 @@ static qboolean R_RecursiveLightPoint( model_t *model, mnode_t *node, float p1f,
midf = p1f + ( p2f - p1f ) * frac;
// co down front side
if( R_RecursiveLightPoint( model, children[side], p1f, midf, cv, start, mid ))
if( R_RecursiveLightPoint( model, node_child( node, side, model ), p1f, midf, cv, start, mid ))
return true; // hit something
if(( back < 0 ) == side )
@@ -366,7 +363,7 @@ static qboolean R_RecursiveLightPoint( model_t *model, mnode_t *node, float p1f,
}
// go down back side
return R_RecursiveLightPoint( model, children[!side], midf, p2f, cv, mid, end );
return R_RecursiveLightPoint( model, node_child( node, !side, model ), midf, p2f, cv, mid, end );
}
/*

View File

@@ -345,7 +345,7 @@ void R_SetupFrustum( void )
{
const ref_overview_t *ov = gEngfuncs.GetOverviewParms();
if( RP_NORMALPASS() && ( ENGINE_GET_PARM( PARM_WATER_LEVEL ) >= 3 ) && ENGINE_GET_PARM( PARM_QUAKE_COMPATIBLE ))
if( RP_NORMALPASS() && FBitSet( gp_host->features, ENGINE_QUAKE_COMPATIBLE ) && ( ENGINE_GET_PARM( PARM_WATER_LEVEL ) >= 3 ))
{
RI.fov_x = atan( tan( DEG2RAD( RI.fov_x ) / 2 ) * ( 0.97f + sin( gp_cl->time * 1.5f ) * 0.03f )) * 2 / (M_PI_F / 180.0f);
RI.fov_y = atan( tan( DEG2RAD( RI.fov_y ) / 2 ) * ( 1.03f - sin( gp_cl->time * 1.5f ) * 0.03f )) * 2 / (M_PI_F / 180.0f);
@@ -617,7 +617,6 @@ watertexture to grab fog values from it
static gl_texture_t *R_RecursiveFindWaterTexture( const mnode_t *node, const mnode_t *ignore, qboolean down )
{
gl_texture_t *tex = NULL;
mnode_t *children[2];
// assure the initial node is not null
// we could check it here, but we would rather check it
@@ -655,18 +654,20 @@ static gl_texture_t *R_RecursiveFindWaterTexture( const mnode_t *node, const mno
// this is a regular node
// traverse children
node_children( children, node, WORLDMODEL );
mnode_t *child = node_child( node, 0, WORLDMODEL );
if( children[0] && ( children[0] != ignore ))
if( child && ( child != ignore ))
{
tex = R_RecursiveFindWaterTexture( children[0], node, true );
tex = R_RecursiveFindWaterTexture( child, node, true );
if( tex ) return tex;
}
if( children[1] && ( children[1] != ignore ))
child = node_child( node, 1, WORLDMODEL );
if( child && ( child != ignore ))
{
tex = R_RecursiveFindWaterTexture( children[1], node, true );
if( tex ) return tex;
tex = R_RecursiveFindWaterTexture( child, node, true );
if( tex ) return tex;
}
// for down recursion, return immediately
@@ -696,7 +697,7 @@ static void R_CheckFog( void )
int i, cnt, count;
// quake global fog
if( ENGINE_GET_PARM( PARM_QUAKE_COMPATIBLE ))
if( FBitSet( gp_host->features, ENGINE_QUAKE_COMPATIBLE ))
{
if( !tr.movevars->fog_settings )
{
@@ -824,9 +825,7 @@ void R_DrawFog( void )
return;
pglEnable( GL_FOG );
if( ENGINE_GET_PARM( PARM_QUAKE_COMPATIBLE ))
pglFogi( GL_FOG_MODE, GL_EXP2 );
else pglFogi( GL_FOG_MODE, GL_EXP );
pglFogi( GL_FOG_MODE, FBitSet( gp_host->features, ENGINE_QUAKE_COMPATIBLE ) ? GL_EXP2 : GL_EXP );
pglFogf( GL_FOG_DENSITY, RI.fogDensity );
pglFogfv( GL_FOG_COLOR, RI.fogColor );
pglHint( GL_FOG_HINT, GL_NICEST );

View File

@@ -108,6 +108,8 @@ void R_NewMap( void )
texture_t *tx;
int i;
tr.worldmodel = gp_cl->models[1];
R_ClearDecals(); // clear all level decals
R_StudioResetPlayerModels();

View File

@@ -20,6 +20,7 @@ GNU General Public License for more details.
typedef struct
{
int allocated[BLOCK_SIZE_MAX];
int max_height; // maximum height currently in use in the block
int current_lightmap_texture;
msurface_t *dynamic_surfaces;
msurface_t *lightmap_surfaces[MAX_LIGHTMAPS];
@@ -653,6 +654,7 @@ static void R_SetCacheState( msurface_t *surf )
static void LM_InitBlock( void )
{
memset( gl_lms.allocated, 0, sizeof( gl_lms.allocated ));
gl_lms.max_height = 0;
}
static int LM_AllocBlock( int w, int h, int *x, int *y )
@@ -662,7 +664,7 @@ static int LM_AllocBlock( int w, int h, int *x, int *y )
best = BLOCK_SIZE;
for( i = 0; i < BLOCK_SIZE - w; i++ )
for( i = 0; i < BLOCK_SIZE - w; )
{
best2 = 0;
@@ -679,6 +681,14 @@ static int LM_AllocBlock( int w, int h, int *x, int *y )
// this is a valid spot
*x = i;
*y = best = best2;
if( best == 0 )
break; // height 0 is optimal, can't do better
i++;
}
else
{
// allocated[i+j] was too tall — no position in [i, i+j] can work
i += j + 1;
}
}
@@ -688,20 +698,15 @@ static int LM_AllocBlock( int w, int h, int *x, int *y )
for( i = 0; i < w; i++ )
gl_lms.allocated[*x + i] = best + h;
if( best + h > gl_lms.max_height )
gl_lms.max_height = best + h;
return true;
}
static void LM_UploadDynamicBlock( void )
{
int height = 0, i;
for( i = 0; i < BLOCK_SIZE; i++ )
{
if( gl_lms.allocated[i] > height )
height = gl_lms.allocated[i];
}
pglTexSubImage2D( GL_TEXTURE_2D, 0, 0, 0, BLOCK_SIZE, height, GL_RGBA, GL_UNSIGNED_BYTE, gl_lms.lightmap_buffer );
pglTexSubImage2D( GL_TEXTURE_2D, 0, 0, 0, BLOCK_SIZE, gl_lms.max_height, GL_RGBA, GL_UNSIGNED_BYTE, gl_lms.lightmap_buffer );
}
static void LM_UploadBlock( qboolean dynamic )
@@ -888,7 +893,7 @@ static void DrawGLPoly( glpoly2_t *p, float xScale, float yScale )
float flRate, flAngle, sy, cy;
gl_texture_t *texture;
if( e == CL_GetEntityByIndex( 0 ) && ENGINE_GET_PARM( PARM_QUAKE_COMPATIBLE ))
if( e == CL_GetEntityByIndex( 0 ) && FBitSet( gp_host->features, ENGINE_QUAKE_COMPATIBLE ))
{
// same as doom speed
flConveyorSpeed = -35.0f;
@@ -1290,70 +1295,59 @@ static void R_RenderDecalsForSurface( msurface_t *fa, int cull_type )
static qboolean R_CheckLightMap( msurface_t *fa )
{
qboolean is_dynamic = false;
int maps;
// check for lightmap modification
if( unlikely( !r_dynamic->value ))
return false;
if( fa->dlightframe == tr.framecount )
return true; // dlighted surfaces are always dynamic
// check for light styles
for( maps = 0; maps < MAXLIGHTMAPS && fa->styles[maps] != 255; maps++ )
{
if( tr.lightstylevalue[fa->styles[maps]] != fa->cached_light[maps] )
goto dynamic;
}
if( tr.lightstylevalue[fa->styles[maps]] == fa->cached_light[maps] )
continue;
// dynamic this frame or dynamic previously
if( fa->dlightframe == tr.framecount )
{
dynamic:
// NOTE: at this point we have only valid textures
if( r_dynamic->value )
is_dynamic = true;
}
if( is_dynamic )
{
const int style = fa->styles[maps];
if( maps < MAXLIGHTMAPS && ( style >= 32 || style == 0 || style == 20 ) && fa->dlightframe != tr.framecount )
{
byte temp[132*132*4];
mextrasurf_t *info = fa->info;
int sample_size;
int smax, tmax;
// flickering light styles can go to dynamic chain
if( !( style >= 32 || style == 0 || style == 20 ))
return true;
sample_size = gEngfuncs.Mod_SampleSizeForFace( fa );
smax = ( info->lightextents[0] / sample_size ) + 1;
tmax = ( info->lightextents[1] / sample_size ) + 1;
byte temp[132*132*4];
mextrasurf_t *info = fa->info;
int sample_size = gEngfuncs.Mod_SampleSizeForFace( fa );
int smax = ( info->lightextents[0] / sample_size ) + 1;
int tmax = ( info->lightextents[1] / sample_size ) + 1;
if( smax < 132 && tmax < 132 )
R_BuildLightMap( fa, temp, smax * 4, true );
else
{
smax = Q_min( smax, 132 );
tmax = Q_min( tmax, 132 );
memset( temp, 255, sizeof( temp ));
//Host_MapDesignError( "%s: bad surface extents: %d %d", __func__, fa->extents[0], fa->extents[1] );
}
R_SetCacheState( fa );
#if XASH_WES
GL_Bind( XASH_TEXTURE1, tr.lightmapTextures[fa->lightmaptexturenum] );
pglTexParameteri( GL_TEXTURE_2D, GL_GENERATE_MIPMAP_SGIS, GL_TRUE );
#else
GL_Bind( XASH_TEXTURE0, tr.lightmapTextures[fa->lightmaptexturenum] );
#endif
pglTexSubImage2D( GL_TEXTURE_2D, 0, fa->light_s, fa->light_t, smax, tmax, GL_RGBA, GL_UNSIGNED_BYTE, temp );
#if XASH_WES
GL_SelectTexture( XASH_TEXTURE0 );
#endif
}
if( smax < 132 && tmax < 132 )
R_BuildLightMap( fa, temp, smax * 4, true );
else
return true; // add to dynamic chain
{
smax = Q_min( smax, 132 );
tmax = Q_min( tmax, 132 );
memset( temp, 255, sizeof( temp ));
//Host_MapDesignError( "%s: bad surface extents: %d %d", __func__, fa->extents[0], fa->extents[1] );
}
R_SetCacheState( fa );
#if XASH_WES
GL_Bind( XASH_TEXTURE1, tr.lightmapTextures[fa->lightmaptexturenum] );
pglTexParameteri( GL_TEXTURE_2D, GL_GENERATE_MIPMAP_SGIS, GL_TRUE );
#else
GL_Bind( XASH_TEXTURE0, tr.lightmapTextures[fa->lightmaptexturenum] );
#endif
pglTexSubImage2D( GL_TEXTURE_2D, 0, fa->light_s, fa->light_t, smax, tmax, GL_RGBA, GL_UNSIGNED_BYTE, temp );
#if XASH_WES
GL_SelectTexture( XASH_TEXTURE0 );
#endif
return false;
}
return false; // updated
return false; // no change
}
static void R_RenderLightmapForSurface( msurface_t *fa )
@@ -1461,7 +1455,7 @@ static void R_DrawTextureChains( void )
continue; // draw translucent water later
}
if( ENGINE_GET_PARM( PARM_QUAKE_COMPATIBLE ) && FBitSet( s->flags, SURF_TRANSPARENT ))
if( FBitSet( gp_host->features, ENGINE_QUAKE_COMPATIBLE ) && FBitSet( s->flags, SURF_TRANSPARENT ))
{
R_AddToSeparatePass( &draw_alpha_surfaces, i );
continue; // draw transparent surfaces later
@@ -1642,7 +1636,7 @@ static void R_SetRenderMode( cl_entity_t *e )
case kRenderTransAlpha:
pglEnable( GL_ALPHA_TEST );
pglTexEnvf( GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE );
if( ENGINE_GET_PARM( PARM_QUAKE_COMPATIBLE ))
if( FBitSet( gp_host->features, ENGINE_QUAKE_COMPATIBLE ))
{
pglBlendFunc( GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA );
pglColor4f( 1.0f, 1.0f, 1.0f, tr.blend );
@@ -1667,7 +1661,7 @@ static void R_SetRenderMode( cl_entity_t *e )
static int R_SortBrushModelSurfaces( cl_entity_t *e, model_t *clmodel, vec3_t mins )
{
qboolean quake_compatible = ENGINE_GET_PARM( PARM_QUAKE_COMPATIBLE );
qboolean quake_compatible = FBitSet( gp_host->features, ENGINE_QUAKE_COMPATIBLE ) ? true : false;
int num_sorted = 0;
for( int i = 0; i < clmodel->nummodelsurfaces; i++ )
@@ -1766,7 +1760,7 @@ void R_DrawBrushModel( cl_entity_t *e )
VectorSubtract( RI.cullorigin, e->origin, tr.modelorg );
}
if( ENGINE_GET_PARM( PARM_QUAKE_COMPATIBLE ) && FBitSet( clmodel->flags, MODEL_TRANSPARENT ))
if( FBitSet( gp_host->features, ENGINE_QUAKE_COMPATIBLE ) && FBitSet( clmodel->flags, MODEL_TRANSPARENT ))
e->curstate.rendermode = kRenderTransAlpha;
e->visframe = tr.realframecount; // visible
@@ -2091,7 +2085,7 @@ void R_GenerateVBO( void )
if( surf->lightmaptexturenum != k )
continue;
if( surf->flags & ( SURF_DRAWSKY | SURF_DRAWTURB | SURF_CONVEYOR | SURF_DRAWTURB_QUADS ) )
if( surf->flags & ( SURF_DRAWSKY | SURF_DRAWTURB | SURF_CONVEYOR | SURF_DRAWTURB_QUADS | SURF_TRANSPARENT ) )
continue;
if( R_TextureAnimation( surf ) != world->textures[j] )
@@ -2162,7 +2156,7 @@ void R_GenerateVBO( void )
if( surf->lightmaptexturenum != k )
continue;
if( surf->flags & ( SURF_DRAWSKY | SURF_DRAWTURB | SURF_CONVEYOR | SURF_DRAWTURB_QUADS ) )
if( surf->flags & ( SURF_DRAWSKY | SURF_DRAWTURB | SURF_CONVEYOR | SURF_DRAWTURB_QUADS | SURF_TRANSPARENT ) )
continue;
if( R_TextureAnimation( surf ) != world->textures[j] )
@@ -3403,14 +3397,6 @@ R_RecursiveWorldNode
*/
static void R_RecursiveWorldNode( mnode_t *node, uint clipflags )
{
int i, clipped;
msurface_t *surf, **mark;
mleaf_t *pleaf;
int c, side;
float dot;
mnode_t *children[2];
int numsurfaces, firstsurface;
loc0:
if( node->contents == CONTENTS_SOLID )
return; // hit a solid leaf
@@ -3420,14 +3406,14 @@ loc0:
if( clipflags && !r_nocull.value )
{
for( i = 0; i < 6; i++ )
for( int i = 0; i < 6; i++ )
{
const mplane_t *p = &RI.frustum.planes[i];
if( !FBitSet( clipflags, BIT( i )))
continue;
clipped = BOX_ON_PLANE_SIDE( node->minmaxs, node->minmaxs + 3, p );
int clipped = BOX_ON_PLANE_SIDE( node->minmaxs, node->minmaxs + 3, p );
if( clipped == 2 ) return;
if( clipped == 1 ) ClearBits( clipflags, BIT( i ));
}
@@ -3436,19 +3422,11 @@ loc0:
// if a leaf node, draw stuff
if( node->contents < 0 )
{
pleaf = (mleaf_t *)node;
mleaf_t *pleaf = (mleaf_t *)node;
msurface_t **mark = pleaf->firstmarksurface;
mark = pleaf->firstmarksurface;
c = pleaf->nummarksurfaces;
if( c )
{
do
{
(*mark)->visframe = tr.framecount;
mark++;
} while( --c );
}
for( int i = 0; i < pleaf->nummarksurfaces; i++ )
mark[i]->visframe = tr.framecount;
// deal with model fragments in this leaf
if( pleaf->efrags )
@@ -3461,19 +3439,20 @@ loc0:
// node is just a decision point, so go down the apropriate sides
// find which side of the node we are on
dot = PlaneDiff( tr.modelorg, node->plane );
side = (dot >= 0.0f) ? 0 : 1;
float dot = PlaneDiff( tr.modelorg, node->plane );
int side = (dot >= 0.0f) ? 0 : 1;
// recurse down the children, front side first
node_children( children, node, WORLDMODEL );
R_RecursiveWorldNode( children[side], clipflags );
R_RecursiveWorldNode( node_child( node, side, WORLDMODEL ), clipflags );
firstsurface = node_firstsurface( node, WORLDMODEL );
numsurfaces = node_numsurfaces( node, WORLDMODEL );
int firstsurface = node_firstsurface( node, WORLDMODEL );
int numsurfaces = node_numsurfaces( node, WORLDMODEL );
// draw stuff
for( c = numsurfaces, surf = WORLDMODEL->surfaces + firstsurface; c; c--, surf++ )
for( int i = firstsurface; i < firstsurface + numsurfaces; i++ )
{
msurface_t *surf = &WORLDMODEL->surfaces[i];
if( R_CullSurface( surf, &RI.frustum, clipflags ))
continue;
@@ -3491,7 +3470,7 @@ loc0:
}
// recurse down the back side
node = children[!side];
node = node_child( node, !side, WORLDMODEL );
goto loc0;
}
@@ -3567,7 +3546,6 @@ static void R_DrawWorldTopView( mnode_t *node, uint clipflags )
do
{
mnode_t *children[2];
int numsurfaces, firstsurface;
if( node->contents == CONTENTS_SOLID )
@@ -3625,9 +3603,8 @@ static void R_DrawWorldTopView( mnode_t *node, uint clipflags )
}
// recurse down both children, we don't care the order...
node_children( children, node, WORLDMODEL );
R_DrawWorldTopView( children[0], clipflags );
node = children[1];
R_DrawWorldTopView( node_child( node, 0, WORLDMODEL ), clipflags );
node = node_child( node, 1, WORLDMODEL );
} while( node );
}
@@ -3728,15 +3705,11 @@ void R_MarkLeaves( void )
{
// merge two leafs that can be a crossed-line contents
if( RI.viewleaf->contents == CONTENTS_EMPTY )
{
VectorSet( test, RI.pvsorigin[0], RI.pvsorigin[1], RI.pvsorigin[2] - 16.0f );
leaf = gEngfuncs.Mod_PointInLeaf( test, WORLDMODEL->nodes );
}
else
{
VectorSet( test, RI.pvsorigin[0], RI.pvsorigin[1], RI.pvsorigin[2] + 16.0f );
leaf = gEngfuncs.Mod_PointInLeaf( test, WORLDMODEL->nodes );
}
leaf = gEngfuncs.Mod_PointInLeaf( test, WORLDMODEL->nodes );
if(( leaf->contents != CONTENTS_SOLID ) && ( RI.viewleaf != leaf ))
force = true;

View File

@@ -2340,15 +2340,24 @@ static void R_StudioDrawPoints( void )
pstudionorms = (vec3_t *)((byte *)m_pStudioHeader + m_pSubModel->normindex);
// backface culling for left-handed weapons
if( R_AllowFlipViewModel( RI.currententity ))
//
// there is a bug in GoldSrc that sets backface culling to GL_FRONT
// but doesn't enable OpenGL GL_CULL_FACE. This allows mod developers to disable
// backface culling through TriAPI call
//
// see https://github.com/FWGS/xash3d-fwgs/issues/2517
if( glState.faceCull != GL_NONE )
{
tr.fFlipViewModel = true;
GL_Cull( GL_NONE );
}
else
{
tr.fFlipViewModel = false;
GL_Cull( GL_FRONT );
if( R_AllowFlipViewModel( RI.currententity ))
{
tr.fFlipViewModel = true;
GL_Cull( GL_NONE );
}
else
{
tr.fFlipViewModel = false;
GL_Cull( GL_FRONT );
}
}
for( j = 0; j < m_pSubModel->nummesh; j++ )

View File

@@ -359,7 +359,7 @@ void R_DrawSolidClippedSubmodelPolygons( model_t *pmodel, mnode_t *topnode )
for( i = 0; i < numsurfaces; i++, psurf++ )
{
if( FBitSet( psurf->flags, SURF_DRAWTURB ) && !ENGINE_GET_PARM( PARM_QUAKE_COMPATIBLE ))
if( FBitSet( psurf->flags, SURF_DRAWTURB ) && !FBitSet( gp_host->features, ENGINE_QUAKE_COMPATIBLE ))
{
if( psurf->plane->type != PLANE_Z && !FBitSet( RI.currententity->curstate.effects, EF_WATERSIDES ))
continue;
@@ -441,7 +441,7 @@ void R_DrawSubmodelPolygons( model_t *pmodel, int clipflags, mnode_t *topnode )
for( i = 0; i < numsurfaces; i++, psurf++ )
{
if( FBitSet( psurf->flags, SURF_DRAWTURB ) && !ENGINE_GET_PARM( PARM_QUAKE_COMPATIBLE ))
if( FBitSet( psurf->flags, SURF_DRAWTURB ) && !FBitSet( gp_host->features, ENGINE_QUAKE_COMPATIBLE ))
{
if( psurf->plane->type != PLANE_Z && !FBitSet( RI.currententity->curstate.effects, EF_WATERSIDES ))
continue;
@@ -559,7 +559,6 @@ static void R_RecursiveWorldNode( mnode_t *node, int clipflags )
}
else
{
mnode_t *children[2];
int firstsurface;
// node is just a decision point, so go down the apropriate sides
@@ -589,8 +588,7 @@ static void R_RecursiveWorldNode( mnode_t *node, int clipflags )
side = 1;
// recurse down the children, front side first
node_children( children, node, WORLDMODEL );
R_RecursiveWorldNode( children[side], clipflags );
R_RecursiveWorldNode( node_child( node, side, WORLDMODEL ), clipflags );
// draw stuff
c = node_numsurfaces( node, WORLDMODEL );
@@ -634,7 +632,7 @@ static void R_RecursiveWorldNode( mnode_t *node, int clipflags )
}
// recurse down the back side
R_RecursiveWorldNode( children[!side], clipflags );
R_RecursiveWorldNode( node_child( node, !side, WORLDMODEL ), clipflags );
}
}

View File

@@ -705,10 +705,7 @@ static void R_DecalNodeSurfaces( model_t *model, mnode_t *node, decalinfo_t *dec
static void R_DecalNode( model_t *model, mnode_t *node, decalinfo_t *decalinfo )
{
mplane_t *splitplane;
float dist;
mnode_t *children[2];
Assert( node != NULL );
float dist;
if( node->contents < 0 )
{
@@ -718,31 +715,22 @@ static void R_DecalNode( model_t *model, mnode_t *node, decalinfo_t *decalinfo )
splitplane = node->plane;
dist = DotProduct( decalinfo->m_Position, splitplane->normal ) - splitplane->dist;
node_children( children, node, model );
// This is arbitrarily set to 10 right now. In an ideal world we'd have the
// exact surface but we don't so, this tells me which planes are "sort of
// close" to the gunshot -- the gunshot is actually 4 units in front of the
// wall (see dlls\weapons.cpp). We also need to check to see if the decal
// actually intersects the texture space of the surface, as this method tags
// parallel surfaces in the same node always.
// JAY: This still tags faces that aren't correct at edges because we don't
// have a surface normal
if( dist > decalinfo->m_Size )
{
R_DecalNode( model, children[0], decalinfo );
R_DecalNode( model, node_child( node, 0, model ), decalinfo );
}
else if( dist < -decalinfo->m_Size )
{
R_DecalNode( model, children[1], decalinfo );
R_DecalNode( model, node_child( node, 1, model ), decalinfo );
}
else
{
if( dist < DECAL_DISTANCE && dist > -DECAL_DISTANCE )
R_DecalNodeSurfaces( model, node, decalinfo );
R_DecalNode( model, children[0], decalinfo );
R_DecalNode( model, children[1], decalinfo );
R_DecalNode( model, node_child( node, 0, model ), decalinfo );
R_DecalNode( model, node_child( node, 1, model ), decalinfo );
}
}

View File

@@ -107,7 +107,6 @@ void R_MarkLights( dlight_t *light, int bit, mnode_t *node )
float dist;
msurface_t *surf;
int i;
mnode_t *children[2];
int firstsurface, numsurfaces;
if( !node || node->contents < 0 )
@@ -115,18 +114,17 @@ void R_MarkLights( dlight_t *light, int bit, mnode_t *node )
dist = PlaneDiff( light->origin, node->plane );
node_children( children, node, RI.currentmodel );
firstsurface = node_firstsurface( node, RI.currentmodel );
numsurfaces = node_numsurfaces( node, RI.currentmodel );
if( dist > light->radius )
{
R_MarkLights( light, bit, children[0] );
R_MarkLights( light, bit, node_child( node, 0, RI.currentmodel ));
return;
}
if( dist < -light->radius )
{
R_MarkLights( light, bit, children[1] );
R_MarkLights( light, bit, node_child( node, 1, RI.currentmodel ));
return;
}
@@ -146,8 +144,8 @@ void R_MarkLights( dlight_t *light, int bit, mnode_t *node )
surf->dlightbits |= bit;
}
R_MarkLights( light, bit, children[0] );
R_MarkLights( light, bit, children[1] );
R_MarkLights( light, bit, node_child( node, 0, RI.currentmodel ));
R_MarkLights( light, bit, node_child( node, 1, RI.currentmodel ));
}
/*
@@ -207,7 +205,6 @@ static qboolean R_RecursiveLightPoint( model_t *model, mnode_t *node, float p1f,
msurface_t *surf;
matrix3x4 tbn;
vec3_t mid;
mnode_t *children[2];
int firstsurface, numsurfaces;
// didn't hit anything
@@ -217,7 +214,6 @@ static qboolean R_RecursiveLightPoint( model_t *model, mnode_t *node, float p1f,
return false;
}
node_children( children, node, model );
firstsurface = node_firstsurface( node, model );
numsurfaces = node_numsurfaces( node, model );
@@ -227,7 +223,7 @@ static qboolean R_RecursiveLightPoint( model_t *model, mnode_t *node, float p1f,
side = front < 0;
if(( back < 0 ) == side )
return R_RecursiveLightPoint( model, children[side], p1f, p2f, cv, start, end );
return R_RecursiveLightPoint( model, node_child( node, side, model ), p1f, p2f, cv, start, end );
frac = front / ( front - back );
@@ -235,7 +231,7 @@ static qboolean R_RecursiveLightPoint( model_t *model, mnode_t *node, float p1f,
midf = p1f + ( p2f - p1f ) * frac;
// co down front side
if( R_RecursiveLightPoint( model, children[side], p1f, midf, cv, start, mid ))
if( R_RecursiveLightPoint( model, node_child( node, side, model ), p1f, midf, cv, start, mid ))
return true; // hit something
if(( back < 0 ) == side )
@@ -337,7 +333,7 @@ static qboolean R_RecursiveLightPoint( model_t *model, mnode_t *node, float p1f,
}
// go down back side
return R_RecursiveLightPoint( model, children[!side], midf, p2f, cv, mid, end );
return R_RecursiveLightPoint( model, node_child( node, !side, model ), midf, p2f, cv, mid, end );
}
/*

View File

@@ -12,7 +12,7 @@ else
git clone --recursive https://github.com/FWGS/hlsdk-portable -b "$1" "$MODPATH"
fi
mkdir ../../build/ios || exit 1
mkdir -p ../../build/ios || exit 1
IOSDIR=$(realpath ../../build/ios)
cd "$MODPATH" || exit 1

View File

@@ -20,7 +20,7 @@ import os
import sys
ANDROID_NDK_ENVVARS = ['ANDROID_NDK_HOME', 'ANDROID_NDK']
ANDROID_NDK_SUPPORTED = [10, 19, 20, 23, 25, 27, 28, 29]
ANDROID_NDK_SUPPORTED = [10, 19, 20, 23, 25, 27, 28, 29, 30]
ANDROID_NDK_HARDFP_MAX = 11 # latest version that supports hardfp
ANDROID_NDK_GCC_MAX = 17 # latest NDK that ships with GCC
ANDROID_NDK_UNIFIED_SYSROOT_MIN = 15
@@ -35,6 +35,7 @@ ANDROID_NDK_API_MIN = {
27: 19,
28: 21,
29: 21,
30: 21,
} # minimal API level ndk revision supports
ANDROID_STPCPY_API_MIN = 21 # stpcpy() introduced in SDK 21