From 9ae72dbc4729af8c2e5ada537f946381c10fa057 Mon Sep 17 00:00:00 2001 From: Alibek Omarov Date: Thu, 10 Mar 2022 04:43:26 +0300 Subject: [PATCH 001/490] common: update netadr_t structure to include IPv6 addresses --- common/netadr.h | 54 ++++++++++++++++++++++++++++++++++++++++++++----- 1 file changed, 49 insertions(+), 5 deletions(-) diff --git a/common/netadr.h b/common/netadr.h index 4dc00011..a9b4655c 100644 --- a/common/netadr.h +++ b/common/netadr.h @@ -16,6 +16,9 @@ #ifndef NETADR_H #define NETADR_H +#include "build.h" +#include STDINT_H + typedef enum { NA_UNUSED, @@ -23,15 +26,56 @@ typedef enum NA_BROADCAST, NA_IP, NA_IPX, - NA_BROADCAST_IPX + NA_BROADCAST_IPX, + NA_IP6, + NA_MULTICAST_IP6, // all nodes multicast } netadrtype_t; +#define NETADR_T_SIZE 20 + +// Original structure: +// typedef struct netadr_s +// { +// netadrtype_t type; +// unsigned char ip[4]; +// unsigned char ipx[10]; +// unsigned short port; +// } netadr_t; + typedef struct netadr_s { - netadrtype_t type; - unsigned char ip[4]; - unsigned char ipx[10]; - unsigned short port; + union + { + struct + { +#if XASH_LITTLE_ENDIAN + uint16_t type6; + uint16_t port6; +#elif XASH_BIG_ENDIAN + uint16_t port6; + uint16_t type6; +#else +#error +#endif + }; + uint32_t type; + }; + union + { + struct + { + uint16_t ip6[8]; + }; + + struct + { + uint8_t ip[4]; + uint8_t ipx[10]; + uint16_t port; + }; + }; } netadr_t; +extern int _check_netadr_t_size[sizeof( netadr_t ) == NETADR_T_SIZE ? 1 : -1]; + #endif//NETADR_H From 12bfb8f795bb8fcc3bb89c51d7995198305a11d5 Mon Sep 17 00:00:00 2001 From: Alibek Omarov Date: Fri, 11 Mar 2022 12:10:00 +0300 Subject: [PATCH 002/490] common: another approach on netadr_t with better compatibility --- common/netadr.h | 29 ++++++++++++----------------- 1 file changed, 12 insertions(+), 17 deletions(-) diff --git a/common/netadr.h b/common/netadr.h index a9b4655c..bd8c7879 100644 --- a/common/netadr.h +++ b/common/netadr.h @@ -42,39 +42,34 @@ typedef enum // unsigned short port; // } netadr_t; +#pragma pack( push, 1 ) typedef struct netadr_s { union { struct { + uint32_t type; + uint8_t ip[4]; + uint8_t ipx[10]; + }; + struct + { #if XASH_LITTLE_ENDIAN uint16_t type6; - uint16_t port6; + uint8_t ip6_0[2]; #elif XASH_BIG_ENDIAN - uint16_t port6; + uint8_t ip6_0[2]; uint16_t type6; #else #error #endif - }; - uint32_t type; - }; - union - { - struct - { - uint16_t ip6[8]; - }; - - struct - { - uint8_t ip[4]; - uint8_t ipx[10]; - uint16_t port; + uint8_t ip6_1[14]; }; }; + uint16_t port; } netadr_t; +#pragma pack( pop ) extern int _check_netadr_t_size[sizeof( netadr_t ) == NETADR_T_SIZE ? 1 : -1]; From 193cde83b65747f5be5fc2464f9f55bfe0440711 Mon Sep 17 00:00:00 2001 From: Alibek Omarov Date: Fri, 11 Mar 2022 12:11:25 +0300 Subject: [PATCH 003/490] engine: add IPv6 address parsing/printing library from GameNetworkingSockets --- engine/common/ipv6text.c | 377 +++++++++++++++++++++++++++++++++++++++ engine/common/ipv6text.h | 54 ++++++ 2 files changed, 431 insertions(+) create mode 100644 engine/common/ipv6text.c create mode 100644 engine/common/ipv6text.h diff --git a/engine/common/ipv6text.c b/engine/common/ipv6text.c new file mode 100644 index 00000000..0a190e55 --- /dev/null +++ b/engine/common/ipv6text.c @@ -0,0 +1,377 @@ +#include +#include +#include "ipv6text.h" + +#ifdef _WIN32 +#ifndef snprintf +#define snprintf _snprintf +#endif +#endif + +void IPv6IPToString( char *pszOutText, const unsigned char *ip ) +{ + + // Find the longest run of consecutive zero quads. + // If there's a tie, we want the leftmost one. + int idxLongestRunStart = -1; + int nLongestRun = 1; // It must be at least 2 quads in a row, a single 0 must not be compressed + int nCurrentRun = 0; + int idxQuad; + for ( idxQuad = 0 ; idxQuad < 8 ; ++idxQuad ) + { + // Zero + if ( ip[idxQuad*2] || ip[idxQuad*2 + 1] ) + { + // Terminate run + nCurrentRun = 0; + } + else + { + // Extend (or begin) run + ++nCurrentRun; + + // Longer than previously found run? + if ( nCurrentRun > nLongestRun ) + { + nLongestRun = nCurrentRun; + idxLongestRunStart = idxQuad - nCurrentRun + 1; + } + } + } + + // Print the quads + char *p = pszOutText; + idxQuad = 0; + bool bNeedColon = false; + while ( idxQuad < 8 ) + { + // Run of compressed zeros? + if ( idxQuad == idxLongestRunStart ) + { + *(p++) = ':'; + *(p++) = ':'; + bNeedColon = false; + idxQuad += nLongestRun; + } + else + { + // Colon to separate from previous, unless + // we are first or immediately follow compressed zero "::" + if ( bNeedColon ) + *(p++) = ':'; + + // Next quad should should print a separator + bNeedColon = true; + + // Assemble 16-bit quad value from the two bytes + unsigned quad = ( (unsigned)ip[idxQuad*2] << 8U ) | ip[idxQuad*2 + 1]; + + // Manually do the hex number formatting. + // Lowercase hex digits, with leading zeros omitted + static const char hexdigits[] = "0123456789abcdef"; + if ( quad >= 0x0010 ) + { + if ( quad >= 0x0100 ) + { + if ( quad >= 0x1000 ) + *(p++) = hexdigits[ quad >> 12U ]; + *(p++) = hexdigits[ ( quad >> 8U ) & 0xf ]; + } + *(p++) = hexdigits[ ( quad >> 4U ) & 0xf ]; + } + + // Least significant digit, which is always printed + *(p++) = hexdigits[ quad & 0xf ]; + + // On to the next one + ++idxQuad; + } + } + + // String terminator + *p = '\0'; +} + +void IPv6AddrToString( char *pszOutText, const unsigned char *ip, uint16_t port, uint32_t scope ) +{ + char *p = pszOutText; + + // Open bracket + *(p++) = '['; + + // Print in the IP + IPv6IPToString( p, ip ); + + // Find the end of the string + while (*p) + ++p; + + if ( scope ) + { + // And now the scope. Max 32-digit scope number is 10 digits + snprintf( p, 12, "%%%d", scope ); + + // Find the end of the string + while (*p) + ++p; + } + + // And now the rest. Max 16-digit port number is 6 digits + snprintf( p, 8, "]:%u", (unsigned int)port ); +} + +static inline int ParseIPv6Addr_HexDigitVal( char c ) +{ + if ( c >= '0' && c <= '9' ) return c - '0'; + if ( c >= 'a' && c <= 'f' ) return c - ('a' - 0xa); + if ( c >= 'A' && c <= 'F' ) return c - ('A' - 0xa); + return -1; +} +static inline int ParseIPv6Addr_DecimalDigitVal( char c ) +{ + if ( c >= '0' && c <= '9' ) return c - '0'; + return -1; +} +bool ParseIPv6Addr_IsSpace( char c ) +{ + // Newlines don't count, intentionally + return c == ' ' || c == '\t'; +} +bool ParseIPv6Addr( const char *pszText, unsigned char *pOutIP, int *pOutPort, uint32_t *pOutScope ) +{ + while ( ParseIPv6Addr_IsSpace( *pszText ) ) + ++pszText; + const char *s = pszText; + + // Skip opening bracket, if present + if ( *s == '[' ) + { + ++s; + while ( ParseIPv6Addr_IsSpace( *s ) ) + ++s; + } + + // Special case for leading "::" + bool bQuadMustFollow = true; + unsigned char *d = pOutIP; + unsigned char *pZeroFill = NULL; + unsigned char *pEndIP = pOutIP + 16; + if ( s[0] == ':' && s[1] == ':' ) + { + pZeroFill = d; + s += 2; + bQuadMustFollow = false; + } + + // Parse quads until we get to the end + for (;;) + { + // Next thing must be a quad, or end of input. Is it a quad? + int quadDigit = ParseIPv6Addr_HexDigitVal( *s ); + if ( quadDigit < 0 ) + { + if ( bQuadMustFollow ) + return false; + break; + } + + // No room for more quads? + if ( d >= pEndIP ) + return false; + + ++s; + int quad = quadDigit; + + // Now parse up to three additional characters + quadDigit = ParseIPv6Addr_HexDigitVal( *s ); + if ( quadDigit >= 0 ) + { + quad = ( quad << 4 ) | quadDigit; + ++s; + + quadDigit = ParseIPv6Addr_HexDigitVal( *s ); + if ( quadDigit >= 0 ) + { + quad = ( quad << 4 ) | quadDigit; + ++s; + + quadDigit = ParseIPv6Addr_HexDigitVal( *s ); + if ( quadDigit >= 0 ) + { + quad = ( quad << 4 ) | quadDigit; + ++s; + } + } + } + + // Stash it in the next slot, ignoring for now the issue + // of compressed zeros + *(d++) = (unsigned char)( quad >> 8 ); + *(d++) = (unsigned char)quad; + + // Only valid character for the IP portion is a colon. + // Anything else ends the IP portion + if ( *s != ':' ) + break; + + // Compressed zeros? + if ( s[1] == ':' ) + { + + // Eat '::' + s += 2; + + // Can only have one range of compressed zeros + if ( pZeroFill ) + return false; + + // Remember where to insert the compressed zeros + pZeroFill = d; + + // An IP can end with '::' + bQuadMustFollow = false; + } + else + { + // If they have filed the entire IP with no compressed zeros, + // then this is unambiguously a port number. That's not + // necessarily the best style, but it *is* unambiguous + // what it should mean, so let's allow it. If there + // are compressed zeros, then this is ambiguous, and we will + // always interpret it as a quad. + if ( !pZeroFill && d >= pEndIP ) + break; // leave ':' as next character, for below + + // Eat ':' + ++s; + + // A single colon must be followed by another quad + bQuadMustFollow = true; + } + } + + // End of the IP. Do we have compressed zeros? + if ( pZeroFill ) + { + // How many zeros do we need to fill? + intptr_t nZeros = pEndIP - d; + if ( nZeros <= 0 ) + return false; + + // Shift the quads after the bytes to the end + memmove( pZeroFill+nZeros, pZeroFill, d-pZeroFill ); + + // And now fill the zeros + memset( pZeroFill, 0, nZeros ); + } + else + { + // No compressed zeros. Just make sure we filled the IP exactly + if ( d != pEndIP ) + return false; + } + + if ( *s == '%' ) + { + ++s; + + // Parse scope number + uint32_t unScope = 0; + int nScopeDigit = ParseIPv6Addr_DecimalDigitVal( *s ); + if ( nScopeDigit < 0 ) + return false; + unScope = (uint32_t)nScopeDigit; + for (;;) + { + ++s; + if ( *s == '\0' || *s == ']' || ParseIPv6Addr_IsSpace( *s ) ) + break; + nScopeDigit = ParseIPv6Addr_DecimalDigitVal( *s ); + if ( nScopeDigit < 0 ) + return false; + unScope = unScope * 10 + nScopeDigit; + } + + if ( pOutScope ) + *pOutScope = unScope; + } + else + { + if ( pOutScope ) + *pOutScope = 0; + } + + // If we started with a bracket, then the next character MUST be a bracket. + // (And this is the only circumstance in which a closing bracket would be legal) + if ( *pszText == '[' ) + { + while ( ParseIPv6Addr_IsSpace( *s ) ) + ++s; + if ( *s != ']' ) + return false; + ++s; + } + + // Now we are definitely at the end of the IP. Do we have a port? + // We support all of the syntaxes mentioned in RFC5952 section 6 other + // than the ambiguous case + if ( *s == ':' || *s == '#' || *s == '.' || *s == 'p' || *s == 'P' ) + { + ++s; + } + else + { + while ( ParseIPv6Addr_IsSpace( *s ) ) + ++s; + if ( *s == '\0' ) + { + // Parsed IP without port OK + if ( pOutPort ) + *pOutPort = -1; + return true; + } + + if ( strncmp( s, "port", 4 ) == 0 ) + { + s += 4; + while ( ParseIPv6Addr_IsSpace( *s ) ) + ++s; + } + else + { + // Extra stuff after the IP which isn't whitespace or a port + return false; + } + } + + // We have a port. If they didn't ask for it, that's considered a parse failure. + if ( !pOutPort ) + return false; + + // Parse port number + int nPort = ParseIPv6Addr_DecimalDigitVal( *s ); + if ( nPort < 0 ) + return false; + for (;;) + { + ++s; + if ( *s == '\0' || ParseIPv6Addr_IsSpace( *s ) ) + break; + int portDigit = ParseIPv6Addr_DecimalDigitVal( *s ); + if ( portDigit < 0 ) + return false; + nPort = nPort * 10 + portDigit; + if ( nPort > 0xffff ) + return false; + } + + // Consume trailing whitespace; confirm nothing else in the input + while ( ParseIPv6Addr_IsSpace( *s ) ) + ++s; + if ( *s != '\0' ) + return false; + + *pOutPort = nPort; + return true; +} + diff --git a/engine/common/ipv6text.h b/engine/common/ipv6text.h new file mode 100644 index 00000000..a6f73c65 --- /dev/null +++ b/engine/common/ipv6text.h @@ -0,0 +1,54 @@ +/// Standalone plain C utilities for parsing and printing IPv6 addresses +#pragma once + +#include +#include + +/// Max length of an IPv6 string, with scope, WITHOUT port number, including \0': +/// 0123:4567:89ab:cdef:0123:4567:89ab:cdef%4294967295 +#define k_ncchMaxIPV6AddrStringWithoutPort 51; + +/// Max number of bytes output by IPv6AddrToString, including '\0': +/// [0123:4567:89ab:cdef:0123:4567:89ab:cdef%4294967295]:12345 +/// There are other strings that are acceptable to ParseIPv6Addr +/// that are longer than this, but this is the longest canonical +/// string. +#define k_ncchMaxIPV6AddrStringWithPort 59; + +#ifdef __cplusplus +extern "C" { +#endif + +/// Format an IPv6 address to the canonical form according to RFC5952. +/// The address should be 16 bytes (e.g. same as in6_addr::s6_addr). +/// Your buffer MUST be at least k_ncchMaxIPV6AddrStringWithoutPort bytes. +extern void IPv6IPToString( char *pszOutText, const unsigned char *ip ); + +/// Format IPv6 IP and port to string. This uses the recommended +/// bracket notation, eg [1234::1]:12345. Your buffer must be +/// at least k_ncchMaxIPV6AddrStringWithPort bytes. +extern void IPv6AddrToString( char *pszOutText, const unsigned char *ip, uint16_t port, uint32_t scope ); + +/// Parse IPv6 address string. Returns true if parsed OK. Returns false +/// if input cannot be parsed, or if input specifies a port but pOutPort is NULL. +/// If input does not specify a port, and pOutPort is non-NULL, then *pOutPort is +/// set to -1. +/// +/// Parsing is tolerant of any unambiguous IPv6 representation, the input +/// need not be the canonical RFC5952 representation. +/// +/// IPv6 zones are not supported. +/// +/// Leading and trailing whitespace is OK around the entire string, +/// but not internal whitespace. The different methods for separating the +/// port in RFC5952 are supported section 6, except the ambiguous case +/// of a colon to separate the port, when the IP contains a double-colon. +/// Brackets around an IP are OK, even if there is no port. +/// +/// Address must point to a 16-byte buffer (e.g. same as in6_addr::s6_addr) +/// Port is returned in host byte order. +extern bool ParseIPv6Addr( const char *pszText, unsigned char *pOutIP, int *pOutPort, uint32_t *pOutScope ); + +#ifdef __cplusplus +} +#endif From 59fba30a5267068097610ada05779850407eae83 Mon Sep 17 00:00:00 2001 From: Alibek Omarov Date: Fri, 11 Mar 2022 12:11:49 +0300 Subject: [PATCH 004/490] engine: IPv6 support * v6 equivalent cvars * hostname resolving for v6 * fix for nonblocking hostname resolve (inverted check) * enabled by default, probably should be disabled for dedicated servers --- engine/client/cl_main.c | 2 + engine/common/net_ws.c | 527 ++++++++++++++++++++++++++++++---------- engine/common/netchan.h | 1 + 3 files changed, 399 insertions(+), 131 deletions(-) diff --git a/engine/client/cl_main.c b/engine/client/cl_main.c index c5807555..0aba692e 100644 --- a/engine/client/cl_main.c +++ b/engine/client/cl_main.c @@ -1567,7 +1567,9 @@ void CL_LocalServers_f( void ) // send a broadcast packet adr.type = NA_BROADCAST; adr.port = MSG_BigShort( PORT_SERVER ); + Netchan_OutOfBandPrint( NS_CLIENT, adr, "info %i", PROTOCOL_VERSION ); + adr.type = NA_MULTICAST_IP6; Netchan_OutOfBandPrint( NS_CLIENT, adr, "info %i", PROTOCOL_VERSION ); } diff --git a/engine/common/net_ws.c b/engine/common/net_ws.c index ef403b3c..9d2ad02f 100644 --- a/engine/common/net_ws.c +++ b/engine/common/net_ws.c @@ -17,6 +17,7 @@ GNU General Public License for more details. #include "client.h" // ConnectionProgress #include "netchan.h" #include "xash3d_mathlib.h" +#include "ipv6text.h" #if XASH_WIN32 // Winsock #include @@ -98,6 +99,8 @@ typedef int WSAsize_t; #define NET_USE_FRAGMENTS +static const uint8_t k_ipv6Bytes_LinkLocalAllNodes[16] = { 0xff, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01 }; // ff02:1 + #define PORT_ANY -1 #define MAX_LOOPBACK 4 #define MASK_LOOPBACK (MAX_LOOPBACK - 1) @@ -158,10 +161,12 @@ typedef struct int split_flags[NET_MAX_FRAGMENTS]; int sequence_number; int ip_sockets[NS_COUNT]; + int ip6_sockets[NS_COUNT]; qboolean initialized; qboolean threads_initialized; qboolean configured; qboolean allow_ip; + qboolean allow_ip6; #if XASH_WIN32 WSADATA winsockdata; #endif @@ -178,6 +183,13 @@ static convar_t *net_fakeloss; static convar_t *net_address; convar_t *net_clockwindow; netadr_t net_local; +netadr_t net6_local; + +// cvars equivalents for IPv6 +static convar_t *net_ip6name; +static convar_t *net_ip6hostport; +static convar_t *net_ip6clientport; +static convar_t *net6_address; /* ==================== @@ -263,12 +275,24 @@ _inline qboolean NET_IsSocketValid( int socket ) #endif } +_inline void NET_NetadrToIP6Bytes( uint8_t ip6[16], const netadr_t *adr ) +{ + memcpy( ip6, adr->ip6_0, 2 ); + memcpy( ip6 + 2, adr->ip6_1, 14 ); +} + +_inline void NET_IP6BytesToNetadr( netadr_t *adr, const uint8_t ip6[16] ) +{ + memcpy( adr->ip6_0, ip6, 2 ); + memcpy( adr->ip6_1, ip6 + 2, 14 ); +} + /* ==================== NET_NetadrToSockadr ==================== */ -static void NET_NetadrToSockadr( netadr_t *a, struct sockaddr *s ) +static void NET_NetadrToSockadr( netadr_t *a, struct sockaddr_storage *s ) { memset( s, 0, sizeof( *s )); @@ -284,6 +308,18 @@ static void NET_NetadrToSockadr( netadr_t *a, struct sockaddr *s ) ((struct sockaddr_in *)s)->sin_addr.s_addr = *(int *)&a->ip; ((struct sockaddr_in *)s)->sin_port = a->port; } + else if( a->type6 == NA_IP6 ) + { + ((struct sockaddr_in6 *)s)->sin6_family = AF_INET6; + NET_NetadrToIP6Bytes( ((struct sockaddr_in6 *)s)->sin6_addr.s6_addr, a ); + ((struct sockaddr_in6 *)s)->sin6_port = a->port; + } + else if( a->type6 == NA_MULTICAST_IP6 ) + { + ((struct sockaddr_in6 *)s)->sin6_family = AF_INET6; + memcpy(((struct sockaddr_in6 *)s)->sin6_addr.s6_addr, k_ipv6Bytes_LinkLocalAllNodes, sizeof( struct in6_addr )); + ((struct sockaddr_in6 *)s)->sin6_port = a->port; + } } /* @@ -291,14 +327,20 @@ static void NET_NetadrToSockadr( netadr_t *a, struct sockaddr *s ) NET_SockadrToNetAdr ==================== */ -static void NET_SockadrToNetadr( struct sockaddr *s, netadr_t *a ) +static void NET_SockadrToNetadr( const struct sockaddr_storage *s, netadr_t *a ) { - if( s->sa_family == AF_INET ) + if( s->ss_family == AF_INET ) { a->type = NA_IP; *(int *)&a->ip = ((struct sockaddr_in *)s)->sin_addr.s_addr; a->port = ((struct sockaddr_in *)s)->sin_port; } + else if( s->ss_family == AF_INET6 ) + { + a->type6 = NA_IP6; + NET_IP6BytesToNetadr( a, ((struct sockaddr_in6 *)s)->sin6_addr.s6_addr ); + a->port = ((struct sockaddr_in6 *)s)->sin6_port; + } } /* @@ -306,23 +348,24 @@ static void NET_SockadrToNetadr( struct sockaddr *s, netadr_t *a ) NET_GetHostByName ============ */ -int NET_GetHostByName( const char *hostname ) +qboolean NET_GetHostByName( const char *hostname, int family, struct sockaddr_storage *addr ) { -#ifdef HAVE_GETADDRINFO +#if defined HAVE_GETADDRINFO struct addrinfo *ai = NULL, *cur; struct addrinfo hints; - int ip = 0; + qboolean ret = false; memset( &hints, 0, sizeof( hints )); - hints.ai_family = AF_INET; + hints.ai_family = family; if( !getaddrinfo( hostname, NULL, &hints, &ai )) { for( cur = ai; cur; cur = cur->ai_next ) { - if( cur->ai_family == AF_INET ) + if( family == AF_UNSPEC || cur->ai_family == family ) { - ip = *((int*)&((struct sockaddr_in *)cur->ai_addr)->sin_addr); + memcpy( addr, cur->ai_addr, cur->ai_addrlen ); + ret = true; break; } } @@ -331,12 +374,16 @@ int NET_GetHostByName( const char *hostname ) freeaddrinfo( ai ); } - return ip; + return ret; #else struct hostent *h; if(!( h = gethostbyname( hostname ))) - return 0; - return *(int *)h->h_addr_list[0]; + return false; + + ((struct sockaddr_in *)addr)->sin_family = AF_INET; + ((struct sockaddr_in *)addr)->sin_addr = *(in_addr *)h->h_addr_list[0]; + + return true; #endif } @@ -389,6 +436,8 @@ static struct nsthread_s thread_t thread; int result; string hostname; + int family; + struct sockaddr_storage addr; qboolean busy; } nsthread #if !XASH_WIN32 @@ -407,7 +456,7 @@ static void NET_InitializeCriticalSections( void ) void NET_ResolveThread( void ) { - int sin_addr = 0; + struct sockaddr_storage addr; RESOLVE_DBG( "[resolve thread] starting resolve for " ); RESOLVE_DBG( nsthread.hostname ); @@ -417,14 +466,12 @@ void NET_ResolveThread( void ) RESOLVE_DBG( " with gethostbyname\n" ); #endif - sin_addr = NET_GetHostByName( nsthread.hostname ); - - if( sin_addr ) + if( NET_GetHostByName( nsthread.hostname, nsthread.family, &addr )) RESOLVE_DBG( "[resolve thread] success\n" ); else RESOLVE_DBG( "[resolve thread] failed\n" ); mutex_lock( &nsthread.mutexres ); - nsthread.result = sin_addr; + nsthread.addr = addr; nsthread.busy = false; RESOLVE_DBG( "[resolve thread] returning result\n" ); mutex_unlock( &nsthread.mutexres ); @@ -444,23 +491,33 @@ idnewt:28000 192.246.40.70:28000 ============= */ -static int NET_StringToSockaddr( const char *s, struct sockaddr *sadr, qboolean nonblocking ) +static int NET_StringToSockaddr( const char *s, struct sockaddr_storage *sadr, qboolean nonblocking, int family ) { - int ip = 0; + int ret = 0, port; char *colon; char copy[128]; + byte ip6[16]; + struct sockaddr_storage temp; if( !net.initialized ) return false; memset( sadr, 0, sizeof( *sadr )); - ((struct sockaddr_in *)sadr)->sin_family = AF_INET; - ((struct sockaddr_in *)sadr)->sin_port = 0; + // try to parse it as IPv6 first + if(( family == AF_UNSPEC || family == AF_INET6 ) && ParseIPv6Addr( s, ip6, &port, NULL )) + { + ((struct sockaddr_in6 *)sadr)->sin6_family = AF_INET6; + ((struct sockaddr_in6 *)sadr)->sin6_port = htons((short)port); + memcpy(((struct sockaddr_in6 *)sadr)->sin6_addr.s6_addr, ip6, sizeof( struct in6_addr )); + + return true; + } Q_strncpy( copy, s, sizeof( copy )); // strip off a trailing :port if present + ((struct sockaddr_in *)sadr)->sin_port = 0; for( colon = copy; *colon; colon++ ) { if( *colon == ':' ) @@ -472,14 +529,15 @@ static int NET_StringToSockaddr( const char *s, struct sockaddr *sadr, qboolean if( copy[0] >= '0' && copy[0] <= '9' ) { - *(int *)&((struct sockaddr_in *)sadr)->sin_addr = inet_addr( copy ); + ((struct sockaddr_in *)sadr)->sin_family = AF_INET; + ((struct sockaddr_in *)sadr)->sin_addr.s_addr = inet_addr( copy ); } else { qboolean asyncfailed = true; #ifdef CAN_ASYNC_NS_RESOLVE - if( net.threads_initialized && !nonblocking ) + if( net.threads_initialized && nonblocking ) { mutex_lock( &nsthread.mutexres ); @@ -491,13 +549,19 @@ static int NET_StringToSockaddr( const char *s, struct sockaddr *sadr, qboolean if( !Q_strcmp( copy, nsthread.hostname ) ) { - ip = nsthread.result; + ret = nsthread.result; + nsthread.hostname[0] = 0; + nsthread.family = AF_UNSPEC; + temp = nsthread.addr; + memset( &nsthread.addr, 0, sizeof( nsthread.addr )); + detach_thread( nsthread.thread ); } else { - Q_strncpy( nsthread.hostname, copy, MAX_STRING ); + Q_strncpy( nsthread.hostname, copy, sizeof( nsthread.hostname )); + nsthread.family = family; nsthread.busy = true; mutex_unlock( &nsthread.mutexres ); @@ -519,13 +583,30 @@ static int NET_StringToSockaddr( const char *s, struct sockaddr *sadr, qboolean if( asyncfailed ) { - ip = NET_GetHostByName( copy ); + ret = NET_GetHostByName( copy, family, &temp ); } - if( !ip ) + if( !ret ) + { + if( family == AF_INET6 ) + sadr->ss_family = AF_INET6; + else sadr->ss_family = AF_INET; return 0; + } - *(int *)&((struct sockaddr_in *)sadr)->sin_addr = ip; + sadr->ss_family = temp.ss_family; + + if( temp.ss_family == AF_INET ) + { + ((struct sockaddr_in *)sadr)->sin_addr = + ((struct sockaddr_in*)&temp)->sin_addr; + } + else if( temp.ss_family == AF_INET6 ) + { + memcpy(&((struct sockaddr_in6 *)sadr)->sin6_addr, + &((struct sockaddr_in6*)&temp)->sin6_addr, + sizeof( struct in6_addr )); + } } return 1; @@ -540,6 +621,18 @@ const char *NET_AdrToString( const netadr_t a ) { if( a.type == NA_LOOPBACK ) return "loopback"; + if( a.type6 == NA_IP6 ) + { + // TODO: remove that!!! + uint8_t ip6[16]; + char *s = va( "" ); + + NET_NetadrToIP6Bytes( ip6, &a ); + + IPv6AddrToString( s, ip6, ntohs( a.port ), 0 ); + return s; + } + return va( "%i.%i.%i.%i:%i", a.ip[0], a.ip[1], a.ip[2], a.ip[3], ntohs( a.port )); } @@ -552,6 +645,17 @@ const char *NET_BaseAdrToString( const netadr_t a ) { if( a.type == NA_LOOPBACK ) return "loopback"; + if( a.type6 == NA_IP6 ) + { + // TODO: remove that!!! + uint8_t ip6[16]; + char *s = va( "" ); + + NET_NetadrToIP6Bytes( ip6, &a ); + + IPv6IPToString( s, ip6 ); + return s; + } return va( "%i.%i.%i.%i", a.ip[0], a.ip[1], a.ip[2], a.ip[3] ); } @@ -576,6 +680,12 @@ qboolean NET_CompareBaseAdr( const netadr_t a, const netadr_t b ) return true; } + if( a.type6 == NA_IP6 ) + { + if( !memcmp( a.ip6_0, b.ip6_0, 2 ) && !memcmp( a.ip6_1, b.ip6_1, 14 )) + return true; + } + return false; } @@ -599,6 +709,9 @@ qboolean NET_CompareClassBAdr( netadr_t a, netadr_t b ) if( a.ip[0] == b.ip[0] && a.ip[1] == b.ip[1] ) return true; } + + // TODO: ipv6 + return false; } @@ -616,18 +729,32 @@ qboolean NET_IsReservedAdr( netadr_t a ) if( a.type == NA_IP ) { - if( a.ip[0] == 10 || a.ip[0] == 127 ) - return true; - - if( a.ip[0] == 172 && a.ip[1] >= 16 ) + if(( a.ip[0] == 10 ) || // 10.x.x.x is reserved + ( a.ip[0] == 127 ) || // 127.x.x.x + ( a.ip[0] == 169 && a.ip[1] == 254 ) || // 169.254.x.x is link-local ipv4 + ( a.ip[0] == 172 && a.ip[1] >= 16 && a.ip[1] <= 31 ) || // 172.16.x.x - 172.31.x.x + ( a.ip[0] == 192 && a.ip[1] >= 168 )) // 192.168.x.x + { + return true; + } + } + + if( a.type6 == NA_IP6 ) + { + // Private addresses, fc00::/7 + // Range is fc00:: to fdff:ffff:etc + if ( a.ip6_0[0] >= 0xFC && a.ip6_0[1] <= 0xFD ) { - if( a.ip[1] >= 32 ) - return false; return true; } - if( a.ip[0] == 192 && a.ip[1] >= 168 ) + // Link-local fe80::/10 + // Range is fe80:: to febf:: + if ( a.ip6_0[0] == 0xFE + && ( a.ip6_0[1] >= 0x80 && a.ip6_0[1] <= 0xBF ) ) + { return true; + } } return false; @@ -650,11 +777,18 @@ qboolean NET_CompareAdr( const netadr_t a, const netadr_t b ) if( a.type == NA_IP ) { - if(!memcmp( a.ip, b.ip, 4 ) && a.port == b.port ) + if( !memcmp( a.ip, b.ip, 4 ) && a.port == b.port ) return true; return false; } + if( a.type6 == NA_IP6 ) + { + if( !memcmp( a.ip6_0, b.ip6_0, 2 ) && !memcmp( a.ip6_1, b.ip6_1, 14 ) && a.port == b.port ) + return true; + } + + Con_DPrintf( S_ERROR "NET_CompareAdr: bad address type\n" ); return false; } @@ -677,9 +811,9 @@ idnewt 192.246.40.70 ============= */ -qboolean NET_StringToAdr( const char *string, netadr_t *adr ) +qboolean NET_StringToAdrEx( const char *string, netadr_t *adr, int family ) { - struct sockaddr s; + struct sockaddr_storage s; memset( adr, 0, sizeof( netadr_t )); @@ -689,16 +823,22 @@ qboolean NET_StringToAdr( const char *string, netadr_t *adr ) return true; } - if( !NET_StringToSockaddr( string, &s, false )) + if( !NET_StringToSockaddr( string, &s, false, family )) return false; NET_SockadrToNetadr( &s, adr ); return true; } + +qboolean NET_StringToAdr( const char *string, netadr_t *adr ) +{ + return NET_StringToAdrEx( string, adr, AF_UNSPEC ); +} + int NET_StringToAdrNB( const char *string, netadr_t *adr ) { - struct sockaddr s; + struct sockaddr_storage s; int res; memset( adr, 0, sizeof( netadr_t )); @@ -708,7 +848,7 @@ int NET_StringToAdrNB( const char *string, netadr_t *adr ) return true; } - res = NET_StringToSockaddr( string, &s, true ); + res = NET_StringToSockaddr( string, &s, true, AF_UNSPEC ); if( res == 0 || res == 2 ) return res; @@ -1086,65 +1226,73 @@ NET_QueuePacket queue normal and lagged packets ================== */ -qboolean NET_QueuePacket( netsrc_t sock, netadr_t *from, byte *data, size_t *length ) +static qboolean NET_QueuePacket( netsrc_t sock, netadr_t *from, byte *data, size_t *length ) { byte buf[NET_MAX_FRAGMENT]; int ret; int net_socket; WSAsize_t addr_len; - struct sockaddr addr; + struct sockaddr_storage addr; + int protocol; *length = 0; - net_socket = net.ip_sockets[sock]; - - if( NET_IsSocketValid( net_socket ) ) + for( protocol = 0; protocol < 2; protocol++ ) { - addr_len = sizeof( addr ); - ret = recvfrom( net_socket, buf, sizeof( buf ), 0, (struct sockaddr *)&addr, &addr_len ); - - if( !NET_IsSocketError( ret ) ) + switch( protocol ) { + case 0: net_socket = net.ip_sockets[sock]; break; + case 1: net_socket = net.ip6_sockets[sock]; break; + } + + if( NET_IsSocketValid( net_socket ) ) + { + addr_len = sizeof( addr ); + ret = recvfrom( net_socket, buf, sizeof( buf ), 0, (struct sockaddr *)&addr, &addr_len ); + NET_SockadrToNetadr( &addr, from ); - if( ret < NET_MAX_FRAGMENT ) + if( !NET_IsSocketError( ret ) ) { - // Transfer data - memcpy( data, buf, ret ); - *length = ret; -#if !XASH_DEDICATED - if( CL_LegacyMode() ) - return NET_LagPacket( true, sock, from, length, data ); - - // check for split message - if( sock == NS_CLIENT && *(int *)data == NET_HEADER_SPLITPACKET ) + if( ret < NET_MAX_FRAGMENT ) { - return NET_GetLong( data, ret, length, CL_GetSplitSize() ); - } + // Transfer data + memcpy( data, buf, ret ); + *length = ret; +#if !XASH_DEDICATED + if( CL_LegacyMode() ) + return NET_LagPacket( true, sock, from, length, data ); + + // check for split message + if( sock == NS_CLIENT && *(int *)data == NET_HEADER_SPLITPACKET ) + { + return NET_GetLong( data, ret, length, CL_GetSplitSize() ); + } #endif - // lag the packet, if needed - return NET_LagPacket( true, sock, from, length, data ); + // lag the packet, if needed + return NET_LagPacket( true, sock, from, length, data ); + } + else + { + Con_Reportf( "NET_QueuePacket: oversize packet from %s\n", NET_AdrToString( *from )); + } } else { - Con_Reportf( "NET_QueuePacket: oversize packet from %s\n", NET_AdrToString( *from )); - } - } - else - { - int err = WSAGetLastError(); + int err = WSAGetLastError(); - switch( err ) - { - case WSAEWOULDBLOCK: - case WSAECONNRESET: - case WSAECONNREFUSED: - case WSAEMSGSIZE: - case WSAETIMEDOUT: - break; - default: // let's continue even after errors - Con_DPrintf( S_ERROR "NET_QueuePacket: %s from %s\n", NET_ErrorString(), NET_AdrToString( *from )); - break; + switch( err ) + { + case WSAEWOULDBLOCK: + case WSAECONNRESET: + case WSAECONNREFUSED: + case WSAEMSGSIZE: + case WSAETIMEDOUT: + break; + default: // let's continue even after errors + Con_DPrintf( S_ERROR "NET_QueuePacket: %s from %s\n", NET_ErrorString(), NET_AdrToString( *from )); + break; + } } } } @@ -1183,7 +1331,7 @@ NET_SendLong Fragment long packets, send short directly ================== */ -int NET_SendLong( netsrc_t sock, int net_socket, const char *buf, size_t len, int flags, const struct sockaddr *to, size_t tolen, size_t splitsize ) +int NET_SendLong( netsrc_t sock, int net_socket, const char *buf, size_t len, int flags, const struct sockaddr_storage *to, size_t tolen, size_t splitsize ) { #ifdef NET_USE_FRAGMENTS // do we need to break this packet up? @@ -1217,13 +1365,13 @@ int NET_SendLong( netsrc_t sock, int net_socket, const char *buf, size_t len, in netadr_t adr; memset( &adr, 0, sizeof( adr )); - NET_SockadrToNetadr((struct sockaddr *)to, &adr ); + NET_SockadrToNetadr( to, &adr ); Con_Printf( "Sending split %i of %i with %i bytes and seq %i to %s\n", packet_number + 1, packet_count, size, net.sequence_number, NET_AdrToString( adr )); } - ret = sendto( net_socket, packet, size + sizeof( SPLITPACKET ), flags, to, tolen ); + ret = sendto( net_socket, packet, size + sizeof( SPLITPACKET ), flags, (const struct sockaddr *)to, tolen ); if( ret < 0 ) return ret; // error if( ret >= size ) @@ -1239,7 +1387,7 @@ int NET_SendLong( netsrc_t sock, int net_socket, const char *buf, size_t len, in #endif { // no fragmenantion for client connection - return sendto( net_socket, buf, len, flags, to, tolen ); + return sendto( net_socket, buf, len, flags, (const struct sockaddr *)to, tolen ); } } @@ -1251,7 +1399,7 @@ NET_SendPacketEx void NET_SendPacketEx( netsrc_t sock, size_t length, const void *data, netadr_t to, size_t splitsize ) { int ret; - struct sockaddr addr; + struct sockaddr_storage addr; SOCKET net_socket = 0; if( !net.initialized || to.type == NA_LOOPBACK ) @@ -1259,21 +1407,21 @@ void NET_SendPacketEx( netsrc_t sock, size_t length, const void *data, netadr_t NET_SendLoopPacket( sock, length, data, to ); return; } - else if( to.type == NA_BROADCAST ) + else if( to.type == NA_BROADCAST || to.type == NA_IP ) { net_socket = net.ip_sockets[sock]; - if( !NET_IsSocketValid( net_socket ) ) + if( !NET_IsSocketValid( net_socket )) return; } - else if( to.type == NA_IP ) + else if( to.type6 == NA_MULTICAST_IP6 || to.type6 == NA_IP6 ) { - net_socket = net.ip_sockets[sock]; - if( !NET_IsSocketValid( net_socket ) ) + net_socket = net.ip6_sockets[sock]; + if( !NET_IsSocketValid( net_socket )) return; } else { - Host_Error( "NET_SendPacket: bad address type %i\n", to.type ); + Host_Error( "NET_SendPacket: bad address type %i (%i)\n", to.type, to.type6 ); } NET_NetadrToSockadr( &to, &addr ); @@ -1289,7 +1437,7 @@ void NET_SendPacketEx( netsrc_t sock, size_t length, const void *data, netadr_t return; // some PPP links don't allow broadcasts - if( err == WSAEADDRNOTAVAIL && to.type == NA_BROADCAST ) + if( err == WSAEADDRNOTAVAIL && ( to.type == NA_BROADCAST || to.type6 == NA_MULTICAST_IP6 )) return; if( Host_IsDedicated() ) @@ -1385,18 +1533,18 @@ qboolean NET_BufferToBufferDecompress( byte *dest, uint *destLen, byte *source, NET_IPSocket ==================== */ -static int NET_IPSocket( const char *net_interface, int port, qboolean multicast ) +static int NET_IPSocket( const char *net_interface, int port, qboolean multicast, qboolean usev6 ) { - struct sockaddr_in addr; + struct sockaddr_storage addr; int err, net_socket; uint optval = 1; dword _true = 1; - if( NET_IsSocketError(( net_socket = socket( PF_INET, SOCK_DGRAM, IPPROTO_UDP )) ) ) + if( NET_IsSocketError(( net_socket = socket( usev6 ? PF_INET6 : PF_INET, SOCK_DGRAM, IPPROTO_UDP )) ) ) { err = WSAGetLastError(); if( err != WSAEAFNOSUPPORT ) - Con_DPrintf( S_WARN "NET_UDPSocket: port: %d socket: %s\n", port, NET_ErrorString( )); + Con_DPrintf( S_WARN "NET_UDPSocket%s: port: %d socket: %s\n", usev6 ? "6" : "", port, NET_ErrorString( )); return INVALID_SOCKET; } @@ -1404,7 +1552,7 @@ static int NET_IPSocket( const char *net_interface, int port, qboolean multicast { struct timeval timeout; - Con_DPrintf( S_WARN "NET_UDPSocket: port: %d ioctl FIONBIO: %s\n", port, NET_ErrorString( )); + Con_DPrintf( S_WARN "NET_UDPSocket%s: port: %d ioctl FIONBIO: %s\n", usev6 ? "6" : "", port, NET_ErrorString( )); // try timeout instead of NBIO timeout.tv_sec = timeout.tv_usec = 0; setsockopt( net_socket, SOL_SOCKET, SO_RCVTIMEO, (char *)&timeout, sizeof(timeout)); @@ -1413,20 +1561,20 @@ static int NET_IPSocket( const char *net_interface, int port, qboolean multicast // make it broadcast capable if( NET_IsSocketError( setsockopt( net_socket, SOL_SOCKET, SO_BROADCAST, (char *)&_true, sizeof( _true ) ) ) ) { - Con_DPrintf( S_WARN "NET_UDPSocket: port: %d setsockopt SO_BROADCAST: %s\n", port, NET_ErrorString( )); + Con_DPrintf( S_WARN "NET_UDPSocket%s: port: %d setsockopt SO_BROADCAST: %s\n", usev6 ? "6" : "", port, NET_ErrorString( )); } if( Sys_CheckParm( "-reuse" ) || multicast ) { if( NET_IsSocketError( setsockopt( net_socket, SOL_SOCKET, SO_REUSEADDR, (const char *)&optval, sizeof( optval )) ) ) { - Con_DPrintf( S_WARN "NET_UDPSocket: port: %d setsockopt SO_REUSEADDR: %s\n", port, NET_ErrorString( )); + Con_DPrintf( S_WARN "NET_UDPSocket%s: port: %d setsockopt SO_REUSEADDR: %s\n", usev6 ? "6" : "", port, NET_ErrorString( )); closesocket( net_socket ); return INVALID_SOCKET; } } - if( Sys_CheckParm( "-tos" )) + if( Sys_CheckParm( "-tos" ) && !usev6 ) { optval = 16; Con_Printf( "Enabling LOWDELAY TOS option\n" ); @@ -1435,24 +1583,47 @@ static int NET_IPSocket( const char *net_interface, int port, qboolean multicast { err = WSAGetLastError(); if( err != WSAENOPROTOOPT ) - Con_Printf( S_WARN "NET_UDPSocket: port: %d setsockopt IP_TOS: %s\n", port, NET_ErrorString( )); + Con_Printf( S_WARN "NET_UDPSocket%s: port: %d setsockopt IP_TOS: %s\n", usev6 ? "6" : "", port, NET_ErrorString( )); closesocket( net_socket ); return INVALID_SOCKET; } } - if( !COM_CheckStringEmpty( net_interface ) || !Q_stricmp( net_interface, "localhost" )) - addr.sin_addr.s_addr = INADDR_ANY; - else NET_StringToSockaddr( net_interface, (struct sockaddr *)&addr, false ); - - if( port == PORT_ANY ) addr.sin_port = 0; - else addr.sin_port = htons((short)port); - - addr.sin_family = AF_INET; - - if( NET_IsSocketError( bind( net_socket, (void *)&addr, sizeof( addr )) ) ) + if( usev6 ) { - Con_DPrintf( S_WARN "NET_UDPSocket: port: %d bind: %s\n", port, NET_ErrorString( )); + if( NET_IsSocketError( setsockopt( net_socket, IPPROTO_IPV6, IPV6_V6ONLY, &_true, sizeof( _true )))) + { + err = WSAGetLastError(); + if( err != WSAENOPROTOOPT ) + Con_Printf( S_WARN "NET_UDPSocket%s: port: %d setsockopt IPV6_V6ONLY: %s\n", usev6 ? "6" : "", port, NET_ErrorString( )); + closesocket( net_socket ); + return INVALID_SOCKET; + } + + if( !COM_CheckStringEmpty( net_interface ) || !Q_stricmp( net_interface, "localhost" )) + memcpy(((struct sockaddr_in6 *)&addr)->sin6_addr.s6_addr, &in6addr_any, sizeof( struct in6_addr )); + else NET_StringToSockaddr( net_interface, &addr, false, AF_INET6 ); + + if( port == PORT_ANY ) ((struct sockaddr_in6 *)&addr)->sin6_port = 0; + else ((struct sockaddr_in6 *)&addr)->sin6_port = htons((short)port); + + ((struct sockaddr_in6 *)&addr)->sin6_family = AF_INET6; + } + else + { + if( !COM_CheckStringEmpty( net_interface ) || !Q_stricmp( net_interface, "localhost" )) + ((struct sockaddr_in *)&addr)->sin_addr.s_addr = INADDR_ANY; + else NET_StringToSockaddr( net_interface, &addr, false, AF_INET ); + + if( port == PORT_ANY ) ((struct sockaddr_in *)&addr)->sin_port = 0; + else ((struct sockaddr_in *)&addr)->sin_port = htons((short)port); + + ((struct sockaddr_in *)&addr)->sin_family = AF_INET; + } + + if( NET_IsSocketError( bind( net_socket, (struct sockaddr *)&addr, sizeof( addr )) ) ) + { + Con_DPrintf( S_WARN "NET_UDPSocket%s: port: %d bind: %s\n", usev6 ? "6" : "", port, NET_ErrorString( )); closesocket( net_socket ); return INVALID_SOCKET; } @@ -1461,7 +1632,7 @@ static int NET_IPSocket( const char *net_interface, int port, qboolean multicast { optval = 1; if( NET_IsSocketError( setsockopt( net_socket, IPPROTO_IP, IP_MULTICAST_LOOP, (const char *)&optval, sizeof( optval )) ) ) - Con_DPrintf( S_WARN "NET_UDPSocket: port %d setsockopt IP_MULTICAST_LOOP: %s\n", port, NET_ErrorString( )); + Con_DPrintf( S_WARN "NET_UDPSocket%s: port %d setsockopt IP_MULTICAST_LOOP: %s\n", usev6 ? "6" : "", port, NET_ErrorString( )); } return net_socket; @@ -1474,18 +1645,17 @@ NET_OpenIP */ static void NET_OpenIP( void ) { - int port, sv_port = 0, cl_port = 0; + int port; if( !NET_IsSocketValid( net.ip_sockets[NS_SERVER] ) ) { port = net_iphostport->value; if( !port ) port = net_hostport->value; if( !port ) port = PORT_SERVER; // forcing to default - net.ip_sockets[NS_SERVER] = NET_IPSocket( net_ipname->string, port, false ); + net.ip_sockets[NS_SERVER] = NET_IPSocket( net_ipname->string, port, false, false ); if( !NET_IsSocketValid( net.ip_sockets[NS_SERVER] ) && Host_IsDedicated() ) Host_Error( "Couldn't allocate dedicated server IP port %d.\n", port ); - sv_port = port; } // dedicated servers don't need client ports @@ -1496,11 +1666,46 @@ static void NET_OpenIP( void ) port = net_ipclientport->value; if( !port ) port = net_clientport->value; if( !port ) port = PORT_ANY; // forcing to default - net.ip_sockets[NS_CLIENT] = NET_IPSocket( net_ipname->string, port, false ); + net.ip_sockets[NS_CLIENT] = NET_IPSocket( net_ipname->string, port, false, false ); if( !NET_IsSocketValid( net.ip_sockets[NS_CLIENT] ) ) - net.ip_sockets[NS_CLIENT] = NET_IPSocket( net_ipname->string, PORT_ANY, false ); - cl_port = port; + net.ip_sockets[NS_CLIENT] = NET_IPSocket( net_ipname->string, PORT_ANY, false, false ); + } +} + +/* +==================== +NET_OpenIP6 +==================== +*/ +static void NET_OpenIP6( void ) +{ + int port; + + if( !NET_IsSocketValid( net.ip6_sockets[NS_SERVER] ) ) + { + port = net_ip6hostport->value; + if( !port ) port = net_hostport->value + 10000; + if( !port ) port = PORT_SERVER + 10000; // forcing to default + + net.ip6_sockets[NS_SERVER] = NET_IPSocket( net_ip6name->string, port, false, true ); + + if( !NET_IsSocketValid( net.ip6_sockets[NS_SERVER] ) && Host_IsDedicated() ) + Host_Error( "Couldn't allocate dedicated server IPv6 port %d.\n", port ); + } + + // dedicated servers don't need client ports + if( Host_IsDedicated() ) return; + + if( !NET_IsSocketValid( net.ip6_sockets[NS_CLIENT] ) ) + { + port = net_ip6clientport->value; + if( !port ) port = net_clientport->value; + if( !port ) port = PORT_ANY; // forcing to default + net.ip6_sockets[NS_CLIENT] = NET_IPSocket( net_ip6name->string, port, false, true ); + + if( !NET_IsSocketValid( net.ip6_sockets[NS_CLIENT] ) ) + net.ip6_sockets[NS_CLIENT] = NET_IPSocket( net_ip6name->string, PORT_ANY, false, true ); } } @@ -1514,10 +1719,11 @@ Returns the servers' ip address as a string. void NET_GetLocalAddress( void ) { char buff[512]; - struct sockaddr_in address; + struct sockaddr_storage address; WSAsize_t namelen; memset( &net_local, 0, sizeof( netadr_t )); + memset( &net6_local, 0, sizeof( netadr_t )); buff[0] = '\0'; if( net.allow_ip ) @@ -1535,7 +1741,7 @@ void NET_GetLocalAddress( void ) buff[511] = 0; } - if( NET_StringToAdr( buff, &net_local )) + if( NET_StringToAdrEx( buff, &net_local, AF_INET )) { namelen = sizeof( address ); @@ -1547,7 +1753,7 @@ void NET_GetLocalAddress( void ) } else { - net_local.port = address.sin_port; + net_local.port = ((struct sockaddr_in *)&address)->sin_port; Con_Printf( "Server IP address %s\n", NET_AdrToString( net_local )); Cvar_FullSet( "net_address", va( "%s", NET_AdrToString( net_local )), FCVAR_READ_ONLY ); } @@ -1557,7 +1763,49 @@ void NET_GetLocalAddress( void ) Con_DPrintf( S_ERROR "Could not get TCP/IP address, Invalid hostname: '%s'\n", buff ); } } - else + + buff[0] = 0; + + if( net.allow_ip6 ) + { + // If we have changed the ip var from the command line, use that instead. + if( Q_strcmp( net_ip6name->string, "localhost" )) + { + Q_strncpy( buff, net_ip6name->string, sizeof( buff ) ); + } + else + { + gethostname( buff, 512 ); + + // ensure that it doesn't overrun the buffer + buff[511] = 0; + } + + if( NET_StringToAdrEx( buff, &net6_local, AF_INET6 )) + { + namelen = sizeof( address ); + + if( NET_IsSocketError( getsockname( net.ip6_sockets[NS_SERVER], (struct sockaddr *)&address, &namelen ) ) ) + { + // this may happens if multiple clients running on single machine + Con_DPrintf( S_ERROR "Could not get IPv6 address. Reason: %s\n", NET_ErrorString( )); +// net.allow_ip6 = false; + } + else + { + net6_local.port = ((struct sockaddr_in6 *)&address)->sin6_port; + Con_Printf( "Server IPv6 address %s\n", NET_AdrToString( net6_local )); + Cvar_FullSet( "net6_address", va( "%s", NET_AdrToString( net6_local )), FCVAR_READ_ONLY ); + } + } + else + { + Con_DPrintf( S_ERROR "Could not get TCP/IP address, Invalid hostname: '%s'\n", buff ); + } + + } + + if( !net.allow_ip && !net.allow_ip6 ) { Con_Printf( "TCP/IP Disabled.\n" ); } @@ -1587,6 +1835,7 @@ void NET_Config( qboolean multiplayer ) { // open sockets if( net.allow_ip ) NET_OpenIP(); + if( net.allow_ip6 ) NET_OpenIP6(); // get our local address, if possible if( bFirst ) @@ -1602,11 +1851,17 @@ void NET_Config( qboolean multiplayer ) // shut down any existing sockets for( i = 0; i < NS_COUNT; i++ ) { - if( net.ip_sockets[i] != INVALID_SOCKET ) + if( NET_IsSocketValid( net.ip_sockets[i] )) { closesocket( net.ip_sockets[i] ); net.ip_sockets[i] = INVALID_SOCKET; } + + if( NET_IsSocketValid( net.ip6_sockets[i] )) + { + closesocket( net.ip6_sockets[i] ); + net.ip6_sockets[i] = INVALID_SOCKET; + } } } @@ -1703,12 +1958,19 @@ void NET_Init( void ) net_fakelag = Cvar_Get( "fakelag", "0", FCVAR_PRIVILEGED, "lag all incoming network data (including loopback) by xxx ms." ); net_fakeloss = Cvar_Get( "fakeloss", "0", FCVAR_PRIVILEGED, "act like we dropped the packet this % of the time." ); + // cvar equivalents for IPv6 + net_ip6name = Cvar_Get( "ip6", "localhost", FCVAR_READ_ONLY, "network ip6 address" ); + net_ip6hostport = Cvar_Get( "ip6_hostport", "0", FCVAR_READ_ONLY, "network ip6 host port" ); + net_ip6clientport = Cvar_Get( "ip6_clientport", "0", FCVAR_READ_ONLY, "network ip6 client port" ); + net6_address = Cvar_Get( "net6_address", "0", FCVAR_READ_ONLY, "contain local IPv6 address of current client" ); + // prepare some network data for( i = 0; i < NS_COUNT; i++ ) { net.lagdata[i].prev = &net.lagdata[i]; net.lagdata[i].next = &net.lagdata[i]; net.ip_sockets[i] = INVALID_SOCKET; + net.ip6_sockets[i] = INVALID_SOCKET; } #if XASH_WIN32 @@ -1722,9 +1984,8 @@ void NET_Init( void ) net.threads_initialized = true; #endif - if( Sys_CheckParm( "-noip" )) - net.allow_ip = false; - else net.allow_ip = true; + net.allow_ip = !Sys_CheckParm( "-noip" ); + net.allow_ip6 = !Sys_CheckParm( "-noip6" ); // specify custom host port if( Sys_GetParmFromCmdLine( "-port", cmd ) && Q_isdigit( cmd )) @@ -1734,6 +1995,10 @@ void NET_Init( void ) if( Sys_GetParmFromCmdLine( "-ip", cmd )) Cvar_FullSet( "ip", cmd, FCVAR_READ_ONLY ); + // specify custom ip6 + if( Sys_GetParmFromCmdLine( "-ip6", cmd )) + Cvar_FullSet( "ip6", cmd, FCVAR_READ_ONLY ); + // adjust clockwindow if( Sys_GetParmFromCmdLine( "-clockwindow", cmd )) Cvar_SetValue( "clockwindow", Q_atof( cmd )); @@ -2095,7 +2360,7 @@ void HTTP_Run( void ) for( curfile = http.first_file; curfile; curfile = curfile->next ) { int res; - struct sockaddr addr; + struct sockaddr_storage addr; if( curfile->state == HTTP_FREE ) continue; @@ -2158,7 +2423,7 @@ void HTTP_Run( void ) if( fResolving ) continue; - res = NET_StringToSockaddr( va( "%s:%d", curfile->server->host, curfile->server->port ), &addr, true ); + res = NET_StringToSockaddr( va( "%s:%d", curfile->server->host, curfile->server->port ), &addr, true, AF_INET ); if( res == 2 ) { @@ -2177,7 +2442,7 @@ void HTTP_Run( void ) if( curfile->state < HTTP_CONNECTED ) // Connection not enstabilished { - res = connect( curfile->socket, &addr, sizeof( struct sockaddr ) ); + res = connect( curfile->socket, (struct sockaddr*)&addr, sizeof( addr ) ); if( res ) { diff --git a/engine/common/netchan.h b/engine/common/netchan.h index a1e7c3a7..daed9f31 100644 --- a/engine/common/netchan.h +++ b/engine/common/netchan.h @@ -279,6 +279,7 @@ typedef struct netchan_s extern netadr_t net_from; extern netadr_t net_local; +extern netadr_t net6_local; extern sizebuf_t net_message; extern byte net_message_buffer[NET_MAX_MESSAGE]; extern convar_t sv_lan; From 5d18c6d678240b27eee3c25ee0adc1e2fb1e2c09 Mon Sep 17 00:00:00 2001 From: Alibek Omarov Date: Fri, 11 Mar 2022 12:26:44 +0300 Subject: [PATCH 005/490] engine: fix declaration-after-statement --- engine/common/ipv6text.c | 52 +++++++++++++++++++++++++--------------- 1 file changed, 33 insertions(+), 19 deletions(-) diff --git a/engine/common/ipv6text.c b/engine/common/ipv6text.c index 0a190e55..2d6d427e 100644 --- a/engine/common/ipv6text.c +++ b/engine/common/ipv6text.c @@ -1,6 +1,7 @@ #include #include #include "ipv6text.h" +#include "xash3d_types.h" #ifdef _WIN32 #ifndef snprintf @@ -10,13 +11,14 @@ void IPv6IPToString( char *pszOutText, const unsigned char *ip ) { - // Find the longest run of consecutive zero quads. // If there's a tie, we want the leftmost one. int idxLongestRunStart = -1; int nLongestRun = 1; // It must be at least 2 quads in a row, a single 0 must not be compressed - int nCurrentRun = 0; - int idxQuad; + int nCurrentRun = 0, idxQuad; + char *p; + qboolean bNeedColon; + for ( idxQuad = 0 ; idxQuad < 8 ; ++idxQuad ) { // Zero @@ -40,9 +42,9 @@ void IPv6IPToString( char *pszOutText, const unsigned char *ip ) } // Print the quads - char *p = pszOutText; + p = pszOutText; idxQuad = 0; - bool bNeedColon = false; + bNeedColon = false; while ( idxQuad < 8 ) { // Run of compressed zeros? @@ -55,6 +57,10 @@ void IPv6IPToString( char *pszOutText, const unsigned char *ip ) } else { + // Lowercase hex digits, with leading zeros omitted + static const char hexdigits[] = "0123456789abcdef"; + unsigned quad; + // Colon to separate from previous, unless // we are first or immediately follow compressed zero "::" if ( bNeedColon ) @@ -64,11 +70,9 @@ void IPv6IPToString( char *pszOutText, const unsigned char *ip ) bNeedColon = true; // Assemble 16-bit quad value from the two bytes - unsigned quad = ( (unsigned)ip[idxQuad*2] << 8U ) | ip[idxQuad*2 + 1]; + quad = ( (unsigned)ip[idxQuad*2] << 8U ) | ip[idxQuad*2 + 1]; // Manually do the hex number formatting. - // Lowercase hex digits, with leading zeros omitted - static const char hexdigits[] = "0123456789abcdef"; if ( quad >= 0x0010 ) { if ( quad >= 0x0100 ) @@ -139,9 +143,14 @@ bool ParseIPv6Addr_IsSpace( char c ) } bool ParseIPv6Addr( const char *pszText, unsigned char *pOutIP, int *pOutPort, uint32_t *pOutScope ) { + unsigned char *d, *pZeroFill, *pEndIP; + const char *s; + qboolean bQuadMustFollow; + int nPort; + while ( ParseIPv6Addr_IsSpace( *pszText ) ) ++pszText; - const char *s = pszText; + s = pszText; // Skip opening bracket, if present if ( *s == '[' ) @@ -152,10 +161,10 @@ bool ParseIPv6Addr( const char *pszText, unsigned char *pOutIP, int *pOutPort, u } // Special case for leading "::" - bool bQuadMustFollow = true; - unsigned char *d = pOutIP; - unsigned char *pZeroFill = NULL; - unsigned char *pEndIP = pOutIP + 16; + bQuadMustFollow = true; + d = pOutIP; + pZeroFill = NULL; + pEndIP = pOutIP + 16; if ( s[0] == ':' && s[1] == ':' ) { pZeroFill = d; @@ -168,6 +177,7 @@ bool ParseIPv6Addr( const char *pszText, unsigned char *pOutIP, int *pOutPort, u { // Next thing must be a quad, or end of input. Is it a quad? int quadDigit = ParseIPv6Addr_HexDigitVal( *s ); + int quad; if ( quadDigit < 0 ) { if ( bQuadMustFollow ) @@ -180,7 +190,7 @@ bool ParseIPv6Addr( const char *pszText, unsigned char *pOutIP, int *pOutPort, u return false; ++s; - int quad = quadDigit; + quad = quadDigit; // Now parse up to three additional characters quadDigit = ParseIPv6Addr_HexDigitVal( *s ); @@ -273,11 +283,13 @@ bool ParseIPv6Addr( const char *pszText, unsigned char *pOutIP, int *pOutPort, u if ( *s == '%' ) { - ++s; - // Parse scope number uint32_t unScope = 0; - int nScopeDigit = ParseIPv6Addr_DecimalDigitVal( *s ); + int nScopeDigit; + + ++s; + + nScopeDigit = ParseIPv6Addr_DecimalDigitVal( *s ); if ( nScopeDigit < 0 ) return false; unScope = (uint32_t)nScopeDigit; @@ -349,15 +361,17 @@ bool ParseIPv6Addr( const char *pszText, unsigned char *pOutIP, int *pOutPort, u return false; // Parse port number - int nPort = ParseIPv6Addr_DecimalDigitVal( *s ); + nPort = ParseIPv6Addr_DecimalDigitVal( *s ); if ( nPort < 0 ) return false; for (;;) { + int portDigit; + ++s; if ( *s == '\0' || ParseIPv6Addr_IsSpace( *s ) ) break; - int portDigit = ParseIPv6Addr_DecimalDigitVal( *s ); + portDigit = ParseIPv6Addr_DecimalDigitVal( *s ); if ( portDigit < 0 ) return false; nPort = nPort * 10 + portDigit; From b072b627a2056e7052769b580b3c776d4b75f2c0 Mon Sep 17 00:00:00 2001 From: Alibek Omarov Date: Fri, 11 Mar 2022 12:51:58 +0300 Subject: [PATCH 006/490] engine: enable getaddrinfo on Windows, fix build --- engine/common/net_ws.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/engine/common/net_ws.c b/engine/common/net_ws.c index 9d2ad02f..09a70abf 100644 --- a/engine/common/net_ws.c +++ b/engine/common/net_ws.c @@ -23,6 +23,8 @@ GNU General Public License for more details. #include typedef int WSAsize_t; +#define HAVE_GETADDRINFO + #elif !defined XASH_NO_NETWORK // BSD sockets #include @@ -381,7 +383,7 @@ qboolean NET_GetHostByName( const char *hostname, int family, struct sockaddr_st return false; ((struct sockaddr_in *)addr)->sin_family = AF_INET; - ((struct sockaddr_in *)addr)->sin_addr = *(in_addr *)h->h_addr_list[0]; + ((struct sockaddr_in *)addr)->sin_addr = *(struct in_addr *)h->h_addr_list[0]; return true; #endif From 1a5a76a20198dc03968d01ac846ecb16907dcb50 Mon Sep 17 00:00:00 2001 From: Alibek Omarov Date: Mon, 14 Mar 2022 19:10:05 +0300 Subject: [PATCH 007/490] engine: common: minor fixes to ipv6text library --- engine/common/ipv6text.c | 2 +- engine/common/ipv6text.h | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/engine/common/ipv6text.c b/engine/common/ipv6text.c index 2d6d427e..c4062c56 100644 --- a/engine/common/ipv6text.c +++ b/engine/common/ipv6text.c @@ -136,7 +136,7 @@ static inline int ParseIPv6Addr_DecimalDigitVal( char c ) if ( c >= '0' && c <= '9' ) return c - '0'; return -1; } -bool ParseIPv6Addr_IsSpace( char c ) +static bool ParseIPv6Addr_IsSpace( char c ) { // Newlines don't count, intentionally return c == ' ' || c == '\t'; diff --git a/engine/common/ipv6text.h b/engine/common/ipv6text.h index a6f73c65..088f45ea 100644 --- a/engine/common/ipv6text.h +++ b/engine/common/ipv6text.h @@ -6,14 +6,14 @@ /// Max length of an IPv6 string, with scope, WITHOUT port number, including \0': /// 0123:4567:89ab:cdef:0123:4567:89ab:cdef%4294967295 -#define k_ncchMaxIPV6AddrStringWithoutPort 51; +#define k_ncchMaxIPV6AddrStringWithoutPort 51 /// Max number of bytes output by IPv6AddrToString, including '\0': /// [0123:4567:89ab:cdef:0123:4567:89ab:cdef%4294967295]:12345 /// There are other strings that are acceptable to ParseIPv6Addr /// that are longer than this, but this is the longest canonical /// string. -#define k_ncchMaxIPV6AddrStringWithPort 59; +#define k_ncchMaxIPV6AddrStringWithPort 59 #ifdef __cplusplus extern "C" { From a1299e1dfe66dffd82f6798fed5cbdd7a6853ad3 Mon Sep 17 00:00:00 2001 From: Alibek Omarov Date: Sun, 20 Mar 2022 05:18:14 +0300 Subject: [PATCH 008/490] common: third iteration of ipv6 netadr_t, now binary compatible with v4-to-v6 mapped addresses --- common/netadr.h | 13 +++++-------- 1 file changed, 5 insertions(+), 8 deletions(-) diff --git a/common/netadr.h b/common/netadr.h index bd8c7879..6404e640 100644 --- a/common/netadr.h +++ b/common/netadr.h @@ -21,7 +21,7 @@ typedef enum { - NA_UNUSED, + NA_UNUSED = 0, NA_LOOPBACK, NA_BROADCAST, NA_IP, @@ -50,23 +50,20 @@ typedef struct netadr_s struct { uint32_t type; - uint8_t ip[4]; - uint8_t ipx[10]; }; struct { #if XASH_LITTLE_ENDIAN uint16_t type6; - uint8_t ip6_0[2]; + uint8_t ip6_10[2]; // or 10-th two IPv6 octets #elif XASH_BIG_ENDIAN - uint8_t ip6_0[2]; + uint8_t ip6_10[2]; // or 10-th two IPv6 octets uint16_t type6; -#else -#error #endif - uint8_t ip6_1[14]; }; }; + uint8_t ip[4]; // or last 4 IPv6 octets + uint8_t ipx[10]; // or first 10 IPv6 octets uint16_t port; } netadr_t; #pragma pack( pop ) From bd1bfea695f5ec3b7e2cf13409b6145a363de740 Mon Sep 17 00:00:00 2001 From: Alibek Omarov Date: Sun, 20 Mar 2022 05:19:26 +0300 Subject: [PATCH 009/490] engine: second iteration of IPv6 support Made code smaller Fixed problem where v6 and v4 socket can't use same port Added support for v4-to-v6 mapped addresses, although it's kept unused Probably final version --- engine/common/net_ws.c | 522 +++++++++++++------------------- engine/platform/posix/net.h | 87 ++++++ engine/platform/stub/net_stub.h | 5 +- engine/platform/win32/net.h | 23 ++ 4 files changed, 321 insertions(+), 316 deletions(-) create mode 100644 engine/platform/posix/net.h create mode 100644 engine/platform/win32/net.h diff --git a/engine/common/net_ws.c b/engine/common/net_ws.c index 09a70abf..5acb4ed0 100644 --- a/engine/common/net_ws.c +++ b/engine/common/net_ws.c @@ -19,90 +19,15 @@ GNU General Public License for more details. #include "xash3d_mathlib.h" #include "ipv6text.h" #if XASH_WIN32 -// Winsock -#include -typedef int WSAsize_t; - -#define HAVE_GETADDRINFO - -#elif !defined XASH_NO_NETWORK -// BSD sockets -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#define WSAGetLastError() errno -#define WSAEINTR EINTR -#define WSAEBADF EBADF -#define WSAEACCES EACCES -#define WSAEFAULT EFAULT -#define WSAEINVAL EINVAL -#define WSAEMFILE EMFILE -#define WSAEWOULDBLOCK EWOULDBLOCK -#define WSAEINPROGRESS EINPROGRESS -#define WSAEALREADY EALREADY -#define WSAENOTSOCK ENOTSOCK -#define WSAEDESTADDRREQ EDESTADDRREQ -#define WSAEMSGSIZE EMSGSIZE -#define WSAEPROTOTYPE EPROTOTYPE -#define WSAENOPROTOOPT ENOPROTOOPT -#define WSAEPROTONOSUPPORT EPROTONOSUPPORT -#define WSAESOCKTNOSUPPORT ESOCKTNOSUPPORT -#define WSAEOPNOTSUPP EOPNOTSUPP -#define WSAEPFNOSUPPORT EPFNOSUPPORT -#define WSAEAFNOSUPPORT EAFNOSUPPORT -#define WSAEADDRINUSE EADDRINUSE -#define WSAEADDRNOTAVAIL EADDRNOTAVAIL -#define WSAENETDOWN ENETDOWN -#define WSAENETUNREACH ENETUNREACH -#define WSAENETRESET ENETRESET -#define WSAECONNABORTED ECONNABORTED -#define WSAECONNRESET ECONNRESET -#define WSAENOBUFS ENOBUFS -#define WSAEISCONN EISCONN -#define WSAENOTCONN ENOTCONN -#define WSAESHUTDOWN ESHUTDOWN -#define WSAETOOMANYREFS ETOOMANYREFS -#define WSAETIMEDOUT ETIMEDOUT -#define WSAECONNREFUSED ECONNREFUSED -#define WSAELOOP ELOOP -#define WSAENAMETOOLONG ENAMETOOLONG -#define WSAEHOSTDOWN EHOSTDOWN - - -#ifndef XASH_DOS4GW -#define HAVE_GETADDRINFO -#define INVALID_SOCKET -1 -#define SOCKET_ERROR -1 - -#if XASH_EMSCRIPTEN -/* All socket operations are non-blocking already */ -static int ioctl_stub( int d, unsigned long r, ... ) -{ - return 0; -} -#define ioctlsocket ioctl_stub -#else // XASH_EMSCRIPTEN -#define ioctlsocket ioctl -#endif // XASH_EMSCRIPTEN -#define closesocket close -#endif -#define SOCKET int -typedef int WSAsize_t; -#else +#include "platform/win32/net.h" +#elif defined XASH_NO_NETWORK #include "platform/stub/net_stub.h" +#else +#include "platform/posix/net.h" #endif #define NET_USE_FRAGMENTS -static const uint8_t k_ipv6Bytes_LinkLocalAllNodes[16] = { 0xff, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01 }; // ff02:1 - #define PORT_ANY -1 #define MAX_LOOPBACK 4 #define MASK_LOOPBACK (MAX_LOOPBACK - 1) @@ -112,6 +37,10 @@ static const uint8_t k_ipv6Bytes_LinkLocalAllNodes[16] = { 0xff, 0x02, 0x00, 0x0 #define SPLITPACKET_MAX_SIZE 64000 #define NET_MAX_FRAGMENTS ( NET_MAX_FRAGMENT / (SPLITPACKET_MIN_SIZE - sizeof( SPLITPACKET )) ) +// ff02:1 +static const uint8_t k_ipv6Bytes_LinkLocalAllNodes[16] = +{ 0xff, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01 }; + typedef struct { byte data[NET_MAX_MESSAGE]; @@ -277,16 +206,18 @@ _inline qboolean NET_IsSocketValid( int socket ) #endif } -_inline void NET_NetadrToIP6Bytes( uint8_t ip6[16], const netadr_t *adr ) +_inline void NET_NetadrToIP6Bytes( uint8_t *ip6, const netadr_t *adr ) { - memcpy( ip6, adr->ip6_0, 2 ); - memcpy( ip6 + 2, adr->ip6_1, 14 ); + memcpy( ip6, adr->ipx, 10 ); + memcpy( ip6 + 10, adr->ip6_10, 2 ); + memcpy( ip6 + 12, adr->ip, 4 ); } -_inline void NET_IP6BytesToNetadr( netadr_t *adr, const uint8_t ip6[16] ) +_inline void NET_IP6BytesToNetadr( netadr_t *adr, const uint8_t *ip6 ) { - memcpy( adr->ip6_0, ip6, 2 ); - memcpy( adr->ip6_1, ip6 + 2, 14 ); + memcpy( adr->ipx, ip6, 10 ); + memcpy( adr->ip6_10, ip6 + 10, 2 ); + memcpy( adr->ip, ip6 + 12, 4 ); } /* @@ -304,7 +235,7 @@ static void NET_NetadrToSockadr( netadr_t *a, struct sockaddr_storage *s ) ((struct sockaddr_in *)s)->sin_port = a->port; ((struct sockaddr_in *)s)->sin_addr.s_addr = INADDR_BROADCAST; } - else if( a->type == NA_IP ) + else if( a->type == NA_IP || a->type == (0xffff0000 | NA_IP6) ) { ((struct sockaddr_in *)s)->sin_family = AF_INET; ((struct sockaddr_in *)s)->sin_addr.s_addr = *(int *)&a->ip; @@ -313,7 +244,7 @@ static void NET_NetadrToSockadr( netadr_t *a, struct sockaddr_storage *s ) else if( a->type6 == NA_IP6 ) { ((struct sockaddr_in6 *)s)->sin6_family = AF_INET6; - NET_NetadrToIP6Bytes( ((struct sockaddr_in6 *)s)->sin6_addr.s6_addr, a ); + NET_NetadrToIP6Bytes(((struct sockaddr_in6 *)s)->sin6_addr.s6_addr, a ); ((struct sockaddr_in6 *)s)->sin6_port = a->port; } else if( a->type6 == NA_MULTICAST_IP6 ) @@ -339,8 +270,13 @@ static void NET_SockadrToNetadr( const struct sockaddr_storage *s, netadr_t *a ) } else if( s->ss_family == AF_INET6 ) { - a->type6 = NA_IP6; NET_IP6BytesToNetadr( a, ((struct sockaddr_in6 *)s)->sin6_addr.s6_addr ); + + if( IN6_IS_ADDR_V4MAPPED( &((struct sockaddr_in6 *)s)->sin6_addr )) + a->type = NA_IP; + else + a->type6 = NA_IP6; + a->port = ((struct sockaddr_in6 *)s)->sin6_port; } } @@ -625,14 +561,13 @@ const char *NET_AdrToString( const netadr_t a ) return "loopback"; if( a.type6 == NA_IP6 ) { - // TODO: remove that!!! + char s[64]; uint8_t ip6[16]; - char *s = va( "" ); NET_NetadrToIP6Bytes( ip6, &a ); - IPv6AddrToString( s, ip6, ntohs( a.port ), 0 ); - return s; + + return va( "%s", s ); } return va( "%i.%i.%i.%i:%i", a.ip[0], a.ip[1], a.ip[2], a.ip[3], ntohs( a.port )); @@ -649,14 +584,13 @@ const char *NET_BaseAdrToString( const netadr_t a ) return "loopback"; if( a.type6 == NA_IP6 ) { - // TODO: remove that!!! + char s[64]; uint8_t ip6[16]; - char *s = va( "" ); NET_NetadrToIP6Bytes( ip6, &a ); - IPv6IPToString( s, ip6 ); - return s; + + return va( "%s", s ); } return va( "%i.%i.%i.%i", a.ip[0], a.ip[1], a.ip[2], a.ip[3] ); } @@ -684,7 +618,9 @@ qboolean NET_CompareBaseAdr( const netadr_t a, const netadr_t b ) if( a.type6 == NA_IP6 ) { - if( !memcmp( a.ip6_0, b.ip6_0, 2 ) && !memcmp( a.ip6_1, b.ip6_1, 14 )) + if( !memcmp( a.ip, b.ip, 4 ) && + !memcmp( a.ip6_10, b.ip6_10, 2 ) && + !memcmp( a.ipx, b.ipx, 10 )) return true; } @@ -712,7 +648,9 @@ qboolean NET_CompareClassBAdr( netadr_t a, netadr_t b ) return true; } - // TODO: ipv6 + // NOTE: we don't check for IPv6 here + // this check is very dumb and only used for LAN restriction + // Actual check is in IsReservedAdr return false; } @@ -729,6 +667,7 @@ qboolean NET_IsReservedAdr( netadr_t a ) if( a.type == NA_LOOPBACK ) return true; + // Following checks was imported from GameNetworkingSockets library if( a.type == NA_IP ) { if(( a.ip[0] == 10 ) || // 10.x.x.x is reserved @@ -745,15 +684,15 @@ qboolean NET_IsReservedAdr( netadr_t a ) { // Private addresses, fc00::/7 // Range is fc00:: to fdff:ffff:etc - if ( a.ip6_0[0] >= 0xFC && a.ip6_0[1] <= 0xFD ) + if ( a.ipx[0] >= 0xFC && a.ipx[1] <= 0xFD ) { return true; } // Link-local fe80::/10 // Range is fe80:: to febf:: - if ( a.ip6_0[0] == 0xFE - && ( a.ip6_0[1] >= 0x80 && a.ip6_0[1] <= 0xBF ) ) + if ( a.ipx[0] == 0xFE + && ( a.ipx[1] >= 0x80 && a.ipx[1] <= 0xBF ) ) { return true; } @@ -786,7 +725,10 @@ qboolean NET_CompareAdr( const netadr_t a, const netadr_t b ) if( a.type6 == NA_IP6 ) { - if( !memcmp( a.ip6_0, b.ip6_0, 2 ) && !memcmp( a.ip6_1, b.ip6_1, 14 ) && a.port == b.port ) + if( !memcmp( a.ip, b.ip, 4 ) && + !memcmp( a.ip6_10, b.ip6_10, 2 ) && + !memcmp( a.ipx, b.ipx, 10 ) && + a.port == b.port ) return true; } @@ -1231,11 +1173,10 @@ queue normal and lagged packets static qboolean NET_QueuePacket( netsrc_t sock, netadr_t *from, byte *data, size_t *length ) { byte buf[NET_MAX_FRAGMENT]; - int ret; + int ret, protocol; int net_socket; WSAsize_t addr_len; struct sockaddr_storage addr; - int protocol; *length = 0; @@ -1247,54 +1188,54 @@ static qboolean NET_QueuePacket( netsrc_t sock, netadr_t *from, byte *data, size case 1: net_socket = net.ip6_sockets[sock]; break; } - if( NET_IsSocketValid( net_socket ) ) + if( !NET_IsSocketValid( net_socket )) + continue; + + addr_len = sizeof( addr ); + ret = recvfrom( net_socket, buf, sizeof( buf ), 0, (struct sockaddr *)&addr, &addr_len ); + + NET_SockadrToNetadr( &addr, from ); + + if( !NET_IsSocketError( ret )) { - addr_len = sizeof( addr ); - ret = recvfrom( net_socket, buf, sizeof( buf ), 0, (struct sockaddr *)&addr, &addr_len ); - - NET_SockadrToNetadr( &addr, from ); - - if( !NET_IsSocketError( ret ) ) + if( ret < NET_MAX_FRAGMENT ) { - if( ret < NET_MAX_FRAGMENT ) - { - // Transfer data - memcpy( data, buf, ret ); - *length = ret; + // Transfer data + memcpy( data, buf, ret ); + *length = ret; #if !XASH_DEDICATED - if( CL_LegacyMode() ) - return NET_LagPacket( true, sock, from, length, data ); - - // check for split message - if( sock == NS_CLIENT && *(int *)data == NET_HEADER_SPLITPACKET ) - { - return NET_GetLong( data, ret, length, CL_GetSplitSize() ); - } -#endif - // lag the packet, if needed + if( CL_LegacyMode() ) return NET_LagPacket( true, sock, from, length, data ); - } - else + + // check for split message + if( sock == NS_CLIENT && *(int *)data == NET_HEADER_SPLITPACKET ) { - Con_Reportf( "NET_QueuePacket: oversize packet from %s\n", NET_AdrToString( *from )); + return NET_GetLong( data, ret, length, CL_GetSplitSize() ); } +#endif + // lag the packet, if needed + return NET_LagPacket( true, sock, from, length, data ); } else { - int err = WSAGetLastError(); + Con_Reportf( "NET_QueuePacket: oversize packet from %s\n", NET_AdrToString( *from )); + } + } + else + { + int err = WSAGetLastError(); - switch( err ) - { - case WSAEWOULDBLOCK: - case WSAECONNRESET: - case WSAECONNREFUSED: - case WSAEMSGSIZE: - case WSAETIMEDOUT: - break; - default: // let's continue even after errors - Con_DPrintf( S_ERROR "NET_QueuePacket: %s from %s\n", NET_ErrorString(), NET_AdrToString( *from )); - break; - } + switch( err ) + { + case WSAEWOULDBLOCK: + case WSAECONNRESET: + case WSAECONNREFUSED: + case WSAEMSGSIZE: + case WSAETIMEDOUT: + break; + default: // let's continue even after errors + Con_DPrintf( S_ERROR "NET_QueuePacket: %s from %s\n", NET_ErrorString(), NET_AdrToString( *from )); + break; } } } @@ -1535,18 +1476,24 @@ qboolean NET_BufferToBufferDecompress( byte *dest, uint *destLen, byte *source, NET_IPSocket ==================== */ -static int NET_IPSocket( const char *net_interface, int port, qboolean multicast, qboolean usev6 ) +static int NET_IPSocket( const char *net_iface, int port, int family ) { struct sockaddr_storage addr; int err, net_socket; uint optval = 1; dword _true = 1; + int pfamily; - if( NET_IsSocketError(( net_socket = socket( usev6 ? PF_INET6 : PF_INET, SOCK_DGRAM, IPPROTO_UDP )) ) ) + if( family == AF_INET6 ) + pfamily = PF_INET6; + else if( family == AF_INET ) + pfamily == PF_INET; + + if( NET_IsSocketError(( net_socket = socket( pfamily, SOCK_DGRAM, IPPROTO_UDP )) ) ) { err = WSAGetLastError(); if( err != WSAEAFNOSUPPORT ) - Con_DPrintf( S_WARN "NET_UDPSocket%s: port: %d socket: %s\n", usev6 ? "6" : "", port, NET_ErrorString( )); + Con_DPrintf( S_WARN "NET_UDPSocket: port: %d socket: %s\n", port, NET_ErrorString( )); return INVALID_SOCKET; } @@ -1554,7 +1501,7 @@ static int NET_IPSocket( const char *net_interface, int port, qboolean multicast { struct timeval timeout; - Con_DPrintf( S_WARN "NET_UDPSocket%s: port: %d ioctl FIONBIO: %s\n", usev6 ? "6" : "", port, NET_ErrorString( )); + Con_DPrintf( S_WARN "NET_UDPSocket: port: %d ioctl FIONBIO: %s\n", port, NET_ErrorString( )); // try timeout instead of NBIO timeout.tv_sec = timeout.tv_usec = 0; setsockopt( net_socket, SOL_SOCKET, SO_RCVTIMEO, (char *)&timeout, sizeof(timeout)); @@ -1563,78 +1510,83 @@ static int NET_IPSocket( const char *net_interface, int port, qboolean multicast // make it broadcast capable if( NET_IsSocketError( setsockopt( net_socket, SOL_SOCKET, SO_BROADCAST, (char *)&_true, sizeof( _true ) ) ) ) { - Con_DPrintf( S_WARN "NET_UDPSocket%s: port: %d setsockopt SO_BROADCAST: %s\n", usev6 ? "6" : "", port, NET_ErrorString( )); + Con_DPrintf( S_WARN "NET_UDPSocket: port: %d setsockopt SO_BROADCAST: %s\n", port, NET_ErrorString( )); } - if( Sys_CheckParm( "-reuse" ) || multicast ) + if( NET_IsSocketError( setsockopt( net_socket, SOL_SOCKET, SO_REUSEADDR, (const char *)&optval, sizeof( optval )) ) ) { - if( NET_IsSocketError( setsockopt( net_socket, SOL_SOCKET, SO_REUSEADDR, (const char *)&optval, sizeof( optval )) ) ) - { - Con_DPrintf( S_WARN "NET_UDPSocket%s: port: %d setsockopt SO_REUSEADDR: %s\n", usev6 ? "6" : "", port, NET_ErrorString( )); - closesocket( net_socket ); - return INVALID_SOCKET; - } - } - - if( Sys_CheckParm( "-tos" ) && !usev6 ) - { - optval = 16; - Con_Printf( "Enabling LOWDELAY TOS option\n" ); - - if( NET_IsSocketError( setsockopt( net_socket, IPPROTO_IP, IP_TOS, (const char *)&optval, sizeof( optval )) ) ) - { - err = WSAGetLastError(); - if( err != WSAENOPROTOOPT ) - Con_Printf( S_WARN "NET_UDPSocket%s: port: %d setsockopt IP_TOS: %s\n", usev6 ? "6" : "", port, NET_ErrorString( )); - closesocket( net_socket ); - return INVALID_SOCKET; - } - } - - if( usev6 ) - { - if( NET_IsSocketError( setsockopt( net_socket, IPPROTO_IPV6, IPV6_V6ONLY, &_true, sizeof( _true )))) - { - err = WSAGetLastError(); - if( err != WSAENOPROTOOPT ) - Con_Printf( S_WARN "NET_UDPSocket%s: port: %d setsockopt IPV6_V6ONLY: %s\n", usev6 ? "6" : "", port, NET_ErrorString( )); - closesocket( net_socket ); - return INVALID_SOCKET; - } - - if( !COM_CheckStringEmpty( net_interface ) || !Q_stricmp( net_interface, "localhost" )) - memcpy(((struct sockaddr_in6 *)&addr)->sin6_addr.s6_addr, &in6addr_any, sizeof( struct in6_addr )); - else NET_StringToSockaddr( net_interface, &addr, false, AF_INET6 ); - - if( port == PORT_ANY ) ((struct sockaddr_in6 *)&addr)->sin6_port = 0; - else ((struct sockaddr_in6 *)&addr)->sin6_port = htons((short)port); - - ((struct sockaddr_in6 *)&addr)->sin6_family = AF_INET6; - } - else - { - if( !COM_CheckStringEmpty( net_interface ) || !Q_stricmp( net_interface, "localhost" )) - ((struct sockaddr_in *)&addr)->sin_addr.s_addr = INADDR_ANY; - else NET_StringToSockaddr( net_interface, &addr, false, AF_INET ); - - if( port == PORT_ANY ) ((struct sockaddr_in *)&addr)->sin_port = 0; - else ((struct sockaddr_in *)&addr)->sin_port = htons((short)port); - - ((struct sockaddr_in *)&addr)->sin_family = AF_INET; - } - - if( NET_IsSocketError( bind( net_socket, (struct sockaddr *)&addr, sizeof( addr )) ) ) - { - Con_DPrintf( S_WARN "NET_UDPSocket%s: port: %d bind: %s\n", usev6 ? "6" : "", port, NET_ErrorString( )); + Con_DPrintf( S_WARN "NET_UDPSocket: port: %d setsockopt SO_REUSEADDR: %s\n", port, NET_ErrorString( )); closesocket( net_socket ); return INVALID_SOCKET; } - if( Sys_CheckParm( "-loopback" )) + addr.ss_family = family; + + if( family == AF_INET6 ) { - optval = 1; - if( NET_IsSocketError( setsockopt( net_socket, IPPROTO_IP, IP_MULTICAST_LOOP, (const char *)&optval, sizeof( optval )) ) ) - Con_DPrintf( S_WARN "NET_UDPSocket%s: port %d setsockopt IP_MULTICAST_LOOP: %s\n", usev6 ? "6" : "", port, NET_ErrorString( )); + if( NET_IsSocketError( setsockopt( net_socket, IPPROTO_IPV6, IPV6_V6ONLY, (char *)&_true, sizeof( _true )))) + { + Con_DPrintf( S_WARN "NET_UDPSocket: port: %d setsockopt IPV6_V6ONLY: %s\n", port, NET_ErrorString( )); + closesocket( net_socket ); + return INVALID_SOCKET; + } + + if( Sys_CheckParm( "-loopback" )) + { + if( NET_IsSocketError( setsockopt( net_socket, IPPROTO_IPV6, IPV6_MULTICAST_LOOP, (char *)&_true, sizeof( _true )))) + Con_DPrintf( S_WARN "NET_UDPSocket: port %d setsockopt IPV6_MULTICAST_LOOP: %s\n", port, NET_ErrorString( )); + } + + if( COM_CheckStringEmpty( net_iface ) && Q_stricmp( net_iface, "localhost" )) + NET_StringToSockaddr( net_iface, &addr, false, AF_INET6 ); + else memcpy(((struct sockaddr_in6 *)&addr)->sin6_addr.s6_addr, &in6addr_any, sizeof( struct in6_addr )); + + if( port == PORT_ANY ) ((struct sockaddr_in6 *)&addr)->sin6_port = 0; + else ((struct sockaddr_in6 *)&addr)->sin6_port = htons((short)port); + + if( NET_IsSocketError( bind( net_socket, (struct sockaddr *)&addr, sizeof( addr )))) + { + Con_DPrintf( S_WARN "NET_UDPSocket: port: %d bind6: %s\n", port, NET_ErrorString( )); + closesocket( net_socket ); + return INVALID_SOCKET; + } + } + else if( family == AF_INET ) + { + if( Sys_CheckParm( "-tos" )) + { + optval = 0x10; // IPTOS_LOWDELAY + Con_Printf( "Enabling LOWDELAY TOS option\n" ); + + if( NET_IsSocketError( setsockopt( net_socket, IPPROTO_IP, IP_TOS, (const char *)&optval, sizeof( optval )))) + { + err = WSAGetLastError(); + if( err != WSAENOPROTOOPT ) + Con_Printf( S_WARN "NET_UDPSocket: port: %d setsockopt IP_TOS: %s\n", port, NET_ErrorString( )); + closesocket( net_socket ); + return INVALID_SOCKET; + } + } + + if( Sys_CheckParm( "-loopback" )) + { + if( NET_IsSocketError( setsockopt( net_socket, IPPROTO_IP, IP_MULTICAST_LOOP, (char *)&_true, sizeof( _true )))) + Con_DPrintf( S_WARN "NET_UDPSocket: port %d setsockopt IP_MULTICAST_LOOP: %s\n", port, NET_ErrorString( )); + } + + if( COM_CheckStringEmpty( net_iface ) && Q_stricmp( net_iface, "localhost" )) + NET_StringToSockaddr( net_iface, &addr, false, AF_INET ); + else ((struct sockaddr_in *)&addr)->sin_addr.s_addr = INADDR_ANY; + + if( port == PORT_ANY ) ((struct sockaddr_in *)&addr)->sin_port = 0; + else ((struct sockaddr_in *)&addr)->sin_port = htons((short)port); + + if( NET_IsSocketError( bind( net_socket, (struct sockaddr *)&addr, sizeof( addr )))) + { + Con_DPrintf( S_WARN "NET_UDPSocket: port: %d bind: %s\n", port, NET_ErrorString( )); + closesocket( net_socket ); + return INVALID_SOCKET; + } } return net_socket; @@ -1645,69 +1597,33 @@ static int NET_IPSocket( const char *net_interface, int port, qboolean multicast NET_OpenIP ==================== */ -static void NET_OpenIP( void ) +static void NET_OpenIP( int *sockets, const char *net_iface, int hostport, int clientport, int family ) { int port; - if( !NET_IsSocketValid( net.ip_sockets[NS_SERVER] ) ) + if( !NET_IsSocketValid( sockets[NS_SERVER] )) { - port = net_iphostport->value; + port = hostport; if( !port ) port = net_hostport->value; if( !port ) port = PORT_SERVER; // forcing to default - net.ip_sockets[NS_SERVER] = NET_IPSocket( net_ipname->string, port, false, false ); + sockets[NS_SERVER] = NET_IPSocket( net_iface, port, family ); - if( !NET_IsSocketValid( net.ip_sockets[NS_SERVER] ) && Host_IsDedicated() ) + if( !NET_IsSocketValid( sockets[NS_SERVER] ) && Host_IsDedicated() ) Host_Error( "Couldn't allocate dedicated server IP port %d.\n", port ); } // dedicated servers don't need client ports if( Host_IsDedicated() ) return; - if( !NET_IsSocketValid( net.ip_sockets[NS_CLIENT] ) ) + if( !NET_IsSocketValid( sockets[NS_CLIENT] )) { - port = net_ipclientport->value; + port = clientport; if( !port ) port = net_clientport->value; if( !port ) port = PORT_ANY; // forcing to default - net.ip_sockets[NS_CLIENT] = NET_IPSocket( net_ipname->string, port, false, false ); + sockets[NS_CLIENT] = NET_IPSocket( net_iface, port, family ); - if( !NET_IsSocketValid( net.ip_sockets[NS_CLIENT] ) ) - net.ip_sockets[NS_CLIENT] = NET_IPSocket( net_ipname->string, PORT_ANY, false, false ); - } -} - -/* -==================== -NET_OpenIP6 -==================== -*/ -static void NET_OpenIP6( void ) -{ - int port; - - if( !NET_IsSocketValid( net.ip6_sockets[NS_SERVER] ) ) - { - port = net_ip6hostport->value; - if( !port ) port = net_hostport->value + 10000; - if( !port ) port = PORT_SERVER + 10000; // forcing to default - - net.ip6_sockets[NS_SERVER] = NET_IPSocket( net_ip6name->string, port, false, true ); - - if( !NET_IsSocketValid( net.ip6_sockets[NS_SERVER] ) && Host_IsDedicated() ) - Host_Error( "Couldn't allocate dedicated server IPv6 port %d.\n", port ); - } - - // dedicated servers don't need client ports - if( Host_IsDedicated() ) return; - - if( !NET_IsSocketValid( net.ip6_sockets[NS_CLIENT] ) ) - { - port = net_ip6clientport->value; - if( !port ) port = net_clientport->value; - if( !port ) port = PORT_ANY; // forcing to default - net.ip6_sockets[NS_CLIENT] = NET_IPSocket( net_ip6name->string, port, false, true ); - - if( !NET_IsSocketValid( net.ip6_sockets[NS_CLIENT] ) ) - net.ip6_sockets[NS_CLIENT] = NET_IPSocket( net_ip6name->string, PORT_ANY, false, true ); + if( !NET_IsSocketValid( sockets[NS_CLIENT] )) + sockets[NS_CLIENT] = NET_IPSocket( net_ipname->string, PORT_ANY, family ); } } @@ -1720,96 +1636,65 @@ Returns the servers' ip address as a string. */ void NET_GetLocalAddress( void ) { + char hostname[512]; char buff[512]; struct sockaddr_storage address; WSAsize_t namelen; memset( &net_local, 0, sizeof( netadr_t )); memset( &net6_local, 0, sizeof( netadr_t )); - buff[0] = '\0'; + + if( !net.allow_ip && !net.allow_ip6 ) + { + Con_Printf( "TCP/IP Disabled.\n" ); + return; + } + + gethostname( hostname, sizeof( hostname )); + hostname[sizeof(hostname) - 1] = 0; if( net.allow_ip ) { // If we have changed the ip var from the command line, use that instead. - if( Q_strcmp( net_ipname->string, "localhost" )) - { - Q_strncpy( buff, net_ipname->string, sizeof( buff ) ); - } - else - { - gethostname( buff, 512 ); - - // ensure that it doesn't overrun the buffer - buff[511] = 0; - } + if( Q_stricmp( net_ipname->string, "localhost" )) + Q_strncpy( buff, net_ipname->string, sizeof( buff )); + else Q_strncpy( buff, hostname, sizeof( buff )); if( NET_StringToAdrEx( buff, &net_local, AF_INET )) { - namelen = sizeof( address ); + namelen = sizeof( struct sockaddr_in ); - if( NET_IsSocketError( getsockname( net.ip_sockets[NS_SERVER], (struct sockaddr *)&address, &namelen ) ) ) - { - // this may happens if multiple clients running on single machine - Con_DPrintf( S_ERROR "Could not get TCP/IP address. Reason: %s\n", NET_ErrorString( )); -// net.allow_ip = false; - } - else + if( !NET_IsSocketError( getsockname( net.ip_sockets[NS_SERVER], (struct sockaddr *)&address, &namelen ))) { net_local.port = ((struct sockaddr_in *)&address)->sin_port; - Con_Printf( "Server IP address %s\n", NET_AdrToString( net_local )); + Con_Printf( "Server IPv4 address %s\n", NET_AdrToString( net_local )); Cvar_FullSet( "net_address", va( "%s", NET_AdrToString( net_local )), FCVAR_READ_ONLY ); } + else Con_DPrintf( S_ERROR "Could not get TCP/IPv4 address. Reason: %s\n", NET_ErrorString( )); } - else - { - Con_DPrintf( S_ERROR "Could not get TCP/IP address, Invalid hostname: '%s'\n", buff ); - } + else Con_DPrintf( S_ERROR "Could not get TCP/IPv4 address, Invalid hostname: '%s'\n", buff ); } - buff[0] = 0; - if( net.allow_ip6 ) { // If we have changed the ip var from the command line, use that instead. - if( Q_strcmp( net_ip6name->string, "localhost" )) - { - Q_strncpy( buff, net_ip6name->string, sizeof( buff ) ); - } - else - { - gethostname( buff, 512 ); - - // ensure that it doesn't overrun the buffer - buff[511] = 0; - } + if( Q_stricmp( net_ip6name->string, "localhost" )) + Q_strncpy( buff, net_ip6name->string, sizeof( buff )); + else Q_strncpy( buff, hostname, sizeof( buff )); if( NET_StringToAdrEx( buff, &net6_local, AF_INET6 )) { - namelen = sizeof( address ); + namelen = sizeof( struct sockaddr_in6 ); - if( NET_IsSocketError( getsockname( net.ip6_sockets[NS_SERVER], (struct sockaddr *)&address, &namelen ) ) ) - { - // this may happens if multiple clients running on single machine - Con_DPrintf( S_ERROR "Could not get IPv6 address. Reason: %s\n", NET_ErrorString( )); -// net.allow_ip6 = false; - } - else + if( !NET_IsSocketError( getsockname( net.ip6_sockets[NS_SERVER], (struct sockaddr *)&address, &namelen ))) { net6_local.port = ((struct sockaddr_in6 *)&address)->sin6_port; Con_Printf( "Server IPv6 address %s\n", NET_AdrToString( net6_local )); Cvar_FullSet( "net6_address", va( "%s", NET_AdrToString( net6_local )), FCVAR_READ_ONLY ); } + else Con_DPrintf( S_ERROR "Could not get TCP/IPv6 address. Reason: %s\n", NET_ErrorString( )); } - else - { - Con_DPrintf( S_ERROR "Could not get TCP/IP address, Invalid hostname: '%s'\n", buff ); - } - - } - - if( !net.allow_ip && !net.allow_ip6 ) - { - Con_Printf( "TCP/IP Disabled.\n" ); + else Con_DPrintf( S_ERROR "Could not get TCP/IPv6 address, Invalid hostname: '%s'\n", buff ); } } @@ -1836,8 +1721,11 @@ void NET_Config( qboolean multiplayer ) if( multiplayer ) { // open sockets - if( net.allow_ip ) NET_OpenIP(); - if( net.allow_ip6 ) NET_OpenIP6(); + if( net.allow_ip ) + NET_OpenIP( net.ip_sockets, net_ipname->string, net_iphostport->value, net_ipclientport->value, AF_INET ); + + if( net.allow_ip6 ) + NET_OpenIP( net.ip6_sockets, net_ip6name->string, net_ip6hostport->value, net_ip6clientport->value, AF_INET6 ); // get our local address, if possible if( bFirst ) @@ -1971,7 +1859,7 @@ void NET_Init( void ) { net.lagdata[i].prev = &net.lagdata[i]; net.lagdata[i].next = &net.lagdata[i]; - net.ip_sockets[i] = INVALID_SOCKET; + net.ip_sockets[i] = INVALID_SOCKET; net.ip6_sockets[i] = INVALID_SOCKET; } @@ -1993,6 +1881,10 @@ void NET_Init( void ) if( Sys_GetParmFromCmdLine( "-port", cmd ) && Q_isdigit( cmd )) Cvar_FullSet( "hostport", cmd, FCVAR_READ_ONLY ); + // specify custom IPv6 host port + if( Sys_GetParmFromCmdLine( "-port6", cmd ) && Q_isdigit( cmd )) + Cvar_FullSet( "ip6_hostport", cmd, FCVAR_READ_ONLY ); + // specify custom ip if( Sys_GetParmFromCmdLine( "-ip", cmd )) Cvar_FullSet( "ip", cmd, FCVAR_READ_ONLY ); diff --git a/engine/platform/posix/net.h b/engine/platform/posix/net.h new file mode 100644 index 00000000..03b57d27 --- /dev/null +++ b/engine/platform/posix/net.h @@ -0,0 +1,87 @@ +/* +net.h - WinSock to BSD sockets wrap +Copyright (C) 2022 a1batross + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. +*/ +#ifndef NET_H +#define NET_H + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define WSAGetLastError() errno +#define WSAEINTR EINTR +#define WSAEBADF EBADF +#define WSAEACCES EACCES +#define WSAEFAULT EFAULT +#define WSAEINVAL EINVAL +#define WSAEMFILE EMFILE +#define WSAEWOULDBLOCK EWOULDBLOCK +#define WSAEINPROGRESS EINPROGRESS +#define WSAEALREADY EALREADY +#define WSAENOTSOCK ENOTSOCK +#define WSAEDESTADDRREQ EDESTADDRREQ +#define WSAEMSGSIZE EMSGSIZE +#define WSAEPROTOTYPE EPROTOTYPE +#define WSAENOPROTOOPT ENOPROTOOPT +#define WSAEPROTONOSUPPORT EPROTONOSUPPORT +#define WSAESOCKTNOSUPPORT ESOCKTNOSUPPORT +#define WSAEOPNOTSUPP EOPNOTSUPP +#define WSAEPFNOSUPPORT EPFNOSUPPORT +#define WSAEAFNOSUPPORT EAFNOSUPPORT +#define WSAEADDRINUSE EADDRINUSE +#define WSAEADDRNOTAVAIL EADDRNOTAVAIL +#define WSAENETDOWN ENETDOWN +#define WSAENETUNREACH ENETUNREACH +#define WSAENETRESET ENETRESET +#define WSAECONNABORTED ECONNABORTED +#define WSAECONNRESET ECONNRESET +#define WSAENOBUFS ENOBUFS +#define WSAEISCONN EISCONN +#define WSAENOTCONN ENOTCONN +#define WSAESHUTDOWN ESHUTDOWN +#define WSAETOOMANYREFS ETOOMANYREFS +#define WSAETIMEDOUT ETIMEDOUT +#define WSAECONNREFUSED ECONNREFUSED +#define WSAELOOP ELOOP +#define WSAENAMETOOLONG ENAMETOOLONG +#define WSAEHOSTDOWN EHOSTDOWN + + +#ifndef XASH_DOS4GW +#define HAVE_GETADDRINFO +#define INVALID_SOCKET -1 +#define SOCKET_ERROR -1 + +#if XASH_EMSCRIPTEN +/* All socket operations are non-blocking already */ +static int ioctl_stub( int d, unsigned long r, ... ) +{ + return 0; +} +#define ioctlsocket ioctl_stub +#else // XASH_EMSCRIPTEN +#define ioctlsocket ioctl +#endif // XASH_EMSCRIPTEN +#define closesocket close +#endif +#define SOCKET int +typedef int WSAsize_t; + +#endif // NET_H diff --git a/engine/platform/stub/net_stub.h b/engine/platform/stub/net_stub.h index cb555929..0c931e56 100644 --- a/engine/platform/stub/net_stub.h +++ b/engine/platform/stub/net_stub.h @@ -12,7 +12,8 @@ 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 NET_STUB_H +#define NET_STUB_H #define INVALID_SOCKET (-1) #define SOCKET_ERROR (-1) @@ -92,3 +93,5 @@ struct timeval {long tv_sec;long tv_usec;}; #define WSAELOOP 34 //ELOOP #define WSAENAMETOOLONG 35 //ENAMETOOLONG #define WSAEHOSTDOWN 36 //EHOSTDOWN + +#endif // NET_STUB_H diff --git a/engine/platform/win32/net.h b/engine/platform/win32/net.h new file mode 100644 index 00000000..465db08a --- /dev/null +++ b/engine/platform/win32/net.h @@ -0,0 +1,23 @@ +/* +net.h - WinSock to BSD sockets wrap +Copyright (C) 2022 a1batross + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. +*/ +#ifndef NET_H +#define NET_H + +#include +typedef int WSAsize_t; + +#define HAVE_GETADDRINFO + +#endif // NET_H From b0a889d1a12b1e4e4c0dc0b5353d2db9112448a8 Mon Sep 17 00:00:00 2001 From: Alibek Omarov Date: Mon, 28 Mar 2022 04:07:40 +0300 Subject: [PATCH 010/490] engine: bring back simple netadr_t, as we don't care about IPv4-to-IPv6 mapped addresses anymore --- common/netadr.h | 9 +++--- engine/common/net_ws.c | 66 +++++++++++++++++++++++++++++------------- engine/common/net_ws.h | 2 ++ 3 files changed, 53 insertions(+), 24 deletions(-) diff --git a/common/netadr.h b/common/netadr.h index 6404e640..99eb15c7 100644 --- a/common/netadr.h +++ b/common/netadr.h @@ -50,20 +50,21 @@ typedef struct netadr_s struct { uint32_t type; + uint8_t ip[4]; // or last 4 IPv6 octets + uint8_t ipx[10]; // or first 10 IPv6 octets }; struct { #if XASH_LITTLE_ENDIAN uint16_t type6; - uint8_t ip6_10[2]; // or 10-th two IPv6 octets + uint8_t ip6[16]; #elif XASH_BIG_ENDIAN - uint8_t ip6_10[2]; // or 10-th two IPv6 octets + uint8_t ip6_0[2]; uint16_t type6; + uint8_t ip6_2[14]; #endif }; }; - uint8_t ip[4]; // or last 4 IPv6 octets - uint8_t ipx[10]; // or first 10 IPv6 octets uint16_t port; } netadr_t; #pragma pack( pop ) diff --git a/engine/common/net_ws.c b/engine/common/net_ws.c index 5acb4ed0..3aef0752 100644 --- a/engine/common/net_ws.c +++ b/engine/common/net_ws.c @@ -206,18 +206,36 @@ _inline qboolean NET_IsSocketValid( int socket ) #endif } -_inline void NET_NetadrToIP6Bytes( uint8_t *ip6, const netadr_t *adr ) +void NET_NetadrToIP6Bytes( uint8_t *ip6, const netadr_t *adr ) { - memcpy( ip6, adr->ipx, 10 ); - memcpy( ip6 + 10, adr->ip6_10, 2 ); - memcpy( ip6 + 12, adr->ip, 4 ); +#if XASH_LITTLE_ENDIAN + memcpy( ip6, adr->ip6, sizeof( adr->ip6 )); +#elif XASH_BIG_ENDIAN + memcpy( ip6, adr->ip6_0, sizeof( adr->ip6_0 )); + memcpy( ip6 + sizeof( adr->ip6_0 ), adr->ip6_2, sizeof( adr->ip6_2 )); +#endif } -_inline void NET_IP6BytesToNetadr( netadr_t *adr, const uint8_t *ip6 ) +void NET_IP6BytesToNetadr( netadr_t *adr, const uint8_t *ip6 ) { - memcpy( adr->ipx, ip6, 10 ); - memcpy( adr->ip6_10, ip6 + 10, 2 ); - memcpy( adr->ip, ip6 + 12, 4 ); +#if XASH_LITTLE_ENDIAN + memcpy( adr->ip6, ip6, sizeof( adr->ip6 ) ); +#elif XASH_BIG_ENDIAN + memcpy( adr->ip6_0, ip6, sizeof( adr->ip6_0 )); + memcpy( adr->ip6_2, ip6 + sizeof( adr->ip6_0 ), sizeof( adr->ip6_2 )); +#endif +} + +_inline int NET_NetadrIP6Compare( const netadr_t *a, const netadr_t *b ) +{ +#if XASH_LITTLE_ENDIAN + return memcmp( a->ip6, b->ip6, sizeof( a->ip6 )); +#elif XASH_BIG_ENDIAN + int ret = memcmp( a->ip6_0, b->ip6_0, sizeof( a->ip6_0 )); + if( !ret ) + return memcmp( a->ip6_2, b->ip6_2, sizeof( a->ip6_2 )); + return ret; +#endif } /* @@ -235,17 +253,30 @@ static void NET_NetadrToSockadr( netadr_t *a, struct sockaddr_storage *s ) ((struct sockaddr_in *)s)->sin_port = a->port; ((struct sockaddr_in *)s)->sin_addr.s_addr = INADDR_BROADCAST; } - else if( a->type == NA_IP || a->type == (0xffff0000 | NA_IP6) ) + else if( a->type == NA_IP ) { ((struct sockaddr_in *)s)->sin_family = AF_INET; - ((struct sockaddr_in *)s)->sin_addr.s_addr = *(int *)&a->ip; + ((struct sockaddr_in *)s)->sin_addr.s_addr = *(uint32_t *)&a->ip; ((struct sockaddr_in *)s)->sin_port = a->port; } else if( a->type6 == NA_IP6 ) { - ((struct sockaddr_in6 *)s)->sin6_family = AF_INET6; - NET_NetadrToIP6Bytes(((struct sockaddr_in6 *)s)->sin6_addr.s6_addr, a ); - ((struct sockaddr_in6 *)s)->sin6_port = a->port; + struct in6_addr ip6; + + NET_NetadrToIP6Bytes( ip6.s6_addr, a ); + + if( IN6_IS_ADDR_V4MAPPED( &ip6 )) + { + ((struct sockaddr_in *)s)->sin_family = AF_INET; + ((struct sockaddr_in *)s)->sin_addr.s_addr = *(uint32_t *)(ip6.s6_addr + 12); + ((struct sockaddr_in *)s)->sin_port = a->port; + } + else + { + ((struct sockaddr_in6 *)s)->sin6_family = AF_INET6; + memcpy( &((struct sockaddr_in6 *)s)->sin6_addr, &ip6, sizeof( struct in6_addr )); + ((struct sockaddr_in6 *)s)->sin6_port = a->port; + } } else if( a->type6 == NA_MULTICAST_IP6 ) { @@ -618,9 +649,7 @@ qboolean NET_CompareBaseAdr( const netadr_t a, const netadr_t b ) if( a.type6 == NA_IP6 ) { - if( !memcmp( a.ip, b.ip, 4 ) && - !memcmp( a.ip6_10, b.ip6_10, 2 ) && - !memcmp( a.ipx, b.ipx, 10 )) + if( !NET_NetadrIP6Compare( &a, &b )) return true; } @@ -725,10 +754,7 @@ qboolean NET_CompareAdr( const netadr_t a, const netadr_t b ) if( a.type6 == NA_IP6 ) { - if( !memcmp( a.ip, b.ip, 4 ) && - !memcmp( a.ip6_10, b.ip6_10, 2 ) && - !memcmp( a.ipx, b.ipx, 10 ) && - a.port == b.port ) + if( a.port == b.port && !NET_NetadrIP6Compare( &a, &b )) return true; } diff --git a/engine/common/net_ws.h b/engine/common/net_ws.h index ede8f0b1..4ad83a1f 100644 --- a/engine/common/net_ws.h +++ b/engine/common/net_ws.h @@ -67,6 +67,8 @@ qboolean NET_BufferToBufferDecompress( byte *dest, uint *destLen, byte *source, void NET_SendPacket( netsrc_t sock, size_t length, const void *data, netadr_t to ); void NET_SendPacketEx( netsrc_t sock, size_t length, const void *data, netadr_t to, size_t splitsize ); void NET_ClearLagData( qboolean bClient, qboolean bServer ); +void NET_IP6BytesToNetadr( netadr_t *adr, const uint8_t *ip6 ); +void NET_NetadrToIP6Bytes( uint8_t *ip6, const netadr_t *adr ); #if !XASH_DEDICATED qboolean CL_LegacyMode( void ); From 2b8b3e199386c078f098100116bf1b7266326139 Mon Sep 17 00:00:00 2001 From: Alibek Omarov Date: Thu, 9 Jun 2022 03:07:19 +0300 Subject: [PATCH 011/490] engine: server: new IP filter, rewritten with IPv6 in mind --- common/netadr.h | 8 +- engine/common/com_strings.h | 1 + engine/common/host.c | 11 +- engine/common/net_ws.c | 183 +++++++++- engine/common/net_ws.h | 4 +- engine/common/tests.h | 19 +- engine/server/sv_filter.c | 684 ++++++++++++++++++++++++------------ 7 files changed, 674 insertions(+), 236 deletions(-) diff --git a/common/netadr.h b/common/netadr.h index 99eb15c7..c56f517f 100644 --- a/common/netadr.h +++ b/common/netadr.h @@ -50,8 +50,12 @@ typedef struct netadr_s struct { uint32_t type; - uint8_t ip[4]; // or last 4 IPv6 octets - uint8_t ipx[10]; // or first 10 IPv6 octets + union + { + uint8_t ip[4]; + uint32_t ip4; + }; + uint8_t ipx[10]; }; struct { diff --git a/engine/common/com_strings.h b/engine/common/com_strings.h index fcb40d41..bc495b42 100644 --- a/engine/common/com_strings.h +++ b/engine/common/com_strings.h @@ -21,6 +21,7 @@ GNU General Public License for more details. #define S_WARN "^3Warning:^7 " #define S_ERROR "^1Error:^7 " #define S_USAGE "Usage: " +#define S_USAGE_INDENT " " #define S_OPENGL_NOTE "^2OpenGL Note:^7 " #define S_OPENGL_WARN "^3OpenGL Warning:^7 " diff --git a/engine/common/host.c b/engine/common/host.c index d3ca35b8..7b55e802 100644 --- a/engine/common/host.c +++ b/engine/common/host.c @@ -818,18 +818,15 @@ static void Host_RunTests( int stage ) { case 0: // early engine load memset( &tests_stats, 0, sizeof( tests_stats )); - Test_RunLibCommon(); - Test_RunCommon(); - Test_RunCmd(); - Test_RunCvar(); + TEST_LIST_0; #if !XASH_DEDICATED - Test_RunCon(); + TEST_LIST_0_CLIENT; #endif /* XASH_DEDICATED */ break; case 1: // after FS load - Test_RunImagelib(); + TEST_LIST_1; #if !XASH_DEDICATED - Test_RunVOX(); + TEST_LIST_1_CLIENT; #endif Msg( "Done! %d passed, %d failed\n", tests_stats.passed, tests_stats.failed ); Sys_Quit(); diff --git a/engine/common/net_ws.c b/engine/common/net_ws.c index 3aef0752..79dce12e 100644 --- a/engine/common/net_ws.c +++ b/engine/common/net_ws.c @@ -581,6 +581,118 @@ static int NET_StringToSockaddr( const char *s, struct sockaddr_storage *sadr, q return 1; } +/* +==================== +NET_StringToFilterAdr + +==================== +*/ +qboolean NET_StringToFilterAdr( const char *s, netadr_t *adr, uint *prefixlen ) +{ + char copy[128], *temp; + qboolean hasCIDR = false; + byte ip6[16]; + uint len; + + if( !COM_CheckStringEmpty( s )) + return false; + + memset( adr, 0, sizeof( *adr )); + + // copy the string and remove CIDR prefix + Q_strncpy( copy, s, sizeof( copy )); + temp = Q_strrchr( copy, '/' ); + + if( temp ) + { + *temp = 0; + if( Q_isdigit( temp + 1 )) + { + len = Q_atoi( temp + 1 ); + hasCIDR = len != 0; + } + } + + // try to parse as IPv6 first + if( ParseIPv6Addr( copy, ip6, NULL, NULL )) + { + NET_IP6BytesToNetadr( adr, ip6 ); + adr->type6 = NA_IP6; + + if( !hasCIDR ) + *prefixlen = 128; + else + *prefixlen = len; + } + else + { + int num = 0; + int octet = 0; + + // parse as ipv4 but we don't need to allow all forms here + for( temp = copy; *temp; temp++ ) + { + char c = *temp; + + if( c >= '0' && c <= '9' ) + { + num *= 10; + num += c - '0'; + } + else if( c == '.' ) + { + if( num > 255 ) + return false; + + adr->ip[octet++] = num; + num = 0; + + if( octet > 3 ) + return false; + } + else + { + return false; + } + } + + if( num > 255 ) + return false; + + adr->ip[octet++] = num; + + if( !hasCIDR ) + { + int i; + + *prefixlen = 32; + + for( i = 3; i >= 0; i-- ) + { + if( !adr->ip[i] ) + *prefixlen -= 8; + else + break; + } + } + else + { + uint32_t mask; + + len = bound( 0, len, 32 ); + *prefixlen = len; + + // drop unneeded bits + mask = htonl( adr->ip4 ) & ( 0xFFFFFFFF << ( 32 - len )); + adr->ip4 = ntohl( mask ); + } + + adr->type = NA_IP; + } + + return true; +} + /* ==================== NET_AdrToString @@ -635,17 +747,14 @@ Compares without the port */ qboolean NET_CompareBaseAdr( const netadr_t a, const netadr_t b ) { - if( a.type != b.type ) + if( a.type6 != b.type6 ) return false; if( a.type == NA_LOOPBACK ) return true; if( a.type == NA_IP ) - { - if( !memcmp( a.ip, b.ip, 4 )) - return true; - } + return a.ip4 == b.ip4; if( a.type6 == NA_IP6 ) { @@ -663,9 +772,9 @@ NET_CompareClassBAdr Compare local masks ==================== */ -qboolean NET_CompareClassBAdr( netadr_t a, netadr_t b ) +qboolean NET_CompareClassBAdr( const netadr_t a, const netadr_t b ) { - if( a.type != b.type ) + if( a.type6 != b.type6 ) return false; if( a.type == NA_LOOPBACK ) @@ -681,6 +790,60 @@ qboolean NET_CompareClassBAdr( netadr_t a, netadr_t b ) // this check is very dumb and only used for LAN restriction // Actual check is in IsReservedAdr + // for real mask compare use NET_CompareAdrByMask + + return false; +} + +/* +==================== +NET_CompareAdrByMask + +Checks if adr is a part of subnet +==================== +*/ +qboolean NET_CompareAdrByMask( const netadr_t a, const netadr_t b, uint prefixlen ) +{ + if( a.type6 != b.type6 || a.type == NA_LOOPBACK ) + return false; + + if( a.type == NA_IP ) + { + uint32_t ipa = htonl( a.ip4 ); + uint32_t ipb = htonl( b.ip4 ); + + if(( ipa & (( 0xFFFFFFFFU ) << ( 32 - prefixlen ))) == ipb ) + return true; + } + else if( a.type6 == NA_IP6 ) + { + uint16_t a_[8], b_[8]; + size_t check = prefixlen / 16; + size_t remaining = prefixlen % 16; + + // convert to 16-bit pieces first + NET_NetadrToIP6Bytes( (uint8_t*)a_, &a ); + NET_NetadrToIP6Bytes( (uint8_t*)b_, &b ); + + // check complete hextets first, if not equal, then it's different subnets + if( check && memcmp( a_, b_, check * sizeof( uint16_t ))) + return false; + + // check by bits now, similar to v4 check but with 16-bit type + if( remaining ) + { + uint16_t hexa, hexb, mask = 0xFFFFU << ( 16 - remaining ); + + hexa = htons( a_[check] ); + hexb = htons( b_[check] ); + + if(( hexa & mask ) == ( hexb & mask )) + return true; + } + else + return true; + } + return false; } @@ -739,7 +902,7 @@ Compare full address */ qboolean NET_CompareAdr( const netadr_t a, const netadr_t b ) { - if( a.type != b.type ) + if( a.type6 != b.type6 ) return false; if( a.type == NA_LOOPBACK ) @@ -747,7 +910,7 @@ qboolean NET_CompareAdr( const netadr_t a, const netadr_t b ) if( a.type == NA_IP ) { - if( !memcmp( a.ip, b.ip, 4 ) && a.port == b.port ) + if( a.ip4 == b.ip4 && a.port == b.port ) return true; return false; } @@ -1513,7 +1676,7 @@ static int NET_IPSocket( const char *net_iface, int port, int family ) if( family == AF_INET6 ) pfamily = PF_INET6; else if( family == AF_INET ) - pfamily == PF_INET; + pfamily = PF_INET; if( NET_IsSocketError(( net_socket = socket( pfamily, SOCK_DGRAM, IPPROTO_UDP )) ) ) { diff --git a/engine/common/net_ws.h b/engine/common/net_ws.h index 4ad83a1f..f5591a6d 100644 --- a/engine/common/net_ws.h +++ b/engine/common/net_ws.h @@ -56,11 +56,13 @@ qboolean NET_IsLocalAddress( netadr_t adr ); const char *NET_AdrToString( const netadr_t a ); const char *NET_BaseAdrToString( const netadr_t a ); qboolean NET_IsReservedAdr( netadr_t a ); -qboolean NET_CompareClassBAdr( netadr_t a, netadr_t b ); +qboolean NET_CompareClassBAdr( const netadr_t a, const netadr_t b ); qboolean NET_StringToAdr( const char *string, netadr_t *adr ); +qboolean NET_StringToFilterAdr( const char *s, netadr_t *adr, uint *prefixlen ); int NET_StringToAdrNB( const char *string, netadr_t *adr ); qboolean NET_CompareAdr( const netadr_t a, const netadr_t b ); qboolean NET_CompareBaseAdr( const netadr_t a, const netadr_t b ); +qboolean NET_CompareAdrByMask( const netadr_t a, const netadr_t b, uint prefixlen ); qboolean NET_GetPacket( netsrc_t sock, netadr_t *from, byte *data, size_t *length ); qboolean NET_BufferToBufferCompress( byte *dest, uint *destLen, byte *source, uint sourceLen ); qboolean NET_BufferToBufferDecompress( byte *dest, uint *destLen, byte *source, uint sourceLen ); diff --git a/engine/common/tests.h b/engine/common/tests.h index e58f20a5..a88b6b22 100644 --- a/engine/common/tests.h +++ b/engine/common/tests.h @@ -26,7 +26,7 @@ extern struct tests_stats_s tests_stats; #define TASSERT( exp ) \ _TASSERT( !(exp), Msg( S_ERROR "assert failed at %s:%i\n", __FILE__, __LINE__ ) ) #define TASSERT_EQi( val1, val2 ) \ - _TASSERT( ( val1 ) != ( val2 ), Msg( S_ERROR "assert failed at %s:%i, \"%d\" != \"%d\"\n", __FILE__, __LINE__, #val1, #val2 )) + _TASSERT( ( val1 ) != ( val2 ), Msg( S_ERROR "assert failed at %s:%i, \"%d\" != \"%d\"\n", __FILE__, __LINE__, val1, val2 )) #define TASSERT_STR( str1, str2 ) \ _TASSERT( Q_strcmp(( str1 ), ( str2 )), Msg( S_ERROR "assert failed at %s:%i, \"%s\" != \"%s\"\n", __FILE__, __LINE__, ( str1 ), ( str2 ))) @@ -37,6 +37,23 @@ void Test_RunCmd( void ); void Test_RunCvar( void ); void Test_RunCon( void ); void Test_RunVOX( void ); +void Test_RunIPFilter( void ); + +#define TEST_LIST_0 \ + Test_RunLibCommon(); \ + Test_RunCommon(); \ + Test_RunCmd(); \ + Test_RunCvar(); \ + Test_RunIPFilter(); + +#define TEST_LIST_0_CLIENT \ + Test_RunCon(); + +#define TEST_LIST_1 \ + Test_RunImagelib(); + +#define TEST_LIST_1_CLIENT \ + Test_RunVOX(); #endif diff --git a/engine/server/sv_filter.c b/engine/server/sv_filter.c index fa7164a0..d3d1228d 100644 --- a/engine/server/sv_filter.c +++ b/engine/server/sv_filter.c @@ -17,21 +17,13 @@ GNU General Public License for more details. #include "server.h" -typedef struct ipfilter_s -{ - float time; - float endTime; // -1 for permanent ban - struct ipfilter_s *next; - uint mask; - uint ip; -} ipfilter_t; +/* +============================================================================= -static ipfilter_t *ipfilter = NULL; - - -// TODO: Is IP filter really needed? -// TODO: Make it IPv6 compatible, for future expansion +PLAYER ID FILTER +============================================================================= +*/ typedef struct cidfilter_s { float endTime; @@ -67,32 +59,6 @@ static void SV_RemoveID( const char *id ) } } -static void SV_RemoveIP( uint ip, uint mask ) -{ - ipfilter_t *filter, *prevfilter = NULL; - - for( filter = ipfilter; filter; filter = filter->next ) - { - if( filter->ip != ip || mask != filter->mask ) - { - prevfilter = filter; - continue; - } - - if( filter == ipfilter ) - { - ipfilter = ipfilter->next; - Mem_Free( filter ); - return; - } - - if( prevfilter ) - prevfilter->next = filter->next; - Mem_Free( filter ); - return; - } -} - qboolean SV_CheckID( const char *id ) { qboolean ret = false; @@ -122,34 +88,6 @@ qboolean SV_CheckID( const char *id ) return ret; } -qboolean SV_CheckIP( netadr_t *addr ) -{ - uint ip = addr->ip[0] << 24 | addr->ip[1] << 16 | addr->ip[2] << 8 | addr->ip[3]; - qboolean ret = false; - ipfilter_t *filter; - - for( filter = ipfilter; filter; filter = filter->next ) - { - while( filter->endTime && host.realtime > filter->endTime ) - { - uint rip = filter->ip; - uint rmask = filter->mask; - SV_RemoveIP( rip, rmask ); - filter = filter->next; - if( !filter ) - return false; - } - - if( (ip & filter->mask) == (filter->ip & filter->mask) ) - { - ret = true; - break; - } - } - - return ret; -} - static void SV_BanID_f( void ) { float time = Q_atof( Cmd_Argv( 1 ) ); @@ -289,167 +227,25 @@ static void SV_WriteID_f( void ) FS_Close( f ); } -static qboolean StringToIP( const char *str, const char *maskstr, uint *outip, uint *outmask ) -{ - byte ip[4] = {0}; - byte mask[4] = {0}; - int i = 0; - - if( *str > '9' || *str < '0' ) - return false; - - do - { - while( *str <= '9' && *str >= '0' ) - { - ip[i] *=10; - ip[i] += *str - '0'; - str++; - } - mask[i] = 255; - i++; - if( *str != '.' ) break; - str++; - } while( i < 4 ); - - i = 0; - - if( !maskstr || *maskstr > '9' || *maskstr < '0' ) - goto end; - - do - { - byte mask1 = 0; - while( *maskstr <= '9' && *maskstr >= '0' ) - { - mask1 *=10; - mask1 += *maskstr - '0'; - maskstr++; - } - mask[i] &= mask1; - i++; - if( *maskstr != '.' ) break; - maskstr++; - } while( i < 4 ); - -end: - *outip = ip[0] << 24 | ip[1] << 16 | ip[2] << 8 | ip[3]; - if( outmask ) - *outmask = mask[0] << 24 | mask[1] << 16 | mask[2] << 8 | mask[3]; - - return true; -} - -#define IPARGS(ip) (ip >> 24) & 0xFF, (ip >> 16) & 0xFF, (ip >> 8) & 0xFF, ip & 0xFF -static void SV_AddIP_f( void ) -{ - float time = Q_atof( Cmd_Argv( 1 ) ); - const char *ipstr = Cmd_Argv( 2 ); - const char *maskstr = Cmd_Argv( 3 ); - uint ip, mask; - ipfilter_t *filter; - - if( time ) - time = host.realtime + time * 60.0f; - - if( !StringToIP( ipstr, maskstr, &ip, &mask ) ) - { - Con_Reportf( "Usage: addip [mask]\n0 minutes for permanent ban\n"); - return; - } - - SV_RemoveIP( ip, mask ); - - filter = Mem_Malloc( host.mempool, sizeof( ipfilter_t ) ); - filter->endTime = time; - filter->ip = ip; - filter->mask = mask; - filter->next = ipfilter; - - ipfilter = filter; -} - -static void SV_ListIP_f( void ) -{ - ipfilter_t *filter; - - Con_Reportf( "ip ban list\n" ); - Con_Reportf( "-----------\n" ); - - for( filter = ipfilter; filter; filter = filter->next ) - { - if( filter->endTime && host.realtime > filter->endTime ) - continue; // no negative time - - if( filter->endTime ) - Con_Reportf( "%d.%d.%d.%d %d.%d.%d.%d expries in %f minutes\n", IPARGS( filter->ip ), IPARGS( filter->mask ), ( filter->endTime - host.realtime ) / 60.0f ); - else - Con_Reportf( "%d.%d.%d.%d %d.%d.%d.%d permanent\n", IPARGS( filter->ip ), IPARGS( filter->mask ) ); - } -} - -static void SV_RemoveIP_f( void ) -{ - uint ip, mask; - - if( !StringToIP( Cmd_Argv(1), Cmd_Argv(2), &ip, &mask ) ) - { - Con_Reportf( "Usage: removeip [mask]\n" ); - return; - } - - SV_RemoveIP( ip, mask ); -} - -static void SV_WriteIP_f( void ) -{ - file_t *f = FS_Open( Cvar_VariableString( "listipcfgfile" ), "w", false ); - ipfilter_t *filter; - - if( !f ) - { - Con_DPrintf( S_ERROR "Could not write %s\n", Cvar_VariableString( "listipcfgfile" ) ); - return; - } - - FS_Printf( f, "//=======================================================================\n" ); - FS_Printf( f, "//\t\tCopyright Flying With Gauss Team %s ©\n", Q_timestamp( TIME_YEAR_ONLY )); - FS_Printf( f, "//\t\t %s - archive of IP blacklist\n", Cvar_VariableString( "listipcfgfile" ) ); - FS_Printf( f, "//=======================================================================\n" ); - - for( filter = ipfilter; filter; filter = filter->next ) - if( !filter->endTime ) // only permanent - FS_Printf( f, "addip 0 %d.%d.%d.%d %d.%d.%d.%d\n", IPARGS(filter->ip), IPARGS(filter->mask) ); - - FS_Close( f ); -} - -void SV_InitFilter( void ) +static void SV_InitIDFilter( void ) { Cmd_AddRestrictedCommand( "banid", SV_BanID_f, "ban player by ID" ); Cmd_AddRestrictedCommand( "listid", SV_ListID_f, "list banned players" ); Cmd_AddRestrictedCommand( "removeid", SV_RemoveID_f, "remove player from banned list" ); Cmd_AddRestrictedCommand( "writeid", SV_WriteID_f, "write banned.cfg" ); - Cmd_AddRestrictedCommand( "addip", SV_AddIP_f, "add entry to IP filter" ); - Cmd_AddRestrictedCommand( "listip", SV_ListIP_f, "list current IP filter" ); - Cmd_AddRestrictedCommand( "removeip", SV_RemoveIP_f, "remove IP filter" ); - Cmd_AddRestrictedCommand( "writeip", SV_WriteIP_f, "write listip.cfg" ); } -void SV_ShutdownFilter( void ) +static void SV_ShutdownIDFilter( void ) { - ipfilter_t *ipList, *ipNext; cidfilter_t *cidList, *cidNext; // should be called manually because banned.cfg is not executed by engine - //SV_WriteIP_f(); //SV_WriteID_f(); - for( ipList = ipfilter; ipList; ipList = ipNext ) - { - ipNext = ipList->next; - Mem_Free( ipList ); - } + Cmd_RemoveCommand( "banid" ); + Cmd_RemoveCommand( "listid" ); + Cmd_RemoveCommand( "removeid" ); + Cmd_RemoveCommand( "writeid" ); for( cidList = cidfilter; cidList; cidList = cidNext ) { @@ -458,5 +254,463 @@ void SV_ShutdownFilter( void ) } cidfilter = NULL; +} + +/* +============================================================================= + +CLIENT IP FILTER + +============================================================================= +*/ + +typedef struct ipfilter_s +{ + float endTime; + struct ipfilter_s *next; + netadr_t adr; + uint prefixlen; +} ipfilter_t; + +static ipfilter_t *ipfilter = NULL; + +static void SV_CleanExpiredIPFilters( void ) +{ + ipfilter_t *f, **back; + + back = &ipfilter; + while( 1 ) + { + f = *back; + if( !f ) return; + + if( f->endTime && host.realtime > f->endTime ) + { + *back = f->next; + back = &f->next; + + Mem_Free( f ); + } + else back = &f->next; + } +} + +static int SV_FilterToString( char *dest, size_t size, qboolean config, ipfilter_t *f ) +{ + const char *strformat; + + if( config ) + { + return Q_snprintf( dest, size, "addip 0 %s/%d\n", NET_AdrToString( f->adr ), f->prefixlen ); + } + else if( f->endTime ) + { + return Q_snprintf( dest, size, "%s/%d (%f minutes)", NET_AdrToString( f->adr ), f->prefixlen, f->endTime ); + } + + return Q_snprintf( dest, size, "%s/%d (permanent)", NET_AdrToString( f->adr ), f->prefixlen ); +} + +static qboolean SV_IPFilterIncludesIPFilter( ipfilter_t *a, ipfilter_t *b ) +{ + if( a->adr.type6 != b->adr.type6 ) + return false; + + // can't include bigger subnet in small + if( a->prefixlen < b->prefixlen ) + return false; + + if( a->prefixlen == b->prefixlen ) + return NET_CompareAdr( a->adr, b->adr ); + + return NET_CompareAdrByMask( a->adr, b->adr, b->prefixlen ); +} + +static void SV_RemoveIPFilter( ipfilter_t *toremove, qboolean removeAll, qboolean verbose ) +{ + ipfilter_t *f, **back; + + back = &ipfilter; + while( 1 ) + { + f = *back; + if( !f ) return; + + if( SV_IPFilterIncludesIPFilter( toremove, f )) + { + if( verbose ) + { + string filterStr; + + SV_FilterToString( filterStr, sizeof( filterStr ), false, f ); + + Con_Printf( "%s removed.\n", filterStr ); + } + + *back = f->next; + back = &f->next; + + Mem_Free( f ); + + if( !removeAll ) + break; + } + else back = &f->next; + } +} + + +qboolean SV_CheckIP( netadr_t *adr ) +{ + // TODO: ip rate limit + ipfilter_t *entry = ipfilter; + + for( ; entry; entry = entry->next ) + { + switch( entry->adr.type6 ) + { + case NA_IP: + case NA_IP6: + if( NET_CompareAdrByMask( *adr, entry->adr, entry->prefixlen )) + return true; + break; + } + } + + return false; +} + +static void SV_AddIP_PrintUsage( void ) +{ + Con_Printf(S_USAGE "addip \n" + S_USAGE_INDENT "addip \n" + "Use 0 minutes for permanent\n" + "ipaddress A.B.C.D/24 is equivalent to A.B.C.0 and A.B.C\n" + "NOTE: IPv6 addresses only support prefix format!\n"); +} + +static void SV_RemoveIP_PrintUsage( void ) +{ + Con_Printf(S_USAGE "removeip [removeAll]\n" + S_USAGE_INDENT "removeip [removeAll]\n" + "Use removeAll to delete all ip filters which ipaddress or ipaddress/CIDR includes\n"); +} + +static void SV_ListIP_PrintUsage( void ) +{ + Con_Printf(S_USAGE "listip [ipaddress]\n" + S_USAGE_INDENT "listip [ipaddress/CIDR]\n"); +} + +static void SV_AddIP_f( void ) +{ + const char *szMinutes = Cmd_Argv( 1 ); + const char *adr = Cmd_Argv( 2 ); + ipfilter_t filter, *newfilter; + float minutes; + int i; + + if( Cmd_Argc() != 3 ) + { + // a1ba: kudos to rehlds for an idea of using CIDR prefixes + // in these commands :) + SV_AddIP_PrintUsage(); + return; + } + + minutes = Q_atof( szMinutes ); + if( minutes < 0.1f ) + minutes = 0; + + if( minutes != 0.0f ) + filter.endTime = host.realtime + minutes * 60; + else filter.endTime = 0; + + if( !NET_StringToFilterAdr( adr, &filter.adr, &filter.prefixlen ) ) + { + Con_Printf( "Invalid IP address!\n" ); + SV_AddIP_PrintUsage(); + return; + } + + newfilter = Mem_Malloc( host.mempool, sizeof( *newfilter )); + newfilter->endTime = filter.endTime; + newfilter->adr = filter.adr; + newfilter->prefixlen = filter.prefixlen; + newfilter->next = ipfilter; + + ipfilter = newfilter; + + for( i = 0; i < svs.maxclients; i++ ) + { + netadr_t clientadr = svs.clients[i].netchan.remote_address; + + if( !NET_CompareAdrByMask( clientadr, filter.adr, filter.prefixlen )) + continue; + + SV_ClientPrintf( &svs.clients[i], "The server operator has added you to banned list\n" ); + SV_DropClient( &svs.clients[i], false ); + } +} + +static void SV_ListIP_f( void ) +{ + qboolean haveFilter = false; + ipfilter_t filter, *f; + + if( Cmd_Argc() > 2 ) + { + SV_ListIP_PrintUsage(); + return; + } + + if( ipfilter == NULL ) + { + Con_Printf( "IP filter list is empty\n" ); + return; + } + + if( Cmd_Argc() == 2 ) + { + haveFilter = NET_StringToFilterAdr( Cmd_Argv( 1 ), &filter.adr, &filter.prefixlen ); + + if( !haveFilter ) + { + Con_Printf( "Invalid IP address!\n" ); + SV_ListIP_PrintUsage(); + return; + } + } + + Con_Printf( "IP filter list:\n" ); + + for( f = ipfilter; f; f = f->next ) + { + string filterStr; + + if( haveFilter && !SV_IPFilterIncludesIPFilter( &filter, f )) + continue; + + SV_FilterToString( filterStr, sizeof( filterStr ), false, f ); + Con_Printf( "%s\n", filterStr ); + } +} + +static void SV_RemoveIP_f( void ) +{ + const char *adr = Cmd_Argv( 1 ); + qboolean removeAll; + ipfilter_t filter; + int i; + + if( Cmd_Argc() != 2 && Cmd_Argc() != 3 ) + { + SV_RemoveIP_PrintUsage(); + return; + } + + removeAll = Cmd_Argc() == 3 && !Q_strcmp( Cmd_Argv( 2 ), "removeAll" ); + + if( !NET_StringToFilterAdr( adr, &filter.adr, &filter.prefixlen ) ) + { + Con_Printf( "Invalid IP address!\n" ); + SV_RemoveIP_PrintUsage(); + return; + } + + SV_RemoveIPFilter( &filter, removeAll, true ); +} + +static void SV_WriteIP_f( void ) +{ + file_t *fd = FS_Open( Cvar_VariableString( "listipcfgfile" ), "w", true ); + ipfilter_t *f; + + if( !fd ) + { + Con_Printf( "Couldn't open listip.cfg\n" ); + return; + } + + for( f = ipfilter; f; f = f->next ) + { + string filterStr; + int size; + + // do not save temporary bans + if( f->endTime ) + continue; + + size = SV_FilterToString( filterStr, sizeof( filterStr ), true, f ); + FS_Write( fd, filterStr, size ); + } + + FS_Close( fd ); +} + +static void SV_InitIPFilter( void ) +{ + Cmd_AddRestrictedCommand( "addip", SV_AddIP_f, "add entry to IP filter" ); + Cmd_AddRestrictedCommand( "listip", SV_ListIP_f, "list current IP filter" ); + Cmd_AddRestrictedCommand( "removeip", SV_RemoveIP_f, "remove IP filter" ); + Cmd_AddRestrictedCommand( "writeip", SV_WriteIP_f, "write listip.cfg" ); +} + +static void SV_ShutdownIPFilter( void ) +{ + ipfilter_t *ipList, *ipNext; + + // should be called manually because banned.cfg is not executed by engine + //SV_WriteIP_f(); + + for( ipList = ipfilter; ipList; ipList = ipNext ) + { + ipNext = ipList->next; + Mem_Free( ipList ); + } + ipfilter = NULL; } + +void SV_InitFilter( void ) +{ + SV_InitIPFilter(); + SV_InitIDFilter(); +} + +void SV_ShutdownFilter( void ) +{ + SV_ShutdownIPFilter(); + SV_ShutdownIDFilter(); +} + +#if XASH_ENGINE_TESTS + +#include "tests.h" + +void Test_StringToFilterAdr( void ) +{ + ipfilter_t f1; + int i; + struct + { + const char *str; + qboolean valid; + int prefixlen; + int a, b, c, d; + } ipv4tests[] = + { + { "127.0.0.0/8", true, 8, 127, 0, 0, 0 }, + { "192.168", true, 16, 192, 168, 0, 0 }, + { "192.168/23", true, 23, 192, 168, 0, 0 }, + { "192.168./23", true, 23, 192, 168, 0, 0 }, + { "192.168../23", true, 23, 192, 168, 0, 0 }, + { "..192...168/23", false }, + { "", false }, + { "abcd", false } + }; + struct + { + const char *str; + qboolean valid; + int prefixlen; + uint8_t x[16]; + } ipv6tests[] = + { + { "::1", true, 128, { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1 } }, + { "fd18:b9d4:65cf:83de::/64", true, 64, { 0xfd, 0x18, 0xb9, 0xd4, 0x65, 0xcf, 0x83, 0xde } }, + { "kkljnljkhfjnkj", false }, + { "fd8a:63d5:e014:0d62:ffff:ffff:ffff:ffff:ffff", false }, + }; + + for( i = 0; i < ARRAYSIZE( ipv4tests ); i++ ) + { + qboolean ret = NET_StringToFilterAdr( ipv4tests[i].str, &f1.adr, &f1.prefixlen ); + + TASSERT_EQi( ret, ipv4tests[i].valid ); + + if( ret ) + { + TASSERT_EQi( f1.prefixlen, ipv4tests[i].prefixlen ); + TASSERT_EQi( f1.adr.ip[0], ipv4tests[i].a ); + TASSERT_EQi( f1.adr.ip[1], ipv4tests[i].b ); + TASSERT_EQi( f1.adr.ip[2], ipv4tests[i].c ); + TASSERT_EQi( f1.adr.ip[3], ipv4tests[i].d ); + } + } + + for( i = 0; i < ARRAYSIZE( ipv6tests ); i++ ) + { + qboolean ret = NET_StringToFilterAdr( ipv6tests[i].str, &f1.adr, &f1.prefixlen ); + uint8_t x[16]; + + TASSERT_EQi( ret, ipv6tests[i].valid ); + + if( ret ) + { + TASSERT_EQi( f1.prefixlen, ipv6tests[i].prefixlen ); + + NET_NetadrToIP6Bytes( (uint8_t*)x, &f1.adr ); + + TASSERT( memcmp( x, ipv6tests[i].x, sizeof( x )) == 0 ); + } + } +} + +void Test_IPFilterIncludesIPFilter( void ) +{ + qboolean ret; + const char *adrs[] = + { + "127.0.0.1/8", // 0 + "127.0.0.1", // 1 + "192.168/16", // 2 + "fe80::/64", // 3 + "fe80::96ab:9a49:2944:1808", // 4 + "2a00:1370:8190:f9eb::/62", // 5 + "2a00:1370:8190:f9eb:3866:6126:330c:b82b" // 6 + }; + ipfilter_t f[7]; + int i; + int tests[][3] = + { + // ipv4 + { 0, 0, true }, + { 0, 1, false }, + { 1, 0, true }, + { 0, 2, false }, + { 2, 0, false }, + + // mixed + { 0, 3, false }, + { 1, 4, false }, + + // ipv6 + { 3, 3, true }, + { 3, 4, false }, + { 4, 3, true }, + { 5, 3, false }, + { 3, 5, false }, + { 6, 5, true }, + }; + + for( i = 0; i < 7; i++ ) + { + NET_StringToFilterAdr( adrs[i], &f[i].adr, &f[i].prefixlen ); + } + + for( i = 0; i < ARRAYSIZE( tests ); i++ ) + { + ret = SV_IPFilterIncludesIPFilter( &f[tests[i][0]], &f[tests[i][1]] ); + + TASSERT_EQi( ret, tests[i][2] ); + } +} + +void Test_RunIPFilter( void ) +{ + Test_StringToFilterAdr(); + Test_IPFilterIncludesIPFilter(); +} + +#endif // XASH_ENGINE_TESTS From 7157c3b441321cb841316c4b408e0b9462d5f51e Mon Sep 17 00:00:00 2001 From: Alibek Omarov Date: Mon, 8 Aug 2022 23:53:17 +0300 Subject: [PATCH 012/490] engine: platform: sdl: don't enable high dpi code for Apple It seems enables HighDPI awareness but doesn't create HighDPI OpenGL context It needs some plist magic to be enabled back again --- engine/platform/sdl/vid_sdl.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/engine/platform/sdl/vid_sdl.c b/engine/platform/sdl/vid_sdl.c index c2453cca..e9dd87e3 100644 --- a/engine/platform/sdl/vid_sdl.c +++ b/engine/platform/sdl/vid_sdl.c @@ -553,7 +553,9 @@ static qboolean VID_SetScreenResolution( int width, int height ) Uint32 wndFlags = 0; static string wndname; +#if !XASH_APPLE if( vid_highdpi->value ) wndFlags |= SDL_WINDOW_ALLOW_HIGHDPI; +#endif Q_strncpy( wndname, GI->title, sizeof( wndname )); want.w = width; From 365f24e1fe2a5b6d7142a2b919224c4d1432f620 Mon Sep 17 00:00:00 2001 From: Alibek Omarov Date: Tue, 9 Aug 2022 13:47:38 +0300 Subject: [PATCH 013/490] waf.bat: use unicode charset Fixes compiler messages in Russian --- waf.bat | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/waf.bat b/waf.bat index 92d4ed37..db5e1d46 100644 --- a/waf.bat +++ b/waf.bat @@ -1,7 +1,7 @@ @echo off rem try fix py2 build -chcp 1252 +chcp 65001 set PYTHONIOENCODING=UTF-8 rem from issue #964 From 5b97c2135adfd6716ba84ff736682b44cc41c10f Mon Sep 17 00:00:00 2001 From: SNMetamorph <25657591+SNMetamorph@users.noreply.github.com> Date: Tue, 9 Aug 2022 23:50:35 +0400 Subject: [PATCH 014/490] engine: server: fixed "wrong version" spamming in NAT bypass mode (fix #953) --- engine/server/sv_client.c | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/engine/server/sv_client.c b/engine/server/sv_client.c index 97fd122f..0e7da563 100644 --- a/engine/server/sv_client.c +++ b/engine/server/sv_client.c @@ -819,19 +819,17 @@ Responds with short info for broadcast scans The second parameter should be the current protocol version number. ================ */ -void SV_Info( netadr_t from ) +void SV_Info( netadr_t from, int protocolVersion ) { char string[MAX_INFO_STRING]; - int version; // ignore in single player if( svs.maxclients == 1 || !svs.initialized ) return; - version = Q_atoi( Cmd_Argv( 1 )); string[0] = '\0'; - if( version != PROTOCOL_VERSION ) + if( protocolVersion != PROTOCOL_VERSION ) { Q_snprintf( string, sizeof( string ), "%s: wrong version\n", hostname.string ); } @@ -2257,7 +2255,7 @@ void SV_ConnectionlessPacket( netadr_t from, sizebuf_t *msg ) if( !Q_strcmp( pcmd, "ping" )) SV_Ping( from ); else if( !Q_strcmp( pcmd, "ack" )) SV_Ack( from ); - else if( !Q_strcmp( pcmd, "info" )) SV_Info( from ); + else if( !Q_strcmp( pcmd, "info" )) SV_Info( from, Q_atoi( Cmd_Argv( 1 ))); else if( !Q_strcmp( pcmd, "bandwidth" )) SV_TestBandWidth( from ); else if( !Q_strcmp( pcmd, "getchallenge" )) SV_GetChallenge( from ); else if( !Q_strcmp( pcmd, "connect" )) SV_ConnectClient( from ); @@ -2274,7 +2272,7 @@ void SV_ConnectionlessPacket( netadr_t from, sizebuf_t *msg ) netadr_t to; if( NET_StringToAdr( Cmd_Argv( 1 ), &to ) && !NET_IsReservedAdr( to )) - SV_Info( to ); + SV_Info( to, PROTOCOL_VERSION ); } } else if( svgame.dllFuncs.pfnConnectionlessPacket( &from, args, buf, &len )) From 911385d019e5baac0366f68fa20f9bee43ab7cfe Mon Sep 17 00:00:00 2001 From: Alibek Omarov Date: Sat, 13 Aug 2022 00:47:00 +0300 Subject: [PATCH 015/490] engine: client: fix memory leak on player disconnect --- engine/client/cl_parse.c | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/engine/client/cl_parse.c b/engine/client/cl_parse.c index 27c1a3d4..30f40da7 100644 --- a/engine/client/cl_parse.c +++ b/engine/client/cl_parse.c @@ -1348,7 +1348,12 @@ void CL_UpdateUserinfo( sizebuf_t *msg ) if( slot == cl.playernum ) memcpy( &gameui.playerinfo, player, sizeof( player_info_t )); } - else memset( player, 0, sizeof( *player )); + else + { + COM_ClearCustomizationList( &player->customdata, true ); + + memset( player, 0, sizeof( *player )); + } } /* From 1d558b33d6a574cdc4a62eb5019050c62210fa60 Mon Sep 17 00:00:00 2001 From: Alibek Omarov Date: Sat, 13 Aug 2022 00:58:45 +0300 Subject: [PATCH 016/490] engine: client: don't let set unsupported con_charset --- engine/client/console.c | 18 ++++++++++++++---- 1 file changed, 14 insertions(+), 4 deletions(-) diff --git a/engine/client/console.c b/engine/client/console.c index bae1a35d..1ffc15ef 100644 --- a/engine/client/console.c +++ b/engine/client/console.c @@ -914,7 +914,7 @@ static int Con_DrawGenericChar( int x, int y, int number, rgba_t color ) if( !con.curFont || !con.curFont->valid ) return 0; - number = Con_UtfProcessChar(number); + number = Con_UtfProcessChar( number ); if( !number ) return 0; @@ -2403,11 +2403,21 @@ void Con_RunConsole( void ) FBitSet( cl_charset->flags, FCVAR_CHANGED ) ) { // update codepage parameters - g_codepage = 0; - if( !Q_stricmp( con_charset->string, "cp1251" ) ) + if( !Q_stricmp( con_charset->string, "cp1251" )) + { g_codepage = 1251; - else if( !Q_stricmp( con_charset->string, "cp1252" ) ) + } + else if( !Q_stricmp( con_charset->string, "cp1252" )) + { g_codepage = 1252; + } + else + { + Con_Printf( S_WARN "Unknown charset %s, defaulting to cp1252", con_charset->string ); + + Cvar_DirectSet( con_charset, "cp1252" ); + g_codepage = 1252; + } g_utf8 = !Q_stricmp( cl_charset->string, "utf-8" ); Con_InvalidateFonts(); From 314672d82c6b7f5622a20fd7bb1c4e43ece79bcb Mon Sep 17 00:00:00 2001 From: Alibek Omarov Date: Sat, 13 Aug 2022 01:29:45 +0300 Subject: [PATCH 017/490] engine: client: fix invalid playerinfo being accessed by renderer --- engine/client/cl_game.c | 15 --------------- engine/client/ref_common.c | 2 +- engine/common/common.h | 1 - 3 files changed, 1 insertion(+), 17 deletions(-) diff --git a/engine/client/cl_game.c b/engine/client/cl_game.c index f7e6d1dc..1453d859 100644 --- a/engine/client/cl_game.c +++ b/engine/client/cl_game.c @@ -147,21 +147,6 @@ qboolean CL_IsThirdPerson( void ) return false; } -/* -==================== -CL_GetPlayerInfo - -get player info by render request -==================== -*/ -player_info_t *CL_GetPlayerInfo( int playerIndex ) -{ - if( playerIndex < 0 || playerIndex >= cl.maxclients ) - return NULL; - - return &cl.players[playerIndex]; -} - /* ==================== CL_CreatePlaylist diff --git a/engine/client/ref_common.c b/engine/client/ref_common.c index 57388705..e71dc5ac 100644 --- a/engine/client/ref_common.c +++ b/engine/client/ref_common.c @@ -151,7 +151,7 @@ static player_info_t *pfnPlayerInfo( int index ) if( index == -1 ) // special index for menu return &gameui.playerinfo; - if( index < 0 || index > cl.maxclients ) + if( index < 0 || index >= cl.maxclients ) return NULL; return &cl.players[index]; diff --git a/engine/common/common.h b/engine/common/common.h index f70337e2..2ce2b327 100644 --- a/engine/common/common.h +++ b/engine/common/common.h @@ -748,7 +748,6 @@ void R_ClearAllDecals( void ); void CL_ClearStaticEntities( void ); qboolean S_StreamGetCurrentState( char *currentTrack, char *loopTrack, int *position ); struct cl_entity_s *CL_GetEntityByIndex( int index ); -struct player_info_s *CL_GetPlayerInfo( int playerIndex ); void CL_ServerCommand( qboolean reliable, const char *fmt, ... ) _format( 2 ); void CL_HudMessage( const char *pMessage ); const char *CL_MsgInfo( int cmd ); From 6ef76fe6656f22b2a73a8ad48611f0378f76d9f4 Mon Sep 17 00:00:00 2001 From: Alibek Omarov Date: Sat, 13 Aug 2022 22:33:58 +0300 Subject: [PATCH 018/490] engine: server: fix bots are counted as real players --- engine/server/sv_client.c | 50 ++++++++++++++++++++++++++------------- 1 file changed, 33 insertions(+), 17 deletions(-) diff --git a/engine/server/sv_client.c b/engine/server/sv_client.c index 0e7da563..9cdc9f92 100644 --- a/engine/server/sv_client.c +++ b/engine/server/sv_client.c @@ -42,6 +42,35 @@ typedef struct ucmd_s static int g_userid = 1; +/* +================= +SV_GetPlayerCount + +================= +*/ +static void SV_GetPlayerCount( int *players, int *bots ) +{ + int i; + + *players = 0; + *bots = 0; + + if( !svs.clients ) + return; + + for( i = 0; i < svs.maxclients; i++ ) + { + if( svs.clients[i].state >= cs_connected ) + { + if( FBitSet( svs.clients[i].flags, FCL_FAKECLIENT )) + (*bots)++; + else + (*players)++; + } + + } +} + /* ================= SV_GetChallenge @@ -835,12 +864,10 @@ void SV_Info( netadr_t from, int protocolVersion ) } else { - int i, count = 0; + int i, count, bots; qboolean havePassword = COM_CheckStringEmpty( sv_password.string ); - for( i = 0; i < svs.maxclients; i++ ) - if( svs.clients[i].state >= cs_connected ) - count++; + SV_GetPlayerCount( &count, &bots ); // a1ba: send protocol version to distinguish old engine and new Info_SetValueForKey( string, "p", va( "%i", PROTOCOL_VERSION ), MAX_INFO_STRING ); @@ -2163,22 +2190,11 @@ void SV_TSourceEngineQuery( netadr_t from ) { // A2S_INFO char answer[1024] = ""; - int count = 0, bots = 0; + int count, bots; int index; sizebuf_t buf; - if( svs.clients ) - { - for( index = 0; index < svs.maxclients; index++ ) - { - if( svs.clients[index].state >= cs_connected ) - { - if( FBitSet( svs.clients[index].flags, FCL_FAKECLIENT )) - bots++; - else count++; - } - } - } + SV_GetPlayerCount( &count, &bots ); MSG_Init( &buf, "TSourceEngineQuery", answer, sizeof( answer )); From 6e864e4f8f0a4d23ee19bab8bd4c7fe65d81bf75 Mon Sep 17 00:00:00 2001 From: Alibek Omarov Date: Wed, 10 Aug 2022 06:55:14 +0300 Subject: [PATCH 019/490] engine: introduce bug compatibility levels * for now we only have GoldSrc bug compatibility, can be used for games that require precise GoldSrc behaviour, like CSCZDS * enabled with -bugcomp command line * added text in --help --- engine/common/common.h | 13 +++++++++++++ engine/common/host.c | 15 ++++++++++++--- 2 files changed, 25 insertions(+), 3 deletions(-) diff --git a/engine/common/common.h b/engine/common/common.h index 2ce2b327..092290a8 100644 --- a/engine/common/common.h +++ b/engine/common/common.h @@ -307,6 +307,16 @@ typedef struct float scale; // curstate.scale } tentlist_t; +typedef enum bugcomp_e +{ + // default mode, we assume that user dlls are not relying on engine bugs + BUGCOMP_OFF, + + // GoldSrc mode, user dlls are relying on GoldSrc specific bugs + // but fixing them may break regular Xash games + BUGCOMP_GOLDSRC, +} bugcomp_t; + typedef struct host_parm_s { HINSTANCE hInst; @@ -381,6 +391,9 @@ typedef struct host_parm_s struct decallist_s *decalList; // used for keep decals, when renderer is restarted or changed int numdecals; + // bug compatibility level, for very "special" games + bugcomp_t bugcomp; + } host_parm_t; extern host_parm_t host; diff --git a/engine/common/host.c b/engine/common/host.c index f259cdca..976525f2 100644 --- a/engine/common/host.c +++ b/engine/common/host.c @@ -151,6 +151,8 @@ void Sys_PrintUsage( void ) O("-clientlib ","override client DLL path") #endif O("-rodir ","set read-only base directory, experimental") + O("-bugcomp ","enable precise bug compatibility. Will break games that don't require it") + O(" ","Refer to engine documentation for more info") O("-ip ","set custom ip") O("-port ","set custom host port") @@ -859,10 +861,10 @@ void Host_InitCommon( int argc, char **argv, const char *progname, qboolean bCha if( !Sys_CheckParm( "-disablehelp" ) ) { - if( Sys_CheckParm( "-help" ) || Sys_CheckParm( "-h" ) || Sys_CheckParm( "--help" ) ) - { + if( Sys_CheckParm( "-help" ) || Sys_CheckParm( "-h" ) || Sys_CheckParm( "--help" ) ) + { Sys_PrintUsage(); - } + } } if( !Sys_CheckParm( "-noch" ) ) @@ -941,6 +943,13 @@ void Host_InitCommon( int argc, char **argv, const char *progname, qboolean bCha // 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 From d202a6c572ea0246b30745edf27a04c68c14e74e Mon Sep 17 00:00:00 2001 From: Alibek Omarov Date: Wed, 10 Aug 2022 07:40:38 +0300 Subject: [PATCH 020/490] engine: server: emulate pfnPEntityOfEntIndex bug only with explicitly enabled GoldSrc bug compatibility --- engine/server/sv_game.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/engine/server/sv_game.c b/engine/server/sv_game.c index f3fe6e6e..3d789b79 100644 --- a/engine/server/sv_game.c +++ b/engine/server/sv_game.c @@ -3375,7 +3375,9 @@ pfnPEntityOfEntIndex static edict_t *pfnPEntityOfEntIndex( int iEntIndex ) { // have to be bug-compatible with GoldSrc in this function - return SV_PEntityOfEntIndex( iEntIndex, false ); + if( host.bugcomp == BUGCOMP_GOLDSRC ) + return SV_PEntityOfEntIndex( iEntIndex, false ); + return SV_PEntityOfEntIndex( iEntIndex, true ); } /* From 532cd779a7f7ea0d7a709189c090fe6040185e8f Mon Sep 17 00:00:00 2001 From: Alibek Omarov Date: Wed, 10 Aug 2022 07:41:03 +0300 Subject: [PATCH 021/490] Documentation: document bug compatibility mode --- Documentation/bug-compatibility.md | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) create mode 100644 Documentation/bug-compatibility.md diff --git a/Documentation/bug-compatibility.md b/Documentation/bug-compatibility.md new file mode 100644 index 00000000..27ac9849 --- /dev/null +++ b/Documentation/bug-compatibility.md @@ -0,0 +1,17 @@ +# 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. + +At this time, we only have implemented GoldSrc bug-compatibility. It can be enabled with `-bugcomp` command line switch. + +## 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 From b8dc7494eb01cb4b2762e20910e8617b7c986cee Mon Sep 17 00:00:00 2001 From: Alibek Omarov Date: Tue, 16 Aug 2022 00:03:19 +0300 Subject: [PATCH 022/490] engine: client: move timescale pitch apply to channel mixing --- engine/client/s_main.c | 2 -- engine/client/s_mix.c | 2 ++ 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/engine/client/s_main.c b/engine/client/s_main.c index 6c926552..332c2beb 100644 --- a/engine/client/s_main.c +++ b/engine/client/s_main.c @@ -533,8 +533,6 @@ void S_StartSound( const vec3_t pos, int ent, int chan, sound_t handle, float fv // spatialize memset( target_chan, 0, sizeof( *target_chan )); - pitch *= (sys_timescale.value + 1) / 2; - VectorCopy( pos, target_chan->origin ); target_chan->staticsound = ( ent == 0 ) ? true : false; target_chan->use_loop = (flags & SND_STOP_LOOPING) ? false : true; diff --git a/engine/client/s_mix.c b/engine/client/s_mix.c index 8cceac98..bcc8304c 100644 --- a/engine/client/s_mix.c +++ b/engine/client/s_mix.c @@ -619,6 +619,8 @@ void MIX_MixChannelsToPaintbuffer( int endtime, int rate, int outputRate ) ch->pitch = VOX_ModifyPitch( ch, ch->basePitch * 0.01f ); else ch->pitch = ch->basePitch * 0.01f; + ch->pitch *= ( sys_timescale.value + 1 ) / 2; + if( CL_GetEntityByIndex( ch->entnum ) && ( ch->entchannel == CHAN_VOICE )) { if( pSource->width == 1 ) From 5a5e72c424faed9db9d153525eb7922c8f8bf1a6 Mon Sep 17 00:00:00 2001 From: Alibek Omarov Date: Wed, 17 Aug 2022 14:43:55 +0300 Subject: [PATCH 023/490] engine: print current bug-compatibility level, if enabled --- engine/common/host.c | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/engine/common/host.c b/engine/common/host.c index 976525f2..97fdb923 100644 --- a/engine/common/host.c +++ b/engine/common/host.c @@ -1053,6 +1053,12 @@ void Host_InitCommon( int argc, char **argv, const char *progname, qboolean bCha Sys_InitLog(); + // print bugcompatibility level here, after log was initialized + if( host.bugcomp == BUGCOMP_GOLDSRC ) + { + Con_Printf( "^3BUGCOMP^7: GoldSrc bug-compatibility enabled\n" ); + } + Cmd_AddCommand( "exec", Host_Exec_f, "execute a script file" ); Cmd_AddCommand( "memlist", Host_MemStats_f, "prints memory pool information" ); Cmd_AddRestrictedCommand( "userconfigd", Host_Userconfigd_f, "execute all scripts from userconfig.d" ); From 01a3321d6313454c7b806ddebc072ee1bafba068 Mon Sep 17 00:00:00 2001 From: Alibek Omarov Date: Wed, 17 Aug 2022 21:17:51 +0300 Subject: [PATCH 024/490] engine: client: only accept server list from master servers (thanks @tyabus for idea) --- engine/client/cl_main.c | 6 ++++++ engine/common/common.h | 1 + engine/common/masterlist.c | 33 +++++++++++++++++++++++++++++---- 3 files changed, 36 insertions(+), 4 deletions(-) diff --git a/engine/client/cl_main.c b/engine/client/cl_main.c index e8e9d5f5..4cd0073e 100644 --- a/engine/client/cl_main.c +++ b/engine/client/cl_main.c @@ -2095,6 +2095,12 @@ void CL_ConnectionlessPacket( netadr_t from, sizebuf_t *msg ) } else if( !Q_strcmp( c, "f" )) { + if( !NET_IsMasterAdr( from )) + { + Con_Printf( S_WARN "unexpected server list packet from %s\n", NET_AdrToString( from )); + return; + } + // serverlist got from masterserver while( MSG_GetNumBitsLeft( msg ) > 8 ) { diff --git a/engine/common/common.h b/engine/common/common.h index 092290a8..3099e436 100644 --- a/engine/common/common.h +++ b/engine/common/common.h @@ -868,6 +868,7 @@ void GAME_EXPORT ID_SetCustomClientID( const char *id ); void NET_InitMasters( void ); void NET_SaveMasters( void ); qboolean NET_SendToMasters( netsrc_t sock, size_t len, const void *data ); +qboolean NET_IsMasterAdr( netadr_t adr ); #ifdef REF_DLL #error "common.h in ref_dll" diff --git a/engine/common/masterlist.c b/engine/common/masterlist.c index 192372e7..bb2edcc8 100644 --- a/engine/common/masterlist.c +++ b/engine/common/masterlist.c @@ -21,6 +21,7 @@ typedef struct master_s qboolean sent; qboolean save; string address; + netadr_t adr; // temporary, rewritten after each send } master_t; struct masterlist_s @@ -44,31 +45,32 @@ qboolean NET_SendToMasters( netsrc_t sock, size_t len, const void *data ) for( list = ml.list; list; list = list->next ) { - netadr_t adr; int res; if( list->sent ) continue; - res = NET_StringToAdrNB( list->address, &adr ); + res = NET_StringToAdrNB( list->address, &list->adr ); if( !res ) { Con_Reportf( "Can't resolve adr: %s\n", list->address ); list->sent = true; + list->adr.type = NA_UNUSED; continue; } if( res == 2 ) { list->sent = false; + list->adr.type = NA_UNUSED; wait = true; continue; } list->sent = true; - NET_SendPacket( sock, len, data, adr ); + NET_SendPacket( sock, len, data, list->adr ); } if( !wait ) @@ -85,6 +87,25 @@ qboolean NET_SendToMasters( netsrc_t sock, size_t len, const void *data ) return wait; } +/* +======================== +NET_IsMasterAdr + +======================== +*/ +qboolean NET_IsMasterAdr( netadr_t adr ) +{ + master_t *master; + + for( master = ml.list; master; master = master->next ) + { + if( NET_CompareAdr( adr, master->adr )) + return true; + } + + return false; +} + /* ======================== NET_AddMaster @@ -107,6 +128,7 @@ static void NET_AddMaster( const char *addr, qboolean save ) master->sent = false; master->save = save; master->next = NULL; + master->adr.type = NA_UNUSED; // link in if( last ) @@ -161,7 +183,10 @@ static void NET_ListMasters_f( void ) for( i = 1, list = ml.list; list; i++, list = list->next ) { - Msg( "%d\t%s\n", i, list->address ); + Msg( "%d\t%s", i, list->address ); + if( list->adr.type != NA_UNUSED ) + Msg( "\t%s\n", NET_AdrToString( list->adr )); + else Msg( "\n" ); } } From b3c96374228ed9072300b3a064602f612841a776 Mon Sep 17 00:00:00 2001 From: Alibek Omarov Date: Wed, 17 Aug 2022 21:18:16 +0300 Subject: [PATCH 025/490] engine: server: send server info to all master servers --- engine/server/sv_main.c | 15 +++------------ 1 file changed, 3 insertions(+), 12 deletions(-) diff --git a/engine/server/sv_main.c b/engine/server/sv_main.c index 514a54d4..e030df21 100644 --- a/engine/server/sv_main.c +++ b/engine/server/sv_main.c @@ -714,13 +714,9 @@ Master_Add */ void Master_Add( void ) { - netadr_t adr; - NET_Config( true, false ); // allow remote - - if( !NET_StringToAdr( MASTERSERVER_ADR, &adr )) - Con_Printf( "can't resolve adr: %s\n", MASTERSERVER_ADR ); - else NET_SendPacket( NS_SERVER, 2, "q\xFF", adr ); + if( NET_SendToMasters( NS_SERVER, 2, "q\xFF" )) + svs.last_heartbeat = MAX_HEARTBEAT; } /* @@ -757,13 +753,8 @@ Informs all masters that this server is going down */ void Master_Shutdown( void ) { - netadr_t adr; - NET_Config( true, false ); // allow remote - - if( !NET_StringToAdr( MASTERSERVER_ADR, &adr )) - Con_Printf( "can't resolve addr: %s\n", MASTERSERVER_ADR ); - else NET_SendPacket( NS_SERVER, 2, "\x62\x0A", adr ); + while( NET_SendToMasters( NS_SERVER, 2, "\x62\x0A" )); } /* From 0d2552c3f65b892eeba4eb59ec1bd96776589158 Mon Sep 17 00:00:00 2001 From: Alibek Omarov Date: Wed, 17 Aug 2022 21:23:08 +0300 Subject: [PATCH 026/490] engine: server: prevent DoS through master server query --- engine/server/sv_main.c | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/engine/server/sv_main.c b/engine/server/sv_main.c index e030df21..7f017abe 100644 --- a/engine/server/sv_main.c +++ b/engine/server/sv_main.c @@ -772,6 +772,12 @@ void SV_AddToMaster( netadr_t from, sizebuf_t *msg ) int clients = 0, bots = 0; int len = sizeof( s ); + if( !NET_IsMasterAdr( from )) + { + Con_Printf( S_WARN "unexpected master server info query packet from %s\n", NET_AdrToString( from )); + return; + } + clients = SV_GetConnectedClientsCount( &bots ); challenge = MSG_ReadUBitLong( msg, sizeof( uint ) << 3 ); From 9d4fe707bb3cc250f63d07fff1593ef61dc40b75 Mon Sep 17 00:00:00 2001 From: Alibek Omarov Date: Wed, 17 Aug 2022 21:52:54 +0300 Subject: [PATCH 027/490] engine: client: carefully check legacy server response, check info string before passing it to UI --- engine/client/cl_main.c | 29 +++++++++++++++++++---------- 1 file changed, 19 insertions(+), 10 deletions(-) diff --git a/engine/client/cl_main.c b/engine/client/cl_main.c index 4cd0073e..b49bb695 100644 --- a/engine/client/cl_main.c +++ b/engine/client/cl_main.c @@ -302,7 +302,7 @@ static float CL_LerpPoint( void ) else if( server_frametime > 0.001f ) { // automatic lerp (classic mode) - frac = ( cl.time - cl.mtime[1] ) / server_frametime; + frac = ( cl.time - cl.mtime[1] ) / server_frametime; } #endif return frac; @@ -367,7 +367,7 @@ void CL_ComputeClientInterpolationAmount( usercmd_t *cmd ) min_interp = 1.0f / cl_updaterate->value; interpolation_time = CL_LerpInterval( ); - + if( (cl_interp->value + epsilon) < min_interp ) { Con_Printf( "ex_interp forced up to %.1f msec\n", min_interp * 1000.f ); @@ -1713,16 +1713,23 @@ void CL_ParseStatusMessage( netadr_t from, sizebuf_t *msg ) static char infostring[MAX_INFO_STRING+8]; char *s = MSG_ReadString( msg ); int i; + const char *magic = ": wrong version\n"; + size_t len = Q_strlen( s ), magiclen = Q_strlen( magic ); - CL_FixupColorStringsForInfoString( s, infostring ); - - if( Q_strstr( infostring, "wrong version" ) ) + if( len >= magiclen && !Q_strcmp( s + len - magiclen, magic )) { Netchan_OutOfBandPrint( NS_CLIENT, from, "info %i", PROTOCOL_LEGACY_VERSION ); - Con_Printf( "^1Server^7: %s, Info: %s\n", NET_AdrToString( from ), infostring ); return; } + if( !Info_IsValid( s )) + { + Con_Printf( "^1Server^7: %s, invalid infostring\n", NET_AdrToString( from )); + return; + } + + CL_FixupColorStringsForInfoString( s, infostring ); + if( !COM_CheckString( Info_ValueForKey( infostring, "gamedir" ))) { Con_Printf( "^1Server^7: %s, Info: %s\n", NET_AdrToString( from ), infostring ); @@ -1732,11 +1739,13 @@ void CL_ParseStatusMessage( netadr_t from, sizebuf_t *msg ) if( !COM_CheckString( Info_ValueForKey( infostring, "p" ))) { Info_SetValueForKey( infostring, "legacy", "1", sizeof( infostring ) ); - Con_Print("Legacy: "); + Con_Printf( "^3Server^7: %s, Game: %s\n", NET_AdrToString( from ), Info_ValueForKey( infostring, "gamedir" )); + } + else + { + // more info about servers + Con_Printf( "^2Server^7: %s, Game: %s\n", NET_AdrToString( from ), Info_ValueForKey( infostring, "gamedir" )); } - - // more info about servers - Con_Printf( "^2Server^7: %s, Game: %s\n", NET_AdrToString( from ), Info_ValueForKey( infostring, "gamedir" )); UI_AddServerToList( from, infostring ); } From 2b9e050f57616ee84335224997e7fc34c9566e0a Mon Sep 17 00:00:00 2001 From: Velaron Date: Sun, 9 May 2021 16:32:53 +0300 Subject: [PATCH 028/490] engine: voice support --- .gitmodules | 3 + 3rdparty/opus | 1 + engine/client/cl_main.c | 10 +- engine/client/cl_parse.c | 30 +++- engine/client/s_main.c | 36 +++- engine/client/s_mix.c | 3 + engine/client/s_mouth.c | 65 +++++++ engine/client/sound.h | 7 +- engine/client/voice.c | 234 +++++++++++++++++++++++++ engine/client/voice.h | 56 ++++++ engine/platform/android/snd_opensles.c | 15 ++ engine/platform/platform.h | 3 + engine/platform/sdl/s_sdl.c | 71 ++++++++ engine/platform/stub/s_stub.c | 15 ++ engine/server/sv_client.c | 55 ++++++ engine/server/sv_init.c | 14 ++ engine/server/sv_main.c | 4 + engine/wscript | 6 +- scripts/waifulib/opus.py | 42 +++++ wscript | 5 +- 20 files changed, 664 insertions(+), 11 deletions(-) create mode 160000 3rdparty/opus create mode 100644 engine/client/voice.c create mode 100644 engine/client/voice.h create mode 100644 scripts/waifulib/opus.py diff --git a/.gitmodules b/.gitmodules index d16a4037..dc4d389a 100644 --- a/.gitmodules +++ b/.gitmodules @@ -13,3 +13,6 @@ [submodule "vgui_support"] path = vgui_support url = https://github.com/FWGS/vgui_support +[submodule "opus"] + path = 3rdparty/opus + url = https://github.com/xiph/opus diff --git a/3rdparty/opus b/3rdparty/opus new file mode 160000 index 00000000..dfd6c88a --- /dev/null +++ b/3rdparty/opus @@ -0,0 +1 @@ +Subproject commit dfd6c88aaa54a03a61434c413e30c217eb98f1d5 diff --git a/engine/client/cl_main.c b/engine/client/cl_main.c index b49bb695..172a724b 100644 --- a/engine/client/cl_main.c +++ b/engine/client/cl_main.c @@ -864,6 +864,9 @@ void CL_WritePacket( void ) cl.commands[cls.netchan.outgoing_sequence & CL_UPDATE_MASK].sendsize = MSG_GetNumBytesWritten( &buf ); cl.commands[cls.netchan.outgoing_sequence & CL_UPDATE_MASK].heldback = false; + // send voice data to the server + CL_AddVoiceToDatagram(); + // composite the rest of the datagram.. if( MSG_GetNumBitsWritten( &cls.datagram ) <= MSG_GetNumBitsLeft( &buf )) MSG_WriteBits( &buf, MSG_GetData( &cls.datagram ), MSG_GetNumBitsWritten( &cls.datagram )); @@ -2823,6 +2826,8 @@ void CL_InitLocal( void ) Cvar_RegisterVariable( &cl_logocolor ); Cvar_RegisterVariable( &cl_test_bandwidth ); + Voice_RegisterCvars(); + // register our variables cl_crosshair = Cvar_Get( "crosshair", "1", FCVAR_ARCHIVE, "show weapon chrosshair" ); cl_nodelta = Cvar_Get ("cl_nodelta", "0", 0, "disable delta-compression for server messages" ); @@ -3024,8 +3029,8 @@ void Host_ClientFrame( void ) // a new portion updates from server CL_RedoPrediction (); - // TODO: implement -// Voice_Idle( host.frametime ); + // update voice + Voice_Idle( host.frametime ); // emit visible entities CL_EmitEntities (); @@ -3079,6 +3084,7 @@ void CL_Init( void ) VID_Init(); // init video S_Init(); // init sound + Voice_Init( "opus", 0 ); // init voice // unreliable buffer. unsed for unreliable commands and voice stream MSG_Init( &cls.datagram, "cls.datagram", cls.datagram_buf, sizeof( cls.datagram_buf )); diff --git a/engine/client/cl_parse.c b/engine/client/cl_parse.c index 30f40da7..311656d0 100644 --- a/engine/client/cl_parse.c +++ b/engine/client/cl_parse.c @@ -1685,7 +1685,10 @@ CL_ParseVoiceInit */ void CL_ParseVoiceInit( sizebuf_t *msg ) { - // TODO: ??? + char *pszCodec = MSG_ReadString( msg ); + int quality = MSG_ReadByte( msg ); + + Voice_Init( pszCodec, quality ); } /* @@ -1696,7 +1699,28 @@ CL_ParseVoiceData */ void CL_ParseVoiceData( sizebuf_t *msg ) { - // TODO: ??? + int size, idx, frames; + unsigned char received[8192]; + + idx = MSG_ReadByte( msg ) + 1; + + frames = MSG_ReadByte( msg ); + + size = MSG_ReadShort( msg ); + size = Q_min( size, 8192 ); + + MSG_ReadBytes( msg, received, size ); + + if ( idx <= 0 || idx > cl.maxclients ) + return; + + if ( idx == cl.playernum + 1 ) + Voice_LocalPlayerTalkingAck(); + + if ( !size ) + return; + + Voice_AddIncomingData( idx, received, size, frames ); } /* @@ -2340,6 +2364,7 @@ void CL_ParseServerMessage( sizebuf_t *msg, qboolean normal_message ) break; case svc_voicedata: CL_ParseVoiceData( msg ); + cl.frames[cl.parsecountmod].graphdata.voicebytes += MSG_GetNumBytesRead( msg ) - bufStart; break; case svc_resourcelocation: CL_ParseResLocation( msg ); @@ -3127,6 +3152,7 @@ void CL_ParseLegacyServerMessage( sizebuf_t *msg, qboolean normal_message ) break; case svc_voicedata: CL_ParseVoiceData( msg ); + cl.frames[cl.parsecountmod].graphdata.voicebytes += MSG_GetNumBytesRead( msg ) - bufStart; break; case svc_resourcelocation: CL_ParseResLocation( msg ); diff --git a/engine/client/s_main.c b/engine/client/s_main.c index 332c2beb..272502f9 100644 --- a/engine/client/s_main.c +++ b/engine/client/s_main.c @@ -1127,7 +1127,7 @@ static uint S_RawSamplesStereo( portable_samplepair_t *rawsamples, uint rawend, S_RawEntSamples =================== */ -static void S_RawEntSamples( int entnum, uint samples, uint rate, word width, word channels, const byte *data, int snd_vol ) +void S_RawEntSamples( int entnum, uint samples, uint rate, word width, word channels, const byte *data, int snd_vol ) { rawchan_t *ch; @@ -1286,6 +1286,9 @@ static void S_FreeIdleRawChannels( void ) if( ch->s_rawend >= paintedtime ) continue; + + if ( ch->entnum > 0 ) + SND_ForceCloseMouth( ch->entnum ); if(( paintedtime - ch->s_rawend ) / SOUND_DMA_SPEED >= S_RAW_SOUND_IDLE_SEC ) { @@ -1858,6 +1861,33 @@ void S_SoundInfo_f( void ) S_PrintBackgroundTrackState (); } +/* +================= +S_VoiceRecordStart_f +================= +*/ +void S_VoiceRecordStart_f( void ) +{ + if( cls.state != ca_active ) + return; + + Voice_RecordStart(); +} + +/* +================= +S_VoiceRecordStop_f +================= +*/ +void S_VoiceRecordStop_f( void ) +{ + if( cls.state != ca_active || !Voice_IsRecording() ) + return; + + CL_AddVoiceToDatagram(); + Voice_RecordStop(); +} + /* ================ S_Init @@ -1892,8 +1922,8 @@ qboolean S_Init( void ) Cmd_AddCommand( "soundlist", S_SoundList_f, "display loaded sounds" ); Cmd_AddCommand( "s_info", S_SoundInfo_f, "print sound system information" ); Cmd_AddCommand( "s_fade", S_SoundFade_f, "fade all sounds then stop all" ); - Cmd_AddCommand( "+voicerecord", Cmd_Null_f, "start voice recording (non-implemented)" ); - Cmd_AddCommand( "-voicerecord", Cmd_Null_f, "stop voice recording (non-implemented)" ); + Cmd_AddCommand( "+voicerecord", S_VoiceRecordStart_f, "start voice recording" ); + Cmd_AddCommand( "-voicerecord", S_VoiceRecordStop_f, "stop voice recording" ); Cmd_AddCommand( "spk", S_SayReliable_f, "reliable play a specified sententce" ); Cmd_AddCommand( "speak", S_Say_f, "playing a specified sententce" ); diff --git a/engine/client/s_mix.c b/engine/client/s_mix.c index bcc8304c..721f05fa 100644 --- a/engine/client/s_mix.c +++ b/engine/client/s_mix.c @@ -958,6 +958,9 @@ void MIX_MixRawSamplesBuffer( int end ) pbuf[j-paintedtime].left += ( ch->rawsamples[j & ( ch->max_samples - 1 )].left * ch->leftvol ) >> 8; pbuf[j-paintedtime].right += ( ch->rawsamples[j & ( ch->max_samples - 1 )].right * ch->rightvol ) >> 8; } + + if ( ch->entnum > 0 ) + SND_MoveMouthRaw( ch, &ch->rawsamples[paintedtime & ( ch->max_samples - 1 )], stop - paintedtime ); } } diff --git a/engine/client/s_mouth.c b/engine/client/s_mouth.c index 42acd578..3b93c8c8 100644 --- a/engine/client/s_mouth.c +++ b/engine/client/s_mouth.c @@ -150,3 +150,68 @@ void SND_MoveMouth16( channel_t *ch, wavdata_t *pSource, int count ) pMouth->sndcount = 0; } } + +void SND_ForceInitMouth( int entnum ) +{ + cl_entity_t *clientEntity; + + clientEntity = CL_GetEntityByIndex( entnum ); + + if ( clientEntity ) + { + clientEntity->mouth.mouthopen = 0; + clientEntity->mouth.sndavg = 0; + clientEntity->mouth.sndcount = 0; + } +} + +void SND_ForceCloseMouth( int entnum ) +{ + cl_entity_t *clientEntity; + + clientEntity = CL_GetEntityByIndex( entnum ); + + if ( clientEntity ) + clientEntity->mouth.mouthopen = 0; +} + +void SND_MoveMouthRaw( rawchan_t *ch, portable_samplepair_t *pData, int count ) +{ + cl_entity_t *clientEntity; + mouth_t *pMouth = NULL; + int savg, data; + int scount = 0; + uint i; + + clientEntity = CL_GetEntityByIndex( ch->entnum ); + if( !clientEntity ) return; + + pMouth = &clientEntity->mouth; + + if( pData == NULL ) + return; + + i = 0; + scount = pMouth->sndcount; + savg = 0; + + while ( i < count && scount < CAVGSAMPLES ) + { + data = pData[i].left; // mono sound anyway + data = ( bound( -32767, data, 0x7ffe ) >> 8 ); + savg += abs( data ); + + i += 80 + ( (byte)data & 0x1F ); + scount++; + } + + pMouth->sndavg += savg; + pMouth->sndcount = (byte)scount; + + if ( pMouth->sndcount >= CAVGSAMPLES ) + { + pMouth->mouthopen = pMouth->sndavg / CAVGSAMPLES; + pMouth->sndavg = 0; + pMouth->sndcount = 0; + } +} \ No newline at end of file diff --git a/engine/client/sound.h b/engine/client/sound.h index 930d114d..326f842b 100644 --- a/engine/client/sound.h +++ b/engine/client/sound.h @@ -27,6 +27,7 @@ extern poolhandle_t sndpool; #define SOUND_22k 22050 // 22khz sample rate #define SOUND_32k 32000 // 32khz sample rate #define SOUND_44k 44100 // 44khz sample rate +#define SOUND_48k 48000 // 48khz sample rate #define DMA_MSEC_PER_SAMPLE ((float)(1000.0 / SOUND_DMA_SPEED)) // fixed point stuff for real-time resampling @@ -202,7 +203,7 @@ typedef struct #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 16 +#define MAX_RAW_CHANNELS 48 #define MAX_RAW_SAMPLES 8192 extern sound_t ambient_sfx[NUM_AMBIENTS]; @@ -271,6 +272,7 @@ int S_GetCurrentStaticSounds( soundlist_t *pout, int size ); int S_GetCurrentDynamicSounds( soundlist_t *pout, int size ); sfx_t *S_GetSfxByHandle( sound_t handle ); rawchan_t *S_FindRawChannel( int entnum, qboolean create ); +void S_RawEntSamples( int entnum, uint samples, uint rate, word width, word channels, const byte *data, int snd_vol ); void S_RawSamples( uint samples, uint rate, word width, word channels, const byte *data, int entnum ); void S_StopSound( int entnum, int channel, const char *soundname ); void S_UpdateFrame( struct ref_viewpass_s *rvp ); @@ -283,9 +285,12 @@ void S_FreeSounds( void ); // s_mouth.c // void SND_InitMouth( int entnum, int entchannel ); +void SND_ForceInitMouth( int entnum ); void SND_MoveMouth8( channel_t *ch, wavdata_t *pSource, int count ); void SND_MoveMouth16( channel_t *ch, wavdata_t *pSource, int count ); +void SND_MoveMouthRaw( rawchan_t *ch, portable_samplepair_t *pData, int count ); void SND_CloseMouth( channel_t *ch ); +void SND_ForceCloseMouth( int entnum ); // // s_stream.c diff --git a/engine/client/voice.c b/engine/client/voice.c new file mode 100644 index 00000000..c0644b2c --- /dev/null +++ b/engine/client/voice.c @@ -0,0 +1,234 @@ +#include "voice.h" + +wavdata_t *input_file; +fs_offset_t input_pos; + +voice_state_t voice; + +CVAR_DEFINE_AUTO( voice_enable, "1", FCVAR_ARCHIVE, "enable voice chat" ); +CVAR_DEFINE_AUTO( voice_loopback, "0", 0, "loopback voice back to the speaker" ); +CVAR_DEFINE_AUTO( voice_scale, "1.0", FCVAR_ARCHIVE, "incoming voice volume scale" ); +CVAR_DEFINE_AUTO( voice_inputfromfile, "0", 0, "input voice from voice_input.wav" ); + +void Voice_RegisterCvars( void ) +{ + Cvar_RegisterVariable( &voice_enable ); + Cvar_RegisterVariable( &voice_loopback ); + Cvar_RegisterVariable( &voice_scale ); + Cvar_RegisterVariable( &voice_inputfromfile ); +} + +static void Voice_Status( int entindex, qboolean bTalking ) +{ + clgame.dllFuncs.pfnVoiceStatus( entindex, bTalking ); +} + +// parameters currently unused +qboolean Voice_Init( const char *pszCodecName, int quality ) +{ + int err; + + if ( !voice_enable.value ) + return false; + + Voice_DeInit(); + + voice.was_init = true; + + voice.channels = 1; + voice.width = 2; + voice.samplerate = SOUND_48k; + voice.frame_size = voice.channels * ( (float)voice.samplerate / ( 1000.0f / 20.0f ) ) * voice.width; + + if ( !VoiceCapture_Init() ) + { + Voice_DeInit(); + return false; + } + + voice.encoder = opus_encoder_create( voice.samplerate, voice.channels, OPUS_APPLICATION_VOIP, &err ); + voice.decoder = opus_decoder_create( voice.samplerate, voice.channels, &err ); + + return true; +} + +void Voice_DeInit( void ) +{ + if ( !voice.was_init ) + return; + + Voice_RecordStop(); + + opus_encoder_destroy( voice.encoder ); + opus_decoder_destroy( voice.decoder ); + + voice.was_init = false; +} + +uint Voice_GetCompressedData( byte *out, uint maxsize, uint *frames ) +{ + uint ofs, size = 0; + + if ( input_file ) + { + uint numbytes; + double time; + + time = Sys_DoubleTime(); + + numbytes = ( time - voice.start_time ) * voice.samplerate; + numbytes = Q_min( numbytes, input_file->size - input_pos ); + numbytes = Q_min( numbytes, sizeof( voice.buffer ) - voice.buffer_pos ); + + memcpy( voice.buffer + voice.buffer_pos, input_file->buffer + input_pos, numbytes ); + voice.buffer_pos += numbytes; + input_pos += numbytes; + + voice.start_time = time; + } + + for ( ofs = 0; voice.buffer_pos - ofs >= voice.frame_size && ofs <= voice.buffer_pos; ofs += voice.frame_size ) + { + int bytes; + + bytes = opus_encode( voice.encoder, (const opus_int16*)(voice.buffer + ofs), voice.frame_size / voice.width, out + size, maxsize ); + memmove( voice.buffer, voice.buffer + voice.frame_size, sizeof( voice.buffer ) - voice.frame_size ); + voice.buffer_pos -= voice.frame_size; + + if ( bytes > 0 ) + { + size += bytes; + (*frames)++; + } + } + + return size; +} + +void Voice_Idle( float frametime ) +{ + if ( !voice_enable.value ) + { + Voice_DeInit(); + return; + } + + if ( voice.talking_ack ) + { + voice.talking_timeout += frametime; + + if ( voice.talking_timeout > 0.2f ) + { + voice.talking_ack = false; + Voice_Status( -2, false ); + } + } +} + +qboolean Voice_IsRecording( void ) +{ + return voice.is_recording; +} + +void Voice_RecordStop( void ) +{ + if ( input_file ) + { + FS_FreeSound( input_file ); + input_file = NULL; + } + + voice.buffer_pos = 0; + memset( voice.buffer, 0, sizeof( voice.buffer ) ); + + if ( Voice_IsRecording() ) + Voice_Status( -1, false ); + + VoiceCapture_RecordStop(); + + voice.is_recording = false; +} + +void Voice_RecordStart( void ) +{ + Voice_RecordStop(); + + if ( voice_inputfromfile.value ) + { + input_file = FS_LoadSound( "voice_input.wav", NULL, 0 ); + + if ( input_file ) + { + Sound_Process( &input_file, voice.samplerate, voice.width, SOUND_RESAMPLE ); + input_pos = 0; + + voice.start_time = Sys_DoubleTime(); + voice.is_recording = true; + } + else + { + FS_FreeSound( input_file ); + input_file = NULL; + } + } + + if ( !Voice_IsRecording() ) + voice.is_recording = VoiceCapture_RecordStart(); + + if ( Voice_IsRecording() ) + Voice_Status( -1, true ); +} + +void Voice_AddIncomingData( int ent, byte *data, uint size, uint frames ) +{ + byte decompressed[MAX_RAW_SAMPLES]; + int samples; + + samples = opus_decode( voice.decoder, (const byte*)data, size, (short *)decompressed, voice.frame_size / voice.width * frames, false ); + + if ( samples > 0 ) + Voice_StartChannel( samples, decompressed, ent ); +} + +void CL_AddVoiceToDatagram( void ) +{ + uint size, frames = 0; + byte data[MAX_RAW_SAMPLES]; + + if ( cls.state != ca_active || !Voice_IsRecording() ) + return; + + size = Voice_GetCompressedData( data, sizeof( data ), &frames ); + + if ( size > 0 && MSG_GetNumBytesLeft( &cls.datagram ) >= size + 32 ) + { + MSG_BeginClientCmd( &cls.datagram, clc_voicedata ); + MSG_WriteByte( &cls.datagram, Voice_GetLoopback() ); + MSG_WriteByte( &cls.datagram, frames ); + MSG_WriteShort( &cls.datagram, size ); + MSG_WriteBytes( &cls.datagram, data, size ); + } +} + +qboolean Voice_GetLoopback( void ) +{ + return voice_loopback.value; +} + +void Voice_LocalPlayerTalkingAck( void ) +{ + if ( !voice.talking_ack ) + { + Voice_Status( -2, true ); + } + + voice.talking_ack = true; + voice.talking_timeout = 0.0f; +} + +void Voice_StartChannel( uint samples, byte *data, int entnum ) +{ + SND_ForceInitMouth( entnum ); + Voice_Status( entnum, true ); + S_RawEntSamples( entnum, samples, voice.samplerate, voice.width, voice.channels, data, 128.0f * voice_scale.value ); +} \ No newline at end of file diff --git a/engine/client/voice.h b/engine/client/voice.h new file mode 100644 index 00000000..441d9c9d --- /dev/null +++ b/engine/client/voice.h @@ -0,0 +1,56 @@ +#ifndef VOICE_H +#define VOICE_H + +#include + +#include "common.h" +#include "client.h" +#include "sound.h" +#include "soundlib/soundlib.h" +#include "library.h" + +#define SAMPLES_PER_SEC ( SOUND_48k / BYTES_PER_SAMPLE ) + +extern convar_t voice_scale; + +typedef struct voice_state_s +{ + qboolean was_init; + qboolean is_recording; + float start_time; + qboolean talking_ack; + float talking_timeout; + + // opus stuff + OpusEncoder *encoder; + OpusDecoder *decoder; + + // audio info + uint channels; + uint width; + uint samplerate; + uint frame_size; + + // input buffer + byte buffer[MAX_RAW_SAMPLES]; + fs_offset_t buffer_pos; +} voice_state_t; + +extern voice_state_t voice; + +void CL_AddVoiceToDatagram( void ); + +void Voice_RegisterCvars( void ); +qboolean Voice_Init( const char *pszCodecName, int quality ); +void Voice_DeInit( void ); +uint Voice_GetCompressedData( byte *out, uint maxsize, uint *frames ); +void Voice_Idle( float frametime ); +qboolean Voice_IsRecording( void ); +void Voice_RecordStop( void ); +void Voice_RecordStart( void ); +void Voice_AddIncomingData( int ent, byte *data, uint size, uint frames ); +qboolean Voice_GetLoopback( void ); +void Voice_LocalPlayerTalkingAck( void ); +void Voice_StartChannel( uint samples, byte *data, int entnum ); + +#endif // VOICE_H \ No newline at end of file diff --git a/engine/platform/android/snd_opensles.c b/engine/platform/android/snd_opensles.c index 012d9313..056ebd62 100644 --- a/engine/platform/android/snd_opensles.c +++ b/engine/platform/android/snd_opensles.c @@ -254,4 +254,19 @@ void SNDDMA_BeginPainting( void ) { pthread_mutex_lock( &snddma_android_mutex ); } + +qboolean VoiceCapture_Init( void ) +{ + return false; +} + +qboolean VoiceCapture_RecordStart( void ) +{ + return false; +} + +void VoiceCapture_RecordStop( void ) +{ + return 0; +} #endif diff --git a/engine/platform/platform.h b/engine/platform/platform.h index e764421a..51c5c3fd 100644 --- a/engine/platform/platform.h +++ b/engine/platform/platform.h @@ -161,5 +161,8 @@ void SNDDMA_Activate( qboolean active ); // pause audio // void SNDDMA_PrintDeviceName( void ); // unused // void SNDDMA_LockSound( void ); // unused // void SNDDMA_UnlockSound( void ); // unused +qboolean VoiceCapture_Init( void ); +qboolean VoiceCapture_RecordStart( void ); +void VoiceCapture_RecordStop( void ); #endif // PLATFORM_H diff --git a/engine/platform/sdl/s_sdl.c b/engine/platform/sdl/s_sdl.c index 8763969c..95edfcc1 100644 --- a/engine/platform/sdl/s_sdl.c +++ b/engine/platform/sdl/s_sdl.c @@ -18,6 +18,7 @@ GNU General Public License for more details. #if XASH_SOUND == SOUND_SDL #include "sound.h" +#include "voice.h" #include @@ -43,6 +44,8 @@ so it can unlock and free the data block after it has been played. ======================================================================= */ static int sdl_dev; +static SDL_AudioDeviceID in_dev; +static SDL_AudioFormat sdl_format; //static qboolean snd_firsttime = true; //static qboolean primary_format_set; @@ -133,6 +136,8 @@ qboolean SNDDMA_Init( void ) dma.buffer = Z_Calloc( dma.samples * 2 ); dma.samplepos = 0; + sdl_format = obtained.format; + Con_Printf( "Using SDL audio driver: %s @ %d Hz\n", SDL_GetCurrentAudioDriver( ), obtained.freq ); dma.initialized = true; @@ -220,4 +225,70 @@ void SNDDMA_Activate( qboolean active ) SDL_PauseAudioDevice( sdl_dev, !active ); } + +/* +=========== +SDL_SoundInputCallback +=========== +*/ +void SDL_SoundInputCallback( void *userdata, Uint8 *stream, int len ) +{ + int size; + + size = Q_min( len, sizeof( voice.buffer ) - voice.buffer_pos ); + SDL_memset( voice.buffer + voice.buffer_pos, 0, size ); + SDL_MixAudioFormat( voice.buffer + voice.buffer_pos, stream, sdl_format, size, SDL_MIX_MAXVOLUME ); + voice.buffer_pos += size; +} + +/* +=========== +VoiceCapture_Init +=========== +*/ +qboolean VoiceCapture_Init( void ) +{ + SDL_AudioSpec wanted, spec; + + SDL_zero( wanted ); + wanted.freq = voice.samplerate; + wanted.format = AUDIO_S16LSB; + wanted.channels = voice.channels; + wanted.samples = voice.frame_size / voice.width; + wanted.callback = SDL_SoundInputCallback; + + in_dev = SDL_OpenAudioDevice( NULL, SDL_TRUE, &wanted, &spec, 0 ); + + if( SDLash_IsAudioError( in_dev ) ) + { + Con_Printf( "VoiceCapture_Init: error creating capture device (%s)\n", SDL_GetError() ); + return false; + } + + Con_Printf( S_NOTE "VoiceCapture_Init: capture device creation success (%i: %s)\n", in_dev, SDL_GetAudioDeviceName( in_dev, SDL_TRUE ) ); + return true; +} + +/* +=========== +VoiceCapture_RecordStart +=========== +*/ +qboolean VoiceCapture_RecordStart( void ) +{ + SDL_PauseAudioDevice( in_dev, SDL_FALSE ); + + return true; +} + +/* +=========== +VoiceCapture_RecordStop +=========== +*/ +void VoiceCapture_RecordStop( void ) +{ + SDL_PauseAudioDevice( in_dev, SDL_TRUE ); +} + #endif // XASH_SOUND == SOUND_SDL diff --git a/engine/platform/stub/s_stub.c b/engine/platform/stub/s_stub.c index 509ae9b7..c3855dd6 100644 --- a/engine/platform/stub/s_stub.c +++ b/engine/platform/stub/s_stub.c @@ -94,5 +94,20 @@ void SNDDMA_Shutdown( void ) } } +qboolean VoiceCapture_Init( void ) +{ + return false; +} + +qboolean VoiceCapture_RecordStart( void ) +{ + return false; +} + +void VoiceCapture_RecordStop( void ) +{ + return 0; +} + #endif #endif diff --git a/engine/server/sv_client.c b/engine/server/sv_client.c index 9cdc9f92..3ab3c04c 100644 --- a/engine/server/sv_client.c +++ b/engine/server/sv_client.c @@ -2561,6 +2561,58 @@ void SV_ParseCvarValue2( sv_client_t *cl, sizebuf_t *msg ) Con_Reportf( "Cvar query response: name:%s, request ID %d, cvar:%s, value:%s\n", cl->name, requestID, name, value ); } +/* +=================== +SV_ParseVoiceData +=================== +*/ +void SV_ParseVoiceData( sv_client_t *cl, sizebuf_t *msg ) +{ + char received[4096]; + sv_client_t *cur; + int i, client; + uint length, size, frames; + + cl->m_bLoopback = MSG_ReadByte( msg ); + + frames = MSG_ReadByte( msg ); + + size = MSG_ReadShort( msg ); + client = cl - svs.clients; + + if ( size > sizeof( received ) ) + { + Con_DPrintf( "SV_ParseVoiceData: invalid incoming packet.\n" ); + SV_DropClient( cl, false ); + return; + } + + if ( !Cvar_VariableInteger( "sv_voiceenable" ) ) + return; + + MSG_ReadBytes( msg, received, size ); + + for( i = 0, cur = svs.clients; i < svs.maxclients; i++, cur++ ) + { + if ( cur->state < cs_connected && cl != cur ) + continue; + + length = size; + + if ( MSG_GetNumBytesLeft( &cur->datagram ) < length + 6 ) + continue; + + if ( cl == cur && !cur->m_bLoopback ) + length = 0; + + MSG_BeginServerCmd( &cur->datagram, svc_voicedata ); + MSG_WriteByte( &cur->datagram, client ); + MSG_WriteByte( &cur->datagram, frames ); + MSG_WriteShort( &cur->datagram, length ); + MSG_WriteBytes( &cur->datagram, received, length ); + } +} + /* =================== SV_ExecuteClientMessage @@ -2631,6 +2683,9 @@ void SV_ExecuteClientMessage( sv_client_t *cl, sizebuf_t *msg ) case clc_fileconsistency: SV_ParseConsistencyResponse( cl, msg ); break; + case clc_voicedata: + SV_ParseVoiceData( cl, msg ); + break; case clc_requestcvarvalue: SV_ParseCvarValue( cl, msg ); break; diff --git a/engine/server/sv_init.c b/engine/server/sv_init.c index 8f55a25e..d77f21a1 100644 --- a/engine/server/sv_init.c +++ b/engine/server/sv_init.c @@ -386,6 +386,18 @@ void SV_CreateResourceList( void ) } } +/* +================ +SV_WriteVoiceCodec +================ +*/ +void SV_WriteVoiceCodec( sizebuf_t *msg ) +{ + MSG_BeginServerCmd( msg, svc_voiceinit ); + MSG_WriteString( msg, "opus" ); + MSG_WriteByte( msg, 0 ); +} + /* ================ SV_CreateBaseline @@ -404,6 +416,8 @@ void SV_CreateBaseline( void ) int delta_type; int entnum; + SV_WriteVoiceCodec( &sv.signon ); + if( FBitSet( host.features, ENGINE_QUAKE_COMPATIBLE )) playermodel = SV_ModelIndex( DEFAULT_PLAYER_PATH_QUAKE ); else playermodel = SV_ModelIndex( DEFAULT_PLAYER_PATH_HALFLIFE ); diff --git a/engine/server/sv_main.c b/engine/server/sv_main.c index 7f017abe..bae1f522 100644 --- a/engine/server/sv_main.c +++ b/engine/server/sv_main.c @@ -112,6 +112,9 @@ CVAR_DEFINE_AUTO( violence_ablood, "1", 0, "draw alien blood" ); CVAR_DEFINE_AUTO( violence_hgibs, "1", 0, "show human gib entities" ); CVAR_DEFINE_AUTO( violence_agibs, "1", 0, "show alien gib entities" ); +// voice chat +CVAR_DEFINE_AUTO( sv_voiceenable, "1", FCVAR_ARCHIVE|FCVAR_SERVER, "enable voice support" ); + convar_t *sv_novis; // disable server culling entities by vis convar_t *sv_pausable; convar_t *timeout; // seconds without any message @@ -974,6 +977,7 @@ void SV_Init( void ) Cvar_RegisterVariable( &listipcfgfile ); Cvar_RegisterVariable( &mapchangecfgfile ); + Cvar_RegisterVariable( &sv_voiceenable ); Cvar_RegisterVariable( &sv_trace_messages ); sv_allow_joystick = Cvar_Get( "sv_allow_joystick", "1", FCVAR_ARCHIVE, "allow connect with joystick enabled" ); diff --git a/engine/wscript b/engine/wscript index 972d8804..23ea25bb 100644 --- a/engine/wscript +++ b/engine/wscript @@ -35,7 +35,7 @@ def options(opt): grp.add_option('--enable-engine-fuzz', action = 'store_true', dest = 'ENGINE_FUZZ', default = False, help = 'add LLVM libFuzzer [default: %default]' ) - opt.load('sdl2') + opt.load('sdl2 opus') def configure(conf): # check for dedicated server build @@ -65,7 +65,7 @@ def configure(conf): else: conf.load('sdl2') if not conf.env.HAVE_SDL2: - conf.fatal('SDL2 not availiable! If you want to build dedicated server, specify --dedicated') + conf.fatal('SDL2 not available! If you want to build dedicated server, specify --dedicated') conf.define('XASH_SDL', 2) if conf.env.DEST_OS == 'haiku': @@ -165,6 +165,8 @@ def build(bld): 'client/*.c', 'client/vgui/*.c', 'client/avi/*.c']) + + libs.append('OPUS') includes = ['common', 'server', 'client', 'client/vgui', 'tests', '.', '../public', '../common', '../filesystem', '../pm_shared' ] diff --git a/scripts/waifulib/opus.py b/scripts/waifulib/opus.py new file mode 100644 index 00000000..d1ef2a07 --- /dev/null +++ b/scripts/waifulib/opus.py @@ -0,0 +1,42 @@ +# encoding: utf-8 + +import os + +def options(opt): + pass + +def configure(conf): + path = conf.path.find_dir('3rdparty/opus') + conf.env.LIB_OPUS = ['opus'] + conf.env.INCLUDES_OPUS = [path.find_dir('include/').abspath()] + +def build(bld): + path = bld.path.find_dir('3rdparty/opus') + + sources = path.ant_glob([ + 'src/*.c', + 'celt/*.c', + 'silk/*.c', + 'silk/float/*.c']) + + includes = [ + path.find_dir('include/'), + path.find_dir('celt/'), + path.find_dir('silk/'), + path.find_dir('silk/float/') + ] + + defines = [ + 'USE_ALLOCA', + 'OPUS_BUILD', + 'PACKAGE_VERSION="1.3.1"' + ] + + bld.stlib( + source = sources, + target = 'opus', + features = 'c', + includes = includes, + defines = defines, + subsystem = bld.env.MSVC_SUBSYSTEM + ) \ No newline at end of file diff --git a/wscript b/wscript index 07e49635..2516c722 100644 --- a/wscript +++ b/wscript @@ -353,9 +353,12 @@ int main(int argc, char **argv) { strcasestr(argv[1], argv[2]); return 0; }''' continue conf.add_subproject(i.name) + + conf.load('opus') def build(bld): - bld.load('xshlib') + bld.load('opus xshlib') + for i in SUBDIRS: if not i.is_enabled(bld): continue From 9242a0a510972e9fcf95323bc3b0b56026cf18b6 Mon Sep 17 00:00:00 2001 From: Velaron Date: Sun, 23 May 2021 17:25:03 +0300 Subject: [PATCH 029/490] engine: update voice --- engine/client/cl_main.c | 1 + engine/client/cl_view.c | 3 --- engine/client/voice.h | 2 -- 3 files changed, 1 insertion(+), 5 deletions(-) diff --git a/engine/client/cl_main.c b/engine/client/cl_main.c index 172a724b..74e95673 100644 --- a/engine/client/cl_main.c +++ b/engine/client/cl_main.c @@ -22,6 +22,7 @@ GNU General Public License for more details. #include "vgui_draw.h" #include "library.h" #include "vid_common.h" +#include "voice.h" #define MAX_TOTAL_CMDS 32 #define MAX_CMD_BUFFER 8000 diff --git a/engine/client/cl_view.c b/engine/client/cl_view.c index 310d3aac..cac27075 100644 --- a/engine/client/cl_view.c +++ b/engine/client/cl_view.c @@ -286,9 +286,6 @@ qboolean V_PreRender( void ) if( !ref.initialized ) return false; - if( host.status == HOST_NOFOCUS ) - return false; - if( host.status == HOST_SLEEP ) return false; diff --git a/engine/client/voice.h b/engine/client/voice.h index 441d9c9d..b3fd162a 100644 --- a/engine/client/voice.h +++ b/engine/client/voice.h @@ -9,8 +9,6 @@ #include "soundlib/soundlib.h" #include "library.h" -#define SAMPLES_PER_SEC ( SOUND_48k / BYTES_PER_SAMPLE ) - extern convar_t voice_scale; typedef struct voice_state_s From 44cd03f4643062be4abd5d05c25e2d2c0bf2ed15 Mon Sep 17 00:00:00 2001 From: Alibek Omarov Date: Mon, 14 Jun 2021 21:19:52 +0300 Subject: [PATCH 030/490] wscript: refactor opus wscript files, now it builds only if system package wasn't found --- 3rdparty/opus | 1 - 3rdparty/opus/opus | 1 + 3rdparty/opus/wscript | 52 ++++++++++++++++++++++++++++++++++++++++ engine/wscript | 2 +- scripts/waifulib/opus.py | 42 -------------------------------- wscript | 5 ++-- 6 files changed, 56 insertions(+), 47 deletions(-) delete mode 160000 3rdparty/opus create mode 160000 3rdparty/opus/opus create mode 100644 3rdparty/opus/wscript delete mode 100644 scripts/waifulib/opus.py diff --git a/3rdparty/opus b/3rdparty/opus deleted file mode 160000 index dfd6c88a..00000000 --- a/3rdparty/opus +++ /dev/null @@ -1 +0,0 @@ -Subproject commit dfd6c88aaa54a03a61434c413e30c217eb98f1d5 diff --git a/3rdparty/opus/opus b/3rdparty/opus/opus new file mode 160000 index 00000000..16395923 --- /dev/null +++ b/3rdparty/opus/opus @@ -0,0 +1 @@ +Subproject commit 1639592368fc2dadc82d63f3be6f17ed0b554d71 diff --git a/3rdparty/opus/wscript b/3rdparty/opus/wscript new file mode 100644 index 00000000..8f1fe0ca --- /dev/null +++ b/3rdparty/opus/wscript @@ -0,0 +1,52 @@ +#! /usr/bin/env python +# encoding: utf-8 + +import os + +OPUS_CHECK='''#include +#include +int main() +{ + OpusEncoder *oe = opus_encoder_create(48000, 2, OPUS_APPLICATION_VOIP, NULL); + OpusDecoder *od = opus_decoder_create(48000, 2, NULL); + return !oe || !od; +} +''' + +def options(opt): + pass + +def configure(conf): + if conf.check_pkg('opus', 'OPUS', OPUS_CHECK, fatal = False): + conf.env.HAVE_OPUS = True + return + + if not conf.path.find_dir('opus') or not conf.path.find_dir('opus/src'): + conf.fatal('Can\'t find opus submodule. Run `git submodule update --init --recursive`.') + return + + # TODO: ARM/x86 intrinsics detection + # TODO: maybe call autotools/cmake/meson instead? + +def build(bld): + if bld.env.HAVE_OPUS: + return + + sources = bld.path.ant_glob([ + 'opus/src/*.c', + 'opus/celt/*.c', + 'opus/silk/*.c', + 'opus/silk/float/*.c' + ]) + includes = ['opus/include/', 'opus/celt/', 'opus/silk/', 'opus/silk/float/'] + defines = ['USE_ALLOCA', 'OPUS_BUILD', 'FLOAT_APPROX', 'PACKAGE_VERSION="1.3.1"'] + + bld.stlib( + source = sources, + target = 'opus', + features = 'c', + includes = includes, + defines = defines, + subsystem = bld.env.MSVC_SUBSYSTEM, + export_includes = ['opus/include/'] + ) diff --git a/engine/wscript b/engine/wscript index 23ea25bb..2fe54df0 100644 --- a/engine/wscript +++ b/engine/wscript @@ -166,7 +166,7 @@ def build(bld): 'client/vgui/*.c', 'client/avi/*.c']) - libs.append('OPUS') + libs.append('opus') includes = ['common', 'server', 'client', 'client/vgui', 'tests', '.', '../public', '../common', '../filesystem', '../pm_shared' ] diff --git a/scripts/waifulib/opus.py b/scripts/waifulib/opus.py deleted file mode 100644 index d1ef2a07..00000000 --- a/scripts/waifulib/opus.py +++ /dev/null @@ -1,42 +0,0 @@ -# encoding: utf-8 - -import os - -def options(opt): - pass - -def configure(conf): - path = conf.path.find_dir('3rdparty/opus') - conf.env.LIB_OPUS = ['opus'] - conf.env.INCLUDES_OPUS = [path.find_dir('include/').abspath()] - -def build(bld): - path = bld.path.find_dir('3rdparty/opus') - - sources = path.ant_glob([ - 'src/*.c', - 'celt/*.c', - 'silk/*.c', - 'silk/float/*.c']) - - includes = [ - path.find_dir('include/'), - path.find_dir('celt/'), - path.find_dir('silk/'), - path.find_dir('silk/float/') - ] - - defines = [ - 'USE_ALLOCA', - 'OPUS_BUILD', - 'PACKAGE_VERSION="1.3.1"' - ] - - bld.stlib( - source = sources, - target = 'opus', - features = 'c', - includes = includes, - defines = defines, - subsystem = bld.env.MSVC_SUBSYSTEM - ) \ No newline at end of file diff --git a/wscript b/wscript index 2516c722..9b060a4c 100644 --- a/wscript +++ b/wscript @@ -54,6 +54,7 @@ class Subproject: return True SUBDIRS = [ + Subproject('3rdparty/opus'), Subproject('public', dedicated=False, mandatory = True), Subproject('filesystem', dedicated=False, mandatory = True), Subproject('game_launch', singlebin=True), @@ -353,11 +354,9 @@ int main(int argc, char **argv) { strcasestr(argv[1], argv[2]); return 0; }''' continue conf.add_subproject(i.name) - - conf.load('opus') def build(bld): - bld.load('opus xshlib') + bld.load('xshlib') for i in SUBDIRS: if not i.is_enabled(bld): From 69a9211fc972e5f2f6588787001bfdd02178ada2 Mon Sep 17 00:00:00 2001 From: Alibek Omarov Date: Mon, 14 Jun 2021 21:21:28 +0300 Subject: [PATCH 031/490] engine: include voice.h globally --- engine/client/client.h | 1 + 1 file changed, 1 insertion(+) diff --git a/engine/client/client.h b/engine/client/client.h index fc4f9a9c..c726b1c9 100644 --- a/engine/client/client.h +++ b/engine/client/client.h @@ -33,6 +33,7 @@ GNU General Public License for more details. #include "net_api.h" #include "world.h" #include "ref_common.h" +#include "voice.h" // client sprite types #define SPR_CLIENT 0 // client sprite for temp-entities or user-textures From 0db95d76a8c0315420f291cc4453340ae05971b5 Mon Sep 17 00:00:00 2001 From: SNMetamorph <25657591+SNMetamorph@users.noreply.github.com> Date: Mon, 15 Aug 2022 08:33:18 +0400 Subject: [PATCH 032/490] engine: wscript: removed opus from opt.load() --- engine/wscript | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/engine/wscript b/engine/wscript index 2fe54df0..b91d79f2 100644 --- a/engine/wscript +++ b/engine/wscript @@ -35,7 +35,7 @@ def options(opt): grp.add_option('--enable-engine-fuzz', action = 'store_true', dest = 'ENGINE_FUZZ', default = False, help = 'add LLVM libFuzzer [default: %default]' ) - opt.load('sdl2 opus') + opt.load('sdl2') def configure(conf): # check for dedicated server build From a254a342b1253922b72a295408602345a5b46272 Mon Sep 17 00:00:00 2001 From: SNMetamorph <25657591+SNMetamorph@users.noreply.github.com> Date: Mon, 15 Aug 2022 08:37:53 +0400 Subject: [PATCH 033/490] engine: voice: minor code fixes --- engine/client/cl_main.c | 1 - engine/platform/android/snd_opensles.c | 2 +- engine/platform/stub/s_stub.c | 2 +- 3 files changed, 2 insertions(+), 3 deletions(-) diff --git a/engine/client/cl_main.c b/engine/client/cl_main.c index 74e95673..172a724b 100644 --- a/engine/client/cl_main.c +++ b/engine/client/cl_main.c @@ -22,7 +22,6 @@ GNU General Public License for more details. #include "vgui_draw.h" #include "library.h" #include "vid_common.h" -#include "voice.h" #define MAX_TOTAL_CMDS 32 #define MAX_CMD_BUFFER 8000 diff --git a/engine/platform/android/snd_opensles.c b/engine/platform/android/snd_opensles.c index 056ebd62..0568ee61 100644 --- a/engine/platform/android/snd_opensles.c +++ b/engine/platform/android/snd_opensles.c @@ -267,6 +267,6 @@ qboolean VoiceCapture_RecordStart( void ) void VoiceCapture_RecordStop( void ) { - return 0; + } #endif diff --git a/engine/platform/stub/s_stub.c b/engine/platform/stub/s_stub.c index c3855dd6..bb1684b5 100644 --- a/engine/platform/stub/s_stub.c +++ b/engine/platform/stub/s_stub.c @@ -106,7 +106,7 @@ qboolean VoiceCapture_RecordStart( void ) void VoiceCapture_RecordStop( void ) { - return 0; + } #endif From b5885d4107edb15b96cf0b92b10112b23dce7606 Mon Sep 17 00:00:00 2001 From: SNMetamorph <25657591+SNMetamorph@users.noreply.github.com> Date: Mon, 15 Aug 2022 08:40:23 +0400 Subject: [PATCH 034/490] 3rdparty: opus: wscript: removed Opus demo sources from build --- 3rdparty/opus/wscript | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/3rdparty/opus/wscript b/3rdparty/opus/wscript index 8f1fe0ca..54daf0ff 100644 --- a/3rdparty/opus/wscript +++ b/3rdparty/opus/wscript @@ -37,6 +37,11 @@ def build(bld): 'opus/celt/*.c', 'opus/silk/*.c', 'opus/silk/float/*.c' + ], excl = [ + 'opus/src/repacketizer_demo.c', + 'opus/src/opus_demo.c', + 'opus/src/opus_compare.c', + 'opus/celt/opus_custom_demo.c' ]) includes = ['opus/include/', 'opus/celt/', 'opus/silk/', 'opus/silk/float/'] defines = ['USE_ALLOCA', 'OPUS_BUILD', 'FLOAT_APPROX', 'PACKAGE_VERSION="1.3.1"'] From 3b9a7910d0fe3f6e994ac00f032cbca15e000959 Mon Sep 17 00:00:00 2001 From: SNMetamorph <25657591+SNMetamorph@users.noreply.github.com> Date: Mon, 15 Aug 2022 08:44:04 +0400 Subject: [PATCH 035/490] opus: fixed path to submodule --- .gitmodules | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.gitmodules b/.gitmodules index dc4d389a..71d05e6d 100644 --- a/.gitmodules +++ b/.gitmodules @@ -14,5 +14,5 @@ path = vgui_support url = https://github.com/FWGS/vgui_support [submodule "opus"] - path = 3rdparty/opus + path = 3rdparty/opus/opus url = https://github.com/xiph/opus From 8866d5cfd6f0f59e45623cc949964c5ac0fb2e20 Mon Sep 17 00:00:00 2001 From: SNMetamorph <25657591+SNMetamorph@users.noreply.github.com> Date: Mon, 15 Aug 2022 08:50:25 +0400 Subject: [PATCH 036/490] engine: wscript: added Opus headers to include directories --- engine/wscript | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/engine/wscript b/engine/wscript index b91d79f2..baeb6fb6 100644 --- a/engine/wscript +++ b/engine/wscript @@ -168,7 +168,7 @@ def build(bld): libs.append('opus') - includes = ['common', 'server', 'client', 'client/vgui', 'tests', '.', '../public', '../common', '../filesystem', '../pm_shared' ] + includes = ['common', 'server', 'client', 'client/vgui', 'tests', '.', '../public', '../common', '../filesystem', '../pm_shared', '../3rdparty/opus/opus/include' ] if bld.env.SINGLE_BINARY: install_path = bld.env.BINDIR From 169ee14724302d36169410c8dea98108144f2675 Mon Sep 17 00:00:00 2001 From: SNMetamorph <25657591+SNMetamorph@users.noreply.github.com> Date: Mon, 15 Aug 2022 10:24:38 +0400 Subject: [PATCH 037/490] engine: client: disabled voice on legacy protocol servers --- engine/client/cl_main.c | 1 + engine/client/s_main.c | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/engine/client/cl_main.c b/engine/client/cl_main.c index 172a724b..53b35734 100644 --- a/engine/client/cl_main.c +++ b/engine/client/cl_main.c @@ -1514,6 +1514,7 @@ void CL_Disconnect( void ) // clear the network channel, too. Netchan_Clear( &cls.netchan ); + Voice_RecordStop(); IN_LockInputDevices( false ); // unlock input devices diff --git a/engine/client/s_main.c b/engine/client/s_main.c index 272502f9..5e1f8429 100644 --- a/engine/client/s_main.c +++ b/engine/client/s_main.c @@ -1868,7 +1868,7 @@ S_VoiceRecordStart_f */ void S_VoiceRecordStart_f( void ) { - if( cls.state != ca_active ) + if( cls.state != ca_active || cls.legacymode ) return; Voice_RecordStart(); From 279894cfd46db5d91ef4ff08a3e4abde242e819d Mon Sep 17 00:00:00 2001 From: SNMetamorph <25657591+SNMetamorph@users.noreply.github.com> Date: Mon, 15 Aug 2022 11:24:56 +0400 Subject: [PATCH 038/490] engine: client: added console command voice_codecinfo --- engine/client/voice.c | 32 ++++++++++++++++++++++++++++++++ 1 file changed, 32 insertions(+) diff --git a/engine/client/voice.c b/engine/client/voice.c index c0644b2c..9eb63cba 100644 --- a/engine/client/voice.c +++ b/engine/client/voice.c @@ -10,12 +10,44 @@ CVAR_DEFINE_AUTO( voice_loopback, "0", 0, "loopback voice back to the speaker" ) CVAR_DEFINE_AUTO( voice_scale, "1.0", FCVAR_ARCHIVE, "incoming voice volume scale" ); CVAR_DEFINE_AUTO( voice_inputfromfile, "0", 0, "input voice from voice_input.wav" ); +static const char* Voice_GetBandwidthTypeName( int bandwidthType ) +{ + switch( bandwidthType ) + { + case OPUS_BANDWIDTH_FULLBAND: return "Full Band (20 kHz)"; + case OPUS_BANDWIDTH_SUPERWIDEBAND: return "Super Wide Band (12 kHz)"; + case OPUS_BANDWIDTH_WIDEBAND: return "Wide Band (8 kHz)"; + case OPUS_BANDWIDTH_MEDIUMBAND: return "Medium Band (6 kHz)"; + case OPUS_BANDWIDTH_NARROWBAND: return "Narrow Band (4 kHz)"; + default: return "Unknown"; + } +} + +static void Voice_CodecInfo_f( void ) +{ + int encoderComplexity; + opus_int32 encoderBitrate; + opus_int32 encoderBandwidthType; + + opus_encoder_ctl( voice.encoder, OPUS_GET_BITRATE( &encoderBitrate )); + opus_encoder_ctl( voice.encoder, OPUS_GET_COMPLEXITY( &encoderComplexity )); + opus_encoder_ctl( voice.encoder, OPUS_GET_BANDWIDTH( &encoderBandwidthType )); + + Con_Printf( "Encoder:\n" ); + Con_Printf( " Bitrate: %.3f kB/second\n", encoderBitrate / 8.0f / 1024.0f ); + Con_Printf( " Complexity: %d\n", encoderComplexity ); + Con_Printf( " Bandwidth: " ); + Con_Printf( Voice_GetBandwidthTypeName( encoderBandwidthType )); + Con_Printf( "\n" ); +} + void Voice_RegisterCvars( void ) { Cvar_RegisterVariable( &voice_enable ); Cvar_RegisterVariable( &voice_loopback ); Cvar_RegisterVariable( &voice_scale ); Cvar_RegisterVariable( &voice_inputfromfile ); + Cmd_AddClientCommand( "voice_codecinfo", Voice_CodecInfo_f ); } static void Voice_Status( int entindex, qboolean bTalking ) From 8d0209b1224bae67e35bc365f58026ec96845480 Mon Sep 17 00:00:00 2001 From: SNMetamorph <25657591+SNMetamorph@users.noreply.github.com> Date: Mon, 15 Aug 2022 11:26:41 +0400 Subject: [PATCH 039/490] engine: server: added support for variable voice chat quality --- engine/server/sv_init.c | 2 +- engine/server/sv_main.c | 2 ++ 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/engine/server/sv_init.c b/engine/server/sv_init.c index d77f21a1..0c421ed0 100644 --- a/engine/server/sv_init.c +++ b/engine/server/sv_init.c @@ -395,7 +395,7 @@ void SV_WriteVoiceCodec( sizebuf_t *msg ) { MSG_BeginServerCmd( msg, svc_voiceinit ); MSG_WriteString( msg, "opus" ); - MSG_WriteByte( msg, 0 ); + MSG_WriteByte( msg, Cvar_VariableInteger( "sv_voicequality" )); } /* diff --git a/engine/server/sv_main.c b/engine/server/sv_main.c index bae1f522..1122905b 100644 --- a/engine/server/sv_main.c +++ b/engine/server/sv_main.c @@ -114,6 +114,7 @@ CVAR_DEFINE_AUTO( violence_agibs, "1", 0, "show alien gib entities" ); // voice chat CVAR_DEFINE_AUTO( sv_voiceenable, "1", FCVAR_ARCHIVE|FCVAR_SERVER, "enable voice support" ); +CVAR_DEFINE_AUTO( sv_voicequality, "3", FCVAR_ARCHIVE|FCVAR_SERVER, "voice chat quality level, from 0 to 5, higher is better" ); convar_t *sv_novis; // disable server culling entities by vis convar_t *sv_pausable; @@ -978,6 +979,7 @@ void SV_Init( void ) Cvar_RegisterVariable( &mapchangecfgfile ); Cvar_RegisterVariable( &sv_voiceenable ); + Cvar_RegisterVariable( &sv_voicequality ); Cvar_RegisterVariable( &sv_trace_messages ); sv_allow_joystick = Cvar_Get( "sv_allow_joystick", "1", FCVAR_ARCHIVE, "allow connect with joystick enabled" ); From c6881a425fb82b9f3c41897df813b1e2211efa10 Mon Sep 17 00:00:00 2001 From: SNMetamorph <25657591+SNMetamorph@users.noreply.github.com> Date: Mon, 15 Aug 2022 11:40:55 +0400 Subject: [PATCH 040/490] engine: client: added support for variable voice chat quality --- engine/client/cl_main.c | 2 +- engine/client/voice.c | 30 +++++++++++++++++++++++++++++- 2 files changed, 30 insertions(+), 2 deletions(-) diff --git a/engine/client/cl_main.c b/engine/client/cl_main.c index 53b35734..f65166f1 100644 --- a/engine/client/cl_main.c +++ b/engine/client/cl_main.c @@ -3085,7 +3085,7 @@ void CL_Init( void ) VID_Init(); // init video S_Init(); // init sound - Voice_Init( "opus", 0 ); // init voice + Voice_Init( "opus", 3 ); // init voice // unreliable buffer. unsed for unreliable commands and voice stream MSG_Init( &cls.datagram, "cls.datagram", cls.datagram_buf, sizeof( cls.datagram_buf )); diff --git a/engine/client/voice.c b/engine/client/voice.c index 9eb63cba..e06e47fb 100644 --- a/engine/client/voice.c +++ b/engine/client/voice.c @@ -79,9 +79,37 @@ qboolean Voice_Init( const char *pszCodecName, int quality ) } voice.encoder = opus_encoder_create( voice.samplerate, voice.channels, OPUS_APPLICATION_VOIP, &err ); + + if( err != OPUS_OK ) + return false; + voice.decoder = opus_decoder_create( voice.samplerate, voice.channels, &err ); - return true; + switch( quality ) + { + case 1: // 4800 bits per second, <4 kHz bandwidth + opus_encoder_ctl( voice.encoder, OPUS_SET_BITRATE( 4800 )); + opus_encoder_ctl( voice.encoder, OPUS_SET_BANDWIDTH( OPUS_BANDWIDTH_NARROWBAND )); + break; + case 2: // 12000 bits per second, <6 kHz bandwidth + opus_encoder_ctl( voice.encoder, OPUS_SET_BITRATE( 12000 )); + opus_encoder_ctl( voice.encoder, OPUS_SET_BANDWIDTH( OPUS_BANDWIDTH_MEDIUMBAND )); + break; + case 4: // automatic bitrate, full band (20 kHz) + opus_encoder_ctl( voice.encoder, OPUS_SET_BITRATE( OPUS_AUTO )); + opus_encoder_ctl( voice.encoder, OPUS_SET_BANDWIDTH( OPUS_BANDWIDTH_FULLBAND )); + break; + case 5: // maximum bitrate, full band (20 kHz) + opus_encoder_ctl( voice.encoder, OPUS_SET_BITRATE( OPUS_BITRATE_MAX )); + opus_encoder_ctl( voice.encoder, OPUS_SET_BANDWIDTH( OPUS_BANDWIDTH_FULLBAND )); + break; + default: // 36000 bits per second, <12 kHz bandwidth + opus_encoder_ctl( voice.encoder, OPUS_SET_BITRATE( 36000 )); + opus_encoder_ctl( voice.encoder, OPUS_SET_BANDWIDTH( OPUS_BANDWIDTH_SUPERWIDEBAND )); + break; + } + + return err == OPUS_OK; } void Voice_DeInit( void ) From 9bcd36cc24b881cf9603a15165a8fbc111899c7c Mon Sep 17 00:00:00 2001 From: SNMetamorph <25657591+SNMetamorph@users.noreply.github.com> Date: Mon, 15 Aug 2022 11:42:06 +0400 Subject: [PATCH 041/490] engine: voice code minor refactoring --- engine/client/voice.c | 48 ++++++++++++++++++------------------- engine/client/voice.h | 12 ++++++---- engine/platform/sdl/s_sdl.c | 8 +++---- 3 files changed, 35 insertions(+), 33 deletions(-) diff --git a/engine/client/voice.c b/engine/client/voice.c index e06e47fb..8c56b2b5 100644 --- a/engine/client/voice.c +++ b/engine/client/voice.c @@ -55,6 +55,11 @@ static void Voice_Status( int entindex, qboolean bTalking ) clgame.dllFuncs.pfnVoiceStatus( entindex, bTalking ); } +static uint Voice_GetFrameSize( float durationMsec ) +{ + return voice.channels * voice.width * (( float )voice.samplerate / ( 1000.0f / durationMsec )); +} + // parameters currently unused qboolean Voice_Init( const char *pszCodecName, int quality ) { @@ -65,12 +70,11 @@ qboolean Voice_Init( const char *pszCodecName, int quality ) Voice_DeInit(); - voice.was_init = true; - + voice.initialized = true; voice.channels = 1; voice.width = 2; voice.samplerate = SOUND_48k; - voice.frame_size = voice.channels * ( (float)voice.samplerate / ( 1000.0f / 20.0f ) ) * voice.width; + voice.frame_size = Voice_GetFrameSize( 20.0f ); if ( !VoiceCapture_Init() ) { @@ -114,7 +118,7 @@ qboolean Voice_Init( const char *pszCodecName, int quality ) void Voice_DeInit( void ) { - if ( !voice.was_init ) + if ( !voice.initialized ) return; Voice_RecordStop(); @@ -122,7 +126,7 @@ void Voice_DeInit( void ) opus_encoder_destroy( voice.encoder ); opus_decoder_destroy( voice.decoder ); - voice.was_init = false; + voice.initialized = false; } uint Voice_GetCompressedData( byte *out, uint maxsize, uint *frames ) @@ -138,22 +142,22 @@ uint Voice_GetCompressedData( byte *out, uint maxsize, uint *frames ) numbytes = ( time - voice.start_time ) * voice.samplerate; numbytes = Q_min( numbytes, input_file->size - input_pos ); - numbytes = Q_min( numbytes, sizeof( voice.buffer ) - voice.buffer_pos ); + numbytes = Q_min( numbytes, sizeof( voice.input_buffer ) - voice.input_buffer_pos ); - memcpy( voice.buffer + voice.buffer_pos, input_file->buffer + input_pos, numbytes ); - voice.buffer_pos += numbytes; + memcpy( voice.input_buffer + voice.input_buffer_pos, input_file->buffer + input_pos, numbytes ); + voice.input_buffer_pos += numbytes; input_pos += numbytes; voice.start_time = time; } - for ( ofs = 0; voice.buffer_pos - ofs >= voice.frame_size && ofs <= voice.buffer_pos; ofs += voice.frame_size ) + for ( ofs = 0; voice.input_buffer_pos - ofs >= voice.frame_size && ofs <= voice.input_buffer_pos; ofs += voice.frame_size ) { int bytes; - bytes = opus_encode( voice.encoder, (const opus_int16*)(voice.buffer + ofs), voice.frame_size / voice.width, out + size, maxsize ); - memmove( voice.buffer, voice.buffer + voice.frame_size, sizeof( voice.buffer ) - voice.frame_size ); - voice.buffer_pos -= voice.frame_size; + bytes = opus_encode( voice.encoder, (const opus_int16*)(voice.input_buffer + ofs), voice.frame_size / voice.width, out + size, maxsize ); + memmove( voice.input_buffer, voice.input_buffer + voice.frame_size, sizeof( voice.input_buffer ) - voice.frame_size ); + voice.input_buffer_pos -= voice.frame_size; if ( bytes > 0 ) { @@ -198,8 +202,8 @@ void Voice_RecordStop( void ) input_file = NULL; } - voice.buffer_pos = 0; - memset( voice.buffer, 0, sizeof( voice.buffer ) ); + voice.input_buffer_pos = 0; + memset( voice.input_buffer, 0, sizeof( voice.input_buffer ) ); if ( Voice_IsRecording() ) Voice_Status( -1, false ); @@ -241,24 +245,20 @@ void Voice_RecordStart( void ) void Voice_AddIncomingData( int ent, byte *data, uint size, uint frames ) { - byte decompressed[MAX_RAW_SAMPLES]; - int samples; - - samples = opus_decode( voice.decoder, (const byte*)data, size, (short *)decompressed, voice.frame_size / voice.width * frames, false ); + int samples = opus_decode( voice.decoder, (const byte*)data, size, (short *)voice.decompress_buffer, voice.frame_size / voice.width * frames, false ); - if ( samples > 0 ) - Voice_StartChannel( samples, decompressed, ent ); + if ( samples > 0 ) + Voice_StartChannel( samples, voice.decompress_buffer, ent ); } void CL_AddVoiceToDatagram( void ) { uint size, frames = 0; - byte data[MAX_RAW_SAMPLES]; if ( cls.state != ca_active || !Voice_IsRecording() ) return; - size = Voice_GetCompressedData( data, sizeof( data ), &frames ); + size = Voice_GetCompressedData( voice.output_buffer, sizeof( voice.output_buffer ), &frames ); if ( size > 0 && MSG_GetNumBytesLeft( &cls.datagram ) >= size + 32 ) { @@ -266,7 +266,7 @@ void CL_AddVoiceToDatagram( void ) MSG_WriteByte( &cls.datagram, Voice_GetLoopback() ); MSG_WriteByte( &cls.datagram, frames ); MSG_WriteShort( &cls.datagram, size ); - MSG_WriteBytes( &cls.datagram, data, size ); + MSG_WriteBytes( &cls.datagram, voice.output_buffer, size ); } } @@ -291,4 +291,4 @@ void Voice_StartChannel( uint samples, byte *data, int entnum ) SND_ForceInitMouth( entnum ); Voice_Status( entnum, true ); S_RawEntSamples( entnum, samples, voice.samplerate, voice.width, voice.channels, data, 128.0f * voice_scale.value ); -} \ No newline at end of file +} diff --git a/engine/client/voice.h b/engine/client/voice.h index b3fd162a..bcf27c7e 100644 --- a/engine/client/voice.h +++ b/engine/client/voice.h @@ -13,7 +13,7 @@ extern convar_t voice_scale; typedef struct voice_state_s { - qboolean was_init; + qboolean initialized; qboolean is_recording; float start_time; qboolean talking_ack; @@ -29,9 +29,11 @@ typedef struct voice_state_s uint samplerate; uint frame_size; - // input buffer - byte buffer[MAX_RAW_SAMPLES]; - fs_offset_t buffer_pos; + // buffers + byte input_buffer[MAX_RAW_SAMPLES]; + byte output_buffer[MAX_RAW_SAMPLES]; + byte decompress_buffer[MAX_RAW_SAMPLES]; + fs_offset_t input_buffer_pos; } voice_state_t; extern voice_state_t voice; @@ -51,4 +53,4 @@ qboolean Voice_GetLoopback( void ); void Voice_LocalPlayerTalkingAck( void ); void Voice_StartChannel( uint samples, byte *data, int entnum ); -#endif // VOICE_H \ No newline at end of file +#endif // VOICE_H diff --git a/engine/platform/sdl/s_sdl.c b/engine/platform/sdl/s_sdl.c index 95edfcc1..39526e2a 100644 --- a/engine/platform/sdl/s_sdl.c +++ b/engine/platform/sdl/s_sdl.c @@ -235,10 +235,10 @@ void SDL_SoundInputCallback( void *userdata, Uint8 *stream, int len ) { int size; - size = Q_min( len, sizeof( voice.buffer ) - voice.buffer_pos ); - SDL_memset( voice.buffer + voice.buffer_pos, 0, size ); - SDL_MixAudioFormat( voice.buffer + voice.buffer_pos, stream, sdl_format, size, SDL_MIX_MAXVOLUME ); - voice.buffer_pos += size; + size = Q_min( len, sizeof( voice.input_buffer ) - voice.input_buffer_pos ); + SDL_memset( voice.input_buffer + voice.input_buffer_pos, 0, size ); + SDL_MixAudioFormat( voice.input_buffer + voice.input_buffer_pos, stream, sdl_format, size, SDL_MIX_MAXVOLUME ); + voice.input_buffer_pos += size; } /* From c5d7e3c78303595a586f79620438fc5e5c6826ac Mon Sep 17 00:00:00 2001 From: SNMetamorph <25657591+SNMetamorph@users.noreply.github.com> Date: Mon, 15 Aug 2022 12:39:36 +0400 Subject: [PATCH 042/490] engine: client: fixed players voice state changing --- engine/client/cl_parse.c | 6 ++++-- engine/client/voice.c | 30 ++++++++++++++++++++++++++++-- engine/client/voice.h | 6 ++++++ 3 files changed, 38 insertions(+), 4 deletions(-) diff --git a/engine/client/cl_parse.c b/engine/client/cl_parse.c index 311656d0..f2cb26cf 100644 --- a/engine/client/cl_parse.c +++ b/engine/client/cl_parse.c @@ -1714,9 +1714,11 @@ void CL_ParseVoiceData( sizebuf_t *msg ) if ( idx <= 0 || idx > cl.maxclients ) return; - if ( idx == cl.playernum + 1 ) + if( idx == cl.playernum + 1 ) Voice_LocalPlayerTalkingAck(); - + else + Voice_PlayerTalkingAck( idx ); + if ( !size ) return; diff --git a/engine/client/voice.c b/engine/client/voice.c index 8c56b2b5..46c61a91 100644 --- a/engine/client/voice.c +++ b/engine/client/voice.c @@ -171,6 +171,8 @@ uint Voice_GetCompressedData( byte *out, uint maxsize, uint *frames ) void Voice_Idle( float frametime ) { + int i; + if ( !voice_enable.value ) { Voice_DeInit(); @@ -180,13 +182,26 @@ void Voice_Idle( float frametime ) if ( voice.talking_ack ) { voice.talking_timeout += frametime; - - if ( voice.talking_timeout > 0.2f ) + if( voice.talking_timeout > 0.2f ) { voice.talking_ack = false; Voice_Status( -2, false ); } } + + for ( i = 0; i < 32; i++ ) + { + if ( voice.players_status[i].talking_ack ) + { + voice.players_status[i].talking_timeout += frametime; + if ( voice.players_status[i].talking_timeout > 0.2f ) + { + voice.players_status[i].talking_ack = false; + if ( i < cl.maxclients ) + Voice_Status( i, false ); + } + } + } } qboolean Voice_IsRecording( void ) @@ -286,6 +301,17 @@ void Voice_LocalPlayerTalkingAck( void ) voice.talking_timeout = 0.0f; } +void Voice_PlayerTalkingAck(int playerIndex) +{ + if( !voice.players_status[playerIndex].talking_ack ) + { + Voice_Status( playerIndex, true ); + } + + voice.players_status[playerIndex].talking_ack = true; + voice.players_status[playerIndex].talking_timeout = 0.0f; +} + void Voice_StartChannel( uint samples, byte *data, int entnum ) { SND_ForceInitMouth( entnum ); diff --git a/engine/client/voice.h b/engine/client/voice.h index bcf27c7e..7ac3fe93 100644 --- a/engine/client/voice.h +++ b/engine/client/voice.h @@ -19,6 +19,11 @@ typedef struct voice_state_s qboolean talking_ack; float talking_timeout; + struct { + qboolean talking_ack; + float talking_timeout; + } players_status[32]; + // opus stuff OpusEncoder *encoder; OpusDecoder *decoder; @@ -51,6 +56,7 @@ void Voice_RecordStart( void ); void Voice_AddIncomingData( int ent, byte *data, uint size, uint frames ); qboolean Voice_GetLoopback( void ); void Voice_LocalPlayerTalkingAck( void ); +void Voice_PlayerTalkingAck( int playerIndex ); void Voice_StartChannel( uint samples, byte *data, int entnum ); #endif // VOICE_H From ae97eae42f2cc08eb786f2c85d9ffa2f908acb15 Mon Sep 17 00:00:00 2001 From: SNMetamorph <25657591+SNMetamorph@users.noreply.github.com> Date: Tue, 16 Aug 2022 00:23:33 +0400 Subject: [PATCH 043/490] engine: client: voice: implemented automatic gain control --- engine/client/voice.c | 50 ++++++++++++++++++++++++++++++++++++++++++- engine/client/voice.h | 8 +++++++ 2 files changed, 57 insertions(+), 1 deletion(-) diff --git a/engine/client/voice.c b/engine/client/voice.c index 46c61a91..3bc53f36 100644 --- a/engine/client/voice.c +++ b/engine/client/voice.c @@ -8,6 +8,8 @@ voice_state_t voice; CVAR_DEFINE_AUTO( voice_enable, "1", FCVAR_ARCHIVE, "enable voice chat" ); CVAR_DEFINE_AUTO( voice_loopback, "0", 0, "loopback voice back to the speaker" ); CVAR_DEFINE_AUTO( voice_scale, "1.0", FCVAR_ARCHIVE, "incoming voice volume scale" ); +CVAR_DEFINE_AUTO( voice_avggain, "0.5", FCVAR_ARCHIVE, "automatic voice gain control (average)" ); +CVAR_DEFINE_AUTO( voice_maxgain, "5.0", FCVAR_ARCHIVE, "automatic voice gain control (maximum)" ); CVAR_DEFINE_AUTO( voice_inputfromfile, "0", 0, "input voice from voice_input.wav" ); static const char* Voice_GetBandwidthTypeName( int bandwidthType ) @@ -46,6 +48,8 @@ void Voice_RegisterCvars( void ) Cvar_RegisterVariable( &voice_enable ); Cvar_RegisterVariable( &voice_loopback ); Cvar_RegisterVariable( &voice_scale ); + Cvar_RegisterVariable( &voice_avggain ); + Cvar_RegisterVariable( &voice_maxgain ); Cvar_RegisterVariable( &voice_inputfromfile ); Cmd_AddClientCommand( "voice_codecinfo", Voice_CodecInfo_f ); } @@ -60,6 +64,47 @@ static uint Voice_GetFrameSize( float durationMsec ) return voice.channels * voice.width * (( float )voice.samplerate / ( 1000.0f / durationMsec )); } +static void Voice_ApplyGainAdjust( opus_int16 *samples, int count ) +{ + float gain, modifiedMax; + int average, adjustedSample; + int blockOffset = 0; + + for (;;) + { + int i; + int localMax = 0; + int localSum = 0; + int blockSize = Q_min( count - ( blockOffset + voice.autogain.block_size ), voice.autogain.block_size ); + + if( blockSize < 1 ) + break; + + for( i = 0; i < blockSize; ++i ) + { + int sample = samples[blockOffset + i]; + if( abs( sample ) > localMax ) { + localMax = abs( sample ); + } + localSum += sample; + + gain = voice.autogain.current_gain + i * voice.autogain.gain_multiplier; + adjustedSample = Q_min( 32767, Q_max(( int )( sample * gain ), -32768 )); + samples[blockOffset + i] = adjustedSample; + } + + if( blockOffset % voice.autogain.block_size == 0 ) + { + 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( 32767.0f / modifiedMax, voice_maxgain.value ) * voice_scale.value; + voice.autogain.gain_multiplier = ( voice.autogain.next_gain - voice.autogain.current_gain ) / ( voice.autogain.block_size - 1 ); + } + blockOffset += blockSize; + } +} + // parameters currently unused qboolean Voice_Init( const char *pszCodecName, int quality ) { @@ -75,6 +120,7 @@ qboolean Voice_Init( const char *pszCodecName, int quality ) voice.width = 2; voice.samplerate = SOUND_48k; voice.frame_size = Voice_GetFrameSize( 20.0f ); + voice.autogain.block_size = 128; if ( !VoiceCapture_Init() ) { @@ -155,6 +201,8 @@ uint Voice_GetCompressedData( byte *out, uint maxsize, uint *frames ) { int bytes; + // adjust gain before encoding + Voice_ApplyGainAdjust((opus_int16*)voice.input_buffer + ofs, voice.frame_size); bytes = opus_encode( voice.encoder, (const opus_int16*)(voice.input_buffer + ofs), voice.frame_size / voice.width, out + size, maxsize ); memmove( voice.input_buffer, voice.input_buffer + voice.frame_size, sizeof( voice.input_buffer ) - voice.frame_size ); voice.input_buffer_pos -= voice.frame_size; @@ -316,5 +364,5 @@ void Voice_StartChannel( uint samples, byte *data, int entnum ) { SND_ForceInitMouth( entnum ); Voice_Status( entnum, true ); - S_RawEntSamples( entnum, samples, voice.samplerate, voice.width, voice.channels, data, 128.0f * voice_scale.value ); + S_RawEntSamples( entnum, samples, voice.samplerate, voice.width, voice.channels, data, 255 ); } diff --git a/engine/client/voice.h b/engine/client/voice.h index 7ac3fe93..ce3f5c21 100644 --- a/engine/client/voice.h +++ b/engine/client/voice.h @@ -39,6 +39,14 @@ typedef struct voice_state_s byte output_buffer[MAX_RAW_SAMPLES]; byte decompress_buffer[MAX_RAW_SAMPLES]; fs_offset_t input_buffer_pos; + + // automatic gain control + struct { + int block_size; + float current_gain; + float next_gain; + float gain_multiplier; + } autogain; } voice_state_t; extern voice_state_t voice; From 97879430e90ef4df6c17a6a7115d18ca2380efa0 Mon Sep 17 00:00:00 2001 From: SNMetamorph <25657591+SNMetamorph@users.noreply.github.com> Date: Tue, 16 Aug 2022 10:39:21 +0400 Subject: [PATCH 044/490] engine: small code fixes related to voice chat --- engine/client/cl_parse.c | 11 ++--------- engine/client/voice.c | 4 ++-- engine/client/voice.h | 2 +- engine/platform/linux/s_alsa.c | 15 +++++++++++++++ engine/server/server.h | 2 ++ engine/server/sv_client.c | 3 ++- engine/server/sv_init.c | 2 +- 7 files changed, 25 insertions(+), 14 deletions(-) diff --git a/engine/client/cl_parse.c b/engine/client/cl_parse.c index f2cb26cf..9320d45a 100644 --- a/engine/client/cl_parse.c +++ b/engine/client/cl_parse.c @@ -1700,14 +1700,14 @@ CL_ParseVoiceData void CL_ParseVoiceData( sizebuf_t *msg ) { int size, idx, frames; - unsigned char received[8192]; + static byte received[8192]; idx = MSG_ReadByte( msg ) + 1; frames = MSG_ReadByte( msg ); size = MSG_ReadShort( msg ); - size = Q_min( size, 8192 ); + size = Q_min( size, sizeof( received )); MSG_ReadBytes( msg, received, size ); @@ -3149,13 +3149,6 @@ void CL_ParseLegacyServerMessage( sizebuf_t *msg, qboolean normal_message ) case svc_director: CL_ParseDirector( msg ); break; - case svc_voiceinit: - CL_ParseVoiceInit( msg ); - break; - case svc_voicedata: - CL_ParseVoiceData( msg ); - cl.frames[cl.parsecountmod].graphdata.voicebytes += MSG_GetNumBytesRead( msg ) - bufStart; - break; case svc_resourcelocation: CL_ParseResLocation( msg ); break; diff --git a/engine/client/voice.c b/engine/client/voice.c index 3bc53f36..87696c0c 100644 --- a/engine/client/voice.c +++ b/engine/client/voice.c @@ -306,9 +306,9 @@ void Voice_RecordStart( void ) Voice_Status( -1, true ); } -void Voice_AddIncomingData( int ent, byte *data, uint size, uint frames ) +void Voice_AddIncomingData( int ent, const byte *data, uint size, uint frames ) { - int samples = opus_decode( voice.decoder, (const byte*)data, size, (short *)voice.decompress_buffer, voice.frame_size / voice.width * frames, false ); + int samples = opus_decode( voice.decoder, data, size, (short *)voice.decompress_buffer, voice.frame_size / voice.width * frames, false ); if ( samples > 0 ) Voice_StartChannel( samples, voice.decompress_buffer, ent ); diff --git a/engine/client/voice.h b/engine/client/voice.h index ce3f5c21..c5e110e2 100644 --- a/engine/client/voice.h +++ b/engine/client/voice.h @@ -61,7 +61,7 @@ void Voice_Idle( float frametime ); qboolean Voice_IsRecording( void ); void Voice_RecordStop( void ); void Voice_RecordStart( void ); -void Voice_AddIncomingData( int ent, byte *data, uint size, uint frames ); +void Voice_AddIncomingData( int ent, const byte *data, uint size, uint frames ); qboolean Voice_GetLoopback( void ); void Voice_LocalPlayerTalkingAck( void ); void Voice_PlayerTalkingAck( int playerIndex ); diff --git a/engine/platform/linux/s_alsa.c b/engine/platform/linux/s_alsa.c index 5a694d39..3a0cc7b3 100644 --- a/engine/platform/linux/s_alsa.c +++ b/engine/platform/linux/s_alsa.c @@ -341,4 +341,19 @@ void SNDDMA_Activate( qboolean active ) } } +qboolean VoiceCapture_Init( void ) +{ + return false; +} + +qboolean VoiceCapture_RecordStart( void ) +{ + return false; +} + +void VoiceCapture_RecordStop( void ) +{ + +} + #endif diff --git a/engine/server/server.h b/engine/server/server.h index 4775397f..c9dbc588 100644 --- a/engine/server/server.h +++ b/engine/server/server.h @@ -418,6 +418,8 @@ extern convar_t sv_stopspeed; extern convar_t sv_maxspeed; extern convar_t sv_wateralpha; extern convar_t sv_wateramp; +extern convar_t sv_voiceenable; +extern convar_t sv_voicequality; extern convar_t sv_stepsize; extern convar_t sv_maxvelocity; extern convar_t sv_rollangle; diff --git a/engine/server/sv_client.c b/engine/server/sv_client.c index 3ab3c04c..ecc4233a 100644 --- a/engine/server/sv_client.c +++ b/engine/server/sv_client.c @@ -2587,7 +2587,7 @@ void SV_ParseVoiceData( sv_client_t *cl, sizebuf_t *msg ) return; } - if ( !Cvar_VariableInteger( "sv_voiceenable" ) ) + if ( (int)sv_voiceenable.value == 0 ) return; MSG_ReadBytes( msg, received, size ); @@ -2599,6 +2599,7 @@ void SV_ParseVoiceData( sv_client_t *cl, sizebuf_t *msg ) length = size; + // 6 is a number of bytes for other parts of message if ( MSG_GetNumBytesLeft( &cur->datagram ) < length + 6 ) continue; diff --git a/engine/server/sv_init.c b/engine/server/sv_init.c index 0c421ed0..abfb28e4 100644 --- a/engine/server/sv_init.c +++ b/engine/server/sv_init.c @@ -395,7 +395,7 @@ void SV_WriteVoiceCodec( sizebuf_t *msg ) { MSG_BeginServerCmd( msg, svc_voiceinit ); MSG_WriteString( msg, "opus" ); - MSG_WriteByte( msg, Cvar_VariableInteger( "sv_voicequality" )); + MSG_WriteByte( msg, (int)sv_voicequality.value ); } /* From aa5a509316e298737a0f0e4a56a5882247e8aefb Mon Sep 17 00:00:00 2001 From: SNMetamorph <25657591+SNMetamorph@users.noreply.github.com> Date: Tue, 16 Aug 2022 10:45:56 +0400 Subject: [PATCH 045/490] engine: client: voice: added GPL notice --- engine/client/voice.c | 16 ++++++++++++++++ engine/client/voice.h | 16 ++++++++++++++++ 2 files changed, 32 insertions(+) diff --git a/engine/client/voice.c b/engine/client/voice.c index 87696c0c..2096d862 100644 --- a/engine/client/voice.c +++ b/engine/client/voice.c @@ -1,3 +1,19 @@ +/* +voice.c - voice chat implementation +Copyright (C) 2022 Velaron +Copyright (C) 2022 SNMetamorph + +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 "voice.h" wavdata_t *input_file; diff --git a/engine/client/voice.h b/engine/client/voice.h index c5e110e2..e5072896 100644 --- a/engine/client/voice.h +++ b/engine/client/voice.h @@ -1,3 +1,19 @@ +/* +voice.h - voice chat implementation +Copyright (C) 2022 Velaron +Copyright (C) 2022 SNMetamorph + +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 VOICE_H #define VOICE_H From 0a6885e02d96a533ad8877e03a414ff84bb6df14 Mon Sep 17 00:00:00 2001 From: SNMetamorph <25657591+SNMetamorph@users.noreply.github.com> Date: Tue, 16 Aug 2022 10:48:56 +0400 Subject: [PATCH 046/490] engine: client: voice chat cvars set as privileged --- engine/client/voice.c | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/engine/client/voice.c b/engine/client/voice.c index 2096d862..cbeba818 100644 --- a/engine/client/voice.c +++ b/engine/client/voice.c @@ -21,12 +21,12 @@ fs_offset_t input_pos; voice_state_t voice; -CVAR_DEFINE_AUTO( voice_enable, "1", FCVAR_ARCHIVE, "enable voice chat" ); -CVAR_DEFINE_AUTO( voice_loopback, "0", 0, "loopback voice back to the speaker" ); -CVAR_DEFINE_AUTO( voice_scale, "1.0", FCVAR_ARCHIVE, "incoming voice volume scale" ); -CVAR_DEFINE_AUTO( voice_avggain, "0.5", FCVAR_ARCHIVE, "automatic voice gain control (average)" ); -CVAR_DEFINE_AUTO( voice_maxgain, "5.0", FCVAR_ARCHIVE, "automatic voice gain control (maximum)" ); -CVAR_DEFINE_AUTO( voice_inputfromfile, "0", 0, "input voice from voice_input.wav" ); +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_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 const char* Voice_GetBandwidthTypeName( int bandwidthType ) { From a520af42e860f817372d7e6ba4ed8cfae0fa437b Mon Sep 17 00:00:00 2001 From: SNMetamorph <25657591+SNMetamorph@users.noreply.github.com> Date: Tue, 16 Aug 2022 10:53:54 +0400 Subject: [PATCH 047/490] wscript: opus subproject set as mandatory --- wscript | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/wscript b/wscript index 9b060a4c..fb7a4d3a 100644 --- a/wscript +++ b/wscript @@ -54,7 +54,7 @@ class Subproject: return True SUBDIRS = [ - Subproject('3rdparty/opus'), + Subproject('3rdparty/opus', mandatory=True), Subproject('public', dedicated=False, mandatory = True), Subproject('filesystem', dedicated=False, mandatory = True), Subproject('game_launch', singlebin=True), From 68f633e9cdb8dd3e9c596c88d5ed42f919f252f0 Mon Sep 17 00:00:00 2001 From: SNMetamorph <25657591+SNMetamorph@users.noreply.github.com> Date: Tue, 16 Aug 2022 19:08:44 +0400 Subject: [PATCH 048/490] wscript: fixed opus include path --- engine/wscript | 6 ++---- wscript | 2 +- 2 files changed, 3 insertions(+), 5 deletions(-) diff --git a/engine/wscript b/engine/wscript index baeb6fb6..7f3bdfce 100644 --- a/engine/wscript +++ b/engine/wscript @@ -107,7 +107,7 @@ def configure(conf): def build(bld): is_cxx_link = False - libs = [ 'public', 'dllemu' ] + libs = [ 'public', 'dllemu', 'opus' ] # basic build: dedicated only source = bld.path.ant_glob([ @@ -166,9 +166,7 @@ def build(bld): 'client/vgui/*.c', 'client/avi/*.c']) - libs.append('opus') - - includes = ['common', 'server', 'client', 'client/vgui', 'tests', '.', '../public', '../common', '../filesystem', '../pm_shared', '../3rdparty/opus/opus/include' ] + includes = ['common', 'server', 'client', 'client/vgui', 'tests', '.', '../public', '../common', '../filesystem', '../pm_shared' ] if bld.env.SINGLE_BINARY: install_path = bld.env.BINDIR diff --git a/wscript b/wscript index fb7a4d3a..de5c5da3 100644 --- a/wscript +++ b/wscript @@ -54,7 +54,7 @@ class Subproject: return True SUBDIRS = [ - Subproject('3rdparty/opus', mandatory=True), + Subproject('3rdparty/opus', dedicated=False, mandatory=True), Subproject('public', dedicated=False, mandatory = True), Subproject('filesystem', dedicated=False, mandatory = True), Subproject('game_launch', singlebin=True), From b0ff51d5a01dcbe255984716b2af37e210823506 Mon Sep 17 00:00:00 2001 From: SNMetamorph <25657591+SNMetamorph@users.noreply.github.com> Date: Tue, 16 Aug 2022 19:29:31 +0400 Subject: [PATCH 049/490] engine: client: voice: changed bitrate units to kbps in voice_codecinfo --- engine/client/voice.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/engine/client/voice.c b/engine/client/voice.c index cbeba818..0147eab4 100644 --- a/engine/client/voice.c +++ b/engine/client/voice.c @@ -52,7 +52,7 @@ static void Voice_CodecInfo_f( void ) opus_encoder_ctl( voice.encoder, OPUS_GET_BANDWIDTH( &encoderBandwidthType )); Con_Printf( "Encoder:\n" ); - Con_Printf( " Bitrate: %.3f kB/second\n", encoderBitrate / 8.0f / 1024.0f ); + Con_Printf( " Bitrate: %.3f kbps\n", encoderBitrate / 1000.0f ); Con_Printf( " Complexity: %d\n", encoderComplexity ); Con_Printf( " Bandwidth: " ); Con_Printf( Voice_GetBandwidthTypeName( encoderBandwidthType )); From a688bed79f368ba4cd224a2bfae6872010f87f9e Mon Sep 17 00:00:00 2001 From: SNMetamorph <25657591+SNMetamorph@users.noreply.github.com> Date: Tue, 16 Aug 2022 19:33:52 +0400 Subject: [PATCH 050/490] engine: client: cl_parse: moved buffer to stack in CL_ParseVoiceData --- engine/client/cl_parse.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/engine/client/cl_parse.c b/engine/client/cl_parse.c index 9320d45a..a8944d71 100644 --- a/engine/client/cl_parse.c +++ b/engine/client/cl_parse.c @@ -1700,7 +1700,7 @@ CL_ParseVoiceData void CL_ParseVoiceData( sizebuf_t *msg ) { int size, idx, frames; - static byte received[8192]; + byte received[8192]; idx = MSG_ReadByte( msg ) + 1; From 1fda8753bdd02a8dbb79fbb52e3184eab229a84a Mon Sep 17 00:00:00 2001 From: Alibek Omarov Date: Fri, 19 Aug 2022 03:10:34 +0300 Subject: [PATCH 051/490] wscript: rework subproject filtering, use lambdas everywhere. Deprecate --ignore-projects flag, use Waf capability to select targets instead --- wscript | 81 +++++++++++++++++++++------------------------------------ 1 file changed, 29 insertions(+), 52 deletions(-) diff --git a/wscript b/wscript index de5c5da3..07737fb5 100644 --- a/wscript +++ b/wscript @@ -14,60 +14,44 @@ top = '.' Context.Context.line_just = 55 # should fit for everything on 80x26 class Subproject: - name = '' - dedicated = True # if true will be ignored when building dedicated server - singlebin = False # if true will be ignored when singlebinary is set - ignore = False # if true will be ignored, set by user request - mandatory = False - - def __init__(self, name, dedicated=True, singlebin=False, mandatory = False, utility = False, fuzzer = False): + def __init__(self, name, fnFilter = None): self.name = name - self.dedicated = dedicated - self.singlebin = singlebin - self.mandatory = mandatory - self.utility = utility - self.fuzzer = fuzzer + self.fnFilter = fnFilter + + def is_exists(self, ctx): + return ctx.path.find_node(self.name + '/wscript') def is_enabled(self, ctx): - if not self.mandatory: - if self.name in ctx.env.IGNORE_PROJECTS: - self.ignore = True - - if self.ignore: + if not self.is_exists(ctx): return False - if ctx.env.SINGLE_BINARY and self.singlebin: - return False - - if ctx.env.DEST_OS == 'android' and self.singlebin: - return False - - if ctx.env.DEDICATED and self.dedicated: - return False - - if self.utility and not ctx.env.ENABLE_UTILS: - return False - - if self.fuzzer and not ctx.env.ENABLE_FUZZER: - return False + if self.fnFilter: + return self.fnFilter(ctx) return True SUBDIRS = [ - Subproject('3rdparty/opus', dedicated=False, mandatory=True), - Subproject('public', dedicated=False, mandatory = True), - Subproject('filesystem', dedicated=False, mandatory = True), - Subproject('game_launch', singlebin=True), - Subproject('ref_gl',), - Subproject('ref_soft'), - Subproject('mainui'), - Subproject('vgui_support'), - Subproject('stub/server', dedicated=False), - Subproject('stub/client'), + # always configured and built + Subproject('public'), + Subproject('filesystem'), + Subproject('engine'), + Subproject('stub/server'), Subproject('dllemu'), - Subproject('engine', dedicated=False), - Subproject('utils/mdldec', utility=True), - Subproject('utils/run-fuzzer', fuzzer=True) + + # disable only by engine feature, makes no sense to even parse subprojects in dedicated mode + Subproject('ref_gl', lambda x: not x.env.DEDICATED), + Subproject('ref_soft', lambda x: not x.env.DEDICATED), + Subproject('mainui', lambda x: not x.env.DEDICATED), + Subproject('vgui_support', lambda x: not x.env.DEDICATED), + Subproject('stub/client', lambda x: not x.env.DEDICATED), + Subproject('game_launch', lambda x: not x.env.SINGLE_BINARY and not x.env.DEST_OS != 'android'), + + # disable only by external dependency presense + Subproject('3rdparty/opus', lambda x: not x.env.HAVE_SYSTEM_OPUS), + + # enabled optionally + Subproject('utils/mdldec', lambda x: x.env.ENABLE_UTILS), + Subproject('utils/run-fuzzer', lambda x: x.env.ENABLE_FUZZER), ] def subdirs(): @@ -94,9 +78,6 @@ def options(opt): grp.add_option('--low-memory-mode', action = 'store', dest = 'LOW_MEMORY', default = 0, type = 'int', help = 'enable low memory mode (only for devices have <128 ram)') - grp.add_option('--ignore-projects', action = 'store', dest = 'IGNORE_PROJECTS', default = None, - help = 'disable selected projects from build [default: %default]') - grp.add_option('--disable-werror', action = 'store_true', dest = 'DISABLE_WERROR', default = False, help = 'disable compilation abort on warning') @@ -111,8 +92,7 @@ def options(opt): opt.load('compiler_optimizations subproject') for i in SUBDIRS: - if not i.mandatory and not opt.path.find_node(i.name+'/wscript'): - i.ignore = True + if not i.is_exists(opt): continue opt.add_subproject(i.name) @@ -124,9 +104,6 @@ def options(opt): def configure(conf): conf.load('fwgslib reconfigure compiler_optimizations') - if conf.options.IGNORE_PROJECTS: - conf.env.IGNORE_PROJECTS = conf.options.IGNORE_PROJECTS.split(',') - conf.env.MSVC_TARGETS = ['x86' if not conf.options.ALLOW64 else 'x64'] # Load compilers early From 238162bb7900c25a1953099b801cd92e705ec5b0 Mon Sep 17 00:00:00 2001 From: Alibek Omarov Date: Fri, 19 Aug 2022 04:03:53 +0300 Subject: [PATCH 052/490] wscript: move system opus detection to main wscript, so we can avoid including subproject --- 3rdparty/opus/wscript | 17 ----------------- wscript | 9 +++++++++ 2 files changed, 9 insertions(+), 17 deletions(-) diff --git a/3rdparty/opus/wscript b/3rdparty/opus/wscript index 54daf0ff..9f3d2098 100644 --- a/3rdparty/opus/wscript +++ b/3rdparty/opus/wscript @@ -3,24 +3,10 @@ import os -OPUS_CHECK='''#include -#include -int main() -{ - OpusEncoder *oe = opus_encoder_create(48000, 2, OPUS_APPLICATION_VOIP, NULL); - OpusDecoder *od = opus_decoder_create(48000, 2, NULL); - return !oe || !od; -} -''' - def options(opt): pass def configure(conf): - if conf.check_pkg('opus', 'OPUS', OPUS_CHECK, fatal = False): - conf.env.HAVE_OPUS = True - return - if not conf.path.find_dir('opus') or not conf.path.find_dir('opus/src'): conf.fatal('Can\'t find opus submodule. Run `git submodule update --init --recursive`.') return @@ -29,9 +15,6 @@ def configure(conf): # TODO: maybe call autotools/cmake/meson instead? def build(bld): - if bld.env.HAVE_OPUS: - return - sources = bld.path.ant_glob([ 'opus/src/*.c', 'opus/celt/*.c', diff --git a/wscript b/wscript index 07737fb5..606cf870 100644 --- a/wscript +++ b/wscript @@ -72,6 +72,9 @@ def options(opt): grp.add_option('-P', '--enable-packaging', action = 'store_true', dest = 'PACKAGING', default = False, help = 'respect prefix option, useful for packaging for various operating systems [default: %default]') + grp.add_option('--enabled-bundled-deps', action = 'store_true', dest = 'BUILD_BUNDLED_DEPS', default = False, + help = 'prefer to build bundled dependencies (like opus) instead of relying on system provided') + grp.add_option('--enable-bsp2', action = 'store_true', dest = 'SUPPORT_BSP2_FORMAT', default = False, help = 'build engine and renderers with BSP2 map support(recommended for Quake, breaks compatibility!) [default: %default]') @@ -323,6 +326,12 @@ int main(int argc, char **argv) { strcasestr(argv[1], argv[2]); return 0; }''' conf.env.SHAREDIR = conf.env.LIBDIR = conf.env.BINDIR = conf.env.PREFIX + if not conf.options.BUILD_BUNDLED_DEPS: + # check if we can use system opus + if conf.check_pkg('opus', 'opus', '''#include +int main(void){ return (opus_encoder_create(48000, 2, OPUS_APPLICATION_VOIP, 0) != 0) && (opus_decoder_create(48000, 2, 0) != 0);}''', fatal = False): + conf.env.HAVE_SYSTEM_OPUS = True + conf.define('XASH_BUILD_COMMIT', conf.env.GIT_VERSION if conf.env.GIT_VERSION else 'notset') conf.define('XASH_LOW_MEMORY', conf.options.LOW_MEMORY) From f7dc9d8e7221f68487949a3fe2c6d950b764dd38 Mon Sep 17 00:00:00 2001 From: SNMetamorph <25657591+SNMetamorph@users.noreply.github.com> Date: Thu, 18 Aug 2022 11:07:43 +0400 Subject: [PATCH 053/490] engine: client: voice: frame size increased to 40 msec --- engine/client/voice.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/engine/client/voice.c b/engine/client/voice.c index 0147eab4..8b7c818b 100644 --- a/engine/client/voice.c +++ b/engine/client/voice.c @@ -135,7 +135,7 @@ qboolean Voice_Init( const char *pszCodecName, int quality ) voice.channels = 1; voice.width = 2; voice.samplerate = SOUND_48k; - voice.frame_size = Voice_GetFrameSize( 20.0f ); + voice.frame_size = Voice_GetFrameSize( 40.0f ); voice.autogain.block_size = 128; if ( !VoiceCapture_Init() ) From 2f5f5ef0a6cceddb6f54703b0cfbeeabce99498d Mon Sep 17 00:00:00 2001 From: SNMetamorph <25657591+SNMetamorph@users.noreply.github.com> Date: Thu, 18 Aug 2022 13:07:47 +0400 Subject: [PATCH 054/490] engine: client: voice: fixed sound playback from file --- engine/client/voice.c | 17 +++++++++-------- 1 file changed, 9 insertions(+), 8 deletions(-) diff --git a/engine/client/voice.c b/engine/client/voice.c index 8b7c818b..1e8de7ee 100644 --- a/engine/client/voice.c +++ b/engine/client/voice.c @@ -198,27 +198,28 @@ uint Voice_GetCompressedData( byte *out, uint maxsize, uint *frames ) if ( input_file ) { uint numbytes; - double time; - - time = Sys_DoubleTime(); + double updateInterval; - numbytes = ( time - voice.start_time ) * voice.samplerate; + updateInterval = cl.mtime[0] - cl.mtime[1]; + numbytes = updateInterval * voice.samplerate * voice.width * voice.channels; numbytes = Q_min( numbytes, input_file->size - input_pos ); numbytes = Q_min( numbytes, sizeof( voice.input_buffer ) - voice.input_buffer_pos ); memcpy( voice.input_buffer + voice.input_buffer_pos, input_file->buffer + input_pos, numbytes ); voice.input_buffer_pos += numbytes; input_pos += numbytes; - - voice.start_time = time; } for ( ofs = 0; voice.input_buffer_pos - ofs >= voice.frame_size && ofs <= voice.input_buffer_pos; ofs += voice.frame_size ) { int bytes; - // adjust gain before encoding - Voice_ApplyGainAdjust((opus_int16*)voice.input_buffer + ofs, voice.frame_size); + if (!input_file) + { + // adjust gain before encoding, but only for input from voice + Voice_ApplyGainAdjust((opus_int16*)voice.input_buffer + ofs, voice.frame_size); + } + bytes = opus_encode( voice.encoder, (const opus_int16*)(voice.input_buffer + ofs), voice.frame_size / voice.width, out + size, maxsize ); memmove( voice.input_buffer, voice.input_buffer + voice.frame_size, sizeof( voice.input_buffer ) - voice.frame_size ); voice.input_buffer_pos -= voice.frame_size; From 74707551ae47f2bd980545f18db365d3e7b8882f Mon Sep 17 00:00:00 2001 From: SNMetamorph <25657591+SNMetamorph@users.noreply.github.com> Date: Thu, 18 Aug 2022 13:17:35 +0400 Subject: [PATCH 055/490] engine: client: voice: fixed hanging voice status --- engine/client/cl_main.c | 2 +- engine/client/voice.c | 11 ++++++++++- engine/client/voice.h | 1 + 3 files changed, 12 insertions(+), 2 deletions(-) diff --git a/engine/client/cl_main.c b/engine/client/cl_main.c index f65166f1..5a6201ff 100644 --- a/engine/client/cl_main.c +++ b/engine/client/cl_main.c @@ -1503,6 +1503,7 @@ void CL_Disconnect( void ) cls.connect_time = 0; cls.changedemo = false; cls.max_fragment_size = FRAGMENT_MAX_SIZE; // reset fragment size + Voice_Disconnect(); CL_Stop_f(); // send a disconnect message to the server @@ -1514,7 +1515,6 @@ void CL_Disconnect( void ) // clear the network channel, too. Netchan_Clear( &cls.netchan ); - Voice_RecordStop(); IN_LockInputDevices( false ); // unlock input devices diff --git a/engine/client/voice.c b/engine/client/voice.c index 1e8de7ee..d06c1147 100644 --- a/engine/client/voice.c +++ b/engine/client/voice.c @@ -323,6 +323,16 @@ void Voice_RecordStart( void ) Voice_Status( -1, true ); } +void Voice_Disconnect( void ) +{ + int i; + + Voice_RecordStop(); + for( i = 0; i <= 32; i++ ) { + Voice_Status( i, false ); + } +} + void Voice_AddIncomingData( int ent, const byte *data, uint size, uint frames ) { int samples = opus_decode( voice.decoder, data, size, (short *)voice.decompress_buffer, voice.frame_size / voice.width * frames, false ); @@ -380,6 +390,5 @@ void Voice_PlayerTalkingAck(int playerIndex) void Voice_StartChannel( uint samples, byte *data, int entnum ) { SND_ForceInitMouth( entnum ); - Voice_Status( entnum, true ); S_RawEntSamples( entnum, samples, voice.samplerate, voice.width, voice.channels, data, 255 ); } diff --git a/engine/client/voice.h b/engine/client/voice.h index e5072896..caabeede 100644 --- a/engine/client/voice.h +++ b/engine/client/voice.h @@ -77,6 +77,7 @@ void Voice_Idle( float frametime ); qboolean Voice_IsRecording( void ); void Voice_RecordStop( void ); void Voice_RecordStart( void ); +void Voice_Disconnect( void ); void Voice_AddIncomingData( int ent, const byte *data, uint size, uint frames ); qboolean Voice_GetLoopback( void ); void Voice_LocalPlayerTalkingAck( void ); From c5dbbea9efda02f3214e1f4c15134e448e1163e3 Mon Sep 17 00:00:00 2001 From: Alibek Omarov Date: Fri, 19 Aug 2022 04:33:41 +0300 Subject: [PATCH 056/490] engine: server: drop bots fake ping to zero --- engine/server/sv_client.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/engine/server/sv_client.c b/engine/server/sv_client.c index ecc4233a..4334a2c4 100644 --- a/engine/server/sv_client.c +++ b/engine/server/sv_client.c @@ -1046,7 +1046,7 @@ int SV_CalcPing( sv_client_t *cl ) // bots don't have a real ping if( FBitSet( cl->flags, FCL_FAKECLIENT ) || !cl->frames ) - return 5; + return 0; if( SV_UPDATE_BACKUP <= 31 ) { From e6c55107c7c762c84693caa9c763540fad70ceaf Mon Sep 17 00:00:00 2001 From: Alibek Omarov Date: Fri, 19 Aug 2022 05:38:47 +0300 Subject: [PATCH 057/490] wscript: fix game_launch not being included --- wscript | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/wscript b/wscript index 606cf870..04a9d90e 100644 --- a/wscript +++ b/wscript @@ -44,7 +44,7 @@ SUBDIRS = [ Subproject('mainui', lambda x: not x.env.DEDICATED), Subproject('vgui_support', lambda x: not x.env.DEDICATED), Subproject('stub/client', lambda x: not x.env.DEDICATED), - Subproject('game_launch', lambda x: not x.env.SINGLE_BINARY and not x.env.DEST_OS != 'android'), + Subproject('game_launch', lambda x: not x.env.SINGLE_BINARY and x.env.DEST_OS != 'android'), # disable only by external dependency presense Subproject('3rdparty/opus', lambda x: not x.env.HAVE_SYSTEM_OPUS), From 327dcc02931b0ee9e7b758d427d9da0436d3984a Mon Sep 17 00:00:00 2001 From: Alibek Omarov Date: Fri, 19 Aug 2022 05:52:53 +0300 Subject: [PATCH 058/490] engine: client: voice: simplify code, simplify including voice.h, do not depend on opus headers globally Autofix few code style mistakes --- engine/client/cl_parse.c | 4 +- engine/client/voice.c | 147 ++++++++++++++++++--------------------- engine/client/voice.h | 36 +++++----- 3 files changed, 85 insertions(+), 102 deletions(-) diff --git a/engine/client/cl_parse.c b/engine/client/cl_parse.c index a8944d71..070b2b9a 100644 --- a/engine/client/cl_parse.c +++ b/engine/client/cl_parse.c @@ -1715,9 +1715,9 @@ void CL_ParseVoiceData( sizebuf_t *msg ) return; if( idx == cl.playernum + 1 ) - Voice_LocalPlayerTalkingAck(); + Voice_StatusAck( &voice.local, VOICE_LOCALPLAYER_INDEX ); else - Voice_PlayerTalkingAck( idx ); + Voice_StatusAck( &voice.players_status[idx], idx ); if ( !size ) return; diff --git a/engine/client/voice.c b/engine/client/voice.c index d06c1147..4be454ae 100644 --- a/engine/client/voice.c +++ b/engine/client/voice.c @@ -14,12 +14,15 @@ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. */ +#include +#include "common.h" +#include "client.h" #include "voice.h" -wavdata_t *input_file; -fs_offset_t input_pos; +static wavdata_t *input_file; +static fs_offset_t input_pos; -voice_state_t voice; +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" ); @@ -47,6 +50,12 @@ static void Voice_CodecInfo_f( void ) opus_int32 encoderBitrate; opus_int32 encoderBandwidthType; + if( !voice.initialized ) + { + Con_Printf( "Voice codec is not initialized!\n" ); + return; + } + opus_encoder_ctl( voice.encoder, OPUS_GET_BITRATE( &encoderBitrate )); opus_encoder_ctl( voice.encoder, OPUS_GET_COMPLEXITY( &encoderComplexity )); opus_encoder_ctl( voice.encoder, OPUS_GET_BANDWIDTH( &encoderBandwidthType )); @@ -54,8 +63,7 @@ static void Voice_CodecInfo_f( void ) Con_Printf( "Encoder:\n" ); Con_Printf( " Bitrate: %.3f kbps\n", encoderBitrate / 1000.0f ); Con_Printf( " Complexity: %d\n", encoderComplexity ); - Con_Printf( " Bandwidth: " ); - Con_Printf( Voice_GetBandwidthTypeName( encoderBandwidthType )); + Con_Printf( " Bandwidth: %s", Voice_GetBandwidthTypeName( encoderBandwidthType )); Con_Printf( "\n" ); } @@ -86,7 +94,7 @@ static void Voice_ApplyGainAdjust( opus_int16 *samples, int count ) int average, adjustedSample; int blockOffset = 0; - for (;;) + for( ;;) { int i; int localMax = 0; @@ -126,7 +134,7 @@ qboolean Voice_Init( const char *pszCodecName, int quality ) { int err; - if ( !voice_enable.value ) + if( !voice_enable.value ) return false; Voice_DeInit(); @@ -138,7 +146,7 @@ qboolean Voice_Init( const char *pszCodecName, int quality ) voice.frame_size = Voice_GetFrameSize( 40.0f ); voice.autogain.block_size = 128; - if ( !VoiceCapture_Init() ) + if( !VoiceCapture_Init() ) { Voice_DeInit(); return false; @@ -180,7 +188,7 @@ qboolean Voice_Init( const char *pszCodecName, int quality ) void Voice_DeInit( void ) { - if ( !voice.initialized ) + if( !voice.initialized ) return; Voice_RecordStop(); @@ -191,11 +199,11 @@ void Voice_DeInit( void ) voice.initialized = false; } -uint Voice_GetCompressedData( byte *out, uint maxsize, uint *frames ) +static uint Voice_GetCompressedData( byte *out, uint maxsize, uint *frames ) { uint ofs, size = 0; - if ( input_file ) + if( input_file ) { uint numbytes; double updateInterval; @@ -210,11 +218,11 @@ uint Voice_GetCompressedData( byte *out, uint maxsize, uint *frames ) input_pos += numbytes; } - for ( ofs = 0; voice.input_buffer_pos - ofs >= voice.frame_size && ofs <= voice.input_buffer_pos; ofs += voice.frame_size ) + for( ofs = 0; voice.input_buffer_pos - ofs >= voice.frame_size && ofs <= voice.input_buffer_pos; ofs += voice.frame_size ) { int bytes; - if (!input_file) + if( !input_file ) { // adjust gain before encoding, but only for input from voice Voice_ApplyGainAdjust((opus_int16*)voice.input_buffer + ofs, voice.frame_size); @@ -224,7 +232,7 @@ uint Voice_GetCompressedData( byte *out, uint maxsize, uint *frames ) memmove( voice.input_buffer, voice.input_buffer + voice.frame_size, sizeof( voice.input_buffer ) - voice.frame_size ); voice.input_buffer_pos -= voice.frame_size; - if ( bytes > 0 ) + if( bytes > 0 ) { size += bytes; (*frames)++; @@ -234,39 +242,43 @@ uint Voice_GetCompressedData( byte *out, uint maxsize, uint *frames ) return size; } -void Voice_Idle( float frametime ) +static void Voice_StatusTimeout( voice_status_t *status, int entindex, double frametime ) +{ + if( status->talking_ack ) + { + status->talking_timeout += frametime; + if( status->talking_timeout > 0.2 ) + { + status->talking_ack = false; + Voice_Status( entindex, false ); + } + } +} + +void Voice_StatusAck( voice_status_t *status, int playerIndex ) +{ + if( !status->talking_ack ) + Voice_Status( playerIndex, true ); + + status->talking_ack = true; + status->talking_timeout = 0.0; +} + +void Voice_Idle( double frametime ) { int i; - if ( !voice_enable.value ) + if( !voice_enable.value ) { Voice_DeInit(); return; } - if ( voice.talking_ack ) - { - voice.talking_timeout += frametime; - if( voice.talking_timeout > 0.2f ) - { - voice.talking_ack = false; - Voice_Status( -2, false ); - } - } + // update local player status first + Voice_StatusTimeout( &voice.local, VOICE_LOCALPLAYER_INDEX, frametime ); - for ( i = 0; i < 32; i++ ) - { - if ( voice.players_status[i].talking_ack ) - { - voice.players_status[i].talking_timeout += frametime; - if ( voice.players_status[i].talking_timeout > 0.2f ) - { - voice.players_status[i].talking_ack = false; - if ( i < cl.maxclients ) - Voice_Status( i, false ); - } - } - } + for( i = 0; i < 32; i++ ) + Voice_StatusTimeout( &voice.players_status[i], i, frametime ); } qboolean Voice_IsRecording( void ) @@ -276,7 +288,7 @@ qboolean Voice_IsRecording( void ) void Voice_RecordStop( void ) { - if ( input_file ) + if( input_file ) { FS_FreeSound( input_file ); input_file = NULL; @@ -285,7 +297,7 @@ void Voice_RecordStop( void ) voice.input_buffer_pos = 0; memset( voice.input_buffer, 0, sizeof( voice.input_buffer ) ); - if ( Voice_IsRecording() ) + if( Voice_IsRecording() ) Voice_Status( -1, false ); VoiceCapture_RecordStop(); @@ -297,11 +309,11 @@ void Voice_RecordStart( void ) { Voice_RecordStop(); - if ( voice_inputfromfile.value ) + if( voice_inputfromfile.value ) { input_file = FS_LoadSound( "voice_input.wav", NULL, 0 ); - if ( input_file ) + if( input_file ) { Sound_Process( &input_file, voice.samplerate, voice.width, SOUND_RESAMPLE ); input_pos = 0; @@ -316,10 +328,10 @@ void Voice_RecordStart( void ) } } - if ( !Voice_IsRecording() ) + if( !Voice_IsRecording() ) voice.is_recording = VoiceCapture_RecordStart(); - if ( Voice_IsRecording() ) + if( Voice_IsRecording() ) Voice_Status( -1, true ); } @@ -333,11 +345,17 @@ void Voice_Disconnect( void ) } } +static void Voice_StartChannel( uint samples, byte *data, int entnum ) +{ + SND_ForceInitMouth( entnum ); + S_RawEntSamples( entnum, samples, voice.samplerate, voice.width, voice.channels, data, 255 ); +} + void Voice_AddIncomingData( int ent, const byte *data, uint size, uint frames ) { int samples = opus_decode( voice.decoder, data, size, (short *)voice.decompress_buffer, voice.frame_size / voice.width * frames, false ); - if ( samples > 0 ) + if( samples > 0 ) Voice_StartChannel( samples, voice.decompress_buffer, ent ); } @@ -345,50 +363,17 @@ void CL_AddVoiceToDatagram( void ) { uint size, frames = 0; - if ( cls.state != ca_active || !Voice_IsRecording() ) + if( cls.state != ca_active || !Voice_IsRecording() ) return; size = Voice_GetCompressedData( voice.output_buffer, sizeof( voice.output_buffer ), &frames ); - if ( size > 0 && MSG_GetNumBytesLeft( &cls.datagram ) >= size + 32 ) + if( size > 0 && MSG_GetNumBytesLeft( &cls.datagram ) >= size + 32 ) { MSG_BeginClientCmd( &cls.datagram, clc_voicedata ); - MSG_WriteByte( &cls.datagram, Voice_GetLoopback() ); + 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 ); } } - -qboolean Voice_GetLoopback( void ) -{ - return voice_loopback.value; -} - -void Voice_LocalPlayerTalkingAck( void ) -{ - if ( !voice.talking_ack ) - { - Voice_Status( -2, true ); - } - - voice.talking_ack = true; - voice.talking_timeout = 0.0f; -} - -void Voice_PlayerTalkingAck(int playerIndex) -{ - if( !voice.players_status[playerIndex].talking_ack ) - { - Voice_Status( playerIndex, true ); - } - - voice.players_status[playerIndex].talking_ack = true; - voice.players_status[playerIndex].talking_timeout = 0.0f; -} - -void Voice_StartChannel( uint samples, byte *data, int entnum ) -{ - SND_ForceInitMouth( entnum ); - S_RawEntSamples( entnum, samples, voice.samplerate, voice.width, voice.channels, data, 255 ); -} diff --git a/engine/client/voice.h b/engine/client/voice.h index caabeede..c6d93b4e 100644 --- a/engine/client/voice.h +++ b/engine/client/voice.h @@ -17,28 +17,30 @@ GNU General Public License for more details. #ifndef VOICE_H #define VOICE_H -#include - -#include "common.h" -#include "client.h" +#include "protocol.h" // MAX_CLIENTS #include "sound.h" -#include "soundlib/soundlib.h" -#include "library.h" extern convar_t voice_scale; +typedef struct OpusDecoder OpusDecoder; +typedef struct OpusEncoder OpusEncoder; + +#define VOICE_LOCALPLAYER_INDEX (-2) + +typedef struct voice_status_s +{ + qboolean talking_ack; + double talking_timeout; +} voice_status_t; + typedef struct voice_state_s { qboolean initialized; qboolean is_recording; - float start_time; - qboolean talking_ack; - float talking_timeout; + double start_time; - struct { - qboolean talking_ack; - float talking_timeout; - } players_status[32]; + voice_status_t local; + voice_status_t players_status[MAX_CLIENTS]; // opus stuff OpusEncoder *encoder; @@ -72,16 +74,12 @@ void CL_AddVoiceToDatagram( void ); void Voice_RegisterCvars( void ); qboolean Voice_Init( const char *pszCodecName, int quality ); void Voice_DeInit( void ); -uint Voice_GetCompressedData( byte *out, uint maxsize, uint *frames ); -void Voice_Idle( float frametime ); +void Voice_Idle( double frametime ); qboolean Voice_IsRecording( void ); void Voice_RecordStop( void ); void Voice_RecordStart( void ); void Voice_Disconnect( void ); void Voice_AddIncomingData( int ent, const byte *data, uint size, uint frames ); -qboolean Voice_GetLoopback( void ); -void Voice_LocalPlayerTalkingAck( void ); -void Voice_PlayerTalkingAck( int playerIndex ); -void Voice_StartChannel( uint samples, byte *data, int entnum ); +void Voice_StatusAck( voice_status_t *status, int playerIndex ); #endif // VOICE_H From 76dbefb9ecf1f5409b5ff92a627c4bfaf82dd2f5 Mon Sep 17 00:00:00 2001 From: SNMetamorph <25657591+SNMetamorph@users.noreply.github.com> Date: Fri, 19 Aug 2022 13:27:35 +0400 Subject: [PATCH 059/490] engine: client: voice: fixed initialization checks --- engine/client/voice.c | 14 +++++++++----- 1 file changed, 9 insertions(+), 5 deletions(-) diff --git a/engine/client/voice.c b/engine/client/voice.c index 4be454ae..ad1390c0 100644 --- a/engine/client/voice.c +++ b/engine/client/voice.c @@ -138,8 +138,8 @@ qboolean Voice_Init( const char *pszCodecName, int quality ) return false; Voice_DeInit(); - - voice.initialized = true; + + voice.initialized = false; voice.channels = 1; voice.width = 2; voice.samplerate = SOUND_48k; @@ -149,13 +149,13 @@ qboolean Voice_Init( const char *pszCodecName, int quality ) if( !VoiceCapture_Init() ) { Voice_DeInit(); - return false; + return voice.initialized; } voice.encoder = opus_encoder_create( voice.samplerate, voice.channels, OPUS_APPLICATION_VOIP, &err ); if( err != OPUS_OK ) - return false; + return voice.initialized; voice.decoder = opus_decoder_create( voice.samplerate, voice.channels, &err ); @@ -183,7 +183,8 @@ qboolean Voice_Init( const char *pszCodecName, int quality ) break; } - return err == OPUS_OK; + voice.initialized = (err == OPUS_OK); + return voice.initialized; } void Voice_DeInit( void ) @@ -353,6 +354,9 @@ static void Voice_StartChannel( uint samples, byte *data, int entnum ) void Voice_AddIncomingData( int ent, const byte *data, uint size, uint frames ) { + if( !voice.initialized ) + return; + int samples = opus_decode( voice.decoder, data, size, (short *)voice.decompress_buffer, voice.frame_size / voice.width * frames, false ); if( samples > 0 ) From 8e48a98d48ae76ee4f0e04ea30e07ca911b648f7 Mon Sep 17 00:00:00 2001 From: SNMetamorph <25657591+SNMetamorph@users.noreply.github.com> Date: Fri, 19 Aug 2022 13:53:22 +0400 Subject: [PATCH 060/490] engine: client: voice: codec quality levels changed --- engine/client/voice.c | 33 +++++++++++++++++++++------------ 1 file changed, 21 insertions(+), 12 deletions(-) diff --git a/engine/client/voice.c b/engine/client/voice.c index ad1390c0..c447a5d0 100644 --- a/engine/client/voice.c +++ b/engine/client/voice.c @@ -161,28 +161,35 @@ qboolean Voice_Init( const char *pszCodecName, int quality ) switch( quality ) { - case 1: // 4800 bits per second, <4 kHz bandwidth - opus_encoder_ctl( voice.encoder, OPUS_SET_BITRATE( 4800 )); - opus_encoder_ctl( voice.encoder, OPUS_SET_BANDWIDTH( OPUS_BANDWIDTH_NARROWBAND )); - break; - case 2: // 12000 bits per second, <6 kHz bandwidth - opus_encoder_ctl( voice.encoder, OPUS_SET_BITRATE( 12000 )); + case 1: // 6 kbps, <6 kHz bandwidth + opus_encoder_ctl( voice.encoder, OPUS_SET_BITRATE( 6000 )); opus_encoder_ctl( voice.encoder, OPUS_SET_BANDWIDTH( OPUS_BANDWIDTH_MEDIUMBAND )); break; - case 4: // automatic bitrate, full band (20 kHz) - opus_encoder_ctl( voice.encoder, OPUS_SET_BITRATE( OPUS_AUTO )); + case 2: // 12 kbps, <12 kHz bandwidth + opus_encoder_ctl( voice.encoder, OPUS_SET_BITRATE( 12000 )); + opus_encoder_ctl( voice.encoder, OPUS_SET_BANDWIDTH( OPUS_BANDWIDTH_SUPERWIDEBAND )); + break; + case 4: // 64 kbps, full band (20 kHz) + opus_encoder_ctl( voice.encoder, OPUS_SET_BITRATE( 64000 )); opus_encoder_ctl( voice.encoder, OPUS_SET_BANDWIDTH( OPUS_BANDWIDTH_FULLBAND )); break; - case 5: // maximum bitrate, full band (20 kHz) - opus_encoder_ctl( voice.encoder, OPUS_SET_BITRATE( OPUS_BITRATE_MAX )); + case 5: // 96 kbps, full band (20 kHz) + opus_encoder_ctl( voice.encoder, OPUS_SET_BITRATE( 96000 )); opus_encoder_ctl( voice.encoder, OPUS_SET_BANDWIDTH( OPUS_BANDWIDTH_FULLBAND )); break; - default: // 36000 bits per second, <12 kHz bandwidth + default: // 36 kbps, <12 kHz bandwidth opus_encoder_ctl( voice.encoder, OPUS_SET_BITRATE( 36000 )); opus_encoder_ctl( voice.encoder, OPUS_SET_BANDWIDTH( OPUS_BANDWIDTH_SUPERWIDEBAND )); break; } + if (quality == 5) { + opus_encoder_ctl( voice.encoder, OPUS_SET_APPLICATION( OPUS_APPLICATION_AUDIO )); + } + else { + opus_encoder_ctl( voice.encoder, OPUS_SET_APPLICATION( OPUS_APPLICATION_VOIP )); + } + voice.initialized = (err == OPUS_OK); return voice.initialized; } @@ -354,10 +361,12 @@ static void Voice_StartChannel( uint samples, byte *data, int entnum ) void Voice_AddIncomingData( int ent, const byte *data, uint size, uint frames ) { + int samples; + if( !voice.initialized ) return; - int samples = opus_decode( voice.decoder, data, size, (short *)voice.decompress_buffer, voice.frame_size / voice.width * frames, false ); + samples = opus_decode( voice.decoder, data, size, (short *)voice.decompress_buffer, voice.frame_size / voice.width * frames, false ); if( samples > 0 ) Voice_StartChannel( samples, voice.decompress_buffer, ent ); From 88c1b1400df7aab545b57325835419c6b3a0ce67 Mon Sep 17 00:00:00 2001 From: Alibek Omarov Date: Fri, 19 Aug 2022 22:18:22 +0300 Subject: [PATCH 061/490] gitignore: add enc_temp_folder by the request of @SNMetamorph --- .gitignore | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/.gitignore b/.gitignore index fe2a728f..58eb4a76 100644 --- a/.gitignore +++ b/.gitignore @@ -331,4 +331,5 @@ __pycache__ .vscode/* *.code-workspace .history/* -.cache/* \ No newline at end of file +.cache/* +enc_temp_folder/ From 905bbf1515d6f04bb623c6d05ef7ac66fe26f31b Mon Sep 17 00:00:00 2001 From: Alibek Omarov Date: Fri, 19 Aug 2022 22:22:40 +0300 Subject: [PATCH 062/490] engine: server: read full voicedata message even if voice is disabled server wide --- engine/server/sv_client.c | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/engine/server/sv_client.c b/engine/server/sv_client.c index 4334a2c4..2193881f 100644 --- a/engine/server/sv_client.c +++ b/engine/server/sv_client.c @@ -2580,18 +2580,18 @@ void SV_ParseVoiceData( sv_client_t *cl, sizebuf_t *msg ) size = MSG_ReadShort( msg ); client = cl - svs.clients; - if ( size > sizeof( received ) ) + if( size > sizeof( received )) { Con_DPrintf( "SV_ParseVoiceData: invalid incoming packet.\n" ); SV_DropClient( cl, false ); return; } - if ( (int)sv_voiceenable.value == 0 ) - return; - MSG_ReadBytes( msg, received, size ); + if( !sv_voiceenable.value ) + return; + for( i = 0, cur = svs.clients; i < svs.maxclients; i++, cur++ ) { if ( cur->state < cs_connected && cl != cur ) @@ -2600,10 +2600,10 @@ void SV_ParseVoiceData( sv_client_t *cl, sizebuf_t *msg ) length = size; // 6 is a number of bytes for other parts of message - if ( MSG_GetNumBytesLeft( &cur->datagram ) < length + 6 ) + if( MSG_GetNumBytesLeft( &cur->datagram ) < length + 6 ) continue; - if ( cl == cur && !cur->m_bLoopback ) + if( cl == cur && !cur->m_bLoopback ) length = 0; MSG_BeginServerCmd( &cur->datagram, svc_voicedata ); From 22ff45f5d009adbf3fd5e77d33ce013cc793712e Mon Sep 17 00:00:00 2001 From: Alibek Omarov Date: Sat, 20 Aug 2022 03:17:51 +0300 Subject: [PATCH 063/490] engine: platform: add audio capture shutdown functions --- engine/platform/android/snd_opensles.c | 5 +++++ engine/platform/linux/s_alsa.c | 5 +++++ engine/platform/platform.h | 1 + engine/platform/sdl/s_sdl.c | 20 +++++++++++++++++--- engine/platform/stub/s_stub.c | 5 +++++ 5 files changed, 33 insertions(+), 3 deletions(-) diff --git a/engine/platform/android/snd_opensles.c b/engine/platform/android/snd_opensles.c index 0568ee61..fb729025 100644 --- a/engine/platform/android/snd_opensles.c +++ b/engine/platform/android/snd_opensles.c @@ -268,5 +268,10 @@ qboolean VoiceCapture_RecordStart( void ) void VoiceCapture_RecordStop( void ) { +} + +void VoiceCapture_Shutdown( void ) +{ + } #endif diff --git a/engine/platform/linux/s_alsa.c b/engine/platform/linux/s_alsa.c index 3a0cc7b3..806e9151 100644 --- a/engine/platform/linux/s_alsa.c +++ b/engine/platform/linux/s_alsa.c @@ -356,4 +356,9 @@ void VoiceCapture_RecordStop( void ) } +void VoiceCapture_Shutdown( void ) +{ + +} + #endif diff --git a/engine/platform/platform.h b/engine/platform/platform.h index 51c5c3fd..ad0c546d 100644 --- a/engine/platform/platform.h +++ b/engine/platform/platform.h @@ -162,6 +162,7 @@ void SNDDMA_Activate( qboolean active ); // pause audio // void SNDDMA_LockSound( void ); // unused // void SNDDMA_UnlockSound( void ); // unused qboolean VoiceCapture_Init( void ); +void VoiceCapture_Shutdown( void ); qboolean VoiceCapture_RecordStart( void ); void VoiceCapture_RecordStop( void ); diff --git a/engine/platform/sdl/s_sdl.c b/engine/platform/sdl/s_sdl.c index 39526e2a..51e303f5 100644 --- a/engine/platform/sdl/s_sdl.c +++ b/engine/platform/sdl/s_sdl.c @@ -44,7 +44,7 @@ so it can unlock and free the data block after it has been played. ======================================================================= */ static int sdl_dev; -static SDL_AudioDeviceID in_dev; +static SDL_AudioDeviceID in_dev = 0; static SDL_AudioFormat sdl_format; //static qboolean snd_firsttime = true; @@ -259,7 +259,7 @@ qboolean VoiceCapture_Init( void ) in_dev = SDL_OpenAudioDevice( NULL, SDL_TRUE, &wanted, &spec, 0 ); - if( SDLash_IsAudioError( in_dev ) ) + if( SDLash_IsAudioError( in_dev )) { Con_Printf( "VoiceCapture_Init: error creating capture device (%s)\n", SDL_GetError() ); return false; @@ -288,7 +288,21 @@ VoiceCapture_RecordStop */ void VoiceCapture_RecordStop( void ) { - SDL_PauseAudioDevice( in_dev, SDL_TRUE ); + if( in_dev ) + SDL_PauseAudioDevice( in_dev, SDL_TRUE ); +} + +/* +========== +VoiceCapture_Shutdown +========== +*/ +void VoiceCapture_Shutdown( void ) +{ + if( !in_dev ) + return; + + SDL_CloseAudioDevice( in_dev ); } #endif // XASH_SOUND == SOUND_SDL diff --git a/engine/platform/stub/s_stub.c b/engine/platform/stub/s_stub.c index bb1684b5..53c12543 100644 --- a/engine/platform/stub/s_stub.c +++ b/engine/platform/stub/s_stub.c @@ -109,5 +109,10 @@ void VoiceCapture_RecordStop( void ) } +void VoiceCapture_Shutdown( void ) +{ + +} + #endif #endif From 0b5067891240b3dd87a5c81c6408932b11c52630 Mon Sep 17 00:00:00 2001 From: Alibek Omarov Date: Sat, 20 Aug 2022 03:56:54 +0300 Subject: [PATCH 064/490] engine: client: voice: refactor, fix issues when missing mic disables voice chat, fix few possible crashes and memory leaks --- engine/client/cl_parse.c | 2 +- engine/client/voice.c | 403 +++++++++++++++++++++++++++++++-------- engine/client/voice.h | 6 +- 3 files changed, 323 insertions(+), 88 deletions(-) diff --git a/engine/client/cl_parse.c b/engine/client/cl_parse.c index 070b2b9a..86d32c28 100644 --- a/engine/client/cl_parse.c +++ b/engine/client/cl_parse.c @@ -1715,7 +1715,7 @@ void CL_ParseVoiceData( sizebuf_t *msg ) return; if( idx == cl.playernum + 1 ) - Voice_StatusAck( &voice.local, VOICE_LOCALPLAYER_INDEX ); + Voice_StatusAck( &voice.local, VOICE_LOOPBACK_INDEX ); else Voice_StatusAck( &voice.players_status[idx], idx ); diff --git a/engine/client/voice.c b/engine/client/voice.c index c447a5d0..2bdf9d7f 100644 --- a/engine/client/voice.c +++ b/engine/client/voice.c @@ -31,6 +31,20 @@ CVAR_DEFINE_AUTO( voice_avggain, "0.5", FCVAR_PRIVILEGED|FCVAR_ARCHIVE, "automat 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" ); +/* +=============================================================================== + + OPUS INTEGRATION + +=============================================================================== +*/ + +/* +========================= +Voice_GetBandwithTypeName + +========================= +*/ static const char* Voice_GetBandwidthTypeName( int bandwidthType ) { switch( bandwidthType ) @@ -44,6 +58,12 @@ static const char* Voice_GetBandwidthTypeName( int bandwidthType ) } } +/* +========================= +Voice_CodecInfo_f + +========================= +*/ static void Voice_CodecInfo_f( void ) { int encoderComplexity; @@ -63,31 +83,26 @@ static void Voice_CodecInfo_f( void ) Con_Printf( "Encoder:\n" ); Con_Printf( " Bitrate: %.3f kbps\n", encoderBitrate / 1000.0f ); Con_Printf( " Complexity: %d\n", encoderComplexity ); - Con_Printf( " Bandwidth: %s", Voice_GetBandwidthTypeName( encoderBandwidthType )); - Con_Printf( "\n" ); + Con_Printf( " Bandwidth: %s\n", Voice_GetBandwidthTypeName( encoderBandwidthType )); } -void Voice_RegisterCvars( void ) -{ - Cvar_RegisterVariable( &voice_enable ); - Cvar_RegisterVariable( &voice_loopback ); - Cvar_RegisterVariable( &voice_scale ); - Cvar_RegisterVariable( &voice_avggain ); - Cvar_RegisterVariable( &voice_maxgain ); - Cvar_RegisterVariable( &voice_inputfromfile ); - Cmd_AddClientCommand( "voice_codecinfo", Voice_CodecInfo_f ); -} - -static void Voice_Status( int entindex, qboolean bTalking ) -{ - clgame.dllFuncs.pfnVoiceStatus( entindex, bTalking ); -} +/* +========================= +Voice_GetFrameSize +========================= +*/ static uint Voice_GetFrameSize( float durationMsec ) { return voice.channels * voice.width * (( float )voice.samplerate / ( 1000.0f / durationMsec )); } +/* +========================= +Voice_ApplyGainAdjust + +========================= +*/ static void Voice_ApplyGainAdjust( opus_int16 *samples, int count ) { float gain, modifiedMax; @@ -129,35 +144,44 @@ static void Voice_ApplyGainAdjust( opus_int16 *samples, int count ) } } -// parameters currently unused -qboolean Voice_Init( const char *pszCodecName, int quality ) +/* +========================= +Voice_InitOpusDecoder + +========================= +*/ +static qboolean Voice_InitOpusDecoder( void ) { int err; + voice.decoder = opus_decoder_create( voice.samplerate, voice.channels, &err ); - if( !voice_enable.value ) - return false; - - Voice_DeInit(); - - voice.initialized = false; - voice.channels = 1; - voice.width = 2; - voice.samplerate = SOUND_48k; - voice.frame_size = Voice_GetFrameSize( 40.0f ); - voice.autogain.block_size = 128; - - if( !VoiceCapture_Init() ) + if( !voice.decoder ) { - Voice_DeInit(); - return voice.initialized; + Con_Printf( S_ERROR "Can't create Opus encoder: %s", opus_strerror( err )); + return false; } - voice.encoder = opus_encoder_create( voice.samplerate, voice.channels, OPUS_APPLICATION_VOIP, &err ); + return true; +} - if( err != OPUS_OK ) - return voice.initialized; +/* +========================= +Voice_InitOpusEncoder - voice.decoder = opus_decoder_create( voice.samplerate, voice.channels, &err ); +========================= +*/ +static qboolean Voice_InitOpusEncoder( int quality ) +{ + int err; + int app = quality == 5 ? OPUS_APPLICATION_AUDIO : OPUS_APPLICATION_VOIP; + + voice.encoder = opus_encoder_create( voice.samplerate, voice.channels, app, &err ); + + if( !voice.encoder ) + { + Con_Printf( S_ERROR "Can't create Opus encoder: %s", opus_strerror( err )); + return false; + } switch( quality ) { @@ -183,31 +207,46 @@ qboolean Voice_Init( const char *pszCodecName, int quality ) break; } - if (quality == 5) { - opus_encoder_ctl( voice.encoder, OPUS_SET_APPLICATION( OPUS_APPLICATION_AUDIO )); - } - else { - opus_encoder_ctl( voice.encoder, OPUS_SET_APPLICATION( OPUS_APPLICATION_VOIP )); - } - - voice.initialized = (err == OPUS_OK); - return voice.initialized; + return true; } -void Voice_DeInit( void ) +/* +========================= +Voice_ShutdownOpusDecoder + +========================= +*/ +static void Voice_ShutdownOpusDecoder( void ) { - if( !voice.initialized ) - return; - - Voice_RecordStop(); - - opus_encoder_destroy( voice.encoder ); - opus_decoder_destroy( voice.decoder ); - - voice.initialized = false; + if( voice.decoder ) + { + opus_decoder_destroy( voice.decoder ); + voice.decoder = NULL; + } } -static uint Voice_GetCompressedData( byte *out, uint maxsize, uint *frames ) +/* +========================= +Voice_ShutdownOpusEncoder + +========================= +*/ +static void Voice_ShutdownOpusEncoder( void ) +{ + if( voice.encoder ) + { + opus_encoder_destroy( voice.encoder ); + voice.encoder = NULL; + } +} + +/* +========================= +Voice_GetOpusCompressedData + +========================= +*/ +static uint Voice_GetOpusCompressedData( byte *out, uint maxsize, uint *frames ) { uint ofs, size = 0; @@ -250,6 +289,35 @@ static uint Voice_GetCompressedData( byte *out, uint maxsize, uint *frames ) return size; } +/* +=============================================================================== + + VOICE CHAT INTEGRATION + +=============================================================================== +*/ + +/* +========================= +Voice_Status + +Notify user dll aboit voice transmission +========================= +*/ +static void Voice_Status( int entindex, qboolean bTalking ) +{ + if( cls.state == ca_active && clgame.dllFuncs.pfnVoiceStatus ) + clgame.dllFuncs.pfnVoiceStatus( entindex, bTalking ); +} + +/* +========================= +Voice_StatusTimeout + +Waits few milliseconds and if there was no +voice transmission, sends notification +========================= +*/ static void Voice_StatusTimeout( voice_status_t *status, int entindex, double frametime ) { if( status->talking_ack ) @@ -263,6 +331,14 @@ static void Voice_StatusTimeout( voice_status_t *status, int entindex, double fr } } +/* +========================= +Voice_StatusAck + +Sends notification to user dll and +zeroes timeouts for this client +========================= +*/ void Voice_StatusAck( voice_status_t *status, int playerIndex ) { if( !status->talking_ack ) @@ -272,28 +348,23 @@ void Voice_StatusAck( voice_status_t *status, int playerIndex ) status->talking_timeout = 0.0; } -void Voice_Idle( double frametime ) -{ - int i; - - if( !voice_enable.value ) - { - Voice_DeInit(); - return; - } - - // update local player status first - Voice_StatusTimeout( &voice.local, VOICE_LOCALPLAYER_INDEX, frametime ); - - for( i = 0; i < 32; i++ ) - Voice_StatusTimeout( &voice.players_status[i], i, frametime ); -} +/* +========================= +Voice_IsRecording +========================= +*/ qboolean Voice_IsRecording( void ) { return voice.is_recording; } +/* +========================= +Voice_RecordStop + +========================= +*/ void Voice_RecordStop( void ) { if( input_file ) @@ -303,16 +374,22 @@ void Voice_RecordStop( void ) } voice.input_buffer_pos = 0; - memset( voice.input_buffer, 0, sizeof( voice.input_buffer ) ); + memset( voice.input_buffer, 0, sizeof( voice.input_buffer )); - if( Voice_IsRecording() ) - Voice_Status( -1, false ); + if( Voice_IsRecording( )) + Voice_Status( VOICE_LOCALCLIENT_INDEX, false ); VoiceCapture_RecordStop(); voice.is_recording = false; } +/* +========================= +Voice_RecordStart + +========================= +*/ void Voice_RecordStart( void ) { Voice_RecordStop(); @@ -340,30 +417,64 @@ void Voice_RecordStart( void ) voice.is_recording = VoiceCapture_RecordStart(); if( Voice_IsRecording() ) - Voice_Status( -1, true ); + Voice_Status( VOICE_LOCALCLIENT_INDEX, true ); } +/* +========================= +Voice_Disconnect + +We're disconnected from server +stop recording and notify user dlls +========================= +*/ void Voice_Disconnect( void ) { int i; Voice_RecordStop(); - for( i = 0; i <= 32; i++ ) { - Voice_Status( i, false ); + + if( voice.local.talking_ack ) + { + Voice_Status( VOICE_LOOPBACK_INDEX, false ); + voice.local.talking_ack = false; + } + + for( i = 0; i < MAX_CLIENTS; i++ ) + { + if( voice.players_status[i].talking_ack ) + { + Voice_Status( i, false ); + voice.players_status[i].talking_ack = false; + } } } +/* +========================= +Voice_StartChannel + +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.channels, data, 255 ); } +/* +========================= +Voice_AddIncomingData + +Received encoded voice data, decode it +========================= +*/ void Voice_AddIncomingData( int ent, const byte *data, uint size, uint frames ) { int samples; - if( !voice.initialized ) + if( !voice.decoder ) return; samples = opus_decode( voice.decoder, data, size, (short *)voice.decompress_buffer, voice.frame_size / voice.width * frames, false ); @@ -372,14 +483,21 @@ void Voice_AddIncomingData( int ent, const byte *data, uint size, uint frames ) Voice_StartChannel( samples, voice.decompress_buffer, ent ); } +/* +========================= +CL_AddVoiceToDatagram + +Encode our voice data and send it to server +========================= +*/ void CL_AddVoiceToDatagram( void ) { uint size, frames = 0; - if( cls.state != ca_active || !Voice_IsRecording() ) + if( cls.state != ca_active || !Voice_IsRecording() || !voice.encoder ) return; - size = Voice_GetCompressedData( voice.output_buffer, sizeof( voice.output_buffer ), &frames ); + size = Voice_GetOpusCompressedData( voice.output_buffer, sizeof( voice.output_buffer ), &frames ); if( size > 0 && MSG_GetNumBytesLeft( &cls.datagram ) >= size + 32 ) { @@ -390,3 +508,122 @@ void CL_AddVoiceToDatagram( void ) MSG_WriteBytes( &cls.datagram, voice.output_buffer, size ); } } + +/* +========================= +Voice_RegisterCvars + +Register voice related cvars and commands +========================= +*/ +void Voice_RegisterCvars( void ) +{ + Cvar_RegisterVariable( &voice_enable ); + Cvar_RegisterVariable( &voice_loopback ); + Cvar_RegisterVariable( &voice_scale ); + Cvar_RegisterVariable( &voice_avggain ); + Cvar_RegisterVariable( &voice_maxgain ); + Cvar_RegisterVariable( &voice_inputfromfile ); + Cmd_AddClientCommand( "voice_codecinfo", Voice_CodecInfo_f ); +} + +/* +========================= +Voice_Shutdown + +Completely shutdown the voice subsystem +========================= +*/ +static void Voice_Shutdown( void ) +{ + int i; + + Voice_RecordStop(); + Voice_ShutdownOpusEncoder(); + Voice_ShutdownOpusDecoder(); + VoiceCapture_Shutdown(); + + if( voice.local.talking_ack ) + Voice_Status( VOICE_LOOPBACK_INDEX, false ); + + for( i = 0; i < MAX_CLIENTS; i++ ) + { + if( voice.players_status[i].talking_ack ) + Voice_Status( i, false ); + } + + memset( &voice, 0, sizeof( voice )); +} + +/* +========================= +Voice_Idle + +Run timeout for all clients +========================= +*/ +void Voice_Idle( double frametime ) +{ + int i; + + if( !voice_enable.value ) + { + Voice_Shutdown(); + return; + } + + // update local player status first + Voice_StatusTimeout( &voice.local, VOICE_LOOPBACK_INDEX, frametime ); + + for( i = 0; i < MAX_CLIENTS; i++ ) + Voice_StatusTimeout( &voice.players_status[i], i, frametime ); +} + +/* +========================= +Voice_Init + +Initialize the voice subsystem +========================= +*/ +qboolean Voice_Init( const char *pszCodecName, int quality ) +{ + if( !voice_enable.value ) + return false; + + Voice_Shutdown(); + + if( Q_strcmp( pszCodecName, "opus" )) + { + Con_Printf( S_ERROR "Server requested unsupported codec: %s", pszCodecName ); + return false; + } + + voice.initialized = false; + voice.channels = 1; + voice.width = 2; + voice.samplerate = SOUND_48k; + voice.frame_size = Voice_GetFrameSize( 40.0f ); + voice.autogain.block_size = 128; + + if( !Voice_InitOpusDecoder( )) + { + // 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; + } + + // we can hear others players, so it's fine to fail now + voice.initialized = true; + + if( !Voice_InitOpusEncoder( quality ) || !VoiceCapture_Init() ) + { + Voice_ShutdownOpusEncoder(); + Con_Printf( S_WARN "Other players will not be able to hear you.\n" ); + return true; + } + + return true; +} diff --git a/engine/client/voice.h b/engine/client/voice.h index c6d93b4e..70d35b45 100644 --- a/engine/client/voice.h +++ b/engine/client/voice.h @@ -20,12 +20,11 @@ GNU General Public License for more details. #include "protocol.h" // MAX_CLIENTS #include "sound.h" -extern convar_t voice_scale; - typedef struct OpusDecoder OpusDecoder; typedef struct OpusEncoder OpusEncoder; -#define VOICE_LOCALPLAYER_INDEX (-2) +#define VOICE_LOOPBACK_INDEX (-2) +#define VOICE_LOCALCLIENT_INDEX (-1) typedef struct voice_status_s { @@ -73,7 +72,6 @@ void CL_AddVoiceToDatagram( void ); void Voice_RegisterCvars( void ); qboolean Voice_Init( const char *pszCodecName, int quality ); -void Voice_DeInit( void ); void Voice_Idle( double frametime ); qboolean Voice_IsRecording( void ); void Voice_RecordStop( void ); From 8630ef2c674d2b5b14c5d3c9b465922395f2f006 Mon Sep 17 00:00:00 2001 From: Alibek Omarov Date: Sat, 20 Aug 2022 06:13:33 +0300 Subject: [PATCH 065/490] engine: client: voice: allow using inputfromfile when microphone isn't connected --- engine/client/voice.c | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/engine/client/voice.c b/engine/client/voice.c index 2bdf9d7f..095ded3b 100644 --- a/engine/client/voice.c +++ b/engine/client/voice.c @@ -618,12 +618,14 @@ qboolean Voice_Init( const char *pszCodecName, int quality ) // we can hear others players, so it's fine to fail now voice.initialized = true; - if( !Voice_InitOpusEncoder( quality ) || !VoiceCapture_Init() ) + if( !Voice_InitOpusEncoder( quality )) { - Voice_ShutdownOpusEncoder(); Con_Printf( S_WARN "Other players will not be able to hear you.\n" ); - return true; + return false; } + if( !VoiceCapture_Init( )) + Con_Printf( S_WARN "No microphone is available.\n" ); + return true; } From d3437c70bd3e5835773dde133649603a7841d080 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=D0=92=D0=BB=D0=B0=D0=B4=D0=B8=D1=81=D0=BB=D0=B0=D0=B2=20?= =?UTF-8?q?=D0=A1=D1=83=D1=85=D0=BE=D0=B2?= <22411953+Vladislav4KZ@users.noreply.github.com> Date: Sat, 20 Aug 2022 14:19:14 +0600 Subject: [PATCH 066/490] engine: soundlib: fix wrong sample rate unit --- engine/common/soundlib/snd_utils.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/engine/common/soundlib/snd_utils.c b/engine/common/soundlib/snd_utils.c index e6ffd977..d681ec05 100644 --- a/engine/common/soundlib/snd_utils.c +++ b/engine/common/soundlib/snd_utils.c @@ -233,7 +233,7 @@ qboolean Sound_ResampleInternal( wavdata_t *sc, int inrate, int inwidth, int out } } - Con_Reportf( "Sound_Resample: from[%d bit %d kHz] to [%d bit %d kHz]\n", inwidth * 8, inrate, outwidth * 8, outrate ); + Con_Reportf( "Sound_Resample: from[%d bit %d Hz] to [%d bit %d Hz]\n", inwidth * 8, inrate, outwidth * 8, outrate ); } sc->rate = outrate; From 9f9141823ae95392b8b4ef9bb663d1cf7aa157ff Mon Sep 17 00:00:00 2001 From: SNMetamorph <25657591+SNMetamorph@users.noreply.github.com> Date: Sat, 20 Aug 2022 13:59:14 +0400 Subject: [PATCH 067/490] engine: added audio backend print to s_info command --- engine/client/s_main.c | 3 ++- engine/client/sound.h | 7 ++++--- engine/platform/android/snd_opensles.c | 2 +- engine/platform/linux/s_alsa.c | 4 ++-- engine/platform/sdl/s_sdl.c | 4 +++- 5 files changed, 12 insertions(+), 8 deletions(-) diff --git a/engine/client/s_main.c b/engine/client/s_main.c index 5e1f8429..5a77e24f 100644 --- a/engine/client/s_main.c +++ b/engine/client/s_main.c @@ -1851,7 +1851,7 @@ S_SoundInfo_f */ void S_SoundInfo_f( void ) { - Con_Printf( "Audio: DirectSound\n" ); + Con_Printf( "Audio backend: %s\n", dma.backendName ); Con_Printf( "%5d channel(s)\n", 2 ); Con_Printf( "%5d samples\n", dma.samples ); Con_Printf( "%5d bits/sample\n", 16 ); @@ -1927,6 +1927,7 @@ qboolean S_Init( void ) Cmd_AddCommand( "spk", S_SayReliable_f, "reliable play a specified sententce" ); Cmd_AddCommand( "speak", S_Say_f, "playing a specified sententce" ); + dma.backendName = "None"; if( !SNDDMA_Init( ) ) { Con_Printf( "Audio: sound system can't be initialized\n" ); diff --git a/engine/client/sound.h b/engine/client/sound.h index 326f842b..c290fca8 100644 --- a/engine/client/sound.h +++ b/engine/client/sound.h @@ -114,10 +114,11 @@ typedef struct snd_format_s typedef struct { snd_format_t format; - int samples; // mono samples in buffer - int samplepos; // in mono samples - byte *buffer; + int samples; // mono samples in buffer + int samplepos; // in mono samples + byte *buffer; qboolean initialized; // sound engine is active + const char *backendName; } dma_t; #include "vox.h" diff --git a/engine/platform/android/snd_opensles.c b/engine/platform/android/snd_opensles.c index fb729025..9916a269 100644 --- a/engine/platform/android/snd_opensles.c +++ b/engine/platform/android/snd_opensles.c @@ -209,7 +209,7 @@ qboolean SNDDMA_Init( void ) } Msg( "OpenSL ES audio initialized.\n" ); - + dma.backendName = "OpenSL ES"; return true; } diff --git a/engine/platform/linux/s_alsa.c b/engine/platform/linux/s_alsa.c index 806e9151..eb738975 100644 --- a/engine/platform/linux/s_alsa.c +++ b/engine/platform/linux/s_alsa.c @@ -185,12 +185,12 @@ qboolean SNDDMA_Init( void ) } dma.buffer = Z_Malloc( samples * 2 ); //allocate pcm frame buffer - dma.samplepos = 0; - dma.samples = samples; dma.format.width = 2; dma.initialized = 1; + dma.backendName = "ALSA"; + snd_pcm_prepare( s_alsa.pcm_handle ); snd_pcm_writei( s_alsa.pcm_handle, dma.buffer, 2 * s_alsa.period_size ); snd_pcm_start( s_alsa.pcm_handle ); diff --git a/engine/platform/sdl/s_sdl.c b/engine/platform/sdl/s_sdl.c index 51e303f5..2a0b9d41 100644 --- a/engine/platform/sdl/s_sdl.c +++ b/engine/platform/sdl/s_sdl.c @@ -46,6 +46,7 @@ so it can unlock and free the data block after it has been played. static int sdl_dev; static SDL_AudioDeviceID in_dev = 0; static SDL_AudioFormat sdl_format; +static char sdl_backend_name[32]; //static qboolean snd_firsttime = true; //static qboolean primary_format_set; @@ -139,8 +140,9 @@ qboolean SNDDMA_Init( void ) sdl_format = obtained.format; Con_Printf( "Using SDL audio driver: %s @ %d Hz\n", SDL_GetCurrentAudioDriver( ), obtained.freq ); - + Q_snprintf( sdl_backend_name, sizeof( sdl_backend_name ), "SDL (%s)", SDL_GetCurrentAudioDriver( )); dma.initialized = true; + dma.backendName = sdl_backend_name; SNDDMA_Activate( true ); From cb34c23844364acb5149ce1e1bb1a375fe0d4f1b Mon Sep 17 00:00:00 2001 From: Alibek Omarov Date: Sun, 21 Aug 2022 17:27:17 +0300 Subject: [PATCH 068/490] common: increase MAX_MAP_MODELS to 1024, to match PrimeXT compilers --- common/bspfile.h | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/common/bspfile.h b/common/bspfile.h index 8087f2dc..1fa5721a 100644 --- a/common/bspfile.h +++ b/common/bspfile.h @@ -73,7 +73,8 @@ BRUSH MODELS #define MAX_MAP_FACES 262144 // can be increased without problems #define MAX_MAP_MARKSURFACES 524288 // can be increased without problems #else -#define MAX_MAP_MODELS 768 // embedded models +// increased to match PrimeXT compilers +#define MAX_MAP_MODELS 1024 // embedded models #define MAX_MAP_ENTSTRING 0x100000 // 1 Mb should be enough #define MAX_MAP_PLANES 65536 // can be increased without problems #define MAX_MAP_NODES 32767 // because negative shorts are leafs From f435a81c97f9f01986c69f7461d9dbc48691466d Mon Sep 17 00:00:00 2001 From: Alibek Omarov Date: Mon, 22 Aug 2022 10:14:01 +0300 Subject: [PATCH 069/490] engine: soundlib: rewrite sfx resampler, fix possible crash if sfx is too long - make same rate and same width resamples noop, as everything signed now - minimize comparisons in loop body --- engine/common/soundlib/snd_utils.c | 195 ++++++++++++++++------------- 1 file changed, 108 insertions(+), 87 deletions(-) diff --git a/engine/common/soundlib/snd_utils.c b/engine/common/soundlib/snd_utils.c index d681ec05..98626066 100644 --- a/engine/common/soundlib/snd_utils.c +++ b/engine/common/soundlib/snd_utils.c @@ -126,31 +126,7 @@ uint GAME_EXPORT Sound_GetApproxWavePlayLen( const char *filepath ) return msecs; } -/* -================ -Sound_ConvertToSigned - -Convert unsigned data to signed -================ -*/ -void Sound_ConvertToSigned( const byte *data, int channels, int samples ) -{ - int i; - - if( channels == 2 ) - { - for( i = 0; i < samples; i++ ) - { - ((signed char *)sound.tempbuffer)[i*2+0] = (int)((byte)(data[i*2+0]) - 128); - ((signed char *)sound.tempbuffer)[i*2+1] = (int)((byte)(data[i*2+1]) - 128); - } - } - else - { - for( i = 0; i < samples; i++ ) - ((signed char *)sound.tempbuffer)[i] = (int)((unsigned char)(data[i]) - 128); - } -} +#define drint( v ) (int)( v + 0.5 ) /* ================ @@ -161,13 +137,15 @@ We need convert sound to signed even if nothing to resample */ qboolean Sound_ResampleInternal( wavdata_t *sc, int inrate, int inwidth, int outrate, int outwidth ) { - float stepscale; - int outcount, srcsample; - int i, sample, sample2, samplefrac, fracstep; - byte *data; + double stepscale, j; + int outcount; + int i; + qboolean handled = false; - data = sc->buffer; - stepscale = (float)inrate / outrate; // this is usually 0.5, 1, or 2 + if( inrate == outrate && inwidth == outwidth ) + return false; + + stepscale = (double)inrate / outrate; // this is usually 0.5, 1, or 2 outcount = sc->samples / stepscale; sc->size = outcount * outwidth * sc->channels; @@ -177,69 +155,112 @@ qboolean Sound_ResampleInternal( wavdata_t *sc, int inrate, int inwidth, int out if( sc->loopStart != -1 ) sc->loopStart = sc->loopStart / stepscale; - // resample / decimate to the current source rate - if( stepscale == 1.0f && inwidth == 1 && outwidth == 1 ) + if( inrate == outrate ) { - Sound_ConvertToSigned( data, sc->channels, outcount ); + 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; + + 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 ) + { + int16_t *data = (int16_t *)sc->buffer; + + 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] / 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; + } + } + } + + if( handled ) + Con_Reportf( "Sound_Resample: from [%d bit %d Hz] to [%d bit %d Hz]\n", inwidth * 8, inrate, outwidth * 8, outrate ); else - { - // general case - samplefrac = 0; - fracstep = stepscale * 256; - - if( sc->channels == 2 ) - { - for( i = 0; i < outcount; i++ ) - { - srcsample = samplefrac >> 8; - samplefrac += fracstep; - - if( inwidth == 2 ) - { - sample = ((short *)data)[srcsample*2+0]; - sample2 = ((short *)data)[srcsample*2+1]; - } - else - { - sample = (int)((char)(data[srcsample*2+0])) << 8; - sample2 = (int)((char)(data[srcsample*2+1])) << 8; - } - - if( outwidth == 2 ) - { - ((short *)sound.tempbuffer)[i*2+0] = sample; - ((short *)sound.tempbuffer)[i*2+1] = sample2; - } - else - { - ((signed char *)sound.tempbuffer)[i*2+0] = sample >> 8; - ((signed char *)sound.tempbuffer)[i*2+1] = sample2 >> 8; - } - } - } - else - { - for( i = 0; i < outcount; i++ ) - { - srcsample = samplefrac >> 8; - samplefrac += fracstep; - - if( inwidth == 2 ) sample = ((short *)data)[srcsample]; - else sample = (int)( (char)(data[srcsample])) << 8; - - if( outwidth == 2 ) ((short *)sound.tempbuffer)[i] = sample; - else ((signed char *)sound.tempbuffer)[i] = sample >> 8; - } - } - - Con_Reportf( "Sound_Resample: from[%d bit %d Hz] to [%d bit %d Hz]\n", inwidth * 8, inrate, outwidth * 8, outrate ); - } + Con_Reportf( 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; - return true; + return handled; } qboolean Sound_Process( wavdata_t **wav, int rate, int width, uint flags ) From e9ae6d08b5896fb4fff4454ebecd81dad152ac30 Mon Sep 17 00:00:00 2001 From: SNMetamorph <25657591+SNMetamorph@users.noreply.github.com> Date: Mon, 22 Aug 2022 11:23:13 +0400 Subject: [PATCH 070/490] engine: client: enable interpolation of local player angles --- engine/client/cl_frame.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/engine/client/cl_frame.c b/engine/client/cl_frame.c index dd1777e6..81976384 100644 --- a/engine/client/cl_frame.c +++ b/engine/client/cl_frame.c @@ -534,7 +534,7 @@ void CL_ComputePlayerOrigin( cl_entity_t *ent ) vec3_t origin; vec3_t angles; - if( !ent->player || ent->index == ( cl.playernum + 1 )) + if( !ent->player ) return; if( cl_nointerp->value > 0.f ) @@ -1094,6 +1094,9 @@ void CL_LinkPlayers( frame_t *frame ) if ( i == cl.playernum ) { + // using interpolation only for local player angles + CL_ComputePlayerOrigin( ent ); + if( cls.demoplayback == DEMO_QUAKE1 ) VectorLerp( ent->prevstate.origin, cl.lerpFrac, ent->curstate.origin, cl.simorg ); VectorCopy( cl.simorg, ent->origin ); From 3c682507e7764b73864006dd386ca854d06c783f Mon Sep 17 00:00:00 2001 From: Alibek Omarov Date: Mon, 22 Aug 2022 11:44:13 +0300 Subject: [PATCH 071/490] engine: client: voice: notify client.dll about localplayer twice, through special loopback index and normal index --- engine/client/cl_parse.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/engine/client/cl_parse.c b/engine/client/cl_parse.c index 86d32c28..de8f46f3 100644 --- a/engine/client/cl_parse.c +++ b/engine/client/cl_parse.c @@ -1714,10 +1714,11 @@ void CL_ParseVoiceData( sizebuf_t *msg ) if ( idx <= 0 || idx > cl.maxclients ) return; + // must notify through as both local player and normal client if( idx == cl.playernum + 1 ) Voice_StatusAck( &voice.local, VOICE_LOOPBACK_INDEX ); - else - Voice_StatusAck( &voice.players_status[idx], idx ); + + Voice_StatusAck( &voice.players_status[idx], idx ); if ( !size ) return; From 3e1db432df0087b48a32f6e6e77a902916d11218 Mon Sep 17 00:00:00 2001 From: Alibek Omarov Date: Tue, 23 Aug 2022 19:15:50 +0300 Subject: [PATCH 072/490] engine: network: fix IPv4 private address checks according to RFC1918 Thanks to @Mr0maks for the fix --- engine/common/net_ws.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/engine/common/net_ws.c b/engine/common/net_ws.c index 999fb705..8f558881 100644 --- a/engine/common/net_ws.c +++ b/engine/common/net_ws.c @@ -616,7 +616,7 @@ qboolean NET_IsReservedAdr( netadr_t a ) if( a.type == NA_IP ) { - if( a.ip[0] == 10 || a.ip[0] == 127 ) + if( a.ip[0] == 10 ) return true; if( a.ip[0] == 172 && a.ip[1] >= 16 ) @@ -626,7 +626,7 @@ qboolean NET_IsReservedAdr( netadr_t a ) return true; } - if( a.ip[0] == 192 && a.ip[1] >= 168 ) + if( a.ip[0] == 192 && a.ip[1] == 168 ) return true; } From ac05acf6dcdc79524fdcbf50e7187713acdabdbe Mon Sep 17 00:00:00 2001 From: Alibek Omarov Date: Tue, 23 Aug 2022 20:04:59 +0300 Subject: [PATCH 073/490] engine: network: I'm fucking blind --- engine/common/net_ws.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/engine/common/net_ws.c b/engine/common/net_ws.c index 8f558881..53f3418f 100644 --- a/engine/common/net_ws.c +++ b/engine/common/net_ws.c @@ -616,7 +616,7 @@ qboolean NET_IsReservedAdr( netadr_t a ) if( a.type == NA_IP ) { - if( a.ip[0] == 10 ) + if( a.ip[0] == 10 || a.ip[0] == 127 ) return true; if( a.ip[0] == 172 && a.ip[1] >= 16 ) From 7341a6b020259e99987452b4974ba829945c39f7 Mon Sep 17 00:00:00 2001 From: Alibek Omarov Date: Thu, 25 Aug 2022 18:22:43 +0300 Subject: [PATCH 074/490] engine: client: add old GoldSrc feature where ConsolePrint could print to notification zone, similar to Con_NPrintf( 0, ... ) --- engine/client/cl_game.c | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/engine/client/cl_game.c b/engine/client/cl_game.c index 1453d859..58ed7ca1 100644 --- a/engine/client/cl_game.c +++ b/engine/client/cl_game.c @@ -1936,7 +1936,14 @@ prints directly into console (can skip notify) */ static void GAME_EXPORT pfnConsolePrint( const char *string ) { - Con_Printf( "%s", string ); + if( !COM_CheckString( string )) + return; + + // WON GoldSrc behavior + if( string[0] != '1' ) + Con_Printf( "%s", string ); + else + Con_NPrintf( 0, "%s", string + 1 ); } /* From 7f1bb9b4a62ebdfaefb8e72a5c73aec90c89c69b Mon Sep 17 00:00:00 2001 From: Alibek Omarov Date: Thu, 25 Aug 2022 19:14:52 +0300 Subject: [PATCH 075/490] public: introduce Q_strnicmpext function The goal is to provide both string compare with fixed length and simple pattern match --- public/crtlib.c | 12 ++++++++++-- public/crtlib.h | 3 ++- 2 files changed, 12 insertions(+), 3 deletions(-) diff --git a/public/crtlib.c b/public/crtlib.c index 652ae917..6dba1ec4 100644 --- a/public/crtlib.c +++ b/public/crtlib.c @@ -338,12 +338,15 @@ static qboolean Q_starcmp( const char *pattern, const char *text ) } } -qboolean Q_stricmpext( const char *pattern, const char *text ) +qboolean Q_strnicmpext( const char *pattern, const char *text, size_t minimumlength ) { + size_t i = 0; char c; while(( c = *pattern++ ) != '\0' ) { + i++; + switch( c ) { case '?': @@ -361,7 +364,12 @@ qboolean Q_stricmpext( const char *pattern, const char *text ) return false; } } - return ( *text == '\0' ); + return ( *text == '\0' ) || i == minimumlength; +} + +qboolean Q_stricmpext( const char *pattern, const char *text ) +{ + return Q_strnicmpext( pattern, text, ~((size_t)0) ); } const char* Q_timestamp( int format ) diff --git a/public/crtlib.h b/public/crtlib.h index 84027ea6..f3013a8f 100644 --- a/public/crtlib.h +++ b/public/crtlib.h @@ -76,7 +76,8 @@ float Q_atof( const char *str ); void Q_atov( float *vec, const char *str, size_t siz ); #define Q_strchr strchr #define Q_strrchr strrchr -qboolean Q_stricmpext( const char *s1, const char *s2 ); +qboolean Q_stricmpext( const char *pattern, const char *text ); +qboolean Q_strnicmpext( const char *pattern, const char *text, size_t minimumlen ); const char *Q_timestamp( int format ); #define Q_vsprintf( buffer, format, args ) Q_vsnprintf( buffer, 99999, format, args ) int Q_vsnprintf( char *buffer, size_t buffersize, const char *format, va_list args ); From ca2a6635b62ed20fa285e416762f3d79a9626566 Mon Sep 17 00:00:00 2001 From: Alibek Omarov Date: Thu, 25 Aug 2022 19:16:40 +0300 Subject: [PATCH 076/490] engine: common: fix cmdlist and cvarlist to match the beginning of command or cvar --- engine/common/cmd.c | 12 ++++++++---- engine/common/cvar.c | 6 +++++- 2 files changed, 13 insertions(+), 5 deletions(-) diff --git a/engine/common/cmd.c b/engine/common/cmd.c index 1df7287b..9eeb1c68 100644 --- a/engine/common/cmd.c +++ b/engine/common/cmd.c @@ -1171,17 +1171,21 @@ void Cmd_List_f( void ) { cmd_t *cmd; int i = 0; - const char *match; + size_t matchlen = 0; + const char *match = NULL; - if( Cmd_Argc() > 1 ) match = Cmd_Argv( 1 ); - else match = NULL; + if( Cmd_Argc() > 1 ) + { + match = Cmd_Argv( 1 ); + matchlen = Q_strlen( match ); + } for( cmd = cmd_functions; cmd; cmd = cmd->next ) { if( cmd->name[0] == '@' ) continue; // never show system cmds - if( match && !Q_stricmpext( match, cmd->name )) + if( match && !Q_strnicmpext( match, cmd->name, matchlen )) continue; Con_Printf( " %-*s ^3%s^7\n", 32, cmd->name, cmd->desc ); diff --git a/engine/common/cvar.c b/engine/common/cvar.c index 0e33ef31..752a6846 100644 --- a/engine/common/cvar.c +++ b/engine/common/cvar.c @@ -934,16 +934,20 @@ void Cvar_List_f( void ) const char *match = NULL; char *value; int count = 0; + size_t matchlen = 0; if( Cmd_Argc() > 1 ) + { match = Cmd_Argv( 1 ); + matchlen = Q_strlen( match ); + } for( var = cvar_vars; var; var = var->next ) { if( var->name[0] == '@' ) continue; // never shows system cvars - if( match && !Q_stricmpext( match, var->name )) + if( match && !Q_strnicmpext( match, var->name, matchlen )) continue; if( Q_colorstr( var->string )) From a7d4cafe10166a33645a1f14247a43b3858c97b0 Mon Sep 17 00:00:00 2001 From: Alibek Omarov Date: Thu, 25 Aug 2022 19:33:16 +0300 Subject: [PATCH 077/490] wscript: finally disable building opus for dedicated --- engine/wscript | 5 +++-- wscript | 2 +- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/engine/wscript b/engine/wscript index 7f3bdfce..3df51568 100644 --- a/engine/wscript +++ b/engine/wscript @@ -107,7 +107,7 @@ def configure(conf): def build(bld): is_cxx_link = False - libs = [ 'public', 'dllemu', 'opus' ] + libs = [ 'public', 'dllemu' ] # basic build: dedicated only source = bld.path.ant_glob([ @@ -165,7 +165,8 @@ def build(bld): 'client/*.c', 'client/vgui/*.c', 'client/avi/*.c']) - + libs += ['opus'] + includes = ['common', 'server', 'client', 'client/vgui', 'tests', '.', '../public', '../common', '../filesystem', '../pm_shared' ] if bld.env.SINGLE_BINARY: diff --git a/wscript b/wscript index 04a9d90e..fb335fb6 100644 --- a/wscript +++ b/wscript @@ -47,7 +47,7 @@ SUBDIRS = [ Subproject('game_launch', lambda x: not x.env.SINGLE_BINARY and x.env.DEST_OS != 'android'), # disable only by external dependency presense - Subproject('3rdparty/opus', lambda x: not x.env.HAVE_SYSTEM_OPUS), + Subproject('3rdparty/opus', lambda x: not x.env.HAVE_SYSTEM_OPUS and not x.env.DEDICATED), # enabled optionally Subproject('utils/mdldec', lambda x: x.env.ENABLE_UTILS), From 9d49985100771446d97d0f59b0edbdc7d9963549 Mon Sep 17 00:00:00 2001 From: Alibek Omarov Date: Thu, 25 Aug 2022 19:34:18 +0300 Subject: [PATCH 078/490] scripts: gha: build tarball for dedicated server --- scripts/gha/build_linux.sh | 19 ++++++++++++++++--- 1 file changed, 16 insertions(+), 3 deletions(-) diff --git a/scripts/gha/build_linux.sh b/scripts/gha/build_linux.sh index a98afa32..7a0ed5d2 100755 --- a/scripts/gha/build_linux.sh +++ b/scripts/gha/build_linux.sh @@ -6,6 +6,10 @@ APP=xash3d-fwgs APPDIR=$APP.AppDir APPIMAGE=$APP-$ARCH.AppImage +DS=xashds-linux +DSDIR=$DS-$ARCH +DSTARGZ=$DS-$ARCH.tar.gz + build_sdl2() { cd "$BUILDDIR"/SDL2_src || die @@ -103,14 +107,23 @@ EOF ./appimagetool.AppImage "$APPDIR" "$APPIMAGE" } +build_dedicated_tarball() +{ + cd "$BUILDDIR" || die + + ./waf install --destdir=$DSDIR || die + + tar -czvf $DSTARGZ $DSDIR +} + mkdir -p artifacts/ rm -rf build # clean-up build directory build_engine dedicated -mv build/engine/xash artifacts/xashds-linux-$ARCH +build_dedicated_tarball +mv $DSTARGZ artifacts/ -rm -rf build build_sdl2 -build_engine full +build_engine full # don't rebuild some common parts twice build_appimage mv $APPIMAGE artifacts/ From fce8afabfb0629d88ae02c87e62cb27e666c7010 Mon Sep 17 00:00:00 2001 From: Alibek Omarov Date: Thu, 25 Aug 2022 20:33:16 +0300 Subject: [PATCH 079/490] filesystem: allow acquiring C interface through CreateInterface export --- filesystem/VFileSystem009.cpp | 12 ++++++++++++ filesystem/filesystem.c | 2 +- filesystem/filesystem.h | 1 + filesystem/filesystem_internal.h | 1 + 4 files changed, 15 insertions(+), 1 deletion(-) diff --git a/filesystem/VFileSystem009.cpp b/filesystem/VFileSystem009.cpp index ffcffe40..7bfee6ba 100644 --- a/filesystem/VFileSystem009.cpp +++ b/filesystem/VFileSystem009.cpp @@ -492,6 +492,18 @@ extern "C" void EXPORT *CreateInterface( const char *interface, int *retval ) return &g_VFileSystem009; } + if( !Q_strcmp( interface, FS_API_CREATEINTERFACE_TAG )) + { + // return a copy, to disallow overriding + static fs_api_t copy = { 0 }; + + if( !copy.InitStdio ) + memcpy( ©, &g_api, sizeof( copy )); + + if( retval ) *retval = 0; + return © + } + if( retval ) *retval = 1; return NULL; } diff --git a/filesystem/filesystem.c b/filesystem/filesystem.c index 66a9b5ae..5e9e3926 100644 --- a/filesystem/filesystem.c +++ b/filesystem/filesystem.c @@ -2868,7 +2868,7 @@ static qboolean FS_InitInterface( int version, fs_interface_t *engfuncs ) return true; } -static fs_api_t g_api = +fs_api_t g_api = { FS_InitStdio, FS_ShutdownStdio, diff --git a/filesystem/filesystem.h b/filesystem/filesystem.h index 6a7a1f4c..54eac76c 100644 --- a/filesystem/filesystem.h +++ b/filesystem/filesystem.h @@ -29,6 +29,7 @@ extern "C" #endif // __cplusplus #define FS_API_VERSION 1 // not stable yet! +#define FS_API_CREATEINTERFACE_TAG "XashFileSystem001" // follow FS_API_VERSION!!! // search path flags enum diff --git a/filesystem/filesystem_internal.h b/filesystem/filesystem_internal.h index 3cf5a462..5f11f52f 100644 --- a/filesystem/filesystem_internal.h +++ b/filesystem/filesystem_internal.h @@ -86,6 +86,7 @@ extern qboolean fs_ext_path; extern char fs_rodir[MAX_SYSPATH]; extern char fs_rootdir[MAX_SYSPATH]; extern char fs_writedir[MAX_SYSPATH]; +extern fs_api_t g_api; #define GI FI.GameInfo From 3e9f2df2bf20177834db74e07b1b712ebd77d286 Mon Sep 17 00:00:00 2001 From: Alibek Omarov Date: Thu, 25 Aug 2022 21:21:22 +0300 Subject: [PATCH 080/490] engine: client: fix incorrect mark for ConsolePrint notifications --- engine/client/cl_game.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/engine/client/cl_game.c b/engine/client/cl_game.c index 58ed7ca1..6bb50705 100644 --- a/engine/client/cl_game.c +++ b/engine/client/cl_game.c @@ -1940,7 +1940,7 @@ static void GAME_EXPORT pfnConsolePrint( const char *string ) return; // WON GoldSrc behavior - if( string[0] != '1' ) + if( string[0] != 1 ) Con_Printf( "%s", string ); else Con_NPrintf( 0, "%s", string + 1 ); From bebfa611fc685d8c533e10642c47e54996f5b862 Mon Sep 17 00:00:00 2001 From: Alibek Omarov Date: Thu, 25 Aug 2022 21:47:17 +0300 Subject: [PATCH 081/490] mainui: update --- mainui | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mainui b/mainui index 97fcbf89..eb38ebb0 160000 --- a/mainui +++ b/mainui @@ -1 +1 @@ -Subproject commit 97fcbf8979f22d774b1cc01cb5553743592d39d0 +Subproject commit eb38ebb023a42555ec3203cef079c686ece02394 From f633b3dbf66342f72361f903bd02024b1ee537e8 Mon Sep 17 00:00:00 2001 From: Alibek Omarov Date: Sat, 27 Aug 2022 22:12:32 +0300 Subject: [PATCH 082/490] engine: increase MAX_INIT_MSG to 192 kilobytes limit * also avoid magic number in sv_client.c --- engine/common/net_ws.h | 4 ++-- engine/server/sv_client.c | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/engine/common/net_ws.h b/engine/common/net_ws.h index 75329383..5ab40ecd 100644 --- a/engine/common/net_ws.h +++ b/engine/common/net_ws.h @@ -31,9 +31,9 @@ typedef enum #if !XASH_LOW_MEMORY -#define MAX_INIT_MSG 0x20000 // max length of possible message +#define MAX_INIT_MSG 0x30000 // max length of possible message #else -#define MAX_INIT_MSG 0x8000 +#define MAX_INIT_MSG 0x8000 #endif // net packets type #define NET_HEADER_OUTOFBANDPACKET -1 diff --git a/engine/server/sv_client.c b/engine/server/sv_client.c index 2193881f..cbf5d912 100644 --- a/engine/server/sv_client.c +++ b/engine/server/sv_client.c @@ -1316,7 +1316,7 @@ a deathmatch. */ void SV_PutClientInServer( sv_client_t *cl ) { - static byte msg_buf[0x20200]; // MAX_INIT_MSG + some space + static byte msg_buf[MAX_INIT_MSG + 0x200]; // MAX_INIT_MSG + some space edict_t *ent = cl->edict; sizebuf_t msg; From 28001ea15005a79f980dcd8d0c2eb3497f05771a Mon Sep 17 00:00:00 2001 From: Alibek Omarov Date: Sun, 28 Aug 2022 00:48:12 +0300 Subject: [PATCH 083/490] engine: client: enable notify messages in non-developer mode --- engine/client/console.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/engine/client/console.c b/engine/client/console.c index 1ffc15ef..3a5ea4a0 100644 --- a/engine/client/console.c +++ b/engine/client/console.c @@ -2041,7 +2041,7 @@ void Con_DrawDebug( void ) if( scr_download->value != -1.0f ) { Q_snprintf( dlstring, sizeof( dlstring ), "Downloading [%d remaining]: ^2%s^7 %5.1f%% time %.f secs", - host.downloadcount, host.downloadfile, scr_download->value, Sys_DoubleTime() - timeStart ); + host.downloadcount, host.downloadfile, scr_download->value, Sys_DoubleTime() - timeStart ); x = refState.width - 500; y = con.curFont->charHeight * 1.05f; Con_DrawString( x, y, dlstring, g_color_table[7] ); @@ -2051,7 +2051,7 @@ void Con_DrawDebug( void ) timeStart = Sys_DoubleTime(); } - if( !host_developer.value || Cvar_VariableInteger( "cl_background" ) || Cvar_VariableInteger( "sv_background" )) + if( Cvar_VariableInteger( "cl_background" ) || Cvar_VariableInteger( "sv_background" )) return; if( con.draw_notify && !Con_Visible( )) @@ -2077,7 +2077,7 @@ void Con_DrawNotify( void ) x = con.curFont->charWidths[' ']; // offset one space at left screen side - if( host_developer.value && ( !Cvar_VariableInteger( "cl_background" ) && !Cvar_VariableInteger( "sv_background" ))) + if( !Cvar_VariableInteger( "cl_background" ) && !Cvar_VariableInteger( "sv_background" )) { for( i = CON_LINES_COUNT - con.num_times; i < CON_LINES_COUNT; i++ ) { From f29a9f5f589b8cdfe9b0c33266ded0418b7eb670 Mon Sep 17 00:00:00 2001 From: Alibek Omarov Date: Wed, 31 Aug 2022 02:42:17 +0300 Subject: [PATCH 084/490] ref_gl: use skybox names generated by CheckSkybox function, don't checking same file twice Also fixes priority for skyboxes --- ref_gl/gl_warp.c | 40 ++++++++++++++++++++++------------------ 1 file changed, 22 insertions(+), 18 deletions(-) diff --git a/ref_gl/gl_warp.c b/ref_gl/gl_warp.c index 72e77c1c..3144324d 100644 --- a/ref_gl/gl_warp.c +++ b/ref_gl/gl_warp.c @@ -60,11 +60,7 @@ float r_turbsin[] = #include "warpsin.h" }; -#define SKYBOX_MISSED 0 -#define SKYBOX_HLSTYLE 1 -#define SKYBOX_Q1STYLE 2 - -static int CheckSkybox( const char *name ) +static qboolean CheckSkybox( const char *name, char out[6][MAX_STRING] ) { const char *skybox_ext[3] = { "dds", "tga", "bmp" }; int i, j, num_checked_sides; @@ -73,32 +69,40 @@ static int CheckSkybox( const char *name ) // search for skybox images for( i = 0; i < 3; i++ ) { + // check HL-style skyboxes num_checked_sides = 0; for( j = 0; j < 6; j++ ) { // build side name sidename = va( "%s%s.%s", name, r_skyBoxSuffix[j], skybox_ext[i] ); if( gEngfuncs.fsapi->FileExists( sidename, false )) + { + Q_strncpy( out[j], sidename, sizeof( out[j] )); num_checked_sides++; - + } } if( num_checked_sides == 6 ) - return SKYBOX_HLSTYLE; // image exists + return true; // image exists + // check Q1-style skyboxes + num_checked_sides = 0; for( j = 0; j < 6; j++ ) { // build side name sidename = va( "%s_%s.%s", name, r_skyBoxSuffix[j], skybox_ext[i] ); if( gEngfuncs.fsapi->FileExists( sidename, false )) + { + Q_strncpy( out[j], sidename, sizeof( out[j] )); num_checked_sides++; + } } if( num_checked_sides == 6 ) - return SKYBOX_Q1STYLE; // images exists + return true; // images exists } - return SKYBOX_MISSED; + return false; } void DrawSkyPolygon( int nump, vec3_t vecs ) @@ -411,8 +415,9 @@ R_SetupSky void R_SetupSky( const char *skyboxname ) { char loadname[MAX_STRING]; - char sidename[MAX_STRING]; - int i, result, len; + char sidenames[6][MAX_STRING]; + int i, len; + qboolean result; if( !COM_CheckString( skyboxname )) { @@ -428,10 +433,10 @@ void R_SetupSky( const char *skyboxname ) if( loadname[len - 1] == '_' ) loadname[len - 1] = '\0'; - result = CheckSkybox( loadname ); + result = CheckSkybox( loadname, sidenames ); // to prevent infinite recursion if default skybox was missed - if( result == SKYBOX_MISSED && Q_stricmp( loadname, DEFAULT_SKYBOX_PATH )) + if( !result && Q_stricmp( loadname, DEFAULT_SKYBOX_PATH )) { gEngfuncs.Con_Reportf( S_WARN "missed or incomplete skybox '%s'\n", skyboxname ); R_SetupSky( "desert" ); // force to default @@ -444,12 +449,11 @@ void R_SetupSky( const char *skyboxname ) for( i = 0; i < 6; i++ ) { - if( result == SKYBOX_HLSTYLE ) - Q_snprintf( sidename, sizeof( sidename ), "%s%s", loadname, r_skyBoxSuffix[i] ); - else Q_snprintf( sidename, sizeof( sidename ), "%s_%s", loadname, r_skyBoxSuffix[i] ); + tr.skyboxTextures[i] = GL_LoadTexture( sidenames[i], NULL, 0, TF_CLAMP|TF_SKY ); + + if( !tr.skyboxTextures[i] ) + break; - tr.skyboxTextures[i] = GL_LoadTexture( sidename, NULL, 0, TF_CLAMP|TF_SKY ); - if( !tr.skyboxTextures[i] ) break; gEngfuncs.Con_DPrintf( "%s%s%s", skyboxname, r_skyBoxSuffix[i], i != 5 ? ", " : ". " ); } From 4b05cf43995b62784f6dc76843262cdc4f671021 Mon Sep 17 00:00:00 2001 From: Alibek Omarov Date: Wed, 31 Aug 2022 02:46:33 +0300 Subject: [PATCH 085/490] engine: client: better fix for notify debug messages, rely on host.allow_console variable to don't allow uninitialized console usage --- engine/client/console.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/engine/client/console.c b/engine/client/console.c index 3a5ea4a0..21f76b67 100644 --- a/engine/client/console.c +++ b/engine/client/console.c @@ -2051,7 +2051,7 @@ void Con_DrawDebug( void ) timeStart = Sys_DoubleTime(); } - if( Cvar_VariableInteger( "cl_background" ) || Cvar_VariableInteger( "sv_background" )) + if( !host.allow_console || Cvar_VariableInteger( "cl_background" ) || Cvar_VariableInteger( "sv_background" )) return; if( con.draw_notify && !Con_Visible( )) @@ -2077,7 +2077,7 @@ void Con_DrawNotify( void ) x = con.curFont->charWidths[' ']; // offset one space at left screen side - if( !Cvar_VariableInteger( "cl_background" ) && !Cvar_VariableInteger( "sv_background" )) + if( host.allow_console && ( !Cvar_VariableInteger( "cl_background" ) && !Cvar_VariableInteger( "sv_background" ))) { for( i = CON_LINES_COUNT - con.num_times; i < CON_LINES_COUNT; i++ ) { From 4110ee092873bd4452fcefbb285e3803fd840e48 Mon Sep 17 00:00:00 2001 From: Alibek Omarov Date: Wed, 31 Aug 2022 05:44:20 +0300 Subject: [PATCH 086/490] wscript: require Opus Custom for build Apparently, not all Linux distributions enable it. Anyway, we can build codec ourselves --- 3rdparty/opus/wscript | 2 +- wscript | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/3rdparty/opus/wscript b/3rdparty/opus/wscript index 9f3d2098..1f6c9af4 100644 --- a/3rdparty/opus/wscript +++ b/3rdparty/opus/wscript @@ -27,7 +27,7 @@ def build(bld): 'opus/celt/opus_custom_demo.c' ]) includes = ['opus/include/', 'opus/celt/', 'opus/silk/', 'opus/silk/float/'] - defines = ['USE_ALLOCA', 'OPUS_BUILD', 'FLOAT_APPROX', 'PACKAGE_VERSION="1.3.1"'] + defines = ['USE_ALLOCA', 'OPUS_BUILD', 'FLOAT_APPROX', 'PACKAGE_VERSION="1.3.1"', 'CUSTOM_MODES'] bld.stlib( source = sources, diff --git a/wscript b/wscript index fb335fb6..26f34e6c 100644 --- a/wscript +++ b/wscript @@ -328,8 +328,8 @@ int main(int argc, char **argv) { strcasestr(argv[1], argv[2]); return 0; }''' if not conf.options.BUILD_BUNDLED_DEPS: # check if we can use system opus - if conf.check_pkg('opus', 'opus', '''#include -int main(void){ return (opus_encoder_create(48000, 2, OPUS_APPLICATION_VOIP, 0) != 0) && (opus_decoder_create(48000, 2, 0) != 0);}''', fatal = False): + if conf.check_pkg('opus', 'opus', '''#include +int main(void){ return opus_custom_mode_create(44100, 1024, 0) != 0; }''', fatal = False): conf.env.HAVE_SYSTEM_OPUS = True conf.define('XASH_BUILD_COMMIT', conf.env.GIT_VERSION if conf.env.GIT_VERSION else 'notset') From 82ab06efdd9041d480b54ac5f4edeb4d7f06f046 Mon Sep 17 00:00:00 2001 From: Alibek Omarov Date: Wed, 31 Aug 2022 06:44:45 +0300 Subject: [PATCH 087/490] engine: client: voice: new version, move to Opus Custom codec * Despite Opus Custom have strict requirements, it's more barebones, allowing us to use maximum frame size and custom sample rate, without resampling * Encode each frame size to network buffer, allowing smooth voice chat even in 10 FPS * Fix possible buffer overruns, underruns and races with platform side * Revise all usages of offset variables, samples vs bytes --- engine/client/cl_main.c | 2 +- engine/client/sound.h | 1 - engine/client/voice.c | 328 ++++++++++++++++++++++------------------ engine/client/voice.h | 35 ++++- 4 files changed, 206 insertions(+), 160 deletions(-) diff --git a/engine/client/cl_main.c b/engine/client/cl_main.c index 5a6201ff..e6149ff7 100644 --- a/engine/client/cl_main.c +++ b/engine/client/cl_main.c @@ -3085,7 +3085,7 @@ void CL_Init( void ) VID_Init(); // init video S_Init(); // init sound - Voice_Init( "opus", 3 ); // init voice + Voice_Init( VOICE_DEFAULT_CODEC, 3 ); // init voice // unreliable buffer. unsed for unreliable commands and voice stream MSG_Init( &cls.datagram, "cls.datagram", cls.datagram_buf, sizeof( cls.datagram_buf )); diff --git a/engine/client/sound.h b/engine/client/sound.h index c290fca8..19f9eead 100644 --- a/engine/client/sound.h +++ b/engine/client/sound.h @@ -27,7 +27,6 @@ extern poolhandle_t sndpool; #define SOUND_22k 22050 // 22khz sample rate #define SOUND_32k 32000 // 32khz sample rate #define SOUND_44k 44100 // 44khz sample rate -#define SOUND_48k 48000 // 48khz sample rate #define DMA_MSEC_PER_SAMPLE ((float)(1000.0 / SOUND_DMA_SPEED)) // fixed point stuff for real-time resampling diff --git a/engine/client/voice.c b/engine/client/voice.c index 095ded3b..90d859cf 100644 --- a/engine/client/voice.c +++ b/engine/client/voice.c @@ -15,13 +15,11 @@ GNU General Public License for more details. */ #include +#include #include "common.h" #include "client.h" #include "voice.h" -static wavdata_t *input_file; -static fs_offset_t input_pos; - voice_state_t voice = { 0 }; CVAR_DEFINE_AUTO( voice_enable, "1", FCVAR_PRIVILEGED|FCVAR_ARCHIVE, "enable voice chat" ); @@ -31,6 +29,8 @@ CVAR_DEFINE_AUTO( voice_avggain, "0.5", FCVAR_PRIVILEGED|FCVAR_ARCHIVE, "automat 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 ); + /* =============================================================================== @@ -39,25 +39,6 @@ CVAR_DEFINE_AUTO( voice_inputfromfile, "0", FCVAR_PRIVILEGED, "input voice from =============================================================================== */ -/* -========================= -Voice_GetBandwithTypeName - -========================= -*/ -static const char* Voice_GetBandwidthTypeName( int bandwidthType ) -{ - switch( bandwidthType ) - { - case OPUS_BANDWIDTH_FULLBAND: return "Full Band (20 kHz)"; - case OPUS_BANDWIDTH_SUPERWIDEBAND: return "Super Wide Band (12 kHz)"; - case OPUS_BANDWIDTH_WIDEBAND: return "Wide Band (8 kHz)"; - case OPUS_BANDWIDTH_MEDIUMBAND: return "Medium Band (6 kHz)"; - case OPUS_BANDWIDTH_NARROWBAND: return "Narrow Band (4 kHz)"; - default: return "Unknown"; - } -} - /* ========================= Voice_CodecInfo_f @@ -68,7 +49,6 @@ static void Voice_CodecInfo_f( void ) { int encoderComplexity; opus_int32 encoderBitrate; - opus_int32 encoderBandwidthType; if( !voice.initialized ) { @@ -76,72 +56,12 @@ static void Voice_CodecInfo_f( void ) return; } - opus_encoder_ctl( voice.encoder, OPUS_GET_BITRATE( &encoderBitrate )); - opus_encoder_ctl( voice.encoder, OPUS_GET_COMPLEXITY( &encoderComplexity )); - opus_encoder_ctl( voice.encoder, OPUS_GET_BANDWIDTH( &encoderBandwidthType )); + opus_custom_encoder_ctl( voice.encoder, OPUS_GET_BITRATE( &encoderBitrate )); + opus_custom_encoder_ctl( voice.encoder, OPUS_GET_COMPLEXITY( &encoderComplexity )); Con_Printf( "Encoder:\n" ); Con_Printf( " Bitrate: %.3f kbps\n", encoderBitrate / 1000.0f ); Con_Printf( " Complexity: %d\n", encoderComplexity ); - Con_Printf( " Bandwidth: %s\n", Voice_GetBandwidthTypeName( encoderBandwidthType )); -} - -/* -========================= -Voice_GetFrameSize - -========================= -*/ -static uint Voice_GetFrameSize( float durationMsec ) -{ - return voice.channels * voice.width * (( float )voice.samplerate / ( 1000.0f / durationMsec )); -} - -/* -========================= -Voice_ApplyGainAdjust - -========================= -*/ -static void Voice_ApplyGainAdjust( opus_int16 *samples, int count ) -{ - float gain, modifiedMax; - int average, adjustedSample; - int blockOffset = 0; - - for( ;;) - { - int i; - int localMax = 0; - int localSum = 0; - int blockSize = Q_min( count - ( blockOffset + voice.autogain.block_size ), voice.autogain.block_size ); - - if( blockSize < 1 ) - break; - - for( i = 0; i < blockSize; ++i ) - { - int sample = samples[blockOffset + i]; - if( abs( sample ) > localMax ) { - localMax = abs( sample ); - } - localSum += sample; - - gain = voice.autogain.current_gain + i * voice.autogain.gain_multiplier; - adjustedSample = Q_min( 32767, Q_max(( int )( sample * gain ), -32768 )); - samples[blockOffset + i] = adjustedSample; - } - - if( blockOffset % voice.autogain.block_size == 0 ) - { - 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( 32767.0f / modifiedMax, voice_maxgain.value ) * voice_scale.value; - voice.autogain.gain_multiplier = ( voice.autogain.next_gain - voice.autogain.current_gain ) / ( voice.autogain.block_size - 1 ); - } - blockOffset += blockSize; - } } /* @@ -153,11 +73,22 @@ Voice_InitOpusDecoder static qboolean Voice_InitOpusDecoder( void ) { int err; - voice.decoder = opus_decoder_create( voice.samplerate, voice.channels, &err ); + 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; + } + + 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", opus_strerror( err )); + Con_Printf( S_ERROR "Can't create Opus encoder: %s\n", opus_strerror( err )); return false; } @@ -173,37 +104,30 @@ Voice_InitOpusEncoder static qboolean Voice_InitOpusEncoder( int quality ) { int err; - int app = quality == 5 ? OPUS_APPLICATION_AUDIO : OPUS_APPLICATION_VOIP; - - voice.encoder = opus_encoder_create( voice.samplerate, voice.channels, app, &err ); + voice.encoder = opus_custom_encoder_create( voice.custom_mode, VOICE_PCM_CHANNELS, &err ); if( !voice.encoder ) { - Con_Printf( S_ERROR "Can't create Opus encoder: %s", opus_strerror( err )); + Con_Printf( S_ERROR "Can't create Opus encoder: %s\n", opus_strerror( err )); return false; } switch( quality ) { - case 1: // 6 kbps, <6 kHz bandwidth - opus_encoder_ctl( voice.encoder, OPUS_SET_BITRATE( 6000 )); - opus_encoder_ctl( voice.encoder, OPUS_SET_BANDWIDTH( OPUS_BANDWIDTH_MEDIUMBAND )); + case 1: // 6 kbps + opus_custom_encoder_ctl( voice.encoder, OPUS_SET_BITRATE( 6000 )); break; - case 2: // 12 kbps, <12 kHz bandwidth - opus_encoder_ctl( voice.encoder, OPUS_SET_BITRATE( 12000 )); - opus_encoder_ctl( voice.encoder, OPUS_SET_BANDWIDTH( OPUS_BANDWIDTH_SUPERWIDEBAND )); + case 2: // 12 kbps + opus_custom_encoder_ctl( voice.encoder, OPUS_SET_BITRATE( 12000 )); break; - case 4: // 64 kbps, full band (20 kHz) - opus_encoder_ctl( voice.encoder, OPUS_SET_BITRATE( 64000 )); - opus_encoder_ctl( voice.encoder, OPUS_SET_BANDWIDTH( OPUS_BANDWIDTH_FULLBAND )); + case 4: // 64 kbps + opus_custom_encoder_ctl( voice.encoder, OPUS_SET_BITRATE( 64000 )); break; - case 5: // 96 kbps, full band (20 kHz) - opus_encoder_ctl( voice.encoder, OPUS_SET_BITRATE( 96000 )); - opus_encoder_ctl( voice.encoder, OPUS_SET_BANDWIDTH( OPUS_BANDWIDTH_FULLBAND )); + case 5: // 96 kbps + opus_custom_encoder_ctl( voice.encoder, OPUS_SET_BITRATE( 96000 )); break; - default: // 36 kbps, <12 kHz bandwidth - opus_encoder_ctl( voice.encoder, OPUS_SET_BITRATE( 36000 )); - opus_encoder_ctl( voice.encoder, OPUS_SET_BANDWIDTH( OPUS_BANDWIDTH_SUPERWIDEBAND )); + default: // 36 kbps + opus_custom_encoder_ctl( voice.encoder, OPUS_SET_BITRATE( 36000 )); break; } @@ -220,7 +144,7 @@ static void Voice_ShutdownOpusDecoder( void ) { if( voice.decoder ) { - opus_decoder_destroy( voice.decoder ); + opus_custom_decoder_destroy( voice.decoder ); voice.decoder = NULL; } } @@ -235,9 +159,15 @@ static void Voice_ShutdownOpusEncoder( void ) { if( voice.encoder ) { - opus_encoder_destroy( voice.encoder ); + opus_custom_encoder_destroy( voice.encoder ); voice.encoder = NULL; } + + if( voice.custom_mode ) + { + opus_custom_mode_destroy( voice.custom_mode ); + voice.custom_mode = NULL; + } } /* @@ -248,44 +178,72 @@ Voice_GetOpusCompressedData */ static uint Voice_GetOpusCompressedData( byte *out, uint maxsize, uint *frames ) { - uint ofs, size = 0; + uint ofs = 0, size = 0; + uint frame_size_bytes = voice.frame_size * voice.width; - if( input_file ) + if( voice.input_file ) { uint numbytes; double updateInterval; updateInterval = cl.mtime[0] - cl.mtime[1]; - numbytes = updateInterval * voice.samplerate * voice.width * voice.channels; - numbytes = Q_min( numbytes, input_file->size - input_pos ); + numbytes = updateInterval * voice.samplerate * voice.width * VOICE_PCM_CHANNELS; + numbytes = Q_min( numbytes, voice.input_file->size - voice.input_file_pos ); numbytes = Q_min( numbytes, sizeof( voice.input_buffer ) - voice.input_buffer_pos ); - memcpy( voice.input_buffer + voice.input_buffer_pos, input_file->buffer + input_pos, numbytes ); + memcpy( voice.input_buffer + voice.input_buffer_pos, voice.input_file->buffer + voice.input_file_pos, numbytes ); voice.input_buffer_pos += numbytes; - input_pos += numbytes; + voice.input_file_pos += numbytes; } - for( ofs = 0; voice.input_buffer_pos - ofs >= voice.frame_size && ofs <= voice.input_buffer_pos; ofs += voice.frame_size ) + if( !voice.input_file ) + VoiceCapture_Lock( true ); + + for( ofs = 0; voice.input_buffer_pos - ofs >= frame_size_bytes && ofs <= voice.input_buffer_pos; ofs += frame_size_bytes ) { int bytes; - if( !input_file ) +#if 1 + 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); } +#endif - bytes = opus_encode( voice.encoder, (const opus_int16*)(voice.input_buffer + ofs), voice.frame_size / voice.width, out + size, maxsize ); - memmove( voice.input_buffer, voice.input_buffer + voice.frame_size, sizeof( voice.input_buffer ) - voice.frame_size ); - voice.input_buffer_pos -= voice.frame_size; + bytes = opus_custom_encode( voice.encoder, (const opus_int16 *)( voice.input_buffer + ofs ), + voice.frame_size, out + size + sizeof( uint16_t ), maxsize ); if( bytes > 0 ) { - size += bytes; + // write compressed frame size + *((uint16_t *)&out[size]) = bytes; + + size += bytes + sizeof( uint16_t ); + maxsize -= bytes + sizeof( uint16_t ); + (*frames)++; } + else + { + Con_Printf( S_ERROR "%s: failed to encode frame: %s\n", __func__, opus_strerror( bytes )); + } } + // did we compress anything? update counters + if( ofs ) + { + fs_offset_t remaining = voice.input_buffer_pos - ofs; + + // move remaining samples to the beginning of buffer + memmove( voice.input_buffer, voice.input_buffer + ofs, remaining ); + + voice.input_buffer_pos = remaining; + } + + if( !voice.input_file ) + VoiceCapture_Lock( false ); + return size; } @@ -297,6 +255,53 @@ static uint Voice_GetOpusCompressedData( byte *out, uint maxsize, uint *frames ) =============================================================================== */ +/* +========================= +Voice_ApplyGainAdjust + +========================= +*/ +static void Voice_ApplyGainAdjust( int16_t *samples, int count ) +{ + float gain, modifiedMax; + int average, adjustedSample, blockOffset = 0; + + for( ;; ) + { + int i, localMax = 0, localSum = 0; + int blockSize = Q_min( count - ( blockOffset + voice.autogain.block_size ), voice.autogain.block_size ); + + if( blockSize < 1 ) + break; + + for( i = 0; i < blockSize; ++i ) + { + int sample = samples[blockOffset + i]; + int absSample = abs( sample ); + + if( absSample > localMax ) + 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; + } + + if( blockOffset % voice.autogain.block_size == 0 ) + { + 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 ); + } + blockOffset += blockSize; + } +} + /* ========================= Voice_Status @@ -367,21 +372,19 @@ Voice_RecordStop */ void Voice_RecordStop( void ) { - if( input_file ) + if( voice.input_file ) { - FS_FreeSound( input_file ); - input_file = NULL; + FS_FreeSound( voice.input_file ); + voice.input_file = NULL; } + VoiceCapture_Activate( false ); + voice.is_recording = false; + + Voice_Status( VOICE_LOCALCLIENT_INDEX, false ); + voice.input_buffer_pos = 0; memset( voice.input_buffer, 0, sizeof( voice.input_buffer )); - - if( Voice_IsRecording( )) - Voice_Status( VOICE_LOCALCLIENT_INDEX, false ); - - VoiceCapture_RecordStop(); - - voice.is_recording = false; } /* @@ -396,25 +399,25 @@ void Voice_RecordStart( void ) if( voice_inputfromfile.value ) { - input_file = FS_LoadSound( "voice_input.wav", NULL, 0 ); + voice.input_file = FS_LoadSound( "voice_input.wav", NULL, 0 ); - if( input_file ) + if( voice.input_file ) { - Sound_Process( &input_file, voice.samplerate, voice.width, SOUND_RESAMPLE ); - input_pos = 0; + Sound_Process( &voice.input_file, voice.samplerate, voice.width, SOUND_RESAMPLE ); + voice.input_file_pos = 0; voice.start_time = Sys_DoubleTime(); voice.is_recording = true; } else { - FS_FreeSound( input_file ); - input_file = NULL; + FS_FreeSound( voice.input_file ); + voice.input_file = NULL; } } if( !Voice_IsRecording() ) - voice.is_recording = VoiceCapture_RecordStart(); + voice.is_recording = VoiceCapture_Activate( true ); if( Voice_IsRecording() ) Voice_Status( VOICE_LOCALCLIENT_INDEX, true ); @@ -460,7 +463,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.channels, data, 255 ); + S_RawEntSamples( entnum, samples, voice.samplerate, voice.width, VOICE_PCM_CHANNELS, data, 255 ); } /* @@ -472,12 +475,35 @@ Received encoded voice data, decode it */ void Voice_AddIncomingData( int ent, const byte *data, uint size, uint frames ) { - int samples; + int samples = 0; + int ofs = 0; if( !voice.decoder ) return; - samples = opus_decode( voice.decoder, data, size, (short *)voice.decompress_buffer, voice.frame_size / voice.width * frames, false ); + // decode frame by frame + for( ;; ) + { + int frame_samples; + uint16_t compressed_size; + + // no compressed size mark + if( ofs + sizeof( uint16_t ) > size ) + break; + + compressed_size = *(const uint16_t *)(data + ofs); + ofs += sizeof( uint16_t ); + + // no frame data + if( ofs + compressed_size > size ) + break; + + frame_samples = opus_custom_decode( voice.decoder, data + ofs, compressed_size, + (opus_int16*)voice.decompress_buffer + samples, voice.frame_size ); + + ofs += compressed_size; + samples += frame_samples; + } if( samples > 0 ) Voice_StartChannel( samples, voice.decompress_buffer, ent ); @@ -566,7 +592,7 @@ void Voice_Idle( double frametime ) { int i; - if( !voice_enable.value ) + if( FBitSet( voice_enable.flags, FCVAR_CHANGED ) && !voice_enable.value ) { Voice_Shutdown(); return; @@ -591,19 +617,16 @@ qboolean Voice_Init( const char *pszCodecName, int quality ) if( !voice_enable.value ) return false; - Voice_Shutdown(); - - if( Q_strcmp( pszCodecName, "opus" )) + if( Q_strcmp( pszCodecName, VOICE_OPUS_CUSTOM_CODEC )) { - Con_Printf( S_ERROR "Server requested unsupported codec: %s", pszCodecName ); + Con_Printf( S_ERROR "Server requested unsupported codec: %s\n", pszCodecName ); return false; } - voice.initialized = false; - voice.channels = 1; - voice.width = 2; - voice.samplerate = SOUND_48k; - voice.frame_size = Voice_GetFrameSize( 40.0f ); + // reinitialize only if codec parameters are different + if( Q_strcmp( voice.codec, pszCodecName ) && voice.quality != quality ) + Voice_Shutdown(); + voice.autogain.block_size = 128; if( !Voice_InitOpusDecoder( )) @@ -617,6 +640,7 @@ qboolean Voice_Init( const char *pszCodecName, int quality ) // we can hear others players, so it's fine to fail now voice.initialized = true; + Q_strncpy( voice.codec, pszCodecName, sizeof( voice.codec )); if( !Voice_InitOpusEncoder( quality )) { @@ -624,6 +648,8 @@ qboolean Voice_Init( const char *pszCodecName, int quality ) return false; } + voice.quality = quality; + if( !VoiceCapture_Init( )) Con_Printf( S_WARN "No microphone is available.\n" ); diff --git a/engine/client/voice.h b/engine/client/voice.h index 70d35b45..f1d878cb 100644 --- a/engine/client/voice.h +++ b/engine/client/voice.h @@ -17,15 +17,29 @@ GNU General Public License for more details. #ifndef VOICE_H #define VOICE_H +#include "common.h" #include "protocol.h" // MAX_CLIENTS #include "sound.h" -typedef struct OpusDecoder OpusDecoder; -typedef struct OpusEncoder OpusEncoder; +typedef struct OpusCustomEncoder OpusCustomEncoder; +typedef struct OpusCustomDecoder OpusCustomDecoder; +typedef struct OpusCustomMode OpusCustomMode; #define VOICE_LOOPBACK_INDEX (-2) #define VOICE_LOCALCLIENT_INDEX (-1) +#define VOICE_PCM_CHANNELS 1 // always mono + +// never change these parameters when using opuscustom +#define VOICE_OPUS_CUSTOM_SAMPLERATE SOUND_44k +// must follow opus custom requirements +// also be divisible with MAX_RAW_SAMPLES +#define VOICE_OPUS_CUSTOM_FRAME_SIZE 1024 +#define VOICE_OPUS_CUSTOM_CODEC "opus_custom_44k_512" + +// a1ba: do not change, we don't have any re-encoding support now +#define VOICE_DEFAULT_CODEC VOICE_OPUS_CUSTOM_CODEC + typedef struct voice_status_s { qboolean talking_ack; @@ -34,6 +48,9 @@ typedef struct voice_status_s typedef struct voice_state_s { + string codec; + int quality; + qboolean initialized; qboolean is_recording; double start_time; @@ -42,20 +59,24 @@ typedef struct voice_state_s voice_status_t players_status[MAX_CLIENTS]; // opus stuff - OpusEncoder *encoder; - OpusDecoder *decoder; + OpusCustomMode *custom_mode; + OpusCustomEncoder *encoder; + OpusCustomDecoder *decoder; // audio info - uint channels; uint width; uint samplerate; - uint frame_size; + uint frame_size; // in samples // buffers byte input_buffer[MAX_RAW_SAMPLES]; byte output_buffer[MAX_RAW_SAMPLES]; byte decompress_buffer[MAX_RAW_SAMPLES]; - fs_offset_t input_buffer_pos; + fs_offset_t input_buffer_pos; // in bytes + + // input from file + wavdata_t *input_file; + fs_offset_t input_file_pos; // in bytes // automatic gain control struct { From 0d7a2e7bad5cdccc0d6f1d5380528fdf03e955ca Mon Sep 17 00:00:00 2001 From: Alibek Omarov Date: Wed, 31 Aug 2022 06:50:06 +0300 Subject: [PATCH 088/490] engine: platform: change capture API to allow locking/unlocking buffer to prevent race condition, use single function for pause --- engine/platform/android/snd_opensles.c | 6 ++-- engine/platform/linux/s_alsa.c | 6 ++-- engine/platform/platform.h | 5 +-- engine/platform/sdl/s_sdl.c | 48 +++++++++++++++++--------- engine/platform/stub/s_stub.c | 6 ++-- 5 files changed, 44 insertions(+), 27 deletions(-) diff --git a/engine/platform/android/snd_opensles.c b/engine/platform/android/snd_opensles.c index 9916a269..4cf5bbe0 100644 --- a/engine/platform/android/snd_opensles.c +++ b/engine/platform/android/snd_opensles.c @@ -260,14 +260,14 @@ qboolean VoiceCapture_Init( void ) return false; } -qboolean VoiceCapture_RecordStart( void ) +qboolean VoiceCapture_Activate( qboolean activate ) { return false; } -void VoiceCapture_RecordStop( void ) +qboolean VoiceCapture_Lock( qboolean lock ) { - + return false; } void VoiceCapture_Shutdown( void ) diff --git a/engine/platform/linux/s_alsa.c b/engine/platform/linux/s_alsa.c index eb738975..6875a0c0 100644 --- a/engine/platform/linux/s_alsa.c +++ b/engine/platform/linux/s_alsa.c @@ -346,14 +346,14 @@ qboolean VoiceCapture_Init( void ) return false; } -qboolean VoiceCapture_RecordStart( void ) +qboolean VoiceCapture_Activate( qboolean activate ) { return false; } -void VoiceCapture_RecordStop( void ) +qboolean VoiceCapture_Lock( qboolean lock ) { - + return false; } void VoiceCapture_Shutdown( void ) diff --git a/engine/platform/platform.h b/engine/platform/platform.h index ad0c546d..ead5bc6f 100644 --- a/engine/platform/platform.h +++ b/engine/platform/platform.h @@ -161,9 +161,10 @@ void SNDDMA_Activate( qboolean active ); // pause audio // void SNDDMA_PrintDeviceName( void ); // unused // void SNDDMA_LockSound( void ); // unused // void SNDDMA_UnlockSound( void ); // unused + qboolean VoiceCapture_Init( void ); void VoiceCapture_Shutdown( void ); -qboolean VoiceCapture_RecordStart( void ); -void VoiceCapture_RecordStop( void ); +qboolean VoiceCapture_Activate( qboolean activate ); +qboolean VoiceCapture_Lock( qboolean lock ); #endif // PLATFORM_H diff --git a/engine/platform/sdl/s_sdl.c b/engine/platform/sdl/s_sdl.c index 2a0b9d41..555441bd 100644 --- a/engine/platform/sdl/s_sdl.c +++ b/engine/platform/sdl/s_sdl.c @@ -32,6 +32,8 @@ GNU General Public License for more details. #define SDL_OpenAudioDevice( a, b, c, d, e ) SDL_OpenAudio( ( c ), ( d ) ) #define SDL_CloseAudioDevice( a ) SDL_CloseAudio() #define SDL_PauseAudioDevice( a, b ) SDL_PauseAudio( ( b ) ) +#define SDL_LockAudioDevice( x ) SDL_LockAudio() +#define SDL_UnlockAudioDevice( x ) SDL_UnlockAudio() #define SDLash_IsAudioError( x ) ( x ) != 0 #else #define SDLash_IsAudioError( x ) ( x ) == 0 @@ -163,7 +165,7 @@ Makes sure dma.buffer is valid */ void SNDDMA_BeginPainting( void ) { - SDL_LockAudio( ); + SDL_LockAudioDevice( sdl_dev ); } /* @@ -176,7 +178,7 @@ Also unlocks the dsound buffer */ void SNDDMA_Submit( void ) { - SDL_UnlockAudio( ); + SDL_UnlockAudioDevice( sdl_dev ); } /* @@ -235,11 +237,13 @@ SDL_SoundInputCallback */ void SDL_SoundInputCallback( void *userdata, Uint8 *stream, int len ) { - int size; + int size = Q_min( len, sizeof( voice.input_buffer ) - voice.input_buffer_pos ); - size = Q_min( len, sizeof( voice.input_buffer ) - voice.input_buffer_pos ); - SDL_memset( voice.input_buffer + voice.input_buffer_pos, 0, size ); - SDL_MixAudioFormat( voice.input_buffer + voice.input_buffer_pos, stream, sdl_format, size, SDL_MIX_MAXVOLUME ); + // engine can't keep up, skip audio + if( !size ) + return; + + memcpy( voice.input_buffer + voice.input_buffer_pos, stream, size ); voice.input_buffer_pos += size; } @@ -252,11 +256,16 @@ qboolean VoiceCapture_Init( void ) { SDL_AudioSpec wanted, spec; + if( !SDLash_IsAudioError( in_dev )) + { + VoiceCapture_Shutdown(); + } + SDL_zero( wanted ); wanted.freq = voice.samplerate; wanted.format = AUDIO_S16LSB; - wanted.channels = voice.channels; - wanted.samples = voice.frame_size / voice.width; + wanted.channels = VOICE_PCM_CHANNELS; + wanted.samples = voice.frame_size; wanted.callback = SDL_SoundInputCallback; in_dev = SDL_OpenAudioDevice( NULL, SDL_TRUE, &wanted, &spec, 0 ); @@ -273,25 +282,32 @@ qboolean VoiceCapture_Init( void ) /* =========== -VoiceCapture_RecordStart +VoiceCapture_Activate =========== */ -qboolean VoiceCapture_RecordStart( void ) +qboolean VoiceCapture_Activate( qboolean activate ) { - SDL_PauseAudioDevice( in_dev, SDL_FALSE ); + if( SDLash_IsAudioError( in_dev )) + return false; + SDL_PauseAudioDevice( in_dev, activate ? SDL_FALSE : SDL_TRUE ); return true; } /* =========== -VoiceCapture_RecordStop +VoiceCapture_Lock =========== */ -void VoiceCapture_RecordStop( void ) +qboolean VoiceCapture_Lock( qboolean lock ) { - if( in_dev ) - SDL_PauseAudioDevice( in_dev, SDL_TRUE ); + if( SDLash_IsAudioError( in_dev )) + return false; + + if( lock ) SDL_LockAudioDevice( in_dev ); + else SDL_UnlockAudioDevice( in_dev ); + + return true; } /* @@ -301,7 +317,7 @@ VoiceCapture_Shutdown */ void VoiceCapture_Shutdown( void ) { - if( !in_dev ) + if( SDLash_IsAudioError( in_dev )) return; SDL_CloseAudioDevice( in_dev ); diff --git a/engine/platform/stub/s_stub.c b/engine/platform/stub/s_stub.c index 53c12543..6bd96966 100644 --- a/engine/platform/stub/s_stub.c +++ b/engine/platform/stub/s_stub.c @@ -99,14 +99,14 @@ qboolean VoiceCapture_Init( void ) return false; } -qboolean VoiceCapture_RecordStart( void ) +qboolean VoiceCapture_Activate( qboolean activate ) { return false; } -void VoiceCapture_RecordStop( void ) +qboolean VoiceCapture_Lock( qboolean lock ) { - + return false; } void VoiceCapture_Shutdown( void ) From 24763f9b07e7d54aff3001c290c0d561e6f62750 Mon Sep 17 00:00:00 2001 From: Alibek Omarov Date: Wed, 31 Aug 2022 06:54:58 +0300 Subject: [PATCH 089/490] engine: server: request client to use Opus Custom codec --- engine/server/sv_init.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/engine/server/sv_init.c b/engine/server/sv_init.c index abfb28e4..ab32ad44 100644 --- a/engine/server/sv_init.c +++ b/engine/server/sv_init.c @@ -17,6 +17,8 @@ GNU General Public License for more details. #include "server.h" #include "net_encode.h" #include "library.h" +#include "voice.h" + #if XASH_LOW_MEMORY != 2 int SV_UPDATE_BACKUP = SINGLEPLAYER_BACKUP; #endif @@ -394,7 +396,7 @@ SV_WriteVoiceCodec void SV_WriteVoiceCodec( sizebuf_t *msg ) { MSG_BeginServerCmd( msg, svc_voiceinit ); - MSG_WriteString( msg, "opus" ); + MSG_WriteString( msg, VOICE_DEFAULT_CODEC ); MSG_WriteByte( msg, (int)sv_voicequality.value ); } From 3a8c58d1924185e6bd79410c89bff78f8e86258a Mon Sep 17 00:00:00 2001 From: Alibek Omarov Date: Wed, 31 Aug 2022 07:13:02 +0300 Subject: [PATCH 090/490] engine: platform: sdl: fix parentheses around IsAudioError macro --- engine/platform/sdl/s_sdl.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/engine/platform/sdl/s_sdl.c b/engine/platform/sdl/s_sdl.c index 555441bd..4f212485 100644 --- a/engine/platform/sdl/s_sdl.c +++ b/engine/platform/sdl/s_sdl.c @@ -34,9 +34,9 @@ GNU General Public License for more details. #define SDL_PauseAudioDevice( a, b ) SDL_PauseAudio( ( b ) ) #define SDL_LockAudioDevice( x ) SDL_LockAudio() #define SDL_UnlockAudioDevice( x ) SDL_UnlockAudio() -#define SDLash_IsAudioError( x ) ( x ) != 0 +#define SDLash_IsAudioError( x ) (( x ) != 0) #else -#define SDLash_IsAudioError( x ) ( x ) == 0 +#define SDLash_IsAudioError( x ) (( x ) == 0) #endif /* From 19c20618313317f6d94b85d65d5e377cd031d3fa Mon Sep 17 00:00:00 2001 From: Alibek Omarov Date: Wed, 31 Aug 2022 19:48:42 +0300 Subject: [PATCH 091/490] wscript: define CUSTOM_MODES and try to link with export that only exists when CUSTOM_MODES was defined in build-time --- engine/client/voice.c | 6 +++--- wscript | 7 ++++++- 2 files changed, 9 insertions(+), 4 deletions(-) diff --git a/engine/client/voice.c b/engine/client/voice.c index 90d859cf..5bcd75d8 100644 --- a/engine/client/voice.c +++ b/engine/client/voice.c @@ -14,7 +14,7 @@ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. */ -#include +#define CUSTOM_MODES 1 // required to correctly link with Opus Custom #include #include "common.h" #include "client.h" @@ -47,8 +47,8 @@ Voice_CodecInfo_f */ static void Voice_CodecInfo_f( void ) { - int encoderComplexity; - opus_int32 encoderBitrate; + int encoderComplexity = 0; + opus_int32 encoderBitrate = 0; if( !voice.initialized ) { diff --git a/wscript b/wscript index 26f34e6c..82c50dbb 100644 --- a/wscript +++ b/wscript @@ -328,9 +328,14 @@ int main(int argc, char **argv) { strcasestr(argv[1], argv[2]); return 0; }''' if not conf.options.BUILD_BUNDLED_DEPS: # check if we can use system opus + conf.define('CUSTOM_MODES', 1) + + # try to link with export that only exists with CUSTOM_MODES defined if conf.check_pkg('opus', 'opus', '''#include -int main(void){ return opus_custom_mode_create(44100, 1024, 0) != 0; }''', fatal = False): +int main(void){ return !opus_custom_encoder_init(0, 0, 0); }''', fatal = False): conf.env.HAVE_SYSTEM_OPUS = True + else: + conf.undefine('CUSTOM_MODES') conf.define('XASH_BUILD_COMMIT', conf.env.GIT_VERSION if conf.env.GIT_VERSION else 'notset') conf.define('XASH_LOW_MEMORY', conf.options.LOW_MEMORY) From 4be48709824ad54fe9485ef9a9e0e21beeaa23d5 Mon Sep 17 00:00:00 2001 From: Alibek Omarov Date: Wed, 31 Aug 2022 19:50:31 +0300 Subject: [PATCH 092/490] engine: client: remove voice_codecinfo command, as Opus Custom don't give any encoder info, possibly an Opus bug --- engine/client/voice.c | 26 -------------------------- 1 file changed, 26 deletions(-) diff --git a/engine/client/voice.c b/engine/client/voice.c index 5bcd75d8..7d5ef8f0 100644 --- a/engine/client/voice.c +++ b/engine/client/voice.c @@ -39,31 +39,6 @@ static void Voice_ApplyGainAdjust( int16_t *samples, int count ); =============================================================================== */ -/* -========================= -Voice_CodecInfo_f - -========================= -*/ -static void Voice_CodecInfo_f( void ) -{ - int encoderComplexity = 0; - opus_int32 encoderBitrate = 0; - - if( !voice.initialized ) - { - Con_Printf( "Voice codec is not initialized!\n" ); - return; - } - - opus_custom_encoder_ctl( voice.encoder, OPUS_GET_BITRATE( &encoderBitrate )); - opus_custom_encoder_ctl( voice.encoder, OPUS_GET_COMPLEXITY( &encoderComplexity )); - - Con_Printf( "Encoder:\n" ); - Con_Printf( " Bitrate: %.3f kbps\n", encoderBitrate / 1000.0f ); - Con_Printf( " Complexity: %d\n", encoderComplexity ); -} - /* ========================= Voice_InitOpusDecoder @@ -550,7 +525,6 @@ void Voice_RegisterCvars( void ) Cvar_RegisterVariable( &voice_avggain ); Cvar_RegisterVariable( &voice_maxgain ); Cvar_RegisterVariable( &voice_inputfromfile ); - Cmd_AddClientCommand( "voice_codecinfo", Voice_CodecInfo_f ); } /* From 45cc7e5bcc95349ef3fb78dd8eba5ea4c906fd10 Mon Sep 17 00:00:00 2001 From: Alibek Omarov Date: Wed, 31 Aug 2022 19:51:52 +0300 Subject: [PATCH 093/490] 3rdparty: opus: update submodule --- 3rdparty/opus/opus | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/3rdparty/opus/opus b/3rdparty/opus/opus index 16395923..997fdf54 160000 --- a/3rdparty/opus/opus +++ b/3rdparty/opus/opus @@ -1 +1 @@ -Subproject commit 1639592368fc2dadc82d63f3be6f17ed0b554d71 +Subproject commit 997fdf54e781ae1c04dee42018f35388a04fe483 From e6bb9d980d465762f66a31eb8df6753207b6dfd3 Mon Sep 17 00:00:00 2001 From: Andrey Akhmichin <15944199+nekonomicon@users.noreply.github.com> Date: Mon, 5 Sep 2022 13:18:48 +0500 Subject: [PATCH 094/490] engine: client: in_touch.c: make touch buttons extention-independent. --- engine/client/in_touch.c | 46 ++++++++++++++++++++-------------------- 1 file changed, 23 insertions(+), 23 deletions(-) diff --git a/engine/client/in_touch.c b/engine/client/in_touch.c index 5bd454d0..50e7b6a4 100644 --- a/engine/client/in_touch.c +++ b/engine/client/in_touch.c @@ -994,7 +994,7 @@ static void Touch_InitEditor( void ) Touch_ClearList( &touch.list_edit ); - temp = Touch_AddButton( &touch.list_edit, "close", "touch_default/edit_close.tga", "touch_disableedit", 0, y, x, y + 0.1f, color, true ); + temp = Touch_AddButton( &touch.list_edit, "close", "touch_default/edit_close", "touch_disableedit", 0, y, x, y + 0.1f, color, true ); SetBits( temp->flags, TOUCH_FL_NOEDIT ); temp = Touch_AddButton( &touch.list_edit, "close", "#Close and save", "", x, y, x + 0.2f, y + 0.1f, color, true ); @@ -1002,7 +1002,7 @@ static void Touch_InitEditor( void ) y += 0.2f; - temp = Touch_AddButton( &touch.list_edit, "cancel", "touch_default/edit_reset.tga", "touch_reloadconfig", 0, y, x, y + 0.1f, color, true ); + temp = Touch_AddButton( &touch.list_edit, "cancel", "touch_default/edit_reset", "touch_reloadconfig", 0, y, x, y + 0.1f, color, true ); SetBits( temp->flags, TOUCH_FL_NOEDIT ); temp = Touch_AddButton( &touch.list_edit, "close", "#Cancel and reset", "", x, y, x + 0.2f, y + 0.1f, color, true ); @@ -1010,7 +1010,7 @@ static void Touch_InitEditor( void ) y += 0.2f; - touch.hidebutton = Touch_AddButton( &touch.list_edit, "showhide", "touch_default/edit_hide.tga", "touch_toggleselection", 0, y, x, y + 0.1f, color, true ); + touch.hidebutton = Touch_AddButton( &touch.list_edit, "showhide", "touch_default/edit_hide", "touch_toggleselection", 0, y, x, y + 0.1f, color, true ); SetBits( touch.hidebutton->flags, TOUCH_FL_HIDE | TOUCH_FL_NOEDIT ); } @@ -1038,23 +1038,23 @@ void Touch_Init( void ) MakeRGBA( color, 255, 255, 255, 255 ); Touch_AddDefaultButton( "look", "", "_look", 0.500000, 0.000000, 1.000000, 1, color, 0, 0, 0 ); Touch_AddDefaultButton( "move", "", "_move", 0.000000, 0.000000, 0.500000, 1, color, 0, 0, 0 ); - Touch_AddDefaultButton( "invnext", "touch_default/next_weap.tga", "invnext", 0.000000, 0.530200, 0.120000, 0.757428, color, 2, 1, 0 ); - Touch_AddDefaultButton( "invprev", "touch_default/prev_weap.tga", "invprev", 0.000000, 0.075743, 0.120000, 0.302971, color, 2, 1, 0 ); - Touch_AddDefaultButton( "use", "touch_default/use.tga", "+use", 0.880000, 0.454457, 1.000000, 0.681685, color, 2, 1, 0 ); - Touch_AddDefaultButton( "jump", "touch_default/jump.tga", "+jump", 0.880000, 0.227228, 1.000000, 0.454457, color, 2, 1, 0 ); - Touch_AddDefaultButton( "attack", "touch_default/shoot.tga", "+attack", 0.760000, 0.530200, 0.880000, 0.757428, color, 2, 1, 0 ); - Touch_AddDefaultButton( "attack2", "touch_default/shoot_alt.tga", "+attack2", 0.760000, 0.302971, 0.880000, 0.530200, color, 2, 1, 0 ); - Touch_AddDefaultButton( "loadquick", "touch_default/load.tga", "loadquick", 0.760000, 0.000000, 0.840000, 0.151486, color, 2, 1, 16 ); - Touch_AddDefaultButton( "savequick", "touch_default/save.tga", "savequick", 0.840000, 0.000000, 0.920000, 0.151486, color, 2, 1, 16 ); - Touch_AddDefaultButton( "messagemode", "touch_default/keyboard.tga", "messagemode", 0.840000, 0.000000, 0.920000, 0.151486, color, 2, 1, 8 ); - Touch_AddDefaultButton( "reload", "touch_default/reload.tga", "+reload", 0.000000, 0.302971, 0.120000, 0.530200, color, 2, 1, 0 ); - Touch_AddDefaultButton( "flashlight", "touch_default/flash_light_filled.tga", "impulse 100", 0.920000, 0.000000, 1.000000, 0.151486, color, 2, 1, 0 ); - Touch_AddDefaultButton( "scores", "touch_default/map.tga", "+showscores", 0.760000, 0.000000, 0.840000, 0.151486, color, 2, 1, 8 ); - Touch_AddDefaultButton( "show_numbers", "touch_default/show_weapons.tga", "exec touch_default/numbers.cfg", 0.440000, 0.833171, 0.520000, 0.984656, color, 2, 1, 0 ); - Touch_AddDefaultButton( "duck", "touch_default/crouch.tga", "+duck", 0.880000, 0.757428, 1.000000, 0.984656, color, 2, 1, 0 ); - Touch_AddDefaultButton( "tduck", "touch_default/tduck.tga", ";+duck", 0.560000, 0.833171, 0.620000, 0.946785, color, 2, 1, 0 ); - Touch_AddDefaultButton( "edit", "touch_default/settings.tga", "touch_enableedit", 0.420000, 0.000000, 0.500000, 0.151486, color, 2, 1, 32 ); - Touch_AddDefaultButton( "menu", "touch_default/menu.tga", "escape", 0.000000, 0.833171, 0.080000, 0.984656, color, 2, 1, 0 ); + Touch_AddDefaultButton( "invnext", "touch_default/next_weap", "invnext", 0.000000, 0.530200, 0.120000, 0.757428, color, 2, 1, 0 ); + Touch_AddDefaultButton( "invprev", "touch_default/prev_weap", "invprev", 0.000000, 0.075743, 0.120000, 0.302971, color, 2, 1, 0 ); + Touch_AddDefaultButton( "use", "touch_default/use", "+use", 0.880000, 0.454457, 1.000000, 0.681685, color, 2, 1, 0 ); + Touch_AddDefaultButton( "jump", "touch_default/jump", "+jump", 0.880000, 0.227228, 1.000000, 0.454457, color, 2, 1, 0 ); + Touch_AddDefaultButton( "attack", "touch_default/shoot", "+attack", 0.760000, 0.530200, 0.880000, 0.757428, color, 2, 1, 0 ); + Touch_AddDefaultButton( "attack2", "touch_default/shoot_alt", "+attack2", 0.760000, 0.302971, 0.880000, 0.530200, color, 2, 1, 0 ); + Touch_AddDefaultButton( "loadquick", "touch_default/load", "loadquick", 0.760000, 0.000000, 0.840000, 0.151486, color, 2, 1, 16 ); + Touch_AddDefaultButton( "savequick", "touch_default/save", "savequick", 0.840000, 0.000000, 0.920000, 0.151486, color, 2, 1, 16 ); + Touch_AddDefaultButton( "messagemode", "touch_default/keyboard", "messagemode", 0.840000, 0.000000, 0.920000, 0.151486, color, 2, 1, 8 ); + Touch_AddDefaultButton( "reload", "touch_default/reload", "+reload", 0.000000, 0.302971, 0.120000, 0.530200, color, 2, 1, 0 ); + Touch_AddDefaultButton( "flashlight", "touch_default/flash_light_filled", "impulse 100", 0.920000, 0.000000, 1.000000, 0.151486, color, 2, 1, 0 ); + Touch_AddDefaultButton( "scores", "touch_default/map", "+showscores", 0.760000, 0.000000, 0.840000, 0.151486, color, 2, 1, 8 ); + Touch_AddDefaultButton( "show_numbers", "touch_default/show_weapons", "exec touch_default/numbers.cfg", 0.440000, 0.833171, 0.520000, 0.984656, color, 2, 1, 0 ); + Touch_AddDefaultButton( "duck", "touch_default/crouch", "+duck", 0.880000, 0.757428, 1.000000, 0.984656, color, 2, 1, 0 ); + Touch_AddDefaultButton( "tduck", "touch_default/tduck", ";+duck", 0.560000, 0.833171, 0.620000, 0.946785, color, 2, 1, 0 ); + Touch_AddDefaultButton( "edit", "touch_default/settings", "touch_enableedit", 0.420000, 0.000000, 0.500000, 0.151486, color, 2, 1, 32 ); + Touch_AddDefaultButton( "menu", "touch_default/menu", "escape", 0.000000, 0.833171, 0.080000, 0.984656, color, 2, 1, 0 ); Cmd_AddCommand( "touch_addbutton", Touch_AddButton_f, "add native touch button" ); @@ -1106,7 +1106,7 @@ void Touch_Init( void ) touch_dpad_radius = Cvar_Get( "touch_dpad_radius", "1.0", FCVAR_FILTERABLE, "dpad radius multiplier" ); touch_joy_radius = Cvar_Get( "touch_joy_radius", "1.0", FCVAR_FILTERABLE, "joy radius multiplier" ); touch_move_indicator = Cvar_Get( "touch_move_indicator", "0.0", FCVAR_FILTERABLE, "indicate move events (0 to disable)" ); - touch_joy_texture = Cvar_Get( "touch_joy_texture", "touch_default/joy.tga", FCVAR_FILTERABLE, "texture for move indicator"); + touch_joy_texture = Cvar_Get( "touch_joy_texture", "touch_default/joy", FCVAR_FILTERABLE, "texture for move indicator"); // input devices cvar touch_enable = Cvar_Get( "touch_enable", DEFAULT_TOUCH_ENABLE, FCVAR_ARCHIVE | FCVAR_FILTERABLE, "enable touch controls" ); @@ -1515,9 +1515,9 @@ static void Touch_EditMove( touchEventType type, int fingerID, float x, float y, touch.hidebutton->flags &= ~TOUCH_FL_HIDE; if( FBitSet( button->flags, TOUCH_FL_HIDE )) - Q_strcpy( touch.hidebutton->texturefile, "touch_default/edit_show.tga" ); + Q_strcpy( touch.hidebutton->texturefile, "touch_default/edit_show" ); else - Q_strcpy( touch.hidebutton->texturefile, "touch_default/edit_hide.tga" ); + Q_strcpy( touch.hidebutton->texturefile, "touch_default/edit_hide" ); } } if( type == event_motion ) // shutdown button move From 7a2ac4a9c0bb7d0c014e4a07dd2fdaee42f614bd Mon Sep 17 00:00:00 2001 From: Alibek Omarov Date: Thu, 8 Sep 2022 04:15:09 +0300 Subject: [PATCH 095/490] engine: client: use generic particles code for blob particles (from tempentity code) only if Quake compatibility was enabled --- engine/client/cl_efx.c | 18 +++++++++++++----- 1 file changed, 13 insertions(+), 5 deletions(-) diff --git a/engine/client/cl_efx.c b/engine/client/cl_efx.c index d7ad1196..6a07dd49 100644 --- a/engine/client/cl_efx.c +++ b/engine/client/cl_efx.c @@ -1111,9 +1111,13 @@ R_ParticleExplosion2 void GAME_EXPORT R_ParticleExplosion2( const vec3_t org, int colorStart, int colorLength ) { int i, j; - int colorMod = 0; + int colorMod = 0, packedColor; particle_t *p; + if( FBitSet( host.features, ENGINE_QUAKE_COMPATIBLE )) + packedColor = 255; // use old code for blob particles + else packedColor = 0; + for( i = 0; i < 512; i++ ) { p = R_AllocParticle( NULL ); @@ -1121,7 +1125,7 @@ void GAME_EXPORT R_ParticleExplosion2( const vec3_t org, int colorStart, int col p->die = cl.time + 0.3f; p->color = colorStart + ( colorMod % colorLength ); - p->packedColor = 255; // use old code for blob particles + p->packedColor = packedColor; colorMod++; p->type = pt_blob; @@ -1143,15 +1147,19 @@ R_BlobExplosion void GAME_EXPORT R_BlobExplosion( const vec3_t org ) { particle_t *p; - int i, j; + int i, j, packedColor; + + if( FBitSet( host.features, ENGINE_QUAKE_COMPATIBLE )) + packedColor = 255; // use old code for blob particles + else packedColor = 0; for( i = 0; i < 1024; i++ ) { p = R_AllocParticle( NULL ); if( !p ) return; - p->die = cl.time + COM_RandomFloat( 2.0f, 2.4f ); - p->packedColor = 255; // use old code for blob particles + p->die = cl.time + COM_RandomFloat( 1.0f, 1.4f ); + p->packedColor = packedColor; if( i & 1 ) { From 0c50e436634c89a2481f5fa079fdef5c623e008e Mon Sep 17 00:00:00 2001 From: Alibek Omarov Date: Thu, 8 Sep 2022 04:15:51 +0300 Subject: [PATCH 096/490] engine: client: enable sound for TE_TAREXPLOSION, use same sound parameters as GoldSrc --- engine/client/cl_tent.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/engine/client/cl_tent.c b/engine/client/cl_tent.c index 36ea6003..8158ce2c 100644 --- a/engine/client/cl_tent.c +++ b/engine/client/cl_tent.c @@ -1974,6 +1974,9 @@ void CL_ParseTempEntity( sizebuf_t *msg ) pos[1] = MSG_ReadCoord( &buf ); pos[2] = MSG_ReadCoord( &buf ); R_BlobExplosion( pos ); + + hSound = S_RegisterSound( cl_explode_sounds[0] ); + S_StartSound( pos, -1, CHAN_AUTO, hSound, VOL_NORM, 1.0f, PITCH_NORM, 0 ); break; case TE_SMOKE: pos[0] = MSG_ReadCoord( &buf ); @@ -2027,7 +2030,7 @@ void CL_ParseTempEntity( sizebuf_t *msg ) dl->decay = 300; hSound = S_RegisterSound( cl_explode_sounds[0] ); - S_StartSound( pos, 0, CHAN_STATIC, hSound, VOL_NORM, 0.6f, PITCH_NORM, 0 ); + S_StartSound( pos, -1, CHAN_AUTO, hSound, VOL_NORM, 0.6f, PITCH_NORM, 0 ); break; case TE_BSPDECAL: case TE_DECAL: From fd152e82e837331b2a34c0a077b686a33d5255f3 Mon Sep 17 00:00:00 2001 From: Andrey Akhmichin <15944199+nekonomicon@users.noreply.github.com> Date: Mon, 5 Sep 2022 00:44:35 +0500 Subject: [PATCH 097/490] engine: common: imagelib: img_png.c: add support for indexed and grayscale PNG images. --- engine/common/imagelib/img_png.c | 138 ++++++++++++++++++++++++++----- engine/common/imagelib/img_png.h | 2 +- 2 files changed, 120 insertions(+), 20 deletions(-) diff --git a/engine/common/imagelib/img_png.c b/engine/common/imagelib/img_png.c index 2c129ba1..25b32544 100644 --- a/engine/common/imagelib/img_png.c +++ b/engine/common/imagelib/img_png.c @@ -26,6 +26,8 @@ GNU General Public License for more details. static const char png_sign[] = {0x89, 'P', 'N', 'G', '\r', '\n', 0x1a, '\n'}; static const char ihdr_sign[] = {'I', 'H', 'D', 'R'}; +static const char trns_sign[] = {'t', 'R', 'N', 'S'}; +static const char plte_sign[] = {'P', 'L', 'T', 'E'}; static const char idat_sign[] = {'I', 'D', 'A', 'T'}; static const char iend_sign[] = {'I', 'E', 'N', 'D'}; static const int iend_crc32 = 0xAE426082; @@ -40,8 +42,9 @@ qboolean Image_LoadPNG( const char *name, const byte *buffer, fs_offset_t filesi int ret; short p, a, b, c, pa, pb, pc; byte *buf_p, *pixbuf, *raw, *prior, *idat_buf = NULL, *uncompressed_buffer = NULL, *rowend; - uint chunk_len, crc32, crc32_check, oldsize = 0, newsize = 0, rowsize; - uint uncompressed_size, pixel_size, i, y, filter_type, chunk_sign; + byte *pallete = NULL, *trns = NULL; + uint chunk_len, trns_len, crc32, crc32_check, oldsize = 0, newsize = 0, rowsize; + uint uncompressed_size, pixel_size, i, y, filter_type, chunk_sign, r_alpha, g_alpha, b_alpha; qboolean has_iend_chunk = false; z_stream stream = {0}; png_t png_hdr; @@ -67,7 +70,7 @@ qboolean Image_LoadPNG( const char *name, const byte *buffer, fs_offset_t filesi // check IHDR chunk length (valid value - 13) if( png_hdr.ihdr_len != sizeof( png_ihdr_t ) ) { - Con_DPrintf( S_ERROR "Image_LoadPNG: Invalid IHDR chunk size (%s)\n", name ); + Con_DPrintf( S_ERROR "Image_LoadPNG: Invalid IHDR chunk size %u (%s)\n", png_hdr.ihdr_len, name ); return false; } @@ -84,7 +87,7 @@ qboolean Image_LoadPNG( const char *name, const byte *buffer, fs_offset_t filesi if( png_hdr.ihdr_chunk.height == 0 || png_hdr.ihdr_chunk.width == 0 ) { - Con_DPrintf( S_ERROR "Image_LoadPNG: Invalid image size %dx%d (%s)\n", png_hdr.ihdr_chunk.width, png_hdr.ihdr_chunk.height, name ); + Con_DPrintf( S_ERROR "Image_LoadPNG: Invalid image size %ux%u (%s)\n", png_hdr.ihdr_chunk.width, png_hdr.ihdr_chunk.height, name ); return false; } @@ -94,21 +97,25 @@ qboolean Image_LoadPNG( const char *name, const byte *buffer, fs_offset_t filesi return false; } - if( png_hdr.ihdr_chunk.colortype != PNG_CT_RGB && png_hdr.ihdr_chunk.colortype != PNG_CT_RGBA ) + if( !( png_hdr.ihdr_chunk.colortype == PNG_CT_RGB + || png_hdr.ihdr_chunk.colortype == PNG_CT_RGBA + || png_hdr.ihdr_chunk.colortype == PNG_CT_GREY + || png_hdr.ihdr_chunk.colortype == PNG_CT_ALPHA + || png_hdr.ihdr_chunk.colortype == PNG_CT_PALLETE ) ) { - Con_DPrintf( S_WARN "Image_LoadPNG: Only 8-bit RGB and RGBA images is supported (%s)\n", name ); + Con_DPrintf( S_WARN "Image_LoadPNG: Unknown color type %u (%s)\n", png_hdr.ihdr_chunk.colortype, name ); return false; } if( png_hdr.ihdr_chunk.compression > 0 ) { - Con_DPrintf( S_ERROR "Image_LoadPNG: Unknown compression method (%s)\n", name ); + Con_DPrintf( S_ERROR "Image_LoadPNG: Unknown compression method %u (%s)\n", png_hdr.ihdr_chunk.compression, name ); return false; } if( png_hdr.ihdr_chunk.filter > 0 ) { - Con_DPrintf( S_ERROR "Image_LoadPNG: Unknown filter type (%s)\n", name ); + Con_DPrintf( S_ERROR "Image_LoadPNG: Unknown filter type %u (%s)\n", png_hdr.ihdr_chunk.filter, name ); return false; } @@ -120,7 +127,7 @@ qboolean Image_LoadPNG( const char *name, const byte *buffer, fs_offset_t filesi if( png_hdr.ihdr_chunk.interlace > 0 ) { - Con_DPrintf( S_ERROR "Image_LoadPNG: Unknown interlacing type (%s)\n", name ); + Con_DPrintf( S_ERROR "Image_LoadPNG: Unknown interlacing type %u (%s)\n", png_hdr.ihdr_chunk.interlace, name ); return false; } @@ -158,8 +165,19 @@ qboolean Image_LoadPNG( const char *name, const byte *buffer, fs_offset_t filesi // move pointer buf_p += sizeof( chunk_sign ); + // find transparency + if( !memcmp( buf_p, trns_sign, sizeof( trns_sign ) ) ) + { + trns = buf_p + sizeof( trns_sign ); + trns_len = chunk_len; + } + // find pallete for indexed image + else if( !memcmp( buf_p, plte_sign, sizeof( plte_sign ) ) ) + { + pallete = buf_p + sizeof( plte_sign ); + } // get all IDAT chunks data - if( !memcmp( buf_p, idat_sign, sizeof( idat_sign ) ) ) + else if( !memcmp( buf_p, idat_sign, sizeof( idat_sign ) ) ) { newsize = oldsize + chunk_len; idat_buf = (byte *)Mem_Realloc( host.imagepool, idat_buf, newsize ); @@ -193,6 +211,13 @@ qboolean Image_LoadPNG( const char *name, const byte *buffer, fs_offset_t filesi buf_p += sizeof( crc32 ); } + if( png_hdr.ihdr_chunk.colortype == PNG_CT_PALLETE && !pallete ) + { + Con_DPrintf( S_ERROR "Image_LoadPNG: PLTE chunk not found (%s)\n", name ); + Mem_Free( idat_buf ); + return false; + } + if( !has_iend_chunk ) { Con_DPrintf( S_ERROR "Image_LoadPNG: IEND chunk not found (%s)\n", name ); @@ -202,7 +227,7 @@ qboolean Image_LoadPNG( const char *name, const byte *buffer, fs_offset_t filesi if( chunk_len != 0 ) { - Con_DPrintf( S_ERROR "Image_LoadPNG: IEND chunk has wrong size (%s)\n", name ); + Con_DPrintf( S_ERROR "Image_LoadPNG: IEND chunk has wrong size %u (%s)\n", chunk_len, name ); Mem_Free( idat_buf ); return false; } @@ -215,6 +240,13 @@ qboolean Image_LoadPNG( const char *name, const byte *buffer, fs_offset_t filesi switch( png_hdr.ihdr_chunk.colortype ) { + case PNG_CT_GREY: + case PNG_CT_PALLETE: + pixel_size = 1; + break; + case PNG_CT_ALPHA: + pixel_size = 2; + break; case PNG_CT_RGB: pixel_size = 3; break; @@ -231,9 +263,11 @@ qboolean Image_LoadPNG( const char *name, const byte *buffer, fs_offset_t filesi image.width = png_hdr.ihdr_chunk.width; image.height = png_hdr.ihdr_chunk.height; image.size = image.height * image.width * 4; - image.flags |= IMAGE_HAS_COLOR; - if( png_hdr.ihdr_chunk.colortype == PNG_CT_RGBA ) + if( png_hdr.ihdr_chunk.colortype & PNG_CT_RGB ) + image.flags |= IMAGE_HAS_COLOR; + + if( trns || ( png_hdr.ihdr_chunk.colortype & PNG_CT_ALPHA ) ) image.flags |= IMAGE_HAS_ALPHA; image.depth = 1; @@ -275,7 +309,7 @@ qboolean Image_LoadPNG( const char *name, const byte *buffer, fs_offset_t filesi raw = uncompressed_buffer; - if( png_hdr.ihdr_chunk.colortype == PNG_CT_RGB ) + if( png_hdr.ihdr_chunk.colortype != PNG_CT_RGBA ) prior = pixbuf = raw; filter_type = *raw++; @@ -377,11 +411,18 @@ qboolean Image_LoadPNG( const char *name, const byte *buffer, fs_offset_t filesi prior = pixbuf; } - // convert RGB-to-RGBA - if( png_hdr.ihdr_chunk.colortype == PNG_CT_RGB ) + pixbuf = image.rgba; + raw = uncompressed_buffer; + + switch( png_hdr.ihdr_chunk.colortype ) { - pixbuf = image.rgba; - raw = uncompressed_buffer; + case PNG_CT_RGB: + if( trns ) + { + r_alpha = trns[0] << 8 | trns[1]; + g_alpha = trns[2] << 8 | trns[3]; + b_alpha = trns[4] << 8 | trns[5]; + } for( y = 0; y < image.height; y++ ) { @@ -391,9 +432,68 @@ qboolean Image_LoadPNG( const char *name, const byte *buffer, fs_offset_t filesi *pixbuf++ = raw[0]; *pixbuf++ = raw[1]; *pixbuf++ = raw[2]; - *pixbuf++ = 0xFF; + + if( trns && r_alpha == raw[0] + && g_alpha == raw[1] + && b_alpha == raw[2] ) + *pixbuf++ = 0; + else + *pixbuf++ = 0xFF; } } + break; + case PNG_CT_GREY: + if( trns ) + r_alpha = trns[0] << 8 | trns[1]; + + for( y = 0; y < image.height; y++ ) + { + rowend = raw + rowsize; + for( ; raw < rowend; raw += pixel_size ) + { + *pixbuf++ = raw[0]; + *pixbuf++ = raw[0]; + *pixbuf++ = raw[0]; + + if( trns && r_alpha == raw[0] ) + *pixbuf++ = 0; + else + *pixbuf++ = 0xFF; + } + } + break; + case PNG_CT_ALPHA: + for( y = 0; y < image.height; y++ ) + { + rowend = raw + rowsize; + for( ; raw < rowend; raw += pixel_size ) + { + *pixbuf++ = raw[0]; + *pixbuf++ = raw[0]; + *pixbuf++ = raw[0]; + *pixbuf++ = raw[1]; + } + } + break; + case PNG_CT_PALLETE: + for( y = 0; y < image.height; y++ ) + { + rowend = raw + rowsize; + for( ; raw < rowend; raw += pixel_size ) + { + *pixbuf++ = pallete[raw[0] + 2]; + *pixbuf++ = pallete[raw[0] + 1]; + *pixbuf++ = pallete[raw[0] + 0]; + + if( trns && raw[0] < trns_len ) + *pixbuf++ = trns[raw[0]]; + else + *pixbuf++ = 0xFF; + } + } + break; + default: + break; } Mem_Free( uncompressed_buffer ); diff --git a/engine/common/imagelib/img_png.h b/engine/common/imagelib/img_png.h index 3477805e..ab1255df 100644 --- a/engine/common/imagelib/img_png.h +++ b/engine/common/imagelib/img_png.h @@ -25,8 +25,8 @@ GNU General Public License for more details. enum png_colortype { PNG_CT_GREY, - PNG_CT_PALLETE = BIT(0), PNG_CT_RGB = BIT(1), + PNG_CT_PALLETE = (PNG_CT_RGB|BIT(0)), PNG_CT_ALPHA = BIT(2), PNG_CT_RGBA = (PNG_CT_RGB|PNG_CT_ALPHA) }; From 7e664679a050b4cce5597e766c8ebc27a2293f87 Mon Sep 17 00:00:00 2001 From: Alibek Omarov Date: Sat, 10 Sep 2022 19:01:38 +0300 Subject: [PATCH 098/490] 3rdparty: move mainui and vgui_support submodules into 3rdparty folder --- .gitmodules | 4 ++-- mainui => 3rdparty/mainui | 0 vgui_support => 3rdparty/vgui_support | 0 3 files changed, 2 insertions(+), 2 deletions(-) rename mainui => 3rdparty/mainui (100%) rename vgui_support => 3rdparty/vgui_support (100%) diff --git a/.gitmodules b/.gitmodules index 71d05e6d..d38cabaf 100644 --- a/.gitmodules +++ b/.gitmodules @@ -1,5 +1,5 @@ [submodule "mainui"] - path = mainui + path = 3rdparty/mainui url = https://github.com/FWGS/mainui_cpp [submodule "ref_gl/nanogl"] path = ref_gl/nanogl @@ -11,7 +11,7 @@ path = ref_gl/gl4es url = https://github.com/ptitSeb/gl4es [submodule "vgui_support"] - path = vgui_support + path = 3rdparty/vgui_support url = https://github.com/FWGS/vgui_support [submodule "opus"] path = 3rdparty/opus/opus diff --git a/mainui b/3rdparty/mainui similarity index 100% rename from mainui rename to 3rdparty/mainui diff --git a/vgui_support b/3rdparty/vgui_support similarity index 100% rename from vgui_support rename to 3rdparty/vgui_support From 61c142e2db5cf3a3b51ced095fb4a71ace1c8b69 Mon Sep 17 00:00:00 2001 From: Alibek Omarov Date: Sat, 10 Sep 2022 19:02:18 +0300 Subject: [PATCH 099/490] ref: move renderers into ref subdirectory --- .gitmodules | 6 +++--- {ref_gl => ref/gl}/exports.txt | 0 {ref_gl => ref/gl}/gl-wes-v2 | 0 {ref_gl => ref/gl}/gl4es | 0 {ref_gl => ref/gl}/gl_alias.c | 0 {ref_gl => ref/gl}/gl_backend.c | 0 {ref_gl => ref/gl}/gl_beams.c | 0 {ref_gl => ref/gl}/gl_context.c | 0 {ref_gl => ref/gl}/gl_cull.c | 0 {ref_gl => ref/gl}/gl_dbghulls.c | 0 {ref_gl => ref/gl}/gl_decals.c | 0 {ref_gl => ref/gl}/gl_draw.c | 0 {ref_gl => ref/gl}/gl_export.h | 0 {ref_gl => ref/gl}/gl_frustum.c | 0 {ref_gl => ref/gl}/gl_frustum.h | 0 {ref_gl => ref/gl}/gl_image.c | 0 {ref_gl => ref/gl}/gl_local.h | 0 {ref_gl => ref/gl}/gl_opengl.c | 0 {ref_gl => ref/gl}/gl_rlight.c | 0 {ref_gl => ref/gl}/gl_rmain.c | 0 {ref_gl => ref/gl}/gl_rmath.c | 0 {ref_gl => ref/gl}/gl_rmisc.c | 0 {ref_gl => ref/gl}/gl_rpart.c | 0 {ref_gl => ref/gl}/gl_rsurf.c | 0 {ref_gl => ref/gl}/gl_sprite.c | 0 {ref_gl => ref/gl}/gl_studio.c | 0 {ref_gl => ref/gl}/gl_triapi.c | 0 {ref_gl => ref/gl}/gl_vgui.c | 0 {ref_gl => ref/gl}/gl_warp.c | 0 {ref_gl => ref/gl}/nanogl | 0 {ref_gl => ref/gl}/wscript | 0 {ref_soft => ref/soft}/adivtab.h | 0 {ref_soft => ref/soft}/r_aclip.c | 0 {ref_soft => ref/soft}/r_beams.c | 0 {ref_soft => ref/soft}/r_bsp.c | 0 {ref_soft => ref/soft}/r_context.c | 0 {ref_soft => ref/soft}/r_decals.c | 0 {ref_soft => ref/soft}/r_draw.c | 0 {ref_soft => ref/soft}/r_edge.c | 0 {ref_soft => ref/soft}/r_glblit.c | 0 {ref_soft => ref/soft}/r_image.c | 0 {ref_soft => ref/soft}/r_light.c | 0 {ref_soft => ref/soft}/r_local.h | 0 {ref_soft => ref/soft}/r_main.c | 0 {ref_soft => ref/soft}/r_math.c | 0 {ref_soft => ref/soft}/r_misc.c | 0 {ref_soft => ref/soft}/r_part.c | 0 {ref_soft => ref/soft}/r_polyse.c | 0 {ref_soft => ref/soft}/r_rast.c | 0 {ref_soft => ref/soft}/r_scan.c | 0 {ref_soft => ref/soft}/r_sprite.c | 0 {ref_soft => ref/soft}/r_studio.c | 0 {ref_soft => ref/soft}/r_surf.c | 0 {ref_soft => ref/soft}/r_trialias.c | 0 {ref_soft => ref/soft}/r_triapi.c | 0 {ref_soft => ref/soft}/r_vgui.c | 0 {ref_soft => ref/soft}/wscript | 0 57 files changed, 3 insertions(+), 3 deletions(-) rename {ref_gl => ref/gl}/exports.txt (100%) rename {ref_gl => ref/gl}/gl-wes-v2 (100%) rename {ref_gl => ref/gl}/gl4es (100%) rename {ref_gl => ref/gl}/gl_alias.c (100%) rename {ref_gl => ref/gl}/gl_backend.c (100%) rename {ref_gl => ref/gl}/gl_beams.c (100%) rename {ref_gl => ref/gl}/gl_context.c (100%) rename {ref_gl => ref/gl}/gl_cull.c (100%) rename {ref_gl => ref/gl}/gl_dbghulls.c (100%) rename {ref_gl => ref/gl}/gl_decals.c (100%) rename {ref_gl => ref/gl}/gl_draw.c (100%) rename {ref_gl => ref/gl}/gl_export.h (100%) rename {ref_gl => ref/gl}/gl_frustum.c (100%) rename {ref_gl => ref/gl}/gl_frustum.h (100%) rename {ref_gl => ref/gl}/gl_image.c (100%) rename {ref_gl => ref/gl}/gl_local.h (100%) rename {ref_gl => ref/gl}/gl_opengl.c (100%) rename {ref_gl => ref/gl}/gl_rlight.c (100%) rename {ref_gl => ref/gl}/gl_rmain.c (100%) rename {ref_gl => ref/gl}/gl_rmath.c (100%) rename {ref_gl => ref/gl}/gl_rmisc.c (100%) rename {ref_gl => ref/gl}/gl_rpart.c (100%) rename {ref_gl => ref/gl}/gl_rsurf.c (100%) rename {ref_gl => ref/gl}/gl_sprite.c (100%) rename {ref_gl => ref/gl}/gl_studio.c (100%) rename {ref_gl => ref/gl}/gl_triapi.c (100%) rename {ref_gl => ref/gl}/gl_vgui.c (100%) rename {ref_gl => ref/gl}/gl_warp.c (100%) rename {ref_gl => ref/gl}/nanogl (100%) rename {ref_gl => ref/gl}/wscript (100%) rename {ref_soft => ref/soft}/adivtab.h (100%) rename {ref_soft => ref/soft}/r_aclip.c (100%) rename {ref_soft => ref/soft}/r_beams.c (100%) rename {ref_soft => ref/soft}/r_bsp.c (100%) rename {ref_soft => ref/soft}/r_context.c (100%) rename {ref_soft => ref/soft}/r_decals.c (100%) rename {ref_soft => ref/soft}/r_draw.c (100%) rename {ref_soft => ref/soft}/r_edge.c (100%) rename {ref_soft => ref/soft}/r_glblit.c (100%) rename {ref_soft => ref/soft}/r_image.c (100%) rename {ref_soft => ref/soft}/r_light.c (100%) rename {ref_soft => ref/soft}/r_local.h (100%) rename {ref_soft => ref/soft}/r_main.c (100%) rename {ref_soft => ref/soft}/r_math.c (100%) rename {ref_soft => ref/soft}/r_misc.c (100%) rename {ref_soft => ref/soft}/r_part.c (100%) rename {ref_soft => ref/soft}/r_polyse.c (100%) rename {ref_soft => ref/soft}/r_rast.c (100%) rename {ref_soft => ref/soft}/r_scan.c (100%) rename {ref_soft => ref/soft}/r_sprite.c (100%) rename {ref_soft => ref/soft}/r_studio.c (100%) rename {ref_soft => ref/soft}/r_surf.c (100%) rename {ref_soft => ref/soft}/r_trialias.c (100%) rename {ref_soft => ref/soft}/r_triapi.c (100%) rename {ref_soft => ref/soft}/r_vgui.c (100%) rename {ref_soft => ref/soft}/wscript (100%) diff --git a/.gitmodules b/.gitmodules index d38cabaf..9cb19541 100644 --- a/.gitmodules +++ b/.gitmodules @@ -2,13 +2,13 @@ path = 3rdparty/mainui url = https://github.com/FWGS/mainui_cpp [submodule "ref_gl/nanogl"] - path = ref_gl/nanogl + path = ref/gl/nanogl url = https://github.com/FWGS/nanogl [submodule "ref_gl/gl-wes-v2"] - path = ref_gl/gl-wes-v2 + path = ref/gl/gl-wes-v2 url = https://github.com/FWGS/gl-wes-v2 [submodule "ref_gl/gl4es"] - path = ref_gl/gl4es + path = ref/gl/gl4es url = https://github.com/ptitSeb/gl4es [submodule "vgui_support"] path = 3rdparty/vgui_support diff --git a/ref_gl/exports.txt b/ref/gl/exports.txt similarity index 100% rename from ref_gl/exports.txt rename to ref/gl/exports.txt diff --git a/ref_gl/gl-wes-v2 b/ref/gl/gl-wes-v2 similarity index 100% rename from ref_gl/gl-wes-v2 rename to ref/gl/gl-wes-v2 diff --git a/ref_gl/gl4es b/ref/gl/gl4es similarity index 100% rename from ref_gl/gl4es rename to ref/gl/gl4es diff --git a/ref_gl/gl_alias.c b/ref/gl/gl_alias.c similarity index 100% rename from ref_gl/gl_alias.c rename to ref/gl/gl_alias.c diff --git a/ref_gl/gl_backend.c b/ref/gl/gl_backend.c similarity index 100% rename from ref_gl/gl_backend.c rename to ref/gl/gl_backend.c diff --git a/ref_gl/gl_beams.c b/ref/gl/gl_beams.c similarity index 100% rename from ref_gl/gl_beams.c rename to ref/gl/gl_beams.c diff --git a/ref_gl/gl_context.c b/ref/gl/gl_context.c similarity index 100% rename from ref_gl/gl_context.c rename to ref/gl/gl_context.c diff --git a/ref_gl/gl_cull.c b/ref/gl/gl_cull.c similarity index 100% rename from ref_gl/gl_cull.c rename to ref/gl/gl_cull.c diff --git a/ref_gl/gl_dbghulls.c b/ref/gl/gl_dbghulls.c similarity index 100% rename from ref_gl/gl_dbghulls.c rename to ref/gl/gl_dbghulls.c diff --git a/ref_gl/gl_decals.c b/ref/gl/gl_decals.c similarity index 100% rename from ref_gl/gl_decals.c rename to ref/gl/gl_decals.c diff --git a/ref_gl/gl_draw.c b/ref/gl/gl_draw.c similarity index 100% rename from ref_gl/gl_draw.c rename to ref/gl/gl_draw.c diff --git a/ref_gl/gl_export.h b/ref/gl/gl_export.h similarity index 100% rename from ref_gl/gl_export.h rename to ref/gl/gl_export.h diff --git a/ref_gl/gl_frustum.c b/ref/gl/gl_frustum.c similarity index 100% rename from ref_gl/gl_frustum.c rename to ref/gl/gl_frustum.c diff --git a/ref_gl/gl_frustum.h b/ref/gl/gl_frustum.h similarity index 100% rename from ref_gl/gl_frustum.h rename to ref/gl/gl_frustum.h diff --git a/ref_gl/gl_image.c b/ref/gl/gl_image.c similarity index 100% rename from ref_gl/gl_image.c rename to ref/gl/gl_image.c diff --git a/ref_gl/gl_local.h b/ref/gl/gl_local.h similarity index 100% rename from ref_gl/gl_local.h rename to ref/gl/gl_local.h diff --git a/ref_gl/gl_opengl.c b/ref/gl/gl_opengl.c similarity index 100% rename from ref_gl/gl_opengl.c rename to ref/gl/gl_opengl.c diff --git a/ref_gl/gl_rlight.c b/ref/gl/gl_rlight.c similarity index 100% rename from ref_gl/gl_rlight.c rename to ref/gl/gl_rlight.c diff --git a/ref_gl/gl_rmain.c b/ref/gl/gl_rmain.c similarity index 100% rename from ref_gl/gl_rmain.c rename to ref/gl/gl_rmain.c diff --git a/ref_gl/gl_rmath.c b/ref/gl/gl_rmath.c similarity index 100% rename from ref_gl/gl_rmath.c rename to ref/gl/gl_rmath.c diff --git a/ref_gl/gl_rmisc.c b/ref/gl/gl_rmisc.c similarity index 100% rename from ref_gl/gl_rmisc.c rename to ref/gl/gl_rmisc.c diff --git a/ref_gl/gl_rpart.c b/ref/gl/gl_rpart.c similarity index 100% rename from ref_gl/gl_rpart.c rename to ref/gl/gl_rpart.c diff --git a/ref_gl/gl_rsurf.c b/ref/gl/gl_rsurf.c similarity index 100% rename from ref_gl/gl_rsurf.c rename to ref/gl/gl_rsurf.c diff --git a/ref_gl/gl_sprite.c b/ref/gl/gl_sprite.c similarity index 100% rename from ref_gl/gl_sprite.c rename to ref/gl/gl_sprite.c diff --git a/ref_gl/gl_studio.c b/ref/gl/gl_studio.c similarity index 100% rename from ref_gl/gl_studio.c rename to ref/gl/gl_studio.c diff --git a/ref_gl/gl_triapi.c b/ref/gl/gl_triapi.c similarity index 100% rename from ref_gl/gl_triapi.c rename to ref/gl/gl_triapi.c diff --git a/ref_gl/gl_vgui.c b/ref/gl/gl_vgui.c similarity index 100% rename from ref_gl/gl_vgui.c rename to ref/gl/gl_vgui.c diff --git a/ref_gl/gl_warp.c b/ref/gl/gl_warp.c similarity index 100% rename from ref_gl/gl_warp.c rename to ref/gl/gl_warp.c diff --git a/ref_gl/nanogl b/ref/gl/nanogl similarity index 100% rename from ref_gl/nanogl rename to ref/gl/nanogl diff --git a/ref_gl/wscript b/ref/gl/wscript similarity index 100% rename from ref_gl/wscript rename to ref/gl/wscript diff --git a/ref_soft/adivtab.h b/ref/soft/adivtab.h similarity index 100% rename from ref_soft/adivtab.h rename to ref/soft/adivtab.h diff --git a/ref_soft/r_aclip.c b/ref/soft/r_aclip.c similarity index 100% rename from ref_soft/r_aclip.c rename to ref/soft/r_aclip.c diff --git a/ref_soft/r_beams.c b/ref/soft/r_beams.c similarity index 100% rename from ref_soft/r_beams.c rename to ref/soft/r_beams.c diff --git a/ref_soft/r_bsp.c b/ref/soft/r_bsp.c similarity index 100% rename from ref_soft/r_bsp.c rename to ref/soft/r_bsp.c diff --git a/ref_soft/r_context.c b/ref/soft/r_context.c similarity index 100% rename from ref_soft/r_context.c rename to ref/soft/r_context.c diff --git a/ref_soft/r_decals.c b/ref/soft/r_decals.c similarity index 100% rename from ref_soft/r_decals.c rename to ref/soft/r_decals.c diff --git a/ref_soft/r_draw.c b/ref/soft/r_draw.c similarity index 100% rename from ref_soft/r_draw.c rename to ref/soft/r_draw.c diff --git a/ref_soft/r_edge.c b/ref/soft/r_edge.c similarity index 100% rename from ref_soft/r_edge.c rename to ref/soft/r_edge.c diff --git a/ref_soft/r_glblit.c b/ref/soft/r_glblit.c similarity index 100% rename from ref_soft/r_glblit.c rename to ref/soft/r_glblit.c diff --git a/ref_soft/r_image.c b/ref/soft/r_image.c similarity index 100% rename from ref_soft/r_image.c rename to ref/soft/r_image.c diff --git a/ref_soft/r_light.c b/ref/soft/r_light.c similarity index 100% rename from ref_soft/r_light.c rename to ref/soft/r_light.c diff --git a/ref_soft/r_local.h b/ref/soft/r_local.h similarity index 100% rename from ref_soft/r_local.h rename to ref/soft/r_local.h diff --git a/ref_soft/r_main.c b/ref/soft/r_main.c similarity index 100% rename from ref_soft/r_main.c rename to ref/soft/r_main.c diff --git a/ref_soft/r_math.c b/ref/soft/r_math.c similarity index 100% rename from ref_soft/r_math.c rename to ref/soft/r_math.c diff --git a/ref_soft/r_misc.c b/ref/soft/r_misc.c similarity index 100% rename from ref_soft/r_misc.c rename to ref/soft/r_misc.c diff --git a/ref_soft/r_part.c b/ref/soft/r_part.c similarity index 100% rename from ref_soft/r_part.c rename to ref/soft/r_part.c diff --git a/ref_soft/r_polyse.c b/ref/soft/r_polyse.c similarity index 100% rename from ref_soft/r_polyse.c rename to ref/soft/r_polyse.c diff --git a/ref_soft/r_rast.c b/ref/soft/r_rast.c similarity index 100% rename from ref_soft/r_rast.c rename to ref/soft/r_rast.c diff --git a/ref_soft/r_scan.c b/ref/soft/r_scan.c similarity index 100% rename from ref_soft/r_scan.c rename to ref/soft/r_scan.c diff --git a/ref_soft/r_sprite.c b/ref/soft/r_sprite.c similarity index 100% rename from ref_soft/r_sprite.c rename to ref/soft/r_sprite.c diff --git a/ref_soft/r_studio.c b/ref/soft/r_studio.c similarity index 100% rename from ref_soft/r_studio.c rename to ref/soft/r_studio.c diff --git a/ref_soft/r_surf.c b/ref/soft/r_surf.c similarity index 100% rename from ref_soft/r_surf.c rename to ref/soft/r_surf.c diff --git a/ref_soft/r_trialias.c b/ref/soft/r_trialias.c similarity index 100% rename from ref_soft/r_trialias.c rename to ref/soft/r_trialias.c diff --git a/ref_soft/r_triapi.c b/ref/soft/r_triapi.c similarity index 100% rename from ref_soft/r_triapi.c rename to ref/soft/r_triapi.c diff --git a/ref_soft/r_vgui.c b/ref/soft/r_vgui.c similarity index 100% rename from ref_soft/r_vgui.c rename to ref/soft/r_vgui.c diff --git a/ref_soft/wscript b/ref/soft/wscript similarity index 100% rename from ref_soft/wscript rename to ref/soft/wscript From 17b091e5a9c57f9fd5b6141b649287b472f82f88 Mon Sep 17 00:00:00 2001 From: Alibek Omarov Date: Sat, 10 Sep 2022 19:06:56 +0300 Subject: [PATCH 100/490] wscript: adapt to new folder structure --- wscript | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/wscript b/wscript index 82c50dbb..a6e97f63 100644 --- a/wscript +++ b/wscript @@ -39,12 +39,12 @@ SUBDIRS = [ Subproject('dllemu'), # disable only by engine feature, makes no sense to even parse subprojects in dedicated mode - Subproject('ref_gl', lambda x: not x.env.DEDICATED), - Subproject('ref_soft', lambda x: not x.env.DEDICATED), - Subproject('mainui', lambda x: not x.env.DEDICATED), - Subproject('vgui_support', lambda x: not x.env.DEDICATED), - Subproject('stub/client', lambda x: not x.env.DEDICATED), - Subproject('game_launch', lambda x: not x.env.SINGLE_BINARY and x.env.DEST_OS != 'android'), + Subproject('ref/gl', lambda x: not x.env.DEDICATED), + Subproject('ref/soft', lambda x: not x.env.DEDICATED), + Subproject('3rdparty/mainui', lambda x: not x.env.DEDICATED), + Subproject('3rdparty/vgui_support', lambda x: not x.env.DEDICATED), + Subproject('stub/client', lambda x: not x.env.DEDICATED), + Subproject('game_launch', lambda x: not x.env.SINGLE_BINARY and x.env.DEST_OS != 'android'), # disable only by external dependency presense Subproject('3rdparty/opus', lambda x: not x.env.HAVE_SYSTEM_OPUS and not x.env.DEDICATED), From e54289f811d6c100d6baf0b7a3a47dd8fab96d37 Mon Sep 17 00:00:00 2001 From: Alibek Omarov Date: Sat, 10 Sep 2022 19:55:37 +0300 Subject: [PATCH 101/490] public: wscript: add dummy sdk_includes target that only exposes standard HLSDK include paths --- public/wscript | 14 ++++---------- 1 file changed, 4 insertions(+), 10 deletions(-) diff --git a/public/wscript b/public/wscript index d2ad2ea8..cc20f0c5 100644 --- a/public/wscript +++ b/public/wscript @@ -16,15 +16,9 @@ def configure(conf): return def build(bld): - source = bld.path.ant_glob(['*.c']) - libs = [] - includes = [ '.', '../common', '../engine' ] - - bld.stlib( - source = source, + bld(name = 'sdk_includes', export_includes = '. ../common ../pm_shared ../engine') + bld.stlib(source = bld.path.ant_glob('*.c'), target = 'public', features = 'c', - includes = includes, - use = libs, - subsystem = bld.env.MSVC_SUBSYSTEM - ) + use = 'sdk_includes', + subsystem = bld.env.MSVC_SUBSYSTEM) From 0ed939196998f9947bf8d2529ddf23ae789c33e8 Mon Sep 17 00:00:00 2001 From: Alibek Omarov Date: Sat, 10 Sep 2022 19:56:12 +0300 Subject: [PATCH 102/490] filesystem: wscript: add dummy filesystem_includes target that only exposes public filesystem_stdio include paths --- filesystem/wscript | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/filesystem/wscript b/filesystem/wscript index 8a4e421a..2b8d8a8b 100644 --- a/filesystem/wscript +++ b/filesystem/wscript @@ -15,10 +15,10 @@ def configure(conf): conf.env.cxxshlib_PATTERN = conf.env.cxxshlib_PATTERN[3:] def build(bld): + bld(name = 'filesystem_includes', export_includes = '.') bld.shlib(target = 'filesystem_stdio', features = 'cxx', source = bld.path.ant_glob(['*.c', '*.cpp']), - includes = ['.', '../common', '../public', '../engine'], - use = ['public'], + use = 'filesystem_includes public', install_path = bld.env.LIBDIR, subsystem = bld.env.MSVC_SUBSYSTEM) From 8ffb3aac8a91ea9e0e70765e0683192fbf87de48 Mon Sep 17 00:00:00 2001 From: Alibek Omarov Date: Sat, 10 Sep 2022 19:57:04 +0300 Subject: [PATCH 103/490] engine: wscript: add dummy engine_includes target that only exposes few internal Xash headers for renderers and utils use --- engine/wscript | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/engine/wscript b/engine/wscript index 3df51568..eb64e7b4 100644 --- a/engine/wscript +++ b/engine/wscript @@ -106,8 +106,11 @@ def configure(conf): conf.define_cond('PSAPI_VERSION', conf.env.DEST_OS == 'win32') # will be defined as 1 def build(bld): + # public includes for renderers and utils use + bld(name = 'engine_includes', export_includes = '. common common/imagelib', use = 'filesystem_includes') + is_cxx_link = False - libs = [ 'public', 'dllemu' ] + libs = [ 'engine_includes', 'public', 'dllemu' ] # basic build: dedicated only source = bld.path.ant_glob([ @@ -167,7 +170,7 @@ def build(bld): 'client/avi/*.c']) libs += ['opus'] - includes = ['common', 'server', 'client', 'client/vgui', 'tests', '.', '../public', '../common', '../filesystem', '../pm_shared' ] + includes = ['server', 'client', 'client/vgui' ] if bld.env.SINGLE_BINARY: install_path = bld.env.BINDIR From 3c8fe508f041c80f9e5669f4001cd1d841a4fd2b Mon Sep 17 00:00:00 2001 From: Alibek Omarov Date: Sat, 10 Sep 2022 20:05:21 +0300 Subject: [PATCH 104/490] game_launch: convert to include targets usage --- game_launch/wscript | 9 ++------- 1 file changed, 2 insertions(+), 7 deletions(-) diff --git a/game_launch/wscript b/game_launch/wscript index d4dab31c..d4ed3ae0 100644 --- a/game_launch/wscript +++ b/game_launch/wscript @@ -21,21 +21,16 @@ def configure(conf): conf.define_cond('XASH_DISABLE_MENU_CHANGEGAME', conf.options.DISABLE_MENU_CHANGEGAME) def build(bld): - source = ['game.cpp'] - includes = '. ../common ../public' - libs = [] - + libs = ['sdk_includes'] if bld.env.DEST_OS != 'win32': libs += [ 'DL' ] else: libs += ['USER32', 'SHELL32'] source += ['game.rc'] - bld( - source = source, + bld(source = 'game.cpp', target = 'xash3d', # hl.exe features = 'c cxx cxxprogram', - includes = includes, use = libs, rpath = '$ORIGIN', install_path = bld.env.BINDIR, From 6d37398fad4bee977320cdbb478528230aab1b9d Mon Sep 17 00:00:00 2001 From: Alibek Omarov Date: Sat, 10 Sep 2022 20:05:35 +0300 Subject: [PATCH 105/490] ref: gl: convert to include targets usage --- ref/gl/wscript | 13 ++----------- 1 file changed, 2 insertions(+), 11 deletions(-) diff --git a/ref/gl/wscript b/ref/gl/wscript index 207e7ec6..82964c98 100644 --- a/ref/gl/wscript +++ b/ref/gl/wscript @@ -54,19 +54,10 @@ def configure(conf): conf.check_cc(lib='log') def build(bld): - libs = [ 'public', 'M' ] + libs = [ 'engine_includes', 'public', 'M' ] source = bld.path.ant_glob(['*.c']) - - includes = ['.', - '../filesystem', - '../engine', - '../engine/common', - '../engine/server', - '../engine/client', - '../public', - '../common', - '../pm_shared' ] + includes = '.' if bld.env.GL: bld.shlib( From dedc144b8b79833bcffa3b2e5896dab01634c88e Mon Sep 17 00:00:00 2001 From: Alibek Omarov Date: Sat, 10 Sep 2022 20:05:45 +0300 Subject: [PATCH 106/490] ref: soft: convert to include targets usage --- ref/soft/wscript | 21 +++------------------ 1 file changed, 3 insertions(+), 18 deletions(-) diff --git a/ref/soft/wscript b/ref/soft/wscript index 9504cd84..e5cf7ca2 100644 --- a/ref/soft/wscript +++ b/ref/soft/wscript @@ -25,26 +25,11 @@ def build(bld): if bld.env.DEDICATED: return - libs = [ 'public', 'M' ] - - source = bld.path.ant_glob(['*.c']) - - includes = ['.', - '../filesystem', - '../engine', - '../engine/common', - '../engine/server', - '../engine/client', - '../public', - '../common', - '../pm_shared' ] - - bld.shlib( - source = source, + bld.shlib(source = bld.path.ant_glob(['*.c']), target = 'ref_soft', features = 'c', - includes = includes, - use = libs, + includes = '.', + use = 'engine_includes public M', install_path = bld.env.LIBDIR, subsystem = bld.env.MSVC_SUBSYSTEM ) From 470121512aab7a7b935977d6ec786c98d98727dd Mon Sep 17 00:00:00 2001 From: Alibek Omarov Date: Sat, 10 Sep 2022 20:05:58 +0300 Subject: [PATCH 107/490] utils: mdldec: convert to include targets usage --- utils/mdldec/wscript | 9 ++------- 1 file changed, 2 insertions(+), 7 deletions(-) diff --git a/utils/mdldec/wscript b/utils/mdldec/wscript index 48619987..05c38ee2 100644 --- a/utils/mdldec/wscript +++ b/utils/mdldec/wscript @@ -16,16 +16,11 @@ def build(bld): if bld.env.DISABLE_UTILS_MDLDEC: return - source = bld.path.ant_glob('*.c') - includes = '. ../../common ../../engine ../../engine/common ../../engine/common/imagelib ../../public' - libs = [ 'public', 'M' ] - - bld( - source = source, + bld(source = bld.path.ant_glob('*.c'), target = 'mdldec', features = 'c cprogram', includes = includes, - use = libs, + use = 'engine_includes public M', install_path = bld.env.BINDIR, subsystem = bld.env.CONSOLE_SUBSYSTEM ) From 327eb330edcc4ed9bf3389bca01279bc165d0b59 Mon Sep 17 00:00:00 2001 From: Alibek Omarov Date: Sat, 10 Sep 2022 20:06:16 +0300 Subject: [PATCH 108/490] ref: gl: minimize dependency on internal engine headers --- ref/gl/gl_beams.c | 1 - ref/gl/gl_decals.c | 1 - ref/gl/gl_rpart.c | 1 - ref/gl/gl_sprite.c | 1 - ref/gl/gl_studio.c | 2 -- 5 files changed, 6 deletions(-) diff --git a/ref/gl/gl_beams.c b/ref/gl/gl_beams.c index 215eb890..4e436d17 100644 --- a/ref/gl/gl_beams.c +++ b/ref/gl/gl_beams.c @@ -19,7 +19,6 @@ GNU General Public License for more details. #include "entity_types.h" #include "triangleapi.h" #include "customentity.h" -#include "cl_tent.h" #include "pm_local.h" #include "studio.h" diff --git a/ref/gl/gl_decals.c b/ref/gl/gl_decals.c index c9e70983..2b3f82b8 100644 --- a/ref/gl/gl_decals.c +++ b/ref/gl/gl_decals.c @@ -14,7 +14,6 @@ GNU General Public License for more details. */ #include "gl_local.h" -#include "cl_tent.h" #define DECAL_OVERLAP_DISTANCE 2 #define DECAL_DISTANCE 4 // too big values produce more clipped polygons diff --git a/ref/gl/gl_rpart.c b/ref/gl/gl_rpart.c index fb4f6cac..00954af1 100644 --- a/ref/gl/gl_rpart.c +++ b/ref/gl/gl_rpart.c @@ -19,7 +19,6 @@ GNU General Public License for more details. #include "entity_types.h" #include "triangleapi.h" #include "pm_local.h" -#include "cl_tent.h" #include "studio.h" static float gTracerSize[11] = { 1.5f, 0.5f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f }; diff --git a/ref/gl/gl_sprite.c b/ref/gl/gl_sprite.c index ceffbb5a..006fe135 100644 --- a/ref/gl/gl_sprite.c +++ b/ref/gl/gl_sprite.c @@ -18,7 +18,6 @@ GNU General Public License for more details. #include "sprite.h" #include "studio.h" #include "entity_types.h" -#include "cl_tent.h" // it's a Valve default value for LoadMapSprite (probably must be power of two) #define MAPSPRITE_SIZE 128 diff --git a/ref/gl/gl_studio.c b/ref/gl/gl_studio.c index c71630d9..910dd07b 100644 --- a/ref/gl/gl_studio.c +++ b/ref/gl/gl_studio.c @@ -20,8 +20,6 @@ GNU General Public License for more details. #include "triangleapi.h" #include "studio.h" #include "pm_local.h" -#include "cl_tent.h" -//#include "client.h" #include "pmtrace.h" #define EVENT_CLIENT 5000 // less than this value it's a server-side studio events From fe9f3f78a0f43414d38dfcb34682385851279b6f Mon Sep 17 00:00:00 2001 From: Alibek Omarov Date: Sat, 10 Sep 2022 20:06:24 +0300 Subject: [PATCH 109/490] ref: soft: minimize dependency on internal engine headers --- ref/soft/r_beams.c | 3 +-- ref/soft/r_decals.c | 1 - ref/soft/r_glblit.c | 2 +- ref/soft/r_local.h | 1 + ref/soft/r_part.c | 1 - ref/soft/r_sprite.c | 1 - ref/soft/r_studio.c | 2 -- 7 files changed, 3 insertions(+), 8 deletions(-) diff --git a/ref/soft/r_beams.c b/ref/soft/r_beams.c index 0e69868d..5c8dd8f5 100644 --- a/ref/soft/r_beams.c +++ b/ref/soft/r_beams.c @@ -19,9 +19,8 @@ GNU General Public License for more details. #include "entity_types.h" #include "triangleapi.h" #include "customentity.h" -#include "cl_tent.h" #include "pm_local.h" - +#include "triangleapi.h" #include "studio.h" #define NOISE_DIVISIONS 64 // don't touch - many tripmines cause the crash when it equal 128 diff --git a/ref/soft/r_decals.c b/ref/soft/r_decals.c index 49fa6111..e3d4805a 100644 --- a/ref/soft/r_decals.c +++ b/ref/soft/r_decals.c @@ -14,7 +14,6 @@ GNU General Public License for more details. */ #include "r_local.h" -//#include "cl_tent.h" #define DECAL_OVERLAP_DISTANCE 2 #define DECAL_DISTANCE 4 // too big values produce more clipped polygons diff --git a/ref/soft/r_glblit.c b/ref/soft/r_glblit.c index 6f73b7f2..99d609b1 100644 --- a/ref/soft/r_glblit.c +++ b/ref/soft/r_glblit.c @@ -1,6 +1,6 @@ #include "r_local.h" #define APIENTRY_LINKAGE static -#include "../ref_gl/gl_export.h" +#include "../gl/gl_export.h" struct swblit_s { diff --git a/ref/soft/r_local.h b/ref/soft/r_local.h index 4546544a..3ee559cc 100644 --- a/ref/soft/r_local.h +++ b/ref/soft/r_local.h @@ -670,6 +670,7 @@ void TriEnd( void ); void TriTexCoord2f( float u, float v ); void TriVertex3fv( const float *v ); void TriVertex3f( float x, float y, float z ); +void TriColor4f( float r, float g, float b, float a ); void _TriColor4f( float r, float g, float b, float a ); void TriColor4ub( byte r, byte g, byte b, byte a ); void _TriColor4ub( byte r, byte g, byte b, byte a ); diff --git a/ref/soft/r_part.c b/ref/soft/r_part.c index c71cb7bf..790b843e 100644 --- a/ref/soft/r_part.c +++ b/ref/soft/r_part.c @@ -19,7 +19,6 @@ GNU General Public License for more details. #include "entity_types.h" #include "triangleapi.h" #include "pm_local.h" -#include "cl_tent.h" #include "studio.h" static float gTracerSize[11] = { 1.5f, 0.5f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f }; diff --git a/ref/soft/r_sprite.c b/ref/soft/r_sprite.c index adb063a3..5aa0f74d 100644 --- a/ref/soft/r_sprite.c +++ b/ref/soft/r_sprite.c @@ -18,7 +18,6 @@ GNU General Public License for more details. #include "sprite.h" #include "studio.h" #include "entity_types.h" -//#include "cl_tent.h" // it's a Valve default value for LoadMapSprite (probably must be power of two) #define MAPSPRITE_SIZE 128 diff --git a/ref/soft/r_studio.c b/ref/soft/r_studio.c index 0a68035a..aa413bae 100644 --- a/ref/soft/r_studio.c +++ b/ref/soft/r_studio.c @@ -20,8 +20,6 @@ GNU General Public License for more details. #include "triangleapi.h" #include "studio.h" #include "pm_local.h" -//#include "cl_tent.h" -//#include "client.h" #include "pmtrace.h" #define EVENT_CLIENT 5000 // less than this value it's a server-side studio events From 12a6088912a47ccbad2a73d962f4b3ce33d8db5e Mon Sep 17 00:00:00 2001 From: Alibek Omarov Date: Sat, 10 Sep 2022 20:11:05 +0300 Subject: [PATCH 110/490] ref: gl: move GLES wrappers libraries to 3rdparty folder --- .gitmodules | 6 +++--- {ref/gl => 3rdparty}/gl-wes-v2 | 0 {ref/gl => 3rdparty/gl4es}/gl4es | 0 {ref/gl => 3rdparty}/nanogl | 0 4 files changed, 3 insertions(+), 3 deletions(-) rename {ref/gl => 3rdparty}/gl-wes-v2 (100%) rename {ref/gl => 3rdparty/gl4es}/gl4es (100%) rename {ref/gl => 3rdparty}/nanogl (100%) diff --git a/.gitmodules b/.gitmodules index 9cb19541..06dea016 100644 --- a/.gitmodules +++ b/.gitmodules @@ -2,13 +2,13 @@ path = 3rdparty/mainui url = https://github.com/FWGS/mainui_cpp [submodule "ref_gl/nanogl"] - path = ref/gl/nanogl + path = 3rdparty/nanogl url = https://github.com/FWGS/nanogl [submodule "ref_gl/gl-wes-v2"] - path = ref/gl/gl-wes-v2 + path = 3rdparty/gl-wes-v2 url = https://github.com/FWGS/gl-wes-v2 [submodule "ref_gl/gl4es"] - path = ref/gl/gl4es + path = 3rdparty/gl4es/gl4es url = https://github.com/ptitSeb/gl4es [submodule "vgui_support"] path = 3rdparty/vgui_support diff --git a/ref/gl/gl-wes-v2 b/3rdparty/gl-wes-v2 similarity index 100% rename from ref/gl/gl-wes-v2 rename to 3rdparty/gl-wes-v2 diff --git a/ref/gl/gl4es b/3rdparty/gl4es/gl4es similarity index 100% rename from ref/gl/gl4es rename to 3rdparty/gl4es/gl4es diff --git a/ref/gl/nanogl b/3rdparty/nanogl similarity index 100% rename from ref/gl/nanogl rename to 3rdparty/nanogl From b1e2e84a111c822b3276ffdaa6cceb7fce5a8f21 Mon Sep 17 00:00:00 2001 From: Alibek Omarov Date: Sat, 10 Sep 2022 20:54:34 +0300 Subject: [PATCH 111/490] wscript: adapt main and ref_gl wscripts to new directory layout, add flag to build all renderers at once --- ref/gl/wscript | 107 ++++++++++++++----------------------------------- wscript | 36 ++++++++++++++--- 2 files changed, 62 insertions(+), 81 deletions(-) diff --git a/ref/gl/wscript b/ref/gl/wscript index 82964c98..1fc5e047 100644 --- a/ref/gl/wscript +++ b/ref/gl/wscript @@ -13,18 +13,6 @@ def options(opt): grp.add_option('--enable-static-gl', action='store_true', dest='GL_STATIC', default=False, help = 'enable direct linking to opengl [default: %default]') - grp.add_option('--enable-gles1', action='store_true', dest='NANOGL', default=False, - help = 'enable gles1 renderer [default: %default]') - - grp.add_option('--enable-gles2', action='store_true', dest='GLWES', default=False, - help = 'enable gles2 renderer [default: %default]') - - grp.add_option('--enable-gl4es', action='store_true', dest='GL4ES', default=False, - help = 'enable gles2 renderer [default: %default]') - - grp.add_option('--disable-gl', action='store_false', dest='GL', default=True, - help = 'disable opengl [default: %default]') - # stub return @@ -35,16 +23,6 @@ def configure(conf): conf.define_cond('SUPPORT_BSP2_FORMAT', conf.options.SUPPORT_BSP2_FORMAT) - conf.env.NANOGL = conf.options.NANOGL - conf.env.GLWES = conf.options.GLWES - conf.env.GL4ES = conf.options.GL4ES - conf.env.GL = conf.options.GL - - if conf.env.NANOGL: - conf.add_subproject('nanogl') - if conf.env.GLWES: - conf.add_subproject('gl-wes-v2') - conf.env.GL_STATIC = conf.options.GL_STATIC if conf.env.GL_STATIC: conf.check(lib='GL') @@ -59,61 +37,38 @@ def build(bld): source = bld.path.ant_glob(['*.c']) includes = '.' - if bld.env.GL: - bld.shlib( - source = source, - target = 'ref_gl', + targets = { + 'ref_gl': { + 'enable': bld.env.GL, + 'libs': ['GL'] if bld.env.GL_STATIC else [], + 'defines': ['XASH_GL_STATIC'] if bld.env.GL_STATIC else [], + }, + 'ref_gles1': { + 'enable': bld.env.NANOGL, + 'libs': ['DL', 'nanogl'], + 'defines': ['XASH_NANOGL'], + }, + 'ref_gles2': { + 'enable': bld.env.GLWES, + 'libs': ['DL', 'gl-wes-v2'], + 'defines': ['XASH_WES'], + }, + 'ref_gl4es': { + 'enable': bld.env.GL4ES, + 'libs': ['DL', 'gl4es', 'LOG'], + 'defines': ['XASH_GL_STATIC', 'XASH_GL4ES'], + }, + } + + for k,v in targets.items(): + if not v['enable']: + continue + + bld.shlib(source = source, + target = k, features = 'c', includes = includes, - use = libs + (['GL'] if bld.env.GL_STATIC else []), - defines = ['XASH_GL_STATIC'] if bld.env.GL_STATIC else [], - install_path = bld.env.LIBDIR, - subsystem = bld.env.MSVC_SUBSYSTEM - ) - - if bld.env.NANOGL: - bld.add_subproject('nanogl') - bld.shlib( - source = source, - target = 'ref_gles1', - features = 'c', - includes = includes, - use = libs + ['DL', 'nanogl'], - defines = ['XASH_NANOGL'], - install_path = bld.env.LIBDIR, - subsystem = bld.env.MSVC_SUBSYSTEM) - - if bld.env.GLWES: - bld.add_subproject('gl-wes-v2') - bld.shlib( - source = source, - target = 'ref_gles2', - features = 'c', - includes = includes, - use = libs + ['DL', 'gl-wes-v2'], - defines = ['XASH_WES'], - install_path = bld.env.LIBDIR, - subsystem = bld.env.MSVC_SUBSYSTEM) - - if bld.env.GL4ES: - gl4es_srcdir = bld.path.find_node('gl4es/src') - - bld.stlib( - source = gl4es_srcdir.ant_glob(['gl/*.c', 'gl/*/*.c', 'glx/hardext.c']), - target = 'gl4es', - features = 'c', - includes = ['gl4es/src', 'gl4es/src/gl', 'gl4es/src/glx', 'gl4es/include'], - defines = ['NOX11', 'NO_GBM', 'NO_INIT_CONSTRUCTOR', 'DEFAULT_ES=2', 'NOEGL', 'EXTERNAL_GETPROCADDRESS=GL4ES_GetProcAddress', 'NO_LOADER', 'STATICLIB'], - cflags = ['-w', '-fvisibility=hidden', '-std=gnu99'], - use = libs, - subsystem = bld.env.MSVC_SUBSYSTEM) - - bld.shlib( - source = source, - target = 'ref_gl4es', - features = 'c', - includes = includes, - use = libs + ['DL', 'gl4es', 'LOG'], - defines = ['XASH_GL4ES', 'XASH_GL_STATIC'], + use = libs + v['libs'], + defines = v['defines'], install_path = bld.env.LIBDIR, subsystem = bld.env.MSVC_SUBSYSTEM) diff --git a/wscript b/wscript index a6e97f63..dfda447a 100644 --- a/wscript +++ b/wscript @@ -39,8 +39,11 @@ SUBDIRS = [ Subproject('dllemu'), # disable only by engine feature, makes no sense to even parse subprojects in dedicated mode - Subproject('ref/gl', lambda x: not x.env.DEDICATED), - Subproject('ref/soft', lambda x: not x.env.DEDICATED), + Subproject('3rdparty/nanogl', lambda x: not x.env.DEDICATED and x.env.NANOGL), + Subproject('3rdparty/gl-wes-v2', lambda x: not x.env.DEDICATED and x.env.GLWES), + Subproject('3rdparty/gl4es', lambda x: not x.env.DEDICATED and x.env.GL4ES), + Subproject('ref/gl', lambda x: not x.env.DEDICATED and (x.env.GL or x.env.NANOGL or x.env.GLWES or x.env.GL4ES)), + Subproject('ref/soft', lambda x: not x.env.DEDICATED and x.env.SOFT), Subproject('3rdparty/mainui', lambda x: not x.env.DEDICATED), Subproject('3rdparty/vgui_support', lambda x: not x.env.DEDICATED), Subproject('stub/client', lambda x: not x.env.DEDICATED), @@ -54,9 +57,6 @@ SUBDIRS = [ Subproject('utils/run-fuzzer', lambda x: x.env.ENABLE_FUZZER), ] -def subdirs(): - return map(lambda x: x.name, SUBDIRS) - def options(opt): grp = opt.add_option_group('Common options') @@ -84,6 +84,26 @@ def options(opt): grp.add_option('--disable-werror', action = 'store_true', dest = 'DISABLE_WERROR', default = False, help = 'disable compilation abort on warning') + grp = opt.add_option_group('Renderers options') + + grp.add_option('--enable-all-renderers', action='store_true', dest='ALL_RENDERERS', default=False, + help = 'enable all renderers supported by Xash3D FWGS [default: %default]') + + grp.add_option('--enable-gles1', action='store_true', dest='NANOGL', default=False, + help = 'enable gles1 renderer [default: %default]') + + grp.add_option('--enable-gles2', action='store_true', dest='GLWES', default=False, + help = 'enable gles2 renderer [default: %default]') + + grp.add_option('--enable-gl4es', action='store_true', dest='GL4ES', default=False, + help = 'enable gles2 renderer [default: %default]') + + grp.add_option('--disable-gl', action='store_false', dest='GL', default=True, + help = 'disable opengl renderer [default: %default]') + + grp.add_option('--disable-soft', action='store_false', dest='SOFT', default=True, + help = 'disable opengl renderer [default: %default]') + grp = opt.add_option_group('Utilities options') grp.add_option('--enable-utils', action = 'store_true', dest = 'ENABLE_UTILS', default = False, @@ -241,6 +261,12 @@ def configure(conf): conf.env.DEDICATED = conf.options.DEDICATED conf.env.SINGLE_BINARY = conf.options.SINGLE_BINARY or conf.env.DEDICATED + conf.env.NANOGL = conf.options.NANOGL or conf.options.ALL_RENDERERS + conf.env.GLWES = conf.options.GLWES or conf.options.ALL_RENDERERS + conf.env.GL4ES = conf.options.GL4ES or conf.options.ALL_RENDERERS + conf.env.GL = conf.options.GL or conf.options.ALL_RENDERERS + conf.env.SOFT = conf.options.SOFT or conf.options.ALL_RENDERERS + if conf.env.DEST_OS != 'win32': conf.check_cc(lib='dl', mandatory=False) From 030a17fc58282c1f3180af51e85bc7a19e0667b6 Mon Sep 17 00:00:00 2001 From: Alibek Omarov Date: Sat, 10 Sep 2022 20:55:58 +0300 Subject: [PATCH 112/490] mainui: update submodule --- 3rdparty/mainui | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/3rdparty/mainui b/3rdparty/mainui index eb38ebb0..14c0922a 160000 --- a/3rdparty/mainui +++ b/3rdparty/mainui @@ -1 +1 @@ -Subproject commit eb38ebb023a42555ec3203cef079c686ece02394 +Subproject commit 14c0922af7a0dfcbb82f0979453a1fcca1d817f7 From 744c6b268052bcb94ac3597fb5c18ca1840f1e29 Mon Sep 17 00:00:00 2001 From: Alibek Omarov Date: Sat, 10 Sep 2022 20:56:06 +0300 Subject: [PATCH 113/490] vgui_support: update submodule --- 3rdparty/vgui_support | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/3rdparty/vgui_support b/3rdparty/vgui_support index 697de1ef..63c134f1 160000 --- a/3rdparty/vgui_support +++ b/3rdparty/vgui_support @@ -1 +1 @@ -Subproject commit 697de1efe2eea79d48118968d9e6a5a7e1117700 +Subproject commit 63c134f188e7c0891927f5a4149f4444b43b0be8 From 3853ff5435f0bedea024dbc40e72ff5868b978aa Mon Sep 17 00:00:00 2001 From: Alibek Omarov Date: Sat, 10 Sep 2022 20:56:51 +0300 Subject: [PATCH 114/490] 3rdparty: gl4es: add wscript to build --- 3rdparty/gl4es/wscript | 24 ++++++++++++++++++++++++ 1 file changed, 24 insertions(+) create mode 100644 3rdparty/gl4es/wscript diff --git a/3rdparty/gl4es/wscript b/3rdparty/gl4es/wscript new file mode 100644 index 00000000..cad76650 --- /dev/null +++ b/3rdparty/gl4es/wscript @@ -0,0 +1,24 @@ +#! /usr/bin/env python +# encoding: utf-8 + +import os + +def options(opt): + pass + +def configure(conf): + if not conf.path.find_dir('gl4es') or not conf.path.find_dir('gl4es/src'): + conf.fatal('Can\'t find gl4es submodule. Run `git submodule update --init --recursive`.') + return + +def build(bld): + gl4es_srcdir = bld.path.find_node('gl4es/src') + + bld.stlib(source = gl4es_srcdir.ant_glob(['gl/*.c', 'gl/*/*.c', 'glx/hardext.c']), + target = 'gl4es', + features = 'c', + includes = ['gl4es/src', 'gl4es/src/gl', 'gl4es/src/glx', 'gl4es/include'], + defines = ['NOX11', 'NO_GBM', 'NO_INIT_CONSTRUCTOR', 'DEFAULT_ES=2', 'NOEGL', 'EXTERNAL_GETPROCADDRESS=GL4ES_GetProcAddress', 'NO_LOADER', 'STATICLIB'], + cflags = ['-w', '-fvisibility=hidden', '-std=gnu99'], + subsystem = bld.env.MSVC_SUBSYSTEM, + export_includes = '.') From 401b145040cbbbacee2d34180e38f40159d11c42 Mon Sep 17 00:00:00 2001 From: Alibek Omarov Date: Sat, 10 Sep 2022 21:48:39 +0300 Subject: [PATCH 115/490] utils: mdldec: fix wscript to build --- utils/mdldec/wscript | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/utils/mdldec/wscript b/utils/mdldec/wscript index 05c38ee2..72b310d0 100644 --- a/utils/mdldec/wscript +++ b/utils/mdldec/wscript @@ -19,7 +19,7 @@ def build(bld): bld(source = bld.path.ant_glob('*.c'), target = 'mdldec', features = 'c cprogram', - includes = includes, + includes = '.', use = 'engine_includes public M', install_path = bld.env.BINDIR, subsystem = bld.env.CONSOLE_SUBSYSTEM From 7ed0499aa9d250ac233cd76f6cd5cd0be6e5dba7 Mon Sep 17 00:00:00 2001 From: Alibek Omarov Date: Sat, 10 Sep 2022 22:14:50 +0300 Subject: [PATCH 116/490] game_launch: wscript: fix Windows build --- game_launch/wscript | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/game_launch/wscript b/game_launch/wscript index d4ed3ae0..138d6911 100644 --- a/game_launch/wscript +++ b/game_launch/wscript @@ -22,13 +22,15 @@ def configure(conf): def build(bld): libs = ['sdk_includes'] + source = ['game.cpp'] + if bld.env.DEST_OS != 'win32': libs += [ 'DL' ] else: libs += ['USER32', 'SHELL32'] source += ['game.rc'] - bld(source = 'game.cpp', + bld(source = source, target = 'xash3d', # hl.exe features = 'c cxx cxxprogram', use = libs, From 234c843f60250cb37bc7988fd6c389718083c135 Mon Sep 17 00:00:00 2001 From: Alibek Omarov Date: Sat, 10 Sep 2022 22:55:07 +0300 Subject: [PATCH 117/490] filesystem: integrate inotify for file changes --- filesystem/filesystem.c | 3 + filesystem/filesystem.h | 7 ++ filesystem/filesystem_internal.h | 7 ++ filesystem/watch.c | 117 +++++++++++++++++++++++++++++++ 4 files changed, 134 insertions(+) create mode 100644 filesystem/watch.c diff --git a/filesystem/filesystem.c b/filesystem/filesystem.c index 5e9e3926..55826b1b 100644 --- a/filesystem/filesystem.c +++ b/filesystem/filesystem.c @@ -29,6 +29,9 @@ GNU General Public License for more details. #endif #include #include +#if XASH_LINUX +#include +#endif #include "port.h" #include "const.h" #include "crtlib.h" diff --git a/filesystem/filesystem.h b/filesystem/filesystem.h index 54eac76c..7f184f7c 100644 --- a/filesystem/filesystem.h +++ b/filesystem/filesystem.h @@ -120,6 +120,9 @@ typedef struct fs_globals_t int numgames; } fs_globals_t; +typedef void (*fs_event_callback_t)( const char *path ); + + typedef struct fs_api_t { qboolean (*InitStdio)( qboolean caseinsensitive, const char *rootdir, const char *basedir, const char *gamedir, const char *rodir ); @@ -174,6 +177,10 @@ typedef struct fs_api_t qboolean (*Delete)( const char *path ); qboolean (*SysFileExists)( const char *path, qboolean casesensitive ); const char *(*GetDiskPath)( const char *name, qboolean gamedironly ); + + // file watcher + void (*WatchFrame)( void ); // engine will read all events and call appropriate callbacks + qboolean (*AddWatch)( const char *path, fs_event_callback_t callback ); } fs_api_t; typedef struct fs_interface_t diff --git a/filesystem/filesystem_internal.h b/filesystem/filesystem_internal.h index 5f11f52f..99250189 100644 --- a/filesystem/filesystem_internal.h +++ b/filesystem/filesystem_internal.h @@ -189,6 +189,13 @@ void FS_SearchWAD( stringlist_t *list, wfile_t *wad, const char *pattern ); byte *FS_LoadWADFile( const char *path, fs_offset_t *sizeptr, qboolean gamedironly ); qboolean FS_AddWad_Fullpath( const char *wadfile, qboolean *already_loaded, int flags ); +// +// watch.c +// +qboolean FS_WatchInitialize( void ); +int FS_AddWatch( const char *path, fs_event_callback_t callback ); +void FS_WatchFrame( void ); + // // zip.c // diff --git a/filesystem/watch.c b/filesystem/watch.c new file mode 100644 index 00000000..1d4cf7ea --- /dev/null +++ b/filesystem/watch.c @@ -0,0 +1,117 @@ +#if 0 +#include "build.h" +#if XASH_LINUX +#include +#include +#include +#endif +#include "filesystem_internal.h" +#include "common/com_strings.h" + +#define MAX_FS_WATCHES 256 + +struct +{ +#if XASH_LINUX + int fd; + int count; + struct + { + fs_event_callback_t callback; + int fd; + } watch[MAX_FS_WATCHES]; +#endif // XASH_LINUX +} fsnotify; + +#if XASH_LINUX +static qboolean FS_InotifyInit( void ) +{ + int fd; + + if(( fd = inotify_init1( IN_NONBLOCK )) < 0 ) + { + Con_Printf( S_ERROR "inotify_init1 failed: %s", strerror( errno )); + return false; + } + + fsnotify.fd = fd; + return true; +} + +static qboolean FS_InotifyWasInit( void ) +{ + return fsnotify.fd >= 0; +} +#endif + +/* +=============== +FS_AddWatch + +Adds on-disk path to filesystem watcher list +Every file modification will call back +=============== +*/ +int FS_AddWatch( const char *path, fs_event_callback_t callback ) +{ +#if XASH_LINUX + int fd; + const uint mask = IN_CREATE | IN_DELETE | IN_MODIFY; + + if( !FS_InotifyWasInit() && !FS_InotifyInit()) + return false; + + if(( fd = inotify_add_watch( fsnotify.fd, path, mask )) < 0 ) + { + Con_Printf( S_ERROR "inotify_add_watch failed: %s", strerror( errno )); + return false; + } + + fsnotify.watch[fsnotify.count].fd = fd; + fsnotify.watch[fsnotify.count].callback = callback; + + return true; +#else + return false; +#endif +} + +/* +=============== +FS_WatchFrame + +Polls any changes and runs call backs +=============== +*/ +void FS_WatchFrame( void ) +{ +#if XASH_LINUX + int i; + + for( i = 0; i < fsnotify.count; i++ ) + { + struct inotify_event events; + } + +#endif +} + +/* +=============== +FS_WatchInitialize + +initializes filesystem watcher subsystem +=============== +*/ +qboolean FS_WatchInitialize( void ) +{ +#if XASH_LINUX + fsnotify.fd = -1; // only call inotify init when requested + fsnotify.count = 0; + + return true; +#else + return false; +#endif +} +#endif // 0 From c29eb458eae7f5684a0b90784a7294c9c0cdd7be Mon Sep 17 00:00:00 2001 From: Alibek Omarov Date: Sun, 11 Sep 2022 01:38:17 +0300 Subject: [PATCH 118/490] 3rdparty: add xash-extras submodule --- .gitmodules | 3 +++ 3rdparty/extras/xash-extras | 1 + 2 files changed, 4 insertions(+) create mode 160000 3rdparty/extras/xash-extras diff --git a/.gitmodules b/.gitmodules index 06dea016..e2ac7c73 100644 --- a/.gitmodules +++ b/.gitmodules @@ -16,3 +16,6 @@ [submodule "opus"] path = 3rdparty/opus/opus url = https://github.com/xiph/opus +[submodule "3rdparty/xash-extras"] + path = 3rdparty/extras/xash-extras + url = https://github.com/FWGS/xash-extras diff --git a/3rdparty/extras/xash-extras b/3rdparty/extras/xash-extras new file mode 160000 index 00000000..e16b9826 --- /dev/null +++ b/3rdparty/extras/xash-extras @@ -0,0 +1 @@ +Subproject commit e16b9826c2a0960a537139737241f498f9c0e938 From 686a966ff7e532ec9b148aaedfc1d3b365c13d46 Mon Sep 17 00:00:00 2001 From: Alibek Omarov Date: Sun, 11 Sep 2022 01:39:29 +0300 Subject: [PATCH 119/490] wscript: now waf builds extras archive by itself, using Python standard library's zipfile --- scripts/waifulib/zip.py | 53 +++++++++++++++++++++++++++++++++++++++++ wscript | 1 + 2 files changed, 54 insertions(+) create mode 100644 scripts/waifulib/zip.py diff --git a/scripts/waifulib/zip.py b/scripts/waifulib/zip.py new file mode 100644 index 00000000..119f028b --- /dev/null +++ b/scripts/waifulib/zip.py @@ -0,0 +1,53 @@ +#! /usr/bin/env python +# encoding: utf-8 + +from waflib import TaskGen, Task, Logs +import zipfile + +class ziparchive(Task.Task): + color = 'YELLOW' + + def __str__(self): + tgt_str = ' '.join([a.path_from(a.ctx.launch_node()) for a in self.outputs]) + count = len(self.inputs) + return '%s: %d files -> %s' % (self.__class__.__name__, count, tgt_str) + + def keyword(self): + return 'Creating' + + def run(self): + outfile = self.outputs[0].path_from(self.outputs[0].ctx.launch_node()) + comp = zipfile.ZIP_STORED if self.compresslevel == 0 else zipfile.ZIP_DEFLATED + + with zipfile.ZipFile(outfile, mode='w', + compression=comp, compresslevel=self.compresslevel, + strict_timestamps=False) as zf: + + for src in self.inputs: + infile = src.path_from(src.ctx.launch_node()) + arcfile = src.path_from(self.relative_to) + + Logs.debug('%s: %s <- %s as %s', self.__class__.__name__, outfile, infile, arcfile) + zf.write(infile, arcfile) + +@TaskGen.feature('zip') +def create_zip_archive(self): + compresslevel = getattr(self, 'compresslevel', 6) # 6 is zip default + if compresslevel < 0 or compresslevel > 9: + self.fatal('Invalid compress level') + + files = getattr(self, 'files', None) + if not files: + self.fatal('No files to archive') + + relative_to = getattr(self, 'relative_to', None) + if not relative_to: + self.fatal('No relative directory supplied') + + self.path.get_bld().mkdir() + target = self.path.get_bld().make_node(self.name) + + tsk = self.create_task('ziparchive', files, target) + + setattr(tsk, 'compresslevel', compresslevel) + setattr(tsk, 'relative_to', relative_to) diff --git a/wscript b/wscript index dfda447a..b8916809 100644 --- a/wscript +++ b/wscript @@ -39,6 +39,7 @@ SUBDIRS = [ Subproject('dllemu'), # disable only by engine feature, makes no sense to even parse subprojects in dedicated mode + Subproject('3rdparty/extras', lambda x: not x.env.DEDICATED), Subproject('3rdparty/nanogl', lambda x: not x.env.DEDICATED and x.env.NANOGL), Subproject('3rdparty/gl-wes-v2', lambda x: not x.env.DEDICATED and x.env.GLWES), Subproject('3rdparty/gl4es', lambda x: not x.env.DEDICATED and x.env.GL4ES), From f571a41cf3e2643e4f8a7d4ab44e074ef13adb75 Mon Sep 17 00:00:00 2001 From: Alibek Omarov Date: Sun, 11 Sep 2022 02:12:44 +0300 Subject: [PATCH 120/490] wscript: allow to setup engine default gamedir during configure --- engine/common/launcher.c | 6 ++++-- game_launch/game.cpp | 6 ++++-- wscript | 6 ++++++ 3 files changed, 14 insertions(+), 4 deletions(-) diff --git a/engine/common/launcher.c b/engine/common/launcher.c index 9410024c..598c1c17 100644 --- a/engine/common/launcher.c +++ b/engine/common/launcher.c @@ -35,7 +35,9 @@ __declspec(dllexport) int AmdPowerXpressRequestHighPerformance = 1; #endif #define E_GAME "XASH3D_GAME" // default env dir to start from -#define GAME_PATH "valve" // default dir to start from +#ifndef XASH_GAMEDIR +#define XASH_GAMEDIR "valve" +#endif static char szGameDir[128]; // safe place to keep gamedir static int szArgc; @@ -56,7 +58,7 @@ _inline int Sys_Start( void ) const char *game = getenv( E_GAME ); if( !game ) - game = GAME_PATH; + game = XASH_GAMEDIR; Q_strncpy( szGameDir, game, sizeof( szGameDir )); #if XASH_EMSCRIPTEN diff --git a/game_launch/game.cpp b/game_launch/game.cpp index 98389f9f..704c9f16 100644 --- a/game_launch/game.cpp +++ b/game_launch/game.cpp @@ -45,7 +45,9 @@ __declspec(dllexport) int AmdPowerXpressRequestHighPerformance = 1; #endif #define E_GAME "XASH3D_GAME" // default env dir to start from -#define GAME_PATH "valve" // default dir to start from +#ifndef XASH_GAMEDIR +#define XASH_GAMEDIR "valve" +#endif typedef void (*pfnChangeGame)( const char *progname ); typedef int (*pfnInit)( int argc, char **argv, const char *progname, int bChangeGame, pfnChangeGame func ); @@ -152,7 +154,7 @@ _inline int Sys_Start( void ) const char *game = getenv( E_GAME ); if( !game ) - game = GAME_PATH; + game = XASH_GAMEDIR; strncpy( szGameDir, game, sizeof( szGameDir ) - 1 ); diff --git a/wscript b/wscript index b8916809..5210889c 100644 --- a/wscript +++ b/wscript @@ -64,6 +64,9 @@ def options(opt): grp.add_option('-d', '--dedicated', action = 'store_true', dest = 'DEDICATED', default = False, help = 'build Xash Dedicated Server [default: %default]') + grp.add_option('--gamedir', action = 'store', dest = 'GAMEDIR', default = 'valve', + help = 'engine default game directory [default: %default]') + grp.add_option('--single-binary', action = 'store_true', dest = 'SINGLE_BINARY', default = False, help = 'build single "xash" binary (always enabled for dedicated) [default: %default]') @@ -268,6 +271,9 @@ def configure(conf): conf.env.GL = conf.options.GL or conf.options.ALL_RENDERERS conf.env.SOFT = conf.options.SOFT or conf.options.ALL_RENDERERS + conf.env.GAMEDIR = conf.options.GAMEDIR + conf.define('XASH_GAMEDIR', conf.options.GAMEDIR) + if conf.env.DEST_OS != 'win32': conf.check_cc(lib='dl', mandatory=False) From 084089b919b2a4bfb196d56d0e395b1f4cb5fea3 Mon Sep 17 00:00:00 2001 From: Alibek Omarov Date: Sun, 11 Sep 2022 02:13:23 +0300 Subject: [PATCH 121/490] 3rdparty: extras: build ZIP archive and install to engine default gamefolder --- 3rdparty/extras/wscript | 29 +++++++++++++++++++++++++++++ scripts/waifulib/zip.py | 11 ++++++++++- 2 files changed, 39 insertions(+), 1 deletion(-) create mode 100644 3rdparty/extras/wscript diff --git a/3rdparty/extras/wscript b/3rdparty/extras/wscript new file mode 100644 index 00000000..9d249bac --- /dev/null +++ b/3rdparty/extras/wscript @@ -0,0 +1,29 @@ +#! /usr/bin/env python +# encoding: utf-8 + +import os + +def options(opt): + pass + +def configure(conf): + if not conf.path.find_dir('xash-extras'): + conf.fatal('Can\'t find xash-extras submodule.') + return + + conf.load('zip') + +def build(bld): + srcdir = bld.path.find_dir('xash-extras') + + if bld.env.DEST_OS in ['android']: + install_path = bld.env.PREFIX + else: + install_path = os.path.join(bld.env.SHAREDIR, bld.env.GAMEDIR) + + bld(features='zip', + name = 'extras.pk3', + files = srcdir.ant_glob('**/*'), + relative_to = srcdir, + compresslevel = 0, + install_path = install_path) diff --git a/scripts/waifulib/zip.py b/scripts/waifulib/zip.py index 119f028b..12f5e44a 100644 --- a/scripts/waifulib/zip.py +++ b/scripts/waifulib/zip.py @@ -1,7 +1,7 @@ #! /usr/bin/env python # encoding: utf-8 -from waflib import TaskGen, Task, Logs +from waflib import TaskGen, Task, Logs, Utils import zipfile class ziparchive(Task.Task): @@ -51,3 +51,12 @@ def create_zip_archive(self): setattr(tsk, 'compresslevel', compresslevel) setattr(tsk, 'relative_to', relative_to) + + try: + inst_to = self.install_path + self.install_task = self.add_install_files( + install_to=inst_to, install_from=target, + chmod=Utils.O644, task=tsk) + + except AttributeError: + pass From 807f3852bd7581b14366d2ff08d5f328b0a56ee6 Mon Sep 17 00:00:00 2001 From: Alibek Omarov Date: Sun, 11 Sep 2022 02:20:36 +0300 Subject: [PATCH 122/490] scripts: gha: remove building extras.pak, as it's already being built --- scripts/gha/build_linux.sh | 5 +---- scripts/gha/build_motomagx.sh | 3 --- scripts/gha/build_win32.sh | 3 --- 3 files changed, 1 insertion(+), 10 deletions(-) diff --git a/scripts/gha/build_linux.sh b/scripts/gha/build_linux.sh index 7a0ed5d2..d3f3ed33 100755 --- a/scripts/gha/build_linux.sh +++ b/scripts/gha/build_linux.sh @@ -66,9 +66,6 @@ build_appimage() ./waf install --destdir="$APPDIR" || die - # Generate extras.pak - python3 scripts/makepak.py xash-extras/ "$APPDIR/extras.pak" - cp SDL2_linux/lib/libSDL2-2.0.so.0 "$APPDIR/" if [ "$ARCH" = "i386" ]; then cp vgui_support/vgui-dev/lib/vgui.so "$APPDIR/" @@ -83,7 +80,7 @@ fi echo "Xash3D FWGS installed as AppImage." echo "Base directory is $XASH3D_BASEDIR. Set XASH3D_BASEDIR environment variable to override this" -export XASH3D_EXTRAS_PAK1="${APPDIR}"/extras.pak +export XASH3D_EXTRAS_PAK1="${APPDIR}"/valve/extras.pk3 ${DEBUGGER} "${APPDIR}"/xash3d "$@" exit $? EOF diff --git a/scripts/gha/build_motomagx.sh b/scripts/gha/build_motomagx.sh index c103bd42..fda2c356 100755 --- a/scripts/gha/build_motomagx.sh +++ b/scripts/gha/build_motomagx.sh @@ -23,14 +23,11 @@ export LD_LIBRARY_PATH=$LD_LIBRARY_PATH:$LIBDIR1:$LIBDIR2:$LIBDIR3 export HOME=$mypath export SDL_QT_INVERT_ROTATION=1 export SWAP_PATH=$HOME/xash.swap -export XASH3D_EXTRAS_PAK1=$HOME/extras.pak cd $mypath sleep 1 exec $mypath/xash -dev $@ EOF -python3 scripts/makepak.py xash-extras/ Xash/extras.pak - mkdir -p artifacts/ 7z a -t7z artifacts/xash3d-fwgs-magx.7z -m0=lzma2 -mx=9 -mfb=64 -md=32m -ms=on -r Xash/ diff --git a/scripts/gha/build_win32.sh b/scripts/gha/build_win32.sh index be8bc506..5154153d 100755 --- a/scripts/gha/build_win32.sh +++ b/scripts/gha/build_win32.sh @@ -24,9 +24,6 @@ else die fi -mkdir valve/ -python3 scripts/makepak.py xash-extras/ valve/extras.pak - mkdir -p artifacts/ 7z a -t7z artifacts/xash3d-fwgs-win32-$ARCH.7z -m0=lzma2 -mx=9 -mfb=64 -md=32m -ms=on \ *.dll *.exe *.pdb activities.txt \ From f141869ce3c42bb6b4f6c7317352fcb86470aca7 Mon Sep 17 00:00:00 2001 From: Alibek Omarov Date: Sun, 11 Sep 2022 02:22:26 +0300 Subject: [PATCH 123/490] wscript: disable building extras.pk3 for Android --- wscript | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/wscript b/wscript index 5210889c..38922b31 100644 --- a/wscript +++ b/wscript @@ -39,7 +39,7 @@ SUBDIRS = [ Subproject('dllemu'), # disable only by engine feature, makes no sense to even parse subprojects in dedicated mode - Subproject('3rdparty/extras', lambda x: not x.env.DEDICATED), + Subproject('3rdparty/extras', lambda x: not x.env.DEDICATED and x.env.DEST_OS != 'android'), Subproject('3rdparty/nanogl', lambda x: not x.env.DEDICATED and x.env.NANOGL), Subproject('3rdparty/gl-wes-v2', lambda x: not x.env.DEDICATED and x.env.GLWES), Subproject('3rdparty/gl4es', lambda x: not x.env.DEDICATED and x.env.GL4ES), From b56f6fa330807a79eb633f5c344408f91b870d90 Mon Sep 17 00:00:00 2001 From: Alibek Omarov Date: Sun, 11 Sep 2022 02:50:52 +0300 Subject: [PATCH 124/490] scripts: waifulib: fix zipfile init for older Python --- scripts/waifulib/zip.py | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/scripts/waifulib/zip.py b/scripts/waifulib/zip.py index 12f5e44a..1e4efe72 100644 --- a/scripts/waifulib/zip.py +++ b/scripts/waifulib/zip.py @@ -19,10 +19,7 @@ class ziparchive(Task.Task): outfile = self.outputs[0].path_from(self.outputs[0].ctx.launch_node()) comp = zipfile.ZIP_STORED if self.compresslevel == 0 else zipfile.ZIP_DEFLATED - with zipfile.ZipFile(outfile, mode='w', - compression=comp, compresslevel=self.compresslevel, - strict_timestamps=False) as zf: - + with zipfile.ZipFile(outfile, mode='w', compression=comp) as zf: for src in self.inputs: infile = src.path_from(src.ctx.launch_node()) arcfile = src.path_from(self.relative_to) From a520d245a669b2fe1213fd04b0f15977b4aab541 Mon Sep 17 00:00:00 2001 From: dettlaff <113319266+de2tla2f@users.noreply.github.com> Date: Mon, 12 Sep 2022 04:18:24 +0000 Subject: [PATCH 125/490] wscript: fix typo in --enable-bundled-deps configure option --- wscript | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/wscript b/wscript index 38922b31..c3219d58 100644 --- a/wscript +++ b/wscript @@ -76,7 +76,7 @@ def options(opt): grp.add_option('-P', '--enable-packaging', action = 'store_true', dest = 'PACKAGING', default = False, help = 'respect prefix option, useful for packaging for various operating systems [default: %default]') - grp.add_option('--enabled-bundled-deps', action = 'store_true', dest = 'BUILD_BUNDLED_DEPS', default = False, + grp.add_option('--enable-bundled-deps', action = 'store_true', dest = 'BUILD_BUNDLED_DEPS', default = False, help = 'prefer to build bundled dependencies (like opus) instead of relying on system provided') grp.add_option('--enable-bsp2', action = 'store_true', dest = 'SUPPORT_BSP2_FORMAT', default = False, From 5678d9a2533bfc92011dbaadc23e963cc0a50f85 Mon Sep 17 00:00:00 2001 From: Alibek Omarov Date: Mon, 12 Sep 2022 08:42:35 +0300 Subject: [PATCH 126/490] engine: server: support MAP_ANON synonym for MAP_ANONYMOUS, disable allocating string pool near server library for OSX --- engine/server/sv_game.c | 15 ++++++++++----- 1 file changed, 10 insertions(+), 5 deletions(-) diff --git a/engine/server/sv_game.c b/engine/server/sv_game.c index 3d789b79..61dd4fc8 100644 --- a/engine/server/sv_game.c +++ b/engine/server/sv_game.c @@ -3092,12 +3092,10 @@ void SV_SetStringArrayMode( qboolean dynamic ) #endif } -#ifdef XASH_64BIT -#if !XASH_WIN32 +#if XASH_64BIT && !XASH_WIN32 && !XASH_APPLE #define USE_MMAP #include #endif -#endif /* ================== @@ -3128,14 +3126,21 @@ void SV_AllocStringPool( void ) #ifdef USE_MMAP { + uint flags; size_t pagesize = sysconf( _SC_PAGESIZE ); int arrlen = (str64.maxstringarray * 2) & ~(pagesize - 1); void *base = svgame.dllFuncs.pfnGameInit; void *start = svgame.hInstance - arrlen; +#if defined(MAP_ANON) + flags = MAP_ANON | MAP_PRIVATE; +#elif defined(MAP_ANONYMOUS) + flags = MAP_ANONYMOUS | MAP_PRIVATE; +#endif + while( start - base > INT_MIN ) { - void *mapptr = mmap((void*)((unsigned long)start & ~(pagesize - 1)), arrlen, PROT_READ | PROT_WRITE, MAP_ANONYMOUS | MAP_PRIVATE, 0, 0 ); + void *mapptr = mmap((void*)((unsigned long)start & ~(pagesize - 1)), arrlen, PROT_READ | PROT_WRITE, flags, 0, 0 ); if( mapptr && mapptr != (void*)-1 && mapptr - base > INT_MIN && mapptr - base < INT_MAX ) { ptr = mapptr; @@ -3150,7 +3155,7 @@ void SV_AllocStringPool( void ) start = base; while( start - base < INT_MAX ) { - void *mapptr = mmap((void*)((unsigned long)start & ~(pagesize - 1)), arrlen, PROT_READ | PROT_WRITE, MAP_ANONYMOUS | MAP_PRIVATE, 0, 0 ); + void *mapptr = mmap((void*)((unsigned long)start & ~(pagesize - 1)), arrlen, PROT_READ | PROT_WRITE, flags, 0, 0 ); if( mapptr && mapptr != (void*)-1 && mapptr - base > INT_MIN && mapptr - base < INT_MAX ) { ptr = mapptr; From c59b244d04735b00d13434e48b10975318145fde Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=D0=92=D0=BB=D0=B0=D0=B4=D0=B8=D1=81=D0=BB=D0=B0=D0=B2=20?= =?UTF-8?q?=D0=A1=D1=83=D1=85=D0=BE=D0=B2?= <22411953+Vladislav4KZ@users.noreply.github.com> Date: Sat, 17 Sep 2022 11:10:36 +0000 Subject: [PATCH 127/490] scripts: gha: fix path to vgui_support submodule --- scripts/gha/build_linux.sh | 2 +- scripts/gha/build_win32.sh | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/scripts/gha/build_linux.sh b/scripts/gha/build_linux.sh index d3f3ed33..3ed86cae 100755 --- a/scripts/gha/build_linux.sh +++ b/scripts/gha/build_linux.sh @@ -68,7 +68,7 @@ build_appimage() cp SDL2_linux/lib/libSDL2-2.0.so.0 "$APPDIR/" if [ "$ARCH" = "i386" ]; then - cp vgui_support/vgui-dev/lib/vgui.so "$APPDIR/" + cp 3rdparty/vgui_support/vgui-dev/lib/vgui.so "$APPDIR/" fi cat > "$APPDIR"/AppRun << 'EOF' diff --git a/scripts/gha/build_win32.sh b/scripts/gha/build_win32.sh index 5154153d..2416ac2c 100755 --- a/scripts/gha/build_win32.sh +++ b/scripts/gha/build_win32.sh @@ -17,7 +17,7 @@ fi if [ "$ARCH" = "i386" ]; then cp SDL2_VC/lib/x86/SDL2.dll . # Install SDL2 - cp vgui_support/vgui-dev/lib/win32_vc6/vgui.dll . + cp 3rdparty/vgui_support/vgui-dev/lib/win32_vc6/vgui.dll . elif [ "$ARCH" = "amd64" ]; then cp SDL2_VC/lib/x64/SDL2.dll . else From 064540294665d186bfee68ec42acad8dd4810426 Mon Sep 17 00:00:00 2001 From: Alibek Omarov Date: Mon, 19 Sep 2022 06:02:41 +0300 Subject: [PATCH 128/490] engine: fix crash when reading GoldSrc hashpaks --- engine/common/hpak.c | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/engine/common/hpak.c b/engine/common/hpak.c index 076c3428..d9be8a68 100644 --- a/engine/common/hpak.c +++ b/engine/common/hpak.c @@ -647,7 +647,7 @@ qboolean HPAK_GetDataPointer( const char *filename, resource_t *pResource, byte for( p = gp_hpak_queue; p != NULL; p = p->next ) { - if( !Q_stricmp(p->name, filename ) && !memcmp( p->resource.rgucMD5_hash, pResource->rgucMD5_hash, 16 )) + if( !Q_stricmp( p->name, filename ) && !memcmp( p->resource.rgucMD5_hash, pResource->rgucMD5_hash, 16 )) { if( buffer ) { @@ -702,11 +702,13 @@ qboolean HPAK_GetDataPointer( const char *filename, resource_t *pResource, byte { entry = &directory.entries[i]; - if( !memcmp( entry->resource.rgucMD5_hash, pResource->rgucMD5_hash, 16 )) + if( entry->filepos > 0 && + entry->disksize > 0 && + !memcmp( entry->resource.rgucMD5_hash, pResource->rgucMD5_hash, 16 )) { FS_Seek( f, entry->filepos, SEEK_SET ); - if( buffer && entry->disksize > 0 ) + if( buffer ) { tmpbuf = Z_Malloc( entry->disksize ); FS_Read( f, tmpbuf, entry->disksize ); From 69b09540073a336a6d3e30ecf231f7cb78fd11d0 Mon Sep 17 00:00:00 2001 From: Alibek Omarov Date: Tue, 20 Sep 2022 18:06:19 +0300 Subject: [PATCH 129/490] engine: change RenderAPI's RenderGetParm return type to intptr_t to insure compatibility with 64-bit --- common/render_api.h | 2 +- engine/client/cl_render.c | 8 ++++---- engine/client/client.h | 2 +- engine/client/ref_common.c | 2 +- engine/ref_api.h | 2 +- 5 files changed, 8 insertions(+), 8 deletions(-) diff --git a/common/render_api.h b/common/render_api.h index f41d297f..25897a85 100644 --- a/common/render_api.h +++ b/common/render_api.h @@ -161,7 +161,7 @@ struct ref_viewpass_s; typedef struct render_api_s { // Get renderer info (doesn't changes engine state at all) - int (*RenderGetParm)( int parm, int arg ); // generic + intptr_t (*RenderGetParm)( int parm, int arg ); // generic void (*GetDetailScaleForTexture)( int texture, float *xScale, float *yScale ); void (*GetExtraParmsForTexture)( int texture, byte *red, byte *green, byte *blue, byte *alpha ); lightstyle_t* (*GetLightStyle)( int number ); diff --git a/engine/client/cl_render.c b/engine/client/cl_render.c index 9af5aec7..4351cde1 100644 --- a/engine/client/cl_render.c +++ b/engine/client/cl_render.c @@ -133,7 +133,7 @@ const char *CL_GenericHandle( int fileindex ) return cl.files_precache[fileindex]; } -int CL_RenderGetParm( const int parm, const int arg, const qboolean checkRef ) +intptr_t CL_RenderGetParm( const int parm, const int arg, const qboolean checkRef ) { switch( parm ) { @@ -161,9 +161,9 @@ int CL_RenderGetParm( const int parm, const int arg, const qboolean checkRef ) case PARM_WATER_ALPHA: return FBitSet( world.flags, FWORLD_WATERALPHA ); case PARM_DELUXEDATA: - return *(int *)&world.deluxedata; + return (intptr_t)world.deluxedata; case PARM_SHADOWDATA: - return *(int *)&world.shadowdata; + return (intptr_t)world.shadowdata; default: // indicates call from client.dll if( checkRef ) @@ -204,7 +204,7 @@ int CL_RenderGetParm( const int parm, const int arg, const qboolean checkRef ) return 0; } -static int pfnRenderGetParm( int parm, int arg ) +static intptr_t pfnRenderGetParm( int parm, int arg ) { return CL_RenderGetParm( parm, arg, true ); } diff --git a/engine/client/client.h b/engine/client/client.h index c726b1c9..684150d1 100644 --- a/engine/client/client.h +++ b/engine/client/client.h @@ -964,7 +964,7 @@ void CL_ClearAllRemaps( void ); // cl_render.c // qboolean R_InitRenderAPI( void ); -int CL_RenderGetParm( const int parm, const int arg, const qboolean checkRef ); +intptr_t CL_RenderGetParm( const int parm, const int arg, const qboolean checkRef ); lightstyle_t *CL_GetLightStyle( int number ); int R_FatPVS( const vec3_t org, float radius, byte *visbuffer, qboolean merge, qboolean fullvis ); const ref_overview_t *GL_GetOverviewParms( void ); diff --git a/engine/client/ref_common.c b/engine/client/ref_common.c index e71dc5ac..2687a509 100644 --- a/engine/client/ref_common.c +++ b/engine/client/ref_common.c @@ -60,7 +60,7 @@ void GL_RenderFrame( const ref_viewpass_t *rvp ) ref.dllFuncs.GL_RenderFrame( rvp ); } -static int pfnEngineGetParm( int parm, int arg ) +static intptr_t pfnEngineGetParm( int parm, int arg ) { return CL_RenderGetParm( parm, arg, false ); // prevent recursion } diff --git a/engine/ref_api.h b/engine/ref_api.h index 2b2e4ca1..c5215541 100644 --- a/engine/ref_api.h +++ b/engine/ref_api.h @@ -256,7 +256,7 @@ typedef enum typedef struct ref_api_s { - int (*EngineGetParm)( int parm, int arg ); // generic + intptr_t (*EngineGetParm)( int parm, int arg ); // generic // cvar handlers cvar_t *(*Cvar_Get)( const char *szName, const char *szValue, int flags, const char *description ); From 5bae2f06ad5e2399188cb1ead9ecd91b0b55a0b5 Mon Sep 17 00:00:00 2001 From: Alibek Omarov Date: Tue, 20 Sep 2022 21:29:19 +0300 Subject: [PATCH 130/490] engine: platform: sdl: do not lock audio device, for some reason it sometimes causes problems although it shouldn't --- engine/platform/sdl/s_sdl.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/engine/platform/sdl/s_sdl.c b/engine/platform/sdl/s_sdl.c index 4f212485..876926b3 100644 --- a/engine/platform/sdl/s_sdl.c +++ b/engine/platform/sdl/s_sdl.c @@ -165,7 +165,7 @@ Makes sure dma.buffer is valid */ void SNDDMA_BeginPainting( void ) { - SDL_LockAudioDevice( sdl_dev ); +// SDL_LockAudioDevice( sdl_dev ); } /* @@ -178,7 +178,7 @@ Also unlocks the dsound buffer */ void SNDDMA_Submit( void ) { - SDL_UnlockAudioDevice( sdl_dev ); +// SDL_UnlockAudioDevice( sdl_dev ); } /* From a52d901f25ed75eb06ea7637f8455fa22900fa81 Mon Sep 17 00:00:00 2001 From: Alibek Omarov Date: Tue, 20 Sep 2022 21:55:28 +0300 Subject: [PATCH 131/490] common: add STATIC_ASSERT macro We're trying to guess if we have C11 static_assert defined in assert.h otherwise use good ol' trick with negative array --- common/xash3d_types.h | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/common/xash3d_types.h b/common/xash3d_types.h index 33f82bb1..9b343e8c 100644 --- a/common/xash3d_types.h +++ b/common/xash3d_types.h @@ -10,6 +10,7 @@ #include // off_t #include STDINT_H +#include typedef unsigned char byte; typedef int sound_t; @@ -103,6 +104,11 @@ typedef uint64_t longtime_t; #define likely(x) (x) #endif +#if defined( static_assert ) // C11 static_assert +#define STATIC_ASSERT static_assert +#else +#define STATIC_ASSERT( x, y ) extern int _static_assert_##__LINE__[( x ) ? 1 : -1] +#endif #ifdef XASH_BIG_ENDIAN #define LittleLong(x) (((int)(((x)&255)<<24)) + ((int)((((x)>>8)&255)<<16)) + ((int)(((x)>>16)&255)<<8) + (((x) >> 24)&255)) From ffe43e1f22b5a8f19746d4ea4f1d284579d2073f Mon Sep 17 00:00:00 2001 From: Alibek Omarov Date: Tue, 20 Sep 2022 21:56:25 +0300 Subject: [PATCH 132/490] scripts: waifulib: fix error reporting in zip.py --- scripts/waifulib/zip.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/scripts/waifulib/zip.py b/scripts/waifulib/zip.py index 1e4efe72..6e903ba7 100644 --- a/scripts/waifulib/zip.py +++ b/scripts/waifulib/zip.py @@ -31,15 +31,15 @@ class ziparchive(Task.Task): def create_zip_archive(self): compresslevel = getattr(self, 'compresslevel', 6) # 6 is zip default if compresslevel < 0 or compresslevel > 9: - self.fatal('Invalid compress level') + self.bld.fatal('Invalid compress level') files = getattr(self, 'files', None) if not files: - self.fatal('No files to archive') + self.bld.fatal('No files to archive') relative_to = getattr(self, 'relative_to', None) if not relative_to: - self.fatal('No relative directory supplied') + self.bld.fatal('No relative directory supplied') self.path.get_bld().mkdir() target = self.path.get_bld().make_node(self.name) From 360dc4f7ed664e1c10beb799fe6070d6a5bc36c4 Mon Sep 17 00:00:00 2001 From: Alibek Omarov Date: Tue, 20 Sep 2022 21:57:13 +0300 Subject: [PATCH 133/490] engine: try to make hashpaks more compatible between 32-bit and 64-bit platforms --- engine/common/hpak.c | 23 +++++++++++++++++----- engine/common/hpak.h | 45 ++++++++++++++++++++++++++++++++++++-------- 2 files changed, 55 insertions(+), 13 deletions(-) diff --git a/engine/common/hpak.c b/engine/common/hpak.c index d9be8a68..9a179439 100644 --- a/engine/common/hpak.c +++ b/engine/common/hpak.c @@ -49,6 +49,18 @@ const char *HPAK_TypeFromIndex( int type ) return "?"; } +static inline void HPAK_ResourceToCompat( dresource_t *dest, resource_t *src ) +{ + memcpy( dest, src, sizeof( *dest )); + dest->pNext = dest->pPrev = 0xDEADBEEF; +} + +static inline void HPAK_ResourceFromCompat( resource_t *dest, dresource_t *src ) +{ + memcpy( dest, src, sizeof( *src )); + dest->pNext = dest->pPrev = (void*)0xDEADBEEF; +} + static void HPAK_AddToQueue( const char *name, resource_t *pResource, void *data, file_t *f ) { hash_pack_queue_t *p; @@ -89,6 +101,7 @@ void HPAK_CreatePak( const char *filename, resource_t *pResource, byte *pData, f byte md5[16]; file_t *fout; MD5Context_t ctx; + dresource_t dresource; if( !COM_CheckString( filename )) return; @@ -145,7 +158,7 @@ void HPAK_CreatePak( const char *filename, resource_t *pResource, byte *pData, f hash_pack_info.count = 1; hash_pack_info.entries = Z_Malloc( sizeof( hpak_lump_t )); - hash_pack_info.entries[0].resource = *pResource; + HPAK_ResourceToCompat( &hash_pack_info.entries[0].resource, pResource ); hash_pack_info.entries[0].filepos = FS_Tell( fout ); hash_pack_info.entries[0].disksize = pResource->nDownloadSize; @@ -181,7 +194,7 @@ static qboolean HPAK_FindResource( hpak_info_t *hpk, byte *hash, resource_t *pRe if( !memcmp( hpk->entries[i].resource.rgucMD5_hash, hash, 16 )) { if( pResource ) - *pResource = hpk->entries[i].resource; + HPAK_ResourceFromCompat( pResource, &hpk->entries[i].resource ); return true; } } @@ -330,7 +343,7 @@ void HPAK_AddLump( qboolean bUseQueue, const char *name, resource_t *pResource, memset( pCurrentEntry, 0, sizeof( hpak_lump_t )); FS_Seek( file_dst, hash_pack_header.infotableofs, SEEK_SET ); - pCurrentEntry->resource = *pResource; + HPAK_ResourceToCompat( &pCurrentEntry->resource, pResource ); pCurrentEntry->filepos = FS_Tell( file_dst ); pCurrentEntry->disksize = pResource->nDownloadSize; @@ -370,7 +383,7 @@ static qboolean HPAK_Validate( const char *filename, qboolean quiet ) int i, num_lumps; MD5Context_t MD5_Hash; string pakname; - resource_t *pRes; + dresource_t *pRes; byte md5[16]; if( quiet ) HPAK_FlushHostQueue(); @@ -621,7 +634,7 @@ static qboolean HPAK_ResourceForIndex( const char *filename, int index, resource directory.entries = Z_Malloc( sizeof( hpak_lump_t ) * directory.count ); FS_Read( f, directory.entries, sizeof( hpak_lump_t ) * directory.count ); - *pResource = directory.entries[index-1].resource; + HPAK_ResourceFromCompat( pResource, &directory.entries[index-1].resource ); Z_Free( directory.entries ); FS_Close( f ); diff --git a/engine/common/hpak.h b/engine/common/hpak.h index 4d5c8fde..5e71eec8 100644 --- a/engine/common/hpak.h +++ b/engine/common/hpak.h @@ -37,24 +37,53 @@ infotable dlumpinfo_t[dwadinfo_t->numlumps] #define IDHPAKHEADER (('K'<<24)+('A'<<16)+('P'<<8)+'H') // little-endian "HPAK" #define IDHPAK_VERSION 1 +// a1ba: because Valve for some reason writes resource_t to file +// I had to make it crossplatform version +#pragma pack( push, 8 ) +typedef struct dresource_s +{ + char szFileName[64]; /* 0 64 */ + /* --- cacheline 1 boundary (64 bytes) --- */ + resourcetype_t type; /* 64 4 */ + int nIndex; /* 68 4 */ + int nDownloadSize; /* 72 4 */ + unsigned char ucFlags; /* 76 1 */ + unsigned char rgucMD5_hash[16]; /* 77 16 */ + unsigned char playernum; /* 93 1 */ + unsigned char rguc_reserved[32]; /* 94 32 */ + + /* XXX 2 bytes hole, try to pack */ + + /* --- cacheline 2 boundary (128 bytes) --- */ + uint32_t pNext; /* 128 4 */ + uint32_t pPrev; /* 132 4 */ + + /* size: 136, cachelines: 3, members: 10 */ + /* sum members: 134, holes: 1, sum holes: 2 */ + /* last cacheline: 8 bytes */ +} dresource_t; +#pragma pack( pop ) + +STATIC_ASSERT( sizeof( dresource_t ) == 136, "invalid dresource_t size, HPAKs won't be compatible (no custom logo in multiplayer!)" ); + typedef struct { - int ident; // should be equal HPAK - int version; - int infotableofs; + int ident; // should be equal HPAK + int version; + int infotableofs; } hpak_header_t; typedef struct { - resource_t resource; - int filepos; - int disksize; + dresource_t resource; + int filepos; + int disksize; } hpak_lump_t; typedef struct { - int count; - hpak_lump_t *entries; // variable sized. + int count; + hpak_lump_t *entries; // variable sized. } hpak_info_t; #endif // HPAK_H From 4a009c1c2c90d0670ca9666694981f82a8734c74 Mon Sep 17 00:00:00 2001 From: Alibek Omarov Date: Mon, 26 Sep 2022 12:54:48 +0300 Subject: [PATCH 134/490] engine: client: touch: move copypasted code from export & writeconfig into separate function --- engine/client/in_touch.c | 185 +++++++++++++++++---------------------- 1 file changed, 78 insertions(+), 107 deletions(-) diff --git a/engine/client/in_touch.c b/engine/client/in_touch.c index 50e7b6a4..a41a7e28 100644 --- a/engine/client/in_touch.c +++ b/engine/client/in_touch.c @@ -214,6 +214,74 @@ static inline int Touch_ExportButtonToConfig( file_t *f, touch_button_t *button, return 0; } +/* +================= +Touch_DumpConfig + +Dump config to file +================= +*/ +qboolean Touch_DumpConfig( const char *name, const char *profilename ) +{ + file_t *f; + touch_button_t *button; + + f = FS_Open( name, "w", true ); + + if( !f ) + { + Con_Printf( S_ERROR "Couldn't write %s.\n", name ); + return false; + } + + FS_Printf( f, "//=======================================================================\n"); + FS_Printf( f, "//\tCopyright FWGS & XashXT group %s (c)\n", Q_timestamp( TIME_YEAR_ONLY )); + FS_Printf( f, "//\t\t\ttouchscreen config\n" ); + FS_Printf( f, "//=======================================================================\n" ); + FS_Printf( f, "\ntouch_config_file \"%s\"\n", profilename ); + FS_Printf( f, "\n// touch cvars\n" ); + FS_Printf( f, "\n// sensitivity settings\n" ); + FS_Printf( f, "touch_pitch \"%f\"\n", touch_pitch->value ); + FS_Printf( f, "touch_yaw \"%f\"\n", touch_yaw->value ); + FS_Printf( f, "touch_forwardzone \"%f\"\n", touch_forwardzone->value ); + FS_Printf( f, "touch_sidezone \"%f\"\n", touch_sidezone->value ); + FS_Printf( f, "touch_nonlinear_look \"%d\"\n", CVAR_TO_BOOL(touch_nonlinear_look)); + FS_Printf( f, "touch_pow_factor \"%f\"\n", touch_pow_factor->value ); + FS_Printf( f, "touch_pow_mult \"%f\"\n", touch_pow_mult->value ); + FS_Printf( f, "touch_exp_mult \"%f\"\n", touch_exp_mult->value ); + FS_Printf( f, "\n// grid settings\n" ); + FS_Printf( f, "touch_grid_count \"%d\"\n", (int)touch_grid_count->value ); + FS_Printf( f, "touch_grid_enable \"%d\"\n", CVAR_TO_BOOL(touch_grid_enable)); + FS_Printf( f, "\n// global overstroke (width, r, g, b, a)\n" ); + FS_Printf( f, "touch_set_stroke %d %d %d %d %d\n", touch.swidth, touch.scolor[0], touch.scolor[1], touch.scolor[2], touch.scolor[3] ); + FS_Printf( f, "\n// highlight when pressed\n" ); + FS_Printf( f, "touch_highlight_r \"%f\"\n", touch_highlight_r->value ); + FS_Printf( f, "touch_highlight_g \"%f\"\n", touch_highlight_g->value ); + FS_Printf( f, "touch_highlight_b \"%f\"\n", touch_highlight_b->value ); + FS_Printf( f, "touch_highlight_a \"%f\"\n", touch_highlight_a->value ); + FS_Printf( f, "\n// _joy and _dpad options\n" ); + FS_Printf( f, "touch_dpad_radius \"%f\"\n", touch_dpad_radius->value ); + FS_Printf( f, "touch_joy_radius \"%f\"\n", touch_joy_radius->value ); + FS_Printf( f, "\n// how much slowdown when Precise Look button pressed\n" ); + FS_Printf( f, "touch_precise_amount \"%f\"\n", touch_precise_amount->value ); + FS_Printf( f, "\n// enable/disable move indicator\n" ); + FS_Printf( f, "touch_move_indicator \"%f\"\n", touch_move_indicator->value ); + + FS_Printf( f, "\n// reset menu state when execing config\n" ); + FS_Printf( f, "touch_setclientonly 0\n" ); + FS_Printf( f, "\n// touch buttons\n" ); + FS_Printf( f, "touch_removeall\n" ); + + for( button = touch.list_user.first; button; button = button->next ) + { + Touch_ExportButtonToConfig( f, button, false ); + } + + FS_Close( f ); + + return true; +} + /* ================= Touch_WriteConfig @@ -237,61 +305,14 @@ void Touch_WriteConfig( void ) Q_snprintf( newconfigfile, sizeof( newconfigfile ), "%s.new", touch_config_file->string ); Q_snprintf( oldconfigfile, sizeof( oldconfigfile ), "%s.bak", touch_config_file->string ); - f = FS_Open( newconfigfile, "w", true ); - if( f ) + if( Touch_DumpConfig( newconfigfile, touch_config_file->string )) { - touch_button_t *button; - FS_Printf( f, "//=======================================================================\n"); - FS_Printf( f, "//\tCopyright FWGS & XashXT group %s (c)\n", Q_timestamp( TIME_YEAR_ONLY )); - FS_Printf( f, "//\t\t\ttouchscreen config\n" ); - FS_Printf( f, "//=======================================================================\n" ); - FS_Printf( f, "\ntouch_config_file \"%s\"\n", touch_config_file->string ); - FS_Printf( f, "\n// touch cvars\n" ); - FS_Printf( f, "\n// sensitivity settings\n" ); - FS_Printf( f, "touch_pitch \"%f\"\n", touch_pitch->value ); - FS_Printf( f, "touch_yaw \"%f\"\n", touch_yaw->value ); - FS_Printf( f, "touch_forwardzone \"%f\"\n", touch_forwardzone->value ); - FS_Printf( f, "touch_sidezone \"%f\"\n", touch_sidezone->value ); - FS_Printf( f, "touch_nonlinear_look \"%d\"\n", CVAR_TO_BOOL(touch_nonlinear_look)); - FS_Printf( f, "touch_pow_factor \"%f\"\n", touch_pow_factor->value ); - FS_Printf( f, "touch_pow_mult \"%f\"\n", touch_pow_mult->value ); - FS_Printf( f, "touch_exp_mult \"%f\"\n", touch_exp_mult->value ); - FS_Printf( f, "\n// grid settings\n" ); - FS_Printf( f, "touch_grid_count \"%d\"\n", (int)touch_grid_count->value ); - FS_Printf( f, "touch_grid_enable \"%d\"\n", CVAR_TO_BOOL(touch_grid_enable)); - FS_Printf( f, "\n// global overstroke (width, r, g, b, a)\n" ); - FS_Printf( f, "touch_set_stroke %d %d %d %d %d\n", touch.swidth, touch.scolor[0], touch.scolor[1], touch.scolor[2], touch.scolor[3] ); - FS_Printf( f, "\n// highlight when pressed\n" ); - FS_Printf( f, "touch_highlight_r \"%f\"\n", touch_highlight_r->value ); - FS_Printf( f, "touch_highlight_g \"%f\"\n", touch_highlight_g->value ); - FS_Printf( f, "touch_highlight_b \"%f\"\n", touch_highlight_b->value ); - FS_Printf( f, "touch_highlight_a \"%f\"\n", touch_highlight_a->value ); - FS_Printf( f, "\n// _joy and _dpad options\n" ); - FS_Printf( f, "touch_dpad_radius \"%f\"\n", touch_dpad_radius->value ); - FS_Printf( f, "touch_joy_radius \"%f\"\n", touch_joy_radius->value ); - FS_Printf( f, "\n// how much slowdown when Precise Look button pressed\n" ); - FS_Printf( f, "touch_precise_amount \"%f\"\n", touch_precise_amount->value ); - FS_Printf( f, "\n// enable/disable move indicator\n" ); - FS_Printf( f, "touch_move_indicator \"%f\"\n", touch_move_indicator->value ); - - FS_Printf( f, "\n// reset menu state when execing config\n" ); - FS_Printf( f, "touch_setclientonly 0\n" ); - FS_Printf( f, "\n// touch buttons\n" ); - FS_Printf( f, "touch_removeall\n" ); - - for( button = touch.list_user.first; button; button = button->next ) - { - Touch_ExportButtonToConfig( f, button, false ); - } - - FS_Close( f ); - FS_Delete( oldconfigfile ); FS_Rename( touch_config_file->string, oldconfigfile ); + FS_Delete( touch_config_file->string ); FS_Rename( newconfigfile, touch_config_file->string ); } - else Con_Printf( S_ERROR "Couldn't write %s.\n", touch_config_file->string ); } /* @@ -303,8 +324,8 @@ export current touch configuration into profile */ static void Touch_ExportConfig_f( void ) { - file_t *f; const char *name; + string profilename, profilebase; if( Cmd_Argc() != 2 ) { @@ -316,65 +337,15 @@ static void Touch_ExportConfig_f( void ) name = Cmd_Argv( 1 ); - Con_Reportf( "Exporting config to %s\n", name ); - f = FS_Open( name, "w", true ); - if( f ) + if( Q_strstr( name, "touch_presets/" )) { - string profilename, profilebase; - touch_button_t *button; - - if( Q_strstr( name, "touch_presets/" ) ) - { - COM_FileBase( name, profilebase ); - Q_snprintf( profilename, sizeof( profilebase ), "touch_profiles/%s (copy).cfg", profilebase ); - } - else Q_strncpy( profilename, name, sizeof( profilename )); - FS_Printf( f, "//=======================================================================\n"); - FS_Printf( f, "//\tCopyright FWGS & XashXT group %s (c)\n", Q_timestamp( TIME_YEAR_ONLY )); - FS_Printf( f, "//\t\t\ttouchscreen preset\n" ); - FS_Printf( f, "//=======================================================================\n" ); - FS_Printf( f, "\ntouch_config_file \"%s\"\n", profilename ); - FS_Printf( f, "\n// touch cvars\n" ); - FS_Printf( f, "\n// sensitivity settings\n" ); - FS_Printf( f, "touch_pitch \"%f\"\n", touch_pitch->value ); - FS_Printf( f, "touch_yaw \"%f\"\n", touch_yaw->value ); - FS_Printf( f, "touch_forwardzone \"%f\"\n", touch_forwardzone->value ); - FS_Printf( f, "touch_sidezone \"%f\"\n", touch_sidezone->value ); - FS_Printf( f, "touch_nonlinear_look \"%d\"\n", CVAR_TO_BOOL(touch_nonlinear_look) ); - FS_Printf( f, "touch_pow_factor \"%f\"\n", touch_pow_factor->value ); - FS_Printf( f, "touch_pow_mult \"%f\"\n", touch_pow_mult->value ); - FS_Printf( f, "touch_exp_mult \"%f\"\n", touch_exp_mult->value ); - FS_Printf( f, "\n// grid settings\n" ); - FS_Printf( f, "touch_grid_count \"%d\"\n", (int)touch_grid_count->value ); - FS_Printf( f, "touch_grid_enable \"%d\"\n", CVAR_TO_BOOL(touch_grid_enable) ); - FS_Printf( f, "\n// global overstroke (width, r, g, b, a)\n" ); - FS_Printf( f, "touch_set_stroke %d %d %d %d %d\n", touch.swidth, touch.scolor[0], touch.scolor[1], touch.scolor[2], touch.scolor[3] ); - FS_Printf( f, "\n// highlight when pressed\n" ); - FS_Printf( f, "touch_highlight_r \"%f\"\n", touch_highlight_r->value ); - FS_Printf( f, "touch_highlight_g \"%f\"\n", touch_highlight_g->value ); - FS_Printf( f, "touch_highlight_b \"%f\"\n", touch_highlight_b->value ); - FS_Printf( f, "touch_highlight_a \"%f\"\n", touch_highlight_a->value ); - FS_Printf( f, "\n// _joy and _dpad options\n" ); - FS_Printf( f, "touch_dpad_radius \"%f\"\n", touch_dpad_radius->value ); - FS_Printf( f, "touch_joy_radius \"%f\"\n", touch_joy_radius->value ); - FS_Printf( f, "\n// how much slowdown when Precise Look button pressed\n" ); - FS_Printf( f, "touch_precise_amount \"%f\"\n", touch_precise_amount->value ); - FS_Printf( f, "\n// enable/disable move indicator\n" ); - FS_Printf( f, "touch_move_indicator \"%f\"\n", touch_move_indicator->value ); - - FS_Printf( f, "\n// reset menu state when execing config\n" ); - FS_Printf( f, "touch_setclientonly 0\n" ); - FS_Printf( f, "\n// touch buttons\n" ); - FS_Printf( f, "touch_removeall\n" ); - for( button = touch.list_user.first; button; button = button->next ) - { - Touch_ExportButtonToConfig( f, button, true ); - } - FS_Printf( f, "\n// round button coordinates to grid\n" ); - FS_Printf( f, "touch_roundall\n" ); - FS_Close( f ); + COM_FileBase( name, profilebase ); + Q_snprintf( profilename, sizeof( profilebase ), "touch_profiles/%s (copy).cfg", profilebase ); } - else Con_Printf( S_ERROR "Couldn't write %s.\n", name ); + else Q_strncpy( profilename, name, sizeof( profilename )); + + Con_Reportf( "Exporting config to \"%s\", profile name \"%s\"\n", name, profilename ); + Touch_DumpConfig( name, profilename ); } /* From 8cf822c426b947ce5e5cee45ea79daadc3852ed0 Mon Sep 17 00:00:00 2001 From: Alibek Omarov Date: Mon, 26 Sep 2022 13:56:53 +0300 Subject: [PATCH 135/490] mainui: update --- 3rdparty/mainui | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/3rdparty/mainui b/3rdparty/mainui index 14c0922a..39301a67 160000 --- a/3rdparty/mainui +++ b/3rdparty/mainui @@ -1 +1 @@ -Subproject commit 14c0922af7a0dfcbb82f0979453a1fcca1d817f7 +Subproject commit 39301a67d613d5d59cf7fcfab358b10544a294f4 From 8ea455def77f47d2a4c3855088df37dd648a4db3 Mon Sep 17 00:00:00 2001 From: Alibek Omarov Date: Tue, 27 Sep 2022 14:00:01 +0300 Subject: [PATCH 136/490] mainui: update --- 3rdparty/mainui | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/3rdparty/mainui b/3rdparty/mainui index 39301a67..a87210a8 160000 --- a/3rdparty/mainui +++ b/3rdparty/mainui @@ -1 +1 @@ -Subproject commit 39301a67d613d5d59cf7fcfab358b10544a294f4 +Subproject commit a87210a84288f2b344e3887f0befd3d0a06c140f From 0621e365ff6066dc0d8ee2469aa9dd0fa85bfa7e Mon Sep 17 00:00:00 2001 From: Alibek Omarov Date: Tue, 27 Sep 2022 17:19:32 +0300 Subject: [PATCH 137/490] mainui: update --- 3rdparty/mainui | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/3rdparty/mainui b/3rdparty/mainui index a87210a8..ebee8914 160000 --- a/3rdparty/mainui +++ b/3rdparty/mainui @@ -1 +1 @@ -Subproject commit a87210a84288f2b344e3887f0befd3d0a06c140f +Subproject commit ebee89143dd110a32f7d8ed457a5e44de199d1a3 From 0f2bb5720bc2a4fe20d4a3659dc011d34b64d5a0 Mon Sep 17 00:00:00 2001 From: Alibek Omarov Date: Sun, 2 Oct 2022 22:28:53 +0300 Subject: [PATCH 138/490] wscript: respect --libdir argument with packaging enabled. Fix a typo in --disable-soft description --- wscript | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/wscript b/wscript index c3219d58..ac122dbe 100644 --- a/wscript +++ b/wscript @@ -106,7 +106,7 @@ def options(opt): help = 'disable opengl renderer [default: %default]') grp.add_option('--disable-soft', action='store_false', dest='SOFT', default=True, - help = 'disable opengl renderer [default: %default]') + help = 'disable soft renderer [default: %default]') grp = opt.add_option_group('Utilities options') @@ -351,7 +351,7 @@ int main(int argc, char **argv) { strcasestr(argv[1], argv[2]); return 0; }''' # indicate if we are packaging for Linux/BSD if conf.options.PACKAGING: - conf.env.LIBDIR = conf.env.BINDIR = '${PREFIX}/lib/xash3d' + conf.env.LIBDIR = conf.env.BINDIR = conf.env.LIBDIR + '/xash3d' conf.env.SHAREDIR = '${PREFIX}/share/xash3d' else: if sys.platform != 'win32' and not conf.env.DEST_OS == 'android': From e0216e2658df6070399c764bb4579b05ef6f7fe4 Mon Sep 17 00:00:00 2001 From: Alibek Omarov Date: Tue, 4 Oct 2022 03:02:49 +0300 Subject: [PATCH 139/490] wscript: don't clean QtCreator files and reconfigure saved options Cleanup imports --- wscript | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/wscript b/wscript index ac122dbe..451ccdc7 100644 --- a/wscript +++ b/wscript @@ -2,8 +2,7 @@ # encoding: utf-8 # a1batross, mittorn, 2018 -from __future__ import print_function -from waflib import Logs, Context, Configure +from waflib import Build, Context, Logs import sys import os @@ -380,6 +379,11 @@ int main(void){ return !opus_custom_encoder_init(0, 0, 0); }''', fatal = False): conf.add_subproject(i.name) def build(bld): + # don't clean QtCreator files and reconfigure saved options + bld.clean_files = bld.bldnode.ant_glob('**', + excl='*.user configuration.py .lock* *conf_check_*/** config.log %s/*' % Build.CACHE_DIR, + quiet=True, generator=True) + bld.load('xshlib') for i in SUBDIRS: From f252161d90ffd7c5379f3bcf0f04da056dc60d86 Mon Sep 17 00:00:00 2001 From: Alibek Omarov Date: Tue, 4 Oct 2022 15:14:54 +0300 Subject: [PATCH 140/490] readme: fix typo and wording --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 119cf2b0..145c9fcc 100644 --- a/README.md +++ b/README.md @@ -49,7 +49,7 @@ NOTE: NEVER USE GitHub's ZIP ARCHIVES. GitHub doesn't include external dependenc ### Prerequisites -If your CPU is x86 compatible, we are building 32-bit code by default. This was dont for keeping compatibility with Steam releases of Half-Life and based on it's engine games. +If your CPU is x86 compatible, we are building 32-bit code by default. This was done to maintain compatibility with Steam releases of Half-Life and based on it's engine games. Even if Xash3D FWGS does support targetting 64-bit, you can't load games without recompiling them from source code! If your CPU is NOT x86 compatible or you decided build 64-bit version of engine, you may want to compile [Half-Life SDK](https://github.com/FWGS/hlsdk-xash3d). From 5a0fcfffb00fff01e079549ca6910dc58e7551e6 Mon Sep 17 00:00:00 2001 From: Alibek Omarov Date: Mon, 10 Oct 2022 13:09:01 +0300 Subject: [PATCH 141/490] mainui: update --- 3rdparty/mainui | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/3rdparty/mainui b/3rdparty/mainui index ebee8914..7ba2010e 160000 --- a/3rdparty/mainui +++ b/3rdparty/mainui @@ -1 +1 @@ -Subproject commit ebee89143dd110a32f7d8ed457a5e44de199d1a3 +Subproject commit 7ba2010e8dffa60ca1bc166db005c13c7b75aeb9 From 2d2523df4a5e748e40842a1e2763bdf8a4c96f6e Mon Sep 17 00:00:00 2001 From: Alibek Omarov Date: Wed, 12 Oct 2022 05:18:19 +0300 Subject: [PATCH 142/490] engine: client: touch: generalise touch emulation code * fix doubleclicks and wheels in VGUI --- engine/client/in_touch.c | 100 ++++++++++++++++++------------- engine/client/input.c | 66 +++++++++++++++----- engine/client/input.h | 5 +- engine/client/keys.c | 1 - engine/client/vgui/vgui_draw.c | 75 ++++++++++++++--------- engine/client/vgui/vgui_draw.h | 2 + engine/platform/linux/in_evdev.c | 21 +++---- engine/platform/sdl/events.c | 37 ++++-------- engine/platform/sdl/in_sdl.c | 7 ++- 9 files changed, 185 insertions(+), 129 deletions(-) diff --git a/engine/client/in_touch.c b/engine/client/in_touch.c index a41a7e28..81d58e9a 100644 --- a/engine/client/in_touch.c +++ b/engine/client/in_touch.c @@ -151,7 +151,6 @@ convar_t *touch_exp_mult; convar_t *touch_grid_enable; convar_t *touch_grid_count; convar_t *touch_config_file; -convar_t *touch_enable; convar_t *touch_in_menu; convar_t *touch_joy_radius; convar_t *touch_dpad_radius; @@ -162,7 +161,9 @@ convar_t *touch_highlight_b; convar_t *touch_highlight_a; convar_t *touch_precise_amount; convar_t *touch_joy_texture; -convar_t *touch_emulate; + +CVAR_DEFINE_AUTO( touch_enable, DEFAULT_TOUCH_ENABLE, FCVAR_ARCHIVE | FCVAR_FILTERABLE, "enable touch controls" ); +CVAR_DEFINE_AUTO( touch_emulate, "0", FCVAR_ARCHIVE | FCVAR_FILTERABLE, "emulate touch with mouse" ); // code looks smaller with it #define B(x) (button->x) @@ -1080,8 +1081,8 @@ void Touch_Init( void ) touch_joy_texture = Cvar_Get( "touch_joy_texture", "touch_default/joy", FCVAR_FILTERABLE, "texture for move indicator"); // input devices cvar - touch_enable = Cvar_Get( "touch_enable", DEFAULT_TOUCH_ENABLE, FCVAR_ARCHIVE | FCVAR_FILTERABLE, "enable touch controls" ); - touch_emulate = Cvar_Get( "touch_emulate", "0", FCVAR_ARCHIVE | FCVAR_FILTERABLE, "emulate touch with mouse" ); + Cvar_RegisterVariable( &touch_enable ); + Cvar_RegisterVariable( &touch_emulate ); /// TODO: touch sdl platform // SDL_SetHint( SDL_HINT_ANDROID_SEPARATE_MOUSE_AND_TOUCH, "1" ); @@ -1343,7 +1344,7 @@ void Touch_Draw( void ) { touch_button_t *button; - if( !touch.initialized || (!CVAR_TO_BOOL(touch_enable) && !touch.clientonly) ) + if( !touch.initialized || ( !touch_enable.value && !touch.clientonly )) return; Touch_InitConfig(); @@ -1937,9 +1938,8 @@ static int Touch_ControlsEvent( touchEventType type, int fingerID, float x, floa int IN_TouchEvent( touchEventType type, int fingerID, float x, float y, float dx, float dy ) { - // simulate menu mouse click - if( cls.key_dest != key_game && !CVAR_TO_BOOL(touch_in_menu) ) + if( cls.key_dest != key_game && !CVAR_TO_BOOL( touch_in_menu )) { touch.move_finger = touch.resize_finger = touch.look_finger = -1; // Hack for keyboard, hope it help @@ -1982,25 +1982,20 @@ int IN_TouchEvent( touchEventType type, int fingerID, float x, float y, float dx { VGui_MouseMove( TO_SCRN_X(x), TO_SCRN_Y(y) ); - if( type != event_motion ) - VGui_KeyEvent( K_MOUSE1, type == event_down ? 1 : 0 ); - - // allow scoreboard scroll - if( host.mouse_visible && type == event_motion ) - return 0; + switch( type ) + { + case event_down: + VGui_MouseEvent( K_MOUSE1, 1 ); + break; + case event_up: + VGui_MouseEvent( K_MOUSE1, 0 ); + break; + default: break; + } } - if( !touch.initialized || (!CVAR_TO_BOOL(touch_enable) && !touch.clientonly) ) - { -#if 0 - if( type == event_down ) - Key_Event( K_MOUSE1, true ); - if( type == event_up ) - Key_Event( K_MOUSE1, false ); - Android_AddMove( dx * (float)refState.width, dy * (float)refState.height ); -#endif + if( !touch.initialized || ( !touch_enable.value && !touch.clientonly )) return 0; - } if( clgame.dllFuncs.pfnTouchEvent && clgame.dllFuncs.pfnTouchEvent( type, fingerID, x, y, dx, dy ) ) return true; @@ -2019,35 +2014,60 @@ void Touch_GetMove( float *forward, float *side, float *yaw, float *pitch ) void Touch_KeyEvent( int key, int down ) { - int xi, yi; - float x, y; static float lx, ly; + static int kidNamedFinger = -1; + touchEventType event; + float x, y; + int finger, xi, yi; - if( !CVAR_TO_BOOL(touch_emulate) ) + if( !touch_emulate.value ) { - if( CVAR_TO_BOOL(touch_enable) ) + if( touch_enable.value ) return; if( !touch.clientonly ) return; } - Platform_GetMousePos( &xi, &yi ); - - x = xi/SCR_W; - y = yi/SCR_H; - - if( cls.key_dest == key_menu && down < 2 && key == K_MOUSE1 ) + if( !key ) { - UI_MouseMove( xi, yi ); - UI_KeyEvent( key, down ); + if( kidNamedFinger < 0 ) + return; + + finger = kidNamedFinger; + event = event_motion; + } + else + { + finger = key == K_MOUSE1 ? 0 : 1; + if( down ) + { + event = event_down; + kidNamedFinger = finger; + } + else + { + event = event_up; + kidNamedFinger = -1; + } } - if( down == 1 ) - Touch_ControlsEvent( event_down, key == K_MOUSE1?0:1, x, y, 0, 0 ); - else - Touch_ControlsEvent( down? event_motion: event_up, key == K_MOUSE1?0:1, x, y, x-lx, y-ly ); - lx = x, ly = y; + // don't deactivate mouse in game + // checking a case when mouse and touchscreen + // can be used simultaneously + Platform_SetCursorType( dc_arrow ); + Platform_GetMousePos( &xi, &yi ); + + x = xi / SCR_W; + y = yi / SCR_H; + + Con_DPrintf( "event %d %.2f %.2f %.2f %.2f\n", + event, x, y, x - lx, y - ly ); + + IN_TouchEvent( event, finger, x, y, x - lx, y - ly ); + + lx = x; + ly = y; } void Touch_Shutdown( void ) diff --git a/engine/client/input.c b/engine/client/input.c index 69c41ca8..43647e57 100644 --- a/engine/client/input.c +++ b/engine/client/input.c @@ -63,7 +63,7 @@ uint IN_CollectInputDevices( void ) if( !m_ignore->value ) // no way to check is mouse connected, so use cvar only ret |= INPUT_DEVICE_MOUSE; - if( CVAR_TO_BOOL(touch_enable) ) + if( touch_enable.value ) ret |= INPUT_DEVICE_TOUCH; if( Joy_IsActive() ) // connected or enabled @@ -94,13 +94,13 @@ void IN_LockInputDevices( qboolean lock ) { SetBits( m_ignore->flags, FCVAR_READ_ONLY ); SetBits( joy_enable->flags, FCVAR_READ_ONLY ); - SetBits( touch_enable->flags, FCVAR_READ_ONLY ); + SetBits( touch_enable.flags, FCVAR_READ_ONLY ); } else { ClearBits( m_ignore->flags, FCVAR_READ_ONLY ); ClearBits( joy_enable->flags, FCVAR_READ_ONLY ); - ClearBits( touch_enable->flags, FCVAR_READ_ONLY ); + ClearBits( touch_enable.flags, FCVAR_READ_ONLY ); } } @@ -308,25 +308,39 @@ IN_MouseMove */ void IN_MouseMove( void ) { - POINT current_pos; + int x, y; if( !in_mouseinitialized ) return; - // find mouse movement - Platform_GetMousePos( ¤t_pos.x, ¤t_pos.y ); + if( FBitSet( touch_emulate.flags, FCVAR_CHANGED )) + { + // FIXME: do not hide cursor if it was requested by GUI + if( !touch_emulate.value ) + Platform_SetCursorType( dc_none ); - VGui_MouseMove( current_pos.x, current_pos.y ); + ClearBits( touch_emulate.flags, FCVAR_CHANGED ); + } + + if( touch_emulate.value ) + { + // touch emulation overrides all input + Touch_KeyEvent( 0, 0 ); + return; + } + + // find mouse movement + Platform_GetMousePos( &x, &y ); + + VGui_MouseMove( x, y ); // HACKHACK: show cursor in UI, as mainui doesn't call // platform-dependent SetCursor anymore -#if XASH_SDL - if( UI_IsVisible() ) - SDL_ShowCursor( SDL_TRUE ); -#endif + if( UI_IsVisible( )) + Platform_SetCursorType( dc_arrow ); // if the menu is visible, move the menu cursor - UI_MouseMove( current_pos.x, current_pos.y ); + UI_MouseMove( x, y ); } /* @@ -336,8 +350,6 @@ IN_MouseEvent */ void IN_MouseEvent( int key, int down ) { - int i; - if( !in_mouseinitialized ) return; @@ -345,10 +357,15 @@ void IN_MouseEvent( int key, int down ) SetBits( in_mstate, BIT( key )); else ClearBits( in_mstate, BIT( key )); - if( cls.key_dest == key_game ) + // touch emulation overrides all input + if( touch_emulate.value ) + { + Touch_KeyEvent( K_MOUSE1 + key, down ); + } + else if( cls.key_dest == key_game ) { // perform button actions - VGui_KeyEvent( K_MOUSE1 + key, down ); + VGui_MouseEvent( K_MOUSE1 + key, down ); // don't do Key_Event here // client may override IN_MouseEvent @@ -363,6 +380,23 @@ void IN_MouseEvent( int key, int down ) } } +/* +============== +IN_MWheelEvent + +direction is negative for wheel down, otherwise wheel up +============== +*/ +void IN_MWheelEvent( int y ) +{ + int b = y > 0 ? K_MWHEELUP : K_MWHEELDOWN; + + VGui_MWheelEvent( y ); + + Key_Event( b, true ); + Key_Event( b, false ); +} + /* =========== IN_Shutdown diff --git a/engine/client/input.h b/engine/client/input.h index 32ad2b6b..25e2df49 100644 --- a/engine/client/input.h +++ b/engine/client/input.h @@ -34,6 +34,7 @@ void IN_Init( void ); void Host_InputFrame( void ); void IN_Shutdown( void ); void IN_MouseEvent( int key, int down ); +void IN_MWheelEvent( int direction ); void IN_ActivateMouse( void ); void IN_DeactivateMouse( void ); void IN_MouseSavePos( void ); @@ -57,8 +58,8 @@ typedef enum event_motion } touchEventType; -extern convar_t *touch_enable; -extern convar_t *touch_emulate; +extern convar_t touch_enable; +extern convar_t touch_emulate; void Touch_Draw( void ); void Touch_SetClientOnly( byte state ); diff --git a/engine/client/keys.c b/engine/client/keys.c index 682f44ad..a1e696c8 100644 --- a/engine/client/keys.c +++ b/engine/client/keys.c @@ -712,7 +712,6 @@ void GAME_EXPORT Key_Event( int key, int down ) } VGui_KeyEvent( key, down ); - Touch_KeyEvent( key, down ); // console key is hardcoded, so the user can never unbind it if( key == '`' || key == '~' ) diff --git a/engine/client/vgui/vgui_draw.c b/engine/client/vgui/vgui_draw.c index 56f456f1..67449772 100644 --- a/engine/client/vgui/vgui_draw.c +++ b/engine/client/vgui/vgui_draw.c @@ -402,9 +402,8 @@ enum VGUI_KeyCode VGUI_MapKey( int keyCode ) { VGUI_InitKeyTranslationTable(); - if( keyCode < 0 || keyCode >= (int)sizeof( s_pVirtualKeyTrans ) / (int)sizeof( s_pVirtualKeyTrans[0] )) + if( keyCode < 0 || keyCode >= ARRAYSIZE( s_pVirtualKeyTrans )) { - //Assert( false ); return (enum VGUI_KeyCode)-1; } else @@ -413,48 +412,66 @@ enum VGUI_KeyCode VGUI_MapKey( int keyCode ) } } -void VGui_KeyEvent( int key, int down ) +void VGui_MouseEvent( int key, int clicks ) { + enum VGUI_MouseAction mact; + enum VGUI_MouseCode code; + if( !vgui.initialized ) return; switch( key ) { - case K_MOUSE1: - if( down && host.mouse_visible ) { - Key_EnableTextInput(true, false); - } - vgui.Mouse( down ? MA_PRESSED : MA_RELEASED, MOUSE_LEFT ); - return; - case K_MOUSE2: - vgui.Mouse( down ? MA_PRESSED : MA_RELEASED, MOUSE_RIGHT ); - return; - case K_MOUSE3: - vgui.Mouse( down ? MA_PRESSED : MA_RELEASED, MOUSE_MIDDLE ); - return; - case K_MWHEELDOWN: - vgui.Mouse( MA_WHEEL, 1 ); - return; - case K_MWHEELUP: - vgui.Mouse( MA_WHEEL, -1 ); - return; - default: - break; + case K_MOUSE1: code = MOUSE_LEFT; break; + case K_MOUSE2: code = MOUSE_RIGHT; break; + case K_MOUSE3: code = MOUSE_MIDDLE; break; + default: return; } - if( down == 2 ) - vgui.Key( KA_TYPED, VGUI_MapKey( key ) ); + if( clicks >= 2 ) + mact = MA_DOUBLE; + else if( clicks == 1 ) + mact = MA_PRESSED; else - vgui.Key( down?KA_PRESSED:KA_RELEASED, VGUI_MapKey( key ) ); - //Msg("VGui_KeyEvent %d %d %d\n", key, VGUI_MapKey( key ), down ); + mact = MA_RELEASED; + + vgui.Mouse( mact, code ); +} + +void VGui_MWheelEvent( int y ) +{ + if( !vgui.initialized ) + return; + + vgui.Mouse( MA_WHEEL, y ); +} + +void VGui_KeyEvent( int key, int down ) +{ + enum VGUI_KeyCode code; + + if( !vgui.initialized ) + return; + + if(( code = VGUI_MapKey( key )) < 0 ) + return; + + if( down ) + { + vgui.Key( KA_PRESSED, code ); + vgui.Key( KA_TYPED, code ); + } + else vgui.Key( KA_RELEASED, code ); } void VGui_MouseMove( int x, int y ) { - float xscale = (float)refState.width / (float)clgame.scrInfo.iWidth; - float yscale = (float)refState.height / (float)clgame.scrInfo.iHeight; if( vgui.initialized ) + { + float xscale = (float)refState.width / (float)clgame.scrInfo.iWidth; + float yscale = (float)refState.height / (float)clgame.scrInfo.iHeight; vgui.MouseMove( x / xscale, y / yscale ); + } } void VGui_Paint( void ) diff --git a/engine/client/vgui/vgui_draw.h b/engine/client/vgui/vgui_draw.h index dd5a4ad2..4119774c 100644 --- a/engine/client/vgui/vgui_draw.h +++ b/engine/client/vgui/vgui_draw.h @@ -29,6 +29,8 @@ void VGui_Startup( const char *clientlib, int width, int height ); void VGui_Shutdown( void ); void VGui_Paint( void ); void VGui_RunFrame( void ); +void VGui_MouseEvent( int key, int clicks ); +void VGui_MWheelEvent( int y ); void VGui_KeyEvent( int key, int down ); void VGui_MouseMove( int x, int y ); qboolean VGui_IsActive( void ); diff --git a/engine/platform/linux/in_evdev.c b/engine/platform/linux/in_evdev.c index 638ceddb..46075f38 100644 --- a/engine/platform/linux/in_evdev.c +++ b/engine/platform/linux/in_evdev.c @@ -343,23 +343,16 @@ void IN_EvdevFrame ( void ) { switch ( ev.code ) { - case REL_X: dx += ev.value; + case REL_X: + dx += ev.value; break; - case REL_Y: dy += ev.value; + case REL_Y: + dy += ev.value; break; - case REL_WHEEL: - if( ev.value > 0) - { - Key_Event( K_MWHEELDOWN, 1 ); - Key_Event( K_MWHEELDOWN, 0 ); - } - else - { - Key_Event( K_MWHEELUP, 1 ); - Key_Event( K_MWHEELUP, 0 ); - } + case REL_WHEEL: + IN_MWheelEvent( ev.value ); break; } } @@ -367,7 +360,7 @@ void IN_EvdevFrame ( void ) { int key = KeycodeFromEvdev( ev.code, ev.value ); - if( CVAR_TO_BOOL(evdev_keydebug) ) + if( CVAR_TO_BOOL( evdev_keydebug )) Con_Printf( "key %d %d %d\n", ev.code, key, ev.value ); Key_Event( key , ev.value ); diff --git a/engine/platform/sdl/events.c b/engine/platform/sdl/events.c index 86d5cde0..2a1a217c 100644 --- a/engine/platform/sdl/events.c +++ b/engine/platform/sdl/events.c @@ -250,18 +250,6 @@ static void SDLash_KeyEvent( SDL_KeyboardEvent key ) Key_Event( keynum, down ); } -static void SDLash_MouseKey( int key, int down, int istouch ) -{ - if( CVAR_TO_BOOL( touch_emulate ) ) - { - Touch_KeyEvent( key, down ); - } - else if( in_mouseinitialized && !m_ignore->value && !istouch ) - { - Key_Event( key, down ); - } -} - /* ============= SDLash_MouseEvent @@ -270,14 +258,20 @@ SDLash_MouseEvent */ static void SDLash_MouseEvent( SDL_MouseButtonEvent button ) { - int down = button.state != SDL_RELEASED; - uint mstate = 0; + int down; #if SDL_VERSION_ATLEAST( 2, 0, 0 ) if( button.which == SDL_TOUCH_MOUSEID ) return; #endif + if( button.state == SDL_RELEASED ) + down = 0; + else if( button.clicks >= 2 ) + down = 2; // special state for double-click in UI + else + down = 1; + switch( button.button ) { case SDL_BUTTON_LEFT: @@ -297,10 +291,10 @@ static void SDLash_MouseEvent( SDL_MouseButtonEvent button ) break; #if ! SDL_VERSION_ATLEAST( 2, 0, 0 ) case SDL_BUTTON_WHEELUP: - Key_Event( K_MWHEELUP, down ); + IN_MWheelEvent( -1 ); break; case SDL_BUTTON_WHEELDOWN: - Key_Event( K_MWHEELDOWN, down ); + IN_MWheelEvent( 1 ); break; #endif // ! SDL_VERSION_ATLEAST( 2, 0, 0 ) default: @@ -435,9 +429,7 @@ static void SDLash_EventFilter( SDL_Event *event ) /* Mouse events */ case SDL_MOUSEMOTION: if( host.mouse_visible ) - { SDL_GetRelativeMouseState( NULL, NULL ); - } break; case SDL_MOUSEBUTTONUP: @@ -478,12 +470,8 @@ static void SDLash_EventFilter( SDL_Event *event ) break; #if SDL_VERSION_ATLEAST( 2, 0, 0 ) case SDL_MOUSEWHEEL: - { - int wheelbutton = event->wheel.y < 0 ? K_MWHEELDOWN : K_MWHEELUP; - Key_Event( wheelbutton, true ); - Key_Event( wheelbutton, false ); + IN_MWheelEvent( event->wheel.y ); break; - } /* Touch events */ case SDL_FINGERDOWN: @@ -556,7 +544,8 @@ static void SDLash_EventFilter( SDL_Event *event ) { // Swap axis to follow default axis binding: // LeftX, LeftY, RightX, RightY, TriggerRight, TriggerLeft - static int sdlControllerAxisToEngine[] = { + static int sdlControllerAxisToEngine[] = + { JOY_AXIS_SIDE, // SDL_CONTROLLER_AXIS_LEFTX, JOY_AXIS_FWD, // SDL_CONTROLLER_AXIS_LEFTY, JOY_AXIS_PITCH, // SDL_CONTROLLER_AXIS_RIGHTX, diff --git a/engine/platform/sdl/in_sdl.c b/engine/platform/sdl/in_sdl.c index 7f18c8ab..42757dca 100644 --- a/engine/platform/sdl/in_sdl.c +++ b/engine/platform/sdl/in_sdl.c @@ -333,11 +333,12 @@ void Platform_SetCursorType( VGUI_DefaultCursor type ) break; } - host.mouse_visible = visible; - - if( CVAR_TO_BOOL( touch_emulate )) + // never disable cursor in touch emulation mode + if( !visible && touch_emulate.value ) return; + host.mouse_visible = visible; + #if SDL_VERSION_ATLEAST( 2, 0, 0 ) if( host.mouse_visible ) { From d45e6e0ad17418915341e01f64ed7706ef06c75d Mon Sep 17 00:00:00 2001 From: Alibek Omarov Date: Thu, 13 Oct 2022 14:07:48 +0300 Subject: [PATCH 143/490] engine: platform: sdl: SetCursorType shouldn't know about current game state --- engine/platform/sdl/in_sdl.c | 3 --- 1 file changed, 3 deletions(-) diff --git a/engine/platform/sdl/in_sdl.c b/engine/platform/sdl/in_sdl.c index 42757dca..36b365b0 100644 --- a/engine/platform/sdl/in_sdl.c +++ b/engine/platform/sdl/in_sdl.c @@ -319,9 +319,6 @@ void Platform_SetCursorType( VGUI_DefaultCursor type ) return; #endif - if( cls.key_dest != key_game || cl.paused ) - return; - switch( type ) { case dc_user: From 1a09d297eed2a84eab35a0c5f5f2e2631f68c967 Mon Sep 17 00:00:00 2001 From: Alibek Omarov Date: Thu, 13 Oct 2022 14:49:48 +0300 Subject: [PATCH 144/490] engine: minimize SetCursorType calls count --- 3rdparty/mainui | 2 +- engine/client/cl_parse.c | 8 ------ engine/client/cl_qparse.c | 4 --- engine/client/in_touch.c | 7 +++++- engine/client/input.c | 46 +++++++++++++++------------------- engine/client/input.h | 1 + engine/client/vgui/vgui_draw.c | 11 +++++--- engine/common/common.h | 2 +- engine/common/host.c | 9 ------- engine/common/system.c | 6 ++++- engine/platform/sdl/events.c | 2 +- 11 files changed, 42 insertions(+), 56 deletions(-) diff --git a/3rdparty/mainui b/3rdparty/mainui index 7ba2010e..0f31c646 160000 --- a/3rdparty/mainui +++ b/3rdparty/mainui @@ -1 +1 @@ -Subproject commit 7ba2010e8dffa60ca1bc166db005c13c7b75aeb9 +Subproject commit 0f31c646d623d75b46a9b16f887ac29a167319a3 diff --git a/engine/client/cl_parse.c b/engine/client/cl_parse.c index de8f46f3..21c70337 100644 --- a/engine/client/cl_parse.c +++ b/engine/client/cl_parse.c @@ -914,11 +914,7 @@ void CL_ParseServerData( sizebuf_t *msg ) // set the background state if( cls.demoplayback && ( cls.demonum != -1 )) - { - // re-init mouse - host.mouse_visible = false; cl.background = true; - } else cl.background = background; if( cl.background ) // tell the game parts about background state @@ -2502,11 +2498,7 @@ void CL_ParseLegacyServerData( sizebuf_t *msg ) // set the background state if( cls.demoplayback && ( cls.demonum != -1 )) - { - // re-init mouse - host.mouse_visible = false; cl.background = true; - } else cl.background = background; if( cl.background ) // tell the game parts about background state diff --git a/engine/client/cl_qparse.c b/engine/client/cl_qparse.c index 76865362..be6752a0 100644 --- a/engine/client/cl_qparse.c +++ b/engine/client/cl_qparse.c @@ -237,10 +237,6 @@ static void CL_ParseQuakeServerInfo( sizebuf_t *msg ) } else Cvar_Reset( "r_decals" ); - // re-init mouse - if( cl.background ) - host.mouse_visible = false; - if( cl.background ) // tell the game parts about background state Cvar_FullSet( "cl_background", "1", FCVAR_READ_ONLY ); else Cvar_FullSet( "cl_background", "0", FCVAR_READ_ONLY ); diff --git a/engine/client/in_touch.c b/engine/client/in_touch.c index 81d58e9a..f8136e6d 100644 --- a/engine/client/in_touch.c +++ b/engine/client/in_touch.c @@ -469,8 +469,8 @@ static touch_button_t *Touch_FindFirst( touchbuttonlist_t *list, const char *nam void Touch_SetClientOnly( byte state ) { + // TODO: fix clash with vgui cursors touch.clientonly = state; - host.mouse_visible = state; touch.move_finger = touch.look_finger = -1; touch.forward = touch.side = 0; @@ -2070,6 +2070,11 @@ void Touch_KeyEvent( int key, int down ) ly = y; } +qboolean Touch_WantVisibleCursor( void ) +{ + return ( touch_enable.value && touch_emulate.value ) || touch.clientonly; +} + void Touch_Shutdown( void ) { if( !touch.initialized ) diff --git a/engine/client/input.c b/engine/client/input.c index 43647e57..68d6e2aa 100644 --- a/engine/client/input.c +++ b/engine/client/input.c @@ -47,8 +47,6 @@ convar_t *cl_backspeed; convar_t *look_filter; convar_t *m_rawinput; -static qboolean s_bRawInput, s_bMouseGrab; - /* ================ IN_CollectInputDevices @@ -173,19 +171,16 @@ Called when key_dest is changed */ void IN_ToggleClientMouse( int newstate, int oldstate ) { - if( newstate == oldstate ) return; + if( newstate == oldstate ) + return; - if( oldstate == key_game ) + // since SetCursorType controls cursor visibility + // execute it first, and then check mouse grab state + if(( newstate == key_menu || newstate == key_console || newstate == key_message ) && + ( !CL_IsBackgroundMap() || CL_IsBackgroundDemo( ))) { - IN_DeactivateMouse(); - } - else if( newstate == key_game ) - { - IN_ActivateMouse(); - } + Platform_SetCursorType( dc_arrow ); - if( ( newstate == key_menu || newstate == key_console || newstate == key_message ) && ( !CL_IsBackgroundMap() || CL_IsBackgroundDemo( ))) - { #if XASH_ANDROID Android_ShowMouse( true ); #endif @@ -195,6 +190,8 @@ void IN_ToggleClientMouse( int newstate, int oldstate ) } else { + Platform_SetCursorType( dc_none ); + #if XASH_ANDROID Android_ShowMouse( false ); #endif @@ -202,10 +199,21 @@ void IN_ToggleClientMouse( int newstate, int oldstate ) Evdev_SetGrab( true ); #endif } + + if( oldstate == key_game ) + { + IN_DeactivateMouse(); + } + else if( newstate == key_game ) + { + IN_ActivateMouse(); + } } void IN_CheckMouseState( qboolean active ) { + static qboolean s_bRawInput, s_bMouseGrab; + #if XASH_WIN32 qboolean useRawInput = CVAR_TO_BOOL( m_rawinput ) && clgame.client_dll_uses_sdl || clgame.dllFuncs.pfnLookEvent; #else @@ -313,15 +321,6 @@ void IN_MouseMove( void ) if( !in_mouseinitialized ) return; - if( FBitSet( touch_emulate.flags, FCVAR_CHANGED )) - { - // FIXME: do not hide cursor if it was requested by GUI - if( !touch_emulate.value ) - Platform_SetCursorType( dc_none ); - - ClearBits( touch_emulate.flags, FCVAR_CHANGED ); - } - if( touch_emulate.value ) { // touch emulation overrides all input @@ -334,11 +333,6 @@ void IN_MouseMove( void ) VGui_MouseMove( x, y ); - // HACKHACK: show cursor in UI, as mainui doesn't call - // platform-dependent SetCursor anymore - if( UI_IsVisible( )) - Platform_SetCursorType( dc_arrow ); - // if the menu is visible, move the menu cursor UI_MouseMove( x, y ); } diff --git a/engine/client/input.h b/engine/client/input.h index 25e2df49..6b992490 100644 --- a/engine/client/input.h +++ b/engine/client/input.h @@ -74,6 +74,7 @@ void Touch_GetMove( float * forward, float *side, float *yaw, float *pitch ); void Touch_ResetDefaultButtons( void ); int IN_TouchEvent( touchEventType type, int fingerID, float x, float y, float dx, float dy ); void Touch_KeyEvent( int key, int down ); +qboolean Touch_WantVisibleCursor( void ); // // in_joy.c diff --git a/engine/client/vgui/vgui_draw.c b/engine/client/vgui/vgui_draw.c index 67449772..cd134e24 100644 --- a/engine/client/vgui/vgui_draw.c +++ b/engine/client/vgui/vgui_draw.c @@ -24,7 +24,7 @@ GNU General Public License for more details. #include "platform/platform.h" static enum VGUI_KeyCode s_pVirtualKeyTrans[256]; -static VGUI_DefaultCursor s_currentCursor; +static VGUI_DefaultCursor s_currentCursor = -1; static HINSTANCE s_pVGuiSupport; // vgui_support library static convar_t *vgui_utf8 = NULL; @@ -50,8 +50,12 @@ void GAME_EXPORT VGUI_GetMousePos( int *_x, int *_y ) void GAME_EXPORT VGUI_CursorSelect( VGUI_DefaultCursor cursor ) { - Platform_SetCursorType( cursor ); - s_currentCursor = cursor; + if( s_currentCursor != cursor ) + { + Platform_SetCursorType( cursor ); + + s_currentCursor = cursor; + } } byte GAME_EXPORT VGUI_GetColor( int i, int j) @@ -250,7 +254,6 @@ void VGui_Startup( const char *clientlib, int width, int height ) if( vgui.initialized ) { - //host.mouse_visible = true; vgui.Startup( width, height ); } else if ( COM_CheckString( clientlib ) ) diff --git a/engine/common/common.h b/engine/common/common.h index 3099e436..b08432e5 100644 --- a/engine/common/common.h +++ b/engine/common/common.h @@ -358,7 +358,7 @@ typedef struct host_parm_s qboolean allow_cheats; // this host will allow cheating qboolean con_showalways; // show console always (developer and dedicated) qboolean change_game; // initialize when game is changed - qboolean mouse_visible; // vgui override cursor control + qboolean mouse_visible; // vgui override cursor control (never change outside Platform_SetCursorType!) qboolean shutdown_issued; // engine is shutting down qboolean force_draw_version; // used when fraps is loaded float force_draw_version_time; diff --git a/engine/common/host.c b/engine/common/host.c index 97fdb923..9dc606e3 100644 --- a/engine/common/host.c +++ b/engine/common/host.c @@ -703,15 +703,6 @@ void GAME_EXPORT Host_Error( const char *error, ... ) static qboolean recursive = false; va_list argptr; - if( host.mouse_visible && !CL_IsInMenu( )) - { - // hide VGUI mouse -#ifdef XASH_SDL - SDL_ShowCursor( 0 ); -#endif - host.mouse_visible = false; - } - va_start( argptr, error ); Q_vsprintf( hosterror1, error, argptr ); va_end( argptr ); diff --git a/engine/common/system.c b/engine/common/system.c index 18ba734f..ed7493a4 100644 --- a/engine/common/system.c +++ b/engine/common/system.c @@ -400,12 +400,16 @@ void Sys_Error( const char *error, ... ) va_list argptr; char text[MAX_PRINT_MSG]; + // enable cursor before debugger call + if( !Host_IsDedicated( )) + Platform_SetCursorType( dc_arrow ); + DEBUG_BREAK; if( host.status == HOST_ERR_FATAL ) return; // don't multiple executes - // make sure what console received last message + // make sure that console received last message if( host.change_game ) Sys_Sleep( 200 ); error_on_exit = true; diff --git a/engine/platform/sdl/events.c b/engine/platform/sdl/events.c index 2a1a217c..515739dd 100644 --- a/engine/platform/sdl/events.c +++ b/engine/platform/sdl/events.c @@ -668,7 +668,7 @@ TODO: kill mouse in win32 clients too */ void Platform_PreCreateMove( void ) { - if( CVAR_TO_BOOL( m_ignore ) ) + if( CVAR_TO_BOOL( m_ignore )) { SDL_GetRelativeMouseState( NULL, NULL ); SDL_ShowCursor( SDL_TRUE ); From 305b2579ebf36c91770925c3fc510294899da931 Mon Sep 17 00:00:00 2001 From: Alibek Omarov Date: Wed, 19 Oct 2022 01:08:39 +0300 Subject: [PATCH 145/490] engine: client: voice: fix crackling voice file input on low FPS --- engine/client/voice.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/engine/client/voice.c b/engine/client/voice.c index 7d5ef8f0..98eb5266 100644 --- a/engine/client/voice.c +++ b/engine/client/voice.c @@ -159,9 +159,11 @@ static uint Voice_GetOpusCompressedData( byte *out, uint maxsize, uint *frames ) if( voice.input_file ) { uint numbytes; - double updateInterval; + double updateInterval, curtime = Sys_DoubleTime(); + + updateInterval = curtime - voice.start_time; + voice.start_time = curtime; - updateInterval = cl.mtime[0] - cl.mtime[1]; numbytes = updateInterval * voice.samplerate * voice.width * VOICE_PCM_CHANNELS; numbytes = Q_min( numbytes, voice.input_file->size - voice.input_file_pos ); numbytes = Q_min( numbytes, sizeof( voice.input_buffer ) - voice.input_buffer_pos ); From ee8098839e8a2a79485408703ba0ba22e8d797e7 Mon Sep 17 00:00:00 2001 From: SNMetamorph <25657591+SNMetamorph@users.noreply.github.com> Date: Wed, 26 Oct 2022 20:13:29 +0400 Subject: [PATCH 146/490] engine: common: pm_trace: fixed non portable code in PM_ConvertTrace --- engine/common/pm_trace.c | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/engine/common/pm_trace.c b/engine/common/pm_trace.c index 0e1ce7c6..968cb23e 100644 --- a/engine/common/pm_trace.c +++ b/engine/common/pm_trace.c @@ -111,9 +111,17 @@ hull_t *PM_HullForBox( const vec3_t mins, const vec3_t maxs ) void PM_ConvertTrace( trace_t *out, pmtrace_t *in, edict_t *ent ) { - memcpy( out, in, 48 ); // matched + out->allsolid = in->allsolid; + out->startsolid = in->startsolid; + out->inopen = in->inopen; + out->inwater = in->inwater; + out->fraction = in->fraction; + out->plane.dist = in->plane.dist; out->hitgroup = in->hitgroup; out->ent = ent; + + VectorCopy( in->endpos, out->endpos ); + VectorCopy( in->plane.normal, out->plane.normal ); } /* From 44a43c2c09434101a16aa33de12eef94acc92540 Mon Sep 17 00:00:00 2001 From: SNMetamorph <25657591+SNMetamorph@users.noreply.github.com> Date: Wed, 26 Oct 2022 20:19:49 +0400 Subject: [PATCH 147/490] engine: client: cl_parse: minor code fix in CL_ParseLegacyServerData --- engine/client/cl_parse.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/engine/client/cl_parse.c b/engine/client/cl_parse.c index 21c70337..b45f575a 100644 --- a/engine/client/cl_parse.c +++ b/engine/client/cl_parse.c @@ -2456,8 +2456,8 @@ void CL_ParseLegacyServerData( sizebuf_t *msg ) i = MSG_ReadLong( msg ); //cls.serverProtocol = i; - if( i != 48 ) - Host_Error( "Server uses invalid protocol (%i should be %i)\n", i, PROTOCOL_VERSION ); + if( i != PROTOCOL_LEGACY_VERSION ) + Host_Error( "Server uses invalid protocol (%i should be %i)\n", i, PROTOCOL_LEGACY_VERSION ); cl.servercount = MSG_ReadLong( msg ); cl.checksum = MSG_ReadLong( msg ); From c1ca2c1a95cff225eb135ceb14a51237dbb6d676 Mon Sep 17 00:00:00 2001 From: Andrey Akhmichin <15944199+nekonomicon@users.noreply.github.com> Date: Mon, 26 Sep 2022 20:20:17 +0500 Subject: [PATCH 148/490] Documentation: opensource-mods.md: add "Oz Deathmatch". --- Documentation/opensource-mods.md | 3 +++ 1 file changed, 3 insertions(+) diff --git a/Documentation/opensource-mods.md b/Documentation/opensource-mods.md index 88676a33..43c36958 100644 --- a/Documentation/opensource-mods.md +++ b/Documentation/opensource-mods.md @@ -127,6 +127,9 @@ Official github repository - https://github.com/unknownworlds/NS ## Overturn Available in mod archive on ModDB - https://www.moddb.com/mods/overturn +## Oz Deathmatch +Mirrored on github - https://github.com/nekonomicon/OZDM + ## Spirit of Half-Life [Logic&Trick's](https://github.com/LogicAndTrick) mirror - https://files.logic-and-trick.com/#/Half-Life/Mods/Spirit%20of%20Half-Life From d23711496294377c7ba9408d07b3b9e661df8a12 Mon Sep 17 00:00:00 2001 From: Andrey Akhmichin <15944199+nekonomicon@users.noreply.github.com> Date: Wed, 2 Nov 2022 03:18:00 +0500 Subject: [PATCH 149/490] engine: common: imagelib: img_png.c: unroll loops. --- engine/common/imagelib/img_png.c | 85 +++++++++++++------------------- 1 file changed, 35 insertions(+), 50 deletions(-) diff --git a/engine/common/imagelib/img_png.c b/engine/common/imagelib/img_png.c index 25b32544..de6ed399 100644 --- a/engine/common/imagelib/img_png.c +++ b/engine/common/imagelib/img_png.c @@ -41,10 +41,10 @@ qboolean Image_LoadPNG( const char *name, const byte *buffer, fs_offset_t filesi { int ret; short p, a, b, c, pa, pb, pc; - byte *buf_p, *pixbuf, *raw, *prior, *idat_buf = NULL, *uncompressed_buffer = NULL, *rowend; + byte *buf_p, *pixbuf, *raw, *prior, *idat_buf = NULL, *uncompressed_buffer = NULL; byte *pallete = NULL, *trns = NULL; uint chunk_len, trns_len, crc32, crc32_check, oldsize = 0, newsize = 0, rowsize; - uint uncompressed_size, pixel_size, i, y, filter_type, chunk_sign, r_alpha, g_alpha, b_alpha; + uint uncompressed_size, pixel_size, pixel_count, i, y, filter_type, chunk_sign, r_alpha, g_alpha, b_alpha; qboolean has_iend_chunk = false; z_stream stream = {0}; png_t png_hdr; @@ -262,7 +262,8 @@ qboolean Image_LoadPNG( const char *name, const byte *buffer, fs_offset_t filesi image.type = PF_RGBA_32; // always exctracted to 32-bit buffer image.width = png_hdr.ihdr_chunk.width; image.height = png_hdr.ihdr_chunk.height; - image.size = image.height * image.width * 4; + pixel_count = image.height * image.width; + image.size = pixel_count * 4; if( png_hdr.ihdr_chunk.colortype & PNG_CT_RGB ) image.flags |= IMAGE_HAS_COLOR; @@ -424,72 +425,56 @@ qboolean Image_LoadPNG( const char *name, const byte *buffer, fs_offset_t filesi b_alpha = trns[4] << 8 | trns[5]; } - for( y = 0; y < image.height; y++ ) + for( y = 0; y < pixel_count; y++, raw += pixel_size ) { - rowend = raw + rowsize; - for( ; raw < rowend; raw += pixel_size ) - { - *pixbuf++ = raw[0]; - *pixbuf++ = raw[1]; - *pixbuf++ = raw[2]; + *pixbuf++ = raw[0]; + *pixbuf++ = raw[1]; + *pixbuf++ = raw[2]; - if( trns && r_alpha == raw[0] - && g_alpha == raw[1] - && b_alpha == raw[2] ) - *pixbuf++ = 0; - else - *pixbuf++ = 0xFF; - } + if( trns && r_alpha == raw[0] + && g_alpha == raw[1] + && b_alpha == raw[2] ) + *pixbuf++ = 0; + else + *pixbuf++ = 0xFF; } break; case PNG_CT_GREY: if( trns ) r_alpha = trns[0] << 8 | trns[1]; - for( y = 0; y < image.height; y++ ) + for( y = 0; y < pixel_count; y++, raw += pixel_size ) { - rowend = raw + rowsize; - for( ; raw < rowend; raw += pixel_size ) - { - *pixbuf++ = raw[0]; - *pixbuf++ = raw[0]; - *pixbuf++ = raw[0]; + *pixbuf++ = raw[0]; + *pixbuf++ = raw[0]; + *pixbuf++ = raw[0]; - if( trns && r_alpha == raw[0] ) - *pixbuf++ = 0; - else - *pixbuf++ = 0xFF; - } + if( trns && r_alpha == raw[0] ) + *pixbuf++ = 0; + else + *pixbuf++ = 0xFF; } break; case PNG_CT_ALPHA: - for( y = 0; y < image.height; y++ ) + for( y = 0; y < pixel_count; y++, raw += pixel_size ) { - rowend = raw + rowsize; - for( ; raw < rowend; raw += pixel_size ) - { - *pixbuf++ = raw[0]; - *pixbuf++ = raw[0]; - *pixbuf++ = raw[0]; - *pixbuf++ = raw[1]; - } + *pixbuf++ = raw[0]; + *pixbuf++ = raw[0]; + *pixbuf++ = raw[0]; + *pixbuf++ = raw[1]; } break; case PNG_CT_PALLETE: - for( y = 0; y < image.height; y++ ) + for( y = 0; y < pixel_count; y++, raw += pixel_size ) { - rowend = raw + rowsize; - for( ; raw < rowend; raw += pixel_size ) - { - *pixbuf++ = pallete[raw[0] + 2]; - *pixbuf++ = pallete[raw[0] + 1]; - *pixbuf++ = pallete[raw[0] + 0]; + *pixbuf++ = pallete[raw[0] + 2]; + *pixbuf++ = pallete[raw[0] + 1]; + *pixbuf++ = pallete[raw[0] + 0]; - if( trns && raw[0] < trns_len ) - *pixbuf++ = trns[raw[0]]; - else - *pixbuf++ = 0xFF; - } + if( trns && raw[0] < trns_len ) + *pixbuf++ = trns[raw[0]]; + else + *pixbuf++ = 0xFF; } break; default: From 816337a7bb9c358b03f2f75680c17749fedf306e Mon Sep 17 00:00:00 2001 From: Bien Pham Date: Wed, 2 Nov 2022 09:44:11 +0800 Subject: [PATCH 150/490] filesystem: set root directory as static Counter-Strike uses COM_ExpandFileName to get full path to filesystem_stdio. When reading liblist.gam, the engine will clear the search path and the root search path will be cleared. Set it as static so it will be excluded from clearing. --- filesystem/filesystem.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/filesystem/filesystem.c b/filesystem/filesystem.c index 55826b1b..17f7f845 100644 --- a/filesystem/filesystem.c +++ b/filesystem/filesystem.c @@ -1452,7 +1452,7 @@ qboolean FS_InitStdio( qboolean caseinsensitive, const char *rootdir, const char } // build list of game directories here - FS_AddGameDirectory( "./", 0 ); + FS_AddGameDirectory( "./", FS_STATIC_PATH ); for( i = 0; i < dirs.numstrings; i++ ) { From 2a6c2e5db40765d786867bf00ad603f09b04f043 Mon Sep 17 00:00:00 2001 From: Andrey Akhmichin <15944199+nekonomicon@users.noreply.github.com> Date: Sat, 5 Nov 2022 07:19:07 +0500 Subject: [PATCH 151/490] .github: workflows: update github actions. --- .github/workflows/c-cpp.yml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/.github/workflows/c-cpp.yml b/.github/workflows/c-cpp.yml index e53a8b16..37ff641f 100644 --- a/.github/workflows/c-cpp.yml +++ b/.github/workflows/c-cpp.yml @@ -48,11 +48,11 @@ jobs: UPLOADTOOL_ISPRERELEASE: true steps: - name: Checkout - uses: actions/checkout@v2 + uses: actions/checkout@v3 with: submodules: recursive - name: Checkout xash-extras - uses: actions/checkout@v2 + uses: actions/checkout@v3 with: repository: FWGS/xash-extras path: xash-extras @@ -66,7 +66,7 @@ jobs: - name: Upload engine (prereleases) run: bash scripts/continious_upload.sh artifacts/* - name: Upload engine (artifacts) - uses: actions/upload-artifact@v2 + uses: actions/upload-artifact@v3 with: name: artifact-${{ matrix.targetos }}-${{ matrix.targetarch }} path: artifacts/* From 1064b41ab219c7f25ffc6633048bfd2d95ccc86b Mon Sep 17 00:00:00 2001 From: Andrey Akhmichin <15944199+nekonomicon@users.noreply.github.com> Date: Sat, 5 Nov 2022 07:41:07 +0500 Subject: [PATCH 152/490] readme: update. --- README.md | 15 +++++++++------ 1 file changed, 9 insertions(+), 6 deletions(-) diff --git a/README.md b/README.md index 145c9fcc..fe9464d2 100644 --- a/README.md +++ b/README.md @@ -18,19 +18,22 @@ Read more about Xash3D on ModDB: https://www.moddb.com/engines/xash3d-engine * Mobility API: allows better game integration on mobile devices(vibration, touch controls) * Different input methods: touch, gamepad and classic mouse & keyboard. * TrueType font rendering, as a part of mainui_cpp. -* Multiple renderers support: OpenGL, GLESv1, GLESv2, Software +* Multiple renderers support: OpenGL, GLESv1, GLESv2, Software. +* Voice support. +* External filesystem module like in GoldSrc engine. +* External vgui support module. +* PNG image format support. * A set of small improvements, without broken compatibility. ## Planned fork features -* Virtual Reality support and game API -* Voice support -* Vulkan renderer +* Virtual Reality support and game API. +* Vulkan renderer. ## Installation & Running 0) Get Xash3D FWGS binaries: you can use [testing](https://github.com/FWGS/xash3d-fwgs/releases/tag/continuous) build or you can compile engine from source code. 1) Copy engine binaries to some directory. 2) Copy `valve` directory from [Half-Life](https://store.steampowered.com/app/70/HalfLife/) to directory with engine binaries. -If your CPU is NOT x86 compatible or you're running 64-bit version of the engine, you may want to compile [Half-Life SDK](https://github.com/FWGS/hlsdk-xash3d). +If your CPU is NOT x86 compatible or you're running 64-bit version of the engine, you may want to compile [Half-Life SDK](https://github.com/FWGS/hlsdk-portable). This repository contains our fork of HLSDK and restored source code for some of the mods. Not all of them, of course. You still needed to copy `valve` directory as all game resources located there. 3) Run the main executable (`xash3d.exe` or AppImage). @@ -52,7 +55,7 @@ NOTE: NEVER USE GitHub's ZIP ARCHIVES. GitHub doesn't include external dependenc If your CPU is x86 compatible, we are building 32-bit code by default. This was done to maintain compatibility with Steam releases of Half-Life and based on it's engine games. Even if Xash3D FWGS does support targetting 64-bit, you can't load games without recompiling them from source code! -If your CPU is NOT x86 compatible or you decided build 64-bit version of engine, you may want to compile [Half-Life SDK](https://github.com/FWGS/hlsdk-xash3d). +If your CPU is NOT x86 compatible or you decided build 64-bit version of engine, you may want to compile [Half-Life SDK](https://github.com/FWGS/hlsdk-portable). This repository contains our fork of HLSDK and restored source code for some of the mods. Not all of them, of course. #### Windows (Visual Studio) From c1fe5479257a959a0d1283a70dfa0e986a6096fa Mon Sep 17 00:00:00 2001 From: Andrey Akhmichin <15944199+nekonomicon@users.noreply.github.com> Date: Sun, 6 Nov 2022 07:07:06 +0500 Subject: [PATCH 153/490] public: update miniz to version 3.0.0 from upstream. --- public/miniz.h | 412 +++++++++++++++++++++++++++++++++---------------- 1 file changed, 277 insertions(+), 135 deletions(-) diff --git a/public/miniz.h b/public/miniz.h index cef9453d..55f8a778 100644 --- a/public/miniz.h +++ b/public/miniz.h @@ -1,4 +1,7 @@ -/* miniz.c 2.1.0 - public domain deflate/inflate, zlib-subset, ZIP reading/writing/appending, PNG writing +#ifndef MINIZ_EXPORT +#define MINIZ_EXPORT +#endif +/* miniz.c 3.0.0 - public domain deflate/inflate, zlib-subset, ZIP reading/writing/appending, PNG writing See "unlicense" statement at the end of this file. Rich Geldreich , last updated Oct. 13, 2013 Implements RFC 1950: http://www.ietf.org/rfc/rfc1950.txt and RFC 1951: http://www.ietf.org/rfc/rfc1951.txt @@ -95,7 +98,7 @@ possibility that the archive's central directory could be lost with this method if anything goes wrong, though. - ZIP archive support limitations: - No zip64 or spanning support. Extraction functions can only handle unencrypted, stored or deflated files. + No spanning support. Extraction functions can only handle unencrypted, stored or deflated files. Requires streams capable of seeking. * This is a header file library, like stb_image.c. To get only a header file, either cut and paste the @@ -114,10 +117,8 @@ - - -/* Defines to completely disable specific portions of miniz.c: - If all macros here are defined the only functionality remaining will be CRC-32, adler-32, tinfl, and tdefl. */ +/* Defines to completely disable specific portions of miniz.c: + If all macros here are defined the only functionality remaining will be CRC-32 and adler-32. */ /* Define MINIZ_NO_STDIO to disable all usage and any functions which rely on stdio for file I/O. */ #define MINIZ_NO_STDIO @@ -127,6 +128,12 @@ /* The current downside is the times written to your archives will be from 1979. */ #define MINIZ_NO_TIME +/* Define MINIZ_NO_DEFLATE_APIS to disable all compression API's. */ +/*#define MINIZ_NO_DEFLATE_APIS */ + +/* Define MINIZ_NO_INFLATE_APIS to disable all decompression API's. */ +/*#define MINIZ_NO_INFLATE_APIS */ + /* Define MINIZ_NO_ARCHIVE_APIS to disable all ZIP archive API's. */ #define MINIZ_NO_ARCHIVE_APIS @@ -139,12 +146,20 @@ /* Define MINIZ_NO_ZLIB_COMPATIBLE_NAME to disable zlib names, to prevent conflicts against stock zlib. */ /*#define MINIZ_NO_ZLIB_COMPATIBLE_NAMES */ -/* Define MINIZ_NO_MALLOC to disable all calls to malloc, free, and realloc. +/* Define MINIZ_NO_MALLOC to disable all calls to malloc, free, and realloc. Note if MINIZ_NO_MALLOC is defined then the user must always provide custom user alloc/free/realloc callbacks to the zlib and archive API's, and a few stand-alone helper API's which don't provide custom user functions (such as tdefl_compress_mem_to_heap() and tinfl_decompress_mem_to_heap()) won't work. */ /*#define MINIZ_NO_MALLOC */ +#ifdef MINIZ_NO_INFLATE_APIS +#define MINIZ_NO_ARCHIVE_APIS +#endif + +#ifdef MINIZ_NO_DEFLATE_APIS +#define MINIZ_NO_ARCHIVE_WRITING_APIS +#endif + #if defined(__TINYC__) && (defined(__linux) || defined(__linux__)) /* TODO: Work around "error: include file 'sys\utime.h' when compiling with tcc on Linux */ #define MINIZ_NO_TIME @@ -163,13 +178,35 @@ #define MINIZ_X86_OR_X64_CPU 0 #endif -#if (__BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__) || MINIZ_X86_OR_X64_CPU +/* Set MINIZ_LITTLE_ENDIAN only if not set */ +#if !defined(MINIZ_LITTLE_ENDIAN) +#if defined(__BYTE_ORDER__) && defined(__ORDER_LITTLE_ENDIAN__) + +#if (__BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__) /* Set MINIZ_LITTLE_ENDIAN to 1 if the processor is little endian. */ #define MINIZ_LITTLE_ENDIAN 1 #else #define MINIZ_LITTLE_ENDIAN 0 #endif +#else + +#if MINIZ_X86_OR_X64_CPU +#define MINIZ_LITTLE_ENDIAN 1 +#else +#define MINIZ_LITTLE_ENDIAN 0 +#endif + +#endif +#endif + +/* Using unaligned loads and stores causes errors when using UBSan */ +#if defined(__has_feature) +#if __has_feature(undefined_behavior_sanitizer) +#define MINIZ_USE_UNALIGNED_LOADS_AND_STORES 0 +#endif +#endif + /* Set MINIZ_USE_UNALIGNED_LOADS_AND_STORES only if not set */ #if !defined(MINIZ_USE_UNALIGNED_LOADS_AND_STORES) #if MINIZ_X86_OR_X64_CPU @@ -198,15 +235,15 @@ extern "C" { typedef unsigned long mz_ulong; /* mz_free() internally uses the MZ_FREE() macro (which by default calls free() unless you've modified the MZ_MALLOC macro) to release a block allocated from the heap. */ -void mz_free(void *p); +MINIZ_EXPORT void mz_free(void *p); #define MZ_ADLER32_INIT (1) /* mz_adler32() returns the initial adler-32 value to use when called with ptr==NULL. */ -mz_ulong mz_adler32(mz_ulong adler, const unsigned char *ptr, size_t buf_len); +MINIZ_EXPORT mz_ulong mz_adler32(mz_ulong adler, const unsigned char *ptr, size_t buf_len); #define MZ_CRC32_INIT (0) /* mz_crc32() returns the initial CRC-32 value to use when called with ptr==NULL. */ -mz_ulong mz_crc32(mz_ulong crc, const unsigned char *ptr, size_t buf_len); +MINIZ_EXPORT mz_ulong mz_crc32(mz_ulong crc, const unsigned char *ptr, size_t buf_len); /* Compression strategies. */ enum @@ -222,7 +259,7 @@ enum #define MZ_DEFLATED 8 /* Heap allocation callbacks. -Note that mz_alloc_func parameter types purpsosely differ from zlib's: items/size is size_t, not unsigned long. */ +Note that mz_alloc_func parameter types purposely differ from zlib's: items/size is size_t, not unsigned long. */ typedef void *(*mz_alloc_func)(void *opaque, size_t items, size_t size); typedef void (*mz_free_func)(void *opaque, void *address); typedef void *(*mz_realloc_func)(void *opaque, void *address, size_t items, size_t size); @@ -238,10 +275,10 @@ enum MZ_DEFAULT_COMPRESSION = -1 }; -#define MZ_VERSION "10.1.0" -#define MZ_VERNUM 0xA100 -#define MZ_VER_MAJOR 10 -#define MZ_VER_MINOR 1 +#define MZ_VERSION "11.0.0" +#define MZ_VERNUM 0xB000 +#define MZ_VER_MAJOR 11 +#define MZ_VER_MINOR 0 #define MZ_VER_REVISION 0 #define MZ_VER_SUBREVISION 0 @@ -304,7 +341,9 @@ typedef struct mz_stream_s typedef mz_stream *mz_streamp; /* Returns the version string of miniz.c. */ -const char *mz_version(void); +MINIZ_EXPORT const char *mz_version(void); + +#ifndef MINIZ_NO_DEFLATE_APIS /* mz_deflateInit() initializes a compressor with default options: */ /* Parameters: */ @@ -317,17 +356,17 @@ const char *mz_version(void); /* MZ_STREAM_ERROR if the stream is bogus. */ /* MZ_PARAM_ERROR if the input parameters are bogus. */ /* MZ_MEM_ERROR on out of memory. */ -int mz_deflateInit(mz_streamp pStream, int level); +MINIZ_EXPORT int mz_deflateInit(mz_streamp pStream, int level); /* mz_deflateInit2() is like mz_deflate(), except with more control: */ /* Additional parameters: */ /* method must be MZ_DEFLATED */ /* window_bits must be MZ_DEFAULT_WINDOW_BITS (to wrap the deflate stream with zlib header/adler-32 footer) or -MZ_DEFAULT_WINDOW_BITS (raw deflate/no header or footer) */ /* mem_level must be between [1, 9] (it's checked but ignored by miniz.c) */ -int mz_deflateInit2(mz_streamp pStream, int level, int method, int window_bits, int mem_level, int strategy); +MINIZ_EXPORT int mz_deflateInit2(mz_streamp pStream, int level, int method, int window_bits, int mem_level, int strategy); /* Quickly resets a compressor without having to reallocate anything. Same as calling mz_deflateEnd() followed by mz_deflateInit()/mz_deflateInit2(). */ -int mz_deflateReset(mz_streamp pStream); +MINIZ_EXPORT int mz_deflateReset(mz_streamp pStream); /* mz_deflate() compresses the input to output, consuming as much of the input and producing as much output as possible. */ /* Parameters: */ @@ -339,34 +378,38 @@ int mz_deflateReset(mz_streamp pStream); /* MZ_STREAM_ERROR if the stream is bogus. */ /* MZ_PARAM_ERROR if one of the parameters is invalid. */ /* MZ_BUF_ERROR if no forward progress is possible because the input and/or output buffers are empty. (Fill up the input buffer or free up some output space and try again.) */ -int mz_deflate(mz_streamp pStream, int flush); +MINIZ_EXPORT int mz_deflate(mz_streamp pStream, int flush); /* mz_deflateEnd() deinitializes a compressor: */ /* Return values: */ /* MZ_OK on success. */ /* MZ_STREAM_ERROR if the stream is bogus. */ -int mz_deflateEnd(mz_streamp pStream); +MINIZ_EXPORT int mz_deflateEnd(mz_streamp pStream); /* mz_deflateBound() returns a (very) conservative upper bound on the amount of data that could be generated by deflate(), assuming flush is set to only MZ_NO_FLUSH or MZ_FINISH. */ -mz_ulong mz_deflateBound(mz_streamp pStream, mz_ulong source_len); +MINIZ_EXPORT mz_ulong mz_deflateBound(mz_streamp pStream, mz_ulong source_len); /* Single-call compression functions mz_compress() and mz_compress2(): */ /* Returns MZ_OK on success, or one of the error codes from mz_deflate() on failure. */ -int mz_compress(unsigned char *pDest, mz_ulong *pDest_len, const unsigned char *pSource, mz_ulong source_len); -int mz_compress2(unsigned char *pDest, mz_ulong *pDest_len, const unsigned char *pSource, mz_ulong source_len, int level); +MINIZ_EXPORT int mz_compress(unsigned char *pDest, mz_ulong *pDest_len, const unsigned char *pSource, mz_ulong source_len); +MINIZ_EXPORT int mz_compress2(unsigned char *pDest, mz_ulong *pDest_len, const unsigned char *pSource, mz_ulong source_len, int level); /* mz_compressBound() returns a (very) conservative upper bound on the amount of data that could be generated by calling mz_compress(). */ -mz_ulong mz_compressBound(mz_ulong source_len); +MINIZ_EXPORT mz_ulong mz_compressBound(mz_ulong source_len); + +#endif /*#ifndef MINIZ_NO_DEFLATE_APIS*/ + +#ifndef MINIZ_NO_INFLATE_APIS /* Initializes a decompressor. */ -int mz_inflateInit(mz_streamp pStream); +MINIZ_EXPORT int mz_inflateInit(mz_streamp pStream); /* mz_inflateInit2() is like mz_inflateInit() with an additional option that controls the window size and whether or not the stream has been wrapped with a zlib header/footer: */ /* window_bits must be MZ_DEFAULT_WINDOW_BITS (to parse zlib header/footer) or -MZ_DEFAULT_WINDOW_BITS (raw deflate). */ -int mz_inflateInit2(mz_streamp pStream, int window_bits); +MINIZ_EXPORT int mz_inflateInit2(mz_streamp pStream, int window_bits); /* Quickly resets a compressor without having to reallocate anything. Same as calling mz_inflateEnd() followed by mz_inflateInit()/mz_inflateInit2(). */ -int mz_inflateReset(mz_streamp pStream); +MINIZ_EXPORT int mz_inflateReset(mz_streamp pStream); /* Decompresses the input stream to the output, consuming only as much of the input as needed, and writing as much to the output as possible. */ /* Parameters: */ @@ -382,17 +425,19 @@ int mz_inflateReset(mz_streamp pStream); /* MZ_PARAM_ERROR if one of the parameters is invalid. */ /* MZ_BUF_ERROR if no forward progress is possible because the input buffer is empty but the inflater needs more input to continue, or if the output buffer is not large enough. Call mz_inflate() again */ /* with more input data, or with more room in the output buffer (except when using single call decompression, described above). */ -int mz_inflate(mz_streamp pStream, int flush); +MINIZ_EXPORT int mz_inflate(mz_streamp pStream, int flush); /* Deinitializes a decompressor. */ -int mz_inflateEnd(mz_streamp pStream); +MINIZ_EXPORT int mz_inflateEnd(mz_streamp pStream); /* Single-call decompression. */ /* Returns MZ_OK on success, or one of the error codes from mz_inflate() on failure. */ -int mz_uncompress(unsigned char *pDest, mz_ulong *pDest_len, const unsigned char *pSource, mz_ulong source_len); +MINIZ_EXPORT int mz_uncompress(unsigned char *pDest, mz_ulong *pDest_len, const unsigned char *pSource, mz_ulong source_len); +MINIZ_EXPORT int mz_uncompress2(unsigned char *pDest, mz_ulong *pDest_len, const unsigned char *pSource, mz_ulong *pSource_len); +#endif /*#ifndef MINIZ_NO_INFLATE_APIS*/ /* Returns a string description of the specified error code, or NULL if the error code is invalid. */ -const char *mz_error(int err); +MINIZ_EXPORT const char *mz_error(int err); /* Redefine zlib-compatible names to miniz equivalents, so miniz.c can be used as a drop-in replacement for the subset of zlib that miniz.c supports. */ /* Define MINIZ_NO_ZLIB_COMPATIBLE_NAMES to disable zlib-compatibility if you use zlib in the same project. */ @@ -440,6 +485,8 @@ typedef void *const voidpc; #define free_func mz_free_func #define internal_state mz_internal_state #define z_stream mz_stream + +#ifndef MINIZ_NO_DEFLATE_APIS #define deflateInit mz_deflateInit #define deflateInit2 mz_deflateInit2 #define deflateReset mz_deflateReset @@ -449,12 +496,18 @@ typedef void *const voidpc; #define compress mz_compress #define compress2 mz_compress2 #define compressBound mz_compressBound +#endif /*#ifndef MINIZ_NO_DEFLATE_APIS*/ + +#ifndef MINIZ_NO_INFLATE_APIS #define inflateInit mz_inflateInit #define inflateInit2 mz_inflateInit2 #define inflateReset mz_inflateReset #define inflate mz_inflate #define inflateEnd mz_inflateEnd #define uncompress mz_uncompress +#define uncompress2 mz_uncompress2 +#endif /*#ifndef MINIZ_NO_INFLATE_APIS*/ + #define crc32 mz_crc32 #define adler32 mz_adler32 #define MAX_WBITS 15 @@ -475,12 +528,19 @@ typedef void *const voidpc; #ifdef __cplusplus } #endif + + + + + #pragma once #include #include #include #include + + /* ------------------- Types and macros */ typedef unsigned char mz_uint8; typedef signed short mz_int16; @@ -511,7 +571,8 @@ typedef int mz_bool; #ifdef MINIZ_NO_TIME typedef struct mz_dummy_time_t_tag { - int m_dummy; + mz_uint32 m_dummy1; + mz_uint32 m_dummy2; } mz_dummy_time_t; #define MZ_TIME_T mz_dummy_time_t #else @@ -533,6 +594,8 @@ typedef struct mz_dummy_time_t_tag #define MZ_MAX(a, b) (((a) > (b)) ? (a) : (b)) #define MZ_MIN(a, b) (((a) < (b)) ? (a) : (b)) #define MZ_CLEAR_OBJ(obj) memset(&(obj), 0, sizeof(obj)) +#define MZ_CLEAR_ARR(obj) memset((obj), 0, sizeof(obj)) +#define MZ_CLEAR_PTR(obj) memset((obj), 0, sizeof(*obj)) #if MINIZ_USE_UNALIGNED_LOADS_AND_STORES && MINIZ_LITTLE_ENDIAN #define MZ_READ_LE16(p) *((const mz_uint16 *)(p)) @@ -556,9 +619,9 @@ typedef struct mz_dummy_time_t_tag extern "C" { #endif -extern void *miniz_def_alloc_func(void *opaque, size_t items, size_t size); -extern void miniz_def_free_func(void *opaque, void *address); -extern void *miniz_def_realloc_func(void *opaque, void *address, size_t items, size_t size); +extern MINIZ_EXPORT void *miniz_def_alloc_func(void *opaque, size_t items, size_t size); +extern MINIZ_EXPORT void miniz_def_free_func(void *opaque, void *address); +extern MINIZ_EXPORT void *miniz_def_realloc_func(void *opaque, void *address, size_t items, size_t size); #define MZ_UINT16_MAX (0xFFFFU) #define MZ_UINT32_MAX (0xFFFFFFFFU) @@ -569,6 +632,8 @@ extern void *miniz_def_realloc_func(void *opaque, void *address, size_t items, s #pragma once +#ifndef MINIZ_NO_DEFLATE_APIS + #ifdef __cplusplus extern "C" { #endif @@ -616,11 +681,11 @@ enum /* Function returns a pointer to the compressed data, or NULL on failure. */ /* *pOut_len will be set to the compressed data's size, which could be larger than src_buf_len on uncompressible data. */ /* The caller must free() the returned block when it's no longer needed. */ -void *tdefl_compress_mem_to_heap(const void *pSrc_buf, size_t src_buf_len, size_t *pOut_len, int flags); +MINIZ_EXPORT void *tdefl_compress_mem_to_heap(const void *pSrc_buf, size_t src_buf_len, size_t *pOut_len, int flags); /* tdefl_compress_mem_to_mem() compresses a block in memory to another block in memory. */ /* Returns 0 on failure. */ -size_t tdefl_compress_mem_to_mem(void *pOut_buf, size_t out_buf_len, const void *pSrc_buf, size_t src_buf_len, int flags); +MINIZ_EXPORT size_t tdefl_compress_mem_to_mem(void *pOut_buf, size_t out_buf_len, const void *pSrc_buf, size_t src_buf_len, int flags); /* Compresses an image to a compressed PNG file in memory. */ /* On entry: */ @@ -632,14 +697,14 @@ size_t tdefl_compress_mem_to_mem(void *pOut_buf, size_t out_buf_len, const void /* Function returns a pointer to the compressed data, or NULL on failure. */ /* *pLen_out will be set to the size of the PNG image file. */ /* The caller must mz_free() the returned heap block (which will typically be larger than *pLen_out) when it's no longer needed. */ -void *tdefl_write_image_to_png_file_in_memory_ex(const void *pImage, int w, int h, int num_chans, size_t *pLen_out, mz_uint level, mz_bool flip); -void *tdefl_write_image_to_png_file_in_memory(const void *pImage, int w, int h, int num_chans, size_t *pLen_out); +MINIZ_EXPORT void *tdefl_write_image_to_png_file_in_memory_ex(const void *pImage, int w, int h, int num_chans, size_t *pLen_out, mz_uint level, mz_bool flip); +MINIZ_EXPORT void *tdefl_write_image_to_png_file_in_memory(const void *pImage, int w, int h, int num_chans, size_t *pLen_out); /* Output stream interface. The compressor uses this interface to write compressed data. It'll typically be called TDEFL_OUT_BUF_SIZE at a time. */ typedef mz_bool (*tdefl_put_buf_func_ptr)(const void *pBuf, int len, void *pUser); /* tdefl_compress_mem_to_output() compresses a block to an output stream. The above helpers use this function internally. */ -mz_bool tdefl_compress_mem_to_output(const void *pBuf, size_t buf_len, tdefl_put_buf_func_ptr pPut_buf_func, void *pPut_buf_user, int flags); +MINIZ_EXPORT mz_bool tdefl_compress_mem_to_output(const void *pBuf, size_t buf_len, tdefl_put_buf_func_ptr pPut_buf_func, void *pPut_buf_user, int flags); enum { @@ -727,39 +792,43 @@ typedef struct /* pBut_buf_func: If NULL, output data will be supplied to the specified callback. In this case, the user should call the tdefl_compress_buffer() API for compression. */ /* If pBut_buf_func is NULL the user should always call the tdefl_compress() API. */ /* flags: See the above enums (TDEFL_HUFFMAN_ONLY, TDEFL_WRITE_ZLIB_HEADER, etc.) */ -tdefl_status tdefl_init(tdefl_compressor *d, tdefl_put_buf_func_ptr pPut_buf_func, void *pPut_buf_user, int flags); +MINIZ_EXPORT tdefl_status tdefl_init(tdefl_compressor *d, tdefl_put_buf_func_ptr pPut_buf_func, void *pPut_buf_user, int flags); /* Compresses a block of data, consuming as much of the specified input buffer as possible, and writing as much compressed data to the specified output buffer as possible. */ -tdefl_status tdefl_compress(tdefl_compressor *d, const void *pIn_buf, size_t *pIn_buf_size, void *pOut_buf, size_t *pOut_buf_size, tdefl_flush flush); +MINIZ_EXPORT tdefl_status tdefl_compress(tdefl_compressor *d, const void *pIn_buf, size_t *pIn_buf_size, void *pOut_buf, size_t *pOut_buf_size, tdefl_flush flush); /* tdefl_compress_buffer() is only usable when the tdefl_init() is called with a non-NULL tdefl_put_buf_func_ptr. */ /* tdefl_compress_buffer() always consumes the entire input buffer. */ -tdefl_status tdefl_compress_buffer(tdefl_compressor *d, const void *pIn_buf, size_t in_buf_size, tdefl_flush flush); +MINIZ_EXPORT tdefl_status tdefl_compress_buffer(tdefl_compressor *d, const void *pIn_buf, size_t in_buf_size, tdefl_flush flush); -tdefl_status tdefl_get_prev_return_status(tdefl_compressor *d); -mz_uint32 tdefl_get_adler32(tdefl_compressor *d); +MINIZ_EXPORT tdefl_status tdefl_get_prev_return_status(tdefl_compressor *d); +MINIZ_EXPORT mz_uint32 tdefl_get_adler32(tdefl_compressor *d); /* Create tdefl_compress() flags given zlib-style compression parameters. */ /* level may range from [0,10] (where 10 is absolute max compression, but may be much slower on some files) */ /* window_bits may be -15 (raw deflate) or 15 (zlib) */ /* strategy may be either MZ_DEFAULT_STRATEGY, MZ_FILTERED, MZ_HUFFMAN_ONLY, MZ_RLE, or MZ_FIXED */ -mz_uint tdefl_create_comp_flags_from_zip_params(int level, int window_bits, int strategy); +MINIZ_EXPORT mz_uint tdefl_create_comp_flags_from_zip_params(int level, int window_bits, int strategy); #ifndef MINIZ_NO_MALLOC /* Allocate the tdefl_compressor structure in C so that */ /* non-C language bindings to tdefl_ API don't need to worry about */ /* structure size and allocation mechanism. */ -tdefl_compressor *tdefl_compressor_alloc(void); -void tdefl_compressor_free(tdefl_compressor *pComp); +MINIZ_EXPORT tdefl_compressor *tdefl_compressor_alloc(void); +MINIZ_EXPORT void tdefl_compressor_free(tdefl_compressor *pComp); #endif #ifdef __cplusplus } #endif + +#endif /*#ifndef MINIZ_NO_DEFLATE_APIS*/ #pragma once /* ------------------- Low-level Decompression API Definitions */ +#ifndef MINIZ_NO_INFLATE_APIS + #ifdef __cplusplus extern "C" { #endif @@ -784,17 +853,17 @@ enum /* Function returns a pointer to the decompressed data, or NULL on failure. */ /* *pOut_len will be set to the decompressed data's size, which could be larger than src_buf_len on uncompressible data. */ /* The caller must call mz_free() on the returned block when it's no longer needed. */ -void *tinfl_decompress_mem_to_heap(const void *pSrc_buf, size_t src_buf_len, size_t *pOut_len, int flags); +MINIZ_EXPORT void *tinfl_decompress_mem_to_heap(const void *pSrc_buf, size_t src_buf_len, size_t *pOut_len, int flags); /* tinfl_decompress_mem_to_mem() decompresses a block in memory to another block in memory. */ /* Returns TINFL_DECOMPRESS_MEM_TO_MEM_FAILED on failure, or the number of bytes written on success. */ #define TINFL_DECOMPRESS_MEM_TO_MEM_FAILED ((size_t)(-1)) -size_t tinfl_decompress_mem_to_mem(void *pOut_buf, size_t out_buf_len, const void *pSrc_buf, size_t src_buf_len, int flags); +MINIZ_EXPORT size_t tinfl_decompress_mem_to_mem(void *pOut_buf, size_t out_buf_len, const void *pSrc_buf, size_t src_buf_len, int flags); /* tinfl_decompress_mem_to_callback() decompresses a block in memory to an internal 32KB buffer, and a user provided callback function will be called to flush the buffer. */ /* Returns 1 on success or 0 on failure. */ typedef int (*tinfl_put_buf_func_ptr)(const void *pBuf, int len, void *pUser); -int tinfl_decompress_mem_to_callback(const void *pIn_buf, size_t *pIn_buf_size, tinfl_put_buf_func_ptr pPut_buf_func, void *pPut_buf_user, int flags); +MINIZ_EXPORT int tinfl_decompress_mem_to_callback(const void *pIn_buf, size_t *pIn_buf_size, tinfl_put_buf_func_ptr pPut_buf_func, void *pPut_buf_user, int flags); struct tinfl_decompressor_tag; typedef struct tinfl_decompressor_tag tinfl_decompressor; @@ -803,8 +872,8 @@ typedef struct tinfl_decompressor_tag tinfl_decompressor; /* Allocate the tinfl_decompressor structure in C so that */ /* non-C language bindings to tinfl_ API don't need to worry about */ /* structure size and allocation mechanism. */ -tinfl_decompressor *tinfl_decompressor_alloc(void); -void tinfl_decompressor_free(tinfl_decompressor *pDecomp); +MINIZ_EXPORT tinfl_decompressor *tinfl_decompressor_alloc(void); +MINIZ_EXPORT void tinfl_decompressor_free(tinfl_decompressor *pDecomp); #endif /* Max size of LZ dictionary. */ @@ -855,7 +924,7 @@ typedef enum { /* Main low-level decompressor coroutine function. This is the only function actually needed for decompression. All the other functions are just high-level helpers for improved usability. */ /* This is a universal API, i.e. it can be used as a building block to build any desired higher level decompression API. In the limit case, it can be called once per every byte input or output. */ -tinfl_status tinfl_decompress(tinfl_decompressor *r, const mz_uint8 *pIn_buf_next, size_t *pIn_buf_size, mz_uint8 *pOut_buf_start, mz_uint8 *pOut_buf_next, size_t *pOut_buf_size, const mz_uint32 decomp_flags); +MINIZ_EXPORT tinfl_status tinfl_decompress(tinfl_decompressor *r, const mz_uint8 *pIn_buf_next, size_t *pIn_buf_size, mz_uint8 *pOut_buf_start, mz_uint8 *pOut_buf_next, size_t *pOut_buf_size, const mz_uint32 decomp_flags); /* Internal/private bits follow. */ enum @@ -868,12 +937,6 @@ enum TINFL_FAST_LOOKUP_SIZE = 1 << TINFL_FAST_LOOKUP_BITS }; -typedef struct -{ - mz_uint8 m_code_size[TINFL_MAX_HUFF_SYMBOLS_0]; - mz_int16 m_look_up[TINFL_FAST_LOOKUP_SIZE], m_tree[TINFL_MAX_HUFF_SYMBOLS_0 * 2]; -} tinfl_huff_table; - #if MINIZ_HAS_64BIT_REGISTERS #define TINFL_USE_64BIT_BITBUF 1 #else @@ -893,7 +956,13 @@ struct tinfl_decompressor_tag mz_uint32 m_state, m_num_bits, m_zhdr0, m_zhdr1, m_z_adler32, m_final, m_type, m_check_adler32, m_dist, m_counter, m_num_extra, m_table_sizes[TINFL_MAX_HUFF_TABLES]; tinfl_bit_buf_t m_bit_buf; size_t m_dist_from_out_buf_start; - tinfl_huff_table m_tables[TINFL_MAX_HUFF_TABLES]; + mz_int16 m_look_up[TINFL_MAX_HUFF_TABLES][TINFL_FAST_LOOKUP_SIZE]; + mz_int16 m_tree_0[TINFL_MAX_HUFF_SYMBOLS_0 * 2]; + mz_int16 m_tree_1[TINFL_MAX_HUFF_SYMBOLS_1 * 2]; + mz_int16 m_tree_2[TINFL_MAX_HUFF_SYMBOLS_2 * 2]; + mz_uint8 m_code_size_0[TINFL_MAX_HUFF_SYMBOLS_0]; + mz_uint8 m_code_size_1[TINFL_MAX_HUFF_SYMBOLS_1]; + mz_uint8 m_code_size_2[TINFL_MAX_HUFF_SYMBOLS_2]; mz_uint8 m_raw_header[4], m_len_codes[TINFL_MAX_HUFF_SYMBOLS_0 + TINFL_MAX_HUFF_SYMBOLS_1 + 137]; }; @@ -901,6 +970,8 @@ struct tinfl_decompressor_tag } #endif +#endif /*#ifndef MINIZ_NO_INFLATE_APIS*/ + #ifndef MINIZ_HEADER_FILE_ONLY /************************************************************************** * @@ -986,6 +1057,12 @@ mz_ulong mz_adler32(mz_ulong adler, const unsigned char *ptr, size_t buf_len) } return ~crcu32; } +#elif defined(USE_EXTERNAL_MZCRC) +/* If USE_EXTERNAL_CRC is defined, an external module will export the + * mz_crc32() symbol for us to use, e.g. an SSE-accelerated version. + * Depending on the impl, it may be necessary to ~ the input/output crc values. + */ +mz_ulong mz_crc32(mz_ulong crc, const mz_uint8 *ptr, size_t buf_len); #else /* Faster, but larger CPU cache footprint. */ @@ -1061,17 +1138,17 @@ void mz_free(void *p) MZ_FREE(p); } -void *miniz_def_alloc_func(void *opaque, size_t items, size_t size) +MINIZ_EXPORT void *miniz_def_alloc_func(void *opaque, size_t items, size_t size) { (void)opaque, (void)items, (void)size; return MZ_MALLOC(items * size); } -void miniz_def_free_func(void *opaque, void *address) +MINIZ_EXPORT void miniz_def_free_func(void *opaque, void *address) { (void)opaque, (void)address; MZ_FREE(address); } -void *miniz_def_realloc_func(void *opaque, void *address, size_t items, size_t size) +MINIZ_EXPORT void *miniz_def_realloc_func(void *opaque, void *address, size_t items, size_t size) { (void)opaque, (void)address, (void)items, (void)size; return MZ_REALLOC(address, items * size); @@ -1084,6 +1161,8 @@ const char *mz_version(void) #ifndef MINIZ_NO_ZLIB_APIS +#ifndef MINIZ_NO_DEFLATE_APIS + int mz_deflateInit(mz_streamp pStream, int level) { return mz_deflateInit2(pStream, level, MZ_DEFLATED, MZ_DEFAULT_WINDOW_BITS, 9, MZ_DEFAULT_STRATEGY); @@ -1218,7 +1297,7 @@ int mz_compress2(unsigned char *pDest, mz_ulong *pDest_len, const unsigned char memset(&stream, 0, sizeof(stream)); /* In case mz_ulong is 64-bits (argh I hate longs). */ - if ((source_len | *pDest_len) > 0xFFFFFFFFU) + if ((mz_uint64)(source_len | *pDest_len) > 0xFFFFFFFFU) return MZ_PARAM_ERROR; stream.next_in = pSource; @@ -1251,6 +1330,10 @@ mz_ulong mz_compressBound(mz_ulong source_len) return mz_deflateBound(NULL, source_len); } +#endif /*#ifndef MINIZ_NO_DEFLATE_APIS*/ + +#ifndef MINIZ_NO_INFLATE_APIS + typedef struct { tinfl_decompressor m_decomp; @@ -1450,19 +1533,18 @@ int mz_inflateEnd(mz_streamp pStream) } return MZ_OK; } - -int mz_uncompress(unsigned char *pDest, mz_ulong *pDest_len, const unsigned char *pSource, mz_ulong source_len) +int mz_uncompress2(unsigned char *pDest, mz_ulong *pDest_len, const unsigned char *pSource, mz_ulong *pSource_len) { mz_stream stream; int status; memset(&stream, 0, sizeof(stream)); /* In case mz_ulong is 64-bits (argh I hate longs). */ - if ((source_len | *pDest_len) > 0xFFFFFFFFU) + if ((mz_uint64)(*pSource_len | *pDest_len) > 0xFFFFFFFFU) return MZ_PARAM_ERROR; stream.next_in = pSource; - stream.avail_in = (mz_uint32)source_len; + stream.avail_in = (mz_uint32)*pSource_len; stream.next_out = pDest; stream.avail_out = (mz_uint32)*pDest_len; @@ -1471,6 +1553,7 @@ int mz_uncompress(unsigned char *pDest, mz_ulong *pDest_len, const unsigned char return status; status = mz_inflate(&stream, MZ_FINISH); + *pSource_len = *pSource_len - stream.avail_in; if (status != MZ_STREAM_END) { mz_inflateEnd(&stream); @@ -1481,6 +1564,13 @@ int mz_uncompress(unsigned char *pDest, mz_ulong *pDest_len, const unsigned char return mz_inflateEnd(&stream); } +int mz_uncompress(unsigned char *pDest, mz_ulong *pDest_len, const unsigned char *pSource, mz_ulong source_len) +{ + return mz_uncompress2(pDest, pDest_len, pSource, &source_len); +} + +#endif /*#ifndef MINIZ_NO_INFLATE_APIS*/ + const char *mz_error(int err) { static struct @@ -1558,6 +1648,7 @@ const char *mz_error(int err) +#ifndef MINIZ_NO_DEFLATE_APIS #ifdef __cplusplus extern "C" { @@ -1637,7 +1728,7 @@ static tdefl_sym_freq *tdefl_radix_sort_syms(mz_uint num_syms, tdefl_sym_freq *p { mz_uint32 total_passes = 2, pass_shift, pass, i, hist[256 * 2]; tdefl_sym_freq *pCur_syms = pSyms0, *pNew_syms = pSyms1; - MZ_CLEAR_OBJ(hist); + MZ_CLEAR_ARR(hist); for (i = 0; i < num_syms; i++) { mz_uint freq = pSyms0[i].m_key; @@ -1755,7 +1846,7 @@ static void tdefl_optimize_huffman_table(tdefl_compressor *d, int table_num, int { int i, j, l, num_codes[1 + TDEFL_MAX_SUPPORTED_HUFF_CODESIZE]; mz_uint next_code[TDEFL_MAX_SUPPORTED_HUFF_CODESIZE + 1]; - MZ_CLEAR_OBJ(num_codes); + MZ_CLEAR_ARR(num_codes); if (static_table) { for (i = 0; i < table_len; i++) @@ -1781,8 +1872,8 @@ static void tdefl_optimize_huffman_table(tdefl_compressor *d, int table_num, int tdefl_huffman_enforce_max_code_size(num_codes, num_used_syms, code_size_limit); - MZ_CLEAR_OBJ(d->m_huff_code_sizes[table_num]); - MZ_CLEAR_OBJ(d->m_huff_codes[table_num]); + MZ_CLEAR_ARR(d->m_huff_code_sizes[table_num]); + MZ_CLEAR_ARR(d->m_huff_codes[table_num]); for (i = 1, j = num_used_syms; i <= code_size_limit; i++) for (l = num_codes[i]; l > 0; l--) d->m_huff_code_sizes[table_num][pSyms[--j].m_sym_index] = (mz_uint8)(i); @@ -1868,7 +1959,7 @@ static void tdefl_optimize_huffman_table(tdefl_compressor *d, int table_num, int } \ } -static mz_uint8 s_tdefl_packed_code_size_syms_swizzle[] = { 16, 17, 18, 0, 8, 7, 9, 6, 10, 5, 11, 4, 12, 3, 13, 2, 14, 1, 15 }; +static const mz_uint8 s_tdefl_packed_code_size_syms_swizzle[] = { 16, 17, 18, 0, 8, 7, 9, 6, 10, 5, 11, 4, 12, 3, 13, 2, 14, 1, 15 }; static void tdefl_start_dynamic_block(tdefl_compressor *d) { @@ -2006,7 +2097,8 @@ static mz_bool tdefl_compress_lz_codes(tdefl_compressor *d) if (flags & 1) { mz_uint s0, s1, n0, n1, sym, num_extra_bits; - mz_uint match_len = pLZ_codes[0], match_dist = *(const mz_uint16 *)(pLZ_codes + 1); + mz_uint match_len = pLZ_codes[0]; + mz_uint match_dist = (pLZ_codes[1] | (pLZ_codes[2] << 8)); pLZ_codes += 3; MZ_ASSERT(d->m_huff_code_sizes[0][s_tdefl_len_sym[match_len]]); @@ -2051,7 +2143,7 @@ static mz_bool tdefl_compress_lz_codes(tdefl_compressor *d) if (pOutput_buf >= d->m_pOutput_buf_end) return MZ_FALSE; - *(mz_uint64 *)pOutput_buf = bit_buffer; + memcpy(pOutput_buf, &bit_buffer, sizeof(mz_uint64)); pOutput_buf += (bits_in >> 3); bit_buffer >>= (bits_in & ~7); bits_in &= 7; @@ -2133,6 +2225,8 @@ static mz_bool tdefl_compress_block(tdefl_compressor *d, mz_bool static_block) return tdefl_compress_lz_codes(d); } +static const mz_uint s_tdefl_num_probes[11]; + static int tdefl_flush_block(tdefl_compressor *d, int flush) { mz_uint saved_bit_buf, saved_bits_in; @@ -2153,8 +2247,27 @@ static int tdefl_flush_block(tdefl_compressor *d, int flush) if ((d->m_flags & TDEFL_WRITE_ZLIB_HEADER) && (!d->m_block_index)) { - TDEFL_PUT_BITS(0x78, 8); - TDEFL_PUT_BITS(0x01, 8); + const mz_uint8 cmf = 0x78; + mz_uint8 flg, flevel = 3; + mz_uint header, i, mz_un = sizeof(s_tdefl_num_probes) / sizeof(mz_uint); + + /* Determine compression level by reversing the process in tdefl_create_comp_flags_from_zip_params() */ + for (i = 0; i < mz_un; i++) + if (s_tdefl_num_probes[i] == (d->m_flags & 0xFFF)) break; + + if (i < 2) + flevel = 0; + else if (i < 6) + flevel = 1; + else if (i == 6) + flevel = 2; + + header = cmf << 8 | (flevel << 6); + header += 31 - (header % 31); + flg = header & 0xFF; + + TDEFL_PUT_BITS(cmf, 8); + TDEFL_PUT_BITS(flg, 8); } TDEFL_PUT_BITS(flush == TDEFL_FINISH, 1); @@ -2607,9 +2720,7 @@ static MZ_FORCEINLINE void tdefl_record_match(tdefl_compressor *d, mz_uint match s0 = s_tdefl_small_dist_sym[match_dist & 511]; s1 = s_tdefl_large_dist_sym[(match_dist >> 8) & 127]; d->m_huff_count[1][(match_dist < 512) ? s0 : s1]++; - - if (match_len >= TDEFL_MIN_MATCH_LEN) - d->m_huff_count[0][s_tdefl_len_sym[match_len - TDEFL_MIN_MATCH_LEN]]++; + d->m_huff_count[0][s_tdefl_len_sym[match_len - TDEFL_MIN_MATCH_LEN]]++; } static mz_bool tdefl_compress_normal(tdefl_compressor *d) @@ -2627,7 +2738,7 @@ static mz_bool tdefl_compress_normal(tdefl_compressor *d) mz_uint dst_pos = (d->m_lookahead_pos + d->m_lookahead_size) & TDEFL_LZ_DICT_SIZE_MASK, ins_pos = d->m_lookahead_pos + d->m_lookahead_size - 2; mz_uint hash = (d->m_dict[ins_pos & TDEFL_LZ_DICT_SIZE_MASK] << TDEFL_LZ_HASH_SHIFT) ^ d->m_dict[(ins_pos + 1) & TDEFL_LZ_DICT_SIZE_MASK]; mz_uint num_bytes_to_process = (mz_uint)MZ_MIN(src_buf_left, TDEFL_MAX_MATCH_LEN - d->m_lookahead_size); - const mz_uint8 *pSrc_end = pSrc + num_bytes_to_process; + const mz_uint8 *pSrc_end = pSrc ? pSrc + num_bytes_to_process : NULL; src_buf_left -= num_bytes_to_process; d->m_lookahead_size += num_bytes_to_process; while (pSrc != pSrc_end) @@ -2837,8 +2948,8 @@ tdefl_status tdefl_compress(tdefl_compressor *d, const void *pIn_buf, size_t *pI d->m_finished = (flush == TDEFL_FINISH); if (flush == TDEFL_FULL_FLUSH) { - MZ_CLEAR_OBJ(d->m_hash); - MZ_CLEAR_OBJ(d->m_next); + MZ_CLEAR_ARR(d->m_hash); + MZ_CLEAR_ARR(d->m_next); d->m_dict_size = 0; } } @@ -2861,11 +2972,12 @@ tdefl_status tdefl_init(tdefl_compressor *d, tdefl_put_buf_func_ptr pPut_buf_fun d->m_greedy_parsing = (flags & TDEFL_GREEDY_PARSING_FLAG) != 0; d->m_max_probes[1] = 1 + (((flags & 0xFFF) >> 2) + 2) / 3; if (!(flags & TDEFL_NONDETERMINISTIC_PARSING_FLAG)) - MZ_CLEAR_OBJ(d->m_hash); + MZ_CLEAR_ARR(d->m_hash); d->m_lookahead_pos = d->m_lookahead_size = d->m_dict_size = d->m_total_lz_bytes = d->m_lz_code_buf_dict_pos = d->m_bits_in = 0; d->m_output_flush_ofs = d->m_output_flush_remaining = d->m_finished = d->m_block_index = d->m_bit_buffer = d->m_wants_to_finish = 0; d->m_pLZ_code_buf = d->m_lz_code_buf + 1; d->m_pLZ_flags = d->m_lz_code_buf; + *d->m_pLZ_flags = 0; d->m_num_flags_left = 8; d->m_pOutput_buf = d->m_output_buf; d->m_pOutput_buf_end = d->m_output_buf; @@ -2881,7 +2993,7 @@ tdefl_status tdefl_init(tdefl_compressor *d, tdefl_put_buf_func_ptr pPut_buf_fun d->m_src_buf_left = 0; d->m_out_buf_ofs = 0; if (!(flags & TDEFL_NONDETERMINISTIC_PARSING_FLAG)) - MZ_CLEAR_OBJ(d->m_dict); + MZ_CLEAR_ARR(d->m_dict); memset(&d->m_huff_count[0][0], 0, sizeof(d->m_huff_count[0][0]) * TDEFL_MAX_HUFF_SYMBOLS_0); memset(&d->m_huff_count[1][0], 0, sizeof(d->m_huff_count[1][0]) * TDEFL_MAX_HUFF_SYMBOLS_1); return TDEFL_STATUS_OKAY; @@ -3091,7 +3203,7 @@ void *tdefl_write_image_to_png_file_in_memory(const void *pImage, int w, int h, /* Allocate the tdefl_compressor and tinfl_decompressor structures in C so that */ /* non-C language bindings to tdefL_ and tinfl_ API don't need to worry about */ /* structure size and allocation mechanism. */ -tdefl_compressor *tdefl_compressor_alloc( void ) +tdefl_compressor *tdefl_compressor_alloc(void) { return (tdefl_compressor *)MZ_MALLOC(sizeof(tdefl_compressor)); } @@ -3109,6 +3221,8 @@ void tdefl_compressor_free(tdefl_compressor *pComp) #ifdef __cplusplus } #endif + +#endif /*#ifndef MINIZ_NO_DEFLATE_APIS*/ /************************************************************************** * * Copyright 2013-2014 RAD Game Tools and Valve Software @@ -3137,6 +3251,8 @@ void tdefl_compressor_free(tdefl_compressor *pComp) +#ifndef MINIZ_NO_INFLATE_APIS + #ifdef __cplusplus extern "C" { #endif @@ -3217,10 +3333,10 @@ extern "C" { /* It reads just enough bytes from the input stream that are needed to decode the next Huffman code (and absolutely no more). It works by trying to fully decode a */ /* Huffman code by using whatever bits are currently present in the bit buffer. If this fails, it reads another byte, and tries again until it succeeds or until the */ /* bit buffer contains >=15 bits (deflate's max. Huffman code size). */ -#define TINFL_HUFF_BITBUF_FILL(state_index, pHuff) \ +#define TINFL_HUFF_BITBUF_FILL(state_index, pLookUp, pTree) \ do \ { \ - temp = (pHuff)->m_look_up[bit_buf & (TINFL_FAST_LOOKUP_SIZE - 1)]; \ + temp = pLookUp[bit_buf & (TINFL_FAST_LOOKUP_SIZE - 1)]; \ if (temp >= 0) \ { \ code_len = temp >> 9; \ @@ -3232,7 +3348,7 @@ extern "C" { code_len = TINFL_FAST_LOOKUP_BITS; \ do \ { \ - temp = (pHuff)->m_tree[~temp + ((bit_buf >> code_len++) & 1)]; \ + temp = pTree[~temp + ((bit_buf >> code_len++) & 1)]; \ } while ((temp < 0) && (num_bits >= (code_len + 1))); \ if (temp >= 0) \ break; \ @@ -3248,7 +3364,7 @@ extern "C" { /* The slow path is only executed at the very end of the input buffer. */ /* v1.16: The original macro handled the case at the very end of the passed-in input buffer, but we also need to handle the case where the user passes in 1+zillion bytes */ /* following the deflate data and our non-conservative read-ahead path won't kick in here on this code. This is much trickier. */ -#define TINFL_HUFF_DECODE(state_index, sym, pHuff) \ +#define TINFL_HUFF_DECODE(state_index, sym, pLookUp, pTree) \ do \ { \ int temp; \ @@ -3257,7 +3373,7 @@ extern "C" { { \ if ((pIn_buf_end - pIn_buf_cur) < 2) \ { \ - TINFL_HUFF_BITBUF_FILL(state_index, pHuff); \ + TINFL_HUFF_BITBUF_FILL(state_index, pLookUp, pTree); \ } \ else \ { \ @@ -3266,14 +3382,14 @@ extern "C" { num_bits += 16; \ } \ } \ - if ((temp = (pHuff)->m_look_up[bit_buf & (TINFL_FAST_LOOKUP_SIZE - 1)]) >= 0) \ + if ((temp = pLookUp[bit_buf & (TINFL_FAST_LOOKUP_SIZE - 1)]) >= 0) \ code_len = temp >> 9, temp &= 511; \ else \ { \ code_len = TINFL_FAST_LOOKUP_BITS; \ do \ { \ - temp = (pHuff)->m_tree[~temp + ((bit_buf >> code_len++) & 1)]; \ + temp = pTree[~temp + ((bit_buf >> code_len++) & 1)]; \ } while (temp < 0); \ } \ sym = temp; \ @@ -3282,20 +3398,33 @@ extern "C" { } \ MZ_MACRO_END +static void tinfl_clear_tree(tinfl_decompressor *r) +{ + if (r->m_type == 0) + MZ_CLEAR_ARR(r->m_tree_0); + else if (r->m_type == 1) + MZ_CLEAR_ARR(r->m_tree_1); + else + MZ_CLEAR_ARR(r->m_tree_2); +} + tinfl_status tinfl_decompress(tinfl_decompressor *r, const mz_uint8 *pIn_buf_next, size_t *pIn_buf_size, mz_uint8 *pOut_buf_start, mz_uint8 *pOut_buf_next, size_t *pOut_buf_size, const mz_uint32 decomp_flags) { - static const int s_length_base[31] = { 3, 4, 5, 6, 7, 8, 9, 10, 11, 13, 15, 17, 19, 23, 27, 31, 35, 43, 51, 59, 67, 83, 99, 115, 131, 163, 195, 227, 258, 0, 0 }; - static const int s_length_extra[31] = { 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 2, 2, 2, 2, 3, 3, 3, 3, 4, 4, 4, 4, 5, 5, 5, 5, 0, 0, 0 }; - static const int s_dist_base[32] = { 1, 2, 3, 4, 5, 7, 9, 13, 17, 25, 33, 49, 65, 97, 129, 193, 257, 385, 513, 769, 1025, 1537, 2049, 3073, 4097, 6145, 8193, 12289, 16385, 24577, 0, 0 }; - static const int s_dist_extra[32] = { 0, 0, 0, 0, 1, 1, 2, 2, 3, 3, 4, 4, 5, 5, 6, 6, 7, 7, 8, 8, 9, 9, 10, 10, 11, 11, 12, 12, 13, 13 }; + static const mz_uint16 s_length_base[31] = { 3, 4, 5, 6, 7, 8, 9, 10, 11, 13, 15, 17, 19, 23, 27, 31, 35, 43, 51, 59, 67, 83, 99, 115, 131, 163, 195, 227, 258, 0, 0 }; + static const mz_uint8 s_length_extra[31] = { 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 2, 2, 2, 2, 3, 3, 3, 3, 4, 4, 4, 4, 5, 5, 5, 5, 0, 0, 0 }; + static const mz_uint16 s_dist_base[32] = { 1, 2, 3, 4, 5, 7, 9, 13, 17, 25, 33, 49, 65, 97, 129, 193, 257, 385, 513, 769, 1025, 1537, 2049, 3073, 4097, 6145, 8193, 12289, 16385, 24577, 0, 0 }; + static const mz_uint8 s_dist_extra[32] = { 0, 0, 0, 0, 1, 1, 2, 2, 3, 3, 4, 4, 5, 5, 6, 6, 7, 7, 8, 8, 9, 9, 10, 10, 11, 11, 12, 12, 13, 13 }; static const mz_uint8 s_length_dezigzag[19] = { 16, 17, 18, 0, 8, 7, 9, 6, 10, 5, 11, 4, 12, 3, 13, 2, 14, 1, 15 }; - static const int s_min_table_sizes[3] = { 257, 1, 4 }; + static const mz_uint16 s_min_table_sizes[3] = { 257, 1, 4 }; + + mz_int16 *pTrees[3]; + mz_uint8 *pCode_sizes[3]; tinfl_status status = TINFL_STATUS_FAILED; mz_uint32 num_bits, dist, counter, num_extra; tinfl_bit_buf_t bit_buf; const mz_uint8 *pIn_buf_cur = pIn_buf_next, *const pIn_buf_end = pIn_buf_next + *pIn_buf_size; - mz_uint8 *pOut_buf_cur = pOut_buf_next, *const pOut_buf_end = pOut_buf_next + *pOut_buf_size; + mz_uint8 *pOut_buf_cur = pOut_buf_next, *const pOut_buf_end = pOut_buf_next ? pOut_buf_next + *pOut_buf_size : NULL; size_t out_buf_size_mask = (decomp_flags & TINFL_FLAG_USING_NON_WRAPPING_OUTPUT_BUF) ? (size_t)-1 : ((pOut_buf_next - pOut_buf_start) + *pOut_buf_size) - 1, dist_from_out_buf_start; /* Ensure the output buffer's size is a power of 2, unless the output buffer is large enough to hold the entire output file (in which case it doesn't matter). */ @@ -3305,6 +3434,13 @@ tinfl_status tinfl_decompress(tinfl_decompressor *r, const mz_uint8 *pIn_buf_nex return TINFL_STATUS_BAD_PARAM; } + pTrees[0] = r->m_tree_0; + pTrees[1] = r->m_tree_1; + pTrees[2] = r->m_tree_2; + pCode_sizes[0] = r->m_code_size_0; + pCode_sizes[1] = r->m_code_size_1; + pCode_sizes[2] = r->m_code_size_2; + num_bits = r->m_num_bits; bit_buf = r->m_bit_buf; dist = r->m_dist; @@ -3321,7 +3457,7 @@ tinfl_status tinfl_decompress(tinfl_decompressor *r, const mz_uint8 *pIn_buf_nex TINFL_GET_BYTE(2, r->m_zhdr1); counter = (((r->m_zhdr0 * 256 + r->m_zhdr1) % 31 != 0) || (r->m_zhdr1 & 32) || ((r->m_zhdr0 & 15) != 8)); if (!(decomp_flags & TINFL_FLAG_USING_NON_WRAPPING_OUTPUT_BUF)) - counter |= (((1U << (8U + (r->m_zhdr0 >> 4))) > 32768U) || ((out_buf_size_mask + 1) < (size_t)(1U << (8U + (r->m_zhdr0 >> 4))))); + counter |= (((1U << (8U + (r->m_zhdr0 >> 4))) > 32768U) || ((out_buf_size_mask + 1) < (size_t)((size_t)1 << (8U + (r->m_zhdr0 >> 4))))); if (counter) { TINFL_CR_RETURN_FOREVER(36, TINFL_STATUS_FAILED); @@ -3382,11 +3518,11 @@ tinfl_status tinfl_decompress(tinfl_decompressor *r, const mz_uint8 *pIn_buf_nex { if (r->m_type == 1) { - mz_uint8 *p = r->m_tables[0].m_code_size; + mz_uint8 *p = r->m_code_size_0; mz_uint i; r->m_table_sizes[0] = 288; r->m_table_sizes[1] = 32; - TINFL_MEMSET(r->m_tables[1].m_code_size, 5, 32); + TINFL_MEMSET(r->m_code_size_1, 5, 32); for (i = 0; i <= 143; ++i) *p++ = 8; for (; i <= 255; ++i) @@ -3403,26 +3539,30 @@ tinfl_status tinfl_decompress(tinfl_decompressor *r, const mz_uint8 *pIn_buf_nex TINFL_GET_BITS(11, r->m_table_sizes[counter], "\05\05\04"[counter]); r->m_table_sizes[counter] += s_min_table_sizes[counter]; } - MZ_CLEAR_OBJ(r->m_tables[2].m_code_size); + MZ_CLEAR_ARR(r->m_code_size_2); for (counter = 0; counter < r->m_table_sizes[2]; counter++) { mz_uint s; TINFL_GET_BITS(14, s, 3); - r->m_tables[2].m_code_size[s_length_dezigzag[counter]] = (mz_uint8)s; + r->m_code_size_2[s_length_dezigzag[counter]] = (mz_uint8)s; } r->m_table_sizes[2] = 19; } for (; (int)r->m_type >= 0; r->m_type--) { int tree_next, tree_cur; - tinfl_huff_table *pTable; + mz_int16 *pLookUp; + mz_int16 *pTree; + mz_uint8 *pCode_size; mz_uint i, j, used_syms, total, sym_index, next_code[17], total_syms[16]; - pTable = &r->m_tables[r->m_type]; - MZ_CLEAR_OBJ(total_syms); - MZ_CLEAR_OBJ(pTable->m_look_up); - MZ_CLEAR_OBJ(pTable->m_tree); + pLookUp = r->m_look_up[r->m_type]; + pTree = pTrees[r->m_type]; + pCode_size = pCode_sizes[r->m_type]; + MZ_CLEAR_ARR(total_syms); + TINFL_MEMSET(pLookUp, 0, sizeof(r->m_look_up[0])); + tinfl_clear_tree(r); for (i = 0; i < r->m_table_sizes[r->m_type]; ++i) - total_syms[pTable->m_code_size[i]]++; + total_syms[pCode_size[i]]++; used_syms = 0, total = 0; next_code[0] = next_code[1] = 0; for (i = 1; i <= 15; ++i) @@ -3436,7 +3576,7 @@ tinfl_status tinfl_decompress(tinfl_decompressor *r, const mz_uint8 *pIn_buf_nex } for (tree_next = -1, sym_index = 0; sym_index < r->m_table_sizes[r->m_type]; ++sym_index) { - mz_uint rev_code = 0, l, cur_code, code_size = pTable->m_code_size[sym_index]; + mz_uint rev_code = 0, l, cur_code, code_size = pCode_size[sym_index]; if (!code_size) continue; cur_code = next_code[code_size]++; @@ -3447,14 +3587,14 @@ tinfl_status tinfl_decompress(tinfl_decompressor *r, const mz_uint8 *pIn_buf_nex mz_int16 k = (mz_int16)((code_size << 9) | sym_index); while (rev_code < TINFL_FAST_LOOKUP_SIZE) { - pTable->m_look_up[rev_code] = k; + pLookUp[rev_code] = k; rev_code += (1 << code_size); } continue; } - if (0 == (tree_cur = pTable->m_look_up[rev_code & (TINFL_FAST_LOOKUP_SIZE - 1)])) + if (0 == (tree_cur = pLookUp[rev_code & (TINFL_FAST_LOOKUP_SIZE - 1)])) { - pTable->m_look_up[rev_code & (TINFL_FAST_LOOKUP_SIZE - 1)] = (mz_int16)tree_next; + pLookUp[rev_code & (TINFL_FAST_LOOKUP_SIZE - 1)] = (mz_int16)tree_next; tree_cur = tree_next; tree_next -= 2; } @@ -3462,24 +3602,24 @@ tinfl_status tinfl_decompress(tinfl_decompressor *r, const mz_uint8 *pIn_buf_nex for (j = code_size; j > (TINFL_FAST_LOOKUP_BITS + 1); j--) { tree_cur -= ((rev_code >>= 1) & 1); - if (!pTable->m_tree[-tree_cur - 1]) + if (!pTree[-tree_cur - 1]) { - pTable->m_tree[-tree_cur - 1] = (mz_int16)tree_next; + pTree[-tree_cur - 1] = (mz_int16)tree_next; tree_cur = tree_next; tree_next -= 2; } else - tree_cur = pTable->m_tree[-tree_cur - 1]; + tree_cur = pTree[-tree_cur - 1]; } tree_cur -= ((rev_code >>= 1) & 1); - pTable->m_tree[-tree_cur - 1] = (mz_int16)sym_index; + pTree[-tree_cur - 1] = (mz_int16)sym_index; } if (r->m_type == 2) { for (counter = 0; counter < (r->m_table_sizes[0] + r->m_table_sizes[1]);) { mz_uint s; - TINFL_HUFF_DECODE(16, dist, &r->m_tables[2]); + TINFL_HUFF_DECODE(16, dist, r->m_look_up[2], r->m_tree_2); if (dist < 16) { r->m_len_codes[counter++] = (mz_uint8)dist; @@ -3499,8 +3639,8 @@ tinfl_status tinfl_decompress(tinfl_decompressor *r, const mz_uint8 *pIn_buf_nex { TINFL_CR_RETURN_FOREVER(21, TINFL_STATUS_FAILED); } - TINFL_MEMCPY(r->m_tables[0].m_code_size, r->m_len_codes, r->m_table_sizes[0]); - TINFL_MEMCPY(r->m_tables[1].m_code_size, r->m_len_codes + r->m_table_sizes[0], r->m_table_sizes[1]); + TINFL_MEMCPY(r->m_code_size_0, r->m_len_codes, r->m_table_sizes[0]); + TINFL_MEMCPY(r->m_code_size_1, r->m_len_codes + r->m_table_sizes[0], r->m_table_sizes[1]); } } for (;;) @@ -3510,7 +3650,7 @@ tinfl_status tinfl_decompress(tinfl_decompressor *r, const mz_uint8 *pIn_buf_nex { if (((pIn_buf_end - pIn_buf_cur) < 4) || ((pOut_buf_end - pOut_buf_cur) < 2)) { - TINFL_HUFF_DECODE(23, counter, &r->m_tables[0]); + TINFL_HUFF_DECODE(23, counter, r->m_look_up[0], r->m_tree_0); if (counter >= 256) break; while (pOut_buf_cur >= pOut_buf_end) @@ -3538,14 +3678,14 @@ tinfl_status tinfl_decompress(tinfl_decompressor *r, const mz_uint8 *pIn_buf_nex num_bits += 16; } #endif - if ((sym2 = r->m_tables[0].m_look_up[bit_buf & (TINFL_FAST_LOOKUP_SIZE - 1)]) >= 0) + if ((sym2 = r->m_look_up[0][bit_buf & (TINFL_FAST_LOOKUP_SIZE - 1)]) >= 0) code_len = sym2 >> 9; else { code_len = TINFL_FAST_LOOKUP_BITS; do { - sym2 = r->m_tables[0].m_tree[~sym2 + ((bit_buf >> code_len++) & 1)]; + sym2 = r->m_tree_0[~sym2 + ((bit_buf >> code_len++) & 1)]; } while (sym2 < 0); } counter = sym2; @@ -3562,14 +3702,14 @@ tinfl_status tinfl_decompress(tinfl_decompressor *r, const mz_uint8 *pIn_buf_nex num_bits += 16; } #endif - if ((sym2 = r->m_tables[0].m_look_up[bit_buf & (TINFL_FAST_LOOKUP_SIZE - 1)]) >= 0) + if ((sym2 = r->m_look_up[0][bit_buf & (TINFL_FAST_LOOKUP_SIZE - 1)]) >= 0) code_len = sym2 >> 9; else { code_len = TINFL_FAST_LOOKUP_BITS; do { - sym2 = r->m_tables[0].m_tree[~sym2 + ((bit_buf >> code_len++) & 1)]; + sym2 = r->m_tree_0[~sym2 + ((bit_buf >> code_len++) & 1)]; } while (sym2 < 0); } bit_buf >>= code_len; @@ -3598,7 +3738,7 @@ tinfl_status tinfl_decompress(tinfl_decompressor *r, const mz_uint8 *pIn_buf_nex counter += extra_bits; } - TINFL_HUFF_DECODE(26, dist, &r->m_tables[1]); + TINFL_HUFF_DECODE(26, dist, r->m_look_up[1], r->m_tree_1); num_extra = s_dist_extra[dist]; dist = s_dist_base[dist]; if (num_extra) @@ -3609,7 +3749,7 @@ tinfl_status tinfl_decompress(tinfl_decompressor *r, const mz_uint8 *pIn_buf_nex } dist_from_out_buf_start = pOut_buf_cur - pOut_buf_start; - if ((dist > dist_from_out_buf_start) && (decomp_flags & TINFL_FLAG_USING_NON_WRAPPING_OUTPUT_BUF)) + if ((dist == 0 || dist > dist_from_out_buf_start || dist_from_out_buf_start == 0) && (decomp_flags & TINFL_FLAG_USING_NON_WRAPPING_OUTPUT_BUF)) { TINFL_CR_RETURN_FOREVER(37, TINFL_STATUS_FAILED); } @@ -3683,7 +3823,7 @@ tinfl_status tinfl_decompress(tinfl_decompressor *r, const mz_uint8 *pIn_buf_nex --pIn_buf_cur; num_bits -= 8; } - bit_buf &= (tinfl_bit_buf_t)((((mz_uint64)1) << num_bits) - (mz_uint64)1); + bit_buf &= ~(~(tinfl_bit_buf_t)0 << num_bits); MZ_ASSERT(!num_bits); /* if this assert fires then we've read beyond the end of non-deflate/zlib streams with following data (such as gzip streams). */ if (decomp_flags & TINFL_FLAG_PARSE_ZLIB_HEADER) @@ -3715,7 +3855,7 @@ common_exit: } } r->m_num_bits = num_bits; - r->m_bit_buf = bit_buf & (tinfl_bit_buf_t)((((mz_uint64)1) << num_bits) - (mz_uint64)1); + r->m_bit_buf = bit_buf & ~(~(tinfl_bit_buf_t)0 << num_bits); r->m_dist = dist; r->m_counter = counter; r->m_num_extra = num_extra; @@ -3810,6 +3950,7 @@ int tinfl_decompress_mem_to_callback(const void *pIn_buf, size_t *pIn_buf_size, size_t in_buf_ofs = 0, dict_ofs = 0; if (!pDict) return TINFL_STATUS_FAILED; + memset(pDict,0,TINFL_LZ_DICT_SIZE); tinfl_init(&decomp); for (;;) { @@ -3832,7 +3973,7 @@ int tinfl_decompress_mem_to_callback(const void *pIn_buf, size_t *pIn_buf_size, } #ifndef MINIZ_NO_MALLOC -tinfl_decompressor *tinfl_decompressor_alloc( void ) +tinfl_decompressor *tinfl_decompressor_alloc(void) { tinfl_decompressor *pDecomp = (tinfl_decompressor *)MZ_MALLOC(sizeof(tinfl_decompressor)); if (pDecomp) @@ -3850,4 +3991,5 @@ void tinfl_decompressor_free(tinfl_decompressor *pDecomp) } #endif -#endif // MINIZ_HEADER_FILE_ONLY +#endif /*#ifndef MINIZ_NO_INFLATE_APIS*/ +#endif /*#ifndef MINIZ_HEADER_FILE_ONLY*/ From 8cbe0e5b30865ca7b29c0d3c0bc7f786e02364b6 Mon Sep 17 00:00:00 2001 From: SNMetamorph <25657591+SNMetamorph@users.noreply.github.com> Date: Tue, 8 Nov 2022 04:01:30 +0400 Subject: [PATCH 154/490] engine: client: vgui: added VGui_UpdateInternalCursorState function --- engine/client/vgui/vgui_draw.c | 8 ++++---- engine/client/vgui/vgui_draw.h | 1 + 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/engine/client/vgui/vgui_draw.c b/engine/client/vgui/vgui_draw.c index cd134e24..4d732049 100644 --- a/engine/client/vgui/vgui_draw.c +++ b/engine/client/vgui/vgui_draw.c @@ -51,11 +51,7 @@ void GAME_EXPORT VGUI_GetMousePos( int *_x, int *_y ) void GAME_EXPORT VGUI_CursorSelect( VGUI_DefaultCursor cursor ) { if( s_currentCursor != cursor ) - { Platform_SetCursorType( cursor ); - - s_currentCursor = cursor; - } } byte GAME_EXPORT VGUI_GetColor( int i, int j) @@ -488,6 +484,10 @@ void VGui_RunFrame( void ) //stub } +void VGui_UpdateInternalCursorState( VGUI_DefaultCursor cursorType ) +{ + s_currentCursor = cursorType; +} void *GAME_EXPORT VGui_GetPanel( void ) { diff --git a/engine/client/vgui/vgui_draw.h b/engine/client/vgui/vgui_draw.h index 4119774c..2cdaf1af 100644 --- a/engine/client/vgui/vgui_draw.h +++ b/engine/client/vgui/vgui_draw.h @@ -36,6 +36,7 @@ void VGui_MouseMove( int x, int y ); qboolean VGui_IsActive( void ); void *VGui_GetPanel( void ); void VGui_ReportTextInput( const char *text ); +void VGui_UpdateInternalCursorState( VGUI_DefaultCursor cursorType ); #ifdef __cplusplus } #endif From b35cf6e30ca4244286df690fb0a6d0ddf9be80cd Mon Sep 17 00:00:00 2001 From: SNMetamorph <25657591+SNMetamorph@users.noreply.github.com> Date: Tue, 8 Nov 2022 04:03:50 +0400 Subject: [PATCH 155/490] engine: platform: update VGUI cursor state in Platform_SetCursorType --- engine/platform/sdl/in_sdl.c | 1 + 1 file changed, 1 insertion(+) diff --git a/engine/platform/sdl/in_sdl.c b/engine/platform/sdl/in_sdl.c index 36b365b0..65282890 100644 --- a/engine/platform/sdl/in_sdl.c +++ b/engine/platform/sdl/in_sdl.c @@ -335,6 +335,7 @@ void Platform_SetCursorType( VGUI_DefaultCursor type ) return; host.mouse_visible = visible; + VGui_UpdateInternalCursorState( type ); #if SDL_VERSION_ATLEAST( 2, 0, 0 ) if( host.mouse_visible ) From d1309c3aeb17bb52ca01902d36cd91095d91cadd Mon Sep 17 00:00:00 2001 From: SNMetamorph <25657591+SNMetamorph@users.noreply.github.com> Date: Sat, 5 Nov 2022 06:09:54 +0400 Subject: [PATCH 156/490] engine: common: backported "set" command from old engine --- engine/common/cvar.c | 163 ++++++++++++++++++++++++++++++++++++++++++- engine/common/cvar.h | 1 + 2 files changed, 162 insertions(+), 2 deletions(-) diff --git a/engine/common/cvar.c b/engine/common/cvar.c index 752a6846..6a8e5c0d 100644 --- a/engine/common/cvar.c +++ b/engine/common/cvar.c @@ -220,6 +220,24 @@ const char *Cvar_ValidateString( convar_t *var, const char *value ) return pszValue; } +/* +============ +Cvar_ValidateVarName +============ +*/ +static qboolean Cvar_ValidateVarName( const char *s, qboolean isvalue ) +{ + if( !s ) + return false; + if( Q_strchr( s, '\\' ) && !isvalue ) + return false; + if( Q_strchr( s, '\"' )) + return false; + if( Q_strchr( s, ';' ) && !isvalue ) + return false; + return true; +} + /* ============ Cvar_UnlinkVar @@ -495,6 +513,113 @@ void Cvar_RegisterVariable( convar_t *var ) #endif } +/* +============ +Cvar_Set2 +============ +*/ +static convar_t *Cvar_Set2( const char *var_name, const char *value ) +{ + convar_t *var; + const char *pszValue; + qboolean dll_variable = false; + qboolean force = false; + + if( !Cvar_ValidateVarName( var_name, false )) + { + Con_DPrintf( S_ERROR "Invalid cvar name string: %s\n", var_name ); + return NULL; + } + + var = Cvar_FindVar( var_name ); + if( !var ) + { + // if cvar not found, create it + return Cvar_Get( var_name, value, FCVAR_USER_CREATED, NULL ); + } + else + { + if( !Cmd_CurrentCommandIsPrivileged( )) + { + if( FBitSet( var->flags, FCVAR_PRIVILEGED )) + { + Con_Printf( "%s is priveleged.\n", var->name ); + return var; + } + + if( cl_filterstuffcmd.value > 0.0f && FBitSet( var->flags, FCVAR_FILTERABLE )) + { + Con_Printf( "%s is filterable.\n", var->name ); + return var; + } + } + } + + // use this check to prevent acessing for unexisting fields + // for cvar_t: latched_string, description, etc + dll_variable = FBitSet( var->flags, FCVAR_EXTDLL ); + + // check value + if( !value ) + { + if( !FBitSet( var->flags, FCVAR_EXTENDED|FCVAR_ALLOCATED )) + { + Con_Printf( "%s has no default value and can't be reset.\n", var->name ); + return var; + } + + if( dll_variable ) + value = "0"; + else + value = var->def_string; // reset to default value + } + + if( !Q_strcmp( value, var->string )) + return var; + + // any latched values not allowed for game cvars + if( dll_variable ) + force = true; + + if( !force ) + { + if( FBitSet( var->flags, FCVAR_READ_ONLY )) + { + Con_Printf( "%s is read-only.\n", var->name ); + return var; + } + + if( FBitSet( var->flags, FCVAR_CHEAT ) && !host.allow_cheats ) + { + Con_Printf( "%s is cheat protected.\n", var->name ); + return var; + } + + // just tell user about deferred changes + if( FBitSet( var->flags, FCVAR_LATCH ) && ( SV_Active() || CL_Active( ))) + Con_Printf( "%s will be changed upon restarting.\n", var->name ); + } + + pszValue = Cvar_ValidateString( var, value ); + + // nothing to change + if( !Q_strcmp( pszValue, var->string )) + return var; + + // fill it cls.userinfo, svs.serverinfo + if( !Cvar_UpdateInfo( var, pszValue, true )) + return var; + + // and finally changed the cvar itself + freestring( var->string ); + var->string = copystring( pszValue ); + var->value = Q_atof( var->string ); + + // tell engine about changes + Cvar_Changed( var ); + return var; +} + /* ============ Cvar_DirectSet @@ -887,6 +1012,40 @@ void Cvar_Toggle_f( void ) Cvar_Set( Cmd_Argv( 1 ), va( "%i", v )); } +/* +============ +Cvar_Set_f + +Allows setting and defining of arbitrary cvars from console, even if they +weren't declared in C code. +============ +*/ +void Cvar_Set_f( void ) +{ + int i, c, l = 0, len; + char combined[MAX_CMD_TOKENS]; + + c = Cmd_Argc(); + if( c < 3 ) + { + Msg( S_USAGE "set \n" ); + return; + } + combined[0] = 0; + + for( i = 2; i < c; i++ ) + { + len = Q_strlen( Cmd_Argv(i) + 1 ); + if( l + len >= MAX_CMD_TOKENS - 2 ) + break; + Q_strcat( combined, Cmd_Argv( i )); + if( i != c-1 ) Q_strcat( combined, " " ); + l += len; + } + + Cvar_Set2( Cmd_Argv( 1 ), combined ); +} + /* ============ Cvar_SetGL_f @@ -999,12 +1158,12 @@ void Cvar_Init( void ) { cvar_vars = NULL; cmd_scripting = Cvar_Get( "cmd_scripting", "0", FCVAR_ARCHIVE|FCVAR_PRIVILEGED, "enable simple condition checking and variable operations" ); - Cvar_RegisterVariable (&host_developer); // early registering for dev + Cvar_RegisterVariable( &host_developer ); // early registering for dev Cvar_RegisterVariable( &cl_filterstuffcmd ); - Cmd_AddRestrictedCommand( "setgl", Cvar_SetGL_f, "change the value of a opengl variable" ); // OBSOLETE Cmd_AddRestrictedCommand( "toggle", Cvar_Toggle_f, "toggles a console variable's values (use for more info)" ); Cmd_AddRestrictedCommand( "reset", Cvar_Reset_f, "reset any type variable to initial value" ); + Cmd_AddCommand( "set", Cvar_Set_f, "create or change the value of a console variable" ); Cmd_AddCommand( "cvarlist", Cvar_List_f, "display all console variables beginning with the specified prefix" ); } diff --git a/engine/common/cvar.h b/engine/common/cvar.h index ad2dbb44..5d5477f7 100644 --- a/engine/common/cvar.h +++ b/engine/common/cvar.h @@ -48,6 +48,7 @@ typedef struct convar_s #define FCVAR_VIDRESTART (1<<20) // recreate the window is cvar with this flag was changed #define FCVAR_TEMPORARY (1<<21) // these cvars holds their values and can be unlink in any time #define FCVAR_MOVEVARS (1<<22) // this cvar is a part of movevars_t struct that shared between client and server +#define FCVAR_USER_CREATED (1<<23) // created by a set command (dll's used) #define CVAR_DEFINE( cv, cvname, cvstr, cvflags, cvdesc ) \ convar_t cv = { (char*)cvname, (char*)cvstr, cvflags, 0.0f, (void *)CVAR_SENTINEL, (char*)cvdesc, NULL } From afa1d429fe5551523cb083568b6faf5d25f3adec Mon Sep 17 00:00:00 2001 From: SNMetamorph <25657591+SNMetamorph@users.noreply.github.com> Date: Sat, 5 Nov 2022 17:24:46 +0400 Subject: [PATCH 157/490] engine: client: backported enttool from old engine --- engine/client/cl_main.c | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/engine/client/cl_main.c b/engine/client/cl_main.c index e6149ff7..9565f2f0 100644 --- a/engine/client/cl_main.c +++ b/engine/client/cl_main.c @@ -2895,6 +2895,12 @@ void CL_InitLocal( void ) Cmd_AddCommand ("god", NULL, "enable godmode" ); Cmd_AddCommand ("fov", NULL, "set client field of view" ); + Cmd_AddRestrictedCommand ("ent_list", NULL, "list entities on server" ); + Cmd_AddRestrictedCommand ("ent_fire", NULL, "fire entity command (be careful)" ); + Cmd_AddRestrictedCommand ("ent_info", NULL, "dump entity information" ); + Cmd_AddRestrictedCommand ("ent_create", NULL, "create entity with specified values (be careful)" ); + Cmd_AddRestrictedCommand ("ent_getvars", NULL, "put parameters of specified entities to client's' ent_last_* cvars" ); + // register our commands Cmd_AddCommand ("pause", NULL, "pause the game (if the server allows pausing)" ); Cmd_AddCommand ("localservers", CL_LocalServers_f, "collect info about local servers" ); From 5d73c6cb84a133b6f62714c381d825058a6eba75 Mon Sep 17 00:00:00 2001 From: SNMetamorph <25657591+SNMetamorph@users.noreply.github.com> Date: Mon, 7 Nov 2022 19:08:38 +0400 Subject: [PATCH 158/490] engine: server: backported enttools from old engine --- engine/server/server.h | 5 + engine/server/sv_client.c | 802 ++++++++++++++++++++++++++++++++++++++ engine/server/sv_game.c | 113 +++--- engine/server/sv_main.c | 6 + 4 files changed, 871 insertions(+), 55 deletions(-) diff --git a/engine/server/server.h b/engine/server/server.h index c9dbc588..90b90b8a 100644 --- a/engine/server/server.h +++ b/engine/server/server.h @@ -435,6 +435,8 @@ extern convar_t sv_consistency; extern convar_t sv_password; extern convar_t sv_uploadmax; extern convar_t sv_trace_messages; +extern convar_t sv_enttools_enable; +extern convar_t sv_enttools_maxfire; extern convar_t deathmatch; extern convar_t hostname; extern convar_t skill; @@ -642,8 +644,11 @@ void SV_RestartAmbientSounds( void ); void SV_RestartDecals( void ); void SV_RestartStaticEnts( void ); int pfnGetCurrentPlayer( void ); +int pfnDropToFloor( edict_t* e ); edict_t *SV_EdictNum( int n ); char *SV_Localinfo( void ); +void SV_SetModel( edict_t *ent, const char *name ); + // // sv_log.c // diff --git a/engine/server/sv_client.c b/engine/server/sv_client.c index cbf5d912..e336f0f3 100644 --- a/engine/server/sv_client.c +++ b/engine/server/sv_client.c @@ -2119,6 +2119,780 @@ static qboolean SV_SendBuildInfo_f( sv_client_t *cl ) return true; } +/* +================== +SV_GetCrossEnt +================== +*/ +static edict_t *SV_GetCrossEnt( edict_t *player ) +{ + edict_t *ent = EDICT_NUM(1); + edict_t *closest = NULL; + float flMaxDot = 0.94; + vec3_t forward; + vec3_t viewPos; + int i; + float maxLen = 1000; + + AngleVectors( player->v.v_angle, forward, NULL, NULL ); + VectorAdd( player->v.origin, player->v.view_ofs, viewPos ); + + // find bmodels by trace + { + trace_t trace; + vec3_t target; + + VectorMA( viewPos, 1000, forward, target ); + trace = SV_Move( viewPos, vec3_origin, vec3_origin, target, 0, player, false ); + closest = trace.ent; + VectorSubtract( viewPos, trace.endpos, target ); + maxLen = VectorLength(target) + 30; + } + + // check untraceable entities + for ( i = 1; i < svgame.numEntities; i++, ent++ ) + { + vec3_t vecLOS; + vec3_t vecOrigin; + float flDot, traceLen; + vec3_t boxSize; + trace_t trace; + vec3_t vecTrace; + + if( ent->free ) + continue; + + if( ent->v.solid == SOLID_BSP || ent->v.movetype == MOVETYPE_PUSHSTEP ) + continue; // bsp models will be found by trace later + + // do not touch following weapons + if( ent->v.movetype == MOVETYPE_FOLLOW ) + continue; + + if( ent == player ) + continue; + + VectorAdd( ent->v.absmin, ent->v.absmax, vecOrigin ); + VectorScale( vecOrigin, 0.5, vecOrigin ); + + VectorSubtract( vecOrigin, viewPos, vecLOS ); + traceLen = VectorLength(vecLOS); + + if( traceLen > maxLen ) + continue; + + VectorCopy( ent->v.size, boxSize); + VectorScale( boxSize, 0.5, boxSize ); + + if ( vecLOS[0] > boxSize[0] ) + vecLOS[0] -= boxSize[0]; + else if ( vecLOS[0] < -boxSize[0] ) + vecLOS[0] += boxSize[0]; + else + vecLOS[0] = 0; + + if ( vecLOS[1] > boxSize[1] ) + vecLOS[1] -= boxSize[1]; + else if ( vecLOS[1] < -boxSize[1] ) + vecLOS[1] += boxSize[1]; + else + vecLOS[1] = 0; + + if ( vecLOS[2] > boxSize[2] ) + vecLOS[2] -= boxSize[2]; + else if ( vecLOS[2] < -boxSize[2] ) + vecLOS[2] += boxSize[2]; + else + vecLOS[2] = 0; + VectorNormalize( vecLOS ); + + flDot = DotProduct (vecLOS , forward); + if ( flDot <= flMaxDot ) + continue; + + trace = SV_Move( viewPos, vec3_origin, vec3_origin, vecOrigin, 0, player, false ); + VectorSubtract( trace.endpos, viewPos, vecTrace ); + if( VectorLength( vecTrace ) + 30 < traceLen ) + continue; + closest = ent, flMaxDot = flDot; + } + + return closest; +} + +/* +================== +SV_EntFindSingle +================== +*/ +static edict_t *SV_EntFindSingle( sv_client_t *cl, const char *pattern ) +{ + edict_t *ent = NULL; + int i = 0; + + if( Q_isdigit( pattern ) ) + { + i = Q_atoi( pattern ); + + if( i >= svgame.numEntities ) + return NULL; + } + else if( !Q_stricmp( pattern, "!cross" ) ) + { + ent = SV_GetCrossEnt( cl->edict ); + + if( !SV_IsValidEdict( ent ) ) + return NULL; + + i = NUM_FOR_EDICT( ent ); + } + else if( pattern[0] == '!' ) // check for correct instance with !(num)_(serial) + { + const char *p = pattern + 1; + i = Q_atoi( p ); + + while( Q_isdigit( p )) p++; + + if( *p++ != '_' ) + return NULL; + + if( i >= svgame.numEntities ) + return NULL; + + ent = EDICT_NUM( i ); + + if( ent->serialnumber != Q_atoi( p ) ) + return NULL; + } + else + { + for( i = svgame.globals->maxClients + 1; i < svgame.numEntities; i++ ) + { + ent = EDICT_NUM( i ); + + if( !SV_IsValidEdict( ent ) ) + continue; + + if( Q_stricmpext( pattern, STRING( ent->v.targetname ) ) ) + break; + } + } + + ent = EDICT_NUM( i ); + + if( !SV_IsValidEdict( ent ) ) + return NULL; + + return ent; +} + +/* +=============== +SV_EntList_f + +Print list of entities to client +=============== +*/ +static qboolean SV_EntList_f( sv_client_t *cl ) +{ + vec3_t borigin; + edict_t *ent = NULL; + int i; + + for( i = 0; i < svgame.numEntities; i++ ) + { + ent = EDICT_NUM( i ); + if( !SV_IsValidEdict( ent )) + continue; + + // filter by string + if( Cmd_Argc() > 1 ) + { + if( !Q_stricmpext( Cmd_Argv( 1 ), STRING( ent->v.classname ) ) && !Q_stricmpext( Cmd_Argv( 1 ), STRING( ent->v.targetname ) ) ) + continue; + } + + VectorAdd( ent->v.absmin, ent->v.absmax, borigin ); + VectorScale( borigin, 0.5, borigin ); + + SV_ClientPrintf( cl, "%5i origin: %.f %.f %.f", i, ent->v.origin[0], ent->v.origin[1], ent->v.origin[2] ); + SV_ClientPrintf( cl, "%5i borigin: %.f %.f %.f", i, borigin[0], borigin[1], borigin[2] ); + + if( ent->v.classname ) + SV_ClientPrintf( cl, ", class: %s", STRING( ent->v.classname )); + + if( ent->v.globalname ) + SV_ClientPrintf( cl, ", global: %s", STRING( ent->v.globalname )); + + if( ent->v.targetname ) + SV_ClientPrintf( cl, ", name: %s", STRING( ent->v.targetname )); + + if( ent->v.target ) + SV_ClientPrintf( cl, ", target: %s", STRING( ent->v.target )); + + if( ent->v.model ) + SV_ClientPrintf( cl, ", model: %s", STRING( ent->v.model )); + + SV_ClientPrintf( cl, "\n" ); + } + return true; +} + +/* +=============== +SV_EntInfo_f + +Print specified entity information to client +=============== +*/ +static qboolean SV_EntInfo_f( sv_client_t *cl ) +{ + edict_t *ent = NULL; + vec3_t borigin; + + if( Cmd_Argc() != 2 ) + { + SV_ClientPrintf( cl, "Use ent_info \n" ); + return false; + } + + ent = SV_EntFindSingle( cl, Cmd_Argv( 1 ) ); + + if( !SV_IsValidEdict( ent )) + return false; + + VectorAdd( ent->v.absmin, ent->v.absmax, borigin ); + VectorScale( borigin, 0.5, borigin ); + + SV_ClientPrintf( cl, "origin: %.f %.f %.f\n", ent->v.origin[0], ent->v.origin[1], ent->v.origin[2] ); + SV_ClientPrintf( cl, "angles: %.f %.f %.f\n", ent->v.angles[0], ent->v.angles[1], ent->v.angles[2] ); + SV_ClientPrintf( cl, "borigin: %.f %.f %.f\n", borigin[0], borigin[1], borigin[2] ); + + if( ent->v.classname ) + SV_ClientPrintf( cl, "class: %s\n", STRING( ent->v.classname )); + + if( ent->v.globalname ) + SV_ClientPrintf( cl, "global: %s\n", STRING( ent->v.globalname )); + + if( ent->v.targetname ) + SV_ClientPrintf( cl, "name: %s\n", STRING( ent->v.targetname )); + + if( ent->v.target ) + SV_ClientPrintf( cl, "target: %s\n", STRING( ent->v.target )); + + if( ent->v.model ) + SV_ClientPrintf( cl, "model: %s\n", STRING( ent->v.model )); + + SV_ClientPrintf( cl, "health: %.f\n", ent->v.health ); + + if( ent->v.gravity != 1.0f ) + SV_ClientPrintf( cl, "gravity: %.2f\n", ent->v.gravity ); + + SV_ClientPrintf( cl, "movetype: %d\n", ent->v.movetype ); + SV_ClientPrintf( cl, "rendermode: %d\n", ent->v.rendermode ); + SV_ClientPrintf( cl, "renderfx: %d\n", ent->v.renderfx ); + SV_ClientPrintf( cl, "renderamt: %f\n", ent->v.renderamt ); + SV_ClientPrintf( cl, "rendercolor: %f %f %f\n", ent->v.rendercolor[0], ent->v.rendercolor[1], ent->v.rendercolor[2] ); + SV_ClientPrintf( cl, "maxspeed: %f\n", ent->v.maxspeed ); + + if( ent->v.solid ) + SV_ClientPrintf( cl, "solid: %d\n", ent->v.solid ); + + SV_ClientPrintf( cl, "flags: 0x%x\n", ent->v.flags ); + SV_ClientPrintf( cl, "spawnflags: 0x%x\n", ent->v.spawnflags ); + return true; +} + +/* +=============== +SV_EntFire_f + +Perform some actions +=============== +*/ +static qboolean SV_EntFire_f( sv_client_t *cl ) +{ + edict_t *ent = NULL; + int i = 1, count = 0; + qboolean single; // true if user specified something that match single entity + + if( Cmd_Argc() < 3 ) + { + SV_ClientPrintf( cl, "Use ent_fire []\n" + "Use ent_fire 0 help to get command list\n" ); + return false; + } + + if( ( single = Q_isdigit( Cmd_Argv( 1 ) ) ) ) + { + i = Q_atoi( Cmd_Argv( 1 ) ); + + if( i < 0 || i >= svgame.numEntities ) + return false; + + ent = EDICT_NUM( i ); + } + else if( ( single = !Q_stricmp( Cmd_Argv( 1 ), "!cross" ) ) ) + { + ent = SV_GetCrossEnt( cl->edict ); + + if (!SV_IsValidEdict(ent)) + return false; + + i = NUM_FOR_EDICT( ent ); + } + else if( ( single = ( Cmd_Argv( 1 )[0] == '!') ) ) // check for correct instance with !(num)_(serial) + { + const char *cmd = Cmd_Argv( 1 ) + 1; + i = Q_atoi( cmd ); + + while( Q_isdigit( cmd )) cmd++; + + if( *cmd++ != '_' ) + return false; + + if( i < 0 || i >= svgame.numEntities ) + return false; + + ent = EDICT_NUM( i ); + if( ent->serialnumber != Q_atoi( cmd ) ) + return false; + } + else + { + i = svgame.globals->maxClients + 1; + } + + for( ; ( i < svgame.numEntities ) && ( count < sv_enttools_maxfire.value ); i++ ) + { + ent = EDICT_NUM( i ); + if( !SV_IsValidEdict( ent )) + { + // SV_ClientPrintf( cl, PRINT_LOW, "Got invalid entity\n" ); + if( single ) + break; + continue; + } + + // if user specified not a number, try find such entity + if( !single ) + { + if( !Q_stricmpext( Cmd_Argv( 1 ), STRING( ent->v.targetname ) ) && !Q_stricmpext( Cmd_Argv( 1 ), STRING( ent->v.classname ) )) + continue; + } + + SV_ClientPrintf( cl, "entity %i\n", i ); + + count++; + + if( !Q_stricmp( Cmd_Argv( 2 ), "health" ) ) + ent->v.health = Q_atoi( Cmd_Argv ( 3 ) ); + else if( !Q_stricmp( Cmd_Argv( 2 ), "gravity" ) ) + ent->v.gravity = Q_atof( Cmd_Argv ( 3 ) ); + else if( !Q_stricmp( Cmd_Argv( 2 ), "movetype" ) ) + ent->v.movetype = Q_atoi( Cmd_Argv ( 3 ) ); + else if( !Q_stricmp( Cmd_Argv( 2 ), "solid" ) ) + ent->v.solid = Q_atoi( Cmd_Argv ( 3 ) ); + else if( !Q_stricmp( Cmd_Argv( 2 ), "rename" ) ) + ent->v.targetname = ALLOC_STRING( Cmd_Argv ( 3 ) ); + else if( !Q_stricmp( Cmd_Argv( 2 ), "settarget" ) ) + ent->v.target = ALLOC_STRING( Cmd_Argv ( 3 ) ); + else if( !Q_stricmp( Cmd_Argv( 2 ), "setmodel" ) ) + SV_SetModel( ent, Cmd_Argv( 3 ) ); + else if( !Q_stricmp( Cmd_Argv( 2 ), "set" ) ) + { + string keyname; + string value; + KeyValueData pkvd; + if( Cmd_Argc() != 5 ) + return false; + + pkvd.szClassName = (char*)STRING( ent->v.classname ); + Q_strncpy( keyname, Cmd_Argv( 3 ), sizeof( keyname )); + Q_strncpy( value, Cmd_Argv( 4 ), sizeof( value )); + pkvd.szKeyName = keyname; + pkvd.szValue = value; + pkvd.fHandled = false; + svgame.dllFuncs.pfnKeyValue( ent, &pkvd ); + + if( pkvd.fHandled ) + SV_ClientPrintf( cl, "value set successfully!\n" ); + } + else if( !Q_stricmp( Cmd_Argv( 2 ), "touch" ) ) + { + if( Cmd_Argc() == 4 ) + { + edict_t *other = SV_EntFindSingle( cl, Cmd_Argv( 3 ) ); + if( other && other->pvPrivateData ) + svgame.dllFuncs.pfnTouch( ent, other ); + } + else + svgame.dllFuncs.pfnTouch( ent, cl->edict ); + } + else if( !Q_stricmp( Cmd_Argv( 2 ), "use" ) ) + { + if( Cmd_Argc() == 4 ) + { + edict_t *other = SV_EntFindSingle( cl, Cmd_Argv( 3 ) ); + if( other && other->pvPrivateData ) + svgame.dllFuncs.pfnUse( ent, other ); + } + else + svgame.dllFuncs.pfnUse( ent, cl->edict ); + } + else if( !Q_stricmp( Cmd_Argv( 2 ), "movehere" ) ) + { + ent->v.origin[2] = cl->edict->v.origin[2] + 25; + ent->v.origin[1] = cl->edict->v.origin[1] + 100 * sin( DEG2RAD( cl->edict->v.angles[1] ) ); + ent->v.origin[0] = cl->edict->v.origin[0] + 100 * cos( DEG2RAD( cl->edict->v.angles[1] ) ); + SV_LinkEdict( ent, true ); + } + else if( !Q_stricmp( Cmd_Argv( 2 ), "drop2floor" ) ) + { + pfnDropToFloor( ent ); + } + else if( !Q_stricmp( Cmd_Argv( 2 ), "moveup" ) ) + { + float dist = 25; + if( Cmd_Argc() >= 4 ) + dist = Q_atof( Cmd_Argv( 3 ) ); + ent->v.origin[2] += dist; + if( Cmd_Argc() >= 5 ) + { + dist = Q_atof( Cmd_Argv( 4 ) ); + ent->v.origin[0] += dist * cos( DEG2RAD( cl->edict->v.angles[1] ) ); + ent->v.origin[1] += dist * sin( DEG2RAD( cl->edict->v.angles[1] ) ); + } + + } + else if( !Q_stricmp( Cmd_Argv( 2 ), "becomeowner" ) ) + { + if( Cmd_Argc() == 4 ) + ent->v.owner = SV_EntFindSingle( cl, Cmd_Argv( 3 ) ); + else + ent->v.owner = cl->edict; + } + else if( !Q_stricmp( Cmd_Argv( 2 ), "becomeenemy" ) ) + { + if( Cmd_Argc() == 4 ) + ent->v.enemy = SV_EntFindSingle( cl, Cmd_Argv( 3 ) ); + else + ent->v.enemy = cl->edict; + } + else if( !Q_stricmp( Cmd_Argv( 2 ), "becomeaiment" ) ) + { + if( Cmd_Argc() == 4 ) + ent->v.aiment= SV_EntFindSingle( cl, Cmd_Argv( 3 ) ); + else + ent->v.aiment = cl->edict; + } + else if( !Q_stricmp( Cmd_Argv( 2 ), "hullmin" ) ) + { + if( Cmd_Argc() != 6 ) + return false; + ent->v.mins[0] = Q_atof( Cmd_Argv( 3 ) ); + ent->v.mins[1] = Q_atof( Cmd_Argv( 4 ) ); + ent->v.mins[2] = Q_atof( Cmd_Argv( 5 ) ); + } + else if( !Q_stricmp( Cmd_Argv( 2 ), "hullmax" ) ) + { + if( Cmd_Argc() != 6 ) + return false; + ent->v.maxs[0] = Q_atof( Cmd_Argv( 3 ) ); + ent->v.maxs[1] = Q_atof( Cmd_Argv( 4 ) ); + ent->v.maxs[2] = Q_atof( Cmd_Argv( 5 ) ); + } + else if( !Q_stricmp( Cmd_Argv( 2 ), "rendercolor" ) ) + { + if( Cmd_Argc() != 6 ) + return false; + ent->v.rendercolor[0] = Q_atof( Cmd_Argv( 3 ) ); + ent->v.rendercolor[1] = Q_atof( Cmd_Argv( 4 ) ); + ent->v.rendercolor[2] = Q_atof( Cmd_Argv( 5 ) ); + } + else if( !Q_stricmp( Cmd_Argv( 2 ), "renderamt" ) ) + { + ent->v.renderamt = Q_atof( Cmd_Argv( 3 ) ); + } + else if( !Q_stricmp( Cmd_Argv( 2 ), "renderfx" ) ) + { + ent->v.renderfx = Q_atoi( Cmd_Argv( 3 ) ); + } + else if( !Q_stricmp( Cmd_Argv( 2 ), "rendermode" ) ) + { + ent->v.rendermode = Q_atoi( Cmd_Argv( 3 ) ); + } + else if( !Q_stricmp( Cmd_Argv( 2 ), "angles" ) ) + { + ent->v.angles[0] = Q_atof( Cmd_Argv( 3 ) ); + ent->v.angles[1] = Q_atof( Cmd_Argv( 4 ) ); + ent->v.angles[2] = Q_atof( Cmd_Argv( 5 ) ); + } + else if( !Q_stricmp( Cmd_Argv( 2 ), "setflag" ) ) + { + ent->v.flags |= 1U << Q_atoi( Cmd_Argv ( 3 ) ); + SV_ClientPrintf( cl, "flags set to 0x%x\n", ent->v.flags ); + } + else if( !Q_stricmp( Cmd_Argv( 2 ), "clearflag" ) ) + { + ent->v.flags &= ~( 1U << Q_atoi( Cmd_Argv ( 3 ) ) ); + SV_ClientPrintf( cl, "flags set to 0x%x\n", ent->v.flags ); + } + else if( !Q_stricmp( Cmd_Argv( 2 ), "setspawnflag" ) ) + { + ent->v.spawnflags |= 1U << Q_atoi( Cmd_Argv ( 3 ) ); + SV_ClientPrintf( cl, "spawnflags set to 0x%x\n", ent->v.spawnflags ); + } + else if( !Q_stricmp( Cmd_Argv( 2 ), "clearspawnflag" ) ) + { + ent->v.spawnflags &= ~( 1U << Q_atoi( Cmd_Argv ( 3 ) ) ); + SV_ClientPrintf( cl, "spawnflags set to 0x%x\n", ent->v.flags ); + } + else if( !Q_stricmp( Cmd_Argv( 2 ), "help" ) ) + { + SV_ClientPrintf( cl, "Available commands:\n" + "Set fields:\n" + " (Only set entity field, does not call any functions)\n" + " health\n" + " gravity\n" + " movetype\n" + " solid\n" + " rendermode\n" + " rendercolor (vector)\n" + " renderfx\n" + " renderamt\n" + " hullmin (vector)\n" + " hullmax (vector)\n" + "Actions\n" + " rename: set entity targetname\n" + " settarget: set entity target (only targetnames)\n" + " setmodel: set entity model\n" + " set: set by server library\n" + " See game FGD to get list.\n" + " command takes two arguments\n" + " touch: touch entity by current player.\n" + " use: use entity by current player.\n" + " movehere: place entity in player fov.\n" + " drop2floor: place entity to nearest floor surface\n" + " moveup: move entity to 25 units up\n" + "Flags:\n" + " (Set/clear specified flag bit, arg is bit number)\n" + " setflag\n" + " clearflag\n" + " setspawnflag\n" + " clearspawnflag\n" + ); + return true; + } + else + { + SV_ClientPrintf( cl, "Unknown command %s!\nUse \"ent_fire 0 help\" to list commands.\n", Cmd_Argv( 2 ) ); + return false; + } + if( single ) + break; + } + return true; +} + +/* +=============== +SV_EntSendVars +=============== +*/ +static void SV_EntSendVars( sv_client_t *cl, edict_t *ent ) +{ + if( !ent ) + return; + + MSG_WriteByte( &cl->netchan.message, svc_stufftext ); + MSG_WriteString( &cl->netchan.message, va( "set ent_last_name \"%s\"\n", STRING( ent->v.targetname ) )); + MSG_WriteByte( &cl->netchan.message, svc_stufftext ); + MSG_WriteString( &cl->netchan.message, va( "set ent_last_num %i\n", NUM_FOR_EDICT( ent ) )); + MSG_WriteByte( &cl->netchan.message, svc_stufftext ); + MSG_WriteString( &cl->netchan.message, va( "set ent_last_inst !%i_%i\n", NUM_FOR_EDICT( ent ), ent->serialnumber )); + MSG_WriteByte( &cl->netchan.message, svc_stufftext ); + MSG_WriteString( &cl->netchan.message, va( "set ent_last_origin \"%f %f %f\"\n", ent->v.origin[0], ent->v.origin[1], ent->v.origin[2])); + MSG_WriteByte( &cl->netchan.message, svc_stufftext ); + MSG_WriteString( &cl->netchan.message, va( "set ent_last_class \"%s\"\n", STRING( ent->v.classname ))); + MSG_WriteByte( &cl->netchan.message, svc_stufftext ); + MSG_WriteString( &cl->netchan.message, "ent_getvars_cb\n" ); // why do we need this? +} + +/* +=============== +SV_EntCreate_f + +Create new entity with specified name. +=============== +*/ +static qboolean SV_EntCreate_f( sv_client_t *cl ) +{ + edict_t *ent = NULL; + int i = 0; + string_t classname; + + if( Cmd_Argc() < 2 ) + { + SV_ClientPrintf( cl, "Use ent_create ...\n" ); + return false; + } + + classname = ALLOC_STRING( Cmd_Argv( 1 ) ); + + ent = SV_AllocPrivateData( 0, classname ); + + // Xash3D extension + if( !ent && svgame.physFuncs.SV_CreateEntity ) + { + ent = SV_AllocEdict(); + ent->v.classname = classname; + if( svgame.physFuncs.SV_CreateEntity( ent, (char*)STRING( classname ) ) == -1 ) + { + if( ent && !ent->free ) + SV_FreeEdict( ent ); + ent = NULL; + } + } + + // XashXT does not implement SV_CreateEntity, use saverestore export + if( !ent && svgame.physFuncs.pfnCreateEntitiesInRestoreList ) + { + SAVERESTOREDATA data = { 0 }; + ENTITYTABLE table = { 0 }; + data.tableCount = 1; + data.pTable = &table; + table.classname = classname; + table.id = -1; + table.size = 1; + svgame.physFuncs.pfnCreateEntitiesInRestoreList( &data, 0, false ); + ent = table.pent; + } + + if( !ent ) + { + SV_ClientPrintf( cl, "Invalid entity!\n" ); + return false; + } + + // choose default origin + ent->v.origin[2] = cl->edict->v.origin[2] + 25; + ent->v.origin[1] = cl->edict->v.origin[1] + 100 * sin( DEG2RAD( cl->edict->v.angles[1] ) ); + ent->v.origin[0] = cl->edict->v.origin[0] + 100 * cos( DEG2RAD( cl->edict->v.angles[1] ) ); + + SV_LinkEdict( ent, false ); + + // apply keyvalues if supported + if( svgame.dllFuncs.pfnKeyValue ) + { + for( i = 2; i < Cmd_Argc() - 1; i++ ) + { + string keyname; + string value; + KeyValueData pkvd; + + // allow split keyvalues to prespawn and postspawn + if( !Q_strcmp( Cmd_Argv( i ), "|" ) ) + break; + + Q_strncpy( keyname, Cmd_Argv( i++ ), sizeof( keyname )); + Q_strncpy( value, Cmd_Argv( i ), sizeof( value )); + pkvd.fHandled = false; + pkvd.szClassName = (char*)STRING( ent->v.classname ); + pkvd.szKeyName = keyname; + pkvd.szValue = value; + svgame.dllFuncs.pfnKeyValue( ent, &pkvd ); + + if( pkvd.fHandled ) + SV_ClientPrintf( cl, "value \"%s\" set to \"%s\"!\n", pkvd.szKeyName, pkvd.szValue ); + } + } + + // set default targetname + if( !ent->v.targetname ) + { + string newname, clientname; + int j; + + for( j = 0; j < sizeof( cl->name ); j++ ) + { + char c = Q_tolower( cl->name[j] ); + if( c < 'a' || c > 'z' ) + c = '_'; + if( !cl->name[j] ) + { + clientname[j] = 0; + break; + } + clientname[j] = c; + } + + // generate name based on nick name and index + Q_snprintf( newname, sizeof( newname ), "%s_%i_e%i", clientname, cl->userid, NUM_FOR_EDICT( ent )); + + // i know, it may break strict aliasing rules + // but we will not lose anything in this case. + Q_strnlwr( newname, newname, sizeof( newname )); + ent->v.targetname = ALLOC_STRING( newname ); + SV_EntSendVars( cl, ent ); + } + + SV_ClientPrintf( cl, "Created %i: %s, targetname %s\n", NUM_FOR_EDICT( ent ), Cmd_Argv( 1 ), STRING( ent->v.targetname ) ); + + if( svgame.dllFuncs.pfnSpawn ) + svgame.dllFuncs.pfnSpawn( ent ); + + // now drop entity to floor. + pfnDropToFloor( ent ); + + // force think. Otherwise given weapon may crash server if player touch it before. + svgame.dllFuncs.pfnThink( ent ); + pfnDropToFloor( ent ); + + // apply postspawn keyvales if supported + if( svgame.dllFuncs.pfnKeyValue ) + { + for( i = i + 1; i < Cmd_Argc() - 1; i++ ) + { + string keyname; + string value; + KeyValueData pkvd; + + Q_strncpy( keyname, Cmd_Argv( i++ ), sizeof( keyname )); + Q_strncpy( value, Cmd_Argv( i ), sizeof( value )); + pkvd.fHandled = false; + pkvd.szClassName = (char*)STRING( ent->v.classname ); + pkvd.szKeyName = keyname; + pkvd.szValue = value; + svgame.dllFuncs.pfnKeyValue( ent, &pkvd ); + + if( pkvd.fHandled ) + SV_ClientPrintf( cl, "value \"%s\" set to \"%s\"!\n", pkvd.szKeyName, pkvd.szValue ); + } + } + return true; +} + +static qboolean SV_EntGetVars_f( sv_client_t *cl ) +{ + edict_t *ent = NULL; + + if( Cmd_Argc() != 2 ) + { + SV_ClientPrintf( cl, "Use ent_getvars \n" ); + return false; + } + + ent = SV_EntFindSingle( cl, Cmd_Argv( 1 ) ); + if( Cmd_Argc() ) + { + if( !SV_IsValidEdict( ent )) + return false; + } + + SV_EntSendVars( cl, ent ); + return true; +} ucmd_t ucmds[] = { @@ -2140,6 +2914,16 @@ ucmd_t ucmds[] = { NULL, NULL } }; +ucmd_t enttoolscmds[] = +{ +{ "ent_list", SV_EntList_f }, +{ "ent_info", SV_EntInfo_f }, +{ "ent_fire", SV_EntFire_f }, +{ "ent_create", SV_EntCreate_f }, +{ "ent_getvars", SV_EntGetVars_f }, +{ NULL, NULL } +}; + /* ================== SV_ExecuteUserCommand @@ -2162,6 +2946,24 @@ void SV_ExecuteClientCommand( sv_client_t *cl, const char *s ) } } + if( !u->name && sv_enttools_enable.value > 0.0f && !sv.background ) + { + for( u = enttoolscmds; u->name; u++ ) + { + if( !Q_strcmp( Cmd_Argv( 0 ), u->name )) + { + Con_Reportf( "enttools->%s(): %s\n", u->name, s ); + Log_Printf( "\"%s<%i><%s><>\" performed: %s\n", Info_ValueForKey( cl->userinfo, "name" ), + cl->userid, SV_GetClientIDString( cl ), NET_AdrToString( cl->netchan.remote_address ), s ); + + if( u->func ) + u->func( cl ); + + break; + } + } + } + if( !u->name && sv.state == ss_active ) { // custom client commands diff --git a/engine/server/sv_game.c b/engine/server/sv_game.c index 61dd4fc8..ffa37f5d 100644 --- a/engine/server/sv_game.c +++ b/engine/server/sv_game.c @@ -257,6 +257,63 @@ void SV_CopyTraceToGlobal( trace_t *trace ) else svgame.globals->trace_ent = svgame.edicts; } +/* +============== +SV_SetModel +============== +*/ +void SV_SetModel( edict_t *ent, const char *modelname ) +{ + char name[MAX_QPATH]; + qboolean found = false; + model_t *mod; + int i = 1; + + if( !SV_IsValidEdict( ent )) + { + Con_Printf( S_WARN "SV_SetModel: invalid entity %s\n", SV_ClassName( ent )); + return; + } + + if( !modelname || modelname[0] <= ' ' ) + { + Con_Printf( S_WARN "SV_SetModel: null name\n" ); + return; + } + + if( *modelname == '\\' || *modelname == '/' ) + modelname++; + + Q_strncpy( name, modelname, sizeof( name )); + COM_FixSlashes( name ); + + i = SV_ModelIndex( name ); + if( i == 0 ) + { + if( sv.state == ss_active ) + Con_Printf( S_ERROR "SV_SetModel: failed to set model %s: world model cannot be changed\n", name ); + return; + } + + if( COM_CheckString( name )) + { + ent->v.model = MAKE_STRING( sv.model_precache[i] ); + ent->v.modelindex = i; + mod = sv.models[i]; + } + else + { + // model will be cleared + ent->v.model = ent->v.modelindex = 0; + mod = NULL; + } + + // set the model size + if( mod && mod->type != mod_studio ) + SV_SetMinMaxSize( ent, mod->mins, mod->maxs, true ); + else SV_SetMinMaxSize( ent, vec3_origin, vec3_origin, true ); +} + /* ============= SV_ConvertTrace @@ -1319,61 +1376,7 @@ pfnSetModel */ void GAME_EXPORT pfnSetModel( edict_t *e, const char *m ) { - char name[MAX_QPATH]; - qboolean found = false; - model_t *mod; - int i = 1; - - if( !SV_IsValidEdict( e )) - return; - - if( *m == '\\' || *m == '/' ) m++; - Q_strncpy( name, m, sizeof( name )); - COM_FixSlashes( name ); - - if( COM_CheckString( name )) - { - // check to see if model was properly precached - for( ; i < MAX_MODELS && sv.model_precache[i][0]; i++ ) - { - if( !Q_stricmp( sv.model_precache[i], name )) - { - found = true; - break; - } - } - - if( !found ) - { - Con_Printf( S_ERROR "Failed to set model %s: was not precached\n", name ); - return; - } - } - - if( e == svgame.edicts ) - { - if( sv.state == ss_active ) - Con_Printf( S_ERROR "Failed to set model %s: world model cannot be changed\n", name ); - return; - } - - if( COM_CheckString( name )) - { - e->v.model = MAKE_STRING( sv.model_precache[i] ); - e->v.modelindex = i; - mod = sv.models[i]; - } - else - { - // model will be cleared - e->v.model = e->v.modelindex = 0; - mod = NULL; - } - - // set the model size - if( mod && mod->type != mod_studio ) - SV_SetMinMaxSize( e, mod->mins, mod->maxs, true ); - else SV_SetMinMaxSize( e, vec3_origin, vec3_origin, true ); + SV_SetModel( e, m ); } /* diff --git a/engine/server/sv_main.c b/engine/server/sv_main.c index 1122905b..6a73724c 100644 --- a/engine/server/sv_main.c +++ b/engine/server/sv_main.c @@ -116,6 +116,10 @@ CVAR_DEFINE_AUTO( violence_agibs, "1", 0, "show alien gib entities" ); CVAR_DEFINE_AUTO( sv_voiceenable, "1", FCVAR_ARCHIVE|FCVAR_SERVER, "enable voice support" ); CVAR_DEFINE_AUTO( sv_voicequality, "3", FCVAR_ARCHIVE|FCVAR_SERVER, "voice chat quality level, from 0 to 5, higher is better" ); +// enttools +CVAR_DEFINE_AUTO( sv_enttools_enable, "0", FCVAR_ARCHIVE|FCVAR_PROTECTED, "enable powerful and dangerous entity tools" ); +CVAR_DEFINE_AUTO( sv_enttools_maxfire, "5", FCVAR_ARCHIVE|FCVAR_PROTECTED, "limit ent_fire actions count to prevent flooding" ); + convar_t *sv_novis; // disable server culling entities by vis convar_t *sv_pausable; convar_t *timeout; // seconds without any message @@ -981,6 +985,8 @@ void SV_Init( void ) Cvar_RegisterVariable( &sv_voiceenable ); Cvar_RegisterVariable( &sv_voicequality ); Cvar_RegisterVariable( &sv_trace_messages ); + Cvar_RegisterVariable( &sv_enttools_enable ); + Cvar_RegisterVariable( &sv_enttools_maxfire ); sv_allow_joystick = Cvar_Get( "sv_allow_joystick", "1", FCVAR_ARCHIVE, "allow connect with joystick enabled" ); sv_allow_mouse = Cvar_Get( "sv_allow_mouse", "1", FCVAR_ARCHIVE, "allow connect with mouse" ); From adb8ec1da8c321b85a9eb837f2a1c2a83a73d544 Mon Sep 17 00:00:00 2001 From: Alibek Omarov Date: Wed, 2 Nov 2022 15:58:41 +0600 Subject: [PATCH 159/490] filesystem: try to normalize linux gamedll path for liblist.gam --- filesystem/filesystem.c | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/filesystem/filesystem.c b/filesystem/filesystem.c index 17f7f845..c9dea1a6 100644 --- a/filesystem/filesystem.c +++ b/filesystem/filesystem.c @@ -746,6 +746,24 @@ void FS_ParseGenericGameInfo( gameinfo_t *GameInfo, const char *buf, const qbool else if( !Q_stricmp( token, "gamedll_linux" )) { pfile = COM_ParseFile( pfile, GameInfo->game_dll_linux, sizeof( GameInfo->game_dll_linux )); + + // try to normalize filename only for liblist.gam + // from hl_i?86.so to hl.so + if( !isGameInfo ) + { + char *p; + COM_StripExtension( GameInfo->game_dll_linux ); + + p = Q_strrchr( GameInfo->game_dll_linux, '_' ); + + if( p && Q_stricmpext( "_i?86", p )) + { + *p = 0; + } + + COM_DefaultExtension( GameInfo->game_dll_linux, "."OS_LIB_EXT ); + } + found_linux = true; } // valid for both From 0a49e6981831e581be5c6fbfa044a8ce02842edc Mon Sep 17 00:00:00 2001 From: Alibek Omarov Date: Wed, 19 Oct 2022 02:25:48 +0300 Subject: [PATCH 160/490] engine: introduce Sys_DebugBreak function to raise an exception for debugger --- engine/common/system.c | 46 +++++++++++++++++++++----------------- engine/common/system.h | 1 + engine/platform/platform.h | 4 +--- 3 files changed, 28 insertions(+), 23 deletions(-) diff --git a/engine/common/system.c b/engine/common/system.c index ed7493a4..72ae9197 100644 --- a/engine/common/system.c +++ b/engine/common/system.c @@ -43,7 +43,6 @@ GNU General Public License for more details. #include "whereami.h" qboolean error_on_exit = false; // arg for exit(); -#define DEBUG_BREAK /* ================ @@ -54,23 +53,28 @@ double GAME_EXPORT Sys_DoubleTime( void ) { return Platform_DoubleTime(); } + +/* +================ +Sys_DebugBreak +================ +*/ +void Sys_DebugBreak( void ) +{ #if XASH_LINUX || ( XASH_WIN32 && !XASH_64BIT ) - #undef DEBUG_BREAK - qboolean Sys_DebuggerPresent( void ); // see sys_linux.c - #if XASH_MSVC - #define DEBUG_BREAK \ - if( Sys_DebuggerPresent() ) \ - _asm{ int 3 } - #elif XASH_X86 - #define DEBUG_BREAK \ - if( Sys_DebuggerPresent() ) \ - asm volatile("int $3;") - #else - #define DEBUG_BREAK \ - if( Sys_DebuggerPresent() ) \ - raise( SIGINT ) - #endif +#if XASH_MSVC + if( Sys_DebuggerPresent() ) + _asm { int 3 } +#elif XASH_X86 + if( Sys_DebuggerPresent() ) + asm volatile( "int $3;" ); +#else + if( Sys_DebuggerPresent() ) + raise( SIGINT ); #endif +#endif +} + #if !XASH_DEDICATED /* ================ @@ -377,12 +381,14 @@ void Sys_Warn( const char *format, ... ) va_list argptr; char text[MAX_PRINT_MSG]; - DEBUG_BREAK; - va_start( argptr, format ); Q_vsnprintf( text, MAX_PRINT_MSG, format, argptr ); va_end( argptr ); + + Sys_DebugBreak(); + Msg( "Sys_Warn: %s\n", text ); + if( !Host_IsDedicated() ) // dedicated server should not hang on messagebox MSGBOX(text); } @@ -404,8 +410,6 @@ void Sys_Error( const char *error, ... ) if( !Host_IsDedicated( )) Platform_SetCursorType( dc_arrow ); - DEBUG_BREAK; - if( host.status == HOST_ERR_FATAL ) return; // don't multiple executes @@ -418,6 +422,8 @@ void Sys_Error( const char *error, ... ) Q_vsnprintf( text, MAX_PRINT_MSG, error, argptr ); va_end( argptr ); + Sys_DebugBreak(); + SV_SysError( text ); if( !Host_IsDedicated() ) diff --git a/engine/common/system.h b/engine/common/system.h index c6d8956d..adf34f3d 100644 --- a/engine/common/system.h +++ b/engine/common/system.h @@ -59,6 +59,7 @@ void Sys_ParseCommandLine( int argc, char **argv ); void Sys_MergeCommandLine( void ); void Sys_SetupCrashHandler( void ); void Sys_RestoreCrashHandler( void ); +void Sys_DebugBreak( void ); #define Sys_GetParmFromCmdLine( parm, out ) _Sys_GetParmFromCmdLine( parm, out, sizeof( out )) qboolean _Sys_GetParmFromCmdLine( const char *parm, char *out, size_t size ); qboolean Sys_GetIntFromCmdLine( const char *parm, int *out ); diff --git a/engine/platform/platform.h b/engine/platform/platform.h index ead5bc6f..0aaee2b5 100644 --- a/engine/platform/platform.h +++ b/engine/platform/platform.h @@ -37,9 +37,7 @@ 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 ); -// commented out, as this is an optional feature or maybe implemented in system API directly -// see system.c -// qboolean Sys_DebuggerPresent( void ); +qboolean Sys_DebuggerPresent( void ); // optional, see Sys_DebugBreak #if XASH_ANDROID const char *Android_GetAndroidID( void ); From 616cbdb1eb4b28f7129640ac769f7835c24077db Mon Sep 17 00:00:00 2001 From: Alibek Omarov Date: Thu, 10 Nov 2022 12:11:16 +0300 Subject: [PATCH 161/490] engine: client: change cl_cmdrate and cl_smoothtime default values to match GoldSrc --- engine/client/cl_main.c | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) diff --git a/engine/client/cl_main.c b/engine/client/cl_main.c index 9565f2f0..f974075e 100644 --- a/engine/client/cl_main.c +++ b/engine/client/cl_main.c @@ -743,8 +743,14 @@ void CL_WritePacket( void ) if( cls.state == ca_connected ) numbackup = 0; // clamp cmdrate - if( cl_cmdrate->value < 0.0f ) Cvar_SetValue( "cl_cmdrate", 0.0f ); - else if( cl_cmdrate->value > 100.0f ) Cvar_SetValue( "cl_cmdrate", 100.0f ); + if( cl_cmdrate->value < 10.0f ) + { + Cvar_SetValue( "cl_cmdrate", 10.0f ); + } + else if( cl_cmdrate->value > 100.0f ) + { + Cvar_SetValue( "cl_cmdrate", 100.0f ); + } // Check to see if we can actually send this command @@ -2863,9 +2869,9 @@ void CL_InitLocal( void ) cl_nosmooth = Cvar_Get( "cl_nosmooth", "0", FCVAR_ARCHIVE, "disable smooth up stair climbing" ); cl_nointerp = Cvar_Get( "cl_nointerp", "0", FCVAR_CLIENTDLL, "disable interpolation of entities and players" ); - cl_smoothtime = Cvar_Get( "cl_smoothtime", "0", FCVAR_ARCHIVE, "time to smooth up" ); + cl_smoothtime = Cvar_Get( "cl_smoothtime", "0.1", FCVAR_ARCHIVE, "time to smooth up" ); cl_cmdbackup = Cvar_Get( "cl_cmdbackup", "10", FCVAR_ARCHIVE, "how many additional history commands are sent" ); - cl_cmdrate = Cvar_Get( "cl_cmdrate", "30", FCVAR_ARCHIVE, "Max number of command packets sent to server per second" ); + cl_cmdrate = Cvar_Get( "cl_cmdrate", "60", FCVAR_ARCHIVE, "Max number of command packets sent to server per second" ); cl_draw_particles = Cvar_Get( "r_drawparticles", "1", FCVAR_CHEAT, "render particles" ); cl_draw_tracers = Cvar_Get( "r_drawtracers", "1", FCVAR_CHEAT, "render tracers" ); cl_draw_beams = Cvar_Get( "r_drawbeams", "1", FCVAR_CHEAT, "render beams" ); From ef74f86ce2d853dd05807ffd791bfe30247a3762 Mon Sep 17 00:00:00 2001 From: Alibek Omarov Date: Thu, 10 Nov 2022 12:37:47 +0300 Subject: [PATCH 162/490] Documentation: restore enttools docs from old engine wiki --- Documentation/entity-tools.md | 150 ++++++++++++++++++++++++++++++++++ 1 file changed, 150 insertions(+) create mode 100644 Documentation/entity-tools.md diff --git a/Documentation/entity-tools.md b/Documentation/entity-tools.md new file mode 100644 index 00000000..8a8a16cf --- /dev/null +++ b/Documentation/entity-tools.md @@ -0,0 +1,150 @@ +# There are few new commands availiable in xash3d fork: + +## Commands: +### ent_create +Create entity with specified classname and key/values + +`ent_create ...` + +for example: + +`ent_create monster_zombie targetname zomb1` + +after creating entity, ent_last_xxx cvars are set to new entity and ent_last_cb called, look at ent_getvars description + +### ent_fire + +Make some actions on entity + +`ent_fire ` +Availiavle commands: +* Set fields (Only set entity field, does not call any functions): + * health + * gravity + * movetype + * solid + * rendermode + * rendercolor (vector) + * renderfx + * renderamt + * hullmin (vector) + * hullmax (vector) +* Actions + * rename: set entity targetname + * settarget: set entity target (only targetnames) + * setmodel: set entity model (does not update) + * set: set key/value by server library + * See game FGD to get list. + * command takes two arguments + * touch: touch entity by current player. + * use: use entity by current player. + * movehere: place entity in player fov. + * drop2floor: place entity to nearest floor surface + * moveup: move entity to 25 units up + * moveup (value): move by y axis relatively to specified value +* Flags (Set/clear specified flag bit, arg is bit number): + * setflag + * clearflag + * setspawnflag + * clearspawnflag + +### ent_info +Print information about entity by identificator + +`ent_info ` + +### ent_getvars +Set client cvars containing entity information (useful for [[Scripting]]) and call ent_last_cb + +`ent_getvars ` + +These cvars are set: +``` +ent_last_name +ent_last_num +ent_last_inst +ent_last_origin +ent_last_class +``` + +### ent_list +Print short information about antities, filtered by pattern + +`ent_list ` + +## Syntax description + +### \ + +* !cross: entity under aim +* Instance code: !\_\ + * set by ent_getvars command +* Entity index +* targetname pattern + +### \ + +Pattern is like identificator, but may filter many entities by classname + +### (vector) + +used by ent_fire command. vector means three float values, entered without quotes + +### key/value + +All entities parameters may be set by specifiing key and value strings. + +Originally, this mechanizm is used in map/bsp format, but it can be used in enttools too. + +Keys and values are passed to server library and processed by entity keyvalue function, setting edict and entity owns parameters. + +If value contains spaces, it must be put in quotes: + +`ent_fire !cross set origin "0 0 0"` + +## Using with scripting + +ent_create and ent_getvars commands are setting cvars on client + +It can be used with ent_last_cb alias that is executed after setting cvars. + +Simple example: + +``` +ent_create weapon_c4 +alias ent_last_cb "ent_fire \$ent_last_inst use" +``` + +Use weapon_c4 after creating it. + +Note that you cannot use many dfferent callbacks at the same time. + +You can set entity name by by pattern and create special script, contatning all callbacks. + +Example: + +example.cfg +``` +alias ent_last_cb exec entity_cb.cfg +ent create \ targetname my_ent1_$name +ent_create \ targetname my_ent2_$name +``` +entity_cb.cfg +``` +if $ent_last_name == my_ent1_$name +:(ent1 actions) +if $ent_last_name == my_ent2_$name +:(ent2 actions) +``` +Note that scripting cannot be blocking. You cannot wait for server answer and continue. But you can use small scripts, connected with ent_last_cb command. The best usage is user interaction. You can add touch buttons to screen or call user command menu actions by callbacks. +## Server side + +To enable entity tools on server, set sv_enttools_enable to 1 + +To change maximum number of entities, touched by ent_fire, change sv_enttools_maxfire to required number. + +To enable actions on players, set sv_enttools_players to 1. + +To enable entity tools for player by nickname, set sv_enttools_godplayer to nickname. Useful to temporary enable from rcon. + +To prevent crash on some actions, set host_mapdesign_fatal to 0 \ No newline at end of file From f469b56b93d712db502477604b629a30f6c18a3d Mon Sep 17 00:00:00 2001 From: Alibek Omarov Date: Thu, 10 Nov 2022 12:42:48 +0300 Subject: [PATCH 163/490] engine: host: only sleep once between frames --- engine/common/host.c | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/engine/common/host.c b/engine/common/host.c index 9dc606e3..48df08dd 100644 --- a/engine/common/host.c +++ b/engine/common/host.c @@ -675,11 +675,19 @@ Host_Frame */ void Host_Frame( float time ) { - Host_CheckSleep(); + static qboolean slept = false; // decide the simulation time if( !Host_FilterTime( time )) + { + if( !slept ) + { + Host_CheckSleep(); + slept = true; + } return; + } + slept = false; Host_InputFrame (); // input frame Host_ClientBegin (); // begin client From d13f7f06e443c551a0ce11dc659adf590506b752 Mon Sep 17 00:00:00 2001 From: Alibek Omarov Date: Thu, 10 Nov 2022 13:05:03 +0300 Subject: [PATCH 164/490] engine: fix framerate being capped to 60FPS with vsync, remove vid_displayfrequency cvar. Never sleep with vsync or timedemo, for accuracy --- engine/client/vid_common.c | 2 -- engine/client/vid_common.h | 1 - engine/common/common.h | 1 - engine/common/host.c | 27 +++++++++++++-------------- 4 files changed, 13 insertions(+), 18 deletions(-) diff --git a/engine/client/vid_common.c b/engine/client/vid_common.c index 1f1d1a74..ac80c22f 100644 --- a/engine/client/vid_common.c +++ b/engine/client/vid_common.c @@ -21,7 +21,6 @@ GNU General Public License for more details. #include "platform/platform.h" #define WINDOW_NAME XASH_ENGINE_NAME " Window" // Half-Life -convar_t *vid_displayfrequency; convar_t *vid_fullscreen; convar_t *vid_mode; convar_t *vid_brightness; @@ -181,7 +180,6 @@ void VID_Init( void ) vid_gamma = Cvar_Get( "gamma", "2.5", FCVAR_ARCHIVE, "gamma amount" ); vid_brightness = Cvar_Get( "brightness", "0.0", FCVAR_ARCHIVE, "brightness factor" ); - vid_displayfrequency = Cvar_Get ( "vid_displayfrequency", "0", FCVAR_RENDERINFO|FCVAR_VIDRESTART, "fullscreen refresh rate" ); vid_fullscreen = Cvar_Get( "fullscreen", "0", FCVAR_RENDERINFO|FCVAR_VIDRESTART, "enable fullscreen mode" ); vid_mode = Cvar_Get( "vid_mode", "0", FCVAR_RENDERINFO, "current video mode index (used just for storage)" ); vid_highdpi = Cvar_Get( "vid_highdpi", "1", FCVAR_RENDERINFO|FCVAR_VIDRESTART, "enable High-DPI mode" ); diff --git a/engine/client/vid_common.h b/engine/client/vid_common.h index 1014419e..57ef8b7c 100644 --- a/engine/client/vid_common.h +++ b/engine/client/vid_common.h @@ -31,7 +31,6 @@ extern glwstate_t glw_state; #define VID_MIN_WIDTH 320 extern convar_t *vid_fullscreen; -extern convar_t *vid_displayfrequency; extern convar_t *vid_highdpi; extern convar_t *vid_rotate; extern convar_t *vid_scale; diff --git a/engine/common/common.h b/engine/common/common.h index b08432e5..36cdb234 100644 --- a/engine/common/common.h +++ b/engine/common/common.h @@ -159,7 +159,6 @@ extern convar_t *scr_download; extern convar_t *cmd_scripting; extern convar_t *sv_maxclients; extern convar_t *cl_allow_levelshots; -extern convar_t *vid_displayfrequency; extern convar_t host_developer; extern convar_t *host_limitlocal; extern convar_t *host_framerate; diff --git a/engine/common/host.c b/engine/common/host.c index 48df08dd..0d607c22 100644 --- a/engine/common/host.c +++ b/engine/common/host.c @@ -276,6 +276,13 @@ void Host_CheckSleep( void ) { int sleeptime = host_sleeptime->value; +#ifndef XASH_DEDICATED + // never sleep in timedemo for benchmarking purposes + // also don't sleep with vsync for less lag + if( CL_IsTimeDemo( ) || CVAR_TO_BOOL( gl_vsync )) + return; +#endif + if( Host_IsDedicated() ) { // let the dedicated server some sleep @@ -597,24 +604,16 @@ double Host_CalcFPS( void ) } else if( Host_IsLocalGame( )) { - fps = host_maxfps->value; + if( !CVAR_TO_BOOL( gl_vsync )) + fps = host_maxfps->value; } else { - fps = host_maxfps->value; - if( fps == 0.0 ) fps = MAX_FPS; - fps = bound( MIN_FPS, fps, MAX_FPS ); - } - - // probably left part of this condition is redundant :-) - if( host.type != HOST_DEDICATED && Host_IsLocalGame( ) && !CL_IsTimeDemo( )) - { - // ajdust fps for vertical synchronization - if( CVAR_TO_BOOL( gl_vsync )) + if( !CVAR_TO_BOOL( gl_vsync )) { - if( vid_displayfrequency->value != 0.0f ) - fps = vid_displayfrequency->value; - else fps = 60.0; // default + fps = host_maxfps->value; + if( fps == 0.0 ) fps = MAX_FPS; + fps = bound( MIN_FPS, fps, MAX_FPS ); } } #endif From 17d0b19f9e6e77aa2120231d0c524647cb0d6011 Mon Sep 17 00:00:00 2001 From: Alibek Omarov Date: Thu, 10 Nov 2022 13:18:20 +0300 Subject: [PATCH 165/490] engine: use generic S_USAGE macro everywhere --- engine/client/cl_demo.c | 8 +------- engine/common/host.c | 2 +- engine/server/sv_cmds.c | 2 +- engine/server/sv_filter.c | 8 ++++---- engine/server/sv_log.c | 2 +- 5 files changed, 8 insertions(+), 14 deletions(-) diff --git a/engine/client/cl_demo.c b/engine/client/cl_demo.c index 92ebdd2b..089898ed 100644 --- a/engine/client/cl_demo.c +++ b/engine/client/cl_demo.c @@ -1408,7 +1408,7 @@ void CL_PlayDemo_f( void ) if( Cmd_Argc() < 2 ) { - Con_Printf( S_USAGE "playdemo \n" ); + Con_Printf( S_USAGE "%s \n", Cmd_Argv( 0 )); return; } @@ -1535,12 +1535,6 @@ timedemo */ void CL_TimeDemo_f( void ) { - if( Cmd_Argc() != 2 ) - { - Con_Printf( S_USAGE "timedemo \n" ); - return; - } - CL_PlayDemo_f (); // cls.td_starttime will be grabbed at the second frame of the demo, so diff --git a/engine/common/host.c b/engine/common/host.c index 0d607c22..0372c5e5 100644 --- a/engine/common/host.c +++ b/engine/common/host.c @@ -74,7 +74,7 @@ void Sys_PrintUsage( void ) #if XASH_MESSAGEBOX == MSGBOX_STDERR "\n" // dirty hack to not have Xash Error: Usage: on same line #endif // XASH_MESSAGEBOX == MSGBOX_STDERR - "Usage:\n" + S_USAGE "\n" #if !XASH_MOBILE_PLATFORM #if XASH_WIN32 O(".exe [options] [+command1] [+command2 arg]","") diff --git a/engine/server/sv_cmds.c b/engine/server/sv_cmds.c index 571c4ffc..b622d766 100644 --- a/engine/server/sv_cmds.c +++ b/engine/server/sv_cmds.c @@ -249,7 +249,7 @@ void SV_Maps_f( void ) if( Cmd_Argc() != 2 ) { - Msg( "Usage: maps \nmaps * for full listing\n" ); + Msg( S_USAGE "maps \nmaps * for full listing\n" ); return; } diff --git a/engine/server/sv_filter.c b/engine/server/sv_filter.c index fa7164a0..1b60e3b9 100644 --- a/engine/server/sv_filter.c +++ b/engine/server/sv_filter.c @@ -162,7 +162,7 @@ static void SV_BanID_f( void ) if( !id[0] ) { - Con_Reportf( "Usage: banid <#userid or unique id>\n0 minutes for permanent ban\n" ); + Con_Reportf( S_USAGE "banid <#userid or unique id>\n0 minutes for permanent ban\n" ); return; } @@ -259,7 +259,7 @@ static void SV_RemoveID_f( void ) if( !id[0] ) { - Con_Reportf("Usage: removeid <#slotnumber or uniqueid>\n"); + Con_Reportf( S_USAGE "removeid <#slotnumber or uniqueid>\n"); return; } @@ -354,7 +354,7 @@ static void SV_AddIP_f( void ) if( !StringToIP( ipstr, maskstr, &ip, &mask ) ) { - Con_Reportf( "Usage: addip [mask]\n0 minutes for permanent ban\n"); + Con_Reportf( S_USAGE "addip [mask]\n0 minutes for permanent ban\n"); return; } @@ -394,7 +394,7 @@ static void SV_RemoveIP_f( void ) if( !StringToIP( Cmd_Argv(1), Cmd_Argv(2), &ip, &mask ) ) { - Con_Reportf( "Usage: removeip [mask]\n" ); + Con_Reportf( S_USAGE "removeip [mask]\n" ); return; } diff --git a/engine/server/sv_log.c b/engine/server/sv_log.c index 7c1c5d6b..db6c4dfe 100644 --- a/engine/server/sv_log.c +++ b/engine/server/sv_log.c @@ -214,7 +214,7 @@ void SV_ServerLog_f( void ) { if( Cmd_Argc() != 2 ) { - Con_Printf("usage: log < on|off >\n" ); + Con_Printf( S_USAGE "log < on|off >\n" ); if( svs.log.active ) Con_Printf( "currently logging\n" ); From 32372654c23c76fdbb5e94d68cbec0dd40fc7c7a Mon Sep 17 00:00:00 2001 From: SNMetamorph <25657591+SNMetamorph@users.noreply.github.com> Date: Fri, 11 Nov 2022 07:14:22 +0400 Subject: [PATCH 166/490] engine: server: sv_client: fixed working ent_create command on XashXT/PrimeXT --- engine/server/sv_client.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/engine/server/sv_client.c b/engine/server/sv_client.c index e336f0f3..987fc8e2 100644 --- a/engine/server/sv_client.c +++ b/engine/server/sv_client.c @@ -2740,7 +2740,7 @@ static qboolean SV_EntCreate_f( sv_client_t *cl ) classname = ALLOC_STRING( Cmd_Argv( 1 ) ); - ent = SV_AllocPrivateData( 0, classname ); + ent = SV_CreateNamedEntity( 0, classname ); // Xash3D extension if( !ent && svgame.physFuncs.SV_CreateEntity ) From b1ee27a3f37b4b79ed71384e4e1bfef321716aab Mon Sep 17 00:00:00 2001 From: Alibek Omarov Date: Thu, 10 Nov 2022 16:38:33 +0300 Subject: [PATCH 167/490] filesystem: add new token internal_vgui_support to mark client dlls that have internal VGUI implementation(to support other UI systems) --- filesystem/filesystem.c | 5 +++++ filesystem/filesystem.h | 9 +++++---- 2 files changed, 10 insertions(+), 4 deletions(-) diff --git a/filesystem/filesystem.c b/filesystem/filesystem.c index c9dea1a6..bbb445d5 100644 --- a/filesystem/filesystem.c +++ b/filesystem/filesystem.c @@ -918,6 +918,11 @@ void FS_ParseGenericGameInfo( gameinfo_t *GameInfo, const char *buf, const qbool pfile = COM_ParseFile( pfile, token, sizeof( token )); GameInfo->render_picbutton_text = Q_atoi( token ); } + else if( !Q_stricmp( token, "internal_vgui_support" )) + { + pfile = COM_ParseFile( pfile, token, sizeof( token )); + GameInfo->internal_vgui_support = Q_atoi( token ); + } } } diff --git a/filesystem/filesystem.h b/filesystem/filesystem.h index 7f184f7c..e9b0a2df 100644 --- a/filesystem/filesystem.h +++ b/filesystem/filesystem.h @@ -76,10 +76,11 @@ typedef struct gameinfo_s size_t size; int gamemode; - qboolean secure; // prevent to console acess - qboolean nomodels; // don't let player to choose model (use player.mdl always) - qboolean noskills; // disable skill menu selection - qboolean render_picbutton_text; // use font renderer to render WON buttons + qboolean secure; // prevent to console acess + qboolean nomodels; // don't let player to choose model (use player.mdl always) + qboolean noskills; // disable skill menu selection + qboolean render_picbutton_text; // use font renderer to render WON buttons + qboolean internal_vgui_support; // skip loading VGUI, pass ingame UI support API to client char sp_entity[32]; // e.g. info_player_start char mp_entity[32]; // e.g. info_player_deathmatch From d27dd680725109f8a47377637b92d47f17874e51 Mon Sep 17 00:00:00 2001 From: Alibek Omarov Date: Thu, 10 Nov 2022 19:56:31 +0300 Subject: [PATCH 168/490] engine: client: vgui: rework loading VGUI and probing client for internal vgui support API --- engine/client/cl_game.c | 27 +- engine/client/cl_main.c | 3 - engine/client/cl_scrn.c | 8 +- engine/client/vgui/vgui_draw.c | 580 +++++++++++++++------------------ engine/client/vgui/vgui_draw.h | 16 +- 5 files changed, 295 insertions(+), 339 deletions(-) diff --git a/engine/client/cl_game.c b/engine/client/cl_game.c index 6bb50705..abe7c98d 100644 --- a/engine/client/cl_game.c +++ b/engine/client/cl_game.c @@ -3937,10 +3937,6 @@ qboolean CL_LoadProgs( const char *name ) clgame.mempool = Mem_AllocPool( "Client Edicts Zone" ); clgame.entities = NULL; - // NOTE: important stuff! - // vgui must startup BEFORE loading client.dll to avoid get error ERROR_NOACESS - // during LoadLibrary - VGui_Startup( name, gameui.globals->scrWidth, gameui.globals->scrHeight ); // a1ba: we need to check if client.dll has direct dependency on SDL2 // and if so, disable relative mouse mode @@ -3959,8 +3955,29 @@ qboolean CL_LoadProgs( const char *name ) clgame.client_dll_uses_sdl = true; #endif + // NOTE: important stuff! + // vgui must startup BEFORE loading client.dll to avoid get error ERROR_NOACESS + // during LoadLibrary + if( !GI->internal_vgui_support && VGui_LoadProgs( NULL )) + { + VGui_Startup( refState.width, refState.height ); + } + else + { + // we failed to load vgui_support, but let's probe client.dll for support anyway + GI->internal_vgui_support = true; + } + clgame.hInstance = COM_LoadLibrary( name, false, false ); - if( !clgame.hInstance ) return false; + + if( !clgame.hInstance ) + return false; + + // delayed vgui initialization for internal support + if( GI->internal_vgui_support && VGui_LoadProgs( NULL )) + { + VGui_Startup( refState.width, refState.height ); + } // clear exports for( func = cdll_exports; func && func->name; func++ ) diff --git a/engine/client/cl_main.c b/engine/client/cl_main.c index f974075e..fc46a8a1 100644 --- a/engine/client/cl_main.c +++ b/engine/client/cl_main.c @@ -3063,9 +3063,6 @@ void Host_ClientFrame( void ) // catch changes video settings VID_CheckChanges(); - // process VGUI - VGui_RunFrame (); - // update the screen SCR_UpdateScreen (); diff --git a/engine/client/cl_scrn.c b/engine/client/cl_scrn.c index ee212d46..b65c9e59 100644 --- a/engine/client/cl_scrn.c +++ b/engine/client/cl_scrn.c @@ -780,7 +780,6 @@ SCR_VidInit */ void SCR_VidInit( void ) { - string libpath; if( !ref.initialized ) // don't call VidInit too soon return; @@ -795,8 +794,11 @@ void SCR_VidInit( void ) gameui.globals->scrHeight = refState.height; } - COM_GetCommonLibraryPath( LIBRARY_CLIENT, libpath, sizeof( libpath )); - VGui_Startup( libpath, refState.width, refState.height ); + // notify vgui about screen size change + if( clgame.hInstance ) + { + VGui_Startup( refState.width, refState.height ); + } CL_ClearSpriteTextures(); // now all hud sprites are invalid diff --git a/engine/client/vgui/vgui_draw.c b/engine/client/vgui/vgui_draw.c index 4d732049..da5f2eb8 100644 --- a/engine/client/vgui/vgui_draw.c +++ b/engine/client/vgui/vgui_draw.c @@ -23,93 +23,101 @@ GNU General Public License for more details. #include "input.h" #include "platform/platform.h" -static enum VGUI_KeyCode s_pVirtualKeyTrans[256]; -static VGUI_DefaultCursor s_currentCursor = -1; -static HINSTANCE s_pVGuiSupport; // vgui_support library -static convar_t *vgui_utf8 = NULL; +CVAR_DEFINE_AUTO( vgui_utf8, "0", FCVAR_ARCHIVE, "enable utf-8 support for vgui text" ); -void GAME_EXPORT *VGUI_EngineMalloc(size_t size) +static void GAME_EXPORT *VGUI_EngineMalloc( size_t size ); +static void GAME_EXPORT VGUI_GetMousePos( int *, int * ); +static void GAME_EXPORT VGUI_CursorSelect( VGUI_DefaultCursor ); +static byte GAME_EXPORT VGUI_GetColor( int, int ); +static int GAME_EXPORT VGUI_UtfProcessChar( int in ); +static qboolean GAME_EXPORT VGUI_IsInGame( void ); + +static struct +{ + qboolean initialized; + vguiapi_t dllFuncs; + VGUI_DefaultCursor cursor; + + HINSTANCE hInstance; + + enum VGUI_KeyCode virtualKeyTrans[256]; +} vgui = +{ + false, + { + false, // Not initialized yet + NULL, // VGUI_DrawInit, + NULL, // VGUI_DrawShutdown, + NULL, // VGUI_SetupDrawingText, + NULL, // VGUI_SetupDrawingRect, + NULL, // VGUI_SetupDrawingImage, + NULL, // VGUI_BindTexture, + NULL, // VGUI_EnableTexture, + NULL, // VGUI_CreateTexture, + NULL, // VGUI_UploadTexture, + NULL, // VGUI_UploadTextureBlock, + NULL, // VGUI_DrawQuad, + NULL, // VGUI_GetTextureSizes, + NULL, // VGUI_GenerateTexture, + VGUI_EngineMalloc, + VGUI_CursorSelect, + VGUI_GetColor, + VGUI_IsInGame, + NULL, + VGUI_GetMousePos, + VGUI_UtfProcessChar, + Platform_GetClipboardText, + Platform_SetClipboardText, + Platform_GetKeyModifiers, + }, + -1 +}; + +static void GAME_EXPORT *VGUI_EngineMalloc( size_t size ) { return Z_Malloc( size ); } -qboolean GAME_EXPORT VGUI_IsInGame( void ) +static qboolean GAME_EXPORT VGUI_IsInGame( void ) { return cls.state == ca_active && cls.key_dest == key_game; } -void GAME_EXPORT VGUI_GetMousePos( int *_x, int *_y ) +static void GAME_EXPORT VGUI_GetMousePos( int *_x, int *_y ) { float xscale = (float)refState.width / (float)clgame.scrInfo.iWidth; float yscale = (float)refState.height / (float)clgame.scrInfo.iHeight; int x, y; Platform_GetMousePos( &x, &y ); - *_x = x / xscale, *_y = y / yscale; + *_x = x / xscale; + *_y = y / yscale; } -void GAME_EXPORT VGUI_CursorSelect( VGUI_DefaultCursor cursor ) +static void GAME_EXPORT VGUI_CursorSelect( VGUI_DefaultCursor cursor ) { - if( s_currentCursor != cursor ) + if( vgui.cursor != cursor ) Platform_SetCursorType( cursor ); } -byte GAME_EXPORT VGUI_GetColor( int i, int j) +static byte GAME_EXPORT VGUI_GetColor( int i, int j ) { return g_color_table[i][j]; } -// Define and initialize vgui API -int GAME_EXPORT VGUI_UtfProcessChar( int in ) +static int GAME_EXPORT VGUI_UtfProcessChar( int in ) { - if( CVAR_TO_BOOL( vgui_utf8 )) + if( vgui_utf8.value ) return Con_UtfProcessCharForce( in ); - else - return in; + return in; } -vguiapi_t vgui = -{ - false, // Not initialized yet - NULL, // VGUI_DrawInit, - NULL, // VGUI_DrawShutdown, - NULL, // VGUI_SetupDrawingText, - NULL, // VGUI_SetupDrawingRect, - NULL, // VGUI_SetupDrawingImage, - NULL, // VGUI_BindTexture, - NULL, // VGUI_EnableTexture, - NULL, // VGUI_CreateTexture, - NULL, // VGUI_UploadTexture, - NULL, // VGUI_UploadTextureBlock, - NULL, // VGUI_DrawQuad, - NULL, // VGUI_GetTextureSizes, - NULL, // VGUI_GenerateTexture, - VGUI_EngineMalloc, - VGUI_CursorSelect, - VGUI_GetColor, - VGUI_IsInGame, - NULL, - VGUI_GetMousePos, - VGUI_UtfProcessChar, - Platform_GetClipboardText, - Platform_SetClipboardText, - Platform_GetKeyModifiers, - NULL, - NULL, - NULL, - NULL, - NULL, - NULL, - NULL, - NULL -}; - qboolean VGui_IsActive( void ) { return vgui.initialized; } -void VGui_FillAPIFromRef( vguiapi_t *to, const ref_interface_t *from ) +static void VGui_FillAPIFromRef( vguiapi_t *to, const ref_interface_t *from ) { to->DrawInit = from->VGUI_DrawInit; to->DrawShutdown = from->VGUI_DrawShutdown; @@ -126,136 +134,86 @@ void VGui_FillAPIFromRef( vguiapi_t *to, const ref_interface_t *from ) to->GenerateTexture = from->VGUI_GenerateTexture; } +void VGui_RegisterCvars( void ) +{ + Cvar_RegisterVariable( &vgui_utf8 ); +} + +qboolean VGui_LoadProgs( HINSTANCE hInstance ) +{ + void (*F)( vguiapi_t* ); + qboolean client = hInstance != NULL; + + // not loading interface from client.dll, load vgui_support.dll instead + if( !client ) + { + string vguiloader, vguilib; + + // HACKHACK: try to load path from custom path + // to support having different versions of VGUI + if( Sys_GetParmFromCmdLine( "-vguilib", vguilib ) && !COM_LoadLibrary( vguilib, false, false )) + { + Con_Reportf( S_WARN "VGUI preloading failed. Default library will be used! Reason: %s", COM_GetLibraryError()); + } + + if( !Sys_GetParmFromCmdLine( "-vguiloader", vguiloader )) + { + Q_strncpy( vguiloader, VGUI_SUPPORT_DLL, sizeof( vguiloader )); + } + + hInstance = vgui.hInstance = COM_LoadLibrary( vguiloader, false, false ); + + if( !vgui.hInstance ) + { + if( FS_FileExists( vguiloader, false )) + Con_Reportf( S_ERROR "Failed to load vgui_support library: %s\n", COM_GetLibraryError() ); + else Con_Reportf( "vgui_support: not found\n" ); + + return false; + } + } + + // try legacy API first + F = COM_GetProcAddress( hInstance, client ? "InitVGUISupportAPI" : "InitAPI" ); + + if( F ) + { + VGui_FillAPIFromRef( &vgui.dllFuncs, &ref.dllFuncs ); + F( &vgui.dllFuncs ); + + vgui.initialized = vgui.dllFuncs.initialized = true; + Con_Reportf( "vgui_support: initialized legacy API in %s module\n", client ? "client" : "support" ); + + return true; + } + + Con_Reportf( S_ERROR "Failed to find VGUI support API entry point in %s module\n", client ? "client" : "support" ); + return false; +} + /* ================ VGui_Startup -Load vgui_support library and call VGui_Startup ================ */ -void VGui_Startup( const char *clientlib, int width, int height ) +void VGui_Startup( int width, int height ) { - static qboolean failed = false; - - void (*F) ( vguiapi_t * ); - char vguiloader[256]; - char vguilib[256]; - - vguiloader[0] = vguilib[0] = '\0'; - - if( failed ) + // vgui not initialized from both support and client modules, skip + if( !vgui.initialized ) return; - if( !vgui.initialized ) - { - vgui_utf8 = Cvar_Get( "vgui_utf8", "0", FCVAR_ARCHIVE, "enable utf-8 support for vgui text" ); + height = Q_min( 480, height ); - VGui_FillAPIFromRef( &vgui, &ref.dllFuncs ); + if( width <= 640 ) width = 640; + else if( width <= 800 ) width = 800; + else if( width <= 1024 ) width = 1024; + else if( width <= 1152 ) width = 1152; + else if( width <= 1280 ) width = 1280; + else if( width <= 1600 ) width = 1600; - s_pVGuiSupport = COM_LoadLibrary( clientlib, false, false ); - - if( s_pVGuiSupport ) - { - F = COM_GetProcAddress( s_pVGuiSupport, "InitVGUISupportAPI" ); - if( F ) - { - F( &vgui ); - vgui.initialized = true; - Con_Reportf( "vgui_support: found internal client support\n" ); - } - else - { - COM_FreeLibrary( s_pVGuiSupport ); - } - } - - if( !vgui.initialized ) - { - // HACKHACK: load vgui with correct path first if specified. - // it will be reused while resolving vgui support and client deps - if( Sys_GetParmFromCmdLine( "-vguilib", vguilib )) - { - if( Q_strstr( vguilib, ".dll" )) - Q_strncpy( vguiloader, "vgui_support.dll", 256 ); - else - Q_strncpy( vguiloader, VGUI_SUPPORT_DLL, 256 ); - - if( !COM_LoadLibrary( vguilib, false, false )) - Con_Reportf( S_WARN "VGUI preloading failed. Default library will be used! Reason: %s\n", COM_GetLibraryError() ); - } - - if( Q_strstr( clientlib, ".dll" )) - Q_strncpy( vguiloader, "vgui_support.dll", 256 ); - - if( !vguiloader[0] && !Sys_GetParmFromCmdLine( "-vguiloader", vguiloader )) - Q_strncpy( vguiloader, VGUI_SUPPORT_DLL, 256 ); - - s_pVGuiSupport = COM_LoadLibrary( vguiloader, false, false ); - - if( !s_pVGuiSupport ) - { - s_pVGuiSupport = COM_LoadLibrary( va( "../%s", vguiloader ), false, false ); - } - - if( !s_pVGuiSupport ) - { - if( FS_FileExists( vguiloader, false )) - { - Con_Reportf( S_ERROR "Failed to load vgui_support library: %s\n", COM_GetLibraryError() ); - } - else - { - Con_Reportf( "vgui_support: not found\n" ); - } - failed = true; - } - else - { - F = COM_GetProcAddress( s_pVGuiSupport, "InitAPI" ); - if( F ) - { - F( &vgui ); - vgui.initialized = true; - } - else - { - Con_Reportf( S_ERROR "Failed to find vgui_support library entry point!\n" ); - failed = true; - COM_FreeLibrary( s_pVGuiSupport ); - } - } - } - } - - if( height < 480 ) - height = 480; - - if( width <= 640 ) - width = 640; - else if( width <= 800 ) - width = 800; - else if( width <= 1024 ) - width = 1024; - else if( width <= 1152 ) - width = 1152; - else if( width <= 1280 ) - width = 1280; - else if( width <= 1600 ) - width = 1600; -#ifdef XASH_DLL_LOADER - else if ( Q_strstr( vguiloader, ".dll" ) ) - width = 1600; -#endif - - - if( vgui.initialized ) - { - vgui.Startup( width, height ); - } - else if ( COM_CheckString( clientlib ) ) - { - failed = true; - } + if( vgui.dllFuncs.Startup ) + vgui.dllFuncs.Startup( width, height ); } @@ -269,146 +227,138 @@ Unload vgui_support library and call VGui_Shutdown */ void VGui_Shutdown( void ) { - if( vgui.Shutdown ) - vgui.Shutdown(); + if( vgui.dllFuncs.Shutdown ) + vgui.dllFuncs.Shutdown(); - if( s_pVGuiSupport ) - COM_FreeLibrary( s_pVGuiSupport ); - s_pVGuiSupport = NULL; + if( vgui.hInstance ) + COM_FreeLibrary( vgui.hInstance ); + vgui.hInstance = NULL; vgui.initialized = false; } -void VGUI_InitKeyTranslationTable( void ) +static void VGUI_InitKeyTranslationTable( void ) { - static qboolean bInitted = false; + static qboolean initialized = false; - if( bInitted ) - return; - bInitted = true; + if( initialized ) return; + + initialized = true; // set virtual key translation table - memset( s_pVirtualKeyTrans, -1, sizeof( s_pVirtualKeyTrans ) ); + memset( vgui.virtualKeyTrans, -1, sizeof( vgui.virtualKeyTrans ) ); - s_pVirtualKeyTrans['0'] = KEY_0; - s_pVirtualKeyTrans['1'] = KEY_1; - s_pVirtualKeyTrans['2'] = KEY_2; - s_pVirtualKeyTrans['3'] = KEY_3; - s_pVirtualKeyTrans['4'] = KEY_4; - s_pVirtualKeyTrans['5'] = KEY_5; - s_pVirtualKeyTrans['6'] = KEY_6; - s_pVirtualKeyTrans['7'] = KEY_7; - s_pVirtualKeyTrans['8'] = KEY_8; - s_pVirtualKeyTrans['9'] = KEY_9; - s_pVirtualKeyTrans['A'] = s_pVirtualKeyTrans['a'] = KEY_A; - s_pVirtualKeyTrans['B'] = s_pVirtualKeyTrans['b'] = KEY_B; - s_pVirtualKeyTrans['C'] = s_pVirtualKeyTrans['c'] = KEY_C; - s_pVirtualKeyTrans['D'] = s_pVirtualKeyTrans['d'] = KEY_D; - s_pVirtualKeyTrans['E'] = s_pVirtualKeyTrans['e'] = KEY_E; - s_pVirtualKeyTrans['F'] = s_pVirtualKeyTrans['f'] = KEY_F; - s_pVirtualKeyTrans['G'] = s_pVirtualKeyTrans['g'] = KEY_G; - s_pVirtualKeyTrans['H'] = s_pVirtualKeyTrans['h'] = KEY_H; - s_pVirtualKeyTrans['I'] = s_pVirtualKeyTrans['i'] = KEY_I; - s_pVirtualKeyTrans['J'] = s_pVirtualKeyTrans['j'] = KEY_J; - s_pVirtualKeyTrans['K'] = s_pVirtualKeyTrans['k'] = KEY_K; - s_pVirtualKeyTrans['L'] = s_pVirtualKeyTrans['l'] = KEY_L; - s_pVirtualKeyTrans['M'] = s_pVirtualKeyTrans['m'] = KEY_M; - s_pVirtualKeyTrans['N'] = s_pVirtualKeyTrans['n'] = KEY_N; - s_pVirtualKeyTrans['O'] = s_pVirtualKeyTrans['o'] = KEY_O; - s_pVirtualKeyTrans['P'] = s_pVirtualKeyTrans['p'] = KEY_P; - s_pVirtualKeyTrans['Q'] = s_pVirtualKeyTrans['q'] = KEY_Q; - s_pVirtualKeyTrans['R'] = s_pVirtualKeyTrans['r'] = KEY_R; - s_pVirtualKeyTrans['S'] = s_pVirtualKeyTrans['s'] = KEY_S; - s_pVirtualKeyTrans['T'] = s_pVirtualKeyTrans['t'] = KEY_T; - s_pVirtualKeyTrans['U'] = s_pVirtualKeyTrans['u'] = KEY_U; - s_pVirtualKeyTrans['V'] = s_pVirtualKeyTrans['v'] = KEY_V; - s_pVirtualKeyTrans['W'] = s_pVirtualKeyTrans['w'] = KEY_W; - s_pVirtualKeyTrans['X'] = s_pVirtualKeyTrans['x'] = KEY_X; - s_pVirtualKeyTrans['Y'] = s_pVirtualKeyTrans['y'] = KEY_Y; - s_pVirtualKeyTrans['Z'] = s_pVirtualKeyTrans['z'] = KEY_Z; + // TODO: engine keys are not enough here! + // make crossplatform way to pass SDL keys here - s_pVirtualKeyTrans[K_KP_5 - 5] = KEY_PAD_0; - s_pVirtualKeyTrans[K_KP_5 - 4] = KEY_PAD_1; - s_pVirtualKeyTrans[K_KP_5 - 3] = KEY_PAD_2; - s_pVirtualKeyTrans[K_KP_5 - 2] = KEY_PAD_3; - s_pVirtualKeyTrans[K_KP_5 - 1] = KEY_PAD_4; - s_pVirtualKeyTrans[K_KP_5 - 0] = KEY_PAD_5; - s_pVirtualKeyTrans[K_KP_5 + 1] = KEY_PAD_6; - s_pVirtualKeyTrans[K_KP_5 + 2] = KEY_PAD_7; - s_pVirtualKeyTrans[K_KP_5 + 3] = KEY_PAD_8; - s_pVirtualKeyTrans[K_KP_5 + 4] = KEY_PAD_9; - s_pVirtualKeyTrans[K_KP_SLASH] = KEY_PAD_DIVIDE; - s_pVirtualKeyTrans['*'] = KEY_PAD_MULTIPLY; - s_pVirtualKeyTrans[K_KP_MINUS] = KEY_PAD_MINUS; - s_pVirtualKeyTrans[K_KP_PLUS] = KEY_PAD_PLUS; - s_pVirtualKeyTrans[K_KP_ENTER] = KEY_PAD_ENTER; - //s_pVirtualKeyTrans[K_KP_DECIMAL] = KEY_PAD_DECIMAL; - s_pVirtualKeyTrans['['] = KEY_LBRACKET; - s_pVirtualKeyTrans[']'] = KEY_RBRACKET; - s_pVirtualKeyTrans[';'] = KEY_SEMICOLON; - s_pVirtualKeyTrans['\''] = KEY_APOSTROPHE; - s_pVirtualKeyTrans['`'] = KEY_BACKQUOTE; - s_pVirtualKeyTrans[','] = KEY_COMMA; - s_pVirtualKeyTrans['.'] = KEY_PERIOD; - s_pVirtualKeyTrans[K_KP_SLASH] = KEY_SLASH; - s_pVirtualKeyTrans['\\'] = KEY_BACKSLASH; - s_pVirtualKeyTrans['-'] = KEY_MINUS; - s_pVirtualKeyTrans['='] = KEY_EQUAL; - s_pVirtualKeyTrans[K_ENTER] = KEY_ENTER; - s_pVirtualKeyTrans[K_SPACE] = KEY_SPACE; - s_pVirtualKeyTrans[K_BACKSPACE] = KEY_BACKSPACE; - s_pVirtualKeyTrans[K_TAB] = KEY_TAB; - s_pVirtualKeyTrans[K_CAPSLOCK] = KEY_CAPSLOCK; - s_pVirtualKeyTrans[K_KP_NUMLOCK] = KEY_NUMLOCK; - s_pVirtualKeyTrans[K_ESCAPE] = KEY_ESCAPE; - //s_pVirtualKeyTrans[K_KP_SCROLLLOCK] = KEY_SCROLLLOCK; - s_pVirtualKeyTrans[K_INS] = KEY_INSERT; - s_pVirtualKeyTrans[K_DEL] = KEY_DELETE; - s_pVirtualKeyTrans[K_HOME] = KEY_HOME; - s_pVirtualKeyTrans[K_END] = KEY_END; - s_pVirtualKeyTrans[K_PGUP] = KEY_PAGEUP; - s_pVirtualKeyTrans[K_PGDN] = KEY_PAGEDOWN; - s_pVirtualKeyTrans[K_PAUSE] = KEY_BREAK; - //s_pVirtualKeyTrans[K_SHIFT] = KEY_RSHIFT; - s_pVirtualKeyTrans[K_SHIFT] = KEY_LSHIFT; // SHIFT -> left SHIFT - //s_pVirtualKeyTrans[SDLK_RALT] = KEY_RALT; - s_pVirtualKeyTrans[K_ALT] = KEY_LALT; // ALT -> left ALT - //s_pVirtualKeyTrans[SDLK_RCTRL] = KEY_RCONTROL; - s_pVirtualKeyTrans[K_CTRL] = KEY_LCONTROL; // CTRL -> left CTRL - s_pVirtualKeyTrans[K_WIN] = KEY_LWIN; - //s_pVirtualKeyTrans[SDLK_APPLICATION] = KEY_RWIN; - //s_pVirtualKeyTrans[K_WIN] = KEY_APP; - s_pVirtualKeyTrans[K_UPARROW] = KEY_UP; - s_pVirtualKeyTrans[K_LEFTARROW] = KEY_LEFT; - s_pVirtualKeyTrans[K_DOWNARROW] = KEY_DOWN; - s_pVirtualKeyTrans[K_RIGHTARROW] = KEY_RIGHT; - s_pVirtualKeyTrans[K_F1] = KEY_F1; - s_pVirtualKeyTrans[K_F2] = KEY_F2; - s_pVirtualKeyTrans[K_F3] = KEY_F3; - s_pVirtualKeyTrans[K_F4] = KEY_F4; - s_pVirtualKeyTrans[K_F5] = KEY_F5; - s_pVirtualKeyTrans[K_F6] = KEY_F6; - s_pVirtualKeyTrans[K_F7] = KEY_F7; - s_pVirtualKeyTrans[K_F8] = KEY_F8; - s_pVirtualKeyTrans[K_F9] = KEY_F9; - s_pVirtualKeyTrans[K_F10] = KEY_F10; - s_pVirtualKeyTrans[K_F11] = KEY_F11; - s_pVirtualKeyTrans[K_F12] = KEY_F12; + vgui.virtualKeyTrans['0'] = KEY_0; + vgui.virtualKeyTrans['1'] = KEY_1; + vgui.virtualKeyTrans['2'] = KEY_2; + vgui.virtualKeyTrans['3'] = KEY_3; + vgui.virtualKeyTrans['4'] = KEY_4; + vgui.virtualKeyTrans['5'] = KEY_5; + vgui.virtualKeyTrans['6'] = KEY_6; + vgui.virtualKeyTrans['7'] = KEY_7; + vgui.virtualKeyTrans['8'] = KEY_8; + vgui.virtualKeyTrans['9'] = KEY_9; + vgui.virtualKeyTrans['A'] = vgui.virtualKeyTrans['a'] = KEY_A; + vgui.virtualKeyTrans['B'] = vgui.virtualKeyTrans['b'] = KEY_B; + vgui.virtualKeyTrans['C'] = vgui.virtualKeyTrans['c'] = KEY_C; + vgui.virtualKeyTrans['D'] = vgui.virtualKeyTrans['d'] = KEY_D; + vgui.virtualKeyTrans['E'] = vgui.virtualKeyTrans['e'] = KEY_E; + vgui.virtualKeyTrans['F'] = vgui.virtualKeyTrans['f'] = KEY_F; + vgui.virtualKeyTrans['G'] = vgui.virtualKeyTrans['g'] = KEY_G; + vgui.virtualKeyTrans['H'] = vgui.virtualKeyTrans['h'] = KEY_H; + vgui.virtualKeyTrans['I'] = vgui.virtualKeyTrans['i'] = KEY_I; + vgui.virtualKeyTrans['J'] = vgui.virtualKeyTrans['j'] = KEY_J; + vgui.virtualKeyTrans['K'] = vgui.virtualKeyTrans['k'] = KEY_K; + vgui.virtualKeyTrans['L'] = vgui.virtualKeyTrans['l'] = KEY_L; + vgui.virtualKeyTrans['M'] = vgui.virtualKeyTrans['m'] = KEY_M; + vgui.virtualKeyTrans['N'] = vgui.virtualKeyTrans['n'] = KEY_N; + vgui.virtualKeyTrans['O'] = vgui.virtualKeyTrans['o'] = KEY_O; + vgui.virtualKeyTrans['P'] = vgui.virtualKeyTrans['p'] = KEY_P; + vgui.virtualKeyTrans['Q'] = vgui.virtualKeyTrans['q'] = KEY_Q; + vgui.virtualKeyTrans['R'] = vgui.virtualKeyTrans['r'] = KEY_R; + vgui.virtualKeyTrans['S'] = vgui.virtualKeyTrans['s'] = KEY_S; + vgui.virtualKeyTrans['T'] = vgui.virtualKeyTrans['t'] = KEY_T; + vgui.virtualKeyTrans['U'] = vgui.virtualKeyTrans['u'] = KEY_U; + vgui.virtualKeyTrans['V'] = vgui.virtualKeyTrans['v'] = KEY_V; + vgui.virtualKeyTrans['W'] = vgui.virtualKeyTrans['w'] = KEY_W; + vgui.virtualKeyTrans['X'] = vgui.virtualKeyTrans['x'] = KEY_X; + vgui.virtualKeyTrans['Y'] = vgui.virtualKeyTrans['y'] = KEY_Y; + vgui.virtualKeyTrans['Z'] = vgui.virtualKeyTrans['z'] = KEY_Z; + + vgui.virtualKeyTrans[K_KP_5 - 5] = KEY_PAD_0; + vgui.virtualKeyTrans[K_KP_5 - 4] = KEY_PAD_1; + vgui.virtualKeyTrans[K_KP_5 - 3] = KEY_PAD_2; + vgui.virtualKeyTrans[K_KP_5 - 2] = KEY_PAD_3; + vgui.virtualKeyTrans[K_KP_5 - 1] = KEY_PAD_4; + vgui.virtualKeyTrans[K_KP_5 - 0] = KEY_PAD_5; + vgui.virtualKeyTrans[K_KP_5 + 1] = KEY_PAD_6; + vgui.virtualKeyTrans[K_KP_5 + 2] = KEY_PAD_7; + vgui.virtualKeyTrans[K_KP_5 + 3] = KEY_PAD_8; + vgui.virtualKeyTrans[K_KP_5 + 4] = KEY_PAD_9; + vgui.virtualKeyTrans[K_KP_SLASH] = KEY_PAD_DIVIDE; + vgui.virtualKeyTrans['*'] = KEY_PAD_MULTIPLY; + vgui.virtualKeyTrans[K_KP_MINUS] = KEY_PAD_MINUS; + vgui.virtualKeyTrans[K_KP_PLUS] = KEY_PAD_PLUS; + vgui.virtualKeyTrans[K_KP_ENTER] = KEY_PAD_ENTER; + vgui.virtualKeyTrans[K_KP_NUMLOCK] = KEY_NUMLOCK; + vgui.virtualKeyTrans['['] = KEY_LBRACKET; + vgui.virtualKeyTrans[']'] = KEY_RBRACKET; + vgui.virtualKeyTrans[';'] = KEY_SEMICOLON; + vgui.virtualKeyTrans['`'] = KEY_BACKQUOTE; + vgui.virtualKeyTrans[','] = KEY_COMMA; + vgui.virtualKeyTrans['.'] = KEY_PERIOD; + vgui.virtualKeyTrans['-'] = KEY_MINUS; + vgui.virtualKeyTrans['='] = KEY_EQUAL; + vgui.virtualKeyTrans['/'] = KEY_SLASH; + vgui.virtualKeyTrans['\\'] = KEY_BACKSLASH; + vgui.virtualKeyTrans['\''] = KEY_APOSTROPHE; + vgui.virtualKeyTrans[K_TAB] = KEY_TAB; + vgui.virtualKeyTrans[K_ENTER] = KEY_ENTER; + vgui.virtualKeyTrans[K_SPACE] = KEY_SPACE; + vgui.virtualKeyTrans[K_CAPSLOCK] = KEY_CAPSLOCK; + vgui.virtualKeyTrans[K_BACKSPACE] = KEY_BACKSPACE; + vgui.virtualKeyTrans[K_ESCAPE] = KEY_ESCAPE; + vgui.virtualKeyTrans[K_INS] = KEY_INSERT; + vgui.virtualKeyTrans[K_DEL] = KEY_DELETE; + vgui.virtualKeyTrans[K_HOME] = KEY_HOME; + vgui.virtualKeyTrans[K_END] = KEY_END; + vgui.virtualKeyTrans[K_PGUP] = KEY_PAGEUP; + vgui.virtualKeyTrans[K_PGDN] = KEY_PAGEDOWN; + vgui.virtualKeyTrans[K_PAUSE] = KEY_BREAK; + vgui.virtualKeyTrans[K_SHIFT] = KEY_LSHIFT; // SHIFT -> left SHIFT + vgui.virtualKeyTrans[K_ALT] = KEY_LALT; // ALT -> left ALT + vgui.virtualKeyTrans[K_CTRL] = KEY_LCONTROL; // CTRL -> left CTRL + vgui.virtualKeyTrans[K_WIN] = KEY_LWIN; + vgui.virtualKeyTrans[K_UPARROW] = KEY_UP; + vgui.virtualKeyTrans[K_LEFTARROW] = KEY_LEFT; + vgui.virtualKeyTrans[K_DOWNARROW] = KEY_DOWN; + vgui.virtualKeyTrans[K_RIGHTARROW] = KEY_RIGHT; + vgui.virtualKeyTrans[K_F1] = KEY_F1; + vgui.virtualKeyTrans[K_F2] = KEY_F2; + vgui.virtualKeyTrans[K_F3] = KEY_F3; + vgui.virtualKeyTrans[K_F4] = KEY_F4; + vgui.virtualKeyTrans[K_F5] = KEY_F5; + vgui.virtualKeyTrans[K_F6] = KEY_F6; + vgui.virtualKeyTrans[K_F7] = KEY_F7; + vgui.virtualKeyTrans[K_F8] = KEY_F8; + vgui.virtualKeyTrans[K_F9] = KEY_F9; + vgui.virtualKeyTrans[K_F10] = KEY_F10; + vgui.virtualKeyTrans[K_F11] = KEY_F11; + vgui.virtualKeyTrans[K_F12] = KEY_F12; } -enum VGUI_KeyCode VGUI_MapKey( int keyCode ) +static enum VGUI_KeyCode VGUI_MapKey( int keyCode ) { VGUI_InitKeyTranslationTable(); - if( keyCode < 0 || keyCode >= ARRAYSIZE( s_pVirtualKeyTrans )) - { - return (enum VGUI_KeyCode)-1; - } - else - { - return s_pVirtualKeyTrans[keyCode]; - } + if( keyCode >= 0 && keyCode < ARRAYSIZE( vgui.virtualKeyTrans )) + return vgui.virtualKeyTrans[keyCode]; + + return (enum VGUI_KeyCode)-1; } void VGui_MouseEvent( int key, int clicks ) @@ -416,7 +366,7 @@ void VGui_MouseEvent( int key, int clicks ) enum VGUI_MouseAction mact; enum VGUI_MouseCode code; - if( !vgui.initialized ) + if( !vgui.dllFuncs.Mouse ) return; switch( key ) @@ -434,22 +384,22 @@ void VGui_MouseEvent( int key, int clicks ) else mact = MA_RELEASED; - vgui.Mouse( mact, code ); + vgui.dllFuncs.Mouse( mact, code ); } void VGui_MWheelEvent( int y ) { - if( !vgui.initialized ) + if( !vgui.dllFuncs.Mouse ) return; - vgui.Mouse( MA_WHEEL, y ); + vgui.dllFuncs.Mouse( MA_WHEEL, y ); } void VGui_KeyEvent( int key, int down ) { enum VGUI_KeyCode code; - if( !vgui.initialized ) + if( !vgui.dllFuncs.Key ) return; if(( code = VGUI_MapKey( key )) < 0 ) @@ -457,47 +407,43 @@ void VGui_KeyEvent( int key, int down ) if( down ) { - vgui.Key( KA_PRESSED, code ); - vgui.Key( KA_TYPED, code ); + vgui.dllFuncs.Key( KA_PRESSED, code ); + vgui.dllFuncs.Key( KA_TYPED, code ); } - else vgui.Key( KA_RELEASED, code ); + else vgui.dllFuncs.Key( KA_RELEASED, code ); } void VGui_MouseMove( int x, int y ) { - if( vgui.initialized ) + if( vgui.dllFuncs.MouseMove ) { float xscale = (float)refState.width / (float)clgame.scrInfo.iWidth; float yscale = (float)refState.height / (float)clgame.scrInfo.iHeight; - vgui.MouseMove( x / xscale, y / yscale ); + vgui.dllFuncs.MouseMove( x / xscale, y / yscale ); } } void VGui_Paint( void ) { - if( vgui.initialized ) - vgui.Paint(); -} - -void VGui_RunFrame( void ) -{ - //stub + if( vgui.dllFuncs.Paint ) + vgui.dllFuncs.Paint(); } void VGui_UpdateInternalCursorState( VGUI_DefaultCursor cursorType ) { - s_currentCursor = cursorType; + vgui.cursor = cursorType; } void *GAME_EXPORT VGui_GetPanel( void ) { - if( vgui.initialized ) - return vgui.GetPanel(); + if( vgui.dllFuncs.GetPanel ) + return vgui.dllFuncs.GetPanel(); return NULL; } void VGui_ReportTextInput( const char *text ) { - if ( vgui.initialized ) - vgui.TextInput( text ); + if( vgui.dllFuncs.TextInput ) + vgui.dllFuncs.TextInput( text ); } + diff --git a/engine/client/vgui/vgui_draw.h b/engine/client/vgui/vgui_draw.h index 2cdaf1af..583c0fdc 100644 --- a/engine/client/vgui/vgui_draw.h +++ b/engine/client/vgui/vgui_draw.h @@ -16,16 +16,12 @@ GNU General Public License for more details. #ifndef VGUI_DRAW_H #define VGUI_DRAW_H -#ifdef __cplusplus -extern "C" { -#endif - -#include "port.h" - // // vgui_draw.c // -void VGui_Startup( const char *clientlib, int width, int height ); +void VGui_RegisterCvars( void ); +qboolean VGui_LoadProgs( HINSTANCE hInstance ); +void VGui_Startup( int width, int height ); void VGui_Shutdown( void ); void VGui_Paint( void ); void VGui_RunFrame( void ); @@ -37,7 +33,5 @@ qboolean VGui_IsActive( void ); void *VGui_GetPanel( void ); void VGui_ReportTextInput( const char *text ); void VGui_UpdateInternalCursorState( VGUI_DefaultCursor cursorType ); -#ifdef __cplusplus -} -#endif -#endif//VGUI_DRAW_H + +#endif // VGUI_DRAW_H From e9da43666c4db1e18970b2b16d4fd1a5071202a3 Mon Sep 17 00:00:00 2001 From: Alibek Omarov Date: Mon, 14 Nov 2022 20:08:01 +0300 Subject: [PATCH 169/490] engine: client: fix loading internal vgui (thx @SNMetamorph) --- engine/client/cl_game.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/engine/client/cl_game.c b/engine/client/cl_game.c index abe7c98d..76a84a12 100644 --- a/engine/client/cl_game.c +++ b/engine/client/cl_game.c @@ -3974,7 +3974,7 @@ qboolean CL_LoadProgs( const char *name ) return false; // delayed vgui initialization for internal support - if( GI->internal_vgui_support && VGui_LoadProgs( NULL )) + if( GI->internal_vgui_support && VGui_LoadProgs( clgame.hInstance )) { VGui_Startup( refState.width, refState.height ); } From ae94a6aebaf6eaeb081be45fac4d967a8e92360d Mon Sep 17 00:00:00 2001 From: SNMetamorph <25657591+SNMetamorph@users.noreply.github.com> Date: Mon, 14 Nov 2022 20:39:01 +0400 Subject: [PATCH 170/490] engine: client: fixed client VGUI API shutdown --- engine/client/cl_game.c | 3 +++ engine/client/cl_main.c | 4 +++- 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/engine/client/cl_game.c b/engine/client/cl_game.c index 76a84a12..8fff1123 100644 --- a/engine/client/cl_game.c +++ b/engine/client/cl_game.c @@ -3898,6 +3898,9 @@ void CL_UnloadProgs( void ) { if( !clgame.hInstance ) return; + if( GI->internal_vgui_support ) + VGui_Shutdown(); + CL_FreeEdicts(); CL_FreeTempEnts(); CL_FreeViewBeams(); diff --git a/engine/client/cl_main.c b/engine/client/cl_main.c index fc46a8a1..5666ed8a 100644 --- a/engine/client/cl_main.c +++ b/engine/client/cl_main.c @@ -3138,7 +3138,9 @@ void CL_Shutdown( void ) CL_UnloadProgs (); cls.initialized = false; - VGui_Shutdown(); + // for client-side VGUI support we use other order + if( !GI->internal_vgui_support ) + VGui_Shutdown(); FS_Delete( "demoheader.tmp" ); // remove tmp file SCR_FreeCinematic (); // release AVI's *after* client.dll because custom renderer may use them From c2a24fbbce472cf3e88cc903576a5288ff52adc2 Mon Sep 17 00:00:00 2001 From: Alibek Omarov Date: Mon, 14 Nov 2022 20:12:10 +0300 Subject: [PATCH 171/490] engine: client: put internal vgui shutdown AFTER HUD_Shutdown, to simulate default, external behavior --- engine/client/cl_game.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/engine/client/cl_game.c b/engine/client/cl_game.c index 8fff1123..7a054b18 100644 --- a/engine/client/cl_game.c +++ b/engine/client/cl_game.c @@ -3898,9 +3898,6 @@ void CL_UnloadProgs( void ) { if( !clgame.hInstance ) return; - if( GI->internal_vgui_support ) - VGui_Shutdown(); - CL_FreeEdicts(); CL_FreeTempEnts(); CL_FreeViewBeams(); @@ -3912,6 +3909,9 @@ void CL_UnloadProgs( void ) if( Q_stricmp( GI->gamefolder, "hlfx" ) || GI->version != 0.5f ) clgame.dllFuncs.pfnShutdown(); + if( GI->internal_vgui_support ) + VGui_Shutdown(); + Cvar_FullSet( "cl_background", "0", FCVAR_READ_ONLY ); Cvar_FullSet( "host_clientloaded", "0", FCVAR_READ_ONLY ); From 927dccc71df619fb7d06bec82bc0170d6d204569 Mon Sep 17 00:00:00 2001 From: Alibek Omarov Date: Wed, 16 Nov 2022 04:19:07 +0300 Subject: [PATCH 172/490] Documentation: add simple instructions on how to cross-compile for Windows from normal operating systems --- Documentation/cross-compiling-for-windows-with-wine.md | 9 +++++++++ 1 file changed, 9 insertions(+) create mode 100644 Documentation/cross-compiling-for-windows-with-wine.md diff --git a/Documentation/cross-compiling-for-windows-with-wine.md b/Documentation/cross-compiling-for-windows-with-wine.md new file mode 100644 index 00000000..734fa529 --- /dev/null +++ b/Documentation/cross-compiling-for-windows-with-wine.md @@ -0,0 +1,9 @@ +# Cross-compiling for Windows with Wine + +This can be useful to test engine in Wine without using virtual machines or dual-booting to Windows. + +0. Clone and install https://github.com/mstorsjo/msvc-wine (you can skip CMake part) +1. Set environment variable WINE_MSVC_PATH to the path to installed MSVC toolchain +2. Pre-load wine: `wineserver -k; wineserver -p; wine64 wineboot` +3. Run `./waf configure -T --enable-wine-msvc --sdl2=../SDL2_VC`. Configuration step will take more time than usual. +4. Follow other typical steps to build from console, except keep in mind you're in decent Operating System now. From aa702f06789cab8e1fc3ee81ac28d6f88a1ec97c Mon Sep 17 00:00:00 2001 From: Alibek Omarov Date: Wed, 16 Nov 2022 04:19:46 +0300 Subject: [PATCH 173/490] scripts: waifulib: xcompile: add msvc-wine support in cross-compile helper script --- scripts/waifulib/xcompile.py | 39 ++++++++++++++++++++++++++---------- 1 file changed, 28 insertions(+), 11 deletions(-) diff --git a/scripts/waifulib/xcompile.py b/scripts/waifulib/xcompile.py index b9ce398f..981a081d 100644 --- a/scripts/waifulib/xcompile.py +++ b/scripts/waifulib/xcompile.py @@ -349,13 +349,13 @@ class Android: return ldflags def options(opt): - android = opt.add_option_group('Android options') - android.add_option('--android', action='store', dest='ANDROID_OPTS', default=None, + xc = opt.add_option_group('Cross compile options') + xc.add_option('--android', action='store', dest='ANDROID_OPTS', default=None, help='enable building for android, format: --android=,,, example: --android=armeabi-v7a-hard,4.9,9') - - magx = opt.add_option_group('MotoMAGX options') - magx.add_option('--enable-magx', action = 'store_true', dest = 'MAGX', default = False, - help = 'enable targetting for MotoMAGX phones [default: %default]') + xc.add_option('--enable-magx', action='store_true', dest='MAGX', default=False, + help='enable building for Motorola MAGX [default: %default]') + xc.add_option('--enable-msvc-wine', action='store_true', dest='MSVC_WINE', default=False, + help='enable building with MSVC using Wine [default: %default]') def configure(conf): if conf.options.ANDROID_OPTS: @@ -389,17 +389,28 @@ def configure(conf): conf.msg('... C/C++ flags', ' '.join(android.cflags()).replace(android.ndk_home, '$NDK/')) conf.msg('... link flags', ' '.join(android.linkflags()).replace(android.ndk_home, '$NDK/')) conf.msg('... ld flags', ' '.join(android.ldflags()).replace(android.ndk_home, '$NDK/')) - - # conf.env.ANDROID_OPTS = android - conf.env.DEST_OS2 = 'android' elif conf.options.MAGX: # useless to change toolchain path, as toolchain meant to be placed in this path toolchain_path = '/opt/toolchains/motomagx/arm-eabi2/lib/' conf.env.INCLUDES_MAGX = [toolchain_path + i for i in ['ezx-z6/include', 'qt-2.3.8/include']] conf.env.LIBPATH_MAGX = [toolchain_path + i for i in ['ezx-z6/lib', 'qt-2.3.8/lib']] conf.env.LINKFLAGS_MAGX = ['-Wl,-rpath-link=' + i for i in conf.env.LIBPATH_MAGX] + elif conf.options.MSVC_WINE: + try: + toolchain_path = conf.environ['MSVC_WINE_PATH'] + except KeyError: + conf.fatal('Set MSVC_WINE_PATH environment variable to the MSVC toolchain root!') + + conf.environ['CC'] = conf.environ['CXX'] = os.path.join(toolchain_path, 'bin', conf.env.MSVC_TARGETS[0], 'cl') + conf.environ['LINK_CXX'] = os.path.join(toolchain_path, 'bin', conf.env.MSVC_TARGETS[0], 'link') + conf.environ['AR'] = os.path.join(toolchain_path, 'bin', conf.env.MSVC_TARGETS[0], 'lib') + conf.environ['WINRC'] = os.path.join(toolchain_path, 'bin', conf.env.MSVC_TARGETS[0], 'rc') + conf.env.DEST_OS = 'win32' + conf.env.DEST_CPU = conf.env.MSVC_TARGETS[0] + conf.env.COMPILER_CXX = conf.env.COMPILER_CC = 'msvc' conf.env.MAGX = conf.options.MAGX + conf.env.MSVC_WINE = conf.options.MSVC_WINE MACRO_TO_DESTOS = OrderedDict({ '__ANDROID__' : 'android' }) for k in c_config.MACRO_TO_DESTOS: MACRO_TO_DESTOS[k] = c_config.MACRO_TO_DESTOS[k] # ordering is important @@ -433,11 +444,17 @@ compiler_cxx_configure = getattr(compiler_cxx, 'configure') compiler_c_configure = getattr(compiler_c, 'configure') def patch_compiler_cxx_configure(conf): - compiler_cxx_configure(conf) + if not conf.env.MSVC_WINE: + compiler_cxx_configure(conf) + else: + conf.load('msvc', funs='no_autodetect') post_compiler_cxx_configure(conf) def patch_compiler_c_configure(conf): - compiler_c_configure(conf) + if not conf.env.MSVC_WINE: + compiler_c_configure(conf) + else: + conf.load('msvc', funs='no_autodetect') post_compiler_c_configure(conf) setattr(compiler_cxx, 'configure', patch_compiler_cxx_configure) From 45de8745982adaac21cc321ecf1d530e6dc85416 Mon Sep 17 00:00:00 2001 From: Alibek Omarov Date: Wed, 16 Nov 2022 04:20:02 +0300 Subject: [PATCH 174/490] wscript: always load msvc options --- wscript | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/wscript b/wscript index 451ccdc7..01b86482 100644 --- a/wscript +++ b/wscript @@ -123,10 +123,7 @@ def options(opt): opt.add_subproject(i.name) - opt.load('xshlib xcompile compiler_cxx compiler_c sdl2 clang_compilation_database strip_on_install waf_unit_test msdev msvs') - if sys.platform == 'win32': - opt.load('msvc') - opt.load('reconfigure') + opt.load('xshlib xcompile compiler_cxx compiler_c sdl2 clang_compilation_database strip_on_install waf_unit_test msdev msvs msvc reconfigure') def configure(conf): conf.load('fwgslib reconfigure compiler_optimizations') From 57c71efe53c9394f59f7cbaee1f02953f8bd729a Mon Sep 17 00:00:00 2001 From: Alibek Omarov Date: Wed, 16 Nov 2022 04:24:32 +0300 Subject: [PATCH 175/490] Documentation: fix wrong envvar in windows xcompile instructions, better wording --- Documentation/cross-compiling-for-windows-with-wine.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Documentation/cross-compiling-for-windows-with-wine.md b/Documentation/cross-compiling-for-windows-with-wine.md index 734fa529..5993e301 100644 --- a/Documentation/cross-compiling-for-windows-with-wine.md +++ b/Documentation/cross-compiling-for-windows-with-wine.md @@ -3,7 +3,7 @@ This can be useful to test engine in Wine without using virtual machines or dual-booting to Windows. 0. Clone and install https://github.com/mstorsjo/msvc-wine (you can skip CMake part) -1. Set environment variable WINE_MSVC_PATH to the path to installed MSVC toolchain +1. Set environment variable MSVC_WINE_PATH to the path to installed MSVC toolchain 2. Pre-load wine: `wineserver -k; wineserver -p; wine64 wineboot` 3. Run `./waf configure -T --enable-wine-msvc --sdl2=../SDL2_VC`. Configuration step will take more time than usual. -4. Follow other typical steps to build from console, except keep in mind you're in decent Operating System now. +4. .. other typical steps to build from console ... From 7b74015c4d2e523a9af7597b30596e632c5bbcb0 Mon Sep 17 00:00:00 2001 From: Alibek Omarov Date: Wed, 16 Nov 2022 20:57:40 +0300 Subject: [PATCH 176/490] engine: server: check if player can hear other before sending voicedata --- engine/server/sv_client.c | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/engine/server/sv_client.c b/engine/server/sv_client.c index 987fc8e2..164d5fd7 100644 --- a/engine/server/sv_client.c +++ b/engine/server/sv_client.c @@ -3396,9 +3396,15 @@ void SV_ParseVoiceData( sv_client_t *cl, sizebuf_t *msg ) for( i = 0, cur = svs.clients; i < svs.maxclients; i++, cur++ ) { - if ( cur->state < cs_connected && cl != cur ) - continue; - + if( cl != cur ) + { + if( cur->state < cs_connected ) + continue; + + if( !FBitSet( cur->listeners, BIT( client ))) + continue; + } + length = size; // 6 is a number of bytes for other parts of message From 42740149ac8fb2de4c9a25ec3e2b81c4c4d3e221 Mon Sep 17 00:00:00 2001 From: Alibek Omarov Date: Wed, 16 Nov 2022 21:02:41 +0300 Subject: [PATCH 177/490] engine: client: fix mouse cursor being hidden in background map --- engine/client/input.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/engine/client/input.c b/engine/client/input.c index 68d6e2aa..6c43c7d2 100644 --- a/engine/client/input.c +++ b/engine/client/input.c @@ -177,7 +177,7 @@ void IN_ToggleClientMouse( int newstate, int oldstate ) // since SetCursorType controls cursor visibility // execute it first, and then check mouse grab state if(( newstate == key_menu || newstate == key_console || newstate == key_message ) && - ( !CL_IsBackgroundMap() || CL_IsBackgroundDemo( ))) + ( CL_IsBackgroundMap() || CL_IsBackgroundDemo( ))) { Platform_SetCursorType( dc_arrow ); From 023f6712f994dbe4f1667b974fd59cbe58ed92a2 Mon Sep 17 00:00:00 2001 From: Alibek Omarov Date: Wed, 16 Nov 2022 21:14:48 +0300 Subject: [PATCH 178/490] engine: client: always toggle mouse on when in console, menu or typing chat message --- engine/client/input.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/engine/client/input.c b/engine/client/input.c index 6c43c7d2..24e71349 100644 --- a/engine/client/input.c +++ b/engine/client/input.c @@ -176,8 +176,7 @@ void IN_ToggleClientMouse( int newstate, int oldstate ) // since SetCursorType controls cursor visibility // execute it first, and then check mouse grab state - if(( newstate == key_menu || newstate == key_console || newstate == key_message ) && - ( CL_IsBackgroundMap() || CL_IsBackgroundDemo( ))) + if( newstate == key_menu || newstate == key_console || newstate == key_message ) { Platform_SetCursorType( dc_arrow ); From a894ca60c603881f6e1b61f02d6d8ec82ef24e8f Mon Sep 17 00:00:00 2001 From: Alibek Omarov Date: Wed, 16 Nov 2022 22:05:31 +0300 Subject: [PATCH 179/490] engine: client: vgui: fix vgui viewport height being limited at 480 pixels --- engine/client/vgui/vgui_draw.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/engine/client/vgui/vgui_draw.c b/engine/client/vgui/vgui_draw.c index da5f2eb8..8ae08584 100644 --- a/engine/client/vgui/vgui_draw.c +++ b/engine/client/vgui/vgui_draw.c @@ -203,7 +203,7 @@ void VGui_Startup( int width, int height ) if( !vgui.initialized ) return; - height = Q_min( 480, height ); + height = Q_max( 480, height ); if( width <= 640 ) width = 640; else if( width <= 800 ) width = 800; From 76d0608ee1d95aa8582ef7831f2166b631d3f71a Mon Sep 17 00:00:00 2001 From: Alibek Omarov Date: Wed, 16 Nov 2022 22:54:07 +0300 Subject: [PATCH 180/490] ref: fix beams end point --- ref/gl/gl_beams.c | 2 +- ref/soft/r_beams.c | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/ref/gl/gl_beams.c b/ref/gl/gl_beams.c index 4e436d17..7886297f 100644 --- a/ref/gl/gl_beams.c +++ b/ref/gl/gl_beams.c @@ -1196,7 +1196,7 @@ void R_BeamDrawCustomEntity( cl_entity_t *ent ) g = ent->curstate.rendercolor.g / 255.0f; b = ent->curstate.rendercolor.b / 255.0f; - R_BeamSetup( &beam, ent->origin, ent->angles, ent->curstate.modelindex, 0, ent->curstate.scale, amp, blend, ent->curstate.animtime ); + R_BeamSetup( &beam, ent->origin, ent->curstate.angles, ent->curstate.modelindex, 0, ent->curstate.scale, amp, blend, ent->curstate.animtime ); R_BeamSetAttributes( &beam, r, g, b, ent->curstate.framerate, ent->curstate.frame ); beam.pFollowModel = NULL; diff --git a/ref/soft/r_beams.c b/ref/soft/r_beams.c index 5c8dd8f5..49142cb2 100644 --- a/ref/soft/r_beams.c +++ b/ref/soft/r_beams.c @@ -1209,7 +1209,7 @@ void R_BeamDrawCustomEntity( cl_entity_t *ent ) g = ent->curstate.rendercolor.g / 255.0f; b = ent->curstate.rendercolor.b / 255.0f; - R_BeamSetup( &beam, ent->origin, ent->angles, ent->curstate.modelindex, 0, ent->curstate.scale, amp, blend, ent->curstate.animtime ); + R_BeamSetup( &beam, ent->origin, ent->curstate.angles, ent->curstate.modelindex, 0, ent->curstate.scale, amp, blend, ent->curstate.animtime ); R_BeamSetAttributes( &beam, r, g, b, ent->curstate.framerate, ent->curstate.frame ); beam.pFollowModel = NULL; From dd881d3da97e385d28bf9707775e7dcdd655269f Mon Sep 17 00:00:00 2001 From: Alibek Omarov Date: Wed, 16 Nov 2022 23:37:26 +0300 Subject: [PATCH 181/490] engine: server: remove duplicate function --- engine/server/server.h | 2 +- engine/server/sv_client.c | 2 +- engine/server/sv_main.c | 39 ++------------------------------------- 3 files changed, 4 insertions(+), 39 deletions(-) diff --git a/engine/server/server.h b/engine/server/server.h index 90b90b8a..6bb28da8 100644 --- a/engine/server/server.h +++ b/engine/server/server.h @@ -474,7 +474,6 @@ void SV_SendResource( resource_t *pResource, sizebuf_t *msg ); void SV_SendResourceList( sv_client_t *cl ); void SV_AddToMaster( netadr_t from, sizebuf_t *msg ); qboolean SV_ProcessUserAgent( netadr_t from, const char *useragent ); -int SV_GetConnectedClientsCount( int *bots ); void Host_SetServerState( int state ); qboolean SV_IsSimulating( void ); void SV_FreeClients( void ); @@ -551,6 +550,7 @@ void SV_InitClientMove( void ); void SV_UpdateServerInfo( void ); void SV_EndRedirect( void ); void SV_RejectConnection( netadr_t from, const char *fmt, ... ) _format( 2 ); +void SV_GetPlayerCount( int *clients, int *bots ); // // sv_cmds.c diff --git a/engine/server/sv_client.c b/engine/server/sv_client.c index 164d5fd7..1476c982 100644 --- a/engine/server/sv_client.c +++ b/engine/server/sv_client.c @@ -48,7 +48,7 @@ SV_GetPlayerCount ================= */ -static void SV_GetPlayerCount( int *players, int *bots ) +void SV_GetPlayerCount( int *players, int *bots ) { int i; diff --git a/engine/server/sv_main.c b/engine/server/sv_main.c index 6a73724c..016bcc8c 100644 --- a/engine/server/sv_main.c +++ b/engine/server/sv_main.c @@ -163,41 +163,6 @@ qboolean SV_HasActivePlayers( void ) return false; } -/* -================ -SV_GetConnectedClientsCount - -returns connected clients count (and optionally bots count) -================ -*/ -int SV_GetConnectedClientsCount(int *bots) -{ - int index; - int clients; - - clients = 0; - if( svs.clients ) - { - if( bots ) - *bots = 0; - - for( index = 0; index < svs.maxclients; index++ ) - { - if( svs.clients[index].state >= cs_connected ) - { - if( FBitSet( svs.clients[index].flags, FCL_FAKECLIENT )) - { - if( bots ) - (*bots)++; - } - else - clients++; - } - } - } - return clients; -} - /* =================== SV_UpdateMovevars @@ -777,7 +742,7 @@ void SV_AddToMaster( netadr_t from, sizebuf_t *msg ) { uint challenge; char s[MAX_INFO_STRING] = "0\n"; // skip 2 bytes of header - int clients = 0, bots = 0; + int clients, bots; int len = sizeof( s ); if( !NET_IsMasterAdr( from )) @@ -786,7 +751,7 @@ void SV_AddToMaster( netadr_t from, sizebuf_t *msg ) return; } - clients = SV_GetConnectedClientsCount( &bots ); + SV_GetPlayerCount( &clients, &bots ); challenge = MSG_ReadUBitLong( msg, sizeof( uint ) << 3 ); Info_SetValueForKey( s, "protocol", va( "%d", PROTOCOL_VERSION ), len ); // protocol version From 88045ce3beb7bb0b7651f3add3a96e8b01717181 Mon Sep 17 00:00:00 2001 From: Alibek Omarov Date: Thu, 17 Nov 2022 01:08:11 +0300 Subject: [PATCH 182/490] ref: gl: link liblog for Android --- ref/gl/wscript | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/ref/gl/wscript b/ref/gl/wscript index 1fc5e047..b8678e46 100644 --- a/ref/gl/wscript +++ b/ref/gl/wscript @@ -45,12 +45,12 @@ def build(bld): }, 'ref_gles1': { 'enable': bld.env.NANOGL, - 'libs': ['DL', 'nanogl'], + 'libs': ['DL', 'nanogl', 'LOG'], 'defines': ['XASH_NANOGL'], }, 'ref_gles2': { 'enable': bld.env.GLWES, - 'libs': ['DL', 'gl-wes-v2'], + 'libs': ['DL', 'gl-wes-v2', 'LOG'], 'defines': ['XASH_WES'], }, 'ref_gl4es': { From 30d9b6d844ae03bce48c703fe943f6c4d7187ea7 Mon Sep 17 00:00:00 2001 From: Alibek Omarov Date: Thu, 17 Nov 2022 01:20:01 +0300 Subject: [PATCH 183/490] engine: platform: win32: fix compile --- engine/platform/win32/sys_win.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/engine/platform/win32/sys_win.c b/engine/platform/win32/sys_win.c index 8af51fd2..917404ea 100644 --- a/engine/platform/win32/sys_win.c +++ b/engine/platform/win32/sys_win.c @@ -56,7 +56,7 @@ void Platform_ShellExecute( const char *path, const char *parms ) void Platform_UpdateStatusLine( void ) { - int clientsCount; + int clientsCount, botsCountUnused; char szStatus[128]; static double lastTime; @@ -67,7 +67,7 @@ void Platform_UpdateStatusLine( void ) if(( sv.time - lastTime ) < 0.5f ) return; - clientsCount = SV_GetConnectedClientsCount( NULL ); + SV_GetPlayerCount( &clientsCount, &botsCountUnused ); Q_snprintf( szStatus, sizeof( szStatus ) - 1, "%.1f fps %2i/%2i on %16s", 1.f / sv.frametime, clientsCount, svs.maxclients, host.game.levelName ); #ifdef XASH_WIN32 Wcon_SetStatus( szStatus ); From 46979419ae30dd7482d29c7eff76c9019175770e Mon Sep 17 00:00:00 2001 From: Alibek Omarov Date: Thu, 17 Nov 2022 01:23:33 +0300 Subject: [PATCH 184/490] wscript: check Android's log library globally --- engine/wscript | 3 --- wscript | 3 +++ 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/engine/wscript b/engine/wscript index eb64e7b4..0b7ef631 100644 --- a/engine/wscript +++ b/engine/wscript @@ -48,9 +48,6 @@ def configure(conf): conf.options.NO_ASYNC_RESOLVE = True if not conf.check_cc( fragment='int main(){ int i = socket();}', lib = 'wattcpwl', mandatory=False ): conf.define('XASH_NO_NETWORK',1) - elif conf.env.DEST_OS == 'android': # Android doesn't need SDL2 - for i in ['log']: - conf.check_cc(lib = i) elif conf.options.FBDEV_SW: # unused, XASH_LINUX without XASH_SDL gives fbdev & alsa support # conf.define('XASH_FBDEV', 1) diff --git a/wscript b/wscript index 01b86482..268afffe 100644 --- a/wscript +++ b/wscript @@ -275,6 +275,9 @@ def configure(conf): if not conf.env.LIB_M: # HACK: already added in xcompile! conf.check_cc(lib='m') + + if conf.env.DEST_OS == 'android': + conf.check_cc(lib='log') else: # Common Win32 libraries # Don't check them more than once, to save time From e30c61c0e2d8e9bfa5b4d1c2be66c1d053992566 Mon Sep 17 00:00:00 2001 From: Alibek Omarov Date: Thu, 17 Nov 2022 19:44:44 +0300 Subject: [PATCH 185/490] common: netadr: use static_assert macro --- common/netadr.h | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/common/netadr.h b/common/netadr.h index c56f517f..69735d57 100644 --- a/common/netadr.h +++ b/common/netadr.h @@ -31,8 +31,6 @@ typedef enum NA_MULTICAST_IP6, // all nodes multicast } netadrtype_t; -#define NETADR_T_SIZE 20 - // Original structure: // typedef struct netadr_s // { @@ -73,6 +71,6 @@ typedef struct netadr_s } netadr_t; #pragma pack( pop ) -extern int _check_netadr_t_size[sizeof( netadr_t ) == NETADR_T_SIZE ? 1 : -1]; +STATIC_ASSERT( sizeof( netadr_t ) == 20, "invalid netadr_t size" ); #endif//NETADR_H From 3da736a1eb7316d0650dcdf9f711b0e6c5d10881 Mon Sep 17 00:00:00 2001 From: Alibek Omarov Date: Thu, 17 Nov 2022 21:06:15 +0300 Subject: [PATCH 186/490] engine: server: try to solve issue when server dll has no voice mgr --- engine/server/sv_client.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/engine/server/sv_client.c b/engine/server/sv_client.c index 1476c982..4ea44b03 100644 --- a/engine/server/sv_client.c +++ b/engine/server/sv_client.c @@ -419,7 +419,9 @@ void SV_ConnectClient( netadr_t from ) // reset viewentities (from previous level) memset( newcl->viewentity, 0, sizeof( newcl->viewentity )); newcl->num_viewents = 0; - newcl->listeners = 0; + // HACKHACK: can hear all players by default to avoid issues + // with server.dll without voice game manager + newcl->listeners = -1; // initailize netchan Netchan_Setup( NS_SERVER, &newcl->netchan, from, qport, newcl, SV_GetFragmentSize ); From 3488d4e65f85b30d1cb06fc69e297433cb8dd938 Mon Sep 17 00:00:00 2001 From: Andrey Akhmichin <15944199+nekonomicon@users.noreply.github.com> Date: Fri, 18 Nov 2022 21:21:17 +0500 Subject: [PATCH 187/490] public: simplified strings operations. --- public/crtlib.c | 18 ++++++++++-------- 1 file changed, 10 insertions(+), 8 deletions(-) diff --git a/public/crtlib.c b/public/crtlib.c index 6dba1ec4..34972978 100644 --- a/public/crtlib.c +++ b/public/crtlib.c @@ -560,14 +560,14 @@ char *Q_pretifymem( float value, int digitsafterdecimal ) if( value > onemb ) { value /= onemb; - Q_sprintf( suffix, " Mb" ); + Q_strcpy( suffix, " Mb" ); } else if( value > onekb ) { value /= onekb; - Q_sprintf( suffix, " Kb" ); + Q_strcpy( suffix, " Kb" ); } - else Q_sprintf( suffix, " bytes" ); + else Q_strcpy( suffix, " bytes" ); // clamp to >= 0 digitsafterdecimal = Q_max( digitsafterdecimal, 0 ); @@ -591,8 +591,8 @@ char *Q_pretifymem( float value, int digitsafterdecimal ) o = out; // search for decimal or if it was integral, find the space after the raw number - dot = Q_strstr( i, "." ); - if( !dot ) dot = Q_strstr( i, " " ); + dot = Q_strchr( i, '.' ); + if( !dot ) dot = Q_strchr( i, ' ' ); pos = dot - i; // compute position of dot pos -= 3; // don't put a comma if it's <= 3 long @@ -750,7 +750,7 @@ void COM_ExtractFilePath( const char *path, char *dest ) memcpy( dest, path, src - path ); dest[src - path - 1] = 0; // cutoff backslash } - else Q_strcpy( dest, "" ); // file without path + else dest[0] = 0; // file without path } /* @@ -785,10 +785,12 @@ COM_DefaultExtension void COM_DefaultExtension( char *path, const char *extension ) { const char *src; + size_t len; // if path doesn't have a .EXT, append extension // (extension should include the .) - src = path + Q_strlen( path ) - 1; + len = Q_strlen( path ); + src = path + len - 1; while( *src != '/' && src != path ) { @@ -797,7 +799,7 @@ void COM_DefaultExtension( char *path, const char *extension ) src--; } - Q_strcat( path, extension ); + Q_strcpy( &path[len], extension ); } /* From 3bd8ad50d24a65e9d2178b54d444e063fbfcfc07 Mon Sep 17 00:00:00 2001 From: SNMetamorph <25657591+SNMetamorph@users.noreply.github.com> Date: Mon, 21 Nov 2022 22:56:23 +0400 Subject: [PATCH 188/490] ref: gl: fixed crash when opening "Customize" menu --- ref/gl/gl_rsurf.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/ref/gl/gl_rsurf.c b/ref/gl/gl_rsurf.c index c4a53edd..d4d4f7de 100644 --- a/ref/gl/gl_rsurf.c +++ b/ref/gl/gl_rsurf.c @@ -3315,8 +3315,10 @@ void R_DrawWorld( void ) // paranoia issues: when gl_renderer is "0" we need have something valid for currententity // to prevent crashing until HeadShield drawing. RI.currententity = gEngfuncs.GetEntityByIndex( 0 ); - RI.currentmodel = RI.currententity->model; + if( !RI.currententity ) + return; + RI.currentmodel = RI.currententity->model; if( !RI.drawWorld || RI.onlyClientDraw ) return; From e204f05726a9af21290bb502287b1bc6abadea91 Mon Sep 17 00:00:00 2001 From: SNMetamorph <25657591+SNMetamorph@users.noreply.github.com> Date: Tue, 22 Nov 2022 18:54:01 +0400 Subject: [PATCH 189/490] engine: client: fixed players spray textures not being updated --- engine/client/cl_tent.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/engine/client/cl_tent.c b/engine/client/cl_tent.c index 8158ce2c..451eab3d 100644 --- a/engine/client/cl_tent.c +++ b/engine/client/cl_tent.c @@ -2928,8 +2928,10 @@ void CL_PlayerDecal( int playernum, int customIndex, int entityIndex, float *pos { if( !pCust->nUserData1 ) { + qboolean updateSprayTexture; const char *decalname = va( "player%dlogo%d", playernum, customIndex ); - pCust->nUserData1 = GL_LoadTextureInternal( decalname, pCust->pInfo, TF_DECAL ); + updateSprayTexture = ref.dllFuncs.GL_FindTexture( decalname ) != 0; + pCust->nUserData1 = ref.dllFuncs.GL_LoadTextureFromBuffer( decalname, pCust->pInfo, TF_DECAL, updateSprayTexture ); } textureIndex = pCust->nUserData1; } From 707c93c32cf91501fb29705d255e7013870e995c Mon Sep 17 00:00:00 2001 From: SNMetamorph <25657591+SNMetamorph@users.noreply.github.com> Date: Tue, 22 Nov 2022 18:54:33 +0400 Subject: [PATCH 190/490] engine: common: hpak: all file operations made to use gamedir only --- engine/common/hpak.c | 22 +++++++++++----------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/engine/common/hpak.c b/engine/common/hpak.c index 9a179439..de5a663c 100644 --- a/engine/common/hpak.c +++ b/engine/common/hpak.c @@ -114,7 +114,7 @@ void HPAK_CreatePak( const char *filename, resource_t *pResource, byte *pData, f Con_Printf( "creating HPAK %s.\n", pakname ); - fout = FS_Open( pakname, "wb", false ); + fout = FS_Open( pakname, "wb", true ); if( !fout ) { Con_DPrintf( S_ERROR "HPAK_CreatePak: can't write %s.\n", pakname ); @@ -260,7 +260,7 @@ void HPAK_AddLump( qboolean bUseQueue, const char *name, resource_t *pResource, Q_strncpy( srcname, name, sizeof( srcname )); COM_ReplaceExtension( srcname, ".hpk" ); - file_src = FS_Open( srcname, "rb", false ); + file_src = FS_Open( srcname, "rb", true ); if( !file_src ) { @@ -272,7 +272,7 @@ void HPAK_AddLump( qboolean bUseQueue, const char *name, resource_t *pResource, Q_strncpy( dstname, srcname, sizeof( dstname )); COM_ReplaceExtension( dstname, ".hp2" ); - file_dst = FS_Open( dstname, "wb", false ); + file_dst = FS_Open( dstname, "wb", true ); if( !file_dst ) { @@ -395,7 +395,7 @@ static qboolean HPAK_Validate( const char *filename, qboolean quiet ) Q_strncpy( pakname, filename, sizeof( pakname )); COM_ReplaceExtension( pakname, ".hpk" ); - f = FS_Open( pakname, "rb", false ); + f = FS_Open( pakname, "rb", true ); if( !f ) { Con_DPrintf( S_ERROR "Couldn't find %s.\n", pakname ); @@ -545,7 +545,7 @@ qboolean HPAK_ResourceForHash( const char *filename, byte *hash, resource_t *pRe Q_strncpy( pakname, filename, sizeof( pakname )); COM_ReplaceExtension( pakname, ".hpk" ); - f = FS_Open( pakname, "rb", false ); + f = FS_Open( pakname, "rb", true ); if( !f ) return false; FS_Read( f, &header, sizeof( header )); @@ -593,7 +593,7 @@ static qboolean HPAK_ResourceForIndex( const char *filename, int index, resource Q_strncpy( pakname, filename, sizeof( pakname )); COM_ReplaceExtension( pakname, ".hpk" ); - f = FS_Open( pakname, "rb", false ); + f = FS_Open( pakname, "rb", true ); if( !f ) { Con_DPrintf( S_ERROR "couldn't open %s.\n", pakname ); @@ -679,7 +679,7 @@ qboolean HPAK_GetDataPointer( const char *filename, resource_t *pResource, byte Q_strncpy( pakname, filename, sizeof( pakname )); COM_ReplaceExtension( pakname, ".hpk" ); - f = FS_Open( pakname, "rb", false ); + f = FS_Open( pakname, "rb", true ); if( !f ) return false; FS_Read( f, &header, sizeof( header )); @@ -762,7 +762,7 @@ void HPAK_RemoveLump( const char *name, resource_t *pResource ) Q_strncpy( read_path, name, sizeof( read_path )); COM_ReplaceExtension( read_path, ".hpk" ); - file_src = FS_Open( read_path, "rb", false ); + file_src = FS_Open( read_path, "rb", true ); if( !file_src ) { Con_DPrintf( S_ERROR "%s couldn't open.\n", read_path ); @@ -771,7 +771,7 @@ void HPAK_RemoveLump( const char *name, resource_t *pResource ) Q_strncpy( save_path, read_path, sizeof( save_path )); COM_ReplaceExtension( save_path, ".hp2" ); - file_dst = FS_Open( save_path, "wb", false ); + file_dst = FS_Open( save_path, "wb", true ); if( !file_dst ) { @@ -892,7 +892,7 @@ void HPAK_List_f( void ) COM_ReplaceExtension( pakname, ".hpk" ); Con_Printf( "Contents for %s.\n", pakname ); - f = FS_Open( pakname, "rb", false ); + f = FS_Open( pakname, "rb", true ); if( !f ) { Con_DPrintf( S_ERROR "couldn't open %s.\n", pakname ); @@ -983,7 +983,7 @@ void HPAK_Extract_f( void ) COM_ReplaceExtension( pakname, ".hpk" ); Con_Printf( "Contents for %s.\n", pakname ); - f = FS_Open( pakname, "rb", false ); + f = FS_Open( pakname, "rb", true ); if( !f ) { Con_DPrintf( S_ERROR "couldn't open %s.\n", pakname ); From 49d93c0e76e5d3c67edc545db658a80762042d2e Mon Sep 17 00:00:00 2001 From: SNMetamorph <25657591+SNMetamorph@users.noreply.github.com> Date: Tue, 22 Nov 2022 18:55:44 +0400 Subject: [PATCH 191/490] engine: common: custom: increased custom decal size limit to 128Kb, added wrong size warning --- engine/common/custom.c | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/engine/common/custom.c b/engine/common/custom.c index cf9d0431..1cb8f746 100644 --- a/engine/common/custom.c +++ b/engine/common/custom.c @@ -101,7 +101,7 @@ qboolean COM_CreateCustomization( customization_t *pListHead, resource_t *pResou { if( !FBitSet( flags, FCUST_IGNOREINIT )) { - if( pResource->nDownloadSize >= (1 * 1024) && pResource->nDownloadSize <= ( 16 * 1024 )) + if( pResource->nDownloadSize >= (1 * 1024) && pResource->nDownloadSize <= ( 128 * 1024 )) { pCust->bTranslated = true; pCust->nUserData1 = 0; @@ -112,6 +112,10 @@ qboolean COM_CreateCustomization( customization_t *pListHead, resource_t *pResou else pCust->pInfo = NULL; if( nLumps ) *nLumps = 1; } + else + { + Con_Printf( S_WARN "Ignoring custom decal \"%s\": wrong size (%i bytes)\n", pResource->szFileName, pResource->nDownloadSize ); + } } } } From 24f7db19d8324355bbfcd02a11e1069f171c45b0 Mon Sep 17 00:00:00 2001 From: Velaron Date: Fri, 18 Nov 2022 15:35:21 +0200 Subject: [PATCH 192/490] filesystem: switch file operations to an interface --- filesystem/dir.c | 110 ++++++++++++++++ filesystem/filesystem.c | 214 +++++-------------------------- filesystem/filesystem_internal.h | 56 +++++--- filesystem/pak.c | 48 ++++--- filesystem/wad.c | 62 ++++++--- filesystem/zip.c | 39 ++++-- 6 files changed, 281 insertions(+), 248 deletions(-) create mode 100644 filesystem/dir.c diff --git a/filesystem/dir.c b/filesystem/dir.c new file mode 100644 index 00000000..a46af43c --- /dev/null +++ b/filesystem/dir.c @@ -0,0 +1,110 @@ +/* +dir.c - directory operations +Copyright (C) 2022 Alibek Omarov, Velaron + +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 +#include +#include +#if XASH_POSIX +#include +#endif +#include +#include +#include "port.h" +#include "filesystem_internal.h" +#include "crtlib.h" +#include "xash3d_mathlib.h" +#include "common/com_strings.h" + +void FS_Close_DIR( searchpath_t *search ) {} + +void FS_PrintInfo_DIR( searchpath_t *search, char *dst, size_t size ) +{ + Q_strncpy( dst, search->filename, size ); +} + +int FS_FindFile_DIR( searchpath_t *search, const char *path ) +{ + char netpath[MAX_SYSPATH]; + + Q_sprintf( netpath, "%s%s", search->filename, path ); + + if( FS_SysFileExists( netpath, !( search->flags & FS_CUSTOM_PATH ) ) ) + return 0; + + return -1; +} + +void FS_Search_DIR( searchpath_t *search, stringlist_t *list, const char *pattern, int caseinsensitive ) +{ + string netpath, temp; + stringlist_t dirlist; + const char *slash, *backslash, *colon, *separator; + int basepathlength, dirlistindex, resultlistindex; + char *basepath; + + slash = Q_strrchr( pattern, '/' ); + backslash = Q_strrchr( pattern, '\\' ); + colon = Q_strrchr( pattern, ':' ); + separator = Q_max( slash, backslash ); + separator = Q_max( separator, colon ); + basepathlength = separator ? (separator + 1 - pattern) : 0; + basepath = Mem_Calloc( fs_mempool, basepathlength + 1 ); + if( basepathlength ) memcpy( basepath, pattern, basepathlength ); + basepath[basepathlength] = 0; + + Q_sprintf( netpath, "%s%s", search->filename, basepath ); + + stringlistinit( &dirlist ); + listdirectory( &dirlist, netpath, caseinsensitive ); + + for( dirlistindex = 0; dirlistindex < dirlist.numstrings; dirlistindex++ ) + { + Q_sprintf( temp, "%s%s", basepath, dirlist.strings[dirlistindex] ); + + if( matchpattern( temp, (char *)pattern, true ) ) + { + for( resultlistindex = 0; resultlistindex < list->numstrings; resultlistindex++ ) + { + if( !Q_strcmp( list->strings[resultlistindex], temp ) ) + break; + } + + if( resultlistindex == list->numstrings ) + stringlistappend( list, temp ); + } + } + + stringlistfreecontents( &dirlist ); + + Mem_Free( basepath ); +} + +int FS_FileTime_DIR( struct searchpath_s *search, const char *filename ) +{ + char path[MAX_SYSPATH]; + + // found in the filesystem? + Q_sprintf( path, "%s%s", search->filename, filename ); + return FS_SysFileTime( path ); +} + +file_t *FS_OpenFile_DIR( struct searchpath_s *search, const char *filename, const char *mode, int pack_ind ) +{ + char path[MAX_SYSPATH]; + + // found in the filesystem? + Q_sprintf( path, "%s%s", search->filename, filename ); + return FS_SysOpen( path, mode ); +} \ No newline at end of file diff --git a/filesystem/filesystem.c b/filesystem/filesystem.c index bbb445d5..e8de5277 100644 --- a/filesystem/filesystem.c +++ b/filesystem/filesystem.c @@ -117,12 +117,12 @@ FILEMATCH COMMON SYSTEM ============================================================================= */ -static void stringlistinit( stringlist_t *list ) +void stringlistinit( stringlist_t *list ) { memset( list, 0, sizeof( *list )); } -static void stringlistfreecontents( stringlist_t *list ) +void stringlistfreecontents( stringlist_t *list ) { int i; @@ -193,7 +193,7 @@ static void listlowercase( stringlist_t *list ) } } -static void listdirectory( stringlist_t *list, const char *path, qboolean lowercase ) +void listdirectory( stringlist_t *list, const char *path, qboolean lowercase ) { int i; signed char *c; @@ -463,6 +463,14 @@ void FS_AddGameDirectory( const char *dir, uint flags ) search->next = fs_searchpaths; search->type = SEARCHPATH_PLAIN; search->flags = flags; + + search->printinfo = FS_PrintInfo_DIR; + search->close = FS_Close_DIR; + search->openfile = FS_OpenFile_DIR; + search->filetime = FS_FileTime_DIR; + search->findfile = FS_FindFile_DIR; + search->search = FS_Search_DIR; + fs_searchpaths = search; } @@ -488,20 +496,7 @@ void FS_ClearSearchPath( void ) } else fs_searchpaths = search->next; - switch( search->type ) - { - case SEARCHPATH_PAK: - FS_ClosePAK( search->pack ); - break; - case SEARCHPATH_WAD: - FS_CloseWAD( search->wad ); - break; - case SEARCHPATH_ZIP: - FS_CloseZIP( search->zip ); - break; - default: - break; - } + search->close( search ); Mem_Free( search ); } @@ -1323,7 +1318,7 @@ static qboolean FS_FindLibrary( const char *dllname, qboolean directpath, fs_dll dllInfo->encrypted = FS_CheckForCrypt( dllInfo->shortPath ); - if( index < 0 && !dllInfo->encrypted && search ) + if( index >= 0 && !dllInfo->encrypted && search ) { Q_snprintf( dllInfo->fullPath, sizeof( dllInfo->fullPath ), "%s%s", search->filename, dllInfo->shortPath ); @@ -1533,21 +1528,7 @@ void FS_Path_f( void ) { string info; - switch( s->type ) - { - case SEARCHPATH_PAK: - FS_PrintPAKInfo( info, sizeof( info ), s->pack ); - break; - case SEARCHPATH_WAD: - FS_PrintWADInfo( info, sizeof( info ), s->wad ); - break; - case SEARCHPATH_ZIP: - FS_PrintZIPInfo( info, sizeof( info ), s->zip ); - break; - case SEARCHPATH_PLAIN: - Q_strncpy( info, s->filename, sizeof( info )); - break; - } + s->printinfo( s, info, sizeof(info) ); Con_Printf( "%s", info ); @@ -1816,48 +1797,16 @@ searchpath_t *FS_FindFile( const char *name, int *index, qboolean gamedironly ) // search through the path, one element at a time for( search = fs_searchpaths; search; search = search->next ) { + int pack_ind; + if( gamedironly & !FBitSet( search->flags, FS_GAMEDIRONLY_SEARCH_FLAGS )) continue; - // is the element a pak file? - if( search->type == SEARCHPATH_PAK ) + pack_ind = search->findfile( search, name ); + if( pack_ind >= 0 ) { - int pack_ind = FS_FindFilePAK( search->pack, name ); - if( pack_ind >= 0 ) - { - if( index ) *index = pack_ind; - return search; - } - } - else if( search->type == SEARCHPATH_WAD ) - { - int pack_ind = FS_FindFileWAD( search->wad, name ); - if( pack_ind >= 0 ) - { - if( index ) *index = pack_ind; - return search; - } - } - else if( search->type == SEARCHPATH_ZIP ) - { - int pack_ind = FS_FindFileZIP( search->zip, name ); - if( pack_ind >= 0 ) - { - if( index ) *index = pack_ind; - return search; - } - } - else - { - char netpath[MAX_SYSPATH]; - - Q_sprintf( netpath, "%s%s", search->filename, name ); - - if( FS_SysFileExists( netpath, !( search->flags & FS_CUSTOM_PATH ) )) - { - if( index != NULL ) *index = -1; - return search; - } + if( index ) *index = pack_ind; + return search; } } @@ -1907,26 +1856,7 @@ file_t *FS_OpenReadFile( const char *filename, const char *mode, qboolean gamedi if( search == NULL ) return NULL; - switch( search->type ) - { - case SEARCHPATH_PAK: - return FS_OpenPackedFile( search->pack, pack_ind ); - case SEARCHPATH_WAD: - return NULL; // let W_LoadFile get lump correctly - case SEARCHPATH_ZIP: - return FS_OpenZipFile( search->zip, pack_ind ); - default: - if( pack_ind < 0 ) - { - char path [MAX_SYSPATH]; - - // found in the filesystem? - Q_sprintf( path, "%s%s", search->filename, filename ); - return FS_SysOpen( path, mode ); - } - } - - return NULL; + return search->openfile( search, filename, mode, pack_ind ); } /* @@ -2611,26 +2541,7 @@ int FS_FileTime( const char *filename, qboolean gamedironly ) search = FS_FindFile( filename, &pack_ind, gamedironly ); if( !search ) return -1; // doesn't exist - switch( search->type ) - { - case SEARCHPATH_PAK: - return FS_FileTimePAK( search->pack ); - case SEARCHPATH_WAD: - return FS_FileTimeWAD( search->wad ); - case SEARCHPATH_ZIP: - return FS_FileTimeZIP( search->zip ); - default: - if( pack_ind < 0 ) - { - char path [MAX_SYSPATH]; - - // found in the filesystem? - Q_sprintf( path, "%s%s", search->filename, filename ); - return FS_SysFileTime( path ); - } - } - - return -1; // doesn't exist + return search->filetime( search, filename ); } /* @@ -2724,80 +2635,23 @@ Allocate and fill a search structure with information on matching filenames. */ search_t *FS_Search( const char *pattern, int caseinsensitive, int gamedironly ) { - search_t *search = NULL; - searchpath_t *searchpath; - pack_t *pak; - wfile_t *wad; - zip_t *zip; - int i, basepathlength, numfiles, numchars; - int resultlistindex, dirlistindex; - const char *slash, *backslash, *colon, *separator; - string netpath, temp; - stringlist_t resultlist; - stringlist_t dirlist; - char *basepath; + search_t *search = NULL; + searchpath_t *searchpath; + int i, numfiles, numchars; + stringlist_t resultlist; if( pattern[0] == '.' || pattern[0] == ':' || pattern[0] == '/' || pattern[0] == '\\' ) return NULL; // punctuation issues stringlistinit( &resultlist ); - stringlistinit( &dirlist ); - slash = Q_strrchr( pattern, '/' ); - backslash = Q_strrchr( pattern, '\\' ); - colon = Q_strrchr( pattern, ':' ); - separator = Q_max( slash, backslash ); - separator = Q_max( separator, colon ); - basepathlength = separator ? (separator + 1 - pattern) : 0; - basepath = Mem_Calloc( fs_mempool, basepathlength + 1 ); - if( basepathlength ) memcpy( basepath, pattern, basepathlength ); - basepath[basepathlength] = 0; // search through the path, one element at a time for( searchpath = fs_searchpaths; searchpath; searchpath = searchpath->next ) { if( gamedironly && !FBitSet( searchpath->flags, FS_GAMEDIRONLY_SEARCH_FLAGS )) continue; - - // is the element a pak file? - if( searchpath->type == SEARCHPATH_PAK ) - { - // look through all the pak file elements - FS_SearchPAK( &resultlist, searchpath->pack, pattern ); - } - else if( searchpath->type == SEARCHPATH_ZIP ) - { - FS_SearchZIP( &resultlist, searchpath->zip, pattern ); - } - else if( searchpath->type == SEARCHPATH_WAD ) - { - FS_SearchWAD( &resultlist, searchpath->wad, pattern ); - } - else - { - // get a directory listing and look at each name - Q_sprintf( netpath, "%s%s", searchpath->filename, basepath ); - stringlistinit( &dirlist ); - listdirectory( &dirlist, netpath, caseinsensitive ); - - for( dirlistindex = 0; dirlistindex < dirlist.numstrings; dirlistindex++ ) - { - Q_sprintf( temp, "%s%s", basepath, dirlist.strings[dirlistindex] ); - - if( matchpattern( temp, (char *)pattern, true )) - { - for( resultlistindex = 0; resultlistindex < resultlist.numstrings; resultlistindex++ ) - { - if( !Q_strcmp( resultlist.strings[resultlistindex], temp )) - break; - } - - if( resultlistindex == resultlist.numstrings ) - stringlistappend( &resultlist, temp ); - } - } - - stringlistfreecontents( &dirlist ); - } + + searchpath->search( searchpath, &resultlist, pattern, caseinsensitive ); } if( resultlist.numstrings ) @@ -2806,21 +2660,21 @@ search_t *FS_Search( const char *pattern, int caseinsensitive, int gamedironly ) numfiles = resultlist.numstrings; numchars = 0; - for( resultlistindex = 0; resultlistindex < resultlist.numstrings; resultlistindex++ ) - numchars += (int)Q_strlen( resultlist.strings[resultlistindex]) + 1; + for( i = 0; i < resultlist.numstrings; i++ ) + numchars += (int)Q_strlen( resultlist.strings[i]) + 1; search = Mem_Calloc( fs_mempool, sizeof(search_t) + numchars + numfiles * sizeof( char* )); search->filenames = (char **)((char *)search + sizeof( search_t )); search->filenamesbuffer = (char *)((char *)search + sizeof( search_t ) + numfiles * sizeof( char* )); search->numfilenames = (int)numfiles; numfiles = numchars = 0; - for( resultlistindex = 0; resultlistindex < resultlist.numstrings; resultlistindex++ ) + for( i = 0; i < resultlist.numstrings; i++ ) { size_t textlen; search->filenames[numfiles] = search->filenamesbuffer + numchars; - textlen = Q_strlen(resultlist.strings[resultlistindex]) + 1; - memcpy( search->filenames[numfiles], resultlist.strings[resultlistindex], textlen ); + textlen = Q_strlen(resultlist.strings[i]) + 1; + memcpy( search->filenames[numfiles], resultlist.strings[i], textlen ); numfiles++; numchars += (int)textlen; } @@ -2828,8 +2682,6 @@ search_t *FS_Search( const char *pattern, int caseinsensitive, int gamedironly ) stringlistfreecontents( &resultlist ); - Mem_Free( basepath ); - return search; } diff --git a/filesystem/filesystem_internal.h b/filesystem/filesystem_internal.h index 99250189..01591f4d 100644 --- a/filesystem/filesystem_internal.h +++ b/filesystem/filesystem_internal.h @@ -69,13 +69,22 @@ typedef struct searchpath_s string filename; int type; int flags; + union { pack_t *pack; wfile_t *wad; zip_t *zip; }; + struct searchpath_s *next; + + void ( *printinfo )( struct searchpath_s *search, char *dst, size_t size ); + void ( *close )( struct searchpath_s *search ); + file_t *( *openfile )( struct searchpath_s *search, const char *filename, const char *mode, int pack_ind ); + int ( *filetime )( struct searchpath_s *search, const char *filename ); + int ( *findfile )( struct searchpath_s *search, const char *path ); + void ( *search )( struct searchpath_s *search, stringlist_t *list, const char *pattern, int caseinsensitive ); } searchpath_t; extern fs_globals_t FI; @@ -156,7 +165,10 @@ qboolean FS_Rename( const char *oldname, const char *newname ); qboolean FS_Delete( const char *path ); qboolean FS_SysFileExists( const char *path, qboolean casesensitive ); const char *FS_GetDiskPath( const char *name, qboolean gamedironly ); +void stringlistinit( stringlist_t *list ); +void stringlistfreecontents( stringlist_t *list ); void stringlistappend( stringlist_t *list, char *text ); +void listdirectory( stringlist_t *list, const char *path, qboolean lowercase ); void FS_CreatePath( char *path ); qboolean FS_SysFolderExists( const char *path ); file_t *FS_OpenReadFile( const char *filename, const char *mode, qboolean gamedironly ); @@ -170,22 +182,22 @@ searchpath_t *FS_FindFile( const char *name, int *index, qboolean gamedironly ); // // pak.c // -int FS_FileTimePAK( pack_t *pack ); -int FS_FindFilePAK( pack_t *pack, const char *name ); -void FS_PrintPAKInfo( char *dst, size_t size, pack_t *pack ); -void FS_ClosePAK( pack_t *pack ); -void FS_SearchPAK( stringlist_t *list, pack_t *pack, const char *pattern ); -file_t *FS_OpenPackedFile( pack_t *pack, int pack_ind ); +int FS_FileTime_PAK( searchpath_t *search, const char *filename ); +int FS_FindFile_PAK( searchpath_t *search, const char *path ); +void FS_PrintInfo_PAK( searchpath_t *search, char *dst, size_t size ); +void FS_Close_PAK( searchpath_t *search ); +void FS_Search_PAK( searchpath_t *search, stringlist_t *list, const char *pattern, int caseinsensitive ); +file_t *FS_OpenFile_PAK( searchpath_t *search, const char *filename, const char *mode, int pack_ind ); qboolean FS_AddPak_Fullpath( const char *pakfile, qboolean *already_loaded, int flags ); // // wad.c // -int FS_FileTimeWAD( wfile_t *wad ); -int FS_FindFileWAD( wfile_t *wad, const char *name ); -void FS_PrintWADInfo( char *dst, size_t size, wfile_t *wad ); -void FS_CloseWAD( wfile_t *wad ); -void FS_SearchWAD( stringlist_t *list, wfile_t *wad, const char *pattern ); +int FS_FileTime_WAD( searchpath_t *search, const char *filename ); +int FS_FindFile_WAD( searchpath_t *search, const char *path ); +void FS_PrintInfo_WAD( searchpath_t *search, char *dst, size_t size ); +void FS_Close_WAD( searchpath_t *search ); +void FS_Search_WAD( searchpath_t *search, stringlist_t *list, const char *pattern, int caseinsensitive ); byte *FS_LoadWADFile( const char *path, fs_offset_t *sizeptr, qboolean gamedironly ); qboolean FS_AddWad_Fullpath( const char *wadfile, qboolean *already_loaded, int flags ); @@ -199,15 +211,25 @@ void FS_WatchFrame( void ); // // zip.c // -int FS_FileTimeZIP( zip_t *zip ); -int FS_FindFileZIP( zip_t *zip, const char *name ); -void FS_PrintZIPInfo( char *dst, size_t size, zip_t *zip ); -void FS_CloseZIP( zip_t *zip ); -void FS_SearchZIP( stringlist_t *list, zip_t *zip, const char *pattern ); +int FS_FileTime_ZIP( searchpath_t *search, const char *filename ); +int FS_FindFile_ZIP( searchpath_t *search, const char *path ); +void FS_PrintInfo_ZIP( searchpath_t *search, char *dst, size_t size ); +void FS_Close_ZIP( searchpath_t *search ); +void FS_Search_ZIP( searchpath_t *search, stringlist_t *list, const char *pattern, int caseinsensitive ); byte *FS_LoadZIPFile( const char *path, fs_offset_t *sizeptr, qboolean gamedironly ); -file_t *FS_OpenZipFile( zip_t *zip, int pack_ind ); +file_t *FS_OpenFile_ZIP( searchpath_t *search, const char *filename, const char *mode, int pack_ind ); qboolean FS_AddZip_Fullpath( const char *zipfile, qboolean *already_loaded, int flags ); +// +// dir.c +// +void FS_PrintInfo_DIR( searchpath_t *search, char *dst, size_t size ); +void FS_Close_DIR( searchpath_t *search ); +file_t *FS_OpenFile_DIR( searchpath_t *search, const char *filename, const char *mode, int pack_ind ); +int FS_FileTime_DIR( searchpath_t *search, const char *filename ); +int FS_FindFile_DIR( searchpath_t *search, const char *path ); +void FS_Search_DIR( searchpath_t *search, stringlist_t *list, const char *pattern, int caseinsensitive ); + #ifdef __cplusplus } #endif diff --git a/filesystem/pak.c b/filesystem/pak.c index 1cc0037a..080fd975 100644 --- a/filesystem/pak.c +++ b/filesystem/pak.c @@ -230,13 +230,13 @@ FS_OpenPackedFile Open a packed file using its package file descriptor =========== */ -file_t *FS_OpenPackedFile( pack_t *pack, int pack_ind ) +file_t *FS_OpenFile_PAK( searchpath_t *search, const char *filename, const char *mode, int pack_ind ) { dpackfile_t *pfile; - pfile = &pack->files[pack_ind]; + pfile = &search->pack->files[pack_ind]; - return FS_OpenHandle( pack->filename, pack->handle, pfile->filepos, pfile->filelen ); + return FS_OpenHandle( search->pack->filename, search->pack->handle, pfile->filepos, pfile->filelen ); } /* @@ -284,6 +284,14 @@ qboolean FS_AddPak_Fullpath( const char *pakfile, qboolean *already_loaded, int search->type = SEARCHPATH_PAK; search->next = fs_searchpaths; search->flags |= flags; + + search->printinfo = FS_PrintInfo_PAK; + search->close = FS_Close_PAK; + search->openfile = FS_OpenFile_PAK; + search->filetime = FS_FileTime_PAK; + search->findfile = FS_FindFile_PAK; + search->search = FS_Search_PAK; + fs_searchpaths = search; Con_Reportf( "Adding pakfile: %s (%i files)\n", pakfile, pak->numfiles ); @@ -308,19 +316,19 @@ qboolean FS_AddPak_Fullpath( const char *pakfile, qboolean *already_loaded, int } } -int FS_FindFilePAK( pack_t *pack, const char *name ) +int FS_FindFile_PAK( searchpath_t *search, const char *path ) { int left, right, middle; // look for the file (binary search) left = 0; - right = pack->numfiles - 1; + right = search->pack->numfiles - 1; while( left <= right ) { int diff; middle = (left + right) / 2; - diff = Q_stricmp( pack->files[middle].name, name ); + diff = Q_stricmp( search->pack->files[middle].name, path ); // Found it if( !diff ) @@ -337,15 +345,15 @@ int FS_FindFilePAK( pack_t *pack, const char *name ) return -1; } -void FS_SearchPAK( stringlist_t *list, pack_t *pack, const char *pattern ) +void FS_Search_PAK( searchpath_t *search, stringlist_t *list, const char *pattern, int caseinsensitive ) { string temp; const char *slash, *backslash, *colon, *separator; int j, i; - for( i = 0; i < pack->numfiles; i++ ) + for( i = 0; i < search->pack->numfiles; i++ ) { - Q_strncpy( temp, pack->files[i].name, sizeof( temp )); + Q_strncpy( temp, search->pack->files[i].name, sizeof( temp )); while( temp[0] ) { if( matchpattern( temp, pattern, true )) @@ -377,21 +385,21 @@ void FS_SearchPAK( stringlist_t *list, pack_t *pack, const char *pattern ) } } -int FS_FileTimePAK( pack_t *pack ) +int FS_FileTime_PAK( searchpath_t *search, const char *filename ) { - return pack->filetime; + return search->pack->filetime; } -void FS_PrintPAKInfo( char *dst, size_t size, pack_t *pack ) +void FS_PrintInfo_PAK( searchpath_t *search, char *dst, size_t size ) { - Q_snprintf( dst, size, "%s (%i files)", pack->filename, pack->numfiles ); + Q_snprintf( dst, size, "%s (%i files)", search->pack->filename, search->pack->numfiles ); } -void FS_ClosePAK( pack_t *pack ) +void FS_Close_PAK( searchpath_t *search ) { - if( pack->files ) - Mem_Free( pack->files ); - if( pack->handle >= 0 ) - close( pack->handle ); - Mem_Free( pack ); -} + if( search->pack->files ) + Mem_Free( search->pack->files ); + if( search->pack->handle >= 0 ) + close( search->pack->handle ); + Mem_Free( search->pack ); +} \ No newline at end of file diff --git a/filesystem/wad.c b/filesystem/wad.c index 646476d3..b1b69ccb 100644 --- a/filesystem/wad.c +++ b/filesystem/wad.c @@ -216,6 +216,26 @@ void FS_CloseWAD( wfile_t *wad ) Mem_Free( wad ); // free himself } +/* +=========== +FS_Close_WAD +=========== +*/ +void FS_Close_WAD( searchpath_t *search ) +{ + FS_CloseWAD( search->wad ); +} + +/* +=========== +FS_OpenFile_WAD +=========== +*/ +file_t *FS_OpenFile_WAD( searchpath_t *search, const char *filename, const char *mode, int pack_ind ) +{ + return NULL; +} + /* =========== W_Open @@ -376,6 +396,14 @@ qboolean FS_AddWad_Fullpath( const char *wadfile, qboolean *already_loaded, int search->type = SEARCHPATH_WAD; search->next = fs_searchpaths; search->flags |= flags; + + search->printinfo = FS_PrintInfo_WAD; + search->close = FS_Close_WAD; + search->openfile = FS_OpenFile_WAD; + search->filetime = FS_FileTime_WAD; + search->findfile = FS_FindFile_WAD; + search->search = FS_Search_WAD; + fs_searchpaths = search; Con_Reportf( "Adding wadfile: %s (%i files)\n", wadfile, wad->numlumps ); @@ -501,20 +529,20 @@ byte *FS_LoadWADFile( const char *path, fs_offset_t *lumpsizeptr, qboolean gamed return NULL; } -int FS_FileTimeWAD( wfile_t *wad ) +int FS_FileTime_WAD( searchpath_t *search, const char *filename ) { - return wad->filetime; + return search->wad->filetime; } -void FS_PrintWADInfo( char *dst, size_t size, wfile_t *wad ) +void FS_PrintInfo_WAD( searchpath_t *search, char *dst, size_t size ) { - Q_snprintf( dst, size, "%s (%i files)", wad->filename, wad->numlumps ); + Q_snprintf( dst, size, "%s (%i files)", search->wad->filename, search->wad->numlumps ); } -int FS_FindFileWAD( wfile_t *wad, const char *name ) +int FS_FindFile_WAD( searchpath_t *search, const char *path ) { dlumpinfo_t *lump; - signed char type = W_TypeFromExt( name ); + signed char type = W_TypeFromExt( path ); qboolean anywadname = true; string wadname, wadfolder; string shortname; @@ -523,7 +551,7 @@ int FS_FindFileWAD( wfile_t *wad, const char *name ) if( type == TYP_NONE ) return -1; - COM_ExtractFilePath( name, wadname ); + COM_ExtractFilePath( path, wadname ); wadfolder[0] = '\0'; if( COM_CheckStringEmpty( wadname ) ) @@ -535,7 +563,7 @@ int FS_FindFileWAD( wfile_t *wad, const char *name ) } // make wadname from wad fullpath - COM_FileBase( wad->filename, shortname ); + COM_FileBase( search->wad->filename, shortname ); COM_DefaultExtension( shortname, ".wad" ); // quick reject by wadname @@ -544,20 +572,20 @@ int FS_FindFileWAD( wfile_t *wad, const char *name ) // NOTE: we can't using long names for wad, // because we using original wad names[16]; - COM_FileBase( name, shortname ); + COM_FileBase( path, shortname ); - lump = W_FindLump( wad, shortname, type ); + lump = W_FindLump( search->wad, shortname, type ); if( lump ) { - return lump - wad->lumps; + return lump - search->wad->lumps; } return -1; } -void FS_SearchWAD( stringlist_t *list, wfile_t *wad, const char *pattern ) +void FS_Search_WAD( searchpath_t *search, stringlist_t *list, const char *pattern, int caseinsensitive ) { string wadpattern, wadname, temp2; signed char type = W_TypeFromExt( pattern ); @@ -583,21 +611,21 @@ void FS_SearchWAD( stringlist_t *list, wfile_t *wad, const char *pattern ) } // make wadname from wad fullpath - COM_FileBase( wad->filename, temp2 ); + COM_FileBase( search->wad->filename, temp2 ); COM_DefaultExtension( temp2, ".wad" ); // quick reject by wadname if( !anywadname && Q_stricmp( wadname, temp2 )) return; - for( i = 0; i < wad->numlumps; i++ ) + for( i = 0; i < search->wad->numlumps; i++ ) { // if type not matching, we already have no chance ... - if( type != TYP_ANY && wad->lumps[i].type != type ) + if( type != TYP_ANY && search->wad->lumps[i].type != type ) continue; // build the lumpname with image suffix (if present) - Q_strncpy( temp, wad->lumps[i].name, sizeof( temp )); + Q_strncpy( temp, search->wad->lumps[i].name, sizeof( temp )); while( temp[0] ) { @@ -613,7 +641,7 @@ void FS_SearchWAD( stringlist_t *list, wfile_t *wad, const char *pattern ) { // build path: wadname/lumpname.ext Q_snprintf( temp2, sizeof(temp2), "%s/%s", wadfolder, temp ); - COM_DefaultExtension( temp2, va(".%s", W_ExtFromType( wad->lumps[i].type ))); + COM_DefaultExtension( temp2, va(".%s", W_ExtFromType( search->wad->lumps[i].type ))); stringlistappend( list, temp2 ); } } diff --git a/filesystem/zip.c b/filesystem/zip.c index 829d1553..cb40160f 100644 --- a/filesystem/zip.c +++ b/filesystem/zip.c @@ -159,6 +159,11 @@ void FS_CloseZIP( zip_t *zip ) Mem_Free( zip ); } +void FS_Close_ZIP( searchpath_t *search ) +{ + FS_CloseZIP( search->zip ); +} + /* ============ FS_SortZip @@ -392,10 +397,10 @@ FS_OpenZipFile Open a packed file using its package file descriptor =========== */ -file_t *FS_OpenZipFile( zip_t *zip, int pack_ind ) +file_t *FS_OpenFile_ZIP( searchpath_t *search, const char *filename, const char *mode, int pack_ind ) { zipfile_t *pfile; - pfile = &zip->files[pack_ind]; + pfile = &search->zip->files[pack_ind]; // compressed files handled in Zip_LoadFile if( pfile->flags != ZIP_COMPRESSION_NO_COMPRESSION ) @@ -404,7 +409,7 @@ file_t *FS_OpenZipFile( zip_t *zip, int pack_ind ) return NULL; } - return FS_OpenHandle( zip->filename, zip->handle, pfile->offset, pfile->size ); + return FS_OpenHandle( search->zip->filename, search->zip->handle, pfile->offset, pfile->size ); } byte *FS_LoadZIPFile( const char *path, fs_offset_t *sizeptr, qboolean gamedironly ) @@ -578,6 +583,14 @@ qboolean FS_AddZip_Fullpath( const char *zipfile, qboolean *already_loaded, int search->type = SEARCHPATH_ZIP; search->next = fs_searchpaths; search->flags |= flags; + + search->printinfo = FS_PrintInfo_ZIP; + search->close = FS_Close_ZIP; + search->openfile = FS_OpenFile_ZIP; + search->filetime = FS_FileTime_ZIP; + search->findfile = FS_FindFile_ZIP; + search->search = FS_Search_ZIP; + fs_searchpaths = search; Con_Reportf( "Adding zipfile: %s (%i files)\n", zipfile, zip->numfiles ); @@ -601,29 +614,29 @@ qboolean FS_AddZip_Fullpath( const char *zipfile, qboolean *already_loaded, int } } -int FS_FileTimeZIP( zip_t *zip ) +int FS_FileTime_ZIP( searchpath_t *search, const char *filename ) { - return zip->filetime; + return search->zip->filetime; } -void FS_PrintZIPInfo( char *dst, size_t size, zip_t *zip ) +void FS_PrintInfo_ZIP( searchpath_t *search, char *dst, size_t size ) { - Q_snprintf( dst, size, "%s (%i files)", zip->filename, zip->numfiles ); + Q_snprintf( dst, size, "%s (%i files)", search->zip->filename, search->zip->numfiles ); } -int FS_FindFileZIP( zip_t *zip, const char *name ) +int FS_FindFile_ZIP( searchpath_t *search, const char *path ) { int left, right, middle; // look for the file (binary search) left = 0; - right = zip->numfiles - 1; + right = search->zip->numfiles - 1; while( left <= right ) { int diff; middle = (left + right) / 2; - diff = Q_stricmp( zip->files[middle].name, name ); + diff = Q_stricmp( search->zip->files[middle].name, path ); // Found it if( !diff ) @@ -638,15 +651,15 @@ int FS_FindFileZIP( zip_t *zip, const char *name ) return -1; } -void FS_SearchZIP( stringlist_t *list, zip_t *zip, const char *pattern ) +void FS_Search_ZIP( searchpath_t *search, stringlist_t *list, const char *pattern, int caseinsensitive ) { string temp; const char *slash, *backslash, *colon, *separator; int j, i; - for( i = 0; i < zip->numfiles; i++ ) + for( i = 0; i < search->zip->numfiles; i++ ) { - Q_strncpy( temp, zip->files[i].name, sizeof( temp )); + Q_strncpy( temp, search->zip->files[i].name, sizeof( temp )); while( temp[0] ) { if( matchpattern( temp, pattern, true )) From 89807250e51b4f9f329e1b02e76ac477caf6feb5 Mon Sep 17 00:00:00 2001 From: Velaron Date: Mon, 21 Nov 2022 12:38:10 +0200 Subject: [PATCH 193/490] filesystem: dir.c: optimize string operations --- filesystem/dir.c | 16 +++++++++------- 1 file changed, 9 insertions(+), 7 deletions(-) diff --git a/filesystem/dir.c b/filesystem/dir.c index a46af43c..b299536d 100644 --- a/filesystem/dir.c +++ b/filesystem/dir.c @@ -57,21 +57,25 @@ void FS_Search_DIR( searchpath_t *search, stringlist_t *list, const char *patter slash = Q_strrchr( pattern, '/' ); backslash = Q_strrchr( pattern, '\\' ); colon = Q_strrchr( pattern, ':' ); + separator = Q_max( slash, backslash ); separator = Q_max( separator, colon ); + basepathlength = separator ? (separator + 1 - pattern) : 0; basepath = Mem_Calloc( fs_mempool, basepathlength + 1 ); if( basepathlength ) memcpy( basepath, pattern, basepathlength ); - basepath[basepathlength] = 0; + basepath[basepathlength] = '\0'; - Q_sprintf( netpath, "%s%s", search->filename, basepath ); + Q_strcpy( temp, basepath ); stringlistinit( &dirlist ); listdirectory( &dirlist, netpath, caseinsensitive ); + Q_strcpy( temp, basepath ); + for( dirlistindex = 0; dirlistindex < dirlist.numstrings; dirlistindex++ ) { - Q_sprintf( temp, "%s%s", basepath, dirlist.strings[dirlistindex] ); + Q_strcpy( &temp[basepathlength], dirlist.strings[dirlistindex] ); if( matchpattern( temp, (char *)pattern, true ) ) { @@ -91,20 +95,18 @@ void FS_Search_DIR( searchpath_t *search, stringlist_t *list, const char *patter Mem_Free( basepath ); } -int FS_FileTime_DIR( struct searchpath_s *search, const char *filename ) +int FS_FileTime_DIR( searchpath_t *search, const char *filename ) { char path[MAX_SYSPATH]; - // found in the filesystem? Q_sprintf( path, "%s%s", search->filename, filename ); return FS_SysFileTime( path ); } -file_t *FS_OpenFile_DIR( struct searchpath_s *search, const char *filename, const char *mode, int pack_ind ) +file_t *FS_OpenFile_DIR( searchpath_t *search, const char *filename, const char *mode, int pack_ind ) { char path[MAX_SYSPATH]; - // found in the filesystem? Q_sprintf( path, "%s%s", search->filename, filename ); return FS_SysOpen( path, mode ); } \ No newline at end of file From fcd741e2b88357f550bf411bc330810688f44f86 Mon Sep 17 00:00:00 2001 From: Velaron Date: Mon, 21 Nov 2022 13:35:23 +0200 Subject: [PATCH 194/490] filesystem: fix FS_FixFileCase behavior --- filesystem/filesystem.c | 38 ++++++++++++++++++++++---------------- 1 file changed, 22 insertions(+), 16 deletions(-) diff --git a/filesystem/filesystem.c b/filesystem/filesystem.c index e8de5277..82327043 100644 --- a/filesystem/filesystem.c +++ b/filesystem/filesystem.c @@ -284,57 +284,63 @@ const char *FS_FixFileCase( const char *path ) #elif !XASH_WIN32 && !XASH_IOS // assume case insensitive DIR *dir; struct dirent *entry; - char path2[PATH_MAX], *fname; + char dirpath[PATH_MAX], *filename; + static char fixedpath[PATH_MAX]; if( !fs_caseinsensitive ) return path; if( path[0] != '/' ) - Q_snprintf( path2, sizeof( path2 ), "./%s", path ); - else Q_strncpy( path2, path, PATH_MAX ); + Q_snprintf( dirpath, sizeof( dirpath ), "./%s", path ); + else Q_strncpy( dirpath, path, PATH_MAX ); - fname = Q_strrchr( path2, '/' ); + filename = Q_strrchr( dirpath, '/' ); - if( fname ) - *fname++ = 0; + if( filename ) + *filename++ = '\0'; else { - fname = (char*)path; - Q_strcpy( path2, "."); + filename = (char*)path; + Q_strcpy( dirpath, "."); } /* android has too slow directory scanning, so drop out some not useful cases */ - if( fname - path2 > 4 ) + if( filename - dirpath > 4 ) { char *point; // too many wad textures - if( !Q_stricmp( fname - 5, ".wad") ) + if( !Q_stricmp( filename - 5, ".wad") ) return path; - point = Q_strchr( fname, '.' ); + point = Q_strchr( filename, '.' ); if( point ) { if( !Q_strcmp( point, ".mip") || !Q_strcmp( point, ".dds" ) || !Q_strcmp( point, ".ent" ) ) return path; - if( fname[0] == '{' ) + if( filename[0] == '{' ) return path; } } //Con_Reportf( "FS_FixFileCase: %s\n", path ); - if( !( dir = opendir( path2 ) ) ) + if( !( dir = opendir( dirpath ) ) ) return path; while( ( entry = readdir( dir ) ) ) { - if( Q_stricmp( entry->d_name, fname ) ) + if( Q_stricmp( entry->d_name, filename ) ) continue; - path = va( "%s/%s", path2, entry->d_name ); - //Con_Reportf( "FS_FixFileCase: %s %s %s\n", path2, fname, entry->d_name ); + Q_snprintf( fixedpath, sizeof( fixedpath ), "%s/%s", dirpath, entry->d_name ); + + //Con_Reportf( "FS_FixFileCase: %s %s %s\n", dirpath, filename, entry->d_name ); + + path = fixedpath; + break; } + closedir( dir ); #endif return path; From 43c61759189bf35c6642b28df499afef036a0259 Mon Sep 17 00:00:00 2001 From: Velaron Date: Mon, 21 Nov 2022 14:11:55 +0200 Subject: [PATCH 195/490] filesystem: dir.c: safer string operations --- filesystem/dir.c | 12 ++++++------ filesystem/filesystem.c | 2 +- 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/filesystem/dir.c b/filesystem/dir.c index b299536d..c93359d7 100644 --- a/filesystem/dir.c +++ b/filesystem/dir.c @@ -38,7 +38,7 @@ int FS_FindFile_DIR( searchpath_t *search, const char *path ) { char netpath[MAX_SYSPATH]; - Q_sprintf( netpath, "%s%s", search->filename, path ); + Q_snprintf( netpath, sizeof( netpath ), "%s%s", search->filename, path ); if( FS_SysFileExists( netpath, !( search->flags & FS_CUSTOM_PATH ) ) ) return 0; @@ -66,16 +66,16 @@ void FS_Search_DIR( searchpath_t *search, stringlist_t *list, const char *patter if( basepathlength ) memcpy( basepath, pattern, basepathlength ); basepath[basepathlength] = '\0'; - Q_strcpy( temp, basepath ); + Q_snprintf( netpath, sizeof( netpath ), "%s%s", search->filename, basepath ); stringlistinit( &dirlist ); listdirectory( &dirlist, netpath, caseinsensitive ); - Q_strcpy( temp, basepath ); + Q_strncpy( temp, basepath, sizeof( temp ) ); for( dirlistindex = 0; dirlistindex < dirlist.numstrings; dirlistindex++ ) { - Q_strcpy( &temp[basepathlength], dirlist.strings[dirlistindex] ); + Q_strncpy( &temp[basepathlength], dirlist.strings[dirlistindex], sizeof( temp ) - basepathlength ); if( matchpattern( temp, (char *)pattern, true ) ) { @@ -99,7 +99,7 @@ int FS_FileTime_DIR( searchpath_t *search, const char *filename ) { char path[MAX_SYSPATH]; - Q_sprintf( path, "%s%s", search->filename, filename ); + Q_snprintf( path, sizeof( path ), "%s%s", search->filename, filename ); return FS_SysFileTime( path ); } @@ -107,6 +107,6 @@ file_t *FS_OpenFile_DIR( searchpath_t *search, const char *filename, const char { char path[MAX_SYSPATH]; - Q_sprintf( path, "%s%s", search->filename, filename ); + Q_snprintf( path, sizeof( path ), "%s%s", search->filename, filename ); return FS_SysOpen( path, mode ); } \ No newline at end of file diff --git a/filesystem/filesystem.c b/filesystem/filesystem.c index 82327043..ba84a143 100644 --- a/filesystem/filesystem.c +++ b/filesystem/filesystem.c @@ -2643,7 +2643,7 @@ search_t *FS_Search( const char *pattern, int caseinsensitive, int gamedironly ) { search_t *search = NULL; searchpath_t *searchpath; - int i, numfiles, numchars; + int i, numfiles, numchars; stringlist_t resultlist; if( pattern[0] == '.' || pattern[0] == ':' || pattern[0] == '/' || pattern[0] == '\\' ) From 4c6fbafd20700182acbdfbc3388eb8267df8eb89 Mon Sep 17 00:00:00 2001 From: Alibek Omarov Date: Fri, 25 Nov 2022 02:36:38 +0300 Subject: [PATCH 196/490] mainui: update --- 3rdparty/mainui | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/3rdparty/mainui b/3rdparty/mainui index 0f31c646..f2d7e1cb 160000 --- a/3rdparty/mainui +++ b/3rdparty/mainui @@ -1 +1 @@ -Subproject commit 0f31c646d623d75b46a9b16f887ac29a167319a3 +Subproject commit f2d7e1cb8b566245281ea909855faf9259f56d2f From b6bd4bc6f8aea87e3ba9d7f9183fd59c3ea28165 Mon Sep 17 00:00:00 2001 From: Alibek Omarov Date: Fri, 25 Nov 2022 05:45:20 +0300 Subject: [PATCH 197/490] engine: server: precisely set client connected time after they were spawned --- engine/server/sv_client.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/engine/server/sv_client.c b/engine/server/sv_client.c index 4ea44b03..ba417dec 100644 --- a/engine/server/sv_client.c +++ b/engine/server/sv_client.c @@ -2106,6 +2106,8 @@ static qboolean SV_Begin_f( sv_client_t *cl ) // now client is spawned cl->state = cs_spawned; + cl->connecttime = host.realtime; + return true; } From 93893050729ac83266b4dbf198cae5ec6d60dde5 Mon Sep 17 00:00:00 2001 From: Alibek Omarov Date: Fri, 25 Nov 2022 22:23:51 +0300 Subject: [PATCH 198/490] engine: common: set cl_filterstuffcmd to 1 by default --- engine/common/cvar.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/engine/common/cvar.c b/engine/common/cvar.c index 6a8e5c0d..3b998cf2 100644 --- a/engine/common/cvar.c +++ b/engine/common/cvar.c @@ -21,7 +21,7 @@ GNU General Public License for more details. convar_t *cvar_vars = NULL; // head of list convar_t *cmd_scripting; -CVAR_DEFINE_AUTO( cl_filterstuffcmd, "0", FCVAR_ARCHIVE | FCVAR_PRIVILEGED, "filter commands coming from server" ); +CVAR_DEFINE_AUTO( cl_filterstuffcmd, "1", FCVAR_ARCHIVE | FCVAR_PRIVILEGED, "filter commands coming from server" ); /* ============ From f6d899696882ef68b560be02662241a55a327445 Mon Sep 17 00:00:00 2001 From: SNMetamorph <25657591+SNMetamorph@users.noreply.github.com> Date: Thu, 24 Nov 2022 03:41:37 +0400 Subject: [PATCH 199/490] engine: common: imagelib: added missed BMP compression type macros --- engine/common/imagelib/img_bmp.h | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/engine/common/imagelib/img_bmp.h b/engine/common/imagelib/img_bmp.h index a5728eeb..baed6380 100644 --- a/engine/common/imagelib/img_bmp.h +++ b/engine/common/imagelib/img_bmp.h @@ -25,7 +25,12 @@ GNU General Public License for more details. #define BI_FILE_HEADER_SIZE 14 #define BI_SIZE 40 // size of bitmap info header. #if !defined(BI_RGB) -#define BI_RGB 0 // uncompressed RGB bitmap(defined in wingdi.h) +#define BI_RGB 0 // uncompressed RGB bitmap (defined in wingdi.h) +#define BI_RLE8 1 +#define BI_RLE4 2 +#define BI_BITFIELDS 3 +#define BI_JPEG 4 +#define BI_PNG 5 #endif #pragma pack( push, 1 ) From 871784333358c66d6ed133f5a26a4cc084ec0cdc Mon Sep 17 00:00:00 2001 From: SNMetamorph <25657591+SNMetamorph@users.noreply.github.com> Date: Tue, 22 Nov 2022 22:14:58 +0400 Subject: [PATCH 200/490] engine: common: imagelib: fixed BMP files estimate size calculation with NPOT textures --- engine/common/imagelib/img_bmp.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/engine/common/imagelib/img_bmp.c b/engine/common/imagelib/img_bmp.c index c81c3701..1fa0c4c3 100644 --- a/engine/common/imagelib/img_bmp.c +++ b/engine/common/imagelib/img_bmp.c @@ -174,7 +174,7 @@ qboolean Image_LoadBMP( const char *name, const byte *buffer, fs_offset_t filesi break; } - estimatedSize = ( buf_p - buffer ) + ( image.width + padSize ) * image.height * ( bhdr.bitsPerPixel >> 3 ); + estimatedSize = ( buf_p - buffer ) + image.width * image.height * ( bhdr.bitsPerPixel >> 3 ); if( filesize < estimatedSize ) { if( image.palette ) From 49fc6143abaa1ea4262093ba47404bb2a7d15476 Mon Sep 17 00:00:00 2001 From: SNMetamorph <25657591+SNMetamorph@users.noreply.github.com> Date: Tue, 22 Nov 2022 22:13:02 +0400 Subject: [PATCH 201/490] engine: common: imagelib: fixed loading 32 bits per pixel BMP files --- engine/common/imagelib/img_bmp.c | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/engine/common/imagelib/img_bmp.c b/engine/common/imagelib/img_bmp.c index 1fa0c4c3..f0f9785a 100644 --- a/engine/common/imagelib/img_bmp.c +++ b/engine/common/imagelib/img_bmp.c @@ -69,8 +69,11 @@ qboolean Image_LoadBMP( const char *name, const byte *buffer, fs_offset_t filesi // bogus compression? Only non-compressed supported. if( bhdr.compression != BI_RGB ) { - Con_DPrintf( S_ERROR "Image_LoadBMP: only uncompressed BMP files supported (%s)\n", name ); - return false; + if( bhdr.bitsPerPixel != 32 || bhdr.compression != BI_BITFIELDS ) + { + Con_DPrintf( S_ERROR "Image_LoadBMP: only uncompressed BMP files supported (%s)\n", name ); + return false; + } } image.width = columns = bhdr.width; From 809d5f1aa8f39f84b104bdf8139efab11ee4179d Mon Sep 17 00:00:00 2001 From: Andrey Akhmichin <15944199+nekonomicon@users.noreply.github.com> Date: Thu, 24 Nov 2022 20:36:30 +0500 Subject: [PATCH 202/490] engine: client: in_touch.c: add spray button. --- engine/client/in_touch.c | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/engine/client/in_touch.c b/engine/client/in_touch.c index f8136e6d..7cf93925 100644 --- a/engine/client/in_touch.c +++ b/engine/client/in_touch.c @@ -1016,18 +1016,18 @@ void Touch_Init( void ) Touch_AddDefaultButton( "jump", "touch_default/jump", "+jump", 0.880000, 0.227228, 1.000000, 0.454457, color, 2, 1, 0 ); Touch_AddDefaultButton( "attack", "touch_default/shoot", "+attack", 0.760000, 0.530200, 0.880000, 0.757428, color, 2, 1, 0 ); Touch_AddDefaultButton( "attack2", "touch_default/shoot_alt", "+attack2", 0.760000, 0.302971, 0.880000, 0.530200, color, 2, 1, 0 ); - Touch_AddDefaultButton( "loadquick", "touch_default/load", "loadquick", 0.760000, 0.000000, 0.840000, 0.151486, color, 2, 1, 16 ); - Touch_AddDefaultButton( "savequick", "touch_default/save", "savequick", 0.840000, 0.000000, 0.920000, 0.151486, color, 2, 1, 16 ); - Touch_AddDefaultButton( "messagemode", "touch_default/keyboard", "messagemode", 0.840000, 0.000000, 0.920000, 0.151486, color, 2, 1, 8 ); + Touch_AddDefaultButton( "loadquick", "touch_default/load", "loadquick", 0.680000, 0.000000, 0.760000, 0.151486, color, 2, 1, 16 ); + Touch_AddDefaultButton( "savequick", "touch_default/save", "savequick", 0.760000, 0.000000, 0.840000, 0.151486, color, 2, 1, 16 ); + Touch_AddDefaultButton( "messagemode", "touch_default/keyboard", "messagemode", 0.760000, 0.000000, 0.840000, 0.151486, color, 2, 1, 8 ); Touch_AddDefaultButton( "reload", "touch_default/reload", "+reload", 0.000000, 0.302971, 0.120000, 0.530200, color, 2, 1, 0 ); Touch_AddDefaultButton( "flashlight", "touch_default/flash_light_filled", "impulse 100", 0.920000, 0.000000, 1.000000, 0.151486, color, 2, 1, 0 ); - Touch_AddDefaultButton( "scores", "touch_default/map", "+showscores", 0.760000, 0.000000, 0.840000, 0.151486, color, 2, 1, 8 ); + Touch_AddDefaultButton( "scores", "touch_default/map", "+showscores", 0.680000, 0.000000, 0.760000, 0.151486, color, 2, 1, 8 ); Touch_AddDefaultButton( "show_numbers", "touch_default/show_weapons", "exec touch_default/numbers.cfg", 0.440000, 0.833171, 0.520000, 0.984656, color, 2, 1, 0 ); Touch_AddDefaultButton( "duck", "touch_default/crouch", "+duck", 0.880000, 0.757428, 1.000000, 0.984656, color, 2, 1, 0 ); Touch_AddDefaultButton( "tduck", "touch_default/tduck", ";+duck", 0.560000, 0.833171, 0.620000, 0.946785, color, 2, 1, 0 ); Touch_AddDefaultButton( "edit", "touch_default/settings", "touch_enableedit", 0.420000, 0.000000, 0.500000, 0.151486, color, 2, 1, 32 ); Touch_AddDefaultButton( "menu", "touch_default/menu", "escape", 0.000000, 0.833171, 0.080000, 0.984656, color, 2, 1, 0 ); - + Touch_AddDefaultButton( "spray", "touch_default/spray", "impulse 201", 0.840000, 0.000000, 0.920000, 0.151486, color, 2, 1, 0 ); Cmd_AddCommand( "touch_addbutton", Touch_AddButton_f, "add native touch button" ); Cmd_AddCommand( "touch_removebutton", IN_TouchRemoveButton_f, "remove native touch button" ); From 65671d87880df6c5a2a07ce81ed10d9a7d910665 Mon Sep 17 00:00:00 2001 From: SNMetamorph <25657591+SNMetamorph@users.noreply.github.com> Date: Fri, 25 Nov 2022 20:56:11 +0400 Subject: [PATCH 203/490] engine: common: minor code fixes in hpak.c --- engine/common/hpak.c | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/engine/common/hpak.c b/engine/common/hpak.c index de5a663c..9264061a 100644 --- a/engine/common/hpak.c +++ b/engine/common/hpak.c @@ -17,8 +17,8 @@ GNU General Public License for more details. #include "hpak.h" #define HPAK_MAX_ENTRIES 0x8000 -#define HPAK_MIN_SIZE (1 * 1024) -#define HPAK_MAX_SIZE (128 * 1024) +#define HPAK_ENTRY_MIN_SIZE (512) +#define HPAK_ENTRY_MAX_SIZE (128 * 1024) typedef struct hash_pack_queue_s { @@ -216,7 +216,7 @@ void HPAK_AddLump( qboolean bUseQueue, const char *name, resource_t *pResource, if( pData == NULL && pFile == NULL ) return; - if( pResource->nDownloadSize < HPAK_MIN_SIZE || pResource->nDownloadSize > HPAK_MAX_SIZE ) + if( pResource->nDownloadSize < HPAK_ENTRY_MIN_SIZE || pResource->nDownloadSize > HPAK_ENTRY_MAX_SIZE ) { Con_Printf( S_ERROR "%s: invalid size %s\n", name, Q_pretifymem( pResource->nDownloadSize, 2 )); return; @@ -325,7 +325,7 @@ void HPAK_AddLump( qboolean bUseQueue, const char *name, resource_t *pResource, // make a new container dstpak.count = srcpak.count + 1; dstpak.entries = Z_Malloc( sizeof( hpak_lump_t ) * dstpak.count ); - memcpy( dstpak.entries, srcpak.entries, srcpak.count ); + memcpy( dstpak.entries, srcpak.entries, sizeof( hpak_lump_t ) * srcpak.count ); for( i = 0; i < srcpak.count; i++ ) { @@ -431,7 +431,7 @@ static qboolean HPAK_Validate( const char *filename, qboolean quiet ) for( i = 0; i < num_lumps; i++ ) { - if( dataDir[i].disksize < 1 || dataDir[i].disksize > 131071 ) + if( dataDir[i].disksize < HPAK_ENTRY_MIN_SIZE || dataDir[i].disksize > HPAK_ENTRY_MAX_SIZE ) { // odd max size Con_DPrintf( S_ERROR "HPAK_ValidatePak: lump %i has invalid size %s\n", i, Q_pretifymem( dataDir[i].disksize, 2 )); @@ -512,7 +512,7 @@ void HPAK_CheckSize( const char *filename ) Q_strncpy( pakname, filename, sizeof( pakname )); COM_ReplaceExtension( pakname, ".hpk" ); - if( FS_FileSize( pakname, false ) > ( maxsize * 1000000 )) + if( FS_FileSize( pakname, false ) > ( maxsize * 1048576 )) { Con_Printf( "Server: Size of %s > %f MB, deleting.\n", filename, hpk_maxsize->value ); Log_Printf( "Server: Size of %s > %f MB, deleting.\n", filename, hpk_maxsize->value ); @@ -1035,7 +1035,7 @@ void HPAK_Extract_f( void ) Con_Printf( "Extracting %i: %10s %s %s\n", nCurrent + 1, type, size, lumpname ); - if( entry->disksize <= 0 || entry->disksize >= HPAK_MAX_SIZE ) + if( entry->disksize < HPAK_ENTRY_MIN_SIZE || entry->disksize > HPAK_ENTRY_MAX_SIZE ) { Con_DPrintf( S_WARN "Unable to extract data, size invalid: %s\n", Q_memprint( entry->disksize )); continue; From a2971ce939b6c61dcd7e9b5071df226ea57412b1 Mon Sep 17 00:00:00 2001 From: SNMetamorph <25657591+SNMetamorph@users.noreply.github.com> Date: Sat, 26 Nov 2022 03:31:24 +0400 Subject: [PATCH 204/490] filesystem: fixed uninitialized pointers in FS_FindFile when fs_ext_path enabled --- filesystem/filesystem.c | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/filesystem/filesystem.c b/filesystem/filesystem.c index ba84a143..70270bc2 100644 --- a/filesystem/filesystem.c +++ b/filesystem/filesystem.c @@ -1824,6 +1824,13 @@ searchpath_t *FS_FindFile( const char *name, int *index, qboolean gamedironly ) search = &fs_directpath; memset( search, 0, sizeof( searchpath_t )); + search->printinfo = FS_PrintInfo_DIR; + search->close = FS_Close_DIR; + search->openfile = FS_OpenFile_DIR; + search->filetime = FS_FileTime_DIR; + search->findfile = FS_FindFile_DIR; + search->search = FS_Search_DIR; + // root folder has a more priority than netpath Q_strncpy( search->filename, fs_rootdir, sizeof( search->filename )); Q_strcat( search->filename, PATH_SPLITTER ); From f377461fdfe1ac09654a2433feba291ccd87c7ec Mon Sep 17 00:00:00 2001 From: Alibek Omarov Date: Sun, 27 Nov 2022 04:44:41 +0300 Subject: [PATCH 205/490] engine: common: made a filter for a filter (lol), so it's possible to play selected games with cl_filterstuffcmd enabled --- engine/common/cvar.c | 77 ++++++++++++++++++++++++++++++++++++++++++++ engine/common/cvar.h | 1 + engine/common/host.c | 1 + 3 files changed, 79 insertions(+) diff --git a/engine/common/cvar.c b/engine/common/cvar.c index 3b998cf2..b89abdfb 100644 --- a/engine/common/cvar.c +++ b/engine/common/cvar.c @@ -21,6 +21,29 @@ GNU General Public License for more details. convar_t *cvar_vars = NULL; // head of list convar_t *cmd_scripting; +#ifdef HACKS_RELATED_HLMODS +typedef struct cvar_filter_quirks_s +{ + const char *gamedir; // gamedir to enable for + const char *cvars; // list of cvars should be excluded from filter +} cvar_filter_quirks_t; + +static cvar_filter_quirks_t cvar_filter_quirks[] = +{ + // EXAMPLE: + //{ + // "valve", + // "test;test1;test100" + //}, + { + "ricochet", + "r_drawviewmodel", + }, +}; + +static cvar_filter_quirks_t *cvar_active_filter_quirks = NULL; +#endif + CVAR_DEFINE_AUTO( cl_filterstuffcmd, "1", FCVAR_ARCHIVE | FCVAR_PRIVILEGED, "filter commands coming from server" ); /* @@ -902,6 +925,39 @@ static qboolean Cvar_ShouldSetCvar( convar_t *v, qboolean isPrivileged ) if( cl_filterstuffcmd.value <= 0.0f ) return true; +#ifdef HACKS_RELATED_HLMODS + // check if game-specific filter exceptions should be applied + // TODO: for cmd exceptions, make generic function + if( cvar_active_filter_quirks ) + { + const char *cur, *next; + + cur = cvar_active_filter_quirks->cvars; + next = Q_strchr( cur, ';' ); + + // TODO: implement Q_strchrnul + while( cur && *cur ) + { + size_t len = next ? next - cur : Q_strlen( cur ); + + // found, quit + if( !Q_strnicmp( cur, v->name, len )) + return true; + + if( next ) + { + cur = next + 1; + next = Q_strchr( cur, ';' ); + } + else + { + // stop + cur = NULL; + } + } + } +#endif + if( FBitSet( v->flags, FCVAR_FILTERABLE )) return false; @@ -1157,6 +1213,7 @@ Reads in all archived cvars void Cvar_Init( void ) { cvar_vars = NULL; + cvar_active_filter_quirks = NULL; cmd_scripting = Cvar_Get( "cmd_scripting", "0", FCVAR_ARCHIVE|FCVAR_PRIVILEGED, "enable simple condition checking and variable operations" ); Cvar_RegisterVariable( &host_developer ); // early registering for dev Cvar_RegisterVariable( &cl_filterstuffcmd ); @@ -1167,6 +1224,26 @@ void Cvar_Init( void ) Cmd_AddCommand( "cvarlist", Cvar_List_f, "display all console variables beginning with the specified prefix" ); } +/* +============ +Cvar_PostFSInit + +============ +*/ +void Cvar_PostFSInit( void ) +{ + int i; + + for( i = 0; i < ARRAYSIZE( cvar_filter_quirks ); i++ ) + { + if( !Q_stricmp( cvar_filter_quirks[i].gamedir, GI->gamefolder )) + { + cvar_active_filter_quirks = &cvar_filter_quirks[i]; + break; + } + } +} + #if XASH_ENGINE_TESTS #include "tests.h" diff --git a/engine/common/cvar.h b/engine/common/cvar.h index 5d5477f7..b234e919 100644 --- a/engine/common/cvar.h +++ b/engine/common/cvar.h @@ -78,6 +78,7 @@ void Cvar_Reset( const char *var_name ); void Cvar_SetCheatState( void ); qboolean Cvar_CommandWithPrivilegeCheck( convar_t *v, qboolean isPrivileged ); void Cvar_Init( void ); +void Cvar_PostFSInit( void ); void Cvar_Unlink( int group ); #endif//CVAR_H diff --git a/engine/common/host.c b/engine/common/host.c index 702422f0..ddba3bd0 100644 --- a/engine/common/host.c +++ b/engine/common/host.c @@ -1067,6 +1067,7 @@ void Host_InitCommon( int argc, char **argv, const char *progname, qboolean bCha #endif FS_LoadGameInfo( NULL ); + Cvar_PostFSInit(); if( FS_FileExists( va( "%s.rc", SI.basedirName ), false )) Q_strncpy( SI.rcName, SI.basedirName, sizeof( SI.rcName )); // e.g. valve.rc From 084fac36066aa22a8422a012f5d036ed3e23ed37 Mon Sep 17 00:00:00 2001 From: SNMetamorph <25657591+SNMetamorph@users.noreply.github.com> Date: Sat, 26 Nov 2022 05:34:05 +0400 Subject: [PATCH 206/490] engine: common: hpak: fixed rest of bugs in HPAK_AddLump --- engine/common/hpak.c | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/engine/common/hpak.c b/engine/common/hpak.c index 9264061a..a949fe5b 100644 --- a/engine/common/hpak.c +++ b/engine/common/hpak.c @@ -226,7 +226,7 @@ void HPAK_AddLump( qboolean bUseQueue, const char *name, resource_t *pResource, memset( &ctx, 0, sizeof( MD5Context_t )); MD5Init( &ctx ); - if( pData == NULL ) + if( !pData ) { byte *temp; @@ -327,9 +327,10 @@ void HPAK_AddLump( qboolean bUseQueue, const char *name, resource_t *pResource, dstpak.entries = Z_Malloc( sizeof( hpak_lump_t ) * dstpak.count ); memcpy( dstpak.entries, srcpak.entries, sizeof( hpak_lump_t ) * srcpak.count ); + // check is there are entry with same hash for( i = 0; i < srcpak.count; i++ ) { - if( memcmp( md5, srcpak.entries[i].resource.rgucMD5_hash, 16 )) + if( memcmp( md5, srcpak.entries[i].resource.rgucMD5_hash, 16 ) == 0 ) { pCurrentEntry = &dstpak.entries[i]; @@ -347,8 +348,10 @@ void HPAK_AddLump( qboolean bUseQueue, const char *name, resource_t *pResource, pCurrentEntry->filepos = FS_Tell( file_dst ); pCurrentEntry->disksize = pResource->nDownloadSize; - if( !pData ) FS_FileCopy( file_dst, file_src, pCurrentEntry->disksize ); - else FS_Write( file_dst, pData, pCurrentEntry->disksize ); + if( !pData ) + FS_FileCopy( file_dst, pFile, pCurrentEntry->disksize ); + else + FS_Write( file_dst, pData, pCurrentEntry->disksize ); hash_pack_header.infotableofs = FS_Tell( file_dst ); FS_Write( file_dst, &dstpak.count, sizeof( dstpak.count )); From 3ab749cc2eae9da981a4cbfa98993e07e9a704ad Mon Sep 17 00:00:00 2001 From: SNMetamorph <25657591+SNMetamorph@users.noreply.github.com> Date: Sat, 26 Nov 2022 21:52:46 +0400 Subject: [PATCH 207/490] engine: client: cl_tent: fixed incorrect players spray textures update --- engine/client/cl_tent.c | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/engine/client/cl_tent.c b/engine/client/cl_tent.c index 451eab3d..e8bde1fc 100644 --- a/engine/client/cl_tent.c +++ b/engine/client/cl_tent.c @@ -2928,10 +2928,14 @@ void CL_PlayerDecal( int playernum, int customIndex, int entityIndex, float *pos { if( !pCust->nUserData1 ) { - qboolean updateSprayTexture; + int sprayTextureIndex; const char *decalname = va( "player%dlogo%d", playernum, customIndex ); - updateSprayTexture = ref.dllFuncs.GL_FindTexture( decalname ) != 0; - pCust->nUserData1 = ref.dllFuncs.GL_LoadTextureFromBuffer( decalname, pCust->pInfo, TF_DECAL, updateSprayTexture ); + sprayTextureIndex = ref.dllFuncs.GL_FindTexture( decalname ); + if( sprayTextureIndex != 0 ) + { + ref.dllFuncs.GL_FreeTexture( sprayTextureIndex ); + } + pCust->nUserData1 = GL_LoadTextureInternal( decalname, pCust->pInfo, TF_DECAL ); } textureIndex = pCust->nUserData1; } From 591d572870743dc76e64a6d3f677edb729793017 Mon Sep 17 00:00:00 2001 From: Alibek Omarov Date: Sun, 27 Nov 2022 06:01:13 +0300 Subject: [PATCH 208/490] mainui: update --- 3rdparty/mainui | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/3rdparty/mainui b/3rdparty/mainui index f2d7e1cb..9fdcd6c9 160000 --- a/3rdparty/mainui +++ b/3rdparty/mainui @@ -1 +1 @@ -Subproject commit f2d7e1cb8b566245281ea909855faf9259f56d2f +Subproject commit 9fdcd6c9b0e49a6798becf35ef354bc2c25c3057 From 93b2f535e22668df5fbafae98cb271a236f4da34 Mon Sep 17 00:00:00 2001 From: Alibek Omarov Date: Sun, 27 Nov 2022 18:26:53 +0300 Subject: [PATCH 209/490] engine: client: tune max commands/max backup commands values for legacy protocol --- engine/client/cl_main.c | 5 +++-- engine/common/protocol.h | 5 +++-- 2 files changed, 6 insertions(+), 4 deletions(-) diff --git a/engine/client/cl_main.c b/engine/client/cl_main.c index 6886ab00..3984bb82 100644 --- a/engine/client/cl_main.c +++ b/engine/client/cl_main.c @@ -739,7 +739,7 @@ void CL_WritePacket( void ) MSG_Init( &buf, "ClientData", data, sizeof( data )); // Determine number of backup commands to send along - numbackup = bound( 0, cl_cmdbackup->value, MAX_BACKUP_COMMANDS ); + numbackup = bound( 0, cl_cmdbackup->value, cls.legacymode ? MAX_LEGACY_BACKUP_CMDS : MAX_BACKUP_COMMANDS ); if( cls.state == ca_connected ) numbackup = 0; // clamp cmdrate @@ -819,12 +819,13 @@ void CL_WritePacket( void ) newcmds = ( cls.netchan.outgoing_sequence - cls.lastoutgoingcommand ); // put an upper/lower bound on this - newcmds = bound( 0, newcmds, cls.legacymode?MAX_LEGACY_TOTAL_CMDS:MAX_TOTAL_CMDS ); + newcmds = bound( 0, newcmds, cls.legacymode ? MAX_LEGACY_TOTAL_CMDS: MAX_TOTAL_CMDS ); if( cls.state == ca_connected ) newcmds = 0; MSG_WriteByte( &buf, newcmds ); numcmds = newcmds + numbackup; + from = -1; for( i = numcmds - 1; i >= 0; i-- ) diff --git a/engine/common/protocol.h b/engine/common/protocol.h index 7d00ba0b..ec0e9230 100644 --- a/engine/common/protocol.h +++ b/engine/common/protocol.h @@ -298,7 +298,8 @@ extern const char *clc_strings[clc_lastmsg+1]; #define SND_LEGACY_LARGE_INDEX (1<<2) // a send sound as short #define MAX_LEGACY_ENTITY_BITS 12 #define MAX_LEGACY_WEAPON_BITS 5 -#define MAX_LEGACY_MODEL_BITS 11 -#define MAX_LEGACY_TOTAL_CMDS 28 // magic number from old engine's sv_client.c +#define MAX_LEGACY_MODEL_BITS 11 +#define MAX_LEGACY_TOTAL_CMDS 16 // 28 - 16 = 12 real legacy max backup +#define MAX_LEGACY_BACKUP_CMDS 12 #endif//NET_PROTOCOL_H From 7013d447ca4bcd94b095489cb338277e870444ba Mon Sep 17 00:00:00 2001 From: Alibek Omarov Date: Mon, 28 Nov 2022 07:54:24 +0300 Subject: [PATCH 210/490] engine: client: get rid of s_registration_sequence, it was incorrect way to look for unused sounds. We're trying to rely on cl.servercount here --- engine/client/s_load.c | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/engine/client/s_load.c b/engine/client/s_load.c index da657038..8a81af3d 100644 --- a/engine/client/s_load.c +++ b/engine/client/s_load.c @@ -14,6 +14,7 @@ GNU General Public License for more details. */ #include "common.h" +#include "client.h" #include "sound.h" // during registration it is possible to have more sounds @@ -28,7 +29,6 @@ static sfx_t s_knownSfx[MAX_SFX]; static sfx_t *s_sfxHashList[MAX_SFX_HASH]; static string s_sentenceImmediateName; // keep dummy sentence name qboolean s_registering = false; -int s_registration_sequence = 0; /* ================= @@ -199,7 +199,7 @@ sfx_t *S_FindName( const char *pname, int *pfInCache ) *pfInCache = ( sfx->cache != NULL ) ? true : false; } // prolonge registration - sfx->servercount = s_registration_sequence; + sfx->servercount = cl.servercount; return sfx; } } @@ -219,7 +219,7 @@ sfx_t *S_FindName( const char *pname, int *pfInCache ) memset( sfx, 0, sizeof( *sfx )); if( pfInCache ) *pfInCache = false; Q_strncpy( sfx->name, name, MAX_STRING ); - sfx->servercount = s_registration_sequence; + sfx->servercount = cl.servercount; sfx->hashValue = COM_HashKey( sfx->name, MAX_SFX_HASH ); // link it in @@ -273,7 +273,6 @@ void S_BeginRegistration( void ) { int i; - s_registration_sequence++; snd_ambient = false; // check for automatic ambient sounds @@ -309,7 +308,7 @@ void S_EndRegistration( void ) if( !sfx->name[0] || !Q_stricmp( sfx->name, "*default" )) continue; // don't release default sound - if( sfx->servercount != s_registration_sequence ) + if( sfx->servercount != cl.servercount ) S_FreeSound( sfx ); // don't need this sound } @@ -349,7 +348,7 @@ sound_t S_RegisterSound( const char *name ) sfx = S_FindName( name, NULL ); if( !sfx ) return -1; - sfx->servercount = s_registration_sequence; + sfx->servercount = cl.servercount; if( !s_registering ) S_LoadSound( sfx ); return sfx - s_knownSfx; From 8b6f12418e00a46c48ae511f16c582be93c297d2 Mon Sep 17 00:00:00 2001 From: Alibek Omarov Date: Tue, 29 Nov 2022 13:29:47 +0300 Subject: [PATCH 211/490] engine: common: make few network cvars privileged --- engine/common/net_ws.c | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/engine/common/net_ws.c b/engine/common/net_ws.c index ae6c152c..a0e449f0 100644 --- a/engine/common/net_ws.c +++ b/engine/common/net_ws.c @@ -1898,7 +1898,7 @@ void NET_GetLocalAddress( void ) { net_local.port = ((struct sockaddr_in *)&address)->sin_port; Con_Printf( "Server IPv4 address %s\n", NET_AdrToString( net_local )); - Cvar_FullSet( "net_address", va( "%s", NET_AdrToString( net_local )), FCVAR_READ_ONLY ); + Cvar_FullSet( "net_address", va( "%s", NET_AdrToString( net_local )), net_address->flags ); } else Con_DPrintf( S_ERROR "Could not get TCP/IPv4 address. Reason: %s\n", NET_ErrorString( )); } @@ -1920,7 +1920,7 @@ void NET_GetLocalAddress( void ) { net6_local.port = ((struct sockaddr_in6 *)&address)->sin6_port; Con_Printf( "Server IPv6 address %s\n", NET_AdrToString( net6_local )); - Cvar_FullSet( "net6_address", va( "%s", NET_AdrToString( net6_local )), FCVAR_READ_ONLY ); + Cvar_FullSet( "net6_address", va( "%s", NET_AdrToString( net6_local )), net6_address->flags ); } else Con_DPrintf( S_ERROR "Could not get TCP/IPv6 address. Reason: %s\n", NET_ErrorString( )); } @@ -2069,8 +2069,8 @@ void NET_Init( void ) if( net.initialized ) return; net_clockwindow = Cvar_Get( "clockwindow", "0.5", FCVAR_PRIVILEGED, "timewindow to execute client moves" ); - net_address = Cvar_Get( "net_address", "0", FCVAR_READ_ONLY, "contain local address of current client" ); - net_ipname = Cvar_Get( "ip", "localhost", 0, "network ip address" ); + net_address = Cvar_Get( "net_address", "0", FCVAR_PRIVILEGED|FCVAR_READ_ONLY, "contain local address of current client" ); + net_ipname = Cvar_Get( "ip", "localhost", FCVAR_PRIVILEGED, "network ip address" ); net_iphostport = Cvar_Get( "ip_hostport", "0", FCVAR_READ_ONLY, "network ip host port" ); net_hostport = Cvar_Get( "hostport", va( "%i", PORT_SERVER ), FCVAR_READ_ONLY, "network default host port" ); net_ipclientport = Cvar_Get( "ip_clientport", "0", FCVAR_READ_ONLY, "network ip client port" ); @@ -2079,10 +2079,10 @@ void NET_Init( void ) net_fakeloss = Cvar_Get( "fakeloss", "0", FCVAR_PRIVILEGED, "act like we dropped the packet this % of the time." ); // cvar equivalents for IPv6 - net_ip6name = Cvar_Get( "ip6", "localhost", FCVAR_READ_ONLY, "network ip6 address" ); + net_ip6name = Cvar_Get( "ip6", "localhost", FCVAR_PRIVILEGED, "network ip6 address" ); net_ip6hostport = Cvar_Get( "ip6_hostport", "0", FCVAR_READ_ONLY, "network ip6 host port" ); net_ip6clientport = Cvar_Get( "ip6_clientport", "0", FCVAR_READ_ONLY, "network ip6 client port" ); - net6_address = Cvar_Get( "net6_address", "0", FCVAR_READ_ONLY, "contain local IPv6 address of current client" ); + net6_address = Cvar_Get( "net6_address", "0", FCVAR_PRIVILEGED|FCVAR_READ_ONLY, "contain local IPv6 address of current client" ); // prepare some network data for( i = 0; i < NS_COUNT; i++ ) @@ -2117,11 +2117,11 @@ void NET_Init( void ) // specify custom ip if( Sys_GetParmFromCmdLine( "-ip", cmd )) - Cvar_FullSet( "ip", cmd, FCVAR_READ_ONLY ); + Cvar_FullSet( "ip", cmd, net_ipname->flags ); // specify custom ip6 if( Sys_GetParmFromCmdLine( "-ip6", cmd )) - Cvar_FullSet( "ip6", cmd, FCVAR_READ_ONLY ); + Cvar_FullSet( "ip6", cmd, net_ip6name->flags ); // adjust clockwindow if( Sys_GetParmFromCmdLine( "-clockwindow", cmd )) From e9d66be10aeaa062d77a8ab0c16e8d67c40ee230 Mon Sep 17 00:00:00 2001 From: Alibek Omarov Date: Tue, 29 Nov 2022 13:30:06 +0300 Subject: [PATCH 212/490] mainui: update --- 3rdparty/mainui | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/3rdparty/mainui b/3rdparty/mainui index 9fdcd6c9..3a0e5fa3 160000 --- a/3rdparty/mainui +++ b/3rdparty/mainui @@ -1 +1 @@ -Subproject commit 9fdcd6c9b0e49a6798becf35ef354bc2c25c3057 +Subproject commit 3a0e5fa324f114969c79bd55eb2902075c6cb4a6 From aa6a1db0a969f5816a3e863d9e7742d9aeebf9a3 Mon Sep 17 00:00:00 2001 From: Alibek Omarov Date: Thu, 1 Dec 2022 00:34:33 +0300 Subject: [PATCH 213/490] engine: remove duplicate svc_strings definition --- engine/client/cl_debug.c | 64 ----------------------------------- engine/common/dedicated.c | 68 -------------------------------------- engine/common/net_buffer.c | 64 ++++++++++++++++++++++++++++++++++- 3 files changed, 63 insertions(+), 133 deletions(-) diff --git a/engine/client/cl_debug.c b/engine/client/cl_debug.c index 7b019daa..1467ebd2 100644 --- a/engine/client/cl_debug.c +++ b/engine/client/cl_debug.c @@ -25,70 +25,6 @@ GNU General Public License for more details. #define MSG_COUNT 32 // last 32 messages parsed #define MSG_MASK (MSG_COUNT - 1) -const char *svc_strings[svc_lastmsg+1] = -{ - "svc_bad", - "svc_nop", - "svc_disconnect", - "svc_event", - "svc_changing", - "svc_setview", - "svc_sound", - "svc_time", - "svc_print", - "svc_stufftext", - "svc_setangle", - "svc_serverdata", - "svc_lightstyle", - "svc_updateuserinfo", - "svc_deltatable", - "svc_clientdata", - "svc_resource", - "svc_pings", - "svc_particle", - "svc_restoresound", - "svc_spawnstatic", - "svc_event_reliable", - "svc_spawnbaseline", - "svc_temp_entity", - "svc_setpause", - "svc_signonnum", - "svc_centerprint", - "svc_unused27", - "svc_unused28", - "svc_unused29", - "svc_intermission", - "svc_finale", - "svc_cdtrack", - "svc_restore", - "svc_cutscene", - "svc_weaponanim", - "svc_bspdecal", - "svc_roomtype", - "svc_addangle", - "svc_usermessage", - "svc_packetentities", - "svc_deltapacketentities", - "svc_choke", - "svc_resourcelist", - "svc_deltamovevars", - "svc_resourcerequest", - "svc_customization", - "svc_crosshairangle", - "svc_soundfade", - "svc_filetxferfailed", - "svc_hltv", - "svc_director", - "svc_voiceinit", - "svc_voicedata", - "svc_deltapacketbones", - "svc_unused55", - "svc_resourcelocation", - "svc_querycvarvalue", - "svc_querycvarvalue2", - "svc_exec", -}; - typedef struct { int command; diff --git a/engine/common/dedicated.c b/engine/common/dedicated.c index 07b77a50..6f8ef3a5 100644 --- a/engine/common/dedicated.c +++ b/engine/common/dedicated.c @@ -19,74 +19,6 @@ GNU General Public License for more details. ref_globals_t refState; -const char *svc_strings[256] = -{ - "svc_bad", - "svc_nop", - "svc_disconnect", - "svc_changing", - "svc_version", - "svc_setview", - "svc_sound", - "svc_time", - "svc_print", - "svc_stufftext", - "svc_setangle", - "svc_serverdata", - "svc_lightstyle", - "svc_updateuserinfo", - "svc_deltatable", - "svc_clientdata", - "svc_stopsound", - "svc_updatepings", - "svc_particle", - "svc_restoresound", - "svc_spawnstatic", - "svc_event_reliable", - "svc_spawnbaseline", - "svc_temp_entity", - "svc_setpause", - "svc_signonnum", - "svc_centerprint", - "svc_event", - "svc_soundindex", - "svc_ambientsound", - "svc_intermission", - "svc_modelindex", - "svc_cdtrack", - "svc_serverinfo", - "svc_eventindex", - "svc_weaponanim", - "svc_bspdecal", - "svc_roomtype", - "svc_addangle", - "svc_usermessage", - "svc_packetentities", - "svc_deltapacketentities", - "svc_chokecount", - "svc_resourcelist", - "svc_deltamovevars", - "svc_customization", - "svc_unused46", - "svc_crosshairangle", - "svc_soundfade", - "svc_unused49", - "svc_unused50", - "svc_director", - "svc_studiodecal", - "svc_unused53", - "svc_unused54", - "svc_unused55", - "svc_unused56", - "svc_querycvarvalue", - "svc_querycvarvalue2", - "svc_exec", - "svc_unused60", - "svc_unused61", - "svc_unused62", - "svc_unused63", -}; - void CL_ProcessFile( qboolean successfully_received, const char *filename ) { diff --git a/engine/common/net_buffer.c b/engine/common/net_buffer.c index fbcd7a36..34483ab9 100644 --- a/engine/common/net_buffer.c +++ b/engine/common/net_buffer.c @@ -17,7 +17,6 @@ GNU General Public License for more details. #include "protocol.h" #include "net_buffer.h" #include "xash3d_mathlib.h" - //#define DEBUG_NET_MESSAGES_SEND //#define DEBUG_NET_MESSAGES_READ @@ -26,6 +25,69 @@ GNU General Public License for more details. // gives a 33% speedup in WriteUBitLong. static dword BitWriteMasks[32][33]; static dword ExtraMasks[32]; +const char *svc_strings[svc_lastmsg+1] = +{ + "svc_bad", + "svc_nop", + "svc_disconnect", + "svc_event", + "svc_changing", + "svc_setview", + "svc_sound", + "svc_time", + "svc_print", + "svc_stufftext", + "svc_setangle", + "svc_serverdata", + "svc_lightstyle", + "svc_updateuserinfo", + "svc_deltatable", + "svc_clientdata", + "svc_resource", + "svc_pings", + "svc_particle", + "svc_restoresound", + "svc_spawnstatic", + "svc_event_reliable", + "svc_spawnbaseline", + "svc_temp_entity", + "svc_setpause", + "svc_signonnum", + "svc_centerprint", + "svc_unused27", + "svc_unused28", + "svc_unused29", + "svc_intermission", + "svc_finale", + "svc_cdtrack", + "svc_restore", + "svc_cutscene", + "svc_weaponanim", + "svc_bspdecal", + "svc_roomtype", + "svc_addangle", + "svc_usermessage", + "svc_packetentities", + "svc_deltapacketentities", + "svc_choke", + "svc_resourcelist", + "svc_deltamovevars", + "svc_resourcerequest", + "svc_customization", + "svc_crosshairangle", + "svc_soundfade", + "svc_filetxferfailed", + "svc_hltv", + "svc_director", + "svc_voiceinit", + "svc_voicedata", + "svc_deltapacketbones", + "svc_unused55", + "svc_resourcelocation", + "svc_querycvarvalue", + "svc_querycvarvalue2", + "svc_exec", +}; unsigned short MSG_BigShort( unsigned short swap ) { From c9e4e624747feaa7f2bb48e198890d5a2ab862c7 Mon Sep 17 00:00:00 2001 From: Alibek Omarov Date: Thu, 1 Dec 2022 00:35:05 +0300 Subject: [PATCH 214/490] engine: client: allow IPv6 in NetAPI --- engine/client/cl_game.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/engine/client/cl_game.c b/engine/client/cl_game.c index 7a054b18..efa03828 100644 --- a/engine/client/cl_game.c +++ b/engine/client/cl_game.c @@ -3360,7 +3360,7 @@ void GAME_EXPORT NetAPI_SendRequest( int context, int request, int flags, double return; } - if( remote_address->type >= NA_IPX ) + if( remote_address->type != NA_IPX && remote_address->type != NA_BROADCAST_IPX ) return; // IPX no longer support // find a free request From 9cbf5ab6a60de7da21054b7d0db2402c54b6dd35 Mon Sep 17 00:00:00 2001 From: Alibek Omarov Date: Thu, 1 Dec 2022 01:51:07 +0300 Subject: [PATCH 215/490] engine: client: streamline constructing master server scan request through common function --- engine/client/cl_game.c | 9 +++++--- engine/client/cl_main.c | 44 +++++++++++++++++++++++++++++++--------- engine/client/client.h | 1 + engine/common/protocol.h | 3 +++ 4 files changed, 44 insertions(+), 13 deletions(-) diff --git a/engine/client/cl_game.c b/engine/client/cl_game.c index efa03828..827d7573 100644 --- a/engine/client/cl_game.c +++ b/engine/client/cl_game.c @@ -3401,9 +3401,12 @@ void GAME_EXPORT NetAPI_SendRequest( int context, int request, int flags, double if( request == NETAPI_REQUEST_SERVERLIST ) { - char fullquery[512] = "1\xFF" "0.0.0.0:0\0" "\\gamedir\\"; + char fullquery[512]; + size_t len; - // make sure what port is specified + len = CL_BuildMasterServerScanRequest( fullquery, sizeof( fullquery ), false ); + + // make sure that port is specified if( !nr->resp.remote_address.port ) nr->resp.remote_address.port = MSG_BigShort( PORT_MASTER ); @@ -3431,7 +3434,7 @@ void GAME_EXPORT NetAPI_CancelRequest( int context ) { net_request_t *nr; int i; - +; // find a specified request for( i = 0; i < MAX_REQUESTS; i++ ) { diff --git a/engine/client/cl_main.c b/engine/client/cl_main.c index 3984bb82..6f3da3bd 100644 --- a/engine/client/cl_main.c +++ b/engine/client/cl_main.c @@ -1584,7 +1584,32 @@ void CL_LocalServers_f( void ) Netchan_OutOfBandPrint( NS_CLIENT, adr, "info %i", PROTOCOL_VERSION ); } -#define MS_SCAN_REQUEST "1\xFF" "0.0.0.0:0\0" +/* +================= +CL_BuildMasterServerScanRequest +================= +*/ +size_t CL_BuildMasterServerScanRequest( char *buf, size_t size, qboolean nat ) +{ + size_t remaining; + char *info; + + if( unlikely( size < sizeof( MS_SCAN_REQUEST ))) + return 0; + + Q_strncpy( buf, MS_SCAN_REQUEST, size ); + + info = buf + sizeof( MS_SCAN_REQUEST ) - 1; + remaining = size - sizeof( MS_SCAN_REQUEST ); + + info[0] = 0; + + Info_SetValueForKey( info, "gamedir", GI->gamefolder, remaining ); + Info_SetValueForKey( info, "clver", XASH_VERSION, remaining ); // let master know about client version + Info_SetValueForKey( info, "nat", nat ? "1" : "0", remaining ); + + return sizeof( MS_SCAN_REQUEST ) + Q_strlen( info ); +} /* ================= @@ -1593,18 +1618,17 @@ CL_InternetServers_f */ void CL_InternetServers_f( void ) { - char fullquery[512] = MS_SCAN_REQUEST; - char *info = fullquery + sizeof( MS_SCAN_REQUEST ) - 1; - const size_t remaining = sizeof( fullquery ) - sizeof( MS_SCAN_REQUEST ); + char fullquery[512]; + size_t len; + qboolean nat = cl_nat->value != 0.0f; + + len = CL_BuildMasterServerScanRequest( fullquery, sizeof( fullquery ), nat ); + + Con_Printf( "Scanning for servers on the internet area...\n" ); NET_Config( true, true ); // allow remote - Con_Printf( "Scanning for servers on the internet area...\n" ); - Info_SetValueForKey( info, "gamedir", GI->gamefolder, remaining ); - Info_SetValueForKey( info, "clver", XASH_VERSION, remaining ); // let master know about client version - Info_SetValueForKey( info, "nat", cl_nat->string, remaining ); - - cls.internetservers_wait = NET_SendToMasters( NS_CLIENT, sizeof( MS_SCAN_REQUEST ) + Q_strlen( info ), fullquery ); + cls.internetservers_wait = NET_SendToMasters( NS_CLIENT, len, fullquery ); cls.internetservers_pending = true; if( !cls.internetservers_wait ) diff --git a/engine/client/client.h b/engine/client/client.h index 684150d1..1ed78ab2 100644 --- a/engine/client/client.h +++ b/engine/client/client.h @@ -754,6 +754,7 @@ int CL_IsDevOverviewMode( void ); void CL_PingServers_f( void ); void CL_SignonReply( void ); void CL_ClearState( void ); +size_t CL_BuildMasterServerScanRequest( char *buf, size_t size, qboolean nat ); // // cl_demo.c diff --git a/engine/common/protocol.h b/engine/common/protocol.h index ec0e9230..aba1fd5e 100644 --- a/engine/common/protocol.h +++ b/engine/common/protocol.h @@ -302,4 +302,7 @@ extern const char *clc_strings[clc_lastmsg+1]; #define MAX_LEGACY_TOTAL_CMDS 16 // 28 - 16 = 12 real legacy max backup #define MAX_LEGACY_BACKUP_CMDS 12 +// Master Server protocol +#define MS_SCAN_REQUEST "1\xFF" "0.0.0.0:0\0" // TODO: implement IP filter + #endif//NET_PROTOCOL_H From d9a245dcb55c082e75117362878e25d7ba2b7ef5 Mon Sep 17 00:00:00 2001 From: Alibek Omarov Date: Thu, 1 Dec 2022 02:44:01 +0300 Subject: [PATCH 216/490] engine: reset cheat cvars on remote games --- engine/client/cl_main.c | 3 +++ engine/server/server.h | 1 + engine/server/sv_init.c | 3 +++ 3 files changed, 7 insertions(+) diff --git a/engine/client/cl_main.c b/engine/client/cl_main.c index 6f3da3bd..ee7aad5b 100644 --- a/engine/client/cl_main.c +++ b/engine/client/cl_main.c @@ -1051,6 +1051,9 @@ void CL_SendConnectPacket( void ) input_devices = IN_CollectInputDevices(); IN_LockInputDevices( true ); + Cvar_SetCheatState(); + Cvar_FullSet( "sv_cheats", "0", FCVAR_READ_ONLY | FCVAR_SERVER ); + Info_SetValueForKey( protinfo, "d", va( "%d", input_devices ), sizeof( protinfo ) ); Info_SetValueForKey( protinfo, "v", XASH_VERSION, sizeof( protinfo ) ); Info_SetValueForKey( protinfo, "b", va( "%d", Q_buildnum() ), sizeof( protinfo ) ); diff --git a/engine/server/server.h b/engine/server/server.h index 6bb28da8..47bf848a 100644 --- a/engine/server/server.h +++ b/engine/server/server.h @@ -441,6 +441,7 @@ extern convar_t deathmatch; extern convar_t hostname; extern convar_t skill; extern convar_t coop; +extern convar_t sv_cheats; extern convar_t *sv_pausable; // allows pause in multiplayer extern convar_t *sv_check_errors; diff --git a/engine/server/sv_init.c b/engine/server/sv_init.c index ab32ad44..ac85534e 100644 --- a/engine/server/sv_init.c +++ b/engine/server/sv_init.c @@ -877,6 +877,9 @@ qboolean SV_SpawnServer( const char *mapname, const char *startspot, qboolean ba if( !SV_InitGame( )) return false; + // unlock sv_cheats in local game + ClearBits( sv_cheats.flags, FCVAR_READ_ONLY ); + svs.initialized = true; Log_Open(); Log_Printf( "Loading map \"%s\"\n", mapname ); From 1ba117a8e9a31158b6844a0774e069ab18e5fb80 Mon Sep 17 00:00:00 2001 From: Alibek Omarov Date: Thu, 1 Dec 2022 03:39:06 +0300 Subject: [PATCH 217/490] engine: client: netgraph: fix few global-buffer-overflow errors --- engine/client/cl_netgraph.c | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/engine/client/cl_netgraph.c b/engine/client/cl_netgraph.c index 7c9ee99a..51c5a4e5 100644 --- a/engine/client/cl_netgraph.c +++ b/engine/client/cl_netgraph.c @@ -281,7 +281,10 @@ static void NetGraph_DrawTimes( wrect_t rect, int x, int w ) for( j = start; j < h; j++ ) { - NetGraph_DrawRect( &fill, netcolors[NETGRAPH_NET_COLORS + j + extrap_point] ); + int color = NETGRAPH_NET_COLORS + j + extrap_point; + color = Q_min( color, ARRAYSIZE( netcolors ) - 1 ); + + NetGraph_DrawRect( &fill, netcolors[color] ); fill.top--; } } @@ -297,7 +300,10 @@ static void NetGraph_DrawTimes( wrect_t rect, int x, int w ) for( j = 0; j < h; j++ ) { - NetGraph_DrawRect( &fill, netcolors[NETGRAPH_NET_COLORS + j + oldh] ); + int color = NETGRAPH_NET_COLORS + j + oldh; + color = Q_min( color, ARRAYSIZE( netcolors ) - 1 ); + + NetGraph_DrawRect( &fill, netcolors[color] ); fill.top--; } } From f30f23ba51166ae89ea5b58078ee76ef574ce929 Mon Sep 17 00:00:00 2001 From: Alibek Omarov Date: Thu, 1 Dec 2022 05:49:47 +0300 Subject: [PATCH 218/490] engine: common: validate and load PNG images in customization --- engine/common/custom.c | 20 ++++++++++++++++---- 1 file changed, 16 insertions(+), 4 deletions(-) diff --git a/engine/common/custom.c b/engine/common/custom.c index 1cb8f746..745d85f6 100644 --- a/engine/common/custom.c +++ b/engine/common/custom.c @@ -17,9 +17,21 @@ GNU General Public License for more details. #include "custom.h" #include "ref_common.h" -qboolean CustomDecal_Validate( void *raw, int nFileSize ) +static rgbdata_t *CustomDecal_LoadImage( const char *path, void *raw, int size ) { - rgbdata_t *test = FS_LoadImage( "#logo.bmp", raw, nFileSize ); + const char *testname; + + // this way we limit file types + if( !Q_stricmp( COM_FileExtension( path ), "png" )) + testname = "#logo.png"; + else testname = "#logo.bmp"; + + return FS_LoadImage( testname, raw, size ); +} + +static qboolean CustomDecal_Validate( const char *path, void *raw, int nFileSize ) +{ + rgbdata_t *test = CustomDecal_LoadImage( path, raw, nFileSize ); if( test ) { @@ -97,7 +109,7 @@ qboolean COM_CreateCustomization( customization_t *pListHead, resource_t *pResou { pCust->resource.playernum = playernumber; - if( CustomDecal_Validate( pCust->pBuffer, pResource->nDownloadSize )) + if( CustomDecal_Validate( pResource->szFileName, pCust->pBuffer, pResource->nDownloadSize )) { if( !FBitSet( flags, FCUST_IGNOREINIT )) { @@ -108,7 +120,7 @@ qboolean COM_CreateCustomization( customization_t *pListHead, resource_t *pResou pCust->nUserData2 = 1; if( !FBitSet( flags, FCUST_WIPEDATA )) - pCust->pInfo = FS_LoadImage( "#logo.bmp", pCust->pBuffer, pCust->resource.nDownloadSize ); + pCust->pInfo = CustomDecal_LoadImage( pResource->szFileName, pCust->pBuffer, pCust->resource.nDownloadSize ); else pCust->pInfo = NULL; if( nLumps ) *nLumps = 1; } From cbe3e608b629d044d513462d26b657f96b2bcdaf Mon Sep 17 00:00:00 2001 From: Alibek Omarov Date: Thu, 1 Dec 2022 05:50:28 +0300 Subject: [PATCH 219/490] engine: client: add cl_logoext cvar that's used by mainui to tell the engine which logo must be packed --- engine/client/cl_main.c | 40 ++++++++++++++++++++++++---------------- 1 file changed, 24 insertions(+), 16 deletions(-) diff --git a/engine/client/cl_main.c b/engine/client/cl_main.c index ee7aad5b..63f5b65a 100644 --- a/engine/client/cl_main.c +++ b/engine/client/cl_main.c @@ -38,6 +38,7 @@ CVAR_DEFINE_AUTO( cl_allow_upload, "1", FCVAR_ARCHIVE, "allow to uploading resou CVAR_DEFINE_AUTO( cl_download_ingame, "1", FCVAR_ARCHIVE, "allow to downloading resources while client is active" ); CVAR_DEFINE_AUTO( cl_logofile, "lambda", FCVAR_ARCHIVE, "player logo name" ); CVAR_DEFINE_AUTO( cl_logocolor, "orange", FCVAR_ARCHIVE, "player logo color" ); +CVAR_DEFINE_AUTO( cl_logoext, "bmp", FCVAR_ARCHIVE, "temporary cvar to tell engine which logo must be packed" ); CVAR_DEFINE_AUTO( cl_test_bandwidth, "1", FCVAR_ARCHIVE, "test network bandwith before connection" ); convar_t *rcon_client_password; convar_t *rcon_address; @@ -1206,7 +1207,7 @@ resource_t *CL_AddResource( resourcetype_t type, const char *name, int size, qbo void CL_CreateResourceList( void ) { - char szFileName[MAX_OSPATH]; + char szFileName[MAX_OSPATH]; byte rgucMD5_hash[16]; resource_t *pNewResource; int nSize; @@ -1214,30 +1215,36 @@ void CL_CreateResourceList( void ) HPAK_FlushHostQueue(); cl.num_resources = 0; - - Q_snprintf( szFileName, sizeof( szFileName ), "logos/remapped.bmp" ); memset( rgucMD5_hash, 0, sizeof( rgucMD5_hash )); + // sanitize cvar value + if( Q_strcmp( cl_logoext.string, "bmp" ) && + Q_strcmp( cl_logoext.string, "png" )) + Cvar_DirectSet( &cl_logoext, "bmp" ); + + Q_snprintf( szFileName, sizeof( szFileName ), + "logos/remapped.%s", cl_logoext.string ); fp = FS_Open( szFileName, "rb", true ); - if( fp ) + if( !fp ) + return; + + MD5_HashFile( rgucMD5_hash, szFileName, NULL ); + nSize = FS_FileLength( fp ); + + if( nSize != 0 ) { - MD5_HashFile( rgucMD5_hash, szFileName, NULL ); - nSize = FS_FileLength( fp ); + pNewResource = CL_AddResource( t_decal, szFileName, nSize, false, 0 ); - if( nSize != 0 ) + if( pNewResource ) { - pNewResource = CL_AddResource( t_decal, szFileName, nSize, false, 0 ); - - if( pNewResource ) - { - SetBits( pNewResource->ucFlags, RES_CUSTOM ); - memcpy( pNewResource->rgucMD5_hash, rgucMD5_hash, 16 ); - HPAK_AddLump( false, CUSTOM_RES_PATH, pNewResource, NULL, fp ); - } + SetBits( pNewResource->ucFlags, RES_CUSTOM ); + memcpy( pNewResource->rgucMD5_hash, rgucMD5_hash, 16 ); + HPAK_AddLump( false, CUSTOM_RES_PATH, pNewResource, NULL, fp ); } - FS_Close( fp ); } + + FS_Close( fp ); } /* @@ -2861,6 +2868,7 @@ void CL_InitLocal( void ) Cvar_RegisterVariable( &cl_download_ingame ); Cvar_RegisterVariable( &cl_logofile ); Cvar_RegisterVariable( &cl_logocolor ); + Cvar_RegisterVariable( &cl_logoext ); Cvar_RegisterVariable( &cl_test_bandwidth ); Voice_RegisterCvars(); From e97310c441e9f746aa2fec533b4d2481238bc245 Mon Sep 17 00:00:00 2001 From: Alibek Omarov Date: Fri, 2 Dec 2022 21:21:53 +0300 Subject: [PATCH 220/490] engine: common: net_ws: fix uninitialized family in IPSocket --- engine/common/net_ws.c | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/engine/common/net_ws.c b/engine/common/net_ws.c index a0e449f0..7579251b 100644 --- a/engine/common/net_ws.c +++ b/engine/common/net_ws.c @@ -1674,12 +1674,10 @@ static int NET_IPSocket( const char *net_iface, int port, int family ) int err, net_socket; uint optval = 1; dword _true = 1; - int pfamily; + int pfamily = PF_INET; if( family == AF_INET6 ) pfamily = PF_INET6; - else if( family == AF_INET ) - pfamily = PF_INET; if( NET_IsSocketError(( net_socket = socket( pfamily, SOCK_DGRAM, IPPROTO_UDP )))) { From ebf3877cdaba892d2677ec041653cd4a0169bbf1 Mon Sep 17 00:00:00 2001 From: Alibek Omarov Date: Fri, 2 Dec 2022 21:22:22 +0300 Subject: [PATCH 221/490] engine: common: throw an error message into log in normal mode too! --- engine/common/system.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/engine/common/system.c b/engine/common/system.c index 72ae9197..7da49d88 100644 --- a/engine/common/system.c +++ b/engine/common/system.c @@ -435,6 +435,7 @@ void Sys_Error( const char *error, ... ) Wcon_ShowConsole( false ); #endif MSGBOX( text ); + Sys_Print( text ); } else { @@ -445,7 +446,7 @@ void Sys_Error( const char *error, ... ) Sys_Print( text ); // print error message Sys_WaitForQuit(); } - + Sys_Quit(); } From 674093517e8b7475d5e5300efacfe4a14c04e2e0 Mon Sep 17 00:00:00 2001 From: Alibek Omarov Date: Fri, 2 Dec 2022 21:29:51 +0300 Subject: [PATCH 222/490] 3rdparty: update various submodules --- 3rdparty/extras/xash-extras | 2 +- 3rdparty/gl-wes-v2 | 2 +- 3rdparty/gl4es/gl4es | 2 +- 3rdparty/nanogl | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/3rdparty/extras/xash-extras b/3rdparty/extras/xash-extras index e16b9826..9aba4527 160000 --- a/3rdparty/extras/xash-extras +++ b/3rdparty/extras/xash-extras @@ -1 +1 @@ -Subproject commit e16b9826c2a0960a537139737241f498f9c0e938 +Subproject commit 9aba4527435b1beda97ca8d8a5f1937cd0088c57 diff --git a/3rdparty/gl-wes-v2 b/3rdparty/gl-wes-v2 index 7f567393..0be6803f 160000 --- a/3rdparty/gl-wes-v2 +++ b/3rdparty/gl-wes-v2 @@ -1 +1 @@ -Subproject commit 7f567393d4273d8f33924d98968c9a96991aba3c +Subproject commit 0be6803f816b5cc3a9f7b990f3d19449559eb0bd diff --git a/3rdparty/gl4es/gl4es b/3rdparty/gl4es/gl4es index 5c28420a..277be116 160000 --- a/3rdparty/gl4es/gl4es +++ b/3rdparty/gl4es/gl4es @@ -1 +1 @@ -Subproject commit 5c28420a384c93345a7a5d060a56a0de5f2ac871 +Subproject commit 277be116c1fce0c0344ab41359aeadfa7f023b93 diff --git a/3rdparty/nanogl b/3rdparty/nanogl index 5a22eab5..5f2892a3 160000 --- a/3rdparty/nanogl +++ b/3rdparty/nanogl @@ -1 +1 @@ -Subproject commit 5a22eab53433dcd0d27110166ae33b91d188e342 +Subproject commit 5f2892a37e70e8baaccecfba84be424d2bd29aa7 From d0928af355e1b69344fb105f2cff0d5aa90016aa Mon Sep 17 00:00:00 2001 From: Alibek Omarov Date: Thu, 1 Dec 2022 05:48:34 +0300 Subject: [PATCH 223/490] mainui: update to png-decals branch --- 3rdparty/mainui | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/3rdparty/mainui b/3rdparty/mainui index 3a0e5fa3..27165516 160000 --- a/3rdparty/mainui +++ b/3rdparty/mainui @@ -1 +1 @@ -Subproject commit 3a0e5fa324f114969c79bd55eb2902075c6cb4a6 +Subproject commit 2716551675c2e209e28598896866b056a4dbcd14 From c61442e960247e309ebcccaf789367133ffdec5c Mon Sep 17 00:00:00 2001 From: Alibek Omarov Date: Sun, 4 Dec 2022 05:20:10 +0300 Subject: [PATCH 224/490] engine: imagelib: simplify string operation --- engine/common/imagelib/img_main.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/engine/common/imagelib/img_main.c b/engine/common/imagelib/img_main.c index 1b99e0c0..51c4b0ac 100644 --- a/engine/common/imagelib/img_main.c +++ b/engine/common/imagelib/img_main.c @@ -224,7 +224,7 @@ rgbdata_t *FS_LoadImage( const char *filename, const byte *buffer, size_t size ) Q_strncpy( loadname, filename, sizeof( loadname )); Image_Reset(); // clear old image - if( Q_stricmp( ext, "" )) + if( COM_CheckStringEmpty( ext )) { // we needs to compare file extension with list of supported formats // and be sure what is real extension, not a filename with dot @@ -355,7 +355,7 @@ writes image as any known format qboolean FS_SaveImage( const char *filename, rgbdata_t *pix ) { const char *ext = COM_FileExtension( filename ); - qboolean anyformat = !Q_stricmp( ext, "" ) ? true : false; + qboolean anyformat = !COM_CheckStringEmpty( ext ); string path, savename; const savepixformat_t *format; From 270e2a76a8347cc068042bd5416b735f7f9734bf Mon Sep 17 00:00:00 2001 From: Alibek Omarov Date: Sun, 4 Dec 2022 05:20:19 +0300 Subject: [PATCH 225/490] engine: soundlib: simplify string operation --- engine/common/soundlib/snd_main.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/engine/common/soundlib/snd_main.c b/engine/common/soundlib/snd_main.c index 94b4ff9d..bd32b8d4 100644 --- a/engine/common/soundlib/snd_main.c +++ b/engine/common/soundlib/snd_main.c @@ -66,7 +66,7 @@ wavdata_t *FS_LoadSound( const char *filename, const byte *buffer, size_t size ) Sound_Reset(); // clear old sounddata Q_strncpy( loadname, filename, sizeof( loadname )); - if( Q_stricmp( ext, "" )) + if( COM_CheckStringEmpty( ext )) { // we needs to compare file extension with list of supported formats // and be sure what is real extension, not a filename with dot @@ -155,7 +155,7 @@ stream_t *FS_OpenStream( const char *filename ) Sound_Reset(); // clear old streaminfo Q_strncpy( loadname, filename, sizeof( loadname )); - if( Q_stricmp( ext, "" )) + if( COM_CheckStringEmpty( ext )) { // we needs to compare file extension with list of supported formats // and be sure what is real extension, not a filename with dot From 6477f1656eb5fa21fc85f0453323aa6181ebf07f Mon Sep 17 00:00:00 2001 From: Alibek Omarov Date: Sun, 4 Dec 2022 05:20:32 +0300 Subject: [PATCH 226/490] filesystem: simplify string operation --- filesystem/wad.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/filesystem/wad.c b/filesystem/wad.c index b1b69ccb..bd1d90e4 100644 --- a/filesystem/wad.c +++ b/filesystem/wad.c @@ -120,7 +120,7 @@ static signed char W_TypeFromExt( const char *lumpname ) const wadtype_t *type; // we not known about filetype, so match only by filename - if( !Q_strcmp( ext, "*" ) || !Q_strcmp( ext, "" )) + if( !Q_strcmp( ext, "*" ) || !COM_CheckStringEmpty( ext )) return TYP_ANY; for( type = wad_types; type->ext; type++ ) From 51161004ebb971e832eec8de9fa4fb2091115455 Mon Sep 17 00:00:00 2001 From: Alibek Omarov Date: Sun, 4 Dec 2022 05:24:35 +0300 Subject: [PATCH 227/490] engine: common: simplify string operations --- engine/common/con_utils.c | 3 ++- engine/common/cvar.c | 2 +- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/engine/common/con_utils.c b/engine/common/con_utils.c index 8bafcb31..aad0b289 100644 --- a/engine/common/con_utils.c +++ b/engine/common/con_utils.c @@ -1324,8 +1324,9 @@ static void Cmd_WriteHelp(const char *name, const char *unused, const char *desc { int length; - if( !desc || !Q_strcmp( desc, "" )) + if( !COM_CheckString( desc )) return; // ignore fantom cmds + if( name[0] == '+' || name[0] == '-' ) return; // key bindings diff --git a/engine/common/cvar.c b/engine/common/cvar.c index b89abdfb..569db02a 100644 --- a/engine/common/cvar.c +++ b/engine/common/cvar.c @@ -409,7 +409,7 @@ convar_t *Cvar_Get( const char *name, const char *value, int flags, const char * // which executed from the config file. So we don't need to // change value here: we *already* have actual value from config. // in other cases we need to rewrite them - if( Q_strcmp( var->desc, "" )) + if( COM_CheckStringEmpty( var->desc )) { // directly set value freestring( var->string ); From 4daab23e2d41ddbc9e88b34dd8e10579ff1279c5 Mon Sep 17 00:00:00 2001 From: Alibek Omarov Date: Sun, 4 Dec 2022 05:31:02 +0300 Subject: [PATCH 228/490] mainui: update --- 3rdparty/mainui | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/3rdparty/mainui b/3rdparty/mainui index 27165516..fbfe1125 160000 --- a/3rdparty/mainui +++ b/3rdparty/mainui @@ -1 +1 @@ -Subproject commit 2716551675c2e209e28598896866b056a4dbcd14 +Subproject commit fbfe112599e979ba159140242cf688bc8972c99a From 2454594a18e3e9cecd4ec33fbae4d8ca5300e630 Mon Sep 17 00:00:00 2001 From: Andrey Akhmichin <15944199+nekonomicon@users.noreply.github.com> Date: Sun, 4 Dec 2022 20:18:41 +0500 Subject: [PATCH 229/490] engine: server: simplify strings operations. --- engine/server/sv_client.c | 4 ++-- engine/server/sv_log.c | 4 ++-- engine/server/sv_main.c | 6 +++--- 3 files changed, 7 insertions(+), 7 deletions(-) diff --git a/engine/server/sv_client.c b/engine/server/sv_client.c index ba417dec..a7bc8c2f 100644 --- a/engine/server/sv_client.c +++ b/engine/server/sv_client.c @@ -2118,8 +2118,8 @@ SV_SendBuildInfo_f */ static qboolean SV_SendBuildInfo_f( sv_client_t *cl ) { - SV_ClientPrintf( cl, "Server running %s %s (build %i-%s, %s-%s)\n", - XASH_ENGINE_NAME, XASH_VERSION, Q_buildnum(), Q_buildcommit(), Q_buildos(), Q_buildarch() ); + SV_ClientPrintf( cl, "Server running " XASH_ENGINE_NAME " " XASH_VERSION " (build %i-%s, %s-%s)\n", + Q_buildnum(), Q_buildcommit(), Q_buildos(), Q_buildarch() ); return true; } diff --git a/engine/server/sv_log.c b/engine/server/sv_log.c index db6c4dfe..bc3a5429 100644 --- a/engine/server/sv_log.c +++ b/engine/server/sv_log.c @@ -76,8 +76,8 @@ void Log_Open( void ) } if( fp ) svs.log.file = fp; - Log_Printf( "Log file started (file \"%s\") (game \"%s\") (version \"%i/%s/%d\")\n", - szTestFile, Info_ValueForKey( SV_Serverinfo(), "*gamedir" ), PROTOCOL_VERSION, XASH_VERSION, Q_buildnum() ); + 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() ); } void Log_Close( void ) diff --git a/engine/server/sv_main.c b/engine/server/sv_main.c index 016bcc8c..97db71cd 100644 --- a/engine/server/sv_main.c +++ b/engine/server/sv_main.c @@ -766,7 +766,7 @@ void SV_AddToMaster( netadr_t from, sizebuf_t *msg ) Info_SetValueForKey( s, "os", "w", len ); // Windows Info_SetValueForKey( s, "secure", "0", len ); // server anti-cheat Info_SetValueForKey( s, "lan", "0", len ); // LAN servers doesn't send info to master - Info_SetValueForKey( s, "version", va( "%s", XASH_VERSION ), len ); // server region. 255 -- all regions + Info_SetValueForKey( s, "version", XASH_VERSION, len ); // server region. 255 -- all regions Info_SetValueForKey( s, "region", "255", len ); // server region. 255 -- all regions Info_SetValueForKey( s, "product", GI->gamefolder, len ); // product? Where is the difference with gamedir? Info_SetValueForKey( s, "nat", sv_nat.string, sizeof(s) ); // Server running under NAT, use reverse connection @@ -964,8 +964,8 @@ void SV_Init( void ) MSG_Init( &net_message, "NetMessage", net_message_buffer, sizeof( net_message_buffer )); - Q_snprintf( versionString, sizeof( versionString ), "%s: %s-%s(%s-%s),%i,%i", - XASH_ENGINE_NAME, XASH_VERSION, Q_buildcommit(), Q_buildos(), Q_buildarch(), PROTOCOL_VERSION, Q_buildnum() ); + Q_snprintf( versionString, sizeof( versionString ), XASH_ENGINE_NAME ": " XASH_VERSION "-%s(%s-%s),%i,%i", + Q_buildcommit(), Q_buildos(), Q_buildarch(), PROTOCOL_VERSION, Q_buildnum() ); Cvar_FullSet( "sv_version", versionString, FCVAR_READ_ONLY ); From 3287501f97d438e1166c8b0d3bdc54f130b4961f Mon Sep 17 00:00:00 2001 From: Andrey Akhmichin <15944199+nekonomicon@users.noreply.github.com> Date: Sun, 4 Dec 2022 20:49:18 +0500 Subject: [PATCH 230/490] engine: client: simplify strings operations. --- engine/client/console.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/engine/client/console.c b/engine/client/console.c index 21f76b67..cf6e1e28 100644 --- a/engine/client/console.c +++ b/engine/client/console.c @@ -2195,7 +2195,7 @@ void Con_DrawSolidConsole( int lines ) // draw current version memcpy( color, g_color_table[7], sizeof( color )); - Q_snprintf( curbuild, MAX_STRING, "%s %i/%s (%s-%s build %i)", XASH_ENGINE_NAME, PROTOCOL_VERSION, XASH_VERSION, Q_buildos(), Q_buildarch(), Q_buildnum( )); + Q_snprintf( curbuild, MAX_STRING, XASH_ENGINE_NAME " %i/" XASH_VERSION " (%s-%s build %i)", PROTOCOL_VERSION, Q_buildos(), Q_buildarch(), Q_buildnum( )); Con_DrawStringLen( curbuild, &stringLen, &charH ); start = refState.width - stringLen; @@ -2348,8 +2348,8 @@ void Con_DrawVersion( void ) host.force_draw_version = false; if( host.force_draw_version || draw_version ) - Q_snprintf( curbuild, MAX_STRING, "%s v%i/%s (%s-%s build %i)", XASH_ENGINE_NAME, PROTOCOL_VERSION, XASH_VERSION, Q_buildos(), Q_buildarch(), Q_buildnum( )); - else Q_snprintf( curbuild, MAX_STRING, "v%i/%s (%s-%s build %i)", PROTOCOL_VERSION, XASH_VERSION, Q_buildos(), Q_buildarch(), Q_buildnum( )); + Q_snprintf( curbuild, MAX_STRING, XASH_ENGINE_NAME " v%i/" XASH_VERSION " (%s-%s build %i)", PROTOCOL_VERSION, Q_buildos(), Q_buildarch(), Q_buildnum( )); + else Q_snprintf( curbuild, MAX_STRING, "v%i/" XASH_VERSION " (%s-%s build %i)", PROTOCOL_VERSION, Q_buildos(), Q_buildarch(), Q_buildnum( )); Con_DrawStringLen( curbuild, &stringLen, &charH ); start = refState.width - stringLen * 1.05f; From 0c4f35e2d09dda3f5f04bf3c6b6bbdf4224b1389 Mon Sep 17 00:00:00 2001 From: Andrey Akhmichin <15944199+nekonomicon@users.noreply.github.com> Date: Sun, 4 Dec 2022 21:04:42 +0500 Subject: [PATCH 231/490] filesystem: simplify strings operations. --- filesystem/filesystem.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/filesystem/filesystem.c b/filesystem/filesystem.c index 70270bc2..7b90de81 100644 --- a/filesystem/filesystem.c +++ b/filesystem/filesystem.c @@ -564,7 +564,7 @@ static void FS_WriteGameInfo( const char *filepath, gameinfo_t *GameInfo ) if( !f ) Sys_Error( "FS_WriteGameInfo: can't write %s\n", filepath ); // may be disk-space is out? - FS_Printf( f, "// generated by %s %s-%s (%s-%s)\n\n\n", XASH_ENGINE_NAME, XASH_VERSION, Q_buildcommit(), Q_buildos(), Q_buildarch() ); + FS_Printf( f, "// generated by " XASH_ENGINE_NAME " " XASH_VERSION "-%s (%s-%s)\n\n\n", Q_buildcommit(), Q_buildos(), Q_buildarch() ); if( COM_CheckStringEmpty( GameInfo->basedir ) ) FS_Printf( f, "basedir\t\t\"%s\"\n", GameInfo->basedir ); From 7bb994f7bc67ba7f4fe46c81e7db20da80450abd Mon Sep 17 00:00:00 2001 From: Andrey Akhmichin <15944199+nekonomicon@users.noreply.github.com> Date: Sun, 4 Dec 2022 02:33:36 +0500 Subject: [PATCH 232/490] engine: common: imagelib: img_png.c: fix wrong palette decoding. --- engine/common/imagelib/img_png.c | 25 ++++++++++++++++++------- 1 file changed, 18 insertions(+), 7 deletions(-) diff --git a/engine/common/imagelib/img_png.c b/engine/common/imagelib/img_png.c index de6ed399..1b85a300 100644 --- a/engine/common/imagelib/img_png.c +++ b/engine/common/imagelib/img_png.c @@ -43,7 +43,7 @@ qboolean Image_LoadPNG( const char *name, const byte *buffer, fs_offset_t filesi short p, a, b, c, pa, pb, pc; byte *buf_p, *pixbuf, *raw, *prior, *idat_buf = NULL, *uncompressed_buffer = NULL; byte *pallete = NULL, *trns = NULL; - uint chunk_len, trns_len, crc32, crc32_check, oldsize = 0, newsize = 0, rowsize; + uint chunk_len, trns_len, plte_len, crc32, crc32_check, oldsize = 0, newsize = 0, rowsize; uint uncompressed_size, pixel_size, pixel_count, i, y, filter_type, chunk_sign, r_alpha, g_alpha, b_alpha; qboolean has_iend_chunk = false; z_stream stream = {0}; @@ -163,7 +163,7 @@ qboolean Image_LoadPNG( const char *name, const byte *buffer, fs_offset_t filesi } // move pointer - buf_p += sizeof( chunk_sign ); + buf_p += sizeof( chunk_len ); // find transparency if( !memcmp( buf_p, trns_sign, sizeof( trns_sign ) ) ) @@ -175,6 +175,7 @@ qboolean Image_LoadPNG( const char *name, const byte *buffer, fs_offset_t filesi else if( !memcmp( buf_p, plte_sign, sizeof( plte_sign ) ) ) { pallete = buf_p + sizeof( plte_sign ); + plte_len = chunk_len / 3; } // get all IDAT chunks data else if( !memcmp( buf_p, idat_sign, sizeof( idat_sign ) ) ) @@ -467,14 +468,24 @@ qboolean Image_LoadPNG( const char *name, const byte *buffer, fs_offset_t filesi case PNG_CT_PALLETE: for( y = 0; y < pixel_count; y++, raw += pixel_size ) { - *pixbuf++ = pallete[raw[0] + 2]; - *pixbuf++ = pallete[raw[0] + 1]; - *pixbuf++ = pallete[raw[0] + 0]; + if( raw[0] < plte_len ) + { + *pixbuf++ = pallete[3 * raw[0] + 0]; + *pixbuf++ = pallete[3 * raw[0] + 1]; + *pixbuf++ = pallete[3 * raw[0] + 2]; - if( trns && raw[0] < trns_len ) - *pixbuf++ = trns[raw[0]]; + if( trns && raw[0] < trns_len ) + *pixbuf++ = trns[raw[0]]; + else + *pixbuf++ = 0xFF; + } else + { + *pixbuf++ = 0; + *pixbuf++ = 0; + *pixbuf++ = 0; *pixbuf++ = 0xFF; + } } break; default: From 177ed2c603097c3b74cdf9345866ecd177f3ebf6 Mon Sep 17 00:00:00 2001 From: Andrey Akhmichin <15944199+nekonomicon@users.noreply.github.com> Date: Sun, 4 Dec 2022 20:08:28 +0500 Subject: [PATCH 233/490] engine: common: simplify strings operations. --- engine/common/con_utils.c | 2 +- engine/common/crashhandler.c | 4 ++-- engine/common/host.c | 2 +- engine/common/net_ws.c | 30 ++++++++++++++++++++---------- 4 files changed, 24 insertions(+), 14 deletions(-) diff --git a/engine/common/con_utils.c b/engine/common/con_utils.c index aad0b289..34f13460 100644 --- a/engine/common/con_utils.c +++ b/engine/common/con_utils.c @@ -368,7 +368,7 @@ qboolean Cmd_GetSavesList( const char *s, char *completedname, int length ) string matchbuf; int i, numsaves; - t = FS_Search( va( "%s%s*.sav", DEFAULT_SAVE_DIRECTORY, s ), true, true ); // lookup only in gamedir + t = FS_Search( va( DEFAULT_SAVE_DIRECTORY "%s*.sav", s ), true, true ); // lookup only in gamedir if( !t ) return false; COM_FileBase( t->filenames[0], matchbuf ); diff --git a/engine/common/crashhandler.c b/engine/common/crashhandler.c index fac7474a..3c638b47 100644 --- a/engine/common/crashhandler.c +++ b/engine/common/crashhandler.c @@ -358,8 +358,8 @@ static void Sys_Crash( int signal, siginfo_t *si, void *context) #endif // safe actions first, stack and memory may be corrupted - len = Q_snprintf( message, sizeof( message ), "Ver: %s %s (build %i-%s, %s-%s)\n", - XASH_ENGINE_NAME, XASH_VERSION, Q_buildnum(), Q_buildcommit(), Q_buildos(), Q_buildarch() ); + len = Q_snprintf( message, sizeof( message ), "Ver: " XASH_ENGINE_NAME " " XASH_VERSION " (build %i-%s, %s-%s)\n", + Q_buildnum(), Q_buildcommit(), Q_buildos(), Q_buildarch() ); #if !XASH_BSD len += Q_snprintf( message + len, sizeof( message ) - len, "Crash: signal %d errno %d with code %d at %p %p\n", signal, si->si_errno, si->si_code, si->si_addr, si->si_ptr ); diff --git a/engine/common/host.c b/engine/common/host.c index ddba3bd0..7a8ff51a 100644 --- a/engine/common/host.c +++ b/engine/common/host.c @@ -1137,7 +1137,7 @@ int EXPORT Host_Main( int argc, char **argv, const char *progname, int bChangeGa build = Cvar_Get( "buildnum", va( "%i", Q_buildnum_compat()), FCVAR_READ_ONLY, "returns a current build number" ); ver = Cvar_Get( "ver", va( "%i/%s (hw build %i)", PROTOCOL_VERSION, XASH_COMPAT_VERSION, Q_buildnum_compat()), FCVAR_READ_ONLY, "shows an engine version" ); - Cvar_Get( "host_ver", va( "%i %s %s %s %s", Q_buildnum(), XASH_VERSION, Q_buildos(), Q_buildarch(), Q_buildcommit() ), FCVAR_READ_ONLY, "detailed info about this build" ); + Cvar_Get( "host_ver", va( "%i " XASH_VERSION " %s %s %s", Q_buildnum(), Q_buildos(), Q_buildarch(), Q_buildcommit() ), FCVAR_READ_ONLY, "detailed info about this build" ); Cvar_Get( "host_lowmemorymode", va( "%i", XASH_LOW_MEMORY ), FCVAR_READ_ONLY, "indicates if engine compiled for low RAM consumption (0 - normal, 1 - low engine limits, 2 - low protocol limits)" ); Mod_Init(); diff --git a/engine/common/net_ws.c b/engine/common/net_ws.c index 7579251b..3e5cc4a7 100644 --- a/engine/common/net_ws.c +++ b/engine/common/net_ws.c @@ -700,20 +700,23 @@ NET_AdrToString */ const char *NET_AdrToString( const netadr_t a ) { + static char s[64]; + if( a.type == NA_LOOPBACK ) return "loopback"; if( a.type6 == NA_IP6 ) { - char s[64]; uint8_t ip6[16]; NET_NetadrToIP6Bytes( ip6, &a ); IPv6AddrToString( s, ip6, ntohs( a.port ), 0 ); - return va( "%s", s ); + return s; } - return va( "%i.%i.%i.%i:%i", a.ip[0], a.ip[1], a.ip[2], a.ip[3], ntohs( a.port )); + Q_sprintf( s, "%i.%i.%i.%i:%i", a.ip[0], a.ip[1], a.ip[2], a.ip[3], ntohs( a.port )); + + return s; } /* @@ -723,19 +726,23 @@ NET_BaseAdrToString */ const char *NET_BaseAdrToString( const netadr_t a ) { + static char s[64]; + if( a.type == NA_LOOPBACK ) return "loopback"; if( a.type6 == NA_IP6 ) { - char s[64]; uint8_t ip6[16]; NET_NetadrToIP6Bytes( ip6, &a ); IPv6IPToString( s, ip6 ); - return va( "%s", s ); + return s; } - return va( "%i.%i.%i.%i", a.ip[0], a.ip[1], a.ip[2], a.ip[3] ); + + Q_sprintf( s, "%i.%i.%i.%i", a.ip[0], a.ip[1], a.ip[2], a.ip[3] ); + + return s; } /* @@ -1868,6 +1875,7 @@ void NET_GetLocalAddress( void ) char buff[512]; struct sockaddr_storage address; WSAsize_t namelen; + const char *net_addr_string; memset( &net_local, 0, sizeof( netadr_t )); memset( &net6_local, 0, sizeof( netadr_t )); @@ -1895,8 +1903,9 @@ void NET_GetLocalAddress( void ) if( !NET_IsSocketError( getsockname( net.ip_sockets[NS_SERVER], (struct sockaddr *)&address, &namelen ))) { net_local.port = ((struct sockaddr_in *)&address)->sin_port; - Con_Printf( "Server IPv4 address %s\n", NET_AdrToString( net_local )); - Cvar_FullSet( "net_address", va( "%s", NET_AdrToString( net_local )), net_address->flags ); + net_addr_string = NET_AdrToString( net_local ); + Con_Printf( "Server IPv4 address %s\n", net_addr_string ); + Cvar_FullSet( "net_address", net_addr_string, net_address->flags ); } else Con_DPrintf( S_ERROR "Could not get TCP/IPv4 address. Reason: %s\n", NET_ErrorString( )); } @@ -1917,8 +1926,9 @@ void NET_GetLocalAddress( void ) if( !NET_IsSocketError( getsockname( net.ip6_sockets[NS_SERVER], (struct sockaddr *)&address, &namelen ))) { net6_local.port = ((struct sockaddr_in6 *)&address)->sin6_port; - Con_Printf( "Server IPv6 address %s\n", NET_AdrToString( net6_local )); - Cvar_FullSet( "net6_address", va( "%s", NET_AdrToString( net6_local )), net6_address->flags ); + net_addr_string = NET_AdrToString( net6_local ); + Con_Printf( "Server IPv6 address %s\n", net_addr_string ); + Cvar_FullSet( "net6_address", net_addr_string, net6_address->flags ); } else Con_DPrintf( S_ERROR "Could not get TCP/IPv6 address. Reason: %s\n", NET_ErrorString( )); } From 58465c3727718c3178413a585e0ee5377ad4a9d5 Mon Sep 17 00:00:00 2001 From: SNMetamorph <25657591+SNMetamorph@users.noreply.github.com> Date: Sun, 4 Dec 2022 11:13:00 +0400 Subject: [PATCH 234/490] engine: common: fixed dedicated server bug The problem is server wasn't executing server.cfg if map specified in startup parameters --- engine/common/host.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/engine/common/host.c b/engine/common/host.c index 7a8ff51a..f7e91114 100644 --- a/engine/common/host.c +++ b/engine/common/host.c @@ -1206,11 +1206,13 @@ int EXPORT Host_Main( int argc, char **argv, const char *progname, int bChangeGa oldtime = Sys_DoubleTime() - 0.1; - if( Host_IsDedicated() && GameState->nextstate == STATE_RUNFRAME ) + if( Host_IsDedicated( )) { + if( GameState->nextstate == STATE_RUNFRAME ) + Con_Printf( "Type 'map ' to start game... (TAB-autocomplete is working too)\n" ); + // execute server.cfg after commandline // so we have a chance to set servercfgfile - Con_Printf( "Type 'map ' to start game... (TAB-autocomplete is working too)\n" ); Cbuf_AddText( va( "exec %s\n", Cvar_VariableString( "servercfgfile" ))); Cbuf_Execute(); } From c34ce2d9e155bd19e996cc42004c51bea867f1d4 Mon Sep 17 00:00:00 2001 From: SNMetamorph <25657591+SNMetamorph@users.noreply.github.com> Date: Sun, 4 Dec 2022 13:15:39 +0400 Subject: [PATCH 235/490] engine: client: fixed max fragment size wrong calculation --- engine/client/cl_main.c | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/engine/client/cl_main.c b/engine/client/cl_main.c index 63f5b65a..485af8c9 100644 --- a/engine/client/cl_main.c +++ b/engine/client/cl_main.c @@ -1109,8 +1109,9 @@ Resend a connect message if the last one has timed out */ void CL_CheckForResend( void ) { - netadr_t adr; + netadr_t adr; int res; + qboolean bandwidthTest; if( cls.internetservers_wait ) CL_InternetServers_f(); @@ -1175,14 +1176,18 @@ void CL_CheckForResend( void ) return; } + bandwidthTest = !cls.legacymode && cl_test_bandwidth.value; cls.serveradr = adr; - cls.max_fragment_size = Q_max( FRAGMENT_MAX_SIZE, cls.max_fragment_size >> Q_min( 1, cls.connect_retry )); + cls.max_fragment_size = Q_min( FRAGMENT_MAX_SIZE, cls.max_fragment_size / (cls.connect_retry + 1)); 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 ); + else Con_Printf( "Connecting to %s... [retry #%i]\n", cls.servername, cls.connect_retry ); - if( !cls.legacymode && cl_test_bandwidth.value ) + if( bandwidthTest ) Netchan_OutOfBandPrint( NS_CLIENT, adr, "bandwidth %i %i\n", PROTOCOL_VERSION, cls.max_fragment_size ); else Netchan_OutOfBandPrint( NS_CLIENT, adr, "getchallenge\n" ); From a19270a0dc9989b0e1edd37ee896fcbc93a39ebe Mon Sep 17 00:00:00 2001 From: SNMetamorph <25657591+SNMetamorph@users.noreply.github.com> Date: Sun, 4 Dec 2022 13:16:43 +0400 Subject: [PATCH 236/490] engine: client: max fragment size test retries increased to 3 --- engine/client/cl_main.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/engine/client/cl_main.c b/engine/client/cl_main.c index 485af8c9..173e0dbf 100644 --- a/engine/client/cl_main.c +++ b/engine/client/cl_main.c @@ -27,7 +27,7 @@ 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 2 +#define CL_TEST_RETRIES_NORESPONCE 3 #define CL_TEST_RETRIES 5 CVAR_DEFINE_AUTO( mp_decals, "300", FCVAR_ARCHIVE, "decals limit in multiplayer" ); From 9381a405063cdd5c9623c67bd24a7da207257c66 Mon Sep 17 00:00:00 2001 From: Alibek Omarov Date: Sun, 4 Dec 2022 06:15:44 +0300 Subject: [PATCH 237/490] filesystem: check only DLLs for encryption --- filesystem/filesystem.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/filesystem/filesystem.c b/filesystem/filesystem.c index 7b90de81..557236a9 100644 --- a/filesystem/filesystem.c +++ b/filesystem/filesystem.c @@ -1266,6 +1266,10 @@ static qboolean FS_CheckForCrypt( const char *dllname ) file_t *f; int key; + // this encryption is specific to DLLs + if( Q_stricmp( COM_FileExtension( dllname ), "dll" )) + return false; + f = FS_Open( dllname, "rb", false ); if( !f ) return false; From eb36fe01551599a89c3ef821205f5d1feb5ce6ea Mon Sep 17 00:00:00 2001 From: Alibek Omarov Date: Sun, 4 Dec 2022 06:18:34 +0300 Subject: [PATCH 238/490] filesystem: fill searchpath DIR functions only if file was found in direct path --- filesystem/filesystem.c | 19 +++++++++---------- 1 file changed, 9 insertions(+), 10 deletions(-) diff --git a/filesystem/filesystem.c b/filesystem/filesystem.c index 557236a9..cea197a5 100644 --- a/filesystem/filesystem.c +++ b/filesystem/filesystem.c @@ -1802,7 +1802,6 @@ and the file index in the package if relevant searchpath_t *FS_FindFile( const char *name, int *index, qboolean gamedironly ) { searchpath_t *search; - char *pEnvPath; // search through the path, one element at a time for( search = fs_searchpaths; search; search = search->next ) @@ -1828,20 +1827,20 @@ searchpath_t *FS_FindFile( const char *name, int *index, qboolean gamedironly ) search = &fs_directpath; memset( search, 0, sizeof( searchpath_t )); - search->printinfo = FS_PrintInfo_DIR; - search->close = FS_Close_DIR; - search->openfile = FS_OpenFile_DIR; - search->filetime = FS_FileTime_DIR; - search->findfile = FS_FindFile_DIR; - search->search = FS_Search_DIR; - // root folder has a more priority than netpath Q_strncpy( search->filename, fs_rootdir, sizeof( search->filename )); Q_strcat( search->filename, PATH_SPLITTER ); - Q_snprintf( netpath, MAX_SYSPATH, "%s%s", search->filename, name ); + Q_snprintf( netpath, sizeof( netpath ), "%s%s", search->filename, name ); - if( FS_SysFileExists( netpath, !( search->flags & FS_CUSTOM_PATH ) )) + if( FS_SysFileExists( netpath, true )) { + search->printinfo = FS_PrintInfo_DIR; + search->close = FS_Close_DIR; + search->openfile = FS_OpenFile_DIR; + search->filetime = FS_FileTime_DIR; + search->findfile = FS_FindFile_DIR; + search->search = FS_Search_DIR; + if( index != NULL ) *index = -1; return search; From 7d0d6b8e0d2eb0a186fcde59694d78ec0c1e6114 Mon Sep 17 00:00:00 2001 From: Alibek Omarov Date: Mon, 5 Dec 2022 03:10:30 +0300 Subject: [PATCH 239/490] engine: common: host: implement adaptive sleeptime, log time to first time for debug purposes --- engine/common/common.h | 5 ++ engine/common/host.c | 101 +++++++++++++++++++++++++---------------- 2 files changed, 66 insertions(+), 40 deletions(-) diff --git a/engine/common/common.h b/engine/common/common.h index 36cdb234..447042fe 100644 --- a/engine/common/common.h +++ b/engine/common/common.h @@ -393,6 +393,11 @@ typedef struct host_parm_s // bug compatibility level, for very "special" games bugcomp_t bugcomp; + // measure time to first frame + double starttime; + + // count of sleeps can be inserted between frames + double pureframetime; } host_parm_t; extern host_parm_t host; diff --git a/engine/common/host.c b/engine/common/host.c index f7e91114..b129b92a 100644 --- a/engine/common/host.c +++ b/engine/common/host.c @@ -269,43 +269,35 @@ void Host_AbortCurrentFrame( void ) /* ================== -Host_CheckSleep +Host_CalcSleep ================== */ -void Host_CheckSleep( void ) +static int Host_CalcSleep( void ) { - int sleeptime = host_sleeptime->value; - #ifndef XASH_DEDICATED // never sleep in timedemo for benchmarking purposes // also don't sleep with vsync for less lag if( CL_IsTimeDemo( ) || CVAR_TO_BOOL( gl_vsync )) - return; + return 0; #endif if( Host_IsDedicated() ) { // let the dedicated server some sleep - Sys_Sleep( sleeptime ); + return host_sleeptime->value; } - else + else if( host.status == HOST_NOFOCUS ) { - if( host.status == HOST_NOFOCUS ) - { - if( SV_Active() && CL_IsInGame( )) - Sys_Sleep( sleeptime ); // listenserver - else Sys_Sleep( 20 ); // sleep 20 ms otherwise - } - else if( host.status == HOST_SLEEP ) - { - // completely sleep in minimized state - Sys_Sleep( 20 ); - } - else - { - Sys_Sleep( sleeptime ); - } + if( SV_Active() && CL_IsInGame( )) + return host_sleeptime->value; // listenserver + return 20; // sleep 20 ms otherwise } + else if( host.status == HOST_SLEEP ) + { + return 20; + } + + return host_sleeptime->value; } void Host_NewInstance( const char *name, const char *finalmsg ) @@ -631,27 +623,53 @@ Returns false if the time is too short to run a frame qboolean Host_FilterTime( float time ) { static double oldtime; - double fps; - double scale = sys_timescale.value; + double fps, scale = sys_timescale.value; host.realtime += time * scale; - fps = Host_CalcFPS( ); + fps = Host_CalcFPS(); // clamp the fps in multiplayer games if( fps != 0.0 ) { + static int sleeps; + double targetframetime; + int sleeptime = Host_CalcSleep(); + // limit fps to withing tolerable range fps = bound( MIN_FPS, fps, MAX_FPS ); - if( Host_IsDedicated() ) + if( Host_IsDedicated( )) + targetframetime = ( 1.0 / ( fps + 1.0 )); + else targetframetime = ( 1.0 / fps ); + + if(( host.realtime - oldtime ) < targetframetime * scale ) { - if(( host.realtime - oldtime ) < ( 1.0 / ( fps + 1.0 )) * scale) - return false; + if( sleeptime > 0 && sleeps > 0 ) + { + Sys_Sleep( sleeptime ); + sleeps--; + } + + return false; } - else + + if( sleeptime > 0 && sleeps <= 0 ) { - if(( host.realtime - oldtime ) < ( 1.0 / fps ) * scale ) - return false; + if( host.status == HOST_FRAME ) + { + // give few sleeps this frame with small margin + double targetsleeptime = targetframetime - host.pureframetime * 2; + + // don't sleep if we can't keep up with the framerate + if( targetsleeptime > 0 ) + sleeps = targetsleeptime / ( sleeptime * 0.001 ); + else sleeps = 0; + } + else + { + // always sleep at least once in minimized/nofocus state + sleeps = 1; + } } } @@ -674,19 +692,16 @@ Host_Frame */ void Host_Frame( float time ) { - static qboolean slept = false; + double t1, t2; // decide the simulation time if( !Host_FilterTime( time )) - { - if( !slept ) - { - Host_CheckSleep(); - slept = true; - } return; - } - slept = false; + + t1 = Sys_DoubleTime(); + + if( host.framecount == 0 ) + Con_DPrintf( "Time to first frame: %.3f seconds\n", t1 - host.starttime ); Host_InputFrame (); // input frame Host_ClientBegin (); // begin client @@ -695,6 +710,10 @@ void Host_Frame( float time ) Host_ClientFrame (); // client frame HTTP_Run(); // both server and client + t2 = Sys_DoubleTime(); + + host.pureframetime = t2 - t1; + host.framecount++; } @@ -1113,6 +1132,8 @@ int EXPORT Host_Main( int argc, char **argv, const char *progname, int bChangeGa { static double oldtime, newtime; + host.starttime = Sys_DoubleTime(); + pChangeGame = func; // may be NULL Host_InitCommon( argc, argv, progname, bChangeGame ); From eabbd0061f6334f65d381a985140f935edd8ab0b Mon Sep 17 00:00:00 2001 From: Alibek Omarov Date: Mon, 5 Dec 2022 03:17:56 +0300 Subject: [PATCH 240/490] mainui: update --- 3rdparty/mainui | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/3rdparty/mainui b/3rdparty/mainui index fbfe1125..73d8bb6d 160000 --- a/3rdparty/mainui +++ b/3rdparty/mainui @@ -1 +1 @@ -Subproject commit fbfe112599e979ba159140242cf688bc8972c99a +Subproject commit 73d8bb6da0639af92c8d627327e003ea11a149c7 From ccf7619ae51fb9072b861df50a802901eead9492 Mon Sep 17 00:00:00 2001 From: Alibek Omarov Date: Mon, 5 Dec 2022 03:18:27 +0300 Subject: [PATCH 241/490] engine: imagelib: refactor image loading function --- engine/common/imagelib/imagelib.h | 3 +- engine/common/imagelib/img_main.c | 184 +++++++++++++++++------------- 2 files changed, 106 insertions(+), 81 deletions(-) diff --git a/engine/common/imagelib/imagelib.h b/engine/common/imagelib/imagelib.h index 376ac04d..d0da19c1 100644 --- a/engine/common/imagelib/imagelib.h +++ b/engine/common/imagelib/imagelib.h @@ -91,7 +91,7 @@ typedef struct imglib_s // global parms rgba_t fogParams; // some water textures has info about underwater fog - image_hint_t hint; // hint for some loaders + int hint; // hint for some loaders byte *tempbuffer; // for convert operations int cmd_flags; // global imglib flags int force_flags; // override cmd_flags @@ -171,7 +171,6 @@ rgbdata_t *Image_Quantize( rgbdata_t *pic ); // img_utils.c // void Image_Reset( void ); -rgbdata_t *ImagePack( void ); byte *Image_Copy( size_t size ); void Image_CopyParms( rgbdata_t *src ); qboolean Image_ValidSize( const char *name ); diff --git a/engine/common/imagelib/img_main.c b/engine/common/imagelib/img_main.c index 51c4b0ac..230d5520 100644 --- a/engine/common/imagelib/img_main.c +++ b/engine/common/imagelib/img_main.c @@ -110,20 +110,21 @@ void Image_Reset( void ) image.size = 0; } -rgbdata_t *ImagePack( void ) +static rgbdata_t *ImagePack( void ) { - rgbdata_t *pack = Mem_Calloc( host.imagepool, sizeof( rgbdata_t )); + rgbdata_t *pack; // clear any force flags image.force_flags = 0; if( image.cubemap && image.num_sides != 6 ) { - // this never be happens, just in case - FS_FreeImage( pack ); + // this never can happen, just in case return NULL; } + pack = Mem_Calloc( host.imagepool, sizeof( *pack )); + if( image.cubemap ) { image.flags |= IMAGE_CUBEMAP; @@ -163,7 +164,7 @@ FS_AddSideToPack ================ */ -qboolean FS_AddSideToPack( const char *name, int adjust_flags ) +static qboolean FS_AddSideToPack( int adjust_flags ) { byte *out, *flipped; qboolean resampled = false; @@ -203,6 +204,88 @@ qboolean FS_AddSideToPack( const char *name, int adjust_flags ) return true; } +static const loadpixformat_t *Image_GetLoadFormatForExtension( const char *ext ) +{ + const loadpixformat_t *format; + + if( !COM_CheckStringEmpty( ext )) + return NULL; + + for( format = image.loadformats; format->formatstring; format++ ) + { + if( !Q_stricmp( ext, format->ext )) + return format; + } + + return NULL; +} + +static qboolean Image_ProbeLoadBuffer_( const loadpixformat_t *fmt, const char *name, const byte *buf, size_t size, int override_hint ) +{ + if( override_hint > 0 ) + image.hint = override_hint; + else image.hint = fmt->hint; + + return fmt->loadfunc( name, buf, size ); +} + +static qboolean Image_ProbeLoadBuffer( const loadpixformat_t *fmt, const char *name, const byte *buf, size_t size, int override_hint ) +{ + if( size <= 0 ) + return false; + + // bruteforce all loaders + if( !fmt ) + { + for( fmt = image.loadformats; fmt->formatstring; fmt++ ) + { + if( Image_ProbeLoadBuffer_( fmt, name, buf, size, override_hint )) + return true; + } + + return false; + } + + return Image_ProbeLoadBuffer_( fmt, name, buf, size, override_hint ); +} + +static qboolean Image_ProbeLoad_( const loadpixformat_t *fmt, const char *name, const char *suffix, int override_hint ) +{ + qboolean success = false; + fs_offset_t filesize; + string path; + byte *f; + + Q_snprintf( path, sizeof( path ), fmt->formatstring, name, suffix, fmt->ext ); + f = FS_LoadFile( path, &filesize, false ); + + if( f ) + { + success = Image_ProbeLoadBuffer( fmt, path, f, filesize, override_hint ); + + Mem_Free( f ); + } + + return success; +} + +static qboolean Image_ProbeLoad( const loadpixformat_t *fmt, const char *name, const char *suffix, int override_hint ) +{ + if( !fmt ) + { + // bruteforce all formats to allow implicit extension + for( fmt = image.loadformats; fmt->formatstring; fmt++ ) + { + if( Image_ProbeLoad_( fmt, name, suffix, override_hint )) + return true; + } + + return false; + } + + return Image_ProbeLoad_( fmt, name, suffix, override_hint ); +} + /* ================ FS_LoadImage @@ -213,87 +296,35 @@ loading and unpack to rgba any known image rgbdata_t *FS_LoadImage( const char *filename, const byte *buffer, size_t size ) { const char *ext = COM_FileExtension( filename ); - string path, loadname, sidename; - qboolean anyformat = true; + string loadname; int i; - fs_offset_t filesize = 0; - const loadpixformat_t *format; + const loadpixformat_t *extfmt; const cubepack_t *cmap; - byte *f; Q_strncpy( loadname, filename, sizeof( loadname )); Image_Reset(); // clear old image - if( COM_CheckStringEmpty( ext )) - { - // we needs to compare file extension with list of supported formats - // and be sure what is real extension, not a filename with dot - for( format = image.loadformats; format && format->formatstring; format++ ) - { - if( !Q_stricmp( format->ext, ext )) - { - COM_StripExtension( loadname ); - anyformat = false; - break; - } - } - } + // we needs to compare file extension with list of supported formats + // and be sure what is real extension, not a filename with dot + if(( extfmt = Image_GetLoadFormatForExtension( ext ))) + COM_StripExtension( loadname ); // special mode: skip any checks, load file from buffer if( filename[0] == '#' && buffer && size ) goto load_internal; - // now try all the formats in the selected list - for( format = image.loadformats; format && format->formatstring; format++) - { - if( anyformat || !Q_stricmp( ext, format->ext )) - { - Q_sprintf( path, format->formatstring, loadname, "", format->ext ); - image.hint = format->hint; - f = FS_LoadFile( path, &filesize, false ); - - if( f && filesize > 0 ) - { - if( format->loadfunc( path, f, filesize )) - { - Mem_Free( f ); // release buffer - return ImagePack(); // loaded - } - else Mem_Free( f ); // release buffer - } - } - } + if( Image_ProbeLoad( extfmt, loadname, "", -1 )) + return ImagePack(); // check all cubemap sides with package suffix for( cmap = load_cubemap; cmap && cmap->type; cmap++ ) { for( i = 0; i < 6; i++ ) { - // for support mixed cubemaps e.g. sky_ft.bmp, sky_rt.tga - // NOTE: all loaders must keep sides in one format for all - for( format = image.loadformats; format && format->formatstring; format++ ) + if( Image_ProbeLoad( extfmt, loadname, cmap->type[i].suf, cmap->type[i].hint ) && + FS_AddSideToPack( cmap->type[i].flags )) // process flags to flip some sides { - if( anyformat || !Q_stricmp( ext, format->ext )) - { - Q_sprintf( path, format->formatstring, loadname, cmap->type[i].suf, format->ext ); - image.hint = (image_hint_t)cmap->type[i].hint; // side hint - - f = FS_LoadFile( path, &filesize, false ); - if( f && filesize > 0 ) - { - // this name will be used only for tell user about problems - if( format->loadfunc( path, f, filesize )) - { - Q_snprintf( sidename, sizeof( sidename ), "%s%s.%s", loadname, cmap->type[i].suf, format->ext ); - if( FS_AddSideToPack( sidename, cmap->type[i].flags )) // process flags to flip some sides - { - Mem_Free( f ); - break; // loaded - } - } - Mem_Free( f ); - } - } + break; } if( image.num_sides != i + 1 ) // check side @@ -323,20 +354,13 @@ rgbdata_t *FS_LoadImage( const char *filename, const byte *buffer, size_t size ) return ImagePack(); // all done load_internal: - for( format = image.loadformats; format && format->formatstring; format++ ) + if( buffer && size ) { - if( anyformat || !Q_stricmp( ext, format->ext )) - { - image.hint = format->hint; - if( buffer && size > 0 ) - { - if( format->loadfunc( loadname, buffer, size )) - return ImagePack(); // loaded - } - } + if( Image_ProbeLoadBuffer( extfmt, loadname, buffer, size, -1 )) + return ImagePack(); } - if( filename[0] != '#' ) + if( loadname[0] != '#' ) Con_Reportf( S_WARN "FS_LoadImage: couldn't load \"%s\"\n", loadname ); // clear any force flags @@ -345,6 +369,8 @@ load_internal: return NULL; } + + /* ================ Image_Save From e48b708fa67aa199dd58be1f6581c975ba7b88a8 Mon Sep 17 00:00:00 2001 From: Alibek Omarov Date: Mon, 5 Dec 2022 03:22:43 +0300 Subject: [PATCH 242/490] engine: imagelib: img_png: validate image size through common engine function --- common/com_image.h | 1 + engine/common/imagelib/img_png.c | 9 +++++---- 2 files changed, 6 insertions(+), 4 deletions(-) diff --git a/common/com_image.h b/common/com_image.h index c270cf26..d9f5ef6b 100644 --- a/common/com_image.h +++ b/common/com_image.h @@ -49,6 +49,7 @@ typedef enum IL_DDS_HARDWARE = BIT(4), // DXT compression is support IL_LOAD_DECAL = BIT(5), // special mode for load gradient decals IL_OVERVIEW = BIT(6), // overview required some unque operations + IL_LOAD_PLAYER_DECAL = BIT(7), // special mode for player decals } ilFlags_t; // goes into rgbdata_t->encode diff --git a/engine/common/imagelib/img_png.c b/engine/common/imagelib/img_png.c index 1b85a300..95addfe8 100644 --- a/engine/common/imagelib/img_png.c +++ b/engine/common/imagelib/img_png.c @@ -82,8 +82,8 @@ qboolean Image_LoadPNG( const char *name, const byte *buffer, fs_offset_t filesi } // convert image width and height to little endian - png_hdr.ihdr_chunk.height = ntohl( png_hdr.ihdr_chunk.height ); - png_hdr.ihdr_chunk.width = ntohl( png_hdr.ihdr_chunk.width ); + image.height = png_hdr.ihdr_chunk.height = ntohl( png_hdr.ihdr_chunk.height ); + image.width = png_hdr.ihdr_chunk.width = ntohl( png_hdr.ihdr_chunk.width ); if( png_hdr.ihdr_chunk.height == 0 || png_hdr.ihdr_chunk.width == 0 ) { @@ -91,6 +91,9 @@ qboolean Image_LoadPNG( const char *name, const byte *buffer, fs_offset_t filesi return false; } + if( !Image_ValidSize( name )) + return false; + if( png_hdr.ihdr_chunk.bitdepth != 8 ) { Con_DPrintf( S_WARN "Image_LoadPNG: Only 8-bit images is supported (%s)\n", name ); @@ -261,8 +264,6 @@ qboolean Image_LoadPNG( const char *name, const byte *buffer, fs_offset_t filesi } image.type = PF_RGBA_32; // always exctracted to 32-bit buffer - image.width = png_hdr.ihdr_chunk.width; - image.height = png_hdr.ihdr_chunk.height; pixel_count = image.height * image.width; image.size = pixel_count * 4; From 21c898d7969d400aa442148a1874d15bbfb73a8d Mon Sep 17 00:00:00 2001 From: Alibek Omarov Date: Mon, 5 Dec 2022 04:55:24 +0300 Subject: [PATCH 243/490] engine: imagelib: validate player decal image size (max 512x512) --- engine/common/imagelib/imagelib.h | 2 ++ engine/common/imagelib/img_utils.c | 11 ++++++++++- 2 files changed, 12 insertions(+), 1 deletion(-) diff --git a/engine/common/imagelib/imagelib.h b/engine/common/imagelib/imagelib.h index d0da19c1..26905ba2 100644 --- a/engine/common/imagelib/imagelib.h +++ b/engine/common/imagelib/imagelib.h @@ -103,6 +103,8 @@ typedef struct imglib_s #define IMAGE_MAXHEIGHT 8192 #define LUMP_MAXWIDTH 1024 // WorldCraft limits #define LUMP_MAXHEIGHT 1024 +#define PLDECAL_MAXWIDTH 512 +#define PLDECAL_MAXHEIGHT 512 enum { diff --git a/engine/common/imagelib/img_utils.c b/engine/common/imagelib/img_utils.c index ce2e57bd..094f6345 100644 --- a/engine/common/imagelib/img_utils.c +++ b/engine/common/imagelib/img_utils.c @@ -237,7 +237,16 @@ void Image_AddCmdFlags( uint flags ) qboolean Image_ValidSize( const char *name ) { - if( image.width > IMAGE_MAXWIDTH || image.height > IMAGE_MAXHEIGHT || image.width <= 0 || image.height <= 0 ) + int max_width = IMAGE_MAXWIDTH; + int max_height = IMAGE_MAXHEIGHT; + + if( Image_CheckFlag( IL_LOAD_PLAYER_DECAL )) + { + max_width = PLDECAL_MAXWIDTH; + max_height = PLDECAL_MAXHEIGHT; + } + + if( image.width > max_width || image.height > max_height || image.width <= 0 || image.height <= 0 ) { Con_DPrintf( S_ERROR "Image: (%s) dims out of range [%dx%d]\n", name, image.width, image.height ); return false; From 5c2c02c317682c97393fb489c1b71056e0ba5134 Mon Sep 17 00:00:00 2001 From: Alibek Omarov Date: Mon, 5 Dec 2022 04:56:07 +0300 Subject: [PATCH 244/490] engine: common: custom: use IL_LOAD_PLAYER_DECAL flag when loading custom player decals --- engine/common/custom.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/engine/common/custom.c b/engine/common/custom.c index 745d85f6..368691f5 100644 --- a/engine/common/custom.c +++ b/engine/common/custom.c @@ -26,6 +26,8 @@ static rgbdata_t *CustomDecal_LoadImage( const char *path, void *raw, int size ) testname = "#logo.png"; else testname = "#logo.bmp"; + Image_SetForceFlags( IL_LOAD_PLAYER_DECAL ); + return FS_LoadImage( testname, raw, size ); } From 9b001987e98d8ee0d5e29421ac78560c0396b3ed Mon Sep 17 00:00:00 2001 From: Alibek Omarov Date: Mon, 5 Dec 2022 05:39:41 +0300 Subject: [PATCH 245/490] engine: imagelib: fix crash when chunk length is more than file size --- engine/common/imagelib/img_png.c | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/engine/common/imagelib/img_png.c b/engine/common/imagelib/img_png.c index 95addfe8..ac013988 100644 --- a/engine/common/imagelib/img_png.c +++ b/engine/common/imagelib/img_png.c @@ -161,7 +161,14 @@ qboolean Image_LoadPNG( const char *name, const byte *buffer, fs_offset_t filesi if( chunk_len > INT_MAX ) { Con_DPrintf( S_ERROR "Image_LoadPNG: Found chunk with wrong size (%s)\n", name ); - Mem_Free( idat_buf ); + if( idat_buf ) Mem_Free( idat_buf ); + return false; + } + + if( chunk_len > filesize - ( buf_p - buffer )) + { + Con_DPrintf( S_ERROR "Image_LoadPNG: Found chunk with size past file size (%s)\n", name ); + if( idat_buf ) Mem_Free( idat_buf ); return false; } From 0e9106685bfb843473994750cf8c9ea097001cbc Mon Sep 17 00:00:00 2001 From: Alibek Omarov Date: Mon, 5 Dec 2022 06:13:04 +0300 Subject: [PATCH 246/490] engine: imagelib: img_png: fix Mem_Free on null ptr --- engine/common/imagelib/img_png.c | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/engine/common/imagelib/img_png.c b/engine/common/imagelib/img_png.c index ac013988..8056ac7c 100644 --- a/engine/common/imagelib/img_png.c +++ b/engine/common/imagelib/img_png.c @@ -214,7 +214,7 @@ qboolean Image_LoadPNG( const char *name, const byte *buffer, fs_offset_t filesi if( ntohl( crc32 ) != crc32_check ) { Con_DPrintf( S_ERROR "Image_LoadPNG: Found chunk with wrong CRC32 sum (%s)\n", name ); - Mem_Free( idat_buf ); + if( idat_buf ) Mem_Free( idat_buf ); return false; } @@ -222,6 +222,12 @@ qboolean Image_LoadPNG( const char *name, const byte *buffer, fs_offset_t filesi buf_p += sizeof( crc32 ); } + if( oldsize == 0 ) + { + Con_DPrintf( S_ERROR "Image_LoadPNG: Couldn't find IDAT chunks (%s)\n", name ); + return false; + } + if( png_hdr.ihdr_chunk.colortype == PNG_CT_PALLETE && !pallete ) { Con_DPrintf( S_ERROR "Image_LoadPNG: PLTE chunk not found (%s)\n", name ); @@ -243,12 +249,6 @@ qboolean Image_LoadPNG( const char *name, const byte *buffer, fs_offset_t filesi return false; } - if( oldsize == 0 ) - { - Con_DPrintf( S_ERROR "Image_LoadPNG: Couldn't find IDAT chunks (%s)\n", name ); - return false; - } - switch( png_hdr.ihdr_chunk.colortype ) { case PNG_CT_GREY: From 406eb828da30708af1818ae4d260b140831f1edd Mon Sep 17 00:00:00 2001 From: Alibek Omarov Date: Mon, 5 Dec 2022 11:59:29 +0300 Subject: [PATCH 247/490] ref: gl: disable underwater distortion by default, enable for Quake compatible mode only --- ref/gl/gl_rmain.c | 2 +- ref/soft/r_main.c | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/ref/gl/gl_rmain.c b/ref/gl/gl_rmain.c index e8f93616..be5cd3d2 100644 --- a/ref/gl/gl_rmain.c +++ b/ref/gl/gl_rmain.c @@ -336,7 +336,7 @@ void R_SetupFrustum( void ) { const ref_overview_t *ov = gEngfuncs.GetOverviewParms(); - if( RP_NORMALPASS() && ( ENGINE_GET_PARM( PARM_WATER_LEVEL ) >= 3 )) + if( RP_NORMALPASS() && ( ENGINE_GET_PARM( PARM_WATER_LEVEL ) >= 3 ) && ENGINE_GET_PARM( PARM_QUAKE_COMPATIBLE )) { RI.fov_x = atan( tan( DEG2RAD( RI.fov_x ) / 2 ) * ( 0.97f + sin( gpGlobals->time * 1.5f ) * 0.03f )) * 2 / (M_PI_F / 180.0f); RI.fov_y = atan( tan( DEG2RAD( RI.fov_y ) / 2 ) * ( 1.03f - sin( gpGlobals->time * 1.5f ) * 0.03f )) * 2 / (M_PI_F / 180.0f); diff --git a/ref/soft/r_main.c b/ref/soft/r_main.c index 44d2a383..243429ad 100644 --- a/ref/soft/r_main.c +++ b/ref/soft/r_main.c @@ -451,7 +451,7 @@ void R_SetupFrustum( void ) #if 1 //ref_overview_t *ov = gEngfuncs.GetOverviewParms(); - /*if( RP_NORMALPASS() && ( ENGINE_GET_PARM( PARM_WATER_LEVEL ) >= 3 )) + /*if( RP_NORMALPASS() && ( ENGINE_GET_PARM( PARM_WATER_LEVEL ) >= 3 ) && ENGINE_GET_PARM( PARM_QUAKE_COMPATIBLE )) { RI.fov_x = atan( tan( DEG2RAD( RI.fov_x ) / 2 ) * ( 0.97 + sin( gpGlobals->time * 1.5 ) * 0.03 )) * 2 / (M_PI / 180.0); RI.fov_y = atan( tan( DEG2RAD( RI.fov_y ) / 2 ) * ( 1.03 - sin( gpGlobals->time * 1.5 ) * 0.03 )) * 2 / (M_PI / 180.0); From 802c7a86a9fc56d458bbeaddc3d17d2bc069570e Mon Sep 17 00:00:00 2001 From: Alibek Omarov Date: Tue, 6 Dec 2022 11:50:38 +0300 Subject: [PATCH 248/490] engine: platform: sdl: add pause and scrolllock handlers --- engine/platform/sdl/events.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/engine/platform/sdl/events.c b/engine/platform/sdl/events.c index 515739dd..0219605c 100644 --- a/engine/platform/sdl/events.c +++ b/engine/platform/sdl/events.c @@ -224,9 +224,11 @@ static void SDLash_KeyEvent( SDL_KeyboardEvent key ) host.force_draw_version_time = host.realtime + FORCE_DRAW_VERSION_TIME; break; } + case SDL_SCANCODE_PAUSE: keynum = K_PAUSE; break; + case SDL_SCANCODE_SCROLLLOCK: keynum = K_SCROLLOCK; break; #if SDL_VERSION_ATLEAST( 2, 0, 0 ) case SDL_SCANCODE_APPLICATION: keynum = K_WIN; break; // (compose key) ??? - // don't console spam on known functional buttons, but not used in engine + // don't console spam on known functional buttons, not used in engine case SDL_SCANCODE_MUTE: case SDL_SCANCODE_VOLUMEUP: case SDL_SCANCODE_VOLUMEDOWN: From 1d8acc16f1411dfbedbba816fd79f529a31f4b55 Mon Sep 17 00:00:00 2001 From: Alibek Omarov Date: Tue, 6 Dec 2022 20:28:24 +0300 Subject: [PATCH 249/490] engine: server: give master server a small time window to reply (by default 4000 ms, should be enough even when master server is overloaded) --- engine/server/sv_main.c | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/engine/server/sv_main.c b/engine/server/sv_main.c index 97db71cd..f78dc565 100644 --- a/engine/server/sv_main.c +++ b/engine/server/sv_main.c @@ -58,6 +58,7 @@ CVAR_DEFINE_AUTO( mp_logfile, "1", 0, "log multiplayer frags to console" ); CVAR_DEFINE_AUTO( sv_log_singleplayer, "0", FCVAR_ARCHIVE, "allows logging in singleplayer games" ); CVAR_DEFINE_AUTO( sv_log_onefile, "0", FCVAR_ARCHIVE, "logs server information to only one file" ); CVAR_DEFINE_AUTO( sv_trace_messages, "0", FCVAR_LATCH, "enable server usermessages tracing (good for developers)" ); +CVAR_DEFINE_AUTO( sv_master_response_timeout, "4", FCVAR_ARCHIVE, "master server heartbeat response timeout in seconds" ); // game-related cvars CVAR_DEFINE_AUTO( mapcyclefile, "mapcycle.txt", 0, "name of multiplayer map cycle configuration file" ); @@ -751,6 +752,12 @@ void SV_AddToMaster( netadr_t from, sizebuf_t *msg ) return; } + if( svs.last_heartbeat + sv_master_response_timeout.value < host.realtime ) + { + Con_Printf( S_WARN "unexpected master server info query packet (too late? try increasing sv_master_response_timeout value)\n"); + return; + } + SV_GetPlayerCount( &clients, &bots ); challenge = MSG_ReadUBitLong( msg, sizeof( uint ) << 3 ); @@ -937,6 +944,7 @@ void SV_Init( void ) Cvar_RegisterVariable( &mp_logfile ); Cvar_RegisterVariable( &sv_log_onefile ); Cvar_RegisterVariable( &sv_log_singleplayer ); + Cvar_RegisterVariable( &sv_master_response_timeout ); Cvar_RegisterVariable( &sv_background_freeze ); From 5098e24806d14848c806fb0eee7630c75f6a2db6 Mon Sep 17 00:00:00 2001 From: Velaron Date: Wed, 7 Dec 2022 13:36:16 +0200 Subject: [PATCH 250/490] engine: client: touch: fix empty list when opening touch buttons menu for the first time --- engine/client/in_touch.c | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/engine/client/in_touch.c b/engine/client/in_touch.c index 7cf93925..1dcb433b 100644 --- a/engine/client/in_touch.c +++ b/engine/client/in_touch.c @@ -1105,8 +1105,14 @@ static void Touch_InitConfig( void ) /// TODO: hud font //pfnGetScreenInfo( NULL ); //HACK: update hud screen parameters like iHeight if( FS_FileExists( touch_config_file->string, true ) ) + { Cbuf_AddText( va( "exec \"%s\"\n", touch_config_file->string ) ); - else Touch_LoadDefaults_f( ); + Cbuf_Execute(); + } + else + { + Touch_LoadDefaults_f(); + } Touch_InitEditor(); touch.joytexture = ref.dllFuncs.GL_LoadTexture( touch_joy_texture->string, NULL, 0, TF_NOMIPMAP ); From 436a788ac8999239a2ae35a7ab812f4182617f0f Mon Sep 17 00:00:00 2001 From: Alibek Omarov Date: Wed, 7 Dec 2022 20:25:14 +0300 Subject: [PATCH 251/490] engine: client: disable FPS counter by default --- engine/client/cl_scrn.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/engine/client/cl_scrn.c b/engine/client/cl_scrn.c index b65c9e59..22cf9280 100644 --- a/engine/client/cl_scrn.c +++ b/engine/client/cl_scrn.c @@ -829,7 +829,7 @@ void SCR_Init( void ) v_dark = Cvar_Get( "v_dark", "0", 0, "starts level from dark screen" ); scr_viewsize = Cvar_Get( "viewsize", "120", FCVAR_ARCHIVE, "screen size" ); net_speeds = Cvar_Get( "net_speeds", "0", FCVAR_ARCHIVE, "show network packets" ); - cl_showfps = Cvar_Get( "cl_showfps", "1", FCVAR_ARCHIVE, "show client fps" ); + cl_showfps = Cvar_Get( "cl_showfps", "0", FCVAR_ARCHIVE, "show client fps" ); cl_showpos = Cvar_Get( "cl_showpos", "0", FCVAR_ARCHIVE, "show local player position and velocity" ); // register our commands From 859f36afceaf894abe505b5536d525d7ef2bccb2 Mon Sep 17 00:00:00 2001 From: Alibek Omarov Date: Wed, 7 Dec 2022 23:14:34 +0300 Subject: [PATCH 252/490] engine: server: remove Master_Add call in ActivateServer, server will announce itself through heartbeat --- engine/server/sv_init.c | 3 --- 1 file changed, 3 deletions(-) diff --git a/engine/server/sv_init.c b/engine/server/sv_init.c index ac85534e..f4ad6ba1 100644 --- a/engine/server/sv_init.c +++ b/engine/server/sv_init.c @@ -628,9 +628,6 @@ void SV_ActivateServer( int runPhysics ) if( COM_CheckString( cycle )) Cbuf_AddText( va( "exec %s\n", cycle )); - - if( public_server->value ) - Master_Add( ); } } From 7469d6a2488d0fe1ba4edfda2a996240285e79a1 Mon Sep 17 00:00:00 2001 From: Alibek Omarov Date: Wed, 7 Dec 2022 23:39:57 +0300 Subject: [PATCH 253/490] engine: server: implement server-to-master challenge extension, to secure server from IP spoofing --- engine/server/server.h | 4 +--- engine/server/sv_main.c | 38 ++++++++++++++++++++++++++++---------- 2 files changed, 29 insertions(+), 13 deletions(-) diff --git a/engine/server/server.h b/engine/server/server.h index 47bf848a..d2df0bdc 100644 --- a/engine/server/server.h +++ b/engine/server/server.h @@ -377,6 +377,7 @@ typedef struct double last_heartbeat; challenge_t challenges[MAX_CHALLENGES]; // to prevent invalid IPs from connecting + uint heartbeat_challenge; } server_static_t; //============================================================================= @@ -478,9 +479,6 @@ qboolean SV_ProcessUserAgent( netadr_t from, const char *useragent ); void Host_SetServerState( int state ); qboolean SV_IsSimulating( void ); void SV_FreeClients( void ); -void Master_Add( void ); -void Master_Heartbeat( void ); -void Master_Packet( void ); // // sv_init.c diff --git a/engine/server/sv_main.c b/engine/server/sv_main.c index f78dc565..eefad86c 100644 --- a/engine/server/sv_main.c +++ b/engine/server/sv_main.c @@ -139,7 +139,7 @@ convar_t *sv_allow_mouse; convar_t *sv_allow_joystick; convar_t *sv_allow_vr; -void Master_Shutdown( void ); +static void Master_Heartbeat( void ); //============================================================================ /* @@ -686,10 +686,21 @@ void Host_SetServerState( int state ) Master_Add ================= */ -void Master_Add( void ) +static void Master_Add( void ) { + sizebuf_t msg; + char buf[16]; + uint challenge; + NET_Config( true, false ); // allow remote - if( NET_SendToMasters( NS_SERVER, 2, "q\xFF" )) + + svs.heartbeat_challenge = challenge = COM_RandomLong( 0, INT_MAX ); + + MSG_Init( &msg, "Master Join", buf, sizeof( buf )); + MSG_WriteBytes( &msg, "q\xFF", 2 ); + MSG_WriteDword( &msg, challenge ); + + if( NET_SendToMasters( NS_SERVER, MSG_GetNumBytesWritten( &msg ), MSG_GetBuf( &msg ))) svs.last_heartbeat = MAX_HEARTBEAT; } @@ -701,7 +712,7 @@ Send a message to the master every few minutes to let it know we are alive, and log information ================ */ -void Master_Heartbeat( void ) +static void Master_Heartbeat( void ) { if( !public_server->value || svs.maxclients == 1 ) return; // only public servers send heartbeats @@ -725,7 +736,7 @@ Master_Shutdown Informs all masters that this server is going down ================= */ -void Master_Shutdown( void ) +static void Master_Shutdown( void ) { NET_Config( true, false ); // allow remote while( NET_SendToMasters( NS_SERVER, 2, "\x62\x0A" )); @@ -741,10 +752,10 @@ Master will validate challenge and this server to public list */ void SV_AddToMaster( netadr_t from, sizebuf_t *msg ) { - uint challenge; + uint challenge, challenge2; char s[MAX_INFO_STRING] = "0\n"; // skip 2 bytes of header int clients, bots; - int len = sizeof( s ); + const int len = sizeof( s ); if( !NET_IsMasterAdr( from )) { @@ -758,9 +769,16 @@ void SV_AddToMaster( netadr_t from, sizebuf_t *msg ) return; } - SV_GetPlayerCount( &clients, &bots ); - challenge = MSG_ReadUBitLong( msg, sizeof( uint ) << 3 ); + challenge = MSG_ReadDword( msg ); + challenge2 = MSG_ReadDword( msg ); + if( challenge2 != svs.heartbeat_challenge ) + { + Con_Printf( S_WARN "unexpected master server info query packet (wrong challenge!)\n" ); + return; + } + + SV_GetPlayerCount( &clients, &bots ); Info_SetValueForKey( s, "protocol", va( "%d", PROTOCOL_VERSION ), len ); // protocol version Info_SetValueForKey( s, "challenge", va( "%u", challenge ), len ); // challenge number Info_SetValueForKey( s, "players", va( "%d", clients ), len ); // current player number, without bots @@ -776,7 +794,7 @@ void SV_AddToMaster( netadr_t from, sizebuf_t *msg ) Info_SetValueForKey( s, "version", XASH_VERSION, len ); // server region. 255 -- all regions Info_SetValueForKey( s, "region", "255", len ); // server region. 255 -- all regions Info_SetValueForKey( s, "product", GI->gamefolder, len ); // product? Where is the difference with gamedir? - Info_SetValueForKey( s, "nat", sv_nat.string, sizeof(s) ); // Server running under NAT, use reverse connection + Info_SetValueForKey( s, "nat", sv_nat.string, len ); // Server running under NAT, use reverse connection NET_SendPacket( NS_SERVER, Q_strlen( s ), s, from ); } From ea3bfd969c06b64126affcec571b38e12fc2ff46 Mon Sep 17 00:00:00 2001 From: Alibek Omarov Date: Thu, 8 Dec 2022 05:39:29 +0300 Subject: [PATCH 254/490] engine: imagelib: img_wad: dirty hack to fix black holes in console background images --- engine/common/imagelib/img_wad.c | 14 +++++++++----- 1 file changed, 9 insertions(+), 5 deletions(-) diff --git a/engine/common/imagelib/img_wad.c b/engine/common/imagelib/img_wad.c index fcb22a35..e6d4f779 100644 --- a/engine/common/imagelib/img_wad.c +++ b/engine/common/imagelib/img_wad.c @@ -311,13 +311,17 @@ qboolean Image_LoadLMP( const char *name, const byte *buffer, fs_offset_t filesi { int numcolors; - for( i = 0; i < pixels; i++ ) + // HACKHACK: console background image shouldn't be transparent + if( !Q_stristr( name, "conback" )) { - if( fin[i] == 255 ) + for( i = 0; i < pixels; i++ ) { - image.flags |= IMAGE_HAS_ALPHA; - rendermode = LUMP_MASKED; - break; + if( fin[i] == 255 ) + { + image.flags |= IMAGE_HAS_ALPHA; + rendermode = LUMP_MASKED; + break; + } } } pal = fin + pixels; From e48133bf4b6373b0a7a6aa7a4e6ec63c25ce0dba Mon Sep 17 00:00:00 2001 From: Alibek Omarov Date: Thu, 8 Dec 2022 05:40:36 +0300 Subject: [PATCH 255/490] engine: server: fix sv_log output for enttools usage --- engine/server/sv_client.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/engine/server/sv_client.c b/engine/server/sv_client.c index a7bc8c2f..c713a1e2 100644 --- a/engine/server/sv_client.c +++ b/engine/server/sv_client.c @@ -2958,7 +2958,7 @@ void SV_ExecuteClientCommand( sv_client_t *cl, const char *s ) { Con_Reportf( "enttools->%s(): %s\n", u->name, s ); Log_Printf( "\"%s<%i><%s><>\" performed: %s\n", Info_ValueForKey( cl->userinfo, "name" ), - cl->userid, SV_GetClientIDString( cl ), NET_AdrToString( cl->netchan.remote_address ), s ); + cl->userid, SV_GetClientIDString( cl ), s ); if( u->func ) u->func( cl ); From d50ed1c087be28d9e2564811949f3a3d50133b2c Mon Sep 17 00:00:00 2001 From: Alibek Omarov Date: Thu, 8 Dec 2022 05:44:17 +0300 Subject: [PATCH 256/490] engine: common: host: don't prepend # to command arguments when changing game to dedicated, it wasn't used and implement anymore --- engine/common/host.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/engine/common/host.c b/engine/common/host.c index b129b92a..13a12e38 100644 --- a/engine/common/host.c +++ b/engine/common/host.c @@ -345,7 +345,7 @@ void Host_ChangeGame_f( void ) } else { - const char *arg1 = va( "%s%s", (host.type == HOST_NORMAL) ? "" : "#", Cmd_Argv( 1 )); + const char *arg1 = va( "%s", Cmd_Argv( 1 )); const char *arg2 = va( "change game to '%s'", FI->games[i]->title ); Host_NewInstance( arg1, arg2 ); From fd3c5e8384d16133b0ff112c4c429341aa514463 Mon Sep 17 00:00:00 2001 From: Alibek Omarov Date: Thu, 8 Dec 2022 06:50:12 +0300 Subject: [PATCH 257/490] ref: gl: VBO is disabled unless somebody picks it up and fixes memory corruption and other bugs --- ref/gl/gl_rsurf.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/ref/gl/gl_rsurf.c b/ref/gl/gl_rsurf.c index d4d4f7de..36472080 100644 --- a/ref/gl/gl_rsurf.c +++ b/ref/gl/gl_rsurf.c @@ -1792,7 +1792,11 @@ void R_GenerateVBO( void ) R_ClearVBO(); // we do not want to write vbo code that does not use multitexture +#if ALLOW_VBO if( !GL_Support( GL_ARB_VERTEX_BUFFER_OBJECT_EXT ) || !GL_Support( GL_ARB_MULTITEXTURE ) || glConfig.max_texture_units < 2 ) +#else + if( 1 ) +#endif { gEngfuncs.Cvar_FullSet( "gl_vbo", "0", FCVAR_READ_ONLY ); return; From 9fa4de6ee8594fa46dcb5fd3cdd82491213e18ad Mon Sep 17 00:00:00 2001 From: Alibek Omarov Date: Thu, 8 Dec 2022 06:55:32 +0300 Subject: [PATCH 258/490] mainui: update --- 3rdparty/mainui | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/3rdparty/mainui b/3rdparty/mainui index 73d8bb6d..17272d34 160000 --- a/3rdparty/mainui +++ b/3rdparty/mainui @@ -1 +1 @@ -Subproject commit 73d8bb6da0639af92c8d627327e003ea11a149c7 +Subproject commit 17272d34a767fb77e62a724186b74cae7dc0aef2 From dbe930947544cf1cec1840d0322f38d00e8e2e94 Mon Sep 17 00:00:00 2001 From: SNMetamorph <25657591+SNMetamorph@users.noreply.github.com> Date: Fri, 9 Dec 2022 17:28:23 +0400 Subject: [PATCH 259/490] engine: netchan: fixed wrong compressed file size calculation --- engine/common/net_chan.c | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/engine/common/net_chan.c b/engine/common/net_chan.c index 93177df1..e2eea39e 100644 --- a/engine/common/net_chan.c +++ b/engine/common/net_chan.c @@ -978,8 +978,12 @@ int Netchan_CreateFileFragments( netchan_t *chan, const char *filename ) if( compressedFileTime >= fileTime ) { // if compressed file already created and newer than source - if( FS_FileSize( compressedfilename, false ) != -1 ) + fs_offset_t compressedSize = FS_FileSize( compressedfilename, false ); + if( compressedSize != -1 ) + { bCompressed = true; + filesize = compressedSize; + } } else { From d72481e5acde160212468dc4329bd12a28fe8a9b Mon Sep 17 00:00:00 2001 From: SNMetamorph <25657591+SNMetamorph@users.noreply.github.com> Date: Fri, 9 Dec 2022 17:30:35 +0400 Subject: [PATCH 260/490] engine: netchan: fixed downloading files output directory --- engine/common/net_chan.c | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/engine/common/net_chan.c b/engine/common/net_chan.c index e2eea39e..5662fe49 100644 --- a/engine/common/net_chan.c +++ b/engine/common/net_chan.c @@ -1192,6 +1192,13 @@ qboolean Netchan_CopyFileFragments( netchan_t *chan, sizebuf_t *msg ) return false; } + if( filename[0] != '!' ) + { + string temp_filename; + Q_snprintf( temp_filename, sizeof( temp_filename ), "downloaded/%s", filename ); + Q_strncpy( filename, temp_filename, sizeof( filename )); + } + Q_strncpy( chan->incomingfilename, filename, sizeof( chan->incomingfilename )); if( filename[0] != '!' && FS_FileExists( filename, false )) From 840283d6e50e1b1dd2a25fe3195bb363d493be85 Mon Sep 17 00:00:00 2001 From: SNMetamorph <25657591+SNMetamorph@users.noreply.github.com> Date: Fri, 9 Dec 2022 17:33:39 +0400 Subject: [PATCH 261/490] engine: netchan: fixed fragbufs very high memory usage --- engine/common/net_chan.c | 16 +++++++++------- engine/common/netchan.h | 2 +- 2 files changed, 10 insertions(+), 8 deletions(-) diff --git a/engine/common/net_chan.c b/engine/common/net_chan.c index 5662fe49..ba000172 100644 --- a/engine/common/net_chan.c +++ b/engine/common/net_chan.c @@ -414,6 +414,7 @@ void Netchan_ClearFragbufs( fragbuf_t **ppbuf ) while( buf ) { n = buf->next; + Mem_Free( buf->frag_message_buf ); Mem_Free( buf ); buf = n; } @@ -535,12 +536,13 @@ Netchan_AllocFragbuf ============================== */ -fragbuf_t *Netchan_AllocFragbuf( void ) +fragbuf_t *Netchan_AllocFragbuf( int fragment_size ) { fragbuf_t *buf; buf = (fragbuf_t *)Mem_Calloc( net_mempool, sizeof( fragbuf_t )); - MSG_Init( &buf->frag_message, "Frag Message", buf->frag_message_buf, sizeof( buf->frag_message_buf )); + buf->frag_message_buf = (byte *)Mem_Calloc( net_mempool, fragment_size ); + MSG_Init( &buf->frag_message, "Frag Message", buf->frag_message_buf, fragment_size ); return buf; } @@ -736,7 +738,7 @@ static void Netchan_CreateFragments_( netchan_t *chan, sizebuf_t *msg ) bytes = Q_min( remaining, chunksize ); remaining -= bytes; - buf = Netchan_AllocFragbuf(); + buf = Netchan_AllocFragbuf( bytes ); buf->bufferid = bufferid++; // Copy in data @@ -803,7 +805,7 @@ fragbuf_t *Netchan_FindBufferById( fragbuf_t **pplist, int id, qboolean allocate return NULL; // create new entry - pnewbuf = Netchan_AllocFragbuf(); + pnewbuf = Netchan_AllocFragbuf( NET_MAX_FRAGMENT ); pnewbuf->bufferid = id; Netchan_AddBufferToList( pplist, pnewbuf ); @@ -894,7 +896,7 @@ void Netchan_CreateFileFragmentsFromBuffer( netchan_t *chan, const char *filenam { send = Q_min( remaining, chunksize ); - buf = Netchan_AllocFragbuf(); + buf = Netchan_AllocFragbuf( send ); buf->bufferid = bufferid++; // copy in data @@ -959,7 +961,7 @@ int Netchan_CreateFileFragments( netchan_t *chan, const char *filename ) qboolean bCompressed = false; fragbufwaiting_t *wait, *p; fragbuf_t *buf; - + if(( filesize = FS_FileSize( filename, false )) <= 0 ) { Con_Printf( S_WARN "Unable to open %s for transfer\n", filename ); @@ -1013,7 +1015,7 @@ int Netchan_CreateFileFragments( netchan_t *chan, const char *filename ) { send = Q_min( remaining, chunksize ); - buf = Netchan_AllocFragbuf(); + buf = Netchan_AllocFragbuf( send ); buf->bufferid = bufferid++; // copy in data diff --git a/engine/common/netchan.h b/engine/common/netchan.h index daed9f31..3acc28bb 100644 --- a/engine/common/netchan.h +++ b/engine/common/netchan.h @@ -185,7 +185,7 @@ typedef struct fragbuf_s struct fragbuf_s *next; // next buffer in chain int bufferid; // id of this buffer sizebuf_t frag_message; // message buffer where raw data is stored - byte frag_message_buf[NET_MAX_FRAGMENT]; // the actual data sits here + byte *frag_message_buf; // the actual data sits here qboolean isfile; // is this a file buffer? qboolean isbuffer; // is this file buffer from memory ( custom decal, etc. ). qboolean iscompressed; // is compressed file, we should using filename.ztmp From b1d910a3a547047a425f8f91dbddf055a2209175 Mon Sep 17 00:00:00 2001 From: SNMetamorph <25657591+SNMetamorph@users.noreply.github.com> Date: Fri, 9 Dec 2022 19:30:07 +0400 Subject: [PATCH 262/490] engine: client: fixed connection hang when all resources downloaded (fix #829) --- engine/client/cl_main.c | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/engine/client/cl_main.c b/engine/client/cl_main.c index 173e0dbf..872de60a 100644 --- a/engine/client/cl_main.c +++ b/engine/client/cl_main.c @@ -2472,11 +2472,18 @@ void CL_ProcessFile( qboolean successfully_received, const char *filename ) { if( filename[0] != '!' ) Con_Printf( "processing %s\n", filename ); + + if( !Q_strnicmp( filename, "downloaded/", 11 )) + { + // skip "downloaded/" part to avoid mismatch with needed resources list + filename += 11; + } } else if( !successfully_received ) { Con_Printf( S_ERROR "server failed to transmit file '%s'\n", CL_CleanFileName( filename )); } + if( cls.legacymode ) { if( host.downloadcount > 0 ) From 080cd146dd4c91356e194911e1987761f24280f8 Mon Sep 17 00:00:00 2001 From: SNMetamorph <25657591+SNMetamorph@users.noreply.github.com> Date: Thu, 10 Nov 2022 19:26:19 +0400 Subject: [PATCH 263/490] common: com_model: changed common structs reserved fields type to intptr_t --- common/com_model.h | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/common/com_model.h b/common/com_model.h index 36f2e936..22d52243 100644 --- a/common/com_model.h +++ b/common/com_model.h @@ -105,7 +105,7 @@ typedef struct vec3_t mins, maxs; // terrain bounds (fill by user) - int reserved[32]; // just for future expansions or mod-makers + intptr_t reserved[32]; // just for future expansions or mod-makers } mfaceinfo_t; typedef struct @@ -173,8 +173,8 @@ struct decal_s short entityIndex; // Entity this is attached to // Xash3D specific vec3_t position; // location of the decal center in world space. - glpoly_t *polys; // precomputed decal vertices - int reserved[4]; // just for future expansions or mod-makers + glpoly_t *polys; // precomputed decal vertices + intptr_t reserved[4]; // just for future expansions or mod-makers }; typedef struct mleaf_s @@ -228,7 +228,7 @@ typedef struct mextrasurf_s unsigned short numverts; // world->vertexes[] int firstvertex; // fisrt look up in tr.tbn_vectors[], then acess to world->vertexes[] - int reserved[32]; // just for future expansions or mod-makers + intptr_t reserved[32]; // just for future expansions or mod-makers } mextrasurf_t; struct msurface_s From 8fa0290e25ac6b6cb0ac7bd3d1d32f5eadd89e05 Mon Sep 17 00:00:00 2001 From: SNMetamorph <25657591+SNMetamorph@users.noreply.github.com> Date: Sun, 11 Dec 2022 20:49:47 +0400 Subject: [PATCH 264/490] Documentation: extensions: added page about expanded common structures --- Documentation/extensions/expanded-common-structures.md | 8 ++++++++ 1 file changed, 8 insertions(+) create mode 100644 Documentation/extensions/expanded-common-structures.md diff --git a/Documentation/extensions/expanded-common-structures.md b/Documentation/extensions/expanded-common-structures.md new file mode 100644 index 00000000..4b72aedc --- /dev/null +++ b/Documentation/extensions/expanded-common-structures.md @@ -0,0 +1,8 @@ +# Expanded structures that used by engine and mods +To make porting and developing mods on 64-bit platforms less painful, we decided to expand size of several structures. +This information important in case you are using codebase like XashXT, Paranoia 2: Savior and want to compile your mod for platform with 64-bit pointer size: you should replace old definitions with new ones, otherwise your mod will not work with Xash3D FWGS (typically, it's just crashing when starting map). +| Structure name | Locates in file | Original size on 64-bit | Current size on 64-bit | +|----------------|-----------------|-------------------------|------------------------| +|`mfaceinfo_t` | `common/com_model.h` | 176 bytes | 304 bytes | +|`decal_s` | `common/com_model.h` | 72 bytes | 88 bytes | +|`mextrasurf_t` | `common/com_model.h` | 376 bytes | 504 bytes | From 18c94b6ec44208adb829d5993bf1c7e894ff5a44 Mon Sep 17 00:00:00 2001 From: Alibek Omarov Date: Mon, 12 Dec 2022 00:17:28 +0300 Subject: [PATCH 265/490] engine: common: add network address comparator function --- engine/common/net_ws.c | 44 +++++++++++++++++++++++++++++++++++++++++- engine/common/net_ws.h | 1 + 2 files changed, 44 insertions(+), 1 deletion(-) diff --git a/engine/common/net_ws.c b/engine/common/net_ws.c index 3e5cc4a7..e1c5719a 100644 --- a/engine/common/net_ws.c +++ b/engine/common/net_ws.c @@ -931,11 +931,53 @@ qboolean NET_CompareAdr( const netadr_t a, const netadr_t b ) return true; } - Con_DPrintf( S_ERROR "NET_CompareAdr: bad address type\n" ); return false; } +/* +==================== +NET_CompareAdrSort + +Network address sorting comparator +==================== +*/ +int NET_CompareAdrSort( const void *_a, const void *_b ) +{ + const netadr_t *a = _a; + const netadr_t *b = _b; + int portdiff; + + if( a->type6 != b->type6 ) + return (int)a->type6 - (int)b->type6; + + portdiff = (int)a->port - (int)b->port; + + switch( a->type6 ) + { + case NA_IP6: + return NET_NetadrIP6Compare( a, b ); + case NA_MULTICAST_IP6: + return portdiff; + } + + if( a->type != b->type ) + return (int)a->type - (int)b->type; + + switch( a->type ) + { + case NA_IP: + return memcmp( a->ip, b->ip, sizeof( a->ip )); + case NA_IPX: + return memcmp( a->ipx, b->ipx, sizeof( a->ipx )); + case NA_BROADCAST: + case NA_BROADCAST_IPX: + return portdiff; + } + + return 0; +} + /* ==================== NET_IsLocalAddress diff --git a/engine/common/net_ws.h b/engine/common/net_ws.h index dc984347..72780902 100644 --- a/engine/common/net_ws.h +++ b/engine/common/net_ws.h @@ -60,6 +60,7 @@ qboolean NET_CompareClassBAdr( const netadr_t a, const netadr_t b ); qboolean NET_StringToAdr( const char *string, netadr_t *adr ); qboolean NET_StringToFilterAdr( const char *s, netadr_t *adr, uint *prefixlen ); int NET_StringToAdrNB( const char *string, netadr_t *adr ); +int NET_CompareAdrSort( const void *_a, const void *_b ); qboolean NET_CompareAdr( const netadr_t a, const netadr_t b ); qboolean NET_CompareBaseAdr( const netadr_t a, const netadr_t b ); qboolean NET_CompareAdrByMask( const netadr_t a, const netadr_t b, uint prefixlen ); From fe9ed0ac9b17763120554646d12a0187cf884f55 Mon Sep 17 00:00:00 2001 From: Alibek Omarov Date: Mon, 12 Dec 2022 00:24:59 +0300 Subject: [PATCH 266/490] engine: client: gameui: add new NET_CompareAdrSort function to menu API --- engine/client/cl_gameui.c | 3 ++- engine/menu_int.h | 6 +++--- 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/engine/client/cl_gameui.c b/engine/client/cl_gameui.c index e5bf2683..5d3b41ec 100644 --- a/engine/client/cl_gameui.c +++ b/engine/client/cl_gameui.c @@ -1262,7 +1262,8 @@ static ui_extendedfuncs_t gExtendedfuncs = pfnGetRenderers, Sys_DoubleTime, pfnParseFileSafe, - NET_AdrToString + NET_AdrToString, + NET_CompareAdrSort, }; void UI_UnloadProgs( void ) diff --git a/engine/menu_int.h b/engine/menu_int.h index d273d8f6..d32c4bb3 100644 --- a/engine/menu_int.h +++ b/engine/menu_int.h @@ -209,12 +209,12 @@ typedef struct ui_extendedfuncs_s { // new engine extended api start here // returns 1 if there are more in list, otherwise 0 int (*pfnGetRenderers)( unsigned int num, char *shortName, size_t size1, char *readableName, size_t size2 ); - double (*pfnDoubleTime)( void ); - char *(*pfnParseFile)( char *data, char *buf, const int size, unsigned int flags, int *len ); - const char *(*pfnAdrToString)( const struct netadr_s a ); + // network address funcs + const char *(*pfnAdrToString)( const struct netadr_s a ); + int (*pfnCompareAdr)( const void *a, const void *b ); // netadr_t } ui_extendedfuncs_t; // deprecated export from old engine From a86a24d33d7fc176c696b2ea397d041648dc660f Mon Sep 17 00:00:00 2001 From: Alibek Omarov Date: Mon, 12 Dec 2022 00:31:27 +0300 Subject: [PATCH 267/490] mainui: update --- 3rdparty/mainui | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/3rdparty/mainui b/3rdparty/mainui index 17272d34..2ca1e593 160000 --- a/3rdparty/mainui +++ b/3rdparty/mainui @@ -1 +1 @@ -Subproject commit 17272d34a767fb77e62a724186b74cae7dc0aef2 +Subproject commit 2ca1e593d8b0b2c3e2324434451efe22f8b5cc1c From 9e54ddfd55664e13af6bb3a18013eaf467d469f5 Mon Sep 17 00:00:00 2001 From: Alibek Omarov Date: Mon, 12 Dec 2022 06:30:07 +0300 Subject: [PATCH 268/490] engine: client: treat dem_unknown as no-op, until we find real cause of empty holes in demoheader --- engine/client/cl_demo.c | 21 +++++++++++++++++---- 1 file changed, 17 insertions(+), 4 deletions(-) diff --git a/engine/client/cl_demo.c b/engine/client/cl_demo.c index 089898ed..94bdb469 100644 --- a/engine/client/cl_demo.c +++ b/engine/client/cl_demo.c @@ -518,14 +518,26 @@ CL_ReadDemoCmdHeader read the demo command ================= */ -void CL_ReadDemoCmdHeader( byte *cmd, float *dt ) +qboolean CL_ReadDemoCmdHeader( byte *cmd, float *dt ) { // read the command - FS_Read( cls.demofile, cmd, sizeof( byte )); - Assert( *cmd >= 1 && *cmd <= dem_lastcmd ); + // HACKHACK: skip NOPs + do + { + FS_Read( cls.demofile, cmd, sizeof( byte )); + } while( *cmd == dem_unknown ); + + if( *cmd > dem_lastcmd ) + { + Con_Printf( S_ERROR "Demo cmd %d > %d, file offset = %d\n", *cmd, dem_lastcmd, (int)FS_Tell( cls.demofile )); + CL_DemoCompleted(); + return false; + } // read the timestamp FS_Read( cls.demofile, dt, sizeof( float )); + + return true; } /* @@ -913,7 +925,8 @@ qboolean CL_DemoReadMessage( byte *buffer, size_t *length ) if( !cls.demofile ) break; curpos = FS_Tell( cls.demofile ); - CL_ReadDemoCmdHeader( &cmd, &demo.timestamp ); + if( !CL_ReadDemoCmdHeader( &cmd, &demo.timestamp )) + return false; fElapsedTime = CL_GetDemoPlaybackClock() - demo.starttime; if( !cls.timedemo ) bSkipMessage = ((demo.timestamp - cl_serverframetime()) >= fElapsedTime) ? true : false; From 934d9ba69afcdc3e60f23f4f2052a446699cb9ab Mon Sep 17 00:00:00 2001 From: Alibek Omarov Date: Mon, 12 Dec 2022 06:54:42 +0300 Subject: [PATCH 269/490] filesystem: fix FS_GetDiskPath --- filesystem/filesystem.c | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/filesystem/filesystem.c b/filesystem/filesystem.c index cea197a5..08e54214 100644 --- a/filesystem/filesystem.c +++ b/filesystem/filesystem.c @@ -2489,14 +2489,13 @@ return NULL for file in pack */ const char *FS_GetDiskPath( const char *name, qboolean gamedironly ) { - int index; searchpath_t *search; - search = FS_FindFile( name, &index, gamedironly ); + search = FS_FindFile( name, NULL, gamedironly ); if( search ) { - if( index != -1 ) // file in pack or wad + if( search->type != SEARCHPATH_PLAIN ) // file in pack or wad return NULL; return va( "%s%s", search->filename, name ); } From b794f2fda0324f8650e8ed38039676a6ce2e09d2 Mon Sep 17 00:00:00 2001 From: Alibek Omarov Date: Mon, 12 Dec 2022 07:56:03 +0300 Subject: [PATCH 270/490] mainui: update --- 3rdparty/mainui | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/3rdparty/mainui b/3rdparty/mainui index 2ca1e593..09669712 160000 --- a/3rdparty/mainui +++ b/3rdparty/mainui @@ -1 +1 @@ -Subproject commit 2ca1e593d8b0b2c3e2324434451efe22f8b5cc1c +Subproject commit 096697124fcb7cc9720c348352b716232f80fa90 From e1431e10401c1ca2711f854f7d5e0451c21f657c Mon Sep 17 00:00:00 2001 From: Alibek Omarov Date: Mon, 12 Dec 2022 08:02:56 +0300 Subject: [PATCH 271/490] engine: server: add rcon_enable cvar to control whether server should accept remote commands --- engine/server/server.h | 1 + engine/server/sv_client.c | 3 +++ engine/server/sv_main.c | 2 ++ 3 files changed, 6 insertions(+) diff --git a/engine/server/server.h b/engine/server/server.h index d2df0bdc..a6dac467 100644 --- a/engine/server/server.h +++ b/engine/server/server.h @@ -396,6 +396,7 @@ extern convar_t sv_maxunlag; extern convar_t sv_unlagpush; extern convar_t sv_unlagsamples; extern convar_t rcon_password; +extern convar_t rcon_enable; extern convar_t sv_instancedbaseline; extern convar_t sv_background_freeze; extern convar_t sv_minupdaterate; diff --git a/engine/server/sv_client.c b/engine/server/sv_client.c index c713a1e2..ac4fd2ff 100644 --- a/engine/server/sv_client.c +++ b/engine/server/sv_client.c @@ -1013,6 +1013,9 @@ void SV_RemoteCommand( netadr_t from, sizebuf_t *msg ) char remaining[1024]; int i; + if( !rcon_enable.value ) + return; + Con_Printf( "Rcon from %s:\n%s\n", NET_AdrToString( from ), MSG_GetData( msg ) + 4 ); Log_Printf( "Rcon: \"%s\" from \"%s\"\n", MSG_GetData( msg ) + 4, NET_AdrToString( from )); SV_BeginRedirect( from, RD_PACKET, outputbuf, sizeof( outputbuf ) - 16, SV_FlushRedirect ); diff --git a/engine/server/sv_main.c b/engine/server/sv_main.c index eefad86c..6aaa2238 100644 --- a/engine/server/sv_main.c +++ b/engine/server/sv_main.c @@ -30,6 +30,7 @@ CVAR_DEFINE_AUTO( sv_maxunlag, "0.5", 0, "max latency value which can be interpo CVAR_DEFINE_AUTO( sv_unlagpush, "0.0", 0, "interpolation bias for unlag time" ); CVAR_DEFINE_AUTO( sv_unlagsamples, "1", 0, "max samples to interpolate" ); CVAR_DEFINE_AUTO( rcon_password, "", 0, "remote connect password" ); +CVAR_DEFINE_AUTO( rcon_enable, "1", 0, "enable rcon" ); CVAR_DEFINE_AUTO( sv_filterban, "1", 0, "filter banned users" ); CVAR_DEFINE_AUTO( sv_cheats, "0", FCVAR_SERVER, "allow cheats on server" ); CVAR_DEFINE_AUTO( sv_instancedbaseline, "1", 0, "allow to use instanced baselines to saves network overhead" ); @@ -909,6 +910,7 @@ void SV_Init( void ) Cvar_RegisterVariable( &temp1 ); Cvar_RegisterVariable( &rcon_password ); + Cvar_RegisterVariable( &rcon_enable ); Cvar_RegisterVariable( &sv_stepsize ); Cvar_RegisterVariable( &sv_newunit ); Cvar_RegisterVariable( &hostname ); From 60e7a7aa2311c14c91afdb091eb6a7ff049a0edf Mon Sep 17 00:00:00 2001 From: Alibek Omarov Date: Mon, 12 Dec 2022 08:05:55 +0300 Subject: [PATCH 272/490] engine: server: set correct flags for rcon_ cvars on server --- engine/server/sv_main.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/engine/server/sv_main.c b/engine/server/sv_main.c index 6aaa2238..8f32e7eb 100644 --- a/engine/server/sv_main.c +++ b/engine/server/sv_main.c @@ -29,8 +29,8 @@ CVAR_DEFINE_AUTO( sv_unlag, "1", 0, "allow lag compensation on server-side" ); CVAR_DEFINE_AUTO( sv_maxunlag, "0.5", 0, "max latency value which can be interpolated (by default ping should not exceed 500 units)" ); CVAR_DEFINE_AUTO( sv_unlagpush, "0.0", 0, "interpolation bias for unlag time" ); CVAR_DEFINE_AUTO( sv_unlagsamples, "1", 0, "max samples to interpolate" ); -CVAR_DEFINE_AUTO( rcon_password, "", 0, "remote connect password" ); -CVAR_DEFINE_AUTO( rcon_enable, "1", 0, "enable rcon" ); +CVAR_DEFINE_AUTO( rcon_password, "", FCVAR_PROTECTED | FCVAR_PRIVILEGED, "remote connect password" ); +CVAR_DEFINE_AUTO( rcon_enable, "1", FCVAR_PROTECTED, "enable accepting remote commands on server" ); CVAR_DEFINE_AUTO( sv_filterban, "1", 0, "filter banned users" ); CVAR_DEFINE_AUTO( sv_cheats, "0", FCVAR_SERVER, "allow cheats on server" ); CVAR_DEFINE_AUTO( sv_instancedbaseline, "1", 0, "allow to use instanced baselines to saves network overhead" ); From a3ef6c955cdb582493baa03fd178d888fde43430 Mon Sep 17 00:00:00 2001 From: Alibek Omarov Date: Mon, 12 Dec 2022 08:13:24 +0300 Subject: [PATCH 273/490] engine: don't double register rcon_password cvar --- engine/client/cl_main.c | 6 ++---- engine/common/common.h | 1 + engine/server/server.h | 1 - 3 files changed, 3 insertions(+), 5 deletions(-) diff --git a/engine/client/cl_main.c b/engine/client/cl_main.c index 872de60a..f0463be5 100644 --- a/engine/client/cl_main.c +++ b/engine/client/cl_main.c @@ -40,7 +40,6 @@ CVAR_DEFINE_AUTO( cl_logofile, "lambda", FCVAR_ARCHIVE, "player logo name" ); CVAR_DEFINE_AUTO( cl_logocolor, "orange", FCVAR_ARCHIVE, "player logo color" ); CVAR_DEFINE_AUTO( cl_logoext, "bmp", FCVAR_ARCHIVE, "temporary cvar to tell engine which logo must be packed" ); CVAR_DEFINE_AUTO( cl_test_bandwidth, "1", FCVAR_ARCHIVE, "test network bandwith before connection" ); -convar_t *rcon_client_password; convar_t *rcon_address; convar_t *cl_timeout; convar_t *cl_nopred; @@ -1312,7 +1311,7 @@ void CL_Rcon_f( void ) string command; int i; - if( !COM_CheckString( rcon_client_password->string )) + if( !COM_CheckString( rcon_password.string )) { Con_Printf( "You must set 'rcon_password' before issuing an rcon command.\n" ); return; @@ -1327,7 +1326,7 @@ void CL_Rcon_f( void ) NET_Config( true, false ); // allow remote Q_strcat( message, "rcon " ); - Q_strcat( message, rcon_client_password->string ); + Q_strcat( message, rcon_password.string ); Q_strcat( message, " " ); for( i = 1; i < Cmd_Argc(); i++ ) @@ -2895,7 +2894,6 @@ void CL_InitLocal( void ) cl_charset = Cvar_Get( "cl_charset", "utf-8", FCVAR_ARCHIVE, "1-byte charset to use (iconv style)" ); hud_utf8 = Cvar_Get( "hud_utf8", "0", FCVAR_ARCHIVE, "Use utf-8 encoding for hud text" ); - rcon_client_password = Cvar_Get( "rcon_password", "", FCVAR_PRIVILEGED, "remote control client password" ); rcon_address = Cvar_Get( "rcon_address", "", FCVAR_PRIVILEGED, "remote control address" ); cl_trace_messages = Cvar_Get( "cl_trace_messages", "0", FCVAR_ARCHIVE|FCVAR_CHEAT, "enable message names tracing (good for developers)"); diff --git a/engine/common/common.h b/engine/common/common.h index 447042fe..c95d456b 100644 --- a/engine/common/common.h +++ b/engine/common/common.h @@ -165,6 +165,7 @@ extern convar_t *host_framerate; extern convar_t *host_maxfps; extern convar_t sys_timescale; extern convar_t cl_filterstuffcmd; +extern convar_t rcon_password; /* ============================================================== diff --git a/engine/server/server.h b/engine/server/server.h index a6dac467..9e90cc24 100644 --- a/engine/server/server.h +++ b/engine/server/server.h @@ -395,7 +395,6 @@ extern convar_t sv_unlag; extern convar_t sv_maxunlag; extern convar_t sv_unlagpush; extern convar_t sv_unlagsamples; -extern convar_t rcon_password; extern convar_t rcon_enable; extern convar_t sv_instancedbaseline; extern convar_t sv_background_freeze; From cb0f513bf0bd3198d657a37a5d6b5e8c0d0e9f51 Mon Sep 17 00:00:00 2001 From: Alibek Omarov Date: Mon, 12 Dec 2022 08:14:01 +0300 Subject: [PATCH 274/490] engine: common: allow cvar substituion in privileged mode only to prevent leaking sensitive data --- engine/common/cmd.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/engine/common/cmd.c b/engine/common/cmd.c index 9eeb1c68..02775d63 100644 --- a/engine/common/cmd.c +++ b/engine/common/cmd.c @@ -984,7 +984,7 @@ static void Cmd_ExecuteStringWithPrivilegeCheck( const char *text, qboolean isPr cmd_condlevel = 0; // cvar value substitution - if( CVAR_TO_BOOL( cmd_scripting )) + if( CVAR_TO_BOOL( cmd_scripting ) && isPrivileged ) { while( *text ) { From af7d6f6fa860d69c6b66f5ad8338499780ab1e81 Mon Sep 17 00:00:00 2001 From: Alibek Omarov Date: Mon, 12 Dec 2022 08:18:00 +0300 Subject: [PATCH 275/490] engine: common: no point to allow if and else commands in unprivileged mode since scripting is available only for privileged --- engine/common/cmd.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/engine/common/cmd.c b/engine/common/cmd.c index 02775d63..2a7c1a8c 100644 --- a/engine/common/cmd.c +++ b/engine/common/cmd.c @@ -1395,8 +1395,8 @@ void Cmd_Init( void ) #endif // XASH_DEDICATED Cmd_AddRestrictedCommand( "alias", Cmd_Alias_f, "create a script function. Without arguments show the list of all alias" ); Cmd_AddRestrictedCommand( "unalias", Cmd_UnAlias_f, "remove a script function" ); - Cmd_AddCommand( "if", Cmd_If_f, "compare and set condition bits" ); - Cmd_AddCommand( "else", Cmd_Else_f, "invert condition bit" ); + Cmd_AddRestrictedCommand( "if", Cmd_If_f, "compare and set condition bits" ); + Cmd_AddRestrictedCommand( "else", Cmd_Else_f, "invert condition bit" ); #if defined(XASH_HASHED_VARS) Cmd_AddCommand( "basecmd_stats", BaseCmd_Stats_f, "print info about basecmd usage" ); From 07afbd64d4a687cec8979cb8befcc0ca437e274e Mon Sep 17 00:00:00 2001 From: Alibek Omarov Date: Tue, 13 Dec 2022 10:54:03 +0300 Subject: [PATCH 276/490] engine: common: host: force set HOST_FRAME status for dedicated as it finished initializing --- engine/common/host.c | 18 +++++++++++------- 1 file changed, 11 insertions(+), 7 deletions(-) diff --git a/engine/common/host.c b/engine/common/host.c index 13a12e38..3de404a3 100644 --- a/engine/common/host.c +++ b/engine/common/host.c @@ -286,14 +286,14 @@ static int Host_CalcSleep( void ) // let the dedicated server some sleep return host_sleeptime->value; } - else if( host.status == HOST_NOFOCUS ) - { - if( SV_Active() && CL_IsInGame( )) - return host_sleeptime->value; // listenserver - return 20; // sleep 20 ms otherwise - } - else if( host.status == HOST_SLEEP ) + + switch( host.status ) { + case HOST_NOFOCUS: + if( SV_Active() && CL_IsInGame()) + return host_sleeptime->value; + // fallthrough + case HOST_SLEEP: return 20; } @@ -1229,6 +1229,10 @@ int EXPORT Host_Main( int argc, char **argv, const char *progname, int bChangeGa if( Host_IsDedicated( )) { + // in dedicated server input system can't set HOST_FRAME status + // so set it here as we're finished initializing + host.status = HOST_FRAME; + if( GameState->nextstate == STATE_RUNFRAME ) Con_Printf( "Type 'map ' to start game... (TAB-autocomplete is working too)\n" ); From cd813bbfbe96ad5c11f3e3de57d0f9a2fbae274c Mon Sep 17 00:00:00 2001 From: Alibek Omarov Date: Tue, 13 Dec 2022 11:12:08 +0300 Subject: [PATCH 277/490] engine: server: do not apply sound precache check for sentences, as they may start with 0 index --- engine/server/sv_game.c | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/engine/server/sv_game.c b/engine/server/sv_game.c index ffa37f5d..46e1b271 100644 --- a/engine/server/sv_game.c +++ b/engine/server/sv_game.c @@ -2091,12 +2091,12 @@ int SV_BuildSoundMsg( sizebuf_t *msg, edict_t *ent, int chan, const char *sample // precache_sound can be used twice: cache sounds when loading // and return sound index when server is active sound_idx = SV_SoundIndex( sample ); - } - if( !sound_idx ) - { - Con_Printf( S_ERROR "SV_StartSound: %s not precached (%d)\n", sample, sound_idx ); - return 0; + if( !sound_idx ) + { + Con_Printf( S_ERROR "SV_StartSound: %s not precached (%d)\n", sample, sound_idx ); + return 0; + } } spawn = FBitSet( flags, SND_RESTORE_POSITION ) ? false : true; From 754d55beef0b5d7fe1e2f19b8260fd888c2ec874 Mon Sep 17 00:00:00 2001 From: Alibek Omarov Date: Tue, 13 Dec 2022 11:34:42 +0300 Subject: [PATCH 278/490] engine: common: fix for HLBSP extended clipnodes hack when BSP2 support is enabled --- engine/common/mod_bmodel.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/engine/common/mod_bmodel.c b/engine/common/mod_bmodel.c index 294d0dd8..b931d6ac 100644 --- a/engine/common/mod_bmodel.c +++ b/engine/common/mod_bmodel.c @@ -254,7 +254,7 @@ static void Mod_LoadLump( const byte *in, mlumpinfo_t *info, mlumpstat_t *stat, else if( info->lumpnumber == LUMP_CLIPNODES && version != Q1BSP_VERSION ) { // never run this check for BSP29 because Arguire QBSP 'broken' clipnodes! - if(( l->filelen % info->entrysize ) || ( l->filelen / info->entrysize ) >= MAX_MAP_CLIPNODES ) + if(( l->filelen % info->entrysize ) || ( l->filelen / info->entrysize ) >= MAX_MAP_CLIPNODES_HLBSP ) { real_entrysize = info->entrysize32; SetBits( flags, LUMP_SILENT ); // shut up warning @@ -2578,7 +2578,7 @@ static void Mod_LoadClipnodes( dbspmodel_t *bmod ) bmod->clipnodes_out = out = (dclipnode32_t *)Mem_Malloc( loadmodel->mempool, bmod->numclipnodes * sizeof( *out )); - if(( bmod->version == QBSP2_VERSION ) || ( bmod->version == HLBSP_VERSION && bmod->numclipnodes >= MAX_MAP_CLIPNODES )) + if(( bmod->version == QBSP2_VERSION ) || ( bmod->version == HLBSP_VERSION && bmod->numclipnodes >= MAX_MAP_CLIPNODES_HLBSP )) { dclipnode32_t *in = bmod->clipnodes32; From f20fddee1c9cdd1bc613960f6caf999d71732610 Mon Sep 17 00:00:00 2001 From: Alibek Omarov Date: Thu, 15 Dec 2022 00:42:40 +0300 Subject: [PATCH 279/490] common: bspfile: add separate definitions for clipnodes limit for HLBSP and QBSP2 --- common/bspfile.h | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/common/bspfile.h b/common/bspfile.h index 1fa5721a..be6642ab 100644 --- a/common/bspfile.h +++ b/common/bspfile.h @@ -61,13 +61,16 @@ BRUSH MODELS #define LS_UNUSED 0xFE #define LS_NONE 0xFF +#define MAX_MAP_CLIPNODES_HLBSP 32767 +#define MAX_MAP_CLIPNODES_BSP2 524288 + // these limis not using by modelloader but only for displaying 'mapstats' correctly #ifdef SUPPORT_BSP2_FORMAT #define MAX_MAP_MODELS 2048 // embedded models #define MAX_MAP_ENTSTRING 0x200000 // 2 Mb should be enough #define MAX_MAP_PLANES 131072 // can be increased without problems #define MAX_MAP_NODES 262144 // can be increased without problems -#define MAX_MAP_CLIPNODES 524288 // can be increased without problems +#define MAX_MAP_CLIPNODES MAX_MAP_CLIPNODES_BSP2 // can be increased without problems #define MAX_MAP_LEAFS 131072 // CRITICAL STUFF to run ad_sepulcher!!! #define MAX_MAP_VERTS 524288 // can be increased without problems #define MAX_MAP_FACES 262144 // can be increased without problems @@ -78,7 +81,7 @@ BRUSH MODELS #define MAX_MAP_ENTSTRING 0x100000 // 1 Mb should be enough #define MAX_MAP_PLANES 65536 // can be increased without problems #define MAX_MAP_NODES 32767 // because negative shorts are leafs -#define MAX_MAP_CLIPNODES 32767 // because negative shorts are contents +#define MAX_MAP_CLIPNODES MAX_MAP_CLIPNODES_HLBSP // because negative shorts are contents #define MAX_MAP_LEAFS 32767 // signed short limit #define MAX_MAP_VERTS 65535 // unsigned short limit #define MAX_MAP_FACES 65535 // unsigned short limit From 48c17d08d93617009c7f4a1dcffe014ecf0536e6 Mon Sep 17 00:00:00 2001 From: Alibek Omarov Date: Thu, 15 Dec 2022 00:52:09 +0300 Subject: [PATCH 280/490] filesystem: dir: move searchpath initialization to dir.c, make all DIR functions static --- filesystem/dir.c | 60 ++++++++++++++++++++++++++++---- filesystem/filesystem.c | 40 +++++---------------- filesystem/filesystem_internal.h | 8 ++--- 3 files changed, 63 insertions(+), 45 deletions(-) diff --git a/filesystem/dir.c b/filesystem/dir.c index c93359d7..b10a15f7 100644 --- a/filesystem/dir.c +++ b/filesystem/dir.c @@ -27,14 +27,17 @@ GNU General Public License for more details. #include "xash3d_mathlib.h" #include "common/com_strings.h" -void FS_Close_DIR( searchpath_t *search ) {} +static void FS_Close_DIR( searchpath_t *search ) +{ -void FS_PrintInfo_DIR( searchpath_t *search, char *dst, size_t size ) +} + +static void FS_PrintInfo_DIR( searchpath_t *search, char *dst, size_t size ) { Q_strncpy( dst, search->filename, size ); } -int FS_FindFile_DIR( searchpath_t *search, const char *path ) +static int FS_FindFile_DIR( searchpath_t *search, const char *path ) { char netpath[MAX_SYSPATH]; @@ -46,7 +49,7 @@ int FS_FindFile_DIR( searchpath_t *search, const char *path ) return -1; } -void FS_Search_DIR( searchpath_t *search, stringlist_t *list, const char *pattern, int caseinsensitive ) +static void FS_Search_DIR( searchpath_t *search, stringlist_t *list, const char *pattern, int caseinsensitive ) { string netpath, temp; stringlist_t dirlist; @@ -95,7 +98,7 @@ void FS_Search_DIR( searchpath_t *search, stringlist_t *list, const char *patter Mem_Free( basepath ); } -int FS_FileTime_DIR( searchpath_t *search, const char *filename ) +static int FS_FileTime_DIR( searchpath_t *search, const char *filename ) { char path[MAX_SYSPATH]; @@ -103,10 +106,53 @@ int FS_FileTime_DIR( searchpath_t *search, const char *filename ) return FS_SysFileTime( path ); } -file_t *FS_OpenFile_DIR( searchpath_t *search, const char *filename, const char *mode, int pack_ind ) +static file_t *FS_OpenFile_DIR( searchpath_t *search, const char *filename, const char *mode, int pack_ind ) { char path[MAX_SYSPATH]; Q_snprintf( path, sizeof( path ), "%s%s", search->filename, filename ); return FS_SysOpen( path, mode ); -} \ No newline at end of file +} + +void FS_InitDirectorySearchpath( searchpath_t *search, const char *path, int flags ) +{ + memset( search, 0, sizeof( searchpath_t )); + + Q_strncpy( search->filename, path, sizeof( search->filename )); + search->type = SEARCHPATH_PLAIN; + search->flags = flags; + search->printinfo = FS_PrintInfo_DIR; + search->close = FS_Close_DIR; + search->openfile = FS_OpenFile_DIR; + search->filetime = FS_FileTime_DIR; + search->findfile = FS_FindFile_DIR; + search->search = FS_Search_DIR; +} + +qboolean FS_AddDir_Fullpath( const char *path, qboolean *already_loaded, int flags ) +{ + searchpath_t *search; + + for( search = fs_searchpaths; search; search = search->next ) + { + if( search->type == SEARCHPATH_PLAIN && !Q_stricmp( search->filename, path )) + { + if( already_loaded ) + *already_loaded = true; + return true; + } + } + + if( already_loaded ) + *already_loaded = false; + + search = (searchpath_t *)Mem_Calloc( fs_mempool, sizeof( searchpath_t )); + FS_InitDirectorySearchpath( search, path, flags ); + + search->next = fs_searchpaths; + fs_searchpaths = search; + + Con_Printf( "Adding directory: %s\n", path ); + + return true; +} diff --git a/filesystem/filesystem.c b/filesystem/filesystem.c index 08e54214..eef2e282 100644 --- a/filesystem/filesystem.c +++ b/filesystem/filesystem.c @@ -464,20 +464,7 @@ void FS_AddGameDirectory( const char *dir, uint flags ) // add the directory to the search path // (unpacked files have the priority over packed files) - search = (searchpath_t *)Mem_Calloc( fs_mempool, sizeof( searchpath_t )); - Q_strncpy( search->filename, dir, sizeof ( search->filename )); - search->next = fs_searchpaths; - search->type = SEARCHPATH_PLAIN; - search->flags = flags; - - search->printinfo = FS_PrintInfo_DIR; - search->close = FS_Close_DIR; - search->openfile = FS_OpenFile_DIR; - search->filetime = FS_FileTime_DIR; - search->findfile = FS_FindFile_DIR; - search->search = FS_Search_DIR; - - fs_searchpaths = search; + FS_AddDir_Fullpath( dir, NULL, flags ); } /* @@ -1155,7 +1142,7 @@ void FS_AddGameHierarchy( const char *dir, uint flags ) { if( !Q_strnicmp( FI.games[i]->gamefolder, dir, 64 )) { - Con_Reportf( "FS_AddGameHierarchy: %d %s %s\n", i, FI.games[i]->gamefolder, FI.games[i]->basedir ); + Con_Reportf( "FS_AddGameHierarchy: adding recursive basedir %s\n", FI.games[i]->basedir ); if( !FI.games[i]->added && Q_stricmp( FI.games[i]->gamefolder, FI.games[i]->basedir )) { FI.games[i]->added = true; @@ -1212,6 +1199,7 @@ void FS_Rescan( void ) FS_AddArchive_Fullpath( str, NULL, extrasFlags ); #endif + if( Q_stricmp( GI->basedir, GI->gamefolder )) FS_AddGameHierarchy( GI->basedir, 0 ); if( Q_stricmp( GI->basedir, GI->falldir ) && Q_stricmp( GI->gamefolder, GI->falldir )) @@ -1821,29 +1809,17 @@ searchpath_t *FS_FindFile( const char *name, int *index, qboolean gamedironly ) if( fs_ext_path ) { - char netpath[MAX_SYSPATH]; - - // clear searchpath - search = &fs_directpath; - memset( search, 0, sizeof( searchpath_t )); - - // root folder has a more priority than netpath - Q_strncpy( search->filename, fs_rootdir, sizeof( search->filename )); - Q_strcat( search->filename, PATH_SPLITTER ); - Q_snprintf( netpath, sizeof( netpath ), "%s%s", search->filename, name ); + char netpath[MAX_SYSPATH], dirpath[MAX_SYSPATH]; + Q_snprintf( dirpath, sizeof( dirpath ), "%s" PATH_SPLITTER, fs_rootdir ); + Q_snprintf( netpath, sizeof( netpath ), "%s%s", dirpath, name ); if( FS_SysFileExists( netpath, true )) { - search->printinfo = FS_PrintInfo_DIR; - search->close = FS_Close_DIR; - search->openfile = FS_OpenFile_DIR; - search->filetime = FS_FileTime_DIR; - search->findfile = FS_FindFile_DIR; - search->search = FS_Search_DIR; + FS_InitDirectorySearchpath( &fs_directpath, dirpath, 0 ); if( index != NULL ) *index = -1; - return search; + return &fs_directpath; } } diff --git a/filesystem/filesystem_internal.h b/filesystem/filesystem_internal.h index 01591f4d..f2fdecb8 100644 --- a/filesystem/filesystem_internal.h +++ b/filesystem/filesystem_internal.h @@ -223,12 +223,8 @@ qboolean FS_AddZip_Fullpath( const char *zipfile, qboolean *already_loaded, int // // dir.c // -void FS_PrintInfo_DIR( searchpath_t *search, char *dst, size_t size ); -void FS_Close_DIR( searchpath_t *search ); -file_t *FS_OpenFile_DIR( searchpath_t *search, const char *filename, const char *mode, int pack_ind ); -int FS_FileTime_DIR( searchpath_t *search, const char *filename ); -int FS_FindFile_DIR( searchpath_t *search, const char *path ); -void FS_Search_DIR( searchpath_t *search, stringlist_t *list, const char *pattern, int caseinsensitive ); +qboolean FS_AddDir_Fullpath( const char *path, qboolean *already_loaded, int flags ); +void FS_InitDirectorySearchpath( searchpath_t *search, const char *path, int flags ); #ifdef __cplusplus } From 08f834cd8295eb74ea58d65fe9a345f0c984bdef Mon Sep 17 00:00:00 2001 From: Alibek Omarov Date: Thu, 15 Dec 2022 00:59:52 +0300 Subject: [PATCH 281/490] filesystem: make all archive searchpath functions private --- filesystem/filesystem_internal.h | 17 -- filesystem/pak.c | 209 ++++++++++-------- filesystem/wad.c | 357 ++++++++++++++++--------------- filesystem/zip.c | 203 +++++++++++------- 4 files changed, 434 insertions(+), 352 deletions(-) diff --git a/filesystem/filesystem_internal.h b/filesystem/filesystem_internal.h index f2fdecb8..5c51919c 100644 --- a/filesystem/filesystem_internal.h +++ b/filesystem/filesystem_internal.h @@ -182,22 +182,11 @@ searchpath_t *FS_FindFile( const char *name, int *index, qboolean gamedironly ); // // pak.c // -int FS_FileTime_PAK( searchpath_t *search, const char *filename ); -int FS_FindFile_PAK( searchpath_t *search, const char *path ); -void FS_PrintInfo_PAK( searchpath_t *search, char *dst, size_t size ); -void FS_Close_PAK( searchpath_t *search ); -void FS_Search_PAK( searchpath_t *search, stringlist_t *list, const char *pattern, int caseinsensitive ); -file_t *FS_OpenFile_PAK( searchpath_t *search, const char *filename, const char *mode, int pack_ind ); qboolean FS_AddPak_Fullpath( const char *pakfile, qboolean *already_loaded, int flags ); // // wad.c // -int FS_FileTime_WAD( searchpath_t *search, const char *filename ); -int FS_FindFile_WAD( searchpath_t *search, const char *path ); -void FS_PrintInfo_WAD( searchpath_t *search, char *dst, size_t size ); -void FS_Close_WAD( searchpath_t *search ); -void FS_Search_WAD( searchpath_t *search, stringlist_t *list, const char *pattern, int caseinsensitive ); byte *FS_LoadWADFile( const char *path, fs_offset_t *sizeptr, qboolean gamedironly ); qboolean FS_AddWad_Fullpath( const char *wadfile, qboolean *already_loaded, int flags ); @@ -211,13 +200,7 @@ void FS_WatchFrame( void ); // // zip.c // -int FS_FileTime_ZIP( searchpath_t *search, const char *filename ); -int FS_FindFile_ZIP( searchpath_t *search, const char *path ); -void FS_PrintInfo_ZIP( searchpath_t *search, char *dst, size_t size ); -void FS_Close_ZIP( searchpath_t *search ); -void FS_Search_ZIP( searchpath_t *search, stringlist_t *list, const char *pattern, int caseinsensitive ); byte *FS_LoadZIPFile( const char *path, fs_offset_t *sizeptr, qboolean gamedironly ); -file_t *FS_OpenFile_ZIP( searchpath_t *search, const char *filename, const char *mode, int pack_ind ); qboolean FS_AddZip_Fullpath( const char *zipfile, qboolean *already_loaded, int flags ); // diff --git a/filesystem/pak.c b/filesystem/pak.c index 080fd975..966d3517 100644 --- a/filesystem/pak.c +++ b/filesystem/pak.c @@ -230,7 +230,7 @@ FS_OpenPackedFile Open a packed file using its package file descriptor =========== */ -file_t *FS_OpenFile_PAK( searchpath_t *search, const char *filename, const char *mode, int pack_ind ) +static file_t *FS_OpenFile_PAK( searchpath_t *search, const char *filename, const char *mode, int pack_ind ) { dpackfile_t *pfile; @@ -239,6 +239,125 @@ file_t *FS_OpenFile_PAK( searchpath_t *search, const char *filename, const char return FS_OpenHandle( search->pack->filename, search->pack->handle, pfile->filepos, pfile->filelen ); } +/* +=========== +FS_FindFile_PAK + +=========== +*/ +static int FS_FindFile_PAK( searchpath_t *search, const char *path ) +{ + int left, right, middle; + + // look for the file (binary search) + left = 0; + right = search->pack->numfiles - 1; + while( left <= right ) + { + int diff; + + middle = (left + right) / 2; + diff = Q_stricmp( search->pack->files[middle].name, path ); + + // Found it + if( !diff ) + { + return middle; + } + + // if we're too far in the list + if( diff > 0 ) + right = middle - 1; + else left = middle + 1; + } + + return -1; +} + +/* +=========== +FS_Search_PAK + +=========== +*/ +static void FS_Search_PAK( searchpath_t *search, stringlist_t *list, const char *pattern, int caseinsensitive ) +{ + string temp; + const char *slash, *backslash, *colon, *separator; + int j, i; + + for( i = 0; i < search->pack->numfiles; i++ ) + { + Q_strncpy( temp, search->pack->files[i].name, sizeof( temp )); + while( temp[0] ) + { + if( matchpattern( temp, pattern, true )) + { + for( j = 0; j < list->numstrings; j++ ) + { + if( !Q_strcmp( list->strings[j], temp )) + break; + } + + if( j == list->numstrings ) + stringlistappend( list, temp ); + } + + // strip off one path element at a time until empty + // this way directories are added to the listing if they match the pattern + slash = Q_strrchr( temp, '/' ); + backslash = Q_strrchr( temp, '\\' ); + colon = Q_strrchr( temp, ':' ); + separator = temp; + if( separator < slash ) + separator = slash; + if( separator < backslash ) + separator = backslash; + if( separator < colon ) + separator = colon; + *((char *)separator) = 0; + } + } +} + +/* +=========== +FS_FileTime_PAK + +=========== +*/ +static int FS_FileTime_PAK( searchpath_t *search, const char *filename ) +{ + return search->pack->filetime; +} + +/* +=========== +FS_PrintInfo_PAK + +=========== +*/ +static void FS_PrintInfo_PAK( searchpath_t *search, char *dst, size_t size ) +{ + Q_snprintf( dst, size, "%s (%i files)", search->pack->filename, search->pack->numfiles ); +} + +/* +=========== +FS_Close_PAK + +=========== +*/ +static void FS_Close_PAK( searchpath_t *search ) +{ + if( search->pack->files ) + Mem_Free( search->pack->files ); + if( search->pack->handle >= 0 ) + close( search->pack->handle ); + Mem_Free( search->pack ); +} + + /* ================ FS_AddPak_Fullpath @@ -315,91 +434,3 @@ qboolean FS_AddPak_Fullpath( const char *pakfile, qboolean *already_loaded, int return false; } } - -int FS_FindFile_PAK( searchpath_t *search, const char *path ) -{ - int left, right, middle; - - // look for the file (binary search) - left = 0; - right = search->pack->numfiles - 1; - while( left <= right ) - { - int diff; - - middle = (left + right) / 2; - diff = Q_stricmp( search->pack->files[middle].name, path ); - - // Found it - if( !diff ) - { - return middle; - } - - // if we're too far in the list - if( diff > 0 ) - right = middle - 1; - else left = middle + 1; - } - - return -1; -} - -void FS_Search_PAK( searchpath_t *search, stringlist_t *list, const char *pattern, int caseinsensitive ) -{ - string temp; - const char *slash, *backslash, *colon, *separator; - int j, i; - - for( i = 0; i < search->pack->numfiles; i++ ) - { - Q_strncpy( temp, search->pack->files[i].name, sizeof( temp )); - while( temp[0] ) - { - if( matchpattern( temp, pattern, true )) - { - for( j = 0; j < list->numstrings; j++ ) - { - if( !Q_strcmp( list->strings[j], temp )) - break; - } - - if( j == list->numstrings ) - stringlistappend( list, temp ); - } - - // strip off one path element at a time until empty - // this way directories are added to the listing if they match the pattern - slash = Q_strrchr( temp, '/' ); - backslash = Q_strrchr( temp, '\\' ); - colon = Q_strrchr( temp, ':' ); - separator = temp; - if( separator < slash ) - separator = slash; - if( separator < backslash ) - separator = backslash; - if( separator < colon ) - separator = colon; - *((char *)separator) = 0; - } - } -} - -int FS_FileTime_PAK( searchpath_t *search, const char *filename ) -{ - return search->pack->filetime; -} - -void FS_PrintInfo_PAK( searchpath_t *search, char *dst, size_t size ) -{ - Q_snprintf( dst, size, "%s (%i files)", search->pack->filename, search->pack->numfiles ); -} - -void FS_Close_PAK( searchpath_t *search ) -{ - if( search->pack->files ) - Mem_Free( search->pack->files ); - if( search->pack->handle >= 0 ) - close( search->pack->handle ); - Mem_Free( search->pack ); -} \ No newline at end of file diff --git a/filesystem/wad.c b/filesystem/wad.c index bd1d90e4..74f9118b 100644 --- a/filesystem/wad.c +++ b/filesystem/wad.c @@ -154,6 +154,48 @@ static const char *W_ExtFromType( signed char lumptype ) return ""; } +/* +=========== +W_FindLump + +Serach for already existed lump +=========== +*/ +static dlumpinfo_t *W_FindLump( wfile_t *wad, const char *name, const signed char matchtype ) +{ + int left, right; + + if( !wad || !wad->lumps || matchtype == TYP_NONE ) + return NULL; + + // look for the file (binary search) + left = 0; + right = wad->numlumps - 1; + + while( left <= right ) + { + int middle = (left + right) / 2; + int diff = Q_stricmp( wad->lumps[middle].name, name ); + + if( !diff ) + { + if(( matchtype == TYP_ANY ) || ( matchtype == wad->lumps[middle].type )) + return &wad->lumps[middle]; // found + else if( wad->lumps[middle].type < matchtype ) + diff = 1; + else if( wad->lumps[middle].type > matchtype ) + diff = -1; + else break; // not found + } + + // if we're too far in the list + if( diff > 0 ) right = middle - 1; + else left = middle + 1; + } + + return NULL; +} + /* ==================== W_AddFileToWad @@ -362,184 +404,35 @@ static wfile_t *W_Open( const char *filename, int *error ) return wad; } -/* -==================== -FS_AddWad_Fullpath -==================== -*/ -qboolean FS_AddWad_Fullpath( const char *wadfile, qboolean *already_loaded, int flags ) -{ - searchpath_t *search; - wfile_t *wad = NULL; - const char *ext = COM_FileExtension( wadfile ); - int errorcode = WAD_LOAD_COULDNT_OPEN; - - for( search = fs_searchpaths; search; search = search->next ) - { - if( search->type == SEARCHPATH_WAD && !Q_stricmp( search->wad->filename, wadfile )) - { - if( already_loaded ) *already_loaded = true; - return true; // already loaded - } - } - - if( already_loaded ) - *already_loaded = false; - - if( !Q_stricmp( ext, "wad" )) - wad = W_Open( wadfile, &errorcode ); - - if( wad ) - { - search = (searchpath_t *)Mem_Calloc( fs_mempool, sizeof( searchpath_t )); - search->wad = wad; - search->type = SEARCHPATH_WAD; - search->next = fs_searchpaths; - search->flags |= flags; - - search->printinfo = FS_PrintInfo_WAD; - search->close = FS_Close_WAD; - search->openfile = FS_OpenFile_WAD; - search->filetime = FS_FileTime_WAD; - search->findfile = FS_FindFile_WAD; - search->search = FS_Search_WAD; - - fs_searchpaths = search; - - Con_Reportf( "Adding wadfile: %s (%i files)\n", wadfile, wad->numlumps ); - return true; - } - else - { - if( errorcode != WAD_LOAD_NO_FILES ) - Con_Reportf( S_ERROR "FS_AddWad_Fullpath: unable to load wad \"%s\"\n", wadfile ); - return false; - } -} - -/* -============================================================================= - -WADSYSTEM PRIVATE ROUTINES - -============================================================================= -*/ - /* =========== -W_FindLump +FS_FileTime_WAD -Serach for already existed lump =========== */ -static dlumpinfo_t *W_FindLump( wfile_t *wad, const char *name, const signed char matchtype ) -{ - int left, right; - - if( !wad || !wad->lumps || matchtype == TYP_NONE ) - return NULL; - - // look for the file (binary search) - left = 0; - right = wad->numlumps - 1; - - while( left <= right ) - { - int middle = (left + right) / 2; - int diff = Q_stricmp( wad->lumps[middle].name, name ); - - if( !diff ) - { - if(( matchtype == TYP_ANY ) || ( matchtype == wad->lumps[middle].type )) - return &wad->lumps[middle]; // found - else if( wad->lumps[middle].type < matchtype ) - diff = 1; - else if( wad->lumps[middle].type > matchtype ) - diff = -1; - else break; // not found - } - - // if we're too far in the list - if( diff > 0 ) right = middle - 1; - else left = middle + 1; - } - - return NULL; -} - -/* -=========== -W_ReadLump - -reading lump into temp buffer -=========== -*/ -static byte *W_ReadLump( wfile_t *wad, dlumpinfo_t *lump, fs_offset_t *lumpsizeptr ) -{ - size_t oldpos, size = 0; - byte *buf; - - // assume error - if( lumpsizeptr ) *lumpsizeptr = 0; - - // no wads loaded - if( !wad || !lump ) return NULL; - - oldpos = FS_Tell( wad->handle ); // don't forget restore original position - - if( FS_Seek( wad->handle, lump->filepos, SEEK_SET ) == -1 ) - { - Con_Reportf( S_ERROR "W_ReadLump: %s is corrupted\n", lump->name ); - FS_Seek( wad->handle, oldpos, SEEK_SET ); - return NULL; - } - - buf = (byte *)Mem_Malloc( wad->mempool, lump->disksize ); - size = FS_Read( wad->handle, buf, lump->disksize ); - - if( size < lump->disksize ) - { - Con_Reportf( S_WARN "W_ReadLump: %s is probably corrupted\n", lump->name ); - FS_Seek( wad->handle, oldpos, SEEK_SET ); - Mem_Free( buf ); - return NULL; - } - - if( lumpsizeptr ) *lumpsizeptr = lump->disksize; - FS_Seek( wad->handle, oldpos, SEEK_SET ); - - return buf; -} - -/* -=========== -FS_LoadWADFile - -loading lump into the tmp buffer -=========== -*/ -byte *FS_LoadWADFile( const char *path, fs_offset_t *lumpsizeptr, qboolean gamedironly ) -{ - searchpath_t *search; - int index; - - search = FS_FindFile( path, &index, gamedironly ); - if( search && search->type == SEARCHPATH_WAD ) - return W_ReadLump( search->wad, &search->wad->lumps[index], lumpsizeptr ); - return NULL; -} - -int FS_FileTime_WAD( searchpath_t *search, const char *filename ) +static int FS_FileTime_WAD( searchpath_t *search, const char *filename ) { return search->wad->filetime; } -void FS_PrintInfo_WAD( searchpath_t *search, char *dst, size_t size ) +/* +=========== +FS_PrintInfo_WAD + +=========== +*/ +static void FS_PrintInfo_WAD( searchpath_t *search, char *dst, size_t size ) { Q_snprintf( dst, size, "%s (%i files)", search->wad->filename, search->wad->numlumps ); } -int FS_FindFile_WAD( searchpath_t *search, const char *path ) +/* +=========== +FS_FindFile_WAD + +=========== +*/ +static int FS_FindFile_WAD( searchpath_t *search, const char *path ) { dlumpinfo_t *lump; signed char type = W_TypeFromExt( path ); @@ -582,10 +475,15 @@ int FS_FindFile_WAD( searchpath_t *search, const char *path ) } return -1; - } -void FS_Search_WAD( searchpath_t *search, stringlist_t *list, const char *pattern, int caseinsensitive ) +/* +=========== +FS_Search_WAD + +=========== +*/ +static void FS_Search_WAD( searchpath_t *search, stringlist_t *list, const char *pattern, int caseinsensitive ) { string wadpattern, wadname, temp2; signed char type = W_TypeFromExt( pattern ); @@ -662,3 +560,128 @@ void FS_Search_WAD( searchpath_t *search, stringlist_t *list, const char *patter } } } + +/* +==================== +FS_AddWad_Fullpath +==================== +*/ +qboolean FS_AddWad_Fullpath( const char *wadfile, qboolean *already_loaded, int flags ) +{ + searchpath_t *search; + wfile_t *wad = NULL; + const char *ext = COM_FileExtension( wadfile ); + int errorcode = WAD_LOAD_COULDNT_OPEN; + + for( search = fs_searchpaths; search; search = search->next ) + { + if( search->type == SEARCHPATH_WAD && !Q_stricmp( search->wad->filename, wadfile )) + { + if( already_loaded ) *already_loaded = true; + return true; // already loaded + } + } + + if( already_loaded ) + *already_loaded = false; + + if( !Q_stricmp( ext, "wad" )) + wad = W_Open( wadfile, &errorcode ); + + if( wad ) + { + search = (searchpath_t *)Mem_Calloc( fs_mempool, sizeof( searchpath_t )); + search->wad = wad; + search->type = SEARCHPATH_WAD; + search->next = fs_searchpaths; + search->flags |= flags; + + search->printinfo = FS_PrintInfo_WAD; + search->close = FS_Close_WAD; + search->openfile = FS_OpenFile_WAD; + search->filetime = FS_FileTime_WAD; + search->findfile = FS_FindFile_WAD; + search->search = FS_Search_WAD; + + fs_searchpaths = search; + + Con_Reportf( "Adding wadfile: %s (%i files)\n", wadfile, wad->numlumps ); + return true; + } + else + { + if( errorcode != WAD_LOAD_NO_FILES ) + Con_Reportf( S_ERROR "FS_AddWad_Fullpath: unable to load wad \"%s\"\n", wadfile ); + return false; + } +} + +/* +============================================================================= + +WADSYSTEM PRIVATE ROUTINES + +============================================================================= +*/ + +/* +=========== +W_ReadLump + +reading lump into temp buffer +=========== +*/ +static byte *W_ReadLump( wfile_t *wad, dlumpinfo_t *lump, fs_offset_t *lumpsizeptr ) +{ + size_t oldpos, size = 0; + byte *buf; + + // assume error + if( lumpsizeptr ) *lumpsizeptr = 0; + + // no wads loaded + if( !wad || !lump ) return NULL; + + oldpos = FS_Tell( wad->handle ); // don't forget restore original position + + if( FS_Seek( wad->handle, lump->filepos, SEEK_SET ) == -1 ) + { + Con_Reportf( S_ERROR "W_ReadLump: %s is corrupted\n", lump->name ); + FS_Seek( wad->handle, oldpos, SEEK_SET ); + return NULL; + } + + buf = (byte *)Mem_Malloc( wad->mempool, lump->disksize ); + size = FS_Read( wad->handle, buf, lump->disksize ); + + if( size < lump->disksize ) + { + Con_Reportf( S_WARN "W_ReadLump: %s is probably corrupted\n", lump->name ); + FS_Seek( wad->handle, oldpos, SEEK_SET ); + Mem_Free( buf ); + return NULL; + } + + if( lumpsizeptr ) *lumpsizeptr = lump->disksize; + FS_Seek( wad->handle, oldpos, SEEK_SET ); + + return buf; +} + +/* +=========== +FS_LoadWADFile + +loading lump into the tmp buffer +=========== +*/ +byte *FS_LoadWADFile( const char *path, fs_offset_t *lumpsizeptr, qboolean gamedironly ) +{ + searchpath_t *search; + int index; + + search = FS_FindFile( path, &index, gamedironly ); + if( search && search->type == SEARCHPATH_WAD ) + return W_ReadLump( search->wad, &search->wad->lumps[index], lumpsizeptr ); + return NULL; +} diff --git a/filesystem/zip.c b/filesystem/zip.c index cb40160f..4b696347 100644 --- a/filesystem/zip.c +++ b/filesystem/zip.c @@ -146,7 +146,12 @@ static void FS_EnsureOpenZip( zip_t *zip ) static void FS_EnsureOpenZip( zip_t *zip ) {} #endif -void FS_CloseZIP( zip_t *zip ) +/* +============ +FS_CloseZIP +============ +*/ +static void FS_CloseZIP( zip_t *zip ) { if( zip->files ) Mem_Free( zip->files ); @@ -159,7 +164,12 @@ void FS_CloseZIP( zip_t *zip ) Mem_Free( zip ); } -void FS_Close_ZIP( searchpath_t *search ) +/* +============ +FS_Close_ZIP +============ +*/ +static void FS_Close_ZIP( searchpath_t *search ) { FS_CloseZIP( search->zip ); } @@ -412,6 +422,12 @@ file_t *FS_OpenFile_ZIP( searchpath_t *search, const char *filename, const char return FS_OpenHandle( search->zip->filename, search->zip->handle, pfile->offset, pfile->size ); } +/* +=========== +FS_LoadZIPFile + +=========== +*/ byte *FS_LoadZIPFile( const char *path, fs_offset_t *sizeptr, qboolean gamedironly ) { searchpath_t *search; @@ -551,7 +567,113 @@ byte *FS_LoadZIPFile( const char *path, fs_offset_t *sizeptr, qboolean gamediron return NULL; } +/* +=========== +FS_FileTime_ZIP +=========== +*/ +int FS_FileTime_ZIP( searchpath_t *search, const char *filename ) +{ + return search->zip->filetime; +} + +/* +=========== +FS_PrintInfo_ZIP + +=========== +*/ +void FS_PrintInfo_ZIP( searchpath_t *search, char *dst, size_t size ) +{ + Q_snprintf( dst, size, "%s (%i files)", search->zip->filename, search->zip->numfiles ); +} + +/* +=========== +FS_FindFile_ZIP + +=========== +*/ +int FS_FindFile_ZIP( searchpath_t *search, const char *path ) +{ + int left, right, middle; + + // look for the file (binary search) + left = 0; + right = search->zip->numfiles - 1; + while( left <= right ) + { + int diff; + + middle = (left + right) / 2; + diff = Q_stricmp( search->zip->files[middle].name, path ); + + // Found it + if( !diff ) + return middle; + + // if we're too far in the list + if( diff > 0 ) + right = middle - 1; + else left = middle + 1; + } + + return -1; +} + +/* +=========== +FS_Search_ZIP + +=========== +*/ +void FS_Search_ZIP( searchpath_t *search, stringlist_t *list, const char *pattern, int caseinsensitive ) +{ + string temp; + const char *slash, *backslash, *colon, *separator; + int j, i; + + for( i = 0; i < search->zip->numfiles; i++ ) + { + Q_strncpy( temp, search->zip->files[i].name, sizeof( temp )); + while( temp[0] ) + { + if( matchpattern( temp, pattern, true )) + { + for( j = 0; j < list->numstrings; j++ ) + { + if( !Q_strcmp( list->strings[j], temp )) + break; + } + + if( j == list->numstrings ) + stringlistappend( list, temp ); + } + + // strip off one path element at a time until empty + // this way directories are added to the listing if they match the pattern + slash = Q_strrchr( temp, '/' ); + backslash = Q_strrchr( temp, '\\' ); + colon = Q_strrchr( temp, ':' ); + separator = temp; + if( separator < slash ) + separator = slash; + if( separator < backslash ) + separator = backslash; + if( separator < colon ) + separator = colon; + *((char *)separator) = 0; + } + } +} + +/* +=========== +FS_AddZip_Fullpath + +=========== +*/ qboolean FS_AddZip_Fullpath( const char *zipfile, qboolean *already_loaded, int flags ) { searchpath_t *search; @@ -614,80 +736,3 @@ qboolean FS_AddZip_Fullpath( const char *zipfile, qboolean *already_loaded, int } } -int FS_FileTime_ZIP( searchpath_t *search, const char *filename ) -{ - return search->zip->filetime; -} - -void FS_PrintInfo_ZIP( searchpath_t *search, char *dst, size_t size ) -{ - Q_snprintf( dst, size, "%s (%i files)", search->zip->filename, search->zip->numfiles ); -} - -int FS_FindFile_ZIP( searchpath_t *search, const char *path ) -{ - int left, right, middle; - - // look for the file (binary search) - left = 0; - right = search->zip->numfiles - 1; - while( left <= right ) - { - int diff; - - middle = (left + right) / 2; - diff = Q_stricmp( search->zip->files[middle].name, path ); - - // Found it - if( !diff ) - return middle; - - // if we're too far in the list - if( diff > 0 ) - right = middle - 1; - else left = middle + 1; - } - - return -1; -} - -void FS_Search_ZIP( searchpath_t *search, stringlist_t *list, const char *pattern, int caseinsensitive ) -{ - string temp; - const char *slash, *backslash, *colon, *separator; - int j, i; - - for( i = 0; i < search->zip->numfiles; i++ ) - { - Q_strncpy( temp, search->zip->files[i].name, sizeof( temp )); - while( temp[0] ) - { - if( matchpattern( temp, pattern, true )) - { - for( j = 0; j < list->numstrings; j++ ) - { - if( !Q_strcmp( list->strings[j], temp )) - break; - } - - if( j == list->numstrings ) - stringlistappend( list, temp ); - } - - // strip off one path element at a time until empty - // this way directories are added to the listing if they match the pattern - slash = Q_strrchr( temp, '/' ); - backslash = Q_strrchr( temp, '\\' ); - colon = Q_strrchr( temp, ':' ); - separator = temp; - if( separator < slash ) - separator = slash; - if( separator < backslash ) - separator = backslash; - if( separator < colon ) - separator = colon; - *((char *)separator) = 0; - } - } -} - From f1ec6128196aef6753f7e1bfb8d7b93677269fd4 Mon Sep 17 00:00:00 2001 From: Alibek Omarov Date: Thu, 15 Dec 2022 01:06:20 +0300 Subject: [PATCH 282/490] filesystem: hungry --- filesystem/dir.c | 12 ++++++------ filesystem/filesystem.c | 12 ++++++------ filesystem/filesystem_internal.h | 12 ++++++------ filesystem/pak.c | 12 ++++++------ filesystem/wad.c | 12 ++++++------ filesystem/zip.c | 12 ++++++------ 6 files changed, 36 insertions(+), 36 deletions(-) diff --git a/filesystem/dir.c b/filesystem/dir.c index b10a15f7..9fa03be0 100644 --- a/filesystem/dir.c +++ b/filesystem/dir.c @@ -121,12 +121,12 @@ void FS_InitDirectorySearchpath( searchpath_t *search, const char *path, int fla Q_strncpy( search->filename, path, sizeof( search->filename )); search->type = SEARCHPATH_PLAIN; search->flags = flags; - search->printinfo = FS_PrintInfo_DIR; - search->close = FS_Close_DIR; - search->openfile = FS_OpenFile_DIR; - search->filetime = FS_FileTime_DIR; - search->findfile = FS_FindFile_DIR; - search->search = FS_Search_DIR; + search->pfnPrintInfo = FS_PrintInfo_DIR; + search->pfnClose = FS_Close_DIR; + search->pfnOpenFile = FS_OpenFile_DIR; + search->pfnFileTime = FS_FileTime_DIR; + search->pfnFindFile = FS_FindFile_DIR; + search->pfnSearch = FS_Search_DIR; } qboolean FS_AddDir_Fullpath( const char *path, qboolean *already_loaded, int flags ) diff --git a/filesystem/filesystem.c b/filesystem/filesystem.c index eef2e282..4e8ceb23 100644 --- a/filesystem/filesystem.c +++ b/filesystem/filesystem.c @@ -489,7 +489,7 @@ void FS_ClearSearchPath( void ) } else fs_searchpaths = search->next; - search->close( search ); + search->pfnClose( search ); Mem_Free( search ); } @@ -1526,7 +1526,7 @@ void FS_Path_f( void ) { string info; - s->printinfo( s, info, sizeof(info) ); + s->pfnPrintInfo( s, info, sizeof(info) ); Con_Printf( "%s", info ); @@ -1799,7 +1799,7 @@ searchpath_t *FS_FindFile( const char *name, int *index, qboolean gamedironly ) if( gamedironly & !FBitSet( search->flags, FS_GAMEDIRONLY_SEARCH_FLAGS )) continue; - pack_ind = search->findfile( search, name ); + pack_ind = search->pfnFindFile( search, name ); if( pack_ind >= 0 ) { if( index ) *index = pack_ind; @@ -1848,7 +1848,7 @@ file_t *FS_OpenReadFile( const char *filename, const char *mode, qboolean gamedi if( search == NULL ) return NULL; - return search->openfile( search, filename, mode, pack_ind ); + return search->pfnOpenFile( search, filename, mode, pack_ind ); } /* @@ -2532,7 +2532,7 @@ int FS_FileTime( const char *filename, qboolean gamedironly ) search = FS_FindFile( filename, &pack_ind, gamedironly ); if( !search ) return -1; // doesn't exist - return search->filetime( search, filename ); + return search->pfnFileTime( search, filename ); } /* @@ -2642,7 +2642,7 @@ search_t *FS_Search( const char *pattern, int caseinsensitive, int gamedironly ) if( gamedironly && !FBitSet( searchpath->flags, FS_GAMEDIRONLY_SEARCH_FLAGS )) continue; - searchpath->search( searchpath, &resultlist, pattern, caseinsensitive ); + searchpath->pfnSearch( searchpath, &resultlist, pattern, caseinsensitive ); } if( resultlist.numstrings ) diff --git a/filesystem/filesystem_internal.h b/filesystem/filesystem_internal.h index 5c51919c..b2473e4a 100644 --- a/filesystem/filesystem_internal.h +++ b/filesystem/filesystem_internal.h @@ -79,12 +79,12 @@ typedef struct searchpath_s struct searchpath_s *next; - void ( *printinfo )( struct searchpath_s *search, char *dst, size_t size ); - void ( *close )( struct searchpath_s *search ); - file_t *( *openfile )( struct searchpath_s *search, const char *filename, const char *mode, int pack_ind ); - int ( *filetime )( struct searchpath_s *search, const char *filename ); - int ( *findfile )( struct searchpath_s *search, const char *path ); - void ( *search )( struct searchpath_s *search, stringlist_t *list, const char *pattern, int caseinsensitive ); + void (*pfnPrintInfo)( struct searchpath_s *search, char *dst, size_t size ); + void (*pfnClose)( struct searchpath_s *search ); + file_t *(*pfnOpenFile)( struct searchpath_s *search, const char *filename, const char *mode, int pack_ind ); + int (*pfnFileTime)( struct searchpath_s *search, const char *filename ); + int (*pfnFindFile)( struct searchpath_s *search, const char *path ); + void (*pfnSearch)( struct searchpath_s *search, stringlist_t *list, const char *pattern, int caseinsensitive ); } searchpath_t; extern fs_globals_t FI; diff --git a/filesystem/pak.c b/filesystem/pak.c index 966d3517..f98cffae 100644 --- a/filesystem/pak.c +++ b/filesystem/pak.c @@ -404,12 +404,12 @@ qboolean FS_AddPak_Fullpath( const char *pakfile, qboolean *already_loaded, int search->next = fs_searchpaths; search->flags |= flags; - search->printinfo = FS_PrintInfo_PAK; - search->close = FS_Close_PAK; - search->openfile = FS_OpenFile_PAK; - search->filetime = FS_FileTime_PAK; - search->findfile = FS_FindFile_PAK; - search->search = FS_Search_PAK; + search->pfnPrintInfo = FS_PrintInfo_PAK; + search->pfnClose = FS_Close_PAK; + search->pfnOpenFile = FS_OpenFile_PAK; + search->pfnFileTime = FS_FileTime_PAK; + search->pfnFindFile = FS_FindFile_PAK; + search->pfnSearch = FS_Search_PAK; fs_searchpaths = search; diff --git a/filesystem/wad.c b/filesystem/wad.c index 74f9118b..6408486b 100644 --- a/filesystem/wad.c +++ b/filesystem/wad.c @@ -596,12 +596,12 @@ qboolean FS_AddWad_Fullpath( const char *wadfile, qboolean *already_loaded, int search->next = fs_searchpaths; search->flags |= flags; - search->printinfo = FS_PrintInfo_WAD; - search->close = FS_Close_WAD; - search->openfile = FS_OpenFile_WAD; - search->filetime = FS_FileTime_WAD; - search->findfile = FS_FindFile_WAD; - search->search = FS_Search_WAD; + search->pfnPrintInfo = FS_PrintInfo_WAD; + search->pfnClose = FS_Close_WAD; + search->pfnOpenFile = FS_OpenFile_WAD; + search->pfnFileTime = FS_FileTime_WAD; + search->pfnFindFile = FS_FindFile_WAD; + search->pfnSearch = FS_Search_WAD; fs_searchpaths = search; diff --git a/filesystem/zip.c b/filesystem/zip.c index 4b696347..5d3cc2ad 100644 --- a/filesystem/zip.c +++ b/filesystem/zip.c @@ -706,12 +706,12 @@ qboolean FS_AddZip_Fullpath( const char *zipfile, qboolean *already_loaded, int search->next = fs_searchpaths; search->flags |= flags; - search->printinfo = FS_PrintInfo_ZIP; - search->close = FS_Close_ZIP; - search->openfile = FS_OpenFile_ZIP; - search->filetime = FS_FileTime_ZIP; - search->findfile = FS_FindFile_ZIP; - search->search = FS_Search_ZIP; + search->pfnPrintInfo = FS_PrintInfo_ZIP; + search->pfnClose = FS_Close_ZIP; + search->pfnOpenFile = FS_OpenFile_ZIP; + search->pfnFileTime = FS_FileTime_ZIP; + search->pfnFindFile = FS_FindFile_ZIP; + search->pfnSearch = FS_Search_ZIP; fs_searchpaths = search; From 9397301a732e020fe5def609c6032852c69ed170 Mon Sep 17 00:00:00 2001 From: Alibek Omarov Date: Thu, 15 Dec 2022 04:01:30 +0300 Subject: [PATCH 283/490] filesystem: remove excessive filename field from archive structs, use common in searchpath_t. Small optimizations for PAK --- filesystem/pak.c | 78 ++++++++++++++---------------------------------- filesystem/wad.c | 31 +++++++++---------- filesystem/zip.c | 11 ++++--- 3 files changed, 41 insertions(+), 79 deletions(-) diff --git a/filesystem/pak.c b/filesystem/pak.c index f98cffae..bb1721c8 100644 --- a/filesystem/pak.c +++ b/filesystem/pak.c @@ -65,54 +65,23 @@ typedef struct struct pack_s { - string filename; int handle; int numfiles; time_t filetime; // common for all packed files - dpackfile_t *files; + dpackfile_t files[1]; // flexible }; /* ==================== -FS_AddFileToPack +FS_SortPak -Add a file to the list of files contained into a package ==================== */ -static dpackfile_t *FS_AddFileToPack( const char *name, pack_t *pack, fs_offset_t offset, fs_offset_t size ) +static int FS_SortPak( const void *_a, const void *_b ) { - int left, right, middle; - dpackfile_t *pfile; + const dpackfile_t *a = _a, *b = _b; - // look for the slot we should put that file into (binary search) - left = 0; - right = pack->numfiles - 1; - - while( left <= right ) - { - int diff; - - middle = (left + right) / 2; - diff = Q_stricmp( pack->files[middle].name, name ); - - // If we found the file, there's a problem - if( !diff ) Con_Reportf( S_WARN "package %s contains the file %s several times\n", pack->filename, name ); - - // If we're too far in the list - if( diff > 0 ) right = middle - 1; - else left = middle + 1; - } - - // We have to move the right of the list by one slot to free the one we need - pfile = &pack->files[left]; - memmove( pfile + 1, pfile, (pack->numfiles - left) * sizeof( *pfile )); - pack->numfiles++; - - Q_strncpy( pfile->name, name, sizeof( pfile->name )); - pfile->filepos = offset; - pfile->filelen = size; - - return pfile; + return Q_stricmp( a->name, b->name ); } /* @@ -129,9 +98,8 @@ static pack_t *FS_LoadPackPAK( const char *packfile, int *error ) { dpackheader_t header; int packhandle; - int i, numpackfiles; + int numpackfiles; pack_t *pack; - dpackfile_t *info; fs_size_t c; packhandle = open( packfile, O_RDONLY|O_BINARY ); @@ -188,28 +156,25 @@ static pack_t *FS_LoadPackPAK( const char *packfile, int *error ) return NULL; } - info = (dpackfile_t *)Mem_Malloc( fs_mempool, sizeof( *info ) * numpackfiles ); + pack = (pack_t *)Mem_Calloc( fs_mempool, sizeof( pack_t ) + sizeof( dpackfile_t ) * ( numpackfiles - 1 )); lseek( packhandle, header.dirofs, SEEK_SET ); - if( header.dirlen != read( packhandle, (void *)info, header.dirlen )) + if( header.dirlen != read( packhandle, (void *)pack->files, header.dirlen )) { Con_Reportf( "%s is an incomplete PAK, not loading\n", packfile ); - if( error ) *error = PAK_LOAD_CORRUPTED; + if( error ) + *error = PAK_LOAD_CORRUPTED; close( packhandle ); - Mem_Free( info ); + Mem_Free( pack ); return NULL; } - pack = (pack_t *)Mem_Calloc( fs_mempool, sizeof( pack_t )); - Q_strncpy( pack->filename, packfile, sizeof( pack->filename )); - pack->files = (dpackfile_t *)Mem_Calloc( fs_mempool, numpackfiles * sizeof( dpackfile_t )); + // TODO: validate directory? + pack->filetime = FS_SysFileTime( packfile ); pack->handle = packhandle; - pack->numfiles = 0; - - // parse the directory - for( i = 0; i < numpackfiles; i++ ) - FS_AddFileToPack( info[i].name, pack, info[i].filepos, info[i].filelen ); + pack->numfiles = numpackfiles; + qsort( pack->files, pack->numfiles, sizeof( pack->files[0] ), FS_SortPak ); #ifdef XASH_REDUCE_FD // will reopen when needed @@ -217,8 +182,8 @@ static pack_t *FS_LoadPackPAK( const char *packfile, int *error ) pack->handle = -1; #endif - if( error ) *error = PAK_LOAD_OK; - Mem_Free( info ); + if( error ) + *error = PAK_LOAD_OK; return pack; } @@ -236,7 +201,7 @@ static file_t *FS_OpenFile_PAK( searchpath_t *search, const char *filename, cons pfile = &search->pack->files[pack_ind]; - return FS_OpenHandle( search->pack->filename, search->pack->handle, pfile->filepos, pfile->filelen ); + return FS_OpenHandle( search->filename, search->pack->handle, pfile->filepos, pfile->filelen ); } /* @@ -339,7 +304,7 @@ FS_PrintInfo_PAK */ static void FS_PrintInfo_PAK( searchpath_t *search, char *dst, size_t size ) { - Q_snprintf( dst, size, "%s (%i files)", search->pack->filename, search->pack->numfiles ); + Q_snprintf( dst, size, "%s (%i files)", search->filename, search->pack->numfiles ); } /* @@ -381,7 +346,7 @@ qboolean FS_AddPak_Fullpath( const char *pakfile, qboolean *already_loaded, int for( search = fs_searchpaths; search; search = search->next ) { - if( search->type == SEARCHPATH_PAK && !Q_stricmp( search->pack->filename, pakfile )) + if( search->type == SEARCHPATH_PAK && !Q_stricmp( search->filename, pakfile )) { if( already_loaded ) *already_loaded = true; return true; // already loaded @@ -399,10 +364,11 @@ qboolean FS_AddPak_Fullpath( const char *pakfile, qboolean *already_loaded, int string fullpath; search = (searchpath_t *)Mem_Calloc( fs_mempool, sizeof( searchpath_t )); + Q_strncpy( search->filename, pakfile, sizeof( search->filename )); search->pack = pak; search->type = SEARCHPATH_PAK; search->next = fs_searchpaths; - search->flags |= flags; + search->flags = flags; search->pfnPrintInfo = FS_PrintInfo_PAK; search->pfnClose = FS_Close_PAK; diff --git a/filesystem/wad.c b/filesystem/wad.c index 6408486b..6b94610d 100644 --- a/filesystem/wad.c +++ b/filesystem/wad.c @@ -71,7 +71,6 @@ typedef struct struct wfile_s { - string filename; int infotableofs; int numlumps; poolhandle_t mempool; // W_ReadLump temp buffers @@ -204,7 +203,7 @@ Add a file to the list of files contained into a package and sort LAT in alpha-bethical order ==================== */ -static dlumpinfo_t *W_AddFileToWad( const char *name, wfile_t *wad, dlumpinfo_t *newlump ) +static dlumpinfo_t *W_AddFileToWad( const char *wadfile, const char *name, wfile_t *wad, dlumpinfo_t *newlump ) { int left, right; dlumpinfo_t *plump; @@ -224,7 +223,7 @@ static dlumpinfo_t *W_AddFileToWad( const char *name, wfile_t *wad, dlumpinfo_t diff = 1; else if( wad->lumps[middle].type > newlump->type ) diff = -1; - else Con_Reportf( S_WARN "Wad %s contains the file %s several times\n", wad->filename, name ); + else Con_Reportf( S_WARN "Wad %s contains the file %s several times\n", wadfile, name ); } // If we're too far in the list @@ -313,7 +312,6 @@ static wfile_t *W_Open( const char *filename, int *error ) } // copy wad name - Q_strncpy( wad->filename, filename, sizeof( wad->filename )); wad->filetime = FS_SysFileTime( filename ); wad->mempool = Mem_AllocPool( filename ); @@ -366,7 +364,7 @@ static wfile_t *W_Open( const char *filename, int *error ) if( FS_Read( wad->handle, srclumps, lat_size ) != lat_size ) { - Con_Reportf( S_ERROR "W_ReadLumpTable: %s has corrupted lump allocation table\n", wad->filename ); + Con_Reportf( S_ERROR "W_ReadLumpTable: %s has corrupted lump allocation table\n", filename ); if( error ) *error = WAD_LOAD_CORRUPTED; Mem_Free( srclumps ); FS_CloseWAD( wad ); @@ -394,7 +392,7 @@ static wfile_t *W_Open( const char *filename, int *error ) if( srclumps[i].type == 68 && !Q_stricmp( srclumps[i].name, "conchars" )) srclumps[i].type = TYP_GFXPIC; - W_AddFileToWad( name, wad, &srclumps[i] ); + W_AddFileToWad( filename, name, wad, &srclumps[i] ); } // release source lumps @@ -423,7 +421,7 @@ FS_PrintInfo_WAD */ static void FS_PrintInfo_WAD( searchpath_t *search, char *dst, size_t size ) { - Q_snprintf( dst, size, "%s (%i files)", search->wad->filename, search->wad->numlumps ); + Q_snprintf( dst, size, "%s (%i files)", search->filename, search->wad->numlumps ); } /* @@ -456,7 +454,7 @@ static int FS_FindFile_WAD( searchpath_t *search, const char *path ) } // make wadname from wad fullpath - COM_FileBase( search->wad->filename, shortname ); + COM_FileBase( search->filename, shortname ); COM_DefaultExtension( shortname, ".wad" ); // quick reject by wadname @@ -509,7 +507,7 @@ static void FS_Search_WAD( searchpath_t *search, stringlist_t *list, const char } // make wadname from wad fullpath - COM_FileBase( search->wad->filename, temp2 ); + COM_FileBase( search->filename, temp2 ); COM_DefaultExtension( temp2, ".wad" ); // quick reject by wadname @@ -575,7 +573,7 @@ qboolean FS_AddWad_Fullpath( const char *wadfile, qboolean *already_loaded, int for( search = fs_searchpaths; search; search = search->next ) { - if( search->type == SEARCHPATH_WAD && !Q_stricmp( search->wad->filename, wadfile )) + if( search->type == SEARCHPATH_WAD && !Q_stricmp( search->filename, wadfile )) { if( already_loaded ) *already_loaded = true; return true; // already loaded @@ -591,10 +589,11 @@ qboolean FS_AddWad_Fullpath( const char *wadfile, qboolean *already_loaded, int if( wad ) { search = (searchpath_t *)Mem_Calloc( fs_mempool, sizeof( searchpath_t )); + Q_strncpy( search->filename, wadfile, sizeof( search->filename )); search->wad = wad; search->type = SEARCHPATH_WAD; search->next = fs_searchpaths; - search->flags |= flags; + search->flags = flags; search->pfnPrintInfo = FS_PrintInfo_WAD; search->pfnClose = FS_Close_WAD; @@ -608,12 +607,10 @@ qboolean FS_AddWad_Fullpath( const char *wadfile, qboolean *already_loaded, int Con_Reportf( "Adding wadfile: %s (%i files)\n", wadfile, wad->numlumps ); return true; } - else - { - if( errorcode != WAD_LOAD_NO_FILES ) - Con_Reportf( S_ERROR "FS_AddWad_Fullpath: unable to load wad \"%s\"\n", wadfile ); - return false; - } + + if( errorcode != WAD_LOAD_NO_FILES ) + Con_Reportf( S_ERROR "FS_AddWad_Fullpath: unable to load wad \"%s\"\n", wadfile ); + return false; } /* diff --git a/filesystem/zip.c b/filesystem/zip.c index 5d3cc2ad..c1edd88c 100644 --- a/filesystem/zip.c +++ b/filesystem/zip.c @@ -120,7 +120,6 @@ typedef struct zipfile_s struct zip_s { - string filename; int handle; int numfiles; time_t filetime; @@ -381,7 +380,6 @@ static zip_t *FS_LoadZip( const char *zipfile, int *error ) info[i].offset = info[i].offset + header.filename_len + header.extrafield_len + sizeof( header ); } - Q_strncpy( zip->filename, zipfile, sizeof( zip->filename ) ); zip->filetime = FS_SysFileTime( zipfile ); zip->numfiles = numpackfiles; zip->files = info; @@ -419,7 +417,7 @@ file_t *FS_OpenFile_ZIP( searchpath_t *search, const char *filename, const char return NULL; } - return FS_OpenHandle( search->zip->filename, search->zip->handle, pfile->offset, pfile->size ); + return FS_OpenHandle( search->filename, search->zip->handle, pfile->offset, pfile->size ); } /* @@ -586,7 +584,7 @@ FS_PrintInfo_ZIP */ void FS_PrintInfo_ZIP( searchpath_t *search, char *dst, size_t size ) { - Q_snprintf( dst, size, "%s (%i files)", search->zip->filename, search->zip->numfiles ); + Q_snprintf( dst, size, "%s (%i files)", search->filename, search->zip->numfiles ); } /* @@ -683,7 +681,7 @@ qboolean FS_AddZip_Fullpath( const char *zipfile, qboolean *already_loaded, int for( search = fs_searchpaths; search; search = search->next ) { - if( search->type == SEARCHPATH_ZIP && !Q_stricmp( search->zip->filename, zipfile )) + if( search->type == SEARCHPATH_ZIP && !Q_stricmp( search->filename, zipfile )) { if( already_loaded ) *already_loaded = true; return true; // already loaded @@ -701,10 +699,11 @@ qboolean FS_AddZip_Fullpath( const char *zipfile, qboolean *already_loaded, int int i; search = (searchpath_t *)Mem_Calloc( fs_mempool, sizeof( searchpath_t ) ); + Q_strncpy( search->filename, zipfile, sizeof( search->filename )); search->zip = zip; search->type = SEARCHPATH_ZIP; search->next = fs_searchpaths; - search->flags |= flags; + search->flags = flags; search->pfnPrintInfo = FS_PrintInfo_ZIP; search->pfnClose = FS_Close_ZIP; From f3400c983e5f97b641cd96636a777afd9e24167e Mon Sep 17 00:00:00 2001 From: Alibek Omarov Date: Thu, 15 Dec 2022 12:24:56 +0300 Subject: [PATCH 284/490] engine: network: fix address comparator --- engine/common/net_ws.c | 36 ++++++++++++++++++++++++++---------- 1 file changed, 26 insertions(+), 10 deletions(-) diff --git a/engine/common/net_ws.c b/engine/common/net_ws.c index e1c5719a..b25173b9 100644 --- a/engine/common/net_ws.c +++ b/engine/common/net_ws.c @@ -940,37 +940,53 @@ qboolean NET_CompareAdr( const netadr_t a, const netadr_t b ) NET_CompareAdrSort Network address sorting comparator +guaranteed to return -1, 0 or 1 ==================== */ int NET_CompareAdrSort( const void *_a, const void *_b ) { - const netadr_t *a = _a; - const netadr_t *b = _b; - int portdiff; + const netadr_t *a = _a, *b = _b; + int porta, portb, portdiff, addrdiff; if( a->type6 != b->type6 ) - return (int)a->type6 - (int)b->type6; + return bound( -1, (int)a->type6 - (int)b->type6, 1 ); - portdiff = (int)a->port - (int)b->port; + porta = ntohs( a->port ); + portb = ntohs( b->port ); + if( porta < portb ) + portdiff = -1; + else if( porta > portb ) + portdiff = 1; + else + portdiff = 0; switch( a->type6 ) { case NA_IP6: - return NET_NetadrIP6Compare( a, b ); + if(( addrdiff = NET_NetadrIP6Compare( a, b ))) + return addrdiff; + // fallthrough case NA_MULTICAST_IP6: return portdiff; } + // don't check for full type earlier, as it's value depends on v6 address if( a->type != b->type ) - return (int)a->type - (int)b->type; + return bound( -1, (int)a->type - (int)b->type, 1 ); switch( a->type ) { case NA_IP: - return memcmp( a->ip, b->ip, sizeof( a->ip )); - case NA_IPX: - return memcmp( a->ipx, b->ipx, sizeof( a->ipx )); + if(( addrdiff = memcmp( a->ip, b->ip, sizeof( a->ipx )))) + return addrdiff; + // fallthrough case NA_BROADCAST: + return portdiff; + + case NA_IPX: + if(( addrdiff = memcmp( a->ipx, b->ipx, sizeof( a->ipx )))) + return addrdiff; + // fallthrough case NA_BROADCAST_IPX: return portdiff; } From ee218f36e09ca5b007c9a99ba6db28d9c1c2d96d Mon Sep 17 00:00:00 2001 From: Alibek Omarov Date: Sat, 17 Dec 2022 00:25:20 +0300 Subject: [PATCH 285/490] filesystem: pak: no need to free files ptr anymore --- filesystem/pak.c | 2 -- 1 file changed, 2 deletions(-) diff --git a/filesystem/pak.c b/filesystem/pak.c index bb1721c8..8f3ee7d2 100644 --- a/filesystem/pak.c +++ b/filesystem/pak.c @@ -315,8 +315,6 @@ FS_Close_PAK */ static void FS_Close_PAK( searchpath_t *search ) { - if( search->pack->files ) - Mem_Free( search->pack->files ); if( search->pack->handle >= 0 ) close( search->pack->handle ); Mem_Free( search->pack ); From 6e179346c9fe1b6311eecd4ce54d166d5cffdaba Mon Sep 17 00:00:00 2001 From: Alibek Omarov Date: Mon, 19 Dec 2022 18:36:31 +0300 Subject: [PATCH 286/490] common: redefine poolhandle_t back to pointer for 32-bit systems --- common/xash3d_types.h | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/common/xash3d_types.h b/common/xash3d_types.h index 9b343e8c..7c1f3a84 100644 --- a/common/xash3d_types.h +++ b/common/xash3d_types.h @@ -23,7 +23,12 @@ typedef byte rgba_t[4]; // unsigned byte colorpack 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 From 327017421cf78bb681f72d122c5f603d96757da6 Mon Sep 17 00:00:00 2001 From: Alibek Omarov Date: Mon, 19 Dec 2022 18:38:49 +0300 Subject: [PATCH 287/490] engine: common: zone: redefine poolhandle_t back to pointer for 32-bit systems --- engine/common/zone.c | 17 +++++++++++++++-- 1 file changed, 15 insertions(+), 2 deletions(-) diff --git a/engine/common/zone.c b/engine/common/zone.c index 4ca83f65..c8a79397 100644 --- a/engine/common/zone.c +++ b/engine/common/zone.c @@ -50,13 +50,16 @@ typedef struct mempool_s 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 uint sentinel2; // should always be MEMHEADER_SENTINEL1 } mempool_t; static mempool_t *poolchain = NULL; // critical stuff +#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 @@ -77,6 +80,12 @@ static mempool_t *Mem_FindPool( poolhandle_t poolptr ) return NULL; } +#else +static mempool_t *Mem_FindPool( poolhandle_t poolptr ) +{ + return (mempool_t *)poolptr; +} +#endif void *_Mem_Alloc( poolhandle_t poolptr, size_t size, qboolean clear, const char *filename, int fileline ) { @@ -219,10 +228,14 @@ poolhandle_t _Mem_AllocPool( const char *name, const char *filename, int filelin pool->realsize = sizeof( mempool_t ); Q_strncpy( pool->name, name, sizeof( pool->name )); pool->next = poolchain; - pool->idx = ++lastidx; poolchain = pool; - + +#if XASH_64BIT + pool->idx = ++lastidx; return pool->idx; +#else + return (poolhandle_t)pool; +#endif } void _Mem_FreePool( poolhandle_t *poolptr, const char *filename, int fileline ) From e273e09fc9af1d1e9e07071aa72bfe1bd221e1b7 Mon Sep 17 00:00:00 2001 From: Andrey Akhmichin <15944199+nekonomicon@users.noreply.github.com> Date: Tue, 20 Dec 2022 08:16:48 +0500 Subject: [PATCH 288/490] engine: platform: win32: simplify strings operations. --- engine/platform/win32/con_win.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/engine/platform/win32/con_win.c b/engine/platform/win32/con_win.c index 4506063a..63a4102b 100644 --- a/engine/platform/win32/con_win.c +++ b/engine/platform/win32/con_win.c @@ -499,12 +499,12 @@ void Wcon_CreateConsole( void ) if( host.type == HOST_NORMAL ) { - Q_strncpy( s_wcd.title, va( "Xash3D %s", XASH_VERSION ), sizeof( s_wcd.title )); + Q_strncpy( s_wcd.title, "Xash3D " 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, va( "XashDS %s", XASH_VERSION ), sizeof( s_wcd.title )); + Q_strncpy( s_wcd.title, "XashDS " 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 } From 0d5cd89144f8a31934ce8f35ea50f0e49aa93294 Mon Sep 17 00:00:00 2001 From: Andrey Akhmichin <15944199+nekonomicon@users.noreply.github.com> Date: Tue, 20 Dec 2022 08:27:02 +0500 Subject: [PATCH 289/490] engine: client: Fix access to uninitialized variable. --- engine/client/cl_video.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/engine/client/cl_video.c b/engine/client/cl_video.c index 1b4ad6e5..9a225d0a 100644 --- a/engine/client/cl_video.c +++ b/engine/client/cl_video.c @@ -209,7 +209,7 @@ qboolean SCR_PlayCinematic( const char *arg ) if( FS_FileExists( arg, false ) && !fullpath ) { - Con_Printf( S_ERROR "Couldn't load %s from packfile. Please extract it\n", path ); + Con_Printf( S_ERROR "Couldn't load %s from packfile. Please extract it\n", arg ); return false; } From f4fb8b4ac2fd0608e3be5adddc8b4e355eb23b90 Mon Sep 17 00:00:00 2001 From: Andrey Akhmichin <15944199+nekonomicon@users.noreply.github.com> Date: Tue, 20 Dec 2022 08:08:59 +0500 Subject: [PATCH 290/490] engine: client: simplify strings operation. --- engine/client/cl_game.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/engine/client/cl_game.c b/engine/client/cl_game.c index 827d7573..a07e1e5d 100644 --- a/engine/client/cl_game.c +++ b/engine/client/cl_game.c @@ -1727,7 +1727,8 @@ static int GAME_EXPORT pfnClientCmd( const char *szCmdString ) else { // will exec later - Q_strncat( host.deferred_cmd, va( "%s\n", szCmdString ), sizeof( host.deferred_cmd )); + Q_strncat( host.deferred_cmd, szCmdString, sizeof( host.deferred_cmd )); + Q_strncat( host.deferred_cmd, "\n", sizeof( host.deferred_cmd )); } return 1; From 7555fefc18f848420086a781b61ec8fc4d207378 Mon Sep 17 00:00:00 2001 From: Andrey Akhmichin <15944199+nekonomicon@users.noreply.github.com> Date: Tue, 20 Dec 2022 00:24:22 +0500 Subject: [PATCH 291/490] ref: gl: simplify strings operations. --- ref/gl/gl_image.c | 20 +++++++++++++++----- ref/gl/gl_rmisc.c | 5 ++--- 2 files changed, 17 insertions(+), 8 deletions(-) diff --git a/ref/gl/gl_image.c b/ref/gl/gl_image.c index 658b0240..5c4aed51 100644 --- a/ref/gl/gl_image.c +++ b/ref/gl/gl_image.c @@ -1570,6 +1570,8 @@ int GL_LoadTextureArray( const char **names, int flags ) uint picFlags = 0; char name[256]; gl_texture_t *tex; + size_t len = 0; + int ret = 0; uint i, j; if( !names || !names[0] || !glw_state.initialized ) @@ -1583,14 +1585,22 @@ int GL_LoadTextureArray( const char **names, int flags ) if( numLayers <= 0 ) return 0; // create complexname from layer names - for( i = 0; i < numLayers; i++ ) + for( i = 0; i < numLayers - 1; i++ ) { COM_FileBase( names[i], basename ); - Q_strncat( name, basename, sizeof( name ) ); - if( i != ( numLayers - 1 )) Q_strncat( name, "|", sizeof( name )); - } + ret = Q_snprintf( &name[len], sizeof( name ) - len, "%s|", basename ); - Q_strncat( name, va( "[%i]", numLayers ), sizeof( name )); + if( ret == -1 ) + return 0; + + len += ret; + } + + COM_FileBase( names[i], basename ); + ret = Q_snprintf( &name[len], sizeof( name ) - len, "%s[%i]", basename, numLayers ); + + if( ret == -1 ) + return 0; if( !GL_CheckTexName( name )) return 0; diff --git a/ref/gl/gl_rmisc.c b/ref/gl/gl_rmisc.c index 230e66b9..9a5b7443 100644 --- a/ref/gl/gl_rmisc.c +++ b/ref/gl/gl_rmisc.c @@ -46,14 +46,13 @@ static void R_ParseDetailTextures( const char *filename ) // NOTE: COM_ParseFile handled some symbols seperately // this code will be fix it pfile = COM_ParseFile( pfile, token, sizeof( token )); - Q_strncat( texname, "{", sizeof( texname )); - Q_strncat( texname, token, sizeof( texname )); + Q_snprintf( texname, sizeof( texname ), "{%s", token ); } else Q_strncpy( texname, token, sizeof( texname )); // read detailtexture name pfile = COM_ParseFile( pfile, token, sizeof( token )); - Q_strncat( detail_texname, token, sizeof( detail_texname )); + Q_strncpy( detail_texname, token, sizeof( detail_texname )); // trying the scales or '{' pfile = COM_ParseFile( pfile, token, sizeof( token )); From 18e68f1ab3f3df4a6994894e141251b3be6dd200 Mon Sep 17 00:00:00 2001 From: Alibek Omarov Date: Tue, 20 Dec 2022 16:34:23 +0300 Subject: [PATCH 292/490] filesystem: fix missing cast --- filesystem/filesystem.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/filesystem/filesystem.c b/filesystem/filesystem.c index 4e8ceb23..b6145267 100644 --- a/filesystem/filesystem.c +++ b/filesystem/filesystem.c @@ -1351,7 +1351,7 @@ static qboolean FS_FindLibrary( const char *dllname, qboolean directpath, fs_dll poolhandle_t _Mem_AllocPool( const char *name, const char *filename, int fileline ) { - return 0xDEADC0DE; + return (poolhandle_t)0xDEADC0DE; } void _Mem_FreePool( poolhandle_t *poolptr, const char *filename, int fileline ) From 9450c08eec33356c8b836c84e21278dd41f4b668 Mon Sep 17 00:00:00 2001 From: Andrey Akhmichin <15944199+nekonomicon@users.noreply.github.com> Date: Mon, 19 Dec 2022 23:56:03 +0500 Subject: [PATCH 293/490] engine: server: simplify strings operations. --- engine/server/sv_client.c | 13 ++++++++++++- engine/server/sv_save.c | 10 +++++----- 2 files changed, 17 insertions(+), 6 deletions(-) diff --git a/engine/server/sv_client.c b/engine/server/sv_client.c index ac4fd2ff..3a1f010f 100644 --- a/engine/server/sv_client.c +++ b/engine/server/sv_client.c @@ -930,15 +930,26 @@ void SV_BuildNetAnswer( netadr_t from ) } else if( type == NETAPI_REQUEST_PLAYERS ) { + size_t len = 0; + string[0] = '\0'; for( i = 0; i < svs.maxclients; i++ ) { if( svs.clients[i].state >= cs_connected ) { + int ret; edict_t *ed = svs.clients[i].edict; float time = host.realtime - svs.clients[i].connection_started; - Q_strncat( string, va( "%c\\%s\\%i\\%f\\", count, svs.clients[i].name, (int)ed->v.frags, time ), sizeof( string )); + ret = Q_snprintf( &string[len], sizeof( string ) - len, "%c\\%s\\%i\\%f\\", count, svs.clients[i].name, (int)ed->v.frags, time ); + + if( ret == -1 ) + { + Con_DPrintf( S_WARN "SV_BuildNetAnswer: NETAPI_REQUEST_PLAYERS: buffer overflow!\n" ); + break; + } + + len += ret; count++; } } diff --git a/engine/server/sv_save.c b/engine/server/sv_save.c index 0fcf66e4..e3d791b3 100644 --- a/engine/server/sv_save.c +++ b/engine/server/sv_save.c @@ -2418,13 +2418,13 @@ int GAME_EXPORT SV_GetSaveComment( const char *savename, char *comment ) if( FBitSet( flags, MAP_INVALID_VERSION )) { - Q_strncpy( comment, va( "", mapName ), MAX_STRING ); + Q_snprintf( comment, sizeof( comment ), "", mapName ); return 0; } if( !FBitSet( flags, MAP_IS_EXIST )) { - Q_strncpy( comment, va( "", mapName ), MAX_STRING ); + Q_snprintf( comment, sizeof( comment ), "", mapName ); return 0; } @@ -2433,10 +2433,10 @@ int GAME_EXPORT SV_GetSaveComment( const char *savename, char *comment ) // split comment to sections if( Q_strstr( savename, "quick" )) - Q_strncat( comment, "[quick]", CS_SIZE ); + Q_snprintf( comment, sizeof( comment ), "[quick]%s", description ); else if( Q_strstr( savename, "autosave" )) - Q_strncat( comment, "[autosave]", CS_SIZE ); - Q_strncat( comment, description, CS_SIZE ); + Q_snprintf( comment, sizeof( comment ), "[autosave]%s", description ); + else Q_strncpy( comment, description, sizeof( comment )); strftime( timestring, sizeof ( timestring ), "%b%d %Y", file_tm ); Q_strncpy( comment + CS_SIZE, timestring, CS_TIME ); strftime( timestring, sizeof( timestring ), "%H:%M", file_tm ); From 444e08f59ac94412f35137f21622113febe3e898 Mon Sep 17 00:00:00 2001 From: Andrey Akhmichin <15944199+nekonomicon@users.noreply.github.com> Date: Tue, 20 Dec 2022 07:39:08 +0500 Subject: [PATCH 294/490] engine: common: simplify strings operations. --- engine/common/mod_bmodel.c | 17 ++++++++++++----- 1 file changed, 12 insertions(+), 5 deletions(-) diff --git a/engine/common/mod_bmodel.c b/engine/common/mod_bmodel.c index b931d6ac..af55ac07 100644 --- a/engine/common/mod_bmodel.c +++ b/engine/common/mod_bmodel.c @@ -434,9 +434,9 @@ void Mod_PrintWorldStats_f( void ) Con_Printf( "Lighting: %s\n", FBitSet( w->flags, MODEL_COLORED_LIGHTING ) ? "colored" : "monochrome" ); Con_Printf( "World total leafs: %d\n", worldmodel->numleafs + 1 ); Con_Printf( "original name: ^1%s\n", worldmodel->name ); - Con_Printf( "internal name: %s\n", (world.message[0]) ? va( "^2%s", world.message ) : "none" ); - Con_Printf( "map compiler: %s\n", (world.compiler[0]) ? va( "^3%s", world.compiler ) : "unknown" ); - Con_Printf( "map editor: %s\n", (world.generator[0]) ? va( "^2%s", world.generator ) : "unknown" ); + Con_Printf( "internal name: ^2%s\n", world.message[0] ? world.message : "none" ); + Con_Printf( "map compiler: ^3%s\n", world.compiler[0] ? world.compiler : "unknown" ); + Con_Printf( "map editor: ^2%s\n", world.generator[0] ? world.generator : "unknown" ); } /* @@ -2781,7 +2781,8 @@ qboolean Mod_LoadBmodelLumps( const byte *mod_base, qboolean isworld ) dbspmodel_t *bmod = &srcmodel; model_t *mod = loadmodel; char wadvalue[2048]; - int i; + size_t len = 0; + int i, ret; // always reset the intermediate struct memset( bmod, 0, sizeof( dbspmodel_t )); @@ -2888,7 +2889,13 @@ qboolean Mod_LoadBmodelLumps( const byte *mod_base, qboolean isworld ) { if( !bmod->wadlist.wadusage[i] ) continue; - Q_strncat( wadvalue, va( "%s.wad; ", bmod->wadlist.wadnames[i] ), sizeof( wadvalue )); + ret = Q_snprintf( &wadvalue[len], sizeof( wadvalue ), "%s.wad; ", bmod->wadlist.wadnames[i] ); + if( ret == -1 ) + { + Con_DPrintf( S_WARN "Too many wad files for output!\n" ); + break; + } + len += ret; } if( COM_CheckString( wadvalue )) From 499cd48e839c282307e9b06d16bddcc16afc827b Mon Sep 17 00:00:00 2001 From: MoeMod Server #2 <824395314@qq.com> Date: Sun, 25 Dec 2022 02:47:10 +0800 Subject: [PATCH 295/490] filesystem: fix lseek for mpg123 --- engine/common/soundlib/snd_mp3.c | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/engine/common/soundlib/snd_mp3.c b/engine/common/soundlib/snd_mp3.c index 4b66664d..16e142f6 100644 --- a/engine/common/soundlib/snd_mp3.c +++ b/engine/common/soundlib/snd_mp3.c @@ -105,6 +105,16 @@ qboolean Sound_LoadMPG( const char *name, const byte *buffer, fs_offset_t filesi return true; } +/* +================= +FS_SeekEx +================= +*/ +fs_offset_t FS_SeekEx( file_t *file, fs_offset_t offset, int whence ) +{ + return FS_Seek( file, offset, whence ) == -1 ? -1 : FS_Tell( file ); +} + /* ================= Stream_OpenMPG @@ -138,7 +148,7 @@ stream_t *Stream_OpenMPG( const char *filename ) if( ret ) Con_DPrintf( S_ERROR "%s\n", get_error( mpeg )); // trying to open stream and read header - if( !open_mpeg_stream( mpeg, file, (void*)FS_Read, (void*)FS_Seek, &sc )) + if( !open_mpeg_stream( mpeg, file, (void*)FS_Read, (void*)FS_SeekEx, &sc )) { Con_DPrintf( S_ERROR "Stream_OpenMPG: failed to load (%s): %s\n", filename, get_error( mpeg )); close_decoder( mpeg ); From 12bb0ca44b966b16c7ec3688ae07404b485ae455 Mon Sep 17 00:00:00 2001 From: Andrey Akhmichin <15944199+nekonomicon@users.noreply.github.com> Date: Sun, 25 Dec 2022 05:35:06 +0500 Subject: [PATCH 296/490] engine: server: Fix broken description for saves. --- engine/server/sv_save.c | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/engine/server/sv_save.c b/engine/server/sv_save.c index e3d791b3..08645645 100644 --- a/engine/server/sv_save.c +++ b/engine/server/sv_save.c @@ -2418,13 +2418,13 @@ int GAME_EXPORT SV_GetSaveComment( const char *savename, char *comment ) if( FBitSet( flags, MAP_INVALID_VERSION )) { - Q_snprintf( comment, sizeof( comment ), "", mapName ); + Q_snprintf( comment, MAX_STRING, "", mapName ); return 0; } if( !FBitSet( flags, MAP_IS_EXIST )) { - Q_snprintf( comment, sizeof( comment ), "", mapName ); + Q_snprintf( comment, MAX_STRING, "", mapName ); return 0; } @@ -2433,10 +2433,10 @@ int GAME_EXPORT SV_GetSaveComment( const char *savename, char *comment ) // split comment to sections if( Q_strstr( savename, "quick" )) - Q_snprintf( comment, sizeof( comment ), "[quick]%s", description ); + Q_snprintf( comment, CS_SIZE, "[quick]%s", description ); else if( Q_strstr( savename, "autosave" )) - Q_snprintf( comment, sizeof( comment ), "[autosave]%s", description ); - else Q_strncpy( comment, description, sizeof( comment )); + Q_snprintf( comment, CS_SIZE, "[autosave]%s", description ); + else Q_strncpy( comment, description, CS_SIZE ); strftime( timestring, sizeof ( timestring ), "%b%d %Y", file_tm ); Q_strncpy( comment + CS_SIZE, timestring, CS_TIME ); strftime( timestring, sizeof( timestring ), "%H:%M", file_tm ); From 953dd3d1a7c3d79dde77b0d110b5393425d88e9f Mon Sep 17 00:00:00 2001 From: Alibek Omarov Date: Sat, 17 Dec 2022 23:16:49 +0300 Subject: [PATCH 297/490] wscript: tidy up checks, add check for GNU strchrnul --- wscript | 49 ++++++++++++++++++++----------------------------- 1 file changed, 20 insertions(+), 29 deletions(-) diff --git a/wscript b/wscript index 268afffe..bcd4011e 100644 --- a/wscript +++ b/wscript @@ -248,14 +248,6 @@ def configure(conf): conf.env.append_unique('CXXFLAGS', cxxflags) conf.env.append_unique('LINKFLAGS', linkflags) - # check if we can use C99 stdint - if conf.check_cc(header_name='stdint.h', mandatory=False): - # use system - conf.define('STDINT_H', 'stdint.h') - else: - # include portable stdint by Paul Hsich - conf.define('STDINT_H', 'pstdint.h') - conf.env.ENABLE_UTILS = conf.options.ENABLE_UTILS conf.env.ENABLE_FUZZER = conf.options.ENABLE_FUZZER conf.env.DEDICATED = conf.options.DEDICATED @@ -270,6 +262,15 @@ def configure(conf): conf.env.GAMEDIR = conf.options.GAMEDIR conf.define('XASH_GAMEDIR', conf.options.GAMEDIR) + # check if we can use C99 stdint + conf.define('STDINT_H', 'stdint.h' if conf.check_cc(header_name='stdint.h', mandatory=False) else 'pstdint.h') + + # check if we can use alloca.h or malloc.h + if conf.check_cc(header_name='alloca.h', mandatory=False): + conf.define('ALLOCA_H', 'alloca.h') + elif conf.check_cc(header_name='malloc.h', mandatory=False): + conf.define('ALLOCA_H', 'malloc.h') + if conf.env.DEST_OS != 'win32': conf.check_cc(lib='dl', mandatory=False) @@ -283,16 +284,7 @@ def configure(conf): # Don't check them more than once, to save time # Usually, they are always available # but we need them in uselib - a = [ - 'user32', - 'shell32', - 'gdi32', - 'advapi32', - 'dbghelp', - 'psapi', - 'ws2_32' - ] - + a = [ 'user32', 'shell32', 'gdi32', 'advapi32', 'dbghelp', 'psapi', 'ws2_32' ] if conf.env.COMPILER_CC == 'msvc': for i in a: conf.start_msg('Checking for MSVC library') @@ -335,18 +327,17 @@ int main(void) { return 0; }''', if conf.env.DEST_OS != 'win32': strcasestr_frag = '''#include int main(int argc, char **argv) { strcasestr(argv[1], argv[2]); return 0; }''' + strchrnul_frag = '''#include +int main(int argc, char **argv) { strchrnul(argv[1], 'x'); return 0; }''' - if conf.check_cc(msg='Checking for strcasestr', mandatory=False, fragment=strcasestr_frag): - conf.define('HAVE_STRCASESTR', 1) - elif conf.check_cc(msg='... with _GNU_SOURCE?', mandatory=False, fragment=strcasestr_frag, defines='_GNU_SOURCE=1'): - conf.define('_GNU_SOURCE', 1) - conf.define('HAVE_STRCASESTR', 1) - - # check if we can use alloca.h or malloc.h - if conf.check_cc(header_name='alloca.h', mandatory=False): - conf.define('ALLOCA_H', 'alloca.h') - elif conf.check_cc(header_name='malloc.h', mandatory=False): - conf.define('ALLOCA_H', 'malloc.h') + def check_gnu_function(frag, msg, define): + if conf.check_cc(msg=msg, mandatory=False, fragment=frag): + conf.define(define, 1) + elif conf.check_cc(msg='... with _GNU_SOURCE?', mandatory=False, fragment=frag, defines='_GNU_SOURCE=1'): + conf.define(define, 1) + conf.define('_GNU_SOURCE', 1) + check_gnu_function(strcasestr_frag, 'Checking for strcasestr', 'HAVE_STRCASESTR') + check_gnu_function(strchrnul_frag, 'Checking for strchrnul', 'HAVE_STRCHRNUL') # indicate if we are packaging for Linux/BSD if conf.options.PACKAGING: From 071638794a12e905f88147f4806110c712596616 Mon Sep 17 00:00:00 2001 From: Alibek Omarov Date: Sat, 17 Dec 2022 23:17:32 +0300 Subject: [PATCH 298/490] public: redefine Q_strpbrk to C standard version, add Q_strchrnul --- public/crtlib.c | 16 ---------------- public/crtlib.h | 16 +++++++++++++++- 2 files changed, 15 insertions(+), 17 deletions(-) diff --git a/public/crtlib.c b/public/crtlib.c index 34972978..25af49de 100644 --- a/public/crtlib.c +++ b/public/crtlib.c @@ -496,22 +496,6 @@ int Q_sprintf( char *buffer, const char *format, ... ) return result; } -char *Q_strpbrk(const char *s, const char *accept) -{ - for( ; *s; s++ ) - { - const char *k; - - for( k = accept; *k; k++ ) - { - if( *s == *k ) - return (char*)s; - } - } - - return NULL; -} - void COM_StripColors( const char *in, char *out ) { while ( *in ) diff --git a/public/crtlib.h b/public/crtlib.h index f3013a8f..6296930d 100644 --- a/public/crtlib.h +++ b/public/crtlib.h @@ -83,7 +83,7 @@ const char *Q_timestamp( int format ); int Q_vsnprintf( char *buffer, size_t buffersize, const char *format, va_list args ); int Q_snprintf( char *buffer, size_t buffersize, const char *format, ... ) _format( 3 ); int Q_sprintf( char *buffer, const char *format, ... ) _format( 2 ); -char *Q_strpbrk(const char *s, const char *accept); +#define Q_strpbrk strpbrk void COM_StripColors( const char *in, char *out ); #define Q_memprint( val ) Q_pretifymem( val, 2 ) char *Q_pretifymem( float value, int digitsafterdecimal ); @@ -161,6 +161,20 @@ static inline char *Q_stristr( const char *s1, const char *s2 ) char *Q_stristr( const char *s1, const char *s2 ); #endif // defined( HAVE_STRCASESTR ) +#if defined( HAVE_STRCHRNUL ) +#define Q_strchrnul strchrnul +#else +static inline const char *Q_strchrnul( const char *s, int c ) +{ + const char *p = Q_strchr( s, c ); + + if( p ) + return p; + + return s + Q_strlen( s ); +} +#endif + #ifdef __cplusplus } #endif From e1ea3387eef1035dc51d02dff9411aebaf87898f Mon Sep 17 00:00:00 2001 From: Alibek Omarov Date: Mon, 19 Dec 2022 15:10:42 +0300 Subject: [PATCH 299/490] common: rename PATH_SPLITTER to PATH_SEPARATOR, change it's type to character --- common/port.h | 15 ++++++++++++--- public/crtlib.c | 4 ++-- 2 files changed, 14 insertions(+), 5 deletions(-) diff --git a/common/port.h b/common/port.h index ae60122f..d56dac85 100644 --- a/common/port.h +++ b/common/port.h @@ -19,6 +19,9 @@ GNU General Public License for more details. #include "build.h" +#define PATH_SEPARATOR_NIX '/' +#define PATH_SEPARATOR_WIN '\\' + #if !XASH_WIN32 #if XASH_APPLE #include @@ -41,14 +44,14 @@ GNU General Public License for more details. #include #include - #define PATH_SPLITTER "/" + #define PATH_SEPARATOR PATH_SEPARATOR_NIX #define HAVE_DUP #define O_BINARY 0 #define O_TEXT 0 #define _mkdir( x ) mkdir( x, S_IRWXU | S_IRWXG | S_IROTH | S_IXOTH ) #elif XASH_DOS4GW - #define PATH_SPLITTER "\\" + #define PATH_SEPARATOR PATH_SEPARATOR_WIN #endif typedef void* HANDLE; @@ -59,7 +62,7 @@ GNU General Public License for more details. int x, y; } POINT; #else // WIN32 - #define PATH_SPLITTER "\\" + #define PATH_SEPARATOR PATH_SEPARATOR_WIN #ifdef __MINGW32__ #define _inline static inline #define FORCEINLINE inline __attribute__((always_inline)) @@ -87,6 +90,12 @@ GNU General Public License for more details. #define XASH_LOW_MEMORY 0 #endif +#if PATH_SEPARATOR == PATH_SEPARATOR_WIN +#define PATH_SEPARATOR_STR "\\" +#else // PATH_SEPARATOR == PATH_SEPARATOR_NIX +#define PATH_SEPARATOR_STR "/" +#endif + #include #include #include diff --git a/public/crtlib.c b/public/crtlib.c index 25af49de..5b830cd4 100644 --- a/public/crtlib.c +++ b/public/crtlib.c @@ -824,8 +824,8 @@ void COM_FixSlashes( char *pname ) { while( *pname ) { - if( *pname == '\\' ) - *pname = '/'; + if( *pname == PATH_SEPARATOR_WIN ) + *pname = PATH_SEPARATOR_NIX; pname++; } } From 3393e2d95c2bdd180bc47f2eb5631cecbe07d978 Mon Sep 17 00:00:00 2001 From: Alibek Omarov Date: Mon, 19 Dec 2022 17:22:30 +0300 Subject: [PATCH 300/490] filesystem: implement directory entries caching, to avoid excessive directory listing syscalls to emulate case-insensitive filesystems * simplify game directory initialization code --- filesystem/dir.c | 331 ++++++++++++++++++++++++++++++- filesystem/filesystem.c | 261 ++++++++---------------- filesystem/filesystem_internal.h | 21 +- filesystem/pak.c | 19 +- filesystem/wad.c | 10 +- filesystem/zip.c | 17 +- 6 files changed, 439 insertions(+), 220 deletions(-) diff --git a/filesystem/dir.c b/filesystem/dir.c index 9fa03be0..138dbdf0 100644 --- a/filesystem/dir.c +++ b/filesystem/dir.c @@ -1,5 +1,5 @@ /* -dir.c - directory operations +dir.c - caseinsensitive directory operations Copyright (C) 2022 Alibek Omarov, Velaron This program is free software: you can redistribute it and/or modify @@ -27,9 +27,309 @@ GNU General Public License for more details. #include "xash3d_mathlib.h" #include "common/com_strings.h" +enum +{ + DIRENTRY_EMPTY_DIRECTORY = 0, // don't care if it's not directory or it's empty + DIRENTRY_NOT_SCANNED = -1, + DIRENTRY_CASEINSENSITIVE = -2, // directory is already caseinsensitive, just copy whatever is left +}; + +typedef struct dir_s +{ + string name; + int numentries; + struct dir_s *entries; // sorted +} dir_t; + +static int FS_SortDir( const void *_a, const void *_b ) +{ + const dir_t *a = _a; + const dir_t *b = _b; + return Q_stricmp( a->name, b->name ); +} + +static void FS_FreeDirEntries( dir_t *dir ) +{ + if( dir->entries ) + { + int i; + for( i = 0; i < dir->numentries; i++ ) + FS_FreeDirEntries( &dir->entries[i] ); + dir->entries = NULL; + } + + dir->numentries = DIRENTRY_NOT_SCANNED; +} + +static void FS_InitDirEntries( dir_t *dir, const stringlist_t *list ) +{ + int i; + + if( !list->numstrings ) + { + dir->numentries = DIRENTRY_EMPTY_DIRECTORY; + dir->entries = NULL; + return; + } + + dir->numentries = list->numstrings; + dir->entries = Mem_Malloc( fs_mempool, sizeof( dir_t ) * dir->numentries ); + + for( i = 0; i < list->numstrings; i++ ) + { + dir_t *entry = &dir->entries[i]; + Q_strncpy( entry->name, list->strings[i], sizeof( entry->name )); + entry->numentries = DIRENTRY_NOT_SCANNED; + entry->entries = NULL; + } + + qsort( dir->entries, dir->numentries, sizeof( dir->entries[0] ), FS_SortDir ); +} + +static void FS_PopulateDirEntries( dir_t *dir, const char *path ) +{ +#if XASH_WIN32 // Windows is always case insensitive + dir->numentries = DIRENTRY_CASEINSENSITIVE; + dir->entries = NULL; +#else + stringlist_t list; + + if( !FS_SysFolderExists( path )) + { + dir->numentries = DIRENTRY_EMPTY_DIRECTORY; + dir->entries = NULL; + return; + } + + stringlistinit( &list ); + listdirectory( &list, path, false ); + FS_InitDirEntries( dir, &list ); + stringlistfreecontents( &list ); +#endif +} + +static int FS_FindDirEntry( dir_t *dir, const char *name ) +{ + int left, right; + + if( dir->numentries < 0 ) + return -1; + + // look for the file (binary search) + left = 0; + right = dir->numentries - 1; + while( left <= right ) + { + int middle = (left + right) / 2; + int diff; + + diff = Q_stricmp( dir->entries[middle].name, name ); + + // found it + if( !diff ) + return middle; + + // if we're too far in the list + if( diff > 0 ) + right = middle - 1; + else left = middle + 1; + } + return -1; +} + +static void FS_MergeDirEntries( dir_t *dir, const stringlist_t *list ) +{ + int i; + dir_t temp; + + FS_InitDirEntries( &temp, list ); + + // copy all entries that has the same name and has subentries + for( i = 0; i < dir->numentries; i++ ) + { + int j; + + // don't care about directories without subentries + if( dir->entries == NULL ) + continue; + + // try to find this directory in new tree + j = FS_FindDirEntry( &temp, dir->entries[i].name ); + + // not found, free memory + if( j < 0 ) + { + FS_FreeDirEntries( &dir->entries[i] ); + continue; + } + + // found directory, move all entries + temp.entries[j].numentries = dir->entries[j].numentries; + temp.entries[j].entries = dir->entries[j].entries; + } + + // now we can free old tree and replace it with temporary + Mem_Free( dir->entries ); + dir->numentries = temp.numentries; + dir->entries = temp.entries; +} + +static int FS_MaybeUpdateDirEntries( dir_t *dir, const char *path, const char *entryname ) +{ + stringlist_t list; + qboolean update = false; + int idx; + + stringlistinit( &list ); + listdirectory( &list, path, false ); + + // find the reason to update entries list + if( list.numstrings != dir->numentries ) + { + // small optimization to not search string in the list + // and directly go updating entries + update = true; + } + else + { + for( idx = 0; idx < list.numstrings; idx++ ) + { + if( !Q_stricmp( list.strings[idx], entryname )) + { + update = true; + break; + } + } + } + + if( !update ) + { + stringlistfreecontents( &list ); + return -1; + } + + FS_MergeDirEntries( dir, &list ); + stringlistfreecontents( &list ); + return FS_FindDirEntry( dir, entryname ); +} + +qboolean FS_FixFileCase( dir_t *dir, const char *path, char *dst, size_t len, qboolean createpath ) +{ + const char *prev = path; + const char *next = Q_strchrnul( prev, PATH_SEPARATOR ); + size_t i = Q_strlen( dst ); // dst is expected to have searchpath filename + + while( true ) + { + char entryname[MAX_SYSPATH]; + int ret; + + // this subdirectory is case insensitive, just slam everything that's left + if( dir->numentries == DIRENTRY_CASEINSENSITIVE ) + { + i += Q_strncpy( &dst[i], prev, len - i ); + if( i >= len ) + { + Con_Printf( "%s: overflow while searching %s (caseinsensitive entry)\n", __FUNCTION__, path ); + return false; + } + } + + // populate cache if needed + if( dir->numentries == DIRENTRY_NOT_SCANNED ) + FS_PopulateDirEntries( dir, dst ); + + // get our entry name + Q_strncpy( entryname, prev, next - prev + 1 ); + ret = FS_FindDirEntry( dir, entryname ); + + // didn't found, but does it exists in FS? + if( ret < 0 ) + { + ret = FS_MaybeUpdateDirEntries( dir, dst, entryname ); + + if( ret < 0 ) + { + // if we're creating files or folders, we don't care if path doesn't exist + // so copy everything that's left and exit without an error + if( createpath ) + { + i += Q_strncpy( &dst[i], prev, len - i ); + if( i >= len ) + { + Con_Printf( "%s: overflow while searching %s (create path)\n", __FUNCTION__, path ); + return false; + } + + return true; + } + return false; + } + } + + dir = &dir->entries[ret]; + ret = Q_strncpy( &dst[i], dir->name, len - i ); + + // file not found, rescan... + if( !FS_SysFileOrFolderExists( dst )) + { + // strip failed part + dst[i] = 0; + + ret = FS_MaybeUpdateDirEntries( dir, dst, entryname ); + + // file not found, exit... =/ + if( ret < 0 ) + { + // if we're creating files or folders, we don't care if path doesn't exist + // so copy everything that's left and exit without an error + if( createpath ) + { + i += Q_strncpy( &dst[i], prev, len - i ); + if( i >= len ) + { + Con_Printf( "%s: overflow while searching %s (create path 2)\n", __FUNCTION__, path ); + return false; + } + + return true; + } + return false; + } + + dir = &dir->entries[ret]; + ret = Q_strncpy( &dst[i], dir->name, len - i ); + } + + i += ret; + if( i >= len ) // overflow! + { + Con_Printf( "%s: overflow while searching %s (appending fixed file name)\n", __FUNCTION__, path ); + return false; + } + + // end of string, found file, return + if( next[0] == '\0' ) + break; + + // move pointer one character forward, find next path split character + prev = next + 1; + next = Q_strchrnul( prev, PATH_SEPARATOR ); + i += Q_strncpy( &dst[i], PATH_SEPARATOR_STR, len - i ); + if( i >= len ) // overflow! + { + Con_Printf( "%s: overflow while searching %s (path separator)\n", __FUNCTION__, path ); + return false; + } + } + + return true; +} + static void FS_Close_DIR( searchpath_t *search ) { - + FS_FreeDirEntries( search->dir ); + Mem_Free( search->dir ); } static void FS_PrintInfo_DIR( searchpath_t *search, char *dst, size_t size ) @@ -37,14 +337,21 @@ static void FS_PrintInfo_DIR( searchpath_t *search, char *dst, size_t size ) Q_strncpy( dst, search->filename, size ); } -static int FS_FindFile_DIR( searchpath_t *search, const char *path ) +static int FS_FindFile_DIR( searchpath_t *search, const char *path, char *fixedname, size_t len ) { char netpath[MAX_SYSPATH]; - Q_snprintf( netpath, sizeof( netpath ), "%s%s", search->filename, path ); + Q_strncpy( netpath, search->filename, sizeof( netpath )); + if( !FS_FixFileCase( search->dir, path, netpath, sizeof( netpath ), false )) + return -1; - if( FS_SysFileExists( netpath, !( search->flags & FS_CUSTOM_PATH ) ) ) + if( FS_SysFileExists( netpath, !FBitSet( search->flags, FS_CUSTOM_PATH ))) + { + // return fixed case file name only local for that searchpath + if( fixedname ) + Q_strncpy( fixedname, netpath + Q_strlen( search->filename ), len ); return 0; + } return -1; } @@ -63,7 +370,7 @@ static void FS_Search_DIR( searchpath_t *search, stringlist_t *list, const char separator = Q_max( slash, backslash ); separator = Q_max( separator, colon ); - + basepathlength = separator ? (separator + 1 - pattern) : 0; basepath = Mem_Calloc( fs_mempool, basepathlength + 1 ); if( basepathlength ) memcpy( basepath, pattern, basepathlength ); @@ -100,6 +407,7 @@ static void FS_Search_DIR( searchpath_t *search, stringlist_t *list, const char static int FS_FileTime_DIR( searchpath_t *search, const char *filename ) { + int time; char path[MAX_SYSPATH]; Q_snprintf( path, sizeof( path ), "%s%s", search->filename, filename ); @@ -127,9 +435,14 @@ void FS_InitDirectorySearchpath( searchpath_t *search, const char *path, int fla search->pfnFileTime = FS_FileTime_DIR; search->pfnFindFile = FS_FindFile_DIR; search->pfnSearch = FS_Search_DIR; + + // create cache root + search->dir = Mem_Malloc( fs_mempool, sizeof( dir_t )); + search->dir->name[0] = 0; // root has no filename, unused + FS_PopulateDirEntries( search->dir, path ); } -qboolean FS_AddDir_Fullpath( const char *path, qboolean *already_loaded, int flags ) +searchpath_t *FS_AddDir_Fullpath( const char *path, qboolean *already_loaded, int flags ) { searchpath_t *search; @@ -139,7 +452,7 @@ qboolean FS_AddDir_Fullpath( const char *path, qboolean *already_loaded, int fla { if( already_loaded ) *already_loaded = true; - return true; + return search; } } @@ -154,5 +467,5 @@ qboolean FS_AddDir_Fullpath( const char *path, qboolean *already_loaded, int fla Con_Printf( "Adding directory: %s\n", path ); - return true; + return search; } diff --git a/filesystem/filesystem.c b/filesystem/filesystem.c index b6145267..c3795909 100644 --- a/filesystem/filesystem.c +++ b/filesystem/filesystem.c @@ -51,8 +51,8 @@ searchpath_t *fs_searchpaths = NULL; // chain char fs_rodir[MAX_SYSPATH]; char fs_rootdir[MAX_SYSPATH]; char fs_writedir[MAX_SYSPATH]; // path that game allows to overwrite, delete and rename files (and create new of course) +searchpath_t *fs_writepath; -static searchpath_t fs_directpath; // static direct path static char fs_basedir[MAX_SYSPATH]; // base game directory static char fs_gamedir[MAX_SYSPATH]; // game current directory #if !XASH_WIN32 @@ -160,7 +160,7 @@ void stringlistappend( stringlist_t *list, char *text ) list->numstrings++; } -static void stringlistsort( stringlist_t *list ) +void stringlistsort( stringlist_t *list ) { char *temp; int i, j; @@ -243,109 +243,6 @@ OTHER PRIVATE FUNCTIONS ============================================================================= */ -/* -================== -FS_FixFileCase - -emulate WIN32 FS behaviour when opening local file -================== -*/ -const char *FS_FixFileCase( const char *path ) -{ -#if defined __DOS__ & !defined __WATCOM_LFN__ - // not fix, but convert to 8.3 CAPS and rotate slashes - // it is still recommended to package game data - static char out[PATH_MAX]; - int i = 0; - int last = 0; - while(*path) - { - char c = *path++; - - if(c == '/') c = '\\'; - else c = toupper(c); - out[i++] = c; - if(c == '\\' || c == '.') - { - if( i - last > 8 ) - { - char *l = &out[last]; - l[6] = '~'; - l[7] = '1'; - l[8] = c; - i = last + 9; - } - last = i; - } - } - - out[i] = 0; - return out; -#elif !XASH_WIN32 && !XASH_IOS // assume case insensitive - DIR *dir; - struct dirent *entry; - char dirpath[PATH_MAX], *filename; - static char fixedpath[PATH_MAX]; - - if( !fs_caseinsensitive ) - return path; - - if( path[0] != '/' ) - Q_snprintf( dirpath, sizeof( dirpath ), "./%s", path ); - else Q_strncpy( dirpath, path, PATH_MAX ); - - filename = Q_strrchr( dirpath, '/' ); - - if( filename ) - *filename++ = '\0'; - else - { - filename = (char*)path; - Q_strcpy( dirpath, "."); - } - - /* android has too slow directory scanning, - so drop out some not useful cases */ - if( filename - dirpath > 4 ) - { - char *point; - // too many wad textures - if( !Q_stricmp( filename - 5, ".wad") ) - return path; - point = Q_strchr( filename, '.' ); - if( point ) - { - if( !Q_strcmp( point, ".mip") || !Q_strcmp( point, ".dds" ) || !Q_strcmp( point, ".ent" ) ) - return path; - if( filename[0] == '{' ) - return path; - } - } - - //Con_Reportf( "FS_FixFileCase: %s\n", path ); - - if( !( dir = opendir( dirpath ) ) ) - return path; - - while( ( entry = readdir( dir ) ) ) - { - if( Q_stricmp( entry->d_name, filename ) ) - continue; - - Q_snprintf( fixedpath, sizeof( fixedpath ), "%s/%s", dirpath, entry->d_name ); - - //Con_Reportf( "FS_FixFileCase: %s %s %s\n", dirpath, filename, entry->d_name ); - - path = fixedpath; - - break; - } - - closedir( dir ); -#endif - return path; -} - #if XASH_WIN32 /* ==================== @@ -397,7 +294,7 @@ static qboolean FS_AddArchive_Fullpath( const char *file, qboolean *already_load { const char *ext = COM_FileExtension( file ); - if( !Q_stricmp( ext, "pk3" ) ) + if( !Q_stricmp( ext, "pk3" )) return FS_AddZip_Fullpath( file, already_loaded, flags ); else if ( !Q_stricmp( ext, "pak" )) return FS_AddPak_Fullpath( file, already_loaded, flags ); @@ -416,13 +313,11 @@ then loads and adds pak1.pak pak2.pak ... */ void FS_AddGameDirectory( const char *dir, uint flags ) { - stringlist_t list; - searchpath_t *search; - string fullpath; - int i; + stringlist_t list; + searchpath_t *search; + char fullpath[MAX_SYSPATH]; + int i; - if( !FBitSet( flags, FS_NOWRITE_PATH )) - Q_strncpy( fs_writedir, dir, sizeof( fs_writedir )); stringlistinit( &list ); listdirectory( &list, dir, false ); stringlistsort( &list ); @@ -430,41 +325,36 @@ void FS_AddGameDirectory( const char *dir, uint flags ) // add any PAK package in the directory for( i = 0; i < list.numstrings; i++ ) { - if( !Q_stricmp( COM_FileExtension( list.strings[i] ), "pak" )) + const char *ext = COM_FileExtension( list.strings[i] ); + + if( !Q_stricmp( ext, "pak" )) { - Q_sprintf( fullpath, "%s%s", dir, list.strings[i] ); + Q_snprintf( fullpath, sizeof( fullpath ), "%s%s", dir, list.strings[i] ); FS_AddPak_Fullpath( fullpath, NULL, flags ); } - } - - // add any Zip package in the directory - for( i = 0; i < list.numstrings; i++ ) - { - if( !Q_stricmp( COM_FileExtension( list.strings[i] ), "pk3" ) ) + else if( !Q_stricmp( ext, "pk3" )) { - Q_sprintf( fullpath, "%s%s", dir, list.strings[i] ); + Q_snprintf( fullpath, sizeof( fullpath ), "%s%s", dir, list.strings[i] ); FS_AddZip_Fullpath( fullpath, NULL, flags ); } - } - - FS_AllowDirectPaths( true ); - - // add any WAD package in the directory - for( i = 0; i < list.numstrings; i++ ) - { - if( !Q_stricmp( COM_FileExtension( list.strings[i] ), "wad" )) + else if( !Q_stricmp( ext, "wad" )) { - Q_sprintf( fullpath, "%s%s", dir, list.strings[i] ); + FS_AllowDirectPaths( true ); + Q_snprintf( fullpath, sizeof( fullpath ), "%s%s", dir, list.strings[i] ); FS_AddWad_Fullpath( fullpath, NULL, flags ); + FS_AllowDirectPaths( false ); } } - stringlistfreecontents( &list ); - FS_AllowDirectPaths( false ); // add the directory to the search path // (unpacked files have the priority over packed files) - FS_AddDir_Fullpath( dir, NULL, flags ); + search = FS_AddDir_Fullpath( dir, NULL, flags ); + if( !FBitSet( flags, FS_NOWRITE_PATH )) + { + Q_strncpy( fs_writedir, dir, sizeof( fs_writedir )); + fs_writepath = search; + } } /* @@ -929,7 +819,7 @@ void FS_ParseGenericGameInfo( gameinfo_t *GameInfo, const char *buf, const qbool } // make sure what gamedir is really exist - if( !FS_SysFolderExists( va( "%s"PATH_SPLITTER"%s", fs_rootdir, GameInfo->falldir ))) + if( !FS_SysFolderExists( va( "%s" PATH_SEPARATOR_STR "%s", fs_rootdir, GameInfo->falldir ))) GameInfo->falldir[0] = '\0'; } @@ -1278,7 +1168,7 @@ search for library, assume index is valid static qboolean FS_FindLibrary( const char *dllname, qboolean directpath, fs_dllinfo_t *dllInfo ) { searchpath_t *search; - int index, start = 0, i, len; + int index, start = 0, i, len; fs_ext_path = directpath; @@ -1302,7 +1192,7 @@ static qboolean FS_FindLibrary( const char *dllname, qboolean directpath, fs_dll COM_DefaultExtension( dllInfo->shortPath, "."OS_LIB_EXT ); // apply ext if forget - search = FS_FindFile( dllInfo->shortPath, &index, false ); + search = FS_FindFile( dllInfo->shortPath, &index, NULL, 0, false ); if( !search && !directpath ) { @@ -1310,7 +1200,7 @@ static qboolean FS_FindLibrary( const char *dllname, qboolean directpath, fs_dll // trying check also 'bin' folder for indirect paths Q_strncpy( dllInfo->shortPath, dllname, sizeof( dllInfo->shortPath )); - search = FS_FindFile( dllInfo->shortPath, &index, false ); + search = FS_FindFile( dllInfo->shortPath, &index, NULL, 0, false ); if( !search ) return false; // unable to find } @@ -1433,8 +1323,11 @@ qboolean FS_InitStdio( qboolean caseinsensitive, const char *rootdir, const char for( i = 0; i < dirs.numstrings; i++ ) { - char *roPath = va( "%s" PATH_SPLITTER "%s" PATH_SPLITTER, fs_rodir, dirs.strings[i] ); - char *rwPath = va( "%s" PATH_SPLITTER "%s" PATH_SPLITTER, fs_rootdir, dirs.strings[i] ); + char roPath[MAX_SYSPATH]; + char rwPath[MAX_SYSPATH]; + + Q_snprintf( roPath, sizeof( roPath ), "%s" PATH_SEPARATOR_STR "%s" PATH_SEPARATOR_STR, fs_rodir, dirs.strings[i] ); + Q_snprintf( rwPath, sizeof( rwPath ), "%s" PATH_SEPARATOR_STR "%s" PATH_SEPARATOR_STR, fs_rootdir, dirs.strings[i] ); // check if it's a directory if( !FS_SysFolderExists( roPath )) @@ -1468,6 +1361,8 @@ qboolean FS_InitStdio( qboolean caseinsensitive, const char *rootdir, const char } // build list of game directories here + if( COM_CheckStringEmpty( fs_rodir )) + FS_AddGameDirectory( va( "%s/", fs_rodir ), FS_STATIC_PATH|FS_NOWRITE_PATH ); FS_AddGameDirectory( "./", FS_STATIC_PATH ); for( i = 0; i < dirs.numstrings; i++ ) @@ -1620,14 +1515,6 @@ file_t *FS_SysOpen( const char *filepath, const char *mode ) #if !XASH_WIN32 if( file->handle < 0 ) - { - const char *ffilepath = FS_FixFileCase( filepath ); - if( ffilepath != filepath ) - file->handle = open( ffilepath, mod|opt, 0666 ); - if( file->handle >= 0 ) - FS_BackupFileName( file, ffilepath, mod|opt ); - } - else FS_BackupFileName( file, filepath, mod|opt ); #endif @@ -1717,14 +1604,6 @@ qboolean FS_SysFileExists( const char *path, qboolean caseinsensitive ) ret = stat( path, &buf ); - // speedup custom path search - if( caseinsensitive && ( ret < 0 ) ) - { - const char *fpath = FS_FixFileCase( path ); - if( fpath != path ) - ret = stat( fpath, &buf ); - } - if( ret < 0 ) return false; @@ -1777,6 +1656,26 @@ qboolean FS_SysFolderExists( const char *path ) #endif } +/* +============== +FS_SysFileOrFolderExists + +Check if filesystem entry exists at all, don't mind the type +============== +*/ +qboolean FS_SysFileOrFolderExists( const char *path ) +{ +#if XASH_WIN32 + return GetFileAttributes( path ) != -1; +#elif XASH_POSIX + struct stat buf; + return stat( path, &buf ) >= 0; +#else +#error +#endif + +} + /* ==================== FS_FindFile @@ -1787,7 +1686,7 @@ Return the searchpath where the file was found (or NULL) and the file index in the package if relevant ==================== */ -searchpath_t *FS_FindFile( const char *name, int *index, qboolean gamedironly ) +searchpath_t *FS_FindFile( const char *name, int *index, char *fixedname, size_t len, qboolean gamedironly ) { searchpath_t *search; @@ -1799,7 +1698,7 @@ searchpath_t *FS_FindFile( const char *name, int *index, qboolean gamedironly ) if( gamedironly & !FBitSet( search->flags, FS_GAMEDIRONLY_SEARCH_FLAGS )) continue; - pack_ind = search->pfnFindFile( search, name ); + pack_ind = search->pfnFindFile( search, name, fixedname, len ); if( pack_ind >= 0 ) { if( index ) *index = pack_ind; @@ -1811,14 +1710,25 @@ searchpath_t *FS_FindFile( const char *name, int *index, qboolean gamedironly ) { char netpath[MAX_SYSPATH], dirpath[MAX_SYSPATH]; - Q_snprintf( dirpath, sizeof( dirpath ), "%s" PATH_SPLITTER, fs_rootdir ); + Q_snprintf( dirpath, sizeof( dirpath ), "%s" PATH_SEPARATOR_STR, fs_rootdir ); Q_snprintf( netpath, sizeof( netpath ), "%s%s", dirpath, name ); if( FS_SysFileExists( netpath, true )) { + static searchpath_t fs_directpath; + + // clear old dir cache + if( fs_directpath.pfnClose ) + fs_directpath.pfnClose( &fs_directpath ); + + // just copy the name, we don't do case sensitivity fix there + if( fixedname ) + { + Q_strncpy( fixedname, name, len ); + } FS_InitDirectorySearchpath( &fs_directpath, dirpath, 0 ); if( index != NULL ) - *index = -1; + *index = 0; return &fs_directpath; } } @@ -1839,16 +1749,17 @@ Look for a file in the search paths and open it in read-only mode */ file_t *FS_OpenReadFile( const char *filename, const char *mode, qboolean gamedironly ) { - searchpath_t *search; - int pack_ind; + searchpath_t *search; + char netpath[MAX_SYSPATH]; + int pack_ind; - search = FS_FindFile( filename, &pack_ind, gamedironly ); + search = FS_FindFile( filename, &pack_ind, netpath, sizeof( netpath ), gamedironly ); // not found? if( search == NULL ) return NULL; - return search->pfnOpenFile( search, filename, mode, pack_ind ); + return search->pfnOpenFile( search, netpath, mode, pack_ind ); } /* @@ -1884,7 +1795,7 @@ file_t *FS_Open( const char *filepath, const char *mode, qboolean gamedironly ) // open the file on disk directly Q_sprintf( real_path, "%s/%s", fs_writedir, filepath ); - FS_CreatePath( real_path );// Create directories up to the file + FS_CreatePath( real_path ); // Create directories up to the file return FS_SysOpen( real_path, mode ); } @@ -2450,9 +2361,7 @@ Look for a file in the packages and in the filesystem */ int GAME_EXPORT FS_FileExists( const char *filename, int gamedironly ) { - if( FS_FindFile( filename, NULL, gamedironly )) - return true; - return false; + return FS_FindFile( filename, NULL, NULL, 0, gamedironly ) != NULL; } /* @@ -2465,15 +2374,16 @@ return NULL for file in pack */ const char *FS_GetDiskPath( const char *name, qboolean gamedironly ) { + static char temp[MAX_SYSPATH]; searchpath_t *search; - search = FS_FindFile( name, NULL, gamedironly ); + search = FS_FindFile( name, NULL, temp, sizeof( temp ), gamedironly ); if( search ) { if( search->type != SEARCHPATH_PLAIN ) // file in pack or wad return NULL; - return va( "%s%s", search->filename, name ); + return temp; } return NULL; @@ -2526,13 +2436,14 @@ return time of creation file in seconds */ int FS_FileTime( const char *filename, qboolean gamedironly ) { - searchpath_t *search; - int pack_ind; + searchpath_t *search; + char netpath[MAX_SYSPATH]; + int pack_ind; - search = FS_FindFile( filename, &pack_ind, gamedironly ); + search = FS_FindFile( filename, &pack_ind, netpath, sizeof( netpath ), gamedironly ); if( !search ) return -1; // doesn't exist - return search->pfnFileTime( search, filename ); + return search->pfnFileTime( search, netpath ); } /* @@ -2550,6 +2461,10 @@ qboolean FS_Rename( const char *oldname, const char *newname ) if( !oldname || !newname || !*oldname || !*newname ) return false; + // no work done + if( !Q_stricmp( oldname, newname )) + return true; + Q_snprintf( oldpath, sizeof( oldpath ), "%s%s", fs_writedir, oldname ); Q_snprintf( newpath, sizeof( newpath ), "%s%s", fs_writedir, newname ); diff --git a/filesystem/filesystem_internal.h b/filesystem/filesystem_internal.h index b2473e4a..66e0886d 100644 --- a/filesystem/filesystem_internal.h +++ b/filesystem/filesystem_internal.h @@ -24,6 +24,7 @@ extern "C" { #endif +typedef struct dir_s dir_t; typedef struct zip_s zip_t; typedef struct pack_s pack_t; typedef struct wfile_s wfile_t; @@ -72,6 +73,7 @@ typedef struct searchpath_s union { + dir_t *dir; pack_t *pack; wfile_t *wad; zip_t *zip; @@ -83,7 +85,7 @@ typedef struct searchpath_s void (*pfnClose)( struct searchpath_s *search ); file_t *(*pfnOpenFile)( struct searchpath_s *search, const char *filename, const char *mode, int pack_ind ); int (*pfnFileTime)( struct searchpath_s *search, const char *filename ); - int (*pfnFindFile)( struct searchpath_s *search, const char *path ); + int (*pfnFindFile)( struct searchpath_s *search, const char *path, char *fixedname, size_t len ); void (*pfnSearch)( struct searchpath_s *search, stringlist_t *list, const char *pattern, int caseinsensitive ); } searchpath_t; @@ -157,6 +159,13 @@ qboolean FS_WriteFile( const char *filename, const void *data, fs_offset_t len ) qboolean CRC32_File( dword *crcvalue, const char *filename ); qboolean MD5_HashFile( byte digest[16], const char *pszFileName, uint seed[4] ); +// stringlist ops +void stringlistinit( stringlist_t *list ); +void stringlistfreecontents( stringlist_t *list ); +void stringlistappend( stringlist_t *list, char *text ); +void stringlistsort( stringlist_t *list ); +void listdirectory( stringlist_t *list, const char *path, qboolean lowercase ); + // filesystem ops int FS_FileExists( const char *filename, int gamedironly ); int FS_FileTime( const char *filename, qboolean gamedironly ); @@ -165,19 +174,15 @@ qboolean FS_Rename( const char *oldname, const char *newname ); qboolean FS_Delete( const char *path ); qboolean FS_SysFileExists( const char *path, qboolean casesensitive ); const char *FS_GetDiskPath( const char *name, qboolean gamedironly ); -void stringlistinit( stringlist_t *list ); -void stringlistfreecontents( stringlist_t *list ); -void stringlistappend( stringlist_t *list, char *text ); -void listdirectory( stringlist_t *list, const char *path, qboolean lowercase ); void FS_CreatePath( char *path ); qboolean FS_SysFolderExists( const char *path ); +qboolean FS_SysFileOrFolderExists( const char *path ); file_t *FS_OpenReadFile( const char *filename, const char *mode, qboolean gamedironly ); int FS_SysFileTime( const char *filename ); file_t *FS_OpenHandle( const char *syspath, int handle, fs_offset_t offset, fs_offset_t len ); file_t *FS_SysOpen( const char *filepath, const char *mode ); -const char *FS_FixFileCase( const char *path ); -searchpath_t *FS_FindFile( const char *name, int *index, qboolean gamedironly ); +searchpath_t *FS_FindFile( const char *name, int *index, char *fixedname, size_t len, qboolean gamedironly ); // // pak.c @@ -206,7 +211,7 @@ qboolean FS_AddZip_Fullpath( const char *zipfile, qboolean *already_loaded, int // // dir.c // -qboolean FS_AddDir_Fullpath( const char *path, qboolean *already_loaded, int flags ); +searchpath_t *FS_AddDir_Fullpath( const char *path, qboolean *already_loaded, int flags ); void FS_InitDirectorySearchpath( searchpath_t *search, const char *path, int flags ); #ifdef __cplusplus diff --git a/filesystem/pak.c b/filesystem/pak.c index 8f3ee7d2..4efc3191 100644 --- a/filesystem/pak.c +++ b/filesystem/pak.c @@ -104,15 +104,6 @@ static pack_t *FS_LoadPackPAK( const char *packfile, int *error ) packhandle = open( packfile, O_RDONLY|O_BINARY ); -#if !XASH_WIN32 - if( packhandle < 0 ) - { - const char *fpackfile = FS_FixFileCase( packfile ); - if( fpackfile != packfile ) - packhandle = open( fpackfile, O_RDONLY|O_BINARY ); - } -#endif - if( packhandle < 0 ) { Con_Reportf( "%s couldn't open: %s\n", packfile, strerror( errno )); @@ -210,7 +201,7 @@ FS_FindFile_PAK =========== */ -static int FS_FindFile_PAK( searchpath_t *search, const char *path ) +static int FS_FindFile_PAK( searchpath_t *search, const char *path, char *fixedname, size_t len ) { int left, right, middle; @@ -227,6 +218,8 @@ static int FS_FindFile_PAK( searchpath_t *search, const char *path ) // Found it if( !diff ) { + if( fixedname ) + Q_strncpy( fixedname, search->pack->files[middle].name, len ); return middle; } @@ -359,8 +352,6 @@ qboolean FS_AddPak_Fullpath( const char *pakfile, qboolean *already_loaded, int if( pak ) { - string fullpath; - search = (searchpath_t *)Mem_Calloc( fs_mempool, sizeof( searchpath_t )); Q_strncpy( search->filename, pakfile, sizeof( search->filename )); search->pack = pak; @@ -384,7 +375,9 @@ qboolean FS_AddPak_Fullpath( const char *pakfile, qboolean *already_loaded, int { if( !Q_stricmp( COM_FileExtension( pak->files[i].name ), "wad" )) { - Q_snprintf( fullpath, MAX_STRING, "%s/%s", pakfile, pak->files[i].name ); + char fullpath[MAX_SYSPATH]; + + Q_snprintf( fullpath, sizeof( fullpath ), "%s/%s", pakfile, pak->files[i].name ); FS_AddWad_Fullpath( fullpath, NULL, flags ); } } diff --git a/filesystem/wad.c b/filesystem/wad.c index 6b94610d..adff3ad1 100644 --- a/filesystem/wad.c +++ b/filesystem/wad.c @@ -299,10 +299,6 @@ static wfile_t *W_Open( const char *filename, int *error ) wad->handle = FS_Open( basename, "rb", false ); - // HACKHACK: try to open WAD by full path for RoDir, when searchpaths are not ready - if( COM_CheckStringEmpty( fs_rodir ) && fs_ext_path && wad->handle == NULL ) - wad->handle = FS_SysOpen( filename, "rb" ); - if( wad->handle == NULL ) { Con_Reportf( S_ERROR "W_Open: couldn't open %s\n", filename ); @@ -430,7 +426,7 @@ FS_FindFile_WAD =========== */ -static int FS_FindFile_WAD( searchpath_t *search, const char *path ) +static int FS_FindFile_WAD( searchpath_t *search, const char *path, char *fixedname, size_t len ) { dlumpinfo_t *lump; signed char type = W_TypeFromExt( path ); @@ -469,6 +465,8 @@ static int FS_FindFile_WAD( searchpath_t *search, const char *path ) if( lump ) { + if( fixedname ) + Q_strncpy( fixedname, lump->name, len ); return lump - search->wad->lumps; } @@ -677,7 +675,7 @@ byte *FS_LoadWADFile( const char *path, fs_offset_t *lumpsizeptr, qboolean gamed searchpath_t *search; int index; - search = FS_FindFile( path, &index, gamedironly ); + search = FS_FindFile( path, &index, NULL, 0, gamedironly ); if( search && search->type == SEARCHPATH_WAD ) return W_ReadLump( search->wad, &search->wad->lumps[index], lumpsizeptr ); return NULL; diff --git a/filesystem/zip.c b/filesystem/zip.c index c1edd88c..e5171f12 100644 --- a/filesystem/zip.c +++ b/filesystem/zip.c @@ -202,15 +202,6 @@ static zip_t *FS_LoadZip( const char *zipfile, int *error ) zip->handle = open( zipfile, O_RDONLY|O_BINARY ); -#if !XASH_WIN32 - if( zip->handle < 0 ) - { - const char *fzipfile = FS_FixFileCase( zipfile ); - if( fzipfile != zipfile ) - zip->handle = open( fzipfile, O_RDONLY|O_BINARY ); - } -#endif - if( zip->handle < 0 ) { Con_Reportf( S_ERROR "%s couldn't open\n", zipfile ); @@ -439,7 +430,7 @@ byte *FS_LoadZIPFile( const char *path, fs_offset_t *sizeptr, qboolean gamediron if( sizeptr ) *sizeptr = 0; - search = FS_FindFile( path, &index, gamedironly ); + search = FS_FindFile( path, &index, NULL, 0, gamedironly ); if( !search || search->type != SEARCHPATH_ZIP ) return NULL; @@ -593,7 +584,7 @@ FS_FindFile_ZIP =========== */ -int FS_FindFile_ZIP( searchpath_t *search, const char *path ) +int FS_FindFile_ZIP( searchpath_t *search, const char *path, char *fixedname, size_t len ) { int left, right, middle; @@ -609,7 +600,11 @@ int FS_FindFile_ZIP( searchpath_t *search, const char *path ) // Found it if( !diff ) + { + if( fixedname ) + Q_strncpy( fixedname, search->zip->files[middle].name, len ); return middle; + } // if we're too far in the list if( diff > 0 ) From ac59f25375ecd723f83f5580d00c4ec2471a9903 Mon Sep 17 00:00:00 2001 From: Alibek Omarov Date: Wed, 21 Dec 2022 07:34:31 +0300 Subject: [PATCH 301/490] DO NOT MERGE filesystem: add naive FixFileCase --- filesystem/dir.c | 61 ++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 61 insertions(+) diff --git a/filesystem/dir.c b/filesystem/dir.c index 138dbdf0..a3cde48f 100644 --- a/filesystem/dir.c +++ b/filesystem/dir.c @@ -213,6 +213,7 @@ static int FS_MaybeUpdateDirEntries( dir_t *dir, const char *path, const char *e return FS_FindDirEntry( dir, entryname ); } +#if 1 qboolean FS_FixFileCase( dir_t *dir, const char *path, char *dst, size_t len, qboolean createpath ) { const char *prev = path; @@ -325,6 +326,66 @@ qboolean FS_FixFileCase( dir_t *dir, const char *path, char *dst, size_t len, qb return true; } +#else +qboolean FS_FixFileCase( dir_t *dir, const char *path, char *dst, size_t len, qboolean createpath ) +{ + const char *prev = path; + const char *next = Q_strchrnul( prev, PATH_SEPARATOR ); + size_t i = Q_strlen( dst ); // dst is expected to have searchpath filename + + while( true ) + { + stringlist_t list; + char entryname[MAX_SYSPATH]; + int idx; + + // get our entry name + Q_strncpy( entryname, prev, next - prev + 1 ); + + stringlistinit( &list ); + listdirectory( &list, dst, false ); + + for( idx = 0; idx < list.numstrings; idx++ ) + { + if( !Q_stricmp( list.strings[idx], entryname )) + break; + } + + if( idx != list.numstrings ) + { + i += Q_strncpy( &dst[i], list.strings[idx], len - i ); + if( i >= len ) // overflow! + { + Con_Printf( "%s: overflow while searching %s (appending fixed file name)\n", __FUNCTION__, path ); + return false; + } + } + else + { + stringlistfreecontents( &list ); + return false; + } + + stringlistfreecontents( &list ); + + // end of string, found file, return + if( next[0] == '\0' ) + break; + + // move pointer one character forward, find next path split character + prev = next + 1; + next = Q_strchrnul( prev, PATH_SEPARATOR ); + i += Q_strncpy( &dst[i], PATH_SEPARATOR_STR, len - i ); + if( i >= len ) // overflow! + { + Con_Printf( "%s: overflow while searching %s (path separator)\n", __FUNCTION__, path ); + return false; + } + } + + return true; +} +#endif static void FS_Close_DIR( searchpath_t *search ) { From 3d71e5d111c3b69065aa82f92fdb2ed65f9f6723 Mon Sep 17 00:00:00 2001 From: Alibek Omarov Date: Wed, 21 Dec 2022 08:02:19 +0300 Subject: [PATCH 302/490] filesystem: dir: fix merging existing cache with new directory entries --- filesystem/dir.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/filesystem/dir.c b/filesystem/dir.c index a3cde48f..394eae79 100644 --- a/filesystem/dir.c +++ b/filesystem/dir.c @@ -164,8 +164,8 @@ static void FS_MergeDirEntries( dir_t *dir, const stringlist_t *list ) } // found directory, move all entries - temp.entries[j].numentries = dir->entries[j].numentries; - temp.entries[j].entries = dir->entries[j].entries; + temp.entries[j].numentries = dir->entries[i].numentries; + temp.entries[j].entries = dir->entries[i].entries; } // now we can free old tree and replace it with temporary From b36ebc294fc29583532c0f59ca0fecb675ac6ff7 Mon Sep 17 00:00:00 2001 From: Alibek Omarov Date: Thu, 22 Dec 2022 10:54:08 +0300 Subject: [PATCH 303/490] filesystem: dir: exit from loop immediately if directory is caseinsensitive --- filesystem/dir.c | 1 + 1 file changed, 1 insertion(+) diff --git a/filesystem/dir.c b/filesystem/dir.c index 394eae79..34b0fce7 100644 --- a/filesystem/dir.c +++ b/filesystem/dir.c @@ -234,6 +234,7 @@ qboolean FS_FixFileCase( dir_t *dir, const char *path, char *dst, size_t len, qb Con_Printf( "%s: overflow while searching %s (caseinsensitive entry)\n", __FUNCTION__, path ); return false; } + break; } // populate cache if needed From 41aa867a21a90047006d0b5f8224ec5ec78c13ac Mon Sep 17 00:00:00 2001 From: Alibek Omarov Date: Mon, 26 Dec 2022 14:27:07 +0300 Subject: [PATCH 304/490] engine: common: don't try to delete now non-existing config file after backing up --- engine/common/con_utils.c | 1 - 1 file changed, 1 deletion(-) diff --git a/engine/common/con_utils.c b/engine/common/con_utils.c index 34f13460..2cd5ffc0 100644 --- a/engine/common/con_utils.c +++ b/engine/common/con_utils.c @@ -1355,7 +1355,6 @@ void Host_FinalizeConfig( file_t *f, const char *config ) FS_Close( f ); FS_Delete( backup ); FS_Rename( config, backup ); - FS_Delete( config ); FS_Rename( newcfg, config ); } From fe1aba35616e572cdeec95f467fbd34c1d56ff06 Mon Sep 17 00:00:00 2001 From: Alibek Omarov Date: Mon, 26 Dec 2022 14:39:51 +0300 Subject: [PATCH 305/490] filesystem: apply caseinsensitivity to file creation Replace fs_writedir with fs_writepath, exposing current writeable searchpath. Fix caseinsensitive FS_Search Remove unused argument from listdirectory() Minor optimizations and refactoring --- filesystem/VFileSystem009.cpp | 4 +- filesystem/dir.c | 293 ++++++++++++------------------- filesystem/filesystem.c | 79 ++++++--- filesystem/filesystem_internal.h | 7 +- 4 files changed, 170 insertions(+), 213 deletions(-) diff --git a/filesystem/VFileSystem009.cpp b/filesystem/VFileSystem009.cpp index 7bfee6ba..e9a3d54a 100644 --- a/filesystem/VFileSystem009.cpp +++ b/filesystem/VFileSystem009.cpp @@ -42,14 +42,14 @@ static inline qboolean IsIdGamedir( const char *id ) !Q_strcmp( id, "GAMEDOWNLOAD" ); } -static inline const char* IdToDir( const char *id ) +static inline const char *IdToDir( const char *id ) { if( !Q_strcmp( id, "GAME" )) return GI->gamefolder; else if( !Q_strcmp( id, "GAMEDOWNLOAD" )) return va( "%s/downloaded", GI->gamefolder ); else if( !Q_strcmp( id, "GAMECONFIG" )) - return fs_writedir; // full path here so it's totally our write allowed directory + return fs_writepath->filename; // full path here so it's totally our write allowed directory else if( !Q_strcmp( id, "PLATFORM" )) return "platform"; // stub else if( !Q_strcmp( id, "CONFIG" )) diff --git a/filesystem/dir.c b/filesystem/dir.c index 34b0fce7..4dd2fdf1 100644 --- a/filesystem/dir.c +++ b/filesystem/dir.c @@ -41,7 +41,7 @@ typedef struct dir_s struct dir_s *entries; // sorted } dir_t; -static int FS_SortDir( const void *_a, const void *_b ) +static int FS_SortDirEntries( const void *_a, const void *_b ) { const dir_t *a = _a; const dir_t *b = _b; @@ -65,25 +65,19 @@ static void FS_InitDirEntries( dir_t *dir, const stringlist_t *list ) { int i; - if( !list->numstrings ) - { - dir->numentries = DIRENTRY_EMPTY_DIRECTORY; - dir->entries = NULL; - return; - } - dir->numentries = list->numstrings; dir->entries = Mem_Malloc( fs_mempool, sizeof( dir_t ) * dir->numentries ); for( i = 0; i < list->numstrings; i++ ) { dir_t *entry = &dir->entries[i]; + Q_strncpy( entry->name, list->strings[i], sizeof( entry->name )); entry->numentries = DIRENTRY_NOT_SCANNED; entry->entries = NULL; } - qsort( dir->entries, dir->numentries, sizeof( dir->entries[0] ), FS_SortDir ); + qsort( dir->entries, dir->numentries, sizeof( dir->entries[0] ), FS_SortDirEntries ); } static void FS_PopulateDirEntries( dir_t *dir, const char *path ) @@ -102,8 +96,16 @@ static void FS_PopulateDirEntries( dir_t *dir, const char *path ) } stringlistinit( &list ); - listdirectory( &list, path, false ); - FS_InitDirEntries( dir, &list ); + listdirectory( &list, path ); + if( !list.numstrings ) + { + dir->numentries = DIRENTRY_EMPTY_DIRECTORY; + dir->entries = NULL; + } + else + { + FS_InitDirEntries( dir, &list ); + } stringlistfreecontents( &list ); #endif } @@ -112,12 +114,10 @@ static int FS_FindDirEntry( dir_t *dir, const char *name ) { int left, right; - if( dir->numentries < 0 ) - return -1; - // look for the file (binary search) left = 0; right = dir->numentries - 1; + while( left <= right ) { int middle = (left + right) / 2; @@ -142,30 +142,37 @@ static void FS_MergeDirEntries( dir_t *dir, const stringlist_t *list ) int i; dir_t temp; + // glorified realloc for sorted dir entries + // make new array and copy old entries with same name and subentries + // everything else get freed + FS_InitDirEntries( &temp, list ); - // copy all entries that has the same name and has subentries for( i = 0; i < dir->numentries; i++ ) { + dir_t *oldentry = &dir->entries[i]; + dir_t *newentry; int j; // don't care about directories without subentries - if( dir->entries == NULL ) + if( oldentry->entries == NULL ) continue; // try to find this directory in new tree - j = FS_FindDirEntry( &temp, dir->entries[i].name ); + j = FS_FindDirEntry( &temp, oldentry->name ); // not found, free memory if( j < 0 ) { - FS_FreeDirEntries( &dir->entries[i] ); + FS_FreeDirEntries( oldentry ); continue; } // found directory, move all entries - temp.entries[j].numentries = dir->entries[i].numentries; - temp.entries[j].entries = dir->entries[i].entries; + newentry = &temp.entries[j]; + + newentry->numentries = oldentry->numentries; + newentry->entries = oldentry->entries; } // now we can free old tree and replace it with temporary @@ -177,216 +184,141 @@ static void FS_MergeDirEntries( dir_t *dir, const stringlist_t *list ) static int FS_MaybeUpdateDirEntries( dir_t *dir, const char *path, const char *entryname ) { stringlist_t list; - qboolean update = false; - int idx; + int ret; stringlistinit( &list ); - listdirectory( &list, path, false ); + listdirectory( &list, path ); - // find the reason to update entries list - if( list.numstrings != dir->numentries ) + if( list.numstrings == 0 ) // empty directory { - // small optimization to not search string in the list - // and directly go updating entries - update = true; + FS_FreeDirEntries( dir ); + dir->numentries = DIRENTRY_EMPTY_DIRECTORY; + ret = -1; + } + else if( dir->numentries < 0 ) // not initialized or was empty + { + FS_InitDirEntries( dir, &list ); + ret = FS_FindDirEntry( dir, entryname ); + } + else if( list.numstrings != dir->numentries ) // quick update + { + FS_MergeDirEntries( dir, &list ); + ret = FS_FindDirEntry( dir, entryname ); } else { - for( idx = 0; idx < list.numstrings; idx++ ) + // do heavy compare if directory now have an entry we need + int i; + + for( i = 0; i < list.numstrings; i++ ) { - if( !Q_stricmp( list.strings[idx], entryname )) - { - update = true; + if( !Q_stricmp( list.strings[i], entryname )) break; - } } + + if( i != list.numstrings ) + { + FS_MergeDirEntries( dir, &list ); + ret = FS_FindDirEntry( dir, entryname ); + } + else ret = -1; } - if( !update ) - { - stringlistfreecontents( &list ); - return -1; - } - - FS_MergeDirEntries( dir, &list ); stringlistfreecontents( &list ); - return FS_FindDirEntry( dir, entryname ); + return ret; } -#if 1 -qboolean FS_FixFileCase( dir_t *dir, const char *path, char *dst, size_t len, qboolean createpath ) +static inline qboolean FS_AppendToPath( char *dst, size_t *pi, const size_t len, const char *src, const char *path, const char *err ) { - const char *prev = path; - const char *next = Q_strchrnul( prev, PATH_SEPARATOR ); - size_t i = Q_strlen( dst ); // dst is expected to have searchpath filename + size_t i = *pi; - while( true ) + i += Q_strncpy( &dst[i], src, len - i ); + *pi = i; + + if( i >= len ) { + Con_Printf( S_ERROR "FS_FixFileCase: overflow while searching %s (%s)\n", path, err ); + return false; + } + return true; +} + +qboolean FS_FixFileCase( dir_t *dir, const char *path, char *dst, const size_t len, qboolean createpath ) +{ + const char *prev, *next; + size_t i = 0; + + if( !FS_AppendToPath( dst, &i, len, dir->name, path, "init" )) + return false; + + for( prev = path, next = Q_strchrnul( prev, PATH_SEPARATOR ); + ; + prev = next + 1, next = Q_strchrnul( prev, PATH_SEPARATOR )) + { + qboolean uptodate = false; // do not run second scan if we're just updated our directory list + size_t temp; char entryname[MAX_SYSPATH]; int ret; // this subdirectory is case insensitive, just slam everything that's left if( dir->numentries == DIRENTRY_CASEINSENSITIVE ) { - i += Q_strncpy( &dst[i], prev, len - i ); - if( i >= len ) - { - Con_Printf( "%s: overflow while searching %s (caseinsensitive entry)\n", __FUNCTION__, path ); + if( !FS_AppendToPath( dst, &i, len, prev, path, "caseinsensitive entry" )) return false; - } break; } - // populate cache if needed if( dir->numentries == DIRENTRY_NOT_SCANNED ) + { + // read directory first time FS_PopulateDirEntries( dir, dst ); + uptodate = true; + } // get our entry name Q_strncpy( entryname, prev, next - prev + 1 ); - ret = FS_FindDirEntry( dir, entryname ); // didn't found, but does it exists in FS? - if( ret < 0 ) + if(( ret = FS_FindDirEntry( dir, entryname )) < 0 ) { - ret = FS_MaybeUpdateDirEntries( dir, dst, entryname ); + // if we're creating files or folders, we don't care if path doesn't exist + // so copy everything that's left and exit without an error + if( uptodate || ( ret = FS_MaybeUpdateDirEntries( dir, dst, entryname )) < 0 ) + return createpath ? FS_AppendToPath( dst, &i, len, prev, path, "create path" ) : false; - if( ret < 0 ) - { - // if we're creating files or folders, we don't care if path doesn't exist - // so copy everything that's left and exit without an error - if( createpath ) - { - i += Q_strncpy( &dst[i], prev, len - i ); - if( i >= len ) - { - Con_Printf( "%s: overflow while searching %s (create path)\n", __FUNCTION__, path ); - return false; - } - - return true; - } - return false; - } + uptodate = true; } dir = &dir->entries[ret]; - ret = Q_strncpy( &dst[i], dir->name, len - i ); + temp = i; + if( !FS_AppendToPath( dst, &temp, len, dir->name, path, "case fix" )) + return false; - // file not found, rescan... - if( !FS_SysFileOrFolderExists( dst )) + if( !uptodate && !FS_SysFileOrFolderExists( dst )) // file not found, rescan... { - // strip failed part - dst[i] = 0; + dst[i] = 0; // strip failed part - ret = FS_MaybeUpdateDirEntries( dir, dst, entryname ); - - // file not found, exit... =/ - if( ret < 0 ) - { - // if we're creating files or folders, we don't care if path doesn't exist - // so copy everything that's left and exit without an error - if( createpath ) - { - i += Q_strncpy( &dst[i], prev, len - i ); - if( i >= len ) - { - Con_Printf( "%s: overflow while searching %s (create path 2)\n", __FUNCTION__, path ); - return false; - } - - return true; - } - return false; - } + // if we're creating files or folders, we don't care if path doesn't exist + // so copy everything that's left and exit without an error + if(( ret = FS_MaybeUpdateDirEntries( dir, dst, entryname )) < 0 ) + return createpath ? FS_AppendToPath( dst, &i, len, prev, path, "create path rescan" ) : false; dir = &dir->entries[ret]; - ret = Q_strncpy( &dst[i], dir->name, len - i ); - } - - i += ret; - if( i >= len ) // overflow! - { - Con_Printf( "%s: overflow while searching %s (appending fixed file name)\n", __FUNCTION__, path ); - return false; - } - - // end of string, found file, return - if( next[0] == '\0' ) - break; - - // move pointer one character forward, find next path split character - prev = next + 1; - next = Q_strchrnul( prev, PATH_SEPARATOR ); - i += Q_strncpy( &dst[i], PATH_SEPARATOR_STR, len - i ); - if( i >= len ) // overflow! - { - Con_Printf( "%s: overflow while searching %s (path separator)\n", __FUNCTION__, path ); - return false; - } - } - - return true; -} -#else -qboolean FS_FixFileCase( dir_t *dir, const char *path, char *dst, size_t len, qboolean createpath ) -{ - const char *prev = path; - const char *next = Q_strchrnul( prev, PATH_SEPARATOR ); - size_t i = Q_strlen( dst ); // dst is expected to have searchpath filename - - while( true ) - { - stringlist_t list; - char entryname[MAX_SYSPATH]; - int idx; - - // get our entry name - Q_strncpy( entryname, prev, next - prev + 1 ); - - stringlistinit( &list ); - listdirectory( &list, dst, false ); - - for( idx = 0; idx < list.numstrings; idx++ ) - { - if( !Q_stricmp( list.strings[idx], entryname )) - break; - } - - if( idx != list.numstrings ) - { - i += Q_strncpy( &dst[i], list.strings[idx], len - i ); - if( i >= len ) // overflow! - { - Con_Printf( "%s: overflow while searching %s (appending fixed file name)\n", __FUNCTION__, path ); + if( !FS_AppendToPath( dst, &temp, len, dir->name, path, "case fix rescan" )) return false; - } } - else - { - stringlistfreecontents( &list ); - return false; - } - - stringlistfreecontents( &list ); + i = temp; // end of string, found file, return - if( next[0] == '\0' ) + if( next[0] == '\0' || ( next[0] == PATH_SEPARATOR && next[1] == '\0' )) break; - // move pointer one character forward, find next path split character - prev = next + 1; - next = Q_strchrnul( prev, PATH_SEPARATOR ); - i += Q_strncpy( &dst[i], PATH_SEPARATOR_STR, len - i ); - if( i >= len ) // overflow! - { - Con_Printf( "%s: overflow while searching %s (path separator)\n", __FUNCTION__, path ); + if( !FS_AppendToPath( dst, &i, len, PATH_SEPARATOR_STR, path, "path separator" )) return false; - } } return true; } -#endif static void FS_Close_DIR( searchpath_t *search ) { @@ -403,7 +335,6 @@ static int FS_FindFile_DIR( searchpath_t *search, const char *path, char *fixedn { char netpath[MAX_SYSPATH]; - Q_strncpy( netpath, search->filename, sizeof( netpath )); if( !FS_FixFileCase( search->dir, path, netpath, sizeof( netpath ), false )) return -1; @@ -438,12 +369,16 @@ static void FS_Search_DIR( searchpath_t *search, stringlist_t *list, const char if( basepathlength ) memcpy( basepath, pattern, basepathlength ); basepath[basepathlength] = '\0'; - Q_snprintf( netpath, sizeof( netpath ), "%s%s", search->filename, basepath ); + if( !FS_FixFileCase( search->dir, basepath, netpath, sizeof( netpath ), false )) + { + Mem_Free( basepath ); + return; + } stringlistinit( &dirlist ); - listdirectory( &dirlist, netpath, caseinsensitive ); + listdirectory( &dirlist, netpath ); - Q_strncpy( temp, basepath, sizeof( temp ) ); + Q_strncpy( temp, basepath, sizeof( temp )); for( dirlistindex = 0; dirlistindex < dirlist.numstrings; dirlistindex++ ) { @@ -500,7 +435,7 @@ void FS_InitDirectorySearchpath( searchpath_t *search, const char *path, int fla // create cache root search->dir = Mem_Malloc( fs_mempool, sizeof( dir_t )); - search->dir->name[0] = 0; // root has no filename, unused + Q_strncpy( search->dir->name, search->filename, sizeof( search->dir->name )); FS_PopulateDirEntries( search->dir, path ); } diff --git a/filesystem/filesystem.c b/filesystem/filesystem.c index c3795909..a7b22415 100644 --- a/filesystem/filesystem.c +++ b/filesystem/filesystem.c @@ -50,7 +50,6 @@ poolhandle_t fs_mempool; searchpath_t *fs_searchpaths = NULL; // chain char fs_rodir[MAX_SYSPATH]; char fs_rootdir[MAX_SYSPATH]; -char fs_writedir[MAX_SYSPATH]; // path that game allows to overwrite, delete and rename files (and create new of course) searchpath_t *fs_writepath; static char fs_basedir[MAX_SYSPATH]; // base game directory @@ -193,10 +192,8 @@ static void listlowercase( stringlist_t *list ) } } -void listdirectory( stringlist_t *list, const char *path, qboolean lowercase ) +void listdirectory( stringlist_t *list, const char *path ) { - int i; - signed char *c; #if XASH_WIN32 char pattern[4096]; struct _finddata_t n_file; @@ -307,7 +304,7 @@ static qboolean FS_AddArchive_Fullpath( const char *file, qboolean *already_load ================ FS_AddGameDirectory -Sets fs_writedir, adds the directory to the head of the path, +Sets fs_writepath, adds the directory to the head of the path, then loads and adds pak1.pak pak2.pak ... ================ */ @@ -319,7 +316,7 @@ void FS_AddGameDirectory( const char *dir, uint flags ) int i; stringlistinit( &list ); - listdirectory( &list, dir, false ); + listdirectory( &list, dir ); stringlistsort( &list ); // add any PAK package in the directory @@ -351,10 +348,7 @@ void FS_AddGameDirectory( const char *dir, uint flags ) // (unpacked files have the priority over packed files) search = FS_AddDir_Fullpath( dir, NULL, flags ); if( !FBitSet( flags, FS_NOWRITE_PATH )) - { - Q_strncpy( fs_writedir, dir, sizeof( fs_writedir )); fs_writepath = search; - } } /* @@ -1318,7 +1312,7 @@ qboolean FS_InitStdio( qboolean caseinsensitive, const char *rootdir, const char } stringlistinit( &dirs ); - listdirectory( &dirs, fs_rodir, false ); + listdirectory( &dirs, fs_rodir ); stringlistsort( &dirs ); for( i = 0; i < dirs.numstrings; i++ ) @@ -1342,7 +1336,7 @@ qboolean FS_InitStdio( qboolean caseinsensitive, const char *rootdir, const char // validate directories stringlistinit( &dirs ); - listdirectory( &dirs, "./", false ); + listdirectory( &dirs, "./" ); stringlistsort( &dirs ); for( i = 0; i < dirs.numstrings; i++ ) @@ -1794,8 +1788,11 @@ file_t *FS_Open( const char *filepath, const char *mode, qboolean gamedironly ) char real_path[MAX_SYSPATH]; // open the file on disk directly - Q_sprintf( real_path, "%s/%s", fs_writedir, filepath ); + if( !FS_FixFileCase( fs_writepath->dir, filepath, real_path, sizeof( real_path ), true )) + return NULL; + FS_CreatePath( real_path ); // Create directories up to the file + return FS_SysOpen( real_path, mode ); } @@ -2455,25 +2452,40 @@ rename specified file from gamefolder */ qboolean FS_Rename( const char *oldname, const char *newname ) { - char oldpath[MAX_SYSPATH], newpath[MAX_SYSPATH]; - qboolean iRet; + char oldname2[MAX_SYSPATH], newname2[MAX_SYSPATH], oldpath[MAX_SYSPATH], newpath[MAX_SYSPATH]; + int ret; - if( !oldname || !newname || !*oldname || !*newname ) + if( !COM_CheckString( oldname ) || !COM_CheckString( newname )) return false; // no work done if( !Q_stricmp( oldname, newname )) return true; - Q_snprintf( oldpath, sizeof( oldpath ), "%s%s", fs_writedir, oldname ); - Q_snprintf( newpath, sizeof( newpath ), "%s%s", fs_writedir, newname ); + // fix up slashes + Q_strncpy( oldname2, oldname, sizeof( oldname2 )); + Q_strncpy( newname2, newname, sizeof( newname2 )); - COM_FixSlashes( oldpath ); - COM_FixSlashes( newpath ); + COM_FixSlashes( oldname2 ); + COM_FixSlashes( newname2 ); - iRet = rename( oldpath, newpath ); + // file does not exist + if( !FS_FixFileCase( fs_writepath->dir, oldname2, oldpath, sizeof( oldpath ), false )) + return false; - return (iRet == 0); + // exit if overflowed + if( !FS_FixFileCase( fs_writepath->dir, newname2, newpath, sizeof( newpath ), true )) + return false; + + ret = rename( oldpath, newpath ); + if( ret < 0 ) + { + Con_Printf( "%s: failed to rename file %s (%s) to %s (%s): %s\n", + __FUNCTION__, oldpath, oldname2, newpath, newname2, strerror( errno )); + return false; + } + + return true; } /* @@ -2485,17 +2497,26 @@ delete specified file from gamefolder */ qboolean GAME_EXPORT FS_Delete( const char *path ) { - char real_path[MAX_SYSPATH]; - qboolean iRet; + char path2[MAX_SYSPATH], real_path[MAX_SYSPATH]; + int ret; - if( !path || !*path ) + if( !COM_CheckString( path )) return false; - Q_snprintf( real_path, sizeof( real_path ), "%s%s", fs_writedir, path ); - COM_FixSlashes( real_path ); - iRet = remove( real_path ); + Q_strncpy( path2, path, sizeof( path2 )); + COM_FixSlashes( path2 ); - return (iRet == 0); + if( !FS_FixFileCase( fs_writepath->dir, path2, real_path, sizeof( real_path ), true )) + return true; + + ret = remove( real_path ); + if( ret < 0 ) + { + Con_Printf( "%s: failed to delete file %s (%s): %s\n", __FUNCTION__, real_path, path, strerror( errno )); + return false; + } + + return true; } /* @@ -2556,7 +2577,7 @@ search_t *FS_Search( const char *pattern, int caseinsensitive, int gamedironly ) { if( gamedironly && !FBitSet( searchpath->flags, FS_GAMEDIRONLY_SEARCH_FLAGS )) continue; - + searchpath->pfnSearch( searchpath, &resultlist, pattern, caseinsensitive ); } diff --git a/filesystem/filesystem_internal.h b/filesystem/filesystem_internal.h index 66e0886d..fb586ebb 100644 --- a/filesystem/filesystem_internal.h +++ b/filesystem/filesystem_internal.h @@ -70,7 +70,7 @@ typedef struct searchpath_s string filename; int type; int flags; - + union { dir_t *dir; @@ -91,12 +91,12 @@ typedef struct searchpath_s extern fs_globals_t FI; extern searchpath_t *fs_searchpaths; +extern searchpath_t *fs_writepath; extern poolhandle_t fs_mempool; extern fs_interface_t g_engfuncs; extern qboolean fs_ext_path; extern char fs_rodir[MAX_SYSPATH]; extern char fs_rootdir[MAX_SYSPATH]; -extern char fs_writedir[MAX_SYSPATH]; extern fs_api_t g_api; #define GI FI.GameInfo @@ -164,7 +164,7 @@ void stringlistinit( stringlist_t *list ); void stringlistfreecontents( stringlist_t *list ); void stringlistappend( stringlist_t *list, char *text ); void stringlistsort( stringlist_t *list ); -void listdirectory( stringlist_t *list, const char *path, qboolean lowercase ); +void listdirectory( stringlist_t *list, const char *path ); // filesystem ops int FS_FileExists( const char *filename, int gamedironly ); @@ -212,6 +212,7 @@ qboolean FS_AddZip_Fullpath( const char *zipfile, qboolean *already_loaded, int // dir.c // searchpath_t *FS_AddDir_Fullpath( const char *path, qboolean *already_loaded, int flags ); +qboolean FS_FixFileCase( dir_t *dir, const char *path, char *dst, const size_t len, qboolean createpath ); void FS_InitDirectorySearchpath( searchpath_t *search, const char *path, int flags ); #ifdef __cplusplus From c454e37064265c4a7526b0afcf998cb56d649065 Mon Sep 17 00:00:00 2001 From: Alibek Omarov Date: Mon, 26 Dec 2022 18:58:03 +0300 Subject: [PATCH 306/490] filesystem: allow to init with NULL overrides --- filesystem/filesystem.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/filesystem/filesystem.c b/filesystem/filesystem.c index a7b22415..43f5350a 100644 --- a/filesystem/filesystem.c +++ b/filesystem/filesystem.c @@ -2731,7 +2731,7 @@ fs_api_t g_api = int EXPORT GetFSAPI( int version, fs_api_t *api, fs_globals_t **globals, fs_interface_t *engfuncs ) { - if( !FS_InitInterface( version, engfuncs )) + if( engfuncs && !FS_InitInterface( version, engfuncs )) return 0; memcpy( api, &g_api, sizeof( *api )); From 2febe632c5cf3a9127c0e71dab9bbe46e4a474a7 Mon Sep 17 00:00:00 2001 From: Alibek Omarov Date: Mon, 26 Dec 2022 18:58:44 +0300 Subject: [PATCH 307/490] filesystem: add caseinsensitive emulation test --- filesystem/tests/caseinsensitive.c | 122 +++++++++++++++++++++++++++++ filesystem/wscript | 22 +++++- 2 files changed, 143 insertions(+), 1 deletion(-) create mode 100644 filesystem/tests/caseinsensitive.c diff --git a/filesystem/tests/caseinsensitive.c b/filesystem/tests/caseinsensitive.c new file mode 100644 index 00000000..f0478630 --- /dev/null +++ b/filesystem/tests/caseinsensitive.c @@ -0,0 +1,122 @@ +#include "port.h" +#include "build.h" +#include +#include +#include +#include "filesystem.h" +#if XASH_POSIX +#include +#define LoadLibrary( x ) dlopen( x, RTLD_NOW ) +#define GetProcAddress( x, y ) dlsym( x, y ) +#define FreeLibrary( x ) dlclose( x ) +#elif XASH_WIN32 +#include +#endif + +void *g_hModule; +FSAPI g_pfnGetFSAPI; +fs_api_t g_fs; +fs_globals_t *g_nullglobals; + + +static qboolean LoadFilesystem( void ) +{ + g_hModule = LoadLibrary( "filesystem_stdio." OS_LIB_EXT ); + if( !g_hModule ) + return false; + + g_pfnGetFSAPI = (void*)GetProcAddress( g_hModule, GET_FS_API ); + if( !g_pfnGetFSAPI ) + return false; + + if( !g_pfnGetFSAPI( FS_API_VERSION, &g_fs, &g_nullglobals, NULL )) + return false; + + return true; +} + +static qboolean CheckFileContents( const char *path, const void *buf, fs_offset_t size ) +{ + fs_offset_t len; + byte *data; + + data = g_fs.LoadFile( path, &len, true ); + if( !data ) + { + printf( "LoadFile fail\n" ); + return false; + } + + if( len != size ) + { + printf( "LoadFile sizeof fail\n" ); + free( data ); + return false; + } + + if( memcmp( data, buf, size )) + { + printf( "LoadFile magic fail\n" ); + free( data ); + return false; + } + + free( data ); + return true; +} + +static qboolean TestCaseinsensitive( void ) +{ + file_t *f1; + FILE *f2; + int magic = rand(); + + // create game dir for us + g_fs.AddGameDirectory( "./", FS_GAMEDIR_PATH ); + + // create some files first and write data + f1 = g_fs.Open( "FOO/Bar.bin", "wb", true ); + g_fs.Write( f1, &magic, sizeof( magic )); + g_fs.Close( f1 ); + + // try to search it with different file name + if( !g_fs.FileExists( "fOO/baR.bin", true )) + { + printf( "FileExists fail\n" ); + return false; + } + + // create a file directly, to check if cache can re-read + f2 = fopen( "FOO/Baz.bin", "wb" ); + fwrite( &magic, sizeof( magic ), 1, f2 ); + fclose( f2 ); + + // try to open first file back but with different file name case + if( !CheckFileContents( "foo/bar.BIN", &magic, sizeof( magic ))) + return false; + + // try to open second file that we created directly + if( !CheckFileContents( "Foo/BaZ.Bin", &magic, sizeof( magic ))) + return false; + + g_fs.Delete( "foo/Baz.biN" ); + g_fs.Delete( "foo/bar.bin" ); + g_fs.Delete( "Foo" ); + + return true; +} + +int main( void ) +{ + if( !LoadFilesystem() ) + return EXIT_FAILURE; + + srand( time( NULL )); + + if( !TestCaseinsensitive()) + return EXIT_FAILURE; + + printf( "success\n" ); + + return EXIT_SUCCESS; +} diff --git a/filesystem/wscript b/filesystem/wscript index 2b8d8a8b..362892b1 100644 --- a/filesystem/wscript +++ b/filesystem/wscript @@ -1,7 +1,12 @@ #!/usr/bin/env python +from waflib.Tools import waf_unit_test + def options(opt): - pass + grp = opt.add_option_group('filesystem_stdio options') + + grp.add_option('--enable-fs-tests', action='store_true', dest = 'FS_TESTS', default = False, + help = 'enable filesystem_stdio tests') def configure(conf): nortti = { @@ -10,6 +15,8 @@ def configure(conf): } conf.env.append_unique('CXXFLAGS', conf.get_flags_by_compiler(nortti, conf.env.COMPILER_CC)) + conf.env.FS_TESTS = conf.options.FS_TESTS + if conf.env.DEST_OS != 'android': if conf.env.cxxshlib_PATTERN.startswith('lib'): conf.env.cxxshlib_PATTERN = conf.env.cxxshlib_PATTERN[3:] @@ -22,3 +29,16 @@ def build(bld): use = 'filesystem_includes public', install_path = bld.env.LIBDIR, subsystem = bld.env.MSVC_SUBSYSTEM) + + if bld.env.FS_TESTS: + # build in same module, so dynamic linking will work + # for now (until we turn libpublic to shared module lol) + bld.program(features = 'test', + source = 'tests/caseinsensitive.c', + target = 'test_caseinsensitive', + use = 'filesystem_includes public DL', + rpath = '$ORIGIN', + subsystem = bld.env.CONSOLE_SUBSYSTEM, + install_path = None) + bld.add_post_fun(waf_unit_test.summary) + bld.add_post_fun(waf_unit_test.set_exit_code) From 256fe7ede9d73ca0f2ed6fda81ab95368d3bf371 Mon Sep 17 00:00:00 2001 From: Alibek Omarov Date: Mon, 26 Dec 2022 19:42:39 +0300 Subject: [PATCH 308/490] scripts: make sure to test filesystem on CI --- scripts/cirrus/build_freebsd.sh | 4 ++-- scripts/gha/build_linux.sh | 4 ++-- scripts/gha/build_win32.sh | 2 +- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/scripts/cirrus/build_freebsd.sh b/scripts/cirrus/build_freebsd.sh index b1b06207..97554dcf 100755 --- a/scripts/cirrus/build_freebsd.sh +++ b/scripts/cirrus/build_freebsd.sh @@ -14,9 +14,9 @@ build_engine() cd "$CIRRUS_WORKING_DIR" || die if [ "$APP" = "xashds" ]; then - ./waf configure -T release -d || die + ./waf configure -T release -d --enable-fs-tests || die elif [ "$APP" = "xash3d-fwgs" ]; then - ./waf configure -T release --enable-stb --enable-utils --enable-gl4es --enable-gles1 --enable-gles2 || die + ./waf configure -T release --enable-stb --enable-utils --enable-gl4es --enable-gles1 --enable-gles2 --enable-fs-tests || die else die fi diff --git a/scripts/gha/build_linux.sh b/scripts/gha/build_linux.sh index 3ed86cae..c9ef119f 100755 --- a/scripts/gha/build_linux.sh +++ b/scripts/gha/build_linux.sh @@ -50,9 +50,9 @@ build_engine() fi if [ "$1" = "dedicated" ]; then - ./waf configure -T release -d $AMD64 || die + ./waf configure -T release -d $AMD64 --enable-fs-tests || die elif [ "$1" = "full" ]; then - ./waf configure --sdl2=SDL2_linux -T release --enable-stb $AMD64 --enable-utils || die + ./waf configure --sdl2=SDL2_linux -T release --enable-stb $AMD64 --enable-utils --enable-fs-tests || die else die fi diff --git a/scripts/gha/build_win32.sh b/scripts/gha/build_win32.sh index 2416ac2c..940a9064 100755 --- a/scripts/gha/build_win32.sh +++ b/scripts/gha/build_win32.sh @@ -11,7 +11,7 @@ fi # NOTE: to build with other version use --msvc_version during configuration # NOTE: sometimes you may need to add WinSDK to %PATH% -./waf.bat configure -s "SDL2_VC" -T "release" --enable-utils --prefix=`pwd` $AMD64 || die +./waf.bat configure -s "SDL2_VC" -T "release" --enable-utils --enable-fs-tests --prefix=`pwd` $AMD64 || die ./waf.bat build -v || die ./waf.bat install || die From 339711c3c7a534ff281ac9cbd743bdfb76580cca Mon Sep 17 00:00:00 2001 From: Alibek Omarov Date: Mon, 26 Dec 2022 21:27:04 +0300 Subject: [PATCH 309/490] filesystem: dir: check casefold directory flag --- filesystem/dir.c | 50 ++++++++++++++++++++++++++++++++++++++++-------- 1 file changed, 42 insertions(+), 8 deletions(-) diff --git a/filesystem/dir.c b/filesystem/dir.c index 4dd2fdf1..b1ff0c69 100644 --- a/filesystem/dir.c +++ b/filesystem/dir.c @@ -13,14 +13,23 @@ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. */ +#include "build.h" #include #include #include -#if XASH_POSIX -#include -#endif #include #include +#if XASH_POSIX +#include +#include +#endif +#if XASH_LINUX +#include +#ifndef FS_CASEFOLD_FL // for compatibility with older distros +#define FS_CASEFOLD_FL 0x40000000 +#endif // FS_CASEFOLD_FL +#endif // XASH_LINUX + #include "port.h" #include "filesystem_internal.h" #include "crtlib.h" @@ -41,6 +50,29 @@ typedef struct dir_s struct dir_s *entries; // sorted } dir_t; +static qboolean Platform_GetDirectoryCaseSensitivity( const char *dir ) +{ +#if XASH_WIN32 + return false; +#elif XASH_LINUX && defined( FS_IOC_GETFLAGS ) + int flags = 0; + int fd; + + fd = open( dir, O_RDONLY | O_NONBLOCK ); + if( fd < 0 ) + return true; + + if( ioctl( fd, FS_IOC_GETFLAGS, &flags ) < 0 ) + return true; + + close( fd ); + + return !FBitSet( flags, FS_CASEFOLD_FL ); +#else + return true; +#endif +} + static int FS_SortDirEntries( const void *_a, const void *_b ) { const dir_t *a = _a; @@ -82,10 +114,6 @@ static void FS_InitDirEntries( dir_t *dir, const stringlist_t *list ) static void FS_PopulateDirEntries( dir_t *dir, const char *path ) { -#if XASH_WIN32 // Windows is always case insensitive - dir->numentries = DIRENTRY_CASEINSENSITIVE; - dir->entries = NULL; -#else stringlist_t list; if( !FS_SysFolderExists( path )) @@ -95,6 +123,13 @@ static void FS_PopulateDirEntries( dir_t *dir, const char *path ) return; } + if( !Platform_GetDirectoryCaseSensitivity( path )) + { + dir->numentries = DIRENTRY_CASEINSENSITIVE; + dir->entries = NULL; + return; + } + stringlistinit( &list ); listdirectory( &list, path ); if( !list.numstrings ) @@ -107,7 +142,6 @@ static void FS_PopulateDirEntries( dir_t *dir, const char *path ) FS_InitDirEntries( dir, &list ); } stringlistfreecontents( &list ); -#endif } static int FS_FindDirEntry( dir_t *dir, const char *name ) From 52061621ac71079bdf88cb90c7f057fb3b040ee4 Mon Sep 17 00:00:00 2001 From: Alibek Omarov Date: Tue, 27 Dec 2022 23:10:11 +0300 Subject: [PATCH 310/490] engine: client: do not let client.dll overwrite usercmd that's was read from demo --- engine/client/cl_main.c | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/engine/client/cl_main.c b/engine/client/cl_main.c index f0463be5..06980afd 100644 --- a/engine/client/cl_main.c +++ b/engine/client/cl_main.c @@ -622,7 +622,7 @@ CL_CreateCmd */ void CL_CreateCmd( void ) { - usercmd_t cmd; + usercmd_t nullcmd, *cmd; runcmd_t *pcmd; vec3_t angles; qboolean active; @@ -635,7 +635,6 @@ void CL_CreateCmd( void ) // store viewangles in case it's will be freeze VectorCopy( cl.viewangles, angles ); ms = bound( 1, host.frametime * 1000, 255 ); - memset( &cmd, 0, sizeof( cmd )); input_override = 0; CL_SetSolidEntities(); @@ -654,12 +653,18 @@ void CL_CreateCmd( void ) pcmd->receivedtime = -1.0; pcmd->heldback = false; pcmd->sendsize = 0; + cmd = &pcmd->cmd; + } + else + { + memset( &nullcmd, 0, sizeof( nullcmd )); + cmd = &nullcmd; } active = (( cls.signon == SIGNONS ) && !cl.paused && !cls.demoplayback ); Platform_PreCreateMove(); - clgame.dllFuncs.CL_CreateMove( host.frametime, &pcmd->cmd, active ); - IN_EngineAppendMove( host.frametime, &pcmd->cmd, active ); + clgame.dllFuncs.CL_CreateMove( host.frametime, cmd, active ); + IN_EngineAppendMove( host.frametime, cmd, active ); CL_PopPMStates(); From e5b32fe8ac5058559b9320e85f13d0f6ae878b01 Mon Sep 17 00:00:00 2001 From: Alibek Omarov Date: Fri, 30 Dec 2022 01:57:10 +0300 Subject: [PATCH 311/490] engine: client: force nearest filter for HUD textures and sprites to avoid artifacts with hud_scale --- engine/client/cl_game.c | 5 +++++ engine/client/cl_scrn.c | 4 ++-- 2 files changed, 7 insertions(+), 2 deletions(-) diff --git a/engine/client/cl_game.c b/engine/client/cl_game.c index a07e1e5d..4ceed3b8 100644 --- a/engine/client/cl_game.c +++ b/engine/client/cl_game.c @@ -1162,6 +1162,11 @@ static qboolean CL_LoadHudSprite( const char *szSpriteName, model_t *m_pSprite, // it's hud sprite, make difference names to prevent free shared textures if( type == SPR_CLIENT || type == SPR_HUDSPRITE ) SetBits( m_pSprite->flags, MODEL_CLIENT ); + + // force nearest filter for hud sprites to have less artifacts with hud_scale + if( type == SPR_HUDSPRITE ) + SetBits( texFlags, TF_NEAREST ); + m_pSprite->numtexinfo = texFlags; // store texFlags into numtexinfo if( !FS_FileExists( szSpriteName, false ) ) diff --git a/engine/client/cl_scrn.c b/engine/client/cl_scrn.c index 22cf9280..a80763bb 100644 --- a/engine/client/cl_scrn.c +++ b/engine/client/cl_scrn.c @@ -587,7 +587,7 @@ qboolean SCR_LoadFixedWidthFont( const char *fontname ) if( !FS_FileExists( fontname, false )) return false; - cls.creditsFont.hFontTexture = ref.dllFuncs.GL_LoadTexture( fontname, NULL, 0, TF_IMAGE|TF_KEEP_SOURCE ); + cls.creditsFont.hFontTexture = ref.dllFuncs.GL_LoadTexture( fontname, NULL, 0, TF_IMAGE|TF_KEEP_SOURCE|TF_NEAREST ); R_GetTextureParms( &fontWidth, NULL, cls.creditsFont.hFontTexture ); cls.creditsFont.charHeight = clgame.scrInfo.iCharHeight = fontWidth / 16; cls.creditsFont.type = FONT_FIXED; @@ -619,7 +619,7 @@ qboolean SCR_LoadVariableWidthFont( const char *fontname ) if( !FS_FileExists( fontname, false )) return false; - cls.creditsFont.hFontTexture = ref.dllFuncs.GL_LoadTexture( fontname, NULL, 0, TF_IMAGE ); + cls.creditsFont.hFontTexture = ref.dllFuncs.GL_LoadTexture( fontname, NULL, 0, TF_IMAGE|TF_NEAREST ); R_GetTextureParms( &fontWidth, NULL, cls.creditsFont.hFontTexture ); // half-life font with variable chars witdh From eb7eb4acbcba761c414c4a5e8ca81e07653a6fd2 Mon Sep 17 00:00:00 2001 From: Alibek Omarov Date: Fri, 30 Dec 2022 01:58:26 +0300 Subject: [PATCH 312/490] engine: client: cl_scrn: minor refactoring --- engine/client/cl_scrn.c | 23 +++++++++++++++-------- 1 file changed, 15 insertions(+), 8 deletions(-) diff --git a/engine/client/cl_scrn.c b/engine/client/cl_scrn.c index a80763bb..3949d4ae 100644 --- a/engine/client/cl_scrn.c +++ b/engine/client/cl_scrn.c @@ -577,7 +577,7 @@ void SCR_UpdateScreen( void ) V_PostRender(); } -qboolean SCR_LoadFixedWidthFont( const char *fontname ) +static qboolean SCR_LoadFixedWidthFont( const char *fontname ) { int i, fontWidth; @@ -606,7 +606,7 @@ qboolean SCR_LoadFixedWidthFont( const char *fontname ) return true; } -qboolean SCR_LoadVariableWidthFont( const char *fontname ) +static qboolean SCR_LoadVariableWidthFont( const char *fontname ) { int i, fontWidth; byte *buffer; @@ -657,21 +657,28 @@ INTERNAL RESOURCE */ void SCR_LoadCreditsFont( void ) { - const char *path = "gfx/creditsfont.fnt"; - dword crc; + qboolean success = false; + dword crc = 0; // replace default gfx.wad textures by current charset's font if( !CRC32_File( &crc, "gfx.wad" ) || crc == 0x49eb9f16 ) { - const char *path2 = va("creditsfont_%s.fnt", Cvar_VariableString( "con_charset" ) ); - if( FS_FileExists( path2, false ) ) - path = path2; + string charsetFnt; + + if( Q_snprintf( charsetFnt, sizeof( charsetFnt ), + "creditsfont_%s.fnt", Cvar_VariableString( "con_charset" )) > 0 ) + { + if( FS_FileExists( charsetFnt, false )) + success = SCR_LoadVariableWidthFont( charsetFnt ); + } } - if( !SCR_LoadVariableWidthFont( path )) + if( !success && !SCR_LoadVariableWidthFont( "gfx/creditsfont.fnt" )) { if( !SCR_LoadFixedWidthFont( "gfx/conchars" )) + { Con_DPrintf( S_ERROR "failed to load HUD font\n" ); + } } } From e621c9860224c14c22423658cd50607718e6c699 Mon Sep 17 00:00:00 2001 From: Alibek Omarov Date: Fri, 30 Dec 2022 02:03:43 +0300 Subject: [PATCH 313/490] engine: network: do not crash if one of v4 or v6 socket opening failed but crash if both. Fix hostport/clientport cvars usage --- engine/common/net_ws.c | 33 ++++++++++++++++++++++----------- 1 file changed, 22 insertions(+), 11 deletions(-) diff --git a/engine/common/net_ws.c b/engine/common/net_ws.c index b25173b9..58acec85 100644 --- a/engine/common/net_ws.c +++ b/engine/common/net_ws.c @@ -1870,13 +1870,10 @@ static void NET_OpenIP( qboolean change_port, int *sockets, const char *net_ifac if( !NET_IsSocketValid( sockets[NS_SERVER] )) { - port = net_iphostport->value; + port = hostport; if( !port ) { - if( sv_nat ) - port = PORT_ANY; - else - port = net_hostport->value; + port = sv_nat ? PORT_ANY : net_hostport->value; if( !port ) port = PORT_SERVER; // forcing to default @@ -1884,7 +1881,7 @@ static void NET_OpenIP( qboolean change_port, int *sockets, const char *net_ifac sockets[NS_SERVER] = NET_IPSocket( net_iface, port, family ); if( !NET_IsSocketValid( sockets[NS_SERVER] ) && Host_IsDedicated( )) - Host_Error( "Couldn't allocate dedicated server IP port %d.\n", port ); + return; } // dedicated servers don't need client ports @@ -1902,13 +1899,10 @@ static void NET_OpenIP( qboolean change_port, int *sockets, const char *net_ifac if( !NET_IsSocketValid( sockets[NS_CLIENT] )) { - port = net_ipclientport->value; + port = clientport; if( !port ) { - if( cl_nat ) - port = PORT_ANY; - else - port = net_clientport->value; + port = cl_nat ? PORT_ANY : net_clientport->value; if( !port ) port = PORT_ANY; // forcing to default @@ -1918,6 +1912,8 @@ static void NET_OpenIP( qboolean change_port, int *sockets, const char *net_ifac if( !NET_IsSocketValid( sockets[NS_CLIENT] )) sockets[NS_CLIENT] = NET_IPSocket( net_ipname->string, PORT_ANY, family ); } + + return; } /* @@ -2023,6 +2019,21 @@ void NET_Config( qboolean multiplayer, qboolean changeport ) if( net.allow_ip6 ) NET_OpenIP( changeport, net.ip6_sockets, net_ip6name->string, net_ip6hostport->value, net_ip6clientport->value, AF_INET6 ); + // validate sockets for dedicated + if( Host_IsDedicated( )) + { + qboolean nov4, nov6; + nov4 = net.allow_ip && NET_IsSocketError( net.ip_sockets[NS_SERVER] ); + nov6 = net.allow_ip6 && NET_IsSocketError( net.ip6_sockets[NS_SERVER] ); + + if( nov4 && nov6 ) + Host_Error( "Couldn't allocate IPv4 and IPv6 server ports." ); + else if( nov4 && !nov6 ) + Con_Printf( S_ERROR "Couldn't allocate IPv4 server port" ); + else if( !nov4 && nov6 ) + Con_Printf( S_ERROR "Couldn't allocate IPv6 server_port" ); + } + // get our local address, if possible if( bFirst ) { From 42a3c7b05974fa4aef51e2f509a5b3151a272c40 Mon Sep 17 00:00:00 2001 From: Alibek Omarov Date: Tue, 3 Jan 2023 00:24:57 +0300 Subject: [PATCH 314/490] filesystem: dir: fix dir entry cache init in case of directory was empty previously --- filesystem/dir.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/filesystem/dir.c b/filesystem/dir.c index b1ff0c69..05b016e6 100644 --- a/filesystem/dir.c +++ b/filesystem/dir.c @@ -210,6 +210,7 @@ static void FS_MergeDirEntries( dir_t *dir, const stringlist_t *list ) } // now we can free old tree and replace it with temporary + // do not add null check there! If we hit it, it's probably a logic error! Mem_Free( dir->entries ); dir->numentries = temp.numentries; dir->entries = temp.entries; @@ -229,7 +230,7 @@ static int FS_MaybeUpdateDirEntries( dir_t *dir, const char *path, const char *e dir->numentries = DIRENTRY_EMPTY_DIRECTORY; ret = -1; } - else if( dir->numentries < 0 ) // not initialized or was empty + else if( dir->numentries <= DIRENTRY_EMPTY_DIRECTORY ) // not initialized or was empty { FS_InitDirEntries( dir, &list ); ret = FS_FindDirEntry( dir, entryname ); From e5763e2e9a542c694ba1a52a564782d32277c97a Mon Sep 17 00:00:00 2001 From: Alibek Omarov Date: Tue, 3 Jan 2023 01:15:24 +0300 Subject: [PATCH 315/490] filesystem: dir: check dir casesensitivity after dir entries list was properly initialized --- filesystem/dir.c | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/filesystem/dir.c b/filesystem/dir.c index 05b016e6..f6e435dd 100644 --- a/filesystem/dir.c +++ b/filesystem/dir.c @@ -295,6 +295,13 @@ qboolean FS_FixFileCase( dir_t *dir, const char *path, char *dst, const size_t l char entryname[MAX_SYSPATH]; int ret; + if( dir->numentries == DIRENTRY_NOT_SCANNED ) + { + // read directory first time + FS_PopulateDirEntries( dir, dst ); + uptodate = true; + } + // this subdirectory is case insensitive, just slam everything that's left if( dir->numentries == DIRENTRY_CASEINSENSITIVE ) { @@ -303,13 +310,6 @@ qboolean FS_FixFileCase( dir_t *dir, const char *path, char *dst, const size_t l break; } - if( dir->numentries == DIRENTRY_NOT_SCANNED ) - { - // read directory first time - FS_PopulateDirEntries( dir, dst ); - uptodate = true; - } - // get our entry name Q_strncpy( entryname, prev, next - prev + 1 ); From c1287b3950652579dc158552b42b303c41f06b32 Mon Sep 17 00:00:00 2001 From: Alibek Omarov Date: Tue, 3 Jan 2023 03:19:46 +0300 Subject: [PATCH 316/490] engine: client: speed up reconnect for legacy servers --- engine/client/cl_main.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/engine/client/cl_main.c b/engine/client/cl_main.c index 06980afd..353c1350 100644 --- a/engine/client/cl_main.c +++ b/engine/client/cl_main.c @@ -1189,7 +1189,7 @@ void CL_CheckForResend( void ) if( bandwidthTest ) Con_Printf( "Connecting to %s... [retry #%i, max 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 ); @@ -1681,6 +1681,8 @@ void CL_Reconnect_f( void ) if( COM_CheckString( cls.servername )) { + qboolean legacy = cls.legacymode; + if( cls.state >= ca_connected ) CL_Disconnect(); @@ -1688,6 +1690,7 @@ void CL_Reconnect_f( void ) cls.demonum = cls.movienum = -1; // not in the demo loop now cls.state = ca_connecting; cls.signon = 0; + cls.legacymode = legacy; // don't change protocol Con_Printf( "reconnecting...\n" ); } From aa3a0fa392eefdbeb59867bf53aad81a570fdd14 Mon Sep 17 00:00:00 2001 From: Alibek Omarov Date: Tue, 3 Jan 2023 03:20:53 +0300 Subject: [PATCH 317/490] engine: server: increase infostring size in SV_Info allowing longer hostnames but try to cut off if it's even longer than that --- engine/server/sv_client.c | 46 +++++++++++++++++++++++++-------------- 1 file changed, 30 insertions(+), 16 deletions(-) diff --git a/engine/server/sv_client.c b/engine/server/sv_client.c index 3a1f010f..26bb4870 100644 --- a/engine/server/sv_client.c +++ b/engine/server/sv_client.c @@ -852,39 +852,53 @@ The second parameter should be the current protocol version number. */ void SV_Info( netadr_t from, int protocolVersion ) { - char string[MAX_INFO_STRING]; + char s[512]; // ignore in single player if( svs.maxclients == 1 || !svs.initialized ) return; - string[0] = '\0'; + s[0] = '\0'; if( protocolVersion != PROTOCOL_VERSION ) { - Q_snprintf( string, sizeof( string ), "%s: wrong version\n", hostname.string ); + Q_snprintf( s, sizeof( s ), "%s: wrong version\n", hostname.string ); } else { - int i, count, bots; - qboolean havePassword = COM_CheckStringEmpty( sv_password.string ); + int count; + int bots; + int remaining; + char temp[sizeof( s )]; + qboolean have_password = COM_CheckStringEmpty( sv_password.string ); SV_GetPlayerCount( &count, &bots ); // a1ba: send protocol version to distinguish old engine and new - Info_SetValueForKey( string, "p", va( "%i", PROTOCOL_VERSION ), MAX_INFO_STRING ); - Info_SetValueForKey( string, "host", hostname.string, MAX_INFO_STRING ); - Info_SetValueForKey( string, "map", sv.name, MAX_INFO_STRING ); - Info_SetValueForKey( string, "dm", va( "%i", (int)svgame.globals->deathmatch ), MAX_INFO_STRING ); - Info_SetValueForKey( string, "team", va( "%i", (int)svgame.globals->teamplay ), MAX_INFO_STRING ); - Info_SetValueForKey( string, "coop", va( "%i", (int)svgame.globals->coop ), MAX_INFO_STRING ); - Info_SetValueForKey( string, "numcl", va( "%i", count ), MAX_INFO_STRING ); - Info_SetValueForKey( string, "maxcl", va( "%i", svs.maxclients ), MAX_INFO_STRING ); - Info_SetValueForKey( string, "gamedir", GI->gamefolder, MAX_INFO_STRING ); - Info_SetValueForKey( string, "password", havePassword ? "1" : "0", MAX_INFO_STRING ); + Info_SetValueForKey( s, "p", va( "%i", PROTOCOL_VERSION ), sizeof( s )); + Info_SetValueForKey( s, "map", sv.name, sizeof( s )); + Info_SetValueForKey( s, "dm", svgame.globals->deathmatch ? "1" : "0", sizeof( s )); + Info_SetValueForKey( s, "team", svgame.globals->teamplay ? "1" : "0", sizeof( s )); + Info_SetValueForKey( s, "coop", svgame.globals->coop ? "1" : "0", sizeof( s )); + Info_SetValueForKey( s, "numcl", va( "%i", count ), sizeof( s )); + Info_SetValueForKey( s, "maxcl", va( "%i", svs.maxclients ), sizeof( s )); + Info_SetValueForKey( s, "gamedir", GI->gamefolder, sizeof( s )); + Info_SetValueForKey( s, "password", have_password ? "1" : "0", sizeof( s )); + + // write host last so we can try to cut off too long hostnames + // TODO: value size limit for infostrings + remaining = sizeof( s ) - Q_strlen( s ) - sizeof( "\\host\\" ) - 1; + if( remaining < 0 ) + { + // should never happen? + Con_Printf( S_ERROR "SV_Info: infostring overflow!\n" ); + return; + } + Q_strncpy( temp, hostname.string, remaining ); + Info_SetValueForKey( s, "host", temp, sizeof( s )); } - Netchan_OutOfBandPrint( NS_SERVER, from, "info\n%s", string ); + Netchan_OutOfBandPrint( NS_SERVER, from, "info\n%s", s ); } /* From 07e622f224e0f0811e916645c21f4a7a68f442e9 Mon Sep 17 00:00:00 2001 From: Alibek Omarov Date: Tue, 3 Jan 2023 06:58:58 +0300 Subject: [PATCH 318/490] public: add generic implementation for Q_memmem --- public/crtlib.c | 17 +++++++++++++++++ public/crtlib.h | 1 + 2 files changed, 18 insertions(+) diff --git a/public/crtlib.c b/public/crtlib.c index 5b830cd4..b1d7ee5c 100644 --- a/public/crtlib.c +++ b/public/crtlib.c @@ -372,6 +372,23 @@ qboolean Q_stricmpext( const char *pattern, const char *text ) return Q_strnicmpext( pattern, text, ~((size_t)0) ); } +const byte *Q_memmem( const byte *haystack, size_t haystacklen, const byte *needle, size_t needlelen ) +{ + const byte *i; + + // quickly find first matching symbol + while( haystacklen && ( i = memchr( haystack, needle[0], haystacklen ))) + { + if( !memcmp( i, needle, needlelen )) + return i; + + haystacklen -= i - haystack; + haystack = i + 1; + } + + return NULL; +} + const char* Q_timestamp( int format ) { static string timestamp; diff --git a/public/crtlib.h b/public/crtlib.h index 6296930d..b3282d06 100644 --- a/public/crtlib.h +++ b/public/crtlib.h @@ -78,6 +78,7 @@ void Q_atov( float *vec, const char *str, size_t siz ); #define Q_strrchr strrchr qboolean Q_stricmpext( const char *pattern, const char *text ); qboolean Q_strnicmpext( const char *pattern, const char *text, size_t minimumlen ); +const byte *Q_memmem( const byte *haystack, size_t haystacklen, const byte *needle, size_t needlelen ); const char *Q_timestamp( int format ); #define Q_vsprintf( buffer, format, args ) Q_vsnprintf( buffer, 99999, format, args ) int Q_vsnprintf( char *buffer, size_t buffersize, const char *format, va_list args ); From df1c9a5029aacecf0f274bf7c6448443aa125325 Mon Sep 17 00:00:00 2001 From: Alibek Omarov Date: Tue, 3 Jan 2023 07:01:36 +0300 Subject: [PATCH 319/490] engine: simplify blue shift swapped lump check, change TestBmodelLumps to avoid reading past mod buffer --- engine/common/con_utils.c | 16 ++-- engine/common/mod_bmodel.c | 156 +++++++++++++++++++------------------ engine/common/mod_local.h | 2 +- engine/server/sv_game.c | 14 ++-- 4 files changed, 97 insertions(+), 91 deletions(-) diff --git a/engine/common/con_utils.c b/engine/common/con_utils.c index 2cd5ffc0..3aed173f 100644 --- a/engine/common/con_utils.c +++ b/engine/common/con_utils.c @@ -80,8 +80,9 @@ int Cmd_ListMaps( search_t *t, char *lastmapname, size_t len ) if( f ) { - dheader_t *header; + dheader_t *header; dextrahdr_t *hdrext; + dlump_t entities; memset( buf, 0, sizeof( buf )); FS_Read( f, buf, sizeof( buf )); @@ -89,10 +90,10 @@ int Cmd_ListMaps( search_t *t, char *lastmapname, size_t len ) ver = header->version; // check all the lumps and some other errors - if( Mod_TestBmodelLumps( t->filenames[i], buf, true )) + if( Mod_TestBmodelLumps( f, t->filenames[i], buf, true, &entities )) { - lumpofs = header->lumps[LUMP_ENTITIES].fileofs; - lumplen = header->lumps[LUMP_ENTITIES].filelen; + lumpofs = entities.fileofs; + lumplen = entities.filelen; ver = header->version; } @@ -904,21 +905,22 @@ qboolean Cmd_CheckMapsList_R( qboolean fRefresh, qboolean onlyingamedir ) { int num_spawnpoints = 0; dheader_t *header; + 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( t->filenames[i], buf, true )) + if( !Mod_TestBmodelLumps( f, t->filenames[i], buf, true, &entities )) { FS_Close( f ); continue; } // after call Mod_TestBmodelLumps we gurantee what map is valid - lumpofs = header->lumps[LUMP_ENTITIES].fileofs; - lumplen = header->lumps[LUMP_ENTITIES].filelen; + lumpofs = entities.fileofs; + lumplen = entities.filelen; Q_strncpy( entfilename, t->filenames[i], sizeof( entfilename )); COM_StripExtension( entfilename ); diff --git a/engine/common/mod_bmodel.c b/engine/common/mod_bmodel.c index af55ac07..a2abc27f 100644 --- a/engine/common/mod_bmodel.c +++ b/engine/common/mod_bmodel.c @@ -2738,33 +2738,14 @@ static void Mod_LoadLighting( dbspmodel_t *bmod ) /* ================= -Mod_LumpLooksLikePlanes +Mod_LumpLooksLikeEntities ================= */ -static qboolean Mod_LumpLooksLikePlanes( const byte *in, dlump_t *lump, qboolean fast ) +static int Mod_LumpLooksLikeEntities( const char *lump, const size_t lumplen ) { - int numplanes, i; - const dplane_t *planes; - - if( lump->filelen < sizeof( dplane_t ) && - lump->filelen % sizeof( dplane_t ) != 0 ) - return false; - - if( fast ) - return true; - - numplanes = lump->filelen / sizeof( dplane_t ); - planes = (const dplane_t*)(in + lump->fileofs); - - for( i = 0; i < numplanes; i++ ) - { - // planes can only be from 0 to 5: PLANE_X, Y, Z and PLANE_ANYX, Y and Z - if( planes[i].type < 0 || planes[i].type > 5 ) - return false; - } - - return true; + // look for "classname" string + return Q_memmem( lump, lumplen, "\"classname\"", sizeof( "\"classname\"" ) - 1 ) != NULL ? 1 : 0; } /* @@ -2800,44 +2781,30 @@ qboolean Mod_LoadBmodelLumps( const byte *mod_base, qboolean isworld ) #endif switch( header->version ) { - case Q1BSP_VERSION: case HLBSP_VERSION: + // only relevant for half-life maps + if( !Mod_LumpLooksLikeEntities( mod_base + header->lumps[LUMP_ENTITIES].fileofs, header->lumps[LUMP_ENTITIES].filelen ) && + Mod_LumpLooksLikeEntities( mod_base + header->lumps[LUMP_PLANES].fileofs, header->lumps[LUMP_PLANES].filelen )) + { + // blue-shift swapped lumps + srclumps[0].lumpnumber = LUMP_PLANES; + srclumps[1].lumpnumber = LUMP_ENTITIES; + break; + } + // intended fallthrough + case Q1BSP_VERSION: case QBSP2_VERSION: + // everything else + srclumps[0].lumpnumber = LUMP_ENTITIES; + srclumps[1].lumpnumber = LUMP_PLANES; break; default: - Con_Printf( S_ERROR "%s has wrong version number (%i should be %i)\n", loadmodel->name, header->version, HLBSP_VERSION ); - loadstat.numerrors++; - return false; } bmod->version = header->version; // share up global if( isworld ) world.flags = 0; // clear world settings bmod->isworld = isworld; - if( header->version == HLBSP_VERSION ) - { - // only relevant for half-life maps - if( !Mod_LumpLooksLikePlanes( mod_base, &header->lumps[LUMP_PLANES], false ) && - Mod_LumpLooksLikePlanes( mod_base, &header->lumps[LUMP_ENTITIES], false )) - { - // blue-shift swapped lumps - srclumps[0].lumpnumber = LUMP_PLANES; - srclumps[1].lumpnumber = LUMP_ENTITIES; - } - else - { - // everything else - srclumps[0].lumpnumber = LUMP_ENTITIES; - srclumps[1].lumpnumber = LUMP_PLANES; - } - } - else - { - // everything else - srclumps[0].lumpnumber = LUMP_ENTITIES; - srclumps[1].lumpnumber = LUMP_PLANES; - } - // loading base lumps for( i = 0; i < ARRAYSIZE( srclumps ); i++ ) Mod_LoadLump( mod_base, &srclumps[i], &worldstats[i], isworld ? (LUMP_SAVESTATS|LUMP_SILENT) : 0 ); @@ -2907,14 +2874,42 @@ qboolean Mod_LoadBmodelLumps( const byte *mod_base, qboolean isworld ) return true; } +static int Mod_LumpLooksLikeEntitiesFile( file_t *f, dlump_t *l, int flags, const char *msg ) +{ + char *buf; + int ret; + + if( FS_Seek( f, l->fileofs, SEEK_SET ) < 0 ) + { + if( !FBitSet( flags, LUMP_SILENT )) + Con_DPrintf( S_ERROR "map ^2%s^7 %s lump past end of file\n", loadstat.name, msg ); + return -1; + } + + buf = Z_Malloc( l->filelen + 1 ); + if( FS_Read( f, buf, l->filelen ) != l->filelen ) + { + if( !FBitSet( flags, LUMP_SILENT )) + Con_DPrintf( S_ERROR "can't read %s lump of map ^2%s^7", msg, loadstat.name ); + Z_Free( buf ); + return -1; + } + + ret = Mod_LumpLooksLikeEntities( buf, l->filelen ); + + Z_Free( buf ); + return ret; +} + /* ================= Mod_TestBmodelLumps check for possible errors +return real entities lump (for bshift swapped lumps) ================= */ -qboolean Mod_TestBmodelLumps( const char *name, const byte *mod_base, qboolean silent ) +qboolean Mod_TestBmodelLumps( file_t *f, const char *name, const byte *mod_base, qboolean silent, dlump_t *entities ) { dheader_t *header = (dheader_t *)mod_base; int i, flags = LUMP_TESTONLY; @@ -2934,11 +2929,42 @@ qboolean Mod_TestBmodelLumps( const char *name, const byte *mod_base, qboolean s return false; } #endif + switch( header->version ) { - case Q1BSP_VERSION: case HLBSP_VERSION: + { + int ret; + + ret = Mod_LumpLooksLikeEntitiesFile( f, &header->lumps[LUMP_ENTITIES], flags, "entities" ); + if( ret < 0 ) + return false; + + if( !ret ) + { + ret = Mod_LumpLooksLikeEntitiesFile( f, &header->lumps[LUMP_PLANES], flags, "planes" ); + if( ret < 0 ) + return false; + + if( ret ) + { + // blue-shift swapped lumps + *entities = header->lumps[LUMP_PLANES]; + + srclumps[0].lumpnumber = LUMP_PLANES; + srclumps[1].lumpnumber = LUMP_ENTITIES; + break; + } + } + } + // intended fallthrough + case Q1BSP_VERSION: case QBSP2_VERSION: + // everything else + *entities = header->lumps[LUMP_ENTITIES]; + + srclumps[0].lumpnumber = LUMP_ENTITIES; + srclumps[1].lumpnumber = LUMP_PLANES; break; default: // don't early out: let me analyze errors @@ -2948,30 +2974,6 @@ qboolean Mod_TestBmodelLumps( const char *name, const byte *mod_base, qboolean s break; } - if( header->version == HLBSP_VERSION ) - { - // only relevant for half-life maps - if( Mod_LumpLooksLikePlanes( mod_base, &header->lumps[LUMP_ENTITIES], true ) && - !Mod_LumpLooksLikePlanes( mod_base, &header->lumps[LUMP_PLANES], true )) - { - // blue-shift swapped lumps - srclumps[0].lumpnumber = LUMP_PLANES; - srclumps[1].lumpnumber = LUMP_ENTITIES; - } - else - { - // everything else - srclumps[0].lumpnumber = LUMP_ENTITIES; - srclumps[1].lumpnumber = LUMP_PLANES; - } - } - else - { - // everything else - srclumps[0].lumpnumber = LUMP_ENTITIES; - srclumps[1].lumpnumber = LUMP_PLANES; - } - // loading base lumps for( i = 0; i < ARRAYSIZE( srclumps ); i++ ) Mod_LoadLump( mod_base, &srclumps[i], &worldstats[i], flags ); diff --git a/engine/common/mod_local.h b/engine/common/mod_local.h index 5c7e579d..16c29531 100644 --- a/engine/common/mod_local.h +++ b/engine/common/mod_local.h @@ -148,7 +148,7 @@ void Mod_FreeUnused( void ); // mod_bmodel.c // void Mod_LoadBrushModel( model_t *mod, const void *buffer, qboolean *loaded ); -qboolean Mod_TestBmodelLumps( const char *name, const byte *mod_base, qboolean silent ); +qboolean Mod_TestBmodelLumps( file_t *f, const char *name, const byte *mod_base, qboolean silent, dlump_t *entities ); qboolean Mod_HeadnodeVisible( mnode_t *node, const byte *visbits, int *lastleaf ); int Mod_FatPVS( const vec3_t org, float radius, byte *visbuffer, int visbytes, qboolean merge, qboolean fullvis ); qboolean Mod_BoxVisible( const vec3_t mins, const vec3_t maxs, const byte *visbits ); diff --git a/engine/server/sv_game.c b/engine/server/sv_game.c index 46e1b271..b439ae29 100644 --- a/engine/server/sv_game.c +++ b/engine/server/sv_game.c @@ -849,6 +849,7 @@ void SV_WriteEntityPatch( const char *filename ) byte buf[MAX_TOKEN]; // 1 kb string bspfilename; dheader_t *header; + dlump_t entities; file_t *f; Q_snprintf( bspfilename, sizeof( bspfilename ), "maps/%s.bsp", filename ); @@ -861,14 +862,14 @@ void SV_WriteEntityPatch( const char *filename ) header = (dheader_t *)buf; // check all the lumps and some other errors - if( !Mod_TestBmodelLumps( bspfilename, buf, true )) + if( !Mod_TestBmodelLumps( f, bspfilename, buf, true, &entities )) { FS_Close( f ); return; } - lumpofs = header->lumps[LUMP_ENTITIES].fileofs; - lumplen = header->lumps[LUMP_ENTITIES].filelen; + lumpofs = entities.fileofs; + lumplen = entities.filelen; if( lumplen >= 10 ) { @@ -899,6 +900,7 @@ static char *SV_ReadEntityScript( const char *filename, int *flags ) byte buf[MAX_TOKEN]; char *ents = NULL; dheader_t *header; + dlump_t entities; size_t ft1, ft2; file_t *f; @@ -915,7 +917,7 @@ static char *SV_ReadEntityScript( const char *filename, int *flags ) header = (dheader_t *)buf; // check all the lumps and some other errors - if( !Mod_TestBmodelLumps( bspfilename, buf, (host_developer.value) ? false : true )) + if( !Mod_TestBmodelLumps( f, bspfilename, buf, (host_developer.value) ? false : true, &entities )) { SetBits( *flags, MAP_INVALID_VERSION ); FS_Close( f ); @@ -923,8 +925,8 @@ static char *SV_ReadEntityScript( const char *filename, int *flags ) } // after call Mod_TestBmodelLumps we gurantee what map is valid - lumpofs = header->lumps[LUMP_ENTITIES].fileofs; - lumplen = header->lumps[LUMP_ENTITIES].filelen; + lumpofs = entities.fileofs; + lumplen = entities.filelen; // check for entfile too Q_snprintf( entfilename, sizeof( entfilename ), "maps/%s.ent", filename ); From 03a3fb83b67057d4e9ba80cb5aeab760e311c814 Mon Sep 17 00:00:00 2001 From: Alibek Omarov Date: Tue, 3 Jan 2023 07:34:05 +0300 Subject: [PATCH 320/490] filesystem: write extended fields to gameinfo.txt, as this function is used to write game info when using rodir --- filesystem/filesystem.c | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/filesystem/filesystem.c b/filesystem/filesystem.c index 43f5350a..2803a33b 100644 --- a/filesystem/filesystem.c +++ b/filesystem/filesystem.c @@ -487,7 +487,6 @@ static void FS_WriteGameInfo( const char *filepath, gameinfo_t *GameInfo ) if( COM_CheckStringEmpty( GameInfo->game_dll_osx ) ) FS_Printf( f, "gamedll_osx\t\t\"%s\"\n", GameInfo->game_dll_osx ); - if( COM_CheckStringEmpty( GameInfo->iconpath )) FS_Printf( f, "icon\t\t\"%s\"\n", GameInfo->iconpath ); @@ -532,6 +531,13 @@ static void FS_WriteGameInfo( const char *filepath, gameinfo_t *GameInfo ) } } + if( GameInfo->noskills ) + FS_Printf( f, "noskills\t\t\"%i\"\n", GameInfo->nomodels ); + + // always expose our extensions :) + FS_Printf( f, "internal_vgui_support\t\t%s\n", GameInfo->internal_vgui_support ? "1" : "0" ); + FS_Printf( f, "render_picbutton_text\t\t%s\n", GameInfo->render_picbutton_text ? "1" : "0" ); + FS_Print( f, "\n\n\n" ); FS_Close( f ); // all done } From 13ed2742b2cd3702f0e6895c5be3b3cf272c4dab Mon Sep 17 00:00:00 2001 From: Alibek Omarov Date: Tue, 3 Jan 2023 07:50:50 +0300 Subject: [PATCH 321/490] engine: check for bsp30ext before trying to use extended clipnodes, fix 32-bit clipnodes check --- engine/common/mod_bmodel.c | 78 ++++++++++++++++++++++---------------- 1 file changed, 45 insertions(+), 33 deletions(-) diff --git a/engine/common/mod_bmodel.c b/engine/common/mod_bmodel.c index a2abc27f..a8859f14 100644 --- a/engine/common/mod_bmodel.c +++ b/engine/common/mod_bmodel.c @@ -134,6 +134,7 @@ typedef struct int lightmap_samples; // samples per lightmap (1 or 3) int version; // model version qboolean isworld; + qboolean isbsp30ext; } dbspmodel_t; typedef struct @@ -159,6 +160,7 @@ typedef struct #define LUMP_SAVESTATS BIT( 0 ) #define LUMP_TESTONLY BIT( 1 ) #define LUMP_SILENT BIT( 2 ) +#define LUMP_BSP30EXT BIT( 3 ) // extra marker for Mod_LoadLump typedef struct { @@ -251,13 +253,12 @@ static void Mod_LoadLump( const byte *in, mlumpinfo_t *info, mlumpstat_t *stat, // always use alternate entrysize for BSP2 real_entrysize = info->entrysize32; } - else if( info->lumpnumber == LUMP_CLIPNODES && version != Q1BSP_VERSION ) + else if( version == HLBSP_VERSION && FBitSet( flags, LUMP_BSP30EXT ) && info->lumpnumber == LUMP_CLIPNODES ) { - // never run this check for BSP29 because Arguire QBSP 'broken' clipnodes! - if(( l->filelen % info->entrysize ) || ( l->filelen / info->entrysize ) >= MAX_MAP_CLIPNODES_HLBSP ) + // if this map is bsp30ext, try to guess extended clipnodes + if((( l->filelen % info->entrysize ) || ( l->filelen / info->entrysize32 ) >= MAX_MAP_CLIPNODES_HLBSP )) { real_entrysize = info->entrysize32; - SetBits( flags, LUMP_SILENT ); // shut up warning } } @@ -2578,7 +2579,7 @@ static void Mod_LoadClipnodes( dbspmodel_t *bmod ) bmod->clipnodes_out = out = (dclipnode32_t *)Mem_Malloc( loadmodel->mempool, bmod->numclipnodes * sizeof( *out )); - if(( bmod->version == QBSP2_VERSION ) || ( bmod->version == HLBSP_VERSION && bmod->numclipnodes >= MAX_MAP_CLIPNODES_HLBSP )) + if(( bmod->version == QBSP2_VERSION ) || ( bmod->version == HLBSP_VERSION && bmod->isbsp30ext && bmod->numclipnodes >= MAX_MAP_CLIPNODES_HLBSP )) { dclipnode32_t *in = bmod->clipnodes32; @@ -2757,13 +2758,13 @@ loading and processing bmodel */ qboolean Mod_LoadBmodelLumps( const byte *mod_base, qboolean isworld ) { - dheader_t *header = (dheader_t *)mod_base; - dextrahdr_t *extrahdr = (dextrahdr_t *)((byte *)mod_base + sizeof( dheader_t )); + const dheader_t *header = (const dheader_t *)mod_base; + const dextrahdr_t *extrahdr = (const dextrahdr_t *)(mod_base + sizeof( dheader_t )); dbspmodel_t *bmod = &srcmodel; model_t *mod = loadmodel; char wadvalue[2048]; size_t len = 0; - int i, ret; + int i, ret, flags = 0; // always reset the intermediate struct memset( bmod, 0, sizeof( dbspmodel_t )); @@ -2782,8 +2783,12 @@ qboolean Mod_LoadBmodelLumps( const byte *mod_base, qboolean isworld ) switch( header->version ) { case HLBSP_VERSION: + if( extrahdr->id == IDEXTRAHEADER ) + { + SetBits( flags, LUMP_BSP30EXT ); + } // only relevant for half-life maps - if( !Mod_LumpLooksLikeEntities( mod_base + header->lumps[LUMP_ENTITIES].fileofs, header->lumps[LUMP_ENTITIES].filelen ) && + else if( !Mod_LumpLooksLikeEntities( mod_base + header->lumps[LUMP_ENTITIES].fileofs, header->lumps[LUMP_ENTITIES].filelen ) && Mod_LumpLooksLikeEntities( mod_base + header->lumps[LUMP_PLANES].fileofs, header->lumps[LUMP_PLANES].filelen )) { // blue-shift swapped lumps @@ -2802,16 +2807,21 @@ qboolean Mod_LoadBmodelLumps( const byte *mod_base, qboolean isworld ) } bmod->version = header->version; // share up global - if( isworld ) world.flags = 0; // clear world settings + if( isworld ) + { + world.flags = 0; // clear world settings + SetBits( flags, LUMP_SAVESTATS|LUMP_SILENT ); + } bmod->isworld = isworld; + bmod->isbsp30ext = FBitSet( flags, LUMP_BSP30EXT ); // loading base lumps for( i = 0; i < ARRAYSIZE( srclumps ); i++ ) - Mod_LoadLump( mod_base, &srclumps[i], &worldstats[i], isworld ? (LUMP_SAVESTATS|LUMP_SILENT) : 0 ); + Mod_LoadLump( mod_base, &srclumps[i], &worldstats[i], flags ); // loading extralumps for( i = 0; i < ARRAYSIZE( extlumps ); i++ ) - Mod_LoadLump( mod_base, &extlumps[i], &worldstats[ARRAYSIZE( srclumps ) + i], isworld ? (LUMP_SAVESTATS|LUMP_SILENT) : 0 ); + Mod_LoadLump( mod_base, &extlumps[i], &worldstats[ARRAYSIZE( srclumps ) + i], flags ); if( !bmod->isworld && loadstat.numerrors ) { @@ -2911,7 +2921,8 @@ return real entities lump (for bshift swapped lumps) */ qboolean Mod_TestBmodelLumps( file_t *f, const char *name, const byte *mod_base, qboolean silent, dlump_t *entities ) { - dheader_t *header = (dheader_t *)mod_base; + const dheader_t *header = (const dheader_t *)mod_base; + const dextrahdr_t *extrahdr = (const dextrahdr_t *)( mod_base + sizeof( dheader_t )); int i, flags = LUMP_TESTONLY; // always reset the intermediate struct @@ -2919,7 +2930,8 @@ qboolean Mod_TestBmodelLumps( file_t *f, const char *name, const byte *mod_base, // store the name to correct show errors and warnings Q_strncpy( loadstat.name, name, sizeof( loadstat.name )); - if( silent ) SetBits( flags, LUMP_SILENT ); + if( silent ) + SetBits( flags, LUMP_SILENT ); #ifndef SUPPORT_BSP2_FORMAT if( header->version == QBSP2_VERSION ) @@ -2933,30 +2945,30 @@ qboolean Mod_TestBmodelLumps( file_t *f, const char *name, const byte *mod_base, switch( header->version ) { case HLBSP_VERSION: - { - int ret; - - ret = Mod_LumpLooksLikeEntitiesFile( f, &header->lumps[LUMP_ENTITIES], flags, "entities" ); - if( ret < 0 ) - return false; - - if( !ret ) + if( extrahdr->id == IDEXTRAHEADER ) { - ret = Mod_LumpLooksLikeEntitiesFile( f, &header->lumps[LUMP_PLANES], flags, "planes" ); - if( ret < 0 ) - return false; - - if( ret ) + SetBits( flags, LUMP_BSP30EXT ); + } + else + { + // only relevant for half-life maps + int ret = Mod_LumpLooksLikeEntitiesFile( f, &header->lumps[LUMP_ENTITIES], flags, "entities" ); + if( ret < 0 ) return false; + if( !ret ) { - // blue-shift swapped lumps - *entities = header->lumps[LUMP_PLANES]; + ret = Mod_LumpLooksLikeEntitiesFile( f, &header->lumps[LUMP_PLANES], flags, "planes" ); + if( ret < 0 ) return false; + if( ret ) + { + // blue-shift swapped lumps + *entities = header->lumps[LUMP_PLANES]; - srclumps[0].lumpnumber = LUMP_PLANES; - srclumps[1].lumpnumber = LUMP_ENTITIES; - break; + srclumps[0].lumpnumber = LUMP_PLANES; + srclumps[1].lumpnumber = LUMP_ENTITIES; + break; + } } } - } // intended fallthrough case Q1BSP_VERSION: case QBSP2_VERSION: From d047dfc319da8786a38e8b495de0129404e32273 Mon Sep 17 00:00:00 2001 From: Alibek Omarov Date: Tue, 3 Jan 2023 18:26:18 +0300 Subject: [PATCH 322/490] engine: add few lines in mod_bmodel back, got removed in previous commits accidentally --- engine/common/mod_bmodel.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/engine/common/mod_bmodel.c b/engine/common/mod_bmodel.c index a8859f14..8d435914 100644 --- a/engine/common/mod_bmodel.c +++ b/engine/common/mod_bmodel.c @@ -2804,6 +2804,9 @@ qboolean Mod_LoadBmodelLumps( const byte *mod_base, qboolean isworld ) srclumps[1].lumpnumber = LUMP_PLANES; break; default: + Con_Printf( S_ERROR "%s has wrong version number (%i should be %i)\n", loadmodel->name, header->version, HLBSP_VERSION ); + loadstat.numerrors++; + return false; } bmod->version = header->version; // share up global From 409edf5a707fcbee2b3437e615ec3950d145a45e Mon Sep 17 00:00:00 2001 From: Alibek Omarov Date: Wed, 4 Jan 2023 17:15:40 +0300 Subject: [PATCH 323/490] filesystem: zip: use stdint types, use enum for errors --- filesystem/zip.c | 95 +++++++++++++++++++++++++----------------------- 1 file changed, 49 insertions(+), 46 deletions(-) diff --git a/filesystem/zip.c b/filesystem/zip.c index e5171f12..6bfbdb34 100644 --- a/filesystem/zip.c +++ b/filesystem/zip.c @@ -29,7 +29,7 @@ GNU General Public License for more details. #include "common/com_strings.h" #include "miniz.h" -#define ZIP_HEADER_LF (('K'<<8)+('P')+(0x03<<16)+(0x04<<24)) +#define ZIP_HEADER_LF (('K'<<8)+('P')+(0x03<<16)+(0x04<<24)) #define ZIP_HEADER_SPANNED ((0x08<<24)+(0x07<<16)+('K'<<8)+'P') #define ZIP_HEADER_CDF ((0x02<<24)+(0x01<<16)+('K'<<8)+'P') @@ -43,16 +43,16 @@ GNU General Public License for more details. #pragma pack( push, 1 ) typedef struct zip_header_s { - unsigned int signature; // little endian ZIP_HEADER - unsigned short version; // version of pkzip need to unpack - unsigned short flags; // flags (16 bits == 16 flags) - unsigned short compression_flags; // compression flags (bits) - unsigned int dos_date; // file modification time and file modification date - unsigned int crc32; //crc32 - unsigned int compressed_size; - unsigned int uncompressed_size; - unsigned short filename_len; - unsigned short extrafield_len; + uint32_t signature; // little endian ZIP_HEADER + uint16_t version; // version of pkzip need to unpack + uint16_t flags; // flags (16 bits == 16 flags) + uint16_t compression_flags; // compression flags (bits) + uint32_t dos_date; // file modification time and file modification date + uint32_t crc32; //crc32 + uint32_t compressed_size; + uint32_t uncompressed_size; + uint16_t filename_len; + uint16_t extrafield_len; } zip_header_t; /* @@ -62,52 +62,55 @@ typedef struct zip_header_s typedef struct zip_header_extra_s { - unsigned int signature; // ZIP_HEADER_SPANNED - unsigned int crc32; - unsigned int compressed_size; - unsigned int uncompressed_size; + uint32_t signature; // ZIP_HEADER_SPANNED + uint32_t crc32; + uint32_t compressed_size; + uint32_t uncompressed_size; } zip_header_extra_t; typedef struct zip_cdf_header_s { - unsigned int signature; - unsigned short version; - unsigned short version_need; - unsigned short generalPurposeBitFlag; - unsigned short flags; - unsigned short modification_time; - unsigned short modification_date; - unsigned int crc32; - unsigned int compressed_size; - unsigned int uncompressed_size; - unsigned short filename_len; - unsigned short extrafield_len; - unsigned short file_commentary_len; - unsigned short disk_start; - unsigned short internal_attr; - unsigned int external_attr; - unsigned int local_header_offset; + uint32_t signature; + uint16_t version; + uint16_t version_need; + uint16_t generalPurposeBitFlag; + uint16_t flags; + uint16_t modification_time; + uint16_t modification_date; + uint32_t crc32; + uint32_t compressed_size; + uint32_t uncompressed_size; + uint16_t filename_len; + uint16_t extrafield_len; + uint16_t file_commentary_len; + uint16_t disk_start; + uint16_t internal_attr; + uint32_t external_attr; + uint32_t local_header_offset; } zip_cdf_header_t; typedef struct zip_header_eocd_s { - unsigned short disk_number; - unsigned short start_disk_number; - unsigned short number_central_directory_record; - unsigned short total_central_directory_record; - unsigned int size_of_central_directory; - unsigned int central_directory_offset; - unsigned short commentary_len; + uint16_t disk_number; + uint16_t start_disk_number; + uint16_t number_central_directory_record; + uint16_t total_central_directory_record; + uint32_t size_of_central_directory; + uint32_t central_directory_offset; + uint16_t commentary_len; } zip_header_eocd_t; #pragma pack( pop ) // ZIP errors -#define ZIP_LOAD_OK 0 -#define ZIP_LOAD_COULDNT_OPEN 1 -#define ZIP_LOAD_BAD_HEADER 2 -#define ZIP_LOAD_BAD_FOLDERS 3 -#define ZIP_LOAD_NO_FILES 5 -#define ZIP_LOAD_CORRUPTED 6 +enum +{ + ZIP_LOAD_OK = 0, + ZIP_LOAD_COULDNT_OPEN, + ZIP_LOAD_BAD_HEADER, + ZIP_LOAD_BAD_FOLDERS, + ZIP_LOAD_NO_FILES, + ZIP_LOAD_CORRUPTED +}; typedef struct zipfile_s { @@ -115,7 +118,7 @@ typedef struct zipfile_s fs_offset_t offset; // offset of local file header fs_offset_t size; //original file size fs_offset_t compressed_size; // compressed file size - unsigned short flags; + uint16_t flags; } zipfile_t; struct zip_s From 75ccd2283b084efe7723875adee02e570e4ba481 Mon Sep 17 00:00:00 2001 From: Alibek Omarov Date: Wed, 4 Jan 2023 17:16:27 +0300 Subject: [PATCH 324/490] public: fix Q_memmem counting haystack size incorrectly --- public/crtlib.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/public/crtlib.c b/public/crtlib.c index b1d7ee5c..3561a1be 100644 --- a/public/crtlib.c +++ b/public/crtlib.c @@ -382,8 +382,11 @@ const byte *Q_memmem( const byte *haystack, size_t haystacklen, const byte *need if( !memcmp( i, needle, needlelen )) return i; + // skip one byte + i++; + haystacklen -= i - haystack; - haystack = i + 1; + haystack = i; } return NULL; From ed47346ef1785ee78b115ce865d7cdb468b49513 Mon Sep 17 00:00:00 2001 From: Alibek Omarov Date: Wed, 4 Jan 2023 17:54:45 +0300 Subject: [PATCH 325/490] engine: crashhandler: fix _GNU_SOURCE redefined warning --- engine/common/crashhandler.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/engine/common/crashhandler.c b/engine/common/crashhandler.c index 3c638b47..64c537c1 100644 --- a/engine/common/crashhandler.c +++ b/engine/common/crashhandler.c @@ -12,7 +12,9 @@ but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. */ +#ifndef _GNU_SOURCE #define _GNU_SOURCE +#endif #include "common.h" From 80507b2ecaecaf46a4f97c85a6b8e97cd4269356 Mon Sep 17 00:00:00 2001 From: Alibek Omarov Date: Wed, 4 Jan 2023 17:55:59 +0300 Subject: [PATCH 326/490] engine: mod_bmodel: fix const qualifier discard warning --- engine/common/mod_bmodel.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/engine/common/mod_bmodel.c b/engine/common/mod_bmodel.c index 8d435914..8d2c0be3 100644 --- a/engine/common/mod_bmodel.c +++ b/engine/common/mod_bmodel.c @@ -2887,7 +2887,7 @@ qboolean Mod_LoadBmodelLumps( const byte *mod_base, qboolean isworld ) return true; } -static int Mod_LumpLooksLikeEntitiesFile( file_t *f, dlump_t *l, int flags, const char *msg ) +static int Mod_LumpLooksLikeEntitiesFile( file_t *f, const dlump_t *l, int flags, const char *msg ) { char *buf; int ret; From cb9605430dc3347c1187daf525ceffeda94492d1 Mon Sep 17 00:00:00 2001 From: Alibek Omarov Date: Wed, 4 Jan 2023 18:07:18 +0300 Subject: [PATCH 327/490] filesystem: bump FS_API_VERSION (removed unused argument from SysFileExists) --- filesystem/filesystem.h | 6 +++--- filesystem/filesystem_internal.h | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/filesystem/filesystem.h b/filesystem/filesystem.h index e9b0a2df..762c11fd 100644 --- a/filesystem/filesystem.h +++ b/filesystem/filesystem.h @@ -28,8 +28,8 @@ extern "C" { #endif // __cplusplus -#define FS_API_VERSION 1 // not stable yet! -#define FS_API_CREATEINTERFACE_TAG "XashFileSystem001" // follow FS_API_VERSION!!! +#define FS_API_VERSION 2 // not stable yet! +#define FS_API_CREATEINTERFACE_TAG "XashFileSystem002" // follow FS_API_VERSION!!! // search path flags enum @@ -176,7 +176,7 @@ typedef struct fs_api_t fs_offset_t (*FileSize)( const char *filename, qboolean gamedironly ); qboolean (*Rename)( const char *oldname, const char *newname ); qboolean (*Delete)( const char *path ); - qboolean (*SysFileExists)( const char *path, qboolean casesensitive ); + qboolean (*SysFileExists)( const char *path ); const char *(*GetDiskPath)( const char *name, qboolean gamedironly ); // file watcher diff --git a/filesystem/filesystem_internal.h b/filesystem/filesystem_internal.h index fb586ebb..b60c271e 100644 --- a/filesystem/filesystem_internal.h +++ b/filesystem/filesystem_internal.h @@ -172,7 +172,7 @@ int FS_FileTime( const char *filename, qboolean gamedironly ); fs_offset_t FS_FileSize( const char *filename, qboolean gamedironly ); qboolean FS_Rename( const char *oldname, const char *newname ); qboolean FS_Delete( const char *path ); -qboolean FS_SysFileExists( const char *path, qboolean casesensitive ); +qboolean FS_SysFileExists( const char *path ); const char *FS_GetDiskPath( const char *name, qboolean gamedironly ); void FS_CreatePath( char *path ); qboolean FS_SysFolderExists( const char *path ); From e69408162608d64dc15f440a36998c396716b65f Mon Sep 17 00:00:00 2001 From: Alibek Omarov Date: Wed, 4 Jan 2023 18:09:00 +0300 Subject: [PATCH 328/490] filesystem: dir: guarantee file existense in FS_FixFileCase for caseinsensitive directories --- filesystem/dir.c | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/filesystem/dir.c b/filesystem/dir.c index f6e435dd..5e073284 100644 --- a/filesystem/dir.c +++ b/filesystem/dir.c @@ -280,7 +280,8 @@ static inline qboolean FS_AppendToPath( char *dst, size_t *pi, const size_t len, qboolean FS_FixFileCase( dir_t *dir, const char *path, char *dst, const size_t len, qboolean createpath ) { - const char *prev, *next; + const char *prev; + const char *next; size_t i = 0; if( !FS_AppendToPath( dst, &i, len, dir->name, path, "init" )) @@ -307,6 +308,9 @@ qboolean FS_FixFileCase( dir_t *dir, const char *path, char *dst, const size_t l { if( !FS_AppendToPath( dst, &i, len, prev, path, "caseinsensitive entry" )) return false; + + if( FS_SysFileOrFolderExists( dst )) // file not found + return createpath; break; } @@ -373,7 +377,7 @@ static int FS_FindFile_DIR( searchpath_t *search, const char *path, char *fixedn if( !FS_FixFileCase( search->dir, path, netpath, sizeof( netpath ), false )) return -1; - if( FS_SysFileExists( netpath, !FBitSet( search->flags, FS_CUSTOM_PATH ))) + if( FS_SysFileExists( netpath )) { // return fixed case file name only local for that searchpath if( fixedname ) From 15846a8ea891442431efb004ae0341b713e89e99 Mon Sep 17 00:00:00 2001 From: Alibek Omarov Date: Wed, 4 Jan 2023 18:10:21 +0300 Subject: [PATCH 329/490] filesystem: use POSIX funcs for Windows too, where possible, apply wide char conversion for Sys functions --- filesystem/filesystem.c | 123 +++++++++++++++++++--------------------- 1 file changed, 57 insertions(+), 66 deletions(-) diff --git a/filesystem/filesystem.c b/filesystem/filesystem.c index 2803a33b..a53131d8 100644 --- a/filesystem/filesystem.c +++ b/filesystem/filesystem.c @@ -15,6 +15,7 @@ GNU General Public License for more details. #include "build.h" #include +#include #include #include #if XASH_WIN32 @@ -99,8 +100,6 @@ static void FS_BackupFileName( file_t *file, const char *path, uint options ) file->backup_options = options; } } - - #else static void FS_EnsureOpenFile( file_t *file ) {} static void FS_BackupFileName( file_t *file, const char *path, uint options ) {} @@ -179,7 +178,8 @@ void stringlistsort( stringlist_t *list ) } } -// convert names to lowercase because windows doesn't care, but pattern matching code often does +#if XASH_DOS4GW +// convert names to lowercase because dos doesn't care, but pattern matching code often does static void listlowercase( stringlist_t *list ) { char *c; @@ -191,6 +191,7 @@ static void listlowercase( stringlist_t *list ) *c = Q_tolower( *c ); } } +#endif void listdirectory( stringlist_t *list, const char *path ) { @@ -1508,7 +1509,7 @@ file_t *FS_SysOpen( const char *filepath, const char *mode ) file->ungetc = EOF; #if XASH_WIN32 - file->handle = _wopen( FS_PathToWideChar(filepath), mod | opt, 0666 ); + file->handle = _wopen( FS_PathToWideChar( filepath ), mod | opt, 0666 ); #else file->handle = open( filepath, mod|opt, 0666 ); #endif @@ -1581,6 +1582,14 @@ file_t *FS_OpenHandle( const char *syspath, int handle, fs_offset_t offset, fs_o return file; } +#if !defined( S_ISREG ) +#define S_ISREG( m ) ( FBitSet( m, S_IFMT ) == S_IFREG ) +#endif + +#if !defined( S_ISDIR ) +#define S_ISDIR( m ) ( FBitSet( m, S_IFMT ) == S_IFDIR ) +#endif + /* ================== FS_SysFileExists @@ -1588,28 +1597,55 @@ FS_SysFileExists Look for a file in the filesystem only ================== */ -qboolean FS_SysFileExists( const char *path, qboolean caseinsensitive ) +qboolean FS_SysFileExists( const char *path ) { -#if XASH_WIN32 - int desc; - - if(( desc = open( path, O_RDONLY|O_BINARY )) < 0 ) - return false; - - close( desc ); - return true; -#elif XASH_POSIX - int ret; struct stat buf; - ret = stat( path, &buf ); - - if( ret < 0 ) +#if XASH_WIN32 + if( _wstat( FS_PathToWideChar( path ), &buf ) < 0 ) +#else + if( stat( path, &buf ) < 0 ) +#endif return false; return S_ISREG( buf.st_mode ); +} + +/* +================== +FS_SysFolderExists + +Look for a existing folder +================== +*/ +qboolean FS_SysFolderExists( const char *path ) +{ + struct stat buf; + +#if XASH_WIN32 + if( _wstat( FS_PathToWideChar( path ), &buf ) < 0 ) #else -#error + if( stat( path, &buf ) < 0 ) +#endif + return false; + + return S_ISDIR( buf.st_mode ); +} + +/* +============== +FS_SysFileOrFolderExists + +Check if filesystem entry exists at all, don't mind the type +============== +*/ +qboolean FS_SysFileOrFolderExists( const char *path ) +{ + struct stat buf; +#if XASH_WIN32 + return _wstat( FS_PathToWideChar( path ), &buf ) >= 0; +#else + return stat( path, &buf ) >= 0; #endif } @@ -1623,7 +1659,7 @@ Sets current directory, path should be in UTF-8 encoding int FS_SetCurrentDirectory( const char *path ) { #if XASH_WIN32 - return SetCurrentDirectoryW( FS_PathToWideChar(path) ); + return SetCurrentDirectoryW( FS_PathToWideChar( path )); #elif XASH_POSIX return !chdir( path ); #else @@ -1631,51 +1667,6 @@ int FS_SetCurrentDirectory( const char *path ) #endif } -/* -================== -FS_SysFolderExists - -Look for a existing folder -================== -*/ -qboolean FS_SysFolderExists( const char *path ) -{ -#if XASH_WIN32 - DWORD dwFlags = GetFileAttributes( path ); - - return ( dwFlags != -1 ) && ( dwFlags & FILE_ATTRIBUTE_DIRECTORY ); -#elif XASH_POSIX - struct stat buf; - - if( stat( path, &buf ) < 0 ) - return false; - - return S_ISDIR( buf.st_mode ); -#else -#error -#endif -} - -/* -============== -FS_SysFileOrFolderExists - -Check if filesystem entry exists at all, don't mind the type -============== -*/ -qboolean FS_SysFileOrFolderExists( const char *path ) -{ -#if XASH_WIN32 - return GetFileAttributes( path ) != -1; -#elif XASH_POSIX - struct stat buf; - return stat( path, &buf ) >= 0; -#else -#error -#endif - -} - /* ==================== FS_FindFile @@ -1712,7 +1703,7 @@ searchpath_t *FS_FindFile( const char *name, int *index, char *fixedname, size_t Q_snprintf( dirpath, sizeof( dirpath ), "%s" PATH_SEPARATOR_STR, fs_rootdir ); Q_snprintf( netpath, sizeof( netpath ), "%s%s", dirpath, name ); - if( FS_SysFileExists( netpath, true )) + if( FS_SysFileExists( netpath )) { static searchpath_t fs_directpath; From 51b5d7a41d8d70ae07e86aa20c6220bcef23eae8 Mon Sep 17 00:00:00 2001 From: Alibek Omarov Date: Wed, 4 Jan 2023 19:39:52 +0300 Subject: [PATCH 330/490] engine: host: ensure we always have right slashes under Windows --- engine/common/host.c | 14 +++++++++++--- 1 file changed, 11 insertions(+), 3 deletions(-) diff --git a/engine/common/host.c b/engine/common/host.c index 3de404a3..c0515eae 100644 --- a/engine/common/host.c +++ b/engine/common/host.c @@ -1006,7 +1006,7 @@ void Host_InitCommon( int argc, char **argv, const char *progname, qboolean bCha if( COM_CheckString( baseDir ) ) { - Q_strncpy( host.rootdir, baseDir, sizeof(host.rootdir) ); + Q_strncpy( host.rootdir, baseDir, sizeof( host.rootdir )); } else { @@ -1018,10 +1018,10 @@ void Host_InitCommon( int argc, char **argv, const char *progname, qboolean bCha if( !( szBasePath = SDL_GetBasePath() ) ) Sys_Error( "couldn't determine current directory: %s", SDL_GetError() ); - Q_strncpy( host.rootdir, szBasePath, sizeof( host.rootdir ) ); + Q_strncpy( host.rootdir, szBasePath, sizeof( host.rootdir )); SDL_free( szBasePath ); #else - if( !getcwd( host.rootdir, sizeof(host.rootdir) ) ) + if( !getcwd( host.rootdir, sizeof( host.rootdir ))) { Sys_Error( "couldn't determine current directory: %s", strerror( errno ) ); host.rootdir[0] = 0; @@ -1029,6 +1029,10 @@ void Host_InitCommon( int argc, char **argv, const char *progname, qboolean bCha #endif } +#if XASH_WIN32 + COM_FixSlashes( host.rootdir ); +#endif + len = Q_strlen( host.rootdir ); if( len && host.rootdir[len - 1] == '/' ) @@ -1045,6 +1049,10 @@ void Host_InitCommon( int argc, char **argv, const char *progname, qboolean bCha Q_strncpy( host.rodir, roDir, sizeof( host.rodir )); } +#if XASH_WIN32 + COM_FixSlashes( host.rootdir ); +#endif + len = Q_strlen( host.rodir ); if( len && host.rodir[len - 1] == '/' ) From b4c0ccbedee350adaf9a2262ca5b7522ad4c37fc Mon Sep 17 00:00:00 2001 From: Alibek Omarov Date: Wed, 4 Jan 2023 22:33:21 +0300 Subject: [PATCH 331/490] filesystem: fix _wstat usage under Windows, remove PATH_SEPARATOR usage, fix listdirectory --- filesystem/filesystem.c | 65 +++++++++++++++++++++-------------------- 1 file changed, 33 insertions(+), 32 deletions(-) diff --git a/filesystem/filesystem.c b/filesystem/filesystem.c index a53131d8..53cf9af1 100644 --- a/filesystem/filesystem.c +++ b/filesystem/filesystem.c @@ -205,7 +205,7 @@ void listdirectory( stringlist_t *list, const char *path ) #endif #if XASH_WIN32 - Q_snprintf( pattern, sizeof( pattern ), "%s*", path ); + Q_snprintf( pattern, sizeof( pattern ), "%s/*", path ); // ask for the directory listing handle hFile = _findfirst( pattern, &n_file ); @@ -820,7 +820,7 @@ void FS_ParseGenericGameInfo( gameinfo_t *GameInfo, const char *buf, const qbool } // make sure what gamedir is really exist - if( !FS_SysFolderExists( va( "%s" PATH_SEPARATOR_STR "%s", fs_rootdir, GameInfo->falldir ))) + if( !FS_SysFolderExists( va( "%s/%s", fs_rootdir, GameInfo->falldir ))) GameInfo->falldir[0] = '\0'; } @@ -893,7 +893,8 @@ static qboolean FS_ReadGameInfo( const char *filepath, const char *gamedir, game char *afile; afile = (char *)FS_LoadFile( filepath, NULL, false ); - if( !afile ) return false; + if( !afile ) + return false; FS_InitGameInfo( GameInfo, gamedir ); @@ -937,7 +938,6 @@ static qboolean FS_ParseGameInfo( const char *gamedir, gameinfo_t *GameInfo ) { string liblist_path, gameinfo_path; string default_gameinfo_path; - gameinfo_t tmpGameInfo; qboolean haveUpdate = false; Q_snprintf( default_gameinfo_path, sizeof( default_gameinfo_path ), "%s/gameinfo.txt", fs_basedir ); @@ -945,18 +945,18 @@ static qboolean FS_ParseGameInfo( const char *gamedir, gameinfo_t *GameInfo ) Q_snprintf( liblist_path, sizeof( liblist_path ), "%s/liblist.gam", gamedir ); // here goes some RoDir magic... - if( COM_CheckStringEmpty( fs_rodir ) ) + if( COM_CheckStringEmpty( fs_rodir )) { - string filepath_ro, liblist_ro; + string gameinfo_ro, liblist_ro; fs_offset_t roLibListTime, roGameInfoTime, rwGameInfoTime; FS_AllowDirectPaths( true ); - Q_snprintf( filepath_ro, sizeof( filepath_ro ), "%s/%s/gameinfo.txt", fs_rodir, gamedir ); + Q_snprintf( gameinfo_ro, sizeof( gameinfo_ro ), "%s/%s/gameinfo.txt", fs_rodir, gamedir ); Q_snprintf( liblist_ro, sizeof( liblist_ro ), "%s/%s/liblist.gam", fs_rodir, gamedir ); roLibListTime = FS_SysFileTime( liblist_ro ); - roGameInfoTime = FS_SysFileTime( filepath_ro ); + roGameInfoTime = FS_SysFileTime( gameinfo_ro ); rwGameInfoTime = FS_SysFileTime( gameinfo_path ); if( roLibListTime > rwGameInfoTime ) @@ -965,17 +965,14 @@ static qboolean FS_ParseGameInfo( const char *gamedir, gameinfo_t *GameInfo ) } else if( roGameInfoTime > rwGameInfoTime ) { - char *afile_ro = (char *)FS_LoadDirectFile( filepath_ro, NULL ); + fs_offset_t len; + char *afile_ro = (char *)FS_LoadDirectFile( gameinfo_ro, &len ); if( afile_ro ) { - gameinfo_t gi; - + Con_DPrintf( "Copy rodir %s to rwdir %s\n", gameinfo_ro, gameinfo_path ); haveUpdate = true; - - FS_InitGameInfo( &gi, gamedir ); - FS_ParseGenericGameInfo( &gi, afile_ro, true ); - FS_WriteGameInfo( gameinfo_path, &gi ); + FS_WriteFile( gameinfo_path, afile_ro, len ); Mem_Free( afile_ro ); } } @@ -990,6 +987,7 @@ static qboolean FS_ParseGameInfo( const char *gamedir, gameinfo_t *GameInfo ) // force to create gameinfo for specified game if missing if(( FS_CheckForGameDir( gamedir ) || !Q_stricmp( fs_gamedir, gamedir )) && !FS_FileExists( gameinfo_path, false )) { + gameinfo_t tmpGameInfo; memset( &tmpGameInfo, 0, sizeof( tmpGameInfo )); if( FS_ReadGameInfo( default_gameinfo_path, gamedir, &tmpGameInfo )) @@ -1005,9 +1003,7 @@ static qboolean FS_ParseGameInfo( const char *gamedir, gameinfo_t *GameInfo ) if( !GameInfo || !FS_FileExists( gameinfo_path, false )) return false; // no dest - if( FS_ReadGameInfo( gameinfo_path, gamedir, GameInfo )) - return true; - return false; + return FS_ReadGameInfo( gameinfo_path, gamedir, GameInfo ); } /* @@ -1327,8 +1323,8 @@ qboolean FS_InitStdio( qboolean caseinsensitive, const char *rootdir, const char char roPath[MAX_SYSPATH]; char rwPath[MAX_SYSPATH]; - Q_snprintf( roPath, sizeof( roPath ), "%s" PATH_SEPARATOR_STR "%s" PATH_SEPARATOR_STR, fs_rodir, dirs.strings[i] ); - Q_snprintf( rwPath, sizeof( rwPath ), "%s" PATH_SEPARATOR_STR "%s" PATH_SEPARATOR_STR, fs_rootdir, dirs.strings[i] ); + Q_snprintf( roPath, sizeof( roPath ), "%s/%s/", fs_rodir, dirs.strings[i] ); + Q_snprintf( rwPath, sizeof( rwPath ), "%s/%s/", fs_rootdir, dirs.strings[i] ); // check if it's a directory if( !FS_SysFolderExists( roPath )) @@ -1368,7 +1364,7 @@ qboolean FS_InitStdio( qboolean caseinsensitive, const char *rootdir, const char for( i = 0; i < dirs.numstrings; i++ ) { - if( !FS_SysFolderExists( dirs.strings[i] ) || ( !Q_strcmp( dirs.strings[i], ".." ) && !fs_ext_path )) + if( !FS_SysFolderExists( dirs.strings[i] )) continue; if( FI.games[FI.numgames] == NULL ) @@ -1445,9 +1441,13 @@ Internal function used to determine filetime */ int FS_SysFileTime( const char *filename ) { +#if XASH_WIN32 + struct _stat buf; + if( _wstat( FS_PathToWideChar( filename ), &buf ) < 0 ) +#else struct stat buf; - - if( stat( filename, &buf ) == -1 ) + if( stat( filename, &buf ) < 0 ) +#endif return -1; return buf.st_mtime; @@ -1599,11 +1599,11 @@ Look for a file in the filesystem only */ qboolean FS_SysFileExists( const char *path ) { - struct stat buf; - #if XASH_WIN32 + struct _stat buf; if( _wstat( FS_PathToWideChar( path ), &buf ) < 0 ) #else + struct stat buf; if( stat( path, &buf ) < 0 ) #endif return false; @@ -1620,11 +1620,11 @@ Look for a existing folder */ qboolean FS_SysFolderExists( const char *path ) { - struct stat buf; - #if XASH_WIN32 + struct _stat buf; if( _wstat( FS_PathToWideChar( path ), &buf ) < 0 ) #else + struct stat buf; if( stat( path, &buf ) < 0 ) #endif return false; @@ -1641,10 +1641,11 @@ Check if filesystem entry exists at all, don't mind the type */ qboolean FS_SysFileOrFolderExists( const char *path ) { - struct stat buf; #if XASH_WIN32 + struct _stat buf; return _wstat( FS_PathToWideChar( path ), &buf ) >= 0; #else + struct stat buf; return stat( path, &buf ) >= 0; #endif } @@ -1692,7 +1693,8 @@ searchpath_t *FS_FindFile( const char *name, int *index, char *fixedname, size_t pack_ind = search->pfnFindFile( search, name, fixedname, len ); if( pack_ind >= 0 ) { - if( index ) *index = pack_ind; + if( index ) + *index = pack_ind; return search; } } @@ -1701,8 +1703,9 @@ searchpath_t *FS_FindFile( const char *name, int *index, char *fixedname, size_t { char netpath[MAX_SYSPATH], dirpath[MAX_SYSPATH]; - Q_snprintf( dirpath, sizeof( dirpath ), "%s" PATH_SEPARATOR_STR, fs_rootdir ); + Q_snprintf( dirpath, sizeof( dirpath ), "%s/", fs_rootdir ); Q_snprintf( netpath, sizeof( netpath ), "%s%s", dirpath, name ); + if( FS_SysFileExists( netpath )) { static searchpath_t fs_directpath; @@ -1713,9 +1716,7 @@ searchpath_t *FS_FindFile( const char *name, int *index, char *fixedname, size_t // just copy the name, we don't do case sensitivity fix there if( fixedname ) - { Q_strncpy( fixedname, name, len ); - } FS_InitDirectorySearchpath( &fs_directpath, dirpath, 0 ); if( index != NULL ) From 34eb258caecef6b5602ff8ceee0c435efd6c15e6 Mon Sep 17 00:00:00 2001 From: Alibek Omarov Date: Wed, 4 Jan 2023 22:34:45 +0300 Subject: [PATCH 332/490] filesystem: dir: remove PATH_SEPARATOR, fix return value when directory is caseinsensitive --- filesystem/dir.c | 13 ++++++------- 1 file changed, 6 insertions(+), 7 deletions(-) diff --git a/filesystem/dir.c b/filesystem/dir.c index 5e073284..550754a5 100644 --- a/filesystem/dir.c +++ b/filesystem/dir.c @@ -287,9 +287,9 @@ qboolean FS_FixFileCase( dir_t *dir, const char *path, char *dst, const size_t l if( !FS_AppendToPath( dst, &i, len, dir->name, path, "init" )) return false; - for( prev = path, next = Q_strchrnul( prev, PATH_SEPARATOR ); + for( prev = path, next = Q_strchrnul( prev, '/' ); ; - prev = next + 1, next = Q_strchrnul( prev, PATH_SEPARATOR )) + prev = next + 1, next = Q_strchrnul( prev, '/' )) { qboolean uptodate = false; // do not run second scan if we're just updated our directory list size_t temp; @@ -309,9 +309,8 @@ qboolean FS_FixFileCase( dir_t *dir, const char *path, char *dst, const size_t l if( !FS_AppendToPath( dst, &i, len, prev, path, "caseinsensitive entry" )) return false; - if( FS_SysFileOrFolderExists( dst )) // file not found - return createpath; - break; + // check file existense + return createpath ? true : FS_SysFileOrFolderExists( dst ); } // get our entry name @@ -349,10 +348,10 @@ qboolean FS_FixFileCase( dir_t *dir, const char *path, char *dst, const size_t l i = temp; // end of string, found file, return - if( next[0] == '\0' || ( next[0] == PATH_SEPARATOR && next[1] == '\0' )) + if( next[0] == '\0' || ( next[0] == '/' && next[1] == '\0' )) break; - if( !FS_AppendToPath( dst, &i, len, PATH_SEPARATOR_STR, path, "path separator" )) + if( !FS_AppendToPath( dst, &i, len, "/", path, "path separator" )) return false; } From 4684f174ee8d1ff800db9c9a4aa98d6367097cc7 Mon Sep 17 00:00:00 2001 From: Alibek Omarov Date: Thu, 5 Jan 2023 06:06:07 +0300 Subject: [PATCH 333/490] public: completely get rid of PATH_SEPARATOR macros --- common/port.h | 15 --------------- public/crtlib.c | 9 ++++----- 2 files changed, 4 insertions(+), 20 deletions(-) diff --git a/common/port.h b/common/port.h index d56dac85..05a732da 100644 --- a/common/port.h +++ b/common/port.h @@ -19,9 +19,6 @@ GNU General Public License for more details. #include "build.h" -#define PATH_SEPARATOR_NIX '/' -#define PATH_SEPARATOR_WIN '\\' - #if !XASH_WIN32 #if XASH_APPLE #include @@ -43,15 +40,10 @@ GNU General Public License for more details. #if XASH_POSIX #include #include - - #define PATH_SEPARATOR PATH_SEPARATOR_NIX #define HAVE_DUP - #define O_BINARY 0 #define O_TEXT 0 #define _mkdir( x ) mkdir( x, S_IRWXU | S_IRWXG | S_IROTH | S_IXOTH ) - #elif XASH_DOS4GW - #define PATH_SEPARATOR PATH_SEPARATOR_WIN #endif typedef void* HANDLE; @@ -62,7 +54,6 @@ GNU General Public License for more details. int x, y; } POINT; #else // WIN32 - #define PATH_SEPARATOR PATH_SEPARATOR_WIN #ifdef __MINGW32__ #define _inline static inline #define FORCEINLINE inline __attribute__((always_inline)) @@ -90,12 +81,6 @@ GNU General Public License for more details. #define XASH_LOW_MEMORY 0 #endif -#if PATH_SEPARATOR == PATH_SEPARATOR_WIN -#define PATH_SEPARATOR_STR "\\" -#else // PATH_SEPARATOR == PATH_SEPARATOR_NIX -#define PATH_SEPARATOR_STR "/" -#endif - #include #include #include diff --git a/public/crtlib.c b/public/crtlib.c index 3561a1be..a0867558 100644 --- a/public/crtlib.c +++ b/public/crtlib.c @@ -837,16 +837,15 @@ void COM_RemoveLineFeed( char *str ) ============ COM_FixSlashes -Changes all '/' characters into '\' characters, in place. +Changes all '\' characters into '/' characters, in place. ============ */ void COM_FixSlashes( char *pname ) { - while( *pname ) + for( ; *pname; pname++ ) { - if( *pname == PATH_SEPARATOR_WIN ) - *pname = PATH_SEPARATOR_NIX; - pname++; + if( *pname == '\\' ) + *pname = '/'; } } From 4bbd0cc40408c59d48e615b7cc3276e1069b77b8 Mon Sep 17 00:00:00 2001 From: Alibek Omarov Date: Thu, 5 Jan 2023 06:07:56 +0300 Subject: [PATCH 334/490] ref: add new special rendermode that specifically used for modulate mode in engine's ScreenFade --- engine/ref_api.h | 4 ++++ ref/gl/gl_backend.c | 4 ++++ ref/soft/r_draw.c | 8 ++++++-- 3 files changed, 14 insertions(+), 2 deletions(-) diff --git a/engine/ref_api.h b/engine/ref_api.h index c5215541..427b3f05 100644 --- a/engine/ref_api.h +++ b/engine/ref_api.h @@ -67,6 +67,10 @@ GNU General Public License for more details. #define FWORLD_WATERALPHA BIT( 2 ) #define FWORLD_HAS_DELUXEMAP BIT( 3 ) +// special rendermode for screenfade modulate +// (probably will be expanded at some point) +#define kRenderScreenFadeModulate 0x1000 + typedef enum { DEMO_INACTIVE = 0, diff --git a/ref/gl/gl_backend.c b/ref/gl/gl_backend.c index da530405..f3f4e2a5 100644 --- a/ref/gl/gl_backend.c +++ b/ref/gl/gl_backend.c @@ -417,6 +417,10 @@ void GL_SetRenderMode( int mode ) pglDisable( GL_ALPHA_TEST ); pglBlendFunc( GL_SRC_ALPHA, GL_ONE ); break; + case kRenderScreenFadeModulate: + pglEnable( GL_BLEND ); + pglDisable( GL_ALPHA_TEST ); + pglBlendFunc( GL_ZERO, GL_SRC_COLOR ); } } diff --git a/ref/soft/r_draw.c b/ref/soft/r_draw.c index 24ff99f7..22ffe279 100644 --- a/ref/soft/r_draw.c +++ b/ref/soft/r_draw.c @@ -165,11 +165,15 @@ void R_DrawStretchPicImplementation( int x, int y, int w, int h, int s1, int t1, pixel_t screen = dest[u]; dest[u] = vid.addmap[(src & 0xff00)|(screen>>8)] << 8 | (screen & 0xff) | ((src & 0xff) >> 0); } + else if( vid.rendermode == kRenderScreenFadeModulate ) + { + pixel_t screen = dest[u]; + dest[u] = BLEND_COLOR( screen, vid.color ); + } else if( alpha < 7) // && (vid.rendermode == kRenderTransAlpha || vid.rendermode == kRenderTransTexture ) ) { pixel_t screen = dest[u]; // | 0xff & screen & src ; - dest[u] = BLEND_ALPHA( alpha, src, screen);//vid.alphamap[( alpha << 16)|(src & 0xff00)|(screen>>8)] << 8 | (screen & 0xff) >> 3 | ((src & 0xff) >> 3); - + dest[u] = BLEND_ALPHA( alpha, src, screen );//vid.alphamap[( alpha << 16)|(src & 0xff00)|(screen>>8)] << 8 | (screen & 0xff) >> 3 | ((src & 0xff) >> 3); } else dest[u] = src; From a4865fd2fcaab9671fe5c94d101a17daec2e50de Mon Sep 17 00:00:00 2001 From: Alibek Omarov Date: Thu, 5 Jan 2023 06:09:04 +0300 Subject: [PATCH 335/490] engine: client: fix ScreenFade FFADE_MODULATE rendering, more accurate alpha blending --- engine/client/cl_game.c | 73 +++++++++++++++++++++++++++-------------- 1 file changed, 48 insertions(+), 25 deletions(-) diff --git a/engine/client/cl_game.c b/engine/client/cl_game.c index 4ceed3b8..290b3faa 100644 --- a/engine/client/cl_game.c +++ b/engine/client/cl_game.c @@ -581,6 +581,41 @@ void CL_DrawCenterPrint( void ) } } +static int V_FadeAlpha( screenfade_t *sf ) +{ + int alpha; + + if( cl.time > sf->fadeReset && cl.time > sf->fadeEnd ) + { + if( !FBitSet( sf->fadeFlags, FFADE_STAYOUT )) + return 0; + } + + if( FBitSet( sf->fadeFlags, FFADE_STAYOUT )) + { + alpha = sf->fadealpha; + if( FBitSet( sf->fadeFlags, FFADE_OUT ) && sf->fadeTotalEnd > cl.time ) + { + alpha += sf->fadeSpeed * ( sf->fadeTotalEnd - cl.time ); + } + else + { + sf->fadeEnd = cl.time + 0.1; + } + } + else + { + alpha = sf->fadeSpeed * ( sf->fadeEnd - cl.time ); + if( FBitSet( sf->fadeFlags, FFADE_OUT )) + { + alpha += sf->fadealpha; + } + } + alpha = bound( 0, alpha, sf->fadealpha ); + + return alpha; +} + /* ============= CL_DrawScreenFade @@ -592,41 +627,29 @@ can be modulated void CL_DrawScreenFade( void ) { screenfade_t *sf = &clgame.fade; - int iFadeAlpha, testFlags; + int alpha; - // keep pushing reset time out indefinitely - if( sf->fadeFlags & FFADE_STAYOUT ) - sf->fadeReset = cl.time + 0.1f; + alpha = V_FadeAlpha( sf ); - if( sf->fadeReset == 0.0f && sf->fadeEnd == 0.0f ) - return; // inactive - - // all done? - if(( cl.time > sf->fadeReset ) && ( cl.time > sf->fadeEnd )) - { - memset( &clgame.fade, 0, sizeof( clgame.fade )); + if( !alpha ) return; - } - testFlags = (sf->fadeFlags & ~FFADE_MODULATE); - - // fading... - if( testFlags == FFADE_STAYOUT ) + if( FBitSet( sf->fadeFlags, FFADE_MODULATE )) { - iFadeAlpha = sf->fadealpha; + ref.dllFuncs.GL_SetRenderMode( kRenderScreenFadeModulate ); + + ref.dllFuncs.Color4ub( + (uint16_t)( sf->fader * alpha + ( 255 - alpha ) * 255 ) >> 8, + (uint16_t)( sf->fadeg * alpha + ( 255 - alpha ) * 255 ) >> 8, + (uint16_t)( sf->fadeb * alpha + ( 255 - alpha ) * 255 ) >> 8, + 255 ); } else { - iFadeAlpha = sf->fadeSpeed * ( sf->fadeEnd - cl.time ); - if( sf->fadeFlags & FFADE_OUT ) iFadeAlpha += sf->fadealpha; - iFadeAlpha = bound( 0, iFadeAlpha, sf->fadealpha ); + ref.dllFuncs.GL_SetRenderMode( kRenderTransTexture ); + ref.dllFuncs.Color4ub( sf->fader, sf->fadeg, sf->fadeb, alpha ); } - ref.dllFuncs.Color4ub( sf->fader, sf->fadeg, sf->fadeb, iFadeAlpha ); - - if( sf->fadeFlags & FFADE_MODULATE ) - ref.dllFuncs.GL_SetRenderMode( kRenderTransAdd ); - else ref.dllFuncs.GL_SetRenderMode( kRenderTransTexture ); ref.dllFuncs.R_DrawStretchPic( 0, 0, refState.width, refState.height, 0, 0, 1, 1, R_GetBuiltinTexture( REF_WHITE_TEXTURE )); ref.dllFuncs.Color4ub( 255, 255, 255, 255 ); From c6bfc82019782c0fcab50ed886005bcaaae45411 Mon Sep 17 00:00:00 2001 From: Alibek Omarov Date: Thu, 5 Jan 2023 07:09:23 +0300 Subject: [PATCH 336/490] ref: soft: implement screenshots --- ref/soft/r_context.c | 5 -- ref/soft/r_glblit.c | 117 +++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 117 insertions(+), 5 deletions(-) diff --git a/ref/soft/r_context.c b/ref/soft/r_context.c index 747e029d..d02ff9e8 100644 --- a/ref/soft/r_context.c +++ b/ref/soft/r_context.c @@ -341,11 +341,6 @@ void GAME_EXPORT R_SetupSky(const char *skyboxname) } -qboolean GAME_EXPORT VID_ScreenShot(const char *filename, int shot_type) -{ - return false; -} - qboolean GAME_EXPORT VID_CubemapShot(const char *base, uint size, const float *vieworg, qboolean skyshot) { // cubemaps? in my softrender??? diff --git a/ref/soft/r_glblit.c b/ref/soft/r_glblit.c index 99d609b1..84eb2ff4 100644 --- a/ref/soft/r_glblit.c +++ b/ref/soft/r_glblit.c @@ -812,3 +812,120 @@ void R_BlitScreen( void ) swblit.pUnlockBuffer(); // gEngfuncs.Con_Printf("blit end\n"); } + +static uint32_t Get8888PixelAt( int u, int start ) +{ + uint32_t s; + switch( swblit.bpp ) + { + case 2: + { + pixel_t color = vid.screen[vid.buffer[start + u]]; + uint8_t c[3]; + c[0] = (((( color >> 11 ) & 0x1F ) * 527 ) + 23 ) >> 6; + c[1] = (((( color >> 5 ) & 0x3F ) * 259 ) + 33 ) >> 6; + c[2] = (((( color ) & 0x1F ) * 527 ) + 23 ) >> 6; + + s = c[0] << 16 | c[1] << 8 | c[2]; + break; + } + case 3: + case 4: + default: + s = vid.screen32[vid.buffer[start + u]]; + break; + } + return s | 0xFF000000; +} + +qboolean GAME_EXPORT VID_ScreenShot( const char *filename, int shot_type ) +{ + rgbdata_t *r_shot; + uint flags = IMAGE_FLIP_Y; + int width = 0, height = 0, u, v; + qboolean result; + + r_shot = Mem_Calloc( r_temppool, sizeof( rgbdata_t )); + r_shot->width = (vid.width + 3) & ~3; + r_shot->height = (vid.height + 3) & ~3; + r_shot->flags = IMAGE_HAS_COLOR; + r_shot->type = PF_BGRA_32; // was RGBA + r_shot->size = r_shot->width * r_shot->height * gEngfuncs.Image_GetPFDesc( r_shot->type )->bpp; + r_shot->palette = NULL; + r_shot->buffer = Mem_Malloc( r_temppool, r_shot->size ); + + // get screen frame + if( swblit.rotate ) + { + uint32_t *pbuf = (uint32_t *)r_shot->buffer; + + for( v = 0; v < vid.height; v++ ) + { + uint start = vid.rowbytes * ( vid.height - v ); + uint d = swblit.stride - v - 1; + + for( u = 0; u < vid.width; u++ ) + { + pbuf[d] = Get8888PixelAt( u, start ); + d += swblit.stride; + } + } + } + else + { + uint32_t *pbuf = (uint32_t *)r_shot->buffer; + + for( v = 0; v < vid.height;v++) + { + uint start = vid.rowbytes * ( vid.height - v ); + uint dstart = swblit.stride * v; + + for( u = 0; u < vid.width; u++ ) + { + pbuf[dstart + u] = Get8888PixelAt( u, start ); + } + } + } + + switch( shot_type ) + { + case VID_SCREENSHOT: + break; + case VID_SNAPSHOT: + gEngfuncs.fsapi->AllowDirectPaths( true ); + break; + case VID_LEVELSHOT: + flags |= IMAGE_RESAMPLE; + if( gpGlobals->wideScreen ) + { + height = 480; + width = 800; + } + else + { + height = 480; + width = 640; + } + break; + case VID_MINISHOT: + flags |= IMAGE_RESAMPLE; + height = 200; + width = 320; + break; + case VID_MAPSHOT: + flags |= IMAGE_RESAMPLE|IMAGE_QUANTIZE; // GoldSrc request overviews in 8-bit format + height = 768; + width = 1024; + break; + } + + gEngfuncs.Image_Process( &r_shot, width, height, flags, 0.0f ); + + // write image + result = gEngfuncs.FS_SaveImage( filename, r_shot ); + gEngfuncs.fsapi->AllowDirectPaths( false ); // always reset after store screenshot + gEngfuncs.FS_FreeImage( r_shot ); + + return result; +} + From 49a65edfc31230cc1930f3789a7a79f932a1a9ed Mon Sep 17 00:00:00 2001 From: Alibek Omarov Date: Thu, 5 Jan 2023 07:24:24 +0300 Subject: [PATCH 337/490] engine: imagelib: img_quant: fix a bug in quantizer (thanks, @SNMetamorph for fix) --- engine/common/imagelib/img_quant.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/engine/common/imagelib/img_quant.c b/engine/common/imagelib/img_quant.c index eb5e2e50..e450e14f 100644 --- a/engine/common/imagelib/img_quant.c +++ b/engine/common/imagelib/img_quant.c @@ -408,7 +408,7 @@ void learn( void ) if( rad ) alterneigh( rad, j, r, g, b ); // alter neighbours p += step; - if( p >= lim ) p -= lengthcount; + while( p >= lim ) p -= lengthcount; i++; From 4cb109abe02586c548ffeb1916ccc778139d0efd Mon Sep 17 00:00:00 2001 From: Alibek Omarov Date: Thu, 5 Jan 2023 23:50:42 +0300 Subject: [PATCH 338/490] engine: make playermove funcs truly shared between client and server --- engine/client/cl_game.c | 31 ++------ engine/client/cl_pmove.c | 131 +++------------------------------ engine/client/client.h | 2 + engine/client/ref_common.c | 2 +- engine/common/common.h | 1 - engine/common/pm_local.h | 7 ++ engine/common/pm_trace.c | 147 +++++++++++++++++++++++++++++++++++++ engine/server/sv_pmove.c | 126 ++----------------------------- 8 files changed, 180 insertions(+), 267 deletions(-) diff --git a/engine/client/cl_game.c b/engine/client/cl_game.c index 290b3faa..e58bf515 100644 --- a/engine/client/cl_game.c +++ b/engine/client/cl_game.c @@ -2222,14 +2222,7 @@ pfnPointContents */ static int GAME_EXPORT pfnPointContents( const float *p, int *truecontents ) { - int cont, truecont; - - truecont = cont = PM_PointContents( clgame.pmove, p ); - if( truecontents ) *truecontents = truecont; - - if( cont <= CONTENTS_CURRENT_0 && cont >= CONTENTS_CURRENT_DOWN ) - cont = CONTENTS_WATER; - return cont; + return PM_PointContentsPmove( clgame.pmove, p, truecontents ); } /* @@ -2534,19 +2527,13 @@ void GAME_EXPORT CL_PlayerTraceExt( float *start, float *end, int traceFlags, in /* ============= -pfnTraceTexture +CL_TraceTexture ============= */ -static const char *pfnTraceTexture( int ground, float *vstart, float *vend ) +const char * GAME_EXPORT PM_CL_TraceTexture( int ground, float *vstart, float *vend ) { - physent_t *pe; - - if( ground < 0 || ground >= clgame.pmove->numphysent ) - return NULL; // bad ground - - pe = &clgame.pmove->physents[ground]; - return PM_TraceTexture( pe, vstart, vend ); + return PM_TraceTexturePmove( clgame.pmove, ground, vstart, vend ); } /* @@ -2557,13 +2544,7 @@ pfnTraceSurface */ struct msurface_s *pfnTraceSurface( int ground, float *vstart, float *vend ) { - physent_t *pe; - - if( ground < 0 || ground >= clgame.pmove->numphysent ) - return NULL; // bad ground - - pe = &clgame.pmove->physents[ground]; - return PM_TraceSurface( pe, vstart, vend ); + return PM_TraceSurfacePmove( clgame.pmove, ground, vstart, vend ); } /* @@ -3741,7 +3722,7 @@ static event_api_t gEventApi = CL_WeaponAnim, pfnPrecacheEvent, CL_PlaybackEvent, - pfnTraceTexture, + PM_CL_TraceTexture, pfnStopAllSounds, pfnKillEvents, CL_PlayerTraceExt, // Xash3D added diff --git a/engine/client/cl_pmove.c b/engine/client/cl_pmove.c index 04dedf1f..ce9a2744 100644 --- a/engine/client/cl_pmove.c +++ b/engine/client/cl_pmove.c @@ -713,33 +713,12 @@ static int GAME_EXPORT pfnTestPlayerPosition( float *pos, pmtrace_t *ptrace ) static void GAME_EXPORT pfnStuckTouch( int hitent, pmtrace_t *tr ) { - int i; - - for( i = 0; i < clgame.pmove->numtouch; i++ ) - { - if( clgame.pmove->touchindex[i].ent == hitent ) - return; - } - - if( clgame.pmove->numtouch >= MAX_PHYSENTS ) - return; - - VectorCopy( clgame.pmove->velocity, tr->deltavelocity ); - tr->ent = hitent; - - clgame.pmove->touchindex[clgame.pmove->numtouch++] = *tr; + return PM_StuckTouch( clgame.pmove, hitent, tr ); } static int GAME_EXPORT pfnPointContents( float *p, int *truecontents ) { - int cont, truecont; - - truecont = cont = PM_PointContents( clgame.pmove, p ); - if( truecontents ) *truecontents = truecont; - - if( cont <= CONTENTS_CURRENT_0 && cont >= CONTENTS_CURRENT_DOWN ) - cont = CONTENTS_WATER; - return cont; + return PM_PointContentsPmove( clgame.pmove, p, truecontents ); } static int GAME_EXPORT pfnTruePointContents( float *p ) @@ -757,91 +736,19 @@ static pmtrace_t GAME_EXPORT pfnPlayerTrace( float *start, float *end, int trace return PM_PlayerTraceExt( clgame.pmove, start, end, traceFlags, clgame.pmove->numphysent, clgame.pmove->physents, ignore_pe, NULL ); } -pmtrace_t *PM_TraceLine( float *start, float *end, int flags, int usehull, int ignore_pe ) +pmtrace_t *PM_CL_TraceLine( float *start, float *end, int flags, int usehull, int ignore_pe ) { - static pmtrace_t tr; - int old_usehull; - - old_usehull = clgame.pmove->usehull; - clgame.pmove->usehull = usehull; - - switch( flags ) - { - case PM_TRACELINE_PHYSENTSONLY: - tr = PM_PlayerTraceExt( clgame.pmove, start, end, 0, clgame.pmove->numphysent, clgame.pmove->physents, ignore_pe, NULL ); - break; - case PM_TRACELINE_ANYVISIBLE: - tr = PM_PlayerTraceExt( clgame.pmove, start, end, 0, clgame.pmove->numvisent, clgame.pmove->visents, ignore_pe, NULL ); - break; - } - - clgame.pmove->usehull = old_usehull; - - return &tr; + return PM_TraceLine( clgame.pmove, start, end, flags, usehull, ignore_pe ); } -static hull_t *pfnHullForBsp( physent_t *pe, float *offset ) +static void *pfnHullForBsp( physent_t *pe, float *offset ) { return PM_HullForBsp( pe, clgame.pmove, offset ); } static float GAME_EXPORT pfnTraceModel( physent_t *pe, float *start, float *end, trace_t *trace ) { - int old_usehull; - vec3_t start_l, end_l; - vec3_t offset, temp; - qboolean rotated; - matrix4x4 matrix; - hull_t *hull; - - PM_InitTrace( trace, end ); - - old_usehull = clgame.pmove->usehull; - clgame.pmove->usehull = 2; - - hull = PM_HullForBsp( pe, clgame.pmove, offset ); - - clgame.pmove->usehull = old_usehull; - - if( pe->solid == SOLID_BSP && !VectorIsNull( pe->angles )) - rotated = true; - else rotated = false; - - if( rotated ) - { - Matrix4x4_CreateFromEntity( matrix, pe->angles, offset, 1.0f ); - Matrix4x4_VectorITransform( matrix, start, start_l ); - Matrix4x4_VectorITransform( matrix, end, end_l ); - } - else - { - VectorSubtract( start, offset, start_l ); - VectorSubtract( end, offset, end_l ); - } - - PM_RecursiveHullCheck( hull, hull->firstclipnode, 0, 1, start_l, end_l, (pmtrace_t *)trace ); - trace->ent = NULL; - - if( rotated ) - { - VectorCopy( trace->plane.normal, temp ); - Matrix4x4_TransformPositivePlane( matrix, temp, trace->plane.dist, trace->plane.normal, &trace->plane.dist ); - } - - VectorLerp( start, trace->fraction, end, trace->endpos ); - - return trace->fraction; -} - -static const char *pfnTraceTexture( int ground, float *vstart, float *vend ) -{ - physent_t *pe; - - if( ground < 0 || ground >= clgame.pmove->numphysent ) - return NULL; // bad ground - - pe = &clgame.pmove->physents[ground]; - return PM_TraceTexture( pe, vstart, vend ); + return PM_TraceModel( clgame.pmove, pe, start, end, trace ); } static void GAME_EXPORT pfnPlaySound( int channel, const char *sample, float volume, float attenuation, int fFlags, int pitch ) @@ -870,25 +777,7 @@ static int GAME_EXPORT pfnTestPlayerPositionEx( float *pos, pmtrace_t *ptrace, p static pmtrace_t *pfnTraceLineEx( float *start, float *end, int flags, int usehull, pfnIgnore pmFilter ) { - static pmtrace_t tr; - int old_usehull; - - old_usehull = clgame.pmove->usehull; - clgame.pmove->usehull = usehull; - - switch( flags ) - { - case PM_TRACELINE_PHYSENTSONLY: - tr = PM_PlayerTraceExt( clgame.pmove, start, end, 0, clgame.pmove->numphysent, clgame.pmove->physents, -1, pmFilter ); - break; - case PM_TRACELINE_ANYVISIBLE: - tr = PM_PlayerTraceExt( clgame.pmove, start, end, 0, clgame.pmove->numvisent, clgame.pmove->visents, -1, pmFilter ); - break; - } - - clgame.pmove->usehull = old_usehull; - - return &tr; + return PM_TraceLineEx( clgame.pmove, start, end, flags, usehull, pmFilter ); } /* @@ -932,19 +821,19 @@ void CL_InitClientMove( void ) clgame.pmove->PM_TruePointContents = pfnTruePointContents; clgame.pmove->PM_HullPointContents = pfnHullPointContents; clgame.pmove->PM_PlayerTrace = pfnPlayerTrace; - clgame.pmove->PM_TraceLine = PM_TraceLine; + clgame.pmove->PM_TraceLine = PM_CL_TraceLine; clgame.pmove->RandomLong = COM_RandomLong; clgame.pmove->RandomFloat = COM_RandomFloat; clgame.pmove->PM_GetModelType = pfnGetModelType; clgame.pmove->PM_GetModelBounds = pfnGetModelBounds; - clgame.pmove->PM_HullForBsp = (void*)pfnHullForBsp; + clgame.pmove->PM_HullForBsp = pfnHullForBsp; clgame.pmove->PM_TraceModel = pfnTraceModel; clgame.pmove->COM_FileSize = COM_FileSize; clgame.pmove->COM_LoadFile = COM_LoadFile; clgame.pmove->COM_FreeFile = COM_FreeFile; clgame.pmove->memfgets = COM_MemFgets; clgame.pmove->PM_PlaySound = pfnPlaySound; - clgame.pmove->PM_TraceTexture = pfnTraceTexture; + clgame.pmove->PM_TraceTexture = PM_CL_TraceTexture; clgame.pmove->PM_PlaybackEventFull = pfnPlaybackEventFull; clgame.pmove->PM_PlayerTraceEx = pfnPlayerTraceEx; clgame.pmove->PM_TestPlayerPositionEx = pfnTestPlayerPositionEx; diff --git a/engine/client/client.h b/engine/client/client.h index 1ed78ab2..957a8719 100644 --- a/engine/client/client.h +++ b/engine/client/client.h @@ -830,6 +830,8 @@ int CL_GetScreenInfo( SCREENINFO *pscrinfo ); void CL_FillRGBA( int x, int y, int width, int height, int r, int g, int b, int a ); void CL_PlayerTrace( float *start, float *end, int traceFlags, int ignore_pe, pmtrace_t *tr ); void CL_PlayerTraceExt( float *start, float *end, int traceFlags, int (*pfnIgnore)( physent_t *pe ), pmtrace_t *tr ); +pmtrace_t *PM_CL_TraceLine( float *start, float *end, int flags, int usehull, int ignore_pe ); +const char *PM_CL_TraceTexture( int ground, float *vstart, float *vend ); void CL_SetTraceHull( int hull ); void CL_GetMousePosition( int *mx, int *my ); // TODO: move to input cl_entity_t* CL_GetViewModel( void ); diff --git a/engine/client/ref_common.c b/engine/client/ref_common.c index 2687a509..b2b51a66 100644 --- a/engine/client/ref_common.c +++ b/engine/client/ref_common.c @@ -359,7 +359,7 @@ static ref_api_t gEngfuncs = pfnGetPhysent, pfnTraceSurface, - PM_TraceLine, + PM_CL_TraceLine, CL_VisTraceLine, CL_TraceLine, pfnGetMoveVars, diff --git a/engine/common/common.h b/engine/common/common.h index c95d456b..856e5a4a 100644 --- a/engine/common/common.h +++ b/engine/common/common.h @@ -753,7 +753,6 @@ struct cmd_s *Cmd_GetFirstFunctionHandle( void ); struct cmd_s *Cmd_GetNextFunctionHandle( struct cmd_s *cmd ); struct cmdalias_s *Cmd_AliasGetList( void ); const char *Cmd_GetName( struct cmd_s *cmd ); -struct pmtrace_s *PM_TraceLine( float *start, float *end, int flags, int usehull, int ignore_pe ); void SV_StartSound( edict_t *ent, int chan, const char *sample, float vol, float attn, int flags, int pitch ); void SV_StartMusic( const char *curtrack, const char *looptrack, int position ); void SV_CreateDecal( sizebuf_t *msg, const float *origin, int decalIndex, int entityIndex, int modelIndex, int flags, float scale ); diff --git a/engine/common/pm_local.h b/engine/common/pm_local.h index 6ad6d8e2..1c2b08cf 100644 --- a/engine/common/pm_local.h +++ b/engine/common/pm_local.h @@ -38,6 +38,13 @@ int PM_TestPlayerPosition( playermove_t *pmove, vec3_t pos, pmtrace_t *ptrace, p int PM_HullPointContents( hull_t *hull, int num, const vec3_t p ); int PM_TruePointContents( playermove_t *pmove, const vec3_t p ); int PM_PointContents( playermove_t *pmove, const vec3_t p ); +float PM_TraceModel( playermove_t *pmove, physent_t *pe, float *start, float *end, trace_t *trace ); +pmtrace_t *PM_TraceLine( playermove_t *pmove, float *start, float *end, int flags, int usehull, int ignore_pe ); +pmtrace_t *PM_TraceLineEx( playermove_t *pmove, float *start, float *end, int flags, int usehull, pfnIgnore pmFilter ); +struct msurface_s *PM_TraceSurfacePmove( playermove_t *pmove, int ground, float *vstart, float *vend ); +const char *PM_TraceTexturePmove( playermove_t *pmove, int ground, float *vstart, float *vend ); +int PM_PointContentsPmove( playermove_t *pmove, const float *p, int *truecontents ); +void PM_StuckTouch( playermove_t *pmove, int hitent, pmtrace_t *tr ); void PM_ConvertTrace( trace_t *out, pmtrace_t *in, edict_t *ent ); static inline void PM_InitTrace( trace_t *trace, const vec3_t end ) diff --git a/engine/common/pm_trace.c b/engine/common/pm_trace.c index 968cb23e..6b62a564 100644 --- a/engine/common/pm_trace.c +++ b/engine/common/pm_trace.c @@ -732,3 +732,150 @@ int PM_PointContents( playermove_t *pmove, const vec3_t p ) return contents; } + +/* +============= +PM_TraceModel + +============= +*/ +float PM_TraceModel( playermove_t *pmove, physent_t *pe, float *start, float *end, trace_t *trace ) +{ + int old_usehull; + vec3_t start_l, end_l; + vec3_t offset, temp; + qboolean rotated; + matrix4x4 matrix; + hull_t *hull; + + PM_InitTrace( trace, end ); + + old_usehull = pmove->usehull; + pmove->usehull = 2; + + hull = PM_HullForBsp( pe, pmove, offset ); + + pmove->usehull = old_usehull; + + if( pe->solid == SOLID_BSP && !VectorIsNull( pe->angles )) + rotated = true; + else rotated = false; + + if( rotated ) + { + Matrix4x4_CreateFromEntity( matrix, pe->angles, offset, 1.0f ); + Matrix4x4_VectorITransform( matrix, start, start_l ); + Matrix4x4_VectorITransform( matrix, end, end_l ); + } + else + { + VectorSubtract( start, offset, start_l ); + VectorSubtract( end, offset, end_l ); + } + + PM_RecursiveHullCheck( hull, hull->firstclipnode, 0, 1, start_l, end_l, (pmtrace_t *)trace ); + trace->ent = NULL; + + if( rotated ) + { + VectorCopy( trace->plane.normal, temp ); + Matrix4x4_TransformPositivePlane( matrix, temp, trace->plane.dist, trace->plane.normal, &trace->plane.dist ); + } + + VectorLerp( start, trace->fraction, end, trace->endpos ); + + return trace->fraction; +} + +pmtrace_t *PM_TraceLine( playermove_t *pmove, float *start, float *end, int flags, int usehull, int ignore_pe ) +{ + static pmtrace_t tr; + int old_usehull; + + old_usehull = pmove->usehull; + pmove->usehull = usehull; + + switch( flags ) + { + case PM_TRACELINE_PHYSENTSONLY: + tr = PM_PlayerTraceExt( pmove, start, end, 0, pmove->numphysent, pmove->physents, ignore_pe, NULL ); + break; + case PM_TRACELINE_ANYVISIBLE: + tr = PM_PlayerTraceExt( pmove, start, end, 0, pmove->numvisent, pmove->visents, ignore_pe, NULL ); + break; + } + + pmove->usehull = old_usehull; + + return &tr; +} + +pmtrace_t *PM_TraceLineEx( playermove_t *pmove, float *start, float *end, int flags, int usehull, pfnIgnore pmFilter ) +{ + static pmtrace_t tr; + int old_usehull; + + old_usehull = pmove->usehull; + pmove->usehull = usehull; + + switch( flags ) + { + case PM_TRACELINE_PHYSENTSONLY: + tr = PM_PlayerTraceExt( pmove, start, end, 0, pmove->numphysent, pmove->physents, -1, pmFilter ); + break; + case PM_TRACELINE_ANYVISIBLE: + tr = PM_PlayerTraceExt( pmove, start, end, 0, pmove->numvisent, pmove->visents, -1, pmFilter ); + break; + } + + pmove->usehull = old_usehull; + + return &tr; +} + +struct msurface_s *PM_TraceSurfacePmove( playermove_t *pmove, int ground, float *vstart, float *vend ) +{ + if( ground < 0 || ground >= pmove->numphysent ) + return NULL; // bad ground + + return PM_TraceSurface( &pmove->physents[ground], vstart, vend ); +} + +const char *PM_TraceTexturePmove( playermove_t *pmove, int ground, float *vstart, float *vend ) +{ + if( ground < 0 || ground >= pmove->numphysent ) + return NULL; // bad ground + + return PM_TraceTexture( &pmove->physents[ground], vstart, vend ); +} + +int PM_PointContentsPmove( playermove_t *pmove, const float *p, int *truecontents ) +{ + int cont, truecont; + + truecont = cont = PM_PointContents( pmove, p ); + if( truecontents ) *truecontents = truecont; + + if( cont <= CONTENTS_CURRENT_0 && cont >= CONTENTS_CURRENT_DOWN ) + cont = CONTENTS_WATER; + return cont; +} + +void PM_StuckTouch( playermove_t *pmove, int hitent, pmtrace_t *tr ) +{ + int i; + + for( i = 0; i < pmove->numtouch; i++ ) + { + if( pmove->touchindex[i].ent == hitent ) + return; + } + + if( pmove->numtouch >= MAX_PHYSENTS ) + return; + + VectorCopy( pmove->velocity, tr->deltavelocity ); + tr->ent = hitent; + + pmove->touchindex[pmove->numtouch++] = *tr; +} diff --git a/engine/server/sv_pmove.c b/engine/server/sv_pmove.c index 53337e80..02b85f0a 100644 --- a/engine/server/sv_pmove.c +++ b/engine/server/sv_pmove.c @@ -374,33 +374,12 @@ static int GAME_EXPORT pfnTestPlayerPosition( float *pos, pmtrace_t *ptrace ) static void GAME_EXPORT pfnStuckTouch( int hitent, pmtrace_t *tr ) { - int i; - - for( i = 0; i < svgame.pmove->numtouch; i++ ) - { - if( svgame.pmove->touchindex[i].ent == hitent ) - return; - } - - if( svgame.pmove->numtouch >= MAX_PHYSENTS ) - return; - - VectorCopy( svgame.pmove->velocity, tr->deltavelocity ); - tr->ent = hitent; - - svgame.pmove->touchindex[svgame.pmove->numtouch++] = *tr; + return PM_StuckTouch( svgame.pmove, hitent, tr ); } static int GAME_EXPORT pfnPointContents( float *p, int *truecontents ) { - int cont, truecont; - - truecont = cont = PM_PointContents( svgame.pmove, p ); - if( truecontents ) *truecontents = truecont; - - if( cont <= CONTENTS_CURRENT_0 && cont >= CONTENTS_CURRENT_DOWN ) - cont = CONTENTS_WATER; - return cont; + return PM_PointContentsPmove( svgame.pmove, p, truecontents ); } static int GAME_EXPORT pfnTruePointContents( float *p ) @@ -420,25 +399,7 @@ static pmtrace_t GAME_EXPORT pfnPlayerTrace( float *start, float *end, int trace static pmtrace_t *pfnTraceLine( float *start, float *end, int flags, int usehull, int ignore_pe ) { - static pmtrace_t tr; - int old_usehull; - - old_usehull = svgame.pmove->usehull; - svgame.pmove->usehull = usehull; - - switch( flags ) - { - case PM_TRACELINE_PHYSENTSONLY: - tr = PM_PlayerTraceExt( svgame.pmove, start, end, 0, svgame.pmove->numphysent, svgame.pmove->physents, ignore_pe, NULL ); - break; - case PM_TRACELINE_ANYVISIBLE: - tr = PM_PlayerTraceExt( svgame.pmove, start, end, 0, svgame.pmove->numvisent, svgame.pmove->visents, ignore_pe, NULL ); - break; - } - - svgame.pmove->usehull = old_usehull; - - return &tr; + return PM_TraceLine( svgame.pmove, start, end, flags, usehull, ignore_pe ); } static hull_t *pfnHullForBsp( physent_t *pe, float *offset ) @@ -448,61 +409,12 @@ static hull_t *pfnHullForBsp( physent_t *pe, float *offset ) static float GAME_EXPORT pfnTraceModel( physent_t *pe, float *start, float *end, trace_t *trace ) { - int old_usehull; - vec3_t start_l, end_l; - vec3_t offset, temp; - qboolean rotated; - matrix4x4 matrix; - hull_t *hull; - - PM_InitTrace( trace, end ); - - old_usehull = svgame.pmove->usehull; - svgame.pmove->usehull = 2; - - hull = PM_HullForBsp( pe, svgame.pmove, offset ); - - svgame.pmove->usehull = old_usehull; - - if( pe->solid == SOLID_BSP && !VectorIsNull( pe->angles )) - rotated = true; - else rotated = false; - - if( rotated ) - { - Matrix4x4_CreateFromEntity( matrix, pe->angles, offset, 1.0f ); - Matrix4x4_VectorITransform( matrix, start, start_l ); - Matrix4x4_VectorITransform( matrix, end, end_l ); - } - else - { - VectorSubtract( start, offset, start_l ); - VectorSubtract( end, offset, end_l ); - } - - PM_RecursiveHullCheck( hull, hull->firstclipnode, 0, 1, start_l, end_l, (pmtrace_t *)trace ); - trace->ent = NULL; - - if( rotated ) - { - VectorCopy( trace->plane.normal, temp ); - Matrix4x4_TransformPositivePlane( matrix, temp, trace->plane.dist, trace->plane.normal, &trace->plane.dist ); - } - - VectorLerp( start, trace->fraction, end, trace->endpos ); - - return trace->fraction; + return PM_TraceModel( svgame.pmove, pe, start, end, trace ); } static const char *pfnTraceTexture( int ground, float *vstart, float *vend ) { - physent_t *pe; - - if( ground < 0 || ground >= svgame.pmove->numphysent ) - return NULL; // bad ground - - pe = &svgame.pmove->physents[ground]; - return PM_TraceTexture( pe, vstart, vend ); + return PM_TraceTexturePmove( svgame.pmove, ground, vstart, vend ); } static void GAME_EXPORT pfnPlaySound( int channel, const char *sample, float volume, float attenuation, int fFlags, int pitch ) @@ -545,36 +457,12 @@ static int GAME_EXPORT pfnTestPlayerPositionEx( float *pos, pmtrace_t *ptrace, p static pmtrace_t *pfnTraceLineEx( float *start, float *end, int flags, int usehull, pfnIgnore pmFilter ) { - static pmtrace_t tr; - int old_usehull; - - old_usehull = svgame.pmove->usehull; - svgame.pmove->usehull = usehull; - - switch( flags ) - { - case PM_TRACELINE_PHYSENTSONLY: - tr = PM_PlayerTraceExt( svgame.pmove, start, end, 0, svgame.pmove->numphysent, svgame.pmove->physents, -1, pmFilter ); - break; - case PM_TRACELINE_ANYVISIBLE: - tr = PM_PlayerTraceExt( svgame.pmove, start, end, 0, svgame.pmove->numvisent, svgame.pmove->visents, -1, pmFilter ); - break; - } - - svgame.pmove->usehull = old_usehull; - - return &tr; + return PM_TraceLineEx( svgame.pmove, start, end, flags, usehull, pmFilter ); } static struct msurface_s *pfnTraceSurface( int ground, float *vstart, float *vend ) { - physent_t *pe; - - if( ground < 0 || ground >= svgame.pmove->numphysent ) - return NULL; // bad ground - - pe = &svgame.pmove->physents[ground]; - return PM_TraceSurface( pe, vstart, vend ); + return PM_TraceSurfacePmove( svgame.pmove, ground, vstart, vend ); } /* From e305b81df0b1fbcbf52801b09250af3f6dd05876 Mon Sep 17 00:00:00 2001 From: Alibek Omarov Date: Thu, 5 Jan 2023 23:59:31 +0300 Subject: [PATCH 339/490] engine: merge PM_TraceTexture into PM_TraceTexturePmove --- engine/client/cl_game.c | 2 +- engine/common/pm_local.h | 3 +-- engine/common/pm_surface.c | 18 ------------------ engine/common/pm_trace.c | 11 +++++++++-- engine/server/sv_pmove.c | 2 +- 5 files changed, 12 insertions(+), 24 deletions(-) diff --git a/engine/client/cl_game.c b/engine/client/cl_game.c index e58bf515..e3c437a7 100644 --- a/engine/client/cl_game.c +++ b/engine/client/cl_game.c @@ -2533,7 +2533,7 @@ CL_TraceTexture */ const char * GAME_EXPORT PM_CL_TraceTexture( int ground, float *vstart, float *vend ) { - return PM_TraceTexturePmove( clgame.pmove, ground, vstart, vend ); + return PM_TraceTexture( clgame.pmove, ground, vstart, vend ); } /* diff --git a/engine/common/pm_local.h b/engine/common/pm_local.h index 1c2b08cf..b283973b 100644 --- a/engine/common/pm_local.h +++ b/engine/common/pm_local.h @@ -42,7 +42,7 @@ float PM_TraceModel( playermove_t *pmove, physent_t *pe, float *start, float *en pmtrace_t *PM_TraceLine( playermove_t *pmove, float *start, float *end, int flags, int usehull, int ignore_pe ); pmtrace_t *PM_TraceLineEx( playermove_t *pmove, float *start, float *end, int flags, int usehull, pfnIgnore pmFilter ); struct msurface_s *PM_TraceSurfacePmove( playermove_t *pmove, int ground, float *vstart, float *vend ); -const char *PM_TraceTexturePmove( playermove_t *pmove, int ground, float *vstart, float *vend ); +const char *PM_TraceTexture( playermove_t *pmove, int ground, float *vstart, float *vend ); int PM_PointContentsPmove( playermove_t *pmove, const float *p, int *truecontents ); void PM_StuckTouch( playermove_t *pmove, int hitent, pmtrace_t *tr ); void PM_ConvertTrace( trace_t *out, pmtrace_t *in, edict_t *ent ); @@ -66,7 +66,6 @@ static inline void PM_InitPMTrace( pmtrace_t *trace, const vec3_t end ) // // pm_surface.c // -const char *PM_TraceTexture( physent_t *pe, vec3_t vstart, vec3_t vend ); msurface_t *PM_RecursiveSurfCheck( model_t *model, mnode_t *node, vec3_t p1, vec3_t p2 ); msurface_t *PM_TraceSurface( physent_t *pe, vec3_t start, vec3_t end ); int PM_TestLineExt( playermove_t *pmove, physent_t *ents, int numents, const vec3_t start, const vec3_t end, int flags ); diff --git a/engine/common/pm_surface.c b/engine/common/pm_surface.c index 6ba55a55..d1124a3d 100644 --- a/engine/common/pm_surface.c +++ b/engine/common/pm_surface.c @@ -214,24 +214,6 @@ msurface_t *PM_TraceSurface( physent_t *pe, vec3_t start, vec3_t end ) return PM_RecursiveSurfCheck( bmodel, &bmodel->nodes[hull->firstclipnode], start_l, end_l ); } -/* -================== -PM_TraceTexture - -find the face where the traceline hit -assume physentity is valid -================== -*/ -const char *PM_TraceTexture( physent_t *pe, vec3_t start, vec3_t end ) -{ - msurface_t *surf = PM_TraceSurface( pe, start, end ); - - if( !surf || !surf->texinfo || !surf->texinfo->texture ) - return NULL; - - return surf->texinfo->texture->name; -} - /* ================== PM_TestLine_r diff --git a/engine/common/pm_trace.c b/engine/common/pm_trace.c index 6b62a564..fb1e29bd 100644 --- a/engine/common/pm_trace.c +++ b/engine/common/pm_trace.c @@ -841,12 +841,19 @@ struct msurface_s *PM_TraceSurfacePmove( playermove_t *pmove, int ground, float return PM_TraceSurface( &pmove->physents[ground], vstart, vend ); } -const char *PM_TraceTexturePmove( playermove_t *pmove, int ground, float *vstart, float *vend ) +const char *PM_TraceTexture( playermove_t *pmove, int ground, float *vstart, float *vend ) { + msurface_t *surf; + if( ground < 0 || ground >= pmove->numphysent ) return NULL; // bad ground - return PM_TraceTexture( &pmove->physents[ground], vstart, vend ); + surf = PM_TraceSurface( &pmove->physents[ground], vstart, vend ); + + if( !surf || !surf->texinfo || !surf->texinfo->texture ) + return NULL; + + return surf->texinfo->texture->name; } int PM_PointContentsPmove( playermove_t *pmove, const float *p, int *truecontents ) diff --git a/engine/server/sv_pmove.c b/engine/server/sv_pmove.c index 02b85f0a..53cd010d 100644 --- a/engine/server/sv_pmove.c +++ b/engine/server/sv_pmove.c @@ -414,7 +414,7 @@ static float GAME_EXPORT pfnTraceModel( physent_t *pe, float *start, float *end, static const char *pfnTraceTexture( int ground, float *vstart, float *vend ) { - return PM_TraceTexturePmove( svgame.pmove, ground, vstart, vend ); + return PM_TraceTexture( svgame.pmove, ground, vstart, vend ); } static void GAME_EXPORT pfnPlaySound( int channel, const char *sample, float volume, float attenuation, int fFlags, int pitch ) From 0bec78a9588de25aa26c977ca2142e4a7d6242d6 Mon Sep 17 00:00:00 2001 From: Alibek Omarov Date: Fri, 6 Jan 2023 00:09:36 +0300 Subject: [PATCH 340/490] engine: client: make few function between pmove and client interface shared, remove unused CL_PointContents wrapper --- engine/client/cl_game.c | 50 ++++------------------------------------ engine/client/cl_pmove.c | 12 +--------- engine/client/cl_tent.c | 2 +- engine/client/client.h | 2 +- engine/common/common.h | 1 - 5 files changed, 8 insertions(+), 59 deletions(-) diff --git a/engine/client/cl_game.c b/engine/client/cl_game.c index e3c437a7..9769efba 100644 --- a/engine/client/cl_game.c +++ b/engine/client/cl_game.c @@ -234,22 +234,6 @@ void CL_InitCDAudio( const char *filename ) Mem_Free( afile ); } -/* -==================== -CL_PointContents - -Return contents for point -==================== -*/ -int CL_PointContents( const vec3_t p ) -{ - int cont = PM_PointContents( clgame.pmove, p ); - - if( cont <= CONTENTS_CURRENT_0 && cont >= CONTENTS_CURRENT_DOWN ) - cont = CONTENTS_WATER; - return cont; -} - /* ============= CL_AdjustXPos @@ -2220,38 +2204,14 @@ pfnPointContents ============= */ -static int GAME_EXPORT pfnPointContents( const float *p, int *truecontents ) +int GAME_EXPORT PM_CL_PointContents( const float *p, int *truecontents ) { return PM_PointContentsPmove( clgame.pmove, p, truecontents ); } -/* -============= -pfnTraceLine - -============= -*/ -static pmtrace_t *pfnTraceLine( float *start, float *end, int flags, int usehull, int ignore_pe ) +pmtrace_t *PM_CL_TraceLine( float *start, float *end, int flags, int usehull, int ignore_pe ) { - static pmtrace_t tr; - int old_usehull; - - old_usehull = clgame.pmove->usehull; - clgame.pmove->usehull = usehull; - - switch( flags ) - { - case PM_TRACELINE_PHYSENTSONLY: - tr = PM_PlayerTraceExt( clgame.pmove, start, end, 0, clgame.pmove->numphysent, clgame.pmove->physents, ignore_pe, NULL ); - break; - case PM_TRACELINE_ANYVISIBLE: - tr = PM_PlayerTraceExt( clgame.pmove, start, end, 0, clgame.pmove->numvisent, clgame.pmove->visents, ignore_pe, NULL ); - break; - } - - clgame.pmove->usehull = old_usehull; - - return &tr; + return PM_TraceLine( clgame.pmove, start, end, flags, usehull, ignore_pe ); } static void GAME_EXPORT pfnPlaySoundByNameAtLocation( char *szSound, float volume, float *origin ) @@ -3827,9 +3787,9 @@ static cl_enginefunc_t gEngfuncs = pfnGetClientTime, pfnCalcShake, pfnApplyShake, - pfnPointContents, + PM_CL_PointContents, CL_WaterEntity, - pfnTraceLine, + PM_CL_TraceLine, CL_LoadModel, CL_AddEntity, CL_GetSpritePointer, diff --git a/engine/client/cl_pmove.c b/engine/client/cl_pmove.c index ce9a2744..fdbdecbc 100644 --- a/engine/client/cl_pmove.c +++ b/engine/client/cl_pmove.c @@ -716,11 +716,6 @@ static void GAME_EXPORT pfnStuckTouch( int hitent, pmtrace_t *tr ) return PM_StuckTouch( clgame.pmove, hitent, tr ); } -static int GAME_EXPORT pfnPointContents( float *p, int *truecontents ) -{ - return PM_PointContentsPmove( clgame.pmove, p, truecontents ); -} - static int GAME_EXPORT pfnTruePointContents( float *p ) { return PM_TruePointContents( clgame.pmove, p ); @@ -736,11 +731,6 @@ static pmtrace_t GAME_EXPORT pfnPlayerTrace( float *start, float *end, int trace return PM_PlayerTraceExt( clgame.pmove, start, end, traceFlags, clgame.pmove->numphysent, clgame.pmove->physents, ignore_pe, NULL ); } -pmtrace_t *PM_CL_TraceLine( float *start, float *end, int flags, int usehull, int ignore_pe ) -{ - return PM_TraceLine( clgame.pmove, start, end, flags, usehull, ignore_pe ); -} - static void *pfnHullForBsp( physent_t *pe, float *offset ) { return PM_HullForBsp( pe, clgame.pmove, offset ); @@ -817,7 +807,7 @@ void CL_InitClientMove( void ) clgame.pmove->Con_Printf = Con_Printf; clgame.pmove->Sys_FloatTime = Sys_DoubleTime; clgame.pmove->PM_StuckTouch = pfnStuckTouch; - clgame.pmove->PM_PointContents = pfnPointContents; + clgame.pmove->PM_PointContents = PM_CL_PointContents; clgame.pmove->PM_TruePointContents = pfnTruePointContents; clgame.pmove->PM_HullPointContents = pfnHullPointContents; clgame.pmove->PM_PlayerTrace = pfnPlayerTrace; diff --git a/engine/client/cl_tent.c b/engine/client/cl_tent.c index e8bde1fc..e0e10c4c 100644 --- a/engine/client/cl_tent.c +++ b/engine/client/cl_tent.c @@ -1037,7 +1037,7 @@ void GAME_EXPORT R_BreakModel( const vec3_t pos, const vec3_t size, const vec3_t vecSpot[1] = pos[1] + COM_RandomFloat( -0.5f, 0.5f ) * size[1]; vecSpot[2] = pos[2] + COM_RandomFloat( -0.5f, 0.5f ) * size[2]; - if( CL_PointContents( vecSpot ) != CONTENTS_SOLID ) + if( PM_CL_PointContents( vecSpot, NULL ) != CONTENTS_SOLID ) break; // valid spot } diff --git a/engine/client/client.h b/engine/client/client.h index 957a8719..a1439cef 100644 --- a/engine/client/client.h +++ b/engine/client/client.h @@ -832,6 +832,7 @@ void CL_PlayerTrace( float *start, float *end, int traceFlags, int ignore_pe, pm void CL_PlayerTraceExt( float *start, float *end, int traceFlags, int (*pfnIgnore)( physent_t *pe ), pmtrace_t *tr ); pmtrace_t *PM_CL_TraceLine( float *start, float *end, int flags, int usehull, int ignore_pe ); const char *PM_CL_TraceTexture( int ground, float *vstart, float *vend ); +int PM_CL_PointContents( const float *p, int *truecontents ); void CL_SetTraceHull( int hull ); void CL_GetMousePosition( int *mx, int *my ); // TODO: move to input cl_entity_t* CL_GetViewModel( void ); @@ -914,7 +915,6 @@ void CL_PredictMovement( qboolean repredicting ); void CL_CheckPredictionError( void ); qboolean CL_IsPredicted( void ); int CL_TruePointContents( const vec3_t p ); -int CL_PointContents( const vec3_t p ); int CL_WaterEntity( const float *rgflPos ); cl_entity_t *CL_GetWaterEntity( const float *rgflPos ); void CL_SetupPMove( playermove_t *pmove, local_state_t *from, usercmd_t *ucmd, qboolean runfuncs, double time ); diff --git a/engine/common/common.h b/engine/common/common.h index 856e5a4a..b54b9b99 100644 --- a/engine/common/common.h +++ b/engine/common/common.h @@ -744,7 +744,6 @@ char *CL_Userinfo( void ); void CL_LegacyUpdateInfo( void ); void CL_CharEvent( int key ); qboolean CL_DisableVisibility( void ); -int CL_PointContents( const vec3_t point ); byte *COM_LoadFile( const char *filename, int usehunk, int *pLength ); int CL_GetDemoComment( const char *demoname, char *comment ); void COM_AddAppDirectoryToSearchPath( const char *pszBaseDir, const char *appName ); From 787d3bc5ddfb36a23392eec0afc230c03d7e978b Mon Sep 17 00:00:00 2001 From: Alibek Omarov Date: Fri, 6 Jan 2023 00:14:49 +0300 Subject: [PATCH 341/490] engine: share playermove ClearPhysEnts function --- engine/client/cl_game.c | 1 - engine/client/cl_main.c | 3 ++- engine/client/cl_pmove.c | 16 +--------------- engine/client/client.h | 1 - engine/common/pm_local.h | 1 + engine/common/pm_trace.c | 8 ++++++++ engine/server/server.h | 1 - engine/server/sv_init.c | 3 ++- engine/server/sv_pmove.c | 8 -------- 9 files changed, 14 insertions(+), 28 deletions(-) diff --git a/engine/client/cl_game.c b/engine/client/cl_game.c index 9769efba..0a85cf21 100644 --- a/engine/client/cl_game.c +++ b/engine/client/cl_game.c @@ -2458,7 +2458,6 @@ pfnSetTraceHull void GAME_EXPORT CL_SetTraceHull( int hull ) { clgame.pmove->usehull = bound( 0, hull, 3 ); - } /* diff --git a/engine/client/cl_main.c b/engine/client/cl_main.c index 353c1350..35530d73 100644 --- a/engine/client/cl_main.c +++ b/engine/client/cl_main.c @@ -22,6 +22,7 @@ GNU General Public License for more details. #include "vgui_draw.h" #include "library.h" #include "vid_common.h" +#include "pm_local.h" #define MAX_TOTAL_CMDS 32 #define MAX_CMD_BUFFER 8000 @@ -1380,7 +1381,7 @@ void CL_ClearState( void ) CL_ClearEffects (); CL_FreeEdicts (); - CL_ClearPhysEnts (); + PM_ClearPhysEnts( clgame.pmove ); NetAPI_CancelAllRequests(); // wipe the entire cl structure diff --git a/engine/client/cl_pmove.c b/engine/client/cl_pmove.c index fdbdecbc..1bca399d 100644 --- a/engine/client/cl_pmove.c +++ b/engine/client/cl_pmove.c @@ -26,20 +26,6 @@ GNU General Public License for more details. #define MIN_PREDICTION_EPSILON 0.5f // complain if error is > this and we have cl_showerror set #define MAX_PREDICTION_ERROR 64.0f // above this is assumed to be a teleport, don't smooth, etc. -/* -============= -CL_ClearPhysEnts - -============= -*/ -void CL_ClearPhysEnts( void ) -{ - clgame.pmove->numtouch = 0; - clgame.pmove->numvisent = 0; - clgame.pmove->nummoveent = 0; - clgame.pmove->numphysent = 0; -} - /* ============= CL_PushPMStates @@ -807,7 +793,7 @@ void CL_InitClientMove( void ) clgame.pmove->Con_Printf = Con_Printf; clgame.pmove->Sys_FloatTime = Sys_DoubleTime; clgame.pmove->PM_StuckTouch = pfnStuckTouch; - clgame.pmove->PM_PointContents = PM_CL_PointContents; + clgame.pmove->PM_PointContents = (void*)PM_CL_PointContents; clgame.pmove->PM_TruePointContents = pfnTruePointContents; clgame.pmove->PM_HullPointContents = pfnHullPointContents; clgame.pmove->PM_PlayerTrace = pfnPlayerTrace; diff --git a/engine/client/client.h b/engine/client/client.h index a1439cef..68ebd016 100644 --- a/engine/client/client.h +++ b/engine/client/client.h @@ -926,7 +926,6 @@ void CL_PopTraceBounds( void ); void CL_MoveSpectatorCamera( void ); void CL_SetLastUpdate( void ); void CL_RedoPrediction( void ); -void CL_ClearPhysEnts( void ); void CL_PushPMStates( void ); void CL_PopPMStates( void ); void CL_SetUpPlayerPrediction( int dopred, int bIncludeLocalClient ); diff --git a/engine/common/pm_local.h b/engine/common/pm_local.h index b283973b..e2583e20 100644 --- a/engine/common/pm_local.h +++ b/engine/common/pm_local.h @@ -30,6 +30,7 @@ void PM_DrawBBox( const vec3_t mins, const vec3_t maxs, const vec3_t origin, int // pm_trace.c // void Pmove_Init( void ); +void PM_ClearPhysEnts( playermove_t *pmove ); void PM_InitBoxHull( void ); hull_t *PM_HullForBsp( physent_t *pe, playermove_t *pmove, float *offset ); qboolean PM_RecursiveHullCheck( hull_t *hull, int num, float p1f, float p2f, vec3_t p1, vec3_t p2, pmtrace_t *trace ); diff --git a/engine/common/pm_trace.c b/engine/common/pm_trace.c index fb1e29bd..375fc9bc 100644 --- a/engine/common/pm_trace.c +++ b/engine/common/pm_trace.c @@ -55,6 +55,14 @@ void Pmove_Init( void ) memcpy( host.player_maxs, pm_hullmaxs, sizeof( pm_hullmaxs )); } +void PM_ClearPhysEnts( playermove_t *pmove ) +{ + pmove->nummoveent = 0; + pmove->numphysent = 0; + pmove->numvisent = 0; + pmove->numtouch = 0; +} + /* =================== PM_InitBoxHull diff --git a/engine/server/server.h b/engine/server/server.h index 9e90cc24..0ca8ee4e 100644 --- a/engine/server/server.h +++ b/engine/server/server.h @@ -697,6 +697,5 @@ void SV_RunLightStyles( void ); void SV_SetLightStyle( int style, const char* s, float f ); const char *SV_GetLightStyle( int style ); int SV_LightForEntity( edict_t *pEdict ); -void SV_ClearPhysEnts( void ); #endif//SERVER_H diff --git a/engine/server/sv_init.c b/engine/server/sv_init.c index f4ad6ba1..699b47b9 100644 --- a/engine/server/sv_init.c +++ b/engine/server/sv_init.c @@ -18,6 +18,7 @@ GNU General Public License for more details. #include "net_encode.h" #include "library.h" #include "voice.h" +#include "pm_local.h" #if XASH_LOW_MEMORY != 2 int SV_UPDATE_BACKUP = SINGLEPLAYER_BACKUP; @@ -651,7 +652,7 @@ void SV_DeactivateServer( void ) SV_FreeEdicts (); - SV_ClearPhysEnts (); + PM_ClearPhysEnts( svgame.pmove ); SV_EmptyStringPool(); diff --git a/engine/server/sv_pmove.c b/engine/server/sv_pmove.c index 53cd010d..356a4345 100644 --- a/engine/server/sv_pmove.c +++ b/engine/server/sv_pmove.c @@ -22,14 +22,6 @@ GNU General Public License for more details. static qboolean has_update = false; -void SV_ClearPhysEnts( void ) -{ - svgame.pmove->numtouch = 0; - svgame.pmove->numvisent = 0; - svgame.pmove->nummoveent = 0; - svgame.pmove->numphysent = 0; -} - qboolean SV_PlayerIsFrozen( edict_t *pClient ) { if( sv_background_freeze.value && sv.background ) From 2479d28cd5231f154644995b3b7a8770434cb566 Mon Sep 17 00:00:00 2001 From: Alibek Omarov Date: Fri, 6 Jan 2023 00:38:05 +0300 Subject: [PATCH 342/490] engine: remove useless pfnHullPointContents wrapper --- engine/client/cl_pmove.c | 7 +------ engine/common/pm_local.h | 2 +- engine/common/pm_trace.c | 2 +- engine/server/sv_pmove.c | 7 +------ 4 files changed, 4 insertions(+), 14 deletions(-) diff --git a/engine/client/cl_pmove.c b/engine/client/cl_pmove.c index 1bca399d..8fee4187 100644 --- a/engine/client/cl_pmove.c +++ b/engine/client/cl_pmove.c @@ -707,11 +707,6 @@ static int GAME_EXPORT pfnTruePointContents( float *p ) return PM_TruePointContents( clgame.pmove, p ); } -static int GAME_EXPORT pfnHullPointContents( struct hull_s *hull, int num, float *p ) -{ - return PM_HullPointContents( hull, num, p ); -} - static pmtrace_t GAME_EXPORT pfnPlayerTrace( float *start, float *end, int traceFlags, int ignore_pe ) { return PM_PlayerTraceExt( clgame.pmove, start, end, traceFlags, clgame.pmove->numphysent, clgame.pmove->physents, ignore_pe, NULL ); @@ -795,7 +790,7 @@ void CL_InitClientMove( void ) clgame.pmove->PM_StuckTouch = pfnStuckTouch; clgame.pmove->PM_PointContents = (void*)PM_CL_PointContents; clgame.pmove->PM_TruePointContents = pfnTruePointContents; - clgame.pmove->PM_HullPointContents = pfnHullPointContents; + clgame.pmove->PM_HullPointContents = PM_HullPointContents; clgame.pmove->PM_PlayerTrace = pfnPlayerTrace; clgame.pmove->PM_TraceLine = PM_CL_TraceLine; clgame.pmove->RandomLong = COM_RandomLong; diff --git a/engine/common/pm_local.h b/engine/common/pm_local.h index e2583e20..b2830741 100644 --- a/engine/common/pm_local.h +++ b/engine/common/pm_local.h @@ -36,7 +36,7 @@ hull_t *PM_HullForBsp( physent_t *pe, playermove_t *pmove, float *offset ); qboolean PM_RecursiveHullCheck( hull_t *hull, int num, float p1f, float p2f, vec3_t p1, vec3_t p2, pmtrace_t *trace ); pmtrace_t PM_PlayerTraceExt( playermove_t *pm, vec3_t p1, vec3_t p2, int flags, int numents, physent_t *ents, int ignore_pe, pfnIgnore pmFilter ); int PM_TestPlayerPosition( playermove_t *pmove, vec3_t pos, pmtrace_t *ptrace, pfnIgnore pmFilter ); -int PM_HullPointContents( hull_t *hull, int num, const vec3_t p ); +int PM_HullPointContents( hull_t *hull, int num, vec3_t p ); int PM_TruePointContents( playermove_t *pmove, const vec3_t p ); int PM_PointContents( playermove_t *pmove, const vec3_t p ); float PM_TraceModel( playermove_t *pmove, physent_t *pe, float *start, float *end, trace_t *trace ); diff --git a/engine/common/pm_trace.c b/engine/common/pm_trace.c index 375fc9bc..0519149b 100644 --- a/engine/common/pm_trace.c +++ b/engine/common/pm_trace.c @@ -138,7 +138,7 @@ PM_HullPointContents ================== */ -int PM_HullPointContents( hull_t *hull, int num, const vec3_t p ) +int PM_HullPointContents( hull_t *hull, int num, vec3_t p ) { mplane_t *plane; diff --git a/engine/server/sv_pmove.c b/engine/server/sv_pmove.c index 356a4345..c8b0be0a 100644 --- a/engine/server/sv_pmove.c +++ b/engine/server/sv_pmove.c @@ -379,11 +379,6 @@ static int GAME_EXPORT pfnTruePointContents( float *p ) return PM_TruePointContents( svgame.pmove, p ); } -static int GAME_EXPORT pfnHullPointContents( struct hull_s *hull, int num, float *p ) -{ - return PM_HullPointContents( hull, num, p ); -} - static pmtrace_t GAME_EXPORT pfnPlayerTrace( float *start, float *end, int traceFlags, int ignore_pe ) { return PM_PlayerTraceExt( svgame.pmove, start, end, traceFlags, svgame.pmove->numphysent, svgame.pmove->physents, ignore_pe, NULL ); @@ -496,7 +491,7 @@ void SV_InitClientMove( void ) svgame.pmove->PM_StuckTouch = pfnStuckTouch; svgame.pmove->PM_PointContents = pfnPointContents; svgame.pmove->PM_TruePointContents = pfnTruePointContents; - svgame.pmove->PM_HullPointContents = pfnHullPointContents; + svgame.pmove->PM_HullPointContents = PM_HullPointContents; svgame.pmove->PM_PlayerTrace = pfnPlayerTrace; svgame.pmove->PM_TraceLine = pfnTraceLine; svgame.pmove->RandomLong = COM_RandomLong; From 4a3efa511c8488d9a26478011d8764c2ed76310b Mon Sep 17 00:00:00 2001 From: Alibek Omarov Date: Sat, 7 Jan 2023 07:20:38 +0300 Subject: [PATCH 343/490] engine: client: correctly decompiled version of CL_AdjustClock (with removed useless float-to-int operation) --- engine/client/cl_main.c | 10 +++++----- engine/client/client.h | 3 +-- 2 files changed, 6 insertions(+), 7 deletions(-) diff --git a/engine/client/cl_main.c b/engine/client/cl_main.c index 35530d73..0ebe93c0 100644 --- a/engine/client/cl_main.c +++ b/engine/client/cl_main.c @@ -3030,12 +3030,12 @@ void CL_AdjustClock( void ) if( fabs( cl.timedelta ) >= 0.001f ) { double msec, adjust; - float sign; + double sign; - msec = ( cl.timedelta * 1000.0f ); - sign = ( msec < 0 ) ? 1.0f : -1.0f; - msec = fabs( msec ); - adjust = sign * ( cl_fixtimerate->value / 1000.0f ); + msec = ( cl.timedelta * 1000.0 ); + sign = ( msec < 0 ) ? 1.0 : -1.0; + msec = Q_min( cl_fixtimerate->value, fabs( msec )); + adjust = sign * ( msec / 1000.0 ); if( fabs( adjust ) < fabs( cl.timedelta )) { diff --git a/engine/client/client.h b/engine/client/client.h index 68ebd016..944f3cf3 100644 --- a/engine/client/client.h +++ b/engine/client/client.h @@ -211,7 +211,7 @@ typedef struct // a lerp point for other data double oldtime; // previous cl.time, time-oldtime is used // to decay light values and smooth step ups - float timedelta; // floating delta between two updates + double timedelta; // floating delta between two updates char serverinfo[MAX_SERVERINFO_STRING]; player_info_t players[MAX_CLIENTS]; // collected info about all other players include himself @@ -917,7 +917,6 @@ qboolean CL_IsPredicted( void ); int CL_TruePointContents( const vec3_t p ); int CL_WaterEntity( const float *rgflPos ); cl_entity_t *CL_GetWaterEntity( const float *rgflPos ); -void CL_SetupPMove( playermove_t *pmove, local_state_t *from, usercmd_t *ucmd, qboolean runfuncs, double time ); int CL_TestLine( const vec3_t start, const vec3_t end, int flags ); pmtrace_t *CL_VisTraceLine( vec3_t start, vec3_t end, int flags ); pmtrace_t CL_TraceLine( vec3_t start, vec3_t end, int flags ); From 40ba0238f8563c6d7f57eefacf64f65254d94b0e Mon Sep 17 00:00:00 2001 From: Alibek Omarov Date: Sat, 7 Jan 2023 11:07:32 +0300 Subject: [PATCH 344/490] engine: client: cosmetic changes in pmove code --- engine/client/cl_pmove.c | 51 ++++++++++++++++------------------------ engine/client/client.h | 2 +- 2 files changed, 21 insertions(+), 32 deletions(-) diff --git a/engine/client/cl_pmove.c b/engine/client/cl_pmove.c index 8fee4187..55a234a2 100644 --- a/engine/client/cl_pmove.c +++ b/engine/client/cl_pmove.c @@ -194,7 +194,7 @@ check for instant movement in case we don't want interpolate this ================== */ -qboolean CL_PlayerTeleported( local_state_t *from, local_state_t *to ) +static qboolean CL_PlayerTeleported( local_state_t *from, local_state_t *to ) { int len, maxlen; vec3_t delta; @@ -815,21 +815,10 @@ void CL_InitClientMove( void ) clgame.dllFuncs.pfnPlayerMoveInit( clgame.pmove ); } -static void PM_CheckMovingGround( clientdata_t *cd, entity_state_t *state, float frametime ) +static void CL_SetupPMove( playermove_t *pmove, const local_state_t *from, const usercmd_t *ucmd, qboolean runfuncs, double time ) { - if(!( cd->flags & FL_BASEVELOCITY )) - { - // apply momentum (add in half of the previous frame of velocity first) - VectorMA( cd->velocity, 1.0f + (frametime * 0.5f), state->basevelocity, cd->velocity ); - VectorClear( state->basevelocity ); - } - cd->flags &= ~FL_BASEVELOCITY; -} - -void CL_SetupPMove( playermove_t *pmove, local_state_t *from, usercmd_t *ucmd, qboolean runfuncs, double time ) -{ - entity_state_t *ps; - clientdata_t *cd; + const entity_state_t *ps; + const clientdata_t *cd; ps = &from->playerstate; cd = &from->client; @@ -846,13 +835,13 @@ void CL_SetupPMove( playermove_t *pmove, local_state_t *from, usercmd_t *ucmd, q VectorCopy( ps->basevelocity, pmove->basevelocity ); VectorCopy( cd->view_ofs, pmove->view_ofs ); VectorClear( pmove->movedir ); - pmove->flDuckTime = cd->flDuckTime; + pmove->flDuckTime = (float)cd->flDuckTime; pmove->bInDuck = cd->bInDuck; pmove->usehull = ps->usehull; pmove->flTimeStepSound = cd->flTimeStepSound; pmove->iStepLeft = ps->iStepLeft; pmove->flFallVelocity = ps->flFallVelocity; - pmove->flSwimTime = cd->flSwimTime; + pmove->flSwimTime = (float)cd->flSwimTime; VectorCopy( cd->punchangle, pmove->punchangle ); pmove->flNextPrimaryAttack = 0.0f; // not used by PM_ code pmove->effects = ps->effects; @@ -860,7 +849,7 @@ void CL_SetupPMove( playermove_t *pmove, local_state_t *from, usercmd_t *ucmd, q pmove->gravity = ps->gravity; pmove->friction = ps->friction; pmove->oldbuttons = ps->oldbuttons; - pmove->waterjumptime = cd->waterjumptime; + pmove->waterjumptime = (float)cd->waterjumptime; pmove->dead = (cl.local.health <= 0); pmove->deadflag = cd->deadflag; pmove->spectator = (cls.spectator != 0); @@ -887,7 +876,7 @@ void CL_SetupPMove( playermove_t *pmove, local_state_t *from, usercmd_t *ucmd, q Q_strncpy( pmove->physinfo, cls.physinfo, MAX_INFO_STRING ); } -void CL_FinishPMove( playermove_t *pmove, local_state_t *to ) +const void CL_FinishPMove( const playermove_t *pmove, local_state_t *to ) { entity_state_t *ps; clientdata_t *cd; @@ -898,7 +887,7 @@ void CL_FinishPMove( playermove_t *pmove, local_state_t *to ) cd->flags = pmove->flags; cd->bInDuck = pmove->bInDuck; cd->flTimeStepSound = pmove->flTimeStepSound; - cd->flDuckTime = pmove->flDuckTime; + cd->flDuckTime = (int)pmove->flDuckTime; cd->flSwimTime = (int)pmove->flSwimTime; cd->waterjumptime = (int)pmove->waterjumptime; cd->watertype = pmove->watertype; @@ -911,7 +900,7 @@ void CL_FinishPMove( playermove_t *pmove, local_state_t *to ) VectorCopy( pmove->angles, ps->angles ); VectorCopy( pmove->basevelocity, ps->basevelocity ); VectorCopy( pmove->punchangle, cd->punchangle ); - ps->oldbuttons = pmove->cmd.buttons; + ps->oldbuttons = (uint)pmove->cmd.buttons; ps->friction = pmove->friction; ps->movetype = pmove->movetype; ps->onground = pmove->onground; @@ -1019,13 +1008,9 @@ void CL_PredictMovement( qboolean repredicting ) { runcmd_t *to_cmd = NULL, *from_cmd; local_state_t *from = NULL, *to = NULL; - uint current_command; - uint current_command_mod; - frame_t *frame = NULL; + frame_t *frame = NULL; uint i, stoppoint; - qboolean runfuncs; double f = 1.0; - cl_entity_t *ent; double time; if( cls.state != ca_active || cls.spectator ) @@ -1036,7 +1021,7 @@ void CL_PredictMovement( qboolean repredicting ) CL_SetUpPlayerPrediction( false, false ); - if( cls.state != ca_active || !cl.validsequence ) + if( !cl.validsequence ) return; if(( cls.netchan.outgoing_sequence - cls.netchan.incoming_acknowledged ) >= CL_UPDATE_MASK ) @@ -1077,6 +1062,10 @@ void CL_PredictMovement( qboolean repredicting ) for( i = 1; i < CL_UPDATE_MASK && cls.netchan.incoming_acknowledged + i < cls.netchan.outgoing_sequence + stoppoint; i++ ) { + uint current_command; + uint current_command_mod; + qboolean runfuncs; + current_command = cls.netchan.incoming_acknowledged + i; current_command_mod = current_command & CL_UPDATE_MASK; @@ -1153,7 +1142,7 @@ void CL_PredictMovement( qboolean repredicting ) if( FBitSet( to->client.flags, FL_ONGROUND )) { - ent = CL_GetEntityByIndex( cl.local.lastground ); + cl_entity_t *ent = CL_GetEntityByIndex( cl.local.lastground ); cl.local.onground = cl.local.lastground; cl.local.moving = false; @@ -1175,13 +1164,13 @@ void CL_PredictMovement( qboolean repredicting ) else { cl.local.onground = -1; - cl.local.moving = 0; + cl.local.moving = false; } if( cls.correction_time > 0 && !cl_nosmooth->value && cl_smoothtime->value ) { - vec3_t delta; - float frac; + vec3_t delta; + float frac; // only decay timer once per frame if( !repredicting ) diff --git a/engine/client/client.h b/engine/client/client.h index 944f3cf3..49dce66d 100644 --- a/engine/client/client.h +++ b/engine/client/client.h @@ -139,7 +139,7 @@ typedef struct int light_level; int waterlevel; int usehull; - int moving; + qboolean moving; int pushmsec; int weapons; float maxspeed; From 9152bbf1066c52d80a7f635122bed801b76a28a7 Mon Sep 17 00:00:00 2001 From: Alibek Omarov Date: Sat, 7 Jan 2023 11:08:32 +0300 Subject: [PATCH 345/490] engine: client: more accurate decompilation of CL_LerpPoint and ComputeInterpolationAmount --- engine/client/cl_main.c | 66 ++++++++++------------------------------- engine/client/client.h | 1 - 2 files changed, 16 insertions(+), 51 deletions(-) diff --git a/engine/client/cl_main.c b/engine/client/cl_main.c index 0ebe93c0..710988cf 100644 --- a/engine/client/cl_main.c +++ b/engine/client/cl_main.c @@ -242,11 +242,6 @@ void CL_SignonReply( void ) } } -float CL_LerpInterval( void ) -{ - return Q_max( cl_interp->value, 1.f / cl_updaterate->value ); -} - /* =============== CL_LerpPoint @@ -257,55 +252,26 @@ should be put at. */ static float CL_LerpPoint( void ) { - float frac = 1.0f; - float server_frametime = cl_serverframetime(); + double f = cl_serverframetime(); + double frac; - if( server_frametime == 0.0f || cls.timedemo ) + if( f == 0.0 || cls.timedemo ) { + double fgap = cl_clientframetime(); cl.time = cl.mtime[0]; + + // maybe don't need for Xash demos + if( cls.demoplayback ) + cl.oldtime = cl.mtime[0] - fgap; + return 1.0f; } - if( server_frametime > 0.1f ) - { - // dropped packet, or start of demo - cl.mtime[1] = cl.mtime[0] - 0.1f; - server_frametime = 0.1f; - } -#if 0 - /* - g-cont: this code more suitable for singleplayer - NOTE in multiplayer causes significant framerate stutter/jitter and - occuring frames with zero time delta and even with negative time delta. - game becomes more twitchy and as if without interpolation. - */ - frac = (cl.time - cl.mtime[1]) / f; - if( frac < 0.0f ) - { - if( frac < -0.01f ) - cl.time = cl.mtime[1]; - frac = 0.0f; - } - else if( frac > 1.0f ) - { - if( frac > 1.01f ) - cl.time = cl.mtime[0]; - frac = 1.0f; - } -#else - // for multiplayer - if( cl_interp->value > 0.001f ) - { - // manual lerp value (goldsrc mode) - float td = Q_max( 0.f, cl.time - cl.mtime[0] ); - frac = td / CL_LerpInterval(); - } - else if( server_frametime > 0.001f ) - { - // automatic lerp (classic mode) - frac = ( cl.time - cl.mtime[1] ) / server_frametime; - } -#endif + if( cl_interp->value <= 0.001 ) + return 1.0f; + + frac = ( cl.time - cl.mtime[0] ) / cl_interp->value; + return frac; } @@ -347,7 +313,7 @@ Validate interpolation cvars, calc interpolation window void CL_ComputeClientInterpolationAmount( usercmd_t *cmd ) { const float epsilon = 0.001f; // to avoid float invalid comparision - float min_interp = MIN_EX_INTERP; + float min_interp; float max_interp = MAX_EX_INTERP; float interpolation_time; @@ -367,7 +333,7 @@ void CL_ComputeClientInterpolationAmount( usercmd_t *cmd ) max_interp = 0.2f; min_interp = 1.0f / cl_updaterate->value; - interpolation_time = CL_LerpInterval( ); + interpolation_time = cl_interp->value * 1000.0; if( (cl_interp->value + epsilon) < min_interp ) { diff --git a/engine/client/client.h b/engine/client/client.h index 49dce66d..f473d426 100644 --- a/engine/client/client.h +++ b/engine/client/client.h @@ -108,7 +108,6 @@ extern int CL_UPDATE_BACKUP; #define MIN_UPDATERATE 10.0f #define MAX_UPDATERATE 102.0f -#define MIN_EX_INTERP 0.005f #define MAX_EX_INTERP 0.1f #define CL_MIN_RESEND_TIME 1.5f // mininum time gap (in seconds) before a subsequent connection request is sent. From c28aeb23623d28fb02714be488712c78be45997e Mon Sep 17 00:00:00 2001 From: Alibek Omarov Date: Mon, 9 Jan 2023 07:55:54 +0300 Subject: [PATCH 346/490] engine: make crashhandler implementation choice private to crashhandler code --- common/backends.h | 6 ----- common/defaults.h | 15 ----------- engine/common/crashhandler.c | 51 +++++++++++++++--------------------- 3 files changed, 21 insertions(+), 51 deletions(-) diff --git a/common/backends.h b/common/backends.h index 84156534..be3cdaa6 100644 --- a/common/backends.h +++ b/common/backends.h @@ -29,12 +29,6 @@ GNU General Public License for more details. #define SOUND_OPENSLES 2 #define SOUND_ALSA 3 -// crash handler (XASH_CRASHHANDLER) -#define CRASHHANDLER_NULL 0 -#define CRASHHANDLER_UCONTEXT 1 -#define CRASHHANDLER_DBGHELP 2 -#define CRASHHANDLER_WIN32 3 - // input (XASH_INPUT) #define INPUT_NULL 0 #define INPUT_SDL 1 diff --git a/common/defaults.h b/common/defaults.h index a717f743..e992ef4d 100644 --- a/common/defaults.h +++ b/common/defaults.h @@ -110,17 +110,6 @@ SETUP BACKENDS DEFINITIONS #endif // !XASH_WIN32 #endif // XASH_MESSAGEBOX -// -// select crashhandler based on defines -// -#ifndef XASH_CRASHHANDLER - #if XASH_WIN32 && defined(DBGHELP) - #define XASH_CRASHHANDLER CRASHHANDLER_DBGHELP - #elif XASH_LINUX || XASH_BSD - #define XASH_CRASHHANDLER CRASHHANDLER_UCONTEXT - #endif // !(XASH_LINUX || XASH_BSD || XASH_WIN32) -#endif - // // no timer - no xash // @@ -157,10 +146,6 @@ SETUP BACKENDS DEFINITIONS #define XASH_INPUT INPUT_NULL #endif // XASH_INPUT -#ifndef XASH_CRASHHANDLER - #define XASH_CRASHHANDLER CRASHHANDLER_NULL -#endif // XASH_CRASHHANDLER - /* ========================================================================= diff --git a/engine/common/crashhandler.c b/engine/common/crashhandler.c index 64c537c1..88b1ad26 100644 --- a/engine/common/crashhandler.c +++ b/engine/common/crashhandler.c @@ -25,9 +25,9 @@ Sys_Crash Crash handler, called from system ================ */ -#if XASH_CRASHHANDLER == CRASHHANDLER_DBGHELP || XASH_CRASHHANDLER == CRASHHANDLER_WIN32 +#if XASH_WIN32 -#if XASH_CRASHHANDLER == CRASHHANDLER_DBGHELP +#if DBGHELP #pragma comment( lib, "dbghelp" ) @@ -189,7 +189,7 @@ static void Sys_StackTrace( PEXCEPTION_POINTERS pInfo ) SymCleanup( process ); } -#endif /* XASH_CRASHHANDLER == CRASHHANDLER_DBGHELP */ +#endif /* DBGHELP */ LPTOP_LEVEL_EXCEPTION_FILTER oldFilter; static long _stdcall Sys_Crash( PEXCEPTION_POINTERS pInfo ) @@ -200,7 +200,7 @@ static long _stdcall Sys_Crash( PEXCEPTION_POINTERS pInfo ) // check to avoid recursive call host.crashed = true; -#if XASH_CRASHHANDLER == CRASHHANDLER_DBGHELP +#if DBGHELP Sys_StackTrace( pInfo ); #else Sys_Warn( "Sys_Crash: call %p at address %p", pInfo->ExceptionRecord->ExceptionAddress, pInfo->ExceptionRecord->ExceptionCode ); @@ -239,21 +239,21 @@ void Sys_RestoreCrashHandler( void ) if( oldFilter ) SetUnhandledExceptionFilter( oldFilter ); } -#elif XASH_CRASHHANDLER == CRASHHANDLER_UCONTEXT +#elif XASH_FREEBSD || XASH_NETBSD || XASH_OPENBSD || XASH_ANDROID || XASH_LINUX // Posix signal handler - -#include "library.h" - -#if XASH_FREEBSD || XASH_NETBSD || XASH_OPENBSD || XASH_ANDROID || XASH_LINUX -#define HAVE_UCONTEXT_H 1 -#endif - -#ifdef HAVE_UCONTEXT_H #include -#endif - #include #include +#include "library.h" + +#define STACK_BACKTRACE_STR "Stack backtrace:\n" +#define STACK_DUMP_STR "Stack dump:\n" + +#define STACK_BACKTRACE_STR_LEN ( sizeof( STACK_BACKTRACE_STR ) - 1 ) +#define STACK_DUMP_STR_LEN ( sizeof( STACK_DUMP_STR ) - 1 ) +#define ALIGN( x, y ) (((uintptr_t) ( x ) + (( y ) - 1 )) & ~(( y ) - 1 )) + +static struct sigaction oldFilter; #ifdef XASH_DYNAMIC_DLADDR static int d_dladdr( void *sym, Dl_info *info ) @@ -284,28 +284,18 @@ static int Sys_PrintFrame( char *buf, int len, int i, void *addr ) if( dlinfo.dli_sname ) return Q_snprintf( buf, len, "%2d: %p <%s+%lu> (%s)\n", i, addr, dlinfo.dli_sname, (unsigned long)addr - (unsigned long)dlinfo.dli_saddr, dlinfo.dli_fname ); // print symbol, module and address - else - return Q_snprintf( buf, len, "%2d: %p (%s)\n", i, addr, dlinfo.dli_fname ); // print module and address + + return Q_snprintf( buf, len, "%2d: %p (%s)\n", i, addr, dlinfo.dli_fname ); // print module and address } - else - return Q_snprintf( buf, len, "%2d: %p\n", i, addr ); // print only address + + return Q_snprintf( buf, len, "%2d: %p\n", i, addr ); // print only address } -struct sigaction oldFilter; - -#define STACK_BACKTRACE_STR "Stack backtrace:\n" -#define STACK_DUMP_STR "Stack dump:\n" - -#define STACK_BACKTRACE_STR_LEN (sizeof( STACK_BACKTRACE_STR ) - 1) -#define STACK_DUMP_STR_LEN (sizeof( STACK_DUMP_STR ) - 1) -#define ALIGN( x, y ) (((uintptr_t) (x) + ((y)-1)) & ~((y)-1)) - static void Sys_Crash( int signal, siginfo_t *si, void *context) { void *pc = NULL, **bp = NULL, **sp = NULL; // this must be set for every OS! char message[8192]; int len, logfd, i = 0; - size_t pagesize; #if XASH_OPENBSD struct sigcontext *ucontext = (struct sigcontext*)context; @@ -382,6 +372,7 @@ static void Sys_Crash( int signal, siginfo_t *si, void *context) if( pc && bp && sp ) { size_t pagesize = sysconf( _SC_PAGESIZE ); + // try to print backtrace write( STDERR_FILENO, STACK_BACKTRACE_STR, STACK_BACKTRACE_STR_LEN ); write( logfd, STACK_BACKTRACE_STR, STACK_BACKTRACE_STR_LEN ); @@ -466,7 +457,7 @@ void Sys_RestoreCrashHandler( void ) sigaction( SIGILL, &oldFilter, NULL ); } -#elif XASH_CRASHHANDLER == CRASHHANDLER_NULL +#else void Sys_SetupCrashHandler( void ) { From 209a03a12a3db75a9a1ec3a539934d754c3e87ee Mon Sep 17 00:00:00 2001 From: Alibek Omarov Date: Mon, 9 Jan 2023 08:01:52 +0300 Subject: [PATCH 347/490] engine, public: prepare to removal of XASH_MSVC macro --- engine/common/lib_common.c | 12 +++++++++++- engine/common/system.c | 2 +- public/xash3d_mathlib.h | 4 ---- 3 files changed, 12 insertions(+), 6 deletions(-) diff --git a/engine/common/lib_common.c b/engine/common/lib_common.c index cdd3f8b6..7ead7a92 100644 --- a/engine/common/lib_common.c +++ b/engine/common/lib_common.c @@ -65,7 +65,7 @@ void *COM_FunctionFromName_SR( void *hInstance, const char *pName ) if( f ) return f; } -#elif XASH_MSVC +#elif _MSC_VER // TODO: COM_ConvertToLocalPlatform doesn't support MSVC yet // also custom loader strips always MSVC mangling, so Win32 // platforms already use platform-neutral names @@ -126,6 +126,16 @@ dll_user_t *FS_FindLibrary( const char *dllname, qboolean directpath ) ============================================================================= */ +enum +{ + +}; + +static void COM_GenerateCommonLibraryName( const char *name, const char *ext, int os, int cpu, char *out, size_t size ) +{ + +} + static void COM_GenerateCommonLibraryName( const char *name, const char *ext, char *out, size_t size ) { #if ( XASH_WIN32 || XASH_LINUX || XASH_APPLE ) && XASH_X86 diff --git a/engine/common/system.c b/engine/common/system.c index 7da49d88..6df8ffe6 100644 --- a/engine/common/system.c +++ b/engine/common/system.c @@ -62,7 +62,7 @@ Sys_DebugBreak void Sys_DebugBreak( void ) { #if XASH_LINUX || ( XASH_WIN32 && !XASH_64BIT ) -#if XASH_MSVC +#if _MSC_VER if( Sys_DebuggerPresent() ) _asm { int 3 } #elif XASH_X86 diff --git a/public/xash3d_mathlib.h b/public/xash3d_mathlib.h index 363090d2..4b6e9412 100644 --- a/public/xash3d_mathlib.h +++ b/public/xash3d_mathlib.h @@ -24,10 +24,6 @@ GNU General Public License for more details. #include "build.h" #include "com_model.h" -#ifdef XASH_MSVC -#pragma warning(disable : 4201) // nonstandard extension used -#endif - // euler angle order #define PITCH 0 #define YAW 1 From 65debeb7389d1eb72afe07a09f861d1939742b7a Mon Sep 17 00:00:00 2001 From: Alibek Omarov Date: Mon, 9 Jan 2023 08:04:58 +0300 Subject: [PATCH 348/490] public: add header buildenums.h declaring all platforms, architectures and ABIs as integer constants. --- public/buildenums.h | 151 ++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 151 insertions(+) create mode 100644 public/buildenums.h diff --git a/public/buildenums.h b/public/buildenums.h new file mode 100644 index 00000000..65601803 --- /dev/null +++ b/public/buildenums.h @@ -0,0 +1,151 @@ +/* +build.h - compile-time build information +Copyright (C) 2023 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. +*/ +#pragma once +#ifndef BUILDENUMS_H +#define BUILDENUMS_H + +#include "build.h" + +// This header defines the enumeration values that can be passed to Q_build* +// functions and get current value through XASH_PLATFORM, XASH_ARCHITECTURE and +// XASH_ARCHITECTURE_ABI defines + +//================================================================ +// +// OPERATING SYSTEM DEFINES +// +//================================================================ +#define PLATFORM_WIN32 1 +#define PLATFORM_ANDROID 2 +#define PLATFORM_LINUX 3 +#define PLATFORM_APPLE 4 +#define PLATFORM_FREEBSD 5 +#define PLATFORM_NETBSD 6 +#define PLATFORM_OPENBSD 7 +#define PLATFORM_EMSCRIPTEN 8 +#define PLATFORM_DOS4GW 9 +#define PLATFORM_HAIKU 10 +#define PLATFORM_SERENITY 11 + +#if XASH_WIN32 + #define XASH_PLATFORM PLATFORM_WIN32 +#elif XASH_ANDROID + #define XASH_PLATFORM PLATFORM_ANDROID +#elif XASH_LINUX + #define XASH_PLATFORM PLATFORM_LINUX +#elif XASH_APPLE + #define XASH_PLATFORM PLATFORM_APPLE +#elif XASH_FREEBSD + #define XASH_PLATFORM PLATFORM_FREEBSD +#elif XASH_NETBSD + #define XASH_PLATFORM PLATFORM_NETBSD +#elif XASH_OPENBSD + #define XASH_PLATFORM PLATFORM_OPENBSD +#elif XASH_EMSCRIPTEN + #define XASH_PLATFORM PLATFORM_EMSCRIPTEN +#elif XASH_DOS4GW + #define XASH_PLATFORM PLATFORM_DOS4GW +#elif XASH_HAIKU + #define XASH_PLATFORM PLATFORM_HAIKU +#elif XASH_SERENITY + #define XASH_PLATFORM PLATFORM_SERENITY +#else + #error +#endif + +//================================================================ +// +// CPU ARCHITECTURE DEFINES +// +//================================================================ +#define ARCHITECTURE_AMD64 1 +#define ARCHITECTURE_X86 2 +#define ARCHITECTURE_ARM 3 +#define ARCHITECTURE_MIPS 4 +#define ARCHITECTURE_JS 6 +#define ARCHITECTURE_E2K 7 +#define ARCHITECTURE_RISCV 8 + +#if XASH_AMD64 + #define XASH_ARCHITECTURE ARCHITECTURE_AMD64 +#elif XASH_X86 + #define XASH_ARCHITECTURE ARCHITECTURE_X86 +#elif XASH_ARM + #define XASH_ARCHITECTURE ARCHITECTURE_ARM +#elif XASH_MIPS + #define XASH_ARCHITECTURE ARCHITECTURE_MIPS +#elif XASH_JS + #define XASH_ARCHITECTURE ARCHITECTURE_JS +#elif XASH_E2K + #define XASH_ARCHITECTURE ARCHITECTURE_E2K +#elif XASH_RISCV + #define XASH_ARCHITECTURE ARCHITECTURE_RISCV +#else + #error +#endif + +//================================================================ +// +// ENDIANNESS DEFINES +// +//================================================================ +#define ENDIANNESS_LITTLE 1 +#define ENDIANNESS_BIG 2 + +#if XASH_LITTLE_ENDIAN + #define XASH_ENDIANNESS ENDIANNESS_LITTLE +#elif XASH_BIG_ENDIAN + #define XASH_ENDIANNESS ENDIANNESS_BIG +#else + #error +#endif + +//================================================================ +// +// APPLICATION BINARY INTERFACE +// +//================================================================ +#define BIT( n ) ( 1U << ( n )) + +#define ARCHITECTURE_ARM_VER_MASK ( BIT( 5 ) - 1 ) +#define ARCHITECTURE_ARM_VER_SHIFT 0 +#define ARCHITECTURE_ARM_HARDFP BIT( 5 ) + +#define ARCHITECTURE_RISCV_FP_SOFT 0 +#define ARCHITECTURE_RISCV_FP_SINGLE 1 +#define ARCHITECTURE_RISCV_FP_DOUBLE 2 +#define ARCHITECTURE_RISCV_FP_MASK ( BIT( 2 ) - 1 ) +#define ARCHITECTURE_RISCV_FP_SHIFT 0 + +#if XASH_ARCHITECTURE == ARCHITECTURE_ARM + #if XASH_ARM_HARDFP + #define XASH_ARCHITECTURE_ABI ( ARCHITECTURE_ARM_HARDFP | XASH_ARM ) + #else + #define XASH_ARCHITECTURE_ABI ( XASH_ARM ) + #endif +#elif XASH_ARCHITECTURE == ARCHITECTURE_RISCV + #if XASH_RISCV_SOFTFP + #define XASH_ARCHITECTURE_ABI ARCHITECTURE_RISCV_FP_SOFT + #elif XASH_RISCV_SINGLEFP + #define XASH_ARCHITECTURE_ABI ARCHITECTURE_RISCV_FP_SINGLE + #elif XASH_RISCV_DOUBLEFP + #define XASH_ARCHITECTURE_ABI ARCHITECTURE_RISCV_FP_DOUBLE + #else + #error + #endif +#endif + + +#endif // BUILDENUMS_H From 15a5975abf8bf30689439b0b05f306857cb01142 Mon Sep 17 00:00:00 2001 From: Alibek Omarov Date: Mon, 9 Jan 2023 08:05:59 +0300 Subject: [PATCH 349/490] public: build.h refactoring, removed XASH_MSVC, XASH_MINGW, as this header never intended to detect the compiler --- public/build.h | 156 ++++++++++++++++++++----------------------------- 1 file changed, 63 insertions(+), 93 deletions(-) diff --git a/public/build.h b/public/build.h index 57a7735f..86767cfd 100644 --- a/public/build.h +++ b/public/build.h @@ -30,15 +30,22 @@ For more information, please refer to #ifndef BUILD_H #define BUILD_H -// All XASH_* macros set by this header are guaranteed to have positive value otherwise not defined +/* +All XASH_* macros set by this header are guaranteed to have positive value +otherwise not defined. -// Any new define must be undefined at first -// You can generate #undef list below with this oneliner: -// $ cat build.h | sed 's/\t//g' | grep '^#define XASH' | awk '{ print $2 }' | sort | uniq | awk '{ print "#undef " $1 }' -// -// So in various buildscripts you can grep for ^#undef XASH and select only second word -// or in another oneliner: -// $ cat build.h | grep '^#undef XASH' | awk '{ print $2 }' +Every macro is intended to be the unified interface for buildsystems that lack +platform & CPU detection, and a neat quick way for checks in platform code +For Q_build* macros, refer to buildenums.h + +Any new define must be undefined at first +You can generate #undef list below with this oneliner: + $ sed 's/\t//g' build.h | grep '^#define XASH' | awk '{ print $2 }' | \ + sort | uniq | awk '{ print "#undef " $1 }' + +Then you can use another oneliner to query all variables: + $ grep '^#undef XASH' build.h | awk '{ print $2 }' +*/ #undef XASH_64BIT #undef XASH_AMD64 @@ -53,7 +60,6 @@ For more information, please refer to #undef XASH_ARMv7 #undef XASH_ARMv8 #undef XASH_BIG_ENDIAN -#undef XASH_BSD #undef XASH_DOS4GW #undef XASH_E2K #undef XASH_EMSCRIPTEN @@ -63,10 +69,8 @@ For more information, please refer to #undef XASH_JS #undef XASH_LINUX #undef XASH_LITTLE_ENDIAN -#undef XASH_MINGW #undef XASH_MIPS #undef XASH_MOBILE_PLATFORM -#undef XASH_MSVC #undef XASH_NETBSD #undef XASH_OPENBSD #undef XASH_POSIX @@ -76,64 +80,48 @@ For more information, please refer to #undef XASH_RISCV_SOFTFP #undef XASH_SERENITY #undef XASH_WIN32 -#undef XASH_WIN64 #undef XASH_X86 //================================================================ // -// OPERATING SYSTEM DEFINES +// PLATFORM DETECTION CODE // //================================================================ -#if defined(_WIN32) +#if defined _WIN32 #define XASH_WIN32 1 - #if defined(__MINGW32__) - #define XASH_MINGW 1 - #elif defined(_MSC_VER) - #define XASH_MSVC 1 - #endif - - #if defined(_WIN64) - #define XASH_WIN64 1 - #endif -#elif defined(__linux__) - #define XASH_LINUX 1 - #if defined(__ANDROID__) - #define XASH_ANDROID 1 - #endif // defined(__ANDROID__) - #define XASH_POSIX 1 -#elif defined(__APPLE__) - #include - #define XASH_APPLE 1 - #if TARGET_OS_IOS - #define XASH_IOS 1 - #endif // TARGET_OS_IOS - #define XASH_POSIX 1 -#elif defined(__FreeBSD__) || defined(__NetBSD__) || defined(__OpenBSD__) - #define XASH_BSD 1 - #if defined(__FreeBSD__) - #define XASH_FREEBSD 1 - #elif defined(__NetBSD__) - #define XASH_NETBSD 1 - #elif defined(__OpenBSD__) - #define XASH_OPENBSD 1 - #endif - #define XASH_POSIX 1 #elif defined __EMSCRIPTEN__ #define XASH_EMSCRIPTEN 1 #elif defined __WATCOMC__ && defined __DOS__ #define XASH_DOS4GW 1 - #define XASH_LITTLE_ENDIAN 1 -#elif defined __HAIKU__ - #define XASH_HAIKU 1 +#else // POSIX compatible #define XASH_POSIX 1 -#elif defined __serenity__ - #define XASH_SERENITY 1 - #define XASH_POSIX 1 -#else -#error "Place your operating system name here! If this is a mistake, try to fix conditions above and report a bug" + #if defined __linux__ + #define XASH_LINUX 1 + #if defined __ANDROID__ + #define XASH_ANDROID 1 + #endif + #elif defined __FreeBSD__ + #define XASH_FREEBSD 1 + #elif defined __NetBSD__ + #define XASH_NETBSD 1 + #elif defined __OpenBSD__ + #define XASH_OPENBSD 1 + #elif defined __HAIKU__ + #define XASH_HAIKU 1 + #elif defined __serenity__ + #define XASH_SERENITY 1 + #elif defined __APPLE__ + #include + #define XASH_APPLE 1 + #if TARGET_OS_IOS + #define XASH_IOS 1 + #endif // TARGET_OS_IOS + #else + #error + #endif #endif -#if defined XASH_ANDROID || defined XASH_IOS +#if XASH_ANDROID || defined XASH_IOS #define XASH_MOBILE_PLATFORM 1 #endif @@ -143,27 +131,17 @@ For more information, please refer to // //================================================================ -#if defined(XASH_FORCE_LITTLE_ENDIAN) && defined(XASH_FORCE_BIG_ENDIAN) - #error "Both XASH_FORCE_LITTLE_ENDIAN and XASH_FORCE_BIG_ENDIAN are defined" -#elif defined(XASH_FORCE_LITTLE_ENDIAN) - #define XASH_LITTLE_ENDIAN 1 -#elif defined(XASH_FORCE_BIG_ENDIAN) - #define XASH_BIG_ENDIAN 1 -#endif - -#if !defined(XASH_LITTLE_ENDIAN) && !defined(XASH_BIG_ENDIAN) - #if defined XASH_MSVC || __LITTLE_ENDIAN__ +#if !defined XASH_ENDIANNESS + #if defined XASH_WIN32 || __LITTLE_ENDIAN__ //!!! Probably all WinNT installations runs in little endian #define XASH_LITTLE_ENDIAN 1 #elif __BIG_ENDIAN__ #define XASH_BIG_ENDIAN 1 - #elif defined(__BYTE_ORDER__) && defined(__ORDER_BIG_ENDIAN__) && defined(__ORDER_LITTLE_ENDIAN__) // some compilers define this + #elif defined__BYTE_ORDER__) && defined__ORDER_BIG_ENDIAN__) && defined__ORDER_LITTLE_ENDIAN__) // some compilers define this #if __BYTE_ORDER__ == __ORDER_BIG_ENDIAN__ #define XASH_BIG_ENDIAN 1 #elif __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__ #define XASH_LITTLE_ENDIAN 1 - #else - #error "Unknown endianness!" #endif #else #include @@ -171,8 +149,6 @@ For more information, please refer to #define XASH_BIG_ENDIAN 1 #elif __BYTE_ORDER == __LITTLE_ENDIAN #define XASH_LITTLE_ENDIAN 1 - #else - #error "Unknown endianness!" #endif #endif // !XASH_WIN32 #endif @@ -182,18 +158,28 @@ For more information, please refer to // CPU ARCHITECTURE DEFINES // //================================================================ -#if defined(__x86_64__) || defined(_M_X64) +#if defined __x86_64__ || defined _M_X64 #define XASH_64BIT 1 #define XASH_AMD64 1 -#elif defined(__i386__) || defined(_X86_) || defined(_M_IX86) +#elif defined __i386__ || defined _X86_ || defined _M_IX86 #define XASH_X86 1 #elif defined __aarch64__ || defined _M_ARM64 #define XASH_64BIT 1 - #define XASH_ARM 8 -#elif defined __arm__ || defined _M_ARM + #define XASH_ARM 8 +#elif defined __mips__ + #define XASH_MIPS 1 +#elif defined __EMSCRIPTEN__ + #define XASH_JS 1 +#elif defined __e2k__ + #define XASH_64BIT 1 + #define XASH_E2K 1 +#elif defined _M_ARM // msvc + #define XASH_ARM 7 + #define XASH_ARM_HARDFP 1 +#elif defined __arm__ #if __ARM_ARCH == 8 || __ARM_ARCH_8__ #define XASH_ARM 8 - #elif __ARM_ARCH == 7 || __ARM_ARCH_7__ || defined _M_ARM // msvc can only armv7 in 32 bit + #elif __ARM_ARCH == 7 || __ARM_ARCH_7__ #define XASH_ARM 7 #elif __ARM_ARCH == 6 || __ARM_ARCH_6__ || __ARM_ARCH_6J__ #define XASH_ARM 6 @@ -205,29 +191,17 @@ For more information, please refer to #error "Unknown ARM" #endif - #if defined _M_ARM - #error "No WinMobile port yet! Need to determine which ARM float ABI msvc uses if applicable" - #endif - #if defined __SOFTFP__ || __ARM_PCS_VFP == 0 #define XASH_ARM_SOFTFP 1 #else // __SOFTFP__ #define XASH_ARM_HARDFP 1 #endif // __SOFTFP__ -#elif defined __mips__ - #define XASH_MIPS 1 -#elif defined __EMSCRIPTEN__ - #define XASH_JS 1 -#elif defined __e2k__ - #define XASH_64BIT 1 - #define XASH_E2K 1 #elif defined __riscv #define XASH_RISCV 1 + #if __riscv_xlen == 64 #define XASH_64BIT 1 - #elif __riscv_xlen == 32 - // ... - #else + #elif __riscv_xlen != 32 #error "Unknown RISC-V ABI" #endif @@ -244,10 +218,6 @@ For more information, please refer to #error "Place your architecture name here! If this is a mistake, try to fix conditions above and report a bug" #endif -#if defined(XASH_WAF_DETECTED_64BIT) && !defined(XASH_64BIT) - #define XASH_64BIT 1 -#endif - #if XASH_ARM == 8 #define XASH_ARMv8 1 #elif XASH_ARM == 7 From 1dc3cc2d57135baee95bc9afe0f5b4d80418426f Mon Sep 17 00:00:00 2001 From: Alibek Omarov Date: Mon, 9 Jan 2023 08:06:58 +0300 Subject: [PATCH 350/490] public: add Q_PlatformStringByID function that returns library naming compliant string by platform identifier from buildenums --- public/build.c | 91 ++++++++++++++++++++++++++++++------------------- public/crtlib.h | 1 + 2 files changed, 56 insertions(+), 36 deletions(-) diff --git a/public/build.c b/public/build.c index b9662c53..384a786e 100644 --- a/public/build.c +++ b/public/build.c @@ -14,18 +14,28 @@ GNU General Public License for more details. */ #include "crtlib.h" +#include "buildenums.h" static const char *date = __DATE__ ; static const char *mon[12] = { "Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec" }; static const char mond[12] = { 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 }; -// returns days since Apr 1 2015 +/* +=============== +Q_buildnum + +returns days since Apr 1 2015 +=============== +*/ int Q_buildnum( void ) { - int m = 0, d = 0, y = 0; static int b = 0; + int m = 0; + int d = 0; + int y = 0; - if( b != 0 ) return b; + if( b != 0 ) + return b; for( m = 0; m < 11; m++ ) { @@ -52,7 +62,7 @@ int Q_buildnum( void ) Q_buildnum_compat Returns a Xash3D build number. This is left for compability with original Xash3D. -IMPORTANT: this value must be changed ONLY after updating to newer Xash3D +IMPORTANT: this value must be changed ONLY after updating to newer Xash3D base IMPORTANT: this value must be acquired through "build" cvar. ============= */ @@ -62,46 +72,55 @@ int Q_buildnum_compat( void ) return 4529; } +/* +============ +Q_buildos_ + +Returns name of operating system by ID. Without any spaces. +============ +*/ +const char *Q_GetPlatformStringByID( const int platform ) +{ + switch( platform ) + { + case PLATFORM_WIN32: + return "win32"; + case PLATFORM_ANDROID: + return "android"; + case PLATFORM_LINUX: + return "linux"; + case PLATFORM_APPLE: + return "apple"; + case PLATFORM_FREEBSD: + return "freebsd"; + case PLATFORM_NETBSD: + return "netbsd"; + case PLATFORM_OPENBSD: + return "openbsd"; + case PLATFORM_EMSCRIPTEN: + return "emscripten"; + case PLATFORM_DOS4GW: + return "DOS4GW"; + case PLATFORM_HAIKU: + return "haiku"; + case PLATFORM_SERENITY: + return "serenity"; + } + + assert( 0 ); + return "unknown"; +} + /* ============ Q_buildos -Returns current name of operating system. Without any spaces. +Shortcut for Q_buildos_ ============ */ const char *Q_buildos( void ) { - const char *osname; - -#if XASH_MINGW - osname = "win32-mingw"; -#elif XASH_WIN32 - osname = "win32"; -#elif XASH_ANDROID - osname = "android"; -#elif XASH_LINUX - osname = "linux"; -#elif XASH_APPLE - osname = "apple"; -#elif XASH_FREEBSD - osname = "freebsd"; -#elif XASH_NETBSD - osname = "netbsd"; -#elif XASH_OPENBSD - osname = "openbsd"; -#elif XASH_EMSCRIPTEN - osname = "emscripten"; -#elif XASH_DOS4GW - osname = "DOS4GW"; -#elif XASH_HAIKU - osname = "haiku"; -#elif XASH_SERENITY - osname = "serenityos"; -#else -#error "Place your operating system name here! If this is a mistake, try to fix conditions above and report a bug" -#endif - - return osname; + return Q_PlatformStringByID( XASH_PLATFORM ); } /* diff --git a/public/crtlib.h b/public/crtlib.h index b3282d06..90ecaf1a 100644 --- a/public/crtlib.h +++ b/public/crtlib.h @@ -49,6 +49,7 @@ enum // int Q_buildnum( void ); int Q_buildnum_compat( void ); +const char *Q_PlatformStringByID( const int platform ); const char *Q_buildos( void ); const char *Q_buildarch( void ); const char *Q_buildcommit( void ); From 113904ea9142f18ea0786132b2841f69d1d58b5d Mon Sep 17 00:00:00 2001 From: Alibek Omarov Date: Mon, 9 Jan 2023 22:47:54 +0300 Subject: [PATCH 351/490] public: fix build --- engine/common/lib_common.c | 10 ---------- public/build.c | 6 ++++-- public/build.h | 2 +- public/buildenums.h | 4 ++-- 4 files changed, 7 insertions(+), 15 deletions(-) diff --git a/engine/common/lib_common.c b/engine/common/lib_common.c index 7ead7a92..f4d3e618 100644 --- a/engine/common/lib_common.c +++ b/engine/common/lib_common.c @@ -126,16 +126,6 @@ dll_user_t *FS_FindLibrary( const char *dllname, qboolean directpath ) ============================================================================= */ -enum -{ - -}; - -static void COM_GenerateCommonLibraryName( const char *name, const char *ext, int os, int cpu, char *out, size_t size ) -{ - -} - static void COM_GenerateCommonLibraryName( const char *name, const char *ext, char *out, size_t size ) { #if ( XASH_WIN32 || XASH_LINUX || XASH_APPLE ) && XASH_X86 diff --git a/public/build.c b/public/build.c index 384a786e..ef25da5f 100644 --- a/public/build.c +++ b/public/build.c @@ -74,12 +74,14 @@ int Q_buildnum_compat( void ) /* ============ -Q_buildos_ +Q_GetPlatformStringByID Returns name of operating system by ID. Without any spaces. + +TODO: add platform-dependent ABI variants, for example, different libc on Linux ============ */ -const char *Q_GetPlatformStringByID( const int platform ) +const char *Q_PlatformStringByID( const int platform ) { switch( platform ) { diff --git a/public/build.h b/public/build.h index 86767cfd..f05e1b59 100644 --- a/public/build.h +++ b/public/build.h @@ -137,7 +137,7 @@ Then you can use another oneliner to query all variables: #define XASH_LITTLE_ENDIAN 1 #elif __BIG_ENDIAN__ #define XASH_BIG_ENDIAN 1 - #elif defined__BYTE_ORDER__) && defined__ORDER_BIG_ENDIAN__) && defined__ORDER_LITTLE_ENDIAN__) // some compilers define this + #elif defined __BYTE_ORDER__ && defined __ORDER_BIG_ENDIAN__ && defined __ORDER_LITTLE_ENDIAN__ // some compilers define this #if __BYTE_ORDER__ == __ORDER_BIG_ENDIAN__ #define XASH_BIG_ENDIAN 1 #elif __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__ diff --git a/public/buildenums.h b/public/buildenums.h index 65601803..525f9fd1 100644 --- a/public/buildenums.h +++ b/public/buildenums.h @@ -126,8 +126,6 @@ GNU General Public License for more details. #define ARCHITECTURE_RISCV_FP_SOFT 0 #define ARCHITECTURE_RISCV_FP_SINGLE 1 #define ARCHITECTURE_RISCV_FP_DOUBLE 2 -#define ARCHITECTURE_RISCV_FP_MASK ( BIT( 2 ) - 1 ) -#define ARCHITECTURE_RISCV_FP_SHIFT 0 #if XASH_ARCHITECTURE == ARCHITECTURE_ARM #if XASH_ARM_HARDFP @@ -145,6 +143,8 @@ GNU General Public License for more details. #else #error #endif +#else + #define XASH_ARCHITECTURE_ABI 0 // unused #endif From 5d98e13fb8fd3f544b0c429fd4c19566b85a7dbc Mon Sep 17 00:00:00 2001 From: Alibek Omarov Date: Mon, 9 Jan 2023 22:53:05 +0300 Subject: [PATCH 352/490] public: add Q_ArcitectureStringByID function to get library naming compliant CPU and ABI string --- public/build.c | 136 ++++++++++++++++++++++++++++-------------------- public/crtlib.h | 1 + 2 files changed, 80 insertions(+), 57 deletions(-) diff --git a/public/build.c b/public/build.c index ef25da5f..e619dfb2 100644 --- a/public/build.c +++ b/public/build.c @@ -125,6 +125,77 @@ const char *Q_buildos( void ) return Q_PlatformStringByID( XASH_PLATFORM ); } + +/* +============ +Q_ArchitectureStringByID + +Returns name of the architecture by it's ID. Without any spaces. +============ +*/ +const char *Q_ArchitectureStringByID( const int arch, const uint abi, const int endianness, const qboolean is64 ) +{ + // I don't want to change this function prototype + // and don't want to use static buffer either + // so encode all possible variants... :) + switch( arch ) + { + case ARCHITECTURE_AMD64: + return "amd64"; + case ARCHITECTURE_X86: + return "i386"; + case ARCHITECTURE_E2K: + return "e2k"; + case ARCHITECTURE_JS: + return "javascript"; + case ARCHITECTURE_MIPS: + return endianness == ENDIANNESS_LITTLE ? + ( is64 ? "mips64el" : "mipsel" ): + ( is64 ? "mips64" : "mips" ); + case ARCHITECTURE_ARM: + // no support for big endian ARM here + if( endianness == ENDIANNESS_LITTLE ) + { + const int ver = ( abi >> ARCHITECTURE_ARM_VER_SHIFT ) & ARCHITECTURE_ARM_VER_MASK; + const qboolean hardfp = FBitSet( abi, ARCHITECTURE_ARM_HARDFP ); + + if( is64 ) + return "aarch64"; + + switch( ver ) + { + case 8: + return hardfp ? "armv8_32hf" : "armv8_32l"; + case 7: + return hardfp ? "armv7hf" : "armv7l"; + case 6: + return "armv6l"; + case 5: + return "armv5l"; + case 4: + return "armv4l"; + } + } + break; + case ARCHITECTURE_RISCV: + switch( abi ) + { + case ARCHITECTURE_RISCV_FP_SOFT: + return is64 ? "riscv64" : "riscv32"; + case ARCHITECTURE_RISCV_FP_SINGLE: + return is64 ? "riscv64f" : "riscv32f"; + case ARCHITECTURE_RISCV_FP_DOUBLE: + return is64 ? "riscv64d" : "riscv64f"; + } + break; + } + + assert( 0 ); + return is64 ? + ( endianness == ENDIANNESS_LITTLE ? "unknown64el" : "unknownel" ) : + ( endianness == ENDIANNESS_LITTLE ? "unknown64be" : "unknownbe" ); +} + /* ============ Q_buildarch @@ -134,65 +205,16 @@ Returns current name of the architecture. Without any spaces. */ const char *Q_buildarch( void ) { - const char *archname; - -#if XASH_AMD64 - archname = "amd64"; -#elif XASH_X86 - archname = "i386"; -#elif XASH_ARM && XASH_64BIT - archname = "arm64"; -#elif XASH_ARM - archname = "armv" - #if XASH_ARM == 8 - "8_32" // for those who (mis)using 32-bit OS on 64-bit CPU - #elif XASH_ARM == 7 - "7" - #elif XASH_ARM == 6 - "6" - #elif XASH_ARM == 5 - "5" - #elif XASH_ARM == 4 - "4" - #endif - - #if XASH_ARM_HARDFP - "hf" - #else - "l" - #endif - ; -#elif XASH_MIPS && XASH_BIG_ENDIAN - archname = "mips" - #if XASH_64BIT - "64" - #endif - #if XASH_LITTLE_ENDIAN - "el" - #endif - ; -#elif XASH_RISCV - archname = "riscv" - #if XASH_64BIT - "64" - #else - "32" - #endif - #if XASH_RISCV_SINGLEFP - "d" - #elif XASH_RISCV_DOUBLEFP - "f" - #endif - ; -#elif XASH_JS - archname = "javascript"; -#elif XASH_E2K - archname = "e2k"; + return Q_ArchitectureStringByID( + XASH_ARCHITECTURE, + XASH_ARCHITECTURE_ABI, + XASH_ENDIANNESS, +#if XASH_64BIT + true #else -#error "Place your architecture name here! If this is a mistake, try to fix conditions above and report a bug" + false #endif - - return archname; + ); } /* diff --git a/public/crtlib.h b/public/crtlib.h index 90ecaf1a..4f159584 100644 --- a/public/crtlib.h +++ b/public/crtlib.h @@ -51,6 +51,7 @@ int Q_buildnum( void ); int Q_buildnum_compat( void ); const char *Q_PlatformStringByID( const int platform ); 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 ); From eb0459a04534913596146bc0ea4dea122e834935 Mon Sep 17 00:00:00 2001 From: Alibek Omarov Date: Tue, 10 Jan 2023 04:51:04 +0300 Subject: [PATCH 353/490] engine: strip Intel suffixes from server library name, but only on special platforms Remove same code from filesystem, it's not what filesystem should do --- engine/common/lib_common.c | 19 ++++++++++++++++++- filesystem/filesystem.c | 18 ------------------ 2 files changed, 18 insertions(+), 19 deletions(-) diff --git a/engine/common/lib_common.c b/engine/common/lib_common.c index f4d3e618..c0ec5299 100644 --- a/engine/common/lib_common.c +++ b/engine/common/lib_common.c @@ -158,6 +158,22 @@ static void COM_GenerateClientLibraryPath( const char *name, char *out, size_t s #endif } +/* +============== +COM_StripIntelSuffix + +Some modders use _i?86 suffix in game library name +So strip it to follow library naming for non-Intel CPUs +============== +*/ +static void COM_StripIntelSuffix( char *out ) +{ + char *suffix = Q_strrchr( out, '_' ); + + if( suffix && Q_stricmpext( "_i?86", suffix )) + *suffix = 0; +} + /* ============== COM_GenerateServerLibraryPath @@ -193,6 +209,7 @@ static void COM_GenerateServerLibraryPath( char *out, size_t size ) ext = COM_FileExtension( dllpath ); COM_StripExtension( dllpath ); + COM_StripIntelSuffix( dllpath ); COM_GenerateCommonLibraryName( dllpath, ext, out, size ); #endif @@ -234,7 +251,7 @@ void COM_GetCommonLibraryPath( ECommonLibraryType eLibType, char *out, size_t si } break; default: - ASSERT( true ); + ASSERT( 0 ); out[0] = 0; break; } diff --git a/filesystem/filesystem.c b/filesystem/filesystem.c index 53cf9af1..014123a2 100644 --- a/filesystem/filesystem.c +++ b/filesystem/filesystem.c @@ -625,24 +625,6 @@ void FS_ParseGenericGameInfo( gameinfo_t *GameInfo, const char *buf, const qbool else if( !Q_stricmp( token, "gamedll_linux" )) { pfile = COM_ParseFile( pfile, GameInfo->game_dll_linux, sizeof( GameInfo->game_dll_linux )); - - // try to normalize filename only for liblist.gam - // from hl_i?86.so to hl.so - if( !isGameInfo ) - { - char *p; - COM_StripExtension( GameInfo->game_dll_linux ); - - p = Q_strrchr( GameInfo->game_dll_linux, '_' ); - - if( p && Q_stricmpext( "_i?86", p )) - { - *p = 0; - } - - COM_DefaultExtension( GameInfo->game_dll_linux, "."OS_LIB_EXT ); - } - found_linux = true; } // valid for both From ca3b0e6246d5cf95a25f006d88bc215fdfea6d7d Mon Sep 17 00:00:00 2001 From: Alibek Omarov Date: Tue, 10 Jan 2023 04:59:28 +0300 Subject: [PATCH 354/490] Documentation: document the _i?86 quirk in library naming scheme --- Documentation/extensions/library-naming.md | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/Documentation/extensions/library-naming.md b/Documentation/extensions/library-naming.md index 286ab461..cec63ab0 100644 --- a/Documentation/extensions/library-naming.md +++ b/Documentation/extensions/library-naming.md @@ -31,11 +31,14 @@ Issue #0. Inconsistency between ABI and Q_buildarch.\ Resolution: Change Q_buildarch return value to use Debian-styled architectures list: https://www.debian.org/ports/, which includes a special naming for big/little-endian and hard/soft-float ARM. Issue #1: Build-system integration.\ -Resolution: implemented as [LibraryNaming.cmake](https://github.com/FWGS/hlsdk-xash3d/blob/master/cmake/LibraryNaming.cmake) and [library_naming.py](https://github.com/FWGS/hlsdk-xash3d/blob/master/scripts/waifulib/library_naming.py) extensions, see +Resolution: implemented as [LibraryNaming.cmake](https://github.com/FWGS/hlsdk-portable/blob/master/cmake/LibraryNaming.cmake) and [library_naming.py](https://github.com/FWGS/hlsdk-portable/blob/master/scripts/waifulib/library_naming.py) extensions, see Issue #2(related to #0): Which ARM flavours we actually need to handle?\ Resolution: Little-endian only, as there is no known big-endian ARM platforms in the wild. Architecture is coded this way: * ```armvxy```, where `x` is ARM instruction set level and `y` is hard-float ABI presence: `hf` where hard float ABI used, otherwise `l`. +Issue #3: Some mods (like The Specialists, Tyrian, ...) already apply suffixes _i386, _i686 to the gamedll path:\ +Resolution: On x86 on **Win/Lin/Mac**, don't change anything. Otherwise, strip the _i?86 part and follow the usual scheme. + See discussion: https://github.com/FWGS/xash3d-fwgs/issues/39 From 2f5e3b0aea5dbb081342d7a9f7907cfc62ef61c0 Mon Sep 17 00:00:00 2001 From: Alibek Omarov Date: Wed, 11 Jan 2023 13:41:54 +0300 Subject: [PATCH 355/490] mainui: update --- 3rdparty/mainui | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/3rdparty/mainui b/3rdparty/mainui index 09669712..8ae451a2 160000 --- a/3rdparty/mainui +++ b/3rdparty/mainui @@ -1 +1 @@ -Subproject commit 096697124fcb7cc9720c348352b716232f80fa90 +Subproject commit 8ae451a2b5b93e488ce40b8f815eee4166880495 From 9b5e0fef01ed6f3ff2dd4d7853c342a2c5209762 Mon Sep 17 00:00:00 2001 From: Alibek Omarov Date: Thu, 12 Jan 2023 04:02:04 +0300 Subject: [PATCH 356/490] engine: common: zone: make Mem_Alloc return aligned addresses on ILP32, thanks Xav101 on Discord for heads up --- engine/common/zone.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/engine/common/zone.c b/engine/common/zone.c index c8a79397..be097963 100644 --- a/engine/common/zone.c +++ b/engine/common/zone.c @@ -35,6 +35,9 @@ typedef struct memheader_s 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 uint fileline; +#if !XASH_64BIT + uint pad0; // doesn't have value, only to make Mem_Alloc return aligned addresses on ILP32 +#endif uint sentinel1; // should always be MEMHEADER_SENTINEL1 // immediately followed by data, which is followed by a MEMHEADER_SENTINEL2 byte From 171c0c8d3b917e8d7f56f5feaeea5aa126908d84 Mon Sep 17 00:00:00 2001 From: Alibek Omarov Date: Thu, 12 Jan 2023 04:04:53 +0300 Subject: [PATCH 357/490] engine: common: zone: use stdint types --- engine/common/zone.c | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/engine/common/zone.c b/engine/common/zone.c index be097963..98af81eb 100644 --- a/engine/common/zone.c +++ b/engine/common/zone.c @@ -15,8 +15,8 @@ GNU General Public License for more details. #include "common.h" -#define MEMHEADER_SENTINEL1 0xDEADF00D -#define MEMHEADER_SENTINEL2 0xDF +#define MEMHEADER_SENTINEL1 0xDEADF00DU +#define MEMHEADER_SENTINEL2 0xDFU #ifdef XASH_CUSTOM_SWAP #include "platform/swap/swap.h" @@ -34,18 +34,18 @@ typedef struct memheader_s 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 - uint fileline; + int fileline; #if !XASH_64BIT - uint pad0; // doesn't have value, only to make Mem_Alloc return aligned addresses on ILP32 + uint32_t pad0; // doesn't have value, only to make Mem_Alloc return aligned addresses on ILP32 #endif - uint sentinel1; // should always be MEMHEADER_SENTINEL1 + uint32_t sentinel1; // should always be MEMHEADER_SENTINEL1 // immediately followed by data, which is followed by a MEMHEADER_SENTINEL2 byte } memheader_t; typedef struct mempool_s { - uint sentinel1; // should always be MEMHEADER_SENTINEL1 + 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) @@ -57,7 +57,7 @@ typedef struct mempool_s poolhandle_t idx; #endif char name[64]; // name of the pool - uint sentinel2; // should always be MEMHEADER_SENTINEL1 + uint32_t sentinel2; // should always be MEMHEADER_SENTINEL1 } mempool_t; static mempool_t *poolchain = NULL; // critical stuff @@ -67,7 +67,7 @@ static mempool_t *poolchain = NULL; // critical stuff // 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 uint lastidx = 0; +static poolhandle_t lastidx = 0; static mempool_t *Mem_FindPool( poolhandle_t poolptr ) { From 1119a9ac22b9e2ec1ec11e1bff7f52a554340225 Mon Sep 17 00:00:00 2001 From: Alibek Omarov Date: Fri, 13 Jan 2023 08:09:16 +0300 Subject: [PATCH 358/490] engine: network: reenable DNS resolving in separate thread for Windows --- engine/common/net_ws.c | 21 +++++++++++---------- 1 file changed, 11 insertions(+), 10 deletions(-) diff --git a/engine/common/net_ws.c b/engine/common/net_ws.c index 58acec85..df569b9b 100644 --- a/engine/common/net_ws.c +++ b/engine/common/net_ws.c @@ -356,9 +356,9 @@ qboolean NET_GetHostByName( const char *hostname, int family, struct sockaddr_st #endif } -#if !defined XASH_NO_ASYNC_NS_RESOLVE && ( XASH_WIN32 || !( XASH_EMSCRIPTEN || XASH_DOS4GW )) +#if !XASH_EMSCRIPTEN && !XASH_DOS4GW && !defined XASH_NO_ASYNC_NS_RESOLVE #define CAN_ASYNC_NS_RESOLVE -#endif +#endif // !XASH_EMSCRIPTEN && !XASH_DOS4GW && !defined XASH_NO_ASYNC_NS_RESOLVE #ifdef CAN_ASYNC_NS_RESOLVE static void NET_ResolveThread( void ); @@ -387,7 +387,7 @@ void *NET_ThreadStart( void *unused ) DWORD WINAPI NET_ThreadStart( LPVOID unused ) { NET_ResolveThread(); - ExitThread(0); + ExitThread( 0 ); return 0; } #endif // !_WIN32 @@ -414,14 +414,14 @@ static struct nsthread_s #endif ; -#if XASH_WIN32 static void NET_InitializeCriticalSections( void ) { net.threads_initialized = true; - pInitializeCriticalSection( &nsthread.mutexns ); - pInitializeCriticalSection( &nsthread.mutexres ); -} +#if XASH_WIN32 + InitializeCriticalSection( &nsthread.mutexns ); + InitializeCriticalSection( &nsthread.mutexres ); #endif +} void NET_ResolveThread( void ) { @@ -2176,9 +2176,10 @@ void NET_Init( void ) Con_DPrintf( S_ERROR "network initialization failed.\n" ); return; } -#else - // we have pthreads by default - net.threads_initialized = true; +#endif + +#ifdef CAN_ASYNC_NS_RESOLVE + NET_InitializeCriticalSections(); #endif net.allow_ip = !Sys_CheckParm( "-noip" ); From acd86ce490b7d920c5f8bbeb4358c4209842f9a3 Mon Sep 17 00:00:00 2001 From: Alibek Omarov Date: Fri, 13 Jan 2023 08:50:33 +0300 Subject: [PATCH 359/490] engine: sound: select which to buffer raw channels will be painted to, choose stream buffer for voice --- engine/client/s_mix.c | 51 +++++++++---------------------------------- 1 file changed, 10 insertions(+), 41 deletions(-) diff --git a/engine/client/s_mix.c b/engine/client/s_mix.c index 721f05fa..8d5e0ba7 100644 --- a/engine/client/s_mix.c +++ b/engine/client/s_mix.c @@ -897,43 +897,13 @@ void S_MixUpsample( int sampleCount, int filtertype ) ppaint->ifilter++; } -void MIX_MixStreamBuffer( int end ) -{ - portable_samplepair_t *pbuf; - rawchan_t *ch; - - pbuf = MIX_GetPFrontFromIPaint( ISTREAMBUFFER ); - ch = S_FindRawChannel( S_RAW_SOUND_BACKGROUNDTRACK, false ); - - // clear the paint buffer - if( s_listener.paused || !ch || ch->s_rawend < paintedtime ) - { - memset( pbuf, 0, (end - paintedtime) * sizeof( portable_samplepair_t )); - } - else - { - int i, stop; - - // copy from the streaming sound source - stop = (end < ch->s_rawend) ? end : ch->s_rawend; - - for( i = paintedtime; i < stop; i++ ) - { - pbuf[i-paintedtime].left = ( ch->rawsamples[i & ( ch->max_samples - 1 )].left * ch->leftvol ) >> 8; - pbuf[i-paintedtime].right = ( ch->rawsamples[i & ( ch->max_samples - 1 )].right * ch->rightvol ) >> 8; - } - - for( ; i < end; i++ ) - pbuf[i-paintedtime].left = pbuf[i-paintedtime].right = 0; - } -} - void MIX_MixRawSamplesBuffer( int end ) { - portable_samplepair_t *pbuf; + portable_samplepair_t *pbuf, *roombuf, *streambuf; uint i, j, stop; - pbuf = MIX_GetCurrentPaintbufferPtr()->pbuf; + roombuf = MIX_GetPFrontFromIPaint( IROOMBUFFER ); + streambuf = MIX_GetPFrontFromIPaint( ISTREAMBUFFER ); if( s_listener.paused ) return; @@ -941,16 +911,19 @@ void MIX_MixRawSamplesBuffer( int end ) for( i = 0; i < MAX_RAW_CHANNELS; i++ ) { // copy from the streaming sound source - rawchan_t *ch = raw_channels[i]; + rawchan_t *ch = raw_channels[i]; + qboolean stream; - // background track should be mixing into another buffer - if( !ch || ch->entnum == S_RAW_SOUND_BACKGROUNDTRACK ) + if( !ch ) continue; // not audible if( !ch->leftvol && !ch->rightvol ) continue; + stream = ch->entnum == S_RAW_SOUND_BACKGROUNDTRACK || CL_IsPlayerIndex( ch->entnum ); + pbuf = stream ? streambuf : roombuf; + stop = (end < ch->s_rawend) ? end : ch->s_rawend; for( j = paintedtime; j < stop; j++ ) @@ -959,7 +932,7 @@ void MIX_MixRawSamplesBuffer( int end ) pbuf[j-paintedtime].right += ( ch->rawsamples[j & ( ch->max_samples - 1 )].right * ch->rightvol ) >> 8; } - if ( ch->entnum > 0 ) + if( ch->entnum > 0 ) SND_MoveMouthRaw( ch, &ch->rawsamples[paintedtime & ( ch->max_samples - 1 )], stop - paintedtime ); } } @@ -970,9 +943,6 @@ void MIX_MixRawSamplesBuffer( int end ) // caller also remixes all into final IPAINTBUFFER output. void MIX_UpsampleAllPaintbuffers( int end, int count ) { - // process stream buffer - MIX_MixStreamBuffer( end ); - // 11khz sounds are mixed into 3 buffers based on distance from listener, and facing direction // These buffers are facing, facingaway, room // These 3 mixed buffers are then each upsampled to 22khz. @@ -1014,7 +984,6 @@ void MIX_UpsampleAllPaintbuffers( int end, int count ) #endif // mix raw samples from the video streams - MIX_SetCurrentPaintbuffer( IROOMBUFFER ); MIX_MixRawSamplesBuffer( end ); MIX_DeactivateAllPaintbuffers(); From 10481a4ecc751564446190dc1b17b24b4ce76285 Mon Sep 17 00:00:00 2001 From: Alibek Omarov Date: Fri, 13 Jan 2023 11:50:01 +0300 Subject: [PATCH 360/490] github: upgrade SDL2 --- .github/workflows/c-cpp.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/c-cpp.yml b/.github/workflows/c-cpp.yml index 37ff641f..88258202 100644 --- a/.github/workflows/c-cpp.yml +++ b/.github/workflows/c-cpp.yml @@ -41,7 +41,7 @@ jobs: targetos: win32 targetarch: i386 env: - SDL_VERSION: 2.0.14 + SDL_VERSION: 2.26.2 GH_CPU_ARCH: ${{ matrix.targetarch }} GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} ANDROID_SDK_TOOLS_VER: 4333796 From 74ce7e9b109620315df2b0bfb9bcffa4e2deb932 Mon Sep 17 00:00:00 2001 From: Alibek Omarov Date: Fri, 13 Jan 2023 15:25:58 +0300 Subject: [PATCH 361/490] ref: don't apply rendercolor to studio models where it's not needed --- ref/gl/gl_studio.c | 44 ++++++++------------------------------------ ref/soft/r_studio.c | 22 ++++------------------ 2 files changed, 12 insertions(+), 54 deletions(-) diff --git a/ref/gl/gl_studio.c b/ref/gl/gl_studio.c index 910dd07b..0f32b8d7 100644 --- a/ref/gl/gl_studio.c +++ b/ref/gl/gl_studio.c @@ -1744,48 +1744,20 @@ void R_LightLambert( vec4_t light[MAX_LOCALLIGHTS], vec3_t normal, vec3_t color, out[2] = finalLight[2] * 255; } -static void R_StudioSetColorBegin(short *ptricmds, vec3_t *pstudionorms ) -{ - float *lv = (float *)g_studio.lightvalues[ptricmds[1]]; - rgba_t color; - - if( g_studio.numlocallights ) - { - color[3] = tr.blend * 255; - R_LightLambert( g_studio.lightpos[ptricmds[0]], pstudionorms[ptricmds[1]], lv, color ); - pglColor4ubv( color ); - } - else - { - if( RI.currententity->curstate.rendermode == kRenderTransColor ) - { - color[3] = tr.blend * 255; - VectorCopy( (byte*)&RI.currententity->curstate.rendercolor, color ); - pglColor4ubv( color ); - } - else pglColor4f( lv[0], lv[1], lv[2], tr.blend ); - } -} - static void R_StudioSetColorArray(short *ptricmds, vec3_t *pstudionorms, byte *color ) { float *lv = (float *)g_studio.lightvalues[ptricmds[1]]; color[3] = tr.blend * 255; + R_LightLambert( g_studio.lightpos[ptricmds[0]], pstudionorms[ptricmds[1]], lv, color ); +} - if( g_studio.numlocallights ) - R_LightLambert( g_studio.lightpos[ptricmds[0]], pstudionorms[ptricmds[1]], lv, color ); - else - { - if( RI.currententity->curstate.rendermode == kRenderTransColor ) - VectorCopy( (byte*)&RI.currententity->curstate.rendercolor, color ); - else - { - color[0] = lv[0] * 255; - color[1] = lv[1] * 255; - color[2] = lv[2] * 255; - } - } +static void R_StudioSetColorBegin( short *ptricmds, vec3_t *pstudionorms ) +{ + rgba_t color; + + R_StudioSetColorArray( ptricmds, pstudionorms, color ); + pglColor4ubv( color ); } /* diff --git a/ref/soft/r_studio.c b/ref/soft/r_studio.c index aa413bae..3aed2ce6 100644 --- a/ref/soft/r_studio.c +++ b/ref/soft/r_studio.c @@ -1752,24 +1752,10 @@ static void R_StudioSetColorBegin(short *ptricmds, vec3_t *pstudionorms ) float *lv = (float *)g_studio.lightvalues[ptricmds[1]]; rgba_t color; - if( g_studio.numlocallights ) - { - color[3] = tr.blend * 255; - R_LightLambert( g_studio.lightpos[ptricmds[0]], pstudionorms[ptricmds[1]], lv, color ); - //pglColor4ubv( color ); - TriColor4ub(color[0], color[1], color[2], color[3]); - } - else - { - if( RI.currententity->curstate.rendermode == kRenderTransColor ) - { - color[3] = tr.blend * 255; - VectorCopy( (byte*)&RI.currententity->curstate.rendercolor, color ); - //pglColor4ubv( color ); - TriColor4ub(color[0], color[1], color[2], color[3]); - } - else _TriColor4f( lv[0], lv[1], lv[2], tr.blend ); - } + color[3] = tr.blend * 255; + + R_LightLambert( g_studio.lightpos[ptricmds[0]], pstudionorms[ptricmds[1]], lv, color ); + TriColor4ub( color[0], color[1], color[2], color[3] ); } From 9466461ce026b23aec7c97472a00e2d2d16a5095 Mon Sep 17 00:00:00 2001 From: Xav101 Date: Sat, 14 Jan 2023 00:35:30 -0600 Subject: [PATCH 362/490] engine: preliminary support for SGI IRIX (#1211) * Added definitions for IRIX * Patchset to get dedicated server to compile on IRIX. * Cleaned up debug statements in wscript * Potential bug in IRIX implementation of isnan? For now just use the portable macro. * Include the platform port files in the build * Temporary execution script for setting appropriate library search paths to the right locations in the build directory. This should probably get replaced with a more permanent script at some point which lives in the same directory as the normal xash binary, or be replaced by a solution that sets the rpath during config or modifies rpath during install. * Clean up formatting and remove unneeded debugging statements * Added GPL copyright notice and description * Moved to irix platform folder and edited script * Re-introduced _inline macro * Replace spaces with tabs Co-authored-by: Xav101 --- common/xash3d_types.h | 4 ++++ engine/common/sys_con.c | 3 +++ engine/common/whereami.c | 27 +++++++++++++++++++++ engine/platform/irix/dladdr.c | 39 +++++++++++++++++++++++++++++++ engine/platform/irix/dladdr.h | 35 +++++++++++++++++++++++++++ engine/platform/irix/xash-exec | 9 +++++++ engine/platform/posix/lib_posix.c | 3 +++ engine/platform/posix/net.h | 3 +++ engine/platform/posix/sys_posix.c | 8 ++++--- engine/wscript | 3 +++ public/build.c | 2 ++ public/build.h | 3 +++ public/buildenums.h | 3 +++ public/xash3d_mathlib.h | 3 +++ wscript | 8 +++++++ 15 files changed, 150 insertions(+), 3 deletions(-) create mode 100644 engine/platform/irix/dladdr.c create mode 100644 engine/platform/irix/dladdr.h create mode 100755 engine/platform/irix/xash-exec diff --git a/common/xash3d_types.h b/common/xash3d_types.h index 7c1f3a84..adca2246 100644 --- a/common/xash3d_types.h +++ b/common/xash3d_types.h @@ -4,6 +4,10 @@ #include "build.h" +#if XASH_IRIX +#include +#endif + #if XASH_WIN32 #include // off_t #endif // _WIN32 diff --git a/engine/common/sys_con.c b/engine/common/sys_con.c index 67c5d3bb..b0a241a6 100644 --- a/engine/common/sys_con.c +++ b/engine/common/sys_con.c @@ -22,6 +22,9 @@ GNU General Public License for more details. #endif #include #include +#if XASH_IRIX +#include +#endif // do not waste precious CPU cycles on mobiles or low memory devices #if !XASH_WIN32 && !XASH_MOBILE_PLATFORM && !XASH_LOW_MEMORY diff --git a/engine/common/whereami.c b/engine/common/whereami.c index 4d3401a1..e7958363 100644 --- a/engine/common/whereami.c +++ b/engine/common/whereami.c @@ -795,6 +795,33 @@ int WAI_PREFIX(getModulePath)(char* out, int capacity, int* dirname_length) return length; } +#elif defined(__sgi) + +/* + * These functions are stubbed for now to get the code compiling. + * In the future it may be possible to get these working in some way. + * Current ideas are checking the working directory for a binary with + * the same executed name and reading links, or worst case just searching + * through the entirety of the filesystem that's readable by the user. + * + * I'm not sure it's actually possible to find the absolute path via a + * direct method on IRIX. Its implementation of /proc is a fairly barebones + * SVR4 implementation. Other UNIXes (e.g. Solaris) have extensions to /proc + * that make finding the absolute path possible but these don't exist on IRIX. + */ + +WAI_FUNCSPEC +int WAI_PREFIX(getExecutablePath)(char* out, int capacity, int* dirname_length) +{ + return -1; +} + +WAI_FUNCSPEC +int WAI_PREFIX(getModulePath)(char* out, int capacity, int* dirname_length) +{ + return -1; +} + #else #error unsupported platform diff --git a/engine/platform/irix/dladdr.c b/engine/platform/irix/dladdr.c new file mode 100644 index 00000000..a7985dba --- /dev/null +++ b/engine/platform/irix/dladdr.c @@ -0,0 +1,39 @@ +/* +dladdr.c - dladdr implementation for SGI IRIX +Copyright (C) 2022 Xav101 +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. +*/ + +/* + * From SGI IRIX's 'man dladdr' + * + * does not contain a prototype for dladdr or definition of + * Dl_info. The #include in the SYNOPSIS line is traditional, + * but contains no dladdr prototype and no IRIX library contains an + * implementation. Write your own declaration based on the code below. + * + * The following code is dependent on internal interfaces that are not + * part of the IRIX compatibility guarantee; however, there is no future + * intention to change this interface, so on a practical level, the code + * below is safe to use on IRIX. + * + * + * + * The following code has been reproduced from the manpage. + */ + +#include "dladdr.h" + +int dladdr(void *address, Dl_info* dl) +{ + void *v; + v = _rld_new_interface(_RLD_DLADDR, address, dl); + return (int)v; +} diff --git a/engine/platform/irix/dladdr.h b/engine/platform/irix/dladdr.h new file mode 100644 index 00000000..f4a4162b --- /dev/null +++ b/engine/platform/irix/dladdr.h @@ -0,0 +1,35 @@ +/* +dladdr.h - dladdr prototypes for SGI IRIX +Copyright (C) 2022 Xav101 +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. +*/ + +/* See engine/platform/irix/dladdr.c for the requirement for this implementation */ + +#ifndef DLADDR_IRIX_H +#define DLADDR_IRIX_H + +#include +#ifndef _RLD_INTERFACE_DLFCN_H_DLADDR +#define _RLD_INTERFACE_DLFCN_H_DLADDR +typedef struct Dl_info { + const char * dli_fname; + void * dli_fbase; + const char * dli_saddr; + int dli_version; + int dli_reserved1; + long dli_reserved[4]; +} Dl_info; +#endif +#define _RLD_DLADDR 14 + +int dladdr(void *address, Dl_info* dl); + +#endif diff --git a/engine/platform/irix/xash-exec b/engine/platform/irix/xash-exec new file mode 100755 index 00000000..c5c3c8c1 --- /dev/null +++ b/engine/platform/irix/xash-exec @@ -0,0 +1,9 @@ +#!/usr/sgug/bin/bash + +# Build path +export LD_LIBRARYN32_PATH=$PWD/filesystem:$LD_LIBRARYN32_PATH + +# Install path +export LD_LIBRARYN32_PATH=$PWD:$LD_LIBRARYN32_PATH + +exec $PWD/build/engine/xash diff --git a/engine/platform/posix/lib_posix.c b/engine/platform/posix/lib_posix.c index abfd46ad..746b73e7 100644 --- a/engine/platform/posix/lib_posix.c +++ b/engine/platform/posix/lib_posix.c @@ -16,6 +16,9 @@ GNU General Public License for more details. #include "platform/platform.h" #if XASH_LIB == LIB_POSIX #include +#ifdef XASH_IRIX +#include "platform/irix/dladdr.h" +#endif #include "common.h" #include "library.h" #include "filesystem.h" diff --git a/engine/platform/posix/net.h b/engine/platform/posix/net.h index 03b57d27..91ba7562 100644 --- a/engine/platform/posix/net.h +++ b/engine/platform/posix/net.h @@ -24,6 +24,9 @@ GNU General Public License for more details. #include #include #include +#if XASH_IRIX +#include +#endif #define WSAGetLastError() errno #define WSAEINTR EINTR diff --git a/engine/platform/posix/sys_posix.c b/engine/platform/posix/sys_posix.c index 328d11db..c5371f98 100644 --- a/engine/platform/posix/sys_posix.c +++ b/engine/platform/posix/sys_posix.c @@ -158,9 +158,11 @@ void Platform_Shutdown( void ) {} double Platform_DoubleTime( void ) { struct timespec ts; - - clock_gettime(CLOCK_MONOTONIC, &ts); - +#if XASH_IRIX + clock_gettime( CLOCK_SGI_CYCLE, &ts ); +#else + clock_gettime( CLOCK_MONOTONIC, &ts ); +#endif return (double) ts.tv_sec + (double) ts.tv_nsec/1000000000.0; } diff --git a/engine/wscript b/engine/wscript index 0b7ef631..8195d772 100644 --- a/engine/wscript +++ b/engine/wscript @@ -132,6 +132,9 @@ def build(bld): if bld.env.DEST_OS == 'linux': source += bld.path.ant_glob(['platform/linux/*.c']) + if bld.env.DEST_OS == 'irix': + source += bld.path.ant_glob(['platform/irix/*.c']) + if bld.env.DEST_OS == 'dos': source += bld.path.ant_glob(['platform/dos/*.c']) source += bld.path.ant_glob(['platform/stub/s_stub.c']) diff --git a/public/build.c b/public/build.c index e619dfb2..758ef738 100644 --- a/public/build.c +++ b/public/build.c @@ -107,6 +107,8 @@ const char *Q_PlatformStringByID( const int platform ) return "haiku"; case PLATFORM_SERENITY: return "serenity"; + case PLATFORM_IRIX: + return "irix"; } assert( 0 ); diff --git a/public/build.h b/public/build.h index f05e1b59..b2ff2c2d 100644 --- a/public/build.h +++ b/public/build.h @@ -66,6 +66,7 @@ Then you can use another oneliner to query all variables: #undef XASH_FREEBSD #undef XASH_HAIKU #undef XASH_IOS +#undef XASH_IRIX #undef XASH_JS #undef XASH_LINUX #undef XASH_LITTLE_ENDIAN @@ -110,6 +111,8 @@ Then you can use another oneliner to query all variables: #define XASH_HAIKU 1 #elif defined __serenity__ #define XASH_SERENITY 1 + #elif defined __sgi + #define XASH_IRIX 1 #elif defined __APPLE__ #include #define XASH_APPLE 1 diff --git a/public/buildenums.h b/public/buildenums.h index 525f9fd1..872975ad 100644 --- a/public/buildenums.h +++ b/public/buildenums.h @@ -38,6 +38,7 @@ GNU General Public License for more details. #define PLATFORM_DOS4GW 9 #define PLATFORM_HAIKU 10 #define PLATFORM_SERENITY 11 +#define PLATFORM_IRIX 12 #if XASH_WIN32 #define XASH_PLATFORM PLATFORM_WIN32 @@ -61,6 +62,8 @@ GNU General Public License for more details. #define XASH_PLATFORM PLATFORM_HAIKU #elif XASH_SERENITY #define XASH_PLATFORM PLATFORM_SERENITY +#elif XASH_IRIX + #define XASH_PLATFORM PLATFORM_IRIX #else #error #endif diff --git a/public/xash3d_mathlib.h b/public/xash3d_mathlib.h index 4b6e9412..259c79fb 100644 --- a/public/xash3d_mathlib.h +++ b/public/xash3d_mathlib.h @@ -77,6 +77,9 @@ GNU General Public License for more details. #define Q_round( x, y ) (floor( x / y + 0.5f ) * y ) #define Q_rint(x) ((x) < 0.0f ? ((int)((x)-0.5f)) : ((int)((x)+0.5f))) +#ifdef XASH_IRIX +#undef isnan +#endif #ifdef isnan // check for C99 isnan #define IS_NAN isnan #else diff --git a/wscript b/wscript index bcd4011e..6442bc1b 100644 --- a/wscript +++ b/wscript @@ -244,6 +244,14 @@ def configure(conf): cxxflags += conf.filter_cxxflags(compiler_optional_flags, cflags) cflags += conf.filter_cflags(compiler_optional_flags + c_compiler_optional_flags, cflags) + # check if we need to use irix linkflags + if conf.env.DEST_OS == 'irix' and conf.env.COMPILER_CC == 'gcc': + linkflags.remove('-Wl,--no-undefined') + linkflags.append('-Wl,--unresolved-symbols=ignore-all') + # check if we're in a sgug environment + if 'sgug' in os.environ['LD_LIBRARYN32_PATH']: + linkflags.append('-lc') + conf.env.append_unique('CFLAGS', cflags) conf.env.append_unique('CXXFLAGS', cxxflags) conf.env.append_unique('LINKFLAGS', linkflags) From a09aa31b7a97afd01703e9c9026cdac06b37b975 Mon Sep 17 00:00:00 2001 From: Alibek Omarov Date: Sat, 14 Jan 2023 09:52:44 +0300 Subject: [PATCH 363/490] github: comment out CI builds for MAGX and Android --- .github/workflows/c-cpp.yml | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/.github/workflows/c-cpp.yml b/.github/workflows/c-cpp.yml index 88258202..31384ebd 100644 --- a/.github/workflows/c-cpp.yml +++ b/.github/workflows/c-cpp.yml @@ -23,16 +23,16 @@ jobs: # targetos: linux # targetarch: aarch64 - - os: ubuntu-18.04 - targetos: android - targetarch: 32 - - os: ubuntu-18.04 - targetos: android - targetarch: 64 +# - os: ubuntu-18.04 +# targetos: android +# targetarch: 32 +# - os: ubuntu-18.04 +# targetos: android +# targetarch: 64 - - os: ubuntu-18.04 - targetos: motomagx - targetarch: armv6 +# - os: ubuntu-18.04 +# targetos: motomagx +# targetarch: armv6 - os: windows-latest targetos: win32 From 2705e77a4e21a1c9b8a42153f79ef052b00ae737 Mon Sep 17 00:00:00 2001 From: Alibek Omarov Date: Sat, 14 Jan 2023 10:06:32 +0300 Subject: [PATCH 364/490] ci: don't spew config.log when it's not needed, less verbosity --- scripts/cirrus/build_freebsd.sh | 4 ++-- scripts/gha/build_linux.sh | 4 ++-- scripts/gha/build_win32.sh | 4 ++-- scripts/lib.sh | 7 ++++++- 4 files changed, 12 insertions(+), 7 deletions(-) diff --git a/scripts/cirrus/build_freebsd.sh b/scripts/cirrus/build_freebsd.sh index 97554dcf..02fc7b80 100755 --- a/scripts/cirrus/build_freebsd.sh +++ b/scripts/cirrus/build_freebsd.sh @@ -14,9 +14,9 @@ build_engine() cd "$CIRRUS_WORKING_DIR" || die if [ "$APP" = "xashds" ]; then - ./waf configure -T release -d --enable-fs-tests || die + ./waf configure -T release -d --enable-fs-tests || die_configure elif [ "$APP" = "xash3d-fwgs" ]; then - ./waf configure -T release --enable-stb --enable-utils --enable-gl4es --enable-gles1 --enable-gles2 --enable-fs-tests || die + ./waf configure -T release --enable-stb --enable-utils --enable-gl4es --enable-gles1 --enable-gles2 --enable-fs-tests || die_configure else die fi diff --git a/scripts/gha/build_linux.sh b/scripts/gha/build_linux.sh index c9ef119f..b2660b59 100755 --- a/scripts/gha/build_linux.sh +++ b/scripts/gha/build_linux.sh @@ -50,9 +50,9 @@ build_engine() fi if [ "$1" = "dedicated" ]; then - ./waf configure -T release -d $AMD64 --enable-fs-tests || die + ./waf configure -T release -d $AMD64 --enable-fs-tests || die_configure elif [ "$1" = "full" ]; then - ./waf configure --sdl2=SDL2_linux -T release --enable-stb $AMD64 --enable-utils --enable-fs-tests || die + ./waf configure --sdl2=SDL2_linux -T release --enable-stb $AMD64 --enable-utils --enable-fs-tests || die_confgure else die fi diff --git a/scripts/gha/build_win32.sh b/scripts/gha/build_win32.sh index 940a9064..dff2b45a 100755 --- a/scripts/gha/build_win32.sh +++ b/scripts/gha/build_win32.sh @@ -11,8 +11,8 @@ fi # NOTE: to build with other version use --msvc_version during configuration # NOTE: sometimes you may need to add WinSDK to %PATH% -./waf.bat configure -s "SDL2_VC" -T "release" --enable-utils --enable-fs-tests --prefix=`pwd` $AMD64 || die -./waf.bat build -v || die +./waf.bat configure -s "SDL2_VC" -T "release" --enable-utils --enable-fs-tests --prefix=`pwd` $AMD64 || die_configure +./waf.bat build || die ./waf.bat install || die if [ "$ARCH" = "i386" ]; then diff --git a/scripts/lib.sh b/scripts/lib.sh index d62cee6a..bc281338 100644 --- a/scripts/lib.sh +++ b/scripts/lib.sh @@ -1,9 +1,14 @@ die() { - cat build/config.log exit 1 } +die_configure() +{ + cat build/config.log + die +} + if [ -n "$TRAVIS_BUILD_DIR" ]; then BUILDDIR=$TRAVIS_BUILD_DIR elif [ -n "$GITHUB_WORKSPACE" ]; then From 6ac3156a825ae659c8b27ee91105100faf223d57 Mon Sep 17 00:00:00 2001 From: Alibek Omarov Date: Sat, 14 Jan 2023 10:37:40 +0300 Subject: [PATCH 365/490] engine: fix discarded const pointer qualifier in PM_HullPointContents --- engine/client/cl_pmove.c | 2 +- engine/common/pm_local.h | 2 +- engine/common/pm_trace.c | 2 +- engine/server/sv_pmove.c | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/engine/client/cl_pmove.c b/engine/client/cl_pmove.c index 55a234a2..3729eb47 100644 --- a/engine/client/cl_pmove.c +++ b/engine/client/cl_pmove.c @@ -790,7 +790,7 @@ void CL_InitClientMove( void ) clgame.pmove->PM_StuckTouch = pfnStuckTouch; clgame.pmove->PM_PointContents = (void*)PM_CL_PointContents; clgame.pmove->PM_TruePointContents = pfnTruePointContents; - clgame.pmove->PM_HullPointContents = PM_HullPointContents; + clgame.pmove->PM_HullPointContents = (void*)PM_HullPointContents; clgame.pmove->PM_PlayerTrace = pfnPlayerTrace; clgame.pmove->PM_TraceLine = PM_CL_TraceLine; clgame.pmove->RandomLong = COM_RandomLong; diff --git a/engine/common/pm_local.h b/engine/common/pm_local.h index b2830741..e2583e20 100644 --- a/engine/common/pm_local.h +++ b/engine/common/pm_local.h @@ -36,7 +36,7 @@ hull_t *PM_HullForBsp( physent_t *pe, playermove_t *pmove, float *offset ); qboolean PM_RecursiveHullCheck( hull_t *hull, int num, float p1f, float p2f, vec3_t p1, vec3_t p2, pmtrace_t *trace ); pmtrace_t PM_PlayerTraceExt( playermove_t *pm, vec3_t p1, vec3_t p2, int flags, int numents, physent_t *ents, int ignore_pe, pfnIgnore pmFilter ); int PM_TestPlayerPosition( playermove_t *pmove, vec3_t pos, pmtrace_t *ptrace, pfnIgnore pmFilter ); -int PM_HullPointContents( hull_t *hull, int num, vec3_t p ); +int PM_HullPointContents( hull_t *hull, int num, const vec3_t p ); int PM_TruePointContents( playermove_t *pmove, const vec3_t p ); int PM_PointContents( playermove_t *pmove, const vec3_t p ); float PM_TraceModel( playermove_t *pmove, physent_t *pe, float *start, float *end, trace_t *trace ); diff --git a/engine/common/pm_trace.c b/engine/common/pm_trace.c index 0519149b..375fc9bc 100644 --- a/engine/common/pm_trace.c +++ b/engine/common/pm_trace.c @@ -138,7 +138,7 @@ PM_HullPointContents ================== */ -int PM_HullPointContents( hull_t *hull, int num, vec3_t p ) +int PM_HullPointContents( hull_t *hull, int num, const vec3_t p ) { mplane_t *plane; diff --git a/engine/server/sv_pmove.c b/engine/server/sv_pmove.c index c8b0be0a..be0cbcb8 100644 --- a/engine/server/sv_pmove.c +++ b/engine/server/sv_pmove.c @@ -491,7 +491,7 @@ void SV_InitClientMove( void ) svgame.pmove->PM_StuckTouch = pfnStuckTouch; svgame.pmove->PM_PointContents = pfnPointContents; svgame.pmove->PM_TruePointContents = pfnTruePointContents; - svgame.pmove->PM_HullPointContents = PM_HullPointContents; + svgame.pmove->PM_HullPointContents = (void*)PM_HullPointContents; svgame.pmove->PM_PlayerTrace = pfnPlayerTrace; svgame.pmove->PM_TraceLine = pfnTraceLine; svgame.pmove->RandomLong = COM_RandomLong; From a5d5c1f60bddfa5e53651d465023264d2b91f940 Mon Sep 17 00:00:00 2001 From: Alibek Omarov Date: Sat, 14 Jan 2023 10:40:03 +0300 Subject: [PATCH 366/490] wscript: cast-align is less effective than ubsan --- wscript | 1 - 1 file changed, 1 deletion(-) diff --git a/wscript b/wscript index 6442bc1b..f8a09cb1 100644 --- a/wscript +++ b/wscript @@ -197,7 +197,6 @@ def configure(conf): '-Werror=duplicated-cond', '-Werror=bool-compare', '-Werror=bool-operation', - '-Wcast-align', '-Werror=cast-align=strict', # =strict is for GCC >=8 '-Werror=packed', '-Werror=packed-not-aligned', From de84df99f3371043d8b82aa9cb06221a8cc829da Mon Sep 17 00:00:00 2001 From: Alibek Omarov Date: Sat, 14 Jan 2023 10:40:42 +0300 Subject: [PATCH 367/490] engine: fix functions returning void returning void value --- engine/client/cl_pmove.c | 2 +- engine/client/in_joy.c | 2 +- engine/server/sv_pmove.c | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/engine/client/cl_pmove.c b/engine/client/cl_pmove.c index 3729eb47..2256b9a1 100644 --- a/engine/client/cl_pmove.c +++ b/engine/client/cl_pmove.c @@ -699,7 +699,7 @@ static int GAME_EXPORT pfnTestPlayerPosition( float *pos, pmtrace_t *ptrace ) static void GAME_EXPORT pfnStuckTouch( int hitent, pmtrace_t *tr ) { - return PM_StuckTouch( clgame.pmove, hitent, tr ); + PM_StuckTouch( clgame.pmove, hitent, tr ); } static int GAME_EXPORT pfnTruePointContents( float *p ) diff --git a/engine/client/in_joy.c b/engine/client/in_joy.c index 1ee9443d..8c407fd7 100644 --- a/engine/client/in_joy.c +++ b/engine/client/in_joy.c @@ -244,7 +244,7 @@ void Joy_AxisMotionEvent( byte axis, short value ) return; } - return Joy_KnownAxisMotionEvent( joyaxesmap[axis], value ); + Joy_KnownAxisMotionEvent( joyaxesmap[axis], value ); } void Joy_KnownAxisMotionEvent( engineAxis_t engineAxis, short value ) diff --git a/engine/server/sv_pmove.c b/engine/server/sv_pmove.c index be0cbcb8..fed1cd8e 100644 --- a/engine/server/sv_pmove.c +++ b/engine/server/sv_pmove.c @@ -366,7 +366,7 @@ static int GAME_EXPORT pfnTestPlayerPosition( float *pos, pmtrace_t *ptrace ) static void GAME_EXPORT pfnStuckTouch( int hitent, pmtrace_t *tr ) { - return PM_StuckTouch( svgame.pmove, hitent, tr ); + PM_StuckTouch( svgame.pmove, hitent, tr ); } static int GAME_EXPORT pfnPointContents( float *p, int *truecontents ) From a6475f530baac26456f5be7143661586d521b04a Mon Sep 17 00:00:00 2001 From: Alibek Omarov Date: Sat, 14 Jan 2023 10:41:44 +0300 Subject: [PATCH 368/490] engine: get rid of MSG_BigShort, use htons instead, since network headers are always included --- engine/client/cl_game.c | 2 +- engine/client/cl_main.c | 8 ++++---- engine/common/net_buffer.c | 5 ----- engine/common/net_buffer.h | 1 - 4 files changed, 5 insertions(+), 11 deletions(-) diff --git a/engine/client/cl_game.c b/engine/client/cl_game.c index 0a85cf21..18080e72 100644 --- a/engine/client/cl_game.c +++ b/engine/client/cl_game.c @@ -3377,7 +3377,7 @@ void GAME_EXPORT NetAPI_SendRequest( int context, int request, int flags, double // make sure that port is specified if( !nr->resp.remote_address.port ) - nr->resp.remote_address.port = MSG_BigShort( PORT_MASTER ); + nr->resp.remote_address.port = htons( PORT_MASTER ); // grab the list from the master server Q_strcpy( &fullquery[22], GI->gamefolder ); diff --git a/engine/client/cl_main.c b/engine/client/cl_main.c index 710988cf..1ffce5ef 100644 --- a/engine/client/cl_main.c +++ b/engine/client/cl_main.c @@ -1006,7 +1006,7 @@ void CL_SendConnectPacket( void ) return; } - if( adr.port == 0 ) adr.port = MSG_BigShort( PORT_SERVER ); + if( adr.port == 0 ) adr.port = htons( PORT_SERVER ); qport = Cvar_VariableString( "net_qport" ); key = ID_GetMD5(); @@ -1134,7 +1134,7 @@ void CL_CheckForResend( void ) return; } - if( adr.port == 0 ) adr.port = MSG_BigShort( PORT_SERVER ); + if( adr.port == 0 ) adr.port = htons( PORT_SERVER ); if( cls.connect_retry == CL_TEST_RETRIES_NORESPONCE ) { @@ -1321,7 +1321,7 @@ void CL_Rcon_f( void ) } NET_StringToAdr( rcon_address->string, &to ); - if( to.port == 0 ) to.port = MSG_BigShort( PORT_SERVER ); + if( to.port == 0 ) to.port = htons( PORT_SERVER ); } NET_SendPacket( NS_CLIENT, Q_strlen( message ) + 1, message, to ); @@ -1563,7 +1563,7 @@ void CL_LocalServers_f( void ) // send a broadcast packet adr.type = NA_BROADCAST; - adr.port = MSG_BigShort( PORT_SERVER ); + adr.port = htons( PORT_SERVER ); Netchan_OutOfBandPrint( NS_CLIENT, adr, "info %i", PROTOCOL_VERSION ); adr.type = NA_MULTICAST_IP6; diff --git a/engine/common/net_buffer.c b/engine/common/net_buffer.c index 34483ab9..567047f2 100644 --- a/engine/common/net_buffer.c +++ b/engine/common/net_buffer.c @@ -89,11 +89,6 @@ const char *svc_strings[svc_lastmsg+1] = "svc_exec", }; -unsigned short MSG_BigShort( unsigned short swap ) -{ - return (swap >> 8)|(swap << 8); -} - void MSG_InitMasks( void ) { uint startbit, endbit; diff --git a/engine/common/net_buffer.h b/engine/common/net_buffer.h index 50cb7361..b76c3011 100644 --- a/engine/common/net_buffer.h +++ b/engine/common/net_buffer.h @@ -63,7 +63,6 @@ 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 ); -unsigned short MSG_BigShort( unsigned short swap ); // init writing void MSG_StartWriting( sizebuf_t *sb, void *pData, int nBytes, int iStartBit, int nBits ); From 6232e288e12831ef39e5d704181873a26ab5731d Mon Sep 17 00:00:00 2001 From: Alibek Omarov Date: Sat, 14 Jan 2023 11:43:46 +0300 Subject: [PATCH 369/490] engine: client: fool proof R_SaveVideoMode --- engine/client/vid_common.c | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/engine/client/vid_common.c b/engine/client/vid_common.c index ac80c22f..0390c3bd 100644 --- a/engine/client/vid_common.c +++ b/engine/client/vid_common.c @@ -66,19 +66,25 @@ void VID_InitDefaultResolution( void ) R_SaveVideoMode ================= */ -void R_SaveVideoMode( int w, int h , int render_w, int render_h ) +void R_SaveVideoMode( int w, int h, int render_w, int render_h ) { + host.renderinfo_changed = false; + + if( !w || !h || !render_w || !render_h ) + return; + host.window_center_x = w / 2; host.window_center_y = h / 2; Cvar_SetValue( "width", w ); Cvar_SetValue( "height", h ); + if( refState.width == render_w && refState.height == render_h ) + return; + refState.width = render_w; refState.height = render_h; - host.renderinfo_changed = false; - // check for 4:3 or 5:4 if( render_w * 3 != render_h * 4 && render_w * 4 != render_h * 5 ) refState.wideScreen = true; From fbedbdca7d693657d3732f715937f0acdb5d7a0a Mon Sep 17 00:00:00 2001 From: Alibek Omarov Date: Sat, 14 Jan 2023 18:52:38 +0300 Subject: [PATCH 370/490] engine: client: fix fullscreen reapplied on window resize --- engine/client/vid_common.c | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/engine/client/vid_common.c b/engine/client/vid_common.c index 0390c3bd..9be8819f 100644 --- a/engine/client/vid_common.c +++ b/engine/client/vid_common.c @@ -68,16 +68,21 @@ R_SaveVideoMode */ void R_SaveVideoMode( int w, int h, int render_w, int render_h ) { - host.renderinfo_changed = false; - if( !w || !h || !render_w || !render_h ) + { + host.renderinfo_changed = false; return; + } host.window_center_x = w / 2; host.window_center_y = h / 2; Cvar_SetValue( "width", w ); Cvar_SetValue( "height", h ); + + // immediately drop changed state or we may trigger + // video subsystem to reapply settings + host.renderinfo_changed = false; if( refState.width == render_w && refState.height == render_h ) return; From 9c0c1a802cb7ef9d28da1110e692861810387c29 Mon Sep 17 00:00:00 2001 From: Alibek Omarov Date: Sun, 15 Jan 2023 13:11:45 +0300 Subject: [PATCH 371/490] filesystem: fix strict order of loading archives --- filesystem/filesystem.c | 20 +++++++++++++++++--- 1 file changed, 17 insertions(+), 3 deletions(-) diff --git a/filesystem/filesystem.c b/filesystem/filesystem.c index 014123a2..0fa54e4e 100644 --- a/filesystem/filesystem.c +++ b/filesystem/filesystem.c @@ -320,7 +320,8 @@ void FS_AddGameDirectory( const char *dir, uint flags ) listdirectory( &list, dir ); stringlistsort( &list ); - // add any PAK package in the directory + // add archives in specific order PAK -> PK3 -> WAD + // so raw WADs takes precedence over WADs included into PAKs and PK3s for( i = 0; i < list.numstrings; i++ ) { const char *ext = COM_FileExtension( list.strings[i] ); @@ -330,12 +331,24 @@ void FS_AddGameDirectory( const char *dir, uint flags ) Q_snprintf( fullpath, sizeof( fullpath ), "%s%s", dir, list.strings[i] ); FS_AddPak_Fullpath( fullpath, NULL, flags ); } - else if( !Q_stricmp( ext, "pk3" )) + } + + for( i = 0; i < list.numstrings; i++ ) + { + const char *ext = COM_FileExtension( list.strings[i] ); + + if( !Q_stricmp( ext, "pk3" )) { Q_snprintf( fullpath, sizeof( fullpath ), "%s%s", dir, list.strings[i] ); FS_AddZip_Fullpath( fullpath, NULL, flags ); } - else if( !Q_stricmp( ext, "wad" )) + } + + for( i = 0; i < list.numstrings; i++ ) + { + const char *ext = COM_FileExtension( list.strings[i] ); + + if( !Q_stricmp( ext, "wad" )) { FS_AllowDirectPaths( true ); Q_snprintf( fullpath, sizeof( fullpath ), "%s%s", dir, list.strings[i] ); @@ -343,6 +356,7 @@ void FS_AddGameDirectory( const char *dir, uint flags ) FS_AllowDirectPaths( false ); } } + stringlistfreecontents( &list ); // add the directory to the search path From 5313dc94756756d8d36a79889568d873a536cd4d Mon Sep 17 00:00:00 2001 From: Alibek Omarov Date: Mon, 16 Jan 2023 09:21:30 +0300 Subject: [PATCH 372/490] engine: client: fix uninitialized fadeTotalEnd in screenfade parsing code --- engine/client/cl_parse.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/engine/client/cl_parse.c b/engine/client/cl_parse.c index b45f575a..a2e4717f 100644 --- a/engine/client/cl_parse.c +++ b/engine/client/cl_parse.c @@ -1835,7 +1835,7 @@ void CL_ParseScreenFade( sizebuf_t *msg ) duration = (float)MSG_ReadWord( msg ); holdTime = (float)MSG_ReadWord( msg ); sf->fadeFlags = MSG_ReadShort( msg ); - flScale = ( sf->fadeFlags & FFADE_LONGFADE ) ? (1.0f / 256.0f) : (1.0f / 4096.0f); + flScale = FBitSet( sf->fadeFlags, FFADE_LONGFADE ) ? (1.0f / 256.0f) : (1.0f / 4096.0f); sf->fader = MSG_ReadByte( msg ); sf->fadeg = MSG_ReadByte( msg ); @@ -1848,7 +1848,7 @@ void CL_ParseScreenFade( sizebuf_t *msg ) // calc fade speed if( duration > 0 ) { - if( sf->fadeFlags & FFADE_OUT ) + if( FBitSet( sf->fadeFlags, FFADE_OUT )) { if( sf->fadeEnd ) { @@ -1856,6 +1856,7 @@ void CL_ParseScreenFade( sizebuf_t *msg ) } sf->fadeEnd += cl.time; + sf->fadeTotalEnd = sf->fadeEnd; sf->fadeReset += sf->fadeEnd; } else From 500ca545507709d0c6fba81a0b5ca87918ef291c Mon Sep 17 00:00:00 2001 From: Alibek Omarov Date: Wed, 18 Jan 2023 19:21:51 +0300 Subject: [PATCH 373/490] Revert "engine: get rid of MSG_BigShort, use htons instead, since network headers are always included" This reverts commit a6475f530baac26456f5be7143661586d521b04a. --- engine/client/cl_game.c | 2 +- engine/client/cl_main.c | 8 ++++---- engine/common/net_buffer.c | 5 +++++ engine/common/net_buffer.h | 1 + 4 files changed, 11 insertions(+), 5 deletions(-) diff --git a/engine/client/cl_game.c b/engine/client/cl_game.c index 18080e72..0a85cf21 100644 --- a/engine/client/cl_game.c +++ b/engine/client/cl_game.c @@ -3377,7 +3377,7 @@ void GAME_EXPORT NetAPI_SendRequest( int context, int request, int flags, double // make sure that port is specified if( !nr->resp.remote_address.port ) - nr->resp.remote_address.port = htons( PORT_MASTER ); + nr->resp.remote_address.port = MSG_BigShort( PORT_MASTER ); // grab the list from the master server Q_strcpy( &fullquery[22], GI->gamefolder ); diff --git a/engine/client/cl_main.c b/engine/client/cl_main.c index 1ffce5ef..710988cf 100644 --- a/engine/client/cl_main.c +++ b/engine/client/cl_main.c @@ -1006,7 +1006,7 @@ void CL_SendConnectPacket( void ) return; } - if( adr.port == 0 ) adr.port = htons( PORT_SERVER ); + if( adr.port == 0 ) adr.port = MSG_BigShort( PORT_SERVER ); qport = Cvar_VariableString( "net_qport" ); key = ID_GetMD5(); @@ -1134,7 +1134,7 @@ void CL_CheckForResend( void ) return; } - if( adr.port == 0 ) adr.port = htons( PORT_SERVER ); + if( adr.port == 0 ) adr.port = MSG_BigShort( PORT_SERVER ); if( cls.connect_retry == CL_TEST_RETRIES_NORESPONCE ) { @@ -1321,7 +1321,7 @@ void CL_Rcon_f( void ) } NET_StringToAdr( rcon_address->string, &to ); - if( to.port == 0 ) to.port = htons( PORT_SERVER ); + if( to.port == 0 ) to.port = MSG_BigShort( PORT_SERVER ); } NET_SendPacket( NS_CLIENT, Q_strlen( message ) + 1, message, to ); @@ -1563,7 +1563,7 @@ void CL_LocalServers_f( void ) // send a broadcast packet adr.type = NA_BROADCAST; - adr.port = htons( PORT_SERVER ); + adr.port = MSG_BigShort( PORT_SERVER ); Netchan_OutOfBandPrint( NS_CLIENT, adr, "info %i", PROTOCOL_VERSION ); adr.type = NA_MULTICAST_IP6; diff --git a/engine/common/net_buffer.c b/engine/common/net_buffer.c index 567047f2..34483ab9 100644 --- a/engine/common/net_buffer.c +++ b/engine/common/net_buffer.c @@ -89,6 +89,11 @@ const char *svc_strings[svc_lastmsg+1] = "svc_exec", }; +unsigned short MSG_BigShort( unsigned short swap ) +{ + return (swap >> 8)|(swap << 8); +} + void MSG_InitMasks( void ) { uint startbit, endbit; diff --git a/engine/common/net_buffer.h b/engine/common/net_buffer.h index b76c3011..50cb7361 100644 --- a/engine/common/net_buffer.h +++ b/engine/common/net_buffer.h @@ -63,6 +63,7 @@ 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 ); +unsigned short MSG_BigShort( unsigned short swap ); // init writing void MSG_StartWriting( sizebuf_t *sb, void *pData, int nBytes, int iStartBit, int nBits ); From 16b162f7bb31582a47c84ce96c8805236a3665ad Mon Sep 17 00:00:00 2001 From: Alibek Omarov Date: Wed, 18 Jan 2023 19:27:40 +0300 Subject: [PATCH 374/490] engine: vid: position window in center by default --- engine/client/vid_common.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/engine/client/vid_common.c b/engine/client/vid_common.c index 9be8819f..a7e86aec 100644 --- a/engine/client/vid_common.c +++ b/engine/client/vid_common.c @@ -186,8 +186,8 @@ void VID_Init( void ) Cvar_Get( "width", "0", FCVAR_RENDERINFO|FCVAR_VIDRESTART, "screen width" ); Cvar_Get( "height", "0", FCVAR_RENDERINFO|FCVAR_VIDRESTART, "screen height" ); - window_xpos = Cvar_Get( "_window_xpos", "130", FCVAR_RENDERINFO, "window position by horizontal" ); - window_ypos = Cvar_Get( "_window_ypos", "48", FCVAR_RENDERINFO, "window position by vertical" ); + window_xpos = Cvar_Get( "_window_xpos", "-1", FCVAR_RENDERINFO, "window position by horizontal" ); + window_ypos = Cvar_Get( "_window_ypos", "-1", FCVAR_RENDERINFO, "window position by vertical" ); vid_gamma = Cvar_Get( "gamma", "2.5", FCVAR_ARCHIVE, "gamma amount" ); vid_brightness = Cvar_Get( "brightness", "0.0", FCVAR_ARCHIVE, "brightness factor" ); From dd1d86c289c483258cd38069b50ae1d372ad7975 Mon Sep 17 00:00:00 2001 From: Alibek Omarov Date: Wed, 18 Jan 2023 19:28:16 +0300 Subject: [PATCH 375/490] engine: platform: sdl: check usable display rect before creating window --- engine/platform/sdl/vid_sdl.c | 30 ++++++++++++++++++++++++++---- 1 file changed, 26 insertions(+), 4 deletions(-) diff --git a/engine/platform/sdl/vid_sdl.c b/engine/platform/sdl/vid_sdl.c index e9dd87e3..e63f7589 100644 --- a/engine/platform/sdl/vid_sdl.c +++ b/engine/platform/sdl/vid_sdl.c @@ -639,11 +639,33 @@ qboolean VID_CreateWindow( int width, int height, qboolean fullscreen ) if( !fullscreen ) { + SDL_Rect r; + wndFlags |= SDL_WINDOW_RESIZABLE; - xpos = Cvar_VariableInteger( "_window_xpos" ); - ypos = Cvar_VariableInteger( "_window_ypos" ); - if( xpos < 0 ) xpos = SDL_WINDOWPOS_CENTERED; - if( ypos < 0 ) ypos = SDL_WINDOWPOS_CENTERED; + +#if SDL_VERSION_ATLEAST( 2, 0, 5 ) + if( SDL_GetDisplayUsableBounds( 0, &r ) < 0 && + SDL_GetDisplayBounds( 0, &r ) < 0 ) +#else + if( SDL_GetDisplayBounds( 0, &r ) < 0 ) +#endif + { + Con_Reportf( S_ERROR "VID_CreateWindow: SDL_GetDisplayBounds failed: %s\n", SDL_GetError( )); + xpos = SDL_WINDOWPOS_CENTERED; + ypos = SDL_WINDOWPOS_CENTERED; + } + else + { + xpos = Cvar_VariableInteger( "_window_xpos" ); + ypos = Cvar_VariableInteger( "_window_ypos" ); + + // don't create window outside of usable display space + if( xpos < r.x || xpos + width > r.x + r.w ) + xpos = SDL_WINDOWPOS_CENTERED; + + if( ypos < r.y || ypos + height > r.y + r.h ) + ypos = SDL_WINDOWPOS_CENTERED; + } } else { From aaeb18f433644e70a31086780bcb24a321c79dc0 Mon Sep 17 00:00:00 2001 From: Alibek Omarov Date: Wed, 18 Jan 2023 19:29:01 +0300 Subject: [PATCH 376/490] engine: inline version of MSG_BigShort --- engine/common/net_buffer.c | 5 ----- engine/common/net_buffer.h | 10 +++++++++- 2 files changed, 9 insertions(+), 6 deletions(-) diff --git a/engine/common/net_buffer.c b/engine/common/net_buffer.c index 34483ab9..567047f2 100644 --- a/engine/common/net_buffer.c +++ b/engine/common/net_buffer.c @@ -89,11 +89,6 @@ const char *svc_strings[svc_lastmsg+1] = "svc_exec", }; -unsigned short MSG_BigShort( unsigned short swap ) -{ - return (swap >> 8)|(swap << 8); -} - void MSG_InitMasks( void ) { uint startbit, endbit; diff --git a/engine/common/net_buffer.h b/engine/common/net_buffer.h index 50cb7361..62ded8dc 100644 --- a/engine/common/net_buffer.h +++ b/engine/common/net_buffer.h @@ -63,7 +63,15 @@ 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 ); -unsigned short MSG_BigShort( unsigned short swap ); + +#if XASH_BIG_ENDIAN +#define MSG_BigShort( x ) ( x ) +#else +static inline uint16_t MSG_BigShort( const uint16_t x ) +{ + return (x >> 8) | (x << 8); +} +#endif // init writing void MSG_StartWriting( sizebuf_t *sb, void *pData, int nBytes, int iStartBit, int nBits ); From ef1572b15b7ad91633796c5e2a6e58a2b0c4d842 Mon Sep 17 00:00:00 2001 From: Alibek Omarov Date: Wed, 18 Jan 2023 19:45:21 +0300 Subject: [PATCH 377/490] engine: crashhandler: fix build on FreeBSD (and probably on NetBSD and OpenBSD) --- engine/common/crashhandler.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/engine/common/crashhandler.c b/engine/common/crashhandler.c index 88b1ad26..871da5fc 100644 --- a/engine/common/crashhandler.c +++ b/engine/common/crashhandler.c @@ -353,7 +353,7 @@ static void Sys_Crash( int signal, siginfo_t *si, void *context) len = Q_snprintf( message, sizeof( message ), "Ver: " XASH_ENGINE_NAME " " XASH_VERSION " (build %i-%s, %s-%s)\n", Q_buildnum(), Q_buildcommit(), Q_buildos(), Q_buildarch() ); -#if !XASH_BSD +#if !XASH_FREEBSD && !XASH_NETBSD && !XASH_OPENBSD len += Q_snprintf( message + len, sizeof( message ) - len, "Crash: signal %d errno %d with code %d at %p %p\n", signal, si->si_errno, si->si_code, si->si_addr, si->si_ptr ); #else len += Q_snprintf( message + len, sizeof( message ) - len, "Crash: signal %d errno %d with code %d at %p\n", signal, si->si_errno, si->si_code, si->si_addr ); From b946ed46251d2f4b6372bd05344dd9a9989c06d2 Mon Sep 17 00:00:00 2001 From: Alibek Omarov Date: Wed, 18 Jan 2023 20:05:00 +0300 Subject: [PATCH 378/490] engine: platform: sdl: don't flood about closest display mode in case of no changes --- engine/platform/sdl/vid_sdl.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/engine/platform/sdl/vid_sdl.c b/engine/platform/sdl/vid_sdl.c index e63f7589..4fc6b72b 100644 --- a/engine/platform/sdl/vid_sdl.c +++ b/engine/platform/sdl/vid_sdl.c @@ -563,10 +563,11 @@ static qboolean VID_SetScreenResolution( int width, int height ) want.driverdata = NULL; want.format = want.refresh_rate = 0; // don't care - if( !SDL_GetClosestDisplayMode(0, &want, &got) ) + if( !SDL_GetClosestDisplayMode( 0, &want, &got )) return false; - Con_Reportf( "Got closest display mode: %ix%i@%i\n", got.w, got.h, got.refresh_rate); + if( got.w != want.w || got.h != want.h ) + Con_Reportf( "Got closest display mode: %ix%i@%i\n", got.w, got.h, got.refresh_rate); if( SDL_SetWindowDisplayMode( host.hWnd, &got) == -1 ) return false; From c481e52558d23e120bba4f22dc7c9ffe0e573a9b Mon Sep 17 00:00:00 2001 From: Alibek Omarov Date: Wed, 25 Jan 2023 02:37:30 +0300 Subject: [PATCH 379/490] engine: client: consolidate variable and quake fixed width font loading functions --- engine/client/cl_scrn.c | 89 +++++++---------------------------------- engine/client/client.h | 2 + engine/client/console.c | 77 ++++++++++++++++++++--------------- 3 files changed, 62 insertions(+), 106 deletions(-) diff --git a/engine/client/cl_scrn.c b/engine/client/cl_scrn.c index 3949d4ae..602c13a9 100644 --- a/engine/client/cl_scrn.c +++ b/engine/client/cl_scrn.c @@ -577,77 +577,6 @@ void SCR_UpdateScreen( void ) V_PostRender(); } -static qboolean SCR_LoadFixedWidthFont( const char *fontname ) -{ - int i, fontWidth; - - if( cls.creditsFont.valid ) - return true; // already loaded - - if( !FS_FileExists( fontname, false )) - return false; - - cls.creditsFont.hFontTexture = ref.dllFuncs.GL_LoadTexture( fontname, NULL, 0, TF_IMAGE|TF_KEEP_SOURCE|TF_NEAREST ); - R_GetTextureParms( &fontWidth, NULL, cls.creditsFont.hFontTexture ); - cls.creditsFont.charHeight = clgame.scrInfo.iCharHeight = fontWidth / 16; - cls.creditsFont.type = FONT_FIXED; - cls.creditsFont.valid = true; - - // build fixed rectangles - for( i = 0; i < 256; i++ ) - { - cls.creditsFont.fontRc[i].left = (i * (fontWidth / 16)) % fontWidth; - cls.creditsFont.fontRc[i].right = cls.creditsFont.fontRc[i].left + fontWidth / 16; - cls.creditsFont.fontRc[i].top = (i / 16) * (fontWidth / 16); - cls.creditsFont.fontRc[i].bottom = cls.creditsFont.fontRc[i].top + fontWidth / 16; - cls.creditsFont.charWidths[i] = clgame.scrInfo.charWidths[i] = fontWidth / 16; - } - - return true; -} - -static qboolean SCR_LoadVariableWidthFont( const char *fontname ) -{ - int i, fontWidth; - byte *buffer; - fs_offset_t length; - qfont_t *src; - - if( cls.creditsFont.valid ) - return true; // already loaded - - if( !FS_FileExists( fontname, false )) - return false; - - cls.creditsFont.hFontTexture = ref.dllFuncs.GL_LoadTexture( fontname, NULL, 0, TF_IMAGE|TF_NEAREST ); - R_GetTextureParms( &fontWidth, NULL, cls.creditsFont.hFontTexture ); - - // half-life font with variable chars witdh - buffer = FS_LoadFile( fontname, &length, false ); - - // setup creditsfont - if( buffer && length >= sizeof( qfont_t )) - { - src = (qfont_t *)buffer; - cls.creditsFont.charHeight = clgame.scrInfo.iCharHeight = src->rowheight; - cls.creditsFont.type = FONT_VARIABLE; - - // build rectangles - for( i = 0; i < 256; i++ ) - { - cls.creditsFont.fontRc[i].left = (word)src->fontinfo[i].startoffset % fontWidth; - cls.creditsFont.fontRc[i].right = cls.creditsFont.fontRc[i].left + src->fontinfo[i].charwidth; - cls.creditsFont.fontRc[i].top = (word)src->fontinfo[i].startoffset / fontWidth; - cls.creditsFont.fontRc[i].bottom = cls.creditsFont.fontRc[i].top + src->rowheight; - cls.creditsFont.charWidths[i] = clgame.scrInfo.charWidths[i] = src->fontinfo[i].charwidth; - } - cls.creditsFont.valid = true; - } - if( buffer ) Mem_Free( buffer ); - - return true; -} - /* ================ SCR_LoadCreditsFont @@ -657,6 +586,7 @@ INTERNAL RESOURCE */ void SCR_LoadCreditsFont( void ) { + cl_font_t *const font = &cls.creditsFont; qboolean success = false; dword crc = 0; @@ -669,17 +599,28 @@ void SCR_LoadCreditsFont( void ) "creditsfont_%s.fnt", Cvar_VariableString( "con_charset" )) > 0 ) { if( FS_FileExists( charsetFnt, false )) - success = SCR_LoadVariableWidthFont( charsetFnt ); + success = Con_LoadVariableWidthFont( charsetFnt, font, 1.0f, TF_FONT ); } } - if( !success && !SCR_LoadVariableWidthFont( "gfx/creditsfont.fnt" )) + if( !success && !Con_LoadVariableWidthFont( "gfx/creditsfont.fnt", font, 1.0f, TF_FONT )) { - if( !SCR_LoadFixedWidthFont( "gfx/conchars" )) + if( !Con_LoadFixedWidthFont( "gfx/conchars", font, 1.0f, TF_FONT )) { Con_DPrintf( S_ERROR "failed to load HUD font\n" ); } } + + // copy font size for client.dll + if( success ) + { + int i; + + clgame.scrInfo.iCharHeight = cls.creditsFont.charHeight; + + for( i = 0; i < ARRAYSIZE( cls.creditsFont.charWidths ); i++ ) + clgame.scrInfo.charWidths[i] = cls.creditsFont.charWidths[i]; + } } /* diff --git a/engine/client/client.h b/engine/client/client.h index f473d426..fc093312 100644 --- a/engine/client/client.h +++ b/engine/client/client.h @@ -1044,6 +1044,8 @@ void Con_Bottom( void ); void Con_Top( void ); void Con_PageDown( int lines ); void Con_PageUp( int lines ); +qboolean Con_LoadVariableWidthFont( const char *fontname, cl_font_t *font, float scale, uint texFlags ); +qboolean Con_LoadFixedWidthFont( const char *fontname, cl_font_t *font, float scale, uint texFlags ); // // s_main.c diff --git a/engine/client/console.c b/engine/client/console.c index cf6e1e28..03823b52 100644 --- a/engine/client/console.c +++ b/engine/client/console.c @@ -560,9 +560,10 @@ qboolean Con_FixedFont( void ) return false; } -static qboolean Con_LoadFixedWidthFont( const char *fontname, cl_font_t *font ) +qboolean Con_LoadFixedWidthFont( const char *fontname, cl_font_t *font, float scale, uint texFlags ) { - int i, fontWidth; + int fontWidth; + int i; if( font->valid ) return true; // already loaded @@ -570,13 +571,12 @@ static qboolean Con_LoadFixedWidthFont( const char *fontname, cl_font_t *font ) if( !FS_FileExists( fontname, false )) return false; - // keep source to print directly into conback image - font->hFontTexture = ref.dllFuncs.GL_LoadTexture( fontname, NULL, 0, TF_FONT|TF_KEEP_SOURCE ); + font->hFontTexture = ref.dllFuncs.GL_LoadTexture( fontname, NULL, 0, texFlags ); R_GetTextureParms( &fontWidth, NULL, font->hFontTexture ); if( font->hFontTexture && fontWidth != 0 ) { - font->charHeight = fontWidth / 16 * con_fontscale->value; + font->charHeight = fontWidth / 16 * scale; font->type = FONT_FIXED; // build fixed rectangles @@ -586,7 +586,7 @@ static qboolean Con_LoadFixedWidthFont( const char *fontname, cl_font_t *font ) font->fontRc[i].right = font->fontRc[i].left + fontWidth / 16; font->fontRc[i].top = (i / 16) * (fontWidth / 16); font->fontRc[i].bottom = font->fontRc[i].top + fontWidth / 16; - font->charWidths[i] = fontWidth / 16 * con_fontscale->value; + font->charWidths[i] = fontWidth / 16 * scale; } font->valid = true; } @@ -594,12 +594,13 @@ static qboolean Con_LoadFixedWidthFont( const char *fontname, cl_font_t *font ) return true; } -static qboolean Con_LoadVariableWidthFont( const char *fontname, cl_font_t *font ) +qboolean Con_LoadVariableWidthFont( const char *fontname, cl_font_t *font, float scale, uint texFlags ) { - int i, fontWidth; - byte *buffer; fs_offset_t length; - qfont_t *src; + qfont_t *src; + byte *buffer; + int fontWidth; + int i; if( font->valid ) return true; // already loaded @@ -607,7 +608,7 @@ static qboolean Con_LoadVariableWidthFont( const char *fontname, cl_font_t *font if( !FS_FileExists( fontname, false )) return false; - font->hFontTexture = ref.dllFuncs.GL_LoadTexture( fontname, NULL, 0, TF_FONT|TF_NEAREST ); + font->hFontTexture = ref.dllFuncs.GL_LoadTexture( fontname, NULL, 0, texFlags ); R_GetTextureParms( &fontWidth, NULL, font->hFontTexture ); // setup consolefont @@ -619,7 +620,7 @@ static qboolean Con_LoadVariableWidthFont( const char *fontname, cl_font_t *font if( buffer && length >= sizeof( qfont_t )) { src = (qfont_t *)buffer; - font->charHeight = src->rowheight * con_fontscale->value; + font->charHeight = src->rowheight * scale; font->type = FONT_VARIABLE; // build rectangles @@ -629,7 +630,7 @@ static qboolean Con_LoadVariableWidthFont( const char *fontname, cl_font_t *font font->fontRc[i].right = font->fontRc[i].left + src->fontinfo[i].charwidth; font->fontRc[i].top = (word)src->fontinfo[i].startoffset / fontWidth; font->fontRc[i].bottom = font->fontRc[i].top + src->rowheight; - font->charWidths[i] = src->fontinfo[i].charwidth * con_fontscale->value; + font->charWidths[i] = src->fontinfo[i].charwidth * scale; } font->valid = true; } @@ -648,32 +649,45 @@ INTERNAL RESOURCE */ static void Con_LoadConsoleFont( int fontNumber, cl_font_t *font ) { - const char *path = NULL; - dword crc = 0; + qboolean success = false; - if( font->valid ) return; // already loaded - - // replace default fonts.wad textures by current charset's font - if( !CRC32_File( &crc, "fonts.wad" ) || crc == 0x3c0a0029 ) - { - const char *path2 = va("font%i_%s.fnt", fontNumber, Cvar_VariableString( "con_charset" ) ); - if( FS_FileExists( path2, false ) ) - path = path2; - } + if( font->valid ) + return; // already loaded // loading conchars if( Sys_CheckParm( "-oldfont" )) - Con_LoadVariableWidthFont( "gfx/conchars.fnt", font ); + { + success = Con_LoadVariableWidthFont( "gfx/conchars.fnt", font, con_fontscale->value, TF_FONT|TF_NEAREST ); + } else { - if( !path ) - path = va( "fonts/font%i", fontNumber ); + string path; + dword crc = 0; - Con_LoadVariableWidthFont( path, font ); + // replace default fonts.wad textures by current charset's font + if( !CRC32_File( &crc, "fonts.wad" ) || crc == 0x3c0a0029 ) + { + if( Q_snprintf( path, sizeof( path ), + "font%i_%s.fnt", fontNumber, Cvar_VariableString( "con_charset" )) > 0 ) + { + success = Con_LoadVariableWidthFont( path, font, con_fontscale->value, TF_FONT|TF_NEAREST ); + } + } + + if( !success ) + { + Q_snprintf( path, sizeof( path ), "fonts/font%i", fontNumber ); + success = Con_LoadVariableWidthFont( path, font, con_fontscale->value, TF_FONT|TF_NEAREST ); + } } - // quake fixed font as fallback - if( !font->valid ) Con_LoadFixedWidthFont( "gfx/conchars", font ); + if( !success ) + { + // quake fixed font as fallback + // keep source to print directly into conback image + if( !Con_LoadFixedWidthFont( "gfx/conchars", font, con_fontscale->value, TF_FONT|TF_KEEP_SOURCE )) + Con_DPrintf( S_ERROR "failed to load console font\n" ); + } } /* @@ -2397,7 +2411,7 @@ void Con_RunConsole( void ) con.vislines = con.showlines; } - if( FBitSet( con_charset->flags, FCVAR_CHANGED ) || + if( FBitSet( con_charset->flags, FCVAR_CHANGED ) || FBitSet( con_fontscale->flags, FCVAR_CHANGED ) || FBitSet( con_fontnum->flags, FCVAR_CHANGED ) || FBitSet( cl_charset->flags, FCVAR_CHANGED ) ) @@ -2428,7 +2442,6 @@ void Con_RunConsole( void ) ClearBits( con_fontnum->flags, FCVAR_CHANGED ); ClearBits( con_fontscale->flags, FCVAR_CHANGED ); ClearBits( cl_charset->flags, FCVAR_CHANGED ); - } } From 84c14b32ec72e9614173efbd235923cbdbc8bec0 Mon Sep 17 00:00:00 2001 From: Alibek Omarov Date: Wed, 25 Jan 2023 04:17:44 +0300 Subject: [PATCH 380/490] engine: client: fix filtering errors by adjusting texcoords by half of a pixel Remove useless wrapper functions and conversions Don't scale texcoords and position if hud_scale is not active --- engine/client/cl_game.c | 91 +++++++++++++++++---------------------- engine/client/cl_gameui.c | 23 +++++++--- engine/client/client.h | 3 +- 3 files changed, 60 insertions(+), 57 deletions(-) diff --git a/engine/client/cl_game.c b/engine/client/cl_game.c index 0a85cf21..1b2600be 100644 --- a/engine/client/cl_game.c +++ b/engine/client/cl_game.c @@ -345,53 +345,43 @@ SPR_AdjustSize draw hudsprite routine ==================== */ -static void SPR_AdjustSize( float *x, float *y, float *w, float *h ) +void SPR_AdjustSize( float *x, float *y, float *w, float *h ) { float xscale, yscale; + if( refState.width == clgame.scrInfo.iWidth && refState.height == clgame.scrInfo.iHeight ) + return; + // scale for screen sizes xscale = refState.width / (float)clgame.scrInfo.iWidth; yscale = refState.height / (float)clgame.scrInfo.iHeight; - if( x ) *x *= xscale; - if( y ) *y *= yscale; - if( w ) *w *= xscale; - if( h ) *h *= yscale; + *x *= xscale; + *y *= yscale; + *w *= xscale; + *h *= yscale; } -/* -==================== -SPR_AdjustSize - -draw hudsprite routine -==================== -*/ -static void SPR_AdjustSizei( int *x, int *y, int *w, int *h ) +void SPR_AdjustTexCoords( float width, float height, float *s1, float *t1, float *s2, float *t2 ) { - float xscale, yscale; + if( refState.width != clgame.scrInfo.iWidth ) + { + // align to texel if scaling + *s1 += 0.5f; + *s2 -= 0.5f; + } - // scale for screen sizes - xscale = refState.width / (float)clgame.scrInfo.iWidth; - yscale = refState.height / (float)clgame.scrInfo.iHeight; + if( refState.height != clgame.scrInfo.iHeight ) + { + // align to texel if scaling + *t1 += 0.5f; + *t2 -= 0.5f; + } - if( x ) *x *= xscale; - if( y ) *y *= yscale; - if( w ) *w *= xscale; - if( h ) *h *= yscale; -} - -/* -==================== -PictAdjustSize - -draw hudsprite routine -==================== -*/ -void PicAdjustSize( float *x, float *y, float *w, float *h ) -{ - if( !clgame.ds.adjust_size ) return; - - SPR_AdjustSize( x, y, w, h ); + *s1 /= width; + *t1 /= height; + *s2 /= width; + *t2 /= height; } static qboolean SPR_Scissor( float *x, float *y, float *width, float *height, float *u0, float *v0, float *u1, float *v1 ) @@ -468,9 +458,7 @@ static void SPR_DrawGeneric( int frame, float x, float y, float width, float hei if( prc ) { - wrect_t rc; - - rc = *prc; + wrect_t rc = *prc; // Sigh! some stupid modmakers set wrong rectangles in hud.txt if( rc.left <= 0 || rc.left >= width ) rc.left = 0; @@ -478,11 +466,13 @@ static void SPR_DrawGeneric( int frame, float x, float y, float width, float hei if( rc.right <= 0 || rc.right > width ) rc.right = width; if( rc.bottom <= 0 || rc.bottom > height ) rc.bottom = height; + s1 = rc.left; + t1 = rc.top; + s2 = rc.right; + t2 = rc.bottom; + // calc user-defined rectangle - s1 = (float)rc.left / width; - t1 = (float)rc.top / height; - s2 = (float)rc.right / width; - t2 = (float)rc.bottom / height; + SPR_AdjustTexCoords( width, height, &s1, &t1, &s2, &t2 ); width = rc.right - rc.left; height = rc.bottom - rc.top; } @@ -950,13 +940,16 @@ draw loading progress bar */ static void CL_DrawLoadingOrPaused( qboolean paused, float percent ) { - int x, y, width, height, right; + float x, y, width, height; + int iWidth, iHeight; - R_GetTextureParms( &width, &height, paused ? cls.pauseIcon : cls.loadingBar ); - x = ( clgame.scrInfo.iWidth - width ) >> 1; - y = ( clgame.scrInfo.iHeight - height) >> 1; + R_GetTextureParms( &iWidth, &iHeight, paused ? cls.pauseIcon : cls.loadingBar ); + x = ( clgame.scrInfo.iWidth - width ) / 2.0f;; + y = ( clgame.scrInfo.iHeight - height ) / 2.0f; + width = iWidth; + height = iHeight; - SPR_AdjustSizei( &x, &y, &width, &height ); + SPR_AdjustSize( &x, &y, &width, &height ); if( !paused ) { @@ -1170,10 +1163,6 @@ static qboolean CL_LoadHudSprite( const char *szSpriteName, model_t *m_pSprite, if( type == SPR_CLIENT || type == SPR_HUDSPRITE ) SetBits( m_pSprite->flags, MODEL_CLIENT ); - // force nearest filter for hud sprites to have less artifacts with hud_scale - if( type == SPR_HUDSPRITE ) - SetBits( texFlags, TF_NEAREST ); - m_pSprite->numtexinfo = texFlags; // store texFlags into numtexinfo if( !FS_FileExists( szSpriteName, false ) ) diff --git a/engine/client/cl_gameui.c b/engine/client/cl_gameui.c index 5d3b41ec..440afa32 100644 --- a/engine/client/cl_gameui.c +++ b/engine/client/cl_gameui.c @@ -487,10 +487,22 @@ static void PIC_DrawGeneric( float x, float y, float width, float height, const if( prc ) { // calc user-defined rectangle - s1 = (float)prc->left / (float)w; - t1 = (float)prc->top / (float)h; - s2 = (float)prc->right / (float)w; - t2 = (float)prc->bottom / (float)h; + s1 = prc->left; + t1 = prc->top; + s2 = prc->right; + t2 = prc->bottom; + + if( clgame.ds.adjust_size ) + { + SPR_AdjustTexCoords( w, h, &s1, &t1, &s2, &t2 ); + } + else + { + s1 /= (float)w; + t1 /= (float)h; + s2 /= (float)w; + t2 /= (float)h; + } if( width == -1 && height == -1 ) { @@ -514,7 +526,8 @@ static void PIC_DrawGeneric( float x, float y, float width, float height, const if( gameui.ds.scissor_test && !PIC_Scissor( &x, &y, &width, &height, &s1, &t1, &s2, &t2 )) return; - PicAdjustSize( &x, &y, &width, &height ); + if( clgame.ds.adjust_size ) + SPR_AdjustSize( &x, &y, &width, &height ); ref.dllFuncs.R_DrawStretchPic( x, y, width, height, s1, t1, s2, t2, gameui.ds.gl_texturenum ); ref.dllFuncs.Color4ub( 255, 255, 255, 255 ); } diff --git a/engine/client/client.h b/engine/client/client.h index fc093312..1883ab9a 100644 --- a/engine/client/client.h +++ b/engine/client/client.h @@ -824,7 +824,8 @@ model_t *CL_LoadClientSprite( const char *filename ); model_t *CL_LoadModel( const char *modelname, int *index ); HSPRITE EXPORT pfnSPR_Load( const char *szPicName ); HSPRITE pfnSPR_LoadExt( const char *szPicName, uint texFlags ); -void PicAdjustSize( float *x, float *y, float *w, float *h ); +void SPR_AdjustSize( float *x, float *y, float *w, float *h ); +void SPR_AdjustTexCoords( float width, float height, float *s1, float *t1, float *s2, float *t2 ); int CL_GetScreenInfo( SCREENINFO *pscrinfo ); void CL_FillRGBA( int x, int y, int width, int height, int r, int g, int b, int a ); void CL_PlayerTrace( float *start, float *end, int traceFlags, int ignore_pe, pmtrace_t *tr ); From ffd5c2d3d06a0a8061bc5d6235357a402ad3b8ef Mon Sep 17 00:00:00 2001 From: Andrey Akhmichin <15944199+nekonomicon@users.noreply.github.com> Date: Wed, 25 Jan 2023 04:22:31 +0500 Subject: [PATCH 381/490] engine: common: soundlib: libmpg: backport fix for CVE-2017-12839. Original patch: https://www.mpg123.de/cgi-bin/scm/mpg123/trunk/src/libmpg123/getbits.h?view=patch&r1=2024&r2=4323 Same as: https://github.com/tyabus/xash3d/commit/8a5e21a2a28e7486e601153ab3ca1beb1495308b --- engine/common/soundlib/libmpg/getbits.h | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/engine/common/soundlib/libmpg/getbits.h b/engine/common/soundlib/libmpg/getbits.h index 350d5ab1..a4e5f165 100644 --- a/engine/common/soundlib/libmpg/getbits.h +++ b/engine/common/soundlib/libmpg/getbits.h @@ -47,6 +47,10 @@ static uint getbits( mpg123_handle_t *fr, int number_of_bits ) { ulong rval; + if( (long)(fr->wordpointer-fr->bsbuf)*8 + + fr->bitindex+number_of_bits > (long)fr->framesize*8 ) + return 0; + rval = fr->wordpointer[0]; rval <<= 8; rval |= fr->wordpointer[1]; From 801dbaa387027ba51db546c6a6d9c8cd9557d152 Mon Sep 17 00:00:00 2001 From: Andrey Akhmichin <15944199+nekonomicon@users.noreply.github.com> Date: Wed, 25 Jan 2023 04:32:48 +0500 Subject: [PATCH 382/490] engine: common: soundlib: libmpg: backport fix for CVE-2017-11126. Original patch: https://www.mpg123.de/cgi-bin/scm/mpg123/trunk/src/libmpg123/layer3.c?view=patch&r1=4275&r2=4274 Same as: https://github.com/tyabus/xash3d/commit/f246a0cdfd9f142cedebb97810cfa211adb60fed --- engine/common/soundlib/libmpg/layer3.c | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/engine/common/soundlib/libmpg/layer3.c b/engine/common/soundlib/libmpg/layer3.c index e4e1228c..5b1b7adb 100644 --- a/engine/common/soundlib/libmpg/layer3.c +++ b/engine/common/soundlib/libmpg/layer3.c @@ -35,10 +35,10 @@ static float tan1_1[16]; static float tan2_1[16]; static float tan1_2[16]; static float tan2_2[16]; -static float pow1_1[2][16]; -static float pow2_1[2][16]; -static float pow1_2[2][16]; -static float pow2_2[2][16]; +static float pow1_1[2][32]; +static float pow2_1[2][32]; +static float pow1_2[2][32]; +static float pow2_2[2][32]; static int mapbuf0[9][152]; static int mapbuf1[9][156]; static int mapbuf2[9][44]; @@ -217,7 +217,10 @@ void init_layer3( void ) tan2_1[i] = DOUBLE_TO_REAL_15( 1.0 / (1.0 + t)); tan1_2[i] = DOUBLE_TO_REAL_15( M_SQRT2 * t / (1.0 + t)); tan2_2[i] = DOUBLE_TO_REAL_15( M_SQRT2 / (1.0 + t)); + } + for( i = 0; i < 32; i++ ) + { for( j = 0; j < 2; j++ ) { double base = pow( 2.0, -0.25 * (j + 1.0)); From 327372e2533caed8f0f33902412d5e665c377f33 Mon Sep 17 00:00:00 2001 From: Alibek Omarov Date: Fri, 27 Jan 2023 05:51:43 +0300 Subject: [PATCH 383/490] engine: client: call SND_ForceOpen/CloseMouth within SND_Open/CloseMouth to avoid copypasted code --- engine/client/s_mouth.c | 28 +++++----------------------- 1 file changed, 5 insertions(+), 23 deletions(-) diff --git a/engine/client/s_mouth.c b/engine/client/s_mouth.c index 3b93c8c8..716fe612 100644 --- a/engine/client/s_mouth.c +++ b/engine/client/s_mouth.c @@ -24,17 +24,7 @@ void SND_InitMouth( int entnum, int entchannel ) { if(( entchannel == CHAN_VOICE || entchannel == CHAN_STREAM ) && entnum > 0 ) { - cl_entity_t *clientEntity; - - // init mouth movement vars - clientEntity = CL_GetEntityByIndex( entnum ); - - if( clientEntity ) - { - clientEntity->mouth.mouthopen = 0; - clientEntity->mouth.sndcount = 0; - clientEntity->mouth.sndavg = 0; - } + SND_ForceInitMouth( entnum ); } } @@ -42,15 +32,7 @@ void SND_CloseMouth( channel_t *ch ) { if( ch->entchannel == CHAN_VOICE || ch->entchannel == CHAN_STREAM ) { - cl_entity_t *clientEntity; - - clientEntity = CL_GetEntityByIndex( ch->entnum ); - - if( clientEntity ) - { - // shut mouth - clientEntity->mouth.mouthopen = 0; - } + SND_ForceCloseMouth( ch->entnum ); } } @@ -157,7 +139,7 @@ void SND_ForceInitMouth( int entnum ) clientEntity = CL_GetEntityByIndex( entnum ); - if ( clientEntity ) + if( clientEntity ) { clientEntity->mouth.mouthopen = 0; clientEntity->mouth.sndavg = 0; @@ -171,7 +153,7 @@ void SND_ForceCloseMouth( int entnum ) clientEntity = CL_GetEntityByIndex( entnum ); - if ( clientEntity ) + if( clientEntity ) clientEntity->mouth.mouthopen = 0; } @@ -214,4 +196,4 @@ void SND_MoveMouthRaw( rawchan_t *ch, portable_samplepair_t *pData, int count ) pMouth->sndavg = 0; pMouth->sndcount = 0; } -} \ No newline at end of file +} From 6df25392b8bccc684702e801f5722b0a2887a8fd Mon Sep 17 00:00:00 2001 From: Alibek Omarov Date: Fri, 27 Jan 2023 06:16:47 +0300 Subject: [PATCH 384/490] engine: server: redirect special sounds, detected by leading asterisk, into CHAN_STREAM In GoldSrc this magic symbol means that we should not override channel this sound is playing on. Originally handled on client but for both static and dynamic sounds so let's redirect channel on server side instead. --- engine/server/sv_game.c | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/engine/server/sv_game.c b/engine/server/sv_game.c index b439ae29..d3d953dc 100644 --- a/engine/server/sv_game.c +++ b/engine/server/sv_game.c @@ -2087,8 +2087,11 @@ int SV_BuildSoundMsg( sizebuf_t *msg, edict_t *ent, int chan, const char *sample } else { - // TESTTEST - if( *sample == '*' ) chan = CHAN_AUTO; + // '*' is special symbol to handle stream sounds + // (CHAN_VOICE but cannot be overriden) + // originally handled on client side + if( *sample == '*' ) + chan = CHAN_STREAM; // precache_sound can be used twice: cache sounds when loading // and return sound index when server is active From 279e3919490787eeb493653ca7cd8a3810a2b5de Mon Sep 17 00:00:00 2001 From: Alibek Omarov Date: Fri, 27 Jan 2023 06:19:17 +0300 Subject: [PATCH 385/490] engine: sound: allow mouth move for CHAN_STREAM --- engine/client/s_mix.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/engine/client/s_mix.c b/engine/client/s_mix.c index 8d5e0ba7..f58b5063 100644 --- a/engine/client/s_mix.c +++ b/engine/client/s_mix.c @@ -621,7 +621,7 @@ void MIX_MixChannelsToPaintbuffer( int endtime, int rate, int outputRate ) ch->pitch *= ( sys_timescale.value + 1 ) / 2; - if( CL_GetEntityByIndex( ch->entnum ) && ( ch->entchannel == CHAN_VOICE )) + if( CL_GetEntityByIndex( ch->entnum ) && ( ch->entchannel == CHAN_VOICE || ch->entchannel == CHAN_STREAM )) { if( pSource->width == 1 ) SND_MoveMouth8( ch, pSource, sampleCount ); From 39fd30a4723db23a5f69404ee3aaaee8dbda6324 Mon Sep 17 00:00:00 2001 From: Alibek Omarov Date: Fri, 27 Jan 2023 19:07:26 +0300 Subject: [PATCH 386/490] engine: client: fix HUD font loading --- engine/client/cl_scrn.c | 13 ++++++------- 1 file changed, 6 insertions(+), 7 deletions(-) diff --git a/engine/client/cl_scrn.c b/engine/client/cl_scrn.c index 602c13a9..e4e99eab 100644 --- a/engine/client/cl_scrn.c +++ b/engine/client/cl_scrn.c @@ -603,13 +603,11 @@ void SCR_LoadCreditsFont( void ) } } - if( !success && !Con_LoadVariableWidthFont( "gfx/creditsfont.fnt", font, 1.0f, TF_FONT )) - { - if( !Con_LoadFixedWidthFont( "gfx/conchars", font, 1.0f, TF_FONT )) - { - Con_DPrintf( S_ERROR "failed to load HUD font\n" ); - } - } + if( !success ) + success = Con_LoadVariableWidthFont( "gfx/creditsfont.fnt", font, 1.0f, TF_FONT ); + + if( !success ) + success = Con_LoadFixedWidthFont( "gfx/conchars", font, 1.0f, TF_FONT ); // copy font size for client.dll if( success ) @@ -621,6 +619,7 @@ void SCR_LoadCreditsFont( void ) for( i = 0; i < ARRAYSIZE( cls.creditsFont.charWidths ); i++ ) clgame.scrInfo.charWidths[i] = cls.creditsFont.charWidths[i]; } + else Con_DPrintf( S_ERROR "failed to load HUD font\n" ); } /* From 6282acc82563ae67576d56ff07bfde74fefd82f8 Mon Sep 17 00:00:00 2001 From: Alibek Omarov Date: Mon, 30 Jan 2023 23:17:53 +0300 Subject: [PATCH 387/490] engine: client: simplify drawing loading or paused bar, fix position with hud_scale active --- engine/client/cl_game.c | 30 ++++++++++-------------------- 1 file changed, 10 insertions(+), 20 deletions(-) diff --git a/engine/client/cl_game.c b/engine/client/cl_game.c index 1b2600be..3e65c516 100644 --- a/engine/client/cl_game.c +++ b/engine/client/cl_game.c @@ -938,31 +938,21 @@ CL_DrawLoading draw loading progress bar ============= */ -static void CL_DrawLoadingOrPaused( qboolean paused, float percent ) +static void CL_DrawLoadingOrPaused( int tex ) { float x, y, width, height; int iWidth, iHeight; - R_GetTextureParms( &iWidth, &iHeight, paused ? cls.pauseIcon : cls.loadingBar ); - x = ( clgame.scrInfo.iWidth - width ) / 2.0f;; - y = ( clgame.scrInfo.iHeight - height ) / 2.0f; + R_GetTextureParms( &iWidth, &iHeight, tex ); + x = ( clgame.scrInfo.iWidth - iWidth ) / 2.0f; + y = ( clgame.scrInfo.iHeight - iHeight ) / 2.0f; width = iWidth; height = iHeight; SPR_AdjustSize( &x, &y, &width, &height ); - - if( !paused ) - { - ref.dllFuncs.Color4ub( 255, 255, 255, 255 ); - ref.dllFuncs.GL_SetRenderMode( kRenderTransTexture ); - ref.dllFuncs.R_DrawStretchPic( x, y, width, height, 0, 0, 1, 1, cls.loadingBar ); - } - else - { - ref.dllFuncs.Color4ub( 255, 255, 255, 255 ); - ref.dllFuncs.GL_SetRenderMode( kRenderTransTexture ); - ref.dllFuncs.R_DrawStretchPic( x, y, width, height, 0, 0, 1, 1, cls.pauseIcon ); - } + ref.dllFuncs.Color4ub( 255, 255, 255, 255 ); + ref.dllFuncs.GL_SetRenderMode( kRenderTransTexture ); + ref.dllFuncs.R_DrawStretchPic( x, y, width, height, 0, 0, 1, 1, tex ); } void CL_DrawHUD( int state ) @@ -988,15 +978,15 @@ void CL_DrawHUD( int state ) CL_DrawCrosshair (); CL_DrawCenterPrint (); clgame.dllFuncs.pfnRedraw( cl.time, cl.intermission ); - CL_DrawLoadingOrPaused( true, 0.0f ); + CL_DrawLoadingOrPaused( cls.pauseIcon ); break; case CL_LOADING: - CL_DrawLoadingOrPaused( false, scr_loading->value ); + CL_DrawLoadingOrPaused( cls.loadingBar ); break; case CL_CHANGELEVEL: if( cls.draw_changelevel ) { - CL_DrawLoadingOrPaused( false, 100.0f ); + CL_DrawLoadingOrPaused( cls.loadingBar ); cls.draw_changelevel = false; } break; From aac0be1ab3f0f3bf5fd54b0c496e50e2deb9fec5 Mon Sep 17 00:00:00 2001 From: Alibek Omarov Date: Tue, 31 Jan 2023 00:49:57 +0300 Subject: [PATCH 388/490] engine: imagelib: img_bmp: fully initialize local palette array --- engine/common/imagelib/img_bmp.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/engine/common/imagelib/img_bmp.c b/engine/common/imagelib/img_bmp.c index f0f9785a..d4edb2e1 100644 --- a/engine/common/imagelib/img_bmp.c +++ b/engine/common/imagelib/img_bmp.c @@ -25,7 +25,7 @@ Image_LoadBMP qboolean Image_LoadBMP( const char *name, const byte *buffer, fs_offset_t filesize ) { byte *buf_p, *pixbuf; - rgba_t palette[256]; + rgba_t palette[256] = { 0 }; int i, columns, column, rows, row, bpp = 1; int cbPalBytes = 0, padSize = 0, bps = 0; int reflectivity[3] = { 0, 0, 0 }; From be084d5603b55a45c1c5351b749cd49fd077a41b Mon Sep 17 00:00:00 2001 From: Alibek Omarov Date: Tue, 31 Jan 2023 00:50:48 +0300 Subject: [PATCH 389/490] engine: crashhandler: fully initialize struct sigaction --- engine/common/crashhandler.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/engine/common/crashhandler.c b/engine/common/crashhandler.c index 871da5fc..d47e5489 100644 --- a/engine/common/crashhandler.c +++ b/engine/common/crashhandler.c @@ -440,7 +440,7 @@ static void Sys_Crash( int signal, siginfo_t *si, void *context) void Sys_SetupCrashHandler( void ) { - struct sigaction act; + struct sigaction act = { 0 }; act.sa_sigaction = Sys_Crash; act.sa_flags = SA_SIGINFO | SA_ONSTACK; sigaction( SIGSEGV, &act, &oldFilter ); From 4f78ec01cf39fda45bd190309f53ce12724b7413 Mon Sep 17 00:00:00 2001 From: Alibek Omarov Date: Tue, 31 Jan 2023 04:14:18 +0300 Subject: [PATCH 390/490] engine: network: do not read from uninitialized sockaddr storage --- engine/common/net_ws.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/engine/common/net_ws.c b/engine/common/net_ws.c index df569b9b..28a31a25 100644 --- a/engine/common/net_ws.c +++ b/engine/common/net_ws.c @@ -1451,10 +1451,10 @@ static qboolean NET_QueuePacket( netsrc_t sock, netadr_t *from, byte *data, size addr_len = sizeof( addr ); ret = recvfrom( net_socket, buf, sizeof( buf ), 0, (struct sockaddr *)&addr, &addr_len ); - NET_SockadrToNetadr( &addr, from ); - if( !NET_IsSocketError( ret )) { + NET_SockadrToNetadr( &addr, from ); + if( ret < NET_MAX_FRAGMENT ) { // Transfer data From ae662912729d661a2d9a03285117083e32551301 Mon Sep 17 00:00:00 2001 From: Alibek Omarov Date: Tue, 31 Jan 2023 04:28:08 +0300 Subject: [PATCH 391/490] scripts: compiler_optimizations: added MSan target --- scripts/waifulib/compiler_optimizations.py | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/scripts/waifulib/compiler_optimizations.py b/scripts/waifulib/compiler_optimizations.py index 3d653770..a0fd9b87 100644 --- a/scripts/waifulib/compiler_optimizations.py +++ b/scripts/waifulib/compiler_optimizations.py @@ -30,7 +30,7 @@ compiler_optimizations.CFLAGS['gottagofast'] = { } ''' -VALID_BUILD_TYPES = ['fastnative', 'fast', 'release', 'debug', 'sanitize', 'none'] +VALID_BUILD_TYPES = ['fastnative', 'fast', 'release', 'debug', 'sanitize', 'msan', 'none'] LINKFLAGS = { 'common': { @@ -38,6 +38,10 @@ LINKFLAGS = { 'gcc': ['-Wl,--no-undefined'], 'owcc': ['-Wl,option stack=512k'] }, + 'msan': { + 'clang': ['-fsanitize=memory', '-pthread'], + 'default': ['NO_MSAN_HERE'] + }, 'sanitize': { 'clang': ['-fsanitize=undefined', '-fsanitize=address', '-pthread'], 'gcc': ['-fsanitize=undefined', '-fsanitize=address', '-pthread'], @@ -81,6 +85,10 @@ CFLAGS = { 'owcc': ['-O0', '-fno-omit-frame-pointer', '-funwind-tables', '-fno-omit-leaf-frame-pointer'], 'default': ['-O0'] }, + 'msan': { + 'clang': ['-O2', '-g', '-fno-omit-frame-pointer', '-fsanitize=memory', '-pthread'], + 'default': ['NO_MSAN_HERE'] + }, 'sanitize': { 'msvc': ['/Od', '/RTC1', '/Zi', '/fsanitize=address'], 'gcc': ['-O0', '-fsanitize=undefined', '-fsanitize=address', '-pthread'], From eaf9a9283a855a8363a22facd6b0d63daf17d893 Mon Sep 17 00:00:00 2001 From: Alibek Omarov Date: Tue, 31 Jan 2023 04:33:27 +0300 Subject: [PATCH 392/490] Revert "engine: network: do not read from uninitialized sockaddr storage" This reverts commit 4f78ec01cf39fda45bd190309f53ce12724b7413. --- engine/common/net_ws.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/engine/common/net_ws.c b/engine/common/net_ws.c index 28a31a25..df569b9b 100644 --- a/engine/common/net_ws.c +++ b/engine/common/net_ws.c @@ -1451,10 +1451,10 @@ static qboolean NET_QueuePacket( netsrc_t sock, netadr_t *from, byte *data, size addr_len = sizeof( addr ); ret = recvfrom( net_socket, buf, sizeof( buf ), 0, (struct sockaddr *)&addr, &addr_len ); + NET_SockadrToNetadr( &addr, from ); + if( !NET_IsSocketError( ret )) { - NET_SockadrToNetadr( &addr, from ); - if( ret < NET_MAX_FRAGMENT ) { // Transfer data From 2e3788f23dd7637901591abee5a84295c91e8d6e Mon Sep 17 00:00:00 2001 From: Alibek Omarov Date: Tue, 31 Jan 2023 07:14:26 +0300 Subject: [PATCH 393/490] engine: dedicated: add CL_HudMessage to dedicated stubs --- engine/common/dedicated.c | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/engine/common/dedicated.c b/engine/common/dedicated.c index 6f8ef3a5..a7d4ba8b 100644 --- a/engine/common/dedicated.c +++ b/engine/common/dedicated.c @@ -257,4 +257,9 @@ void CL_Crashed( void ) { } +void CL_HudMessage( const char *pMessage ) +{ + +} + #endif // XASH_DEDICATED From f7d4e5a2ea2e2a2bd66b3b5b4ee272983d73e4dd Mon Sep 17 00:00:00 2001 From: Alibek Omarov Date: Tue, 31 Jan 2023 07:16:08 +0300 Subject: [PATCH 394/490] engine: server: don't show GAMESAVED message in autosaves, small refactoring --- engine/server/server.h | 2 +- engine/server/sv_cmds.c | 9 +++++++-- engine/server/sv_save.c | 25 ++++++++++--------------- 3 files changed, 18 insertions(+), 18 deletions(-) diff --git a/engine/server/server.h b/engine/server/server.h index 0ca8ee4e..1f0ba150 100644 --- a/engine/server/server.h +++ b/engine/server/server.h @@ -660,7 +660,7 @@ void SV_SetLogAddress_f( void ); // // sv_save.c // -void SV_SaveGame( const char *pName ); +qboolean SV_SaveGame( const char *pName ); qboolean SV_LoadGame( const char *pName ); int SV_LoadGameState( char const *level ); void SV_ChangeLevel( qboolean loadfromsavedgame, const char *mapname, const char *start, qboolean background ); diff --git a/engine/server/sv_cmds.c b/engine/server/sv_cmds.c index b622d766..d4b733f2 100644 --- a/engine/server/sv_cmds.c +++ b/engine/server/sv_cmds.c @@ -439,18 +439,23 @@ SV_Save_f */ void SV_Save_f( void ) { + qboolean ret = false; + switch( Cmd_Argc( )) { case 1: - SV_SaveGame( "new" ); + ret = SV_SaveGame( "new" ); break; case 2: - SV_SaveGame( Cmd_Argv( 1 )); + ret = SV_SaveGame( Cmd_Argv( 1 )); break; default: Con_Printf( S_USAGE "save \n" ); break; } + + if( ret && CL_Active() && !FBitSet( host.features, ENGINE_QUAKE_COMPATIBLE )) + CL_HudMessage( "GAMESAVED" ); // defined in titles.txt } /* diff --git a/engine/server/sv_save.c b/engine/server/sv_save.c index 08645645..2c9da0cf 100644 --- a/engine/server/sv_save.c +++ b/engine/server/sv_save.c @@ -1693,7 +1693,7 @@ SaveGameSlot do a save game ============= */ -static int SaveGameSlot( const char *pSaveName, const char *pSaveComment ) +static qboolean SaveGameSlot( const char *pSaveName, const char *pSaveComment ) { char hlPath[MAX_QPATH]; char name[MAX_QPATH]; @@ -1704,7 +1704,7 @@ static int SaveGameSlot( const char *pSaveName, const char *pSaveComment ) file_t *pFile; pSaveData = SaveGameState( false ); - if( !pSaveData ) return 0; + if( !pSaveData ) return false; SaveFinish( pSaveData ); pSaveData = SaveInit( SAVE_HEAPSIZE, SAVE_HASHSTRINGS ); // re-init the buffer @@ -1735,7 +1735,7 @@ static int SaveGameSlot( const char *pSaveName, const char *pSaveComment ) { // something bad is happens SaveFinish( pSaveData ); - return 0; + return false; } // pending the preview image for savegame @@ -1759,7 +1759,7 @@ static int SaveGameSlot( const char *pSaveName, const char *pSaveComment ) SaveFinish( pSaveData ); FS_Close( pFile ); - return 1; + return true; } /* @@ -2169,17 +2169,17 @@ qboolean SV_LoadGame( const char *pPath ) SV_SaveGame ================== */ -void SV_SaveGame( const char *pName ) +qboolean SV_SaveGame( const char *pName ) { char comment[80]; - int result; string savename; if( !COM_CheckString( pName )) - return; + return false; // can we save at this point? - if( !IsValidSave( )) return; + if( !IsValidSave( )) + return false; if( !Q_stricmp( pName, "new" )) { @@ -2197,7 +2197,7 @@ void SV_SaveGame( const char *pName ) if( n == 1000 ) { Con_Printf( S_ERROR "no free slots for savegame\n" ); - return; + return false; } } else Q_strncpy( savename, pName, sizeof( savename )); @@ -2208,12 +2208,7 @@ void SV_SaveGame( const char *pName ) #endif // XASH_DEDICATED SaveBuildComment( comment, sizeof( comment )); - result = SaveGameSlot( savename, comment ); - -#if !XASH_DEDICATED - if( result && !FBitSet( host.features, ENGINE_QUAKE_COMPATIBLE )) - CL_HudMessage( "GAMESAVED" ); // defined in titles.txt -#endif // XASH_DEDICATED + return SaveGameSlot( savename, comment ); } /* From d7af50ea6c3cb7a0c818e88360e0bf8bc733c559 Mon Sep 17 00:00:00 2001 From: Alibek Omarov Date: Tue, 31 Jan 2023 07:18:23 +0300 Subject: [PATCH 395/490] engine: ref: remove direction vectors from Ref API, renderers calculate them from viewangles to local data anyway --- engine/client/ref_common.c | 1 - engine/ref_api.h | 1 - 2 files changed, 2 deletions(-) diff --git a/engine/client/ref_common.c b/engine/client/ref_common.c index b2b51a66..71cbd87c 100644 --- a/engine/client/ref_common.c +++ b/engine/client/ref_common.c @@ -55,7 +55,6 @@ void GL_RenderFrame( const ref_viewpass_t *rvp ) VectorCopy( rvp->vieworigin, refState.vieworg ); VectorCopy( rvp->viewangles, refState.viewangles ); - AngleVectors( refState.viewangles, refState.vforward, refState.vright, refState.vup ); ref.dllFuncs.GL_RenderFrame( rvp ); } diff --git a/engine/ref_api.h b/engine/ref_api.h index 427b3f05..3c14b719 100644 --- a/engine/ref_api.h +++ b/engine/ref_api.h @@ -102,7 +102,6 @@ typedef struct ref_globals_s vec3_t vieworg; vec3_t viewangles; - vec3_t vforward, vright, vup; // todo: fill this without engine help // move to local From ced6e8869ab453727d372018e5a2bb014cbdd46e Mon Sep 17 00:00:00 2001 From: Alibek Omarov Date: Wed, 1 Feb 2023 04:55:21 +0300 Subject: [PATCH 396/490] ref: fix chrome texture being misaligned --- ref/gl/gl_studio.c | 12 ++++++------ ref/soft/r_studio.c | 12 ++++++------ 2 files changed, 12 insertions(+), 12 deletions(-) diff --git a/ref/gl/gl_studio.c b/ref/gl/gl_studio.c index 0f32b8d7..af117914 100644 --- a/ref/gl/gl_studio.c +++ b/ref/gl/gl_studio.c @@ -1260,18 +1260,18 @@ void R_StudioSetupChrome( float *pchrome, int bone, vec3_t normal ) vec3_t tmp; // vector pointing at bone in world reference frame VectorNegate( g_studio.chrome_origin, tmp ); - tmp[0] += g_studio.bonestransform[bone][0][3]; - tmp[1] += g_studio.bonestransform[bone][1][3]; - tmp[2] += g_studio.bonestransform[bone][2][3]; + tmp[0] += g_studio.lighttransform[bone][0][3]; + tmp[1] += g_studio.lighttransform[bone][1][3]; + tmp[2] += g_studio.lighttransform[bone][2][3]; VectorNormalize( tmp ); CrossProduct( tmp, RI.vright, chromeupvec ); VectorNormalize( chromeupvec ); - CrossProduct( tmp, chromeupvec, chromerightvec ); + CrossProduct( chromeupvec, tmp, chromerightvec ); VectorNormalize( chromerightvec ); - Matrix3x4_VectorIRotate( g_studio.bonestransform[bone], chromeupvec, g_studio.chromeup[bone] ); - Matrix3x4_VectorIRotate( g_studio.bonestransform[bone], chromerightvec, g_studio.chromeright[bone] ); + Matrix3x4_VectorIRotate( g_studio.lighttransform[bone], chromeupvec, g_studio.chromeup[bone] ); + Matrix3x4_VectorIRotate( g_studio.lighttransform[bone], chromerightvec, g_studio.chromeright[bone] ); g_studio.chromeage[bone] = g_studio.framecount; } diff --git a/ref/soft/r_studio.c b/ref/soft/r_studio.c index 3aed2ce6..a678715b 100644 --- a/ref/soft/r_studio.c +++ b/ref/soft/r_studio.c @@ -1256,18 +1256,18 @@ void R_StudioSetupChrome( float *pchrome, int bone, vec3_t normal ) vec3_t tmp; // vector pointing at bone in world reference frame VectorNegate( g_studio.chrome_origin, tmp ); - tmp[0] += g_studio.bonestransform[bone][0][3]; - tmp[1] += g_studio.bonestransform[bone][1][3]; - tmp[2] += g_studio.bonestransform[bone][2][3]; + tmp[0] += g_studio.lighttransform[bone][0][3]; + tmp[1] += g_studio.lighttransform[bone][1][3]; + tmp[2] += g_studio.lighttransform[bone][2][3]; VectorNormalize( tmp ); CrossProduct( tmp, RI.vright, chromeupvec ); VectorNormalize( chromeupvec ); - CrossProduct( tmp, chromeupvec, chromerightvec ); + CrossProduct( chromeupvec, tmp, chromerightvec ); VectorNormalize( chromerightvec ); - Matrix3x4_VectorIRotate( g_studio.bonestransform[bone], chromeupvec, g_studio.chromeup[bone] ); - Matrix3x4_VectorIRotate( g_studio.bonestransform[bone], chromerightvec, g_studio.chromeright[bone] ); + Matrix3x4_VectorIRotate( g_studio.lighttransform[bone], chromeupvec, g_studio.chromeup[bone] ); + Matrix3x4_VectorIRotate( g_studio.lighttransform[bone], chromerightvec, g_studio.chromeright[bone] ); g_studio.chromeage[bone] = g_studio.framecount; } From f3ff942ea900b1b51b2e3d7f8a82996291a0e7c4 Mon Sep 17 00:00:00 2001 From: Alibek Omarov Date: Wed, 1 Feb 2023 04:58:12 +0300 Subject: [PATCH 397/490] scripts: flatpak: add basic i386 flatpak script --- scripts/flatpak/run.sh | 20 +++++ .../flatpak/su.xash.Engine.Compat.i386.yml | 82 +++++++++++++++++++ 2 files changed, 102 insertions(+) create mode 100644 scripts/flatpak/run.sh create mode 100644 scripts/flatpak/su.xash.Engine.Compat.i386.yml diff --git a/scripts/flatpak/run.sh b/scripts/flatpak/run.sh new file mode 100644 index 00000000..725161c5 --- /dev/null +++ b/scripts/flatpak/run.sh @@ -0,0 +1,20 @@ +#!/bin/sh + +echo "Xash3D FWGS installed as Flatpak." + +# TODO: detect by libraryfolders.vdf and installed apps +HALFLIFESTEAMDIR="$HOME/.steam/steam/steamapps/common/Half-Life" + +if [ -d "$HALFLIFESTEAMDIR" ]; then + echo "Detected Half-Life installation in $HALFLIFESTEAMDIR, using as RoDir" + export XASH3D_RODIR=$HALFLIFESTEAMDIR +fi + +XASHDATADIR="$HOME/.xash/" + +mkdir -p $XASHDATADIR +export XASH3D_BASEDIR="$XASHDATADIR" +echo "Base directory is $XASH3D_BASEDIR" + +export XASH3D_EXTRAS_PAK1=/app/share/xash3d/valve/extras.pk3 +exec /app/lib32/xash3d/xash3d "$@" diff --git a/scripts/flatpak/su.xash.Engine.Compat.i386.yml b/scripts/flatpak/su.xash.Engine.Compat.i386.yml new file mode 100644 index 00000000..a2ac1c75 --- /dev/null +++ b/scripts/flatpak/su.xash.Engine.Compat.i386.yml @@ -0,0 +1,82 @@ +app-id: su.xash.Engine.Compat.i386 +runtime: org.freedesktop.Platform +runtime-version: &runtime-version '22.08' +x-gl-version: &gl-version '1.4' +x-gl-versions: &gl-versions 22.08;1.4 +sdk: org.freedesktop.Sdk +command: run.sh +separate-locales: false +sdk-extensions: + - org.freedesktop.Sdk.Compat.i386 + - org.freedesktop.Sdk.Extension.toolchain-i386 + +finish-args: + - --share=ipc + - --socket=wayland + - --socket=x11 + - --socket=pulseaudio + - --share=network + - --filesystem=~/.steam:ro + - --filesystem=~/.xash:rw + - --filesystem=xdg-run/app/com.discordapp.Discord:create + - --device=all + - --allow=multiarch + - --allow=devel + - --allow=bluetooth + +add-extensions: + org.freedesktop.Platform.Compat.i386: + directory: lib/i386-linux-gnu + version: *runtime-version + + org.freedesktop.Platform.Compat.i386.Debug: + directory: lib/debug/lib/i386-linux-gnu + version: *runtime-version + no-autodownload: true + + org.freedesktop.Platform.GL32: + directory: lib/i386-linux-gnu/GL + version: *gl-version + versions: *gl-versions + subdirectories: true + autodelete: false + add-ld-path: lib + merge-dirs: vulkan/icd.d;glvnd/egl_vendor.d;OpenCL/vendors;lib/dri;lib/d3d;vulkan/explicit_layer.d;vulkan/implicit_layer.d + download-if: active-gl-driver + enable-if: active-gl-driver + +x-compat-i386-opts: &compat-i386-opts + prepend-pkg-config-path: /app/lib32/pkgconfig:/usr/lib/i386-linux-gnu/pkgconfig + ldflags: -L/app/lib32 + prepend-path: /usr/lib/sdk/toolchain-i386/bin + env: + CC: i686-unknown-linux-gnu-gcc + CXX: i686-unknown-linux-gnu-g++ + libdir: /app/lib32 + +modules: + - name: bundle-setup + buildsystem: simple + build-commands: + - | + mkdir -p /app/lib/i386-linux-gnu + mkdir -p /app/lib/debug/lib/i386-linux-gnu + mkdir -p /app/lib/i386-linux-gnu/GL + install -Dm644 ld.so.conf /app/etc/ld.so.conf + sources: + - type: file + dest-filename: ld.so.conf + url: data:/app/lib32%0A/app/lib/i386-linux-gnu%0A + - name: xash + buildsystem: simple + build-options: *compat-i386-opts + build-commands: + - | + python waf configure -T release --enable-all-renderers --enable-packaging --prefix=/app --libdir=/app/lib32 + python waf build + python waf install --destdir=/ + mkdir -p /app/bin + install -m 0755 scripts/flatpak/run.sh /app/bin/run.sh + sources: + - type: dir + path: ../../ From 92138428c555ee373d463cd7cecb455c6ab24883 Mon Sep 17 00:00:00 2001 From: Alibek Omarov Date: Wed, 1 Feb 2023 06:42:31 +0300 Subject: [PATCH 398/490] engine: touch: don't emulate touch and mouse through SDL2 (taken from @Velaron branch) --- engine/client/in_touch.c | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/engine/client/in_touch.c b/engine/client/in_touch.c index 1dcb433b..b3cd2b3a 100644 --- a/engine/client/in_touch.c +++ b/engine/client/in_touch.c @@ -1084,8 +1084,13 @@ void Touch_Init( void ) Cvar_RegisterVariable( &touch_enable ); Cvar_RegisterVariable( &touch_emulate ); - /// TODO: touch sdl platform - // SDL_SetHint( SDL_HINT_ANDROID_SEPARATE_MOUSE_AND_TOUCH, "1" ); + // TODO: touch platform +#if SDL_VERSION_ATLEAST( 2, 0, 10 ) + SDL_SetHint( SDL_HINT_MOUSE_TOUCH_EVENTS, "0" ); + SDL_SetHint( SDL_HINT_TOUCH_MOUSE_EVENTS, "0" ); +#else + SDL_SetHint( SDL_HINT_ANDROID_SEPARATE_MOUSE_AND_TOUCH, "1" ); +#endif touch.initialized = true; } From d5fe491c144dc7ac6e79a469d9b6b19c269bc2a9 Mon Sep 17 00:00:00 2001 From: Alibek Omarov Date: Thu, 2 Feb 2023 02:49:18 +0300 Subject: [PATCH 399/490] engine: client: add a little auto-disconnect message in case of server timeout --- engine/client/cl_main.c | 1 + 1 file changed, 1 insertion(+) diff --git a/engine/client/cl_main.c b/engine/client/cl_main.c index 710988cf..97862fd3 100644 --- a/engine/client/cl_main.c +++ b/engine/client/cl_main.c @@ -752,6 +752,7 @@ void CL_WritePacket( void ) if(( host.realtime - cls.netchan.last_received ) > CONNECTION_PROBLEM_TIME ) { Con_NPrintf( 1, "^3Warning:^1 Connection Problem^7\n" ); + Con_NPrintf( 2, "^1Auto-disconnect in %.1f seconds^7", cl_timeout->value - ( host.realtime - cls.netchan.last_received )); cl.validsequence = 0; } } From d7116afc92f55a116f1b59cf20c473596f0700f8 Mon Sep 17 00:00:00 2001 From: Alibek Omarov Date: Thu, 2 Feb 2023 04:57:53 +0300 Subject: [PATCH 400/490] engine: client: add hud_fontscale cvar to control HUD font scaling (not wired to any logic yet) --- engine/client/cl_main.c | 2 ++ engine/client/client.h | 1 + 2 files changed, 3 insertions(+) diff --git a/engine/client/cl_main.c b/engine/client/cl_main.c index 97862fd3..fe237168 100644 --- a/engine/client/cl_main.c +++ b/engine/client/cl_main.c @@ -57,6 +57,7 @@ convar_t *cl_nosmooth; convar_t *cl_smoothtime; convar_t *cl_clockreset; convar_t *cl_fixtimerate; +convar_t *hud_fontscale; convar_t *hud_scale; convar_t *cl_solid_players; convar_t *cl_draw_beams; @@ -2904,6 +2905,7 @@ void CL_InitLocal( void ) cl_bmodelinterp = Cvar_Get( "cl_bmodelinterp", "1", FCVAR_ARCHIVE, "enable bmodel interpolation" ); cl_clockreset = Cvar_Get( "cl_clockreset", "0.1", FCVAR_ARCHIVE, "frametime delta maximum value before reset" ); cl_fixtimerate = Cvar_Get( "cl_fixtimerate", "7.5", FCVAR_ARCHIVE, "time in msec to client clock adjusting" ); + hud_fontscale = Cvar_Get( "hud_fontscale", "1.0", FCVAR_ARCHIVE|FCVAR_LATCH, "scale hud font texture" ); hud_scale = Cvar_Get( "hud_scale", "0", FCVAR_ARCHIVE|FCVAR_LATCH, "scale hud at current resolution" ); Cvar_Get( "cl_background", "0", FCVAR_READ_ONLY, "indicate what background map is running" ); cl_showevents = Cvar_Get( "cl_showevents", "0", FCVAR_ARCHIVE, "show events playback" ); diff --git a/engine/client/client.h b/engine/client/client.h index 1883ab9a..ba84a9ab 100644 --- a/engine/client/client.h +++ b/engine/client/client.h @@ -671,6 +671,7 @@ extern convar_t *cl_levelshot_name; extern convar_t *cl_draw_beams; extern convar_t *cl_clockreset; extern convar_t *cl_fixtimerate; +extern convar_t *hud_fontscale; extern convar_t *hud_scale; extern convar_t *gl_showtextures; extern convar_t *cl_bmodelinterp; From 402a0f129dce2a71b70745be5ade1c4aa0267d99 Mon Sep 17 00:00:00 2001 From: Alibek Omarov Date: Fri, 3 Feb 2023 08:50:41 +0300 Subject: [PATCH 401/490] engine: platform: sdl: use SDL joystick rumble for Platform_Vibrate --- engine/platform/sdl/in_sdl.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/engine/platform/sdl/in_sdl.c b/engine/platform/sdl/in_sdl.c index 65282890..3957b2e9 100644 --- a/engine/platform/sdl/in_sdl.c +++ b/engine/platform/sdl/in_sdl.c @@ -122,7 +122,8 @@ Platform_Vibrate */ void Platform_Vibrate( float time, char flags ) { - // stub + if( g_joy ) + SDL_JoystickRumble( g_joy, 0xFFFF, 0xFFFF, time * 1000.0f ); } /* From 77ea03a62c6925a49192a516489a17aa6eeafab3 Mon Sep 17 00:00:00 2001 From: Alibek Omarov Date: Fri, 3 Feb 2023 08:51:16 +0300 Subject: [PATCH 402/490] engine: client: introduce bare-bones font manager and text drawing manager * wire hud_fontscale so HUD font scaling can be used independently from hud_scale * allow small optimizatinons, like optional UTF-8 decoding, or not calling SetRenderMode for each character * even less copypasted code in text drawing between client code and console * get rid of direct DrawCharacter calls when it can be just DrawString * fix net_speeds, r_speeds with scaled console fonts * try to fix MobilityAPI's pfnDrawCharacterScaled * center keyboard keys in OSK code --- engine/client/cl_font.c | 291 ++++++++++++++++++++++++++++ engine/client/cl_game.c | 137 +++++-------- engine/client/cl_gameui.c | 22 +-- engine/client/cl_mobile.c | 37 ++-- engine/client/cl_netgraph.c | 20 +- engine/client/cl_scrn.c | 47 +---- engine/client/client.h | 50 +++-- engine/client/console.c | 372 ++++-------------------------------- engine/client/keys.c | 15 +- 9 files changed, 469 insertions(+), 522 deletions(-) create mode 100644 engine/client/cl_font.c diff --git a/engine/client/cl_font.c b/engine/client/cl_font.c new file mode 100644 index 00000000..3445f40d --- /dev/null +++ b/engine/client/cl_font.c @@ -0,0 +1,291 @@ +/* +cl_font.c - bare bones engine font manager +Copyright (C) 2023 Alibek Omarov + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. +*/ + +#include "common.h" +#include "filesystem.h" +#include "client.h" +#include "qfont.h" + +qboolean CL_FixedFont( cl_font_t *font ) +{ + return font && font->valid && font->type == FONT_FIXED; +} + +static int CL_LoadFontTexture( const char *fontname, uint texFlags, int *width ) +{ + int font_width; + int tex; + + if( !g_fsapi.FileExists( fontname, false )) + return 0; + + tex = ref.dllFuncs.GL_LoadTexture( fontname, NULL, 0, texFlags ); + if( !tex ) + return 0; + + font_width = REF_GET_PARM( PARM_TEX_WIDTH, tex ); + if( !font_width ) + { + ref.dllFuncs.GL_FreeTexture( tex ); + return 0; + } + + *width = font_width; + return tex; +} + +qboolean Con_LoadFixedWidthFont( const char *fontname, cl_font_t *font, float scale, int rendermode, uint texFlags ) +{ + int font_width, i; + + if( font->valid ) + return true; // already loaded + + font->hFontTexture = CL_LoadFontTexture( fontname, texFlags, &font_width ); + if( !font->hFontTexture ) + return false; + + font->type = FONT_FIXED; + font->valid = true; + font->scale = scale; + font->nearest = FBitSet( texFlags, TF_NEAREST ); + font->rendermode = rendermode; + font->charHeight = Q_rint( font_width / 16 * scale ); + + for( i = 0; i < ARRAYSIZE( font->fontRc ); i++ ) + { + font->fontRc[i].left = ( i * font_width / 16 ) % font_width; + font->fontRc[i].right = font->fontRc[i].left + font_width / 16; + font->fontRc[i].top = ( i / 16 ) * ( font_width / 16 ); + font->fontRc[i].bottom = font->fontRc[i].top + font_width / 16; + + font->charWidths[i] = Q_rint( font_width / 16 * scale ); + } + + return true; +} + +qboolean Con_LoadVariableWidthFont( const char *fontname, cl_font_t *font, float scale, int rendermode, uint texFlags ) +{ + qfont_t src; + file_t *fd; + int font_width, i; + + if( font->valid ) + return true; + + fd = g_fsapi.Open( fontname, "r", false ); + if( !fd ) + return false; + + if( g_fsapi.Read( fd, &src, sizeof( qfont_t )) != sizeof( qfont_t )) + { + g_fsapi.Close( fd ); + return false; + } + + g_fsapi.Close( fd ); + + font->hFontTexture = CL_LoadFontTexture( fontname, texFlags, &font_width ); + if( !font->hFontTexture ) + return false; + + font->type = FONT_VARIABLE; + font->valid = true; + font->scale = scale; + font->nearest = FBitSet( texFlags, TF_NEAREST ); + font->rendermode = rendermode; + font->charHeight = Q_rint( src.rowheight * scale ); + + for( i = 0; i < ARRAYSIZE( font->fontRc ); i++ ) + { + const charinfo *ci = &src.fontinfo[i]; + + font->fontRc[i].left = (word)ci->startoffset % font_width; + font->fontRc[i].right = font->fontRc[i].left + ci->charwidth; + font->fontRc[i].top = (word)ci->startoffset / font_width; + font->fontRc[i].bottom = font->fontRc[i].top + src.rowheight; + + font->charWidths[i] = Q_rint( src.fontinfo[i].charwidth * scale ); + } + + return true; +} + +void CL_FreeFont( cl_font_t *font ) +{ + if( !font || !font->valid ) + return; + + ref.dllFuncs.GL_FreeTexture( font->hFontTexture ); + memset( font, 0, sizeof( *font )); +} + +int CL_DrawCharacter( float x, float y, int number, rgba_t color, cl_font_t *font, int flags ) +{ + wrect_t *rc; + float w, h; + float s1, t1, s2, t2, half = 0.5f; + int texw, texh; + + if( !font || !font->valid || y < -font->charHeight ) + return 0; + + if( FBitSet( flags, FONT_DRAW_UTF8 )) + number = Con_UtfProcessChar( number & 255 ); + else number &= 255; + + if( !number || !font->charWidths[number]) + return 0; + + R_GetTextureParms( &texw, &texh, font->hFontTexture ); + if( !texw || !texh ) + return 0; + + rc = &font->fontRc[number]; + if( font->nearest ) + half = 0; + + s1 = ((float)rc->left + half ) / texw; + t1 = ((float)rc->top + half ) / texh; + s2 = ((float)rc->right - half ) / texw; + t2 = ((float)rc->bottom - half ) / texh; + w = ( rc->right - rc->left ) * font->scale; + h = ( rc->bottom - rc->top ) * font->scale; + + if( FBitSet( flags, FONT_DRAW_HUD )) + SPR_AdjustSize( &x, &y, &w, &h ); + + if( !FBitSet( flags, FONT_DRAW_NORENDERMODE )) + ref.dllFuncs.GL_SetRenderMode( font->rendermode ); + + // don't apply color to fixed fonts it's already colored + if( font->type != FONT_FIXED || REF_GET_PARM( PARM_TEX_GLFORMAT, font->hFontTexture ) == 0x8045 ) // GL_LUMINANCE8_ALPHA8 + ref.dllFuncs.Color4ub( color[0], color[1], color[2], color[3] ); + else ref.dllFuncs.Color4ub( 255, 255, 255, color[3] ); + ref.dllFuncs.R_DrawStretchPic( x, y, w, h, s1, t1, s2, t2, font->hFontTexture ); + ref.dllFuncs.Color4ub( 255, 255, 255, 255 ); // don't forget reset color + + return font->charWidths[number]; +} + +int CL_DrawString( float x, float y, const char *s, rgba_t color, cl_font_t *font, int flags ) +{ + rgba_t current_color; + int draw_len = 0; + + if( !font || !font->valid ) + return 0; + + if( FBitSet( flags, FONT_DRAW_UTF8 )) + Con_UtfProcessChar( 0 ); // clear utf state + + if( !FBitSet( flags, FONT_DRAW_NORENDERMODE )) + ref.dllFuncs.GL_SetRenderMode( font->rendermode ); + + Vector4Copy( color, current_color ); + + while( *s ) + { + if( *s == '\n' ) + { + s++; + + if( !*s ) + break; + + draw_len = 0; + y += font->charHeight; + } + + if( IsColorString( s )) + { + if( FBitSet( flags, FONT_DRAW_FORCECOL )) + VectorCopy( g_color_table[ColorIndex(*( s + 1 ))], current_color ); + + s += 2; + continue; + } + + // skip setting rendermode, it was changed for this string already + draw_len += CL_DrawCharacter( x + draw_len, y, (byte)*s, color, font, flags | FONT_DRAW_NORENDERMODE ); + + s++; + } + + return draw_len; +} + +void CL_DrawCharacterLen( cl_font_t *font, int number, int *width, int *height ) +{ + if( !font || !font->valid ) return; + if( width ) *width = font->charWidths[number & 255]; + if( height ) *height = font->charHeight; +} + +void CL_DrawStringLen( cl_font_t *font, const char *s, int *width, int *height, int flags ) +{ + int draw_len = 0; + + if( !font || !font->valid ) + return; + + if( height ) + *height = font->charHeight; + + if( width ) + *width = 0; + + if( !COM_CheckString( s )) + return; + + if( FBitSet( flags, FONT_DRAW_UTF8 )) + Con_UtfProcessChar( 0 ); // reset utf state + + while( *s ) + { + int number; + + if( *s == '\n' ) + { + // BUG: no check for end string here + // but high chances somebody's relying on this + s++; + draw_len = 0; + if( height ) + *height += font->charHeight; + } + + if( IsColorString( s )) + { + s += 2; + continue; + } + + if( FBitSet( flags, FONT_DRAW_UTF8 )) + number = Con_UtfProcessChar( (byte)*s ); + else number = (byte)*s; + + if( number ) + { + draw_len += font->charWidths[*s]; + + if( draw_len > *width ) + *width = draw_len; + } + + s++; + } +} diff --git a/engine/client/cl_game.c b/engine/client/cl_game.c index 3e65c516..a1549f4a 100644 --- a/engine/client/cl_game.c +++ b/engine/client/cl_game.c @@ -306,9 +306,7 @@ print centerscreen message */ void CL_CenterPrint( const char *text, float y ) { - int length = 0; - int width = 0; - char *s; + cl_font_t *font = Con_GetCurFont(); if( !COM_CheckString( text )) return; @@ -317,24 +315,13 @@ void CL_CenterPrint( const char *text, float y ) clgame.centerPrint.totalWidth = 0; clgame.centerPrint.time = cl.mtime[0]; // allow pause for centerprint Q_strncpy( clgame.centerPrint.message, text, sizeof( clgame.centerPrint.message )); - s = clgame.centerPrint.message; - // count the number of lines for centering - while( *s ) - { - if( *s == '\n' ) - { - clgame.centerPrint.lines++; - if( width > clgame.centerPrint.totalWidth ) - clgame.centerPrint.totalWidth = width; - width = 0; - } - else width += clgame.scrInfo.charWidths[*s]; - s++; - length++; - } + CL_DrawStringLen( font, + clgame.centerPrint.message, + &clgame.centerPrint.totalWidth, + &clgame.centerPrint.totalHeight, + FONT_DRAW_HUD | FONT_DRAW_UTF8 ); - clgame.centerPrint.totalHeight = ( clgame.centerPrint.lines * clgame.scrInfo.iCharHeight ); clgame.centerPrint.y = CL_AdjustYPos( y, clgame.centerPrint.totalHeight ); } @@ -502,6 +489,7 @@ called each frame */ void CL_DrawCenterPrint( void ) { + cl_font_t *font = Con_GetCurFont(); char *pText; int i, j, x, y; int width, lineLength; @@ -521,8 +509,10 @@ void CL_DrawCenterPrint( void ) y = clgame.centerPrint.y; // start y colorDefault = g_color_table[7]; pText = clgame.centerPrint.message; - Con_DrawCharacterLen( 0, NULL, &charHeight ); + CL_DrawCharacterLen( font, 0, NULL, &charHeight ); + + ref.dllFuncs.GL_SetRenderMode( font->rendermode ); for( i = 0; i < clgame.centerPrint.lines; i++ ) { lineLength = 0; @@ -532,7 +522,7 @@ void CL_DrawCenterPrint( void ) { byte c = *pText; line[lineLength] = c; - Con_DrawCharacterLen( c, &charWidth, NULL ); + CL_DrawCharacterLen( font, c, &charWidth, NULL ); width += charWidth; lineLength++; pText++; @@ -549,7 +539,7 @@ void CL_DrawCenterPrint( void ) for( j = 0; j < lineLength; j++ ) { if( x >= 0 && y >= 0 && x <= refState.width ) - x += Con_DrawCharacter( x, y, line[j], colorDefault ); + x += CL_DrawCharacter( x, y, line[j], colorDefault, font, FONT_DRAW_UTF8 | FONT_DRAW_HUD | FONT_DRAW_NORENDERMODE ); } y += charHeight; } @@ -1600,6 +1590,14 @@ int GAME_EXPORT CL_GetScreenInfo( SCREENINFO *pscrinfo ) { float scale_factor = hud_scale->value; + if( FBitSet( hud_fontscale->flags, FCVAR_CHANGED )) + { + CL_FreeFont( &cls.creditsFont ); + SCR_LoadCreditsFont(); + + ClearBits( hud_fontscale->flags, FCVAR_CHANGED ); + } + // setup screen info clgame.scrInfo.iSize = sizeof( clgame.scrInfo ); clgame.scrInfo.iFlags = SCRINFO_SCREENFLASH; @@ -1844,24 +1842,13 @@ returns drawed chachter width (in real screen pixels) */ static int GAME_EXPORT pfnDrawCharacter( int x, int y, int number, int r, int g, int b ) { - if( !cls.creditsFont.valid ) - return 0; + rgba_t color = { r, g, b, 255 }; + int flags = FONT_DRAW_HUD; if( hud_utf8->value ) - number = Con_UtfProcessChar( number ); + flags |= FONT_DRAW_UTF8; - number &= 255; - - if( number < 32 ) return 0; - if( y < -clgame.scrInfo.iCharHeight ) - return 0; - - clgame.ds.adjust_size = true; - pfnPIC_Set( cls.creditsFont.hFontTexture, r, g, b, 255 ); - pfnPIC_DrawAdditive( x, y, -1, -1, &cls.creditsFont.fontRc[number] ); - clgame.ds.adjust_size = false; - - return clgame.scrInfo.charWidths[number]; + return CL_DrawCharacter( x, y, number, color, &cls.creditsFont, flags ); } /* @@ -1873,20 +1860,12 @@ drawing string like a console string */ int GAME_EXPORT pfnDrawConsoleString( int x, int y, char *string ) { - int drawLen; + cl_font_t *font = Con_GetFont( con_fontsize->value ); + rgba_t color; + Vector4Copy( clgame.ds.textColor, color ); + Vector4Set( clgame.ds.textColor, 255, 255, 255, 255 ); - if( !COM_CheckString( string )) - return 0; // silent ignore - Con_SetFont( con_fontsize->value ); - - clgame.ds.adjust_size = true; - drawLen = Con_DrawString( x, y, string, clgame.ds.textColor ); - MakeRGBA( clgame.ds.textColor, 255, 255, 255, 255 ); - clgame.ds.adjust_size = false; - - Con_RestoreFont(); - - return (x + drawLen); // exclude color prexfixes + return x + CL_DrawString( x, y, string, color, font, FONT_DRAW_UTF8 | FONT_DRAW_HUD ); } /* @@ -1914,9 +1893,9 @@ compute string length in screen pixels */ void GAME_EXPORT pfnDrawConsoleStringLen( const char *pText, int *length, int *height ) { - Con_SetFont( con_fontsize->value ); - Con_DrawStringLen( pText, length, height ); - Con_RestoreFont(); + cl_font_t *font = Con_GetFont( con_fontsize->value ); + + CL_DrawStringLen( font, pText, length, height, FONT_DRAW_UTF8 | FONT_DRAW_HUD ); } /* @@ -2839,23 +2818,7 @@ pfnVGUI2DrawCharacter */ static int GAME_EXPORT pfnVGUI2DrawCharacter( int x, int y, int number, unsigned int font ) { - if( !cls.creditsFont.valid ) - return 0; - - number &= 255; - - number = Con_UtfProcessChar( number ); - - if( number < 32 ) return 0; - if( y < -clgame.scrInfo.iCharHeight ) - return 0; - - clgame.ds.adjust_size = true; - gameui.ds.gl_texturenum = cls.creditsFont.hFontTexture; - pfnPIC_DrawAdditive( x, y, -1, -1, &cls.creditsFont.fontRc[number] ); - clgame.ds.adjust_size = false; - - return clgame.scrInfo.charWidths[number]; + return pfnDrawCharacter( x, y, number, 255, 255, 255 ); } /* @@ -2866,9 +2829,6 @@ pfnVGUI2DrawCharacterAdditive */ static int GAME_EXPORT pfnVGUI2DrawCharacterAdditive( int x, int y, int ch, int r, int g, int b, unsigned int font ) { - if( !hud_utf8->value ) - ch = Con_UtfProcessChar( ch ); - return pfnDrawCharacter( x, y, ch, r, g, b ); } @@ -2880,16 +2840,13 @@ pfnDrawString */ static int GAME_EXPORT pfnDrawString( int x, int y, const char *str, int r, int g, int b ) { - int iWidth = 0; - Con_UtfProcessChar(0); + rgba_t color = { r, g, b, 255 }; + int flags = FONT_DRAW_HUD; - // draw the string until we hit the null character or a newline character - for ( ; *str != 0 && *str != '\n'; str++ ) - { - iWidth += pfnVGUI2DrawCharacterAdditive( x + iWidth, y, (unsigned char)*str, r, g, b, 0 ); - } + if( hud_utf8->value ) + SetBits( flags, FONT_DRAW_UTF8 ); - return iWidth; + return CL_DrawString( x, y, str, color, &cls.creditsFont, flags ); } /* @@ -2900,11 +2857,19 @@ pfnDrawStringReverse */ static int GAME_EXPORT pfnDrawStringReverse( int x, int y, const char *str, int r, int g, int b ) { - // find the end of the string - char *szIt; - for( szIt = (char*)str; *szIt != 0; szIt++ ) - x -= clgame.scrInfo.charWidths[ (unsigned char) *szIt ]; - return pfnDrawString( x, y, str, r, g, b ); + rgba_t color = { r, g, b, 255 }; + int flags = FONT_DRAW_HUD; + int width, height; + + if( hud_utf8->value ) + SetBits( flags, FONT_DRAW_UTF8 ); + + CL_DrawStringLen( &cls.creditsFont, str, &width, &height, flags ); + + x -= width; + y -= height; + + return CL_DrawString( x, y, str, color, &cls.creditsFont, flags ); } /* diff --git a/engine/client/cl_gameui.c b/engine/client/cl_gameui.c index 440afa32..e641cbbd 100644 --- a/engine/client/cl_gameui.c +++ b/engine/client/cl_gameui.c @@ -487,22 +487,10 @@ static void PIC_DrawGeneric( float x, float y, float width, float height, const if( prc ) { // calc user-defined rectangle - s1 = prc->left; - t1 = prc->top; - s2 = prc->right; - t2 = prc->bottom; - - if( clgame.ds.adjust_size ) - { - SPR_AdjustTexCoords( w, h, &s1, &t1, &s2, &t2 ); - } - else - { - s1 /= (float)w; - t1 /= (float)h; - s2 /= (float)w; - t2 /= (float)h; - } + s1 = prc->left / (float)w; + t1 = prc->top / (float)h; + s2 = prc->right / (float)w; + t2 = prc->bottom / (float)h; if( width == -1 && height == -1 ) { @@ -526,8 +514,6 @@ static void PIC_DrawGeneric( float x, float y, float width, float height, const if( gameui.ds.scissor_test && !PIC_Scissor( &x, &y, &width, &height, &s1, &t1, &s2, &t2 )) return; - if( clgame.ds.adjust_size ) - SPR_AdjustSize( &x, &y, &width, &height ); ref.dllFuncs.R_DrawStretchPic( x, y, width, height, s1, t1, s2, t2, gameui.ds.gl_texturenum ); ref.dllFuncs.Color4ub( 255, 255, 255, 255 ); } diff --git a/engine/client/cl_mobile.c b/engine/client/cl_mobile.c index 3e59b85a..db8791c5 100644 --- a/engine/client/cl_mobile.c +++ b/engine/client/cl_mobile.c @@ -25,6 +25,9 @@ mobile_engfuncs_t *gMobileEngfuncs; convar_t *vibration_length; convar_t *vibration_enable; +static cl_font_t g_scaled_font; +static float g_font_scale; + static void pfnVibrate( float life, char flags ) { if( !vibration_enable->value ) @@ -60,28 +63,28 @@ static void pfnEnableTextInput( int enable ) static int pfnDrawScaledCharacter( int x, int y, int number, int r, int g, int b, float scale ) { - int width = clgame.scrInfo.charWidths[number] * scale * hud_scale->value; - int height = clgame.scrInfo.iCharHeight * scale * hud_scale->value; + // this call is very ineffective and possibly broken! + rgba_t color = { r, g, b, 255 }; + int flags = FONT_DRAW_HUD; - if( !cls.creditsFont.valid ) - return 0; + if( hud_utf8->value ) + SetBits( flags, FONT_DRAW_UTF8 ); - x *= hud_scale->value; - y *= hud_scale->value; + if( fabs( g_font_scale - scale ) > 0.1f || + g_scaled_font.hFontTexture != cls.creditsFont.hFontTexture ) + { + int i; - number &= 255; - number = Con_UtfProcessChar( number ); + g_scaled_font = cls.creditsFont; + g_scaled_font.scale *= scale; + g_scaled_font.charHeight *= scale; + for( i = 0; i < ARRAYSIZE( g_scaled_font.charWidths ); i++ ) + g_scaled_font.charWidths[i] *= scale; - if( number < 32 ) - return 0; + g_font_scale = scale; + } - if( y < -height ) - return 0; - - pfnPIC_Set( cls.creditsFont.hFontTexture, r, g, b, 255 ); - pfnPIC_DrawAdditive( x, y, width, height, &cls.creditsFont.fontRc[number] ); - - return width; + return CL_DrawCharacter( x, y, number, color, &g_scaled_font, flags ); } static void *pfnGetNativeObject( const char *obj ) diff --git a/engine/client/cl_netgraph.c b/engine/client/cl_netgraph.c index 51c5a4e5..484adc1b 100644 --- a/engine/client/cl_netgraph.c +++ b/engine/client/cl_netgraph.c @@ -364,6 +364,7 @@ NetGraph_DrawTextFields static void NetGraph_DrawTextFields( int x, int y, int w, wrect_t rect, int count, float avg, int packet_loss, int packet_choke, int graphtype ) { static int lastout; + cl_font_t *font = Con_GetFont( 0 ); rgba_t colors = { 0.9 * 255, 0.9 * 255, 0.7 * 255, 255 }; int ptx = Q_max( x + w - NETGRAPH_LERP_HEIGHT - 1, 1 ); int pty = Q_max( rect.top + rect.bottom - NETGRAPH_LERP_HEIGHT - 3, 1 ); @@ -385,16 +386,17 @@ static void NetGraph_DrawTextFields( int x, int y, int w, wrect_t rect, int coun // move rolling average framerate = FRAMERATE_AVG_FRAC * host.frametime + ( 1.0f - FRAMERATE_AVG_FRAC ) * framerate; - Con_SetFont( 0 ); + + ref.dllFuncs.GL_SetRenderMode( font->rendermode ); if( framerate > 0.0f ) { y -= net_graphheight->value; - Con_DrawString( x, y, va( "%.1f fps" , 1.0f / framerate ), colors ); + CL_DrawString( x, y, va( "%.1f fps" , 1.0f / framerate ), colors, font, FONT_DRAW_NORENDERMODE ); if( avg > 1.0f ) - Con_DrawString( x + 75, y, va( "%i ms" , (int)avg ), colors ); + CL_DrawString( x + 75, y, va( "%i ms" , (int)avg ), colors, font, FONT_DRAW_NORENDERMODE ); y += 15; @@ -402,10 +404,10 @@ static void NetGraph_DrawTextFields( int x, int y, int w, wrect_t rect, int coun if( !out ) out = lastout; else lastout = out; - Con_DrawString( x, y, va( "in : %i %.2f kb/s", netstat_graph[j].msgbytes, cls.netchan.flow[FLOW_INCOMING].avgkbytespersec ), colors ); + CL_DrawString( x, y, va( "in : %i %.2f kb/s", netstat_graph[j].msgbytes, cls.netchan.flow[FLOW_INCOMING].avgkbytespersec ), colors, font, FONT_DRAW_NORENDERMODE ); y += 15; - Con_DrawString( x, y, va( "out: %i %.2f kb/s", out, cls.netchan.flow[FLOW_OUTGOING].avgkbytespersec ), colors ); + CL_DrawString( x, y, va( "out: %i %.2f kb/s", out, cls.netchan.flow[FLOW_OUTGOING].avgkbytespersec ), colors, font, FONT_DRAW_NORENDERMODE ); y += 15; if( graphtype > 2 ) @@ -413,16 +415,14 @@ static void NetGraph_DrawTextFields( int x, int y, int w, wrect_t rect, int coun int loss = (int)(( packet_loss + PACKETLOSS_AVG_FRAC ) - 0.01f ); int choke = (int)(( packet_choke + PACKETCHOKE_AVG_FRAC ) - 0.01f ); - Con_DrawString( x, y, va( "loss: %i choke: %i", loss, choke ), colors ); + CL_DrawString( x, y, va( "loss: %i choke: %i", loss, choke ), colors, font, FONT_DRAW_NORENDERMODE ); } } if( graphtype < 3 ) - Con_DrawString( ptx, pty, va( "%i/s", (int)cl_cmdrate->value ), colors ); + CL_DrawString( ptx, pty, va( "%i/s", (int)cl_cmdrate->value ), colors, font, FONT_DRAW_NORENDERMODE ); - Con_DrawString( ptx, last_y, va( "%i/s" , (int)cl_updaterate->value ), colors ); - - Con_RestoreFont(); + CL_DrawString( ptx, last_y, va( "%i/s" , (int)cl_updaterate->value ), colors, font, FONT_DRAW_NORENDERMODE ); } /* diff --git a/engine/client/cl_scrn.c b/engine/client/cl_scrn.c index e4e99eab..db343ce4 100644 --- a/engine/client/cl_scrn.c +++ b/engine/client/cl_scrn.c @@ -160,6 +160,7 @@ void SCR_NetSpeeds( void ) static int max_clfps = 0; int cur_clfps = 0; rgba_t color; + cl_font_t *font = Con_GetCurFont(); if( !host.allow_console ) return; @@ -196,25 +197,11 @@ void SCR_NetSpeeds( void ) Q_memprint( cls.netchan.total_sended ) ); - x = refState.width - 320; + x = refState.width - 320 * font->scale; y = 384; - Con_DrawStringLen( NULL, NULL, &height ); MakeRGBA( color, 255, 255, 255, 255 ); - - p = start = msg; - - do - { - end = Q_strchr( p, '\n' ); - if( end ) msg[end-start] = '\0'; - - Con_DrawString( x, y, p, color ); - y += height; - - if( end ) p = end + 1; - else break; - } while( 1 ); + CL_DrawString( x, y, msg, color, font, 0 ); } /* @@ -234,28 +221,13 @@ void SCR_RSpeeds( void ) int x, y, height; char *p, *start, *end; rgba_t color; + cl_font_t *font = Con_GetCurFont(); - x = refState.width - 340; + x = refState.width - 340 * font->scale; y = 64; - Con_DrawStringLen( NULL, NULL, &height ); MakeRGBA( color, 255, 255, 255, 255 ); - - p = start = msg; - do - { - end = Q_strchr( p, '\n' ); - if( end ) msg[end-start] = '\0'; - - Con_DrawString( x, y, p, color ); - y += height; - - // handle '\n\n' - if( *p == '\n' ) - y += height; - if( end ) p = end + 1; - else break; - } while( 1 ); + CL_DrawString( x, y, msg, color, font, 0 ); } } @@ -588,6 +560,7 @@ void SCR_LoadCreditsFont( void ) { cl_font_t *const font = &cls.creditsFont; qboolean success = false; + float scale = hud_fontscale->value; dword crc = 0; // replace default gfx.wad textures by current charset's font @@ -599,15 +572,15 @@ void SCR_LoadCreditsFont( void ) "creditsfont_%s.fnt", Cvar_VariableString( "con_charset" )) > 0 ) { if( FS_FileExists( charsetFnt, false )) - success = Con_LoadVariableWidthFont( charsetFnt, font, 1.0f, TF_FONT ); + success = Con_LoadVariableWidthFont( charsetFnt, font, scale, kRenderTransAdd, TF_FONT ); } } if( !success ) - success = Con_LoadVariableWidthFont( "gfx/creditsfont.fnt", font, 1.0f, TF_FONT ); + success = Con_LoadVariableWidthFont( "gfx/creditsfont.fnt", font, scale, kRenderTransAdd, TF_FONT ); if( !success ) - success = Con_LoadFixedWidthFont( "gfx/conchars", font, 1.0f, TF_FONT ); + success = Con_LoadFixedWidthFont( "gfx/conchars", font, scale, kRenderTransAdd, TF_FONT ); // copy font size for client.dll if( success ) diff --git a/engine/client/client.h b/engine/client/client.h index ba84a9ab..370da0f2 100644 --- a/engine/client/client.h +++ b/engine/client/client.h @@ -318,17 +318,25 @@ typedef struct pfnEventHook func; // user-defined function } cl_user_event_t; -#define FONT_FIXED 0 -#define FONT_VARIABLE 1 +#define FONT_FIXED 0 +#define FONT_VARIABLE 1 + +#define FONT_DRAW_HUD BIT( 0 ) // pass to drawing function to apply hud_scale +#define FONT_DRAW_UTF8 BIT( 1 ) // call UtfProcessChar +#define FONT_DRAW_FORCECOL BIT( 2 ) // ignore colorcodes +#define FONT_DRAW_NORENDERMODE BIT( 3 ) // ignore font's default rendermode typedef struct { - int hFontTexture; // handle to texture - wrect_t fontRc[256]; // rectangles - byte charWidths[256]; - int charHeight; - int type; - qboolean valid; // all rectangles are valid + int hFontTexture; // handle to texture + wrect_t fontRc[256]; // tex coords + float scale; // scale factor + byte charWidths[256]; // scaled widths + int charHeight; // scaled height + int type; // fixed width font or variable + int rendermode; // default rendermode + qboolean nearest; // nearest filtering enabled + qboolean valid; // all rectangles are valid } cl_font_t; typedef struct @@ -342,7 +350,6 @@ typedef struct int scissor_width; int scissor_height; qboolean scissor_test; - qboolean adjust_size; // allow to adjust scale for fonts int renderMode; // override kRenderMode from TriAPI TRICULLSTYLE cullMode; // override CULL FACE from TriAPI @@ -796,6 +803,19 @@ void CL_ResetEvent( event_info_t *ei ); word CL_EventIndex( const char *name ); void CL_FireEvents( void ); +// +// cl_font.c +// +qboolean CL_FixedFont( cl_font_t *font ); +qboolean Con_LoadFixedWidthFont( const char *fontname, cl_font_t *font, float scale, int rendermode, uint texFlags ); +qboolean Con_LoadVariableWidthFont( const char *fontname, cl_font_t *font, float scale, int rendermode, uint texFlags ); +void CL_FreeFont( cl_font_t *font ); +int CL_DrawCharacter( float x, float y, int number, rgba_t color, cl_font_t *font, int flags ); +int CL_DrawString( float x, float y, const char *s, rgba_t color, cl_font_t *font, int flags ); +void CL_DrawCharacterLen( cl_font_t *font, int number, int *width, int *height ); +void CL_DrawStringLen( cl_font_t *font, const char *s, int *width, int *height, int flags ); + + // // cl_game.c // @@ -1030,13 +1050,13 @@ int Con_UtfProcessChar( int in ); int Con_UtfProcessCharForce( int in ); int Con_UtfMoveLeft( char *str, int pos ); int Con_UtfMoveRight( char *str, int pos, int length ); -void Con_DrawStringLen( const char *pText, int *length, int *height ); -int Con_DrawString( int x, int y, const char *string, rgba_t setColor ); -int Con_DrawCharacter( int x, int y, int number, rgba_t color ); -void Con_DrawCharacterLen( int number, int *width, int *height ); void Con_DefaultColor( int r, int g, int b ); void Con_InvalidateFonts( void ); -void Con_SetFont( int fontNum ); +cl_font_t *Con_GetCurFont( void ); +cl_font_t *Con_GetFont( int num ); +void Con_DrawCharacterLen( int number, int *width, int *height ); +int Con_DrawString( int x, int y, const char *string, rgba_t setColor ); // legacy, use cl_font.c +void GAME_EXPORT Con_DrawStringLen( const char *pText, int *length, int *height ); // legacy, use cl_font.c void Con_CharEvent( int key ); void Con_RestoreFont( void ); void Key_Console( int key ); @@ -1046,8 +1066,6 @@ void Con_Bottom( void ); void Con_Top( void ); void Con_PageDown( int lines ); void Con_PageUp( int lines ); -qboolean Con_LoadVariableWidthFont( const char *fontname, cl_font_t *font, float scale, uint texFlags ); -qboolean Con_LoadFixedWidthFont( const char *fontname, cl_font_t *font, float scale, uint texFlags ); // // s_main.c diff --git a/engine/client/console.c b/engine/client/console.c index 03823b52..2267b44c 100644 --- a/engine/client/console.c +++ b/engine/client/console.c @@ -116,7 +116,7 @@ typedef struct // console fonts cl_font_t chars[CON_NUMFONTS];// fonts.wad/font1.fnt - cl_font_t *curFont, *lastUsedFont; + cl_font_t *curFont; // console input field_t input; @@ -555,90 +555,9 @@ Con_FixedFont */ qboolean Con_FixedFont( void ) { - if( con.curFont && con.curFont->valid && con.curFont->type == FONT_FIXED ) - return true; - return false; + return CL_FixedFont( con.curFont ); } -qboolean Con_LoadFixedWidthFont( const char *fontname, cl_font_t *font, float scale, uint texFlags ) -{ - int fontWidth; - int i; - - if( font->valid ) - return true; // already loaded - - if( !FS_FileExists( fontname, false )) - return false; - - font->hFontTexture = ref.dllFuncs.GL_LoadTexture( fontname, NULL, 0, texFlags ); - R_GetTextureParms( &fontWidth, NULL, font->hFontTexture ); - - if( font->hFontTexture && fontWidth != 0 ) - { - font->charHeight = fontWidth / 16 * scale; - font->type = FONT_FIXED; - - // build fixed rectangles - for( i = 0; i < 256; i++ ) - { - font->fontRc[i].left = (i * (fontWidth / 16)) % fontWidth; - font->fontRc[i].right = font->fontRc[i].left + fontWidth / 16; - font->fontRc[i].top = (i / 16) * (fontWidth / 16); - font->fontRc[i].bottom = font->fontRc[i].top + fontWidth / 16; - font->charWidths[i] = fontWidth / 16 * scale; - } - font->valid = true; - } - - return true; -} - -qboolean Con_LoadVariableWidthFont( const char *fontname, cl_font_t *font, float scale, uint texFlags ) -{ - fs_offset_t length; - qfont_t *src; - byte *buffer; - int fontWidth; - int i; - - if( font->valid ) - return true; // already loaded - - if( !FS_FileExists( fontname, false )) - return false; - - font->hFontTexture = ref.dllFuncs.GL_LoadTexture( fontname, NULL, 0, texFlags ); - R_GetTextureParms( &fontWidth, NULL, font->hFontTexture ); - - // setup consolefont - if( font->hFontTexture && fontWidth != 0 ) - { - // half-life font with variable chars witdh - buffer = FS_LoadFile( fontname, &length, false ); - - if( buffer && length >= sizeof( qfont_t )) - { - src = (qfont_t *)buffer; - font->charHeight = src->rowheight * scale; - font->type = FONT_VARIABLE; - - // build rectangles - for( i = 0; i < 256; i++ ) - { - font->fontRc[i].left = (word)src->fontinfo[i].startoffset % fontWidth; - font->fontRc[i].right = font->fontRc[i].left + src->fontinfo[i].charwidth; - font->fontRc[i].top = (word)src->fontinfo[i].startoffset / fontWidth; - font->fontRc[i].bottom = font->fontRc[i].top + src->rowheight; - font->charWidths[i] = src->fontinfo[i].charwidth * scale; - } - font->valid = true; - } - if( buffer ) Mem_Free( buffer ); - } - - return true; -} /* ================ @@ -650,6 +569,7 @@ INTERNAL RESOURCE static void Con_LoadConsoleFont( int fontNumber, cl_font_t *font ) { qboolean success = false; + float scale = con_fontscale->value; if( font->valid ) return; // already loaded @@ -657,7 +577,7 @@ static void Con_LoadConsoleFont( int fontNumber, cl_font_t *font ) // loading conchars if( Sys_CheckParm( "-oldfont" )) { - success = Con_LoadVariableWidthFont( "gfx/conchars.fnt", font, con_fontscale->value, TF_FONT|TF_NEAREST ); + success = Con_LoadVariableWidthFont( "gfx/conchars.fnt", font, scale, kRenderTransTexture, TF_FONT|TF_NEAREST ); } else { @@ -670,14 +590,14 @@ static void Con_LoadConsoleFont( int fontNumber, cl_font_t *font ) if( Q_snprintf( path, sizeof( path ), "font%i_%s.fnt", fontNumber, Cvar_VariableString( "con_charset" )) > 0 ) { - success = Con_LoadVariableWidthFont( path, font, con_fontscale->value, TF_FONT|TF_NEAREST ); + success = Con_LoadVariableWidthFont( path, font, scale, kRenderTransTexture, TF_FONT|TF_NEAREST ); } } if( !success ) { Q_snprintf( path, sizeof( path ), "fonts/font%i", fontNumber ); - success = Con_LoadVariableWidthFont( path, font, con_fontscale->value, TF_FONT|TF_NEAREST ); + success = Con_LoadVariableWidthFont( path, font, scale, kRenderTransTexture, TF_FONT|TF_NEAREST ); } } @@ -685,7 +605,7 @@ static void Con_LoadConsoleFont( int fontNumber, cl_font_t *font ) { // quake fixed font as fallback // keep source to print directly into conback image - if( !Con_LoadFixedWidthFont( "gfx/conchars", font, con_fontscale->value, TF_FONT|TF_KEEP_SOURCE )) + if( !Con_LoadFixedWidthFont( "gfx/conchars", font, scale, kRenderTransTexture, TF_FONT|TF_KEEP_SOURCE )) Con_DPrintf( S_ERROR "failed to load console font\n" ); } } @@ -716,7 +636,7 @@ static void Con_LoadConchars( void ) fontSize = CON_NUMFONTS - 1; // sets the current font - con.lastUsedFont = con.curFont = &con.chars[fontSize]; + con.curFont = &con.chars[fontSize]; } // CP1251 table @@ -889,129 +809,25 @@ static void Con_DrawCharToConback( int num, const byte *conchars, byte *dest ) /* ==================== -Con_TextAdjustSize +Con_GetFont -draw charcters routine ==================== */ -static void Con_TextAdjustSize( int *x, int *y, int *w, int *h ) +cl_font_t *Con_GetFont( int num ) { - float xscale, yscale; - - if( !x && !y && !w && !h ) return; - - // scale for screen sizes - xscale = (float)refState.width / (float)clgame.scrInfo.iWidth; - yscale = (float)refState.height / (float)clgame.scrInfo.iHeight; - - if( x ) *x *= xscale; - if( y ) *y *= yscale; - if( w ) *w *= xscale; - if( h ) *h *= yscale; + num = bound( 0, num, CON_NUMFONTS - 1 ); + return &con.chars[num]; } /* ==================== -Con_DrawGenericChar +Con_GetCurFont -draw console single character ==================== */ -static int Con_DrawGenericChar( int x, int y, int number, rgba_t color ) +cl_font_t *Con_GetCurFont( void ) { - int width, height; - float s1, t1, s2, t2; - wrect_t *rc; - - number &= 255; - - if( !con.curFont || !con.curFont->valid ) - return 0; - - number = Con_UtfProcessChar( number ); - if( !number ) - return 0; - - if( y < -con.curFont->charHeight ) - return 0; - - rc = &con.curFont->fontRc[number]; - R_GetTextureParms( &width, &height, con.curFont->hFontTexture ); - - if( !width || !height ) - return con.curFont->charWidths[number]; - - // don't apply color to fixed fonts it's already colored - if( con.curFont->type != FONT_FIXED || REF_GET_PARM( PARM_TEX_GLFORMAT, con.curFont->hFontTexture ) == 0x8045 ) // GL_LUMINANCE8_ALPHA8 - ref.dllFuncs.Color4ub( color[0], color[1], color[2], color[3] ); - else ref.dllFuncs.Color4ub( 255, 255, 255, color[3] ); - - // calc rectangle - s1 = (float)rc->left / width; - t1 = (float)rc->top / height; - s2 = (float)rc->right / width; - t2 = (float)rc->bottom / height; - width = ( rc->right - rc->left ) * con_fontscale->value; - height = ( rc->bottom - rc->top ) * con_fontscale->value; - - if( clgame.ds.adjust_size ) - Con_TextAdjustSize( &x, &y, &width, &height ); - ref.dllFuncs.R_DrawStretchPic( x, y, width, height, s1, t1, s2, t2, con.curFont->hFontTexture ); - ref.dllFuncs.Color4ub( 255, 255, 255, 255 ); // don't forget reset color - - return con.curFont->charWidths[number]; -} - -/* -==================== -Con_SetFont - -choose font size -==================== -*/ -void Con_SetFont( int fontNum ) -{ - fontNum = bound( 0, fontNum, CON_NUMFONTS - 1 ); - con.curFont = &con.chars[fontNum]; -} - -/* -==================== -Con_RestoreFont - -restore auto-selected console font -(that based on screen resolution) -==================== -*/ -void Con_RestoreFont( void ) -{ - con.curFont = con.lastUsedFont; -} - -/* -==================== -Con_DrawCharacter - -client version of routine -==================== -*/ -int Con_DrawCharacter( int x, int y, int number, rgba_t color ) -{ - ref.dllFuncs.GL_SetRenderMode( kRenderTransTexture ); - return Con_DrawGenericChar( x, y, number, color ); -} - -/* -==================== -Con_DrawCharacterLen - -returns character sizes in screen pixels -==================== -*/ -void Con_DrawCharacterLen( int number, int *width, int *height ) -{ - if( width && con.curFont ) *width = con.curFont->charWidths[number]; - if( height && con.curFont ) *height = con.curFont->charHeight; + return con.curFont; } /* @@ -1023,105 +839,7 @@ compute string width and height in screen pixels */ void GAME_EXPORT Con_DrawStringLen( const char *pText, int *length, int *height ) { - int curLength = 0; - - if( !con.curFont ) - return; - if( height ) - *height = con.curFont->charHeight; - if (!length) - return; - - *length = 0; - - while( *pText ) - { - byte c = *pText; - - if( *pText == '\n' ) - { - pText++; - curLength = 0; - } - - // skip color strings they are not drawing - if( IsColorString( pText )) - { - pText += 2; - continue; - } - - - // Convert to unicode - c = Con_UtfProcessChar( c ); - - if( c ) - curLength += con.curFont->charWidths[c]; - - pText++; - - if( curLength > *length ) - *length = curLength; - } -} - -/* -================== -Con_DrawString - -Draws a multi-colored string, optionally forcing -to a fixed color. -================== -*/ -int Con_DrawGenericString( int x, int y, const char *string, rgba_t setColor, qboolean forceColor, int hideChar ) -{ - rgba_t color; - int drawLen = 0; - int numDraws = 0; - const char *s; - - if( !con.curFont ) return 0; // no font set - - Con_UtfProcessChar( 0 ); - - // draw the colored text - memcpy( color, setColor, sizeof( color )); - s = string; - - while( *s ) - { - if( *s == '\n' ) - { - s++; - if( !*s ) break; // at end the string - drawLen = 0; // begin new row - y += con.curFont->charHeight; - } - - if( IsColorString( s )) - { - if( !forceColor ) - { - memcpy( color, g_color_table[ColorIndex(*(s+1))], sizeof( color )); - color[3] = setColor[3]; - } - - s += 2; - numDraws++; - continue; - } - - // hide char for overstrike mode - if( hideChar == numDraws ) - drawLen += con.curFont->charWidths[*s]; - else drawLen += Con_DrawCharacter( x + drawLen, y, *s, color ); - - numDraws++; - s++; - } - - ref.dllFuncs.Color4ub( 255, 255, 255, 255 ); - return drawLen; + return CL_DrawStringLen( con.curFont, pText, length, height, FONT_DRAW_UTF8 ); } /* @@ -1133,10 +851,9 @@ client version of routine */ int Con_DrawString( int x, int y, const char *string, rgba_t setColor ) { - return Con_DrawGenericString( x, y, string, setColor, false, -1 ); + return CL_DrawString( x, y, string, setColor, con.curFont, FONT_DRAW_UTF8 ); } - /* ================ Con_Init @@ -1628,7 +1345,7 @@ Field_DrawInputLine void Field_DrawInputLine( int x, int y, field_t *edit ) { int len, cursorChar; - int drawLen, hideChar = -1; + int drawLen; int prestep, curPos; char str[MAX_SYSPATH]; byte *colorDefault; @@ -1665,35 +1382,23 @@ void Field_DrawInputLine( int x, int y, field_t *edit ) // save char for overstrike cursorChar = str[edit->cursor - prestep]; - if( host.key_overstrike && cursorChar && !((int)( host.realtime * 4 ) & 1 )) - hideChar = edit->cursor - prestep; // skip this char - // draw it - Con_DrawGenericString( x, y, str, colorDefault, false, hideChar ); + CL_DrawString( x, y, str, colorDefault, con.curFont, FONT_DRAW_UTF8 ); // draw the cursor if((int)( host.realtime * 4 ) & 1 ) return; // off blink // calc cursor position str[edit->cursor - prestep] = 0; - Con_DrawStringLen( str, &curPos, NULL ); - Con_UtfProcessChar( 0 ); + CL_DrawStringLen( con.curFont, str, &curPos, NULL, FONT_DRAW_UTF8 ); - if( host.key_overstrike && cursorChar ) + if( host.key_overstrike ) { - // overstrike cursor -#if 0 - pglEnable( GL_BLEND ); - pglDisable( GL_ALPHA_TEST ); - pglBlendFunc( GL_ONE_MINUS_SRC_ALPHA, GL_SRC_ALPHA ); - pglTexEnvi( GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE ); -#endif - Con_DrawGenericChar( x + curPos, y, cursorChar, colorDefault ); + CL_DrawCharacter( x + curPos, y, '|', colorDefault, con.curFont, 0 ); } else { - Con_UtfProcessChar( 0 ); - Con_DrawCharacter( x + curPos, y, '_', colorDefault ); + CL_DrawCharacter( x + curPos, y, '_', colorDefault, con.curFont, 0 ); } } @@ -1997,7 +1702,7 @@ void Con_DrawInput( int lines ) return; y = lines - ( con.curFont->charHeight * 2 ); - Con_DrawCharacter( con.curFont->charWidths[' '], y, ']', g_color_table[7] ); + CL_DrawCharacter( con.curFont->charWidths[' '], y, ']', g_color_table[7], con.curFont, 0 ); Field_DrawInputLine( con.curFont->charWidths[' ']*2, y, &con.input ); } @@ -2142,7 +1847,11 @@ int Con_DrawConsoleLine( int y, int lineno ) return 0; // this string will be shown only at notify if( y >= con.curFont->charHeight ) - Con_DrawGenericString( con.curFont->charWidths[' '], y, li->start, g_color_table[7], false, -1 ); + { + float x = con.curFont->charWidths[' ']; + + CL_DrawString( x, y, li->start, g_color_table[7], con.curFont, FONT_DRAW_UTF8 ); + } return con.curFont->charHeight; } @@ -2212,14 +1921,12 @@ void Con_DrawSolidConsole( int lines ) Q_snprintf( curbuild, MAX_STRING, XASH_ENGINE_NAME " %i/" XASH_VERSION " (%s-%s build %i)", PROTOCOL_VERSION, Q_buildos(), Q_buildarch(), Q_buildnum( )); Con_DrawStringLen( curbuild, &stringLen, &charH ); - start = refState.width - stringLen; - stringLen = Con_StringLength( curbuild ); + start = refState.width - stringLen; fraction = lines / (float)refState.height; color[3] = Q_min( fraction * 2.0f, 1.0f ) * 255; // fadeout version number - for( i = 0; i < stringLen; i++ ) - width += Con_DrawCharacter( start + width, 0, curbuild[i], color ); + Con_DrawString( start, 0, curbuild, color ); // draw the text if( CON_LINES_COUNT > 0 ) @@ -2236,7 +1943,7 @@ void Con_DrawSolidConsole( int lines ) // draw red arrows to show the buffer is backscrolled for( x = 0; x < con.linewidth; x += 4 ) - Con_DrawCharacter(( x + 1 ) * start, y, '^', g_color_table[1] ); + CL_DrawCharacter( ( x + 1 ) * start, y, '^', g_color_table[1], con.curFont, 0 ); y -= con.curFont->charHeight; } x = lastline; @@ -2339,7 +2046,7 @@ void Con_DrawVersion( void ) { // draws the current build byte *color = g_color_table[7]; - int i, stringLen, width = 0, charH = 0; + int stringLen, charH = 0; int start, height = refState.height; qboolean draw_version = false; string curbuild; @@ -2370,8 +2077,7 @@ void Con_DrawVersion( void ) stringLen = Con_StringLength( curbuild ); height -= charH * 1.05f; - for( i = 0; i < stringLen; i++ ) - width += Con_DrawCharacter( start + width, height, curbuild[i], color ); + Con_DrawString( start, height, curbuild, color ); } /* @@ -2414,7 +2120,7 @@ void Con_RunConsole( void ) if( FBitSet( con_charset->flags, FCVAR_CHANGED ) || FBitSet( con_fontscale->flags, FCVAR_CHANGED ) || FBitSet( con_fontnum->flags, FCVAR_CHANGED ) || - FBitSet( cl_charset->flags, FCVAR_CHANGED ) ) + FBitSet( cl_charset->flags, FCVAR_CHANGED )) { // update codepage parameters if( !Q_stricmp( con_charset->string, "cp1251" )) @@ -2436,8 +2142,6 @@ void Con_RunConsole( void ) g_utf8 = !Q_stricmp( cl_charset->string, "utf-8" ); Con_InvalidateFonts(); Con_LoadConchars(); - cls.creditsFont.valid = false; - SCR_LoadCreditsFont(); ClearBits( con_charset->flags, FCVAR_CHANGED ); ClearBits( con_fontnum->flags, FCVAR_CHANGED ); ClearBits( con_fontscale->flags, FCVAR_CHANGED ); @@ -2576,8 +2280,10 @@ Con_InvalidateFonts */ void Con_InvalidateFonts( void ) { - memset( con.chars, 0, sizeof( con.chars )); - con.curFont = con.lastUsedFont = NULL; + int i; + for( i = 0; i < ARRAYSIZE( con.chars ); i++ ) + CL_FreeFont( &con.chars[i] ); + con.curFont = NULL; } /* diff --git a/engine/client/keys.c b/engine/client/keys.c index a1e696c8..80c45ba2 100644 --- a/engine/client/keys.c +++ b/engine/client/keys.c @@ -1148,7 +1148,7 @@ Draw button with symbol on it */ static void OSK_DrawSymbolButton( int symb, float x, float y, float width, float height ) { - char str[] = {symb & 255, 0}; + cl_font_t *font = Con_GetCurFont(); byte color[] = { 255, 255, 255, 255 }; int x1 = x * refState.width, y1 = y * refState.height, @@ -1156,14 +1156,15 @@ static void OSK_DrawSymbolButton( int symb, float x, float y, float width, float h = height * refState.height; if( symb == osk.curbutton.val ) - { ref.dllFuncs.FillRGBABlend( x1, y1, w, h, 255, 160, 0, 100 ); - } if( !symb || symb == ' ' || (symb >= OSK_TAB && symb < OSK_SPECKEY_LAST ) ) return; - Con_DrawCharacter( x1 + 1, y1, symb, color ); + CL_DrawCharacter( + x1 + width * 0.4 * refState.width, + y1 + height * 0.4 * refState.height, + symb, color, font, 0 ); } /* @@ -1177,7 +1178,11 @@ static void OSK_DrawSpecialButton( const char *name, float x, float y, float wid { byte color[] = { 0, 255, 0, 255 }; - Con_DrawString( x * refState.width, y * refState.height, name, color ); + Con_DrawString( + x * refState.width + width * 0.4 * refState.width, + y * refState.height + height * 0.4 * refState.height, + name, + color ); } From a19d34035db7c33e271b07da36e8a729ab2589ec Mon Sep 17 00:00:00 2001 From: Alibek Omarov Date: Fri, 3 Feb 2023 17:49:06 +0300 Subject: [PATCH 403/490] engine: client: font: do not use OpenFile on WADs >_< --- engine/client/cl_font.c | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/engine/client/cl_font.c b/engine/client/cl_font.c index 3445f40d..d0f24dd2 100644 --- a/engine/client/cl_font.c +++ b/engine/client/cl_font.c @@ -79,24 +79,26 @@ qboolean Con_LoadFixedWidthFont( const char *fontname, cl_font_t *font, float sc qboolean Con_LoadVariableWidthFont( const char *fontname, cl_font_t *font, float scale, int rendermode, uint texFlags ) { + fs_offset_t length; qfont_t src; - file_t *fd; + byte *pfile; int font_width, i; if( font->valid ) return true; - fd = g_fsapi.Open( fontname, "r", false ); - if( !fd ) + pfile = g_fsapi.LoadFile( fontname, &length, false ); + if( !pfile ) return false; - if( g_fsapi.Read( fd, &src, sizeof( qfont_t )) != sizeof( qfont_t )) + if( length < sizeof( src )) { - g_fsapi.Close( fd ); + Mem_Free( pfile ); return false; } - g_fsapi.Close( fd ); + memcpy( &src, pfile, sizeof( src )); + Mem_Free( pfile ); font->hFontTexture = CL_LoadFontTexture( fontname, texFlags, &font_width ); if( !font->hFontTexture ) From e2c2821191bd02477415f2410e1660bca73bebf4 Mon Sep 17 00:00:00 2001 From: Alibek Omarov Date: Fri, 3 Feb 2023 18:06:07 +0300 Subject: [PATCH 404/490] engine: client: font: do not apply filtering hack when fonts aren't upscaled --- engine/client/cl_font.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/engine/client/cl_font.c b/engine/client/cl_font.c index d0f24dd2..8e5f787c 100644 --- a/engine/client/cl_font.c +++ b/engine/client/cl_font.c @@ -157,7 +157,7 @@ int CL_DrawCharacter( float x, float y, int number, rgba_t color, cl_font_t *fon return 0; rc = &font->fontRc[number]; - if( font->nearest ) + if( font->nearest || font->scale > 1.0f ) half = 0; s1 = ((float)rc->left + half ) / texw; From bec0b36bb9aa5a93a9d6f96a59c1ac83e77bbd3c Mon Sep 17 00:00:00 2001 From: Alibek Omarov Date: Fri, 3 Feb 2023 18:20:02 +0300 Subject: [PATCH 405/490] engine: client: font: fix colorcodes, don't reset Colo4ub, it will be reset by consequent draw calls anyway --- engine/client/cl_font.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/engine/client/cl_font.c b/engine/client/cl_font.c index 8e5f787c..79eb5ceb 100644 --- a/engine/client/cl_font.c +++ b/engine/client/cl_font.c @@ -178,7 +178,6 @@ int CL_DrawCharacter( float x, float y, int number, rgba_t color, cl_font_t *fon ref.dllFuncs.Color4ub( color[0], color[1], color[2], color[3] ); else ref.dllFuncs.Color4ub( 255, 255, 255, color[3] ); ref.dllFuncs.R_DrawStretchPic( x, y, w, h, s1, t1, s2, t2, font->hFontTexture ); - ref.dllFuncs.Color4ub( 255, 255, 255, 255 ); // don't forget reset color return font->charWidths[number]; } @@ -214,7 +213,8 @@ int CL_DrawString( float x, float y, const char *s, rgba_t color, cl_font_t *fon if( IsColorString( s )) { - if( FBitSet( flags, FONT_DRAW_FORCECOL )) + // don't copy alpha + if( !FBitSet( flags, FONT_DRAW_FORCECOL )) VectorCopy( g_color_table[ColorIndex(*( s + 1 ))], current_color ); s += 2; @@ -222,7 +222,7 @@ int CL_DrawString( float x, float y, const char *s, rgba_t color, cl_font_t *fon } // skip setting rendermode, it was changed for this string already - draw_len += CL_DrawCharacter( x + draw_len, y, (byte)*s, color, font, flags | FONT_DRAW_NORENDERMODE ); + draw_len += CL_DrawCharacter( x + draw_len, y, (byte)*s, current_color, font, flags | FONT_DRAW_NORENDERMODE ); s++; } From 82b6da493a3b04876a30c832571569138dd073e4 Mon Sep 17 00:00:00 2001 From: Alibek Omarov Date: Fri, 3 Feb 2023 18:51:50 +0300 Subject: [PATCH 406/490] wscript: enforce GCC suspicious sizeof operations warnings as errors --- wscript | 3 +++ 1 file changed, 3 insertions(+) diff --git a/wscript b/wscript index f8a09cb1..4931ad6a 100644 --- a/wscript +++ b/wscript @@ -206,6 +206,9 @@ def configure(conf): '-Werror=implicit-fallthrough=2', # clang incompatible without "=2" '-Werror=logical-op', '-Werror=write-strings', + '-Werror=sizeof-pointer-memaccess', + '-Werror=sizeof-array-div', + '-Werror=sizeof-pointer-div', # '-Werror=format=2', # '-Wdouble-promotion', # disable warning flood '-Wstrict-aliasing', From d14e486721248ee74eaac9fc552d577f95dbc711 Mon Sep 17 00:00:00 2001 From: Alibek Omarov Date: Sat, 4 Feb 2023 20:53:52 +0300 Subject: [PATCH 407/490] engine: client: font: add special flag to ignore linefeeds when drawing strings --- engine/client/cl_font.c | 8 ++++++-- engine/client/client.h | 1 + 2 files changed, 7 insertions(+), 2 deletions(-) diff --git a/engine/client/cl_font.c b/engine/client/cl_font.c index 79eb5ceb..20720e70 100644 --- a/engine/client/cl_font.c +++ b/engine/client/cl_font.c @@ -207,8 +207,12 @@ int CL_DrawString( float x, float y, const char *s, rgba_t color, cl_font_t *fon if( !*s ) break; - draw_len = 0; - y += font->charHeight; + // some client functions ignore newlines + if( !FBitSet( flags, FONT_DRAW_NOLF )) + { + draw_len = 0; + y += font->charHeight; + } } if( IsColorString( s )) diff --git a/engine/client/client.h b/engine/client/client.h index 370da0f2..b61848d0 100644 --- a/engine/client/client.h +++ b/engine/client/client.h @@ -325,6 +325,7 @@ typedef struct #define FONT_DRAW_UTF8 BIT( 1 ) // call UtfProcessChar #define FONT_DRAW_FORCECOL BIT( 2 ) // ignore colorcodes #define FONT_DRAW_NORENDERMODE BIT( 3 ) // ignore font's default rendermode +#define FONT_DRAW_NOLF BIT( 4 ) // ignore \n typedef struct { From 2225915702e2b50cbda024f9b1c6b27c709edfb1 Mon Sep 17 00:00:00 2001 From: Alibek Omarov Date: Sat, 4 Feb 2023 20:54:17 +0300 Subject: [PATCH 408/490] engine: client: font: fix CL_DrawStringLen --- engine/client/cl_font.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/engine/client/cl_font.c b/engine/client/cl_font.c index 20720e70..67837b5d 100644 --- a/engine/client/cl_font.c +++ b/engine/client/cl_font.c @@ -286,7 +286,7 @@ void CL_DrawStringLen( cl_font_t *font, const char *s, int *width, int *height, if( number ) { - draw_len += font->charWidths[*s]; + draw_len += font->charWidths[number]; if( draw_len > *width ) *width = draw_len; From fd63018fb58420d072317d85d69bb8f2205b83f1 Mon Sep 17 00:00:00 2001 From: Alibek Omarov Date: Sat, 4 Feb 2023 20:58:33 +0300 Subject: [PATCH 409/490] engine: client: make client string drawing functions ignore linefeeds --- engine/client/cl_font.c | 7 +++++-- engine/client/cl_game.c | 12 ++++++------ 2 files changed, 11 insertions(+), 8 deletions(-) diff --git a/engine/client/cl_font.c b/engine/client/cl_font.c index 67837b5d..a75fc3cf 100644 --- a/engine/client/cl_font.c +++ b/engine/client/cl_font.c @@ -270,8 +270,11 @@ void CL_DrawStringLen( cl_font_t *font, const char *s, int *width, int *height, // but high chances somebody's relying on this s++; draw_len = 0; - if( height ) - *height += font->charHeight; + if( !FBitSet( flags, FONT_DRAW_NOLF )) + { + if( height ) + *height += font->charHeight; + } } if( IsColorString( s )) diff --git a/engine/client/cl_game.c b/engine/client/cl_game.c index a1549f4a..e305f2ba 100644 --- a/engine/client/cl_game.c +++ b/engine/client/cl_game.c @@ -1895,7 +1895,8 @@ void GAME_EXPORT pfnDrawConsoleStringLen( const char *pText, int *length, int *h { cl_font_t *font = Con_GetFont( con_fontsize->value ); - CL_DrawStringLen( font, pText, length, height, FONT_DRAW_UTF8 | FONT_DRAW_HUD ); + if( height ) *height = font->charHeight; + CL_DrawStringLen( font, pText, length, NULL, FONT_DRAW_UTF8 | FONT_DRAW_HUD ); } /* @@ -2841,7 +2842,7 @@ pfnDrawString static int GAME_EXPORT pfnDrawString( int x, int y, const char *str, int r, int g, int b ) { rgba_t color = { r, g, b, 255 }; - int flags = FONT_DRAW_HUD; + int flags = FONT_DRAW_HUD | FONT_DRAW_NOLF; if( hud_utf8->value ) SetBits( flags, FONT_DRAW_UTF8 ); @@ -2858,16 +2859,15 @@ pfnDrawStringReverse static int GAME_EXPORT pfnDrawStringReverse( int x, int y, const char *str, int r, int g, int b ) { rgba_t color = { r, g, b, 255 }; - int flags = FONT_DRAW_HUD; - int width, height; + int flags = FONT_DRAW_HUD | FONT_DRAW_NOLF; + int width; if( hud_utf8->value ) SetBits( flags, FONT_DRAW_UTF8 ); - CL_DrawStringLen( &cls.creditsFont, str, &width, &height, flags ); + CL_DrawStringLen( &cls.creditsFont, str, &width, NULL, flags ); x -= width; - y -= height; return CL_DrawString( x, y, str, color, &cls.creditsFont, flags ); } From ba6dd3c751c6c96f125adea167bdcce2f9fba1aa Mon Sep 17 00:00:00 2001 From: Alibek Omarov Date: Sat, 4 Feb 2023 21:23:51 +0300 Subject: [PATCH 410/490] engine: client: font: fix another inverted check --- engine/client/cl_font.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/engine/client/cl_font.c b/engine/client/cl_font.c index a75fc3cf..64d857e0 100644 --- a/engine/client/cl_font.c +++ b/engine/client/cl_font.c @@ -157,7 +157,7 @@ int CL_DrawCharacter( float x, float y, int number, rgba_t color, cl_font_t *fon return 0; rc = &font->fontRc[number]; - if( font->nearest || font->scale > 1.0f ) + if( font->nearest || font->scale <= 1.0f ) half = 0; s1 = ((float)rc->left + half ) / texw; From c0fa91bec9f6b6523cc5e9d546a297abce43f139 Mon Sep 17 00:00:00 2001 From: Alibek Omarov Date: Sat, 4 Feb 2023 21:24:19 +0300 Subject: [PATCH 411/490] engine: client: consolidate client and menu scissor functions --- engine/client/cl_game.c | 149 +++++++++++++++++++++++--------------- engine/client/cl_gameui.c | 64 +--------------- engine/client/client.h | 32 ++++---- 3 files changed, 111 insertions(+), 134 deletions(-) diff --git a/engine/client/cl_game.c b/engine/client/cl_game.c index e305f2ba..26b6c91d 100644 --- a/engine/client/cl_game.c +++ b/engine/client/cl_game.c @@ -371,55 +371,6 @@ void SPR_AdjustTexCoords( float width, float height, float *s1, float *t1, float *t2 /= height; } -static qboolean SPR_Scissor( float *x, float *y, float *width, float *height, float *u0, float *v0, float *u1, float *v1 ) -{ - float dudx, dvdy; - - // clip sub rect to sprite - if(( width == 0 ) || ( height == 0 )) - return false; - - if( *x + *width <= clgame.ds.scissor_x ) - return false; - if( *x >= clgame.ds.scissor_x + clgame.ds.scissor_width ) - return false; - if( *y + *height <= clgame.ds.scissor_y ) - return false; - if( *y >= clgame.ds.scissor_y + clgame.ds.scissor_height ) - return false; - - dudx = (*u1 - *u0) / *width; - dvdy = (*v1 - *v0) / *height; - - if( *x < clgame.ds.scissor_x ) - { - *u0 += (clgame.ds.scissor_x - *x) * dudx; - *width -= clgame.ds.scissor_x - *x; - *x = clgame.ds.scissor_x; - } - - if( *x + *width > clgame.ds.scissor_x + clgame.ds.scissor_width ) - { - *u1 -= (*x + *width - (clgame.ds.scissor_x + clgame.ds.scissor_width)) * dudx; - *width = clgame.ds.scissor_x + clgame.ds.scissor_width - *x; - } - - if( *y < clgame.ds.scissor_y ) - { - *v0 += (clgame.ds.scissor_y - *y) * dvdy; - *height -= clgame.ds.scissor_y - *y; - *y = clgame.ds.scissor_y; - } - - if( *y + *height > clgame.ds.scissor_y + clgame.ds.scissor_height ) - { - *v1 -= (*y + *height - (clgame.ds.scissor_y + clgame.ds.scissor_height)) * dvdy; - *height = clgame.ds.scissor_y + clgame.ds.scissor_height - *y; - } - - return true; -} - /* ==================== SPR_DrawGeneric @@ -470,7 +421,7 @@ static void SPR_DrawGeneric( int frame, float x, float y, float width, float hei } // pass scissor test if supposed - if( clgame.ds.scissor_test && !SPR_Scissor( &x, &y, &width, &height, &s1, &t1, &s2, &t2 )) + if( !CL_Scissor( &clgame.ds.scissor, &x, &y, &width, &height, &s1, &t1, &s2, &t2 )) return; // scale for screen sizes @@ -825,6 +776,92 @@ const char *CL_SoundFromIndex( int index ) return sfx->name; } +/* +================ +CL_EnableScissor + +enable scissor test +================ +*/ +void CL_EnableScissor( scissor_state_t *scissor, int x, int y, int width, int height ) +{ + scissor->x = x; + scissor->y = y; + scissor->width = width; + scissor->height = height; + scissor->test = true; +} + +/* +================ +CL_DisableScissor + +disable scissor test +================ +*/ +void CL_DisableScissor( scissor_state_t *scissor ) +{ + scissor->test = false; +} + +/* +================ +CL_Scissor + +perform common scissor test +================ +*/ +qboolean CL_Scissor( const scissor_state_t *scissor, float *x, float *y, float *width, float *height, float *u0, float *v0, float *u1, float *v1 ) +{ + float dudx, dvdy; + + if( !scissor->test ) + return true; + + // clip sub rect to sprite + if( *width == 0 || *height == 0 ) + return false; + + if( *x + *width <= scissor->x ) + return false; + if( *x >= scissor->x + scissor->width ) + return false; + if( *y + *height <= scissor->y ) + return false; + if( *y >= scissor->y + scissor->height ) + return false; + + dudx = (*u1 - *u0) / *width; + dvdy = (*v1 - *v0) / *height; + + if( *x < scissor->x ) + { + *u0 += (scissor->x - *x) * dudx; + *width -= scissor->x - *x; + *x = scissor->x; + } + + if( *x + *width > scissor->x + scissor->width ) + { + *u1 -= (*x + *width - (scissor->x + scissor->width)) * dudx; + *width = scissor->x + scissor->width - *x; + } + + if( *y < scissor->y ) + { + *v0 += (scissor->y - *y) * dvdy; + *height -= scissor->y - *y; + *y = scissor->y; + } + + if( *y + *height > scissor->y + scissor->height ) + { + *v1 -= (*y + *height - (scissor->y + scissor->height)) * dvdy; + *height = scissor->y + scissor->height - *y; + } + return true; +} + /* ========= SPR_EnableScissor @@ -839,11 +876,7 @@ static void GAME_EXPORT SPR_EnableScissor( int x, int y, int width, int height ) width = bound( 0, width, clgame.scrInfo.iWidth - x ); height = bound( 0, height, clgame.scrInfo.iHeight - y ); - clgame.ds.scissor_x = x; - clgame.ds.scissor_width = width; - clgame.ds.scissor_y = y; - clgame.ds.scissor_height = height; - clgame.ds.scissor_test = true; + CL_EnableScissor( &clgame.ds.scissor, x, y, width, height ); } /* @@ -854,11 +887,7 @@ SPR_DisableScissor */ static void GAME_EXPORT SPR_DisableScissor( void ) { - clgame.ds.scissor_x = 0; - clgame.ds.scissor_width = 0; - clgame.ds.scissor_y = 0; - clgame.ds.scissor_height = 0; - clgame.ds.scissor_test = false; + CL_DisableScissor( &clgame.ds.scissor ); } /* diff --git a/engine/client/cl_gameui.c b/engine/client/cl_gameui.c index e641cbbd..d19087c5 100644 --- a/engine/client/cl_gameui.c +++ b/engine/client/cl_gameui.c @@ -421,54 +421,6 @@ static void UI_ConvertGameInfo( GAMEINFO *out, gameinfo_t *in ) out->flags |= GFL_RENDER_PICBUTTON_TEXT; } -static qboolean PIC_Scissor( float *x, float *y, float *width, float *height, float *u0, float *v0, float *u1, float *v1 ) -{ - float dudx, dvdy; - - // clip sub rect to sprite - if(( width == 0 ) || ( height == 0 )) - return false; - - if( *x + *width <= gameui.ds.scissor_x ) - return false; - if( *x >= gameui.ds.scissor_x + gameui.ds.scissor_width ) - return false; - if( *y + *height <= gameui.ds.scissor_y ) - return false; - if( *y >= gameui.ds.scissor_y + gameui.ds.scissor_height ) - return false; - - dudx = (*u1 - *u0) / *width; - dvdy = (*v1 - *v0) / *height; - - if( *x < gameui.ds.scissor_x ) - { - *u0 += (gameui.ds.scissor_x - *x) * dudx; - *width -= gameui.ds.scissor_x - *x; - *x = gameui.ds.scissor_x; - } - - if( *x + *width > gameui.ds.scissor_x + gameui.ds.scissor_width ) - { - *u1 -= (*x + *width - (gameui.ds.scissor_x + gameui.ds.scissor_width)) * dudx; - *width = gameui.ds.scissor_x + gameui.ds.scissor_width - *x; - } - - if( *y < gameui.ds.scissor_y ) - { - *v0 += (gameui.ds.scissor_y - *y) * dvdy; - *height -= gameui.ds.scissor_y - *y; - *y = gameui.ds.scissor_y; - } - - if( *y + *height > gameui.ds.scissor_y + gameui.ds.scissor_height ) - { - *v1 -= (*y + *height - (gameui.ds.scissor_y + gameui.ds.scissor_height)) * dvdy; - *height = gameui.ds.scissor_y + gameui.ds.scissor_height - *y; - } - return true; -} - /* ==================== PIC_DrawGeneric @@ -511,7 +463,7 @@ static void PIC_DrawGeneric( float x, float y, float width, float height, const } // pass scissor test if supposed - if( gameui.ds.scissor_test && !PIC_Scissor( &x, &y, &width, &height, &s1, &t1, &s2, &t2 )) + if( !CL_Scissor( &gameui.ds.scissor, &x, &y, &width, &height, &s1, &t1, &s2, &t2 )) return; ref.dllFuncs.R_DrawStretchPic( x, y, width, height, s1, t1, s2, t2, gameui.ds.gl_texturenum ); @@ -658,11 +610,7 @@ static void GAME_EXPORT pfnPIC_EnableScissor( int x, int y, int width, int heigh width = bound( 0, width, gameui.globals->scrWidth - x ); height = bound( 0, height, gameui.globals->scrHeight - y ); - gameui.ds.scissor_x = x; - gameui.ds.scissor_width = width; - gameui.ds.scissor_y = y; - gameui.ds.scissor_height = height; - gameui.ds.scissor_test = true; + CL_EnableScissor( &gameui.ds.scissor, x, y, width, height ); } /* @@ -673,11 +621,7 @@ pfnPIC_DisableScissor */ static void GAME_EXPORT pfnPIC_DisableScissor( void ) { - gameui.ds.scissor_x = 0; - gameui.ds.scissor_width = 0; - gameui.ds.scissor_y = 0; - gameui.ds.scissor_height = 0; - gameui.ds.scissor_test = false; + CL_DisableScissor( &gameui.ds.scissor ); } /* @@ -765,7 +709,7 @@ static void GAME_EXPORT pfnDrawCharacter( int ix, int iy, int iwidth, int iheigh t2 = t1 + size; // pass scissor test if supposed - if( gameui.ds.scissor_test && !PIC_Scissor( &x, &y, &width, &height, &s1, &t1, &s2, &t2 )) + if( !CL_Scissor( &gameui.ds.scissor, &x, &y, &width, &height, &s1, &t1, &s2, &t2 )) return; ref.dllFuncs.GL_SetRenderMode( kRenderTransTexture ); diff --git a/engine/client/client.h b/engine/client/client.h index b61848d0..8c324c1c 100644 --- a/engine/client/client.h +++ b/engine/client/client.h @@ -340,18 +340,23 @@ typedef struct qboolean valid; // all rectangles are valid } cl_font_t; +typedef struct scissor_state_s +{ + int x; + int y; + int width; + int height; + qboolean test; +} scissor_state_t; + typedef struct { + // scissor test + scissor_state_t scissor; + // temp handle const model_t *pSprite; // pointer to current SpriteTexture - // scissor test - int scissor_x; - int scissor_y; - int scissor_width; - int scissor_height; - qboolean scissor_test; - int renderMode; // override kRenderMode from TriAPI TRICULLSTYLE cullMode; // override CULL FACE from TriAPI @@ -378,14 +383,10 @@ typedef struct cl_predicted_player_s typedef struct { - int gl_texturenum; // this is a real texnum - // scissor test - int scissor_x; - int scissor_y; - int scissor_width; - int scissor_height; - qboolean scissor_test; + scissor_state_t scissor; + + int gl_texturenum; // this is a real texnum // holds text color rgba_t textColor; @@ -862,6 +863,9 @@ void pfnGetScreenFade( struct screenfade_s *fade ); physent_t *pfnGetPhysent( int idx ); struct msurface_s *pfnTraceSurface( int ground, float *vstart, float *vend ); movevars_t *pfnGetMoveVars( void ); +void CL_EnableScissor( scissor_state_t *scissor, int x, int y, int width, int height ); +void CL_DisableScissor( scissor_state_t *scissor ); +qboolean CL_Scissor( const scissor_state_t *scissor, float *x, float *y, float *width, float *height, float *u0, float *v0, float *u1, float *v1 ); _inline cl_entity_t *CL_EDICT_NUM( int n ) { From 6eae3471cf9d11abdf48452c77e812fa52d378bc Mon Sep 17 00:00:00 2001 From: Alibek Omarov Date: Sat, 4 Feb 2023 21:59:29 +0300 Subject: [PATCH 412/490] engine: client: font: fix consecutive newlines skipped, add flag to reset color after a newline --- engine/client/cl_font.c | 5 +++++ engine/client/cl_scrn.c | 10 ++++------ engine/client/client.h | 1 + 3 files changed, 10 insertions(+), 6 deletions(-) diff --git a/engine/client/cl_font.c b/engine/client/cl_font.c index 64d857e0..f3f2935e 100644 --- a/engine/client/cl_font.c +++ b/engine/client/cl_font.c @@ -213,6 +213,10 @@ int CL_DrawString( float x, float y, const char *s, rgba_t color, cl_font_t *fon draw_len = 0; y += font->charHeight; } + + if( FBitSet( flags, FONT_DRAW_RESETCOLORONLF )) + Vector4Copy( color, current_color ); + continue; } if( IsColorString( s )) @@ -275,6 +279,7 @@ void CL_DrawStringLen( cl_font_t *font, const char *s, int *width, int *height, if( height ) *height += font->charHeight; } + continue; } if( IsColorString( s )) diff --git a/engine/client/cl_scrn.c b/engine/client/cl_scrn.c index db343ce4..f476b162 100644 --- a/engine/client/cl_scrn.c +++ b/engine/client/cl_scrn.c @@ -150,8 +150,7 @@ same as r_speeds but for network channel void SCR_NetSpeeds( void ) { static char msg[MAX_SYSPATH]; - int x, y, height; - char *p, *start, *end; + int x, y; float time = cl.mtime[0]; static int min_svfps = 100; static int max_svfps = 0; @@ -201,7 +200,7 @@ void SCR_NetSpeeds( void ) y = 384; MakeRGBA( color, 255, 255, 255, 255 ); - CL_DrawString( x, y, msg, color, font, 0 ); + CL_DrawString( x, y, msg, color, font, FONT_DRAW_RESETCOLORONLF ); } /* @@ -218,8 +217,7 @@ void SCR_RSpeeds( void ) if( ref.dllFuncs.R_SpeedsMessage( msg, sizeof( msg ))) { - int x, y, height; - char *p, *start, *end; + int x, y; rgba_t color; cl_font_t *font = Con_GetCurFont(); @@ -227,7 +225,7 @@ void SCR_RSpeeds( void ) y = 64; MakeRGBA( color, 255, 255, 255, 255 ); - CL_DrawString( x, y, msg, color, font, 0 ); + CL_DrawString( x, y, msg, color, font, FONT_DRAW_RESETCOLORONLF ); } } diff --git a/engine/client/client.h b/engine/client/client.h index 8c324c1c..d4e40441 100644 --- a/engine/client/client.h +++ b/engine/client/client.h @@ -326,6 +326,7 @@ typedef struct #define FONT_DRAW_FORCECOL BIT( 2 ) // ignore colorcodes #define FONT_DRAW_NORENDERMODE BIT( 3 ) // ignore font's default rendermode #define FONT_DRAW_NOLF BIT( 4 ) // ignore \n +#define FONT_DRAW_RESETCOLORONLF BIT( 5 ) // yet another flag to simulate consecutive Con_DrawString calls... typedef struct { From f910f4896ca7883a3819573f694dbaf475d17479 Mon Sep 17 00:00:00 2001 From: Alibek Omarov Date: Sat, 4 Feb 2023 22:53:10 +0300 Subject: [PATCH 413/490] engine: client: font: finally add support for tab character in engine --- engine/client/cl_font.c | 37 +++++++++++++++++++++++++++++++++++-- 1 file changed, 35 insertions(+), 2 deletions(-) diff --git a/engine/client/cl_font.c b/engine/client/cl_font.c index f3f2935e..b9fe68eb 100644 --- a/engine/client/cl_font.c +++ b/engine/client/cl_font.c @@ -135,6 +135,18 @@ void CL_FreeFont( cl_font_t *font ) memset( font, 0, sizeof( *font )); } +static int CL_CalcTabStop( const cl_font_t *font, int x ) +{ + int space = font->charWidths[' ']; + int tab = space * 6; // 6 spaces + int stop = tab - x % tab; + + if( stop < space ) + return tab * 2 - x % tab; // select next + + return stop; +} + int CL_DrawCharacter( float x, float y, int number, rgba_t color, cl_font_t *font, int flags ) { wrect_t *rc; @@ -145,6 +157,16 @@ int CL_DrawCharacter( float x, float y, int number, rgba_t color, cl_font_t *fon if( !font || !font->valid || y < -font->charHeight ) return 0; + // check if printable + if( number <= 32 ) + { + if( number == ' ' ) + return font->charWidths[' ']; + else if( number == '\t' ) + return CL_CalcTabStop( font, x ); + return 0; + } + if( FBitSet( flags, FONT_DRAW_UTF8 )) number = Con_UtfProcessChar( number & 255 ); else number &= 255; @@ -154,7 +176,7 @@ int CL_DrawCharacter( float x, float y, int number, rgba_t color, cl_font_t *fon R_GetTextureParms( &texw, &texh, font->hFontTexture ); if( !texw || !texh ) - return 0; + return font->charWidths[number]; rc = &font->fontRc[number]; if( font->nearest || font->scale <= 1.0f ) @@ -241,7 +263,12 @@ int CL_DrawString( float x, float y, const char *s, rgba_t color, cl_font_t *fon void CL_DrawCharacterLen( cl_font_t *font, int number, int *width, int *height ) { if( !font || !font->valid ) return; - if( width ) *width = font->charWidths[number & 255]; + if( width ) + { + if( number == '\t' ) + *width = CL_CalcTabStop( font, 0 ); // at least return max tabstop + else *width = font->charWidths[number & 255]; + } if( height ) *height = font->charHeight; } @@ -281,6 +308,12 @@ void CL_DrawStringLen( cl_font_t *font, const char *s, int *width, int *height, } continue; } + else if( *s == '\t' ) + { + draw_len += CL_CalcTabStop( font, 0 ); // at least return max tabstop + s++; + continue; + } if( IsColorString( s )) { From 2109d49aa35eddd59cd9b2b924e2d170f5cf15d0 Mon Sep 17 00:00:00 2001 From: Alibek Omarov Date: Sun, 5 Feb 2023 00:06:14 +0300 Subject: [PATCH 414/490] ci: disable xash-extras fetching, it's a submodule now --- .github/workflows/c-cpp.yml | 8 -------- 1 file changed, 8 deletions(-) diff --git a/.github/workflows/c-cpp.yml b/.github/workflows/c-cpp.yml index 31384ebd..e86788ac 100644 --- a/.github/workflows/c-cpp.yml +++ b/.github/workflows/c-cpp.yml @@ -51,18 +51,10 @@ jobs: uses: actions/checkout@v3 with: submodules: recursive - - name: Checkout xash-extras - uses: actions/checkout@v3 - with: - repository: FWGS/xash-extras - path: xash-extras - - name: Install dependencies run: bash scripts/gha/deps_${{ matrix.targetos }}.sh - - name: Build engine run: bash scripts/gha/build_${{ matrix.targetos }}.sh - - name: Upload engine (prereleases) run: bash scripts/continious_upload.sh artifacts/* - name: Upload engine (artifacts) From 0ffd1b9ff101697bf2132a22e080cd2b5ed66e24 Mon Sep 17 00:00:00 2001 From: Alibek Omarov Date: Sun, 5 Feb 2023 00:06:37 +0300 Subject: [PATCH 415/490] github: try to enable building flatpak bundle --- .github/workflows/c-cpp.yml | 25 +++++++++++++++++++++++++ 1 file changed, 25 insertions(+) diff --git a/.github/workflows/c-cpp.yml b/.github/workflows/c-cpp.yml index e86788ac..3b30e109 100644 --- a/.github/workflows/c-cpp.yml +++ b/.github/workflows/c-cpp.yml @@ -62,3 +62,28 @@ jobs: with: name: artifact-${{ matrix.targetos }}-${{ matrix.targetarch }} path: artifacts/* + flatpak: + name: "Flatpak" + runs-on: ubuntu-latest + strategy: + matrix: + include: + - app: su.xash.Engine.Compat.i386 + container: + image: bilelmoussaoui/flatpak-github-actions:freedesktop-22.08 + options: --privileged + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + UPLOADTOOL_ISPRERELEASE: true + steps: + - name: Checkout + uses: actions/checkout@v3 + with: + submodules: recursive + - name: Build flatpak (Compat.i386) + uses: FWGS/flatpak-github-actions/flatpak-builder@v5 + with: + bundle: ${{ matrix.app }}.flatpak + manifest-path: scripts/flatpak/${{ matrix.app }}.yml + - name: Upload engine (prereleases) + run: bash scripts/continious_upload.sh ${{ matrix.app }}.flatpak From 00ddd95c27c5789587957db700f15f08e2e22c63 Mon Sep 17 00:00:00 2001 From: Alibek Omarov Date: Sun, 5 Feb 2023 02:25:21 +0300 Subject: [PATCH 416/490] scripts: flatpak: try to add ourselves to desktop menu entries --- scripts/flatpak/su.xash.Engine.Compat.i386.desktop | 10 ++++++++++ scripts/flatpak/su.xash.Engine.Compat.i386.yml | 4 ++++ 2 files changed, 14 insertions(+) create mode 100644 scripts/flatpak/su.xash.Engine.Compat.i386.desktop diff --git a/scripts/flatpak/su.xash.Engine.Compat.i386.desktop b/scripts/flatpak/su.xash.Engine.Compat.i386.desktop new file mode 100644 index 00000000..cec00eca --- /dev/null +++ b/scripts/flatpak/su.xash.Engine.Compat.i386.desktop @@ -0,0 +1,10 @@ +[Desktop Entry] +Categories=Game;Shooter; +Comment=Half-Life compatible game engine +Exec= +Icon=su.xash.Engine.Compat.i386 +Keywords=first;person;shooter;multiplayer;half-life;halflife;singleplayer; +Name=Xash3D FWGS +PrefersNonDefaultGPU=true +Terminal=false +Type=Application diff --git a/scripts/flatpak/su.xash.Engine.Compat.i386.yml b/scripts/flatpak/su.xash.Engine.Compat.i386.yml index a2ac1c75..06b3a205 100644 --- a/scripts/flatpak/su.xash.Engine.Compat.i386.yml +++ b/scripts/flatpak/su.xash.Engine.Compat.i386.yml @@ -76,7 +76,11 @@ modules: python waf build python waf install --destdir=/ mkdir -p /app/bin + mkdir -p /app/share/icons/hicolor/256x256/apps/ + mkdir -p /app/share/applications install -m 0755 scripts/flatpak/run.sh /app/bin/run.sh + install -m 0644 game_launch/icon-xash-material.png /app/share/icons/hicolor/256x256/apps/su.xash.Engine.Compat.i386.png + install -m 0644 scripts/flatpak/su.xash.Engine.Compat.i386.desktop /app/share/applications/su.xash.Engine.Compat.i386.desktop sources: - type: dir path: ../../ From 9cb867a7d4a07dcbb4aeb2a40709154041a2be15 Mon Sep 17 00:00:00 2001 From: Alibek Omarov Date: Sun, 5 Feb 2023 02:42:35 +0300 Subject: [PATCH 417/490] filesystem: wad: print errno if wad can't be opened --- filesystem/wad.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/filesystem/wad.c b/filesystem/wad.c index adff3ad1..16153429 100644 --- a/filesystem/wad.c +++ b/filesystem/wad.c @@ -301,7 +301,7 @@ static wfile_t *W_Open( const char *filename, int *error ) if( wad->handle == NULL ) { - Con_Reportf( S_ERROR "W_Open: couldn't open %s\n", filename ); + Con_Reportf( S_ERROR "W_Open: couldn't open %s: %s\n", filename, strerror( errno )); if( error ) *error = WAD_LOAD_COULDNT_OPEN; FS_CloseWAD( wad ); return NULL; From f63f1a0dc6b8f274b8e1b9acc6a924a5d061cb5c Mon Sep 17 00:00:00 2001 From: Alibek Omarov Date: Sun, 5 Feb 2023 02:43:36 +0300 Subject: [PATCH 418/490] scripts: flatpak: add another default Steam library path --- scripts/flatpak/su.xash.Engine.Compat.i386.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/scripts/flatpak/su.xash.Engine.Compat.i386.yml b/scripts/flatpak/su.xash.Engine.Compat.i386.yml index 06b3a205..7b495f9b 100644 --- a/scripts/flatpak/su.xash.Engine.Compat.i386.yml +++ b/scripts/flatpak/su.xash.Engine.Compat.i386.yml @@ -17,6 +17,7 @@ finish-args: - --socket=pulseaudio - --share=network - --filesystem=~/.steam:ro + - --filesystem=~/.local/share/Steam:ro - --filesystem=~/.xash:rw - --filesystem=xdg-run/app/com.discordapp.Discord:create - --device=all From 30b698067a00c57d3c50d6fd39e967c82e853d3d Mon Sep 17 00:00:00 2001 From: Alibek Omarov Date: Sun, 5 Feb 2023 03:25:07 +0300 Subject: [PATCH 419/490] scripts: flatpak: allow easy debugger attach --- scripts/flatpak/run.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scripts/flatpak/run.sh b/scripts/flatpak/run.sh index 725161c5..60127680 100644 --- a/scripts/flatpak/run.sh +++ b/scripts/flatpak/run.sh @@ -17,4 +17,4 @@ export XASH3D_BASEDIR="$XASHDATADIR" echo "Base directory is $XASH3D_BASEDIR" export XASH3D_EXTRAS_PAK1=/app/share/xash3d/valve/extras.pk3 -exec /app/lib32/xash3d/xash3d "$@" +exec $DEBUGGER /app/lib32/xash3d/xash3d "$@" From a610b1545b7dae91be5feada42953f4a42a03e6b Mon Sep 17 00:00:00 2001 From: Alibek Omarov Date: Sun, 5 Feb 2023 03:41:58 +0300 Subject: [PATCH 420/490] scripts: flatpak: check .local directory for steam library --- scripts/flatpak/run.sh | 14 +++++++++----- 1 file changed, 9 insertions(+), 5 deletions(-) mode change 100644 => 100755 scripts/flatpak/run.sh diff --git a/scripts/flatpak/run.sh b/scripts/flatpak/run.sh old mode 100644 new mode 100755 index 60127680..e347669e --- a/scripts/flatpak/run.sh +++ b/scripts/flatpak/run.sh @@ -3,12 +3,16 @@ echo "Xash3D FWGS installed as Flatpak." # TODO: detect by libraryfolders.vdf and installed apps -HALFLIFESTEAMDIR="$HOME/.steam/steam/steamapps/common/Half-Life" +HALFLIFESTEAMDIRS="$HOME/.local/share/Steam/steamapps/common/Half-Life $HOME/.steam/steam/steamapps/common/Half-Life" -if [ -d "$HALFLIFESTEAMDIR" ]; then - echo "Detected Half-Life installation in $HALFLIFESTEAMDIR, using as RoDir" - export XASH3D_RODIR=$HALFLIFESTEAMDIR -fi +for i in $HALFLIFESTEAMDIRS; do +# echo $i + if [ -d "$i" ]; then + echo "Detected Half-Life installation in $i, using as RoDir" + export XASH3D_RODIR=$i + break + fi +done XASHDATADIR="$HOME/.xash/" From 25a1cb8ce725286575d60fbe64193f882c22db71 Mon Sep 17 00:00:00 2001 From: fgsfds Date: Sun, 5 Feb 2023 02:09:32 +0100 Subject: [PATCH 421/490] Nintendo Switch support (again) --- common/backends.h | 1 + common/defaults.h | 11 +- common/port.h | 9 +- engine/common/host.c | 2 +- engine/common/imagelib/img_png.c | 3 + engine/common/sys_con.c | 5 + engine/common/system.c | 21 +++- engine/common/whereami.c | 2 +- engine/platform/nswitch/icon.jpg | Bin 0 -> 7846 bytes engine/platform/nswitch/sys_nswitch.c | 140 +++++++++++++++++++++ engine/platform/nswitch/xash3d-fwgs.nacp | Bin 0 -> 16384 bytes engine/platform/platform.h | 5 + engine/platform/posix/lib_posix.c | 1 - engine/platform/posix/sys_posix.c | 2 +- engine/platform/sdl/sys_sdl.c | 6 + engine/server/sv_game.c | 2 +- engine/wscript | 61 ++++++--- public/build.c | 2 + public/build.h | 5 +- public/buildenums.h | 3 + scripts/waifulib/compiler_optimizations.py | 4 + scripts/waifulib/nswitch.py | 64 ++++++++++ scripts/waifulib/xcompile.py | 100 ++++++++++++++- wscript | 24 +++- 24 files changed, 441 insertions(+), 32 deletions(-) create mode 100644 engine/platform/nswitch/icon.jpg create mode 100644 engine/platform/nswitch/sys_nswitch.c create mode 100644 engine/platform/nswitch/xash3d-fwgs.nacp create mode 100644 scripts/waifulib/nswitch.py diff --git a/common/backends.h b/common/backends.h index be3cdaa6..77bcced8 100644 --- a/common/backends.h +++ b/common/backends.h @@ -47,6 +47,7 @@ GNU General Public License for more details. #define MSGBOX_SDL 1 #define MSGBOX_ANDROID 2 #define MSGBOX_WIN32 3 +#define MSGBOX_NSWITCH 4 // library loading (XASH_LIB) diff --git a/common/defaults.h b/common/defaults.h index e992ef4d..2144b3be 100644 --- a/common/defaults.h +++ b/common/defaults.h @@ -47,7 +47,9 @@ SETUP BACKENDS DEFINITIONS #endif // XASH_TIMER #ifndef XASH_MESSAGEBOX - #define XASH_MESSAGEBOX MSGBOX_SDL + #if !XASH_NSWITCH // SDL2 messageboxes not available + #define XASH_MESSAGEBOX MSGBOX_SDL + #endif #endif // XASH_MESSAGEBOX #endif #elif XASH_ANDROID @@ -105,6 +107,8 @@ SETUP BACKENDS DEFINITIONS #ifndef XASH_MESSAGEBOX #if XASH_WIN32 #define XASH_MESSAGEBOX MSGBOX_WIN32 + #elif XASH_NSWITCH + #define XASH_MESSAGEBOX MSGBOX_NSWITCH #else // !XASH_WIN32 #define XASH_MESSAGEBOX MSGBOX_STDERR #endif // !XASH_WIN32 @@ -177,4 +181,9 @@ Default build-depended cvar and constant values #define DEFAULT_FULLSCREEN 1 #endif // DEFAULT_FULLSCREEN +#if XASH_NSWITCH + #define DEFAULT_MODE_WIDTH 1280 + #define DEFAULT_MODE_HEIGHT 720 +#endif // XASH_NSWITCH + #endif // DEFAULTS_H diff --git a/common/port.h b/common/port.h index 05a732da..bf55e171 100644 --- a/common/port.h +++ b/common/port.h @@ -39,8 +39,13 @@ GNU General Public License for more details. #if XASH_POSIX #include - #include - #define HAVE_DUP + #ifdef XASH_NSWITCH + #define SOLDER_LIBDL_COMPAT + #include + #else + #include + #define HAVE_DUP + #endif #define O_BINARY 0 #define O_TEXT 0 #define _mkdir( x ) mkdir( x, S_IRWXU | S_IRWXG | S_IROTH | S_IXOTH ) diff --git a/engine/common/host.c b/engine/common/host.c index c0515eae..e558b285 100644 --- a/engine/common/host.c +++ b/engine/common/host.c @@ -1013,7 +1013,7 @@ void Host_InitCommon( int argc, char **argv, const char *progname, qboolean bCha #if TARGET_OS_IOS const char *IOS_GetDocsDir(); Q_strncpy( host.rootdir, IOS_GetDocsDir(), sizeof(host.rootdir) ); -#elif XASH_SDL == 2 +#elif (XASH_SDL == 2) && !XASH_NSWITCH // GetBasePath not impl'd in switch-sdl2 char *szBasePath; if( !( szBasePath = SDL_GetBasePath() ) ) diff --git a/engine/common/imagelib/img_png.c b/engine/common/imagelib/img_png.c index 8056ac7c..2a4c742d 100644 --- a/engine/common/imagelib/img_png.c +++ b/engine/common/imagelib/img_png.c @@ -20,6 +20,9 @@ GNU General Public License for more details. #if defined(XASH_NO_NETWORK) #include "platform/stub/net_stub.h" +#elif XASH_NSWITCH + // our ntohl is here + #include #elif !XASH_WIN32 #include #endif diff --git a/engine/common/sys_con.c b/engine/common/sys_con.c index b0a241a6..6a0c3dd2 100644 --- a/engine/common/sys_con.c +++ b/engine/common/sys_con.c @@ -264,6 +264,11 @@ static void Sys_PrintStdout( const char *logtime, const char *msg ) void IOS_Log( const char * ); IOS_Log( buf ); #endif // TARGET_OS_IOS + +#if XASH_NSWITCH && NSWITCH_DEBUG + // just spew it to stderr normally in debug mode + fprintf( stderr, "%s %s", logtime, buf ); +#endif // XASH_NSWITCH && NSWITCH_DEBUG #elif !XASH_WIN32 // Wcon does the job Sys_PrintLogfile( STDOUT_FILENO, logtime, msg, XASH_COLORIZE_CONSOLE ); Sys_FlushStdout(); diff --git a/engine/common/system.c b/engine/common/system.c index 6df8ffe6..d7d08165 100644 --- a/engine/common/system.c +++ b/engine/common/system.c @@ -26,7 +26,6 @@ GNU General Public License for more details. #if XASH_POSIX #include #include -#include #if !XASH_ANDROID #include @@ -37,6 +36,10 @@ GNU General Public License for more details. #include #endif +#if XASH_NSWITCH +#include +#endif + #include "menu_int.h" // _UPDATE_PAGE macro #include "library.h" @@ -126,7 +129,7 @@ const char *Sys_GetCurrentUser( void ) if( GetUserName( s_userName, &size )) return s_userName; -#elif XASH_POSIX && !XASH_ANDROID +#elif XASH_POSIX && !XASH_ANDROID && !XASH_NSWITCH uid_t uid = geteuid(); struct passwd *pw = getpwuid( uid ); @@ -566,6 +569,19 @@ it explicitly doesn't use internal allocation or string copy utils */ qboolean Sys_NewInstance( const char *gamedir ) { +#if XASH_NSWITCH + char newargs[4096]; + const char *exe = host.argv[0]; // arg 0 is always the full NRO path + + // TODO: carry over the old args (assuming you can even pass any) + Q_snprintf( newargs, sizeof( newargs ), "%s -game %s", exe, gamedir ); + // just restart the entire thing + printf( "envSetNextLoad exe: `%s`\n", exe ); + printf( "envSetNextLoad argv:\n`%s`\n", newargs ); + Host_Shutdown( ); + envSetNextLoad( exe, newargs ); + exit( 0 ); +#else int i = 0; qboolean replacedArg = false; size_t exelen; @@ -613,6 +629,7 @@ qboolean Sys_NewInstance( const char *gamedir ) free( newargs[i] ); free( newargs ); free( exe ); +#endif return false; } diff --git a/engine/common/whereami.c b/engine/common/whereami.c index e7958363..0a0fb570 100644 --- a/engine/common/whereami.c +++ b/engine/common/whereami.c @@ -795,7 +795,7 @@ int WAI_PREFIX(getModulePath)(char* out, int capacity, int* dirname_length) return length; } -#elif defined(__sgi) +#elif defined(__sgi) || defined(__SWITCH__) /* * These functions are stubbed for now to get the code compiling. diff --git a/engine/platform/nswitch/icon.jpg b/engine/platform/nswitch/icon.jpg new file mode 100644 index 0000000000000000000000000000000000000000..3bb62ec231e41f118705343c492fb1355859bc85 GIT binary patch literal 7846 zcmb_gbyQW)w?8-O?vh4ArMnw0AY4Kuqyz<&PLa4srvfSiF#{-GjV?J? z1q_COFAEd`0YhL=7z_r5LSb+$I1GjbheF{va4hW00)yk=;$Y)m4lWnD)Vy&C zv2iY@0ep-im>5C~0HB{|7r+7P-&Ou^6u8n9ZBk(H-^%wQ_0`efqitqX1lUbkXDrnolxxE@wc-0*fiNF^>c!AFkxnj(`3@u3>)x zc<y%4}`fA}-23k3HHvh`8B(sYH>*!Zj-PQ{vNu!!LsiQ2h;A7kh2P zFN&7pb6R{UCM&;e?jy>X3;ec<(;m9NQ}&FuLt(VQ`-YhU4_Qaqepz`}@APdPYrk9r zk94eLu-^X7x>vF?VD{y>BjNa3lCWr@lWoqp**eIT(*SX)T0*0v52mOwHJx@WO)+e8 zGNBC^_xha>GWlz!{@C4wdbXc!O*?xcl+h9+jCqs0bu&QXU~VzCQeND zDl?`|d&V!ESh?ec^dB} zoPhjY5(b51SP(2?3OQ18N+uFA5fl|OKgN2oFO3@p{*AjV_V&VoVB9PuGqAzxe3ti=+$G1A{D1*NY1f{I@3IU5rR(`p40OVbbF0psTM)HV-$ zVZ<+p2Lw*rR;$A187OS4XkBp_N~&(U$yB+Y?eHtA+E8Vcx2Kg+*mpoFCO>)zf7f_B zntv_CWKpXN_5MCfqyo)&Tc;Kg8!wkHf;+X~wW^o?ieMJbXO}|9@m^k&&o|B=wN|N4 zc~tm?PZO>5PyYP=*xENz1U;XWyk%8s(wsC8?cCuINZa7PW}YL9`LjwS7^{Us@L|}v zfAJdgR{2tlM5Z%ZkrqFf(*Ubw8yHz4JAj)OVB4TmoLZk{x5 z8a}M1EUlC-2`SnldZfQ>b&Y};%PV1(qF=D=vFKj2V0WocE7gaFJF5<~_qdFm2#^0m zTxSw*$%-i@l7s%F%h~uQZbr_qPGsk+uM8{ya0~zn}$ajZ8)Yv>nqnl76GDAYH8=WVosJ3 zPJth{=LF%73gCI`0^a%yVD)34E_ZFTLL64ul9RglZ42qowgb#&lxHbFKdJ>yQ-+CT zl`pY4+BMZb&vf2g?8zirlR!TV=~Uv>R4&-!y(3C0xp*QGNf%`^9fPJGI3@HZz%?hk zOJ5Z$7uq2bE3Xq?$1t$Op&0z;^+?Vl(Yjb^aa~PFkY{GWIj?U=DaUY8QDB3Ir|3%p znG?mYs>puRm5s87l5gXl&*c&$WH^>K$@sfYq~1g!+nH~1)zpVtovDnhl*Qij8pgN( z7FrKagaOGG^rKDOiW{=f7aE7-mnM^`c65BS(XnCcAFE9?53;fh$&;KON>gd6PcqCec%5 z&w{i7{=2qRav#&!?%;lx7jE`AO1jV5!k6AQK4AF%q3u!behH_L*mmy*fqcURM43fH zMv7t0C3#SgF8_x6ATEahvHiO|Sgay$`Nv+D5XZ-5+-mUc+DCQ%>M6!0&y$Soyh8wwZqSetBi##)A2^wK*>@i9Bcm zPLhY<&&YRuG;_|4;wlPMzUf8avzoZosWx#Lo8!<(Od7UEWZ+dk@vU zL=&XLZ58cEuiAcM98DMVd;F!wnvV&=lhIA<-%)d23W@C#hxT}tl;I_`;c-3E=9`jH zj}$F?BqZaefH-VYxQ~aHb$?`rKnh>b9c5f8LXL+{_4LaOi;IPugs<8yKTLvK(++Dz z(-4Z=54Y?yV;kBn?4+7#kB(dY6wAWzz4Hl(nC0_w87)c@gJqq0^F{xRX?>|=O`x|A zWhz^cXvxzPuW{t%@t!N;##LJq(&My?NDb=p5-}pkUjWYnKHWUL@00yAMNG|Ug7#xg+{rB#{_=*^Sf)kUF@spA>3CPLwl~XVyb@c+{ zC|THqES;}V=?av<)}?XAEf(Mi_2V zZ+Rb6E%l=Q^r|yZYIks=(f*l5z*Cjh%c`7_&AR0P_FG!4*iWtgIje1ae8omM)IKoy z6!zd%o1V-sF){Dfq~=&EqYGdV>(>W@+#KuLRQjA1KX770|ZQ#;p>y zX&xXeo5sfXXnP5*_7)(mRP9QO%$@AH1Yso2<1FbCf&{Et^QYQ~0Vgr>u-+RvywG{= zW#8H1is?EZHtoUGw!*bvtBPjq^j7@_US6)xUp5>;kz~-O34@>7UWCY>t25??q}fJ= zxlfHA_DhMxmedQPe%?xBwOjx8B9+SvIkH7M3f{0?u>H`m4ivnOpfHZml6qIF!nL4h zjj&v@wS28yCdWX!N3P#u)u>@)c&PWveGNEunOeW|V}iZkRJhJqOCaNXE~6QFFaC4( zZ8~0!0XMN@)xn=^rqWc1;Yth%`R4&vw!_aeBS8 z*Rf>1uH!|~;*p_@dB?WUE!;rc@;kXNi+ZQmW+DQ#NytL`bt_B;WUTg@igaim7>tL_ zUnkH!xchj_(AFdJrWQw%;`aP;0o?2Y;QSWsgR8xmZOkS2z)rrhMS|FE#xE_$iExXG zbH`YsdhZa@EnzNSSX?w&%Dn+rD~NP99vV#S%OvZB@td-w$+F)1By@|-$5Z?3pT<~H zY5ul;tyNB&^@dtTeO;X$-Hh3u-~FeL=CzBR9G>GB6%aCLpZX zvPdqXXLaAj)eV(UL8%azHS*U1gUf<{!|X-I9P+mh=rFw}0$T_7nu~e+Owz+PN6U1A z+E8B+({BD@?vYS7OZr>ag{HqXdxbV4Ejb>?^{^|ZIDPv^Dp5ba4-5Fp@x$c_9wMDH zL}tBrynHx--@JL-%Zf}%kDdGA-ezjQ*P24Mh!(eWpYK5(nRN!h4wwRc$}6b5t>Mf2 z`Pn&)w#DNWy7q1^;U(f-t-Lj+s!j7X7Xab{SbjzLN>?+2S*&c4Yhioz)+>I|%5}$& zg{~~0*4*?$4qt`s62?7XZnx7`cag_d>z@`DE{d8@>DwrkmV8CfSLhltOFQEv_`ah+ z!EK<&?rxZqO{Rg>Tb_t-*Yk@z(`JIl52y>ip~OrdE!sRmO20$;-bKf#4;b5wbCf~| zu2X%Pvhy&CZ2U~oED_O7kmbf9>2V8LAExJM?Kh23YcRsE2(J93+@m%IkCT74wZqEu zigj9i=gW$JSM4Z$Qvo?N<#(ol%+`8JYm@*kZC1$KTlEqxrkyGxCi1|^vI+9#zOF~DWVh6%z zcw=fa#@_joFQ2~SZS)IGtS4&gCyfjYY~8q*}1kuRZ%j8E(wvhfkfZlj1e294cRHDhu?TGdzNhu)E2 z`yj0GhvAj^>WwQDrEK^;l?YQdDBmZ?GgSf1nbc@@e=;7e8-6~v^!AjIJ+Ho9$~Jia z`*Z(fVw-A)=yhDmgrv$hk6M&db_G6WtC;*?|K+(USuR{G%%$D{DXHFLIN9$lXmoML zzsdTpAtd|u>qSqELLTGT2Lo!B9YTBL+V@;Z#?4-98K=>@I1P77)A`Ql z9XZHQh^HrX)N*65Hat$}c^$QeRMR~lG2-}42tpSqL1W2mL}^;>kUfg5?5X29d9QOE z-5OGZ58Zzm5ecEI$N1TzJNE*XKb(1r%yfqC67%!uoNiIRqU*(VY2|hC`We+fvqB)- z;gl+L0Z_yU_N)i#eo62#HF=n#60qXq`?d(-kZ80R`+>rf&EsjwhD5+N$Nc-e^F4|)-v&lwpi5-uHH8K_-`Nw!>gbfVd5=s?{J-K>J@xn(iZ%mbOZx5>5DER23%oLI^ zLbo#@QMTAuX^1jJ8C14i(<5qR*8`rJ1!lxr#%uwwibDbEqcX70`(vw9EjkJiFuVYg zyH#Z!+F*I4RlgVQ3R6>$s~Z1+-mR64dl@LDpwCV9d(|?#V%mx@^pjadH1$-Lt`w`d zmt$xbeR_2XW$VJP(L}k9k6IemRp1|Gwac^KE1r^{L|sMRlrzitiHW3nr-w|GqUS8K z^Mr4HR3N+$loTmXxgLHBmZuS;xz2nohjE4s+)R%+{koM^8}?-M^E1&Wk{+5o-+m+q zG$k}K6P&{XxYUx33`>P4XoHCot&fW4ogeDXI<#x78Iz8#usJeLjn6%)m))cGW%v~F zA`C5-!5qSBMEf%B5!q>xzwq}!c4|loXbZu+TkGY=zIGzvQ`y#GQ}>+?`=_9CIo$)v zWUp9Ghx}%xA_Q15u56ccvGpvM(a~zY&T>ZwSq;s+0LpJP+_#MXwRhciBD3G1!R7*x z-|l+%4I8_F+O5B2?nLtKySbEH-tM`D*7ZaBx6>}SS0d_<8A9vM1wgrH4`!1|V?D)I zJ!0rnk#Rk@`}mJ|Hb2%;qMp2`AgI;Ye(Bw}SMP;p0yn!a$dIaLs5m!Y02yl^{JF?i zRQ>ArwuEH-U&aLQ`~8U3G{G+*tu@y3u4_$p*pEErU2p0;DYeX!&NG$N&L-Ss@?bky zovHG3``%Rh)72Uv^CYLH~b{7+l~Xxx@ubY8H+O zldsmi!UcH@Ffh5ujqnA=we0LVXOu7MTp`0{EKPiV%M3lBrJX;OSzI5=`DIN*A$uQh#3&4rJ|(?zsO|GRVg5~gd&sqxs;$-&anb)$FMgWUJw1RYt6#a*0FRsC=2 zB0wU9gX5;O9(5e8Jv|QXi}GuCie5bkd#ctk#b}Br5PJ!_iVzJRqxCbUi2&-cq=L z*wH7C{ZSXjeF01mDTXdgDtp-Jqc^bXrjRt{M=T6(}soACIpN}ONN5#MHw-nQe9lCqW?3mBZ zs4F+o*O>|#DQymGXZ8909HN^LJZE&`P(H4IL#aEFtW6zIZKvE36;IGsFQ2tk!&Pcx zcwS?%;52=9r+vIOW_&2QNOAZXwNjka+qkU`s|Kf-ov(gvg%WDu^=fbmLaqDt?RSTW zq6AsiRrQQD`a`8vmqLqWlhYPMk?SDzBr%pzR0iI=Yxq>0*=BqklgE?`Vg9A2!9psC z2VlO+3U9ZT!Kc;9O;R%_L$V}K-3XAH_f6lz^`zCu9&Z{w8J<&!5(jLdejfKsMg#?- zSrlZs?V`NwtFQZ}rXJwK@XGijc5nK#PUkHYSA^r$(|GJlN;{+P_50rw*HAK|%E)?E zj_lwUrdc?RiOsE8`9&1u#J8qhU>mDcAtNa(`f)@aArNR$*TP5pj?h-~I?3G~b4AID z4fF6{rR`5g@~vf)8Tr2xoGLe#QMg-~37-u<+DWhSCp#={^&h)UfR6PM-FJ}RVL|6} z<33l3jb=O|J~y}w__!|vJ}4&H_|F}Rv3?R}etC?o$RPt=%3JtG7I)(^cAT$lx8;#;@Pg}fn>>qn)b(R>`-^k9jOjusiDskjO1xbeU*gZ$Jo!1t4(<2EZvui8mm{3HKHvtnIg( zWuMp_48QxiaAF(QTMj%%L2xV(&&NUrv;%|q1Qek7cTdU~Y1&AZNS28e%T8G-0Z%LD zL4hrz&aPl07JpBv41pyJ@`dlWzt?mQQK%;4$E&E#Cag;t0mC$K>@I%BlMeMW_VWC1 z)JC;FAIWeTmD)1%-k6XbH3!5Zq0d)2g*c!_!T3lyE^+|Oz~QEzbFOvh8Vq-*(CIBAc8~gG1t#5k{dch z?23YA1&0a*s5Y{U8bM`~Koduy=gLV0FL{d}bBb-hP9)(urD56Oqh$G}g2&=~Z1K9| zoMD$s5=0sv71&bErvSeugQd>ORJ?=xmbls?;F?QxS~&W9^?I5(gi)3`T*H|kch_oG zZ$;32Ov>a2d`Cw-x${vb;AktGA4wM1OT-FtcFbP^CeQk1Lkdu2lUG~dj=orKD}Oscd`Sg?5^;OQ`U?vzN=LQn=9%ish$K-+z{i7BMbu$tB~E;f%1~T_Xsv3zaCoEYzG+b{!n>FZM z4X;!^-3%e2wpAq2H2;7#Nz|@Zmy<2t0vd|D-G)G<7JaA-QdHUOK8{Oe>DF`9R9LjK zh!lEmNc(a=Z_5+2FLZpH;_;eh;2Via1$Z*grv_7uU9b%1Bi697*cV+S7L-W+KkW{7YeLY|s zSTw={@N{+~hF%ROEg0QtTxEO`zMetwL-HU@ z1cl;5NCEay%YHG5U=oEP)*r{CN|thqcsUtC>bBa6XQl`OVZm#xWxV%=V>a1cK#LYu zyt)xe&iW93QGAv-?BPNUGE@=)V4tzoMa0}4B8I_X{EX)d`Kwxip951YBkn!$jr1ebdbh_ndJ-Ta0xN$0%)Wkg^8to1!fvb1{V z9~p@~FE}T|4|k?$?mouvCgVZdOFoSb^lh^krY&{Ls~sNJwO6EsYErj1_?nfuhV?-c j@jS6w%o@GsYMO;;sJCH!9Z!3Iz1rq7ym1Z-xS0AcFDT$Q literal 0 HcmV?d00001 diff --git a/engine/platform/nswitch/sys_nswitch.c b/engine/platform/nswitch/sys_nswitch.c new file mode 100644 index 00000000..8165f211 --- /dev/null +++ b/engine/platform/nswitch/sys_nswitch.c @@ -0,0 +1,140 @@ +/* +switch.c - switch backend +Copyright (C) 2021-2023 fgsfds + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. +*/ + +#include "platform/platform.h" +#include +#include +#include +#include +#include +#include +#include +#include +#include + +static int nxlink_sock = -1; + +/* HACKHACK: force-export stuff required by the dynamic libs */ + +// libunwind stuff for C++ exceptions, required by filesystem_stdio +extern void *_Unwind_GetIPInfo; +extern void *_Unwind_Resume_or_Rethrow; +extern void *_Unwind_GetRegionStart; +extern void *_Unwind_Resume; +extern void *_Unwind_DeleteException; +extern void *_Unwind_RaiseException; +extern void *_Unwind_SetIP; +extern void *_Unwind_GetTextRelBase; +extern void *_Unwind_GetLanguageSpecificData; +extern void *_Unwind_SetGR; +extern void *_Unwind_GetDataRelBase; + +// these are macros in our libc, so we need to wrap them +static int tolower_fn(int c) { return tolower(c); } +static int toupper_fn(int c) { return toupper(c); } +static int isalnum_fn(int c) { return isalnum(c); } +static int isalpha_fn(int c) { return isalpha(c); } + +static const solder_export_t aux_exports[] = +{ + SOLDER_EXPORT("tolower", tolower_fn), + SOLDER_EXPORT("toupper", toupper_fn), + SOLDER_EXPORT("isalnum", isalnum_fn), + SOLDER_EXPORT("isalpha", isalpha_fn), + SOLDER_EXPORT_SYMBOL(mkdir), + SOLDER_EXPORT_SYMBOL(remove), + SOLDER_EXPORT_SYMBOL(rename), + SOLDER_EXPORT_SYMBOL(fsync), + SOLDER_EXPORT_SYMBOL(strchrnul), + SOLDER_EXPORT_SYMBOL(stpcpy), + SOLDER_EXPORT_SYMBOL(_Unwind_GetIPInfo), + SOLDER_EXPORT_SYMBOL(_Unwind_Resume_or_Rethrow), + SOLDER_EXPORT_SYMBOL(_Unwind_GetRegionStart), + SOLDER_EXPORT_SYMBOL(_Unwind_Resume), + SOLDER_EXPORT_SYMBOL(_Unwind_DeleteException), + SOLDER_EXPORT_SYMBOL(_Unwind_RaiseException), + SOLDER_EXPORT_SYMBOL(_Unwind_SetIP), + SOLDER_EXPORT_SYMBOL(_Unwind_GetTextRelBase), + SOLDER_EXPORT_SYMBOL(_Unwind_GetLanguageSpecificData), + SOLDER_EXPORT_SYMBOL(_Unwind_SetGR), + SOLDER_EXPORT_SYMBOL(_Unwind_GetDataRelBase), +}; + +const solder_export_t *__solder_aux_exports = aux_exports; +const size_t __solder_num_aux_exports = sizeof(aux_exports) / sizeof(*aux_exports); + +/* end of export crap */ + +void Platform_ShellExecute( const char *path, const char *parms ) +{ + Con_Reportf( S_WARN "Tried to shell execute ;%s; -- not supported\n", path ); +} + +#if XASH_MESSAGEBOX == MSGBOX_NSWITCH +void Platform_MessageBox( const char *title, const char *message, qboolean unused ) +{ + // TODO: maybe figure out how to show an actual messagebox or an on-screen console + // without murdering the renderer + // assume this is a fatal error + FILE *f = fopen( "fatal.log", "w" ); + if ( f ) + { + fprintf( f, "%s:\n%s\n", title, message ); + fclose( f ); + } + // dump to nxlink as well + fprintf( stderr, "%s:\n%s\n", title, message ); +} +#endif // XASH_MESSAGEBOX == MSGBOX_NSWITCH + +// this gets executed before main(), do not delete +void userAppInit( void ) +{ + socketInitializeDefault( ); +#ifdef NSWITCH_DEBUG + nxlink_sock = nxlinkStdio( ); +#endif + if ( solder_init( 0 ) < 0 ) + { + fprintf( stderr, "solder_init() failed: %s\n", solder_dlerror() ); + fflush( stderr ); + exit( 1 ); + } +} + +// this gets executed on exit(), do not delete +void userAppExit( void ) +{ + solder_quit( ); + if ( nxlink_sock >= 0 ) + { + close( nxlink_sock ); + nxlink_sock = -1; + } + socketExit( ); +} + +void NSwitch_Init( void ) +{ + printf( "NSwitch_Init\n" ); +} + +void NSwitch_Shutdown( void ) +{ + printf( "NSwitch_Shutdown\n" ); + // force deinit everything SDL-related to avoid issues with changing games + if ( SDL_WasInit( 0 ) ) + SDL_Quit( ); +} diff --git a/engine/platform/nswitch/xash3d-fwgs.nacp b/engine/platform/nswitch/xash3d-fwgs.nacp new file mode 100644 index 0000000000000000000000000000000000000000..7f8c9c53e02eb0fdbd46b5f7b34c51c45b978e40 GIT binary patch literal 16384 zcmeIyI|{-;6op|QohH5ItU!EhL6QYnByB1a5SwV>ZnKk7i9rseOd*_q8o46|@9&3Y z)z-&T$cp?tB$G?j3 zn(&W*FY&r9{_(FOye9nP-%Gr1i+}v92(JnM`1ca8+u|SpD#B~RKmNVM>$dLxwFVOf zC_n)UP=Epypa2CZU|(R~7l`Yj#P{MoCNZvJ@;PZ5N6v10*bko?f9;n2{bu~_iEkIb fuRigUc^m~OKmiI+fC3bt00k&O0SZvye+#?-u-uLr literal 0 HcmV?d00001 diff --git a/engine/platform/platform.h b/engine/platform/platform.h index 0aaee2b5..ccf53067 100644 --- a/engine/platform/platform.h +++ b/engine/platform/platform.h @@ -51,6 +51,11 @@ void Platform_UpdateStatusLine( void ); static inline void Platform_UpdateStatusLine( void ) { } #endif +#if XASH_NSWITCH +void NSwitch_Init( void ); +void NSwitch_Shutdown( void ); +#endif + /* ============================================================================== diff --git a/engine/platform/posix/lib_posix.c b/engine/platform/posix/lib_posix.c index 746b73e7..ad97130e 100644 --- a/engine/platform/posix/lib_posix.c +++ b/engine/platform/posix/lib_posix.c @@ -15,7 +15,6 @@ GNU General Public License for more details. #define _GNU_SOURCE #include "platform/platform.h" #if XASH_LIB == LIB_POSIX -#include #ifdef XASH_IRIX #include "platform/irix/dladdr.h" #endif diff --git a/engine/platform/posix/sys_posix.c b/engine/platform/posix/sys_posix.c index c5371f98..3872f8da 100644 --- a/engine/platform/posix/sys_posix.c +++ b/engine/platform/posix/sys_posix.c @@ -67,7 +67,7 @@ static qboolean Sys_FindExecutable( const char *baseName, char *buf, size_t size return false; } -#if !XASH_ANDROID +#if !XASH_ANDROID && !XASH_NSWITCH void Platform_ShellExecute( const char *path, const char *parms ) { char xdgOpen[128]; diff --git a/engine/platform/sdl/sys_sdl.c b/engine/platform/sdl/sys_sdl.c index 0194c291..8c6fc13f 100644 --- a/engine/platform/sdl/sys_sdl.c +++ b/engine/platform/sdl/sys_sdl.c @@ -68,6 +68,9 @@ void Platform_Init( void ) #ifdef XASH_WIN32 Wcon_CreateConsole(); // system console used by dedicated server or show fatal errors #endif +#ifdef XASH_NSWITCH + NSwitch_Init(); +#endif SDLash_InitCursors(); } @@ -79,4 +82,7 @@ void Platform_Shutdown( void ) #ifdef XASH_WIN32 Wcon_DestroyConsole(); #endif +#ifdef XASH_NSWITCH + NSwitch_Shutdown(); +#endif } diff --git a/engine/server/sv_game.c b/engine/server/sv_game.c index d3d953dc..c2d84e01 100644 --- a/engine/server/sv_game.c +++ b/engine/server/sv_game.c @@ -3100,7 +3100,7 @@ void SV_SetStringArrayMode( qboolean dynamic ) #endif } -#if XASH_64BIT && !XASH_WIN32 && !XASH_APPLE +#if XASH_64BIT && !XASH_WIN32 && !XASH_APPLE && !XASH_NSWITCH #define USE_MMAP #include #endif diff --git a/engine/wscript b/engine/wscript index 8195d772..1639147e 100644 --- a/engine/wscript +++ b/engine/wscript @@ -48,6 +48,14 @@ def configure(conf): conf.options.NO_ASYNC_RESOLVE = True if not conf.check_cc( fragment='int main(){ int i = socket();}', lib = 'wattcpwl', mandatory=False ): conf.define('XASH_NO_NETWORK',1) + elif conf.env.DEST_OS == 'nswitch': + conf.load('sdl2') + if not conf.env.HAVE_SDL2: + conf.fatal('SDL2 not availiable! Install switch-sdl2!') + conf.define('XASH_SDL', 2) + # disallow undefined symbols + conf.env.append_unique('CXXFLAGS', '-Wl,--no-undefined') + conf.env.append_unique('CFLAGS', '-Wl,--no-undefined') elif conf.options.FBDEV_SW: # unused, XASH_LINUX without XASH_SDL gives fbdev & alsa support # conf.define('XASH_FBDEV', 1) @@ -123,7 +131,7 @@ def build(bld): if bld.env.DEST_OS == 'win32': libs += ['USER32', 'SHELL32', 'GDI32', 'ADVAPI32', 'DBGHELP', 'PSAPI', 'WS2_32' ] source += bld.path.ant_glob(['platform/win32/*.c']) - elif bld.env.DEST_OS != 'dos': #posix + elif bld.env.DEST_OS != 'dos' and bld.env.DEST_OS != 'nswitch': #posix libs += [ 'M', 'RT', 'PTHREAD', 'ASOUND'] if not bld.env.STATIC: libs += ['DL'] @@ -162,6 +170,11 @@ def build(bld): libs += ['LOG'] source += bld.path.ant_glob(['platform/android/*.cpp', 'platform/android/*.c', 'platform/linux/*.c']) + if bld.env.DEST_OS == 'nswitch': + libs += [ 'SOLDER' ] + source += bld.path.ant_glob(['platform/posix/*.c']) + source += bld.path.ant_glob(['platform/nswitch/*.c']) + # add client files if not bld.env.DEDICATED: source += bld.path.ant_glob([ @@ -172,22 +185,34 @@ def build(bld): includes = ['server', 'client', 'client/vgui' ] - if bld.env.SINGLE_BINARY: - install_path = bld.env.BINDIR - program = 'cxxprogram' if is_cxx_link else 'cprogram' - if bld.env.STATIC: - program += '_static' - features = ['c', program] + # Switch has custom parameters + if bld.env.DEST_OS == 'nswitch': + bld(source = source, + target = 'xash', + features = 'c cxxprogram', + includes = includes, + use = libs, + install_path = None, + nro_install_path = bld.env.BINDIR, + nacp = 'platform/nswitch/xash3d-fwgs.nacp', + icon = 'platform/nswitch/icon.jpg') else: - install_path = bld.env.LIBDIR - features = ['c', 'cxxshlib' if is_cxx_link else 'cshlib'] + if bld.env.SINGLE_BINARY: + install_path = bld.env.BINDIR + program = 'cxxprogram' if is_cxx_link else 'cprogram' + if bld.env.STATIC: + program += '_static' + features = ['c', program] + else: + install_path = bld.env.LIBDIR + features = ['c', 'cxxshlib' if is_cxx_link else 'cshlib'] - bld(source = source, - target = 'xash', - features = features, - includes = includes, - use = libs, - install_path = install_path, - subsystem = bld.env.MSVC_SUBSYSTEM, - rpath = '$ORIGIN' - ) + bld(source = source, + target = 'xash', + features = features, + includes = includes, + use = libs, + install_path = install_path, + subsystem = bld.env.MSVC_SUBSYSTEM, + rpath = '$ORIGIN' + ) diff --git a/public/build.c b/public/build.c index 758ef738..08827800 100644 --- a/public/build.c +++ b/public/build.c @@ -109,6 +109,8 @@ const char *Q_PlatformStringByID( const int platform ) return "serenity"; case PLATFORM_IRIX: return "irix"; + case PLATFORM_NSWITCH: + return "nswitch"; } assert( 0 ); diff --git a/public/build.h b/public/build.h index b2ff2c2d..8a4db711 100644 --- a/public/build.h +++ b/public/build.h @@ -82,6 +82,7 @@ Then you can use another oneliner to query all variables: #undef XASH_SERENITY #undef XASH_WIN32 #undef XASH_X86 +#undef XASH_NSWITCH //================================================================ // @@ -119,12 +120,14 @@ Then you can use another oneliner to query all variables: #if TARGET_OS_IOS #define XASH_IOS 1 #endif // TARGET_OS_IOS + #elif defined __SWITCH__ + #define XASH_NSWITCH 1 #else #error #endif #endif -#if XASH_ANDROID || defined XASH_IOS +#if XASH_ANDROID || defined XASH_IOS || defined XASH_NSWITCH #define XASH_MOBILE_PLATFORM 1 #endif diff --git a/public/buildenums.h b/public/buildenums.h index 872975ad..ed7a0c58 100644 --- a/public/buildenums.h +++ b/public/buildenums.h @@ -39,6 +39,7 @@ GNU General Public License for more details. #define PLATFORM_HAIKU 10 #define PLATFORM_SERENITY 11 #define PLATFORM_IRIX 12 +#define PLATFORM_NSWITCH 13 #if XASH_WIN32 #define XASH_PLATFORM PLATFORM_WIN32 @@ -64,6 +65,8 @@ GNU General Public License for more details. #define XASH_PLATFORM PLATFORM_SERENITY #elif XASH_IRIX #define XASH_PLATFORM PLATFORM_IRIX +#elif XASH_NSWITCH + #define XASH_PLATFORM PLATFORM_NSWITCH #else #error #endif diff --git a/scripts/waifulib/compiler_optimizations.py b/scripts/waifulib/compiler_optimizations.py index a0fd9b87..110b0762 100644 --- a/scripts/waifulib/compiler_optimizations.py +++ b/scripts/waifulib/compiler_optimizations.py @@ -169,4 +169,8 @@ def get_optimization_flags(conf): if conf.options.POLLY: cflags += conf.get_flags_by_compiler(POLLY_CFLAGS, conf.env.COMPILER_CC) + if conf.env.DEST_OS == 'nswitch' and conf.options.BUILD_TYPE == 'debug': + # enable remote debugger + cflags.append('-DNSWITCH_DEBUG') + return cflags, linkflags diff --git a/scripts/waifulib/nswitch.py b/scripts/waifulib/nswitch.py new file mode 100644 index 00000000..63391768 --- /dev/null +++ b/scripts/waifulib/nswitch.py @@ -0,0 +1,64 @@ +# encoding: utf-8 +# nswitch.py -- switch NRO task +# Copyright (C) 2018 a1batross +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. + +from waflib.Tools import ccroot +from waflib import * + +def configure(conf): + conf.find_program('elf2nro') + + v = conf.env + + v.ELF2NRO_NACP_F = ['--nacp='] + v.ELF2NRO_ICON_F = ['--icon='] + +class elf2nro(Task.Task): + color = 'RED' + run_str = '${ELF2NRO} ${ELFFILE} ${TGT} ${ELF2NRO_NACP_F?NACP}${NACP} ${ELF2NRO_ICON_F?ICON}${ICON}' + + def keyword(self): + if Logs.colors_lst['USE']: # red/blue switch colors :) + return '%sConverting to NRO' % Logs.colors_lst['CYAN'] + return 'Converting to NRO' + +@TaskGen.feature('cxxprogram') +@TaskGen.after_method('apply_link') +def apply_nro(self): + elffile = self.link_task.outputs[0] + + nodes = [elffile] + + def add_source_file(ctx, nodes, f): + if f: + if isinstance(f, str): + node = ctx.path.make_node(f) + elif isinstance(f, Node.Node): + node = f + + nodes += [node] + return node + return None + + nacpfile = add_source_file(self, nodes, getattr(self, 'nacp', None)) + iconfile = add_source_file(self, nodes, getattr(self, 'icon', None)) + self.env.ELFFILE = str(elffile) + if nacpfile: self.env.NACP = str(nacpfile) + if iconfile: self.env.ICON = str(iconfile) + + tsk = self.nro_task = self.create_task('elf2nro', nodes) + self.nro_task.set_outputs(nodes[0].change_ext('.nro')) + + inst_to = getattr(self, 'nro_install_path', None) + if inst_to: + self.add_install_files(install_to=inst_to, + install_from=tsk.outputs[:], chmod=Utils.O755, task=tsk) diff --git a/scripts/waifulib/xcompile.py b/scripts/waifulib/xcompile.py index 981a081d..f2fa4091 100644 --- a/scripts/waifulib/xcompile.py +++ b/scripts/waifulib/xcompile.py @@ -30,6 +30,8 @@ ANDROID_NDK_API_MIN = { 10: 3, 19: 16, 20: 16, 23: 16, 25: 19 } # minimal API le ANDROID_STPCPY_API_MIN = 21 # stpcpy() introduced in SDK 21 ANDROID_64BIT_API_MIN = 21 # minimal API level that supports 64-bit targets +NSWITCH_ENVVARS = ['DEVKITPRO'] + # This class does support ONLY r10e and r19c/r20 NDK class Android: ctx = None # waf context @@ -348,6 +350,87 @@ class Android: ldflags += ['-march=armv5te'] return ldflags +class NintendoSwitch: + ctx = None # waf context + arch = "aarch64" + dkp_dir = None + portlibs_dir = None + dka64_dir = None + libnx_dir = None + + def __init__(self, ctx): + self.ctx = ctx + + for i in NSWITCH_ENVVARS: + self.dkp_dir = os.getenv(i) + if self.dkp_dir != None: + break + else: + ctx.fatal('Set %s environment variable pointing to the DEVKITPRO home!' % + ' or '.join(NSWITCH_ENVVARS)) + + self.dkp_dir = os.path.abspath(self.dkp_dir) + + self.dka64_dir = os.path.join(self.dkp_dir, 'devkitA64') + if not os.path.exists(self.dka64_dir): + ctx.fatal('devkitA64 not found in `%s`. Install devkitA64!' % self.dka64_dir) + + self.libnx_dir = os.path.join(self.dkp_dir, 'libnx') + if not os.path.exists(self.libnx_dir): + ctx.fatal('libnx not found in `%s`. Install libnx!' % self.libnx_dir) + + self.portlibs_dir = os.path.join(self.dkp_dir, 'portlibs', 'switch') + if not os.path.exists(self.portlibs_dir): + ctx.fatal('No Switch libraries found in `%s`!' % self.portlibs_dir) + + def gen_toolchain_prefix(self): + return 'aarch64-none-elf-' + + def gen_gcc_toolchain_path(self): + return os.path.join(self.dka64_dir, 'bin', self.gen_toolchain_prefix()) + + def cc(self): + return self.gen_gcc_toolchain_path() + 'gcc' + + def cxx(self): + return self.gen_gcc_toolchain_path() + 'g++' + + def strip(self): + return self.gen_gcc_toolchain_path() + 'strip' + + def pkgconfig(self): + # counter-intuitively, this motherfucker is in $DEVKITPRO/portlibs/switch/bin + return os.path.join(self.portlibs_dir, 'bin', self.gen_toolchain_prefix() + 'pkg-config') + + def cflags(self, cxx = False): + cflags = [] + # arch flags + cflags += ['-D__SWITCH__', '-march=armv8-a+crc+crypto', '-mtune=cortex-a57', '-mtp=soft', '-ftls-model=local-exec', '-fPIE'] + # help the linker out + cflags += ['-ffunction-sections', '-fdata-sections'] + # base include dirs + cflags += ['-isystem %s/include' % self.libnx_dir, '-I%s/include' % self.portlibs_dir] + # the game wants GNU extensions + if cxx: + cflags += ['-std=gnu++17', '-D_GNU_SOURCE'] + else: + cflags += ['-std=gnu11', '-D_GNU_SOURCE'] + return cflags + + # they go before object list + def linkflags(self): + linkflags = ['-fPIE', '-specs=%s/switch.specs' % self.libnx_dir] + # libsolder only supports sysv hashes and we need to build everything with -rdynamic + linkflags += ['-Wl,--hash-style=sysv', '-rdynamic'] + # avoid pulling in and exposing mesa's internals, that crashes it for some god forsaken reason + linkflags += ['-Wl,--exclude-libs=libglapi.a', '-Wl,--exclude-libs=libdrm_nouveau.a'] + return linkflags + + def ldflags(self): + # system libraries implicitly require math and C++ standard library + ldflags = ['-lm', '-lstdc++'] + return ldflags + def options(opt): xc = opt.add_option_group('Cross compile options') xc.add_option('--android', action='store', dest='ANDROID_OPTS', default=None, @@ -356,6 +439,8 @@ def options(opt): help='enable building for Motorola MAGX [default: %default]') xc.add_option('--enable-msvc-wine', action='store_true', dest='MSVC_WINE', default=False, help='enable building with MSVC using Wine [default: %default]') + xc.add_option('--nswitch', action='store_true', dest='NSWITCH', default = False, + help ='enable building for Nintendo Switch [default: %default]') def configure(conf): if conf.options.ANDROID_OPTS: @@ -408,10 +493,23 @@ def configure(conf): conf.env.DEST_OS = 'win32' conf.env.DEST_CPU = conf.env.MSVC_TARGETS[0] conf.env.COMPILER_CXX = conf.env.COMPILER_CC = 'msvc' + elif conf.options.NSWITCH: + conf.nswitch = nswitch = NintendoSwitch(conf) + conf.environ['CC'] = nswitch.cc() + conf.environ['CXX'] = nswitch.cxx() + conf.environ['STRIP'] = nswitch.strip() + conf.env.PKGCONFIG = nswitch.pkgconfig() + conf.env.CFLAGS += nswitch.cflags() + conf.env.CXXFLAGS += nswitch.cflags(True) + conf.env.LINKFLAGS += nswitch.linkflags() + conf.env.LDFLAGS += nswitch.ldflags() + conf.env.HAVE_M = True + conf.env.LIB_M = ['m'] + conf.env.DEST_OS = 'nswitch' conf.env.MAGX = conf.options.MAGX conf.env.MSVC_WINE = conf.options.MSVC_WINE - MACRO_TO_DESTOS = OrderedDict({ '__ANDROID__' : 'android' }) + MACRO_TO_DESTOS = OrderedDict({ '__ANDROID__' : 'android', '__SWITCH__' : 'nswitch' }) for k in c_config.MACRO_TO_DESTOS: MACRO_TO_DESTOS[k] = c_config.MACRO_TO_DESTOS[k] # ordering is important c_config.MACRO_TO_DESTOS = MACRO_TO_DESTOS diff --git a/wscript b/wscript index 4931ad6a..be23b686 100644 --- a/wscript +++ b/wscript @@ -132,6 +132,9 @@ def configure(conf): # Load compilers early conf.load('xshlib xcompile compiler_c compiler_cxx') + if conf.options.NSWITCH: + conf.load('nswitch') + # HACKHACK: override msvc DEST_CPU value by something that we understand if conf.env.DEST_CPU == 'amd64': conf.env.DEST_CPU = 'x86_64' @@ -170,6 +173,12 @@ def configure(conf): enforce_pic = False elif conf.env.DEST_OS == 'dos': conf.options.SINGLE_BINARY = True + elif conf.env.DEST_OS == 'nswitch': + conf.options.NO_VGUI = True + conf.options.GL = True + conf.options.SINGLE_BINARY = True + conf.options.NO_ASYNC_RESOLVE = True + conf.options.USE_STBTT = True if conf.env.STATIC_LINKING: enforce_pic = False # PIC may break full static builds @@ -233,6 +242,14 @@ def configure(conf): cflags, linkflags = conf.get_optimization_flags() + # on the Switch, allow undefined symbols by default, which is needed for libsolder to work + # we'll specifically disallow for the engine executable + # additionally, shared libs are linked without libc + if conf.env.DEST_OS == 'nswitch': + linkflags.remove('-Wl,--no-undefined') + conf.env.append_unique('LINKFLAGS_cshlib', ['-nostdlib', '-nostartfiles']) + conf.env.append_unique('LINKFLAGS_cxxshlib', ['-nostdlib', '-nostartfiles']) + # And here C++ flags starts to be treated separately cxxflags = list(cflags) if conf.env.COMPILER_CC != 'msvc' and not conf.options.DISABLE_WERROR: @@ -282,7 +299,10 @@ def configure(conf): conf.define('ALLOCA_H', 'malloc.h') if conf.env.DEST_OS != 'win32': - conf.check_cc(lib='dl', mandatory=False) + if conf.env.DEST_OS == 'nswitch': + conf.check_cfg(package='solder', args='--cflags --libs', uselib_store='SOLDER') + else: + conf.check_cc(lib='dl', mandatory=False) if not conf.env.LIB_M: # HACK: already added in xcompile! conf.check_cc(lib='m') @@ -354,7 +374,7 @@ int main(int argc, char **argv) { strchrnul(argv[1], 'x'); return 0; }''' conf.env.LIBDIR = conf.env.BINDIR = conf.env.LIBDIR + '/xash3d' conf.env.SHAREDIR = '${PREFIX}/share/xash3d' else: - if sys.platform != 'win32' and not conf.env.DEST_OS == 'android': + if sys.platform != 'win32' and conf.env.DEST_OS != 'android': conf.env.PREFIX = '/' conf.env.SHAREDIR = conf.env.LIBDIR = conf.env.BINDIR = conf.env.PREFIX From 34b0cdc125552209dc8237f9358d133992ee42f5 Mon Sep 17 00:00:00 2001 From: Alibek Omarov Date: Sun, 5 Feb 2023 04:13:45 +0300 Subject: [PATCH 422/490] scripts: flatpak: use relative paths as RoDir doesn't allow absolute paths for now --- scripts/flatpak/run.sh | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/scripts/flatpak/run.sh b/scripts/flatpak/run.sh index e347669e..7183cf94 100755 --- a/scripts/flatpak/run.sh +++ b/scripts/flatpak/run.sh @@ -2,8 +2,13 @@ echo "Xash3D FWGS installed as Flatpak." +export XASH3D_BASEDIR="$HOME/.xash/" +mkdir -p $XASH3D_BASEDIR +cd $XASH3D_BASEDIR +echo "Base directory is $XASH3D_BASEDIR" + # TODO: detect by libraryfolders.vdf and installed apps -HALFLIFESTEAMDIRS="$HOME/.local/share/Steam/steamapps/common/Half-Life $HOME/.steam/steam/steamapps/common/Half-Life" +HALFLIFESTEAMDIRS="../.local/share/Steam/steamapps/common/Half-Life ../.steam/steam/steamapps/common/Half-Life" for i in $HALFLIFESTEAMDIRS; do # echo $i @@ -14,11 +19,6 @@ for i in $HALFLIFESTEAMDIRS; do fi done -XASHDATADIR="$HOME/.xash/" - -mkdir -p $XASHDATADIR -export XASH3D_BASEDIR="$XASHDATADIR" -echo "Base directory is $XASH3D_BASEDIR" export XASH3D_EXTRAS_PAK1=/app/share/xash3d/valve/extras.pk3 exec $DEBUGGER /app/lib32/xash3d/xash3d "$@" From 36e7856b9c96cd064653db6732fec5ede356c9fd Mon Sep 17 00:00:00 2001 From: fgsfds Date: Sun, 5 Feb 2023 02:16:38 +0100 Subject: [PATCH 423/490] don't redefine _GNU_SOURCE --- engine/platform/posix/lib_posix.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/engine/platform/posix/lib_posix.c b/engine/platform/posix/lib_posix.c index ad97130e..315fc059 100644 --- a/engine/platform/posix/lib_posix.c +++ b/engine/platform/posix/lib_posix.c @@ -12,7 +12,9 @@ but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. */ +#ifndef _GNU_SOURCE #define _GNU_SOURCE +#endif #include "platform/platform.h" #if XASH_LIB == LIB_POSIX #ifdef XASH_IRIX From 25fb89f7171e79d4194601285384789a68fab022 Mon Sep 17 00:00:00 2001 From: fgsfds Date: Sun, 5 Feb 2023 02:16:56 +0100 Subject: [PATCH 424/490] nswitch: don't redefine O_BINARY --- common/port.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/common/port.h b/common/port.h index bf55e171..1781ee61 100644 --- a/common/port.h +++ b/common/port.h @@ -45,9 +45,9 @@ GNU General Public License for more details. #else #include #define HAVE_DUP + #define O_BINARY 0 #endif - #define O_BINARY 0 - #define O_TEXT 0 + #define O_TEXT 0 #define _mkdir( x ) mkdir( x, S_IRWXU | S_IRWXG | S_IROTH | S_IXOTH ) #endif From f87863b8bc5af8bad0f3040c68c530bd68a04992 Mon Sep 17 00:00:00 2001 From: fgsfds Date: Sun, 5 Feb 2023 02:18:11 +0100 Subject: [PATCH 425/490] nswitch: always enable console --- engine/common/host.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/engine/common/host.c b/engine/common/host.c index e558b285..44a83d09 100644 --- a/engine/common/host.c +++ b/engine/common/host.c @@ -895,8 +895,11 @@ void Host_InitCommon( int argc, char **argv, const char *progname, qboolean bCha host.mempool = Mem_AllocPool( "Zone Engine" ); // HACKHACK: Quake console is always allowed + // HACKHACK: console is also always allowed on the Switch since we can't really pass command line // TODO: determine if we are running QWrap more reliable - if( Sys_CheckParm( "-console" ) || !Q_stricmp( SI.exeName, "quake" )) +#if !XASH_NSWITCH + if( Sys_CheckParm( "-console" ) || !Q_stricmp( SI.exeName, "quake" ) ) +#endif host.allow_console = true; if( Sys_CheckParm( "-dev" )) From cb28101732abcf6ace3a81996411f330bc7bc9fb Mon Sep 17 00:00:00 2001 From: fgsfds Date: Sun, 5 Feb 2023 02:26:17 +0100 Subject: [PATCH 426/490] touch: only pop up OSK on FINGERDOWN events --- engine/client/in_touch.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/engine/client/in_touch.c b/engine/client/in_touch.c index b3cd2b3a..a35a1a67 100644 --- a/engine/client/in_touch.c +++ b/engine/client/in_touch.c @@ -1956,7 +1956,8 @@ int IN_TouchEvent( touchEventType type, int fingerID, float x, float y, float dx // Hack for keyboard, hope it help if( cls.key_dest == key_console || cls.key_dest == key_message ) { - Key_EnableTextInput( true, true ); + if ( type == event_down ) // don't pop it again on event_up + Key_EnableTextInput( true, true ); if( cls.key_dest == key_console ) { static float y1 = 0; From 705f252ed88f1e3fb65a0afedcb3dfcee8e58e26 Mon Sep 17 00:00:00 2001 From: fgsfds Date: Sun, 5 Feb 2023 02:26:48 +0100 Subject: [PATCH 427/490] nswitch: don't automatically pop up OSK when opening console --- engine/client/keys.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/engine/client/keys.c b/engine/client/keys.c index 80c45ba2..f1c0d688 100644 --- a/engine/client/keys.c +++ b/engine/client/keys.c @@ -841,7 +841,9 @@ void GAME_EXPORT Key_SetKeyDest( int key_dest ) cls.key_dest = key_menu; break; case key_console: +#if !XASH_NSWITCH // if we don't disable this, pops up the keyboard during load Key_EnableTextInput( true, false ); +#endif cls.key_dest = key_console; break; case key_message: From f3e50b5500d054e873a25c6737926fbdd0dbe0cb Mon Sep 17 00:00:00 2001 From: fgsfds Date: Sun, 5 Feb 2023 02:27:12 +0100 Subject: [PATCH 428/490] add barebones gamepad controls for input fields and console --- engine/client/console.c | 34 ++++++++++++++++++++++++++-------- 1 file changed, 26 insertions(+), 8 deletions(-) diff --git a/engine/client/console.c b/engine/client/console.c index 2267b44c..cf7336e8 100644 --- a/engine/client/console.c +++ b/engine/client/console.c @@ -1230,7 +1230,7 @@ void Field_KeyDownEvent( field_t *edit, int key ) return; } - if( key == K_BACKSPACE ) + if( key == K_BACKSPACE || key == K_X_BUTTON ) { if( edit->cursor > 0 ) { @@ -1242,7 +1242,7 @@ void Field_KeyDownEvent( field_t *edit, int key ) return; } - if( key == K_RIGHTARROW ) + if( key == K_RIGHTARROW || key == K_DPAD_RIGHT ) { if( edit->cursor < len ) edit->cursor = Con_UtfMoveRight( edit->buffer, edit->cursor, edit->widthInChars ); if( edit->cursor >= edit->scroll + edit->widthInChars && edit->cursor <= len ) @@ -1250,7 +1250,7 @@ void Field_KeyDownEvent( field_t *edit, int key ) return; } - if( key == K_LEFTARROW ) + if( key == K_LEFTARROW || key == K_DPAD_LEFT ) { if( edit->cursor > 0 ) edit->cursor = Con_UtfMoveLeft( edit->buffer, edit->cursor ); if( edit->cursor < edit->scroll ) edit->scroll--; @@ -1547,8 +1547,8 @@ void Key_Console( int key ) return; } - // enter finishes the line - if( key == K_ENTER || key == K_KP_ENTER ) + // enter or A finish the line + if( key == K_ENTER || key == K_KP_ENTER || key == K_A_BUTTON ) { // backslash text are commands, else chat if( con.input.buffer[0] == '\\' || con.input.buffer[0] == '/' ) @@ -1575,7 +1575,7 @@ void Key_Console( int key ) } // command completion - if( key == K_TAB ) + if( key == K_TAB || key == K_L2_BUTTON ) { Con_CompleteCommand( &con.input ); Con_Bottom(); @@ -1638,6 +1638,24 @@ void Key_Console( int key ) return; } +#if XASH_NSWITCH + // enable the OSK with button press + if( key == K_Y_BUTTON ) + { + Key_EnableTextInput( true, true ); + return; + } + + // exit the console by pressing MINUS + if( key == K_BACK_BUTTON ) + { + if( cls.state == ca_active && !cl.background ) + Key_SetKeyDest( key_game ); + else UI_SetActiveMenu( true ); + return; + } +#endif + // pass to the normal editline routine Field_KeyDownEvent( &con.input, key ); } @@ -1653,14 +1671,14 @@ void Key_Message( int key ) { char buffer[MAX_SYSPATH]; - if( key == K_ESCAPE ) + if( key == K_ESCAPE || key == K_BACK_BUTTON ) { Key_SetKeyDest( key_game ); Con_ClearField( &con.chat ); return; } - if( key == K_ENTER || key == K_KP_ENTER ) + if( key == K_ENTER || key == K_KP_ENTER || key == K_A_BUTTON ) { if( con.chat.buffer[0] && cls.state == ca_active ) { From 87c307f47e71ed9fbfdb475941ecb37d97c79bf9 Mon Sep 17 00:00:00 2001 From: Alibek Omarov Date: Sun, 5 Feb 2023 04:27:58 +0300 Subject: [PATCH 429/490] scripts: flatpak: install vgui --- scripts/flatpak/su.xash.Engine.Compat.i386.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/scripts/flatpak/su.xash.Engine.Compat.i386.yml b/scripts/flatpak/su.xash.Engine.Compat.i386.yml index 7b495f9b..02a02ad7 100644 --- a/scripts/flatpak/su.xash.Engine.Compat.i386.yml +++ b/scripts/flatpak/su.xash.Engine.Compat.i386.yml @@ -79,6 +79,7 @@ modules: mkdir -p /app/bin mkdir -p /app/share/icons/hicolor/256x256/apps/ mkdir -p /app/share/applications + install -m 0755 3rdparty/vgui_support/vgui-dev/lib/vgui.so /app/lib32/xash3d/vgui.so install -m 0755 scripts/flatpak/run.sh /app/bin/run.sh install -m 0644 game_launch/icon-xash-material.png /app/share/icons/hicolor/256x256/apps/su.xash.Engine.Compat.i386.png install -m 0644 scripts/flatpak/su.xash.Engine.Compat.i386.desktop /app/share/applications/su.xash.Engine.Compat.i386.desktop From 6557ac7fb40e99124bb63b465d5270c8a4f31cf3 Mon Sep 17 00:00:00 2001 From: fgsfds Date: Sun, 5 Feb 2023 02:29:22 +0100 Subject: [PATCH 430/490] server: add sv_autosave cvar --- engine/server/server.h | 1 + engine/server/sv_cmds.c | 3 ++- engine/server/sv_main.c | 3 +++ 3 files changed, 6 insertions(+), 1 deletion(-) diff --git a/engine/server/server.h b/engine/server/server.h index 1f0ba150..eee4e358 100644 --- a/engine/server/server.h +++ b/engine/server/server.h @@ -438,6 +438,7 @@ extern convar_t sv_uploadmax; extern convar_t sv_trace_messages; extern convar_t sv_enttools_enable; extern convar_t sv_enttools_maxfire; +extern convar_t sv_autosave; extern convar_t deathmatch; extern convar_t hostname; extern convar_t skill; diff --git a/engine/server/sv_cmds.c b/engine/server/sv_cmds.c index d4b733f2..8812811d 100644 --- a/engine/server/sv_cmds.c +++ b/engine/server/sv_cmds.c @@ -502,7 +502,8 @@ void SV_AutoSave_f( void ) return; } - SV_SaveGame( "autosave" ); + if( Cvar_VariableInteger( "sv_autosave" ) ) + SV_SaveGame( "autosave" ); } /* diff --git a/engine/server/sv_main.c b/engine/server/sv_main.c index 8f32e7eb..92e44673 100644 --- a/engine/server/sv_main.c +++ b/engine/server/sv_main.c @@ -60,6 +60,7 @@ CVAR_DEFINE_AUTO( sv_log_singleplayer, "0", FCVAR_ARCHIVE, "allows logging in si CVAR_DEFINE_AUTO( sv_log_onefile, "0", FCVAR_ARCHIVE, "logs server information to only one file" ); CVAR_DEFINE_AUTO( sv_trace_messages, "0", FCVAR_LATCH, "enable server usermessages tracing (good for developers)" ); CVAR_DEFINE_AUTO( sv_master_response_timeout, "4", FCVAR_ARCHIVE, "master server heartbeat response timeout in seconds" ); +CVAR_DEFINE_AUTO( sv_autosave, "1", FCVAR_ARCHIVE|FCVAR_SERVER, "enable autosave command" ); // game-related cvars CVAR_DEFINE_AUTO( mapcyclefile, "mapcycle.txt", 0, "name of multiplayer map cycle configuration file" ); @@ -981,6 +982,8 @@ void SV_Init( void ) Cvar_RegisterVariable( &sv_enttools_enable ); Cvar_RegisterVariable( &sv_enttools_maxfire ); + Cvar_RegisterVariable( &sv_autosave ); + sv_allow_joystick = Cvar_Get( "sv_allow_joystick", "1", FCVAR_ARCHIVE, "allow connect with joystick enabled" ); sv_allow_mouse = Cvar_Get( "sv_allow_mouse", "1", FCVAR_ARCHIVE, "allow connect with mouse" ); sv_allow_touch = Cvar_Get( "sv_allow_touch", "1", FCVAR_ARCHIVE, "allow connect with touch controls" ); From 0ba4ef678c59683665102dafdf1d6560f66adf69 Mon Sep 17 00:00:00 2001 From: fgsfds Date: Sun, 5 Feb 2023 03:39:32 +0100 Subject: [PATCH 431/490] engine: net_ws: pass correct sockaddr lengths where needed --- engine/common/net_ws.c | 21 +++++++++++++++++---- 1 file changed, 17 insertions(+), 4 deletions(-) diff --git a/engine/common/net_ws.c b/engine/common/net_ws.c index df569b9b..549536b4 100644 --- a/engine/common/net_ws.c +++ b/engine/common/net_ws.c @@ -188,6 +188,19 @@ char *NET_ErrorString( void ) #endif } +_inline socklen_t NET_SockAddrLen( const struct sockaddr_storage *addr ) +{ + switch ( addr->ss_family ) + { + case AF_INET: + return sizeof( struct sockaddr_in ); + case AF_INET6: + return sizeof( struct sockaddr_in6 ); + default: + return sizeof( *addr ); // what the fuck is this? + } +} + _inline qboolean NET_IsSocketError( int retval ) { #if XASH_WIN32 || XASH_DOS4GW @@ -1626,7 +1639,7 @@ void NET_SendPacketEx( netsrc_t sock, size_t length, const void *data, netadr_t NET_NetadrToSockadr( &to, &addr ); - ret = NET_SendLong( sock, net_socket, data, length, 0, &addr, sizeof( addr ), splitsize ); + ret = NET_SendLong( sock, net_socket, data, length, 0, &addr, NET_SockAddrLen( &addr ), splitsize ); if( NET_IsSocketError( ret )) { @@ -1799,7 +1812,7 @@ static int NET_IPSocket( const char *net_iface, int port, int family ) if( port == PORT_ANY ) ((struct sockaddr_in6 *)&addr)->sin6_port = 0; else ((struct sockaddr_in6 *)&addr)->sin6_port = htons((short)port); - if( NET_IsSocketError( bind( net_socket, (struct sockaddr *)&addr, sizeof( addr )))) + if( NET_IsSocketError( bind( net_socket, (struct sockaddr *)&addr, sizeof( struct sockaddr_in6 )))) { Con_DPrintf( S_WARN "NET_UDPSocket: port: %d bind6: %s\n", port, NET_ErrorString( )); closesocket( net_socket ); @@ -1836,7 +1849,7 @@ static int NET_IPSocket( const char *net_iface, int port, int family ) if( port == PORT_ANY ) ((struct sockaddr_in *)&addr)->sin_port = 0; else ((struct sockaddr_in *)&addr)->sin_port = htons((short)port); - if( NET_IsSocketError( bind( net_socket, (struct sockaddr *)&addr, sizeof( addr )))) + if( NET_IsSocketError( bind( net_socket, (struct sockaddr *)&addr, sizeof( struct sockaddr_in )))) { Con_DPrintf( S_WARN "NET_UDPSocket: port: %d bind: %s\n", port, NET_ErrorString( )); closesocket( net_socket ); @@ -2644,7 +2657,7 @@ void HTTP_Run( void ) if( curfile->state < HTTP_CONNECTED ) // Connection not enstabilished { - res = connect( curfile->socket, (struct sockaddr*)&addr, sizeof( addr )); + res = connect( curfile->socket, (struct sockaddr*)&addr, NET_SockAddrLen( &addr ) ); if( res ) { From b73c16c216e3bcfcd25faff11ba57392b642d794 Mon Sep 17 00:00:00 2001 From: fgsfds Date: Sun, 5 Feb 2023 03:39:32 +0100 Subject: [PATCH 432/490] engine: net_ws: pass correct sockaddr lengths where needed --- engine/common/net_ws.c | 21 +++++++++++++++++---- 1 file changed, 17 insertions(+), 4 deletions(-) diff --git a/engine/common/net_ws.c b/engine/common/net_ws.c index df569b9b..549536b4 100644 --- a/engine/common/net_ws.c +++ b/engine/common/net_ws.c @@ -188,6 +188,19 @@ char *NET_ErrorString( void ) #endif } +_inline socklen_t NET_SockAddrLen( const struct sockaddr_storage *addr ) +{ + switch ( addr->ss_family ) + { + case AF_INET: + return sizeof( struct sockaddr_in ); + case AF_INET6: + return sizeof( struct sockaddr_in6 ); + default: + return sizeof( *addr ); // what the fuck is this? + } +} + _inline qboolean NET_IsSocketError( int retval ) { #if XASH_WIN32 || XASH_DOS4GW @@ -1626,7 +1639,7 @@ void NET_SendPacketEx( netsrc_t sock, size_t length, const void *data, netadr_t NET_NetadrToSockadr( &to, &addr ); - ret = NET_SendLong( sock, net_socket, data, length, 0, &addr, sizeof( addr ), splitsize ); + ret = NET_SendLong( sock, net_socket, data, length, 0, &addr, NET_SockAddrLen( &addr ), splitsize ); if( NET_IsSocketError( ret )) { @@ -1799,7 +1812,7 @@ static int NET_IPSocket( const char *net_iface, int port, int family ) if( port == PORT_ANY ) ((struct sockaddr_in6 *)&addr)->sin6_port = 0; else ((struct sockaddr_in6 *)&addr)->sin6_port = htons((short)port); - if( NET_IsSocketError( bind( net_socket, (struct sockaddr *)&addr, sizeof( addr )))) + if( NET_IsSocketError( bind( net_socket, (struct sockaddr *)&addr, sizeof( struct sockaddr_in6 )))) { Con_DPrintf( S_WARN "NET_UDPSocket: port: %d bind6: %s\n", port, NET_ErrorString( )); closesocket( net_socket ); @@ -1836,7 +1849,7 @@ static int NET_IPSocket( const char *net_iface, int port, int family ) if( port == PORT_ANY ) ((struct sockaddr_in *)&addr)->sin_port = 0; else ((struct sockaddr_in *)&addr)->sin_port = htons((short)port); - if( NET_IsSocketError( bind( net_socket, (struct sockaddr *)&addr, sizeof( addr )))) + if( NET_IsSocketError( bind( net_socket, (struct sockaddr *)&addr, sizeof( struct sockaddr_in )))) { Con_DPrintf( S_WARN "NET_UDPSocket: port: %d bind: %s\n", port, NET_ErrorString( )); closesocket( net_socket ); @@ -2644,7 +2657,7 @@ void HTTP_Run( void ) if( curfile->state < HTTP_CONNECTED ) // Connection not enstabilished { - res = connect( curfile->socket, (struct sockaddr*)&addr, sizeof( addr )); + res = connect( curfile->socket, (struct sockaddr*)&addr, NET_SockAddrLen( &addr ) ); if( res ) { From 15ba932046c57cd14dc42546989f24bfd1324844 Mon Sep 17 00:00:00 2001 From: fgsfds Date: Fri, 28 May 2021 20:12:01 +0300 Subject: [PATCH 433/490] engine: server: add sv_autosave cvar * a1ba: added FCVAR_PRIVILEGED just in case --- engine/server/server.h | 1 + engine/server/sv_cmds.c | 3 ++- engine/server/sv_main.c | 2 ++ 3 files changed, 5 insertions(+), 1 deletion(-) diff --git a/engine/server/server.h b/engine/server/server.h index 1f0ba150..eee4e358 100644 --- a/engine/server/server.h +++ b/engine/server/server.h @@ -438,6 +438,7 @@ extern convar_t sv_uploadmax; extern convar_t sv_trace_messages; extern convar_t sv_enttools_enable; extern convar_t sv_enttools_maxfire; +extern convar_t sv_autosave; extern convar_t deathmatch; extern convar_t hostname; extern convar_t skill; diff --git a/engine/server/sv_cmds.c b/engine/server/sv_cmds.c index d4b733f2..8812811d 100644 --- a/engine/server/sv_cmds.c +++ b/engine/server/sv_cmds.c @@ -502,7 +502,8 @@ void SV_AutoSave_f( void ) return; } - SV_SaveGame( "autosave" ); + if( Cvar_VariableInteger( "sv_autosave" ) ) + SV_SaveGame( "autosave" ); } /* diff --git a/engine/server/sv_main.c b/engine/server/sv_main.c index 8f32e7eb..0db6b0fc 100644 --- a/engine/server/sv_main.c +++ b/engine/server/sv_main.c @@ -60,6 +60,7 @@ CVAR_DEFINE_AUTO( sv_log_singleplayer, "0", FCVAR_ARCHIVE, "allows logging in si CVAR_DEFINE_AUTO( sv_log_onefile, "0", FCVAR_ARCHIVE, "logs server information to only one file" ); CVAR_DEFINE_AUTO( sv_trace_messages, "0", FCVAR_LATCH, "enable server usermessages tracing (good for developers)" ); CVAR_DEFINE_AUTO( sv_master_response_timeout, "4", FCVAR_ARCHIVE, "master server heartbeat response timeout in seconds" ); +CVAR_DEFINE_AUTO( sv_autosave, "1", FCVAR_ARCHIVE|FCVAR_SERVER|FCVAR_PRIVILEGED, "enable autosaving" ); // game-related cvars CVAR_DEFINE_AUTO( mapcyclefile, "mapcycle.txt", 0, "name of multiplayer map cycle configuration file" ); @@ -967,6 +968,7 @@ void SV_Init( void ) Cvar_RegisterVariable( &sv_master_response_timeout ); Cvar_RegisterVariable( &sv_background_freeze ); + Cvar_RegisterVariable( &sv_autosave ); Cvar_RegisterVariable( &mapcyclefile ); Cvar_RegisterVariable( &motdfile ); From d944301a60e3ec158b3697b4f0507419d189a67a Mon Sep 17 00:00:00 2001 From: fgsfds Date: Mon, 24 May 2021 04:10:24 +0300 Subject: [PATCH 434/490] engine: client: add barebones gamepad controls to input fields --- engine/client/console.c | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/engine/client/console.c b/engine/client/console.c index 2267b44c..dc461fec 100644 --- a/engine/client/console.c +++ b/engine/client/console.c @@ -1230,7 +1230,7 @@ void Field_KeyDownEvent( field_t *edit, int key ) return; } - if( key == K_BACKSPACE ) + if( key == K_BACKSPACE || key == K_B_BUTTON ) { if( edit->cursor > 0 ) { @@ -1242,7 +1242,7 @@ void Field_KeyDownEvent( field_t *edit, int key ) return; } - if( key == K_RIGHTARROW ) + if( key == K_RIGHTARROW || key == K_DPAD_RIGHT ) { if( edit->cursor < len ) edit->cursor = Con_UtfMoveRight( edit->buffer, edit->cursor, edit->widthInChars ); if( edit->cursor >= edit->scroll + edit->widthInChars && edit->cursor <= len ) @@ -1250,7 +1250,7 @@ void Field_KeyDownEvent( field_t *edit, int key ) return; } - if( key == K_LEFTARROW ) + if( key == K_LEFTARROW || key == K_DPAD_LEFT ) { if( edit->cursor > 0 ) edit->cursor = Con_UtfMoveLeft( edit->buffer, edit->cursor ); if( edit->cursor < edit->scroll ) edit->scroll--; @@ -1547,8 +1547,8 @@ void Key_Console( int key ) return; } - // enter finishes the line - if( key == K_ENTER || key == K_KP_ENTER ) + // enter or A finish the line + if( key == K_ENTER || key == K_KP_ENTER || key == K_A_BUTTON ) { // backslash text are commands, else chat if( con.input.buffer[0] == '\\' || con.input.buffer[0] == '/' ) @@ -1575,7 +1575,7 @@ void Key_Console( int key ) } // command completion - if( key == K_TAB ) + if( key == K_TAB || key == K_X_BUTTON ) { Con_CompleteCommand( &con.input ); Con_Bottom(); From b68def2b9cca456f99a9778e28b42f669f5a18a0 Mon Sep 17 00:00:00 2001 From: fgsfds Date: Mon, 24 May 2021 04:53:35 +0300 Subject: [PATCH 435/490] engine: touch: only pop up touch keyboard on FINGERDOWN events --- engine/client/in_touch.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/engine/client/in_touch.c b/engine/client/in_touch.c index b3cd2b3a..a35a1a67 100644 --- a/engine/client/in_touch.c +++ b/engine/client/in_touch.c @@ -1956,7 +1956,8 @@ int IN_TouchEvent( touchEventType type, int fingerID, float x, float y, float dx // Hack for keyboard, hope it help if( cls.key_dest == key_console || cls.key_dest == key_message ) { - Key_EnableTextInput( true, true ); + if ( type == event_down ) // don't pop it again on event_up + Key_EnableTextInput( true, true ); if( cls.key_dest == key_console ) { static float y1 = 0; From f782d444a84c27ed4a5f81c313cc5b67a7801529 Mon Sep 17 00:00:00 2001 From: fgsfds Date: Sun, 5 Feb 2023 02:16:38 +0100 Subject: [PATCH 436/490] engine: platform: posix: don't redefine _GNU_SOURCE --- engine/platform/posix/lib_posix.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/engine/platform/posix/lib_posix.c b/engine/platform/posix/lib_posix.c index 746b73e7..fe1204e0 100644 --- a/engine/platform/posix/lib_posix.c +++ b/engine/platform/posix/lib_posix.c @@ -12,7 +12,9 @@ but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. */ +#ifndef _GNU_SOURCE #define _GNU_SOURCE +#endif #include "platform/platform.h" #if XASH_LIB == LIB_POSIX #include From 634574f249398fb6b398b4aae7e7152fc80fcfc9 Mon Sep 17 00:00:00 2001 From: Alibek Omarov Date: Mon, 6 Feb 2023 00:29:14 +0300 Subject: [PATCH 437/490] engine: platform: sdl: don't enable text mode with cursor??? --- engine/platform/sdl/in_sdl.c | 2 -- 1 file changed, 2 deletions(-) diff --git a/engine/platform/sdl/in_sdl.c b/engine/platform/sdl/in_sdl.c index 3957b2e9..0603a421 100644 --- a/engine/platform/sdl/in_sdl.c +++ b/engine/platform/sdl/in_sdl.c @@ -343,12 +343,10 @@ void Platform_SetCursorType( VGUI_DefaultCursor type ) { SDL_SetCursor( cursors.cursors[type] ); SDL_ShowCursor( true ); - Key_EnableTextInput( true, false ); } else { SDL_ShowCursor( false ); - Key_EnableTextInput( false, false ); } #else if( host.mouse_visible ) From 3fca567b81c370848ad20f0da58a68be5cf8f8d5 Mon Sep 17 00:00:00 2001 From: Alibek Omarov Date: Fri, 3 Feb 2023 19:01:15 +0300 Subject: [PATCH 438/490] wscript: few more warnings-as-errors --- wscript | 3 +++ 1 file changed, 3 insertions(+) diff --git a/wscript b/wscript index 4931ad6a..8beb2778 100644 --- a/wscript +++ b/wscript @@ -209,6 +209,9 @@ def configure(conf): '-Werror=sizeof-pointer-memaccess', '-Werror=sizeof-array-div', '-Werror=sizeof-pointer-div', + '-Werror=string-compare', + '-Werror=use-after-free=3', + '-Werror=sequence-point', # '-Werror=format=2', # '-Wdouble-promotion', # disable warning flood '-Wstrict-aliasing', From 12154de6f5c719ec08c1816cc380592e9faffaff Mon Sep 17 00:00:00 2001 From: Alibek Omarov Date: Fri, 3 Feb 2023 19:01:28 +0300 Subject: [PATCH 439/490] ref: soft: fix -Wsequence-point --- ref/soft/r_triapi.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ref/soft/r_triapi.c b/ref/soft/r_triapi.c index c1867083..88c69695 100644 --- a/ref/soft/r_triapi.c +++ b/ref/soft/r_triapi.c @@ -119,7 +119,7 @@ void GAME_EXPORT TriBegin( int mode1 ) if( mode1 == TRI_QUADS ) mode1 = TRI_TRIANGLE_FAN; mode = mode1; - vertcount = n = vertcount = 0; + n = vertcount = 0; } /* From 05016f86396709a6c26d3c65365f2004ba4e7e99 Mon Sep 17 00:00:00 2001 From: Alibek Omarov Date: Mon, 6 Feb 2023 16:40:36 +0300 Subject: [PATCH 440/490] engine: vgui: add EnableTextInput to the API --- engine/client/vgui/vgui_draw.c | 2 +- engine/vgui_api.h | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/engine/client/vgui/vgui_draw.c b/engine/client/vgui/vgui_draw.c index 8ae08584..a1b3fe9f 100644 --- a/engine/client/vgui/vgui_draw.c +++ b/engine/client/vgui/vgui_draw.c @@ -63,7 +63,7 @@ static struct VGUI_CursorSelect, VGUI_GetColor, VGUI_IsInGame, - NULL, + Key_EnableTextInput, VGUI_GetMousePos, VGUI_UtfProcessChar, Platform_GetClipboardText, diff --git a/engine/vgui_api.h b/engine/vgui_api.h index 2ce89226..2412d76d 100644 --- a/engine/vgui_api.h +++ b/engine/vgui_api.h @@ -180,7 +180,7 @@ typedef struct vguiapi_s void (*CursorSelect)( VGUI_DefaultCursor cursor ); byte (*GetColor)( int i, int j ); qboolean (*IsInGame)( void ); - void (*Unused)( void ); + void (*EnableTextInput)( qboolean enable, qboolean force ); void (*GetCursorPos)( int *x, int *y ); int (*ProcessUtfChar)( int ch ); int (*GetClipboardText)( char *buffer, size_t bufferSize ); From 98a7f6fa3f4ab23ad2fe89b96866fdc05b09d779 Mon Sep 17 00:00:00 2001 From: fgsfds Date: Tue, 7 Feb 2023 18:34:58 +0100 Subject: [PATCH 441/490] ci: add nswitch build scripts --- scripts/gha/build_nswitch.sh | 50 ++++++++++++++++++++++++++++++++++++ scripts/gha/deps_nswitch.sh | 30 ++++++++++++++++++++++ 2 files changed, 80 insertions(+) create mode 100644 scripts/gha/build_nswitch.sh create mode 100644 scripts/gha/deps_nswitch.sh diff --git a/scripts/gha/build_nswitch.sh b/scripts/gha/build_nswitch.sh new file mode 100644 index 00000000..1c29f73a --- /dev/null +++ b/scripts/gha/build_nswitch.sh @@ -0,0 +1,50 @@ +#!/bin/bash + +. scripts/lib.sh + +# args: branch name, gamedir name +build_hlsdk() +{ + echo "Building HLSDK: $1 branch..." + git checkout switch-$1 + ./waf configure -T release --nswitch || die_configure + ./waf build || die + cp build/dlls/$1_nswitch_aarch64.so ../../pkgtemp/xash3d/$2/dlls/ + cp build/cl_dll/client_nswitch_aarch64.so ../../pkgtemp/xash3d/$2/cl_dlls/ + ./waf clean +} + +export DEVKITPRO=/opt/devkitpro +source $DEVKITPRO/switchvars.sh || die +cd "$BUILDDIR" || die + +rm -rf artifacts build pkgtemp hlsdktemp + +mkdir -p pkgtemp/xash3d/{valve,gearbox,bshift}/{dlls,cl_dlls} || die + +echo "Building engine..." + +./waf configure -T release --nswitch || die_configure +./waf build || die + +echo "Building HLSDK..." + +mkdir -p hlsdktemp || die +cd hlsdktemp || die + +# TODO: replace with FWGS/hlsdk-portable.git when PRs are merged +git clone --recursive https://github.com/fgsfdsfgs/hlsdk-xash3d.git +cd hlsdk-xash3d || die +build_hlsdk hl valve +build_hlsdk opfor gearbox +build_hlsdk bshift bshift + +echo "Packaging artifacts..." + +cp build/engine/xash.nro pkgtemp/xash3d/xash3d.nro +cp build/ref/gl/libref_gl.so pkgtemp/xash3d/ +cp build/ref/soft/libref_soft.so pkgtemp/xash3d/ +cp build/3rdparty/mainui/libmenu.so pkgtemp/xash3d/ +cp build/3rdparty/extras/extras.pk3 pkgtemp/xash3d/valve/ + +mkdir -p artifacts/ || die diff --git a/scripts/gha/deps_nswitch.sh b/scripts/gha/deps_nswitch.sh new file mode 100644 index 00000000..394308c4 --- /dev/null +++ b/scripts/gha/deps_nswitch.sh @@ -0,0 +1,30 @@ +#!/bin/bash + +cd $GITHUB_WORKSPACE + +echo "Downloading and installing dkp-pacman..." + +wget https://apt.devkitpro.org/install-devkitpro-pacman +chmod +x ./install-devkitpro-pacman +sudo ./install-devkitpro-pacman +sudo dkp-pacman --noconfirm -Sy + +echo "Downloading and installing devkitA64..." + +export DEVKITPRO=/opt/devkitpro +export DEVKITA64=$DEVKITPRO/devkitA64 +export PORTLIBS=$DEVKITPRO/portlibs + +sudo dkp-pacman --noconfirm -S devkitA64 dkp-toolchain-vars switch-cmake switch-pkg-config + +echo "Downloading and installing packaged dependencies..." + +sudo dkp-pacman --noconfirm -S libnx switch-mesa switch-libdrm_nouveau switch-sdl2 + +echo "Downloading and installing libsolder..." + +git clone https://github.com/fgsfdsfgs/libsolder.git + +pushd ./libsolder +make && make install +popd From 59cd5493e7b372509efdd06738d6826a421f88ea Mon Sep 17 00:00:00 2001 From: fgsfds Date: Tue, 7 Feb 2023 18:35:08 +0100 Subject: [PATCH 442/490] github: add nswitch build target --- .github/workflows/c-cpp.yml | 3 +++ 1 file changed, 3 insertions(+) diff --git a/.github/workflows/c-cpp.yml b/.github/workflows/c-cpp.yml index 3b30e109..82430f05 100644 --- a/.github/workflows/c-cpp.yml +++ b/.github/workflows/c-cpp.yml @@ -34,6 +34,9 @@ jobs: # targetos: motomagx # targetarch: armv6 + - os: ubuntu-20.04 + targetos: nswitch + targetarch: aarch64 - os: windows-latest targetos: win32 targetarch: amd64 From 030d05f0186d33b8176681d403f6bd236f58ba0b Mon Sep 17 00:00:00 2001 From: fgsfds Date: Tue, 7 Feb 2023 18:38:18 +0100 Subject: [PATCH 443/490] ci: nswitch: make install with sudo --- scripts/gha/deps_nswitch.sh | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/scripts/gha/deps_nswitch.sh b/scripts/gha/deps_nswitch.sh index 394308c4..95daf01d 100644 --- a/scripts/gha/deps_nswitch.sh +++ b/scripts/gha/deps_nswitch.sh @@ -26,5 +26,6 @@ echo "Downloading and installing libsolder..." git clone https://github.com/fgsfdsfgs/libsolder.git pushd ./libsolder -make && make install +make +sudo make install popd From 72b82469698202bff1671279838e8781c8a22b7f Mon Sep 17 00:00:00 2001 From: fgsfds Date: Tue, 7 Feb 2023 19:54:00 +0100 Subject: [PATCH 444/490] ci: nswitch: attempt to use dkp's docker image because they banned the ci server from accessing their pacman repo --- scripts/gha/build_nswitch.sh | 43 ++++------------------------- scripts/gha/build_nswitch_docker.sh | 43 +++++++++++++++++++++++++++++ scripts/gha/deps_nswitch.sh | 31 ++++++--------------- 3 files changed, 58 insertions(+), 59 deletions(-) create mode 100644 scripts/gha/build_nswitch_docker.sh diff --git a/scripts/gha/build_nswitch.sh b/scripts/gha/build_nswitch.sh index 1c29f73a..37f961c1 100644 --- a/scripts/gha/build_nswitch.sh +++ b/scripts/gha/build_nswitch.sh @@ -2,49 +2,18 @@ . scripts/lib.sh -# args: branch name, gamedir name -build_hlsdk() -{ - echo "Building HLSDK: $1 branch..." - git checkout switch-$1 - ./waf configure -T release --nswitch || die_configure - ./waf build || die - cp build/dlls/$1_nswitch_aarch64.so ../../pkgtemp/xash3d/$2/dlls/ - cp build/cl_dll/client_nswitch_aarch64.so ../../pkgtemp/xash3d/$2/cl_dlls/ - ./waf clean -} - -export DEVKITPRO=/opt/devkitpro -source $DEVKITPRO/switchvars.sh || die cd "$BUILDDIR" || die -rm -rf artifacts build pkgtemp hlsdktemp +rm -rf artifacts build pkgtemp mkdir -p pkgtemp/xash3d/{valve,gearbox,bshift}/{dlls,cl_dlls} || die +mkdir -p artifacts/ || die -echo "Building engine..." +echo "Running build script in Docker container..." -./waf configure -T release --nswitch || die_configure -./waf build || die - -echo "Building HLSDK..." - -mkdir -p hlsdktemp || die -cd hlsdktemp || die - -# TODO: replace with FWGS/hlsdk-portable.git when PRs are merged -git clone --recursive https://github.com/fgsfdsfgs/hlsdk-xash3d.git -cd hlsdk-xash3d || die -build_hlsdk hl valve -build_hlsdk opfor gearbox -build_hlsdk bshift bshift +docker --name xash-build --rm -v `pwd`:`pwd` -w `pwd` devkitpro/devkita64:latest bash ./scripts/gha/build_nswitch_docker.sh || die echo "Packaging artifacts..." -cp build/engine/xash.nro pkgtemp/xash3d/xash3d.nro -cp build/ref/gl/libref_gl.so pkgtemp/xash3d/ -cp build/ref/soft/libref_soft.so pkgtemp/xash3d/ -cp build/3rdparty/mainui/libmenu.so pkgtemp/xash3d/ -cp build/3rdparty/extras/extras.pk3 pkgtemp/xash3d/valve/ - -mkdir -p artifacts/ || die +find artifacts +7z a -t7z artifacts/xash3d-fwgs-nswitch.7z -m0=lzma2 -mx=9 -mfb=64 -md=32m -ms=on -r artifacts/xash3d diff --git a/scripts/gha/build_nswitch_docker.sh b/scripts/gha/build_nswitch_docker.sh new file mode 100644 index 00000000..3d38cda4 --- /dev/null +++ b/scripts/gha/build_nswitch_docker.sh @@ -0,0 +1,43 @@ +#!/bin/bash + +. scripts/lib.sh + +export DEVKITPRO=/opt/devkitpro +source $DEVKITPRO/switchvars.sh || die + +# args: branch name, gamedir name +build_hlsdk() +{ + echo "Building HLSDK: $1 branch..." + git checkout switch-$1 + ./waf configure -T release --nswitch || die_configure + ./waf build || die + cp build/dlls/$1_nswitch_aarch64.so ../pkgtemp/xash3d/$2/dlls/ + cp build/cl_dll/client_nswitch_aarch64.so ../pkgtemp/xash3d/$2/cl_dlls/ + ./waf clean +} + +echo "Building libsolder..." + +sudo make -C libsolder install || exit 1 + +echo "Building engine..." + +./waf configure -T release --nswitch || die_configure +./waf build || die + +echo "Building HLSDK..." + +# TODO: replace with FWGS/hlsdk-portable.git when PRs are merged +cd hlsdk-xash3d || die +build_hlsdk hl valve +build_hlsdk opfor gearbox +build_hlsdk bshift bshift + +echo "Copying artifacts..." + +cp build/engine/xash.nro pkgtemp/xash3d/xash3d.nro +cp build/ref/gl/libref_gl.so pkgtemp/xash3d/ +cp build/ref/soft/libref_soft.so pkgtemp/xash3d/ +cp build/3rdparty/mainui/libmenu.so pkgtemp/xash3d/ +cp build/3rdparty/extras/extras.pk3 pkgtemp/xash3d/valve/ diff --git a/scripts/gha/deps_nswitch.sh b/scripts/gha/deps_nswitch.sh index 95daf01d..5f577818 100644 --- a/scripts/gha/deps_nswitch.sh +++ b/scripts/gha/deps_nswitch.sh @@ -2,30 +2,17 @@ cd $GITHUB_WORKSPACE -echo "Downloading and installing dkp-pacman..." +echo "Downloading devkitA64 docker container..." -wget https://apt.devkitpro.org/install-devkitpro-pacman -chmod +x ./install-devkitpro-pacman -sudo ./install-devkitpro-pacman -sudo dkp-pacman --noconfirm -Sy +docker pull devkitpro/devkita64:latest || exit 1 -echo "Downloading and installing devkitA64..." +echo "Downloading libsolder..." -export DEVKITPRO=/opt/devkitpro -export DEVKITA64=$DEVKITPRO/devkitA64 -export PORTLIBS=$DEVKITPRO/portlibs +rm -rf libsolder +git clone https://github.com/fgsfdsfgs/libsolder.git || exit 1 -sudo dkp-pacman --noconfirm -S devkitA64 dkp-toolchain-vars switch-cmake switch-pkg-config +echo "Downloading HLSDK..." -echo "Downloading and installing packaged dependencies..." - -sudo dkp-pacman --noconfirm -S libnx switch-mesa switch-libdrm_nouveau switch-sdl2 - -echo "Downloading and installing libsolder..." - -git clone https://github.com/fgsfdsfgs/libsolder.git - -pushd ./libsolder -make -sudo make install -popd +# TODO: change to FWGS/hlsdk-portable.git when changes are merged in +rm -rf hlsdk-xash3d hlsdk-portable +git clone --recursive https://github.com/fgsfdsfgs/hlsdk-xash3d.git || exit 1 From d103f022b42c605c5ebd4cc91f39d78a06c6e2ec Mon Sep 17 00:00:00 2001 From: fgsfds Date: Tue, 7 Feb 2023 19:55:33 +0100 Subject: [PATCH 445/490] ci: nswitch: forgot run --- scripts/gha/build_nswitch.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scripts/gha/build_nswitch.sh b/scripts/gha/build_nswitch.sh index 37f961c1..64926775 100644 --- a/scripts/gha/build_nswitch.sh +++ b/scripts/gha/build_nswitch.sh @@ -11,7 +11,7 @@ mkdir -p artifacts/ || die echo "Running build script in Docker container..." -docker --name xash-build --rm -v `pwd`:`pwd` -w `pwd` devkitpro/devkita64:latest bash ./scripts/gha/build_nswitch_docker.sh || die +docker run --name xash-build --rm -v `pwd`:`pwd` -w `pwd` devkitpro/devkita64:latest bash ./scripts/gha/build_nswitch_docker.sh || die echo "Packaging artifacts..." From eff75e5d500c9e467b530f149d7a5173916cc6b9 Mon Sep 17 00:00:00 2001 From: fgsfds Date: Tue, 7 Feb 2023 20:00:49 +0100 Subject: [PATCH 446/490] ci: nswitch: the docker container is missing dkp-toolchain-vars, install it --- scripts/gha/build_nswitch_docker.sh | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/scripts/gha/build_nswitch_docker.sh b/scripts/gha/build_nswitch_docker.sh index 3d38cda4..dbea54ca 100644 --- a/scripts/gha/build_nswitch_docker.sh +++ b/scripts/gha/build_nswitch_docker.sh @@ -2,9 +2,6 @@ . scripts/lib.sh -export DEVKITPRO=/opt/devkitpro -source $DEVKITPRO/switchvars.sh || die - # args: branch name, gamedir name build_hlsdk() { @@ -17,9 +14,13 @@ build_hlsdk() ./waf clean } +echo "Downloading missing dka64 packages..." + +dkp-pacman -S --noconfirm dkp-toolchain-vars || die + echo "Building libsolder..." -sudo make -C libsolder install || exit 1 +make -C libsolder install || die echo "Building engine..." From dab959fc32adbcc04404dcf353807b8ca5784b19 Mon Sep 17 00:00:00 2001 From: fgsfds Date: Tue, 7 Feb 2023 20:04:36 +0100 Subject: [PATCH 447/490] ci: nswitch: the docker container is missing python, install it --- scripts/gha/build_nswitch_docker.sh | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/scripts/gha/build_nswitch_docker.sh b/scripts/gha/build_nswitch_docker.sh index dbea54ca..311f2bc0 100644 --- a/scripts/gha/build_nswitch_docker.sh +++ b/scripts/gha/build_nswitch_docker.sh @@ -14,8 +14,9 @@ build_hlsdk() ./waf clean } -echo "Downloading missing dka64 packages..." +echo "Downloading missing deps..." +apt-get install -y --no-install-recommends python3 || die dkp-pacman -S --noconfirm dkp-toolchain-vars || die echo "Building libsolder..." From 20bcd03f195053d8f3344880fa84a07a3b6c9076 Mon Sep 17 00:00:00 2001 From: fgsfds Date: Tue, 7 Feb 2023 20:07:48 +0100 Subject: [PATCH 448/490] ci: nswitch: ...and set it to be the default python install --- scripts/gha/build_nswitch_docker.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scripts/gha/build_nswitch_docker.sh b/scripts/gha/build_nswitch_docker.sh index 311f2bc0..149e4c91 100644 --- a/scripts/gha/build_nswitch_docker.sh +++ b/scripts/gha/build_nswitch_docker.sh @@ -16,7 +16,7 @@ build_hlsdk() echo "Downloading missing deps..." -apt-get install -y --no-install-recommends python3 || die +apt-get install -y --no-install-recommends python3 python-is-python3 || die dkp-pacman -S --noconfirm dkp-toolchain-vars || die echo "Building libsolder..." From 430c51b71a577e57756944b0acff16d5fd1ec467 Mon Sep 17 00:00:00 2001 From: fgsfds Date: Tue, 7 Feb 2023 20:14:11 +0100 Subject: [PATCH 449/490] ci: nswitch: there is no python-is-python3 where we're going --- scripts/gha/build_nswitch_docker.sh | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/scripts/gha/build_nswitch_docker.sh b/scripts/gha/build_nswitch_docker.sh index 149e4c91..448d46e8 100644 --- a/scripts/gha/build_nswitch_docker.sh +++ b/scripts/gha/build_nswitch_docker.sh @@ -16,8 +16,9 @@ build_hlsdk() echo "Downloading missing deps..." -apt-get install -y --no-install-recommends python3 python-is-python3 || die dkp-pacman -S --noconfirm dkp-toolchain-vars || die +# forgive me father, for I have sinned +ln -s /usr/bin/python3 /usr/bin/python echo "Building libsolder..." From 663b574b8bd2731363a3f4740d9432e61d5adbba Mon Sep 17 00:00:00 2001 From: fgsfds Date: Tue, 7 Feb 2023 20:22:30 +0100 Subject: [PATCH 450/490] github: checkout the switch_newer branch for now --- .github/workflows/c-cpp.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/workflows/c-cpp.yml b/.github/workflows/c-cpp.yml index 82430f05..ad92f754 100644 --- a/.github/workflows/c-cpp.yml +++ b/.github/workflows/c-cpp.yml @@ -54,6 +54,7 @@ jobs: uses: actions/checkout@v3 with: submodules: recursive + ref: switch_newer # REMOVEME: remove when merging into the main repo! - name: Install dependencies run: bash scripts/gha/deps_${{ matrix.targetos }}.sh - name: Build engine From 1a54ec92e0e2785cad14da3944652aea3d82dc09 Mon Sep 17 00:00:00 2001 From: fgsfds Date: Tue, 7 Feb 2023 20:26:13 +0100 Subject: [PATCH 451/490] github: ref name not required --- .github/workflows/c-cpp.yml | 1 - 1 file changed, 1 deletion(-) diff --git a/.github/workflows/c-cpp.yml b/.github/workflows/c-cpp.yml index ad92f754..82430f05 100644 --- a/.github/workflows/c-cpp.yml +++ b/.github/workflows/c-cpp.yml @@ -54,7 +54,6 @@ jobs: uses: actions/checkout@v3 with: submodules: recursive - ref: switch_newer # REMOVEME: remove when merging into the main repo! - name: Install dependencies run: bash scripts/gha/deps_${{ matrix.targetos }}.sh - name: Build engine From 07922c02392e9264b25890a2aae2358fc5f1be45 Mon Sep 17 00:00:00 2001 From: fgsfds Date: Tue, 7 Feb 2023 20:26:24 +0100 Subject: [PATCH 452/490] ci: nswitch: actually set env vars properly --- scripts/gha/build_nswitch_docker.sh | 2 ++ 1 file changed, 2 insertions(+) diff --git a/scripts/gha/build_nswitch_docker.sh b/scripts/gha/build_nswitch_docker.sh index 448d46e8..ebcc4419 100644 --- a/scripts/gha/build_nswitch_docker.sh +++ b/scripts/gha/build_nswitch_docker.sh @@ -20,6 +20,8 @@ dkp-pacman -S --noconfirm dkp-toolchain-vars || die # forgive me father, for I have sinned ln -s /usr/bin/python3 /usr/bin/python +source $DEVKITPRO/switchvars.sh + echo "Building libsolder..." make -C libsolder install || die From 6c8b9af6bb50f785fdd34e280e9e1724a4f73227 Mon Sep 17 00:00:00 2001 From: fgsfds Date: Tue, 7 Feb 2023 20:35:24 +0100 Subject: [PATCH 453/490] ci: nswitch: fix artifact packaging --- scripts/gha/build_nswitch.sh | 3 +-- scripts/gha/build_nswitch_docker.sh | 5 +++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/scripts/gha/build_nswitch.sh b/scripts/gha/build_nswitch.sh index 64926775..c93532fe 100644 --- a/scripts/gha/build_nswitch.sh +++ b/scripts/gha/build_nswitch.sh @@ -15,5 +15,4 @@ docker run --name xash-build --rm -v `pwd`:`pwd` -w `pwd` devkitpro/devkita64:la echo "Packaging artifacts..." -find artifacts -7z a -t7z artifacts/xash3d-fwgs-nswitch.7z -m0=lzma2 -mx=9 -mfb=64 -md=32m -ms=on -r artifacts/xash3d +7z a -t7z artifacts/xash3d-fwgs-nswitch.7z -m0=lzma2 -mx=9 -mfb=64 -md=32m -ms=on -r pkgtemp/xash3d diff --git a/scripts/gha/build_nswitch_docker.sh b/scripts/gha/build_nswitch_docker.sh index ebcc4419..53698f51 100644 --- a/scripts/gha/build_nswitch_docker.sh +++ b/scripts/gha/build_nswitch_docker.sh @@ -33,11 +33,12 @@ echo "Building engine..." echo "Building HLSDK..." -# TODO: replace with FWGS/hlsdk-portable.git when PRs are merged -cd hlsdk-xash3d || die +# TODO: replace with hlsdk-portable when PRs are merged +pushd hlsdk-xash3d build_hlsdk hl valve build_hlsdk opfor gearbox build_hlsdk bshift bshift +popd echo "Copying artifacts..." From 749ac5ed544bf325820fb6ab6c1f24133252cb01 Mon Sep 17 00:00:00 2001 From: fgsfds Date: Tue, 7 Feb 2023 20:38:40 +0100 Subject: [PATCH 454/490] ci: nswitch: do not rely on dkp-pacman at all --- scripts/gha/build_nswitch_docker.sh | 22 ++++++++++++++++++---- 1 file changed, 18 insertions(+), 4 deletions(-) diff --git a/scripts/gha/build_nswitch_docker.sh b/scripts/gha/build_nswitch_docker.sh index 53698f51..94af4a34 100644 --- a/scripts/gha/build_nswitch_docker.sh +++ b/scripts/gha/build_nswitch_docker.sh @@ -14,14 +14,28 @@ build_hlsdk() ./waf clean } -echo "Downloading missing deps..." +echo "Setting up environment..." + +# we can't actually download dkp-toolchain-vars even from here, so +export PORTLIBS_ROOT=${DEVKITPRO}/portlibs +export PATH=${DEVKITPRO}/tools/bin:${DEVKITPRO}/devkitA64/bin:$PATH +export TOOL_PREFIX=aarch64-none-elf- +export CC=${TOOL_PREFIX}gcc +export CXX=${TOOL_PREFIX}g++ +export AR=${TOOL_PREFIX}gcc-ar +export RANLIB=${TOOL_PREFIX}gcc-ranlib +export PORTLIBS_PREFIX=${DEVKITPRO}/portlibs/switch +export PATH=$PORTLIBS_PREFIX/bin:$PATH +export ARCH="-march=armv8-a+crc+crypto -mtune=cortex-a57 -mtp=soft -fPIC -ftls-model=local-exec" +export CFLAGS="${ARCH} -O2 -ffunction-sections -fdata-sections" +export CXXFLAGS="${CFLAGS}" +export CPPFLAGS="-D__SWITCH__ -I${PORTLIBS_PREFIX}/include -isystem ${DEVKITPRO}/libnx/include" +export LDFLAGS="${ARCH} -L${PORTLIBS_PREFIX}/lib -L${DEVKITPRO}/libnx/lib" +export LIBS="-lnx" -dkp-pacman -S --noconfirm dkp-toolchain-vars || die # forgive me father, for I have sinned ln -s /usr/bin/python3 /usr/bin/python -source $DEVKITPRO/switchvars.sh - echo "Building libsolder..." make -C libsolder install || die From 080b9b30a16ba5d4bb43bea22e7d3fbd621dc306 Mon Sep 17 00:00:00 2001 From: fgsfds Date: Tue, 7 Feb 2023 20:45:42 +0100 Subject: [PATCH 455/490] ci: nswitch: do not put the pkgtemp folder into the .7z --- scripts/gha/build_nswitch.sh | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/scripts/gha/build_nswitch.sh b/scripts/gha/build_nswitch.sh index c93532fe..062fd748 100644 --- a/scripts/gha/build_nswitch.sh +++ b/scripts/gha/build_nswitch.sh @@ -15,4 +15,6 @@ docker run --name xash-build --rm -v `pwd`:`pwd` -w `pwd` devkitpro/devkita64:la echo "Packaging artifacts..." -7z a -t7z artifacts/xash3d-fwgs-nswitch.7z -m0=lzma2 -mx=9 -mfb=64 -md=32m -ms=on -r pkgtemp/xash3d +pushd pkgtemp +7z a -t7z artifacts/xash3d-fwgs-nswitch.7z -m0=lzma2 -mx=9 -mfb=64 -md=32m -ms=on -r xash3d/ +popd From 0ee2fd8a8a9ffb501a30b6a05e6f53c5f3f2a0e3 Mon Sep 17 00:00:00 2001 From: fgsfds Date: Tue, 7 Feb 2023 20:52:23 +0100 Subject: [PATCH 456/490] ci: nswitch: do not dumb, 00 penalty --- scripts/gha/build_nswitch.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scripts/gha/build_nswitch.sh b/scripts/gha/build_nswitch.sh index 062fd748..03f0353d 100644 --- a/scripts/gha/build_nswitch.sh +++ b/scripts/gha/build_nswitch.sh @@ -16,5 +16,5 @@ docker run --name xash-build --rm -v `pwd`:`pwd` -w `pwd` devkitpro/devkita64:la echo "Packaging artifacts..." pushd pkgtemp -7z a -t7z artifacts/xash3d-fwgs-nswitch.7z -m0=lzma2 -mx=9 -mfb=64 -md=32m -ms=on -r xash3d/ +7z a -t7z ../artifacts/xash3d-fwgs-nswitch.7z -m0=lzma2 -mx=9 -mfb=64 -md=32m -ms=on -r xash3d/ popd From 5ba2449d1058c3635c083775dd658baf184878d3 Mon Sep 17 00:00:00 2001 From: Alibek Omarov Date: Tue, 7 Feb 2023 04:50:52 +0300 Subject: [PATCH 457/490] engine: common: static-ify functions in mod_studio.c --- engine/common/mod_studio.c | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/engine/common/mod_studio.c b/engine/common/mod_studio.c index 45a39c54..56f241ae 100644 --- a/engine/common/mod_studio.c +++ b/engine/common/mod_studio.c @@ -116,7 +116,7 @@ void Mod_ClearStudioCache( void ) AddToStudioCache ==================== */ -void Mod_AddToStudioCache( float frame, int sequence, vec3_t angles, vec3_t origin, vec3_t size, byte *pcontroller, byte *pblending, model_t *model, hull_t *hull, int numhitboxes ) +static void Mod_AddToStudioCache( float frame, int sequence, vec3_t angles, vec3_t origin, vec3_t size, byte *pcontroller, byte *pblending, model_t *model, hull_t *hull, int numhitboxes ) { mstudiocache_t *pCache; @@ -153,7 +153,7 @@ void Mod_AddToStudioCache( float frame, int sequence, vec3_t angles, vec3_t orig CheckStudioCache ==================== */ -mstudiocache_t *Mod_CheckStudioCache( model_t *model, float frame, int sequence, vec3_t angles, vec3_t origin, vec3_t size, byte *controller, byte *blending ) +static mstudiocache_t *Mod_CheckStudioCache( model_t *model, float frame, int sequence, vec3_t angles, vec3_t origin, vec3_t size, byte *controller, byte *blending ) { mstudiocache_t *pCached; int i; @@ -204,7 +204,7 @@ mstudiocache_t *Mod_CheckStudioCache( model_t *model, float frame, int sequence, SetStudioHullPlane ==================== */ -void Mod_SetStudioHullPlane( int planenum, int bone, int axis, float offset, const vec3_t size ) +static void Mod_SetStudioHullPlane( int planenum, int bone, int axis, float offset, const vec3_t size ) { mplane_t *pl = &studio_planes[planenum]; @@ -823,7 +823,7 @@ int Mod_HitgroupForStudioHull( int index ) StudioBoundVertex ==================== */ -void Mod_StudioBoundVertex( vec3_t mins, vec3_t maxs, int *numverts, const vec3_t vertex ) +static void Mod_StudioBoundVertex( vec3_t mins, vec3_t maxs, int *numverts, const vec3_t vertex ) { if((*numverts) == 0 ) ClearBounds( mins, maxs ); @@ -837,7 +837,7 @@ void Mod_StudioBoundVertex( vec3_t mins, vec3_t maxs, int *numverts, const vec3_ StudioAccumulateBoneVerts ==================== */ -void Mod_StudioAccumulateBoneVerts( vec3_t mins, vec3_t maxs, int *numverts, vec3_t bone_mins, vec3_t bone_maxs, int *numbones ) +static void Mod_StudioAccumulateBoneVerts( vec3_t mins, vec3_t maxs, int *numverts, vec3_t bone_mins, vec3_t bone_maxs, int *numbones ) { vec3_t delta; vec3_t point; @@ -1009,7 +1009,7 @@ static int Mod_StudioBodyVariations( model_t *mod ) R_StudioLoadHeader ================= */ -studiohdr_t *R_StudioLoadHeader( model_t *mod, const void *buffer ) +static studiohdr_t *R_StudioLoadHeader( model_t *mod, const void *buffer ) { byte *pin; studiohdr_t *phdr; From 03a7c6773178dc06937833d9efdec0aa3093b742 Mon Sep 17 00:00:00 2001 From: Alibek Omarov Date: Wed, 8 Feb 2023 00:03:01 +0300 Subject: [PATCH 458/490] public: build: revert arm64 renaming to aarch64, we shouldn't enforce naming changes without a reason --- public/build.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/public/build.c b/public/build.c index 758ef738..a00202c1 100644 --- a/public/build.c +++ b/public/build.c @@ -162,7 +162,7 @@ const char *Q_ArchitectureStringByID( const int arch, const uint abi, const int const qboolean hardfp = FBitSet( abi, ARCHITECTURE_ARM_HARDFP ); if( is64 ) - return "aarch64"; + return "arm64"; // keep as arm64, it's not aarch64! switch( ver ) { From 4e87eb068a9d795dac4046919276f07a54a0571c Mon Sep 17 00:00:00 2001 From: fgsfds Date: Tue, 7 Feb 2023 23:03:59 +0100 Subject: [PATCH 459/490] engine: common: nswitch: use #if instead of #ifdef --- common/port.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/common/port.h b/common/port.h index 1781ee61..63419cc6 100644 --- a/common/port.h +++ b/common/port.h @@ -39,7 +39,7 @@ GNU General Public License for more details. #if XASH_POSIX #include - #ifdef XASH_NSWITCH + #if XASH_NSWITCH #define SOLDER_LIBDL_COMPAT #include #else From 69607d7890946558409b107a206ca3bafe699753 Mon Sep 17 00:00:00 2001 From: fgsfds Date: Wed, 8 Feb 2023 00:52:48 +0100 Subject: [PATCH 460/490] nswitch: do not link libstdc++ into dynamic libraries instead only link it to the main executable with --whole-archive, letting the dynamic libs import anything they want from it --- engine/platform/nswitch/sys_nswitch.c | 59 ++++++++++----------------- engine/wscript | 14 +++++-- scripts/waifulib/xcompile.py | 5 ++- wscript | 8 ++-- 4 files changed, 41 insertions(+), 45 deletions(-) diff --git a/engine/platform/nswitch/sys_nswitch.c b/engine/platform/nswitch/sys_nswitch.c index 8165f211..61203e1a 100644 --- a/engine/platform/nswitch/sys_nswitch.c +++ b/engine/platform/nswitch/sys_nswitch.c @@ -28,52 +28,37 @@ static int nxlink_sock = -1; /* HACKHACK: force-export stuff required by the dynamic libs */ -// libunwind stuff for C++ exceptions, required by filesystem_stdio -extern void *_Unwind_GetIPInfo; -extern void *_Unwind_Resume_or_Rethrow; -extern void *_Unwind_GetRegionStart; +// this is required by some std::filesystem crap in libstdc++ +// we don't have it defined in our libc +long pathconf( const char *path, int name ) { return -1; } + +// part of libunwind; required by any dynamic lib that uses C++ exceptions extern void *_Unwind_Resume; -extern void *_Unwind_DeleteException; -extern void *_Unwind_RaiseException; -extern void *_Unwind_SetIP; -extern void *_Unwind_GetTextRelBase; -extern void *_Unwind_GetLanguageSpecificData; -extern void *_Unwind_SetGR; -extern void *_Unwind_GetDataRelBase; // these are macros in our libc, so we need to wrap them -static int tolower_fn(int c) { return tolower(c); } -static int toupper_fn(int c) { return toupper(c); } -static int isalnum_fn(int c) { return isalnum(c); } -static int isalpha_fn(int c) { return isalpha(c); } +static int tolower_fn( int c ) { return tolower( c ); } +static int toupper_fn( int c ) { return toupper( c ); } +static int isalnum_fn( int c ) { return isalnum( c ); } +static int isalpha_fn( int c ) { return isalpha( c ); } static const solder_export_t aux_exports[] = { - SOLDER_EXPORT("tolower", tolower_fn), - SOLDER_EXPORT("toupper", toupper_fn), - SOLDER_EXPORT("isalnum", isalnum_fn), - SOLDER_EXPORT("isalpha", isalpha_fn), - SOLDER_EXPORT_SYMBOL(mkdir), - SOLDER_EXPORT_SYMBOL(remove), - SOLDER_EXPORT_SYMBOL(rename), - SOLDER_EXPORT_SYMBOL(fsync), - SOLDER_EXPORT_SYMBOL(strchrnul), - SOLDER_EXPORT_SYMBOL(stpcpy), - SOLDER_EXPORT_SYMBOL(_Unwind_GetIPInfo), - SOLDER_EXPORT_SYMBOL(_Unwind_Resume_or_Rethrow), - SOLDER_EXPORT_SYMBOL(_Unwind_GetRegionStart), - SOLDER_EXPORT_SYMBOL(_Unwind_Resume), - SOLDER_EXPORT_SYMBOL(_Unwind_DeleteException), - SOLDER_EXPORT_SYMBOL(_Unwind_RaiseException), - SOLDER_EXPORT_SYMBOL(_Unwind_SetIP), - SOLDER_EXPORT_SYMBOL(_Unwind_GetTextRelBase), - SOLDER_EXPORT_SYMBOL(_Unwind_GetLanguageSpecificData), - SOLDER_EXPORT_SYMBOL(_Unwind_SetGR), - SOLDER_EXPORT_SYMBOL(_Unwind_GetDataRelBase), + SOLDER_EXPORT( "tolower", tolower_fn ), + SOLDER_EXPORT( "toupper", toupper_fn ), + SOLDER_EXPORT( "isalnum", isalnum_fn ), + SOLDER_EXPORT( "isalpha", isalpha_fn ), + SOLDER_EXPORT_SYMBOL( mkdir ), + SOLDER_EXPORT_SYMBOL( remove ), + SOLDER_EXPORT_SYMBOL( rename ), + SOLDER_EXPORT_SYMBOL( pathconf ), + SOLDER_EXPORT_SYMBOL( fsync ), + SOLDER_EXPORT_SYMBOL( strchrnul ), + SOLDER_EXPORT_SYMBOL( stpcpy ), + SOLDER_EXPORT_SYMBOL( _Unwind_Resume ), }; const solder_export_t *__solder_aux_exports = aux_exports; -const size_t __solder_num_aux_exports = sizeof(aux_exports) / sizeof(*aux_exports); +const size_t __solder_num_aux_exports = sizeof( aux_exports ) / sizeof( *aux_exports ); /* end of export crap */ diff --git a/engine/wscript b/engine/wscript index 1639147e..d4b32f73 100644 --- a/engine/wscript +++ b/engine/wscript @@ -49,13 +49,21 @@ def configure(conf): if not conf.check_cc( fragment='int main(){ int i = socket();}', lib = 'wattcpwl', mandatory=False ): conf.define('XASH_NO_NETWORK',1) elif conf.env.DEST_OS == 'nswitch': + # re-enable undefined reference errors + conf.env.CXXFLAGS += ['-Wl,--no-undefined'] + conf.env.CFLAGS += ['-Wl,--no-undefined'] + # allow the SDL2 sanity check to complete properly by linking in libstdc++ and lm normally + conf.env.LDFLAGS += ['-lstdc++', '-lm'] conf.load('sdl2') if not conf.env.HAVE_SDL2: conf.fatal('SDL2 not availiable! Install switch-sdl2!') conf.define('XASH_SDL', 2) - # disallow undefined symbols - conf.env.append_unique('CXXFLAGS', '-Wl,--no-undefined') - conf.env.append_unique('CFLAGS', '-Wl,--no-undefined') + # HACK: now link in the entirety of libstdc++ so that dynamic libs could use all of it without manual exporting + # we can't do this right away because std::filesystem will complain about not having pathconf(), + # which we have defined in sys_nswitch.c + conf.env.LDFLAGS.remove('-lstdc++') + conf.env.LDFLAGS.remove('-lm') + conf.env.LDFLAGS += ['-Wl,--whole-archive', '-lstdc++', '-Wl,--no-whole-archive', '-lm'] elif conf.options.FBDEV_SW: # unused, XASH_LINUX without XASH_SDL gives fbdev & alsa support # conf.define('XASH_FBDEV', 1) diff --git a/scripts/waifulib/xcompile.py b/scripts/waifulib/xcompile.py index f2fa4091..34700ddd 100644 --- a/scripts/waifulib/xcompile.py +++ b/scripts/waifulib/xcompile.py @@ -427,8 +427,9 @@ class NintendoSwitch: return linkflags def ldflags(self): - # system libraries implicitly require math and C++ standard library - ldflags = ['-lm', '-lstdc++'] + # NOTE: shared libraries should be built without standard libs, so that they could import their contents from the NRO, + # but executables, including the SDL2 sanity check, will generally require libstdc++ and libm, which we will add manually + ldflags = [] # ['-lm', '-lstdc++'] return ldflags def options(opt): diff --git a/wscript b/wscript index 541c8adc..03105e6c 100644 --- a/wscript +++ b/wscript @@ -246,8 +246,8 @@ def configure(conf): cflags, linkflags = conf.get_optimization_flags() # on the Switch, allow undefined symbols by default, which is needed for libsolder to work - # we'll specifically disallow for the engine executable - # additionally, shared libs are linked without libc + # we'll specifically disallow them for the engine executable + # additionally, shared libs are linked without standard libs, we'll add those back in the engine wscript if conf.env.DEST_OS == 'nswitch': linkflags.remove('-Wl,--no-undefined') conf.env.append_unique('LINKFLAGS_cshlib', ['-nostdlib', '-nostartfiles']) @@ -303,7 +303,9 @@ def configure(conf): if conf.env.DEST_OS != 'win32': if conf.env.DEST_OS == 'nswitch': - conf.check_cfg(package='solder', args='--cflags --libs', uselib_store='SOLDER') + conf.check_cfg(package='solder', args='--cflags --libs', uselib_store='SOLDER', mandatory=True) + if conf.env.HAVE_SOLDER and conf.env.LIB_SOLDER and conf.options.BUILD_TYPE == 'debug': + conf.env.LIB_SOLDER[0] += 'd' # load libsolderd in debug mode else: conf.check_cc(lib='dl', mandatory=False) From f7489a37475930577218be921b6f0a0ef5ed407d Mon Sep 17 00:00:00 2001 From: fgsfds Date: Wed, 8 Feb 2023 01:04:31 +0100 Subject: [PATCH 461/490] scripts: nswitch: it's arm64, not aarch64 --- .github/workflows/c-cpp.yml | 2 +- scripts/gha/build_nswitch_docker.sh | 4 ++-- scripts/waifulib/xcompile.py | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/.github/workflows/c-cpp.yml b/.github/workflows/c-cpp.yml index 82430f05..e80c4b19 100644 --- a/.github/workflows/c-cpp.yml +++ b/.github/workflows/c-cpp.yml @@ -36,7 +36,7 @@ jobs: - os: ubuntu-20.04 targetos: nswitch - targetarch: aarch64 + targetarch: arm64 - os: windows-latest targetos: win32 targetarch: amd64 diff --git a/scripts/gha/build_nswitch_docker.sh b/scripts/gha/build_nswitch_docker.sh index 94af4a34..4732facc 100644 --- a/scripts/gha/build_nswitch_docker.sh +++ b/scripts/gha/build_nswitch_docker.sh @@ -9,8 +9,8 @@ build_hlsdk() git checkout switch-$1 ./waf configure -T release --nswitch || die_configure ./waf build || die - cp build/dlls/$1_nswitch_aarch64.so ../pkgtemp/xash3d/$2/dlls/ - cp build/cl_dll/client_nswitch_aarch64.so ../pkgtemp/xash3d/$2/cl_dlls/ + cp build/dlls/$1_nswitch_arm64.so ../pkgtemp/xash3d/$2/dlls/ + cp build/cl_dll/client_nswitch_arm64.so ../pkgtemp/xash3d/$2/cl_dlls/ ./waf clean } diff --git a/scripts/waifulib/xcompile.py b/scripts/waifulib/xcompile.py index 34700ddd..21631d38 100644 --- a/scripts/waifulib/xcompile.py +++ b/scripts/waifulib/xcompile.py @@ -352,7 +352,7 @@ class Android: class NintendoSwitch: ctx = None # waf context - arch = "aarch64" + arch = "arm64" dkp_dir = None portlibs_dir = None dka64_dir = None From b2cc96cf0d39ecf8bad69241a7daff83d5a66308 Mon Sep 17 00:00:00 2001 From: fgsfds Date: Wed, 8 Feb 2023 01:37:35 +0100 Subject: [PATCH 462/490] scripts: wscript: nswitch: do the libstdc++ hack right before build to not pollute the environment --- engine/wscript | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/engine/wscript b/engine/wscript index d4b32f73..d650a457 100644 --- a/engine/wscript +++ b/engine/wscript @@ -58,12 +58,8 @@ def configure(conf): if not conf.env.HAVE_SDL2: conf.fatal('SDL2 not availiable! Install switch-sdl2!') conf.define('XASH_SDL', 2) - # HACK: now link in the entirety of libstdc++ so that dynamic libs could use all of it without manual exporting - # we can't do this right away because std::filesystem will complain about not having pathconf(), - # which we have defined in sys_nswitch.c conf.env.LDFLAGS.remove('-lstdc++') conf.env.LDFLAGS.remove('-lm') - conf.env.LDFLAGS += ['-Wl,--whole-archive', '-lstdc++', '-Wl,--no-whole-archive', '-lm'] elif conf.options.FBDEV_SW: # unused, XASH_LINUX without XASH_SDL gives fbdev & alsa support # conf.define('XASH_FBDEV', 1) @@ -195,6 +191,10 @@ def build(bld): # Switch has custom parameters if bld.env.DEST_OS == 'nswitch': + # HACK: link in the entirety of libstdc++ so that dynamic libs could use all of it without manual exporting + # we can't do this right away because std::filesystem will complain about not having pathconf(), + # which we have defined in sys_nswitch.c + bld.env.LDFLAGS += ['-Wl,--whole-archive', '-lstdc++', '-Wl,--no-whole-archive', '-lm'] bld(source = source, target = 'xash', features = 'c cxxprogram', From 35e073cefffcf140c1870611738880a87cbb53b1 Mon Sep 17 00:00:00 2001 From: fgsfds Date: Wed, 8 Feb 2023 01:53:26 +0100 Subject: [PATCH 463/490] ci: nswitch: don't forget filesystem_stdio --- scripts/gha/build_nswitch_docker.sh | 1 + 1 file changed, 1 insertion(+) diff --git a/scripts/gha/build_nswitch_docker.sh b/scripts/gha/build_nswitch_docker.sh index 4732facc..5557e9ea 100644 --- a/scripts/gha/build_nswitch_docker.sh +++ b/scripts/gha/build_nswitch_docker.sh @@ -57,6 +57,7 @@ popd echo "Copying artifacts..." cp build/engine/xash.nro pkgtemp/xash3d/xash3d.nro +cp build/filesystem/filesystem_stdio.so pkgtemp/xash3d/ cp build/ref/gl/libref_gl.so pkgtemp/xash3d/ cp build/ref/soft/libref_soft.so pkgtemp/xash3d/ cp build/3rdparty/mainui/libmenu.so pkgtemp/xash3d/ From b5b6b8b78516a47e914a8b6a5cd20dc7e5e7c9a2 Mon Sep 17 00:00:00 2001 From: Alibek Omarov Date: Wed, 8 Feb 2023 20:00:51 +0300 Subject: [PATCH 464/490] engine: network: fix some unitialized sockaddr_storage's --- engine/common/net_ws.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/engine/common/net_ws.c b/engine/common/net_ws.c index 549536b4..bc676d30 100644 --- a/engine/common/net_ws.c +++ b/engine/common/net_ws.c @@ -1446,7 +1446,7 @@ static qboolean NET_QueuePacket( netsrc_t sock, netadr_t *from, byte *data, size int ret, protocol; int net_socket; WSAsize_t addr_len; - struct sockaddr_storage addr; + struct sockaddr_storage addr = { 0 }; *length = 0; @@ -1612,7 +1612,7 @@ NET_SendPacketEx void NET_SendPacketEx( netsrc_t sock, size_t length, const void *data, netadr_t to, size_t splitsize ) { int ret; - struct sockaddr_storage addr; + struct sockaddr_storage addr = { 0 }; SOCKET net_socket = 0; if( !net.initialized || to.type == NA_LOOPBACK ) @@ -1748,7 +1748,7 @@ NET_IPSocket */ static int NET_IPSocket( const char *net_iface, int port, int family ) { - struct sockaddr_storage addr; + struct sockaddr_storage addr = { 0 }; int err, net_socket; uint optval = 1; dword _true = 1; From 12b8965a8c49f7099c796a4baba8e9f70f8534c0 Mon Sep 17 00:00:00 2001 From: Alibek Omarov Date: Wed, 8 Feb 2023 20:01:06 +0300 Subject: [PATCH 465/490] mainui: update --- 3rdparty/mainui | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/3rdparty/mainui b/3rdparty/mainui index 8ae451a2..2f615a74 160000 --- a/3rdparty/mainui +++ b/3rdparty/mainui @@ -1 +1 @@ -Subproject commit 8ae451a2b5b93e488ce40b8f815eee4166880495 +Subproject commit 2f615a74802e665014cddaf766e4edc2bac24a55 From f7f9cfecfc01adf8089509cde4758e812eb2dcf4 Mon Sep 17 00:00:00 2001 From: Alibek Omarov Date: Thu, 9 Feb 2023 05:35:30 +0300 Subject: [PATCH 466/490] ci: nswitch: use waf install to copy build artifacts --- scripts/gha/build_nswitch_docker.sh | 28 ++++++++-------------------- scripts/gha/deps_nswitch.sh | 5 ++--- 2 files changed, 10 insertions(+), 23 deletions(-) diff --git a/scripts/gha/build_nswitch_docker.sh b/scripts/gha/build_nswitch_docker.sh index 5557e9ea..18aa6dec 100644 --- a/scripts/gha/build_nswitch_docker.sh +++ b/scripts/gha/build_nswitch_docker.sh @@ -2,16 +2,13 @@ . scripts/lib.sh -# args: branch name, gamedir name build_hlsdk() { - echo "Building HLSDK: $1 branch..." - git checkout switch-$1 - ./waf configure -T release --nswitch || die_configure - ./waf build || die - cp build/dlls/$1_nswitch_arm64.so ../pkgtemp/xash3d/$2/dlls/ - cp build/cl_dll/client_nswitch_arm64.so ../pkgtemp/xash3d/$2/cl_dlls/ - ./waf clean + echo "Building HLSDK: $1 branch..." + git checkout $1 + ./waf configure -T release --nswitch || die_configure + ./waf build install --destdir=../pkgtemp/xash3d || die + ./waf clean } echo "Setting up environment..." @@ -43,22 +40,13 @@ make -C libsolder install || die echo "Building engine..." ./waf configure -T release --nswitch || die_configure -./waf build || die +./waf build install --destdir=pkgtemp/xash3d || die echo "Building HLSDK..." # TODO: replace with hlsdk-portable when PRs are merged -pushd hlsdk-xash3d -build_hlsdk hl valve +pushd hlsdk-portable +build_hlsdk mobile_hacks valve build_hlsdk opfor gearbox build_hlsdk bshift bshift popd - -echo "Copying artifacts..." - -cp build/engine/xash.nro pkgtemp/xash3d/xash3d.nro -cp build/filesystem/filesystem_stdio.so pkgtemp/xash3d/ -cp build/ref/gl/libref_gl.so pkgtemp/xash3d/ -cp build/ref/soft/libref_soft.so pkgtemp/xash3d/ -cp build/3rdparty/mainui/libmenu.so pkgtemp/xash3d/ -cp build/3rdparty/extras/extras.pk3 pkgtemp/xash3d/valve/ diff --git a/scripts/gha/deps_nswitch.sh b/scripts/gha/deps_nswitch.sh index 5f577818..7f7933f6 100644 --- a/scripts/gha/deps_nswitch.sh +++ b/scripts/gha/deps_nswitch.sh @@ -9,10 +9,9 @@ docker pull devkitpro/devkita64:latest || exit 1 echo "Downloading libsolder..." rm -rf libsolder -git clone https://github.com/fgsfdsfgs/libsolder.git || exit 1 +git clone https://github.com/fgsfdsfgs/libsolder.git --depth=1 || exit 1 echo "Downloading HLSDK..." -# TODO: change to FWGS/hlsdk-portable.git when changes are merged in rm -rf hlsdk-xash3d hlsdk-portable -git clone --recursive https://github.com/fgsfdsfgs/hlsdk-xash3d.git || exit 1 +git clone --recursive https://github.com/FWGS/hlsdk-portable || exit 1 From 555fd02407a3dc1073afff56d7048712d6ad91fa Mon Sep 17 00:00:00 2001 From: Alibek Omarov Date: Thu, 9 Feb 2023 05:55:35 +0300 Subject: [PATCH 467/490] defaults: reorganize platform default overrides, disable touch for nswitch --- common/defaults.h | 41 ++++++++++++++++++++++++++--------------- 1 file changed, 26 insertions(+), 15 deletions(-) diff --git a/common/defaults.h b/common/defaults.h index 2144b3be..746b0f4d 100644 --- a/common/defaults.h +++ b/common/defaults.h @@ -158,32 +158,43 @@ Default build-depended cvar and constant values ========================================================================= */ -#if XASH_MOBILE_PLATFORM - #define DEFAULT_TOUCH_ENABLE "1" - #define DEFAULT_M_IGNORE "1" -#else // !XASH_MOBILE_PLATFORM +// Platform overrides +#if XASH_NSWITCH #define DEFAULT_TOUCH_ENABLE "0" - #define DEFAULT_M_IGNORE "0" -#endif // !XASH_MOBILE_PLATFORM + #define DEFAULT_M_IGNORE "1" + #define DEFAULT_MODE_WIDTH 1280 + #define DEFAULT_MODE_HEIGHT 720 + #define DEFAULT_ALLOWCONSOLE 1 +#elif XASH_MOBILE_PLATFORM + #define DEFAULT_TOUCH_ENABLE "1" + #define DEFAULT_M_IGNORE "1" +#endif // !XASH_MOBILE_PLATFORM && !XASH_NSWITCH #if XASH_ANDROID || XASH_IOS || XASH_EMSCRIPTEN -#define XASH_INTERNAL_GAMELIBS -// this means that libraries are provided with engine, but not in game data -// You need add library loading code to library.c when adding new platform + // this means that libraries are provided with engine, but not in game data + // You need add library loading code to library.c when adding new platform + #define XASH_INTERNAL_GAMELIBS #endif // XASH_ANDROID || XASH_IOS || XASH_EMSCRIPTEN -// allow override for developer/debug builds +// Defaults +#ifndef DEFAULT_TOUCH_ENABLE + #define DEFAULT_TOUCH_ENABLE "0" +#endif // DEFAULT_TOUCH_ENABLE + +#ifndef DEFAULT_M_IGNORE + #define DEFAULT_M_IGNORE "0" +#endif // DEFAULT_M_IGNORE + #ifndef DEFAULT_DEV #define DEFAULT_DEV 0 #endif // DEFAULT_DEV +#ifndef DEFAULT_ALLOWCONSOLE + #define DEFAULT_ALLOWCONSOLE 0 +#endif // DEFAULT_ALLOWCONSOLE + #ifndef DEFAULT_FULLSCREEN #define DEFAULT_FULLSCREEN 1 #endif // DEFAULT_FULLSCREEN -#if XASH_NSWITCH - #define DEFAULT_MODE_WIDTH 1280 - #define DEFAULT_MODE_HEIGHT 720 -#endif // XASH_NSWITCH - #endif // DEFAULTS_H From a0edfd28b2fea428a2f66e131ad1e411de8bc4af Mon Sep 17 00:00:00 2001 From: Alibek Omarov Date: Thu, 9 Feb 2023 05:56:11 +0300 Subject: [PATCH 468/490] engine: common: host: use DEFAULT_ALLOWCONSOLE macro to set default console state --- engine/common/host.c | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/engine/common/host.c b/engine/common/host.c index 44a83d09..c3297c96 100644 --- a/engine/common/host.c +++ b/engine/common/host.c @@ -894,12 +894,11 @@ void Host_InitCommon( int argc, char **argv, const char *progname, qboolean bCha host.mempool = Mem_AllocPool( "Zone Engine" ); + host.allow_console = DEFAULT_ALLOWCONSOLE; + // HACKHACK: Quake console is always allowed - // HACKHACK: console is also always allowed on the Switch since we can't really pass command line // TODO: determine if we are running QWrap more reliable -#if !XASH_NSWITCH - if( Sys_CheckParm( "-console" ) || !Q_stricmp( SI.exeName, "quake" ) ) -#endif + if( !host.allow_console && ( Sys_CheckParm( "-console" ) || !Q_stricmp( SI.exeName, "quake" ))) host.allow_console = true; if( Sys_CheckParm( "-dev" )) From 5e1b5d89f7820fef6795f932fe9eafe6b6a423b3 Mon Sep 17 00:00:00 2001 From: Alibek Omarov Date: Thu, 9 Feb 2023 05:57:08 +0300 Subject: [PATCH 469/490] engine: client: console: allow opening OSK and existing console with gamepads for all platforms --- engine/client/console.c | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/engine/client/console.c b/engine/client/console.c index cf7336e8..615142a0 100644 --- a/engine/client/console.c +++ b/engine/client/console.c @@ -1638,7 +1638,6 @@ void Key_Console( int key ) return; } -#if XASH_NSWITCH // enable the OSK with button press if( key == K_Y_BUTTON ) { @@ -1646,15 +1645,15 @@ void Key_Console( int key ) return; } - // exit the console by pressing MINUS - if( key == K_BACK_BUTTON ) + // exit the console by pressing MINUS on NSwitch + // or both Back(Select)/Start buttons for everyone else + if( key == K_BACK_BUTTON || key == K_START_BUTTON ) { if( cls.state == ca_active && !cl.background ) Key_SetKeyDest( key_game ); else UI_SetActiveMenu( true ); return; } -#endif // pass to the normal editline routine Field_KeyDownEvent( &con.input, key ); From d6d98bd2974a00e48b007493865fbd7ffadc27b8 Mon Sep 17 00:00:00 2001 From: Alibek Omarov Date: Thu, 9 Feb 2023 05:59:30 +0300 Subject: [PATCH 470/490] engine: platform: sdl: minor style changes --- engine/common/sys_con.c | 1 + engine/platform/sdl/sys_sdl.c | 20 +++++++++----------- 2 files changed, 10 insertions(+), 11 deletions(-) diff --git a/engine/common/sys_con.c b/engine/common/sys_con.c index 6a0c3dd2..53cb27d3 100644 --- a/engine/common/sys_con.c +++ b/engine/common/sys_con.c @@ -269,6 +269,7 @@ static void Sys_PrintStdout( const char *logtime, const char *msg ) // just spew it to stderr normally in debug mode fprintf( stderr, "%s %s", logtime, buf ); #endif // XASH_NSWITCH && NSWITCH_DEBUG + #elif !XASH_WIN32 // Wcon does the job Sys_PrintLogfile( STDOUT_FILENO, logtime, msg, XASH_COLORIZE_CONSOLE ); Sys_FlushStdout(); diff --git a/engine/platform/sdl/sys_sdl.c b/engine/platform/sdl/sys_sdl.c index 8c6fc13f..ed1c3661 100644 --- a/engine/platform/sdl/sys_sdl.c +++ b/engine/platform/sdl/sys_sdl.c @@ -62,14 +62,13 @@ void Platform_Init( void ) SDL_SetHint(SDL_HINT_ACCELEROMETER_AS_JOYSTICK, "0"); SDL_StopTextInput(); #endif // XASH_SDL == 2 -#if XASH_POSIX - Posix_Daemonize(); -#endif -#ifdef XASH_WIN32 - Wcon_CreateConsole(); // system console used by dedicated server or show fatal errors -#endif -#ifdef XASH_NSWITCH + +#if XASH_NSWITCH NSwitch_Init(); +#elif XASH_WIN32 + Wcon_CreateConsole(); // system console used by dedicated server or show fatal errors +#elif XASH_POSIX + Posix_Daemonize(); #endif SDLash_InitCursors(); @@ -79,10 +78,9 @@ void Platform_Shutdown( void ) { SDLash_FreeCursors(); -#ifdef XASH_WIN32 +#if XASH_NSWITCH + NSwitch_Shutdown(); +#elif XASH_WIN32 Wcon_DestroyConsole(); #endif -#ifdef XASH_NSWITCH - NSwitch_Shutdown(); -#endif } From c741ec223f88a1bcc8677019e717ad2b6d379453 Mon Sep 17 00:00:00 2001 From: Alibek Omarov Date: Thu, 9 Feb 2023 06:29:16 +0300 Subject: [PATCH 471/490] engine: client: keys: reserve some more buttons as gamepad buttons according to latest SDL2 GameController header --- engine/client/keys.c | 14 +++++++------- engine/keydefs.h | 12 ++++++++++++ 2 files changed, 19 insertions(+), 7 deletions(-) diff --git a/engine/client/keys.c b/engine/client/keys.c index f1c0d688..7a53da72 100644 --- a/engine/client/keys.c +++ b/engine/client/keys.c @@ -123,13 +123,13 @@ keyname_t keynames[] = {"JOY4" , K_JOY4 , ""}, {"C_BUTTON", K_C_BUTTON, ""}, {"Z_BUTTON", K_Z_BUTTON, ""}, -{"AUX20", K_AUX20, ""}, // generic -{"AUX21", K_AUX21, ""}, -{"AUX22", K_AUX22, ""}, -{"AUX23", K_AUX23, ""}, -{"AUX24", K_AUX24, ""}, -{"AUX25", K_AUX25, ""}, -{"AUX26", K_AUX26, ""}, +{"MISC_BUTTON", K_MISC_BUTTON, ""}, +{"PADDLE1", K_PADDLE1_BUTTON, ""}, +{"PADDLE2", K_PADDLE2_BUTTON, ""}, +{"PADDLE3", K_PADDLE3_BUTTON, ""}, +{"PADDLE4", K_PADDLE4_BUTTON, ""}, +{"TOUCHPAD", K_TOUCHPAD, ""}, +{"AUX26", K_AUX26, ""}, // generic {"AUX27", K_AUX27, ""}, {"AUX28", K_AUX28, ""}, {"AUX29", K_AUX29, ""}, diff --git a/engine/keydefs.h b/engine/keydefs.h index 6bce443a..61c5e03e 100644 --- a/engine/keydefs.h +++ b/engine/keydefs.h @@ -145,11 +145,23 @@ #define K_DPAD_RIGHT K_AUX19 #define K_AUX20 226 +#define K_MISC_BUTTON K_AUX20 // Xbox Series X share button, PS5 microphone button, Nintendo Switch Pro capture button + #define K_AUX21 227 +#define K_PADDLE1_BUTTON K_AUX21 // Xbox Elite paddle P1-P4 + #define K_AUX22 228 +#define K_PADDLE2_BUTTON K_AUX22 + #define K_AUX23 229 +#define K_PADDLE3_BUTTON K_AUX23 + #define K_AUX24 230 +#define K_PADDLE4_BUTTON K_AUX24 + #define K_AUX25 231 +#define K_TOUCHPAD K_AUX25 // PS4/PS5 touchpad button + #define K_AUX26 232 #define K_AUX27 233 #define K_AUX28 234 From 33c9f7118b255415d861a32473fb1dd28f0bd64a Mon Sep 17 00:00:00 2001 From: Alibek Omarov Date: Thu, 9 Feb 2023 06:31:19 +0300 Subject: [PATCH 472/490] engine: platform: sdl: sanitize buttons/axes from SDL, add ABXY->BAYX swap for NSwitch --- engine/platform/sdl/events.c | 62 +++++++++++++++++++++++------------- 1 file changed, 40 insertions(+), 22 deletions(-) diff --git a/engine/platform/sdl/events.c b/engine/platform/sdl/events.c index 0219605c..bd45bb9d 100644 --- a/engine/platform/sdl/events.c +++ b/engine/platform/sdl/events.c @@ -91,6 +91,34 @@ GNU General Public License for more details. #define SDL_JoystickID Uint8 #endif +static int SDLash_GameControllerButtonMapping[] = +{ +#if XASH_NSWITCH // devkitPro/SDL has inverted Nintendo layout for SDL_GameController + K_B_BUTTON, K_A_BUTTON, K_Y_BUTTON, K_X_BUTTON, +#else + K_A_BUTTON, K_B_BUTTON, K_X_BUTTON, K_Y_BUTTON, +#endif + K_BACK_BUTTON, K_MODE_BUTTON, K_START_BUTTON, + K_LSTICK, K_RSTICK, + K_L1_BUTTON, K_R1_BUTTON, + K_DPAD_UP, K_DPAD_DOWN, K_DPAD_LEFT, K_DPAD_RIGHT, + K_MISC_BUTTON, + K_PADDLE1_BUTTON, K_PADDLE2_BUTTON, K_PADDLE3_BUTTON, K_PADDLE4_BUTTON, + K_TOUCHPAD, +}; + +// Swap axis to follow default axis binding: +// LeftX, LeftY, RightX, RightY, TriggerRight, TriggerLeft +static int SDLash_GameControllerAxisMapping[] = +{ + JOY_AXIS_SIDE, // SDL_CONTROLLER_AXIS_LEFTX, + JOY_AXIS_FWD, // SDL_CONTROLLER_AXIS_LEFTY, + JOY_AXIS_PITCH, // SDL_CONTROLLER_AXIS_RIGHTX, + JOY_AXIS_YAW, // SDL_CONTROLLER_AXIS_RIGHTY, + JOY_AXIS_LT, // SDL_CONTROLLER_AXIS_TRIGGERLEFT, + JOY_AXIS_RT, // SDL_CONTROLLER_AXIS_TRIGGERRIGHT, +}; + static qboolean SDLash_IsInstanceIDAGameController( SDL_JoystickID joyId ) { #if !SDL_VERSION_ATLEAST( 2, 0, 4 ) @@ -544,37 +572,27 @@ static void SDLash_EventFilter( SDL_Event *event ) /* GameController API */ case SDL_CONTROLLERAXISMOTION: { - // Swap axis to follow default axis binding: - // LeftX, LeftY, RightX, RightY, TriggerRight, TriggerLeft - static int sdlControllerAxisToEngine[] = + if( !Joy_IsActive( )) + break; + + if( event->caxis.axis >= 0 && event->caxis.axis < ARRAYSIZE( SDLash_GameControllerAxisMapping )) { - JOY_AXIS_SIDE, // SDL_CONTROLLER_AXIS_LEFTX, - JOY_AXIS_FWD, // SDL_CONTROLLER_AXIS_LEFTY, - JOY_AXIS_PITCH, // SDL_CONTROLLER_AXIS_RIGHTX, - JOY_AXIS_YAW, // SDL_CONTROLLER_AXIS_RIGHTY, - JOY_AXIS_LT, // SDL_CONTROLLER_AXIS_TRIGGERLEFT, - JOY_AXIS_RT, // SDL_CONTROLLER_AXIS_TRIGGERRIGHT, - }; - if( Joy_IsActive() && event->caxis.axis != (Uint8)SDL_CONTROLLER_AXIS_INVALID ) - Joy_KnownAxisMotionEvent( sdlControllerAxisToEngine[event->caxis.axis], event->caxis.value ); + Joy_KnownAxisMotionEvent( SDLash_GameControllerAxisMapping[event->caxis.axis], event->caxis.value ); + } break; } case SDL_CONTROLLERBUTTONDOWN: case SDL_CONTROLLERBUTTONUP: { - static int sdlControllerButtonToEngine[] = - { - K_A_BUTTON, K_B_BUTTON, K_X_BUTTON, K_Y_BUTTON, - K_BACK_BUTTON, K_MODE_BUTTON, K_START_BUTTON, - K_LSTICK, K_RSTICK, - K_L1_BUTTON, K_R1_BUTTON, - K_DPAD_UP, K_DPAD_DOWN, K_DPAD_LEFT, K_DPAD_RIGHT - }; + if( !Joy_IsActive( )) + break; // TODO: Use joyinput funcs, for future multiple gamepads support - if( Joy_IsActive() && event->cbutton.button != (Uint8)SDL_CONTROLLER_BUTTON_INVALID ) - Key_Event( sdlControllerButtonToEngine[event->cbutton.button], event->cbutton.state ); + if( event->cbutton.button >= 0 && event->cbutton.button < ARRAYSIZE( SDLash_GameControllerButtonMapping )) + { + Key_Event( SDLash_GameControllerButtonMapping[event->cbutton.button], event->cbutton.state ); + } break; } From c23396f533ac42d06591302486fd06345aaf9689 Mon Sep 17 00:00:00 2001 From: Alibek Omarov Date: Thu, 9 Feb 2023 06:32:14 +0300 Subject: [PATCH 473/490] engine: client: keys: hardcode K_START_BUTTON as escape button, cancelselect doesn't exist anymore and many games seems to use this button for menu access, and back for pause --- engine/client/keys.c | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/engine/client/keys.c b/engine/client/keys.c index 7a53da72..292d5a29 100644 --- a/engine/client/keys.c +++ b/engine/client/keys.c @@ -104,9 +104,9 @@ keyname_t keynames[] = {"B_BUTTON", K_B_BUTTON, "+use"}, {"X_BUTTON", K_X_BUTTON, "+reload"}, {"Y_BUTTON", K_Y_BUTTON, "impulse 100"}, // Flashlight -{"BACK", K_BACK_BUTTON, "cancelselect"}, // Menu +{"BACK", K_BACK_BUTTON, "pause"}, // Menu {"MODE", K_MODE_BUTTON, ""}, -{"START", K_START_BUTTON, "pause"}, +{"START", K_START_BUTTON, "escape"}, {"STICK1", K_LSTICK, "+speed"}, {"STICK2", K_RSTICK, "+duck"}, {"L1_BUTTON", K_L1_BUTTON, "+duck"}, @@ -374,7 +374,7 @@ void Key_Unbindall_f( void ) { int i; - for( i = 0; i < 256; i++ ) + for( i = 0; i < ARRAYSIZE( keys ); i++ ) { if( keys[i].binding ) Key_SetBinding( i, "" ); @@ -382,6 +382,7 @@ void Key_Unbindall_f( void ) // set some defaults Key_SetBinding( K_ESCAPE, "escape" ); + Key_SetBinding( K_START_BUTTON, "escape" ); } /* @@ -395,7 +396,7 @@ void Key_Reset_f( void ) int i; // clear all keys first - for( i = 0; i < 256; i++ ) + for( i = 0; i < ARRAYSIZE( keys ); i++ ) { if( keys[i].binding ) Key_SetBinding( i, "" ); From fccf04497613215c6ddc32ad771544ac0b0cf556 Mon Sep 17 00:00:00 2001 From: Alibek Omarov Date: Thu, 9 Feb 2023 17:57:45 +0300 Subject: [PATCH 474/490] engine: initialize network buffers used on player connect and after --- engine/client/cl_main.c | 1 + engine/common/net_chan.c | 1 + engine/server/sv_client.c | 2 ++ engine/server/sv_frame.c | 1 + 4 files changed, 5 insertions(+) diff --git a/engine/client/cl_main.c b/engine/client/cl_main.c index fe237168..8f6e439e 100644 --- a/engine/client/cl_main.c +++ b/engine/client/cl_main.c @@ -709,6 +709,7 @@ void CL_WritePacket( void ) CL_ComputePacketLoss (); + memset( data, 0, sizeof( data )); MSG_Init( &buf, "ClientData", data, sizeof( data )); // Determine number of backup commands to send along diff --git a/engine/common/net_chan.c b/engine/common/net_chan.c index ba000172..934cf007 100644 --- a/engine/common/net_chan.c +++ b/engine/common/net_chan.c @@ -1589,6 +1589,7 @@ void Netchan_TransmitBits( netchan_t *chan, int length, byte *data ) } } + memset( send_buf, 0, sizeof( send_buf )); MSG_Init( &send, "NetSend", send_buf, sizeof( send_buf )); // prepare the packet header diff --git a/engine/server/sv_client.c b/engine/server/sv_client.c index 26bb4870..0894091b 100644 --- a/engine/server/sv_client.c +++ b/engine/server/sv_client.c @@ -1632,6 +1632,7 @@ static qboolean SV_New_f( sv_client_t *cl ) sizebuf_t msg; int i; + memset( msg_buf, 0, sizeof( msg_buf )); MSG_Init( &msg, "New", msg_buf, sizeof( msg_buf )); if( cl->state != cs_connected ) @@ -1982,6 +1983,7 @@ static qboolean SV_SendRes_f( sv_client_t *cl ) if( cl->state != cs_connected ) return false; + memset( buffer, 0, sizeof( buffer )); MSG_Init( &msg, "SendResources", buffer, sizeof( buffer )); if( svs.maxclients > 1 && FBitSet( cl->flags, FCL_SEND_RESOURCES )) diff --git a/engine/server/sv_frame.c b/engine/server/sv_frame.c index 55c871f5..c1452a24 100644 --- a/engine/server/sv_frame.c +++ b/engine/server/sv_frame.c @@ -711,6 +711,7 @@ void SV_SendClientDatagram( sv_client_t *cl ) byte msg_buf[MAX_DATAGRAM]; sizebuf_t msg; + memset( msg_buf, 0, sizeof( msg_buf )); MSG_Init( &msg, "Datagram", msg_buf, sizeof( msg_buf )); // always send servertime at new frame From 11f3d97cd7a0c4db5dedd6e757adc72cacf9ec7f Mon Sep 17 00:00:00 2001 From: Alibek Omarov Date: Sat, 11 Feb 2023 03:34:48 +0300 Subject: [PATCH 475/490] scripts: flatpak: respect XDG data home in launcher script, add Steam Flatpak data directory --- scripts/flatpak/run.sh | 64 ++++++++++++++++++++++++++++++++---------- 1 file changed, 49 insertions(+), 15 deletions(-) diff --git a/scripts/flatpak/run.sh b/scripts/flatpak/run.sh index 7183cf94..17096006 100755 --- a/scripts/flatpak/run.sh +++ b/scripts/flatpak/run.sh @@ -1,24 +1,58 @@ #!/bin/sh +die() +{ + echo "$@" + exit 1 +} + echo "Xash3D FWGS installed as Flatpak." -export XASH3D_BASEDIR="$HOME/.xash/" -mkdir -p $XASH3D_BASEDIR -cd $XASH3D_BASEDIR -echo "Base directory is $XASH3D_BASEDIR" +# https://specifications.freedesktop.org/basedir-spec/basedir-spec-latest.html +# $XDG_DATA_HOME defines the base directory relative to which user-specific data files should be stored. +# If $XDG_DATA_HOME is either not set or empty, a default equal to $HOME/.local/share should be used. +if [ -z "$XDG_DATA_HOME" ]; then + export XDG_DATA_HOME="$HOME/.local/share" +fi -# TODO: detect by libraryfolders.vdf and installed apps -HALFLIFESTEAMDIRS="../.local/share/Steam/steamapps/common/Half-Life ../.steam/steam/steamapps/common/Half-Life" +if [ -z "$XASH3D_BASEDIR" ]; then + export XASH3D_BASEDIR="$XDG_DATA_HOME/xash3d-fwgs/" +fi -for i in $HALFLIFESTEAMDIRS; do -# echo $i - if [ -d "$i" ]; then - echo "Detected Half-Life installation in $i, using as RoDir" - export XASH3D_RODIR=$i - break - fi -done +mkdir -p "$XASH3D_BASEDIR" +cd "$XASH3D_BASEDIR" || die "Can't cd into $XASH3D_BASEDIR" +echo "XASH3D_BASEDIR is $XASH3D_BASEDIR" +if [ -z "$XASH3D_RODIR" ]; then + # TODO: detect by libraryfolders.vdf and installed apps + STEAMDIRS="\ + $HOME/.local/share/Steam/steamapps/common/ \ + $HOME/.var/app/com.valvesoftware.Steam/.local/share/Steam/steamapps/common \ + $HOME/.steam/steam/steamapps/common" + HALFLIFEDIR="Half-Life" + + for i in $STEAMDIRS; do + if [ ! -d "$i" ]; then + continue + fi + + echo "Detected Steam library in $i, probing Half-Life..." + + if [ ! -d "$i$HALFLIFEDIR" ]; then + continue + fi + + echo "Detected Half-Life installation in $i$HALFLIFEDIR..." + + export XASH3D_RODIR="$i$HALFLIFEDIR" + break + done +fi +echo "XASH3D_RODIR is $XASH3D_RODIR" + +if [ -z "$XASH3D_EXTRAS_PAK1" ]; then + export XASH3D_EXTRAS_PAK1=/app/share/xash3d/valve/extras.pk3 +fi +echo "XASH3D_EXTRAS_PAK1 is $XASH3D_EXTRAS_PAK1" -export XASH3D_EXTRAS_PAK1=/app/share/xash3d/valve/extras.pk3 exec $DEBUGGER /app/lib32/xash3d/xash3d "$@" From 1bdd8448606a75beb303795f6c8e5ae4b8d19f2d Mon Sep 17 00:00:00 2001 From: Alibek Omarov Date: Sat, 11 Feb 2023 03:36:07 +0300 Subject: [PATCH 476/490] scripts: flatpak: minimize filesystem permissions, add Steam Flatpak data directory --- scripts/flatpak/su.xash.Engine.Compat.i386.yml | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/scripts/flatpak/su.xash.Engine.Compat.i386.yml b/scripts/flatpak/su.xash.Engine.Compat.i386.yml index 02a02ad7..5feeb369 100644 --- a/scripts/flatpak/su.xash.Engine.Compat.i386.yml +++ b/scripts/flatpak/su.xash.Engine.Compat.i386.yml @@ -16,10 +16,15 @@ finish-args: - --socket=x11 - --socket=pulseaudio - --share=network - - --filesystem=~/.steam:ro - - --filesystem=~/.local/share/Steam:ro - - --filesystem=~/.xash:rw - - --filesystem=xdg-run/app/com.discordapp.Discord:create + # Steam library path on Ubuntu + - --filesystem=~/.steam/steam/steamapps/common:ro + # ... on SteamOS 3 + - --filesystem=~/.local/share/Steam/steamapps/common:ro + # ... on Steam Flatpak + - --filesystem=~/.var/app/com.valvesoftware.Steam/.local/share/Steam/steamapps/common:ro + # allow talking with Discord for mods + # TODO: enable when somebody asks for it + # - --filesystem=xdg-run/app/com.discordapp.Discord:create - --device=all - --allow=multiarch - --allow=devel From d58105d64d46ec7878b44b880510f0dc868ace8c Mon Sep 17 00:00:00 2001 From: Alibek Omarov Date: Sat, 11 Feb 2023 03:56:40 +0300 Subject: [PATCH 477/490] scripts: flatpak: fix Half-Life directory detection --- scripts/flatpak/run.sh | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/scripts/flatpak/run.sh b/scripts/flatpak/run.sh index 17096006..c0fc4667 100755 --- a/scripts/flatpak/run.sh +++ b/scripts/flatpak/run.sh @@ -26,7 +26,7 @@ echo "XASH3D_BASEDIR is $XASH3D_BASEDIR" if [ -z "$XASH3D_RODIR" ]; then # TODO: detect by libraryfolders.vdf and installed apps STEAMDIRS="\ - $HOME/.local/share/Steam/steamapps/common/ \ + $HOME/.local/share/Steam/steamapps/common \ $HOME/.var/app/com.valvesoftware.Steam/.local/share/Steam/steamapps/common \ $HOME/.steam/steam/steamapps/common" HALFLIFEDIR="Half-Life" @@ -38,13 +38,13 @@ if [ -z "$XASH3D_RODIR" ]; then echo "Detected Steam library in $i, probing Half-Life..." - if [ ! -d "$i$HALFLIFEDIR" ]; then + if [ ! -d "$i/$HALFLIFEDIR" ]; then continue fi - echo "Detected Half-Life installation in $i$HALFLIFEDIR..." + echo "Detected Half-Life installation in $i/$HALFLIFEDIR..." - export XASH3D_RODIR="$i$HALFLIFEDIR" + export XASH3D_RODIR="$i/$HALFLIFEDIR" break done fi From 3cfdb1213b0c4d99a51314ead254655a55913e6a Mon Sep 17 00:00:00 2001 From: Alibek Omarov Date: Sat, 11 Feb 2023 06:06:21 +0300 Subject: [PATCH 478/490] engine: client: consolidate modern and legacy protocol parsing functions, if possible --- engine/client/cl_parse.c | 289 ++++++++++----------------------------- engine/common/protocol.h | 3 + 2 files changed, 75 insertions(+), 217 deletions(-) diff --git a/engine/client/cl_parse.c b/engine/client/cl_parse.c index a2e4717f..0db7595b 100644 --- a/engine/client/cl_parse.c +++ b/engine/client/cl_parse.c @@ -843,15 +843,15 @@ void CL_ParseFileTransferFailed( sizebuf_t *msg ) CL_ParseServerData ================== */ -void CL_ParseServerData( sizebuf_t *msg ) +void CL_ParseServerData( sizebuf_t *msg, qboolean legacy ) { char gamefolder[MAX_QPATH]; qboolean background; int i; - Con_Reportf( "Serverdata packet received.\n" ); - cls.timestart = Sys_DoubleTime(); + Con_Reportf( "%s packet received.\n", legacy ? "Legacy serverdata" : "Serverdata" ); + cls.timestart = Sys_DoubleTime(); cls.demowaiting = false; // server is changed // wipe the client_t struct @@ -862,27 +862,46 @@ void CL_ParseServerData( sizebuf_t *msg ) // parse protocol version number i = MSG_ReadLong( msg ); - if( i != PROTOCOL_VERSION ) - Host_Error( "Server use invalid protocol (%i should be %i)\n", i, PROTOCOL_VERSION ); + if( legacy ) + { + if( i != PROTOCOL_LEGACY_VERSION ) + Host_Error( "Server use invalid protocol (%i should be %i)\n", i, PROTOCOL_LEGACY_VERSION ); + } + else + { + if( i != PROTOCOL_VERSION ) + Host_Error( "Server use invalid protocol (%i should be %i)\n", i, PROTOCOL_VERSION ); + } cl.servercount = MSG_ReadLong( msg ); cl.checksum = MSG_ReadLong( msg ); cl.playernum = MSG_ReadByte( msg ); cl.maxclients = MSG_ReadByte( msg ); clgame.maxEntities = MSG_ReadWord( msg ); - clgame.maxEntities = bound( MIN_EDICTS, clgame.maxEntities, MAX_EDICTS ); - clgame.maxModels = MSG_ReadWord( msg ); - Q_strncpy( clgame.mapname, MSG_ReadString( msg ), MAX_STRING ); - Q_strncpy( clgame.maptitle, MSG_ReadString( msg ), MAX_STRING ); + if( legacy ) + { + clgame.maxEntities = bound( MIN_LEGACY_EDICTS, clgame.maxEntities, MAX_LEGACY_EDICTS ); + clgame.maxModels = 512; // ??? + } + else + { + clgame.maxEntities = bound( MIN_EDICTS, clgame.maxEntities, MAX_EDICTS ); + clgame.maxModels = MSG_ReadWord( msg ); + } + Q_strncpy( clgame.mapname, MSG_ReadString( msg ), sizeof( clgame.mapname )); + Q_strncpy( clgame.maptitle, MSG_ReadString( msg ), sizeof( clgame.maptitle )); background = MSG_ReadOneBit( msg ); - Q_strncpy( gamefolder, MSG_ReadString( msg ), MAX_QPATH ); + Q_strncpy( gamefolder, MSG_ReadString( msg ), sizeof( gamefolder )); host.features = (uint)MSG_ReadLong( msg ); - // receive the player hulls - for( i = 0; i < MAX_MAP_HULLS * 3; i++ ) + if( !legacy ) { - host.player_mins[i/3][i%3] = MSG_ReadChar( msg ); - host.player_maxs[i/3][i%3] = MSG_ReadChar( msg ); + // receive the player hulls + for( i = 0; i < MAX_MAP_HULLS * 3; i++ ) + { + host.player_mins[i/3][i%3] = MSG_ReadChar( msg ); + host.player_maxs[i/3][i%3] = MSG_ReadChar( msg ); + } } if( clgame.maxModels > MAX_MODELS ) @@ -961,8 +980,11 @@ void CL_ParseServerData( sizebuf_t *msg ) COM_ClearCustomizationList( &cl.players[i].customdata, true ); CL_CreateCustomizationList(); - // request resources from server - CL_ServerCommand( true, "sendres %i\n", cl.servercount ); + if( !legacy ) + { + // request resources from server + CL_ServerCommand( true, "sendres %i\n", cl.servercount ); + } memset( &clgame.movevars, 0, sizeof( clgame.movevars )); memset( &clgame.oldmovevars, 0, sizeof( clgame.oldmovevars )); @@ -1137,7 +1159,7 @@ void CL_ParseClientData( sizebuf_t *msg ) CL_ParseBaseline ================== */ -void CL_ParseBaseline( sizebuf_t *msg ) +void CL_ParseBaseline( sizebuf_t *msg, qboolean legacy ) { int i, newnum; entity_state_t nullstate; @@ -1150,8 +1172,15 @@ void CL_ParseBaseline( sizebuf_t *msg ) while( 1 ) { - newnum = MSG_ReadUBitLong( msg, MAX_ENTITY_BITS ); - if( newnum == LAST_EDICT ) break; // end of baselines + if( legacy ) + { + newnum = MSG_ReadWord( msg ); + } + else + { + newnum = MSG_ReadUBitLong( msg, MAX_ENTITY_BITS ); + if( newnum == LAST_EDICT ) break; // end of baselines + } player = CL_IsPlayerIndex( newnum ); if( newnum >= clgame.maxEntities ) @@ -1162,14 +1191,22 @@ void CL_ParseBaseline( sizebuf_t *msg ) ent->index = newnum; MSG_ReadDeltaEntity( msg, &ent->prevstate, &ent->baseline, newnum, player, 1.0f ); + + if( legacy ) + { + break; // only one baseline allowed in legacy protocol + } } - cl.instanced_baseline_count = MSG_ReadUBitLong( msg, 6 ); - - for( i = 0; i < cl.instanced_baseline_count; i++ ) + if( !legacy ) { - newnum = MSG_ReadUBitLong( msg, MAX_ENTITY_BITS ); - MSG_ReadDeltaEntity( msg, &nullstate, &cl.instanced_baseline[i], newnum, false, 1.0f ); + cl.instanced_baseline_count = MSG_ReadUBitLong( msg, 6 ); + + for( i = 0; i < cl.instanced_baseline_count; i++ ) + { + newnum = MSG_ReadUBitLong( msg, MAX_ENTITY_BITS ); + MSG_ReadDeltaEntity( msg, &nullstate, &cl.instanced_baseline[i], newnum, false, 1.0f ); + } } } @@ -1317,7 +1354,7 @@ CL_UpdateUserinfo collect userinfo from all players ================ */ -void CL_UpdateUserinfo( sizebuf_t *msg ) +void CL_UpdateUserinfo( sizebuf_t *msg, qboolean legacy ) { int slot, id; qboolean active; @@ -1328,7 +1365,8 @@ void CL_UpdateUserinfo( sizebuf_t *msg ) if( slot >= MAX_CLIENTS ) Host_Error( "CL_ParseServerMessage: svc_updateuserinfo >= MAX_CLIENTS\n" ); - id = MSG_ReadLong( msg ); // unique user ID + if( !legacy ) + id = MSG_ReadLong( msg ); // unique user ID player = &cl.players[slot]; active = MSG_ReadOneBit( msg ) ? true : false; @@ -1340,7 +1378,8 @@ void CL_UpdateUserinfo( sizebuf_t *msg ) player->topcolor = Q_atoi( Info_ValueForKey( player->userinfo, "topcolor" )); player->bottomcolor = Q_atoi( Info_ValueForKey( player->userinfo, "bottomcolor" )); player->spectator = Q_atoi( Info_ValueForKey( player->userinfo, "*hltv" )); - MSG_ReadBytes( msg, player->hashedcdkey, sizeof( player->hashedcdkey )); + if( !legacy ) + MSG_ReadBytes( msg, player->hashedcdkey, sizeof( player->hashedcdkey )); if( slot == cl.playernum ) memcpy( &gameui.playerinfo, player, sizeof( player_info_t )); } @@ -2230,13 +2269,13 @@ void CL_ParseServerMessage( sizebuf_t *msg, qboolean normal_message ) break; case svc_serverdata: Cbuf_Execute(); // make sure any stuffed commands are done - CL_ParseServerData( msg ); + CL_ParseServerData( msg, false ); break; case svc_lightstyle: CL_ParseLightStyle( msg ); break; case svc_updateuserinfo: - CL_UpdateUserinfo( msg ); + CL_UpdateUserinfo( msg, false ); break; case svc_deltatable: Delta_ParseTableField( msg ); @@ -2266,7 +2305,7 @@ void CL_ParseServerMessage( sizebuf_t *msg, qboolean normal_message ) cl.frames[cl.parsecountmod].graphdata.event += MSG_GetNumBytesRead( msg ) - bufStart; break; case svc_spawnbaseline: - CL_ParseBaseline( msg ); + CL_ParseBaseline( msg, false ); break; case svc_temp_entity: CL_ParseTempEntity( msg ); @@ -2403,156 +2442,6 @@ void CL_ParseServerMessage( sizebuf_t *msg, qboolean normal_message ) } } -/* -================== -CL_ParseBaseline -================== -*/ -void CL_LegacyParseBaseline( sizebuf_t *msg ) -{ - int i, newnum; - qboolean player; - cl_entity_t *ent; - - Delta_InitClient (); // finalize client delta's - - newnum = MSG_ReadWord( msg ); - player = CL_IsPlayerIndex( newnum ); - - if( newnum >= clgame.maxEntities ) - Host_Error( "CL_AllocEdict: no free edicts\n" ); - - ent = CL_EDICT_NUM( newnum ); - memset( &ent->prevstate, 0, sizeof( ent->prevstate )); - ent->index = newnum; - - MSG_ReadDeltaEntity( msg, &ent->prevstate, &ent->baseline, newnum, player, 1.0f ); - -} - -/* -================== -CL_ParseServerData -================== -*/ -void CL_ParseLegacyServerData( sizebuf_t *msg ) -{ - string gamefolder; - qboolean background; - int i; - - Con_Reportf( "Legacy serverdata packet received.\n" ); - - cls.timestart = Sys_DoubleTime(); - - cls.demowaiting = false; // server is changed - //clgame.load_sequence++; // now all hud sprites are invalid - - // wipe the client_t struct - if( !cls.changelevel && !cls.changedemo ) - CL_ClearState (); - cls.state = ca_connected; - - // parse protocol version number - i = MSG_ReadLong( msg ); - //cls.serverProtocol = i; - - if( i != PROTOCOL_LEGACY_VERSION ) - Host_Error( "Server uses invalid protocol (%i should be %i)\n", i, PROTOCOL_LEGACY_VERSION ); - - cl.servercount = MSG_ReadLong( msg ); - cl.checksum = MSG_ReadLong( msg ); - cl.playernum = MSG_ReadByte( msg ); - cl.maxclients = MSG_ReadByte( msg ); - clgame.maxEntities = MSG_ReadWord( msg ); - clgame.maxEntities = bound( 30, clgame.maxEntities, 4096 ); - clgame.maxModels = 512; - Q_strncpy( clgame.mapname, MSG_ReadString( msg ), MAX_STRING ); - Q_strncpy( clgame.maptitle, MSG_ReadString( msg ), MAX_STRING ); - background = MSG_ReadOneBit( msg ); - Q_strncpy( gamefolder, MSG_ReadString( msg ), MAX_STRING ); - host.features = (uint)MSG_ReadLong( msg ); - - // Re-init hud video, especially if we changed game directories - clgame.dllFuncs.pfnVidInit(); - - if( Con_FixedFont( )) - { - // seperate the printfs so the server message can have a color - Con_Print( "\n\35\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\37\n" ); - Con_Print( va( "%c%s\n\n", 2, clgame.maptitle )); - } - - // multiplayer game? - if( cl.maxclients > 1 ) - { - // allow console in multiplayer games - host.allow_console = true; - - // loading user settings - CSCR_LoadDefaultCVars( "user.scr" ); - - if( r_decals->value > mp_decals.value ) - Cvar_SetValue( "r_decals", mp_decals.value ); - } - else Cvar_Reset( "r_decals" ); - - // set the background state - if( cls.demoplayback && ( cls.demonum != -1 )) - cl.background = true; - else cl.background = background; - - if( cl.background ) // tell the game parts about background state - Cvar_FullSet( "cl_background", "1", FCVAR_READ_ONLY ); - else Cvar_FullSet( "cl_background", "0", FCVAR_READ_ONLY ); - - if( !cls.changelevel ) - { - // continue playing if we are changing level - S_StopBackgroundTrack (); - } - - if( !cls.changedemo ) - UI_SetActiveMenu( cl.background ); - else if( !cls.demoplayback ) - Key_SetKeyDest( key_menu ); - - // don't reset cursor in background mode - if( cl.background ) - IN_MouseRestorePos(); - - // will be changed later - cl.viewentity = cl.playernum + 1; - gameui.globals->maxClients = cl.maxclients; - Q_strncpy( gameui.globals->maptitle, clgame.maptitle, sizeof( gameui.globals->maptitle )); - - if( !cls.changelevel && !cls.changedemo ) - CL_InitEdicts (); // re-arrange edicts - - // get splash name - if( cls.demoplayback && ( cls.demonum != -1 )) - Cvar_Set( "cl_levelshot_name", va( "levelshots/%s_%s", cls.demoname, refState.wideScreen ? "16x9" : "4x3" )); - else Cvar_Set( "cl_levelshot_name", va( "levelshots/%s_%s", clgame.mapname, refState.wideScreen ? "16x9" : "4x3" )); - Cvar_SetValue( "scr_loading", 0.0f ); // reset progress bar - - if(( cl_allow_levelshots->value && !cls.changelevel ) || cl.background ) - { - if( !FS_FileExists( va( "%s.bmp", cl_levelshot_name->string ), true )) - Cvar_Set( "cl_levelshot_name", "*black" ); // render a black screen - cls.scrshot_request = scrshot_plaque; // request levelshot even if exist (check filetime) - } - - for( i = 0; i < MAX_CLIENTS; i++ ) - COM_ClearCustomizationList( &cl.players[i].customdata, true ); - CL_CreateCustomizationList(); - - memset( &clgame.movevars, 0, sizeof( clgame.movevars )); - memset( &clgame.oldmovevars, 0, sizeof( clgame.oldmovevars )); - memset( &clgame.centerPrint, 0, sizeof( clgame.centerPrint )); - cl.video_prepped = false; - cl.audio_prepped = false; -} - /* ================== CL_ParseStaticEntity @@ -2628,8 +2517,6 @@ void CL_LegacyParseStaticEntity( sizebuf_t *msg ) R_AddEfrags( ent ); // add link } - - void CL_LegacyParseSoundPacket( sizebuf_t *msg, qboolean is_ambient ) { vec3_t pos; @@ -2761,36 +2648,6 @@ void CL_LegacyPrecacheEvent( sizebuf_t *msg ) CL_SetEventIndex( cl.event_precache[eventIndex], eventIndex ); } - -void CL_LegacyUpdateUserinfo( sizebuf_t *msg ) -{ - int slot, id = 0; - qboolean active; - player_info_t *player; - - slot = MSG_ReadUBitLong( msg, MAX_CLIENT_BITS ); - - if( slot >= MAX_CLIENTS ) - Host_Error( "CL_ParseServerMessage: svc_updateuserinfo >= MAX_CLIENTS\n" ); - - //id = MSG_ReadLong( msg ); // unique user ID - player = &cl.players[slot]; - active = MSG_ReadOneBit( msg ) ? true : false; - - if( active ) - { - Q_strncpy( player->userinfo, MSG_ReadString( msg ), sizeof( player->userinfo )); - Q_strncpy( player->name, Info_ValueForKey( player->userinfo, "name" ), sizeof( player->name )); - Q_strncpy( player->model, Info_ValueForKey( player->userinfo, "model" ), sizeof( player->model )); - player->topcolor = Q_atoi( Info_ValueForKey( player->userinfo, "topcolor" )); - player->bottomcolor = Q_atoi( Info_ValueForKey( player->userinfo, "bottomcolor" )); - player->spectator = Q_atoi( Info_ValueForKey( player->userinfo, "*hltv" )); - //MSG_ReadBytes( msg, player->hashedcdkey, sizeof( player->hashedcdkey )); - - if( slot == cl.playernum ) memcpy( &gameui.playerinfo, player, sizeof( player_info_t )); - } - else memset( player, 0, sizeof( *player )); -} #if XASH_LOW_MEMORY == 0 #define MAX_LEGACY_RESOURCES 2048 #elif XASH_LOW_MEMORY == 2 @@ -2994,13 +2851,13 @@ void CL_ParseLegacyServerMessage( sizebuf_t *msg, qboolean normal_message ) break; case svc_serverdata: Cbuf_Execute(); // make sure any stuffed commands are done - CL_ParseLegacyServerData( msg ); + CL_ParseServerData( msg, true ); break; case svc_lightstyle: CL_ParseLightStyle( msg ); break; case svc_updateuserinfo: - CL_LegacyUpdateUserinfo( msg ); + CL_UpdateUserinfo( msg, true ); break; case svc_deltatable: Delta_ParseTableField( msg ); @@ -3030,7 +2887,7 @@ void CL_ParseLegacyServerMessage( sizebuf_t *msg, qboolean normal_message ) cl.frames[cl.parsecountmod].graphdata.event += MSG_GetNumBytesRead( msg ) - bufStart; break; case svc_spawnbaseline: - CL_LegacyParseBaseline( msg ); + CL_ParseBaseline( msg, true ); break; case svc_temp_entity: CL_ParseTempEntity( msg ); @@ -3050,7 +2907,6 @@ void CL_ParseLegacyServerMessage( sizebuf_t *msg, qboolean normal_message ) break; case svc_legacy_modelindex: CL_LegacyPrecacheModel( msg ); - break; case svc_legacy_soundindex: CL_LegacyPrecacheSound( msg ); @@ -3066,7 +2922,6 @@ void CL_ParseLegacyServerMessage( sizebuf_t *msg, qboolean normal_message ) CL_ParseRestore( msg ); break; case svc_legacy_eventindex: - //CL_ParseFinaleCutscene( msg, 3 ); CL_LegacyPrecacheEvent(msg); break; case svc_weaponanim: diff --git a/engine/common/protocol.h b/engine/common/protocol.h index aba1fd5e..7d525677 100644 --- a/engine/common/protocol.h +++ b/engine/common/protocol.h @@ -302,6 +302,9 @@ extern const char *clc_strings[clc_lastmsg+1]; #define MAX_LEGACY_TOTAL_CMDS 16 // 28 - 16 = 12 real legacy max backup #define MAX_LEGACY_BACKUP_CMDS 12 +#define MAX_LEGACY_EDICTS (1 << MAX_LEGACY_ENTITY_BITS) // 4096 edicts +#define MIN_LEGACY_EDICTS 30 + // Master Server protocol #define MS_SCAN_REQUEST "1\xFF" "0.0.0.0:0\0" // TODO: implement IP filter From 13bf607031c552ee81f0e78b7faca27db835990b Mon Sep 17 00:00:00 2001 From: Alibek Omarov Date: Sat, 11 Feb 2023 06:43:34 +0300 Subject: [PATCH 479/490] engine: client: call VidInit early in svc_serverdata parsing, GoldSrc compatibility --- engine/client/cl_parse.c | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/engine/client/cl_parse.c b/engine/client/cl_parse.c index 0db7595b..ba69c4a0 100644 --- a/engine/client/cl_parse.c +++ b/engine/client/cl_parse.c @@ -857,6 +857,10 @@ void CL_ParseServerData( sizebuf_t *msg, qboolean legacy ) // wipe the client_t struct if( !cls.changelevel && !cls.changedemo ) CL_ClearState (); + + // Re-init hud video, especially if we changed game directories + clgame.dllFuncs.pfnVidInit(); + cls.state = ca_connected; // parse protocol version number @@ -907,9 +911,6 @@ void CL_ParseServerData( sizebuf_t *msg, qboolean legacy ) if( clgame.maxModels > MAX_MODELS ) Con_Printf( S_WARN "server model limit is above client model limit %i > %i\n", clgame.maxModels, MAX_MODELS ); - // Re-init hud video, especially if we changed game directories - clgame.dllFuncs.pfnVidInit(); - if( Con_FixedFont( )) { // seperate the printfs so the server message can have a color From f42a1744825241cca68af3ee9aa598964ad7f828 Mon Sep 17 00:00:00 2001 From: Alibek Omarov Date: Sat, 11 Feb 2023 06:45:20 +0300 Subject: [PATCH 480/490] engine: client: empty current map name in CL_ClearState --- engine/client/cl_main.c | 1 + 1 file changed, 1 insertion(+) diff --git a/engine/client/cl_main.c b/engine/client/cl_main.c index 8f6e439e..2aad47e1 100644 --- a/engine/client/cl_main.c +++ b/engine/client/cl_main.c @@ -1358,6 +1358,7 @@ void CL_ClearState( void ) MSG_Clear( &cls.netchan.message ); memset( &clgame.fade, 0, sizeof( clgame.fade )); memset( &clgame.shake, 0, sizeof( clgame.shake )); + clgame.mapname[0] = '\0'; Cvar_FullSet( "cl_background", "0", FCVAR_READ_ONLY ); cl.maxclients = 1; // allow to drawing player in menu cl.mtime[0] = cl.mtime[1] = 1.0f; // because level starts from 1.0f second From f4961d9da7dd1f344d8604a6260a3074ddb5f7ae Mon Sep 17 00:00:00 2001 From: Alibek Omarov Date: Sat, 11 Feb 2023 06:48:12 +0300 Subject: [PATCH 481/490] engine: client: return empty string in pfnGetLevelName if no map is loaded yet --- engine/client/cl_game.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/engine/client/cl_game.c b/engine/client/cl_game.c index 26b6c91d..09b82230 100644 --- a/engine/client/cl_game.c +++ b/engine/client/cl_game.c @@ -2582,7 +2582,10 @@ static const char *pfnGetLevelName( void ) { static char mapname[64]; - if( cls.state >= ca_connected ) + // a1ba: don't return maps/.bsp if no map is loaded yet + // in GoldSrc this is handled by cl.levelname field but we don't have it + // so emulate this behavior here + if( cls.state >= ca_connected && COM_CheckStringEmpty( clgame.mapname )) Q_snprintf( mapname, sizeof( mapname ), "maps/%s.bsp", clgame.mapname ); else mapname[0] = '\0'; // not in game From 0984368a3178dc580762b6c92732833367abd13e Mon Sep 17 00:00:00 2001 From: Alibek Omarov Date: Sat, 11 Feb 2023 07:22:04 +0300 Subject: [PATCH 482/490] engine: server: GoldSrc compliant pfnServerExecute(), don't execute config.cfg for server! --- engine/common/common.h | 1 - engine/common/cvar.c | 2 -- engine/server/server.h | 1 - engine/server/sv_game.c | 15 --------------- 4 files changed, 19 deletions(-) diff --git a/engine/common/common.h b/engine/common/common.h index b54b9b99..3d747d3c 100644 --- a/engine/common/common.h +++ b/engine/common/common.h @@ -365,7 +365,6 @@ typedef struct host_parm_s qboolean apply_game_config; // when true apply only to game cvars and ignore all other commands qboolean apply_opengl_config;// when true apply only to opengl cvars and ignore all other commands qboolean config_executed; // a bit who indicated was config.cfg already executed e.g. from valve.rc - int sv_cvars_restored; // count of restored server cvars qboolean crashed; // set to true if crashed qboolean daemonized; qboolean enabledll; diff --git a/engine/common/cvar.c b/engine/common/cvar.c index 569db02a..397a5808 100644 --- a/engine/common/cvar.c +++ b/engine/common/cvar.c @@ -1021,8 +1021,6 @@ qboolean Cvar_CommandWithPrivilegeCheck( convar_t *v, qboolean isPrivileged ) else { Cvar_DirectSet( v, Cmd_Argv( 1 )); - if( host.apply_game_config ) - host.sv_cvars_restored++; return true; } } diff --git a/engine/server/server.h b/engine/server/server.h index eee4e358..6d488aea 100644 --- a/engine/server/server.h +++ b/engine/server/server.h @@ -328,7 +328,6 @@ typedef struct qboolean msg_trace; // trace this message void *hInstance; // pointer to game.dll - qboolean config_executed; // should to execute config.cfg once time to restore FCVAR_ARCHIVE that specified in hl.dll edict_t *edicts; // solid array of server entities int numEntities; // actual entities count diff --git a/engine/server/sv_game.c b/engine/server/sv_game.c index c2d84e01..9473d79e 100644 --- a/engine/server/sv_game.c +++ b/engine/server/sv_game.c @@ -2455,21 +2455,6 @@ pfnServerExecute void GAME_EXPORT pfnServerExecute( void ) { Cbuf_Execute(); - - if( svgame.config_executed ) - return; - - // here we restore arhcived cvars only from game.dll - host.apply_game_config = true; - Cbuf_AddText( "exec config.cfg\n" ); - Cbuf_Execute(); - - if( host.sv_cvars_restored > 0 ) - Con_Reportf( "server executing ^2config.cfg^7 (%i cvars)\n", host.sv_cvars_restored ); - - host.apply_game_config = false; - svgame.config_executed = true; - host.sv_cvars_restored = 0; } /* From e95161aa14e42fd6181167d6a990b8bba42564cb Mon Sep 17 00:00:00 2001 From: Alibek Omarov Date: Sun, 12 Feb 2023 17:09:53 +0300 Subject: [PATCH 483/490] filesystem: fix ClearSearchPath --- filesystem/filesystem.c | 27 +++++++++++++++------------ 1 file changed, 15 insertions(+), 12 deletions(-) diff --git a/filesystem/filesystem.c b/filesystem/filesystem.c index 0fa54e4e..f8fc449c 100644 --- a/filesystem/filesystem.c +++ b/filesystem/filesystem.c @@ -373,24 +373,27 @@ FS_ClearSearchPath */ void FS_ClearSearchPath( void ) { - while( fs_searchpaths ) + searchpath_t *cur, **prev; + + prev = &fs_searchpaths; + + while( true ) { - searchpath_t *search = fs_searchpaths; + cur = *prev; - if( !search ) break; + if( !cur ) + break; - if( FBitSet( search->flags, FS_STATIC_PATH )) + // never delete static paths + if( FBitSet( cur->flags, FS_STATIC_PATH )) { - // skip read-only pathes - if( search->next ) - fs_searchpaths = search->next->next; - else break; + prev = &cur->next; + continue; } - else fs_searchpaths = search->next; - search->pfnClose( search ); - - Mem_Free( search ); + *prev = cur->next; + cur->pfnClose( cur ); + Mem_Free( cur ); } } From f4069de7f2b6a1946d427db4616d95fa208f1c62 Mon Sep 17 00:00:00 2001 From: Alibek Omarov Date: Mon, 13 Feb 2023 05:23:13 +0300 Subject: [PATCH 484/490] engine: move SlerpBones, CalcBonePosition/Quaternion from engine to libpublic --- engine/client/ref_common.c | 3 - engine/common/mod_local.h | 3 - engine/common/mod_studio.c | 190 ------------------------------------- engine/ref_api.h | 6 +- public/xash3d_mathlib.c | 190 +++++++++++++++++++++++++++++++++++++ public/xash3d_mathlib.h | 5 + ref/gl/gl_studio.c | 18 ++-- ref/soft/r_studio.c | 18 ++-- 8 files changed, 215 insertions(+), 218 deletions(-) diff --git a/engine/client/ref_common.c b/engine/client/ref_common.c index 71cbd87c..a736f51f 100644 --- a/engine/client/ref_common.c +++ b/engine/client/ref_common.c @@ -278,9 +278,6 @@ static ref_api_t gEngfuncs = Mod_PointInLeaf, Mod_CreatePolygonsForHull, - R_StudioSlerpBones, - R_StudioCalcBoneQuaternion, - R_StudioCalcBonePosition, R_StudioGetAnim, pfnStudioEvent, diff --git a/engine/common/mod_local.h b/engine/common/mod_local.h index 16c29531..3dcf50dc 100644 --- a/engine/common/mod_local.h +++ b/engine/common/mod_local.h @@ -182,9 +182,6 @@ qboolean Mod_GetStudioBounds( const char *name, vec3_t mins, vec3_t maxs ); void Mod_StudioGetAttachment( const edict_t *e, int iAttachment, float *org, float *ang ); void Mod_GetBonePosition( const edict_t *e, int iBone, float *org, float *ang ); hull_t *Mod_HullForStudio( model_t *m, float frame, int seq, vec3_t ang, vec3_t org, vec3_t size, byte *pcnt, byte *pbl, int *hitboxes, edict_t *ed ); -void R_StudioSlerpBones( int numbones, vec4_t q1[], float pos1[][3], vec4_t q2[], float pos2[][3], float s ); -void R_StudioCalcBoneQuaternion( int frame, float s, mstudiobone_t *pbone, mstudioanim_t *panim, float *adj, vec4_t q ); -void R_StudioCalcBonePosition( int frame, float s, mstudiobone_t *pbone, mstudioanim_t *panim, vec3_t adj, vec3_t pos ); void *R_StudioGetAnim( studiohdr_t *m_pStudioHeader, model_t *m_pSubModel, mstudioseqdesc_t *pseqdesc ); void Mod_StudioComputeBounds( void *buffer, vec3_t mins, vec3_t maxs, qboolean ignore_sequences ); int Mod_HitgroupForStudioHull( int index ); diff --git a/engine/common/mod_studio.c b/engine/common/mod_studio.c index 56f241ae..251c9a0c 100644 --- a/engine/common/mod_studio.c +++ b/engine/common/mod_studio.c @@ -397,196 +397,6 @@ static void Mod_StudioCalcRotations( int boneused[], int numbones, const byte *p if( pseqdesc->motiontype & STUDIO_Z ) pos[pseqdesc->motionbone][2] = 0.0f; } -/* -==================== -StudioCalcBoneQuaternion - -==================== -*/ -void R_StudioCalcBoneQuaternion( int frame, float s, mstudiobone_t *pbone, mstudioanim_t *panim, float *adj, vec4_t q ) -{ - vec3_t angles1; - vec3_t angles2; - int j, k; - - for( j = 0; j < 3; j++ ) - { - if( !panim || panim->offset[j+3] == 0 ) - { - angles2[j] = angles1[j] = pbone->value[j+3]; // default; - } - else - { - mstudioanimvalue_t *panimvalue = (mstudioanimvalue_t *)((byte *)panim + panim->offset[j+3]); - - k = frame; - - // debug - if( panimvalue->num.total < panimvalue->num.valid ) - k = 0; - - // find span of values that includes the frame we want - while( panimvalue->num.total <= k ) - { - k -= panimvalue->num.total; - panimvalue += panimvalue->num.valid + 1; - - // debug - if( panimvalue->num.total < panimvalue->num.valid ) - k = 0; - } - - // bah, missing blend! - if( panimvalue->num.valid > k ) - { - angles1[j] = panimvalue[k+1].value; - - if( panimvalue->num.valid > k + 1 ) - { - angles2[j] = panimvalue[k+2].value; - } - else - { - if( panimvalue->num.total > k + 1 ) - angles2[j] = angles1[j]; - else angles2[j] = panimvalue[panimvalue->num.valid+2].value; - } - } - else - { - angles1[j] = panimvalue[panimvalue->num.valid].value; - if( panimvalue->num.total > k + 1 ) - angles2[j] = angles1[j]; - else angles2[j] = panimvalue[panimvalue->num.valid+2].value; - } - - angles1[j] = pbone->value[j+3] + angles1[j] * pbone->scale[j+3]; - angles2[j] = pbone->value[j+3] + angles2[j] * pbone->scale[j+3]; - } - - if( pbone->bonecontroller[j+3] != -1 && adj != NULL ) - { - angles1[j] += adj[pbone->bonecontroller[j+3]]; - angles2[j] += adj[pbone->bonecontroller[j+3]]; - } - } - - if( !VectorCompare( angles1, angles2 )) - { - vec4_t q1, q2; - - AngleQuaternion( angles1, q1, true ); - AngleQuaternion( angles2, q2, true ); - QuaternionSlerp( q1, q2, s, q ); - } - else - { - AngleQuaternion( angles1, q, true ); - } -} - -/* -==================== -StudioCalcBonePosition - -==================== -*/ -void R_StudioCalcBonePosition( int frame, float s, mstudiobone_t *pbone, mstudioanim_t *panim, float *adj, vec3_t pos ) -{ - vec3_t origin1; - vec3_t origin2; - int j, k; - - for( j = 0; j < 3; j++ ) - { - if( !panim || panim->offset[j] == 0 ) - { - origin2[j] = origin1[j] = pbone->value[j]; // default; - } - else - { - mstudioanimvalue_t *panimvalue = (mstudioanimvalue_t *)((byte *)panim + panim->offset[j]); - - k = frame; - - // debug - if( panimvalue->num.total < panimvalue->num.valid ) - k = 0; - - // find span of values that includes the frame we want - while( panimvalue->num.total <= k ) - { - k -= panimvalue->num.total; - panimvalue += panimvalue->num.valid + 1; - - // debug - if( panimvalue->num.total < panimvalue->num.valid ) - k = 0; - } - - // bah, missing blend! - if( panimvalue->num.valid > k ) - { - origin1[j] = panimvalue[k+1].value; - - if( panimvalue->num.valid > k + 1 ) - { - origin2[j] = panimvalue[k+2].value; - } - else - { - if( panimvalue->num.total > k + 1 ) - origin2[j] = origin1[j]; - else origin2[j] = panimvalue[panimvalue->num.valid+2].value; - } - } - else - { - origin1[j] = panimvalue[panimvalue->num.valid].value; - if( panimvalue->num.total > k + 1 ) - origin2[j] = origin1[j]; - else origin2[j] = panimvalue[panimvalue->num.valid+2].value; - } - - origin1[j] = pbone->value[j] + origin1[j] * pbone->scale[j]; - origin2[j] = pbone->value[j] + origin2[j] * pbone->scale[j]; - } - - if( pbone->bonecontroller[j] != -1 && adj != NULL ) - { - origin1[j] += adj[pbone->bonecontroller[j]]; - origin2[j] += adj[pbone->bonecontroller[j]]; - } - } - - if( !VectorCompare( origin1, origin2 )) - { - VectorLerp( origin1, s, origin2, pos ); - } - else - { - VectorCopy( origin1, pos ); - } -} - -/* -==================== -StudioSlerpBones - -==================== -*/ -void R_StudioSlerpBones( int numbones, vec4_t q1[], float pos1[][3], vec4_t q2[], float pos2[][3], float s ) -{ - int i; - - s = bound( 0.0f, s, 1.0f ); - - for( i = 0; i < numbones; i++ ) - { - QuaternionSlerp( q1[i], q2[i], s, q1[i] ); - VectorLerp( pos1[i], s, pos2[i], pos1[i] ); - } -} /* ==================== diff --git a/engine/ref_api.h b/engine/ref_api.h index 3c14b719..1ac5adcb 100644 --- a/engine/ref_api.h +++ b/engine/ref_api.h @@ -32,7 +32,8 @@ GNU General Public License for more details. // RefAPI changelog: // 1. Initial release // 2. FS functions are removed, instead we have full fs_api_t -#define REF_API_VERSION 2 +// 3. SlerpBones, CalcBonePosition/Quaternion calls were moved to libpublic/mathlib +#define REF_API_VERSION 3 #define TF_SKY (TF_SKYSIDE|TF_NOMIPMAP) @@ -312,9 +313,6 @@ typedef struct ref_api_s void (*Mod_CreatePolygonsForHull)( int hullnum ); // studio models - void (*R_StudioSlerpBones)( int numbones, vec4_t q1[], float pos1[][3], vec4_t q2[], float pos2[][3], float s ); - void (*R_StudioCalcBoneQuaternion)( int frame, float s, mstudiobone_t *pbone, mstudioanim_t *panim, float *adj, vec4_t q ); - void (*R_StudioCalcBonePosition)( int frame, float s, mstudiobone_t *pbone, mstudioanim_t *panim, vec3_t adj, vec3_t pos ); void *(*R_StudioGetAnim)( studiohdr_t *m_pStudioHeader, model_t *m_pSubModel, mstudioseqdesc_t *pseqdesc ); void (*pfnStudioEvent)( const struct mstudioevent_s *event, const cl_entity_t *entity ); diff --git a/public/xash3d_mathlib.c b/public/xash3d_mathlib.c index 2fd8f705..39dc3089 100644 --- a/public/xash3d_mathlib.c +++ b/public/xash3d_mathlib.c @@ -872,3 +872,193 @@ int BoxOnPlaneSide( const vec3_t emins, const vec3_t emaxs, const mplane_t *p ) return sides; } +/* +==================== +StudioSlerpBones + +==================== +*/ +void R_StudioSlerpBones( int numbones, vec4_t q1[], float pos1[][3], const vec4_t q2[], const float pos2[][3], float s ) +{ + int i; + + s = bound( 0.0f, s, 1.0f ); + + for( i = 0; i < numbones; i++ ) + { + QuaternionSlerp( q1[i], q2[i], s, q1[i] ); + VectorLerp( pos1[i], s, pos2[i], pos1[i] ); + } +} + +/* +==================== +StudioCalcBoneQuaternion + +==================== +*/ +void R_StudioCalcBoneQuaternion( int frame, float s, const mstudiobone_t *pbone, const mstudioanim_t *panim, const float *adj, vec4_t q ) +{ + vec3_t angles1; + vec3_t angles2; + int j, k; + + for( j = 0; j < 3; j++ ) + { + if( !panim || panim->offset[j+3] == 0 ) + { + angles2[j] = angles1[j] = pbone->value[j+3]; // default; + } + else + { + mstudioanimvalue_t *panimvalue = (mstudioanimvalue_t *)((byte *)panim + panim->offset[j+3]); + + k = frame; + + // debug + if( panimvalue->num.total < panimvalue->num.valid ) + k = 0; + + // find span of values that includes the frame we want + while( panimvalue->num.total <= k ) + { + k -= panimvalue->num.total; + panimvalue += panimvalue->num.valid + 1; + + // debug + if( panimvalue->num.total < panimvalue->num.valid ) + k = 0; + } + + // bah, missing blend! + if( panimvalue->num.valid > k ) + { + angles1[j] = panimvalue[k+1].value; + + if( panimvalue->num.valid > k + 1 ) + { + angles2[j] = panimvalue[k+2].value; + } + else + { + if( panimvalue->num.total > k + 1 ) + angles2[j] = angles1[j]; + else angles2[j] = panimvalue[panimvalue->num.valid+2].value; + } + } + else + { + angles1[j] = panimvalue[panimvalue->num.valid].value; + if( panimvalue->num.total > k + 1 ) + angles2[j] = angles1[j]; + else angles2[j] = panimvalue[panimvalue->num.valid+2].value; + } + + angles1[j] = pbone->value[j+3] + angles1[j] * pbone->scale[j+3]; + angles2[j] = pbone->value[j+3] + angles2[j] * pbone->scale[j+3]; + } + + if( pbone->bonecontroller[j+3] != -1 && adj != NULL ) + { + angles1[j] += adj[pbone->bonecontroller[j+3]]; + angles2[j] += adj[pbone->bonecontroller[j+3]]; + } + } + + if( !VectorCompare( angles1, angles2 )) + { + vec4_t q1, q2; + + AngleQuaternion( angles1, q1, true ); + AngleQuaternion( angles2, q2, true ); + QuaternionSlerp( q1, q2, s, q ); + } + else + { + AngleQuaternion( angles1, q, true ); + } +} + +/* +==================== +StudioCalcBonePosition + +==================== +*/ +void R_StudioCalcBonePosition( int frame, float s, const mstudiobone_t *pbone, const mstudioanim_t *panim, const float *adj, vec3_t pos ) +{ + vec3_t origin1; + vec3_t origin2; + int j, k; + + for( j = 0; j < 3; j++ ) + { + if( !panim || panim->offset[j] == 0 ) + { + origin2[j] = origin1[j] = pbone->value[j]; // default; + } + else + { + mstudioanimvalue_t *panimvalue = (mstudioanimvalue_t *)((byte *)panim + panim->offset[j]); + + k = frame; + + // debug + if( panimvalue->num.total < panimvalue->num.valid ) + k = 0; + + // find span of values that includes the frame we want + while( panimvalue->num.total <= k ) + { + k -= panimvalue->num.total; + panimvalue += panimvalue->num.valid + 1; + + // debug + if( panimvalue->num.total < panimvalue->num.valid ) + k = 0; + } + + // bah, missing blend! + if( panimvalue->num.valid > k ) + { + origin1[j] = panimvalue[k+1].value; + + if( panimvalue->num.valid > k + 1 ) + { + origin2[j] = panimvalue[k+2].value; + } + else + { + if( panimvalue->num.total > k + 1 ) + origin2[j] = origin1[j]; + else origin2[j] = panimvalue[panimvalue->num.valid+2].value; + } + } + else + { + origin1[j] = panimvalue[panimvalue->num.valid].value; + if( panimvalue->num.total > k + 1 ) + origin2[j] = origin1[j]; + else origin2[j] = panimvalue[panimvalue->num.valid+2].value; + } + + origin1[j] = pbone->value[j] + origin1[j] * pbone->scale[j]; + origin2[j] = pbone->value[j] + origin2[j] * pbone->scale[j]; + } + + if( pbone->bonecontroller[j] != -1 && adj != NULL ) + { + origin1[j] += adj[pbone->bonecontroller[j]]; + origin2[j] += adj[pbone->bonecontroller[j]]; + } + } + + if( !VectorCompare( origin1, origin2 )) + { + VectorLerp( origin1, s, origin2, pos ); + } + else + { + VectorCopy( origin1, pos ); + } +} diff --git a/public/xash3d_mathlib.h b/public/xash3d_mathlib.h index 259c79fb..02c10c3e 100644 --- a/public/xash3d_mathlib.h +++ b/public/xash3d_mathlib.h @@ -23,6 +23,7 @@ GNU General Public License for more details. #include "build.h" #include "com_model.h" +#include "studio.h" // euler angle order #define PITCH 0 @@ -210,6 +211,10 @@ qboolean Matrix4x4_Invert_Full( matrix4x4 out, const matrix4x4 in1 ); float V_CalcFov( float *fov_x, float width, float height ); void V_AdjustFov( float *fov_x, float *fov_y, float width, float height, qboolean lock_x ); +void R_StudioSlerpBones( int numbones, vec4_t q1[], float pos1[][3], const vec4_t q2[], const float pos2[][3], float s ); +void R_StudioCalcBoneQuaternion( int frame, float s, const mstudiobone_t *pbone, const mstudioanim_t *panim, const float *adj, vec4_t q ); +void R_StudioCalcBonePosition( int frame, float s, const mstudiobone_t *pbone, const mstudioanim_t *panim, const vec3_t adj, vec3_t pos ); + int BoxOnPlaneSide( const vec3_t emins, const vec3_t emaxs, const mplane_t *p ); #define BOX_ON_PLANE_SIDE( emins, emaxs, p ) \ ((( p )->type < 3 ) ? \ diff --git a/ref/gl/gl_studio.c b/ref/gl/gl_studio.c index af117914..1c0aa6a4 100644 --- a/ref/gl/gl_studio.c +++ b/ref/gl/gl_studio.c @@ -847,8 +847,8 @@ void R_StudioCalcRotations( cl_entity_t *e, float pos[][3], vec4_t *q, mstudiose for( i = 0; i < m_pStudioHeader->numbones; i++, pbone++, panim++ ) { - gEngfuncs.R_StudioCalcBoneQuaternion( frame, s, pbone, panim, adj, q[i] ); - gEngfuncs.R_StudioCalcBonePosition( frame, s, pbone, panim, adj, pos[i] ); + R_StudioCalcBoneQuaternion( frame, s, pbone, panim, adj, q[i] ); + R_StudioCalcBonePosition( frame, s, pbone, panim, adj, pos[i] ); } if( pseqdesc->motiontype & STUDIO_X ) pos[pseqdesc->motionbone][0] = 0.0f; @@ -960,7 +960,7 @@ void R_StudioSetupBones( cl_entity_t *e ) dadt = R_StudioEstimateInterpolant( e ); s = (e->curstate.blending[0] * dadt + e->latched.prevblending[0] * (1.0f - dadt)) / 255.0f; - gEngfuncs.R_StudioSlerpBones( m_pStudioHeader->numbones, q, pos, q2, pos2, s ); + R_StudioSlerpBones( m_pStudioHeader->numbones, q, pos, q2, pos2, s ); if( pseqdesc->numblends == 4 ) { @@ -971,10 +971,10 @@ void R_StudioSetupBones( cl_entity_t *e ) R_StudioCalcRotations( e, pos4, q4, pseqdesc, panim, f ); s = (e->curstate.blending[0] * dadt + e->latched.prevblending[0] * (1.0f - dadt)) / 255.0f; - gEngfuncs.R_StudioSlerpBones( m_pStudioHeader->numbones, q3, pos3, q4, pos4, s ); + R_StudioSlerpBones( m_pStudioHeader->numbones, q3, pos3, q4, pos4, s ); s = (e->curstate.blending[1] * dadt + e->latched.prevblending[1] * (1.0f - dadt)) / 255.0f; - gEngfuncs.R_StudioSlerpBones( m_pStudioHeader->numbones, q, pos, q3, pos3, s ); + R_StudioSlerpBones( m_pStudioHeader->numbones, q, pos, q3, pos3, s ); } } @@ -997,7 +997,7 @@ void R_StudioSetupBones( cl_entity_t *e ) R_StudioCalcRotations( e, pos2, q2, pseqdesc, panim, e->latched.prevframe ); s = (e->latched.prevseqblending[0]) / 255.0f; - gEngfuncs.R_StudioSlerpBones( m_pStudioHeader->numbones, q1b, pos1b, q2, pos2, s ); + R_StudioSlerpBones( m_pStudioHeader->numbones, q1b, pos1b, q2, pos2, s ); if( pseqdesc->numblends == 4 ) { @@ -1008,15 +1008,15 @@ void R_StudioSetupBones( cl_entity_t *e ) R_StudioCalcRotations( e, pos4, q4, pseqdesc, panim, e->latched.prevframe ); s = (e->latched.prevseqblending[0]) / 255.0f; - gEngfuncs.R_StudioSlerpBones( m_pStudioHeader->numbones, q3, pos3, q4, pos4, s ); + R_StudioSlerpBones( m_pStudioHeader->numbones, q3, pos3, q4, pos4, s ); s = (e->latched.prevseqblending[1]) / 255.0f; - gEngfuncs.R_StudioSlerpBones( m_pStudioHeader->numbones, q1b, pos1b, q3, pos3, s ); + R_StudioSlerpBones( m_pStudioHeader->numbones, q1b, pos1b, q3, pos3, s ); } } s = 1.0f - ( g_studio.time - e->latched.sequencetime ) / 0.2f; - gEngfuncs.R_StudioSlerpBones( m_pStudioHeader->numbones, q, pos, q1b, pos1b, s ); + R_StudioSlerpBones( m_pStudioHeader->numbones, q, pos, q1b, pos1b, s ); } else { diff --git a/ref/soft/r_studio.c b/ref/soft/r_studio.c index a678715b..e3df3fa3 100644 --- a/ref/soft/r_studio.c +++ b/ref/soft/r_studio.c @@ -843,8 +843,8 @@ void R_StudioCalcRotations( cl_entity_t *e, float pos[][3], vec4_t *q, mstudiose for( i = 0; i < m_pStudioHeader->numbones; i++, pbone++, panim++ ) { - gEngfuncs.R_StudioCalcBoneQuaternion( frame, s, pbone, panim, adj, q[i] ); - gEngfuncs.R_StudioCalcBonePosition( frame, s, pbone, panim, adj, pos[i] ); + R_StudioCalcBoneQuaternion( frame, s, pbone, panim, adj, q[i] ); + R_StudioCalcBonePosition( frame, s, pbone, panim, adj, pos[i] ); } if( pseqdesc->motiontype & STUDIO_X ) pos[pseqdesc->motionbone][0] = 0.0f; @@ -956,7 +956,7 @@ void R_StudioSetupBones( cl_entity_t *e ) dadt = R_StudioEstimateInterpolant( e ); s = (e->curstate.blending[0] * dadt + e->latched.prevblending[0] * (1.0f - dadt)) / 255.0f; - gEngfuncs.R_StudioSlerpBones( m_pStudioHeader->numbones, q, pos, q2, pos2, s ); + R_StudioSlerpBones( m_pStudioHeader->numbones, q, pos, q2, pos2, s ); if( pseqdesc->numblends == 4 ) { @@ -967,10 +967,10 @@ void R_StudioSetupBones( cl_entity_t *e ) R_StudioCalcRotations( e, pos4, q4, pseqdesc, panim, f ); s = (e->curstate.blending[0] * dadt + e->latched.prevblending[0] * (1.0f - dadt)) / 255.0f; - gEngfuncs.R_StudioSlerpBones( m_pStudioHeader->numbones, q3, pos3, q4, pos4, s ); + R_StudioSlerpBones( m_pStudioHeader->numbones, q3, pos3, q4, pos4, s ); s = (e->curstate.blending[1] * dadt + e->latched.prevblending[1] * (1.0f - dadt)) / 255.0f; - gEngfuncs.R_StudioSlerpBones( m_pStudioHeader->numbones, q, pos, q3, pos3, s ); + R_StudioSlerpBones( m_pStudioHeader->numbones, q, pos, q3, pos3, s ); } } @@ -993,7 +993,7 @@ void R_StudioSetupBones( cl_entity_t *e ) R_StudioCalcRotations( e, pos2, q2, pseqdesc, panim, e->latched.prevframe ); s = (e->latched.prevseqblending[0]) / 255.0f; - gEngfuncs.R_StudioSlerpBones( m_pStudioHeader->numbones, q1b, pos1b, q2, pos2, s ); + R_StudioSlerpBones( m_pStudioHeader->numbones, q1b, pos1b, q2, pos2, s ); if( pseqdesc->numblends == 4 ) { @@ -1004,15 +1004,15 @@ void R_StudioSetupBones( cl_entity_t *e ) R_StudioCalcRotations( e, pos4, q4, pseqdesc, panim, e->latched.prevframe ); s = (e->latched.prevseqblending[0]) / 255.0f; - gEngfuncs.R_StudioSlerpBones( m_pStudioHeader->numbones, q3, pos3, q4, pos4, s ); + R_StudioSlerpBones( m_pStudioHeader->numbones, q3, pos3, q4, pos4, s ); s = (e->latched.prevseqblending[1]) / 255.0f; - gEngfuncs.R_StudioSlerpBones( m_pStudioHeader->numbones, q1b, pos1b, q3, pos3, s ); + R_StudioSlerpBones( m_pStudioHeader->numbones, q1b, pos1b, q3, pos3, s ); } } s = 1.0f - ( g_studio.time - e->latched.sequencetime ) / 0.2f; - gEngfuncs.R_StudioSlerpBones( m_pStudioHeader->numbones, q, pos, q1b, pos1b, s ); + R_StudioSlerpBones( m_pStudioHeader->numbones, q, pos, q1b, pos1b, s ); } else { From 1e8c26a527f8f8397d0ba3a665787bd7b9c3e5d6 Mon Sep 17 00:00:00 2001 From: Alibek Omarov Date: Mon, 13 Feb 2023 06:49:29 +0300 Subject: [PATCH 485/490] filesystem: wad: fix loading WADs by absolute paths --- filesystem/wad.c | 24 ++++++++++++++++++++---- 1 file changed, 20 insertions(+), 4 deletions(-) diff --git a/filesystem/wad.c b/filesystem/wad.c index 16153429..a456e49f 100644 --- a/filesystem/wad.c +++ b/filesystem/wad.c @@ -287,17 +287,33 @@ open the wad for reading & writing static wfile_t *W_Open( const char *filename, int *error ) { wfile_t *wad = (wfile_t *)Mem_Calloc( fs_mempool, sizeof( wfile_t )); - const char *basename; int i, lumpcount; dlumpinfo_t *srclumps; size_t lat_size; dwadinfo_t header; // NOTE: FS_Open is load wad file from the first pak in the list (while fs_ext_path is false) - if( fs_ext_path ) basename = filename; - else basename = COM_FileWithoutPath( filename ); + if( fs_ext_path ) + { + int ind; + searchpath_t *search = FS_FindFile( filename, &ind, NULL, 0, false ); - wad->handle = FS_Open( basename, "rb", false ); + // allow direct absolute paths + // TODO: catch them in FS_FindFile_DIR! + if( !search || ind < 0 ) + { + wad->handle = FS_SysOpen( filename, "rb" ); + } + else + { + wad->handle = search->pfnOpenFile( search, filename, "rb", ind ); + } + } + else + { + const char *basename = COM_FileWithoutPath( filename ); + wad->handle = FS_Open( basename, "rb", false ); + } if( wad->handle == NULL ) { From 3c27384b6bec2612d834fce50e23d7da7c07c8db Mon Sep 17 00:00:00 2001 From: Alibek Omarov Date: Mon, 13 Feb 2023 06:49:52 +0300 Subject: [PATCH 486/490] filesystem: speedup fs_ext_path case in FS_FindFile --- filesystem/filesystem.c | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/filesystem/filesystem.c b/filesystem/filesystem.c index f8fc449c..805bcf11 100644 --- a/filesystem/filesystem.c +++ b/filesystem/filesystem.c @@ -1709,17 +1709,21 @@ searchpath_t *FS_FindFile( const char *name, int *index, char *fixedname, size_t { static searchpath_t fs_directpath; - // clear old dir cache - if( fs_directpath.pfnClose ) - fs_directpath.pfnClose( &fs_directpath ); + // clear old dir cache, if needed + if( 0 != Q_strcmp( fs_directpath.filename, dirpath )) + { + if( fs_directpath.pfnClose ) + fs_directpath.pfnClose( &fs_directpath ); + FS_InitDirectorySearchpath( &fs_directpath, dirpath, 0 ); + } // just copy the name, we don't do case sensitivity fix there if( fixedname ) Q_strncpy( fixedname, name, len ); - FS_InitDirectorySearchpath( &fs_directpath, dirpath, 0 ); if( index != NULL ) *index = 0; + return &fs_directpath; } } From 9b0ac7cb322ea1edc9b5623b0d5e265705595a1e Mon Sep 17 00:00:00 2001 From: Alibek Omarov Date: Tue, 14 Feb 2023 01:45:20 +0300 Subject: [PATCH 487/490] common: add shared synctype_t definition header, borrowed from Quake's modelgen.h --- common/synctype.h | 24 ++++++++++++++++++++++++ 1 file changed, 24 insertions(+) create mode 100644 common/synctype.h diff --git a/common/synctype.h b/common/synctype.h new file mode 100644 index 00000000..fac3e6ca --- /dev/null +++ b/common/synctype.h @@ -0,0 +1,24 @@ +/* +synctype.h -- shared synctype_t definition +Copyright (C) 1996-1997 Id Software, Inc. +Copyright (C) 2023 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 2 +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. + +You should have received a copy of the GNU General Public License +along with this program; if not, write to the Free Software +Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +*/ +#ifndef SYNCTYPE_H +#define SYNCTYPE_H +typedef enum {ST_SYNC=0, ST_RAND } synctype_t; +#endif From 858597832d8b6dc7e2a5d3c208eb3010723fe4a0 Mon Sep 17 00:00:00 2001 From: Alibek Omarov Date: Tue, 14 Feb 2023 18:29:18 +0300 Subject: [PATCH 488/490] engine: alias: migrate header to stdint.h, remove usage of enums in data structs for portability, add static sizeof checks --- engine/alias.h | 72 ++++++++++++++++++++++++++++++-------------------- 1 file changed, 43 insertions(+), 29 deletions(-) diff --git a/engine/alias.h b/engine/alias.h index fda0ba0b..94f43414 100644 --- a/engine/alias.h +++ b/engine/alias.h @@ -16,6 +16,10 @@ #ifndef ALIAS_H #define ALIAS_H +#include "build.h" +#include STDINT_H +#include "synctype.h" + /* ============================================================================== @@ -39,16 +43,6 @@ Alias models are position independent, so the cache manager can move them. #define ALIAS_TRACER2 0x0040 // orange split trail + rotate #define ALIAS_TRACER3 0x0080 // purple trail -// must match definition in sprite.h -#ifndef SYNCTYPE_T -#define SYNCTYPE_T -typedef enum -{ - ST_SYNC = 0, - ST_RAND -} synctype_t; -#endif - typedef enum { ALIAS_SINGLE = 0, @@ -63,36 +57,42 @@ typedef enum typedef struct { - int ident; - int version; + int32_t ident; + int32_t version; vec3_t scale; vec3_t scale_origin; float boundingradius; vec3_t eyeposition; - int numskins; - int skinwidth; - int skinheight; - int numverts; - int numtris; - int numframes; - synctype_t synctype; - int flags; + int32_t numskins; + int32_t skinwidth; + int32_t skinheight; + int32_t numverts; + int32_t numtris; + int32_t numframes; + uint32_t synctype; // was synctype_t + int32_t flags; float size; } daliashdr_t; +STATIC_ASSERT( sizeof( daliashdr_t ) == 84, "invalid daliashdr_t size" ); + typedef struct { - int onseam; - int s; - int t; + int32_t onseam; + int32_t s; + int32_t t; } stvert_t; +STATIC_ASSERT( sizeof( stvert_t ) == 12, "invalid stvert_t size" ); + typedef struct dtriangle_s { - int facesfront; - int vertindex[3]; + int32_t facesfront; + int32_t vertindex[3]; } dtriangle_t; +STATIC_ASSERT( sizeof( dtriangle_t ) == 16, "invalid dtriangle_t size" ); + #define DT_FACES_FRONT 0x0010 #define ALIAS_ONSEAM 0x0020 @@ -103,36 +103,50 @@ typedef struct char name[16]; // frame name from grabbing } daliasframe_t; +STATIC_ASSERT( sizeof( daliasframe_t ) == 24, "invalid daliasframe_t size" ); + typedef struct { - int numframes; + int32_t numframes; trivertex_t bboxmin; // lightnormal isn't used trivertex_t bboxmax; // lightnormal isn't used } daliasgroup_t; +STATIC_ASSERT( sizeof( daliasgroup_t ) == 12, "invalid daliasgrou_t size" ); + typedef struct { - int numskins; + int32_t numskins; } daliasskingroup_t; +STATIC_ASSERT( sizeof( daliasskingroup_t ) == 4, "invalid daliasskingroup_t size" ); + typedef struct { float interval; } daliasinterval_t; +STATIC_ASSERT( sizeof( daliasinterval_t ) == 4, "invalid daliasinterval_t size" ); + typedef struct { float interval; } daliasskininterval_t; +STATIC_ASSERT( sizeof( daliasskininterval_t ) == 4, "invalid daliasskininterval_t size" ); + typedef struct { - aliasframetype_t type; + uint32_t type; // was aliasframetype_t } daliasframetype_t; +STATIC_ASSERT( sizeof( daliasframetype_t ) == 4, "invalid daliasframetype_t size" ); + typedef struct { - aliasskintype_t type; + uint32_t type; // was aliasskintype_t } daliasskintype_t; +STATIC_ASSERT( sizeof( daliasskintype_t ) == 4, "invalid daliasskintype_t size" ); + #endif//ALIAS_H From 0bff62e6969b8759d26543cc690894e665ebd781 Mon Sep 17 00:00:00 2001 From: Alibek Omarov Date: Tue, 14 Feb 2023 18:29:27 +0300 Subject: [PATCH 489/490] engine: sprite: migrate header to stdint.h, remove usage of enums in data structs for portability, add static sizeof checks --- engine/sprite.h | 78 +++++++++++++++++++++++++++---------------------- 1 file changed, 43 insertions(+), 35 deletions(-) diff --git a/engine/sprite.h b/engine/sprite.h index 1ff1d8b5..72cbf2f6 100644 --- a/engine/sprite.h +++ b/engine/sprite.h @@ -16,6 +16,10 @@ #ifndef SPRITE_H #define SPRITE_H +#include "build.h" +#include STDINT_H +#include "synctype.h" + /* ============================================================================== @@ -31,16 +35,6 @@ SPRITE MODELS #define SPRITE_VERSION_HL 2 // Half-Life sprites #define SPRITE_VERSION_32 32 // Captain Obvious mode on -// must match definition in alias.h -#ifndef SYNCTYPE_T -#define SYNCTYPE_T -typedef enum -{ - ST_SYNC = 0, - ST_RAND -} synctype_t; -#endif - typedef enum { FRAME_SINGLE = 0, @@ -74,55 +68,69 @@ typedef enum // generic helper typedef struct { - int ident; // LittleLong 'ISPR' - int version; // current version 2 + int32_t ident; // LittleLong 'ISPR' + int32_t version; // current version 2 } dsprite_t; +STATIC_ASSERT( sizeof( dsprite_t ) == 8, "invalid dsprite_t size" ); + typedef struct { - int ident; // LittleLong 'ISPR' - int version; // current version 2 - int type; // camera align + int32_t ident; // LittleLong 'ISPR' + int32_t version; // current version 2 + int32_t type; // camera align float boundingradius; // quick face culling - int bounds[2]; // mins\maxs - int numframes; // including groups + int32_t bounds[2]; // mins\maxs + int32_t numframes; // including groups float beamlength; // ??? - synctype_t synctype; // animation synctype + uint32_t synctype; // animation synctype, was synctype_t } dsprite_q1_t; +STATIC_ASSERT( sizeof( dsprite_q1_t ) == 36, "invalid dsprite_q1_t size" ); + typedef struct { - int ident; // LittleLong 'ISPR' - int version; // current version 2 - angletype_t type; // camera align - drawtype_t texFormat; // rendering mode - int boundingradius; // quick face culling - int bounds[2]; // mins\maxs - int numframes; // including groups - facetype_t facetype; // cullface (Xash3D ext) - synctype_t synctype; // animation synctype + int32_t ident; // LittleLong 'ISPR' + int32_t version; // current version 2 + uint32_t type; // camera align, was angletype_t + uint32_t texFormat; // rendering mode, was drawtype_t + int32_t boundingradius; // quick face culling + int32_t bounds[2]; // mins\maxs + int32_t numframes; // including groups + uint32_t facetype; // cullface (Xash3D ext), was facetype_t + uint32_t synctype; // animation synctype, was synctype_t } dsprite_hl_t; -typedef struct -{ - int origin[2]; - int width; - int height; -} dspriteframe_t; +STATIC_ASSERT( sizeof( dsprite_hl_t ) == 40, "invalid dsprite_hl_t size" ); typedef struct { - int numframes; + int32_t origin[2]; + int32_t width; + int32_t height; +} dspriteframe_t; + +STATIC_ASSERT( sizeof( dspriteframe_t ) == 16, "invalid dspriteframe_t size" ); + +typedef struct +{ + int32_t numframes; } dspritegroup_t; +STATIC_ASSERT( sizeof( dspritegroup_t ) == 4, "invalid dspritegroup_t size" ); + typedef struct { float interval; } dspriteinterval_t; +STATIC_ASSERT( sizeof( dspriteinterval_t ) == 4, "invalid dspriteinterval_t size" ); + typedef struct { - frametype_t type; + uint32_t type; // was frametype_t } dframetype_t; +STATIC_ASSERT( sizeof( dframetype_t ) == 4, "invalid dframetype_t size" ); + #endif//SPRITE_H From 21b9f07323c2792a63adbe9000875d5ab63ac471 Mon Sep 17 00:00:00 2001 From: Alibek Omarov Date: Tue, 14 Feb 2023 18:52:51 +0300 Subject: [PATCH 490/490] engine: platform: sdl: remove legacy XASH_NANOGL macro from vid code, it's only relevant for ref_gl --- engine/platform/sdl/vid_sdl.c | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/engine/platform/sdl/vid_sdl.c b/engine/platform/sdl/vid_sdl.c index 4fc6b72b..3383fe56 100644 --- a/engine/platform/sdl/vid_sdl.c +++ b/engine/platform/sdl/vid_sdl.c @@ -422,15 +422,11 @@ GL_GetProcAddress */ void *GL_GetProcAddress( const char *name ) { -#if defined( XASH_NANOGL ) - void *func = nanoGL_GetProcAddress( name ); -#else void *func = SDL_GL_GetProcAddress( name ); -#endif if( !func ) { - Con_Reportf( S_ERROR "Error: GL_GetProcAddress failed for %s\n", name ); + Con_Reportf( S_ERROR "GL_GetProcAddress failed for %s\n", name ); } return func;