Compare commits

...

130 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
173 changed files with 5712 additions and 10965 deletions

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

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

View File

@@ -159,11 +159,22 @@ typedef struct snd_globals_s
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 );
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)

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

@@ -82,6 +82,7 @@ typedef struct cldll_func_s
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

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

@@ -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 )
{
@@ -130,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

View File

@@ -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();

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 ))
{

View File

@@ -761,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
@@ -874,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
@@ -972,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
//
@@ -1209,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 );
@@ -1234,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

@@ -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"
@@ -105,7 +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 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 );
@@ -373,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 )
{
@@ -1423,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;
}
@@ -1438,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;
}
@@ -1453,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;
}
@@ -2172,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;
}
@@ -3323,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 );
@@ -3928,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;
@@ -4065,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 );
@@ -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
//

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

@@ -65,6 +65,10 @@ static CVAR_DEFINE_AUTO( joy_calibrated, "0", FCVAR_READ_ONLY, "tells whether cu
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
@@ -194,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;
@@ -315,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] )
{
@@ -337,12 +341,19 @@ void Joy_FinalizeMove( float *fw, float *side, float *dpitch, float *dyaw )
*dpitch += joy_pitch.value * (float)joyaxis[JOY_AXIS_PITCH].val/(float)SHRT_MAX * host.realframetime;
*dyaw -= joy_yaw.value * (float)joyaxis[JOY_AXIS_YAW ].val/(float)SHRT_MAX * host.realframetime;
if( joy_have_gyro.value && (int)joy_calibrated.value == JOY_CALIBRATED )
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;
@@ -467,6 +478,12 @@ void Joy_DrawDebug( void )
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++ )
{
@@ -485,10 +502,22 @@ void Joy_DrawDebug( void )
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 deadzones or prevval like axes
// gyro doesn't have prevval like axes
y += bar_h + 2;
}
}
@@ -568,10 +597,16 @@ 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

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 )
{

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,6 +564,7 @@ static void IN_CollectInput( float *forward, float *side, float *pitch, float *y
#endif
}
IN_GyroFinalizeMove( forward, side, pitch, yaw );
Joy_FinalizeMove( forward, side, pitch, yaw );
Touch_GetMove( forward, side, pitch, yaw );

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"
@@ -704,13 +705,35 @@ CL_ParseResourceRequest
==================
*/
static 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 @@ static 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 );
}
/*
@@ -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 ();

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

View File

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

@@ -1287,7 +1287,7 @@ uint S_RawSamplesStereo( portable_samplepair_t *rawsamples, uint rawend, uint ma
S_RawEntSamples
===================
*/
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 )
{
rawchan_t *ch;
@@ -1298,7 +1298,7 @@ void S_RawEntSamples( int entnum, uint samples, uint rate, word width, word chan
return;
ch->master_vol = snd_vol;
ch->dist_mult = (ATTN_NONE / SND_CLIP_DISTANCE);
ch->dist_mult = (attn / SND_CLIP_DISTANCE);
ch->s_rawend = S_RawSamplesStereo( ch->rawsamples, ch->s_rawend, ch->max_samples, samples, rate, width, channels, data );
ch->leftvol = ch->rightvol = snd_vol;
}
@@ -1938,6 +1938,9 @@ static void S_VoiceRecordStop_f( void )
static const sound_api_t gSoundAPI = {
CL_GetEntitySpatialization,
S_GetSfxByHandle,
S_RawEntSamples,
SND_ForceInitMouth,
Voice_GetAudioInfo,
};
/*

View File

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

View File

@@ -403,11 +403,11 @@ 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 );

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

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

@@ -615,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 );

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 ));
@@ -967,8 +967,8 @@ static void Test_LZSS( void )
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

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

View File

@@ -833,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 );
}
@@ -1276,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
@@ -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
@@ -3746,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
@@ -3790,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 + 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;
}
@@ -3819,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 )
{
@@ -3848,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:
@@ -3863,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 )
{
@@ -3875,17 +4296,17 @@ 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
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 );
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?
@@ -3893,6 +4314,7 @@ static qboolean Mod_LoadBmodelLumps( model_t *mod, byte *mod_base, size_t buffer
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
}
@@ -3957,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;
}
@@ -3999,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 ));
@@ -4012,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 )
{
@@ -4037,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;
@@ -4057,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;
}

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

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

View File

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

File diff suppressed because it is too large Load Diff

View File

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

File diff suppressed because it is too large Load Diff

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -1,95 +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 <unistd.h>
#include <SDL.h>
#include <dlfcn.h>
#include <sys/stat.h>
#include "crtlib.h"
#include "library.h"
#include "platform/ios/lib_ios.h"
#include "common.h"
const char *g_szLibrarySuffix;
static void *IOS_LoadLibraryInternal( const char *dllname )
{
void *pHandle;
char path[MAX_SYSPATH];
Q_snprintf( path, sizeof( path ), "%s%s", SDL_GetBasePath(), dllname );
pHandle = dlopen( path, RTLD_LAZY );
if( !pHandle )
COM_PushLibraryError(dlerror( ));
return pHandle;
}
static qboolean IOS_LibraryExistsInternal( const char *name )
{
struct stat buf;
char path[MAX_SYSPATH];
Q_snprintf( path, sizeof( path ), "%s%s", SDL_GetBasePath(), name );
return stat( path, &buf ) == 0;
}
static const char *IOS_GetLibraryPostfix( void )
{
if( g_szLibrarySuffix )
return g_szLibrarySuffix;
return Q_strcmp( host.default_gamedir, FS_Gamedir( )) ? FS_Gamedir( ) : "";
}
static void IOS_PrepareGameLibraryPath( const char *dllname, char *out, size_t outsize )
{
string strippedname;
Q_strncpy( strippedname, dllname, sizeof( strippedname ));
COM_StripExtension( strippedname );
Q_snprintf( out, outsize, "%s_%s.dylib", strippedname, IOS_GetLibraryPostfix( ));
}
void *IOS_LoadLibrary( const char *dllname )
{
// filesystem_stdio is a special case, as engine won't work correctly without it
// but it's always located at known path
if( !Q_strcmp( dllname, "filesystem_stdio.dylib" ))
return IOS_LoadLibraryInternal( dllname );
string name;
char *pHandle;
IOS_PrepareGameLibraryPath( dllname, name, sizeof( name ));
pHandle = IOS_LoadLibraryInternal( name );
if( !pHandle )
pHandle = IOS_LoadLibraryInternal( dllname );
return pHandle;
}
qboolean IOS_LibraryExists( const char *dllname )
{
string name;
IOS_PrepareGameLibraryPath( dllname, name, sizeof( name ));
return IOS_LibraryExistsInternal( name ) || IOS_LibraryExistsInternal( dllname );
}

View File

@@ -1,26 +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 );
qboolean IOS_LibraryExists( const char *name );
#endif // IOS_LIB_H
#endif // TARGET_OS_IPHONE

View File

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

View File

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

View File

@@ -40,12 +40,23 @@ void Platform_MessageBox( const char *title, const char *message, qboolean paren
void Platform_SetStatus( const char *status );
qboolean Platform_DebuggerPresent( void );
typedef enum
{
ORIENTATION_UNKNOWN = 0,
ORIENTATION_LANDSCAPE,
ORIENTATION_LANDSCAPE_FLIPPED,
ORIENTATION_PORTRAIT,
ORIENTATION_PORTRAIT_FLIPPED
} platform_orientation_t;
platform_orientation_t Platform_GetDisplayOrientation( void );
// legacy iOS port functions
#if TARGET_OS_IOS
#if XASH_IOS
int IOS_GetArgs( char ***argv );
const char *IOS_GetDocsDir( void );
const char *IOS_GetExecDir( void );
void IOS_LaunchDialog( void );
#include "platform/ios/lib_ios.h"
#endif // TARGET_OS_IOS
#if XASH_WIN32 || XASH_LINUX
@@ -64,6 +75,7 @@ char *Posix_Input( void );
void SDLash_Init( void );
void SDLash_Shutdown( void );
void SDLash_NanoSleep( int nsec );
qboolean SDLash_GyroIsAvailable( void );
#endif
#if XASH_ANDROID
@@ -212,9 +224,7 @@ static inline void Sys_RestoreCrashHandler( void )
static inline qboolean Platform_LibraryExists( const char *name, qboolean gamedironly )
{
#if XASH_IOS
return IOS_LibraryExists( name );
#elif XASH_ANDROID
#if XASH_ANDROID
// sorry, unimplemented
return false;
#else

View File

@@ -27,15 +27,10 @@ static qboolean enable_libbacktrace;
static void Sys_BacktraceError( void *data, const char *msg, int errnum )
{
if( errnum < 0 )
{
Con_Printf( S_ERROR "no symbol info, no libbacktrace\n" );
return;
}
Con_Printf( S_ERROR "libbacktrace: %s (%d)\n", msg, errnum );
Con_Printf( S_ERROR "libbacktrace error: %s (%d)\n", msg, errnum );
enable_libbacktrace = false;
if( errnum > 0 )
enable_libbacktrace = false;
}
struct print_data
@@ -45,8 +40,16 @@ struct print_data
int len;
int idx;
int logfd;
qboolean skip_wrappers;
};
static qboolean Sys_IsCrashHandlerFrame( const char *name )
{
if( !name )
return false;
return !Q_strcmp( name, "Sys_Crash" ) || !Q_strcmp( name, "Sys_CrashDetailsLibbacktrace" );
}
static void Sys_AppendPrint( struct print_data *pd, const char *fmt, ... )
{
va_list va;
@@ -58,13 +61,10 @@ static void Sys_AppendPrint( struct print_data *pd, const char *fmt, ... )
if( len > 0 )
{
char ch = '\n';
write( pd->logfd, pd->message, len );
write( pd->logfd, &ch, 1 );
if( pd->logfd >= 0 )
write( pd->logfd, pd->message, len );
write( STDERR_FILENO, pd->message, len );
write( STDERR_FILENO, &ch, 1 );
pd->message += len;
pd->len += len;
@@ -84,6 +84,13 @@ static void Sys_BacktracePrintSyminfo( void *data, uintptr_t pc, const char *sym
Dl_info dlinfo = { 0 };
const char *module_name;
if( pd->skip_wrappers )
{
if( Sys_IsCrashHandlerFrame( symname ))
return;
pd->skip_wrappers = false;
}
if( dladdr((void *)pc, &dlinfo ))
module_name = dlinfo.dli_fname;
else module_name = NULL;
@@ -110,6 +117,13 @@ static int Sys_BacktracePrintFull( void *data, uintptr_t pc, const char *filenam
Dl_info dlinfo = { 0 };
const char *module_name;
if( pd->skip_wrappers )
{
if( Sys_IsCrashHandlerFrame( function ))
return 0;
pd->skip_wrappers = false;
}
if( dladdr((void *)pc, &dlinfo ))
module_name = dlinfo.dli_fname;
else module_name = NULL;
@@ -136,12 +150,13 @@ int Sys_CrashDetailsLibbacktrace( int logfd, char *message, int len, size_t max_
struct print_data pd =
{
.message = message + len,
.message_size = sizeof( message ) - len,
.message_size = max_len - len,
.logfd = logfd,
.len = len,
.skip_wrappers = true,
};
backtrace_full( g_bt_state, 1, Sys_BacktracePrintFull, Sys_BacktracePrintError, &pd );
backtrace_full( g_bt_state, 0, Sys_BacktracePrintFull, Sys_BacktracePrintError, &pd );
return pd.len;
}

View File

@@ -15,14 +15,7 @@ GNU General Public License for more details.
#include "common.h"
// while this is mostly POSIX-compatible functions
// the contents of ucontext_t is platform-dependent
// before adding new OS here, check platform.h
#define _XOPEN_SOURCE 1 // required for ucontext
#if XASH_FREEBSD || XASH_NETBSD || XASH_OPENBSD || XASH_ANDROID || XASH_LINUX || XASH_APPLE
#ifndef XASH_OPENBSD
#include <ucontext.h>
#endif
#include <signal.h>
#include <sys/mman.h>
#include "library.h"
@@ -51,7 +44,8 @@ static void Sys_Crash( int signal, siginfo_t *si, void *context )
// now get log fd and write trace directly to log
logfd = Sys_LogFileNo();
write( logfd, message, len );
if( logfd >= 0 )
write( logfd, message, len );
#if HAVE_LIBBACKTRACE
if( have_libbacktrace && !detailed_message )

View File

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

View File

@@ -106,7 +106,7 @@ qboolean SNDDMA_Init( void )
memset( &desired, 0, sizeof( desired ) );
desired.freq = SOUND_DMA_SPEED;
desired.format = AUDIO_S16LSB;
desired.format = AUDIO_S16SYS;
desired.samples = 1024;
desired.channels = 2;
desired.callback = SDL_SoundCallback;
@@ -119,7 +119,7 @@ qboolean SNDDMA_Init( void )
return false;
}
if( obtained.format != AUDIO_S16LSB )
if( obtained.format != AUDIO_S16SYS )
{
Con_Printf( "SDL audio format %d unsupported.\n", obtained.format );
goto fail;

View File

@@ -248,13 +248,6 @@ static void SDLash_ActiveEvent( int gain )
}
else
{
#if TARGET_OS_IPHONE
{
// Keep running if ftp server enabled
void IOS_StartBackgroundTask( void );
IOS_StartBackgroundTask();
}
#endif
host.status = HOST_NOFOCUS;
if( cls.key_dest == key_game )
@@ -375,6 +368,11 @@ static void SDLash_EventHandler( SDL_Event *event )
#endif
SDLash_HandleGameControllerEvent( event );
break;
#if SDL_VERSION_ATLEAST( 2, 0, 14 )
case SDL_SENSORUPDATE:
SDLash_SensorUpdate( event->sensor );
break;
#endif
case SDL_WINDOWEVENT:
if( event->window.windowID != SDL_GetWindowID( host.hWnd ) )

View File

@@ -35,5 +35,15 @@ void SDLash_FreeCursors( void );
//
void SDLash_HandleGameControllerEvent( SDL_Event *ev );
//
// sensor_sdl2.c
//
void SDLash_InitSensors( void );
void SDLash_ShutdownSensors( void );
qboolean SDLash_GyroIsAvailable( void );
#if SDL_VERSION_ATLEAST( 2, 0, 14 )
void SDLash_SensorUpdate( SDL_SensorEvent sensor );
#endif
#endif // XASH_SDL
#endif // KEYWRAPPER_H

View File

@@ -123,7 +123,7 @@ qboolean SNDDMA_Init( void )
memset( &desired, 0, sizeof( desired ) );
desired.freq = SOUND_DMA_SPEED;
desired.format = AUDIO_S16LSB;
desired.format = AUDIO_S16SYS;
desired.samples = 1024;
desired.channels = 2;
desired.callback = SDL_SoundCallback;
@@ -136,7 +136,7 @@ qboolean SNDDMA_Init( void )
return false;
}
if( obtained.format != AUDIO_S16LSB )
if( obtained.format != AUDIO_S16SYS )
{
Con_Printf( "SDL audio format %d unsupported.\n", obtained.format );
goto fail;
@@ -277,7 +277,7 @@ qboolean VoiceCapture_Init( void )
SDL_zero( wanted );
wanted.freq = voice.samplerate;
wanted.format = AUDIO_S16LSB;
wanted.format = AUDIO_S16SYS;
wanted.channels = VOICE_PCM_CHANNELS;
wanted.samples = voice.frame_size;
wanted.callback = SDL_SoundInputCallback;

View File

@@ -0,0 +1,103 @@
/*
sensor_sdl2.c - SDL2 sensor handling
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 <SDL.h>
#include "common.h"
#include "input.h"
#include "platform_sdl2.h"
#if SDL_VERSION_ATLEAST( 2, 0, 14 )
static SDL_SensorID g_system_gyro_id = -1;
static SDL_Sensor *g_system_gyro;
/*
==============
SDLash_InitSensors
==============
*/
void SDLash_InitSensors( void )
{
if( SDL_InitSubSystem( SDL_INIT_SENSOR ) == 0 )
{
int num_sensors = SDL_NumSensors();
for( int i = 0; i < num_sensors; i++ )
{
if( SDL_SensorGetDeviceType( i ) == SDL_SENSOR_GYRO )
{
g_system_gyro = SDL_SensorOpen( i );
if( g_system_gyro )
{
g_system_gyro_id = SDL_SensorGetInstanceID( g_system_gyro );
Con_Printf( "SDL: Opened built-in gyroscope: %s\n", SDL_SensorGetName( g_system_gyro ) );
break;
}
}
}
}
else
{
Con_Reportf( S_ERROR "Failed to init SDL Sensor subsystem: %s\n", SDL_GetError() );
}
}
/*
==============
SDLash_ShutdownSensors
==============
*/
void SDLash_ShutdownSensors( void )
{
if( g_system_gyro )
{
SDL_SensorClose( g_system_gyro );
g_system_gyro = NULL;
g_system_gyro_id = -1;
}
SDL_QuitSubSystem( SDL_INIT_SENSOR );
}
/*
==============
SDLash_GyroIsAvailable
==============
*/
qboolean SDLash_GyroIsAvailable( void )
{
return ( g_system_gyro != NULL );
}
/*
==============
SDLash_SensorUpdate
==============
*/
void SDLash_SensorUpdate( SDL_SensorEvent sensor )
{
if( sensor.which == g_system_gyro_id )
{
IN_GyroEvent( sensor.data );
}
}
#else
void SDLash_InitSensors( void ) { }
void SDLash_ShutdownSensors( void ) { }
qboolean SDLash_GyroIsAvailable( void ) { return false; }
#endif

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