Compare commits

...

82 Commits

Author SHA1 Message Date
Alibek Omarov
0cf571602c ref: common: wscript: fix psvita build 2026-04-13 12:48:55 +05:00
Alibek Omarov
a125f90d12 ref: soft: fix build 2026-04-13 10:00:15 +05:00
Alibek Omarov
313cda5478 ref: add GL_ResampleTexture to libref_common 2026-04-12 23:30:08 +05:00
Alibek Omarov
fc4e7eb18b ref: move remaining light functions to libref_common 2026-04-12 23:18:31 +05:00
Alibek Omarov
0836519b2d ref: gl: refactor R_LightVec 2026-04-12 23:06:43 +05:00
Alibek Omarov
5eeb250907 ref: common: add R_PushDlightsForBmodel 2026-04-12 22:44:55 +05:00
Alibek Omarov
2e579d0dd3 ref: move R_PushDlights and R_MarkLights to ref_common 2026-04-12 22:38:43 +05:00
Alibek Omarov
8cd40db261 ref: move RunLightstyles to ref_common 2026-04-12 22:12:50 +05:00
Alibek Omarov
9d03959196 engine: move sprite texture loading back to engine, only leave rendering for renderer 2026-04-12 17:29:45 +05:00
Alibek Omarov
acd3b0258d ref: common: move malloc and GL_InitRandomTable 2026-04-12 16:10:48 +05:00
Alibek Omarov
0b49e60c22 ref: common: draft new static library, containing shared code for ref_gl and ref_soft 2026-04-12 15:52:33 +05:00
Alibek Omarov
6719443d99 engine: draft new RefAPI 14: remove reserved functions from render api, remove CL_AddCustomBeam 2026-04-12 15:25:19 +05:00
a1batross
5f6577df2e engine: client: don't allow con_fontscale lower than 1
Prevents possible division by zero
2026-04-12 14:50:58 +05:00
Sergey Degtyar
e87f307ac1 engine: image: fix blue halo around sprays 2026-04-10 15:00:02 +05:00
Владислав Сухов
7e015a268b engine: client: in_joy: implement per-axis gyroscope deadzone support 2026-04-10 14:59:30 +05:00
TheEVolk
a679c53111 engine: client: add cvar sv_voice_singleplayer flag FCVAR_SERVER 2026-04-10 14:58:46 +05:00
TheEVolk
9e8d248044 engine: client: voice api 2026-04-10 14:58:46 +05:00
Alibek Omarov
055d646225 ref: gl: merge refactoring changes from litwater branch 2026-04-09 11:43:47 +05:00
ksagameng
a2514b6916 platform: ios: Remove FTP server as latest apple clang broke it 2026-04-09 10:25:18 +05:00
ksagameng
0ac5e78040 scripts: ios: Modify script to work with dylib loading changes 2026-04-09 10:25:18 +05:00
ksagameng
a82221e3df engine: platform: No more lib_ios, use FS_FindLibaray to load dylibs instead 2026-04-09 10:25:18 +05:00
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
132 changed files with 3112 additions and 8898 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

@@ -134,7 +134,7 @@ To build you should clone [SDL](https://github.com/libsdl-org/SDL) from `SDL2` b
0) (optional) Examine which build options are available: `./waf --help`.
1) Configure build: `./waf configure --ios --enable-bundled-deps --sdl2 (path/to/SDL2.framework)`, set `--ios-simulator` instead of `--ios` if you want to build for simulator.
2) Compile `./waf build`.
3) Navigate to `build` and copy your compiled SDL2.framework there, then add your client dylibs to `build/ios/cl_dlls` and any other dylibs to `build/ios/dlls` (You can also run `scripts/ios/buildhlsdk.sh` instead to automatically create an ipa with hlsdk dylibs)
3) Navigate to `build` and copy your compiled SDL2.framework there, then add your game dylibs to `build/ios/libs/(gamedir)/(dlls/cl_dlls)`(You can also run `scripts/ios/buildhlsdk.sh` instead to automatically create an ipa with hlsdk dylibs)
4) Run `scripts/ios/createipa.sh` to create an installable ipa
### Running tests

View File

@@ -116,6 +116,8 @@ typedef enum
IMAGE_MULTILAYER = BIT(8), // to differentiate from 3D texture
IMAGE_ONEBIT_ALPHA = BIT(9), // binary alpha
IMAGE_QUAKEPAL = BIT(10), // image has quake1 palette
IMAGE_PREMULTIPLIED = BIT(11),
IMAGE_PLAYERDECAL = BIT(12), // loaded with IL_LOAD_PLAYER_DECAL (custom player spray)
// Image_Process manipulation flags
IMAGE_FLIP_X = BIT(16), // flip the image by width

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
}

View File

@@ -101,7 +101,7 @@ typedef enum
TF_TEXTURE_3D = (1<<20), // this is GL_TEXTURE_3D
TF_ATLAS_PAGE = (1<<21), // bit who indicate lightmap page or deluxemap page
TF_ALPHACONTRAST = (1<<22), // special texture mode for A2C
// reserved
TF_PREMULTIPLIED = (1<<23), // RGBA was stored premultiplied (HL gradient / indexalpha)
// reserved
TF_IMG_UPLOADED = (1<<25), // this is set for first time when called glTexImage, otherwise it will be call glTexSubImage
TF_ARB_FLOAT = (1<<26), // float textures

196
common/sound_api.h Normal file
View File

@@ -0,0 +1,196 @@
/*
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;
typedef struct voice_audio_info_s
{
uint width;
uint samplerate;
uint frame_size; // in samples
} voice_audio_info_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 );
// Voice extensions
void (*pfnS_RawEntSamples)( int entnum, uint samples, uint rate, word width, word channels, const byte *data, int snd_vol, float attn );
void (*pfnSND_ForceInitMouth)( int entnum );
voice_audio_info_t (*pfnVoice_GetAudioInfo)( void );
} 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,9 @@ 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 );
int (*pfnVoice_StartChannel)( int samples, byte *data, int entnum );
} 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

@@ -981,15 +981,8 @@ qboolean CL_AddVisibleEntity( cl_entity_t *ent, int entityType )
if( !draw_player )
return false;
if( entityType == ET_BEAM )
{
ref.dllFuncs.CL_AddCustomBeam( ent );
return true;
}
else if( !ref.dllFuncs.R_AddEntity( ent, entityType ))
{
if( !ref.dllFuncs.R_AddEntity( ent, entityType ))
return false;
}
// because pTemp->entity.curstate.effects
// is already occupied by FTENT_FLICKER
@@ -1381,7 +1374,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,8 @@ 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 FWGS ext
{ "Voice_StartChannel", (void **)&clgame.dllFuncs.pfnVoice_StartChannel }, // Xash3D FWGS ext
};
static void pfnSPR_DrawHoles( int frame, int x, int y, const wrect_t *prc );
@@ -179,7 +190,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] ),
@@ -357,13 +374,13 @@ static void SPR_DrawGeneric( int frame, float x, float y, float width, float hei
int w, h;
// assume we get sizes from image
ref.dllFuncs.R_GetSpriteParms( &w, &h, NULL, frame, clgame.ds.pSprite );
R_GetSpriteParms( &w, &h, NULL, frame, clgame.ds.pSprite );
width = w;
height = h;
}
texnum = ref.dllFuncs.R_GetSpriteTexture( clgame.ds.pSprite, frame );
texnum = R_GetSpriteTexture( clgame.ds.pSprite, frame );
if( prc )
{
@@ -560,31 +577,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 +657,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
@@ -1468,7 +1424,7 @@ int EXPORT pfnSPR_Frames( HSPRITE hPic )
{
int numFrames = 0;
ref.dllFuncs.R_GetSpriteParms( NULL, NULL, &numFrames, 0, CL_GetSpritePointer( hPic ));
R_GetSpriteParms( NULL, NULL, &numFrames, 0, CL_GetSpritePointer( hPic ));
return numFrames;
}
@@ -1483,7 +1439,7 @@ static int GAME_EXPORT pfnSPR_Height( HSPRITE hPic, int frame )
{
int sprHeight = 0;
ref.dllFuncs.R_GetSpriteParms( NULL, &sprHeight, NULL, frame, CL_GetSpritePointer( hPic ));
R_GetSpriteParms( NULL, &sprHeight, NULL, frame, CL_GetSpritePointer( hPic ));
return sprHeight;
}
@@ -1498,7 +1454,7 @@ static int GAME_EXPORT pfnSPR_Width( HSPRITE hPic, int frame )
{
int sprWidth = 0;
ref.dllFuncs.R_GetSpriteParms( &sprWidth, NULL, NULL, frame, CL_GetSpritePointer( hPic ));
R_GetSpriteParms( &sprWidth, NULL, NULL, frame, CL_GetSpritePointer( hPic ));
return sprWidth;
}
@@ -1974,13 +1930,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;
}
@@ -3369,7 +3324,7 @@ int TriSpriteTexture( model_t *pSpriteModel, int frame )
{
int gl_texturenum;
if(( gl_texturenum = ref.dllFuncs.R_GetSpriteTexture( pSpriteModel, frame )) <= 0 )
if(( gl_texturenum = R_GetSpriteTexture( pSpriteModel, frame )) <= 0 )
return 0;
ref.dllFuncs.GL_Bind( XASH_TEXTURE0, gl_texturenum );
@@ -4095,14 +4050,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

@@ -930,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;
@@ -947,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;
@@ -974,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 ));
@@ -1029,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
@@ -1041,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
@@ -1059,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 )

360
engine/client/cl_sprite.c Normal file
View File

@@ -0,0 +1,360 @@
/*
cl_sprite.c - sprite loading
Copyright (C) 2010 Uncle Mike
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
*/
#include "common.h"
#include "client.h"
#include "sprite.h"
#include "ref_common.h"
static char sprite_name[MAX_QPATH];
static char group_suffix[8];
static uint r_texFlags = 0;
static int sprite_version;
/*
================
R_GetSpriteFrame
assume pModel is valid
================
*/
mspriteframe_t *R_GetSpriteFrame( const model_t *pModel, int frame, float yaw )
{
msprite_t *psprite;
mspritegroup_t *pspritegroup;
mspriteframe_t *pspriteframe = NULL;
float *pintervals, fullinterval;
int i, numframes;
float targettime;
if( !pModel )
return NULL;
psprite = pModel->cache.data;
if( frame < 0 )
{
frame = 0;
}
else if( frame >= psprite->numframes )
{
if( frame > psprite->numframes )
Con_Printf( S_WARN "%s: no such frame %d (%s)\n", __func__, frame, pModel->name );
frame = psprite->numframes - 1;
}
if( psprite->frames[frame].type == SPR_SINGLE )
{
pspriteframe = psprite->frames[frame].frameptr;
}
else if( psprite->frames[frame].type == SPR_GROUP )
{
pspritegroup = (mspritegroup_t *)psprite->frames[frame].frameptr;
pintervals = pspritegroup->intervals;
numframes = pspritegroup->numframes;
fullinterval = pintervals[numframes - 1];
// when loading in Mod_LoadSpriteGroup, we guaranteed all interval values
// are positive, so we don't have to worry about division by zero
targettime = cl.time - ((int)( cl.time / fullinterval )) * fullinterval;
for( i = 0; i < ( numframes - 1 ); i++ )
{
if( pintervals[i] > targettime )
break;
}
pspriteframe = pspritegroup->frames[i];
}
else if( psprite->frames[frame].type == FRAME_ANGLED )
{
int angleframe = (int)( Q_rint(( refState.viewangles[1] - yaw + 45.0f ) / 360 * 8 ) - 4 ) & 7;
// e.g. doom-style sprite monsters
pspritegroup = (mspritegroup_t *)psprite->frames[frame].frameptr;
pspriteframe = pspritegroup->frames[angleframe];
}
return pspriteframe;
}
void R_GetSpriteParms( int *frameWidth, int *frameHeight, int *numFrames, int currentFrame, const model_t *pSprite )
{
mspriteframe_t *pFrame;
if( !pSprite || pSprite->type != mod_sprite )
return;
pFrame = R_GetSpriteFrame( pSprite, currentFrame, 0.0f );
if( frameWidth )
*frameWidth = pFrame->width;
if( frameHeight )
*frameHeight = pFrame->height;
if( numFrames )
*numFrames = pSprite->numframes;
}
int R_GetSpriteTexture( const model_t *m_pSpriteModel, int frame )
{
if( !m_pSpriteModel || m_pSpriteModel->type != mod_sprite || !m_pSpriteModel->cache.data )
return 0;
return R_GetSpriteFrame( m_pSpriteModel, frame, 0.0f )->gl_texturenum;
}
static void Mod_SpriteTextureReplacementReport( const char *modelname, int gl_texturenum, const char *foundpath )
{
if( host_allow_materials.value != 2.0f )
return;
if( gl_texturenum > 0 )
Con_Printf( "Looking for %s tex replacement..." S_GREEN "OK (%s)\n", modelname, foundpath );
else if( gl_texturenum < 0 )
Con_Printf( "Looking for %s tex replacement..." S_YELLOW "MISS (%s)\n", modelname, foundpath );
else
Con_Printf( "Looking for %s tex replacement..." S_RED "FAIL (%s)\n", modelname, foundpath );
}
static qboolean Mod_SpriteSearchForTextureReplacement( char *out, size_t size, const char *modelname, const char *fmt, ... )
{
va_list ap;
int ret;
va_start( ap, fmt );
ret = Q_vsnprintf( out, size, fmt, ap );
va_end( ap );
if( ret < 0 )
{
Mod_SpriteTextureReplacementReport( modelname, -1, "overflow" );
return false;
}
if( FS_FileExists( out, false ))
return true;
Mod_SpriteTextureReplacementReport( modelname, -1, out );
return false;
}
static const byte *Mod_SpriteLoadFrame( model_t *mod, const void *pin, mspriteframe_t **ppframe, int num )
{
dspriteframe_t pinframe;
mspriteframe_t *pspriteframe;
int gl_texturenum = 0;
char texname[128];
int bytes = 1;
memcpy( &pinframe, pin, sizeof( dspriteframe_t ));
if( sprite_version == SPRITE_VERSION_32 )
bytes = 4;
// build unique frame name
if( FBitSet( mod->flags, MODEL_CLIENT )) // it's a HUD sprite
{
Q_snprintf( texname, sizeof( texname ), "#HUD/%s(%s:%i%i).spr", sprite_name, group_suffix, num / 10, num % 10 );
gl_texturenum = ref.dllFuncs.GL_LoadTexture( texname, pin, pinframe.width * pinframe.height * bytes, r_texFlags );
}
else
{
// partial HD-textures support
if( Mod_AllowMaterials( ))
{
if( Mod_SpriteSearchForTextureReplacement( texname, sizeof( texname ), sprite_name, "materials/%s/%s%i%i.tga", sprite_name, group_suffix, num / 10, num % 10 ))
{
gl_texturenum = ref.dllFuncs.GL_LoadTexture( texname, NULL, 0, r_texFlags );
Mod_SpriteTextureReplacementReport( sprite_name, gl_texturenum, texname );
}
}
if( gl_texturenum == 0 )
{
Q_snprintf( texname, sizeof( texname ), "#%s(%s:%i%i).spr", sprite_name, group_suffix, num / 10, num % 10 );
gl_texturenum = ref.dllFuncs.GL_LoadTexture( texname, pin, pinframe.width * pinframe.height * bytes, r_texFlags );
}
}
// setup frame description
pspriteframe = Mem_Malloc( mod->mempool, sizeof( mspriteframe_t ));
pspriteframe->width = pinframe.width;
pspriteframe->height = pinframe.height;
pspriteframe->up = pinframe.origin[1];
pspriteframe->left = pinframe.origin[0];
pspriteframe->down = pinframe.origin[1] - pinframe.height;
pspriteframe->right = pinframe.width + pinframe.origin[0];
pspriteframe->gl_texturenum = gl_texturenum;
*ppframe = pspriteframe;
return (const byte *)pin + sizeof( dspriteframe_t ) + pinframe.width * pinframe.height * bytes;
}
static const byte *Mod_SpriteLoadGroup( model_t *mod, const void *pin, mspriteframe_t **ppframe, int framenum )
{
const dspritegroup_t *pingroup;
mspritegroup_t *pspritegroup;
const dspriteinterval_t *pin_intervals;
float *poutintervals;
int i, groupsize, numframes;
const void *ptemp;
pingroup = (const dspritegroup_t *)pin;
numframes = pingroup->numframes;
groupsize = sizeof( mspritegroup_t ) + ( numframes - 1 ) * sizeof( pspritegroup->frames[0] );
pspritegroup = Mem_Calloc( mod->mempool, groupsize );
pspritegroup->numframes = numframes;
*ppframe = (mspriteframe_t *)pspritegroup;
pin_intervals = (const dspriteinterval_t *)( pingroup + 1 );
poutintervals = Mem_Calloc( mod->mempool, numframes * sizeof( float ));
pspritegroup->intervals = poutintervals;
for( i = 0; i < numframes; i++ )
{
*poutintervals = pin_intervals->interval;
if( *poutintervals <= 0.0f )
*poutintervals = 1.0f; // set error value
poutintervals++;
pin_intervals++;
}
ptemp = (const void *)pin_intervals;
for( i = 0; i < numframes; i++ )
{
ptemp = Mod_SpriteLoadFrame( mod, ptemp, &pspritegroup->frames[i], framenum * 10 + i );
}
return ptemp;
}
void Mod_SpriteLoadTextures( model_t *mod, const void *buffer )
{
const dsprite_t *pin = buffer;
const short *numi = NULL;
const byte *pframetype;
msprite_t *psprite = mod->cache.data;
int i;
if( pin->version == SPRITE_VERSION_Q1 || pin->version == SPRITE_VERSION_32 )
numi = NULL;
else if( pin->version == SPRITE_VERSION_HL )
numi = (const short *)((const byte *)buffer + sizeof( dsprite_hl_t ));
r_texFlags = mod->numtexinfo;
sprite_version = pin->version;
Q_strncpy( sprite_name, mod->name, sizeof( sprite_name ));
COM_StripExtension( sprite_name );
if( numi == NULL )
{
rgbdata_t *pal;
pal = FS_LoadImage( "#id.pal", (byte *)&i, 768 );
pframetype = (const byte *)buffer + sizeof( dsprite_q1_t );
FS_FreeImage( pal );
}
else if( *numi <= 256 )
{
const byte *src = (const byte *)( numi + 1 );
rgbdata_t *pal;
size_t pal_bytes = *numi * 3;
// install palette
switch( psprite->texFormat )
{
case SPR_INDEXALPHA:
pal = FS_LoadImage( "#gradient.pal", src, pal_bytes );
break;
case SPR_ALPHTEST:
pal = FS_LoadImage( "#masked.pal", src, pal_bytes );
break;
default:
pal = FS_LoadImage( "#texgamma.pal", src, pal_bytes );
break;
}
pframetype = (const byte *)( src + pal_bytes );
FS_FreeImage( pal );
}
else
{
Con_DPrintf( S_ERROR "%s has wrong number of palette colors %i (should be less or equal than 256)\n", mod->name, *numi );
return;
}
if( mod->numframes < 1 )
return;
for( i = 0; i < mod->numframes; i++ )
{
frametype_t frametype;
dframetype_t dframetype;
memcpy( &dframetype, pframetype, sizeof( dframetype ));
frametype = dframetype.type;
psprite->frames[i].type = (spriteframetype_t)frametype;
switch( frametype )
{
case FRAME_SINGLE:
Q_strncpy( group_suffix, "frame", sizeof( group_suffix ));
pframetype = Mod_SpriteLoadFrame( mod, pframetype + sizeof( dframetype_t ), &psprite->frames[i].frameptr, i );
break;
case FRAME_GROUP:
Q_strncpy( group_suffix, "group", sizeof( group_suffix ));
pframetype = Mod_SpriteLoadGroup( mod, pframetype + sizeof( dframetype_t ), &psprite->frames[i].frameptr, i );
break;
case FRAME_ANGLED:
Q_strncpy( group_suffix, "angle", sizeof( group_suffix ));
pframetype = Mod_SpriteLoadGroup( mod, pframetype + sizeof( dframetype_t ), &psprite->frames[i].frameptr, i );
break;
}
if( pframetype == NULL )
break;
}
}
void Mod_SpriteUnloadTextures( void *data )
{
msprite_t *psprite = data;
int i;
if( !data )
return;
// release all textures
for( i = 0; i < psprite->numframes; i++ )
{
if( !psprite->frames[i].frameptr )
continue;
if( psprite->frames[i].type == SPR_SINGLE )
{
ref.dllFuncs.GL_FreeTexture( psprite->frames[i].frameptr->gl_texturenum );
}
else
{
mspritegroup_t *pspritegroup = (mspritegroup_t *)psprite->frames[i].frameptr;
int j;
for( j = 0; j < pspritegroup->numframes; j++ )
{
if( pspritegroup->frames[j] )
ref.dllFuncs.GL_FreeTexture( pspritegroup->frames[j]->gl_texturenum );
}
}
}
}

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 );
@@ -989,6 +972,13 @@ void SCR_DrawPos( void );
void SCR_DrawEnts( void );
void SCR_DrawUserCmd( void );
//
// cl_sprite.c
//
mspriteframe_t *R_GetSpriteFrame( const model_t *pModel, int frame, float yaw );
void R_GetSpriteParms( int *frameWidth, int *frameHeight, int *numFrames, int currentFrame, const model_t *pSprite );
int R_GetSpriteTexture( const model_t *m_pSpriteModel, int frame );
//
// cl_netgraph.c
//
@@ -1083,6 +1073,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();
@@ -2164,8 +2164,11 @@ void Con_RunConsole( void )
con.vislines = con.showlines;
}
if( FBitSet( con_charset.flags|con_fontscale.flags|con_fontnum.flags|cl_charset.flags|con_oldfont.flags, FCVAR_CHANGED ))
if( FBitSet( con_charset.flags|con_fontscale.flags|con_fontnum.flags|cl_charset.flags|con_oldfont.flags, FCVAR_CHANGED ))
{
if( con_fontscale.value < 1.0f )
Cvar_DirectSet( &con_fontscale, "1" );
// update codepage parameters
if( !Q_stricmp( con_charset.string, "cp1251" ))
{

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,18 @@ 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_gyro_pitch_deadzone, "0.5", FCVAR_ARCHIVE | FCVAR_FILTERABLE, "gyroscope pitch axis deadzone (deg/s)" );
static CVAR_DEFINE_AUTO( joy_gyro_yaw_deadzone, "0.5", FCVAR_ARCHIVE | FCVAR_FILTERABLE, "gyroscope yaw axis deadzone (deg/s)" );
static CVAR_DEFINE_AUTO( joy_gyro_roll_deadzone, "0.5", FCVAR_ARCHIVE | FCVAR_FILTERABLE, "gyroscope roll axis deadzone (deg/s)" );
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 +240,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 +271,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 +297,8 @@ Gyroscope events
*/
void Joy_GyroEvent( vec3_t data )
{
VectorCopy( data, joy_gyro_speed );
VectorCopy( data, joy_gyro_speed_display );
}
/*
@@ -290,7 +310,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 +328,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 +337,218 @@ 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 );
if( fabs( pitch_speed ) < joy_gyro_pitch_deadzone.value )
pitch_speed = 0.0f;
if( fabs( yaw_speed ) < joy_gyro_yaw_deadzone.value )
yaw_speed = 0.0f;
if( fabs( roll_speed ) < joy_gyro_roll_deadzone.value )
roll_speed = 0.0f;
*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" };
static const convar_t *const gyro_deadzone[3] =
{
&joy_gyro_pitch_deadzone,
&joy_gyro_yaw_deadzone,
&joy_gyro_roll_deadzone,
};
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] );
// deadzone in deg/s; display scale is +-5 rad/s
float fthreshold = gyro_deadzone[i]->value * ( M_PI / 180.0f ) / 5.0f;
fthreshold = bound( 0.0f, fthreshold, 1.0f );
if( fthreshold > 0.0f )
{
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 );
// gyro doesn't have 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 +597,16 @@ 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_gyro_pitch_deadzone );
Cvar_RegisterVariable( &joy_gyro_yaw_deadzone );
Cvar_RegisterVariable( &joy_gyro_roll_deadzone );
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

@@ -465,6 +465,7 @@ static const ref_api_t gEngfuncs =
&g_fsapi,
R_GetWindowHandle,
R_GetSpriteFrame,
};
static void R_UnloadProgs( 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;
@@ -285,7 +285,7 @@ static int S_MixNormalChannels( portable_samplepair_t *dst, int end, int rate )
// if it's also not looping, free it
if( ch->leftvol < 8 && ch->rightvol < 8 )
{
if( !FBitSet( sc->flags, SOUND_LOOPED ) || !ch->use_loop )
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;
@@ -308,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 ));
}
}
@@ -318,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 );
}
}
@@ -375,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 ));
@@ -383,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 ));
@@ -402,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;
@@ -434,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;
@@ -443,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 );
@@ -486,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 ();
@@ -496,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;
@@ -526,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 );
@@ -550,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 );
@@ -253,7 +253,7 @@ void S_StreamBackgroundTrack( void )
{
// add to raw buffer
int music_vol = (int)(255.0f * S_GetMusicVolume());
S_RawEntSamples( S_RAW_SOUND_BACKGROUNDTRACK, fileSamples, info->rate, info->width, info->channels, raw, music_vol );
S_RawEntSamples( S_RAW_SOUND_BACKGROUNDTRACK, fileSamples, info->rate, info->width, info->channels, raw, music_vol, ATTN_NONE );
}
else
{
@@ -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,110 +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;
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
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
vec3_t origin; // only use if fixed_origin is set
float dist_mult; // distance multiplier (attenuation/clipK)
int entnum; // entity soundsource
int entchannel; // sound channel (CHAN_STREAM, CHAN_VOICE, etc.)
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 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;
// 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[CVOXWORDMAX];
} 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)
@@ -150,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;
@@ -222,7 +111,7 @@ int S_GetCurrentDynamicSounds( soundlist_t *pout, int size );
sfx_t *S_GetSfxByHandle( sound_t handle );
rawchan_t *S_FindRawChannel( int entnum, qboolean create );
uint S_RawSamplesStereo( portable_samplepair_t *rawsamples, uint rawend, uint max_samples, uint samples, uint rate, word width, word channels, const byte *data );
void S_RawEntSamples( int entnum, uint samples, uint rate, word width, word channels, const byte *data, int snd_vol );
void S_RawEntSamples( int entnum, uint samples, uint rate, word width, word channels, const byte *data, int snd_vol, float attn );
void S_StopSound( int entnum, int channel, const char *soundname );
void S_UpdateFrame( struct ref_viewpass_s *rvp );
void S_StopAllSounds( qboolean ambient );

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

@@ -939,8 +939,14 @@ Feed the decoded data to engine sound subsystem
*/
static void Voice_StartChannel( uint samples, byte *data, int entnum )
{
if( clgame.dllFuncs.pfnVoice_StartChannel != NULL )
{
if( clgame.dllFuncs.pfnVoice_StartChannel( samples, data, entnum ))
return;
}
SND_ForceInitMouth( entnum );
S_RawEntSamples( entnum, samples, voice.samplerate, voice.width, VOICE_PCM_CHANNELS, data, bound( 0, 255 * voice_scale.value, 255 ));
S_RawEntSamples( entnum, samples, voice.samplerate, voice.width, VOICE_PCM_CHANNELS, data, bound( 0, 255 * voice_scale.value, 255 ), ATTN_NONE );
Voice_Status( entnum, true );
}
@@ -1194,3 +1200,13 @@ qboolean Voice_Init( const char *pszCodecName, int quality, qboolean preinit )
voice.initialized = true;
return true;
}
voice_audio_info_t Voice_GetAudioInfo( void )
{
voice_audio_info_t info;
info.width = voice.width;
info.samplerate = voice.samplerate;
info.frame_size = voice.frame_size;
return info;
}

View File

@@ -131,5 +131,6 @@ void Voice_Disconnect( void );
void Voice_AddIncomingData( int ent, const byte *data, uint size, uint frames );
void Voice_StopChannel( int entnum );
void Voice_LoopbackAck( void ); // sends VOICE_LOOPBACK_INDEX to client, gets disabled on timeout
voice_audio_info_t Voice_GetAudioInfo( void );
#endif // VOICE_H

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

@@ -37,8 +37,6 @@ GNU General Public License for more details.
#include "com_model.h"
#include "com_strings.h"
#include "crtlib.h"
#define FSCALLBACK_OVERRIDE_MALLOC_LIKE
#include "fscallback.h"
#include "cvar.h"
#include "con_nprint.h"
#include "crclib.h"
@@ -383,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__ )

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

@@ -320,6 +320,11 @@ static qboolean FS_DetermineReadOnlyRootDirectory( char *out, size_t size )
return true;
}
#if XASH_IOS
Q_strncpy( out, IOS_GetExecDir(), size );
return true;
#endif
return false;
}

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;
@@ -861,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
@@ -998,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

@@ -173,6 +173,9 @@ static MALLOC_LIKE( FS_FreeImage, 1 ) rgbdata_t *ImagePack( const char *name )
Image_ReportLookupsCount( name );
if( Image_CheckFlag( IL_LOAD_PLAYER_DECAL ))
SetBits( image.flags, IMAGE_PLAYERDECAL );
// clear any force flags
image.force_flags = 0;

View File

@@ -250,6 +250,8 @@ qboolean Image_LoadSPR( const char *name, const byte *buffer, fs_offset_t filesi
SetBits( image.flags, IMAGE_ONEBIT_ALPHA );
// intentionally fallthrough
case LUMP_GRADIENT:
SetBits( image.flags, IMAGE_HAS_ALPHA );
break;
case LUMP_QUAKE1:
SetBits( image.flags, IMAGE_HAS_ALPHA );
break;

View File

@@ -2014,7 +2014,7 @@ static void Mod_SetupSubmodels( model_t *mod, dbspmodel_t *bmod )
SetBits( mod->flags, MODEL_HAS_ORIGIN );
#ifdef HACKS_RELATED_HLMODS
// c2a1 doesn't have origin brush it's just placed at center of the level
if( !Q_stricmp( name, "maps/c2a1.bsp" ) && ( i == 11 ))
if( i == 11 && !Q_stricmp( name, "maps/c2a1.bsp" ))
SetBits( mod->flags, MODEL_HAS_ORIGIN );
#endif
}
@@ -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

@@ -197,6 +197,8 @@ void Mod_ClearStudioCache( void );
// mod_sprite.c
//
void Mod_LoadSpriteModel( model_t *mod, const void *buffer, size_t buffersize, qboolean *loaded );
#endif
void Mod_SpriteLoadTextures( model_t *mod, const void *buffer );
void Mod_SpriteUnloadTextures( void *data );
#endif
#endif//MOD_LOCAL_H

View File

@@ -17,9 +17,6 @@ GNU General Public License for more details.
#include "common.h"
#include "sprite.h"
#include "studio.h"
#if !XASH_DEDICATED
#include "ref_common.h"
#endif // XASH_DEDICATED
#include "mod_local.h"
@@ -147,4 +144,8 @@ void Mod_LoadSpriteModel( model_t *mod, const void *buffer, size_t buffersize, q
psprite->numframes = 0;
return;
}
#if !XASH_DEDICATED
Mod_SpriteLoadTextures( mod, buffer );
#endif
}

View File

@@ -88,6 +88,22 @@ static void Mod_Modellist_f( void )
Con_Printf( "\n" );
}
static void Mod_UnloadRenderData( model_t *mod )
{
#if !XASH_DEDICATED
switch( mod->type )
{
case mod_sprite:
Mod_SpriteUnloadTextures( mod->cache.data );
break;
default:
break;
}
ref.dllFuncs.Mod_ProcessRenderData( mod, false, NULL, 0 );
#endif
}
/*
================
Mod_FreeUserData
@@ -107,12 +123,10 @@ static void Mod_FreeUserData( model_t *mod )
svgame.physFuncs.Mod_ProcessUserData( mod, false, NULL );
}
}
#if !XASH_DEDICATED
else
{
ref.dllFuncs.Mod_ProcessRenderData( mod, false, NULL, 0 );
Mod_UnloadRenderData( mod );
}
#endif
}
/*

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

@@ -168,6 +168,8 @@ typedef struct physics_interface_s
void *(*SV_HullForBsp)( edict_t *ent, const float *mins, const float *maxs, float *offset );
// handle player custom think function
int (*SV_PlayerThink)( edict_t *ent, float frametime, double time );
// voice data
qboolean (*pfnVoiceData)( int client, uint frames, uint size, qboolean loopback, const char *received );
} physics_interface_t;
#endif//PHYSINT_H

View File

@@ -1,282 +0,0 @@
//
// AsyncSocket.h
//
// This class is in the public domain.
// Originally created by Dustin Voss on Wed Jan 29 2003.
// Updated and maintained by Deusty Designs and the Mac development community.
//
// http://code.google.com/p/cocoaasyncsocket/
//
#import <Foundation/Foundation.h>
@class AsyncSocket;
@class AsyncReadPacket;
@class AsyncWritePacket;
extern NSString *const AsyncSocketException;
extern NSString *const AsyncSocketErrorDomain;
enum AsyncSocketError
{
AsyncSocketCFSocketError = kCFSocketError, // From CFSocketError enum.
AsyncSocketNoError = 0, // Never used.
AsyncSocketCanceledError, // onSocketWillConnect: returned NO.
AsyncSocketReadMaxedOutError, // Reached set maxLength without completing
AsyncSocketReadTimeoutError,
AsyncSocketWriteTimeoutError
};
typedef enum AsyncSocketError AsyncSocketError;
@interface NSObject (AsyncSocketDelegate)
/**
* In the event of an error, the socket is closed.
* You may call "unreadData" during this call-back to get the last bit of data off the socket.
* When connecting, this delegate method may be called
* before"onSocket:didAcceptNewSocket:" or "onSocket:didConnectToHost:".
**/
- (void)onSocket:(AsyncSocket *)sock willDisconnectWithError:(NSError *)err;
/**
* Called when a socket disconnects with or without error. If you want to release a socket after it disconnects,
* do so here. It is not safe to do that during "onSocket:willDisconnectWithError:".
**/
- (void)onSocketDidDisconnect:(AsyncSocket *)sock;
/**
* Called when a socket accepts a connection. Another socket is spawned to handle it. The new socket will have
* the same delegate and will call "onSocket:didConnectToHost:port:".
**/
- (void)onSocket:(AsyncSocket *)sock didAcceptNewSocket:(AsyncSocket *)newSocket;
/**
* Called when a new socket is spawned to handle a connection. This method should return the run-loop of the
* thread on which the new socket and its delegate should operate. If omitted, [NSRunLoop currentRunLoop] is used.
**/
- (NSRunLoop *)onSocket:(AsyncSocket *)sock wantsRunLoopForNewSocket:(AsyncSocket *)newSocket;
/**
* Called when a socket is about to connect. This method should return YES to continue, or NO to abort.
* If aborted, will result in AsyncSocketCanceledError.
*
* If the connectToHost:onPort:error: method was called, the delegate will be able to access and configure the
* CFReadStream and CFWriteStream as desired prior to connection.
*
* If the connectToAddress:error: method was called, the delegate will be able to access and configure the
* CFSocket and CFSocketNativeHandle (BSD socket) as desired prior to connection. You will be able to access and
* configure the CFReadStream and CFWriteStream in the onSocket:didConnectToHost:port: method.
**/
- (BOOL)onSocketWillConnect:(AsyncSocket *)sock;
/**
* Called when a read stream is closed. This method should return YES, if you
* want to close the socket of the stream, or NO then the socket won't be closed.
* This was added for the workaround to solve the issue of firefox and os-x finder,
* which seem to close a read stream for ftp's list command.
* 'iosftpserver' returns YES only when the socket is for FTP::STORE command.
**/
- (BOOL)onReadStreamEnded:(AsyncSocket *)sock;
/**
* Called when a socket connects and is ready for reading and writing.
* The host parameter will be an IP address, not a DNS name.
**/
- (void)onSocket:(AsyncSocket *)sock didConnectToHost:(NSString *)host port:(UInt16)port;
/**
* Called when a socket has completed reading the requested data into memory.
* Not called if there is an error.
**/
- (void)onSocket:(AsyncSocket *)sock didReadData:(NSData *)data withTag:(long)tag;
/**
* Called when a socket has read in data, but has not yet completed the read.
* This would occur if using readToData: or readToLength: methods.
* It may be used to for things such as updating progress bars.
**/
- (void)onSocket:(AsyncSocket *)sock didReadPartialDataOfLength:(CFIndex)partialLength tag:(long)tag;
/**
* Called when a socket has completed writing the requested data. Not called if there is an error.
**/
- (void)onSocket:(AsyncSocket *)sock didWriteDataWithTag:(long)tag;
@end
@interface AsyncSocket : NSObject
{
CFSocketRef theSocket; // IPv4 accept or connect socket
CFSocketRef theSocket6; // IPv6 accept or connect socket
CFReadStreamRef theReadStream;
CFWriteStreamRef theWriteStream;
CFRunLoopSourceRef theSource; // For theSocket
CFRunLoopSourceRef theSource6; // For theSocket6
CFRunLoopRef theRunLoop;
CFSocketContext theContext;
NSMutableArray *theReadQueue;
AsyncReadPacket *theCurrentRead;
NSTimer *theReadTimer;
NSMutableData *partialReadBuffer;
NSMutableArray *theWriteQueue;
AsyncWritePacket *theCurrentWrite;
NSTimer *theWriteTimer;
id theDelegate;
Byte theFlags;
long theUserData;
}
- (id)init;
- (id)initWithDelegate:(id)delegate;
- (id)initWithDelegate:(id)delegate userData:(long)userData;
/* String representation is long but has no "\n". */
- (NSString *)description;
/**
* Use "canSafelySetDelegate" to see if there is any pending business (reads and writes) with the current delegate
* before changing it. It is, of course, safe to change the delegate before connecting or accepting connections.
**/
- (id)delegate;
- (BOOL)canSafelySetDelegate;
- (void)setDelegate:(id)delegate;
/* User data can be a long, or an id or void * cast to a long. */
- (long)userData;
- (void)setUserData:(long)userData;
/* Don't use these to read or write. And don't close them, either! */
- (CFSocketRef)getCFSocket;
- (CFReadStreamRef)getCFReadStream;
- (CFWriteStreamRef)getCFWriteStream;
/**
* Once one of these methods is called, the AsyncSocket instance is locked in, and the rest can't be called without
* disconnecting the socket first. If the attempt times out or fails, these methods either return NO or
* call "onSocket:willDisconnectWithError:" and "onSockedDidDisconnect:".
**/
- (BOOL)acceptOnPort:(UInt16)port error:(NSError **)errPtr;
- (BOOL)acceptOnAddress:(NSString *)hostaddr port:(UInt16)port error:(NSError **)errPtr;
- (BOOL)connectToHost:(NSString *)hostname onPort:(UInt16)port error:(NSError **)errPtr;
- (BOOL)connectToAddress:(NSData *)remoteAddr error:(NSError **)errPtr;
/**
* Disconnects immediately. Any pending reads or writes are dropped.
**/
- (void)disconnect;
/**
* Disconnects after all pending writes have completed.
* After calling this, the read and write methods (including "readDataWithTimeout:tag:") will do nothing.
* The socket will disconnect even if there are still pending reads.
**/
- (void)disconnectAfterWriting;
/* Returns YES if the socket and streams are open, connected, and ready for reading and writing. */
- (BOOL)isConnected;
/**
* Returns the local or remote host and port to which this socket is connected, or nil and 0 if not connected.
* The host will be an IP address.
**/
- (NSString *)connectedHost;
- (UInt16)connectedPort;
- (NSString *)localHost;
- (UInt16)localPort;
- (BOOL)isIPv4;
- (BOOL)isIPv6;
// The readData and writeData methods won't block. To not time out, use a negative time interval.
// If they time out, "onSocket:disconnectWithError:" is called. The tag is for your convenience.
// You can use it as an array index, step number, state id, pointer, etc., just like the socket's user data.
/**
* This will read a certain number of bytes into memory, and call the delegate method when those bytes have been read.
* If there is an error, partially read data is lost.
* If the length is 0, this method does nothing and the delegate is not called.
**/
- (void)readDataToLength:(CFIndex)length withTimeout:(NSTimeInterval)timeout tag:(long)tag;
/**
* This reads bytes until (and including) the passed "data" parameter, which acts as a separator.
* The bytes and the separator are returned by the delegate method.
*
* If you pass nil or zero-length data as the "data" parameter,
* the method will do nothing, and the delegate will not be called.
*
* To read a line from the socket, use the line separator (e.g. CRLF for HTTP, see below) as the "data" parameter.
* Note that this method is not character-set aware, so if a separator can occur naturally as part of the encoding for
* a character, the read will prematurely end.
**/
- (void)readDataToData:(NSData *)data withTimeout:(NSTimeInterval)timeout tag:(long)tag;
/**
* Same as readDataToData:withTimeout:tag, with the additional restriction that the amount of data read
* may not surpass the given maxLength (specified in bytes).
*
* If you pass a maxLength parameter that is less than the length of the data parameter,
* the method will do nothing, and the delegate will not be called.
*
* If the max length is surpassed, it is treated the same as a timeout - the socket is closed.
*
* Pass -1 as maxLength if no length restriction is desired, or simply use the readDataToData:withTimeout:tag method.
**/
- (void)readDataToData:(NSData *)data withTimeout:(NSTimeInterval)timeout maxLength:(CFIndex)length tag:(long)tag;
/**
* Reads the first available bytes that become available on the socket.
**/
- (void)readDataWithTimeout:(NSTimeInterval)timeout tag:(long)tag;
/* Writes data to the socket, and calls the delegate when finished.
*
* If you pass in nil or zero-length data, this method does nothing and the delegate will not be called.
**/
- (void)writeData:(NSData *)data withTimeout:(NSTimeInterval)timeout tag:(long)tag;
/**
* Returns progress of current read or write, from 0.0 to 1.0, or NaN if no read/write (use isnan() to check).
* "tag", "done" and "total" will be filled in if they aren't NULL.
**/
- (float)progressOfReadReturningTag:(long *)tag bytesDone:(CFIndex *)done total:(CFIndex *)total;
- (float)progressOfWriteReturningTag:(long *)tag bytesDone:(CFIndex *)done total:(CFIndex *)total;
/**
* For handling readDataToData requests, data is necessarily read from the socket in small increments.
* The performance can be improved by allowing AsyncSocket to read larger chunks at a time and
* store any overflow in a small internal buffer.
* This is termed pre-buffering, as some data may be read for you before you ask for it.
* If you use readDataToData a lot, enabling pre-buffering may offer a small performance improvement.
*
* Pre-buffering is disabled by default. You must explicitly enable it to turn it on.
*
* Note: If your protocol negotiates upgrades to TLS (as opposed to using TLS from the start), you should
* consider how, if at all, pre-buffering could affect the TLS negotiation sequence.
* This is because TLS runs atop TCP, and requires sending/receiving a TLS handshake over the TCP socket.
* If the negotiation sequence is poorly designed, pre-buffering could potentially pre-read part of the TLS handshake,
* thus causing TLS to fail. In almost all cases, especially when implementing a formalized protocol, this will never
* be a hazard.
**/
- (void)enablePreBuffering;
/**
* In the event of an error, this method may be called during onSocket:willDisconnectWithError: to read
* any data that's left on the socket.
**/
- (NSData *)unreadData;
/* A few common line separators, for use with "readDataToData:withTimeout:tag:". */
+ (NSData *)CRLFData; // 0x0D0A
+ (NSData *)CRData; // 0x0D
+ (NSData *)LFData; // 0x0A
+ (NSData *)ZeroData; // 0x00
@end

File diff suppressed because it is too large Load Diff

View File

@@ -1,129 +0,0 @@
/*
iosFtpServer
Copyright (C) 2008 Richard Dearlove ( monsta )
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 2.1 of the License, or (at your option) any later version.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public
License along with this library; if not, write to the Free Software
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
#import <UIKit/UIKit.h>
#import "AsyncSocket.h"
#import "FtpDataConnection.h"
#import "FtpDefines.h"
#include <sys/time.h>
#include <time.h>
@class FtpServer;
@interface FtpConnection : NSObject {
AsyncSocket *connectionSocket; // Socket for this particular connection
FtpServer *server; // pointer to Server object
AsyncSocket *dataListeningSocket; // Socket to listen for a data connection to spawn on <-- think this is now redundant
AsyncSocket *dataSocket; // duplicates the listening socket - remove listening socket from code when working - which seems to be the case.
FtpDataConnection *dataConnection; // instance handling spawned data connection socket
NSArray *msgComponents; // The Message rcvd broken into an array
UInt16 dataPort;
int transferMode;
NSMutableArray *queuedData;
NSString *currentUser; // The current user for this connection
NSString *currentDir; // The current directory for this connection
NSString *currentFile; // File that is about to be uploaded
NSFileHandle *currentFileHandle; // File handle of what to save
NSString *rnfrFilename; // rnfr
}
-(id)initWithAsyncSocket:(AsyncSocket*)newSocket forServer:(id)myServer ;
#pragma mark STATE
@property(readwrite)int transferMode;
@property(readwrite, retain ) NSString *currentFile;
@property(readwrite, retain ) NSString *currentDir;
@property(readwrite, retain ) NSString *rnfrFilename;
-(NSString*)connectionAddress;
#pragma mark ASYNCSOCKET DATACONN
-(BOOL)openDataSocket:(int)portNumber;
-(int)choosePasvDataPort;
-(BOOL)onSocketWillConnect:(AsyncSocket *)sock;
-(void)onSocket:(AsyncSocket *)sock didAcceptNewSocket:(AsyncSocket *)newSocket;
#pragma mark ASYNCSOCKET FTPCLIENT CONNECTION
-(void)onSocket:(AsyncSocket*)sock didReadData:(NSData*)data withTag:(long)tag;
-(void)onSocket:(AsyncSocket*)sock didWriteDataWithTag:(long)tag;
-(void)sendMessage:(NSString*)ftpMessage; // calls FC writedata
-(void)sendDataString:(NSString*)dataString; // calls FDC writedata
-(void)sendData:(NSMutableData*)data;
-(void)didReceiveDataWritten; // notification that FDC wrote data
-(void)didReceiveDataRead; // notification that FDC read data ie a transfer
-(void)didFinishReading; // Called at the closing == end of a data connection from the client we presume
#pragma mark PROCESS
-(void)processDataRead:(NSData*)data;
-(void)processCommand;
#pragma mark COMMANDS
-(void)doQuit:(id)sender arguments:(NSArray*)arguments;
-(void)doUser:(id)sender arguments:(NSArray*)arguments;
-(void)doPass:(id)sender arguments:(NSArray*)arguments;
-(void)doStat:(id)sender arguments:(NSArray*)arguments;
-(void)doFeat:(id)sender arguments:(NSArray*)arguments;
-(void)doList:(id)sender arguments:(NSArray*)arguments;
-(void)doPwd:(id)sender arguments:(NSArray*)arguments;
-(void)doNoop:(id)sender arguments:(NSArray*)arguments;
-(void)doSyst:(id)sender arguments:(NSArray*)arguments;
-(void)doLprt:(id)sender arguments:(NSArray*)arguments;
-(void)doPasv:(id)sender arguments:(NSArray*)arguments;
-(void)doEpsv:(id)sender arguments:(NSArray*)arguments;
-(void)doPort:(id)sender arguments:(NSArray*)arguments;
-(void)doNlst:(id)sender arguments:(NSArray*)arguments;
-(void)doStor:(id)sender arguments:(NSArray*)arguments;
-(void)doRetr:(id)sender arguments:(NSArray*)arguments;
-(void)doDele:(id)sender arguments:(NSArray*)arguments;
-(void)doMlst:(id)sender arguments:(NSArray*)arguments;
-(void)doSize:(id)sender arguments:(NSArray*)arguments;
-(void)doMkdir:(id)sender arguments:(NSArray*)arguments;
-(void)doCdUp:(id)sender arguments:(NSArray*)arguments;
-(void)doRnfr:(id)sender arguments:(NSArray*)arguments;
-(void)doRnto:(id)sender arguments:(NSArray*)arguments;
#pragma mark UTITILITES
-(NSString*)makeFilePathFrom:(NSString*)filename;
-(unsigned long long)fileSize:(NSString*)filePath;
-(NSString*)fileNameFromArgs:(NSArray*)arguments;
- (Boolean)changedCurrentDirectoryTo:(NSString *)newDirectory;
-(Boolean)canChangeDirectoryTo:(NSString *)testDirectory;
- (Boolean)accessibleFilePath:(NSString*)filePath; // check filepath exists and is in basedir ( if set )
- (Boolean)validNewFilePath:(NSString*)filePath;
- (NSString *)visibleCurrentDir;
-(NSString *)rootedPath:(NSString*)path;
@end

File diff suppressed because it is too large Load Diff

View File

@@ -1,54 +0,0 @@
/*
iosFtpServer
Copyright (C) 2008 Richard Dearlove ( monsta )
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 2.1 of the License, or (at your option) any later version.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public
License along with this library; if not, write to the Free Software
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
#import <UIKit/UIKit.h>
#import "AsyncSocket.h"
#import "FtpDefines.h"
@class FtpConnection;
@interface FtpDataConnection : NSObject {
AsyncSocket *dataSocket;
FtpConnection *ftpConnection; // connection which generated data socket we are tied to
AsyncSocket *dataListeningSocket;
id dataConnection;
NSMutableData *receivedData;
int connectionState;
}
-(id)initWithAsyncSocket:(AsyncSocket*)newSocket forConnection:(id)aConnection withQueuedData:(NSMutableArray*)queuedData;
-(void)writeString:(NSString*)dataString;
-(void)writeData:(NSMutableData*)data;
-(void)writeQueuedData:(NSMutableArray*)queuedData;
-(void)closeConnection;
#pragma mark ASYNCSOCKET DELEGATES
-(BOOL)onSocketWillConnect:(AsyncSocket *)sock;
-(void)onSocket:(AsyncSocket *)sock didAcceptNewSocket:(AsyncSocket *)newSocket;
-(void)onSocket:(AsyncSocket*)sock didReadData:(NSData*)data withTag:(long)tag;
-(void)onSocket:(AsyncSocket*)sock didWriteDataWithTag:(long)tag;
-(void)onSocket:(AsyncSocket *)sock willDisconnectWithError:(NSError *)err;
@property (readonly) NSMutableData *receivedData;
@property (readwrite) int connectionState;
@end

View File

@@ -1,192 +0,0 @@
/*
iosFtpServer
Copyright (C) 2008 Richard Dearlove ( monsta )
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 2.1 of the License, or (at your option) any later version.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public
License along with this library; if not, write to the Free Software
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
#import "FtpDataConnection.h"
#import "FtpConnection.h"
@implementation FtpDataConnection
@synthesize receivedData;
@synthesize connectionState;
// ----------------------------------------------------------------------------------------------------------
-(id)initWithAsyncSocket:(AsyncSocket*)newSocket forConnection:(id)aConnection withQueuedData:(NSMutableArray*)queuedData
// ----------------------------------------------------------------------------------------------------------
{
self = [super init ];
if (self)
{
dataSocket = [newSocket retain ]; // Hang onto the socket that was generated - the FDC is retained by FC
ftpConnection = aConnection;
[ dataSocket setDelegate:self ];
if ( [queuedData count ] )
{
NSLog(@"FC:Write Queued Data");
[self writeQueuedData:queuedData ];
[ queuedData removeAllObjects ]; // Clear out queue
}
// [ dataSocket readDataToData:[AsyncSocket CRLFData] withTimeout:READ_TIMEOUT tag:FTP_CLIENT_REQUEST ];
[ dataSocket readDataWithTimeout:READ_TIMEOUT tag:FTP_CLIENT_REQUEST ];
dataListeningSocket = nil;
receivedData = nil; // [[ NSMutableData alloc ] init ] 12/nov/08 - no need for this. rcd is just a pointer
connectionState = clientQuiet; // Nothing coming through
}
return self;
}
// ----------------------------------------------------------------------------------------------------------
-(void)dealloc
// ----------------------------------------------------------------------------------------------------------
{
[dataSocket release];
[dataListeningSocket release];
[dataConnection release];
// [receivedData release];
[super dealloc];
}
// ----------------------------------------------------------------------------------------------------------
-(void)writeString:(NSString*)dataString
// ----------------------------------------------------------------------------------------------------------
{
NSLog(@"FDC:writeStringData");
NSMutableData *data = [[ dataString dataUsingEncoding:NSUTF8StringEncoding ] mutableCopy]; // Autoreleased
[ data appendData:[AsyncSocket CRLFData] ];
[ dataSocket writeData:data withTimeout:READ_TIMEOUT tag:FTP_CLIENT_REQUEST ];
[ dataSocket readDataWithTimeout:READ_TIMEOUT tag:FTP_CLIENT_REQUEST ];
}
// ----------------------------------------------------------------------------------------------------------
-(void)writeData:(NSMutableData*)data
// ----------------------------------------------------------------------------------------------------------
{
NSLog(@"FDC:writeData");
// [ data appendData:[AsyncSocket CRLFData] ]; // Add on CRLF to end of data - as Windows Explorer needs it
connectionState = clientReceiving; // We hope
[ dataSocket writeData:data withTimeout:READ_TIMEOUT tag:FTP_CLIENT_REQUEST ];
[ dataSocket readDataWithTimeout:READ_TIMEOUT tag:FTP_CLIENT_REQUEST ];
}
// ----------------------------------------------------------------------------------------------------------
-(void)writeQueuedData:(NSMutableArray*)queuedData
// ----------------------------------------------------------------------------------------------------------
{
for (NSMutableData* data in queuedData) {
[self writeData:data ];
}
}
// ----------------------------------------------------------------------------------------------------------
-(void)closeConnection
// ----------------------------------------------------------------------------------------------------------
{
NSLog(@"FDC:closeConnection");
[ dataSocket disconnect ];
}
#pragma mark ASYNCSOCKET DELEGATES
// ----------------------------------------------------------------------------------------------------------
-(BOOL)onSocketWillConnect:(AsyncSocket *)sock
// ----------------------------------------------------------------------------------------------------------
{
NSLog(@"FDC:onSocketWillConnect");
[ dataSocket readDataWithTimeout:READ_TIMEOUT tag:0 ];
return YES;
}
// ----------------------------------------------------------------------------------------------------------
-(void)onSocket:(AsyncSocket *)sock didAcceptNewSocket:(AsyncSocket *)newSocket
// ----------------------------------------------------------------------------------------------------------
{
// This shouldnt happen - we should be connected already - and havent set up a listening socket
NSLog(@"FDC:New Connection -- shouldn't be called");
}
// ----------------------------------------------------------------------------------------------------------
-(void)onSocket:(AsyncSocket*)sock didReadData:(NSData*)data withTag:(long)tag
// ----------------------------------------------------------------------------------------------------------
{
NSLog(@"FDC:didReadData");
// [ dataSocket readDataToData:[AsyncSocket CRLFData] withTimeout:READ_TIMEOUT tag:FTP_CLIENT_REQUEST ]; // continue reading
[ dataSocket readDataWithTimeout:READ_TIMEOUT tag:FTP_CLIENT_REQUEST ];
NSMutableData *mutdata = [data mutableCopy];
receivedData = [ mutdata retain]; // make autoreleased copy of data
// notify connection data came through, ( will write data for us )
[ftpConnection didReceiveDataRead ]; // notify, the connection, so it knows to write the data
[ receivedData release ]; // let go, not our business anymore
connectionState = clientSent;
}
// ----------------------------------------------------------------------------------------------------------
-(void)onSocket:(AsyncSocket*)sock didWriteDataWithTag:(long)tag
// ----------------------------------------------------------------------------------------------------------
{
NSLog(@"FDC:didWriteData");
[ ftpConnection didReceiveDataWritten ]; // notify that we are finished writing
// [ dataSocket readDataToData:[AsyncSocket CRLFData] withTimeout:READ_TIMEOUT tag:FTP_CLIENT_REQUEST ]; // continue reading
[ dataSocket readDataWithTimeout:READ_TIMEOUT tag:FTP_CLIENT_REQUEST ];
}
// ----------------------------------------------------------------------------------------------------------
-(void)onSocket:(AsyncSocket *)sock willDisconnectWithError:(NSError *)err
// ----------------------------------------------------------------------------------------------------------
{
NSLog(@"FDC:willDisconnect");
// if we were writing and there's no error, then it must be the end of file
if ( connectionState == clientSending )
{
NSLog(@"FDC::did FinishReading");
// hopefully this is the end of the connection. not sure how we can tell
}
else
{
NSLog(@"FDC: we werent expecting this as we never set clientSending prob late coming up");
}
[ ftpConnection didFinishReading ]; // its over, please send the message
}
- (BOOL)onReadStreamEnded:(AsyncSocket*)sock
{
NSLog( @"FDC: onReadStreamEnded %d(clientSending is %d)", connectionState, clientSending );
if ( connectionState == clientSent ||
connectionState == clientSending ) return YES;
return NO;
}
@end

View File

@@ -1,15 +0,0 @@
enum {
pasvftp=0,epsvftp,portftp,lprtftp, eprtftp
};
#define DATASTR(args) [ args dataUsingEncoding:NSUTF8StringEncoding ]
#define SERVER_PORT 20000
#define READ_TIMEOUT -1
#define FTP_CLIENT_REQUEST 0
enum {
clientSending=0, clientReceiving=1, clientQuiet=2,clientSent=3
};

View File

@@ -1,72 +0,0 @@
/*
iosFtpServer
Copyright (C) 2008 Richard Dearlove ( monsta )
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 2.1 of the License, or (at your option) any later version.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public
License along with this library; if not, write to the Free Software
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
#import <UIKit/UIKit.h>
#import "AsyncSocket.h"
#import "FtpDefines.h"
#import "FtpConnection.h"
#import "list.h"
@interface FtpServer : NSObject {
AsyncSocket *listenSocket;
NSMutableArray *connectedSockets;
id server;
id notificationObject;
int portNumber;
id delegate;
NSMutableArray *connections;
NSDictionary *commands;
NSString *baseDir;
Boolean changeRoot; // Change root to virtual root ( basedir )
int clientEncoding; // FTP client encoding type
}
- (id)initWithPort:(unsigned)serverPort withDir:(NSString *)aDirectory notifyObject:(id)sender;
- (void)stopFtpServer;
#pragma mark ASYNCSOCKET DELEGATES
- (void)onSocket:(AsyncSocket *)sock didAcceptNewSocket:(AsyncSocket *)newSocket;
- (void)onSocket:(AsyncSocket *)sock didConnectToHost:(NSString *)host port:(UInt16)port;
#pragma mark NOTIFICATIONS
- (void)didReceiveFileListChanged;
#pragma mark CONNECTIONS
- (void)closeConnection:(id)theConnection;
- (NSString *)createList:(NSString *)directoryPath;
@property (readwrite, retain) AsyncSocket *listenSocket;
@property (readwrite, retain) NSMutableArray *connectedSockets;
@property (readwrite, retain) id server;
@property (readwrite, retain) id notificationObject;
@property (readwrite) int portNumber;
@property (readwrite, retain) id delegate;
@property (readwrite, retain) NSMutableArray *connections;
@property (readwrite, retain) NSDictionary *commands;
@property (readwrite, retain) NSString *baseDir;
@property (readwrite) Boolean changeRoot;
@property (readwrite) int clientEncoding;
@end

View File

@@ -1,198 +0,0 @@
/*
iosFtpServer
Copyright (C) 2008 Richard Dearlove ( monsta )
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 2.1 of the License, or (at your option) any later version.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public
License along with this library; if not, write to the Free Software
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
// 11/02/08 changes to made to allow stopping of the server. Also added a bit of code to show users IP address.
// Cleaned up the code to make the retains and releases a bit more obvious... mostly programming style preferences.
// Added a stop Ftp server method to be called from where ever it was start. See iphoneLibTestViewController.m to see how I was calling it.
// Most users wouldn't want the server to continue to run once the transfers are done.
#import "FtpServer.h"
@implementation FtpServer
@synthesize listenSocket, connectedSockets, server, notificationObject, portNumber, delegate, commands, baseDir, connections;
@synthesize clientEncoding;
@synthesize changeRoot;
// ----------------------------------------------------------------------------------------------------------
- (id)initWithPort:(unsigned)serverPort withDir:(NSString*)aDirectory notifyObject:(id)sender
// ----------------------------------------------------------------------------------------------------------
{
if( self = [super init] ) {
NSError *error = nil;
self.notificationObject = sender;
// Load up commands
NSString *plistPath = [[ NSBundle mainBundle ] pathForResource:@"ftp_commands" ofType:@"plist"];
if ( ! [ [ NSFileManager defaultManager ] fileExistsAtPath:plistPath ] )
{
NSLog(@"ftp_commands.plist missing");
}
commands = [ [ NSDictionary alloc ] initWithContentsOfFile:plistPath];
// Clear out connections list
NSMutableArray *myConnections = [[NSMutableArray alloc] init];
self.connections = myConnections;
[myConnections release];
// Create a socket
self.portNumber = serverPort;
AsyncSocket *myListenSocket = [[AsyncSocket alloc] initWithDelegate:self];
self.listenSocket = myListenSocket;
[myListenSocket release];
NSLog(@"Listening on %u", portNumber);
[listenSocket acceptOnPort:serverPort error:&error]; // start lisetning on this port.
NSMutableArray *myConnectedSockets = [[NSMutableArray alloc] initWithCapacity:1];
self.connectedSockets = myConnectedSockets;
[myConnectedSockets release];
// Set directory - have to do this because on iphone, some directories arent what they report back as, so need real path it resolves to, CHECKME - might be an easier way
NSFileManager *fileManager = [ NSFileManager defaultManager ];
NSString *expandedPath = [ aDirectory stringByStandardizingPath ];
// CHANGE TO NEW DIRECTORY & GET SYSTEM FILE PATH ( no .. etc )
if ([ fileManager changeCurrentDirectoryPath:expandedPath ]) // try changing to directory
{
self.baseDir = [[ fileManager currentDirectoryPath ] copy] ; // Gets the real path. CHECKME.
// self.baseDir = @"/Users"; // REMOVEME - added for testing 7/6/10
}
else
{
self.baseDir = aDirectory; // shouldnt get to this line really
}
self.changeRoot = false; // true if you want them to be sandboxed/chrooted into the basedir
// the default client encoding is UTF8
self.clientEncoding = NSUTF8StringEncoding;
}
return self;
}
// ----------------------------------------------------------------------------------------------------------
-(void)stopFtpServer
// ----------------------------------------------------------------------------------------------------------
{
if(listenSocket)[listenSocket disconnect];
[connectedSockets removeAllObjects];
[connections removeAllObjects];
}
#pragma mark ASYNCSOCKET DELEGATES
// ----------------------------------------------------------------------------------------------------------
- (void)onSocket:(AsyncSocket *)sock didAcceptNewSocket:(AsyncSocket *)newSocket
// ----------------------------------------------------------------------------------------------------------
{
FtpConnection *newConnection = [[[ FtpConnection alloc ] initWithAsyncSocket:newSocket forServer:self] autorelease]; // Create an ftp connection
[ connections addObject:newConnection ]; // Add this to our list of connections
NSLog(@"FS:didAcceptNewSocket port:%i", [sock localPort]);
if ([sock localPort] == portNumber )
{
NSLog(@"Connection on Server Port");
}
else
{
// must be a data comms port
// spawn a data comms port
// look for the connection with the same port
// and attach it
NSLog(@"--ERROR %i, %i", [sock localPort],portNumber);
}
}
// ----------------------------------------------------------------------------------------------------------
- (void)onSocket:(AsyncSocket *)sock didConnectToHost:(NSString *)host port:(UInt16)port
// ----------------------------------------------------------------------------------------------------------
{
NSLog(@"FtpServer:didConnectToHost port:%i", [sock localPort]);
}
#pragma mark NOTIFICATIONS
// ----------------------------------------------------------------------------------------------------------
-(void)didReceiveFileListChanged
// ----------------------------------------------------------------------------------------------------------
{
if ([notificationObject respondsToSelector:@selector(didReceiveFileListChanged)])
[notificationObject didReceiveFileListChanged ];
}
#pragma mark CONNECTIONS
// ----------------------------------------------------------------------------------------------------------
- (void)closeConnection:(id)theConnection
// ----------------------------------------------------------------------------------------------------------
{
// Search through connections for this one - and delete
// this should release it - and delloc
[connections removeObject:theConnection ];
}
// ----------------------------------------------------------------------------------------------------------
-(NSString*)createList:(NSString*)directoryPath
// ----------------------------------------------------------------------------------------------------------
{
return createList(directoryPath);
}
// ----------------------------------------------------------------------------------------------------------
- (void)dealloc
// ----------------------------------------------------------------------------------------------------------
{
if(listenSocket)
{
[listenSocket disconnect];
[listenSocket release];
}
[connectedSockets release];
[notificationObject release];
[connections release];
[commands release];
[baseDir release];
[super dealloc];
}
@end

View File

@@ -1,26 +0,0 @@
//
// networkController.h
// DiddyDJ
//
// Created by Richard Dearlove on 21/10/2008.
// Copyright 2008 DiddySoft. All rights reserved.
//
#import <UIKit/UIKit.h>
#import <SystemConfiguration/SystemConfiguration.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <netdb.h>
#include <unistd.h>
#include <ifaddrs.h>
@interface NetworkController : NSObject {
}
+ (NSString *)localWifiIPAddress;
+ (NSString *) localIPAddress;
+ (BOOL)addressFromString:(NSString *)IPAddress address:(struct sockaddr_in *)address;
+ (NSString *) getIPAddressForHost: (NSString *) theHost;
+ (BOOL) hostAvailable: (NSString *) theHost;
+ (BOOL) connectedToNetwork;
@end

View File

@@ -1,184 +0,0 @@
//
// networkController.m
//
// Created by Richard Dearlove on 21/10/2008.
// Copyright 2008 DiddySoft. All rights reserved.
//
#import "NetworkController.h"
@implementation NetworkController
// Return the localized IP address
// ----------------------------------------------------------------------------------------------------------
+ (NSString *)localWifiIPAddress
// ----------------------------------------------------------------------------------------------------------
{
NSString *address = @"error";
struct ifaddrs *interfaces = NULL;
struct ifaddrs *temp_addr = NULL;
int success = 0;
// retrieve the current interfaces - returns 0 on success
success = getifaddrs(&interfaces);
if (success == 0)
{
// Loop through linked list of interfaces
temp_addr = interfaces;
while(temp_addr != NULL)
{
if(temp_addr->ifa_addr->sa_family == AF_INET)
{
// Check if interface is en0 which is the wifi connection on the iPhone
if([[NSString stringWithUTF8String:temp_addr->ifa_name] isEqualToString:@"en0"])
{
// Get NSString from C String
address = [NSString stringWithUTF8String:inet_ntoa(((struct sockaddr_in *)temp_addr->ifa_addr)->sin_addr)];
}
}
temp_addr = temp_addr->ifa_next;
}
}
// Free memory
freeifaddrs(interfaces);
return address;
}
// ----------------------------------------------------------------------------------------------------------
// ----------------------------------------------------------------------------------------------------------
+ (NSString *) localIPAddress
// ----------------------------------------------------------------------------------------------------------
{
char baseHostName[255];
gethostname(baseHostName, 255);
// Adjust for iPhone -- add .local to the host name
char hn[255];
sprintf(hn, "%s.local", baseHostName);
struct hostent *host = gethostbyname(hn);
if (host == NULL)
{
herror("resolv");
return NULL;
}
else {
struct in_addr **list = (struct in_addr **)host->h_addr_list;
return [NSString stringWithCString:inet_ntoa(*list[0])];
}
return NULL;
}
// ----------------------------------------------------------------------------------------------------------
+ (BOOL)addressFromString:(NSString *)IPAddress address:(struct sockaddr_in *)address
// ----------------------------------------------------------------------------------------------------------
{
if (!IPAddress || ![IPAddress length]) {
return NO;
}
memset((char *) address, sizeof(struct sockaddr_in), 0);
address->sin_family = AF_INET;
address->sin_len = sizeof(struct sockaddr_in);
int conversionResult = inet_aton([IPAddress UTF8String], &address->sin_addr);
if (conversionResult == 0) {
NSAssert1(conversionResult != 1, @"Failed to convert the IP address string into a sockaddr_in: %@", IPAddress);
return NO;
}
return YES;
}
// ----------------------------------------------------------------------------------------------------------
+ (NSString *) getIPAddressForHost: (NSString *) theHost
// ----------------------------------------------------------------------------------------------------------
{
struct hostent *host = gethostbyname([theHost UTF8String]);
if (host == NULL) {
herror("resolv");
return NULL;
}
struct in_addr **list = (struct in_addr **)host->h_addr_list;
NSString *addressString = [NSString stringWithCString:inet_ntoa(*list[0])];
return addressString;
}
// ----------------------------------------------------------------------------------------------------------
+ (BOOL) hostAvailable: (NSString *) theHost
// ----------------------------------------------------------------------------------------------------------
{
NSString *addressString = [self getIPAddressForHost:theHost];
if (!addressString)
{
printf("Error recovering IP address from host name\n");
return NO;
}
struct sockaddr_in address;
BOOL gotAddress = [self addressFromString:addressString address:&address];
if (!gotAddress)
{
printf("Error recovering sockaddr address from %s\n", [addressString UTF8String]);
return NO;
}
SCNetworkReachabilityRef defaultRouteReachability = SCNetworkReachabilityCreateWithAddress(NULL, (struct sockaddr *)&address);
SCNetworkReachabilityFlags flags;
BOOL didRetrieveFlags = SCNetworkReachabilityGetFlags(defaultRouteReachability, &flags);
CFRelease(defaultRouteReachability);
if (!didRetrieveFlags)
{
printf("Error. Could not recover network reachability flags\n");
return NO;
}
BOOL isReachable = flags & kSCNetworkFlagsReachable;
return isReachable ? YES : NO;;
}
// ----------------------------------------------------------------------------------------------------------
+ (BOOL) connectedToNetwork
// ----------------------------------------------------------------------------------------------------------
{
// Create zero addy
struct sockaddr_in zeroAddress;
bzero(&zeroAddress, sizeof(zeroAddress));
zeroAddress.sin_len = sizeof(zeroAddress);
zeroAddress.sin_family = AF_INET;
// Recover reachability flags
SCNetworkReachabilityRef defaultRouteReachability = SCNetworkReachabilityCreateWithAddress(NULL, (struct sockaddr *)&zeroAddress);
CFRelease(defaultRouteReachability);
SCNetworkReachabilityFlags flags;
BOOL didRetrieveFlags = SCNetworkReachabilityGetFlags(defaultRouteReachability, &flags);
if (!didRetrieveFlags)
{
printf("Error. Could not recover network reachability flags\n");
return 0;
}
BOOL isReachable = flags & kSCNetworkFlagsReachable;
BOOL needsConnection = flags & kSCNetworkFlagsConnectionRequired;
// BOOL isEDGE = flags & kSCNetworkReachabilityFlagsIsWWAN;
return (isReachable && !needsConnection) ? YES : NO;
}
@end

View File

@@ -1,64 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>ack</key>
<string>doAck:</string>
<key>cdup</key>
<string>doCdUp:</string>
<key>cwd</key>
<string>doCwd:</string>
<key>dele</key>
<string>doDele:</string>
<key>eprt</key>
<string>doEprt:</string>
<key>epsv</key>
<string>doEpsv:</string>
<key>feat</key>
<string>doFeat:</string>
<key>list</key>
<string>doList:</string>
<key>lprt</key>
<string>doLprt:</string>
<key>mkd</key>
<string>doMkdir:</string>
<key>mlst</key>
<string>doMlst:</string>
<key>nlst</key>
<string>doList:</string>
<key>noop</key>
<string>doNoop:</string>
<key>opts</key>
<string>doOpts:</string>
<key>pass</key>
<string>doPass:</string>
<key>pasv</key>
<string>doPasv:</string>
<key>port</key>
<string>doPort:</string>
<key>pwd</key>
<string>doPwd:</string>
<key>quit</key>
<string>doQuit:</string>
<key>retr</key>
<string>doRetr:</string>
<key>rmd</key>
<string>doDele:</string>
<key>rnfr</key>
<string>doRnfr:</string>
<key>rnto</key>
<string>doRnto:</string>
<key>size</key>
<string>doSize:</string>
<key>stat</key>
<string>doStat:</string>
<key>stor</key>
<string>doStor:</string>
<key>syst</key>
<string>doSyst:</string>
<key>type</key>
<string>doType:</string>
<key>user</key>
<string>doUser:</string>
</dict>
</plist>

View File

@@ -17,7 +17,6 @@
#import <Foundation/Foundation.h>
#import <UIKit/UIKit.h>
#import <AVFoundation/AVFoundation.h>
#import "FtpServer.h"
#include <sys/stat.h>
#include "dlfcn.h"
@@ -78,36 +77,22 @@ const char *IOS_GetDocsDir(void)
}
}
UIBackgroundTaskIdentifier task;
FtpServer *server = NULL;
void IOS_StartBackgroundTask(void)
const char *IOS_GetExecDir(void)
{
if( !server ) return;
if( task != UIBackgroundTaskInvalid )
return;
UIApplication* app = [UIApplication sharedApplication];
static const char *dir = NULL;
task = [app beginBackgroundTaskWithExpirationHandler:^{
[app endBackgroundTask:task];
task = UIBackgroundTaskInvalid;
}];
if( dir )
return dir;
// Start the long-running task and return immediately.
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
UIBackgroundTaskIdentifier local = task;
// do not keep "zombie" tasks
while( 1 )
sleep(1);
NSString *executableDirctory = [[NSBundle mainBundle] bundlePath];
[app endBackgroundTask:local];
task = UIBackgroundTaskInvalid;
});
dir = [executableDirctory fileSystemRepresentation];
NSLog(@"IOS_GetDocsDir: %s", dir);
return dir;
}
#define SETTINGS_MAGIC 111
typedef struct settings_s
@@ -188,8 +173,6 @@ void IOS_LaunchDialog( void )
UIScrollView *scroll = [[UIScrollView alloc] initWithFrame:CGRectMake(0, 0, 300, 200)];
UISwitch *ftpswitch = [[UISwitch alloc] initWithFrame:CGRectMake(210,60,80,30)];
UILabel *argstitle = [[UILabel alloc] initWithFrame:CGRectMake(0, 0, 300, 30)];
[argstitle setText:@"Command-line arguments:"];
@@ -197,12 +180,6 @@ void IOS_LaunchDialog( void )
[args setBackgroundColor:[[UIColor alloc] initWithRed:1 green:1 blue:1 alpha:1]];
if(isdark) [args setBackgroundColor:[[UIColor alloc] initWithRed:0 green:0 blue:0 alpha:1]];
UILabel *ftptitle = [[UILabel alloc] initWithFrame:CGRectMake(0, 60, 200, 30)];
[ftptitle setText:@"FTP Server"];
UITextField *port = [[UITextField alloc] initWithFrame:CGRectMake(110, 60, 100, 30)];
UITextField *suffix = [[UITextField alloc] initWithFrame:CGRectMake(140, 90, 160, 30 )];
[suffix setBackgroundColor:[[UIColor alloc] initWithRed:1 green:1 blue:1 alpha:1]];
@@ -214,9 +191,6 @@ void IOS_LaunchDialog( void )
[scroll addSubview:argstitle];
[scroll addSubview:args];
[scroll addSubview:suffix];
[scroll addSubview:ftpswitch];
[scroll addSubview:ftptitle];
[scroll addSubview:port];
[scroll addSubview:suffixtitle];
settingsfile = fopen( settingspath, "rb" );
@@ -225,14 +199,11 @@ void IOS_LaunchDialog( void )
settings.args[1023] = 0;
settings.suffix[31] = 0;
[args setText:@(settings.args)];
[port setText:[NSString stringWithFormat:@"%u", settings.port]];
[suffix setText:@(settings.suffix)];
ftpswitch.on = settings.ftpserver;
}
else
{
[args setText:@"-dev 2 -log"];
[port setText:@"21135"];
}
scroll.contentSize=CGSizeMake(250, 200);
@@ -248,10 +219,8 @@ void IOS_LaunchDialog( void )
if( (settingsfile = fopen( settingspath, "wb" )) )
{
settings.ftpserver = ftpswitch.on;
strlcpy(settings.args, [args.text UTF8String], 1024);
strlcpy(settings.suffix, [suffix.text UTF8String], 32 );
settings.port = [port.text intValue];
settings.magic = 111;
fwrite(&settings, sizeof(settings), 1, settingsfile);
@@ -263,25 +232,6 @@ void IOS_LaunchDialog( void )
exit(0);
}
if( ftpswitch.on )
{
button = -1;
[[[UIAlertView alloc] initWithTitle:@"Xash3D" message:[NSString stringWithFormat:@"Started FTP server on port %@", port.text] delegate:delegate cancelButtonTitle:@"Ok" otherButtonTitles:nil] show];
server = [[ FtpServer alloc ] initWithPort:[port.text integerValue] withDir:@(docsDir) notifyObject:nil ];
IOS_StartBackgroundTask();
@autoreleasepool {
while( button == -1 ) {
[[NSRunLoop currentRunLoop] runMode:NSDefaultRunLoopMode beforeDate:[NSDate distantFuture]];
IOS_StartBackgroundTask(); // keep running
}
}
}
NSArray *argv = [ args.text componentsSeparatedByString:@" " ];
int count = [argv count];
@@ -299,11 +249,8 @@ void IOS_LaunchDialog( void )
alert.delegate = nil;
[ftpswitch release];
[ftptitle release];
[args release];
[argstitle release];
[port release];
[suffix release];
[suffixtitle release];

View File

@@ -1,75 +0,0 @@
/*
ios_lib.c - dynamic library code for iOS
Copyright (C) 2017-2018 mittorn
This program is free software: you can redistribute it and/sor modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
*/
#include <string.h>
#include <SDL.h>
#include <dlfcn.h>
#include "crtlib.h"
#include "library.h"
#include "platform/ios/lib_ios.h"
#include "common.h"
#define EXT_LENGTH 5
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 );
pHandle = dlopen( path, RTLD_LAZY );
if( !pHandle )
{
COM_PushLibraryError(errorstring);
COM_PushLibraryError(dlerror());
}
return pHandle;
}
const char *g_szLibrarySuffix;
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" ) )
{
return IOS_LoadLibraryInternal( dllname );
}
string name;
string strippedname;
const char *postfix = g_szLibrarySuffix;
char *pHandle;
if( !postfix )
{
if ( Q_strcmp(FS_Gamedir(), host.default_gamedir ) )
{
postfix = FS_Gamedir( );
}
else postfix = "";
}
Q_strncpy( strippedname, dllname, sizeof( strippedname ) );
COM_StripExtension(strippedname);
Q_snprintf( name, MAX_STRING, "%s_%s.dylib", strippedname, postfix );
pHandle = IOS_LoadLibraryInternal( name );
if( pHandle )
return pHandle;
return IOS_LoadLibraryInternal( dllname );
}

View File

@@ -1,25 +0,0 @@
/*
ios_lib.h - dynamic library code for iOS
Copyright (C) 2017-2018 mittorn
This program is free software: you can redistribute it and/sor modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
*/
#pragma once
#if TARGET_OS_IPHONE
#ifndef IOS_LIB_H
#define IOS_LIB_H
#define Platform_POSIX_LoadLibrary( x ) IOS_LoadLibrary(( x ))
void *IOS_LoadLibrary( const char *dllname );
#endif // IOS_LIB_H
#endif // TARGET_OS_IPHONE

View File

@@ -1,29 +0,0 @@
/*
iosFtpServer
Copyright (C) 2008 Richard Dearlove ( monsta )
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 2.1 of the License, or (at your option) any later version.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public
License along with this library; if not, write to the Free Software
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
#import <Foundation/Foundation.h>
#pragma mark LS replacement
NSString* createList(NSString* directoryPath);
#pragma mark Supporting Functions
int filesinDirectory(NSString* filePath );
NSMutableString* int2BinString(int x);
NSMutableString *byte2String(int x );
NSMutableString *bin2perms(NSString *binaryValue);

View File

@@ -1,202 +0,0 @@
/*
iosFtpServer
Copyright (C) 2008 Richard Dearlove ( monsta )
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 2.1 of the License, or (at your option) any later version.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public
License along with this library; if not, write to the Free Software
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
#import "list.h"
// ----------------------------------------------------------------------------------------------------------
NSString* createList(NSString* directoryPath)
// ----------------------------------------------------------------------------------------------------------
{
NSFileManager *fileManager = [ NSFileManager defaultManager ];
NSDictionary *fileAttributes;
NSError *error;
NSString* fileType;
NSNumber* filePermissions;
long fileSubdirCount;
NSString* fileOwner;
NSString* fileGroup;
NSNumber* fileSize;
NSDate* fileModified;
NSString* fileDateFormatted;
NSDateFormatter* dateFormatter = [[[ NSDateFormatter alloc ] init ] autorelease ];
BOOL fileIsDirectory;
NSMutableString* returnString= [ NSMutableString new ];
NSString* formattedString;
NSString* binaryString;
[returnString appendString:@"\r\n"];
NSDirectoryEnumerator *dirEnum = [fileManager enumeratorAtPath:directoryPath];
NSString *filePath;
NSString* firstChar;
NSString* fullFilePath;
[dateFormatter setDateFormat:@"MMM dd HH:mm"];
NSLocale *englishLocale = [[NSLocale alloc] initWithLocaleIdentifier:@"en"];
[dateFormatter setLocale:englishLocale];
[englishLocale release];
NSLog(@"Get LS for %@", directoryPath );
int numberOfFiles = 0;
while (filePath = [dirEnum nextObject]) {
firstChar = [ filePath substringToIndex:1 ];
[dirEnum skipDescendents ]; // don't go down that recursive road
if ( ![ firstChar isEqualToString:@"."] ) // dont show hidden files
{
fullFilePath = [directoryPath stringByAppendingPathComponent:filePath];
fileAttributes = [ fileManager attributesOfItemAtPath:fullFilePath error:&error ];
fileType = [ fileAttributes valueForKey:NSFileType ];
filePermissions = [ fileAttributes valueForKey:NSFilePosixPermissions ];
fileSubdirCount = filesinDirectory(fullFilePath);
fileOwner = [ fileAttributes valueForKey:NSFileOwnerAccountName ];
fileGroup = [ fileAttributes valueForKey:NSFileGroupOwnerAccountName ];
fileSize = [ fileAttributes valueForKey:NSFileSize ];
fileModified = [ fileAttributes valueForKey:NSFileModificationDate ];
fileDateFormatted = [ dateFormatter stringFromDate:fileModified ];
fileIsDirectory = (fileType == NSFileTypeDirectory );
fileSubdirCount = fileSubdirCount <1 ? 1 : fileSubdirCount;
binaryString = int2BinString([filePermissions unsignedLongValue]) ;
binaryString = [ binaryString substringFromIndex:7 ];// snip off the front
formattedString = [ NSString stringWithFormat:@"%@%@ %5i %12@ %12@ %10qu %@ %@", fileIsDirectory ? @"d" : @"-" ,bin2perms(binaryString),fileSubdirCount, fileOwner, fileGroup, [fileSize unsignedLongLongValue], fileDateFormatted , filePath ];
[ returnString appendString:formattedString ];
[ returnString appendString:@"\n" ];
numberOfFiles++;
}
}
[returnString insertString: [NSString stringWithFormat:@"total %d", numberOfFiles] atIndex:0];
// NSLog(returnString );
return [returnString autorelease]; // FIXME - release count
}
// ----------------------------------------------------------------------------------------------------------
int filesinDirectory(NSString* filePath )
// ----------------------------------------------------------------------------------------------------------
{
int no_files =0;
NSFileManager *fileManager = [ NSFileManager defaultManager ];
NSDirectoryEnumerator *dirEnum = [fileManager enumeratorAtPath:filePath];
while (filePath = [dirEnum nextObject]) {
[dirEnum skipDescendents ]; // don't want children
no_files++;
}
return no_files;
}
// ----------------------------------------------------------------------------------------------------------
NSMutableString* int2BinString(int x)
// ----------------------------------------------------------------------------------------------------------
{
NSMutableString *returnString = [[ NSMutableString alloc ] init];
int hi, lo;
hi=(x>>8) & 0xff;
lo=x&0xff;
[ returnString appendString:byte2String(hi) ];
[ returnString appendString:byte2String(lo) ];
return [ returnString autorelease ];
}
// ----------------------------------------------------------------------------------------------------------
NSMutableString *byte2String(int x )
// ----------------------------------------------------------------------------------------------------------
{
NSMutableString *returnString = [[ NSMutableString alloc ]init];
int n;
for(n=0; n<8; n++)
{
if((x & 0x80) !=0)
{
[ returnString appendString:@"1"];
}
else
{
[ returnString appendString:@"0"];
}
x = x<<1;
}
return [returnString autorelease];
}
// ----------------------------------------------------------------------------------------------------------
NSMutableString *bin2perms(NSString *binaryValue)
// ----------------------------------------------------------------------------------------------------------
{
NSMutableString *returnString = [[ NSMutableString alloc ] init];
NSRange subStringRange;
subStringRange.length = 1;
NSString *replaceWithChar;
for (int n=0; n < [binaryValue length]; n++)
{
subStringRange.location = n;
// take the char
// if pos = 0, 3,6
if ( n == 0 || n == 3 || n ==6)
{
replaceWithChar = @"r";
}
if ( n == 1 || n == 4 || n ==7)
{
replaceWithChar = @"w";
}
if ( n == 2 || n == 5 || n ==7)
{
replaceWithChar = @"x";
}
if ( [[binaryValue substringWithRange:subStringRange ] isEqualToString:@"1" ] )
{
[ returnString appendString:replaceWithChar ];
}
else
{
[ returnString appendString:@"-" ];
}
}
return [ returnString autorelease ];
}

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

@@ -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"
@@ -39,9 +41,10 @@ void Platform_SetStatus( const char *status );
qboolean Platform_DebuggerPresent( void );
// legacy iOS port functions
#if TARGET_OS_IOS
#if XASH_IOS
int IOS_GetArgs( char ***argv );
const char *IOS_GetDocsDir( void );
const char *IOS_GetExecDir( void );
void IOS_LaunchDialog( void );
#endif // TARGET_OS_IOS
@@ -207,6 +210,16 @@ static inline void Sys_RestoreCrashHandler( void )
}
#endif
static inline qboolean Platform_LibraryExists( const char *name, qboolean gamedironly )
{
#if XASH_ANDROID
// sorry, unimplemented
return false;
#else
return g_fsapi.FileExists( name, gamedironly );
#endif
}
/*
==============================================================================

View File

@@ -36,7 +36,6 @@ GNU General Public License for more details.
#include "filesystem.h"
#include "server.h"
#include "platform/android/lib_android.h"
#include "platform/ios/lib_ios.h"
#ifdef XASH_NO_LIBDL
void *dlsym( void *handle, const char *symbol )

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

@@ -248,13 +248,6 @@ static void SDLash_ActiveEvent( int gain )
}
else
{
#if TARGET_OS_IPHONE
{
// Keep running if ftp server enabled
void IOS_StartBackgroundTask( void );
IOS_StartBackgroundTask();
}
#endif
host.status = HOST_NOFOCUS;
if( cls.key_dest == key_game )

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

@@ -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

@@ -19,11 +19,11 @@ GNU General Public License for more details.
#include <stdarg.h>
#include "com_image.h"
#include "vgui_api.h"
#include "const.h"
#include "com_model.h"
#include "cl_entity.h"
#include "render_api.h"
#include "triangleapi.h"
#include "const.h"
#include "cl_entity.h"
#include "com_model.h"
#include "studio.h"
#include "r_efx.h"
#include "filesystem.h"
@@ -62,7 +62,9 @@ GNU General Public License for more details.
// 11. Added size argument to Mod_ProcessRenderData
// 12. Added Image_CalcImageSize
// 13. Removed ignore_flags argument from GetCvarPointer
#define REF_API_VERSION 13
// 14. Removed reserved functions, they are leftover from RenderAPI
// Removed CL_AddCustomBeam, it's now handled through R_AddEntity
#define REF_API_VERSION 14
#define TF_SKY (TF_SKYSIDE|TF_NOMIPMAP|TF_ALLOW_NEAREST)
#define TF_FONT (TF_NOMIPMAP|TF_CLAMP|TF_ALLOW_NEAREST)
@@ -90,10 +92,10 @@ GNU General Public License for more details.
#define MODEL_CLIENT BIT( 30 ) // client sprite
// goes into world.flags
#define FWORLD_SKYSPHERE BIT( 0 )
#define FWORLD_CUSTOM_SKYBOX BIT( 1 )
#define FWORLD_WATERALPHA BIT( 2 )
#define FWORLD_HAS_DELUXEMAP BIT( 3 )
#define FWORLD_SKYSPHERE BIT( 0 )
#define FWORLD_CUSTOM_SKYBOX BIT( 1 )
#define FWORLD_WATERALPHA BIT( 2 )
#define FWORLD_HAS_DELUXEMAP BIT( 3 )
// special rendermode for screenfade modulate
// (probably will be expanded at some point)
@@ -499,6 +501,9 @@ typedef struct ref_api_s
// for abstracting the engine's rendering
ref_window_type_t (*R_GetWindowHandle)( void **handle, ref_window_type_t type );
// sprites
struct mspriteframe_s *(*R_GetSpriteFrame)( const struct model_s *pModel, int frame, float yaw );
} ref_api_t;
struct mip_s;
@@ -533,7 +538,6 @@ typedef struct ref_interface_s
void (*GL_SetRenderMode)( int renderMode );
qboolean (*R_AddEntity)( struct cl_entity_s *clent, int type );
void (*CL_AddCustomBeam)( cl_entity_t *pEnvBeam );
void (*R_ProcessEntData)( qboolean allocate, cl_entity_t *entities, unsigned int max_entities );
void (*R_Flush)( unsigned int flush_flags );
@@ -577,9 +581,6 @@ typedef struct ref_interface_s
void (*GL_SubdivideSurface)( model_t *mod, msurface_t *fa );
void (*CL_RunLightStyles)( lightstyle_t *ls );
// sprites
void (*R_GetSpriteParms)( int *frameWidth, int *frameHeight, int *numFrames, int currentFrame, const model_t *pSprite );
int (*R_GetSpriteTexture)( const model_t *m_pSpriteModel, int frame );
// model management
// flags ignored for everything except spritemodels
@@ -632,8 +633,6 @@ typedef struct ref_interface_s
void (*GL_TextureTarget)( unsigned int target ); // change texture unit mode without bind texture
void (*GL_TexCoordArrayMode)( unsigned int texmode );
void (*GL_UpdateTexSize)( int texnum, int width, int height, int depth ); // recalc statistics
void (*GL_Reserved0)( void ); // for potential interface expansion without broken compatibility
void (*GL_Reserved1)( void );
// Misc renderer functions
void (*GL_DrawParticles)( const struct ref_viewpass_s *rvp, qboolean trans_pass, float frametime );

View File

@@ -406,6 +406,7 @@ extern convar_t sv_wateralpha;
extern convar_t sv_wateramp;
extern convar_t sv_voiceenable;
extern convar_t sv_voicequality;
extern convar_t sv_voice_singleplayer;
extern convar_t sv_maxvelocity;
extern convar_t sv_stepsize;
extern convar_t sv_skyname;

View File

@@ -3571,7 +3571,16 @@ static void SV_ParseVoiceData( sv_client_t *cl, sizebuf_t *msg )
MSG_ReadBytes( msg, received, size );
if( !sv_voiceenable.value || svs.maxclients <= 1 || cl->state != cs_spawned )
if( !sv_voiceenable.value || cl->state != cs_spawned )
return;
if( svgame.physFuncs.pfnVoiceData != NULL )
{
if( svgame.physFuncs.pfnVoiceData( client, frames, size, loopback, received ))
return;
}
if( svs.maxclients <= 1 && sv_voice_singleplayer.value == 0.0f )
return;
for( i = 0; i < svs.maxclients; i++ )

View File

@@ -463,7 +463,7 @@ static void SV_CreateBaseline( void )
int delta_type;
int entnum;
if( svs.maxclients > 1 )
if( svs.maxclients > 1 || sv_voice_singleplayer.value )
SV_WriteVoiceCodec( &sv.signon );
if( FBitSet( host.features, ENGINE_QUAKE_COMPATIBLE ))

View File

@@ -124,6 +124,7 @@ static CVAR_DEFINE_AUTO( violence_agibs, "1", 0, "show alien gib entities" );
// voice chat
CVAR_DEFINE_AUTO( sv_voiceenable, "1", FCVAR_ARCHIVE|FCVAR_SERVER, "enable voice support" );
CVAR_DEFINE_AUTO( sv_voicequality, "3", FCVAR_ARCHIVE, "voice chat quality level, from 0 to 5, higher is better" );
CVAR_DEFINE_AUTO( sv_voice_singleplayer, "0", FCVAR_ARCHIVE|FCVAR_SERVER, "enable voice subsystem in singleplayer games (maxclients == 1)" );
// enttools
CVAR_DEFINE_AUTO( sv_enttools_enable, "0", FCVAR_ARCHIVE|FCVAR_PROTECTED, "enable powerful and dangerous entity tools" );
@@ -975,6 +976,7 @@ void SV_Init( void )
Cvar_RegisterVariable( &sv_voiceenable );
Cvar_RegisterVariable( &sv_voicequality );
Cvar_RegisterVariable( &sv_voice_singleplayer );
Cvar_RegisterVariable( &sv_trace_messages );
Cvar_RegisterVariable( &sv_enttools_enable );
Cvar_RegisterVariable( &sv_enttools_maxfire );

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;
}
}
}

91
ref/common/ref_common.h Normal file
View File

@@ -0,0 +1,91 @@
/*
ref_common.h - shared renderer code
Copyright (C) 2010 Uncle Mike
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
*/
#ifndef REF_COMMON_H
#define REF_COMMON_H
#include "xash3d_mathlib.h"
#include "ref_api.h"
//
// ref_context.c
//
extern ref_api_t gEngfuncs;
extern ref_globals_t *gpGlobals;
extern ref_client_t *gp_cl;
extern ref_host_t *gp_host;
extern const ref_interface_t gReffuncs;
DECLARE_ENGINE_SHARED_CVAR_LIST()
#define Assert( x ) if( !( x )) gEngfuncs.Host_Error( "assert failed at %s:%i\n", __FILE__, __LINE__ )
#define ENGINE_GET_PARM_ (*gEngfuncs.EngineGetParm)
#define ENGINE_GET_PARM( parm ) ENGINE_GET_PARM_( (parm), 0 )
extern uint16_t rtable[MOD_FRAMES][MOD_FRAMES];
void GL_InitRandomTable( void );
void _Mem_Free( void *data, const char *filename, int fileline );
void *_Mem_Alloc( poolhandle_t poolptr, size_t size, qboolean clear, const char *filename, int fileline )
ALLOC_CHECK( 2 ) MALLOC_LIKE( _Mem_Free, 1 ) WARN_UNUSED_RESULT;
void *_Mem_Realloc( poolhandle_t poolptr, void *memptr, size_t size, qboolean clear, const char *filename, int fileline )
ALLOC_CHECK( 3 ) WARN_UNUSED_RESULT;
#define Mem_Malloc( pool, size ) _Mem_Alloc( pool, size, false, __FILE__, __LINE__ )
#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_AllocPool( name ) gEngfuncs._Mem_AllocPool( name, __FILE__, __LINE__ )
#define Mem_FreePool( pool ) gEngfuncs._Mem_FreePool( pool, __FILE__, __LINE__ )
#define Mem_EmptyPool( pool ) gEngfuncs._Mem_EmptyPool( pool, __FILE__, __LINE__ )
extern dlight_t *gp_dlights;
extern int g_lightstylevalue[MAX_LIGHTSTYLES];
extern poolhandle_t r_temppool;
//
// ref_common cvars
//
extern convar_t r_dlight_virtual_radius;
extern convar_t r_lighting_extended;
//
// ref_math.c
//
void Matrix4x4_Concat( matrix4x4 out, const matrix4x4 in1, const matrix4x4 in2 );
void Matrix4x4_ConcatTranslate( matrix4x4 out, float x, float y, float z );
void Matrix4x4_ConcatRotate( matrix4x4 out, float angle, float x, float y, float z );
void Matrix4x4_CreateProjection( matrix4x4 out, float xMax, float xMin, float yMax, float yMin, float zNear, float zFar );
void Matrix4x4_CreateOrtho( matrix4x4 m, float xLeft, float xRight, float yBottom, float yTop, float zNear, float zFar );
void Matrix4x4_CreateModelview( matrix4x4 out );
void Matrix4x4_ToArrayFloatGL( const matrix4x4 in, float out[16] );
//
// ref_light.c
//
void CL_RunLightStyles( lightstyle_t *ls );
void R_PushDlightsForBmodel( model_t *model, int framecount, const matrix4x4 object_matrix );
int R_PushDlights( model_t *model, int framecount );
colorVec R_LightVec( const vec3_t start, const vec3_t end, vec3_t lspot, vec3_t lvec );
colorVec R_LightPoint( const vec3_t p0 );
void R_UpdateSurfaceCachedLight( msurface_t *surf );
//
// ref_image.c
//
byte *GL_ResampleTexture( const byte *source, int in_w, int in_h, int out_w, int out_h, qboolean isNormalMap );
#endif // REF_COMMON_H

81
ref/common/ref_context.c Normal file
View File

@@ -0,0 +1,81 @@
/*
ref_context.c - shared renderer context
Copyright (C) 2018 a1batross
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
*/
#include "ref_common.h"
#include "com_strings.h"
DEFINE_ENGINE_SHARED_CVAR_LIST()
ref_api_t gEngfuncs;
ref_globals_t *gpGlobals;
ref_client_t *gp_cl;
ref_host_t *gp_host;
uint16_t rtable[MOD_FRAMES][MOD_FRAMES];
dlight_t *gp_dlights;
int g_lightstylevalue[MAX_LIGHTSTYLES];
poolhandle_t r_temppool;
void _Mem_Free( void *data, const char *filename, int fileline )
{
gEngfuncs._Mem_Free( data, filename, fileline );
}
void *_Mem_Alloc( poolhandle_t poolptr, size_t size, qboolean clear, const char *filename, int fileline )
{
return gEngfuncs._Mem_Alloc( poolptr, size, clear, filename, fileline );
}
void *_Mem_Realloc( poolhandle_t poolptr, void *memptr, size_t size, qboolean clear, const char *filename, int fileline )
{
return gEngfuncs._Mem_Realloc( poolptr, memptr, size, clear, filename, fileline );
}
void GL_InitRandomTable( void )
{
int tu, tv;
for( tu = 0; tu < MOD_FRAMES; tu++ )
{
for( tv = 0; tv < MOD_FRAMES; tv++ )
{
rtable[tu][tv] = gEngfuncs.COM_RandomLong( 0, 0x7FFF );
}
}
gEngfuncs.COM_SetRandomSeed( 0 );
}
int EXPORT GetRefAPI( int version, ref_interface_t *funcs, ref_api_t *engfuncs, ref_globals_t *globals );
int EXPORT GetRefAPI( int version, ref_interface_t *funcs, ref_api_t *engfuncs, ref_globals_t *globals )
{
if( version != REF_API_VERSION )
return 0;
// fill in our callbacks
*funcs = gReffuncs;
gEngfuncs = *engfuncs;
gpGlobals = globals;
gp_cl = (ref_client_t *)ENGINE_GET_PARM( PARM_GET_CLIENT_PTR );
gp_host = (ref_host_t *)ENGINE_GET_PARM( PARM_GET_HOST_PTR );
gp_dlights = (dlight_t *)ENGINE_GET_PARM( PARM_GET_DLIGHTS_PTR );
RETRIEVE_ENGINE_SHARED_CVAR_LIST();
gEngfuncs.Cvar_RegisterVariable( &r_dlight_virtual_radius );
gEngfuncs.Cvar_RegisterVariable( &r_lighting_extended );
return REF_API_VERSION;
}

108
ref/common/ref_image.c Normal file
View File

@@ -0,0 +1,108 @@
/*
ref_image.c - shared image code
Copyright (C) 2010 Uncle Mike
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
*/
#include "ref_common.h"
#include "xash3d_mathlib.h"
/*
================
GL_ResampleTexture
Assume input buffer is RGBA
=================
*/
byte *GL_ResampleTexture( const byte *source, int inWidth, int inHeight, int outWidth, int outHeight, qboolean isNormalMap )
{
uint frac, fracStep;
uint *in = (uint *)source;
uint p1[0x1000], p2[0x1000];
byte *pix1, *pix2, *pix3, *pix4;
uint *out, *inRow1, *inRow2;
static byte *scaledImage = NULL; // pointer to a scaled image
vec3_t normal;
int i, x, y;
if( !source ) return NULL;
scaledImage = Mem_Realloc( r_temppool, scaledImage, outWidth * outHeight * 4 );
fracStep = inWidth * 0x10000 / outWidth;
out = (uint *)scaledImage;
frac = fracStep >> 2;
for( i = 0; i < outWidth; i++ )
{
p1[i] = 4 * (frac >> 16);
frac += fracStep;
}
frac = (fracStep >> 2) * 3;
for( i = 0; i < outWidth; i++ )
{
p2[i] = 4 * (frac >> 16);
frac += fracStep;
}
if( isNormalMap )
{
for( y = 0; y < outHeight; y++, out += outWidth )
{
inRow1 = in + inWidth * (int)(((float)y + 0.25f) * inHeight / outHeight);
inRow2 = in + inWidth * (int)(((float)y + 0.75f) * inHeight / outHeight);
for( x = 0; x < outWidth; x++ )
{
pix1 = (byte *)inRow1 + p1[x];
pix2 = (byte *)inRow1 + p2[x];
pix3 = (byte *)inRow2 + p1[x];
pix4 = (byte *)inRow2 + p2[x];
normal[0] = MAKE_SIGNED( pix1[0] ) + MAKE_SIGNED( pix2[0] ) + MAKE_SIGNED( pix3[0] ) + MAKE_SIGNED( pix4[0] );
normal[1] = MAKE_SIGNED( pix1[1] ) + MAKE_SIGNED( pix2[1] ) + MAKE_SIGNED( pix3[1] ) + MAKE_SIGNED( pix4[1] );
normal[2] = MAKE_SIGNED( pix1[2] ) + MAKE_SIGNED( pix2[2] ) + MAKE_SIGNED( pix3[2] ) + MAKE_SIGNED( pix4[2] );
if( !VectorNormalizeLength( normal ))
VectorSet( normal, 0.5f, 0.5f, 1.0f );
((byte *)(out+x))[0] = 128 + (byte)(127.0f * normal[0]);
((byte *)(out+x))[1] = 128 + (byte)(127.0f * normal[1]);
((byte *)(out+x))[2] = 128 + (byte)(127.0f * normal[2]);
((byte *)(out+x))[3] = 255;
}
}
}
else
{
for( y = 0; y < outHeight; y++, out += outWidth )
{
inRow1 = in + inWidth * (int)(((float)y + 0.25f) * inHeight / outHeight);
inRow2 = in + inWidth * (int)(((float)y + 0.75f) * inHeight / outHeight);
for( x = 0; x < outWidth; x++ )
{
pix1 = (byte *)inRow1 + p1[x];
pix2 = (byte *)inRow1 + p2[x];
pix3 = (byte *)inRow2 + p1[x];
pix4 = (byte *)inRow2 + p2[x];
((byte *)(out+x))[0] = (pix1[0] + pix2[0] + pix3[0] + pix4[0]) >> 2;
((byte *)(out+x))[1] = (pix1[1] + pix2[1] + pix3[1] + pix4[1]) >> 2;
((byte *)(out+x))[2] = (pix1[2] + pix2[2] + pix3[2] + pix4[2]) >> 2;
((byte *)(out+x))[3] = (pix1[3] + pix2[3] + pix3[3] + pix4[3]) >> 2;
}
}
}
return scaledImage;
}

View File

@@ -1,5 +1,5 @@
/*
gl_rlight.c - dynamic and static lights
ref_light.c - shared light code
Copyright (C) 2010 Uncle Mike
This program is free software: you can redistribute it and/or modify
@@ -13,19 +13,13 @@ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
*/
#include "gl_local.h"
#include "pm_local.h"
#include "studio.h"
#include "ref_common.h"
#include "xash3d_mathlib.h"
#include "ref_params.h"
#include "pm_local.h"
/*
=============================================================================
CVAR_DEFINE_AUTO( r_dlight_virtual_radius, "3", FCVAR_GLCONFIG, "increase dlight radius virtually by this amount" );
CVAR_DEFINE_AUTO( r_lighting_extended, "1", FCVAR_GLCONFIG, "allow to get lighting from world and bmodels" );
DYNAMIC LIGHTS
=============================================================================
*/
/*
==================
CL_RunLightStyles
@@ -34,16 +28,17 @@ CL_RunLightStyles
*/
void CL_RunLightStyles( lightstyle_t *ls )
{
const model_t *world = gp_cl->models[1];
int i;
float frametime = (gp_cl->time - gp_cl->oldtime);
float frametime = gp_cl->time - gp_cl->oldtime;
if( !WORLDMODEL )
if( !world )
return;
if( !WORLDMODEL->lightdata )
if( r_fullbright->value || !world->lightdata )
{
for( i = 0; i < MAX_LIGHTSTYLES; i++ )
tr.lightstylevalue[i] = 256 * 256;
g_lightstylevalue[i] = 256 * 256;
return;
}
@@ -59,13 +54,13 @@ void CL_RunLightStyles( lightstyle_t *ls )
if( !ls[i].length )
{
tr.lightstylevalue[i] = 256;
g_lightstylevalue[i] = 256;
continue;
}
else if( ls[i].length == 1 )
{
// single length style so don't bother interpolating
tr.lightstylevalue[i] = ( ls[i].map[0] ) * 22;
g_lightstylevalue[i] = ls[i].map[0] * 22;
continue;
}
@@ -73,7 +68,7 @@ void CL_RunLightStyles( lightstyle_t *ls )
if( !ls[i].interp || !cl_lightstyle_lerping->value )
{
tr.lightstylevalue[i] = ls[i].map[flight%ls[i].length] * 22;
g_lightstylevalue[i] = ls[i].map[flight % ls[i].length] * 22;
continue;
}
@@ -90,7 +85,7 @@ void CL_RunLightStyles( lightstyle_t *ls )
k = ls[i].map[clight % ls[i].length];
l += (float)( k * 22.0f ) * lerpfrac;
tr.lightstylevalue[i] = (int)l;
g_lightstylevalue[i] = (int)l;
}
}
@@ -99,45 +94,39 @@ void CL_RunLightStyles( lightstyle_t *ls )
R_MarkLights
=============
*/
void R_MarkLights( const dlight_t *light, int bit, const mnode_t *node )
static void R_MarkLights( const dlight_t *light, int bit, const mnode_t *node, model_t *model, int dlightframecount )
{
const float virtual_radius = light->radius * Q_max( 1.0f, r_dlight_virtual_radius.value );
const float maxdist = light->radius * light->radius;
float dist;
int i;
mnode_t *children[2];
int firstsurface, numsurfaces;
start:
if( !node || node->contents < 0 )
return;
dist = PlaneDiff( light->origin, node->plane );
node_children( children, node, RI.currentmodel );
float dist = PlaneDiff( light->origin, node->plane );
if( dist > virtual_radius )
{
node = children[0];
node = node_child( node, 0, model );
goto start;
}
if( dist < -virtual_radius )
{
node = children[1];
node = node_child( node, 1, model );
goto start;
}
// mark the polygons
firstsurface = node_firstsurface( node, RI.currentmodel );
numsurfaces = node_numsurfaces( node, RI.currentmodel );
const float dist_sq = dist * dist;
for( i = 0; i < numsurfaces; i++ )
// mark the polygons
int firstsurface = node_firstsurface( node, model );
int numsurfaces = node_numsurfaces( node, model );
for( int i = 0; i < numsurfaces && dist_sq < maxdist; i++ )
{
vec3_t impact;
float s, t, l;
msurface_t *surf = &RI.currentmodel->surfaces[firstsurface + i];
msurface_t *surf = &model->surfaces[firstsurface + i];
const mextrasurf_t *info = surf->info;
if( surf->plane->type < 3 )
@@ -160,19 +149,20 @@ 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 )
if( surf->dlightframe != dlightframecount )
{
surf->dlightbits = bit;
surf->dlightframe = tr.dlightframecount;
surf->dlightframe = dlightframecount;
}
else surf->dlightbits |= bit;
}
R_MarkLights( light, bit, children[0] );
R_MarkLights( light, bit, children[1] );
R_MarkLights( light, bit, node_child( node, 0, model ), model, dlightframecount );
node = node_child( node, 1, model );
goto start;
}
/*
@@ -180,31 +170,40 @@ start:
R_PushDlights
=============
*/
void R_PushDlights( void )
int R_PushDlights( model_t *model, int framecount )
{
int i;
if( !model )
return framecount;
tr.dlightframecount = tr.framecount;
RI.currententity = CL_GetEntityByIndex( 0 );
// no world -- no dlights
if( !RI.currententity )
return;
RI.currentmodel = RI.currententity->model;
for( i = 0; i < MAX_DLIGHTS; i++ )
for( int i = 0; i < MAX_DLIGHTS; i++ )
{
dlight_t *l = &tr.dlights[i];
const dlight_t *l = &gp_dlights[i];
if( l->die < gp_cl->time || !l->radius )
continue;
if( GL_FrustumCullSphere( &RI.frustum, l->origin, l->radius, 15 ))
R_MarkLights( l, 1 << i, model->nodes, model, framecount );
}
return framecount;
}
void R_PushDlightsForBmodel( model_t *model, int framecount, const matrix4x4 object_matrix )
{
for( int i = 0; i < MAX_DLIGHTS; i++ )
{
dlight_t *l = &gp_dlights[i];
if( l->die < gp_cl->time || !l->radius )
continue;
R_MarkLights( l, 1<<i, RI.currentmodel->nodes );
vec3_t oldorigin;
VectorCopy( l->origin, oldorigin );
Matrix4x4_VectorITransform( object_matrix, oldorigin, l->origin );
R_MarkLights( l, 1 << i, model->nodes + model->hulls[0].firstclipnode, model, framecount );
VectorCopy( oldorigin, l->origin );
}
}
@@ -226,19 +225,7 @@ R_RecursiveLightPoint
*/
static qboolean R_RecursiveLightPoint( model_t *model, mnode_t *node, float p1f, float p2f, colorVec *cv, const vec3_t start, const vec3_t end )
{
float front, back, frac, midf;
int i, map, side, size;
float ds, dt, s, t;
int sample_size;
color24 *lm, *dm;
mextrasurf_t *info;
msurface_t *surf;
mtexinfo_t *tex;
matrix3x4 tbn;
vec3_t mid;
mnode_t *children[2];
int firstsurface, numsurfaces;
start:
// didn't hit anything
if( !node || node->contents < 0 )
{
@@ -246,25 +233,26 @@ 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 );
// calculate mid point
front = PlaneDiff( start, node->plane );
back = PlaneDiff( end, node->plane );
float front = PlaneDiff( start, node->plane );
float back = PlaneDiff( end, node->plane );
side = front < 0;
int side = front < 0;
if(( back < 0 ) == side )
return R_RecursiveLightPoint( model, children[side], p1f, p2f, cv, start, end );
{
node = node_child( node, side, model );
goto start;
}
frac = front / ( front - back );
float frac = front / ( front - back );
vec3_t mid;
VectorLerp( start, frac, end, mid );
midf = p1f + ( p2f - p1f ) * frac;
float 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 )
@@ -274,27 +262,27 @@ static qboolean R_RecursiveLightPoint( model_t *model, mnode_t *node, float p1f,
}
// check for impact on this node
surf = model->surfaces + firstsurface;
int firstsurface = node_firstsurface( node, model );
int numsurfaces = node_numsurfaces( node, model );
VectorCopy( mid, g_trace_lightspot );
for( i = 0; i < numsurfaces; i++, surf++ )
for( int i = 0; i < numsurfaces; i++ )
{
int smax, tmax;
tex = surf->texinfo;
info = surf->info;
const msurface_t *surf = &model->surfaces[firstsurface + i];
const mextrasurf_t *info = surf->info;
if( FBitSet( surf->flags, SURF_DRAWTILED ))
continue; // no lightmaps
s = DotProduct( mid, info->lmvecs[0] ) + info->lmvecs[0][3];
t = DotProduct( mid, info->lmvecs[1] ) + info->lmvecs[1][3];
float s = DotProduct( mid, info->lmvecs[0] ) + info->lmvecs[0][3];
float t = DotProduct( mid, info->lmvecs[1] ) + info->lmvecs[1][3];
if( s < info->lightmapmins[0] || t < info->lightmapmins[1] )
continue;
ds = s - info->lightmapmins[0];
dt = t - info->lightmapmins[1];
float ds = s - info->lightmapmins[0];
float dt = t - info->lightmapmins[1];
if ( ds > info->lightextents[0] || dt > info->lightextents[1] )
continue;
@@ -304,21 +292,25 @@ static qboolean R_RecursiveLightPoint( model_t *model, mnode_t *node, float p1f,
if( !surf->samples )
return true;
sample_size = gEngfuncs.Mod_SampleSizeForFace( surf );
smax = (info->lightextents[0] / sample_size) + 1;
tmax = (info->lightextents[1] / sample_size) + 1;
int sample_size = gEngfuncs.Mod_SampleSizeForFace( surf );
int smax = (info->lightextents[0] / sample_size) + 1;
int tmax = (info->lightextents[1] / sample_size) + 1;
ds /= sample_size;
dt /= sample_size;
lm = surf->samples + Q_rint( dt ) * smax + Q_rint( ds );
g_trace_fraction = midf;
size = smax * tmax;
dm = NULL;
const color24 *lm = surf->samples + Q_rint( dt ) * smax + Q_rint( ds );
const color24 *dm = NULL;
matrix3x4 tbn;
if( surf->info->deluxemap )
{
vec3_t faceNormal;
dm = surf->info->deluxemap + Q_rint( dt ) * smax + Q_rint( ds );
if( FBitSet( surf->flags, SURF_PLANEBACK ))
VectorNegate( surf->plane->normal, faceNormal );
else VectorCopy( surf->plane->normal, faceNormal );
@@ -336,12 +328,13 @@ static qboolean R_RecursiveLightPoint( model_t *model, mnode_t *node, float p1f,
VectorNormalize( tbn[0] );
VectorNormalize( tbn[1] );
VectorNormalize( tbn[2] );
dm = surf->info->deluxemap + Q_rint( dt ) * smax + Q_rint( ds );
}
for( map = 0; map < MAXLIGHTMAPS && surf->styles[map] != 255; map++ )
int size = smax * tmax;
for( int map = 0; map < MAXLIGHTMAPS && surf->styles[map] != 255; map++ )
{
uint scale = tr.lightstylevalue[surf->styles[map]];
uint scale = g_lightstylevalue[surf->styles[map]];
cv->r += lm->r * scale;
cv->g += lm->g * scale;
@@ -366,88 +359,80 @@ 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 );
}
/*
=================
R_LightVec
R_LightVecInternal
check bspmodels to get light from
=================
*/
static colorVec R_LightVecInternal( const vec3_t start, const vec3_t end, vec3_t lspot, vec3_t lvec )
{
float last_fraction;
int i, maxEnts = 1;
colorVec light, cv;
if( lspot )
VectorClear( lspot );
if( lspot ) VectorClear( lspot );
if( lvec ) VectorClear( lvec );
if( lvec )
VectorClear( lvec );
if( WORLDMODEL && WORLDMODEL->lightdata )
if( !gp_cl->models[1] || !gp_cl->models[1]->lightdata )
return (colorVec){ 255, 255, 255, 0 };
float last_fraction = 1.0f;
int max_ents = r_lighting_extended.value ? MAX_PHYSENTS : 1; // get light from bmodels too
colorVec light = { 0 };
// check all the bsp-models
for( int i = 0; i < max_ents; i++ )
{
light.r = light.g = light.b = light.a = 0;
last_fraction = 1.0f;
const physent_t *pe = gEngfuncs.EV_GetPhysent( i );
// get light from bmodels too
if( r_lighting_extended.value )
maxEnts = MAX_PHYSENTS;
if( !pe )
break;
// check all the bsp-models
for( i = 0; i < maxEnts; i++ )
if( !pe->model || pe->model->type != mod_brush )
continue; // skip non-bsp models
mnode_t *pnodes = &pe->model->nodes[pe->model->hulls[0].firstclipnode];
vec3_t offset, start_l, end_l;
VectorSubtract( pe->model->hulls[0].clip_mins, vec3_origin, offset );
VectorAdd( offset, pe->origin, offset );
VectorSubtract( start, offset, start_l );
VectorSubtract( end, offset, end_l );
// rotate start and end into the models frame of reference
if( !VectorIsNull( pe->angles ))
{
physent_t *pe = gEngfuncs.EV_GetPhysent( i );
vec3_t offset, start_l, end_l;
mnode_t *pnodes;
matrix4x4 matrix;
if( !pe )
break;
if( !pe->model || pe->model->type != mod_brush )
continue; // skip non-bsp models
pnodes = &pe->model->nodes[pe->model->hulls[0].firstclipnode];
VectorSubtract( pe->model->hulls[0].clip_mins, vec3_origin, offset );
VectorAdd( offset, pe->origin, offset );
VectorSubtract( start, offset, start_l );
VectorSubtract( end, offset, end_l );
// rotate start and end into the models frame of reference
if( !VectorIsNull( pe->angles ))
{
Matrix4x4_CreateFromEntity( matrix, pe->angles, offset, 1.0f );
Matrix4x4_VectorITransform( matrix, start, start_l );
Matrix4x4_VectorITransform( matrix, end, end_l );
}
VectorClear( g_trace_lightspot );
VectorClear( g_trace_lightvec );
g_trace_fraction = 1.0f;
if( !R_RecursiveLightPoint( pe->model, pnodes, 0.0f, 1.0f, &cv, start_l, end_l ))
continue; // didn't hit anything
if( g_trace_fraction < last_fraction )
{
if( lspot ) VectorCopy( g_trace_lightspot, lspot );
if( lvec ) VectorNormalize2( g_trace_lightvec, lvec );
light.r = Q_min(( cv.r >> 8 ), 255 );
light.g = Q_min(( cv.g >> 8 ), 255 );
light.b = Q_min(( cv.b >> 8 ), 255 );
last_fraction = g_trace_fraction;
if(( light.r + light.g + light.b ) != 0 )
break; // we get light now
}
matrix4x4 matrix;
Matrix4x4_CreateFromEntity( matrix, pe->angles, offset, 1.0f );
Matrix4x4_VectorITransform( matrix, start, start_l );
Matrix4x4_VectorITransform( matrix, end, end_l );
}
VectorClear( g_trace_lightspot );
VectorClear( g_trace_lightvec );
g_trace_fraction = 1.0f;
colorVec cv;
if( !R_RecursiveLightPoint( pe->model, pnodes, 0.0f, 1.0f, &cv, start_l, end_l ))
continue; // didn't hit anything
if( g_trace_fraction < last_fraction )
{
if( lspot ) VectorCopy( g_trace_lightspot, lspot );
if( lvec ) VectorNormalize2( g_trace_lightvec, lvec );
light.r = Q_min(( cv.r >> 8 ), 255 );
light.g = Q_min(( cv.g >> 8 ), 255 );
light.b = Q_min(( cv.b >> 8 ), 255 );
last_fraction = g_trace_fraction;
if(( light.r + light.g + light.b ) != 0 )
break; // we get light now
}
}
else
{
light.r = light.g = light.b = 255;
light.a = 0;
}
return light;
@@ -462,7 +447,7 @@ check bspmodels to get light from
*/
colorVec R_LightVec( const vec3_t start, const vec3_t end, vec3_t lspot, vec3_t lvec )
{
colorVec light = R_LightVecInternal( start, end, lspot, lvec );
colorVec light = R_LightVecInternal( start, end, lspot, lvec );
if( r_lighting_extended.value && lspot != NULL && lvec != NULL )
{
@@ -483,9 +468,20 @@ light from floor
*/
colorVec R_LightPoint( const vec3_t p0 )
{
vec3_t p1;
vec3_t p1;
VectorSet( p1, p0[0], p0[1], p0[2] - 2048.0f );
return R_LightVec( p0, p1, NULL, NULL );
}
/*
================
R_SetCacheState
================
*/
void R_UpdateSurfaceCachedLight( msurface_t *surf )
{
for( int i = 0; i < MAXLIGHTMAPS && surf->styles[i] != 255; i++ )
surf->cached_light[i] = g_lightstylevalue[surf->styles[i]];
}

View File

@@ -1,5 +1,5 @@
/*
gl_rmath.c - renderer mathlib
ref_math.c - renderer mathlib
Copyright (C) 2010 Uncle Mike
This program is free software: you can redistribute it and/or modify
@@ -13,8 +13,7 @@ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
*/
#include "r_local.h"
#include "xash3d_mathlib.h"
#include "ref_common.h"
/*
========================================================================
@@ -99,7 +98,27 @@ void Matrix4x4_CreateModelview( matrix4x4 out )
out[1][2] = 1.0f;
}
void Matrix4x4_CreateTranslate( matrix4x4 out, float x, float y, float z )
void Matrix4x4_ToArrayFloatGL( const matrix4x4 in, float out[16] )
{
out[ 0] = in[0][0];
out[ 1] = in[1][0];
out[ 2] = in[2][0];
out[ 3] = in[3][0];
out[ 4] = in[0][1];
out[ 5] = in[1][1];
out[ 6] = in[2][1];
out[ 7] = in[3][1];
out[ 8] = in[0][2];
out[ 9] = in[1][2];
out[10] = in[2][2];
out[11] = in[3][2];
out[12] = in[0][3];
out[13] = in[1][3];
out[14] = in[2][3];
out[15] = in[3][3];
}
static void Matrix4x4_CreateTranslate( matrix4x4 out, float x, float y, float z )
{
out[0][0] = 1.0f;
out[0][1] = 0.0f;
@@ -119,7 +138,7 @@ void Matrix4x4_CreateTranslate( matrix4x4 out, float x, float y, float z )
out[3][3] = 1.0f;
}
void Matrix4x4_CreateRotate( matrix4x4 out, float angle, float x, float y, float z )
static void Matrix4x4_CreateRotate( matrix4x4 out, float angle, float x, float y, float z )
{
float len, c, s;

27
ref/common/wscript Normal file
View File

@@ -0,0 +1,27 @@
#! /usr/bin/env python
# encoding: utf-8
def options(opt):
pass
def configure(conf):
pass
def build(bld):
use = ['engine_includes', 'sdk_includes', 'werror']
# FIXME: figure out why psvita needs it and how can we avoid it
if bld.env.DEST_OS == 'psvita':
bld.env.CFLAGS_cstlib.remove('-fno-PIC')
bld.env.CFLAGS += ['-fPIC']
else:
use += ['public']
bld.stlib(source = bld.path.ant_glob('*.c'),
target = 'ref_common',
includes = '.',
export_includes = '.',
defines = 'REF_DLL=1',
use = use,
subsystem = bld.env.MSVC_SUBSYSTEM
)

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;
@@ -687,7 +684,7 @@ static void R_AliasDynamicLight( cl_entity_t *ent, alight_t *plight )
for( lnum = 0; lnum < MAX_DLIGHTS; lnum++ )
{
const dlight_t *dl = &tr.dlights[lnum];
const dlight_t *dl = &gp_dlights[lnum];
if( dl->die < g_alias.time || !r_dynamic->value )
continue;

View File

@@ -151,28 +151,6 @@ qboolean R_BeamCull( const vec3_t start, const vec3_t end, qboolean pvsOnly )
return true;
}
/*
================
CL_AddCustomBeam
Add the beam that encoded as custom entity
================
*/
void CL_AddCustomBeam( cl_entity_t *pEnvBeam )
{
if( tr.draw_list->num_beam_entities >= MAX_VISIBLE_PACKET )
{
gEngfuncs.Con_Printf( S_ERROR "Too many beams %d!\n", tr.draw_list->num_beam_entities );
return;
}
if( pEnvBeam )
{
tr.draw_list->beam_entities[tr.draw_list->num_beam_entities] = pEnvBeam;
tr.draw_list->num_beam_entities++;
}
}
/*
==============================================================

View File

@@ -22,20 +22,7 @@ GNU General Public License for more details.
#include "gl4es/include/gl4esinit.h"
#endif
ref_api_t gEngfuncs;
ref_globals_t *gpGlobals;
ref_client_t *gp_cl;
ref_host_t *gp_host;
void _Mem_Free( void *data, const char *filename, int fileline )
{
gEngfuncs._Mem_Free( data, filename, fileline );
}
void *_Mem_Alloc( poolhandle_t poolptr, size_t size, qboolean clear, const char *filename, int fileline )
{
return gEngfuncs._Mem_Alloc( poolptr, size, clear, filename, fileline );
}
static void R_ClearScreen( void )
{
@@ -132,7 +119,6 @@ static void Mod_UnloadTextures( model_t *mod )
Mod_BrushUnloadTextures( mod );
break;
case mod_sprite:
Mod_SpriteUnloadTextures( mod->cache.data );
break;
default:
Assert( 0 );
@@ -159,7 +145,7 @@ static qboolean Mod_ProcessRenderData( model_t *mod, qboolean create, const byte
loaded = true;
break;
case mod_sprite:
Mod_LoadSpriteModel( mod, buf, &loaded, mod->numtexinfo );
loaded = true;
break;
case mod_alias:
Mod_LoadAliasModel( mod, buf, &loaded );
@@ -228,7 +214,7 @@ static int GL_RefGetParm( int parm, int arg )
return glState.activeTMU;
case PARM_LIGHTSTYLEVALUE:
arg = bound( 0, arg, MAX_LIGHTSTYLES - 1 );
return tr.lightstylevalue[arg];
return g_lightstylevalue[arg];
case PARM_MAX_IMAGE_UNITS:
return GL_MaxTextureUnits();
case PARM_REBUILD_GAMMA:
@@ -427,7 +413,7 @@ static const char *R_GetConfigName( void )
return "opengl";
}
static const ref_interface_t gReffuncs =
const ref_interface_t gReffuncs =
{
R_Init,
R_Shutdown,
@@ -452,7 +438,6 @@ static const ref_interface_t gReffuncs =
GL_SetRenderMode,
R_AddEntity,
CL_AddCustomBeam,
R_ProcessEntData,
R_Flush,
@@ -487,8 +472,6 @@ static const ref_interface_t gReffuncs =
GL_SubdivideSurface,
CL_RunLightStyles,
R_GetSpriteParms,
R_GetSpriteTexture,
Mod_ProcessRenderData,
Mod_StudioLoadTextures,
@@ -531,8 +514,6 @@ static const ref_interface_t gReffuncs =
GL_TextureTarget,
GL_SetTexCoordArrayMode,
GL_UpdateTexSize,
NULL,
NULL,
CL_DrawParticlesExternal,
R_LightVec,
@@ -564,19 +545,3 @@ static const ref_interface_t gReffuncs =
VGUI_UploadTextureBlock,
};
int EXPORT GetRefAPI( int version, ref_interface_t *funcs, ref_api_t *engfuncs, ref_globals_t *globals );
int EXPORT GetRefAPI( int version, ref_interface_t *funcs, ref_api_t *engfuncs, ref_globals_t *globals )
{
if( version != REF_API_VERSION )
return 0;
// fill in our callbacks
*funcs = gReffuncs;
gEngfuncs = *engfuncs;
gpGlobals = globals;
gp_cl = (ref_client_t *)ENGINE_GET_PARM( PARM_GET_CLIENT_PTR );
gp_host = (ref_host_t *)ENGINE_GET_PARM( PARM_GET_HOST_PTR );
return REF_API_VERSION;
}

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 );
}
}
@@ -880,6 +868,10 @@ void DrawSingleDecal( decal_t *pDecal, msurface_t *fa )
GL_Bind( XASH_TEXTURE0, pDecal->texture );
if( FBitSet( R_GetTexture( pDecal->texture )->flags, TF_PREMULTIPLIED ))
pglBlendFunc( GL_ONE, GL_ONE_MINUS_SRC_ALPHA );
else pglBlendFunc( GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA );
pglBegin( GL_POLYGON );
for( i = 0; i < numVerts; i++, v += VERTEXSIZE )

View File

@@ -29,34 +29,6 @@ void R_GetTextureParms( int *w, int *h, int texnum )
if( h ) *h = glt->srcHeight;
}
/*
=============
R_GetSpriteParms
same as GetImageParms but used
for sprite models
=============
*/
void R_GetSpriteParms( int *frameWidth, int *frameHeight, int *numFrames, int currentFrame, const model_t *pSprite )
{
mspriteframe_t *pFrame;
if( !pSprite || pSprite->type != mod_sprite ) return; // bad model ?
pFrame = R_GetSpriteFrame( pSprite, currentFrame, 0.0f );
if( frameWidth ) *frameWidth = pFrame->width;
if( frameHeight ) *frameHeight = pFrame->height;
if( numFrames ) *numFrames = pSprite->numframes;
}
int R_GetSpriteTexture( const model_t *m_pSpriteModel, int frame )
{
if( !m_pSpriteModel || m_pSpriteModel->type != mod_sprite || !m_pSpriteModel->cache.data )
return 0;
return R_GetSpriteFrame( m_pSpriteModel, frame, 0.0f )->gl_texturenum;
}
/*
=============
R_DrawStretchPic

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];
}
@@ -686,97 +691,6 @@ static void GL_SetTextureFormat( gl_texture_t *tex, pixformat_t format, int chan
}
}
/*
=================
GL_ResampleTexture
Assume input buffer is RGBA
=================
*/
byte *GL_ResampleTexture( const byte *source, int inWidth, int inHeight, int outWidth, int outHeight, qboolean isNormalMap )
{
uint frac, fracStep;
uint *in = (uint *)source;
uint p1[0x1000], p2[0x1000];
byte *pix1, *pix2, *pix3, *pix4;
uint *out, *inRow1, *inRow2;
static byte *scaledImage = NULL; // pointer to a scaled image
vec3_t normal;
int i, x, y;
if( !source ) return NULL;
scaledImage = Mem_Realloc( r_temppool, scaledImage, outWidth * outHeight * 4 );
fracStep = inWidth * 0x10000 / outWidth;
out = (uint *)scaledImage;
frac = fracStep >> 2;
for( i = 0; i < outWidth; i++ )
{
p1[i] = 4 * (frac >> 16);
frac += fracStep;
}
frac = (fracStep >> 2) * 3;
for( i = 0; i < outWidth; i++ )
{
p2[i] = 4 * (frac >> 16);
frac += fracStep;
}
if( isNormalMap )
{
for( y = 0; y < outHeight; y++, out += outWidth )
{
inRow1 = in + inWidth * (int)(((float)y + 0.25f) * inHeight / outHeight);
inRow2 = in + inWidth * (int)(((float)y + 0.75f) * inHeight / outHeight);
for( x = 0; x < outWidth; x++ )
{
pix1 = (byte *)inRow1 + p1[x];
pix2 = (byte *)inRow1 + p2[x];
pix3 = (byte *)inRow2 + p1[x];
pix4 = (byte *)inRow2 + p2[x];
normal[0] = MAKE_SIGNED( pix1[0] ) + MAKE_SIGNED( pix2[0] ) + MAKE_SIGNED( pix3[0] ) + MAKE_SIGNED( pix4[0] );
normal[1] = MAKE_SIGNED( pix1[1] ) + MAKE_SIGNED( pix2[1] ) + MAKE_SIGNED( pix3[1] ) + MAKE_SIGNED( pix4[1] );
normal[2] = MAKE_SIGNED( pix1[2] ) + MAKE_SIGNED( pix2[2] ) + MAKE_SIGNED( pix3[2] ) + MAKE_SIGNED( pix4[2] );
if( !VectorNormalizeLength( normal ))
VectorSet( normal, 0.5f, 0.5f, 1.0f );
((byte *)(out+x))[0] = 128 + (byte)(127.0f * normal[0]);
((byte *)(out+x))[1] = 128 + (byte)(127.0f * normal[1]);
((byte *)(out+x))[2] = 128 + (byte)(127.0f * normal[2]);
((byte *)(out+x))[3] = 255;
}
}
}
else
{
for( y = 0; y < outHeight; y++, out += outWidth )
{
inRow1 = in + inWidth * (int)(((float)y + 0.25f) * inHeight / outHeight);
inRow2 = in + inWidth * (int)(((float)y + 0.75f) * inHeight / outHeight);
for( x = 0; x < outWidth; x++ )
{
pix1 = (byte *)inRow1 + p1[x];
pix2 = (byte *)inRow1 + p2[x];
pix3 = (byte *)inRow2 + p1[x];
pix4 = (byte *)inRow2 + p2[x];
((byte *)(out+x))[0] = (pix1[0] + pix2[0] + pix3[0] + pix4[0]) >> 2;
((byte *)(out+x))[1] = (pix1[1] + pix2[1] + pix3[1] + pix4[1]) >> 2;
((byte *)(out+x))[2] = (pix1[2] + pix2[2] + pix3[2] + pix4[2]) >> 2;
((byte *)(out+x))[3] = (pix1[3] + pix2[3] + pix3[3] + pix4[3]) >> 2;
}
}
}
return scaledImage;
}
/*
=================
GL_BoxFilter3x3
@@ -1233,6 +1147,8 @@ static void GL_ProcessImage( gl_texture_t *tex, rgbdata_t *pic )
// force upload texture as RGB or RGBA (detail textures requires this)
if( tex->flags & TF_FORCE_COLOR ) pic->flags |= IMAGE_HAS_COLOR;
if( pic->flags & IMAGE_HAS_ALPHA ) tex->flags |= TF_HAS_ALPHA;
if( FBitSet( pic->flags, IMAGE_PREMULTIPLIED ))
SetBits( tex->flags, TF_PREMULTIPLIED );
tex->encode = pic->encode; // share encode method
@@ -1270,6 +1186,23 @@ static void GL_ProcessImage( gl_texture_t *tex, rgbdata_t *pic )
// processing image before uploading (force to rgba, make luma etc)
if( pic->buffer ) gEngfuncs.Image_Process( &pic, 0, 0, img_flags, 0 );
if( FBitSet( pic->flags, IMAGE_PLAYERDECAL ) && pic->buffer && pic->type == PF_RGBA_32 && FBitSet( pic->flags, IMAGE_HAS_ALPHA )
&& !FBitSet( pic->flags, IMAGE_PREMULTIPLIED ))
{
int i, cnt = (int)( pic->width * pic->height );
byte *p = pic->buffer;
for( i = 0; i < cnt; i++, p += 4 )
{
int a = p[3];
p[0] = ( p[0] * a + 127 ) / 255;
p[1] = ( p[1] * a + 127 ) / 255;
p[2] = ( p[2] * a + 127 ) / 255;
}
SetBits( pic->flags, IMAGE_PREMULTIPLIED );
SetBits( tex->flags, TF_PREMULTIPLIED );
}
if( FBitSet( tex->flags, TF_LUMINANCE ))
ClearBits( pic->flags, IMAGE_HAS_COLOR );
}

Some files were not shown because too many files have changed in this diff Show More