Compare commits

..

201 Commits

Author SHA1 Message Date
Alibek Omarov
5b6b372cfe ref: gl: drop broken minlight/scale cvars for litwater. Keep them for mods. Fix post-merge issues 2026-04-28 11:21:56 +05:00
Alibek Omarov
2f31cf315c ref: gl: add litwater support 2026-04-28 11:12:44 +05:00
Alibek Omarov
910cce56ca Documentation: extensions: document lightmapped water extension 2026-04-28 11:12:11 +05:00
Alibek Omarov
68dba8b4e0 engine: add litwater support 2026-04-28 11:12:11 +05:00
Alibek Omarov
dbfeacfecc 3rdparty: vgui_support: update 2026-04-28 09:50:50 +05:00
Alibek Omarov
0bdd4648bc utils: mdldec: explicitly cast chars to bytes in ctype functions 2026-04-28 09:50:33 +05:00
Alibek Omarov
ec8059173d public: explicitly cast chars to bytes in ctype functions 2026-04-28 09:50:33 +05:00
Alibek Omarov
f91e5a7647 engine: explicitly cast chars to bytes in ctype functions 2026-04-28 09:50:33 +05:00
Alibek Omarov
7942d9b1bc engine: mod_bmodel: refactor unified bmodel loader
* Use offsets into dbspmodel_t struct, get rid of srcmodel global variable.
* Allocate dbspmodel_t when it's required
* Make srclumps const, move Blue-Shift hack into lump loader's flag.
2026-04-26 06:29:04 +05:00
Alibek Omarov
7abf0c819f 3rdparty: update xash-extras and mainui 2026-04-26 05:52:30 +05:00
Alibek Omarov
85eb82a370 Revert "engine: client: show difference between demo time and real time in timedemo result"
This reverts commit 90e25cc623.
2026-04-26 04:26:50 +05:00
Alibek Omarov
90e25cc623 engine: client: show difference between demo time and real time in timedemo result 2026-04-26 04:15:15 +05:00
Alibek Omarov
fca4152d5a ref: gl: fix typo in gl_drawrangeelements 2026-04-26 04:13:50 +05:00
Alibek Omarov
2ccef63754 engine: ref_api: change RefGetParm return type to intptr_t, bump RefAPI version 2026-04-26 04:12:52 +05:00
Alibek Omarov
87615784b9 engine: client: fix server list response parsing on BE 2026-04-26 04:12:52 +05:00
Alibek Omarov
d3b01d313b ref: gl: do not limit texture units by broken report of shaders extension 2026-04-26 04:12:52 +05:00
Alibek Omarov
a3897f402a 3rdparty: mainui: update 2026-04-26 04:12:52 +05:00
Alibek Omarov
21d84ee692 ref: gl: add -glnoerr flag that enables no error context, for possible performance gains 2026-04-26 04:12:52 +05:00
Alibek Omarov
e30ce9c150 engine: fix palette and builtin textures on BE 2026-04-26 04:12:52 +05:00
Alibek Omarov
22391eba1b engine: handle swapping sequence files in LoadCacheFile for games with external StudioModelRenderer 2026-04-26 04:12:52 +05:00
Alibek Omarov
800725453a engine: soundlib: pass XASH_BIG_ENDIAN to ov_read in vorbis loader 2026-04-26 04:12:52 +05:00
Alibek Omarov
22ecec095e engine: platform: sdl: request AUDIO_S16SYS to make sound output in native endian 2026-04-26 04:12:52 +05:00
Alibek Omarov
8545ed5ccb engine: mod_studio: implement loading sequence files on BE 2026-04-26 04:12:52 +05:00
Alibek Omarov
a4fdf19ad8 engine: client: Fix font loading on BE 2026-04-26 04:12:52 +05:00
Alibek Omarov
75e4a21d7a engine: mod_studio: more studio model swapping 2026-04-26 04:12:52 +05:00
Alibek Omarov
1099344a7d engine: soundlib: swap WAV data 2026-04-26 04:12:52 +05:00
Alibek Omarov
ab1eaed0a5 engine: platform: posix: remove Sys_Crash and Sys_CrashDetailsLibbacktrace from the output by matching function name only once 2026-04-26 04:12:52 +05:00
Alibek Omarov
76a01d0855 engine: platform: posix: remove extra newline from libbacktrace output 2026-04-26 04:12:52 +05:00
Alibek Omarov
b00679ee30 engine: make Sys_LogFileNo return -1 when log file isn't opened 2026-04-26 04:12:52 +05:00
Alibek Omarov
b3bb2fc16f engine: imagelib: add IL_HOST_ENDIAN flag to mark on-disk data that's already in host endianness 2026-04-26 04:12:52 +05:00
Alibek Omarov
86a2ea29e4 wscript: define libbacktrace as libbacktrace_custom, fix DL_ITERATE_PHDR not defined due to failing fallback check
* Changed to libbacktrace_custom to not collide with libbacktrace built as part of compiler's runtime
2026-04-26 04:12:52 +05:00
Alibek Omarov
d4f8543556 engine: platform: posix: better libbacktrace error output 2026-04-26 04:12:52 +05:00
Alibek Omarov
e850728016 engine: soundlib: fixes for BE 2026-04-26 04:12:52 +05:00
Alibek Omarov
11877f4c96 engine: imagelib: fixes for BE 2026-04-26 04:12:52 +05:00
Denis Drakhnia
2ba5030b62 engine: server: fix no response to goldsrc info query 2026-04-24 21:56:22 +05:00
Alibek Omarov
06b14800e1 engine: mod_bmodel: fix bspx swap definition 2026-04-24 08:00:49 +05:00
Alibek Omarov
bb7c85fd22 wscript: turn on progress bar by default 2026-04-24 07:31:36 +05:00
Alibek Omarov
37ce62fa6a waf: update to latest wafsrc + progress-bar-enhacnement 2026-04-24 07:31:11 +05:00
Alibek Omarov
e1b29e2da3 engine: temporarily disable swapping extended header 2026-04-24 06:58:53 +05:00
Alibek Omarov
b60afd73e9 public: swaplib: add comments 2026-04-24 06:58:53 +05:00
Alibek Omarov
84447a5292 public: crclib: add fix for md5 from q3 2026-04-24 06:58:53 +05:00
Alibek Omarov
fe4ea4aabf engine: server: fix pfnMessageEnd on BE 2026-04-24 06:58:53 +05:00
Alibek Omarov
878822290c public: crclib: fix CRC32_BlockSequence on BE 2026-04-24 06:58:53 +05:00
Alibek Omarov
04ca5d7cb0 engine: common: use FloatAsUint and UintAsFloat to write and read floats to/from sizebuf_t 2026-04-24 06:58:53 +05:00
Alibek Omarov
d46ed40f33 engine: pre-swap studio model before loading on BE 2026-04-24 06:58:53 +05:00
Alibek Omarov
82e031205c engine: pre-swap sprite before loading on BE 2026-04-24 06:58:53 +05:00
Alibek Omarov
6f5f04a29d engine: server: fix testpacket on BE server 2026-04-24 06:58:53 +05:00
Alibek Omarov
f9ed578bf1 engine: move CRC32_MapFIle to mod_bmodel, as it's a common function. Use le_struct_swap 2026-04-24 06:58:53 +05:00
Alibek Omarov
86c84c7de7 engine: common: add struct swapping for BSP models
With support for Quake BSP, HL BSP, extended HL BSP and BSPX.
2026-04-24 06:58:53 +05:00
Alibek Omarov
489c1c9dd5 public: add swaplib 2026-04-24 06:58:53 +05:00
Alibek Omarov
1df6a040ec engine: common: fix WriteBits/ReadBits 2026-04-24 06:58:53 +05:00
Alibek Omarov
82c2ca53fb engine: common: fix WriteUBitLong/ReadUBitLong 2026-04-24 06:58:53 +05:00
Alibek Omarov
3ddd1e2448 engine: common: fix LZSS on big endian 2026-04-24 06:58:53 +05:00
Alibek Omarov
371d619685 engine: platform: posix: remove dead ucontext.h include 2026-04-24 06:58:53 +05:00
Владислав Сухов
480f29e96e engine: common: mod_studio: fix model traversal and crash in Mod_StudioComputeBounds
Mod_StudioComputeBounds incorrectly assumed that all models across all body parts were laid out contiguously in memory. This caused out-of-bounds reads or reading garbage data when loading complex models, leading to crashes.
2026-04-24 01:13:46 +05:00
Zane van Iperen
95548053eb engine: common: set cmd_args to "" when tokenising, if we've no arguments
Fixes metamod-fwgs[1] breaking when CMD_ARGS() returns NULL.

[1]: eedede40e8/metamod/src/commands_meta.cpp (L63)
2026-04-22 21:27:27 +05:00
Владислав Сухов
0daf2c2b4d engine: client: add cvar to enable/disable gamepad gyro 2026-04-22 21:26:34 +05:00
Alibek Omarov
e2d5bea327 ref: cleanup unused functions 2026-04-21 23:10:32 +05:00
Alibek Omarov
306a5812a9 engine: server: fix svc_setpause usage in spawn command, fix minor protocol issue when spawnnig particle from pmove 2026-04-20 01:33:54 +05:00
Alibek Omarov
a3dba64513 public: move atlas packing code from renderer, add tests 2026-04-20 01:33:54 +05:00
Alibek Omarov
491ba9bc7d 3rdparty: mainui: update 2026-04-19 23:12:55 +05:00
Alibek Omarov
1c462df8a2 common: q_client: include stdint.h and xash3d_types.h 2026-04-19 23:12:44 +05:00
Alibek Omarov
ed975976f8 engine: client: add feature to reupload logo if any of logo related cvars were changed 2026-04-19 22:46:51 +05:00
Alibek Omarov
bbf8ab378d engine: server: add rate limiting for updating client's resources data too often 2026-04-19 22:30:04 +05:00
Alibek Omarov
5383e36af5 engine: net_chan: fix off-by-one error when trying to decompress packet 2026-04-19 20:50:42 +05:00
Alibek Omarov
897fb98b93 engine: net_chan: fix Netchan_AddBufferToList leaking pointer 2026-04-19 20:43:32 +05:00
Alibek Omarov
96e88adfcd engine: client: try to make R_Blood more accurate 2026-04-19 17:05:26 +05:00
Alibek Omarov
4dde4047f1 engine: make particle texture white, shift 1 pixel down and to the right 2026-04-19 17:04:40 +05:00
Alibek Omarov
905161529f engine: client: ref_common: reorganize R_CreateBuiltinTextures, but also fix a typo without assigned alpha to particle texture 2026-04-19 01:32:40 +05:00
Alibek Omarov
044523434d ref: add R_EntityDynamicLight that calculates lighting for studio and alias models, and also located in libref_common 2026-04-19 01:17:28 +05:00
Alibek Omarov
da4c4bb76a ref: remove unused sprite_radius global variable 2026-04-19 00:32:45 +05:00
Alibek Omarov
95b3271d68 engine: client: move input stuff to input subfolder 2026-04-18 22:18:50 +05:00
Alibek Omarov
3361a5d760 engine: client: split OSK into it's own file 2026-04-18 22:18:50 +05:00
Alibek Omarov
b725d2ece0 engine: client: moving beds 2026-04-18 22:18:50 +05:00
Alibek Omarov
66bee8b468 engine: client: fix trimmed autocomplete output for keys 2026-04-18 22:18:50 +05:00
Alibek Omarov
5e664dc3e9 engine: client: check for upper bound limit in Key_StringToKeynum 2026-04-18 22:18:50 +05:00
Alibek Omarov
bfdfeb3dc0 engine: client: fix Joy_GetHatValueForAxis threshold for forward axis, fix bindings limit 2026-04-18 22:18:50 +05:00
Alibek Omarov
49248f5e14 engine: client: touch fixes, fix division-by-zero in Touch_AspectRatio, GRID_COUNT, fix Touch_RemoveButtonFromList 2026-04-18 22:18:50 +05:00
Владислав Сухов
d03ea4c10a engine: client: in_gyro: fix inverted yaw axis 2026-04-18 00:41:46 +05:00
ksagameng
780dbd6183 scripts: gha: fix ios ci builds not including game libs 2026-04-17 19:41:30 +05:00
GLoOoccK
50da79f1e0 Update cl_parse_gs.c 2026-04-17 19:40:58 +05:00
Alibek Omarov
62201900cf ref: null: remove unused function 2026-04-16 23:25:32 +05:00
Alibek Omarov
ae233ac491 Merge branch 'refapi-15' 2026-04-16 23:19:06 +05:00
Владислав Сухов
dd1cb04cb7 engine: client: sdl: add support for built-in device gyroscope using SDL2 Sensor API (Android/iOS) 2026-04-16 00:15:35 +05:00
Alibek Omarov
74869c8f5d ref: make R_GatherPlayerLight shared between renderers 2026-04-14 22:36:15 +05:00
Alibek Omarov
dd712a0df9 engine: update Mod_PointInLeaf signature for RefAPI 2026-04-14 22:36:15 +05:00
Alibek Omarov
bfdb62d31e engine: ref_api: complete the RefAPI 15 changelog 2026-04-14 22:36:15 +05:00
Alibek Omarov
ae7b9eb28d engine: move creation of default textures to the engnie 2026-04-14 22:36:15 +05:00
Alibek Omarov
c63e13c8f4 engine: move handling of AVI textures into the engine 2026-04-14 22:36:15 +05:00
Alibek Omarov
d3e1407675 engine: remove unused R_Flush and VGUI_UploadTextureBlock from RefAPI 2026-04-14 22:36:15 +05:00
Alibek Omarov
e2e4728c7c engine: remove R_BeamCull from RefAPI 2026-04-14 22:36:15 +05:00
Alibek Omarov
dd3583d11a engine: move details textures parser to the engine 2026-04-14 21:18:57 +05:00
Alibek Omarov
e6924844da engine: move FillRenderAPI and FillTriAPI to renderers to reduce amount of exports 2026-04-14 21:18:56 +05:00
Alibek Omarov
a49db03561 engine: move StudioAPI negotiation to the engine 2026-04-14 21:18:03 +05:00
Alibek Omarov
84b7b5803b ref: soft: remove mistakingly put const on RI.rvp 2026-04-13 23:13:49 +05:00
Alibek Omarov
c224d39ba7 ref: soft: replace RI.onlyClientDraw with RF_ONLY_CLIENTDRAW flag in viewpass 2026-04-13 23:13:49 +05:00
Alibek Omarov
9b24b08df1 ref: gl: remove onlyClientDraw, drawOrtho, RI.params with viewpass flag check. RF_DRAW_OVERVIEW and RF_DRAW_CUBEMAP are cannot be set at the same time, so drawOrtho check replaced by RF_DRAW_CUBEMAP only 2026-04-13 23:13:49 +05:00
Alibek Omarov
a37b0e58f1 ref: soft: get rid of RI.drawOrtho, it's a debugging/development tool, not suited for software renderer use 2026-04-13 23:13:49 +05:00
Alibek Omarov
afca422e35 ref: soft: read ref_viewpass_t flags instead of cached RI.drawWorld 2026-04-13 23:13:49 +05:00
Alibek Omarov
3440ddf443 ref: gl: read ref_viewpass_t flags instead of cached RI.drawWorld 2026-04-13 23:13:49 +05:00
Alibek Omarov
6730ed9555 ref: soft: remove unused isSkyVisible 2026-04-13 23:13:49 +05:00
Alibek Omarov
fdd3a44fa0 ref: gl: remove unused isSkyVisible 2026-04-13 23:13:49 +05:00
Alibek Omarov
241c926076 ref: soft: replace RI.{vieworg,viewangles,pvsorigin} by ref_viewpass_t fields 2026-04-13 23:13:49 +05:00
Alibek Omarov
148a9a5a8c ref: gl: replace RI.{vieworg,viewangles,pvsorigin} by ref_viewpass_t fields 2026-04-13 23:13:49 +05:00
Alibek Omarov
513984d7fd ref: soft: save original viewpass and access viewport and fov through it 2026-04-13 23:13:49 +05:00
Alibek Omarov
1009795d5c ref: gl: save original viewpass and access viewport and fov through it 2026-04-13 23:13:49 +05:00
Alibek Omarov
e3d2da74db engine: move underwater fov effect handling to the engine 2026-04-13 23:13:49 +05:00
Alibek Omarov
ff3a57ae8a ref: gl: drop legacy mirror viewpass code 2026-04-13 23:13:49 +05:00
Alibek Omarov
271a6e2210 ref: soft: drop broken code for all non-viewer viewpasses 2026-04-13 23:13:49 +05:00
Alibek Omarov
0d40357b93 ref: common: wscript: fix psvita build 2026-04-13 13:01:18 +05:00
Alibek Omarov
a9dcdd034c ref: soft: fix build 2026-04-13 13:01:18 +05:00
Alibek Omarov
7324ff43fc ref: add GL_ResampleTexture to libref_common 2026-04-13 13:01:18 +05:00
Alibek Omarov
d85e7fc2b1 ref: move remaining light functions to libref_common 2026-04-13 13:01:18 +05:00
Alibek Omarov
d91ecfa3df ref: gl: refactor R_LightVec 2026-04-13 13:01:18 +05:00
Alibek Omarov
b7a3a02aad ref: common: add R_PushDlightsForBmodel 2026-04-13 13:01:18 +05:00
Alibek Omarov
5aca78951c ref: move R_PushDlights and R_MarkLights to ref_common 2026-04-13 13:01:18 +05:00
Alibek Omarov
81abae65b5 ref: move RunLightstyles to ref_common 2026-04-13 13:01:18 +05:00
Alibek Omarov
f5f6d9bbe4 engine: move sprite texture loading back to engine, only leave rendering for renderer 2026-04-13 13:01:18 +05:00
Alibek Omarov
ef73b55413 ref: common: move malloc and GL_InitRandomTable 2026-04-13 13:01:18 +05:00
Alibek Omarov
8334695522 ref: common: draft new static library, containing shared code for ref_gl and ref_soft 2026-04-13 13:01:18 +05:00
Alibek Omarov
d7f416d659 engine: draft new RefAPI 14: remove reserved functions from render api, remove CL_AddCustomBeam 2026-04-13 13:01:18 +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
Alibek Omarov
8e450d869b engine: common: add host.default_gamedir containing gamedir passed to us from game launcher 2026-04-03 04:09:25 +05:00
Alibek Omarov
e8ef7ad60c engine: server: throw Host_Error when game tries to call precache model with invalid argument, like GoldSrc 2026-04-03 01:43:23 +05:00
Alibek Omarov
216eedcdcd ref: gl: better wording about backface culling goldsrc bug [skip ci] 2026-04-03 01:28:04 +05:00
Alibek Omarov
fd55716633 ref: gl: change backface culling only if it wasn't disabled by mod developer beforehand 2026-04-03 01:23:55 +05:00
Alibek Omarov
0b920be381 engine: client: delay freeing up the sound channel when it becomes inaudible 2026-04-03 00:03:52 +05:00
Alibek Omarov
e6dfccb6ba engine: reorganize common.h header 2026-04-02 20:45:00 +05:00
Alibek Omarov
3129081050 engine: fix a typo USING_ASAN -> USE_ASAN 2026-04-02 02:16:50 +05:00
Alibek Omarov
00dac928c9 ref: replace checking PARM_QUAKE_COMPATIBLE with direct check of feature flag set 2026-04-02 02:13:27 +05:00
Alibek Omarov
3726256514 engine: simplify IsQuakeCompatible by enforcing QUAKE_COMPATIBLE feature flag when Quake demo starts 2026-04-02 02:11:40 +05:00
Alibek Omarov
5eb43505cb engine: platform: try to fix build on 32-bit arm in debug builds (r7 is used by frame pointer, so we push it into stack before using for a syscall) 2026-04-02 02:02:59 +05:00
193 changed files with 7631 additions and 12354 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

@@ -20,10 +20,10 @@ int main (void) {
}'''
FRAGMENT_GETPAGESIZE='''#include <unistd.h>
int main(void) { return getpagesize() }'''
int main(void) { return getpagesize(); }'''
FRAGMENT_GETEXECNAME='''#include <stdlib.h>
int main(void) { return getexecname() != 0 }'''
int main(void) { return getexecname() != 0; }'''
FRAGMENT_STRNLEN='''#include <string.h>
int main(int argc, char **argv) { return (int)strnlen(argv[0], 10); }'''
@@ -120,8 +120,8 @@ def configure(conf):
check_frag(FRAGMENT_SYNC, ' __sync extensions', 'HAVE_SYNC_FUNCTIONS'),
check_frag(FRAGMENT_GETPAGESIZE, ' getpagesize function', 'HAVE_DECL_GETPAGESIZE'),
check_frag(FRAGMENT_STRNLEN, ' strnlen function', 'HAVE_DECL_STRNLEN'),
check_frag(FRAGMENT_DL_ITERATE_PHDR % 'link.h', ' dl_iterate_phdr function in link.h', 'HAVE_DL_ITERATE_PHDR', after_tests=['link.h']),
check_frag(FRAGMENT_DL_ITERATE_PHDR % 'sys/link.h', ' dl_iterate_phdr function in sys/link.h', 'HAVE_DL_ITERATE_PHDR', after_tests=['sys/link.h']),
check_frag(FRAGMENT_DL_ITERATE_PHDR % 'link.h', ' dl_iterate_phdr function in link.h', 'HAVE_DL_ITERATE_PHDR_LINK_H', after_tests=['link.h']),
check_frag(FRAGMENT_DL_ITERATE_PHDR % 'sys/link.h', ' dl_iterate_phdr function in sys/link.h', 'HAVE_DL_ITERATE_PHDR_SYS_LINK_H', after_tests=['sys/link.h']),
check_frag(FRAGMENT_FCNTL, 'fnctl function', 'HAVE_FCNTL'),
check_frag(FRAGMENT_GETEXECNAME, 'getexecname function','HAVE_GETEXECNAME'),
check_frag(FRAGMENT_GETIPINFO, '_Unwind_GetIPInfo function', 'HAVE_GETIPINFO'),
@@ -137,6 +137,9 @@ def configure(conf):
msg='Checking for in parallel'
)
if conf.is_defined('HAVE_DL_ITERATE_PHDR_LINK_H') or conf.is_defined('HAVE_DL_ITERATE_PHDR_SYS_LINK_H'):
conf.define('HAVE_DL_ITERATE_PHDR', 1)
conf.env[DEFKEYS].sort()
conf.write_config_header()
@@ -181,7 +184,8 @@ def build(bld):
task = bld.stlib(
source = ['libbacktrace/' + i for i in sources],
target = 'backtrace',
# while we're using upstream libbacktrace, sometimes compiler picks up it's own version that isn't built with required options
target = 'backtrace_custom',
features = 'frandomseed' if bld.env.HAVE_FRANDOM_SEED else '',
use = 'EXTRAFLAGS lzma z zstd',
includes = '. libbacktrace/',

View File

@@ -0,0 +1,19 @@
## Lightmapped water
Xash3D FWGS supports lightmapped water, as an extension. It adds three new cvars and new worldspawn key values.
### For level designers:
If you're a level designer and intend to make your level to have lightmapped water, you can put these keyvalues to worldspawn entity description (always first entity in entities list):
| Key | Value | Description |
| ---------------------- | ------- | ----------- |
| `_litwater` | integer | Set to any non-zero value to enable lightmapped water. Overrides `gl_litwater_force` cvar value. |
| `_litwater_minlight` | integer | Minimal lightmap value water surface will receive. Helps to avoid too dark areas when water isn't properly lit. If not set, defaults to zero. |
| `_litwater_scale` | float | Scales up lightmap value for water surfaces. If not set, defaults to 1.0. |
### For players:
Some of the maps already have computed lightmap for water surfaces and sometimes water has been properly lit but the support hasn't been declared by the level designer.
As a player, you can enable it in `Video options` menu or through console with `gl_litwater_force` cvar. There are also `gl_litwater_minlight` and `gl_litwater_scale` cvars that function similar to keys above. The default values has been set to `192` and `1.25` respectively to slightly avoid issues with maps that wasn't intended to have lightmapped water.

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

@@ -87,6 +87,7 @@ typedef enum
IL_LOAD_PLAYER_DECAL = BIT(7), // special mode for player decals
IL_KTX2_RAW = BIT(8), // renderer can consume raw KTX2 files (e.g. ref_vk)
IL_ALLOW_WAD3_LUMA = BIT(9), // allow usage of luma textures in wad3 (tilde textures)
IL_HOST_ENDIAN = BIT(10), // buffer is already in host endian, skip byte-swapping
} ilFlags_t;
// goes into rgbdata_t->encode
@@ -116,6 +117,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

@@ -22,6 +22,9 @@ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
#ifndef QUAKE_CLIENT_H
#define QUAKE_CLIENT_H
#include <stdint.h>
#include "xash3d_types.h"
// Definitions originally taken from Quake, modified for binary compatibility
typedef struct usercmd_s

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

@@ -243,7 +243,10 @@ typedef int qboolean;
#define Swap32Store( x ) ( x = Swap32( x ))
#define Swap16Store( x ) ( x = Swap16( x ))
#ifdef XASH_BIG_ENDIAN
#define LittleFourCC( a, b, c, d ) (((uint32_t)( d ) << 24 ) | ((uint32_t)( c ) << 16 ) | ((uint32_t)( b ) << 8 ) | (uint32_t)( a ))
#define BigFourCC( a, b, c, d ) (((uint32_t)( a ) << 24 ) | ((uint32_t)( b ) << 16 ) | ((uint32_t)( c ) << 8 ) | (uint32_t)( d ))
#if XASH_BIG_ENDIAN
#define LittleLong( x ) Swap32( x )
#define LittleShort( x ) Swap16( x )
#define LittleLongSW( x ) Swap32Store( x )
@@ -252,6 +255,7 @@ typedef int qboolean;
#define BigLong( x ) ( x )
#define BigShort( x ) ( x )
#define BigFloat( x ) ( x )
#define HostFourCC( a, b, c, d ) BigFourCC( a, b, c, d )
#else
#define LittleLong( x ) ( x )
#define LittleShort( x ) ( x )
@@ -261,6 +265,7 @@ typedef int qboolean;
#define BigLong( x ) Swap32( x )
#define BigShort( x ) Swap16( x )
#define BigFloat( x ) SwapFloat( x )
#define HostFourCC( a, b, c, d ) LittleFourCC( a, b, c, d )
#endif
#endif // XASH_TYPES_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
@@ -444,13 +444,16 @@ qboolean AVI_Think( movie_state_t *Avi )
if( Avi->texture == 0 )
{
int cinTexture = SCR_GetCinematicTexture();
int w = Avi->w >= 0 ? Avi->w : refState.width;
int h = Avi->h >= 0 ? Avi->h : refState.height;
ref.dllFuncs.R_DrawStretchRaw( Avi->x, Avi->y, w, h, Avi->xres, Avi->yres, Avi->dst, redraw );
if( redraw )
ref.dllFuncs.GL_UpdateTexture( cinTexture, Avi->xres, Avi->yres, Avi->xres, Avi->yres, Avi->dst, PF_BGRA_32 );
ref.dllFuncs.R_DrawStretchPic( Avi->x, Avi->y, w, h, 0, 0, 1, 1, cinTexture );
}
else if( redraw && Avi->texture > 0 )
ref.dllFuncs.AVI_UploadRawFrame( Avi->texture, Avi->xres, Avi->yres, Avi->w, Avi->h, Avi->dst );
ref.dllFuncs.GL_UpdateTexture( Avi->texture, Avi->xres, Avi->yres, Avi->w, Avi->h, Avi->dst, PF_BGRA_32 );
if( flushing && !decoded )
return false; // probably hit an EOF

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

@@ -849,6 +849,31 @@ BEAM * GAME_EXPORT R_BeamEnts( int startEnt, int endEnt, int modelIndex, float l
return pbeam;
}
static qboolean R_BeamVisible( const vec3_t start, const vec3_t end )
{
vec3_t mins, maxs;
for( int i = 0; i < 3; i++ )
{
if( start[i] < end[i] )
{
mins[i] = start[i];
maxs[i] = end[i];
}
else
{
mins[i] = end[i];
maxs[i] = start[i];
}
// don't let it be zero sized
if( mins[i] == maxs[i] )
maxs[i] += 1.0f;
}
return Mod_BoxVisible( mins, maxs, ref.dllFuncs.Mod_GetCurrentVis( ));
}
/*
==============
R_BeamPoints
@@ -861,7 +886,7 @@ BEAM * GAME_EXPORT R_BeamPoints( vec3_t start, vec3_t end, int modelIndex, float
{
BEAM *pbeam;
if( life != 0 && ref.dllFuncs.R_BeamCull( start, end, true ))
if( life != 0 && !R_BeamVisible( start, end ))
return NULL;
pbeam = R_BeamAlloc();
@@ -1214,8 +1239,8 @@ particle spray
void GAME_EXPORT R_Blood( const vec3_t org, const vec3_t ndir, int pcolor, int speed )
{
vec3_t pos, dir, vec;
float pspeed = speed * 3.0f;
int i, j;
int pspeed = speed * 3;
particle_t *p;
VectorNormalize2( ndir, dir );
@@ -1225,7 +1250,7 @@ void GAME_EXPORT R_Blood( const vec3_t org, const vec3_t ndir, int pcolor, int s
VectorAddScalar( org, COM_RandomFloat( -3.0f, 3.0f ), pos );
VectorAddScalar( dir, COM_RandomFloat( -0.06f, 0.06f ), vec );
for( j = 0; j < 7; j++ )
for( j = 0; j < 8; j++ )
{
p = R_AllocParticle( NULL );
if( !p ) return;
@@ -1237,6 +1262,8 @@ void GAME_EXPORT R_Blood( const vec3_t org, const vec3_t ndir, int pcolor, int s
VectorAddScalar( pos, COM_RandomFloat( -1.0f, 1.0f ), p->org );
VectorScale( vec, pspeed, p->vel );
}
pspeed -= speed;
}
}
@@ -1308,10 +1335,7 @@ void GAME_EXPORT R_BloodStream( const vec3_t org, const vec3_t ndir, int pcolor,
p->color = pcolor + COM_RandomLong( 0, 9 );
p->type = pt_4x_slowgrav;
p->org[0] = org[0] + COM_RandomFloat( -1.0f, 1.0f );
p->org[1] = org[1] + COM_RandomFloat( -1.0f, 1.0f );
p->org[2] = org[2] + COM_RandomFloat( -1.0f, 1.0f );
VectorAddScalar( org, COM_RandomFloat( -1.0f, 1.0f ), p->org );
VectorCopy( dir, p->vel );
p->vel[2] -= arc;

View File

@@ -17,6 +17,20 @@ GNU General Public License for more details.
#include "filesystem.h"
#include "client.h"
#include "qfont.h"
#include "swaplib.h"
le_struct_begin( charinfo_swap )
le_struct_field( charinfo, startoffset )
le_struct_field( charinfo, charwidth )
le_struct_end();
le_struct_begin( qfont_swap )
le_struct_field( qfont_t, width )
le_struct_field( qfont_t, height )
le_struct_field( qfont_t, rowcount )
le_struct_field( qfont_t, rowheight )
le_struct_array_child( qfont_t, fontinfo, charinfo_swap, NUM_GLYPHS )
le_struct_end();
qboolean CL_FixedFont( cl_font_t *font )
{
@@ -70,11 +84,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 )
@@ -134,6 +144,7 @@ qboolean Con_LoadVariableWidthFont( const char *fontname, cl_font_t *font, float
}
memcpy( &src, pfile, sizeof( src ));
le_struct_swap( qfont_swap, &src );
Mem_Free( pfile );
font->hFontTexture = CL_LoadFontTexture( fontname, texFlags, &font_width );

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

@@ -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 ));
@@ -1385,6 +1385,10 @@ static void CL_CreateResourceList( void )
cl.num_resources = 0;
memset( rgucMD5_hash, 0, sizeof( rgucMD5_hash ));
ClearBits( cl_logofile.flags, FCVAR_CHANGED );
ClearBits( cl_logocolor.flags, FCVAR_CHANGED );
ClearBits( cl_logoext.flags, FCVAR_CHANGED );
// sanitize cvar value
if( Q_strcmp( cl_logoext.string, "bmp" ) && Q_strcmp( cl_logoext.string, "png" ))
Cvar_DirectSet( &cl_logoext, "bmp" );
@@ -1400,7 +1404,6 @@ static void CL_CreateResourceList( void )
if( !fp )
return;
MD5_HashFile( rgucMD5_hash, szFileName, NULL );
nSize = FS_FileLength( fp );
if( nSize != 0 )
@@ -1409,6 +1412,7 @@ static void CL_CreateResourceList( void )
if( pNewResource )
{
MD5_HashFile( rgucMD5_hash, szFileName, NULL );
SetBits( pNewResource->ucFlags, RES_CUSTOM );
memcpy( pNewResource->rgucMD5_hash, rgucMD5_hash, 16 );
HPAK_AddLump( false, hpk_custom_file.string, pNewResource, NULL, fp );
@@ -1418,6 +1422,42 @@ static void CL_CreateResourceList( void )
FS_Close( fp );
}
/*
==================
CL_CheckLogoChanged
==================
*/
static void CL_CheckLogoChanged( void )
{
player_info_t *player;
int i;
if( cls.state != ca_active )
return;
if( !FBitSet( cl_logofile.flags | cl_logocolor.flags | cl_logoext.flags, FCVAR_CHANGED ))
return;
CL_CreateResourceList();
if( cl.num_resources == 0 )
return;
player = &cl.players[cl.playernum];
COM_ClearCustomizationList( &player->customdata, true );
for( i = 0; i < cl.num_resources; i++ )
{
resource_t *pResource = &cl.resourcelist[i];
if( !COM_CreateCustomization( &player->customdata, pResource, cl.playernum, 0, NULL, NULL ))
Con_Printf( "Unable to create custom decal\n" );
}
CL_SendResourceList( cl.resourcelist, cl.num_resources );
}
static qboolean CL_StringToProtocol( const char *s, connprotocol_t *proto )
{
if( !Q_stricmp( s, "current" ) || !Q_strcmp( s, "49" ))
@@ -2543,7 +2583,7 @@ static void CL_ServerList( netadr_t from, sizebuf_t *msg )
MSG_ReadBytes( msg, servadr.ip, sizeof( servadr.ip )); // 4 bytes for IP
NET_NetadrSetType( &servadr, NA_IP );
}
servadr.port = MSG_ReadShort( msg ); // 2 bytes for Port
MSG_ReadBytes( msg, &servadr.port, sizeof( servadr.port )); // 2 bytes for Port, in network byte order
// list is ends here
if( !servadr.port )
@@ -3589,6 +3629,9 @@ void Host_ClientBegin( void )
// finalize connection process if needs
CL_CheckClientState();
// check if logo cvars changed and re-upload if needed
CL_CheckLogoChanged();
// tell the client.dll about client data
CL_UpdateClientData();
@@ -3678,8 +3721,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 +3730,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

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

@@ -25,6 +25,7 @@ AVI PLAYING
*/
static movie_state_t *cin_state;
static int cin_texture;
/*
==================
@@ -281,6 +282,12 @@ void SCR_InitCinematic( void )
{
AVI_Initailize ();
cin_state = AVI_GetState( CIN_MAIN );
cin_texture = ref.dllFuncs.GL_CreateTexture( "*cintexture", 64, 64, NULL, TF_NOMIPMAP|TF_CLAMP );
}
int SCR_GetCinematicTexture( void )
{
return cin_texture;
}
/*

View File

@@ -360,6 +360,29 @@ qboolean V_PreRender( void )
//============================================================================
/*
====================
V_ApplyRefUnderwater
apply underwater fov effect
====================
*/
static void V_ApplyRefUnderwater( ref_viewpass_t *rvp )
{
if( !FBitSet( rvp->flags, RF_DRAW_CUBEMAP|RF_DRAW_OVERVIEW ))
return;
if( !FBitSet( host.features, ENGINE_QUAKE_COMPATIBLE ))
return;
if( cl.local.waterlevel < 3 )
return;
rvp->fov_x = atan( tan( DEG2RAD( rvp->fov_x ) / 2 ) * ( 0.97f + sin( cl.time * 1.5f ) * 0.03f )) * 2 / (M_PI_F / 180.0f);
rvp->fov_y = atan( tan( DEG2RAD( rvp->fov_y ) / 2 ) * ( 1.03f - sin( cl.time * 1.5f ) * 0.03f )) * 2 / (M_PI_F / 180.0f);
}
/*
==================
V_RenderView
@@ -389,6 +412,7 @@ void V_RenderView( void )
clgame.dllFuncs.pfnCalcRefdef( &rp );
V_GetRefParams( &rp, &rvp );
V_RefApplyOverview( &rvp );
V_ApplyRefUnderwater( &rvp );
if( viewnum == 0 && FBitSet( rvp.flags, RF_ONLY_CLIENTDRAW ))
{
@@ -535,6 +559,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];
//=================================================
@@ -757,6 +761,7 @@ void CL_AddToResourceList( resource_t *pResource, resource_t *pList );
void CL_RemoveFromResourceList( resource_t *pResource );
void CL_MoveToOnHandList( resource_t *pResource );
void CL_ClearResourceLists( void );
void CL_SendResourceList( const resource_t *list, int count );
//
// cl_debug.c
@@ -853,6 +858,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 );
@@ -869,7 +875,6 @@ struct msurface_s *pfnTraceSurface( int ground, float *vstart, float *vend );
void CL_EnableScissor( scissor_state_t *scissor, int x, int y, int width, int height );
void CL_DisableScissor( scissor_state_t *scissor );
qboolean CL_Scissor( const scissor_state_t *scissor, float *x, float *y, float *width, float *height, float *u0, float *v0, float *u1, float *v1 );
static inline cl_entity_t *CL_EDICT_NUM( int index )
{
if( unlikely( !clgame.entities )) // not in game yet
@@ -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 );
@@ -1222,6 +1216,7 @@ void SteamBroker_TerminateGameConnection( void );
// cl_video.c
//
void SCR_InitCinematic( void );
int SCR_GetCinematicTexture( void );
void SCR_FreeCinematic( void );
qboolean SCR_PlayCinematic( const char *name );
qboolean SCR_DrawCinematic( void );
@@ -1247,7 +1242,6 @@ void Key_EnumCmds_f( void );
void Key_SetKeyDest( int key_dest );
void Key_EnableTextInput( qboolean enable, qboolean force );
int Key_ToUpper( int key );
void OSK_Draw( void );
qboolean Cmd_GetKeysList( const char *s, char *completedname, int length, qboolean print_suggestions );
//

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

@@ -23,6 +23,8 @@ GNU General Public License for more details.
#include "client.h"
#include "const.h"
#include "triangleapi.h"
#include "r_studioint.h"
#include "mod_local.h"
#include "r_efx.h"
#include "demo_api.h"
#include "ivoicetweak.h"
@@ -37,11 +39,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 +107,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 +192,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 +376,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 +579,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 +659,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 +1426,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 +1441,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 +1456,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 +1932,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;
}
@@ -2218,7 +2175,7 @@ pfnGetViewModel
=============
*/
static cl_entity_t* GAME_EXPORT CL_GetViewModel( void )
static cl_entity_t *GAME_EXPORT CL_GetViewModel( void )
{
return &clgame.viewent;
}
@@ -3369,7 +3326,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 );
@@ -3974,6 +3931,69 @@ void CL_UnloadProgs( void )
memset( &clgame, 0, sizeof( clgame ));
}
static model_t *pfnStudio_Mod_ForName( const char *name, int crash )
{
return Mod_ForName( name, crash, false );
}
static void *pfnStudio_Mod_Extradata( model_t *mod )
{
return Mod_StudioExtradata( mod );
}
static void pfnStudio_GetAliasScale( float *x, float *y )
{
if( x ) *x = 1.0f;
if( y ) *y = 1.0f;
}
static float ***pfnStudio_GetAliasTransform( void )
{
return NULL;
}
static model_t *pfnStudio_GetChromeSprite( void )
{
return cl_sprite_shell;
}
static int pfnStudio_IsHardware( void )
{
return 1;
}
static engine_studio_api_t gStudioAPI =
{
.Mem_Calloc = Mod_Calloc,
.Cache_Check = Mod_CacheCheck,
.LoadCacheFile = Mod_LoadCacheFile,
.Mod_ForName = pfnStudio_Mod_ForName,
.Mod_Extradata = pfnStudio_Mod_Extradata,
.GetModelByIndex = CL_ModelHandle,
.GetCvar = pfnCVarGetPointer,
.GetChromeSprite = pfnStudio_GetChromeSprite,
.GetAliasScale = pfnStudio_GetAliasScale,
.StudioGetAliasTransform = pfnStudio_GetAliasTransform,
.GetViewEntity = CL_GetViewModel,
.IsHardware = pfnStudio_IsHardware,
};
static void CL_InitStudioAPI( void )
{
static r_studio_interface_t gDefaultStudioDraw;
r_studio_interface_t *pStudioDraw;
if( !ref.dllFuncs.R_StudioFillAPI( &gStudioAPI, &gDefaultStudioDraw ))
return;
pStudioDraw = &gDefaultStudioDraw;
if( clgame.dllFuncs.pfnGetStudioModelInterface )
clgame.dllFuncs.pfnGetStudioModelInterface( STUDIO_INTERFACE_VERSION, &pStudioDraw, &gStudioAPI );
ref.dllFuncs.R_StudioSetDrawInterface( pStudioDraw );
}
qboolean CL_LoadProgs( const char *name )
{
static playermove_t gpMove;
@@ -4095,14 +4115,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
@@ -4111,7 +4131,7 @@ qboolean CL_LoadProgs( const char *name )
// initialize game
clgame.dllFuncs.pfnInit();
ref.dllFuncs.CL_InitStudioAPI();
CL_InitStudioAPI();
return true;
}

View File

@@ -214,6 +214,11 @@ static void pfnAVI_StreamSound( movie_state_t *avi, int entnum, float fvol, floa
return; // stub, use AVI_SetParm and AVI_Think to stream AVI sound
}
static void pfnAVI_UploadRawFrame( int texture, int cols, int rows, int width, int height, const byte *data )
{
ref.dllFuncs.GL_UpdateTexture( texture, cols, rows, width, height, data, PF_BGRA_32 );
}
static render_api_t gRenderAPI =
{
pfnRenderGetParm, // GL_RenderGetParm,
@@ -243,7 +248,7 @@ static render_api_t gRenderAPI =
AVI_GetVideoInfo,
AVI_GetVideoFrameNumber,
AVI_GetVideoFrame,
NULL, // R_UploadStretchRaw,
pfnAVI_UploadRawFrame, // AVI_UploadRawFrame
AVI_FreeVideo,
AVI_IsActive,
pfnAVI_StreamSound,
@@ -283,40 +288,6 @@ static render_api_t gRenderAPI =
COM_SetRandomSeed,
};
static void R_FillRenderAPIFromRef( render_api_t *to, const ref_interface_t *from )
{
to->GetDetailScaleForTexture = from->GetDetailScaleForTexture;
to->GetExtraParmsForTexture = from->GetExtraParmsForTexture;
to->GetFrameTime = from->GetFrameTime;
to->R_SetCurrentEntity = from->R_SetCurrentEntity;
to->R_SetCurrentModel = from->R_SetCurrentModel;
to->GL_FindTexture = from->GL_FindTexture;
to->GL_TextureName = from->GL_TextureName;
to->GL_TextureData = from->GL_TextureData;
to->GL_LoadTexture = from->GL_LoadTexture;
to->GL_CreateTexture = from->GL_CreateTexture;
to->GL_LoadTextureArray = from->GL_LoadTextureArray;
to->GL_CreateTextureArray = from->GL_CreateTextureArray;
to->GL_FreeTexture = from->GL_FreeTexture;
to->DrawSingleDecal = from->DrawSingleDecal;
to->R_DecalSetupVerts = from->R_DecalSetupVerts;
to->R_EntityRemoveDecals = from->R_EntityRemoveDecals;
to->AVI_UploadRawFrame = from->AVI_UploadRawFrame;
to->GL_Bind = from->GL_Bind;
to->GL_SelectTexture = from->GL_SelectTexture;
to->GL_LoadTextureMatrix = from->GL_LoadTextureMatrix;
to->GL_TexMatrixIdentity = from->GL_TexMatrixIdentity;
to->GL_CleanUpTextureUnits = from->GL_CleanUpTextureUnits;
to->GL_TexGen = from->GL_TexGen;
to->GL_TextureTarget = from->GL_TextureTarget;
to->GL_TexCoordArrayMode = from->GL_TexCoordArrayMode;
to->GL_UpdateTexSize = from->GL_UpdateTexSize;
to->GL_DrawParticles = from->GL_DrawParticles;
to->LightVec = from->LightVec;
to->StudioGetTexture = from->StudioGetTexture;
to->GL_GetProcAddress = from->R_GetProcAddress;
}
/*
===============
R_InitRenderAPI
@@ -329,8 +300,15 @@ qboolean R_InitRenderAPI( void )
// make sure what render functions is cleared
memset( &clgame.drawFuncs, 0, sizeof( clgame.drawFuncs ));
// fill missing functions from renderer
R_FillRenderAPIFromRef( &gRenderAPI, &ref.dllFuncs );
gRenderAPI.GetDetailScaleForTexture = ref.dllFuncs.R_GetDetailScaleForTexture;
gRenderAPI.GL_FindTexture = ref.dllFuncs.GL_FindTexture;
gRenderAPI.GL_TextureName = ref.dllFuncs.GL_TextureName;
gRenderAPI.GL_TextureData = ref.dllFuncs.GL_TextureData;
gRenderAPI.GL_LoadTexture = ref.dllFuncs.GL_LoadTexture;
gRenderAPI.GL_FreeTexture = ref.dllFuncs.GL_FreeTexture;
gRenderAPI.GL_Bind = ref.dllFuncs.GL_Bind;
ref.dllFuncs.R_FillRenderAPI( &gRenderAPI );
if( clgame.dllFuncs.pfnGetRenderInterface )
{

View File

@@ -287,13 +287,6 @@ static entity_state_t *R_StudioGetPlayerState( int index )
return &cl.frames[cl.parsecountmod].playerstate[index];
}
static int pfnGetStudioModelInterface( int version, struct r_studio_interface_s **ppinterface, struct engine_studio_api_s *pstudio )
{
return clgame.dllFuncs.pfnGetStudioModelInterface ?
clgame.dllFuncs.pfnGetStudioModelInterface( version, ppinterface, pstudio ) :
0;
}
static const bpc_desc_t *pfnImage_GetPFDesc( int idx )
{
return &PFDesc[idx];
@@ -329,11 +322,6 @@ static qboolean R_Init_Video_( ref_graphic_apis_t type )
return R_Init_Video( type );
}
static mleaf_t *pfnMod_PointInLeaf( const vec3_t p, mnode_t *node )
{
// FIXME: get rid of this on next RefAPI update
return Mod_PointInLeaf( p, node, cl.models[1] );
}
static const ref_api_t gEngfuncs =
{
@@ -375,7 +363,7 @@ static const ref_api_t gEngfuncs =
Mod_SampleSizeForFace,
Mod_BoxVisible,
pfnMod_PointInLeaf,
Mod_PointInLeaf,
R_DrawWorldHull,
R_DrawModelHull,
@@ -411,7 +399,6 @@ static const ref_api_t gEngfuncs =
Mod_CacheCheck,
Mod_LoadCacheFile,
Mod_Calloc,
pfnGetStudioModelInterface,
_Mem_AllocPool,
_Mem_FreePool,
@@ -465,6 +452,7 @@ static const ref_api_t gEngfuncs =
&g_fsapi,
R_GetWindowHandle,
R_GetSpriteFrame,
};
static void R_UnloadProgs( void )
@@ -487,28 +475,78 @@ static void R_UnloadProgs( void )
memset( &ref.dllFuncs, 0, sizeof( ref.dllFuncs ));
}
static void CL_FillTriAPIFromRef( triangleapi_t *dst, const ref_interface_t *src )
static void CL_FillTriAPI( triangleapi_t *dst )
{
dst->version = TRI_API_VERSION;
dst->Begin = src->Begin;
dst->RenderMode = TriRenderMode;
dst->End = src->End;
dst->Color4f = TriColor4f;
dst->Color4ub = TriColor4ub;
dst->TexCoord2f = src->TexCoord2f;
dst->Vertex3f = src->Vertex3f;
dst->Vertex3fv = src->Vertex3fv;
dst->Brightness = TriBrightness;
dst->CullFace = TriCullFace;
dst->SpriteTexture = TriSpriteTexture;
dst->WorldToScreen = TriWorldToScreen;
dst->Fog = src->Fog;
dst->ScreenToWorld = src->ScreenToWorld;
dst->GetMatrix = src->GetMatrix;
dst->BoxInPVS = TriBoxInPVS;
dst->LightAtPoint = TriLightAtPoint;
dst->Color4fRendermode = TriColor4fRendermode;
dst->FogParams = src->FogParams;
dst->Begin = ref.dllFuncs.Begin;
dst->End = ref.dllFuncs.End;
dst->Vertex3f = ref.dllFuncs.Vertex3f;
dst->Vertex3fv = ref.dllFuncs.Vertex3fv;
ref.dllFuncs.R_FillTriAPI( dst );
}
static void R_CreateBuiltinTextures( void )
{
// default checkerboard
uint data16x16[256];
for( int y = 0; y < 16; y++ )
{
for( int x = 0; x < 16; x++ )
{
if(( y < 8 ) ^ ( x < 8 ))
data16x16[y * 16 + x] = HostFourCC( 255, 0, 255, 255 );
else
data16x16[y * 16 + x] = HostFourCC( 0, 0, 0, 255 );
}
}
ref.dllFuncs.GL_CreateTexture( REF_DEFAULT_TEXTURE, 16, 16, data16x16, 0 );
// particle texture
uint particle[64];
for( int y = 0; y < 8; y++ )
{
for( int x = 0; x < 8; x++ )
particle[y * 8 + x] = HostFourCC( 255, 255, 255, 0 ); // fill it with invisible white
}
// a1ba: moved one pixel down and to the right to make it look better with linear interpolation
// set to 0 to make it look like in goldsrc
const int corner = 1;
for( int y = corner; y < 4 + corner; y++ )
{
for( int x = corner; x < 4 + corner; x++ )
{
if(( x == corner || x == 3 + corner ) && ( y == corner || y == 3 + corner ))
continue;
particle[y * 8 + x] = HostFourCC( 255, 255, 255, 255 );
}
}
ref.dllFuncs.GL_CreateTexture( REF_PARTICLE_TEXTURE, 8, 8, particle, TF_CLAMP|TF_HAS_ALPHA );
// solid colors
uint data4x4[16];
memset( data4x4, 0xFF, sizeof( data4x4 ));
ref.dllFuncs.GL_CreateTexture( REF_WHITE_TEXTURE, 4, 4, data4x4, 0 );
for( int x = 0; x < 16; x++ )
data4x4[x] = HostFourCC( 127, 127, 127, 255 );
ref.dllFuncs.GL_CreateTexture( REF_GRAY_TEXTURE, 4, 4, data4x4, 0 );
for( int x = 0; x < 16; x++ )
data4x4[x] = HostFourCC( 0, 0, 0, 255 );
ref.dllFuncs.GL_CreateTexture( REF_BLACK_TEXTURE, 4, 4, data4x4, 0 );
}
static qboolean R_LoadProgs( const char *name )
@@ -554,8 +592,8 @@ static qboolean R_LoadProgs( const char *name )
Cvar_FullSet( "host_refloaded", "1", FCVAR_READ_ONLY );
ref.initialized = true;
// initialize TriAPI callbacks
CL_FillTriAPIFromRef( &gTriApi, &ref.dllFuncs );
R_CreateBuiltinTextures();
CL_FillTriAPI( &gTriApi );
return true;
}

View File

@@ -39,6 +39,10 @@ void IN_DeactivateMouse( void );
void IN_MouseSavePos( void );
void IN_MouseRestorePos( void );
void IN_ToggleClientMouse( int newstate, int oldstate );
void IN_GyroInit( void );
void IN_GyroCheckAvailability( void );
void IN_GyroEvent( vec3_t data );
void IN_GyroFinalizeMove( float *fw, float *side, float *dpitch, float *dyaw );
uint IN_CollectInputDevices( void );
void IN_LockInputDevices( qboolean lock );
@@ -71,7 +75,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 +91,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 );
@@ -95,6 +99,15 @@ qboolean Touch_WantVisibleCursor( void );
void Touch_NotifyResize( void );
#endif
//
// in_osk.c
//
extern convar_t osk_enable;
void OSK_Init( void );
qboolean OSK_KeyEvent( int key, int down );
void OSK_EnableTextInput( qboolean enable, qboolean force );
void OSK_Draw( void );
//
// in_joy.c
//
@@ -119,7 +132,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 +149,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

@@ -0,0 +1,120 @@
/*
in_gyro.c - System gyroscope input code
Copyright (C) 2026 Xash3D FWGS contributors
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 "input.h"
#include "client.h"
static CVAR_DEFINE_AUTO( gyro_enable, "0", FCVAR_ARCHIVE | FCVAR_FILTERABLE, "enables aiming with built-in device gyroscope" );
static CVAR_DEFINE_AUTO( gyro_available, "0", FCVAR_READ_ONLY, "tells whether system gyroscope hardware is available or not" );
static CVAR_DEFINE_AUTO( gyro_pitch, "1.0", FCVAR_ARCHIVE | FCVAR_FILTERABLE, "built-in gyroscope sensitivity for looking up and down" );
static CVAR_DEFINE_AUTO( gyro_yaw, "1.0", FCVAR_ARCHIVE | FCVAR_FILTERABLE, "built-in gyroscope sensitivity for turning left and right" );
static CVAR_DEFINE_AUTO( gyro_roll, "0.0", FCVAR_ARCHIVE | FCVAR_FILTERABLE, "built-in gyroscope sensitivity when tilting the device sideways" );
static CVAR_DEFINE_AUTO( gyro_pitch_deadzone, "0.5", FCVAR_ARCHIVE | FCVAR_FILTERABLE, "built-in gyroscope pitch axis deadzone (deg/s)" );
static CVAR_DEFINE_AUTO( gyro_yaw_deadzone, "0.5", FCVAR_ARCHIVE | FCVAR_FILTERABLE, "built-in gyroscope yaw axis deadzone (deg/s)" );
static CVAR_DEFINE_AUTO( gyro_roll_deadzone, "0.5", FCVAR_ARCHIVE | FCVAR_FILTERABLE, "built-in gyroscope roll axis deadzone (deg/s)" );
// stores the latest instantaneous rotation rates from built-in gyroscope
static vec3_t gyro_speed;
/*
==============
IN_GyroInit
==============
*/
void IN_GyroInit( void )
{
Cvar_RegisterVariable( &gyro_enable );
Cvar_RegisterVariable( &gyro_available );
Cvar_RegisterVariable( &gyro_pitch );
Cvar_RegisterVariable( &gyro_yaw );
Cvar_RegisterVariable( &gyro_roll );
Cvar_RegisterVariable( &gyro_pitch_deadzone );
Cvar_RegisterVariable( &gyro_yaw_deadzone );
Cvar_RegisterVariable( &gyro_roll_deadzone );
}
/*
==============
IN_GyroCheckAvailability
One-time late check called after startup and configs
==============
*/
void IN_GyroCheckAvailability( void )
{
#if XASH_SDL
if( gyro_available.value )
return;
if( SDLash_GyroIsAvailable() )
{
Cvar_FullSet( "gyro_available", "1", FCVAR_READ_ONLY );
}
#endif
}
/*
=============
IN_GyroEvent
System gyroscope events from platform
=============
*/
void IN_GyroEvent( vec3_t data )
{
VectorCopy( data, gyro_speed );
}
/*
=============
IN_GyroFinalizeMove
Apply gyro movement to view angles
=============
*/
void IN_GyroFinalizeMove( float *fw, float *side, float *dpitch, float *dyaw )
{
platform_orientation_t orient;
float orient_scale = 1.0f;
if( !gyro_enable.value || !gyro_available.value )
return;
orient = Platform_GetDisplayOrientation();
if( orient == ORIENTATION_LANDSCAPE_FLIPPED )
orient_scale = -1.0f;
// In Landscape mode axes are swapped relative to natural (Portrait) orientation
// Y axis rotation becomes Pitch (up/down)
// X axis rotation becomes Yaw (left/right)
float pitch_speed = -orient_scale * gyro_speed[1] * ( 180.0f / M_PI );
float yaw_speed = orient_scale * gyro_speed[0] * ( 180.0f / M_PI );
float roll_speed = orient_scale * gyro_speed[2] * ( 180.0f / M_PI );
if( fabs( pitch_speed ) < gyro_pitch_deadzone.value )
pitch_speed = 0.0f;
if( fabs( yaw_speed ) < gyro_yaw_deadzone.value )
yaw_speed = 0.0f;
if( fabs( roll_speed ) < gyro_roll_deadzone.value )
roll_speed = 0.0f;
*dpitch -= gyro_pitch.value * pitch_speed * host.realframetime;
*dyaw += gyro_yaw.value * yaw_speed * host.realframetime;
*dyaw += gyro_roll.value * roll_speed * host.realframetime;
VectorClear( gyro_speed );
}

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,19 @@ 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_gyro_enable, "1", FCVAR_ARCHIVE | FCVAR_FILTERABLE, "enables aiming with gamepad gyroscope" );
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;
/*
============
@@ -185,7 +198,7 @@ static int Joy_GetHatValueForAxis( const engineAxis_t engineAxis )
positive = JOY_HAT_RIGHT;
break;
case JOY_AXIS_FWD:
threshold = joy_side_key_threshold.value;
threshold = joy_forward_key_threshold.value;
negative = JOY_HAT_UP;
positive = JOY_HAT_DOWN;
break;
@@ -228,6 +241,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 +272,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 +298,8 @@ Gyroscope events
*/
void Joy_GyroEvent( vec3_t data )
{
VectorCopy( data, joy_gyro_speed );
VectorCopy( data, joy_gyro_speed_display );
}
/*
@@ -290,7 +311,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 ) )
@@ -298,7 +319,7 @@ void Joy_FinalizeMove( float *fw, float *side, float *dpitch, float *dyaw )
const char *bind = joy_axis_binding.string;
size_t i;
for( i = 0; bind[i]; i++ )
for( i = 0; bind[i] && i < MAX_AXES; i++ )
{
switch( bind[i] )
{
@@ -308,7 +329,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 +338,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_gyro_enable.value && 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 )
@@ -366,6 +597,17 @@ void Joy_Init( void )
Cvar_RegisterVariable( &joy_have_gyro );
Cvar_RegisterVariable( &joy_calibrated );
Cvar_RegisterVariable( &joy_enable );
Cvar_RegisterVariable( &joy_gyro_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

View File

@@ -158,9 +158,6 @@ static const keyname_t keynames[] =
{ "INTERNATIONAL9", K_INTERNATIONAL + 8, "" },
};
static void OSK_EnableTextInput( qboolean enable, qboolean force );
static qboolean OSK_KeyEvent( int key, int down );
static CVAR_DEFINE_AUTO( osk_enable, "0", FCVAR_ARCHIVE|FCVAR_FILTERABLE, "enable built-in on-screen keyboard" );
static CVAR_DEFINE_AUTO( key_rotate, "0", FCVAR_ARCHIVE|FCVAR_FILTERABLE, "rotate arrow keys (0-3)" );
/*
@@ -200,7 +197,14 @@ static int Key_StringToKeynum( const char *str )
// check for hex code
if( str[0] == '0' && str[1] == 'x' )
return Q_atoi( str );
{
int key = Q_atoi( str );
if( key >= ARRAYSIZE( keys ))
return -1;
return key;
}
// scan for a text match
for( i = 0; i < ARRAYSIZE( keynames ); i++ )
@@ -520,7 +524,7 @@ qboolean Cmd_GetKeysList( const char *s, char *completedname, int length, qboole
const char *keyname = Key_KeynumToString( i );
if(( *s == '*' ) || !Q_strnicmp( keyname, s, len ))
Q_strncpy( keys_strings[numkeys++], keyname, sizeof( keys[0] ));
Q_strncpy( keys_strings[numkeys++], keyname, sizeof( keys_strings[0] ));
}
if( !numkeys ) return false;
@@ -579,7 +583,6 @@ void Key_Init( void )
for( i = 0; i < ARRAYSIZE( keynames ); i++ )
Key_SetBinding( keynames[i].keynum, keynames[i].binding );
Cvar_RegisterVariable( &osk_enable );
Cvar_RegisterVariable( &key_rotate );
}
@@ -985,288 +988,3 @@ int Key_ToUpper( int keynum )
return Q_toupper( keynum );
}
/* On-screen keyboard:
*
* 4 lines with 13 buttons each
* Left trigger == backspace
* Right trigger == space
* Any button press is button press on keyboard
*
* Our layout:
* 0 1 2 3 4 5 6 7 8 9 10 11 12
* +--+--+--+--+--+--+--+--+--+--+--+--+--+
* |` |1 |2 |3 |4 |5 |6 |7 |8 |9 |0 |- |= | 0
* +--+--+--+--+--+--+--+--+--+--+--+--+--+
* |q |w |e |r |t |y |u |i |o |p |[ |] |\ | 1
* +--+--+--+--+--+--+--+--+--+--+--+--+--+
* |CL|a |s |d |f |g |h |j |k |l |; |' |BS| 2
* +--+--+--+--+--+--+--+--+--+--+--+--+--+
* |SH|z |x |c |v |b |n |m |, |. |/ |SP|EN| 3
* +--+--+--+--+--+--+--+--+--+--+--+--+--+
*/
#define MAX_OSK_ROWS 13
#define MAX_OSK_LINES 4
enum
{
OSK_DEFAULT = 0,
OSK_UPPER, // on caps, shift
/*
OSK_RUSSIAN,
OSK_RUSSIAN_UPPER,
*/
OSK_LAST
};
enum
{
OSK_TAB = 16,
OSK_SHIFT,
OSK_BACKSPACE,
OSK_ENTER,
OSK_SPECKEY_LAST
};
static const char *osk_keylayout[][4] =
{
{
"`1234567890-=", // 13
"qwertyuiop[]\\", // 13
"\x10" "asdfghjkl;'" "\x12", // 11 + caps on a left, enter on a right
"\x11" "zxcvbnm,./ " "\x13" // 10 + esc on left + shift on a left/right
},
{
"~!@#$%^&*()_+",
"QWERTYUIOP{}|",
"\x10" "ASDFGHJKL:\"" "\x12",
"\x11" "ZXCVBNM<>? " "\x13"
}
};
struct osk_s
{
qboolean enable;
int curlayout;
qboolean shift;
qboolean sending;
struct {
signed char x;
signed char y;
char val;
} curbutton;
} osk;
static qboolean OSK_KeyEvent( int key, int down )
{
if( !osk.enable || !osk_enable.value )
return false;
if( osk.sending )
{
osk.sending = false;
return false;
}
if( osk.curbutton.val == 0 )
{
if( key == K_ENTER || key == K_A_BUTTON )
{
osk.curbutton.val = osk_keylayout[osk.curlayout][osk.curbutton.y][osk.curbutton.x];
return true;
}
return false;
}
switch ( key )
{
case K_A_BUTTON:
case K_ENTER:
switch( osk.curbutton.val )
{
case OSK_ENTER:
osk.sending = true;
Key_Event( K_ENTER, down );
//osk_enable = false; // TODO: handle multiline
break;
case OSK_SHIFT:
if( !down )
break;
if( osk.curlayout & 1 )
osk.curlayout--;
else
osk.curlayout++;
osk.shift = true;
osk.curbutton.val = osk_keylayout[osk.curlayout][osk.curbutton.y][osk.curbutton.x];
break;
case OSK_BACKSPACE:
Key_Event( K_BACKSPACE, down ); break;
case OSK_TAB:
Key_Event( K_TAB, down ); break;
default:
{
int ch;
if( !down )
{
if( osk.shift && osk.curlayout & 1 )
osk.curlayout--;
osk.shift = false;
osk.curbutton.val = osk_keylayout[osk.curlayout][osk.curbutton.y][osk.curbutton.x];
break;
}
ch = (byte)osk.curbutton.val;
// do not pass UTF-8 sequence into the engine, convert it here
if( !cls.accept_utf8 )
ch = Con_UtfProcessCharForce( ch );
if( !ch )
break;
CL_CharEvent( ch );
break;
}
}
break;
case K_UPARROW:
if( down && --osk.curbutton.y < 0 )
{
osk.curbutton.y = MAX_OSK_LINES - 1;
osk.curbutton.val = 0;
return true;
}
break;
case K_DOWNARROW:
if( down && ++osk.curbutton.y >= MAX_OSK_LINES )
{
osk.curbutton.y = 0;
osk.curbutton.val = 0;
return true;
}
break;
case K_LEFTARROW:
if( down && --osk.curbutton.x < 0 )
osk.curbutton.x = MAX_OSK_ROWS - 1;
break;
case K_RIGHTARROW:
if( down && ++osk.curbutton.x >= MAX_OSK_ROWS )
osk.curbutton.x = 0;
break;
default:
return false;
}
osk.curbutton.val = osk_keylayout[osk.curlayout][osk.curbutton.y][osk.curbutton.x];
return true;
}
/*
=============
OSK_EnableTextInput
Enables built-in IME
=============
*/
static void OSK_EnableTextInput( qboolean enable, qboolean force )
{
qboolean old = osk.enable;
osk.enable = enable;
if( osk.enable && (!old || force) )
{
osk.curlayout = 0;
osk.curbutton.val = osk_keylayout[osk.curlayout][osk.curbutton.y][osk.curbutton.x];
}
}
#define X_START 0.1347475f
#define Y_START 0.567f
#define X_STEP 0.05625
#define Y_STEP 0.0825
/*
============
Joy_DrawSymbolButton
Draw button with symbol on it
============
*/
static void OSK_DrawSymbolButton( int symb, float x, float y, float width, float height )
{
cl_font_t *font = Con_GetCurFont();
byte color[] = { 255, 255, 255, 255 };
int x1 = x * refState.width,
y1 = y * refState.height,
w = width * refState.width,
h = height * refState.height;
if( symb == osk.curbutton.val )
ref.dllFuncs.FillRGBA( kRenderTransTexture, x1, y1, w, h, 255, 160, 0, 100 );
if( !symb || symb == ' ' || (symb >= OSK_TAB && symb < OSK_SPECKEY_LAST ) )
return;
CL_DrawCharacter(
x1 + width * 0.4 * refState.width,
y1 + height * 0.4 * refState.height,
symb, color, font, 0 );
}
/*
=============
Joy_DrawSpecialButton
Draw special button, like shift, enter or esc
=============
*/
static void OSK_DrawSpecialButton( const char *name, float x, float y, float width, float height )
{
byte color[] = { 0, 255, 0, 255 };
Con_DrawString(
x * refState.width + width * 0.4 * refState.width,
y * refState.height + height * 0.4 * refState.height,
name,
color );
}
/*
=============
Joy_DrawOnScreenKeyboard
Draw on screen keyboard, if enabled
=============
*/
void OSK_Draw( void )
{
const char **curlayout = osk_keylayout[osk.curlayout]; // shortcut :)
float x, y;
int i, j;
if( !osk.enable || !osk_enable.value || !osk.curbutton.val )
return;
// draw keyboard
ref.dllFuncs.FillRGBA( kRenderTransTexture, X_START * refState.width, Y_START * refState.height,
X_STEP * MAX_OSK_ROWS * refState.width,
Y_STEP * MAX_OSK_LINES * refState.height, 100, 100, 100, 100 );
OSK_DrawSpecialButton( "-]", X_START, Y_START + Y_STEP * 2, X_STEP, Y_STEP );
OSK_DrawSpecialButton( "<-", X_START + X_STEP * 12, Y_START + Y_STEP * 2, X_STEP, Y_STEP );
OSK_DrawSpecialButton( "sh", X_START, Y_START + Y_STEP * 3, X_STEP, Y_STEP );
OSK_DrawSpecialButton( "en", X_START + X_STEP * 12, Y_START + Y_STEP * 3, X_STEP, Y_STEP );
for( y = Y_START, j = 0; j < MAX_OSK_LINES; j++, y += Y_STEP )
for( x = X_START, i = 0; i < MAX_OSK_ROWS; i++, x += X_STEP )
OSK_DrawSymbolButton( curlayout[j][i], x, y, X_STEP, Y_STEP );
}

View File

@@ -0,0 +1,312 @@
/*
in_osk.c - on-screen keyboard input
Copyright (C) 2016-2026 Alibek Omarov
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 "input.h"
#include "client.h"
CVAR_DEFINE_AUTO( osk_enable, "0", FCVAR_ARCHIVE | FCVAR_FILTERABLE, "enable built-in on-screen keyboard" );
/* On-screen keyboard:
*
* 4 lines with 13 buttons each
* Left trigger == backspace
* Right trigger == space
* Any button press is button press on keyboard
*
* Our layout:
* 0 1 2 3 4 5 6 7 8 9 10 11 12
* +--+--+--+--+--+--+--+--+--+--+--+--+--+
* |` |1 |2 |3 |4 |5 |6 |7 |8 |9 |0 |- |= | 0
* +--+--+--+--+--+--+--+--+--+--+--+--+--+
* |q |w |e |r |t |y |u |i |o |p |[ |] |\ | 1
* +--+--+--+--+--+--+--+--+--+--+--+--+--+
* |CL|a |s |d |f |g |h |j |k |l |; |' |BS| 2
* +--+--+--+--+--+--+--+--+--+--+--+--+--+
* |SH|z |x |c |v |b |n |m |, |. |/ |SP|EN| 3
* +--+--+--+--+--+--+--+--+--+--+--+--+--+
*/
#define MAX_OSK_ROWS 13
#define MAX_OSK_LINES 4
enum
{
OSK_DEFAULT = 0,
OSK_UPPER, // on caps, shift
/*
OSK_RUSSIAN,
OSK_RUSSIAN_UPPER,
*/
OSK_LAST
};
enum
{
OSK_TAB = 16,
OSK_SHIFT,
OSK_BACKSPACE,
OSK_ENTER,
OSK_SPECKEY_LAST
};
static const char *osk_keylayout[][4] =
{
{
"`1234567890-=", // 13
"qwertyuiop[]\\", // 13
"\x10" "asdfghjkl;'" "\x12", // 11 + caps on a left, enter on a right
"\x11" "zxcvbnm,./ " "\x13" // 10 + esc on left + shift on a left/right
},
{
"~!@#$%^&*()_+",
"QWERTYUIOP{}|",
"\x10" "ASDFGHJKL:\"" "\x12",
"\x11" "ZXCVBNM<>? " "\x13"
}
};
static struct osk_s
{
qboolean enable;
int curlayout;
qboolean shift;
qboolean sending;
struct
{
signed char x;
signed char y;
char val;
} curbutton;
} osk;
qboolean OSK_KeyEvent( int key, int down )
{
if( !osk.enable || !osk_enable.value )
return false;
if( osk.sending )
{
osk.sending = false;
return false;
}
if( osk.curbutton.val == 0 )
{
if( key == K_ENTER || key == K_A_BUTTON )
{
osk.curbutton.val = osk_keylayout[osk.curlayout][osk.curbutton.y][osk.curbutton.x];
return true;
}
return false;
}
switch( key )
{
case K_A_BUTTON:
case K_ENTER:
switch( osk.curbutton.val )
{
case OSK_ENTER:
osk.sending = true;
Key_Event( K_ENTER, down );
// osk_enable = false; // TODO: handle multiline
break;
case OSK_SHIFT:
if( !down )
break;
if( osk.curlayout & 1 )
osk.curlayout--;
else
osk.curlayout++;
osk.shift = true;
osk.curbutton.val = osk_keylayout[osk.curlayout][osk.curbutton.y][osk.curbutton.x];
break;
case OSK_BACKSPACE:
Key_Event( K_BACKSPACE, down );
break;
case OSK_TAB:
Key_Event( K_TAB, down );
break;
default:
{
int ch;
if( !down )
{
if( osk.shift && osk.curlayout & 1 )
osk.curlayout--;
osk.shift = false;
osk.curbutton.val = osk_keylayout[osk.curlayout][osk.curbutton.y][osk.curbutton.x];
break;
}
ch = (byte)osk.curbutton.val;
// do not pass UTF-8 sequence into the engine, convert it here
if( !cls.accept_utf8 )
ch = Con_UtfProcessCharForce( ch );
if( !ch )
break;
CL_CharEvent( ch );
break;
}
}
break;
case K_UPARROW:
if( down && --osk.curbutton.y < 0 )
{
osk.curbutton.y = MAX_OSK_LINES - 1;
osk.curbutton.val = 0;
return true;
}
break;
case K_DOWNARROW:
if( down && ++osk.curbutton.y >= MAX_OSK_LINES )
{
osk.curbutton.y = 0;
osk.curbutton.val = 0;
return true;
}
break;
case K_LEFTARROW:
if( down && --osk.curbutton.x < 0 )
osk.curbutton.x = MAX_OSK_ROWS - 1;
break;
case K_RIGHTARROW:
if( down && ++osk.curbutton.x >= MAX_OSK_ROWS )
osk.curbutton.x = 0;
break;
default:
return false;
}
osk.curbutton.val = osk_keylayout[osk.curlayout][osk.curbutton.y][osk.curbutton.x];
return true;
}
/*
=============
OSK_EnableTextInput
Enables built-in IME
=============
*/
void OSK_EnableTextInput( qboolean enable, qboolean force )
{
qboolean old = osk.enable;
osk.enable = enable;
if( osk.enable && ( !old || force ))
{
osk.curlayout = 0;
osk.curbutton.val = osk_keylayout[osk.curlayout][osk.curbutton.y][osk.curbutton.x];
}
}
#define X_START 0.1347475f
#define Y_START 0.567f
#define X_STEP 0.05625
#define Y_STEP 0.0825
/*
============
OSK_DrawSymbolButton
Draw button with symbol on it
============
*/
static void OSK_DrawSymbolButton( int symb, float x, float y, float width, float height )
{
cl_font_t *font = Con_GetCurFont();
byte color[] = { 255, 255, 255, 255 };
int x1 = x * refState.width,
y1 = y * refState.height,
w = width * refState.width,
h = height * refState.height;
if( symb == osk.curbutton.val )
ref.dllFuncs.FillRGBA( kRenderTransTexture, x1, y1, w, h, 255, 160, 0, 100 );
if( !symb || symb == ' ' || ( symb >= OSK_TAB && symb < OSK_SPECKEY_LAST ))
return;
CL_DrawCharacter(
x1 + width * 0.4 * refState.width,
y1 + height * 0.4 * refState.height,
symb, color, font, 0 );
}
/*
=============
OSK_DrawSpecialButton
Draw special button, like shift, enter or esc
=============
*/
static void OSK_DrawSpecialButton( const char *name, float x, float y, float width, float height )
{
byte color[] = { 0, 255, 0, 255 };
Con_DrawString(
x * refState.width + width * 0.4 * refState.width,
y * refState.height + height * 0.4 * refState.height,
name,
color );
}
/*
=============
OSK_Draw
Draw on screen keyboard, if enabled
=============
*/
void OSK_Draw( void )
{
const char **curlayout = osk_keylayout[osk.curlayout]; // shortcut :)
float x, y;
int i, j;
if( !osk.enable || !osk_enable.value || !osk.curbutton.val )
return;
// draw keyboard
ref.dllFuncs.FillRGBA( kRenderTransTexture, X_START * refState.width, Y_START * refState.height,
X_STEP * MAX_OSK_ROWS * refState.width,
Y_STEP * MAX_OSK_LINES * refState.height, 100, 100, 100, 100 );
OSK_DrawSpecialButton( "-]", X_START, Y_START + Y_STEP * 2, X_STEP, Y_STEP );
OSK_DrawSpecialButton( "<-", X_START + X_STEP * 12, Y_START + Y_STEP * 2, X_STEP, Y_STEP );
OSK_DrawSpecialButton( "sh", X_START, Y_START + Y_STEP * 3, X_STEP, Y_STEP );
OSK_DrawSpecialButton( "en", X_START + X_STEP * 12, Y_START + Y_STEP * 3, X_STEP, Y_STEP );
for( y = Y_START, j = 0; j < MAX_OSK_LINES; j++, y += Y_STEP )
for( x = X_START, i = 0; i < MAX_OSK_ROWS; i++, x += X_STEP )
OSK_DrawSymbolButton( curlayout[j][i], x, y, X_STEP, Y_STEP );
}
void OSK_Init( void )
{
Cvar_RegisterVariable( &osk_enable );
}

View File

@@ -185,10 +185,10 @@ void Touch_NotifyResize( void )
static inline float Touch_AspectRatio( void )
{
if( touch.config_aspect_ratio )
if( touch.config_aspect_ratio >= 0.25f )
return touch.config_aspect_ratio;
if( touch.actual_aspect_ratio )
if( touch.actual_aspect_ratio >= 0.25f )
return touch.actual_aspect_ratio;
if( refState.width && refState.height )
@@ -369,7 +369,7 @@ static void Touch_ExportConfig_f( void )
string profilebase;
COM_FileBase( name, profilebase, sizeof( profilebase ));
Q_snprintf( profilename, sizeof( profilebase ), "touch_profiles/%s (copy).cfg", profilebase );
Q_snprintf( profilename, sizeof( profilename ), "touch_profiles/%s (copy).cfg", profilebase );
}
else Q_strncpy( profilename, name, sizeof( profilename ));
@@ -588,7 +588,7 @@ static void Touch_RemoveButtonFromList( touchbuttonlist_t *list, const char *nam
IN_TouchEditClear();
while(( button = Touch_FindFirst( &touch.list_user, name, privileged )))
while(( button = Touch_FindFirst( list, name, privileged )))
{
if( button->prev )
button->prev->next = button->next;
@@ -860,8 +860,7 @@ void Touch_AddClientButton( const char *name, const char *texture, const char *c
if( !touch.initialized )
return;
if( round )
IN_TouchCheckCoords( &x1, &y1, &x2, &y2 );
IN_TouchCheckCoords( &x1, &y1, &x2, &y2 );
if( round == round_aspect )
y2 = y1 + ( x2 - x1 ) / (Touch_AspectRatio()) * aspect;
@@ -1068,7 +1067,7 @@ static void Touch_InitEditor( void )
temp = Touch_AddButton( &touch.list_edit, "close", "touch_default/edit_close", "touch_disableedit", 0, y, x, y + 0.1f, color, true );
SetBits( temp->flags, TOUCH_FL_NOEDIT );
temp = Touch_AddButton( &touch.list_edit, "close", "#Close and save", "", x, y, x + 0.2f, y + 0.1f, color, true );
temp = Touch_AddButton( &touch.list_edit, "close_label", "#Close and save", "", x, y, x + 0.2f, y + 0.1f, color, true );
SetBits( temp->flags, TOUCH_FL_NOEDIT );
y += 0.2f;
@@ -1076,7 +1075,7 @@ static void Touch_InitEditor( void )
temp = Touch_AddButton( &touch.list_edit, "cancel", "touch_default/edit_reset", "touch_reloadconfig", 0, y, x, y + 0.1f, color, true );
SetBits( temp->flags, TOUCH_FL_NOEDIT );
temp = Touch_AddButton( &touch.list_edit, "close", "#Cancel and reset", "", x, y, x + 0.2f, y + 0.1f, color, true );
temp = Touch_AddButton( &touch.list_edit, "cancel_label", "#Cancel and reset", "", x, y, x + 0.2f, y + 0.1f, color, true );
SetBits( temp->flags, TOUCH_FL_NOEDIT );
y += 0.2f;
@@ -1256,12 +1255,21 @@ static void Touch_DrawTexture( float x1, float y1, float x2, float y2, int textu
0, 0, 1, 1, texture );
}
#define GRID_COUNT_X ((int)touch_grid_count.value )
#define GRID_COUNT_Y (((int)touch_grid_count.value ) * Touch_AspectRatio( ))
#define GRID_X ( 1.0f / GRID_COUNT_X )
#define GRID_Y ( 1.0f / Touch_AspectRatio() / GRID_COUNT_X )
#define GRID_ROUND_X( x ) ((float)round(( x ) * GRID_COUNT_X ) / GRID_COUNT_X )
#define GRID_ROUND_Y( x ) ((float)round(( x ) * GRID_COUNT_Y ) / GRID_COUNT_Y )
static inline int Touch_GridCountX( void )
{
return Q_max((int)touch_grid_count.value, 1 );
}
static inline int Touch_GridCountY( void )
{
float grid_count_y = touch_grid_count.value * Touch_AspectRatio();
return Q_max((int)grid_count_y, 1 );
}
#define GRID_X ( 1.0f / Touch_GridCountX( ))
#define GRID_Y ( 1.0f / Touch_GridCountY( ))
#define GRID_ROUND_X( x ) ((float)round(( x ) * Touch_GridCountX() ) / Touch_GridCountX())
#define GRID_ROUND_Y( x ) ((float)round(( x ) * Touch_GridCountY() ) / Touch_GridCountY())
static void IN_TouchCheckCoords( float *x1, float *y1, float *x2, float *y2 )
{
@@ -2189,12 +2197,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

@@ -445,6 +445,10 @@ void IN_Init( void )
{
IN_StartupMouse( );
IN_GyroInit();
OSK_Init();
Joy_Init(); // common joystick support init
Touch_Init();
@@ -560,8 +564,9 @@ 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 );
IN_GyroFinalizeMove( forward, side, pitch, yaw );
Joy_FinalizeMove( forward, side, pitch, yaw );
Touch_GetMove( forward, side, pitch, yaw );
if( look_filter.value )
{

View File

@@ -15,6 +15,7 @@ GNU General Public License for more details.
#include "common.h"
#include "client.h"
#include "mod_local.h"
#include "net_encode.h"
#include "cl_tent.h"
#include "shake.h"
@@ -42,7 +43,7 @@ CL_ParseViewEntity
==================
*/
void CL_ParseViewEntity( sizebuf_t *msg )
static void CL_ParseViewEntity( sizebuf_t *msg )
{
cl.viewentity = MSG_ReadWord( msg );
@@ -161,7 +162,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 +210,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 +363,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 +617,7 @@ CL_ParseCustomization
==================
*/
void CL_ParseCustomization( sizebuf_t *msg )
static void CL_ParseCustomization( sizebuf_t *msg )
{
customization_t *pExistingCustomization;
customization_t *pList;
@@ -704,13 +705,35 @@ CL_ParseResourceRequest
==================
*/
void CL_ParseResourceRequest( sizebuf_t *msg )
void CL_SendResourceList( const resource_t *list, int count )
{
byte buffer[MAX_INIT_MSG];
int i, arg, nStartIndex;
sizebuf_t sbuf;
int i;
MSG_Init( &sbuf, "ResourceBlock", buffer, sizeof( buffer ));
MSG_BeginClientCmd( &sbuf, clc_resourcelist );
MSG_WriteShort( &sbuf, count );
for( i = 0; i < count; i++ )
{
MSG_WriteString( &sbuf, list[i].szFileName );
MSG_WriteByte( &sbuf, list[i].type );
MSG_WriteShort( &sbuf, list[i].nIndex );
MSG_WriteLong( &sbuf, list[i].nDownloadSize );
MSG_WriteByte( &sbuf, list[i].ucFlags );
if( FBitSet( list[i].ucFlags, RES_CUSTOM ))
MSG_WriteBytes( &sbuf, list[i].rgucMD5_hash, 16 );
}
Netchan_CreateFragments( &cls.netchan, &sbuf );
Netchan_FragSend( &cls.netchan );
}
static void CL_ParseResourceRequest( sizebuf_t *msg )
{
int arg, nStartIndex;
arg = MSG_ReadLong( msg );
nStartIndex = MSG_ReadLong( msg );
@@ -718,31 +741,10 @@ void CL_ParseResourceRequest( sizebuf_t *msg )
if( cl.servercount != arg )
return;
if( nStartIndex < 0 && nStartIndex > cl.num_resources )
if( nStartIndex < 0 || nStartIndex >= cl.num_resources )
return;
MSG_BeginClientCmd( &sbuf, clc_resourcelist );
MSG_WriteShort( &sbuf, cl.num_resources );
for( i = nStartIndex; i < cl.num_resources; i++ )
{
MSG_WriteString( &sbuf, cl.resourcelist[i].szFileName );
MSG_WriteByte( &sbuf, cl.resourcelist[i].type );
MSG_WriteShort( &sbuf, cl.resourcelist[i].nIndex );
MSG_WriteLong( &sbuf, cl.resourcelist[i].nDownloadSize );
MSG_WriteByte( &sbuf, cl.resourcelist[i].ucFlags );
if( FBitSet( cl.resourcelist[i].ucFlags, RES_CUSTOM ))
MSG_WriteBytes( &sbuf, cl.resourcelist[i].rgucMD5_hash, 16 );
}
// a1ba: useless check? MSG_BeginClientCmd and MSG_WriteShort will always
// write to the buffer
// if( MSG_GetNumBytesWritten( &sbuf ) > 0 )
{
Netchan_CreateFragments( &cls.netchan, &sbuf );
Netchan_FragSend( &cls.netchan );
}
CL_SendResourceList( &cl.resourcelist[nStartIndex], cl.num_resources - nStartIndex );
}
/*
@@ -776,7 +778,7 @@ CL_ParseFileTransferFailed
==================
*/
void CL_ParseFileTransferFailed( sizebuf_t *msg )
static void CL_ParseFileTransferFailed( sizebuf_t *msg )
{
const char *name = MSG_ReadString( msg );
@@ -796,7 +798,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 +1236,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 +1257,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 +1269,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 +1298,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 +1312,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 +1339,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 +1377,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;
@@ -1738,6 +1740,8 @@ void CL_RegisterResources( sizebuf_t *msg, connprotocol_t proto )
// tell rendering system we have a new set of models.
ref.dllFuncs.R_NewMap ();
Mod_LoadDetailTextures( cl.worldmodel );
// check if this map must start from dark screen
CL_StartDark ();
@@ -1898,7 +1902,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 +1916,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 +1966,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 +1995,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 +2029,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 +2123,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 +2185,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 +2361,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 +2513,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 +2525,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 +2594,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 +2616,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 +2672,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 +2689,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 +2706,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 +2719,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

@@ -514,17 +514,26 @@ static void CL_ParseSoundPacketGS( sizebuf_t *msg )
static void CL_ParseSpawnStaticSound( sizebuf_t *msg )
{
vec3_t pos;
int handle, entnum, pitch, flags;
int sound, entnum, pitch, flags;
float volume, attn;
sound_t handle = 0;
MSG_ReadVec3Coord( msg, pos );
handle = MSG_ReadShort( msg );
sound = MSG_ReadShort( msg );
volume = MSG_ReadByte( msg ) * ( 1.0f / 255.0f );
attn = MSG_ReadByte( msg ) * ( 1.0f / 64.0f );
entnum = MSG_ReadShort( msg );
pitch = MSG_ReadByte( msg );
flags = MSG_ReadByte( msg );
if( FBitSet( flags, SND_SENTENCE ))
{
char sentenceName[32];
Q_snprintf( sentenceName, sizeof( sentenceName ), "!%i", sound );
handle = S_RegisterSound( sentenceName );
}
else handle = cl.sound_index[sound]; // see precached sound
S_AmbientSound( pos, entnum, handle, volume, attn, pitch, flags );
}
@@ -569,22 +578,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 +605,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 +628,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 +646,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 +668,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 +676,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 +685,6 @@ void CL_ParseGoldSrcServerMessage( sizebuf_t *msg )
Con_Reportf( S_ERROR "%s: svc_goldsrc_timescale: implement me!\n", __func__ );
MSG_ReadFloat( msg );
break;
case svc_querycvarvalue:
CL_ParseCvarValue( msg, false, PROTO_GOLDSRC );
break;
case svc_querycvarvalue2:
CL_ParseCvarValue( msg, true, PROTO_GOLDSRC );
break;
case svc_exec:
CL_ParseExec( msg );
break;
default:
CL_ParseUserMessage( msg, cmd, PROTO_GOLDSRC );
cl.frames[cl.parsecountmod].graphdata.usr += MSG_GetNumBytesRead( msg ) - bufStart;

View File

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

View File

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

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

File diff suppressed because it is too large Load Diff

View File

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

View File

@@ -92,7 +92,7 @@ void S_StartBackgroundTrack( const char *introTrack, const char *mainTrack, int
{
S_StopBackgroundTrack();
if( !dma.initialized ) return;
if( !snd.initialized ) return;
// check for special symbols
if( introTrack && *introTrack == '*' )
@@ -132,9 +132,9 @@ S_StopBackgroundTrack
*/
void S_StopBackgroundTrack( void )
{
s_listener.stream_paused = false;
snd.stream_paused = false;
if( !dma.initialized ) return;
if( !snd.initialized ) return;
if( !s_bgTrack.stream ) return;
FS_FreeStream( s_bgTrack.stream );
@@ -149,7 +149,7 @@ S_StreamSetPause
*/
void S_StreamSetPause( int pause )
{
s_listener.stream_paused = pause;
snd.stream_paused = pause;
}
/*
@@ -197,11 +197,11 @@ void S_StreamBackgroundTrack( void )
int r, fileBytes;
rawchan_t *ch = NULL;
if( !dma.initialized || !s_bgTrack.stream || s_listener.streaming )
if( !snd.initialized || !s_bgTrack.stream || snd.streaming )
return;
// don't bother playing anything if musicvolume is 0
if( !s_musicvolume.value || cl.paused || s_listener.stream_paused )
if( !s_musicvolume.value || cl.paused || snd.stream_paused )
return;
if( !cl.background )
@@ -218,14 +218,14 @@ void S_StreamBackgroundTrack( void )
Assert( ch != NULL );
// see how many samples should be copied into the raw buffer
if( ch->s_rawend < soundtime )
ch->s_rawend = soundtime;
if( ch->s_rawend < snd.soundtime )
ch->s_rawend = snd.soundtime;
while( ch->s_rawend < soundtime + ch->max_samples )
while( ch->s_rawend < snd.soundtime + ch->max_samples )
{
const stream_t *info = s_bgTrack.stream;
bufferSamples = ch->max_samples - (ch->s_rawend - soundtime);
bufferSamples = ch->max_samples - (ch->s_rawend - snd.soundtime);
// decide how much data needs to be read from the file
fileSamples = bufferSamples * ((float)info->rate / SOUND_DMA_SPEED );
@@ -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,
};
}
@@ -406,29 +403,34 @@ static qboolean VOX_ParseWordParams( char *psz, voxword_t *pvoxword, voxword_t *
command = *psz++;
if( !isdigit( *psz ))
if( !isdigit((byte)*psz ))
break;
memset( sznum, 0, sizeof( sznum ));
for( i = 0; i < sizeof( sznum ) - 1 && isdigit( *psz ); i++, psz++ )
for( i = 0; i < sizeof( sznum ) - 1 && isdigit((byte)*psz ); i++, psz++ )
sznum[i] = *psz;
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

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

@@ -157,7 +157,7 @@ qboolean Sound_LoadOggVorbis( const char *name, const byte *buffer, fs_offset_t
SetBits( sound.flags, SOUND_RESAMPLE );
Sound_ScanVorbisComments( &vorbisFile );
while(( ret = ov_read( &vorbisFile, (char *)sound.wav + written, sound.size - written, 0, sound.width, 1, &section )) != 0 )
while(( ret = ov_read( &vorbisFile, (char *)sound.wav + written, sound.size - written, XASH_BIG_ENDIAN, sound.width, 1, &section )) != 0 )
{
if( ret < 0 )
{
@@ -232,7 +232,7 @@ int Stream_ReadOggVorbis( stream_t *stream, int needBytes, void *buffer )
if( !stream->buffsize )
{
stream->pos = ov_read( &ctx->vf, (char *)stream->temp, OUTBUF_SIZE, 0, stream->width, 1, &section );
stream->pos = ov_read( &ctx->vf, (char *)stream->temp, OUTBUF_SIZE, XASH_BIG_ENDIAN, stream->width, 1, &section );
if( stream->pos == 0 )
{
break; // end of file

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;
@@ -152,6 +152,8 @@ static qboolean StreamFindNextChunk( file_t *file, const char *name, int *last_c
if( FS_Read( file, &iff_chunk_len, sizeof( iff_chunk_len )) != sizeof( iff_chunk_len ))
return false;
iff_chunk_len = LittleLong( iff_chunk_len );
if( iff_chunk_len < 0 )
return false; // didn't find the chunk
@@ -317,6 +319,15 @@ qboolean Sound_LoadWAV( const char *name, const byte *buffer, fs_offset_t filesi
memcpy( sound.wav, buffer + (iff_dataPtr - buffer), sound.size );
// swap 16-bit samples from little endian to native
if( sound.width == 2 )
{
short *p = (short *)sound.wav;
int count = sound.size / 2;
for( int i = 0; i < count; i++ )
p[i] = LittleShort( p[i] );
}
// now convert 8-bit sounds to signed
if( sound.width == 1 )
{
@@ -394,6 +405,7 @@ stream_t *Stream_OpenWAV( const char *filename )
FS_Read( file, chunkName, 4 );
FS_Read( file, &t, sizeof( t ));
t = LittleShort( t );
if( t != 1 )
{
Con_DPrintf( S_ERROR "%s: %s not a microsoft PCM format\n", __func__, filename );
@@ -402,14 +414,15 @@ stream_t *Stream_OpenWAV( const char *filename )
}
FS_Read( file, &t, sizeof( t ));
sound.channels = t;
sound.channels = LittleShort( t );
FS_Read( file, &sound.rate, sizeof( int ));
sound.rate = LittleLong( sound.rate );
FS_Seek( file, 6, SEEK_CUR );
FS_Read( file, &t, sizeof( t ));
sound.width = t / 8;
sound.width = LittleShort( t ) / 8;
sound.loopstart = 0;
@@ -423,7 +436,7 @@ stream_t *Stream_OpenWAV( const char *filename )
}
FS_Read( file, &sound.samples, sizeof( int ));
sound.samples = ( sound.samples / sound.width ) / sound.channels;
sound.samples = ( LittleLong( sound.samples ) / sound.width ) / sound.channels;
// at this point we have valid stream
stream = Mem_Calloc( host.soundpool, sizeof( stream_t ));
@@ -458,6 +471,14 @@ int Stream_ReadWAV( stream_t *stream, int bytes, void *buffer )
stream->pos += bytes;
FS_Read( stream->file, buffer, bytes );
if( stream->width == 2 )
{
short *p = (short *)buffer;
int count = bytes / 2;
for( int i = 0; i < count; i++ )
p[i] = LittleShort( p[i] );
}
return bytes;
}

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;
}
/*
@@ -610,8 +615,11 @@ void Cmd_TokenizeString( const char *text )
if( !*text )
return;
if( cmd_argc == 1 )
cmd_args = text;
switch( cmd_argc )
{
case 0: cmd_args = ""; break;
case 1: cmd_args = text; break;
}
text = COM_ParseFileSafe( (char*)text, cmd_token, sizeof( cmd_token ), PFILE_IGNOREBRACKET, NULL, NULL );
@@ -1234,26 +1242,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 +1320,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 +1457,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

@@ -227,7 +227,7 @@ qboolean LZSS_IsCompressed( const byte *source, size_t input_len )
phdr = (const lzss_header_t *)source;
if( phdr && phdr->id == LZSS_ID )
if( phdr && phdr->id == LittleLong( LZSS_ID ))
return true;
return false;
}
@@ -241,8 +241,8 @@ uint LZSS_GetActualSize( const byte *source, size_t input_len )
phdr = (const lzss_header_t *)source;
if( phdr && phdr->id == LZSS_ID )
return phdr->size;
if( phdr && phdr->id == LittleLong( LZSS_ID ))
return LittleLong( phdr->size );
return 0;
}
@@ -296,8 +296,8 @@ static byte *LZSS_CompressNoAlloc( lzss_state_t *state, byte *pInput, int input_
return NULL;
// set LZSS header
header->id = LZSS_ID;
header->size = input_length;
header->id = LittleLong( LZSS_ID );
header->size = LittleLong( input_length );
// create the compression work buffers, small enough (~64K) for stack
state->hash_table = (lzss_list_t *)alloca( 256 * sizeof( lzss_list_t ));
@@ -961,14 +961,14 @@ static void Test_LZSS( void )
};
const char decompressed[] = "Do you like what you see?";
#ifdef USING_ASAN
#ifdef USE_ASAN
ASAN_POISON_MEMORY_REGION( poison1, sizeof( poison1 ));
ASAN_POISON_MEMORY_REGION( poison2, sizeof( poison2 ));
ASAN_POISON_MEMORY_REGION( poison3, sizeof( poison3 ));
#endif
hdr->size = sizeof( in ) - sizeof( *hdr );
hdr->id = LZSS_ID;
hdr->size = LittleLong( sizeof( in ) - sizeof( *hdr ));
hdr->id = LittleLong( LZSS_ID );
memset( in + sizeof( *hdr ), 0xff, sizeof( in ) - sizeof( *hdr ));
result = LZSS_Decompress( in, out, sizeof( in ), sizeof( out ));
@@ -978,18 +978,18 @@ static void Test_LZSS( void )
result = LZSS_Decompress( in, out, sizeof( in ), sizeof( out ));
TASSERT_EQi( result, 0 );
hdr->size = 1;
hdr->id = LZSS_ID;
hdr->size = LittleLong( 1 );
hdr->id = LittleLong( LZSS_ID );
result = LZSS_Decompress( in, out, sizeof( in ), sizeof( out ));
TASSERT_EQi( result, 0 );
hdr->size = 999;
hdr->id = LZSS_ID;
hdr->size = LittleLong( 999 );
hdr->id = LittleLong( LZSS_ID );
result = LZSS_Decompress( in, out, sizeof( in ), sizeof( out ));
TASSERT_EQi( result, 0 );
hdr->size = sizeof( in ) - sizeof( *hdr );
hdr->id = 0xa1ba;
hdr->size = LittleLong( sizeof( in ) - sizeof( *hdr ));
hdr->id = LittleLong( 0xa1ba );
result = LZSS_Decompress( in, out, sizeof( in ), sizeof( out ));
TASSERT_EQi( result, 0 );

View File

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

View File

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

View File

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

View File

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

View File

@@ -16,6 +16,24 @@ GNU General Public License for more details.
#include "imagelib.h"
#include "xash3d_mathlib.h"
#include "img_bmp.h"
#include "swaplib.h"
le_struct_begin( bmp_swap )
le_struct_field( bmp_t, fileSize )
le_struct_field( bmp_t, reserved0 )
le_struct_field( bmp_t, bitmapDataOffset )
le_struct_field( bmp_t, bitmapHeaderSize )
le_struct_field( bmp_t, width )
le_struct_field( bmp_t, height )
le_struct_field( bmp_t, planes )
le_struct_field( bmp_t, bitsPerPixel )
le_struct_field( bmp_t, compression )
le_struct_field( bmp_t, bitmapDataSize )
le_struct_field( bmp_t, hRes )
le_struct_field( bmp_t, vRes )
le_struct_field( bmp_t, colors )
le_struct_field( bmp_t, importantColors )
le_struct_end();
/*
=============
@@ -41,6 +59,7 @@ qboolean Image_LoadBMP( const char *name, const byte *buffer, fs_offset_t filesi
buf_p = (byte *)buffer;
memcpy( &bhdr, buf_p, sizeof( bmp_t ));
le_struct_swap( bmp_swap, &bhdr );
buf_p += BI_FILE_HEADER_SIZE + bhdr.bitmapHeaderSize;
// bogus file header check
@@ -282,7 +301,8 @@ qboolean Image_LoadBMP( const char *name, const byte *buffer, fs_offset_t filesi
}
break;
case 16:
shortPixel = *(word *)buf_p, buf_p += 2;
shortPixel = buf_p[0] | (buf_p[1] << 8);
buf_p += 2;
*pixbuf++ = blue = (shortPixel & ( 31 << 10 )) >> 7;
*pixbuf++ = green = (shortPixel & ( 31 << 5 )) >> 2;
*pixbuf++ = red = (shortPixel & ( 31 )) << 3;
@@ -396,7 +416,9 @@ qboolean Image_SaveBMP( const char *name, rgbdata_t *pix )
hdr.colors = ( pixel_size == 1 ) ? 256 : 0;
hdr.importantColors = 0;
le_struct_swap( bmp_swap, &hdr );
FS_Write( pfile, &hdr, sizeof( bmp_t ));
le_struct_swap( bmp_swap, &hdr );
pbBmpBits = Mem_Malloc( host.imagepool, cbBmpBits );

View File

@@ -16,6 +16,49 @@ GNU General Public License for more details.
#include "imagelib.h"
#include "xash3d_mathlib.h"
#include "img_dds.h"
#include "swaplib.h"
le_struct_begin( dds_pixf_swap )
le_struct_field( dds_pixf_t, dwSize )
le_struct_field( dds_pixf_t, dwFlags )
le_struct_field( dds_pixf_t, dwFourCC )
le_struct_field( dds_pixf_t, dwRGBBitCount )
le_struct_field( dds_pixf_t, dwRBitMask )
le_struct_field( dds_pixf_t, dwGBitMask )
le_struct_field( dds_pixf_t, dwBBitMask )
le_struct_field( dds_pixf_t, dwABitMask )
le_struct_end();
le_struct_begin( dds_caps_swap )
le_struct_field( dds_caps_t, dwCaps1 )
le_struct_field( dds_caps_t, dwCaps2 )
le_struct_field( dds_caps_t, dwCaps3 )
le_struct_field( dds_caps_t, dwCaps4 )
le_struct_end();
le_struct_begin( dds_swap )
le_struct_field( dds_t, dwIdent )
le_struct_field( dds_t, dwSize )
le_struct_field( dds_t, dwFlags )
le_struct_field( dds_t, dwHeight )
le_struct_field( dds_t, dwWidth )
le_struct_field( dds_t, dwLinearSize )
le_struct_field( dds_t, dwDepth )
le_struct_field( dds_t, dwMipMapCount )
le_struct_field( dds_t, dwAlphaBitDepth )
le_struct_array( dds_t, dwReserved1, 10 )
le_struct_child( dds_t, dsPixelFormat, dds_pixf_swap )
le_struct_child( dds_t, dsCaps, dds_caps_swap )
le_struct_field( dds_t, dwTextureStage )
le_struct_end();
le_struct_begin( dds_dxt10_swap )
le_struct_field( dds_header_dxt10_t, dxgiFormat )
le_struct_field( dds_header_dxt10_t, resourceDimension )
le_struct_field( dds_header_dxt10_t, miscFlag )
le_struct_field( dds_header_dxt10_t, arraySize )
le_struct_field( dds_header_dxt10_t, miscFlags2 )
le_struct_end();
static qboolean Image_CheckDXT3Alpha( dds_t *hdr, byte *fin )
{
@@ -295,6 +338,7 @@ qboolean Image_LoadDDS( const char *name, const byte *buffer, fs_offset_t filesi
return false;
memcpy( &header, buffer, sizeof( header ));
le_struct_swap( dds_swap, &header );
if( header.dwIdent != DDSHEADER )
return false; // it's not a dds file, just skip it
@@ -315,6 +359,7 @@ qboolean Image_LoadDDS( const char *name, const byte *buffer, fs_offset_t filesi
if( header.dsPixelFormat.dwFourCC == TYPE_DX10 )
{
memcpy( &header2, buffer + sizeof( header ), sizeof( header2 ));
le_struct_swap( dds_dxt10_swap, &header2 );
headersOffset += sizeof( header2 );
}

View File

@@ -16,6 +16,34 @@ GNU General Public License for more details.
#include "imagelib.h"
#include "xash3d_mathlib.h"
#include "img_ktx2.h"
#include "swaplib.h"
le_struct_begin( ktx2_header_swap )
le_struct_field( ktx2_header_t, vkFormat )
le_struct_field( ktx2_header_t, typeSize )
le_struct_field( ktx2_header_t, pixelWidth )
le_struct_field( ktx2_header_t, pixelHeight )
le_struct_field( ktx2_header_t, pixelDepth )
le_struct_field( ktx2_header_t, layerCount )
le_struct_field( ktx2_header_t, faceCount )
le_struct_field( ktx2_header_t, levelCount )
le_struct_field( ktx2_header_t, supercompressionScheme )
le_struct_end();
le_struct_begin( ktx2_index_swap )
le_struct_field( ktx2_index_t, dfdByteOffset )
le_struct_field( ktx2_index_t, dfdByteLength )
le_struct_field( ktx2_index_t, kvdByteOffset )
le_struct_field( ktx2_index_t, kvdByteLength )
le_struct_field( ktx2_index_t, sgdByteOffset )
le_struct_field( ktx2_index_t, sgdByteLength )
le_struct_end();
le_struct_begin( ktx2_level_swap )
le_struct_field( ktx2_level_t, byteOffset )
le_struct_field( ktx2_level_t, byteLength )
le_struct_field( ktx2_level_t, uncompressedByteLength )
le_struct_end();
static void Image_KTX2Format( uint32_t ktx2_format )
{
@@ -125,6 +153,7 @@ static qboolean Image_KTX2Parse( const ktx2_header_t *header, const byte *buffer
}
memcpy( &index, buffer + KTX2_IDENTIFIER_SIZE + sizeof( ktx2_header_t ), sizeof( index ));
le_struct_swap( ktx2_index_swap, &index );
for( mip = 0; mip < header->levelCount; ++mip )
{
@@ -134,6 +163,7 @@ static qboolean Image_KTX2Parse( const ktx2_header_t *header, const byte *buffer
ktx2_level_t level;
memcpy( &level, levels_begin + mip * sizeof( level ), sizeof( level ));
le_struct_swap( ktx2_level_swap, &level );
if( mip_size != level.byteLength )
{
@@ -159,6 +189,7 @@ static qboolean Image_KTX2Parse( const ktx2_header_t *header, const byte *buffer
{
ktx2_level_t level;
memcpy( &level, levels_begin + mip * sizeof( level ), sizeof( level ));
le_struct_swap( ktx2_level_swap, &level );
memcpy( image.rgba + cursor, buffer + level.byteOffset, level.byteLength );
cursor += level.byteLength;
}
@@ -180,6 +211,7 @@ qboolean Image_LoadKTX2( const char *name, const byte *buffer, fs_offset_t files
}
memcpy( &header, buffer + KTX2_IDENTIFIER_SIZE, sizeof( header ));
le_struct_swap( ktx2_header_swap, &header );
image.width = header.pixelWidth;
image.height = header.pixelHeight;

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

@@ -16,6 +16,16 @@ GNU General Public License for more details.
#include "imagelib.h"
#include "xash3d_mathlib.h"
#include "img_tga.h"
#include "swaplib.h"
le_struct_begin( tga_swap )
le_struct_field( tga_t, colormap_index )
le_struct_field( tga_t, colormap_length )
le_struct_field( tga_t, x_origin )
le_struct_field( tga_t, y_origin )
le_struct_field( tga_t, width )
le_struct_field( tga_t, height )
le_struct_end();
/*
=============
@@ -44,10 +54,10 @@ qboolean Image_LoadTGA( const char *name, const byte *buffer, fs_offset_t filesi
targa_header.colormap_index = buf_p[0] + buf_p[1] * 256; buf_p += 2;
targa_header.colormap_length = buf_p[0] + buf_p[1] * 256; buf_p += 2;
targa_header.colormap_size = *buf_p; buf_p += 1;
targa_header.x_origin = *(short *)buf_p; buf_p += 2;
targa_header.y_origin = *(short *)buf_p; buf_p += 2;
targa_header.width = image.width = *(short *)buf_p; buf_p += 2;
targa_header.height = image.height = *(short *)buf_p; buf_p += 2;
targa_header.x_origin = buf_p[0] + buf_p[1] * 256; buf_p += 2;
targa_header.y_origin = buf_p[0] + buf_p[1] * 256; buf_p += 2;
targa_header.width = image.width = buf_p[0] + buf_p[1] * 256; buf_p += 2;
targa_header.height = image.height = buf_p[0] + buf_p[1] * 256; buf_p += 2;
targa_header.pixel_size = *buf_p++;
targa_header.attributes = *buf_p++;
if( targa_header.id_length != 0 ) buf_p += targa_header.id_length; // skip TARGA image comment
@@ -284,6 +294,7 @@ qboolean Image_SaveTGA( const char *name, rgbdata_t *pix )
out = buffer;
le_struct_swap( tga_swap, &targa_header );
memcpy( out, &targa_header, sizeof( tga_t ) );
out += sizeof( tga_t );

View File

@@ -285,66 +285,34 @@ int Image_ComparePalette( const byte *pal )
static void Image_SetPalette( const byte *pal, uint *d_table )
{
byte rgba[4];
uint uirgba; // TODO: palette looks byte-swapped on big-endian
int i;
// setup palette
switch( image.d_rendermode )
{
case LUMP_NORMAL:
for( i = 0; i < 256; i++ )
{
memcpy( rgba, &pal[i * 3], 3 );
rgba[3] = 0xFF;
memcpy( &uirgba, rgba, sizeof( uirgba ));
d_table[i] = uirgba;
}
for( int i = 0; i < 256; i++ )
d_table[i] = HostFourCC( pal[i * 3 + 0], pal[i * 3 + 1], pal[i * 3 + 2], 0xFF );
break;
case LUMP_TEXGAMMA:
for( i = 0; i < 256; i++ )
for( int i = 0; i < 256; i++ )
{
rgba[0] = TextureToGamma( pal[i * 3 + 0] );
rgba[1] = TextureToGamma( pal[i * 3 + 1] );
rgba[2] = TextureToGamma( pal[i * 3 + 2] );
rgba[3] = 0xFF;
memcpy( &uirgba, rgba, sizeof( uirgba ));
d_table[i] = uirgba;
d_table[i] = HostFourCC(
TextureToGamma( pal[i * 3 + 0] ),
TextureToGamma( pal[i * 3 + 1] ),
TextureToGamma( pal[i * 3 + 2] ),
0xFF );
}
break;
case LUMP_GRADIENT:
for( i = 0; i < 256; i++ )
{
rgba[0] = pal[765];
rgba[1] = pal[766];
rgba[2] = pal[767];
rgba[3] = i;
memcpy( &uirgba, rgba, sizeof( uirgba ));
d_table[i] = uirgba;
}
for( int i = 0; i < 256; i++ )
d_table[i] = HostFourCC( pal[765], pal[766], pal[767], i );
break;
case LUMP_MASKED:
for( i = 0; i < 255; i++ )
{
rgba[0] = pal[i*3+0];
rgba[1] = pal[i*3+1];
rgba[2] = pal[i*3+2];
rgba[3] = 0xFF;
memcpy( &uirgba, rgba, sizeof( uirgba ));
d_table[i] = uirgba;
}
for( int i = 0; i < 255; i++ )
d_table[i] = HostFourCC( pal[i * 3 + 0], pal[i * 3 + 1], pal[i * 3 + 2], 0xFF );
d_table[255] = 0;
break;
case LUMP_EXTENDED:
for( i = 0; i < 256; i++ )
{
rgba[0] = pal[i*4+0];
rgba[1] = pal[i*4+1];
rgba[2] = pal[i*4+2];
rgba[3] = pal[i*4+3];
memcpy( &uirgba, rgba, sizeof( uirgba ));
d_table[i] = uirgba;
}
for( int i = 0; i < 256; i++ )
d_table[i] = HostFourCC( pal[i * 4 + 0], pal[i * 4 + 1], pal[i * 4 + 2], pal[i * 4 + 3] );
break;
}
}

View File

@@ -19,6 +19,43 @@ GNU General Public License for more details.
#include "studio.h"
#include "sprite.h"
#include "qfont.h"
#include "swaplib.h"
le_struct_begin( charinfo_swap )
le_struct_field( charinfo, startoffset )
le_struct_field( charinfo, charwidth )
le_struct_end();
le_struct_begin( qfont_swap )
le_struct_field( qfont_t, width )
le_struct_field( qfont_t, height )
le_struct_field( qfont_t, rowcount )
le_struct_field( qfont_t, rowheight )
le_struct_array_child( qfont_t, fontinfo, charinfo_swap, NUM_GLYPHS )
le_struct_end();
le_struct_begin( lmp_swap )
le_struct_field( lmp_t, width )
le_struct_field( lmp_t, height )
le_struct_end();
le_struct_begin( mip_swap )
le_struct_field( mip_t, width )
le_struct_field( mip_t, height )
le_struct_array( mip_t, offsets, 4 )
le_struct_end();
le_struct_begin( dwadinfo_swap )
le_struct_field( dwadinfo_t, ident )
le_struct_field( dwadinfo_t, numlumps )
le_struct_field( dwadinfo_t, infotableofs )
le_struct_end();
le_struct_begin( dlumpinfo_swap )
le_struct_field( dlumpinfo_t, filepos )
le_struct_field( dlumpinfo_t, disksize )
le_struct_field( dlumpinfo_t, size )
le_struct_end();
/*
============
@@ -98,6 +135,7 @@ qboolean Image_LoadFNT( const char *name, const byte *buffer, fs_offset_t filesi
return false;
memcpy( &font, buffer, sizeof( font ));
le_struct_swap( qfont_swap, &font );
// last sixty four bytes - what the hell ????
size = sizeof( qfont_t ) - 4 + ( font.height * font.width * QCHAR_WIDTH ) + sizeof( short ) + 768 + 64;
@@ -120,7 +158,8 @@ qboolean Image_LoadFNT( const char *name, const byte *buffer, fs_offset_t filesi
fin = buffer + sizeof( font ) - 4;
pal = fin + (image.width * image.height);
numcolors = *(short *)pal, pal += sizeof( short );
numcolors = pal[0] | (pal[1] << 8);
pal += sizeof( short );
if( numcolors == 768 || numcolors == 256 )
{
@@ -250,6 +289,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;
@@ -303,6 +344,7 @@ qboolean Image_LoadLMP( const char *name, const byte *buffer, fs_offset_t filesi
{
fin = (byte *)buffer;
memcpy( &lmp, fin, sizeof( lmp ));
le_struct_swap( lmp_swap, &lmp );
image.width = lmp.width;
image.height = lmp.height;
rendermode = LUMP_NORMAL;
@@ -335,7 +377,7 @@ qboolean Image_LoadLMP( const char *name, const byte *buffer, fs_offset_t filesi
}
}
pal = fin + pixels;
numcolors = *(short *)pal;
numcolors = pal[0] | (pal[1] << 8);
if( numcolors != 256 ) pal = NULL; // corrupted lump ?
else pal += sizeof( short );
}
@@ -395,6 +437,8 @@ qboolean Image_LoadMIP( const char *name, const byte *buffer, fs_offset_t filesi
return false;
memcpy( &mip, buffer, sizeof( mip ));
if( !Image_CheckFlag( IL_HOST_ENDIAN ))
le_struct_swap( mip_swap, &mip );
image.width = mip.width;
image.height = mip.height;
@@ -409,7 +453,7 @@ qboolean Image_LoadMIP( const char *name, const byte *buffer, fs_offset_t filesi
// half-life 1.0.0.1 mip version with palette
fin = (byte *)buffer + mip.offsets[0];
pal = (byte *)buffer + mip.offsets[0] + (((image.width * image.height) * 85)>>6);
numcolors = *(short *)pal;
numcolors = pal[0] | (pal[1] << 8);
if( numcolors != 256 ) pal = NULL; // corrupted mip ?
else pal += sizeof( short ); // skip colorsize
@@ -470,7 +514,7 @@ qboolean Image_LoadMIP( const char *name, const byte *buffer, fs_offset_t filesi
// three checks here: check if we can load luma, check the texture name and, finally,
// validate that palette isn't NULL
if( Image_CheckFlag( IL_ALLOW_WAD3_LUMA )
&& ( mip.name[0] == '~' || ( mip.name[0] == '+' && isdigit( mip.name[1] ) && mip.name[2] == '~' ))
&& ( mip.name[0] == '~' || ( mip.name[0] == '+' && isdigit((byte)mip.name[1] ) && mip.name[2] == '~' ))
&& pal != NULL )
{
SetBits( image.flags, IMAGE_HAS_LUMA );
@@ -594,24 +638,23 @@ Image_LoadWAD
*/
qboolean Image_LoadWAD( const char *name, const byte *buffer, fs_offset_t filesize )
{
const dwadinfo_t *header;
const dlumpinfo_t *lumps;
dwadinfo_t whdr;
const unsigned char *mipdata;
int i, j;
if( !buffer || filesize < sizeof( dwadinfo_t ))
return false;
header = (const dwadinfo_t *)buffer;
if( header->numlumps <= 0 || header->infotableofs <= 0 || header->infotableofs >= (int)filesize )
memcpy( &whdr, buffer, sizeof( whdr ));
le_struct_swap( dwadinfo_swap, &whdr );
if( whdr.numlumps <= 0 || whdr.infotableofs <= 0 || whdr.infotableofs >= (int)filesize )
return false;
lumps = (const dlumpinfo_t *)((const unsigned char *)buffer + header->infotableofs );
for( i = 0; i < header->numlumps; ++i )
for( i = 0; i < whdr.numlumps; ++i )
{
const unsigned char *pixels, *palette, *use_palette;
unsigned char grad_palette[256 * 3];
dlumpinfo_t lump;
int mip_size;
mip_t mip;
uint32_t width, height, offset0;
@@ -621,16 +664,20 @@ qboolean Image_LoadWAD( const char *name, const byte *buffer, fs_offset_t filesi
float t;
byte idx;
if( lumps[i].type != TYP_MIPTEX && lumps[i].type != TYP_PALETTE )
memcpy( &lump, buffer + whdr.infotableofs + i * sizeof( dlumpinfo_t ), sizeof( lump ));
le_struct_swap( dlumpinfo_swap, &lump );
if( lump.type != TYP_MIPTEX && lump.type != TYP_PALETTE )
continue;
// get lump data and validate
mipdata = (const unsigned char *)buffer + lumps[i].filepos;
mip_size = lumps[i].disksize;
if( lumps[i].filepos < 0 || lumps[i].filepos + mip_size > (int)filesize )
mipdata = (const unsigned char *)buffer + lump.filepos;
mip_size = lump.disksize;
if( lump.filepos < 0 || lump.filepos + mip_size > (int)filesize )
continue;
memcpy( &mip, mipdata, sizeof( mip ));
le_struct_swap( mip_swap, &mip );
width = mip.width;
height = mip.height;
@@ -650,7 +697,7 @@ qboolean Image_LoadWAD( const char *name, const byte *buffer, fs_offset_t filesi
use_palette = palette;
// handle gradient palette
if( lumps[i].type == TYP_PALETTE )
if( lump.type == TYP_PALETTE )
{
// gradient palette
const unsigned char *frontColorPtr = palette + 255 * 3;
@@ -753,12 +800,16 @@ qboolean Image_SaveWAD( const char *name, rgbdata_t *pix )
header.ident = IDWAD3HEADER;
header.numlumps = 1;
le_struct_swap( dwadinfo_swap, &header );
FS_Write( f, &header, sizeof( header ));
le_struct_swap( mip_swap, &miptex );
FS_Write( f, &miptex, sizeof( mip_t ));
le_struct_swap( mip_swap, &miptex );
FS_Write( f, pix->buffer, m0size );
FS_Write( f, mip1_data, m1size );
FS_Write( f, mip2_data, m2size );
FS_Write( f, mip3_data, m3size );
palette_size = LittleShort( palette_size );
FS_Write( f, &palette_size, sizeof( short ));
if( lump_type == TYP_PALETTE )
@@ -791,10 +842,11 @@ qboolean Image_SaveWAD( const char *name, rgbdata_t *pix )
lump.type = (char)lump_type;
lump.attribs = 0;
Q_strncpy( lump.name, "tempdecal", sizeof( lump.name ));
le_struct_swap( dlumpinfo_swap, &lump );
FS_Write( f, &lump, sizeof( lump ));
FS_Seek( f, offsetof( dwadinfo_t, infotableofs ), SEEK_SET );
infotableofs32 = (int)infotableofs;
infotableofs32 = LittleLong((int)infotableofs );
FS_Write( f, &infotableofs32, sizeof( int ));
FS_Close( f );

View File

@@ -327,7 +327,7 @@ static char *COM_GetItaniumName( const char * const in_name )
{
// parse symbol length marker
len = 0;
for( ; isdigit( *f ) && remaining > 0; f++, remaining-- )
for( ; isdigit((byte)*f ) && remaining > 0; f++, remaining-- )
len = len * 10 + ( *f - '0' );
// sane value
@@ -344,7 +344,7 @@ static char *COM_GetItaniumName( const char * const in_name )
if( *f == 'E' )
break;
if( !isdigit( *f ) || remaining <= 0 )
if( !isdigit((byte)*f ) || remaining <= 0 )
goto invalid_format;
}

View File

@@ -23,6 +23,7 @@ GNU General Public License for more details.
#include "enginefeatures.h"
#include "client.h"
#include "server.h" // LUMP_ error codes
#include "swaplib.h"
#include "ref_common.h"
#if defined( HAVE_OPENMP )
#include <omp.h>
@@ -165,35 +166,188 @@ typedef enum
LOADLUMP_BSPX, // ... from BSPX data
} loadlump_source_t;
#define LUMP_SAVESTATS BIT( 0 )
#define LUMP_TESTONLY BIT( 1 )
#define LUMP_SILENT BIT( 2 )
#define LUMP_BSP30EXT BIT( 3 ) // extra marker for Mod_LoadLump
#define LUMP_BSPX BIT( 4 )
#define LUMP_SAVESTATS BIT( 0 )
#define LUMP_BSHIFT_SWAP BIT( 1 )
#define LUMP_SILENT BIT( 2 )
#define LUMP_BSP30EXT BIT( 3 ) // extra marker for Mod_LoadLump
#define LUMP_BSPX BIT( 4 )
typedef struct
{
int lumpnumber;
const int lumpnumber;
// BSPX
const char lumpname[24];
int flags;
const int flags;
const size_t mincount;
const size_t maxcount;
const int entrysize;
const int entrysize32; // extended size (0 if not available)
const char *loadname;
const void **dataptr;
size_t *count;
const size_t dataofs; // offsetof into dbspmodel_t for data pointer
const size_t countofs; // offsetof into dbspmodel_t for count/size
#if XASH_BIG_ENDIAN // do not waste memory on little endian
const swap_struct_def_t *swap;
const size_t swaplen;
const swap_struct_def_t *swap32;
const size_t swaplen32;
#endif
} mlumpinfo_t;
// all these macros are ugly af
#if XASH_BIG_ENDIAN
#define LUMP_SWAP( x ) .swap = x, .swaplen = ARRAYSIZE( x )
#define LUMP_SWAP32( x, y ) .swap = x, .swaplen = ARRAYSIZE( x ), .swap32 = y, .swaplen32 = ARRAYSIZE( y )
#else
#define LUMP_SWAP( x )
#define LUMP_SWAP32( x, y )
#endif
le_struct_begin( dlump_swap )
le_struct_field( dlump_t, fileofs )
le_struct_field( dlump_t, filelen )
le_struct_end();
le_struct_begin( dheader_swap )
le_struct_field( dheader_t, version )
le_struct_array_child( dheader_t, lumps, dlump_swap, HEADER_LUMPS )
le_struct_end();
le_struct_begin( dextrahdr_swap )
le_struct_field( dextrahdr_t, id )
le_struct_field( dextrahdr_t, version )
le_struct_array_child( dextrahdr_t, lumps, dlump_swap, EXTRA_LUMPS )
le_struct_end();
le_struct_begin( dbspx_hdr_swap )
le_struct_field( dbspx_hdr_t, id )
le_struct_field( dbspx_hdr_t, numlumps )
// flexible array member omitted
le_struct_end();
le_struct_begin( dbspx_lump_swap )
le_struct_field( dbspx_lump_t, fileofs )
le_struct_field( dbspx_lump_t, filelen )
le_struct_end();
le_struct_begin( dplane_swap )
le_struct_array( dplane_t, normal, 3 )
le_struct_field( dplane_t, dist )
le_struct_field( dplane_t, type )
le_struct_end();
le_struct_begin( dvertex_swap )
le_struct_array( dvertex_t, point, 3 )
le_struct_end();
le_struct_begin( dnode_swap )
le_struct_field( dnode_t, planenum )
le_struct_array( dnode_t, children, 2 )
le_struct_array( dnode_t, mins, 3 )
le_struct_array( dnode_t, maxs, 3 )
le_struct_field( dnode_t, firstface )
le_struct_field( dnode_t, numfaces )
le_struct_end();
le_struct_begin( dnode32_swap )
le_struct_field( dnode32_t, planenum )
le_struct_array( dnode32_t, children, 2 )
le_struct_array( dnode32_t, mins, 3 )
le_struct_array( dnode32_t, maxs, 3 )
le_struct_field( dnode32_t, firstface )
le_struct_field( dnode32_t, numfaces )
le_struct_end();
le_struct_begin( dtexinfo_swap )
le_struct_array( dtexinfo_t, vecs[0], 4 )
le_struct_array( dtexinfo_t, vecs[1], 4 )
le_struct_field( dtexinfo_t, miptex )
le_struct_field( dtexinfo_t, flags )
le_struct_field( dtexinfo_t, faceinfo )
le_struct_end();
le_struct_begin( dface_swap )
le_struct_field( dface_t, planenum )
le_struct_field( dface_t, side )
le_struct_field( dface_t, firstedge )
le_struct_field( dface_t, numedges )
le_struct_field( dface_t, texinfo )
le_struct_field( dface_t, lightofs )
le_struct_end();
le_struct_begin( dface32_swap )
le_struct_field( dface32_t, planenum )
le_struct_field( dface32_t, side )
le_struct_field( dface32_t, firstedge )
le_struct_field( dface32_t, numedges )
le_struct_field( dface32_t, texinfo )
le_struct_field( dface32_t, lightofs )
le_struct_end();
le_struct_begin( dclipnode_swap )
le_struct_field( dclipnode_t, planenum )
le_struct_array( dclipnode_t, children, 2 )
le_struct_end();
le_struct_begin( dclipnode32_swap )
le_struct_field( dclipnode32_t, planenum )
le_struct_array( dclipnode32_t, children, 2 )
le_struct_end();
le_struct_begin( dleaf_swap )
le_struct_field( dleaf_t, contents )
le_struct_field( dleaf_t, visofs )
le_struct_array( dleaf_t, mins, 3 )
le_struct_array( dleaf_t, maxs, 3 )
le_struct_field( dleaf_t, firstmarksurface )
le_struct_field( dleaf_t, nummarksurfaces )
le_struct_end();
le_struct_begin( dleaf32_swap )
le_struct_field( dleaf32_t, contents )
le_struct_field( dleaf32_t, visofs )
le_struct_array( dleaf32_t, mins, 3 )
le_struct_array( dleaf32_t, maxs, 3 )
le_struct_field( dleaf32_t, firstmarksurface )
le_struct_field( dleaf32_t, nummarksurfaces )
le_struct_end();
le_struct_begin( dedge_swap )
le_struct_array( dedge_t, v, 2 )
le_struct_end();
le_struct_begin( dedge32_swap )
le_struct_array( dedge32_t, v, 2 )
le_struct_end();
le_struct_begin( dmodel_swap )
le_struct_array( dmodel_t, mins, 3 )
le_struct_array( dmodel_t, maxs, 3 )
le_struct_array( dmodel_t, origin, 3 )
le_struct_array( dmodel_t, headnode, MAX_MAP_HULLS )
le_struct_field( dmodel_t, visleafs )
le_struct_field( dmodel_t, firstface )
le_struct_field( dmodel_t, numfaces )
le_struct_end();
le_struct_begin( dfaceinfo_swap )
le_struct_field( dfaceinfo_t, texture_step )
le_struct_field( dfaceinfo_t, max_extent )
le_struct_field( dfaceinfo_t, groupid )
le_struct_end();
le_struct_begin( mip_swap )
le_struct_field( mip_t, width )
le_struct_field( mip_t, height )
le_struct_array( mip_t, offsets, 4 )
le_struct_end();
world_static_t world;
static dbspmodel_t srcmodel;
static loadstat_t loadstat;
static model_t *worldmodel;
static byte g_visdata[(MAX_MAP_LEAFS+7)/8]; // intermediate buffer
static mlumpinfo_t srclumps[HEADER_LUMPS] =
static const mlumpinfo_t srclumps[HEADER_LUMPS] =
{
{
.lumpnumber = LUMP_ENTITIES,
@@ -201,8 +355,8 @@ static mlumpinfo_t srclumps[HEADER_LUMPS] =
.maxcount = MAX_MAP_ENTSTRING,
.entrysize = sizeof( byte ),
.loadname = "entities",
.dataptr = (const void **)&srcmodel.entdata,
.count = &srcmodel.entdatasize,
.dataofs = offsetof( dbspmodel_t, entdata ),
.countofs = offsetof( dbspmodel_t, entdatasize ),
},
{
.lumpnumber = LUMP_PLANES,
@@ -210,8 +364,9 @@ static mlumpinfo_t srclumps[HEADER_LUMPS] =
.maxcount = MAX_MAP_PLANES,
.entrysize = sizeof( dplane_t ),
.loadname = "planes",
.dataptr = (const void **)&srcmodel.planes,
.count = &srcmodel.numplanes,
.dataofs = offsetof( dbspmodel_t, planes ),
.countofs = offsetof( dbspmodel_t, numplanes ),
LUMP_SWAP( dplane_swap )
},
{
.lumpnumber = LUMP_TEXTURES,
@@ -219,24 +374,25 @@ static mlumpinfo_t srclumps[HEADER_LUMPS] =
.maxcount = MAX_MAP_MIPTEX,
.entrysize = sizeof( byte ),
.loadname = "textures",
.dataptr = (const void **)&srcmodel.textures,
.count = &srcmodel.texdatasize,
.dataofs = offsetof( dbspmodel_t, textures ),
.countofs = offsetof( dbspmodel_t, texdatasize ),
},
{
.lumpnumber = LUMP_VERTEXES,
.maxcount = MAX_MAP_VERTS,
.entrysize = sizeof( dvertex_t ),
.loadname = "vertexes",
.dataptr = (const void **)&srcmodel.vertexes,
.count = &srcmodel.numvertexes,
.dataofs = offsetof( dbspmodel_t, vertexes ),
.countofs = offsetof( dbspmodel_t, numvertexes ),
LUMP_SWAP( dvertex_swap )
},
{
.lumpnumber = LUMP_VISIBILITY,
.maxcount = MAX_MAP_VISIBILITY,
.entrysize = sizeof( byte ),
.loadname = "visibility",
.dataptr = (const void **)&srcmodel.visdata,
.count = &srcmodel.visdatasize,
.dataofs = offsetof( dbspmodel_t, visdata ),
.countofs = offsetof( dbspmodel_t, visdatasize ),
},
{
.lumpnumber = LUMP_NODES,
@@ -246,8 +402,9 @@ static mlumpinfo_t srclumps[HEADER_LUMPS] =
.entrysize32 = sizeof( dnode32_t ),
.loadname = "nodes",
.flags = CHECK_OVERFLOW,
.dataptr = (const void **)&srcmodel.nodes,
.count = &srcmodel.numnodes,
.dataofs = offsetof( dbspmodel_t, nodes ),
.countofs = offsetof( dbspmodel_t, numnodes ),
LUMP_SWAP32( dnode_swap, dnode32_swap )
},
{
.lumpnumber = LUMP_TEXINFO,
@@ -256,8 +413,9 @@ static mlumpinfo_t srclumps[HEADER_LUMPS] =
.entrysize = sizeof( dtexinfo_t ),
.loadname = "texinfo",
.flags = CHECK_OVERFLOW,
.dataptr = (const void **)&srcmodel.texinfo,
.count = &srcmodel.numtexinfo,
.dataofs = offsetof( dbspmodel_t, texinfo ),
.countofs = offsetof( dbspmodel_t, numtexinfo ),
LUMP_SWAP( dtexinfo_swap )
},
{
.lumpnumber = LUMP_FACES,
@@ -267,8 +425,9 @@ static mlumpinfo_t srclumps[HEADER_LUMPS] =
.entrysize32 = sizeof( dface32_t ),
.loadname = "faces",
.flags = CHECK_OVERFLOW,
.dataptr = (const void **)&srcmodel.surfaces,
.count = &srcmodel.numsurfaces,
.dataofs = offsetof( dbspmodel_t, surfaces ),
.countofs = offsetof( dbspmodel_t, numsurfaces ),
LUMP_SWAP32( dface_swap, dface32_swap )
},
{
.lumpnumber = LUMP_LIGHTING,
@@ -277,8 +436,8 @@ static mlumpinfo_t srclumps[HEADER_LUMPS] =
.entrysize = sizeof( byte ),
.loadname = "lightmaps",
.flags = 0,
.dataptr = (const void **)&srcmodel.lightdata,
.count = &srcmodel.lightdatasize,
.dataofs = offsetof( dbspmodel_t, lightdata ),
.countofs = offsetof( dbspmodel_t, lightdatasize ),
},
{
.lumpnumber = LUMP_CLIPNODES,
@@ -288,8 +447,9 @@ static mlumpinfo_t srclumps[HEADER_LUMPS] =
.entrysize32 = sizeof( dclipnode32_t ),
.loadname = "clipnodes",
.flags = 0,
.dataptr = (const void **)&srcmodel.clipnodes,
.count = &srcmodel.numclipnodes,
.dataofs = offsetof( dbspmodel_t, clipnodes ),
.countofs = offsetof( dbspmodel_t, numclipnodes ),
LUMP_SWAP32( dclipnode_swap, dclipnode32_swap )
},
{
.lumpnumber = LUMP_LEAFS,
@@ -299,8 +459,9 @@ static mlumpinfo_t srclumps[HEADER_LUMPS] =
.entrysize32 = sizeof( dleaf32_t ),
.loadname = "leafs",
.flags = CHECK_OVERFLOW,
.dataptr = (const void **)&srcmodel.leafs,
.count = &srcmodel.numleafs,
.dataofs = offsetof( dbspmodel_t, leafs ),
.countofs = offsetof( dbspmodel_t, numleafs ),
LUMP_SWAP32( dleaf_swap, dleaf32_swap )
},
{
.lumpnumber = LUMP_MARKSURFACES,
@@ -310,8 +471,8 @@ static mlumpinfo_t srclumps[HEADER_LUMPS] =
.entrysize32 = sizeof( dmarkface32_t ),
.loadname = "markfaces",
.flags = 0,
.dataptr = (const void **)&srcmodel.markfaces,
.count = &srcmodel.nummarkfaces,
.dataofs = offsetof( dbspmodel_t, markfaces ),
.countofs = offsetof( dbspmodel_t, nummarkfaces ),
},
{
.lumpnumber = LUMP_EDGES,
@@ -321,8 +482,9 @@ static mlumpinfo_t srclumps[HEADER_LUMPS] =
.entrysize32 = sizeof( dedge32_t ),
.loadname = "edges",
.flags = 0,
.dataptr = (const void **)&srcmodel.edges,
.count = &srcmodel.numedges,
.dataofs = offsetof( dbspmodel_t, edges ),
.countofs = offsetof( dbspmodel_t, numedges ),
LUMP_SWAP32( dedge_swap, dedge32_swap )
},
{
.lumpnumber = LUMP_SURFEDGES,
@@ -331,8 +493,8 @@ static mlumpinfo_t srclumps[HEADER_LUMPS] =
.entrysize = sizeof( dsurfedge_t ),
.loadname = "surfedges",
.flags = 0,
.dataptr = (const void **)&srcmodel.surfedges,
.count = &srcmodel.numsurfedges,
.dataofs = offsetof( dbspmodel_t, surfedges ),
.countofs = offsetof( dbspmodel_t, numsurfedges ),
},
{
.lumpnumber = LUMP_MODELS,
@@ -341,8 +503,9 @@ static mlumpinfo_t srclumps[HEADER_LUMPS] =
.entrysize = sizeof( dmodel_t ),
.loadname = "models",
.flags = CHECK_OVERFLOW,
.dataptr = (const void **)&srcmodel.submodels,
.count = &srcmodel.numsubmodels,
.dataofs = offsetof( dbspmodel_t, submodels ),
.countofs = offsetof( dbspmodel_t, numsubmodels ),
LUMP_SWAP( dmodel_swap )
},
};
@@ -354,8 +517,8 @@ static const mlumpinfo_t extlumps[EXTRA_LUMPS] =
.maxcount = MAX_MAP_LIGHTING,
.entrysize = sizeof( byte ),
.loadname = "deluxmaps",
.dataptr = (const void **)&srcmodel.deluxdata,
.count = &srcmodel.deluxdatasize,
.dataofs = offsetof( dbspmodel_t, deluxdata ),
.countofs = offsetof( dbspmodel_t, deluxdatasize ),
},
{
.lumpnumber = LUMP_FACEINFO,
@@ -364,8 +527,9 @@ static const mlumpinfo_t extlumps[EXTRA_LUMPS] =
.entrysize = sizeof( dfaceinfo_t ),
.loadname = "faceinfos",
.flags = CHECK_OVERFLOW,
.dataptr = (const void **)&srcmodel.faceinfo,
.count = &srcmodel.numfaceinfo,
.dataofs = offsetof( dbspmodel_t, faceinfo ),
.countofs = offsetof( dbspmodel_t, numfaceinfo ),
LUMP_SWAP( dfaceinfo_swap )
},
{
.lumpnumber = LUMP_SHADOWMAP,
@@ -373,8 +537,8 @@ static const mlumpinfo_t extlumps[EXTRA_LUMPS] =
.maxcount = MAX_MAP_LIGHTING / 3,
.entrysize = sizeof( byte ),
.loadname = "shadowmap",
.dataptr = (const void **)&srcmodel.shadowdata,
.count = &srcmodel.shadowdatasize,
.dataofs = offsetof( dbspmodel_t, shadowdata ),
.countofs = offsetof( dbspmodel_t, shadowdatasize ),
},
};
@@ -385,16 +549,16 @@ static const mlumpinfo_t bspxlumps[] =
.maxcount = MAX_MAP_LIGHTING,
.entrysize = sizeof( byte ),
.loadname = "rgblighting",
.dataptr = (const void **)&srcmodel.rgblightdata,
.count = &srcmodel.rgblightdatasize,
.dataofs = offsetof( dbspmodel_t, rgblightdata ),
.countofs = offsetof( dbspmodel_t, rgblightdatasize ),
},
{
.lumpname = "LIGHTINGDIR",
.maxcount = MAX_MAP_LIGHTING,
.entrysize = sizeof( byte ),
.loadname = "lightingdir",
.dataptr = (const void **)&srcmodel.deluxdata,
.count = &srcmodel.deluxdatasize,
.dataofs = offsetof( dbspmodel_t, deluxdata ),
.countofs = offsetof( dbspmodel_t, deluxdatasize ),
},
};
@@ -593,7 +757,7 @@ Mod_LoadLump
generic loader
=================
*/
static void Mod_LoadLump( const void *in, const mlumpinfo_t *info, mlumpstat_t *stat, int flags, loadlump_source_t source, const void *bspx_data )
static void Mod_LoadLump( const void *in, const mlumpinfo_t *info, mlumpstat_t *stat, int flags, loadlump_source_t source, const void *bspx_data, dbspmodel_t *bmod )
{
int version = ((const dheader_t *)in)->version, i;
size_t numelems, real_entrysize;
@@ -604,7 +768,16 @@ static void Mod_LoadLump( const void *in, const mlumpinfo_t *info, mlumpstat_t *
case LOADLUMP_STANDARD:
{
const dheader_t *header = in;
l = header->lumps[info->lumpnumber];
int lumpnumber = info->lumpnumber;
if( FBitSet( flags, LUMP_BSHIFT_SWAP ))
{
if( lumpnumber == LUMP_ENTITIES )
lumpnumber = LUMP_PLANES;
else if( lumpnumber == LUMP_PLANES )
lumpnumber = LUMP_ENTITIES;
}
l = header->lumps[lumpnumber];
break;
}
case LOADLUMP_BSP30EXT:
@@ -671,7 +844,7 @@ static void Mod_LoadLump( const void *in, const mlumpinfo_t *info, mlumpstat_t *
}
// bmodels not required the visibility
if( !FBitSet( flags, LUMP_TESTONLY ) && !world.loading && info->lumpnumber == LUMP_VISIBILITY )
if( info->lumpnumber == LUMP_VISIBILITY && !world.loading && bmod )
SetBits( flags, LUMP_SILENT ); // shut up warning
// fill the stats for world
@@ -749,15 +922,33 @@ static void Mod_LoadLump( const void *in, const mlumpinfo_t *info, mlumpstat_t *
}
}
if( FBitSet( flags, LUMP_TESTONLY ))
return; // don't fill the intermediate struct
// if no bmod is passed, we are only testing if BSP lumps are not corrupted
if( !bmod )
return;
byte *data = (byte *)in + l.fileofs;
// all checks are passed, store pointers
if( info->dataptr )
*info->dataptr = (void *)((byte *)in + l.fileofs);
*(byte **)((byte *)bmod + info->dataofs) = data;
*(size_t *)((byte *)bmod + info->countofs) = numelems;
if( info->count )
*info->count = numelems;
// finally, process the data
#if XASH_BIG_ENDIAN
const swap_struct_def_t *swap = real_entrysize == info->entrysize32 ? info->swap32 : info->swap;
size_t swaplen = real_entrysize == info->entrysize32 ? info->swaplen32 : info->swaplen;
if( swap )
{
for( size_t j = 0; j < numelems; j++ )
swap_struct_( swap, swaplen, data + j * real_entrysize );
}
// some lumps don't need a swapdef, as all needed data in the lump info
else if( real_entrysize > 1 )
{
for( size_t j = 0; j < numelems; j++ )
swap_field_( data + j * real_entrysize, real_entrysize );
}
#endif
}
/*
@@ -2014,7 +2205,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
}
@@ -2216,6 +2407,9 @@ static void Mod_LoadEntities( model_t *mod, const dbspmodel_t *bmod )
world.wadlist = NULL;
world.wadcount = 0;
world.litwater_minlight = -1;
world.litwater_scale = -1.0f;
// parse all the wads for loading textures in right ordering
while(( pfile = COM_ParseFile( pfile, token, sizeof( token ))) != NULL )
{
@@ -2259,6 +2453,15 @@ static void Mod_LoadEntities( model_t *mod, const dbspmodel_t *bmod )
Mem_Free( world.generator );
world.generator = copystring( token );
}
else if( !Q_stricmp( keyname, "_litwater" ))
{
if( Q_atoi( token ) != 0 )
SetBits( world.flags, FWORLD_HAS_LITWATER );
}
else if( !Q_stricmp( keyname, "_litwater_minlight" ))
world.litwater_minlight = Q_atoi( token );
else if( !Q_stricmp( keyname, "_litwater_scale" ))
world.litwater_scale = Q_atof( token );
}
return; // all done
}
@@ -2535,6 +2738,7 @@ static void Mod_InitSkyClouds( model_t *mod, const mip_t *mt, texture_t *tx, qbo
if( custom_palette )
size += sizeof( short ) + 768;
Image_SetForceFlags( IL_HOST_ENDIAN );
r_sky = FS_LoadImage( texname, (byte *)mt, size );
}
else
@@ -2587,7 +2791,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 +2814,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 );
@@ -2717,7 +2921,7 @@ static void Mod_LoadTextureData( model_t *mod, dbspmodel_t *bmod, int textureInd
const size_t size = Mod_CalculateMipTexSize( mipTex, usesCustomPalette );
Q_snprintf( texName, sizeof( texName ), "#%s:%s.mip", loadstat.name, mipTex->name );
Image_SetForceFlags( texture_force_flags );
Image_SetForceFlags( texture_force_flags | IL_HOST_ENDIAN );
texture->gl_texturenum = ref.dllFuncs.GL_LoadTexture( texName, (byte *)mipTex, size, txFlags );
}
@@ -2750,7 +2954,7 @@ static void Mod_LoadTextureData( model_t *mod, dbspmodel_t *bmod, int textureInd
Q_snprintf( texName, sizeof( texName ), "#%s:%s_luma.mip", loadstat.name, mipTex->name );
Image_SetForceFlags( texture_force_flags );
Image_SetForceFlags( texture_force_flags | IL_HOST_ENDIAN );
if( mipTex->offsets[0] > 0 )
{
@@ -2975,6 +3179,24 @@ static void Mod_LoadTextures( model_t *mod, dbspmodel_t *bmod )
lump = bmod->textures;
#if XASH_BIG_ENDIAN
if( lump )
{
lump->nummiptex = LittleLong( lump->nummiptex );
for( int i = 0; i < lump->nummiptex; i++ )
{
lump->dataofs[i] = LittleLong( lump->dataofs[i] );
if( lump->dataofs[i] >= 0 )
{
mip_t *mt = (mip_t *)((byte *)lump + lump->dataofs[i]);
le_struct_swap( mip_swap, mt );
}
}
}
#endif
if( bmod->texdatasize < 1 || !lump || lump->nummiptex < 1 )
{
// no textures
@@ -2989,6 +3211,105 @@ static void Mod_LoadTextures( model_t *mod, dbspmodel_t *bmod )
Mod_SequenceAllAnimatedTextures( mod );
}
#if !XASH_DEDICATED
static void Mod_ParseDetailTextures( model_t *mod )
{
byte *afile;
char *pfile;
string token, texname;
string detail_texname;
string detail_path;
float xScale, yScale;
texture_t *tex;
int i;
string filepath;
Q_strncpy( filepath, mod->name, sizeof( filepath ));
COM_StripExtension( filepath );
Q_strncat( filepath, "_detail.txt", sizeof( filepath ));
afile = FS_LoadFile( filepath, NULL, false );
if( !afile )
return;
pfile = (char *)afile;
// format: 'texturename' 'detailtexture' 'xScale' 'yScale'
while(( pfile = COM_ParseFile( pfile, token, sizeof( token ))) != NULL )
{
texname[0] = '\0';
detail_texname[0] = '\0';
// read texname
if( token[0] == '{' )
{
// NOTE: COM_ParseFile handled some symbols seperately
// this code will be fix it
pfile = COM_ParseFile( pfile, token, sizeof( token ));
Q_snprintf( texname, sizeof( texname ), "{%s", token );
}
else
Q_strncpy( texname, token, sizeof( texname ));
// read detailtexture name
pfile = COM_ParseFile( pfile, token, sizeof( token ));
Q_strncpy( detail_texname, token, sizeof( detail_texname ));
// trying the scales or '{'
pfile = COM_ParseFile( pfile, token, sizeof( token ));
// read second part of detailtexture name
if( token[0] == '{' )
{
Q_strncat( detail_texname, token, sizeof( detail_texname ));
pfile = COM_ParseFile( pfile, token, sizeof( token )); // read scales
Q_strncat( detail_texname, token, sizeof( detail_texname ));
pfile = COM_ParseFile( pfile, token, sizeof( token )); // parse scales
}
Q_snprintf( detail_path, sizeof( detail_path ), "gfx/%s", detail_texname );
// read scales
xScale = Q_atof( token );
pfile = COM_ParseFile( pfile, token, sizeof( token ));
yScale = Q_atof( token );
if( xScale <= 0.0f || yScale <= 0.0f )
continue;
// search for existing texture and uploading detail texture
for( i = 0; i < mod->numtextures; i++ )
{
tex = mod->textures[i];
if( Q_stricmp( tex->name, texname ))
continue;
tex->dt_texturenum = ref.dllFuncs.GL_LoadTexture( detail_path, NULL, 0, TF_FORCE_COLOR|TF_NOFLIP_TGA );
if( tex->dt_texturenum )
ref.dllFuncs.R_SetDetailScaleForTexture( tex->gl_texturenum, xScale, yScale );
break;
}
}
Mem_Free( afile );
}
void Mod_LoadDetailTextures( model_t *mod )
{
convar_t *r_detailtextures = Cvar_FindVar( "r_detailtextures" );
if( !r_detailtextures || !r_detailtextures->value )
return;
Mod_ParseDetailTextures( mod );
}
#endif // !XASH_DEDICATED
/*
=================
Mod_LoadTexInfo
@@ -3599,6 +3920,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 );
}
@@ -3742,6 +4067,103 @@ static int Mod_LumpLooksLikeEntities( const char *lump, const size_t lumplen )
return Q_memmem( lump, lumplen, "\"classname\"", sizeof( "\"classname\"" ) - 1 ) != NULL ? 1 : 0;
}
static void Mod_SwapBSPLumps( byte *mod_base, size_t bufferlen )
{
dheader_t *header = (dheader_t *)mod_base;
le_struct_swap( dheader_swap, header );
// BSP30ext pass
if( header->version == HLBSP_VERSION && bufferlen > sizeof( *header ) + sizeof( dextrahdr_t ))
{
dextrahdr_t *ext = (dextrahdr_t *)( mod_base + sizeof( *header ));
if( ext->id == LittleLong( IDEXTRAHEADER ))
le_struct_swap( dextrahdr_swap, ext );
}
}
/*
=================
CRC32_MapFile
compute CRC for the map lump data
=================
*/
qboolean CRC32_MapFile( dword *crcvalue, const char *filename, qboolean multiplayer )
{
byte headbuf[sizeof( dheader_t )];
char buffer[1024];
int i, num_bytes, lumplen;
dheader_t *header;
file_t *f;
if( !crcvalue )
return false;
// always calc same checksum for singleplayer
if( multiplayer == false )
{
*crcvalue = (('H'<<24)+('S'<<16)+('A'<<8)+'X');
return true;
}
f = FS_Open( filename, "rb", false );
if( !f )
return false;
num_bytes = FS_Read( f, headbuf, sizeof( headbuf ));
if( num_bytes != sizeof( headbuf ))
{
FS_Close( f );
return false;
}
header = (dheader_t *)headbuf;
le_struct_swap( dheader_swap, header );
switch( header->version )
{
case Q1BSP_VERSION:
case HLBSP_VERSION:
case QBSP2_VERSION:
break;
default:
FS_Close( f );
return false;
}
CRC32_Init( crcvalue );
for( i = LUMP_PLANES; i < HEADER_LUMPS; i++ )
{
lumplen = header->lumps[i].filelen;
FS_Seek( f, header->lumps[i].fileofs, SEEK_SET );
while( lumplen > 0 )
{
if( lumplen >= sizeof( buffer ))
num_bytes = FS_Read( f, buffer, sizeof( buffer ));
else
num_bytes = FS_Read( f, buffer, lumplen );
if( num_bytes > 0 )
{
lumplen -= num_bytes;
CRC32_ProcessBuffer( crcvalue, buffer, num_bytes );
}
if( FS_Eof( f ))
break;
}
}
FS_Close( f );
return true;
}
/*
=================
Mod_FindEndOfBSPFile
@@ -3786,20 +4208,26 @@ Mod_FindBSPX
find BSPX header position, returns -1 on error
=================
*/
static fs_offset_t Mod_FindBSPX( const byte *mod_base, size_t bufferlen )
static fs_offset_t Mod_FindBSPX( byte *mod_base, size_t bufferlen )
{
fs_offset_t max_offset = Mod_FindEndOfBSPFile( mod_base, bufferlen );
const dbspx_hdr_t *bspx_header;
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 );
dbspx_hdr_t *bspx_header = (dbspx_hdr_t *)( mod_base + max_offset );
if( bspx_header->id != LittleLong( IDBSPXHEADER ))
return -1;
bspx_header->id = LittleLong( bspx_header->id );
bspx_header->numlumps = LittleLong( bspx_header->numlumps );
for( int i = 0; i < bspx_header->numlumps; i++ )
le_struct_swap( dbspx_lump_swap, &bspx_header->lumps[i] );
Con_DPrintf( "Found valid BSPX signature at %lld\n", (long long)max_offset );
return max_offset;
}
@@ -3815,22 +4243,19 @@ static qboolean Mod_LoadBmodelLumps( model_t *mod, byte *mod_base, size_t buffer
{
dheader_t *header = (dheader_t *)mod_base;
int *extident = (int *)(mod_base + sizeof( dheader_t ));
dbspmodel_t *bmod = &srcmodel;
char wadvalue[2048];
size_t len = 0;
int i, stat_index = 0, ret, flags = 0;
fs_offset_t bspx_header_offset;
// always reset the intermediate struct
memset( bmod, 0, sizeof( *bmod ));
memset( &loadstat, 0, sizeof( loadstat ));
Q_strncpy( loadstat.name, mod->name, sizeof( loadstat.name ));
wadvalue[0] = '\0';
// restore default lump numbers
srclumps[0].lumpnumber = LUMP_ENTITIES;
srclumps[1].lumpnumber = LUMP_PLANES;
// byte-swap BSP header and lump directory from little-endian
Mod_SwapBSPLumps( mod_base, bufferlen );
switch( header->version )
{
@@ -3844,8 +4269,7 @@ static qboolean Mod_LoadBmodelLumps( model_t *mod, byte *mod_base, size_t buffer
Mod_LumpLooksLikeEntities( mod_base + header->lumps[LUMP_PLANES].fileofs, header->lumps[LUMP_PLANES].filelen ))
{
// blue-shift swapped lumps
srclumps[0].lumpnumber = LUMP_PLANES;
srclumps[1].lumpnumber = LUMP_ENTITIES;
SetBits( flags, LUMP_BSHIFT_SWAP );
}
break;
case Q1BSP_VERSION:
@@ -3859,6 +4283,7 @@ static qboolean Mod_LoadBmodelLumps( model_t *mod, byte *mod_base, size_t buffer
return false;
}
dbspmodel_t *bmod = Mem_Calloc( mod->mempool, sizeof( *bmod ));
bmod->version = header->version; // share up global
if( isworld )
{
@@ -3871,21 +4296,25 @@ static qboolean Mod_LoadBmodelLumps( model_t *mod, byte *mod_base, size_t buffer
// loading base lumps
for( i = 0; i < ARRAYSIZE( srclumps ); i++, stat_index++ )
Mod_LoadLump( mod_base, &srclumps[i], &worldstats[stat_index], flags, LOADLUMP_STANDARD, NULL );
Mod_LoadLump( mod_base, &srclumps[i], &worldstats[stat_index], flags, LOADLUMP_STANDARD, NULL, bmod );
// loading extralumps
for( i = 0; i < ARRAYSIZE( extlumps ); i++, stat_index++ )
Mod_LoadLump( mod_base, &extlumps[i], &worldstats[stat_index], flags, LOADLUMP_BSP30EXT, NULL );
Mod_LoadLump( mod_base, &extlumps[i], &worldstats[stat_index], flags, LOADLUMP_BSP30EXT, NULL, bmod );
// 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, bmod );
}
if( !bmod->isworld ) // a1ba: why world excluded here?
{
if( loadstat.numerrors )
{
Con_DPrintf( "%s: %i error(s), %i warning(s)\n", __func__, loadstat.numerrors, loadstat.numwarnings );
Mem_Free( bmod );
return false; // there were errors, we can't load this map
}
@@ -3950,6 +4379,7 @@ static qboolean Mod_LoadBmodelLumps( model_t *mod, byte *mod_base, size_t buffer
Con_Reportf( "Wad files required to run the map: \"%s\"\n", wadvalue );
}
Mem_Free( bmod );
return true;
}
@@ -3992,7 +4422,7 @@ qboolean Mod_TestBmodelLumps( file_t *f, const char *name, byte *mod_base, size_
{
dheader_t *header = (dheader_t *)mod_base;
int *extident = (int *)( mod_base + sizeof( dheader_t ));
int i, flags = LUMP_TESTONLY, stat_index = 0;
int i, flags = 0, stat_index = 0;
// always reset the intermediate struct
memset( &loadstat, 0, sizeof( loadstat_t ));
@@ -4005,9 +4435,8 @@ qboolean Mod_TestBmodelLumps( file_t *f, const char *name, byte *mod_base, size_
if( buffersize < sizeof( *header ))
return false;
// restore default lump numbers
srclumps[0].lumpnumber = LUMP_ENTITIES;
srclumps[1].lumpnumber = LUMP_PLANES;
// byte-swap BSP header and lump directory from little-endian
Mod_SwapBSPLumps( mod_base, buffersize );
switch( header->version )
{
@@ -4030,11 +4459,7 @@ qboolean Mod_TestBmodelLumps( file_t *f, const char *name, byte *mod_base, size_
return false;
if( ret )
{
// blue-shift swapped lumps
srclumps[0].lumpnumber = LUMP_PLANES;
srclumps[1].lumpnumber = LUMP_ENTITIES;
}
SetBits( flags, LUMP_BSHIFT_SWAP );
}
}
break;
@@ -4050,15 +4475,15 @@ qboolean Mod_TestBmodelLumps( file_t *f, const char *name, byte *mod_base, size_
}
// get entities lump to caller
*entities = header->lumps[srclumps[0].lumpnumber];
*entities = header->lumps[FBitSet( flags, LUMP_BSHIFT_SWAP ) ? LUMP_PLANES : LUMP_ENTITIES];
// loading base lumps
for( i = 0; i < ARRAYSIZE( srclumps ); i++, stat_index++ )
Mod_LoadLump( mod_base, &srclumps[i], &worldstats[stat_index], flags, LOADLUMP_STANDARD, NULL );
Mod_LoadLump( mod_base, &srclumps[i], &worldstats[stat_index], flags, LOADLUMP_STANDARD, NULL, NULL );
// loading extralumps
for( i = 0; i < ARRAYSIZE( extlumps ); i++, stat_index++ )
Mod_LoadLump( mod_base, &extlumps[i], &worldstats[stat_index], flags, LOADLUMP_BSP30EXT, NULL );
Mod_LoadLump( mod_base, &extlumps[i], &worldstats[stat_index], flags, LOADLUMP_BSP30EXT, NULL, NULL );
// FIXME: BSPX testing

View File

@@ -124,6 +124,10 @@ typedef struct world_static_s
wadentry_t *wadlist;
int wadcount;
// lightmapped water extra info
float litwater_scale;
int litwater_minlight;
} world_static_t;
#ifndef REF_DLL
@@ -156,6 +160,7 @@ model_t *Mod_ForName( const char *name, qboolean crash, qboolean trackCRC );
qboolean Mod_ValidateCRC( const char *name, uint32_t crc );
void Mod_NeedCRC( const char *name, qboolean needCRC );
void Mod_FreeUnused( void );
void Mod_LoadDetailTextures( model_t *mod );
//
// mod_alias.c
@@ -180,7 +185,7 @@ void Mod_PrintWorldStats_f( void );
//
// mod_studio.c
//
void Mod_LoadStudioModel( model_t *mod, const void *buffer, qboolean *loaded );
void Mod_LoadStudioModel( model_t *mod, void *buffer, size_t buffersize, qboolean *loaded );
void Mod_InitStudioAPI( void );
void Mod_InitStudioHull( void );
void Mod_ResetStudioAPI( void );
@@ -190,13 +195,16 @@ void Mod_StudioGetAttachment( const edict_t *e, int iAttachment, float *org, flo
void Mod_GetBonePosition( const edict_t *e, int iBone, float *org, float *ang );
hull_t *Mod_HullForStudio( model_t *m, float frame, int seq, vec3_t ang, vec3_t org, vec3_t size, byte *pcnt, byte *pbl, int *hitboxes, edict_t *ed );
void *R_StudioGetAnim( studiohdr_t *m_pStudioHeader, model_t *m_pSubModel, mstudioseqdesc_t *pseqdesc );
void Mod_SwapStudioSeqGroupAnims( studiohdr_t *phdr, mstudioseqdesc_t *pseq, byte *buf );
int Mod_HitgroupForStudioHull( int index );
void Mod_ClearStudioCache( void );
//
// mod_sprite.c
//
void Mod_LoadSpriteModel( model_t *mod, const void *buffer, size_t buffersize, qboolean *loaded );
#endif
void Mod_LoadSpriteModel( model_t *mod, void *buffer, size_t buffersize, qboolean *loaded );
void Mod_SpriteLoadTextures( model_t *mod, const void *buffer );
void Mod_SpriteUnloadTextures( void *data );
#endif
#endif//MOD_LOCAL_H

View File

@@ -17,11 +17,166 @@ 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"
#include "swaplib.h"
le_struct_begin( dsprite_q1_swap )
le_struct_field( dsprite_q1_t, ident )
le_struct_field( dsprite_q1_t, version )
le_struct_field( dsprite_q1_t, type )
le_struct_field( dsprite_q1_t, boundingradius )
le_struct_array( dsprite_q1_t, bounds, 2 )
le_struct_field( dsprite_q1_t, numframes )
le_struct_field( dsprite_q1_t, beamlength )
le_struct_field( dsprite_q1_t, synctype )
le_struct_end();
le_struct_begin( dsprite_hl_swap )
le_struct_field( dsprite_hl_t, ident )
le_struct_field( dsprite_hl_t, version )
le_struct_field( dsprite_hl_t, type )
le_struct_field( dsprite_hl_t, texFormat )
le_struct_field( dsprite_hl_t, boundingradius )
le_struct_array( dsprite_hl_t, bounds, 2 )
le_struct_field( dsprite_hl_t, numframes )
le_struct_field( dsprite_hl_t, facetype )
le_struct_field( dsprite_hl_t, synctype )
le_struct_end();
le_struct_begin( dspriteframe_swap )
le_struct_array( dspriteframe_t, origin, 2 )
le_struct_field( dspriteframe_t, width )
le_struct_field( dspriteframe_t, height )
le_struct_end();
static byte *Mod_SwapSpriteFrame( byte *p, byte *end, int bytes )
{
dspriteframe_t *frame;
if( p + sizeof( *frame ) > end )
return NULL;
frame = (dspriteframe_t *)p;
le_struct_swap( dspriteframe_swap, frame );
p += sizeof( *frame );
// skip pixel data
if( p + frame->width * frame->height * bytes > end )
return NULL;
p += frame->width * frame->height * bytes;
return p;
}
static byte *Mod_SwapSpriteGroup( byte *p, byte *end, int bytes )
{
dspritegroup_t *group;
if( p + sizeof( *group ) > end )
return NULL;
group = (dspritegroup_t *)p;
group->numframes = LittleLong( group->numframes );
p += sizeof( *group );
// swap intervals
int numframes = group->numframes;
if( p + numframes * sizeof( dspriteinterval_t ) > end )
return NULL;
for( int i = 0; i < numframes; i++ )
{
dspriteinterval_t *interval = (dspriteinterval_t *)p;
interval->interval = LittleFloat( interval->interval );
p += sizeof( *interval );
}
// swap each frame in the group
for( int i = 0; i < numframes; i++ )
{
p = Mod_SwapSpriteFrame( p, end, bytes );
if( !p )
return NULL;
}
return p;
}
static qboolean Mod_SwapSprite( void *buffer, size_t buffersize, int *out_version )
{
byte *end = (byte *)buffer + buffersize;
int version, numframes, bytes;
byte *p;
if( buffersize < sizeof( dsprite_t ))
return false;
// peek at ident + version before full swap
version = LittleLong(((dsprite_t *)buffer)->version );
switch( version )
{
case SPRITE_VERSION_Q1:
case SPRITE_VERSION_32:
if( buffersize < sizeof( dsprite_q1_t ))
return false;
le_struct_swap( dsprite_q1_swap, buffer );
numframes = ((dsprite_q1_t *)buffer)->numframes;
p = (byte *)buffer + sizeof( dsprite_q1_t );
break;
case SPRITE_VERSION_HL:
{
if( buffersize < sizeof( dsprite_hl_t ))
return false;
le_struct_swap( dsprite_hl_swap, buffer );
numframes = ((dsprite_hl_t *)buffer)->numframes;
p = (byte *)buffer + sizeof( dsprite_hl_t );
// HL sprites have a palette count (short) + palette data
if( p + sizeof( short ) > end )
return false;
short numi = LittleShort( *(short *)p );
*(short *)p = numi;
p += sizeof( short ) + numi * 3;
break;
}
default:
return false;
}
*out_version = version;
bytes = ( version == SPRITE_VERSION_32 ) ? 4 : 1;
// swap all frames
for( int i = 0; i < numframes && p && p < end; i++ )
{
dframetype_t *frametype;
if( p + sizeof( *frametype ) > end )
return false;
frametype = (dframetype_t *)p;
frametype->type = LittleLong( frametype->type );
p += sizeof( *frametype );
switch( frametype->type )
{
case FRAME_SINGLE:
p = Mod_SwapSpriteFrame( p, end, bytes );
break;
case FRAME_GROUP:
case FRAME_ANGLED:
p = Mod_SwapSpriteGroup( p, end, bytes );
break;
default:
return false;
}
}
return true;
}
/*
====================
@@ -30,46 +185,18 @@ Mod_LoadSpriteModel
load sprite model
====================
*/
void Mod_LoadSpriteModel( model_t *mod, const void *buffer, size_t buffersize, qboolean *loaded )
void Mod_LoadSpriteModel( model_t *mod, void *buffer, size_t buffersize, qboolean *loaded )
{
const dsprite_t *pin = buffer;
msprite_t *psprite;
char poolname[MAX_VA_STRING];
int version;
if( loaded )
*loaded = false;
if( buffersize < sizeof( dsprite_t ))
if( !Mod_SwapSprite( buffer, buffersize, &version ))
{
Con_DPrintf( S_ERROR "%s: %s have incorrect file size %zu should be greater than %zu (%s)\n", __func__, mod->name, buffersize, sizeof( dsprite_t ), "basic header" );
return;
}
if( pin->ident != IDSPRITEHEADER )
{
Con_DPrintf( S_ERROR "%s: %s has wrong id (0x%x should be 0x%x)\n", __func__, mod->name, pin->ident, IDSPRITEHEADER );
return;
}
switch( pin->version )
{
case SPRITE_VERSION_Q1:
case SPRITE_VERSION_32:
if( buffersize < sizeof( dsprite_q1_t ))
{
Con_DPrintf( S_ERROR "%s: %s have incorrect file size %zu should be greater than %zu (%s)\n", __func__, mod->name, buffersize, sizeof( dsprite_q1_t ), "q1 header" );
return;
}
break;
case SPRITE_VERSION_HL:
if( buffersize < sizeof( dsprite_hl_t ))
{
Con_DPrintf( S_ERROR "%s: %s have incorrect file size %zu should be greater than %zu (%s)\n", __func__, mod->name, buffersize, sizeof( dsprite_hl_t ), "hl header" );
return;
}
break;
default:
Con_DPrintf( S_ERROR "%s: %s has wrong version number (%i should be %i, %i or %i)\n", __func__, mod->name, pin->version, SPRITE_VERSION_Q1, SPRITE_VERSION_32, SPRITE_VERSION_HL );
Con_DPrintf( S_ERROR "%s: %s is not a valid sprite\n", __func__, mod->name );
return;
}
@@ -77,9 +204,9 @@ void Mod_LoadSpriteModel( model_t *mod, const void *buffer, size_t buffersize, q
Q_snprintf( poolname, sizeof( poolname ), "^2%s^7", mod->name );
mod->mempool = Mem_AllocPool( poolname );
if( pin->version == SPRITE_VERSION_Q1 || pin->version == SPRITE_VERSION_32 )
if( version == SPRITE_VERSION_Q1 || version == SPRITE_VERSION_32 )
{
const dsprite_q1_t *pinq1 = buffer;
dsprite_q1_t *pinq1 = buffer;
size_t size;
if( pinq1->numframes == 0 )
@@ -109,9 +236,9 @@ void Mod_LoadSpriteModel( model_t *mod, const void *buffer, size_t buffersize, q
mod->mins[2] = -pinq1->bounds[1] * 0.5f;
mod->maxs[2] = pinq1->bounds[1] * 0.5f;
}
else // if( pin->version == SPRITE_VERSION_HL )
else // if( version == SPRITE_VERSION_HL )
{
const dsprite_hl_t *pinhl = buffer;
dsprite_hl_t *pinhl = buffer;
size_t size;
if( pinhl->numframes == 0 )
@@ -147,4 +274,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

@@ -19,6 +19,7 @@ GNU General Public License for more details.
#include "r_studioint.h"
#include "library.h"
#include "ref_common.h"
#include "swaplib.h"
typedef int (*STUDIOAPI)( int, sv_blending_interface_t**, server_studio_api_t*, float (*transform)[3][4], float (*bones)[MAXSTUDIOBONES][3][4] );
@@ -58,6 +59,151 @@ static int cache_current;
static int cache_current_hull;
static int cache_current_plane;
le_struct_begin( studiohdr_swap )
le_struct_field( studiohdr_t, ident )
le_struct_field( studiohdr_t, version )
le_struct_field( studiohdr_t, length )
le_struct_array( studiohdr_t, eyeposition, 3 )
le_struct_array( studiohdr_t, min, 3 )
le_struct_array( studiohdr_t, max, 3 )
le_struct_array( studiohdr_t, bbmin, 3 )
le_struct_array( studiohdr_t, bbmax, 3 )
le_struct_field( studiohdr_t, flags )
le_struct_field( studiohdr_t, numbones )
le_struct_field( studiohdr_t, boneindex )
le_struct_field( studiohdr_t, numbonecontrollers )
le_struct_field( studiohdr_t, bonecontrollerindex )
le_struct_field( studiohdr_t, numhitboxes )
le_struct_field( studiohdr_t, hitboxindex )
le_struct_field( studiohdr_t, numseq )
le_struct_field( studiohdr_t, seqindex )
le_struct_field( studiohdr_t, numseqgroups )
le_struct_field( studiohdr_t, seqgroupindex )
le_struct_field( studiohdr_t, numtextures )
le_struct_field( studiohdr_t, textureindex )
le_struct_field( studiohdr_t, texturedataindex )
le_struct_field( studiohdr_t, numskinref )
le_struct_field( studiohdr_t, numskinfamilies )
le_struct_field( studiohdr_t, skinindex )
le_struct_field( studiohdr_t, numbodyparts )
le_struct_field( studiohdr_t, bodypartindex )
le_struct_field( studiohdr_t, numattachments )
le_struct_field( studiohdr_t, attachmentindex )
le_struct_field( studiohdr_t, studiohdr2index )
le_struct_field( studiohdr_t, numtransitions )
le_struct_field( studiohdr_t, transitionindex )
le_struct_end();
le_struct_begin( mstudiobone_swap )
le_struct_field( mstudiobone_t, parent )
le_struct_field( mstudiobone_t, unused )
le_struct_array( mstudiobone_t, bonecontroller, 6 )
le_struct_array( mstudiobone_t, value, 6 )
le_struct_array( mstudiobone_t, scale, 6 )
le_struct_end();
le_struct_begin( mstudiobonecontroller_swap )
le_struct_field( mstudiobonecontroller_t, bone )
le_struct_field( mstudiobonecontroller_t, type )
le_struct_field( mstudiobonecontroller_t, start )
le_struct_field( mstudiobonecontroller_t, end )
le_struct_field( mstudiobonecontroller_t, unused )
le_struct_field( mstudiobonecontroller_t, index )
le_struct_end();
le_struct_begin( mstudiobbox_swap )
le_struct_field( mstudiobbox_t, bone )
le_struct_field( mstudiobbox_t, group )
le_struct_array( mstudiobbox_t, bbmin, 3 )
le_struct_array( mstudiobbox_t, bbmax, 3 )
le_struct_end();
le_struct_begin( mstudioseqgroup_swap )
// should we even swap seqgroup?
le_struct_field( mstudioseqgroup_t, unused )
le_struct_field( mstudioseqgroup_t, unused2 )
le_struct_end();
le_struct_begin( mstudioseqdesc_swap )
le_struct_field( mstudioseqdesc_t, fps )
le_struct_field( mstudioseqdesc_t, flags )
le_struct_field( mstudioseqdesc_t, activity )
le_struct_field( mstudioseqdesc_t, actweight )
le_struct_field( mstudioseqdesc_t, numevents )
le_struct_field( mstudioseqdesc_t, eventindex )
le_struct_field( mstudioseqdesc_t, numframes )
le_struct_field( mstudioseqdesc_t, weightlistindex )
le_struct_field( mstudioseqdesc_t, iklockindex )
le_struct_field( mstudioseqdesc_t, motiontype )
le_struct_field( mstudioseqdesc_t, motionbone )
le_struct_array( mstudioseqdesc_t, linearmovement, 3 )
le_struct_field( mstudioseqdesc_t, autolayerindex )
le_struct_field( mstudioseqdesc_t, keyvalueindex )
le_struct_array( mstudioseqdesc_t, bbmin, 3 )
le_struct_array( mstudioseqdesc_t, bbmax, 3 )
le_struct_field( mstudioseqdesc_t, numblends )
le_struct_field( mstudioseqdesc_t, animindex )
le_struct_array( mstudioseqdesc_t, blendtype, 2 )
le_struct_array( mstudioseqdesc_t, blendstart, 2 )
le_struct_array( mstudioseqdesc_t, blendend, 2 )
le_struct_field( mstudioseqdesc_t, seqgroup )
le_struct_field( mstudioseqdesc_t, entrynode )
le_struct_field( mstudioseqdesc_t, exitnode )
le_struct_field( mstudioseqdesc_t, animdescindex )
le_struct_end();
le_struct_begin( mstudioevent_swap )
le_struct_field( mstudioevent_t, frame )
le_struct_field( mstudioevent_t, event )
le_struct_field( mstudioevent_t, unused )
le_struct_end();
le_struct_begin( mstudioattachment_swap )
le_struct_field( mstudioattachment_t, flags )
le_struct_field( mstudioattachment_t, bone )
le_struct_array( mstudioattachment_t, org, 3 )
le_struct_end();
le_struct_begin( mstudiobodyparts_swap )
le_struct_field( mstudiobodyparts_t, nummodels )
le_struct_field( mstudiobodyparts_t, base )
le_struct_field( mstudiobodyparts_t, modelindex )
le_struct_end();
le_struct_begin( mstudiotexture_swap )
le_struct_field( mstudiotexture_t, flags )
le_struct_field( mstudiotexture_t, width )
le_struct_field( mstudiotexture_t, height )
le_struct_field( mstudiotexture_t, index )
le_struct_end();
le_struct_begin( mstudiomodel_swap )
le_struct_field( mstudiomodel_t, unused )
le_struct_field( mstudiomodel_t, unused2 )
le_struct_field( mstudiomodel_t, nummesh )
le_struct_field( mstudiomodel_t, meshindex )
le_struct_field( mstudiomodel_t, numverts )
le_struct_field( mstudiomodel_t, vertinfoindex )
le_struct_field( mstudiomodel_t, vertindex )
le_struct_field( mstudiomodel_t, numnorms )
le_struct_field( mstudiomodel_t, norminfoindex )
le_struct_field( mstudiomodel_t, normindex )
le_struct_field( mstudiomodel_t, blendvertinfoindex )
le_struct_field( mstudiomodel_t, blendnorminfoindex )
le_struct_end();
le_struct_begin( mstudiomesh_swap )
le_struct_field( mstudiomesh_t, numtris )
le_struct_field( mstudiomesh_t, triindex )
le_struct_field( mstudiomesh_t, skinref )
le_struct_field( mstudiomesh_t, numnorms )
le_struct_field( mstudiomesh_t, unused )
le_struct_end();
le_struct_begin( mstudioanim_swap )
le_struct_array( mstudioanim_t, offset, 6 )
le_struct_end();
/*
====================
Mod_InitStudioHull
@@ -404,6 +550,38 @@ static void Mod_StudioCalcRotations( int boneused[], int numbones, const byte *p
}
void Mod_SwapStudioSeqGroupAnims( studiohdr_t *phdr, mstudioseqdesc_t *pseq, byte *buf )
{
mstudioanim_t *panim = (mstudioanim_t *)( buf + pseq->animindex );
int numanims = pseq->numblends * phdr->numbones;
for( int j = 0; j < numanims; j++, panim++ )
{
le_struct_swap( mstudioanim_swap, panim );
for( int k = 0; k < 6; k++ )
{
if( panim->offset[k] == 0 )
continue;
mstudioanimvalue_t *panimvalue = (mstudioanimvalue_t *)((byte *)panim + panim->offset[k] );
int frames = pseq->numframes;
while( frames > 0 )
{
int valid = panimvalue->num.valid;
int total = panimvalue->num.total;
for( int l = 1; l <= valid; l++ )
panimvalue[l].value = LittleShort( panimvalue[l].value );
panimvalue += valid + 1;
frames -= total;
}
}
}
}
/*
====================
StudioGetAnim
@@ -412,17 +590,10 @@ StudioGetAnim
*/
void *R_StudioGetAnim( studiohdr_t *m_pStudioHeader, model_t *m_pSubModel, mstudioseqdesc_t *pseqdesc )
{
mstudioseqgroup_t *pseqgroup;
cache_user_t *paSequences;
fs_offset_t filesize;
byte *buf;
pseqgroup = (mstudioseqgroup_t *)((byte *)m_pStudioHeader + m_pStudioHeader->seqgroupindex) + pseqdesc->seqgroup;
if( pseqdesc->seqgroup == 0 )
return ((byte *)m_pStudioHeader + pseqdesc->animindex);
paSequences = (cache_user_t *)m_pSubModel->submodels;
cache_user_t *paSequences = (cache_user_t *)m_pSubModel->submodels;
if( paSequences == NULL )
{
paSequences = (cache_user_t *)Mem_Calloc( com_studiocache, MAXSTUDIOGROUPS * sizeof( cache_user_t ));
@@ -432,23 +603,31 @@ void *R_StudioGetAnim( studiohdr_t *m_pStudioHeader, model_t *m_pSubModel, mstud
// check for already loaded
if( !Mod_CacheCheck(( cache_user_t *)&( paSequences[pseqdesc->seqgroup] )))
{
string filepath, modelname, modelpath;
string modelname;
COM_FileBase( m_pSubModel->name, modelname, sizeof( modelname ));
string modelpath;
COM_ExtractFilePath( m_pSubModel->name, modelpath );
// NOTE: here we build real sub-animation filename because stupid user may rename model without recompile
string filepath;
Q_snprintf( filepath, sizeof( filepath ), "%s/%s%i%i.mdl", modelpath, modelname, pseqdesc->seqgroup / 10, pseqdesc->seqgroup % 10 );
buf = FS_LoadFile( filepath, &filesize, false );
if( !buf || !filesize ) Host_Error( "%s: can't load %s\n", __func__, filepath );
if( IDSEQGRPHEADER != *(uint *)buf ) Host_Error( "%s: %s is corrupted\n", __func__, filepath );
fs_offset_t filesize;
byte *buf = FS_LoadFile( filepath, &filesize, false );
if( !buf || !filesize )
Host_Error( "%s: can't load %s\n", __func__, filepath );
if( LittleLong( IDSEQGRPHEADER ) != *(uint *)buf )
Host_Error( "%s: %s is corrupted\n", __func__, filepath );
Con_Printf( "loading: %s\n", filepath );
paSequences[pseqdesc->seqgroup].data = Mem_Calloc( com_studiocache, filesize );
memcpy( paSequences[pseqdesc->seqgroup].data, buf, filesize );
Mem_Free( buf );
Mod_SwapStudioSeqGroupAnims( m_pStudioHeader, pseqdesc,
(byte *)paSequences[pseqdesc->seqgroup].data );
}
return ((byte *)paSequences[pseqdesc->seqgroup].data + pseqdesc->animindex);
@@ -690,7 +869,6 @@ static void Mod_StudioComputeBounds( void *buffer, vec3_t mins, vec3_t maxs, qbo
vec3_t bone_mins, bone_maxs;
vec3_t vert_mins, vert_maxs;
int vert_count, bone_count;
int bodyCount = 0;
vec3_t pos, *pverts;
vert_count = bone_count = 0;
@@ -706,18 +884,16 @@ static void Mod_StudioComputeBounds( void *buffer, vec3_t mins, vec3_t maxs, qbo
// each body part has nummodels variations so there are as many total variations as there
// are in a matrix of each part by each other part
for( i = 0; i < pstudiohdr->numbodyparts; i++ )
bodyCount += pbodypart[i].nummodels;
// The studio models we want are vec3_t mins, vec3_t maxsight after the bodyparts (still need to
// find a detailed breakdown of the mdl format). Move pointer there.
m_pSubModel = (mstudiomodel_t *)(&pbodypart[pstudiohdr->numbodyparts]);
for( i = 0; i < bodyCount; i++ )
{
pverts = (vec3_t *)((byte *)pstudiohdr + m_pSubModel[i].vertindex);
m_pSubModel = (mstudiomodel_t *)((byte *)pstudiohdr + pbodypart[i].modelindex);
for( j = 0; j < m_pSubModel[i].numverts; j++ )
Mod_StudioBoundVertex( bone_mins, bone_maxs, &vert_count, pverts[j] );
for( j = 0; j < pbodypart[i].nummodels; j++ )
{
pverts = (vec3_t *)((byte *)pstudiohdr + m_pSubModel[j].vertindex);
for( k = 0; k < m_pSubModel[j].numverts; k++ )
Mod_StudioBoundVertex( bone_mins, bone_maxs, &vert_count, pverts[k] );
}
}
pbones = (mstudiobone_t *)((byte *)pstudiohdr + pstudiohdr->boneindex);
@@ -820,26 +996,123 @@ static int Mod_StudioBodyVariations( model_t *mod )
return count;
}
static qboolean Mod_SwapStudioModel( const char *name, void *buffer, size_t buffersize )
{
studiohdr_t *phdr = buffer;
byte *mod_base = buffer;
if( buffersize < sizeof( studiohdr_t ))
return false;
le_struct_swap( studiohdr_swap, phdr );
if( phdr->ident != IDSTUDIOHEADER || phdr->version != STUDIO_VERSION )
return false;
#if XASH_BIG_ENDIAN
if( phdr->studiohdr2index > 0 && phdr->studiohdr2index < phdr->length )
{
Con_Printf( S_ERROR "byteswapping extended studio model \"%s\" is unsupoprted\n", name );
return false;
}
#endif
for( int i = 0; i < phdr->numbones; i++ )
le_struct_swap( mstudiobone_swap, (mstudiobone_t *)( mod_base + phdr->boneindex ) + i );
for( int i = 0; i < phdr->numbonecontrollers; i++ )
le_struct_swap( mstudiobonecontroller_swap, (mstudiobonecontroller_t *)( mod_base + phdr->bonecontrollerindex ) + i );
for( int i = 0; i < phdr->numhitboxes; i++ )
le_struct_swap( mstudiobbox_swap, (mstudiobbox_t *)( mod_base + phdr->hitboxindex ) + i );
for( int i = 0; i < phdr->numseqgroups; i++ )
le_struct_swap( mstudioseqgroup_swap, (mstudioseqgroup_t *)( mod_base + phdr->seqgroupindex ) + i );
for( int i = 0; i < phdr->numattachments; i++ )
{
mstudioattachment_t *pattach = (mstudioattachment_t *)( mod_base + phdr->attachmentindex ) + i;
le_struct_swap( mstudioattachment_swap, pattach );
le_array_swap((float *)pattach->vectors, 3 * 3 );
}
for( int i = 0; i < phdr->numtextures; i++ )
le_struct_swap( mstudiotexture_swap, (mstudiotexture_t *)( mod_base + phdr->textureindex ) + i );
for( int i = 0; i < phdr->numbodyparts; i++ )
le_struct_swap( mstudiobodyparts_swap, (mstudiobodyparts_t *)( mod_base + phdr->bodypartindex ) + i );
for( int i = 0; i < phdr->numseq; i++ )
{
mstudioseqdesc_t *pseq = (mstudioseqdesc_t *)( mod_base + phdr->seqindex ) + i;
le_struct_swap( mstudioseqdesc_swap, pseq );
for( int j = 0; j < pseq->numevents; j++ )
le_struct_swap( mstudioevent_swap, (mstudioevent_t *)( mod_base + pseq->eventindex ) + j );
if( pseq->seqgroup != 0 )
continue;
Mod_SwapStudioSeqGroupAnims( phdr, pseq, mod_base );
}
for( int i = 0; i < phdr->numbodyparts; i++ )
{
mstudiobodyparts_t *pbodypart = (mstudiobodyparts_t *)( mod_base + phdr->bodypartindex ) + i;
for( int j = 0; j < pbodypart->nummodels; j++ )
{
mstudiomodel_t *pmodel = (mstudiomodel_t *)( mod_base + pbodypart->modelindex ) + j;
le_struct_swap( mstudiomodel_swap, pmodel );
le_array_swap((float *)( mod_base + pmodel->vertindex ), pmodel->numverts * 3 );
le_array_swap((float *)( mod_base + pmodel->normindex ), pmodel->numnorms * 3 );
for( int k = 0; k < pmodel->nummesh; k++ )
{
mstudiomesh_t *pmesh = (mstudiomesh_t *)( mod_base + pmodel->meshindex ) + k;
le_struct_swap( mstudiomesh_swap, pmesh );
short *ptricmds = (short *)( mod_base + pmesh->triindex );
short n;
while(( n = LittleShort( *ptricmds )))
{
*ptricmds++ = n;
int count = abs( n ) * 4;
for( int l = 0; l < count; l++, ptricmds++ )
*ptricmds = LittleShort( *ptricmds );
}
*ptricmds = 0;
}
}
}
short *pskinref = (short *)( mod_base + phdr->skinindex );
for( int i = 0; i < phdr->numskinfamilies * phdr->numskinref; i++ )
pskinref[i] = LittleShort( pskinref[i] );
return true;
}
/*
=================
R_StudioLoadHeader
=================
*/
static studiohdr_t *R_StudioLoadHeader( model_t *mod, const void *buffer )
static studiohdr_t *R_StudioLoadHeader( model_t *mod, void *buffer, size_t buffersize )
{
byte *pin;
studiohdr_t *phdr;
int i;
studiohdr_t *phdr;
if( !buffer ) return NULL;
if( !buffer )
return NULL;
pin = (byte *)buffer;
phdr = (studiohdr_t *)pin;
i = phdr->version;
if( i != STUDIO_VERSION )
if( !Mod_SwapStudioModel( mod->name, buffer, buffersize ))
{
Con_Printf( S_ERROR "%s has wrong version number (%i should be %i)\n", mod->name, i, STUDIO_VERSION );
phdr = (studiohdr_t *)buffer;
Con_Printf( S_ERROR "%s has wrong version number (%i should be %i)\n", mod->name, phdr->version, STUDIO_VERSION );
return NULL;
}
@@ -867,7 +1140,7 @@ static studiohdr_t *Mod_MaybeTruncateStudioTextureData( model_t *mod )
Mod_LoadStudioModel
=================
*/
void Mod_LoadStudioModel( model_t *mod, const void *buffer, qboolean *loaded )
void Mod_LoadStudioModel( model_t *mod, void *buffer, size_t buffersize, qboolean *loaded )
{
char poolname[MAX_VA_STRING];
studiohdr_t *phdr;
@@ -879,7 +1152,7 @@ void Mod_LoadStudioModel( model_t *mod, const void *buffer, qboolean *loaded )
mod->mempool = Mem_AllocPool( poolname );
mod->type = mod_studio;
phdr = R_StudioLoadHeader( mod, buffer );
phdr = R_StudioLoadHeader( mod, buffer, buffersize );
if( !phdr || phdr->length < sizeof( studiohdr_t )) // garbage value in length
return; // bad model
@@ -888,9 +1161,10 @@ void Mod_LoadStudioModel( model_t *mod, const void *buffer, qboolean *loaded )
{
studiohdr_t *thdr;
void *buffer2;
fs_offset_t size2_len;
buffer2 = FS_LoadFile( Mod_StudioTexName( mod->name ), NULL, false );
thdr = R_StudioLoadHeader( mod, buffer2 );
buffer2 = FS_LoadFile( Mod_StudioTexName( mod->name ), &size2_len, false );
thdr = R_StudioLoadHeader( mod, buffer2, size2_len );
if( thdr != NULL )
{

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
}
/*
@@ -333,7 +347,7 @@ model_t *Mod_LoadModel( model_t *mod, qboolean crash )
switch( *(uint *)buf )
{
case LittleLong( IDSTUDIOHEADER ):
Mod_LoadStudioModel( mod, buf, &loaded );
Mod_LoadStudioModel( mod, buf, length, &loaded );
break;
case LittleLong( IDSPRITEHEADER ):
Mod_LoadSpriteModel( mod, buf, length, &loaded );
@@ -594,6 +608,31 @@ void GAME_EXPORT Mod_LoadCacheFile( const char *filename, cache_user_t *cu )
cu->data = Mem_Malloc( com_studiocache, size );
memcpy( cu->data, buf, size );
Mem_Free( buf );
// this handles when studio model renderer tries to load sequence files on it's own
// which is what they always do in HLSDK
#if XASH_BIG_ENDIAN
if( size >= sizeof( int ) && LittleLong( IDSEQGRPHEADER ) == *(uint *)cu->data )
{
studiohdr_t *phdr = (studiohdr_t *)REF_GET_PARM( PARM_GET_STUDIO_HDR, 0 );
if( !phdr )
return;
mstudioseqdesc_t *pseq = (mstudioseqdesc_t *)((byte *)phdr + phdr->seqindex );
for( int i = 0; i < phdr->numseq; i++ )
{
if( pseq[i].seqgroup == 0 )
continue;
mstudioseqgroup_t *pgrp = (mstudioseqgroup_t *)((byte *)phdr + phdr->seqgroupindex ) + pseq[i].seqgroup;
// assuming filename passes seqgroup's name
if( !Q_stricmp( pgrp->name, filename ))
Mod_SwapStudioSeqGroupAnims( phdr, &pseq[i], (byte *)cu->data );
}
}
#endif
}
/*

View File

@@ -321,8 +321,10 @@ void MSG_WriteUBitLong( sizebuf_t *sb, uint curData, int numbits )
}
iCurBitMasked = iCurBit & 31;
((uint32_t *)sb->pData)[iDWord] &= BitWriteMasks[iCurBitMasked][nBitsLeft-1];
((uint32_t *)sb->pData)[iDWord] |= curData << iCurBitMasked;
uint32_t dword = LittleLong(((uint32_t *)sb->pData)[iDWord] );
dword &= BitWriteMasks[iCurBitMasked][nBitsLeft-1];
dword |= curData << iCurBitMasked;
((uint32_t *)sb->pData)[iDWord] = LittleLong( dword );
// did it span a dword?
nBitsWritten = 32 - iCurBitMasked;
@@ -334,8 +336,10 @@ void MSG_WriteUBitLong( sizebuf_t *sb, uint curData, int numbits )
curData >>= nBitsWritten;
iCurBitMasked = iCurBit & 31;
((uint32_t *)sb->pData)[iDWord+1] &= BitWriteMasks[iCurBitMasked][nBitsLeft-1];
((uint32_t *)sb->pData)[iDWord+1] |= curData << iCurBitMasked;
uint32_t dword2 = LittleLong(((uint32_t *)sb->pData)[iDWord+1] );
dword2 &= BitWriteMasks[iCurBitMasked][nBitsLeft-1];
dword2 |= curData << iCurBitMasked;
((uint32_t *)sb->pData)[iDWord+1] = LittleLong( dword2 );
}
sb->iCurBit += numbits;
}
@@ -396,7 +400,8 @@ qboolean MSG_WriteBits( sizebuf_t *sb, const void *pData, int nBits )
// read dwords.
while( nBitsLeft >= 32 )
{
MSG_WriteUBitLong( sb, *(( uint32_t *)pOut ), 32 );
uint32_t dword = *((uint32_t *)pOut);
MSG_WriteUBitLong( sb, LittleLong( dword ), 32 );
pOut += sizeof( uint32_t );
nBitsLeft -= 32;
@@ -519,7 +524,7 @@ void MSG_WriteDword( sizebuf_t *sb, uint val )
void MSG_WriteFloat( sizebuf_t *sb, float val )
{
MSG_WriteBits( sb, &val, sizeof( val ) << 3 );
MSG_WriteUBitLong( sb, FloatAsUint( val ), sizeof( val ) << 3 );
}
qboolean MSG_WriteBytes( sizebuf_t *sb, const void *pBuf, int nBytes )
@@ -593,7 +598,7 @@ uint MSG_ReadUBitLong( sizebuf_t *sb, int numbits )
// Read the current dword.
idword1 = sb->iCurBit >> 5;
dword1 = ((uint *)sb->pData)[idword1];
dword1 = LittleLong(((uint *)sb->pData)[idword1] );
dword1 >>= ( sb->iCurBit & 31 ); // get the bits we're interested in.
sb->iCurBit += numbits;
@@ -608,7 +613,7 @@ uint MSG_ReadUBitLong( sizebuf_t *sb, int numbits )
else
{
int nExtraBits = sb->iCurBit & 31;
uint dword2 = ((uint *)sb->pData)[idword1+1] & ExtraMasks[nExtraBits];
uint dword2 = LittleLong(((uint *)sb->pData)[idword1+1] ) & ExtraMasks[nExtraBits];
// no need to mask since we hit the end of the dword.
// shift the second dword's part into the high bits.
@@ -633,7 +638,8 @@ qboolean MSG_ReadBits( sizebuf_t *sb, void *pOutData, int nBits )
// read dwords.
while( nBitsLeft >= 32 )
{
*((uint32_t *)pOut) = MSG_ReadUBitLong( sb, 32 );
uint32_t dword = MSG_ReadUBitLong( sb, 32 );
*((uint32_t *)pOut) = LittleLong( dword );
pOut += sizeof( uint32_t );
nBitsLeft -= 32;
}
@@ -790,11 +796,7 @@ uint MSG_ReadDword( sizebuf_t *sb )
float MSG_ReadFloat( sizebuf_t *sb )
{
float ret;
MSG_ReadBits( sb, &ret, sizeof( ret ) << 3 );
return ret;
return UintAsFloat( MSG_ReadUBitLong( sb, sizeof( float ) << 3 ));
}
qboolean MSG_ReadBytes( sizebuf_t *sb, void *pOut, int nBytes )

View File

@@ -617,7 +617,10 @@ void Netchan_AddBufferToList( fragbuf_t **pplist, fragbuf_t *pbuf )
if( id1 > id2 )
{
// insert here
pbuf->next = n->next;
// pbuf->next = n->next;
// a1ba: this seems to be incorrect insertion, as `n` gets removed from list and effectively leaked
pbuf->next = n;
pprev->next = pbuf;
return;
}
@@ -1314,14 +1317,14 @@ qboolean Netchan_CopyFileFragments( netchan_t *chan, sizebuf_t *msg )
Host_Error( "%s: BZ2 compression is not supported for server", __func__ );
#endif
}
else if( chan->use_lzss && LZSS_IsCompressed( buffer, nsize + 1 ))
else if( chan->use_lzss && LZSS_IsCompressed( buffer, nsize ))
{
byte *uncompressedBuffer;
uncompressedSize = LZSS_GetActualSize( buffer, nsize + 1 ) + 1;
uncompressedSize = LZSS_GetActualSize( buffer, nsize );
uncompressedBuffer = Mem_Calloc( net_mempool, uncompressedSize );
nsize = LZSS_Decompress( buffer, uncompressedBuffer, nsize + 1, uncompressedSize );
nsize = LZSS_Decompress( buffer, uncompressedBuffer, nsize, uncompressedSize );
Mem_Free( buffer );
buffer = uncompressedBuffer;
}
@@ -1658,7 +1661,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,37 +84,37 @@ void Sound_Shutdown( void )
uint GAME_EXPORT Sound_GetApproxWavePlayLen( const char *filepath )
{
string name;
file_t *f;
wavehdr_t wav;
size_t filesize;
uint msecs;
string name;
Q_strncpy( name, filepath, sizeof( name ));
COM_FixSlashes( name );
f = FS_Open( name, "rb", false );
file_t *f = FS_Open( name, "rb", false );
if( !f )
return 0;
wavehdr_t wav;
if( FS_Read( f, &wav, sizeof( wav )) != sizeof( wav ))
{
FS_Close( f );
return 0;
}
filesize = FS_FileLength( f );
filesize -= 128; // magic number from GoldSrc, seems to be header size
// magic number from GoldSrc, seems to be header size
size_t filesize = FS_FileLength( f ) - 128;
FS_Close( f );
// is real wav file ?
if( wav.riff_id != RIFFHEADER || wav.wave_id != WAVEHEADER || wav.fmt_id != FORMHEADER )
if( wav.riff_id != LittleLong( RIFFHEADER ) || wav.wave_id != LittleLong( WAVEHEADER ) || wav.fmt_id != LittleLong( FORMHEADER ))
return 0;
if( wav.nAvgBytesPerSec >= 1000 )
msecs = (uint)((float)filesize / ((float)wav.nAvgBytesPerSec / 1000.0f));
else msecs = (uint)(((float)filesize / (float)wav.nAvgBytesPerSec) * 1000.0f);
int avgBytes = LittleLong( wav.nAvgBytesPerSec );
uint msecs;
if( avgBytes >= 1000 )
msecs = (uint)((float)filesize / ((float)avgBytes / 1000.0f));
else
msecs = (uint)(((float)filesize / (float)avgBytes) * 1000.0f);
return msecs;
}

View File

@@ -61,6 +61,8 @@ SYSTEM LOG
*/
int Sys_LogFileNo( void )
{
if( !s_ld.logfile )
return -1;
return s_ld.logfileno;
}
@@ -84,7 +86,7 @@ void Sys_InitLog( void )
if( Sys_CheckParm( "-log" ))
{
if( !Sys_GetParmFromCmdLine( "-log", s_ld.log_path ) || !isalnum( s_ld.log_path[0] ))
if( !Sys_GetParmFromCmdLine( "-log", s_ld.log_path ) || !isalnum((byte)s_ld.log_path[0] ))
Q_strncpy( s_ld.log_path, "engine.log", sizeof( s_ld.log_path ));
COM_DefaultExtension( s_ld.log_path, ".log", sizeof( s_ld.log_path ));

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,13 +17,9 @@
#import <Foundation/Foundation.h>
#import <UIKit/UIKit.h>
#import <AVFoundation/AVFoundation.h>
#import "FtpServer.h"
#include <sys/stat.h>
#include "dlfcn.h"
#ifndef XASH_GAMEDIR
#define XASH_GAMEDIR "valve" // !!! Replace with your default (base) game directory !!!
#endif
#define XASHLIB "@rpath/libxash.dylib"
@@ -81,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
@@ -191,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:"];
@@ -200,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]];
@@ -217,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" );
@@ -228,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);
@@ -251,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);
@@ -266,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];
@@ -302,11 +249,8 @@ void IOS_LaunchDialog( void )
alert.delegate = nil;
[ftpswitch release];
[ftptitle release];
[args release];
[argstitle release];
[port release];
[suffix release];
[suffixtitle release];
@@ -331,4 +275,4 @@ int IOS_GetArgs( char ***out )
{
*out = szArgv;
return szArgc;
}
}

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 "crtlib.h"
#include "fscallback.h"
#include "library.h"
#include "platform/ios/lib_ios.h"
#include <dlfcn.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(), "valve" ) )
{
postfix = FS_Gamedir( );
}
else postfix = "";
}
Q_strncpy( strippedname, dllname, sizeof( strippedname ) );
COM_StripExtension(strippedname);
Q_snprintf( name, MAX_STRING, "%s_%s.dylib", strippedname, postfix );
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 ];
}

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