mirror of
https://github.com/FWGS/xash3d-fwgs.git
synced 2026-08-05 11:35:05 +08:00
Compare commits
166 Commits
continuous
...
continuous
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
5d9bc950ce | ||
|
|
714da79395 | ||
|
|
823fe9ebbd | ||
|
|
4bf72b5383 | ||
|
|
51945f002b | ||
|
|
bcca9387de | ||
|
|
b8417fa46d | ||
|
|
85b0699288 | ||
|
|
6c03fb0226 | ||
|
|
a084dea07c | ||
|
|
2e0fc3e4c1 | ||
|
|
24f4d410ce | ||
|
|
052e0445ab | ||
|
|
758c908d5a | ||
|
|
3917c2589d | ||
|
|
f4a77308ec | ||
|
|
103b9724f9 | ||
|
|
75a315ecf0 | ||
|
|
63b3b9ef6d | ||
|
|
a6c2cfe89b | ||
|
|
5120657386 | ||
|
|
e754de46d1 | ||
|
|
5d718aa0d6 | ||
|
|
55fcbc8880 | ||
|
|
e19aa001b2 | ||
|
|
b447ea9c18 | ||
|
|
13274655d4 | ||
|
|
3723ac60ef | ||
|
|
da578f47f1 | ||
|
|
433e7de686 | ||
|
|
e18e9ae2ea | ||
|
|
27d5123a61 | ||
|
|
89b9f9ffe2 | ||
|
|
a17b8aaa13 | ||
|
|
e210969612 | ||
|
|
1208356b92 | ||
|
|
d7cd74fe2f | ||
|
|
31b63e9939 | ||
|
|
b99c2c2df1 | ||
|
|
94d3eff9ce | ||
|
|
b78e9961c6 | ||
|
|
b0704ca5d6 | ||
|
|
31c0934108 | ||
|
|
37083c8aef | ||
|
|
af23b0c67b | ||
|
|
a9e83fb9cf | ||
|
|
51126e1691 | ||
|
|
dd410a2de5 | ||
|
|
7ccb0b5c02 | ||
|
|
7f3e62e456 | ||
|
|
fcfc29d7ea | ||
|
|
2f3429a144 | ||
|
|
20782693f4 | ||
|
|
0cfb354374 | ||
|
|
9be5b7c6c9 | ||
|
|
e7653d2ea6 | ||
|
|
1494887e29 | ||
|
|
42dab2b1a5 | ||
|
|
36a0c1fb17 | ||
|
|
60c6767337 | ||
|
|
75451cc7fa | ||
|
|
db10035d9d | ||
|
|
de21d845ec | ||
|
|
320f8466e6 | ||
|
|
f3208e95b2 | ||
|
|
ea34bc8652 | ||
|
|
3daf014af8 | ||
|
|
ca9d3d262a | ||
|
|
a08f5e439d | ||
|
|
8f6771dff4 | ||
|
|
cd47c6b5b3 | ||
|
|
1f4f3d7fda | ||
|
|
e81b5144b3 | ||
|
|
9ec1caed53 | ||
|
|
ca3b4a9d7f | ||
|
|
87ac217887 | ||
|
|
213650db8f | ||
|
|
ac46164a6e | ||
|
|
6d12d84e94 | ||
|
|
2a18cde60c | ||
|
|
35783bcec2 | ||
|
|
1677835b45 | ||
|
|
2f2780cb1b | ||
|
|
95b134b5a6 | ||
|
|
f60e856f35 | ||
|
|
2ec0d25d85 | ||
|
|
fcd0982524 | ||
|
|
6881ee742d | ||
|
|
f070bbef3c | ||
|
|
dd8c66d90e | ||
|
|
f995d055b5 | ||
|
|
fed65dd497 | ||
|
|
c896425ad9 | ||
|
|
4107bd4e0f | ||
|
|
156b2b2b10 | ||
|
|
13a063bf7d | ||
|
|
ef786b7d79 | ||
|
|
5c0c1b1226 | ||
|
|
d041b2aa27 | ||
|
|
f6d489e038 | ||
|
|
d34fedea69 | ||
|
|
80b3f90091 | ||
|
|
2a4fafc3f9 | ||
|
|
9b3ccf5f04 | ||
|
|
f59ac2360a | ||
|
|
ff21fb42e6 | ||
|
|
288cac69de | ||
|
|
2640b6d869 | ||
|
|
d8910b0e38 | ||
|
|
049bed555e | ||
|
|
fe5944720b | ||
|
|
665c46b281 | ||
|
|
975ab6785e | ||
|
|
6fa4dbfba2 | ||
|
|
0506a1dfce | ||
|
|
908a9930e6 | ||
|
|
7d7e72196b | ||
|
|
7cc67157e5 | ||
|
|
b7ff9c7a24 | ||
|
|
aab1fa1ba2 | ||
|
|
effeba2f44 | ||
|
|
e039ef35c0 | ||
|
|
599a1f026e | ||
|
|
39cba17f01 | ||
|
|
17e106f96d | ||
|
|
081cf9132b | ||
|
|
6a7e027248 | ||
|
|
27100e7751 | ||
|
|
75d9ed341a | ||
|
|
1a0867523d | ||
|
|
7584bbe47e | ||
|
|
505b6cc264 | ||
|
|
24f23908be | ||
|
|
43bd73f37b | ||
|
|
bb2753cbfc | ||
|
|
5568eccc8a | ||
|
|
c3b2cc4423 | ||
|
|
d84130219f | ||
|
|
d97d4d6c42 | ||
|
|
15798aaf52 | ||
|
|
e53892e827 | ||
|
|
cb12af69ed | ||
|
|
e82050a4bc | ||
|
|
e4edd2da45 | ||
|
|
4f80922358 | ||
|
|
ae7ed03376 | ||
|
|
c082e46662 | ||
|
|
b0bbcab6b4 | ||
|
|
0fc83080ec | ||
|
|
73883b504f | ||
|
|
8efc9d74e8 | ||
|
|
373321ff00 | ||
|
|
a508467aac | ||
|
|
abbe993e80 | ||
|
|
c644e4d569 | ||
|
|
c200e008f1 | ||
|
|
e95a2da6d0 | ||
|
|
8afca1a79c | ||
|
|
827c9ca857 | ||
|
|
2416635ba3 | ||
|
|
39eec89580 | ||
|
|
98d88266a6 | ||
|
|
0f3359fc05 | ||
|
|
6c77608c34 | ||
|
|
abdff6eff6 | ||
|
|
95a75465c9 |
15
.cirrus.yml
15
.cirrus.yml
@@ -1,16 +1,3 @@
|
||||
task:
|
||||
# Seems packages for it were removed from the mirrors
|
||||
# name: freebsd-12-amd64
|
||||
# freebsd_instance:
|
||||
# image_family: freebsd-12-4
|
||||
# setup_script:
|
||||
# - pkg update
|
||||
# - pkg install -y pkgconf git sdl2 python fontconfig opus
|
||||
# - git submodule update --init --recursive
|
||||
# test_script:
|
||||
# - ./scripts/cirrus/build_freebsd.sh dedicated
|
||||
# - ./scripts/cirrus/build_freebsd.sh full
|
||||
|
||||
task:
|
||||
name: freebsd-13-amd64
|
||||
freebsd_instance:
|
||||
@@ -26,7 +13,7 @@ task:
|
||||
task:
|
||||
name: freebsd-14-amd64
|
||||
freebsd_instance:
|
||||
image_family: freebsd-14-0-snap
|
||||
image_family: freebsd-14-0
|
||||
setup_script:
|
||||
- pkg update
|
||||
- pkg install -y pkgconf git sdl2 python fontconfig opus
|
||||
|
||||
1
.github/FUNDING.yml
vendored
Normal file
1
.github/FUNDING.yml
vendored
Normal file
@@ -0,0 +1 @@
|
||||
custom: https://github.com/FWGS/xash3d-fwgs/blob/master/Documentation/donate.md
|
||||
2
.github/workflows/c-cpp.yml
vendored
2
.github/workflows/c-cpp.yml
vendored
@@ -44,7 +44,7 @@ jobs:
|
||||
targetos: win32
|
||||
targetarch: i386
|
||||
env:
|
||||
SDL_VERSION: 2.28.5
|
||||
SDL_VERSION: 2.30.3
|
||||
GH_CPU_ARCH: ${{ matrix.targetarch }}
|
||||
steps:
|
||||
- name: Checkout
|
||||
|
||||
2
3rdparty/gl4es/gl4es
vendored
2
3rdparty/gl4es/gl4es
vendored
Submodule 3rdparty/gl4es/gl4es updated: 5226f5f937...e39434a2b1
2
3rdparty/mainui
vendored
2
3rdparty/mainui
vendored
Submodule 3rdparty/mainui updated: da084b47f6...dc467364d0
2
3rdparty/nanogl
vendored
2
3rdparty/nanogl
vendored
Submodule 3rdparty/nanogl updated: f48735b730...e75792de03
@@ -1,17 +1,16 @@
|
||||
# Bug-compatibility in Xash3D FWGS
|
||||
|
||||
Xash3D FWGS has special mode for games that rely on original engine bugs.
|
||||
|
||||
In this mode, we emulate the behaviour of selected functions that may help running mods relying on engine bugs, but enabling them by default may break majority of other games.
|
||||
Xash3D FWGS has special mode for games that rely on original engine bugs. In this mode, we emulate the behaviour of selected functions that may help running mods relying on engine bugs, but enabling them by default may break majority of other games.
|
||||
|
||||
At this time, we only have implemented GoldSrc bug-compatibility. It can be enabled with `-bugcomp` command line switch.
|
||||
|
||||
When `-bugcomp` is specified without argument, it enables everything. This behavior might be changed or removed in future versions.
|
||||
|
||||
When `-bugcomp` is specified with argument, it interpreted as flags separated with `+`. This way it's possible to combine multiple levels of bug-compatibility.
|
||||
|
||||
## GoldSrc bug-compatibility
|
||||
|
||||
### Emulated bugs
|
||||
|
||||
* `pfnPEntityOfEntIndex` in GoldSrc returns NULL for last player due to incorrect player index comparison
|
||||
|
||||
### Games and mods that require this
|
||||
|
||||
* Counter-Strike: Condition Zero - Deleted Scenes
|
||||
| Flag | Description | Games that require this flag |
|
||||
| ------- | ----------- | ---------------------------- |
|
||||
| `peoei` | Reverts `pfnPEntityOfEntIndex` behavior to GoldSrc, where it returns NULL for last player due to incorrect player index comparison | * Counter-Strike: Condition Zero - Deleted Scenes |
|
||||
| `gsmrf` | Rewrites message at the moment when Game DLL attempts to write an internal engine message, usually specific to GoldSrc protocol. Right now only supports `svc_spawnstaticsound`, more messages added by request. | * MetaMod/AMXModX based mods |
|
||||
|
||||
21
Documentation/environment-variables.md
Normal file
21
Documentation/environment-variables.md
Normal file
@@ -0,0 +1,21 @@
|
||||
## Environment variables
|
||||
|
||||
#### Xash3D FWGS
|
||||
|
||||
The engine respects these environment variables:
|
||||
|
||||
| Variable | Type | Description |
|
||||
| --------------------- | ---------- | ----------- |
|
||||
| `XASH3D_GAME` | _string_ | Overrides default game directory. Ignored if `-game` command line argument is set |
|
||||
| `XASH3D_BASEDIR` | _string_ | Sets path to base (root) directory, instead of current working directory |
|
||||
| `XASH3D_RODIR` | _string_ | Sets path to read-only base (root) directory. Ignored if `-rodir` command line argument is set |
|
||||
| `XASH3D_EXTRAS_PAK1` | _string_ | Archive file from specified path will be added to virtual filesystem search path in the lowest possible priority |
|
||||
| `XASH3D_EXTRAS_PAK2` | _string_ | Similar to `XASH3D_EXTRAS_PAK1` but next to it in priority list |
|
||||
|
||||
Environment variables NOT listed in the table above are used internally, and aren't considered as stable interface.
|
||||
|
||||
#### mdldec
|
||||
|
||||
| Variable | Type | Description |
|
||||
| --------------------- | ---------- | ----------- |
|
||||
| `MDLDEC_ACT_PATH` | _string_ | If set, will read activities list from this path |
|
||||
@@ -534,5 +534,14 @@ typedef struct
|
||||
#define MAX_EFRAGS 8192 // Arcane Dimensions required
|
||||
#define MAX_REQUESTS 64
|
||||
|
||||
#if ! XASH_64BIT
|
||||
STATIC_ASSERT( sizeof( mextrasurf_t ) == 324, "mextrasurf_t unexpected size" );
|
||||
STATIC_ASSERT( sizeof( decal_t ) == 60, "decal_t unexpected size");
|
||||
STATIC_ASSERT( sizeof( mfaceinfo_t ) == 176, "mfaceinfo_t unexpected size");
|
||||
#else
|
||||
STATIC_ASSERT( sizeof( mextrasurf_t) == 496, "mextrasurf_t unexpected size");
|
||||
STATIC_ASSERT( sizeof( decal_t ) == 88, "decal_t unexpected size");
|
||||
STATIC_ASSERT( sizeof( mfaceinfo_t ) == 304, "mfaceinfo_t unexpected size");
|
||||
#endif
|
||||
|
||||
#endif//COM_MODEL_H
|
||||
|
||||
@@ -28,11 +28,7 @@ typedef byte rgb_t[3]; // unsigned byte colorpack
|
||||
typedef vec_t matrix3x4[3][4];
|
||||
typedef vec_t matrix4x4[4][4];
|
||||
|
||||
#if XASH_64BIT
|
||||
typedef uint32_t poolhandle_t;
|
||||
#else
|
||||
typedef void* poolhandle_t;
|
||||
#endif
|
||||
|
||||
#undef true
|
||||
#undef false
|
||||
@@ -74,54 +70,48 @@ typedef uint64_t longtime_t;
|
||||
#define IsColorString( p ) ( p && *( p ) == '^' && *(( p ) + 1) && *(( p ) + 1) >= '0' && *(( p ) + 1 ) <= '9' )
|
||||
#define ColorIndex( c ) ((( c ) - '0' ) & 7 )
|
||||
|
||||
#if defined(__GNUC__)
|
||||
#ifdef __i386__
|
||||
#define EXPORT __attribute__ ((visibility ("default"),force_align_arg_pointer))
|
||||
#define GAME_EXPORT __attribute((force_align_arg_pointer))
|
||||
#if defined( __GNUC__ )
|
||||
#if defined( __i386__ )
|
||||
#define EXPORT __attribute__(( visibility( "default" ), force_align_arg_pointer ))
|
||||
#define GAME_EXPORT __attribute(( force_align_arg_pointer ))
|
||||
#else
|
||||
#define EXPORT __attribute__ ((visibility ("default")))
|
||||
#define EXPORT __attribute__(( visibility ( "default" )))
|
||||
#define GAME_EXPORT
|
||||
#endif
|
||||
#define _format(x) __attribute__((format(printf, x, x+1)))
|
||||
#define NORETURN __attribute__((noreturn))
|
||||
#define NONNULL __attribute__((nonnull))
|
||||
#define ALLOC_CHECK(x) __attribute__((alloc_size(x)))
|
||||
#define FORCEINLINE inline __attribute__((always_inline))
|
||||
#define NOINLINE __attribute__((noinline))
|
||||
#elif defined(_MSC_VER)
|
||||
#define EXPORT __declspec( dllexport )
|
||||
#define GAME_EXPORT
|
||||
#define _format(x)
|
||||
#define NORETURN
|
||||
#define NONNULL
|
||||
#define ALLOC_CHECK(x)
|
||||
#define FORCEINLINE __forceinline
|
||||
#define NOINLINE __declspec( noinline )
|
||||
|
||||
#define NORETURN __attribute__(( noreturn ))
|
||||
#define NONNULL __attribute__(( nonnull ))
|
||||
#define _format( x ) __attribute__(( format( printf, x, x + 1 )))
|
||||
#define ALLOC_CHECK( x ) __attribute__(( alloc_size( x )))
|
||||
#define RENAME_SYMBOL( x ) asm( x )
|
||||
#else
|
||||
#define EXPORT
|
||||
#if defined( _MSC_VER )
|
||||
#define EXPORT __declspec( dllexport )
|
||||
#else
|
||||
#define EXPORT
|
||||
#endif
|
||||
#define GAME_EXPORT
|
||||
#define _format(x)
|
||||
#define NORETURN
|
||||
#define NONNULL
|
||||
#define ALLOC_CHECK(x)
|
||||
#define FORCEINLINE
|
||||
#define NOINLINE
|
||||
#define _format( x )
|
||||
#define ALLOC_CHECK( x )
|
||||
#define RENAME_SYMBOL( x )
|
||||
#endif
|
||||
|
||||
#if ( __GNUC__ >= 3 )
|
||||
#define unlikely(x) __builtin_expect(x, 0)
|
||||
#define likely(x) __builtin_expect(x, 1)
|
||||
#define unlikely( x ) __builtin_expect( x, 0 )
|
||||
#define likely( x ) __builtin_expect( x, 1 )
|
||||
#elif defined( __has_builtin )
|
||||
#if __has_builtin( __builtin_expect )
|
||||
#define unlikely(x) __builtin_expect(x, 0)
|
||||
#define likely(x) __builtin_expect(x, 1)
|
||||
#define unlikely( x ) __builtin_expect( x, 0 )
|
||||
#define likely( x ) __builtin_expect( x, 1 )
|
||||
#else
|
||||
#define unlikely(x) (x)
|
||||
#define likely(x) (x)
|
||||
#define unlikely( x ) ( x )
|
||||
#define likely( x ) ( x )
|
||||
#endif
|
||||
#else
|
||||
#define unlikely(x) (x)
|
||||
#define likely(x) (x)
|
||||
#define unlikely( x ) ( x )
|
||||
#define likely( x ) ( x )
|
||||
#endif
|
||||
|
||||
#if __STDC_VERSION__ >= 202311L || __cplusplus >= 201103L // C23 or C++ static_assert is a keyword
|
||||
|
||||
@@ -50,7 +50,15 @@ const char *CL_MsgInfo( int cmd )
|
||||
if( cmd >= 0 && cmd <= svc_lastmsg )
|
||||
{
|
||||
// get engine message name
|
||||
Q_strncpy( sz, svc_strings[cmd], sizeof( sz ));
|
||||
const char *svc_string = NULL;
|
||||
|
||||
if( cls.legacymode )
|
||||
svc_string = svc_legacy_strings[cmd];
|
||||
|
||||
if( !svc_string )
|
||||
svc_string = svc_strings[cmd];
|
||||
|
||||
Q_strncpy( sz, svc_string, sizeof( sz ));
|
||||
}
|
||||
else if( cmd > svc_lastmsg && cmd <= ( svc_lastmsg + MAX_USER_MESSAGES ))
|
||||
{
|
||||
|
||||
@@ -192,7 +192,7 @@ void CL_RegisterEvent( int lastnum, const char *szEvName, pfnEventHook func )
|
||||
ev = clgame.events[lastnum];
|
||||
|
||||
// NOTE: ev->index will be set later
|
||||
Q_strncpy( ev->name, szEvName, MAX_QPATH );
|
||||
Q_strncpy( ev->name, szEvName, sizeof( ev->name ));
|
||||
ev->func = func;
|
||||
}
|
||||
|
||||
|
||||
@@ -1448,7 +1448,7 @@ pfnSPR_Draw
|
||||
*/
|
||||
static void GAME_EXPORT pfnSPR_Draw( int frame, int x, int y, const wrect_t *prc )
|
||||
{
|
||||
ref.dllFuncs.GL_SetRenderMode( kRenderNormal );
|
||||
ref.dllFuncs.GL_SetRenderMode( kRenderTransAlpha );
|
||||
SPR_DrawGeneric( frame, x, y, -1, -1, prc );
|
||||
}
|
||||
|
||||
@@ -1713,7 +1713,8 @@ pfnCvar_RegisterVariable
|
||||
static cvar_t *GAME_EXPORT pfnCvar_RegisterClientVariable( const char *szName, const char *szValue, int flags )
|
||||
{
|
||||
// a1ba: try to mitigate outdated client.dll vulnerabilities
|
||||
if( !Q_stricmp( szName, "motdfile" ))
|
||||
if( !Q_stricmp( szName, "motdfile" )
|
||||
|| !Q_stricmp( szName, "sensitivity" ))
|
||||
flags |= FCVAR_PRIVILEGED;
|
||||
|
||||
return (cvar_t *)Cvar_Get( szName, szValue, flags|FCVAR_CLIENTDLL, Cvar_BuildAutoDescription( szName, flags|FCVAR_CLIENTDLL ));
|
||||
|
||||
@@ -28,7 +28,6 @@ GNU General Public License for more details.
|
||||
#define MAX_CMD_BUFFER 8000
|
||||
#define CONNECTION_PROBLEM_TIME 15.0 // 15 seconds
|
||||
#define CL_CONNECTION_RETRIES 10
|
||||
#define CL_TEST_RETRIES_NORESPONCE 3
|
||||
#define CL_TEST_RETRIES 5
|
||||
|
||||
CVAR_DEFINE_AUTO( mp_decals, "300", FCVAR_ARCHIVE, "decals limit in multiplayer" );
|
||||
@@ -1072,6 +1071,22 @@ static void CL_SendConnectPacket( void )
|
||||
cls.timestart = Sys_DoubleTime();
|
||||
}
|
||||
|
||||
/*
|
||||
=================
|
||||
CL_GetTestFragmentSize
|
||||
|
||||
Returns bandwidth test fragment size
|
||||
=================
|
||||
*/
|
||||
static int CL_GetTestFragmentSize( void )
|
||||
{
|
||||
const int fragmentSizes[CL_TEST_RETRIES] = { 64000, 32000, 10666, 5200, 1400 };
|
||||
if( cls.connect_retry >= 0 && cls.connect_retry < CL_TEST_RETRIES )
|
||||
return bound( FRAGMENT_MIN_SIZE, fragmentSizes[cls.connect_retry], FRAGMENT_MAX_SIZE );
|
||||
else
|
||||
return FRAGMENT_MIN_SIZE;
|
||||
}
|
||||
|
||||
/*
|
||||
=================
|
||||
CL_CheckForResend
|
||||
@@ -1083,8 +1098,9 @@ static void CL_CheckForResend( void )
|
||||
{
|
||||
netadr_t adr;
|
||||
net_gai_state_t res;
|
||||
float resendTime;
|
||||
qboolean bandwidthTest;
|
||||
|
||||
|
||||
if( cls.internetservers_wait )
|
||||
CL_SendMasterServerScanRequest();
|
||||
|
||||
@@ -1110,7 +1126,10 @@ static void CL_CheckForResend( void )
|
||||
else if( cl_resend.value > CL_MAX_RESEND_TIME )
|
||||
Cvar_SetValue( "cl_resend", CL_MAX_RESEND_TIME );
|
||||
|
||||
if(( host.realtime - cls.connect_time ) < cl_resend.value )
|
||||
bandwidthTest = !cls.legacymode && cl_test_bandwidth.value && cls.connect_retry <= CL_TEST_RETRIES;
|
||||
resendTime = bandwidthTest ? 1.0f : cl_resend.value;
|
||||
|
||||
if(( host.realtime - cls.connect_time ) < resendTime )
|
||||
return;
|
||||
|
||||
res = NET_StringToAdrNB( cls.servername, &adr );
|
||||
@@ -1137,10 +1156,11 @@ static void CL_CheckForResend( void )
|
||||
|
||||
if( adr.port == 0 ) adr.port = MSG_BigShort( PORT_SERVER );
|
||||
|
||||
if( cls.connect_retry == CL_TEST_RETRIES_NORESPONCE )
|
||||
if( cls.connect_retry == CL_TEST_RETRIES )
|
||||
{
|
||||
// too many fails use default connection method
|
||||
Con_Printf( "hi-speed connection is failed, use default method\n" );
|
||||
Con_Printf( "Bandwidth test failed, fallback to default connecting method\n" );
|
||||
Con_Printf( "Connecting to %s... (retry #%i)\n", cls.servername, cls.connect_retry + 1 );
|
||||
Netchan_OutOfBandPrint( NS_CLIENT, adr, "getchallenge\n" );
|
||||
Cvar_SetValue( "cl_dlmax", FRAGMENT_MIN_SIZE );
|
||||
cls.connect_time = host.realtime;
|
||||
@@ -1148,16 +1168,15 @@ static void CL_CheckForResend( void )
|
||||
return;
|
||||
}
|
||||
|
||||
bandwidthTest = !cls.legacymode && cl_test_bandwidth.value;
|
||||
cls.serveradr = adr;
|
||||
cls.max_fragment_size = Q_min( FRAGMENT_MAX_SIZE, cls.max_fragment_size / (cls.connect_retry + 1));
|
||||
cls.max_fragment_size = CL_GetTestFragmentSize();
|
||||
cls.connect_time = host.realtime; // for retransmit requests
|
||||
cls.connect_retry++;
|
||||
|
||||
if( bandwidthTest )
|
||||
Con_Printf( "Connecting to %s... [retry #%i, max fragment size %i]\n", cls.servername, cls.connect_retry, cls.max_fragment_size );
|
||||
Con_Printf( "Connecting to %s... (retry #%i, fragment size %i)\n", cls.servername, cls.connect_retry, cls.max_fragment_size );
|
||||
else
|
||||
Con_Printf( "Connecting to %s... [retry #%i]\n", cls.servername, cls.connect_retry );
|
||||
Con_Printf( "Connecting to %s... (retry #%i)\n", cls.servername, cls.connect_retry );
|
||||
|
||||
if( bandwidthTest )
|
||||
Netchan_OutOfBandPrint( NS_CLIENT, adr, "bandwidth %i %i\n", PROTOCOL_VERSION, cls.max_fragment_size );
|
||||
|
||||
@@ -1877,8 +1877,12 @@ void CL_ParseHLTV( sizebuf_t *msg )
|
||||
break;
|
||||
case HLTV_LISTEN:
|
||||
cls.signon = SIGNONS;
|
||||
#if 1
|
||||
MSG_ReadString( msg );
|
||||
#else
|
||||
NET_StringToAdr( MSG_ReadString( msg ), &cls.hltv_listen_address );
|
||||
// NET_JoinGroup( cls.netchan.sock, cls.hltv_listen_address );
|
||||
NET_JoinGroup( cls.netchan.sock, cls.hltv_listen_address );
|
||||
#endif
|
||||
SCR_EndLoadingPlaque();
|
||||
break;
|
||||
default:
|
||||
|
||||
@@ -194,7 +194,7 @@ static void CL_LegacyPrecacheModel( sizebuf_t *msg )
|
||||
if( modelIndex < 0 || modelIndex >= MAX_MODELS )
|
||||
Host_Error( "CL_PrecacheModel: bad modelindex %i\n", modelIndex );
|
||||
|
||||
Q_strncpy( model, MSG_ReadString( msg ), MAX_STRING );
|
||||
Q_strncpy( model, MSG_ReadString( msg ), sizeof( model ));
|
||||
//Q_strncpy( cl.model_precache[modelIndex], BF_ReadString( msg ), sizeof( cl.model_precache[0] ));
|
||||
|
||||
// when we loading map all resources is precached sequentially
|
||||
@@ -261,7 +261,7 @@ static void CL_LegacyParseResourceList( sizebuf_t *msg )
|
||||
for( i = 0; i < reslist.rescount; i++ )
|
||||
{
|
||||
reslist.restype[i] = MSG_ReadWord( msg );
|
||||
Q_strncpy( reslist.resnames[i], MSG_ReadString( msg ), MAX_QPATH );
|
||||
Q_strncpy( reslist.resnames[i], MSG_ReadString( msg ), sizeof( reslist.resnames[i] ));
|
||||
}
|
||||
|
||||
if( CL_IsPlaybackDemo() )
|
||||
|
||||
@@ -795,25 +795,14 @@ static void CL_SetupPMove( playermove_t *pmove, const local_state_t *from, const
|
||||
|
||||
pmove->player_index = ps->number - 1;
|
||||
|
||||
// a1ba: workaround bug on old protocol where the server refuse to send
|
||||
// our local player in delta. cl.playernum, in theory, must be equal
|
||||
// to our local player index anyway
|
||||
// a1ba: workaround bug where the server refuse to send our local player in delta
|
||||
// cl.playernum, in theory, must be equal to our local player index anyway
|
||||
//
|
||||
// this might not be a real solution, since everything else will be bogus
|
||||
// but we need to properly run prediction and avoid potential memory
|
||||
// corruption
|
||||
// either debug this, or remove when old protocol will be dropped!!!
|
||||
if( pmove->player_index < 0 )
|
||||
{
|
||||
if( cls.legacymode )
|
||||
{
|
||||
pmove->player_index = bound( 0, cl.playernum, cl.maxclients - 1 );
|
||||
}
|
||||
else
|
||||
{
|
||||
// if this happens, record a demo and send it to a1ba
|
||||
Host_Error( "%s: ps->number == %d\n", __func__, ps->number );
|
||||
}
|
||||
}
|
||||
pmove->player_index = bound( 0, cl.playernum, cl.maxclients - 1 );
|
||||
|
||||
pmove->multiplayer = (cl.maxclients > 1);
|
||||
pmove->runfuncs = runfuncs;
|
||||
@@ -864,7 +853,7 @@ static void CL_SetupPMove( playermove_t *pmove, const local_state_t *from, const
|
||||
VectorCopy( cd->vuser4, pmove->vuser4 );
|
||||
pmove->cmd = *ucmd; // copy current cmds
|
||||
|
||||
Q_strncpy( pmove->physinfo, cls.physinfo, MAX_INFO_STRING );
|
||||
Q_strncpy( pmove->physinfo, cls.physinfo, sizeof( pmove->physinfo ));
|
||||
}
|
||||
|
||||
static const void CL_FinishPMove( const playermove_t *pmove, local_state_t *to )
|
||||
|
||||
@@ -211,7 +211,7 @@ static void CL_ParseQuakeServerInfo( sizebuf_t *msg )
|
||||
clgame.maxEntities = GI->max_edicts;
|
||||
clgame.maxEntities = bound( 600, clgame.maxEntities, MAX_EDICTS );
|
||||
clgame.maxModels = MAX_MODELS;
|
||||
Q_strncpy( clgame.maptitle, MSG_ReadString( msg ), MAX_STRING );
|
||||
Q_strncpy( clgame.maptitle, MSG_ReadString( msg ), sizeof( clgame.maptitle ));
|
||||
|
||||
// Re-init hud video, especially if we changed game directories
|
||||
clgame.dllFuncs.pfnVidInit();
|
||||
|
||||
@@ -539,12 +539,8 @@ typedef struct
|
||||
|
||||
poolhandle_t mempool; // client premamnent pool: edicts etc
|
||||
|
||||
netadr_t hltv_listen_address;
|
||||
|
||||
int signon; // 0 to SIGNONS, for the signon sequence.
|
||||
int quakePort; // a 16 bit value that allows quake servers
|
||||
// to work around address translating routers
|
||||
// g-cont. this port allow many copies of engine in multiplayer game
|
||||
|
||||
// connection information
|
||||
char servername[MAX_QPATH]; // name of server from original connect
|
||||
double connect_time; // for connection retransmits
|
||||
|
||||
@@ -21,6 +21,7 @@ GNU General Public License for more details.
|
||||
#include "qfont.h"
|
||||
#include "wadfile.h"
|
||||
#include "input.h"
|
||||
#include "utflib.h"
|
||||
|
||||
static CVAR_DEFINE_AUTO( scr_conspeed, "600", FCVAR_ARCHIVE, "console moving speed" );
|
||||
static CVAR_DEFINE_AUTO( con_notifytime, "3", FCVAR_ARCHIVE, "notify time to live" );
|
||||
@@ -610,19 +611,6 @@ static void Con_LoadConchars( void )
|
||||
con.curFont = &con.chars[fontSize];
|
||||
}
|
||||
|
||||
// CP1251 table
|
||||
|
||||
int table_cp1251[64] = {
|
||||
0x0402, 0x0403, 0x201A, 0x0453, 0x201E, 0x2026, 0x2020, 0x2021,
|
||||
0x20AC, 0x2030, 0x0409, 0x2039, 0x040A, 0x040C, 0x040B, 0x040F,
|
||||
0x0452, 0x2018, 0x2019, 0x201C, 0x201D, 0x2022, 0x2013, 0x2014,
|
||||
0x007F, 0x2122, 0x0459, 0x203A, 0x045A, 0x045C, 0x045B, 0x045F,
|
||||
0x00A0, 0x040E, 0x045E, 0x0408, 0x00A4, 0x0490, 0x00A6, 0x00A7,
|
||||
0x0401, 0x00A9, 0x0404, 0x00AB, 0x00AC, 0x00AD, 0x00AE, 0x0407,
|
||||
0x00B0, 0x00B1, 0x0406, 0x0456, 0x0491, 0x00B5, 0x00B6, 0x00B7,
|
||||
0x0451, 0x2116, 0x0454, 0x00BB, 0x0458, 0x0405, 0x0455, 0x0457
|
||||
};
|
||||
|
||||
/*
|
||||
============================
|
||||
Con_UtfProcessChar
|
||||
@@ -632,83 +620,24 @@ Convert utf char to current font's single-byte encoding
|
||||
*/
|
||||
int Con_UtfProcessCharForce( int in )
|
||||
{
|
||||
static int m = -1, k = 0; //multibyte state
|
||||
static int uc = 0; //unicode char
|
||||
// TODO: get rid of global state where possible
|
||||
static utfstate_t state = { 0 };
|
||||
|
||||
if( !in )
|
||||
{
|
||||
m = -1;
|
||||
k = 0;
|
||||
uc = 0;
|
||||
return 0;
|
||||
}
|
||||
int ch = Q_DecodeUTF8( &state, in );
|
||||
|
||||
// Get character length
|
||||
if(m == -1)
|
||||
{
|
||||
uc = 0;
|
||||
if( in >= 0xF8 )
|
||||
return 0;
|
||||
else if( in >= 0xF0 )
|
||||
uc = in & 0x07, m = 3;
|
||||
else if( in >= 0xE0 )
|
||||
uc = in & 0x0F, m = 2;
|
||||
else if( in >= 0xC0 )
|
||||
uc = in & 0x1F, m = 1;
|
||||
else if( in <= 0x7F)
|
||||
return in; //ascii
|
||||
// return 0 if we need more chars to decode one
|
||||
k=0;
|
||||
return 0;
|
||||
}
|
||||
// get more chars
|
||||
else if( k <= m )
|
||||
{
|
||||
uc <<= 6;
|
||||
uc += in & 0x3F;
|
||||
k++;
|
||||
}
|
||||
if( in > 0xBF || m < 0 )
|
||||
{
|
||||
m = -1;
|
||||
return 0;
|
||||
}
|
||||
if( k == m )
|
||||
{
|
||||
k = m = -1;
|
||||
if( g_codepage == 1251 )
|
||||
{
|
||||
// cp1251 now
|
||||
if( uc >= 0x0410 && uc <= 0x042F )
|
||||
return uc - 0x410 + 0xC0;
|
||||
if( uc >= 0x0430 && uc <= 0x044F )
|
||||
return uc - 0x430 + 0xE0;
|
||||
else
|
||||
{
|
||||
int i;
|
||||
for( i = 0; i < 64; i++ )
|
||||
if( table_cp1251[i] == uc )
|
||||
return i + 0x80;
|
||||
}
|
||||
}
|
||||
else if( g_codepage == 1252 )
|
||||
{
|
||||
if( uc < 255 )
|
||||
return uc;
|
||||
}
|
||||
if( g_codepage == 1251 )
|
||||
return Q_UnicodeToCP1251( ch );
|
||||
if( g_codepage == 1252 )
|
||||
return Q_UnicodeToCP1252( ch );
|
||||
|
||||
// not implemented yet
|
||||
return '?';
|
||||
}
|
||||
return 0;
|
||||
return '?'; // not implemented yet
|
||||
}
|
||||
|
||||
int GAME_EXPORT Con_UtfProcessChar( int in )
|
||||
{
|
||||
if( !g_utf8 )
|
||||
return in;
|
||||
else
|
||||
return Con_UtfProcessCharForce( in );
|
||||
return Con_UtfProcessCharForce( in );
|
||||
}
|
||||
/*
|
||||
=================
|
||||
@@ -719,16 +648,22 @@ get position of previous printful char
|
||||
*/
|
||||
int Con_UtfMoveLeft( char *str, int pos )
|
||||
{
|
||||
int i, k = 0;
|
||||
// int j;
|
||||
utfstate_t state = { 0 };
|
||||
int k = 0;
|
||||
int i;
|
||||
|
||||
if( !g_utf8 )
|
||||
return pos - 1;
|
||||
Con_UtfProcessChar( 0 );
|
||||
if(pos == 1) return 0;
|
||||
for( i = 0; i < pos-1; i++ )
|
||||
if( Con_UtfProcessChar( (unsigned char)str[i] ) )
|
||||
k = i+1;
|
||||
Con_UtfProcessChar( 0 );
|
||||
|
||||
if( pos == 1 )
|
||||
return 0;
|
||||
|
||||
for( i = 0; i < pos - 1; i++ )
|
||||
{
|
||||
if( Q_DecodeUTF8( &state, (byte)str[i] ))
|
||||
k = i + 1;
|
||||
}
|
||||
|
||||
return k;
|
||||
}
|
||||
|
||||
@@ -741,17 +676,19 @@ get next of previous printful char
|
||||
*/
|
||||
int Con_UtfMoveRight( char *str, int pos, int length )
|
||||
{
|
||||
utfstate_t state = { 0 };
|
||||
int i;
|
||||
|
||||
if( !g_utf8 )
|
||||
return pos + 1;
|
||||
Con_UtfProcessChar( 0 );
|
||||
|
||||
for( i = pos; i <= length; i++ )
|
||||
{
|
||||
if( Con_UtfProcessChar( (unsigned char)str[i] ) )
|
||||
return i+1;
|
||||
if( Q_DecodeUTF8( &state, (byte)str[i] ))
|
||||
return i + 1;
|
||||
}
|
||||
Con_UtfProcessChar( 0 );
|
||||
return pos+1;
|
||||
|
||||
return pos + 1;
|
||||
}
|
||||
|
||||
static void Con_DrawCharToConback( int num, const byte *conchars, byte *dest )
|
||||
@@ -1125,7 +1062,7 @@ Con_ClearField
|
||||
*/
|
||||
static void Con_ClearField( field_t *edit )
|
||||
{
|
||||
memset( edit->buffer, 0, MAX_STRING );
|
||||
memset( edit->buffer, 0, sizeof( edit->buffer ));
|
||||
edit->cursor = 0;
|
||||
edit->scroll = 0;
|
||||
}
|
||||
@@ -1138,7 +1075,7 @@ Field_Set
|
||||
static void Field_Set( field_t *f, const char *string )
|
||||
{
|
||||
f->scroll = 0;
|
||||
f->cursor = Q_strncpy( f->buffer, string, MAX_STRING );
|
||||
f->cursor = Q_strncpy( f->buffer, string, sizeof( f->buffer ));
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -1444,36 +1381,49 @@ static void Con_HistoryAppend( con_history_t *self, field_t *from )
|
||||
|
||||
static void Con_LoadHistory( con_history_t *self )
|
||||
{
|
||||
const byte *aFile = FS_LoadFile( "console_history.txt", NULL, true );
|
||||
const char *pLine, *pFile;
|
||||
int i, len;
|
||||
field_t *f;
|
||||
file_t *fd;
|
||||
int i;
|
||||
|
||||
if( !aFile )
|
||||
fd = FS_Open( "console_history.txt", "rb", true );
|
||||
|
||||
if( !fd )
|
||||
return;
|
||||
|
||||
for( pFile = pLine = (char *)aFile; *pFile; pFile++ )
|
||||
while( !FS_Eof( fd ))
|
||||
{
|
||||
if( *pFile != '\n' )
|
||||
f = &self->lines[self->next % CON_HISTORY];
|
||||
|
||||
Con_ClearField( f );
|
||||
f->widthInChars = con.linewidth;
|
||||
|
||||
FS_Gets( fd, f->buffer, sizeof( f->buffer ));
|
||||
f->cursor = Q_strlen( f->buffer );
|
||||
|
||||
// skip empty lines
|
||||
if( f->cursor == 0 )
|
||||
continue;
|
||||
|
||||
Con_ClearField( &self->lines[self->next] );
|
||||
|
||||
len = Q_min( pFile - pLine + 1, sizeof( f->buffer ));
|
||||
f = &self->lines[self->next % CON_HISTORY];
|
||||
f->widthInChars = con.linewidth;
|
||||
f->cursor = len - 1;
|
||||
Q_strncpy( f->buffer, pLine, len);
|
||||
// skip repeating lines
|
||||
if( self->next > 0 )
|
||||
{
|
||||
field_t *prev;
|
||||
prev = &self->lines[(self->next - 1) % CON_HISTORY];
|
||||
if( !Q_stricmp( prev->buffer, f->buffer ))
|
||||
continue;
|
||||
}
|
||||
|
||||
self->next++;
|
||||
|
||||
pLine = pFile + 1;
|
||||
}
|
||||
|
||||
FS_Close( fd );
|
||||
|
||||
for( i = self->next; i < CON_HISTORY; i++ )
|
||||
{
|
||||
Con_ClearField( &self->lines[i] );
|
||||
self->lines[i].widthInChars = con.linewidth;
|
||||
f = &self->lines[i];
|
||||
|
||||
Con_ClearField( f );
|
||||
f->widthInChars = con.linewidth;
|
||||
}
|
||||
|
||||
self->line = self->next;
|
||||
@@ -1491,7 +1441,7 @@ static void Con_SaveHistory( con_history_t *self )
|
||||
if( historyStart < 0 )
|
||||
historyStart = 0;
|
||||
|
||||
f = FS_Open( "console_history.txt", "w", true );
|
||||
f = FS_Open( "console_history.txt", "wb", true );
|
||||
|
||||
for( i = historyStart; i < self->next; i++ )
|
||||
FS_Printf( f, "%s\n", self->lines[i % CON_HISTORY].buffer );
|
||||
@@ -1536,6 +1486,9 @@ void Key_Console( int key )
|
||||
Con_Printf( ">%s\n", con.input.buffer );
|
||||
|
||||
// copy line to history buffer
|
||||
// just in case, remove all CR and LF characters pushing it to the history
|
||||
// not sure how they get even added in the first place
|
||||
COM_RemoveLineFeed( con.input.buffer, sizeof( con.input.buffer ));
|
||||
Con_HistoryAppend( &con.history, &con.input );
|
||||
|
||||
Con_ClearField( &con.input );
|
||||
|
||||
@@ -1055,10 +1055,7 @@ static qboolean OSK_KeyEvent( int key, int down )
|
||||
break;
|
||||
}
|
||||
|
||||
if( !Q_stricmp( cl_charset.string, "utf-8" ) )
|
||||
ch = (unsigned char)osk.curbutton.val;
|
||||
else
|
||||
ch = Con_UtfProcessCharForce( (unsigned char)osk.curbutton.val );
|
||||
ch = Con_UtfProcessChar((byte)osk.curbutton.val );
|
||||
|
||||
if( !ch )
|
||||
break;
|
||||
|
||||
@@ -565,7 +565,7 @@ static void R_CollectRendererNames( void )
|
||||
"GL4ES",
|
||||
#endif
|
||||
#if XASH_REF_GLES3COMPAT_ENABLED
|
||||
"GLES3 (gl2_shim)"
|
||||
"GLES3 (gl2_shim)",
|
||||
#endif
|
||||
#if XASH_REF_SOFT_ENABLED
|
||||
"Software",
|
||||
|
||||
@@ -18,19 +18,18 @@ GNU General Public License for more details.
|
||||
|
||||
#include "ref_api.h"
|
||||
|
||||
#define RP_LOCALCLIENT( e ) ((e) != NULL && (e)->index == ( cl.playernum + 1 ) && e->player )
|
||||
#define RP_LOCALCLIENT( e ) ((e) != NULL && (e)->index == ( cl.playernum + 1 ) && e->player )
|
||||
|
||||
struct ref_state_s
|
||||
{
|
||||
qboolean initialized;
|
||||
|
||||
HINSTANCE hInstance;
|
||||
HINSTANCE hInstance;
|
||||
qboolean initialized;
|
||||
int numRenderers;
|
||||
ref_interface_t dllFuncs;
|
||||
|
||||
// depends on build configuration
|
||||
int numRenderers;
|
||||
const char **shortNames;
|
||||
const char **readableNames;
|
||||
const char **shortNames;
|
||||
const char **readableNames;
|
||||
};
|
||||
|
||||
extern struct ref_state_s ref;
|
||||
@@ -46,8 +45,8 @@ void R_GetTextureParms( int *w, int *h, int texnum );
|
||||
void GL_RenderFrame( const struct ref_viewpass_s *rvp );
|
||||
|
||||
// common engine and renderer cvars
|
||||
extern convar_t r_decals;
|
||||
extern convar_t r_adjust_fov;
|
||||
extern convar_t r_decals;
|
||||
extern convar_t r_adjust_fov;
|
||||
extern convar_t gl_clear;
|
||||
|
||||
qboolean R_Init( void );
|
||||
|
||||
@@ -52,8 +52,11 @@ void S_SoundList_f( void )
|
||||
{
|
||||
totalSize += sc->size;
|
||||
|
||||
if( sc->loopStart >= 0 ) Con_Printf( "L" );
|
||||
else Con_Printf( " " );
|
||||
if( FBitSet( sc->flags, SOUND_LOOPED ))
|
||||
Con_Printf( "L" );
|
||||
else
|
||||
Con_Printf( " " );
|
||||
|
||||
if( sfx->name[0] == '*' || !Q_strncmp( sfx->name, DEFAULT_SOUNDPATH, sizeof( DEFAULT_SOUNDPATH ) - 1 ))
|
||||
Con_Printf( " (%2db) %s : %s\n", sc->width * 8, Q_memprint( sc->size ), sfx->name );
|
||||
else Con_Printf( " (%2db) %s : " DEFAULT_SOUNDPATH "%s\n", sc->width * 8, Q_memprint( sc->size ), sfx->name );
|
||||
@@ -110,7 +113,7 @@ static wavdata_t *S_CreateDefaultSound( void )
|
||||
|
||||
sc->width = 2;
|
||||
sc->channels = 1;
|
||||
sc->loopStart = -1;
|
||||
sc->loopStart = 0;
|
||||
sc->rate = SOUND_DMA_SPEED;
|
||||
sc->samples = SOUND_DMA_SPEED;
|
||||
sc->size = sc->samples * sc->width * sc->channels;
|
||||
@@ -155,7 +158,7 @@ wavdata_t *S_LoadSound( sfx_t *sfx )
|
||||
Sound_Process( &sc, SOUND_11k, sc->width, SOUND_RESAMPLE );
|
||||
else if( sc->rate > SOUND_11k && sc->rate < SOUND_22k ) // some bad sounds
|
||||
Sound_Process( &sc, SOUND_22k, sc->width, SOUND_RESAMPLE );
|
||||
else if( sc->rate > SOUND_22k && sc->rate <= SOUND_32k ) // some bad sounds
|
||||
else if( sc->rate > SOUND_22k && sc->rate < SOUND_44k ) // some bad sounds
|
||||
Sound_Process( &sc, SOUND_44k, sc->width, SOUND_RESAMPLE );
|
||||
|
||||
sfx->cache = sc;
|
||||
@@ -218,7 +221,7 @@ sfx_t *S_FindName( const char *pname, int *pfInCache )
|
||||
sfx = &s_knownSfx[i];
|
||||
memset( sfx, 0, sizeof( *sfx ));
|
||||
if( pfInCache ) *pfInCache = false;
|
||||
Q_strncpy( sfx->name, name, MAX_STRING );
|
||||
Q_strncpy( sfx->name, name, sizeof( sfx->name ));
|
||||
sfx->servercount = cl.servercount;
|
||||
sfx->hashValue = COM_HashKey( sfx->name, MAX_SFX_HASH );
|
||||
|
||||
@@ -377,7 +380,7 @@ S_InitSounds
|
||||
void S_InitSounds( void )
|
||||
{
|
||||
// create unused 0-entry
|
||||
Q_strncpy( s_knownSfx->name, "*default", MAX_QPATH );
|
||||
Q_strncpy( s_knownSfx->name, "*default", sizeof( s_knownSfx->name ));
|
||||
s_knownSfx->hashValue = COM_HashKey( s_knownSfx->name, MAX_SFX_HASH );
|
||||
s_knownSfx->hashNext = s_sfxHashList[s_knownSfx->hashValue];
|
||||
s_sfxHashList[s_knownSfx->hashValue] = s_knownSfx;
|
||||
|
||||
@@ -43,7 +43,7 @@ CVAR_DEFINE_AUTO( s_lerping, "0", FCVAR_ARCHIVE|FCVAR_FILTERABLE, "apply interpo
|
||||
static CVAR_DEFINE( s_ambient_level, "ambient_level", "0.3", FCVAR_ARCHIVE|FCVAR_FILTERABLE, "volume of environment noises (water and wind)" );
|
||||
static CVAR_DEFINE( s_ambient_fade, "ambient_fade", "1000", FCVAR_ARCHIVE|FCVAR_FILTERABLE, "rate of volume fading when client is moving" );
|
||||
static CVAR_DEFINE_AUTO( s_combine_sounds, "0", FCVAR_ARCHIVE|FCVAR_FILTERABLE, "combine channels with same sounds" );
|
||||
CVAR_DEFINE_AUTO( snd_mute_losefocus, "1", FCVAR_ARCHIVE|FCVAR_FILTERABLE, "silence the audio when game window loses focus" );
|
||||
static CVAR_DEFINE_AUTO( snd_mute_losefocus, "1", FCVAR_ARCHIVE|FCVAR_FILTERABLE, "silence the audio when game window loses focus" );
|
||||
CVAR_DEFINE_AUTO( s_test, "0", 0, "engine developer cvar for quick testing new features" );
|
||||
CVAR_DEFINE_AUTO( s_samplecount, "0", FCVAR_ARCHIVE|FCVAR_FILTERABLE, "sample count (0 for default value)" );
|
||||
CVAR_DEFINE_AUTO( s_warn_late_precache, "0", FCVAR_ARCHIVE|FCVAR_FILTERABLE, "warn about late precached sounds on client-side" );
|
||||
@@ -64,6 +64,18 @@ float S_GetMasterVolume( void )
|
||||
{
|
||||
float scale = 1.0f;
|
||||
|
||||
if( host.status == HOST_NOFOCUS && snd_mute_losefocus.value != 0.0f )
|
||||
{
|
||||
// we return zero volume to keep sounds running
|
||||
return 0.0f;
|
||||
}
|
||||
|
||||
if( s_listener.inmenu && !ui_renderworld.value && !Host_IsLocalGame( ))
|
||||
{
|
||||
// mute sounds in menu when it's not transparent and we're in multiplayer
|
||||
return 0.0f;
|
||||
}
|
||||
|
||||
if( !s_listener.inmenu && soundfade.percent != 0 )
|
||||
{
|
||||
scale = bound( 0.0f, soundfade.percent / 100.0f, 1.0f );
|
||||
@@ -322,7 +334,7 @@ channel_t *SND_PickDynamicChannel( int entnum, int channel, sfx_t *sfx, qboolean
|
||||
// don't restart looping sounds for the same entity
|
||||
wavdata_t *sc = channels[first_to_die].sfx->cache;
|
||||
|
||||
if( sc && sc->loopStart != -1 )
|
||||
if( sc && FBitSet( sc->flags, SOUND_LOOPED ))
|
||||
{
|
||||
channel_t *ch = &channels[first_to_die];
|
||||
|
||||
@@ -497,7 +509,7 @@ static void SND_Spatialize( channel_t *ch )
|
||||
|
||||
pSource = ch->sfx->cache;
|
||||
|
||||
if( ch->use_loop && pSource && pSource->loopStart != -1 )
|
||||
if( ch->use_loop && pSource && FBitSet( pSource->flags, SOUND_LOOPED ))
|
||||
looping = true;
|
||||
|
||||
if( !ch->staticsound )
|
||||
@@ -635,7 +647,7 @@ void S_StartSound( const vec3_t pos, int ent, int chan, sound_t handle, float fv
|
||||
if( !target_chan->leftvol && !target_chan->rightvol )
|
||||
{
|
||||
// looping sounds don't use this optimization because they should stick around until they're killed.
|
||||
if( !sfx->cache || sfx->cache->loopStart == -1 )
|
||||
if( !sfx->cache || !FBitSet( sfx->cache->flags, SOUND_LOOPED ))
|
||||
{
|
||||
// if this is a streaming sound, play the whole thing.
|
||||
if( chan != CHAN_STREAM )
|
||||
@@ -887,7 +899,7 @@ int S_GetCurrentStaticSounds( soundlist_t *pout, int size )
|
||||
VectorCopy( channels[i].origin, pout->origin );
|
||||
pout->volume = (float)channels[i].master_vol / 255.0f;
|
||||
pout->attenuation = channels[i].dist_mult * SND_CLIP_DISTANCE;
|
||||
pout->looping = ( channels[i].use_loop && channels[i].sfx->cache->loopStart != -1 );
|
||||
pout->looping = ( channels[i].use_loop && FBitSet( channels[i].sfx->cache->flags, SOUND_LOOPED ));
|
||||
pout->pitch = channels[i].basePitch;
|
||||
pout->channel = channels[i].entchannel;
|
||||
pout->wordIndex = channels[i].wordIndex;
|
||||
@@ -922,7 +934,7 @@ int S_GetCurrentDynamicSounds( soundlist_t *pout, int size )
|
||||
if( !channels[i].sfx || !channels[i].sfx->name[0] || !Q_stricmp( channels[i].sfx->name, "*default" ))
|
||||
continue; // don't serialize default sounds
|
||||
|
||||
looped = ( channels[i].use_loop && channels[i].sfx->cache->loopStart != -1 );
|
||||
looped = ( channels[i].use_loop && FBitSet( channels[i].sfx->cache->flags, SOUND_LOOPED ));
|
||||
|
||||
if( channels[i].entchannel == CHAN_STATIC && looped && !Host_IsQuakeCompatible())
|
||||
continue; // never serialize static looped sounds. It will be restoring in game code
|
||||
@@ -1593,7 +1605,6 @@ void SND_UpdateSound( void )
|
||||
// release raw-channels that no longer used more than 10 secs
|
||||
S_FreeIdleRawChannels();
|
||||
|
||||
VectorCopy( cl.simvel, s_listener.velocity );
|
||||
s_listener.frametime = (cl.time - cl.oldtime);
|
||||
s_listener.waterlevel = cl.local.waterlevel;
|
||||
s_listener.active = CL_IsInGame();
|
||||
|
||||
@@ -17,30 +17,56 @@ GNU General Public License for more details.
|
||||
#include "sound.h"
|
||||
#include "client.h"
|
||||
|
||||
#define IPAINTBUFFER 0
|
||||
#define IROOMBUFFER 1
|
||||
#define ISTREAMBUFFER 2
|
||||
enum
|
||||
{
|
||||
IPAINTBUFFER = 0,
|
||||
IROOMBUFFER,
|
||||
ISTREAMBUFFER,
|
||||
CPAINTBUFFERS,
|
||||
};
|
||||
|
||||
#define FILTERTYPE_NONE 0
|
||||
#define FILTERTYPE_LINEAR 1
|
||||
#define FILTERTYPE_CUBIC 2
|
||||
enum
|
||||
{
|
||||
FILTERTYPE_NONE = 0,
|
||||
FILTERTYPE_LINEAR,
|
||||
FILTERTYPE_CUBIC,
|
||||
};
|
||||
|
||||
#define CCHANVOLUMES 2
|
||||
|
||||
#define SND_SCALE_BITS 7
|
||||
#define SND_SCALE_SHIFT (8 - SND_SCALE_BITS)
|
||||
#define SND_SCALE_LEVELS (1 << SND_SCALE_BITS)
|
||||
#define SND_SCALE_BITS 7
|
||||
#define SND_SCALE_SHIFT ( 8 - SND_SCALE_BITS )
|
||||
#define SND_SCALE_LEVELS ( 1 << SND_SCALE_BITS )
|
||||
|
||||
portable_samplepair_t *g_curpaintbuffer;
|
||||
portable_samplepair_t streambuffer[(PAINTBUFFER_SIZE+1)];
|
||||
portable_samplepair_t paintbuffer[(PAINTBUFFER_SIZE+1)];
|
||||
portable_samplepair_t roombuffer[(PAINTBUFFER_SIZE+1)];
|
||||
portable_samplepair_t facingbuffer[(PAINTBUFFER_SIZE+1)];
|
||||
portable_samplepair_t temppaintbuffer[(PAINTBUFFER_SIZE+1)];
|
||||
paintbuffer_t paintbuffers[CPAINTBUFFERS];
|
||||
// sound mixing buffer
|
||||
#define CPAINTFILTERMEM 3
|
||||
#define CPAINTFILTERS 4 // maximum number of consecutive upsample passes per paintbuffer
|
||||
|
||||
int snd_scaletable[SND_SCALE_LEVELS][256];
|
||||
// fixed point stuff for real-time resampling
|
||||
#define FIX_BITS 28
|
||||
#define FIX_SCALE ( 1 << FIX_BITS )
|
||||
#define FIX_MASK (( 1 << FIX_BITS ) - 1 )
|
||||
#define FIX_FLOAT( a ) ((int)(( a ) * FIX_SCALE ))
|
||||
#define FIX( a ) (((int)( a )) << FIX_BITS )
|
||||
#define FIX_INTPART( a ) (((int)( a )) >> FIX_BITS )
|
||||
#define FIX_FRACPART( a ) (( a ) & FIX_MASK )
|
||||
|
||||
typedef struct
|
||||
{
|
||||
qboolean factive; // if true, mix to this paintbuffer using flags
|
||||
portable_samplepair_t *pbuf; // front stereo mix buffer, for 2 or 4 channel mixing
|
||||
int ifilter; // current filter memory buffer to use for upsampling pass
|
||||
portable_samplepair_t fltmem[CPAINTFILTERS][CPAINTFILTERMEM];
|
||||
} paintbuffer_t;
|
||||
|
||||
static portable_samplepair_t *g_curpaintbuffer;
|
||||
static portable_samplepair_t streambuffer[(PAINTBUFFER_SIZE+1)];
|
||||
static portable_samplepair_t paintbuffer[(PAINTBUFFER_SIZE+1)];
|
||||
static portable_samplepair_t roombuffer[(PAINTBUFFER_SIZE+1)];
|
||||
static portable_samplepair_t temppaintbuffer[(PAINTBUFFER_SIZE+1)];
|
||||
static paintbuffer_t paintbuffers[CPAINTBUFFERS];
|
||||
|
||||
static int snd_scaletable[SND_SCALE_LEVELS][256];
|
||||
void S_InitScaletable( void )
|
||||
{
|
||||
int i, j;
|
||||
@@ -67,7 +93,7 @@ static void S_TransferPaintBuffer( int endtime )
|
||||
dword *pbuf;
|
||||
|
||||
pbuf = (dword *)dma.buffer;
|
||||
snd_p = (int *)PAINTBUFFER;
|
||||
snd_p = (int *)g_curpaintbuffer;
|
||||
lpaintedtime = paintedtime;
|
||||
sampleMask = ((dma.samples >> 1) - 1);
|
||||
|
||||
@@ -111,20 +137,20 @@ static void S_TransferPaintBuffer( int endtime )
|
||||
//===============================================================================
|
||||
// Activate a paintbuffer. All active paintbuffers are mixed in parallel within
|
||||
// MIX_MixChannelsToPaintbuffer, according to flags
|
||||
_inline void MIX_ActivatePaintbuffer( int ipaintbuffer )
|
||||
static void MIX_ActivatePaintbuffer( int ipaintbuffer )
|
||||
{
|
||||
Assert( ipaintbuffer < CPAINTBUFFERS );
|
||||
paintbuffers[ipaintbuffer].factive = true;
|
||||
}
|
||||
|
||||
_inline void MIX_SetCurrentPaintbuffer( int ipaintbuffer )
|
||||
static void MIX_SetCurrentPaintbuffer( int ipaintbuffer )
|
||||
{
|
||||
Assert( ipaintbuffer < CPAINTBUFFERS );
|
||||
g_curpaintbuffer = paintbuffers[ipaintbuffer].pbuf;
|
||||
Assert( g_curpaintbuffer != NULL );
|
||||
}
|
||||
|
||||
_inline int MIX_GetCurrentPaintbufferIndex( void )
|
||||
static int MIX_GetCurrentPaintbufferIndex( void )
|
||||
{
|
||||
int i;
|
||||
|
||||
@@ -136,7 +162,7 @@ _inline int MIX_GetCurrentPaintbufferIndex( void )
|
||||
return 0;
|
||||
}
|
||||
|
||||
_inline paintbuffer_t *MIX_GetCurrentPaintbufferPtr( void )
|
||||
static paintbuffer_t *MIX_GetCurrentPaintbufferPtr( void )
|
||||
{
|
||||
int ipaint = MIX_GetCurrentPaintbufferIndex();
|
||||
|
||||
@@ -145,7 +171,7 @@ _inline paintbuffer_t *MIX_GetCurrentPaintbufferPtr( void )
|
||||
}
|
||||
|
||||
// Don't mix into any paintbuffers
|
||||
_inline void MIX_DeactivateAllPaintbuffers( void )
|
||||
static void MIX_DeactivateAllPaintbuffers( void )
|
||||
{
|
||||
int i;
|
||||
|
||||
@@ -154,7 +180,7 @@ _inline void MIX_DeactivateAllPaintbuffers( void )
|
||||
}
|
||||
|
||||
// set upsampling filter indexes back to 0
|
||||
_inline void MIX_ResetPaintbufferFilterCounters( void )
|
||||
static void MIX_ResetPaintbufferFilterCounters( void )
|
||||
{
|
||||
int i;
|
||||
|
||||
@@ -163,13 +189,13 @@ _inline void MIX_ResetPaintbufferFilterCounters( void )
|
||||
}
|
||||
|
||||
// return pointer to front paintbuffer pbuf, given index
|
||||
_inline portable_samplepair_t *MIX_GetPFrontFromIPaint( int ipaintbuffer )
|
||||
static portable_samplepair_t *MIX_GetPFrontFromIPaint( int ipaintbuffer )
|
||||
{
|
||||
Assert( ipaintbuffer < CPAINTBUFFERS );
|
||||
return paintbuffers[ipaintbuffer].pbuf;
|
||||
}
|
||||
|
||||
_inline paintbuffer_t *MIX_GetPPaintFromIPaint( int ipaint )
|
||||
static paintbuffer_t *MIX_GetPPaintFromIPaint( int ipaint )
|
||||
{
|
||||
Assert( ipaint < CPAINTBUFFERS );
|
||||
return &paintbuffers[ipaint];
|
||||
@@ -523,6 +549,7 @@ static void MIX_MixChannelsToPaintbuffer( int endtime, int rate, int outputRate
|
||||
wavdata_t *pSource;
|
||||
int i, sampleCount;
|
||||
qboolean bZeroVolume;
|
||||
qboolean local = Host_IsLocalGame();
|
||||
|
||||
// mix each channel into paintbuffer
|
||||
ch = channels;
|
||||
@@ -549,7 +576,7 @@ static void MIX_MixChannelsToPaintbuffer( int endtime, int rate, int outputRate
|
||||
{
|
||||
// play, playvol
|
||||
}
|
||||
else if(( s_listener.inmenu || s_listener.paused ) && !ch->localsound )
|
||||
else if(( s_listener.inmenu || s_listener.paused ) && !ch->localsound && local )
|
||||
{
|
||||
// play only local sounds, keep pause for other
|
||||
continue;
|
||||
@@ -567,6 +594,7 @@ static void MIX_MixChannelsToPaintbuffer( int endtime, int rate, int outputRate
|
||||
|
||||
// Don't mix sound data for sounds with zero volume. If it's a non-looping sound,
|
||||
// just remove the sound when its volume goes to zero.
|
||||
|
||||
bZeroVolume = !ch->leftvol && !ch->rightvol;
|
||||
|
||||
if( !bZeroVolume )
|
||||
@@ -576,7 +604,7 @@ static void MIX_MixChannelsToPaintbuffer( int endtime, int rate, int outputRate
|
||||
bZeroVolume = true;
|
||||
}
|
||||
|
||||
if( !pSource || ( bZeroVolume && pSource->loopStart == -1 ))
|
||||
if( !pSource || ( bZeroVolume && !FBitSet( pSource->flags, SOUND_LOOPED )))
|
||||
{
|
||||
if( !pSource )
|
||||
{
|
||||
@@ -629,7 +657,7 @@ static void MIX_MixChannelsToPaintbuffer( int endtime, int rate, int outputRate
|
||||
}
|
||||
|
||||
// pass in index -1...count+2, return pointer to source sample in either paintbuffer or delay buffer
|
||||
_inline portable_samplepair_t *S_GetNextpFilter( int i, portable_samplepair_t *pbuffer, portable_samplepair_t *pfiltermem )
|
||||
static portable_samplepair_t *S_GetNextpFilter( int i, portable_samplepair_t *pbuffer, portable_samplepair_t *pfiltermem )
|
||||
{
|
||||
// The delay buffer is assumed to precede the paintbuffer by 6 duplicated samples
|
||||
if( i == -1 ) return (&(pfiltermem[0]));
|
||||
@@ -838,6 +866,14 @@ static void MIX_MixPaintbuffers( int ibuf1, int ibuf2, int ibuf3, int count, flo
|
||||
pbuf2 = paintbuffers[ibuf2].pbuf;
|
||||
pbuf3 = paintbuffers[ibuf3].pbuf;
|
||||
|
||||
if( !gain )
|
||||
{
|
||||
// do not mix buf2 into buf3, just copy
|
||||
if( pbuf1 != pbuf3 )
|
||||
memcpy( pbuf3, pbuf1, sizeof( *pbuf1 ) * count );
|
||||
return;
|
||||
}
|
||||
|
||||
// destination buffer stereo - average n chans down to stereo
|
||||
|
||||
// destination 2ch:
|
||||
|
||||
@@ -142,7 +142,7 @@ S_StreamGetCurrentState
|
||||
save\restore code
|
||||
=================
|
||||
*/
|
||||
qboolean S_StreamGetCurrentState( char *currentTrack, char *loopTrack, int *position )
|
||||
qboolean S_StreamGetCurrentState( char *currentTrack, size_t currentTrackSize, char *loopTrack, size_t loopTrackSize, int *position )
|
||||
{
|
||||
if( !s_bgTrack.stream )
|
||||
return false; // not active
|
||||
@@ -150,15 +150,15 @@ qboolean S_StreamGetCurrentState( char *currentTrack, char *loopTrack, int *posi
|
||||
if( currentTrack )
|
||||
{
|
||||
if( s_bgTrack.current[0] )
|
||||
Q_strncpy( currentTrack, s_bgTrack.current, MAX_STRING );
|
||||
else Q_strncpy( currentTrack, "*", MAX_STRING ); // no track
|
||||
Q_strncpy( currentTrack, s_bgTrack.current, currentTrackSize );
|
||||
else Q_strncpy( currentTrack, "*", currentTrackSize ); // no track
|
||||
}
|
||||
|
||||
if( loopTrack )
|
||||
{
|
||||
if( s_bgTrack.loopName[0] )
|
||||
Q_strncpy( loopTrack, s_bgTrack.loopName, MAX_STRING );
|
||||
else Q_strncpy( loopTrack, "*", MAX_STRING ); // no track
|
||||
Q_strncpy( loopTrack, s_bgTrack.loopName, loopTrackSize );
|
||||
else Q_strncpy( loopTrack, "*", loopTrackSize ); // no track
|
||||
}
|
||||
|
||||
if( position )
|
||||
|
||||
@@ -27,7 +27,7 @@ int S_ConvertLoopedPosition( wavdata_t *pSource, int samplePosition, qboolean us
|
||||
// convert to a position within the loop
|
||||
// At the end of the loop, we return a short buffer, and subsequent call
|
||||
// will loop back and get the rest of the buffer
|
||||
if( pSource->loopStart >= 0 && samplePosition >= pSource->samples && use_loop )
|
||||
if( FBitSet( pSource->flags, SOUND_LOOPED ) && samplePosition >= pSource->samples && use_loop )
|
||||
{
|
||||
// size of loop
|
||||
int loopSize = pSource->samples - pSource->loopStart;
|
||||
|
||||
@@ -20,208 +20,179 @@ extern poolhandle_t sndpool;
|
||||
|
||||
#include "xash3d_mathlib.h"
|
||||
|
||||
// sound engine rate defines
|
||||
#define SOUND_DMA_SPEED 44100 // hardware playback rate
|
||||
#define SOUND_11k 11025 // 11khz sample rate
|
||||
#define SOUND_16k 16000 // 16khz sample rate
|
||||
#define SOUND_22k 22050 // 22khz sample rate
|
||||
#define SOUND_32k 32000 // 32khz sample rate
|
||||
#define SOUND_44k 44100 // 44khz sample rate
|
||||
#define DMA_MSEC_PER_SAMPLE ((float)(1000.0 / SOUND_DMA_SPEED))
|
||||
#define XASH_AUDIO_CD_QUALITY 1 // some platforms might need this
|
||||
|
||||
// fixed point stuff for real-time resampling
|
||||
#define FIX_BITS 28
|
||||
#define FIX_SCALE (1 << FIX_BITS)
|
||||
#define FIX_MASK ((1 << FIX_BITS)-1)
|
||||
#define FIX_FLOAT(a) ((int)((a) * FIX_SCALE))
|
||||
#define FIX(a) (((int)(a)) << FIX_BITS)
|
||||
#define FIX_INTPART(a) (((int)(a)) >> FIX_BITS)
|
||||
#define FIX_FRACTION(a,b) (FIX(a)/(b))
|
||||
#define FIX_FRACPART(a) ((a) & FIX_MASK)
|
||||
// sound engine rate defines
|
||||
#if XASH_AUDIO_CD_QUALITY
|
||||
#define SOUND_11k 11025 // 11khz sample rate
|
||||
#define SOUND_22k 22050 // 22khz sample rate
|
||||
#define SOUND_44k 44100 // 44khz sample rate
|
||||
#else // XASH_AUDIO_CD_QUALITY
|
||||
#define SOUND_11k 12000 // 11khz sample rate
|
||||
#define SOUND_22k 24000 // 22khz sample rate
|
||||
#define SOUND_44k 48000 // 44khz sample rate
|
||||
#endif // XASH_AUDIO_CD_QUALITY
|
||||
|
||||
#define SOUND_DMA_SPEED SOUND_44k // hardware playback rate
|
||||
|
||||
// NOTE: clipped sound at 32760 to avoid overload
|
||||
#define CLIP( x ) (( x ) > 32760 ? 32760 : (( x ) < -32760 ? -32760 : ( x )))
|
||||
#define CLIP( x ) (( x ) > 32760 ? 32760 : (( x ) < -32760 ? -32760 : ( x )))
|
||||
|
||||
#define PAINTBUFFER_SIZE 1024 // 44k: was 512
|
||||
#define PAINTBUFFER (g_curpaintbuffer)
|
||||
#define CPAINTBUFFERS 3
|
||||
#define PAINTBUFFER_SIZE 1024 // 44k: was 512
|
||||
|
||||
// sound mixing buffer
|
||||
#define CPAINTFILTERMEM 3
|
||||
#define CPAINTFILTERS 4 // maximum number of consecutive upsample passes per paintbuffer
|
||||
|
||||
#define S_RAW_SOUND_IDLE_SEC 10 // time interval for idling raw sound before it's freed
|
||||
#define S_RAW_SOUND_BACKGROUNDTRACK -2
|
||||
#define S_RAW_SOUND_SOUNDTRACK -1
|
||||
#define S_RAW_SAMPLES_PRECISION_BITS 14
|
||||
|
||||
#define CIN_FRAMETIME (1.0f / 30.0f)
|
||||
#define S_RAW_SOUND_IDLE_SEC 10 // time interval for idling raw sound before it's freed
|
||||
#define S_RAW_SOUND_BACKGROUNDTRACK -2
|
||||
#define S_RAW_SOUND_SOUNDTRACK -1
|
||||
#define S_RAW_SAMPLES_PRECISION_BITS 14
|
||||
|
||||
typedef struct
|
||||
{
|
||||
int left;
|
||||
int right;
|
||||
int left;
|
||||
int right;
|
||||
} portable_samplepair_t;
|
||||
|
||||
typedef struct
|
||||
{
|
||||
qboolean factive; // if true, mix to this paintbuffer using flags
|
||||
portable_samplepair_t *pbuf; // front stereo mix buffer, for 2 or 4 channel mixing
|
||||
int ifilter; // current filter memory buffer to use for upsampling pass
|
||||
portable_samplepair_t fltmem[CPAINTFILTERS][CPAINTFILTERMEM];
|
||||
} paintbuffer_t;
|
||||
|
||||
typedef struct sfx_s
|
||||
{
|
||||
char name[MAX_QPATH];
|
||||
wavdata_t *cache;
|
||||
char name[MAX_QPATH];
|
||||
wavdata_t *cache;
|
||||
|
||||
int servercount;
|
||||
uint hashValue;
|
||||
struct sfx_s *hashNext;
|
||||
int servercount;
|
||||
uint hashValue;
|
||||
struct sfx_s *hashNext;
|
||||
} sfx_t;
|
||||
|
||||
extern portable_samplepair_t paintbuffer[];
|
||||
extern portable_samplepair_t roombuffer[];
|
||||
extern portable_samplepair_t temppaintbuffer[];
|
||||
extern portable_samplepair_t *g_curpaintbuffer;
|
||||
extern paintbuffer_t paintbuffers[];
|
||||
|
||||
// structure used for fading in and out client sound volume.
|
||||
typedef struct
|
||||
{
|
||||
float initial_percent;
|
||||
float percent; // how far to adjust client's volume down by.
|
||||
float starttime; // GetHostTime() when we started adjusting volume
|
||||
float fadeouttime; // # of seconds to get to faded out state
|
||||
float holdtime; // # of seconds to hold
|
||||
float fadeintime; // # of seconds to restore
|
||||
float initial_percent;
|
||||
float percent; // how far to adjust client's volume down by.
|
||||
float starttime; // GetHostTime() when we started adjusting volume
|
||||
float fadeouttime; // # of seconds to get to faded out state
|
||||
float holdtime; // # of seconds to hold
|
||||
float fadeintime; // # of seconds to restore
|
||||
} soundfade_t;
|
||||
|
||||
typedef struct
|
||||
{
|
||||
float percent;
|
||||
float percent;
|
||||
} musicfade_t;
|
||||
|
||||
typedef struct snd_format_s
|
||||
{
|
||||
unsigned int speed;
|
||||
unsigned int width;
|
||||
unsigned int channels;
|
||||
uint speed;
|
||||
byte width;
|
||||
byte channels;
|
||||
} snd_format_t;
|
||||
|
||||
typedef struct
|
||||
{
|
||||
snd_format_t format;
|
||||
int samples; // mono samples in buffer
|
||||
int samplepos; // in mono samples
|
||||
byte *buffer;
|
||||
qboolean initialized; // sound engine is active
|
||||
const char *backendName;
|
||||
snd_format_t format;
|
||||
int samples; // mono samples in buffer
|
||||
int samplepos; // in mono samples
|
||||
qboolean initialized; // sound engine is active
|
||||
byte *buffer;
|
||||
const char *backendName;
|
||||
} dma_t;
|
||||
|
||||
#include "vox.h"
|
||||
|
||||
typedef struct
|
||||
{
|
||||
double sample;
|
||||
|
||||
wavdata_t *pData;
|
||||
double forcedEndSample;
|
||||
qboolean finished;
|
||||
double sample;
|
||||
wavdata_t *pData;
|
||||
double forcedEndSample;
|
||||
qboolean finished;
|
||||
} mixer_t;
|
||||
|
||||
typedef struct rawchan_s
|
||||
{
|
||||
int entnum;
|
||||
int master_vol;
|
||||
int leftvol; // 0-255 left volume
|
||||
int rightvol; // 0-255 right volume
|
||||
float dist_mult; // distance multiplier (attenuation/clipK)
|
||||
vec3_t origin; // only use if fixed_origin is set
|
||||
volatile uint s_rawend;
|
||||
wavdata_t sound_info; // advance play position
|
||||
float oldtime; // catch time jumps
|
||||
size_t max_samples; // buffer length
|
||||
portable_samplepair_t rawsamples[1]; // variable sized
|
||||
int entnum;
|
||||
int master_vol;
|
||||
int leftvol; // 0-255 left volume
|
||||
int rightvol; // 0-255 right volume
|
||||
float dist_mult; // distance multiplier (attenuation/clipK)
|
||||
vec3_t origin; // only use if fixed_origin is set
|
||||
volatile uint s_rawend;
|
||||
float oldtime; // catch time jumps
|
||||
wavdata_t sound_info; // advance play position
|
||||
size_t max_samples; // buffer length
|
||||
portable_samplepair_t rawsamples[1]; // variable sized
|
||||
} rawchan_t;
|
||||
|
||||
typedef struct channel_s
|
||||
{
|
||||
char name[16]; // keept sentence name
|
||||
sfx_t *sfx; // sfx number
|
||||
char name[16]; // keep sentence name
|
||||
sfx_t *sfx; // sfx number
|
||||
|
||||
int leftvol; // 0-255 left volume
|
||||
int rightvol; // 0-255 right volume
|
||||
int leftvol; // 0-255 left volume
|
||||
int rightvol; // 0-255 right volume
|
||||
|
||||
int entnum; // entity soundsource
|
||||
int entchannel; // sound channel (CHAN_STREAM, CHAN_VOICE, etc.)
|
||||
vec3_t origin; // only use if fixed_origin is set
|
||||
float dist_mult; // distance multiplier (attenuation/clipK)
|
||||
int master_vol; // 0-255 master volume
|
||||
qboolean isSentence; // bit who indicated sentence
|
||||
int basePitch; // base pitch percent (100% is normal pitch playback)
|
||||
float pitch; // real-time pitch after any modulation or shift by dynamic data
|
||||
qboolean use_loop; // don't loop default and local sounds
|
||||
qboolean staticsound; // use origin instead of fetching entnum's origin
|
||||
qboolean localsound; // it's a local menu sound (not looped, not paused)
|
||||
mixer_t pMixer;
|
||||
int entnum; // entity soundsource
|
||||
int entchannel; // sound channel (CHAN_STREAM, CHAN_VOICE, etc.)
|
||||
vec3_t origin; // only use if fixed_origin is set
|
||||
float dist_mult; // distance multiplier (attenuation/clipK)
|
||||
int master_vol; // 0-255 master volume
|
||||
int basePitch; // base pitch percent (100% is normal pitch playback)
|
||||
float pitch; // real-time pitch after any modulation or shift by dynamic data
|
||||
qboolean use_loop; // don't loop default and local sounds
|
||||
qboolean staticsound; // use origin instead of fetching entnum's origin
|
||||
qboolean localsound; // it's a local menu sound (not looped, not paused)
|
||||
mixer_t pMixer;
|
||||
|
||||
// sentence mixer
|
||||
int wordIndex;
|
||||
mixer_t *currentWord; // NULL if sentence is finished
|
||||
voxword_t words[CVOXWORDMAX];
|
||||
qboolean isSentence; // bit indicating sentence
|
||||
int wordIndex;
|
||||
mixer_t *currentWord; // NULL if sentence is finished
|
||||
voxword_t words[CVOXWORDMAX];
|
||||
} channel_t;
|
||||
|
||||
typedef struct
|
||||
{
|
||||
vec3_t origin; // simorg + view_ofs
|
||||
vec3_t velocity;
|
||||
vec3_t forward;
|
||||
vec3_t right;
|
||||
vec3_t up;
|
||||
vec3_t origin; // simorg + view_ofs
|
||||
vec3_t forward;
|
||||
vec3_t right;
|
||||
vec3_t up;
|
||||
|
||||
int entnum;
|
||||
int waterlevel;
|
||||
float frametime; // used for sound fade
|
||||
qboolean active;
|
||||
qboolean inmenu; // listener in-menu ?
|
||||
qboolean paused;
|
||||
qboolean streaming; // playing AVI-file
|
||||
qboolean stream_paused; // pause only background track
|
||||
int entnum;
|
||||
int waterlevel;
|
||||
float frametime; // used for sound fade
|
||||
qboolean active;
|
||||
qboolean inmenu; // listener in-menu ?
|
||||
qboolean paused;
|
||||
qboolean streaming; // playing AVI-file
|
||||
qboolean stream_paused; // pause only background track
|
||||
} listener_t;
|
||||
|
||||
typedef struct
|
||||
{
|
||||
string current; // a currently playing track
|
||||
string loopName; // may be empty
|
||||
stream_t *stream;
|
||||
int source; // may be game, menu, etc
|
||||
string current; // a currently playing track
|
||||
string loopName; // may be empty
|
||||
stream_t *stream;
|
||||
int source; // may be game, menu, etc
|
||||
} bg_track_t;
|
||||
|
||||
//====================================================================
|
||||
|
||||
#define MAX_DYNAMIC_CHANNELS (60 + NUM_AMBIENTS)
|
||||
#define MAX_CHANNELS (256 + MAX_DYNAMIC_CHANNELS) // Scourge Of Armagon has too many static sounds on hip2m4.bsp
|
||||
#define MAX_RAW_CHANNELS 48
|
||||
#define MAX_RAW_SAMPLES 8192
|
||||
#define MAX_DYNAMIC_CHANNELS (60 + NUM_AMBIENTS)
|
||||
#define MAX_CHANNELS (256 + MAX_DYNAMIC_CHANNELS) // Scourge Of Armagon has too many static sounds on hip2m4.bsp
|
||||
#define MAX_RAW_CHANNELS 48
|
||||
#define MAX_RAW_SAMPLES 8192
|
||||
|
||||
extern sound_t ambient_sfx[NUM_AMBIENTS];
|
||||
extern qboolean snd_ambient;
|
||||
extern channel_t channels[MAX_CHANNELS];
|
||||
extern rawchan_t *raw_channels[MAX_RAW_CHANNELS];
|
||||
extern int total_channels;
|
||||
extern int paintedtime;
|
||||
extern int soundtime;
|
||||
extern listener_t s_listener;
|
||||
extern int idsp_room;
|
||||
extern dma_t dma;
|
||||
extern sound_t ambient_sfx[NUM_AMBIENTS];
|
||||
extern qboolean snd_ambient;
|
||||
extern channel_t channels[MAX_CHANNELS];
|
||||
extern rawchan_t *raw_channels[MAX_RAW_CHANNELS];
|
||||
extern int total_channels;
|
||||
extern int paintedtime;
|
||||
extern int soundtime;
|
||||
extern listener_t s_listener;
|
||||
extern int idsp_room;
|
||||
extern dma_t dma;
|
||||
|
||||
extern convar_t s_musicvolume;
|
||||
extern convar_t s_lerping;
|
||||
extern convar_t s_test; // cvar to testify new effects
|
||||
extern convar_t s_musicvolume;
|
||||
extern convar_t s_lerping;
|
||||
extern convar_t s_test; // cvar to test new effects
|
||||
extern convar_t s_samplecount;
|
||||
extern convar_t snd_mute_losefocus;
|
||||
extern convar_t s_warn_late_precache;
|
||||
|
||||
void S_InitScaletable( void );
|
||||
@@ -293,7 +264,6 @@ void SND_ForceCloseMouth( int entnum );
|
||||
//
|
||||
void S_StreamSoundTrack( void );
|
||||
void S_StreamBackgroundTrack( void );
|
||||
qboolean S_StreamGetCurrentState( char *currentTrack, char *loopTrack, int *position );
|
||||
void S_PrintBackgroundTrackState( void );
|
||||
void S_FadeMusicVolume( float fadePercent );
|
||||
|
||||
|
||||
@@ -20,8 +20,6 @@ GNU General Public License for more details.
|
||||
#include "vid_common.h"
|
||||
#include "platform/platform.h"
|
||||
|
||||
static CVAR_DEFINE( window_width, "width", "0", FCVAR_RENDERINFO|FCVAR_VIDRESTART, "screen width" );
|
||||
static CVAR_DEFINE( window_height, "height", "0", FCVAR_RENDERINFO|FCVAR_VIDRESTART, "screen height" );
|
||||
static CVAR_DEFINE_AUTO( vid_mode, "0", FCVAR_RENDERINFO, "current video mode index (used only for storage)" );
|
||||
static CVAR_DEFINE_AUTO( vid_rotate, "0", FCVAR_RENDERINFO|FCVAR_VIDRESTART, "screen rotation (0-3)" );
|
||||
static CVAR_DEFINE_AUTO( vid_scale, "1.0", FCVAR_RENDERINFO|FCVAR_VIDRESTART, "pixel scale" );
|
||||
@@ -29,6 +27,8 @@ static CVAR_DEFINE_AUTO( vid_scale, "1.0", FCVAR_RENDERINFO|FCVAR_VIDRESTART, "p
|
||||
CVAR_DEFINE_AUTO( vid_highdpi, "1", FCVAR_RENDERINFO|FCVAR_VIDRESTART, "enable High-DPI mode" );
|
||||
CVAR_DEFINE_AUTO( vid_maximized, "0", FCVAR_RENDERINFO, "window maximized state, read-only" );
|
||||
CVAR_DEFINE( vid_fullscreen, "fullscreen", "0", FCVAR_RENDERINFO|FCVAR_VIDRESTART, "fullscreen state (0 windowed, 1 fullscreen, 2 borderless)" );
|
||||
CVAR_DEFINE( window_width, "width", "0", FCVAR_RENDERINFO|FCVAR_VIDRESTART, "screen width" );
|
||||
CVAR_DEFINE( window_height, "height", "0", FCVAR_RENDERINFO|FCVAR_VIDRESTART, "screen height" );
|
||||
CVAR_DEFINE( window_xpos, "_window_xpos", "-1", FCVAR_RENDERINFO, "window position by horizontal" );
|
||||
CVAR_DEFINE( window_ypos, "_window_ypos", "-1", FCVAR_RENDERINFO, "window position by vertical" );
|
||||
|
||||
|
||||
@@ -38,6 +38,8 @@ extern glwstate_t glw_state;
|
||||
extern convar_t vid_fullscreen;
|
||||
extern convar_t vid_maximized;
|
||||
extern convar_t vid_highdpi;
|
||||
extern convar_t window_width;
|
||||
extern convar_t window_height;
|
||||
extern convar_t window_xpos;
|
||||
extern convar_t window_ypos;
|
||||
extern convar_t gl_msaa_samples;
|
||||
|
||||
@@ -25,11 +25,12 @@ voice_state_t voice = { 0 };
|
||||
CVAR_DEFINE_AUTO( voice_enable, "1", FCVAR_PRIVILEGED|FCVAR_ARCHIVE, "enable voice chat" );
|
||||
CVAR_DEFINE_AUTO( voice_loopback, "0", FCVAR_PRIVILEGED, "loopback voice back to the speaker" );
|
||||
CVAR_DEFINE_AUTO( voice_scale, "1.0", FCVAR_PRIVILEGED|FCVAR_ARCHIVE, "incoming voice volume scale" );
|
||||
CVAR_DEFINE_AUTO( voice_transmit_scale, "1.0", FCVAR_PRIVILEGED|FCVAR_ARCHIVE, "outcoming voice volume scale" );
|
||||
CVAR_DEFINE_AUTO( voice_avggain, "0.5", FCVAR_PRIVILEGED|FCVAR_ARCHIVE, "automatic voice gain control (average)" );
|
||||
CVAR_DEFINE_AUTO( voice_maxgain, "5.0", FCVAR_PRIVILEGED|FCVAR_ARCHIVE, "automatic voice gain control (maximum)" );
|
||||
CVAR_DEFINE_AUTO( voice_inputfromfile, "0", FCVAR_PRIVILEGED, "input voice from voice_input.wav" );
|
||||
|
||||
static void Voice_ApplyGainAdjust( int16_t *samples, int count );
|
||||
static void Voice_ApplyGainAdjust( int16_t *samples, int count, float scale );
|
||||
|
||||
/*
|
||||
===============================================================================
|
||||
@@ -39,6 +40,25 @@ static void Voice_ApplyGainAdjust( int16_t *samples, int count );
|
||||
===============================================================================
|
||||
*/
|
||||
|
||||
static qboolean Voice_InitCustomMode( void )
|
||||
{
|
||||
int err = 0;
|
||||
|
||||
voice.width = sizeof( opus_int16 );
|
||||
voice.samplerate = VOICE_OPUS_CUSTOM_SAMPLERATE;
|
||||
voice.frame_size = VOICE_OPUS_CUSTOM_FRAME_SIZE;
|
||||
|
||||
voice.custom_mode = opus_custom_mode_create( SOUND_44k, voice.frame_size, &err );
|
||||
|
||||
if( !voice.custom_mode )
|
||||
{
|
||||
Con_Printf( S_ERROR "Can't create Opus Custom mode: %s\n", opus_strerror( err ));
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/*
|
||||
=========================
|
||||
Voice_InitOpusDecoder
|
||||
@@ -47,24 +67,17 @@ Voice_InitOpusDecoder
|
||||
*/
|
||||
static qboolean Voice_InitOpusDecoder( void )
|
||||
{
|
||||
int err;
|
||||
int err = 0;
|
||||
|
||||
voice.width = sizeof( opus_int16 );
|
||||
voice.samplerate = VOICE_OPUS_CUSTOM_SAMPLERATE;
|
||||
voice.frame_size = VOICE_OPUS_CUSTOM_FRAME_SIZE;
|
||||
|
||||
voice.custom_mode = opus_custom_mode_create( SOUND_44k, voice.frame_size, &err );
|
||||
if( !voice.custom_mode )
|
||||
for( int i = 0; i < cl.maxclients; i++ )
|
||||
{
|
||||
Con_Printf( S_ERROR "Can't create Opus Custom mode: %s\n", opus_strerror( err ));
|
||||
return false;
|
||||
}
|
||||
voice.decoders[i] = opus_custom_decoder_create( voice.custom_mode, VOICE_PCM_CHANNELS, &err );
|
||||
|
||||
voice.decoder = opus_custom_decoder_create( voice.custom_mode, VOICE_PCM_CHANNELS, &err );
|
||||
if( !voice.decoder )
|
||||
{
|
||||
Con_Printf( S_ERROR "Can't create Opus encoder: %s\n", opus_strerror( err ));
|
||||
return false;
|
||||
if( !voice.decoders[i] )
|
||||
{
|
||||
Con_Printf( S_ERROR "Can't create Opus decoder for %i: %s\n", i, opus_strerror( err ));
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
@@ -78,7 +91,7 @@ Voice_InitOpusEncoder
|
||||
*/
|
||||
static qboolean Voice_InitOpusEncoder( int quality )
|
||||
{
|
||||
int err;
|
||||
int err = 0;
|
||||
|
||||
voice.encoder = opus_custom_encoder_create( voice.custom_mode, VOICE_PCM_CHANNELS, &err );
|
||||
if( !voice.encoder )
|
||||
@@ -117,10 +130,13 @@ Voice_ShutdownOpusDecoder
|
||||
*/
|
||||
static void Voice_ShutdownOpusDecoder( void )
|
||||
{
|
||||
if( voice.decoder )
|
||||
for( int i = 0; i < MAX_CLIENTS; i++ )
|
||||
{
|
||||
opus_custom_decoder_destroy( voice.decoder );
|
||||
voice.decoder = NULL;
|
||||
if( !voice.decoders[i] )
|
||||
continue;
|
||||
|
||||
opus_custom_decoder_destroy( voice.decoders[i] );
|
||||
voice.decoders[i] = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -137,7 +153,10 @@ static void Voice_ShutdownOpusEncoder( void )
|
||||
opus_custom_encoder_destroy( voice.encoder );
|
||||
voice.encoder = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
static void Voice_ShutdownCustomMode( void )
|
||||
{
|
||||
if( voice.custom_mode )
|
||||
{
|
||||
opus_custom_mode_destroy( voice.custom_mode );
|
||||
@@ -184,7 +203,7 @@ static uint Voice_GetOpusCompressedData( byte *out, uint maxsize, uint *frames )
|
||||
if( !voice.input_file )
|
||||
{
|
||||
// adjust gain before encoding, but only for input from voice
|
||||
Voice_ApplyGainAdjust((opus_int16*)(voice.input_buffer + ofs), voice.frame_size);
|
||||
Voice_ApplyGainAdjust((opus_int16*)(voice.input_buffer + ofs), voice.frame_size, voice_transmit_scale.value);
|
||||
}
|
||||
#endif
|
||||
|
||||
@@ -238,10 +257,10 @@ Voice_ApplyGainAdjust
|
||||
|
||||
=========================
|
||||
*/
|
||||
static void Voice_ApplyGainAdjust( int16_t *samples, int count )
|
||||
static void Voice_ApplyGainAdjust( int16_t *samples, int count, float scale )
|
||||
{
|
||||
float gain, modifiedMax;
|
||||
int average, adjustedSample, blockOffset = 0;
|
||||
int average, blockOffset = 0;
|
||||
|
||||
for( ;; )
|
||||
{
|
||||
@@ -260,10 +279,8 @@ static void Voice_ApplyGainAdjust( int16_t *samples, int count )
|
||||
localMax = absSample;
|
||||
|
||||
localSum += absSample;
|
||||
|
||||
gain = voice.autogain.current_gain + i * voice.autogain.gain_multiplier;
|
||||
adjustedSample = Q_min( SHRT_MAX, Q_max(( int )( sample * gain ), SHRT_MIN ));
|
||||
samples[blockOffset + i] = adjustedSample;
|
||||
samples[blockOffset + i] = bound( SHRT_MIN, (int)( sample * gain ), SHRT_MAX );
|
||||
}
|
||||
|
||||
if( blockOffset % voice.autogain.block_size == 0 )
|
||||
@@ -271,9 +288,9 @@ static void Voice_ApplyGainAdjust( int16_t *samples, int count )
|
||||
average = localSum / blockSize;
|
||||
modifiedMax = average + ( localMax - average ) * voice_avggain.value;
|
||||
|
||||
voice.autogain.current_gain = voice.autogain.next_gain * voice_scale.value;
|
||||
voice.autogain.next_gain = Q_min( (float)SHRT_MAX / modifiedMax, voice_maxgain.value ) * voice_scale.value;
|
||||
voice.autogain.gain_multiplier = ( voice.autogain.next_gain - voice.autogain.current_gain ) / ( voice.autogain.block_size - 1 );
|
||||
voice.autogain.current_gain = voice.autogain.next_gain * scale;
|
||||
voice.autogain.next_gain = Q_min( (float)SHRT_MAX / modifiedMax, voice_maxgain.value ) * scale;
|
||||
voice.autogain.gain_multiplier = ( voice.autogain.next_gain - voice.autogain.current_gain ) / ( blockSize - 1 );
|
||||
}
|
||||
blockOffset += blockSize;
|
||||
}
|
||||
@@ -443,7 +460,7 @@ Feed the decoded data to engine sound subsystem
|
||||
static void Voice_StartChannel( uint samples, byte *data, int entnum )
|
||||
{
|
||||
SND_ForceInitMouth( entnum );
|
||||
S_RawEntSamples( entnum, samples, voice.samplerate, voice.width, VOICE_PCM_CHANNELS, data, 255 );
|
||||
S_RawEntSamples( entnum, samples, voice.samplerate, voice.width, VOICE_PCM_CHANNELS, data, bound( 0, 255 * voice_scale.value, 255 ));
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -455,10 +472,11 @@ Received encoded voice data, decode it
|
||||
*/
|
||||
void Voice_AddIncomingData( int ent, const byte *data, uint size, uint frames )
|
||||
{
|
||||
const int playernum = ent - 1;
|
||||
int samples = 0;
|
||||
int ofs = 0;
|
||||
|
||||
if( !voice.decoder )
|
||||
if( playernum < 0 || playernum >= cl.maxclients || !voice.decoders[playernum] )
|
||||
return;
|
||||
|
||||
// decode frame by frame
|
||||
@@ -478,7 +496,7 @@ void Voice_AddIncomingData( int ent, const byte *data, uint size, uint frames )
|
||||
if( ofs + compressed_size > size )
|
||||
break;
|
||||
|
||||
frame_samples = opus_custom_decode( voice.decoder, data + ofs, compressed_size,
|
||||
frame_samples = opus_custom_decode( voice.decoders[playernum], data + ofs, compressed_size,
|
||||
(opus_int16*)voice.decompress_buffer + samples, voice.frame_size );
|
||||
|
||||
ofs += compressed_size;
|
||||
@@ -502,8 +520,8 @@ void CL_AddVoiceToDatagram( void )
|
||||
|
||||
if( cls.state != ca_active || !Voice_IsRecording() || !voice.encoder )
|
||||
return;
|
||||
|
||||
size = Voice_GetOpusCompressedData( voice.output_buffer, sizeof( voice.output_buffer ), &frames );
|
||||
|
||||
size = Voice_GetOpusCompressedData( voice.compress_buffer, sizeof( voice.compress_buffer ), &frames );
|
||||
|
||||
if( size > 0 && MSG_GetNumBytesLeft( &cls.datagram ) >= size + 32 )
|
||||
{
|
||||
@@ -511,7 +529,7 @@ void CL_AddVoiceToDatagram( void )
|
||||
MSG_WriteByte( &cls.datagram, voice_loopback.value != 0 );
|
||||
MSG_WriteByte( &cls.datagram, frames );
|
||||
MSG_WriteShort( &cls.datagram, size );
|
||||
MSG_WriteBytes( &cls.datagram, voice.output_buffer, size );
|
||||
MSG_WriteBytes( &cls.datagram, voice.compress_buffer, size );
|
||||
}
|
||||
}
|
||||
|
||||
@@ -527,6 +545,7 @@ void Voice_RegisterCvars( void )
|
||||
Cvar_RegisterVariable( &voice_enable );
|
||||
Cvar_RegisterVariable( &voice_loopback );
|
||||
Cvar_RegisterVariable( &voice_scale );
|
||||
Cvar_RegisterVariable( &voice_transmit_scale );
|
||||
Cvar_RegisterVariable( &voice_avggain );
|
||||
Cvar_RegisterVariable( &voice_maxgain );
|
||||
Cvar_RegisterVariable( &voice_inputfromfile );
|
||||
@@ -544,8 +563,9 @@ static void Voice_Shutdown( void )
|
||||
int i;
|
||||
|
||||
Voice_RecordStop();
|
||||
Voice_ShutdownOpusEncoder();
|
||||
Voice_ShutdownOpusDecoder();
|
||||
Voice_ShutdownOpusEncoder();
|
||||
Voice_ShutdownCustomMode();
|
||||
VoiceCapture_Shutdown();
|
||||
|
||||
if( voice.local.talking_ack )
|
||||
@@ -609,11 +629,10 @@ qboolean Voice_Init( const char *pszCodecName, int quality, qboolean preinit )
|
||||
|
||||
voice.autogain.block_size = 128;
|
||||
|
||||
if( !Voice_InitOpusDecoder( ))
|
||||
if( !Voice_InitCustomMode( ))
|
||||
{
|
||||
// no reason to init encoder and open audio device
|
||||
// if we can't hear other players
|
||||
Con_Printf( S_ERROR "Voice chat disabled.\n" );
|
||||
Voice_Shutdown();
|
||||
return false;
|
||||
}
|
||||
@@ -633,6 +652,16 @@ qboolean Voice_Init( const char *pszCodecName, int quality, qboolean preinit )
|
||||
|
||||
if( !preinit )
|
||||
{
|
||||
Voice_ShutdownOpusDecoder();
|
||||
if( !Voice_InitOpusDecoder())
|
||||
{
|
||||
// no reason to init encoder and open audio device
|
||||
// if we can't hear other players
|
||||
Con_Printf( S_ERROR "Can't create decoders, voice chat is disabled.\n" );
|
||||
Voice_Shutdown();
|
||||
return false;
|
||||
}
|
||||
|
||||
voice.device_opened = VoiceCapture_Init();
|
||||
|
||||
if( !voice.device_opened )
|
||||
|
||||
@@ -31,7 +31,7 @@ typedef struct OpusCustomMode OpusCustomMode;
|
||||
#define VOICE_PCM_CHANNELS 1 // always mono
|
||||
|
||||
// never change these parameters when using opuscustom
|
||||
#define VOICE_OPUS_CUSTOM_SAMPLERATE SOUND_44k
|
||||
#define VOICE_OPUS_CUSTOM_SAMPLERATE 44100
|
||||
// must follow opus custom requirements
|
||||
// also be divisible with MAX_RAW_SAMPLES
|
||||
#define VOICE_OPUS_CUSTOM_FRAME_SIZE 1024
|
||||
@@ -62,7 +62,7 @@ typedef struct voice_state_s
|
||||
// opus stuff
|
||||
OpusCustomMode *custom_mode;
|
||||
OpusCustomEncoder *encoder;
|
||||
OpusCustomDecoder *decoder;
|
||||
OpusCustomDecoder *decoders[MAX_CLIENTS];
|
||||
|
||||
// audio info
|
||||
uint width;
|
||||
@@ -71,7 +71,7 @@ typedef struct voice_state_s
|
||||
|
||||
// buffers
|
||||
byte input_buffer[MAX_RAW_SAMPLES];
|
||||
byte output_buffer[MAX_RAW_SAMPLES];
|
||||
byte compress_buffer[MAX_RAW_SAMPLES];
|
||||
byte decompress_buffer[MAX_RAW_SAMPLES];
|
||||
fs_offset_t input_buffer_pos; // in bytes
|
||||
|
||||
|
||||
@@ -16,21 +16,20 @@ GNU General Public License for more details.
|
||||
#ifndef VOX_H
|
||||
#define VOX_H
|
||||
|
||||
#define CVOXWORDMAX 64
|
||||
|
||||
#define SENTENCE_INDEX -99999 // unique sentence index
|
||||
#define CVOXWORDMAX 64
|
||||
#define SENTENCE_INDEX -99999 // unique sentence index
|
||||
|
||||
typedef struct voxword_s
|
||||
{
|
||||
int volume; // increase percent, ie: 125 = 125% increase
|
||||
int pitch; // pitch shift up percent
|
||||
int start; // offset start of wave percent
|
||||
int end; // offset end of wave percent
|
||||
int cbtrim; // end of wave after being trimmed to 'end'
|
||||
int fKeepCached; // 1 if this word was already in cache before sentence referenced it
|
||||
int samplefrac; // if pitch shifting, this is position into wav * 256
|
||||
int timecompress; // % of wave to skip during playback (causes no pitch shift)
|
||||
sfx_t *sfx; // name and cache pointer
|
||||
int volume; // increase percent, ie: 125 = 125% increase
|
||||
int pitch; // pitch shift up percent
|
||||
int start; // offset start of wave percent
|
||||
int end; // offset end of wave percent
|
||||
int cbtrim; // end of wave after being trimmed to 'end'
|
||||
int fKeepCached; // 1 if this word was already in cache before sentence referenced it
|
||||
int samplefrac; // if pitch shifting, this is position into wav * 256
|
||||
int timecompress; // % of wave to skip during playback (causes no pitch shift)
|
||||
sfx_t *sfx; // name and cache pointer
|
||||
} voxword_t;
|
||||
|
||||
struct channel_s;
|
||||
|
||||
@@ -19,13 +19,15 @@ GNU General Public License for more details.
|
||||
|
||||
#define HASH_SIZE 128 // 128 * 4 * 4 == 2048 bytes
|
||||
|
||||
typedef struct base_command_hashmap_s
|
||||
typedef struct base_command_hashmap_s base_command_hashmap_t;
|
||||
|
||||
struct base_command_hashmap_s
|
||||
{
|
||||
base_command_t *basecmd; // base command: cvar, alias or command
|
||||
const char *name; // key for searching
|
||||
base_command_type_e type; // type for faster searching
|
||||
struct base_command_hashmap_s *next;
|
||||
} base_command_hashmap_t;
|
||||
base_command_t *basecmd; // base command: cvar, alias or command
|
||||
base_command_hashmap_t *next;
|
||||
base_command_type_e type; // type for faster searching
|
||||
char name[1]; // key for searching
|
||||
};
|
||||
|
||||
static base_command_hashmap_t *hashed_cmds[HASH_SIZE];
|
||||
|
||||
@@ -124,11 +126,12 @@ void BaseCmd_Insert( base_command_type_e type, base_command_t *basecmd, const ch
|
||||
{
|
||||
base_command_hashmap_t *elem, *cur, *find;
|
||||
uint hash = BaseCmd_HashKey( name );
|
||||
size_t len = Q_strlen( name );
|
||||
|
||||
elem = Z_Malloc( sizeof( base_command_hashmap_t ) );
|
||||
elem = Z_Malloc( sizeof( base_command_hashmap_t ) + len );
|
||||
elem->basecmd = basecmd;
|
||||
elem->type = type;
|
||||
elem->name = name;
|
||||
Q_strncpy( elem->name, name, len + 1 );
|
||||
|
||||
// link the variable in alphanumerical order
|
||||
for( cur = NULL, find = hashed_cmds[hash];
|
||||
|
||||
@@ -31,8 +31,6 @@ typedef enum base_command_type
|
||||
|
||||
typedef void base_command_t;
|
||||
|
||||
|
||||
|
||||
void BaseCmd_Init( void );
|
||||
base_command_t *BaseCmd_Find( base_command_type_e type, const char *name );
|
||||
void BaseCmd_FindAll( const char *name,
|
||||
|
||||
@@ -949,7 +949,7 @@ static void Cmd_Else_f( void )
|
||||
|
||||
static qboolean Cmd_ShouldAllowCommand( cmd_t *cmd, qboolean isPrivileged )
|
||||
{
|
||||
const char *prefixes[] = { "cl_", "gl_", "r_", "m_", "hud_" };
|
||||
const char *prefixes[] = { "cl_", "gl_", "r_", "m_", "hud_", "joy_", "con_", "scr_" };
|
||||
int i;
|
||||
|
||||
// always allow local commands
|
||||
@@ -1015,7 +1015,7 @@ static void Cmd_ExecuteStringWithPrivilegeCheck( const char *text, qboolean isPr
|
||||
*ptoken++ = *text++;
|
||||
*ptoken = 0;
|
||||
|
||||
len += Q_strncpy( pcmd, Cvar_VariableString( token ), MAX_CMD_LINE - len );
|
||||
len += Q_strncpy( pcmd, Cvar_VariableString( token ), sizeof( token ) - len );
|
||||
pcmd = command + len;
|
||||
|
||||
if( !*text ) break;
|
||||
|
||||
@@ -74,6 +74,7 @@ GNU General Public License for more details.
|
||||
#define DEFAULT_UPDATE_PAGE "https://github.com/FWGS/xash3d-fwgs/releases/latest"
|
||||
|
||||
#define XASH_ENGINE_NAME "Xash3D FWGS"
|
||||
#define XASH_DEDICATED_SERVER_NAME "XashDS"
|
||||
#define XASH_VERSION "0.20" // engine current version
|
||||
#define XASH_COMPAT_VERSION "0.99" // version we are based on
|
||||
|
||||
|
||||
@@ -390,6 +390,9 @@ byte *LZSS_Compress( byte *pInput, int inputLength, uint *pOutputSize )
|
||||
byte *pFinal = NULL;
|
||||
lzss_state_t state;
|
||||
|
||||
if( !pStart )
|
||||
return NULL;
|
||||
|
||||
memset( &state, 0, sizeof( state ));
|
||||
state.window_size = LZSS_WINDOW_SIZE;
|
||||
|
||||
|
||||
@@ -273,12 +273,12 @@ typedef struct
|
||||
|
||||
typedef enum bugcomp_e
|
||||
{
|
||||
// default mode, we assume that user dlls are not relying on engine bugs
|
||||
BUGCOMP_OFF,
|
||||
// reverts fix for pfnPEntityOfEntIndex for bug compatibility with GoldSrc
|
||||
BUGCOMP_PENTITYOFENTINDEX_FLAG = BIT( 0 ),
|
||||
|
||||
// GoldSrc mode, user dlls are relying on GoldSrc specific bugs
|
||||
// but fixing them may break regular Xash games
|
||||
BUGCOMP_GOLDSRC,
|
||||
// rewrites mod's attempts to write GoldSrc-specific messages into Xash protocol
|
||||
// (new wrappers are added by request)
|
||||
BUGCOMP_MESSAGE_REWRITE_FACILITY_FLAG = BIT( 1 ),
|
||||
} bugcomp_t;
|
||||
|
||||
typedef struct host_parm_s
|
||||
@@ -356,7 +356,7 @@ typedef struct host_parm_s
|
||||
int numdecals;
|
||||
|
||||
// bug compatibility level, for very "special" games
|
||||
bugcomp_t bugcomp;
|
||||
uint32_t bugcomp;
|
||||
|
||||
// measure time to first frame
|
||||
double starttime;
|
||||
@@ -479,14 +479,6 @@ typedef enum
|
||||
WF_TOTALCOUNT, // must be last
|
||||
} sndformat_t;
|
||||
|
||||
// soundlib global settings
|
||||
typedef enum
|
||||
{
|
||||
SL_USE_LERPING = BIT(0), // lerping sounds during resample
|
||||
SL_KEEP_8BIT = BIT(1), // don't expand 8bit sounds automatically up to 16 bit
|
||||
SL_ALLOW_OVERWRITE = BIT(2), // allow to overwrite stored sounds
|
||||
} slFlags_t;
|
||||
|
||||
// wavdata output flags
|
||||
typedef enum
|
||||
{
|
||||
@@ -495,21 +487,20 @@ typedef enum
|
||||
SOUND_STREAM = BIT( 1 ), // this is a streaminfo, not a real sound
|
||||
|
||||
// Sound_Process manipulation flags
|
||||
SOUND_RESAMPLE = BIT(12), // resample sound to specified rate
|
||||
SOUND_CONVERT16BIT = BIT(13), // change sound resolution from 8 bit to 16
|
||||
SOUND_RESAMPLE = BIT( 12 ), // resample sound to specified rate
|
||||
} sndFlags_t;
|
||||
|
||||
typedef struct
|
||||
{
|
||||
word rate; // num samples per second (e.g. 11025 - 11 khz)
|
||||
byte width; // resolution - bum bits divided by 8 (8 bit is 1, 16 bit is 2)
|
||||
byte channels; // num channels (1 - mono, 2 - stereo)
|
||||
int loopStart; // offset at this point sound will be looping while playing more than only once
|
||||
int samples; // total samplecount in wav
|
||||
uint type; // compression type
|
||||
uint flags; // misc sound flags
|
||||
byte *buffer; // sound buffer
|
||||
size_t size; // for bounds checking
|
||||
word rate; // num samples per second (e.g. 11025 - 11 khz)
|
||||
byte width; // resolution - bum bits divided by 8 (8 bit is 1, 16 bit is 2)
|
||||
byte channels; // num channels (1 - mono, 2 - stereo)
|
||||
uint loopStart; // offset at this point sound will be looping while playing more than only once
|
||||
uint samples; // total samplecount in wav
|
||||
uint type; // compression type
|
||||
uint flags; // misc sound flags
|
||||
byte *buffer; // sound buffer
|
||||
size_t size; // for bounds checking
|
||||
} wavdata_t;
|
||||
|
||||
//
|
||||
@@ -704,7 +695,7 @@ void Log_Printf( const char *fmt, ... ) _format( 1 );
|
||||
void SV_BroadcastCommand( const char *fmt, ... ) _format( 1 );
|
||||
void SV_BroadcastPrintf( struct sv_client_s *ignore, const char *fmt, ... ) _format( 2 );
|
||||
void CL_ClearStaticEntities( void );
|
||||
qboolean S_StreamGetCurrentState( char *currentTrack, char *loopTrack, int *position );
|
||||
qboolean S_StreamGetCurrentState( char *currentTrack, size_t currentTrackSize, char *loopTrack, size_t loopTrackSize, int *position );
|
||||
void CL_ServerCommand( qboolean reliable, const char *fmt, ... ) _format( 2 );
|
||||
void CL_HudMessage( const char *pMessage );
|
||||
const char *CL_MsgInfo( int cmd );
|
||||
|
||||
@@ -465,7 +465,7 @@ static qboolean Cmd_GetSoundList( const char *s, char *completedname, int length
|
||||
t = FS_Search( va( "%s%s*.*", DEFAULT_SOUNDPATH, s ), true, false );
|
||||
if( !t ) return false;
|
||||
|
||||
Q_strncpy( matchbuf, t->filenames[0] + sizeof( DEFAULT_SOUNDPATH ) - 1, MAX_STRING );
|
||||
Q_strncpy( matchbuf, t->filenames[0] + sizeof( DEFAULT_SOUNDPATH ) - 1, sizeof( matchbuf ));
|
||||
COM_StripExtension( matchbuf );
|
||||
if( completedname && length )
|
||||
Q_strncpy( completedname, matchbuf, length );
|
||||
@@ -478,7 +478,7 @@ static qboolean Cmd_GetSoundList( const char *s, char *completedname, int length
|
||||
if( Q_stricmp( ext, "wav" ) && Q_stricmp( ext, "mp3" ))
|
||||
continue;
|
||||
|
||||
Q_strncpy( matchbuf, t->filenames[i] + sizeof( DEFAULT_SOUNDPATH ) - 1, MAX_STRING );
|
||||
Q_strncpy( matchbuf, t->filenames[i] + sizeof( DEFAULT_SOUNDPATH ) - 1, sizeof( matchbuf ));
|
||||
COM_StripExtension( matchbuf );
|
||||
Con_Printf( "%16s\n", matchbuf );
|
||||
numsounds++;
|
||||
@@ -764,14 +764,14 @@ static qboolean Cmd_GetGamesList( const char *s, char *completedname, int length
|
||||
}
|
||||
|
||||
if( !numgamedirs ) return false;
|
||||
Q_strncpy( matchbuf, gamedirs[0], MAX_STRING );
|
||||
Q_strncpy( matchbuf, gamedirs[0], sizeof( matchbuf ));
|
||||
if( completedname && length )
|
||||
Q_strncpy( completedname, matchbuf, length );
|
||||
if( numgamedirs == 1 ) return true;
|
||||
|
||||
for( i = 0; i < numgamedirs; i++ )
|
||||
{
|
||||
Q_strncpy( matchbuf, gamedirs[i], MAX_STRING );
|
||||
Q_strncpy( matchbuf, gamedirs[i], sizeof( matchbuf ));
|
||||
Con_Printf( "%16s\n", matchbuf );
|
||||
}
|
||||
|
||||
@@ -825,14 +825,14 @@ static qboolean Cmd_GetCDList( const char *s, char *completedname, int length )
|
||||
}
|
||||
|
||||
if( !numcdcommands ) return false;
|
||||
Q_strncpy( matchbuf, cdcommands[0], MAX_STRING );
|
||||
Q_strncpy( matchbuf, cdcommands[0], sizeof( matchbuf ));
|
||||
if( completedname && length )
|
||||
Q_strncpy( completedname, matchbuf, length );
|
||||
if( numcdcommands == 1 ) return true;
|
||||
|
||||
for( i = 0; i < numcdcommands; i++ )
|
||||
{
|
||||
Q_strncpy( matchbuf, cdcommands[i], MAX_STRING );
|
||||
Q_strncpy( matchbuf, cdcommands[i], sizeof( matchbuf ));
|
||||
Con_Printf( "%16s\n", matchbuf );
|
||||
}
|
||||
|
||||
@@ -866,7 +866,7 @@ static qboolean Cmd_CheckMapsList_R( qboolean fRefresh, qboolean onlyingamedir )
|
||||
return true; // exist
|
||||
|
||||
// setup mpfilter
|
||||
size = Q_snprintf( mpfilter, sizeof( mpfilter ), "maps/%s", GI->mp_filter );
|
||||
Q_snprintf( mpfilter, sizeof( mpfilter ), "maps/%s", GI->mp_filter );
|
||||
t = FS_Search( "maps/*.bsp", false, onlyingamedir );
|
||||
|
||||
if( !t )
|
||||
@@ -900,13 +900,11 @@ static qboolean Cmd_CheckMapsList_R( qboolean fRefresh, qboolean onlyingamedir )
|
||||
|
||||
if( f )
|
||||
{
|
||||
int num_spawnpoints = 0;
|
||||
dheader_t *header;
|
||||
dlump_t entities;
|
||||
qboolean have_spawnpoints = false;
|
||||
dlump_t entities;
|
||||
|
||||
memset( buf, 0, MAX_SYSPATH );
|
||||
FS_Read( f, buf, MAX_SYSPATH );
|
||||
header = (dheader_t *)buf;
|
||||
|
||||
// check all the lumps and some other errors
|
||||
if( !Mod_TestBmodelLumps( f, t->filenames[i], buf, true, &entities ))
|
||||
@@ -937,13 +935,24 @@ static qboolean Cmd_CheckMapsList_R( qboolean fRefresh, qboolean onlyingamedir )
|
||||
char token[MAX_TOKEN];
|
||||
qboolean worldspawn = true;
|
||||
|
||||
Q_strncpy( message, "No Title", MAX_STRING );
|
||||
Q_strncpy( message, "No Title", sizeof( message ));
|
||||
pfile = ents;
|
||||
|
||||
while(( pfile = COM_ParseFile( pfile, token, sizeof( token ))) != NULL )
|
||||
{
|
||||
if( token[0] == '}' && worldspawn )
|
||||
{
|
||||
worldspawn = false;
|
||||
|
||||
// if mod has mp_filter set up, then it's a mod that
|
||||
// might not have valid mp_entity set in GI
|
||||
// if mod is multiplayer only, assume all maps are valid
|
||||
if( use_filter || GI->gamemode == GAME_MULTIPLAYER_ONLY )
|
||||
{
|
||||
have_spawnpoints = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
else if( !Q_strcmp( token, "message" ) && worldspawn )
|
||||
{
|
||||
// get the message contents
|
||||
@@ -952,17 +961,23 @@ static qboolean Cmd_CheckMapsList_R( qboolean fRefresh, qboolean onlyingamedir )
|
||||
else if( !Q_strcmp( token, "classname" ))
|
||||
{
|
||||
pfile = COM_ParseFile( pfile, token, sizeof( token ));
|
||||
if( !Q_strcmp( token, GI->mp_entity ) || use_filter )
|
||||
num_spawnpoints++;
|
||||
|
||||
if( !Q_strcmp( token, GI->mp_entity ))
|
||||
{
|
||||
have_spawnpoints = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if( num_spawnpoints ) break; // valid map
|
||||
|
||||
if( have_spawnpoints )
|
||||
break; // valid map
|
||||
}
|
||||
Mem_Free( ents );
|
||||
}
|
||||
|
||||
if( f ) FS_Close( f );
|
||||
|
||||
if( num_spawnpoints )
|
||||
if( have_spawnpoints )
|
||||
{
|
||||
// format: mapname "maptitle"\n
|
||||
Q_snprintf( result, sizeof( result ), "%s \"%s\"\n", mapname, message );
|
||||
|
||||
@@ -490,13 +490,14 @@ static void Sys_Crash( int signal, siginfo_t *si, void *context)
|
||||
#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;
|
||||
host.crashed = true;
|
||||
|
||||
|
||||
Sys_Quit();
|
||||
}
|
||||
|
||||
@@ -152,8 +152,10 @@ static qboolean Cvar_UpdateInfo( convar_t *var, const char *value, qboolean noti
|
||||
if ( Host_IsDedicated() )
|
||||
{
|
||||
// g-cont. this is a very strange behavior...
|
||||
Info_SetValueForKey( SV_Serverinfo(), var->name, value, MAX_SERVERINFO_STRING ),
|
||||
SV_BroadcastCommand( "fullserverinfo \"%s\"\n", SV_Serverinfo( ));
|
||||
char *info = SV_Serverinfo();
|
||||
|
||||
Info_SetValueForKey( info, var->name, value, MAX_SERVERINFO_STRING ),
|
||||
SV_BroadcastCommand( "fullserverinfo \"%s\"\n", info );
|
||||
}
|
||||
#if !XASH_DEDICATED
|
||||
else
|
||||
@@ -953,7 +955,7 @@ static void Cvar_SetGL( const char *name, const char *value )
|
||||
|
||||
static qboolean Cvar_ShouldSetCvar( convar_t *v, qboolean isPrivileged )
|
||||
{
|
||||
const char *prefixes[] = { "cl_", "gl_", "m_", "r_", "hud_" };
|
||||
const char *prefixes[] = { "cl_", "gl_", "m_", "r_", "hud_", "joy_", "con_", "scr_" };
|
||||
int i;
|
||||
|
||||
if( isPrivileged )
|
||||
|
||||
@@ -89,32 +89,32 @@ qboolean FS_LoadProgs( void )
|
||||
|
||||
if( !fs_hInstance )
|
||||
{
|
||||
Host_Error( "FS_LoadProgs: can't load filesystem library %s: %s\n", name, COM_GetLibraryError() );
|
||||
Host_Error( "%s: can't load filesystem library %s: %s\n", __func__, name, COM_GetLibraryError() );
|
||||
return false;
|
||||
}
|
||||
|
||||
if( !( GetFSAPI = (FSAPI)COM_GetProcAddress( fs_hInstance, GET_FS_API )))
|
||||
{
|
||||
FS_UnloadProgs();
|
||||
Host_Error( "FS_LoadProgs: can't find GetFSAPI entry point in %s\n", name );
|
||||
Host_Error( "%s: can't find GetFSAPI entry point in %s\n", __func__, name );
|
||||
return false;
|
||||
}
|
||||
|
||||
if( !GetFSAPI( FS_API_VERSION, &g_fsapi, &FI, &fs_memfuncs ))
|
||||
if( GetFSAPI( FS_API_VERSION, &g_fsapi, &FI, &fs_memfuncs ) != FS_API_VERSION )
|
||||
{
|
||||
FS_UnloadProgs();
|
||||
Host_Error( "FS_LoadProgs: can't initialize filesystem API: wrong version\n" );
|
||||
Host_Error( "%s: can't initialize filesystem API: wrong version\n", __func__ );
|
||||
return false;
|
||||
}
|
||||
|
||||
if( !( fs_pfnCreateInterface = (pfnCreateInterface_t)COM_GetProcAddress( fs_hInstance, "CreateInterface" )))
|
||||
{
|
||||
FS_UnloadProgs();
|
||||
Host_Error( "FS_LoadProgs: can't find CreateInterface entry point in %s\n", name );
|
||||
Host_Error( "%s: can't find CreateInterface entry point in %s\n", __func__, name );
|
||||
return false;
|
||||
}
|
||||
|
||||
Con_DPrintf( "FS_LoadProgs: filesystem_stdio successfully loaded\n" );
|
||||
Con_DPrintf( "%s: filesystem_stdio successfully loaded\n", __func__ );
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -65,6 +65,33 @@ static CVAR_DEFINE( host_sleeptime, "sleeptime", "1", FCVAR_ARCHIVE|FCVAR_FILTER
|
||||
static CVAR_DEFINE_AUTO( host_sleeptime_debug, "0", 0, "print sleeps between frames" );
|
||||
CVAR_DEFINE( con_gamemaps, "con_mapfilter", "1", FCVAR_ARCHIVE, "when true show only maps in game folder" );
|
||||
|
||||
typedef struct feature_message_s
|
||||
{
|
||||
uint32_t mask;
|
||||
const char *msg;
|
||||
const char *arg;
|
||||
} feature_message_t;
|
||||
|
||||
static feature_message_t bugcomp_features[] =
|
||||
{
|
||||
{ BUGCOMP_PENTITYOFENTINDEX_FLAG, "pfnPEntityOfEntIndex bugfix revert", "peoei" },
|
||||
{ BUGCOMP_MESSAGE_REWRITE_FACILITY_FLAG, "GoldSrc Message Rewrite Facility", "gsmrf" },
|
||||
};
|
||||
|
||||
static feature_message_t engine_features[] =
|
||||
{
|
||||
{ ENGINE_WRITE_LARGE_COORD, "Big World Support" },
|
||||
{ ENGINE_QUAKE_COMPATIBLE, "Quake Compatibility" },
|
||||
{ ENGINE_LOAD_DELUXEDATA, "Deluxemap Support" },
|
||||
{ ENGINE_PHYSICS_PUSHER_EXT, "Improved MOVETYPE_PUSH" },
|
||||
{ ENGINE_LARGE_LIGHTMAPS, "Large Lightmaps" },
|
||||
{ ENGINE_COMPENSATE_QUAKE_BUG, "Stupid Quake Bug Compensation" },
|
||||
{ ENGINE_IMPROVED_LINETRACE, "Improved Trace Line" },
|
||||
{ ENGINE_COMPUTE_STUDIO_LERP, "Studio MOVETYPE_STEP Lerping" },
|
||||
{ ENGINE_LINEAR_GAMMA_SPACE, "Linear Gamma Space" },
|
||||
{ ENGINE_STEP_POSHISTORY_LERP, "MOVETYPE_STEP Position History Based Lerping" },
|
||||
};
|
||||
|
||||
static void Sys_PrintUsage( void )
|
||||
{
|
||||
string version_str;
|
||||
@@ -92,8 +119,9 @@ static void Sys_PrintUsage( void )
|
||||
O("-minidumps ", "enable writing minidumps when game is crashed")
|
||||
#endif
|
||||
O("-rodir <path> ", "set read-only base directory")
|
||||
O("-bugcomp ", "enable precise bug compatibility. Will break games that don't require it")
|
||||
O(" ", "Refer to engine documentation for more info")
|
||||
O("-bugcomp [opts] ", "enable precise bug compatibility")
|
||||
O(" ", "will break games that don't require it")
|
||||
O(" ", "refer to engine documentation for more info")
|
||||
O("-disablehelp ", "disable this message")
|
||||
#if !XASH_DEDICATED
|
||||
O("-dedicated ", "run engine in dedicated mode")
|
||||
@@ -106,7 +134,8 @@ static void Sys_PrintUsage( void )
|
||||
O("-noip6 ", "disable IPv6")
|
||||
O("-ip6 <ip> ", "set IPv6 address")
|
||||
O("-port6 <port> ", "set IPv6 port")
|
||||
O("-clockwindow <cw>", "adjust clockwindow used to ignore client commands to prevent speed hacks")
|
||||
O("-clockwindow <cw>", "adjust clockwindow used to ignore client commands")
|
||||
O(" ", "to prevent speed hacks")
|
||||
|
||||
"\nGame options:\n"
|
||||
O("-game <directory>", "set game directory to start engine with")
|
||||
@@ -191,31 +220,14 @@ void Host_ShutdownServer( void )
|
||||
Host_PrintEngineFeatures
|
||||
================
|
||||
*/
|
||||
static void Host_PrintEngineFeatures( int features )
|
||||
static void Host_PrintFeatures( uint32_t flags, const char *s, feature_message_t *features, size_t size )
|
||||
{
|
||||
struct
|
||||
{
|
||||
uint32_t mask;
|
||||
const char *msg;
|
||||
} features_str[] =
|
||||
{
|
||||
{ ENGINE_WRITE_LARGE_COORD, "Big World Support" },
|
||||
{ ENGINE_QUAKE_COMPATIBLE, "Quake Compatibility" },
|
||||
{ ENGINE_LOAD_DELUXEDATA, "Deluxemap Support" },
|
||||
{ ENGINE_PHYSICS_PUSHER_EXT, "Improved MOVETYPE_PUSH" },
|
||||
{ ENGINE_LARGE_LIGHTMAPS, "Large Lightmaps" },
|
||||
{ ENGINE_COMPENSATE_QUAKE_BUG, "Stupid Quake Bug Compensation" },
|
||||
{ ENGINE_IMPROVED_LINETRACE, "Improved Trace Line" },
|
||||
{ ENGINE_COMPUTE_STUDIO_LERP, "Studio MOVETYPE_STEP Lerping" },
|
||||
{ ENGINE_LINEAR_GAMMA_SPACE, "Linear Gamma Space" },
|
||||
{ ENGINE_STEP_POSHISTORY_LERP, "MOVETYPE_STEP Position History Based Lerping" },
|
||||
};
|
||||
int i;
|
||||
size_t i;
|
||||
|
||||
for( i = 0; i < ARRAYSIZE( features_str ); i++ )
|
||||
for( i = 0; i < size; i++ )
|
||||
{
|
||||
if( FBitSet( features, features_str[i].mask ))
|
||||
Con_Reportf( "^3EXT:^7 %s is enabled\n", features_str[i].msg );
|
||||
if( FBitSet( flags, features[i].mask ))
|
||||
Con_Printf( "^3%s:^7 %s is enabled\n", s, features[i].msg );
|
||||
}
|
||||
}
|
||||
|
||||
@@ -243,7 +255,7 @@ void Host_ValidateEngineFeatures( uint32_t features )
|
||||
SetBits( features, ENGINE_STEP_POSHISTORY_LERP );
|
||||
|
||||
// print requested first
|
||||
Host_PrintEngineFeatures( features );
|
||||
Host_PrintFeatures( features, "EXT", engine_features, ARRAYSIZE( engine_features ));
|
||||
|
||||
// now warn about incompatible bits
|
||||
if( FBitSet( features, ENGINE_STEP_POSHISTORY_LERP|ENGINE_COMPUTE_STUDIO_LERP ) == ( ENGINE_STEP_POSHISTORY_LERP|ENGINE_COMPUTE_STUDIO_LERP ))
|
||||
@@ -853,7 +865,7 @@ void GAME_EXPORT Host_Error( const char *error, ... )
|
||||
}
|
||||
|
||||
recursive = true;
|
||||
Q_strncpy( hosterror2, hosterror1, MAX_SYSPATH );
|
||||
Q_strncpy( hosterror2, hosterror1, sizeof( hosterror2 ));
|
||||
host.errorframe = host.framecount; // to avoid multply calls per frame
|
||||
Q_snprintf( host.finalmsg, sizeof( host.finalmsg ), "Server crashed: %s", hosterror1 );
|
||||
|
||||
@@ -939,11 +951,59 @@ static void Host_RunTests( int stage )
|
||||
TEST_LIST_1_CLIENT;
|
||||
#endif
|
||||
Msg( "Done! %d passed, %d failed\n", tests_stats.passed, tests_stats.failed );
|
||||
error_on_exit = tests_stats.failed > 0 ? EXIT_FAILURE : EXIT_SUCCESS;
|
||||
Sys_Quit();
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
static uint32_t Host_CheckBugcomp( void )
|
||||
{
|
||||
const char *prev, *next;
|
||||
uint32_t flags = 0;
|
||||
string args, arg;
|
||||
size_t i;
|
||||
|
||||
if( !Sys_CheckParm( "-bugcomp" ))
|
||||
return 0;
|
||||
|
||||
if( Sys_GetParmFromCmdLine( "-bugcomp", args ) && isalpha( args[0] ))
|
||||
{
|
||||
for( prev = args, next = Q_strchrnul( prev, '+' ); ; prev = next + 1, next = Q_strchrnul( prev, '+' ))
|
||||
{
|
||||
Q_strncpy( arg, prev, next - prev + 1 );
|
||||
for( i = 0; i < ARRAYSIZE( bugcomp_features ); i++ )
|
||||
{
|
||||
if( !Q_stricmp( bugcomp_features[i].arg, arg ))
|
||||
{
|
||||
SetBits( flags, bugcomp_features[i].mask );
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if( i == ARRAYSIZE( bugcomp_features ))
|
||||
{
|
||||
Con_Printf( S_ERROR "Unknown bugcomp flag %s\n", arg );
|
||||
Con_Printf( "Valid flags are:\n" );
|
||||
for( i = 0; i < ARRAYSIZE( bugcomp_features ); i++ )
|
||||
Con_Printf( "\t%s: %s\n", bugcomp_features[i].arg, bugcomp_features[i].msg );
|
||||
}
|
||||
|
||||
if( !*next )
|
||||
break;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// no argument specified -bugcomp just enables everything
|
||||
flags = -1;
|
||||
}
|
||||
|
||||
Host_PrintFeatures( flags, "BUGCOMP", bugcomp_features, ARRAYSIZE( bugcomp_features ));
|
||||
|
||||
return flags;
|
||||
}
|
||||
|
||||
/*
|
||||
=================
|
||||
Host_InitCommon
|
||||
@@ -1048,13 +1108,6 @@ static void Host_InitCommon( int argc, char **argv, const char *progname, qboole
|
||||
// member console allowing
|
||||
host.allow_console_init = host.allow_console;
|
||||
|
||||
if( Sys_CheckParm( "-bugcomp" ))
|
||||
{
|
||||
// add argument check here when we add other levels
|
||||
// of bugcompatibility
|
||||
host.bugcomp = BUGCOMP_GOLDSRC;
|
||||
}
|
||||
|
||||
// timeBeginPeriod( 1 ); // a1ba: Do we need this?
|
||||
|
||||
// NOTE: this message couldn't be passed into game console but it doesn't matter
|
||||
@@ -1181,11 +1234,11 @@ static void Host_InitCommon( int argc, char **argv, const char *progname, qboole
|
||||
|
||||
Sys_InitLog();
|
||||
|
||||
// print bugcompatibility level here, after log was initialized
|
||||
if( host.bugcomp == BUGCOMP_GOLDSRC )
|
||||
{
|
||||
Con_Printf( "^3BUGCOMP^7: GoldSrc bug-compatibility enabled\n" );
|
||||
}
|
||||
// print current developer level to simplify processing users feedback
|
||||
if( developer > 0 )
|
||||
Con_Printf( "Developer level: ^3%i\n", developer );
|
||||
|
||||
host.bugcomp = Host_CheckBugcomp();
|
||||
|
||||
Cmd_AddCommand( "exec", Host_Exec_f, "execute a script file" );
|
||||
Cmd_AddCommand( "memlist", Host_MemStats_f, "prints memory pool information" );
|
||||
@@ -1369,7 +1422,11 @@ int EXPORT Host_Main( int argc, char **argv, const char *progname, int bChangeGa
|
||||
host.status = HOST_FRAME;
|
||||
|
||||
if( GameState->nextstate == STATE_RUNFRAME )
|
||||
#if XASH_WIN32 // FIXME: implement autocomplete on *nix
|
||||
Con_Printf( "Type 'map <mapname>' to start game... (TAB-autocomplete is working too)\n" );
|
||||
#else // !XASH_WIN32
|
||||
Con_Printf( "Type 'map <mapname>' to start game...\n" );
|
||||
#endif // !XASH_WIN32
|
||||
|
||||
// execute server.cfg after commandline
|
||||
// so we have a chance to set servercfgfile
|
||||
|
||||
@@ -400,7 +400,8 @@ static qboolean HPAK_Validate( const char *filename, qboolean quiet, qboolean de
|
||||
f = FS_Open( pakname, "rb", true );
|
||||
if( !f )
|
||||
{
|
||||
Con_DPrintf( S_ERROR "Couldn't find %s.\n", pakname );
|
||||
if( !quiet )
|
||||
Con_DPrintf( S_ERROR "Couldn't find %s.\n", pakname );
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
@@ -70,6 +70,7 @@ static qboolean Image_KTX2Parse( const ktx2_header_t *header, const byte *buffer
|
||||
ktx2_index_t index;
|
||||
size_t total_size = 0;
|
||||
size_t max_offset = 0;
|
||||
int mip, cursor;
|
||||
const byte *const levels_begin = buffer + KTX2_LEVELS_OFFSET;
|
||||
|
||||
// Sets image.type and image.flags
|
||||
@@ -125,7 +126,7 @@ static qboolean Image_KTX2Parse( const ktx2_header_t *header, const byte *buffer
|
||||
|
||||
memcpy( &index, buffer + KTX2_IDENTIFIER_SIZE + sizeof( ktx2_header_t ), sizeof( index ));
|
||||
|
||||
for( int mip = 0; mip < header->levelCount; ++mip )
|
||||
for( mip = 0; mip < header->levelCount; ++mip )
|
||||
{
|
||||
const uint32_t width = Q_max( 1, ( header->pixelWidth >> mip ));
|
||||
const uint32_t height = Q_max( 1, ( header->pixelHeight >> mip ));
|
||||
@@ -154,7 +155,7 @@ static qboolean Image_KTX2Parse( const ktx2_header_t *header, const byte *buffer
|
||||
image.rgba = Mem_Malloc( host.imagepool, image.size );
|
||||
memcpy( image.rgba, buffer, image.size );
|
||||
|
||||
for( int mip = 0, cursor = 0; mip < header->levelCount; ++mip )
|
||||
for( mip = 0, cursor = 0; mip < header->levelCount; ++mip )
|
||||
{
|
||||
ktx2_level_t level;
|
||||
memcpy( &level, levels_begin + mip * sizeof( level ), sizeof( level ));
|
||||
|
||||
@@ -390,7 +390,7 @@ qboolean Image_LoadMIP( const char *name, const byte *buffer, fs_offset_t filesi
|
||||
if( Q_strrchr( name, '{' ))
|
||||
{
|
||||
// NOTE: decals with 'blue base' can be interpret as colored decals
|
||||
if( !Image_CheckFlag( IL_LOAD_DECAL ) || ( pal[765] == 0 && pal[766] == 0 && pal[767] == 255 ))
|
||||
if( !Image_CheckFlag( IL_LOAD_DECAL ) || ( pal && pal[765] == 0 && pal[766] == 0 && pal[767] == 255 ))
|
||||
{
|
||||
SetBits( image.flags, IMAGE_ONEBIT_ALPHA );
|
||||
rendermode = LUMP_MASKED;
|
||||
@@ -488,37 +488,40 @@ qboolean Image_LoadMIP( const char *name, const byte *buffer, fs_offset_t filesi
|
||||
}
|
||||
|
||||
// check for half-life water texture
|
||||
if( hl_texture && ( mip.name[0] == '!' || !Q_strnicmp( mip.name, "water", 5 )))
|
||||
if( pal != NULL )
|
||||
{
|
||||
// grab the fog color
|
||||
image.fogParams[0] = pal[3*3+0];
|
||||
image.fogParams[1] = pal[3*3+1];
|
||||
image.fogParams[2] = pal[3*3+2];
|
||||
|
||||
// grab the fog density
|
||||
image.fogParams[3] = pal[4*3+0];
|
||||
}
|
||||
else if( hl_texture && ( rendermode == LUMP_GRADIENT ))
|
||||
{
|
||||
// grab the decal color
|
||||
image.fogParams[0] = pal[255*3+0];
|
||||
image.fogParams[1] = pal[255*3+1];
|
||||
image.fogParams[2] = pal[255*3+2];
|
||||
|
||||
// calc the decal reflectivity
|
||||
image.fogParams[3] = VectorAvg( image.fogParams );
|
||||
}
|
||||
else if( pal != NULL )
|
||||
{
|
||||
// calc texture reflectivity
|
||||
for( i = 0; i < 256; i++ )
|
||||
if( hl_texture && ( mip.name[0] == '!' || !Q_strnicmp( mip.name, "water", 5 )))
|
||||
{
|
||||
reflectivity[0] += pal[i*3+0];
|
||||
reflectivity[1] += pal[i*3+1];
|
||||
reflectivity[2] += pal[i*3+2];
|
||||
}
|
||||
// grab the fog color
|
||||
image.fogParams[0] = pal[3*3+0];
|
||||
image.fogParams[1] = pal[3*3+1];
|
||||
image.fogParams[2] = pal[3*3+2];
|
||||
|
||||
VectorDivide( reflectivity, 256, image.fogParams );
|
||||
// grab the fog density
|
||||
image.fogParams[3] = pal[4*3+0];
|
||||
}
|
||||
else if( hl_texture && ( rendermode == LUMP_GRADIENT ))
|
||||
{
|
||||
// grab the decal color
|
||||
image.fogParams[0] = pal[255*3+0];
|
||||
image.fogParams[1] = pal[255*3+1];
|
||||
image.fogParams[2] = pal[255*3+2];
|
||||
|
||||
// calc the decal reflectivity
|
||||
image.fogParams[3] = VectorAvg( image.fogParams );
|
||||
}
|
||||
else
|
||||
{
|
||||
// calc texture reflectivity
|
||||
for( i = 0; i < 256; i++ )
|
||||
{
|
||||
reflectivity[0] += pal[i*3+0];
|
||||
reflectivity[1] += pal[i*3+1];
|
||||
reflectivity[2] += pal[i*3+2];
|
||||
}
|
||||
|
||||
VectorDivide( reflectivity, 256, image.fogParams );
|
||||
}
|
||||
}
|
||||
|
||||
image.type = PF_INDEXED_32; // 32-bit palete
|
||||
|
||||
@@ -24,7 +24,8 @@ typedef struct dll_user_s
|
||||
qboolean custom_loader; // a bit who indicated loader type
|
||||
qboolean encrypted; // dll is crypted (some client.dll in HL, CS etc)
|
||||
char dllName[32]; // for debug messages
|
||||
string fullPath, shortPath; // actual dll paths
|
||||
char fullPath[2048];
|
||||
string shortPath; // actual dll paths
|
||||
|
||||
// ordinals stuff, valid only on Win32
|
||||
word *ordinals;
|
||||
|
||||
@@ -118,7 +118,7 @@ static void NET_AnnounceToMaster( master_t *m )
|
||||
MSG_WriteBytes( &msg, "q\xFF", 2 );
|
||||
MSG_WriteDword( &msg, m->heartbeat_challenge );
|
||||
|
||||
NET_SendPacket( NS_SERVER, MSG_GetNumBytesWritten( &msg ), MSG_GetBuf( &msg ), m->adr );
|
||||
NET_SendPacket( NS_SERVER, MSG_GetNumBytesWritten( &msg ), MSG_GetData( &msg ), m->adr );
|
||||
|
||||
if( sv_verbose_heartbeats.value )
|
||||
{
|
||||
@@ -262,7 +262,7 @@ static void NET_AddMaster( const char *addr, qboolean save )
|
||||
}
|
||||
|
||||
master = Mem_Malloc( host.mempool, sizeof( master_t ) );
|
||||
Q_strncpy( master->address, addr, MAX_STRING );
|
||||
Q_strncpy( master->address, addr, sizeof( master->address ));
|
||||
master->sent = false;
|
||||
master->save = save;
|
||||
master->next = NULL;
|
||||
|
||||
@@ -1823,8 +1823,8 @@ static void Mod_LoadEntities( model_t *mod, dbspmodel_t *bmod )
|
||||
{
|
||||
char *pszWadFile;
|
||||
|
||||
Q_strncpy( wadstring, token, MAX_TOKEN - 2 );
|
||||
wadstring[MAX_TOKEN - 2] = 0;
|
||||
Q_strncpy( wadstring, token, sizeof( wadstring ) - 2 );
|
||||
wadstring[sizeof( wadstring ) - 2] = 0;
|
||||
|
||||
if( !Q_strchr( wadstring, ';' ))
|
||||
Q_strncat( wadstring, ";", sizeof( wadstring ));
|
||||
|
||||
@@ -89,6 +89,36 @@ const char *svc_strings[svc_lastmsg+1] =
|
||||
"svc_exec",
|
||||
};
|
||||
|
||||
const char *svc_legacy_strings[svc_lastmsg+1] =
|
||||
{
|
||||
[svc_legacy_changing] = "svc_legacy_changing",
|
||||
[svc_legacy_ambientsound] = "svc_legacy_ambientsound",
|
||||
[svc_legacy_soundindex] = "svc_legacy_soundindex",
|
||||
[svc_legacy_ambientsound] = "svc_legacy_ambientsound",
|
||||
[svc_legacy_modelindex] = "svc_legacy_modelindex",
|
||||
[svc_legacy_eventindex] = "svc_legacy_eventindex",
|
||||
[svc_legacy_chokecount] = "svc_legacy_chokecount",
|
||||
};
|
||||
|
||||
const char *svc_goldsrc_strings[svc_lastmsg+1] =
|
||||
{
|
||||
[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_damage] = "svc_goldsrc_damage",
|
||||
[svc_goldsrc_killedmonster] = "svc_goldsrc_killedmonster",
|
||||
[svc_goldsrc_foundsecret] = "svc_goldsrc_foundsecret",
|
||||
[svc_goldsrc_spawnstaticsound] = "svc_goldsrc_spawnstaticsound",
|
||||
[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_timescale] = "svc_goldsrc_timescale",
|
||||
[svc_goldsrc_sendcvarvalue] = "svc_goldsrc_sendcvarvalue",
|
||||
[svc_goldsrc_sendcvarvalue2] = "svc_goldsrc_sendcvarvalue2",
|
||||
};
|
||||
|
||||
void MSG_InitMasks( void )
|
||||
{
|
||||
uint startbit, endbit;
|
||||
@@ -109,85 +139,6 @@ void MSG_InitMasks( void )
|
||||
ExtraMasks[maskBit] = (uint)BIT( maskBit ) - 1;
|
||||
}
|
||||
|
||||
void MSG_InitExt( sizebuf_t *sb, const char *pDebugName, void *pData, int nBytes, int nMaxBits )
|
||||
{
|
||||
MSG_StartWriting( sb, pData, nBytes, 0, nMaxBits );
|
||||
|
||||
sb->pDebugName = pDebugName;
|
||||
}
|
||||
|
||||
void MSG_StartWriting( sizebuf_t *sb, void *pData, int nBytes, int iStartBit, int nBits )
|
||||
{
|
||||
// make sure it's dword aligned and padded.
|
||||
Assert(((uint32_t)pData & 3 ) == 0 );
|
||||
|
||||
sb->pDebugName = "Unnamed";
|
||||
sb->pData = (byte *)pData;
|
||||
|
||||
if( nBits == -1 )
|
||||
{
|
||||
sb->nDataBits = nBytes << 3;
|
||||
}
|
||||
else
|
||||
{
|
||||
Assert( nBits <= nBytes * 8 );
|
||||
sb->nDataBits = nBits;
|
||||
}
|
||||
|
||||
sb->iCurBit = iStartBit;
|
||||
sb->bOverflow = false;
|
||||
}
|
||||
|
||||
/*
|
||||
=======================
|
||||
MSG_Clear
|
||||
|
||||
for clearing overflowed buffer
|
||||
=======================
|
||||
*/
|
||||
void MSG_Clear( sizebuf_t *sb )
|
||||
{
|
||||
sb->iCurBit = 0;
|
||||
sb->bOverflow = false;
|
||||
}
|
||||
|
||||
static qboolean MSG_Overflow( sizebuf_t *sb, int nBits )
|
||||
{
|
||||
if( sb->iCurBit + nBits > sb->nDataBits )
|
||||
sb->bOverflow = true;
|
||||
return sb->bOverflow;
|
||||
}
|
||||
|
||||
qboolean MSG_CheckOverflow( sizebuf_t *sb )
|
||||
{
|
||||
return MSG_Overflow( sb, 0 );
|
||||
}
|
||||
|
||||
int MSG_SeekToBit( sizebuf_t *sb, int bitPos, int whence )
|
||||
{
|
||||
// compute the file offset
|
||||
switch( whence )
|
||||
{
|
||||
case SEEK_CUR:
|
||||
bitPos += sb->iCurBit;
|
||||
break;
|
||||
case SEEK_SET:
|
||||
break;
|
||||
case SEEK_END:
|
||||
bitPos += sb->nDataBits;
|
||||
break;
|
||||
default:
|
||||
return -1;
|
||||
}
|
||||
|
||||
if( bitPos < 0 || bitPos > sb->nDataBits )
|
||||
return -1;
|
||||
|
||||
sb->iCurBit = bitPos;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void MSG_WriteOneBit( sizebuf_t *sb, int nValue )
|
||||
{
|
||||
if( !MSG_Overflow( sb, 1 ))
|
||||
@@ -669,7 +620,7 @@ qboolean MSG_ReadBytes( sizebuf_t *sb, void *pOut, int nBytes )
|
||||
return MSG_ReadBits( sb, pOut, nBytes << 3 );
|
||||
}
|
||||
|
||||
char *MSG_ReadStringExt( sizebuf_t *sb, qboolean bLine )
|
||||
static char *MSG_ReadStringExt( sizebuf_t *sb, qboolean bLine )
|
||||
{
|
||||
static char string[4096];
|
||||
int l = 0, c;
|
||||
@@ -695,6 +646,16 @@ char *MSG_ReadStringExt( sizebuf_t *sb, qboolean bLine )
|
||||
return string;
|
||||
}
|
||||
|
||||
char *MSG_ReadString( sizebuf_t *sb )
|
||||
{
|
||||
return MSG_ReadStringExt( sb, false );
|
||||
}
|
||||
|
||||
char *MSG_ReadStringLine( sizebuf_t *sb )
|
||||
{
|
||||
return MSG_ReadStringExt( sb, true );
|
||||
}
|
||||
|
||||
void MSG_ExciseBits( sizebuf_t *sb, int startbit, int bitstoremove )
|
||||
{
|
||||
int i, endbit = startbit + bitstoremove;
|
||||
|
||||
@@ -30,39 +30,128 @@ GNU General Public License for more details.
|
||||
// So PAD_NUMBER(0,4) is 0 and PAD_NUMBER(1,4) is 4
|
||||
#define PAD_NUMBER( num, boundary ) ((( num ) + (( boundary ) - 1 )) / ( boundary )) * ( boundary )
|
||||
|
||||
_inline int BitByte( int bits )
|
||||
static inline int BitByte( int bits )
|
||||
{
|
||||
return PAD_NUMBER( bits, 8 ) >> 3;
|
||||
}
|
||||
|
||||
struct sizebuf_s
|
||||
{
|
||||
qboolean bOverflow; // overflow reading or writing
|
||||
const char *pDebugName; // buffer name (pointer to const name)
|
||||
|
||||
byte *pData;
|
||||
int iCurBit;
|
||||
int nDataBits;
|
||||
byte *pData;
|
||||
qboolean bOverflow; // overflow reading or writing
|
||||
int iCurBit;
|
||||
int nDataBits;
|
||||
const char *pDebugName; // buffer name (pointer to const name)
|
||||
};
|
||||
|
||||
#define MSG_StartReading MSG_StartWriting
|
||||
#define MSG_GetNumBytesRead MSG_GetNumBytesWritten
|
||||
#define MSG_GetRealBytesRead MSG_GetRealBytesWritten
|
||||
#define MSG_GetNumBitsRead MSG_GetNumBitsWritten
|
||||
#define MSG_ReadBitAngles MSG_ReadBitVec3Coord
|
||||
#define MSG_ReadString( sb ) MSG_ReadStringExt( sb, false )
|
||||
#define MSG_ReadStringLine( sb ) MSG_ReadStringExt( sb, true )
|
||||
#define MSG_ReadAngle( sb ) (float)(MSG_ReadChar( sb ) * ( 360.0f / 256.0f ))
|
||||
#define MSG_Init( sb, name, data, bytes ) MSG_InitExt( sb, name, data, bytes, -1 )
|
||||
#define MSG_StartReading MSG_StartWriting
|
||||
#define MSG_GetNumBytesRead MSG_GetNumBytesWritten
|
||||
#define MSG_GetRealBytesRead MSG_GetRealBytesWritten
|
||||
#define MSG_GetNumBitsRead MSG_GetNumBitsWritten
|
||||
#define MSG_ReadBitAngles MSG_ReadBitVec3Coord
|
||||
#define MSG_ReadAngle( sb ) (float)( MSG_ReadChar( sb ) * ( 360.0f / 256.0f ))
|
||||
#define MSG_Init( sb, name, data, bytes ) MSG_InitExt( sb, name, data, bytes, -1 )
|
||||
#define MSG_CheckOverflow( sb ) MSG_Overflow( sb, 0 )
|
||||
|
||||
// common functions
|
||||
void MSG_InitExt( sizebuf_t *sb, const char *pDebugName, void *pData, int nBytes, int nMaxBits );
|
||||
void MSG_InitMasks( void ); // called once at startup engine
|
||||
int MSG_SeekToBit( sizebuf_t *sb, int bitPos, int whence );
|
||||
void MSG_ExciseBits( sizebuf_t *sb, int startbit, int bitstoremove );
|
||||
_inline int MSG_TellBit( sizebuf_t *sb ) { return sb->iCurBit; }
|
||||
_inline const char *MSG_GetName( sizebuf_t *sb ) { return sb->pDebugName; }
|
||||
qboolean MSG_CheckOverflow( sizebuf_t *sb );
|
||||
static inline void MSG_Clear( sizebuf_t *sb )
|
||||
{
|
||||
sb->bOverflow = false;
|
||||
sb->iCurBit = 0;
|
||||
}
|
||||
|
||||
static inline void MSG_InitExt( sizebuf_t *sb, const char *pDebugName, void *pData, int nBytes, int nBits )
|
||||
{
|
||||
sb->pData = pData;
|
||||
MSG_Clear( sb );
|
||||
|
||||
if( nBits < 0 )
|
||||
sb->nDataBits = nBytes << 3;
|
||||
else
|
||||
sb->nDataBits = nBits;
|
||||
|
||||
sb->pDebugName = pDebugName;
|
||||
}
|
||||
|
||||
static inline void MSG_StartWriting( sizebuf_t *sb, void *pData, int nBytes, int iStartBit, int nBits )
|
||||
{
|
||||
MSG_InitExt( sb, "Unnamed", pData, nBytes, nBits );
|
||||
sb->iCurBit = iStartBit;
|
||||
}
|
||||
|
||||
static inline int MSG_SeekToBit( sizebuf_t *sb, int bitPos, int whence )
|
||||
{
|
||||
// compute the file offset
|
||||
switch( whence )
|
||||
{
|
||||
case SEEK_CUR:
|
||||
bitPos += sb->iCurBit;
|
||||
break;
|
||||
case SEEK_SET:
|
||||
break;
|
||||
case SEEK_END:
|
||||
bitPos += sb->nDataBits;
|
||||
break;
|
||||
default:
|
||||
return -1;
|
||||
}
|
||||
|
||||
if( unlikely( bitPos < 0 || bitPos > sb->nDataBits ))
|
||||
return -1;
|
||||
|
||||
sb->iCurBit = bitPos;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static inline int MSG_TellBit( sizebuf_t *sb )
|
||||
{
|
||||
return sb->iCurBit;
|
||||
}
|
||||
|
||||
static inline const char *MSG_GetName( sizebuf_t *sb )
|
||||
{
|
||||
return sb->pDebugName;
|
||||
}
|
||||
|
||||
static inline int MSG_GetNumBytesWritten( sizebuf_t *sb )
|
||||
{
|
||||
return BitByte( sb->iCurBit );
|
||||
}
|
||||
|
||||
static inline int MSG_GetRealBytesWritten( sizebuf_t *sb )
|
||||
{
|
||||
return sb->iCurBit >> 3; // unpadded
|
||||
}
|
||||
static inline int MSG_GetNumBitsWritten( sizebuf_t *sb )
|
||||
{
|
||||
return sb->iCurBit;
|
||||
}
|
||||
|
||||
static inline int MSG_GetMaxBits( sizebuf_t *sb )
|
||||
{
|
||||
return sb->nDataBits;
|
||||
}
|
||||
|
||||
static inline int MSG_GetMaxBytes( sizebuf_t *sb )
|
||||
{
|
||||
return sb->nDataBits >> 3;
|
||||
}
|
||||
|
||||
static inline int MSG_GetNumBitsLeft( sizebuf_t *sb )
|
||||
{
|
||||
return sb->nDataBits - sb->iCurBit;
|
||||
}
|
||||
|
||||
static inline int MSG_GetNumBytesLeft( sizebuf_t *sb )
|
||||
{
|
||||
return MSG_GetNumBitsLeft( sb ) >> 3;
|
||||
}
|
||||
|
||||
static inline byte *MSG_GetData( sizebuf_t *sb )
|
||||
{
|
||||
return sb->pData;
|
||||
}
|
||||
|
||||
#if XASH_BIG_ENDIAN
|
||||
#define MSG_BigShort( x ) ( x )
|
||||
@@ -73,9 +162,15 @@ static inline uint16_t MSG_BigShort( const uint16_t x )
|
||||
}
|
||||
#endif
|
||||
|
||||
// init writing
|
||||
void MSG_StartWriting( sizebuf_t *sb, void *pData, int nBytes, int iStartBit, int nBits );
|
||||
void MSG_Clear( sizebuf_t *sb );
|
||||
static inline qboolean MSG_Overflow( sizebuf_t *sb, int nBits )
|
||||
{
|
||||
if( sb->iCurBit + nBits > sb->nDataBits )
|
||||
sb->bOverflow = true;
|
||||
return sb->bOverflow;
|
||||
}
|
||||
|
||||
void MSG_InitMasks( void ); // called once at startup engine
|
||||
void MSG_ExciseBits( sizebuf_t *sb, int startbit, int bitstoremove );
|
||||
|
||||
// Bit-write functions
|
||||
void MSG_WriteOneBit( sizebuf_t *sb, int nValue );
|
||||
@@ -99,20 +194,11 @@ void MSG_WriteCoord( sizebuf_t *sb, float val );
|
||||
void MSG_WriteFloat( sizebuf_t *sb, float val );
|
||||
void MSG_WriteVec3Coord( sizebuf_t *sb, const float *fa );
|
||||
void MSG_WriteVec3Angles( sizebuf_t *sb, const float *fa );
|
||||
qboolean MSG_WriteBytes( sizebuf_t *sb, const void *pBuf, int nBytes ); // same as MSG_WriteData
|
||||
qboolean MSG_WriteString( sizebuf_t *sb, const char *pStr ); // returns false if it overflows the buffer.
|
||||
qboolean MSG_WriteStringf( sizebuf_t *sb, const char *format, ... ) _format( 2 );
|
||||
qboolean MSG_WriteBytes( sizebuf_t *sb, const void *pBuf, int nBytes );
|
||||
|
||||
// helper functions
|
||||
_inline int MSG_GetNumBytesWritten( sizebuf_t *sb ) { return BitByte( sb->iCurBit ); }
|
||||
_inline int MSG_GetRealBytesWritten( sizebuf_t *sb ) { return sb->iCurBit >> 3; } // unpadded
|
||||
_inline int MSG_GetNumBitsWritten( sizebuf_t *sb ) { return sb->iCurBit; }
|
||||
_inline int MSG_GetMaxBits( sizebuf_t *sb ) { return sb->nDataBits; }
|
||||
_inline int MSG_GetMaxBytes( sizebuf_t *sb ) { return sb->nDataBits >> 3; }
|
||||
_inline int MSG_GetNumBitsLeft( sizebuf_t *sb ) { return sb->nDataBits - sb->iCurBit; }
|
||||
_inline int MSG_GetNumBytesLeft( sizebuf_t *sb ) { return MSG_GetNumBitsLeft( sb ) >> 3; }
|
||||
_inline byte *MSG_GetData( sizebuf_t *sb ) { return sb->pData; }
|
||||
_inline byte *MSG_GetBuf( sizebuf_t *sb ) { return sb->pData; } // just an alias
|
||||
|
||||
// Bit-read functions
|
||||
int MSG_ReadOneBit( sizebuf_t *sb );
|
||||
@@ -136,7 +222,8 @@ float MSG_ReadCoord( sizebuf_t *sb );
|
||||
float MSG_ReadFloat( sizebuf_t *sb );
|
||||
void MSG_ReadVec3Coord( sizebuf_t *sb, vec3_t fa );
|
||||
void MSG_ReadVec3Angles( sizebuf_t *sb, vec3_t fa );
|
||||
char *MSG_ReadString( sizebuf_t *sb );
|
||||
char *MSG_ReadStringLine( sizebuf_t *sb );
|
||||
qboolean MSG_ReadBytes( sizebuf_t *sb, void *pOut, int nBytes );
|
||||
char *MSG_ReadStringExt( sizebuf_t *sb, qboolean bLine );
|
||||
|
||||
#endif//NET_BUFFER_H
|
||||
|
||||
@@ -315,6 +315,43 @@ static const delta_field_t ent_fields[] =
|
||||
{ NULL },
|
||||
};
|
||||
|
||||
#if XASH_ENGINE_TESTS
|
||||
typedef struct delta_test_struct_t
|
||||
{
|
||||
char dt_string[128]; // always signed
|
||||
float dt_timewindow_big; // always signed
|
||||
float dt_timewindow_8; // always signed
|
||||
float dt_angle; // always_signed
|
||||
float dt_float_signed;
|
||||
float dt_float_unsigned;
|
||||
int32_t dt_integer_signed;
|
||||
uint32_t dt_integer_unsigned;
|
||||
int16_t dt_short_signed;
|
||||
uint16_t dt_short_unsigned;
|
||||
int8_t dt_byte_signed;
|
||||
uint8_t dt_byte_unsigned;
|
||||
} delta_test_struct_t;
|
||||
|
||||
#define TEST_DEF( x ) #x, offsetof( delta_test_struct_t, x ), sizeof( ((delta_test_struct_t *)0)->x )
|
||||
|
||||
static const delta_field_t test_fields[] =
|
||||
{
|
||||
{ TEST_DEF( dt_string ) },
|
||||
{ TEST_DEF( dt_timewindow_big )},
|
||||
{ TEST_DEF( dt_timewindow_8 )},
|
||||
{ TEST_DEF( dt_angle ) },
|
||||
{ TEST_DEF( dt_float_signed ) },
|
||||
{ TEST_DEF( dt_float_unsigned ) },
|
||||
{ TEST_DEF( dt_integer_signed ) },
|
||||
{ TEST_DEF( dt_integer_unsigned ) },
|
||||
{ TEST_DEF( dt_short_signed ) },
|
||||
{ TEST_DEF( dt_short_unsigned ) },
|
||||
{ TEST_DEF( dt_byte_signed ) },
|
||||
{ TEST_DEF( dt_byte_unsigned ) },
|
||||
{ NULL },
|
||||
};
|
||||
#endif
|
||||
|
||||
enum
|
||||
{
|
||||
DT_EVENT_T = 0,
|
||||
@@ -325,6 +362,10 @@ enum
|
||||
DT_ENTITY_STATE_T,
|
||||
DT_ENTITY_STATE_PLAYER_T,
|
||||
DT_CUSTOM_ENTITY_STATE_T,
|
||||
#if XASH_ENGINE_TESTS
|
||||
DT_DELTA_TEST_STRUCT_T,
|
||||
#endif
|
||||
DT_STRUCT_COUNT
|
||||
};
|
||||
|
||||
static delta_info_t dt_info[] =
|
||||
@@ -337,7 +378,10 @@ static delta_info_t dt_info[] =
|
||||
[DT_ENTITY_STATE_T] = { "entity_state_t", ent_fields, NUM_FIELDS( ent_fields ) },
|
||||
[DT_ENTITY_STATE_PLAYER_T] = { "entity_state_player_t", ent_fields, NUM_FIELDS( ent_fields ) },
|
||||
[DT_CUSTOM_ENTITY_STATE_T] = { "custom_entity_state_t", ent_fields, NUM_FIELDS( ent_fields ) },
|
||||
{ NULL },
|
||||
#if XASH_ENGINE_TESTS
|
||||
[DT_DELTA_TEST_STRUCT_T] = { "delta_test_struct_t", test_fields, NUM_FIELDS( test_fields ) },
|
||||
#endif
|
||||
[DT_STRUCT_COUNT] = { NULL },
|
||||
};
|
||||
|
||||
static delta_info_t *Delta_FindStruct( const char *name )
|
||||
@@ -411,9 +455,7 @@ static void Delta_CustomEncode( delta_info_t *dt, const void *from, const void *
|
||||
dt->pFields[i].bInactive = false;
|
||||
|
||||
if( dt->userCallback )
|
||||
{
|
||||
dt->userCallback( dt->pFields, from, to );
|
||||
}
|
||||
}
|
||||
|
||||
static delta_field_t *Delta_FindFieldInfo( const delta_field_t *pInfo, const char *fieldName )
|
||||
@@ -974,14 +1016,14 @@ static qboolean Delta_CompareField( delta_t *pField, void *from, void *to, doubl
|
||||
toF = *(uint8_t *)((int8_t *)to + pField->offset );
|
||||
}
|
||||
|
||||
if( !Q_equal(pField->multiplier, 1.0f ))
|
||||
{
|
||||
fromF *= pField->multiplier;
|
||||
toF *= pField->multiplier;
|
||||
}
|
||||
|
||||
fromF = Delta_ClampIntegerField( pField, fromF, signbit, pField->bits );
|
||||
toF = Delta_ClampIntegerField( pField, toF, signbit, pField->bits );
|
||||
|
||||
if( !Q_equal(pField->multiplier, 1.0f ))
|
||||
fromF *= pField->multiplier;
|
||||
|
||||
if( !Q_equal( pField->multiplier, 1.0f ))
|
||||
toF *= pField->multiplier;
|
||||
}
|
||||
else if( pField->flags & DT_SHORT )
|
||||
{
|
||||
@@ -996,14 +1038,14 @@ static qboolean Delta_CompareField( delta_t *pField, void *from, void *to, doubl
|
||||
toF = *(uint16_t *)((int8_t *)to + pField->offset );
|
||||
}
|
||||
|
||||
if( !Q_equal(pField->multiplier, 1.0f ))
|
||||
{
|
||||
fromF *= pField->multiplier;
|
||||
toF *= pField->multiplier;
|
||||
}
|
||||
|
||||
fromF = Delta_ClampIntegerField( pField, fromF, signbit, pField->bits );
|
||||
toF = Delta_ClampIntegerField( pField, toF, signbit, pField->bits );
|
||||
|
||||
if( !Q_equal(pField->multiplier, 1.0f ))
|
||||
fromF *= pField->multiplier;
|
||||
|
||||
if( !Q_equal( pField->multiplier, 1.0f ))
|
||||
toF *= pField->multiplier;
|
||||
}
|
||||
else if( pField->flags & DT_INTEGER )
|
||||
{
|
||||
@@ -1017,14 +1059,15 @@ static qboolean Delta_CompareField( delta_t *pField, void *from, void *to, doubl
|
||||
fromF = *(uint32_t *)((int8_t *)from + pField->offset );
|
||||
toF = *(uint32_t *)((int8_t *)to + pField->offset );
|
||||
}
|
||||
fromF = Delta_ClampIntegerField( pField, fromF, signbit, pField->bits );
|
||||
toF = Delta_ClampIntegerField( pField, toF, signbit, pField->bits );
|
||||
|
||||
if( !Q_equal(pField->multiplier, 1.0f ))
|
||||
{
|
||||
fromF *= pField->multiplier;
|
||||
|
||||
if( !Q_equal( pField->multiplier, 1.0f ))
|
||||
toF *= pField->multiplier;
|
||||
}
|
||||
|
||||
fromF = Delta_ClampIntegerField( pField, fromF, signbit, pField->bits );
|
||||
toF = Delta_ClampIntegerField( pField, toF, signbit, pField->bits );
|
||||
}
|
||||
else if( pField->flags & ( DT_ANGLE|DT_FLOAT ))
|
||||
{
|
||||
@@ -1034,33 +1077,17 @@ static qboolean Delta_CompareField( delta_t *pField, void *from, void *to, doubl
|
||||
}
|
||||
else if( pField->flags & DT_TIMEWINDOW_8 )
|
||||
{
|
||||
val_a = Q_rint((*(float *)((byte *)from + pField->offset )) * 100.0f );
|
||||
val_b = Q_rint((*(float *)((byte *)to + pField->offset )) * 100.0f );
|
||||
val_a -= Q_rint(timebase * 100.0);
|
||||
val_b -= Q_rint(timebase * 100.0);
|
||||
fromF = FloatAsInt( val_a );
|
||||
toF = FloatAsInt( val_b );
|
||||
val_a = *(float *)((byte *)from + pField->offset );
|
||||
val_b = *(float *)((byte *)to + pField->offset );
|
||||
fromF = Q_rint( val_a * 100.0 );
|
||||
toF = Q_rint( val_b * 100.0 );
|
||||
}
|
||||
else if( pField->flags & DT_TIMEWINDOW_BIG )
|
||||
{
|
||||
val_a = (*(float *)((byte *)from + pField->offset ));
|
||||
val_b = (*(float *)((byte *)to + pField->offset ));
|
||||
|
||||
if( !Q_equal( pField->multiplier, 1.0f ))
|
||||
{
|
||||
val_a *= pField->multiplier;
|
||||
val_b *= pField->multiplier;
|
||||
val_a = (timebase * pField->multiplier) - val_a;
|
||||
val_b = (timebase * pField->multiplier) - val_b;
|
||||
}
|
||||
else
|
||||
{
|
||||
val_a = timebase - val_a;
|
||||
val_b = timebase - val_b;
|
||||
}
|
||||
|
||||
fromF = FloatAsInt( val_a );
|
||||
toF = FloatAsInt( val_b );
|
||||
val_a = *(float *)((byte *)from + pField->offset );
|
||||
val_b = *(float *)((byte *)to + pField->offset );
|
||||
fromF = Q_rint( val_a * pField->multiplier );
|
||||
toF = Q_rint( val_b * pField->multiplier );
|
||||
}
|
||||
else if( pField->flags & DT_STRING )
|
||||
{
|
||||
@@ -1144,6 +1171,7 @@ static qboolean Delta_WriteField( sizebuf_t *msg, delta_t *pField, void *from, v
|
||||
int signbit = FBitSet( pField->flags, DT_SIGNED ) ? 1 : 0;
|
||||
float flValue, flAngle;
|
||||
uint iValue;
|
||||
int dt;
|
||||
const char *pStr;
|
||||
|
||||
if( Delta_CompareField( pField, from, to, timebase ))
|
||||
@@ -1160,11 +1188,11 @@ static qboolean Delta_WriteField( sizebuf_t *msg, delta_t *pField, void *from, v
|
||||
iValue = *(int8_t *)((int8_t *)to + pField->offset );
|
||||
else
|
||||
iValue = *(uint8_t *)((int8_t *)to + pField->offset );
|
||||
iValue = Delta_ClampIntegerField( pField, iValue, signbit, pField->bits );
|
||||
|
||||
if( !Q_equal( pField->multiplier, 1.0 ) )
|
||||
if( !Q_equal( pField->multiplier, 1.0 ))
|
||||
iValue *= pField->multiplier;
|
||||
|
||||
iValue = Delta_ClampIntegerField( pField, iValue, signbit, pField->bits );
|
||||
MSG_WriteBitLong( msg, iValue, pField->bits, signbit );
|
||||
}
|
||||
else if( pField->flags & DT_SHORT )
|
||||
@@ -1173,11 +1201,11 @@ static qboolean Delta_WriteField( sizebuf_t *msg, delta_t *pField, void *from, v
|
||||
iValue = *(int16_t *)((int8_t *)to + pField->offset );
|
||||
else
|
||||
iValue = *(uint16_t *)((int8_t *)to + pField->offset );
|
||||
iValue = Delta_ClampIntegerField( pField, iValue, signbit, pField->bits );
|
||||
|
||||
if( !Q_equal( pField->multiplier, 1.0 ) )
|
||||
if( !Q_equal( pField->multiplier, 1.0 ))
|
||||
iValue *= pField->multiplier;
|
||||
|
||||
|
||||
iValue = Delta_ClampIntegerField( pField, iValue, signbit, pField->bits );
|
||||
MSG_WriteBitLong( msg, iValue, pField->bits, signbit );
|
||||
}
|
||||
else if( pField->flags & DT_INTEGER )
|
||||
@@ -1186,11 +1214,11 @@ static qboolean Delta_WriteField( sizebuf_t *msg, delta_t *pField, void *from, v
|
||||
iValue = *(int32_t *)((int8_t *)to + pField->offset );
|
||||
else
|
||||
iValue = *(uint32_t *)((int8_t *)to + pField->offset );
|
||||
iValue = Delta_ClampIntegerField( pField, iValue, signbit, pField->bits );
|
||||
|
||||
if( !Q_equal( pField->multiplier, 1.0 ) )
|
||||
if( !Q_equal( pField->multiplier, 1.0 ))
|
||||
iValue *= pField->multiplier;
|
||||
|
||||
iValue = Delta_ClampIntegerField( pField, iValue, signbit, pField->bits );
|
||||
MSG_WriteBitLong( msg, iValue, pField->bits, signbit );
|
||||
}
|
||||
else if( pField->flags & DT_FLOAT )
|
||||
@@ -1210,19 +1238,17 @@ static qboolean Delta_WriteField( sizebuf_t *msg, delta_t *pField, void *from, v
|
||||
}
|
||||
else if( pField->flags & DT_TIMEWINDOW_8 )
|
||||
{
|
||||
signbit = 1; // timewindow is always signed
|
||||
flValue = *(float *)((byte *)to + pField->offset );
|
||||
iValue = (int)Q_rint( timebase * 100.0 ) - (int)Q_rint( flValue * 100.0 );
|
||||
iValue = Delta_ClampIntegerField( pField, iValue, signbit, pField->bits );
|
||||
MSG_WriteBitLong( msg, iValue, pField->bits, signbit );
|
||||
dt = Q_rint(( timebase - flValue ) * 100.0 );
|
||||
dt = Delta_ClampIntegerField( pField, dt, 1, pField->bits );
|
||||
MSG_WriteSBitLong( msg, dt, pField->bits );
|
||||
}
|
||||
else if( pField->flags & DT_TIMEWINDOW_BIG )
|
||||
{
|
||||
signbit = 1; // timewindow is always signed
|
||||
flValue = *(float *)((byte *)to + pField->offset );
|
||||
iValue = (int)Q_rint( timebase * pField->multiplier ) - (int)Q_rint( flValue * pField->multiplier );
|
||||
iValue = Delta_ClampIntegerField( pField, iValue, signbit, pField->bits );
|
||||
MSG_WriteBitLong( msg, iValue, pField->bits, signbit );
|
||||
dt = Q_rint(( timebase - flValue ) * pField->multiplier );
|
||||
dt = Delta_ClampIntegerField( pField, dt, 1, pField->bits );
|
||||
MSG_WriteSBitLong( msg, dt, pField->bits );
|
||||
}
|
||||
else if( pField->flags & DT_STRING )
|
||||
{
|
||||
@@ -1306,9 +1332,12 @@ static qboolean Delta_ReadField( sizebuf_t *msg, delta_t *pField, void *from, vo
|
||||
if( pField->flags & DT_BYTE )
|
||||
{
|
||||
iValue = MSG_ReadBitLong( msg, pField->bits, bSigned );
|
||||
if( !Q_equal( pField->multiplier, 1.0 ) )
|
||||
if( !Q_equal( pField->multiplier, 1.0 ))
|
||||
iValue /= pField->multiplier;
|
||||
|
||||
if( !Q_equal( pField->post_multiplier, 1.0 ))
|
||||
iValue *= pField->post_multiplier;
|
||||
|
||||
if( bSigned )
|
||||
*(int8_t *)((uint8_t *)to + pField->offset ) = iValue;
|
||||
else
|
||||
@@ -1317,9 +1346,12 @@ static qboolean Delta_ReadField( sizebuf_t *msg, delta_t *pField, void *from, vo
|
||||
else if( pField->flags & DT_SHORT )
|
||||
{
|
||||
iValue = MSG_ReadBitLong( msg, pField->bits, bSigned );
|
||||
if( !Q_equal( pField->multiplier, 1.0 ) )
|
||||
if( !Q_equal( pField->multiplier, 1.0 ))
|
||||
iValue /= pField->multiplier;
|
||||
|
||||
if( !Q_equal( pField->post_multiplier, 1.0 ))
|
||||
iValue *= pField->post_multiplier;
|
||||
|
||||
if( bSigned )
|
||||
*(int16_t *)((uint8_t *)to + pField->offset ) = iValue;
|
||||
else
|
||||
@@ -1328,9 +1360,12 @@ static qboolean Delta_ReadField( sizebuf_t *msg, delta_t *pField, void *from, vo
|
||||
else if( pField->flags & DT_INTEGER )
|
||||
{
|
||||
iValue = MSG_ReadBitLong( msg, pField->bits, bSigned );
|
||||
if( !Q_equal( pField->multiplier, 1.0 ) )
|
||||
if( !Q_equal( pField->multiplier, 1.0 ))
|
||||
iValue /= pField->multiplier;
|
||||
|
||||
if( !Q_equal( pField->post_multiplier, 1.0 ))
|
||||
iValue *= pField->post_multiplier;
|
||||
|
||||
if( bSigned )
|
||||
*(int32_t *)((uint8_t *)to + pField->offset ) = iValue;
|
||||
else
|
||||
@@ -1344,11 +1379,11 @@ static qboolean Delta_ReadField( sizebuf_t *msg, delta_t *pField, void *from, vo
|
||||
else
|
||||
flValue = iValue;
|
||||
|
||||
if( !Q_equal( pField->multiplier, 1.0 ) )
|
||||
flValue = flValue / pField->multiplier;
|
||||
if( !Q_equal( pField->multiplier, 1.0 ))
|
||||
flValue /= pField->multiplier;
|
||||
|
||||
if( !Q_equal( pField->post_multiplier, 1.0 ) )
|
||||
flValue = flValue * pField->post_multiplier;
|
||||
if( !Q_equal( pField->post_multiplier, 1.0 ))
|
||||
flValue *= pField->post_multiplier;
|
||||
|
||||
*(float *)((byte *)to + pField->offset ) = flValue;
|
||||
}
|
||||
@@ -1359,22 +1394,14 @@ static qboolean Delta_ReadField( sizebuf_t *msg, delta_t *pField, void *from, vo
|
||||
}
|
||||
else if( pField->flags & DT_TIMEWINDOW_8 )
|
||||
{
|
||||
bSigned = true; // timewindow is always signed
|
||||
iValue = MSG_ReadBitLong( msg, pField->bits, bSigned );
|
||||
flTime = (timebase * 100.0 - (int)iValue) / 100.0;
|
||||
|
||||
iValue = MSG_ReadSBitLong( msg, pField->bits );
|
||||
flTime = ( timebase * 100.0 - (int)iValue ) / 100.0;
|
||||
*(float *)((byte *)to + pField->offset ) = flTime;
|
||||
}
|
||||
else if( pField->flags & DT_TIMEWINDOW_BIG )
|
||||
{
|
||||
bSigned = true; // timewindow is always signed
|
||||
iValue = MSG_ReadBitLong( msg, pField->bits, bSigned );
|
||||
|
||||
if( !Q_equal( pField->multiplier, 1.0 ) )
|
||||
flTime = ( timebase * pField->multiplier - (int)iValue ) / pField->multiplier;
|
||||
else
|
||||
flTime = timebase - (int)iValue;
|
||||
|
||||
iValue = MSG_ReadSBitLong( msg, pField->bits );
|
||||
flTime = ( timebase * pField->multiplier - (int)iValue ) / pField->multiplier;
|
||||
*(float *)((byte *)to + pField->offset ) = flTime;
|
||||
}
|
||||
else if( pField->flags & DT_STRING )
|
||||
@@ -2069,3 +2096,103 @@ void GAME_EXPORT Delta_UnsetFieldByIndex( delta_t *pFields, int fieldNumber )
|
||||
|
||||
dt->pFields[fieldNumber].bInactive = true;
|
||||
}
|
||||
|
||||
#if XASH_ENGINE_TESTS
|
||||
#include "tests.h"
|
||||
|
||||
void Test_RunDelta( void )
|
||||
{
|
||||
delta_info_t *dt = &dt_info[DT_DELTA_TEST_STRUCT_T];
|
||||
delta_test_struct_t from, to = { 0 };
|
||||
delta_test_struct_t null = { 0 };
|
||||
sizebuf_t msg;
|
||||
int i;
|
||||
char buffer[4096] = { 0 };
|
||||
const double timebase = 123.123;
|
||||
|
||||
// a1ba: netbuffer bitmasks are initialized in netchan for some reason
|
||||
// initialize it ourselves just in case
|
||||
MSG_InitMasks(); // initialize bit-masks
|
||||
|
||||
Delta_AddField( dt, "dt_string", DT_STRING, 1, 1.0f, 1.0f );
|
||||
Delta_AddField( dt, "dt_timewindow_big", DT_TIMEWINDOW_BIG, 24, 1000.f, 1.0f );
|
||||
Delta_AddField( dt, "dt_timewindow_8", DT_TIMEWINDOW_8, 8, 1.0f, 1.0f );
|
||||
Delta_AddField( dt, "dt_angle", DT_ANGLE, 16, 1.0f, 1.0f );
|
||||
Delta_AddField( dt, "dt_float_signed", DT_FLOAT | DT_SIGNED, 22, 100.0f, 1.0f );
|
||||
Delta_AddField( dt, "dt_float_unsigned", DT_FLOAT, 24, 10000.0f, 0.1f );
|
||||
Delta_AddField( dt, "dt_integer_signed", DT_INTEGER | DT_SIGNED, 24, 1.0f, 1.0f );
|
||||
Delta_AddField( dt, "dt_integer_unsigned", DT_INTEGER, 24, 1.0f, 1.0f );
|
||||
Delta_AddField( dt, "dt_short_signed", DT_SHORT | DT_SIGNED, 16, 1.0f, 1.0f );
|
||||
Delta_AddField( dt, "dt_short_unsigned", DT_SHORT, 15, 0.125f, 1.0f );
|
||||
Delta_AddField( dt, "dt_byte_signed", DT_BYTE | DT_SIGNED, 6, 1.0f, 1.0f );
|
||||
Delta_AddField( dt, "dt_byte_unsigned", DT_BYTE, 8, 1.0f, 1.0f );
|
||||
|
||||
Q_strncpy( from.dt_string, "test data check it's the same", sizeof( from.dt_string ));
|
||||
from.dt_timewindow_big = timebase + 2.3456;
|
||||
from.dt_timewindow_8 = timebase + 0.0234;
|
||||
from.dt_angle = 160.245f;
|
||||
from.dt_float_signed = -15.123f;
|
||||
from.dt_float_unsigned = 1235.321f;
|
||||
from.dt_integer_signed = -412784;
|
||||
from.dt_integer_unsigned = 123453;
|
||||
from.dt_short_signed = -12343;
|
||||
from.dt_short_unsigned = 32131;
|
||||
from.dt_byte_signed = 16;
|
||||
from.dt_byte_unsigned = 218;
|
||||
|
||||
MSG_Init( &msg, "test message", buffer, sizeof( buffer ));
|
||||
|
||||
for( i = 0; i < dt->numFields; i++ )
|
||||
Delta_WriteField( &msg, &dt->pFields[i], &null, &from, timebase );
|
||||
|
||||
MSG_SeekToBit( &msg, 0, SEEK_SET );
|
||||
|
||||
for( i = 0; i < dt->numFields; i++ )
|
||||
Delta_ReadField( &msg, &dt->pFields[i], &null, &to, timebase );
|
||||
|
||||
Con_Printf( "struct as encoded to delta:\n" );
|
||||
TASSERT_STR( from.dt_string, to.dt_string );
|
||||
|
||||
// the epsilon value is derived from multiplier value
|
||||
TASSERT( Q_equal_e( from.dt_timewindow_big, to.dt_timewindow_big, 0.001f ));
|
||||
|
||||
// dt_timewindow_8 type has multiplier locked at 100.0f
|
||||
TASSERT( Q_equal_e( from.dt_timewindow_8, to.dt_timewindow_8, 0.01f ));
|
||||
TASSERT( Q_equal_e( from.dt_angle, to.dt_angle, 0.1f ));
|
||||
TASSERT( Q_equal_e( from.dt_float_signed, to.dt_float_signed, 0.01f ));
|
||||
|
||||
// dt_float_unsigned has post-multiplier that doesn't affect network data
|
||||
// and therefore should be reverted back when comparing
|
||||
TASSERT( Q_equal_e( from.dt_float_unsigned, to.dt_float_unsigned * 10.f , 0.01f ));
|
||||
|
||||
TASSERT_EQi( from.dt_integer_signed, to.dt_integer_signed );
|
||||
TASSERT_EQi( from.dt_integer_unsigned, to.dt_integer_unsigned );
|
||||
TASSERT_EQi( from.dt_short_signed, to.dt_short_signed );
|
||||
TASSERT(( from.dt_short_unsigned & ( 0xffff << 3 )) == to.dt_short_unsigned );
|
||||
TASSERT_EQi( from.dt_byte_signed, to.dt_byte_signed );
|
||||
TASSERT_EQi( from.dt_byte_unsigned, to.dt_byte_unsigned );
|
||||
|
||||
Con_Printf( "from.dt_timewindow_big = %f\n", from.dt_timewindow_big );
|
||||
Con_Printf( "to.dt_timewindow_big = %f\n", to.dt_timewindow_big );
|
||||
Con_Printf( "from.dt_timewindow_8 = %f\n", from.dt_timewindow_8 );
|
||||
Con_Printf( "to.dt_timewindow_8 = %f\n", to.dt_timewindow_8 );
|
||||
Con_Printf( "from.dt_angle = %f\n", from.dt_angle );
|
||||
Con_Printf( "to.dt_angle = %f\n", to.dt_angle );
|
||||
Con_Printf( "from.dt_float_signed = %f\n", from.dt_float_signed );
|
||||
Con_Printf( "to.dt_float_signed = %f\n", to.dt_float_signed );
|
||||
Con_Printf( "from.dt_float_unsigned = %f\n", from.dt_float_unsigned );
|
||||
Con_Printf( "to.dt_float_unsigned = %f\n", to.dt_float_unsigned );
|
||||
Con_Printf( "from.dt_integer_signed = %i\n", from.dt_integer_signed );
|
||||
Con_Printf( "to.dt_integer_signed = %i\n", to.dt_integer_signed );
|
||||
Con_Printf( "from.dt_integer_unsigned = %i\n", from.dt_integer_unsigned );
|
||||
Con_Printf( "to.dt_integer_unsigned = %i\n", to.dt_integer_unsigned );
|
||||
Con_Printf( "from.dt_short_signed = %i\n", from.dt_short_signed );
|
||||
Con_Printf( "to.dt_short_signed = %i\n", to.dt_short_signed );
|
||||
Con_Printf( "from.dt_short_unsigned = %i\n", from.dt_short_unsigned );
|
||||
Con_Printf( "to.dt_short_unsigned = %i\n", to.dt_short_unsigned );
|
||||
Con_Printf( "from.dt_byte_signed = %i\n", from.dt_byte_signed );
|
||||
Con_Printf( "to.dt_byte_signed = %i\n", to.dt_byte_signed );
|
||||
Con_Printf( "from.dt_byte_unsigned = %i\n", from.dt_byte_unsigned );
|
||||
Con_Printf( "to.dt_byte_unsigned = %i\n", to.dt_byte_unsigned );
|
||||
}
|
||||
#endif // XASH_ENGINE_TESTS
|
||||
|
||||
@@ -127,7 +127,7 @@ GNU General Public License for more details.
|
||||
#define MAX_CUSTOM (1<<MAX_CUSTOM_BITS)// 10 bits == 1024 generic file
|
||||
#define MAX_USER_MESSAGES 197 // another 58 messages reserved for engine routines
|
||||
#define MAX_DLIGHTS 32 // dynamic lights (rendered per one frame)
|
||||
#define MAX_ELIGHTS 64 // entity only point lights
|
||||
#define MAX_ELIGHTS 128 // a1ba: increased from 64 to 128, entity only point lights
|
||||
#define MAX_LIGHTSTYLES 256 // a1ba: increased from 64 to 256, protocol limit
|
||||
#define MAX_RENDER_DECALS 4096 // max rendering decals per a level
|
||||
|
||||
@@ -278,6 +278,8 @@ GNU General Public License for more details.
|
||||
#define SU_WEAPON (1<<14)
|
||||
|
||||
extern const char *svc_strings[svc_lastmsg+1];
|
||||
extern const char *svc_legacy_strings[svc_lastmsg+1];
|
||||
extern const char *svc_goldsrc_strings[svc_lastmsg+1];
|
||||
extern const char *clc_strings[clc_lastmsg+1];
|
||||
|
||||
// FWGS extensions
|
||||
@@ -316,4 +318,40 @@ extern const char *clc_strings[clc_lastmsg+1];
|
||||
// Master Server protocol
|
||||
#define MS_SCAN_REQUEST "1\xFF" "0.0.0.0:0\0" // TODO: implement IP filter
|
||||
|
||||
// GoldSrc protocol definitions
|
||||
#define PROTOCOL_GOLDSRC_VERSION_REAL 48
|
||||
#define PROTOCOL_GOLDSRC_VERSION (PROTOCOL_GOLDSRC_VERSION_REAL | (BIT( 7 ))) // should be 48, only to differentiate it from PROTOCOL_LEGACY_VERSION
|
||||
|
||||
#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_damage svc_restoresound
|
||||
#define svc_goldsrc_killedmonster 27
|
||||
#define svc_goldsrc_foundsecret 28
|
||||
#define svc_goldsrc_spawnstaticsound 29
|
||||
#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_timescale 55
|
||||
#define svc_goldsrc_sendcvarvalue svc_querycvarvalue
|
||||
#define svc_goldsrc_sendcvarvalue2 svc_querycvarvalue2
|
||||
|
||||
#define clc_goldsrc_hltv clc_requestcvarvalue // 9
|
||||
#define clc_goldsrc_requestcvarvalue clc_requestcvarvalue2 // 10
|
||||
#define clc_goldsrc_requestcvarvalue2 11
|
||||
#define clc_goldsrc_lastmsg 12
|
||||
|
||||
#define S2C_REJECT_BADPASSWORD '8'
|
||||
#define S2C_REJECT '9'
|
||||
#define S2C_CHALLENGE "A00000000"
|
||||
#define S2C_CONNECTION "B"
|
||||
|
||||
#define MAX_GOLDSRC_RESOURCE_BITS 12
|
||||
#define MAX_GOLDSRC_ENTITY_BITS 11
|
||||
// #define MAX_GOLDSRC_EDICTS BIT( MAX_ENTITY_BITS )
|
||||
#define MAX_GOLDSRC_EDICTS ( BIT( MAX_ENTITY_BITS ) + ( MAX_CLIENTS * 15 ))
|
||||
#define LAST_GOLDSRC_EDICT ( BIT( MAX_ENTITY_BITS ) - 1 )
|
||||
|
||||
#endif//NET_PROTOCOL_H
|
||||
|
||||
@@ -216,7 +216,7 @@ wavdata_t *FS_StreamInfo( stream_t *stream )
|
||||
if( !stream ) return NULL;
|
||||
|
||||
// fill structure
|
||||
info.loopStart = -1;
|
||||
info.loopStart = 0;
|
||||
info.rate = stream->rate;
|
||||
info.width = stream->width;
|
||||
info.channels = stream->channels;
|
||||
|
||||
@@ -74,7 +74,10 @@ static uint32_t Sound_ParseSynchInteger( uint32_t v )
|
||||
static void Sound_HandleCustomID3Comment( const char *key, const char *value )
|
||||
{
|
||||
if( !Q_strcmp( key, "LOOP_START" ) || !Q_strcmp( key, "LOOPSTART" ))
|
||||
{
|
||||
sound.loopstart = Q_atoi( value );
|
||||
SetBits( sound.flags, SOUND_LOOPED );
|
||||
}
|
||||
// unknown comment is not an error
|
||||
}
|
||||
|
||||
@@ -233,7 +236,6 @@ qboolean Sound_LoadMPG( const char *name, const byte *buffer, fs_offset_t filesi
|
||||
sound.channels = sc.channels;
|
||||
sound.rate = sc.rate;
|
||||
sound.width = 2; // always 16-bit PCM
|
||||
sound.loopstart = -1;
|
||||
sound.size = ( sound.channels * sound.rate * sound.width ) * ( sc.playtime / 1000 ); // in bytes
|
||||
padsize = sound.size % FRAME_SIZE;
|
||||
pos += FRAME_SIZE; // evaluate pos
|
||||
@@ -241,7 +243,6 @@ qboolean Sound_LoadMPG( const char *name, const byte *buffer, fs_offset_t filesi
|
||||
if( !Sound_ParseID3Tag( buffer, filesize ))
|
||||
{
|
||||
Con_DPrintf( S_WARN "Sound_LoadMPG: (%s) failed to extract LOOP_START tag\n", name );
|
||||
sound.loopstart = -1;
|
||||
}
|
||||
|
||||
if( !sound.size )
|
||||
|
||||
@@ -14,6 +14,9 @@ GNU General Public License for more details.
|
||||
*/
|
||||
|
||||
#include "soundlib.h"
|
||||
#if XASH_SDL
|
||||
#include <SDL_audio.h>
|
||||
#endif // XASH_SDL
|
||||
|
||||
/*
|
||||
=============================================================================
|
||||
@@ -130,7 +133,309 @@ uint GAME_EXPORT Sound_GetApproxWavePlayLen( const char *filepath )
|
||||
return msecs;
|
||||
}
|
||||
|
||||
#define drint( v ) (int)( v + 0.5 )
|
||||
static qboolean Sound_ConvertNoResample( wavdata_t *sc, int inwidth, int outwidth, int outcount )
|
||||
{
|
||||
size_t i;
|
||||
|
||||
if( inwidth == 1 && outwidth == 2 ) // S8 to S16
|
||||
{
|
||||
for( i = 0; i < outcount * sc->channels; i++ )
|
||||
((int16_t*)sound.tempbuffer)[i] = ((int8_t *)sc->buffer)[i] * 256;
|
||||
return true;
|
||||
}
|
||||
|
||||
if( inwidth == 2 && outwidth == 1 ) // S16 to S8
|
||||
{
|
||||
for( i = 0; i < outcount * sc->channels; i++ )
|
||||
((int8_t*)sound.tempbuffer)[i] = ((int16_t *)sc->buffer)[i] / 256;
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
static qboolean Sound_ConvertDownsample( wavdata_t *sc, int inwidth, int outwidth, int outcount, double stepscale )
|
||||
{
|
||||
size_t i;
|
||||
double j;
|
||||
|
||||
if( inwidth == 1 && outwidth == 1 )
|
||||
{
|
||||
int8_t *data = (int8_t *)sc->buffer;
|
||||
|
||||
if( outwidth == 1 )
|
||||
{
|
||||
int8_t *outdata = (int8_t *)sound.tempbuffer;
|
||||
|
||||
if( sc->channels == 2 )
|
||||
{
|
||||
for( i = 0; i < outcount; i++ )
|
||||
{
|
||||
j = stepscale * i;
|
||||
outdata[i*2+0] = data[((int)j)*2+0];
|
||||
outdata[i*2+1] = data[((int)j)*2+1];
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
for( i = 0; i < outcount; i++ )
|
||||
{
|
||||
j = stepscale * i;
|
||||
outdata[i] = data[(int)j];
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
if( outwidth == 2 )
|
||||
{
|
||||
int16_t *outdata = (int16_t *)sound.tempbuffer;
|
||||
|
||||
if( sc->channels == 2 )
|
||||
{
|
||||
for( i = 0; i < outcount; i++ )
|
||||
{
|
||||
j = stepscale * i;
|
||||
outdata[i*2+0] = data[((int)j)*2+0] * 256;
|
||||
outdata[i*2+1] = data[((int)j)*2+1] * 256;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
for( i = 0; i < outcount; i++ )
|
||||
{
|
||||
j = stepscale * i;
|
||||
outdata[i] = data[(int)j] * 256;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
if( inwidth == 2 )
|
||||
{
|
||||
int16_t *data = (int16_t *)sc->buffer;
|
||||
|
||||
if( outwidth == 1 )
|
||||
{
|
||||
int8_t *outdata = (int8_t *)sound.tempbuffer;
|
||||
|
||||
if( sc->channels == 2 )
|
||||
{
|
||||
for( i = 0; i < outcount; i++ )
|
||||
{
|
||||
j = stepscale * i;
|
||||
outdata[i*2+0] = data[((int)j)*2+0] / 256;
|
||||
outdata[i*2+1] = data[((int)j)*2+1] / 256;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
for( i = 0; i < outcount; i++ )
|
||||
{
|
||||
j = stepscale * i;
|
||||
outdata[i] = data[(int)j] / 256;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
if( outwidth == 2 )
|
||||
{
|
||||
int16_t *outdata = (int16_t *)sound.tempbuffer;
|
||||
|
||||
if( sc->channels == 2 )
|
||||
{
|
||||
for( i = 0; i < outcount; i++ )
|
||||
{
|
||||
j = stepscale * i;
|
||||
outdata[i*2+0] = data[((int)j)*2+0];
|
||||
outdata[i*2+1] = data[((int)j)*2+1];
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
for( i = 0; i < outcount; i++ )
|
||||
{
|
||||
j = stepscale * i;
|
||||
outdata[i] = data[(int)j];
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
static qboolean Sound_ConvertUpsample( wavdata_t *sc, int inwidth, int outwidth, int outcount, int incount, double stepscale )
|
||||
{
|
||||
size_t i;
|
||||
double j;
|
||||
double frac;
|
||||
|
||||
incount--; // to not go past last sample while interpolating
|
||||
|
||||
if( inwidth == 1 )
|
||||
{
|
||||
int8_t *data = (int8_t *)sc->buffer;
|
||||
|
||||
if( outwidth == 1 )
|
||||
{
|
||||
int8_t *outdata = (int8_t *)sound.tempbuffer;
|
||||
|
||||
if( sc->channels == 2 )
|
||||
{
|
||||
for( i = 0; i < outcount; i++ )
|
||||
{
|
||||
j = stepscale * i;
|
||||
outdata[i*2+0] = data[((int)j)*2+0];
|
||||
outdata[i*2+1] = data[((int)j)*2+1];
|
||||
if( j != (int)j && j < incount )
|
||||
{
|
||||
frac = j - (int)j;
|
||||
outdata[i*2+0] += (data[((int)j+1)*2+0] - data[((int)j)*2+0]) * frac;
|
||||
outdata[i*2+1] += (data[((int)j+1)*2+1] - data[((int)j)*2+1]) * frac;
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
for( i = 0; i < outcount; i++ )
|
||||
{
|
||||
j = stepscale * i;
|
||||
outdata[i] = data[(int)j];
|
||||
if( j != (int)j && j < incount )
|
||||
{
|
||||
frac = j - (int)j;
|
||||
outdata[i] += (data[(int)j+1] - data[(int)j]) * frac;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
if( outwidth == 2 )
|
||||
{
|
||||
int16_t *outdata = (int16_t *)sound.tempbuffer;
|
||||
|
||||
if( sc->channels == 2 )
|
||||
{
|
||||
for( i = 0; i < outcount; i++ )
|
||||
{
|
||||
j = stepscale * i;
|
||||
outdata[i*2+0] = data[((int)j)*2+0] * 256;
|
||||
outdata[i*2+1] = data[((int)j)*2+1] * 256;
|
||||
if( j != (int)j && j < incount )
|
||||
{
|
||||
frac = ( j - (int)j ) * 256;
|
||||
outdata[i*2+0] += (data[((int)j+1)*2+0] - data[((int)j)*2+0]) * frac;
|
||||
outdata[i*2+1] += (data[((int)j+1)*2+1] - data[((int)j)*2+1]) * frac;
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
for( i = 0; i < outcount; i++ )
|
||||
{
|
||||
j = stepscale * i;
|
||||
outdata[i] = data[(int)j] * 256;
|
||||
if( j != (int)j && j < incount )
|
||||
{
|
||||
frac = ( j - (int)j ) * 256;
|
||||
outdata[i] += (data[(int)j+1] - data[(int)j]) * frac;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
if( inwidth == 2 )
|
||||
{
|
||||
int16_t *data = (int16_t *)sc->buffer;
|
||||
|
||||
if( outwidth == 1 )
|
||||
{
|
||||
int8_t *outdata = (int8_t *)sound.tempbuffer;
|
||||
|
||||
if( sc->channels == 2 )
|
||||
{
|
||||
for( i = 0; i < outcount; i++ )
|
||||
{
|
||||
j = stepscale * i;
|
||||
outdata[i*2+0] = data[((int)j)*2+0] / 256;
|
||||
outdata[i*2+1] = data[((int)j)*2+1] / 256;
|
||||
if( j != (int)j && j < incount )
|
||||
{
|
||||
frac = ( j - (int)j ) / 256;
|
||||
outdata[i*2+0] += (data[((int)j+1)*2+0] - data[((int)j)*2+0]) * frac;
|
||||
outdata[i*2+1] += (data[((int)j+1)*2+1] - data[((int)j)*2+1]) * frac;
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
for( i = 0; i < outcount; i++ )
|
||||
{
|
||||
j = stepscale * i;
|
||||
outdata[i] = data[(int)j] / 256;
|
||||
if( j != (int)j && j < incount )
|
||||
{
|
||||
frac = ( j - (int)j ) / 256;
|
||||
outdata[i] += (data[(int)j+1] - data[(int)j]) * frac;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
if( outwidth == 2 )
|
||||
{
|
||||
int16_t *outdata = (int16_t *)sound.tempbuffer;
|
||||
|
||||
if( sc->channels == 2 )
|
||||
{
|
||||
for( i = 0; i < outcount; i++ )
|
||||
{
|
||||
j = stepscale * i;
|
||||
outdata[i*2+0] = data[((int)j)*2+0];
|
||||
outdata[i*2+1] = data[((int)j)*2+1];
|
||||
if( j != (int)j && j < incount )
|
||||
{
|
||||
frac = j - (int)j;
|
||||
outdata[i*2+0] += (data[((int)j+1)*2+0] - data[((int)j)*2+0]) * frac;
|
||||
outdata[i*2+1] += (data[((int)j+1)*2+1] - data[((int)j)*2+1]) * frac;
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
for( i = 0; i < outcount; i++ )
|
||||
{
|
||||
j = stepscale * i;
|
||||
outdata[i] = data[(int)j];
|
||||
if( j != (int)j && j < incount )
|
||||
{
|
||||
frac = j - (int)j;
|
||||
outdata[i] += (data[(int)j+1] - data[(int)j]) * frac;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/*
|
||||
================
|
||||
@@ -141,125 +446,70 @@ We need convert sound to signed even if nothing to resample
|
||||
*/
|
||||
static qboolean Sound_ResampleInternal( wavdata_t *sc, int inrate, int inwidth, int outrate, int outwidth )
|
||||
{
|
||||
double stepscale, j;
|
||||
int outcount;
|
||||
int i;
|
||||
const size_t oldsize = sc->size;
|
||||
qboolean handled = false;
|
||||
double stepscale;
|
||||
double t1, t2;
|
||||
int outcount, incount = sc->samples;
|
||||
|
||||
if( inrate == outrate && inwidth == outwidth )
|
||||
return false;
|
||||
|
||||
t1 = Sys_DoubleTime();
|
||||
|
||||
stepscale = (double)inrate / outrate; // this is usually 0.5, 1, or 2
|
||||
outcount = sc->samples / stepscale;
|
||||
sc->size = outcount * outwidth * sc->channels;
|
||||
|
||||
sound.tempbuffer = (byte *)Mem_Realloc( host.soundpool, sound.tempbuffer, sc->size );
|
||||
|
||||
sc->samples = outcount;
|
||||
if( sc->loopStart != -1 )
|
||||
if( FBitSet( sc->flags, SOUND_LOOPED ))
|
||||
sc->loopStart = sc->loopStart / stepscale;
|
||||
|
||||
if( inrate == outrate )
|
||||
#if 0 && XASH_SDL // slow but somewhat accurate
|
||||
{
|
||||
if( inwidth == 1 && outwidth == 2 ) // S8 to S16
|
||||
{
|
||||
for( i = 0; i < outcount * sc->channels; i++ )
|
||||
((int16_t*)sound.tempbuffer)[i] = ((int8_t *)sc->buffer)[i] * 256;
|
||||
handled = true;
|
||||
}
|
||||
else if( inwidth == 2 && outwidth == 1 ) // S16 to S8
|
||||
{
|
||||
for( i = 0; i < outcount * sc->channels; i++ )
|
||||
((int8_t*)sound.tempbuffer)[i] = ((int16_t *)sc->buffer)[i] / 256;
|
||||
handled = true;
|
||||
}
|
||||
}
|
||||
else // resample case
|
||||
{
|
||||
if( inwidth == 1 )
|
||||
{
|
||||
int8_t *data = (int8_t *)sc->buffer;
|
||||
const SDL_AudioFormat infmt = inwidth == 1 ? AUDIO_S8 : AUDIO_S16;
|
||||
const SDL_AudioFormat outfmt = outwidth == 1 ? AUDIO_S8 : AUDIO_S16;
|
||||
SDL_AudioCVT cvt;
|
||||
|
||||
if( outwidth == 1 )
|
||||
{
|
||||
if( sc->channels == 2 )
|
||||
{
|
||||
for( i = 0, j = 0; i < outcount; i++, j += stepscale )
|
||||
{
|
||||
((int8_t*)sound.tempbuffer)[i*2+0] = data[((int)j)*2+0];
|
||||
((int8_t*)sound.tempbuffer)[i*2+1] = data[((int)j)*2+1];
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
for( i = 0, j = 0; i < outcount; i++, j += stepscale )
|
||||
((int8_t*)sound.tempbuffer)[i] = data[(int)j];
|
||||
}
|
||||
handled = true;
|
||||
}
|
||||
else if( outwidth == 2 )
|
||||
{
|
||||
if( sc->channels == 2 )
|
||||
{
|
||||
for( i = 0, j = 0; i < outcount; i++, j += stepscale )
|
||||
{
|
||||
((int16_t*)sound.tempbuffer)[i*2+0] = data[((int)j)*2+0] * 256;
|
||||
((int16_t*)sound.tempbuffer)[i*2+1] = data[((int)j)*2+1] * 256;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
for( i = 0, j = 0; i < outcount; i++, j += stepscale )
|
||||
((int16_t*)sound.tempbuffer)[i] = data[(int)j] * 256;
|
||||
}
|
||||
handled = true;
|
||||
}
|
||||
}
|
||||
else if( inwidth == 2 )
|
||||
// SDL_AudioCVT does conversion in place, original buffer is used for it
|
||||
if( SDL_BuildAudioCVT( &cvt, infmt, sc->channels, inrate, outfmt, sc->channels, outrate ) > 0 && cvt.needed )
|
||||
{
|
||||
int16_t *data = (int16_t *)sc->buffer;
|
||||
sc->buffer = (byte *)Mem_Realloc( host.soundpool, sc->buffer, oldsize * cvt.len_mult );
|
||||
cvt.len = oldsize;
|
||||
cvt.buf = sc->buffer;
|
||||
|
||||
if( outwidth == 1 )
|
||||
if( !SDL_ConvertAudio( &cvt ))
|
||||
{
|
||||
if( sc->channels == 2 )
|
||||
{
|
||||
for( i = 0, j = 0; i < outcount; i++, j += stepscale )
|
||||
{
|
||||
((int8_t*)sound.tempbuffer)[i*2+0] = data[((int)j)*2+0] / 256;
|
||||
((int8_t*)sound.tempbuffer)[i*2+1] = data[((int)j)*2+1] / 256;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
for( i = 0, j = 0; i < outcount; i++, j += stepscale )
|
||||
((int8_t*)sound.tempbuffer)[i] = data[(int)j] / 256;
|
||||
}
|
||||
handled = true;
|
||||
}
|
||||
else if( outwidth == 2 )
|
||||
{
|
||||
if( sc->channels == 2 )
|
||||
{
|
||||
for( i = 0, j = 0; i < outcount; i++, j += stepscale )
|
||||
{
|
||||
((int16_t*)sound.tempbuffer)[i*2+0] = data[((int)j)*2+0];
|
||||
((int16_t*)sound.tempbuffer)[i*2+1] = data[((int)j)*2+1];
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
for( i = 0, j = 0; i < outcount; i++, j += stepscale )
|
||||
((int16_t*)sound.tempbuffer)[i] = data[(int)j];
|
||||
}
|
||||
handled = true;
|
||||
t2 = Sys_DoubleTime();
|
||||
Con_Reportf( "Sound_Resample: from [%d bit %d Hz] to [%d bit %d Hz] (took %.3fs through SDL)\n", inwidth * 8, inrate, outwidth * 8, outrate, t2 - t1 );
|
||||
sc->rate = outrate;
|
||||
sc->width = outwidth;
|
||||
return false; // HACKHACK: return false so Sound_Process won't reallocate buffer
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
sound.tempbuffer = (byte *)Mem_Realloc( host.soundpool, sound.tempbuffer, sc->size );
|
||||
|
||||
if( inrate == outrate ) // no resampling, just copy data
|
||||
handled = Sound_ConvertNoResample( sc, inwidth, outwidth, outcount );
|
||||
else if( inrate > outrate ) // fast case, usually downsample but is also ok for upsampling
|
||||
handled = Sound_ConvertDownsample( sc, inwidth, outwidth, outcount, stepscale );
|
||||
else // upsample case, w/ interpolation
|
||||
handled = Sound_ConvertUpsample( sc, inwidth, outwidth, outcount, incount, stepscale );
|
||||
|
||||
t2 = Sys_DoubleTime();
|
||||
|
||||
if( handled )
|
||||
Con_Reportf( "Sound_Resample: from [%d bit %d Hz] to [%d bit %d Hz]\n", inwidth * 8, inrate, outwidth * 8, outrate );
|
||||
{
|
||||
if( t2 - t1 > 0.01f ) // critical, report to mod developer
|
||||
Con_Printf( S_WARN "Sound_Resample: from [%d bit %d Hz] to [%d bit %d Hz] (took %.3fs)\n", inwidth * 8, inrate, outwidth * 8, outrate, t2 - t1 );
|
||||
else
|
||||
Con_Reportf( "Sound_Resample: from [%d bit %d Hz] to [%d bit %d Hz] (took %.3fs)\n", inwidth * 8, inrate, outwidth * 8, outrate, t2 - t1 );
|
||||
}
|
||||
else
|
||||
Con_Reportf( S_ERROR "Sound_Resample: unsupported from [%d bit %d Hz] to [%d bit %d Hz]\n", inwidth * 8, inrate, outwidth * 8, outrate );
|
||||
Con_Printf( S_ERROR "Sound_Resample: unsupported from [%d bit %d Hz] to [%d bit %d Hz]\n", inwidth * 8, inrate, outwidth * 8, outrate );
|
||||
|
||||
sc->rate = outrate;
|
||||
sc->width = outwidth;
|
||||
@@ -276,23 +526,20 @@ qboolean Sound_Process( wavdata_t **wav, int rate, int width, uint flags )
|
||||
if( !snd || !snd->buffer )
|
||||
return false;
|
||||
|
||||
if(( flags & SOUND_RESAMPLE ) && ( width > 0 || rate > 0 ))
|
||||
if( FBitSet( flags, SOUND_RESAMPLE ) && ( width > 0 || rate > 0 ))
|
||||
{
|
||||
if( Sound_ResampleInternal( snd, snd->rate, snd->width, rate, width ))
|
||||
result = Sound_ResampleInternal( snd, snd->rate, snd->width, rate, width );
|
||||
|
||||
if( result )
|
||||
{
|
||||
Mem_Free( snd->buffer ); // free original image buffer
|
||||
snd->buffer = Sound_Copy( snd->size ); // unzone buffer (don't touch image.tempbuffer)
|
||||
}
|
||||
else
|
||||
{
|
||||
// not resampled
|
||||
result = false;
|
||||
snd->buffer = Sound_Copy( snd->size ); // unzone buffer (don't touch sound.tempbuffer)
|
||||
}
|
||||
}
|
||||
|
||||
*wav = snd;
|
||||
|
||||
return false;
|
||||
return result;
|
||||
}
|
||||
|
||||
qboolean Sound_SupportedFileFormat( const char *fileext )
|
||||
|
||||
@@ -23,6 +23,11 @@ static const byte *iff_end;
|
||||
static const byte *iff_lastChunk;
|
||||
static int iff_chunkLen;
|
||||
|
||||
static int IsFourCC( const byte *ptr, const byte *fourcc )
|
||||
{
|
||||
return 0 == memcmp( ptr, fourcc, 4 );
|
||||
}
|
||||
|
||||
/*
|
||||
=================
|
||||
GetLittleShort
|
||||
@@ -86,7 +91,21 @@ static void FindNextChunk( const char *filename, const char *name )
|
||||
|
||||
if( iff_chunkLen > remaining )
|
||||
{
|
||||
Con_DPrintf( "%s: '%s' truncated by %i bytes\n", __func__, filename, iff_chunkLen - remaining );
|
||||
// only print this warning if selected chunk is truncated
|
||||
//
|
||||
// otherwise this warning becomes misleading because some
|
||||
// idiot programs like CoolEdit (i.e. Adobe Audition) don't always
|
||||
// respect pad byte. The file isn't actually truncated, it just
|
||||
// can't be reliably parsed as a whole
|
||||
|
||||
if( IsFourCC( iff_lastChunk, "RIFF" )
|
||||
|| IsFourCC( iff_lastChunk, "fmt " )
|
||||
|| IsFourCC( iff_lastChunk, "cue " )
|
||||
|| IsFourCC( iff_lastChunk, "LIST" )
|
||||
|| IsFourCC( iff_lastChunk, "data" ))
|
||||
{
|
||||
Con_DPrintf( "%s: '%s' truncated by %i bytes\n", __func__, filename, iff_chunkLen - remaining );
|
||||
}
|
||||
iff_chunkLen = remaining;
|
||||
}
|
||||
|
||||
@@ -94,9 +113,9 @@ static void FindNextChunk( const char *filename, const char *name )
|
||||
iff_dataPtr -= 8;
|
||||
|
||||
iff_lastChunk = iff_dataPtr + 8 + iff_chunkLen;
|
||||
if ((iff_chunkLen&1) && remaining)
|
||||
if(( iff_chunkLen & 1 ) && remaining )
|
||||
iff_lastChunk++;
|
||||
if (!Q_strncmp(iff_dataPtr, name, 4))
|
||||
if( IsFourCC( iff_dataPtr, name ))
|
||||
return;
|
||||
}
|
||||
}
|
||||
@@ -130,15 +149,18 @@ static qboolean StreamFindNextChunk( file_t *file, const char *name, int *last_c
|
||||
return false; // didn't find the chunk
|
||||
|
||||
FS_Seek( file, 4, SEEK_CUR );
|
||||
FS_Read( file, &iff_chunk_len, sizeof( iff_chunk_len ));
|
||||
if( FS_Read( file, &iff_chunk_len, sizeof( iff_chunk_len )) != sizeof( iff_chunk_len ))
|
||||
return false;
|
||||
|
||||
if( iff_chunk_len < 0 )
|
||||
return false; // didn't find the chunk
|
||||
|
||||
FS_Seek( file, -8, SEEK_CUR );
|
||||
*last_chunk = FS_Tell( file ) + 8 + (( iff_chunk_len + 1 ) & ~1 );
|
||||
FS_Read( file, chunkName, 4 );
|
||||
if( FS_Read( file, chunkName, sizeof( chunkName )) != sizeof( chunkName ))
|
||||
return false;
|
||||
|
||||
if( !Q_strncmp( chunkName, name, 4 ))
|
||||
if( IsFourCC( chunkName, name ))
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -164,7 +186,7 @@ qboolean Sound_LoadWAV( const char *name, const byte *buffer, fs_offset_t filesi
|
||||
// find "RIFF" chunk
|
||||
FindChunk( name, "RIFF" );
|
||||
|
||||
if( !( iff_dataPtr && !Q_strncmp( (const char *)iff_dataPtr + 8, "WAVE", 4 )))
|
||||
if( !iff_dataPtr || !IsFourCC( iff_dataPtr + 8, "WAVE" ))
|
||||
{
|
||||
Con_DPrintf( S_ERROR "Sound_LoadWAV: %s missing 'RIFF/WAVE' chunks\n", name );
|
||||
return false;
|
||||
@@ -223,11 +245,12 @@ qboolean Sound_LoadWAV( const char *name, const byte *buffer, fs_offset_t filesi
|
||||
{
|
||||
iff_dataPtr += 32;
|
||||
sound.loopstart = GetLittleLong();
|
||||
SetBits( sound.flags, SOUND_LOOPED );
|
||||
FindNextChunk( name, "LIST" ); // if the next chunk is a LIST chunk, look for a cue length marker
|
||||
|
||||
if( iff_dataPtr )
|
||||
{
|
||||
if( !Q_strncmp( (const char *)iff_dataPtr + 28, "mark", 4 ))
|
||||
if( IsFourCC( iff_dataPtr + 28, "mark" ))
|
||||
{
|
||||
// this is not a proper parse, but it works with CoolEdit...
|
||||
iff_dataPtr += 24;
|
||||
@@ -237,7 +260,7 @@ qboolean Sound_LoadWAV( const char *name, const byte *buffer, fs_offset_t filesi
|
||||
}
|
||||
else
|
||||
{
|
||||
sound.loopstart = -1;
|
||||
sound.loopstart = 0;
|
||||
sound.samples = 0;
|
||||
}
|
||||
|
||||
@@ -342,8 +365,16 @@ stream_t *Stream_OpenWAV( const char *filename )
|
||||
return NULL;
|
||||
}
|
||||
|
||||
FS_Read( file, chunkName, 4 );
|
||||
if( !Q_strncmp( chunkName, "WAVE", 4 ))
|
||||
FS_Seek( file, 4, SEEK_CUR );
|
||||
|
||||
if( FS_Read( file, chunkName, 4 ) != 4 )
|
||||
{
|
||||
Con_DPrintf( S_ERROR "%s: %s missing WAVE chunk, truncated\n", filename );
|
||||
FS_Close( file );
|
||||
return false;
|
||||
}
|
||||
|
||||
if( !IsFourCC( chunkName, "WAVE" ))
|
||||
{
|
||||
Con_DPrintf( S_ERROR "Stream_OpenWAV: %s missing WAVE chunk\n", filename );
|
||||
FS_Close( file );
|
||||
@@ -351,7 +382,7 @@ stream_t *Stream_OpenWAV( const char *filename )
|
||||
}
|
||||
|
||||
// get "fmt " chunk
|
||||
iff_data = FS_Tell( file ) + 4;
|
||||
iff_data = FS_Tell( file );
|
||||
last_chunk = iff_data;
|
||||
if( !StreamFindNextChunk( file, "fmt ", &last_chunk ))
|
||||
{
|
||||
|
||||
@@ -50,7 +50,7 @@ typedef struct sndlib_s
|
||||
int rate; // num samples per second (e.g. 11025 - 11 khz)
|
||||
int width; // resolution - bum bits divided by 8 (8 bit is 1, 16 bit is 2)
|
||||
int channels; // num channels (1 - mono, 2 - stereo)
|
||||
int loopstart; // start looping from
|
||||
uint loopstart; // start looping from
|
||||
uint samples; // total samplecount in sound
|
||||
uint flags; // additional sound flags
|
||||
size_t size; // sound unpacked size (for bounds checking)
|
||||
|
||||
@@ -135,6 +135,10 @@ void Sys_InitLog( void )
|
||||
mode = "a";
|
||||
else mode = "w";
|
||||
|
||||
if( Host_IsDedicated( ))
|
||||
Q_strncpy( s_ld.title, XASH_DEDICATED_SERVER_NAME " " XASH_VERSION, sizeof( s_ld.title ));
|
||||
else Q_strncpy( s_ld.title, XASH_ENGINE_NAME " " XASH_VERSION, sizeof( s_ld.title ));
|
||||
|
||||
// create log if needed
|
||||
if( s_ld.log_active )
|
||||
{
|
||||
@@ -142,15 +146,18 @@ void Sys_InitLog( void )
|
||||
|
||||
if ( !s_ld.logfile )
|
||||
{
|
||||
Con_Reportf( S_ERROR "Sys_InitLog: can't create log file %s: %s\n", s_ld.log_path, strerror( errno ) );
|
||||
Con_Reportf( S_ERROR "Sys_InitLog: can't create log file %s: %s\n", s_ld.log_path, strerror( errno ));
|
||||
return;
|
||||
}
|
||||
|
||||
s_ld.logfileno = fileno( s_ld.logfile );
|
||||
|
||||
fprintf( s_ld.logfile, "=================================================================================\n" );
|
||||
fprintf( s_ld.logfile, "\t%s (build %i commit %s (%s-%s)) started at %s\n", s_ld.title, Q_buildnum(), Q_buildcommit(), Q_buildos(), Q_buildarch(), Q_timestamp( TIME_FULL ) );
|
||||
fprintf( s_ld.logfile, "=================================================================================\n" );
|
||||
// fit to 80 columns for easier read on standard terminal
|
||||
fputs( "================================================================================\n", s_ld.logfile );
|
||||
fprintf( s_ld.logfile, "%s (%i, %s, %s, %s-%s)\n", s_ld.title, Q_buildnum(), Q_buildcommit(), Q_buildbranch(), Q_buildos(), Q_buildarch());
|
||||
fprintf( s_ld.logfile, "Game started at %s\n", Q_timestamp( TIME_FULL ));
|
||||
fputs( "================================================================================\n", s_ld.logfile );
|
||||
fflush( s_ld.logfile );
|
||||
}
|
||||
}
|
||||
|
||||
@@ -177,12 +184,11 @@ void Sys_CloseLog( void )
|
||||
|
||||
if( s_ld.logfile )
|
||||
{
|
||||
fprintf( s_ld.logfile, "\n");
|
||||
fprintf( s_ld.logfile, "=================================================================================");
|
||||
if( host.change_game ) fprintf( s_ld.logfile, "\n\t%s (build %i) %s\n", s_ld.title, Q_buildnum(), event_name );
|
||||
else fprintf( s_ld.logfile, "\n\t%s (build %i) %s at %s\n", s_ld.title, Q_buildnum(), event_name, Q_timestamp( TIME_FULL ));
|
||||
fprintf( s_ld.logfile, "=================================================================================\n");
|
||||
|
||||
fputc( '\n', s_ld.logfile );
|
||||
fputs( "================================================================================\n", s_ld.logfile );
|
||||
fprintf( s_ld.logfile, "%s (%i, %s, %s, %s-%s)\n", s_ld.title, Q_buildnum(), Q_buildcommit(), Q_buildbranch(), Q_buildos(), Q_buildarch());
|
||||
fprintf( s_ld.logfile, "Stopped with reason \"%s\" at %s\n", event_name, Q_timestamp( TIME_FULL ));
|
||||
fputs( "================================================================================\n", s_ld.logfile );
|
||||
fclose( s_ld.logfile );
|
||||
s_ld.logfile = NULL;
|
||||
}
|
||||
@@ -335,15 +341,18 @@ void GAME_EXPORT Con_Printf( const char *szFmt, ... )
|
||||
{
|
||||
static char buffer[MAX_PRINT_MSG];
|
||||
va_list args;
|
||||
qboolean add_newline;
|
||||
|
||||
if( !host.allow_console )
|
||||
return;
|
||||
|
||||
va_start( args, szFmt );
|
||||
Q_vsnprintf( buffer, sizeof( buffer ), szFmt, args );
|
||||
add_newline = Q_vsnprintf( buffer, sizeof( buffer ), szFmt, args ) < 0;
|
||||
va_end( args );
|
||||
|
||||
Sys_Print( buffer );
|
||||
if( add_newline )
|
||||
Sys_Print( "\n" );
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -356,18 +365,21 @@ void GAME_EXPORT Con_DPrintf( const char *szFmt, ... )
|
||||
{
|
||||
static char buffer[MAX_PRINT_MSG];
|
||||
va_list args;
|
||||
qboolean add_newline;
|
||||
|
||||
if( host_developer.value < DEV_NORMAL )
|
||||
return;
|
||||
|
||||
va_start( args, szFmt );
|
||||
Q_vsnprintf( buffer, sizeof( buffer ), szFmt, args );
|
||||
add_newline = Q_vsnprintf( buffer, sizeof( buffer ), szFmt, args ) < 0;
|
||||
va_end( args );
|
||||
|
||||
if( buffer[0] == '0' && buffer[1] == '\n' && buffer[2] == '\0' )
|
||||
return; // hlrally spam
|
||||
|
||||
Sys_Print( buffer );
|
||||
if( add_newline )
|
||||
Sys_Print( "\n" );
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -380,15 +392,18 @@ void Con_Reportf( const char *szFmt, ... )
|
||||
{
|
||||
static char buffer[MAX_PRINT_MSG];
|
||||
va_list args;
|
||||
qboolean add_newline;
|
||||
|
||||
if( host_developer.value < DEV_EXTENDED )
|
||||
return;
|
||||
|
||||
va_start( args, szFmt );
|
||||
Q_vsnprintf( buffer, sizeof( buffer ), szFmt, args );
|
||||
add_newline = Q_vsnprintf( buffer, sizeof( buffer ), szFmt, args ) < 0;
|
||||
va_end( args );
|
||||
|
||||
Sys_Print( buffer );
|
||||
if( add_newline )
|
||||
Sys_Print( "\n" );
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -19,6 +19,10 @@ GNU General Public License for more details.
|
||||
#include <stdlib.h>
|
||||
#include <errno.h>
|
||||
|
||||
#if _MSC_VER
|
||||
#include <intrin.h>
|
||||
#endif
|
||||
|
||||
#ifdef XASH_SDL
|
||||
#include <SDL.h>
|
||||
#endif
|
||||
@@ -49,7 +53,7 @@ GNU General Public License for more details.
|
||||
#include "library.h"
|
||||
#include "whereami.h"
|
||||
|
||||
qboolean error_on_exit = false; // arg for exit();
|
||||
int error_on_exit = 0; // arg for exit();
|
||||
|
||||
/*
|
||||
================
|
||||
@@ -68,17 +72,15 @@ Sys_DebugBreak
|
||||
*/
|
||||
void Sys_DebugBreak( void )
|
||||
{
|
||||
#if XASH_LINUX || ( XASH_WIN32 && !XASH_64BIT )
|
||||
#if _MSC_VER
|
||||
if( Sys_DebuggerPresent() )
|
||||
_asm { int 3 }
|
||||
#elif XASH_X86
|
||||
if( Sys_DebuggerPresent() )
|
||||
asm volatile( "int $3;" );
|
||||
#else
|
||||
if( Sys_DebuggerPresent() )
|
||||
raise( SIGINT );
|
||||
#endif
|
||||
if( Sys_DebuggerPresent( ))
|
||||
__debugbreak();
|
||||
#else // !_MSC_VER
|
||||
if( Sys_DebuggerPresent( ))
|
||||
{
|
||||
INLINE_RAISE( SIGINT );
|
||||
INLINE_NANOSLEEP1(); // sometimes signal comes with delay, let it interrupt nanosleep
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
@@ -402,7 +404,7 @@ void Sys_Warn( const char *format, ... )
|
||||
Msg( "Sys_Warn: %s\n", text );
|
||||
|
||||
if( !Host_IsDedicated() ) // dedicated server should not hang on messagebox
|
||||
Platform_MessageBox( "Xash Warning", text, false );
|
||||
Platform_MessageBox( "Xash Warning", text, true );
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -428,7 +430,7 @@ void Sys_Error( const char *error, ... )
|
||||
// make sure that console received last message
|
||||
if( host.change_game ) Sys_Sleep( 200 );
|
||||
|
||||
error_on_exit = true;
|
||||
error_on_exit = 1;
|
||||
host.status = HOST_ERR_FATAL;
|
||||
va_start( argptr, error );
|
||||
Q_vsnprintf( text, MAX_PRINT_MSG, error, argptr );
|
||||
@@ -447,7 +449,7 @@ void Sys_Error( const char *error, ... )
|
||||
Wcon_ShowConsole( false );
|
||||
#endif
|
||||
Sys_Print( text );
|
||||
Platform_MessageBox( "Xash Error", text, false );
|
||||
Platform_MessageBox( "Xash Error", text, true );
|
||||
}
|
||||
else
|
||||
{
|
||||
|
||||
@@ -41,7 +41,7 @@ NOTE: never change this structure because all dll descriptions in xash code
|
||||
writes into struct by offsets not names
|
||||
========================================================================
|
||||
*/
|
||||
|
||||
extern int error_on_exit;
|
||||
void Sys_Sleep( int msec );
|
||||
double Sys_DoubleTime( void );
|
||||
char *Sys_GetClipboardData( void );
|
||||
|
||||
@@ -39,13 +39,15 @@ void Test_RunCon( void );
|
||||
void Test_RunVOX( void );
|
||||
void Test_RunIPFilter( void );
|
||||
void Test_RunGamma( void );
|
||||
void Test_RunDelta( void );
|
||||
|
||||
#define TEST_LIST_0 \
|
||||
Test_RunLibCommon(); \
|
||||
Test_RunCommon(); \
|
||||
Test_RunCmd(); \
|
||||
Test_RunCvar(); \
|
||||
Test_RunIPFilter();
|
||||
Test_RunIPFilter(); \
|
||||
Test_RunDelta();
|
||||
|
||||
#define TEST_LIST_0_CLIENT \
|
||||
Test_RunCon(); \
|
||||
|
||||
@@ -25,18 +25,39 @@ GNU General Public License for more details.
|
||||
#include "platform/swap/swap.h"
|
||||
#define Q_malloc SWAP_Malloc
|
||||
#define Q_free SWAP_Free
|
||||
|
||||
static void *Q_realloc( void *mem, size_t size )
|
||||
{
|
||||
void *newmem;
|
||||
|
||||
if( mem && size == 0 )
|
||||
{
|
||||
Q_free( mem );
|
||||
return NULL;
|
||||
}
|
||||
|
||||
newmem = Q_malloc( size );
|
||||
if( mem && newmem )
|
||||
{
|
||||
memcpy( newmem, mem, size );
|
||||
Q_free( mem );
|
||||
}
|
||||
|
||||
return newmem;
|
||||
}
|
||||
#else
|
||||
#define Q_malloc malloc
|
||||
#define Q_free free
|
||||
#define Q_realloc realloc
|
||||
#endif
|
||||
|
||||
typedef struct memheader_s
|
||||
{
|
||||
struct memheader_s *next; // next and previous memheaders in chain belonging to pool
|
||||
struct memheader_s *prev;
|
||||
struct mempool_s *pool; // pool this memheader belongs to
|
||||
size_t size; // size of the memory after the header (excluding header and sentinel2)
|
||||
const char *filename; // file name and line where Mem_Alloc was called
|
||||
size_t size; // size of the memory after the header (excluding header and sentinel2)
|
||||
poolhandle_t poolptr; // pool this memheader belongs to
|
||||
int fileline;
|
||||
#if !XASH_64BIT
|
||||
uint32_t pad0; // doesn't have value, only to make Mem_Alloc return aligned addresses on ILP32
|
||||
@@ -48,50 +69,34 @@ typedef struct memheader_s
|
||||
|
||||
typedef struct mempool_s
|
||||
{
|
||||
uint32_t sentinel1; // should always be MEMHEADER_SENTINEL1
|
||||
struct memheader_s *chain; // chain of individual memory allocations
|
||||
size_t totalsize; // total memory allocated in this pool (inside memheaders)
|
||||
size_t realsize; // total memory allocated in this pool (actual malloc total)
|
||||
size_t lastchecksize; // updated each time the pool is displayed by memlist
|
||||
struct mempool_s *next; // linked into global mempool list
|
||||
const char *filename; // file name and line where Mem_AllocPool was called
|
||||
int fileline;
|
||||
#if XASH_64BIT
|
||||
poolhandle_t idx;
|
||||
#endif
|
||||
char name[64]; // name of the pool
|
||||
uint32_t sentinel2; // should always be MEMHEADER_SENTINEL1
|
||||
} mempool_t;
|
||||
|
||||
static mempool_t *poolchain = NULL; // critical stuff
|
||||
static size_t poolcount = 0;
|
||||
|
||||
#if XASH_64BIT
|
||||
// a1ba: due to mempool being passed with the model through reused 32-bit field
|
||||
// which makes engine incompatible with 64-bit pointers I changed mempool type
|
||||
// from pointer to 32-bit handle, thankfully mempool structure is private
|
||||
// But! Mempools are handled through linked list so we can't index them safely
|
||||
static poolhandle_t lastidx = 0;
|
||||
|
||||
static mempool_t *Mem_FindPool( poolhandle_t poolptr )
|
||||
{
|
||||
mempool_t *pool;
|
||||
|
||||
for( pool = poolchain; pool; pool = pool->next )
|
||||
{
|
||||
if( pool->idx == poolptr )
|
||||
return pool;
|
||||
}
|
||||
if( likely( poolptr > 0 && poolptr <= poolcount ))
|
||||
return &poolchain[poolptr - 1];
|
||||
|
||||
Sys_Error( "%s: not allocated or double freed pool %d", __FUNCTION__, poolptr );
|
||||
|
||||
return NULL;
|
||||
}
|
||||
#else
|
||||
static mempool_t *Mem_FindPool( poolhandle_t poolptr )
|
||||
|
||||
static poolhandle_t Mem_PoolIndex( mempool_t *mempool )
|
||||
{
|
||||
return (mempool_t *)poolptr;
|
||||
return (poolhandle_t)(mempool - poolchain) + 1;
|
||||
}
|
||||
#endif
|
||||
|
||||
static inline void Mem_PoolAdd( mempool_t *pool, size_t size )
|
||||
{
|
||||
@@ -111,7 +116,7 @@ static inline void Mem_PoolLinkAlloc( mempool_t *pool, memheader_t *mem )
|
||||
if( mem->next ) mem->next->prev = mem;
|
||||
pool->chain = mem;
|
||||
mem->prev = NULL;
|
||||
mem->pool = pool;
|
||||
mem->poolptr = Mem_PoolIndex( pool );
|
||||
}
|
||||
|
||||
static inline void Mem_PoolUnlinkAlloc( mempool_t *pool, memheader_t *mem )
|
||||
@@ -119,7 +124,7 @@ static inline void Mem_PoolUnlinkAlloc( mempool_t *pool, memheader_t *mem )
|
||||
if( mem->next ) mem->next->prev = mem->prev;
|
||||
if( mem->prev ) mem->prev->next = mem->next;
|
||||
else pool->chain = mem->next;
|
||||
mem->pool = NULL;
|
||||
mem->poolptr = 0;
|
||||
}
|
||||
|
||||
static inline void Mem_InitAlloc( memheader_t *mem, size_t size, const char *filename, int fileline )
|
||||
@@ -165,23 +170,6 @@ static qboolean Mem_CheckAllocHeader( const char *func, const memheader_t *mem,
|
||||
return true;
|
||||
}
|
||||
|
||||
static qboolean Mem_CheckPool( const char *func, const mempool_t *pool, const char *filename, int fileline )
|
||||
{
|
||||
if( pool->sentinel1 != MEMHEADER_SENTINEL1 )
|
||||
{
|
||||
Sys_Error( "%s: trashed pool sentinel 1 (allocpool at %s:%i, freepool at %s:%i)\n", func, pool->filename, pool->fileline, filename, fileline );
|
||||
return false;
|
||||
}
|
||||
|
||||
if( pool->sentinel2 != MEMHEADER_SENTINEL1 )
|
||||
{
|
||||
Sys_Error( "%s: trashed pool sentinel 2 (allocpool at %s:%i, freepool at %s:%i)\n", func, pool->filename, pool->fileline, filename, fileline );
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void *_Mem_Alloc( poolhandle_t poolptr, size_t size, qboolean clear, const char *filename, int fileline )
|
||||
{
|
||||
memheader_t *mem;
|
||||
@@ -190,12 +178,16 @@ void *_Mem_Alloc( poolhandle_t poolptr, size_t size, qboolean clear, const char
|
||||
if( size <= 0 )
|
||||
return NULL;
|
||||
|
||||
if( !poolptr )
|
||||
if( unlikely( !poolptr ))
|
||||
{
|
||||
Sys_Error( "%s: pool == NULL (alloc at %s:%i)\n", __func__, filename, fileline );
|
||||
return NULL;
|
||||
}
|
||||
|
||||
pool = Mem_FindPool( poolptr );
|
||||
if( !pool )
|
||||
return NULL;
|
||||
|
||||
mem = (memheader_t *)Q_malloc( sizeof( memheader_t ) + size + sizeof( byte ));
|
||||
if( mem == NULL )
|
||||
{
|
||||
@@ -205,7 +197,6 @@ void *_Mem_Alloc( poolhandle_t poolptr, size_t size, qboolean clear, const char
|
||||
|
||||
Mem_InitAlloc( mem, size, filename, fileline );
|
||||
|
||||
pool = Mem_FindPool( poolptr );
|
||||
Mem_PoolAdd( pool, size );
|
||||
Mem_PoolLinkAlloc( pool, mem );
|
||||
|
||||
@@ -222,7 +213,9 @@ static void Mem_FreeBlock( memheader_t *mem, const char *filename, int fileline
|
||||
if( !Mem_CheckAllocHeader( __func__, mem, filename, fileline ))
|
||||
return;
|
||||
|
||||
pool = mem->pool;
|
||||
pool = Mem_FindPool( mem->poolptr );
|
||||
if( !pool )
|
||||
return;
|
||||
|
||||
// unlink memheader from doubly linked list
|
||||
if(( mem->prev ? mem->prev->next != mem : pool->chain != mem ) || ( mem->next && mem->next->prev != mem ))
|
||||
@@ -258,7 +251,7 @@ void *_Mem_Realloc( poolhandle_t poolptr, void *data, size_t size, qboolean clea
|
||||
if( size <= 0 )
|
||||
return data; // no need to reallocate
|
||||
|
||||
if( !poolptr )
|
||||
if( unlikely( !poolptr ))
|
||||
{
|
||||
Sys_Error( "Mem_Realloc: pool == NULL (alloc at %s:%i)\n", filename, fileline );
|
||||
return NULL;
|
||||
@@ -272,25 +265,21 @@ void *_Mem_Realloc( poolhandle_t poolptr, void *data, size_t size, qboolean clea
|
||||
if( !Mem_CheckAllocHeader( "Mem_Realloc", mem, filename, fileline ))
|
||||
return NULL;
|
||||
|
||||
if( unlikely( mem->poolptr != poolptr ))
|
||||
{
|
||||
Sys_Error( "Mem_Realloc: pool migration is not allowed (alloc at %s:%i, realloc at %s:%i)\n",
|
||||
Mem_CheckFilename( mem->filename ), mem->fileline, filename, fileline );
|
||||
return NULL;
|
||||
}
|
||||
|
||||
oldsize = mem->size;
|
||||
if( size == oldsize )
|
||||
return data;
|
||||
|
||||
#if XASH_CUSTOM_SWAP
|
||||
{
|
||||
char *nb = _Mem_Alloc( poolptr, size, clear, filename, fileline );
|
||||
|
||||
size_t newsize = mem->size < size ? mem->size : size; // upper data can be trucnated!
|
||||
memcpy( nb, data, newsize );
|
||||
_Mem_Free( data, filename, fileline ); // free unused old block
|
||||
|
||||
return nb;
|
||||
}
|
||||
#else // XASH_CUSTOM_SWAP
|
||||
pool = Mem_FindPool( poolptr );
|
||||
|
||||
oldmem = (uintptr_t)mem;
|
||||
mem = realloc( mem, sizeof( memheader_t ) + size + sizeof( byte ));
|
||||
mem = Q_realloc( mem, sizeof( memheader_t ) + size + sizeof( byte ));
|
||||
|
||||
if( mem == NULL )
|
||||
{
|
||||
@@ -312,14 +301,7 @@ void *_Mem_Realloc( poolhandle_t poolptr, void *data, size_t size, qboolean clea
|
||||
}
|
||||
else Mem_PoolSubtract( pool, oldsize - size );
|
||||
|
||||
// if allocation was migrated from one pool to another
|
||||
// (this is possible with original Mem_Realloc func)
|
||||
if( unlikely( mem->pool != pool ))
|
||||
{
|
||||
Mem_PoolUnlinkAlloc( mem->pool, mem );
|
||||
Mem_PoolLinkAlloc( pool, mem );
|
||||
}
|
||||
else if( oldmem != (uintptr_t)mem ) // just relink pointers
|
||||
if( oldmem != (uintptr_t)mem ) // just relink pointers
|
||||
{
|
||||
if( mem->next ) mem->next->prev = mem;
|
||||
if( mem->prev ) mem->prev->next = mem;
|
||||
@@ -327,69 +309,81 @@ void *_Mem_Realloc( poolhandle_t poolptr, void *data, size_t size, qboolean clea
|
||||
}
|
||||
|
||||
return (void *)((byte *)mem + sizeof( memheader_t ));
|
||||
#endif // XASH_CUSTOM_SWAP
|
||||
}
|
||||
|
||||
static poolhandle_t Mem_InitPool( mempool_t *pool, const char *name, const char *filename, int fileline )
|
||||
{
|
||||
memset( pool, 0, sizeof( *pool ));
|
||||
|
||||
// fill header
|
||||
pool->filename = filename;
|
||||
pool->fileline = fileline;
|
||||
pool->realsize = sizeof( mempool_t );
|
||||
Q_strncpy( pool->name, name, sizeof( pool->name ));
|
||||
|
||||
return Mem_PoolIndex( pool );
|
||||
}
|
||||
|
||||
poolhandle_t _Mem_AllocPool( const char *name, const char *filename, int fileline )
|
||||
{
|
||||
mempool_t *pool;
|
||||
size_t i;
|
||||
|
||||
pool = (mempool_t *)Q_malloc( sizeof( mempool_t ));
|
||||
for( i = 0, pool = poolchain; i < poolcount; i++, pool++ )
|
||||
{
|
||||
if( pool->filename == NULL )
|
||||
return Mem_InitPool( pool, name, filename, fileline );
|
||||
}
|
||||
|
||||
pool = (mempool_t *)Q_realloc( poolchain, sizeof( *poolchain ) * ( poolcount + 1 ));
|
||||
if( pool == NULL )
|
||||
{
|
||||
Sys_Error( "Mem_AllocPool: out of memory (allocpool at %s:%i)\n", filename, fileline );
|
||||
return 0;
|
||||
}
|
||||
memset( pool, 0, sizeof( mempool_t ));
|
||||
|
||||
// fill header
|
||||
pool->sentinel1 = MEMHEADER_SENTINEL1;
|
||||
pool->sentinel2 = MEMHEADER_SENTINEL1;
|
||||
pool->filename = filename;
|
||||
pool->fileline = fileline;
|
||||
pool->chain = NULL;
|
||||
pool->totalsize = 0;
|
||||
pool->realsize = sizeof( mempool_t );
|
||||
Q_strncpy( pool->name, name, sizeof( pool->name ));
|
||||
pool->next = poolchain;
|
||||
poolchain = pool;
|
||||
|
||||
#if XASH_64BIT
|
||||
pool->idx = ++lastidx;
|
||||
return pool->idx;
|
||||
#else
|
||||
return (poolhandle_t)pool;
|
||||
#endif
|
||||
pool = &poolchain[poolcount++];
|
||||
return Mem_InitPool( pool, name, filename, fileline );
|
||||
}
|
||||
|
||||
void _Mem_FreePool( poolhandle_t *poolptr, const char *filename, int fileline )
|
||||
{
|
||||
mempool_t *pool;
|
||||
mempool_t **chainaddress;
|
||||
|
||||
if( *poolptr && ( pool = Mem_FindPool( *poolptr )))
|
||||
{
|
||||
// unlink pool from chain
|
||||
for( chainaddress = &poolchain; *chainaddress && *chainaddress != pool; chainaddress = &((*chainaddress)->next));
|
||||
if( *chainaddress != pool ) Sys_Error( "Mem_FreePool: pool already free (freepool at %s:%i)\n", filename, fileline );
|
||||
Mem_CheckPool( "Mem_FreePool", pool, filename, fileline );
|
||||
*chainaddress = pool->next;
|
||||
if( !pool->filename )
|
||||
{
|
||||
Sys_Error( "Mem_FreePool: pool already free (freepool at %s:%i)\n", filename, fileline );
|
||||
*poolptr = 0;
|
||||
return;
|
||||
}
|
||||
|
||||
// free memory owned by the pool
|
||||
while( pool->chain ) Mem_FreeBlock( pool->chain, filename, fileline );
|
||||
while( pool->chain )
|
||||
Mem_FreeBlock( pool->chain, filename, fileline );
|
||||
|
||||
// free the pool itself
|
||||
memset( pool, 0xBF, sizeof( mempool_t ));
|
||||
Q_free( pool );
|
||||
pool->chain = NULL;
|
||||
pool->filename = NULL; // mark as reusable
|
||||
*poolptr = 0;
|
||||
}
|
||||
}
|
||||
|
||||
void _Mem_EmptyPool( poolhandle_t poolptr, const char *filename, int fileline )
|
||||
{
|
||||
mempool_t *pool = Mem_FindPool( poolptr );
|
||||
if( !poolptr ) Sys_Error( "Mem_EmptyPool: pool == NULL (emptypool at %s:%i)\n", filename, fileline );
|
||||
mempool_t *pool;
|
||||
if( unlikely( !poolptr ))
|
||||
{
|
||||
Sys_Error( "Mem_EmptyPool: pool == NULL (emptypool at %s:%i)\n", filename, fileline );
|
||||
return;
|
||||
}
|
||||
|
||||
Mem_CheckPool( "Mem_FreePool", pool, filename, fileline );
|
||||
pool = Mem_FindPool( poolptr );
|
||||
if( !pool )
|
||||
return;
|
||||
|
||||
// free memory owned by the pool
|
||||
while( pool->chain ) Mem_FreeBlock( pool->chain, filename, fileline );
|
||||
@@ -412,7 +406,8 @@ static qboolean Mem_CheckAlloc( mempool_t *pool, void *data )
|
||||
else
|
||||
{
|
||||
// search all pools
|
||||
for( pool = poolchain; pool; pool = pool->next )
|
||||
size_t i;
|
||||
for( i = 0, pool = poolchain; i < poolcount; i++, pool++ )
|
||||
{
|
||||
if( Mem_CheckAlloc( pool, data ))
|
||||
return true;
|
||||
@@ -440,23 +435,24 @@ void _Mem_Check( const char *filename, int fileline )
|
||||
{
|
||||
memheader_t *mem;
|
||||
mempool_t *pool;
|
||||
size_t i;
|
||||
|
||||
for( pool = poolchain; pool; pool = pool->next )
|
||||
Mem_CheckPool( "Mem_CheckSentinels", pool, filename, fileline );
|
||||
|
||||
for( pool = poolchain; pool; pool = pool->next )
|
||||
for( i = 0, pool = poolchain; i < poolcount; i++, pool++ )
|
||||
for( mem = pool->chain; mem; mem = mem->next )
|
||||
Mem_CheckAllocHeader( "Mem_CheckSentinels", mem, filename, fileline );
|
||||
}
|
||||
|
||||
void Mem_PrintStats( void )
|
||||
{
|
||||
size_t count = 0, size = 0, realsize = 0;
|
||||
size_t count = 0, size = 0, realsize = 0, i;
|
||||
mempool_t *pool;
|
||||
|
||||
Mem_Check();
|
||||
for( pool = poolchain; pool; pool = pool->next )
|
||||
for( i = 0, pool = poolchain; i < poolcount; i++, pool++ )
|
||||
{
|
||||
if( !pool->filename )
|
||||
continue;
|
||||
|
||||
count++;
|
||||
size += pool->totalsize;
|
||||
realsize += pool->realsize;
|
||||
@@ -470,15 +466,19 @@ void Mem_PrintList( size_t minallocationsize )
|
||||
{
|
||||
mempool_t *pool;
|
||||
memheader_t *mem;
|
||||
size_t i;
|
||||
|
||||
Mem_Check();
|
||||
|
||||
Con_Printf( "memory pool list:\n" );
|
||||
Con_Printf( "\t^3size\t\t\t\tname\n");
|
||||
for( pool = poolchain; pool; pool = pool->next )
|
||||
for( i = 0, pool = poolchain; i < poolcount; i++, pool++ )
|
||||
{
|
||||
long changed_size = (long)pool->totalsize - (long)pool->lastchecksize;
|
||||
|
||||
if( !pool->filename )
|
||||
continue;
|
||||
|
||||
// poolnames can contain color symbols, make sure what color is reset
|
||||
if( pool->lastchecksize != 0 && changed_size != 0 )
|
||||
{
|
||||
|
||||
@@ -50,6 +50,10 @@ GNU General Public License for more details.
|
||||
#define LUMP_SAVE_NO_DATA 7
|
||||
#define LUMP_SAVE_CORRUPTED 8
|
||||
|
||||
#ifndef ALLOC_CHECK
|
||||
#define ALLOC_CHECK( x )
|
||||
#endif
|
||||
|
||||
typedef struct areanode_s
|
||||
{
|
||||
int axis; // -1 = leaf node
|
||||
|
||||
@@ -175,11 +175,13 @@ void Evdev_Autodetect_f( void )
|
||||
if( evdev.devices >= MAX_EVDEV_DEVICES )
|
||||
continue;
|
||||
|
||||
Q_snprintf( path, MAX_STRING, "/dev/input/%s", entry->d_name );
|
||||
Q_snprintf( path, sizeof( path ), "/dev/input/%s", entry->d_name );
|
||||
|
||||
for( i = 0; i < evdev.devices; i++ )
|
||||
if( !Q_strncmp( evdev.paths[i], path, MAX_STRING ) )
|
||||
{
|
||||
if( !Q_strncmp( evdev.paths[i], path, sizeof( evdev.paths[i] ))
|
||||
goto next;
|
||||
}
|
||||
|
||||
if( Q_strncmp( entry->d_name, "event", 5 ) )
|
||||
continue;
|
||||
@@ -220,7 +222,7 @@ void Evdev_Autodetect_f( void )
|
||||
}
|
||||
goto close;
|
||||
open:
|
||||
Q_strncpy( evdev.paths[evdev.devices], path, MAX_STRING );
|
||||
Q_strncpy( evdev.paths[evdev.devices], path, sizeof( evdev.paths[0] ));
|
||||
evdev.fds[evdev.devices++] = fd;
|
||||
Con_Printf( "Opened device %s\n", path );
|
||||
#if XASH_INPUT == INPUT_EVDEV
|
||||
@@ -260,7 +262,7 @@ void Evdev_OpenDevice ( const char *path )
|
||||
|
||||
for( i = 0; i < evdev.devices; i++ )
|
||||
{
|
||||
if( !Q_strncmp( evdev.paths[i], path, MAX_STRING ) )
|
||||
if( !Q_strncmp( evdev.paths[i], path, sizeof( evdev.paths[i] )))
|
||||
{
|
||||
Con_Printf( "device %s already open!\n", path );
|
||||
return;
|
||||
@@ -275,7 +277,7 @@ void Evdev_OpenDevice ( const char *path )
|
||||
}
|
||||
Con_Printf( "Input device #%d: %s opened sucessfully\n", evdev.devices, path );
|
||||
evdev.fds[evdev.devices] = ret;
|
||||
Q_strncpy( evdev.paths[evdev.devices++], path, MAX_STRING );
|
||||
Q_strncpy( evdev.paths[evdev.devices++], path, sizeof( evdev.paths[0] ));
|
||||
|
||||
#if XASH_INPUT == INPUT_EVDEV
|
||||
if( Sys_CheckParm( "-grab" ) )
|
||||
@@ -309,7 +311,7 @@ void Evdev_CloseDevice_f ( void )
|
||||
if( Q_isdigit( arg ) )
|
||||
i = Q_atoi( arg );
|
||||
else for( i = 0; i < evdev.devices; i++ )
|
||||
if( !Q_strncmp( evdev.paths[i], arg, MAX_STRING ) )
|
||||
if( !Q_strncmp( evdev.paths[i], arg, sizeof( evdev.paths[i] )))
|
||||
break;
|
||||
|
||||
if( i >= evdev.devices )
|
||||
@@ -324,7 +326,7 @@ void Evdev_CloseDevice_f ( void )
|
||||
|
||||
for( ; i < evdev.devices; i++ )
|
||||
{
|
||||
Q_strncpy( evdev.paths[i], evdev.paths[i+1], MAX_STRING );
|
||||
Q_strncpy( evdev.paths[i], evdev.paths[i+1], sizeof( evdev.paths[i] ));
|
||||
evdev.fds[i] = evdev.fds[i+1];
|
||||
}
|
||||
}
|
||||
|
||||
@@ -52,7 +52,7 @@ static pid_t gettid( void )
|
||||
static void *g_hsystemd;
|
||||
static int (*g_pfn_sd_notify)( int unset_environment, const char *state );
|
||||
|
||||
qboolean Sys_DebuggerPresent( void )
|
||||
qboolean Platform_DebuggerPresent( void )
|
||||
{
|
||||
char buf[4096];
|
||||
ssize_t num_read;
|
||||
@@ -111,6 +111,11 @@ void Linux_Init( void )
|
||||
if( !Host_IsDedicated( ))
|
||||
return;
|
||||
|
||||
// manpage says sd_notify will send messages to socket in NOTIFY_SOCKET
|
||||
// environment variable. Check if it's available.
|
||||
if( getenv( "NOTIFY_SOCKET" ) == NULL )
|
||||
return;
|
||||
|
||||
if(( g_hsystemd = dlopen( "libsystemd.so.0", RTLD_LAZY )) == NULL )
|
||||
return;
|
||||
|
||||
|
||||
@@ -34,8 +34,8 @@ double Platform_DoubleTime( void );
|
||||
void Platform_Sleep( int msec );
|
||||
void Platform_ShellExecute( const char *path, const char *parms );
|
||||
void Platform_MessageBox( const char *title, const char *message, qboolean parentMainWindow );
|
||||
qboolean Sys_DebuggerPresent( void ); // optional, see Sys_DebugBreak
|
||||
void Platform_SetStatus( const char *status );
|
||||
qboolean Platform_DebuggerPresent( void );
|
||||
|
||||
// legacy iOS port functions
|
||||
#if TARGET_OS_IOS
|
||||
@@ -64,6 +64,8 @@ const char *Android_LoadID( void );
|
||||
void Android_SaveID( const char *id );
|
||||
void Android_Init( void );
|
||||
void *Android_GetNativeObject( const char *name );
|
||||
int Android_GetKeyboardHeight( void );
|
||||
void Android_Shutdown( void );
|
||||
#endif
|
||||
|
||||
#if XASH_WIN32
|
||||
@@ -140,6 +142,15 @@ static inline void Platform_Shutdown( void )
|
||||
#endif
|
||||
}
|
||||
|
||||
static inline qboolean Sys_DebuggerPresent( void )
|
||||
{
|
||||
#if XASH_LINUX || XASH_WIN32
|
||||
return Platform_DebuggerPresent();
|
||||
#else
|
||||
return false;
|
||||
#endif
|
||||
}
|
||||
|
||||
/*
|
||||
==============================================================================
|
||||
|
||||
@@ -256,4 +267,89 @@ void VoiceCapture_Shutdown( void );
|
||||
qboolean VoiceCapture_Activate( qboolean activate );
|
||||
qboolean VoiceCapture_Lock( qboolean lock );
|
||||
|
||||
// this allows to make break in current line, without entering libc code
|
||||
// libc built with -fomit-frame-pointer may just eat stack frame (hello, glibc), making entering libc even more useless
|
||||
// calling syscalls directly allows to make break like if it was asm("int $3") on x86
|
||||
#if XASH_LINUX && XASH_X86
|
||||
#define INLINE_RAISE(x) asm volatile( "int $3;" );
|
||||
#define INLINE_NANOSLEEP1() // nothing!
|
||||
#elif XASH_LINUX && XASH_ARM && !XASH_64BIT
|
||||
#define INLINE_RAISE(x) do \
|
||||
{ \
|
||||
int raise_pid = getpid(); \
|
||||
int raise_tid = gettid(); \
|
||||
int raise_sig = (x); \
|
||||
__asm__ volatile ( \
|
||||
"mov r7,#268\n\t" \
|
||||
"mov r0,%0\n\t" \
|
||||
"mov r1,%1\n\t" \
|
||||
"mov r2,%2\n\t" \
|
||||
"svc 0\n\t" \
|
||||
: \
|
||||
: "r"(raise_pid), "r"(raise_tid), "r"(raise_sig) \
|
||||
: "r0", "r1", "r2", "r7", "memory" \
|
||||
); \
|
||||
} while( 0 )
|
||||
#define INLINE_NANOSLEEP1() do \
|
||||
{ \
|
||||
struct timespec ns_t1 = {1, 0}; \
|
||||
struct timespec ns_t2 = {0, 0}; \
|
||||
__asm__ volatile ( \
|
||||
"mov r7,#162\n\t" \
|
||||
"mov r0,%0\n\t" \
|
||||
"mov r1,%1\n\t" \
|
||||
"svc 0\n\t" \
|
||||
: \
|
||||
: "r"(&ns_t1), "r"(&ns_t2) \
|
||||
: "r0", "r1", "r7", "memory" \
|
||||
); \
|
||||
} while( 0 )
|
||||
#elif XASH_LINUX && XASH_ARM && XASH_64BIT
|
||||
#define INLINE_RAISE(x) do \
|
||||
{ \
|
||||
int raise_pid = getpid(); \
|
||||
int raise_tid = gettid(); \
|
||||
int raise_sig = (x); \
|
||||
__asm__ volatile ( \
|
||||
"mov x8,#131\n\t" \
|
||||
"mov x0,%0\n\t" \
|
||||
"mov x1,%1\n\t" \
|
||||
"mov x2,%2\n\t" \
|
||||
"svc 0\n\t" \
|
||||
: \
|
||||
: "r"(raise_pid), "r"(raise_tid), "r"(raise_sig) \
|
||||
: "x0", "x1", "x2", "x8", "memory", "cc" \
|
||||
); \
|
||||
} while( 0 )
|
||||
#define INLINE_NANOSLEEP1() do \
|
||||
{ \
|
||||
struct timespec ns_t1 = {1, 0}; \
|
||||
struct timespec ns_t2 = {0, 0}; \
|
||||
__asm__ volatile ( \
|
||||
"mov x8,#101\n\t" \
|
||||
"mov x0,%0\n\t" \
|
||||
"mov x1,%1\n\t" \
|
||||
"svc 0\n\t" \
|
||||
: \
|
||||
: "r"(&ns_t1), "r"(&ns_t2) \
|
||||
: "x0", "x1", "x8", "memory", "cc" \
|
||||
); \
|
||||
} while( 0 )
|
||||
#elif XASH_LINUX
|
||||
#ifdef __NR_tgkill
|
||||
#define INLINE_RAISE(x) syscall( __NR_tgkill, getpid(), gettid(), x )
|
||||
#else // __NR_tgkill
|
||||
#define INLINE_RAISE(x) raise(x)
|
||||
#endif // __NR_tgkill
|
||||
#define INLINE_NANOSLEEP1() do \
|
||||
{ \
|
||||
struct timespec ns_t1 = {1, 0}; \
|
||||
struct timespec ns_t2 = {0, 0}; \
|
||||
nanosleep( &ns_t1, &ns_t2 ); \
|
||||
} while( 0 )
|
||||
#else // generic
|
||||
#define INLINE_RAISE(x) raise(x)
|
||||
#define INLINE_NANOSLEEP1() sleep(1)
|
||||
#endif // generic
|
||||
|
||||
#endif // PLATFORM_H
|
||||
|
||||
@@ -212,8 +212,8 @@ const char *COM_NameForFunction( void *hInstance, void *function )
|
||||
// NOTE: dladdr() is a glibc extension
|
||||
{
|
||||
Dl_info info = {0};
|
||||
dladdr( (void*)function, &info );
|
||||
if( info.dli_sname )
|
||||
int ret = dladdr( (void*)function, &info );
|
||||
if( ret && info.dli_sname )
|
||||
return COM_GetPlatformNeutralName( info.dli_sname );
|
||||
}
|
||||
#ifdef XASH_ALLOW_SAVERESTORE_OFFSETS
|
||||
|
||||
@@ -148,7 +148,6 @@ static void SDLash_KeyEvent( SDL_KeyboardEvent key )
|
||||
#else
|
||||
int keynum = key.keysym.sym;
|
||||
#endif
|
||||
qboolean numLock = FBitSet( SDL_GetModState(), KMOD_NUM );
|
||||
|
||||
#if XASH_ANDROID
|
||||
if( keynum == SDL_SCANCODE_VOLUMEUP || keynum == SDL_SCANCODE_VOLUMEDOWN )
|
||||
@@ -157,32 +156,39 @@ static void SDLash_KeyEvent( SDL_KeyboardEvent key )
|
||||
}
|
||||
#endif
|
||||
|
||||
if( SDL_IsTextInputActive() && down && cls.key_dest != key_game )
|
||||
if( SDL_IsTextInputActive( ))
|
||||
{
|
||||
if( FBitSet( SDL_GetModState(), KMOD_CTRL ))
|
||||
// this is how engine understands ctrl+c, ctrl+v and other hotkeys
|
||||
if( down && cls.key_dest != key_game )
|
||||
{
|
||||
if( keynum >= SDL_SCANCODE_A && keynum <= SDL_SCANCODE_Z )
|
||||
if( FBitSet( SDL_GetModState(), KMOD_CTRL ))
|
||||
{
|
||||
keynum = keynum - SDL_SCANCODE_A + 1;
|
||||
CL_CharEvent( keynum );
|
||||
}
|
||||
if( keynum >= SDL_SCANCODE_A && keynum <= SDL_SCANCODE_Z )
|
||||
{
|
||||
keynum = keynum - SDL_SCANCODE_A + 1;
|
||||
CL_CharEvent( keynum );
|
||||
}
|
||||
|
||||
return;
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
#if !SDL_VERSION_ATLEAST( 2, 0, 0 )
|
||||
#if SDL_VERSION_ATLEAST( 2, 0, 0 )
|
||||
// ignore printable keys, they are coming through SDL_TEXTINPUT
|
||||
if(( keynum >= SDL_SCANCODE_A && keynum <= SDL_SCANCODE_Z )
|
||||
|| ( keynum >= SDL_SCANCODE_1 && keynum <= SDL_SCANCODE_0 )
|
||||
|| ( keynum >= SDL_SCANCODE_KP_1 && keynum <= SDL_SCANCODE_KP_0 ))
|
||||
return;
|
||||
#else
|
||||
if( keynum >= SDLK_KP0 && keynum <= SDLK_KP9 )
|
||||
keynum -= SDLK_KP0 + '0';
|
||||
|
||||
if( isprint( keynum ) )
|
||||
if( isprint( keynum ))
|
||||
{
|
||||
if( FBitSet( SDL_GetModState(), KMOD_SHIFT ))
|
||||
{
|
||||
keynum = Key_ToUpper( keynum );
|
||||
}
|
||||
|
||||
CL_CharEvent( keynum );
|
||||
return;
|
||||
}
|
||||
#endif
|
||||
}
|
||||
@@ -198,6 +204,8 @@ static void SDLash_KeyEvent( SDL_KeyboardEvent key )
|
||||
else DECLARE_KEY_RANGE( SDL_SCANCODE_F1, SDL_SCANCODE_F12, K_F1 )
|
||||
else
|
||||
{
|
||||
qboolean numLock = FBitSet( SDL_GetModState(), KMOD_NUM );
|
||||
|
||||
switch( keynum )
|
||||
{
|
||||
case SDL_SCANCODE_GRAVE: keynum = '`'; break;
|
||||
@@ -355,10 +363,7 @@ static void SDLash_InputEvent( SDL_TextInputEvent input )
|
||||
{
|
||||
int ch;
|
||||
|
||||
if( !Q_stricmp( cl_charset.string, "utf-8" ) )
|
||||
ch = (unsigned char)*text;
|
||||
else
|
||||
ch = Con_UtfProcessCharForce( (unsigned char)*text );
|
||||
ch = Con_UtfProcessChar((byte)*text );
|
||||
|
||||
if( !ch )
|
||||
continue;
|
||||
@@ -374,14 +379,8 @@ static void SDLash_ActiveEvent( int gain )
|
||||
{
|
||||
host.status = HOST_FRAME;
|
||||
if( cls.key_dest == key_game )
|
||||
{
|
||||
IN_ActivateMouse( );
|
||||
}
|
||||
|
||||
if( dma.initialized && snd_mute_losefocus.value )
|
||||
{
|
||||
SNDDMA_Activate( true );
|
||||
}
|
||||
host.force_draw_version_time = host.realtime + FORCE_DRAW_VERSION_TIME;
|
||||
if( vid_fullscreen.value == WINDOW_MODE_FULLSCREEN )
|
||||
VID_SetMode();
|
||||
@@ -397,14 +396,8 @@ static void SDLash_ActiveEvent( int gain )
|
||||
#endif
|
||||
host.status = HOST_NOFOCUS;
|
||||
if( cls.key_dest == key_game )
|
||||
{
|
||||
IN_DeactivateMouse();
|
||||
}
|
||||
|
||||
if( dma.initialized && snd_mute_losefocus.value )
|
||||
{
|
||||
SNDDMA_Activate( false );
|
||||
}
|
||||
host.force_draw_version_time = host.realtime + 2.0;
|
||||
VID_RestoreScreenResolution();
|
||||
}
|
||||
|
||||
@@ -44,6 +44,18 @@ Platform_GetMousePos
|
||||
void GAME_EXPORT Platform_GetMousePos( int *x, int *y )
|
||||
{
|
||||
SDL_GetMouseState( x, y );
|
||||
|
||||
if( x && window_width.value && window_width.value != refState.width )
|
||||
{
|
||||
float factor = refState.width / window_width.value;
|
||||
*x = *x * factor;
|
||||
}
|
||||
|
||||
if( y && window_height.value && window_height.value != refState.height )
|
||||
{
|
||||
float factor = refState.height / window_height.value;
|
||||
*y = *y * factor;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
|
||||
@@ -576,10 +576,8 @@ static qboolean VID_SetScreenResolution( int width, int height, window_mode_t wi
|
||||
SDL_DisplayMode got;
|
||||
Uint32 wndFlags = 0;
|
||||
|
||||
#if !XASH_APPLE
|
||||
if( vid_highdpi.value )
|
||||
SetBits( wndFlags, SDL_WINDOW_ALLOW_HIGHDPI );
|
||||
#endif
|
||||
|
||||
SDL_SetWindowBordered( host.hWnd, SDL_FALSE );
|
||||
|
||||
|
||||
@@ -504,12 +504,12 @@ void Wcon_CreateConsole( void )
|
||||
|
||||
if( host.type == HOST_NORMAL )
|
||||
{
|
||||
Q_strncpy( s_wcd.title, "Xash3D " XASH_VERSION, sizeof( s_wcd.title ));
|
||||
Q_strncpy( s_wcd.title, XASH_ENGINE_NAME " " XASH_VERSION, sizeof( s_wcd.title ));
|
||||
Q_strncpy( s_wcd.log_path, "engine.log", sizeof( s_wcd.log_path ));
|
||||
}
|
||||
else // dedicated console
|
||||
{
|
||||
Q_strncpy( s_wcd.title, "XashDS " XASH_VERSION, sizeof( s_wcd.title ));
|
||||
Q_strncpy( s_wcd.title, XASH_DEDICATED_SERVER_NAME " " XASH_VERSION, sizeof( s_wcd.title ));
|
||||
Q_strncpy( s_wcd.log_path, "dedicated.log", sizeof( s_wcd.log_path ));
|
||||
s_wcd.log_active = true; // always make log
|
||||
}
|
||||
|
||||
@@ -41,7 +41,7 @@ void Platform_Sleep( int msec )
|
||||
}
|
||||
#endif // XASH_TIMER == TIMER_WIN32
|
||||
|
||||
qboolean Sys_DebuggerPresent( void )
|
||||
qboolean Platform_DebuggerPresent( void )
|
||||
{
|
||||
return IsDebuggerPresent();
|
||||
}
|
||||
|
||||
@@ -43,10 +43,9 @@ extern int SV_UPDATE_BACKUP;
|
||||
#define SVF_MERGE_VISIBILITY BIT( 1 ) // we are do portal pass
|
||||
|
||||
// mapvalid flags
|
||||
#define MAP_IS_EXIST BIT( 0 )
|
||||
#define MAP_HAS_SPAWNPOINT BIT( 1 )
|
||||
#define MAP_HAS_LANDMARK BIT( 2 )
|
||||
#define MAP_INVALID_VERSION BIT( 3 )
|
||||
#define MAP_IS_EXIST BIT( 0 )
|
||||
#define MAP_HAS_LANDMARK BIT( 2 )
|
||||
#define MAP_INVALID_VERSION BIT( 3 )
|
||||
|
||||
#define SV_SPAWN_TIME 0.1
|
||||
|
||||
@@ -330,6 +329,8 @@ typedef struct
|
||||
int msg_realsize; // left in bytes
|
||||
int msg_index; // for debug messages
|
||||
int msg_dest; // msg destination ( MSG_ONE, MSG_ALL etc )
|
||||
int msg_rewrite_index;
|
||||
int msg_rewrite_pos;
|
||||
qboolean msg_started; // to avoid recursive included messages
|
||||
edict_t *msg_ent; // user message member entity
|
||||
vec3_t msg_org; // user message member origin
|
||||
@@ -393,10 +394,10 @@ typedef struct
|
||||
|
||||
//=============================================================================
|
||||
|
||||
extern server_static_t svs; // persistant server info
|
||||
extern server_t sv; // local server
|
||||
extern svgame_static_t svgame; // persistant game info
|
||||
extern areanode_t sv_areanodes[]; // AABB dynamic tree
|
||||
extern server_static_t svs RENAME_SYMBOL( "svs_" ); // persistant server info
|
||||
extern server_t sv RENAME_SYMBOL( "sv_" ); // local server
|
||||
extern svgame_static_t svgame; // persistant game info
|
||||
extern areanode_t sv_areanodes[]; // AABB dynamic tree
|
||||
|
||||
extern convar_t mp_logecho;
|
||||
extern convar_t mp_logfile;
|
||||
@@ -481,7 +482,7 @@ extern convar_t sv_aim;
|
||||
//
|
||||
void SV_FinalMessage( const char *message, qboolean reconnect );
|
||||
void SV_KickPlayer( sv_client_t *cl, const char *fmt, ... ) _format( 2 );
|
||||
void SV_DropClient( sv_client_t *cl, qboolean crash );
|
||||
void SV_DropClient( sv_client_t *cl, qboolean crash ) RENAME_SYMBOL( "SV_DropClient_" );
|
||||
void SV_UpdateMovevars( qboolean initialize );
|
||||
int SV_ModelIndex( const char *name );
|
||||
int SV_SoundIndex( const char *name );
|
||||
@@ -613,11 +614,9 @@ string_t SV_MakeString( const char *szValue );
|
||||
const char *SV_GetString( string_t iString );
|
||||
void SV_SetStringArrayMode( qboolean dynamic );
|
||||
void SV_EmptyStringPool( void );
|
||||
#ifdef XASH_64BIT
|
||||
void SV_PrintStr64Stats_f( void );
|
||||
#endif
|
||||
sv_client_t *SV_ClientFromEdict( const edict_t *pEdict, qboolean spawned_only );
|
||||
uint SV_MapIsValid( const char *filename, const char *spawn_entity, const char *landmark_name );
|
||||
uint SV_MapIsValid( const char *filename, const char *landmark_name );
|
||||
void SV_StartSound( edict_t *ent, int chan, const char *sample, float vol, float attn, int flags, int pitch );
|
||||
edict_t *SV_FindGlobalEntity( string_t classname, string_t globalname );
|
||||
qboolean SV_CreateStaticEntity( struct sizebuf_s *msg, int index );
|
||||
@@ -628,12 +627,18 @@ void SV_RestartAmbientSounds( void );
|
||||
void SV_RestartDecals( void );
|
||||
void SV_RestartStaticEnts( void );
|
||||
int pfnDropToFloor( edict_t* e );
|
||||
edict_t *SV_EdictNum( int n );
|
||||
void SV_SetModel( edict_t *ent, const char *name );
|
||||
int pfnDecalIndex( const char *m );
|
||||
void SV_CreateDecal( sizebuf_t *msg, const float *origin, int decalIndex, int entityIndex, int modelIndex, int flags, float scale );
|
||||
qboolean SV_RestoreCustomDecal( struct decallist_s *entry, edict_t *pEdict, qboolean adjacent );
|
||||
|
||||
static inline edict_t *SV_EdictNum( int n )
|
||||
{
|
||||
if( likely( n >= 0 && n < GI->max_edicts ))
|
||||
return &svgame.edicts[n];
|
||||
return NULL;
|
||||
}
|
||||
|
||||
//
|
||||
// sv_log.c
|
||||
//
|
||||
|
||||
@@ -417,7 +417,7 @@ static void SV_ConnectClient( netadr_t from )
|
||||
newcl->userid = g_userid++; // create unique userid
|
||||
newcl->state = cs_connected;
|
||||
newcl->extensions = extensions & (NET_EXT_SPLITSIZE);
|
||||
Q_strncpy( newcl->useragent, protinfo, MAX_INFO_STRING );
|
||||
Q_strncpy( newcl->useragent, protinfo, sizeof( newcl->useragent ));
|
||||
|
||||
// reset viewentities (from previous level)
|
||||
memset( newcl->viewentity, 0, sizeof( newcl->viewentity ));
|
||||
@@ -1690,7 +1690,7 @@ static qboolean SV_New_f( sv_client_t *cl )
|
||||
|
||||
// server info string
|
||||
MSG_BeginServerCmd( &msg, svc_stufftext );
|
||||
MSG_WriteStringf( &msg, "fullserverinfo \"%s\"\n", SV_Serverinfo( ));
|
||||
MSG_WriteStringf( &msg, "fullserverinfo \"%s\"\n", svs.serverinfo );
|
||||
|
||||
// collect the info about all the players and send to me
|
||||
for( i = 0, cur = svs.clients; i < svs.maxclients; i++, cur++ )
|
||||
|
||||
@@ -169,17 +169,11 @@ SV_ValidateMap
|
||||
check map for typically errors
|
||||
==================
|
||||
*/
|
||||
static qboolean SV_ValidateMap( const char *pMapName, qboolean check_spawn )
|
||||
static qboolean SV_ValidateMap( const char *pMapName )
|
||||
{
|
||||
char *spawn_entity;
|
||||
int flags;
|
||||
|
||||
// determine spawn entity classname
|
||||
if( !check_spawn || (int)sv_maxclients.value <= 1 )
|
||||
spawn_entity = GI->sp_entity;
|
||||
else spawn_entity = GI->mp_entity;
|
||||
|
||||
flags = SV_MapIsValid( pMapName, spawn_entity, NULL );
|
||||
flags = SV_MapIsValid( pMapName, NULL );
|
||||
|
||||
if( FBitSet( flags, MAP_INVALID_VERSION ))
|
||||
{
|
||||
@@ -193,12 +187,6 @@ static qboolean SV_ValidateMap( const char *pMapName, qboolean check_spawn )
|
||||
return false;
|
||||
}
|
||||
|
||||
if( check_spawn && !FBitSet( flags, MAP_HAS_SPAWNPOINT ))
|
||||
{
|
||||
Con_Printf( S_ERROR "map %s doesn't have a valid spawnpoint\n", pMapName );
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -224,7 +212,7 @@ static void SV_Map_f( void )
|
||||
Q_strncpy( mapname, Cmd_Argv( 1 ), sizeof( mapname ));
|
||||
COM_StripExtension( mapname );
|
||||
|
||||
if( !SV_ValidateMap( mapname, true ))
|
||||
if( !SV_ValidateMap( mapname ))
|
||||
return;
|
||||
|
||||
Cvar_DirectSet( &sv_hostmap, mapname );
|
||||
@@ -296,7 +284,7 @@ static void SV_MapBackground_f( void )
|
||||
Q_strncpy( mapname, Cmd_Argv( 1 ), sizeof( mapname ));
|
||||
COM_StripExtension( mapname );
|
||||
|
||||
if( !SV_ValidateMap( mapname, false ))
|
||||
if( !SV_ValidateMap( mapname ))
|
||||
return;
|
||||
|
||||
// background map is always run as singleplayer
|
||||
@@ -346,7 +334,7 @@ static void SV_NextMap_f( void )
|
||||
Cvar_DirectSet( &sv_hostmap, nextmap );
|
||||
|
||||
// found current point, check for valid
|
||||
if( SV_ValidateMap( nextmap, true ))
|
||||
if( SV_ValidateMap( nextmap ))
|
||||
{
|
||||
// found and valid
|
||||
COM_LoadLevel( nextmap, false );
|
||||
@@ -368,13 +356,12 @@ SV_NewGame_f
|
||||
*/
|
||||
static void SV_NewGame_f( void )
|
||||
{
|
||||
if( Cmd_Argc() != 1 )
|
||||
{
|
||||
if( Cmd_Argc() == 1 )
|
||||
COM_NewGame( GI->startmap );
|
||||
else if( Cmd_Argc() == 2 )
|
||||
COM_NewGame( Cmd_Argv( 1 ));
|
||||
else
|
||||
Con_Printf( S_USAGE "newgame\n" );
|
||||
return;
|
||||
}
|
||||
|
||||
COM_NewGame( GI->startmap );
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -547,7 +534,7 @@ classic change level
|
||||
*/
|
||||
static void SV_ChangeLevel_f( void )
|
||||
{
|
||||
if( Cmd_Argc() != 2 )
|
||||
if( Cmd_Argc() < 2 ) // allow extra arguments, for compatibility
|
||||
{
|
||||
Con_Printf( S_USAGE "changelevel <mapname>\n" );
|
||||
return;
|
||||
@@ -565,13 +552,15 @@ smooth change level
|
||||
*/
|
||||
static void SV_ChangeLevel2_f( void )
|
||||
{
|
||||
if( Cmd_Argc() != 3 )
|
||||
if( Cmd_Argc() < 2 ) // allow extra arguments, for compatibility
|
||||
{
|
||||
Con_Printf( S_USAGE "changelevel2 <mapname> <landmark>\n" );
|
||||
Con_Printf( S_USAGE "changelevel2 <mapname> [landmark]\n" );
|
||||
return;
|
||||
}
|
||||
|
||||
SV_QueueChangeLevel( Cmd_Argv( 1 ), Cmd_Argv( 2 ));
|
||||
if( Cmd_Argc() == 2 ) // with single argument, behaves like usual changelevel
|
||||
SV_QueueChangeLevel( Cmd_Argv( 1 ), NULL );
|
||||
else SV_QueueChangeLevel( Cmd_Argv( 1 ), Cmd_Argv( 2 ));
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -701,7 +690,7 @@ static void SV_ConSay_f( void )
|
||||
}
|
||||
|
||||
p = Cmd_Args();
|
||||
Q_strncpy( text, *p == '"' ? p + 1 : p, MAX_SYSPATH );
|
||||
Q_strncpy( text, *p == '"' ? p + 1 : p, sizeof( text ));
|
||||
|
||||
if( *p == '"' )
|
||||
{
|
||||
@@ -764,7 +753,7 @@ static void SV_ServerInfo_f( void )
|
||||
}
|
||||
|
||||
Info_SetValueForStarKey( svs.serverinfo, Cmd_Argv( 1 ), Cmd_Argv( 2 ), MAX_SERVERINFO_STRING );
|
||||
SV_BroadcastCommand( "fullserverinfo \"%s\"\n", SV_Serverinfo( ));
|
||||
SV_BroadcastCommand( "fullserverinfo \"%s\"\n", svs.serverinfo );
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -1018,6 +1007,7 @@ void SV_InitOperatorCommands( void )
|
||||
Cmd_AddCommand( "redirect", Rcon_Redirect_f, "force enable rcon redirection" );
|
||||
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( "str64stats", SV_PrintStr64Stats_f, "print engine pool string statistics" );
|
||||
|
||||
if( host.type == HOST_NORMAL )
|
||||
{
|
||||
@@ -1056,6 +1046,7 @@ void SV_KillOperatorCommands( void )
|
||||
Cmd_RemoveCommand( "redirect" );
|
||||
Cmd_RemoveCommand( "logaddress" );
|
||||
Cmd_RemoveCommand( "log" );
|
||||
Cmd_RemoveCommand( "str64stats" );
|
||||
|
||||
if( host.type == HOST_NORMAL )
|
||||
{
|
||||
|
||||
@@ -811,17 +811,17 @@ static void SV_UpdateToReliableMessages( void )
|
||||
continue; // reliables go to all connected or spawned
|
||||
|
||||
if( MSG_GetNumBytesWritten( &sv.reliable_datagram ) < MSG_GetNumBytesLeft( &cl->netchan.message ))
|
||||
MSG_WriteBits( &cl->netchan.message, MSG_GetBuf( &sv.reliable_datagram ), MSG_GetNumBitsWritten( &sv.reliable_datagram ));
|
||||
MSG_WriteBits( &cl->netchan.message, MSG_GetData( &sv.reliable_datagram ), MSG_GetNumBitsWritten( &sv.reliable_datagram ));
|
||||
else Netchan_CreateFragments( &cl->netchan, &sv.reliable_datagram );
|
||||
|
||||
if( MSG_GetNumBytesWritten( &sv.datagram ) < MSG_GetNumBytesLeft( &cl->datagram ))
|
||||
MSG_WriteBits( &cl->datagram, MSG_GetBuf( &sv.datagram ), MSG_GetNumBitsWritten( &sv.datagram ));
|
||||
MSG_WriteBits( &cl->datagram, MSG_GetData( &sv.datagram ), MSG_GetNumBitsWritten( &sv.datagram ));
|
||||
else Con_DPrintf( S_WARN "Ignoring unreliable datagram for %s, would overflow\n", cl->name );
|
||||
|
||||
if( FBitSet( cl->flags, FCL_HLTV_PROXY ))
|
||||
{
|
||||
if( MSG_GetNumBytesWritten( &sv.spec_datagram ) < MSG_GetNumBytesLeft( &cl->datagram ))
|
||||
MSG_WriteBits( &cl->datagram, MSG_GetBuf( &sv.spec_datagram ), MSG_GetNumBitsWritten( &sv.spec_datagram ));
|
||||
MSG_WriteBits( &cl->datagram, MSG_GetData( &sv.spec_datagram ), MSG_GetNumBitsWritten( &sv.spec_datagram ));
|
||||
else Con_DPrintf( S_WARN "Ignoring spectator datagram for %s, would overflow\n", cl->name );
|
||||
}
|
||||
}
|
||||
|
||||
@@ -39,13 +39,6 @@ static vec3_t viewPoint[MAX_CLIENTS];
|
||||
typedef void (__cdecl *LINK_ENTITY_FUNC)( entvars_t *pev );
|
||||
typedef void (__stdcall *GIVEFNPTRSTODLL)( enginefuncs_t* engfuncs, globalvars_t *pGlobals );
|
||||
|
||||
edict_t *SV_EdictNum( int n )
|
||||
{
|
||||
if(( n >= 0 ) && ( n < GI->max_edicts ))
|
||||
return svgame.edicts + n;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
#ifndef NDEBUG
|
||||
qboolean SV_CheckEdict( const edict_t *e, const char *file, const int line )
|
||||
{
|
||||
@@ -150,18 +143,6 @@ char *SV_Serverinfo( void )
|
||||
return svs.serverinfo;
|
||||
}
|
||||
|
||||
/*
|
||||
=============
|
||||
SV_LocalInfo
|
||||
|
||||
get local infostring
|
||||
=============
|
||||
*/
|
||||
static char *SV_Localinfo( void )
|
||||
{
|
||||
return svs.localinfo;
|
||||
}
|
||||
|
||||
/*
|
||||
=============
|
||||
SV_AngleMod
|
||||
@@ -268,7 +249,6 @@ SV_SetModel
|
||||
void GAME_EXPORT SV_SetModel( edict_t *ent, const char *modelname )
|
||||
{
|
||||
char name[MAX_QPATH];
|
||||
qboolean found = false;
|
||||
model_t *mod;
|
||||
int i = 1;
|
||||
|
||||
@@ -686,7 +666,7 @@ void SV_RestartAmbientSounds( void )
|
||||
|
||||
#if !XASH_DEDICATED // TODO: ???
|
||||
// restart soundtrack
|
||||
if( S_StreamGetCurrentState( curtrack, looptrack, &position ))
|
||||
if( S_StreamGetCurrentState( curtrack, sizeof( curtrack ), looptrack, sizeof( looptrack ), &position ))
|
||||
{
|
||||
SV_StartMusic( curtrack, looptrack, position );
|
||||
}
|
||||
@@ -778,7 +758,6 @@ void SV_QueueChangeLevel( const char *level, const char *landname )
|
||||
{
|
||||
uint flags, smooth = false;
|
||||
char mapname[MAX_QPATH];
|
||||
char *spawn_entity;
|
||||
|
||||
// hold mapname to other place
|
||||
Q_strncpy( mapname, level, sizeof( mapname ));
|
||||
@@ -787,12 +766,7 @@ void SV_QueueChangeLevel( const char *level, const char *landname )
|
||||
if( COM_CheckString( landname ))
|
||||
smooth = true;
|
||||
|
||||
// determine spawn entity classname
|
||||
if( svs.maxclients == 1 )
|
||||
spawn_entity = GI->sp_entity;
|
||||
else spawn_entity = GI->mp_entity;
|
||||
|
||||
flags = SV_MapIsValid( mapname, spawn_entity, landname );
|
||||
flags = SV_MapIsValid( mapname, landname );
|
||||
|
||||
if( FBitSet( flags, MAP_INVALID_VERSION ))
|
||||
{
|
||||
@@ -826,15 +800,6 @@ void SV_QueueChangeLevel( const char *level, const char *landname )
|
||||
return;
|
||||
}
|
||||
|
||||
if( !smooth && !FBitSet( flags, MAP_HAS_SPAWNPOINT ))
|
||||
{
|
||||
if( sv_validate_changelevel.value )
|
||||
{
|
||||
Con_Printf( S_ERROR "changelevel: %s doesn't have a valid spawnpoint. Ignored.\n", mapname );
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// bad changelevel position invoke enables in one-way transition
|
||||
if( sv.framecount < 15 )
|
||||
{
|
||||
@@ -864,7 +829,6 @@ void SV_WriteEntityPatch( const char *filename )
|
||||
int lumpofs = 0, lumplen = 0;
|
||||
byte buf[MAX_TOKEN]; // 1 kb
|
||||
string bspfilename;
|
||||
dheader_t *header;
|
||||
dlump_t entities;
|
||||
file_t *f;
|
||||
|
||||
@@ -875,7 +839,6 @@ void SV_WriteEntityPatch( const char *filename )
|
||||
|
||||
memset( buf, 0, MAX_TOKEN );
|
||||
FS_Read( f, buf, MAX_TOKEN );
|
||||
header = (dheader_t *)buf;
|
||||
|
||||
// check all the lumps and some other errors
|
||||
if( !Mod_TestBmodelLumps( f, bspfilename, buf, true, &entities ))
|
||||
@@ -915,7 +878,6 @@ static char *SV_ReadEntityScript( const char *filename, int *flags )
|
||||
int lumpofs = 0, lumplen = 0;
|
||||
byte buf[MAX_TOKEN];
|
||||
char *ents = NULL;
|
||||
dheader_t *header;
|
||||
dlump_t entities;
|
||||
size_t ft1, ft2;
|
||||
file_t *f;
|
||||
@@ -930,7 +892,6 @@ static char *SV_ReadEntityScript( const char *filename, int *flags )
|
||||
SetBits( *flags, MAP_IS_EXIST );
|
||||
memset( buf, 0, MAX_TOKEN );
|
||||
FS_Read( f, buf, MAX_TOKEN );
|
||||
header = (dheader_t *)buf;
|
||||
|
||||
// check all the lumps and some other errors
|
||||
if( !Mod_TestBmodelLumps( f, bspfilename, buf, (host_developer.value) ? false : true, &entities ))
|
||||
@@ -977,7 +938,7 @@ SV_MapIsValid
|
||||
Validate map
|
||||
==============
|
||||
*/
|
||||
uint SV_MapIsValid( const char *filename, const char *spawn_entity, const char *landmark_name )
|
||||
uint SV_MapIsValid( const char *filename, const char *landmark_name )
|
||||
{
|
||||
uint flags = 0;
|
||||
char *pfile;
|
||||
@@ -993,45 +954,27 @@ uint SV_MapIsValid( const char *filename, const char *spawn_entity, const char *
|
||||
|
||||
need_landmark = COM_CheckString( landmark_name );
|
||||
|
||||
// g-cont. in-dev mode we can entering on map even without "info_player_start"
|
||||
if( !need_landmark && host_developer.value )
|
||||
if( !need_landmark )
|
||||
{
|
||||
// not transition
|
||||
Mem_Free( ents );
|
||||
|
||||
// skip spawnpoint checks in devmode
|
||||
return (flags|MAP_HAS_SPAWNPOINT);
|
||||
return flags;
|
||||
}
|
||||
|
||||
pfile = ents;
|
||||
|
||||
while(( pfile = COM_ParseFile( pfile, token, sizeof( token ))) != NULL )
|
||||
{
|
||||
if( !Q_strcmp( token, "classname" ))
|
||||
{
|
||||
// check classname for spawn entity
|
||||
pfile = COM_ParseFile( pfile, check_name, sizeof( check_name ));
|
||||
if( !Q_strcmp( spawn_entity, check_name ))
|
||||
{
|
||||
SetBits( flags, MAP_HAS_SPAWNPOINT );
|
||||
|
||||
// we already find landmark, stop the parsing
|
||||
if( need_landmark && FBitSet( flags, MAP_HAS_LANDMARK ))
|
||||
break;
|
||||
}
|
||||
}
|
||||
else if( need_landmark && !Q_strcmp( token, "targetname" ))
|
||||
if( !Q_strcmp( token, "targetname" ))
|
||||
{
|
||||
// check targetname for landmark entity
|
||||
pfile = COM_ParseFile( pfile, check_name, sizeof( check_name ));
|
||||
|
||||
if( !Q_strcmp( landmark_name, check_name ))
|
||||
{
|
||||
// we found landmark, stop the parsing
|
||||
SetBits( flags, MAP_HAS_LANDMARK );
|
||||
|
||||
// we already find spawnpoint, stop the parsing
|
||||
if( FBitSet( flags, MAP_HAS_SPAWNPOINT ))
|
||||
break;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1174,15 +1117,22 @@ static LINK_ENTITY_FUNC SV_GetEntityClass( const char *pszClassName )
|
||||
SV_AllocPrivateData
|
||||
|
||||
allocate private data for a given edict
|
||||
|
||||
if customentity is NULL, no "custom" entity EXPORT is being done
|
||||
if customentity is not NULL, will be set to true if "custom" export
|
||||
was used to create this entity
|
||||
==============
|
||||
*/
|
||||
static edict_t* SV_AllocPrivateData( edict_t *ent, string_t className )
|
||||
static edict_t* SV_AllocPrivateData( edict_t *ent, string_t className, qboolean *customentity )
|
||||
{
|
||||
const char *pszClassName;
|
||||
LINK_ENTITY_FUNC SpawnEdict;
|
||||
|
||||
pszClassName = STRING( className );
|
||||
|
||||
if( customentity )
|
||||
*customentity = false;
|
||||
|
||||
if( !ent )
|
||||
{
|
||||
// allocate a new one
|
||||
@@ -1205,7 +1155,11 @@ static edict_t* SV_AllocPrivateData( edict_t *ent, string_t className )
|
||||
if( svgame.physFuncs.SV_CreateEntity && svgame.physFuncs.SV_CreateEntity( ent, pszClassName ) != -1 )
|
||||
return ent;
|
||||
|
||||
SpawnEdict = SV_GetEntityClass( "custom" );
|
||||
if( customentity )
|
||||
{
|
||||
SpawnEdict = SV_GetEntityClass( "custom" );
|
||||
*customentity = SpawnEdict != NULL;
|
||||
}
|
||||
|
||||
if( !SpawnEdict )
|
||||
{
|
||||
@@ -1216,8 +1170,6 @@ static edict_t* SV_AllocPrivateData( edict_t *ent, string_t className )
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
SetBits( ent->v.flags, FL_CUSTOMENTITY ); // it's a custom entity but not a beam!
|
||||
}
|
||||
|
||||
SpawnEdict( &ent->v );
|
||||
@@ -1234,12 +1186,7 @@ create specified entity, alloc private data
|
||||
*/
|
||||
edict_t* SV_CreateNamedEntity( edict_t *ent, string_t className )
|
||||
{
|
||||
edict_t *ed = SV_AllocPrivateData( ent, className );
|
||||
|
||||
// for some reasons this flag should be immediately cleared
|
||||
if( ed ) ClearBits( ed->v.flags, FL_CUSTOMENTITY );
|
||||
|
||||
return ed;
|
||||
return SV_AllocPrivateData( ent, className, NULL );
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -2554,6 +2501,62 @@ int GAME_EXPORT pfnDecalIndex( const char *m )
|
||||
return -1;
|
||||
}
|
||||
|
||||
static int SV_CanRewriteMessage( int msg_num )
|
||||
{
|
||||
// feature is disabled
|
||||
if( !FBitSet( host.bugcomp, BUGCOMP_MESSAGE_REWRITE_FACILITY_FLAG ))
|
||||
return 0;
|
||||
|
||||
switch( msg_num )
|
||||
{
|
||||
case svc_goldsrc_spawnstaticsound:
|
||||
return svc_sound;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static qboolean SV_RewriteMessage( void )
|
||||
{
|
||||
vec3_t origin;
|
||||
const char *sample = NULL;
|
||||
float vol, attn;
|
||||
int ent, pitch, flags, idx;
|
||||
int cmd;
|
||||
|
||||
MSG_SeekToBit( &sv.multicast, svgame.msg_rewrite_pos, SEEK_SET );
|
||||
|
||||
cmd = MSG_ReadCmd( &sv.multicast, NS_SERVER );
|
||||
|
||||
switch( cmd )
|
||||
{
|
||||
case svc_goldsrc_spawnstaticsound:
|
||||
MSG_ReadVec3Coord( &sv.multicast, origin );
|
||||
idx = MSG_ReadShort( &sv.multicast );
|
||||
vol = MSG_ReadByte( &sv.multicast );
|
||||
attn = MSG_ReadByte( &sv.multicast ) / 64.0f;
|
||||
ent = MSG_ReadShort( &sv.multicast );
|
||||
pitch = MSG_ReadByte( &sv.multicast );
|
||||
flags = MSG_ReadByte( &sv.multicast );
|
||||
|
||||
if( FBitSet( flags, SND_SENTENCE ))
|
||||
sample = va( "!%i", idx );
|
||||
else if( idx >= 0 && idx < MAX_SOUNDS )
|
||||
sample = sv.sound_precache[idx];
|
||||
|
||||
if( !COM_CheckString( sample ))
|
||||
{
|
||||
Con_Printf( S_ERROR "%s: unrecognized sample in svc_spawnstaticsound, index %d, flags 0x%x\n", __func__, idx, flags );
|
||||
return false;
|
||||
}
|
||||
|
||||
MSG_SeekToBit( &sv.multicast, svgame.msg_rewrite_pos, SEEK_SET );
|
||||
return SV_BuildSoundMsg( &sv.multicast, EDICT_NUM( ent ), CHAN_STATIC, sample, vol, attn, flags, pitch, origin );
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/*
|
||||
=============
|
||||
pfnMessageBegin
|
||||
@@ -2571,10 +2574,24 @@ static void GAME_EXPORT pfnMessageBegin( int msg_dest, int msg_num, const float
|
||||
// check range
|
||||
msg_num = bound( svc_bad, msg_num, 255 );
|
||||
|
||||
svgame.msg_rewrite_index = 0;
|
||||
svgame.msg_rewrite_pos = 0;
|
||||
|
||||
if( msg_num <= svc_lastmsg )
|
||||
{
|
||||
svgame.msg_index = -msg_num; // this is a system message
|
||||
svgame.msg_name = svc_strings[msg_num];
|
||||
// check if we should rewrite this message into something else...
|
||||
if( SV_CanRewriteMessage( msg_num ))
|
||||
{
|
||||
svgame.msg_index = -SV_CanRewriteMessage( msg_num );
|
||||
svgame.msg_name = svc_goldsrc_strings[msg_num] ? svc_goldsrc_strings[msg_num] : svc_strings[msg_num];
|
||||
svgame.msg_rewrite_index = msg_num;
|
||||
svgame.msg_rewrite_pos = MSG_TellBit( &sv.multicast );
|
||||
}
|
||||
else
|
||||
{
|
||||
svgame.msg_index = -msg_num; // this is a system message
|
||||
svgame.msg_name = svc_strings[msg_num];
|
||||
}
|
||||
|
||||
if( msg_num == svc_temp_entity )
|
||||
iSize = -1; // temp entity have variable size
|
||||
@@ -2649,6 +2666,25 @@ static void GAME_EXPORT pfnMessageEnd( void )
|
||||
return;
|
||||
}
|
||||
|
||||
if( svgame.msg_rewrite_index != 0 )
|
||||
{
|
||||
if( SV_RewriteMessage( ))
|
||||
{
|
||||
if( MSG_CheckOverflow( &sv.multicast ))
|
||||
{
|
||||
Con_Printf( S_ERROR "MessageEnd: %s has overflow multicast buffer (post-rewrite)\n", name );
|
||||
MSG_Clear( &sv.multicast );
|
||||
return;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
Con_Printf( S_ERROR "MessageEnd: failed to rewrite message %s\n", name );
|
||||
MSG_Clear( &sv.multicast );
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// check for system message
|
||||
if( svgame.msg_index < 0 )
|
||||
{
|
||||
@@ -2715,9 +2751,7 @@ static void GAME_EXPORT pfnMessageEnd( void )
|
||||
// update some messages in case their was format was changed and we want to keep backward compatibility
|
||||
if( svgame.msg_index < 0 )
|
||||
{
|
||||
int svc_msg = abs( svgame.msg_index );
|
||||
|
||||
if(( svc_msg == svc_finale || svc_msg == svc_cutscene ) && svgame.msg_realsize == 0 )
|
||||
if(( svgame.msg_index == -svc_finale || svgame.msg_index == -svc_cutscene ) && svgame.msg_realsize == 0 )
|
||||
MSG_WriteChar( &sv.multicast, 0 ); // write null string
|
||||
}
|
||||
|
||||
@@ -2851,8 +2885,10 @@ standard path to register game variable
|
||||
static void GAME_EXPORT pfnCvar_RegisterServerVariable( cvar_t *variable )
|
||||
{
|
||||
if( variable != NULL )
|
||||
{
|
||||
SetBits( variable->flags, FCVAR_EXTDLL );
|
||||
Cvar_RegisterVariable( (convar_t *)variable );
|
||||
Cvar_RegisterVariable( (convar_t *)variable );
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -3218,7 +3254,9 @@ string_t GAME_EXPORT SV_AllocString( const char *szValue )
|
||||
{
|
||||
char *newString = NULL;
|
||||
uint len;
|
||||
#ifdef XASH_64BIT
|
||||
int cmp;
|
||||
#endif
|
||||
|
||||
if( svgame.physFuncs.pfnAllocString != NULL )
|
||||
{
|
||||
@@ -3281,19 +3319,21 @@ string_t GAME_EXPORT SV_AllocString( const char *szValue )
|
||||
#endif
|
||||
}
|
||||
|
||||
#ifdef XASH_64BIT
|
||||
void SV_PrintStr64Stats_f( void )
|
||||
{
|
||||
Msg( "====================\n" );
|
||||
Msg( "64 bit string pool statistics\n" );
|
||||
Msg( "====================\n" );
|
||||
Msg( "string array size: %lu\n", str64.maxstringarray );
|
||||
Msg( "total alloc %lu\n", str64.totalalloc );
|
||||
Msg( "maximum array usage: %lu\n", str64.maxalloc );
|
||||
Msg( "overflow counter: %lu\n", str64.numoverflows );
|
||||
Msg( "dup string counter: %lu\n", str64.numdups );
|
||||
}
|
||||
#ifdef XASH_64BIT
|
||||
Con_Printf( "====================\n" );
|
||||
Con_Printf( "64 bit string pool statistics\n" );
|
||||
Con_Printf( "====================\n" );
|
||||
Con_Printf( "string array size: %lu\n", str64.maxstringarray );
|
||||
Con_Printf( "total alloc %lu\n", str64.totalalloc );
|
||||
Con_Printf( "maximum array usage: %lu\n", str64.maxalloc );
|
||||
Con_Printf( "overflow counter: %lu\n", str64.numoverflows );
|
||||
Con_Printf( "dup string counter: %lu\n", str64.numdups );
|
||||
#else
|
||||
Con_Printf( "Not implemented\n" );
|
||||
#endif
|
||||
}
|
||||
|
||||
/*
|
||||
=============
|
||||
@@ -3391,12 +3431,10 @@ pfnPEntityOfEntIndex
|
||||
|
||||
=============
|
||||
*/
|
||||
static edict_t *pfnPEntityOfEntIndex( int iEntIndex )
|
||||
static edict_t *pfnPEntityOfEntIndexBroken( int iEntIndex )
|
||||
{
|
||||
// have to be bug-compatible with GoldSrc in this function
|
||||
if( host.bugcomp == BUGCOMP_GOLDSRC )
|
||||
return SV_PEntityOfEntIndex( iEntIndex, false );
|
||||
return SV_PEntityOfEntIndex( iEntIndex, true );
|
||||
return SV_PEntityOfEntIndex( iEntIndex, false );
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -3737,9 +3775,9 @@ vaild map must contain one info_player_deatchmatch
|
||||
*/
|
||||
int GAME_EXPORT pfnIsMapValid( char *filename )
|
||||
{
|
||||
uint flags = SV_MapIsValid( filename, GI->mp_entity, NULL );
|
||||
uint flags = SV_MapIsValid( filename, NULL );
|
||||
|
||||
if( FBitSet( flags, MAP_IS_EXIST ) && FBitSet( flags, MAP_HAS_SPAWNPOINT ))
|
||||
if( FBitSet( flags, MAP_IS_EXIST ))
|
||||
return true;
|
||||
return false;
|
||||
}
|
||||
@@ -3873,11 +3911,11 @@ static char *GAME_EXPORT pfnGetInfoKeyBuffer( edict_t *e )
|
||||
|
||||
// NULL passes localinfo
|
||||
if( !SV_IsValidEdict( e ))
|
||||
return SV_Localinfo();
|
||||
return svs.localinfo;
|
||||
|
||||
// world passes serverinfo
|
||||
if( e == svgame.edicts )
|
||||
return SV_Serverinfo();
|
||||
return svs.serverinfo;
|
||||
|
||||
// userinfo for specified edict
|
||||
if(( cl = SV_ClientFromEdict( e, false )) != NULL )
|
||||
@@ -4144,7 +4182,16 @@ void GAME_EXPORT SV_PlaybackEventFull( int flags, const edict_t *pInvoker, word
|
||||
continue;
|
||||
}
|
||||
|
||||
if( FBitSet( flags, FEV_NOTHOST ) && cl == sv.current_client && FBitSet( cl->flags, FCL_LOCAL_WEAPONS ))
|
||||
// a1ba: GoldSrc never cleans up host_client pointer (similar to sv.current_client)
|
||||
// so it's always points at some client and in singleplayer this check always succeedes
|
||||
// in Xash, however, sv.current_client might be reset and set to NULL
|
||||
// this is especially dangerous when weapons play events in Think functions
|
||||
//
|
||||
// IMHO, it doesn't make sense to me to compare it against current client when we have
|
||||
// invoker edict pointer but to preserve behaviour check for them both
|
||||
//
|
||||
// if it breaks some mods, probably sv.current_client semantics must be reworked to match GoldSrc
|
||||
if( FBitSet( flags, FEV_NOTHOST ) && ( cl == sv.current_client || cl->edict == pInvoker ) && FBitSet( cl->flags, FCL_LOCAL_WEAPONS ))
|
||||
continue; // will be played on client side
|
||||
|
||||
if( FBitSet( flags, FEV_HOSTONLY ) && cl->edict != pInvoker )
|
||||
@@ -4731,7 +4778,7 @@ static enginefuncs_t gEngfuncs =
|
||||
pfnPEntityOfEntOffset,
|
||||
pfnEntOffsetOfPEntity,
|
||||
pfnIndexOfEdict,
|
||||
pfnPEntityOfEntIndex,
|
||||
pfnPEntityOfEntIndexAllEntities,
|
||||
pfnFindEntityByVars,
|
||||
pfnGetModelPtr,
|
||||
pfnRegUserMsg,
|
||||
@@ -4820,6 +4867,15 @@ static enginefuncs_t gEngfuncs =
|
||||
pfnPEntityOfEntIndexAllEntities,
|
||||
};
|
||||
|
||||
static void SV_FreeKeyValueStrings( KeyValueData *kvd, int numpairs )
|
||||
{
|
||||
for( int i = 0; i < numpairs; i++ )
|
||||
{
|
||||
Mem_Free( kvd[i].szKeyName );
|
||||
Mem_Free( kvd[i].szValue );
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
====================
|
||||
SV_ParseEdict
|
||||
@@ -4831,36 +4887,34 @@ ed should be a properly initialized empty edict.
|
||||
static qboolean SV_ParseEdict( char **pfile, edict_t *ent )
|
||||
{
|
||||
KeyValueData pkvd[256]; // per one entity
|
||||
qboolean adjust_origin = false;
|
||||
qboolean adjust_origin = false, customentity;
|
||||
int i, numpairs = 0;
|
||||
char *classname = NULL;
|
||||
char token[2048];
|
||||
vec3_t origin;
|
||||
const char *classname = NULL;
|
||||
|
||||
// go through all the dictionary pairs
|
||||
while( 1 )
|
||||
{
|
||||
string keyname;
|
||||
char value[2048];
|
||||
int len;
|
||||
|
||||
// parse key
|
||||
if(( *pfile = COM_ParseFile( *pfile, token, sizeof( token ))) == NULL )
|
||||
if(( *pfile = COM_ParseFile( *pfile, keyname, sizeof( keyname ))) == NULL )
|
||||
Host_Error( "ED_ParseEdict: EOF without closing brace\n" );
|
||||
if( token[0] == '}' ) break; // end of desc
|
||||
|
||||
Q_strncpy( keyname, token, sizeof( keyname ));
|
||||
if( keyname[0] == '}' )
|
||||
break; // end of desc
|
||||
|
||||
// parse value
|
||||
if(( *pfile = COM_ParseFile( *pfile, token, sizeof( token ))) == NULL )
|
||||
if(( *pfile = COM_ParseFile( *pfile, value, sizeof( value ))) == NULL )
|
||||
Host_Error( "ED_ParseEdict: EOF without closing brace\n" );
|
||||
|
||||
if( token[0] == '}' )
|
||||
if( value[0] == '}' )
|
||||
Host_Error( "ED_ParseEdict: closing brace without data\n" );
|
||||
|
||||
// ignore attempts to set key ""
|
||||
if( !keyname[0] ) continue;
|
||||
|
||||
// ignore attempts to set empty key or value
|
||||
// "wad" field is already handled
|
||||
if( !Q_strcmp( keyname, "wad" ))
|
||||
if( !keyname[0] || !value[0] || !Q_strcmp( keyname, "wad" ))
|
||||
continue;
|
||||
|
||||
// keynames with a leading underscore are used for
|
||||
@@ -4868,57 +4922,83 @@ static qboolean SV_ParseEdict( char **pfile, edict_t *ent )
|
||||
if( FBitSet( world.flags, FWORLD_SKYSPHERE ) && keyname[0] == '_' )
|
||||
continue;
|
||||
|
||||
// ignore attempts to set value ""
|
||||
if( !token[0] ) continue;
|
||||
// classname must be first
|
||||
if( !Q_strcmp( keyname, "classname" ))
|
||||
{
|
||||
KeyValueData kvd = {
|
||||
.szClassName = NULL,
|
||||
.szKeyName = keyname,
|
||||
.szValue = value,
|
||||
.fHandled = false
|
||||
};
|
||||
|
||||
// don't allow double classnames
|
||||
if( classname != NULL )
|
||||
continue;
|
||||
|
||||
svgame.dllFuncs.pfnKeyValue( ent, &kvd );
|
||||
|
||||
// ideally, all game dlls should handle classname.
|
||||
// throw an error for now, improve the logic if it causes
|
||||
// compatibility issues with Xash-based games
|
||||
if( !kvd.fHandled )
|
||||
Host_Error( "%s: game didn't handled \"%s\" classname\n", __func__, value );
|
||||
|
||||
// this lets game dll override custom entity classname
|
||||
// to something bogus that's exported in game dll
|
||||
classname = STRING( ent->v.classname );
|
||||
continue;
|
||||
}
|
||||
|
||||
// GoldSrc removes trailing spaces
|
||||
// but does this after sucking out classname
|
||||
// which doesn't have similar check
|
||||
for( len = Q_strlen( keyname ); len > 0 && keyname[len - 1] == ' '; len-- )
|
||||
keyname[len - 1] = '\0';
|
||||
|
||||
// create keyvalue strings
|
||||
pkvd[numpairs].szClassName = (char*)""; // unknown at this moment
|
||||
pkvd[numpairs].szKeyName = copystring( keyname );
|
||||
pkvd[numpairs].szValue = copystring( token );
|
||||
pkvd[numpairs].szValue = copystring( value );
|
||||
pkvd[numpairs].fHandled = false;
|
||||
numpairs++;
|
||||
|
||||
if( !Q_strcmp( keyname, "classname" ) && classname == NULL )
|
||||
classname = copystring( pkvd[numpairs].szValue );
|
||||
if( ++numpairs >= 256 ) break;
|
||||
if( numpairs > ARRAYSIZE( pkvd ))
|
||||
{
|
||||
if( classname )
|
||||
Con_Printf( S_ERROR "%s: too many keyvalue pairs for %s!\n", __func__, classname );
|
||||
else Con_Printf( S_ERROR "%s: too many keyvalue pairs!\n", __func__ );
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if( classname == NULL )
|
||||
{
|
||||
// release allocated strings
|
||||
for( i = 0; i < numpairs; i++ )
|
||||
{
|
||||
Mem_Free( pkvd[i].szKeyName );
|
||||
Mem_Free( pkvd[i].szValue );
|
||||
}
|
||||
SV_FreeKeyValueStrings( pkvd, numpairs );
|
||||
return false;
|
||||
}
|
||||
|
||||
ent = SV_AllocPrivateData( ent, ALLOC_STRING( classname ));
|
||||
ent = SV_AllocPrivateData( ent, ent->v.classname, &customentity );
|
||||
|
||||
if( !SV_IsValidEdict( ent ) || FBitSet( ent->v.flags, FL_KILLME ))
|
||||
{
|
||||
// release allocated strings
|
||||
for( i = 0; i < numpairs; i++ )
|
||||
{
|
||||
Mem_Free( pkvd[i].szKeyName );
|
||||
Mem_Free( pkvd[i].szValue );
|
||||
}
|
||||
SV_FreeKeyValueStrings( pkvd, numpairs );
|
||||
return false;
|
||||
}
|
||||
|
||||
if( FBitSet( ent->v.flags, FL_CUSTOMENTITY ))
|
||||
if( customentity )
|
||||
{
|
||||
if( numpairs < 256 )
|
||||
{
|
||||
pkvd[numpairs].szClassName = (char*)"custom";
|
||||
pkvd[numpairs].szKeyName = (char*)"customclass";
|
||||
pkvd[numpairs].szValue = classname;
|
||||
pkvd[numpairs].fHandled = false;
|
||||
numpairs++;
|
||||
}
|
||||
KeyValueData kvd = {
|
||||
.szClassName = (char *)"custom",
|
||||
.szKeyName = (char *)"customclass",
|
||||
.szValue = (char *)classname,
|
||||
.fHandled = false
|
||||
};
|
||||
|
||||
// clear it now - no longer used
|
||||
ClearBits( ent->v.flags, FL_CUSTOMENTITY );
|
||||
svgame.dllFuncs.pfnKeyValue( ent, &kvd );
|
||||
// no fHandled check, GoldSrc behavior
|
||||
}
|
||||
|
||||
#ifdef HACKS_RELATED_HLMODS
|
||||
@@ -4932,6 +5012,14 @@ static qboolean SV_ParseEdict( char **pfile, edict_t *ent )
|
||||
|
||||
for( i = 0; i < numpairs; i++ )
|
||||
{
|
||||
char *keyname, *value;
|
||||
char temp[MAX_VA_STRING];
|
||||
|
||||
#if 0 // this is stupid bug in GoldSrc, disable
|
||||
if( !Q_strcmp( pkvd[i].szValue, classname ))
|
||||
continue;
|
||||
#endif
|
||||
|
||||
if( !Q_strcmp( pkvd[i].szKeyName, "angle" ))
|
||||
{
|
||||
float flYawAngle = Q_atof( pkvd[i].szValue );
|
||||
@@ -4942,8 +5030,6 @@ static qboolean SV_ParseEdict( char **pfile, edict_t *ent )
|
||||
|
||||
if( flYawAngle >= 0.0f )
|
||||
{
|
||||
char temp[MAX_VA_STRING];
|
||||
|
||||
Q_snprintf( temp, sizeof( temp ), "%g %g %g", ent->v.angles[0], flYawAngle, ent->v.angles[2] );
|
||||
pkvd[i].szValue = copystring( temp );
|
||||
}
|
||||
@@ -4954,11 +5040,10 @@ static qboolean SV_ParseEdict( char **pfile, edict_t *ent )
|
||||
else pkvd[i].szValue = copystring( "0 0 0" ); // technically an error
|
||||
}
|
||||
|
||||
#ifdef HACKS_RELATED_HLMODS
|
||||
if( adjust_origin && !Q_strcmp( pkvd[i].szKeyName, "origin" ))
|
||||
{
|
||||
char temp[MAX_VA_STRING];
|
||||
char *pstart = pkvd[i].szValue;
|
||||
char *pstart = pkvd[i].szValue;
|
||||
vec3_t origin;
|
||||
|
||||
COM_ParseVector( &pstart, origin, 3 );
|
||||
Mem_Free( pkvd[i].szValue ); // release old value, so we don't need these
|
||||
@@ -4966,24 +5051,18 @@ static qboolean SV_ParseEdict( char **pfile, edict_t *ent )
|
||||
Q_snprintf( temp, sizeof( temp ), "%g %g %g", origin[0], origin[1], origin[2] - 16.0f );
|
||||
pkvd[i].szValue = copystring( temp );
|
||||
}
|
||||
#endif
|
||||
if( !pkvd[i].fHandled )
|
||||
{
|
||||
pkvd[i].szClassName = classname;
|
||||
svgame.dllFuncs.pfnKeyValue( ent, &pkvd[i] );
|
||||
}
|
||||
|
||||
// no reason to keep this data
|
||||
if( Mem_IsAllocatedExt( host.mempool, pkvd[i].szKeyName ))
|
||||
Mem_Free( pkvd[i].szKeyName );
|
||||
// do not leak memory if game overwritten these pointers
|
||||
keyname = pkvd[i].szKeyName;
|
||||
value = pkvd[i].szValue;
|
||||
|
||||
if( Mem_IsAllocatedExt( host.mempool, pkvd[i].szValue ))
|
||||
Mem_Free( pkvd[i].szValue );
|
||||
pkvd[i].szClassName = (char *)classname;
|
||||
svgame.dllFuncs.pfnKeyValue( ent, &pkvd[i] );
|
||||
|
||||
Mem_Free( keyname );
|
||||
Mem_Free( value );
|
||||
}
|
||||
|
||||
if( Mem_IsAllocatedExt( host.mempool, classname ))
|
||||
Mem_Free( classname );
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -5102,8 +5181,6 @@ void SV_UnloadProgs( void )
|
||||
/// SV_UnloadProgs will be disabled
|
||||
//Mod_ClearUserData ();
|
||||
|
||||
SV_FreeStringPool();
|
||||
|
||||
if( svgame.dllFuncs2.pfnGameShutdown != NULL )
|
||||
svgame.dllFuncs2.pfnGameShutdown ();
|
||||
|
||||
@@ -5124,6 +5201,8 @@ void SV_UnloadProgs( void )
|
||||
Cvar_Unlink( FCVAR_EXTDLL );
|
||||
Cmd_Unlink( CMD_SERVERDLL );
|
||||
|
||||
SV_FreeStringPool();
|
||||
|
||||
Mod_ResetStudioAPI ();
|
||||
|
||||
COM_FreeLibrary( svgame.hInstance );
|
||||
@@ -5170,6 +5249,11 @@ qboolean SV_LoadProgs( const char *name )
|
||||
// make sure what physic functions is cleared
|
||||
memset( &svgame.physFuncs, 0, sizeof( svgame.physFuncs ));
|
||||
|
||||
// revert fix for pfnPEntityOfEntIndex to be compatible with GoldSrc
|
||||
// games that rely on this bug
|
||||
if( FBitSet( host.bugcomp, BUGCOMP_PENTITYOFENTINDEX_FLAG ))
|
||||
gEngfuncs.pfnPEntityOfEntIndex = pfnPEntityOfEntIndexBroken;
|
||||
|
||||
// make local copy of engfuncs to prevent overwrite it with bots.dll
|
||||
memcpy( &gpEngfuncs, &gEngfuncs, sizeof( gpEngfuncs ));
|
||||
|
||||
@@ -5182,7 +5266,7 @@ qboolean SV_LoadProgs( const char *name )
|
||||
COM_FreeLibrary( svgame.hInstance );
|
||||
Con_Printf( S_ERROR "SV_LoadProgs: failed to get address of GetEntityAPI proc\n" );
|
||||
svgame.hInstance = NULL;
|
||||
Mem_FreePool(&svgame.mempool);
|
||||
Mem_FreePool( &svgame.mempool );
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -5193,7 +5277,7 @@ qboolean SV_LoadProgs( const char *name )
|
||||
COM_FreeLibrary( svgame.hInstance );
|
||||
Con_Printf( S_ERROR "SV_LoadProgs: failed to get address of GiveFnptrsToDll proc\n" );
|
||||
svgame.hInstance = NULL;
|
||||
Mem_FreePool(&svgame.mempool);
|
||||
Mem_FreePool( &svgame.mempool );
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -5218,7 +5302,8 @@ qboolean SV_LoadProgs( const char *name )
|
||||
{
|
||||
if( !GetEntityAPI2( &svgame.dllFuncs, &version ))
|
||||
{
|
||||
Con_Printf( S_WARN "SV_LoadProgs: interface version %i should be %i\n", INTERFACE_VERSION, version );
|
||||
if( INTERFACE_VERSION != version )
|
||||
Con_Printf( S_WARN "SV_LoadProgs: interface version %i should be %i\n", INTERFACE_VERSION, version );
|
||||
|
||||
// fallback to old API
|
||||
if( !GetEntityAPI( &svgame.dllFuncs, version ))
|
||||
@@ -5226,9 +5311,10 @@ qboolean SV_LoadProgs( const char *name )
|
||||
COM_FreeLibrary( svgame.hInstance );
|
||||
Con_Printf( S_ERROR "SV_LoadProgs: couldn't get entity API\n" );
|
||||
svgame.hInstance = NULL;
|
||||
Mem_FreePool(&svgame.mempool);
|
||||
Mem_FreePool( &svgame.mempool );
|
||||
return false;
|
||||
}
|
||||
else Con_Reportf( "SV_LoadProgs: ^2initailized legacy EntityAPI ^7ver. %i\n", version );
|
||||
}
|
||||
else Con_Reportf( "SV_LoadProgs: ^2initailized extended EntityAPI ^7ver. %i\n", version );
|
||||
}
|
||||
@@ -5237,9 +5323,10 @@ qboolean SV_LoadProgs( const char *name )
|
||||
COM_FreeLibrary( svgame.hInstance );
|
||||
Con_Printf( S_ERROR "SV_LoadProgs: couldn't get entity API\n" );
|
||||
svgame.hInstance = NULL;
|
||||
Mem_FreePool(&svgame.mempool);
|
||||
Mem_FreePool( &svgame.mempool );
|
||||
return false;
|
||||
}
|
||||
else Con_Reportf( "SV_LoadProgs: ^2initailized legacy EntityAPI ^7ver. %i\n", version );
|
||||
|
||||
SV_InitOperatorCommands();
|
||||
Mod_InitStudioAPI();
|
||||
|
||||
@@ -922,11 +922,11 @@ static void SV_GenerateTestPacket( void )
|
||||
// testpacket already generated once, exit
|
||||
// testpacket and lookup table takes ~300k of memory
|
||||
// disable for low memory mode
|
||||
if( svs.testpacket_buf || XASH_LOW_MEMORY >= 0 )
|
||||
if( svs.testpacket_buf || XASH_LOW_MEMORY > 0 )
|
||||
return;
|
||||
|
||||
// don't need in singleplayer with full client
|
||||
if( svs.maxclients <= 1 && !Host_IsDedicated( ))
|
||||
if( svs.maxclients <= 1 )
|
||||
return;
|
||||
|
||||
file = FS_Open( "gfx.wad", "rb", false );
|
||||
|
||||
@@ -77,7 +77,7 @@ void Log_Open( void )
|
||||
|
||||
if( fp ) svs.log.file = fp;
|
||||
Log_Printf( "Log file started (file \"%s\") (game \"%s\") (version \"%i/" XASH_VERSION "/%d\")\n",
|
||||
szTestFile, Info_ValueForKey( SV_Serverinfo(), "*gamedir" ), PROTOCOL_VERSION, Q_buildnum() );
|
||||
szTestFile, Info_ValueForKey( svs.serverinfo, "*gamedir" ), PROTOCOL_VERSION, Q_buildnum() );
|
||||
}
|
||||
|
||||
void Log_Close( void )
|
||||
|
||||
@@ -418,10 +418,8 @@ static void GAME_EXPORT pfnPlaybackEventFull( int flags, int clientindex, word e
|
||||
ent = EDICT_NUM( clientindex + 1 );
|
||||
if( !SV_IsValidEdict( ent )) return;
|
||||
|
||||
if( Host_IsDedicated() )
|
||||
flags |= FEV_NOTHOST; // no local clients for dedicated server
|
||||
|
||||
SV_PlaybackEventFull( flags, ent, eventindex,
|
||||
// GoldSrc always sets FEV_NOTHOST in PMove version of this function
|
||||
SV_PlaybackEventFull( flags | FEV_NOTHOST, ent, eventindex,
|
||||
delay, origin, angles,
|
||||
fparam1, fparam2,
|
||||
iparam1, iparam2,
|
||||
@@ -590,7 +588,7 @@ static void SV_SetupPMove( playermove_t *pmove, sv_client_t *cl, usercmd_t *ucmd
|
||||
pmove->cmd = *ucmd; // setup current cmds
|
||||
pmove->runfuncs = true;
|
||||
|
||||
Q_strncpy( pmove->physinfo, physinfo, MAX_INFO_STRING );
|
||||
Q_strncpy( pmove->physinfo, physinfo, sizeof( pmove->physinfo ));
|
||||
|
||||
// setup physents
|
||||
pmove->numvisent = 0;
|
||||
|
||||
@@ -652,7 +652,7 @@ static void DirectoryCopy( const char *pPath, file_t *pFile )
|
||||
fileSize = FS_FileLength( pCopy );
|
||||
|
||||
memset( szName, 0, sizeof( szName )); // clearing the string to prevent garbage in output file
|
||||
Q_strncpy( szName, COM_FileWithoutPath( t->filenames[i] ), MAX_OSPATH );
|
||||
Q_strncpy( szName, COM_FileWithoutPath( t->filenames[i] ), sizeof( szName ));
|
||||
FS_Write( pFile, szName, MAX_OSPATH );
|
||||
FS_Write( pFile, &fileSize, sizeof( int ));
|
||||
FS_FileCopy( pFile, pCopy, fileSize );
|
||||
@@ -1202,7 +1202,7 @@ static void SaveClientState( SAVERESTOREDATA *pSaveData, const char *level, int
|
||||
header.soundCount = S_GetCurrentDynamicSounds( soundInfo, MAX_CHANNELS );
|
||||
#if !XASH_DEDICATED
|
||||
// music not reqiured to save position: it's just continue playing on a next level
|
||||
S_StreamGetCurrentState( header.introTrack, header.mainTrack, &header.trackPosition );
|
||||
S_StreamGetCurrentState( header.introTrack, sizeof( header.introTrack ), header.mainTrack, sizeof( header.mainTrack ), &header.trackPosition );
|
||||
#endif
|
||||
}
|
||||
|
||||
@@ -2110,7 +2110,7 @@ qboolean SV_LoadGame( const char *pPath )
|
||||
if( validload )
|
||||
{
|
||||
// now check for map problems
|
||||
flags = SV_MapIsValid( gameHeader.mapName, GI->sp_entity, NULL );
|
||||
flags = SV_MapIsValid( gameHeader.mapName, NULL );
|
||||
|
||||
if( FBitSet( flags, MAP_INVALID_VERSION ))
|
||||
{
|
||||
@@ -2386,7 +2386,7 @@ int GAME_EXPORT SV_GetSaveComment( const char *savename, char *comment )
|
||||
uint flags;
|
||||
|
||||
// now check for map problems
|
||||
flags = SV_MapIsValid( mapName, GI->sp_entity, NULL );
|
||||
flags = SV_MapIsValid( mapName, NULL );
|
||||
|
||||
if( FBitSet( flags, MAP_INVALID_VERSION ))
|
||||
{
|
||||
|
||||
@@ -21,16 +21,16 @@ def options(opt):
|
||||
help = 'enable custom swap allocator. For devices with no swap support')
|
||||
|
||||
grp.add_option('--enable-legacy-sdl', action = 'store_true', dest = 'SDL12', default = False,
|
||||
help = 'enable using SDL1.2 instead of SDL2(not recommended) [default: %default]')
|
||||
help = 'enable using SDL1.2 instead of SDL2(not recommended) [default: %(default)s]')
|
||||
|
||||
grp.add_option('--enable-static-binary', action = 'store_true', dest = 'STATIC', default = False,
|
||||
help = 'build static binary(not recommended, --single-binary required) [default: %default]')
|
||||
help = 'build static binary(not recommended, --single-binary required) [default: %(default)s]')
|
||||
|
||||
grp.add_option('--enable-engine-tests', action = 'store_true', dest = 'ENGINE_TESTS', default = False,
|
||||
help = 'embed tests into the engine, jump into them by -runtests command line switch [default: %default]')
|
||||
help = 'embed tests into the engine, jump into them by -runtests command line switch [default: %(default)s]')
|
||||
|
||||
grp.add_option('--enable-engine-fuzz', action = 'store_true', dest = 'ENGINE_FUZZ', default = False,
|
||||
help = 'add LLVM libFuzzer [default: %default]' )
|
||||
help = 'add LLVM libFuzzer [default: %(default)s]' )
|
||||
|
||||
opt.load('sdl2')
|
||||
|
||||
|
||||
@@ -320,6 +320,12 @@ public:
|
||||
return nullptr;
|
||||
|
||||
state = new CSearchState( &searchHead, search );
|
||||
if( !state )
|
||||
{
|
||||
Mem_Free( search );
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
*handle = state->handle;
|
||||
return state->search->filenames[0];
|
||||
}
|
||||
|
||||
@@ -276,6 +276,9 @@ searchpath_t *FS_AddAndroidAssets_Fullpath( const char *path, int flags )
|
||||
android_assets_t *assets = NULL;
|
||||
qboolean engine = true;
|
||||
|
||||
if( !jni.getPackageName || !jni.getCallingPackage || !jni.getAssetsList || !jni.getAssets )
|
||||
return NULL;
|
||||
|
||||
if( FBitSet( flags, FS_STATIC_PATH | FS_CUSTOM_PATH ))
|
||||
return NULL;
|
||||
|
||||
@@ -286,7 +289,7 @@ searchpath_t *FS_AddAndroidAssets_Fullpath( const char *path, int flags )
|
||||
|
||||
if( !assets )
|
||||
{
|
||||
Con_Reportf( S_ERROR "%s: unable to load Android assets \"%s\"\n", __FUNCTION__, Android_GetPackageName( engine ));
|
||||
Con_Reportf( S_ERROR "%s: unable to load Android assets \"%s\"\n", __func__, Android_GetPackageName( engine ));
|
||||
return NULL;
|
||||
}
|
||||
|
||||
@@ -326,6 +329,9 @@ void FS_InitAndroid( void )
|
||||
jni.getCallingPackage = (*jni.env)->GetMethodID( jni.env, jni.activity_class, "getCallingPackage", "()Ljava/lang/String;" );
|
||||
jni.getAssetsList = (*jni.env)->GetMethodID( jni.env, jni.activity_class, "getAssetsList", "(ZLjava/lang/String;)[Ljava/lang/String;" );
|
||||
jni.getAssets = (*jni.env)->GetMethodID( jni.env, jni.activity_class, "getAssets", "(Z)Landroid/content/res/AssetManager;" );
|
||||
|
||||
if( !jni.getPackageName || !jni.getCallingPackage || !jni.getAssetsList || !jni.getAssets )
|
||||
Con_Reportf( S_WARN "%s: unable to find required JNI interface to load Android assets\n", __func__ );
|
||||
}
|
||||
|
||||
#endif // XASH_ANDROID
|
||||
|
||||
@@ -72,7 +72,7 @@ const fs_archive_t g_archives[] =
|
||||
static const fs_archive_t g_directory_archive =
|
||||
{ NULL, SEARCHPATH_PLAIN, FS_AddDir_Fullpath, false };
|
||||
|
||||
#ifdef XASH_ANDROID
|
||||
#if XASH_ANDROID
|
||||
static const fs_archive_t g_android_archive =
|
||||
{ NULL, SEARCHPATH_ANDROID_ASSETS, FS_AddAndroidAssets_Fullpath, false };
|
||||
#endif
|
||||
@@ -406,7 +406,7 @@ void FS_AddGameDirectory( const char *dir, uint flags )
|
||||
|
||||
stringlistfreecontents( &list );
|
||||
|
||||
#ifdef XASH_ANDROID
|
||||
#if XASH_ANDROID
|
||||
FS_AddArchive_Fullpath( &g_android_archive, dir, flags );
|
||||
#endif
|
||||
|
||||
@@ -632,30 +632,23 @@ static void FS_InitGameInfo( gameinfo_t *GameInfo, const char *gamedir )
|
||||
memset( GameInfo, 0, sizeof( *GameInfo ));
|
||||
|
||||
// filesystem info
|
||||
Q_strncpy( GameInfo->gamefolder, gamedir, sizeof( GameInfo->gamefolder ));
|
||||
Q_strncpy( GameInfo->basedir, "valve", sizeof( GameInfo->basedir ));
|
||||
GameInfo->falldir[0] = 0;
|
||||
Q_strncpy( GameInfo->startmap, "c0a0", sizeof( GameInfo->startmap ));
|
||||
Q_strncpy( GameInfo->trainmap, "t0a0", sizeof( GameInfo->trainmap ));
|
||||
Q_strncpy( GameInfo->title, "New Game", sizeof( GameInfo->title ));
|
||||
GameInfo->version = 1.0f;
|
||||
|
||||
// .dll pathes
|
||||
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->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 ));
|
||||
|
||||
// .ico path
|
||||
Q_strncpy( GameInfo->iconpath, "game.ico", sizeof( GameInfo->iconpath ));
|
||||
|
||||
Q_strncpy( GameInfo->sp_entity, "info_player_start", sizeof( GameInfo->sp_entity ));
|
||||
Q_strncpy( GameInfo->mp_entity, "info_player_deathmatch", sizeof( GameInfo->mp_entity ));
|
||||
|
||||
GameInfo->max_edicts = DEFAULT_MAX_EDICTS; // default value if not specified
|
||||
GameInfo->max_tents = 500;
|
||||
GameInfo->max_beams = 128;
|
||||
GameInfo->max_particles = 4096;
|
||||
GameInfo->version = 1.0f;
|
||||
}
|
||||
|
||||
static void FS_ParseGenericGameInfo( gameinfo_t *GameInfo, const char *buf, const qboolean isGameInfo )
|
||||
@@ -1370,8 +1363,13 @@ static qboolean FS_FindLibrary( const char *dllname, qboolean directpath, fs_dll
|
||||
|
||||
if( index >= 0 && !dllInfo->encrypted && search )
|
||||
{
|
||||
Q_snprintf( dllInfo->fullPath, sizeof( dllInfo->fullPath ),
|
||||
"%s%s", search->filename, dllInfo->shortPath );
|
||||
// gamedll might resolve it's own path using dladdr()
|
||||
// combine it with engine returned path to gamedir
|
||||
// it might lead to double gamedir like this
|
||||
// - valve/valve/dlls/hl.so
|
||||
// instead of expected
|
||||
// - valve/dlls/hl.so
|
||||
Q_snprintf( dllInfo->fullPath, sizeof( dllInfo->fullPath ), "%s/%s%s", fs_rootdir, search->filename, dllInfo->shortPath );
|
||||
dllInfo->custom_loader = false; // we can loading from disk and use normal debugging
|
||||
}
|
||||
else
|
||||
@@ -1468,7 +1466,7 @@ qboolean FS_InitStdio( qboolean unused_set_to_true, const char *rootdir, const c
|
||||
|
||||
FS_InitMemory();
|
||||
|
||||
#ifdef XASH_ANDROID
|
||||
#if XASH_ANDROID
|
||||
FS_InitAndroid();
|
||||
#endif
|
||||
|
||||
@@ -2127,7 +2125,7 @@ fs_offset_t FS_Read( file_t *file, void *buffer, size_t buffersize )
|
||||
{
|
||||
fs_offset_t done;
|
||||
fs_offset_t nb;
|
||||
size_t count;
|
||||
fs_offset_t count;
|
||||
|
||||
// nothing to copy
|
||||
if( buffersize == 0 ) return 1;
|
||||
@@ -2146,12 +2144,13 @@ fs_offset_t FS_Read( file_t *file, void *buffer, size_t buffersize )
|
||||
if( file->buff_ind < file->buff_len )
|
||||
{
|
||||
count = file->buff_len - file->buff_ind;
|
||||
count = ( buffersize > count ) ? count : (fs_offset_t)buffersize;
|
||||
|
||||
done += ( buffersize > count ) ? (fs_offset_t)count : (fs_offset_t)buffersize;
|
||||
memcpy( buffer, &file->buff[file->buff_ind], done );
|
||||
file->buff_ind += done;
|
||||
done += count;
|
||||
memcpy( buffer, &file->buff[file->buff_ind], count );
|
||||
file->buff_ind += count;
|
||||
|
||||
buffersize -= done;
|
||||
buffersize -= count;
|
||||
if( buffersize == 0 )
|
||||
return done;
|
||||
}
|
||||
@@ -2165,10 +2164,10 @@ fs_offset_t FS_Read( file_t *file, void *buffer, size_t buffersize )
|
||||
// if we have a lot of data to get, put them directly into "buffer"
|
||||
if( buffersize > sizeof( file->buff ) / 2 )
|
||||
{
|
||||
if( count > buffersize )
|
||||
count = buffersize;
|
||||
if( count > (fs_offset_t)buffersize )
|
||||
count = (fs_offset_t)buffersize;
|
||||
lseek( file->handle, file->offset + file->position, SEEK_SET );
|
||||
nb = read( file->handle, (byte *)buffer + done, count );
|
||||
nb = read( file->handle, &((byte *)buffer)[done], count );
|
||||
|
||||
if( nb > 0 )
|
||||
{
|
||||
@@ -2180,8 +2179,8 @@ fs_offset_t FS_Read( file_t *file, void *buffer, size_t buffersize )
|
||||
}
|
||||
else
|
||||
{
|
||||
if( count > sizeof( file->buff ))
|
||||
count = sizeof( file->buff );
|
||||
if( count > (fs_offset_t)sizeof( file->buff ))
|
||||
count = (fs_offset_t)sizeof( file->buff );
|
||||
lseek( file->handle, file->offset + file->position, SEEK_SET );
|
||||
nb = read( file->handle, file->buff, count );
|
||||
|
||||
|
||||
@@ -31,7 +31,7 @@ extern "C"
|
||||
{
|
||||
#endif // __cplusplus
|
||||
|
||||
#define FS_API_VERSION 2 // not stable yet!
|
||||
#define FS_API_VERSION 3 // not stable yet!
|
||||
#define FS_API_CREATEINTERFACE_TAG "XashFileSystem002" // follow FS_API_VERSION!!!
|
||||
#define FILESYSTEM_INTERFACE_VERSION "VFileSystem009" // never change this!
|
||||
|
||||
@@ -121,8 +121,8 @@ typedef enum
|
||||
|
||||
typedef struct fs_dllinfo_t
|
||||
{
|
||||
string fullPath;
|
||||
string shortPath;
|
||||
char fullPath[2048]; // absolute disk path
|
||||
string shortPath; // vfs path
|
||||
qboolean encrypted;
|
||||
qboolean custom_loader;
|
||||
} fs_dllinfo_t;
|
||||
|
||||
@@ -358,7 +358,7 @@ searchpath_t *FS_AddPak_Fullpath( const char *pakfile, int flags )
|
||||
search->pfnFindFile = FS_FindFile_PAK;
|
||||
search->pfnSearch = FS_Search_PAK;
|
||||
|
||||
Con_Reportf( "Adding pakfile: %s (%i files)\n", pakfile, pak->numfiles );
|
||||
Con_Reportf( "Adding PAK: %s (%i files)\n", pakfile, pak->numfiles );
|
||||
|
||||
return search;
|
||||
}
|
||||
|
||||
@@ -670,6 +670,6 @@ searchpath_t *FS_AddWad_Fullpath( const char *wadfile, int flags )
|
||||
search->pfnSearch = FS_Search_WAD;
|
||||
search->pfnLoadFile = W_ReadLump;
|
||||
|
||||
Con_Reportf( "Adding wadfile: %s (%i files)\n", wadfile, wad->numlumps );
|
||||
Con_Reportf( "Adding WAD: %s (%i files)\n", wadfile, wad->numlumps );
|
||||
return search;
|
||||
}
|
||||
|
||||
@@ -701,7 +701,7 @@ searchpath_t *FS_AddZip_Fullpath( const char *zipfile, int flags )
|
||||
search->pfnSearch = FS_Search_ZIP;
|
||||
search->pfnLoadFile = FS_LoadZIPFile;
|
||||
|
||||
Con_Reportf( "Adding zipfile: %s (%i files)\n", zipfile, zip->numfiles );
|
||||
Con_Reportf( "Adding ZIP: %s (%i files)\n", zipfile, zip->numfiles );
|
||||
return search;
|
||||
}
|
||||
|
||||
|
||||
@@ -12,7 +12,7 @@ def options(opt):
|
||||
grp = opt.add_option_group('Game launcher options')
|
||||
|
||||
grp.add_option('--disable-menu-changegame', action = 'store_true', dest = 'DISABLE_MENU_CHANGEGAME', default = False,
|
||||
help = 'disable changing the game from the menu [default: %default]')
|
||||
help = 'disable changing the game from the menu [default: %(default)s]')
|
||||
|
||||
def configure(conf):
|
||||
if conf.env.DEST_OS == 'win32':
|
||||
|
||||
@@ -237,19 +237,25 @@ const char *Q_buildarch( void )
|
||||
=============
|
||||
Q_buildcommit
|
||||
|
||||
Returns a short hash of current commit in VCS as string.
|
||||
Returns a short hash of current commit in VCS as string
|
||||
XASH_BUILD_COMMIT must be passed in quotes
|
||||
|
||||
if XASH_BUILD_COMMIT is not defined,
|
||||
Q_buildcommit will identify this build as "notset"
|
||||
=============
|
||||
*/
|
||||
const char *Q_buildcommit( void )
|
||||
{
|
||||
#ifdef XASH_BUILD_COMMIT
|
||||
return XASH_BUILD_COMMIT;
|
||||
#else
|
||||
return "notset";
|
||||
#endif
|
||||
}
|
||||
|
||||
/*
|
||||
=============
|
||||
Q_buildbranch
|
||||
|
||||
Returns current branch name in VCS as string
|
||||
XASH_BUILD_BRANCH must be passed in quotes
|
||||
=============
|
||||
*/
|
||||
const char *Q_buildbranch( void )
|
||||
{
|
||||
return XASH_BUILD_BRANCH;
|
||||
}
|
||||
|
||||
|
||||
@@ -434,7 +434,7 @@ returns hash key for string
|
||||
*/
|
||||
uint COM_HashKey( const char *string, uint hashSize )
|
||||
{
|
||||
int hashKey = 5381;
|
||||
uint hashKey = 5381;
|
||||
unsigned char i;
|
||||
|
||||
while(( i = *string++ ))
|
||||
|
||||
@@ -18,7 +18,6 @@ GNU General Public License for more details.
|
||||
#include "const.h"
|
||||
#include <math.h>
|
||||
#include <stdarg.h>
|
||||
#include <ctype.h>
|
||||
#include <time.h>
|
||||
#include "stdio.h"
|
||||
#include "crtlib.h"
|
||||
@@ -38,26 +37,6 @@ void Q_strnlwr( const char *in, char *out, size_t size_out )
|
||||
*out = '\0';
|
||||
}
|
||||
|
||||
qboolean Q_isdigit( const char *str )
|
||||
{
|
||||
if( str && *str )
|
||||
{
|
||||
while( isdigit( *str )) str++;
|
||||
if( !*str ) return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
qboolean Q_isspace( const char *str )
|
||||
{
|
||||
if( str && *str )
|
||||
{
|
||||
while( isspace( *str ) ) str++;
|
||||
if( !*str ) return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
size_t Q_colorstr( const char *string )
|
||||
{
|
||||
size_t len;
|
||||
@@ -81,59 +60,6 @@ size_t Q_colorstr( const char *string )
|
||||
return len;
|
||||
}
|
||||
|
||||
char Q_toupper( const char in )
|
||||
{
|
||||
char out;
|
||||
|
||||
if( in >= 'a' && in <= 'z' )
|
||||
out = in + 'A' - 'a';
|
||||
else out = in;
|
||||
|
||||
return out;
|
||||
}
|
||||
|
||||
char Q_tolower( const char in )
|
||||
{
|
||||
char out;
|
||||
|
||||
if( in >= 'A' && in <= 'Z' )
|
||||
out = in + 'a' - 'A';
|
||||
else out = in;
|
||||
|
||||
return out;
|
||||
}
|
||||
|
||||
size_t Q_strncat( char *dst, const char *src, size_t size )
|
||||
{
|
||||
register char *d = dst;
|
||||
register const char *s = src;
|
||||
register size_t n = size;
|
||||
size_t dlen;
|
||||
|
||||
if( !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 )
|
||||
{
|
||||
*d++ = *s;
|
||||
n--;
|
||||
}
|
||||
s++;
|
||||
}
|
||||
|
||||
*d = '\0';
|
||||
return( dlen + ( s - src )); // count does not include NULL
|
||||
}
|
||||
|
||||
int Q_atoi( const char *str )
|
||||
{
|
||||
int val = 0;
|
||||
@@ -253,13 +179,11 @@ float Q_atof( const char *str )
|
||||
|
||||
void Q_atov( float *vec, const char *str, size_t siz )
|
||||
{
|
||||
string buffer;
|
||||
char *pstr, *pfront;
|
||||
const char *pstr, *pfront;
|
||||
int j;
|
||||
|
||||
Q_strncpy( buffer, str, sizeof( buffer ));
|
||||
memset( vec, 0, sizeof( vec_t ) * siz );
|
||||
pstr = pfront = buffer;
|
||||
pstr = pfront = str;
|
||||
|
||||
for( j = 0; j < siz; j++ )
|
||||
{
|
||||
@@ -394,7 +318,7 @@ const char* Q_timestamp( int format )
|
||||
return timestamp;
|
||||
}
|
||||
|
||||
#if !defined( HAVE_STRCASESTR )
|
||||
#if !HAVE_STRCASESTR
|
||||
char *Q_stristr( const char *string, const char *string2 )
|
||||
{
|
||||
int c;
|
||||
@@ -721,14 +645,14 @@ void COM_ReplaceExtension( char *path, const char *extension, size_t size )
|
||||
COM_RemoveLineFeed
|
||||
============
|
||||
*/
|
||||
void COM_RemoveLineFeed( char *str )
|
||||
void COM_RemoveLineFeed( char *str, size_t bufsize )
|
||||
{
|
||||
while( *str != '\0' )
|
||||
size_t i;
|
||||
|
||||
for( i = 0; i < bufsize && *str != '\0'; i++, str++ )
|
||||
{
|
||||
if( *str == '\r' || *str == '\n' )
|
||||
*str = '\0';
|
||||
|
||||
++str;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
186
public/crtlib.h
186
public/crtlib.h
@@ -18,6 +18,7 @@ GNU General Public License for more details.
|
||||
|
||||
#include <string.h>
|
||||
#include <stdarg.h>
|
||||
#include <ctype.h>
|
||||
#include "build.h"
|
||||
#include "xash3d_types.h"
|
||||
|
||||
@@ -55,6 +56,7 @@ const char *Q_buildos( void );
|
||||
const char *Q_ArchitectureStringByID( const int arch, const uint abi, const int endianness, const qboolean is64 );
|
||||
const char *Q_buildarch( void );
|
||||
const char *Q_buildcommit( void );
|
||||
const char *Q_buildbranch( void );
|
||||
|
||||
//
|
||||
// crtlib.c
|
||||
@@ -62,11 +64,6 @@ const char *Q_buildcommit( void );
|
||||
void Q_strnlwr( const char *in, char *out, size_t size_out );
|
||||
#define Q_strlen( str ) (( str ) ? strlen(( str )) : 0 )
|
||||
size_t Q_colorstr( const char *string );
|
||||
char Q_toupper( const char in );
|
||||
char Q_tolower( const char in );
|
||||
size_t Q_strncat( char *dst, const char *src, size_t siz );
|
||||
qboolean Q_isdigit( const char *str );
|
||||
qboolean Q_isspace( const char *str );
|
||||
int Q_atoi( const char *str );
|
||||
float Q_atof( const char *str );
|
||||
void Q_atov( float *vec, const char *str, size_t siz );
|
||||
@@ -89,7 +86,7 @@ void COM_ReplaceExtension( char *path, const char *extension, size_t size );
|
||||
void COM_ExtractFilePath( const char *path, char *dest );
|
||||
const char *COM_FileWithoutPath( const char *in );
|
||||
void COM_StripExtension( char *path );
|
||||
void COM_RemoveLineFeed( char *str );
|
||||
void COM_RemoveLineFeed( char *str, size_t bufsize );
|
||||
void COM_FixSlashes( char *pname );
|
||||
void COM_PathSlashFix( char *path );
|
||||
char COM_Hex2Char( uint8_t hex );
|
||||
@@ -102,91 +99,186 @@ 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_with_separator( const char *in, const char *pattern, qboolean caseinsensitive, const char *separators, qboolean wildcard_least_one );
|
||||
|
||||
// libc implementations
|
||||
static inline char Q_toupper( const char in )
|
||||
{
|
||||
char out;
|
||||
|
||||
if( in >= 'a' && in <= 'z' )
|
||||
out = in + 'A' - 'a';
|
||||
else out = in;
|
||||
|
||||
return out;
|
||||
}
|
||||
|
||||
static inline char Q_tolower( const char in )
|
||||
{
|
||||
char out;
|
||||
|
||||
if( in >= 'A' && in <= 'Z' )
|
||||
out = in + 'a' - 'A';
|
||||
else out = in;
|
||||
|
||||
return out;
|
||||
}
|
||||
|
||||
static inline qboolean Q_isdigit( const char *str )
|
||||
{
|
||||
if( likely( str && *str ))
|
||||
{
|
||||
while( isdigit( *str )) str++;
|
||||
if( !*str ) return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
static inline qboolean Q_isspace( const char *str )
|
||||
{
|
||||
if( likely( str && *str ))
|
||||
{
|
||||
while( isspace( *str ) ) str++;
|
||||
if( !*str ) return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
static inline int Q_strcmp( const char *s1, const char *s2 )
|
||||
{
|
||||
return unlikely(!s1) ?
|
||||
( !s2 ? 0 : -1 ) :
|
||||
( unlikely(!s2) ? 1 : strcmp( s1, s2 ));
|
||||
if( likely( s1 && s2 ))
|
||||
return strcmp( s1, s2 );
|
||||
return ( s1 ? 1 : 0 ) - ( s2 ? 1 : 0 );
|
||||
}
|
||||
|
||||
static inline int Q_strncmp( const char *s1, const char *s2, size_t n )
|
||||
{
|
||||
return unlikely(!s1) ?
|
||||
( !s2 ? 0 : -1 ) :
|
||||
( unlikely(!s2) ? 1 : strncmp( s1, s2, n ));
|
||||
if( likely( s1 && s2 ))
|
||||
return strncmp( s1, s2, n );
|
||||
return ( s1 ? 1 : 0 ) - ( s2 ? 1 : 0 );
|
||||
}
|
||||
|
||||
static inline char *Q_strstr( const char *s1, const char *s2 )
|
||||
{
|
||||
return unlikely( !s1 || !s2 ) ? NULL : (char*)strstr( s1, s2 );
|
||||
if( likely( s1 && s2 ))
|
||||
return (char *)strstr( s1, s2 );
|
||||
return NULL;
|
||||
}
|
||||
|
||||
// Q_strncpy is the same as strlcpy
|
||||
static inline size_t Q_strncpy( char *dst, const char *src, size_t siz )
|
||||
// libc extensions, be careful what to enable or what not
|
||||
static inline size_t Q_strncpy( char *dst, const char *src, size_t size )
|
||||
{
|
||||
#if HAVE_STRLCPY
|
||||
if( unlikely( !dst || !src || !size ))
|
||||
return 0;
|
||||
return strlcpy( dst, src, size );
|
||||
#else
|
||||
size_t len;
|
||||
|
||||
if( unlikely( !dst || !src || !siz ))
|
||||
if( unlikely( !dst || !src || !size ))
|
||||
return 0;
|
||||
|
||||
len = strlen( src );
|
||||
if( len + 1 > siz ) // check if truncate
|
||||
if( len + 1 > size ) // check if truncate
|
||||
{
|
||||
memcpy( dst, src, siz - 1 );
|
||||
dst[siz - 1] = 0;
|
||||
memcpy( dst, src, size - 1 );
|
||||
dst[size - 1] = 0;
|
||||
}
|
||||
else memcpy( dst, src, len + 1 );
|
||||
|
||||
return len; // count does not include NULL
|
||||
#endif
|
||||
}
|
||||
|
||||
// libc extensions, be careful
|
||||
static inline size_t Q_strncat( char *dst, const char *src, size_t size )
|
||||
{
|
||||
#if HAVE_STRLCAT
|
||||
if( unlikely( !dst || !src || !size ))
|
||||
return 0;
|
||||
return strlcat( dst, src, size );
|
||||
#else
|
||||
char *d = dst;
|
||||
const char *s = src;
|
||||
size_t n = size;
|
||||
size_t dlen;
|
||||
|
||||
#if XASH_WIN32
|
||||
#define strcasecmp stricmp
|
||||
#define strncasecmp strnicmp
|
||||
#endif // XASH_WIN32
|
||||
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 )
|
||||
{
|
||||
*d++ = *s;
|
||||
n--;
|
||||
}
|
||||
s++;
|
||||
}
|
||||
|
||||
*d = '\0';
|
||||
return( dlen + ( s - src )); // count does not include NULL
|
||||
#endif
|
||||
}
|
||||
|
||||
#if HAVE_STRICMP || HAVE_STRCASECMP
|
||||
static inline int Q_stricmp( const char *s1, const char *s2 )
|
||||
{
|
||||
return unlikely(!s1) ?
|
||||
( !s2 ? 0 : -1 ) :
|
||||
( unlikely(!s2) ? 1 : strcasecmp( s1, s2 ));
|
||||
if( likely( s1 && s2 ))
|
||||
{
|
||||
#if HAVE_STRICMP
|
||||
return stricmp( s1, s2 );
|
||||
#elif HAVE_STRCASECMP
|
||||
return strcasecmp( s1, s2 );
|
||||
#endif
|
||||
}
|
||||
return ( s1 ? 1 : 0 ) - ( s2 ? 1 : 0 );
|
||||
}
|
||||
#else
|
||||
int Q_stricmp( const char *s1, const char *s2 );
|
||||
#endif
|
||||
|
||||
#if HAVE_STRICMP || HAVE_STRCASECMP
|
||||
static inline int Q_strnicmp( const char *s1, const char *s2, size_t n )
|
||||
{
|
||||
return unlikely(!s1) ?
|
||||
( !s2 ? 0 : -1 ) :
|
||||
( unlikely(!s2) ? 1 : strncasecmp( s1, s2, n ));
|
||||
}
|
||||
|
||||
#if defined( HAVE_STRCASESTR )
|
||||
#if XASH_WIN32
|
||||
#define strcasestr stristr
|
||||
if( likely( s1 && s2 ))
|
||||
{
|
||||
#if HAVE_STRICMP
|
||||
return strnicmp( s1, s2, n );
|
||||
#elif HAVE_STRCASECMP
|
||||
return strncasecmp( s1, s2, n );
|
||||
#endif
|
||||
}
|
||||
return ( s1 ? 1 : 0 ) - ( s2 ? 1 : 0 );
|
||||
}
|
||||
#else
|
||||
int Q_strnicmp( const char *s1, const char *s2, size_t n );
|
||||
#endif
|
||||
|
||||
|
||||
#if HAVE_STRCASESTR
|
||||
static inline char *Q_stristr( const char *s1, const char *s2 )
|
||||
{
|
||||
return unlikely( !s1 || !s2 ) ? NULL : (char *)strcasestr( s1, s2 );
|
||||
if( likely( s1 && s2 ))
|
||||
return (char *)strcasestr( s1, s2 );
|
||||
return NULL;
|
||||
}
|
||||
#else // defined( HAVE_STRCASESTR )
|
||||
#else // !HAVE_STRCASESTR
|
||||
char *Q_stristr( const char *s1, const char *s2 );
|
||||
#endif // defined( HAVE_STRCASESTR )
|
||||
#endif // !HAVE_STRCASESTR
|
||||
|
||||
#if defined( HAVE_STRCHRNUL )
|
||||
#if HAVE_STRCHRNUL
|
||||
#define Q_strchrnul strchrnul
|
||||
#else
|
||||
#else // !HAVE_STRCHRNUL
|
||||
static inline const char *Q_strchrnul( const char *s, int c )
|
||||
{
|
||||
const char *p = Q_strchr( s, c );
|
||||
|
||||
if( p )
|
||||
return p;
|
||||
|
||||
if( p ) return p;
|
||||
return s + Q_strlen( s );
|
||||
}
|
||||
#endif
|
||||
#endif // !HAVE_STRCHRNUL
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
|
||||
246
public/utflib.c
Normal file
246
public/utflib.c
Normal file
@@ -0,0 +1,246 @@
|
||||
/*
|
||||
utflib.c - small unicode conversion library
|
||||
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 "utflib.h"
|
||||
#include "xash3d_types.h"
|
||||
|
||||
uint32_t Q_DecodeUTF8( utfstate_t *s, uint32_t in )
|
||||
{
|
||||
// get character length
|
||||
if( s->len == 0 )
|
||||
{
|
||||
// init state
|
||||
s->uc = 0;
|
||||
|
||||
// expect ASCII symbols by default
|
||||
if( likely( in <= 0x7fu ))
|
||||
return in;
|
||||
|
||||
// invalid sequence
|
||||
if( unlikely( in >= 0xf8u ))
|
||||
return 0;
|
||||
|
||||
s->k = 0;
|
||||
|
||||
if( in >= 0xf0u )
|
||||
{
|
||||
s->uc = in & 0x07u;
|
||||
s->len = 3;
|
||||
}
|
||||
else if( in >= 0xe0u )
|
||||
{
|
||||
s->uc = in & 0x0fu;
|
||||
s->len = 2;
|
||||
}
|
||||
else if( in >= 0xc0u )
|
||||
{
|
||||
s->uc = in & 0x1fu;
|
||||
s->len = 1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
// invalid sequence, reset
|
||||
if( unlikely( in > 0xbfu ))
|
||||
{
|
||||
s->len = 0;
|
||||
return 0;
|
||||
}
|
||||
|
||||
s->uc <<= 6;
|
||||
s->uc += in & 0x3fu;
|
||||
s->k++;
|
||||
|
||||
// sequence complete, reset and return code point
|
||||
if( likely( s->k == s->len ))
|
||||
{
|
||||
s->len = 0;
|
||||
return s->uc;
|
||||
}
|
||||
|
||||
// feed more characters
|
||||
return 0;
|
||||
}
|
||||
|
||||
uint32_t Q_DecodeUTF16( utfstate_t *s, uint32_t in )
|
||||
{
|
||||
// get character length
|
||||
if( s->len == 0 )
|
||||
{
|
||||
// init state
|
||||
s->uc = 0;
|
||||
|
||||
// expect simple case, after all decoding UTF-16 must be easy
|
||||
if( likely( in < 0xd800u || in > 0xdfffu ))
|
||||
return in;
|
||||
|
||||
s->uc = (( in - 0xd800u ) << 10 ) + 0x10000u;
|
||||
s->len = 1;
|
||||
s->k = 0;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
// invalid sequence, reset
|
||||
if( unlikely( in < 0xdc00u || in > 0xdfffu ))
|
||||
{
|
||||
s->len = 0;
|
||||
return 0;
|
||||
}
|
||||
|
||||
s->uc |= in - 0xdc00u;
|
||||
s->k++;
|
||||
|
||||
// sequence complete, reset and return code point
|
||||
if( likely( s->k == s->len ))
|
||||
{
|
||||
s->len = 0;
|
||||
return s->uc;
|
||||
}
|
||||
|
||||
// feed more characters (should never happen with UTF-16)
|
||||
return 0;
|
||||
}
|
||||
|
||||
size_t Q_EncodeUTF8( char dst[4], uint32_t ch )
|
||||
{
|
||||
if( ch <= 0x7fu )
|
||||
{
|
||||
dst[0] = ch;
|
||||
return 1;
|
||||
}
|
||||
else if( ch <= 0x7ffu )
|
||||
{
|
||||
dst[0] = 0xc0u | (( ch >> 6 ) & 0x1fu );
|
||||
dst[1] = 0x80u | (( ch ) & 0x3fu );
|
||||
return 2;
|
||||
}
|
||||
else if( ch <= 0xffffu )
|
||||
{
|
||||
dst[0] = 0xe0u | (( ch >> 12 ) & 0x0fu );
|
||||
dst[1] = 0x80u | (( ch >> 6 ) & 0x3fu );
|
||||
dst[2] = 0x80u | (( ch ) & 0x3fu );
|
||||
return 3;
|
||||
}
|
||||
|
||||
dst[0] = 0xf0u | (( ch >> 18 ) & 0x07u );
|
||||
dst[1] = 0x80u | (( ch >> 12 ) & 0x3fu );
|
||||
dst[2] = 0x80u | (( ch >> 6 ) & 0x3fu );
|
||||
dst[3] = 0x80u | (( ch ) & 0x3fu );
|
||||
return 4;
|
||||
}
|
||||
|
||||
size_t Q_UTF8Length( const char *s )
|
||||
{
|
||||
size_t len = 0;
|
||||
utfstate_t state = { 0 };
|
||||
|
||||
if( !s )
|
||||
return 0;
|
||||
|
||||
for( ; *s; s++ )
|
||||
{
|
||||
uint32_t ch = Q_DecodeUTF8( &state, (uint32_t)*s );
|
||||
|
||||
if( ch == 0 )
|
||||
continue;
|
||||
|
||||
len++;
|
||||
}
|
||||
|
||||
return len;
|
||||
}
|
||||
|
||||
static size_t Q_CodepointLength( uint32_t ch )
|
||||
{
|
||||
if( ch <= 0x7fu )
|
||||
return 1;
|
||||
else if( ch <= 0x7ffu )
|
||||
return 2;
|
||||
else if( ch <= 0xffffu )
|
||||
return 3;
|
||||
|
||||
return 4;
|
||||
}
|
||||
|
||||
size_t Q_UTF16ToUTF8( char *dst, size_t dstsize, const uint16_t *src, size_t srcsize )
|
||||
{
|
||||
utfstate_t state = { 0 };
|
||||
size_t dsti = 0, srci;
|
||||
|
||||
if( !dst || !src || !dstsize || !srcsize )
|
||||
return 0;
|
||||
|
||||
for( srci = 0; srci < srcsize && src[srci]; srci++ )
|
||||
{
|
||||
uint32_t ch;
|
||||
size_t len;
|
||||
|
||||
ch = Q_DecodeUTF16( &state, src[srci] );
|
||||
|
||||
if( ch == 0 )
|
||||
continue;
|
||||
|
||||
len = Q_CodepointLength( ch );
|
||||
|
||||
if( dsti + len + 1 > dstsize )
|
||||
break;
|
||||
|
||||
dsti += Q_EncodeUTF8( &dst[dsti], ch );
|
||||
}
|
||||
|
||||
dst[dsti] = 0;
|
||||
|
||||
return dsti;
|
||||
}
|
||||
|
||||
static uint16_t table_cp1251[64] = {
|
||||
0x0402, 0x0403, 0x201A, 0x0453, 0x201E, 0x2026, 0x2020, 0x2021,
|
||||
0x20AC, 0x2030, 0x0409, 0x2039, 0x040A, 0x040C, 0x040B, 0x040F,
|
||||
0x0452, 0x2018, 0x2019, 0x201C, 0x201D, 0x2022, 0x2013, 0x2014,
|
||||
0x007F, 0x2122, 0x0459, 0x203A, 0x045A, 0x045C, 0x045B, 0x045F,
|
||||
0x00A0, 0x040E, 0x045E, 0x0408, 0x00A4, 0x0490, 0x00A6, 0x00A7,
|
||||
0x0401, 0x00A9, 0x0404, 0x00AB, 0x00AC, 0x00AD, 0x00AE, 0x0407,
|
||||
0x00B0, 0x00B1, 0x0406, 0x0456, 0x0491, 0x00B5, 0x00B6, 0x00B7,
|
||||
0x0451, 0x2116, 0x0454, 0x00BB, 0x0458, 0x0405, 0x0455, 0x0457
|
||||
};
|
||||
|
||||
uint32_t Q_UnicodeToCP1251( uint32_t uc )
|
||||
{
|
||||
size_t i;
|
||||
|
||||
if( uc < 0x80 )
|
||||
return uc;
|
||||
|
||||
if( uc >= 0x0410 && uc <= 0x042F )
|
||||
return uc - 0x410 + 0xC0;
|
||||
|
||||
if( uc >= 0x0430 && uc <= 0x044F )
|
||||
return uc - 0x430 + 0xE0;
|
||||
|
||||
for( i = 0; i < sizeof( table_cp1251 ) / sizeof( table_cp1251[0] ); i++ )
|
||||
{
|
||||
if( uc == (uint32_t)table_cp1251[i] )
|
||||
return i + 0x80;
|
||||
}
|
||||
|
||||
return '?';
|
||||
}
|
||||
|
||||
uint32_t Q_UnicodeToCP1252( uint32_t uc )
|
||||
{
|
||||
// this is NOT valid way to convert Unicode codepoint back to CP1252!!!
|
||||
return uc < 0xFF ? uc : '?';
|
||||
}
|
||||
44
public/utflib.h
Normal file
44
public/utflib.h
Normal file
@@ -0,0 +1,44 @@
|
||||
/*
|
||||
utflib.h - small unicode conversion library
|
||||
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.
|
||||
*/
|
||||
#ifndef UTFLIB_H
|
||||
#define UTFLIB_H
|
||||
|
||||
#include STDINT_H
|
||||
#include <stddef.h>
|
||||
|
||||
typedef struct utfstate_s
|
||||
{
|
||||
uint32_t uc;
|
||||
uint8_t len;
|
||||
uint8_t k;
|
||||
} utfstate_t;
|
||||
|
||||
// feed utf8 characters one by one
|
||||
// if it returns 0, feed more
|
||||
// utfstate_t must be zero initialized
|
||||
uint32_t Q_DecodeUTF8( utfstate_t *s, uint32_t ch );
|
||||
uint32_t Q_DecodeUTF16( utfstate_t *s, uint32_t ch );
|
||||
size_t Q_EncodeUTF8( char dst[4], uint32_t ch );
|
||||
|
||||
size_t Q_UTF8Length( const char *s );
|
||||
|
||||
// srcsize in byte pairs
|
||||
size_t Q_UTF16ToUTF8( char *dst, size_t dstsize, const uint16_t *src, size_t srcsize );
|
||||
|
||||
// function to convert Unicode codepoints into CP1251 or CP1252
|
||||
uint32_t Q_UnicodeToCP1251( uint32_t uc );
|
||||
uint32_t Q_UnicodeToCP1252( uint32_t uc );
|
||||
|
||||
#endif // UTFLIB_H
|
||||
@@ -2,20 +2,110 @@
|
||||
# encoding: utf-8
|
||||
# mittorn, 2018
|
||||
|
||||
from waflib import Logs
|
||||
from waflib import Logs, Configure
|
||||
import os
|
||||
|
||||
top = '.'
|
||||
|
||||
TGMATH_H_TEST = '''#include <tgmath.h>
|
||||
const float val = 2, val2 = 3;
|
||||
int main(void){ return (int)(-asin(val) + cos(val2)); }'''
|
||||
|
||||
STRNCASECMP_TEST = '''#include <string.h>
|
||||
int main(int argc, char **argv) { return strncasecmp(argv[1], argv[2], 10); }'''
|
||||
|
||||
STRCASECMP_TEST = '''#include <string.h>
|
||||
int main(int argc, char **argv) { return strcasecmp(argv[1], argv[2]); }'''
|
||||
|
||||
STRCASESTR_TEST = '''#include <string.h>
|
||||
int main(int argc, char **argv) { argv[0] = strcasestr(argv[1], argv[2]); return 0; }'''
|
||||
|
||||
STRCHRNUL_TEST = '''#include <string.h>
|
||||
int main(int argc, char **argv) { argv[2] = strchrnul(argv[1], 'x'); return 0; }'''
|
||||
|
||||
STRLCPY_TEST = '''#include <string.h>
|
||||
int main(int argc, char **argv) { return strlcpy(argv[1], argv[2], 10); }'''
|
||||
|
||||
STRLCAT_TEST = '''#include <string.h>
|
||||
int main(int argc, char **argv) { return strlcat(argv[1], argv[2], 10); }'''
|
||||
|
||||
ALLOCA_TEST = '''#include <%s>
|
||||
int main(void) { alloca(1); return 0; }'''
|
||||
|
||||
HEADER_TEST = '''#include <%s>
|
||||
int main(void) { return 0; }
|
||||
'''
|
||||
|
||||
def options(opt):
|
||||
# stub
|
||||
return
|
||||
|
||||
def header_frag(header_name):
|
||||
return '#include <%s>' % header_name + EMPTY_PROGRAM
|
||||
|
||||
@Configure.conf
|
||||
def export_define(conf, define, value=1):
|
||||
if not value:
|
||||
return
|
||||
if value is True:
|
||||
value = 1 # so python won't define it as string True
|
||||
|
||||
conf.env.EXPORT_DEFINES_LIST += ['%s=%s' % (define, value)]
|
||||
|
||||
@Configure.conf
|
||||
def simple_check(conf, fragment, msg, mandatory=False, **kw):
|
||||
return conf.check_cc(fragment=fragment, msg='Checking for %s' % msg, mandatory=mandatory, **kw)
|
||||
|
||||
def configure(conf):
|
||||
conf.define('XASH_BUILD_COMMIT', conf.env.GIT_VERSION if conf.env.GIT_VERSION else 'notset')
|
||||
# private to libpublic
|
||||
conf.load('gitversion')
|
||||
conf.define('XASH_BUILD_COMMIT', conf.env.GIT_VERSION if conf.env.GIT_VERSION else 'unknown-commit')
|
||||
conf.define('XASH_BUILD_BRANCH', conf.env.GIT_BRANCH if conf.env.GIT_BRANCH else 'unknown-branch')
|
||||
|
||||
# need to expose it for everyone using libpublic headers
|
||||
conf.env.EXPORT_DEFINES_LIST = []
|
||||
conf.export_define('_CRT_SILENCE_NONCONFORMING_TGMATH_H', conf.env.COMPILER_CC == 'msvc')
|
||||
conf.export_define('_GNU_SOURCE', conf.env.DEST_OS != 'win32')
|
||||
|
||||
# create temporary uselib that just enables extensions
|
||||
conf.env.DEFINES_export = list(conf.env.EXPORT_DEFINES_LIST)
|
||||
|
||||
# check platform-specific header name for alloca(3)
|
||||
if conf.simple_check(ALLOCA_TEST % 'alloca.h', msg='alloca in alloca.h'):
|
||||
conf.export_define('ALLOCA_H', '<alloca.h>')
|
||||
elif conf.simple_check(ALLOCA_TEST % 'malloc.h', msg='alloca in malloc.h'):
|
||||
conf.export_define('ALLOCA_H', '<malloc.h>')
|
||||
elif conf.simple_check(ALLOCA_TEST % 'stdlib.h', msg='alloca in stdlib.h'):
|
||||
conf.export_define('ALLOCA_H', '<stdlib.h>')
|
||||
|
||||
conf.export_define('STDINT_H', '<stdint.h>' if conf.simple_check(HEADER_TEST % 'stdint.h', 'stdint.h') else '<pstdint.h>')
|
||||
conf.export_define('HAVE_TGMATH_H', conf.simple_check(TGMATH_H_TEST, 'tgmath.h', use='M werror export'))
|
||||
|
||||
# save some time on Windows, msvc is too slow
|
||||
# these calls must be available with both msvc and mingw
|
||||
if conf.env.DEST_OS == 'win32':
|
||||
conf.export_define('HAVE_STRNICMP')
|
||||
conf.export_define('HAVE_STRICMP')
|
||||
else:
|
||||
# TODO: multicheck for speed
|
||||
def check_libc_extension(frag, msg, define):
|
||||
conf.export_define(define, conf.simple_check(frag, msg, use='werror export'))
|
||||
|
||||
check_libc_extension(STRNCASECMP_TEST, 'strncasecmp', 'HAVE_STRNCASECMP')
|
||||
check_libc_extension(STRCASECMP_TEST, 'strcasecmp', 'HAVE_STRCASECMP')
|
||||
check_libc_extension(STRCASESTR_TEST, 'strcasestr', 'HAVE_STRCASESTR')
|
||||
check_libc_extension(STRCHRNUL_TEST, 'strchrnul', 'HAVE_STRCHRNUL')
|
||||
check_libc_extension(STRLCPY_TEST, 'strlcpy', 'HAVE_STRLCPY')
|
||||
check_libc_extension(STRLCAT_TEST, 'strlcat', 'HAVE_STRLCAT')
|
||||
|
||||
# kill temporary uselib
|
||||
del conf.env.DEFINES_export
|
||||
|
||||
def build(bld):
|
||||
bld(name = 'sdk_includes', export_includes = '. ../common ../pm_shared ../engine')
|
||||
bld(name = 'sdk_includes',
|
||||
export_includes = '. ../common ../pm_shared ../engine',
|
||||
export_defines = bld.env.EXPORT_DEFINES_LIST)
|
||||
|
||||
bld.stlib(source = bld.path.ant_glob('*.c'),
|
||||
target = 'public',
|
||||
features = 'c',
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user