Compare commits

...

79 Commits

Author SHA1 Message Date
Alibek Omarov
98ff33d9e2 engine: mod_bmodel: rewrite .lit file loader using normal stream io functions 2025-01-09 08:02:39 +03:00
Alibek Omarov
baa8fbaaec engine: mod_bmodel: fix misleading debug message telling about monochrome lighting when .lit file is used 2025-01-09 08:02:39 +03:00
Alibek Omarov
a0b46da04b engine: consolidate all box clipnodes used in studio models, pmove and server in a single place 2025-01-09 08:02:39 +03:00
Alibek Omarov
e70ba7ad64 engine: now, remove SUPPORT_BSP2_FORMAT macro, only prevent ref_soft from loading such maps 2025-01-09 08:02:39 +03:00
Alibek Omarov
8a41be9d47 engine: server: finally, make a union for large leaf indices in edict_t so we can run bsp2 maps without recompiling 2025-01-09 08:02:39 +03:00
Alibek Omarov
47aff9e30b engine: hack mnode_t struct so we can have 24-bit face and children indices to support BSP2 format in runtime 2025-01-09 08:02:39 +03:00
Alibek Omarov
ced8744ac9 engine: split edge struct into two with 16-bit indicies and 32-bit indicies to support BSP2 format in runtime 2025-01-08 14:46:07 +03:00
Alibek Omarov
734c07ddab engine: split clipnodes struct into two with 16-bit indicies and 32-bit indicies to support BSP2 format in runtime 2025-01-08 14:46:07 +03:00
Alibek Omarov
ceb603488a engine: platform: sdl: set up our custom log output function to catch all SDL errors in engine log file 2025-01-08 10:10:45 +03:00
Alibek Omarov
80bac157eb Update SDL to 2.30.11, use GitHub links for SDL, as it might be faster on Actions virtual machines 2025-01-08 09:42:53 +03:00
Alibek Omarov
b45581c630 Documentation: gameinfo: add a note about liblist.gam and gameinfo.txt changes 2025-01-08 01:22:23 +03:00
Alibek Omarov
d4fe3d6f8a filesystem: wscript: check d_type field in struct dirent, as this is an extension and some supported ports (like psvita) don't have it 2025-01-08 01:22:23 +03:00
Alibek Omarov
1907485895 filesystem: re-enable folder check for array returned by listdirectory with dirs_only set to true
According to glibc manual, not all filesystems support it and some might return DT_UNKNOWN.
2025-01-08 01:22:23 +03:00
Alibek Omarov
cfebb3e1d6 filesystem: massively rework how scanning game directories work
* No more conversion from liblist.gam to gameinfo.txt. We are using liblist.gam directly now.
  gameinfo.txt being native format to Xash3D not only remains, it takes priority over liblist.gam.
* Quake game directories now don't receive autogenerated gameinfo.txt.
* Empty directories don't get gameinfo.txt either, finally making it easier to support HD addon folders.
* If user still wishes to generate gameinfo.txt, there is now command fs_make_gameinfo that creates
  gameinfo.txt for currently running game.
* No more creating empty folders for RoDir. They are now created on demand.
2025-01-08 01:22:23 +03:00
Alibek Omarov
f9a0057c28 wscript: fix bsp2 build bruh 2025-01-08 01:22:23 +03:00
Alibek Omarov
ceb2b0c5e4 filesystem: add utility function FS_CheckForQuakePak that parses Quake PAK file and looks for specific files in it's root
Used to properly detect Quake game dirs.
2025-01-08 01:22:23 +03:00
Alibek Omarov
83b5edea88 engine: platform: sdl: clear key states on losing window focus 2025-01-06 16:38:16 +03:00
Alibek Omarov
9d2a85f11f engine: client: clear mouse state along with everything else in Key_ClearStates 2025-01-06 16:38:16 +03:00
Alibek Omarov
93d3df81ee engine: client: small refactoring and better explanation for char input handler for menus that don't query our extended API 2025-01-06 16:00:11 +03:00
Alibek Omarov
2a7f13151a engine: cmd: when command is already defined and has the same function pointer, show it as warning 2025-01-06 13:51:25 +03:00
Alibek Omarov
0c4f6ae61d engine: client: like GoldSrc, use cancelselect command for escape key. It is handled in client.dll and allows mods to override Esc key behavior 2025-01-06 13:51:25 +03:00
Andrey Akhmichin
b7150c3208 Documentation: opensource-mods.md: add "Earth Special Forces". 2025-01-02 05:35:33 +03:00
Alibek Omarov
d6ebff81e2 engine: client: vgui: track textures upload to avoid issues in buggy mods (that are probably will never get fixed) 2024-12-30 07:28:33 +03:00
Alibek Omarov
ce4148f351 engine: client: vgui: deprecate VGUI_CreateTexture and VGUI_UploadTextureBlock functions, as they never used by vgui_support module
* vgui_support used them in old engine for font cache, but we don't have that anymore.
2024-12-30 07:16:30 +03:00
Alibek Omarov
08c2632c14 engine: platform: nswitch: export _Unwind_GetIPInfo as required by cs16-client 2024-12-29 09:11:21 +03:00
Alibek Omarov
03eac904c1 public: make COM_FixSlashes inlined 2024-12-29 09:05:15 +03:00
Alibek Omarov
62f3354f4e mainui: update 2024-12-29 08:06:58 +03:00
Alibek Omarov
d5b6eff0c0 game_launch: fix --disable-menu-changegame by request 2024-12-29 08:06:50 +03:00
Alibek Omarov
c9ee844a24 scripts: gha: bundle SDL2 pdb file for better stacktraces on Windows 2024-12-29 02:08:05 +03:00
Alibek Omarov
687c9b004f engine: platform: sdl: set directsound as default audio driver on Windows, due to incompatibilities with mods 2024-12-29 02:01:23 +03:00
Alibek Omarov
14e79f04ac engine: prevent extra malloc in exec by checking if cfg already contains newline at EOL and appending it to cbuf if required 2024-12-28 16:03:49 +03:00
Alibek Omarov
5d6cf48687 engine: client: fix lightstyle timings on GoldSrc protocol, share the parser with Quake protocol handler 2024-12-28 01:40:59 +03:00
Alibek Omarov
88949a9ee6 engine: launcher: drop support for changing games from within internal launcher for platforms that do not have execv equivalent 2024-12-27 18:45:31 +03:00
Alibek Omarov
2d1e76199b engine: host: always print Host_Error text to log, even if console hasn't been enabled 2024-12-27 18:37:25 +03:00
Alibek Omarov
a5d78ecfd7 engine: be more verbose on why the engine is going to be shut down 2024-12-27 18:30:46 +03:00
Alibek Omarov
3576e474e6 engine: get rid of SendKeyEvents function, it only listened for WM_QUIT on Windows and we don't use that with SDL 2024-12-27 18:30:01 +03:00
Alibek Omarov
d50927f21f ref: gl: make r_ripple work with non-square non-pot textures, fix holes caused by unexpected transparency 2024-12-26 06:31:53 +03:00
Alibek Omarov
c6e3146290 ref: gl: fix broken water texture when it was discarded by R_UploadRipples 2024-12-26 03:49:12 +03:00
Alibek Omarov
9811988cce engine: server: set mapname globals field before making first call into DLL, as some mods tend to read it too early 2024-12-26 02:50:30 +03:00
Alibek Omarov
48bdd4238e engine: always include reason to server shutdown 2024-12-26 02:13:13 +03:00
Alibek Omarov
fd2b7e9197 engine: client: declare music command as overridable as the same command exists in HLU SDK 2024-12-26 01:16:48 +03:00
Alibek Omarov
0e42e5b9e3 engine: command: introduce new type of commands that can be overriden by a mod DLL for better compatibility 2024-12-26 01:15:32 +03:00
Alibek Omarov
65750f0cf3 engine: server: hack save fields that use types unsupported by Half-Life Unified SDK through array types 2024-12-26 00:36:28 +03:00
Alibek Omarov
6469b0a5f5 engine: common: shut up warn-unused-result on log write functions, only fetch system time for system logs when necessary 2024-12-24 11:27:34 +03:00
Alibek Omarov
4f98187e9a engine: move select-based stdin input function to platform/posix 2024-12-24 11:13:05 +03:00
Alibek Omarov
74c19a1557 engine: client: leave SDL2 client.dll detection for Windows only 2024-12-24 10:46:39 +03:00
Alibek Omarov
cfebee6ea8 engine: split crashhandler to win32 and posix versions 2024-12-24 10:37:49 +03:00
Alibek Omarov
bd0d6644e0 engine: move Sys_SendKeyEvents to platform/win32 2024-12-24 09:59:47 +03:00
Alibek Omarov
f41450127a Documentation: add note about pkg-config when compiling for Windows under Wine 2024-12-24 09:59:09 +03:00
Alibek Omarov
70897af362 public: add dll helpers to simplify basic interaction with exported functions lists 2024-12-23 04:43:15 +03:00
Alibek Omarov
f1d7101ad8 engine: client: get rid of NULL sentinels at the end of arrays 2024-12-22 07:43:26 +03:00
Alibek Omarov
ac9d960eef engine: common: get rid of NULL sentinels at the end of arrays 2024-12-22 07:22:10 +03:00
Alibek Omarov
0c471aecb1 engine: common: imagelib: get rid of NULL sentinels at the end of arrays 2024-12-22 07:21:06 +03:00
Alibek Omarov
72d0d70587 engine: server: get rid of NULL sentinels at the end of arrays 2024-12-22 07:12:32 +03:00
Alibek Omarov
bec526a77e utils: xar: fix xar build 2024-12-22 07:11:53 +03:00
Alibek Omarov
4d1119391a ref: gl: get rid of NULL sentinels at the end of arrays 2024-12-22 07:11:28 +03:00
Alibek Omarov
1dd622fcbb engine: client: keys: refactoring, remove NULL sentinels, make keys structs and few more functions private to keys.c file 2024-12-21 15:22:48 +03:00
Alibek Omarov
af4b64983a engine: fix a typo in Cmd_AddFilteredCommand flags passed to Cmd_AddCommandEx. Make them return Cmd_AddCommandEx value 2024-12-21 15:01:04 +03:00
Alibek Omarov
e751092555 engine: common: cmd: consider filtered buffer privileged when we're running local singleplayer game 2024-12-20 05:48:33 +03:00
Alibek Omarov
ce06b1d3d7 engine: client: allow CL_GetLocalPlayer to return NULL rather than halting whole server 2024-12-20 05:47:46 +03:00
Alibek Omarov
a834f4996f engine: move client adding functions to their appropriate subsystems and make them static. Make in-engine functions inlined. 2024-12-20 05:47:46 +03:00
Alibek Omarov
fdcf267fe3 engine: client: fix usermessage being removed because of case-sensitivity (message names are case-insensitive when transferred from server to client) 2024-12-20 03:05:14 +03:00
Alibek Omarov
7f2ebd1b5e engine: server: add command sv_list_messages to print all registered user messages 2024-12-20 02:37:38 +03:00
Alibek Omarov
68b2d9f269 engine: server: fix possible message number inconsistency if server requested it twice 2024-12-20 02:37:01 +03:00
Alibek Omarov
3f1eed9f59 engine: client: remove svc_goldsrc_* definitions for message that semantics matches Xash3D 49 protocol and instead use it's definitions 2024-12-20 01:51:24 +03:00
Alibek Omarov
0637dbaab1 engine: client: refactor and simplify CL_RegisterUserMessage 2024-12-20 01:51:24 +03:00
Alibek Omarov
85388e97c1 engine: client: add command cl_list_messages to print all registered user messages 2024-12-20 01:51:24 +03:00
Alibek Omarov
de961eb45e engine: client: use new extension for creating temporary files for demoheader 2024-12-19 07:10:02 +03:00
Alibek Omarov
3d60770b84 filesystem: add flag to open files in a RAM
On Linux, it uses memfd_create syscall that can be found on Linux 3.17 and
higher. By default memfds are executable, so we set MFD_NOEXEC_SEAL flag to
prevent execution at creation time.
2024-12-19 04:22:25 +03:00
Alibek Omarov
93c5853356 engine: server: merge SV_FindBestBaseline for normal entities and static entities into single function 2024-12-18 06:57:43 +03:00
Alibek Omarov
98e587285b engine: client: allocate static entities only when server sends static entity packet 2024-12-18 06:57:43 +03:00
Alibek Omarov
2aa13c8347 public: xash3d_mathlib: include string.h with memset definition 2024-12-17 21:58:42 +03:00
Alibek Omarov
53344771fd public: xash3d_mathlib: get rid of matrix identity global macros 2024-12-17 21:01:51 +03:00
Alibek Omarov
2373743920 public: mention FSF in copyright header, as some of the libc extensions callbacks were derived from glibc [skip ci] 2024-12-17 20:41:55 +03:00
Alibek Omarov
90a3497afe public: improve Q_strncat fallback implementation 2024-12-17 20:40:19 +03:00
Alibek Omarov
339aebb08c public: add Q_strnlen with fallback to memchr 2024-12-17 20:03:09 +03:00
Alibek Omarov
b4afe390d6 public: slight refactoring 2024-12-17 19:57:27 +03:00
Alibek Omarov
ca0c5f929a engine: slight refactoring of lightstyle handling functions 2024-12-17 17:04:26 +03:00
Alibek Omarov
48cc526c7e engine: client: fix possible svc_pings misparse by reading until null bit is encountered 2024-12-15 18:22:40 +03:00
107 changed files with 3094 additions and 2421 deletions

View File

@@ -66,7 +66,7 @@ jobs:
targetos: apple targetos: apple
targetarch: amd64 targetarch: amd64
env: env:
SDL_VERSION: 2.30.9 SDL_VERSION: 2.30.11
GH_CPU_ARCH: ${{ matrix.targetarch }} GH_CPU_ARCH: ${{ matrix.targetarch }}
GH_CROSSCOMPILING: ${{ matrix.cross }} GH_CROSSCOMPILING: ${{ matrix.cross }}
steps: steps:

View File

@@ -5,5 +5,10 @@ This can be useful to test engine in Wine without using virtual machines or dual
0. Clone and install https://github.com/mstorsjo/msvc-wine (you can skip CMake part) 0. Clone and install https://github.com/mstorsjo/msvc-wine (you can skip CMake part)
1. Set environment variable MSVC_WINE_PATH to the path to installed MSVC toolchain 1. Set environment variable MSVC_WINE_PATH to the path to installed MSVC toolchain
2. Pre-load wine: `wineserver -k; wineserver -p; wine64 wineboot` 2. Pre-load wine: `wineserver -k; wineserver -p; wine64 wineboot`
3. Run `./waf configure -T <build-type> --enable-wine-msvc --sdl2=../SDL2_VC`. Configuration step will take more time than usual. 3. Run `PKGCONFIG=/bin/false ./waf configure -T <build-type> --enable-wine-msvc --sdl2=../SDL2_VC`. Configuration step will take more time than usual.
4. .. other typical steps to build from console ... 4. .. other typical steps to build from console ...
> [!NOTE]
> Notice the usage of PKGCONFIG=/bin/false here. We're disabling pkg-config so we don't accidentally pull
> system-wide dependencies and force building them from source. In future builds we might set custom
> directory to pull dependencies from, like ffmpeg...

View File

@@ -85,6 +85,9 @@ These strings are specific to Xash3D FWGS.
As Xash3D accidentally supports GoldSrc games, it also supports parsing liblist.gam.\ As Xash3D accidentally supports GoldSrc games, it also supports parsing liblist.gam.\
Xash3D will use this file if gameinfo.txt is absent, or if its modification timestamp is older than liblist.gam. Xash3D will use this file if gameinfo.txt is absent, or if its modification timestamp is older than liblist.gam.
> [!NOTE]
> Starting from January 2025, Xash3D FWGS doesn't automatically generate gameinfo.txt from liblist.gam. The key conversion table still remains but if you wish to use gameinfo.txt instead of liblist.gam, you can execute `fs_make_gameinfo` in console.
For game creators who plan supporting only Xash3D, using this file is not recommended. For game creators who plan supporting only Xash3D, using this file is not recommended.
The table below defines conversion rules from liblist.gam to gameinfo.txt. Some keys' interpretation does differ from `gameinfo.txt`, in this case a note will be left. If `liblist.gam` key isn't present in this table, it's ignored. The table below defines conversion rules from liblist.gam to gameinfo.txt. Some keys' interpretation does differ from `gameinfo.txt`, in this case a note will be left. If `liblist.gam` key isn't present in this table, it's ignored.

View File

@@ -38,6 +38,9 @@ Uploaded to github by Oleg Cherkasky - https://github.com/gunrunners-paradise/Ct
## Deathmatch Classic ## Deathmatch Classic
Available in Valve's Half-Life repository - https://github.com/ValveSoftware/halflife/tree/master/dmc Available in Valve's Half-Life repository - https://github.com/ValveSoftware/halflife/tree/master/dmc
## Earth Special Forces
Alpha 2.0 - https://www.gamers-desire.de/details/2830
## ESHQ ## ESHQ
Official github repository - https://github.com/adslbarxatov/xash3d-for-ESHQ Official github repository - https://github.com/adslbarxatov/xash3d-for-ESHQ

View File

@@ -16,8 +16,6 @@ GNU General Public License for more details.
#ifndef BSPFILE_H #ifndef BSPFILE_H
#define BSPFILE_H #define BSPFILE_H
//#define SUPPORT_BSP2_FORMAT // allow to loading Darkplaces BSP2 maps (with broke binary compatibility)
/* /*
============================================================================== ==============================================================================
@@ -65,7 +63,6 @@ BRUSH MODELS
#define MAX_MAP_CLIPNODES_BSP2 524288 #define MAX_MAP_CLIPNODES_BSP2 524288
// these limis not using by modelloader but only for displaying 'mapstats' correctly // these limis not using by modelloader but only for displaying 'mapstats' correctly
#ifdef SUPPORT_BSP2_FORMAT
#define MAX_MAP_MODELS 2048 // embedded models #define MAX_MAP_MODELS 2048 // embedded models
#define MAX_MAP_ENTSTRING 0x200000 // 2 Mb should be enough #define MAX_MAP_ENTSTRING 0x200000 // 2 Mb should be enough
#define MAX_MAP_PLANES 131072 // can be increased without problems #define MAX_MAP_PLANES 131072 // can be increased without problems
@@ -75,18 +72,6 @@ BRUSH MODELS
#define MAX_MAP_VERTS 524288 // can be increased without problems #define MAX_MAP_VERTS 524288 // can be increased without problems
#define MAX_MAP_FACES 262144 // can be increased without problems #define MAX_MAP_FACES 262144 // can be increased without problems
#define MAX_MAP_MARKSURFACES 524288 // can be increased without problems #define MAX_MAP_MARKSURFACES 524288 // can be increased without problems
#else
// increased to match PrimeXT compilers
#define MAX_MAP_MODELS 1024 // embedded models
#define MAX_MAP_ENTSTRING 0x100000 // 1 Mb should be enough
#define MAX_MAP_PLANES 65536 // can be increased without problems
#define MAX_MAP_NODES 32767 // because negative shorts are leafs
#define MAX_MAP_CLIPNODES MAX_MAP_CLIPNODES_HLBSP // because negative shorts are contents
#define MAX_MAP_LEAFS 32767 // signed short limit
#define MAX_MAP_VERTS 65535 // unsigned short limit
#define MAX_MAP_FACES 65535 // unsigned short limit
#define MAX_MAP_MARKSURFACES 65535 // unsigned short limit
#endif
#define MAX_MAP_ENTITIES 8192 // network limit #define MAX_MAP_ENTITIES 8192 // network limit
#define MAX_MAP_TEXINFO MAX_MAP_FACES // in theory each face may have personal texinfo #define MAX_MAP_TEXINFO MAX_MAP_FACES // in theory each face may have personal texinfo

View File

@@ -60,26 +60,29 @@ typedef struct
vec3_t position; vec3_t position;
} mvertex_t; } mvertex_t;
typedef struct typedef struct mclipnode32_s
{ {
int planenum; int planenum;
#ifdef SUPPORT_BSP2_FORMAT int children[2]; // negative numbers are contents
int children[2]; // negative numbers are contents } mclipnode32_t;
#else
short children[2]; // negative numbers are contents typedef struct mclipnode16_s
#endif {
} mclipnode_t; int planenum;
short children[2]; // negative numbers are contents
} mclipnode16_t;
// size is matched but representation is not // size is matched but representation is not
typedef struct typedef struct medge32_s
{ {
#ifdef SUPPORT_BSP2_FORMAT
unsigned int v[2]; unsigned int v[2];
#else } medge32_t;
typedef struct medge16_s
{
unsigned short v[2]; unsigned short v[2];
unsigned int cachededgeoffset; unsigned int cachededgeoffset;
#endif } medge16_t;
} medge_t;
typedef struct texture_s typedef struct texture_s
{ {
@@ -156,13 +159,31 @@ typedef struct mnode_s
// node specific // node specific
mplane_t *plane; mplane_t *plane;
struct mnode_s *children[2];
#ifdef SUPPORT_BSP2_FORMAT #if !XASH_64BIT
int firstsurface; union
int numsurfaces; {
struct mnode_s *children_[2];
struct
{
// the ordering is important
int child_0_leaf : 1;
int child_0_off : 23;
int firstsurface_1 : 8;
int child_1_leaf : 1;
int child_1_off : 23;
int numsurfaces_1 : 8;
};
};
unsigned short firstsurface_0;
unsigned short numsurfaces_0;
#else #else
unsigned short firstsurface; // in 64-bit ABI this struct has 4 more bytes of padding, let's use it!
unsigned short numsurfaces; struct mnode_s *children_[2];
unsigned short firstsurface_0;
unsigned short numsurfaces_0;
unsigned short firstsurface_1;
unsigned short numsurfaces_1;
#endif #endif
} mnode_t; } mnode_t;
@@ -203,7 +224,6 @@ typedef struct mleaf_s
int nummarksurfaces; int nummarksurfaces;
int cluster; // helper to acess to uncompressed visdata int cluster; // helper to acess to uncompressed visdata
byte ambient_sound_level[NUM_AMBIENTS]; byte ambient_sound_level[NUM_AMBIENTS];
} mleaf_t; } mleaf_t;
// surface extradata // surface extradata
@@ -291,7 +311,11 @@ struct msurface_s
typedef struct hull_s typedef struct hull_s
{ {
mclipnode_t *clipnodes; union
{
mclipnode16_t *clipnodes16;
mclipnode32_t *clipnodes32;
};
mplane_t *planes; mplane_t *planes;
int firstclipnode; int firstclipnode;
int lastclipnode; int lastclipnode;
@@ -341,7 +365,12 @@ typedef struct model_s
mvertex_t *vertexes; mvertex_t *vertexes;
int numedges; int numedges;
medge_t *edges; union
{
medge16_t *edges16;
medge32_t *edges32;
};
int numnodes; int numnodes;
mnode_t *nodes; mnode_t *nodes;
@@ -356,7 +385,11 @@ typedef struct model_s
int *surfedges; int *surfedges;
int numclipnodes; int numclipnodes;
mclipnode_t *clipnodes; union
{
mclipnode16_t *clipnodes16;
mclipnode32_t *clipnodes32;
};
int nummarksurfaces; int nummarksurfaces;
msurface_t **marksurfaces; msurface_t **marksurfaces;
@@ -550,16 +583,68 @@ typedef struct
#define ANIM_CYCLE 2 #define ANIM_CYCLE 2
#define MOD_FRAMES 20 #define MOD_FRAMES 20
#define MAX_DEMOS 32 #define MAX_DEMOS 32
#define MAX_MOVIES 8 #define MAX_MOVIES 8
#define MAX_CDTRACKS 32 #define MAX_CDTRACKS 32
#define MAX_CLIENT_SPRITES 512 // SpriteTextures (0-256 hud, 256-512 client) #define MAX_CLIENT_SPRITES 512 // SpriteTextures (0-256 hud, 256-512 client)
#define MAX_REQUESTS 64 #define MAX_REQUESTS 64
STATIC_CHECK_SIZEOF( mnode_t, 52, 72 );
STATIC_CHECK_SIZEOF( mextrasurf_t, 324, 496 ); STATIC_CHECK_SIZEOF( mextrasurf_t, 324, 496 );
STATIC_CHECK_SIZEOF( decal_t, 60, 88 ); STATIC_CHECK_SIZEOF( decal_t, 60, 88 );
STATIC_CHECK_SIZEOF( mfaceinfo_t, 176, 304 ); STATIC_CHECK_SIZEOF( mfaceinfo_t, 176, 304 );
// model flags (stored in model_t->flags)
#define MODEL_QBSP2 BIT( 28 ) // uses 32-bit types
// access functions
static inline mnode_t *node_child( const mnode_t *n, int side, const model_t *mod )
{
#if !XASH_64BIT
if( unlikely( mod->flags & MODEL_QBSP2 )) // MODEL_QBSP2
{
if( side == 0 )
{
if( n->child_0_leaf )
return (mnode_t *)(mod->leafs + n->child_0_off);
else
return (mnode_t *)(mod->nodes + n->child_0_off);
}
else
{
if( n->child_1_leaf )
return (mnode_t *)(mod->leafs + n->child_1_off);
else
return (mnode_t *)(mod->nodes + n->child_1_off);
}
}
return n->children_[side];
#else
return n->children_[side];
#endif
}
static inline void node_children( mnode_t *children[2], const mnode_t *n, const model_t *mod )
{
children[0] = node_child( n, 0, mod );
children[1] = node_child( n, 1, mod );
}
static inline int node_firstsurface( const mnode_t *n, const model_t *mod )
{
if( mod->flags & MODEL_QBSP2 )
return n->firstsurface_0 + ( n->firstsurface_1 << 16 );
else
return n->firstsurface_0;
}
static inline int node_numsurfaces( const mnode_t *n, const model_t *mod )
{
if( mod->flags & MODEL_QBSP2 )
return n->numsurfaces_0 + ( n->numsurfaces_1 << 16 );
else
return n->numsurfaces_0;
}
#endif//COM_MODEL_H #endif//COM_MODEL_H

View File

@@ -232,6 +232,7 @@ typedef struct dll_info_s
{ {
const char *name; // name of library const char *name; // name of library
const dllfunc_t *fcts; // list of dll exports const dllfunc_t *fcts; // list of dll exports
const size_t num_fcts;
qboolean crash; // crash if dll not found qboolean crash; // crash if dll not found
void *link; // hinstance of loading library void *link; // hinstance of loading library
} dll_info_t; } dll_info_t;

View File

@@ -30,10 +30,9 @@ static dllfunc_t msvfw_funcs[] =
{ "DrawDibOpen", (void **) &pDrawDibOpen }, { "DrawDibOpen", (void **) &pDrawDibOpen },
{ "DrawDibDraw", (void **) &pDrawDibDraw }, { "DrawDibDraw", (void **) &pDrawDibDraw },
{ "DrawDibClose", (void **) &pDrawDibClose }, { "DrawDibClose", (void **) &pDrawDibClose },
{ NULL, NULL }
}; };
dll_info_t msvfw_dll = { "msvfw32.dll", msvfw_funcs, false }; dll_info_t msvfw_dll = { "msvfw32.dll", msvfw_funcs, ARRAYSIZE( msvfw_funcs ), false };
// msacm32.dll exports // msacm32.dll exports
static MMRESULT (_stdcall *pacmStreamOpen)( LPHACMSTREAM, HACMDRIVER, LPWAVEFORMATEX, LPWAVEFORMATEX, LPWAVEFILTER, DWORD, DWORD, DWORD ); static MMRESULT (_stdcall *pacmStreamOpen)( LPHACMSTREAM, HACMDRIVER, LPWAVEFORMATEX, LPWAVEFORMATEX, LPWAVEFILTER, DWORD, DWORD, DWORD );
@@ -51,10 +50,9 @@ static dllfunc_t msacm_funcs[] =
{ "acmStreamConvert", (void **) &pacmStreamConvert }, { "acmStreamConvert", (void **) &pacmStreamConvert },
{ "acmStreamSize", (void **) &pacmStreamSize }, { "acmStreamSize", (void **) &pacmStreamSize },
{ "acmStreamClose", (void **) &pacmStreamClose }, { "acmStreamClose", (void **) &pacmStreamClose },
{ NULL, NULL }
}; };
dll_info_t msacm_dll = { "msacm32.dll", msacm_funcs, false }; dll_info_t msacm_dll = { "msacm32.dll", msacm_funcs, ARRAYSIZE( msacm_funcs ), false };
// avifil32.dll exports // avifil32.dll exports
static int (_stdcall *pAVIStreamInfo)( PAVISTREAM pavi, AVISTREAMINFO *psi, LONG lSize ); static int (_stdcall *pAVIStreamInfo)( PAVISTREAM pavi, AVISTREAMINFO *psi, LONG lSize );
@@ -88,10 +86,9 @@ static dllfunc_t avifile_funcs[] =
{ "AVIStreamRelease", (void **) &pAVIStreamRelease }, { "AVIStreamRelease", (void **) &pAVIStreamRelease },
{ "AVIStreamStart", (void **) &pAVIStreamStart }, { "AVIStreamStart", (void **) &pAVIStreamStart },
{ "AVIStreamTimeToSample", (void **) &pAVIStreamTimeToSample }, { "AVIStreamTimeToSample", (void **) &pAVIStreamTimeToSample },
{ NULL, NULL }
}; };
dll_info_t avifile_dll = { "avifil32.dll", avifile_funcs, false }; dll_info_t avifile_dll = { "avifil32.dll", avifile_funcs, ARRAYSIZE( avifile_funcs ), false };
typedef struct movie_state_s typedef struct movie_state_s
{ {

View File

@@ -157,7 +157,7 @@ void CL_StartupDemoHeader( void )
{ {
CL_CloseDemoHeader(); CL_CloseDemoHeader();
cls.demoheader = FS_Open( "demoheader.tmp", "w+b", true ); cls.demoheader = FS_Open( "demoheader.tmp", "w+bm", true );
if( !cls.demoheader ) if( !cls.demoheader )
{ {
@@ -724,7 +724,7 @@ static void CL_DemoStartPlayback( int mode )
{ {
// NOTE: at this point demo is still valid // NOTE: at this point demo is still valid
CL_Disconnect(); CL_Disconnect();
Host_ShutdownServer(); SV_Shutdown( "Server was killed due to demo playback start\n" );
Con_FastClose(); Con_FastClose();
UI_SetActiveMenu( false ); UI_SetActiveMenu( false );

View File

@@ -127,8 +127,10 @@ static void R_SplitEntityOnNode( mnode_t *node )
} }
// recurse down the contacted sides // recurse down the contacted sides
if( sides & 1 ) R_SplitEntityOnNode( node->children[0] ); if( sides & 1 )
if( sides & 2 ) R_SplitEntityOnNode( node->children[1] ); R_SplitEntityOnNode( node_child( node, 0, cl.worldmodel ));
if( sides & 2 )
R_SplitEntityOnNode( node_child( node, 1, cl.worldmodel ));
} }
/* /*

View File

@@ -76,7 +76,6 @@ static const dllfunc_t cdll_exports[] =
{ "IN_ClearStates", (void **)&clgame.dllFuncs.IN_ClearStates }, { "IN_ClearStates", (void **)&clgame.dllFuncs.IN_ClearStates },
{ "V_CalcRefdef", (void **)&clgame.dllFuncs.pfnCalcRefdef }, { "V_CalcRefdef", (void **)&clgame.dllFuncs.pfnCalcRefdef },
{ "KB_Find", (void **)&clgame.dllFuncs.KB_Find }, { "KB_Find", (void **)&clgame.dllFuncs.KB_Find },
{ NULL, NULL }
}; };
// optional exports // optional exports
@@ -91,7 +90,6 @@ 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_ClientTouchEvent", (void **)&clgame.dllFuncs.pfnTouchEvent}, // Xash3D FWGS ext
{ "IN_ClientMoveEvent", (void **)&clgame.dllFuncs.pfnMoveEvent}, // Xash3D FWGS ext { "IN_ClientMoveEvent", (void **)&clgame.dllFuncs.pfnMoveEvent}, // Xash3D FWGS ext
{ "IN_ClientLookEvent", (void **)&clgame.dllFuncs.pfnLookEvent}, // Xash3D FWGS ext { "IN_ClientLookEvent", (void **)&clgame.dllFuncs.pfnLookEvent}, // Xash3D FWGS ext
{ NULL, NULL }
}; };
static void pfnSPR_DrawHoles( int frame, int x, int y, const wrect_t *prc ); static void pfnSPR_DrawHoles( int frame, int x, int y, const wrect_t *prc );
@@ -976,7 +974,7 @@ static void CL_ClearUserMessage( char *pszName, int svc_num )
int i; int i;
for( i = 0; i < MAX_USER_MESSAGES && clgame.msg[i].name[0]; i++ ) for( i = 0; i < MAX_USER_MESSAGES && clgame.msg[i].name[0]; i++ )
if( ( clgame.msg[i].number == svc_num ) && Q_strcmp( clgame.msg[i].name, pszName ) ) if( ( clgame.msg[i].number == svc_num ) && Q_stricmp( clgame.msg[i].name, pszName ) )
clgame.msg[i].number = 0; clgame.msg[i].number = 0;
} }
@@ -1046,7 +1044,7 @@ void CL_InitEdicts( int maxclients )
cls.num_client_entities = CL_UPDATE_BACKUP * NUM_PACKET_ENTITIES; cls.num_client_entities = CL_UPDATE_BACKUP * NUM_PACKET_ENTITIES;
cls.packet_entities = Mem_Realloc( clgame.mempool, cls.packet_entities, sizeof( entity_state_t ) * cls.num_client_entities ); cls.packet_entities = Mem_Realloc( clgame.mempool, cls.packet_entities, sizeof( entity_state_t ) * cls.num_client_entities );
clgame.entities = Mem_Calloc( clgame.mempool, sizeof( cl_entity_t ) * clgame.maxEntities ); clgame.entities = Mem_Calloc( clgame.mempool, sizeof( cl_entity_t ) * clgame.maxEntities );
clgame.static_entities = Mem_Calloc( clgame.mempool, sizeof( cl_entity_t ) * MAX_STATIC_ENTITIES ); clgame.static_entities = NULL; // will be initialized later
clgame.numStatics = 0; clgame.numStatics = 0;
if(( clgame.maxRemapInfos - 1 ) != clgame.maxEntities ) if(( clgame.maxRemapInfos - 1 ) != clgame.maxEntities )
@@ -1765,6 +1763,17 @@ static cvar_t *GAME_EXPORT pfnCvar_RegisterClientVariable( const char *szName, c
return (cvar_t *)Cvar_Get( szName, szValue, flags|FCVAR_CLIENTDLL, Cvar_BuildAutoDescription( szName, flags|FCVAR_CLIENTDLL )); return (cvar_t *)Cvar_Get( szName, szValue, flags|FCVAR_CLIENTDLL, Cvar_BuildAutoDescription( szName, flags|FCVAR_CLIENTDLL ));
} }
static int GAME_EXPORT Cmd_AddClientCommand( const char *cmd_name, xcommand_t function )
{
int flags = CMD_CLIENTDLL;
// a1ba: try to mitigate outdated client.dll vulnerabilities
if( !Q_stricmp( cmd_name, "motd_write" ))
flags |= CMD_PRIVILEGED;
return Cmd_AddCommandEx( cmd_name, function, "client command", flags, __func__ );
}
/* /*
============= =============
pfnHookUserMsg pfnHookUserMsg
@@ -2698,17 +2707,6 @@ static const char *pfnGetGameDirectory( void )
return szGetGameDir; return szGetGameDir;
} }
/*
=============
Key_LookupBinding
=============
*/
static const char *Key_LookupBinding( const char *pBinding )
{
return Key_KeynumToString( Key_GetKey( pBinding ));
}
/* /*
============= =============
pfnGetLevelName pfnGetLevelName
@@ -4007,9 +4005,10 @@ void CL_UnloadProgs( void )
qboolean CL_LoadProgs( const char *name ) qboolean CL_LoadProgs( const char *name )
{ {
static playermove_t gpMove; static playermove_t gpMove;
const dllfunc_t *func; CL_EXPORT_FUNCS GetClientAPI; // single export
CL_EXPORT_FUNCS GetClientAPI; // single export qboolean valid_single_export = false;
qboolean critical_exports = true; qboolean missed_exports = false;
int i;
if( clgame.hInstance ) CL_UnloadProgs(); if( clgame.hInstance ) CL_UnloadProgs();
@@ -4020,36 +4019,19 @@ qboolean CL_LoadProgs( const char *name )
clgame.mempool = Mem_AllocPool( "Client Edicts Zone" ); clgame.mempool = Mem_AllocPool( "Client Edicts Zone" );
clgame.entities = NULL; clgame.entities = NULL;
// a1ba: we need to check if client.dll has direct dependency on SDL2 // a1ba: we need to check if client.dll has direct dependency on SDL2
// and if so, disable relative mouse mode // and if so, disable relative mouse mode
#if XASH_WIN32 && !XASH_64BIT #if XASH_WIN32 && !XASH_64BIT
if( ( clgame.client_dll_uses_sdl = COM_CheckLibraryDirectDependency( name, OS_LIB_PREFIX "SDL2." OS_LIB_EXT, false ) ) ) clgame.client_dll_uses_sdl = COM_CheckLibraryDirectDependency( name, OS_LIB_PREFIX "SDL2." OS_LIB_EXT, false );
{ Con_Printf( S_NOTE "%s uses %s for mouse input\n", name, clgame.client_dll_uses_sdl ? "SDL2" : "Windows API" );
Con_Printf( S_NOTE "%s uses SDL2 for mouse input\n", name );
}
else
{
Con_Printf( S_NOTE "%s uses Windows API for mouse input\n", name );
}
#else
// this doesn't mean other platforms uses SDL2 in any case
// it just helps input code to stay platform-independent
clgame.client_dll_uses_sdl = true;
#endif #endif
// NOTE: important stuff! // NOTE: important stuff!
// vgui must startup BEFORE loading client.dll to avoid get error ERROR_NOACESS // vgui must startup BEFORE loading client.dll to avoid get error ERROR_NOACESS during LoadLibrary
// during LoadLibrary
if( !GI->internal_vgui_support && VGui_LoadProgs( NULL )) if( !GI->internal_vgui_support && VGui_LoadProgs( NULL ))
{
VGui_Startup( refState.width, refState.height ); VGui_Startup( refState.width, refState.height );
}
else else
{ GI->internal_vgui_support = true; // we failed to load vgui_support, but let's probe client.dll for support anyway
// we failed to load vgui_support, but let's probe client.dll for support anyway
GI->internal_vgui_support = true;
}
clgame.hInstance = COM_LoadLibrary( name, false, false ); clgame.hInstance = COM_LoadLibrary( name, false, false );
@@ -4058,13 +4040,10 @@ qboolean CL_LoadProgs( const char *name )
// delayed vgui initialization for internal support // delayed vgui initialization for internal support
if( GI->internal_vgui_support && VGui_LoadProgs( clgame.hInstance )) if( GI->internal_vgui_support && VGui_LoadProgs( clgame.hInstance ))
{
VGui_Startup( refState.width, refState.height ); VGui_Startup( refState.width, refState.height );
}
// clear exports // clear exports
for( func = cdll_exports; func && func->name; func++ ) ClearExports( cdll_exports, ARRAYSIZE( cdll_exports ));
*func->func = NULL;
// trying to get single export // trying to get single export
if(( GetClientAPI = (void *)COM_GetProcAddress( clgame.hInstance, "GetClientAPI" )) != NULL ) if(( GetClientAPI = (void *)COM_GetProcAddress( clgame.hInstance, "GetClientAPI" )) != NULL )
@@ -4082,56 +4061,44 @@ qboolean CL_LoadProgs( const char *name )
CL_GetSecuredClientAPI( GetClientAPI ); CL_GetSecuredClientAPI( GetClientAPI );
} }
if ( GetClientAPI != NULL ) if( GetClientAPI != NULL ) // check critical functions again
{ valid_single_export = ValidateExports( cdll_exports, ARRAYSIZE( cdll_exports ));
// check critical functions again
for( func = cdll_exports; func && func->name; func++ )
{
if( func->func == NULL )
break; // BAH critical function was missed
}
// because all the exports are loaded through function 'F" for( i = 0; i < ARRAYSIZE( cdll_exports ); i++ )
if( !func || !func->name )
critical_exports = false;
}
for( func = cdll_exports; func && func->name != NULL; func++ )
{ {
if( *func->func != NULL ) if( *(cdll_exports[i].func) != NULL )
continue; // already get through 'F' continue; // already gott through 'F' or 'GetClientAPI'
// functions are cleared before all the extensions are evaluated // functions are cleared before all the extensions are evaluated
if(( *func->func = (void *)COM_GetProcAddress( clgame.hInstance, func->name )) == NULL ) if(( *(cdll_exports[i].func) = (void *)COM_GetProcAddress( clgame.hInstance, cdll_exports[i].name )) == NULL )
{ {
Con_Reportf( "%s: failed to get address of %s proc\n", __func__, func->name ); Con_Reportf( S_ERROR "%s: failed to get address of %s proc\n", __func__, cdll_exports[i].name );
if( critical_exports ) // print all not found exports at once, for debug
{ missed_exports = true;
COM_FreeLibrary( clgame.hInstance );
clgame.hInstance = NULL;
return false;
}
} }
} }
if( missed_exports )
{
COM_FreeLibrary( clgame.hInstance );
clgame.hInstance = NULL;
return false;
}
// it may be loaded through 'GetClientAPI' so we don't need to clear them // it may be loaded through 'GetClientAPI' so we don't need to clear them
if( critical_exports ) if( !valid_single_export )
{ ClearExports( cdll_new_exports, ARRAYSIZE( cdll_new_exports ));
// clear new exports
for( func = cdll_new_exports; func && func->name; func++ )
*func->func = NULL;
}
for( func = cdll_new_exports; func && func->name != NULL; func++ ) for( i = 0; i < ARRAYSIZE( cdll_new_exports ); i++ )
{ {
if( *func->func != NULL ) if( *(cdll_new_exports[i].func) != NULL )
continue; // already get through 'F' continue; // already gott through 'F' or 'GetClientAPI'
// functions are cleared before all the extensions are evaluated // functions are cleared before all the extensions are evaluated
// NOTE: new exports can be missed without stop the engine // NOTE: new exports can be missed without stop the engine
if(( *func->func = (void *)COM_GetProcAddress( clgame.hInstance, func->name )) == NULL ) if(( *(cdll_new_exports[i].func) = (void *)COM_GetProcAddress( clgame.hInstance, cdll_new_exports[i].name )) == NULL )
Con_Reportf( "%s: failed to get address of %s proc\n", __func__, func->name ); Con_Reportf( S_WARN "%s: failed to get address of %s proc\n", __func__, cdll_new_exports[i].name );
} }
if( !clgame.dllFuncs.pfnInitialize( &gEngfuncs, CLDLL_INTERFACE_VERSION )) if( !clgame.dllFuncs.pfnInitialize( &gEngfuncs, CLDLL_INTERFACE_VERSION ))

View File

@@ -694,6 +694,11 @@ static cvar_t *GAME_EXPORT pfnCvar_RegisterGameUIVariable( const char *szName, c
return (cvar_t *)Cvar_Get( szName, szValue, flags|FCVAR_GAMEUIDLL, Cvar_BuildAutoDescription( szName, flags|FCVAR_GAMEUIDLL )); return (cvar_t *)Cvar_Get( szName, szValue, flags|FCVAR_GAMEUIDLL, Cvar_BuildAutoDescription( szName, flags|FCVAR_GAMEUIDLL ));
} }
static int GAME_EXPORT Cmd_AddGameUICommand( const char *cmd_name, xcommand_t function )
{
return Cmd_AddCommandEx( cmd_name, function, "gameui command", CMD_GAMEUIDLL, __func__ );
}
/* /*
============= =============
pfnClientCmd pfnClientCmd
@@ -1109,7 +1114,7 @@ static void GAME_EXPORT UI_ShellExecute( const char *path, const char *parms, in
Platform_ShellExecute( path, parms ); Platform_ShellExecute( path, parms );
if( shouldExit ) if( shouldExit )
Sys_Quit(); Sys_Quit( __func__ );
} }
/* /*

View File

@@ -965,7 +965,7 @@ CL_Quit_f
void CL_Quit_f( void ) void CL_Quit_f( void )
{ {
CL_Disconnect(); CL_Disconnect();
Sys_Quit(); Sys_Quit( "command" );
} }
/* /*
@@ -1401,7 +1401,8 @@ static void CL_Connect_f( void )
Q_strncpy( server, Cmd_Argv( 1 ), sizeof( server )); Q_strncpy( server, Cmd_Argv( 1 ), sizeof( server ));
// if running a local server, kill it and reissue // if running a local server, kill it and reissue
if( SV_Active( )) Host_ShutdownServer(); if( SV_Active( ))
SV_Shutdown( "Server was killed due to connection to remote server\n" );
NET_Config( true, !cl_nat.value ); // allow remote NET_Config( true, !cl_nat.value ); // allow remote
Con_Printf( "server %s\n", server ); Con_Printf( "server %s\n", server );
@@ -3326,6 +3327,22 @@ static void CL_Escape_f( void )
else UI_SetActiveMenu( true ); else UI_SetActiveMenu( true );
} }
static void CL_ListMessages_f( void )
{
int i;
Con_Printf( "num size name\n" );
for( i = 0; i < MAX_USER_MESSAGES; i++ )
{
if( !COM_CheckStringEmpty( clgame.msg[i].name ))
break;
Con_Printf( "%3d\t%3d\t%s\n", clgame.msg[i].number, clgame.msg[i].size, clgame.msg[i].name );
}
Con_Printf( "Total %i messages\n", i );
}
/* /*
================= =================
CL_InitLocal CL_InitLocal
@@ -3483,6 +3500,7 @@ static void CL_InitLocal( void )
Cmd_AddCommand( "richpresence_gamemode", Cmd_Null_f, "compatibility command, does nothing" ); Cmd_AddCommand( "richpresence_gamemode", Cmd_Null_f, "compatibility command, does nothing" );
Cmd_AddCommand( "richpresence_update", Cmd_Null_f, "compatibility command, does nothing" ); Cmd_AddCommand( "richpresence_update", Cmd_Null_f, "compatibility command, does nothing" );
Cmd_AddCommand( "cl_list_messages", CL_ListMessages_f, "list registered user messages" );
} }
//============================================================================ //============================================================================

View File

@@ -311,10 +311,13 @@ static client entity
static void CL_ParseStaticEntity( sizebuf_t *msg ) static void CL_ParseStaticEntity( sizebuf_t *msg )
{ {
int i, newnum; int i, newnum;
entity_state_t from, to; const entity_state_t from = { 0 };
entity_state_t to;
cl_entity_t *ent; cl_entity_t *ent;
memset( &from, 0, sizeof( from )); if( !clgame.static_entities )
clgame.static_entities = Mem_Calloc( clgame.mempool, sizeof( cl_entity_t ) * MAX_STATIC_ENTITIES );
newnum = MSG_ReadUBitLong( msg, MAX_ENTITY_BITS ); newnum = MSG_ReadUBitLong( msg, MAX_ENTITY_BITS );
MSG_ReadDeltaEntity( msg, &from, &to, 0, DELTA_STATIC, cl.mtime[0] ); MSG_ReadDeltaEntity( msg, &from, &to, 0, DELTA_STATIC, cl.mtime[0] );
@@ -1322,11 +1325,11 @@ void CL_ParseLightStyle( sizebuf_t *msg, connprotocol_t proto )
{ {
int style; int style;
const char *s; const char *s;
float f = 0.0f; float f = cl.mtime[0];
style = MSG_ReadByte( msg ); style = MSG_ReadByte( msg );
s = MSG_ReadString( msg ); s = MSG_ReadString( msg );
if( proto != PROTO_GOLDSRC ) if( proto != PROTO_GOLDSRC && proto != PROTO_QUAKE )
f = MSG_ReadFloat( msg ); f = MSG_ReadFloat( msg );
CL_SetLightstyle( style, s, f ); CL_SetLightstyle( style, s, f );
@@ -1424,37 +1427,31 @@ register new user message or update existing
void CL_RegisterUserMessage( sizebuf_t *msg, connprotocol_t proto ) void CL_RegisterUserMessage( sizebuf_t *msg, connprotocol_t proto )
{ {
char *pszName; char *pszName;
int svc_num, size, bits; char szName[17];
int size;
svc_num = MSG_ReadByte( msg ); int svc_num = MSG_ReadByte( msg );
if( proto == PROTO_LEGACY || proto == PROTO_GOLDSRC ) if( proto == PROTO_LEGACY || proto == PROTO_GOLDSRC )
{ {
size = MSG_ReadByte( msg ); size = MSG_ReadByte( msg );
bits = 8; if( size == UINT8_MAX )
size = -1;
} }
else else
{ {
size = MSG_ReadWord( msg ); size = MSG_ReadWord( msg );
bits = 16; if( size == UINT16_MAX )
size = -1;
} }
if( proto == PROTO_GOLDSRC ) if( proto == PROTO_GOLDSRC )
{ {
static char szName[17];
MSG_ReadBytes( msg, szName, sizeof( szName ) - 1 ); MSG_ReadBytes( msg, szName, sizeof( szName ) - 1 );
szName[16] = 0; szName[16] = 0;
pszName = szName; pszName = szName;
} }
else pszName = MSG_ReadString( msg ); else pszName = MSG_ReadString( msg );
// important stuff
if( size == ( BIT( bits ) - 1 ) )
size = -1;
svc_num = bound( 0, svc_num, 255 );
CL_LinkUserMessage( pszName, svc_num, size ); CL_LinkUserMessage( pszName, svc_num, size );
} }
@@ -1593,17 +1590,23 @@ collect pings and packet lossage from clients
*/ */
void CL_UpdateUserPings( sizebuf_t *msg ) void CL_UpdateUserPings( sizebuf_t *msg )
{ {
int i, slot; // a1ba: there was a MAX_PLAYERS check but it doesn't make sense
player_info_t *player; // because pings message always ends by null bit
while( 1 )
for( i = 0; i < MAX_CLIENTS; i++ )
{ {
if( !MSG_ReadOneBit( msg )) break; // end of message int slot;
player_info_t *player;
if( !MSG_ReadOneBit( msg ))
break; // end of message
slot = MSG_ReadUBitLong( msg, MAX_CLIENT_BITS ); slot = MSG_ReadUBitLong( msg, MAX_CLIENT_BITS );
if( slot >= MAX_CLIENTS ) if( unlikely( slot >= MAX_CLIENTS ))
{
Host_Error( "%s: svc_pings > MAX_CLIENTS\n", __func__ ); Host_Error( "%s: svc_pings > MAX_CLIENTS\n", __func__ );
return;
}
player = &cl.players[slot]; player = &cl.players[slot];
player->ping = MSG_ReadUBitLong( msg, 12 ); player->ping = MSG_ReadUBitLong( msg, 12 );

View File

@@ -33,10 +33,12 @@ static client entity
static void CL_LegacyParseStaticEntity( sizebuf_t *msg ) static void CL_LegacyParseStaticEntity( sizebuf_t *msg )
{ {
int i; int i;
entity_state_t state; entity_state_t state = { 0 };
cl_entity_t *ent; cl_entity_t *ent;
memset( &state, 0, sizeof( state )); if( !clgame.static_entities )
clgame.static_entities = Mem_Calloc( clgame.mempool, sizeof( cl_entity_t ) * MAX_STATIC_ENTITIES );
state.modelindex = MSG_ReadShort( msg ); state.modelindex = MSG_ReadShort( msg );
state.sequence = MSG_ReadByte( msg ); state.sequence = MSG_ReadByte( msg );
state.frame = MSG_ReadByte( msg ); state.frame = MSG_ReadByte( msg );

View File

@@ -615,7 +615,7 @@ void CL_ParseGoldSrcServerMessage( sizebuf_t *msg )
case svc_setangle: case svc_setangle:
CL_ParseSetAngle( msg ); CL_ParseSetAngle( msg );
break; break;
case svc_goldsrc_serverinfo: case svc_serverdata:
Cbuf_Execute(); // make sure any stuffed commands are done Cbuf_Execute(); // make sure any stuffed commands are done
CL_ParseServerData( msg, PROTO_GOLDSRC ); CL_ParseServerData( msg, PROTO_GOLDSRC );
break; break;
@@ -625,7 +625,7 @@ void CL_ParseGoldSrcServerMessage( sizebuf_t *msg )
case svc_updateuserinfo: case svc_updateuserinfo:
CL_UpdateUserinfo( msg, PROTO_GOLDSRC ); CL_UpdateUserinfo( msg, PROTO_GOLDSRC );
break; break;
case svc_goldsrc_deltadescription: case svc_deltatable:
Delta_ParseTableField_GS( msg ); Delta_ParseTableField_GS( msg );
break; break;
case svc_clientdata: case svc_clientdata:
@@ -687,7 +687,7 @@ void CL_ParseGoldSrcServerMessage( sizebuf_t *msg )
case svc_addangle: case svc_addangle:
CL_ParseAddAngle( msg ); CL_ParseAddAngle( msg );
break; break;
case svc_goldsrc_newusermsg: case svc_usermessage:
CL_RegisterUserMessage( msg, PROTO_GOLDSRC ); CL_RegisterUserMessage( msg, PROTO_GOLDSRC );
break; break;
case svc_packetentities: case svc_packetentities:
@@ -709,7 +709,7 @@ void CL_ParseGoldSrcServerMessage( sizebuf_t *msg )
CL_ParseResourceList( msg, PROTO_GOLDSRC ); CL_ParseResourceList( msg, PROTO_GOLDSRC );
MSG_EndBitWriting( msg ); MSG_EndBitWriting( msg );
break; break;
case svc_goldsrc_newmovevars: case svc_deltamovevars:
CL_ParseNewMovevars( msg ); CL_ParseNewMovevars( msg );
break; break;
case svc_resourcerequest: case svc_resourcerequest:
@@ -749,10 +749,10 @@ void CL_ParseGoldSrcServerMessage( sizebuf_t *msg )
Con_Reportf( S_ERROR "%s: svc_goldsrc_timescale: implement me!\n", __func__ ); Con_Reportf( S_ERROR "%s: svc_goldsrc_timescale: implement me!\n", __func__ );
MSG_ReadFloat( msg ); MSG_ReadFloat( msg );
break; break;
case svc_goldsrc_sendcvarvalue: case svc_querycvarvalue:
CL_ParseCvarValue( msg, false, PROTO_GOLDSRC ); CL_ParseCvarValue( msg, false, PROTO_GOLDSRC );
break; break;
case svc_goldsrc_sendcvarvalue2: case svc_querycvarvalue2:
CL_ParseCvarValue( msg, true, PROTO_GOLDSRC ); CL_ParseCvarValue( msg, true, PROTO_GOLDSRC );
break; break;
case svc_exec: case svc_exec:

View File

@@ -628,11 +628,12 @@ CL_ParseStaticEntity
*/ */
static void CL_ParseQuakeStaticEntity( sizebuf_t *msg ) static void CL_ParseQuakeStaticEntity( sizebuf_t *msg )
{ {
entity_state_t state; entity_state_t state = { 0 };
cl_entity_t *ent; cl_entity_t *ent;
int i; int i;
memset( &state, 0, sizeof( state )); if( !clgame.static_entities )
clgame.static_entities = Mem_Calloc( clgame.mempool, sizeof( cl_entity_t ) * MAX_STATIC_ENTITIES );
state.modelindex = MSG_ReadByte( msg ); state.modelindex = MSG_ReadByte( msg );
state.frame = MSG_ReadByte( msg ); state.frame = MSG_ReadByte( msg );
@@ -969,9 +970,7 @@ void CL_ParseQuakeMessage( sizebuf_t *msg )
CL_ParseQuakeServerInfo( msg ); CL_ParseQuakeServerInfo( msg );
break; break;
case svc_lightstyle: case svc_lightstyle:
param1 = MSG_ReadByte( msg ); CL_ParseLightStyle( msg, PROTO_QUAKE );
str = MSG_ReadString( msg );
CL_SetLightstyle( param1, str, cl.mtime[0] );
break; break;
case svc_updatename: case svc_updatename:
param1 = MSG_ReadByte( msg ); param1 = MSG_ReadByte( msg );

View File

@@ -138,10 +138,7 @@ intptr_t CL_RenderGetParm( const int parm, const int arg, const qboolean checkRe
switch( parm ) switch( parm )
{ {
case PARM_BSP2_SUPPORTED: case PARM_BSP2_SUPPORTED:
#ifdef SUPPORT_BSP2_FORMAT
return 1; return 1;
#endif
return 0;
case PARAM_GAMEPAUSED: case PARAM_GAMEPAUSED:
return cl.paused; return cl.paused;
case PARM_CLIENT_INGAME: case PARM_CLIENT_INGAME:

View File

@@ -124,7 +124,7 @@ void SCR_DrawPos( void )
if( cls.state != ca_active || !cl_showpos.value || cl.background ) if( cls.state != ca_active || !cl_showpos.value || cl.background )
return; return;
ent = CL_EDICT_NUM( cl.playernum + 1 ); ent = CL_GetLocalPlayer();
speed = VectorLength( cl.simvel ); speed = VectorLength( cl.simvel );
Q_snprintf( msg, MAX_SYSPATH, Q_snprintf( msg, MAX_SYSPATH,

View File

@@ -2427,18 +2427,18 @@ static void CL_ClearLightStyles( void )
void CL_SetLightstyle( int style, const char *s, float f ) void CL_SetLightstyle( int style, const char *s, float f )
{ {
int i, k; int i;
lightstyle_t *ls; lightstyle_t *ls;
float val1, val2;
Assert( s != NULL ); if( unlikely( style < 0 || style >= MAX_LIGHTSTYLES ))
Assert( style >= 0 && style < MAX_LIGHTSTYLES ); {
Con_Printf( S_WARN "%s: ignored invalid lightstyle id %d\n", style );
return;
}
ls = &cl.lightstyles[style]; ls = &cl.lightstyles[style];
Q_strncpy( ls->pattern, s, sizeof( ls->pattern )); ls->length = Q_strncpy( ls->pattern, s, sizeof( ls->pattern ));
ls->length = Q_strlen( s );
ls->time = f; // set local time ls->time = f; // set local time
for( i = 0; i < ls->length; i++ ) for( i = 0; i < ls->length; i++ )
@@ -2448,10 +2448,10 @@ void CL_SetLightstyle( int style, const char *s, float f )
// check for allow interpolate // check for allow interpolate
// NOTE: fast flickering styles looks ugly when interpolation is running // NOTE: fast flickering styles looks ugly when interpolation is running
for( k = 0; k < (ls->length - 1); k++ ) for( i = 0; i < ( ls->length - 1 ); i++ )
{ {
val1 = ls->map[(k+0) % ls->length]; float val1 = ls->map[( i + 0 ) % ls->length];
val2 = ls->map[(k+1) % ls->length]; float val2 = ls->map[( i + 1 ) % ls->length];
if( fabs( val1 - val2 ) > STYLE_LERPING_THRESHOLD ) if( fabs( val1 - val2 ) > STYLE_LERPING_THRESHOLD )
{ {
@@ -2471,8 +2471,8 @@ DLIGHT MANAGEMENT
============================================================== ==============================================================
*/ */
dlight_t cl_dlights[MAX_DLIGHTS]; static dlight_t cl_dlights[MAX_DLIGHTS];
dlight_t cl_elights[MAX_ELIGHTS]; static dlight_t cl_elights[MAX_ELIGHTS];
/* /*
================ ================

View File

@@ -466,8 +466,8 @@ static void R_ShowTree_r( mnode_t *node, float x, float y, float scale, int show
R_DrawNodeConnection( x, y, x + scale, y + scale ); R_DrawNodeConnection( x, y, x + scale, y + scale );
} }
R_ShowTree_r( node->children[1], x - scale, y + scale, downScale, shownodes, viewleaf ); R_ShowTree_r( node_child( node, 1, cl.worldmodel ), x - scale, y + scale, downScale, shownodes, viewleaf );
R_ShowTree_r( node->children[0], x + scale, y + scale, downScale, shownodes, viewleaf ); R_ShowTree_r( node_child( node, 0, cl.worldmodel ), x + scale, y + scale, downScale, shownodes, viewleaf );
world.recursion_level--; world.recursion_level--;
} }
@@ -482,7 +482,7 @@ static void R_ShowTree( void )
return; return;
world.recursion_level = 0; world.recursion_level = 0;
viewleaf = Mod_PointInLeaf( refState.vieworg, cl.worldmodel->nodes ); viewleaf = Mod_PointInLeaf( refState.vieworg, cl.worldmodel->nodes, cl.worldmodel );
ref.dllFuncs.TriRenderMode( kRenderTransTexture ); ref.dllFuncs.TriRenderMode( kRenderTransTexture );

View File

@@ -495,7 +495,9 @@ typedef struct
cl_entity_t viewent; // viewmodel cl_entity_t viewent; // viewmodel
#if XASH_WIN32
qboolean client_dll_uses_sdl; qboolean client_dll_uses_sdl;
#endif
} clgame_static_t; } clgame_static_t;
typedef struct typedef struct
@@ -881,10 +883,12 @@ static inline qboolean CL_IsThirdPerson( void )
static inline cl_entity_t *CL_GetLocalPlayer( void ) static inline cl_entity_t *CL_GetLocalPlayer( void )
{ {
cl_entity_t *player; cl_entity_t *player = CL_GetEntityByIndex( cl.playernum + 1 );
player = CL_EDICT_NUM( cl.playernum + 1 ); // HACKHACK: GoldSrc doesn't do this, but some mods actually check it for null pointer
Assert( player != NULL ); // this is a lesser evil than changing semantics of HUD_VidInit and call it after entities are allocated
if( !player )
Con_Printf( S_WARN "%s: client entities are not initialized yet! Returning NULL...\n", __func__ );
return player; return player;
} }
@@ -1195,10 +1199,9 @@ void Key_Init( void );
void Key_WriteBindings( file_t *f ); void Key_WriteBindings( file_t *f );
const char *Key_GetBinding( int keynum ); const char *Key_GetBinding( int keynum );
void Key_SetBinding( int keynum, const char *binding ); void Key_SetBinding( int keynum, const char *binding );
const char *Key_LookupBinding( const char *pBinding );
void Key_ClearStates( void ); void Key_ClearStates( void );
const char *Key_KeynumToString( int keynum ); const char *Key_KeynumToString( int keynum );
int Key_StringToKeynum( const char *str );
int Key_GetKey( const char *binding );
void Key_EnumCmds_f( void ); void Key_EnumCmds_f( void );
void Key_SetKeyDest( int key_dest ); void Key_SetKeyDest( int key_dest );
void Key_EnableTextInput( qboolean enable, qboolean force ); void Key_EnableTextInput( qboolean enable, qboolean force );

View File

@@ -1101,7 +1101,7 @@ void Touch_Init( void )
Touch_AddDefaultButton( "duck", "touch_default/crouch", "+duck", 0.880000, 0.757428, 1.000000, 0.984656, color, 2, 1, 0 ); Touch_AddDefaultButton( "duck", "touch_default/crouch", "+duck", 0.880000, 0.757428, 1.000000, 0.984656, color, 2, 1, 0 );
Touch_AddDefaultButton( "tduck", "touch_default/tduck", ";+duck", 0.560000, 0.833171, 0.620000, 0.946785, color, 2, 1, 0 ); Touch_AddDefaultButton( "tduck", "touch_default/tduck", ";+duck", 0.560000, 0.833171, 0.620000, 0.946785, color, 2, 1, 0 );
Touch_AddDefaultButton( "edit", "touch_default/settings", "touch_enableedit", 0.420000, 0.000000, 0.500000, 0.151486, color, 2, 1, 32 ); Touch_AddDefaultButton( "edit", "touch_default/settings", "touch_enableedit", 0.420000, 0.000000, 0.500000, 0.151486, color, 2, 1, 32 );
Touch_AddDefaultButton( "menu", "touch_default/menu", "escape", 0.000000, 0.833171, 0.080000, 0.984656, color, 2, 1, 0 ); Touch_AddDefaultButton( "menu", "touch_default/menu", "cancelselect", 0.000000, 0.833171, 0.080000, 0.984656, color, 2, 1, 0 );
Touch_AddDefaultButton( "spray", "touch_default/spray", "impulse 201", 0.680000, 0.000000, 0.760000, 0.142222, color, 2, 1, 8 ); Touch_AddDefaultButton( "spray", "touch_default/spray", "impulse 201", 0.680000, 0.000000, 0.760000, 0.142222, color, 2, 1, 8 );
Touch_AddDefaultButton( "voicechat", "touch_default/microphone", "+voicerecord", 0.780000, 0.817778, 0.860000, 0.960000, color, 2, 1, 8 ); Touch_AddDefaultButton( "voicechat", "touch_default/microphone", "+voicerecord", 0.780000, 0.817778, 0.860000, 0.960000, color, 2, 1, 8 );

View File

@@ -633,8 +633,6 @@ Called every frame, even if not generating commands
*/ */
void Host_InputFrame( void ) void Host_InputFrame( void )
{ {
Sys_SendKeyEvents ();
IN_Commands(); IN_Commands();
IN_MouseMove(); IN_MouseMove();

View File

@@ -34,13 +34,13 @@ typedef struct keyname_s
const char *binding; // default bind const char *binding; // default bind
} keyname_t; } keyname_t;
enginekey_t keys[256]; static enginekey_t keys[256];
keyname_t keynames[] = static const keyname_t keynames[] =
{ {
{"TAB", K_TAB, "" }, {"TAB", K_TAB, "" },
{"ENTER", K_ENTER, "" }, {"ENTER", K_ENTER, "" },
{"ESCAPE", K_ESCAPE, "escape" }, // hardcoded {"ESCAPE", K_ESCAPE, "cancelselect" }, // hardcoded
{"SPACE", K_SPACE, "+jump" }, {"SPACE", K_SPACE, "+jump" },
{"BACKSPACE", K_BACKSPACE, "" }, {"BACKSPACE", K_BACKSPACE, "" },
{"UPARROW", K_UPARROW, "+forward" }, {"UPARROW", K_UPARROW, "+forward" },
@@ -106,7 +106,7 @@ keyname_t keynames[] =
{"Y_BUTTON", K_Y_BUTTON, "impulse 100"}, // Flashlight {"Y_BUTTON", K_Y_BUTTON, "impulse 100"}, // Flashlight
{"BACK", K_BACK_BUTTON, "pause"}, // Menu {"BACK", K_BACK_BUTTON, "pause"}, // Menu
{"MODE", K_MODE_BUTTON, ""}, {"MODE", K_MODE_BUTTON, ""},
{"START", K_START_BUTTON, "escape"}, {"START", K_START_BUTTON, "cancelselect"},
{"STICK1", K_LSTICK, "+speed"}, {"STICK1", K_LSTICK, "+speed"},
{"STICK2", K_RSTICK, "+duck"}, {"STICK2", K_RSTICK, "+duck"},
{"L1_BUTTON", K_L1_BUTTON, "+duck"}, {"L1_BUTTON", K_L1_BUTTON, "+duck"},
@@ -139,7 +139,6 @@ keyname_t keynames[] =
// raw semicolon seperates commands // raw semicolon seperates commands
{"SEMICOLON", ';', "" }, {"SEMICOLON", ';', "" },
{NULL, 0, NULL },
}; };
static void OSK_EnableTextInput( qboolean enable, qboolean force ); static void OSK_EnableTextInput( qboolean enable, qboolean force );
@@ -172,48 +171,25 @@ the K_* names are matched up.
to be configured even if they don't have defined names. to be configured even if they don't have defined names.
=================== ===================
*/ */
int Key_StringToKeynum( const char *str ) static int Key_StringToKeynum( const char *str )
{ {
keyname_t *kn; int i;
if( !str || !str[0] ) return -1; if( !str || !str[0] )
if( !str[1] ) return str[0]; return -1;
if( !str[1] )
return str[0];
// check for hex code // check for hex code
if( str[0] == '0' && str[1] == 'x' && Q_strlen( str ) == 4 ) if( str[0] == '0' && str[1] == 'x' && Q_strlen( str ) == 4 )
{ return COM_Nibble( str[2] ) << 4 | COM_Nibble( str[3] );
int n1, n2;
n1 = str[2];
if( n1 >= '0' && n1 <= '9' )
{
n1 -= '0';
}
else if( n1 >= 'a' && n1 <= 'f' )
{
n1 = n1 - 'a' + 10;
}
else n1 = 0;
n2 = str[3];
if( n2 >= '0' && n2 <= '9' )
{
n2 -= '0';
}
else if( n2 >= 'a' && n2 <= 'f' )
{
n2 = n2 - 'a' + 10;
}
else n2 = 0;
return n1 * 16 + n2;
}
// scan for a text match // scan for a text match
for( kn = keynames; kn->name; kn++ ) for( i = 0; i < ARRAYSIZE( keynames ); i++ )
{ {
if( !Q_stricmp( str, kn->name )) if( !Q_stricmp( str, keynames[i].name ))
return kn->keynum; return keynames[i].keynum;
} }
return -1; return -1;
@@ -229,12 +205,14 @@ given keynum.
*/ */
const char *Key_KeynumToString( int keynum ) const char *Key_KeynumToString( int keynum )
{ {
keyname_t *kn;
static char tinystr[5]; static char tinystr[5];
int i, j; int i, j;
if ( keynum == -1 ) return "<KEY NOT FOUND>"; if( keynum == -1 )
if ( keynum < 0 || keynum > 255 ) return "<OUT OF RANGE>"; return "<KEY NOT FOUND>";
if( keynum < 0 || keynum > 255 )
return "<OUT OF RANGE>";
// check for printable ascii (don't use quote) // check for printable ascii (don't use quote)
if( keynum > 32 && keynum < 127 && keynum != '"' && keynum != ';' && keynum != K_SCROLLOCK ) if( keynum > 32 && keynum < 127 && keynum != '"' && keynum != ';' && keynum != K_SCROLLOCK )
@@ -245,10 +223,10 @@ const char *Key_KeynumToString( int keynum )
} }
// check for a key string // check for a key string
for( kn = keynames; kn->name; kn++ ) for( i = 0; i < ARRAYSIZE( keynames ); i++ )
{ {
if( keynum == kn->keynum ) if( keynum == keynames[i].keynum )
return kn->name; return keynames[i].name;
} }
// make a hex string // make a hex string
@@ -301,7 +279,7 @@ const char *Key_GetBinding( int keynum )
Key_GetKey Key_GetKey
=================== ===================
*/ */
int Key_GetKey( const char *pBinding ) static int Key_GetKey( const char *pBinding )
{ {
int i, len; int i, len;
const char *p; const char *p;
@@ -327,6 +305,17 @@ int Key_GetKey( const char *pBinding )
return -1; return -1;
} }
/*
=============
Key_LookupBinding
=============
*/
const char *Key_LookupBinding( const char *pBinding )
{
return Key_KeynumToString( Key_GetKey( pBinding ));
}
/* /*
=================== ===================
Key_Unbind_f Key_Unbind_f
@@ -350,6 +339,12 @@ static void Key_Unbind_f( void )
return; return;
} }
if( b == K_ESCAPE )
{
Con_Printf( "Can't unbind ESCAPE key\n" );
return;
}
Key_SetBinding( b, "" ); Key_SetBinding( b, "" );
} }
@@ -369,8 +364,8 @@ static void Key_Unbindall_f( void )
} }
// set some defaults // set some defaults
Key_SetBinding( K_ESCAPE, "escape" ); Key_SetBinding( K_ESCAPE, "cancelselect" );
Key_SetBinding( K_START_BUTTON, "escape" ); Key_SetBinding( K_START_BUTTON, "cancelselect" );
} }
/* /*
@@ -380,7 +375,6 @@ Key_Reset_f
*/ */
static void Key_Reset_f( void ) static void Key_Reset_f( void )
{ {
keyname_t *kn;
int i; int i;
// clear all keys first // clear all keys first
@@ -391,8 +385,8 @@ static void Key_Reset_f( void )
} }
// apply default values // apply default values
for( kn = keynames; kn->name; kn++ ) for( i = 0; i < ARRAYSIZE( keynames ); i++ )
Key_SetBinding( kn->keynum, kn->binding ); Key_SetBinding( keynames[i].keynum, keynames[i].binding );
} }
/* /*
@@ -500,7 +494,7 @@ Key_Init
*/ */
void Key_Init( void ) void Key_Init( void )
{ {
keyname_t *kn; int i;
// register our functions // register our functions
Cmd_AddRestrictedCommand( "bind", Key_Bind_f, "binds a command to the specified key in bindmap" ); Cmd_AddRestrictedCommand( "bind", Key_Bind_f, "binds a command to the specified key in bindmap" );
@@ -511,7 +505,8 @@ void Key_Init( void )
Cmd_AddCommand( "makehelp", Key_EnumCmds_f, "write help.txt that contains all console cvars and cmds" ); Cmd_AddCommand( "makehelp", Key_EnumCmds_f, "write help.txt that contains all console cvars and cmds" );
// setup default binding. "unbindall" from config.cfg will be reset it // setup default binding. "unbindall" from config.cfg will be reset it
for( kn = keynames; kn->name; kn++ ) Key_SetBinding( kn->keynum, kn->binding ); for( i = 0; i < ARRAYSIZE( keynames ); i++ )
Key_SetBinding( keynames[i].keynum, keynames[i].binding );
Cvar_RegisterVariable( &osk_enable ); Cvar_RegisterVariable( &osk_enable );
Cvar_RegisterVariable( &key_rotate ); Cvar_RegisterVariable( &key_rotate );
@@ -748,18 +743,24 @@ void GAME_EXPORT Key_Event( int key, int down )
if( cls.key_dest == key_menu ) if( cls.key_dest == key_menu )
{ {
// only non printable keys passed // classic Xash3D menus don't have an extension that tells engine
// to enable text input
if( !gameui.use_extended_api ) if( !gameui.use_extended_api )
Key_EnableTextInput( true, false );
//pass printable chars for old menus
if( !gameui.use_extended_api && !host.textmode && down && ( key >= 32 ) && ( key <= 'z' ) )
{ {
if( Key_IsDown( K_SHIFT ) ) // we don't know if menu wants text input or not
// enable it unconditionally
Key_EnableTextInput( true, false );
// pass this key to the menu, if printable
if( !host.textmode && down && ( key >= 32 ) && ( key <= 'z' ))
{ {
key += 'A' - 'a'; if( Key_IsDown( K_SHIFT ))
key += 'A' - 'a';
UI_CharEvent( key );
} }
UI_CharEvent( key );
} }
UI_KeyEvent( key, down ); UI_KeyEvent( key, down );
return; return;
} }
@@ -855,11 +856,12 @@ void GAME_EXPORT Key_ClearStates( void )
int i; int i;
// don't clear keys during changelevel // don't clear keys during changelevel
if( cls.changelevel ) return; if( cls.changelevel )
return;
for( i = 0; i < 256; i++ ) for( i = 0; i < 256; i++ )
{ {
if( keys[i].down ) if( keys[i].down && i < K_MOUSE1 && i > K_MOUSE5 )
Key_Event( i, false ); Key_Event( i, false );
keys[i].down = 0; keys[i].down = 0;
@@ -867,6 +869,9 @@ void GAME_EXPORT Key_ClearStates( void )
keys[i].gamedown = 0; keys[i].gamedown = 0;
} }
for( i = K_MOUSE1; i < K_MOUSE5; i++ ) // from K_MOUSE1 to K_MOUSE5
IN_MouseEvent( i - K_MOUSE1, false );
if( clgame.hInstance ) if( clgame.hInstance )
clgame.dllFuncs.IN_ClearStates(); clgame.dllFuncs.IN_ClearStates();
} }
@@ -909,17 +914,16 @@ A helper function if platform input doesn't support text mode properly
*/ */
int Key_ToUpper( int keynum ) int Key_ToUpper( int keynum )
{ {
keynum = Q_toupper( keynum );
if( keynum == '-' ) if( keynum == '-' )
keynum = '_'; return '_';
if( keynum == '=' ) if( keynum == '=' )
keynum = '+'; return '+';
if( keynum == ';' ) if( keynum == ';' )
keynum = ':'; return ':';
if( keynum == '\'' ) if( keynum == '\'' )
keynum = '"'; return '"';
return keynum; return Q_toupper( keynum );
} }
/* On-screen keyboard: /* On-screen keyboard:

View File

@@ -470,16 +470,16 @@ out_free:
* This is a stack of the clipnodes we have traversed * This is a stack of the clipnodes we have traversed
* "sides" indicates which side we went down each time * "sides" indicates which side we went down each time
*/ */
static mclipnode_t *node_stack[MAX_CLIPNODE_DEPTH]; static int node_stack[MAX_CLIPNODE_DEPTH];
static int side_stack[MAX_CLIPNODE_DEPTH]; static int side_stack[MAX_CLIPNODE_DEPTH];
static uint node_stack_depth; static uint node_stack_depth;
static void push_node( mclipnode_t *node, int side ) static void push_node( int nodenum, int side )
{ {
if( node_stack_depth == MAX_CLIPNODE_DEPTH ) if( node_stack_depth == MAX_CLIPNODE_DEPTH )
Host_Error( "node stack overflow\n" ); Host_Error( "node stack overflow\n" );
node_stack[node_stack_depth] = node; node_stack[node_stack_depth] = nodenum;
side_stack[node_stack_depth] = side; side_stack[node_stack_depth] = side;
node_stack_depth++; node_stack_depth++;
} }
@@ -502,22 +502,27 @@ static void free_hull_polys( hullnode_t *hull_polys )
} }
} }
static void hull_windings_r( hull_t *hull, mclipnode_t *node, hullnode_t *polys, hull_model_t *model ); static void hull_windings_r( hull_t *hull, int nodenum, hullnode_t *polys, hull_model_t *model );
static void do_hull_recursion( hull_t *hull, mclipnode_t *node, int side, hullnode_t *polys, hull_model_t *model ) static void do_hull_recursion( hull_t *hull, int nodenum, int side, hullnode_t *polys, hull_model_t *model )
{ {
winding_t *w, *next; winding_t *w, *next;
int childnum;
if( node->children[side] >= 0 ) if( world.version == QBSP2_VERSION )
childnum = hull->clipnodes32[nodenum].children[side];
else
childnum = hull->clipnodes16[nodenum].children[side];
if( childnum >= 0 )
{ {
mclipnode_t *child = hull->clipnodes + node->children[side]; push_node( nodenum, side );
push_node( node, side ); hull_windings_r( hull, childnum, polys, model );
hull_windings_r( hull, child, polys, model );
pop_node(); pop_node();
} }
else else
{ {
switch( node->children[side] ) switch( childnum )
{ {
case CONTENTS_EMPTY: case CONTENTS_EMPTY:
case CONTENTS_WATER: case CONTENTS_WATER:
@@ -542,20 +547,25 @@ static void do_hull_recursion( hull_t *hull, mclipnode_t *node, int side, hullno
} }
break; break;
default: default:
Host_Error( "bad contents: %i\n", node->children[side] ); Host_Error( "bad contents: %i\n", childnum );
break; break;
} }
} }
} }
static void hull_windings_r( hull_t *hull, mclipnode_t *node, hullnode_t *polys, hull_model_t *model ) static void hull_windings_r( hull_t *hull, int nodenum, hullnode_t *polys, hull_model_t *model )
{ {
mplane_t *plane = hull->planes + node->planenum; mplane_t *plane;
hullnode_t frontlist = LIST_HEAD_INIT( frontlist ); hullnode_t frontlist = LIST_HEAD_INIT( frontlist );
hullnode_t backlist = LIST_HEAD_INIT( backlist ); hullnode_t backlist = LIST_HEAD_INIT( backlist );
winding_t *w, *next, *front, *back; winding_t *w, *next, *front, *back;
int i; int i;
if( world.version == QBSP2_VERSION )
plane = hull->planes + hull->clipnodes32[nodenum].planenum;
else
plane = hull->planes + hull->clipnodes16[nodenum].planenum;
list_for_each_entry_safe( w, next, polys, chain ) list_for_each_entry_safe( w, next, polys, chain )
{ {
// PARANIOA - PAIR CHECK // PARANIOA - PAIR CHECK
@@ -601,7 +611,13 @@ static void hull_windings_r( hull_t *hull, mclipnode_t *node, hullnode_t *polys,
for( i = 0; w && i < node_stack_depth; i++ ) for( i = 0; w && i < node_stack_depth; i++ )
{ {
mplane_t *p = hull->planes + node_stack[i]->planenum; mplane_t *p;
if( world.version == QBSP2_VERSION )
p = hull->planes + hull->clipnodes32[node_stack[i]].planenum;
else
p = hull->planes + hull->clipnodes16[node_stack[i]].planenum;
w = winding_clip( w, p, false, side_stack[i], 0.00001 ); w = winding_clip( w, p, false, side_stack[i], 0.00001 );
} }
@@ -625,8 +641,8 @@ static void hull_windings_r( hull_t *hull, mclipnode_t *node, hullnode_t *polys,
Con_Printf( S_WARN "new winding was clipped away!\n" ); Con_Printf( S_WARN "new winding was clipped away!\n" );
} }
do_hull_recursion( hull, node, 0, &frontlist, model ); do_hull_recursion( hull, nodenum, 0, &frontlist, model );
do_hull_recursion( hull, node, 1, &backlist, model ); do_hull_recursion( hull, nodenum, 1, &backlist, model );
} }
static void remove_paired_polys( hull_model_t *model ) static void remove_paired_polys( hull_model_t *model )
@@ -655,7 +671,7 @@ static void make_hull_windings( hull_t *hull, hull_model_t *model )
if( hull->planes != NULL ) if( hull->planes != NULL )
{ {
hull_windings_r( hull, hull->clipnodes + hull->firstclipnode, &head, model ); hull_windings_r( hull, hull->firstclipnode, &head, model );
remove_paired_polys( model ); remove_paired_polys( model );
} }
Con_Reportf( "%i hull polys\n", model->num_polys ); Con_Reportf( "%i hull polys\n", model->num_polys );

View File

@@ -181,6 +181,11 @@ static void pfnCvar_FullSet( const char *var_name, const char *value, int flags
Cvar_FullSet( var_name, value, flags | FCVAR_REFDLL ); Cvar_FullSet( var_name, value, flags | FCVAR_REFDLL );
} }
static int Cmd_AddRefCommand( const char *cmd_name, xcommand_t function, const char *description )
{
return Cmd_AddCommandEx( cmd_name, function, description, CMD_REFDLL, __func__ );
}
static void pfnStudioEvent( const mstudioevent_t *event, const cl_entity_t *e ) static void pfnStudioEvent( const mstudioevent_t *event, const cl_entity_t *e )
{ {
clgame.dllFuncs.pfnStudioEvent( event, e ); clgame.dllFuncs.pfnStudioEvent( event, e );
@@ -295,6 +300,12 @@ static qboolean R_Init_Video_( const int type )
return R_Init_Video( 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 = static const ref_api_t gEngfuncs =
{ {
pfnEngineGetParm, pfnEngineGetParm,
@@ -335,7 +346,7 @@ static const ref_api_t gEngfuncs =
Mod_SampleSizeForFace, Mod_SampleSizeForFace,
Mod_BoxVisible, Mod_BoxVisible,
Mod_PointInLeaf, pfnMod_PointInLeaf,
R_DrawWorldHull, R_DrawWorldHull,
R_DrawModelHull, R_DrawModelHull,

View File

@@ -996,7 +996,7 @@ static void S_UpdateAmbientSounds( void )
// calc ambient sound levels // calc ambient sound levels
if( !cl.worldmodel ) return; if( !cl.worldmodel ) return;
leaf = Mod_PointInLeaf( s_listener.origin, cl.worldmodel->nodes ); leaf = Mod_PointInLeaf( s_listener.origin, cl.worldmodel->nodes, cl.worldmodel );
if( !leaf || !s_ambient_level.value ) if( !leaf || !s_ambient_level.value )
{ {
@@ -1944,7 +1944,8 @@ qboolean S_Init( void )
Cmd_AddCommand( "play2", S_Play2_f, "playing a group of specified sound files" ); // nehahra stuff Cmd_AddCommand( "play2", S_Play2_f, "playing a group of specified sound files" ); // nehahra stuff
Cmd_AddCommand( "playvol", S_PlayVol_f, "playing a specified sound file with specified volume" ); Cmd_AddCommand( "playvol", S_PlayVol_f, "playing a specified sound file with specified volume" );
Cmd_AddCommand( "stopsound", S_StopSound_f, "stop all sounds" ); Cmd_AddCommand( "stopsound", S_StopSound_f, "stop all sounds" );
Cmd_AddCommand( "music", S_Music_f, "starting a background track" ); // HLU SDK have command with the same name
Cmd_AddCommandWithFlags( "music", S_Music_f, "starting a background track", CMD_OVERRIDABLE );
Cmd_AddCommand( "soundlist", S_SoundList_f, "display loaded sounds" ); Cmd_AddCommand( "soundlist", S_SoundList_f, "display loaded sounds" );
Cmd_AddCommand( "s_info", S_SoundInfo_f, "print sound system information" ); Cmd_AddCommand( "s_info", S_SoundInfo_f, "print sound system information" );
Cmd_AddCommand( "s_fade", S_SoundFade_f, "fade all sounds then stop all" ); Cmd_AddCommand( "s_fade", S_SoundFade_f, "fade all sounds then stop all" );
@@ -1987,7 +1988,8 @@ void S_Shutdown( void )
Cmd_RemoveCommand( "play" ); Cmd_RemoveCommand( "play" );
Cmd_RemoveCommand( "playvol" ); Cmd_RemoveCommand( "playvol" );
Cmd_RemoveCommand( "stopsound" ); Cmd_RemoveCommand( "stopsound" );
Cmd_RemoveCommand( "music" ); if( Cmd_Exists( "music" ))
Cmd_RemoveCommand( "music" );
Cmd_RemoveCommand( "soundlist" ); Cmd_RemoveCommand( "soundlist" );
Cmd_RemoveCommand( "s_info" ); Cmd_RemoveCommand( "s_info" );
Cmd_RemoveCommand( "s_fade" ); Cmd_RemoveCommand( "s_fade" );

View File

@@ -25,20 +25,29 @@ GNU General Public License for more details.
#define VGUI_MAX_TEXTURES 1024 #define VGUI_MAX_TEXTURES 1024
typedef struct vgui_reusable_texture_s
{
int gl_texturenum;
byte hash[16];
} vgui_reusable_texture_t;
typedef struct vgui_static_s typedef struct vgui_static_s
{ {
qboolean initialized; qboolean initialized;
VGUI_DefaultCursor cursor; VGUI_DefaultCursor cursor;
vguiapi_t dllFuncs; vguiapi_t dllFuncs;
int textures[VGUI_MAX_TEXTURES]; vgui_reusable_texture_t *textures;
int texture_id; int texture_id;
int max_textures;
int bound_texture; int bound_texture;
byte color[4]; byte color[4];
qboolean enable_texture; qboolean enable_texture;
HINSTANCE hInstance; HINSTANCE hInstance;
poolhandle_t mempool;
enum VGUI_KeyCode virtualKeyTrans[256]; enum VGUI_KeyCode virtualKeyTrans[256];
} vgui_static_t; } vgui_static_t;
@@ -49,9 +58,16 @@ static CVAR_DEFINE_AUTO( vgui_utf8, "0", FCVAR_ARCHIVE, "enable utf-8 support fo
static void GAME_EXPORT VGUI_DrawInit( void ) static void GAME_EXPORT VGUI_DrawInit( void )
{ {
memset( vgui.textures, 0, sizeof( vgui.textures )); if( vgui.mempool )
Mem_EmptyPool( vgui.mempool );
else vgui.mempool = Mem_AllocPool( "VGui Support Pool" );
vgui.textures = NULL;
memset( vgui.color, 0, sizeof( vgui.color )); memset( vgui.color, 0, sizeof( vgui.color ));
vgui.texture_id = vgui.bound_texture = 0; vgui.texture_id = 0;
vgui.bound_texture = 0;
vgui.max_textures = 0;
vgui.enable_texture = true; vgui.enable_texture = true;
} }
@@ -60,28 +76,79 @@ static void GAME_EXPORT VGUI_DrawShutdown( void )
int i; int i;
for( i = 1; i < vgui.texture_id; i++ ) for( i = 1; i < vgui.texture_id; i++ )
ref.dllFuncs.GL_FreeTexture( vgui.textures[i] ); ref.dllFuncs.GL_FreeTexture( vgui.textures[i].gl_texturenum );
Mem_FreePool( &vgui.mempool );
vgui.textures = NULL;
memset( vgui.color, 0, sizeof( vgui.color ));
vgui.texture_id = 0;
vgui.bound_texture = 0;
vgui.max_textures = 0;
} }
static int GAME_EXPORT VGUI_GenerateTexture( void ) static int GAME_EXPORT VGUI_GenerateTexture( void )
{ {
if( ++vgui.texture_id >= VGUI_MAX_TEXTURES ) // allocate new
Host_Error( "%s: VGUI_MAX_TEXTURES limit exceeded\n", __func__ ); if( vgui.texture_id + 1 >= vgui.max_textures )
{
if( vgui.max_textures + VGUI_MAX_TEXTURES >= VGUI_MAX_TEXTURES * VGUI_MAX_TEXTURES )
{
// in theory it might look up texture that hasn't been bound for a while and
// reuse that but it will eventually overwrite some important textures anyway
Con_Printf( S_ERROR "%s: Refusing resizing VGUI textures array due to memory leak\n", __func__ );
return vgui.texture_id;
}
return vgui.texture_id; vgui.max_textures += VGUI_MAX_TEXTURES;
// this potentially might leak memory if VGUI is used incorrectly!
// (like in Cry of Fear)
vgui.textures = Mem_Realloc( vgui.mempool, vgui.textures, sizeof( *vgui.textures ) * vgui.max_textures );
// warn mod developer
if( vgui.max_textures >= VGUI_MAX_TEXTURES * 4 )
Con_Printf( S_ERROR "%s: Potential memory leak in VGUI code is detected!\n", __func__ );
}
return ++vgui.texture_id;
} }
static void GAME_EXPORT VGUI_UploadTexture( int id, const char *buffer, int width, int height ) static void GAME_EXPORT VGUI_UploadTexture( int id, const char *buffer, int width, int height )
{ {
rgbdata_t r_image = { 0 }; rgbdata_t r_image = { 0 };
char texName[32]; char texName[32];
MD5Context_t ctx;
byte hash[16];
if( id <= 0 || id >= VGUI_MAX_TEXTURES ) if( id <= 0 || id >= vgui.max_textures || width <= 0 || height <= 0 )
{ {
Con_DPrintf( S_ERROR "%s: bad texture %i. Ignored\n", __func__, id ); Con_DPrintf( S_ERROR "%s: bad texture %i. Ignored\n", __func__, id );
return; return;
} }
// need to do this as some mods tend to upload same texture over and over
// exhausing engine-wide limit on textures and leaking vram
MD5Init( &ctx );
MD5Update( &ctx, buffer, width * height * 4 );
MD5Final( hash, &ctx );
// it's a new texture, try to find a copy
if( vgui.textures[id].gl_texturenum == 0 )
{
int i;
for( i = 1; i < vgui.texture_id; i++ )
{
if( vgui.textures[i].gl_texturenum != 0 && !memcmp( vgui.textures[i].hash, hash, sizeof( hash )))
{
// copy data to new texture id
vgui.textures[id] = vgui.textures[i];
return;
}
}
}
Q_snprintf( texName, sizeof( texName ), "*vgui%i", id ); Q_snprintf( texName, sizeof( texName ), "*vgui%i", id );
r_image.width = width; r_image.width = width;
@@ -91,51 +158,29 @@ static void GAME_EXPORT VGUI_UploadTexture( int id, const char *buffer, int widt
r_image.flags = IMAGE_HAS_COLOR|IMAGE_HAS_ALPHA; r_image.flags = IMAGE_HAS_COLOR|IMAGE_HAS_ALPHA;
r_image.buffer = (byte*)buffer; r_image.buffer = (byte*)buffer;
vgui.textures[id] = GL_LoadTextureInternal( texName, &r_image, TF_IMAGE ); vgui.textures[id].gl_texturenum = GL_LoadTextureInternal( texName, &r_image, TF_IMAGE );
memcpy( vgui.textures[id].hash, hash, sizeof( hash ));
} }
static void GAME_EXPORT VGUI_CreateTexture( int id, int width, int height ) static void GAME_EXPORT VGUI_CreateTexture( int id, int width, int height )
{ {
rgbdata_t r_image = { 0 }; // nothing uses it, it can be removed
char texName[32]; Host_Error( "%s: deprecated\n", __func__ );
if( id <= 0 || id >= VGUI_MAX_TEXTURES )
{
Con_DPrintf( S_ERROR "%s: bad texture %i. Ignored\n", __func__, id );
return;
}
Q_snprintf( texName, sizeof( texName ), "*vgui%i", id );
r_image.width = width;
r_image.height = height;
r_image.type = PF_RGBA_32;
r_image.size = width * height * 4;
r_image.flags = IMAGE_HAS_COLOR|IMAGE_HAS_ALPHA;
r_image.buffer = NULL;
vgui.textures[id] = GL_LoadTextureInternal( texName, &r_image, TF_IMAGE );
vgui.bound_texture = id;
} }
static void GAME_EXPORT VGUI_UploadTextureBlock( int id, int drawX, int drawY, const byte *rgba, int blockWidth, int blockHeight ) static void GAME_EXPORT VGUI_UploadTextureBlock( int id, int drawX, int drawY, const byte *rgba, int blockWidth, int blockHeight )
{ {
if( id <= 0 || id >= VGUI_MAX_TEXTURES || vgui.textures[id] == 0 ) // nothing uses it, it can be removed
{ Host_Error( "%s: deprecated\n", __func__ );
Con_DPrintf( S_ERROR "%s: bad texture %i. Ignored\n", __func__, id );
return;
}
ref.dllFuncs.VGUI_UploadTextureBlock( drawX, drawY, rgba, blockWidth, blockHeight );
vgui.bound_texture = id;
} }
static void GAME_EXPORT VGUI_BindTexture( int id ) static void GAME_EXPORT VGUI_BindTexture( int id )
{ {
if( id <= 0 || id >= VGUI_MAX_TEXTURES || !vgui.textures[id] ) if( id <= 0 || id >= vgui.max_textures || !vgui.textures[id].gl_texturenum )
id = 1; // NOTE: same as bogus index 2700 in GoldSrc id = 1; // NOTE: same as bogus index 2700 in GoldSrc
ref.dllFuncs.GL_Bind( XASH_TEXTURE0, vgui.textures[id] ); ref.dllFuncs.GL_Bind( XASH_TEXTURE0, vgui.textures[id].gl_texturenum );
vgui.bound_texture = id; vgui.bound_texture = id;
} }
@@ -144,7 +189,7 @@ static void GAME_EXPORT VGUI_GetTextureSizes( int *w, int *h )
int texnum; int texnum;
if( vgui.bound_texture ) if( vgui.bound_texture )
texnum = vgui.textures[vgui.bound_texture]; texnum = vgui.textures[vgui.bound_texture].gl_texturenum;
else else
texnum = R_GetBuiltinTexture( REF_DEFAULT_TEXTURE ); texnum = R_GetBuiltinTexture( REF_DEFAULT_TEXTURE );
@@ -187,7 +232,7 @@ static void GAME_EXPORT VGUI_DrawQuad( const vpoint_t *ul, const vpoint_t *lr )
t2 = lr->coord[1]; t2 = lr->coord[1];
ref.dllFuncs.Color4ub( vgui.color[0], vgui.color[1], vgui.color[2], vgui.color[3] ); ref.dllFuncs.Color4ub( vgui.color[0], vgui.color[1], vgui.color[2], vgui.color[3] );
ref.dllFuncs.R_DrawStretchPic( x, y, w, h, s1, t1, s2, t2, vgui.textures[vgui.bound_texture] ); ref.dllFuncs.R_DrawStretchPic( x, y, w, h, s1, t1, s2, t2, vgui.textures[vgui.bound_texture].gl_texturenum );
} }
else else
{ {

View File

@@ -142,28 +142,33 @@ void Cbuf_AddFilteredText( const char *text )
Cbuf_InsertText Cbuf_InsertText
Adds command text immediately after the current command Adds command text immediately after the current command
Adds a \n to the text
============ ============
*/ */
static void Cbuf_InsertTextToBuffer( cmdbuf_t *buf, const char *text ) static void Cbuf_InsertTextToBuffer( cmdbuf_t *buf, const char *text, size_t len, size_t requested_len )
{ {
int l = Q_strlen( text ); if(( buf->cursize + requested_len ) >= buf->maxsize )
if(( buf->cursize + l ) >= buf->maxsize )
{ {
Con_Reportf( S_WARN "%s: overflow\n", __func__ ); Con_Reportf( S_WARN "%s: overflow\n", __func__ );
} }
else else
{ {
memmove( buf->data + l, buf->data, buf->cursize ); memmove( buf->data + len, buf->data, buf->cursize );
memcpy( buf->data, text, l ); memcpy( buf->data, text, len );
buf->cursize += l; buf->cursize += len;
} }
} }
void Cbuf_InsertTextLen( const char *text, size_t len, size_t requested_len )
{
// sometimes we need to insert more data than we have
// but also prevent overflow
Cbuf_InsertTextToBuffer( &cmd_text, text, len, requested_len );
}
void Cbuf_InsertText( const char *text ) void Cbuf_InsertText( const char *text )
{ {
Cbuf_InsertTextToBuffer( &cmd_text, text ); size_t l = Q_strlen( text );
Cbuf_InsertTextToBuffer( &cmd_text, text, l, l );
} }
/* /*
@@ -263,10 +268,16 @@ Cbuf_Execute
void Cbuf_Execute( void ) void Cbuf_Execute( void )
{ {
Cbuf_ExecuteCommandsFromBuffer( &cmd_text, true, -1 ); Cbuf_ExecuteCommandsFromBuffer( &cmd_text, true, -1 );
// a1ba: unlimited commands for filtered buffer per frame
// a1ba: goldsrc limits unprivileged commands per frame to 1 here
// I don't see any sense in restricting that at this moment // I don't see any sense in restricting that at this moment
// but in future we may limit this // but in future we may limit this
Cbuf_ExecuteCommandsFromBuffer( &filteredcmd_text, false, -1 );
// a1ba: there is little to no sense limit privileged commands in
// local game, as client runs server code anyway
// do this for singleplayer only though, to make it easier to catch
// possible bugs during local multiplayer testing
Cbuf_ExecuteCommandsFromBuffer( &filteredcmd_text, SV_Active() && SV_GetMaxClients() == 1, -1 );
} }
/* /*
@@ -512,14 +523,14 @@ static void Cmd_UnAlias_f ( void )
============================================================================= =============================================================================
*/ */
typedef struct cmd_s struct cmd_s
{ {
struct cmd_s *next; struct cmd_s *next;
char *name; char *name;
xcommand_t function; xcommand_t function;
int flags; int flags;
char *desc; char *desc;
} cmd_t; };
static int cmd_argc; static int cmd_argc;
static const char *cmd_args = NULL; static const char *cmd_args = NULL;
@@ -670,8 +681,7 @@ void Cmd_TokenizeString( const char *text )
Cmd_AddCommandEx Cmd_AddCommandEx
============ ============
*/ */
static int Cmd_AddCommandEx( const char *funcname, const char *cmd_name, xcommand_t function, int Cmd_AddCommandEx( const char *cmd_name, xcommand_t function, const char *cmd_desc, int iFlags, const char *funcname )
const char *cmd_desc, int iFlags )
{ {
cmd_t *cmd, *cur, *prev; cmd_t *cmd, *cur, *prev;
@@ -688,11 +698,28 @@ static int Cmd_AddCommandEx( const char *funcname, const char *cmd_name, xcomman
return 0; return 0;
} }
// fail if the command already exists // fail if the command already exists and cannot be overriden
if( Cmd_Exists( cmd_name )) cmd = Cmd_Exists( cmd_name );
if( cmd )
{ {
Con_DPrintf( S_ERROR "%s: %s already defined\n", funcname, cmd_name ); // some mods register commands that share the name with some engine's commands
return 0; // when they aren't critical to keep engine running, we can let mods to override them
// unfortunately, we lose original command this way
if( FBitSet( cmd->flags, CMD_OVERRIDABLE ))
{
Mem_Free( cmd->desc );
cmd->desc = copystring( cmd_desc );
cmd->function = function;
cmd->flags = iFlags;
Con_DPrintf( S_WARN "%s: %s already defined but is allowed to be overriden\n", funcname, cmd_name );
return 1;
}
else
{
Con_DPrintf( "%s%s: %s already defined\n", cmd->function == function ? S_WARN : S_ERROR, funcname, cmd_name );
return 0;
}
} }
// use a small malloc to avoid zone fragmentation // use a small malloc to avoid zone fragmentation
@@ -716,75 +743,6 @@ static int Cmd_AddCommandEx( const char *funcname, const char *cmd_name, xcomman
return 1; return 1;
} }
/*
============
Cmd_AddCommand
============
*/
void Cmd_AddCommand( const char *cmd_name, xcommand_t function, const char *cmd_desc )
{
Cmd_AddCommandEx( __func__, cmd_name, function, cmd_desc, 0 );
}
/*
============
Cmd_AddRestrictedCommand
============
*/
void Cmd_AddRestrictedCommand( const char *cmd_name, xcommand_t function, const char *cmd_desc )
{
Cmd_AddCommandEx( __func__, cmd_name, function, cmd_desc, CMD_PRIVILEGED );
}
/*
============
Cmd_AddServerCommand
============
*/
void GAME_EXPORT Cmd_AddServerCommand( const char *cmd_name, xcommand_t function )
{
Cmd_AddCommandEx( __func__, cmd_name, function, "server command", CMD_SERVERDLL );
}
/*
============
Cmd_AddClientCommand
============
*/
int GAME_EXPORT Cmd_AddClientCommand( const char *cmd_name, xcommand_t function )
{
int flags = CMD_CLIENTDLL;
// a1ba: try to mitigate outdated client.dll vulnerabilities
if( !Q_stricmp( cmd_name, "motd_write" ))
{
flags |= CMD_PRIVILEGED;
}
return Cmd_AddCommandEx( __func__, cmd_name, function, "client command", flags );
}
/*
============
Cmd_AddGameUICommand
============
*/
int GAME_EXPORT Cmd_AddGameUICommand( const char *cmd_name, xcommand_t function )
{
return Cmd_AddCommandEx( __func__, cmd_name, function, "gameui command", CMD_GAMEUIDLL );
}
/*
============
Cmd_AddRefCommand
============
*/
int Cmd_AddRefCommand( const char *cmd_name, xcommand_t function, const char *description )
{
return Cmd_AddCommandEx( __func__, cmd_name, function, description, CMD_REFDLL );
}
/* /*
============ ============
Cmd_RemoveCommand Cmd_RemoveCommand
@@ -853,19 +811,18 @@ void Cmd_LookupCmds( void *buffer, void *ptr, setpair_t callback )
Cmd_Exists Cmd_Exists
============ ============
*/ */
qboolean Cmd_Exists( const char *cmd_name ) cmd_t *Cmd_Exists( const char *cmd_name )
{ {
#if defined(XASH_HASHED_VARS) #if defined(XASH_HASHED_VARS)
return BaseCmd_Find( HM_CMD, cmd_name ) != NULL; return BaseCmd_Find( HM_CMD, cmd_name );
#else #else
cmd_t *cmd; cmd_t *cmd;
for( cmd = cmd_functions; cmd; cmd = cmd->next ) for( cmd = cmd_functions; cmd; cmd = cmd->next )
{ {
if( !Q_strcmp( cmd_name, cmd->name )) if( !Q_strcmp( cmd_name, cmd->name ))
return true; return cmd;
} }
return false; return NULL;
#endif #endif
} }
@@ -1058,9 +1015,10 @@ static void Cmd_ExecuteStringWithPrivilegeCheck( const char *text, qboolean isPr
if( a ) if( a )
{ {
size_t len = Q_strlen( a->value );
Cbuf_InsertTextToBuffer( Cbuf_InsertTextToBuffer(
isPrivileged ? &cmd_text : &filteredcmd_text, isPrivileged ? &cmd_text : &filteredcmd_text,
a->value ); a->value, len, len );
return; return;
} }
} }

View File

@@ -72,8 +72,6 @@ GNU General Public License for more details.
#define CVAR_GLCONFIG_DESCRIPTION "enable or disable %s" #define CVAR_GLCONFIG_DESCRIPTION "enable or disable %s"
#define DEFAULT_BSP_BUILD_ERROR "%s can't be loaded in this build. Please rebuild engine with enabled SUPPORT_BSP2_FORMAT\n"
#define DEFAULT_UPDATE_PAGE "https://github.com/FWGS/xash3d-fwgs/releases/latest" #define DEFAULT_UPDATE_PAGE "https://github.com/FWGS/xash3d-fwgs/releases/latest"
#define XASH_ENGINE_NAME "Xash3D FWGS" #define XASH_ENGINE_NAME "Xash3D FWGS"

View File

@@ -612,7 +612,7 @@ COM_Nibble
Returns the 4 bit nibble for a hex character Returns the 4 bit nibble for a hex character
================== ==================
*/ */
static byte COM_Nibble( char c ) byte COM_Nibble( char c )
{ {
if(( c >= '0' ) && ( c <= '9' )) if(( c >= '0' ) && ( c <= '9' ))
{ {

View File

@@ -362,12 +362,13 @@ typedef struct host_parm_s
extern host_parm_t host; extern host_parm_t host;
#define CMD_SERVERDLL BIT( 0 ) // added by server.dll #define CMD_SERVERDLL BIT( 0 ) // added by server.dll
#define CMD_CLIENTDLL BIT( 1 ) // added by client.dll #define CMD_CLIENTDLL BIT( 1 ) // added by client.dll
#define CMD_GAMEUIDLL BIT( 2 ) // added by GameUI.dll #define CMD_GAMEUIDLL BIT( 2 ) // added by GameUI.dll
#define CMD_PRIVILEGED BIT( 3 ) // only available in privileged mode #define CMD_PRIVILEGED BIT( 3 ) // only available in privileged mode
#define CMD_FILTERABLE BIT( 4 ) // filtered in unprivileged mode if cl_filterstuffcmd is 1 #define CMD_FILTERABLE BIT( 4 ) // filtered in unprivileged mode if cl_filterstuffcmd is 1
#define CMD_REFDLL BIT( 5 ) // added by ref.dll #define CMD_REFDLL BIT( 5 ) // added by ref.dll
#define CMD_OVERRIDABLE BIT( 6 ) // can be removed by DLLs if name matches
typedef void (*xcommand_t)( void ); typedef void (*xcommand_t)( void );
@@ -418,11 +419,13 @@ byte *FS_LoadDirectFile( const char *path, fs_offset_t *filesizeptr )
// //
// cmd.c // cmd.c
// //
typedef struct cmd_s cmd_t;
void Cbuf_Clear( void ); void Cbuf_Clear( void );
void Cbuf_AddText( const char *text ); void Cbuf_AddText( const char *text );
void Cbuf_AddTextf( const char *text, ... ) FORMAT_CHECK( 1 ); void Cbuf_AddTextf( const char *text, ... ) FORMAT_CHECK( 1 );
void Cbuf_AddFilteredText( const char *text ); void Cbuf_AddFilteredText( const char *text );
void Cbuf_InsertText( const char *text ); void Cbuf_InsertText( const char *text );
void Cbuf_InsertTextLen( const char *text, size_t len, size_t requested_len );
void Cbuf_ExecStuffCmds( void ); void Cbuf_ExecStuffCmds( void );
void Cbuf_Execute (void); void Cbuf_Execute (void);
qboolean Cmd_CurrentCommandIsPrivileged( void ); qboolean Cmd_CurrentCommandIsPrivileged( void );
@@ -431,14 +434,25 @@ const char *Cmd_Args( void ) RETURNS_NONNULL;
const char *Cmd_Argv( int arg ) RETURNS_NONNULL; const char *Cmd_Argv( int arg ) RETURNS_NONNULL;
void Cmd_Init( void ); void Cmd_Init( void );
void Cmd_Unlink( int group ); void Cmd_Unlink( int group );
void Cmd_AddCommand( const char *cmd_name, xcommand_t function, const char *cmd_desc ); int Cmd_AddCommandEx( const char *cmd_name, xcommand_t function, const char *cmd_desc, int flags, const char *funcname );
void Cmd_AddRestrictedCommand( const char *cmd_name, xcommand_t function, const char *cmd_desc );
void Cmd_AddServerCommand( const char *cmd_name, xcommand_t function ); static inline int Cmd_AddCommand( const char *cmd_name, xcommand_t function, const char *cmd_desc )
int Cmd_AddClientCommand( const char *cmd_name, xcommand_t function ); {
int Cmd_AddGameUICommand( const char *cmd_name, xcommand_t function ); return Cmd_AddCommandEx( cmd_name, function, cmd_desc, 0, __func__ );
int Cmd_AddRefCommand( const char *cmd_name, xcommand_t function, const char *description ); }
static inline int Cmd_AddRestrictedCommand( const char *cmd_name, xcommand_t function, const char *cmd_desc )
{
return Cmd_AddCommandEx( cmd_name, function, cmd_desc, CMD_PRIVILEGED, __func__ );
}
static inline int Cmd_AddCommandWithFlags( const char *cmd_name, xcommand_t function, const char *cmd_desc, int flags )
{
return Cmd_AddCommandEx( cmd_name, function, cmd_desc, flags, __func__ );
}
void Cmd_RemoveCommand( const char *cmd_name ); void Cmd_RemoveCommand( const char *cmd_name );
qboolean Cmd_Exists( const char *cmd_name ); cmd_t *Cmd_Exists( const char *cmd_name );
void Cmd_LookupCmds( void *buffer, void *ptr, setpair_t callback ); void Cmd_LookupCmds( void *buffer, void *ptr, setpair_t callback );
int Cmd_ListMaps( search_t *t , char *lastmapname, size_t len ); int Cmd_ListMaps( search_t *t , char *lastmapname, size_t len );
void Cmd_TokenizeString( const char *text ); void Cmd_TokenizeString( const char *text );
@@ -534,7 +548,7 @@ qboolean Sound_SupportedFileFormat( const char *fileext );
typedef void( *pfnChangeGame )( const char *progname ); typedef void( *pfnChangeGame )( const char *progname );
qboolean Host_IsQuakeCompatible( void ); qboolean Host_IsQuakeCompatible( void );
void EXPORT Host_Shutdown( void ); void Host_ShutdownWithReason( const char *reason );
int EXPORT Host_Main( int argc, char **argv, const char *progname, int bChangeGame, pfnChangeGame func ); int EXPORT Host_Main( int argc, char **argv, const char *progname, int bChangeGame, pfnChangeGame func );
void Host_EndGame( qboolean abort, const char *message, ... ) FORMAT_CHECK( 2 ); void Host_EndGame( qboolean abort, const char *message, ... ) FORMAT_CHECK( 2 );
void Host_AbortCurrentFrame( void ) NORETURN; void Host_AbortCurrentFrame( void ) NORETURN;
@@ -542,7 +556,6 @@ void Host_WriteServerConfig( const char *name );
void Host_WriteOpenGLConfig( void ); void Host_WriteOpenGLConfig( void );
void Host_WriteVideoConfig( void ); void Host_WriteVideoConfig( void );
void Host_WriteConfig( void ); void Host_WriteConfig( void );
void Host_ShutdownServer( void );
void Host_Error( const char *error, ... ) FORMAT_CHECK( 1 ); void Host_Error( const char *error, ... ) FORMAT_CHECK( 1 );
void Host_ValidateEngineFeatures( uint32_t mask, uint32_t features ); void Host_ValidateEngineFeatures( uint32_t mask, uint32_t features );
void Host_Frame( double time ); void Host_Frame( double time );
@@ -586,6 +599,7 @@ qboolean SV_Active( void );
*/ */
char *COM_MemFgets( byte *pMemFile, int fileSize, int *filePos, char *pBuffer, int bufferSize ); char *COM_MemFgets( byte *pMemFile, int fileSize, int *filePos, char *pBuffer, int bufferSize );
void COM_HexConvert( const char *pszInput, int nInputLength, byte *pOutput ); void COM_HexConvert( const char *pszInput, int nInputLength, byte *pOutput );
byte COM_Nibble( char c );
int COM_SaveFile( const char *filename, const void *data, int len ); int COM_SaveFile( const char *filename, const void *data, int len );
byte *COM_LoadFileForMe( const char *filename, int *pLength ) MALLOC_LIKE( free, 1 ); byte *COM_LoadFileForMe( const char *filename, int *pLength ) MALLOC_LIKE( free, 1 );
qboolean COM_IsSafeFileToDownload( const char *filename ); qboolean COM_IsSafeFileToDownload( const char *filename );

View File

@@ -1040,7 +1040,6 @@ static const autocomplete_list_t cmd_list[] =
{ "map", 1, Cmd_GetMapList }, { "map", 1, Cmd_GetMapList },
{ "cd", 1, Cmd_GetCDList }, { "cd", 1, Cmd_GetCDList },
{ "mp3", 1, Cmd_GetCDList }, { "mp3", 1, Cmd_GetCDList },
{ NULL }, // termiantor
}; };
/* /*
@@ -1073,12 +1072,12 @@ for various cmds
*/ */
static qboolean Cmd_AutocompleteName( const char *source, int arg, char *buffer, size_t bufsize ) static qboolean Cmd_AutocompleteName( const char *source, int arg, char *buffer, size_t bufsize )
{ {
const autocomplete_list_t *list; int i;
for( list = cmd_list; list->name; list++ ) for( i = 0; i < ARRAYSIZE( cmd_list ); i++ )
{ {
if( list->arg == arg && Cmd_CheckName( list->name )) if( cmd_list[i].arg == arg && Cmd_CheckName( cmd_list[i].name ))
return list->func( source, buffer, bufsize ); return cmd_list[i].func( source, buffer, bufsize );
} }
return false; return false;

View File

@@ -1,558 +0,0 @@
/*
crashhandler.c - advanced crashhandler
Copyright (C) 2016 Mittorn
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
*/
#ifndef _GNU_SOURCE
#define _GNU_SOURCE
#endif
#include "common.h"
/*
================
Sys_Crash
Crash handler, called from system
================
*/
#if XASH_WIN32
#if DBGHELP
#pragma comment( lib, "dbghelp" )
#include <winnt.h>
#include <dbghelp.h>
#include <psapi.h>
#include <time.h>
#ifndef XASH_SDL
typedef ULONG_PTR DWORD_PTR, *PDWORD_PTR;
#endif
static int Sys_ModuleName( HANDLE process, char *name, void *address, int len )
{
DWORD_PTR baseAddress = 0;
static HMODULE *moduleArray;
static unsigned int moduleCount;
LPBYTE moduleArrayBytes;
DWORD bytesRequired;
int i;
if( len < 3 )
return 0;
if( !moduleArray && EnumProcessModules( process, NULL, 0, &bytesRequired ) )
{
if ( bytesRequired )
{
moduleArrayBytes = (LPBYTE)LocalAlloc( LPTR, bytesRequired );
if( moduleArrayBytes && EnumProcessModules( process, (HMODULE *)moduleArrayBytes, bytesRequired, &bytesRequired ) )
{
moduleCount = bytesRequired / sizeof( HMODULE );
moduleArray = (HMODULE *)moduleArrayBytes;
}
}
}
for( i = 0; i < moduleCount; i++ )
{
MODULEINFO info;
GetModuleInformation( process, moduleArray[i], &info, sizeof(MODULEINFO) );
if( ( address > info.lpBaseOfDll ) &&
( (DWORD64)address < (DWORD64)info.lpBaseOfDll + (DWORD64)info.SizeOfImage ) )
return GetModuleBaseName( process, moduleArray[i], name, len );
}
return Q_snprintf( name, len, "???" );
}
static void Sys_StackTrace( PEXCEPTION_POINTERS pInfo )
{
char message[8192]; // match *nix Sys_Crash
int len = 0;
size_t i;
HANDLE process = GetCurrentProcess();
HANDLE thread = GetCurrentThread();
IMAGEHLP_LINE64 line;
DWORD dline = 0;
DWORD options;
CONTEXT context;
STACKFRAME64 stackframe;
DWORD image;
context = *pInfo->ContextRecord;
options = SymGetOptions();
options |= SYMOPT_DEBUG;
options |= SYMOPT_LOAD_LINES;
SymSetOptions( options );
SymInitialize( process, NULL, TRUE );
ZeroMemory( &stackframe, sizeof( STACKFRAME64 ));
#ifdef _M_IX86
image = IMAGE_FILE_MACHINE_I386;
stackframe.AddrPC.Offset = context.Eip;
stackframe.AddrPC.Mode = AddrModeFlat;
stackframe.AddrFrame.Offset = context.Ebp;
stackframe.AddrFrame.Mode = AddrModeFlat;
stackframe.AddrStack.Offset = context.Esp;
stackframe.AddrStack.Mode = AddrModeFlat;
#elif _M_X64
image = IMAGE_FILE_MACHINE_AMD64;
stackframe.AddrPC.Offset = context.Rip;
stackframe.AddrPC.Mode = AddrModeFlat;
stackframe.AddrFrame.Offset = context.Rsp;
stackframe.AddrFrame.Mode = AddrModeFlat;
stackframe.AddrStack.Offset = context.Rsp;
stackframe.AddrStack.Mode = AddrModeFlat;
#elif _M_IA64
image = IMAGE_FILE_MACHINE_IA64;
stackframe.AddrPC.Offset = context.StIIP;
stackframe.AddrPC.Mode = AddrModeFlat;
stackframe.AddrFrame.Offset = context.IntSp;
stackframe.AddrFrame.Mode = AddrModeFlat;
stackframe.AddrBStore.Offset = context.RsBSP;
stackframe.AddrBStore.Mode = AddrModeFlat;
stackframe.AddrStack.Offset = context.IntSp;
stackframe.AddrStack.Mode = AddrModeFlat;
#elif _M_ARM
image = IMAGE_FILE_MACHINE_ARMNT;
stackframe.AddrPC.Offset = context.Pc;
stackframe.AddrPC.Mode = AddrModeFlat;
stackframe.AddrFrame.Offset = context.R11;
stackframe.AddrFrame.Mode = AddrModeFlat;
stackframe.AddrStack.Offset = context.Sp;
stackframe.AddrStack.Mode = AddrModeFlat;
#elif _M_ARM64
image = IMAGE_FILE_MACHINE_ARM64;
stackframe.AddrPC.Offset = context.Pc;
stackframe.AddrPC.Mode = AddrModeFlat;
stackframe.AddrFrame.Offset = context.Fp;
stackframe.AddrFrame.Mode = AddrModeFlat;
stackframe.AddrStack.Offset = context.Sp;
stackframe.AddrStack.Mode = AddrModeFlat;
#elif
#error
#endif
len = Q_snprintf( message, sizeof( message ), "Ver: " XASH_ENGINE_NAME " " XASH_VERSION " (build %i-%s, %s-%s)\n",
Q_buildnum(), Q_buildcommit(), Q_buildos(), Q_buildarch() );
len += Q_snprintf( message + len, 1024 - len, "Sys_Crash: address %p, code %p\n",
pInfo->ExceptionRecord->ExceptionAddress, (void*)pInfo->ExceptionRecord->ExceptionCode );
if( SymGetLineFromAddr64( process, (DWORD64)pInfo->ExceptionRecord->ExceptionAddress, &dline, &line ) )
{
len += Q_snprintf(message + len, 1024 - len, "Exception: %s:%d:%d\n",
(char*)line.FileName, (int)line.LineNumber, (int)dline);
}
if( SymGetLineFromAddr64( process, stackframe.AddrPC.Offset, &dline, &line ) )
{
len += Q_snprintf(message + len, 1024 - len,"PC: %s:%d:%d\n",
(char*)line.FileName, (int)line.LineNumber, (int)dline);
}
if( SymGetLineFromAddr64( process, stackframe.AddrFrame.Offset, &dline, &line ) )
{
len += Q_snprintf(message + len, 1024 - len,"Frame: %s:%d:%d\n",
(char*)line.FileName, (int)line.LineNumber, (int)dline);
}
for( i = 0; i < 25; i++ )
{
char buffer[sizeof(SYMBOL_INFO) + MAX_SYM_NAME * sizeof(TCHAR)];
PSYMBOL_INFO symbol = (PSYMBOL_INFO)buffer;
BOOL result = StackWalk64(
image, process, thread,
&stackframe, &context, NULL,
SymFunctionTableAccess64, SymGetModuleBase64, NULL);
DWORD64 displacement = 0;
if( !result )
break;
symbol->SizeOfStruct = sizeof(SYMBOL_INFO);
symbol->MaxNameLen = MAX_SYM_NAME;
len += Q_snprintf( message + len, 1024 - len, "% 2d %p",
i, (void*)stackframe.AddrPC.Offset );
if( SymFromAddr( process, stackframe.AddrPC.Offset, &displacement, symbol ) )
{
len += Q_snprintf( message + len, 1024 - len, " %s ", symbol->Name );
}
if( SymGetLineFromAddr64( process, stackframe.AddrPC.Offset, &dline, &line ) )
{
len += Q_snprintf(message + len, 1024 - len,"(%s:%d:%d) ",
(char*)line.FileName, (int)line.LineNumber, (int)dline);
}
len += Q_snprintf( message + len, 1024 - len, "(");
len += Sys_ModuleName( process, message + len, (void*)stackframe.AddrPC.Offset, 1024 - len );
len += Q_snprintf( message + len, 1024 - len, ")\n");
}
#if XASH_SDL == 2
if( host.type != HOST_DEDICATED ) // let system to restart server automaticly
SDL_ShowSimpleMessageBox( SDL_MESSAGEBOX_ERROR, "Sys_Crash", message, host.hWnd );
#endif
Sys_PrintLog( message );
SymCleanup( process );
}
static void Sys_GetProcessName( char *processName, size_t bufferSize )
{
char fullpath[MAX_PATH];
GetModuleBaseName( GetCurrentProcess(), NULL, fullpath, sizeof( fullpath ) - 1 );
COM_FileBase( fullpath, processName, bufferSize );
}
static void Sys_GetMinidumpFileName( const char *processName, char *mdmpFileName, size_t bufferSize )
{
time_t currentUtcTime = time( NULL );
struct tm *currentLocalTime = localtime( &currentUtcTime );
Q_snprintf( mdmpFileName, bufferSize, "%s_%s_crash_%d%.2d%.2d_%.2d%.2d%.2d.mdmp",
processName,
Q_buildcommit(),
currentLocalTime->tm_year + 1900,
currentLocalTime->tm_mon + 1,
currentLocalTime->tm_mday,
currentLocalTime->tm_hour,
currentLocalTime->tm_min,
currentLocalTime->tm_sec);
}
static qboolean Sys_WriteMinidump(PEXCEPTION_POINTERS exceptionInfo, MINIDUMP_TYPE minidumpType)
{
HRESULT errorCode;
string processName;
string mdmpFileName;
MINIDUMP_EXCEPTION_INFORMATION minidumpInfo;
Sys_GetProcessName( processName, sizeof( processName ));
Sys_GetMinidumpFileName( processName, mdmpFileName, sizeof( mdmpFileName ));
SetLastError( NOERROR );
HANDLE fileHandle = CreateFile(
mdmpFileName, GENERIC_WRITE, FILE_SHARE_WRITE,
NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);
errorCode = HRESULT_FROM_WIN32( GetLastError( ));
if( !SUCCEEDED( errorCode )) {
CloseHandle( fileHandle );
return false;
}
minidumpInfo.ThreadId = GetCurrentThreadId();
minidumpInfo.ExceptionPointers = exceptionInfo;
minidumpInfo.ClientPointers = FALSE;
qboolean status = MiniDumpWriteDump(
GetCurrentProcess(), GetCurrentProcessId(), fileHandle,
minidumpType, &minidumpInfo, NULL, NULL);
CloseHandle( fileHandle );
return status;
}
#endif /* DBGHELP */
LPTOP_LEVEL_EXCEPTION_FILTER oldFilter;
static long _stdcall Sys_Crash( PEXCEPTION_POINTERS pInfo )
{
// save config
if( host.status != HOST_CRASHED )
{
// check to avoid recursive call
host.crashed = true;
#ifdef XASH_SDL
SDL_SetWindowGrab( host.hWnd, SDL_FALSE );
#endif // XASH_SDL
#if DBGHELP
Sys_StackTrace( pInfo );
#else
Sys_Warn( "Sys_Crash: call %p at address %p", pInfo->ExceptionRecord->ExceptionAddress, pInfo->ExceptionRecord->ExceptionCode );
#endif
if( host.type == HOST_NORMAL )
CL_Crashed(); // tell client about crash
else host.status = HOST_CRASHED;
#if DBGHELP
if( Sys_CheckParm( "-minidumps" ))
{
int minidumpFlags = (
MiniDumpWithDataSegs |
MiniDumpWithCodeSegs |
MiniDumpWithHandleData |
MiniDumpWithFullMemory |
MiniDumpWithFullMemoryInfo |
MiniDumpWithIndirectlyReferencedMemory |
MiniDumpWithThreadInfo |
MiniDumpWithModuleHeaders);
if( !Sys_WriteMinidump( pInfo, (MINIDUMP_TYPE)minidumpFlags )) {
// fallback method, create minidump with minimal info in it
Sys_WriteMinidump( pInfo, MiniDumpWithDataSegs );
}
}
#endif
if( host_developer.value <= 0 )
{
// no reason to call debugger in release build - just exit
Sys_Quit();
return EXCEPTION_CONTINUE_EXECUTION;
}
// all other states keep unchanged to let debugger find bug
Sys_DestroyConsole();
}
if( oldFilter )
return oldFilter( pInfo );
return EXCEPTION_CONTINUE_EXECUTION;
}
void Sys_SetupCrashHandler( void )
{
SetErrorMode( SEM_FAILCRITICALERRORS ); // no abort/retry/fail errors
oldFilter = SetUnhandledExceptionFilter( Sys_Crash );
}
void Sys_RestoreCrashHandler( void )
{
// restore filter
if( oldFilter ) SetUnhandledExceptionFilter( oldFilter );
}
#elif XASH_FREEBSD || XASH_NETBSD || XASH_OPENBSD || XASH_ANDROID || XASH_LINUX
// Posix signal handler
#ifndef XASH_OPENBSD
#include <ucontext.h>
#endif
#include <signal.h>
#include <sys/mman.h>
#include "library.h"
#define STACK_BACKTRACE_STR "Stack backtrace:\n"
#define STACK_DUMP_STR "Stack dump:\n"
#define STACK_BACKTRACE_STR_LEN ( sizeof( STACK_BACKTRACE_STR ) - 1 )
#define STACK_DUMP_STR_LEN ( sizeof( STACK_DUMP_STR ) - 1 )
#define ALIGN( x, y ) (((uintptr_t) ( x ) + (( y ) - 1 )) & ~(( y ) - 1 ))
static struct sigaction oldFilter;
static int Sys_PrintFrame( char *buf, int len, int i, void *addr )
{
Dl_info dlinfo;
if( len <= 0 )
return 0; // overflow
if( dladdr( addr, &dlinfo ))
{
if( dlinfo.dli_sname )
return Q_snprintf( buf, len, "%2d: %p <%s+%lu> (%s)\n", i, addr, dlinfo.dli_sname,
(unsigned long)addr - (unsigned long)dlinfo.dli_saddr, dlinfo.dli_fname ); // print symbol, module and address
return Q_snprintf( buf, len, "%2d: %p (%s)\n", i, addr, dlinfo.dli_fname ); // print module and address
}
return Q_snprintf( buf, len, "%2d: %p\n", i, addr ); // print only address
}
static void Sys_Crash( int signal, siginfo_t *si, void *context)
{
void *pc = NULL, **bp = NULL, **sp = NULL; // this must be set for every OS!
char message[8192];
int len, logfd, i = 0;
#if XASH_OPENBSD
struct sigcontext *ucontext = (struct sigcontext*)context;
#else
ucontext_t *ucontext = (ucontext_t*)context;
#endif
#if XASH_AMD64
#if XASH_FREEBSD
pc = (void*)ucontext->uc_mcontext.mc_rip;
bp = (void**)ucontext->uc_mcontext.mc_rbp;
sp = (void**)ucontext->uc_mcontext.mc_rsp;
#elif XASH_NETBSD
pc = (void*)ucontext->uc_mcontext.__gregs[_REG_RIP];
bp = (void**)ucontext->uc_mcontext.__gregs[_REG_RBP];
sp = (void**)ucontext->uc_mcontext.__gregs[_REG_RSP];
#elif XASH_OPENBSD
pc = (void*)ucontext->sc_rip;
bp = (void**)ucontext->sc_rbp;
sp = (void**)ucontext->sc_rsp;
#else
pc = (void*)ucontext->uc_mcontext.gregs[REG_RIP];
bp = (void**)ucontext->uc_mcontext.gregs[REG_RBP];
sp = (void**)ucontext->uc_mcontext.gregs[REG_RSP];
#endif
#elif XASH_X86
#if XASH_FREEBSD
pc = (void*)ucontext->uc_mcontext.mc_eip;
bp = (void**)ucontext->uc_mcontext.mc_ebp;
sp = (void**)ucontext->uc_mcontext.mc_esp;
#elif XASH_NETBSD
pc = (void*)ucontext->uc_mcontext.__gregs[_REG_EIP];
bp = (void**)ucontext->uc_mcontext.__gregs[_REG_EBP];
sp = (void**)ucontext->uc_mcontext.__gregs[_REG_ESP];
#elif XASH_OPENBSD
pc = (void*)ucontext->sc_eip;
bp = (void**)ucontext->sc_ebp;
sp = (void**)ucontext->sc_esp;
#else
pc = (void*)ucontext->uc_mcontext.gregs[REG_EIP];
bp = (void**)ucontext->uc_mcontext.gregs[REG_EBP];
sp = (void**)ucontext->uc_mcontext.gregs[REG_ESP];
#endif
#elif XASH_ARM && XASH_64BIT
pc = (void*)ucontext->uc_mcontext.pc;
bp = (void*)ucontext->uc_mcontext.regs[29];
sp = (void*)ucontext->uc_mcontext.sp;
#elif XASH_ARM
pc = (void*)ucontext->uc_mcontext.arm_pc;
bp = (void*)ucontext->uc_mcontext.arm_fp;
sp = (void*)ucontext->uc_mcontext.arm_sp;
#endif
// safe actions first, stack and memory may be corrupted
len = Q_snprintf( message, sizeof( message ), "Ver: " XASH_ENGINE_NAME " " XASH_VERSION " (build %i-%s, %s-%s)\n",
Q_buildnum(), Q_buildcommit(), Q_buildos(), Q_buildarch() );
#if !XASH_FREEBSD && !XASH_NETBSD && !XASH_OPENBSD
len += Q_snprintf( message + len, sizeof( message ) - len, "Crash: signal %d errno %d with code %d at %p %p\n", signal, si->si_errno, si->si_code, si->si_addr, si->si_ptr );
#else
len += Q_snprintf( message + len, sizeof( message ) - len, "Crash: signal %d errno %d with code %d at %p\n", signal, si->si_errno, si->si_code, si->si_addr );
#endif
write( STDERR_FILENO, message, len );
// flush buffers before writing directly to descriptors
fflush( stdout );
fflush( stderr );
// now get log fd and write trace directly to log
logfd = Sys_LogFileNo();
write( logfd, message, len );
if( pc && bp && sp )
{
size_t pagesize = sysconf( _SC_PAGESIZE );
// try to print backtrace
write( STDERR_FILENO, STACK_BACKTRACE_STR, STACK_BACKTRACE_STR_LEN );
write( logfd, STACK_BACKTRACE_STR, STACK_BACKTRACE_STR_LEN );
Q_strncpy( message + len, STACK_BACKTRACE_STR, sizeof( message ) - len );
len += STACK_BACKTRACE_STR_LEN;
// false on success, true on failure
#define try_allow_read(pointer, pagesize) \
((mprotect( (char *)ALIGN( (pointer), (pagesize) ), (pagesize), PROT_READ | PROT_WRITE | PROT_EXEC ) == -1) && \
( mprotect( (char *)ALIGN( (pointer), (pagesize) ), (pagesize), PROT_READ | PROT_EXEC ) == -1) && \
( mprotect( (char *)ALIGN( (pointer), (pagesize) ), (pagesize), PROT_READ | PROT_WRITE ) == -1) && \
( mprotect( (char *)ALIGN( (pointer), (pagesize) ), (pagesize), PROT_READ ) == -1))
do
{
int line = Sys_PrintFrame( message + len, sizeof( message ) - len, ++i, pc);
write( STDERR_FILENO, message + len, line );
write( logfd, message + len, line );
len += line;
//if( !dladdr(bp,0) ) break; // only when bp is in module
if( try_allow_read( bp, pagesize ) )
break;
if( try_allow_read( bp[0], pagesize ) )
break;
pc = bp[1];
bp = (void**)bp[0];
}
while( bp && i < 128 );
// try to print stack
write( STDERR_FILENO, STACK_DUMP_STR, STACK_DUMP_STR_LEN );
write( logfd, STACK_DUMP_STR, STACK_DUMP_STR_LEN );
Q_strncpy( message + len, STACK_DUMP_STR, sizeof( message ) - len );
len += STACK_DUMP_STR_LEN;
if( !try_allow_read( sp, pagesize ) )
{
for( i = 0; i < 32; i++ )
{
int line = Sys_PrintFrame( message + len, sizeof( message ) - len, i, sp[i] );
write( STDERR_FILENO, message + len, line );
write( logfd, message + len, line );
len += line;
}
}
#undef try_allow_read
}
// put MessageBox as Sys_Error
Msg( "%s\n", message );
#ifdef XASH_SDL
SDL_SetWindowGrab( host.hWnd, SDL_FALSE );
#endif
host.crashed = true;
Platform_MessageBox( "Xash Error", message, false );
// log saved, now we can try to save configs and close log correctly, it may crash
if( host.type == HOST_NORMAL )
CL_Crashed();
host.status = HOST_CRASHED;
Sys_Quit();
}
void Sys_SetupCrashHandler( void )
{
struct sigaction act = { 0 };
act.sa_sigaction = Sys_Crash;
act.sa_flags = SA_SIGINFO | SA_ONSTACK;
sigaction( SIGSEGV, &act, &oldFilter );
sigaction( SIGABRT, &act, &oldFilter );
sigaction( SIGBUS, &act, &oldFilter );
sigaction( SIGILL, &act, &oldFilter );
}
void Sys_RestoreCrashHandler( void )
{
sigaction( SIGSEGV, &oldFilter, NULL );
sigaction( SIGABRT, &oldFilter, NULL );
sigaction( SIGBUS, &oldFilter, NULL );
sigaction( SIGILL, &oldFilter, NULL );
}
#else
void Sys_SetupCrashHandler( void )
{
// stub
}
void Sys_RestoreCrashHandler( void )
{
// stub
}
#endif

View File

@@ -84,6 +84,11 @@ static void FS_Path_f_( void )
FS_Path_f(); FS_Path_f();
} }
static void FS_MakeGameInfo_f( void )
{
g_fsapi.MakeGameInfo();
}
static const fs_interface_t fs_memfuncs = static const fs_interface_t fs_memfuncs =
{ {
Con_Printf, Con_Printf,
@@ -272,6 +277,7 @@ void FS_Init( const char *basedir )
Cmd_AddRestrictedCommand( "fs_rescan", FS_Rescan_f, "rescan filesystem search pathes" ); Cmd_AddRestrictedCommand( "fs_rescan", FS_Rescan_f, "rescan filesystem search pathes" );
Cmd_AddRestrictedCommand( "fs_path", FS_Path_f_, "show filesystem search pathes" ); Cmd_AddRestrictedCommand( "fs_path", FS_Path_f_, "show filesystem search pathes" );
Cmd_AddRestrictedCommand( "fs_clearpaths", FS_ClearPaths_f, "clear filesystem search pathes" ); Cmd_AddRestrictedCommand( "fs_clearpaths", FS_ClearPaths_f, "clear filesystem search pathes" );
Cmd_AddRestrictedCommand( "fs_make_gameinfo", FS_MakeGameInfo_f, "create gameinfo.txt for current running game" );
if( !Sys_GetParmFromCmdLine( "-dll", host.gamedll )) if( !Sys_GetParmFromCmdLine( "-dll", host.gamedll ))
host.gamedll[0] = 0; host.gamedll[0] = 0;

View File

@@ -209,7 +209,7 @@ static void Sys_PrintUsage( const char *exename )
fprintf( stderr, usage_str, exename ); fprintf( stderr, usage_str, exename );
#endif #endif
Sys_Quit(); Sys_Quit( NULL );
} }
static void Sys_PrintBugcompUsage( const char *exename ) static void Sys_PrintBugcompUsage( const char *exename )
@@ -234,12 +234,7 @@ static void Sys_PrintBugcompUsage( const char *exename )
fprintf( stderr, usage_str, exename ); fprintf( stderr, usage_str, exename );
#endif #endif
Sys_Quit(); Sys_Quit( NULL );
}
void Host_ShutdownServer( void )
{
SV_Shutdown( "Server was killed\n" );
} }
/* /*
@@ -423,8 +418,7 @@ static void Host_Exec_f( void )
{ {
string cfgpath; string cfgpath;
byte *f; byte *f;
char *txt; fs_offset_t len;
fs_offset_t len;
if( Cmd_Argc() != 2 ) if( Cmd_Argc() != 2 )
{ {
@@ -479,20 +473,29 @@ static void Host_Exec_f( void )
return; return;
} }
// len is fs_offset_t, which can be larger than size_t
if( len >= SIZE_MAX )
{
Con_Reportf( "%s: %s is too long\n", __func__, Cmd_Argv( 1 ));
return;
}
if( !Q_stricmp( "config.cfg", cfgpath )) if( !Q_stricmp( "config.cfg", cfgpath ))
host.config_executed = true; host.config_executed = true;
// adds \n\0 at end of the file
txt = Z_Calloc( len + 2 );
memcpy( txt, f, len );
txt[len] = '\n';
txt[len + 1] = '\0';
Mem_Free( f );
if( !host.apply_game_config ) if( !host.apply_game_config )
Con_Printf( "execing %s\n", Cmd_Argv( 1 )); Con_Printf( "execing %s\n", Cmd_Argv( 1 ));
Cbuf_InsertText( txt );
Mem_Free( txt ); // adds \n at end of the file
// FS_LoadFile always null terminates
if( f[len - 1] != '\n' )
{
Cbuf_InsertTextLen( f, len, len + 1 );
Cbuf_InsertTextLen( "\n", 1, 1 );
}
else Cbuf_InsertTextLen( f, len, len );
Mem_Free( f );
} }
/* /*
@@ -595,7 +598,7 @@ static void Host_GetCommands( void )
{ {
char *cmd; char *cmd;
while( ( cmd = Sys_Input() ) ) while( ( cmd = Platform_Input() ) )
{ {
Cbuf_AddText( cmd ); Cbuf_AddText( cmd );
Cbuf_Execute(); Cbuf_Execute();
@@ -814,11 +817,11 @@ void GAME_EXPORT Host_Error( const char *error, ... )
} }
else else
{ {
Con_Printf( "%s: %s", __func__, hosterror1 );
if( host.allow_console ) if( host.allow_console )
{ {
UI_SetActiveMenu( false ); UI_SetActiveMenu( false );
Key_SetKeyDest( key_console ); Key_SetKeyDest( key_console );
Con_Printf( "%s: %s", __func__, hosterror1 );
} }
else Platform_MessageBox( "Host Error", hosterror1, true ); else Platform_MessageBox( "Host Error", hosterror1, true );
} }
@@ -841,7 +844,7 @@ void GAME_EXPORT Host_Error( const char *error, ... )
COM_InitHostState(); COM_InitHostState();
Cbuf_Clear(); Cbuf_Clear();
Host_ShutdownServer(); SV_Shutdown( "Server was killed due to an error\n" );
CL_Drop(); // drop clients CL_Drop(); // drop clients
// recreate world if needs // recreate world if needs
@@ -920,7 +923,7 @@ static void Host_RunTests( int stage )
#endif #endif
Msg( "Done! %d passed, %d failed\n", tests_stats.passed, tests_stats.failed ); Msg( "Done! %d passed, %d failed\n", tests_stats.passed, tests_stats.failed );
error_on_exit = tests_stats.failed > 0 ? EXIT_FAILURE : EXIT_SUCCESS; error_on_exit = tests_stats.failed > 0 ? EXIT_FAILURE : EXIT_SUCCESS;
Sys_Quit(); Sys_Quit( NULL );
} }
} }
#endif #endif
@@ -1167,6 +1170,11 @@ static void Host_FreeCommon( void )
FS_Shutdown(); FS_Shutdown();
} }
static void Sys_Quit_f( void )
{
Sys_Quit( "command" );
}
/* /*
================= =================
Host_Main Host_Main
@@ -1241,8 +1249,8 @@ int EXPORT Host_Main( int argc, char **argv, const char *progname, int bChangeGa
// disable texture replacements for dedicated // disable texture replacements for dedicated
Cvar_FullSet( "host_allow_materials", "0", FCVAR_READ_ONLY ); Cvar_FullSet( "host_allow_materials", "0", FCVAR_READ_ONLY );
Cmd_AddRestrictedCommand( "quit", Sys_Quit, "quit the game" ); Cmd_AddRestrictedCommand( "quit", Sys_Quit_f, "quit the game" );
Cmd_AddRestrictedCommand( "exit", Sys_Quit, "quit the game" ); Cmd_AddRestrictedCommand( "exit", Sys_Quit_f, "quit the game" );
} }
else Cmd_AddRestrictedCommand( "minimize", Host_Minimize_f, "minimize main window to tray" ); else Cmd_AddRestrictedCommand( "minimize", Host_Minimize_f, "minimize main window to tray" );
@@ -1325,20 +1333,34 @@ int EXPORT Host_Main( int argc, char **argv, const char *progname, int bChangeGa
return 0; return 0;
} }
void EXPORT Host_Shutdown( void );
void EXPORT Host_Shutdown( void )
{
Host_ShutdownWithReason( "launcher shutdown" );
}
/* /*
================= =================
Host_Shutdown Host_Shutdown
================= =================
*/ */
void EXPORT Host_Shutdown( void ) void Host_ShutdownWithReason( const char *reason )
{ {
qboolean error = host.status == HOST_ERR_FATAL; qboolean error = host.status == HOST_ERR_FATAL;
if( host.shutdown_issued ) return; if( host.shutdown_issued )
return;
host.shutdown_issued = true; host.shutdown_issued = true;
if( host.status != HOST_ERR_FATAL ) host.status = HOST_SHUTDOWN; // prepare host to normal shutdown if( reason != NULL )
if( !host.change_game ) Q_strncpy( host.finalmsg, "Server shutdown", sizeof( host.finalmsg )); Con_Printf( S_NOTE "Issuing host shutdown due to reason \"%s\"\n", reason );
if( host.status != HOST_ERR_FATAL )
host.status = HOST_SHUTDOWN; // prepare host to normal shutdown
if( !host.change_game )
Q_strncpy( host.finalmsg, "Server shutdown", sizeof( host.finalmsg ));
#if !XASH_DEDICATED #if !XASH_DEDICATED
if( host.type == HOST_NORMAL && !error ) if( host.type == HOST_NORMAL && !error )

View File

@@ -15,6 +15,7 @@ GNU General Public License for more details.
#include <math.h> #include <math.h>
#include "imagelib.h" #include "imagelib.h"
#include "eiface.h" // ARRAYSIZE
// global image variables // global image variables
imglib_t image; imglib_t image;
@@ -67,7 +68,6 @@ static const cubepack_t load_cubemap[] =
{ "3Ds Sky1", skybox_qv1 }, { "3Ds Sky1", skybox_qv1 },
{ "3Ds Sky2", skybox_qv2 }, { "3Ds Sky2", skybox_qv2 },
{ "3Ds Cube", cubemap_v1 }, { "3Ds Cube", cubemap_v1 },
{ NULL, NULL },
}; };
// soul of ImageLib - table of image format constants // soul of ImageLib - table of image format constants
@@ -297,9 +297,8 @@ rgbdata_t *FS_LoadImage( const char *filename, const byte *buffer, size_t size )
{ {
const char *ext = COM_FileExtension( filename ); const char *ext = COM_FileExtension( filename );
string loadname; string loadname;
int i; int i, j;
const loadpixformat_t *extfmt; const loadpixformat_t *extfmt;
const cubepack_t *cmap;
Q_strncpy( loadname, filename, sizeof( loadname )); Q_strncpy( loadname, filename, sizeof( loadname ));
Image_Reset(); // clear old image Image_Reset(); // clear old image
@@ -317,8 +316,10 @@ rgbdata_t *FS_LoadImage( const char *filename, const byte *buffer, size_t size )
return ImagePack(); return ImagePack();
// check all cubemap sides with package suffix // check all cubemap sides with package suffix
for( cmap = load_cubemap; cmap && cmap->type; cmap++ ) for( j = 0; j < ARRAYSIZE( load_cubemap ); j++ )
{ {
const cubepack_t *cmap = &load_cubemap[j];
for( i = 0; i < 6; i++ ) for( i = 0; i < 6; i++ )
{ {
if( Image_ProbeLoad( extfmt, loadname, cmap->type[i].suf, cmap->type[i].hint )) if( Image_ProbeLoad( extfmt, loadname, cmap->type[i].suf, cmap->type[i].hint ))

View File

@@ -37,11 +37,7 @@ static char **szArgv;
static void Sys_ChangeGame( const char *progname ) static void Sys_ChangeGame( const char *progname )
{ {
// a1ba: may never be called within engine // stub
// if platform supports execv() function
Q_strncpy( szGameDir, progname, sizeof( szGameDir ));
Host_Shutdown( );
exit( Host_Main( szArgc, szArgv, szGameDir, 1, &Sys_ChangeGame ) );
} }
static int Sys_Start( void ) static int Sys_Start( void )

View File

@@ -385,10 +385,7 @@ void NET_SaveMasters( void )
master_t *m; master_t *m;
if( !ml.modified ) if( !ml.modified )
{
Con_Reportf( "Master server list not changed\n" );
return; return;
}
f = FS_Open( "xashcomm.lst", "w", true ); f = FS_Open( "xashcomm.lst", "w", true );

View File

@@ -385,6 +385,35 @@ static const mlumpinfo_t extlumps[EXTRA_LUMPS] =
}, },
}; };
#define BOX_CLIPNODES_INITIALIZER \
{ \
.planenum = 0, \
.children = { CONTENTS_EMPTY, 1 }, \
}, \
{ \
.planenum = 1, \
.children = { 2, CONTENTS_EMPTY }, \
}, \
{ \
.planenum = 2, \
.children = { CONTENTS_EMPTY, 3 }, \
}, \
{ \
.planenum = 3, \
.children = { 4, CONTENTS_EMPTY }, \
}, \
{ \
.planenum = 4, \
.children = { CONTENTS_EMPTY, 5 }, \
}, \
{ \
.planenum = 5, \
.children = { CONTENTS_SOLID, CONTENTS_EMPTY }, \
}, \
const mclipnode16_t box_clipnodes16[6] = { BOX_CLIPNODES_INITIALIZER };
const mclipnode32_t box_clipnodes32[6] = { BOX_CLIPNODES_INITIALIZER };
/* /*
=============================================================================== ===============================================================================
@@ -856,7 +885,7 @@ Mod_PointInLeaf
================== ==================
*/ */
mleaf_t *Mod_PointInLeaf( const vec3_t p, mnode_t *node ) mleaf_t *Mod_PointInLeaf( const vec3_t p, mnode_t *node, model_t *mod )
{ {
Assert( node != NULL ); Assert( node != NULL );
@@ -864,7 +893,7 @@ mleaf_t *Mod_PointInLeaf( const vec3_t p, mnode_t *node )
{ {
if( node->contents < 0 ) if( node->contents < 0 )
return (mleaf_t *)node; return (mleaf_t *)node;
node = node->children[PlaneDiff( p, node->plane ) <= 0]; node = node_child( node, PlaneDiff( p, node->plane ) <= 0, mod );
} }
// never reached // never reached
@@ -885,7 +914,7 @@ byte *Mod_GetPVSForPoint( const vec3_t p )
ASSERT( worldmodel != NULL ); ASSERT( worldmodel != NULL );
leaf = Mod_PointInLeaf( p, worldmodel->nodes ); leaf = Mod_PointInLeaf( p, worldmodel->nodes, worldmodel );
if( leaf && leaf->cluster >= 0 ) if( leaf && leaf->cluster >= 0 )
return Mod_DecompressPVS( leaf->compressed_vis, world.visbytes ); return Mod_DecompressPVS( leaf->compressed_vis, world.visbytes );
@@ -905,14 +934,14 @@ static void Mod_FatPVS_RecursiveBSPNode( const vec3_t org, float radius, byte *v
float d = PlaneDiff( org, node->plane ); float d = PlaneDiff( org, node->plane );
if( d > radius ) if( d > radius )
node = node->children[0]; node = node_child( node, 0, worldmodel );
else if( d < -radius ) else if( d < -radius )
node = node->children[1]; node = node_child( node, 1, worldmodel );
else else
{ {
// go down both sides // go down both sides
Mod_FatPVS_RecursiveBSPNode( org, radius, visbuffer, visbytes, node->children[0], phs ); Mod_FatPVS_RecursiveBSPNode( org, radius, visbuffer, visbytes, node_child( node, 0, worldmodel ), phs );
node = node->children[1]; node = node_child( node, 1, worldmodel );
} }
} }
@@ -950,7 +979,7 @@ int Mod_FatPVS( const vec3_t org, float radius, byte *visbuffer, int visbytes, q
ASSERT( worldmodel != NULL ); ASSERT( worldmodel != NULL );
leaf = Mod_PointInLeaf( org, worldmodel->nodes ); leaf = Mod_PointInLeaf( org, worldmodel->nodes, worldmodel );
bytes = Q_min( bytes, visbytes ); bytes = Q_min( bytes, visbytes );
// enable full visibility for some reasons // enable full visibility for some reasons
@@ -1009,20 +1038,16 @@ static void Mod_BoxLeafnums_r( leaflist_t *ll, mnode_t *node )
sides = BOX_ON_PLANE_SIDE( ll->mins, ll->maxs, node->plane ); sides = BOX_ON_PLANE_SIDE( ll->mins, ll->maxs, node->plane );
if( sides == 1 ) if( sides == 1 )
{ node = node_child( node, 0, worldmodel );
node = node->children[0];
}
else if( sides == 2 ) else if( sides == 2 )
{ node = node_child( node, 1, worldmodel );
node = node->children[1];
}
else else
{ {
// go down both // go down both
if( ll->topnode == -1 ) if( ll->topnode == -1 )
ll->topnode = node - worldmodel->nodes; ll->topnode = node - worldmodel->nodes;
Mod_BoxLeafnums_r( ll, node->children[0] ); Mod_BoxLeafnums_r( ll, node_child( node, 0, worldmodel ));
node = node->children[1]; node = node_child( node, 1, worldmodel );
} }
} }
} }
@@ -1079,35 +1104,6 @@ qboolean Mod_BoxVisible( const vec3_t mins, const vec3_t maxs, const byte *visbi
return false; return false;
} }
/*
=============
Mod_HeadnodeVisible
=============
*/
qboolean Mod_HeadnodeVisible( mnode_t *node, const byte *visbits, int *lastleaf )
{
if( !node || node->contents == CONTENTS_SOLID )
return false;
if( node->contents < 0 )
{
if( !CHECKVISBIT( visbits, ((mleaf_t *)node)->cluster ))
return false;
if( lastleaf )
*lastleaf = ((mleaf_t *)node)->cluster;
return true;
}
if( Mod_HeadnodeVisible( node->children[0], visbits, lastleaf ))
return true;
if( Mod_HeadnodeVisible( node->children[1], visbits, lastleaf ))
return true;
return false;
}
/* /*
================= =================
Mod_FindModelOrigin Mod_FindModelOrigin
@@ -1264,22 +1260,35 @@ Mod_GetFaceContents
determine face contents by name determine face contents by name
================== ==================
*/ */
static mvertex_t *Mod_GetVertexByNumber( model_t *mod, int surfedge ) static mvertex_t *Mod_GetVertexByNumber( model_t *mod, int surfedge, const dbspmodel_t *bmod )
{ {
int lindex; int lindex = mod->surfedges[surfedge];
medge_t *edge;
lindex = mod->surfedges[surfedge]; if( bmod->version == QBSP2_VERSION )
if( lindex > 0 )
{ {
edge = &mod->edges[lindex]; if( lindex > 0 )
return &mod->vertexes[edge->v[0]]; {
medge32_t *edge = &mod->edges32[lindex];
return &mod->vertexes[edge->v[0]];
}
else
{
medge32_t *edge = &mod->edges32[-lindex];
return &mod->vertexes[edge->v[1]];
}
} }
else else
{ {
edge = &mod->edges[-lindex]; if( lindex > 0 )
return &mod->vertexes[edge->v[1]]; {
medge16_t *edge = &mod->edges16[lindex];
return &mod->vertexes[edge->v[0]];
}
else
{
medge16_t *edge = &mod->edges16[-lindex];
return &mod->vertexes[edge->v[1]];
}
} }
} }
@@ -1364,7 +1373,7 @@ Mod_CalcSurfaceExtents
Fills in surf->texturemins[] and surf->extents[] Fills in surf->texturemins[] and surf->extents[]
================= =================
*/ */
static void Mod_CalcSurfaceExtents( model_t *mod, msurface_t *surf ) static void Mod_CalcSurfaceExtents( model_t *mod, msurface_t *surf, const dbspmodel_t *bmod )
{ {
// this place is VERY critical to precision // this place is VERY critical to precision
// keep it as float, don't use double, because it causes issues with lightmap // keep it as float, don't use double, because it causes issues with lightmap
@@ -1391,8 +1400,16 @@ static void Mod_CalcSurfaceExtents( model_t *mod, msurface_t *surf )
if( e >= mod->numedges || e <= -mod->numedges ) if( e >= mod->numedges || e <= -mod->numedges )
Host_Error( "%s: bad edge\n", __func__ ); Host_Error( "%s: bad edge\n", __func__ );
if( e >= 0 ) v = &mod->vertexes[mod->edges[e].v[0]]; if( bmod->version == QBSP2_VERSION )
else v = &mod->vertexes[mod->edges[-e].v[1]]; {
if( e >= 0 ) v = &mod->vertexes[mod->edges32[e].v[0]];
else v = &mod->vertexes[mod->edges32[-e].v[1]];
}
else
{
if( e >= 0 ) v = &mod->vertexes[mod->edges16[e].v[0]];
else v = &mod->vertexes[mod->edges16[-e].v[1]];
}
for( j = 0; j < 2; j++ ) for( j = 0; j < 2; j++ )
{ {
@@ -1446,7 +1463,7 @@ Mod_CalcSurfaceBounds
fills in surf->mins and surf->maxs fills in surf->mins and surf->maxs
================= =================
*/ */
static void Mod_CalcSurfaceBounds( model_t *mod, msurface_t *surf ) static void Mod_CalcSurfaceBounds( model_t *mod, msurface_t *surf, const dbspmodel_t *bmod )
{ {
int i, e; int i, e;
mvertex_t *v; mvertex_t *v;
@@ -1460,8 +1477,16 @@ static void Mod_CalcSurfaceBounds( model_t *mod, msurface_t *surf )
if( e >= mod->numedges || e <= -mod->numedges ) if( e >= mod->numedges || e <= -mod->numedges )
Host_Error( "%s: bad edge\n", __func__ ); Host_Error( "%s: bad edge\n", __func__ );
if( e >= 0 ) v = &mod->vertexes[mod->edges[e].v[0]]; if( bmod->version == QBSP2_VERSION )
else v = &mod->vertexes[mod->edges[-e].v[1]]; {
if( e >= 0 ) v = &mod->vertexes[mod->edges32[e].v[0]];
else v = &mod->vertexes[mod->edges32[-e].v[1]];
}
else
{
if( e >= 0 ) v = &mod->vertexes[mod->edges16[e].v[0]];
else v = &mod->vertexes[mod->edges16[-e].v[1]];
}
AddPointToBounds( v->position, surf->info->mins, surf->info->maxs ); AddPointToBounds( v->position, surf->info->mins, surf->info->maxs );
} }
@@ -1473,7 +1498,7 @@ static void Mod_CalcSurfaceBounds( model_t *mod, msurface_t *surf )
Mod_CreateFaceBevels Mod_CreateFaceBevels
================= =================
*/ */
static void Mod_CreateFaceBevels( model_t *mod, msurface_t *surf ) static void Mod_CreateFaceBevels( model_t *mod, msurface_t *surf, const dbspmodel_t *bmod )
{ {
vec3_t delta, edgevec; vec3_t delta, edgevec;
byte *facebevel; byte *facebevel;
@@ -1506,8 +1531,8 @@ static void Mod_CreateFaceBevels( model_t *mod, msurface_t *surf )
{ {
mplane_t *dest = &fb->edges[i]; mplane_t *dest = &fb->edges[i];
v0 = Mod_GetVertexByNumber( mod, surf->firstedge + i ); v0 = Mod_GetVertexByNumber( mod, surf->firstedge + i, bmod );
v1 = Mod_GetVertexByNumber( mod, surf->firstedge + (i + 1) % surf->numedges ); v1 = Mod_GetVertexByNumber( mod, surf->firstedge + (i + 1) % surf->numedges, bmod );
VectorSubtract( v1->position, v0->position, edgevec ); VectorSubtract( v1->position, v0->position, edgevec );
CrossProduct( faceNormal, edgevec, dest->normal ); CrossProduct( faceNormal, edgevec, dest->normal );
VectorNormalize( dest->normal ); VectorNormalize( dest->normal );
@@ -1521,7 +1546,7 @@ static void Mod_CreateFaceBevels( model_t *mod, msurface_t *surf )
// compute face radius // compute face radius
for( i = 0; i < surf->numedges; i++ ) for( i = 0; i < surf->numedges; i++ )
{ {
v0 = Mod_GetVertexByNumber( mod, surf->firstedge + i ); v0 = Mod_GetVertexByNumber( mod, surf->firstedge + i, bmod );
VectorSubtract( v0->position, fb->origin, delta ); VectorSubtract( v0->position, fb->origin, delta );
radius = DotProduct( delta, delta ); radius = DotProduct( delta, delta );
fb->radius = Q_max( radius, fb->radius ); fb->radius = Q_max( radius, fb->radius );
@@ -1533,13 +1558,15 @@ static void Mod_CreateFaceBevels( model_t *mod, msurface_t *surf )
Mod_SetParent Mod_SetParent
================= =================
*/ */
static void Mod_SetParent( mnode_t *node, mnode_t *parent ) static void Mod_SetParent( model_t *mod, mnode_t *node, mnode_t *parent )
{ {
node->parent = parent; node->parent = parent;
if( node->contents < 0 ) return; // it's leaf if( node->contents < 0 )
Mod_SetParent( node->children[0], node ); return; // it's leaf
Mod_SetParent( node->children[1], node );
Mod_SetParent( mod, node_child( node, 0, mod ), node );
Mod_SetParent( mod, node_child( node, 1, mod ), node );
} }
/* /*
@@ -1547,7 +1574,7 @@ static void Mod_SetParent( mnode_t *node, mnode_t *parent )
CountClipNodes_r CountClipNodes_r
================== ==================
*/ */
static void CountClipNodes_r( mclipnode_t *src, hull_t *hull, int nodenum ) static void CountClipNodes16_r( mclipnode16_t *src, hull_t *hull, int nodenum )
{ {
// leaf? // leaf?
if( nodenum < 0 ) return; if( nodenum < 0 ) return;
@@ -1556,16 +1583,11 @@ static void CountClipNodes_r( mclipnode_t *src, hull_t *hull, int nodenum )
Host_Error( "MAX_MAP_CLIPNODES limit exceeded\n" ); Host_Error( "MAX_MAP_CLIPNODES limit exceeded\n" );
hull->lastclipnode++; hull->lastclipnode++;
CountClipNodes_r( src, hull, src[nodenum].children[0] ); CountClipNodes16_r( src, hull, src[nodenum].children[0] );
CountClipNodes_r( src, hull, src[nodenum].children[1] ); CountClipNodes16_r( src, hull, src[nodenum].children[1] );
} }
/* static void CountClipNodes32_r( mclipnode32_t *src, hull_t *hull, int nodenum )
==================
CountClipNodes32_r
==================
*/
static void CountClipNodes32_r( dclipnode32_t *src, hull_t *hull, int nodenum )
{ {
// leaf? // leaf?
if( nodenum < 0 ) return; if( nodenum < 0 ) return;
@@ -1578,15 +1600,27 @@ static void CountClipNodes32_r( dclipnode32_t *src, hull_t *hull, int nodenum )
CountClipNodes32_r( src, hull, src[nodenum].children[1] ); CountClipNodes32_r( src, hull, src[nodenum].children[1] );
} }
static void CountDClipNodes_r( dclipnode32_t *src, hull_t *hull, int nodenum )
{
// leaf?
if( nodenum < 0 ) return;
if( hull->lastclipnode == MAX_MAP_CLIPNODES )
Host_Error( "MAX_MAP_CLIPNODES limit exceeded\n" );
hull->lastclipnode++;
CountDClipNodes_r( src, hull, src[nodenum].children[0] );
CountDClipNodes_r( src, hull, src[nodenum].children[1] );
}
/* /*
================== ==================
RemapClipNodes_r RemapClipNodes_r
================== ==================
*/ */
static int RemapClipNodes_r( dclipnode32_t *srcnodes, hull_t *hull, int nodenum ) static int RemapClipNodes_r( dbspmodel_t *bmod, dclipnode32_t *srcnodes, hull_t *hull, int nodenum )
{ {
dclipnode32_t *src; dclipnode32_t *src;
mclipnode_t *out;
int i, c; int i, c;
// leaf? // leaf?
@@ -1599,13 +1633,22 @@ static int RemapClipNodes_r( dclipnode32_t *srcnodes, hull_t *hull, int nodenum
src = srcnodes + nodenum; src = srcnodes + nodenum;
c = hull->lastclipnode; c = hull->lastclipnode;
out = &hull->clipnodes[c];
hull->lastclipnode++; hull->lastclipnode++;
out->planenum = src->planenum; if( bmod->version == QBSP2_VERSION )
{
for( i = 0; i < 2; i++ ) mclipnode32_t *out = &hull->clipnodes32[c];
out->children[i] = RemapClipNodes_r( srcnodes, hull, src->children[i] ); out->planenum = src->planenum;
for( i = 0; i < 2; i++ )
out->children[i] = RemapClipNodes_r( bmod, srcnodes, hull, src->children[i] );
}
else
{
mclipnode16_t *out = &hull->clipnodes16[c];
out->planenum = src->planenum;
for( i = 0; i < 2; i++ )
out->children[i] = RemapClipNodes_r( bmod, srcnodes, hull, src->children[i] );
}
return c; return c;
} }
@@ -1617,34 +1660,64 @@ Mod_MakeHull0
Duplicate the drawing hull structure as a clipping hull Duplicate the drawing hull structure as a clipping hull
================= =================
*/ */
static void Mod_MakeHull0( model_t *mod ) static void Mod_MakeHull0( model_t *mod, const dbspmodel_t *bmod )
{ {
mnode_t *in, *child; hull_t *hull = &mod->hulls[0];
mclipnode_t *out; int i;
hull_t *hull;
int i, j;
hull = &mod->hulls[0];
hull->clipnodes = out = Mem_Malloc( mod->mempool, mod->numnodes * sizeof( *out ));
in = mod->nodes;
hull->firstclipnode = 0; hull->firstclipnode = 0;
hull->lastclipnode = mod->numnodes - 1; hull->lastclipnode = mod->numnodes - 1;
hull->planes = mod->planes; hull->planes = mod->planes;
for( i = 0; i < mod->numnodes; i++, out++, in++ ) if( bmod->version == QBSP2_VERSION )
{ {
out->planenum = in->plane - mod->planes; mclipnode32_t *out;
mnode_t *in = mod->nodes;
for( j = 0; j < 2; j++ ) hull->clipnodes32 = out = Mem_Malloc( mod->mempool, mod->numnodes * sizeof( *hull->clipnodes32 ));
for( i = 0; i < mod->numnodes; i++, out++, in++ )
{ {
child = in->children[j]; int j;
if( child->contents < 0 ) out->planenum = in->plane - mod->planes;
out->children[j] = child->contents;
else out->children[j] = child - mod->nodes; for( j = 0; j < 2; j++ )
{
mnode_t *child = node_child( in, j, mod );
if( child->contents < 0 )
out->children[j] = child->contents;
else
out->children[j] = child - mod->nodes;
}
} }
} }
else
{
mclipnode16_t *out;
mnode_t *in = mod->nodes;
hull->clipnodes16 = out = Mem_Malloc( mod->mempool, mod->numnodes * sizeof( *hull->clipnodes16 ));
for( i = 0; i < mod->numnodes; i++, out++, in++ )
{
int j;
out->planenum = in->plane - mod->planes;
for( j = 0; j < 2; j++ )
{
mnode_t *child = node_child( in, j, mod );
if( child->contents < 0 )
out->children[j] = child->contents;
else
out->children[j] = child - mod->nodes;
}
}
}
} }
/* /*
@@ -1688,15 +1761,18 @@ static void Mod_SetupHull( dbspmodel_t *bmod, model_t *mod, poolhandle_t mempool
if( VectorIsNull( hull->clip_mins ) && VectorIsNull( hull->clip_maxs )) if( VectorIsNull( hull->clip_mins ) && VectorIsNull( hull->clip_maxs ))
return; // no hull specified return; // no hull specified
CountClipNodes32_r( bmod->clipnodes_out, hull, headnode ); CountDClipNodes_r( bmod->clipnodes_out, hull, headnode );
// fit array to real count // fit array to real count
hull->clipnodes = (mclipnode_t *)Mem_Malloc( mempool, sizeof( mclipnode_t ) * hull->lastclipnode ); if( bmod->version == QBSP2_VERSION )
hull->clipnodes32 = Mem_Malloc( mempool, sizeof( *hull->clipnodes32 ) * hull->lastclipnode );
else
hull->clipnodes16 = Mem_Malloc( mempool, sizeof( *hull->clipnodes16 ) * hull->lastclipnode );
hull->planes = mod->planes; // share planes hull->planes = mod->planes; // share planes
hull->lastclipnode = 0; // restart counting hull->lastclipnode = 0; // restart counting
// remap clipnodes to 16-bit indexes RemapClipNodes_r( bmod, bmod->clipnodes_out, hull, headnode ); // remap clipnodes to 16-bit indexes
RemapClipNodes_r( bmod->clipnodes_out, hull, headnode );
} }
static qboolean Mod_LoadLitfile( model_t *mod, const char *ext, size_t expected_size, color24 **out, size_t *outsize ) static qboolean Mod_LoadLitfile( model_t *mod, const char *ext, size_t expected_size, color24 **out, size_t *outsize )
@@ -1704,7 +1780,8 @@ static qboolean Mod_LoadLitfile( model_t *mod, const char *ext, size_t expected_
char modelname[64], path[64]; char modelname[64], path[64];
int iCompare; int iCompare;
fs_offset_t datasize; fs_offset_t datasize;
byte *in; file_t *f;
uint hdr[2];
COM_FileBase( mod->name, modelname, sizeof( modelname )); COM_FileBase( mod->name, modelname, sizeof( modelname ));
Q_snprintf( path, sizeof( path ), "maps/%s.%s", modelname, ext ); Q_snprintf( path, sizeof( path ), "maps/%s.%s", modelname, ext );
@@ -1715,31 +1792,15 @@ static qboolean Mod_LoadLitfile( model_t *mod, const char *ext, size_t expected_
if( iCompare < 0 ) // this may happens if level-designer used -onlyents key for hlcsg if( iCompare < 0 ) // this may happens if level-designer used -onlyents key for hlcsg
Con_Printf( S_WARN "%s probably is out of date\n", path ); Con_Printf( S_WARN "%s probably is out of date\n", path );
in = FS_LoadFile( path, &datasize, false ); f = FS_Open( path, "rb", false );
if( !in ) if( !f )
{ {
Con_Printf( S_ERROR "couldn't load %s\n", path ); Con_Printf( S_ERROR "couldn't load %s\n", path );
return false; return false;
} }
if( datasize <= 8 ) // header + version datasize = FS_FileLength( f );
{
Con_Printf( S_ERROR "%s is too short\n", path );
goto cleanup_and_error;
}
if( LittleLong( ((uint *)in)[0] ) != IDDELUXEMAPHEADER )
{
Con_Printf( S_ERROR "%s is corrupted\n", path );
goto cleanup_and_error;
}
if( LittleLong( ((uint *)in)[1] ) != DELUXEMAP_VERSION )
{
Con_Printf( S_ERROR "has %s mismatched version (%u should be %u)\n", path, LittleLong( ((uint *)in)[1] ), DELUXEMAP_VERSION );
goto cleanup_and_error;
}
// skip header bytes // skip header bytes
datasize -= 8; datasize -= 8;
@@ -1750,14 +1811,33 @@ static qboolean Mod_LoadLitfile( model_t *mod, const char *ext, size_t expected_
goto cleanup_and_error; goto cleanup_and_error;
} }
if( FS_Read( f, hdr, sizeof( hdr )) != sizeof( hdr ))
{
Con_Printf( S_ERROR "failed reading header from %s\n", path );
goto cleanup_and_error;
}
if( LittleLong( hdr[0] ) != IDDELUXEMAPHEADER )
{
Con_Printf( S_ERROR "%s is corrupted\n", path );
goto cleanup_and_error;
}
if( LittleLong( hdr[1] ) != DELUXEMAP_VERSION )
{
Con_Printf( S_ERROR "has %s mismatched version (%u should be %u)\n", path, LittleLong( hdr[1] ), DELUXEMAP_VERSION );
goto cleanup_and_error;
}
*out = Mem_Malloc( mod->mempool, datasize ); *out = Mem_Malloc( mod->mempool, datasize );
memcpy( *out, in + 8, datasize );
*outsize = datasize; *outsize = datasize;
Mem_Free( in );
FS_Read( f, *out, datasize );
FS_Close( f );
return true; return true;
cleanup_and_error: cleanup_and_error:
Mem_Free( in ); FS_Close( f );
return false; return false;
} }
@@ -1772,6 +1852,7 @@ for embedded submodels
static void Mod_SetupSubmodels( model_t *mod, dbspmodel_t *bmod ) static void Mod_SetupSubmodels( model_t *mod, dbspmodel_t *bmod )
{ {
qboolean colored = false; qboolean colored = false;
qboolean qbsp2 = false;
poolhandle_t mempool; poolhandle_t mempool;
char *ents; char *ents;
dmodel_t *bm; dmodel_t *bm;
@@ -1783,6 +1864,9 @@ static void Mod_SetupSubmodels( model_t *mod, dbspmodel_t *bmod )
if( FBitSet( mod->flags, MODEL_COLORED_LIGHTING )) if( FBitSet( mod->flags, MODEL_COLORED_LIGHTING ))
colored = true; colored = true;
if( FBitSet( mod->flags, MODEL_QBSP2 ))
qbsp2 = true;
mod->numframes = 2; // regular and alternate animation mod->numframes = 2; // regular and alternate animation
// set up the submodels // set up the submodels
@@ -1795,7 +1879,10 @@ static void Mod_SetupSubmodels( model_t *mod, dbspmodel_t *bmod )
mod->hulls[0].lastclipnode = bm->headnode[0]; // need to be real count mod->hulls[0].lastclipnode = bm->headnode[0]; // need to be real count
// counting a real number of clipnodes per each submodel // counting a real number of clipnodes per each submodel
CountClipNodes_r( mod->hulls[0].clipnodes, &mod->hulls[0], bm->headnode[0] ); if( bmod->version == QBSP2_VERSION )
CountClipNodes32_r( mod->hulls[0].clipnodes32, &mod->hulls[0], bm->headnode[0] );
else
CountClipNodes16_r( mod->hulls[0].clipnodes16, &mod->hulls[0], bm->headnode[0] );
// but hulls1-3 is build individually for a each given submodel // but hulls1-3 is build individually for a each given submodel
for( j = 1; j < MAX_MAP_HULLS; j++ ) for( j = 1; j < MAX_MAP_HULLS; j++ )
@@ -1813,6 +1900,7 @@ static void Mod_SetupSubmodels( model_t *mod, dbspmodel_t *bmod )
// this bit will be shared between all the submodels include worldmodel // this bit will be shared between all the submodels include worldmodel
if( colored ) SetBits( mod->flags, MODEL_COLORED_LIGHTING ); if( colored ) SetBits( mod->flags, MODEL_COLORED_LIGHTING );
if( qbsp2 ) SetBits( mod->flags, MODEL_QBSP2 );
if( i != 0 ) if( i != 0 )
{ {
@@ -2135,15 +2223,15 @@ Mod_LoadEdges
*/ */
static void Mod_LoadEdges( model_t *mod, dbspmodel_t *bmod ) static void Mod_LoadEdges( model_t *mod, dbspmodel_t *bmod )
{ {
medge_t *out;
int i; int i;
mod->edges = out = Mem_Malloc( mod->mempool, bmod->numedges * sizeof( medge_t ));
mod->numedges = bmod->numedges; mod->numedges = bmod->numedges;
if( bmod->version == QBSP2_VERSION ) if( bmod->version == QBSP2_VERSION )
{ {
dedge32_t *in = (dedge32_t *)bmod->edges32; dedge32_t *in = bmod->edges32;
medge32_t *out;
mod->edges32 = out = Mem_Malloc( mod->mempool, bmod->numedges * sizeof( *out ));
for( i = 0; i < bmod->numedges; i++, in++, out++ ) for( i = 0; i < bmod->numedges; i++, in++, out++ )
{ {
@@ -2153,7 +2241,9 @@ static void Mod_LoadEdges( model_t *mod, dbspmodel_t *bmod )
} }
else else
{ {
dedge_t *in = (dedge_t *)bmod->edges; dedge_t *in = bmod->edges;
medge16_t *out;
mod->edges16 = out = Mem_Malloc( mod->mempool, bmod->numedges * sizeof( *out ));
for( i = 0; i < bmod->numedges; i++, in++, out++ ) for( i = 0; i < bmod->numedges; i++, in++, out++ )
{ {
@@ -2913,9 +3003,9 @@ static void Mod_LoadSurfaces( model_t *mod, dbspmodel_t *bmod )
if( FBitSet( out->texinfo->flags, TEX_SPECIAL )) if( FBitSet( out->texinfo->flags, TEX_SPECIAL ))
SetBits( out->flags, SURF_DRAWTILED ); SetBits( out->flags, SURF_DRAWTILED );
Mod_CalcSurfaceBounds( mod, out ); Mod_CalcSurfaceBounds( mod, out, bmod );
Mod_CalcSurfaceExtents( mod, out ); Mod_CalcSurfaceExtents( mod, out, bmod );
Mod_CreateFaceBevels( mod, out ); Mod_CreateFaceBevels( mod, out, bmod );
// grab the second sample to detect colored lighting // grab the second sample to detect colored lighting
if( test_lightsize > 0 && lightofs != -1 ) if( test_lightsize > 0 && lightofs != -1 )
@@ -2962,7 +3052,6 @@ static void Mod_LoadSurfaces( model_t *mod, dbspmodel_t *bmod )
if( samples == 1 || samples == 3 ) if( samples == 1 || samples == 3 )
{ {
bmod->lightmap_samples = (int)samples; bmod->lightmap_samples = (int)samples;
Con_Reportf( "lighting: %s\n", (bmod->lightmap_samples == 1) ? "monochrome" : "colored" );
bmod->lightmap_samples = Q_max( bmod->lightmap_samples, 1 ); // avoid division by zero bmod->lightmap_samples = Q_max( bmod->lightmap_samples, 1 ); // avoid division by zero
} }
else Con_DPrintf( S_WARN "lighting invalid samplecount: %g, defaulting to %i\n", samples, bmod->lightmap_samples ); else Con_DPrintf( S_WARN "lighting invalid samplecount: %g, defaulting to %i\n", samples, bmod->lightmap_samples );
@@ -2994,16 +3083,62 @@ static void Mod_LoadNodes( model_t *mod, dbspmodel_t *bmod )
out->minmaxs[j+3] = in->maxs[j]; out->minmaxs[j+3] = in->maxs[j];
} }
#if !XASH_64BIT
if( in->firstface >= BIT( 24 ))
{
Host_Error( "%s: face index limit exceeded on node %i\n", __func__, i );
return;
}
if( in->numfaces >= BIT( 24 ))
{
Host_Error( "%s: face count limit exceeded on node %i\n", __func__, i );
return;
}
#endif
p = in->planenum; p = in->planenum;
out->plane = mod->planes + p; out->plane = mod->planes + p;
out->firstsurface = in->firstface; out->firstsurface_0 = in->firstface & 0xFFFF;
out->numsurfaces = in->numfaces; out->numsurfaces_0 = in->numfaces & 0xFFFF;
out->firstsurface_1 = in->firstface >> 16;
out->numsurfaces_1 = in->numfaces >> 16;
for( j = 0; j < 2; j++ ) for( j = 0; j < 2; j++ )
{ {
p = in->children[j]; p = in->children[j];
if( p >= 0 ) out->children[j] = mod->nodes + p; #if XASH_64BIT
else out->children[j] = (mnode_t *)(mod->leafs + ( -1 - p )); if( p >= 0 ) out->children_[j] = mod->nodes + p;
else out->children_[j] = (mnode_t *)(mod->leafs + ( -1 - p ));
#else
if( j == 0 )
{
if( p >= 0 )
{
out->child_0_leaf = 0;
out->child_0_off = p;
}
else
{
out->child_0_leaf = 1;
out->child_0_off = -1 - p;
}
}
else
{
if( p >= 0 )
{
out->child_1_leaf = 0;
out->child_1_off = p;
}
else
{
out->child_1_leaf = 1;
out->child_1_off = -1 - p;
}
}
#endif
} }
} }
else else
@@ -3018,20 +3153,20 @@ static void Mod_LoadNodes( model_t *mod, dbspmodel_t *bmod )
p = in->planenum; p = in->planenum;
out->plane = mod->planes + p; out->plane = mod->planes + p;
out->firstsurface = in->firstface; out->firstsurface_0 = in->firstface;
out->numsurfaces = in->numfaces; out->numsurfaces_0 = in->numfaces;
for( j = 0; j < 2; j++ ) for( j = 0; j < 2; j++ )
{ {
p = in->children[j]; p = in->children[j];
if( p >= 0 ) out->children[j] = mod->nodes + p; if( p >= 0 ) out->children_[j] = mod->nodes + p;
else out->children[j] = (mnode_t *)(mod->leafs + ( -1 - p )); else out->children_[j] = (mnode_t *)(mod->leafs + ( -1 - p ));
} }
} }
} }
// sets nodes and leafs // sets nodes and leafs
Mod_SetParent( mod->nodes, NULL ); Mod_SetParent( mod, mod->nodes, NULL );
} }
/* /*
@@ -3413,6 +3548,8 @@ static void Mod_LoadLighting( model_t *mod, dbspmodel_t *bmod )
break; break;
} }
Con_Reportf( "lighting: %s\n", FBitSet( mod->flags, MODEL_COLORED_LIGHTING ) ? "colored" : "monochrome" );
// not supposed to be load ? // not supposed to be load ?
if( FBitSet( host.features, ENGINE_LOAD_DELUXEDATA )) if( FBitSet( host.features, ENGINE_LOAD_DELUXEDATA ))
{ {
@@ -3491,13 +3628,6 @@ static qboolean Mod_LoadBmodelLumps( model_t *mod, const byte *mod_base, qboolea
Q_strncpy( loadstat.name, mod->name, sizeof( loadstat.name )); Q_strncpy( loadstat.name, mod->name, sizeof( loadstat.name ));
wadvalue[0] = '\0'; wadvalue[0] = '\0';
#ifndef SUPPORT_BSP2_FORMAT
if( header->version == QBSP2_VERSION )
{
Con_Printf( S_ERROR DEFAULT_BSP_BUILD_ERROR, mod->name );
return false;
}
#endif
switch( header->version ) switch( header->version )
{ {
case HLBSP_VERSION: case HLBSP_VERSION:
@@ -3520,6 +3650,9 @@ static qboolean Mod_LoadBmodelLumps( model_t *mod, const byte *mod_base, qboolea
// everything else // everything else
srclumps[0].lumpnumber = LUMP_ENTITIES; srclumps[0].lumpnumber = LUMP_ENTITIES;
srclumps[1].lumpnumber = LUMP_PLANES; srclumps[1].lumpnumber = LUMP_PLANES;
if( header->version == QBSP2_VERSION )
SetBits( mod->flags, MODEL_QBSP2 );
break; break;
default: default:
Con_Printf( S_ERROR "%s has wrong version number (%i should be %i)\n", mod->name, header->version, HLBSP_VERSION ); Con_Printf( S_ERROR "%s has wrong version number (%i should be %i)\n", mod->name, header->version, HLBSP_VERSION );
@@ -3570,7 +3703,7 @@ static qboolean Mod_LoadBmodelLumps( model_t *mod, const byte *mod_base, qboolea
Mod_LoadClipnodes( mod, bmod ); Mod_LoadClipnodes( mod, bmod );
// preform some post-initalization // preform some post-initalization
Mod_MakeHull0( mod ); Mod_MakeHull0( mod, bmod );
Mod_SetupSubmodels( mod, bmod ); Mod_SetupSubmodels( mod, bmod );
if( isworld ) if( isworld )
@@ -3660,15 +3793,6 @@ qboolean Mod_TestBmodelLumps( file_t *f, const char *name, const byte *mod_base,
if( silent ) if( silent )
SetBits( flags, LUMP_SILENT ); SetBits( flags, LUMP_SILENT );
#ifndef SUPPORT_BSP2_FORMAT
if( header->version == QBSP2_VERSION )
{
if( !FBitSet( flags, LUMP_SILENT ))
Con_Printf( S_ERROR DEFAULT_BSP_BUILD_ERROR, name );
return false;
}
#endif
switch( header->version ) switch( header->version )
{ {
case HLBSP_VERSION: case HLBSP_VERSION:

View File

@@ -135,6 +135,8 @@ extern poolhandle_t com_studiocache;
extern convar_t mod_studiocache; extern convar_t mod_studiocache;
extern convar_t r_wadtextures; extern convar_t r_wadtextures;
extern convar_t r_showhull; extern convar_t r_showhull;
extern const mclipnode16_t box_clipnodes16[6];
extern const mclipnode32_t box_clipnodes32[6];
// //
// model.c // model.c
@@ -167,13 +169,12 @@ void Mod_LoadAliasModel( model_t *mod, const void *buffer, qboolean *loaded );
// //
void Mod_LoadBrushModel( model_t *mod, const void *buffer, qboolean *loaded ); void Mod_LoadBrushModel( model_t *mod, const void *buffer, qboolean *loaded );
qboolean Mod_TestBmodelLumps( file_t *f, const char *name, const byte *mod_base, qboolean silent, dlump_t *entities ); qboolean Mod_TestBmodelLumps( file_t *f, const char *name, const byte *mod_base, qboolean silent, dlump_t *entities );
qboolean Mod_HeadnodeVisible( mnode_t *node, const byte *visbits, int *lastleaf );
int Mod_FatPVS( const vec3_t org, float radius, byte *visbuffer, int visbytes, qboolean merge, qboolean fullvis, qboolean false ); int Mod_FatPVS( const vec3_t org, float radius, byte *visbuffer, int visbytes, qboolean merge, qboolean fullvis, qboolean false );
qboolean Mod_BoxVisible( const vec3_t mins, const vec3_t maxs, const byte *visbits ); qboolean Mod_BoxVisible( const vec3_t mins, const vec3_t maxs, const byte *visbits );
int Mod_CheckLump( const char *filename, const int lump, int *lumpsize ); int Mod_CheckLump( const char *filename, const int lump, int *lumpsize );
int Mod_ReadLump( const char *filename, const int lump, void **lumpdata, int *lumpsize ); int Mod_ReadLump( const char *filename, const int lump, void **lumpdata, int *lumpsize );
int Mod_SaveLump( const char *filename, const int lump, void *lumpdata, int lumpsize ); int Mod_SaveLump( const char *filename, const int lump, void *lumpdata, int lumpsize );
mleaf_t *Mod_PointInLeaf( const vec3_t p, mnode_t *node ); mleaf_t *Mod_PointInLeaf( const vec3_t p, mnode_t *node, model_t *mod );
int Mod_SampleSizeForFace( const msurface_t *surf ); int Mod_SampleSizeForFace( const msurface_t *surf );
byte *Mod_GetPVSForPoint( const vec3_t p ); byte *Mod_GetPVSForPoint( const vec3_t p );
void Mod_UnloadBrushModel( model_t *mod ); void Mod_UnloadBrushModel( model_t *mod );

View File

@@ -50,9 +50,8 @@ static matrix3x4 studio_bones[MAXSTUDIOBONES];
static uint studio_hull_hitgroup[MAXSTUDIOBONES]; static uint studio_hull_hitgroup[MAXSTUDIOBONES];
static uint cache_hull_hitgroup[MAXSTUDIOBONES]; static uint cache_hull_hitgroup[MAXSTUDIOBONES];
static mstudiocache_t cache_studio[STUDIO_CACHESIZE]; static mstudiocache_t cache_studio[STUDIO_CACHESIZE];
static mclipnode_t studio_clipnodes[6]; static mplane_t studio_planes[MAXSTUDIOBONES * 6];
static mplane_t studio_planes[768]; static mplane_t cache_planes[MAXSTUDIOBONES * 6];
static mplane_t cache_planes[768];
// current cache state // current cache state
static int cache_current; static int cache_current;
@@ -66,25 +65,14 @@ Mod_InitStudioHull
*/ */
void Mod_InitStudioHull( void ) void Mod_InitStudioHull( void )
{ {
int i, side; int i;
if( studio_hull[0].planes != NULL ) if( studio_hull[0].planes != NULL )
return; // already initailized return; // already initailized
for( i = 0; i < 6; i++ )
{
studio_clipnodes[i].planenum = i;
side = i & 1;
studio_clipnodes[i].children[side] = CONTENTS_EMPTY;
if( i != 5 ) studio_clipnodes[i].children[side^1] = i + 1;
else studio_clipnodes[i].children[side^1] = CONTENTS_SOLID;
}
for( i = 0; i < MAXSTUDIOBONES; i++ ) for( i = 0; i < MAXSTUDIOBONES; i++ )
{ {
studio_hull[i].clipnodes = studio_clipnodes; studio_hull[i].clipnodes16 = (mclipnode16_t *)box_clipnodes16;
studio_hull[i].planes = &studio_planes[i*6]; studio_hull[i].planes = &studio_planes[i*6];
studio_hull[i].firstclipnode = 0; studio_hull[i].firstclipnode = 0;
studio_hull[i].lastclipnode = 5; studio_hull[i].lastclipnode = 5;
@@ -270,6 +258,11 @@ hull_t *Mod_HullForStudio( model_t *model, float frame, int sequence, vec3_t ang
for( i = j = 0; i < mod_studiohdr->numhitboxes; i++, j += 6 ) for( i = j = 0; i < mod_studiohdr->numhitboxes; i++, j += 6 )
{ {
if( world.version == QBSP2_VERSION )
studio_hull[i].clipnodes32 = (mclipnode32_t *)box_clipnodes32;
else
studio_hull[i].clipnodes16 = (mclipnode16_t *)box_clipnodes16;
if( bSkipShield && i == 21 ) if( bSkipShield && i == 21 )
continue; // CS stuff continue; // CS stuff

View File

@@ -101,20 +101,14 @@ const char *const svc_legacy_strings[svc_lastmsg+1] =
const char *const svc_goldsrc_strings[svc_lastmsg+1] = const char *const svc_goldsrc_strings[svc_lastmsg+1] =
{ {
[svc_goldsrc_version] = "svc_goldsrc_version", [svc_goldsrc_version] = "svc_goldsrc_version",
[svc_goldsrc_serverinfo] = "svc_goldsrc_serverinfo",
[svc_goldsrc_deltadescription] = "svc_goldsrc_deltadescription",
[svc_goldsrc_stopsound] = "svc_goldsrc_stopsound", [svc_goldsrc_stopsound] = "svc_goldsrc_stopsound",
[svc_goldsrc_damage] = "svc_goldsrc_damage", [svc_goldsrc_damage] = "svc_goldsrc_damage",
[svc_goldsrc_killedmonster] = "svc_goldsrc_killedmonster", [svc_goldsrc_killedmonster] = "svc_goldsrc_killedmonster",
[svc_goldsrc_foundsecret] = "svc_goldsrc_foundsecret", [svc_goldsrc_foundsecret] = "svc_goldsrc_foundsecret",
[svc_goldsrc_spawnstaticsound] = "svc_goldsrc_spawnstaticsound", [svc_goldsrc_spawnstaticsound] = "svc_goldsrc_spawnstaticsound",
[svc_goldsrc_decalname] = "svc_goldsrc_decalname", [svc_goldsrc_decalname] = "svc_goldsrc_decalname",
[svc_goldsrc_newusermsg] = "svc_goldsrc_newusermsg",
[svc_goldsrc_newmovevars] = "svc_goldsrc_newmovevars",
[svc_goldsrc_sendextrainfo] = "svc_goldsrc_sendextrainfo", [svc_goldsrc_sendextrainfo] = "svc_goldsrc_sendextrainfo",
[svc_goldsrc_timescale] = "svc_goldsrc_timescale", [svc_goldsrc_timescale] = "svc_goldsrc_timescale",
[svc_goldsrc_sendcvarvalue] = "svc_goldsrc_sendcvarvalue",
[svc_goldsrc_sendcvarvalue2] = "svc_goldsrc_sendcvarvalue2",
}; };
const char *const svc_quake_strings[svc_lastmsg+1] = const char *const svc_quake_strings[svc_lastmsg+1] =

View File

@@ -39,8 +39,6 @@ GNU General Public License for more details.
#define DT_SIGNED BIT( 8 ) // sign modificator #define DT_SIGNED BIT( 8 ) // sign modificator
#define DT_SIGNED_GS BIT( 31 ) // GoldSrc-specific sign modificator #define DT_SIGNED_GS BIT( 31 ) // GoldSrc-specific sign modificator
#define NUM_FIELDS( x ) ((sizeof( x ) / sizeof( x[0] )) - 1)
// helper macroses // helper macroses
#define ENTS_DEF( x ) #x, offsetof( entity_state_t, x ), sizeof( ((entity_state_t *)0)->x ) #define ENTS_DEF( x ) #x, offsetof( entity_state_t, x ), sizeof( ((entity_state_t *)0)->x )
#define UCMD_DEF( x ) #x, offsetof( usercmd_t, x ), sizeof( ((usercmd_t *)0)->x ) #define UCMD_DEF( x ) #x, offsetof( usercmd_t, x ), sizeof( ((usercmd_t *)0)->x )
@@ -71,7 +69,6 @@ static const delta_field_t cmd_fields[] =
{ UCMD_DEF( impact_position[0] ) }, { UCMD_DEF( impact_position[0] ) },
{ UCMD_DEF( impact_position[1] ) }, { UCMD_DEF( impact_position[1] ) },
{ UCMD_DEF( impact_position[2] ) }, { UCMD_DEF( impact_position[2] ) },
{ NULL },
}; };
static const delta_field_t pm_fields[] = static const delta_field_t pm_fields[] =
@@ -107,7 +104,6 @@ static const delta_field_t pm_fields[] =
{ PHYS_DEF( skydir_y ) }, { PHYS_DEF( skydir_y ) },
{ PHYS_DEF( skydir_z ) }, { PHYS_DEF( skydir_z ) },
{ PHYS_DEF( skyangle ) }, { PHYS_DEF( skyangle ) },
{ NULL },
}; };
static const delta_field_t ev_fields[] = static const delta_field_t ev_fields[] =
@@ -130,7 +126,6 @@ static const delta_field_t ev_fields[] =
{ EVNT_DEF( iparam2 ) }, { EVNT_DEF( iparam2 ) },
{ EVNT_DEF( bparam1 ) }, { EVNT_DEF( bparam1 ) },
{ EVNT_DEF( bparam2 ) }, { EVNT_DEF( bparam2 ) },
{ NULL },
}; };
static const delta_field_t wd_fields[] = static const delta_field_t wd_fields[] =
@@ -157,7 +152,6 @@ static const delta_field_t wd_fields[] =
{ WPDT_DEF( fuser2 ) }, { WPDT_DEF( fuser2 ) },
{ WPDT_DEF( fuser3 ) }, { WPDT_DEF( fuser3 ) },
{ WPDT_DEF( fuser4 ) }, { WPDT_DEF( fuser4 ) },
{ NULL },
}; };
static const delta_field_t cd_fields[] = static const delta_field_t cd_fields[] =
@@ -218,7 +212,6 @@ static const delta_field_t cd_fields[] =
{ CLDT_DEF( vuser4[0] ) }, { CLDT_DEF( vuser4[0] ) },
{ CLDT_DEF( vuser4[1] ) }, { CLDT_DEF( vuser4[1] ) },
{ CLDT_DEF( vuser4[2] ) }, { CLDT_DEF( vuser4[2] ) },
{ NULL },
}; };
static const delta_field_t ent_fields[] = static const delta_field_t ent_fields[] =
@@ -314,7 +307,6 @@ static const delta_field_t ent_fields[] =
{ ENTS_DEF( vuser4[0] ) }, { ENTS_DEF( vuser4[0] ) },
{ ENTS_DEF( vuser4[1] ) }, { ENTS_DEF( vuser4[1] ) },
{ ENTS_DEF( vuser4[2] ) }, { ENTS_DEF( vuser4[2] ) },
{ NULL },
}; };
static const delta_field_t meta_fields[] = static const delta_field_t meta_fields[] =
@@ -326,7 +318,6 @@ static const delta_field_t meta_fields[] =
{ DESC_DEF( significant_bits ), }, { DESC_DEF( significant_bits ), },
{ DESC_DEF( premultiply ), }, { DESC_DEF( premultiply ), },
{ DESC_DEF( postmultiply ), }, { DESC_DEF( postmultiply ), },
{ NULL },
}; };
#if XASH_ENGINE_TESTS #if XASH_ENGINE_TESTS
@@ -362,24 +353,22 @@ static const delta_field_t test_fields[] =
{ TEST_DEF( dt_short_unsigned ) }, { TEST_DEF( dt_short_unsigned ) },
{ TEST_DEF( dt_byte_signed ) }, { TEST_DEF( dt_byte_signed ) },
{ TEST_DEF( dt_byte_unsigned ) }, { TEST_DEF( dt_byte_unsigned ) },
{ NULL },
}; };
#endif #endif
static delta_info_t dt_info[] = static delta_info_t dt_info[] =
{ {
[DT_EVENT_T] = { "event_t", ev_fields, NUM_FIELDS( ev_fields ) }, [DT_EVENT_T] = { "event_t", ev_fields, ARRAYSIZE( ev_fields ) },
[DT_MOVEVARS_T] = { "movevars_t", pm_fields, NUM_FIELDS( pm_fields ) }, [DT_MOVEVARS_T] = { "movevars_t", pm_fields, ARRAYSIZE( pm_fields ) },
[DT_USERCMD_T] = { "usercmd_t", cmd_fields, NUM_FIELDS( cmd_fields ) }, [DT_USERCMD_T] = { "usercmd_t", cmd_fields, ARRAYSIZE( cmd_fields ) },
[DT_CLIENTDATA_T] = { "clientdata_t", cd_fields, NUM_FIELDS( cd_fields ) }, [DT_CLIENTDATA_T] = { "clientdata_t", cd_fields, ARRAYSIZE( cd_fields ) },
[DT_WEAPONDATA_T] = { "weapon_data_t", wd_fields, NUM_FIELDS( wd_fields ) }, [DT_WEAPONDATA_T] = { "weapon_data_t", wd_fields, ARRAYSIZE( wd_fields ) },
[DT_ENTITY_STATE_T] = { "entity_state_t", ent_fields, NUM_FIELDS( ent_fields ) }, [DT_ENTITY_STATE_T] = { "entity_state_t", ent_fields, ARRAYSIZE( ent_fields ) },
[DT_ENTITY_STATE_PLAYER_T] = { "entity_state_player_t", ent_fields, NUM_FIELDS( ent_fields ) }, [DT_ENTITY_STATE_PLAYER_T] = { "entity_state_player_t", ent_fields, ARRAYSIZE( ent_fields ) },
[DT_CUSTOM_ENTITY_STATE_T] = { "custom_entity_state_t", ent_fields, NUM_FIELDS( ent_fields ) }, [DT_CUSTOM_ENTITY_STATE_T] = { "custom_entity_state_t", ent_fields, ARRAYSIZE( ent_fields ) },
#if XASH_ENGINE_TESTS #if XASH_ENGINE_TESTS
[DT_DELTA_TEST_STRUCT_T] = { "delta_test_struct_t", test_fields, NUM_FIELDS( test_fields ) }, [DT_DELTA_TEST_STRUCT_T] = { "delta_test_struct_t", test_fields, ARRAYSIZE( test_fields ) },
#endif #endif
[DT_STRUCT_COUNT] = { NULL },
}; };
// meta description is special, it cannot be overriden // meta description is special, it cannot be overriden
@@ -387,9 +376,9 @@ static const delta_info_t dt_goldsrc_meta =
{ {
.pName = "goldsrc_delta_t", .pName = "goldsrc_delta_t",
.pInfo = meta_fields, .pInfo = meta_fields,
.maxFields = NUM_FIELDS( meta_fields ), .maxFields = ARRAYSIZE( meta_fields ),
.numFields = NUM_FIELDS( meta_fields ), .numFields = ARRAYSIZE( meta_fields ),
.pFields = (delta_t[NUM_FIELDS( meta_fields )]) .pFields = (delta_t[ARRAYSIZE( meta_fields )])
{ {
{ {
DESC_DEF( fieldType ), DESC_DEF( fieldType ),
@@ -451,7 +440,7 @@ static delta_info_t *Delta_FindStruct( const char *name )
if( !COM_CheckString( name )) if( !COM_CheckString( name ))
return NULL; return NULL;
for( i = 0; i < NUM_FIELDS( dt_info ); i++ ) for( i = 0; i < ARRAYSIZE( dt_info ); i++ )
{ {
if( !Q_stricmp( dt_info[i].pName, name )) if( !Q_stricmp( dt_info[i].pName, name ))
return &dt_info[i]; return &dt_info[i];
@@ -465,7 +454,7 @@ static delta_info_t *Delta_FindStruct( const char *name )
static int Delta_NumTables( void ) static int Delta_NumTables( void )
{ {
return NUM_FIELDS( dt_info ); return ARRAYSIZE( dt_info );
} }
static delta_info_t *Delta_FindStructByIndex( int index ) static delta_info_t *Delta_FindStructByIndex( int index )
@@ -480,7 +469,7 @@ static delta_info_t *Delta_FindStructByEncoder( const char *encoderName )
if( !COM_CheckString( encoderName ) ) if( !COM_CheckString( encoderName ) )
return NULL; return NULL;
for( i = 0; i < NUM_FIELDS( dt_info ); i++ ) for( i = 0; i < ARRAYSIZE( dt_info ); i++ )
{ {
if( !Q_stricmp( dt_info[i].funcName, encoderName )) if( !Q_stricmp( dt_info[i].funcName, encoderName ))
return &dt_info[i]; return &dt_info[i];
@@ -495,7 +484,7 @@ static delta_info_t *Delta_FindStructByDelta( const delta_t *pFields )
if( !pFields ) return NULL; if( !pFields ) return NULL;
for( i = 0; i < NUM_FIELDS( dt_info ); i++ ) for( i = 0; i < ARRAYSIZE( dt_info ); i++ )
{ {
if( dt_info[i].pFields == pFields ) if( dt_info[i].pFields == pFields )
return &dt_info[i]; return &dt_info[i];
@@ -518,29 +507,32 @@ static void Delta_CustomEncode( delta_info_t *dt, const void *from, const void *
dt->userCallback( dt->pFields, from, to ); dt->userCallback( dt->pFields, from, to );
} }
static delta_field_t *Delta_FindFieldInfo( const delta_field_t *pInfo, const char *fieldName ) static const delta_field_t *Delta_FindFieldInfo( const delta_field_t *pInfo, const char *fieldName, int maxFields )
{ {
int i;
if( !fieldName || !*fieldName ) if( !fieldName || !*fieldName )
return NULL; return NULL;
for( ; pInfo->name; pInfo++ ) for( i = 0; i < maxFields; i++ )
{ {
if( !Q_strcmp( pInfo->name, fieldName )) if( !Q_strcmp( pInfo[i].name, fieldName ))
return (delta_field_t *)pInfo; return &pInfo[i];
} }
return NULL; return NULL;
} }
static int Delta_IndexForFieldInfo( const delta_field_t *pInfo, const char *fieldName ) static int Delta_IndexForFieldInfo( const delta_field_t *pInfo, const char *fieldName, int maxFields )
{ {
int i; int i;
if( !fieldName || !*fieldName ) if( !fieldName || !*fieldName )
return -1; return -1;
for( i = 0; pInfo->name; i++, pInfo++ ) for( i = 0; i < maxFields; i++ )
{ {
if( !Q_strcmp( pInfo->name, fieldName )) if( !Q_strcmp( pInfo[i].name, fieldName ))
return i; return i;
} }
return -1; return -1;
@@ -548,7 +540,7 @@ static int Delta_IndexForFieldInfo( const delta_field_t *pInfo, const char *fiel
static qboolean Delta_AddField( delta_info_t *dt, const char *pName, int flags, int bits, float mul, float post_mul ) static qboolean Delta_AddField( delta_info_t *dt, const char *pName, int flags, int bits, float mul, float post_mul )
{ {
delta_field_t *pFieldInfo; const delta_field_t *pFieldInfo;
delta_t *pField; delta_t *pField;
int i; int i;
@@ -567,7 +559,7 @@ static qboolean Delta_AddField( delta_info_t *dt, const char *pName, int flags,
} }
// find field description // find field description
pFieldInfo = Delta_FindFieldInfo( dt->pInfo, pName ); pFieldInfo = Delta_FindFieldInfo( dt->pInfo, pName, dt->maxFields );
if( !pFieldInfo ) if( !pFieldInfo )
{ {
Con_DPrintf( S_ERROR "%s: couldn't find description for %s->%s\n", __func__, dt->pName, pName ); Con_DPrintf( S_ERROR "%s: couldn't find description for %s->%s\n", __func__, dt->pName, pName );
@@ -610,7 +602,7 @@ static void Delta_WriteTableField( sizebuf_t *msg, int tableIndex, const delta_t
dt = Delta_FindStructByIndex( tableIndex ); dt = Delta_FindStructByIndex( tableIndex );
Assert( dt && dt->bInitialized ); Assert( dt && dt->bInitialized );
nameIndex = Delta_IndexForFieldInfo( dt->pInfo, pField->name ); nameIndex = Delta_IndexForFieldInfo( dt->pInfo, pField->name, dt->maxFields );
Assert( nameIndex >= 0 && nameIndex < dt->maxFields ); Assert( nameIndex >= 0 && nameIndex < dt->maxFields );
MSG_BeginServerCmd( msg, svc_deltatable ); MSG_BeginServerCmd( msg, svc_deltatable );
@@ -681,10 +673,10 @@ void Delta_ParseTableField( sizebuf_t *msg )
Delta_AddField( dt, pName, flags, bits, mul, post_mul ); Delta_AddField( dt, pName, flags, bits, mul, post_mul );
} }
static qboolean Delta_ParseField( char **delta_script, const delta_field_t *pInfo, delta_t *pField, qboolean bPost ) static qboolean Delta_ParseField( char **delta_script, const delta_info_t *dt, delta_t *pField, qboolean bPost )
{ {
const delta_field_t *pFieldInfo;
string token; string token;
delta_field_t *pFieldInfo;
char *oldpos; char *oldpos;
*delta_script = COM_ParseFile( *delta_script, token, sizeof( token )); *delta_script = COM_ParseFile( *delta_script, token, sizeof( token ));
@@ -701,7 +693,7 @@ static qboolean Delta_ParseField( char **delta_script, const delta_field_t *pInf
return false; return false;
} }
pFieldInfo = Delta_FindFieldInfo( pInfo, token ); pFieldInfo = Delta_FindFieldInfo( dt->pInfo, token, dt->maxFields );
if( !pFieldInfo ) if( !pFieldInfo )
{ {
Con_DPrintf( S_ERROR "%s: unable to find field %s\n", __func__, token ); Con_DPrintf( S_ERROR "%s: unable to find field %s\n", __func__, token );
@@ -825,13 +817,11 @@ static void Delta_ParseTable( char **delta_script, delta_info_t *dt, const char
{ {
string token; string token;
delta_t *pField; delta_t *pField;
const delta_field_t *pInfo;
// allocate the delta-structures // allocate the delta-structures
if( !dt->pFields ) dt->pFields = (delta_t *)Z_Calloc( dt->maxFields * sizeof( delta_t )); if( !dt->pFields ) dt->pFields = (delta_t *)Z_Calloc( dt->maxFields * sizeof( delta_t ));
pField = dt->pFields; pField = dt->pFields;
pInfo = dt->pInfo;
dt->numFields = 0; dt->numFields = 0;
// assume we have handled '{' // assume we have handled '{'
@@ -841,12 +831,12 @@ static void Delta_ParseTable( char **delta_script, delta_info_t *dt, const char
if( !Q_strcmp( token, "DEFINE_DELTA" )) if( !Q_strcmp( token, "DEFINE_DELTA" ))
{ {
if( Delta_ParseField( delta_script, pInfo, &pField[dt->numFields], false )) if( Delta_ParseField( delta_script, dt, &pField[dt->numFields], false ))
dt->numFields++; dt->numFields++;
} }
else if( !Q_strcmp( token, "DEFINE_DELTA_POST" )) else if( !Q_strcmp( token, "DEFINE_DELTA_POST" ))
{ {
if( Delta_ParseField( delta_script, pInfo, &pField[dt->numFields], true )) if( Delta_ParseField( delta_script, dt, &pField[dt->numFields], true ))
dt->numFields++; dt->numFields++;
} }
else if( token[0] == '}' ) else if( token[0] == '}' )
@@ -963,7 +953,7 @@ void Delta_Init( void )
Delta_AddField( dt, "skyvec_z", DT_FLOAT|DT_SIGNED, 16, 32.0f, 1.0f ); Delta_AddField( dt, "skyvec_z", DT_FLOAT|DT_SIGNED, 16, 32.0f, 1.0f );
Delta_AddField( dt, "wateralpha", DT_FLOAT|DT_SIGNED, 16, 32.0f, 1.0f ); Delta_AddField( dt, "wateralpha", DT_FLOAT|DT_SIGNED, 16, 32.0f, 1.0f );
Delta_AddField( dt, "fog_settings", DT_INTEGER, 32, 1.0f, 1.0f ); Delta_AddField( dt, "fog_settings", DT_INTEGER, 32, 1.0f, 1.0f );
dt->numFields = NUM_FIELDS( pm_fields ) - 4; dt->numFields = ARRAYSIZE( pm_fields ) - 4;
// now done // now done
dt->bInitialized = true; dt->bInitialized = true;
@@ -976,7 +966,7 @@ void Delta_InitClient( void )
// already initalized // already initalized
if( delta_init ) return; if( delta_init ) return;
for( i = 0; i < NUM_FIELDS( dt_info ); i++ ) for( i = 0; i < ARRAYSIZE( dt_info ); i++ )
{ {
if( dt_info[i].numFields > 0 ) if( dt_info[i].numFields > 0 )
{ {
@@ -994,7 +984,7 @@ void Delta_Shutdown( void )
if( !delta_init ) return; if( !delta_init ) return;
for( i = 0; i < NUM_FIELDS( dt_info ); i++ ) for( i = 0; i < ARRAYSIZE( dt_info ); i++ )
{ {
dt_info[i].numFields = 0; dt_info[i].numFields = 0;
dt_info[i].customEncode = CUSTOM_NONE; dt_info[i].customEncode = CUSTOM_NONE;

View File

@@ -46,7 +46,6 @@ enum
#if XASH_ENGINE_TESTS #if XASH_ENGINE_TESTS
DT_DELTA_TEST_STRUCT_T, DT_DELTA_TEST_STRUCT_T,
#endif #endif
DT_STRUCT_COUNT
}; };
// struct info (filled by engine) // struct info (filled by engine)

View File

@@ -112,6 +112,9 @@ msurface_t *PM_RecursiveSurfCheck( model_t *mod, mnode_t *node, vec3_t p1, vec3_
int i, side; int i, side;
msurface_t *surf; msurface_t *surf;
vec3_t mid; vec3_t mid;
mnode_t *children[2];
int numsurfaces, firstsurface;
loc0: loc0:
if( node->contents < 0 ) if( node->contents < 0 )
return NULL; return NULL;
@@ -119,15 +122,17 @@ loc0:
t1 = PlaneDiff( p1, node->plane ); t1 = PlaneDiff( p1, node->plane );
t2 = PlaneDiff( p2, node->plane ); t2 = PlaneDiff( p2, node->plane );
node_children( children, node, mod );
if( t1 >= -FRAC_EPSILON && t2 >= -FRAC_EPSILON ) if( t1 >= -FRAC_EPSILON && t2 >= -FRAC_EPSILON )
{ {
node = node->children[0]; node = children[0];
goto loc0; goto loc0;
} }
if( t1 < FRAC_EPSILON && t2 < FRAC_EPSILON ) if( t1 < FRAC_EPSILON && t2 < FRAC_EPSILON )
{ {
node = node->children[1]; node = children[1];
goto loc0; goto loc0;
} }
@@ -137,13 +142,15 @@ loc0:
VectorLerp( p1, frac, p2, mid ); VectorLerp( p1, frac, p2, mid );
if(( surf = PM_RecursiveSurfCheck( mod, node->children[side], p1, mid )) != NULL ) if(( surf = PM_RecursiveSurfCheck( mod, children[side], p1, mid )) != NULL )
return surf; return surf;
// walk through real faces // walk through real faces
for( i = 0; i < node->numsurfaces; i++ ) numsurfaces = node_numsurfaces( node, mod );
firstsurface = node_firstsurface( node, mod );
for( i = 0; i < numsurfaces; i++ )
{ {
msurface_t *surf = &mod->surfaces[node->firstsurface + i]; msurface_t *surf = &mod->surfaces[firstsurface + i];
mextrasurf_t *info = surf->info; mextrasurf_t *info = surf->info;
mfacebevel_t *fb = info->bevel; mfacebevel_t *fb = info->bevel;
int j, contents; int j, contents;
@@ -172,7 +179,7 @@ loc0:
return NULL; // through the fence return NULL; // through the fence
} }
return PM_RecursiveSurfCheck( mod, node->children[side^1], mid, p2 ); return PM_RecursiveSurfCheck( mod, children[side^1], mid, p2 );
} }
/* /*
@@ -227,6 +234,9 @@ static int PM_TestLine_r( model_t *mod, mnode_t *node, vec_t p1f, vec_t p2f, con
float frac, midf; float frac, midf;
int i, r, side; int i, r, side;
vec3_t mid; vec3_t mid;
mnode_t *children[2];
int numsurfaces, firstsurface;
loc0: loc0:
if( node->contents < 0 ) if( node->contents < 0 )
{ {
@@ -242,15 +252,17 @@ loc0:
front = PlaneDiff( start, node->plane ); front = PlaneDiff( start, node->plane );
back = PlaneDiff( stop, node->plane ); back = PlaneDiff( stop, node->plane );
node_children( children, node, mod );
if( front >= -FRAC_EPSILON && back >= -FRAC_EPSILON ) if( front >= -FRAC_EPSILON && back >= -FRAC_EPSILON )
{ {
node = node->children[0]; node = children[0];
goto loc0; goto loc0;
} }
if( front < FRAC_EPSILON && back < FRAC_EPSILON ) if( front < FRAC_EPSILON && back < FRAC_EPSILON )
{ {
node = node->children[1]; node = children[1];
goto loc0; goto loc0;
} }
@@ -261,7 +273,7 @@ loc0:
VectorLerp( start, frac, stop, mid ); VectorLerp( start, frac, stop, mid );
midf = p1f + ( p2f - p1f ) * frac; midf = p1f + ( p2f - p1f ) * frac;
r = PM_TestLine_r( mod, node->children[side], p1f, midf, start, mid, trace ); r = PM_TestLine_r( mod, children[side], p1f, midf, start, mid, trace );
if( r != CONTENTS_EMPTY ) if( r != CONTENTS_EMPTY )
{ {
@@ -272,9 +284,11 @@ loc0:
} }
// walk through real faces // walk through real faces
for( i = 0; i < node->numsurfaces; i++ ) numsurfaces = node_numsurfaces( node, mod );
firstsurface = node_firstsurface( node, mod );
for( i = 0; i < numsurfaces; i++ )
{ {
msurface_t *surf = &mod->surfaces[node->firstsurface + i]; msurface_t *surf = &mod->surfaces[firstsurface + i];
mextrasurf_t *info = surf->info; mextrasurf_t *info = surf->info;
mfacebevel_t *fb = info->bevel; mfacebevel_t *fb = info->bevel;
int j, contents; int j, contents;
@@ -308,7 +322,7 @@ loc0:
return contents; return contents;
} }
return PM_TestLine_r( mod, node->children[!side], midf, p2f, mid, stop, trace ); return PM_TestLine_r( mod, children[!side], midf, p2f, mid, stop, trace );
} }
int PM_TestLineExt( playermove_t *pmove, physent_t *ents, int numents, const vec3_t start, const vec3_t end, int flags ) int PM_TestLineExt( playermove_t *pmove, physent_t *ents, int numents, const vec3_t start, const vec3_t end, int flags )

View File

@@ -25,8 +25,7 @@ GNU General Public License for more details.
#define PM_AllowHitBoxTrace( model, hull ) ( model && model->type == mod_studio && ( FBitSet( model->flags, STUDIO_TRACE_HITBOX ) || hull == 2 )) #define PM_AllowHitBoxTrace( model, hull ) ( model && model->type == mod_studio && ( FBitSet( model->flags, STUDIO_TRACE_HITBOX ) || hull == 2 ))
static mplane_t pm_boxplanes[6]; static mplane_t pm_boxplanes[6];
static mclipnode_t pm_boxclipnodes[6]; static hull_t pm_boxhull;
static hull_t pm_boxhull;
// default hullmins // default hullmins
static const vec3_t pm_hullmins[MAX_MAP_HULLS] = static const vec3_t pm_hullmins[MAX_MAP_HULLS] =
@@ -65,23 +64,15 @@ can just be stored out and get a proper hull_t structure.
*/ */
void PM_InitBoxHull( void ) void PM_InitBoxHull( void )
{ {
int i, side; int i;
pm_boxhull.clipnodes = pm_boxclipnodes; pm_boxhull.clipnodes16 = (mclipnode16_t *)box_clipnodes16;
pm_boxhull.planes = pm_boxplanes; pm_boxhull.planes = pm_boxplanes;
pm_boxhull.firstclipnode = 0; pm_boxhull.firstclipnode = 0;
pm_boxhull.lastclipnode = 5; pm_boxhull.lastclipnode = 5;
for( i = 0; i < 6; i++ ) for( i = 0; i < 6; i++ )
{ {
pm_boxclipnodes[i].planenum = i;
side = i & 1;
pm_boxclipnodes[i].children[side] = CONTENTS_EMPTY;
if( i != 5 ) pm_boxclipnodes[i].children[side^1] = i + 1;
else pm_boxclipnodes[i].children[side^1] = CONTENTS_SOLID;
pm_boxplanes[i].type = i>>1; pm_boxplanes[i].type = i>>1;
pm_boxplanes[i].normal[i>>1] = 1.0f; pm_boxplanes[i].normal[i>>1] = 1.0f;
pm_boxplanes[i].signbits = 0; pm_boxplanes[i].signbits = 0;
@@ -106,6 +97,11 @@ static hull_t *PM_HullForBox( const vec3_t mins, const vec3_t maxs )
pm_boxplanes[4].dist = maxs[2]; pm_boxplanes[4].dist = maxs[2];
pm_boxplanes[5].dist = mins[2]; pm_boxplanes[5].dist = mins[2];
if( world.version == QBSP2_VERSION )
pm_boxhull.clipnodes32 = (mclipnode32_t *)box_clipnodes32;
else
pm_boxhull.clipnodes16 = (mclipnode16_t *)box_clipnodes16;
return &pm_boxhull; return &pm_boxhull;
} }
@@ -122,10 +118,21 @@ int GAME_EXPORT PM_HullPointContents( hull_t *hull, int num, const vec3_t p )
if( !hull || !hull->planes ) // fantom bmodels? if( !hull || !hull->planes ) // fantom bmodels?
return CONTENTS_NONE; return CONTENTS_NONE;
while( num >= 0 ) if( world.version == QBSP2_VERSION )
{ {
plane = &hull->planes[hull->clipnodes[num].planenum]; while( num >= 0 )
num = hull->clipnodes[num].children[PlaneDiff( p, plane ) < 0]; {
plane = &hull->planes[hull->clipnodes32[num].planenum];
num = hull->clipnodes32[num].children[PlaneDiff( p, plane ) < 0];
}
}
else
{
while( num >= 0 )
{
plane = &hull->planes[hull->clipnodes16[num].planenum];
num = hull->clipnodes16[num].children[PlaneDiff( p, plane ) < 0];
}
} }
return num; return num;
} }
@@ -193,7 +200,7 @@ PM_RecursiveHullCheck
*/ */
qboolean PM_RecursiveHullCheck( hull_t *hull, int num, float p1f, float p2f, vec3_t p1, vec3_t p2, pmtrace_t *trace ) qboolean PM_RecursiveHullCheck( hull_t *hull, int num, float p1f, float p2f, vec3_t p1, vec3_t p2, pmtrace_t *trace )
{ {
mclipnode_t *node; int children[2];
mplane_t *plane; mplane_t *plane;
float t1, t2; float t1, t2;
float frac, midf; float frac, midf;
@@ -226,21 +233,31 @@ loc0:
Host_Error( "%s: bad node number %i\n", __func__, num ); Host_Error( "%s: bad node number %i\n", __func__, num );
// find the point distances // find the point distances
node = hull->clipnodes + num; if( world.version == QBSP2_VERSION )
plane = hull->planes + node->planenum; {
children[0] = hull->clipnodes32[num].children[0];
children[1] = hull->clipnodes32[num].children[1];
plane = hull->planes + hull->clipnodes32[num].planenum;
}
else
{
children[0] = hull->clipnodes16[num].children[0];
children[1] = hull->clipnodes16[num].children[1];
plane = hull->planes + hull->clipnodes16[num].planenum;
}
t1 = PlaneDiff( p1, plane ); t1 = PlaneDiff( p1, plane );
t2 = PlaneDiff( p2, plane ); t2 = PlaneDiff( p2, plane );
if( t1 >= 0.0f && t2 >= 0.0f ) if( t1 >= 0.0f && t2 >= 0.0f )
{ {
num = node->children[0]; num = children[0];
goto loc0; goto loc0;
} }
if( t1 < 0.0f && t2 < 0.0f ) if( t1 < 0.0f && t2 < 0.0f )
{ {
num = node->children[1]; num = children[1];
goto loc0; goto loc0;
} }
@@ -257,14 +274,14 @@ loc0:
VectorLerp( p1, frac, p2, mid ); VectorLerp( p1, frac, p2, mid );
// move up to the node // move up to the node
if( !PM_RecursiveHullCheck( hull, node->children[side], p1f, midf, p1, mid, trace )) if( !PM_RecursiveHullCheck( hull, children[side], p1f, midf, p1, mid, trace ))
return false; return false;
// this recursion can not be optimized because mid would need to be duplicated on a stack // this recursion can not be optimized because mid would need to be duplicated on a stack
if( PM_HullPointContents( hull, node->children[side^1], mid ) != CONTENTS_SOLID ) if( PM_HullPointContents( hull, children[side^1], mid ) != CONTENTS_SOLID )
{ {
// go past the node // go past the node
return PM_RecursiveHullCheck( hull, node->children[side^1], midf, p2f, mid, p2, trace ); return PM_RecursiveHullCheck( hull, children[side^1], midf, p2f, mid, p2, trace );
} }
// never got out of the solid area // never got out of the solid area

View File

@@ -324,20 +324,14 @@ extern const char *const clc_strings[clc_lastmsg+1];
#define PROTOCOL_GOLDSRC_VERSION 48 #define PROTOCOL_GOLDSRC_VERSION 48
#define svc_goldsrc_version svc_changing #define svc_goldsrc_version svc_changing
#define svc_goldsrc_serverinfo svc_serverdata
#define svc_goldsrc_deltadescription svc_deltatable
#define svc_goldsrc_stopsound svc_resource #define svc_goldsrc_stopsound svc_resource
#define svc_goldsrc_damage svc_restoresound #define svc_goldsrc_damage svc_restoresound
#define svc_goldsrc_killedmonster 27 #define svc_goldsrc_killedmonster 27
#define svc_goldsrc_foundsecret 28 #define svc_goldsrc_foundsecret 28
#define svc_goldsrc_spawnstaticsound 29 #define svc_goldsrc_spawnstaticsound 29
#define svc_goldsrc_decalname svc_bspdecal #define svc_goldsrc_decalname svc_bspdecal
#define svc_goldsrc_newusermsg svc_usermessage
#define svc_goldsrc_newmovevars svc_deltamovevars
#define svc_goldsrc_sendextrainfo 54 #define svc_goldsrc_sendextrainfo 54
#define svc_goldsrc_timescale 55 #define svc_goldsrc_timescale 55
#define svc_goldsrc_sendcvarvalue svc_querycvarvalue
#define svc_goldsrc_sendcvarvalue2 svc_querycvarvalue2
#define clc_goldsrc_hltv clc_requestcvarvalue // 9 #define clc_goldsrc_hltv clc_requestcvarvalue // 9
#define clc_goldsrc_requestcvarvalue clc_requestcvarvalue2 // 10 #define clc_goldsrc_requestcvarvalue clc_requestcvarvalue2 // 10

View File

@@ -14,10 +14,7 @@ GNU General Public License for more details.
*/ */
#include "common.h" #include "common.h"
#if XASH_WIN32 #if XASH_ANDROID
#define STDOUT_FILENO 1
#include <io.h>
#elif XASH_ANDROID
#include <android/log.h> #include <android/log.h>
#endif #endif
#include <string.h> #include <string.h>
@@ -25,20 +22,13 @@ GNU General Public License for more details.
#if XASH_IRIX #if XASH_IRIX
#include <sys/time.h> #include <sys/time.h>
#endif #endif
#include "xash3d_mathlib.h"
// do not waste precious CPU cycles on mobiles or low memory devices // do not waste precious CPU cycles on mobiles or low memory devices
#if !XASH_WIN32 && !XASH_MOBILE_PLATFORM && !XASH_LOW_MEMORY #if !XASH_WIN32 && !XASH_MOBILE_PLATFORM && !XASH_LOW_MEMORY
#define XASH_COLORIZE_CONSOLE true #define XASH_COLORIZE_CONSOLE 1
// use with caution, running engine in Qt Creator may cause a freeze in read() call
// I have never encountered this bug anywhere else, so still enable by default
#define XASH_USE_SELECT 1
#else #else
#define XASH_COLORIZE_CONSOLE false #define XASH_COLORIZE_CONSOLE 0
#endif
#if XASH_USE_SELECT
// non-blocking console input
#include <sys/select.h>
#endif #endif
typedef struct { typedef struct {
@@ -51,41 +41,6 @@ typedef struct {
static LogData s_ld; static LogData s_ld;
char *Sys_Input( void )
{
#if XASH_USE_SELECT
if( Host_IsDedicated( ))
{
fd_set rfds;
static char line[1024];
static int len;
struct timeval tv;
tv.tv_sec = 0;
tv.tv_usec = 0;
FD_ZERO( &rfds );
FD_SET( 0, &rfds); // stdin
while( select( 1, &rfds, NULL, NULL, &tv ) > 0 )
{
if( read( 0, &line[len], 1 ) != 1 )
break;
if( line[len] == '\n' || len > 1022 )
{
line[ ++len ] = 0;
len = 0;
return line;
}
len++;
tv.tv_sec = 0;
tv.tv_usec = 0;
}
}
#endif
#if XASH_WIN32
return Wcon_Input();
#endif
return NULL;
}
void Sys_DestroyConsole( void ) void Sys_DestroyConsole( void )
{ {
// last text message into console or log // last text message into console or log
@@ -194,8 +149,8 @@ void Sys_CloseLog( void )
} }
} }
#if XASH_COLORIZE_CONSOLE == true #if XASH_COLORIZE_CONSOLE
static void Sys_WriteEscapeSequenceForColorcode( int fd, int c ) static qboolean Sys_WriteEscapeSequenceForColorcode( int fd, int c )
{ {
static const char *q3ToAnsi[ 8 ] = static const char *q3ToAnsi[ 8 ] =
{ {
@@ -210,19 +165,26 @@ static void Sys_WriteEscapeSequenceForColorcode( int fd, int c )
}; };
const char *esc = q3ToAnsi[c]; const char *esc = q3ToAnsi[c];
if( c == 7 ) return write( fd, esc, c == 7 ? 4 : 7 ) < 0 ? false : true;
write( fd, esc, 4 );
else write( fd, esc, 7 );
} }
#else #else
static void Sys_WriteEscapeSequenceForColorcode( int fd, int c ) {} static qboolean Sys_WriteEscapeSequenceForColorcode( int fd, int c )
{
return true;
}
#endif #endif
static void Sys_PrintLogfile( const int fd, const char *logtime, const char *msg, const qboolean colorize ) static void Sys_PrintLogfile( const int fd, const char *logtime, size_t logtime_len, const char *msg, const int colorize )
{ {
const char *p = msg; const char *p = msg;
write( fd, logtime, Q_strlen( logtime ) ); if( logtime_len != 0 )
{
if( write( fd, logtime, logtime_len ) < 0 )
{
// not critical for us
}
}
while( p && *p ) while( p && *p )
{ {
@@ -262,7 +224,7 @@ static void Sys_PrintLogfile( const int fd, const char *logtime, const char *msg
Sys_WriteEscapeSequenceForColorcode( fd, 7 ); Sys_WriteEscapeSequenceForColorcode( fd, 7 );
} }
static void Sys_PrintStdout( const char *logtime, const char *msg ) static void Sys_PrintStdout( const char *logtime, size_t logtime_len, const char *msg )
{ {
#if XASH_MOBILE_PLATFORM #if XASH_MOBILE_PLATFORM
static char buf[MAX_PRINT_MSG]; static char buf[MAX_PRINT_MSG];
@@ -294,7 +256,7 @@ static void Sys_PrintStdout( const char *logtime, const char *msg )
#endif #endif
#elif !XASH_WIN32 // Wcon does the job #elif !XASH_WIN32 // Wcon does the job
Sys_PrintLogfile( STDOUT_FILENO, logtime, msg, XASH_COLORIZE_CONSOLE ); Sys_PrintLogfile( STDOUT_FILENO, logtime, logtime_len, msg, XASH_COLORIZE_CONSOLE );
Sys_FlushStdout(); Sys_FlushStdout();
#endif #endif
} }
@@ -305,33 +267,44 @@ void Sys_PrintLog( const char *pMsg )
const struct tm *crt_tm; const struct tm *crt_tm;
char logtime[32] = ""; char logtime[32] = "";
static char lastchar; static char lastchar;
size_t len; qboolean print_time = true;
size_t len, logtime_len = 0;
time( &crt_time ); if( !lastchar || lastchar == '\n' )
crt_tm = localtime( &crt_time ); {
if( time( &crt_time ) >= 0 )
{
crt_tm = localtime( &crt_time );
if( crt_tm == NULL )
print_time = false;
}
}
else print_time = false;
if( !lastchar || lastchar == '\n') if( print_time )
strftime( logtime, sizeof( logtime ), "[%H:%M:%S] ", crt_tm ); //short time {
logtime_len = strftime( logtime, sizeof( logtime ), "[%H:%M:%S] ", crt_tm ); // short time
logtime_len = Q_min( logtime_len, sizeof( logtime ) - 1 ); // just in case
}
// spew to stdout // spew to stdout
Sys_PrintStdout( logtime, pMsg ); Sys_PrintStdout( logtime, logtime_len, pMsg );
len = Q_strlen( pMsg ); len = Q_strlen( pMsg );
if( !s_ld.logfile )
{
// save last char to detect when line was not ended
lastchar = len > 0 ? pMsg[len - 1] : 0;
return;
}
if( !lastchar || lastchar == '\n')
strftime( logtime, sizeof( logtime ), "[%Y:%m:%d|%H:%M:%S] ", crt_tm ); //full time
// save last char to detect when line was not ended // save last char to detect when line was not ended
lastchar = len > 0 ? pMsg[len - 1] : 0; lastchar = len > 0 ? pMsg[len - 1] : 0;
Sys_PrintLogfile( s_ld.logfileno, logtime, pMsg, false ); if( !s_ld.logfile )
return;
if( print_time )
{
logtime_len = strftime( logtime, sizeof( logtime ), "[%Y:%m:%d|%H:%M:%S] ", crt_tm ); //full time
logtime_len = Q_min( logtime_len, sizeof( logtime ) - 1 ); // just in case
}
Sys_PrintLogfile( s_ld.logfileno, logtime, logtime_len, pMsg, false );
Sys_FlushLogfile(); Sys_FlushLogfile();
} }

View File

@@ -234,28 +234,12 @@ qboolean Sys_GetIntFromCmdLine( const char* argName, int *out )
return true; return true;
} }
void Sys_SendKeyEvents( void )
{
#if XASH_WIN32
MSG msg;
while( PeekMessage( &msg, NULL, 0, 0, PM_NOREMOVE ))
{
if( !GetMessage( &msg, NULL, 0, 0 ))
Sys_Quit ();
TranslateMessage( &msg );
DispatchMessage( &msg );
}
#endif
}
//======================================================================= //=======================================================================
// DLL'S MANAGER SYSTEM // DLL'S MANAGER SYSTEM
//======================================================================= //=======================================================================
qboolean Sys_LoadLibrary( dll_info_t *dll ) qboolean Sys_LoadLibrary( dll_info_t *dll )
{ {
const dllfunc_t *func; size_t i;
string errorstring; string errorstring;
// check errors // check errors
@@ -267,12 +251,8 @@ qboolean Sys_LoadLibrary( dll_info_t *dll )
Con_Reportf( "%s: Loading %s", __func__, dll->name ); Con_Reportf( "%s: Loading %s", __func__, dll->name );
if( dll->fcts ) if( dll->fcts ) // lookup export table
{ ClearExports( dll->fcts, dll->num_fcts );
// lookup export table
for( func = dll->fcts; func && func->name != NULL; func++ )
*func->func = NULL;
}
if( !dll->link ) if( !dll->link )
dll->link = COM_LoadLibrary( dll->name, false, true ); // environment pathes dll->link = COM_LoadLibrary( dll->name, false, true ); // environment pathes
@@ -285,9 +265,10 @@ qboolean Sys_LoadLibrary( dll_info_t *dll )
} }
// Get the function adresses // Get the function adresses
for( func = dll->fcts; func && func->name != NULL; func++ ) for( i = 0; i < dll->num_fcts; i++ )
{ {
if( !( *func->func = Sys_GetProcAddress( dll, func->name ))) const dllfunc_t *func = &dll->fcts[i];
if( !( *func->func = COM_GetProcAddress( dll->link, func->name )))
{ {
Q_snprintf( errorstring, sizeof( errorstring ), "Sys_LoadLibrary: %s missing or invalid function (%s)\n", dll->name, func->name ); Q_snprintf( errorstring, sizeof( errorstring ), "Sys_LoadLibrary: %s missing or invalid function (%s)\n", dll->name, func->name );
goto error; goto error;
@@ -300,19 +281,11 @@ error:
Con_Reportf( " - failed\n" ); Con_Reportf( " - failed\n" );
Sys_FreeLibrary( dll ); // trying to free Sys_FreeLibrary( dll ); // trying to free
if( dll->crash ) Sys_Error( "%s", errorstring ); if( dll->crash ) Sys_Error( "%s", errorstring );
else Con_Reportf( S_ERROR "%s", errorstring ); else Con_Reportf( S_ERROR "%s", errorstring );
return false; return false;
} }
void* Sys_GetProcAddress( dll_info_t *dll, const char* name )
{
if( !dll || !dll->link ) // invalid desc
return NULL;
return (void *)COM_GetProcAddress( dll->link, name );
}
qboolean Sys_FreeLibrary( dll_info_t *dll ) qboolean Sys_FreeLibrary( dll_info_t *dll )
{ {
// invalid desc or alredy freed // invalid desc or alredy freed
@@ -330,6 +303,8 @@ qboolean Sys_FreeLibrary( dll_info_t *dll )
COM_FreeLibrary( dll->link ); COM_FreeLibrary( dll->link );
dll->link = NULL; dll->link = NULL;
ClearExports( dll->fcts, dll->num_fcts );
return true; return true;
} }
@@ -437,7 +412,7 @@ void Sys_Error( const char *error, ... )
Sys_WaitForQuit(); Sys_WaitForQuit();
} }
Sys_Quit(); Sys_Quit( "caught an error" );
} }
#if XASH_EMSCRIPTEN #if XASH_EMSCRIPTEN
@@ -460,9 +435,9 @@ void my_exit(int ret)
Sys_Quit Sys_Quit
================ ================
*/ */
void Sys_Quit( void ) void Sys_Quit( const char *reason )
{ {
Host_Shutdown(); Host_ShutdownWithReason( reason );
exit( error_on_exit ); exit( error_on_exit );
} }
@@ -565,7 +540,7 @@ qboolean Sys_NewInstance( const char *gamedir )
// just restart the entire thing // just restart the entire thing
printf( "envSetNextLoad exe: `%s`\n", exe ); printf( "envSetNextLoad exe: `%s`\n", exe );
printf( "envSetNextLoad argv:\n`%s`\n", newargs ); printf( "envSetNextLoad argv:\n`%s`\n", newargs );
Host_Shutdown( ); Host_ShutdownWithReason( "changing game" );
envSetNextLoad( exe, newargs ); envSetNextLoad( exe, newargs );
exit( 0 ); exit( 0 );
#else #else
@@ -603,7 +578,7 @@ qboolean Sys_NewInstance( const char *gamedir )
#if XASH_PSVITA #if XASH_PSVITA
// under normal circumstances it's always going to be the same path // under normal circumstances it's always going to be the same path
exe = strdup( "app0:/eboot.bin" ); exe = strdup( "app0:/eboot.bin" );
Host_Shutdown( ); Host_ShutdownWithReason( "changing game" );
sceAppMgrLoadExec( exe, newargs, NULL ); sceAppMgrLoadExec( exe, newargs, NULL );
#else #else
exelen = wai_getExecutablePath( NULL, 0, NULL ); exelen = wai_getExecutablePath( NULL, 0, NULL );
@@ -611,7 +586,7 @@ qboolean Sys_NewInstance( const char *gamedir )
wai_getExecutablePath( exe, exelen, NULL ); wai_getExecutablePath( exe, exelen, NULL );
exe[exelen] = 0; exe[exelen] = 0;
Host_Shutdown(); Host_ShutdownWithReason( "changing game" );
execv( exe, newargs ); execv( exe, newargs );
#endif #endif

View File

@@ -49,21 +49,17 @@ int Sys_CheckParm( const char *parm );
void Sys_Warn( const char *format, ... ) FORMAT_CHECK( 1 ); void Sys_Warn( const char *format, ... ) FORMAT_CHECK( 1 );
void Sys_Error( const char *error, ... ) FORMAT_CHECK( 1 ); void Sys_Error( const char *error, ... ) FORMAT_CHECK( 1 );
qboolean Sys_LoadLibrary( dll_info_t *dll ); qboolean Sys_LoadLibrary( dll_info_t *dll );
void* Sys_GetProcAddress( dll_info_t *dll, const char* name );
qboolean Sys_FreeLibrary( dll_info_t *dll ); qboolean Sys_FreeLibrary( dll_info_t *dll );
void Sys_ParseCommandLine( int argc, char **argv ); void Sys_ParseCommandLine( int argc, char **argv );
void Sys_SetupCrashHandler( void );
void Sys_RestoreCrashHandler( void );
void Sys_DebugBreak( void ); void Sys_DebugBreak( void );
#define Sys_GetParmFromCmdLine( parm, out ) _Sys_GetParmFromCmdLine( parm, out, sizeof( out )) #define Sys_GetParmFromCmdLine( parm, out ) _Sys_GetParmFromCmdLine( parm, out, sizeof( out ))
qboolean _Sys_GetParmFromCmdLine( const char *parm, char *out, size_t size ); qboolean _Sys_GetParmFromCmdLine( const char *parm, char *out, size_t size );
qboolean Sys_GetIntFromCmdLine( const char *parm, int *out ); qboolean Sys_GetIntFromCmdLine( const char *parm, int *out );
void Sys_SendKeyEvents( void );
void Sys_Print( const char *pMsg ); void Sys_Print( const char *pMsg );
void Sys_PrintLog( const char *pMsg ); void Sys_PrintLog( const char *pMsg );
void Sys_InitLog( void ); void Sys_InitLog( void );
void Sys_CloseLog( void ); void Sys_CloseLog( void );
void Sys_Quit( void ) NORETURN; void Sys_Quit( const char *reason ) NORETURN;
qboolean Sys_NewInstance( const char *gamedir ); qboolean Sys_NewInstance( const char *gamedir );
void *Sys_GetNativeObject( const char *obj ); void *Sys_GetNativeObject( const char *obj );

View File

@@ -16,11 +16,8 @@
#ifndef EDICT_H #ifndef EDICT_H
#define EDICT_H #define EDICT_H
#ifdef SUPPORT_BSP2_FORMAT #define MAX_ENT_LEAFS_32 24 // Orignally was 16
#define MAX_ENT_LEAFS 24 // Orignally was 16 #define MAX_ENT_LEAFS_16 48
#else
#define MAX_ENT_LEAFS 48
#endif
#include "progdefs.h" #include "progdefs.h"
@@ -33,11 +30,12 @@ struct edict_s
int headnode; // -1 to use normal leaf check int headnode; // -1 to use normal leaf check
int num_leafs; int num_leafs;
#ifdef SUPPORT_BSP2_FORMAT union
int leafnums[MAX_ENT_LEAFS]; {
#else int leafnums32[MAX_ENT_LEAFS_32];
short leafnums[MAX_ENT_LEAFS]; short leafnums16[MAX_ENT_LEAFS_16];
#endif };
float freetime; // sv.time when the object was freed float freetime; // sv.time when the object was freed
void* pvPrivateData; // Alloced and freed by engine, used by DLLs void* pvPrivateData; // Alloced and freed by engine, used by DLLs

View File

@@ -34,6 +34,7 @@ long pathconf( const char *path, int name ) { return -1; }
// part of libunwind; required by any dynamic lib that uses C++ exceptions // part of libunwind; required by any dynamic lib that uses C++ exceptions
extern void *_Unwind_Resume; extern void *_Unwind_Resume;
extern void *_Unwind_GetIPInfo;
// these are macros in our libc, so we need to wrap them // these are macros in our libc, so we need to wrap them
static int tolower_fn( int c ) { return tolower( c ); } static int tolower_fn( int c ) { return tolower( c ); }
@@ -55,6 +56,7 @@ static const solder_export_t aux_exports[] =
SOLDER_EXPORT_SYMBOL( strchrnul ), SOLDER_EXPORT_SYMBOL( strchrnul ),
SOLDER_EXPORT_SYMBOL( stpcpy ), SOLDER_EXPORT_SYMBOL( stpcpy ),
SOLDER_EXPORT_SYMBOL( _Unwind_Resume ), SOLDER_EXPORT_SYMBOL( _Unwind_Resume ),
SOLDER_EXPORT_SYMBOL( _Unwind_GetIPInfo ),
}; };
const solder_export_t *__solder_aux_exports = aux_exports; const solder_export_t *__solder_aux_exports = aux_exports;

View File

@@ -51,6 +51,7 @@ void IOS_LaunchDialog( void );
#if XASH_POSIX #if XASH_POSIX
void Posix_Daemonize( void ); void Posix_Daemonize( void );
void Posix_SetupSigtermHandling( void ); void Posix_SetupSigtermHandling( void );
char *Posix_Input( void );
#endif #endif
#if XASH_SDL #if XASH_SDL
@@ -177,6 +178,20 @@ static inline void Platform_Sleep( int msec )
#endif #endif
} }
#if XASH_WIN32 || XASH_FREEBSD || XASH_NETBSD || XASH_OPENBSD || XASH_ANDROID || XASH_LINUX
void Sys_SetupCrashHandler( void );
void Sys_RestoreCrashHandler( void );
#else
static inline void Sys_SetupCrashHandler( void )
{
}
static inline void Sys_RestoreCrashHandler( void )
{
}
#endif
/* /*
============================================================================== ==============================================================================
@@ -227,6 +242,17 @@ static inline void Platform_SetTimer( float time )
#endif #endif
} }
static inline char *Platform_Input( void )
{
#if XASH_WIN32
return Wcon_Input();
#elif XASH_POSIX && !XASH_MOBILE_PLATFORM && !XASH_LOW_MEMORY
return Posix_Input();
#else
return NULL;
#endif
}
/* /*
============================================================================== ==============================================================================

View File

@@ -0,0 +1,52 @@
/*
con_posix.c - reading from stdin
Copyright (C) 2024 Flying With Gauss
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 "platform/platform.h"
#if !XASH_MOBILE_PLATFORM && !XASH_LOW_MEMORY
// use with caution, running engine in Qt Creator may cause a freeze in read() call
// I have never encountered this bug anywhere else, so still enable by default
#include <sys/select.h>
char *Posix_Input( void )
{
fd_set rfds;
static char line[1024];
static int len;
struct timeval tv = { 0 };
if( !Host_IsDedicated( ))
return NULL;
FD_ZERO( &rfds );
FD_SET( 0, &rfds); // stdin
while( select( 1, &rfds, NULL, NULL, &tv ) > 0 )
{
if( read( 0, &line[len], 1 ) != 1 )
break;
if( line[len] == '\n' || len > ( sizeof( line ) - 2 ))
{
line[ ++len ] = 0;
len = 0;
return line;
}
len++;
tv.tv_sec = 0;
tv.tv_usec = 0;
}
return NULL;
}
#endif // !XASH_MOBILE_PLATFORM && !XASH_LOW_MEMORY

View File

@@ -0,0 +1,222 @@
/*
crashhandler.c - advanced crashhandler
Copyright (C) 2016 Mittorn
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"
// while this is mostly POSIX-compatible functions
// the contents of ucontext_t is platform-dependent
// before adding new OS here, check platform.h
#if XASH_FREEBSD || XASH_NETBSD || XASH_OPENBSD || XASH_ANDROID || XASH_LINUX
#ifndef XASH_OPENBSD
#include <ucontext.h>
#endif
#include <signal.h>
#include <sys/mman.h>
#include "library.h"
#define STACK_BACKTRACE_STR "Stack backtrace:\n"
#define STACK_DUMP_STR "Stack dump:\n"
#define STACK_BACKTRACE_STR_LEN ( sizeof( STACK_BACKTRACE_STR ) - 1 )
#define STACK_DUMP_STR_LEN ( sizeof( STACK_DUMP_STR ) - 1 )
#define ALIGN( x, y ) (((uintptr_t) ( x ) + (( y ) - 1 )) & ~(( y ) - 1 ))
static struct sigaction oldFilter;
static int Sys_PrintFrame( char *buf, int len, int i, void *addr )
{
Dl_info dlinfo;
if( len <= 0 )
return 0; // overflow
if( dladdr( addr, &dlinfo ))
{
if( dlinfo.dli_sname )
return Q_snprintf( buf, len, "%2d: %p <%s+%lu> (%s)\n", i, addr, dlinfo.dli_sname,
(unsigned long)addr - (unsigned long)dlinfo.dli_saddr, dlinfo.dli_fname ); // print symbol, module and address
return Q_snprintf( buf, len, "%2d: %p (%s)\n", i, addr, dlinfo.dli_fname ); // print module and address
}
return Q_snprintf( buf, len, "%2d: %p\n", i, addr ); // print only address
}
static void Sys_Crash( int signal, siginfo_t *si, void *context)
{
void *pc = NULL, **bp = NULL, **sp = NULL; // this must be set for every OS!
char message[8192];
int len, logfd, i = 0;
#if XASH_OPENBSD
struct sigcontext *ucontext = (struct sigcontext*)context;
#else
ucontext_t *ucontext = (ucontext_t*)context;
#endif
#if XASH_AMD64
#if XASH_FREEBSD
pc = (void*)ucontext->uc_mcontext.mc_rip;
bp = (void**)ucontext->uc_mcontext.mc_rbp;
sp = (void**)ucontext->uc_mcontext.mc_rsp;
#elif XASH_NETBSD
pc = (void*)ucontext->uc_mcontext.__gregs[_REG_RIP];
bp = (void**)ucontext->uc_mcontext.__gregs[_REG_RBP];
sp = (void**)ucontext->uc_mcontext.__gregs[_REG_RSP];
#elif XASH_OPENBSD
pc = (void*)ucontext->sc_rip;
bp = (void**)ucontext->sc_rbp;
sp = (void**)ucontext->sc_rsp;
#else
pc = (void*)ucontext->uc_mcontext.gregs[REG_RIP];
bp = (void**)ucontext->uc_mcontext.gregs[REG_RBP];
sp = (void**)ucontext->uc_mcontext.gregs[REG_RSP];
#endif
#elif XASH_X86
#if XASH_FREEBSD
pc = (void*)ucontext->uc_mcontext.mc_eip;
bp = (void**)ucontext->uc_mcontext.mc_ebp;
sp = (void**)ucontext->uc_mcontext.mc_esp;
#elif XASH_NETBSD
pc = (void*)ucontext->uc_mcontext.__gregs[_REG_EIP];
bp = (void**)ucontext->uc_mcontext.__gregs[_REG_EBP];
sp = (void**)ucontext->uc_mcontext.__gregs[_REG_ESP];
#elif XASH_OPENBSD
pc = (void*)ucontext->sc_eip;
bp = (void**)ucontext->sc_ebp;
sp = (void**)ucontext->sc_esp;
#else
pc = (void*)ucontext->uc_mcontext.gregs[REG_EIP];
bp = (void**)ucontext->uc_mcontext.gregs[REG_EBP];
sp = (void**)ucontext->uc_mcontext.gregs[REG_ESP];
#endif
#elif XASH_ARM && XASH_64BIT
pc = (void*)ucontext->uc_mcontext.pc;
bp = (void*)ucontext->uc_mcontext.regs[29];
sp = (void*)ucontext->uc_mcontext.sp;
#elif XASH_ARM
pc = (void*)ucontext->uc_mcontext.arm_pc;
bp = (void*)ucontext->uc_mcontext.arm_fp;
sp = (void*)ucontext->uc_mcontext.arm_sp;
#endif
// safe actions first, stack and memory may be corrupted
len = Q_snprintf( message, sizeof( message ), "Ver: " XASH_ENGINE_NAME " " XASH_VERSION " (build %i-%s, %s-%s)\n",
Q_buildnum(), Q_buildcommit(), Q_buildos(), Q_buildarch() );
#if !XASH_FREEBSD && !XASH_NETBSD && !XASH_OPENBSD
len += Q_snprintf( message + len, sizeof( message ) - len, "Crash: signal %d errno %d with code %d at %p %p\n", signal, si->si_errno, si->si_code, si->si_addr, si->si_ptr );
#else
len += Q_snprintf( message + len, sizeof( message ) - len, "Crash: signal %d errno %d with code %d at %p\n", signal, si->si_errno, si->si_code, si->si_addr );
#endif
write( STDERR_FILENO, message, len );
// flush buffers before writing directly to descriptors
fflush( stdout );
fflush( stderr );
// now get log fd and write trace directly to log
logfd = Sys_LogFileNo();
write( logfd, message, len );
if( pc && bp && sp )
{
size_t pagesize = sysconf( _SC_PAGESIZE );
// try to print backtrace
write( STDERR_FILENO, STACK_BACKTRACE_STR, STACK_BACKTRACE_STR_LEN );
write( logfd, STACK_BACKTRACE_STR, STACK_BACKTRACE_STR_LEN );
Q_strncpy( message + len, STACK_BACKTRACE_STR, sizeof( message ) - len );
len += STACK_BACKTRACE_STR_LEN;
// false on success, true on failure
#define try_allow_read(pointer, pagesize) \
(( mprotect( (char *)ALIGN( (pointer), (pagesize) ), (pagesize), PROT_READ | PROT_WRITE | PROT_EXEC ) == -1 ) && \
( mprotect( (char *)ALIGN( (pointer), (pagesize) ), (pagesize), PROT_READ | PROT_EXEC ) == -1 ) && \
( mprotect( (char *)ALIGN( (pointer), (pagesize) ), (pagesize), PROT_READ | PROT_WRITE ) == -1 ) && \
( mprotect( (char *)ALIGN( (pointer), (pagesize) ), (pagesize), PROT_READ ) == -1 ))
do
{
int line = Sys_PrintFrame( message + len, sizeof( message ) - len, ++i, pc);
write( STDERR_FILENO, message + len, line );
write( logfd, message + len, line );
len += line;
//if( !dladdr(bp,0) ) break; // only when bp is in module
if( try_allow_read( bp, pagesize ) )
break;
if( try_allow_read( bp[0], pagesize ) )
break;
pc = bp[1];
bp = (void**)bp[0];
}
while( bp && i < 128 );
// try to print stack
write( STDERR_FILENO, STACK_DUMP_STR, STACK_DUMP_STR_LEN );
write( logfd, STACK_DUMP_STR, STACK_DUMP_STR_LEN );
Q_strncpy( message + len, STACK_DUMP_STR, sizeof( message ) - len );
len += STACK_DUMP_STR_LEN;
if( !try_allow_read( sp, pagesize ) )
{
for( i = 0; i < 32; i++ )
{
int line = Sys_PrintFrame( message + len, sizeof( message ) - len, i, sp[i] );
write( STDERR_FILENO, message + len, line );
write( logfd, message + len, line );
len += line;
}
}
#undef try_allow_read
}
// put MessageBox as Sys_Error
Msg( "%s\n", message );
#ifdef XASH_SDL
SDL_SetWindowGrab( host.hWnd, SDL_FALSE );
#endif
host.crashed = true;
Platform_MessageBox( "Xash Error", message, false );
// log saved, now we can try to save configs and close log correctly, it may crash
if( host.type == HOST_NORMAL )
CL_Crashed();
host.status = HOST_CRASHED;
Sys_Quit( "crashed" );
}
void Sys_SetupCrashHandler( void )
{
struct sigaction act = { 0 };
act.sa_sigaction = Sys_Crash;
act.sa_flags = SA_SIGINFO | SA_ONSTACK;
sigaction( SIGSEGV, &act, &oldFilter );
sigaction( SIGABRT, &act, &oldFilter );
sigaction( SIGBUS, &act, &oldFilter );
sigaction( SIGILL, &act, &oldFilter );
}
void Sys_RestoreCrashHandler( void )
{
sigaction( SIGSEGV, &oldFilter, NULL );
sigaction( SIGABRT, &oldFilter, NULL );
sigaction( SIGBUS, &oldFilter, NULL );
sigaction( SIGILL, &oldFilter, NULL );
}
#endif // XASH_FREEBSD || XASH_NETBSD || XASH_OPENBSD || XASH_ANDROID || XASH_LINUX

View File

@@ -148,7 +148,9 @@ void Posix_Daemonize( void )
static void Posix_SigtermCallback( int signal ) static void Posix_SigtermCallback( int signal )
{ {
Sys_Quit(); string reason;
Con_Printf( reason, sizeof( reason ), "caught signal %d", signal );
Sys_Quit( reason );
} }
void Posix_SetupSigtermHandling( void ) void Posix_SetupSigtermHandling( void )

View File

@@ -396,8 +396,12 @@ static void SDLash_ActiveEvent( int gain )
} }
#endif #endif
host.status = HOST_NOFOCUS; host.status = HOST_NOFOCUS;
if( cls.key_dest == key_game ) if( cls.key_dest == key_game )
{
Key_ClearStates();
IN_DeactivateMouse(); IN_DeactivateMouse();
}
host.force_draw_version_time = host.realtime + 2.0; host.force_draw_version_time = host.realtime + 2.0;
VID_RestoreScreenResolution(); VID_RestoreScreenResolution();
@@ -498,7 +502,7 @@ static void SDLash_EventHandler( SDL_Event *event )
break; break;
case SDL_QUIT: case SDL_QUIT:
Sys_Quit(); Sys_Quit( "caught SDL_QUIT" );
break; break;
#if SDL_VERSION_ATLEAST( 2, 0, 0 ) #if SDL_VERSION_ATLEAST( 2, 0, 0 )
case SDL_MOUSEWHEEL: case SDL_MOUSEWHEEL:

View File

@@ -99,13 +99,43 @@ qboolean SNDDMA_Init( void )
{ {
SDL_AudioSpec desired, obtained; SDL_AudioSpec desired, obtained;
int samplecount; int samplecount;
const char *driver = NULL;
// Modders often tend to use proprietary crappy solutions
// like FMOD to play music, sometimes even with versions outdated by a few decades!
//
// As these bullshit sound engines prefer to use DirectSound, we ask SDL2 to do
// the same. Why you might ask? If SDL2 uses another audio API, like WASAPI on
// more modern versions of Windows, it breaks the logic inside Windows, and the whole
// application could hang in WaitFor{Single,Multiple}Object function, either called by
// SDL2 if FMOD was shut down first, or deep in dsound.dll->fmod.dll if SDL2 audio
// was shut down first.
//
// I honestly don't know who is the real culprit here: FMOD, HL modders, Windows, SDL2
// or us.
//
// Also, fun note, GoldSrc seems doesn't use SDL2 for sound stuff at all, as nothing
// reference SDL audio functions there. It's probably has DirectSound backend, that's
// why modders never stumble upon this bug.
#if XASH_WIN32
driver = "directsound";
if( SDL_getenv( "SDL_AUDIODRIVER" ))
driver = NULL; // let SDL2 and user decide
SDL_SetHint( SDL_HINT_AUDIODRIVER, driver );
#endif // XASH_WIN32
// even if we don't have PA // even if we don't have PA
// we still can safely set env variables // we still can safely set env variables
SDL_setenv( "PULSE_PROP_application.name", GI->title, 1 ); SDL_setenv( "PULSE_PROP_application.name", GI->title, 1 );
SDL_setenv( "PULSE_PROP_media.role", "game", 1 ); SDL_setenv( "PULSE_PROP_media.role", "game", 1 );
if( SDL_Init( SDL_INIT_AUDIO )) // reinitialize SDL with our driver just in case
if( SDL_WasInit( SDL_INIT_AUDIO ))
SDL_QuitSubSystem( SDL_INIT_AUDIO );
if( SDL_InitSubSystem( SDL_INIT_AUDIO ))
{ {
Con_Reportf( S_ERROR "Audio: SDL: %s \n", SDL_GetError( ) ); Con_Reportf( S_ERROR "Audio: SDL: %s \n", SDL_GetError( ) );
return false; return false;
@@ -212,7 +242,7 @@ void SNDDMA_Shutdown( void )
} }
#if !XASH_EMSCRIPTEN #if !XASH_EMSCRIPTEN
if( SDL_WasInit( SDL_INIT_AUDIO ) ) if( SDL_WasInit( SDL_INIT_AUDIO ))
SDL_QuitSubSystem( SDL_INIT_AUDIO ); SDL_QuitSubSystem( SDL_INIT_AUDIO );
#endif #endif

View File

@@ -41,8 +41,54 @@ void Platform_MessageBox( const char *title, const char *message, qboolean paren
} }
#endif // XASH_MESSAGEBOX == MSGBOX_SDL #endif // XASH_MESSAGEBOX == MSGBOX_SDL
static const char *SDLash_CategoryToString( int category )
{
switch( category )
{
case SDL_LOG_CATEGORY_APPLICATION: return "App";
case SDL_LOG_CATEGORY_ERROR: return "Error";
case SDL_LOG_CATEGORY_ASSERT: return "Assert";
case SDL_LOG_CATEGORY_SYSTEM: return "System";
case SDL_LOG_CATEGORY_AUDIO: return "Audio";
case SDL_LOG_CATEGORY_VIDEO: return "Video";
case SDL_LOG_CATEGORY_RENDER: return "Render";
case SDL_LOG_CATEGORY_INPUT: return "Input";
case SDL_LOG_CATEGORY_TEST: return "Test";
default: return "Unknown";
}
}
static void SDLCALL SDLash_LogOutputFunction( void *userdata, int category, SDL_LogPriority priority, const char *message )
{
switch( priority )
{
case SDL_LOG_PRIORITY_CRITICAL:
case SDL_LOG_PRIORITY_ERROR:
Con_Printf( S_ERROR S_BLUE "SDL" S_DEFAULT ": [%s] %s\n", SDLash_CategoryToString( category ), message );
break;
case SDL_LOG_PRIORITY_WARN:
Con_DPrintf( S_WARN S_BLUE "SDL" S_DEFAULT ": [%s] %s\n", SDLash_CategoryToString( category ), message );
break;
case SDL_LOG_PRIORITY_INFO:
Con_Reportf( S_NOTE S_BLUE "SDL" S_DEFAULT ": [%s] %s\n", SDLash_CategoryToString( category ), message );
break;
default:
Con_Reportf( S_BLUE "SDL" S_DEFAULT ": [%s] %s\n", SDLash_CategoryToString( category ), message );
break;
}
}
void SDLash_Init( void ) void SDLash_Init( void )
{ {
SDL_LogSetOutputFunction( SDLash_LogOutputFunction, NULL );
if( host_developer.value >= 2 )
SDL_LogSetAllPriority( SDL_LOG_PRIORITY_VERBOSE );
else if( host_developer.value >= 1 )
SDL_LogSetAllPriority( SDL_LOG_PRIORITY_WARN );
else
SDL_LogSetAllPriority( SDL_LOG_PRIORITY_ERROR );
#ifndef SDL_INIT_EVENTS #ifndef SDL_INIT_EVENTS
#define SDL_INIT_EVENTS 0 #define SDL_INIT_EVENTS 0
#endif #endif

View File

@@ -0,0 +1,331 @@
/*
crashhandler.c - advanced crashhandler
Copyright (C) 2016 Mittorn
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 "platform/platform.h"
#define DBGHELP 1 // we always enable dbghelp.dll on Windows targets
#if DBGHELP
#include <winnt.h>
#include <dbghelp.h>
#include <psapi.h>
#include <time.h>
#ifndef XASH_SDL
typedef ULONG_PTR DWORD_PTR, *PDWORD_PTR;
#endif
static int Sys_ModuleName( HANDLE process, char *name, void *address, int len )
{
DWORD_PTR baseAddress = 0;
static HMODULE *moduleArray;
static unsigned int moduleCount;
LPBYTE moduleArrayBytes;
DWORD bytesRequired;
int i;
if( len < 3 )
return 0;
if( !moduleArray && EnumProcessModules( process, NULL, 0, &bytesRequired ) )
{
if ( bytesRequired )
{
moduleArrayBytes = (LPBYTE)LocalAlloc( LPTR, bytesRequired );
if( moduleArrayBytes && EnumProcessModules( process, (HMODULE *)moduleArrayBytes, bytesRequired, &bytesRequired ) )
{
moduleCount = bytesRequired / sizeof( HMODULE );
moduleArray = (HMODULE *)moduleArrayBytes;
}
}
}
for( i = 0; i < moduleCount; i++ )
{
MODULEINFO info;
GetModuleInformation( process, moduleArray[i], &info, sizeof(MODULEINFO) );
if( ( address > info.lpBaseOfDll ) &&
( (DWORD64)address < (DWORD64)info.lpBaseOfDll + (DWORD64)info.SizeOfImage ) )
return GetModuleBaseName( process, moduleArray[i], name, len );
}
return Q_snprintf( name, len, "???" );
}
static void Sys_StackTrace( PEXCEPTION_POINTERS pInfo )
{
char message[8192]; // match *nix Sys_Crash
int len = 0;
size_t i;
HANDLE process = GetCurrentProcess();
HANDLE thread = GetCurrentThread();
IMAGEHLP_LINE64 line;
DWORD dline = 0;
DWORD options;
CONTEXT context;
STACKFRAME64 stackframe;
DWORD image;
context = *pInfo->ContextRecord;
options = SymGetOptions();
options |= SYMOPT_DEBUG;
options |= SYMOPT_LOAD_LINES;
SymSetOptions( options );
SymInitialize( process, NULL, TRUE );
ZeroMemory( &stackframe, sizeof( STACKFRAME64 ));
#ifdef _M_IX86
image = IMAGE_FILE_MACHINE_I386;
stackframe.AddrPC.Offset = context.Eip;
stackframe.AddrPC.Mode = AddrModeFlat;
stackframe.AddrFrame.Offset = context.Ebp;
stackframe.AddrFrame.Mode = AddrModeFlat;
stackframe.AddrStack.Offset = context.Esp;
stackframe.AddrStack.Mode = AddrModeFlat;
#elif _M_X64
image = IMAGE_FILE_MACHINE_AMD64;
stackframe.AddrPC.Offset = context.Rip;
stackframe.AddrPC.Mode = AddrModeFlat;
stackframe.AddrFrame.Offset = context.Rsp;
stackframe.AddrFrame.Mode = AddrModeFlat;
stackframe.AddrStack.Offset = context.Rsp;
stackframe.AddrStack.Mode = AddrModeFlat;
#elif _M_IA64
image = IMAGE_FILE_MACHINE_IA64;
stackframe.AddrPC.Offset = context.StIIP;
stackframe.AddrPC.Mode = AddrModeFlat;
stackframe.AddrFrame.Offset = context.IntSp;
stackframe.AddrFrame.Mode = AddrModeFlat;
stackframe.AddrBStore.Offset = context.RsBSP;
stackframe.AddrBStore.Mode = AddrModeFlat;
stackframe.AddrStack.Offset = context.IntSp;
stackframe.AddrStack.Mode = AddrModeFlat;
#elif _M_ARM
image = IMAGE_FILE_MACHINE_ARMNT;
stackframe.AddrPC.Offset = context.Pc;
stackframe.AddrPC.Mode = AddrModeFlat;
stackframe.AddrFrame.Offset = context.R11;
stackframe.AddrFrame.Mode = AddrModeFlat;
stackframe.AddrStack.Offset = context.Sp;
stackframe.AddrStack.Mode = AddrModeFlat;
#elif _M_ARM64
image = IMAGE_FILE_MACHINE_ARM64;
stackframe.AddrPC.Offset = context.Pc;
stackframe.AddrPC.Mode = AddrModeFlat;
stackframe.AddrFrame.Offset = context.Fp;
stackframe.AddrFrame.Mode = AddrModeFlat;
stackframe.AddrStack.Offset = context.Sp;
stackframe.AddrStack.Mode = AddrModeFlat;
#elif
#error
#endif
len = Q_snprintf( message, sizeof( message ), "Ver: " XASH_ENGINE_NAME " " XASH_VERSION " (build %i-%s, %s-%s)\n",
Q_buildnum(), Q_buildcommit(), Q_buildos(), Q_buildarch() );
len += Q_snprintf( message + len, 1024 - len, "Sys_Crash: address %p, code %p\n",
pInfo->ExceptionRecord->ExceptionAddress, (void*)pInfo->ExceptionRecord->ExceptionCode );
if( SymGetLineFromAddr64( process, (DWORD64)pInfo->ExceptionRecord->ExceptionAddress, &dline, &line ) )
{
len += Q_snprintf(message + len, 1024 - len, "Exception: %s:%d:%d\n",
(char*)line.FileName, (int)line.LineNumber, (int)dline);
}
if( SymGetLineFromAddr64( process, stackframe.AddrPC.Offset, &dline, &line ) )
{
len += Q_snprintf(message + len, 1024 - len,"PC: %s:%d:%d\n",
(char*)line.FileName, (int)line.LineNumber, (int)dline);
}
if( SymGetLineFromAddr64( process, stackframe.AddrFrame.Offset, &dline, &line ) )
{
len += Q_snprintf(message + len, 1024 - len,"Frame: %s:%d:%d\n",
(char*)line.FileName, (int)line.LineNumber, (int)dline);
}
for( i = 0; i < 25; i++ )
{
char buffer[sizeof(SYMBOL_INFO) + MAX_SYM_NAME * sizeof(TCHAR)];
PSYMBOL_INFO symbol = (PSYMBOL_INFO)buffer;
BOOL result = StackWalk64(
image, process, thread,
&stackframe, &context, NULL,
SymFunctionTableAccess64, SymGetModuleBase64, NULL);
DWORD64 displacement = 0;
if( !result )
break;
symbol->SizeOfStruct = sizeof(SYMBOL_INFO);
symbol->MaxNameLen = MAX_SYM_NAME;
len += Q_snprintf( message + len, 1024 - len, "% 2d %p",
i, (void*)stackframe.AddrPC.Offset );
if( SymFromAddr( process, stackframe.AddrPC.Offset, &displacement, symbol ) )
{
len += Q_snprintf( message + len, 1024 - len, " %s ", symbol->Name );
}
if( SymGetLineFromAddr64( process, stackframe.AddrPC.Offset, &dline, &line ) )
{
len += Q_snprintf(message + len, 1024 - len,"(%s:%d:%d) ",
(char*)line.FileName, (int)line.LineNumber, (int)dline);
}
len += Q_snprintf( message + len, 1024 - len, "(");
len += Sys_ModuleName( process, message + len, (void*)stackframe.AddrPC.Offset, 1024 - len );
len += Q_snprintf( message + len, 1024 - len, ")\n");
}
#if XASH_SDL == 2
if( host.type != HOST_DEDICATED ) // let system to restart server automaticly
SDL_ShowSimpleMessageBox( SDL_MESSAGEBOX_ERROR, "Sys_Crash", message, host.hWnd );
#endif
Sys_PrintLog( message );
SymCleanup( process );
}
static void Sys_GetProcessName( char *processName, size_t bufferSize )
{
char fullpath[MAX_PATH];
GetModuleBaseName( GetCurrentProcess(), NULL, fullpath, sizeof( fullpath ) - 1 );
COM_FileBase( fullpath, processName, bufferSize );
}
static void Sys_GetMinidumpFileName( const char *processName, char *mdmpFileName, size_t bufferSize )
{
time_t currentUtcTime = time( NULL );
struct tm *currentLocalTime = localtime( &currentUtcTime );
Q_snprintf( mdmpFileName, bufferSize, "%s_%s_crash_%d%.2d%.2d_%.2d%.2d%.2d.mdmp",
processName,
Q_buildcommit(),
currentLocalTime->tm_year + 1900,
currentLocalTime->tm_mon + 1,
currentLocalTime->tm_mday,
currentLocalTime->tm_hour,
currentLocalTime->tm_min,
currentLocalTime->tm_sec);
}
static qboolean Sys_WriteMinidump(PEXCEPTION_POINTERS exceptionInfo, MINIDUMP_TYPE minidumpType)
{
HRESULT errorCode;
string processName;
string mdmpFileName;
MINIDUMP_EXCEPTION_INFORMATION minidumpInfo;
Sys_GetProcessName( processName, sizeof( processName ));
Sys_GetMinidumpFileName( processName, mdmpFileName, sizeof( mdmpFileName ));
SetLastError( NOERROR );
HANDLE fileHandle = CreateFile(
mdmpFileName, GENERIC_WRITE, FILE_SHARE_WRITE,
NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);
errorCode = HRESULT_FROM_WIN32( GetLastError( ));
if( !SUCCEEDED( errorCode )) {
CloseHandle( fileHandle );
return false;
}
minidumpInfo.ThreadId = GetCurrentThreadId();
minidumpInfo.ExceptionPointers = exceptionInfo;
minidumpInfo.ClientPointers = FALSE;
qboolean status = MiniDumpWriteDump(
GetCurrentProcess(), GetCurrentProcessId(), fileHandle,
minidumpType, &minidumpInfo, NULL, NULL);
CloseHandle( fileHandle );
return status;
}
#endif /* DBGHELP */
static LPTOP_LEVEL_EXCEPTION_FILTER oldFilter;
static long _stdcall Sys_Crash( PEXCEPTION_POINTERS pInfo )
{
// save config
if( host.status != HOST_CRASHED )
{
// check to avoid recursive call
host.crashed = true;
#ifdef XASH_SDL
SDL_SetWindowGrab( host.hWnd, SDL_FALSE );
#endif // XASH_SDL
#if DBGHELP
Sys_StackTrace( pInfo );
#else
Sys_Warn( "Sys_Crash: call %p at address %p", pInfo->ExceptionRecord->ExceptionAddress, pInfo->ExceptionRecord->ExceptionCode );
#endif
if( host.type == HOST_NORMAL )
CL_Crashed(); // tell client about crash
else host.status = HOST_CRASHED;
#if DBGHELP
if( Sys_CheckParm( "-minidumps" ))
{
int minidumpFlags = (
MiniDumpWithDataSegs |
MiniDumpWithCodeSegs |
MiniDumpWithHandleData |
MiniDumpWithFullMemory |
MiniDumpWithFullMemoryInfo |
MiniDumpWithIndirectlyReferencedMemory |
MiniDumpWithThreadInfo |
MiniDumpWithModuleHeaders);
if( !Sys_WriteMinidump( pInfo, (MINIDUMP_TYPE)minidumpFlags )) {
// fallback method, create minidump with minimal info in it
Sys_WriteMinidump( pInfo, MiniDumpWithDataSegs );
}
}
#endif
if( host_developer.value <= 0 )
{
// no reason to call debugger in release build - just exit
Sys_Quit( "crashed" );
return EXCEPTION_CONTINUE_EXECUTION;
}
// all other states keep unchanged to let debugger find bug
Sys_DestroyConsole();
}
if( oldFilter )
return oldFilter( pInfo );
return EXCEPTION_CONTINUE_EXECUTION;
}
void Sys_SetupCrashHandler( void )
{
SetErrorMode( SEM_FAILCRITICALERRORS ); // no abort/retry/fail errors
oldFilter = SetUnhandledExceptionFilter( Sys_Crash );
}
void Sys_RestoreCrashHandler( void )
{
// restore filter
if( oldFilter ) SetUnhandledExceptionFilter( oldFilter );
}

View File

@@ -55,3 +55,4 @@ void Platform_MessageBox( const char *title, const char *message, qboolean paren
MessageBox( parentMainWindow ? host.hWnd : NULL, message, title, MB_OK|MB_SETFOREGROUND|MB_ICONSTOP ); MessageBox( parentMainWindow ? host.hWnd : NULL, message, title, MB_OK|MB_SETFOREGROUND|MB_ICONSTOP );
} }
#endif // XASH_MESSAGEBOX == MSGBOX_WIN32 #endif // XASH_MESSAGEBOX == MSGBOX_WIN32

View File

@@ -80,6 +80,7 @@ GNU General Public License for more details.
#define MODEL_LIQUID BIT( 2 ) // model has only point hull #define MODEL_LIQUID BIT( 2 ) // model has only point hull
#define MODEL_TRANSPARENT BIT( 3 ) // have transparent surfaces #define MODEL_TRANSPARENT BIT( 3 ) // have transparent surfaces
#define MODEL_COLORED_LIGHTING BIT( 4 ) // lightmaps stored as RGB #define MODEL_COLORED_LIGHTING BIT( 4 ) // lightmaps stored as RGB
#define MODEL_WORLD BIT( 29 ) // it's a worldmodel #define MODEL_WORLD BIT( 29 ) // it's a worldmodel
#define MODEL_CLIENT BIT( 30 ) // client sprite #define MODEL_CLIENT BIT( 30 ) // client sprite

View File

@@ -67,6 +67,8 @@ extern int SV_UPDATE_BACKUP;
#define MAX_PUSHED_ENTS 256 #define MAX_PUSHED_ENTS 256
#define MAX_VIEWENTS 128 #define MAX_VIEWENTS 128
#define MAX_ENT_LEAFS( ext ) (( ext ) ? MAX_ENT_LEAFS_32 : MAX_ENT_LEAFS_16 )
#define FCL_RESEND_USERINFO BIT( 0 ) #define FCL_RESEND_USERINFO BIT( 0 )
#define FCL_RESEND_MOVEVARS BIT( 1 ) #define FCL_RESEND_MOVEVARS BIT( 1 )
#define FCL_SKIP_NET_MESSAGE BIT( 2 ) #define FCL_SKIP_NET_MESSAGE BIT( 2 )
@@ -593,7 +595,7 @@ qboolean SV_CheckID( const char *id );
// sv_frame.c // sv_frame.c
// //
void SV_InactivateClients( void ); void SV_InactivateClients( void );
int SV_FindBestBaselineForStatic( int index, entity_state_t **baseline, entity_state_t *to ); int SV_FindBestBaseline( int index, entity_state_t **baseline, entity_state_t *to, client_frame_t *frame, qboolean player );
void SV_SkipUpdates( void ); void SV_SkipUpdates( void );
// //

View File

@@ -3022,7 +3022,6 @@ static const ucmd_t ucmds[] =
{ "disconnect", SV_Disconnect_f }, { "disconnect", SV_Disconnect_f },
{ "userinfo", SV_UpdateUserinfo_f }, { "userinfo", SV_UpdateUserinfo_f },
{ "_sv_build_info", SV_SendBuildInfo_f }, { "_sv_build_info", SV_SendBuildInfo_f },
{ NULL, NULL }
}; };
static const ucmd_t enttoolscmds[] = static const ucmd_t enttoolscmds[] =
@@ -3032,7 +3031,6 @@ static const ucmd_t enttoolscmds[] =
{ "ent_fire", SV_EntFire_f }, { "ent_fire", SV_EntFire_f },
{ "ent_create", SV_EntCreate_f }, { "ent_create", SV_EntCreate_f },
{ "ent_getvars", SV_EntGetVars_f }, { "ent_getvars", SV_EntGetVars_f },
{ NULL, NULL }
}; };
/* /*
@@ -3042,25 +3040,30 @@ SV_ExecuteUserCommand
*/ */
static void SV_ExecuteClientCommand( sv_client_t *cl, const char *s ) static void SV_ExecuteClientCommand( sv_client_t *cl, const char *s )
{ {
const ucmd_t *u; int i;
Cmd_TokenizeString( s ); Cmd_TokenizeString( s );
for( u = ucmds; u->name; u++ ) for( i = 0; i < ARRAYSIZE( ucmds ); i++ )
{ {
const ucmd_t *u = &ucmds[i];
if( !Q_strcmp( Cmd_Argv( 0 ), u->name )) if( !Q_strcmp( Cmd_Argv( 0 ), u->name ))
{ {
if( !u->func( cl )) if( !u->func( cl ))
Con_Printf( "'%s' is not valid from the console\n", u->name ); Con_Printf( "'%s' is not valid from the console\n", u->name );
else Con_Reportf( "ucmd->%s()\n", u->name ); else
break; Con_Reportf( "ucmd->%s()\n", u->name );
return;
} }
} }
if( !u->name && sv_enttools_enable.value > 0.0f && !sv.background ) if( sv_enttools_enable.value > 0.0f && !sv.background )
{ {
for( u = enttoolscmds; u->name; u++ ) for( i = 0; i < ARRAYSIZE( enttoolscmds ); i++ )
{ {
const ucmd_t *u = &enttoolscmds[i];
if( !Q_strcmp( Cmd_Argv( 0 ), u->name )) if( !Q_strcmp( Cmd_Argv( 0 ), u->name ))
{ {
Con_Reportf( "enttools->%s(): %s\n", u->name, s ); Con_Reportf( "enttools->%s(): %s\n", u->name, s );
@@ -3070,12 +3073,12 @@ static void SV_ExecuteClientCommand( sv_client_t *cl, const char *s )
if( u->func ) if( u->func )
u->func( cl ); u->func( cl );
break; return;
} }
} }
} }
if( !u->name && sv.state == ss_active ) if( sv.state == ss_active )
{ {
qboolean fullupdate = !Q_strcmp( Cmd_Argv( 0 ), "fullupdate" ); qboolean fullupdate = !Q_strcmp( Cmd_Argv( 0 ), "fullupdate" );

View File

@@ -846,7 +846,7 @@ Kick everyone off, possibly in preparation for a new game
*/ */
static void SV_KillServer_f( void ) static void SV_KillServer_f( void )
{ {
Host_ShutdownServer(); SV_Shutdown( "Server was killed due to shutdownserver command\n" );
} }
/* /*
@@ -954,6 +954,22 @@ static void Rcon_Redirect_f( void )
Msg( "Redirection enabled for next %d lines\n", lines ); Msg( "Redirection enabled for next %d lines\n", lines );
} }
static void SV_ListMessages_f( void )
{
int i;
Con_Printf( "num size name\n" );
for( i = 1; i < MAX_USER_MESSAGES; i++ )
{
if( !COM_CheckStringEmpty( svgame.msg[i].name ))
break;
Con_Printf( "%3d\t%3d\t%s\n", svgame.msg[i].number, svgame.msg[i].size, svgame.msg[i].name );
}
Con_Printf( "Total %i messages\n", i - 1 );
}
/* /*
================== ==================
SV_InitHostCommands SV_InitHostCommands
@@ -1006,6 +1022,7 @@ void SV_InitOperatorCommands( void )
Cmd_AddCommand( "logaddress", SV_SetLogAddress_f, "sets address and port for remote logging host" ); Cmd_AddCommand( "logaddress", SV_SetLogAddress_f, "sets address and port for remote logging host" );
Cmd_AddCommand( "log", SV_ServerLog_f, "enables logging to file" ); Cmd_AddCommand( "log", SV_ServerLog_f, "enables logging to file" );
Cmd_AddCommand( "str64stats", SV_PrintStr64Stats_f, "print engine pool string statistics" ); Cmd_AddCommand( "str64stats", SV_PrintStr64Stats_f, "print engine pool string statistics" );
Cmd_AddCommand( "sv_list_messages", SV_ListMessages_f, "list registered user messages" );
if( host.type == HOST_NORMAL ) if( host.type == HOST_NORMAL )
{ {

View File

@@ -178,9 +178,10 @@ Encode a client frame onto the network channel
SV_FindBestBaseline SV_FindBestBaseline
trying to deltas with previous entities trying to deltas with previous entities
set frame to NULL to check for static entities
============= =============
*/ */
static int SV_FindBestBaseline( sv_client_t *cl, int index, entity_state_t **baseline, entity_state_t *to, client_frame_t *frame, qboolean player ) int SV_FindBestBaseline( int index, entity_state_t **baseline, entity_state_t *to, client_frame_t *frame, qboolean player )
{ {
int bestBitCount; int bestBitCount;
int i, bitCount; int i, bitCount;
@@ -193,7 +194,13 @@ static int SV_FindBestBaseline( sv_client_t *cl, int index, entity_state_t **bas
for( i = index - 1; bestBitCount > 0 && i >= 0 && ( index - i ) < ( MAX_CUSTOM_BASELINES - 1 ); i-- ) for( i = index - 1; bestBitCount > 0 && i >= 0 && ( index - i ) < ( MAX_CUSTOM_BASELINES - 1 ); i-- )
{ {
// don't worry about underflow in circular buffer // don't worry about underflow in circular buffer
entity_state_t *test = &svs.packet_entities[(frame->first_entity+i) % svs.num_client_entities]; entity_state_t *test;
// if set, then it's normal entity
if( frame != NULL )
test = &svs.packet_entities[(frame->first_entity+i) % svs.num_client_entities];
else
test = &svs.static_entities[i];
if( to->entityType == test->entityType ) if( to->entityType == test->entityType )
{ {
@@ -209,44 +216,12 @@ static int SV_FindBestBaseline( sv_client_t *cl, int index, entity_state_t **bas
// using delta from previous entity as baseline for current // using delta from previous entity as baseline for current
if( index != bestfound ) if( index != bestfound )
*baseline = &svs.packet_entities[(frame->first_entity+bestfound) % svs.num_client_entities];
return index - bestfound;
}
/*
=============
SV_FindBestBaselineForStatic
trying to deltas with previous static entities
=============
*/
int SV_FindBestBaselineForStatic( int index, entity_state_t **baseline, entity_state_t *to )
{
int bestBitCount;
int i, bitCount;
int bestfound, j;
bestBitCount = j = Delta_TestBaseline( *baseline, to, false, sv.time );
bestfound = index;
// lookup backward for previous 64 states and try to interpret current delta as baseline
for( i = index - 1; bestBitCount > 0 && i >= 0 && ( index - i ) < ( MAX_CUSTOM_BASELINES - 1 ); i-- )
{ {
// don't worry about underflow in circular buffer if( frame != NULL )
entity_state_t *test = &svs.static_entities[i]; *baseline = &svs.packet_entities[(frame->first_entity+bestfound) % svs.num_client_entities];
else
bitCount = Delta_TestBaseline( test, to, false, sv.time ); *baseline = &svs.static_entities[bestfound];
if( bitCount < bestBitCount )
{
bestBitCount = bitCount;
bestfound = i;
}
} }
// using delta from previous entity as baseline for current
if( index != bestfound )
*baseline = &svs.static_entities[bestfound];
return index - bestfound; return index - bestfound;
} }
@@ -347,7 +322,7 @@ static void SV_EmitPacketEntities( sv_client_t *cl, client_frame_t *to, sizebuf_
// trying to reduce message by select optimal baseline // trying to reduce message by select optimal baseline
if( !sv_instancedbaseline.value || !sv.num_instanced || sv.last_valid_baseline > newnum ) if( !sv_instancedbaseline.value || !sv.num_instanced || sv.last_valid_baseline > newnum )
{ {
offset = SV_FindBestBaseline( cl, newindex, &baseline, newent, to, player ); offset = SV_FindBestBaseline( newindex, &baseline, newent, to, player );
} }
else else
{ {

View File

@@ -328,7 +328,7 @@ static qboolean SV_CheckClientVisiblity( sv_client_t *cl, const byte *mask )
else else
VectorCopy( cl->edict->v.origin, vieworg ); VectorCopy( cl->edict->v.origin, vieworg );
leaf = Mod_PointInLeaf( vieworg, sv.worldmodel->nodes ); leaf = Mod_PointInLeaf( vieworg, sv.worldmodel->nodes, sv.worldmodel );
if( CHECKVISBIT( mask, leaf->cluster )) if( CHECKVISBIT( mask, leaf->cluster ))
return true; // visible from player view or camera view return true; // visible from player view or camera view
@@ -342,7 +342,7 @@ static qboolean SV_CheckClientVisiblity( sv_client_t *cl, const byte *mask )
continue; continue;
VectorAdd( view->v.origin, view->v.view_ofs, vieworg ); VectorAdd( view->v.origin, view->v.view_ofs, vieworg );
leaf = Mod_PointInLeaf( vieworg, sv.worldmodel->nodes ); leaf = Mod_PointInLeaf( vieworg, sv.worldmodel->nodes, sv.worldmodel );
if( CHECKVISBIT( mask, leaf->cluster )) if( CHECKVISBIT( mask, leaf->cluster ))
return true; // visible from portal camera view return true; // visible from portal camera view
@@ -578,7 +578,7 @@ qboolean SV_CreateStaticEntity( sizebuf_t *msg, int index )
state->number = 0; state->number = 0;
// trying to compress with previous delta's // trying to compress with previous delta's
offset = SV_FindBestBaselineForStatic( index, &baseline, state ); offset = SV_FindBestBaseline( index, &baseline, state, NULL, false );
MSG_BeginServerCmd( msg, svc_spawnstatic ); MSG_BeginServerCmd( msg, svc_spawnstatic );
MSG_WriteDeltaEntity( baseline, state, msg, true, DELTA_STATIC, sv.time, offset ); MSG_WriteDeltaEntity( baseline, state, msg, true, DELTA_STATIC, sv.time, offset );
@@ -1565,21 +1565,6 @@ edict_t *SV_FindGlobalEntity( string_t classname, string_t globalname )
return pent; return pent;
} }
/*
==============
pfnGetEntityIllum
returns averaged lightvalue for entity
==============
*/
static int GAME_EXPORT pfnGetEntityIllum( edict_t* pEnt )
{
if( !SV_IsValidEdict( pEnt ))
return -1;
return SV_LightForEntity( pEnt );
}
/* /*
================= =================
pfnFindEntityInSphere pfnFindEntityInSphere
@@ -1747,7 +1732,7 @@ static edict_t* GAME_EXPORT pfnFindClientInPVS( edict_t *pEdict )
VectorAdd( pEdict->v.origin, pEdict->v.view_ofs, view ); VectorAdd( pEdict->v.origin, pEdict->v.view_ofs, view );
} }
leaf = Mod_PointInLeaf( view, sv.worldmodel->nodes ); leaf = Mod_PointInLeaf( view, sv.worldmodel->nodes, sv.worldmodel );
if( CHECKVISBIT( clientpvs, leaf->cluster )) if( CHECKVISBIT( clientpvs, leaf->cluster ))
return pClient; // client which currently in PVS return pClient; // client which currently in PVS
@@ -2450,10 +2435,17 @@ pfnLightStyle
*/ */
static void GAME_EXPORT pfnLightStyle( int style, const char* val ) static void GAME_EXPORT pfnLightStyle( int style, const char* val )
{ {
if( style < 0 ) style = 0; if( style < 0 )
style = 0;
if( style >= MAX_LIGHTSTYLES ) if( style >= MAX_LIGHTSTYLES )
{
Host_Error( "%s: style: %i >= %d", __func__, style, MAX_LIGHTSTYLES ); Host_Error( "%s: style: %i >= %d", __func__, style, MAX_LIGHTSTYLES );
if( sv.loadgame ) return; // don't let the world overwrite our restored styles return;
}
if( sv.loadgame )
return; // don't let the world overwrite our restored styles
SV_SetLightStyle( style, val, 0.0f ); // set correct style SV_SetLightStyle( style, val, 0.0f ); // set correct style
} }
@@ -3520,7 +3512,7 @@ static int GAME_EXPORT pfnRegUserMsg( const char *pszName, int iSize )
{ {
// see if already registered // see if already registered
if( !Q_strcmp( svgame.msg[i].name, pszName )) if( !Q_strcmp( svgame.msg[i].name, pszName ))
return svc_lastmsg + i; // offset return svgame.msg[i].number;
} }
if( i == MAX_USER_MESSAGES ) if( i == MAX_USER_MESSAGES )
@@ -4299,6 +4291,35 @@ static byte *GAME_EXPORT pfnSetFatPAS( const float *org )
return fatphs; return fatphs;
} }
/*
=============
Mod_HeadnodeVisible
=============
*/
static qboolean Mod_HeadnodeVisible( model_t *mod, mnode_t *node, const byte *visbits, int *lastleaf )
{
if( !node || node->contents == CONTENTS_SOLID )
return false;
if( node->contents < 0 )
{
if( !CHECKVISBIT( visbits, ((mleaf_t *)node)->cluster ))
return false;
if( lastleaf )
*lastleaf = ((mleaf_t *)node)->cluster;
return true;
}
if( Mod_HeadnodeVisible( mod, node_child( node, 0, mod ), visbits, lastleaf ))
return true;
if( Mod_HeadnodeVisible( mod, node_child( node, 1, mod ), visbits, lastleaf ))
return true;
return false;
}
/* /*
============= =============
pfnCheckVisibility pfnCheckVisibility
@@ -4308,6 +4329,7 @@ pfnCheckVisibility
static int GAME_EXPORT pfnCheckVisibility( const edict_t *ent, byte *pset ) static int GAME_EXPORT pfnCheckVisibility( const edict_t *ent, byte *pset )
{ {
int i, leafnum; int i, leafnum;
qboolean large_leafs = FBitSet( sv.worldmodel->flags, MODEL_QBSP2 );
if( !SV_IsValidEdict( ent )) if( !SV_IsValidEdict( ent ))
return 0; return 0;
@@ -4323,17 +4345,28 @@ static int GAME_EXPORT pfnCheckVisibility( const edict_t *ent, byte *pset )
// check individual leafs // check individual leafs
for( i = 0; i < ent->num_leafs; i++ ) for( i = 0; i < ent->num_leafs; i++ )
{ {
if( CHECKVISBIT( pset, ent->leafnums[i] )) if( large_leafs )
return 1; // visible passed by leaf {
if( CHECKVISBIT( pset, ent->leafnums32[i] ))
return 1; // visible passed by leaf
}
else
{
if( CHECKVISBIT( pset, ent->leafnums16[i] ))
return 1; // visible passed by leaf
}
} }
return 0; return 0;
} }
else else
{ {
for( i = 0; i < MAX_ENT_LEAFS; i++ ) for( i = 0; i < MAX_ENT_LEAFS( large_leafs ); i++ )
{ {
leafnum = ent->leafnums[i]; if( large_leafs )
leafnum = ent->leafnums32[i];
else
leafnum = ent->leafnums16[i];
if( leafnum == -1 ) break; if( leafnum == -1 ) break;
if( CHECKVISBIT( pset, leafnum )) if( CHECKVISBIT( pset, leafnum ))
@@ -4341,11 +4374,15 @@ static int GAME_EXPORT pfnCheckVisibility( const edict_t *ent, byte *pset )
} }
// too many leafs for individual check, go by headnode // too many leafs for individual check, go by headnode
if( !Mod_HeadnodeVisible( &sv.worldmodel->nodes[ent->headnode], pset, &leafnum )) if( !Mod_HeadnodeVisible( sv.worldmodel, &sv.worldmodel->nodes[ent->headnode], pset, &leafnum ))
return 0; return 0;
((edict_t *)ent)->leafnums[ent->num_leafs] = leafnum; if( large_leafs )
((edict_t *)ent)->num_leafs = (ent->num_leafs + 1) % MAX_ENT_LEAFS; ((edict_t *)ent)->leafnums32[ent->num_leafs] = leafnum;
else
((edict_t *)ent)->leafnums16[ent->num_leafs] = leafnum;
((edict_t *)ent)->num_leafs = (ent->num_leafs + 1) % MAX_ENT_LEAFS( large_leafs );
return 2; // visible passed by headnode return 2; // visible passed by headnode
} }
@@ -4446,6 +4483,11 @@ static void GAME_EXPORT pfnGetPlayerStats( const edict_t *pClient, int *ping, in
if( ping ) *ping = cl->latency * 1000; if( ping ) *ping = cl->latency * 1000;
} }
static void GAME_EXPORT Cmd_AddServerCommand( const char *cmd_name, xcommand_t function )
{
Cmd_AddCommandEx( cmd_name, function, "server command", CMD_SERVERDLL, __func__ );
}
/* /*
============= =============
pfnForceUnmodified pfnForceUnmodified
@@ -4677,7 +4719,7 @@ static enginefuncs_t gEngfuncs =
pfnChangeYaw, pfnChangeYaw,
pfnChangePitch, pfnChangePitch,
SV_FindEntityByString, SV_FindEntityByString,
pfnGetEntityIllum, SV_LightForEntity,
pfnFindEntityInSphere, pfnFindEntityInSphere,
pfnFindClientInPVS, pfnFindClientInPVS,
pfnEntitiesInPVS, pfnEntitiesInPVS,

View File

@@ -805,7 +805,8 @@ static void SV_SetupClients( void )
if( !changed_maxclients ) return; // nothing to change if( !changed_maxclients ) return; // nothing to change
// if clients count was changed we need to run full shutdown procedure // if clients count was changed we need to run full shutdown procedure
if( svs.maxclients ) Host_ShutdownServer(); if( svs.maxclients )
SV_Shutdown( "Server was killed due to maxclients change\n" );
// copy the actual value from cvar // copy the actual value from cvar
svs.maxclients = (int)sv_maxclients.value; svs.maxclients = (int)sv_maxclients.value;

View File

@@ -1784,21 +1784,23 @@ static void SV_Physics_Entity( edict_t *ent )
static void SV_RunLightStyles( void ) static void SV_RunLightStyles( void )
{ {
int i, ofs; int i;
lightstyle_t *ls;
float scale;
scale = 1.0f;
// run lightstyles animation // run lightstyles animation
for( i = 0, ls = sv.lightstyles; i < MAX_LIGHTSTYLES; i++, ls++ ) for( i = 0; i < MAX_LIGHTSTYLES; i++ )
{ {
lightstyle_t *ls = &sv.lightstyles[i];
int ofs;
ls->time += sv.frametime; ls->time += sv.frametime;
ofs = (ls->time * 10); ofs = (ls->time * 10);
if( ls->length == 0 ) ls->value = scale; // disable this light if( ls->length == 0 )
else if( ls->length == 1 ) ls->value = ( ls->map[0] / 12.0f ) * scale; ls->value = 1.0f; // disable this light
else ls->value = ( ls->map[ofs % ls->length] / 12.0f ) * scale; else if( ls->length == 1 )
ls->value = ls->map[0] / 12.0f;
else
ls->value = ls->map[ofs % ls->length] / 12.0f;
} }
} }

View File

@@ -141,7 +141,8 @@ static TYPEDESCRIPTION gSaveClient[] =
DEFINE_ARRAY( SAVE_CLIENT, introTrack, FIELD_CHARACTER, 64 ), DEFINE_ARRAY( SAVE_CLIENT, introTrack, FIELD_CHARACTER, 64 ),
DEFINE_ARRAY( SAVE_CLIENT, mainTrack, FIELD_CHARACTER, 64 ), DEFINE_ARRAY( SAVE_CLIENT, mainTrack, FIELD_CHARACTER, 64 ),
DEFINE_FIELD( SAVE_CLIENT, trackPosition, FIELD_INTEGER ), DEFINE_FIELD( SAVE_CLIENT, trackPosition, FIELD_INTEGER ),
DEFINE_FIELD( SAVE_CLIENT, viewentity, FIELD_SHORT ), // mods based on HLU SDK disallow usage of FIELD_SHORT
DEFINE_ARRAY( SAVE_CLIENT, viewentity, FIELD_CHARACTER, sizeof( short )),
DEFINE_FIELD( SAVE_CLIENT, wateralpha, FIELD_FLOAT ), DEFINE_FIELD( SAVE_CLIENT, wateralpha, FIELD_FLOAT ),
DEFINE_FIELD( SAVE_CLIENT, wateramp, FIELD_FLOAT ), DEFINE_FIELD( SAVE_CLIENT, wateramp, FIELD_FLOAT ),
}; };
@@ -150,7 +151,8 @@ static TYPEDESCRIPTION gDecalEntry[] =
{ {
DEFINE_FIELD( decallist_t, position, FIELD_VECTOR ), DEFINE_FIELD( decallist_t, position, FIELD_VECTOR ),
DEFINE_ARRAY( decallist_t, name, FIELD_CHARACTER, 64 ), DEFINE_ARRAY( decallist_t, name, FIELD_CHARACTER, 64 ),
DEFINE_FIELD( decallist_t, entityIndex, FIELD_SHORT ), // mods based on HLU SDK disallow usage of FIELD_SHORT
DEFINE_ARRAY( decallist_t, entityIndex, FIELD_CHARACTER, sizeof( short )),
DEFINE_FIELD( decallist_t, depth, FIELD_CHARACTER ), DEFINE_FIELD( decallist_t, depth, FIELD_CHARACTER ),
DEFINE_FIELD( decallist_t, flags, FIELD_CHARACTER ), DEFINE_FIELD( decallist_t, flags, FIELD_CHARACTER ),
DEFINE_FIELD( decallist_t, scale, FIELD_FLOAT ), DEFINE_FIELD( decallist_t, scale, FIELD_FLOAT ),
@@ -158,6 +160,7 @@ static TYPEDESCRIPTION gDecalEntry[] =
DEFINE_ARRAY( decallist_t, studio_state, FIELD_CHARACTER, sizeof( modelstate_t )), DEFINE_ARRAY( decallist_t, studio_state, FIELD_CHARACTER, sizeof( modelstate_t )),
}; };
// Can use any FIELD type here because only Xash3D games will spawn static entities
static TYPEDESCRIPTION gStaticEntry[] = static TYPEDESCRIPTION gStaticEntry[] =
{ {
DEFINE_FIELD( entity_state_t, messagenum, FIELD_MODELNAME ), // HACKHACK: store model into messagenum DEFINE_FIELD( entity_state_t, messagenum, FIELD_MODELNAME ), // HACKHACK: store model into messagenum
@@ -200,7 +203,8 @@ static TYPEDESCRIPTION gStaticEntry[] =
static TYPEDESCRIPTION gSoundEntry[] = static TYPEDESCRIPTION gSoundEntry[] =
{ {
DEFINE_ARRAY( soundlist_t, name, FIELD_CHARACTER, 64 ), DEFINE_ARRAY( soundlist_t, name, FIELD_CHARACTER, 64 ),
DEFINE_FIELD( soundlist_t, entnum, FIELD_SHORT ), // mods based on HLU SDK disallow usage of FIELD_SHORT
DEFINE_ARRAY( soundlist_t, entnum, FIELD_CHARACTER, sizeof( short )),
DEFINE_FIELD( soundlist_t, origin, FIELD_VECTOR ), DEFINE_FIELD( soundlist_t, origin, FIELD_VECTOR ),
DEFINE_FIELD( soundlist_t, volume, FIELD_FLOAT ), DEFINE_FIELD( soundlist_t, volume, FIELD_FLOAT ),
DEFINE_FIELD( soundlist_t, attenuation, FIELD_FLOAT ), DEFINE_FIELD( soundlist_t, attenuation, FIELD_FLOAT ),
@@ -1602,6 +1606,10 @@ static int LoadGameState( char const *level, qboolean changelevel )
pSaveData = LoadSaveData( level ); pSaveData = LoadSaveData( level );
if( !pSaveData ) return 0; // couldn't load the file if( !pSaveData ) return 0; // couldn't load the file
// must set mapname before calling into DLL
Q_strncpy( sv.name, level, sizeof( sv.name ));
svgame.globals->mapname = MAKE_STRING( sv.name );
ParseSaveTables( pSaveData, &header, true ); ParseSaveTables( pSaveData, &header, true );
EntityPatchRead( pSaveData, level ); EntityPatchRead( pSaveData, level );
@@ -1609,8 +1617,6 @@ static int LoadGameState( char const *level, qboolean changelevel )
sv.loadgame = sv.paused = true; sv.loadgame = sv.paused = true;
Cvar_SetValue( "skill", header.skillLevel ); Cvar_SetValue( "skill", header.skillLevel );
Q_strncpy( sv.name, header.mapName, sizeof( sv.name ));
svgame.globals->mapname = MAKE_STRING( sv.name );
Cvar_Set( "sv_skyname", header.skyName ); Cvar_Set( "sv_skyname", header.skyName );
// restore sky parms // restore sky parms

View File

@@ -40,9 +40,8 @@ HULL BOXES
=============================================================================== ===============================================================================
*/ */
static hull_t box_hull; static hull_t box_hull;
static mclipnode_t box_clipnodes[6]; static mplane_t box_planes[6];
static mplane_t box_planes[6];
/* /*
=================== ===================
@@ -54,23 +53,15 @@ can just be stored out and get a proper hull_t structure.
*/ */
static void SV_InitBoxHull( void ) static void SV_InitBoxHull( void )
{ {
int i, side; int i;
box_hull.clipnodes = box_clipnodes; box_hull.clipnodes16 = (mclipnode16_t *)box_clipnodes16;
box_hull.planes = box_planes; box_hull.planes = box_planes;
box_hull.firstclipnode = 0; box_hull.firstclipnode = 0;
box_hull.lastclipnode = 5; box_hull.lastclipnode = 5;
for( i = 0; i < 6; i++ ) for( i = 0; i < 6; i++ )
{ {
box_clipnodes[i].planenum = i;
side = i & 1;
box_clipnodes[i].children[side] = CONTENTS_EMPTY;
if( i != 5 ) box_clipnodes[i].children[side^1] = i + 1;
else box_clipnodes[i].children[side^1] = CONTENTS_SOLID;
box_planes[i].type = i>>1; box_planes[i].type = i>>1;
box_planes[i].normal[i>>1] = 1; box_planes[i].normal[i>>1] = 1;
box_planes[i].signbits = 0; box_planes[i].signbits = 0;
@@ -167,6 +158,11 @@ static hull_t *SV_HullForBox( const vec3_t mins, const vec3_t maxs )
box_planes[4].dist = maxs[2]; box_planes[4].dist = maxs[2];
box_planes[5].dist = mins[2]; box_planes[5].dist = mins[2];
if( world.version == QBSP2_VERSION )
box_hull.clipnodes32 = (mclipnode32_t *)box_clipnodes32;
else
box_hull.clipnodes16 = (mclipnode16_t *)box_clipnodes16;
return &box_hull; return &box_hull;
} }
@@ -594,7 +590,7 @@ SV_FindTouchedLeafs
=============== ===============
*/ */
static void SV_FindTouchedLeafs( edict_t *ent, mnode_t *node, int *headnode ) static void SV_FindTouchedLeafs( edict_t *ent, model_t *mod, mnode_t *node, int *headnode )
{ {
int sides; int sides;
mleaf_t *leaf; mleaf_t *leaf;
@@ -605,16 +601,19 @@ static void SV_FindTouchedLeafs( edict_t *ent, mnode_t *node, int *headnode )
// add an efrag if the node is a leaf // add an efrag if the node is a leaf
if( node->contents < 0 ) if( node->contents < 0 )
{ {
if( ent->num_leafs > ( MAX_ENT_LEAFS - 1 )) if( ent->num_leafs > MAX_ENT_LEAFS( FBitSet( mod->flags, MODEL_QBSP2 )))
{ {
// continue counting leafs, // continue counting leafs,
// so we know how many it's overrun // so we know how many it's overrun
ent->num_leafs = (MAX_ENT_LEAFS + 1); ent->num_leafs = (MAX_ENT_LEAFS( FBitSet( mod->flags, MODEL_QBSP2 )) + 1);
} }
else else
{ {
leaf = (mleaf_t *)node; leaf = (mleaf_t *)node;
ent->leafnums[ent->num_leafs] = leaf->cluster; if( FBitSet( mod->flags, MODEL_QBSP2 ))
ent->leafnums32[ent->num_leafs] = leaf->cluster;
else
ent->leafnums16[ent->num_leafs] = leaf->cluster;
ent->num_leafs++; ent->num_leafs++;
} }
return; return;
@@ -624,11 +623,13 @@ static void SV_FindTouchedLeafs( edict_t *ent, mnode_t *node, int *headnode )
sides = BOX_ON_PLANE_SIDE( ent->v.absmin, ent->v.absmax, node->plane ); sides = BOX_ON_PLANE_SIDE( ent->v.absmin, ent->v.absmax, node->plane );
if(( sides == 3 ) && ( *headnode == -1 )) if(( sides == 3 ) && ( *headnode == -1 ))
*headnode = node - sv.worldmodel->nodes; *headnode = node - mod->nodes;
// recurse down the contacted sides // recurse down the contacted sides
if( sides & 1 ) SV_FindTouchedLeafs( ent, node->children[0], headnode ); if( sides & 1 )
if( sides & 2 ) SV_FindTouchedLeafs( ent, node->children[1], headnode ); SV_FindTouchedLeafs( ent, mod, node_child( node, 0, mod ), headnode );
if( sides & 2 )
SV_FindTouchedLeafs( ent, mod, node_child( node, 1, mod ), headnode );
} }
/* /*
@@ -650,7 +651,7 @@ void GAME_EXPORT SV_LinkEdict( edict_t *ent, qboolean touch_triggers )
if( ent->v.movetype == MOVETYPE_FOLLOW && SV_IsValidEdict( ent->v.aiment )) if( ent->v.movetype == MOVETYPE_FOLLOW && SV_IsValidEdict( ent->v.aiment ))
{ {
memcpy( ent->leafnums, ent->v.aiment->leafnums, sizeof( ent->leafnums )); memcpy( ent->leafnums32, ent->v.aiment->leafnums32, sizeof( ent->leafnums32 ));
ent->num_leafs = ent->v.aiment->num_leafs; ent->num_leafs = ent->v.aiment->num_leafs;
ent->headnode = ent->v.aiment->headnode; ent->headnode = ent->v.aiment->headnode;
} }
@@ -662,11 +663,11 @@ void GAME_EXPORT SV_LinkEdict( edict_t *ent, qboolean touch_triggers )
headnode = -1; headnode = -1;
if( ent->v.modelindex ) if( ent->v.modelindex )
SV_FindTouchedLeafs( ent, sv.worldmodel->nodes, &headnode ); SV_FindTouchedLeafs( ent, sv.worldmodel, sv.worldmodel->nodes, &headnode );
if( ent->num_leafs > MAX_ENT_LEAFS ) if( ent->num_leafs > MAX_ENT_LEAFS( FBitSet( sv.worldmodel->flags, MODEL_QBSP2 )))
{ {
memset( ent->leafnums, -1, sizeof( ent->leafnums )); memset( ent->leafnums32, -1, sizeof( ent->leafnums32 ));
ent->num_leafs = 0; // so we use headnode instead ent->num_leafs = 0; // so we use headnode instead
ent->headnode = headnode; ent->headnode = headnode;
} }
@@ -1508,23 +1509,18 @@ trace_t SV_MoveToss( edict_t *tossent, edict_t *ignore )
=============================================================================== ===============================================================================
*/ */
static vec3_t sv_pointColor;
/* /*
================= =================
SV_RecursiveLightPoint SV_RecursiveLightPoint
================= =================
*/ */
static qboolean SV_RecursiveLightPoint( model_t *model, mnode_t *node, const vec3_t start, const vec3_t end ) static qboolean SV_RecursiveLightPoint( model_t *model, mnode_t *node, const vec3_t start, const vec3_t end, vec3_t point_color )
{ {
float front, back, scale, frac; float front, back, frac;
int i, map, side, size; int i, side;
float ds, dt, s, t; vec3_t mid;
int sample_size; mnode_t *children[2];
msurface_t *surf; int numsurfaces, firstsurface;
mextrasurf_t *info;
color24 *lm;
vec3_t mid;
// didn't hit anything // didn't hit anything
if( !node || node->contents < 0 ) if( !node || node->contents < 0 )
@@ -1534,29 +1530,34 @@ static qboolean SV_RecursiveLightPoint( model_t *model, mnode_t *node, const vec
front = PlaneDiff( start, node->plane ); front = PlaneDiff( start, node->plane );
back = PlaneDiff( end, node->plane ); back = PlaneDiff( end, node->plane );
node_children( children, node, model );
side = front < 0.0f; side = front < 0.0f;
if(( back < 0.0f ) == side ) if(( back < 0.0f ) == side )
return SV_RecursiveLightPoint( model, node->children[side], start, end ); return SV_RecursiveLightPoint( model, children[side], start, end, point_color );
frac = front / ( front - back ); frac = front / ( front - back );
VectorLerp( start, frac, end, mid ); VectorLerp( start, frac, end, mid );
// co down front side // co down front side
if( SV_RecursiveLightPoint( model, node->children[side], start, mid )) if( SV_RecursiveLightPoint( model, children[side], start, mid, point_color ))
return true; // hit something return true; // hit something
if(( back < 0.0f ) == side ) if(( back < 0.0f ) == side )
return false;// didn't hit anything return false; // didn't hit anything
// check for impact on this node // check for impact on this node
surf = model->surfaces + node->firstsurface; numsurfaces = node_numsurfaces( node, model );
firstsurface = node_firstsurface( node, model );
for( i = 0; i < node->numsurfaces; i++, surf++ ) for( i = 0; i < numsurfaces; i++ )
{ {
int smax, tmax; const msurface_t *surf = &model->surfaces[firstsurface + i];
const mextrasurf_t *info = surf->info;
info = surf->info; int smax, tmax, map, size;
int sample_size;
float ds, dt, s, t;
const color24 *lm;
if( FBitSet( surf->flags, SURF_DRAWTILED )) if( FBitSet( surf->flags, SURF_DRAWTILED ))
continue; // no lightmaps continue; // no lightmaps
@@ -1582,26 +1583,27 @@ static qboolean SV_RecursiveLightPoint( model_t *model, mnode_t *node, const vec
ds /= sample_size; ds /= sample_size;
dt /= sample_size; dt /= sample_size;
VectorClear( sv_pointColor ); VectorClear( point_color );
lm = surf->samples + Q_rint( dt ) * smax + Q_rint( ds ); lm = surf->samples + Q_rint( dt ) * smax + Q_rint( ds );
size = smax * tmax; size = smax * tmax;
for( map = 0; map < MAXLIGHTMAPS && surf->styles[map] != 255; map++ ) for( map = 0; map < MAXLIGHTMAPS && surf->styles[map] != 255; map++ )
{ {
scale = sv.lightstyles[surf->styles[map]].value; float scale = sv.lightstyles[surf->styles[map]].value;
sv_pointColor[0] += lm->r * scale; point_color[0] += lm->r * scale;
sv_pointColor[1] += lm->g * scale; point_color[1] += lm->g * scale;
sv_pointColor[2] += lm->b * scale; point_color[2] += lm->b * scale;
lm += size; // skip to next lightmap lm += size; // skip to next lightmap
} }
return true; return true;
} }
// go down back side // go down back side
return SV_RecursiveLightPoint( model, node->children[!side], mid, end ); return SV_RecursiveLightPoint( model, children[!side], mid, end, point_color );
} }
/* /*
@@ -1615,10 +1617,8 @@ void SV_SetLightStyle( int style, const char* s, float f )
{ {
int j, k; int j, k;
Q_strncpy( sv.lightstyles[style].pattern, s, sizeof( sv.lightstyles[0].pattern )); j = Q_strncpy( sv.lightstyles[style].pattern, s, sizeof( sv.lightstyles[0].pattern ));
sv.lightstyles[style].time = f; sv.lightstyles[style].time = f;
j = Q_strlen( s );
sv.lightstyles[style].length = j; sv.lightstyles[style].length = j;
for( k = 0; k < j; k++ ) for( k = 0; k < j; k++ )
@@ -1642,12 +1642,16 @@ grab the ambient lighting color for current point
*/ */
int SV_LightForEntity( edict_t *pEdict ) int SV_LightForEntity( edict_t *pEdict )
{ {
vec3_t start, end; vec3_t point_color = { 1.0f, 1.0f, 1.0f };
vec3_t start, end;
if( !SV_IsValidEdict( pEdict ))
return -1;
if( FBitSet( pEdict->v.effects, EF_FULLBRIGHT ) || !sv.worldmodel->lightdata ) if( FBitSet( pEdict->v.effects, EF_FULLBRIGHT ) || !sv.worldmodel->lightdata )
return 255; return 255;
// player has more precision light level that come from client-side // player has more precise light level that come from client-side
if( FBitSet( pEdict->v.flags, FL_CLIENT )) if( FBitSet( pEdict->v.flags, FL_CLIENT ))
return pEdict->v.light_level; return pEdict->v.light_level;
@@ -1657,9 +1661,8 @@ int SV_LightForEntity( edict_t *pEdict )
if( FBitSet( pEdict->v.effects, EF_INVLIGHT )) if( FBitSet( pEdict->v.effects, EF_INVLIGHT ))
end[2] = start[2] + world.size[2]; end[2] = start[2] + world.size[2];
else end[2] = start[2] - world.size[2]; else end[2] = start[2] - world.size[2];
VectorSet( sv_pointColor, 1.0f, 1.0f, 1.0f );
SV_RecursiveLightPoint( sv.worldmodel, sv.worldmodel->nodes, start, end ); SV_RecursiveLightPoint( sv.worldmodel, sv.worldmodel->nodes, start, end, point_color );
return VectorAvg( sv_pointColor ); return VectorAvg( point_color );
} }

View File

@@ -170,9 +170,9 @@ typedef struct vguiapi_s
void (*SetupDrawingImage)( int *pColor ); void (*SetupDrawingImage)( int *pColor );
void (*BindTexture)( int id ); void (*BindTexture)( int id );
void (*EnableTexture)( qboolean enable ); void (*EnableTexture)( qboolean enable );
void (*CreateTexture)( int id, int width, int height ); void (*Reserved0)( int id, int width, int height );
void (*UploadTexture)( int id, const char *buffer, int width, int height ); void (*UploadTexture)( int id, const char *buffer, int width, int height );
void (*UploadTextureBlock)( int id, int drawX, int drawY, const byte *rgba, int blockWidth, int blockHeight ); void (*Reserved1)( int id, int drawX, int drawY, const byte *rgba, int blockWidth, int blockHeight );
void (*DrawQuad)( const vpoint_t *ul, const vpoint_t *lr ); void (*DrawQuad)( const vpoint_t *ul, const vpoint_t *lr );
void (*GetTextureSizes)( int *width, int *height ); void (*GetTextureSizes)( int *width, int *height );
int (*GenerateTexture)( void ); int (*GenerateTexture)( void );

View File

@@ -130,7 +130,6 @@ def configure(conf):
conf.define_cond('XASH_CUSTOM_SWAP', conf.options.CUSTOM_SWAP) conf.define_cond('XASH_CUSTOM_SWAP', conf.options.CUSTOM_SWAP)
conf.define_cond('XASH_ENABLE_MAIN', conf.env.DISABLE_LAUNCHER) conf.define_cond('XASH_ENABLE_MAIN', conf.env.DISABLE_LAUNCHER)
conf.define_cond('XASH_NO_ASYNC_NS_RESOLVE', conf.options.NO_ASYNC_RESOLVE) conf.define_cond('XASH_NO_ASYNC_NS_RESOLVE', conf.options.NO_ASYNC_RESOLVE)
conf.define_cond('DBGHELP', conf.env.DEST_OS == 'win32')
conf.define_cond('PSAPI_VERSION', conf.env.DEST_OS == 'win32') # will be defined as 1 conf.define_cond('PSAPI_VERSION', conf.env.DEST_OS == 'win32') # will be defined as 1
for refdll in conf.refdlls: for refdll in conf.refdlls:

View File

@@ -134,7 +134,7 @@ static void FS_PopulateDirEntries( dir_t *dir, const char *path )
} }
stringlistinit( &list ); stringlistinit( &list );
listdirectory( &list, path ); listdirectory( &list, path, false );
if( !list.numstrings ) if( !list.numstrings )
{ {
dir->numentries = DIRENTRY_EMPTY_DIRECTORY; dir->numentries = DIRENTRY_EMPTY_DIRECTORY;
@@ -225,7 +225,7 @@ static int FS_MaybeUpdateDirEntries( dir_t *dir, const char *path, const char *e
int ret; int ret;
stringlistinit( &list ); stringlistinit( &list );
listdirectory( &list, path ); listdirectory( &list, path, false );
if( list.numstrings == 0 ) // empty directory if( list.numstrings == 0 ) // empty directory
{ {
@@ -421,7 +421,7 @@ static void FS_Search_DIR( searchpath_t *search, stringlist_t *list, const char
} }
stringlistinit( &dirlist ); stringlistinit( &dirlist );
listdirectory( &dirlist, netpath ); listdirectory( &dirlist, netpath, false );
Q_strncpy( temp, basepath, sizeof( temp )); Q_strncpy( temp, basepath, sizeof( temp ));

View File

@@ -16,6 +16,8 @@ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details. GNU General Public License for more details.
*/ */
#define _GNU_SOURCE 1
#include "build.h" #include "build.h"
#include <fcntl.h> #include <fcntl.h>
#include <sys/types.h> #include <sys/types.h>
@@ -32,6 +34,9 @@ GNU General Public License for more details.
#endif #endif
#include <stdio.h> #include <stdio.h>
#include <stdarg.h> #include <stdarg.h>
#if HAVE_MEMFD_CREATE
#include <sys/mman.h>
#endif
#include "port.h" #include "port.h"
#include "defaults.h" #include "defaults.h"
#include "const.h" #include "const.h"
@@ -220,18 +225,13 @@ static void listlowercase( stringlist_t *list )
} }
#endif #endif
void listdirectory( stringlist_t *list, const char *path ) void listdirectory( stringlist_t *list, const char *path, qboolean dirs_only )
{ {
#if XASH_WIN32 #if XASH_WIN32
char pattern[4096]; char pattern[4096];
struct _finddata_t n_file; struct _finddata_t n_file;
intptr_t hFile; intptr_t hFile;
#else
DIR *dir;
struct dirent *entry;
#endif
#if XASH_WIN32
Q_snprintf( pattern, sizeof( pattern ), "%s/*", path ); Q_snprintf( pattern, sizeof( pattern ), "%s/*", path );
// ask for the directory listing handle // ask for the directory listing handle
@@ -242,15 +242,33 @@ void listdirectory( stringlist_t *list, const char *path )
stringlistappend( list, n_file.name ); stringlistappend( list, n_file.name );
// iterate through the directory // iterate through the directory
while( _findnext( hFile, &n_file ) == 0 ) while( _findnext( hFile, &n_file ) == 0 )
{
if( dirs_only && !FBitSet( n_file.attrib, _A_SUBDIR ))
continue;
stringlistappend( list, n_file.name ); stringlistappend( list, n_file.name );
}
_findclose( hFile ); _findclose( hFile );
#else #else
if( !( dir = opendir( path ) ) ) DIR *dir;
struct dirent *entry;
dir = opendir( path );
if( !dir )
return; return;
// iterate through the directory // iterate through the directory
while( ( entry = readdir( dir ) )) while(( entry = readdir( dir )))
{
#if HAVE_DIRENT_D_TYPE
if( dirs_only && entry->d_type != DT_DIR && entry->d_type != DT_UNKNOWN )
continue;
#endif
stringlistappend( list, entry->d_name ); stringlistappend( list, entry->d_name );
}
closedir( dir ); closedir( dir );
#endif #endif
@@ -391,7 +409,7 @@ void FS_AddGameDirectory( const char *dir, uint flags )
int i; int i;
stringlistinit( &list ); stringlistinit( &list );
listdirectory( &list, dir ); listdirectory( &list, dir, false );
stringlistsort( &list ); stringlistsort( &list );
for( archive = g_archives; archive->ext; archive++ ) for( archive = g_archives; archive->ext; archive++ )
@@ -503,12 +521,13 @@ FS_WriteGameInfo
assume GameInfo is valid assume GameInfo is valid
================ ================
*/ */
static void FS_WriteGameInfo( const char *filepath, gameinfo_t *GameInfo ) static qboolean FS_WriteGameInfo( const char *filepath, gameinfo_t *GameInfo )
{ {
file_t *f = FS_Open( filepath, "w", false ); // we in binary-mode file_t *f = FS_Open( filepath, "w", false ); // we in binary-mode
int i, write_ambients = false; int i, write_ambients = false;
if( !f ) Sys_Error( "%s: can't write %s\n", __func__, filepath ); // may be disk-space is out? if( !f )
return false;
FS_Printf( f, "// generated by " XASH_ENGINE_NAME " " XASH_VERSION "-%s (%s-%s)\n\n\n", Q_buildcommit(), Q_buildos(), Q_buildarch() ); FS_Printf( f, "// generated by " XASH_ENGINE_NAME " " XASH_VERSION "-%s (%s-%s)\n\n\n", Q_buildcommit(), Q_buildos(), Q_buildarch() );
@@ -632,25 +651,50 @@ static void FS_WriteGameInfo( const char *filepath, gameinfo_t *GameInfo )
FS_Printf( f, "demomap\t\t\"%s\"\n", GameInfo->demomap ); FS_Printf( f, "demomap\t\t\"%s\"\n", GameInfo->demomap );
FS_Close( f ); // all done FS_Close( f ); // all done
return true;
} }
static void FS_InitGameInfo( gameinfo_t *GameInfo, const char *gamedir ) static void FS_MakeGameInfo( void )
{
if( FS_WriteGameInfo( "gameinfo.txt", FI.GameInfo ))
Con_Printf( "Successfully generated %s/gameinfo.txt\n", FI.GameInfo->gamefolder );
else
Con_Printf( S_ERROR "Can't open %s/gameinfo.txt for write\n", FI.GameInfo->gamefolder );
}
static void FS_InitGameInfo( gameinfo_t *GameInfo, const char *gamedir, qboolean quake, time_t mtime )
{ {
memset( GameInfo, 0, sizeof( *GameInfo )); memset( GameInfo, 0, sizeof( *GameInfo ));
// filesystem info // filesystem info
Q_strncpy( GameInfo->title, "New Game", sizeof( GameInfo->title )); GameInfo->mtime = mtime;
Q_strncpy( GameInfo->gamefolder, gamedir, sizeof( GameInfo->gamefolder )); Q_strncpy( GameInfo->gamefolder, gamedir, sizeof( GameInfo->gamefolder ));
Q_strncpy( GameInfo->basedir, fs_basedir, sizeof( GameInfo->basedir ));
Q_strncpy( GameInfo->sp_entity, "info_player_start", sizeof( GameInfo->sp_entity )); Q_strncpy( GameInfo->sp_entity, "info_player_start", sizeof( GameInfo->sp_entity ));
Q_strncpy( GameInfo->mp_entity, "info_player_deathmatch", sizeof( GameInfo->mp_entity )); Q_strncpy( GameInfo->mp_entity, "info_player_deathmatch", sizeof( GameInfo->mp_entity ));
Q_strncpy( GameInfo->startmap, "newmap", sizeof( GameInfo->startmap ));
Q_strncpy( GameInfo->dll_path, "cl_dlls", sizeof( GameInfo->dll_path ));
Q_strncpy( GameInfo->game_dll, "dlls/hl.dll", sizeof( GameInfo->game_dll ));
Q_strncpy( GameInfo->game_dll_linux, "dlls/hl.so", sizeof( GameInfo->game_dll_linux ));
Q_strncpy( GameInfo->game_dll_osx, "dlls/hl.dylib", sizeof( GameInfo->game_dll_osx ));
Q_strncpy( GameInfo->iconpath, "game.ico", sizeof( GameInfo->iconpath )); Q_strncpy( GameInfo->iconpath, "game.ico", sizeof( GameInfo->iconpath ));
if( quake )
{
Q_strncpy( GameInfo->basedir, "id1", sizeof( GameInfo->basedir ));
Q_strncpy( GameInfo->title, gamedir, sizeof( GameInfo->title ));
Q_strncpy( GameInfo->startmap, "start", sizeof( GameInfo->startmap ));
Q_strncpy( GameInfo->dll_path, "bin", sizeof( GameInfo->dll_path ));
Q_strncpy( GameInfo->game_dll, "bin/progs.dll", sizeof( GameInfo->game_dll ));
Q_strncpy( GameInfo->game_dll_linux, "bin/progs.so", sizeof( GameInfo->game_dll_linux ));
Q_strncpy( GameInfo->game_dll_osx, "bin/progs.dylib", sizeof( GameInfo->game_dll_osx ));
}
else
{
Q_strncpy( GameInfo->basedir, fs_basedir, sizeof( GameInfo->basedir ));
Q_strncpy( GameInfo->title, "New Game", sizeof( GameInfo->title ));
Q_strncpy( GameInfo->startmap, "newmap", sizeof( GameInfo->startmap ));
Q_strncpy( GameInfo->dll_path, "cl_dlls", sizeof( GameInfo->dll_path ));
Q_strncpy( GameInfo->game_dll, "dlls/hl.dll", sizeof( GameInfo->game_dll ));
Q_strncpy( GameInfo->game_dll_linux, "dlls/hl.so", sizeof( GameInfo->game_dll_linux ));
Q_strncpy( GameInfo->game_dll_osx, "dlls/hl.dylib", sizeof( GameInfo->game_dll_osx ));
}
GameInfo->max_edicts = DEFAULT_MAX_EDICTS; // default value if not specified GameInfo->max_edicts = DEFAULT_MAX_EDICTS; // default value if not specified
GameInfo->max_tents = 500; GameInfo->max_tents = 500;
GameInfo->max_beams = 128; GameInfo->max_beams = 128;
@@ -935,36 +979,19 @@ static void FS_ParseGenericGameInfo( gameinfo_t *GameInfo, const char *buf, cons
} }
} }
/*
================
FS_CreateDefaultGameInfo
================
*/
static void FS_CreateDefaultGameInfo( const char *filename )
{
gameinfo_t defGI;
FS_InitGameInfo( &defGI, fs_basedir );
// make simple gameinfo.txt
FS_WriteGameInfo( filename, &defGI );
}
/* /*
================ ================
FS_ParseLiblistGam FS_ParseLiblistGam
================ ================
*/ */
static qboolean FS_ParseLiblistGam( const char *filename, const char *gamedir, gameinfo_t *GameInfo ) static qboolean FS_ParseLiblistGam( const char *filename, const char *gamedir, gameinfo_t *GameInfo, time_t mtime )
{ {
char *afile; char *afile = FS_LoadDirectFile( filename, NULL );
if( !GameInfo ) return false; if( !afile )
afile = (char *)FS_LoadDirectFile( filename, NULL ); return false;
if( !afile ) return false;
FS_InitGameInfo( GameInfo, gamedir );
FS_InitGameInfo( GameInfo, gamedir, false, mtime );
FS_ParseGenericGameInfo( GameInfo, afile, false ); FS_ParseGenericGameInfo( GameInfo, afile, false );
Mem_Free( afile ); Mem_Free( afile );
@@ -977,18 +1004,15 @@ static qboolean FS_ParseLiblistGam( const char *filename, const char *gamedir, g
FS_ConvertGameInfo FS_ConvertGameInfo
================ ================
*/ */
static qboolean FS_ConvertGameInfo( const char *gamedir, const char *gameinfo_path, const char *liblist_path ) static qboolean FS_ConvertGameInfo( const char *gamedir, const char *gameinfo_path, const char *liblist_path, gameinfo_t *gi, time_t liblist_mtime )
{ {
gameinfo_t GameInfo; memset( gi, 0, sizeof( *gi ));
memset( &GameInfo, 0, sizeof( GameInfo )); // liblist.gam to gameinfo.txt conversion is deprecated, only support for RwDir!
if( FS_ParseLiblistGam( liblist_path, gamedir, gi, liblist_mtime ))
if( FS_ParseLiblistGam( liblist_path, gamedir, &GameInfo ))
{ {
Con_DPrintf( "Convert %s to %s\n", liblist_path, gameinfo_path ); Con_DPrintf( "Convert %s to %s\n", liblist_path, gameinfo_path );
FS_WriteGameInfo( gameinfo_path, &GameInfo ); return FS_WriteGameInfo( gameinfo_path, gi );
return true;
} }
return false; return false;
@@ -999,16 +1023,14 @@ static qboolean FS_ConvertGameInfo( const char *gamedir, const char *gameinfo_pa
FS_ReadGameInfo FS_ReadGameInfo
================ ================
*/ */
static qboolean FS_ReadGameInfo( const char *filepath, const char *gamedir, gameinfo_t *GameInfo ) static qboolean FS_ReadGameInfo( const char *filename, const char *gamedir, gameinfo_t *GameInfo, time_t mtime )
{ {
char *afile; char *afile = FS_LoadDirectFile( filename, NULL );
afile = (char *)FS_LoadFile( filepath, NULL, false );
if( !afile ) if( !afile )
return false; return false;
FS_InitGameInfo( GameInfo, gamedir ); FS_InitGameInfo( GameInfo, gamedir, false, mtime );
FS_ParseGenericGameInfo( GameInfo, afile, true ); FS_ParseGenericGameInfo( GameInfo, afile, true );
Mem_Free( afile ); Mem_Free( afile );
@@ -1024,20 +1046,32 @@ Checks if game directory resembles Quake Engine game directory
(some of checks may as well work with Xash gamedirs, it's not a bug) (some of checks may as well work with Xash gamedirs, it's not a bug)
================ ================
*/ */
static qboolean FS_CheckForQuakeGameDir( const char *gamedir, qboolean direct ) static qboolean FS_CheckForQuakeGameDir( const char *gamedir )
{ {
// if directory contain config.cfg or progs.dat it's 100% gamedir // if directory contain quake.rc or progs.dat it's 100% quake gamedir
// quake mods probably always archived but can missed config.cfg before first running // quake mods probably always archived, so check pak0.pak too
const char *files[] = { "config.cfg", "progs.dat", "pak0.pak" }; const char *files[] = { "progs.dat", "quake.rc" };
char buf[MAX_SYSPATH];
int i; int i;
// try to read pak0.pak first, most quake mods are archived
if( Q_snprintf( buf, sizeof( buf ), "%s/pak0.pak", gamedir ) > 0 )
{
if( FS_SysFileExists( buf ))
{
if( FS_CheckForQuakePak( buf, files, sizeof( files ) / sizeof( files[0] )))
return true;
}
}
// search it in the filesystem
for( i = 0; i < sizeof( files ) / sizeof( files[0] ); i++ ) for( i = 0; i < sizeof( files ) / sizeof( files[0] ); i++ )
{ {
char buf[MAX_VA_STRING]; if( Q_snprintf( buf, sizeof( buf ), "%s/%s", gamedir, files[i] ) > 0 )
{
Q_snprintf( buf, sizeof( buf ), "%s/%s", gamedir, files[i] ); if( FS_SysFileExists( buf ))
if( direct ? FS_SysFileExists( buf ) : FS_FileExists( buf, false )) return true;
return true; }
} }
return false; return false;
@@ -1050,7 +1084,7 @@ FS_CheckForXashGameDir
Checks if game directory resembles Xash3D game directory Checks if game directory resembles Xash3D game directory
=============== ===============
*/ */
static qboolean FS_CheckForXashGameDir( const char *gamedir, qboolean direct ) static qboolean FS_CheckForXashGameDir( const char *gamedir )
{ {
// if directory contain gameinfo.txt or liblist.gam it's 100% gamedir // if directory contain gameinfo.txt or liblist.gam it's 100% gamedir
const char *files[] = { "gameinfo.txt", "liblist.gam" }; const char *files[] = { "gameinfo.txt", "liblist.gam" };
@@ -1058,15 +1092,16 @@ static qboolean FS_CheckForXashGameDir( const char *gamedir, qboolean direct )
for( i = 0; i < sizeof( files ) / sizeof( files[0] ); i++ ) for( i = 0; i < sizeof( files ) / sizeof( files[0] ); i++ )
{ {
char buf[MAX_SYSPATH]; char buf[MAX_SYSPATH];
Q_snprintf( buf, sizeof( buf ), "%s/%s", gamedir, files[i] ); if( Q_snprintf( buf, sizeof( buf ), "%s/%s", gamedir, files[i] ) > 0 )
if( direct ? FS_SysFileExists( buf ) : FS_FileExists( buf, false )) {
return true; if( FS_SysFileExists( buf ))
return true;
}
} }
return false; return false;
} }
/* /*
@@ -1074,86 +1109,74 @@ static qboolean FS_CheckForXashGameDir( const char *gamedir, qboolean direct )
FS_ParseGameInfo FS_ParseGameInfo
================ ================
*/ */
static qboolean FS_ParseGameInfo( const char *gamedir, gameinfo_t *GameInfo ) static qboolean FS_ParseGameInfo( const char *gamedir, gameinfo_t *GameInfo, qboolean rodir )
{ {
string liblist_path, gameinfo_path; char liblist_path[MAX_SYSPATH];
string default_gameinfo_path; char gameinfo_path[MAX_SYSPATH];
qboolean haveUpdate = false; char gamedir_path[MAX_SYSPATH];
time_t liblist_mtime = -1;
time_t gameinfo_mtime = -1;
Q_snprintf( default_gameinfo_path, sizeof( default_gameinfo_path ), "%s/gameinfo.txt", fs_basedir ); if( rodir )
Q_snprintf( gameinfo_path, sizeof( gameinfo_path ), "%s/gameinfo.txt", gamedir ); Q_snprintf( gamedir_path, sizeof( gamedir_path ), "%s/%s", fs_rodir, gamedir );
Q_snprintf( liblist_path, sizeof( liblist_path ), "%s/liblist.gam", gamedir ); else
Q_snprintf( gamedir_path, sizeof( gamedir_path ), "%s/%s", fs_rootdir, gamedir );
// here goes some RoDir magic... if( !FS_CheckForXashGameDir( gamedir_path ))
if( COM_CheckStringEmpty( fs_rodir ))
{ {
string gameinfo_ro, liblist_ro; // check if we need to generate gameinfo for Quake
fs_offset_t roLibListTime, roGameInfoTime, rwGameInfoTime; if( FS_CheckForQuakeGameDir( gamedir_path ))
FS_AllowDirectPaths( true );
Q_snprintf( gameinfo_ro, sizeof( gameinfo_ro ), "%s/%s/gameinfo.txt", fs_rodir, gamedir );
Q_snprintf( liblist_ro, sizeof( liblist_ro ), "%s/%s/liblist.gam", fs_rodir, gamedir );
roLibListTime = FS_SysFileTime( liblist_ro );
roGameInfoTime = FS_SysFileTime( gameinfo_ro );
rwGameInfoTime = FS_SysFileTime( gameinfo_path );
// if rodir's liblist.gam newer than rwdir's gameinfo.txt, then convert it
if( roLibListTime > rwGameInfoTime )
{ {
haveUpdate = FS_ConvertGameInfo( gamedir, gameinfo_path, liblist_ro ); // just generate stub gameinfo in memory
} FS_InitGameInfo( GameInfo, gamedir, true, -1 );
// if rodir's gameinfo.txt newer than rwdir's gameinfo.txt, just copy the file GameInfo->rodir = rodir;
else if( roGameInfoTime > rwGameInfoTime ) return true;
{
file_t *ro, *rw;
fs_offset_t ro_size;
// read & write as binary to copy the exact file
ro = FS_SysOpen( gameinfo_ro, "rb" );
rw = FS_SysOpen( gameinfo_path, "wb" );
FS_Seek( ro, 0, SEEK_END );
ro_size = FS_Tell( ro );
FS_Seek( ro, 0, SEEK_SET );
FS_FileCopy( rw, ro, ro_size );
FS_Close( rw );
FS_Close( ro );
haveUpdate = true;
} }
FS_AllowDirectPaths( false ); // don't add empty or addon directories
return false;
} }
// do not update gameinfo.txt, if it was just copied from rodir's Q_snprintf( gameinfo_path, sizeof( gameinfo_path ), "%s/gameinfo.txt", gamedir_path );
// if user change liblist.gam update the gameinfo.txt Q_snprintf( liblist_path, sizeof( liblist_path ), "%s/liblist.gam", gamedir_path );
if( !haveUpdate && FS_FileTime( liblist_path, false ) > FS_FileTime( gameinfo_path, false ))
FS_ConvertGameInfo( gamedir, gameinfo_path, liblist_path );
// force to create gameinfo for specified game if missing liblist_mtime = FS_SysFileTime( liblist_path );
if(( FS_CheckForQuakeGameDir( gamedir, false ) || !Q_stricmp( fs_gamedir, gamedir )) && !FS_FileExists( gameinfo_path, false )) gameinfo_mtime = FS_SysFileTime( gameinfo_path );
// in this function we never write new files for RoDir compatibility
// since RoDir is only FWGS feature, do gameinfo.txt conversion only
// for RwDir for those who worked with original Xash3D
if( !rodir )
{ {
gameinfo_t tmpGameInfo; // !!!only if we have both liblist.gam and gameinfo.txt try to convert liblist.gam to gameinfo.txt if it's newer!!!
memset( &tmpGameInfo, 0, sizeof( tmpGameInfo )); if( liblist_mtime >= 0 && gameinfo_mtime >= 0 && liblist_mtime > gameinfo_mtime )
if( FS_ReadGameInfo( default_gameinfo_path, gamedir, &tmpGameInfo ))
{ {
// now we have copy of game info from basedir but needs to change gamedir if( FS_ConvertGameInfo( gamedir, gameinfo_path, liblist_path, GameInfo, liblist_mtime ))
Con_DPrintf( "Convert %s to %s\n", default_gameinfo_path, gameinfo_path ); return true;
Q_strncpy( tmpGameInfo.gamefolder, gamedir, sizeof( tmpGameInfo.gamefolder ));
FS_WriteGameInfo( gameinfo_path, &tmpGameInfo );
} }
else FS_CreateDefaultGameInfo( gameinfo_path );
} }
if( !GameInfo || !FS_FileExists( gameinfo_path, false )) // can we parse gameinfo.txt?
return false; // no dest if( gameinfo_mtime >= 0 )
{
if( FS_ReadGameInfo( gameinfo_path, gamedir, GameInfo, gameinfo_mtime ))
{
GameInfo->rodir = rodir;
return true;
}
}
return FS_ReadGameInfo( gameinfo_path, gamedir, GameInfo ); // can we parse liblist.gam?
if( liblist_mtime >= 0 )
{
if( FS_ParseLiblistGam( liblist_path, gamedir, GameInfo, liblist_mtime ))
{
GameInfo->rodir = rodir;
return true;
}
}
return false;
} }
/* /*
@@ -1192,7 +1215,7 @@ void FS_AddGameHierarchy( const char *dir, uint flags )
} }
} }
if( COM_CheckStringEmpty( fs_rodir ) ) if( COM_CheckStringEmpty( fs_rodir ))
{ {
// append new flags to rodir, except FS_GAMEDIR_PATH and FS_CUSTOM_PATH // append new flags to rodir, except FS_GAMEDIR_PATH and FS_CUSTOM_PATH
uint new_flags = FS_NOWRITE_PATH | (flags & (~FS_GAMEDIR_PATH|FS_CUSTOM_PATH)); uint new_flags = FS_NOWRITE_PATH | (flags & (~FS_GAMEDIR_PATH|FS_CUSTOM_PATH));
@@ -1261,10 +1284,12 @@ void FS_LoadGameInfo( const char *rootfolder )
// lock uplevel of gamedir for read\write // lock uplevel of gamedir for read\write
FS_AllowDirectPaths( false ); FS_AllowDirectPaths( false );
if( rootfolder ) Q_strncpy( fs_gamedir, rootfolder, sizeof( fs_gamedir )); if( rootfolder )
Q_strncpy( fs_gamedir, rootfolder, sizeof( fs_gamedir ));
Con_Reportf( "%s( %s )\n", __func__, fs_gamedir ); Con_Reportf( "%s( %s )\n", __func__, fs_gamedir );
// clear any old pathes // clear any old paths
FS_ClearSearchPath(); FS_ClearSearchPath();
// validate gamedir // validate gamedir
@@ -1279,6 +1304,14 @@ void FS_LoadGameInfo( const char *rootfolder )
FI.GameInfo = FI.games[i]; FI.GameInfo = FI.games[i];
if( FI.GameInfo->rodir )
{
// ensure we have directory in rwdir
char buf[MAX_SYSPATH + MAX_QPATH + 2]; // and have plenty of space
Q_snprintf( buf, sizeof( buf ), "%s/%s/", fs_rootdir, FI.GameInfo->gamefolder );
FS_CreatePath( buf );
}
FS_Rescan(); // create new filesystem FS_Rescan(); // create new filesystem
} }
@@ -1339,7 +1372,7 @@ static qboolean FS_FindLibrary( const char *dllname, qboolean directpath, fs_dll
{ {
string fixedname; string fixedname;
searchpath_t *search; searchpath_t *search;
int index, start = 0, i, len; int index, start = 0, len;
// check for bad exports // check for bad exports
if( !COM_CheckString( dllname )) if( !COM_CheckString( dllname ))
@@ -1474,6 +1507,33 @@ static void *Sys_GetNativeObjectStub( const char *object )
return NULL; return NULL;
} }
static void FS_ValidateDirectories( const char *path, qboolean *has_base_dir, qboolean *has_game_dir )
{
stringlist_t dirs;
int i;
stringlistinit( &dirs );
listdirectory( &dirs, path, true );
stringlistsort( &dirs );
for( i = 0; i < dirs.numstrings; i++ )
{
if( !FS_SysFolderExists( dirs.strings[i] ))
continue;
if( !Q_stricmp( fs_basedir, dirs.strings[i] ))
*has_base_dir = true;
if( !Q_stricmp( fs_gamedir, dirs.strings[i] ))
*has_game_dir = true;
if( *has_base_dir && *has_game_dir )
break;
}
stringlistfreecontents( &dirs );
}
/* /*
================ ================
FS_Init FS_Init
@@ -1481,11 +1541,9 @@ FS_Init
*/ */
qboolean FS_InitStdio( qboolean unused_set_to_true, const char *rootdir, const char *basedir, const char *gamedir, const char *rodir ) qboolean FS_InitStdio( qboolean unused_set_to_true, const char *rootdir, const char *basedir, const char *gamedir, const char *rodir )
{ {
stringlist_t dirs; stringlist_t dirs;
qboolean hasBaseDir = false; int i, rodir_num_games;
qboolean hasGameDir = false; char buf[MAX_SYSPATH];
int i;
char buf[MAX_VA_STRING];
FS_InitMemory(); FS_InitMemory();
@@ -1498,63 +1556,40 @@ qboolean FS_InitStdio( qboolean unused_set_to_true, const char *rootdir, const c
Q_strncpy( fs_basedir, basedir, sizeof( fs_basedir )); Q_strncpy( fs_basedir, basedir, sizeof( fs_basedir ));
Q_strncpy( fs_rodir, rodir, sizeof( fs_rodir )); Q_strncpy( fs_rodir, rodir, sizeof( fs_rodir ));
// add readonly directories first // validate user input
if( COM_CheckStringEmpty( fs_rodir )) if( COM_CheckStringEmpty( fs_rodir ) && !Q_stricmp( fs_rodir, fs_rootdir ))
{ {
if( !Q_stricmp( fs_rodir, fs_rootdir )) Sys_Error( "RoDir and default rootdir can't point to same directory!" );
return false;
}
// look for game directories in RwDir first
// this whole check only required to fallback to base directory
// (i.e. default game directory hardcoded in the executable file)
{
qboolean has_base_dir = false;
qboolean has_game_dir = false;
FS_ValidateDirectories( "./", &has_base_dir, &has_game_dir );
if( !has_game_dir )
{ {
Sys_Error( "RoDir and default rootdir can't point to same directory!" ); // look for game directories in RoDir now
return false; if( COM_CheckStringEmpty( fs_rodir ))
} FS_ValidateDirectories( fs_rodir, &has_base_dir, &has_game_dir );
stringlistinit( &dirs ); if( !has_game_dir )
listdirectory( &dirs, fs_rodir );
stringlistsort( &dirs );
for( i = 0; i < dirs.numstrings; i++ )
{
char roPath[MAX_SYSPATH];
Q_snprintf( roPath, sizeof( roPath ), "%s/%s/", fs_rodir, dirs.strings[i] );
// check if it's a directory
if( !FS_SysFolderExists( roPath ))
continue;
// check if it's gamedir
if( FS_CheckForXashGameDir( roPath, true ) || FS_CheckForQuakeGameDir( roPath, true ))
{ {
char rwPath[MAX_SYSPATH]; Con_Printf( S_ERROR "game directory \"%s\" not exist\n", fs_gamedir );
Q_snprintf( rwPath, sizeof( rwPath ), "%s/%s/", fs_rootdir, dirs.strings[i] ); // revert to base game directory
FS_CreatePath( rwPath ); if( has_base_dir )
Q_strncpy( fs_gamedir, fs_basedir, sizeof( fs_gamedir ));
} }
} }
stringlistfreecontents( &dirs );
} }
// validate directories // now start building first level of directory hierarchy
stringlistinit( &dirs );
listdirectory( &dirs, "./" );
stringlistsort( &dirs );
for( i = 0; i < dirs.numstrings; i++ )
{
if( !Q_stricmp( fs_basedir, dirs.strings[i] ))
hasBaseDir = true;
if( !Q_stricmp( fs_gamedir, dirs.strings[i] ))
hasGameDir = true;
}
if( !hasGameDir )
{
Con_Printf( S_ERROR "game directory \"%s\" not exist\n", fs_gamedir );
if( hasBaseDir ) Q_strncpy( fs_gamedir, fs_basedir, sizeof( fs_gamedir ));
}
// build list of game directories here
if( COM_CheckStringEmpty( fs_rodir )) if( COM_CheckStringEmpty( fs_rodir ))
{ {
Q_snprintf( buf, sizeof( buf ), "%s/", fs_rodir ); Q_snprintf( buf, sizeof( buf ), "%s/", fs_rodir );
@@ -1562,15 +1597,62 @@ qboolean FS_InitStdio( qboolean unused_set_to_true, const char *rootdir, const c
} }
FS_AddGameDirectory( "./", FS_STATIC_PATH ); FS_AddGameDirectory( "./", FS_STATIC_PATH );
// but scan rodir for games first
if( COM_CheckStringEmpty( fs_rodir ))
{
stringlistinit( &dirs );
listdirectory( &dirs, fs_rodir, true );
stringlistsort( &dirs );
for( i = 0; i < dirs.numstrings; i++ )
{
if( !FS_SysFolderExists( dirs.strings[i] ))
continue;
if( FI.games[FI.numgames] == NULL )
FI.games[FI.numgames] = Mem_Malloc( fs_mempool, sizeof( *FI.games[FI.numgames] ));
if( FS_ParseGameInfo( dirs.strings[i], FI.games[FI.numgames], true ))
FI.numgames++;
}
stringlistfreecontents( &dirs );
}
rodir_num_games = FI.numgames;
stringlistinit( &dirs );
listdirectory( &dirs, "./", true );
stringlistsort( &dirs );
for( i = 0; i < dirs.numstrings; i++ ) for( i = 0; i < dirs.numstrings; i++ )
{ {
int j;
if( !FS_SysFolderExists( dirs.strings[i] )) if( !FS_SysFolderExists( dirs.strings[i] ))
continue; continue;
if( FI.games[FI.numgames] == NULL ) // update gameinfo from rwdir, if it's newer
FI.games[FI.numgames] = (gameinfo_t *)Mem_Calloc( fs_mempool, sizeof( gameinfo_t )); // with rodir we should never create new gameinfos anymore,
// so this only catches possible user changes
for( j = 0; j < rodir_num_games; j++ )
{
if( !Q_stricmp( dirs.strings[i], FI.games[j]->gamefolder ))
{
gameinfo_t gi;
if( FS_ParseGameInfo( dirs.strings[i], &gi, false ))
{
if( gi.mtime > FI.games[j]->mtime )
*FI.games[j] = gi;
}
break;
}
}
if( FS_ParseGameInfo( dirs.strings[i], FI.games[FI.numgames] )) if( FI.games[FI.numgames] == NULL )
FI.games[FI.numgames] = Mem_Calloc( fs_mempool, sizeof( *FI.games[FI.numgames] ));
if( FS_ParseGameInfo( dirs.strings[i], FI.games[FI.numgames], false ))
FI.numgames++; // added FI.numgames++; // added
} }
@@ -1666,9 +1748,10 @@ Internal function used to create a file_t and open the relevant non-packed file
*/ */
file_t *FS_SysOpen( const char *filepath, const char *mode ) file_t *FS_SysOpen( const char *filepath, const char *mode )
{ {
file_t *file; file_t *file;
int mod, opt; int mod, opt, fd = -1;
uint ind; qboolean memfile = false;
uint ind;
// Parse the mode string // Parse the mode string
switch( mode[0] ) switch( mode[0] )
@@ -1708,30 +1791,52 @@ file_t *FS_SysOpen( const char *filepath, const char *mode )
} }
} }
file = (file_t *)Mem_Calloc( fs_mempool, sizeof( *file )); // the 'm' flag let's user to create temporary file in memory
file->filetime = FS_SysFileTime( filepath ); // through so-called "anonymous files"
file->ungetc = EOF; if( Q_strchr( mode, 'm' ))
{
#if HAVE_MEMFD_CREATE
#ifndef MFD_NOEXEC_SEAL
#define MFD_NOEXEC_SEAL 8U
#endif
fd = memfd_create( filepath, MFD_CLOEXEC | MFD_NOEXEC_SEAL );
// through fcntl() and MFD_ALLOW_SEALING we could enforce
// read-write flags but we don't really care about them yet
if( fd < 0 )
Con_Printf( S_ERROR "%s: can't create anonymous file %s: %s", __func__, filepath, strerror( errno ));
else memfile = true;
#endif
// if it's unsupported, we can open it on disk
}
if( fd < 0 )
{
#if XASH_WIN32 #if XASH_WIN32
file->handle = _wopen( FS_PathToWideChar( filepath ), mod | opt, 0666 ); fd = _wopen( FS_PathToWideChar( filepath ), mod | opt, 0666 );
#else #else
file->handle = open( filepath, mod|opt, 0666 ); fd = open( filepath, mod|opt, 0666 );
#endif #endif
}
#if !XASH_WIN32 if( fd < 0 )
if( file->handle < 0 )
FS_BackupFileName( file, filepath, mod|opt );
#endif
if( file->handle < 0 )
{ {
if( errno != ENOENT ) if( errno != ENOENT )
Con_Printf( S_ERROR "%s: can't open file %s: %s\n", __func__, filepath, strerror( errno )); Con_Printf( S_ERROR "%s: can't open file %s: %s\n", __func__, filepath, strerror( errno ));
Mem_Free( file );
return NULL; return NULL;
} }
file = (file_t *)Mem_Calloc( fs_mempool, sizeof( *file ));
file->filetime = memfile ? 0 : FS_SysFileTime( filepath );
file->ungetc = EOF;
file->handle = fd;
#if !XASH_WIN32
if( !memfile )
FS_BackupFileName( file, filepath, mod|opt );
#endif
file->searchpath = NULL; file->searchpath = NULL;
file->real_length = lseek( file->handle, 0, SEEK_END ); file->real_length = lseek( file->handle, 0, SEEK_END );
@@ -3159,6 +3264,8 @@ const fs_api_t g_api =
FS_LoadFileFromArchive, FS_LoadFileFromArchive,
FS_GetRootDirectory, FS_GetRootDirectory,
FS_MakeGameInfo,
}; };
int EXPORT GetFSAPI( int version, fs_api_t *api, fs_globals_t **globals, fs_interface_t *engfuncs ); int EXPORT GetFSAPI( int version, fs_api_t *api, fs_globals_t **globals, fs_interface_t *engfuncs );

View File

@@ -123,6 +123,9 @@ typedef struct gameinfo_s
qboolean animated_title; qboolean animated_title;
char demomap[MAX_QPATH]; char demomap[MAX_QPATH];
qboolean rodir; // if true, parsed from rodir
int64_t mtime;
} gameinfo_t; } gameinfo_t;
typedef struct fs_dllinfo_t typedef struct fs_dllinfo_t
@@ -226,6 +229,8 @@ typedef struct fs_api_t
// gets current root directory, set by InitStdio // gets current root directory, set by InitStdio
qboolean (*GetRootDirectory)( char *path, size_t size ); qboolean (*GetRootDirectory)( char *path, size_t size );
void (*MakeGameInfo)( void );
} fs_api_t; } fs_api_t;
typedef struct fs_interface_t typedef struct fs_interface_t

View File

@@ -205,7 +205,7 @@ void stringlistinit( stringlist_t *list );
void stringlistfreecontents( stringlist_t *list ); void stringlistfreecontents( stringlist_t *list );
void stringlistappend( stringlist_t *list, const char *text ); void stringlistappend( stringlist_t *list, const char *text );
void stringlistsort( stringlist_t *list ); void stringlistsort( stringlist_t *list );
void listdirectory( stringlist_t *list, const char *path ); void listdirectory( stringlist_t *list, const char *path, qboolean dirs_only );
// filesystem ops // filesystem ops
int FS_FileExists( const char *filename, int gamedironly ); int FS_FileExists( const char *filename, int gamedironly );
@@ -230,6 +230,7 @@ qboolean FS_FullPathToRelativePath( char *dst, const char *src, size_t size );
// //
// pak.c // pak.c
// //
qboolean FS_CheckForQuakePak( const char *pakfile, const char *files[], size_t num_files );
searchpath_t *FS_AddPak_Fullpath( const char *pakfile, int flags ); searchpath_t *FS_AddPak_Fullpath( const char *pakfile, int flags );
// //

View File

@@ -105,6 +105,8 @@ static pack_t *FS_LoadPackPAK( const char *packfile, int *error )
// TODO: use FS_Open to allow PK3 to be included into other archives // TODO: use FS_Open to allow PK3 to be included into other archives
// Currently, it doesn't work with rodir due to FS_FindFile logic // Currently, it doesn't work with rodir due to FS_FindFile logic
// when it will use FS_Open, check that FS_CheckForQuakePak correctly
// detects Quake gamedirs in RoDir
packhandle = FS_SysOpen( packfile, "rb" ); packhandle = FS_SysOpen( packfile, "rb" );
if( packhandle == NULL ) if( packhandle == NULL )
@@ -364,3 +366,45 @@ searchpath_t *FS_AddPak_Fullpath( const char *pakfile, int flags )
return search; return search;
} }
/*
================
FS_CheckForQuakePak
To generate fake gameinfo for Quake directory, we need to parse pak0.pak
and find progs.dat in it
================
*/
qboolean FS_CheckForQuakePak( const char *pakfile, const char *files[], size_t num_files )
{
qboolean is_quake = false;
pack_t *pak;
int i;
pak = FS_LoadPackPAK( pakfile, NULL );
if( !pak )
return false;
for( i = 0; i < num_files; i++ )
{
int j;
for( j = 0; j < pak->numfiles; j++ )
{
if( Q_strchr( pak->files[j].name, '/' ))
continue; // exclude subdirectories
if( !Q_stricmp( pak->files[j].name, files[i] ))
{
is_quake = true;
break;
}
}
if( is_quake )
break;
}
Mem_Free( pak );
return is_quake;
}

View File

@@ -1,5 +1,14 @@
#!/usr/bin/env python #!/usr/bin/env python
MEMFD_CREATE_TEST = '''#define _GNU_SOURCE
#include <sys/mman.h>
int main(int argc, char **argv) { return memfd_create(argv[0], 0); }'''
DIRENT_D_TYPE_TEST = '''#define _GNU_SOURCE
#include <dirent.h>
int main(int argc, char **argv) { struct dirent entry; entry.d_type = DT_DIR; return 0; }
'''
def options(opt): def options(opt):
pass pass
@@ -11,6 +20,12 @@ def configure(conf):
elif conf.env.cxxshlib_PATTERN.startswith('lib'): # remove lib prefix for other systems than Android elif conf.env.cxxshlib_PATTERN.startswith('lib'): # remove lib prefix for other systems than Android
conf.env.cxxshlib_PATTERN = conf.env.cxxshlib_PATTERN[3:] conf.env.cxxshlib_PATTERN = conf.env.cxxshlib_PATTERN[3:]
if conf.check_cc(fragment=MEMFD_CREATE_TEST, msg='Checking for memfd_create', mandatory=False):
conf.define('HAVE_MEMFD_CREATE', 1)
if conf.check_cc(fragment=DIRENT_D_TYPE_TEST, msg='Checking for d_type field in struct dirent', mandatory=False):
conf.define('HAVE_DIRENT_D_TYPE', 1)
def build(bld): def build(bld):
bld(name = 'filesystem_includes', export_includes = '.') bld(name = 'filesystem_includes', export_includes = '.')

View File

@@ -171,7 +171,7 @@ _inline int Sys_Start( void )
if( Xash_Shutdown ) if( Xash_Shutdown )
changeGame = Sys_ChangeGame; changeGame = Sys_ChangeGame;
ret = Xash_Main( szArgc, szArgv, szGameDir, 0, changeGame ); ret = Xash_Main( szArgc, szArgv, szGameDir, 0, XASH_DISABLE_MENU_CHANGEGAME ? NULL : changeGame );
Sys_UnloadEngine(); Sys_UnloadEngine();

View File

@@ -18,7 +18,7 @@ def configure(conf):
if conf.env.DEST_OS == 'win32': if conf.env.DEST_OS == 'win32':
conf.load('winres') conf.load('winres')
conf.define_cond('XASH_DISABLE_MENU_CHANGEGAME', conf.options.DISABLE_MENU_CHANGEGAME) conf.define('XASH_DISABLE_MENU_CHANGEGAME', conf.options.DISABLE_MENU_CHANGEGAME)
def build(bld): def build(bld):
source = ['game.cpp'] source = ['game.cpp']

View File

@@ -663,21 +663,6 @@ void COM_RemoveLineFeed( char *str, size_t bufsize )
} }
} }
/*
============
COM_FixSlashes
Changes all '\' characters into '/' characters, in place.
============
*/
void COM_FixSlashes( char *pname )
{
while(( pname = Q_strchr( pname, '\\' )))
{
*pname = '/';
}
}
/* /*
============ ============
COM_PathSlashFix COM_PathSlashFix

View File

@@ -1,6 +1,7 @@
/* /*
crtlib.h - internal stdlib crtlib.h - internal stdlib
Copyright (C) 2011 Uncle Mike Copyright (C) 2011 Uncle Mike
Copyright (C) Free Software Foundation, Inc
This program is free software: you can redistribute it and/or modify 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 it under the terms of the GNU General Public License as published by
@@ -95,7 +96,6 @@ void COM_ExtractFilePath( const char *path, char *dest );
const char *COM_FileWithoutPath( const char *in ); const char *COM_FileWithoutPath( const char *in );
void COM_StripExtension( char *path ); void COM_StripExtension( char *path );
void COM_RemoveLineFeed( char *str, size_t bufsize ); void COM_RemoveLineFeed( char *str, size_t bufsize );
void COM_FixSlashes( char *pname );
void COM_PathSlashFix( char *path ); void COM_PathSlashFix( char *path );
// return 0 on empty or null string, 1 otherwise // return 0 on empty or null string, 1 otherwise
#define COM_CheckString( string ) ( ( !string || !*string ) ? 0 : 1 ) #define COM_CheckString( string ) ( ( !string || !*string ) ? 0 : 1 )
@@ -105,6 +105,12 @@ char *COM_ParseFileSafe( char *data, char *token, const int size, unsigned int f
int matchpattern( const char *in, const char *pattern, qboolean caseinsensitive ); int matchpattern( const char *in, const char *pattern, qboolean caseinsensitive );
int matchpattern_with_separator( const char *in, const char *pattern, qboolean caseinsensitive, const char *separators, qboolean wildcard_least_one ); int matchpattern_with_separator( const char *in, const char *pattern, qboolean caseinsensitive, const char *separators, qboolean wildcard_least_one );
//
// dllhelpers.c
//
void ClearExports( const dllfunc_t *funcs, size_t num_funcs );
qboolean ValidateExports( const dllfunc_t *funcs, size_t num_funcs );
static inline char Q_toupper( const char in ) static inline char Q_toupper( const char in )
{ {
char out; char out;
@@ -169,63 +175,62 @@ static inline char *Q_strstr( const char *s1, const char *s2 )
} }
// libc extensions, be careful what to enable or what not // libc extensions, be careful what to enable or what not
static inline size_t Q_strnlen( const char *str, size_t size )
{
#if HAVE_STRNLEN
return strnlen( str, size );
#else
const char *p = (const char *)memchr( str, 0, size );
return p ? p - str : size;
#endif
}
static inline size_t Q_strncpy( char *dst, const char *src, size_t size ) static inline size_t Q_strncpy( char *dst, const char *src, size_t size )
{ {
#if HAVE_STRLCPY
if( unlikely( !dst || !src || !size )) if( unlikely( !dst || !src || !size ))
return 0; return 0;
#if HAVE_STRLCPY
return strlcpy( dst, src, size ); return strlcpy( dst, src, size );
#else #else
size_t len;
if( unlikely( !dst || !src || !size ))
return 0;
len = strlen( src );
if( len + 1 > size ) // check if truncate
{ {
memcpy( dst, src, size - 1 ); size_t len = strlen( src );
dst[size - 1] = 0;
}
else memcpy( dst, src, len + 1 );
return len; // count does not include NULL if( len >= size ) // check if truncate
{
memcpy( dst, src, size );
dst[size - 1] = 0;
}
else memcpy( dst, src, len + 1 );
return len; // count does not include NULL
}
#endif #endif
} }
static inline size_t Q_strncat( char *dst, const char *src, size_t size ) static inline size_t Q_strncat( char *dst, const char *src, size_t size )
{ {
#if HAVE_STRLCAT
if( unlikely( !dst || !src || !size )) if( unlikely( !dst || !src || !size ))
return 0; return 0;
#if HAVE_STRLCAT
return strlcat( dst, src, size ); return strlcat( dst, src, size );
#else #else
char *d = dst;
const char *s = src;
size_t n = size;
size_t dlen;
if( unlikely( !dst || !src || !size ))
return 0;
// find the end of dst and adjust bytes left but don't go past end
while( n-- != 0 && *d != '\0' ) d++;
dlen = d - dst;
n = size - dlen;
if( n == 0 ) return( dlen + Q_strlen( s ));
while( *s != '\0' )
{ {
if( n != 1 ) size_t slen = strlen( src );
{ size_t dlen = Q_strnlen( dst, size );
*d++ = *s;
n--;
}
s++;
}
*d = '\0'; if( dlen != size )
return( dlen + ( s - src )); // count does not include NULL {
size_t copy = size - dlen - 1;
if( copy > slen )
copy = slen;
memcpy( &dst[dlen], src, copy );
dst[dlen + copy] = 0;
}
return dlen + slen;
}
#endif #endif
} }
@@ -314,6 +319,19 @@ static inline int Q_splitstr( char *str, int delim, void *userdata,
return ret; return ret;
} }
/*
============
COM_FixSlashes
Changes all '\' characters into '/' characters, in place.
============
*/
static inline void COM_FixSlashes( char *pname )
{
while(( pname = Q_strchr( pname, '\\' )))
*pname = '/';
}
#ifdef __cplusplus #ifdef __cplusplus
} }
#endif #endif

36
public/dllhelpers.c Normal file
View File

@@ -0,0 +1,36 @@
/*
dllhelpers.c - dll exports helpers
Copyright (C) 2024 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 "crtlib.h"
void ClearExports( const dllfunc_t *funcs, size_t num_funcs )
{
size_t i;
for( i = 0; i < num_funcs; i++ )
*(funcs[i].func) = NULL;
}
qboolean ValidateExports( const dllfunc_t *funcs, size_t num_funcs )
{
size_t i;
for( i = 0; i < num_funcs; i++ )
{
if( *(funcs[i].func) == NULL )
return false;
}
return true;
}

View File

@@ -29,6 +29,10 @@ int main(int argc, char **argv) { return strlcpy(argv[1], argv[2], 10); }'''
STRLCAT_TEST = '''#include <string.h> STRLCAT_TEST = '''#include <string.h>
int main(int argc, char **argv) { return strlcat(argv[1], argv[2], 10); }''' int main(int argc, char **argv) { return strlcat(argv[1], argv[2], 10); }'''
STRNLEN_TEST = '''#include <string.h>
int main(int argc, char **argv) { return (int)strnlen(argv[0], 10); }
'''
ALLOCA_TEST = '''#include <%s> ALLOCA_TEST = '''#include <%s>
int main(void) { alloca(1); return 0; }''' int main(void) { alloca(1); return 0; }'''
@@ -106,6 +110,7 @@ def configure(conf):
check_libc_extension(STRCHRNUL_TEST, 'strchrnul', 'HAVE_STRCHRNUL') check_libc_extension(STRCHRNUL_TEST, 'strchrnul', 'HAVE_STRCHRNUL')
check_libc_extension(STRLCPY_TEST, 'strlcpy', 'HAVE_STRLCPY') check_libc_extension(STRLCPY_TEST, 'strlcpy', 'HAVE_STRLCPY')
check_libc_extension(STRLCAT_TEST, 'strlcat', 'HAVE_STRLCAT') check_libc_extension(STRLCAT_TEST, 'strlcat', 'HAVE_STRLCAT')
check_libc_extension(STRNLEN_TEST, 'strnlen', 'HAVE_STRNLEN')
# kill temporary uselib # kill temporary uselib
del conf.env.DEFINES_export del conf.env.DEFINES_export

View File

@@ -20,6 +20,7 @@ GNU General Public License for more details.
#if HAVE_TGMATH_H #if HAVE_TGMATH_H
#include <tgmath.h> #include <tgmath.h>
#endif #endif
#include <string.h>
#include "build.h" #include "build.h"
#include "xash3d_types.h" #include "xash3d_types.h"
@@ -141,15 +142,6 @@ CONSTANTS GLOBALS
// a1ba: we never return pointers to these globals // a1ba: we never return pointers to these globals
// so help compiler optimize constants away // so help compiler optimize constants away
#define vec3_origin ((vec3_t){ 0.0f, 0.0f, 0.0f }) #define vec3_origin ((vec3_t){ 0.0f, 0.0f, 0.0f })
#define m_matrix3x4_identity ((matrix3x4) { \
{ 1.0f, 0.0f, 0.0f, 0.0f }, \
{ 0.0f, 1.0f, 0.0f, 0.0f }, \
{ 0.0f, 0.0f, 1.0f, 0.0f }} )
#define m_matrix4x4_identity ((matrix4x4) { \
{ 1.0f, 0.0f, 0.0f, 0.0f }, \
{ 0.0f, 1.0f, 0.0f, 0.0f }, \
{ 0.0f, 0.0f, 1.0f, 0.0f }, \
{ 0.0f, 0.0f, 0.0f, 1.0f }} )
extern const int boxpnt[6][4]; extern const int boxpnt[6][4];
extern const float m_bytenormals[NUMVERTEXNORMALS][3]; extern const float m_bytenormals[NUMVERTEXNORMALS][3];
@@ -191,7 +183,11 @@ int BoxOnPlaneSide( const vec3_t emins, const vec3_t emaxs, const mplane_t *p );
// //
// matrixlib.c // matrixlib.c
// //
#define Matrix3x4_LoadIdentity( mat ) Matrix3x4_Copy( mat, m_matrix3x4_identity ) static inline void Matrix3x4_LoadIdentity( matrix3x4 m )
{
memset( m, 0, sizeof( *m ));
m[0][0] = m[1][1] = m[2][2] = 1.0f;
}
#define Matrix3x4_Copy( out, in ) memcpy( out, in, sizeof( matrix3x4 )) #define Matrix3x4_Copy( out, in ) memcpy( out, in, sizeof( matrix3x4 ))
void Matrix3x4_VectorTransform( const matrix3x4 in, const float v[3], float out[3] ); void Matrix3x4_VectorTransform( const matrix3x4 in, const float v[3], float out[3] );
void Matrix3x4_VectorITransform( const matrix3x4 in, const float v[3], float out[3] ); void Matrix3x4_VectorITransform( const matrix3x4 in, const float v[3], float out[3] );
@@ -203,7 +199,11 @@ void Matrix3x4_CreateFromEntity( matrix3x4 out, const vec3_t angles, const vec3_
void Matrix3x4_TransformAABB( const matrix3x4 world, const vec3_t mins, const vec3_t maxs, vec3_t absmin, vec3_t absmax ); void Matrix3x4_TransformAABB( const matrix3x4 world, const vec3_t mins, const vec3_t maxs, vec3_t absmin, vec3_t absmax );
void Matrix3x4_AnglesFromMatrix( const matrix3x4 in, vec3_t out ); void Matrix3x4_AnglesFromMatrix( const matrix3x4 in, vec3_t out );
#define Matrix4x4_LoadIdentity( mat ) Matrix4x4_Copy( mat, m_matrix4x4_identity ) static inline void Matrix4x4_LoadIdentity( matrix4x4 m )
{
memset( m, 0, sizeof( *m ));
m[0][0] = m[1][1] = m[2][2] = m[3][3] = 1.0f;
}
#define Matrix4x4_Copy( out, in ) memcpy( out, in, sizeof( matrix4x4 )) #define Matrix4x4_Copy( out, in ) memcpy( out, in, sizeof( matrix4x4 ))
void Matrix4x4_VectorTransform( const matrix4x4 in, const float v[3], float out[3] ); void Matrix4x4_VectorTransform( const matrix4x4 in, const float v[3], float out[3] );
void Matrix4x4_VectorITransform( const matrix4x4 in, const float v[3], float out[3] ); void Matrix4x4_VectorITransform( const matrix4x4 in, const float v[3], float out[3] );

View File

@@ -670,10 +670,14 @@ static void R_DecalNodeSurfaces( model_t *model, mnode_t *node, decalinfo_t *dec
// iterate over all surfaces in the node // iterate over all surfaces in the node
msurface_t *surf; msurface_t *surf;
int i; int i;
int firstsurface, numsurfaces;
surf = model->surfaces + node->firstsurface; firstsurface = node_firstsurface( node, model );
numsurfaces = node_numsurfaces( node, model );
for( i = 0; i < node->numsurfaces; i++, surf++ ) surf = model->surfaces + firstsurface;
for( i = 0; i < numsurfaces; i++, surf++ )
{ {
// never apply decals on the water or sky surfaces // never apply decals on the water or sky surfaces
if( surf->flags & (SURF_DRAWTURB|SURF_DRAWSKY|SURF_CONVEYOR)) if( surf->flags & (SURF_DRAWTURB|SURF_DRAWSKY|SURF_CONVEYOR))
@@ -695,6 +699,7 @@ static void R_DecalNode( model_t *model, mnode_t *node, decalinfo_t *decalinfo )
{ {
mplane_t *splitplane; mplane_t *splitplane;
float dist; float dist;
mnode_t *children[2];
Assert( node != NULL ); Assert( node != NULL );
@@ -706,6 +711,7 @@ static void R_DecalNode( model_t *model, mnode_t *node, decalinfo_t *decalinfo )
splitplane = node->plane; splitplane = node->plane;
dist = DotProduct( decalinfo->m_Position, splitplane->normal ) - splitplane->dist; dist = DotProduct( decalinfo->m_Position, splitplane->normal ) - splitplane->dist;
node_children( children, node, model );
// This is arbitrarily set to 10 right now. In an ideal world we'd have the // This is arbitrarily set to 10 right now. In an ideal world we'd have the
// exact surface but we don't so, this tells me which planes are "sort of // exact surface but we don't so, this tells me which planes are "sort of
@@ -717,19 +723,19 @@ static void R_DecalNode( model_t *model, mnode_t *node, decalinfo_t *decalinfo )
// have a surface normal // have a surface normal
if( dist > decalinfo->m_Size ) if( dist > decalinfo->m_Size )
{ {
R_DecalNode( model, node->children[0], decalinfo ); R_DecalNode( model, children[0], decalinfo );
} }
else if( dist < -decalinfo->m_Size ) else if( dist < -decalinfo->m_Size )
{ {
R_DecalNode( model, node->children[1], decalinfo ); R_DecalNode( model, children[1], decalinfo );
} }
else else
{ {
if( dist < DECAL_DISTANCE && dist > -DECAL_DISTANCE ) if( dist < DECAL_DISTANCE && dist > -DECAL_DISTANCE )
R_DecalNodeSurfaces( model, node, decalinfo ); R_DecalNodeSurfaces( model, node, decalinfo );
R_DecalNode( model, node->children[0], decalinfo ); R_DecalNode( model, children[0], decalinfo );
R_DecalNode( model, node->children[1], decalinfo ); R_DecalNode( model, children[1], decalinfo );
} }
} }

View File

@@ -502,11 +502,11 @@ void R_ClearSkyBox( void );
void R_DrawSkyBox( void ); void R_DrawSkyBox( void );
void R_DrawClouds( void ); void R_DrawClouds( void );
void R_UnloadSkybox( void ); void R_UnloadSkybox( void );
void EmitWaterPolys( msurface_t *warp, qboolean reverse ); void EmitWaterPolys( msurface_t *warp, qboolean reverse, qboolean ripples );
void R_InitRipples( void ); void R_InitRipples( void );
void R_ResetRipples( void ); void R_ResetRipples( void );
void R_AnimateRipples( void ); void R_AnimateRipples( void );
void R_UploadRipples( texture_t *image ); qboolean R_UploadRipples( const texture_t *image );
//#include "vid_common.h" //#include "vid_common.h"
@@ -561,8 +561,6 @@ void GL_CheckForErrors_( const char *filename, const int fileline );
const char *GL_ErrorString( int err ); const char *GL_ErrorString( int err );
qboolean GL_Support( int r_ext ); qboolean GL_Support( int r_ext );
int GL_MaxTextureUnits( void ); int GL_MaxTextureUnits( void );
qboolean GL_CheckExtension( const char *name, const dllfunc_t *funcs, const char *cvarname, int r_ext, float minver );
void GL_SetExtension( int r_ext, int enable );
// //
// gl_triapi.c // gl_triapi.c

View File

@@ -38,7 +38,6 @@ CVAR_DEFINE_AUTO( r_ripple, "0", FCVAR_GLCONFIG, "enable software-like water tex
CVAR_DEFINE_AUTO( r_ripple_updatetime, "0.05", FCVAR_GLCONFIG, "how fast ripple simulation is" ); CVAR_DEFINE_AUTO( r_ripple_updatetime, "0.05", FCVAR_GLCONFIG, "how fast ripple simulation is" );
CVAR_DEFINE_AUTO( r_ripple_spawntime, "0.1", FCVAR_GLCONFIG, "how fast new ripples spawn" ); CVAR_DEFINE_AUTO( r_ripple_spawntime, "0.1", FCVAR_GLCONFIG, "how fast new ripples spawn" );
DEFINE_ENGINE_SHARED_CVAR_LIST() DEFINE_ENGINE_SHARED_CVAR_LIST()
poolhandle_t r_temppool; poolhandle_t r_temppool;
@@ -54,208 +53,199 @@ glwstate_t glw_state;
#define GL_CALL( x ) #x, (void**)&p##x #define GL_CALL( x ) #x, (void**)&p##x
#endif #endif
static dllfunc_t opengl_110funcs[] = static const dllfunc_t opengl_110funcs[] =
{ {
{ GL_CALL( glClearColor ) }, { GL_CALL( glClearColor ) },
{ GL_CALL( glClear ) }, { GL_CALL( glClear ) },
{ GL_CALL( glAlphaFunc ) }, { GL_CALL( glAlphaFunc ) },
{ GL_CALL( glBlendFunc ) }, { GL_CALL( glBlendFunc ) },
{ GL_CALL( glCullFace ) }, { GL_CALL( glCullFace ) },
{ GL_CALL( glDrawBuffer ) }, { GL_CALL( glDrawBuffer ) },
{ GL_CALL( glReadBuffer ) }, { GL_CALL( glReadBuffer ) },
{ GL_CALL( glAccum ) }, { GL_CALL( glAccum ) },
{ GL_CALL( glEnable ) }, { GL_CALL( glEnable ) },
{ GL_CALL( glDisable ) }, { GL_CALL( glDisable ) },
{ GL_CALL( glEnableClientState ) }, { GL_CALL( glEnableClientState ) },
{ GL_CALL( glDisableClientState ) }, { GL_CALL( glDisableClientState ) },
{ GL_CALL( glGetBooleanv ) }, { GL_CALL( glGetBooleanv ) },
{ GL_CALL( glGetDoublev ) }, { GL_CALL( glGetDoublev ) },
{ GL_CALL( glGetFloatv ) }, { GL_CALL( glGetFloatv ) },
{ GL_CALL( glGetIntegerv ) }, { GL_CALL( glGetIntegerv ) },
{ GL_CALL( glGetError ) }, { GL_CALL( glGetError ) },
{ GL_CALL( glGetString ) }, { GL_CALL( glGetString ) },
{ GL_CALL( glFinish ) }, { GL_CALL( glFinish ) },
{ GL_CALL( glFlush ) }, { GL_CALL( glFlush ) },
{ GL_CALL( glClearDepth ) }, { GL_CALL( glClearDepth ) },
{ GL_CALL( glDepthFunc ) }, { GL_CALL( glDepthFunc ) },
{ GL_CALL( glDepthMask ) }, { GL_CALL( glDepthMask ) },
{ GL_CALL( glDepthRange ) }, { GL_CALL( glDepthRange ) },
{ GL_CALL( glFrontFace ) }, { GL_CALL( glFrontFace ) },
{ GL_CALL( glDrawElements ) }, { GL_CALL( glDrawElements ) },
{ GL_CALL( glDrawArrays ) }, { GL_CALL( glDrawArrays ) },
{ GL_CALL( glColorMask ) }, { GL_CALL( glColorMask ) },
{ GL_CALL( glIndexPointer ) }, { GL_CALL( glIndexPointer ) },
{ GL_CALL( glVertexPointer ) }, { GL_CALL( glVertexPointer ) },
{ GL_CALL( glNormalPointer ) }, { GL_CALL( glNormalPointer ) },
{ GL_CALL( glColorPointer ) }, { GL_CALL( glColorPointer ) },
{ GL_CALL( glTexCoordPointer ) }, { GL_CALL( glTexCoordPointer ) },
{ GL_CALL( glArrayElement ) }, { GL_CALL( glArrayElement ) },
{ GL_CALL( glColor3f ) }, { GL_CALL( glColor3f ) },
{ GL_CALL( glColor3fv ) }, { GL_CALL( glColor3fv ) },
{ GL_CALL( glColor4f ) }, { GL_CALL( glColor4f ) },
{ GL_CALL( glColor4fv ) }, { GL_CALL( glColor4fv ) },
{ GL_CALL( glColor3ub ) }, { GL_CALL( glColor3ub ) },
{ GL_CALL( glColor4ub ) }, { GL_CALL( glColor4ub ) },
{ GL_CALL( glColor4ubv ) }, { GL_CALL( glColor4ubv ) },
{ GL_CALL( glTexCoord1f ) }, { GL_CALL( glTexCoord1f ) },
{ GL_CALL( glTexCoord2f ) }, { GL_CALL( glTexCoord2f ) },
{ GL_CALL( glTexCoord3f ) }, { GL_CALL( glTexCoord3f ) },
{ GL_CALL( glTexCoord4f ) }, { GL_CALL( glTexCoord4f ) },
{ GL_CALL( glTexCoord1fv ) }, { GL_CALL( glTexCoord1fv ) },
{ GL_CALL( glTexCoord2fv ) }, { GL_CALL( glTexCoord2fv ) },
{ GL_CALL( glTexCoord3fv ) }, { GL_CALL( glTexCoord3fv ) },
{ GL_CALL( glTexCoord4fv ) }, { GL_CALL( glTexCoord4fv ) },
{ GL_CALL( glTexGenf ) }, { GL_CALL( glTexGenf ) },
{ GL_CALL( glTexGenfv ) }, { GL_CALL( glTexGenfv ) },
{ GL_CALL( glTexGeni ) }, { GL_CALL( glTexGeni ) },
{ GL_CALL( glVertex2f ) }, { GL_CALL( glVertex2f ) },
{ GL_CALL( glVertex3f ) }, { GL_CALL( glVertex3f ) },
{ GL_CALL( glVertex3fv ) }, { GL_CALL( glVertex3fv ) },
{ GL_CALL( glNormal3f ) }, { GL_CALL( glNormal3f ) },
{ GL_CALL( glNormal3fv ) }, { GL_CALL( glNormal3fv ) },
{ GL_CALL( glBegin ) }, { GL_CALL( glBegin ) },
{ GL_CALL( glEnd ) }, { GL_CALL( glEnd ) },
{ GL_CALL( glLineWidth ) }, { GL_CALL( glLineWidth ) },
{ GL_CALL( glPointSize ) }, { GL_CALL( glPointSize ) },
{ GL_CALL( glMatrixMode ) }, { GL_CALL( glMatrixMode ) },
{ GL_CALL( glOrtho ) }, { GL_CALL( glOrtho ) },
{ GL_CALL( glRasterPos2f ) }, { GL_CALL( glRasterPos2f ) },
{ GL_CALL( glFrustum ) }, { GL_CALL( glFrustum ) },
{ GL_CALL( glViewport ) }, { GL_CALL( glViewport ) },
{ GL_CALL( glPushMatrix ) }, { GL_CALL( glPushMatrix ) },
{ GL_CALL( glPopMatrix ) }, { GL_CALL( glPopMatrix ) },
{ GL_CALL( glPushAttrib ) }, { GL_CALL( glPushAttrib ) },
{ GL_CALL( glPopAttrib ) }, { GL_CALL( glPopAttrib ) },
{ GL_CALL( glLoadIdentity ) }, { GL_CALL( glLoadIdentity ) },
{ GL_CALL( glLoadMatrixd ) }, { GL_CALL( glLoadMatrixd ) },
{ GL_CALL( glLoadMatrixf ) }, { GL_CALL( glLoadMatrixf ) },
{ GL_CALL( glMultMatrixd ) }, { GL_CALL( glMultMatrixd ) },
{ GL_CALL( glMultMatrixf ) }, { GL_CALL( glMultMatrixf ) },
{ GL_CALL( glRotated ) }, { GL_CALL( glRotated ) },
{ GL_CALL( glRotatef ) }, { GL_CALL( glRotatef ) },
{ GL_CALL( glScaled ) }, { GL_CALL( glScaled ) },
{ GL_CALL( glScalef ) }, { GL_CALL( glScalef ) },
{ GL_CALL( glTranslated ) }, { GL_CALL( glTranslated ) },
{ GL_CALL( glTranslatef ) }, { GL_CALL( glTranslatef ) },
{ GL_CALL( glReadPixels ) }, { GL_CALL( glReadPixels ) },
{ GL_CALL( glDrawPixels ) }, { GL_CALL( glDrawPixels ) },
{ GL_CALL( glStencilFunc ) }, { GL_CALL( glStencilFunc ) },
{ GL_CALL( glStencilMask ) }, { GL_CALL( glStencilMask ) },
{ GL_CALL( glStencilOp ) }, { GL_CALL( glStencilOp ) },
{ GL_CALL( glClearStencil ) }, { GL_CALL( glClearStencil ) },
{ GL_CALL( glIsEnabled ) }, { GL_CALL( glIsEnabled ) },
{ GL_CALL( glIsList ) }, { GL_CALL( glIsList ) },
{ GL_CALL( glIsTexture ) }, { GL_CALL( glIsTexture ) },
{ GL_CALL( glTexEnvf ) }, { GL_CALL( glTexEnvf ) },
{ GL_CALL( glTexEnvfv ) }, { GL_CALL( glTexEnvfv ) },
{ GL_CALL( glTexEnvi ) }, { GL_CALL( glTexEnvi ) },
{ GL_CALL( glTexParameterf ) }, { GL_CALL( glTexParameterf ) },
{ GL_CALL( glTexParameterfv ) }, { GL_CALL( glTexParameterfv ) },
{ GL_CALL( glTexParameteri ) }, { GL_CALL( glTexParameteri ) },
{ GL_CALL( glHint ) }, { GL_CALL( glHint ) },
{ GL_CALL( glPixelStoref ) }, { GL_CALL( glPixelStoref ) },
{ GL_CALL( glPixelStorei ) }, { GL_CALL( glPixelStorei ) },
{ GL_CALL( glGenTextures ) }, { GL_CALL( glGenTextures ) },
{ GL_CALL( glDeleteTextures ) }, { GL_CALL( glDeleteTextures ) },
{ GL_CALL( glBindTexture ) }, { GL_CALL( glBindTexture ) },
{ GL_CALL( glTexImage1D ) }, { GL_CALL( glTexImage1D ) },
{ GL_CALL( glTexImage2D ) }, { GL_CALL( glTexImage2D ) },
{ GL_CALL( glTexSubImage1D ) }, { GL_CALL( glTexSubImage1D ) },
{ GL_CALL( glTexSubImage2D ) }, { GL_CALL( glTexSubImage2D ) },
{ GL_CALL( glCopyTexImage1D ) }, { GL_CALL( glCopyTexImage1D ) },
{ GL_CALL( glCopyTexImage2D ) }, { GL_CALL( glCopyTexImage2D ) },
{ GL_CALL( glCopyTexSubImage1D ) }, { GL_CALL( glCopyTexSubImage1D ) },
{ GL_CALL( glCopyTexSubImage2D ) }, { GL_CALL( glCopyTexSubImage2D ) },
{ GL_CALL( glScissor ) }, { GL_CALL( glScissor ) },
{ GL_CALL( glGetTexImage ) }, { GL_CALL( glGetTexImage ) },
{ GL_CALL( glGetTexEnviv ) }, { GL_CALL( glGetTexEnviv ) },
{ GL_CALL( glPolygonOffset ) }, { GL_CALL( glPolygonOffset ) },
{ GL_CALL( glPolygonMode ) }, { GL_CALL( glPolygonMode ) },
{ GL_CALL( glPolygonStipple ) }, { GL_CALL( glPolygonStipple ) },
{ GL_CALL( glClipPlane ) }, { GL_CALL( glClipPlane ) },
{ GL_CALL( glGetClipPlane ) }, { GL_CALL( glGetClipPlane ) },
{ GL_CALL( glShadeModel ) }, { GL_CALL( glShadeModel ) },
{ GL_CALL( glGetTexLevelParameteriv ) }, { GL_CALL( glGetTexLevelParameteriv ) },
{ GL_CALL( glGetTexLevelParameterfv ) }, { GL_CALL( glGetTexLevelParameterfv ) },
{ GL_CALL( glFogfv ) }, { GL_CALL( glFogfv ) },
{ GL_CALL( glFogf ) }, { GL_CALL( glFogf ) },
{ GL_CALL( glFogi ) }, { GL_CALL( glFogi ) },
{ NULL , NULL }
}; };
static dllfunc_t debugoutputfuncs[] = static const dllfunc_t debugoutputfuncs[] =
{ {
{ GL_CALL( glDebugMessageControlARB ) }, { GL_CALL( glDebugMessageControlARB ) },
{ GL_CALL( glDebugMessageInsertARB ) }, { GL_CALL( glDebugMessageInsertARB ) },
{ GL_CALL( glDebugMessageCallbackARB ) }, { GL_CALL( glDebugMessageCallbackARB ) },
{ GL_CALL( glGetDebugMessageLogARB ) }, { GL_CALL( glGetDebugMessageLogARB ) },
{ NULL , NULL }
}; };
static dllfunc_t multitexturefuncs[] = static const dllfunc_t multitexturefuncs[] =
{ {
{ GL_CALL( glMultiTexCoord1f ) }, { GL_CALL( glMultiTexCoord1f ) },
{ GL_CALL( glMultiTexCoord2f ) }, { GL_CALL( glMultiTexCoord2f ) },
{ GL_CALL( glMultiTexCoord3f ) }, { GL_CALL( glMultiTexCoord3f ) },
{ GL_CALL( glMultiTexCoord4f ) }, { GL_CALL( glMultiTexCoord4f ) },
{ GL_CALL( glActiveTexture ) }, { GL_CALL( glActiveTexture ) },
{ GL_CALL( glActiveTextureARB ) }, { GL_CALL( glActiveTextureARB ) },
{ GL_CALL( glClientActiveTexture ) }, { GL_CALL( glClientActiveTexture ) },
{ GL_CALL( glClientActiveTextureARB ) }, { GL_CALL( glClientActiveTextureARB ) },
{ NULL , NULL }
}; };
static dllfunc_t texture3dextfuncs[] = static const dllfunc_t texture3dextfuncs[] =
{ {
{ GL_CALL( glTexImage3D ) }, { GL_CALL( glTexImage3D ) },
{ GL_CALL( glTexSubImage3D ) }, { GL_CALL( glTexSubImage3D ) },
{ GL_CALL( glCopyTexSubImage3D ) }, { GL_CALL( glCopyTexSubImage3D ) },
{ NULL , NULL }
}; };
static dllfunc_t texturecompressionfuncs[] = static const dllfunc_t texturecompressionfuncs[] =
{ {
{ GL_CALL( glCompressedTexImage3DARB ) }, { GL_CALL( glCompressedTexImage3DARB ) },
{ GL_CALL( glCompressedTexImage2DARB ) }, { GL_CALL( glCompressedTexImage2DARB ) },
{ GL_CALL( glCompressedTexImage1DARB ) }, { GL_CALL( glCompressedTexImage1DARB ) },
{ GL_CALL( glCompressedTexSubImage3DARB ) }, { GL_CALL( glCompressedTexSubImage3DARB ) },
{ GL_CALL( glCompressedTexSubImage2DARB ) }, { GL_CALL( glCompressedTexSubImage2DARB ) },
{ GL_CALL( glCompressedTexSubImage1DARB ) }, { GL_CALL( glCompressedTexSubImage1DARB ) },
{ GL_CALL( glGetCompressedTexImage ) }, { GL_CALL( glGetCompressedTexImage ) },
{ NULL , NULL }
}; };
static dllfunc_t vbofuncs[] = static const dllfunc_t vbofuncs[] =
{ {
{ GL_CALL( glBindBufferARB ) }, { GL_CALL( glBindBufferARB ) },
{ GL_CALL( glDeleteBuffersARB ) }, { GL_CALL( glDeleteBuffersARB ) },
{ GL_CALL( glGenBuffersARB ) }, { GL_CALL( glGenBuffersARB ) },
{ GL_CALL( glIsBufferARB ) }, { GL_CALL( glIsBufferARB ) },
#if !XASH_GLES #if !XASH_GLES
{ GL_CALL( glMapBufferARB ) }, { GL_CALL( glMapBufferARB ) },
{ GL_CALL( glUnmapBufferARB ) }, { GL_CALL( glUnmapBufferARB ) },
#endif #endif
{ GL_CALL( glBufferDataARB ) }, { GL_CALL( glBufferDataARB ) },
{ GL_CALL( glBufferSubDataARB ) }, { GL_CALL( glBufferSubDataARB ) },
{ NULL, NULL }
}; };
static dllfunc_t multisampletexfuncs[] = static const dllfunc_t multisampletexfuncs[] =
{ {
{ GL_CALL(glTexImage2DMultisample) }, { GL_CALL(glTexImage2DMultisample) },
{ NULL, NULL }
}; };
static dllfunc_t drawrangeelementsfuncs[] = static const dllfunc_t drawrangeelementsfuncs[] =
{ {
{ GL_CALL( glDrawRangeElements ) }, { GL_CALL( glDrawRangeElements ) },
{ NULL, NULL }
}; };
static dllfunc_t drawrangeelementsextfuncs[] = static const dllfunc_t drawrangeelementsextfuncs[] =
{ {
{ GL_CALL( glDrawRangeElementsEXT ) }, { GL_CALL( glDrawRangeElementsEXT ) },
{ NULL, NULL }
}; };
@@ -263,85 +253,80 @@ static dllfunc_t drawrangeelementsextfuncs[] =
// still need resolve some ext dynamicly, and mangling beginend wrappers will help only with LTO // still need resolve some ext dynamicly, and mangling beginend wrappers will help only with LTO
// anyway this will not work with gl-wes/nanogl, we do not link to libGLESv2, so skip this now // anyway this will not work with gl-wes/nanogl, we do not link to libGLESv2, so skip this now
#if !XASH_GL_STATIC #if !XASH_GL_STATIC
static dllfunc_t mapbufferrangefuncs[] = static const dllfunc_t mapbufferrangefuncs[] =
{ {
{ GL_CALL( glMapBufferRange ) }, { GL_CALL( glMapBufferRange ) },
{ GL_CALL( glFlushMappedBufferRange ) }, { GL_CALL( glFlushMappedBufferRange ) },
#if XASH_GLES #if XASH_GLES
{ GL_CALL( glUnmapBufferARB ) }, { GL_CALL( glUnmapBufferARB ) },
#endif #endif
{ NULL, NULL }
}; };
static dllfunc_t drawrangeelementsbasevertexfuncs[] = static const dllfunc_t drawrangeelementsbasevertexfuncs[] =
{ {
{ GL_CALL( glDrawRangeElementsBaseVertex ) }, { GL_CALL( glDrawRangeElementsBaseVertex ) },
{ NULL, NULL }
}; };
static dllfunc_t bufferstoragefuncs[] = static const dllfunc_t bufferstoragefuncs[] =
{ {
{ GL_CALL( glBufferStorage ) }, { GL_CALL( glBufferStorage ) },
{ NULL, NULL }
}; };
static dllfunc_t shaderobjectsfuncs[] = static const dllfunc_t shaderobjectsfuncs[] =
{ {
{ GL_CALL( glDeleteObjectARB ) }, { GL_CALL( glDeleteObjectARB ) },
{ GL_CALL( glGetHandleARB ) }, { GL_CALL( glGetHandleARB ) },
{ GL_CALL( glDetachObjectARB ) }, { GL_CALL( glDetachObjectARB ) },
{ GL_CALL( glCreateShaderObjectARB ) }, { GL_CALL( glCreateShaderObjectARB ) },
{ GL_CALL( glShaderSourceARB ) }, { GL_CALL( glShaderSourceARB ) },
{ GL_CALL( glCompileShaderARB ) }, { GL_CALL( glCompileShaderARB ) },
{ GL_CALL( glCreateProgramObjectARB ) }, { GL_CALL( glCreateProgramObjectARB ) },
{ GL_CALL( glAttachObjectARB ) }, { GL_CALL( glAttachObjectARB ) },
{ GL_CALL( glLinkProgramARB ) }, { GL_CALL( glLinkProgramARB ) },
{ GL_CALL( glUseProgramObjectARB ) }, { GL_CALL( glUseProgramObjectARB ) },
{ GL_CALL( glValidateProgramARB ) }, { GL_CALL( glValidateProgramARB ) },
{ GL_CALL( glUniform1fARB ) }, { GL_CALL( glUniform1fARB ) },
{ GL_CALL( glUniform2fARB ) }, { GL_CALL( glUniform2fARB ) },
{ GL_CALL( glUniform3fARB ) }, { GL_CALL( glUniform3fARB ) },
{ GL_CALL( glUniform4fARB ) }, { GL_CALL( glUniform4fARB ) },
{ GL_CALL( glUniform1iARB ) }, { GL_CALL( glUniform1iARB ) },
{ GL_CALL( glUniform2iARB ) }, { GL_CALL( glUniform2iARB ) },
{ GL_CALL( glUniform3iARB ) }, { GL_CALL( glUniform3iARB ) },
{ GL_CALL( glUniform4iARB ) }, { GL_CALL( glUniform4iARB ) },
{ GL_CALL( glUniform1fvARB ) }, { GL_CALL( glUniform1fvARB ) },
{ GL_CALL( glUniform2fvARB ) }, { GL_CALL( glUniform2fvARB ) },
{ GL_CALL( glUniform3fvARB ) }, { GL_CALL( glUniform3fvARB ) },
{ GL_CALL( glUniform4fvARB ) }, { GL_CALL( glUniform4fvARB ) },
{ GL_CALL( glUniform1ivARB ) }, { GL_CALL( glUniform1ivARB ) },
{ GL_CALL( glUniform2ivARB ) }, { GL_CALL( glUniform2ivARB ) },
{ GL_CALL( glUniform3ivARB ) }, { GL_CALL( glUniform3ivARB ) },
{ GL_CALL( glUniform4ivARB ) }, { GL_CALL( glUniform4ivARB ) },
{ GL_CALL( glUniformMatrix2fvARB ) }, { GL_CALL( glUniformMatrix2fvARB ) },
{ GL_CALL( glUniformMatrix3fvARB ) }, { GL_CALL( glUniformMatrix3fvARB ) },
{ GL_CALL( glUniformMatrix4fvARB ) }, { GL_CALL( glUniformMatrix4fvARB ) },
{ GL_CALL( glGetObjectParameterfvARB ) }, { GL_CALL( glGetObjectParameterfvARB ) },
{ GL_CALL( glGetObjectParameterivARB ) }, { GL_CALL( glGetObjectParameterivARB ) },
{ GL_CALL( glGetInfoLogARB ) }, { GL_CALL( glGetInfoLogARB ) },
{ GL_CALL( glGetAttachedObjectsARB ) }, { GL_CALL( glGetAttachedObjectsARB ) },
{ GL_CALL( glGetUniformLocationARB ) }, { GL_CALL( glGetUniformLocationARB ) },
{ GL_CALL( glGetActiveUniformARB ) }, { GL_CALL( glGetActiveUniformARB ) },
{ GL_CALL( glGetUniformfvARB ) }, { GL_CALL( glGetUniformfvARB ) },
{ GL_CALL( glGetUniformivARB ) }, { GL_CALL( glGetUniformivARB ) },
{ GL_CALL( glGetShaderSourceARB ) }, { GL_CALL( glGetShaderSourceARB ) },
{ GL_CALL( glVertexAttribPointerARB ) }, { GL_CALL( glVertexAttribPointerARB ) },
{ GL_CALL( glEnableVertexAttribArrayARB ) }, { GL_CALL( glEnableVertexAttribArrayARB ) },
{ GL_CALL( glDisableVertexAttribArrayARB ) }, { GL_CALL( glDisableVertexAttribArrayARB ) },
{ GL_CALL( glBindAttribLocationARB ) }, { GL_CALL( glBindAttribLocationARB ) },
{ GL_CALL( glGetActiveAttribARB ) }, { GL_CALL( glGetActiveAttribARB ) },
{ GL_CALL( glGetAttribLocationARB ) }, { GL_CALL( glGetAttribLocationARB ) },
{ GL_CALL( glVertexAttrib2fARB ) }, { GL_CALL( glVertexAttrib2fARB ) },
{ GL_CALL( glVertexAttrib2fvARB ) }, { GL_CALL( glVertexAttrib2fvARB ) },
// { GL_CALL( glVertexAttrib3fv ) }, //{ GL_CALL( glVertexAttrib3fv ) },
// { GL_CALL( glVertexAttrib4f ) }, //{ GL_CALL( glVertexAttrib4f ) },
// { GL_CALL( glVertexAttrib4fv ) }, //{ GL_CALL( glVertexAttrib4fv ) },
// { GL_CALL( glVertexAttrib4ubv ) }, //{ GL_CALL( glVertexAttrib4ubv ) },
{ NULL, NULL }
}; };
/* /*
================== ==================
Even if *ARB functions may work in GL driver in Core context, Even if *ARB functions may work in GL driver in Core context,
@@ -354,90 +339,86 @@ Commented out lines left there intentionally to prevent usage on core/gles
================== ==================
*/ */
static dllfunc_t shaderobjectsfuncs_gles[] = static const dllfunc_t shaderobjectsfuncs_gles[] =
{ {
{ "glDeleteShader" , (void **)&pglDeleteObjectARB }, { "glDeleteShader" , (void **)&pglDeleteObjectARB },
//{ "glGetHandleARB" , (void **)&pglGetHandleARB }, //{ "glGetHandleARB" , (void **)&pglGetHandleARB },
{ "glDetachShader" , (void **)&pglDetachObjectARB }, { "glDetachShader" , (void **)&pglDetachObjectARB },
{ "glCreateShader" , (void **)&pglCreateShaderObjectARB }, { "glCreateShader" , (void **)&pglCreateShaderObjectARB },
{ "glShaderSource" , (void **)&pglShaderSourceARB }, { "glShaderSource" , (void **)&pglShaderSourceARB },
{ "glCompileShader" , (void **)&pglCompileShaderARB }, { "glCompileShader" , (void **)&pglCompileShaderARB },
{ "glCreateProgram" , (void **)&pglCreateProgramObjectARB }, { "glCreateProgram" , (void **)&pglCreateProgramObjectARB },
{ "glAttachShader" , (void **)&pglAttachObjectARB }, { "glAttachShader" , (void **)&pglAttachObjectARB },
{ "glLinkProgram" , (void **)&pglLinkProgramARB }, { "glLinkProgram" , (void **)&pglLinkProgramARB },
{ "glUseProgram" , (void **)&pglUseProgramObjectARB }, { "glUseProgram" , (void **)&pglUseProgramObjectARB },
{ "glValidateProgram" , (void **)&pglValidateProgramARB }, { "glValidateProgram" , (void **)&pglValidateProgramARB },
{ "glUniform1f" , (void **)&pglUniform1fARB }, { "glUniform1f" , (void **)&pglUniform1fARB },
{ "glUniform2f" , (void **)&pglUniform2fARB }, { "glUniform2f" , (void **)&pglUniform2fARB },
{ "glUniform3f" , (void **)&pglUniform3fARB }, { "glUniform3f" , (void **)&pglUniform3fARB },
{ "glUniform4f" , (void **)&pglUniform4fARB }, { "glUniform4f" , (void **)&pglUniform4fARB },
{ "glUniform1i" , (void **)&pglUniform1iARB }, { "glUniform1i" , (void **)&pglUniform1iARB },
{ "glUniform2i" , (void **)&pglUniform2iARB }, { "glUniform2i" , (void **)&pglUniform2iARB },
{ "glUniform3i" , (void **)&pglUniform3iARB }, { "glUniform3i" , (void **)&pglUniform3iARB },
{ "glUniform4i" , (void **)&pglUniform4iARB }, { "glUniform4i" , (void **)&pglUniform4iARB },
{ "glUniform1f" , (void **)&pglUniform1fvARB }, { "glUniform1f" , (void **)&pglUniform1fvARB },
{ "glUniform2fv" , (void **)&pglUniform2fvARB }, { "glUniform2fv" , (void **)&pglUniform2fvARB },
{ "glUniform3fv" , (void **)&pglUniform3fvARB }, { "glUniform3fv" , (void **)&pglUniform3fvARB },
{ "glUniform4fv" , (void **)&pglUniform4fvARB }, { "glUniform4fv" , (void **)&pglUniform4fvARB },
{ "glUniform1iv" , (void **)&pglUniform1ivARB }, { "glUniform1iv" , (void **)&pglUniform1ivARB },
{ "glUniform2iv" , (void **)&pglUniform2ivARB }, { "glUniform2iv" , (void **)&pglUniform2ivARB },
{ "glUniform3iv" , (void **)&pglUniform3ivARB }, { "glUniform3iv" , (void **)&pglUniform3ivARB },
{ "glUniform4iv" , (void **)&pglUniform4ivARB }, { "glUniform4iv" , (void **)&pglUniform4ivARB },
{ "glUniformMatrix2fv" , (void **)&pglUniformMatrix2fvARB }, { "glUniformMatrix2fv" , (void **)&pglUniformMatrix2fvARB },
{ "glUniformMatrix3fv" , (void **)&pglUniformMatrix3fvARB }, { "glUniformMatrix3fv" , (void **)&pglUniformMatrix3fvARB },
{ "glUniformMatrix4fv" , (void **)&pglUniformMatrix4fvARB }, { "glUniformMatrix4fv" , (void **)&pglUniformMatrix4fvARB },
// { "glGetShaderfv" , (void **)&pglGetObjectParameterfvARB }, // missing in ES2? //{ "glGetShaderfv" , (void **)&pglGetObjectParameterfvARB }, // missing in ES2?
{ "glGetShaderiv" , (void **)&pglGetObjectParameterivARB }, { "glGetShaderiv" , (void **)&pglGetObjectParameterivARB },
{ "glGetShaderInfoLog" , (void **)&pglGetInfoLogARB }, { "glGetShaderInfoLog" , (void **)&pglGetInfoLogARB },
// { "glGetAttachedObjects" , (void **)&pglGetAttachedObjectsARB }, // missing in ES2? //{ "glGetAttachedObjects" , (void **)&pglGetAttachedObjectsARB }, // missing in ES2?
{ "glGetUniformLocation" , (void **)&pglGetUniformLocationARB }, { "glGetUniformLocation" , (void **)&pglGetUniformLocationARB },
{ "glGetActiveUniform" , (void **)&pglGetActiveUniformARB }, { "glGetActiveUniform" , (void **)&pglGetActiveUniformARB },
{ "glGetUniformfv" , (void **)&pglGetUniformfvARB }, { "glGetUniformfv" , (void **)&pglGetUniformfvARB },
{ "glGetUniformiv" , (void **)&pglGetUniformivARB }, { "glGetUniformiv" , (void **)&pglGetUniformivARB },
{ "glGetShaderSource" , (void **)&pglGetShaderSourceARB }, { "glGetShaderSource" , (void **)&pglGetShaderSourceARB },
{ "glVertexAttribPointer" , (void **)&pglVertexAttribPointerARB }, { "glVertexAttribPointer" , (void **)&pglVertexAttribPointerARB },
{ "glEnableVertexAttribArray" , (void **)&pglEnableVertexAttribArrayARB }, { "glEnableVertexAttribArray" , (void **)&pglEnableVertexAttribArrayARB },
{ "glDisableVertexAttribArray" , (void **)&pglDisableVertexAttribArrayARB }, { "glDisableVertexAttribArray" , (void **)&pglDisableVertexAttribArrayARB },
{ "glBindAttribLocation" , (void **)&pglBindAttribLocationARB }, { "glBindAttribLocation" , (void **)&pglBindAttribLocationARB },
{ "glGetActiveAttrib" , (void **)&pglGetActiveAttribARB }, { "glGetActiveAttrib" , (void **)&pglGetActiveAttribARB },
{ "glGetAttribLocation" , (void **)&pglGetAttribLocationARB }, { "glGetAttribLocation" , (void **)&pglGetAttribLocationARB },
{ "glVertexAttrib2f" , (void **)&pglVertexAttrib2fARB }, { "glVertexAttrib2f" , (void **)&pglVertexAttrib2fARB },
{ "glVertexAttrib2fv" , (void **)&pglVertexAttrib2fvARB }, { "glVertexAttrib2fv" , (void **)&pglVertexAttrib2fvARB },
{ "glVertexAttrib3fv" , (void **)&pglVertexAttrib3fvARB }, { "glVertexAttrib3fv" , (void **)&pglVertexAttrib3fvARB },
// Core/GLES only // Core/GLES only
{ GL_CALL( glGetProgramiv ) }, { GL_CALL( glGetProgramiv ) },
{ GL_CALL( glDeleteProgram ) }, { GL_CALL( glDeleteProgram ) },
{ GL_CALL( glGetProgramInfoLog ) }, { GL_CALL( glGetProgramInfoLog ) },
//{ "glVertexAttrib4f" , (void **)&pglVertexAttrib4fARB }, //{ "glVertexAttrib4f" , (void **)&pglVertexAttrib4fARB },
//{ "glVertexAttrib4fv" , (void **)&pglVertexAttrib4fvARB }, //{ "glVertexAttrib4fv" , (void **)&pglVertexAttrib4fvARB },
//{ "glVertexAttrib4ubv" , (void **)&pglVertexAttrib4ubvARB }, //{ "glVertexAttrib4ubv" , (void **)&pglVertexAttrib4ubvARB },
{ NULL, NULL }
}; };
static dllfunc_t vaofuncs[] = static const dllfunc_t vaofuncs[] =
{ {
{ GL_CALL( glBindVertexArray ) }, { GL_CALL( glBindVertexArray ) },
{ GL_CALL( glDeleteVertexArrays ) }, { GL_CALL( glDeleteVertexArrays ) },
{ GL_CALL( glGenVertexArrays ) }, { GL_CALL( glGenVertexArrays ) },
{ GL_CALL( glIsVertexArray ) }, { GL_CALL( glIsVertexArray ) },
{ NULL, NULL }
}; };
static dllfunc_t multitexturefuncs_es[] = static const dllfunc_t multitexturefuncs_es[] =
{ {
{ GL_CALL( glActiveTexture ) }, { GL_CALL( glActiveTexture ) },
{ GL_CALL( glActiveTextureARB ) }, { GL_CALL( glActiveTextureARB ) },
{ GL_CALL( glClientActiveTexture ) }, { GL_CALL( glClientActiveTexture ) },
{ GL_CALL( glClientActiveTextureARB ) }, { GL_CALL( glClientActiveTextureARB ) },
{ NULL , NULL }
}; };
static dllfunc_t multitexturefuncs_es2[] = static const dllfunc_t multitexturefuncs_es2[] =
{ {
{ GL_CALL( glActiveTexture ) }, { GL_CALL( glActiveTexture ) },
{ GL_CALL( glActiveTextureARB ) }, { GL_CALL( glActiveTextureARB ) },
{ NULL , NULL }
}; };
#endif // !XASH_GL_STATIC #endif // !XASH_GL_STATIC
@@ -476,7 +457,7 @@ static void APIENTRY GL_DebugOutput( GLuint source, GLuint type, GLuint id, GLui
GL_SetExtension GL_SetExtension
================= =================
*/ */
void GL_SetExtension( int r_ext, int enable ) static void GL_SetExtension( int r_ext, int enable )
{ {
if( r_ext >= 0 && r_ext < GL_EXTCOUNT ) if( r_ext >= 0 && r_ext < GL_EXTCOUNT )
glConfig.extension[r_ext] = enable ? GL_TRUE : GL_FALSE; glConfig.extension[r_ext] = enable ? GL_TRUE : GL_FALSE;
@@ -514,12 +495,12 @@ int GL_MaxTextureUnits( void )
GL_CheckExtension GL_CheckExtension
================= =================
*/ */
qboolean GL_CheckExtension( const char *name, const dllfunc_t *funcs, const char *cvarname, int r_ext, float minver ) static qboolean GL_CheckExtension( const char *name, const dllfunc_t *funcs, size_t num_funcs, const char *cvarname, int r_ext, float minver )
{ {
const dllfunc_t *func; size_t i;
cvar_t *parm = NULL; cvar_t *parm = NULL;
const char *extensions_string; const char *extensions_string;
char desc[MAX_VA_STRING]; char desc[MAX_VA_STRING];
float glver = (float)glConfig.version_major + glConfig.version_minor / 10.0f; float glver = (float)glConfig.version_major + glConfig.version_minor / 10.0f;
gEngfuncs.Con_Reportf( "%s: %s ", __func__, name ); gEngfuncs.Con_Reportf( "%s: %s ", __func__, name );
@@ -550,17 +531,16 @@ qboolean GL_CheckExtension( const char *name, const dllfunc_t *funcs, const char
#if !XASH_GL_STATIC #if !XASH_GL_STATIC
// clear exports // clear exports
for( func = funcs; func && func->name; func++ ) ClearExports( funcs, num_funcs );
*func->func = NULL;
for( func = funcs; func && func->name; func++ ) for( i = 0; i < num_funcs; i++ )
{ {
// functions are cleared before all the extensions are evaluated // functions are cleared before all the extensions are evaluated
if(( *func->func = (void *)gEngfuncs.GL_GetProcAddress( func->name )) == NULL ) if(( *(funcs[i].func) = (void *)gEngfuncs.GL_GetProcAddress( funcs[i].name )) == NULL )
{ {
string name; string name;
char *end; char *end;
size_t i = 0; size_t j = 0;
#if XASH_GLES #if XASH_GLES
const char *suffixes[] = { "", "EXT", "OES" }; const char *suffixes[] = { "", "EXT", "OES" };
#else #else
@@ -568,7 +548,7 @@ qboolean GL_CheckExtension( const char *name, const dllfunc_t *funcs, const char
#endif #endif
// HACK: fix ARB names // HACK: fix ARB names
Q_strncpy( name, func->name, sizeof( name )); Q_strncpy( name, funcs[i].name, sizeof( name ));
if(( end = Q_strstr( name, "ARB" ))) if(( end = Q_strstr( name, "ARB" )))
{ {
*end = '\0'; *end = '\0';
@@ -576,21 +556,21 @@ qboolean GL_CheckExtension( const char *name, const dllfunc_t *funcs, const char
else // I need Q_strstrnul else // I need Q_strstrnul
{ {
end = name + Q_strlen( name ); end = name + Q_strlen( name );
i++; // skip empty suffix j++; // skip empty suffix
} }
for( ; i < sizeof( suffixes ) / sizeof( suffixes[0] ); i++ ) for( ; j < sizeof( suffixes ) / sizeof( suffixes[0] ); j++ )
{ {
void *f; void *f;
Q_strncat( name, suffixes[i], sizeof( name )); Q_strncat( name, suffixes[j], sizeof( name ));
if(( f = gEngfuncs.GL_GetProcAddress( name ))) if(( f = gEngfuncs.GL_GetProcAddress( name )))
{ {
// GL_GetProcAddress prints errors about missing functions, so tell user that we found it with different name // GL_GetProcAddress prints errors about missing functions, so tell user that we found it with different name
gEngfuncs.Con_Printf( S_NOTE "found %s\n", name ); gEngfuncs.Con_Printf( S_NOTE "found %s\n", name );
*func->func = f; *(funcs[i].func) = f;
break; break;
} }
else else
@@ -600,7 +580,7 @@ qboolean GL_CheckExtension( const char *name, const dllfunc_t *funcs, const char
} }
// not found... // not found...
if( i == sizeof( suffixes ) / sizeof( suffixes[0] )) if( j == sizeof( suffixes ) / sizeof( suffixes[0] ))
{ {
GL_SetExtension( r_ext, false ); GL_SetExtension( r_ext, false );
} }
@@ -798,14 +778,14 @@ static void GL_InitExtensionsGLES( void )
switch( extid ) switch( extid )
{ {
case GL_ARB_VERTEX_BUFFER_OBJECT_EXT: case GL_ARB_VERTEX_BUFFER_OBJECT_EXT:
GL_CheckExtension( "vertex_buffer_object", vbofuncs, "gl_vertex_buffer_object", extid, 1.0 ); GL_CheckExtension( "vertex_buffer_object", vbofuncs, ARRAYSIZE( vbofuncs ), "gl_vertex_buffer_object", extid, 1.0 );
break; break;
case GL_ARB_MULTITEXTURE: case GL_ARB_MULTITEXTURE:
if( !GL_CheckExtension( "multitexture", multitexturefuncs, "gl_arb_multitexture", GL_ARB_MULTITEXTURE, 1.0 ) && glConfig.wrapper == GLES_WRAPPER_NONE ) if( !GL_CheckExtension( "multitexture", multitexturefuncs, ARRAYSIZE( multitexturefuncs ), "gl_arb_multitexture", GL_ARB_MULTITEXTURE, 1.0 ) && glConfig.wrapper == GLES_WRAPPER_NONE )
{ {
#if !XASH_GL_STATIC #if !XASH_GL_STATIC
if( !GL_CheckExtension( "multitexture_es1", multitexturefuncs_es, "gl_arb_multitexture", GL_ARB_MULTITEXTURE, 1.0 ) if( !GL_CheckExtension( "multitexture_es1", multitexturefuncs_es, ARRAYSIZE( multitexturefuncs_es ), "gl_arb_multitexture", GL_ARB_MULTITEXTURE, 1.0 )
&& !GL_CheckExtension( "multitexture_es2", multitexturefuncs_es2, "gl_arb_multitexture", GL_ARB_MULTITEXTURE, 2.0 )) && !GL_CheckExtension( "multitexture_es2", multitexturefuncs_es2, ARRAYSIZE( multitexturefuncs_es2 ), "gl_arb_multitexture", GL_ARB_MULTITEXTURE, 2.0 ))
break; break;
#endif #endif
} }
@@ -823,48 +803,48 @@ static void GL_InitExtensionsGLES( void )
glConfig.max_texture_coords = glConfig.max_teximage_units = glConfig.max_texture_units; glConfig.max_texture_coords = glConfig.max_teximage_units = glConfig.max_texture_units;
break; break;
case GL_TEXTURE_CUBEMAP_EXT: case GL_TEXTURE_CUBEMAP_EXT:
if( GL_CheckExtension( "GL_OES_texture_cube_map", NULL, "gl_texture_cubemap", extid, 0 )) if( GL_CheckExtension( "GL_OES_texture_cube_map", NULL, 0, "gl_texture_cubemap", extid, 0 ))
pglGetIntegerv( GL_MAX_CUBE_MAP_TEXTURE_SIZE_ARB, &glConfig.max_cubemap_size ); pglGetIntegerv( GL_MAX_CUBE_MAP_TEXTURE_SIZE_ARB, &glConfig.max_cubemap_size );
break; break;
case GL_ANISOTROPY_EXT: case GL_ANISOTROPY_EXT:
glConfig.max_texture_anisotropy = 0.0f; glConfig.max_texture_anisotropy = 0.0f;
if( GL_CheckExtension( "GL_EXT_texture_filter_anisotropic", NULL, "gl_ext_anisotropic_filter", extid, 0 )) if( GL_CheckExtension( "GL_EXT_texture_filter_anisotropic", NULL, 0, "gl_ext_anisotropic_filter", extid, 0 ))
pglGetFloatv( GL_MAX_TEXTURE_MAX_ANISOTROPY_EXT, &glConfig.max_texture_anisotropy ); pglGetFloatv( GL_MAX_TEXTURE_MAX_ANISOTROPY_EXT, &glConfig.max_texture_anisotropy );
break; break;
case GL_TEXTURE_LOD_BIAS: case GL_TEXTURE_LOD_BIAS:
if( GL_CheckExtension( "GL_EXT_texture_lod_bias", NULL, "gl_texture_mipmap_biasing", extid, 0 )) if( GL_CheckExtension( "GL_EXT_texture_lod_bias", NULL, 0, "gl_texture_mipmap_biasing", extid, 0 ))
pglGetFloatv( GL_MAX_TEXTURE_LOD_BIAS_EXT, &glConfig.max_texture_lod_bias ); pglGetFloatv( GL_MAX_TEXTURE_LOD_BIAS_EXT, &glConfig.max_texture_lod_bias );
break; break;
case GL_ARB_TEXTURE_NPOT_EXT: case GL_ARB_TEXTURE_NPOT_EXT:
GL_CheckExtension( "GL_OES_texture_npot", NULL, "gl_texture_npot", extid, 0 ); GL_CheckExtension( "GL_OES_texture_npot", NULL, 0, "gl_texture_npot", extid, 0 );
break; break;
#if !XASH_GL_STATIC #if !XASH_GL_STATIC
case GL_SHADER_OBJECTS_EXT: case GL_SHADER_OBJECTS_EXT:
GL_CheckExtension( "ES2 Shaders", shaderobjectsfuncs_gles, "gl_shaderobjects", extid, 2.0 ); GL_CheckExtension( "ES2 Shaders", shaderobjectsfuncs_gles, ARRAYSIZE( shaderobjectsfuncs_gles ), "gl_shaderobjects", extid, 2.0 );
break; break;
case GL_ARB_VERTEX_ARRAY_OBJECT_EXT: case GL_ARB_VERTEX_ARRAY_OBJECT_EXT:
if( !GL_CheckExtension( "GL_OES_vertex_array_object", vaofuncs, "gl_vertex_array_object", extid, 3.0 )) if( !GL_CheckExtension( "GL_OES_vertex_array_object", vaofuncs, ARRAYSIZE( vaofuncs ), "gl_vertex_array_object", extid, 3.0 ))
GL_CheckExtension( "GL_EXT_vertex_array_object", vaofuncs, "gl_vertex_array_object", extid, 3.0 ); GL_CheckExtension( "GL_EXT_vertex_array_object", vaofuncs, ARRAYSIZE( vaofuncs ), "gl_vertex_array_object", extid, 3.0 );
break; break;
case GL_DRAW_RANGEELEMENTS_EXT: case GL_DRAW_RANGEELEMENTS_EXT:
if( !GL_CheckExtension( "GL_EXT_draw_range_elements", drawrangeelementsfuncs, "gl_drawrangeelements", extid, 3.0 )) if( !GL_CheckExtension( "GL_EXT_draw_range_elements", drawrangeelementsfuncs, ARRAYSIZE( drawrangeelementsfuncs ), "gl_drawrangeelements", extid, 3.0 ))
GL_CheckExtension( "GL_OES_draw_range_elements", drawrangeelementsfuncs, "gl_drawrangeelements", extid, 3.0 ); GL_CheckExtension( "GL_OES_draw_range_elements", drawrangeelementsfuncs, ARRAYSIZE( drawrangeelementsfuncs ), "gl_drawrangeelements", extid, 3.0 );
break; break;
case GL_DRAW_RANGE_ELEMENTS_BASE_VERTEX_EXT: case GL_DRAW_RANGE_ELEMENTS_BASE_VERTEX_EXT:
if( !GL_CheckExtension( "GL_OES_draw_elements_base_vertex", drawrangeelementsbasevertexfuncs, "gl_drawrangeelementsbasevertex", GL_DRAW_RANGE_ELEMENTS_BASE_VERTEX_EXT, 0 )) if( !GL_CheckExtension( "GL_OES_draw_elements_base_vertex", drawrangeelementsbasevertexfuncs, ARRAYSIZE( drawrangeelementsbasevertexfuncs ), "gl_drawrangeelementsbasevertex", GL_DRAW_RANGE_ELEMENTS_BASE_VERTEX_EXT, 0 ))
GL_CheckExtension( "GL_EXT_draw_elements_base_vertex", drawrangeelementsbasevertexfuncs, "gl_drawrangeelementsbasevertex", GL_DRAW_RANGE_ELEMENTS_BASE_VERTEX_EXT, 3.2 ); GL_CheckExtension( "GL_EXT_draw_elements_base_vertex", drawrangeelementsbasevertexfuncs, ARRAYSIZE( drawrangeelementsbasevertexfuncs ), "gl_drawrangeelementsbasevertex", GL_DRAW_RANGE_ELEMENTS_BASE_VERTEX_EXT, 3.2 );
break; break;
case GL_MAP_BUFFER_RANGE_EXT: case GL_MAP_BUFFER_RANGE_EXT:
GL_CheckExtension( "GL_EXT_map_buffer_range", mapbufferrangefuncs, "gl_map_buffer_range", GL_MAP_BUFFER_RANGE_EXT , 3.0); GL_CheckExtension( "GL_EXT_map_buffer_range", mapbufferrangefuncs, ARRAYSIZE( mapbufferrangefuncs ), "gl_map_buffer_range", GL_MAP_BUFFER_RANGE_EXT , 3.0);
break; break;
case GL_BUFFER_STORAGE_EXT: case GL_BUFFER_STORAGE_EXT:
GL_CheckExtension( "GL_EXT_buffer_storage", bufferstoragefuncs, "gl_buffer_storage", GL_BUFFER_STORAGE_EXT, 0); GL_CheckExtension( "GL_EXT_buffer_storage", bufferstoragefuncs, ARRAYSIZE( bufferstoragefuncs ), "gl_buffer_storage", GL_BUFFER_STORAGE_EXT, 0);
break; break;
#endif #endif
case GL_DEBUG_OUTPUT: case GL_DEBUG_OUTPUT:
if( glw_state.extended ) if( glw_state.extended )
GL_CheckExtension( "GL_KHR_debug", debugoutputfuncs, "gl_debug_output", extid, 0 ); GL_CheckExtension( "GL_KHR_debug", debugoutputfuncs, ARRAYSIZE( debugoutputfuncs ), "gl_debug_output", extid, 0 );
else else
GL_SetExtension( extid, false ); GL_SetExtension( extid, false );
break; break;
@@ -918,7 +898,7 @@ static void GL_InitExtensionsBigGL( void )
// multitexture // multitexture
glConfig.max_texture_units = glConfig.max_texture_coords = glConfig.max_teximage_units = 1; glConfig.max_texture_units = glConfig.max_texture_coords = glConfig.max_teximage_units = 1;
if( GL_CheckExtension( "GL_ARB_multitexture", multitexturefuncs, "gl_arb_multitexture", GL_ARB_MULTITEXTURE, 1.3f )) if( GL_CheckExtension( "GL_ARB_multitexture", multitexturefuncs, ARRAYSIZE( multitexturefuncs ), "gl_arb_multitexture", GL_ARB_MULTITEXTURE, 1.3f ))
{ {
pglGetIntegerv( GL_MAX_TEXTURE_UNITS_ARB, &glConfig.max_texture_units ); pglGetIntegerv( GL_MAX_TEXTURE_UNITS_ARB, &glConfig.max_texture_units );
} }
@@ -927,7 +907,7 @@ static void GL_InitExtensionsBigGL( void )
GL_SetExtension( GL_ARB_MULTITEXTURE, false ); GL_SetExtension( GL_ARB_MULTITEXTURE, false );
// 3d texture support // 3d texture support
if( GL_CheckExtension( "GL_EXT_texture3D", texture3dextfuncs, "gl_texture_3d", GL_TEXTURE_3D_EXT, 2.0f )) if( GL_CheckExtension( "GL_EXT_texture3D", texture3dextfuncs, ARRAYSIZE( texture3dextfuncs ), "gl_texture_3d", GL_TEXTURE_3D_EXT, 2.0f ))
{ {
pglGetIntegerv( GL_MAX_3D_TEXTURE_SIZE, &glConfig.max_3d_texture_size ); pglGetIntegerv( GL_MAX_3D_TEXTURE_SIZE, &glConfig.max_3d_texture_size );
@@ -939,25 +919,25 @@ static void GL_InitExtensionsBigGL( void )
} }
// 2d texture array support // 2d texture array support
if( GL_CheckExtension( "GL_EXT_texture_array", texture3dextfuncs, "gl_texture_2d_array", GL_TEXTURE_ARRAY_EXT, 0 )) if( GL_CheckExtension( "GL_EXT_texture_array", texture3dextfuncs, ARRAYSIZE( texture3dextfuncs ), "gl_texture_2d_array", GL_TEXTURE_ARRAY_EXT, 0 ))
pglGetIntegerv( GL_MAX_ARRAY_TEXTURE_LAYERS_EXT, &glConfig.max_2d_texture_layers ); pglGetIntegerv( GL_MAX_ARRAY_TEXTURE_LAYERS_EXT, &glConfig.max_2d_texture_layers );
// cubemaps support // cubemaps support
if( GL_CheckExtension( "GL_ARB_texture_cube_map", NULL, "gl_texture_cubemap", GL_TEXTURE_CUBEMAP_EXT, 0 )) if( GL_CheckExtension( "GL_ARB_texture_cube_map", NULL, 0, "gl_texture_cubemap", GL_TEXTURE_CUBEMAP_EXT, 0 ))
{ {
pglGetIntegerv( GL_MAX_CUBE_MAP_TEXTURE_SIZE_ARB, &glConfig.max_cubemap_size ); pglGetIntegerv( GL_MAX_CUBE_MAP_TEXTURE_SIZE_ARB, &glConfig.max_cubemap_size );
// check for seamless cubemaps too // check for seamless cubemaps too
GL_CheckExtension( "GL_ARB_seamless_cube_map", NULL, "gl_texture_cubemap_seamless", GL_ARB_SEAMLESS_CUBEMAP, 0 ); GL_CheckExtension( "GL_ARB_seamless_cube_map", NULL, 0, "gl_texture_cubemap_seamless", GL_ARB_SEAMLESS_CUBEMAP, 0 );
} }
GL_CheckExtension( "GL_ARB_texture_non_power_of_two", NULL, "gl_texture_npot", GL_ARB_TEXTURE_NPOT_EXT, 0 ); GL_CheckExtension( "GL_ARB_texture_non_power_of_two", NULL, 0, "gl_texture_npot", GL_ARB_TEXTURE_NPOT_EXT, 0 );
GL_CheckExtension( "GL_ARB_texture_compression", texturecompressionfuncs, "gl_texture_dxt_compression", GL_TEXTURE_COMPRESSION_EXT, 0 ); GL_CheckExtension( "GL_ARB_texture_compression", texturecompressionfuncs, ARRAYSIZE( texturecompressionfuncs ), "gl_texture_dxt_compression", GL_TEXTURE_COMPRESSION_EXT, 0 );
if( !GL_CheckExtension( "GL_EXT_texture_edge_clamp", NULL, "gl_clamp_to_edge", GL_CLAMPTOEDGE_EXT, 2.0 )) // present in ES2 if( !GL_CheckExtension( "GL_EXT_texture_edge_clamp", NULL, 0, "gl_clamp_to_edge", GL_CLAMPTOEDGE_EXT, 2.0 )) // present in ES2
GL_CheckExtension( "GL_SGIS_texture_edge_clamp", NULL, "gl_clamp_to_edge", GL_CLAMPTOEDGE_EXT, 0 ); GL_CheckExtension( "GL_SGIS_texture_edge_clamp", NULL, 0, "gl_clamp_to_edge", GL_CLAMPTOEDGE_EXT, 0 );
glConfig.max_texture_anisotropy = 0.0f; glConfig.max_texture_anisotropy = 0.0f;
if( GL_CheckExtension( "GL_EXT_texture_filter_anisotropic", NULL, "gl_texture_anisotropic_filter", GL_ANISOTROPY_EXT, 0 )) if( GL_CheckExtension( "GL_EXT_texture_filter_anisotropic", NULL, 0, "gl_texture_anisotropic_filter", GL_ANISOTROPY_EXT, 0 ))
pglGetFloatv( GL_MAX_TEXTURE_MAX_ANISOTROPY_EXT, &glConfig.max_texture_anisotropy ); pglGetFloatv( GL_MAX_TEXTURE_MAX_ANISOTROPY_EXT, &glConfig.max_texture_anisotropy );
#if XASH_WIN32 // Win32 only drivers? #if XASH_WIN32 // Win32 only drivers?
@@ -965,30 +945,30 @@ static void GL_InitExtensionsBigGL( void )
if( glConfig.hardware_type != GLHW_INTEL ) if( glConfig.hardware_type != GLHW_INTEL )
#endif #endif
{ {
if( GL_CheckExtension( "GL_EXT_texture_lod_bias", NULL, "gl_texture_mipmap_biasing", GL_TEXTURE_LOD_BIAS, 1.4 )) if( GL_CheckExtension( "GL_EXT_texture_lod_bias", NULL, 0, "gl_texture_mipmap_biasing", GL_TEXTURE_LOD_BIAS, 1.4 ))
pglGetFloatv( GL_MAX_TEXTURE_LOD_BIAS_EXT, &glConfig.max_texture_lod_bias ); pglGetFloatv( GL_MAX_TEXTURE_LOD_BIAS_EXT, &glConfig.max_texture_lod_bias );
} }
GL_CheckExtension( "GL_ARB_texture_border_clamp", NULL, NULL, GL_CLAMP_TEXBORDER_EXT, 2.0 ); // present in ES2 GL_CheckExtension( "GL_ARB_texture_border_clamp", NULL, 0, NULL, GL_CLAMP_TEXBORDER_EXT, 2.0 ); // present in ES2
GL_CheckExtension( "GL_ARB_depth_texture", NULL, NULL, GL_DEPTH_TEXTURE, 1.4 ); // missing in gles, check GL_OES_depth_texture GL_CheckExtension( "GL_ARB_depth_texture", NULL, 0, NULL, GL_DEPTH_TEXTURE, 1.4 ); // missing in gles, check GL_OES_depth_texture
GL_CheckExtension( "GL_ARB_texture_float", NULL, "gl_texture_float", GL_ARB_TEXTURE_FLOAT_EXT, 0 ); GL_CheckExtension( "GL_ARB_texture_float", NULL, 0, "gl_texture_float", GL_ARB_TEXTURE_FLOAT_EXT, 0 );
GL_CheckExtension( "GL_ARB_depth_buffer_float", NULL, "gl_texture_depth_float", GL_ARB_DEPTH_FLOAT_EXT, 0 ); GL_CheckExtension( "GL_ARB_depth_buffer_float", NULL, 0, "gl_texture_depth_float", GL_ARB_DEPTH_FLOAT_EXT, 0 );
GL_CheckExtension( "GL_EXT_gpu_shader4", NULL, NULL, GL_EXT_GPU_SHADER4, 0 ); // don't confuse users GL_CheckExtension( "GL_EXT_gpu_shader4", NULL, 0, NULL, GL_EXT_GPU_SHADER4, 0 ); // don't confuse users
GL_CheckExtension( "GL_ARB_vertex_buffer_object", vbofuncs, "gl_vertex_buffer_object", GL_ARB_VERTEX_BUFFER_OBJECT_EXT, 2.0 ); GL_CheckExtension( "GL_ARB_vertex_buffer_object", vbofuncs, ARRAYSIZE( vbofuncs ), "gl_vertex_buffer_object", GL_ARB_VERTEX_BUFFER_OBJECT_EXT, 2.0 );
GL_CheckExtension( "GL_ARB_texture_multisample", multisampletexfuncs, "gl_texture_multisample", GL_TEXTURE_MULTISAMPLE, 0 ); GL_CheckExtension( "GL_ARB_texture_multisample", multisampletexfuncs, ARRAYSIZE( multisampletexfuncs ), "gl_texture_multisample", GL_TEXTURE_MULTISAMPLE, 0 );
GL_CheckExtension( "GL_ARB_texture_compression_bptc", NULL, "gl_texture_bptc_compression", GL_ARB_TEXTURE_COMPRESSION_BPTC, 0 ); GL_CheckExtension( "GL_ARB_texture_compression_bptc", NULL, 0, "gl_texture_bptc_compression", GL_ARB_TEXTURE_COMPRESSION_BPTC, 0 );
#if !XASH_GL_STATIC #if !XASH_GL_STATIC
if( glConfig.context == CONTEXT_TYPE_GL_CORE ) if( glConfig.context == CONTEXT_TYPE_GL_CORE )
GL_CheckExtension( "shader_objects", shaderobjectsfuncs_gles, "gl_shaderobjects", GL_SHADER_OBJECTS_EXT, 2.0 ); GL_CheckExtension( "shader_objects", shaderobjectsfuncs_gles, ARRAYSIZE( shaderobjectsfuncs_gles ), "gl_shaderobjects", GL_SHADER_OBJECTS_EXT, 2.0 );
else else
GL_CheckExtension( "GL_ARB_shader_objects", shaderobjectsfuncs, "gl_shaderobjects", GL_SHADER_OBJECTS_EXT, 2.0 ); GL_CheckExtension( "GL_ARB_shader_objects", shaderobjectsfuncs, ARRAYSIZE( shaderobjectsfuncs ), "gl_shaderobjects", GL_SHADER_OBJECTS_EXT, 2.0 );
GL_CheckExtension( "GL_ARB_vertex_array_object", vaofuncs, "gl_vertex_array_object", GL_ARB_VERTEX_ARRAY_OBJECT_EXT, 3.0 ); GL_CheckExtension( "GL_ARB_vertex_array_object", vaofuncs, ARRAYSIZE( vaofuncs ), "gl_vertex_array_object", GL_ARB_VERTEX_ARRAY_OBJECT_EXT, 3.0 );
GL_CheckExtension( "GL_ARB_buffer_storage", bufferstoragefuncs, "gl_buffer_storage", GL_BUFFER_STORAGE_EXT, 4.4); GL_CheckExtension( "GL_ARB_buffer_storage", bufferstoragefuncs, ARRAYSIZE( bufferstoragefuncs ), "gl_buffer_storage", GL_BUFFER_STORAGE_EXT, 4.4);
GL_CheckExtension( "GL_ARB_map_buffer_range", mapbufferrangefuncs, "gl_map_buffer_range", GL_MAP_BUFFER_RANGE_EXT , 3.0); GL_CheckExtension( "GL_ARB_map_buffer_range", mapbufferrangefuncs, ARRAYSIZE( mapbufferrangefuncs ), "gl_map_buffer_range", GL_MAP_BUFFER_RANGE_EXT , 3.0);
GL_CheckExtension( "GL_ARB_draw_elements_base_vertex", drawrangeelementsbasevertexfuncs, "gl_drawrangeelementsbasevertex", GL_DRAW_RANGE_ELEMENTS_BASE_VERTEX_EXT, 3.2 ); GL_CheckExtension( "GL_ARB_draw_elements_base_vertex", drawrangeelementsbasevertexfuncs, ARRAYSIZE( drawrangeelementsbasevertexfuncs ), "gl_drawrangeelementsbasevertex", GL_DRAW_RANGE_ELEMENTS_BASE_VERTEX_EXT, 3.2 );
#endif #endif
if( GL_CheckExtension( "GL_ARB_shading_language_100", NULL, NULL, GL_SHADER_GLSL100_EXT, 2.0 )) if( GL_CheckExtension( "GL_ARB_shading_language_100", NULL, 0, NULL, GL_SHADER_GLSL100_EXT, 2.0 ))
{ {
pglGetIntegerv( GL_MAX_TEXTURE_COORDS_ARB, &glConfig.max_texture_coords ); pglGetIntegerv( GL_MAX_TEXTURE_COORDS_ARB, &glConfig.max_texture_coords );
pglGetIntegerv( GL_MAX_TEXTURE_IMAGE_UNITS_ARB, &glConfig.max_teximage_units ); pglGetIntegerv( GL_MAX_TEXTURE_IMAGE_UNITS_ARB, &glConfig.max_teximage_units );
@@ -1009,11 +989,11 @@ static void GL_InitExtensionsBigGL( void )
} }
// rectangle textures support // rectangle textures support
GL_CheckExtension( "GL_ARB_texture_rectangle", NULL, "gl_texture_rectangle", GL_TEXTURE_2D_RECT_EXT, 0 ); GL_CheckExtension( "GL_ARB_texture_rectangle", NULL, 0, "gl_texture_rectangle", GL_TEXTURE_2D_RECT_EXT, 0 );
if( !GL_CheckExtension( "glDrawRangeElements", drawrangeelementsfuncs, "gl_drawrangeelements", GL_DRAW_RANGEELEMENTS_EXT, 0 ) ) if( !GL_CheckExtension( "glDrawRangeElements", drawrangeelementsfuncs, ARRAYSIZE( drawrangeelementsfuncs ), "gl_drawrangeelements", GL_DRAW_RANGEELEMENTS_EXT, 0 ) )
{ {
if( GL_CheckExtension( "glDrawRangeElementsEXT", drawrangeelementsextfuncs, if( GL_CheckExtension( "glDrawRangeElementsEXT", drawrangeelementsextfuncs, ARRAYSIZE( drawrangeelementsextfuncs ),
"gl_drawrangelements", GL_DRAW_RANGEELEMENTS_EXT, 0 )) "gl_drawrangelements", GL_DRAW_RANGEELEMENTS_EXT, 0 ))
{ {
#if !XASH_GL_STATIC #if !XASH_GL_STATIC
@@ -1024,7 +1004,7 @@ static void GL_InitExtensionsBigGL( void )
// this won't work without extended context // this won't work without extended context
if( glw_state.extended ) if( glw_state.extended )
GL_CheckExtension( "GL_ARB_debug_output", debugoutputfuncs, "gl_debug_output", GL_DEBUG_OUTPUT, 0 ); GL_CheckExtension( "GL_ARB_debug_output", debugoutputfuncs, ARRAYSIZE( debugoutputfuncs ), "gl_debug_output", GL_DEBUG_OUTPUT, 0 );
#if XASH_PSVITA #if XASH_PSVITA
// not all GL1.1 functions are implemented in vitaGL, but there's enough // not all GL1.1 functions are implemented in vitaGL, but there's enough
@@ -1047,7 +1027,7 @@ void GL_InitExtensions( void )
GL_OnContextCreated(); GL_OnContextCreated();
// initialize gl extensions // initialize gl extensions
GL_CheckExtension( "OpenGL 1.1.0", opengl_110funcs, NULL, GL_OPENGL_110, 1.0 ); GL_CheckExtension( "OpenGL 1.1.0", opengl_110funcs, ARRAYSIZE( opengl_110funcs ), NULL, GL_OPENGL_110, 1.0 );
// get our various GL strings // get our various GL strings
glConfig.vendor_string = (const char *)pglGetString( GL_VENDOR ); glConfig.vendor_string = (const char *)pglGetString( GL_VENDOR );

View File

@@ -104,27 +104,33 @@ void R_MarkLights( dlight_t *light, int bit, mnode_t *node )
float dist; float dist;
msurface_t *surf; msurface_t *surf;
int i; int i;
mnode_t *children[2];
int firstsurface, numsurfaces;
if( !node || node->contents < 0 ) if( !node || node->contents < 0 )
return; return;
dist = PlaneDiff( light->origin, node->plane ); dist = PlaneDiff( light->origin, node->plane );
node_children( children, node, RI.currentmodel );
firstsurface = node_firstsurface( node, RI.currentmodel );
numsurfaces = node_numsurfaces( node, RI.currentmodel );
if( dist > light->radius ) if( dist > light->radius )
{ {
R_MarkLights( light, bit, node->children[0] ); R_MarkLights( light, bit, children[0] );
return; return;
} }
if( dist < -light->radius ) if( dist < -light->radius )
{ {
R_MarkLights( light, bit, node->children[1] ); R_MarkLights( light, bit, children[1] );
return; return;
} }
// mark the polygons // mark the polygons
surf = RI.currentmodel->surfaces + node->firstsurface; surf = RI.currentmodel->surfaces + firstsurface;
for( i = 0; i < node->numsurfaces; i++, surf++ ) for( i = 0; i < numsurfaces; i++, surf++ )
{ {
if( !BoundsAndSphereIntersect( surf->info->mins, surf->info->maxs, light->origin, light->radius )) if( !BoundsAndSphereIntersect( surf->info->mins, surf->info->maxs, light->origin, light->radius ))
continue; // no intersection continue; // no intersection
@@ -137,8 +143,8 @@ void R_MarkLights( dlight_t *light, int bit, mnode_t *node )
surf->dlightbits |= bit; surf->dlightbits |= bit;
} }
R_MarkLights( light, bit, node->children[0] ); R_MarkLights( light, bit, children[0] );
R_MarkLights( light, bit, node->children[1] ); R_MarkLights( light, bit, children[1] );
} }
/* /*
@@ -202,6 +208,8 @@ static qboolean R_RecursiveLightPoint( model_t *model, mnode_t *node, float p1f,
mtexinfo_t *tex; mtexinfo_t *tex;
matrix3x4 tbn; matrix3x4 tbn;
vec3_t mid; vec3_t mid;
mnode_t *children[2];
int firstsurface, numsurfaces;
// didn't hit anything // didn't hit anything
if( !node || node->contents < 0 ) if( !node || node->contents < 0 )
@@ -210,13 +218,17 @@ static qboolean R_RecursiveLightPoint( model_t *model, mnode_t *node, float p1f,
return false; return false;
} }
node_children( children, node, model );
firstsurface = node_firstsurface( node, model );
numsurfaces = node_numsurfaces( node, model );
// calculate mid point // calculate mid point
front = PlaneDiff( start, node->plane ); front = PlaneDiff( start, node->plane );
back = PlaneDiff( end, node->plane ); back = PlaneDiff( end, node->plane );
side = front < 0; side = front < 0;
if(( back < 0 ) == side ) if(( back < 0 ) == side )
return R_RecursiveLightPoint( model, node->children[side], p1f, p2f, cv, start, end ); return R_RecursiveLightPoint( model, children[side], p1f, p2f, cv, start, end );
frac = front / ( front - back ); frac = front / ( front - back );
@@ -224,7 +236,7 @@ static qboolean R_RecursiveLightPoint( model_t *model, mnode_t *node, float p1f,
midf = p1f + ( p2f - p1f ) * frac; midf = p1f + ( p2f - p1f ) * frac;
// co down front side // co down front side
if( R_RecursiveLightPoint( model, node->children[side], p1f, midf, cv, start, mid )) if( R_RecursiveLightPoint( model, children[side], p1f, midf, cv, start, mid ))
return true; // hit something return true; // hit something
if(( back < 0 ) == side ) if(( back < 0 ) == side )
@@ -234,10 +246,10 @@ static qboolean R_RecursiveLightPoint( model_t *model, mnode_t *node, float p1f,
} }
// check for impact on this node // check for impact on this node
surf = model->surfaces + node->firstsurface; surf = model->surfaces + firstsurface;
VectorCopy( mid, g_trace_lightspot ); VectorCopy( mid, g_trace_lightspot );
for( i = 0; i < node->numsurfaces; i++, surf++ ) for( i = 0; i < numsurfaces; i++, surf++ )
{ {
int smax, tmax; int smax, tmax;
@@ -326,7 +338,7 @@ static qboolean R_RecursiveLightPoint( model_t *model, mnode_t *node, float p1f,
} }
// go down back side // go down back side
return R_RecursiveLightPoint( model, node->children[!side], midf, p2f, cv, mid, end ); return R_RecursiveLightPoint( model, children[!side], midf, p2f, cv, mid, end );
} }
/* /*

View File

@@ -600,6 +600,7 @@ watertexture to grab fog values from it
static gl_texture_t *R_RecursiveFindWaterTexture( const mnode_t *node, const mnode_t *ignore, qboolean down ) static gl_texture_t *R_RecursiveFindWaterTexture( const mnode_t *node, const mnode_t *ignore, qboolean down )
{ {
gl_texture_t *tex = NULL; gl_texture_t *tex = NULL;
mnode_t *children[2];
// assure the initial node is not null // assure the initial node is not null
// we could check it here, but we would rather check it // we could check it here, but we would rather check it
@@ -637,15 +638,17 @@ static gl_texture_t *R_RecursiveFindWaterTexture( const mnode_t *node, const mno
// this is a regular node // this is a regular node
// traverse children // traverse children
if( node->children[0] && ( node->children[0] != ignore )) node_children( children, node, WORLDMODEL );
if( children[0] && ( children[0] != ignore ))
{ {
tex = R_RecursiveFindWaterTexture( node->children[0], node, true ); tex = R_RecursiveFindWaterTexture( children[0], node, true );
if( tex ) return tex; if( tex ) return tex;
} }
if( node->children[1] && ( node->children[1] != ignore )) if( children[1] && ( children[1] != ignore ))
{ {
tex = R_RecursiveFindWaterTexture( node->children[1], node, true ); tex = R_RecursiveFindWaterTexture( children[1], node, true );
if( tex ) return tex; if( tex ) return tex;
} }

View File

@@ -125,12 +125,25 @@ static void R_TextureCoord( const vec3_t v, const msurface_t *surf, vec2_t coord
static void R_GetEdgePosition( const model_t *mod, const msurface_t *fa, int i, vec3_t vec ) static void R_GetEdgePosition( const model_t *mod, const msurface_t *fa, int i, vec3_t vec )
{ {
const int lindex = mod->surfedges[fa->firstedge + i]; const int lindex = mod->surfedges[fa->firstedge + i];
const medge_t *pedges = mod->edges;
if( lindex > 0 ) if( FBitSet( mod->flags, MODEL_QBSP2 ))
VectorCopy( mod->vertexes[pedges[lindex].v[0]].position, vec ); {
const medge32_t *pedges = mod->edges32;
if( lindex > 0 )
VectorCopy( mod->vertexes[pedges[lindex].v[0]].position, vec );
else
VectorCopy( mod->vertexes[pedges[-lindex].v[1]].position, vec );
}
else else
VectorCopy( mod->vertexes[pedges[-lindex].v[1]].position, vec ); {
const medge16_t *pedges = mod->edges16;
if( lindex > 0 )
VectorCopy( mod->vertexes[pedges[lindex].v[0]].position, vec );
else
VectorCopy( mod->vertexes[pedges[-lindex].v[1]].position, vec );
}
} }
static void BoundPoly( int numverts, float *verts, vec3_t mins, vec3_t maxs ) static void BoundPoly( int numverts, float *verts, vec3_t mins, vec3_t maxs )
@@ -1317,10 +1330,8 @@ static void R_RenderBrushPoly( msurface_t *fa, int cull_type )
if( FBitSet( fa->flags, SURF_DRAWTURB )) if( FBitSet( fa->flags, SURF_DRAWTURB ))
{ {
R_UploadRipples( t );
// warp texture, no lightmaps // warp texture, no lightmaps
EmitWaterPolys( fa, (cull_type == CULL_BACKSIDE)); EmitWaterPolys( fa, cull_type == CULL_BACKSIDE, R_UploadRipples( t ));
return; return;
} }
else GL_Bind( XASH_TEXTURE0, t->gl_texturenum ); else GL_Bind( XASH_TEXTURE0, t->gl_texturenum );
@@ -1500,11 +1511,8 @@ void R_DrawWaterSurfaces( void )
if( !FBitSet( s->flags, SURF_DRAWTURB )) if( !FBitSet( s->flags, SURF_DRAWTURB ))
continue; continue;
// set modulate mode explicitly
R_UploadRipples( t );
for( ; s; s = s->texturechain ) for( ; s; s = s->texturechain )
EmitWaterPolys( s, false ); EmitWaterPolys( s, false, R_UploadRipples( t ));
t->texturechain = NULL; t->texturechain = NULL;
} }
@@ -3311,6 +3319,9 @@ static void R_RecursiveWorldNode( mnode_t *node, uint clipflags )
mleaf_t *pleaf; mleaf_t *pleaf;
int c, side; int c, side;
float dot; float dot;
mnode_t *children[2];
int numsurfaces, firstsurface;
loc0: loc0:
if( node->contents == CONTENTS_SOLID ) if( node->contents == CONTENTS_SOLID )
return; // hit a solid leaf return; // hit a solid leaf
@@ -3365,10 +3376,14 @@ loc0:
side = (dot >= 0.0f) ? 0 : 1; side = (dot >= 0.0f) ? 0 : 1;
// recurse down the children, front side first // recurse down the children, front side first
R_RecursiveWorldNode( node->children[side], clipflags ); node_children( children, node, WORLDMODEL );
R_RecursiveWorldNode( children[side], clipflags );
firstsurface = node_firstsurface( node, WORLDMODEL );
numsurfaces = node_numsurfaces( node, WORLDMODEL );
// draw stuff // draw stuff
for( c = node->numsurfaces, surf = WORLDMODEL->surfaces + node->firstsurface; c; c--, surf++ ) for( c = numsurfaces, surf = WORLDMODEL->surfaces + firstsurface; c; c--, surf++ )
{ {
if( R_CullSurface( surf, &RI.frustum, clipflags )) if( R_CullSurface( surf, &RI.frustum, clipflags ))
continue; continue;
@@ -3387,7 +3402,7 @@ loc0:
} }
// recurse down the back side // recurse down the back side
node = node->children[!side]; node = children[!side];
goto loc0; goto loc0;
} }
@@ -3463,6 +3478,9 @@ static void R_DrawWorldTopView( mnode_t *node, uint clipflags )
do do
{ {
mnode_t *children[2];
int numsurfaces, firstsurface;
if( node->contents == CONTENTS_SOLID ) if( node->contents == CONTENTS_SOLID )
return; // hit a solid leaf return; // hit a solid leaf
@@ -3496,7 +3514,10 @@ static void R_DrawWorldTopView( mnode_t *node, uint clipflags )
} }
// draw stuff // draw stuff
for( c = node->numsurfaces, surf = WORLDMODEL->surfaces + node->firstsurface; c; c--, surf++ ) numsurfaces = node_numsurfaces( node, WORLDMODEL );
firstsurface = node_firstsurface( node, WORLDMODEL );
for( c = numsurfaces, surf = WORLDMODEL->surfaces + firstsurface; c; c--, surf++ )
{ {
// don't process the same surface twice // don't process the same surface twice
if( surf->visframe == tr.framecount ) if( surf->visframe == tr.framecount )
@@ -3515,9 +3536,9 @@ static void R_DrawWorldTopView( mnode_t *node, uint clipflags )
} }
// recurse down both children, we don't care the order... // recurse down both children, we don't care the order...
R_DrawWorldTopView( node->children[0], clipflags ); node_children( children, node, WORLDMODEL );
node = node->children[1]; R_DrawWorldTopView( children[0], clipflags );
node = children[1];
} while( node ); } while( node );
} }

View File

@@ -81,7 +81,6 @@ static struct
uint32_t texture[RIPPLES_TEXSIZE]; uint32_t texture[RIPPLES_TEXSIZE];
int gl_texturenum; int gl_texturenum;
int rippletexturenum; int rippletexturenum;
float texturescale; // not all textures are 128x128, scale the texcoords down
} g_ripple; } g_ripple;
@@ -565,7 +564,7 @@ EmitWaterPolys
Does a water warp on the pre-fragmented glpoly_t chain Does a water warp on the pre-fragmented glpoly_t chain
============= =============
*/ */
void EmitWaterPolys( msurface_t *warp, qboolean reverse ) void EmitWaterPolys( msurface_t *warp, qboolean reverse, qboolean ripples )
{ {
float *v, nv, waveHeight; float *v, nv, waveHeight;
float s, t, os, ot; float s, t, os, ot;
@@ -609,15 +608,15 @@ void EmitWaterPolys( msurface_t *warp, qboolean reverse )
os = v[3]; os = v[3];
ot = v[4]; ot = v[4];
if( !r_ripple.value ) if( !ripples )
{ {
s = os + r_turbsin[(int)((ot * 0.125f + gp_cl->time) * TURBSCALE) & 255]; s = os + r_turbsin[(int)((ot * 0.125f + gp_cl->time) * TURBSCALE) & 255];
t = ot + r_turbsin[(int)((os * 0.125f + gp_cl->time) * TURBSCALE) & 255]; t = ot + r_turbsin[(int)((os * 0.125f + gp_cl->time) * TURBSCALE) & 255];
} }
else else
{ {
s = os / g_ripple.texturescale; s = os;
t = ot / g_ripple.texturescale; t = ot;
} }
s *= ( 1.0f / SUBDIVIDE_SIZE ); s *= ( 1.0f / SUBDIVIDE_SIZE );
@@ -710,17 +709,6 @@ static void R_RunRipplesAnimation( const short *oldbuf, short *pbuf )
} }
} }
static int MostSignificantBit( unsigned int v )
{
#if __GNUC__
return 31 - __builtin_clz( v );
#else
int i;
for( i = 0, v >>= 1; v; v >>= 1, i++ );
return i;
#endif
}
void R_AnimateRipples( void ) void R_AnimateRipples( void )
{ {
double frametime = gp_cl->time - g_ripple.time; double frametime = gp_cl->time - g_ripple.time;
@@ -750,67 +738,72 @@ void R_AnimateRipples( void )
R_RunRipplesAnimation( g_ripple.oldbuf, g_ripple.curbuf ); R_RunRipplesAnimation( g_ripple.oldbuf, g_ripple.curbuf );
} }
void R_UploadRipples( texture_t *image ) qboolean R_UploadRipples( const texture_t *image )
{ {
gl_texture_t *glt; const gl_texture_t *glt;
uint32_t *pixels; const uint32_t *pixels;
int wbits, wmask, wshft;
int y; int y;
int width, height, size;
// discard unuseful textures if( !r_ripple.value )
if( !r_ripple.value || image->width > RIPPLES_CACHEWIDTH || image->width != image->height )
{ {
GL_Bind( XASH_TEXTURE0, image->gl_texturenum ); GL_Bind( XASH_TEXTURE0, image->gl_texturenum );
return; return false;
} }
// discard unuseful textures
glt = R_GetTexture( image->gl_texturenum ); glt = R_GetTexture( image->gl_texturenum );
if( !glt || !glt->original || !glt->original->buffer || !FBitSet( glt->flags, TF_EXPAND_SOURCE )) if( !glt || !glt->original || !glt->original->buffer || !FBitSet( glt->flags, TF_EXPAND_SOURCE ))
{ {
GL_Bind( XASH_TEXTURE0, image->gl_texturenum ); GL_Bind( XASH_TEXTURE0, image->gl_texturenum );
return; return false;
} }
GL_Bind( XASH_TEXTURE0, g_ripple.rippletexturenum ); GL_Bind( XASH_TEXTURE0, g_ripple.rippletexturenum );
// no updates this frame // no updates this frame
if( !g_ripple.update && image->gl_texturenum == g_ripple.gl_texturenum ) if( !g_ripple.update && image->gl_texturenum == g_ripple.gl_texturenum )
return; return true;
g_ripple.gl_texturenum = image->gl_texturenum; g_ripple.gl_texturenum = image->gl_texturenum;
if( r_ripple.value == 1.0f )
{
g_ripple.texturescale = Q_max( 1.0f, image->width / 64.0f );
}
else
{
g_ripple.texturescale = 1.0f;
}
size = r_ripple.value == 1.0f ? 64 : RIPPLES_CACHEWIDTH;
pixels = (uint32_t *)glt->original->buffer; // try to preserve aspect ratio
wbits = MostSignificantBit( image->width ); width = height = RIPPLES_CACHEWIDTH; // always render at maximum size
wshft = 7 - wbits; if( image->width > image->height )
wmask = image->width - 1; height = (float)image->height / image->width * width;
else if( image->width < image->height )
width = (float)image->width / image->height * height;
for( y = 0; y < image->height; y++ ) pixels = (const uint32_t *)glt->original->buffer;
for( y = 0; y < height; y++ )
{ {
int ry = y << ( 7 + wshft ); int ry = (float)y / height * size;
int x; int x;
for( x = 0; x < image->width; x++ ) for( x = 0; x < width; x++ )
{ {
int rx = x << wshft; int rx = (float)x / width * size;
int val = g_ripple.curbuf[ry + rx] >> 4; int val = g_ripple.curbuf[ry * RIPPLES_CACHEWIDTH + rx] / 16;
int py = (y - val) & wmask; // transform it to texture space and get nice tiling effect
int px = (x + val) & wmask; int rpy = ( y - val ) % height;
int p = ( py << wbits ) + px; int rpx = ( x + val ) % width;
g_ripple.texture[(y << wbits) + x] = pixels[p]; int py = (float)rpy / height * image->height;
int px = (float)rpx / width * image->width;
if( py < 0 ) py = image->height + py;
if( px < 0 ) px = image->width + px;
g_ripple.texture[y * width + x] = pixels[py * image->width + px];
} }
} }
pglTexImage2D( GL_TEXTURE_2D, 0, GL_RGBA, image->width, image->width, 0, pglTexImage2D( GL_TEXTURE_2D, 0, glt->format, width, height, 0,
GL_RGBA, GL_UNSIGNED_BYTE, g_ripple.texture ); GL_RGBA, GL_UNSIGNED_BYTE, g_ripple.texture );
return true;
} }

View File

@@ -175,7 +175,7 @@ void R_RotateBmodel( void )
R_RecursiveClipBPoly R_RecursiveClipBPoly
================ ================
*/ */
static void R_RecursiveClipBPoly( bedge_t *pedges, mnode_t *pnode, msurface_t *psurf ) static void R_RecursiveClipBPoly( model_t *mod, bedge_t *pedges, mnode_t *pnode, msurface_t *psurf )
{ {
bedge_t *psideedges[2], *pnextedge, *ptedge; bedge_t *psideedges[2], *pnextedge, *ptedge;
int i, side, lastside; int i, side, lastside;
@@ -316,7 +316,7 @@ static void R_RecursiveClipBPoly( bedge_t *pedges, mnode_t *pnode, msurface_t *p
{ {
// draw if we've reached a non-solid leaf, done if all that's left is a // draw if we've reached a non-solid leaf, done if all that's left is a
// solid leaf, and continue down the tree if it's not a leaf // solid leaf, and continue down the tree if it's not a leaf
pn = pnode->children[i]; pn = node_child( pnode, i, mod );
// we're done with this branch if the node or leaf isn't in the PVS // we're done with this branch if the node or leaf isn't in the PVS
if( pn->visframe == tr.visframecount ) if( pn->visframe == tr.visframecount )
@@ -332,8 +332,7 @@ static void R_RecursiveClipBPoly( bedge_t *pedges, mnode_t *pnode, msurface_t *p
} }
else else
{ {
R_RecursiveClipBPoly( psideedges[i], pnode->children[i], R_RecursiveClipBPoly( mod, psideedges[i], pn, psurf );
psurf );
} }
} }
} }
@@ -356,13 +355,13 @@ void R_DrawSolidClippedSubmodelPolygons( model_t *pmodel, mnode_t *topnode )
mplane_t *pplane; mplane_t *pplane;
mvertex_t bverts[MAX_BMODEL_VERTS]; mvertex_t bverts[MAX_BMODEL_VERTS];
bedge_t bedges[MAX_BMODEL_EDGES], *pbedge; bedge_t bedges[MAX_BMODEL_EDGES], *pbedge;
medge_t *pedge, *pedges; medge16_t *pedge, *pedges;
// FIXME: use bounding-box-based frustum clipping info? // FIXME: use bounding-box-based frustum clipping info?
psurf = &pmodel->surfaces[pmodel->firstmodelsurface]; psurf = &pmodel->surfaces[pmodel->firstmodelsurface];
numsurfaces = pmodel->nummodelsurfaces; numsurfaces = pmodel->nummodelsurfaces;
pedges = pmodel->edges; pedges = pmodel->edges16;
for( i = 0; i < numsurfaces; i++, psurf++ ) for( i = 0; i < numsurfaces; i++, psurf++ )
{ {
@@ -419,7 +418,7 @@ void R_DrawSolidClippedSubmodelPolygons( model_t *pmodel, mnode_t *topnode )
pbedge[j - 1].pnext = NULL; // mark end of edges pbedge[j - 1].pnext = NULL; // mark end of edges
// if ( !( psurf->texinfo->flags & ( SURF_TRANS66 | SURF_TRANS33 ) ) ) // if ( !( psurf->texinfo->flags & ( SURF_TRANS66 | SURF_TRANS33 ) ) )
R_RecursiveClipBPoly( pbedge, topnode, psurf ); R_RecursiveClipBPoly( pmodel, pbedge, topnode, psurf );
// else // else
// R_RenderBmodelFace( pbedge, psurf ); // R_RenderBmodelFace( pbedge, psurf );
} }
@@ -566,6 +565,9 @@ static void R_RecursiveWorldNode( mnode_t *node, int clipflags )
} }
else else
{ {
mnode_t *children[2];
int firstsurface;
// node is just a decision point, so go down the apropriate sides // node is just a decision point, so go down the apropriate sides
// find which side of the node we are on // find which side of the node we are on
@@ -593,14 +595,16 @@ static void R_RecursiveWorldNode( mnode_t *node, int clipflags )
side = 1; side = 1;
// recurse down the children, front side first // recurse down the children, front side first
R_RecursiveWorldNode( node->children[side], clipflags ); node_children( children, node, WORLDMODEL );
R_RecursiveWorldNode( children[side], clipflags );
// draw stuff // draw stuff
c = node->numsurfaces; c = node_numsurfaces( node, WORLDMODEL );
firstsurface = node_firstsurface( node, WORLDMODEL );
if( c ) if( c )
{ {
surf = WORLDMODEL->surfaces + node->firstsurface; surf = WORLDMODEL->surfaces + firstsurface;
if( dot < -BACKFACE_EPSILON ) if( dot < -BACKFACE_EPSILON )
{ {
@@ -636,7 +640,7 @@ static void R_RecursiveWorldNode( mnode_t *node, int clipflags )
} }
// recurse down the back side // recurse down the back side
R_RecursiveWorldNode( node->children[!side], clipflags ); R_RecursiveWorldNode( children[!side], clipflags );
} }
} }

View File

@@ -679,23 +679,24 @@ static void R_DecalSurface( msurface_t *surf, decalinfo_t *decalinfo )
static void R_DecalNodeSurfaces( model_t *model, mnode_t *node, decalinfo_t *decalinfo ) static void R_DecalNodeSurfaces( model_t *model, mnode_t *node, decalinfo_t *decalinfo )
{ {
// iterate over all surfaces in the node // iterate over all surfaces in the node
msurface_t *surf; msurface_t *surf;
int i; int i;
int firstsurface, numsurfaces;
surf = model->surfaces + node->firstsurface; firstsurface = node_firstsurface( node, model );
numsurfaces = node_numsurfaces( node, model );
for( i = 0; i < node->numsurfaces; i++, surf++ ) surf = model->surfaces + firstsurface;
for( i = 0; i < numsurfaces; i++, surf++ )
{ {
// never apply decals on the water or sky surfaces // never apply decals on the water or sky surfaces
if( surf->flags & ( SURF_DRAWTURB | SURF_DRAWSKY | SURF_CONVEYOR )) if( surf->flags & (SURF_DRAWTURB|SURF_DRAWSKY|SURF_CONVEYOR))
continue; continue;
// we can implement alpha testing without stencil
// if( surf->flags & SURF_TRANSPARENT && !glState.stencilEnabled )
// continue;
R_DecalSurface( surf, decalinfo ); R_DecalSurface( surf, decalinfo );
} }
} }
// ----------------------------------------------------------------------------- // -----------------------------------------------------------------------------
@@ -707,6 +708,7 @@ static void R_DecalNode( model_t *model, mnode_t *node, decalinfo_t *decalinfo )
{ {
mplane_t *splitplane; mplane_t *splitplane;
float dist; float dist;
mnode_t *children[2];
Assert( node != NULL ); Assert( node != NULL );
@@ -718,6 +720,7 @@ static void R_DecalNode( model_t *model, mnode_t *node, decalinfo_t *decalinfo )
splitplane = node->plane; splitplane = node->plane;
dist = DotProduct( decalinfo->m_Position, splitplane->normal ) - splitplane->dist; dist = DotProduct( decalinfo->m_Position, splitplane->normal ) - splitplane->dist;
node_children( children, node, model );
// This is arbitrarily set to 10 right now. In an ideal world we'd have the // This is arbitrarily set to 10 right now. In an ideal world we'd have the
// exact surface but we don't so, this tells me which planes are "sort of // exact surface but we don't so, this tells me which planes are "sort of
@@ -729,19 +732,19 @@ static void R_DecalNode( model_t *model, mnode_t *node, decalinfo_t *decalinfo )
// have a surface normal // have a surface normal
if( dist > decalinfo->m_Size ) if( dist > decalinfo->m_Size )
{ {
R_DecalNode( model, node->children[0], decalinfo ); R_DecalNode( model, children[0], decalinfo );
} }
else if( dist < -decalinfo->m_Size ) else if( dist < -decalinfo->m_Size )
{ {
R_DecalNode( model, node->children[1], decalinfo ); R_DecalNode( model, children[1], decalinfo );
} }
else else
{ {
if( dist < DECAL_DISTANCE && dist > -DECAL_DISTANCE ) if( dist < DECAL_DISTANCE && dist > -DECAL_DISTANCE )
R_DecalNodeSurfaces( model, node, decalinfo ); R_DecalNodeSurfaces( model, node, decalinfo );
R_DecalNode( model, node->children[0], decalinfo ); R_DecalNode( model, children[0], decalinfo );
R_DecalNode( model, node->children[1], decalinfo ); R_DecalNode( model, children[1], decalinfo );
} }
} }

View File

@@ -104,44 +104,50 @@ R_MarkLights
*/ */
void R_MarkLights( dlight_t *light, int bit, mnode_t *node ) void R_MarkLights( dlight_t *light, int bit, mnode_t *node )
{ {
float dist; float dist;
msurface_t *surf; msurface_t *surf;
int i; int i;
mnode_t *children[2];
int firstsurface, numsurfaces;
if( !node || node->contents < 0 ) if( !node || node->contents < 0 )
return; return;
dist = PlaneDiff( light->origin, node->plane ); dist = PlaneDiff( light->origin, node->plane );
node_children( children, node, RI.currentmodel );
firstsurface = node_firstsurface( node, RI.currentmodel );
numsurfaces = node_numsurfaces( node, RI.currentmodel );
if( dist > light->radius ) if( dist > light->radius )
{ {
R_MarkLights( light, bit, node->children[0] ); R_MarkLights( light, bit, children[0] );
return; return;
} }
if( dist < -light->radius ) if( dist < -light->radius )
{ {
R_MarkLights( light, bit, node->children[1] ); R_MarkLights( light, bit, children[1] );
return; return;
} }
// mark the polygons // mark the polygons
surf = RI.currentmodel->surfaces + node->firstsurface; surf = RI.currentmodel->surfaces + firstsurface;
for( i = 0; i < node->numsurfaces; i++, surf++ ) for( i = 0; i < numsurfaces; i++, surf++ )
{ {
if( !BoundsAndSphereIntersect( surf->info->mins, surf->info->maxs, light->origin, light->radius )) if( !BoundsAndSphereIntersect( surf->info->mins, surf->info->maxs, light->origin, light->radius ))
continue; // no intersection continue; // no intersection
if( surf->dlightframe != tr.framecount ) // tr.dlightframecount ) if( surf->dlightframe != tr.dlightframecount )
{ {
surf->dlightbits = 0; surf->dlightbits = 0;
surf->dlightframe = tr.framecount; // tr.dlightframecount; surf->dlightframe = tr.dlightframecount;
} }
surf->dlightbits |= bit; surf->dlightbits |= bit;
} }
R_MarkLights( light, bit, node->children[0] ); R_MarkLights( light, bit, children[0] );
R_MarkLights( light, bit, node->children[1] ); R_MarkLights( light, bit, children[1] );
} }
/* /*
@@ -205,6 +211,8 @@ static qboolean R_RecursiveLightPoint( model_t *model, mnode_t *node, float p1f,
mtexinfo_t *tex; mtexinfo_t *tex;
matrix3x4 tbn; matrix3x4 tbn;
vec3_t mid; vec3_t mid;
mnode_t *children[2];
int firstsurface, numsurfaces;
// didn't hit anything // didn't hit anything
if( !node || node->contents < 0 ) if( !node || node->contents < 0 )
@@ -213,13 +221,17 @@ static qboolean R_RecursiveLightPoint( model_t *model, mnode_t *node, float p1f,
return false; return false;
} }
node_children( children, node, model );
firstsurface = node_firstsurface( node, model );
numsurfaces = node_numsurfaces( node, model );
// calculate mid point // calculate mid point
front = PlaneDiff( start, node->plane ); front = PlaneDiff( start, node->plane );
back = PlaneDiff( end, node->plane ); back = PlaneDiff( end, node->plane );
side = front < 0; side = front < 0;
if(( back < 0 ) == side ) if(( back < 0 ) == side )
return R_RecursiveLightPoint( model, node->children[side], p1f, p2f, cv, start, end ); return R_RecursiveLightPoint( model, children[side], p1f, p2f, cv, start, end );
frac = front / ( front - back ); frac = front / ( front - back );
@@ -227,7 +239,7 @@ static qboolean R_RecursiveLightPoint( model_t *model, mnode_t *node, float p1f,
midf = p1f + ( p2f - p1f ) * frac; midf = p1f + ( p2f - p1f ) * frac;
// co down front side // co down front side
if( R_RecursiveLightPoint( model, node->children[side], p1f, midf, cv, start, mid )) if( R_RecursiveLightPoint( model, children[side], p1f, midf, cv, start, mid ))
return true; // hit something return true; // hit something
if(( back < 0 ) == side ) if(( back < 0 ) == side )
@@ -237,10 +249,10 @@ static qboolean R_RecursiveLightPoint( model_t *model, mnode_t *node, float p1f,
} }
// check for impact on this node // check for impact on this node
surf = model->surfaces + node->firstsurface; surf = model->surfaces + firstsurface;
VectorCopy( mid, g_trace_lightspot ); VectorCopy( mid, g_trace_lightspot );
for( i = 0; i < node->numsurfaces; i++, surf++ ) for( i = 0; i < numsurfaces; i++, surf++ )
{ {
int smax, tmax; int smax, tmax;
@@ -330,7 +342,7 @@ static qboolean R_RecursiveLightPoint( model_t *model, mnode_t *node, float p1f,
} }
// go down back side // go down back side
return R_RecursiveLightPoint( model, node->children[!side], midf, p2f, cv, mid, end ); return R_RecursiveLightPoint( model, children[!side], midf, p2f, cv, mid, end );
} }
/* /*

View File

@@ -933,7 +933,7 @@ typedef struct edge_s
unsigned short surfs[2]; unsigned short surfs[2];
struct edge_s *nextremove; struct edge_s *nextremove;
float nearzi; float nearzi;
medge_t *owner; medge16_t *owner;
} edge_t; } edge_t;

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