From c532c3032cfafebe819ff58099be342add51582f Mon Sep 17 00:00:00 2001 From: Alibek Omarov Date: Thu, 9 Jun 2022 03:28:38 +0300 Subject: [PATCH 001/300] engine: menu_int: hack to compile on MotoMAGX --- engine/menu_int.h | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/engine/menu_int.h b/engine/menu_int.h index cdb918bf..f8b23614 100644 --- a/engine/menu_int.h +++ b/engine/menu_int.h @@ -53,6 +53,11 @@ typedef struct ui_globalvars_s struct ref_viewpass_s; +#if __GNUC__ == 3 +#undef NORETURN +#define NORETURN +#endif // GCC 3.x have problems with noreturn attribute on function pointer + typedef struct ui_enginefuncs_s { // image handlers From eeb170af2201dbaf8ea196917f39ad9ccf33ceaf Mon Sep 17 00:00:00 2001 From: Alibek Omarov Date: Thu, 9 Jun 2022 13:41:37 +0300 Subject: [PATCH 002/300] engine: client: set failed status for vgui_support if we wasn't able to load one. Unload library in case of error --- engine/client/vgui/vgui_draw.c | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/engine/client/vgui/vgui_draw.c b/engine/client/vgui/vgui_draw.c index 94ff455b..56f456f1 100644 --- a/engine/client/vgui/vgui_draw.c +++ b/engine/client/vgui/vgui_draw.c @@ -200,9 +200,14 @@ void VGui_Startup( const char *clientlib, int width, int height ) 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 { @@ -213,7 +218,11 @@ void VGui_Startup( const char *clientlib, int width, int height ) vgui.initialized = true; } else + { Con_Reportf( S_ERROR "Failed to find vgui_support library entry point!\n" ); + failed = true; + COM_FreeLibrary( s_pVGuiSupport ); + } } } } From 07a9c4602daa8527e8b52456ce3a2072004a8e6c Mon Sep 17 00:00:00 2001 From: Alibek Omarov Date: Thu, 9 Jun 2022 13:42:16 +0300 Subject: [PATCH 003/300] game_launch: change game with execve on supported platforms --- engine/common/launcher.c | 77 +++++++++++++++++++++++++--------------- game_launch/game.cpp | 69 ++++++++++++++++++++++------------- 2 files changed, 93 insertions(+), 53 deletions(-) diff --git a/engine/common/launcher.c b/engine/common/launcher.c index 7f85ff06..85e71d6a 100644 --- a/engine/common/launcher.c +++ b/engine/common/launcher.c @@ -25,63 +25,82 @@ GNU General Public License for more details. #include #endif -#if XASH_WIN32 -#define XASH_NOCONHOST 1 -#endif - static char szGameDir[128]; // safe place to keep gamedir static int g_iArgc; static char **g_pszArgv; +#if XASH_WIN32 || XASH_POSIX +#define USE_EXECVE_FOR_CHANGE_GAME 1 +#else +#define USE_EXECVE_FOR_CHANGE_GAME 0 +#endif + +#define E_GAME "XASH3D_GAME" // default env dir to start from +#define GAME_PATH "valve" // default dir to start from + void Launcher_ChangeGame( const char *progname ) { +#if USE_EXECVE_FOR_CHANGE_GAME + Host_Shutdown(); + +#if XASH_WIN32 + _putenv_s( E_GAME, progname ); + _execve( szArgv[0], szArgv, environ ); +#else + char envstr[256]; + snprintf( envstr, sizeof( envstr ), E_GAME "=%s", progname ); + putenv( envstr ); + execve( szArgv[0], szArgv, environ ); +#endif + +#else Q_strncpy( szGameDir, progname, sizeof( szGameDir ) - 1 ); Host_Shutdown( ); exit( Host_Main( g_iArgc, g_pszArgv, szGameDir, 1, &Launcher_ChangeGame ) ); +#endif } -#if XASH_NOCONHOST +#if XASH_WIN32 #include #include // CommandLineToArgvW int __stdcall WinMain(HINSTANCE hInst, HINSTANCE hPrevInst, LPSTR cmdLine, int nShow) { - int szArgc; - char **szArgv; - LPWSTR* lpArgv = CommandLineToArgvW(GetCommandLineW(), &szArgc); - int i = 0; + LPWSTR* lpArgv; + int ret, i; - szArgv = (char**)malloc(szArgc*sizeof(char*)); - for (; i < szArgc; ++i) + lpArgv = CommandLineToArgvW( GetCommandLineW(), &szArgc ); + szArgv = ( char** )malloc( (szArgc + 1) * sizeof( char* )); + + for( i = 0; i < szArgc; ++i ) { size_t size = wcslen(lpArgv[i]) + 1; - szArgv[i] = (char*)malloc(size); - wcstombs(szArgv[i], lpArgv[i], size); + + // just in case, allocate some more memory + szArgv[i] = ( char * )malloc( size * sizeof( wchar_t )); + wcstombs( szArgv[i], lpArgv[i], size ); } - szArgv[i] = NULL; + szArgv[szArgc] = 0; - LocalFree(lpArgv); + LocalFree( lpArgv ); - main( szArgc, szArgv ); + ret = main( szArgc, szArgv ); for( i = 0; i < szArgc; ++i ) free( szArgv[i] ); free( szArgv ); + + return ret; } -#endif +#endif // XASH_WIN32 + int main( int argc, char** argv ) { - char gamedir_buf[32] = ""; - const char *gamedir = getenv( "XASH3D_GAMEDIR" ); + const char *game = getenv( E_GAME ); - if( !COM_CheckString( gamedir ) ) - { - gamedir = "valve"; - } - else - { - Q_strncpy( gamedir_buf, gamedir, 32 ); - gamedir = gamedir_buf; - } + if( !game ) + game = GAME_PATH; + + Q_strncpy( szGameDir, gamedir, sizeof( szGameDir )); #if XASH_EMSCRIPTEN #ifdef EMSCRIPTEN_LIB_FS @@ -108,7 +127,7 @@ int main( int argc, char** argv ) IOS_LaunchDialog(); } #endif - return Host_Main( g_iArgc, g_pszArgv, gamedir, 0, &Launcher_ChangeGame ); + return Host_Main( g_iArgc, g_pszArgv, szGameDir, 0, &Launcher_ChangeGame ); } #endif diff --git a/game_launch/game.cpp b/game_launch/game.cpp index 7b637989..78d53b84 100644 --- a/game_launch/game.cpp +++ b/game_launch/game.cpp @@ -14,24 +14,25 @@ GNU General Public License for more details. */ #include "port.h" +#include "build.h" #include #include #include #include +#include -#if defined(__APPLE__) || defined(__unix__) || defined(__HAIKU__) - #define XASHLIB "libxash." OS_LIB_EXT -#elif _WIN32 - #if !__MINGW32__ && _MSC_VER >= 1200 - #define USE_WINMAIN - #endif - #define XASHLIB "xash.dll" - #define dlerror() GetStringLastError() - #include // CommandLineToArgvW +#if XASH_POSIX +#define XASHLIB "libxash." OS_LIB_EXT +#elif XASH_WIN32 +#define XASHLIB "xash.dll" +#define dlerror() GetStringLastError() +#include // CommandLineToArgvW +#else +#error // port me! #endif -#ifdef WIN32 +#ifdef XASH_WIN32 extern "C" { // Enable NVIDIA High Performance Graphics while using Integrated Graphics. @@ -42,6 +43,12 @@ __declspec(dllexport) int AmdPowerXpressRequestHighPerformance = 1; } #endif +#if XASH_WIN32 || XASH_POSIX +#define USE_EXECVE_FOR_CHANGE_GAME 1 +#else +#define USE_EXECVE_FOR_CHANGE_GAME 0 +#endif + #define E_GAME "XASH3D_GAME" // default env dir to start from #define GAME_PATH "valve" // default dir to start from @@ -54,7 +61,7 @@ static pfnShutdown Xash_Shutdown = NULL; static char szGameDir[128]; // safe place to keep gamedir static int szArgc; static char **szArgv; -static HINSTANCE hEngine; +static HINSTANCE hEngine; static void Xash_Error( const char *szFmt, ... ) { @@ -70,6 +77,7 @@ static void Xash_Error( const char *szFmt, ... ) #else fprintf( stderr, "Xash Error: %s\n", buffer ); #endif + exit( 1 ); } @@ -116,32 +124,44 @@ static void Sys_ChangeGame( const char *progname ) if( !progname || !progname[0] ) Xash_Error( "Sys_ChangeGame: NULL gamedir" ); +#if USE_EXECVE_FOR_CHANGE_GAME +#if XASH_WIN32 + _putenv_s( E_GAME, progname ); + + Sys_UnloadEngine(); + _execve( szArgv[0], szArgv, environ ); +#else + char envstr[256]; + snprintf( envstr, sizeof( envstr ), E_GAME "=%s", progname ); + putenv( envstr ); + + Sys_UnloadEngine(); + execve( szArgv[0], szArgv, environ ); +#endif +#else if( Xash_Shutdown == NULL ) Xash_Error( "Sys_ChangeGame: missed 'Host_Shutdown' export\n" ); + Sys_UnloadEngine(); strncpy( szGameDir, progname, sizeof( szGameDir ) - 1 ); - - Sys_UnloadEngine (); Sys_LoadEngine (); - Xash_Main( szArgc, szArgv, szGameDir, 1, Sys_ChangeGame ); +#endif } _inline int Sys_Start( void ) { int ret; pfnChangeGame changeGame = NULL; + const char *game = getenv( E_GAME ); + + if( !game ) + game = GAME_PATH; Sys_LoadEngine(); -#ifndef XASH_DISABLE_MENU_CHANGEGAME if( Xash_Shutdown ) changeGame = Sys_ChangeGame; -#endif - - const char *game = getenv( E_GAME ); - if( !game ) - game = GAME_PATH; ret = Xash_Main( szArgc, szArgv, game, 0, changeGame ); @@ -150,7 +170,7 @@ _inline int Sys_Start( void ) return ret; } -#ifndef USE_WINMAIN +#if !XASH_WIN32 int main( int argc, char **argv ) { szArgc = argc; @@ -159,7 +179,6 @@ int main( int argc, char **argv ) return Sys_Start(); } #else -//#pragma comment(lib, "shell32.lib") int __stdcall WinMain( HINSTANCE hInst, HINSTANCE hPrevInst, LPSTR cmdLine, int nShow ) { LPWSTR* lpArgv; @@ -170,8 +189,10 @@ int __stdcall WinMain( HINSTANCE hInst, HINSTANCE hPrevInst, LPSTR cmdLine, int for( i = 0; i < szArgc; ++i ) { - int size = wcslen(lpArgv[i]) + 1; - szArgv[i] = ( char* )malloc( size ); + size_t size = wcslen(lpArgv[i]) + 1; + + // just in case, allocate some more memory + szArgv[i] = ( char * )malloc( size * sizeof( wchar_t )); wcstombs( szArgv[i], lpArgv[i], size ); } szArgv[szArgc] = 0; From a717b7fc492c6bcbb0190eb0bd56443c1775b8b8 Mon Sep 17 00:00:00 2001 From: Alibek Omarov Date: Thu, 9 Jun 2022 14:44:04 +0300 Subject: [PATCH 004/300] game_launch: declare environ variable, by standard it must be declared by user program --- engine/common/launcher.c | 12 ++++++++---- game_launch/game.cpp | 3 ++- 2 files changed, 10 insertions(+), 5 deletions(-) diff --git a/engine/common/launcher.c b/engine/common/launcher.c index 85e71d6a..e3ed73a4 100644 --- a/engine/common/launcher.c +++ b/engine/common/launcher.c @@ -25,6 +25,9 @@ GNU General Public License for more details. #include #endif +#include + +extern char **environ; static char szGameDir[128]; // safe place to keep gamedir static int g_iArgc; static char **g_pszArgv; @@ -40,17 +43,18 @@ static char **g_pszArgv; void Launcher_ChangeGame( const char *progname ) { + char envstr[256]; + #if USE_EXECVE_FOR_CHANGE_GAME Host_Shutdown(); #if XASH_WIN32 _putenv_s( E_GAME, progname ); - _execve( szArgv[0], szArgv, environ ); + _execve( g_pszArgv[0], g_pszArgv, _environ ); #else - char envstr[256]; snprintf( envstr, sizeof( envstr ), E_GAME "=%s", progname ); putenv( envstr ); - execve( szArgv[0], szArgv, environ ); + execve( g_pszArgv[0], g_pszArgv, environ ); #endif #else @@ -100,7 +104,7 @@ int main( int argc, char** argv ) if( !game ) game = GAME_PATH; - Q_strncpy( szGameDir, gamedir, sizeof( szGameDir )); + Q_strncpy( szGameDir, game, sizeof( szGameDir )); #if XASH_EMSCRIPTEN #ifdef EMSCRIPTEN_LIB_FS diff --git a/game_launch/game.cpp b/game_launch/game.cpp index 78d53b84..1f50c956 100644 --- a/game_launch/game.cpp +++ b/game_launch/game.cpp @@ -56,6 +56,7 @@ typedef void (*pfnChangeGame)( const char *progname ); typedef int (*pfnInit)( int argc, char **argv, const char *progname, int bChangeGame, pfnChangeGame func ); typedef void (*pfnShutdown)( void ); +extern char **environ; static pfnInit Xash_Main; static pfnShutdown Xash_Shutdown = NULL; static char szGameDir[128]; // safe place to keep gamedir @@ -129,7 +130,7 @@ static void Sys_ChangeGame( const char *progname ) _putenv_s( E_GAME, progname ); Sys_UnloadEngine(); - _execve( szArgv[0], szArgv, environ ); + _execve( szArgv[0], szArgv, _environ ); #else char envstr[256]; snprintf( envstr, sizeof( envstr ), E_GAME "=%s", progname ); From f467d0c807c6756c73c90a179556cf7dce0ac244 Mon Sep 17 00:00:00 2001 From: Alibek Omarov Date: Thu, 9 Jun 2022 20:27:27 +0300 Subject: [PATCH 005/300] game_launch: fix Windows build (as suggested by @SNMetamorph) --- engine/common/launcher.c | 6 +++++- game_launch/game.cpp | 3 ++- 2 files changed, 7 insertions(+), 2 deletions(-) diff --git a/engine/common/launcher.c b/engine/common/launcher.c index e3ed73a4..2a47b597 100644 --- a/engine/common/launcher.c +++ b/engine/common/launcher.c @@ -25,7 +25,11 @@ GNU General Public License for more details. #include #endif -#include +#if XASH_WIN32 +#include // _execve +#else +#include // execve +#endif extern char **environ; static char szGameDir[128]; // safe place to keep gamedir diff --git a/game_launch/game.cpp b/game_launch/game.cpp index 1f50c956..e9ad83c9 100644 --- a/game_launch/game.cpp +++ b/game_launch/game.cpp @@ -20,14 +20,15 @@ GNU General Public License for more details. #include #include #include -#include #if XASH_POSIX #define XASHLIB "libxash." OS_LIB_EXT +#include // execve #elif XASH_WIN32 #define XASHLIB "xash.dll" #define dlerror() GetStringLastError() #include // CommandLineToArgvW +#include // _execve #else #error // port me! #endif From 4c7bf1ff441b7a7b316667ba966ca127c9646a20 Mon Sep 17 00:00:00 2001 From: Velaron Date: Thu, 9 Jun 2022 22:19:02 +0300 Subject: [PATCH 006/300] platform: win32: improve error reporting when loading DLLs and move custom DLL loader to a separate file --- engine/platform/win32/lib_custom_win.c | 478 ++++++++++++++++++ engine/platform/win32/lib_win.c | 655 +++++-------------------- engine/platform/win32/lib_win.h | 24 + 3 files changed, 637 insertions(+), 520 deletions(-) create mode 100644 engine/platform/win32/lib_custom_win.c create mode 100644 engine/platform/win32/lib_win.h diff --git a/engine/platform/win32/lib_custom_win.c b/engine/platform/win32/lib_custom_win.c new file mode 100644 index 00000000..f4dfe4aa --- /dev/null +++ b/engine/platform/win32/lib_custom_win.c @@ -0,0 +1,478 @@ +/* +lib_custom_win.c - win32 custom dlls loader +Copyright (C) 2008 Uncle Mike + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. +*/ +#include "common.h" + +#if XASH_LIB == LIB_WIN32 && XASH_X86 +#include "lib_win.h" + +#define NUMBER_OF_DIRECTORY_ENTRIES 16 +#ifndef IMAGE_SIZEOF_BASE_RELOCATION +#define IMAGE_SIZEOF_BASE_RELOCATION ( sizeof( IMAGE_BASE_RELOCATION )) +#endif + +typedef struct +{ + PIMAGE_NT_HEADERS headers; + byte *codeBase; + void **modules; + int numModules; + int initialized; +} MEMORYMODULE, *PMEMORYMODULE; + +// Protection flags for memory pages (Executable, Readable, Writeable) +static int ProtectionFlags[2][2][2] = +{ +{ +{ PAGE_NOACCESS, PAGE_WRITECOPY }, // not executable +{ PAGE_READONLY, PAGE_READWRITE }, +}, +{ +{ PAGE_EXECUTE, PAGE_EXECUTE_WRITECOPY }, // executable +{ PAGE_EXECUTE_READ, PAGE_EXECUTE_READWRITE }, +}, +}; + +typedef BOOL (WINAPI *DllEntryProc)( HINSTANCE hinstDLL, DWORD fdwReason, LPVOID lpReserved ); + +#define GET_HEADER_DICTIONARY( module, idx ) &(module)->headers->OptionalHeader.DataDirectory[idx] + +static void CopySections( const byte *data, PIMAGE_NT_HEADERS old_headers, PMEMORYMODULE module ) +{ + PIMAGE_SECTION_HEADER section = IMAGE_FIRST_SECTION( module->headers ); + byte *codeBase = module->codeBase; + int i, size; + byte *dest; + + for( i = 0; i < module->headers->FileHeader.NumberOfSections; i++, section++ ) + { + if( section->SizeOfRawData == 0 ) + { + // section doesn't contain data in the dll itself, but may define + // uninitialized data + size = old_headers->OptionalHeader.SectionAlignment; + + if( size > 0 ) + { + dest = (byte *)VirtualAlloc((byte *)CALCULATE_ADDRESS(codeBase, section->VirtualAddress), size, MEM_COMMIT, PAGE_READWRITE ); + section->Misc.PhysicalAddress = (DWORD)dest; + memset( dest, 0, size ); + } + // section is empty + continue; + } + + // commit memory block and copy data from dll + dest = (byte *)VirtualAlloc((byte *)CALCULATE_ADDRESS(codeBase, section->VirtualAddress), section->SizeOfRawData, MEM_COMMIT, PAGE_READWRITE ); + memcpy( dest, (byte *)CALCULATE_ADDRESS(data, section->PointerToRawData), section->SizeOfRawData ); + section->Misc.PhysicalAddress = (DWORD)dest; + } +} + +static void FreeSections( PIMAGE_NT_HEADERS old_headers, PMEMORYMODULE module ) +{ + PIMAGE_SECTION_HEADER section = IMAGE_FIRST_SECTION(module->headers); + byte *codeBase = module->codeBase; + int i, size; + + for( i = 0; i < module->headers->FileHeader.NumberOfSections; i++, section++ ) + { + if( section->SizeOfRawData == 0 ) + { + size = old_headers->OptionalHeader.SectionAlignment; + if( size > 0 ) + { + VirtualFree((byte *)CALCULATE_ADDRESS( codeBase, section->VirtualAddress ), size, MEM_DECOMMIT ); + section->Misc.PhysicalAddress = 0; + } + continue; + } + + VirtualFree((byte *)CALCULATE_ADDRESS( codeBase, section->VirtualAddress ), section->SizeOfRawData, MEM_DECOMMIT ); + section->Misc.PhysicalAddress = 0; + } +} + +static void FinalizeSections( MEMORYMODULE *module ) +{ + PIMAGE_SECTION_HEADER section = IMAGE_FIRST_SECTION( module->headers ); + int i; + + // loop through all sections and change access flags + for( i = 0; i < module->headers->FileHeader.NumberOfSections; i++, section++ ) + { + DWORD protect, oldProtect, size; + int executable = (section->Characteristics & IMAGE_SCN_MEM_EXECUTE) != 0; + int readable = (section->Characteristics & IMAGE_SCN_MEM_READ) != 0; + int writeable = (section->Characteristics & IMAGE_SCN_MEM_WRITE) != 0; + + if( section->Characteristics & IMAGE_SCN_MEM_DISCARDABLE ) + { + // section is not needed any more and can safely be freed + VirtualFree((LPVOID)section->Misc.PhysicalAddress, section->SizeOfRawData, MEM_DECOMMIT); + continue; + } + + // determine protection flags based on characteristics + protect = ProtectionFlags[executable][readable][writeable]; + if( section->Characteristics & IMAGE_SCN_MEM_NOT_CACHED ) + protect |= PAGE_NOCACHE; + + // determine size of region + size = section->SizeOfRawData; + + if( size == 0 ) + { + if( section->Characteristics & IMAGE_SCN_CNT_INITIALIZED_DATA ) + size = module->headers->OptionalHeader.SizeOfInitializedData; + else if( section->Characteristics & IMAGE_SCN_CNT_UNINITIALIZED_DATA ) + size = module->headers->OptionalHeader.SizeOfUninitializedData; + } + + if( size > 0 ) + { + // change memory access flags + if( !VirtualProtect((LPVOID)section->Misc.PhysicalAddress, size, protect, &oldProtect )) + Sys_Error( "error protecting memory page\n" ); + } + } +} + +static void PerformBaseRelocation( MEMORYMODULE *module, DWORD delta ) +{ + PIMAGE_DATA_DIRECTORY directory = GET_HEADER_DICTIONARY( module, IMAGE_DIRECTORY_ENTRY_BASERELOC ); + byte *codeBase = module->codeBase; + DWORD i; + + if( directory->Size > 0 ) + { + PIMAGE_BASE_RELOCATION relocation = (PIMAGE_BASE_RELOCATION)CALCULATE_ADDRESS( codeBase, directory->VirtualAddress ); + for( ; relocation->VirtualAddress > 0; ) + { + byte *dest = (byte *)CALCULATE_ADDRESS( codeBase, relocation->VirtualAddress ); + word *relInfo = (word *)((byte *)relocation + IMAGE_SIZEOF_BASE_RELOCATION ); + + for( i = 0; i<((relocation->SizeOfBlock-IMAGE_SIZEOF_BASE_RELOCATION) / 2); i++, relInfo++ ) + { + DWORD *patchAddrHL; + int type, offset; + + // the upper 4 bits define the type of relocation + type = *relInfo >> 12; + // the lower 12 bits define the offset + offset = *relInfo & 0xfff; + + switch( type ) + { + case IMAGE_REL_BASED_ABSOLUTE: + // skip relocation + break; + case IMAGE_REL_BASED_HIGHLOW: + // change complete 32 bit address + patchAddrHL = (DWORD *)CALCULATE_ADDRESS( dest, offset ); + *patchAddrHL += delta; + break; + default: + Con_Reportf( S_ERROR "PerformBaseRelocation: unknown relocation: %d\n", type ); + break; + } + } + + // advance to next relocation block + relocation = (PIMAGE_BASE_RELOCATION)CALCULATE_ADDRESS( relocation, relocation->SizeOfBlock ); + } + } +} + +FARPROC MemoryGetProcAddress( void *module, const char *name ) +{ + PIMAGE_DATA_DIRECTORY directory = GET_HEADER_DICTIONARY((MEMORYMODULE *)module, IMAGE_DIRECTORY_ENTRY_EXPORT ); + byte *codeBase = ((PMEMORYMODULE)module)->codeBase; + PIMAGE_EXPORT_DIRECTORY exports; + int idx = -1; + DWORD i, *nameRef; + WORD *ordinal; + + if( directory->Size == 0 ) + { + // no export table found + return NULL; + } + + exports = (PIMAGE_EXPORT_DIRECTORY)CALCULATE_ADDRESS( codeBase, directory->VirtualAddress ); + + if( exports->NumberOfNames == 0 || exports->NumberOfFunctions == 0 ) + { + // DLL doesn't export anything + return NULL; + } + + // search function name in list of exported names + nameRef = (DWORD *)CALCULATE_ADDRESS( codeBase, exports->AddressOfNames ); + ordinal = (WORD *)CALCULATE_ADDRESS( codeBase, exports->AddressOfNameOrdinals ); + + for( i = 0; i < exports->NumberOfNames; i++, nameRef++, ordinal++ ) + { + // GetProcAddress case insensative ????? + if( !Q_stricmp( name, (const char *)CALCULATE_ADDRESS( codeBase, *nameRef ))) + { + idx = *ordinal; + break; + } + } + + if( idx == -1 ) + { + // exported symbol not found + return NULL; + } + + if((DWORD)idx > exports->NumberOfFunctions ) + { + // name <-> ordinal number don't match + return NULL; + } + + // addressOfFunctions contains the RVAs to the "real" functions + return (FARPROC)CALCULATE_ADDRESS( codeBase, *(DWORD *)CALCULATE_ADDRESS( codeBase, exports->AddressOfFunctions + (idx * 4))); +} + +static int BuildImportTable( MEMORYMODULE *module ) +{ + PIMAGE_DATA_DIRECTORY directory = GET_HEADER_DICTIONARY( module, IMAGE_DIRECTORY_ENTRY_IMPORT ); + byte *codeBase = module->codeBase; + int result = 1; + + if( directory->Size > 0 ) + { + PIMAGE_IMPORT_DESCRIPTOR importDesc = (PIMAGE_IMPORT_DESCRIPTOR)CALCULATE_ADDRESS( codeBase, directory->VirtualAddress ); + + for( ; !IsBadReadPtr( importDesc, sizeof( IMAGE_IMPORT_DESCRIPTOR )) && importDesc->Name; importDesc++ ) + { + DWORD *thunkRef, *funcRef; + LPCSTR libname; + void *handle; + + libname = (LPCSTR)CALCULATE_ADDRESS( codeBase, importDesc->Name ); + handle = COM_LoadLibrary( libname, false, true ); + + if( handle == NULL ) + { + Con_Printf( S_ERROR "couldn't load library %s\n", libname ); + result = 0; + break; + } + + module->modules = (void *)Mem_Realloc( host.mempool, module->modules, (module->numModules + 1) * (sizeof( void* ))); + module->modules[module->numModules++] = handle; + + if( importDesc->OriginalFirstThunk ) + { + thunkRef = (DWORD *)CALCULATE_ADDRESS( codeBase, importDesc->OriginalFirstThunk ); + funcRef = (DWORD *)CALCULATE_ADDRESS( codeBase, importDesc->FirstThunk ); + } + else + { + // no hint table + thunkRef = (DWORD *)CALCULATE_ADDRESS( codeBase, importDesc->FirstThunk ); + funcRef = (DWORD *)CALCULATE_ADDRESS( codeBase, importDesc->FirstThunk ); + } + + for( ; *thunkRef; thunkRef++, funcRef++ ) + { + LPCSTR funcName; + + if( IMAGE_SNAP_BY_ORDINAL( *thunkRef )) + { + funcName = (LPCSTR)IMAGE_ORDINAL( *thunkRef ); + *funcRef = (DWORD)COM_GetProcAddress( handle, funcName ); + } + else + { + PIMAGE_IMPORT_BY_NAME thunkData = (PIMAGE_IMPORT_BY_NAME)CALCULATE_ADDRESS( codeBase, *thunkRef ); + funcName = (LPCSTR)&thunkData->Name; + *funcRef = (DWORD)COM_GetProcAddress( handle, funcName ); + } + + if( *funcRef == 0 ) + { + Con_Printf( S_ERROR "%s unable to find address: %s\n", libname, funcName ); + result = 0; + break; + } + } + if( !result ) break; + } + } + return result; +} + +void MemoryFreeLibrary( void *hInstance ) +{ + MEMORYMODULE *module = (MEMORYMODULE *)hInstance; + + if( module != NULL ) + { + int i; + + if( module->initialized != 0 ) + { + // notify library about detaching from process + DllEntryProc DllEntry = (DllEntryProc)CALCULATE_ADDRESS( module->codeBase, module->headers->OptionalHeader.AddressOfEntryPoint ); + (*DllEntry)((HINSTANCE)module->codeBase, DLL_PROCESS_DETACH, 0 ); + module->initialized = 0; + } + + if( module->modules != NULL ) + { + // free previously opened libraries + for( i = 0; i < module->numModules; i++ ) + { + if( module->modules[i] != NULL ) + COM_FreeLibrary( module->modules[i] ); + } + Mem_Free( module->modules ); // Mem_Realloc end + } + + FreeSections( module->headers, module ); + + if( module->codeBase != NULL ) + { + // release memory of library + VirtualFree( module->codeBase, 0, MEM_RELEASE ); + } + + HeapFree( GetProcessHeap(), 0, module ); + } +} + +void *MemoryLoadLibrary( const char *name ) +{ + MEMORYMODULE *result = NULL; + PIMAGE_DOS_HEADER dos_header; + PIMAGE_NT_HEADERS old_header; + byte *code, *headers; + DWORD locationDelta; + DllEntryProc DllEntry; + string errorstring; + qboolean successfull; + void *data = NULL; + + data = FS_LoadFile( name, NULL, false ); + + if( !data ) + { + Q_sprintf( errorstring, "couldn't load %s", name ); + goto library_error; + } + + dos_header = (PIMAGE_DOS_HEADER)data; + if( dos_header->e_magic != IMAGE_DOS_SIGNATURE ) + { + Q_sprintf( errorstring, "%s it's not a valid executable file", name ); + goto library_error; + } + + old_header = (PIMAGE_NT_HEADERS)&((const byte *)(data))[dos_header->e_lfanew]; + if( old_header->Signature != IMAGE_NT_SIGNATURE ) + { + Q_sprintf( errorstring, "%s missing PE header", name ); + goto library_error; + } + + // reserve memory for image of library + code = (byte *)VirtualAlloc((LPVOID)(old_header->OptionalHeader.ImageBase), old_header->OptionalHeader.SizeOfImage, MEM_RESERVE, PAGE_READWRITE ); + + if( code == NULL ) + { + // try to allocate memory at arbitrary position + code = (byte *)VirtualAlloc( NULL, old_header->OptionalHeader.SizeOfImage, MEM_RESERVE, PAGE_READWRITE ); + } + + if( code == NULL ) + { + Q_sprintf( errorstring, "%s can't reserve memory", name ); + goto library_error; + } + + result = (MEMORYMODULE *)HeapAlloc( GetProcessHeap(), 0, sizeof( MEMORYMODULE )); + result->codeBase = code; + result->numModules = 0; + result->modules = NULL; + result->initialized = 0; + + // XXX: is it correct to commit the complete memory region at once? + // calling DllEntry raises an exception if we don't... + VirtualAlloc( code, old_header->OptionalHeader.SizeOfImage, MEM_COMMIT, PAGE_READWRITE ); + + // commit memory for headers + headers = (byte *)VirtualAlloc( code, old_header->OptionalHeader.SizeOfHeaders, MEM_COMMIT, PAGE_READWRITE ); + + // copy PE header to code + memcpy( headers, dos_header, dos_header->e_lfanew + old_header->OptionalHeader.SizeOfHeaders ); + result->headers = (PIMAGE_NT_HEADERS)&((const byte *)(headers))[dos_header->e_lfanew]; + + // update position + result->headers->OptionalHeader.ImageBase = (DWORD)code; + + // copy sections from DLL file block to new memory location + CopySections( data, old_header, result ); + + // adjust base address of imported data + locationDelta = (DWORD)(code - old_header->OptionalHeader.ImageBase); + if( locationDelta != 0 ) PerformBaseRelocation( result, locationDelta ); + + // load required dlls and adjust function table of imports + if( !BuildImportTable( result )) + { + Q_sprintf( errorstring, "%s failed to build import table", name ); + goto library_error; + } + + // mark memory pages depending on section headers and release + // sections that are marked as "discardable" + FinalizeSections( result ); + + // get entry point of loaded library + if( result->headers->OptionalHeader.AddressOfEntryPoint != 0 ) + { + DllEntry = (DllEntryProc)CALCULATE_ADDRESS( code, result->headers->OptionalHeader.AddressOfEntryPoint ); + if( DllEntry == 0 ) + { + Q_sprintf( errorstring, "%s has no entry point", name ); + goto library_error; + } + + // notify library about attaching to process + successfull = (*DllEntry)((HINSTANCE)code, DLL_PROCESS_ATTACH, 0 ); + if( !successfull ) + { + Q_sprintf( errorstring, "can't attach library %s", name ); + goto library_error; + } + result->initialized = 1; + } + + Mem_Free( data ); // release memory + return (void *)result; +library_error: + // cleanup + if( data ) Mem_Free( data ); + MemoryFreeLibrary( result ); + Con_Printf( S_ERROR "LoadLibrary: %s\n", errorstring ); + + return NULL; +} + +#endif // XASH_LIB == LIB_WIN32 && XASH_X86 \ No newline at end of file diff --git a/engine/platform/win32/lib_win.c b/engine/platform/win32/lib_win.c index b5ee3e76..f5133b58 100644 --- a/engine/platform/win32/lib_win.c +++ b/engine/platform/win32/lib_win.c @@ -1,5 +1,5 @@ /* -library.c - custom dlls loader +lin_win.c - win32 dynamic library loading Copyright (C) 2008 Uncle Mike This program is free software: you can redistribute it and/or modify @@ -12,481 +12,11 @@ but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. */ -#include "platform/platform.h" -#if XASH_LIB == LIB_WIN32 + #include "common.h" -#include "library.h" -#include -#define CALCULATE_ADDRESS( base, offset ) ( ( DWORD )( base ) + ( DWORD )( offset ) ) - -#if XASH_X86 -/* ---------------------------------------------------------------- - - Custom dlls loader - ---------------------------------------------------------------- -*/ - -#define NUMBER_OF_DIRECTORY_ENTRIES 16 -#ifndef IMAGE_SIZEOF_BASE_RELOCATION -#define IMAGE_SIZEOF_BASE_RELOCATION ( sizeof( IMAGE_BASE_RELOCATION )) -#endif - -typedef struct -{ - PIMAGE_NT_HEADERS headers; - byte *codeBase; - void **modules; - int numModules; - int initialized; -} MEMORYMODULE, *PMEMORYMODULE; - -// Protection flags for memory pages (Executable, Readable, Writeable) -static int ProtectionFlags[2][2][2] = -{ -{ -{ PAGE_NOACCESS, PAGE_WRITECOPY }, // not executable -{ PAGE_READONLY, PAGE_READWRITE }, -}, -{ -{ PAGE_EXECUTE, PAGE_EXECUTE_WRITECOPY }, // executable -{ PAGE_EXECUTE_READ, PAGE_EXECUTE_READWRITE }, -}, -}; - -typedef BOOL (WINAPI *DllEntryProc)( HINSTANCE hinstDLL, DWORD fdwReason, LPVOID lpReserved ); - -#define GET_HEADER_DICTIONARY( module, idx ) &(module)->headers->OptionalHeader.DataDirectory[idx] - -static void CopySections( const byte *data, PIMAGE_NT_HEADERS old_headers, PMEMORYMODULE module ) -{ - PIMAGE_SECTION_HEADER section = IMAGE_FIRST_SECTION( module->headers ); - byte *codeBase = module->codeBase; - int i, size; - byte *dest; - - for( i = 0; i < module->headers->FileHeader.NumberOfSections; i++, section++ ) - { - if( section->SizeOfRawData == 0 ) - { - // section doesn't contain data in the dll itself, but may define - // uninitialized data - size = old_headers->OptionalHeader.SectionAlignment; - - if( size > 0 ) - { - dest = (byte *)VirtualAlloc((byte *)CALCULATE_ADDRESS(codeBase, section->VirtualAddress), size, MEM_COMMIT, PAGE_READWRITE ); - section->Misc.PhysicalAddress = (DWORD)dest; - memset( dest, 0, size ); - } - // section is empty - continue; - } - - // commit memory block and copy data from dll - dest = (byte *)VirtualAlloc((byte *)CALCULATE_ADDRESS(codeBase, section->VirtualAddress), section->SizeOfRawData, MEM_COMMIT, PAGE_READWRITE ); - memcpy( dest, (byte *)CALCULATE_ADDRESS(data, section->PointerToRawData), section->SizeOfRawData ); - section->Misc.PhysicalAddress = (DWORD)dest; - } -} - -static void FreeSections( PIMAGE_NT_HEADERS old_headers, PMEMORYMODULE module ) -{ - PIMAGE_SECTION_HEADER section = IMAGE_FIRST_SECTION(module->headers); - byte *codeBase = module->codeBase; - int i, size; - - for( i = 0; i < module->headers->FileHeader.NumberOfSections; i++, section++ ) - { - if( section->SizeOfRawData == 0 ) - { - size = old_headers->OptionalHeader.SectionAlignment; - if( size > 0 ) - { - VirtualFree((byte *)CALCULATE_ADDRESS( codeBase, section->VirtualAddress ), size, MEM_DECOMMIT ); - section->Misc.PhysicalAddress = 0; - } - continue; - } - - VirtualFree((byte *)CALCULATE_ADDRESS( codeBase, section->VirtualAddress ), section->SizeOfRawData, MEM_DECOMMIT ); - section->Misc.PhysicalAddress = 0; - } -} - -static void FinalizeSections( MEMORYMODULE *module ) -{ - PIMAGE_SECTION_HEADER section = IMAGE_FIRST_SECTION( module->headers ); - int i; - - // loop through all sections and change access flags - for( i = 0; i < module->headers->FileHeader.NumberOfSections; i++, section++ ) - { - DWORD protect, oldProtect, size; - int executable = (section->Characteristics & IMAGE_SCN_MEM_EXECUTE) != 0; - int readable = (section->Characteristics & IMAGE_SCN_MEM_READ) != 0; - int writeable = (section->Characteristics & IMAGE_SCN_MEM_WRITE) != 0; - - if( section->Characteristics & IMAGE_SCN_MEM_DISCARDABLE ) - { - // section is not needed any more and can safely be freed - VirtualFree((LPVOID)section->Misc.PhysicalAddress, section->SizeOfRawData, MEM_DECOMMIT); - continue; - } - - // determine protection flags based on characteristics - protect = ProtectionFlags[executable][readable][writeable]; - if( section->Characteristics & IMAGE_SCN_MEM_NOT_CACHED ) - protect |= PAGE_NOCACHE; - - // determine size of region - size = section->SizeOfRawData; - - if( size == 0 ) - { - if( section->Characteristics & IMAGE_SCN_CNT_INITIALIZED_DATA ) - size = module->headers->OptionalHeader.SizeOfInitializedData; - else if( section->Characteristics & IMAGE_SCN_CNT_UNINITIALIZED_DATA ) - size = module->headers->OptionalHeader.SizeOfUninitializedData; - } - - if( size > 0 ) - { - // change memory access flags - if( !VirtualProtect((LPVOID)section->Misc.PhysicalAddress, size, protect, &oldProtect )) - Sys_Error( "error protecting memory page\n" ); - } - } -} - -static void PerformBaseRelocation( MEMORYMODULE *module, DWORD delta ) -{ - PIMAGE_DATA_DIRECTORY directory = GET_HEADER_DICTIONARY( module, IMAGE_DIRECTORY_ENTRY_BASERELOC ); - byte *codeBase = module->codeBase; - DWORD i; - - if( directory->Size > 0 ) - { - PIMAGE_BASE_RELOCATION relocation = (PIMAGE_BASE_RELOCATION)CALCULATE_ADDRESS( codeBase, directory->VirtualAddress ); - for( ; relocation->VirtualAddress > 0; ) - { - byte *dest = (byte *)CALCULATE_ADDRESS( codeBase, relocation->VirtualAddress ); - word *relInfo = (word *)((byte *)relocation + IMAGE_SIZEOF_BASE_RELOCATION ); - - for( i = 0; i<((relocation->SizeOfBlock-IMAGE_SIZEOF_BASE_RELOCATION) / 2); i++, relInfo++ ) - { - DWORD *patchAddrHL; - int type, offset; - - // the upper 4 bits define the type of relocation - type = *relInfo >> 12; - // the lower 12 bits define the offset - offset = *relInfo & 0xfff; - - switch( type ) - { - case IMAGE_REL_BASED_ABSOLUTE: - // skip relocation - break; - case IMAGE_REL_BASED_HIGHLOW: - // change complete 32 bit address - patchAddrHL = (DWORD *)CALCULATE_ADDRESS( dest, offset ); - *patchAddrHL += delta; - break; - default: - Con_Reportf( S_ERROR "PerformBaseRelocation: unknown relocation: %d\n", type ); - break; - } - } - - // advance to next relocation block - relocation = (PIMAGE_BASE_RELOCATION)CALCULATE_ADDRESS( relocation, relocation->SizeOfBlock ); - } - } -} - -static FARPROC MemoryGetProcAddress( void *module, const char *name ) -{ - PIMAGE_DATA_DIRECTORY directory = GET_HEADER_DICTIONARY((MEMORYMODULE *)module, IMAGE_DIRECTORY_ENTRY_EXPORT ); - byte *codeBase = ((PMEMORYMODULE)module)->codeBase; - PIMAGE_EXPORT_DIRECTORY exports; - int idx = -1; - DWORD i, *nameRef; - WORD *ordinal; - - if( directory->Size == 0 ) - { - // no export table found - return NULL; - } - - exports = (PIMAGE_EXPORT_DIRECTORY)CALCULATE_ADDRESS( codeBase, directory->VirtualAddress ); - - if( exports->NumberOfNames == 0 || exports->NumberOfFunctions == 0 ) - { - // DLL doesn't export anything - return NULL; - } - - // search function name in list of exported names - nameRef = (DWORD *)CALCULATE_ADDRESS( codeBase, exports->AddressOfNames ); - ordinal = (WORD *)CALCULATE_ADDRESS( codeBase, exports->AddressOfNameOrdinals ); - - for( i = 0; i < exports->NumberOfNames; i++, nameRef++, ordinal++ ) - { - // GetProcAddress case insensative ????? - if( !Q_stricmp( name, (const char *)CALCULATE_ADDRESS( codeBase, *nameRef ))) - { - idx = *ordinal; - break; - } - } - - if( idx == -1 ) - { - // exported symbol not found - return NULL; - } - - if((DWORD)idx > exports->NumberOfFunctions ) - { - // name <-> ordinal number don't match - return NULL; - } - - // addressOfFunctions contains the RVAs to the "real" functions - return (FARPROC)CALCULATE_ADDRESS( codeBase, *(DWORD *)CALCULATE_ADDRESS( codeBase, exports->AddressOfFunctions + (idx * 4))); -} - -static int BuildImportTable( MEMORYMODULE *module ) -{ - PIMAGE_DATA_DIRECTORY directory = GET_HEADER_DICTIONARY( module, IMAGE_DIRECTORY_ENTRY_IMPORT ); - byte *codeBase = module->codeBase; - int result = 1; - - if( directory->Size > 0 ) - { - PIMAGE_IMPORT_DESCRIPTOR importDesc = (PIMAGE_IMPORT_DESCRIPTOR)CALCULATE_ADDRESS( codeBase, directory->VirtualAddress ); - - for( ; !IsBadReadPtr( importDesc, sizeof( IMAGE_IMPORT_DESCRIPTOR )) && importDesc->Name; importDesc++ ) - { - DWORD *thunkRef, *funcRef; - LPCSTR libname; - void *handle; - - libname = (LPCSTR)CALCULATE_ADDRESS( codeBase, importDesc->Name ); - handle = COM_LoadLibrary( libname, false, true ); - - if( handle == NULL ) - { - Con_Printf( S_ERROR "couldn't load library %s\n", libname ); - result = 0; - break; - } - - module->modules = (void *)Mem_Realloc( host.mempool, module->modules, (module->numModules + 1) * (sizeof( void* ))); - module->modules[module->numModules++] = handle; - - if( importDesc->OriginalFirstThunk ) - { - thunkRef = (DWORD *)CALCULATE_ADDRESS( codeBase, importDesc->OriginalFirstThunk ); - funcRef = (DWORD *)CALCULATE_ADDRESS( codeBase, importDesc->FirstThunk ); - } - else - { - // no hint table - thunkRef = (DWORD *)CALCULATE_ADDRESS( codeBase, importDesc->FirstThunk ); - funcRef = (DWORD *)CALCULATE_ADDRESS( codeBase, importDesc->FirstThunk ); - } - - for( ; *thunkRef; thunkRef++, funcRef++ ) - { - LPCSTR funcName; - - if( IMAGE_SNAP_BY_ORDINAL( *thunkRef )) - { - funcName = (LPCSTR)IMAGE_ORDINAL( *thunkRef ); - *funcRef = (DWORD)COM_GetProcAddress( handle, funcName ); - } - else - { - PIMAGE_IMPORT_BY_NAME thunkData = (PIMAGE_IMPORT_BY_NAME)CALCULATE_ADDRESS( codeBase, *thunkRef ); - funcName = (LPCSTR)&thunkData->Name; - *funcRef = (DWORD)COM_GetProcAddress( handle, funcName ); - } - - if( *funcRef == 0 ) - { - Con_Printf( S_ERROR "%s unable to find address: %s\n", libname, funcName ); - result = 0; - break; - } - } - if( !result ) break; - } - } - return result; -} - -static void MemoryFreeLibrary( void *hInstance ) -{ - MEMORYMODULE *module = (MEMORYMODULE *)hInstance; - - if( module != NULL ) - { - int i; - - if( module->initialized != 0 ) - { - // notify library about detaching from process - DllEntryProc DllEntry = (DllEntryProc)CALCULATE_ADDRESS( module->codeBase, module->headers->OptionalHeader.AddressOfEntryPoint ); - (*DllEntry)((HINSTANCE)module->codeBase, DLL_PROCESS_DETACH, 0 ); - module->initialized = 0; - } - - if( module->modules != NULL ) - { - // free previously opened libraries - for( i = 0; i < module->numModules; i++ ) - { - if( module->modules[i] != NULL ) - COM_FreeLibrary( module->modules[i] ); - } - Mem_Free( module->modules ); // Mem_Realloc end - } - - FreeSections( module->headers, module ); - - if( module->codeBase != NULL ) - { - // release memory of library - VirtualFree( module->codeBase, 0, MEM_RELEASE ); - } - - HeapFree( GetProcessHeap(), 0, module ); - } -} - -void *MemoryLoadLibrary( const char *name ) -{ - MEMORYMODULE *result = NULL; - PIMAGE_DOS_HEADER dos_header; - PIMAGE_NT_HEADERS old_header; - byte *code, *headers; - DWORD locationDelta; - DllEntryProc DllEntry; - string errorstring; - qboolean successfull; - void *data = NULL; - - data = FS_LoadFile( name, NULL, false ); - - if( !data ) - { - Q_sprintf( errorstring, "couldn't load %s", name ); - goto library_error; - } - - dos_header = (PIMAGE_DOS_HEADER)data; - if( dos_header->e_magic != IMAGE_DOS_SIGNATURE ) - { - Q_sprintf( errorstring, "%s it's not a valid executable file", name ); - goto library_error; - } - - old_header = (PIMAGE_NT_HEADERS)&((const byte *)(data))[dos_header->e_lfanew]; - if( old_header->Signature != IMAGE_NT_SIGNATURE ) - { - Q_sprintf( errorstring, "%s missing PE header", name ); - goto library_error; - } - - // reserve memory for image of library - code = (byte *)VirtualAlloc((LPVOID)(old_header->OptionalHeader.ImageBase), old_header->OptionalHeader.SizeOfImage, MEM_RESERVE, PAGE_READWRITE ); - - if( code == NULL ) - { - // try to allocate memory at arbitrary position - code = (byte *)VirtualAlloc( NULL, old_header->OptionalHeader.SizeOfImage, MEM_RESERVE, PAGE_READWRITE ); - } - - if( code == NULL ) - { - Q_sprintf( errorstring, "%s can't reserve memory", name ); - goto library_error; - } - - result = (MEMORYMODULE *)HeapAlloc( GetProcessHeap(), 0, sizeof( MEMORYMODULE )); - result->codeBase = code; - result->numModules = 0; - result->modules = NULL; - result->initialized = 0; - - // XXX: is it correct to commit the complete memory region at once? - // calling DllEntry raises an exception if we don't... - VirtualAlloc( code, old_header->OptionalHeader.SizeOfImage, MEM_COMMIT, PAGE_READWRITE ); - - // commit memory for headers - headers = (byte *)VirtualAlloc( code, old_header->OptionalHeader.SizeOfHeaders, MEM_COMMIT, PAGE_READWRITE ); - - // copy PE header to code - memcpy( headers, dos_header, dos_header->e_lfanew + old_header->OptionalHeader.SizeOfHeaders ); - result->headers = (PIMAGE_NT_HEADERS)&((const byte *)(headers))[dos_header->e_lfanew]; - - // update position - result->headers->OptionalHeader.ImageBase = (DWORD)code; - - // copy sections from DLL file block to new memory location - CopySections( data, old_header, result ); - - // adjust base address of imported data - locationDelta = (DWORD)(code - old_header->OptionalHeader.ImageBase); - if( locationDelta != 0 ) PerformBaseRelocation( result, locationDelta ); - - // load required dlls and adjust function table of imports - if( !BuildImportTable( result )) - { - Q_sprintf( errorstring, "%s failed to build import table", name ); - goto library_error; - } - - // mark memory pages depending on section headers and release - // sections that are marked as "discardable" - FinalizeSections( result ); - - // get entry point of loaded library - if( result->headers->OptionalHeader.AddressOfEntryPoint != 0 ) - { - DllEntry = (DllEntryProc)CALCULATE_ADDRESS( code, result->headers->OptionalHeader.AddressOfEntryPoint ); - if( DllEntry == 0 ) - { - Q_sprintf( errorstring, "%s has no entry point", name ); - goto library_error; - } - - // notify library about attaching to process - successfull = (*DllEntry)((HINSTANCE)code, DLL_PROCESS_ATTACH, 0 ); - if( !successfull ) - { - Q_sprintf( errorstring, "can't attach library %s", name ); - goto library_error; - } - result->initialized = 1; - } - - Mem_Free( data ); // release memory - return (void *)result; -library_error: - // cleanup - if( data ) Mem_Free( data ); - MemoryFreeLibrary( result ); - Con_Printf( S_ERROR "LoadLibrary: %s\n", errorstring ); - - return NULL; -} -#endif +#if XASH_LIB == LIB_WIN32 +#include "lib_win.h" static DWORD GetOffsetByRVA( DWORD rva, PIMAGE_NT_HEADERS nt_header ) { @@ -764,70 +294,146 @@ table_error: return false; } -qboolean COM_CheckLibraryDirectDependency( const char *name, const char *depname, qboolean directpath ) +static const char *GetLastErrorAsString( void ) { - PIMAGE_DOS_HEADER dosHeader; - PIMAGE_NT_HEADERS peHeader; - PIMAGE_DATA_DIRECTORY importDir; + DWORD errorcode; + static string errormessage; + + errorcode = GetLastError(); + if ( !errorcode ) return ""; + + FormatMessageA( FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS | FORMAT_MESSAGE_MAX_WIDTH_MASK, + NULL, errorcode, MAKELANGID( LANG_NEUTRAL, SUBLANG_DEFAULT ), + (LPSTR)&errormessage, sizeof( errormessage ), NULL ); + + return errormessage; +} + +static PIMAGE_IMPORT_DESCRIPTOR GetImportDescriptor( const char *name, byte *data, PIMAGE_NT_HEADERS *peheader ) +{ + PIMAGE_DOS_HEADER dosHeader; + PIMAGE_NT_HEADERS peHeader; + PIMAGE_DATA_DIRECTORY importDir; PIMAGE_IMPORT_DESCRIPTOR importDesc; - string errorstring = ""; - byte *data = NULL; - dll_user_t *hInst; - hInst = FS_FindLibrary( name, directpath ); - if( !hInst ) + if ( !data ) { - return false; // nothing to load + Con_Printf( S_ERROR "%s: couldn't load %s\n", __FUNCTION__, name ); + return NULL; } - data = FS_LoadFile( name, NULL, false ); - if( !data ) + dosHeader = (PIMAGE_DOS_HEADER)data; + if ( dosHeader->e_magic != IMAGE_DOS_SIGNATURE ) { - Q_snprintf( errorstring, sizeof( errorstring ), "couldn't load %s", name ); - goto libraryerror; + Con_Printf( S_ERROR "%s: %s is not a valid executable file\n", __FUNCTION__, name ); + return NULL; } - dosHeader = ( PIMAGE_DOS_HEADER )data; - if( dosHeader->e_magic != IMAGE_DOS_SIGNATURE ) + peHeader = (PIMAGE_NT_HEADERS)( data + dosHeader->e_lfanew ); + if ( peHeader->Signature != IMAGE_NT_SIGNATURE ) { - Q_snprintf( errorstring, sizeof( errorstring ), "%s it's not a valid executable file", name ); - goto libraryerror; - } - - peHeader = ( PIMAGE_NT_HEADERS )(data + dosHeader->e_lfanew); - if( peHeader->Signature != IMAGE_NT_SIGNATURE ) - { - Q_snprintf( errorstring, sizeof( errorstring ), "%s missing PE header", name ); - goto libraryerror; + Con_Printf( S_ERROR "%s: %s is missing a PE header\n", __FUNCTION__, name ); + return NULL; } importDir = &peHeader->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_IMPORT]; if( importDir->Size <= 0 ) { - Con_Printf( S_WARN "%s: %s has no dependencies. Is this library valid?\n", __FUNCTION__, name ); - goto libraryerror; + Con_Printf( S_ERROR "%s: %s has no dependencies\n", __FUNCTION__, name ); + return NULL; } - importDesc = (PIMAGE_IMPORT_DESCRIPTOR)CALCULATE_ADDRESS( data, GetOffsetByRVA(importDir->VirtualAddress, peHeader) ); - for( ; !IsBadReadPtr( importDesc, sizeof( IMAGE_IMPORT_DESCRIPTOR)) && importDesc->Name; importDesc++ ) + *peheader = peHeader; + importDesc = (PIMAGE_IMPORT_DESCRIPTOR)CALCULATE_ADDRESS( data, GetOffsetByRVA( importDir->VirtualAddress, peHeader ) ); + + return importDesc; +} + +static void ListMissingModules( dll_user_t *hInst ) +{ + DWORD cbNeeded; + PIMAGE_NT_HEADERS peHeader; + PIMAGE_IMPORT_DESCRIPTOR importDesc; + HANDLE hProcess; + byte *data; + + if ( !hInst ) return; + + hProcess = GetCurrentProcess(); + + data = FS_LoadFile( hInst->dllName, NULL, false ); + if ( !data ) + { + CloseHandle( hProcess ); + return; + } + + importDesc = GetImportDescriptor( hInst->dllName, data, &peHeader ); + if ( !importDesc ) + { + CloseHandle( hProcess ); + Mem_Free( data ); + return; + } + + for( ; !IsBadReadPtr( importDesc, sizeof( IMAGE_IMPORT_DESCRIPTOR ) ) && importDesc->Name; importDesc++ ) + { + HMODULE hMod; + const char *importName = (const char *)CALCULATE_ADDRESS( data, GetOffsetByRVA( importDesc->Name, peHeader ) ); + + hMod = LoadLibraryEx( importName, NULL, LOAD_LIBRARY_AS_DATAFILE ); + if ( !hMod ) + COM_PushLibraryError( va( "%s not found!", importName ) ); + else + FreeLibrary( hMod ); + } + + CloseHandle( hProcess ); + Mem_Free( data ); + return; +} + +qboolean COM_CheckLibraryDirectDependency( const char *name, const char *depname, qboolean directpath ) +{ + PIMAGE_NT_HEADERS peHeader; + PIMAGE_IMPORT_DESCRIPTOR importDesc; + byte *data; + dll_user_t *hInst; + qboolean ret = FALSE; + + hInst = FS_FindLibrary( name, directpath ); + if ( !hInst ) return FALSE; + + data = FS_LoadFile( name, NULL, false ); + if ( !data ) + { + COM_FreeLibrary( hInst ); + return FALSE; + } + + importDesc = GetImportDescriptor( name, data, &peHeader ); + if ( !importDesc ) + { + COM_FreeLibrary( hInst ); + Mem_Free( data ); + return FALSE; + } + + for( ; !IsBadReadPtr( importDesc, sizeof( IMAGE_IMPORT_DESCRIPTOR ) ) && importDesc->Name; importDesc++ ) { const char *importName = (const char *)CALCULATE_ADDRESS( data, GetOffsetByRVA( importDesc->Name, peHeader ) ); - Con_Reportf( "library %s has direct dependency %s\n", name, importName ); - if( !Q_stricmp( importName, depname ) ) + if ( !Q_stricmp( importName, depname ) ) { + COM_FreeLibrary( hInst ); Mem_Free( data ); - return true; + return TRUE; } } -libraryerror: - if( errorstring[0] ) - { - Con_Printf( S_ERROR "%s: %s\n", __FUNCTION__, errorstring ); - } - if( data ) Mem_Free( data ); // release memory - return false; + COM_FreeLibrary( hInst ); + Mem_Free( data ); + return FALSE; } /* @@ -841,12 +447,19 @@ void *COM_LoadLibrary( const char *dllname, int build_ordinals_table, qboolean d { dll_user_t *hInst; + COM_ResetLibraryError(); + hInst = FS_FindLibrary( dllname, directpath ); - if( !hInst ) return NULL; // nothing to load + if( !hInst ) + { + COM_PushLibraryError( va( "Failed to find library %s", dllname ) ); + return NULL; + } if( hInst->encrypted ) { - Con_Printf( S_ERROR "LoadLibrary: couldn't load encrypted library %s\n", dllname ); + COM_PushLibraryError( va( "Library %s is encrypted, cannot load", hInst->shortPath ) ); + COM_FreeLibrary( hInst ); return NULL; } @@ -863,7 +476,11 @@ void *COM_LoadLibrary( const char *dllname, int build_ordinals_table, qboolean d if( !hInst->hInstance ) { - Con_Reportf( "LoadLibrary: Loading %s - failed\n", dllname ); + COM_PushLibraryError( GetLastErrorAsString() ); + + if ( GetLastError() == ERROR_MOD_NOT_FOUND ) + ListMissingModules( hInst ); + COM_FreeLibrary( hInst ); return NULL; } @@ -873,14 +490,12 @@ void *COM_LoadLibrary( const char *dllname, int build_ordinals_table, qboolean d { if( !LibraryLoadSymbols( hInst )) { - Con_Reportf( "LoadLibrary: Loading %s - failed\n", dllname ); + COM_PushLibraryError( va( "Failed to load library %s", dllname ) ); COM_FreeLibrary( hInst ); return NULL; } } - Con_Reportf( "LoadLibrary: Loading %s - ok\n", dllname ); - return hInst; } @@ -944,7 +559,7 @@ void *COM_FunctionFromName( void *hInstance, const char *pName ) if( !Q_strcmp( pName, hInst->names[i] )) { index = hInst->ordinals[i]; - return hInst->funcs[index] + hInst->funcBase; + return (void *)( hInst->funcs[index] + hInst->funcBase ); } } diff --git a/engine/platform/win32/lib_win.h b/engine/platform/win32/lib_win.h new file mode 100644 index 00000000..429dd4f8 --- /dev/null +++ b/engine/platform/win32/lib_win.h @@ -0,0 +1,24 @@ +/* +lib_win.h - common win32 dll definitions +Copyright (C) 2022 Flying With Gauss + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. +*/ + +#include "library.h" +#include +#include + +#define CALCULATE_ADDRESS( base, offset ) ( ( DWORD )( base ) + ( DWORD )( offset ) ) + +FARPROC MemoryGetProcAddress( void *module, const char *name ); +void MemoryFreeLibrary( void *hInstance ); +void *MemoryLoadLibrary( const char *name ); \ No newline at end of file From c10b70cb6143a217d4247bc3d4fbfe803c96a7eb Mon Sep 17 00:00:00 2001 From: Velaron Date: Thu, 9 Jun 2022 22:19:56 +0300 Subject: [PATCH 007/300] game_launch: explicitly check for SDL2 on win32 platforms --- game_launch/game.cpp | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/game_launch/game.cpp b/game_launch/game.cpp index e9ad83c9..347f1973 100644 --- a/game_launch/game.cpp +++ b/game_launch/game.cpp @@ -26,6 +26,7 @@ GNU General Public License for more details. #include // execve #elif XASH_WIN32 #define XASHLIB "xash.dll" +#define SDL2LIB "SDL2.dll" #define dlerror() GetStringLastError() #include // CommandLineToArgvW #include // _execve @@ -98,6 +99,15 @@ static const char *GetStringLastError() static void Sys_LoadEngine( void ) { +#if XASH_WIN32 + HMODULE hSdl; + + if ( ( hSdl = LoadLibraryEx( SDL2LIB, NULL, LOAD_LIBRARY_AS_DATAFILE ) ) == NULL ) + Xash_Error("Unable to load the " SDL2LIB ": %s", dlerror() ); + else + FreeLibrary( hSdl ); +#endif + if(( hEngine = LoadLibrary( XASHLIB )) == NULL ) { Xash_Error("Unable to load the " XASHLIB ": %s", dlerror() ); From 341c9dae39bb1675a548b29f8f2f18e6c850aee1 Mon Sep 17 00:00:00 2001 From: Bohdan Shulyar Date: Thu, 9 Jun 2022 22:29:52 +0300 Subject: [PATCH 008/300] paltform: win32: fix typo >_< --- engine/platform/win32/lib_win.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/engine/platform/win32/lib_win.c b/engine/platform/win32/lib_win.c index f5133b58..997123f3 100644 --- a/engine/platform/win32/lib_win.c +++ b/engine/platform/win32/lib_win.c @@ -1,5 +1,5 @@ /* -lin_win.c - win32 dynamic library loading +lib_win.c - win32 dynamic library loading Copyright (C) 2008 Uncle Mike This program is free software: you can redistribute it and/or modify From 437630d26d4529c9bc937b864d51d00194eb470d Mon Sep 17 00:00:00 2001 From: Velaron Date: Fri, 10 Jun 2022 00:06:37 +0300 Subject: [PATCH 009/300] platform: win32: remove forgotten code --- 3rdparty/opus/opus | 1 + engine/platform/win32/lib_win.c | 12 +----------- 2 files changed, 2 insertions(+), 11 deletions(-) create mode 160000 3rdparty/opus/opus 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/engine/platform/win32/lib_win.c b/engine/platform/win32/lib_win.c index 997123f3..21ee1fab 100644 --- a/engine/platform/win32/lib_win.c +++ b/engine/platform/win32/lib_win.c @@ -351,27 +351,18 @@ static PIMAGE_IMPORT_DESCRIPTOR GetImportDescriptor( const char *name, byte *dat static void ListMissingModules( dll_user_t *hInst ) { - DWORD cbNeeded; PIMAGE_NT_HEADERS peHeader; PIMAGE_IMPORT_DESCRIPTOR importDesc; - HANDLE hProcess; byte *data; if ( !hInst ) return; - - hProcess = GetCurrentProcess(); data = FS_LoadFile( hInst->dllName, NULL, false ); - if ( !data ) - { - CloseHandle( hProcess ); - return; - } + if ( !data ) return; importDesc = GetImportDescriptor( hInst->dllName, data, &peHeader ); if ( !importDesc ) { - CloseHandle( hProcess ); Mem_Free( data ); return; } @@ -388,7 +379,6 @@ static void ListMissingModules( dll_user_t *hInst ) FreeLibrary( hMod ); } - CloseHandle( hProcess ); Mem_Free( data ); return; } From 89335938c1ebfe1aa3579b661b4145e68a811dc8 Mon Sep 17 00:00:00 2001 From: Velaron Date: Fri, 10 Jun 2022 09:56:44 +0300 Subject: [PATCH 010/300] remove accidentally added submodule --- 3rdparty/opus/opus | 1 - 1 file changed, 1 deletion(-) delete mode 160000 3rdparty/opus/opus diff --git a/3rdparty/opus/opus b/3rdparty/opus/opus deleted file mode 160000 index 16395923..00000000 --- a/3rdparty/opus/opus +++ /dev/null @@ -1 +0,0 @@ -Subproject commit 1639592368fc2dadc82d63f3be6f17ed0b554d71 From 6b4f55c4bfc6816a14d57a7d218ea02b1295dd6c Mon Sep 17 00:00:00 2001 From: Andrey Akhmichin <15944199+nekonomicon@users.noreply.github.com> Date: Fri, 10 Jun 2022 22:12:19 +0500 Subject: [PATCH 011/300] engine: common: filesystem.c: do not load archives with zip extention again --- engine/common/filesystem.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/engine/common/filesystem.c b/engine/common/filesystem.c index 0438b153..3b036b73 100644 --- a/engine/common/filesystem.c +++ b/engine/common/filesystem.c @@ -1210,7 +1210,7 @@ static qboolean FS_AddZip_Fullpath( const char *zipfile, qboolean *already_loade if( already_loaded ) *already_loaded = false; - if( !Q_stricmp( ext, "pk3" ) || !Q_stricmp( ext, "zip" )) + if( !Q_stricmp( ext, "pk3" ) ) zip = FS_LoadZip( zipfile, &errorcode ); if( zip ) @@ -1254,7 +1254,7 @@ static qboolean FS_AddArchive_Fullpath( const char *file, qboolean *already_load { const char *ext = COM_FileExtension( file ); - if( !Q_stricmp( ext, "zip" ) || !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 ); @@ -1297,7 +1297,7 @@ void FS_AddGameDirectory( const char *dir, uint flags ) // add any Zip package in the directory for( i = 0; i < list.numstrings; i++ ) { - if( !Q_stricmp( COM_FileExtension( list.strings[i] ), "zip" ) || !Q_stricmp( COM_FileExtension( list.strings[i] ), "pk3" )) + if( !Q_stricmp( COM_FileExtension( list.strings[i] ), "pk3" ) ) { Q_sprintf( fullpath, "%s%s", dir, list.strings[i] ); FS_AddZip_Fullpath( fullpath, NULL, flags ); From 7c9f5f8ab1478bda25156d9153c05008a8d36f80 Mon Sep 17 00:00:00 2001 From: Alibek Omarov Date: Sat, 11 Jun 2022 02:17:04 +0300 Subject: [PATCH 012/300] engine: remove LoadLibrary macros, to avoid possible misuse. Although macros moved to game_launch, it's part of it's own problem from now --- common/port.h | 6 ------ engine/common/system.c | 9 ++++++--- game_launch/game.cpp | 5 ++++- 3 files changed, 10 insertions(+), 10 deletions(-) diff --git a/common/port.h b/common/port.h index 435de115..ae60122f 100644 --- a/common/port.h +++ b/common/port.h @@ -47,14 +47,8 @@ GNU General Public License for more details. #define O_BINARY 0 #define O_TEXT 0 #define _mkdir( x ) mkdir( x, S_IRWXU | S_IRWXG | S_IROTH | S_IXOTH ) - #define LoadLibrary( x ) dlopen( x, RTLD_NOW ) - #define GetProcAddress( x, y ) dlsym( x, y ) - #define FreeLibrary( x ) dlclose( x ) #elif XASH_DOS4GW #define PATH_SPLITTER "\\" - #define LoadLibrary( x ) (0) - #define GetProcAddress( x, y ) (0) - #define FreeLibrary( x ) (0) #endif typedef void* HANDLE; diff --git a/engine/common/system.c b/engine/common/system.c index 6916fdd1..c41854e8 100644 --- a/engine/common/system.c +++ b/engine/common/system.c @@ -34,6 +34,8 @@ GNU General Public License for more details. #include "menu_int.h" // _UPDATE_PAGE macro +#include "library.h" + qboolean error_on_exit = false; // arg for exit(); #define DEBUG_BREAK @@ -272,7 +274,8 @@ qboolean Sys_LoadLibrary( dll_info_t *dll ) *func->func = NULL; } - if( !dll->link ) dll->link = LoadLibrary ( dll->name ); // environment pathes + if( !dll->link ) + dll->link = COM_LoadLibrary( dll->name, false, true ); // environment pathes // no DLL found if( !dll->link ) @@ -307,7 +310,7 @@ void* Sys_GetProcAddress( dll_info_t *dll, const char* name ) if( !dll || !dll->link ) // invalid desc return NULL; - return (void *)GetProcAddress( dll->link, name ); + return (void *)COM_GetProcAddress( dll->link, name ); } qboolean Sys_FreeLibrary( dll_info_t *dll ) @@ -324,7 +327,7 @@ qboolean Sys_FreeLibrary( dll_info_t *dll ) } else Con_Reportf( "Sys_FreeLibrary: Unloading %s\n", dll->name ); - FreeLibrary( dll->link ); + COM_FreeLibrary( dll->link ); dll->link = NULL; return true; diff --git a/game_launch/game.cpp b/game_launch/game.cpp index 347f1973..5ebac7e8 100644 --- a/game_launch/game.cpp +++ b/game_launch/game.cpp @@ -23,6 +23,9 @@ GNU General Public License for more details. #if XASH_POSIX #define XASHLIB "libxash." OS_LIB_EXT +#define LoadLibrary( x ) dlopen( x, RTLD_NOW ) +#define GetProcAddress( x, y ) dlsym( x, y ) +#define FreeLibrary( x ) dlclose( x ) #include // execve #elif XASH_WIN32 #define XASHLIB "xash.dll" @@ -46,7 +49,7 @@ __declspec(dllexport) int AmdPowerXpressRequestHighPerformance = 1; #endif #if XASH_WIN32 || XASH_POSIX -#define USE_EXECVE_FOR_CHANGE_GAME 1 +#define USE_EXECVE_FOR_CHANGE_GAME 0 #else #define USE_EXECVE_FOR_CHANGE_GAME 0 #endif From 6c7d57e1eb7c191fddad52588997396f150d3c4d Mon Sep 17 00:00:00 2001 From: Alibek Omarov Date: Sun, 12 Jun 2022 03:06:03 +0300 Subject: [PATCH 013/300] engine: client: move vgui deinitialization out of CL_UnloadProgs. Delete cls.initialized check in CL_Shutdown, that used to shutdown various client-side subsystems that usually have needed checks by themselves --- engine/client/cl_game.c | 1 - engine/client/cl_main.c | 8 ++++---- 2 files changed, 4 insertions(+), 5 deletions(-) diff --git a/engine/client/cl_game.c b/engine/client/cl_game.c index 2b64bec0..66a0fc76 100644 --- a/engine/client/cl_game.c +++ b/engine/client/cl_game.c @@ -3914,7 +3914,6 @@ void CL_UnloadProgs( void ) Cvar_FullSet( "host_clientloaded", "0", FCVAR_READ_ONLY ); COM_FreeLibrary( clgame.hInstance ); - VGui_Shutdown(); Mem_FreePool( &cls.mempool ); Mem_FreePool( &clgame.mempool ); memset( &clgame, 0, sizeof( clgame )); diff --git a/engine/client/cl_main.c b/engine/client/cl_main.c index c5807555..518f4225 100644 --- a/engine/client/cl_main.c +++ b/engine/client/cl_main.c @@ -3089,10 +3089,6 @@ CL_Shutdown */ void CL_Shutdown( void ) { - // already freed - if( !cls.initialized ) return; - cls.initialized = false; - Con_Printf( "CL_Shutdown()\n" ); if( !host.crashed ) @@ -3109,6 +3105,9 @@ void CL_Shutdown( void ) Mobile_Shutdown (); SCR_Shutdown (); CL_UnloadProgs (); + cls.initialized = false; + + VGui_Shutdown(); FS_Delete( "demoheader.tmp" ); // remove tmp file SCR_FreeCinematic (); // release AVI's *after* client.dll because custom renderer may use them @@ -3116,4 +3115,5 @@ void CL_Shutdown( void ) R_Shutdown (); Con_Shutdown (); + } From acffeb1dadea8dc0b75bf6fa557f8512f951b166 Mon Sep 17 00:00:00 2001 From: Alibek Omarov Date: Sun, 12 Jun 2022 03:06:31 +0300 Subject: [PATCH 014/300] wscript: enable gl4es for Android builds --- wscript | 1 + 1 file changed, 1 insertion(+) diff --git a/wscript b/wscript index 2254efa4..0b23397c 100644 --- a/wscript +++ b/wscript @@ -154,6 +154,7 @@ def configure(conf): conf.options.NO_VGUI= True # skip vgui conf.options.NANOGL = True conf.options.GLWES = True + conf.options.GL4ES = True conf.options.GL = False elif conf.env.MAGX: conf.options.USE_SELECT = True From 5524aaae1ec3c3b8c4c0eec155302c5d300ee23e Mon Sep 17 00:00:00 2001 From: Alibek Omarov Date: Sun, 12 Jun 2022 03:07:09 +0300 Subject: [PATCH 015/300] engine: server: fix server dll leak when no map was loaded --- engine/server/sv_main.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/engine/server/sv_main.c b/engine/server/sv_main.c index 44b28e4d..52f314c0 100644 --- a/engine/server/sv_main.c +++ b/engine/server/sv_main.c @@ -1091,6 +1091,8 @@ void SV_Shutdown( const char *finalmsg ) // drop the client if want to load a new map if( CL_IsPlaybackDemo( )) CL_Drop(); + + SV_UnloadProgs (); return; } From 8eec5389fe2a979be502351c2ff3e29442d38128 Mon Sep 17 00:00:00 2001 From: Alibek Omarov Date: Sun, 12 Jun 2022 03:07:37 +0300 Subject: [PATCH 016/300] game_launch: fix gamedir pointer being lost --- game_launch/game.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/game_launch/game.cpp b/game_launch/game.cpp index 5ebac7e8..c4da3a55 100644 --- a/game_launch/game.cpp +++ b/game_launch/game.cpp @@ -138,7 +138,6 @@ static void Sys_ChangeGame( const char *progname ) { if( !progname || !progname[0] ) Xash_Error( "Sys_ChangeGame: NULL gamedir" ); - #if USE_EXECVE_FOR_CHANGE_GAME #if XASH_WIN32 _putenv_s( E_GAME, progname ); @@ -157,8 +156,9 @@ static void Sys_ChangeGame( const char *progname ) if( Xash_Shutdown == NULL ) Xash_Error( "Sys_ChangeGame: missed 'Host_Shutdown' export\n" ); - Sys_UnloadEngine(); strncpy( szGameDir, progname, sizeof( szGameDir ) - 1 ); + + Sys_UnloadEngine(); Sys_LoadEngine (); Xash_Main( szArgc, szArgv, szGameDir, 1, Sys_ChangeGame ); #endif From b138a6350234c644c2a23658ec86d802092c684e Mon Sep 17 00:00:00 2001 From: Alibek Omarov Date: Sun, 12 Jun 2022 03:54:35 +0300 Subject: [PATCH 017/300] vgui_support: remove it from the engine --- .gitmodules | 3 - vgui-dev | 1 - vgui_support/Android.mk | 23 -- vgui_support/Makefile.linux | 29 --- vgui_support/cl.bat | 5 - vgui_support/vgui_clip.cpp | 124 ---------- vgui_support/vgui_input.cpp | 91 -------- vgui_support/vgui_int.cpp | 123 ---------- vgui_support/vgui_main.h | 156 ------------- vgui_support/vgui_surf.cpp | 440 ------------------------------------ vgui_support/wscript | 125 ---------- 11 files changed, 1120 deletions(-) delete mode 160000 vgui-dev delete mode 100644 vgui_support/Android.mk delete mode 100644 vgui_support/Makefile.linux delete mode 100644 vgui_support/cl.bat delete mode 100644 vgui_support/vgui_clip.cpp delete mode 100644 vgui_support/vgui_input.cpp delete mode 100644 vgui_support/vgui_int.cpp delete mode 100644 vgui_support/vgui_main.h delete mode 100644 vgui_support/vgui_surf.cpp delete mode 100644 vgui_support/wscript diff --git a/.gitmodules b/.gitmodules index f281fbd6..a3179edb 100644 --- a/.gitmodules +++ b/.gitmodules @@ -7,9 +7,6 @@ [submodule "ref_gl/gl-wes-v2"] path = ref_gl/gl-wes-v2 url = https://github.com/FWGS/gl-wes-v2 -[submodule "vgui-dev"] - path = vgui-dev - url = https://github.com/FWGS/vgui-dev [submodule "ref_gl/gl4es"] path = ref_gl/gl4es url = https://github.com/ptitSeb/gl4es diff --git a/vgui-dev b/vgui-dev deleted file mode 160000 index 93573075..00000000 --- a/vgui-dev +++ /dev/null @@ -1 +0,0 @@ -Subproject commit 93573075afe885618ea15831e72d44bdacd65bfb diff --git a/vgui_support/Android.mk b/vgui_support/Android.mk deleted file mode 100644 index 78c7db35..00000000 --- a/vgui_support/Android.mk +++ /dev/null @@ -1,23 +0,0 @@ -LOCAL_PATH := $(call my-dir) - -include $(CLEAR_VARS) - -LOCAL_MODULE = vgui_support - -include $(XASH3D_CONFIG) - -LOCAL_CFLAGS = -fsigned-char -DVGUI_TOUCH_SCROLL -DNO_STL -DXASH_GLES -DINTERNAL_VGUI_SUPPORT -Wall -Wextra -Wno-unused-parameter -Wno-unused-variable - -LOCAL_CPPFLAGS = -frtti -fno-exceptions -Wno-write-strings -Wno-invalid-offsetof -std=gnu++98 - -LOCAL_C_INCLUDES := \ - $(LOCAL_PATH)/../common \ - $(LOCAL_PATH)/../engine \ - $(HLSDK_PATH)/utils/vgui/include \ - $(VGUI_DIR)/include - -LOCAL_SRC_FILES := vgui_clip.cpp vgui_font.cpp vgui_input.cpp vgui_int.cpp vgui_surf.cpp - -LOCAL_STATIC_LIBRARIES := vgui - -include $(BUILD_STATIC_LIBRARY) diff --git a/vgui_support/Makefile.linux b/vgui_support/Makefile.linux deleted file mode 100644 index 8031d116..00000000 --- a/vgui_support/Makefile.linux +++ /dev/null @@ -1,29 +0,0 @@ -CC ?= gcc -m32 -CXX ?= g++ -m32 -std=gnu++98 -CFLAGS ?= -O2 -ggdb -fPIC -TOPDIR = $(PWD)/.. -VGUI_DIR ?= ./vgui-dev -INCLUDES = -I../common -I../engine -I../engine/common -I../engine/client -I../engine/client/vgui -I../pm_shared -INCLUDES += -I$(VGUI_DIR)/include/ -DEFINES = -DNO_STL -%.o : %.cpp - $(CXX) $(CFLAGS) $(INCLUDES) $(DEFINES) -c $< -o $@ - -SRCS = $(wildcard *.cpp) -OBJS = $(SRCS:.cpp=.o) - -libvgui_support.so : $(OBJS) - $(CXX) $(LDFLAGS) -o libvgui_support.so -shared $(OBJS) vgui.so - -.PHONY: depend clean - -clean : - $(RM) $(OBJS) libvgui_support.so - -depend: Makefile.dep - -Makefile.dep: $(SRCS) - rm -f ./Makefile.dep - $(CC) $(CFLAGS) $(INCLUDES) $(DEFINES) -MM $^>>./Makefile.dep - -include Makefile.dep diff --git a/vgui_support/cl.bat b/vgui_support/cl.bat deleted file mode 100644 index c0f59c0d..00000000 --- a/vgui_support/cl.bat +++ /dev/null @@ -1,5 +0,0 @@ - set MSVCDir=Z:\path\to\msvc - set INCLUDE=%MSVCDir%\include - set LIB=%MSVCDir%\lib - set PATH=%MSVCDir%\bin;%PATH% -cl.exe %* \ No newline at end of file diff --git a/vgui_support/vgui_clip.cpp b/vgui_support/vgui_clip.cpp deleted file mode 100644 index 67085577..00000000 --- a/vgui_support/vgui_clip.cpp +++ /dev/null @@ -1,124 +0,0 @@ -/* -vgui_clip.cpp - clip in 2D space -Copyright (C) 2011 Uncle Mike - -This program is free software: you can redistribute it and/or modify -it under the terms of the GNU General Public License as published by -the Free Software Foundation, either version 3 of the License, or -(at your option) any later version. - -This program is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU General Public License for more details. - -In addition, as a special exception, the author gives permission -to link the code of this program with VGUI library developed by -Valve, L.L.C ("Valve"). You must obey the GNU General Public License -in all respects for all of the code used other than VGUI library. -If you modify this file, you may extend this exception to your -version of the file, but you are not obligated to do so. If -you do not wish to do so, delete this exception statement -from your version. - -*/ - -#include "vgui_main.h" -#include "wrect.h" - -//----------------------------------------------------------------------------- -// For simulated scissor tests... -//----------------------------------------------------------------------------- -static wrect_t g_ScissorRect; -static qboolean g_bScissor = false; -namespace vgui_support { -//----------------------------------------------------------------------------- -// Enable/disable scissoring... -//----------------------------------------------------------------------------- -void EnableScissor( qboolean enable ) -{ - g_bScissor = enable; -} - -void SetScissorRect( int left, int top, int right, int bottom ) -{ - // Check for a valid rectangle... - assert( left <= right ); - assert( top <= bottom ); - - g_ScissorRect.left = left; - g_ScissorRect.top = top; - g_ScissorRect.right = right; - g_ScissorRect.bottom = bottom; -} - -//----------------------------------------------------------------------------- -// Purpose: Used for clipping, produces an interpolated texture coordinate -//----------------------------------------------------------------------------- -inline float InterpTCoord( float val, float mins, float maxs, float tMin, float tMax ) -{ - float flPercent; - - if( mins != maxs ) - flPercent = (float)(val - mins) / (maxs - mins); - else flPercent = 0.5f; - - return tMin + (tMax - tMin) * flPercent; -} - -//----------------------------------------------------------------------------- -// Purpose: Does a scissor clip of the input rectangle. -// Returns false if it is completely clipped off. -//----------------------------------------------------------------------------- -qboolean ClipRect( const vpoint_t &inUL, const vpoint_t &inLR, vpoint_t *pOutUL, vpoint_t *pOutLR ) -{ - if( g_bScissor ) - { - // pick whichever left side is larger - if( g_ScissorRect.left > inUL.point[0] ) - pOutUL->point[0] = g_ScissorRect.left; - else - pOutUL->point[0] = inUL.point[0]; - - // pick whichever right side is smaller - if( g_ScissorRect.right <= inLR.point[0] ) - pOutLR->point[0] = g_ScissorRect.right; - else - pOutLR->point[0] = inLR.point[0]; - - // pick whichever top side is larger - if( g_ScissorRect.top > inUL.point[1] ) - pOutUL->point[1] = g_ScissorRect.top; - else - pOutUL->point[1] = inUL.point[1]; - - // pick whichever bottom side is smaller - if( g_ScissorRect.bottom <= inLR.point[1] ) - pOutLR->point[1] = g_ScissorRect.bottom; - else - pOutLR->point[1] = inLR.point[1]; - - // Check for non-intersecting - if(( pOutUL->point[0] > pOutLR->point[0] ) || ( pOutUL->point[1] > pOutLR->point[1] )) - { - return false; - } - - pOutUL->coord[0] = InterpTCoord(pOutUL->point[0], - inUL.point[0], inLR.point[0], inUL.coord[0], inLR.coord[0] ); - pOutLR->coord[0] = InterpTCoord(pOutLR->point[0], - inUL.point[0], inLR.point[0], inUL.coord[0], inLR.coord[0] ); - - pOutUL->coord[1] = InterpTCoord(pOutUL->point[1], - inUL.point[1], inLR.point[1], inUL.coord[1], inLR.coord[1] ); - pOutLR->coord[1] = InterpTCoord(pOutLR->point[1], - inUL.point[1], inLR.point[1], inUL.coord[1], inLR.coord[1] ); - } - else - { - *pOutUL = inUL; - *pOutLR = inLR; - } - return true; -} -} diff --git a/vgui_support/vgui_input.cpp b/vgui_support/vgui_input.cpp deleted file mode 100644 index 0a6ee74b..00000000 --- a/vgui_support/vgui_input.cpp +++ /dev/null @@ -1,91 +0,0 @@ -/* -vgui_input.cpp - handle kb & mouse -Copyright (C) 2011 Uncle Mike - -This program is free software: you can redistribute it and/or modify -it under the terms of the GNU General Public License as published by -the Free Software Foundation, either version 3 of the License, or -(at your option) any later version. - -This program is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU General Public License for more details. - -In addition, as a special exception, the author gives permission -to link the code of this program with VGUI library developed by -Valve, L.L.C ("Valve"). You must obey the GNU General Public License -in all respects for all of the code used other than VGUI library. -If you modify this file, you may extend this exception to your -version of the file, but you are not obligated to do so. If -you do not wish to do so, delete this exception statement -from your version. - -*/ - -#define OEMRESOURCE // for OCR_* cursor junk - -#include "vgui_main.h" - -namespace vgui_support { -void VGUI_Key(VGUI_KeyAction action, VGUI_KeyCode code) -{ - App *pApp = App::getInstance(); - if(!surface) - return; - switch( action ) - { - case KA_PRESSED: - pApp->internalKeyPressed( (KeyCode) code, surface ); - break; - case KA_RELEASED: - pApp->internalKeyReleased( (KeyCode) code, surface ); - break; - case KA_TYPED: - pApp->internalKeyTyped( (KeyCode) code, surface ); - break; - } - //fprintf(stdout,"vgui_support: VGUI key action %d %d\n", action, code); - //fflush(stdout); -} - -void VGUI_Mouse(VGUI_MouseAction action, int code) -{ - App *pApp = App::getInstance(); - if(!surface) - return; - switch( action ) - { - case MA_PRESSED: - pApp->internalMousePressed( (MouseCode) code, surface ); - break; - case MA_RELEASED: - pApp->internalMouseReleased( (MouseCode) code, surface ); - break; - case MA_DOUBLE: - pApp->internalMouseDoublePressed( (MouseCode) code, surface ); - break; - case MA_WHEEL: - //fprintf(stdout, "vgui_support: VGUI mouse wheeled %d %d\n", action, code); - pApp->internalMouseWheeled( code, surface ); - break; - } - //fprintf(stdout, "vgui_support: VGUI mouse action %d %d\n", action, code); - //fflush(stdout); -} - -void VGUI_MouseMove(int x, int y) -{ - App *pApp = App::getInstance(); - //fprintf(stdout, "vgui_support: VGUI mouse move %d %d %p\n", x, y, surface); - //fflush(stdout); - if(!surface) - return; - pApp->internalCursorMoved( x, y, surface ); -} - -void VGUI_TextInput(const char *text) -{ - // stub -} -} diff --git a/vgui_support/vgui_int.cpp b/vgui_support/vgui_int.cpp deleted file mode 100644 index 1506767a..00000000 --- a/vgui_support/vgui_int.cpp +++ /dev/null @@ -1,123 +0,0 @@ -/* -vgui_int.c - vgui dll interaction -Copyright (C) 2011 Uncle Mike - -This program is free software: you can redistribute it and/or modify -it under the terms of the GNU General Public License as published by -the Free Software Foundation, either version 3 of the License, or -(at your option) any later version. - -This program is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU General Public License for more details. - -In addition, as a special exception, the author gives permission -to link the code of this program with VGUI library developed by -Valve, L.L.C ("Valve"). You must obey the GNU General Public License -in all respects for all of the code used other than VGUI library. -If you modify this file, you may extend this exception to your -version of the file, but you are not obligated to do so. If -you do not wish to do so, delete this exception statement -from your version. - -*/ - -#include "vgui_main.h" -#include "xash3d_types.h" -namespace vgui_support { - -vguiapi_t *g_api; - -Panel *rootpanel = NULL; -CEngineSurface *surface = NULL; -CEngineApp staticApp; - -void VGui_Startup( int width, int height ) -{ - if( rootpanel ) - { - rootpanel->setSize( width, height ); - return; - } - - rootpanel = new Panel; - rootpanel->setSize( width, height ); - rootpanel->setPaintBorderEnabled( false ); - rootpanel->setPaintBackgroundEnabled( false ); - rootpanel->setVisible( true ); - rootpanel->setCursor( new Cursor( Cursor::dc_none )); - - staticApp.start(); - staticApp.setMinimumTickMillisInterval( 0 ); - - surface = new CEngineSurface( rootpanel ); - rootpanel->setSurfaceBaseTraverse( surface ); - - - //ASSERT( rootpanel->getApp() != NULL ); - //ASSERT( rootpanel->getSurfaceBase() != NULL ); - - g_api->DrawInit (); -} - -void VGui_Shutdown( void ) -{ - staticApp.stop(); - - delete rootpanel; - delete surface; - - rootpanel = NULL; - surface = NULL; -} - -void VGui_Paint( void ) -{ - int w, h; - - //if( cls.state != ca_active || !rootpanel ) - // return; - if( !g_api->IsInGame() || !rootpanel ) - return; - - // setup the base panel to cover the screen - Panel *pVPanel = surface->getEmbeddedPanel(); - if( !pVPanel ) return; - //SDL_GetWindowSize(host.hWnd, &w, &h); - //host.input_enabled = rootpanel->isVisible(); - rootpanel->getSize(w, h); - EnableScissor( true ); - - staticApp.externalTick (); - - pVPanel->setBounds( 0, 0, w, h ); - pVPanel->repaint(); - - // paint everything - pVPanel->paintTraverse(); - - EnableScissor( false ); -} -void *VGui_GetPanel( void ) -{ - return (void *)rootpanel; -} -} - -#ifdef INTERNAL_VGUI_SUPPORT -#define InitAPI InitVGUISupportAPI -#endif - -extern "C" EXPORT void InitAPI(vguiapi_t * api) -{ - g_api = api; - g_api->Startup = VGui_Startup; - g_api->Shutdown = VGui_Shutdown; - g_api->GetPanel = VGui_GetPanel; - g_api->Paint = VGui_Paint; - g_api->Mouse = VGUI_Mouse; - g_api->MouseMove = VGUI_MouseMove; - g_api->Key = VGUI_Key; - g_api->TextInput = VGUI_TextInput; -} diff --git a/vgui_support/vgui_main.h b/vgui_support/vgui_main.h deleted file mode 100644 index 5819531d..00000000 --- a/vgui_support/vgui_main.h +++ /dev/null @@ -1,156 +0,0 @@ -/* -vgui_main.h - vgui main header -Copyright (C) 2011 Uncle Mike - -This program is free software: you can redistribute it and/or modify -it under the terms of the GNU General Public License as published by -the Free Software Foundation, either version 3 of the License, or -(at your option) any later version. - -This program is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU General Public License for more details. - -In addition, as a special exception, the author gives permission -to link the code of this program with VGUI library developed by -Valve, L.L.C ("Valve"). You must obey the GNU General Public License -in all respects for all of the code used other than VGUI library. -If you modify this file, you may extend this exception to your -version of the file, but you are not obligated to do so. If -you do not wish to do so, delete this exception statement -from your version. - -*/ -#ifndef VGUI_MAIN_H -#define VGUI_MAIN_H - -#ifdef _WIN32 -#include -#else -#include -#endif - -#include - -#include "vgui_api.h" - -#include -#include -#include -#include -#include -#include -#include -#include -#include - -namespace vgui_support -{ -extern vguiapi_t *g_api; - -using namespace vgui; - -struct PaintStack -{ - Panel *m_pPanel; - int iTranslateX; - int iTranslateY; - int iScissorLeft; - int iScissorRight; - int iScissorTop; - int iScissorBottom; -}; - -class CEngineSurface : public SurfaceBase -{ -private: - - // point translation for current panel - int _translateX; - int _translateY; - - // the size of the window to draw into - int _surfaceExtents[4]; - - void SetupPaintState( const PaintStack *paintState ); - void InitVertex( vpoint_t &vertex, int x, int y, float u, float v ); -public: - CEngineSurface( Panel *embeddedPanel ); - ~CEngineSurface(); -public: - virtual Panel *getEmbeddedPanel( void ); - virtual bool setFullscreenMode( int wide, int tall, int bpp ); - virtual void setWindowedMode( void ); - virtual void setTitle( const char *title ) { } - virtual void createPopup( Panel* embeddedPanel ) { } - virtual bool isWithin( int x, int y ) { return true; } - virtual bool hasFocus( void ); - // now it's not abstract class, yay - virtual void GetMousePos(int &x, int &y) { - g_api->GetCursorPos(&x, &y); - } - void drawPrintChar(int x, int y, int wide, int tall, float s0, float t0, float s1, float t1, int color[]); -protected: - virtual int createNewTextureID( void ); - virtual void drawSetColor( int r, int g, int b, int a ); - virtual void drawSetTextColor( int r, int g, int b, int a ); - virtual void drawFilledRect( int x0, int y0, int x1, int y1 ); - virtual void drawOutlinedRect( int x0,int y0,int x1,int y1 ); - virtual void drawSetTextFont( Font *font ); - virtual void drawSetTextPos( int x, int y ); - virtual void drawPrintText( const char* text, int textLen ); - virtual void drawSetTextureRGBA( int id, const char* rgba, int wide, int tall ); - virtual void drawSetTexture( int id ); - virtual void drawTexturedRect( int x0, int y0, int x1, int y1 ); - virtual bool createPlat( void ) { return false; } - virtual bool recreateContext( void ) { return false; } - virtual void setCursor( Cursor* cursor ); - virtual void pushMakeCurrent( Panel* panel, bool useInsets ); - virtual void popMakeCurrent( Panel* panel ); - - // not used in engine instance - virtual void enableMouseCapture( bool state ) { } - virtual void invalidate( Panel *panel ) { } - virtual void setAsTopMost( bool state ) { } - virtual void applyChanges( void ) { } - virtual void swapBuffers( void ) { } -protected: - Cursor* _hCurrentCursor; - int _drawTextPos[2]; - int _drawColor[4]; - int _drawTextColor[4]; - friend class App; - friend class Panel; -}; - -// initialize VGUI::App as external (part of engine) -class CEngineApp : public App -{ -public: - CEngineApp( bool externalMain = true ) : App( externalMain ) { } - virtual void main( int argc, char* argv[] ) { } // stub -}; - -// -// vgui_input.cpp -// -void *VGui_GetPanel( void ); -void VGui_Paint( void ); -void VGUI_Mouse(VGUI_MouseAction action, int code); -void VGUI_Key(VGUI_KeyAction action, VGUI_KeyCode code); -void VGUI_MouseMove(int x, int y); -void VGUI_TextInput(const char *text); - -// -// vgui_clip.cpp -// -void EnableScissor( qboolean enable ); -void SetScissorRect( int left, int top, int right, int bottom ); -qboolean ClipRect( const vpoint_t &inUL, const vpoint_t &inLR, vpoint_t *pOutUL, vpoint_t *pOutLR ); - -extern CEngineSurface *surface; -extern Panel *root; -} -using namespace vgui_support; -#endif//VGUI_MAIN_H diff --git a/vgui_support/vgui_surf.cpp b/vgui_support/vgui_surf.cpp deleted file mode 100644 index 21802dab..00000000 --- a/vgui_support/vgui_surf.cpp +++ /dev/null @@ -1,440 +0,0 @@ -/* -vgui_surf.cpp - main vgui layer -Copyright (C) 2011 Uncle Mike - -This program is free software: you can redistribute it and/or modify -it under the terms of the GNU General Public License as published by -the Free Software Foundation, either version 3 of the License, or -(at your option) any later version. - -This program is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU General Public License for more details. - -In addition, as a special exception, the author gives permission -to link the code of this program with VGUI library developed by -Valve, L.L.C ("Valve"). You must obey the GNU General Public License -in all respects for all of the code used other than VGUI library. -If you modify this file, you may extend this exception to your -version of the file, but you are not obligated to do so. If -you do not wish to do so, delete this exception statement -from your version. - -*/ - -#include -#include "vgui_main.h" - -#define MAX_PAINT_STACK 16 -#define FONT_SIZE 512 -#define FONT_PAGES 8 - -struct FontInfo -{ - int id; - int pageCount; - int pageForChar[256]; - int bindIndex[FONT_PAGES]; - float texCoord[256][FONT_PAGES]; - int contextCount; -}; - -static int staticContextCount = 0; -static char staticRGBA[FONT_SIZE * FONT_SIZE * 4]; -static Font* staticFont = NULL; -static FontInfo* staticFontInfo; -static Dar staticFontInfoDar; -static PaintStack paintStack[MAX_PAINT_STACK]; -static int staticPaintStackPos = 0; - -#define ColorIndex( c )((( c ) - '0' ) & 7 ) - -CEngineSurface :: CEngineSurface( Panel *embeddedPanel ):SurfaceBase( embeddedPanel ) -{ - _embeddedPanel = embeddedPanel; - _drawColor[0] = _drawColor[1] = _drawColor[2] = _drawColor[3] = 255; - _drawTextColor[0] = _drawTextColor[1] = _drawTextColor[2] = _drawTextColor[3] = 255; - - _surfaceExtents[0] = _surfaceExtents[1] = 0; - //_surfaceExtents[2] = menu.globals->scrWidth; - //_surfaceExtents[3] = menu.globals->scrHeight; - embeddedPanel->getSize(_surfaceExtents[2], _surfaceExtents[3]); - _drawTextPos[0] = _drawTextPos[1] = 0; - _hCurrentCursor = null; - - staticFont = NULL; - staticFontInfo = NULL; - staticFontInfoDar.setCount( 0 ); - staticPaintStackPos = 0; - staticContextCount++; - - _translateX = _translateY = 0; -} - -CEngineSurface :: ~CEngineSurface( void ) -{ - g_api->DrawShutdown (); -} - -Panel *CEngineSurface :: getEmbeddedPanel( void ) -{ - return _embeddedPanel; -} - -bool CEngineSurface :: hasFocus( void ) -{ - // What differs when window does not has focus? - //return host.state != HOST_NOFOCUS; - return true; -} - -void CEngineSurface :: setCursor( Cursor *cursor ) -{ - _currentCursor = cursor; - - if( cursor ) - { - g_api->CursorSelect( (VGUI_DefaultCursor)cursor->getDefaultCursor() ); - } -} - -void CEngineSurface :: SetupPaintState( const PaintStack *paintState ) -{ - _translateX = paintState->iTranslateX; - _translateY = paintState->iTranslateY; - SetScissorRect( paintState->iScissorLeft, paintState->iScissorTop, - paintState->iScissorRight, paintState->iScissorBottom ); -} - -void CEngineSurface :: InitVertex( vpoint_t &vertex, int x, int y, float u, float v ) -{ - vertex.point[0] = x + _translateX; - vertex.point[1] = y + _translateY; - vertex.coord[0] = u; - vertex.coord[1] = v; -} - -int CEngineSurface :: createNewTextureID( void ) -{ - return g_api->GenerateTexture(); -} - -void CEngineSurface :: drawSetColor( int r, int g, int b, int a ) -{ - _drawColor[0] = r; - _drawColor[1] = g; - _drawColor[2] = b; - _drawColor[3] = a; -} - -void CEngineSurface :: drawSetTextColor( int r, int g, int b, int a ) -{ - _drawTextColor[0] = r; - _drawTextColor[1] = g; - _drawTextColor[2] = b; - _drawTextColor[3] = a; -} - -void CEngineSurface :: drawFilledRect( int x0, int y0, int x1, int y1 ) -{ - vpoint_t rect[2]; - vpoint_t clippedRect[2]; - - if( _drawColor[3] >= 255 ) return; - - InitVertex( rect[0], x0, y0, 0, 0 ); - InitVertex( rect[1], x1, y1, 0, 0 ); - - // fully clipped? - if( !ClipRect( rect[0], rect[1], &clippedRect[0], &clippedRect[1] )) - return; - - g_api->SetupDrawingRect( _drawColor ); - g_api->EnableTexture( false ); - g_api->DrawQuad( &clippedRect[0], &clippedRect[1] ); - g_api->EnableTexture( true ); -} - -void CEngineSurface :: drawOutlinedRect( int x0, int y0, int x1, int y1 ) -{ - if( _drawColor[3] >= 255 ) return; - - drawFilledRect( x0, y0, x1, y0 + 1 ); // top - drawFilledRect( x0, y1 - 1, x1, y1 ); // bottom - drawFilledRect( x0, y0 + 1, x0 + 1, y1 - 1 ); // left - drawFilledRect( x1 - 1, y0 + 1, x1, y1 - 1 ); // right -} - -void CEngineSurface :: drawSetTextFont( Font *font ) -{ - staticFont = font; - - if( font ) - { - bool buildFont = false; - - staticFontInfo = NULL; - - for( int i = 0; i < staticFontInfoDar.getCount(); i++ ) - { - if( staticFontInfoDar[i]->id == font->getId( )) - { - staticFontInfo = staticFontInfoDar[i]; - if( staticFontInfo->contextCount != staticContextCount ) - buildFont = true; - } - } - - if( !staticFontInfo || buildFont ) - { - staticFontInfo = new FontInfo; - staticFontInfo->id = 0; - staticFontInfo->pageCount = 0; - staticFontInfo->bindIndex[0] = 0; - staticFontInfo->bindIndex[1] = 0; - staticFontInfo->bindIndex[2] = 0; - staticFontInfo->bindIndex[3] = 0; - memset( staticFontInfo->pageForChar, 0, sizeof( staticFontInfo->pageForChar )); - staticFontInfo->contextCount = -1; - staticFontInfo->id = staticFont->getId(); - staticFontInfoDar.putElement( staticFontInfo ); - staticFontInfo->contextCount = staticContextCount; - - int currentPage = 0; - int x = 0, y = 0; - - memset( staticRGBA, 0, sizeof( staticRGBA )); - - for( int i = 0; i < 256; i++ ) - { - int abcA, abcB, abcC; - staticFont->getCharABCwide( i, abcA, abcB, abcC ); - - int wide = abcB; - - if( isspace( i )) continue; - - int tall = staticFont->getTall(); - - if( x + wide + 1 > FONT_SIZE ) - { - x = 0; - y += tall + 1; - } - - if( y + tall + 1 > FONT_SIZE ) - { - if( !staticFontInfo->bindIndex[currentPage] ) - staticFontInfo->bindIndex[currentPage] = createNewTextureID(); - drawSetTextureRGBA( staticFontInfo->bindIndex[currentPage], staticRGBA, FONT_SIZE, FONT_SIZE ); - currentPage++; - - if( currentPage == FONT_PAGES ) - break; - - memset( staticRGBA, 0, sizeof( staticRGBA )); - x = y = 0; - } - - staticFont->getCharRGBA( i, x, y, FONT_SIZE, FONT_SIZE, (byte *)staticRGBA ); - staticFontInfo->pageForChar[i] = currentPage; - staticFontInfo->texCoord[i][0] = (float)((double)x / (double)FONT_SIZE ); - staticFontInfo->texCoord[i][1] = (float)((double)y / (double)FONT_SIZE ); - staticFontInfo->texCoord[i][2] = (float)((double)(x + wide)/(double)FONT_SIZE ); - staticFontInfo->texCoord[i][3] = (float)((double)(y + tall)/(double)FONT_SIZE ); - x += wide + 1; - } - - if( currentPage != FONT_PAGES ) - { - if( !staticFontInfo->bindIndex[currentPage] ) - staticFontInfo->bindIndex[currentPage] = createNewTextureID(); - drawSetTextureRGBA( staticFontInfo->bindIndex[currentPage], staticRGBA, FONT_SIZE, FONT_SIZE ); - } - staticFontInfo->pageCount = currentPage + 1; - } - } -} - -void CEngineSurface :: drawSetTextPos( int x, int y ) -{ - _drawTextPos[0] = x; - _drawTextPos[1] = y; -} - -void CEngineSurface :: drawPrintChar( int x, int y, int wide, int tall, float s0, float t0, float s1, float t1, int color[4] ) -{ - vpoint_t ul, lr; - - ul.point[0] = x; - ul.point[1] = y; - lr.point[0] = x + wide; - lr.point[1] = y + tall; - - // gets at the texture coords for this character in its texture page - ul.coord[0] = s0; - ul.coord[1] = t0; - lr.coord[0] = s1; - lr.coord[1] = t1; - - vpoint_t clippedRect[2]; - - if( !ClipRect( ul, lr, &clippedRect[0], &clippedRect[1] )) - return; - - g_api->SetupDrawingImage( color ); - g_api->DrawQuad( &clippedRect[0], &clippedRect[1] ); // draw the letter -} - -void CEngineSurface :: drawPrintText( const char* text, int textLen ) -{ - //return; - static bool hasColor = 0; - static int numColor = 7; - - if( !text || !staticFont || _drawTextColor[3] >= 255 ) - return; - - int x = _drawTextPos[0] + _translateX; - int y = _drawTextPos[1] + _translateY; - - int tall = staticFont->getTall(); - - int j, iTotalWidth = 0; - int curTextColor[4]; - - // HACKHACK: allow color strings in VGUI - if( numColor != 7 ) - { - for( j = 0; j < 3; j++ ) // grab predefined color - curTextColor[j] = g_api->GetColor(numColor,j); - } - else - { - for( j = 0; j < 3; j++ ) // revert default color - curTextColor[j] = _drawTextColor[j]; - } - curTextColor[3] = _drawTextColor[3]; // copy alpha - - if( textLen == 1 ) - { - if( *text == '^' ) - { - hasColor = true; - return; // skip '^' - } - else if( hasColor && isdigit( *text )) - { - numColor = ColorIndex( *text ); - hasColor = false; // handled - return; // skip colornum - } - else hasColor = false; - } - for( int i = 0; i < textLen; i++ ) - { - int curCh = g_api->ProcessUtfChar( (unsigned char)text[i] ); - if( !curCh ) - { - continue; - } - - int abcA, abcB, abcC; - - staticFont->getCharABCwide( curCh, abcA, abcB, abcC ); - - float s0 = staticFontInfo->texCoord[curCh][0]; - float t0 = staticFontInfo->texCoord[curCh][1]; - float s1 = staticFontInfo->texCoord[curCh][2]; - float t1 = staticFontInfo->texCoord[curCh][3]; - int wide = abcB; - - iTotalWidth += abcA; - drawSetTexture( staticFontInfo->bindIndex[staticFontInfo->pageForChar[curCh]] ); - drawPrintChar( x + iTotalWidth, y, wide, tall, s0, t0, s1, t1, curTextColor ); - iTotalWidth += wide + abcC; - } - - _drawTextPos[0] += iTotalWidth; -} - -void CEngineSurface :: drawSetTextureRGBA( int id, const char* rgba, int wide, int tall ) -{ - g_api->UploadTexture( id, rgba, wide, tall ); -} - -void CEngineSurface :: drawSetTexture( int id ) -{ - g_api->BindTexture( id ); -} - -void CEngineSurface :: drawTexturedRect( int x0, int y0, int x1, int y1 ) -{ - vpoint_t rect[2]; - vpoint_t clippedRect[2]; - - InitVertex( rect[0], x0, y0, 0, 0 ); - InitVertex( rect[1], x1, y1, 1, 1 ); - - // fully clipped? - if( !ClipRect( rect[0], rect[1], &clippedRect[0], &clippedRect[1] )) - return; - - g_api->SetupDrawingImage( _drawColor ); - g_api->DrawQuad( &clippedRect[0], &clippedRect[1] ); -} - -void CEngineSurface :: pushMakeCurrent( Panel* panel, bool useInsets ) -{ - int insets[4] = { 0, 0, 0, 0 }; - int absExtents[4]; - int clipRect[4]; - - if( useInsets ) - panel->getInset( insets[0], insets[1], insets[2], insets[3] ); - panel->getAbsExtents( absExtents[0], absExtents[1], absExtents[2], absExtents[3] ); - panel->getClipRect( clipRect[0], clipRect[1], clipRect[2], clipRect[3] ); - - PaintStack *paintState = &paintStack[staticPaintStackPos]; - - assert( staticPaintStackPos < MAX_PAINT_STACK ); - - paintState->m_pPanel = panel; - - // determine corrected top left origin - paintState->iTranslateX = insets[0] + absExtents[0]; - paintState->iTranslateY = insets[1] + absExtents[1]; - // setup clipping rectangle for scissoring - paintState->iScissorLeft = clipRect[0]; - paintState->iScissorTop = clipRect[1]; - paintState->iScissorRight = clipRect[2]; - paintState->iScissorBottom = clipRect[3]; - - SetupPaintState( paintState ); - staticPaintStackPos++; -} - -void CEngineSurface :: popMakeCurrent( Panel *panel ) -{ - int top = staticPaintStackPos - 1; - - // more pops that pushes? - assert( top >= 0 ); - - // didn't pop in reverse order of push? - assert( paintStack[top].m_pPanel == panel ); - - staticPaintStackPos--; - - if( top > 0 ) SetupPaintState( &paintStack[top-1] ); -} - -bool CEngineSurface :: setFullscreenMode( int wide, int tall, int bpp ) -{ - return false; -} - -void CEngineSurface :: setWindowedMode( void ) -{ -} diff --git a/vgui_support/wscript b/vgui_support/wscript deleted file mode 100644 index b3613ec2..00000000 --- a/vgui_support/wscript +++ /dev/null @@ -1,125 +0,0 @@ -#! /usr/bin/env python -# encoding: utf-8 -# mittorn, 2018 - -from waflib import Logs -import os - -top = '.' - -VGUI_SUPPORTED_OS = ['win32', 'darwin', 'linux'] - -def options(opt): - grp = opt.add_option_group('VGUI options') - grp.add_option('--vgui', action = 'store', dest = 'VGUI_DEV', default='vgui-dev', - help = 'path to vgui-dev repo [default: %default]') - - grp.add_option('--disable-vgui', action = 'store_true', dest = 'NO_VGUI', default = False, - help = 'disable vgui_support [default: %default]') - - grp.add_option('--skip-vgui-sanity-check', action = 'store_false', dest = 'VGUI_SANITY_CHECK', default=False, - help = 'skip checking VGUI sanity [default: %default]' ) - return - -def configure(conf): - conf.env.NO_VGUI = conf.options.NO_VGUI - if conf.options.NO_VGUI: - return - - conf.start_msg('Does this architecture support VGUI?') - - if conf.env.DEST_CPU != 'x86' and not (conf.env.DEST_CPU == 'x86_64' and not conf.options.ALLOW64): - conf.end_msg('no') - Logs.warn('vgui is not supported on this CPU: ' + str(conf.env.DEST_CPU)) - conf.env.NO_VGUI = True - return - else: - conf.end_msg('yes') - - conf.start_msg('Does this OS support VGUI?') - if conf.env.DEST_OS not in VGUI_SUPPORTED_OS: - conf.end_msg('no') - Logs.warn('vgui is not supported on this OS: ' + str(conf.env.DEST_OS)) - conf.env.NO_VGUI = True - return - else: - conf.end_msg('yes') - - conf.start_msg('Does this toolchain able to link VGUI?') - if conf.env.DEST_OS == 'win32' and conf.env.COMPILER_CXX == 'g++': - conf.end_msg('no') - # we have ABI incompatibility ONLY on MinGW - Logs.warn('vgui_support can\'t be built with MinGW') - conf.env.NO_VGUI = True - return - else: - conf.end_msg('yes') - - if conf.env.NO_VGUI: - return - - if conf.options.VGUI_DEV: - conf.start_msg('Configuring VGUI by provided path') - conf.env.VGUI_DEV = conf.options.VGUI_DEV - else: - conf.start_msg('Configuring VGUI by default path') - conf.env.VGUI_DEV = 'vgui-dev' - - if conf.env.DEST_OS == 'win32': - conf.env.LIB_VGUI = ['vgui'] - conf.env.LIBPATH_VGUI = [os.path.abspath(os.path.join(conf.env.VGUI_DEV, 'lib/win32_vc6/'))] - else: - libpath = os.path.abspath(os.path.join(conf.env.VGUI_DEV, 'lib')) - if conf.env.DEST_OS == 'linux': - conf.env.LIB_VGUI = [':vgui.so'] - conf.env.LIBPATH_VGUI = [libpath] - elif conf.env.DEST_OS == 'darwin': - conf.env.LDFLAGS_VGUI = [os.path.join(libpath, 'vgui.dylib')] - else: - conf.fatal('vgui is not supported on this OS: ' + conf.env.DEST_OS) - conf.env.INCLUDES_VGUI = [os.path.abspath(os.path.join(conf.env.VGUI_DEV, 'include'))] - - conf.env.HAVE_VGUI = 1 - conf.end_msg('yes: {0}, {1}, {2}'.format(conf.env.LIB_VGUI, conf.env.LIBPATH_VGUI, conf.env.INCLUDES_VGUI)) - - if conf.env.HAVE_VGUI and conf.options.VGUI_SANITY_CHECK: - try: - conf.check_cxx( - fragment=''' - #include - int main( int argc, char **argv ) - { - return 0; - }''', - msg = 'Checking for library VGUI sanity', - use = 'VGUI', - execute = False) - except conf.errors.ConfigurationError: - conf.fatal("Can't compile simple program. Check your path to vgui-dev repository.") - -def build(bld): - if bld.env.NO_VGUI: - return - - libs = [] - - # basic build: dedicated only, no dependencies - if bld.env.DEST_OS != 'win32': - libs += ['DL','M'] - - libs.append('VGUI') - - source = bld.path.ant_glob(['*.cpp']) - - includes = [ '.', '../common', '../engine', '../public' ] - - bld.shlib( - source = source, - target = 'vgui_support', - features = 'cxx', - includes = includes, - use = libs, - rpath = '.', - install_path = bld.env.LIBDIR, - subsystem = bld.env.MSVC_SUBSYSTEM - ) From 5b0b9174680166e22b027dcbbad2f4ca2c7b1b97 Mon Sep 17 00:00:00 2001 From: Alibek Omarov Date: Sun, 12 Jun 2022 04:15:41 +0300 Subject: [PATCH 018/300] vgui_support: re-add as submodule --- .gitmodules | 3 +++ vgui_support | 1 + 2 files changed, 4 insertions(+) create mode 160000 vgui_support diff --git a/.gitmodules b/.gitmodules index a3179edb..d16a4037 100644 --- a/.gitmodules +++ b/.gitmodules @@ -10,3 +10,6 @@ [submodule "ref_gl/gl4es"] path = ref_gl/gl4es url = https://github.com/ptitSeb/gl4es +[submodule "vgui_support"] + path = vgui_support + url = https://github.com/FWGS/vgui_support diff --git a/vgui_support b/vgui_support new file mode 160000 index 00000000..2c17ffff --- /dev/null +++ b/vgui_support @@ -0,0 +1 @@ +Subproject commit 2c17ffffdb9afc282fffe45c863868485064b1fb From f63dd7226039b16d6eb176b8c869dd68884125ac Mon Sep 17 00:00:00 2001 From: Alibek Omarov Date: Sun, 12 Jun 2022 04:16:41 +0300 Subject: [PATCH 019/300] scripts: gha: fix including vgui.dll --- 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 c238e8d6..a98afa32 100755 --- a/scripts/gha/build_linux.sh +++ b/scripts/gha/build_linux.sh @@ -67,7 +67,7 @@ build_appimage() cp SDL2_linux/lib/libSDL2-2.0.so.0 "$APPDIR/" if [ "$ARCH" = "i386" ]; then - cp vgui-dev/lib/vgui.so "$APPDIR/" + cp 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 e52f6ddf..5f36d124 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-dev/lib/win32_vc6/vgui.dll . + cp vgui_support/vgui-dev/lib/win32_vc6/vgui.dll . elif [ "$ARCH" = "amd64" ]; then cp SDL2_VC/lib/x64/SDL2.dll . else From c69ea00e793119131feb114a5f61f8877e9b7fa9 Mon Sep 17 00:00:00 2001 From: Alibek Omarov Date: Sun, 12 Jun 2022 04:25:09 +0300 Subject: [PATCH 020/300] wscript: better _FILE_OFFSET_BITS check, as suggested by @nekonomicon --- wscript | 17 +++++++++++------ 1 file changed, 11 insertions(+), 6 deletions(-) diff --git a/wscript b/wscript index 0b23397c..378c29b6 100644 --- a/wscript +++ b/wscript @@ -301,12 +301,17 @@ def configure(conf): # set _FILE_OFFSET_BITS=64 for filesystems with 64-bit inodes if conf.env.DEST_OS != 'win32' and conf.env.DEST_SIZEOF_VOID_P == 4: - file_offset_bits_usable = conf.check_cc(fragment='''#define _FILE_OFFSET_BITS 64 - #include - #ifndef __USE_FILE_OFFSET64 - #error - #endif - int main(void){ return 0; }''', + # check was borrowed from libarchive source code + file_offset_bits_usable = conf.check_cc(fragment=''' +#define _FILE_OFFSET_BITS 64 +#include +#define KB ((off_t)1024) +#define MB ((off_t)1024 * KB) +#define GB ((off_t)1024 * MB) +#define TB ((off_t)1024 * GB) +int t2[(((64 * GB -1) % 671088649) == 268434537) + && (((TB - (64 * GB -1) + 255) % 1792151290) == 305159546)? 1: -1]; +int main(void) { return 0; }''', msg='Checking if _FILE_OFFSET_BITS can be defined to 64', mandatory=False) if file_offset_bits_usable: conf.define('_FILE_OFFSET_BITS', 64) From cc2c97cfad44db585329c777c9ed58b58d2589dd Mon Sep 17 00:00:00 2001 From: Alibek Omarov Date: Mon, 13 Jun 2022 03:07:37 +0300 Subject: [PATCH 021/300] engine: include whereami library to detect current executable path, may be reworked later if needed --- engine/common/whereami.c | 804 +++++++++++++++++++++++++++++++++++++++ engine/common/whereami.h | 67 ++++ 2 files changed, 871 insertions(+) create mode 100644 engine/common/whereami.c create mode 100644 engine/common/whereami.h diff --git a/engine/common/whereami.c b/engine/common/whereami.c new file mode 100644 index 00000000..97f7f858 --- /dev/null +++ b/engine/common/whereami.c @@ -0,0 +1,804 @@ +// (‑●‑●)> dual licensed under the WTFPL v2 and MIT licenses +// without any warranty. +// by Gregory Pakosz (@gpakosz) +// https://github.com/gpakosz/whereami + +// in case you want to #include "whereami.c" in a larger compilation unit +#if !defined(WHEREAMI_H) +#include +#endif + +#ifdef __cplusplus +extern "C" { +#endif + +#if defined(__linux__) || defined(__CYGWIN__) +#undef _DEFAULT_SOURCE +#define _DEFAULT_SOURCE +#elif defined(__APPLE__) +#undef _DARWIN_C_SOURCE +#define _DARWIN_C_SOURCE +#define _DARWIN_BETTER_REALPATH +#endif + +#if !defined(WAI_MALLOC) || !defined(WAI_FREE) || !defined(WAI_REALLOC) +#include +#endif + +#if !defined(WAI_MALLOC) +#define WAI_MALLOC(size) malloc(size) +#endif + +#if !defined(WAI_FREE) +#define WAI_FREE(p) free(p) +#endif + +#if !defined(WAI_REALLOC) +#define WAI_REALLOC(p, size) realloc(p, size) +#endif + +#ifndef WAI_NOINLINE +#if defined(_MSC_VER) +#define WAI_NOINLINE __declspec(noinline) +#elif defined(__GNUC__) +#define WAI_NOINLINE __attribute__((noinline)) +#else +#error unsupported compiler +#endif +#endif + +#if defined(_MSC_VER) +#define WAI_RETURN_ADDRESS() _ReturnAddress() +#elif defined(__GNUC__) +#define WAI_RETURN_ADDRESS() __builtin_extract_return_addr(__builtin_return_address(0)) +#else +#error unsupported compiler +#endif + +#if defined(_WIN32) + +#ifndef WIN32_LEAN_AND_MEAN +#define WIN32_LEAN_AND_MEAN +#endif +#if defined(_MSC_VER) +#pragma warning(push, 3) +#endif +#include +#include +#if defined(_MSC_VER) +#pragma warning(pop) +#endif +#include + +static int WAI_PREFIX(getModulePath_)(HMODULE module, char* out, int capacity, int* dirname_length) +{ + wchar_t buffer1[MAX_PATH]; + wchar_t buffer2[MAX_PATH]; + wchar_t* path = NULL; + int length = -1; + bool ok; + + for (ok = false; !ok; ok = true) + { + DWORD size; + int length_, length__; + + size = GetModuleFileNameW(module, buffer1, sizeof(buffer1) / sizeof(buffer1[0])); + + if (size == 0) + break; + else if (size == (DWORD)(sizeof(buffer1) / sizeof(buffer1[0]))) + { + DWORD size_ = size; + do + { + wchar_t* path_; + + path_ = (wchar_t*)WAI_REALLOC(path, sizeof(wchar_t) * size_ * 2); + if (!path_) + break; + size_ *= 2; + path = path_; + size = GetModuleFileNameW(module, path, size_); + } + while (size == size_); + + if (size == size_) + break; + } + else + path = buffer1; + + if (!_wfullpath(buffer2, path, MAX_PATH)) + break; + length_ = (int)wcslen(buffer2); + length__ = WideCharToMultiByte(CP_UTF8, 0, buffer2, length_ , out, capacity, NULL, NULL); + + if (length__ == 0) + length__ = WideCharToMultiByte(CP_UTF8, 0, buffer2, length_, NULL, 0, NULL, NULL); + if (length__ == 0) + break; + + if (length__ <= capacity && dirname_length) + { + int i; + + for (i = length__ - 1; i >= 0; --i) + { + if (out[i] == '\\') + { + *dirname_length = i; + break; + } + } + } + + length = length__; + } + + if (path != buffer1) + WAI_FREE(path); + + return ok ? length : -1; +} + +WAI_NOINLINE WAI_FUNCSPEC +int WAI_PREFIX(getExecutablePath)(char* out, int capacity, int* dirname_length) +{ + return WAI_PREFIX(getModulePath_)(NULL, out, capacity, dirname_length); +} + +WAI_NOINLINE WAI_FUNCSPEC +int WAI_PREFIX(getModulePath)(char* out, int capacity, int* dirname_length) +{ + HMODULE module; + int length = -1; + +#if defined(_MSC_VER) +#pragma warning(push) +#pragma warning(disable: 4054) +#endif + if (GetModuleHandleEx(GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS | GET_MODULE_HANDLE_EX_FLAG_UNCHANGED_REFCOUNT, (LPCTSTR)WAI_RETURN_ADDRESS(), &module)) +#if defined(_MSC_VER) +#pragma warning(pop) +#endif + { + length = WAI_PREFIX(getModulePath_)(module, out, capacity, dirname_length); + } + + return length; +} + +#elif defined(__linux__) || defined(__CYGWIN__) || defined(__sun) || defined(WAI_USE_PROC_SELF_EXE) + +#include +#include +#include +#if defined(__linux__) +#include +#else +#include +#endif +#ifndef __STDC_FORMAT_MACROS +#define __STDC_FORMAT_MACROS +#endif +#include +#include + +#if !defined(WAI_PROC_SELF_EXE) +#if defined(__sun) +#define WAI_PROC_SELF_EXE "/proc/self/path/a.out" +#else +#define WAI_PROC_SELF_EXE "/proc/self/exe" +#endif +#endif + +WAI_FUNCSPEC +int WAI_PREFIX(getExecutablePath)(char* out, int capacity, int* dirname_length) +{ + char buffer[PATH_MAX]; + char* resolved = NULL; + int length = -1; + bool ok; + + for (ok = false; !ok; ok = true) + { + resolved = realpath(WAI_PROC_SELF_EXE, buffer); + if (!resolved) + break; + + length = (int)strlen(resolved); + if (length <= capacity) + { + memcpy(out, resolved, length); + + if (dirname_length) + { + int i; + + for (i = length - 1; i >= 0; --i) + { + if (out[i] == '/') + { + *dirname_length = i; + break; + } + } + } + } + } + + return ok ? length : -1; +} + +#if !defined(WAI_PROC_SELF_MAPS_RETRY) +#define WAI_PROC_SELF_MAPS_RETRY 5 +#endif + +#if !defined(WAI_PROC_SELF_MAPS) +#if defined(__sun) +#define WAI_PROC_SELF_MAPS "/proc/self/map" +#else +#define WAI_PROC_SELF_MAPS "/proc/self/maps" +#endif +#endif + +#if defined(__ANDROID__) || defined(ANDROID) +#include +#include +#include +#endif +#include + +WAI_NOINLINE WAI_FUNCSPEC +int WAI_PREFIX(getModulePath)(char* out, int capacity, int* dirname_length) +{ + int length = -1; + FILE* maps = NULL; + + for (int r = 0; r < WAI_PROC_SELF_MAPS_RETRY; ++r) + { + maps = fopen(WAI_PROC_SELF_MAPS, "r"); + if (!maps) + break; + + for (;;) + { + char buffer[PATH_MAX < 1024 ? 1024 : PATH_MAX]; + uint64_t low, high; + char perms[5]; + uint64_t offset; + uint32_t major, minor; + char path[PATH_MAX]; + uint32_t inode; + + if (!fgets(buffer, sizeof(buffer), maps)) + break; + + if (sscanf(buffer, "%" PRIx64 "-%" PRIx64 " %s %" PRIx64 " %x:%x %u %s\n", &low, &high, perms, &offset, &major, &minor, &inode, path) == 8) + { + uint64_t addr = (uintptr_t)WAI_RETURN_ADDRESS(); + if (low <= addr && addr <= high) + { + char* resolved; + + resolved = realpath(path, buffer); + if (!resolved) + break; + + length = (int)strlen(resolved); +#if defined(__ANDROID__) || defined(ANDROID) + if (length > 4 + &&buffer[length - 1] == 'k' + &&buffer[length - 2] == 'p' + &&buffer[length - 3] == 'a' + &&buffer[length - 4] == '.') + { + int fd = open(path, O_RDONLY); + if (fd == -1) + { + length = -1; // retry + break; + } + + char* begin = (char*)mmap(0, offset, PROT_READ, MAP_SHARED, fd, 0); + if (begin == MAP_FAILED) + { + close(fd); + length = -1; // retry + break; + } + + char* p = begin + offset - 30; // minimum size of local file header + while (p >= begin) // scan backwards + { + if (*((uint32_t*)p) == 0x04034b50UL) // local file header signature found + { + uint16_t length_ = *((uint16_t*)(p + 26)); + + if (length + 2 + length_ < (int)sizeof(buffer)) + { + memcpy(&buffer[length], "!/", 2); + memcpy(&buffer[length + 2], p + 30, length_); + length += 2 + length_; + } + + break; + } + + --p; + } + + munmap(begin, offset); + close(fd); + } +#endif + if (length <= capacity) + { + memcpy(out, resolved, length); + + if (dirname_length) + { + int i; + + for (i = length - 1; i >= 0; --i) + { + if (out[i] == '/') + { + *dirname_length = i; + break; + } + } + } + } + + break; + } + } + } + + fclose(maps); + maps = NULL; + + if (length != -1) + break; + } + + return length; +} + +#elif defined(__APPLE__) + +#include +#include +#include +#include +#include +#include + +WAI_FUNCSPEC +int WAI_PREFIX(getExecutablePath)(char* out, int capacity, int* dirname_length) +{ + char buffer1[PATH_MAX]; + char buffer2[PATH_MAX]; + char* path = buffer1; + char* resolved = NULL; + int length = -1; + bool ok; + + for (ok = false; !ok; ok = true) + { + uint32_t size = (uint32_t)sizeof(buffer1); + if (_NSGetExecutablePath(path, &size) == -1) + { + path = (char*)WAI_MALLOC(size); + if (!_NSGetExecutablePath(path, &size)) + break; + } + + resolved = realpath(path, buffer2); + if (!resolved) + break; + + length = (int)strlen(resolved); + if (length <= capacity) + { + memcpy(out, resolved, length); + + if (dirname_length) + { + int i; + + for (i = length - 1; i >= 0; --i) + { + if (out[i] == '/') + { + *dirname_length = i; + break; + } + } + } + } + } + + if (path != buffer1) + WAI_FREE(path); + + return ok ? length : -1; +} + +WAI_NOINLINE WAI_FUNCSPEC +int WAI_PREFIX(getModulePath)(char* out, int capacity, int* dirname_length) +{ + char buffer[PATH_MAX]; + char* resolved = NULL; + int length = -1; + + for(;;) + { + Dl_info info; + + if (dladdr(WAI_RETURN_ADDRESS(), &info)) + { + resolved = realpath(info.dli_fname, buffer); + if (!resolved) + break; + + length = (int)strlen(resolved); + if (length <= capacity) + { + memcpy(out, resolved, length); + + if (dirname_length) + { + int i; + + for (i = length - 1; i >= 0; --i) + { + if (out[i] == '/') + { + *dirname_length = i; + break; + } + } + } + } + } + + break; + } + + return length; +} + +#elif defined(__QNXNTO__) + +#include +#include +#include +#include +#include +#include + +#if !defined(WAI_PROC_SELF_EXE) +#define WAI_PROC_SELF_EXE "/proc/self/exefile" +#endif + +WAI_FUNCSPEC +int WAI_PREFIX(getExecutablePath)(char* out, int capacity, int* dirname_length) +{ + char buffer1[PATH_MAX]; + char buffer2[PATH_MAX]; + char* resolved = NULL; + FILE* self_exe = NULL; + int length = -1; + bool ok; + + for (ok = false; !ok; ok = true) + { + self_exe = fopen(WAI_PROC_SELF_EXE, "r"); + if (!self_exe) + break; + + if (!fgets(buffer1, sizeof(buffer1), self_exe)) + break; + + resolved = realpath(buffer1, buffer2); + if (!resolved) + break; + + length = (int)strlen(resolved); + if (length <= capacity) + { + memcpy(out, resolved, length); + + if (dirname_length) + { + int i; + + for (i = length - 1; i >= 0; --i) + { + if (out[i] == '/') + { + *dirname_length = i; + break; + } + } + } + } + } + + fclose(self_exe); + + return ok ? length : -1; +} + +WAI_FUNCSPEC +int WAI_PREFIX(getModulePath)(char* out, int capacity, int* dirname_length) +{ + char buffer[PATH_MAX]; + char* resolved = NULL; + int length = -1; + + for(;;) + { + Dl_info info; + + if (dladdr(WAI_RETURN_ADDRESS(), &info)) + { + resolved = realpath(info.dli_fname, buffer); + if (!resolved) + break; + + length = (int)strlen(resolved); + if (length <= capacity) + { + memcpy(out, resolved, length); + + if (dirname_length) + { + int i; + + for (i = length - 1; i >= 0; --i) + { + if (out[i] == '/') + { + *dirname_length = i; + break; + } + } + } + } + } + + break; + } + + return length; +} + +#elif defined(__DragonFly__) || defined(__FreeBSD__) || \ + defined(__FreeBSD_kernel__) || defined(__NetBSD__) || defined(__OpenBSD__) + +#include +#include +#include +#include +#include +#include +#include + +#if defined(__OpenBSD__) + +#include + +WAI_FUNCSPEC +int WAI_PREFIX(getExecutablePath)(char* out, int capacity, int* dirname_length) +{ + char buffer1[4096]; + char buffer2[PATH_MAX]; + char buffer3[PATH_MAX]; + char** argv = (char**)buffer1; + char* resolved = NULL; + int length = -1; + bool ok; + + for (ok = false; !ok; ok = true) + { + int mib[4] = { CTL_KERN, KERN_PROC_ARGS, getpid(), KERN_PROC_ARGV }; + size_t size; + + if (sysctl(mib, 4, NULL, &size, NULL, 0) != 0) + break; + + if (size > sizeof(buffer1)) + { + argv = (char**)WAI_MALLOC(size); + if (!argv) + break; + } + + if (sysctl(mib, 4, argv, &size, NULL, 0) != 0) + break; + + if (strchr(argv[0], '/')) + { + resolved = realpath(argv[0], buffer2); + if (!resolved) + break; + } + else + { + const char* PATH = getenv("PATH"); + if (!PATH) + break; + + size_t argv0_length = strlen(argv[0]); + + const char* begin = PATH; + while (1) + { + const char* separator = strchr(begin, ':'); + const char* end = separator ? separator : begin + strlen(begin); + + if (end - begin > 0) + { + if (*(end -1) == '/') + --end; + + if (((end - begin) + 1 + argv0_length + 1) <= sizeof(buffer2)) + { + memcpy(buffer2, begin, end - begin); + buffer2[end - begin] = '/'; + memcpy(buffer2 + (end - begin) + 1, argv[0], argv0_length + 1); + + resolved = realpath(buffer2, buffer3); + if (resolved) + break; + } + } + + if (!separator) + break; + + begin = ++separator; + } + + if (!resolved) + break; + } + + length = (int)strlen(resolved); + if (length <= capacity) + { + memcpy(out, resolved, length); + + if (dirname_length) + { + int i; + + for (i = length - 1; i >= 0; --i) + { + if (out[i] == '/') + { + *dirname_length = i; + break; + } + } + } + } + } + + if (argv != (char**)buffer1) + WAI_FREE(argv); + + return ok ? length : -1; +} + +#else + +WAI_FUNCSPEC +int WAI_PREFIX(getExecutablePath)(char* out, int capacity, int* dirname_length) +{ + char buffer1[PATH_MAX]; + char buffer2[PATH_MAX]; + char* path = buffer1; + char* resolved = NULL; + int length = -1; + bool ok; + + for (ok = false; !ok; ok = true) + { +#if defined(__NetBSD__) + int mib[4] = { CTL_KERN, KERN_PROC_ARGS, -1, KERN_PROC_PATHNAME }; +#else + int mib[4] = { CTL_KERN, KERN_PROC, KERN_PROC_PATHNAME, -1 }; +#endif + size_t size = sizeof(buffer1); + + if (sysctl(mib, 4, path, &size, NULL, 0) != 0) + break; + + resolved = realpath(path, buffer2); + if (!resolved) + break; + + length = (int)strlen(resolved); + if (length <= capacity) + { + memcpy(out, resolved, length); + + if (dirname_length) + { + int i; + + for (i = length - 1; i >= 0; --i) + { + if (out[i] == '/') + { + *dirname_length = i; + break; + } + } + } + } + } + + return ok ? length : -1; +} + +#endif + +WAI_NOINLINE WAI_FUNCSPEC +int WAI_PREFIX(getModulePath)(char* out, int capacity, int* dirname_length) +{ + char buffer[PATH_MAX]; + char* resolved = NULL; + int length = -1; + + for(;;) + { + Dl_info info; + + if (dladdr(WAI_RETURN_ADDRESS(), &info)) + { + resolved = realpath(info.dli_fname, buffer); + if (!resolved) + break; + + length = (int)strlen(resolved); + if (length <= capacity) + { + memcpy(out, resolved, length); + + if (dirname_length) + { + int i; + + for (i = length - 1; i >= 0; --i) + { + if (out[i] == '/') + { + *dirname_length = i; + break; + } + } + } + } + } + + break; + } + + return length; +} + +#else + +#error unsupported platform + +#endif + +#ifdef __cplusplus +} +#endif diff --git a/engine/common/whereami.h b/engine/common/whereami.h new file mode 100644 index 00000000..670db54c --- /dev/null +++ b/engine/common/whereami.h @@ -0,0 +1,67 @@ +// (‑●‑●)> dual licensed under the WTFPL v2 and MIT licenses +// without any warranty. +// by Gregory Pakosz (@gpakosz) +// https://github.com/gpakosz/whereami + +#ifndef WHEREAMI_H +#define WHEREAMI_H + +#ifdef __cplusplus +extern "C" { +#endif + +#ifndef WAI_FUNCSPEC + #define WAI_FUNCSPEC +#endif +#ifndef WAI_PREFIX +#define WAI_PREFIX(function) wai_##function +#endif + +/** + * Returns the path to the current executable. + * + * Usage: + * - first call `int length = wai_getExecutablePath(NULL, 0, NULL);` to + * retrieve the length of the path + * - allocate the destination buffer with `path = (char*)malloc(length + 1);` + * - call `wai_getExecutablePath(path, length, NULL)` again to retrieve the + * path + * - add a terminal NUL character with `path[length] = '\0';` + * + * @param out destination buffer, optional + * @param capacity destination buffer capacity + * @param dirname_length optional recipient for the length of the dirname part + * of the path. + * + * @return the length of the executable path on success (without a terminal NUL + * character), otherwise `-1` + */ +WAI_FUNCSPEC +int WAI_PREFIX(getExecutablePath)(char* out, int capacity, int* dirname_length); + +/** + * Returns the path to the current module + * + * Usage: + * - first call `int length = wai_getModulePath(NULL, 0, NULL);` to retrieve + * the length of the path + * - allocate the destination buffer with `path = (char*)malloc(length + 1);` + * - call `wai_getModulePath(path, length, NULL)` again to retrieve the path + * - add a terminal NUL character with `path[length] = '\0';` + * + * @param out destination buffer, optional + * @param capacity destination buffer capacity + * @param dirname_length optional recipient for the length of the dirname part + * of the path. + * + * @return the length of the module path on success (without a terminal NUL + * character), otherwise `-1` + */ +WAI_FUNCSPEC +int WAI_PREFIX(getModulePath)(char* out, int capacity, int* dirname_length); + +#ifdef __cplusplus +} +#endif + +#endif // #ifndef WHEREAMI_H From df83b155a142c2cd132794ba36815731c46f0235 Mon Sep 17 00:00:00 2001 From: Alibek Omarov Date: Mon, 13 Jun 2022 03:26:44 +0300 Subject: [PATCH 022/300] game_launch: rip out execv code --- engine/common/launcher.c | 138 ++++++++++++++++++--------------------- game_launch/game.cpp | 42 ++++-------- 2 files changed, 75 insertions(+), 105 deletions(-) diff --git a/engine/common/launcher.c b/engine/common/launcher.c index 2a47b597..d1ae3706 100644 --- a/engine/common/launcher.c +++ b/engine/common/launcher.c @@ -23,54 +23,78 @@ GNU General Public License for more details. #if XASH_EMSCRIPTEN #include -#endif +#elif XASH_WIN32 +extern "C" +{ +// Enable NVIDIA High Performance Graphics while using Integrated Graphics. +__declspec(dllexport) DWORD NvOptimusEnablement = 0x00000001; -#if XASH_WIN32 -#include // _execve -#else -#include // execve -#endif - -extern char **environ; -static char szGameDir[128]; // safe place to keep gamedir -static int g_iArgc; -static char **g_pszArgv; - -#if XASH_WIN32 || XASH_POSIX -#define USE_EXECVE_FOR_CHANGE_GAME 1 -#else -#define USE_EXECVE_FOR_CHANGE_GAME 0 +// Enable AMD High Performance Graphics while using Integrated Graphics. +__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 -void Launcher_ChangeGame( const char *progname ) +static char szGameDir[128]; // safe place to keep gamedir +static int szArgc; +static char **szArgv; + +static void Sys_ChangeGame( const char *progname ) { - char envstr[256]; - -#if USE_EXECVE_FOR_CHANGE_GAME - Host_Shutdown(); - -#if XASH_WIN32 - _putenv_s( E_GAME, progname ); - _execve( g_pszArgv[0], g_pszArgv, _environ ); -#else - snprintf( envstr, sizeof( envstr ), E_GAME "=%s", progname ); - putenv( envstr ); - execve( g_pszArgv[0], g_pszArgv, environ ); -#endif - -#else + // a1ba: may never be called within engine + // if platform supports execv() function Q_strncpy( szGameDir, progname, sizeof( szGameDir ) - 1 ); Host_Shutdown( ); - exit( Host_Main( g_iArgc, g_pszArgv, szGameDir, 1, &Launcher_ChangeGame ) ); -#endif + exit( Host_Main( szArgc, szArgv, szGameDir, 1, &Launcher_ChangeGame ) ); } -#if XASH_WIN32 -#include -#include // CommandLineToArgvW +_inline int Sys_Start( void ) +{ + int ret; + const char *game = getenv( E_GAME ); + + if( !game ) + game = GAME_PATH; + + Q_strncpy( szGameDir, game, sizeof( szGameDir )); +#if XASH_EMSCRIPTEN +#ifdef EMSCRIPTEN_LIB_FS + // For some unknown reason emscripten refusing to load libraries later + COM_LoadLibrary("menu", 0 ); + COM_LoadLibrary("server", 0 ); + COM_LoadLibrary("client", 0 ); +#endif +#if XASH_DEDICATED + // NodeJS support for debug + EM_ASM(try{ + FS.mkdir('/xash'); + FS.mount(NODEFS, { root: '.'}, '/xash' ); + FS.chdir('/xash'); + }catch(e){};); +#endif +#elif XASH_IOS + { + void IOS_LaunchDialog( void ); + IOS_LaunchDialog(); + } +#endif + + ret = Host_Main( szArgc, szArgv, game, 0, Launcher_ChangeGame ); + + return ret; +} + +#if !XASH_WIN32 +int main( int argc, char **argv ) +{ + szArgc = argc; + szArgv = argv; + + return Sys_Start(); +} +#else int __stdcall WinMain(HINSTANCE hInst, HINSTANCE hPrevInst, LPSTR cmdLine, int nShow) { LPWSTR* lpArgv; @@ -91,9 +115,9 @@ int __stdcall WinMain(HINSTANCE hInst, HINSTANCE hPrevInst, LPSTR cmdLine, int n LocalFree( lpArgv ); - ret = main( szArgc, szArgv ); + ret = Sys_Start(); - for( i = 0; i < szArgc; ++i ) + for( ; i < szArgc; ++i ) free( szArgv[i] ); free( szArgv ); @@ -101,41 +125,5 @@ int __stdcall WinMain(HINSTANCE hInst, HINSTANCE hPrevInst, LPSTR cmdLine, int n } #endif // XASH_WIN32 -int main( int argc, char** argv ) -{ - const char *game = getenv( E_GAME ); - - if( !game ) - game = GAME_PATH; - - Q_strncpy( szGameDir, game, sizeof( szGameDir )); - -#if XASH_EMSCRIPTEN -#ifdef EMSCRIPTEN_LIB_FS - // For some unknown reason emscripten refusing to load libraries later - COM_LoadLibrary("menu", 0 ); - COM_LoadLibrary("server", 0 ); - COM_LoadLibrary("client", 0 ); -#endif -#if XASH_DEDICATED - // NodeJS support for debug - EM_ASM(try{ - FS.mkdir('/xash'); - FS.mount(NODEFS, { root: '.'}, '/xash' ); - FS.chdir('/xash'); - }catch(e){};); -#endif -#endif - - g_iArgc = argc; - g_pszArgv = argv; -#if XASH_IOS - { - void IOS_LaunchDialog( void ); - IOS_LaunchDialog(); - } -#endif - return Host_Main( g_iArgc, g_pszArgv, szGameDir, 0, &Launcher_ChangeGame ); -} #endif diff --git a/game_launch/game.cpp b/game_launch/game.cpp index c4da3a55..cdb6556a 100644 --- a/game_launch/game.cpp +++ b/game_launch/game.cpp @@ -26,18 +26,11 @@ GNU General Public License for more details. #define LoadLibrary( x ) dlopen( x, RTLD_NOW ) #define GetProcAddress( x, y ) dlsym( x, y ) #define FreeLibrary( x ) dlclose( x ) -#include // execve #elif XASH_WIN32 #define XASHLIB "xash.dll" #define SDL2LIB "SDL2.dll" #define dlerror() GetStringLastError() -#include // CommandLineToArgvW -#include // _execve -#else -#error // port me! -#endif -#ifdef XASH_WIN32 extern "C" { // Enable NVIDIA High Performance Graphics while using Integrated Graphics. @@ -46,12 +39,8 @@ __declspec(dllexport) DWORD NvOptimusEnablement = 0x00000001; // Enable AMD High Performance Graphics while using Integrated Graphics. __declspec(dllexport) int AmdPowerXpressRequestHighPerformance = 1; } -#endif - -#if XASH_WIN32 || XASH_POSIX -#define USE_EXECVE_FOR_CHANGE_GAME 0 #else -#define USE_EXECVE_FOR_CHANGE_GAME 0 +#error // port me! #endif #define E_GAME "XASH3D_GAME" // default env dir to start from @@ -61,7 +50,6 @@ typedef void (*pfnChangeGame)( const char *progname ); typedef int (*pfnInit)( int argc, char **argv, const char *progname, int bChangeGame, pfnChangeGame func ); typedef void (*pfnShutdown)( void ); -extern char **environ; static pfnInit Xash_Main; static pfnShutdown Xash_Shutdown = NULL; static char szGameDir[128]; // safe place to keep gamedir @@ -105,10 +93,14 @@ static void Sys_LoadEngine( void ) #if XASH_WIN32 HMODULE hSdl; - if ( ( hSdl = LoadLibraryEx( SDL2LIB, NULL, LOAD_LIBRARY_AS_DATAFILE ) ) == NULL ) + if (( hSdl = LoadLibraryEx( SDL2LIB, NULL, LOAD_LIBRARY_AS_DATAFILE )) == NULL ) + { Xash_Error("Unable to load the " SDL2LIB ": %s", dlerror() ); + } else + { FreeLibrary( hSdl ); + } #endif if(( hEngine = LoadLibrary( XASHLIB )) == NULL ) @@ -130,29 +122,18 @@ static void Sys_UnloadEngine( void ) if( Xash_Shutdown ) Xash_Shutdown( ); if( hEngine ) FreeLibrary( hEngine ); + hEngine = NULL; Xash_Main = NULL; Xash_Shutdown = NULL; } static void Sys_ChangeGame( const char *progname ) { + // a1ba: may never be called within engine + // if platform supports execv() function if( !progname || !progname[0] ) Xash_Error( "Sys_ChangeGame: NULL gamedir" ); -#if USE_EXECVE_FOR_CHANGE_GAME -#if XASH_WIN32 - _putenv_s( E_GAME, progname ); - Sys_UnloadEngine(); - _execve( szArgv[0], szArgv, _environ ); -#else - char envstr[256]; - snprintf( envstr, sizeof( envstr ), E_GAME "=%s", progname ); - putenv( envstr ); - - Sys_UnloadEngine(); - execve( szArgv[0], szArgv, environ ); -#endif -#else if( Xash_Shutdown == NULL ) Xash_Error( "Sys_ChangeGame: missed 'Host_Shutdown' export\n" ); @@ -161,7 +142,6 @@ static void Sys_ChangeGame( const char *progname ) Sys_UnloadEngine(); Sys_LoadEngine (); Xash_Main( szArgc, szArgv, szGameDir, 1, Sys_ChangeGame ); -#endif } _inline int Sys_Start( void ) @@ -173,12 +153,14 @@ _inline int Sys_Start( void ) if( !game ) game = GAME_PATH; + strncpy( szGameDir, game, sizeof( szGameDir ) - 1 ); + Sys_LoadEngine(); if( Xash_Shutdown ) changeGame = Sys_ChangeGame; - ret = Xash_Main( szArgc, szArgv, game, 0, changeGame ); + ret = Xash_Main( szArgc, szArgv, szGameDir, 0, changeGame ); Sys_UnloadEngine(); From e6a2c207de9ca2f2e21dbede197029c0b02d4896 Mon Sep 17 00:00:00 2001 From: Alibek Omarov Date: Mon, 13 Jun 2022 03:42:20 +0300 Subject: [PATCH 023/300] engine: implement change game with execv in-engine. For now it enabled for all platforms, will probably disabled selectively --- engine/common/host.c | 6 ++-- engine/common/system.c | 69 ++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 73 insertions(+), 2 deletions(-) diff --git a/engine/common/host.c b/engine/common/host.c index d3ca35b8..fcac92dd 100644 --- a/engine/common/host.c +++ b/engine/common/host.c @@ -305,7 +305,9 @@ void Host_NewInstance( const char *name, const char *finalmsg ) host.change_game = true; Q_strncpy( host.finalmsg, finalmsg, sizeof( host.finalmsg )); - pChangeGame( name ); // call from hl.exe + + if( !Platform_ChangeGame( name )) + pChangeGame( name ); // call from hl.exe } /* @@ -868,7 +870,7 @@ void Host_InitCommon( int argc, char **argv, const char *progname, qboolean bCha host.enabledll = !Sys_CheckParm( "-nodll" ); - host.change_game = bChangeGame; + host.change_game = bChangeGame || Sys_CheckParm( "-changegame" ); host.config_executed = false; host.status = HOST_INIT; // initialzation started diff --git a/engine/common/system.c b/engine/common/system.c index c41854e8..f154f7ee 100644 --- a/engine/common/system.c +++ b/engine/common/system.c @@ -32,9 +32,14 @@ GNU General Public License for more details. #endif #endif +#if XASH_WIN32 +#include +#endif + #include "menu_int.h" // _UPDATE_PAGE macro #include "library.h" +#include "whereami.h" qboolean error_on_exit = false; // arg for exit(); #define DEBUG_BREAK @@ -539,3 +544,67 @@ void Sys_Print( const char *pMsg ) Rcon_Print( pMsg ); } + +/* +================== +Sys_ChangeGame + +This is a special function + +Here we restart engine with new -game parameter +but since engine will be unloaded during this call +it explicitly doesn't use internal allocation or string copy utils +================== +*/ +qboolean Sys_ChangeGame( const char *gamedir ) +{ + int i = 0; + qboolean replacedArg = false; + size_t exelen; + char *exe, **newargs; + + // don't use engine allocation utils here + // they will be freed after Host_Shutdown + newargs = calloc( host.argc + 4, sizeof( *newargs )); + while( i < host.argc ) + { + newargs[i] = strdup( host.argv[i] ); + + // replace existing -game argument + if( !Q_stricmp( newargs[i], "-game" )) + { + newargs[i + 1] = strdup( gamedir ); + replacedArg = true; + i += 2; + } + else i++; + } + + if( !replacedArg ) + { + newargs[i++] = strdup( "-game" ); + newargs[i++] = strdup( gamedir ); + } + + newargs[i++] = strdup( "-changegame" ); + newargs[i] = NULL; + + exelen = wai_getExecutablePath( NULL, 0, NULL ); + exe = malloc( exelen + 1 ); + wai_getExecutablePath( exe, exelen, NULL ); + exe[exelen] = 0; + + Host_Shutdown(); + + execv( exe, newargs ); + + // if execv returned, it's probably an error + printf( "execv failed: %s", strerror( errno )); + + for( ; i >= 0; i-- ) + free( newargs[i] ); + free( newargs ); + free( exe ); + + return false; +} From e44718d531142da34a9ee5b52db76efcc8179a77 Mon Sep 17 00:00:00 2001 From: Alibek Omarov Date: Mon, 13 Jun 2022 04:05:50 +0300 Subject: [PATCH 024/300] engine: fix build --- engine/common/host.c | 2 +- engine/common/launcher.c | 4 ++-- engine/common/system.c | 3 ++- engine/common/system.h | 1 + 4 files changed, 6 insertions(+), 4 deletions(-) diff --git a/engine/common/host.c b/engine/common/host.c index fcac92dd..fee751ce 100644 --- a/engine/common/host.c +++ b/engine/common/host.c @@ -306,7 +306,7 @@ void Host_NewInstance( const char *name, const char *finalmsg ) host.change_game = true; Q_strncpy( host.finalmsg, finalmsg, sizeof( host.finalmsg )); - if( !Platform_ChangeGame( name )) + if( !Sys_NewInstance( name )) pChangeGame( name ); // call from hl.exe } diff --git a/engine/common/launcher.c b/engine/common/launcher.c index d1ae3706..9410024c 100644 --- a/engine/common/launcher.c +++ b/engine/common/launcher.c @@ -47,7 +47,7 @@ static void Sys_ChangeGame( const char *progname ) // if platform supports execv() function Q_strncpy( szGameDir, progname, sizeof( szGameDir ) - 1 ); Host_Shutdown( ); - exit( Host_Main( szArgc, szArgv, szGameDir, 1, &Launcher_ChangeGame ) ); + exit( Host_Main( szArgc, szArgv, szGameDir, 1, &Sys_ChangeGame ) ); } _inline int Sys_Start( void ) @@ -81,7 +81,7 @@ _inline int Sys_Start( void ) } #endif - ret = Host_Main( szArgc, szArgv, game, 0, Launcher_ChangeGame ); + ret = Host_Main( szArgc, szArgv, game, 0, Sys_ChangeGame ); return ret; } diff --git a/engine/common/system.c b/engine/common/system.c index f154f7ee..bfff6c66 100644 --- a/engine/common/system.c +++ b/engine/common/system.c @@ -17,6 +17,7 @@ GNU General Public License for more details. #include "xash3d_mathlib.h" #include "platform/platform.h" #include +#include #ifdef XASH_SDL #include @@ -556,7 +557,7 @@ but since engine will be unloaded during this call it explicitly doesn't use internal allocation or string copy utils ================== */ -qboolean Sys_ChangeGame( const char *gamedir ) +qboolean Sys_NewInstance( const char *gamedir ) { int i = 0; qboolean replacedArg = false; diff --git a/engine/common/system.h b/engine/common/system.h index 48fb1807..6a186602 100644 --- a/engine/common/system.h +++ b/engine/common/system.h @@ -68,6 +68,7 @@ void Sys_PrintLog( const char *pMsg ); void Sys_InitLog( void ); void Sys_CloseLog( void ); void Sys_Quit( void ) NORETURN; +qboolean Sys_NewInstance( const char *gamedir ); // // sys_con.c From 7a883799170218398b562f19cd4a0a934b9f1f17 Mon Sep 17 00:00:00 2001 From: Alibek Omarov Date: Mon, 13 Jun 2022 04:21:57 +0300 Subject: [PATCH 025/300] game_launch: fix win32 build --- game_launch/game.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/game_launch/game.cpp b/game_launch/game.cpp index cdb6556a..98389f9f 100644 --- a/game_launch/game.cpp +++ b/game_launch/game.cpp @@ -27,6 +27,7 @@ GNU General Public License for more details. #define GetProcAddress( x, y ) dlsym( x, y ) #define FreeLibrary( x ) dlclose( x ) #elif XASH_WIN32 +#include // CommandLineToArgvW #define XASHLIB "xash.dll" #define SDL2LIB "SDL2.dll" #define dlerror() GetStringLastError() From ac308f729827fd8415f36c1645f1c9b9c6aecc47 Mon Sep 17 00:00:00 2001 From: Alibek Omarov Date: Mon, 13 Jun 2022 04:27:54 +0300 Subject: [PATCH 026/300] engine: platform: sdl: fix SDL1.2 build --- engine/platform/sdl/in_sdl.c | 19 +++++++++++++++---- 1 file changed, 15 insertions(+), 4 deletions(-) diff --git a/engine/platform/sdl/in_sdl.c b/engine/platform/sdl/in_sdl.c index a369a90d..129759bb 100644 --- a/engine/platform/sdl/in_sdl.c +++ b/engine/platform/sdl/in_sdl.c @@ -260,11 +260,11 @@ SDLash_InitCursors */ void SDLash_InitCursors( void ) { +#if SDL_VERSION_ATLEAST( 2, 0, 0 ) if( cursors.initialized ) SDLash_FreeCursors(); // load up all default cursors -#if SDL_VERSION_ATLEAST( 2, 0, 0 ) cursors.cursors[dc_none] = NULL; cursors.cursors[dc_arrow] = SDL_CreateSystemCursor(SDL_SYSTEM_CURSOR_ARROW); cursors.cursors[dc_ibeam] = SDL_CreateSystemCursor(SDL_SYSTEM_CURSOR_IBEAM); @@ -278,8 +278,8 @@ void SDLash_InitCursors( void ) cursors.cursors[dc_sizeall] = SDL_CreateSystemCursor(SDL_SYSTEM_CURSOR_SIZEALL); cursors.cursors[dc_no] = SDL_CreateSystemCursor(SDL_SYSTEM_CURSOR_NO); cursors.cursors[dc_hand] = SDL_CreateSystemCursor(SDL_SYSTEM_CURSOR_HAND); -#endif cursors.initialized = true; +#endif } /* @@ -314,8 +314,10 @@ void Platform_SetCursorType( VGUI_DefaultCursor type ) { qboolean visible; +#if SDL_VERSION_ATLEAST( 2, 0, 0 ) if( !cursors.initialized ) return; +#endif if( cls.key_dest != key_game || cl.paused ) return; @@ -331,10 +333,10 @@ void Platform_SetCursorType( VGUI_DefaultCursor type ) break; } -#if SDL_VERSION_ATLEAST( 2, 0, 0 ) if( CVAR_TO_BOOL( touch_emulate )) return; +#if SDL_VERSION_ATLEAST( 2, 0, 0 ) if( visible && !host.mouse_visible ) { SDL_SetCursor( cursors.cursors[type] ); @@ -346,8 +348,17 @@ void Platform_SetCursorType( VGUI_DefaultCursor type ) SDL_ShowCursor( false ); Key_EnableTextInput( false, false ); } - host.mouse_visible = visible; +#else + if( visible && !host.mouse_visible ) + { + SDL_ShowCursor( true ); + } + else if( !visible && host.mouse_visible ) + { + SDL_ShowCursor( false ); + } #endif + host.mouse_visible = visible; } /* From e5562a7b61ab270ee72d67ea585e6997d863a6be Mon Sep 17 00:00:00 2001 From: Alibek Omarov Date: Mon, 13 Jun 2022 04:33:12 +0300 Subject: [PATCH 027/300] engine: whereami: fix C89 --- engine/common/whereami.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/engine/common/whereami.c b/engine/common/whereami.c index 97f7f858..79b01dcc 100644 --- a/engine/common/whereami.c +++ b/engine/common/whereami.c @@ -255,8 +255,9 @@ int WAI_PREFIX(getModulePath)(char* out, int capacity, int* dirname_length) { int length = -1; FILE* maps = NULL; + int r; - for (int r = 0; r < WAI_PROC_SELF_MAPS_RETRY; ++r) + for (r = 0; r < WAI_PROC_SELF_MAPS_RETRY; ++r) { maps = fopen(WAI_PROC_SELF_MAPS, "r"); if (!maps) From 568c7fd917013fd43064bb5bb8fdadf6283ae672 Mon Sep 17 00:00:00 2001 From: Velaron Date: Fri, 10 Jun 2022 11:20:58 +0300 Subject: [PATCH 028/300] engine: strip color codes when writing to log --- engine/common/sys_con.c | 15 +++++++++------ public/crtlib.c | 11 +++++++++++ public/crtlib.h | 1 + 3 files changed, 21 insertions(+), 6 deletions(-) diff --git a/engine/common/sys_con.c b/engine/common/sys_con.c index 96f4bb98..93c0a4b5 100644 --- a/engine/common/sys_con.c +++ b/engine/common/sys_con.c @@ -230,6 +230,9 @@ void Sys_PrintLog( const char *pMsg ) time( &crt_time ); crt_tm = localtime( &crt_time ); + // strip color codes + Q_cleanstr( pMsg, pMsg ); + // platform-specific output #if XASH_ANDROID && !XASH_DEDICATED __android_log_print( ANDROID_LOG_DEBUG, "Xash", "%s", pMsg ); @@ -248,21 +251,21 @@ void Sys_PrintLog( const char *pMsg ) #ifdef XASH_COLORIZE_CONSOLE Sys_PrintColorized( logtime, pMsg ); #else - printf( "%s %s", logtime, pMsg ); + printf( "%s%s", logtime, pMsg ); #endif Sys_FlushStdout(); #endif - // save last char to detect when line was not ended - lastchar = pMsg[strlen(pMsg)-1]; - if( !s_ld.logfile ) return; if( !lastchar || lastchar == '\n') - strftime( logtime, sizeof( logtime ), "[%Y:%m:%d|%H:%M:%S]", crt_tm ); //full time + strftime( logtime, sizeof( logtime ), "[%Y:%m:%d|%H:%M:%S] ", crt_tm ); //full time + + // save last char to detect when line was not ended + lastchar = pMsg[strlen(pMsg)-1]; - fprintf( s_ld.logfile, "%s %s", logtime, pMsg ); + fprintf( s_ld.logfile, "%s%s", logtime, pMsg ); Sys_FlushLogfile(); } diff --git a/public/crtlib.c b/public/crtlib.c index 73bafdf4..cd3cdb28 100644 --- a/public/crtlib.c +++ b/public/crtlib.c @@ -615,6 +615,17 @@ char *Q_strpbrk(const char *s, const char *accept) return NULL; } +void Q_cleanstr( const char *in, char *out ) +{ + while ( *in ) + { + if ( IsColorString( in ) ) + in += 2; + else *out++ = *in++; + } + *out = '\0'; +} + uint Q_hashkey( const char *string, uint hashSize, qboolean caseinsensitive ) { uint i, hashKey = 0; diff --git a/public/crtlib.h b/public/crtlib.h index 5e23423f..1cccf1e3 100644 --- a/public/crtlib.h +++ b/public/crtlib.h @@ -73,6 +73,7 @@ int Q_vsnprintf( char *buffer, size_t buffersize, const char *format, va_list ar 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); +void Q_cleanstr( char *in, char *out ); #define Q_memprint( val ) Q_pretifymem( val, 2 ) char *Q_pretifymem( float value, int digitsafterdecimal ); char *va( const char *format, ... ) _format( 1 ); From 15dc25d2cd4df9d123ed7e2d2586792afa5e1be4 Mon Sep 17 00:00:00 2001 From: Velaron Date: Fri, 10 Jun 2022 18:43:04 +0300 Subject: [PATCH 029/300] engine: rename Q_cleanstr to something more meaningful --- engine/common/sys_con.c | 2 +- public/crtlib.c | 2 +- public/crtlib.h | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/engine/common/sys_con.c b/engine/common/sys_con.c index 93c0a4b5..b79f9e8a 100644 --- a/engine/common/sys_con.c +++ b/engine/common/sys_con.c @@ -231,7 +231,7 @@ void Sys_PrintLog( const char *pMsg ) crt_tm = localtime( &crt_time ); // strip color codes - Q_cleanstr( pMsg, pMsg ); + COM_StripColors( pMsg, pMsg ); // platform-specific output #if XASH_ANDROID && !XASH_DEDICATED diff --git a/public/crtlib.c b/public/crtlib.c index cd3cdb28..cc24a6f3 100644 --- a/public/crtlib.c +++ b/public/crtlib.c @@ -615,7 +615,7 @@ char *Q_strpbrk(const char *s, const char *accept) return NULL; } -void Q_cleanstr( const char *in, char *out ) +void COM_StripColors( const char *in, char *out ) { while ( *in ) { diff --git a/public/crtlib.h b/public/crtlib.h index 1cccf1e3..a50288d8 100644 --- a/public/crtlib.h +++ b/public/crtlib.h @@ -73,7 +73,7 @@ int Q_vsnprintf( char *buffer, size_t buffersize, const char *format, va_list ar 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); -void Q_cleanstr( char *in, char *out ); +void COM_StripColors( const char *in, char *out ); #define Q_memprint( val ) Q_pretifymem( val, 2 ) char *Q_pretifymem( float value, int digitsafterdecimal ); char *va( const char *format, ... ) _format( 1 ); From 6807cf5849fd9c34aabbad3b08f7865f636eabf5 Mon Sep 17 00:00:00 2001 From: Velaron Date: Fri, 10 Jun 2022 20:29:01 +0300 Subject: [PATCH 030/300] engine: fix build on android --- engine/common/sys_con.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/engine/common/sys_con.c b/engine/common/sys_con.c index b79f9e8a..8dfb9938 100644 --- a/engine/common/sys_con.c +++ b/engine/common/sys_con.c @@ -231,7 +231,7 @@ void Sys_PrintLog( const char *pMsg ) crt_tm = localtime( &crt_time ); // strip color codes - COM_StripColors( pMsg, pMsg ); + COM_StripColors( pMsg, (char *)pMsg ); // platform-specific output #if XASH_ANDROID && !XASH_DEDICATED From 402b38951df1c55e4c04c3103088390021fef189 Mon Sep 17 00:00:00 2001 From: Velaron Date: Mon, 13 Jun 2022 22:36:43 +0300 Subject: [PATCH 031/300] engine: better color code stripping --- engine/common/sys_con.c | 84 +++++++++++++++++++++++++++-------------- 1 file changed, 56 insertions(+), 28 deletions(-) diff --git a/engine/common/sys_con.c b/engine/common/sys_con.c index 8dfb9938..ffa0db2b 100644 --- a/engine/common/sys_con.c +++ b/engine/common/sys_con.c @@ -14,7 +14,10 @@ GNU General Public License for more details. */ #include "common.h" -#if XASH_ANDROID +#if XASH_WIN32 +#define STDOUT_FILENO 1 +#include +#elif XASH_ANDROID #include #endif #include @@ -129,16 +132,18 @@ void Sys_InitLog( void ) if( s_ld.log_active ) { s_ld.logfile = fopen( s_ld.log_path, mode ); - if( !s_ld.logfile ) + + if ( !s_ld.logfile ) { Con_Reportf( S_ERROR "Sys_InitLog: can't create log file %s: %s\n", s_ld.log_path, strerror( errno ) ); + return; } - else - { - fprintf( s_ld.logfile, "=================================================================================\n" ); - fprintf( s_ld.logfile, "\t%s (build %i) started at %s\n", s_ld.title, Q_buildnum(), Q_timestamp( TIME_FULL )); - fprintf( s_ld.logfile, "=================================================================================\n" ); - } + + s_ld.logfileno = fileno( s_ld.logfile ); + + fprintf( s_ld.logfile, "=================================================================================\n" ); + fprintf( s_ld.logfile, "\t%s (build %i) started at %s\n", s_ld.title, Q_buildnum(), Q_timestamp( TIME_FULL ) ); + fprintf( s_ld.logfile, "=================================================================================\n" ); } } @@ -220,9 +225,47 @@ static void Sys_PrintColorized( const char *logtime, const char *msg ) printf( "\033[34m%s\033[0m%s\033[0m", logtime, colored ); } +static void Sys_PrintFile( int fd, const char *logtime, const char *msg ) +{ + write( fd, logtime, Q_strlen( logtime ) ); + + while ( *msg ) + { + const char *p = strchr( msg, '^' ); + + if ( p && IsColorString( p ) ) + { + msg += write( fd, msg, p - msg ); + msg += 2; + } else msg += write( fd, msg, Q_strlen( msg ) ); + } +} + +static void Sys_PrintStdout( const char *logtime, const char *msg ) +{ +#if XASH_MOBILE_PLATFORM + static char buf[MAX_PRINT_MSG]; + + // strip color codes + COM_StripColors( msg, buf ); + + // platform-specific output +#if XASH_ANDROID && !XASH_DEDICATED + __android_log_print( ANDROID_LOG_DEBUG, "Xash", "%s", buf ); +#endif // XASH_ANDROID && !XASH_DEDICATED + +#if TARGET_OS_IOS + void IOS_Log( const char * ); + IOS_Log( buf ); +#endif // TARGET_OS_IOS +#else // XASH_MOBILE_PLATFORM + Sys_PrintFile( STDOUT_FILENO, logtime, msg ); +#endif +} + void Sys_PrintLog( const char *pMsg ) { - time_t crt_time; + time_t crt_time; const struct tm *crt_tm; char logtime[32] = ""; static char lastchar; @@ -230,31 +273,16 @@ void Sys_PrintLog( const char *pMsg ) time( &crt_time ); crt_tm = localtime( &crt_time ); - // strip color codes - COM_StripColors( pMsg, (char *)pMsg ); - - // platform-specific output -#if XASH_ANDROID && !XASH_DEDICATED - __android_log_print( ANDROID_LOG_DEBUG, "Xash", "%s", pMsg ); -#endif - -#if TARGET_OS_IOS - void IOS_Log(const char*); - IOS_Log(pMsg); -#endif - if( !lastchar || lastchar == '\n') strftime( logtime, sizeof( logtime ), "[%H:%M:%S] ", crt_tm ); //short time - // spew to stdout, except mobiles -#if !XASH_MOBILE_PLATFORM + // spew to stdout #ifdef XASH_COLORIZE_CONSOLE Sys_PrintColorized( logtime, pMsg ); #else - printf( "%s%s", logtime, pMsg ); + Sys_PrintStdout( logtime, pMsg ); #endif Sys_FlushStdout(); -#endif if( !s_ld.logfile ) return; @@ -263,9 +291,9 @@ void Sys_PrintLog( const char *pMsg ) strftime( logtime, sizeof( logtime ), "[%Y:%m:%d|%H:%M:%S] ", crt_tm ); //full time // save last char to detect when line was not ended - lastchar = pMsg[strlen(pMsg)-1]; + lastchar = pMsg[Q_strlen( pMsg ) - 1]; - fprintf( s_ld.logfile, "%s%s", logtime, pMsg ); + Sys_PrintFile( s_ld.logfileno, logtime, pMsg ); Sys_FlushLogfile(); } From ab6214142ff17ca29bb2c3c0e9809132904272d1 Mon Sep 17 00:00:00 2001 From: Alibek Omarov Date: Tue, 14 Jun 2022 03:23:46 +0300 Subject: [PATCH 032/300] public: remove custom str(r)chr functions --- public/crtlib.c | 26 -------------------------- public/crtlib.h | 4 ++-- 2 files changed, 2 insertions(+), 28 deletions(-) diff --git a/public/crtlib.c b/public/crtlib.c index cc24a6f3..7260595e 100644 --- a/public/crtlib.c +++ b/public/crtlib.c @@ -317,32 +317,6 @@ void Q_atov( float *vec, const char *str, size_t siz ) } } -char *Q_strchr( const char *s, char c ) -{ - size_t len = Q_strlen( s ); - - while( len-- ) - { - if( *++s == c ) - return (char *)s; - } - return 0; -} - -char *Q_strrchr( const char *s, char c ) -{ - size_t len = Q_strlen( s ); - - s += len; - - while( len-- ) - { - if( *--s == c ) - return (char *)s; - } - return 0; -} - int Q_strnicmp( const char *s1, const char *s2, int n ) { int c1, c2; diff --git a/public/crtlib.h b/public/crtlib.h index a50288d8..b03aa812 100644 --- a/public/crtlib.h +++ b/public/crtlib.h @@ -58,8 +58,8 @@ qboolean Q_isspace( const char *str ); int Q_atoi( const char *str ); float Q_atof( const char *str ); void Q_atov( float *vec, const char *str, size_t siz ); -char *Q_strchr( const char *s, char c ); -char *Q_strrchr( const char *s, char c ); +#define Q_strchr strchr +#define Q_strrchr strrchr #define Q_stricmp( s1, s2 ) Q_strnicmp( s1, s2, 99999 ) int Q_strnicmp( const char *s1, const char *s2, int n ); #define Q_strcmp( s1, s2 ) Q_strncmp( s1, s2, 99999 ) From 347c6d6a919afaa0f026f39d35b774b2c3d08a98 Mon Sep 17 00:00:00 2001 From: Alibek Omarov Date: Tue, 14 Jun 2022 03:27:08 +0300 Subject: [PATCH 033/300] engine: common: don't output log to stdout on Win32 where it's done by Wcon. Better colorcode filtration --- engine/common/sys_con.c | 31 +++++++++++++++++++++++-------- 1 file changed, 23 insertions(+), 8 deletions(-) diff --git a/engine/common/sys_con.c b/engine/common/sys_con.c index ffa0db2b..7cd88bee 100644 --- a/engine/common/sys_con.c +++ b/engine/common/sys_con.c @@ -24,7 +24,7 @@ GNU General Public License for more details. #include #if !XASH_WIN32 && !XASH_MOBILE_PLATFORM -#define XASH_COLORIZE_CONSOLE +// #define XASH_COLORIZE_CONSOLE // use with caution, running engine in Qt Creator may cause a freeze in read() call // I was never encountered this bug anywhere else, so still enable by default // #define XASH_USE_SELECT 1 @@ -227,17 +227,32 @@ static void Sys_PrintColorized( const char *logtime, const char *msg ) static void Sys_PrintFile( int fd, const char *logtime, const char *msg ) { + const char *p = msg; + write( fd, logtime, Q_strlen( logtime ) ); - while ( *msg ) + while( p && *p ) { - const char *p = strchr( msg, '^' ); + p = Q_strchr( msg, '^' ); - if ( p && IsColorString( p ) ) + if( p == NULL ) { - msg += write( fd, msg, p - msg ); - msg += 2; - } else msg += write( fd, msg, Q_strlen( msg ) ); + write( fd, msg, Q_strlen( msg )); + break; + } + else if( IsColorString( p )) + { + if( p != msg ) + { + write( fd, msg, p - msg ); + } + msg = p + 2; + } + else + { + write( fd, msg, p - msg + 1 ); + msg = p + 1; + } } } @@ -279,7 +294,7 @@ void Sys_PrintLog( const char *pMsg ) // spew to stdout #ifdef XASH_COLORIZE_CONSOLE Sys_PrintColorized( logtime, pMsg ); -#else +#elif !XASH_WIN32 // Wcon already does the job Sys_PrintStdout( logtime, pMsg ); #endif Sys_FlushStdout(); From 45bf927c7417d28496f089cee5545cfd74ff9a0b Mon Sep 17 00:00:00 2001 From: Alibek Omarov Date: Tue, 14 Jun 2022 03:27:56 +0300 Subject: [PATCH 034/300] engine: filesystem: avoid FS_SysFolderExists spam if stat returned ENOTDIR --- engine/common/filesystem.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/engine/common/filesystem.c b/engine/common/filesystem.c index 3b036b73..734255e7 100644 --- a/engine/common/filesystem.c +++ b/engine/common/filesystem.c @@ -2536,7 +2536,8 @@ qboolean FS_SysFolderExists( const char *path ) if( stat( path, &buf ) < 0 ) { - Con_Reportf( S_ERROR "FS_SysFolderExists: problem while opening dir: %s\n", strerror( errno )); + if( errno != ENOTDIR ) + Con_Reportf( S_ERROR "FS_SysFolderExists: problem while opening dir: %s\n", strerror( errno )); return false; } From 40298cefb628e0d763d11b76821672ead576c7d5 Mon Sep 17 00:00:00 2001 From: Alibek Omarov Date: Tue, 14 Jun 2022 03:30:10 +0300 Subject: [PATCH 035/300] engine: client: don't save configs if shutdown was issued before client was initialized. Remove dead ucmd --- engine/client/cl_main.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/engine/client/cl_main.c b/engine/client/cl_main.c index 518f4225..225172aa 100644 --- a/engine/client/cl_main.c +++ b/engine/client/cl_main.c @@ -2872,7 +2872,6 @@ void CL_InitLocal( void ) Cmd_AddRestrictedCommand ("kill", NULL, "die instantly" ); Cmd_AddCommand ("god", NULL, "enable godmode" ); Cmd_AddCommand ("fov", NULL, "set client field of view" ); - Cmd_AddCommand ("log", NULL, "logging server events" ); // register our commands Cmd_AddCommand ("pause", NULL, "pause the game (if the server allows pausing)" ); @@ -3091,7 +3090,7 @@ void CL_Shutdown( void ) { Con_Printf( "CL_Shutdown()\n" ); - if( !host.crashed ) + if( !host.crashed && cls.initialized ) { Host_WriteOpenGLConfig (); Host_WriteVideoConfig (); From 2388260848a60a0fe81a1933943f93515e367356 Mon Sep 17 00:00:00 2001 From: Alibek Omarov Date: Tue, 14 Jun 2022 04:23:09 +0300 Subject: [PATCH 036/300] engine: optimize colored output * removed unneeded formatting for Android * make generic function to output colorless and colorized strings * disable color output on low memory devices to not waste CPU cycles on it --- engine/common/sys_con.c | 90 ++++++++++++++++++----------------------- 1 file changed, 39 insertions(+), 51 deletions(-) diff --git a/engine/common/sys_con.c b/engine/common/sys_con.c index 7cd88bee..32c760da 100644 --- a/engine/common/sys_con.c +++ b/engine/common/sys_con.c @@ -23,11 +23,14 @@ GNU General Public License for more details. #include #include -#if !XASH_WIN32 && !XASH_MOBILE_PLATFORM -// #define XASH_COLORIZE_CONSOLE +// do not waste precious CPU cycles on mobiles or low memory devices +#if !XASH_WIN32 && !XASH_MOBILE_PLATFORM && !XASH_LOW_MEMORY +#define XASH_COLORIZE_CONSOLE true // use with caution, running engine in Qt Creator may cause a freeze in read() call // I was never encountered this bug anywhere else, so still enable by default // #define XASH_USE_SELECT 1 +#else +#define XASH_COLORIZE_CONSOLE false #endif #if XASH_USE_SELECT @@ -181,51 +184,31 @@ void Sys_CloseLog( void ) } } -static void Sys_PrintColorized( const char *logtime, const char *msg ) +#if XASH_COLORIZE_CONSOLE == true +static void Sys_WriteEscapeSequenceForColorcode( int fd, int c ) { - char colored[4096]; - int len = 0; - - while( *msg && ( len < 4090 ) ) + static const char *q3ToAnsi[ 8 ] = { - static char q3ToAnsi[ 8 ] = - { - '0', // COLOR_BLACK - '1', // COLOR_RED - '2', // COLOR_GREEN - '3', // COLOR_YELLOW - '4', // COLOR_BLUE - '6', // COLOR_CYAN - '5', // COLOR_MAGENTA - 0 // COLOR_WHITE - }; + "\033[30m", // COLOR_BLACK + "\033[31m", // COLOR_RED + "\033[32m", // COLOR_GREEN + "\033[33m", // COLOR_YELLOW + "\033[34m", // COLOR_BLUE + "\033[36m", // COLOR_CYAN + "\033[35m", // COLOR_MAGENTA + "\033[0m", // COLOR_WHITE + }; + const char *esc = q3ToAnsi[c]; - if( IsColorString( msg ) ) - { - int color; - - msg++; - color = q3ToAnsi[ *msg++ % 8 ]; - colored[len++] = '\033'; - colored[len++] = '['; - if( color ) - { - colored[len++] = '3'; - colored[len++] = color; - } - else - colored[len++] = '0'; - colored[len++] = 'm'; - } - else - colored[len++] = *msg++; - } - colored[len] = 0; - - printf( "\033[34m%s\033[0m%s\033[0m", logtime, colored ); + if( c == 7 ) + write( fd, esc, 4 ); + else write( fd, esc, 5 ); } +#else +static void Sys_WriteEscapeSequenceForColorcode( int fd, int c ) {} +#endif -static void Sys_PrintFile( int fd, const char *logtime, const char *msg ) +static void Sys_PrintLogfile( const int fd, const char *logtime, const char *msg, const qboolean colorize ) { const char *p = msg; @@ -243,10 +226,11 @@ static void Sys_PrintFile( int fd, const char *logtime, const char *msg ) else if( IsColorString( p )) { if( p != msg ) - { write( fd, msg, p - msg ); - } msg = p + 2; + + if( colorize ) + Sys_WriteEscapeSequenceForColorcode( fd, ColorIndex( p[1] )); } else { @@ -254,6 +238,10 @@ static void Sys_PrintFile( int fd, const char *logtime, const char *msg ) msg = p + 1; } } + + // flush the color + if( colorize ) + Sys_WriteEscapeSequenceForColorcode( fd, 7 ); } static void Sys_PrintStdout( const char *logtime, const char *msg ) @@ -266,7 +254,7 @@ static void Sys_PrintStdout( const char *logtime, const char *msg ) // platform-specific output #if XASH_ANDROID && !XASH_DEDICATED - __android_log_print( ANDROID_LOG_DEBUG, "Xash", "%s", buf ); + __android_log_write( ANDROID_LOG_DEBUG, "Xash", buf ); #endif // XASH_ANDROID && !XASH_DEDICATED #if TARGET_OS_IOS @@ -274,7 +262,7 @@ static void Sys_PrintStdout( const char *logtime, const char *msg ) IOS_Log( buf ); #endif // TARGET_OS_IOS #else // XASH_MOBILE_PLATFORM - Sys_PrintFile( STDOUT_FILENO, logtime, msg ); + Sys_PrintLogfile( STDOUT_FILENO, logtime, msg, XASH_COLORIZE_CONSOLE ); #endif } @@ -292,15 +280,15 @@ void Sys_PrintLog( const char *pMsg ) strftime( logtime, sizeof( logtime ), "[%H:%M:%S] ", crt_tm ); //short time // spew to stdout -#ifdef XASH_COLORIZE_CONSOLE - Sys_PrintColorized( logtime, pMsg ); -#elif !XASH_WIN32 // Wcon already does the job Sys_PrintStdout( logtime, pMsg ); -#endif Sys_FlushStdout(); if( !s_ld.logfile ) + { + // save last char to detect when line was not ended + lastchar = pMsg[Q_strlen( pMsg ) - 1]; return; + } if( !lastchar || lastchar == '\n') strftime( logtime, sizeof( logtime ), "[%Y:%m:%d|%H:%M:%S] ", crt_tm ); //full time @@ -308,7 +296,7 @@ void Sys_PrintLog( const char *pMsg ) // save last char to detect when line was not ended lastchar = pMsg[Q_strlen( pMsg ) - 1]; - Sys_PrintFile( s_ld.logfileno, logtime, pMsg ); + Sys_PrintLogfile( s_ld.logfileno, logtime, pMsg, false ); Sys_FlushLogfile(); } From 2218126c75d2d81afb669e9492ca39e4159f6973 Mon Sep 17 00:00:00 2001 From: Alibek Omarov Date: Tue, 14 Jun 2022 04:25:40 +0300 Subject: [PATCH 037/300] engine: disable stdout output and flush on Windows, Wcon already does the job --- engine/common/sys_con.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/engine/common/sys_con.c b/engine/common/sys_con.c index 32c760da..67c5d3bb 100644 --- a/engine/common/sys_con.c +++ b/engine/common/sys_con.c @@ -261,8 +261,9 @@ static void Sys_PrintStdout( const char *logtime, const char *msg ) void IOS_Log( const char * ); IOS_Log( buf ); #endif // TARGET_OS_IOS -#else // XASH_MOBILE_PLATFORM +#elif !XASH_WIN32 // Wcon does the job Sys_PrintLogfile( STDOUT_FILENO, logtime, msg, XASH_COLORIZE_CONSOLE ); + Sys_FlushStdout(); #endif } @@ -281,7 +282,6 @@ void Sys_PrintLog( const char *pMsg ) // spew to stdout Sys_PrintStdout( logtime, pMsg ); - Sys_FlushStdout(); if( !s_ld.logfile ) { From fef0993f54b271c42af23b8250651d0a6daa4d82 Mon Sep 17 00:00:00 2001 From: SNMetamorph <25657591+SNMetamorph@users.noreply.github.com> Date: Wed, 15 Jun 2022 02:35:23 +0400 Subject: [PATCH 038/300] engine: client: cl_game: fixed hudGetModelByIndex function in client API (fix #518) --- 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 66a0fc76..4f5fc5b3 100644 --- a/engine/client/cl_game.c +++ b/engine/client/cl_game.c @@ -3865,7 +3865,7 @@ static cl_enginefunc_t gEngfuncs = (void*)Cmd_GetName, pfnGetClientOldTime, pfnGetGravity, - Mod_Handle, + CL_ModelHandle, pfnEnableTexSort, pfnSetLightmapColor, pfnSetLightmapScale, From 463997da51a0a5a4c3a61996ff230f440c136354 Mon Sep 17 00:00:00 2001 From: Alibek Omarov Date: Wed, 15 Jun 2022 13:54:54 +0300 Subject: [PATCH 039/300] engine: common: delete unused Mod_Handle function --- engine/common/mod_local.h | 1 - engine/common/model.c | 17 ----------------- 2 files changed, 18 deletions(-) diff --git a/engine/common/mod_local.h b/engine/common/mod_local.h index 0a794e5f..5c7e579d 100644 --- a/engine/common/mod_local.h +++ b/engine/common/mod_local.h @@ -143,7 +143,6 @@ model_t *Mod_ForName( const char *name, qboolean crash, qboolean trackCRC ); qboolean Mod_ValidateCRC( const char *name, CRC32_t crc ); void Mod_NeedCRC( const char *name, qboolean needCRC ); void Mod_FreeUnused( void ); -model_t *Mod_Handle( int handle ); // // mod_bmodel.c diff --git a/engine/common/model.c b/engine/common/model.c index 1b75a2df..ad8e80ca 100644 --- a/engine/common/model.c +++ b/engine/common/model.c @@ -600,20 +600,3 @@ void Mod_NeedCRC( const char *name, qboolean needCRC ) if( needCRC ) SetBits( p->flags, FCRC_SHOULD_CHECKSUM ); else ClearBits( p->flags, FCRC_SHOULD_CHECKSUM ); } - - -/* -================== -Mod_Handle - -================== -*/ -model_t *GAME_EXPORT Mod_Handle( int handle ) -{ - if( handle < 0 || handle >= MAX_MODELS ) - { - Con_Reportf( "Mod_Handle: bad handle #%i\n", handle ); - return NULL; - } - return &mod_known[handle]; -} From 78d2cd848ae3ba83d27ba2233682f4db3c79e824 Mon Sep 17 00:00:00 2001 From: Alibek Omarov Date: Wed, 15 Jun 2022 18:20:19 +0300 Subject: [PATCH 040/300] mainui: update --- mainui | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mainui b/mainui index 0a28db38..c97245c0 160000 --- a/mainui +++ b/mainui @@ -1 +1 @@ -Subproject commit 0a28db384b9572f2aefbc335fe5255e65f9f7b7f +Subproject commit c97245c0f526880a1b1b7354ce34f92307742d63 From fa09854671a1dc99f3674264396dfd211113e1de Mon Sep 17 00:00:00 2001 From: Alibek Omarov Date: Wed, 15 Jun 2022 18:22:57 +0300 Subject: [PATCH 041/300] engine: client: deprecate and remove ChangeInstance menu call --- engine/client/cl_gameui.c | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/engine/client/cl_gameui.c b/engine/client/cl_gameui.c index 6308e2e6..f295e701 100644 --- a/engine/client/cl_gameui.c +++ b/engine/client/cl_gameui.c @@ -1060,10 +1060,7 @@ pfnChangeInstance */ static void GAME_EXPORT pfnChangeInstance( const char *newInstance, const char *szFinalMessage ) { - if( !szFinalMessage ) szFinalMessage = ""; - if( !newInstance || !*newInstance ) return; - - Host_NewInstance( newInstance, szFinalMessage ); + Con_Reportf( S_ERROR "ChangeInstance menu call is deprecated!\n" ); } /* From a6f12d30093d32a8c908a1b3fa05ab8750ff4b25 Mon Sep 17 00:00:00 2001 From: Alibek Omarov Date: Wed, 15 Jun 2022 18:26:41 +0300 Subject: [PATCH 042/300] ref_gl: gl4es: update --- ref_gl/gl4es | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ref_gl/gl4es b/ref_gl/gl4es index 36a5cd54..5c28420a 160000 --- a/ref_gl/gl4es +++ b/ref_gl/gl4es @@ -1 +1 @@ -Subproject commit 36a5cd54e7ff6cad15ce6d1924c63bf5cf64c1f1 +Subproject commit 5c28420a384c93345a7a5d060a56a0de5f2ac871 From ad4d0cf00a9e1f6ece8e18aa8e650a9cf3a4f816 Mon Sep 17 00:00:00 2001 From: Alibek Omarov Date: Wed, 15 Jun 2022 18:26:51 +0300 Subject: [PATCH 043/300] vgui_support: update --- vgui_support | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/vgui_support b/vgui_support index 2c17ffff..99108598 160000 --- a/vgui_support +++ b/vgui_support @@ -1 +1 @@ -Subproject commit 2c17ffffdb9afc282fffe45c863868485064b1fb +Subproject commit 991085982209a1b8eefabae04d842004d4f4fe4f From 0cef18af86451f6b3c00850ab743acc39c5e889a Mon Sep 17 00:00:00 2001 From: SNMetamorph <25657591+SNMetamorph@users.noreply.github.com> Date: Thu, 16 Jun 2022 00:37:38 +0400 Subject: [PATCH 044/300] engine: client: cl_netgraph: fixed uninitialized alpha in netcolors --- engine/client/cl_netgraph.c | 1 + 1 file changed, 1 insertion(+) diff --git a/engine/client/cl_netgraph.c b/engine/client/cl_netgraph.c index a361c9bd..28cc592c 100644 --- a/engine/client/cl_netgraph.c +++ b/engine/client/cl_netgraph.c @@ -158,6 +158,7 @@ static void NetGraph_InitColors( void ) f = (float)(i - hfrac) / (float)(NETGRAPH_LERP_HEIGHT - hfrac ); VectorMA( mincolor[1], f, dc[1], netcolors[NETGRAPH_NET_COLORS + i] ); } + netcolors[NETGRAPH_NET_COLORS + i][3] = 255; } } From ce8e5880e2a5f2287c568310b8a5fdf9d2bb1d38 Mon Sep 17 00:00:00 2001 From: SNMetamorph <25657591+SNMetamorph@users.noreply.github.com> Date: Thu, 16 Jun 2022 00:39:48 +0400 Subject: [PATCH 045/300] engine: client: cl_netgraph: added clamp for bars height --- engine/client/cl_netgraph.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/engine/client/cl_netgraph.c b/engine/client/cl_netgraph.c index 28cc592c..b31cca65 100644 --- a/engine/client/cl_netgraph.c +++ b/engine/client/cl_netgraph.c @@ -260,7 +260,7 @@ static void NetGraph_DrawTimes( wrect_t rect, int x, int w ) for( a = 0; a < w; a++ ) { i = ( cls.netchan.outgoing_sequence - a ) & NET_TIMINGS_MASK; - h = ( netstat_cmdinfo[i].cmd_lerp / 3.0f ) * NETGRAPH_LERP_HEIGHT; + h = Q_min(( netstat_cmdinfo[i].cmd_lerp / 3.0f ) * NETGRAPH_LERP_HEIGHT, net_graphheight->value * 0.7f); fill.left = x + w - a - 1; fill.right = fill.bottom = 1; From d4bb5423aeb6886479af9619c3a12b0921ad324f Mon Sep 17 00:00:00 2001 From: SNMetamorph <25657591+SNMetamorph@users.noreply.github.com> Date: Thu, 16 Jun 2022 00:41:11 +0400 Subject: [PATCH 046/300] engine: client: cl_netgraph: set rendermode to transparent instead additive --- engine/client/cl_netgraph.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/engine/client/cl_netgraph.c b/engine/client/cl_netgraph.c index b31cca65..7bc6e2a6 100644 --- a/engine/client/cl_netgraph.c +++ b/engine/client/cl_netgraph.c @@ -673,7 +673,7 @@ void SCR_DrawNetGraph( void ) if( graphtype < 3 ) { - ref.dllFuncs.GL_SetRenderMode( kRenderTransAdd ); + ref.dllFuncs.GL_SetRenderMode( kRenderTransColor ); ref.dllFuncs.GL_Bind( XASH_TEXTURE0, R_GetBuiltinTexture( REF_WHITE_TEXTURE ) ); ref.dllFuncs.Begin( TRI_QUADS ); // draw all the fills as a long solid sequence of quads for speedup reasons From a68afcc6726ac903b56f80af514da06fd7e1a3f5 Mon Sep 17 00:00:00 2001 From: SNMetamorph <25657591+SNMetamorph@users.noreply.github.com> Date: Thu, 16 Jun 2022 00:42:57 +0400 Subject: [PATCH 047/300] engine: client: cl_netgraph: added kilobytes per seconds unit to in/out fields --- engine/client/cl_netgraph.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/engine/client/cl_netgraph.c b/engine/client/cl_netgraph.c index 7bc6e2a6..e58a255c 100644 --- a/engine/client/cl_netgraph.c +++ b/engine/client/cl_netgraph.c @@ -396,10 +396,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 k/s", netstat_graph[j].msgbytes, cls.netchan.flow[FLOW_INCOMING].avgkbytespersec ), colors ); + Con_DrawString( x, y, va( "in : %i %.2f kb/s", netstat_graph[j].msgbytes, cls.netchan.flow[FLOW_INCOMING].avgkbytespersec ), colors ); y += 15; - Con_DrawString( x, y, va( "out: %i %.2f k/s", out, cls.netchan.flow[FLOW_OUTGOING].avgkbytespersec ), colors ); + Con_DrawString( x, y, va( "out: %i %.2f kb/s", out, cls.netchan.flow[FLOW_OUTGOING].avgkbytespersec ), colors ); y += 15; if( graphtype > 2 ) From afaabe26a63dcc39223d1c6c3a9c57c03e116592 Mon Sep 17 00:00:00 2001 From: SNMetamorph <25657591+SNMetamorph@users.noreply.github.com> Date: Thu, 16 Jun 2022 00:44:23 +0400 Subject: [PATCH 048/300] engine: client: cl_netgraph: fixed netgraph position calculation in center mode --- engine/client/cl_netgraph.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/engine/client/cl_netgraph.c b/engine/client/cl_netgraph.c index e58a255c..7c9ee99a 100644 --- a/engine/client/cl_netgraph.c +++ b/engine/client/cl_netgraph.c @@ -616,7 +616,7 @@ static void NetGraph_GetScreenPos( wrect_t *rect, int *w, int *x, int *y ) *x = rect->left + rect->right - 5 - *w; break; case 2: // center - *x = rect->left + ( rect->right - 10 - *w ) / 2; + *x = ( rect->left + ( rect->right - 10 - *w )) / 2; break; default: // left sided *x = rect->left + 5; From a1ce5faac20ffc4d9656fdade00b0b3685840d60 Mon Sep 17 00:00:00 2001 From: Tim Schumacher Date: Mon, 2 May 2022 01:22:35 +0200 Subject: [PATCH 049/300] Engine: Keep HTTP from endlessly formatting NaN values For whatever reason, our progress count for HTTP downloads stays at 0. This results in the engine calculating a NaN progress value many times each frame, which results in a significant performance hit. --- 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 ef403b3c..8b491ba9 100644 --- a/engine/common/net_ws.c +++ b/engine/common/net_ws.c @@ -2280,7 +2280,7 @@ void HTTP_Run( void ) } // update progress - if( !Host_IsDedicated() ) + if( !Host_IsDedicated() && iProgressCount != 0 ) Cvar_SetValue( "scr_download", flProgress/iProgressCount * 100 ); HTTP_AutoClean(); From b23d5ed3547883971d1f3bd7b1cf3e3ceef3251e Mon Sep 17 00:00:00 2001 From: Jesse Buhagiar Date: Sun, 2 Jan 2022 00:39:02 +1100 Subject: [PATCH 050/300] Build: Add SerenityOS to list of compatible systems This is required by the build system to spit out a library with the correct name/platform. --- engine/common/build.c | 2 ++ public/build.h | 4 ++++ 2 files changed, 6 insertions(+) diff --git a/engine/common/build.c b/engine/common/build.c index c4ddaeeb..42ba572c 100644 --- a/engine/common/build.c +++ b/engine/common/build.c @@ -95,6 +95,8 @@ const char *Q_buildos( void ) 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 diff --git a/public/build.h b/public/build.h index 6e1f326d..57a7735f 100644 --- a/public/build.h +++ b/public/build.h @@ -74,6 +74,7 @@ For more information, please refer to #undef XASH_RISCV_DOUBLEFP #undef XASH_RISCV_SINGLEFP #undef XASH_RISCV_SOFTFP +#undef XASH_SERENITY #undef XASH_WIN32 #undef XASH_WIN64 #undef XASH_X86 @@ -125,6 +126,9 @@ For more information, please refer to #elif defined __HAIKU__ #define XASH_HAIKU 1 #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" #endif From 99de598ea46dbb5622d252527bbc356177f21a4f Mon Sep 17 00:00:00 2001 From: Andrey Akhmichin <15944199+nekonomicon@users.noreply.github.com> Date: Fri, 24 Jun 2022 07:50:16 +0500 Subject: [PATCH 051/300] engine: common: imagelib: img_tga.c: fix broken tga flip. --- engine/common/imagelib/img_tga.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/engine/common/imagelib/img_tga.c b/engine/common/imagelib/img_tga.c index 59228bdf..a182075b 100644 --- a/engine/common/imagelib/img_tga.c +++ b/engine/common/imagelib/img_tga.c @@ -127,7 +127,7 @@ qboolean Image_LoadTGA( const char *name, const byte *buffer, fs_offset_t filesi targa_rgba = image.rgba = Mem_Malloc( host.imagepool, image.size ); // if bit 5 of attributes isn't set, the image has been stored from bottom to top - if( !Image_CheckFlag( IL_DONTFLIP_TGA ) && targa_header.attributes & 0x20 ) + if( Image_CheckFlag( IL_DONTFLIP_TGA ) || targa_header.attributes & 0x20 ) { pixbuf = targa_rgba; row_inc = 0; From 6063ca2ad10f78322ce98136d97fb7c85d94307b Mon Sep 17 00:00:00 2001 From: Andrey Akhmichin <15944199+nekonomicon@users.noreply.github.com> Date: Thu, 23 Jun 2022 07:25:26 +0500 Subject: [PATCH 052/300] Documentation: opensource-mods: add Urbicide and Sewer. --- Documentation/opensource-mods.md | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/Documentation/opensource-mods.md b/Documentation/opensource-mods.md index 70e666e8..3f50bb6c 100644 --- a/Documentation/opensource-mods.md +++ b/Documentation/opensource-mods.md @@ -24,6 +24,7 @@ Official github repository - https://github.com/CKFDevPowered/CKF3Alpha ## Cold Ice Version 1.9 is available on ModDB - https://www.moddb.com/mods/cold-ice/downloads/cold-ice-sdk + Version 1.9 mirrored on github - https://github.com/solidi/hl-mods/tree/master/ci ## Cold Ice Ressurection @@ -241,6 +242,9 @@ Spirit of Half Life: Opposing-Force Edition - https://github.com/Hammermaps-DEV/ ## Half-Life: Rebellion Reverse-engineered code, branch **rebellion** in hlsdk-xash3d - https://github.com/FWGS/hlsdk-xash3d/tree/rebellion +## Half-Life: Urbicide +Branch **hl_urbicide** in hlsdk-xash3d - https://github.com/FWGS/hlsdk-xash3d/tree/hl_urbicide + ## Half-Life: Visitors malortie's recreation - https://github.com/malortie/hl-visitors @@ -270,6 +274,9 @@ Reverse-engineered code, branch **residual_point** in hlsdk-xash3d - https://git ## Residual Point Reverse-engineered code, branch **residual_point** in hlsdk-xash3d - https://github.com/FWGS/hlsdk-xash3d/tree/residual_point +## Sewer Beta +Branch **sewer_beta** in hlsdk-xash3d - https://github.com/FWGS/hlsdk-xash3d/tree/sewer_beta + ## Team Fortress Classic Reverse-engineered code by Velaron - https://github.com/Velaron/tf15-client From e5720cf8b998cee653018583a116ff2f377bc14b Mon Sep 17 00:00:00 2001 From: Andrey Akhmichin <15944199+nekonomicon@users.noreply.github.com> Date: Fri, 24 Jun 2022 16:00:15 +0500 Subject: [PATCH 053/300] engine: common: imagelib: img_tga.c: fix broken tga flip again. --- engine/common/imagelib/img_tga.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/engine/common/imagelib/img_tga.c b/engine/common/imagelib/img_tga.c index a182075b..6ad7ced3 100644 --- a/engine/common/imagelib/img_tga.c +++ b/engine/common/imagelib/img_tga.c @@ -127,7 +127,7 @@ qboolean Image_LoadTGA( const char *name, const byte *buffer, fs_offset_t filesi targa_rgba = image.rgba = Mem_Malloc( host.imagepool, image.size ); // if bit 5 of attributes isn't set, the image has been stored from bottom to top - if( Image_CheckFlag( IL_DONTFLIP_TGA ) || targa_header.attributes & 0x20 ) + if( Image_CheckFlag( IL_DONTFLIP_TGA ) && targa_header.attributes & 0x20 ) { pixbuf = targa_rgba; row_inc = 0; From 2182ba9630d0d07ab459042bf626bf01de738d2d Mon Sep 17 00:00:00 2001 From: Alibek Omarov Date: Fri, 24 Jun 2022 19:15:28 +0300 Subject: [PATCH 054/300] engine: platform: sdl: try to enable ICO icons for 64-bit Windows, ignore TGA flip attribute for icons --- engine/platform/sdl/vid_sdl.c | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/engine/platform/sdl/vid_sdl.c b/engine/platform/sdl/vid_sdl.c index 4496c772..f589b53d 100644 --- a/engine/platform/sdl/vid_sdl.c +++ b/engine/platform/sdl/vid_sdl.c @@ -598,7 +598,7 @@ void VID_RestoreScreenResolution( void ) #endif // SDL_VERSION_ATLEAST( 2, 0, 0 ) } -#if XASH_WIN32 && !XASH_64BIT // ICO support only for Win32 +#if XASH_WIN32 // ICO support only for Win32 #include "SDL_syswm.h" static void WIN_SetWindowIcon( HICON ico ) { @@ -686,7 +686,7 @@ qboolean VID_CreateWindow( int width, int height, qboolean fullscreen ) VID_RestoreScreenResolution(); } -#if XASH_WIN32 && !XASH_64BIT // ICO support only for Win32 +#if XASH_WIN32 // ICO support only for Win32 if( FS_FileExists( GI->iconpath, true ) ) { HICON ico; @@ -701,7 +701,7 @@ qboolean VID_CreateWindow( int width, int height, qboolean fullscreen ) WIN_SetWindowIcon( ico ); } } -#endif // _WIN32 && !XASH_64BIT +#endif // _WIN32 if( !iconLoaded ) { @@ -709,13 +709,15 @@ qboolean VID_CreateWindow( int width, int height, qboolean fullscreen ) COM_StripExtension( iconpath ); COM_DefaultExtension( iconpath, ".tga" ); + Image_SetForceFlags( IL_DONTFLIP_TGA ); icon = FS_LoadImage( iconpath, NULL, 0 ); + Image_ClearForceFlags(); if( icon ) { SDL_Surface *surface = SDL_CreateRGBSurfaceFrom( icon->buffer, icon->width, icon->height, 32, 4 * icon->width, - 0x000000ff, 0x0000ff00, 0x00ff0000, 0xff000000 ); + 0x000000ff, 0x0000ff00, 0x00ff0000, 0xff000000 ); if( surface ) { From 4e295622be449db43a5d1426f7cb16836a0349cb Mon Sep 17 00:00:00 2001 From: Alibek Omarov Date: Sat, 25 Jun 2022 17:03:11 +0300 Subject: [PATCH 055/300] Revert "engine: platform: sdl: try to enable ICO icons for 64-bit Windows, ignore TGA flip attribute for icons" This reverts commit 2182ba9630d0d07ab459042bf626bf01de738d2d. --- engine/platform/sdl/vid_sdl.c | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/engine/platform/sdl/vid_sdl.c b/engine/platform/sdl/vid_sdl.c index f589b53d..4496c772 100644 --- a/engine/platform/sdl/vid_sdl.c +++ b/engine/platform/sdl/vid_sdl.c @@ -598,7 +598,7 @@ void VID_RestoreScreenResolution( void ) #endif // SDL_VERSION_ATLEAST( 2, 0, 0 ) } -#if XASH_WIN32 // ICO support only for Win32 +#if XASH_WIN32 && !XASH_64BIT // ICO support only for Win32 #include "SDL_syswm.h" static void WIN_SetWindowIcon( HICON ico ) { @@ -686,7 +686,7 @@ qboolean VID_CreateWindow( int width, int height, qboolean fullscreen ) VID_RestoreScreenResolution(); } -#if XASH_WIN32 // ICO support only for Win32 +#if XASH_WIN32 && !XASH_64BIT // ICO support only for Win32 if( FS_FileExists( GI->iconpath, true ) ) { HICON ico; @@ -701,7 +701,7 @@ qboolean VID_CreateWindow( int width, int height, qboolean fullscreen ) WIN_SetWindowIcon( ico ); } } -#endif // _WIN32 +#endif // _WIN32 && !XASH_64BIT if( !iconLoaded ) { @@ -709,15 +709,13 @@ qboolean VID_CreateWindow( int width, int height, qboolean fullscreen ) COM_StripExtension( iconpath ); COM_DefaultExtension( iconpath, ".tga" ); - Image_SetForceFlags( IL_DONTFLIP_TGA ); icon = FS_LoadImage( iconpath, NULL, 0 ); - Image_ClearForceFlags(); if( icon ) { SDL_Surface *surface = SDL_CreateRGBSurfaceFrom( icon->buffer, icon->width, icon->height, 32, 4 * icon->width, - 0x000000ff, 0x0000ff00, 0x00ff0000, 0xff000000 ); + 0x000000ff, 0x0000ff00, 0x00ff0000, 0xff000000 ); if( surface ) { From 6199426e5ed355f3e4f94cff8b944ec442f5dc3f Mon Sep 17 00:00:00 2001 From: Alibek Omarov Date: Sat, 25 Jun 2022 17:03:12 +0300 Subject: [PATCH 056/300] Revert "engine: common: imagelib: img_tga.c: fix broken tga flip again." This reverts commit e5720cf8b998cee653018583a116ff2f377bc14b. --- engine/common/imagelib/img_tga.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/engine/common/imagelib/img_tga.c b/engine/common/imagelib/img_tga.c index 6ad7ced3..a182075b 100644 --- a/engine/common/imagelib/img_tga.c +++ b/engine/common/imagelib/img_tga.c @@ -127,7 +127,7 @@ qboolean Image_LoadTGA( const char *name, const byte *buffer, fs_offset_t filesi targa_rgba = image.rgba = Mem_Malloc( host.imagepool, image.size ); // if bit 5 of attributes isn't set, the image has been stored from bottom to top - if( Image_CheckFlag( IL_DONTFLIP_TGA ) && targa_header.attributes & 0x20 ) + if( Image_CheckFlag( IL_DONTFLIP_TGA ) || targa_header.attributes & 0x20 ) { pixbuf = targa_rgba; row_inc = 0; From 36cec298c2fdffdff695deb5ea56bf9175bc448a Mon Sep 17 00:00:00 2001 From: Alibek Omarov Date: Sat, 25 Jun 2022 17:03:13 +0300 Subject: [PATCH 057/300] Revert "engine: common: imagelib: img_tga.c: fix broken tga flip." This reverts commit 99de598ea46dbb5622d252527bbc356177f21a4f. --- engine/common/imagelib/img_tga.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/engine/common/imagelib/img_tga.c b/engine/common/imagelib/img_tga.c index a182075b..59228bdf 100644 --- a/engine/common/imagelib/img_tga.c +++ b/engine/common/imagelib/img_tga.c @@ -127,7 +127,7 @@ qboolean Image_LoadTGA( const char *name, const byte *buffer, fs_offset_t filesi targa_rgba = image.rgba = Mem_Malloc( host.imagepool, image.size ); // if bit 5 of attributes isn't set, the image has been stored from bottom to top - if( Image_CheckFlag( IL_DONTFLIP_TGA ) || targa_header.attributes & 0x20 ) + if( !Image_CheckFlag( IL_DONTFLIP_TGA ) && targa_header.attributes & 0x20 ) { pixbuf = targa_rgba; row_inc = 0; From 3e23634369236fd0dfffb4219f0f1a529b1821d5 Mon Sep 17 00:00:00 2001 From: Alibek Omarov Date: Sat, 25 Jun 2022 17:04:02 +0300 Subject: [PATCH 058/300] engine: platform: sdl: enable ICO support on Win64 --- engine/platform/sdl/vid_sdl.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/engine/platform/sdl/vid_sdl.c b/engine/platform/sdl/vid_sdl.c index 4496c772..14785a4c 100644 --- a/engine/platform/sdl/vid_sdl.c +++ b/engine/platform/sdl/vid_sdl.c @@ -598,7 +598,7 @@ void VID_RestoreScreenResolution( void ) #endif // SDL_VERSION_ATLEAST( 2, 0, 0 ) } -#if XASH_WIN32 && !XASH_64BIT // ICO support only for Win32 +#if XASH_WIN32 // ICO support only for Win32 #include "SDL_syswm.h" static void WIN_SetWindowIcon( HICON ico ) { @@ -686,7 +686,7 @@ qboolean VID_CreateWindow( int width, int height, qboolean fullscreen ) VID_RestoreScreenResolution(); } -#if XASH_WIN32 && !XASH_64BIT // ICO support only for Win32 +#if XASH_WIN32 // ICO support only for Win32 if( FS_FileExists( GI->iconpath, true ) ) { HICON ico; @@ -728,7 +728,7 @@ qboolean VID_CreateWindow( int width, int height, qboolean fullscreen ) } } -#if XASH_WIN32 && !XASH_64BIT // ICO support only for Win32 +#if XASH_WIN32 // ICO support only for Win32 if( !iconLoaded ) { WIN_SetWindowIcon( LoadIcon( host.hInst, MAKEINTRESOURCE( 101 ) ) ); From 5285f51a268262f94eae86751a95d6a2cfae2c97 Mon Sep 17 00:00:00 2001 From: Alibek Omarov Date: Sat, 25 Jun 2022 17:12:08 +0300 Subject: [PATCH 059/300] engine: platform: sdl: fix loading ICO when it's in RoDir --- engine/platform/sdl/vid_sdl.c | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/engine/platform/sdl/vid_sdl.c b/engine/platform/sdl/vid_sdl.c index 14785a4c..89461073 100644 --- a/engine/platform/sdl/vid_sdl.c +++ b/engine/platform/sdl/vid_sdl.c @@ -628,6 +628,7 @@ qboolean VID_CreateWindow( int width, int height, qboolean fullscreen ) qboolean iconLoaded = false; char iconpath[MAX_STRING]; int xpos, ypos; + const char *localIcoPath; if( vid_highdpi->value ) wndFlags |= SDL_WINDOW_ALLOW_HIGHDPI; Q_strncpy( wndname, GI->title, sizeof( wndname )); @@ -687,13 +688,11 @@ qboolean VID_CreateWindow( int width, int height, qboolean fullscreen ) } #if XASH_WIN32 // ICO support only for Win32 - if( FS_FileExists( GI->iconpath, true ) ) + if(( localIcoPath = FS_GetDiskPath( GI->iconpath, true ))) { HICON ico; - char localPath[MAX_PATH]; - Q_snprintf( localPath, sizeof( localPath ), "%s/%s", GI->gamefolder, GI->iconpath ); - ico = (HICON)LoadImage( NULL, localPath, IMAGE_ICON, 0, 0, LR_LOADFROMFILE|LR_DEFAULTSIZE ); + ico = (HICON)LoadImage( NULL, localIcoPath, IMAGE_ICON, 0, 0, LR_LOADFROMFILE|LR_DEFAULTSIZE ); if( ico ) { From 87ce35b32d1d7101b5322b4b409e0d47c277365f Mon Sep 17 00:00:00 2001 From: Andrey Akhmichin <15944199+nekonomicon@users.noreply.github.com> Date: Sat, 25 Jun 2022 18:25:38 +0500 Subject: [PATCH 060/300] engine: common: imagelib: img_utils.c: change formats priority. --- engine/common/imagelib/img_utils.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/engine/common/imagelib/img_utils.c b/engine/common/imagelib/img_utils.c index 5ff568cf..ce2e57bd 100644 --- a/engine/common/imagelib/img_utils.c +++ b/engine/common/imagelib/img_utils.c @@ -96,8 +96,8 @@ static const loadpixformat_t load_null[] = static const loadpixformat_t load_game[] = { { "%s%s.%s", "dds", Image_LoadDDS, IL_HINT_NO }, // dds for world and studio models -{ "%s%s.%s", "tga", Image_LoadTGA, IL_HINT_NO }, // hl vgui menus { "%s%s.%s", "bmp", Image_LoadBMP, IL_HINT_NO }, // WON menu images +{ "%s%s.%s", "tga", Image_LoadTGA, IL_HINT_NO }, // hl vgui menus { "%s%s.%s", "png", Image_LoadPNG, IL_HINT_NO }, // NightFire 007 menus { "%s%s.%s", "mip", Image_LoadMIP, IL_HINT_NO }, // hl textures from wad or buffer { "%s%s.%s", "mdl", Image_LoadMDL, IL_HINT_HL }, // hl studio model skins From c076f4ff8e7bc3bdf72a9a5ec290024b14578417 Mon Sep 17 00:00:00 2001 From: Alibek Omarov Date: Sun, 26 Jun 2022 04:39:15 +0300 Subject: [PATCH 061/300] engine: common: add generic trace_t initialize function --- engine/common/pm_local.h | 16 ++++++++++++++++ engine/common/pm_trace.c | 15 +++------------ engine/server/sv_world.c | 15 +++------------ 3 files changed, 22 insertions(+), 24 deletions(-) diff --git a/engine/common/pm_local.h b/engine/common/pm_local.h index d73dd375..6ad6d8e2 100644 --- a/engine/common/pm_local.h +++ b/engine/common/pm_local.h @@ -40,6 +40,22 @@ int PM_TruePointContents( playermove_t *pmove, const vec3_t p ); int PM_PointContents( playermove_t *pmove, const vec3_t p ); void PM_ConvertTrace( trace_t *out, pmtrace_t *in, edict_t *ent ); +static inline void PM_InitTrace( trace_t *trace, const vec3_t end ) +{ + memset( trace, 0, sizeof( *trace )); + VectorCopy( end, trace->endpos ); + trace->allsolid = true; + trace->fraction = 1.0f; +} + +static inline void PM_InitPMTrace( pmtrace_t *trace, const vec3_t end ) +{ + memset( trace, 0, sizeof( *trace )); + VectorCopy( end, trace->endpos ); + trace->allsolid = true; + trace->fraction = 1.0f; +} + // // pm_surface.c // diff --git a/engine/common/pm_trace.c b/engine/common/pm_trace.c index c60a55eb..0e1ce7c6 100644 --- a/engine/common/pm_trace.c +++ b/engine/common/pm_trace.c @@ -446,10 +446,7 @@ pmtrace_t PM_PlayerTraceExt( playermove_t *pmove, vec3_t start, vec3_t end, int VectorSubtract( end, offset, end_l ); } - memset( &trace_bbox, 0, sizeof( trace_bbox )); - VectorCopy( end, trace_bbox.endpos ); - trace_bbox.allsolid = true; - trace_bbox.fraction = 1.0f; + PM_InitPMTrace( &trace_bbox, end ); if( hullcount < 1 ) { @@ -475,10 +472,7 @@ pmtrace_t PM_PlayerTraceExt( playermove_t *pmove, vec3_t start, vec3_t end, int for( last_hitgroup = 0, j = 0; j < hullcount; j++ ) { - memset( &trace_hitbox, 0, sizeof( trace_hitbox )); - VectorCopy( end, trace_hitbox.endpos ); - trace_hitbox.allsolid = true; - trace_hitbox.fraction = 1.0f; + PM_InitPMTrace( &trace_hitbox, end ); PM_RecursiveHullCheck( &hull[j], hull[j].firstclipnode, 0, 1, start_l, end_l, &trace_hitbox ); @@ -622,10 +616,7 @@ int PM_TestPlayerPosition( playermove_t *pmove, vec3_t pos, pmtrace_t *ptrace, p { pmtrace_t trace; - memset( &trace, 0, sizeof( trace )); - VectorCopy( pos, trace.endpos ); - trace.allsolid = true; - trace.fraction = 1.0f; + PM_InitPMTrace( &trace, pos ); // run custom sweep callback if( pmove->server || Host_IsLocalClient( )) diff --git a/engine/server/sv_world.c b/engine/server/sv_world.c index ea823974..71079e65 100644 --- a/engine/server/sv_world.c +++ b/engine/server/sv_world.c @@ -871,10 +871,7 @@ void SV_ClipMoveToEntity( edict_t *ent, const vec3_t start, vec3_t mins, vec3_t qboolean rotated, transform_bbox; matrix4x4 matrix; - memset( trace, 0, sizeof( trace_t )); - VectorCopy( end, trace->endpos ); - trace->fraction = 1.0f; - trace->allsolid = 1; + PM_InitTrace( trace, end ); model = SV_ModelHandle( ent->v.modelindex ); @@ -945,10 +942,7 @@ void SV_ClipMoveToEntity( edict_t *ent, const vec3_t start, vec3_t mins, vec3_t for( i = 0; i < hullcount; i++ ) { - memset( &trace_hitbox, 0, sizeof( trace_t )); - VectorCopy( end, trace_hitbox.endpos ); - trace_hitbox.fraction = 1.0; - trace_hitbox.allsolid = 1; + PM_InitTrace( &trace_hitbox, end ); PM_RecursiveHullCheck( &hull[i], hull[i].firstclipnode, 0.0f, 1.0f, start_l, end_l, (pmtrace_t *)&trace_hitbox ); @@ -1114,10 +1108,7 @@ or custom physics implementation void SV_CustomClipMoveToEntity( edict_t *ent, const vec3_t start, vec3_t mins, vec3_t maxs, const vec3_t end, trace_t *trace ) { // initialize custom trace - memset( trace, 0, sizeof( trace_t )); - VectorCopy( end, trace->endpos ); - trace->allsolid = true; - trace->fraction = 1.0f; + PM_InitTrace( trace, end ); if( svgame.physFuncs.ClipMoveToEntity != NULL ) { From 85895c5311764667300557b53c66407208f5e10a Mon Sep 17 00:00:00 2001 From: Alibek Omarov Date: Sun, 26 Jun 2022 04:39:52 +0300 Subject: [PATCH 062/300] engine: pmove: initialize trace argument in PM_TraceModel --- engine/client/cl_pmove.c | 2 ++ engine/server/sv_pmove.c | 2 ++ 2 files changed, 4 insertions(+) diff --git a/engine/client/cl_pmove.c b/engine/client/cl_pmove.c index 3d3a9a98..04dedf1f 100644 --- a/engine/client/cl_pmove.c +++ b/engine/client/cl_pmove.c @@ -794,6 +794,8 @@ static float GAME_EXPORT pfnTraceModel( physent_t *pe, float *start, float *end, matrix4x4 matrix; hull_t *hull; + PM_InitTrace( trace, end ); + old_usehull = clgame.pmove->usehull; clgame.pmove->usehull = 2; diff --git a/engine/server/sv_pmove.c b/engine/server/sv_pmove.c index e8e0ea29..53337e80 100644 --- a/engine/server/sv_pmove.c +++ b/engine/server/sv_pmove.c @@ -455,6 +455,8 @@ static float GAME_EXPORT pfnTraceModel( physent_t *pe, float *start, float *end, matrix4x4 matrix; hull_t *hull; + PM_InitTrace( trace, end ); + old_usehull = svgame.pmove->usehull; svgame.pmove->usehull = 2; From f0a856d9c84b4b99d7228c461ae87a0b64d49ff9 Mon Sep 17 00:00:00 2001 From: Alibek Omarov Date: Sun, 26 Jun 2022 15:05:06 +0300 Subject: [PATCH 063/300] engine: server: fix writing message size for engine messages, as it's expected to be 2-bytes --- engine/server/sv_game.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/engine/server/sv_game.c b/engine/server/sv_game.c index 588bc7d8..d7343fb0 100644 --- a/engine/server/sv_game.c +++ b/engine/server/sv_game.c @@ -2673,7 +2673,7 @@ void GAME_EXPORT pfnMessageEnd( void ) return; } - sv.multicast.pData[svgame.msg_size_index] = svgame.msg_realsize; + *(word *)&sv.multicast.pData[svgame.msg_size_index] = svgame.msg_realsize; } } else if( svgame.msg[svgame.msg_index].size != -1 ) From af8febabd11735e640ab9466e903ac34d1f00be9 Mon Sep 17 00:00:00 2001 From: Alibek Omarov Date: Sun, 26 Jun 2022 15:09:36 +0300 Subject: [PATCH 064/300] engine: client: make temp entity buffer larger in case of long textmessages --- engine/client/cl_tent.c | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/engine/client/cl_tent.c b/engine/client/cl_tent.c index 1e9c19e9..36ea6003 100644 --- a/engine/client/cl_tent.c +++ b/engine/client/cl_tent.c @@ -1904,7 +1904,7 @@ handle temp-entity messages void CL_ParseTempEntity( sizebuf_t *msg ) { sizebuf_t buf; - byte pbuf[256]; + byte pbuf[2048]; int iSize; int type, color, count, flags; int decalIndex, modelIndex, entityIndex; @@ -1923,6 +1923,10 @@ void CL_ParseTempEntity( sizebuf_t *msg ) decalIndex = modelIndex = entityIndex = 0; + // this will probably be fatal anyway + if( iSize > sizeof( pbuf )) + Con_Printf( S_ERROR "%s: Temp buffer overflow!\n", __FUNCTION__ ); + // parse user message into buffer MSG_ReadBytes( msg, pbuf, iSize ); From b9b8b0521bb8cd91b708c2ef38ac2480ccac072e Mon Sep 17 00:00:00 2001 From: Alibek Omarov Date: Wed, 29 Jun 2022 02:36:39 +0300 Subject: [PATCH 065/300] common: add unlikely()/likely() macros --- common/xash3d_types.h | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/common/xash3d_types.h b/common/xash3d_types.h index 71ceca77..ffc3faee 100644 --- a/common/xash3d_types.h +++ b/common/xash3d_types.h @@ -87,6 +87,16 @@ typedef uint64_t longtime_t; #define NORETURN #endif +#if defined(__has_builtin) +#if __has_builtin(__builtin_expect) +#define unlikely(x) __builtin_expect(x, 0) +#define likely(x) __builtin_expect(x, 1) +#else // __has_builtin(__builtin_expect) +#define unlikely(x) (x) +#define likely(x) (x) +#endif // __has_builtin(__builtin_expect) +#endif // defined(__has_builtin) + #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 12815bfbf2778f349e54f975b18af12041acbca9 Mon Sep 17 00:00:00 2001 From: Alibek Omarov Date: Wed, 29 Jun 2022 02:39:18 +0300 Subject: [PATCH 066/300] public: remove naive implementations of standard function, add them with standard C with few extensions --- public/crtlib.c | 93 +++---------------------------------------------- public/crtlib.h | 63 +++++++++++++++++++++++++++++---- 2 files changed, 60 insertions(+), 96 deletions(-) diff --git a/public/crtlib.c b/public/crtlib.c index 7260595e..11efccc3 100644 --- a/public/crtlib.c +++ b/public/crtlib.c @@ -317,68 +317,6 @@ void Q_atov( float *vec, const char *str, size_t siz ) } } -int Q_strnicmp( const char *s1, const char *s2, int n ) -{ - int c1, c2; - - if( s1 == NULL ) - { - if( s2 == NULL ) - return 0; - else return -1; - } - else if( s2 == NULL ) - { - return 1; - } - - do { - c1 = *s1++; - c2 = *s2++; - - if( !n-- ) return 0; // strings are equal until end point - - if( c1 != c2 ) - { - if( c1 >= 'a' && c1 <= 'z' ) c1 -= ('a' - 'A'); - if( c2 >= 'a' && c2 <= 'z' ) c2 -= ('a' - 'A'); - if( c1 != c2 ) return c1 < c2 ? -1 : 1; - } - } while( c1 ); - - // strings are equal - return 0; -} - -int Q_strncmp( const char *s1, const char *s2, int n ) -{ - int c1, c2; - - if( s1 == NULL ) - { - if( s2 == NULL ) - return 0; - else return -1; - } - else if( s2 == NULL ) - { - return 1; - } - - do { - c1 = *s1++; - c2 = *s2++; - - // strings are equal until end point - if( !n-- ) return 0; - if( c1 != c2 ) return c1 < c2 ? -1 : 1; - - } while( c1 ); - - // strings are equal - return 0; -} - static qboolean Q_starcmp( const char *pattern, const char *text ) { char c, c1; @@ -470,32 +408,8 @@ const char* Q_timestamp( int format ) return timestamp; } -char *Q_strstr( const char *string, const char *string2 ) -{ - int c; - size_t len; - - if( !string || !string2 ) return NULL; - - c = *string2; - len = Q_strlen( string2 ); - - while( string ) - { - for( ; *string && *string != c; string++ ); - - if( *string ) - { - if( !Q_strncmp( string, string2, len )) - break; - string++; - } - else return NULL; - } - return (char *)string; -} - -char *Q_stristr( const char *string, const char *string2 ) +#if !defined( HAVE_STRCASESTR ) +const char *Q_stristr( const char *string, const char *string2 ) { int c; size_t len; @@ -517,8 +431,9 @@ char *Q_stristr( const char *string, const char *string2 ) } else return NULL; } - return (char *)string; + return string; } +#endif // !defined( HAVE_STRCASESTR ) int Q_vsnprintf( char *buffer, size_t buffersize, const char *format, va_list args ) { diff --git a/public/crtlib.h b/public/crtlib.h index b03aa812..0bcf9ce7 100644 --- a/public/crtlib.h +++ b/public/crtlib.h @@ -16,9 +16,10 @@ GNU General Public License for more details. #ifndef STDLIB_H #define STDLIB_H -#include #include +#include #include "build.h" +#include "xash3d_types.h" // timestamp modes enum @@ -60,14 +61,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 -#define Q_stricmp( s1, s2 ) Q_strnicmp( s1, s2, 99999 ) -int Q_strnicmp( const char *s1, const char *s2, int n ); -#define Q_strcmp( s1, s2 ) Q_strncmp( s1, s2, 99999 ) -int Q_strncmp( const char *s1, const char *s2, int n ); qboolean Q_stricmpext( const char *s1, const char *s2 ); const char *Q_timestamp( int format ); -char *Q_stristr( const char *string, const char *string2 ); -char *Q_strstr( const char *string, const char *string2 ); #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 ); int Q_snprintf( char *buffer, size_t buffersize, const char *format, ... ) _format( 3 ); @@ -95,4 +90,58 @@ char *_COM_ParseFileSafe( char *data, char *token, const int size, unsigned int int matchpattern( const char *in, const char *pattern, qboolean caseinsensitive ); int matchpattern_with_separator( const char *in, const char *pattern, qboolean caseinsensitive, const char *separators, qboolean wildcard_least_one ); +// libc implementations +static inline int Q_strcmp( const char *s1, const char *s2 ) +{ + return unlikely(!s1) ? + ( !s2 ? 0 : -1 ) : + ( unlikely(!s2) ? 1 : strcmp( s1, s2 )); +} + +static inline int Q_strncmp( const char *s1, const char *s2, size_t n ) +{ + return unlikely(!s1) ? + ( !s2 ? 0 : -1 ) : + ( unlikely(!s2) ? 1 : strncmp( s1, s2, n )); +} + +static inline const char *Q_strstr( const char *s1, const char *s2 ) +{ + return unlikely( !s1 || !s2 ) ? NULL : strstr( s1, s2 ); +} + +// libc extensions, be careful + +#if XASH_WIN32 +#define strcasecmp stricmp +#define strncasecmp strnicmp +#endif // XASH_WIN32 + +static inline int Q_stricmp( const char *s1, const char *s2 ) +{ + return unlikely(!s1) ? + ( !s2 ? 0 : -1 ) : + ( unlikely(!s2) ? 1 : strcasecmp( s1, s2 )); +} + +static inline int Q_strnicmp( const char *s1, const char *s2, size_t n ) +{ + return unlikely(!s1) ? + ( !s2 ? 0 : -1 ) : + ( unlikely(!s2) ? 1 : strncasecmp( s1, s2, n )); +} + +#if defined( HAVE_STRCASESTR ) +#if XASH_WIN32 +#define strcasestr stristr +#endif +static inline const char *Q_stristr( const char *s1, const char *s2 ) +{ + return unlikely( !s1 || !s2 ) ? NULL : strcasestr( s1, s2 ); +} +#else // defined( HAVE_STRCASESTR ) +const char *Q_stristr( const char *s1, const char *s2 ); +#endif // defined( HAVE_STRCASESTR ) + + #endif//STDLIB_H From d52b07beaccc13e8714990d12ba323696f1c5e3e Mon Sep 17 00:00:00 2001 From: Alibek Omarov Date: Wed, 29 Jun 2022 02:43:09 +0300 Subject: [PATCH 067/300] wscript: add stristr/strcasestr presense check --- wscript | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/wscript b/wscript index 378c29b6..f4ffcb20 100644 --- a/wscript +++ b/wscript @@ -317,6 +317,19 @@ int main(void) { return 0; }''', conf.define('_FILE_OFFSET_BITS', 64) else: conf.undefine('_FILE_OFFSET_BITS') + if conf.env.DEST_OS == 'win32': + # msvcrt always has stristr + conf.define('HAVE_STRCASESTR', 1) + else: + strcasestr_frag = '''#include +int main(int argc, char **argv) { strcasestr(argv[1], argv[2]); 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') From 79dc090aae4b27ee2de1f88640cfa26bfc8a15c8 Mon Sep 17 00:00:00 2001 From: Alibek Omarov Date: Wed, 29 Jun 2022 02:44:50 +0300 Subject: [PATCH 068/300] engine: platform: sdl: use SetClassLongPtr function to be compatible with 64-bit Windows API --- engine/platform/sdl/vid_sdl.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/engine/platform/sdl/vid_sdl.c b/engine/platform/sdl/vid_sdl.c index 89461073..c2453cca 100644 --- a/engine/platform/sdl/vid_sdl.c +++ b/engine/platform/sdl/vid_sdl.c @@ -609,7 +609,7 @@ static void WIN_SetWindowIcon( HICON ico ) if( SDL_GetWindowWMInfo( host.hWnd, &wminfo ) ) { - SetClassLong( wminfo.info.win.window, GCL_HICON, (LONG)ico ); + SetClassLongPtr( wminfo.info.win.window, GCLP_HICON, (LONG)ico ); } } #endif From 2905f951bd5a789677a4228a5c82f086a650cc4c Mon Sep 17 00:00:00 2001 From: Alibek Omarov Date: Wed, 29 Jun 2022 03:20:55 +0300 Subject: [PATCH 069/300] engine: menu_int: expose TF_EXPAND_SOURCE imagelib flag --- engine/menu_int.h | 1 + 1 file changed, 1 insertion(+) diff --git a/engine/menu_int.h b/engine/menu_int.h index f8b23614..8a6905d6 100644 --- a/engine/menu_int.h +++ b/engine/menu_int.h @@ -30,6 +30,7 @@ typedef int HIMAGE; // handle to a graphic #define PIC_NEAREST (1<<0) // disable texfilter #define PIC_KEEP_SOURCE (1<<1) // some images keep source #define PIC_NOFLIP_TGA (1<<2) // Steam background completely ignore tga attribute 0x20 +#define PIC_EXPAND_SOURCE (1<<3) // don't keep as 8-bit source, expand to RGBA // flags for COM_ParseFileSafe #define PFILE_IGNOREBRACKET (1<<0) From ac9664c7ae39c4c1b9bb43a41d68aa39d6047d4c Mon Sep 17 00:00:00 2001 From: Alibek Omarov Date: Wed, 29 Jun 2022 03:23:51 +0300 Subject: [PATCH 070/300] mainui: update --- mainui | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mainui b/mainui index c97245c0..2ba099b2 160000 --- a/mainui +++ b/mainui @@ -1 +1 @@ -Subproject commit c97245c0f526880a1b1b7354ce34f92307742d63 +Subproject commit 2ba099b2a1b32dfdb4ecb66b27fc6df095199eb4 From 5d4f8373c44a9fa6b8e96b76bbad46b8f46662f5 Mon Sep 17 00:00:00 2001 From: Alibek Omarov Date: Wed, 29 Jun 2022 03:50:50 +0300 Subject: [PATCH 071/300] common: fix usage of __has_builtin for old GCC releases --- common/xash3d_types.h | 24 +++++++++++++++--------- 1 file changed, 15 insertions(+), 9 deletions(-) diff --git a/common/xash3d_types.h b/common/xash3d_types.h index ffc3faee..71472d43 100644 --- a/common/xash3d_types.h +++ b/common/xash3d_types.h @@ -87,15 +87,21 @@ typedef uint64_t longtime_t; #define NORETURN #endif -#if defined(__has_builtin) -#if __has_builtin(__builtin_expect) -#define unlikely(x) __builtin_expect(x, 0) -#define likely(x) __builtin_expect(x, 1) -#else // __has_builtin(__builtin_expect) -#define unlikely(x) (x) -#define likely(x) (x) -#endif // __has_builtin(__builtin_expect) -#endif // defined(__has_builtin) +#if ( __GNUC__ >= 3 ) + #define unlikely(x) __builtin_expect(x, 0) + #define likely(x) __builtin_expect(x, 1) +#elif defined( __has_builtin ) + #if __has_builtin( __builtin_expect ) + #define unlikely(x) __builtin_expect(x, 0) + #define likely(x) __builtin_expect(x, 1) + #else + #define unlikely(x) (x) + #define likely(x) (x) + #endif +#else + #define unlikely(x) (x) + #define likely(x) (x) +#endif #ifdef XASH_BIG_ENDIAN From fb43a5590f820f18318fe41068170e819316ec16 Mon Sep 17 00:00:00 2001 From: Alibek Omarov Date: Wed, 29 Jun 2022 03:56:42 +0300 Subject: [PATCH 072/300] public: Q_strstr should return pointer to non-const data --- public/crtlib.c | 4 ++-- public/crtlib.h | 10 +++++----- 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/public/crtlib.c b/public/crtlib.c index 11efccc3..2cc3e4e9 100644 --- a/public/crtlib.c +++ b/public/crtlib.c @@ -409,7 +409,7 @@ const char* Q_timestamp( int format ) } #if !defined( HAVE_STRCASESTR ) -const char *Q_stristr( const char *string, const char *string2 ) +char *Q_stristr( const char *string, const char *string2 ) { int c; size_t len; @@ -431,7 +431,7 @@ const char *Q_stristr( const char *string, const char *string2 ) } else return NULL; } - return string; + return (char *)string; } #endif // !defined( HAVE_STRCASESTR ) diff --git a/public/crtlib.h b/public/crtlib.h index 0bcf9ce7..43a0c775 100644 --- a/public/crtlib.h +++ b/public/crtlib.h @@ -105,9 +105,9 @@ static inline int Q_strncmp( const char *s1, const char *s2, size_t n ) ( unlikely(!s2) ? 1 : strncmp( s1, s2, n )); } -static inline const char *Q_strstr( const char *s1, const char *s2 ) +static inline char *Q_strstr( const char *s1, const char *s2 ) { - return unlikely( !s1 || !s2 ) ? NULL : strstr( s1, s2 ); + return unlikely( !s1 || !s2 ) ? NULL : (char*)strstr( s1, s2 ); } // libc extensions, be careful @@ -135,12 +135,12 @@ static inline int Q_strnicmp( const char *s1, const char *s2, size_t n ) #if XASH_WIN32 #define strcasestr stristr #endif -static inline const char *Q_stristr( const char *s1, const char *s2 ) +static inline char *Q_stristr( const char *s1, const char *s2 ) { - return unlikely( !s1 || !s2 ) ? NULL : strcasestr( s1, s2 ); + return unlikely( !s1 || !s2 ) ? NULL : (char *)strcasestr( s1, s2 ); } #else // defined( HAVE_STRCASESTR ) -const char *Q_stristr( const char *s1, const char *s2 ); +char *Q_stristr( const char *s1, const char *s2 ); #endif // defined( HAVE_STRCASESTR ) From 854bfb86730d94cb67160b62eacbf334c2a1f9d5 Mon Sep 17 00:00:00 2001 From: Alibek Omarov Date: Wed, 29 Jun 2022 04:00:35 +0300 Subject: [PATCH 073/300] wscript: woops, msvcrt don't actually have stristr --- wscript | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/wscript b/wscript index f4ffcb20..f87fc7ff 100644 --- a/wscript +++ b/wscript @@ -317,10 +317,7 @@ int main(void) { return 0; }''', conf.define('_FILE_OFFSET_BITS', 64) else: conf.undefine('_FILE_OFFSET_BITS') - if conf.env.DEST_OS == 'win32': - # msvcrt always has stristr - conf.define('HAVE_STRCASESTR', 1) - else: + if conf.env.DEST_OS != 'win32': strcasestr_frag = '''#include int main(int argc, char **argv) { strcasestr(argv[1], argv[2]); return 0; }''' From 3fe392b41f17b2a49fdd830ef2aa77e91adf205f Mon Sep 17 00:00:00 2001 From: Alibek Omarov Date: Wed, 29 Jun 2022 18:05:51 +0300 Subject: [PATCH 074/300] ref: fix processing indexed textures --- ref_gl/gl_image.c | 5 +++++ ref_soft/r_image.c | 5 +++++ 2 files changed, 10 insertions(+) diff --git a/ref_gl/gl_image.c b/ref_gl/gl_image.c index 1b5ed033..658b0240 100644 --- a/ref_gl/gl_image.c +++ b/ref_gl/gl_image.c @@ -1908,6 +1908,11 @@ void GL_ProcessTexture( int texnum, float gamma, int topColor, int bottomColor ) // all the operations makes over the image copy not an original pic = gEngfuncs.FS_CopyImage( image->original ); + + // we need to expand image into RGBA buffer + if( pic->type == PF_INDEXED_24 || pic->type == PF_INDEXED_32 ) + flags |= IMAGE_FORCE_RGBA; + gEngfuncs.Image_Process( &pic, topColor, bottomColor, flags, 0.0f ); GL_UploadTexture( image, pic ); diff --git a/ref_soft/r_image.c b/ref_soft/r_image.c index a374d401..247e2e1e 100644 --- a/ref_soft/r_image.c +++ b/ref_soft/r_image.c @@ -1186,6 +1186,11 @@ void GAME_EXPORT GL_ProcessTexture( int texnum, float gamma, int topColor, int b // all the operations makes over the image copy not an original pic = gEngfuncs.FS_CopyImage( image->original ); + + // we need to expand image into RGBA buffer + if( pic->type == PF_INDEXED_24 || pic->type == PF_INDEXED_32 ) + flags |= IMAGE_FORCE_RGBA; + gEngfuncs.Image_Process( &pic, topColor, bottomColor, flags, 0.0f ); GL_UploadTexture( image, pic ); From 95ed044fee9c450f880f6e5e89f95ffe2f8df350 Mon Sep 17 00:00:00 2001 From: Alibek Omarov Date: Wed, 29 Jun 2022 18:14:53 +0300 Subject: [PATCH 075/300] engine: common: disable cl_filterstuffcmd 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 108b3956..0e33ef31 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, "1", FCVAR_ARCHIVE | FCVAR_PRIVILEGED, "filter commands coming from server" ); +CVAR_DEFINE_AUTO( cl_filterstuffcmd, "0", FCVAR_ARCHIVE | FCVAR_PRIVILEGED, "filter commands coming from server" ); /* ============ From d46d62bf0325c52191377b76ff4abdbc179a761c Mon Sep 17 00:00:00 2001 From: Alibek Omarov Date: Wed, 29 Jun 2022 18:57:10 +0300 Subject: [PATCH 076/300] engine: client: drop loading plaque on second signon, remove servercount check --- engine/client/cl_main.c | 3 +-- engine/client/cl_scrn.c | 1 - engine/client/client.h | 3 --- 3 files changed, 1 insertion(+), 6 deletions(-) diff --git a/engine/client/cl_main.c b/engine/client/cl_main.c index 225172aa..46782aef 100644 --- a/engine/client/cl_main.c +++ b/engine/client/cl_main.c @@ -195,8 +195,6 @@ void CL_CheckClientState( void ) Netchan_ReportFlow( &cls.netchan ); Con_DPrintf( "client connected at %.2f sec\n", Sys_DoubleTime() - cls.timestart ); - if(( cls.demoplayback || cls.disable_servercount != cl.servercount ) && cl.video_prepped ) - SCR_EndLoadingPlaque(); // get rid of loading plaque } } @@ -234,6 +232,7 @@ void CL_SignonReply( void ) Mem_PrintStats(); break; case 2: + SCR_EndLoadingPlaque(); if( cl.proxy_redirect && !cls.spectator ) CL_Disconnect(); cl.proxy_redirect = false; diff --git a/engine/client/cl_scrn.c b/engine/client/cl_scrn.c index 62a42047..ee212d46 100644 --- a/engine/client/cl_scrn.c +++ b/engine/client/cl_scrn.c @@ -418,7 +418,6 @@ void SCR_BeginLoadingPlaque( qboolean is_background ) cls.draw_changelevel = !is_background; SCR_UpdateScreen(); cls.disable_screen = host.realtime; - cls.disable_servercount = cl.servercount; cl.background = is_background; // set right state before svc_serverdata is came if( !Host_IsDedicated() ) diff --git a/engine/client/client.h b/engine/client/client.h index 129caca6..07bb0c57 100644 --- a/engine/client/client.h +++ b/engine/client/client.h @@ -527,9 +527,6 @@ typedef struct float disable_screen; // showing loading plaque between levels // or changing rendering dlls // if time gets > 30 seconds ahead, break it - int disable_servercount; // when we receive a frame and cl.servercount - // > cls.disable_servercount, clear disable_screen - qboolean draw_changelevel; // draw changelevel image 'Loading...' keydest_t key_dest; From 64eb0a694d17f9a2f24c42827e802d3be7a1d1cb Mon Sep 17 00:00:00 2001 From: Alibek Omarov Date: Wed, 29 Jun 2022 18:58:01 +0300 Subject: [PATCH 077/300] engine: client: also drop loading plaque on toggleconsole, in case if it's stuck --- engine/client/console.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/engine/client/console.c b/engine/client/console.c index e53aafce..bae1a35d 100644 --- a/engine/client/console.c +++ b/engine/client/console.c @@ -283,6 +283,8 @@ void Con_ToggleConsole_f( void ) if( !host.allow_console || UI_CreditsActive( )) return; // disabled + SCR_EndLoadingPlaque(); + // show console only in game or by special call from menu if( cls.state != ca_active || cls.key_dest == key_menu ) return; From 3ec997721223b10616a0f1135f7166aee2cbf508 Mon Sep 17 00:00:00 2001 From: SNMetamorph <25657591+SNMetamorph@users.noreply.github.com> Date: Thu, 30 Jun 2022 03:34:28 +0400 Subject: [PATCH 078/300] wscript: added parallel builds compiler flag for MSVC --- scripts/waifulib/compiler_optimizations.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scripts/waifulib/compiler_optimizations.py b/scripts/waifulib/compiler_optimizations.py index 15b70e7a..77eb951a 100644 --- a/scripts/waifulib/compiler_optimizations.py +++ b/scripts/waifulib/compiler_optimizations.py @@ -51,7 +51,7 @@ LINKFLAGS = { CFLAGS = { 'common': { # disable thread-safe local static initialization for C++11 code, as it cause crashes on Windows XP - 'msvc': ['/D_USING_V110_SDK71_', '/FS', '/Zc:threadSafeInit-', '/MT'], + 'msvc': ['/D_USING_V110_SDK71_', '/FS', '/Zc:threadSafeInit-', '/MT', '/MP'], 'clang': ['-g', '-gdwarf-2', '-fvisibility=hidden', '-fno-threadsafe-statics'], 'gcc': ['-g', '-fvisibility=hidden'], 'owcc': ['-fno-short-enum', '-ffloat-store', '-g3'] From c4a25e0d704b6bac0c83dff530b18307adf2bd1b 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: Thu, 30 Jun 2022 16:00:52 +0600 Subject: [PATCH 079/300] Documentation: supported-mod-list: fix dead links Renamed domains: hl-lab.ru -> gamer-lab.com planetphilip.com -> runthinkshootlive.com --- Documentation/supported-mod-list.md | 28 ++++++++++++++-------------- 1 file changed, 14 insertions(+), 14 deletions(-) diff --git a/Documentation/supported-mod-list.md b/Documentation/supported-mod-list.md index a155f7ba..ea6345f1 100644 --- a/Documentation/supported-mod-list.md +++ b/Documentation/supported-mod-list.md @@ -46,7 +46,7 @@ For mappacks - place *.bsp files to **valve/maps** folder, *.wad files to **valv 16. [Betrayal](http://www.moddb.com/mods/betrayal) (this mod has inner bugs; set **gl_allow_mirrors** to **0** to prevent drops of game's perfomance in some areas) 17. [Between Two Worlds](https://www.runthinkshootlive.com/posts/between-two-worlds/) 18. [Black Mesa Energy Testing Chamber](http://twhl.info/competitions.php?results=17) -19. [Black Mesa Sideline](http://www.isolated-design.de/half-life-mods/black-mesa-sideline/) (set **gl_allow_mirrors** to **0** to prevent drops of game's perfomance in some areas) +19. [Black Mesa Sideline](https://www.moddb.com/mods/black-mesa-sideline) (set **gl_allow_mirrors** to **0** to prevent drops of game's perfomance in some areas) 20. [BlackMesa 2007( Black Mesa Missions 2007 )](http://www.moddb.com/addons/blackmesa-2007) 21. [Blood and Bones](https://www.runthinkshootlive.com/posts/blood-and-bones/) 22. Boom (Huknenn) v1.0 & [HL Boom: Gold Edition v1.1](http://www.moddb.com/mods/boom) @@ -103,8 +103,8 @@ For mappacks - place *.bsp files to **valve/maps** folder, *.wad files to **valv 72. [Freeman](https://www.runthinkshootlive.com/posts/freeman/) 73. [Freeman's Escape](http://www.moddb.com/mods/freeman-escape-french) (2 maps by *JiggZ*) 74. [Freeman's Fight](https://www.runthinkshootlive.com/posts/freemans-fight/) -75. [Freeman's Return](http://jpmaps.wz.cz/epizody.htm) -76. [Freeman's Return 2](http://jpmaps.wz.cz/epizody.htm) +75. [Freeman's Return](https://www.moddb.com/games/half-life/addons/freemans-return-v12) +76. [Freeman's Return 2](https://www.moddb.com/games/half-life/addons/freemans-return-2-v11) 77. [Freeman's Revenge](https://www.runthinkshootlive.com/posts/freemans-revenge/) 78. [Freeman's Tomb( The Crypt )](http://twhl.info/vault.php?map=3787) 79. [G-Invasion v2.5](http://www.moddb.com/mods/g-man-invasion) aka G-Man Invasion (there is an inner crash bug on final titles: some text lines are too long and cannot be properly displayed) @@ -129,7 +129,7 @@ For mappacks - place *.bsp files to **valve/maps** folder, *.wad files to **valv 96. [Haunted](https://www.runthinkshootlive.com/posts/haunted/) (set **sv_validate_changelevel** to **0** to avoid a problem with level change between maps **pagan7** and **pagan8**) 97. [Hazardous Materials: Episode 1 & 2](http://www.moddb.com/mods/half-life-hazardous-materials) 98. [Hazardous-Course 2](http://www.moddb.com/mods/hazardous-course-2) -99. [Help Wanted](http://maps.mrsucko.org/work/) +99. [Help Wanted](https://www.moddb.com/games/half-life/addons/help-wanted) 100. [Hidden Evil v1.01](https://www.runthinkshootlive.com/posts/hidden-evil/) 101. [High Speed](http://www.moddb.com/games/half-life/addons/high-speed-english-version) 102. [hlife_hotdog_compo26](http://twhl.info/vault.php?map=5181) @@ -155,7 +155,7 @@ For mappacks - place *.bsp files to **valve/maps** folder, *.wad files to **valv 122. [Mario Keys](http://www.moddb.com/mods/mario-keys) 123. [McBeth](https://www.runthinkshootlive.com/posts/mcbeth/) 124. [Medieval World](http://www.moddb.com/mods/medieval-world) -125. [Mel Soaring 2: Star Rancor](http://www.etherealhell.com/etherealhell/index.php/my-levels/half-life) +125. [Mel Soaring 2: Star Rancor](https://www.moddb.com/addons/mel-soaring-2-star-rancor) 126. [MINIMICUS](http://twhl.info/vault.php?map=163) 127. [Mission Failed](http://www.moddb.com/mods/mission-failed) 128. [Mission MC Poker](http://www.moddb.com/mods/half-life-mission-mc-poker) @@ -176,7 +176,7 @@ For mappacks - place *.bsp files to **valve/maps** folder, *.wad files to **valv 143. [Operation: Nova](http://www.moddb.com/mods/half-life-operation-nova) (there is an inner bug with an M60 machinegun on a map with Osprey: it can be activated and used if you are staying in front of it, not behind it) 144. [Optimum Fear](https://www.fileplanet.com/7472/0/fileinfo/Optimum-Fear) 145. [Outrun](http://www.moddb.com/mods/outrun) -146. [Outwards (Day One)](http://www.visgi.info/maps/) +146. [Outwards (Day One)](http://www.visgi.info/maps/) (link dead!) 147. [Overhaul Pack](http://www.moddb.com/mods/half-life-overhaul-pack) (Just HD pack for Half-Life) 148. [P.I.Z.D.E.C.](https://www.runthinkshootlive.com/posts/pizdec/) 149. [pagoda](http://www.moddb.com/mods/pagoda1) @@ -184,7 +184,7 @@ For mappacks - place *.bsp files to **valve/maps** folder, *.wad files to **valv 151. [Phobos IV](http://www.moddb.com/mods/phobos-iv) 152. [Pimp My Car](https://www.runthinkshootlive.com/posts/pimp-my-car/) (there is an inner issue with incorrect responses of buttons on combination locks) 153. [Point Blank – Stackdeath Complex](https://www.runthinkshootlive.com/posts/half-life-point-blank-stackdeath-complex/) -154. [Prisoner Escaped](http://four0four.org/downloads/maps/) +154. [Prisoner Escaped](https://www.moddb.com/mods/prisoner-escaped-1-2) 155. [Prisoner of Event](http://wp.vondur.net/?page_id=40) 156. [Prisoner of War](https://www.runthinkshootlive.com/posts/prisoner-of-war/) 157. [Project Quantum Leap](http://www.moddb.com/mods/project-quantum-leap) @@ -469,7 +469,7 @@ Mods: 80. [Extinct Lifeform Hunt](https://www.fileplanet.com/13501/10000/fileinfo/Extinct-Lifeform-Hunt) 81. [Facility](http://twhl.info/vault.php?map=5482) 82. [Facility Escape](http://twhl.info/vault.php?map=3673) -83. [Fallout](http://www.thewall.de/forum/thread/hl1-sp-48h-mapping-contest/64975.4.html) (map by *simb*) +83. [Fallout](http://www.thewall.de/forum/thread/hl1-sp-48h-mapping-contest/64975.4.html) (map by *simb*) (link dead!) 84. [Final Assault](http://twhl.info/vault.php?map=4500) 85. [Flat](http://hl.loess.ru/?mods=&search=Flat) 86. [Freeman's Allegiance](https://www.runthinkshootlive.com/posts/freemans-allegiance/) @@ -499,7 +499,7 @@ Mods: 110. [Hostage](https://www.runthinkshootlive.com/posts/hostage-2/) 111. [House](http://www.artpeter.net/Data/HalfLife/Hl.php) 112. [Impulse 101 Fun - The Train](http://web.archive.org/web/20070305075816/http://www.twhl.co.za/mapvault/2817.zip) (map from *TWHL* by *Archie* aka *The Hunter*) -113. [In America](http://www.lambda-force.org/load/half_life/karty/half_life_in_america/18-1-0-476) +113. [In America](http://www.lambda-force.org/load/half_life/karty/half_life_in_america/18-1-0-476) (link dead!) 114. [In the Kitchen](http://twhl.info/vault.php?map=4314) 115. [Infiltration](https://www.fileplanet.com/7467/0/fileinfo/Infiltration) 116. [Interactivity & Lots of Entities](http://twhl.info/vault.php?map=5744) (link dead!) @@ -668,7 +668,7 @@ Mods: 279. [X-treme Violence](http://www.moddb.com/mods/x-treme-violence) 280. [XargoL's Entry for Vassy's Compo](http://twhl.info/vault.php?map=769) 281. [Xen Again](https://www.fileplanet.com/9132/0/fileinfo/XEN-AGAIN) -282. [Xen World](http://www.lambda-force.org/load/half_life/karty/half_life_xen_world/18-1-0-481) +282. [Xen World](http://www.lambda-force.org/load/half_life/karty/half_life_xen_world/18-1-0-481) (link dead!) 283. [XUnil](https://www.fileplanet.com/7883/0/fileinfo/XUNIL) 284. [Zeeba-G's TWHL Compo 26 Entry](http://twhl.info/competitions.php?results=26) 285. [Zombies!](https://gamebanana.com/maps/160812) @@ -694,7 +694,7 @@ For Linux and OS X you must download crossbuild from [here](http://www.moddb.com 2. [Big Scientists](http://www.moddb.com/mods/big-scientists) 3. [Black Silla Assault DEMO v1.2](http://www.moddb.com/mods/black-silla-assault) 4. [Blbej Den](http://www.moddb.com/mods/blbej-den) -5. [Cold Experiment v1](http://jpmaps.wz.cz/epizody.htm) (you will need some files from SoHL to play it properly; download SoHL 1.2, extract files and copy following folders into **coldexv1** folder: cl_dlls, dlls, models, sprites) +5. [Cold Experiment v1](https://www.moddb.com/games/half-life/addons/cold-experiment) (you will need some files from SoHL to play it properly; download SoHL 1.2, extract files and copy following folders into **coldexv1** folder: cl_dlls, dlls, models, sprites) 6. [Dead Sector v1.0a](http://www.moddb.com/mods/dead-sector) 7. [Death Is Dead](http://www.moddb.com/games/half-life/addons/death-is-dead) (there is a fog-related inner bug at the first map) 8. [Escape from Black Mesa Alpha](http://www.moddb.com/mods/escape-from-black-mesa) @@ -712,7 +712,7 @@ For Linux and OS X you must download crossbuild from [here](http://www.moddb.com 20. [Space Prisoner v1.1](http://www.moddb.com/games/half-life/addons/space-prisoner-v11) (after installing of the mod open **liblist.gam** or **gameinfo.txt** file in the mod's folder and correct the line **gamedll "..\prison\dlls\spirit.dll"** for **gamedll "dlls\spirit.dll"**, otherwise you'll not be able to start a game; there is also a scripting error on a third map of the mod, so you'll be forced to use **noclip** to pass around bugged place) 21. [Terrorist Attack 2](http://terroristattack.wz.cz/mody.html) (link dead!) 22. [Timeline III: The Heart of Darkness](http://www.moddb.com/mods/timeline-series) -23. [Underground 2 Demo](http://www.isolated-design.de/half-life-mods/underground-2/) +23. [Underground 2 Demo](https://www.moddb.com/games/half-life/addons/underground-2-demo) ## Mods which based on modified Spirit of Half-Life 1.2 or older(Partially playable with SoHL 1.2 libraries or not playable at all) 1. [Black Death](http://www.moddb.com/mods/half-life-black-death) @@ -825,10 +825,10 @@ Mods: 3. [Bomb Squad](http://www.snarkpit.net/index.php?s=maps&map=3471) 4. [Corruption](http://www.snarkpit.net/index.php?s=maps&map=3154) 5. [Critical Mass](http://www.moddb.com/mods/half-life-critical-mass) [C3M1 Build 1 Demo](https://www.fileplanet.com/125746/120000/fileinfo/C3M1-Build-1) -6. [EPRST!](http://www.planetphillip.com/posts/ep-rst-opposing-force/) (link dead!) +6. [EPRST!](https://www.runthinkshootlive.com/posts/ep-rst/) 7. [Firing Range](https://www.runthinkshootlive.com/posts/firing-range/) 8. [Friendly Fire](https://www.runthinkshootlive.com/posts/friendly-fire/) -9. [Guitar Star Pre-Alpha](http://hl-lab.ru/eng/mods_goldsrc/Guitar_Star_(Prealpha)) (link dead!) +9. [Guitar Star Pre-Alpha](http://gamer-lab.com/rus/mods_goldsrc/Guitar_Star_(Prealpha)) 10. [J2000](https://www.runthinkshootlive.com/posts/j2000/) 11. [Killing House for OF](https://www.runthinkshootlive.com/posts/scientist-rescue-aka-cqb/) 12. [Klabautermann](https://www.runthinkshootlive.com/posts/klabautermann/) (though the map has some inner issues, it can be properly finished, just don't let the welder soldier die and don't press the fifth button until you mount a special gun in its' place) From 8211acf4e05f9a29da4f8f6f6395fd42e8b3e9f7 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: Thu, 30 Jun 2022 17:04:41 +0600 Subject: [PATCH 080/300] Documentation: supported-mod-list: fix link for Lost in Black Mesa --- Documentation/supported-mod-list.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Documentation/supported-mod-list.md b/Documentation/supported-mod-list.md index ea6345f1..1719b520 100644 --- a/Documentation/supported-mod-list.md +++ b/Documentation/supported-mod-list.md @@ -379,7 +379,7 @@ Mods: 5. [Half-Life Baby v1.4](http://www.moddb.com/games/half-life/addons/half-life-baby) (it's an unfinished but playable mod; after installing of the mod open **liblist.gam** or **gameinfo.txt** file in the mod's folder and correct the line **gamedll "..\hlbaby\dlls\hl.dll"** for **gamedll "dlls\hl.dll"**, otherwise you'll not be able to start a game) 6. [Half-Secret](http://www.moddb.com/mods/half-secret) (this mod has custom **weapon_snark** code) 7. [Induction](http://www.moddb.com/mods/half-life-induction) (this mod has new item - **item_flashlight**) -8. [Lost in Black Mesa(first version without HLFX)](http://half-life.ru/forum/showthread.php?threadid=13959) +8. [Lost in Black Mesa(first version without HLFX)](https://drive.google.com/file/d/1bEnm_AxJs-ly8hTEZIQBw_v2BsXdSiGa/view?usp=sharing) 9. [Soldier](http://www.moddb.com/mods/half-life-soldier) 10. [Solo Operations](http://www.moddb.com/mods/solo-operations) 11. [The Blood v1.1](http://hl.loess.ru/?mods=&search=The+Blood) (there are some inner bugs in the mod, but they don't interfere with a game progress) From cbcb90638b5488ffee45a3a4593905421710fc7d Mon Sep 17 00:00:00 2001 From: Andrey Akhmichin <15944199+nekonomicon@users.noreply.github.com> Date: Tue, 21 Jun 2022 11:32:05 +0500 Subject: [PATCH 081/300] utils: mdldec: try to create user defined destination directory. --- utils/mdldec/mdldec.c | 6 +++--- utils/mdldec/utils.c | 16 ++++++++-------- utils/mdldec/utils.h | 4 ++-- 3 files changed, 13 insertions(+), 13 deletions(-) diff --git a/utils/mdldec/mdldec.c b/utils/mdldec/mdldec.c index d902d400..c137be93 100644 --- a/utils/mdldec/mdldec.c +++ b/utils/mdldec/mdldec.c @@ -164,9 +164,9 @@ static qboolean LoadMDL( const char *modelname ) if( destdir[0] != '\0' ) { - if( !IsFileExists( destdir ) ) + if( !MakeDirectory( destdir ) ) { - fprintf( stderr, "ERROR: Couldn't find directory %s\n", destdir ); + fprintf( stderr, "ERROR: Couldn't create directory %s\n", destdir ); return false; } @@ -175,7 +175,7 @@ static qboolean LoadMDL( const char *modelname ) else COM_ExtractFilePath( modelname, destdir ); - len -= 4; // path length without extension + len -= ( sizeof( ".mdl" ) - 1 ); // path length without extension if( !model_hdr->numtextures ) { diff --git a/utils/mdldec/utils.c b/utils/mdldec/utils.c index 739390d9..3e1014cc 100644 --- a/utils/mdldec/utils.c +++ b/utils/mdldec/utils.c @@ -18,22 +18,22 @@ GNU General Public License for more details. #include #include #include "xash3d_types.h" +#include "port.h" #include "crtlib.h" #include "utils.h" /* ============ -IsFileExists +MakeDirectory ============ */ -qboolean IsFileExists( const char *filename ) +qboolean MakeDirectory( const char *path ) { struct stat st; - int ret; - ret = stat( filename, &st ); - - if( ret == -1 ) + if( -1 == _mkdir( path ) + && ( -1 == stat( path, &st ) + || !S_ISDIR(st.st_mode ) ) ) return false; return true; @@ -44,7 +44,7 @@ qboolean IsFileExists( const char *filename ) GetFileSize ============ */ -off_t GetFileSize( FILE *fp ) +off_t GetSizeOfFile( FILE *fp ) { struct stat st; int fd; @@ -71,7 +71,7 @@ byte *LoadFile( const char *filename ) if( !fp ) return NULL; - size = GetFileSize( fp ); + size = GetSizeOfFile( fp ); buf = malloc( size ); diff --git a/utils/mdldec/utils.h b/utils/mdldec/utils.h index b2e9958f..08d07202 100644 --- a/utils/mdldec/utils.h +++ b/utils/mdldec/utils.h @@ -16,8 +16,8 @@ GNU General Public License for more details. #ifndef UTILS_H #define UTILS_H -qboolean IsFileExists( const char *filename ); -off_t GetFileSize( FILE *fp ); +qboolean MakeDirectory( const char *path ); +off_t GetSizeOfFile( FILE *fp ); byte *LoadFile( const char *filename ); #endif // UTILS_H From d562642e269cb2f16b717c81851cbf6c1bca72b4 Mon Sep 17 00:00:00 2001 From: Alibek Omarov Date: Thu, 30 Jun 2022 18:32:40 +0300 Subject: [PATCH 082/300] utils: mdldec: fix build on Windows, use GetFileAttributes instead of stat here --- utils/mdldec/utils.c | 23 +++++++++++++++++++---- 1 file changed, 19 insertions(+), 4 deletions(-) diff --git a/utils/mdldec/utils.c b/utils/mdldec/utils.c index 3e1014cc..d43fe266 100644 --- a/utils/mdldec/utils.c +++ b/utils/mdldec/utils.c @@ -17,6 +17,7 @@ GNU General Public License for more details. #include #include #include +#include #include "xash3d_types.h" #include "port.h" #include "crtlib.h" @@ -29,12 +30,26 @@ MakeDirectory */ qboolean MakeDirectory( const char *path ) { - struct stat st; + if( -1 == _mkdir( path )) + { + if( errno == EEXIST ) + { + // TODO: when filesystem library will be ready + // use FS_SysFolderExists here or replace this whole function + // with FS_CreatePath +#if XASH_WIN32 + DWORD dwFlags = GetFileAttributes( path ); - if( -1 == _mkdir( path ) - && ( -1 == stat( path, &st ) - || !S_ISDIR(st.st_mode ) ) ) + return ( dwFlags != -1 ) && ( dwFlags & FILE_ATTRIBUTE_DIRECTORY ); +#else + struct stat buf; + + if( !stat( path, &buf )) + return S_ISDIR( buf.st_mode ); +#endif + } return false; + } return true; } From 500dd0a82b53c46c4616f5fa78ef12d6d7ebc6b1 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: Thu, 30 Jun 2022 22:13:24 +0600 Subject: [PATCH 083/300] Documentation: supported-mod-list: fix dead links Restored some mods at Google Drive --- Documentation/supported-mod-list.md | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/Documentation/supported-mod-list.md b/Documentation/supported-mod-list.md index 1719b520..54f35a3f 100644 --- a/Documentation/supported-mod-list.md +++ b/Documentation/supported-mod-list.md @@ -176,7 +176,7 @@ For mappacks - place *.bsp files to **valve/maps** folder, *.wad files to **valv 143. [Operation: Nova](http://www.moddb.com/mods/half-life-operation-nova) (there is an inner bug with an M60 machinegun on a map with Osprey: it can be activated and used if you are staying in front of it, not behind it) 144. [Optimum Fear](https://www.fileplanet.com/7472/0/fileinfo/Optimum-Fear) 145. [Outrun](http://www.moddb.com/mods/outrun) -146. [Outwards (Day One)](http://www.visgi.info/maps/) (link dead!) +146. [Outwards (Day One)](https://drive.google.com/file/d/1mjQ8wUazYbwTq9Ocg0Vs0raq4avbTRne/view?usp=sharing) 147. [Overhaul Pack](http://www.moddb.com/mods/half-life-overhaul-pack) (Just HD pack for Half-Life) 148. [P.I.Z.D.E.C.](https://www.runthinkshootlive.com/posts/pizdec/) 149. [pagoda](http://www.moddb.com/mods/pagoda1) @@ -282,7 +282,7 @@ For mappacks - place *.bsp files to **valve/maps** folder, *.wad files to **valv 6. [Catacombs: Part 1](https://www.runthinkshootlive.com/posts/catacombs/) 7. [Cro-man's Office Mappack](http://twhl.info/vault.php?map=5547) 8. [Half-Life Episode Two Demo Alpha v1.0](http://hl.loess.ru/?mod=451) -9. [Hazardous-Course (first version of Hazardous-Course 2)](http://www.richmans-maps.ch.vu/) (link dead!) +9. [Hazardous-Course (first version of Hazardous-Course 2)](https://drive.google.com/file/d/16djJZSpNWKyScSxSyMqX-u_S-2i400rl/view?usp=sharing) 10. [High Tech v0.2](http://hl.loess.ru/?mod=499) 11. [HL Shadows, Part 1](https://www.fileplanet.com/7466/0/fileinfo/'HL-Shadows',-Part-1) 12. [HLNewEnd](http://twhl.info/vault.php?map=2275) @@ -305,11 +305,11 @@ For mappacks - place *.bsp files to **valve/maps** folder, *.wad files to **valv 29. [Shortcut v1.0 Beta](https://www.runthinkshootlive.com/posts/shortcut/) 30. [Stoka](https://www.ceskemody.cz/mapy.php?lng=1&clanek=222&razeni_hra=1&pn=stoka) 31. [Striker's Compo 26](http://twhl.info/vault.php?map=5190) (buggy) -32. [Technology Test 2](http://www.richmans-maps.ch.vu/) (link dead!) +32. [Technology Test 2](https://drive.google.com/file/d/1LfldzsMIN5j07bgtNkfY5v0Xl0S9de_G/view?usp=sharing) 33. [The Gate Playable Demo](https://www.fileplanet.com/116347/110000/fileinfo/The-Gate-Playable-Demo) 34. [They are Back](https://www.runthinkshootlive.com/posts/they-are-back/) 35. [Tiefseelabor( Deep Sea Laboratory )](https://www.runthinkshootlive.com/posts/deep-sea-laboratory/) -36. [Time-Shift](http://www.richmans-maps.ch.vu/) (link dead!) +36. [Time-Shift](https://drive.google.com/file/d/1pK1s-2SIlSMQHVRPMLdC_94wTa__8_DX/view?usp=sharing) 37. [Train Single Beta](https://cs-mapping.com.ua/forum/showthread.php?t=36394) (Remove **gfx.wad** from **TrainSingle** folder) 38. [WAR: The Killer Beta 0.1](http://www.moddb.com/mods/war) 39. [White Force Beta( Residual Point prototype )](http://www.moddb.com/mods/hl-residual-point/downloads/white-force-beta-2002) @@ -440,7 +440,7 @@ Mods: 51. [Data-base](https://www.runthinkshootlive.com/posts/database/) 52. [de_dust2_azabetfeN](https://cs-mapping.com.ua/forum/showthread.php?t=36394) (remove **cl_dlls** & **dlls** folders from inside of mod's directory before you start the game) 53. [De-railed](http://twhl.info/competitions.php?results=7) -54. [Dead Shift Beta](http://www.moddb.com/mods/dead-shift) - [Demo 1](https://www.gamewatcher.com/mods/half-life-mod/dead-shift-1-0-beta) & [Demo 2](https://www.gamefront.com/files/13532512) (3rd link dead!) +54. [Dead Shift Beta](http://www.moddb.com/mods/dead-shift) - [Demo 1](https://www.gamewatcher.com/mods/half-life-mod/dead-shift-1-0-beta) & [Demo 2](https://drive.google.com/file/d/1uHvr8xONogTTNOy-8X6G4ehFV6Eawvbq/view?usp=sharing) 55. [Deep](http://twhl.info/vault.php?map=1669) 56. [Desert Attack](http://fyzzer.narod.ru/index.html) 57. [Desert Combat Demo](https://www.fileplanet.com/190487/190000/fileinfo/Half-Life---Desert-Combat-Mod) (despite a big file size there's only one small unfinished map) @@ -498,8 +498,8 @@ Mods: 109. [Hospital](https://gamebanana.com/maps/167446) (you need to edit **liblist.gam** file in the mod's folder - delete *gamedll & type* strings from it before you start to play) 110. [Hostage](https://www.runthinkshootlive.com/posts/hostage-2/) 111. [House](http://www.artpeter.net/Data/HalfLife/Hl.php) -112. [Impulse 101 Fun - The Train](http://web.archive.org/web/20070305075816/http://www.twhl.co.za/mapvault/2817.zip) (map from *TWHL* by *Archie* aka *The Hunter*) -113. [In America](http://www.lambda-force.org/load/half_life/karty/half_life_in_america/18-1-0-476) (link dead!) +112. [Impulse 101 Fun - The Train](https://drive.google.com/file/d/1jAuMCCmREH0mfAEAscqaG8FQGa2uNknb/view?usp=sharing) (map from *TWHL* by *Archie* aka *The Hunter*) +113. [In America](https://drive.google.com/file/d/1gOC8zfnUvBxRy8Rr8juNiUNbLYjoZpnn/view?usp=sharing) 114. [In the Kitchen](http://twhl.info/vault.php?map=4314) 115. [Infiltration](https://www.fileplanet.com/7467/0/fileinfo/Infiltration) 116. [Interactivity & Lots of Entities](http://twhl.info/vault.php?map=5744) (link dead!) @@ -710,7 +710,7 @@ For Linux and OS X you must download crossbuild from [here](http://www.moddb.com 18. [Santa's Revenge](http://twhl.info/vault.php?map=4332) (set **fps_max** to **60** to avoid an inner problem of the last map with final scripted sequence, otherwise the mod can not be finished properly) 19. [Sector 6](https://www.moddb.com/mods/sector-6/) 20. [Space Prisoner v1.1](http://www.moddb.com/games/half-life/addons/space-prisoner-v11) (after installing of the mod open **liblist.gam** or **gameinfo.txt** file in the mod's folder and correct the line **gamedll "..\prison\dlls\spirit.dll"** for **gamedll "dlls\spirit.dll"**, otherwise you'll not be able to start a game; there is also a scripting error on a third map of the mod, so you'll be forced to use **noclip** to pass around bugged place) -21. [Terrorist Attack 2](http://terroristattack.wz.cz/mody.html) (link dead!) +21. [Terrorist Attack 2](https://www.moddb.com/games/half-life/addons/terrorist-attack-2) 22. [Timeline III: The Heart of Darkness](http://www.moddb.com/mods/timeline-series) 23. [Underground 2 Demo](https://www.moddb.com/games/half-life/addons/underground-2-demo) From 6e031b518a41e413da21831b9824682556ab7173 Mon Sep 17 00:00:00 2001 From: Alibek Omarov Date: Fri, 1 Jul 2022 03:40:24 +0300 Subject: [PATCH 084/300] engine: client: increase limit of client sprites, lower part of it can be used only for HUD sprites higher part used for client sprites map overview sprites are loaded as normal models thus we have equal internal engine and hud sprite indices and it fixes compatibility issues for mods like Half-Rats Parasomnia --- common/com_model.h | 2 +- engine/client/cl_game.c | 31 +++++++++++++++++++++---------- engine/client/client.h | 2 +- 3 files changed, 23 insertions(+), 12 deletions(-) diff --git a/common/com_model.h b/common/com_model.h index 9f8ceda8..36f2e936 100644 --- a/common/com_model.h +++ b/common/com_model.h @@ -530,7 +530,7 @@ typedef struct #define MAX_DEMOS 32 #define MAX_MOVIES 8 #define MAX_CDTRACKS 32 -#define MAX_CLIENT_SPRITES 256 // SpriteTextures +#define MAX_CLIENT_SPRITES 512 // SpriteTextures (0-256 hud, 256-512 client) #define MAX_EFRAGS 8192 // Arcane Dimensions required #define MAX_REQUESTS 64 diff --git a/engine/client/cl_game.c b/engine/client/cl_game.c index 4f5fc5b3..b093c82c 100644 --- a/engine/client/cl_game.c +++ b/engine/client/cl_game.c @@ -1235,6 +1235,10 @@ static model_t *CL_LoadSpriteModel( const char *filename, uint type, uint texFla model_t *mod; int i; + // use high indices for client sprites + // for GoldSrc bug-compatibility + const int start = type != SPR_HUDSPRITE ? MAX_CLIENT_SPRITES / 2 : 0; + if( !COM_CheckString( filename )) { Con_Reportf( S_ERROR "CL_LoadSpriteModel: bad name!\n" ); @@ -1244,8 +1248,7 @@ static model_t *CL_LoadSpriteModel( const char *filename, uint type, uint texFla Q_strncpy( name, filename, sizeof( name )); COM_FixSlashes( name ); - // slot 0 isn't used - for( i = 1, mod = clgame.sprites; i < MAX_CLIENT_SPRITES; i++, mod++ ) + for( i = 0, mod = clgame.sprites + start; i < MAX_CLIENT_SPRITES / 2; i++, mod++ ) { if( !Q_stricmp( mod->name, name )) { @@ -1262,12 +1265,12 @@ static model_t *CL_LoadSpriteModel( const char *filename, uint type, uint texFla } // find a free model slot spot - for( i = 1, mod = clgame.sprites; i < MAX_CLIENT_SPRITES; i++, mod++ ) + for( i = 0, mod = clgame.sprites + start; i < MAX_CLIENT_SPRITES / 2; i++, mod++ ) if( !mod->name[0] ) break; // this is a valid spot - if( i == MAX_CLIENT_SPRITES ) + if( i == MAX_CLIENT_SPRITES / 2 ) { - Con_Printf( S_ERROR "MAX_CLIENT_SPRITES limit exceeded (%d)\n", MAX_CLIENT_SPRITES ); + Con_Printf( S_ERROR "MAX_CLIENT_SPRITES limit exceeded (%d)\n", MAX_CLIENT_SPRITES / 2 ); return NULL; } @@ -1308,7 +1311,7 @@ HSPRITE pfnSPR_LoadExt( const char *szPicName, uint texFlags ) if(( spr = CL_LoadSpriteModel( szPicName, SPR_CLIENT, texFlags )) == NULL ) return 0; - return (spr - clgame.sprites); // return index + return (spr - clgame.sprites) + 1; // return index } /* @@ -1324,7 +1327,7 @@ HSPRITE EXPORT pfnSPR_Load( const char *szPicName ) if(( spr = CL_LoadSpriteModel( szPicName, SPR_HUDSPRITE, 0 )) == NULL ) return 0; - return (spr - clgame.sprites); // return index + return (spr - clgame.sprites) + 1; // return index } /* @@ -1336,10 +1339,11 @@ CL_GetSpritePointer const model_t *CL_GetSpritePointer( HSPRITE hSprite ) { model_t *mod; + int index = hSprite - 1; - if( hSprite <= 0 || hSprite >= MAX_CLIENT_SPRITES ) + if( index < 0 || index >= MAX_CLIENT_SPRITES ) return NULL; // bad image - mod = &clgame.sprites[hSprite]; + mod = &clgame.sprites[index]; if( mod->needload == NL_NEEDS_LOADED ) { @@ -2666,7 +2670,14 @@ pfnLoadMapSprite */ model_t *pfnLoadMapSprite( const char *filename ) { - return CL_LoadSpriteModel( filename, SPR_MAPSPRITE, 0 ); + model_t *mod; + + mod = Mod_FindName( filename, false ); + + if( CL_LoadHudSprite( filename, mod, SPR_MAPSPRITE, 0 )) + return mod; + + return NULL; } /* diff --git a/engine/client/client.h b/engine/client/client.h index 07bb0c57..c757245a 100644 --- a/engine/client/client.h +++ b/engine/client/client.h @@ -465,7 +465,7 @@ typedef struct string cdtracks[MAX_CDTRACKS]; // 32 cd-tracks read from cdaudio.txt - model_t sprites[MAX_CLIENT_SPRITES]; // client spritetextures + model_t sprites[MAX_CLIENT_SPRITES*2]; // hud&client spritetexturesz int viewport[4]; // viewport sizes client_draw_t ds; // draw2d stuff (hud, weaponmenu etc) From 786c408f6e15d097fcca0cdcd9430c5c101cbbd0 Mon Sep 17 00:00:00 2001 From: Alibek Omarov Date: Fri, 1 Jul 2022 14:12:32 +0300 Subject: [PATCH 085/300] engine: client: fix sprites array size, it wasn't meant to be increased after previous commit --- engine/client/client.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/engine/client/client.h b/engine/client/client.h index c757245a..cc6683df 100644 --- a/engine/client/client.h +++ b/engine/client/client.h @@ -465,7 +465,7 @@ typedef struct string cdtracks[MAX_CDTRACKS]; // 32 cd-tracks read from cdaudio.txt - model_t sprites[MAX_CLIENT_SPRITES*2]; // hud&client spritetexturesz + model_t sprites[MAX_CLIENT_SPRITES]; // hud&client spritetexturesz int viewport[4]; // viewport sizes client_draw_t ds; // draw2d stuff (hud, weaponmenu etc) From fbdfed84bec30ce5cef28eff6fa16f9aac2774b9 Mon Sep 17 00:00:00 2001 From: SNMetamorph <25657591+SNMetamorph@users.noreply.github.com> Date: Sat, 9 Jul 2022 01:21:42 +0400 Subject: [PATCH 086/300] engine: platform: sdl: fixed bug with unhidable mouse cursor in center of screen --- engine/platform/sdl/in_sdl.c | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/engine/platform/sdl/in_sdl.c b/engine/platform/sdl/in_sdl.c index 129759bb..7f18c8ab 100644 --- a/engine/platform/sdl/in_sdl.c +++ b/engine/platform/sdl/in_sdl.c @@ -333,32 +333,33 @@ void Platform_SetCursorType( VGUI_DefaultCursor type ) break; } + host.mouse_visible = visible; + if( CVAR_TO_BOOL( touch_emulate )) return; #if SDL_VERSION_ATLEAST( 2, 0, 0 ) - if( visible && !host.mouse_visible ) + if( host.mouse_visible ) { SDL_SetCursor( cursors.cursors[type] ); SDL_ShowCursor( true ); Key_EnableTextInput( true, false ); } - else if( !visible && host.mouse_visible ) + else { SDL_ShowCursor( false ); Key_EnableTextInput( false, false ); } #else - if( visible && !host.mouse_visible ) + if( host.mouse_visible ) { SDL_ShowCursor( true ); } - else if( !visible && host.mouse_visible ) + else { SDL_ShowCursor( false ); } #endif - host.mouse_visible = visible; } /* From 8f9183099773ab6cdb44005e78315bdf807fb3f5 Mon Sep 17 00:00:00 2001 From: SNMetamorph <25657591+SNMetamorph@users.noreply.github.com> Date: Sun, 10 Jul 2022 02:04:13 +0400 Subject: [PATCH 087/300] engine: added changeport parameter for NET_Config --- engine/client/cl_game.c | 2 +- engine/client/cl_main.c | 8 ++++---- engine/common/net_ws.c | 4 ++-- engine/common/net_ws.h | 2 +- engine/server/sv_init.c | 2 +- engine/server/sv_main.c | 6 +++--- 6 files changed, 12 insertions(+), 12 deletions(-) diff --git a/engine/client/cl_game.c b/engine/client/cl_game.c index b093c82c..e516f423 100644 --- a/engine/client/cl_game.c +++ b/engine/client/cl_game.c @@ -3323,7 +3323,7 @@ NetAPI_InitNetworking */ void GAME_EXPORT NetAPI_InitNetworking( void ) { - NET_Config( true ); // allow remote + NET_Config( true, false ); // allow remote } /* diff --git a/engine/client/cl_main.c b/engine/client/cl_main.c index 46782aef..8c47c1f8 100644 --- a/engine/client/cl_main.c +++ b/engine/client/cl_main.c @@ -1298,7 +1298,7 @@ void CL_Rcon_f( void ) message[3] = (char)255; message[4] = 0; - NET_Config( true ); // allow remote + NET_Config( true, false ); // allow remote Q_strcat( message, "rcon " ); Q_strcat( message, rcon_client_password->string ); @@ -1561,7 +1561,7 @@ void CL_LocalServers_f( void ) netadr_t adr; Con_Printf( "Scanning for servers on the local network area...\n" ); - NET_Config( true ); // allow remote + NET_Config( true, true ); // allow remote // send a broadcast packet adr.type = NA_BROADCAST; @@ -1583,7 +1583,7 @@ void CL_InternetServers_f( void ) char *info = fullquery + sizeof( MS_SCAN_REQUEST ) - 1; const size_t remaining = sizeof( fullquery ) - sizeof( MS_SCAN_REQUEST ); - NET_Config( true ); // allow remote + NET_Config( true, true ); // allow remote Con_Printf( "Scanning for servers on the internet area...\n" ); Info_SetValueForKey( info, "gamedir", GI->gamefolder, remaining ); @@ -2152,7 +2152,7 @@ void CL_ConnectionlessPacket( netadr_t from, sizebuf_t *msg ) } else if( clgame.request_type == NET_REQUEST_GAMEUI ) { - NET_Config( true ); // allow remote + NET_Config( true, false ); // allow remote Netchan_OutOfBandPrint( NS_CLIENT, servadr, "info %i", PROTOCOL_VERSION ); } } diff --git a/engine/common/net_ws.c b/engine/common/net_ws.c index 8b491ba9..0727b6dd 100644 --- a/engine/common/net_ws.c +++ b/engine/common/net_ws.c @@ -1570,7 +1570,7 @@ NET_Config A single player game will only use the loopback code ==================== */ -void NET_Config( qboolean multiplayer ) +void NET_Config( qboolean multiplayer, qboolean changeport ) { static qboolean bFirst = true; static qboolean old_config; @@ -1756,7 +1756,7 @@ void NET_Shutdown( void ) NET_ClearLagData( true, true ); - NET_Config( false ); + NET_Config( false, false ); #if XASH_WIN32 WSACleanup(); #endif diff --git a/engine/common/net_ws.h b/engine/common/net_ws.h index ede8f0b1..75329383 100644 --- a/engine/common/net_ws.h +++ b/engine/common/net_ws.h @@ -51,7 +51,7 @@ void NET_Shutdown( void ); void NET_Sleep( int msec ); qboolean NET_IsActive( void ); qboolean NET_IsConfigured( void ); -void NET_Config( qboolean net_enable ); +void NET_Config( qboolean net_enable, qboolean changeport ); qboolean NET_IsLocalAddress( netadr_t adr ); const char *NET_AdrToString( const netadr_t a ); const char *NET_BaseAdrToString( const netadr_t a ); diff --git a/engine/server/sv_init.c b/engine/server/sv_init.c index b045fdfd..8f55a25e 100644 --- a/engine/server/sv_init.c +++ b/engine/server/sv_init.c @@ -761,7 +761,7 @@ void SV_SetupClients( void ) Con_Reportf( "%s alloced by server packet entities\n", Q_memprint( sizeof( entity_state_t ) * svs.num_client_entities )); // init network stuff - NET_Config(( svs.maxclients > 1 )); + NET_Config(( svs.maxclients > 1 ), true ); svgame.numEntities = svs.maxclients + 1; // clients + world ClearBits( sv_maxclients->flags, FCVAR_CHANGED ); } diff --git a/engine/server/sv_main.c b/engine/server/sv_main.c index 52f314c0..6d75b7e6 100644 --- a/engine/server/sv_main.c +++ b/engine/server/sv_main.c @@ -715,7 +715,7 @@ void Master_Add( void ) { netadr_t adr; - NET_Config( true ); // allow remote + NET_Config( true, false ); // allow remote if( !NET_StringToAdr( MASTERSERVER_ADR, &adr )) Con_Printf( "can't resolve adr: %s\n", MASTERSERVER_ADR ); @@ -758,7 +758,7 @@ void Master_Shutdown( void ) { netadr_t adr; - NET_Config( true ); // allow remote + NET_Config( true, false ); // allow remote if( !NET_StringToAdr( MASTERSERVER_ADR, &adr )) Con_Printf( "can't resolve addr: %s\n", MASTERSERVER_ADR ); @@ -1108,7 +1108,7 @@ void SV_Shutdown( const char *finalmsg ) if( public_server->value && svs.maxclients != 1 ) Master_Shutdown(); - NET_Config( false ); + NET_Config( false, false ); SV_UnloadProgs (); CL_Drop(); From 6891ed8064b4660507601ee83ebf1570e3cb200a Mon Sep 17 00:00:00 2001 From: SNMetamorph <25657591+SNMetamorph@users.noreply.github.com> Date: Sun, 10 Jul 2022 02:07:25 +0400 Subject: [PATCH 088/300] engine: common: net_ws: backported NAT bypass feature --- engine/common/net_ws.c | 54 +++++++++++++++++++++++++++++++++++------- 1 file changed, 45 insertions(+), 9 deletions(-) diff --git a/engine/common/net_ws.c b/engine/common/net_ws.c index 0727b6dd..675ffab0 100644 --- a/engine/common/net_ws.c +++ b/engine/common/net_ws.c @@ -1472,35 +1472,71 @@ static int NET_IPSocket( const char *net_interface, int port, qboolean multicast NET_OpenIP ==================== */ -static void NET_OpenIP( void ) +static void NET_OpenIP( qboolean change_port ) { - int port, sv_port = 0, cl_port = 0; + int port; + qboolean sv_nat = Cvar_VariableInteger("sv_nat"); + qboolean cl_nat = Cvar_VariableInteger("cl_nat"); + + if( change_port && ( FBitSet( net_hostport->flags, FCVAR_CHANGED ) || sv_nat )) + { + // reopen socket to set random port + if( NET_IsSocketValid( net.ip_sockets[NS_SERVER] )) + closesocket( net.ip_sockets[NS_SERVER] ); + + net.ip_sockets[NS_SERVER] = 0; + ClearBits( net_hostport->flags, FCVAR_CHANGED ); + } 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 + if( !port ) + { + if( sv_nat ) + port = PORT_ANY; + else + port = net_hostport->value; + + if( !port ) + port = PORT_SERVER; // forcing to default + } net.ip_sockets[NS_SERVER] = NET_IPSocket( net_ipname->string, port, 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 if( Host_IsDedicated() ) return; + if (change_port && ( FBitSet( net_clientport->flags, FCVAR_CHANGED ) || cl_nat )) + { + // reopen socket to set random port + if( NET_IsSocketValid(net.ip_sockets[NS_CLIENT] )) + closesocket( net.ip_sockets[NS_CLIENT] ); + + net.ip_sockets[NS_CLIENT] = 0; + ClearBits( net_clientport->flags, FCVAR_CHANGED ); + } + if( !NET_IsSocketValid( net.ip_sockets[NS_CLIENT] ) ) { port = net_ipclientport->value; - if( !port ) port = net_clientport->value; - if( !port ) port = PORT_ANY; // forcing to default + if( !port ) + { + if( cl_nat ) + port = PORT_ANY; + else + port = net_clientport->value; + + if( !port ) + port = PORT_ANY; // forcing to default + } net.ip_sockets[NS_CLIENT] = NET_IPSocket( net_ipname->string, port, 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; } } @@ -1586,7 +1622,7 @@ void NET_Config( qboolean multiplayer, qboolean changeport ) if( multiplayer ) { // open sockets - if( net.allow_ip ) NET_OpenIP(); + if( net.allow_ip ) NET_OpenIP( changeport ); // get our local address, if possible if( bFirst ) From ffe7114a47638a5b9ea689ab4169fb9de636a084 Mon Sep 17 00:00:00 2001 From: SNMetamorph <25657591+SNMetamorph@users.noreply.github.com> Date: Sun, 10 Jul 2022 02:07:42 +0400 Subject: [PATCH 089/300] engine: client: backported NAT bypass feature --- engine/client/cl_main.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/engine/client/cl_main.c b/engine/client/cl_main.c index 8c47c1f8..e8e9d5f5 100644 --- a/engine/client/cl_main.c +++ b/engine/client/cl_main.c @@ -70,6 +70,7 @@ convar_t *cl_upmax; convar_t *cl_lw; convar_t *cl_charset; convar_t *cl_trace_messages; +convar_t *cl_nat; convar_t *hud_utf8; convar_t *ui_renderworld; @@ -1252,7 +1253,7 @@ void CL_Connect_f( void ) // if running a local server, kill it and reissue if( SV_Active( )) Host_ShutdownServer(); - NET_Config( true ); // allow remote + NET_Config( true, !CVAR_TO_BOOL( cl_nat )); // allow remote Con_Printf( "server %s\n", server ); CL_Disconnect(); @@ -1588,7 +1589,7 @@ void CL_InternetServers_f( void ) 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 ); + 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_pending = true; @@ -2829,6 +2830,7 @@ void CL_InitLocal( void ) cl_updaterate = Cvar_Get( "cl_updaterate", "20", FCVAR_USERINFO|FCVAR_ARCHIVE, "refresh rate of server messages" ); cl_dlmax = Cvar_Get( "cl_dlmax", "0", FCVAR_USERINFO|FCVAR_ARCHIVE, "max allowed outcoming fragment size" ); cl_upmax = Cvar_Get( "cl_upmax", "1200", FCVAR_ARCHIVE, "max allowed incoming fragment size" ); + cl_nat = Cvar_Get( "cl_nat", "0", 0, "show servers running under NAT" ); rate = Cvar_Get( "rate", "3500", FCVAR_USERINFO|FCVAR_ARCHIVE|FCVAR_FILTERABLE, "player network rate" ); topcolor = Cvar_Get( "topcolor", "0", FCVAR_USERINFO|FCVAR_ARCHIVE, "player top color" ); bottomcolor = Cvar_Get( "bottomcolor", "0", FCVAR_USERINFO|FCVAR_ARCHIVE, "player bottom color" ); From cf84ad12f1ea0f2d5d4ed1472a56f3bec995d1b8 Mon Sep 17 00:00:00 2001 From: SNMetamorph <25657591+SNMetamorph@users.noreply.github.com> Date: Sun, 10 Jul 2022 02:07:59 +0400 Subject: [PATCH 090/300] engine: server: backported NAT bypass feature --- engine/server/sv_main.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/engine/server/sv_main.c b/engine/server/sv_main.c index 6d75b7e6..514a54d4 100644 --- a/engine/server/sv_main.c +++ b/engine/server/sv_main.c @@ -18,11 +18,12 @@ GNU General Public License for more details. #include "net_encode.h" #include "platform/platform.h" -#define HEARTBEAT_SECONDS 300.0f // 300 seconds +#define HEARTBEAT_SECONDS ((sv_nat.value > 0.0f) ? 60.0f : 300.0f) // 1 or 5 minutes // server cvars CVAR_DEFINE_AUTO( sv_lan, "0", 0, "server is a lan server ( no heartbeat, no authentication, no non-class C addresses, 9999.0 rate, etc." ); CVAR_DEFINE_AUTO( sv_lan_rate, "20000.0", 0, "rate for lan server" ); +CVAR_DEFINE_AUTO( sv_nat, "0", 0, "enable NAT bypass for this server" ); CVAR_DEFINE_AUTO( sv_aim, "1", FCVAR_ARCHIVE|FCVAR_SERVER, "auto aiming option" ); 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)" ); @@ -798,6 +799,7 @@ void SV_AddToMaster( netadr_t from, sizebuf_t *msg ) Info_SetValueForKey( s, "version", va( "%s", 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 NET_SendPacket( NS_SERVER, Q_strlen( s ), s, from ); } @@ -956,6 +958,7 @@ void SV_Init( void ) sv_hostmap = Cvar_Get( "hostmap", GI->startmap, 0, "keep name of last entered map" ); Cvar_RegisterVariable( &sv_password ); Cvar_RegisterVariable( &sv_lan ); + Cvar_RegisterVariable( &sv_nat ); Cvar_RegisterVariable( &violence_ablood ); Cvar_RegisterVariable( &violence_hblood ); Cvar_RegisterVariable( &violence_agibs ); From 36b0d47f5f113462d853ec4af279a7711ff52454 Mon Sep 17 00:00:00 2001 From: SNMetamorph <25657591+SNMetamorph@users.noreply.github.com> Date: Sun, 10 Jul 2022 02:23:24 +0400 Subject: [PATCH 091/300] engine: common: net_ws: fixed sockets reinitialization in NET_OpenIP --- 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 675ffab0..2dfcea62 100644 --- a/engine/common/net_ws.c +++ b/engine/common/net_ws.c @@ -1484,7 +1484,7 @@ static void NET_OpenIP( qboolean change_port ) if( NET_IsSocketValid( net.ip_sockets[NS_SERVER] )) closesocket( net.ip_sockets[NS_SERVER] ); - net.ip_sockets[NS_SERVER] = 0; + net.ip_sockets[NS_SERVER] = INVALID_SOCKET; ClearBits( net_hostport->flags, FCVAR_CHANGED ); } @@ -1516,7 +1516,7 @@ static void NET_OpenIP( qboolean change_port ) if( NET_IsSocketValid(net.ip_sockets[NS_CLIENT] )) closesocket( net.ip_sockets[NS_CLIENT] ); - net.ip_sockets[NS_CLIENT] = 0; + net.ip_sockets[NS_CLIENT] = INVALID_SOCKET; ClearBits( net_clientport->flags, FCVAR_CHANGED ); } From fce3959d150b3879049056a7dfb4674df9ebf1d6 Mon Sep 17 00:00:00 2001 From: SNMetamorph <25657591+SNMetamorph@users.noreply.github.com> Date: Sun, 10 Jul 2022 02:45:18 +0400 Subject: [PATCH 092/300] engine: server: added "c" command to SV_ConnectionlessPacket --- engine/server/sv_client.c | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/engine/server/sv_client.c b/engine/server/sv_client.c index e1215491..29b47c15 100644 --- a/engine/server/sv_client.c +++ b/engine/server/sv_client.c @@ -2266,6 +2266,13 @@ void SV_ConnectionlessPacket( netadr_t from, sizebuf_t *msg ) else if( !Q_strcmp( pcmd, "s" )) SV_AddToMaster( from, msg ); else if( !Q_strcmp( pcmd, "T" "Source" )) SV_TSourceEngineQuery( from ); else if( !Q_strcmp( pcmd, "i" )) NET_SendPacket( NS_SERVER, 5, "\xFF\xFF\xFF\xFFj", from ); // A2A_PING + else if (!Q_strcmp( pcmd, "c" )) + { + netadr_t to; + + if( NET_StringToAdr( Cmd_Argv( 1 ), &to )) + SV_Info( to, PROTOCOL_VERSION ); + } else if( svgame.dllFuncs.pfnConnectionlessPacket( &from, args, buf, &len )) { // user out of band message (must be handled in CL_ConnectionlessPacket) From d8724f0be4f35d7bc38032fe42caa67225946951 Mon Sep 17 00:00:00 2001 From: SNMetamorph <25657591+SNMetamorph@users.noreply.github.com> Date: Sun, 10 Jul 2022 03:34:14 +0400 Subject: [PATCH 093/300] engine: common: net_ws: disabled read-only flag for ip cvar --- engine/common/net_ws.c | 2 +- engine/server/sv_client.c | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/engine/common/net_ws.c b/engine/common/net_ws.c index 2dfcea62..999fb705 100644 --- a/engine/common/net_ws.c +++ b/engine/common/net_ws.c @@ -1731,7 +1731,7 @@ void NET_Init( void ) 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", FCVAR_READ_ONLY, "network ip address" ); + net_ipname = Cvar_Get( "ip", "localhost", 0, "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" ); diff --git a/engine/server/sv_client.c b/engine/server/sv_client.c index 29b47c15..93a7ae4a 100644 --- a/engine/server/sv_client.c +++ b/engine/server/sv_client.c @@ -2271,7 +2271,7 @@ void SV_ConnectionlessPacket( netadr_t from, sizebuf_t *msg ) netadr_t to; if( NET_StringToAdr( Cmd_Argv( 1 ), &to )) - SV_Info( to, PROTOCOL_VERSION ); + SV_Info( to ); } else if( svgame.dllFuncs.pfnConnectionlessPacket( &from, args, buf, &len )) { From ab43486ee05f3d601f540e313f0e784538ea82af Mon Sep 17 00:00:00 2001 From: a1batross Date: Mon, 11 Jul 2022 02:43:26 +0300 Subject: [PATCH 094/300] engine: client: don't segfault when client wasn't loaded --- engine/client/input.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/engine/client/input.c b/engine/client/input.c index 83c89a06..69c41ca8 100644 --- a/engine/client/input.c +++ b/engine/client/input.c @@ -276,7 +276,8 @@ void IN_ActivateMouse( void ) return; IN_CheckMouseState( true ); - clgame.dllFuncs.IN_ActivateMouse(); + if( clgame.dllFuncs.IN_ActivateMouse ) + clgame.dllFuncs.IN_ActivateMouse(); in_mouseactive = true; } @@ -293,7 +294,8 @@ void IN_DeactivateMouse( void ) return; IN_CheckMouseState( false ); - clgame.dllFuncs.IN_DeactivateMouse(); + if( clgame.dllFuncs.IN_DeactivateMouse ) + clgame.dllFuncs.IN_DeactivateMouse(); in_mouseactive = false; } From fc84cd2a0ac06f4850de333061cd1226380e8436 Mon Sep 17 00:00:00 2001 From: a1batross Date: Mon, 11 Jul 2022 02:45:40 +0300 Subject: [PATCH 095/300] engine: common: always show message box when we're in normal mode Only dedicated server is expected to throw errors to console --- engine/common/system.c | 16 ++++++---------- 1 file changed, 6 insertions(+), 10 deletions(-) diff --git a/engine/common/system.c b/engine/common/system.c index bfff6c66..18ba734f 100644 --- a/engine/common/system.c +++ b/engine/common/system.c @@ -421,9 +421,12 @@ void Sys_Error( const char *error, ... ) #if XASH_SDL == 2 if( host.hWnd ) SDL_HideWindow( host.hWnd ); #endif +#if XASH_WIN32 + Wcon_ShowConsole( false ); +#endif + MSGBOX( text ); } - - if( host_developer.value ) + else { #if XASH_WIN32 Wcon_ShowConsole( true ); @@ -432,14 +435,7 @@ void Sys_Error( const char *error, ... ) Sys_Print( text ); // print error message Sys_WaitForQuit(); } - else - { -#if XASH_WIN32 - Wcon_ShowConsole( false ); -#endif - MSGBOX( text ); - } - + Sys_Quit(); } From 5bf6f814ef1b632e64bac6822c5c5b1e589b7efd Mon Sep 17 00:00:00 2001 From: Andrey Akhmichin <15944199+nekonomicon@users.noreply.github.com> Date: Wed, 13 Jul 2022 05:59:21 +0500 Subject: [PATCH 096/300] Documentation: opensource-mods.md: update links. --- Documentation/opensource-mods.md | 89 ++++++++++++++++++-------------- 1 file changed, 49 insertions(+), 40 deletions(-) diff --git a/Documentation/opensource-mods.md b/Documentation/opensource-mods.md index 3f50bb6c..88676a33 100644 --- a/Documentation/opensource-mods.md +++ b/Documentation/opensource-mods.md @@ -17,7 +17,9 @@ Mirrored on github - https://github.com/JoelTroch/am_src_rebirth Mirrored on github - https://github.com/nekonomicon/BattleGrounds ## Bubblemod -Download page on official site - http://www.bubblemod.org/dl_default.php +Download page on official site - http://www.bubblemod.org/dl_default.php (dead link!) + +Mirrored on github - https://github.com/HLSources/BubbleMod ## Chicken Fortress Official github repository - https://github.com/CKFDevPowered/CKF3Alpha @@ -43,11 +45,13 @@ Official github repository - https://github.com/adslbarxatov/xash3d-for-ESHQ Available on GamerLab - http://gamer-lab.com/eng/code_mods_goldsrc/Half-Life_2D_(Flat-Life) ## Gang Wars -Mirrored on github - https://github.com/hammermaps/gw1.45src +Mirrored on github - https://github.com/nekonomicon/gw1.45src ## Go-mod Versions 2.0 and 3.0, available in mod archives on ModDB - https://www.moddb.com/mods/go-mod/downloads +Version 3.0, mirrored on github - https://github.com/nekonomicon/Go-mod30 + ## GT mod Available on GamerLab - http://gamer-lab.com/eng/code_mods_goldsrc/GT_mod_(Polnie_ishodniki) @@ -64,10 +68,10 @@ Available on ModDB - https://www.moddb.com/mods/half-life-echoes Available on ModDB - https://www.moddb.com/mods/half-life-expanded-arsenal ## Half-Life: Gravgun mod -Branch **gravgun** in hlsdk-xash3d - https://github.com/FWGS/hlsdk-xash3d/tree/gravgun +Branch **gravgun** in hlsdk-portable - https://github.com/FWGS/hlsdk-portable/tree/gravgun ## Half-Life: Invasion -official github repository - https://github.com/jlecorre/hlinvasion +Official github repository - https://github.com/jlecorre/hlinvasion ## Half-Life: Pong Mirrored on github - https://github.com/solidi/hl-mods/tree/master/pong @@ -106,7 +110,7 @@ Official github repository - https://github.com/desukuran/half-screwed Version 1.3 on ModDB - https://www.moddb.com/mods/headcrab-frenzy/downloads/headcrab-frenzy-13-beta-source-code ## Heart of Evil -Available on ModDB - https://www.moddb.com/mods/heart-of-evil/downloads/heart-of-evil-dlls-source-code +Mirrored on github - https://github.com/nekonomicon/HeartOfEvil ## Ingram Chillin' Mod Official SourceForge repository - https://sourceforge.net/projects/icm-hl/ @@ -169,7 +173,7 @@ Mirrored on github - https://github.com/ZXCmod/ZXCmod # Reimplementation ## Absolute Redemption -Branch **redempt** in hlsdk-xash3d - https://github.com/FWGS/hlsdk-xash3d/tree/redempt +Branch **redempt** in hlsdk-portable - https://github.com/FWGS/hlsdk-portable/tree/redempt ## Adrenaline Gamer OpenAG by YaLTeR - https://github.com/YaLTeR/OpenAG @@ -177,34 +181,34 @@ OpenAG by YaLTeR - https://github.com/YaLTeR/OpenAG ## Afraid of Monsters malortie's recreation - https://github.com/malortie/hl-aom -Branch **aom** in hlsdk-xash3d - https://github.com/FWGS/hlsdk-xash3d/tree/aom +Branch **aom** in hlsdk-portable - https://github.com/FWGS/hlsdk-portable/tree/aom ## Afraid of Monsters: Director's cut -Reverse-engineered code, branch **aomdc** in hlsdk-xash3d - https://github.com/FWGS/hlsdk-xash3d/tree/aomdc +Reverse-engineered code, branch **aomdc** in hlsdk-portable - https://github.com/FWGS/hlsdk-portable/tree/aomdc ## Azure Sheep malortie's recreation - https://github.com/malortie/hl-asheep -Reverse-engineered code, branch **asheep** in hlsdk-xash3d - https://github.com/FWGS/hlsdk-xash3d/tree/asheep +Reverse-engineered code, branch **asheep** in hlsdk-portable - https://github.com/FWGS/hlsdk-portable/tree/asheep ## Big Lolly malortie's recreation - https://github.com/malortie/hl-biglolly -Branch **biglolly** in hlsdk-xash3d - https://github.com/FWGS/hlsdk-xash3d/tree/biglolly +Branch **biglolly** in hlsdk-portable - https://github.com/FWGS/hlsdk-portable/tree/biglolly ## Black Ops malortie's recreation - https://github.com/malortie/hl-blackops -Branch **blackops** in hlsdk-xash3d - https://github.com/FWGS/hlsdk-xash3d/tree/blackops +Branch **blackops** in hlsdk-portable - https://github.com/FWGS/hlsdk-portable/tree/blackops ## Bloody Pizza: Vendetta -Branch **caseclosed** in hlsdk-xash3d - https://github.com/FWGS/hlsdk-xash3d/tree/caseclosed +Branch **caseclosed** in hlsdk-portable - https://github.com/FWGS/hlsdk-portable/tree/caseclosed ## Case Closed -Branch **caseclosed** in hlsdk-xash3d - https://github.com/FWGS/hlsdk-xash3d/tree/caseclosed +Branch **caseclosed** in hlsdk-portable - https://github.com/FWGS/hlsdk-portable/tree/caseclosed ## Cleaner's Adventures -Branch **CAd** in hlsdk-xash3d - https://github.com/FWGS/hlsdk-xash3d/tree/CAd +Branch **CAd** in hlsdk-portable - https://github.com/FWGS/hlsdk-portable/tree/CAd ## Counter Strike Reverse-engineered code of client part by a1batross - https://github.com/FWGS/cs16-client/tree/v1.32 @@ -222,60 +226,60 @@ Recreation by lostgamer aka nillerusr - https://github.com/LostGamerHL/crack_lif ## Escape from the Darkness malortie's recreation - https://github.com/malortie/hl-eftd -Reverse-engineered code, branch **eftd** in hlsdk-xash3d - https://github.com/FWGS/hlsdk-xash3d/tree/eftd +Reverse-engineered code, branch **eftd** in hlsdk-portable - https://github.com/FWGS/hlsdk-portable/tree/eftd ## Half-Life: Blue Shift Unkle Mike's recreation - https://hlfx.ru/forum/showthread.php?s=&threadid=5253 -Reverse-engineered code, branch **bshift** in hlsdk-xash3d - https://github.com/FWGS/hlsdk-xash3d/tree/bshift +Reverse-engineered code, branch **bshift** in hlsdk-portable - https://github.com/FWGS/hlsdk-portable/tree/bshift ## Half-Life: Induction -Branch **induction** in hlsdk-xash3d - https://github.com/FWGS/hlsdk-xash3d/tree/induction +Branch **induction** in hlsdk-portable - https://github.com/FWGS/hlsdk-portable/tree/induction ## Half-Life: Opposing Force Recreation by lostgamer aka nillerusr - https://github.com/LostGamerHL/hlsdk-xash3d -Reverse-engineered code, clean branch **opfor** in hlsdk-xash3d - https://github.com/FWGS/hlsdk-xash3d/tree/opfor +Reverse-engineered code, clean branch **opfor** in hlsdk-portable - https://github.com/FWGS/hlsdk-portable/tree/opfor Spirit of Half Life: Opposing-Force Edition - https://github.com/Hammermaps-DEV/SOHL-V1.9-Opposing-Force-Edition ## Half-Life: Rebellion -Reverse-engineered code, branch **rebellion** in hlsdk-xash3d - https://github.com/FWGS/hlsdk-xash3d/tree/rebellion +Reverse-engineered code, branch **rebellion** in hlsdk-portable - https://github.com/FWGS/hlsdk-portable/tree/rebellion ## Half-Life: Urbicide -Branch **hl_urbicide** in hlsdk-xash3d - https://github.com/FWGS/hlsdk-xash3d/tree/hl_urbicide +Branch **hl_urbicide** in hlsdk-portable - https://github.com/FWGS/hlsdk-portable/tree/hl_urbicide ## Half-Life: Visitors malortie's recreation - https://github.com/malortie/hl-visitors -Reverse-engineered code, branch **visitors** in hlsdk-xash3d - https://github.com/FWGS/hlsdk-xash3d/tree/visitors +Reverse-engineered code, branch **visitors** in hlsdk-portable - https://github.com/FWGS/hlsdk-portable/tree/visitors ## Half-Secret -Branch **half-secret** in hlsdk-xash3d - https://github.com/FWGS/hlsdk-xash3d/tree/half-secret +Branch **half-secret** in hlsdk-portable - https://github.com/FWGS/hlsdk-portable/tree/half-secret ## Night at the Office malortie's recreation - https://github.com/malortie/hl-nato -Branch **noffice** in hlsdk-xash3d - https://github.com/FWGS/hlsdk-xash3d/tree/noffice +Branch **noffice** in hlsdk-portable - https://github.com/FWGS/hlsdk-portable/tree/noffice ## Poke 646 malortie's recreation - https://github.com/malortie/hl-poke646 -Reverse-engineered code, branch **poke646** in hlsdk-xash3d - https://github.com/FWGS/hlsdk-xash3d/tree/poke646 +Reverse-engineered code, branch **poke646** in hlsdk-portable - https://github.com/FWGS/hlsdk-portable/tree/poke646 ## Poke 646: Vendetta malortie's recreation - https://github.com/malortie/hl-poke646-vendetta -Reverse-engineered code, branch **poke646_vendetta** in hlsdk-xash3d - https://github.com/FWGS/hlsdk-xash3d/tree/poke646_vendetta +Reverse-engineered code, branch **poke646_vendetta** in hlsdk-portable - https://github.com/FWGS/hlsdk-portable/tree/poke646_vendetta ## Residual Life -Reverse-engineered code, branch **residual_point** in hlsdk-xash3d - https://github.com/FWGS/hlsdk-xash3d/tree/residual_point +Reverse-engineered code, branch **residual_point** in hlsdk-portable - https://github.com/FWGS/hlsdk-portable/tree/residual_point ## Residual Point -Reverse-engineered code, branch **residual_point** in hlsdk-xash3d - https://github.com/FWGS/hlsdk-xash3d/tree/residual_point +Reverse-engineered code, branch **residual_point** in hlsdk-portable - https://github.com/FWGS/hlsdk-portable/tree/residual_point ## Sewer Beta -Branch **sewer_beta** in hlsdk-xash3d - https://github.com/FWGS/hlsdk-xash3d/tree/sewer_beta +Branch **sewer_beta** in hlsdk-portable - https://github.com/FWGS/hlsdk-portable/tree/sewer_beta ## Team Fortress Classic Reverse-engineered code by Velaron - https://github.com/Velaron/tf15-client @@ -283,59 +287,64 @@ Reverse-engineered code by Velaron - https://github.com/Velaron/tf15-client ## The Gate malortie's recreation - https://github.com/malortie/hl-thegate -Reverse-engineered code, branch **thegate** in hlsdk-xash3d - https://github.com/FWGS/hlsdk-xash3d/tree/thegate +Reverse-engineered code, branch **thegate** in hlsdk-portable - https://github.com/FWGS/hlsdk-portable/tree/thegate ## They Hunger malortie's recreation - https://github.com/malortie/hl-theyhunger -Reverse-engineered code, branch **theyhunger** in hlsdk-xash3d - https://github.com/FWGS/hlsdk-xash3d/tree/theyhunger +Reverse-engineered code, branch **theyhunger** in hlsdk-portable - https://github.com/FWGS/hlsdk-portable/tree/theyhunger They Hunger: Tactical - https://www.moddb.com/mods/they-hunger-tactical/downloads/tht-source-code-documentation ## Times of Troubles malortie's recreation - https://github.com/malortie/hl-tot -Branch **tot** in hlsdk-xash3d - https://github.com/FWGS/hlsdk-xash3d/tree/tot +Branch **tot** in hlsdk-portable - https://github.com/FWGS/hlsdk-portable/tree/tot # Derived work ## Adrenaline Gamer -Branch **aghl** in hlsdk-xash3d - https://github.com/FWGS/hlsdk-xash3d/tree/aghl +Branch **aghl** in hlsdk-portable - https://github.com/FWGS/hlsdk-portable/tree/aghl ## Bubblemod -Branch **bubblemod** in hlsdk-xash3d - https://github.com/FWGS/hlsdk-xash3d/tree/bubblemod +Branch **bubblemod** in hlsdk-portable - https://github.com/FWGS/hlsdk-portable/tree/bubblemod ## Deathmatch Classic Deathmatch Classic: Adrenaline Gamer Edition - https://github.com/martinwebrant/agmod/tree/master/src/dmc Deathmatch Quaked - https://www.moddb.com/games/deathmatch-classic/downloads/dmq2 -Branch **dmc** in hlsdk-xash3d - https://github.com/FWGS/hlsdk-xash3d/tree/dmc +Branch **dmc** in hlsdk-portable - https://github.com/FWGS/hlsdk-portable/tree/dmc ## Half-Life: Echoes -Branch **echoes** in hlsdk-xash3d - https://github.com/FWGS/hlsdk-xash3d/tree/echoes +Branch **echoes** in hlsdk-portable - https://github.com/FWGS/hlsdk-portable/tree/echoes ## Half-Life: Invasion Port to HLSDK 2.4 by malortie - https://github.com/malortie/hl-invasion Port to Linux by fmoraw - https://github.com/fmoraw/hlinvasion +Branch **invasion** in hlsdk-portable - https://github.com/FWGS/hlsdk-portable/tree/invasion + ## Half-Life: Top-Down -Branch **hltopdown** in hlsdk-xash3d - https://github.com/FWGS/hlsdk-xash3d/tree/hltopdown +Branch **hltopdown** in hlsdk-portable - https://github.com/FWGS/hlsdk-portable/tree/hltopdown ## Half-Screwed -Branch **half-screwed** in hlsdk-xash3d - https://github.com/FWGS/hlsdk-xash3d/tree/half-screwed +Branch **half-screwed** in hlsdk-portable - https://github.com/FWGS/hlsdk-portable/tree/half-screwed ## Natural Selection Port to Linux - https://github.com/fmoraw/NS +## Spirit of Half-Life +Version 1.2, branch **sohl1.2** in hlsdk-portable - https://github.com/FWGS/hlsdk-portable/tree/sohl1.2 + ## Swiss Cheese Halloween 2002 Just more playable version by malortie - https://github.com/malortie/hl-shall -Branch **halloween** in hlsdk-xash3d - https://github.com/FWGS/hlsdk-xash3d/tree/halloween +Branch **halloween** in hlsdk-portable - https://github.com/FWGS/hlsdk-portable/tree/halloween ## Threewave CTF -Branch **dmc** in hlsdk-xash3d - https://github.com/FWGS/hlsdk-xash3d/tree/dmc +Branch **dmc** in hlsdk-portable - https://github.com/FWGS/hlsdk-portable/tree/dmc ## Zombie-X -Branch **zombie-x** in hlsdk-xash3d - https://github.com/FWGS/hlsdk-xash3d/tree/zombie-x +Branch **zombie-x** in hlsdk-portable - https://github.com/FWGS/hlsdk-portable/tree/zombie-x From a2d11f670a10aca5099034bcddc32faa81226a55 Mon Sep 17 00:00:00 2001 From: Alibek Omarov Date: Wed, 13 Jul 2022 19:20:43 +0300 Subject: [PATCH 097/300] engine, public: fix float precision issues in mathlib and monster navigation code --- engine/server/sv_move.c | 2 +- public/xash3d_mathlib.h | 8 ++++---- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/engine/server/sv_move.c b/engine/server/sv_move.c index 491e0a89..48753f21 100644 --- a/engine/server/sv_move.c +++ b/engine/server/sv_move.c @@ -243,7 +243,7 @@ float SV_VecToYaw( const vec3_t src ) } else { - yaw = (int)( atan2( src[1], src[0] ) * 180.0f / M_PI_F ); + yaw = (int)( atan2( src[1], src[0] ) * 180.0 / M_PI ); if( yaw < 0 ) yaw += 360.0f; } return yaw; diff --git a/public/xash3d_mathlib.h b/public/xash3d_mathlib.h index 985e5d1f..363090d2 100644 --- a/public/xash3d_mathlib.h +++ b/public/xash3d_mathlib.h @@ -34,18 +34,18 @@ GNU General Public License for more details. #define ROLL 2 #ifndef M_PI -#define M_PI (float)3.14159265358979323846 +#define M_PI (double)3.14159265358979323846 #endif #ifndef M_PI2 -#define M_PI2 ((float)(M_PI * 2)) +#define M_PI2 ((double)(M_PI * 2)) #endif #define M_PI_F ((float)(M_PI)) #define M_PI2_F ((float)(M_PI2)) -#define RAD2DEG( x ) ((float)(x) * (float)(180.f / M_PI_F)) -#define DEG2RAD( x ) ((float)(x) * (float)(M_PI_F / 180.f)) +#define RAD2DEG( x ) ((double)(x) * (double)(180.0 / M_PI)) +#define DEG2RAD( x ) ((double)(x) * (double)(M_PI / 180.0)) #define NUMVERTEXNORMALS 162 From 5e1f189db32431f5bef414f7f7c7331e1ccf61ee Mon Sep 17 00:00:00 2001 From: Alibek Omarov Date: Wed, 13 Jul 2022 19:23:45 +0300 Subject: [PATCH 098/300] engine: platform: posix: use RTLD_NOW instead of lazy. It actually was a misconception coming from old engine fork We want to track unresolved symbols before library could be loaded Also, disable "symbol not found" spam in FunctionFromName. Due to how savefile mangling convert works and compatibility with GoldSrc saves, this function is used to bruteforce possible symbol names. --- engine/platform/posix/lib_posix.c | 11 +++-------- 1 file changed, 3 insertions(+), 8 deletions(-) diff --git a/engine/platform/posix/lib_posix.c b/engine/platform/posix/lib_posix.c index c4026706..abfd46ad 100644 --- a/engine/platform/posix/lib_posix.c +++ b/engine/platform/posix/lib_posix.c @@ -102,7 +102,7 @@ void *COM_LoadLibrary( const char *dllname, int build_ordinals_table, qboolean d // try to find by linker(LD_LIBRARY_PATH, DYLD_LIBRARY_PATH, LD_32_LIBRARY_PATH and so on...) if( !pHandle ) { - pHandle = dlopen( dllname, RTLD_LAZY ); + pHandle = dlopen( dllname, RTLD_NOW ); if( pHandle ) return pHandle; @@ -139,7 +139,7 @@ void *COM_LoadLibrary( const char *dllname, int build_ordinals_table, qboolean d else #endif { - if( !( hInst->hInstance = dlopen( hInst->fullPath, RTLD_LAZY ) ) ) + if( !( hInst->hInstance = dlopen( hInst->fullPath, RTLD_NOW ) ) ) { COM_PushLibraryError( dlerror() ); Mem_Free( hInst ); @@ -188,12 +188,7 @@ void *COM_GetProcAddress( void *hInstance, const char *name ) void *COM_FunctionFromName( void *hInstance, const char *pName ) { - void *function; - if( !( function = COM_GetProcAddress( hInstance, pName ) ) ) - { - Con_Reportf( S_ERROR "FunctionFromName: Can't get symbol %s: %s\n", pName, dlerror()); - } - return function; + return COM_GetProcAddress( hInstance, pName ); } #ifdef XASH_DYNAMIC_DLADDR From 657a6af9dcab2bd375534283c6bf59a8c95d0856 Mon Sep 17 00:00:00 2001 From: Alibek Omarov Date: Wed, 13 Jul 2022 19:45:42 +0300 Subject: [PATCH 099/300] mainui: update --- mainui | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mainui b/mainui index 2ba099b2..97fcbf89 160000 --- a/mainui +++ b/mainui @@ -1 +1 @@ -Subproject commit 2ba099b2a1b32dfdb4ecb66b27fc6df095199eb4 +Subproject commit 97fcbf8979f22d774b1cc01cb5553743592d39d0 From 772f4dcb60a8a2594df40195af4a0d4bd8ea7863 Mon Sep 17 00:00:00 2001 From: Alibek Omarov Date: Thu, 14 Jul 2022 18:26:47 +0300 Subject: [PATCH 100/300] scripts: gha: win32: fix build type from debug to release --- scripts/gha/build_win32.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scripts/gha/build_win32.sh b/scripts/gha/build_win32.sh index 5f36d124..be8bc506 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 "debug" --enable-utils --prefix=`pwd` $AMD64 || die +./waf.bat configure -s "SDL2_VC" -T "release" --enable-utils --prefix=`pwd` $AMD64 || die ./waf.bat build -v || die ./waf.bat install || die From 3ad60a0fa1af834b13a73051bf5cd535ee83b74a Mon Sep 17 00:00:00 2001 From: Valery Klachkov Date: Sat, 16 Jul 2022 13:59:41 +0000 Subject: [PATCH 101/300] engine: fix uninitialized variable in demo parsing code, fix incorrect size counter in memory allocator --- engine/client/cl_demo.c | 2 +- engine/common/zone.c | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/engine/client/cl_demo.c b/engine/client/cl_demo.c index 7443d51b..92ebdd2b 100644 --- a/engine/client/cl_demo.c +++ b/engine/client/cl_demo.c @@ -888,7 +888,7 @@ qboolean CL_DemoReadMessage( byte *buffer, size_t *length ) qboolean swallowmessages = true; static int tdlastdemoframe = 0; byte *userbuf = NULL; - size_t size; + size_t size = 0; byte cmd; if( !cls.demofile ) diff --git a/engine/common/zone.c b/engine/common/zone.c index f3200ed1..4ca83f65 100644 --- a/engine/common/zone.c +++ b/engine/common/zone.c @@ -91,8 +91,8 @@ void *_Mem_Alloc( poolhandle_t poolptr, size_t size, qboolean clear, const char pool->totalsize += size; // big allocations are not clumped - pool->realsize += sizeof( memheader_t ) + size + sizeof( int ); - mem = (memheader_t *)Q_malloc( sizeof( memheader_t ) + size + sizeof( int )); + pool->realsize += sizeof( memheader_t ) + size + sizeof( size_t ); + mem = (memheader_t *)Q_malloc( sizeof( memheader_t ) + size + sizeof( size_t )); if( mem == NULL ) Sys_Error( "Mem_Alloc: out of memory (alloc at %s:%i)\n", filename, fileline ); mem->filename = filename; @@ -162,7 +162,7 @@ static void Mem_FreeBlock( memheader_t *mem, const char *filename, int fileline // memheader has been unlinked, do the actual free now pool->totalsize -= mem->size; - pool->realsize -= sizeof( memheader_t ) + mem->size + sizeof( int ); + pool->realsize -= sizeof( memheader_t ) + mem->size + sizeof( size_t ); Q_free( mem ); } From c326853617c2cea82173441b5958d240343a4867 Mon Sep 17 00:00:00 2001 From: Alibek Omarov Date: Thu, 21 Jul 2022 01:52:10 +0300 Subject: [PATCH 102/300] engine: server: restore original PEntityOfEntIndex function, but still bug-compatible with GoldSrc --- engine/server/sv_game.c | 21 ++++++++++++++------- 1 file changed, 14 insertions(+), 7 deletions(-) diff --git a/engine/server/sv_game.c b/engine/server/sv_game.c index d7343fb0..f3fe6e6e 100644 --- a/engine/server/sv_game.c +++ b/engine/server/sv_game.c @@ -62,16 +62,23 @@ qboolean SV_CheckEdict( const edict_t *e, const char *file, const int line ) static edict_t *SV_PEntityOfEntIndex( const int iEntIndex, const qboolean allentities ) { - edict_t *pEdict = EDICT_NUM( iEntIndex ); - qboolean player = allentities ? iEntIndex <= svs.maxclients : iEntIndex < svs.maxclients; + if( iEntIndex >= 0 && iEntIndex < GI->max_edicts ) + { + edict_t *pEdict = EDICT_NUM( iEntIndex ); + qboolean player = allentities ? iEntIndex <= svs.maxclients : iEntIndex < svs.maxclients; - if( !SV_IsValidEdict( pEdict )) - return NULL; + if( !iEntIndex || FBitSet( host.features, ENGINE_QUAKE_COMPATIBLE )) + return pEdict; // just get access to array - if( !player && !pEdict->pvPrivateData ) - return NULL; + if( SV_IsValidEdict( pEdict ) && pEdict->pvPrivateData ) + return pEdict; - return pEdict; + // g-cont: world and clients can be accessed even without private data + if( SV_IsValidEdict( pEdict ) && player ) + return pEdict; + } + + return NULL; } From fc132e87f48b899141a99bac14c315d526b09185 Mon Sep 17 00:00:00 2001 From: Alibek Omarov Date: Mon, 25 Jul 2022 19:56:31 +0300 Subject: [PATCH 103/300] engine, game_launch: fix rpath usage --- engine/wscript | 3 ++- game_launch/wscript | 2 +- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/engine/wscript b/engine/wscript index c1479c45..4d2c9eed 100644 --- a/engine/wscript +++ b/engine/wscript @@ -184,5 +184,6 @@ def build(bld): includes = includes, use = libs, install_path = install_path, - subsystem = bld.env.MSVC_SUBSYSTEM + subsystem = bld.env.MSVC_SUBSYSTEM, + rpath = '$ORIGIN' ) diff --git a/game_launch/wscript b/game_launch/wscript index 4b9ad831..d4dab31c 100644 --- a/game_launch/wscript +++ b/game_launch/wscript @@ -37,7 +37,7 @@ def build(bld): features = 'c cxx cxxprogram', includes = includes, use = libs, - rpath = '.', + rpath = '$ORIGIN', install_path = bld.env.BINDIR, subsystem = bld.env.MSVC_SUBSYSTEM ) From 5350d88f570597e20b206f35ffcd9ffdfd84dab7 Mon Sep 17 00:00:00 2001 From: Alibek Omarov Date: Mon, 11 Jul 2022 00:34:48 +0300 Subject: [PATCH 104/300] public: crtlib: add quotation mark support for ParseFile, required for filesystem_stdio --- engine/client/cl_game.c | 6 +----- engine/client/cl_gameui.c | 7 ++++++- engine/client/cl_mobile.c | 7 ++++++- engine/client/cl_qparse.c | 2 +- engine/common/cmd.c | 2 +- engine/common/common.c | 12 ++++++------ public/crtlib.c | 8 +++++++- public/crtlib.h | 6 ++++-- 8 files changed, 32 insertions(+), 18 deletions(-) diff --git a/engine/client/cl_game.c b/engine/client/cl_game.c index e516f423..f7e6d1dc 100644 --- a/engine/client/cl_game.c +++ b/engine/client/cl_game.c @@ -3098,11 +3098,7 @@ handle colon separately */ char *pfnParseFile( char *data, char *token ) { - char *out; - - out = _COM_ParseFileSafe( data, token, PFILE_TOKEN_MAX_LENGTH, PFILE_HANDLECOLON, NULL ); - - return out; + return COM_ParseFileSafe( data, token, PFILE_TOKEN_MAX_LENGTH, PFILE_HANDLECOLON, NULL, NULL ); } /* diff --git a/engine/client/cl_gameui.c b/engine/client/cl_gameui.c index f295e701..bae521d0 100644 --- a/engine/client/cl_gameui.c +++ b/engine/client/cl_gameui.c @@ -1224,6 +1224,11 @@ static int pfnGetRenderers( unsigned int num, char *shortName, size_t size1, cha return 1; } +static char *pfnParseFileSafe( char *data, char *buf, const int size, unsigned int flags, int *len ) +{ + return COM_ParseFileSafe( data, buf, size, flags, len, NULL ); +} + static ui_extendedfuncs_t gExtendedfuncs = { pfnEnableTextInput, @@ -1232,7 +1237,7 @@ static ui_extendedfuncs_t gExtendedfuncs = Con_UtfMoveRight, pfnGetRenderers, Sys_DoubleTime, - _COM_ParseFileSafe, + pfnParseFileSafe, NET_AdrToString }; diff --git a/engine/client/cl_mobile.c b/engine/client/cl_mobile.c index 2dc84d34..3e59b85a 100644 --- a/engine/client/cl_mobile.c +++ b/engine/client/cl_mobile.c @@ -103,6 +103,11 @@ static void pfnTouch_RemoveButton( const char *name ) Touch_RemoveButton( name, true ); } +static char *pfnParseFileSafe( char *data, char *buf, const int size, unsigned int flags, int *len ) +{ + return COM_ParseFileSafe( data, buf, size, flags, len, NULL ); +} + static mobile_engfuncs_t gpMobileEngfuncs = { MOBILITY_API_VERSION, @@ -118,7 +123,7 @@ static mobile_engfuncs_t gpMobileEngfuncs = Sys_Warn, pfnGetNativeObject, ID_SetCustomClientID, - _COM_ParseFileSafe + pfnParseFileSafe }; qboolean Mobile_Init( void ) diff --git a/engine/client/cl_qparse.c b/engine/client/cl_qparse.c index bfdf753e..76865362 100644 --- a/engine/client/cl_qparse.c +++ b/engine/client/cl_qparse.c @@ -864,7 +864,7 @@ void CL_QuakeExecStuff( void ) if( !*text ) break; - text = _COM_ParseFileSafe( text, token, sizeof( token ), PFILE_IGNOREBRACKET, NULL ); + text = COM_ParseFileSafe( text, token, sizeof( token ), PFILE_IGNOREBRACKET, NULL, NULL ); if( !text ) break; diff --git a/engine/common/cmd.c b/engine/common/cmd.c index 3a268e5a..1df7287b 100644 --- a/engine/common/cmd.c +++ b/engine/common/cmd.c @@ -647,7 +647,7 @@ void Cmd_TokenizeString( const char *text ) if( cmd_argc == 1 ) cmd_args = text; - text = _COM_ParseFileSafe( (char*)text, cmd_token, sizeof( cmd_token ), PFILE_IGNOREBRACKET, NULL ); + text = COM_ParseFileSafe( (char*)text, cmd_token, sizeof( cmd_token ), PFILE_IGNOREBRACKET, NULL, NULL ); if( !text ) return; diff --git a/engine/common/common.c b/engine/common/common.c index c8baf28b..2e00df6a 100644 --- a/engine/common/common.c +++ b/engine/common/common.c @@ -1182,22 +1182,22 @@ void Test_RunCommon( void ) Msg( "Checking COM_ParseFile...\n" ); - file = _COM_ParseFileSafe( file, buf, sizeof( buf ), 0, &len ); + file = COM_ParseFileSafe( file, buf, sizeof( buf ), 0, &len, NULL ); TASSERT( !Q_strcmp( buf, "q" ) && len == 1); - file = _COM_ParseFileSafe( file, buf, sizeof( buf ), 0, &len ); + file = COM_ParseFileSafe( file, buf, sizeof( buf ), 0, &len, NULL ); TASSERT( !Q_strcmp( buf, "asdf" ) && len == 4); - file = _COM_ParseFileSafe( file, buf, sizeof( buf ), 0, &len ); + file = COM_ParseFileSafe( file, buf, sizeof( buf ), 0, &len, NULL ); TASSERT( !Q_strcmp( buf, "qwer" ) && len == -1); - file = _COM_ParseFileSafe( file, buf, sizeof( buf ), 0, &len ); + file = COM_ParseFileSafe( file, buf, sizeof( buf ), 0, &len, NULL ); TASSERT( !Q_strcmp( buf, "f \"f" ) && len == 4); - file = _COM_ParseFileSafe( file, buf, sizeof( buf ), 0, &len ); + file = COM_ParseFileSafe( file, buf, sizeof( buf ), 0, &len, NULL ); TASSERT( !Q_strcmp( buf, "meow" ) && len == -1); - file = _COM_ParseFileSafe( file, buf, sizeof( buf ), 0, &len ); + file = COM_ParseFileSafe( file, buf, sizeof( buf ), 0, &len, NULL ); TASSERT( !Q_strcmp( buf, "bark" ) && len == 4); } #endif diff --git a/public/crtlib.c b/public/crtlib.c index 2cc3e4e9..d183c7a5 100644 --- a/public/crtlib.c +++ b/public/crtlib.c @@ -889,11 +889,14 @@ COM_ParseFile text parser ============== */ -char *_COM_ParseFileSafe( char *data, char *token, const int size, unsigned int flags, int *plen ) +char *COM_ParseFileSafe( char *data, char *token, const int size, unsigned int flags, int *plen, qboolean *quoted ) { int c, len = 0; qboolean overflow = false; + if( quoted ) + *quoted = false; + if( !token || !size ) { if( plen ) *plen = 0; @@ -927,6 +930,9 @@ skipwhite: // handle quoted strings specially if( c == '\"' ) { + if( quoted ) + *quoted = true; + data++; while( 1 ) { diff --git a/public/crtlib.h b/public/crtlib.h index 43a0c775..6c1ad224 100644 --- a/public/crtlib.h +++ b/public/crtlib.h @@ -37,6 +37,7 @@ enum #define PFILE_IGNOREBRACKET (1<<0) #define PFILE_HANDLECOLON (1<<1) #define PFILE_TOKEN_MAX_LENGTH 1024 +#define PFILE_FS_TOKEN_MAX_LENGTH 512 // // crtlib.c @@ -83,10 +84,11 @@ void COM_RemoveLineFeed( char *str ); void COM_PathSlashFix( char *path ); char COM_Hex2Char( uint8_t hex ); void COM_Hex2String( uint8_t hex, char *str ); +// return 0 on empty or null string, 1 otherwise #define COM_CheckString( string ) ( ( !string || !*string ) ? 0 : 1 ) #define COM_CheckStringEmpty( string ) ( ( !*string ) ? 0 : 1 ) -char *_COM_ParseFileSafe( char *data, char *token, const int size, unsigned int flags, int *len ); -#define COM_ParseFile( data, token, size ) _COM_ParseFileSafe( data, token, size, 0, NULL ) +char *COM_ParseFileSafe( char *data, char *token, const int size, unsigned int flags, int *len, qboolean *quoted ); +#define COM_ParseFile( data, token, size ) COM_ParseFileSafe( data, token, size, 0, NULL, NULL ) int matchpattern( const char *in, const char *pattern, qboolean caseinsensitive ); int matchpattern_with_separator( const char *in, const char *pattern, qboolean caseinsensitive, const char *separators, qboolean wildcard_least_one ); From a9c82dbe211b59fd58f386f85dac8a2c57ca8e97 Mon Sep 17 00:00:00 2001 From: Alibek Omarov Date: Mon, 11 Jul 2022 03:59:35 +0300 Subject: [PATCH 105/300] public: make crtlib linkable with C++ --- public/crtlib.h | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/public/crtlib.h b/public/crtlib.h index 6c1ad224..5b86502a 100644 --- a/public/crtlib.h +++ b/public/crtlib.h @@ -21,6 +21,11 @@ GNU General Public License for more details. #include "build.h" #include "xash3d_types.h" +#ifdef __cplusplus +extern "C" +{ +#endif + // timestamp modes enum { @@ -145,5 +150,8 @@ static inline char *Q_stristr( const char *s1, const char *s2 ) char *Q_stristr( const char *s1, const char *s2 ); #endif // defined( HAVE_STRCASESTR ) +#ifdef __cplusplus +} +#endif #endif//STDLIB_H From a41f8cb01bebae92408da72e3ca031023667172c Mon Sep 17 00:00:00 2001 From: Alibek Omarov Date: Tue, 26 Jul 2022 04:07:52 +0300 Subject: [PATCH 106/300] engine: move version strings to com_strings.h file, in preparation of filesystem_stdio branch merge --- engine/common/com_strings.h | 2 ++ engine/common/common.h | 3 --- 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/engine/common/com_strings.h b/engine/common/com_strings.h index fcb40d41..32c5c0a1 100644 --- a/engine/common/com_strings.h +++ b/engine/common/com_strings.h @@ -64,6 +64,8 @@ GNU General Public License for more details. #define DEFAULT_UPDATE_PAGE "https://github.com/FWGS/xash3d-fwgs/releases/latest" #define XASH_ENGINE_NAME "Xash3D FWGS" +#define XASH_VERSION "0.20" // engine current version +#define XASH_COMPAT_VERSION "0.99" // version we are based on // renderers order is important, software is always a last chance fallback #define DEFAULT_RENDERERS { "gl", "gles1", "gles2", "gl4es", "soft" } diff --git a/engine/common/common.h b/engine/common/common.h index 333f31aa..badeeea5 100644 --- a/engine/common/common.h +++ b/engine/common/common.h @@ -119,9 +119,6 @@ typedef enum #include "con_nprint.h" #include "crclib.h" -#define XASH_VERSION "0.20" // engine current version -#define XASH_COMPAT_VERSION "0.99" // version we are based on - // PERFORMANCE INFO #define MIN_FPS 20.0f // host minimum fps value for maxfps. #define MAX_FPS 200.0f // upper limit for maxfps. From 12ea6dcfd7d9242f7e9412e0e4d7ada4184fc256 Mon Sep 17 00:00:00 2001 From: Alibek Omarov Date: Tue, 26 Jul 2022 04:10:36 +0300 Subject: [PATCH 107/300] public: move build.c from engine to public library, in preparation of filesystem_stdio merge --- engine/common/common.h | 10 ---------- {engine/common => public}/build.c | 2 +- public/crtlib.h | 9 +++++++++ 3 files changed, 10 insertions(+), 11 deletions(-) rename {engine/common => public}/build.c (99%) diff --git a/engine/common/common.h b/engine/common/common.h index badeeea5..8b112178 100644 --- a/engine/common/common.h +++ b/engine/common/common.h @@ -667,16 +667,6 @@ void FS_FreeStream( stream_t *stream ); qboolean Sound_Process( wavdata_t **wav, int rate, int width, uint flags ); uint Sound_GetApproxWavePlayLen( const char *filepath ); -// -// build.c -// -int Q_buildnum( void ); -int Q_buildnum_compat( void ); -const char *Q_buildos( void ); -const char *Q_buildarch( void ); -const char *Q_buildcommit( void ); - - // // host.c // diff --git a/engine/common/build.c b/public/build.c similarity index 99% rename from engine/common/build.c rename to public/build.c index 42ba572c..b9662c53 100644 --- a/engine/common/build.c +++ b/public/build.c @@ -13,7 +13,7 @@ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. */ -#include "common.h" +#include "crtlib.h" static const char *date = __DATE__ ; static const char *mon[12] = { "Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec" }; diff --git a/public/crtlib.h b/public/crtlib.h index 5b86502a..2d44939f 100644 --- a/public/crtlib.h +++ b/public/crtlib.h @@ -44,6 +44,15 @@ enum #define PFILE_TOKEN_MAX_LENGTH 1024 #define PFILE_FS_TOKEN_MAX_LENGTH 512 +// +// build.c +// +int Q_buildnum( void ); +int Q_buildnum_compat( void ); +const char *Q_buildos( void ); +const char *Q_buildarch( void ); +const char *Q_buildcommit( void ); + // // crtlib.c // From 5e4fc64430ccca8b05df67c6f3c453ff75d029cc Mon Sep 17 00:00:00 2001 From: Alibek Omarov Date: Fri, 1 Jul 2022 19:37:21 +0300 Subject: [PATCH 108/300] filesystem: introduce new module, based on engine filesystem. The goal is to share filesystem code between engine and utilities and provide C++ VFileSystem interface in the future --- engine/client/cl_gameui.c | 36 +- engine/client/ref_common.c | 8 +- engine/common/common.c | 17 - engine/common/common.h | 142 +- engine/common/con_utils.c | 6 +- engine/common/filesystem.h | 200 -- engine/common/filesystem_engine.c | 146 ++ engine/common/host.c | 29 +- engine/common/hpak.c | 4 +- engine/common/hpak.h | 60 + engine/common/imagelib/img_png.c | 1 - engine/common/lib_common.c | 25 + engine/platform/win32/lib_win.c | 4 +- engine/ref_api.h | 16 +- engine/wscript | 2 +- {engine/common => filesystem}/filesystem.c | 2439 +++++--------------- filesystem/filesystem.h | 202 ++ filesystem/filesystem_internal.h | 204 ++ filesystem/fscallback.h | 80 + filesystem/pak.c | 394 ++++ filesystem/wad.c | 634 +++++ filesystem/wscript | 17 + filesystem/zip.c | 678 ++++++ public/crtlib.c | 17 + public/crtlib.h | 1 + {engine/common => public}/miniz.h | 0 ref_gl/gl_alias.c | 2 +- ref_gl/gl_backend.c | 4 +- ref_gl/gl_rmisc.c | 2 +- ref_gl/gl_studio.c | 2 +- ref_gl/gl_warp.c | 4 +- ref_gl/wscript | 1 + ref_soft/r_studio.c | 2 +- ref_soft/wscript | 1 + wscript | 1 + 35 files changed, 3102 insertions(+), 2279 deletions(-) delete mode 100644 engine/common/filesystem.h create mode 100644 engine/common/filesystem_engine.c create mode 100644 engine/common/hpak.h rename {engine/common => filesystem}/filesystem.c (56%) create mode 100644 filesystem/filesystem.h create mode 100644 filesystem/filesystem_internal.h create mode 100644 filesystem/fscallback.h create mode 100644 filesystem/pak.c create mode 100644 filesystem/wad.c create mode 100644 filesystem/wscript create mode 100644 filesystem/zip.c rename {engine/common => public}/miniz.h (100%) diff --git a/engine/client/cl_gameui.c b/engine/client/cl_gameui.c index bae521d0..e5bf2683 100644 --- a/engine/client/cl_gameui.c +++ b/engine/client/cl_gameui.c @@ -980,7 +980,7 @@ pfnGetGamesList */ static GAMEINFO ** GAME_EXPORT pfnGetGamesList( int *numGames ) { - if( numGames ) *numGames = SI.numgames; + if( numGames ) *numGames = FI->numgames; return gameui.modsInfo; } @@ -1117,6 +1117,30 @@ static char *pfnParseFile( char *buf, char *token ) return COM_ParseFile( buf, token, INT_MAX ); } +/* +============= +pfnFileExists + +legacy wrapper +============= +*/ +static int pfnFileExists( const char *path, int gamedironly ) +{ + return FS_FileExists( path, gamedironly ); +} + +/* +============= +pfnDelete + +legacy wrapper +============= +*/ +static int pfnDelete( const char *path ) +{ + return FS_Delete( path ); +} + // engine callbacks static ui_enginefuncs_t gEngfuncs = { @@ -1163,7 +1187,7 @@ static ui_enginefuncs_t gEngfuncs = pfnRenderScene, pfnAddEntity, Host_Error, - FS_FileExists, + pfnFileExists, pfnGetGameDir, Cmd_CheckMapsList, CL_Active, @@ -1202,7 +1226,7 @@ static ui_enginefuncs_t gEngfuncs = COM_CompareFileTime, VID_GetModeString, (void*)COM_SaveFile, - (void*)FS_Delete + pfnDelete }; static void pfnEnableTextInput( int enable ) @@ -1358,13 +1382,13 @@ qboolean UI_LoadProgs( void ) Cvar_FullSet( "host_gameuiloaded", "1", FCVAR_READ_ONLY ); // setup gameinfo - for( i = 0; i < SI.numgames; i++ ) + for( i = 0; i < FI->numgames; i++ ) { gameui.modsInfo[i] = Mem_Calloc( gameui.mempool, sizeof( GAMEINFO )); - UI_ConvertGameInfo( gameui.modsInfo[i], SI.games[i] ); + UI_ConvertGameInfo( gameui.modsInfo[i], FI->games[i] ); } - UI_ConvertGameInfo( &gameui.gameInfo, SI.GameInfo ); // current gameinfo + UI_ConvertGameInfo( &gameui.gameInfo, FI->GameInfo ); // current gameinfo // setup globals gameui.globals->developer = host.allow_console; diff --git a/engine/client/ref_common.c b/engine/client/ref_common.c index 0760d904..57388705 100644 --- a/engine/client/ref_common.c +++ b/engine/client/ref_common.c @@ -334,10 +334,6 @@ static ref_api_t gEngfuncs = COM_FreeLibrary, COM_GetProcAddress, - FS_LoadFile, - FS_FileExists, - FS_AllowDirectPaths, - R_Init_Video_, R_Free_Video, @@ -383,7 +379,9 @@ static ref_api_t gEngfuncs = pfnDrawNormalTriangles, pfnDrawTransparentTriangles, - &clgame.drawFuncs + &clgame.drawFuncs, + + &g_fsapi, }; static void R_UnloadProgs( void ) diff --git a/engine/common/common.c b/engine/common/common.c index 2e00df6a..684718dd 100644 --- a/engine/common/common.c +++ b/engine/common/common.c @@ -585,23 +585,6 @@ void COM_TrimSpace( const char *source, char *dest ) dest[length] = 0; } -/* -============ -COM_FixSlashes - -Changes all '/' characters into '\' characters, in place. -============ -*/ -void COM_FixSlashes( char *pname ) -{ - while( *pname ) - { - if( *pname == '\\' ) - *pname = '/'; - pname++; - } -} - /* ================== COM_Nibble diff --git a/engine/common/common.h b/engine/common/common.h index 8b112178..58741bf3 100644 --- a/engine/common/common.h +++ b/engine/common/common.h @@ -76,13 +76,6 @@ XASH SPECIFIC - sort of hack that works only in Xash3D not in GoldSrc #define HACKS_RELATED_HLMODS // some HL-mods works differently under Xash and can't be fixed without some hacks at least at current time -typedef struct -{ - int numfilenames; - char **filenames; - char *filenamesbuffer; -} search_t; - enum { DEV_NONE = 0, @@ -118,6 +111,8 @@ typedef enum #include "cvar.h" #include "con_nprint.h" #include "crclib.h" +#include "ref_api.h" +#include "fscallback.h" // PERFORMANCE INFO #define MIN_FPS 20.0f // host minimum fps value for maxfps. @@ -147,18 +142,6 @@ typedef enum #define MAX_STATIC_ENTITIES 32 // static entities that moved on the client when level is spawn #endif -// filesystem flags -#define FS_STATIC_PATH ( 1U << 0 ) // FS_ClearSearchPath will be ignore this path -#define FS_NOWRITE_PATH ( 1U << 1 ) // default behavior - last added gamedir set as writedir. This flag disables it -#define FS_GAMEDIR_PATH ( 1U << 2 ) // just a marker for gamedir path -#define FS_CUSTOM_PATH ( 1U << 3 ) // custom directory -#define FS_GAMERODIR_PATH ( 1U << 4 ) // caseinsensitive - -#define FS_GAMEDIRONLY_SEARCH_FLAGS ( FS_GAMEDIR_PATH | FS_CUSTOM_PATH | FS_GAMERODIR_PATH ) - -#define GI SI.GameInfo -#define FS_Gamedir() SI.GameInfo->gamefolder -#define FS_Title() SI.GameInfo->title #define GameState (&host.game) #define FORCE_DRAW_VERSION_TIME 5.0f // draw version for 5 seconds @@ -199,61 +182,6 @@ GAMEINFO stuff internal shared gameinfo structure (readonly for engine parts) ======================================================================== */ -typedef struct gameinfo_s -{ - // filesystem info - char gamefolder[MAX_QPATH]; // used for change game '-game x' - char basedir[MAX_QPATH]; // base game directory (like 'id1' for Quake or 'valve' for Half-Life) - char falldir[MAX_QPATH]; // used as second basedir - char startmap[MAX_QPATH];// map to start singleplayer game - char trainmap[MAX_QPATH];// map to start hazard course (if specified) - char title[64]; // Game Main Title - float version; // game version (optional) - - // .dll pathes - char dll_path[MAX_QPATH]; // e.g. "bin" or "cl_dlls" - char game_dll[MAX_QPATH]; // custom path for game.dll - - // .ico path - char iconpath[MAX_QPATH]; // "game.ico" by default - - // about mod info - string game_url; // link to a developer's site - string update_url; // link to updates page - char type[MAX_QPATH]; // single, toolkit, multiplayer etc - char date[MAX_QPATH]; - 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 - - char sp_entity[32]; // e.g. info_player_start - char mp_entity[32]; // e.g. info_player_deathmatch - char mp_filter[32]; // filtering multiplayer-maps - - char ambientsound[NUM_AMBIENTS][MAX_QPATH]; // quake ambient sounds - - int max_edicts; // min edicts is 600, max edicts is 8196 - int max_tents; // min temp ents is 300, max is 2048 - int max_beams; // min beams is 64, max beams is 512 - int max_particles; // min particles is 4096, max particles is 32768 - - char game_dll_linux[64]; // custom path for game.dll - char game_dll_osx[64]; // custom path for game.dll - - qboolean added; -} gameinfo_t; - -typedef enum -{ - GAME_NORMAL, - GAME_SINGLEPLAYER_ONLY, - GAME_MULTIPLAYER_ONLY -} gametype_t; - typedef struct sysinfo_s { string exeName; // exe.filename @@ -261,9 +189,6 @@ typedef struct sysinfo_s string basedirName; // name of base directory string gamedll; string clientlib; - gameinfo_t *GameInfo; // current GameInfo - gameinfo_t *games[MAX_MODS]; // environment games (founded at each engine start) - int numgames; } sysinfo_t; typedef enum @@ -470,6 +395,14 @@ extern sysinfo_t SI; typedef void (*xcommand_t)( void ); +// +// filesystem_engine.c +// +#define FILESYSTEM_STDIO_DLL "filesystem_stdio." OS_LIB_EXT +qboolean FS_LoadProgs( const char *name ); +void FS_Init( void ); +void FS_Shutdown( void ); + // // cmd.c // @@ -529,56 +462,6 @@ void Mem_PrintStats( void ); #define Mem_IsAllocated( mem ) Mem_IsAllocatedExt( NULL, mem ) #define Mem_Check() _Mem_Check( __FILE__, __LINE__ ) -// -// filesystem.c -// -void FS_Init( void ); -void FS_Path( void ); -void FS_Rescan( void ); -void FS_Shutdown( void ); -void FS_ClearSearchPath( void ); -void FS_AllowDirectPaths( qboolean enable ); -void FS_AddGameDirectory( const char *dir, uint flags ); -void FS_AddGameHierarchy( const char *dir, uint flags ); -void FS_LoadGameInfo( const char *rootfolder ); -const char *FS_GetDiskPath( const char *name, qboolean gamedironly ); -byte *W_LoadLump( wfile_t *wad, const char *lumpname, size_t *lumpsizeptr, const char type ); -void W_Close( wfile_t *wad ); -byte *FS_LoadFile( const char *path, fs_offset_t *filesizeptr, qboolean gamedironly ); -qboolean CRC32_File( dword *crcvalue, const char *filename ); -qboolean MD5_HashFile( byte digest[16], const char *pszFileName, uint seed[4] ); -byte *FS_LoadDirectFile( const char *path, fs_offset_t *filesizeptr ); -qboolean FS_WriteFile( const char *filename, const void *data, fs_offset_t len ); -qboolean COM_ParseVector( char **pfile, float *v, size_t size ); -void COM_NormalizeAngles( vec3_t angles ); -int COM_FileSize( const char *filename ); -void COM_FixSlashes( char *pname ); -void COM_FreeFile( void *buffer ); -int COM_CompareFileTime( const char *filename1, const char *filename2, int *iCompare ); -search_t *FS_Search( const char *pattern, int caseinsensitive, int gamedironly ); -file_t *FS_Open( const char *filepath, const char *mode, qboolean gamedironly ); -fs_offset_t FS_Write( file_t *file, const void *data, size_t datasize ); -fs_offset_t FS_Read( file_t *file, void *buffer, size_t buffersize ); -int FS_VPrintf( file_t *file, const char *format, va_list ap ); -int FS_Seek( file_t *file, fs_offset_t offset, int whence ); -int FS_Gets( file_t *file, byte *string, size_t bufsize ); -int FS_Printf( file_t *file, const char *format, ... ) _format( 2 ); -fs_offset_t FS_FileSize( const char *filename, qboolean gamedironly ); -int FS_FileTime( const char *filename, qboolean gamedironly ); -int FS_Print( file_t *file, const char *msg ); -qboolean FS_Rename( const char *oldname, const char *newname ); -int FS_FileExists( const char *filename, int gamedironly ); -int FS_SetCurrentDirectory( const char *path ); -qboolean FS_SysFileExists( const char *path, qboolean casesensitive ); -qboolean FS_FileCopy( file_t *pOutput, file_t *pInput, int fileSize ); -qboolean FS_Delete( const char *path ); -int FS_UnGetc( file_t *file, byte c ); -fs_offset_t FS_Tell( file_t *file ); -qboolean FS_Eof( file_t *file ); -int FS_Close( file_t *file ); -int FS_Getc( file_t *file ); -fs_offset_t FS_FileLength( file_t *f ); - // // imagelib // @@ -942,6 +825,11 @@ void UI_SetActiveMenu( qboolean fActive ); void UI_ShowConnectionWarning( void ); void Cmd_Null_f( void ); void Rcon_Print( const char *pMsg ); +qboolean COM_ParseVector( char **pfile, float *v, size_t size ); +void COM_NormalizeAngles( vec3_t angles ); +int COM_FileSize( const char *filename ); +void COM_FreeFile( void *buffer ); +int COM_CompareFileTime( const char *filename1, const char *filename2, int *iCompare ); // soundlib shared exports qboolean S_Init( void ); diff --git a/engine/common/con_utils.c b/engine/common/con_utils.c index b07374e4..8bafcb31 100644 --- a/engine/common/con_utils.c +++ b/engine/common/con_utils.c @@ -761,10 +761,10 @@ qboolean Cmd_GetGamesList( const char *s, char *completedname, int length ) // compare gamelist with current keyword len = Q_strlen( s ); - for( i = 0, numgamedirs = 0; i < SI.numgames; i++ ) + for( i = 0, numgamedirs = 0; i < FI->numgames; i++ ) { - if(( *s == '*' ) || !Q_strnicmp( SI.games[i]->gamefolder, s, len)) - Q_strcpy( gamedirs[numgamedirs++], SI.games[i]->gamefolder ); + if(( *s == '*' ) || !Q_strnicmp( FI->games[i]->gamefolder, s, len)) + Q_strcpy( gamedirs[numgamedirs++], FI->games[i]->gamefolder ); } if( !numgamedirs ) return false; diff --git a/engine/common/filesystem.h b/engine/common/filesystem.h deleted file mode 100644 index 3abba403..00000000 --- a/engine/common/filesystem.h +++ /dev/null @@ -1,200 +0,0 @@ -/* -filesystem.h - engine FS -Copyright (C) 2007 Uncle Mike - -This program is free software: you can redistribute it and/or modify -it under the terms of the GNU General Public License as published by -the Free Software Foundation, either version 3 of the License, or -(at your option) any later version. - -This program is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU General Public License for more details. -*/ - -#ifndef FILESYSTEM_H -#define FILESYSTEM_H - -/* -======================================================================== -PAK FILES - -The .pak files are just a linear collapse of a directory tree -======================================================================== -*/ -// header -#define IDPACKV1HEADER (('K'<<24)+('C'<<16)+('A'<<8)+'P') // little-endian "PACK" - -#define MAX_FILES_IN_PACK 65536 // pak - -typedef struct -{ - int ident; - int dirofs; - int dirlen; -} dpackheader_t; - -typedef struct -{ - char name[56]; // total 64 bytes - int filepos; - int filelen; -} dpackfile_t; - -/* -======================================================================== -.WAD archive format (WhereAllData - WAD) - -List of compressed files, that can be identify only by TYPE_* - - -header: dwadinfo_t[dwadinfo_t] -file_1: byte[dwadinfo_t[num]->disksize] -file_2: byte[dwadinfo_t[num]->disksize] -file_3: byte[dwadinfo_t[num]->disksize] -... -file_n: byte[dwadinfo_t[num]->disksize] -infotable dlumpinfo_t[dwadinfo_t->numlumps] -======================================================================== -*/ -#define WAD3_NAMELEN 16 -#define HINT_NAMELEN 5 // e.g. _mask, _norm -#define MAX_FILES_IN_WAD 65535 // real limit as above <2Gb size not a lumpcount - -#include "const.h" - -typedef struct -{ - int ident; // should be WAD3 - int numlumps; // num files - int infotableofs; // LUT offset -} dwadinfo_t; - -typedef struct -{ - int filepos; // file offset in WAD - int disksize; // compressed or uncompressed - int size; // uncompressed - signed char type; // TYP_* - signed char attribs; // file attribs - signed char pad0; - signed char pad1; - char name[WAD3_NAMELEN]; // must be null terminated -} dlumpinfo_t; - -#include "custom.h" - -/* -======================================================================== -.HPK archive format (Hash PAK - HPK) - -List of compressed files, that can be identify only by TYPE_* - - -header: dwadinfo_t[dwadinfo_t] -file_1: byte[dwadinfo_t[num]->disksize] -file_2: byte[dwadinfo_t[num]->disksize] -file_3: byte[dwadinfo_t[num]->disksize] -... -file_n: byte[dwadinfo_t[num]->disksize] -infotable dlumpinfo_t[dwadinfo_t->numlumps] -======================================================================== -*/ - -#define IDHPAKHEADER (('K'<<24)+('A'<<16)+('P'<<8)+'H') // little-endian "HPAK" -#define IDHPAK_VERSION 1 - -typedef struct -{ - int ident; // should be equal HPAK - int version; - int infotableofs; -} hpak_header_t; - -typedef struct -{ - resource_t resource; - int filepos; - int disksize; -} hpak_lump_t; - -typedef struct -{ - int count; - hpak_lump_t *entries; // variable sized. -} hpak_info_t; - -#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') -#define ZIP_HEADER_EOCD ((0x06<<24)+(0x05<<16)+('K'<<8)+'P') - -#define ZIP_COMPRESSION_NO_COMPRESSION 0 -#define ZIP_COMPRESSION_DEFLATED 8 - -#define ZIP_ZIP64 0xffffffff - -#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; -} zip_header_t; - -/* - in zip64 comp and uncompr size == 0xffffffff remeber this - compressed and uncompress filesize stored in extra field -*/ - -typedef struct zip_header_extra_s -{ - unsigned int signature; // ZIP_HEADER_SPANNED - unsigned int crc32; - unsigned int compressed_size; - unsigned int 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; -} 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; -} zip_header_eocd_t; -#pragma pack( pop ) - -#endif//FILESYSTEM_H diff --git a/engine/common/filesystem_engine.c b/engine/common/filesystem_engine.c new file mode 100644 index 00000000..42754134 --- /dev/null +++ b/engine/common/filesystem_engine.c @@ -0,0 +1,146 @@ + /* +filesystem.c - game filesystem based on DP fs +Copyright (C) 2007 Uncle Mike + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. +*/ + +#include "common.h" +#include "library.h" + +fs_api_t g_fsapi; +fs_globals_t *FI; + +static HINSTANCE fs_hInstance; + +static void FS_Rescan_f( void ) +{ + FS_Rescan(); +} + +static void FS_ClearPaths_f( void ) +{ + FS_ClearSearchPath(); +} + +static void FS_Path_f_( void ) +{ + FS_Path_f(); +} + +static fs_interface_t fs_memfuncs = +{ + Con_Printf, + Con_DPrintf, + Con_Reportf, + Sys_Error, + + _Mem_AllocPool, + _Mem_FreePool, + _Mem_Alloc, + _Mem_Realloc, + _Mem_Free, +}; + +static void FS_UnloadProgs( void ) +{ + COM_FreeLibrary( fs_hInstance ); + fs_hInstance = 0; +} + +qboolean FS_LoadProgs( const char *name ) +{ + FSAPI GetFSAPI; + + fs_hInstance = COM_LoadLibrary( name, false, true ); + + if( !fs_hInstance ) + { + Host_Error( "FS_LoadProgs: can't load filesystem library %s: %s\n", name, COM_GetLibraryError() ); + return false; + } + + if( !( GetFSAPI = (FSAPI)COM_GetProcAddress( fs_hInstance, GET_FS_API ))) + { + FS_UnloadProgs(); + Host_Error( "FS_LoadProgs: can't find GetFSAPI entry point in %s\n", name ); + return false; + } + + if( !GetFSAPI( FS_API_VERSION, &g_fsapi, &FI, &fs_memfuncs )) + { + FS_UnloadProgs(); + Host_Error( "FS_LoadProgs: can't initialize filesystem API: wrong version\n" ); + return false; + } + + Con_DPrintf( "FS_LoadProgs: filesystem_stdio successfully loaded\n" ); + + return true; +} + +/* +================ +FS_Init +================ +*/ +void FS_Init( void ) +{ + qboolean hasBaseDir = false; + qboolean hasGameDir = false; + qboolean caseinsensitive = true; + int i; + string gamedir; + + Cmd_AddRestrictedCommand( "fs_rescan", FS_Rescan_f, "rescan filesystem search pathes" ); + Cmd_AddRestrictedCommand( "fs_path", FS_Path_f_, "show filesystem search pathes" ); + Cmd_AddRestrictedCommand( "fs_clearpaths", FS_ClearPaths_f, "clear filesystem search pathes" ); + +#if !XASH_WIN32 + if( Sys_CheckParm( "-casesensitive" ) ) + caseinsensitive = false; +#endif + + if( !Sys_GetParmFromCmdLine( "-game", gamedir )) + Q_strncpy( gamedir, SI.basedirName, sizeof( gamedir )); // gamedir == basedir + + if( !FS_InitStdio( caseinsensitive, host.rootdir, SI.basedirName, gamedir, host.rodir )) + { + Host_Error( "Can't init filesystem_stdio!\n" ); + return; + } + + if( !Sys_GetParmFromCmdLine( "-dll", SI.gamedll )) + SI.gamedll[0] = 0; + + if( !Sys_GetParmFromCmdLine( "-clientlib", SI.clientlib )) + SI.clientlib[0] = 0; +} + +/* +================ +FS_Shutdown +================ +*/ +void FS_Shutdown( void ) +{ + int i; + + FS_ShutdownStdio(); + + memset( &SI, 0, sizeof( sysinfo_t )); + + FS_UnloadProgs(); +} + + + + diff --git a/engine/common/host.c b/engine/common/host.c index fee751ce..3f89d176 100644 --- a/engine/common/host.c +++ b/engine/common/host.c @@ -328,13 +328,13 @@ void Host_ChangeGame_f( void ) } // validate gamedir - for( i = 0; i < SI.numgames; i++ ) + for( i = 0; i < FI->numgames; i++ ) { - if( !Q_stricmp( SI.games[i]->gamefolder, Cmd_Argv( 1 ))) + if( !Q_stricmp( FI->games[i]->gamefolder, Cmd_Argv( 1 ))) break; } - if( i == SI.numgames ) + if( i == FI->numgames ) { Con_Printf( "%s not exist\n", Cmd_Argv( 1 )); } @@ -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 *arg2 = va( "change game to '%s'", SI.games[i]->title ); + const char *arg2 = va( "change game to '%s'", FI->games[i]->title ); Host_NewInstance( arg1, arg2 ); } @@ -1027,18 +1027,27 @@ void Host_InitCommon( int argc, char **argv, const char *progname, qboolean bCha if( len && host.rodir[len - 1] == '/' ) host.rodir[len - 1] = 0; - if( !COM_CheckStringEmpty( host.rootdir ) || FS_SetCurrentDirectory( host.rootdir ) != 0 ) + if( !COM_CheckStringEmpty( host.rootdir )) + { + Sys_Error( "Changing working directory failed (empty working directory)\n" ); + return; + } + + FS_LoadProgs( FILESYSTEM_STDIO_DLL ); + + if( FS_SetCurrentDirectory( host.rootdir ) != 0 ) Con_Reportf( "%s is working directory now\n", host.rootdir ); else Sys_Error( "Changing working directory to %s failed.\n", host.rootdir ); + FS_Init(); + Sys_InitLog(); 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" ); - FS_Init(); Image_Init(); Sound_Init(); @@ -1048,8 +1057,16 @@ void Host_InitCommon( int argc, char **argv, const char *progname, qboolean bCha #endif FS_LoadGameInfo( NULL ); + + if( FS_FileExists( va( "%s.rc", SI.basedirName ), false )) + Q_strncpy( SI.rcName, SI.basedirName, sizeof( SI.rcName )); // e.g. valve.rc + else Q_strncpy( SI.rcName, SI.exeName, sizeof( SI.rcName )); // e.g. quake.rc + Q_strncpy( host.gamefolder, GI->gamefolder, sizeof( host.gamefolder )); + Image_CheckPaletteQ1 (); + Host_InitDecals (); // reload decals + // DEPRECATED: by FWGS fork #if 0 if( GI->secure ) diff --git a/engine/common/hpak.c b/engine/common/hpak.c index 0da8e8a7..076c3428 100644 --- a/engine/common/hpak.c +++ b/engine/common/hpak.c @@ -14,7 +14,7 @@ GNU General Public License for more details. */ #include "common.h" -#include "filesystem.h" +#include "hpak.h" #define HPAK_MAX_ENTRIES 0x8000 #define HPAK_MIN_SIZE (1 * 1024) @@ -402,7 +402,7 @@ static qboolean HPAK_Validate( const char *filename, qboolean quiet ) FS_Seek( f, hdr.infotableofs, SEEK_SET ); FS_Read( f, &num_lumps, sizeof( num_lumps )); - if( num_lumps < 1 || num_lumps > MAX_FILES_IN_WAD ) + if( num_lumps < 1 || num_lumps > HPAK_MAX_ENTRIES ) { Con_DPrintf( S_ERROR "HPAK_ValidatePak: %s has too many lumps %u.\n", pakname, num_lumps ); FS_Close( f ); diff --git a/engine/common/hpak.h b/engine/common/hpak.h new file mode 100644 index 00000000..4d5c8fde --- /dev/null +++ b/engine/common/hpak.h @@ -0,0 +1,60 @@ +/* +hpak.c - custom user package to send other clients +Copyright (C) 2010 Uncle Mike + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. +*/ +#ifndef HPAK_H +#define HPAK_H + +#include "custom.h" + +/* +======================================================================== +.HPK archive format (Hash PAK - HPK) + +List of compressed files, that can be identify only by TYPE_* + + +header: dwadinfo_t[dwadinfo_t] +file_1: byte[dwadinfo_t[num]->disksize] +file_2: byte[dwadinfo_t[num]->disksize] +file_3: byte[dwadinfo_t[num]->disksize] +... +file_n: byte[dwadinfo_t[num]->disksize] +infotable dlumpinfo_t[dwadinfo_t->numlumps] +======================================================================== +*/ + +#define IDHPAKHEADER (('K'<<24)+('A'<<16)+('P'<<8)+'H') // little-endian "HPAK" +#define IDHPAK_VERSION 1 + +typedef struct +{ + int ident; // should be equal HPAK + int version; + int infotableofs; +} hpak_header_t; + +typedef struct +{ + resource_t resource; + int filepos; + int disksize; +} hpak_lump_t; + +typedef struct +{ + int count; + hpak_lump_t *entries; // variable sized. +} hpak_info_t; + +#endif // HPAK_H diff --git a/engine/common/imagelib/img_png.c b/engine/common/imagelib/img_png.c index 9569a241..2c129ba1 100644 --- a/engine/common/imagelib/img_png.c +++ b/engine/common/imagelib/img_png.c @@ -13,7 +13,6 @@ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. */ -#define MINIZ_HEADER_FILE_ONLY #include "miniz.h" #include "imagelib.h" #include "xash3d_mathlib.h" diff --git a/engine/common/lib_common.c b/engine/common/lib_common.c index e18161db..5b86df77 100644 --- a/engine/common/lib_common.c +++ b/engine/common/lib_common.c @@ -86,6 +86,31 @@ const char *COM_OffsetNameForFunction( void *function ) return sname; } +dll_user_t *FS_FindLibrary( const char *dllname, qboolean directpath ) +{ + dll_user_t *p; + fs_dllinfo_t dllInfo; + + // no fs loaded, can't search + if( !g_fsapi.FindLibrary ) + return NULL; + + // fs can't find library + if( !g_fsapi.FindLibrary( dllname, directpath, &dllInfo )) + return NULL; + + // NOTE: for libraries we not fail even if search is NULL + // let the OS find library himself + p = Mem_Calloc( host.mempool, sizeof( dll_user_t )); + Q_strncpy( p->shortPath, dllInfo.shortPath, sizeof( p->shortPath )); + Q_strncpy( p->fullPath, dllInfo.fullPath, sizeof( p->fullPath )); + Q_strncpy( p->dllName, dllname, sizeof( p->dllName )); + p->custom_loader = dllInfo.custom_loader; + p->encrypted = dllInfo.encrypted; + + return p; +} + /* ============================================================================= diff --git a/engine/platform/win32/lib_win.c b/engine/platform/win32/lib_win.c index 21ee1fab..b6cf9cf8 100644 --- a/engine/platform/win32/lib_win.c +++ b/engine/platform/win32/lib_win.c @@ -391,7 +391,7 @@ qboolean COM_CheckLibraryDirectDependency( const char *name, const char *depname dll_user_t *hInst; qboolean ret = FALSE; - hInst = FS_FindLibrary( name, directpath ); + hInst = COM_FindLibrary( name, directpath ); if ( !hInst ) return FALSE; data = FS_LoadFile( name, NULL, false ); @@ -439,7 +439,7 @@ void *COM_LoadLibrary( const char *dllname, int build_ordinals_table, qboolean d COM_ResetLibraryError(); - hInst = FS_FindLibrary( dllname, directpath ); + hInst = COM_FindLibrary( dllname, directpath ); if( !hInst ) { COM_PushLibraryError( va( "Failed to find library %s", dllname ) ); diff --git a/engine/ref_api.h b/engine/ref_api.h index 3c3119d3..2f78b709 100644 --- a/engine/ref_api.h +++ b/engine/ref_api.h @@ -27,8 +27,12 @@ GNU General Public License for more details. #include "studio.h" #include "r_efx.h" #include "com_image.h" +#include "filesystem.h" -#define REF_API_VERSION 1 +// RefAPI changelog: +// 1. Initial release +// 2. FS functions are removed, instead we have full fs_api_t +#define REF_API_VERSION 2 #define TF_SKY (TF_SKYSIDE|TF_NOMIPMAP) @@ -367,13 +371,6 @@ typedef struct ref_api_s void (*COM_FreeLibrary)( void *handle ); void *(*COM_GetProcAddress)( void *handle, const char *name ); - // filesystem - byte* (*COM_LoadFile)( const char *path, fs_offset_t *pLength, qboolean gamedironly ); - // use Mem_Free instead - // void (*COM_FreeFile)( void *buffer ); - int (*FS_FileExists)( const char *filename, int gamedironly ); - void (*FS_AllowDirectPaths)( qboolean enable ); - // video init // try to create window // will call GL_SetupAttributes in case of REF_GL @@ -430,6 +427,9 @@ typedef struct ref_api_s void (*pfnDrawNormalTriangles)( void ); void (*pfnDrawTransparentTriangles)( void ); render_interface_t *drawFuncs; + + // filesystem exports + fs_api_t *fsapi; } ref_api_t; struct mip_s; diff --git a/engine/wscript b/engine/wscript index 4d2c9eed..972d8804 100644 --- a/engine/wscript +++ b/engine/wscript @@ -166,7 +166,7 @@ def build(bld): 'client/vgui/*.c', 'client/avi/*.c']) - includes = ['common', 'server', 'client', 'client/vgui', 'tests', '.', '../public', '../common', '../pm_shared' ] + 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/engine/common/filesystem.c b/filesystem/filesystem.c similarity index 56% rename from engine/common/filesystem.c rename to filesystem/filesystem.c index 734255e7..6f755d92 100644 --- a/engine/common/filesystem.c +++ b/filesystem/filesystem.c @@ -27,139 +27,39 @@ GNU General Public License for more details. #include #include #endif -#include "miniz.h" // header-only zlib replacement -#include "common.h" -#include "wadfile.h" +#include +#include +#include "port.h" +#include "const.h" +#include "crtlib.h" +#include "crclib.h" #include "filesystem.h" -#include "library.h" +#include "filesystem_internal.h" #include "xash3d_mathlib.h" -#include "protocol.h" +#include "common/com_strings.h" +#include "common/protocol.h" #define FILE_COPY_SIZE (1024 * 1024) -#define FILE_BUFF_SIZE (2048) +qboolean fs_ext_path = false; // attempt to read\write from ./ or ../ pathes +poolhandle_t fs_mempool; +searchpath_t *fs_searchpaths = NULL; // chain +char fs_rodir[MAX_SYSPATH]; +char fs_rootdir[MAX_SYSPATH]; -// PAK errors -#define PAK_LOAD_OK 0 -#define PAK_LOAD_COULDNT_OPEN 1 -#define PAK_LOAD_BAD_HEADER 2 -#define PAK_LOAD_BAD_FOLDERS 3 -#define PAK_LOAD_TOO_MANY_FILES 4 -#define PAK_LOAD_NO_FILES 5 -#define PAK_LOAD_CORRUPTED 6 - -// WAD errors -#define WAD_LOAD_OK 0 -#define WAD_LOAD_COULDNT_OPEN 1 -#define WAD_LOAD_BAD_HEADER 2 -#define WAD_LOAD_BAD_FOLDERS 3 -#define WAD_LOAD_TOO_MANY_FILES 4 -#define WAD_LOAD_NO_FILES 5 -#define WAD_LOAD_CORRUPTED 6 - -// 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 - -typedef struct stringlist_s -{ - // maxstrings changes as needed, causing reallocation of strings[] array - int maxstrings; - int numstrings; - char **strings; -} stringlist_t; - -typedef struct wadtype_s -{ - const char *ext; - signed char type; -} wadtype_t; - -struct file_s -{ - int handle; // file descriptor - int ungetc; // single stored character from ungetc, cleared to EOF when read - fs_offset_t real_length; // uncompressed file size (for files opened in "read" mode) - fs_offset_t position; // current position in the file - fs_offset_t offset; // offset into the package (0 if external file) - time_t filetime; // pak, wad or real filetime - // contents buffer - fs_offset_t buff_ind, buff_len; // buffer current index and length - byte buff[FILE_BUFF_SIZE]; // intermediate buffer -#ifdef XASH_REDUCE_FD - const char *backup_path; - fs_offset_t backup_position; - uint backup_options; -#endif -}; - -struct wfile_s -{ - string filename; - int infotableofs; - int numlumps; - poolhandle_t mempool; // W_ReadLump temp buffers - file_t *handle; - dlumpinfo_t *lumps; - time_t filetime; -}; - -typedef struct pack_s -{ - string filename; - int handle; - int numfiles; - time_t filetime; // common for all packed files - dpackfile_t *files; -} pack_t; - -typedef struct zipfile_s -{ - char name[MAX_SYSPATH]; - 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; -} zipfile_t; - -typedef struct zip_s -{ - string filename; - int handle; - int numfiles; - time_t filetime; - zipfile_t *files; -} zip_t; - -typedef struct searchpath_s -{ - string filename; - pack_t *pack; - wfile_t *wad; - zip_t *zip; - int flags; - struct searchpath_s *next; -} searchpath_t; - -static poolhandle_t fs_mempool; -static searchpath_t *fs_searchpaths = NULL; // chain 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 static char fs_writedir[MAX_SYSPATH]; // path that game allows to overwrite, delete and rename files (and create new of course) - -static qboolean fs_ext_path = false; // attempt to read\write from ./ or ../ pathes #if !XASH_WIN32 static qboolean fs_caseinsensitive = true; // try to search missing files #endif +static fs_globals_t FI; +#define GI FI.GameInfo + #ifdef XASH_REDUCE_FD static file_t *fs_last_readfile; static zip_t *fs_last_zip; -static pack_t *fs_last_pak; static void FS_EnsureOpenFile( file_t *file ) { @@ -183,21 +83,6 @@ static void FS_EnsureOpenFile( file_t *file ) } } -static void FS_EnsureOpenZip( zip_t *zip ) -{ - if( fs_last_zip == zip ) - return; - - if( fs_last_zip && (fs_last_zip->handle != -1) ) - { - close( fs_last_zip->handle ); - fs_last_zip->handle = -1; - } - fs_last_zip = zip; - if( zip && (zip->handle == -1) ) - zip->handle = open( zip->filename, O_RDONLY|O_BINARY ); -} - static void FS_BackupFileName( file_t *file, const char *path, uint options ) { if( path == NULL ) @@ -217,21 +102,10 @@ static void FS_BackupFileName( file_t *file, const char *path, uint options ) #else static void FS_EnsureOpenFile( file_t *file ) {} -static void FS_EnsureOpenZip( zip_t *zip ) {} static void FS_BackupFileName( file_t *file, const char *path, uint options ) {} #endif static void FS_InitMemory( void ); -static searchpath_t *FS_FindFile( const char *name, int *index, qboolean gamedironly ); -static dlumpinfo_t *W_FindLump( wfile_t *wad, const char *name, const signed char matchtype ); -static dpackfile_t *FS_AddFileToPack( const char* name, pack_t *pack, fs_offset_t offset, fs_offset_t size ); -void Zip_Close( zip_t *zip ); -static byte *W_LoadFile( const char *path, fs_offset_t *filesizeptr, qboolean gamedironly ); -static wfile_t *W_Open( const char *filename, int *errorcode ); -static qboolean FS_SysFolderExists( const char *path ); -static int FS_SysFileTime( const char *filename ); -static signed char W_TypeFromExt( const char *lumpname ); -static const char *W_ExtFromType( signed char lumptype ); static void FS_Purge( file_t* file ); /* @@ -265,7 +139,7 @@ static void stringlistfreecontents( stringlist_t *list ) list->strings = NULL; } -static void stringlistappend( stringlist_t *list, char *text ) +void stringlistappend( stringlist_t *list, char *text ) { size_t textlen; @@ -374,7 +248,7 @@ FS_FixFileCase emulate WIN32 FS behaviour when opening local file ================== */ -static const char *FS_FixFileCase( const char *path ) +const char *FS_FixFileCase( const char *path ) { #if defined __DOS__ & !defined __WATCOM_LFN__ // not fix, but convert to 8.3 CAPS and rotate slashes @@ -428,7 +302,7 @@ static const char *FS_FixFileCase( const char *path ) } /* android has too slow directory scanning, - so drop out some not useful cases */ + so drop out some not useful cases */ if( fname - path2 > 4 ) { char *point; @@ -472,7 +346,7 @@ FS_PathToWideChar Converts input UTF-8 string to wide char string. ==================== */ -const wchar_t *FS_PathToWideChar( const char *path ) +static const wchar_t *FS_PathToWideChar( const char *path ) { static wchar_t pathBuffer[MAX_PATH]; MultiByteToWideChar( CP_UTF8, 0, path, -1, pathBuffer, MAX_PATH ); @@ -480,49 +354,6 @@ const wchar_t *FS_PathToWideChar( const char *path ) } #endif -/* -==================== -FS_AddFileToPack - -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 ) -{ - int left, right, middle; - dpackfile_t *pfile; - - // 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; -} - /* ============ FS_CreatePath @@ -547,703 +378,7 @@ void FS_CreatePath( char *path ) } } -/* -============ -FS_Path_f -debug info -============ -*/ -void FS_Path_f( void ) -{ - searchpath_t *s; - - Con_Printf( "Current search path:\n" ); - - for( s = fs_searchpaths; s; s = s->next ) - { - if( s->pack ) Con_Printf( "%s (%i files)", s->pack->filename, s->pack->numfiles ); - else if( s->wad ) Con_Printf( "%s (%i files)", s->wad->filename, s->wad->numlumps ); - else if( s->zip ) Con_Printf( "%s (%i files)", s->zip->filename, s->zip->numfiles ); - else Con_Printf( "%s", s->filename ); - - if( s->flags & FS_GAMERODIR_PATH ) Con_Printf( " ^2rodir^7" ); - if( s->flags & FS_GAMEDIR_PATH ) Con_Printf( " ^2gamedir^7" ); - if( s->flags & FS_CUSTOM_PATH ) Con_Printf( " ^2custom^7" ); - if( s->flags & FS_NOWRITE_PATH ) Con_Printf( " ^2nowrite^7" ); - if( s->flags & FS_STATIC_PATH ) Con_Printf( " ^2static^7" ); - - Con_Printf( "\n" ); - } -} - -/* -============ -FS_ClearPath_f - -only for debug targets -============ -*/ -void FS_ClearPaths_f( void ) -{ - FS_ClearSearchPath(); -} - -/* -================= -FS_LoadPackPAK - -Takes an explicit (not game tree related) path to a pak file. - -Loads the header and directory, adding the files at the beginning -of the list so they override previous pack files. -================= -*/ -pack_t *FS_LoadPackPAK( const char *packfile, int *error ) -{ - dpackheader_t header; - int packhandle; - int i, numpackfiles; - pack_t *pack; - dpackfile_t *info; - fs_size_t c; - - 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 )); - if( error ) *error = PAK_LOAD_COULDNT_OPEN; - return NULL; - } - - c = read( packhandle, (void *)&header, sizeof( header )); - - if( c != sizeof( header ) || header.ident != IDPACKV1HEADER ) - { - Con_Reportf( "%s is not a packfile. Ignored.\n", packfile ); - if( error ) *error = PAK_LOAD_BAD_HEADER; - close( packhandle ); - return NULL; - } - - if( header.dirlen % sizeof( dpackfile_t )) - { - Con_Reportf( S_ERROR "%s has an invalid directory size. Ignored.\n", packfile ); - if( error ) *error = PAK_LOAD_BAD_FOLDERS; - close( packhandle ); - return NULL; - } - - numpackfiles = header.dirlen / sizeof( dpackfile_t ); - - if( numpackfiles > MAX_FILES_IN_PACK ) - { - Con_Reportf( S_ERROR "%s has too many files ( %i ). Ignored.\n", packfile, numpackfiles ); - if( error ) *error = PAK_LOAD_TOO_MANY_FILES; - close( packhandle ); - return NULL; - } - - if( numpackfiles <= 0 ) - { - Con_Reportf( "%s has no files. Ignored.\n", packfile ); - if( error ) *error = PAK_LOAD_NO_FILES; - close( packhandle ); - return NULL; - } - - info = (dpackfile_t *)Mem_Malloc( fs_mempool, sizeof( *info ) * numpackfiles ); - lseek( packhandle, header.dirofs, SEEK_SET ); - - if( header.dirlen != read( packhandle, (void *)info, header.dirlen )) - { - Con_Reportf( "%s is an incomplete PAK, not loading\n", packfile ); - if( error ) *error = PAK_LOAD_CORRUPTED; - close( packhandle ); - Mem_Free( info ); - 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 )); - 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 ); - -#ifdef XASH_REDUCE_FD - // will reopen when needed - close( pack->handle ); - pack->handle = -1; -#endif - - if( error ) *error = PAK_LOAD_OK; - Mem_Free( info ); - - return pack; -} - -/* -============ -FS_SortZip -============ -*/ -static int FS_SortZip( const void *a, const void *b ) -{ - return Q_stricmp( ( ( zipfile_t* )a )->name, ( ( zipfile_t* )b )->name ); -} - -/* -============ -FS_LoadZip -============ -*/ -static zip_t *FS_LoadZip( const char *zipfile, int *error ) -{ - int numpackfiles = 0, i; - zip_cdf_header_t header_cdf; - zip_header_eocd_t header_eocd; - uint32_t signature; - fs_offset_t filepos = 0, length; - zipfile_t *info = NULL; - char filename_buffer[MAX_SYSPATH]; - zip_t *zip = (zip_t *)Mem_Calloc( fs_mempool, sizeof( *zip )); - fs_size_t c; - - 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 ); - - if( error ) - *error = ZIP_LOAD_COULDNT_OPEN; - - Zip_Close( zip ); - return NULL; - } - - length = lseek( zip->handle, 0, SEEK_END ); - - if( length > UINT_MAX ) - { - Con_Reportf( S_ERROR "%s bigger than 4GB.\n", zipfile ); - - if( error ) - *error = ZIP_LOAD_COULDNT_OPEN; - - Zip_Close( zip ); - return NULL; - } - - lseek( zip->handle, 0, SEEK_SET ); - - c = read( zip->handle, &signature, sizeof( signature ) ); - - if( c != sizeof( signature ) || signature == ZIP_HEADER_EOCD ) - { - Con_Reportf( S_WARN "%s has no files. Ignored.\n", zipfile ); - - if( error ) - *error = ZIP_LOAD_NO_FILES; - - Zip_Close( zip ); - return NULL; - } - - if( signature != ZIP_HEADER_LF ) - { - Con_Reportf( S_ERROR "%s is not a zip file. Ignored.\n", zipfile ); - - if( error ) - *error = ZIP_LOAD_BAD_HEADER; - - Zip_Close( zip ); - return NULL; - } - - // Find oecd - lseek( zip->handle, 0, SEEK_SET ); - filepos = length; - - while ( filepos > 0 ) - { - lseek( zip->handle, filepos, SEEK_SET ); - c = read( zip->handle, &signature, sizeof( signature ) ); - - if( c == sizeof( signature ) && signature == ZIP_HEADER_EOCD ) - break; - - filepos -= sizeof( char ); // step back one byte - } - - if( ZIP_HEADER_EOCD != signature ) - { - Con_Reportf( S_ERROR "cannot find EOCD in %s. Zip file corrupted.\n", zipfile ); - - if( error ) - *error = ZIP_LOAD_BAD_HEADER; - - Zip_Close( zip ); - return NULL; - } - - c = read( zip->handle, &header_eocd, sizeof( header_eocd ) ); - - if( c != sizeof( header_eocd )) - { - Con_Reportf( S_ERROR "invalid EOCD header in %s. Zip file corrupted.\n", zipfile ); - - if( error ) - *error = ZIP_LOAD_BAD_HEADER; - - Zip_Close( zip ); - return NULL; - } - - // Move to CDF start - lseek( zip->handle, header_eocd.central_directory_offset, SEEK_SET ); - - // Calc count of files in archive - info = (zipfile_t *)Mem_Calloc( fs_mempool, sizeof( *info ) * header_eocd.total_central_directory_record ); - - for( i = 0; i < header_eocd.total_central_directory_record; i++ ) - { - c = read( zip->handle, &header_cdf, sizeof( header_cdf ) ); - - if( c != sizeof( header_cdf ) || header_cdf.signature != ZIP_HEADER_CDF ) - { - Con_Reportf( S_ERROR "CDF signature mismatch in %s. Zip file corrupted.\n", zipfile ); - - if( error ) - *error = ZIP_LOAD_BAD_HEADER; - - Mem_Free( info ); - Zip_Close( zip ); - return NULL; - } - - if( header_cdf.uncompressed_size && header_cdf.filename_len && ( header_cdf.filename_len < MAX_SYSPATH ) ) - { - memset( &filename_buffer, '\0', MAX_SYSPATH ); - c = read( zip->handle, &filename_buffer, header_cdf.filename_len ); - - if( c != header_cdf.filename_len ) - { - Con_Reportf( S_ERROR "filename length mismatch in %s. Zip file corrupted.\n", zipfile ); - - if( error ) - *error = ZIP_LOAD_CORRUPTED; - - Mem_Free( info ); - Zip_Close( zip ); - return NULL; - } - - Q_strncpy( info[numpackfiles].name, filename_buffer, MAX_SYSPATH ); - - info[numpackfiles].size = header_cdf.uncompressed_size; - info[numpackfiles].compressed_size = header_cdf.compressed_size; - info[numpackfiles].offset = header_cdf.local_header_offset; - numpackfiles++; - } - else - lseek( zip->handle, header_cdf.filename_len, SEEK_CUR ); - - if( header_cdf.extrafield_len ) - lseek( zip->handle, header_cdf.extrafield_len, SEEK_CUR ); - - if( header_cdf.file_commentary_len ) - lseek( zip->handle, header_cdf.file_commentary_len, SEEK_CUR ); - } - - // recalculate offsets - for( i = 0; i < numpackfiles; i++ ) - { - zip_header_t header; - - lseek( zip->handle, info[i].offset, SEEK_SET ); - c = read( zip->handle, &header, sizeof( header ) ); - - if( c != sizeof( header )) - { - Con_Reportf( S_ERROR "header length mismatch in %s. Zip file corrupted.\n", zipfile ); - - if( error ) - *error = ZIP_LOAD_CORRUPTED; - - Mem_Free( info ); - Zip_Close( zip ); - return NULL; - } - - info[i].flags = header.compression_flags; - 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; - - qsort( zip->files, zip->numfiles, sizeof( *zip->files ), FS_SortZip ); - -#ifdef XASH_REDUCE_FD - // will reopen when needed - close(zip->handle); - zip->handle = -1; -#endif - - if( error ) - *error = ZIP_LOAD_OK; - - return zip; -} - -void Zip_Close( zip_t *zip ) -{ - if( !zip ) - return; - - if( zip->files ) - Mem_Free( zip->files ); - - FS_EnsureOpenZip( NULL ); - - if( zip->handle >= 0 ) - close( zip->handle ); - - Mem_Free( zip ); -} - -static byte *Zip_LoadFile( const char *path, fs_offset_t *sizeptr, qboolean gamedironly ) -{ - searchpath_t *search; - int index; - zipfile_t *file = NULL; - byte *compressed_buffer = NULL, *decompressed_buffer = NULL; - int zlib_result = 0; - dword test_crc, final_crc; - z_stream decompress_stream; - size_t c; - - if( sizeptr ) *sizeptr = 0; - - search = FS_FindFile( path, &index, gamedironly ); - - if( !search || !search->zip ) - return NULL; - - file = &search->zip->files[index]; - - FS_EnsureOpenZip( search->zip ); - - if( lseek( search->zip->handle, file->offset, SEEK_SET ) == -1 ) - return NULL; - - /*if( read( search->zip->handle, &header, sizeof( header ) ) < 0 ) - return NULL; - - if( header.signature != ZIP_HEADER_LF ) - { - Con_Reportf( S_ERROR "Zip_LoadFile: %s signature error\n", file->name ); - return NULL; - }*/ - - if( file->flags == ZIP_COMPRESSION_NO_COMPRESSION ) - { - decompressed_buffer = Mem_Malloc( fs_mempool, file->size + 1 ); - decompressed_buffer[file->size] = '\0'; - - c = read( search->zip->handle, decompressed_buffer, file->size ); - if( c != file->size ) - { - Con_Reportf( S_ERROR "Zip_LoadFile: %s size doesn't match\n", file->name ); - return NULL; - } - -#if 0 - CRC32_Init( &test_crc ); - CRC32_ProcessBuffer( &test_crc, decompressed_buffer, file->size ); - - final_crc = CRC32_Final( test_crc ); - - if( final_crc != file->crc32 ) - { - Con_Reportf( S_ERROR "Zip_LoadFile: %s file crc32 mismatch\n", file->name ); - Mem_Free( decompressed_buffer ); - return NULL; - } -#endif - if( sizeptr ) *sizeptr = file->size; - - FS_EnsureOpenZip( NULL ); - return decompressed_buffer; - } - else if( file->flags == ZIP_COMPRESSION_DEFLATED ) - { - compressed_buffer = Mem_Malloc( fs_mempool, file->compressed_size + 1 ); - decompressed_buffer = Mem_Malloc( fs_mempool, file->size + 1 ); - decompressed_buffer[file->size] = '\0'; - - c = read( search->zip->handle, compressed_buffer, file->compressed_size ); - if( c != file->compressed_size ) - { - Con_Reportf( S_ERROR "Zip_LoadFile: %s compressed size doesn't match\n", file->name ); - return NULL; - } - - memset( &decompress_stream, 0, sizeof( decompress_stream ) ); - - decompress_stream.total_in = decompress_stream.avail_in = file->compressed_size; - decompress_stream.next_in = (Bytef *)compressed_buffer; - decompress_stream.total_out = decompress_stream.avail_out = file->size; - decompress_stream.next_out = (Bytef *)decompressed_buffer; - - decompress_stream.zalloc = Z_NULL; - decompress_stream.zfree = Z_NULL; - decompress_stream.opaque = Z_NULL; - - if( inflateInit2( &decompress_stream, -MAX_WBITS ) != Z_OK ) - { - Con_Printf( S_ERROR "Zip_LoadFile: inflateInit2 failed\n" ); - Mem_Free( compressed_buffer ); - Mem_Free( decompressed_buffer ); - return NULL; - } - - zlib_result = inflate( &decompress_stream, Z_NO_FLUSH ); - inflateEnd( &decompress_stream ); - - if( zlib_result == Z_OK || zlib_result == Z_STREAM_END ) - { - Mem_Free( compressed_buffer ); // finaly free compressed buffer -#if 0 - CRC32_Init( &test_crc ); - CRC32_ProcessBuffer( &test_crc, decompressed_buffer, file->size ); - - final_crc = CRC32_Final( test_crc ); - - if( final_crc != file->crc32 ) - { - Con_Reportf( S_ERROR "Zip_LoadFile: %s file crc32 mismatch\n", file->name ); - Mem_Free( decompressed_buffer ); - return NULL; - } -#endif - if( sizeptr ) *sizeptr = file->size; - - FS_EnsureOpenZip( NULL ); - return decompressed_buffer; - } - else - { - Con_Reportf( S_ERROR "Zip_LoadFile: %s : error while file decompressing. Zlib return code %d.\n", file->name, zlib_result ); - Mem_Free( compressed_buffer ); - Mem_Free( decompressed_buffer ); - return NULL; - } - - } - else - { - Con_Reportf( S_ERROR "Zip_LoadFile: %s : file compressed with unknown algorithm.\n", file->name ); - return NULL; - } - - FS_EnsureOpenZip( NULL ); - return NULL; -} - -/* -==================== -FS_AddWad_Fullpath -==================== -*/ -static 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->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->next = fs_searchpaths; - search->flags |= flags; - 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; - } -} - -/* -================ -FS_AddPak_Fullpath - -Adds the given pack to the search path. -The pack type is autodetected by the file extension. - -Returns true if the file was successfully added to the -search path or if it was already included. - -If keep_plain_dirs is set, the pack will be added AFTER the first sequence of -plain directories. -================ -*/ -static qboolean FS_AddPak_Fullpath( const char *pakfile, qboolean *already_loaded, int flags ) -{ - searchpath_t *search; - pack_t *pak = NULL; - const char *ext = COM_FileExtension( pakfile ); - int i, errorcode = PAK_LOAD_COULDNT_OPEN; - - for( search = fs_searchpaths; search; search = search->next ) - { - if( search->pack && !Q_stricmp( search->pack->filename, pakfile )) - { - if( already_loaded ) *already_loaded = true; - return true; // already loaded - } - } - - if( already_loaded ) - *already_loaded = false; - - if( !Q_stricmp( ext, "pak" )) - pak = FS_LoadPackPAK( pakfile, &errorcode ); - - if( pak ) - { - string fullpath; - - search = (searchpath_t *)Mem_Calloc( fs_mempool, sizeof( searchpath_t )); - search->pack = pak; - search->next = fs_searchpaths; - search->flags |= flags; - fs_searchpaths = search; - - Con_Reportf( "Adding pakfile: %s (%i files)\n", pakfile, pak->numfiles ); - - // time to add in search list all the wads that contains in current pakfile (if do) - for( i = 0; i < pak->numfiles; i++ ) - { - if( !Q_stricmp( COM_FileExtension( pak->files[i].name ), "wad" )) - { - Q_snprintf( fullpath, MAX_STRING, "%s/%s", pakfile, pak->files[i].name ); - FS_AddWad_Fullpath( fullpath, NULL, flags ); - } - } - - return true; - } - else - { - if( errorcode != PAK_LOAD_NO_FILES ) - Con_Reportf( S_ERROR "FS_AddPak_Fullpath: unable to load pak \"%s\"\n", pakfile ); - return false; - } -} - -static qboolean FS_AddZip_Fullpath( const char *zipfile, qboolean *already_loaded, int flags ) -{ - searchpath_t *search; - zip_t *zip = NULL; - const char *ext = COM_FileExtension( zipfile ); - int errorcode = ZIP_LOAD_COULDNT_OPEN; - - for( search = fs_searchpaths; search; search = search->next ) - { - if( search->pack && !Q_stricmp( search->pack->filename, zipfile )) - { - if( already_loaded ) *already_loaded = true; - return true; // already loaded - } - } - - if( already_loaded ) *already_loaded = false; - - if( !Q_stricmp( ext, "pk3" ) ) - zip = FS_LoadZip( zipfile, &errorcode ); - - if( zip ) - { - string fullpath; - int i; - - search = (searchpath_t *)Mem_Calloc( fs_mempool, sizeof( searchpath_t ) ); - search->zip = zip; - search->next = fs_searchpaths; - search->flags |= flags; - fs_searchpaths = search; - - Con_Reportf( "Adding zipfile: %s (%i files)\n", zipfile, zip->numfiles ); - - // time to add in search list all the wads that contains in current pakfile (if do) - for( i = 0; i < zip->numfiles; i++ ) - { - if( !Q_stricmp( COM_FileExtension( zip->files[i].name ), "wad" )) - { - Q_snprintf( fullpath, MAX_STRING, "%s/%s", zipfile, zip->files[i].name ); - FS_AddWad_Fullpath( fullpath, NULL, flags ); - } - } - return true; - } - else - { - if( errorcode != ZIP_LOAD_NO_FILES ) - Con_Reportf( S_ERROR "FS_AddZip_Fullpath: unable to load zip \"%s\"\n", zipfile ); - return false; - } -} /* ================ @@ -1324,62 +459,11 @@ void FS_AddGameDirectory( const char *dir, uint flags ) 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; fs_searchpaths = search; } -/* -================ -FS_AddGameHierarchy -================ -*/ -void FS_AddGameHierarchy( const char *dir, uint flags ) -{ - int i; - qboolean isGameDir = flags & FS_GAMEDIR_PATH; - - GI->added = true; - - if( !COM_CheckString( dir )) - return; - - // add the common game directory - - // recursive gamedirs - // for example, czeror->czero->cstrike->valve - for( i = 0; i < SI.numgames; i++ ) - { - if( !Q_strnicmp( SI.games[i]->gamefolder, dir, 64 )) - { - Con_Reportf( "FS_AddGameHierarchy: %d %s %s\n", i, SI.games[i]->gamefolder, SI.games[i]->basedir ); - if( !SI.games[i]->added && Q_stricmp( SI.games[i]->gamefolder, SI.games[i]->basedir ) ) - { - SI.games[i]->added = true; - FS_AddGameHierarchy( SI.games[i]->basedir, flags & (~FS_GAMEDIR_PATH) ); - } - break; - } - } - - if( COM_CheckStringEmpty( host.rodir ) ) - { - // append new flags to rodir, except FS_GAMEDIR_PATH and FS_CUSTOM_PATH - uint newFlags = FS_NOWRITE_PATH | (flags & (~FS_GAMEDIR_PATH|FS_CUSTOM_PATH)); - if( isGameDir ) - newFlags |= FS_GAMERODIR_PATH; - - FS_AllowDirectPaths( true ); - FS_AddGameDirectory( va( "%s/%s/", host.rodir, dir ), newFlags ); - FS_AllowDirectPaths( false ); - } - - if( isGameDir ) - FS_AddGameDirectory( va( "%s/downloaded/", dir ), FS_NOWRITE_PATH | FS_CUSTOM_PATH ); - FS_AddGameDirectory( va( "%s/", dir ), flags ); - if( isGameDir ) - FS_AddGameDirectory( va( "%s/custom/", dir ), FS_NOWRITE_PATH | FS_CUSTOM_PATH ); -} - /* ================ FS_ClearSearchPath @@ -1402,23 +486,19 @@ void FS_ClearSearchPath( void ) } else fs_searchpaths = search->next; - if( search->pack ) + switch( search->type ) { - if( search->pack->files ) - Mem_Free( search->pack->files ); - if( search->pack->handle >= 0 ) - close( search->pack->handle ); - Mem_Free( search->pack ); - } - - if( search->wad ) - { - W_Close( search->wad ); - } - - if( search->zip ) - { - Zip_Close(search->zip); + 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; } Mem_Free( search ); @@ -1428,19 +508,18 @@ void FS_ClearSearchPath( void ) /* ==================== FS_CheckNastyPath - Return true if the path should be rejected due to one of the following: 1: path elements that are non-portable 2: path elements that would allow access to files outside the game directory, - or are just not a good idea for a mod to be using. + or are just not a good idea for a mod to be using. ==================== */ -int FS_CheckNastyPath( const char *path, qboolean isgamedir ) +int FS_CheckNastyPath (const char *path, qboolean isgamedir) { // all: never allow an empty path, as for gamedir it would access the parent directory and a non-gamedir path it is just useless if( !COM_CheckString( path )) return 2; - if( fs_ext_path ) return 0; // allow any path + if( fs_ext_path ) return 0; // allow any path // Mac: don't allow Mac-only filenames - : is a directory separator // instead of /, but we rely on / working already, so there's no reason to @@ -1449,72 +528,23 @@ int FS_CheckNastyPath( const char *path, qboolean isgamedir ) if( Q_strchr( path, ':' )) return 1; // non-portable attempt to go to root of drive // Amiga: // is parent directory - if( Q_strstr( path, "//" )) return 1; // non-portable attempt to go to parent directory + if( Q_strstr( path, "//")) return 1; // non-portable attempt to go to parent directory // all: don't allow going to parent directory (../ or /../) - if( Q_strstr( path, ".." )) return 2; // attempt to go outside the game directory + if( Q_strstr( path, "..")) return 2; // attempt to go outside the game directory // Windows and UNIXes: don't allow absolute paths - if( path[0] == '/' ) return 2; // attempt to go outside the game directory + if( path[0] == '/') return 2; // attempt to go outside the game directory // all: forbid trailing slash on gamedir - if( isgamedir && path[Q_strlen( path )-1] == '/' ) return 2; + if( isgamedir && path[Q_strlen(path)-1] == '/' ) return 2; // all: forbid leading dot on any filename for any reason - if( Q_strstr( path, "/." )) return 2; // attempt to go outside the game directory + if( Q_strstr(path, "/.")) return 2; // attempt to go outside the game directory // after all these checks we're pretty sure it's a / separated filename // and won't do much if any harm - return 0; -} - -/* -================ -FS_Rescan -================ -*/ -void FS_Rescan( void ) -{ - const char *str; - const int extrasFlags = FS_NOWRITE_PATH | FS_CUSTOM_PATH; - Con_Reportf( "FS_Rescan( %s )\n", GI->title ); - - FS_ClearSearchPath(); - -#if XASH_IOS - { - FS_AddPak_Fullpath( va( "%sextras.pak", SDL_GetBasePath() ), NULL, extrasFlags ); - FS_AddPak_Fullpath( va( "%sextras_%s.pak", SDL_GetBasePath(), GI->gamefolder ), NULL, extrasFlags ); - } -#else - str = getenv( "XASH3D_EXTRAS_PAK1" ); - if( COM_CheckString( str )) - FS_AddArchive_Fullpath( str, NULL, extrasFlags ); - - str = getenv( "XASH3D_EXTRAS_PAK2" ); - if( COM_CheckString( str )) - 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 )) - FS_AddGameHierarchy( GI->falldir, 0 ); - FS_AddGameHierarchy( GI->gamefolder, FS_GAMEDIR_PATH ); - - if( FS_FileExists( va( "%s.rc", SI.basedirName ), false )) - Q_strncpy( SI.rcName, SI.basedirName, sizeof( SI.rcName )); // e.g. valve.rc - else Q_strncpy( SI.rcName, SI.exeName, sizeof( SI.rcName )); // e.g. quake.rc -} - -/* -================ -FS_Rescan_f -================ -*/ -void FS_Rescan_f( void ) -{ - FS_Rescan(); + return false; } /* @@ -1886,7 +916,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", host.rootdir, GameInfo->falldir ))) + if( !FS_SysFolderExists( va( "%s"PATH_SPLITTER"%s", fs_rootdir, GameInfo->falldir ))) GameInfo->falldir[0] = '\0'; } @@ -2011,15 +1041,15 @@ 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( host.rodir ) ) + if( COM_CheckStringEmpty( fs_rodir ) ) { string filepath_ro, liblist_ro; fs_offset_t roLibListTime, roGameInfoTime, rwGameInfoTime; FS_AllowDirectPaths( true ); - Q_snprintf( filepath_ro, sizeof( filepath_ro ), "%s/%s/gameinfo.txt", host.rodir, gamedir ); - Q_snprintf( liblist_ro, sizeof( liblist_ro ), "%s/%s/liblist.gam", host.rodir, gamedir ); + Q_snprintf( filepath_ro, sizeof( filepath_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 ); @@ -2076,6 +1106,93 @@ static qboolean FS_ParseGameInfo( const char *gamedir, gameinfo_t *GameInfo ) return false; } +/* +================ +FS_AddGameHierarchy +================ +*/ +void FS_AddGameHierarchy( const char *dir, uint flags ) +{ + int i; + qboolean isGameDir = flags & FS_GAMEDIR_PATH; + + GI->added = true; + + if( !COM_CheckString( dir )) + return; + + // add the common game directory + + // recursive gamedirs + // for example, czeror->czero->cstrike->valve + for( i = 0; i < FI.numgames; i++ ) + { + 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 ); + if( !FI.games[i]->added && Q_stricmp( FI.games[i]->gamefolder, FI.games[i]->basedir )) + { + FI.games[i]->added = true; + FS_AddGameHierarchy( FI.games[i]->basedir, flags & (~FS_GAMEDIR_PATH) ); + } + break; + } + } + + if( COM_CheckStringEmpty( fs_rodir ) ) + { + // append new flags to rodir, except FS_GAMEDIR_PATH and FS_CUSTOM_PATH + uint newFlags = FS_NOWRITE_PATH | (flags & (~FS_GAMEDIR_PATH|FS_CUSTOM_PATH)); + if( isGameDir ) + newFlags |= FS_GAMERODIR_PATH; + + FS_AllowDirectPaths( true ); + FS_AddGameDirectory( va( "%s/%s/", fs_rodir, dir ), newFlags ); + FS_AllowDirectPaths( false ); + } + + if( isGameDir ) + FS_AddGameDirectory( va( "%s/downloaded/", dir ), FS_NOWRITE_PATH | FS_CUSTOM_PATH ); + FS_AddGameDirectory( va( "%s/", dir ), flags ); + if( isGameDir ) + FS_AddGameDirectory( va( "%s/custom/", dir ), FS_NOWRITE_PATH | FS_CUSTOM_PATH ); +} + +/* +================ +FS_Rescan +================ +*/ +void FS_Rescan( void ) +{ + const char *str; + const int extrasFlags = FS_NOWRITE_PATH | FS_CUSTOM_PATH; + Con_Reportf( "FS_Rescan( %s )\n", GI->title ); + + FS_ClearSearchPath(); + +#if XASH_IOS + { + FS_AddPak_Fullpath( va( "%sextras.pak", SDL_GetBasePath() ), NULL, extrasFlags ); + FS_AddPak_Fullpath( va( "%sextras_%s.pak", SDL_GetBasePath(), GI->gamefolder ), NULL, extrasFlags ); + } +#else + str = getenv( "XASH3D_EXTRAS_PAK1" ); + if( COM_CheckString( str )) + FS_AddArchive_Fullpath( str, NULL, extrasFlags ); + + str = getenv( "XASH3D_EXTRAS_PAK2" ); + if( COM_CheckString( str )) + 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 )) + FS_AddGameHierarchy( GI->falldir, 0 ); + FS_AddGameHierarchy( GI->gamefolder, FS_GAMEDIR_PATH ); +} + /* ================ FS_LoadGameInfo @@ -2097,39 +1214,176 @@ void FS_LoadGameInfo( const char *rootfolder ) FS_ClearSearchPath(); // validate gamedir - for( i = 0; i < SI.numgames; i++ ) + for( i = 0; i < FI.numgames; i++ ) { - if( !Q_stricmp( SI.games[i]->gamefolder, fs_gamedir )) + if( !Q_stricmp( FI.games[i]->gamefolder, fs_gamedir )) break; } - if( i == SI.numgames ) + if( i == FI.numgames ) Sys_Error( "Couldn't find game directory '%s'\n", fs_gamedir ); - SI.GameInfo = SI.games[i]; - - if( !Sys_GetParmFromCmdLine( "-dll", SI.gamedll ) ) - { - SI.gamedll[0] = 0; - } - - if( !Sys_GetParmFromCmdLine( "-clientlib", SI.clientlib ) ) - { - SI.clientlib[0] = 0; - } + FI.GameInfo = FI.games[i]; FS_Rescan(); // create new filesystem - - Image_CheckPaletteQ1 (); - Host_InitDecals (); // reload decals } +/* +================== +FS_CheckForCrypt + +return true if library is crypted +================== +*/ +static qboolean FS_CheckForCrypt( const char *dllname ) +{ + file_t *f; + int key; + + f = FS_Open( dllname, "rb", false ); + if( !f ) return false; + + FS_Seek( f, 64, SEEK_SET ); // skip first 64 bytes + FS_Read( f, &key, sizeof( key )); + FS_Close( f ); + + return ( key == 0x12345678 ) ? true : false; +} + +/* +================== +FS_FindLibrary + +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; + + fs_ext_path = directpath; + + // check for bad exports + if( !COM_CheckString( dllname )) + return false; + + // HACKHACK remove absoulte path to valve folder + if( !Q_strnicmp( dllname, "..\\valve\\", 9 ) || !Q_strnicmp( dllname, "../valve/", 9 )) + start += 9; + + // replace all backward slashes + len = Q_strlen( dllname ); + + for( i = 0; i < len; i++ ) + { + if( dllname[i+start] == '\\' ) dllInfo->shortPath[i] = '/'; + else dllInfo->shortPath[i] = Q_tolower( dllname[i+start] ); + } + dllInfo->shortPath[i] = '\0'; + + COM_DefaultExtension( dllInfo->shortPath, "."OS_LIB_EXT ); // apply ext if forget + + search = FS_FindFile( dllInfo->shortPath, &index, false ); + + if( !search && !directpath ) + { + fs_ext_path = false; + + // trying check also 'bin' folder for indirect paths + Q_strncpy( dllInfo->shortPath, dllname, sizeof( dllInfo->shortPath )); + search = FS_FindFile( dllInfo->shortPath, &index, false ); + if( !search ) return false; // unable to find + } + + dllInfo->encrypted = FS_CheckForCrypt( dllInfo->shortPath ); + + if( index < 0 && !dllInfo->encrypted && search ) + { + Q_snprintf( dllInfo->fullPath, sizeof( dllInfo->fullPath ), + "%s%s", search->filename, dllInfo->shortPath ); + dllInfo->custom_loader = false; // we can loading from disk and use normal debugging + } + else + { + // NOTE: if search is NULL let the OS found library himself + Q_strncpy( dllInfo->fullPath, dllInfo->shortPath, sizeof( dllInfo->fullPath )); + + if( search && search->type != SEARCHPATH_PLAIN ) + { +#if XASH_WIN32 && XASH_X86 // a1ba: custom loader is non-portable (I just don't want to touch it) + Con_Printf( S_WARN "%s: loading libraries from packs is deprecated " + "and will be removed in the future\n", __FUNCTION__ ); + *custom_loader = true; +#else + Con_Printf( S_WARN "%s: loading libraries from packs is unsupported on " + "this platform\n", __FUNCTION__ ); + dllInfo->custom_loader = false; +#endif + } + else + { + dllInfo->custom_loader = false; + } + } + fs_ext_path = false; // always reset direct paths + + return true; +} + +poolhandle_t _Mem_AllocPool( const char *name, const char *filename, int fileline ) +{ + return 0xDEADC0DE; +} + +void _Mem_FreePool( poolhandle_t *poolptr, const char *filename, int fileline ) +{ + // stub +} + +void* _Mem_Alloc( poolhandle_t poolptr, size_t size, qboolean clear, const char *filename, int fileline ) +{ + if( clear ) return calloc( 1, size ); + return malloc( size ); +} + +void* _Mem_Realloc( poolhandle_t poolptr, void *memptr, size_t size, qboolean clear, const char *filename, int fileline ) +{ + return realloc( memptr, size ); +} + +void _Mem_Free( void *data, const char *filename, int fileline ) +{ + free( data ); +} + +void _Con_Printf( const char *fmt, ... ) +{ + va_list ap; + + va_start( ap, fmt ); + vprintf( fmt, ap ); + va_end( ap ); +} + +void _Sys_Error( const char *fmt, ... ) +{ + va_list ap; + + va_start( ap, fmt ); + vfprintf( stderr, fmt, ap ); + va_end( ap ); + + exit( 1 ); +} + + /* ================ FS_Init ================ */ -void FS_Init( void ) +qboolean FS_InitStdio( qboolean caseinsensitive, const char *rootdir, const char *basedir, const char *gamedir, const char *rodir ) { stringlist_t dirs; qboolean hasBaseDir = false; @@ -2138,61 +1392,30 @@ void FS_Init( void ) FS_InitMemory(); - Cmd_AddRestrictedCommand( "fs_rescan", FS_Rescan_f, "rescan filesystem search pathes" ); - Cmd_AddRestrictedCommand( "fs_path", FS_Path_f, "show filesystem search pathes" ); - Cmd_AddRestrictedCommand( "fs_clearpaths", FS_ClearPaths_f, "clear filesystem search pathes" ); + fs_caseinsensitive = caseinsensitive; -#if !XASH_WIN32 - if( Sys_CheckParm( "-casesensitive" ) ) - fs_caseinsensitive = false; - - if( !fs_caseinsensitive ) - { - if( COM_CheckStringEmpty( host.rodir ) && !Q_strcmp( host.rodir, host.rootdir ) ) - { - Sys_Error( "RoDir and default rootdir can't point to same directory!" ); - } - } - else -#endif - { - if( COM_CheckStringEmpty( host.rodir ) && !Q_stricmp( host.rodir, host.rootdir ) ) - { - Sys_Error( "RoDir and default rootdir can't point to same directory!" ); - } - } - - // ignore commandlineoption "-game" for other stuff - SI.numgames = 0; - - Q_strncpy( fs_basedir, SI.basedirName, sizeof( fs_basedir )); // default dir - - if( !Sys_GetParmFromCmdLine( "-game", fs_gamedir )) - Q_strncpy( fs_gamedir, fs_basedir, sizeof( fs_gamedir )); // gamedir == basedir - - if( FS_CheckNastyPath( fs_basedir, true )) - { - // this is completely fatal... - Sys_Error( "invalid base directory \"%s\"\n", fs_basedir ); - } - - if( FS_CheckNastyPath( fs_gamedir, true )) - { - Con_Printf( S_ERROR "invalid game directory \"%s\"\n", fs_gamedir ); - Q_strncpy( fs_gamedir, fs_basedir, sizeof( fs_gamedir )); // default dir - } + Q_strncpy( fs_rootdir, rootdir, sizeof( fs_rootdir )); + Q_strncpy( fs_gamedir, gamedir, sizeof( fs_gamedir )); + Q_strncpy( fs_basedir, basedir, sizeof( fs_basedir )); + Q_strncpy( fs_rodir, rodir, sizeof( fs_rodir )); // add readonly directories first - if( COM_CheckStringEmpty( host.rodir ) ) + if( COM_CheckStringEmpty( fs_rodir )) { + if( !Q_stricmp( fs_rodir, fs_rootdir )) + { + Sys_Error( "RoDir and default rootdir can't point to same directory!" ); + return false; + } + stringlistinit( &dirs ); - listdirectory( &dirs, host.rodir, false ); + listdirectory( &dirs, fs_rodir, false ); stringlistsort( &dirs ); for( i = 0; i < dirs.numstrings; i++ ) { - char *roPath = va( "%s" PATH_SPLITTER "%s" PATH_SPLITTER, host.rodir, dirs.strings[i] ); - char *rwPath = va( "%s" PATH_SPLITTER "%s" PATH_SPLITTER, host.rootdir, dirs.strings[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] ); // check if it's a directory if( !FS_SysFolderExists( roPath )) @@ -2233,15 +1456,17 @@ void FS_Init( void ) if( !FS_SysFolderExists( dirs.strings[i] ) || ( !Q_strcmp( dirs.strings[i], ".." ) && !fs_ext_path )) continue; - if( SI.games[SI.numgames] == NULL ) - SI.games[SI.numgames] = (gameinfo_t *)Mem_Calloc( fs_mempool, sizeof( gameinfo_t )); + if( FI.games[FI.numgames] == NULL ) + FI.games[FI.numgames] = (gameinfo_t *)Mem_Calloc( fs_mempool, sizeof( gameinfo_t )); - if( FS_ParseGameInfo( dirs.strings[i], SI.games[SI.numgames] )) - SI.numgames++; // added + if( FS_ParseGameInfo( dirs.strings[i], FI.games[FI.numgames] )) + FI.numgames++; // added } stringlistfreecontents( &dirs ); Con_Reportf( "FS_Init: done\n" ); + + return true; } void FS_AllowDirectPaths( qboolean enable ) @@ -2254,20 +1479,62 @@ void FS_AllowDirectPaths( qboolean enable ) FS_Shutdown ================ */ -void FS_Shutdown( void ) +void FS_ShutdownStdio( void ) { - int i; - + int i; // release gamedirs - for( i = 0; i < SI.numgames; i++ ) - if( SI.games[i] ) Mem_Free( SI.games[i] ); - - memset( &SI, 0, sizeof( sysinfo_t )); + for( i = 0; i < FI.numgames; i++ ) + if( FI.games[i] ) Mem_Free( FI.games[i] ); FS_ClearSearchPath(); // release all wad files too Mem_FreePool( &fs_mempool ); } +/* +============ +FS_Path_f + +debug info +============ +*/ +void FS_Path_f( void ) +{ + searchpath_t *s; + + Con_Printf( "Current search path:\n" ); + + for( s = fs_searchpaths; s; s = s->next ) + { + 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; + } + + Con_Printf( "%s", info ); + + if( s->flags & FS_GAMERODIR_PATH ) Con_Printf( " ^2rodir^7" ); + if( s->flags & FS_GAMEDIR_PATH ) Con_Printf( " ^2gamedir^7" ); + if( s->flags & FS_CUSTOM_PATH ) Con_Printf( " ^2custom^7" ); + if( s->flags & FS_NOWRITE_PATH ) Con_Printf( " ^2nowrite^7" ); + if( s->flags & FS_STATIC_PATH ) Con_Printf( " ^2static^7" ); + + Con_Printf( "\n" ); + } +} + /* ==================== FS_SysFileTime @@ -2275,7 +1542,7 @@ FS_SysFileTime Internal function used to determine filetime ==================== */ -static int FS_SysFileTime( const char *filename ) +int FS_SysFileTime( const char *filename ) { struct stat buf; @@ -2292,7 +1559,7 @@ FS_SysOpen Internal function used to create a file_t and open the relevant non-packed file on disk ==================== */ -static file_t *FS_SysOpen( const char *filepath, const char *mode ) +file_t *FS_SysOpen( const char *filepath, const char *mode ) { file_t *file; int mod, opt; @@ -2391,7 +1658,7 @@ static int FS_DuplicateHandle( const char *filename, int handle, fs_offset_t pos } */ -static file_t *FS_OpenHandle( const char *syspath, int handle, fs_offset_t offset, fs_offset_t len ) +file_t *FS_OpenHandle( const char *syspath, int handle, fs_offset_t offset, fs_offset_t len ) { file_t *file = (file_t *)Mem_Calloc( fs_mempool, sizeof( file_t )); #ifndef XASH_REDUCE_FD @@ -2422,44 +1689,6 @@ static file_t *FS_OpenHandle( const char *syspath, int handle, fs_offset_t offse return file; } -/* -=========== -FS_OpenPackedFile - -Open a packed file using its package file descriptor -=========== -*/ -file_t *FS_OpenPackedFile( pack_t *pack, int pack_ind ) -{ - dpackfile_t *pfile; - - pfile = &pack->files[pack_ind]; - - return FS_OpenHandle( pack->filename, pack->handle, pfile->filepos, pfile->filelen ); -} - -/* -=========== -FS_OpenZipFile - -Open a packed file using its package file descriptor -=========== -*/ -file_t *FS_OpenZipFile( zip_t *zip, int pack_ind ) -{ - zipfile_t *pfile; - pfile = &zip->files[pack_ind]; - - // compressed files handled in Zip_LoadFile - if( pfile->flags != ZIP_COMPRESSION_NO_COMPRESSION ) - { - Con_Printf( S_ERROR "%s: can't open compressed file %s\n", __FUNCTION__, pfile->name ); - return NULL; - } - - return FS_OpenHandle( zip->filename, zip->handle, pfile->offset, pfile->size ); -} - /* ================== FS_SysFileExists @@ -2535,11 +1764,7 @@ qboolean FS_SysFolderExists( const char *path ) struct stat buf; if( stat( path, &buf ) < 0 ) - { - if( errno != ENOTDIR ) - Con_Reportf( S_ERROR "FS_SysFolderExists: problem while opening dir: %s\n", strerror( errno )); return false; - } return S_ISDIR( buf.st_mode ); #else @@ -2557,7 +1782,7 @@ Return the searchpath where the file was found (or NULL) and the file index in the package if relevant ==================== */ -static searchpath_t *FS_FindFile( const char *name, int *index, qboolean gamedironly ) +searchpath_t *FS_FindFile( const char *name, int *index, qboolean gamedironly ) { searchpath_t *search; char *pEnvPath; @@ -2569,107 +1794,31 @@ static searchpath_t *FS_FindFile( const char *name, int *index, qboolean gamedir continue; // is the element a pak file? - if( search->pack ) + if( search->type == SEARCHPATH_PAK ) { - int left, right, middle; - pack_t *pak; - - pak = search->pack; - - // look for the file (binary search) - left = 0; - right = pak->numfiles - 1; - while( left <= right ) + int pack_ind = FS_FindFilePAK( search->pack, name ); + if( pack_ind >= 0 ) { - int diff; - - middle = (left + right) / 2; - diff = Q_stricmp( pak->files[middle].name, name ); - - // Found it - if( !diff ) - { - if( index ) *index = middle; - return search; - } - - // if we're too far in the list - if( diff > 0 ) - right = middle - 1; - else left = middle + 1; - } - } - else if( search->wad ) - { - dlumpinfo_t *lump; - signed char type = W_TypeFromExt( name ); - qboolean anywadname = true; - string wadname, wadfolder; - string shortname; - - // quick reject by filetype - if( type == TYP_NONE ) continue; - COM_ExtractFilePath( name, wadname ); - wadfolder[0] = '\0'; - - if( COM_CheckStringEmpty( wadname ) ) - { - COM_FileBase( wadname, wadname ); - Q_strncpy( wadfolder, wadname, sizeof( wadfolder )); - COM_DefaultExtension( wadname, ".wad" ); - anywadname = false; - } - - // make wadname from wad fullpath - COM_FileBase( search->wad->filename, shortname ); - COM_DefaultExtension( shortname, ".wad" ); - - // quick reject by wadname - if( !anywadname && Q_stricmp( wadname, shortname )) - continue; - - // NOTE: we can't using long names for wad, - // because we using original wad names[16]; - COM_FileBase( name, shortname ); - - lump = W_FindLump( search->wad, shortname, type ); - - if( lump ) - { - if( index ) - *index = lump - search->wad->lumps; + if( index ) *index = pack_ind; return search; } } - else if( search->zip ) + else if( search->type == SEARCHPATH_WAD ) { - int left, right, middle; - zip_t *zip; - - zip = search->zip; - - // look for the file (binary search) - left = 0; - right = zip->numfiles - 1; - - while( left <= right ) + int pack_ind = FS_FindFileWAD( search->wad, name ); + if( pack_ind >= 0 ) { - int diff; - - middle = (left + right) / 2; - diff = Q_stricmp( zip->files[middle].name, name ); - - // Found it - if( !diff ) - { - if( index ) *index = middle; - return search; - } - - // if we're too far in the list - if( diff > 0 ) - right = middle - 1; - else left = middle + 1; + 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 @@ -2695,7 +1844,7 @@ static searchpath_t *FS_FindFile( const char *name, int *index, qboolean gamedir memset( search, 0, sizeof( searchpath_t )); // root folder has a more priority than netpath - Q_strncpy( search->filename, host.rootdir, sizeof( search->filename )); + 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 ); @@ -2705,26 +1854,6 @@ static searchpath_t *FS_FindFile( const char *name, int *index, qboolean gamedir *index = -1; return search; } - -#if 0 - // search for environment path - while( ( pEnvPath = getenv( "Path" ) ) ) - { - char *end = Q_strchr( pEnvPath, ';' ); - if( !end ) break; - Q_strncpy( search->filename, pEnvPath, (end - pEnvPath) + 1 ); - Q_strcat( search->filename, PATH_SPLITTER ); - Q_snprintf( netpath, MAX_SYSPATH, "%s%s", search->filename, name ); - - if( FS_SysFileExists( netpath, !( search->flags & FS_CUSTOM_PATH ) )) - { - if( index != NULL ) - *index = -1; - return search; - } - pEnvPath += (end - pEnvPath) + 1; // move pointer - } -#endif // 0 } if( index != NULL ) @@ -2752,19 +1881,23 @@ file_t *FS_OpenReadFile( const char *filename, const char *mode, qboolean gamedi if( search == NULL ) return NULL; - if( search->pack ) - return FS_OpenPackedFile( search->pack, pack_ind ); - else if( search->wad ) - return NULL; // let W_LoadFile get lump correctly - else if( search->zip ) - return FS_OpenZipFile( search->zip, pack_ind ); - else if( pack_ind < 0 ) + switch( search->type ) { - char path [MAX_SYSPATH]; + 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 ); + // found in the filesystem? + Q_sprintf( path, "%s%s", search->filename, filename ); + return FS_SysOpen( path, mode ); + } } return NULL; @@ -2832,6 +1965,27 @@ int FS_Close( file_t *file ) return 0; } +/* +==================== +FS_Flush + +flushes written data to disk +==================== +*/ +int FS_Flush( file_t *file ) +{ + if( !file ) return 0; + + // purge cached data + FS_Purge( file ); + + // sync + if( fsync( file->handle ) < 0 ) + return EOF; + + return 0; +} + /* ==================== FS_Write @@ -3159,7 +2313,7 @@ FS_Purge Erases any buffered input or output data ==================== */ -void FS_Purge( file_t *file ) +static void FS_Purge( file_t *file ) { file->buff_len = 0; file->buff_ind = 0; @@ -3193,10 +2347,10 @@ byte *FS_LoadFile( const char *path, fs_offset_t *filesizeptr, qboolean gamediro } else { - buf = W_LoadFile( path, &filesize, gamedironly ); + buf = FS_LoadWADFile( path, &filesize, gamedironly ); if( !buf ) - buf = Zip_LoadFile( path, &filesize, gamedironly ); + buf = FS_LoadZIPFile( path, &filesize, gamedironly ); } @@ -3215,7 +2369,6 @@ qboolean CRC32_File( dword *crcvalue, const char *filename ) f = FS_Open( filename, "rb", false ); if( !f ) return false; - Assert( crcvalue != NULL ); CRC32_Init( crcvalue ); while( 1 ) @@ -3374,122 +2527,6 @@ const char *FS_GetDiskPath( const char *name, qboolean gamedironly ) return NULL; } -/* -================== -FS_CheckForCrypt - -return true if library is crypted -================== -*/ -qboolean FS_CheckForCrypt( const char *dllname ) -{ - file_t *f; - int key; - - f = FS_Open( dllname, "rb", false ); - if( !f ) return false; - - FS_Seek( f, 64, SEEK_SET ); // skip first 64 bytes - FS_Read( f, &key, sizeof( key )); - FS_Close( f ); - - return ( key == 0x12345678 ) ? true : false; -} - -/* -================== -FS_FindLibrary - -search for library, assume index is valid -only for internal use -================== -*/ -dll_user_t *FS_FindLibrary( const char *dllname, qboolean directpath ) -{ - string dllpath; - searchpath_t *search; - dll_user_t *hInst; - int i, index; - int start = 0; - int len; - - // check for bad exports - if( !COM_CheckString( dllname )) - return NULL; - - fs_ext_path = directpath; - - // HACKHACK remove absoulte path to valve folder - if( !Q_strnicmp( dllname, "..\\valve\\", 9 ) || !Q_strnicmp( dllname, "../valve/", 9 )) - start += 9; - - // replace all backward slashes - len = Q_strlen( dllname ); - - for( i = 0; i < len; i++ ) - { - if( dllname[i+start] == '\\' ) dllpath[i] = '/'; - else dllpath[i] = Q_tolower( dllname[i+start] ); - } - dllpath[i] = '\0'; - - COM_DefaultExtension( dllpath, "."OS_LIB_EXT ); // apply ext if forget - search = FS_FindFile( dllpath, &index, false ); - - if( !search && !directpath ) - { - fs_ext_path = false; - - // trying check also 'bin' folder for indirect paths - Q_strncpy( dllpath, dllname, sizeof( dllpath )); - search = FS_FindFile( dllpath, &index, false ); - if( !search ) return NULL; // unable to find - } - - // NOTE: for libraries we not fail even if search is NULL - // let the OS find library himself - hInst = Mem_Calloc( host.mempool, sizeof( dll_user_t )); - - // save dllname for debug purposes - Q_strncpy( hInst->dllName, dllname, sizeof( hInst->dllName )); - - // shortPath is used for LibraryLoadSymbols only - Q_strncpy( hInst->shortPath, dllpath, sizeof( hInst->shortPath )); - - hInst->encrypted = FS_CheckForCrypt( dllpath ); - - if( index < 0 && !hInst->encrypted && search ) - { - Q_snprintf( hInst->fullPath, sizeof( hInst->fullPath ), "%s%s", search->filename, dllpath ); - hInst->custom_loader = false; // we can loading from disk and use normal debugging - } - else - { - // NOTE: if search is NULL let the OS found library himself - Q_strncpy( hInst->fullPath, dllpath, sizeof( hInst->fullPath )); - - if( search && ( search->wad || search->pack || search->zip ) ) - { -#if XASH_WIN32 && XASH_X86 // a1ba: custom loader is non-portable (I just don't want to touch it) - Con_Printf( S_WARN "%s: loading libraries from packs is deprecated " - "and will be removed in the future\n", __FUNCTION__ ); - hInst->custom_loader = true; -#else - Con_Printf( S_WARN "%s: loading libraries from packs is unsupported on " - "this platform\n", __FUNCTION__ ); - hInst->custom_loader = false; -#endif - } - else - { - hInst->custom_loader = false; - } - } - fs_ext_path = false; // always reset direct paths - - return hInst; -} - /* ================== FS_FileSize @@ -3543,19 +2580,23 @@ int FS_FileTime( const char *filename, qboolean gamedironly ) search = FS_FindFile( filename, &pack_ind, gamedironly ); if( !search ) return -1; // doesn't exist - if( search->pack ) // grab pack filetime - return search->pack->filetime; - else if( search->wad ) // grab wad filetime - return search->wad->filetime; - else if( search->zip ) - return search->zip->filetime; - else if( pack_ind < 0 ) + switch( search->type ) { - // found in the filesystem? - char path [MAX_SYSPATH]; + 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]; - Q_sprintf( path, "%s%s", search->filename, filename ); - return FS_SysFileTime( path ); + // found in the filesystem? + Q_sprintf( path, "%s%s", search->filename, filename ); + return FS_SysFileTime( path ); + } } return -1; // doesn't exist @@ -3687,154 +2728,18 @@ search_t *FS_Search( const char *pattern, int caseinsensitive, int gamedironly ) continue; // is the element a pak file? - if( searchpath->pack ) + if( searchpath->type == SEARCHPATH_PAK ) { // look through all the pak file elements - pak = searchpath->pack; - for( i = 0; i < pak->numfiles; i++ ) - { - Q_strncpy( temp, pak->files[i].name, sizeof( temp )); - while( temp[0] ) - { - 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 ); - } - - // 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_SearchPAK( &resultlist, searchpath->pack, pattern ); } - else if( searchpath->zip ) + else if( searchpath->type == SEARCHPATH_ZIP ) { - zip = searchpath->zip; - for( i = 0; i < zip->numfiles; i++ ) - { - Q_strncpy( temp, zip->files[i].name, sizeof(temp) ); - while( temp[0] ) - { - 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 ); - } - - // 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_SearchZIP( &resultlist, searchpath->zip, pattern ); } - else if( searchpath->wad ) + else if( searchpath->type == SEARCHPATH_WAD ) { - string wadpattern, wadname, temp2; - signed char type = W_TypeFromExt( pattern ); - qboolean anywadname = true; - string wadfolder; - - // quick reject by filetype - if( type == TYP_NONE ) continue; - COM_ExtractFilePath( pattern, wadname ); - COM_FileBase( pattern, wadpattern ); - wadfolder[0] = '\0'; - - if( COM_CheckStringEmpty( wadname )) - { - COM_FileBase( wadname, wadname ); - Q_strncpy( wadfolder, wadname, sizeof( wadfolder )); - COM_DefaultExtension( wadname, ".wad" ); - anywadname = false; - } - - // make wadname from wad fullpath - COM_FileBase( searchpath->wad->filename, temp2 ); - COM_DefaultExtension( temp2, ".wad" ); - - // quick reject by wadname - if( !anywadname && Q_stricmp( wadname, temp2 )) - continue; - - // look through all the wad file elements - wad = searchpath->wad; - - for( i = 0; i < wad->numlumps; i++ ) - { - // if type not matching, we already have no chance ... - if( type != TYP_ANY && wad->lumps[i].type != type ) - continue; - - // build the lumpname with image suffix (if present) - Q_strncpy( temp, wad->lumps[i].name, sizeof( temp )); - - while( temp[0] ) - { - if( matchpattern( temp, wadpattern, true )) - { - for( resultlistindex = 0; resultlistindex < resultlist.numstrings; resultlistindex++ ) - { - if( !Q_strcmp( resultlist.strings[resultlistindex], temp )) - break; - } - - if( resultlistindex == resultlist.numstrings ) - { - // 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 ))); - stringlistappend( &resultlist, temp2 ); - } - } - - // 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_SearchWAD( &resultlist, searchpath->wad, pattern ); } else { @@ -3903,376 +2808,124 @@ void FS_InitMemory( void ) fs_searchpaths = NULL; } -/* -============================================================================= - -WADSYSTEM PRIVATE ROUTINES - -============================================================================= -*/ -// associate extension with wad type -static const wadtype_t wad_types[7] = +fs_interface_t g_engfuncs = { -{ "pal", TYP_PALETTE }, // palette -{ "dds", TYP_DDSTEX }, // DDS image -{ "lmp", TYP_GFXPIC }, // quake1, hl pic -{ "fnt", TYP_QFONT }, // hl qfonts -{ "mip", TYP_MIPTEX }, // hl/q1 mip -{ "txt", TYP_SCRIPT }, // scripts -{ NULL, TYP_NONE } + _Con_Printf, + _Con_Printf, + _Con_Printf, + _Sys_Error, + _Mem_AllocPool, + _Mem_FreePool, + _Mem_Alloc, + _Mem_Realloc, + _Mem_Free }; -/* -=========== -W_TypeFromExt - -Extracts file type from extension -=========== -*/ -static signed char W_TypeFromExt( const char *lumpname ) +static qboolean FS_InitInterface( int version, fs_interface_t *engfuncs ) { - const char *ext = COM_FileExtension( lumpname ); - const wadtype_t *type; - - // we not known about filetype, so match only by filename - if( !Q_strcmp( ext, "*" ) || !Q_strcmp( ext, "" )) - return TYP_ANY; - - for( type = wad_types; type->ext; type++ ) + // to be extended in future interface revisions + if( version != FS_API_VERSION ) { - if( !Q_stricmp( ext, type->ext )) - return type->type; + Con_Printf( S_ERROR "filesystem optional interface version mismatch: expected %d, got %d\n", + FS_API_VERSION, version ); + return false; } - return TYP_NONE; + + if( engfuncs->_Con_Printf ) + g_engfuncs._Con_Printf = engfuncs->_Con_Printf; + + if( engfuncs->_Con_DPrintf ) + g_engfuncs._Con_DPrintf = engfuncs->_Con_DPrintf; + + if( engfuncs->_Con_Reportf ) + g_engfuncs._Con_Reportf = engfuncs->_Con_Reportf; + + if( engfuncs->_Sys_Error ) + g_engfuncs._Sys_Error = engfuncs->_Sys_Error; + + if( engfuncs->_Mem_AllocPool && engfuncs->_Mem_FreePool ) + { + g_engfuncs._Mem_AllocPool = engfuncs->_Mem_AllocPool; + g_engfuncs._Mem_FreePool = engfuncs->_Mem_FreePool; + + Con_Reportf( "filesystem_stdio: custom pool allocation functions found\n" ); + } + + if( engfuncs->_Mem_Alloc && engfuncs->_Mem_Realloc && engfuncs->_Mem_Free ) + { + g_engfuncs._Mem_Alloc = engfuncs->_Mem_Alloc; + g_engfuncs._Mem_Realloc = engfuncs->_Mem_Realloc; + g_engfuncs._Mem_Free = engfuncs->_Mem_Free; + + Con_Reportf( "filesystem_stdio: custom memory allocation functions found\n" ); + } + + return true; } -/* -=========== -W_ExtFromType - -Convert type to extension -=========== -*/ -static const char *W_ExtFromType( signed char lumptype ) +static fs_api_t g_api = { - const wadtype_t *type; + FS_InitStdio, + FS_ShutdownStdio, - // we not known aboyt filetype, so match only by filename - if( lumptype == TYP_NONE || lumptype == TYP_ANY ) - return ""; + // search path utils + FS_Rescan, + FS_ClearSearchPath, + FS_AllowDirectPaths, + FS_AddGameDirectory, + FS_AddGameHierarchy, + FS_Search, + FS_SetCurrentDirectory, + FS_FindLibrary, + FS_Path_f, - for( type = wad_types; type->ext; type++ ) - { - if( lumptype == type->type ) - return type->ext; - } - return ""; -} + // gameinfo utils + FS_LoadGameInfo, -/* -=========== -W_FindLump + // file ops + FS_Open, + FS_Write, + FS_Read, + FS_Seek, + FS_Tell, + FS_Eof, + FS_Flush, + FS_Close, + FS_Gets, + FS_UnGetc, + FS_Getc, + FS_VPrintf, + FS_Printf, + FS_Print, + FS_FileLength, + FS_FileCopy, -Serach for already existed lump -=========== -*/ -static dlumpinfo_t *W_FindLump( wfile_t *wad, const char *name, const signed char matchtype ) + // file buffer ops + FS_LoadFile, + FS_LoadDirectFile, + FS_WriteFile, + + // file hashing + CRC32_File, + MD5_HashFile, + + // filesystem ops + FS_FileExists, + FS_FileTime, + FS_FileSize, + FS_Rename, + FS_Delete, + FS_SysFileExists, + FS_GetDiskPath, +}; + +int EXPORT GetFSAPI( int version, fs_api_t *api, fs_globals_t **globals, fs_interface_t *engfuncs ) { - int left, right; + if( !FS_InitInterface( version, engfuncs )) + return 0; - if( !wad || !wad->lumps || matchtype == TYP_NONE ) - return NULL; + memcpy( api, &g_api, sizeof( *api )); + *globals = &FI; - // 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 - -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 ) -{ - int left, right; - dlumpinfo_t *plump; - - // look for the slot we should put that file into (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( wad->lumps[middle].type < newlump->type ) - 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 ); - } - - // 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 - plump = &wad->lumps[left]; - memmove( plump + 1, plump, ( wad->numlumps - left ) * sizeof( *plump )); - wad->numlumps++; - - *plump = *newlump; - memcpy( plump->name, name, sizeof( plump->name )); - - return plump; -} - -/* -=========== -W_ReadLump - -reading lump into temp buffer -=========== -*/ -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; -} - -/* -============================================================================= - -WADSYSTEM PUBLIC BASE FUNCTIONS - -============================================================================= -*/ -/* -=========== -W_Open - -open the wad for reading & writing -=========== -*/ -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 ); - - 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( host.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 ); - if( error ) *error = WAD_LOAD_COULDNT_OPEN; - W_Close( wad ); - return NULL; - } - - // copy wad name - Q_strncpy( wad->filename, filename, sizeof( wad->filename )); - wad->filetime = FS_SysFileTime( filename ); - wad->mempool = Mem_AllocPool( filename ); - - if( FS_Read( wad->handle, &header, sizeof( dwadinfo_t )) != sizeof( dwadinfo_t )) - { - Con_Reportf( S_ERROR "W_Open: %s can't read header\n", filename ); - if( error ) *error = WAD_LOAD_BAD_HEADER; - W_Close( wad ); - return NULL; - } - - if( header.ident != IDWAD2HEADER && header.ident != IDWAD3HEADER ) - { - Con_Reportf( S_ERROR "W_Open: %s is not a WAD2 or WAD3 file\n", filename ); - if( error ) *error = WAD_LOAD_BAD_HEADER; - W_Close( wad ); - return NULL; - } - - lumpcount = header.numlumps; - - if( lumpcount >= MAX_FILES_IN_WAD ) - { - Con_Reportf( S_WARN "W_Open: %s is full (%i lumps)\n", filename, lumpcount ); - if( error ) *error = WAD_LOAD_TOO_MANY_FILES; - } - else if( lumpcount <= 0 ) - { - Con_Reportf( S_ERROR "W_Open: %s has no lumps\n", filename ); - if( error ) *error = WAD_LOAD_NO_FILES; - W_Close( wad ); - return NULL; - } - else if( error ) *error = WAD_LOAD_OK; - - wad->infotableofs = header.infotableofs; // save infotableofs position - - if( FS_Seek( wad->handle, wad->infotableofs, SEEK_SET ) == -1 ) - { - Con_Reportf( S_ERROR "W_Open: %s can't find lump allocation table\n", filename ); - if( error ) *error = WAD_LOAD_BAD_FOLDERS; - W_Close( wad ); - return NULL; - } - - lat_size = lumpcount * sizeof( dlumpinfo_t ); - - // NOTE: lumps table can be reallocated for O_APPEND mode - srclumps = (dlumpinfo_t *)Mem_Malloc( wad->mempool, lat_size ); - - 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 ); - if( error ) *error = WAD_LOAD_CORRUPTED; - Mem_Free( srclumps ); - W_Close( wad ); - return NULL; - } - - // starting to add lumps - wad->lumps = (dlumpinfo_t *)Mem_Calloc( wad->mempool, lat_size ); - wad->numlumps = 0; - - // sort lumps for binary search - for( i = 0; i < lumpcount; i++ ) - { - char name[16]; - int k; - - // cleanup lumpname - Q_strnlwr( srclumps[i].name, name, sizeof( srclumps[i].name )); - - // check for '*' symbol issues (quake1) - k = Q_strlen( Q_strrchr( name, '*' )); - if( k ) name[Q_strlen( name ) - k] = '!'; - - // check for Quake 'conchars' issues (only lmp loader really allows to read this lame pic) - if( srclumps[i].type == 68 && !Q_stricmp( srclumps[i].name, "conchars" )) - srclumps[i].type = TYP_GFXPIC; - - W_AddFileToWad( name, wad, &srclumps[i] ); - } - - // release source lumps - Mem_Free( srclumps ); - - // and leave the file open - return wad; -} - -/* -=========== -W_Close - -finalize wad or just close -=========== -*/ -void W_Close( wfile_t *wad ) -{ - if( !wad ) return; - - Mem_FreePool( &wad->mempool ); - if( wad->handle != NULL ) - FS_Close( wad->handle ); - Mem_Free( wad ); // free himself -} - -/* -============================================================================= - -FILESYSTEM IMPLEMENTATION - -============================================================================= -*/ -/* -=========== -W_LoadFile - -loading lump into the tmp buffer -=========== -*/ -static byte *W_LoadFile( const char *path, fs_offset_t *lumpsizeptr, qboolean gamedironly ) -{ - searchpath_t *search; - int index; - - search = FS_FindFile( path, &index, gamedironly ); - if( search && search->wad ) - return W_ReadLump( search->wad, &search->wad->lumps[index], lumpsizeptr ); - return NULL; + return FS_API_VERSION; } diff --git a/filesystem/filesystem.h b/filesystem/filesystem.h new file mode 100644 index 00000000..6a7a1f4c --- /dev/null +++ b/filesystem/filesystem.h @@ -0,0 +1,202 @@ +/* +filesystem.h - engine FS +Copyright (C) 2007 Uncle Mike + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. +*/ + +#ifndef FILESYSTEM_H +#define FILESYSTEM_H + +#include +#include +#include +#include "xash3d_types.h" +#include "const.h" +#include "com_model.h" + +#ifdef __cplusplus +extern "C" +{ +#endif // __cplusplus + +#define FS_API_VERSION 1 // not stable yet! + +// search path flags +enum +{ + FS_STATIC_PATH = BIT( 0 ), // FS_ClearSearchPath will be ignore this path + FS_NOWRITE_PATH = BIT( 1 ), // default behavior - last added gamedir set as writedir. This flag disables it + FS_GAMEDIR_PATH = BIT( 2 ), // just a marker for gamedir path + FS_CUSTOM_PATH = BIT( 3 ), // gamedir but with custom/mod data + FS_GAMERODIR_PATH = BIT( 4 ), // gamedir but read-only + + FS_GAMEDIRONLY_SEARCH_FLAGS = FS_GAMEDIR_PATH | FS_CUSTOM_PATH | FS_GAMERODIR_PATH +}; + +typedef struct +{ + int numfilenames; + char **filenames; + char *filenamesbuffer; +} search_t; + +typedef struct gameinfo_s +{ + // filesystem info + char gamefolder[MAX_QPATH]; // used for change game '-game x' + char basedir[MAX_QPATH]; // base game directory (like 'id1' for Quake or 'valve' for Half-Life) + char falldir[MAX_QPATH]; // used as second basedir + char startmap[MAX_QPATH];// map to start singleplayer game + char trainmap[MAX_QPATH];// map to start hazard course (if specified) + char title[64]; // Game Main Title + float version; // game version (optional) + + // .dll pathes + char dll_path[MAX_QPATH]; // e.g. "bin" or "cl_dlls" + char game_dll[MAX_QPATH]; // custom path for game.dll + + // .ico path + char iconpath[MAX_QPATH]; // "game.ico" by default + + // about mod info + string game_url; // link to a developer's site + string update_url; // link to updates page + char type[MAX_QPATH]; // single, toolkit, multiplayer etc + char date[MAX_QPATH]; + 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 + + char sp_entity[32]; // e.g. info_player_start + char mp_entity[32]; // e.g. info_player_deathmatch + char mp_filter[32]; // filtering multiplayer-maps + + char ambientsound[NUM_AMBIENTS][MAX_QPATH]; // quake ambient sounds + + int max_edicts; // min edicts is 600, max edicts is 8196 + int max_tents; // min temp ents is 300, max is 2048 + int max_beams; // min beams is 64, max beams is 512 + int max_particles; // min particles is 4096, max particles is 32768 + + char game_dll_linux[64]; // custom path for game.dll + char game_dll_osx[64]; // custom path for game.dll + + qboolean added; +} gameinfo_t; + +typedef enum +{ + GAME_NORMAL, + GAME_SINGLEPLAYER_ONLY, + GAME_MULTIPLAYER_ONLY +} gametype_t; + +typedef struct fs_dllinfo_t +{ + string fullPath; + string shortPath; + qboolean encrypted; + qboolean custom_loader; +} fs_dllinfo_t; + +typedef struct fs_globals_t +{ + gameinfo_t *GameInfo; // current GameInfo + gameinfo_t *games[MAX_MODS]; // environment games (founded at each engine start) + int numgames; +} fs_globals_t; + +typedef struct fs_api_t +{ + qboolean (*InitStdio)( qboolean caseinsensitive, const char *rootdir, const char *basedir, const char *gamedir, const char *rodir ); + void (*ShutdownStdio)( void ); + + // search path utils + void (*Rescan)( void ); + void (*ClearSearchPath)( void ); + void (*AllowDirectPaths)( qboolean enable ); + void (*AddGameDirectory)( const char *dir, uint flags ); + void (*AddGameHierarchy)( const char *dir, uint flags ); + search_t *(*Search)( const char *pattern, int caseinsensitive, int gamedironly ); + int (*SetCurrentDirectory)( const char *path ); + qboolean (*FindLibrary)( const char *dllname, qboolean directpath, fs_dllinfo_t *dllinfo ); + void (*Path_f)( void ); + + // gameinfo utils + void (*LoadGameInfo)( const char *rootfolder ); + + // file ops + file_t *(*Open)( const char *filepath, const char *mode, qboolean gamedironly ); + fs_offset_t (*Write)( file_t *file, const void *data, size_t datasize ); + fs_offset_t (*Read)( file_t *file, void *buffer, size_t buffersize ); + int (*Seek)( file_t *file, fs_offset_t offset, int whence ); + fs_offset_t (*Tell)( file_t *file ); + qboolean (*Eof)( file_t *file ); + int (*Flush)( file_t *file ); + int (*Close)( file_t *file ); + int (*Gets)( file_t *file, byte *string, size_t bufsize ); + int (*UnGetc)( file_t *file, byte c ); + int (*Getc)( file_t *file ); + int (*VPrintf)( file_t *file, const char *format, va_list ap ); + int (*Printf)( file_t *file, const char *format, ... ) _format( 2 ); + int (*Print)( file_t *file, const char *msg ); + fs_offset_t (*FileLength)( file_t *f ); + qboolean (*FileCopy)( file_t *pOutput, file_t *pInput, int fileSize ); + + // file buffer ops + byte *(*LoadFile)( const char *path, fs_offset_t *filesizeptr, qboolean gamedironly ); + byte *(*LoadDirectFile)( const char *path, fs_offset_t *filesizeptr ); + qboolean (*WriteFile)( const char *filename, const void *data, fs_offset_t len ); + + // file hashing + qboolean (*CRC32_File)( dword *crcvalue, const char *filename ); + qboolean (*MD5_HashFile)( byte digest[16], const char *pszFileName, uint seed[4] ); + + // filesystem ops + int (*FileExists)( const char *filename, int gamedironly ); + int (*FileTime)( const char *filename, qboolean gamedironly ); + 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 ); + const char *(*GetDiskPath)( const char *name, qboolean gamedironly ); +} fs_api_t; + +typedef struct fs_interface_t +{ + // logging + void (*_Con_Printf)( const char *fmt, ... ) _format( 1 ); // typical console allowed messages + void (*_Con_DPrintf)( const char *fmt, ... ) _format( 1 ); // -dev 1 + void (*_Con_Reportf)( const char *fmt, ... ) _format( 1 ); // -dev 2 + + void (*_Sys_Error)( const char *fmt, ... ) _format( 1 ); + + // memory + poolhandle_t (*_Mem_AllocPool)( const char *name, const char *filename, int fileline ); + void (*_Mem_FreePool)( poolhandle_t *poolptr, const char *filename, int fileline ); + void *(*_Mem_Alloc)( poolhandle_t poolptr, size_t size, qboolean clear, const char *filename, int fileline ); + void *(*_Mem_Realloc)( poolhandle_t poolptr, void *memptr, size_t size, qboolean clear, const char *filename, int fileline ); + void (*_Mem_Free)( void *data, const char *filename, int fileline ); +} fs_interface_t; + +typedef int (*FSAPI)( int version, fs_api_t *api, fs_globals_t **globals, fs_interface_t *interface ); +#define GET_FS_API "GetFSAPI" + +#ifdef __cplusplus +} +#endif // __cplusplus + +#endif//FILESYSTEM_H diff --git a/filesystem/filesystem_internal.h b/filesystem/filesystem_internal.h new file mode 100644 index 00000000..c466b1db --- /dev/null +++ b/filesystem/filesystem_internal.h @@ -0,0 +1,204 @@ +/* +filesystem.h - engine FS +Copyright (C) 2007 Uncle Mike + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. +*/ + +#ifndef FILESYSTEM_INTERNAL_H +#define FILESYSTEM_INTERNAL_H + +#include "xash3d_types.h" +#include "filesystem.h" + +#ifdef __cplusplus +extern "C" +{ +#endif + +typedef struct zip_s zip_t; +typedef struct pack_s pack_t; +typedef struct wfile_s wfile_t; + + +#define FILE_BUFF_SIZE (2048) + +struct file_s +{ + int handle; // file descriptor + int ungetc; // single stored character from ungetc, cleared to EOF when read + fs_offset_t real_length; // uncompressed file size (for files opened in "read" mode) + fs_offset_t position; // current position in the file + fs_offset_t offset; // offset into the package (0 if external file) + time_t filetime; // pak, wad or real filetime + // contents buffer + fs_offset_t buff_ind, buff_len; // buffer current index and length + byte buff[FILE_BUFF_SIZE]; // intermediate buffer +#ifdef XASH_REDUCE_FD + const char *backup_path; + fs_offset_t backup_position; + uint backup_options; +#endif +}; + +enum +{ + SEARCHPATH_PLAIN = 0, + SEARCHPATH_PAK, + SEARCHPATH_WAD, + SEARCHPATH_ZIP +}; + +typedef struct stringlist_s +{ + // maxstrings changes as needed, causing reallocation of strings[] array + int maxstrings; + int numstrings; + char **strings; +} stringlist_t; + +typedef struct searchpath_s +{ + string filename; + int type; + int flags; + union + { + pack_t *pack; + wfile_t *wad; + zip_t *zip; + }; + struct searchpath_s *next; +} searchpath_t; + +extern searchpath_t *fs_searchpaths; +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]; + +#define Mem_Malloc( pool, size ) g_engfuncs._Mem_Alloc( pool, size, false, __FILE__, __LINE__ ) +#define Mem_Calloc( pool, size ) g_engfuncs._Mem_Alloc( pool, size, true, __FILE__, __LINE__ ) +#define Mem_Realloc( pool, ptr, size ) g_engfuncs._Mem_Realloc( pool, ptr, size, true, __FILE__, __LINE__ ) +#define Mem_Free( mem ) g_engfuncs._Mem_Free( mem, __FILE__, __LINE__ ) +#define Mem_AllocPool( name ) g_engfuncs._Mem_AllocPool( name, __FILE__, __LINE__ ) +#define Mem_FreePool( pool ) g_engfuncs._Mem_FreePool( pool, __FILE__, __LINE__ ) + +#define Con_Printf (*g_engfuncs._Con_Printf) +#define Con_DPrintf (*g_engfuncs._Con_DPrintf) +#define Con_Reportf (*g_engfuncs._Con_Reportf) +#define Sys_Error (*g_engfuncs._Sys_Error) + +// +// filesystem.c +// +qboolean FS_InitStdio( qboolean caseinsensitive, const char *rootdir, const char *basedir, const char *gamedir, const char *rodir ); +void FS_ShutdownStdio( void ); + +// search path utils +void FS_Rescan( void ); +void FS_ClearSearchPath( void ); +void FS_AllowDirectPaths( qboolean enable ); +void FS_AddGameDirectory( const char *dir, uint flags ); +void FS_AddGameHierarchy( const char *dir, uint flags ); +search_t *FS_Search( const char *pattern, int caseinsensitive, int gamedironly ); +int FS_SetCurrentDirectory( const char *path ); +void FS_Path_f( void ); + +// gameinfo utils +void FS_LoadGameInfo( const char *rootfolder ); + +// file ops +file_t *FS_Open( const char *filepath, const char *mode, qboolean gamedironly ); +fs_offset_t FS_Write( file_t *file, const void *data, size_t datasize ); +fs_offset_t FS_Read( file_t *file, void *buffer, size_t buffersize ); +int FS_Seek( file_t *file, fs_offset_t offset, int whence ); +fs_offset_t FS_Tell( file_t *file ); +qboolean FS_Eof( file_t *file ); +int FS_Flush( file_t *file ); +int FS_Close( file_t *file ); +int FS_Gets( file_t *file, byte *string, size_t bufsize ); +int FS_UnGetc( file_t *file, byte c ); +int FS_Getc( file_t *file ); +int FS_VPrintf( file_t *file, const char *format, va_list ap ); +int FS_Printf( file_t *file, const char *format, ... ) _format( 2 ); +int FS_Print( file_t *file, const char *msg ); +fs_offset_t FS_FileLength( file_t *f ); +qboolean FS_FileCopy( file_t *pOutput, file_t *pInput, int fileSize ); + +// file buffer ops +byte *FS_LoadFile( const char *path, fs_offset_t *filesizeptr, qboolean gamedironly ); +byte *FS_LoadDirectFile( const char *path, fs_offset_t *filesizeptr ); +qboolean FS_WriteFile( const char *filename, const void *data, fs_offset_t len ); + +// file hashing +qboolean CRC32_File( dword *crcvalue, const char *filename ); +qboolean MD5_HashFile( byte digest[16], const char *pszFileName, uint seed[4] ); + +// filesystem ops +int FS_FileExists( const char *filename, int gamedironly ); +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 ); +const char *FS_GetDiskPath( const char *name, qboolean gamedironly ); +void stringlistappend( stringlist_t *list, char *text ); +void FS_CreatePath( char *path ); +qboolean FS_SysFolderExists( 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 ); + +// +// 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 ); +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 ); +byte *FS_LoadWADFile( const char *path, fs_offset_t *sizeptr, qboolean gamedironly ); +qboolean FS_AddWad_Fullpath( const char *wadfile, qboolean *already_loaded, int flags ); + +// +// 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 ); +byte *FS_LoadZIPFile( const char *path, fs_offset_t *sizeptr, qboolean gamedironly ); +file_t *FS_OpenZipFile( zip_t *zip, int pack_ind ); +qboolean FS_AddZip_Fullpath( const char *zipfile, qboolean *already_loaded, int flags ); + +#ifdef __cplusplus +} +#endif + +#endif // FILESYSTEM_INTERNAL_H diff --git a/filesystem/fscallback.h b/filesystem/fscallback.h new file mode 100644 index 00000000..bebfdd29 --- /dev/null +++ b/filesystem/fscallback.h @@ -0,0 +1,80 @@ +/* +fscallback.h - common filesystem callbacks +Copyright (C) 2022 Alibek Omarov + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. +*/ +#ifndef FSCALLBACK_H +#define FSCALLBACK_H + +#include "filesystem.h" + +extern fs_api_t g_fsapi; +extern fs_globals_t *FI; + +#define GI FI->GameInfo +#define FS_Gamedir() GI->gamefolder +#define FS_Title() GI->title + +#define FS_InitStdio (*g_fsapi.InitStdio) +#define FS_ShutdownStdio (*g_fsapi.ShutdownStdio) + +// search path utils +#define FS_Rescan (*g_fsapi.Rescan) +#define FS_ClearSearchPath (*g_fsapi.ClearSearchPath) +#define FS_AllowDirectPaths (*g_fsapi.AllowDirectPaths) +#define FS_AddGameDirectory (*g_fsapi.AddGameDirectory) +#define FS_AddGameHierarchy (*g_fsapi.AddGameHierarchy) +#define FS_Search (*g_fsapi.Search) +#define FS_SetCurrentDirectory (*g_fsapi.SetCurrentDirectory) +#define FS_Path_f (*g_fsapi.Path_f) + +// gameinfo utils +#define FS_LoadGameInfo (*g_fsapi.LoadGameInfo) + +// file ops +#define FS_Open (*g_fsapi.Open) +#define FS_Write (*g_fsapi.Write) +#define FS_Read (*g_fsapi.Read) +#define FS_Seek (*g_fsapi.Seek) +#define FS_Tell (*g_fsapi.Tell) +#define FS_Eof (*g_fsapi.Eof) +#define FS_Flush (*g_fsapi.Flush) +#define FS_Close (*g_fsapi.Close) +#define FS_Gets (*g_fsapi.Gets) +#define FS_UnGetc (*g_fsapi.UnGetc) +#define FS_Getc (*g_fsapi.Getc) +#define FS_VPrintf (*g_fsapi.VPrintf) +#define FS_Printf (*g_fsapi.Printf) +#define FS_Print (*g_fsapi.Print) +#define FS_FileLength (*g_fsapi.FileLength) +#define FS_FileCopy (*g_fsapi.FileCopy) + +// file buffer ops +#define FS_LoadFile (*g_fsapi.LoadFile) +#define FS_LoadDirectFile (*g_fsapi.LoadDirectFile) +#define FS_WriteFile (*g_fsapi.WriteFile) + +// file hashing +#define CRC32_File (*g_fsapi.CRC32_File) +#define MD5_HashFile (*g_fsapi.MD5_HashFile) + +// filesystem ops +#define FS_FileExists (*g_fsapi.FileExists) +#define FS_FileTime (*g_fsapi.FileTime) +#define FS_FileSize (*g_fsapi.FileSize) +#define FS_Rename (*g_fsapi.Rename) +#define FS_Delete (*g_fsapi.Delete) +#define FS_SysFileExists (*g_fsapi.SysFileExists) +#define FS_GetDiskPath (*g_fsapi.GetDiskPath) + + +#endif // FSCALLBACK_H diff --git a/filesystem/pak.c b/filesystem/pak.c new file mode 100644 index 00000000..7dac5b8f --- /dev/null +++ b/filesystem/pak.c @@ -0,0 +1,394 @@ +/* +pak.c - PAK support for filesystem +Copyright (C) 2007 Uncle Mike +Copyright (C) 2022 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 +#include +#include +#include +#include +#include +#include "port.h" +#include "filesystem_internal.h" +#include "crtlib.h" +#include "common/com_strings.h" + +/* +======================================================================== +PAK FILES + +The .pak files are just a linear collapse of a directory tree +======================================================================== +*/ +// header +#define IDPACKV1HEADER (('K'<<24)+('C'<<16)+('A'<<8)+'P') // little-endian "PACK" + +#define MAX_FILES_IN_PACK 65536 // pak + +typedef struct +{ + int ident; + int dirofs; + int dirlen; +} dpackheader_t; + +typedef struct +{ + char name[56]; // total 64 bytes + int filepos; + int filelen; +} dpackfile_t; + +// PAK errors +#define PAK_LOAD_OK 0 +#define PAK_LOAD_COULDNT_OPEN 1 +#define PAK_LOAD_BAD_HEADER 2 +#define PAK_LOAD_BAD_FOLDERS 3 +#define PAK_LOAD_TOO_MANY_FILES 4 +#define PAK_LOAD_NO_FILES 5 +#define PAK_LOAD_CORRUPTED 6 + +typedef struct pack_s +{ + string filename; + int handle; + int numfiles; + time_t filetime; // common for all packed files + dpackfile_t *files; +} pack_t; + +/* +==================== +FS_AddFileToPack + +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 ) +{ + int left, right, middle; + dpackfile_t *pfile; + + // 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; +} + +/* +================= +FS_LoadPackPAK + +Takes an explicit (not game tree related) path to a pak file. + +Loads the header and directory, adding the files at the beginning +of the list so they override previous pack files. +================= +*/ +static pack_t *FS_LoadPackPAK( const char *packfile, int *error ) +{ + dpackheader_t header; + int packhandle; + int i, numpackfiles; + pack_t *pack; + dpackfile_t *info; + fs_size_t c; + + 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 )); + if( error ) *error = PAK_LOAD_COULDNT_OPEN; + return NULL; + } + + c = read( packhandle, (void *)&header, sizeof( header )); + + if( c != sizeof( header ) || header.ident != IDPACKV1HEADER ) + { + Con_Reportf( "%s is not a packfile. Ignored.\n", packfile ); + if( error ) *error = PAK_LOAD_BAD_HEADER; + close( packhandle ); + return NULL; + } + + if( header.dirlen % sizeof( dpackfile_t )) + { + Con_Reportf( S_ERROR "%s has an invalid directory size. Ignored.\n", packfile ); + if( error ) *error = PAK_LOAD_BAD_FOLDERS; + close( packhandle ); + return NULL; + } + + numpackfiles = header.dirlen / sizeof( dpackfile_t ); + + if( numpackfiles > MAX_FILES_IN_PACK ) + { + Con_Reportf( S_ERROR "%s has too many files ( %i ). Ignored.\n", packfile, numpackfiles ); + if( error ) *error = PAK_LOAD_TOO_MANY_FILES; + close( packhandle ); + return NULL; + } + + if( numpackfiles <= 0 ) + { + Con_Reportf( "%s has no files. Ignored.\n", packfile ); + if( error ) *error = PAK_LOAD_NO_FILES; + close( packhandle ); + return NULL; + } + + info = (dpackfile_t *)Mem_Malloc( fs_mempool, sizeof( *info ) * numpackfiles ); + lseek( packhandle, header.dirofs, SEEK_SET ); + + if( header.dirlen != read( packhandle, (void *)info, header.dirlen )) + { + Con_Reportf( "%s is an incomplete PAK, not loading\n", packfile ); + if( error ) *error = PAK_LOAD_CORRUPTED; + close( packhandle ); + Mem_Free( info ); + 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 )); + 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 ); + +#ifdef XASH_REDUCE_FD + // will reopen when needed + close( pack->handle ); + pack->handle = -1; +#endif + + if( error ) *error = PAK_LOAD_OK; + Mem_Free( info ); + + return pack; +} + +/* +=========== +FS_OpenPackedFile + +Open a packed file using its package file descriptor +=========== +*/ +file_t *FS_OpenPackedFile( pack_t *pack, int pack_ind ) +{ + dpackfile_t *pfile; + + pfile = &pack->files[pack_ind]; + + return FS_OpenHandle( pack->filename, pack->handle, pfile->filepos, pfile->filelen ); +} + +/* +================ +FS_AddPak_Fullpath + +Adds the given pack to the search path. +The pack type is autodetected by the file extension. + +Returns true if the file was successfully added to the +search path or if it was already included. + +If keep_plain_dirs is set, the pack will be added AFTER the first sequence of +plain directories. +================ +*/ +qboolean FS_AddPak_Fullpath( const char *pakfile, qboolean *already_loaded, int flags ) +{ + searchpath_t *search; + pack_t *pak = NULL; + const char *ext = COM_FileExtension( pakfile ); + int i, errorcode = PAK_LOAD_COULDNT_OPEN; + + for( search = fs_searchpaths; search; search = search->next ) + { + if( search->type == SEARCHPATH_PAK && !Q_stricmp( search->pack->filename, pakfile )) + { + if( already_loaded ) *already_loaded = true; + return true; // already loaded + } + } + + if( already_loaded ) + *already_loaded = false; + + if( !Q_stricmp( ext, "pak" )) + pak = FS_LoadPackPAK( pakfile, &errorcode ); + + if( pak ) + { + string fullpath; + + search = (searchpath_t *)Mem_Calloc( fs_mempool, sizeof( searchpath_t )); + search->pack = pak; + search->type = SEARCHPATH_PAK; + search->next = fs_searchpaths; + search->flags |= flags; + fs_searchpaths = search; + + Con_Reportf( "Adding pakfile: %s (%i files)\n", pakfile, pak->numfiles ); + + // time to add in search list all the wads that contains in current pakfile (if do) + for( i = 0; i < pak->numfiles; i++ ) + { + if( !Q_stricmp( COM_FileExtension( pak->files[i].name ), "wad" )) + { + Q_snprintf( fullpath, MAX_STRING, "%s/%s", pakfile, pak->files[i].name ); + FS_AddWad_Fullpath( fullpath, NULL, flags ); + } + } + + return true; + } + else + { + if( errorcode != PAK_LOAD_NO_FILES ) + Con_Reportf( S_ERROR "FS_AddPak_Fullpath: unable to load pak \"%s\"\n", pakfile ); + return false; + } +} + +int FS_FindFilePAK( pack_t *pack, const char *name ) +{ + int left, right, middle; + + // look for the file (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 ); + + // 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_SearchPAK( stringlist_t *list, pack_t *pack, const char *pattern ) +{ + string temp; + const char *slash, *backslash, *colon, *separator; + int j, i; + + for( i = 0; i < pack->numfiles; i++ ) + { + Q_strncpy( temp, 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_FileTimePAK( pack_t *pack ) +{ + return pack->filetime; +} + +void FS_PrintPAKInfo( char *dst, size_t size, pack_t *pack ) +{ + Q_snprintf( dst, size, "%s (%i files)", pack->filename, pack->numfiles ); +} + +void FS_ClosePAK( pack_t *pack ) +{ + if( pack->files ) + Mem_Free( pack->files ); + if( pack->handle >= 0 ) + close( pack->handle ); + Mem_Free( pack ); +} diff --git a/filesystem/wad.c b/filesystem/wad.c new file mode 100644 index 00000000..9cbfa886 --- /dev/null +++ b/filesystem/wad.c @@ -0,0 +1,634 @@ +/* +wad.c - WAD support for filesystem +Copyright (C) 2007 Uncle Mike +Copyright (C) 2022 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 +#include +#include +#include +#include +#include +#include "port.h" +#include "filesystem_internal.h" +#include "crtlib.h" +#include "common/com_strings.h" +#include "wadfile.h" + +/* +======================================================================== +.WAD archive format (WhereAllData - WAD) + +List of compressed files, that can be identify only by TYPE_* + + +header: dwadinfo_t[dwadinfo_t] +file_1: byte[dwadinfo_t[num]->disksize] +file_2: byte[dwadinfo_t[num]->disksize] +file_3: byte[dwadinfo_t[num]->disksize] +... +file_n: byte[dwadinfo_t[num]->disksize] +infotable dlumpinfo_t[dwadinfo_t->numlumps] +======================================================================== +*/ +#define WAD3_NAMELEN 16 +#define HINT_NAMELEN 5 // e.g. _mask, _norm +#define MAX_FILES_IN_WAD 65535 // real limit as above <2Gb size not a lumpcount + +#include "const.h" + +typedef struct +{ + int ident; // should be WAD3 + int numlumps; // num files + int infotableofs; // LUT offset +} dwadinfo_t; + +typedef struct +{ + int filepos; // file offset in WAD + int disksize; // compressed or uncompressed + int size; // uncompressed + signed char type; // TYP_* + signed char attribs; // file attribs + signed char pad0; + signed char pad1; + char name[WAD3_NAMELEN]; // must be null terminated +} dlumpinfo_t; + +typedef struct wfile_s +{ + string filename; + int infotableofs; + int numlumps; + poolhandle_t mempool; // W_ReadLump temp buffers + file_t *handle; + dlumpinfo_t *lumps; + time_t filetime; +} wfile_t; + +// WAD errors +#define WAD_LOAD_OK 0 +#define WAD_LOAD_COULDNT_OPEN 1 +#define WAD_LOAD_BAD_HEADER 2 +#define WAD_LOAD_BAD_FOLDERS 3 +#define WAD_LOAD_TOO_MANY_FILES 4 +#define WAD_LOAD_NO_FILES 5 +#define WAD_LOAD_CORRUPTED 6 + +typedef struct wadtype_s +{ + const char *ext; + signed char type; +} wadtype_t; + +// associate extension with wad type +static const wadtype_t wad_types[7] = +{ +{ "pal", TYP_PALETTE }, // palette +{ "dds", TYP_DDSTEX }, // DDS image +{ "lmp", TYP_GFXPIC }, // quake1, hl pic +{ "fnt", TYP_QFONT }, // hl qfonts +{ "mip", TYP_MIPTEX }, // hl/q1 mip +{ "txt", TYP_SCRIPT }, // scripts +{ NULL, TYP_NONE } +}; + +/* +=========== +W_TypeFromExt + +Extracts file type from extension +=========== +*/ +static signed char W_TypeFromExt( const char *lumpname ) +{ + const char *ext = COM_FileExtension( lumpname ); + const wadtype_t *type; + + // we not known about filetype, so match only by filename + if( !Q_strcmp( ext, "*" ) || !Q_strcmp( ext, "" )) + return TYP_ANY; + + for( type = wad_types; type->ext; type++ ) + { + if( !Q_stricmp( ext, type->ext )) + return type->type; + } + return TYP_NONE; +} + +/* +=========== +W_ExtFromType + +Convert type to extension +=========== +*/ +static const char *W_ExtFromType( signed char lumptype ) +{ + const wadtype_t *type; + + // we not known aboyt filetype, so match only by filename + if( lumptype == TYP_NONE || lumptype == TYP_ANY ) + return ""; + + for( type = wad_types; type->ext; type++ ) + { + if( lumptype == type->type ) + return type->ext; + } + return ""; +} + +/* +==================== +W_AddFileToWad + +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 ) +{ + int left, right; + dlumpinfo_t *plump; + + // look for the slot we should put that file into (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( wad->lumps[middle].type < newlump->type ) + 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 ); + } + + // 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 + plump = &wad->lumps[left]; + memmove( plump + 1, plump, ( wad->numlumps - left ) * sizeof( *plump )); + wad->numlumps++; + + *plump = *newlump; + memcpy( plump->name, name, sizeof( plump->name )); + + return plump; +} + +/* +=========== +FS_CloseWAD + +finalize wad or just close +=========== +*/ +void FS_CloseWAD( wfile_t *wad ) +{ + Mem_FreePool( &wad->mempool ); + if( wad->handle != NULL ) + FS_Close( wad->handle ); + Mem_Free( wad ); // free himself +} + +/* +=========== +W_Open + +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 ); + + 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 ); + if( error ) *error = WAD_LOAD_COULDNT_OPEN; + FS_CloseWAD( wad ); + return NULL; + } + + // copy wad name + Q_strncpy( wad->filename, filename, sizeof( wad->filename )); + wad->filetime = FS_SysFileTime( filename ); + wad->mempool = Mem_AllocPool( filename ); + + if( FS_Read( wad->handle, &header, sizeof( dwadinfo_t )) != sizeof( dwadinfo_t )) + { + Con_Reportf( S_ERROR "W_Open: %s can't read header\n", filename ); + if( error ) *error = WAD_LOAD_BAD_HEADER; + FS_CloseWAD( wad ); + return NULL; + } + + if( header.ident != IDWAD2HEADER && header.ident != IDWAD3HEADER ) + { + Con_Reportf( S_ERROR "W_Open: %s is not a WAD2 or WAD3 file\n", filename ); + if( error ) *error = WAD_LOAD_BAD_HEADER; + FS_CloseWAD( wad ); + return NULL; + } + + lumpcount = header.numlumps; + + if( lumpcount >= MAX_FILES_IN_WAD ) + { + Con_Reportf( S_WARN "W_Open: %s is full (%i lumps)\n", filename, lumpcount ); + if( error ) *error = WAD_LOAD_TOO_MANY_FILES; + } + else if( lumpcount <= 0 ) + { + Con_Reportf( S_ERROR "W_Open: %s has no lumps\n", filename ); + if( error ) *error = WAD_LOAD_NO_FILES; + FS_CloseWAD( wad ); + return NULL; + } + else if( error ) *error = WAD_LOAD_OK; + + wad->infotableofs = header.infotableofs; // save infotableofs position + + if( FS_Seek( wad->handle, wad->infotableofs, SEEK_SET ) == -1 ) + { + Con_Reportf( S_ERROR "W_Open: %s can't find lump allocation table\n", filename ); + if( error ) *error = WAD_LOAD_BAD_FOLDERS; + FS_CloseWAD( wad ); + return NULL; + } + + lat_size = lumpcount * sizeof( dlumpinfo_t ); + + // NOTE: lumps table can be reallocated for O_APPEND mode + srclumps = (dlumpinfo_t *)Mem_Malloc( wad->mempool, lat_size ); + + 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 ); + if( error ) *error = WAD_LOAD_CORRUPTED; + Mem_Free( srclumps ); + FS_CloseWAD( wad ); + return NULL; + } + + // starting to add lumps + wad->lumps = (dlumpinfo_t *)Mem_Calloc( wad->mempool, lat_size ); + wad->numlumps = 0; + + // sort lumps for binary search + for( i = 0; i < lumpcount; i++ ) + { + char name[16]; + int k; + + // cleanup lumpname + Q_strnlwr( srclumps[i].name, name, sizeof( srclumps[i].name )); + + // check for '*' symbol issues (quake1) + k = Q_strlen( Q_strrchr( name, '*' )); + if( k ) name[Q_strlen( name ) - k] = '!'; + + // check for Quake 'conchars' issues (only lmp loader really allows to read this lame pic) + if( srclumps[i].type == 68 && !Q_stricmp( srclumps[i].name, "conchars" )) + srclumps[i].type = TYP_GFXPIC; + + W_AddFileToWad( name, wad, &srclumps[i] ); + } + + // release source lumps + Mem_Free( srclumps ); + + // and leave the file open + 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; + 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 + +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_FileTimeWAD( wfile_t *wad ) +{ + return wad->filetime; +} + +void FS_PrintWADInfo( char *dst, size_t size, wfile_t *wad ) +{ + Q_snprintf( dst, size, "%s (%i files)", wad->filename, wad->numlumps ); +} + +int FS_FindFileWAD( wfile_t *wad, const char *name ) +{ + dlumpinfo_t *lump; + signed char type = W_TypeFromExt( name ); + qboolean anywadname = true; + string wadname, wadfolder; + string shortname; + + // quick reject by filetype + if( type == TYP_NONE ) + return -1; + + COM_ExtractFilePath( name, wadname ); + wadfolder[0] = '\0'; + + if( COM_CheckStringEmpty( wadname ) ) + { + COM_FileBase( wadname, wadname ); + Q_strncpy( wadfolder, wadname, sizeof( wadfolder )); + COM_DefaultExtension( wadname, ".wad" ); + anywadname = false; + } + + // make wadname from wad fullpath + COM_FileBase( wad->filename, shortname ); + COM_DefaultExtension( shortname, ".wad" ); + + // quick reject by wadname + if( !anywadname && Q_stricmp( wadname, shortname )) + return -1; + + // NOTE: we can't using long names for wad, + // because we using original wad names[16]; + COM_FileBase( name, shortname ); + + lump = W_FindLump( wad, shortname, type ); + + if( lump ) + { + return lump - wad->lumps; + } + + return -1; + +} + +void FS_SearchWAD( stringlist_t *list, wfile_t *wad, const char *pattern ) +{ + string wadpattern, wadname, temp2; + signed char type = W_TypeFromExt( pattern ); + qboolean anywadname = true; + string wadfolder, temp; + int j, i; + const char *slash, *backslash, *colon, *separator; + + // quick reject by filetype + if( type == TYP_NONE ) + return; + + COM_ExtractFilePath( pattern, wadname ); + COM_FileBase( pattern, wadpattern ); + wadfolder[0] = '\0'; + + if( COM_CheckStringEmpty( wadname )) + { + COM_FileBase( wadname, wadname ); + Q_strncpy( wadfolder, wadname, sizeof( wadfolder )); + COM_DefaultExtension( wadname, ".wad" ); + anywadname = false; + } + + // make wadname from wad fullpath + COM_FileBase( 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++ ) + { + // if type not matching, we already have no chance ... + if( type != TYP_ANY && wad->lumps[i].type != type ) + continue; + + // build the lumpname with image suffix (if present) + Q_strncpy( temp, wad->lumps[i].name, sizeof( temp )); + + while( temp[0] ) + { + if( matchpattern( temp, wadpattern, true )) + { + for( j = 0; j < list->numstrings; j++ ) + { + if( !Q_strcmp( list->strings[j], temp )) + break; + } + + if( j == list->numstrings ) + { + // 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 ))); + stringlistappend( list, temp2 ); + } + } + + // 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; + } + } +} diff --git a/filesystem/wscript b/filesystem/wscript new file mode 100644 index 00000000..bfcf35c0 --- /dev/null +++ b/filesystem/wscript @@ -0,0 +1,17 @@ +#!/usr/bin/env python + +def options(opt): + pass + +def configure(conf): + if conf.env.cxxshlib_PATTERN.startswith('lib'): + conf.env.cxxshlib_PATTERN = conf.env.cxxshlib_PATTERN[3:] + +def build(bld): + bld.shlib(target = 'filesystem_stdio', + features = 'c', + source = bld.path.ant_glob(['*.c']), + includes = ['.', '../common', '../public', '../engine'], + use = ['public'], + install_path = bld.env.LIBDIR, + subsystem = bld.env.MSVC_SUBSYSTEM) diff --git a/filesystem/zip.c b/filesystem/zip.c new file mode 100644 index 00000000..dcad291b --- /dev/null +++ b/filesystem/zip.c @@ -0,0 +1,678 @@ +/* +zip.c - ZIP support for filesystem +Copyright (C) 2019 Mr0maks +Copyright (C) 2022 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 +#include +#include +#include +#include +#include +#include STDINT_H +#include "port.h" +#include "filesystem_internal.h" +#include "crtlib.h" +#include "common/com_strings.h" +#include "miniz.h" + +#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') +#define ZIP_HEADER_EOCD ((0x06<<24)+(0x05<<16)+('K'<<8)+'P') + +#define ZIP_COMPRESSION_NO_COMPRESSION 0 +#define ZIP_COMPRESSION_DEFLATED 8 + +#define ZIP_ZIP64 0xffffffff + +#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; +} zip_header_t; + +/* + in zip64 comp and uncompr size == 0xffffffff remeber this + compressed and uncompress filesize stored in extra field +*/ + +typedef struct zip_header_extra_s +{ + unsigned int signature; // ZIP_HEADER_SPANNED + unsigned int crc32; + unsigned int compressed_size; + unsigned int 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; +} 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; +} 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 + +typedef struct zipfile_s +{ + char name[MAX_SYSPATH]; + 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; +} zipfile_t; + +typedef struct zip_s +{ + string filename; + int handle; + int numfiles; + time_t filetime; + zipfile_t *files; +} zip_t; + +#ifdef XASH_REDUCE_FD +static void FS_EnsureOpenZip( zip_t *zip ) +{ + if( fs_last_zip == zip ) + return; + + if( fs_last_zip && (fs_last_zip->handle != -1) ) + { + close( fs_last_zip->handle ); + fs_last_zip->handle = -1; + } + fs_last_zip = zip; + if( zip && (zip->handle == -1) ) + zip->handle = open( zip->filename, O_RDONLY|O_BINARY ); +} +#else +static void FS_EnsureOpenZip( zip_t *zip ) {} +#endif + +void FS_CloseZIP( zip_t *zip ) +{ + if( zip->files ) + Mem_Free( zip->files ); + + FS_EnsureOpenZip( NULL ); + + if( zip->handle >= 0 ) + close( zip->handle ); + + Mem_Free( zip ); +} + +/* +============ +FS_SortZip +============ +*/ +static int FS_SortZip( const void *a, const void *b ) +{ + return Q_stricmp( ( ( zipfile_t* )a )->name, ( ( zipfile_t* )b )->name ); +} + +/* +============ +FS_LoadZip +============ +*/ +static zip_t *FS_LoadZip( const char *zipfile, int *error ) +{ + int numpackfiles = 0, i; + zip_cdf_header_t header_cdf; + zip_header_eocd_t header_eocd; + uint32_t signature; + fs_offset_t filepos = 0, length; + zipfile_t *info = NULL; + char filename_buffer[MAX_SYSPATH]; + zip_t *zip = (zip_t *)Mem_Calloc( fs_mempool, sizeof( *zip )); + fs_size_t c; + + 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 ); + + if( error ) + *error = ZIP_LOAD_COULDNT_OPEN; + + FS_CloseZIP( zip ); + return NULL; + } + + length = lseek( zip->handle, 0, SEEK_END ); + + if( length > UINT_MAX ) + { + Con_Reportf( S_ERROR "%s bigger than 4GB.\n", zipfile ); + + if( error ) + *error = ZIP_LOAD_COULDNT_OPEN; + + FS_CloseZIP( zip ); + return NULL; + } + + lseek( zip->handle, 0, SEEK_SET ); + + c = read( zip->handle, &signature, sizeof( signature ) ); + + if( c != sizeof( signature ) || signature == ZIP_HEADER_EOCD ) + { + Con_Reportf( S_WARN "%s has no files. Ignored.\n", zipfile ); + + if( error ) + *error = ZIP_LOAD_NO_FILES; + + FS_CloseZIP( zip ); + return NULL; + } + + if( signature != ZIP_HEADER_LF ) + { + Con_Reportf( S_ERROR "%s is not a zip file. Ignored.\n", zipfile ); + + if( error ) + *error = ZIP_LOAD_BAD_HEADER; + + FS_CloseZIP( zip ); + return NULL; + } + + // Find oecd + lseek( zip->handle, 0, SEEK_SET ); + filepos = length; + + while ( filepos > 0 ) + { + lseek( zip->handle, filepos, SEEK_SET ); + c = read( zip->handle, &signature, sizeof( signature ) ); + + if( c == sizeof( signature ) && signature == ZIP_HEADER_EOCD ) + break; + + filepos -= sizeof( char ); // step back one byte + } + + if( ZIP_HEADER_EOCD != signature ) + { + Con_Reportf( S_ERROR "cannot find EOCD in %s. Zip file corrupted.\n", zipfile ); + + if( error ) + *error = ZIP_LOAD_BAD_HEADER; + + FS_CloseZIP( zip ); + return NULL; + } + + c = read( zip->handle, &header_eocd, sizeof( header_eocd ) ); + + if( c != sizeof( header_eocd )) + { + Con_Reportf( S_ERROR "invalid EOCD header in %s. Zip file corrupted.\n", zipfile ); + + if( error ) + *error = ZIP_LOAD_BAD_HEADER; + + FS_CloseZIP( zip ); + return NULL; + } + + // Move to CDF start + lseek( zip->handle, header_eocd.central_directory_offset, SEEK_SET ); + + // Calc count of files in archive + info = (zipfile_t *)Mem_Calloc( fs_mempool, sizeof( *info ) * header_eocd.total_central_directory_record ); + + for( i = 0; i < header_eocd.total_central_directory_record; i++ ) + { + c = read( zip->handle, &header_cdf, sizeof( header_cdf ) ); + + if( c != sizeof( header_cdf ) || header_cdf.signature != ZIP_HEADER_CDF ) + { + Con_Reportf( S_ERROR "CDF signature mismatch in %s. Zip file corrupted.\n", zipfile ); + + if( error ) + *error = ZIP_LOAD_BAD_HEADER; + + Mem_Free( info ); + FS_CloseZIP( zip ); + return NULL; + } + + if( header_cdf.uncompressed_size && header_cdf.filename_len && ( header_cdf.filename_len < MAX_SYSPATH ) ) + { + memset( &filename_buffer, '\0', MAX_SYSPATH ); + c = read( zip->handle, &filename_buffer, header_cdf.filename_len ); + + if( c != header_cdf.filename_len ) + { + Con_Reportf( S_ERROR "filename length mismatch in %s. Zip file corrupted.\n", zipfile ); + + if( error ) + *error = ZIP_LOAD_CORRUPTED; + + Mem_Free( info ); + FS_CloseZIP( zip ); + return NULL; + } + + Q_strncpy( info[numpackfiles].name, filename_buffer, MAX_SYSPATH ); + + info[numpackfiles].size = header_cdf.uncompressed_size; + info[numpackfiles].compressed_size = header_cdf.compressed_size; + info[numpackfiles].offset = header_cdf.local_header_offset; + numpackfiles++; + } + else + lseek( zip->handle, header_cdf.filename_len, SEEK_CUR ); + + if( header_cdf.extrafield_len ) + lseek( zip->handle, header_cdf.extrafield_len, SEEK_CUR ); + + if( header_cdf.file_commentary_len ) + lseek( zip->handle, header_cdf.file_commentary_len, SEEK_CUR ); + } + + // recalculate offsets + for( i = 0; i < numpackfiles; i++ ) + { + zip_header_t header; + + lseek( zip->handle, info[i].offset, SEEK_SET ); + c = read( zip->handle, &header, sizeof( header ) ); + + if( c != sizeof( header )) + { + Con_Reportf( S_ERROR "header length mismatch in %s. Zip file corrupted.\n", zipfile ); + + if( error ) + *error = ZIP_LOAD_CORRUPTED; + + Mem_Free( info ); + FS_CloseZIP( zip ); + return NULL; + } + + info[i].flags = header.compression_flags; + 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; + + qsort( zip->files, zip->numfiles, sizeof( *zip->files ), FS_SortZip ); + +#ifdef XASH_REDUCE_FD + // will reopen when needed + close(zip->handle); + zip->handle = -1; +#endif + + if( error ) + *error = ZIP_LOAD_OK; + + return zip; +} + +/* +=========== +FS_OpenZipFile + +Open a packed file using its package file descriptor +=========== +*/ +file_t *FS_OpenZipFile( zip_t *zip, int pack_ind ) +{ + zipfile_t *pfile; + pfile = &zip->files[pack_ind]; + + // compressed files handled in Zip_LoadFile + if( pfile->flags != ZIP_COMPRESSION_NO_COMPRESSION ) + { + Con_Printf( S_ERROR "%s: can't open compressed file %s\n", __FUNCTION__, pfile->name ); + return NULL; + } + + return FS_OpenHandle( zip->filename, zip->handle, pfile->offset, pfile->size ); +} + +byte *FS_LoadZIPFile( const char *path, fs_offset_t *sizeptr, qboolean gamedironly ) +{ + searchpath_t *search; + int index; + zipfile_t *file = NULL; + byte *compressed_buffer = NULL, *decompressed_buffer = NULL; + int zlib_result = 0; + dword test_crc, final_crc; + z_stream decompress_stream; + size_t c; + + if( sizeptr ) *sizeptr = 0; + + search = FS_FindFile( path, &index, gamedironly ); + + if( !search || search->type != SEARCHPATH_ZIP ) + return NULL; + + file = &search->zip->files[index]; + + FS_EnsureOpenZip( search->zip ); + + if( lseek( search->zip->handle, file->offset, SEEK_SET ) == -1 ) + return NULL; + + /*if( read( search->zip->handle, &header, sizeof( header ) ) < 0 ) + return NULL; + + if( header.signature != ZIP_HEADER_LF ) + { + Con_Reportf( S_ERROR "Zip_LoadFile: %s signature error\n", file->name ); + return NULL; + }*/ + + if( file->flags == ZIP_COMPRESSION_NO_COMPRESSION ) + { + decompressed_buffer = Mem_Malloc( fs_mempool, file->size + 1 ); + decompressed_buffer[file->size] = '\0'; + + c = read( search->zip->handle, decompressed_buffer, file->size ); + if( c != file->size ) + { + Con_Reportf( S_ERROR "Zip_LoadFile: %s size doesn't match\n", file->name ); + return NULL; + } + +#if 0 + CRC32_Init( &test_crc ); + CRC32_ProcessBuffer( &test_crc, decompressed_buffer, file->size ); + + final_crc = CRC32_Final( test_crc ); + + if( final_crc != file->crc32 ) + { + Con_Reportf( S_ERROR "Zip_LoadFile: %s file crc32 mismatch\n", file->name ); + Mem_Free( decompressed_buffer ); + return NULL; + } +#endif + if( sizeptr ) *sizeptr = file->size; + + FS_EnsureOpenZip( NULL ); + return decompressed_buffer; + } + else if( file->flags == ZIP_COMPRESSION_DEFLATED ) + { + compressed_buffer = Mem_Malloc( fs_mempool, file->compressed_size + 1 ); + decompressed_buffer = Mem_Malloc( fs_mempool, file->size + 1 ); + decompressed_buffer[file->size] = '\0'; + + c = read( search->zip->handle, compressed_buffer, file->compressed_size ); + if( c != file->compressed_size ) + { + Con_Reportf( S_ERROR "Zip_LoadFile: %s compressed size doesn't match\n", file->name ); + return NULL; + } + + memset( &decompress_stream, 0, sizeof( decompress_stream ) ); + + decompress_stream.total_in = decompress_stream.avail_in = file->compressed_size; + decompress_stream.next_in = (Bytef *)compressed_buffer; + decompress_stream.total_out = decompress_stream.avail_out = file->size; + decompress_stream.next_out = (Bytef *)decompressed_buffer; + + decompress_stream.zalloc = Z_NULL; + decompress_stream.zfree = Z_NULL; + decompress_stream.opaque = Z_NULL; + + if( inflateInit2( &decompress_stream, -MAX_WBITS ) != Z_OK ) + { + Con_Printf( S_ERROR "Zip_LoadFile: inflateInit2 failed\n" ); + Mem_Free( compressed_buffer ); + Mem_Free( decompressed_buffer ); + return NULL; + } + + zlib_result = inflate( &decompress_stream, Z_NO_FLUSH ); + inflateEnd( &decompress_stream ); + + if( zlib_result == Z_OK || zlib_result == Z_STREAM_END ) + { + Mem_Free( compressed_buffer ); // finaly free compressed buffer +#if 0 + CRC32_Init( &test_crc ); + CRC32_ProcessBuffer( &test_crc, decompressed_buffer, file->size ); + + final_crc = CRC32_Final( test_crc ); + + if( final_crc != file->crc32 ) + { + Con_Reportf( S_ERROR "Zip_LoadFile: %s file crc32 mismatch\n", file->name ); + Mem_Free( decompressed_buffer ); + return NULL; + } +#endif + if( sizeptr ) *sizeptr = file->size; + + FS_EnsureOpenZip( NULL ); + return decompressed_buffer; + } + else + { + Con_Reportf( S_ERROR "Zip_LoadFile: %s : error while file decompressing. Zlib return code %d.\n", file->name, zlib_result ); + Mem_Free( compressed_buffer ); + Mem_Free( decompressed_buffer ); + return NULL; + } + + } + else + { + Con_Reportf( S_ERROR "Zip_LoadFile: %s : file compressed with unknown algorithm.\n", file->name ); + return NULL; + } + + FS_EnsureOpenZip( NULL ); + return NULL; +} + + +qboolean FS_AddZip_Fullpath( const char *zipfile, qboolean *already_loaded, int flags ) +{ + searchpath_t *search; + zip_t *zip = NULL; + const char *ext = COM_FileExtension( zipfile ); + int errorcode = ZIP_LOAD_COULDNT_OPEN; + + for( search = fs_searchpaths; search; search = search->next ) + { + if( search->type == SEARCHPATH_ZIP && !Q_stricmp( search->zip->filename, zipfile )) + { + if( already_loaded ) *already_loaded = true; + return true; // already loaded + } + } + + if( already_loaded ) *already_loaded = false; + + if( !Q_stricmp( ext, "pk3" ) ) + zip = FS_LoadZip( zipfile, &errorcode ); + + if( zip ) + { + string fullpath; + int i; + + search = (searchpath_t *)Mem_Calloc( fs_mempool, sizeof( searchpath_t ) ); + search->zip = zip; + search->type = SEARCHPATH_ZIP; + search->next = fs_searchpaths; + search->flags |= flags; + fs_searchpaths = search; + + Con_Reportf( "Adding zipfile: %s (%i files)\n", zipfile, zip->numfiles ); + + // time to add in search list all the wads that contains in current pakfile (if do) + for( i = 0; i < zip->numfiles; i++ ) + { + if( !Q_stricmp( COM_FileExtension( zip->files[i].name ), "wad" )) + { + Q_snprintf( fullpath, MAX_STRING, "%s/%s", zipfile, zip->files[i].name ); + FS_AddWad_Fullpath( fullpath, NULL, flags ); + } + } + return true; + } + else + { + if( errorcode != ZIP_LOAD_NO_FILES ) + Con_Reportf( S_ERROR "FS_AddZip_Fullpath: unable to load zip \"%s\"\n", zipfile ); + return false; + } +} + +int FS_FileTimeZIP( zip_t *zip ) +{ + return zip->filetime; +} + +void FS_PrintZIPInfo( char *dst, size_t size, zip_t *zip ) +{ + Q_snprintf( dst, size, "%s (%i files)", zip->filename, zip->numfiles ); +} + +int FS_FindFileZIP( zip_t *zip, const char *name ) +{ + int left, right, middle; + + // look for the file (binary search) + left = 0; + right = zip->numfiles - 1; + while( left <= right ) + { + int diff; + + middle = (left + right) / 2; + diff = Q_stricmp( zip->files[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; +} + +void FS_SearchZIP( stringlist_t *list, zip_t *zip, const char *pattern ) +{ + string temp; + const char *slash, *backslash, *colon, *separator; + int j, i; + + for( i = 0; i < zip->numfiles; i++ ) + { + Q_strncpy( temp, 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; + } + } +} + diff --git a/public/crtlib.c b/public/crtlib.c index d183c7a5..652ae917 100644 --- a/public/crtlib.c +++ b/public/crtlib.c @@ -819,6 +819,23 @@ void COM_RemoveLineFeed( char *str ) } } +/* +============ +COM_FixSlashes + +Changes all '/' characters into '\' characters, in place. +============ +*/ +void COM_FixSlashes( char *pname ) +{ + while( *pname ) + { + if( *pname == '\\' ) + *pname = '/'; + pname++; + } +} + /* ============ COM_PathSlashFix diff --git a/public/crtlib.h b/public/crtlib.h index 2d44939f..84027ea6 100644 --- a/public/crtlib.h +++ b/public/crtlib.h @@ -95,6 +95,7 @@ void COM_ExtractFilePath( const char *path, char *dest ); const char *COM_FileWithoutPath( const char *in ); void COM_StripExtension( char *path ); void COM_RemoveLineFeed( char *str ); +void COM_FixSlashes( char *pname ); void COM_PathSlashFix( char *path ); char COM_Hex2Char( uint8_t hex ); void COM_Hex2String( uint8_t hex, char *str ); diff --git a/engine/common/miniz.h b/public/miniz.h similarity index 100% rename from engine/common/miniz.h rename to public/miniz.h diff --git a/ref_gl/gl_alias.c b/ref_gl/gl_alias.c index 77b27b77..d9766755 100644 --- a/ref_gl/gl_alias.c +++ b/ref_gl/gl_alias.c @@ -486,7 +486,7 @@ void *Mod_LoadSingleSkin( daliasskintype_t *pskintype, int skinnum, int size ) Q_snprintf( name, sizeof( name ), "%s:frame%i", loadmodel->name, skinnum ); Q_snprintf( lumaname, sizeof( lumaname ), "%s:luma%i", loadmodel->name, skinnum ); Q_snprintf( checkname, sizeof( checkname ), "%s_%i.tga", loadmodel->name, skinnum ); - if( !gEngfuncs.FS_FileExists( checkname, false ) || ( pic = gEngfuncs.FS_LoadImage( checkname, NULL, 0 )) == NULL ) + if( !gEngfuncs.fsapi->FileExists( checkname, false ) || ( pic = gEngfuncs.FS_LoadImage( checkname, NULL, 0 )) == NULL ) pic = Mod_CreateSkinData( loadmodel, (byte *)(pskintype + 1), m_pAliasHeader->skinwidth, m_pAliasHeader->skinheight ); m_pAliasHeader->gl_texturenum[skinnum][0] = diff --git a/ref_gl/gl_backend.c b/ref_gl/gl_backend.c index 88881f9f..da530405 100644 --- a/ref_gl/gl_backend.c +++ b/ref_gl/gl_backend.c @@ -478,7 +478,7 @@ qboolean VID_ScreenShot( const char *filename, int shot_type ) case VID_SCREENSHOT: break; case VID_SNAPSHOT: - gEngfuncs.FS_AllowDirectPaths( true ); + gEngfuncs.fsapi->AllowDirectPaths( true ); break; case VID_LEVELSHOT: flags |= IMAGE_RESAMPLE; @@ -509,7 +509,7 @@ qboolean VID_ScreenShot( const char *filename, int shot_type ) // write image result = gEngfuncs.FS_SaveImage( filename, r_shot ); - gEngfuncs.FS_AllowDirectPaths( false ); // always reset after store screenshot + gEngfuncs.fsapi->AllowDirectPaths( false ); // always reset after store screenshot gEngfuncs.FS_FreeImage( r_shot ); return result; diff --git a/ref_gl/gl_rmisc.c b/ref_gl/gl_rmisc.c index c402e337..230e66b9 100644 --- a/ref_gl/gl_rmisc.c +++ b/ref_gl/gl_rmisc.c @@ -29,7 +29,7 @@ static void R_ParseDetailTextures( const char *filename ) texture_t *tex; int i; - afile = gEngfuncs.COM_LoadFile( filename, NULL, false ); + afile = gEngfuncs.fsapi->LoadFile( filename, NULL, false ); if( !afile ) return; pfile = (char *)afile; diff --git a/ref_gl/gl_studio.c b/ref_gl/gl_studio.c index bb52c262..c71630d9 100644 --- a/ref_gl/gl_studio.c +++ b/ref_gl/gl_studio.c @@ -2699,7 +2699,7 @@ static model_t *R_StudioSetupPlayerModel( int index ) Q_snprintf( state->modelname, sizeof( state->modelname ), "models/player/%s/%s.mdl", info->model, info->model ); - if( gEngfuncs.FS_FileExists( state->modelname, false )) + if( gEngfuncs.fsapi->FileExists( state->modelname, false )) state->model = gEngfuncs.Mod_ForName( state->modelname, false, true ); else state->model = NULL; diff --git a/ref_gl/gl_warp.c b/ref_gl/gl_warp.c index 32359d89..72e77c1c 100644 --- a/ref_gl/gl_warp.c +++ b/ref_gl/gl_warp.c @@ -78,7 +78,7 @@ static int CheckSkybox( const char *name ) { // build side name sidename = va( "%s%s.%s", name, r_skyBoxSuffix[j], skybox_ext[i] ); - if( gEngfuncs.FS_FileExists( sidename, false )) + if( gEngfuncs.fsapi->FileExists( sidename, false )) num_checked_sides++; } @@ -90,7 +90,7 @@ static int CheckSkybox( const char *name ) { // build side name sidename = va( "%s_%s.%s", name, r_skyBoxSuffix[j], skybox_ext[i] ); - if( gEngfuncs.FS_FileExists( sidename, false )) + if( gEngfuncs.fsapi->FileExists( sidename, false )) num_checked_sides++; } diff --git a/ref_gl/wscript b/ref_gl/wscript index af60caea..207e7ec6 100644 --- a/ref_gl/wscript +++ b/ref_gl/wscript @@ -59,6 +59,7 @@ def build(bld): source = bld.path.ant_glob(['*.c']) includes = ['.', + '../filesystem', '../engine', '../engine/common', '../engine/server', diff --git a/ref_soft/r_studio.c b/ref_soft/r_studio.c index 3dad15b6..0a68035a 100644 --- a/ref_soft/r_studio.c +++ b/ref_soft/r_studio.c @@ -2462,7 +2462,7 @@ static model_t *R_StudioSetupPlayerModel( int index ) Q_snprintf( state->modelname, sizeof( state->modelname ), "models/player/%s/%s.mdl", info->model, info->model ); - if( gEngfuncs.FS_FileExists( state->modelname, false )) + if( gEngfuncs.fsapi->FileExists( state->modelname, false )) state->model = gEngfuncs.Mod_ForName( state->modelname, false, true ); else state->model = NULL; diff --git a/ref_soft/wscript b/ref_soft/wscript index 93933140..9504cd84 100644 --- a/ref_soft/wscript +++ b/ref_soft/wscript @@ -30,6 +30,7 @@ def build(bld): source = bld.path.ant_glob(['*.c']) includes = ['.', + '../filesystem', '../engine', '../engine/common', '../engine/server', diff --git a/wscript b/wscript index f87fc7ff..6565e53c 100644 --- a/wscript +++ b/wscript @@ -55,6 +55,7 @@ class Subproject: SUBDIRS = [ Subproject('public', dedicated=False, mandatory = True), + Subproject('filesystem', dedicated=False, mandatory = True), Subproject('game_launch', singlebin=True), Subproject('ref_gl',), Subproject('ref_soft'), From 56d7c05b5180347dcd9ad6050f676a4de43708e7 Mon Sep 17 00:00:00 2001 From: Alibek Omarov Date: Mon, 4 Jul 2022 18:30:10 +0300 Subject: [PATCH 109/300] filesystem: generated VFileSystem009 from pdwtags output, removed unneeded in C++ this first argument --- filesystem/VFileSystem009.h | 153 ++++++++++++++++++++++++++++++++++++ 1 file changed, 153 insertions(+) create mode 100644 filesystem/VFileSystem009.h diff --git a/filesystem/VFileSystem009.h b/filesystem/VFileSystem009.h new file mode 100644 index 00000000..a0b73123 --- /dev/null +++ b/filesystem/VFileSystem009.h @@ -0,0 +1,153 @@ +/* +VFileSystem009.h - C++ interface for filesystem_stdio +Copyright (C) 2022 Alibek Omarov + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. +*/ + +#ifndef VFILESYSTEM009_H +#define VFILESYSTEM009_H + +// exported from dwarf +typedef enum { + FILESYSTEM_SEEK_HEAD = 0, + FILESYSTEM_SEEK_CURRENT = 1, + FILESYSTEM_SEEK_TAIL = 2, +} FileSystemSeek_t; /* size: 4 */ + +typedef enum { + FILESYSTEM_WARNING_QUIET = 0, + FILESYSTEM_WARNING_REPORTUNCLOSED = 1, + FILESYSTEM_WARNING_REPORTUSAGE = 2, + FILESYSTEM_WARNING_REPORTALLACCESSES = 3, +} FileWarningLevel_t; /* size: 4 */ + +typedef void * FileHandle_t; /* size: 4 */ +typedef int FileFindHandle_t; /* size: 4 */ +typedef int WaitForResourcesHandle_t; /* size: 4 */ + +class IBaseInterface +{ +public: + virtual ~IBaseInterface() {} +}; + +class IVFileSystem009 : public IBaseInterface +{ +public: + virtual void Mount() = 0; /* linkage=_ZN11IFileSystem5MountEv */ + + virtual void Unmount() = 0; /* linkage=_ZN11IFileSystem7UnmountEv */ + + virtual void RemoveAllSearchPaths() = 0; /* linkage=_ZN11IFileSystem20RemoveAllSearchPathsEv */ + + virtual void AddSearchPath(const char *, const char *) = 0; /* linkage=_ZN11IFileSystem13AddSearchPathEPKcS1_ */ + + virtual bool RemoveSearchPath(const char *) = 0; /* linkage=_ZN11IFileSystem16RemoveSearchPathEPKc */ + + virtual void RemoveFile(const char *, const char *) = 0; /* linkage=_ZN11IFileSystem10RemoveFileEPKcS1_ */ + + virtual void CreateDirHierarchy(const char *, const char *) = 0; /* linkage=_ZN11IFileSystem18CreateDirHierarchyEPKcS1_ */ + + virtual bool FileExists(const char *) = 0; /* linkage=_ZN11IFileSystem10FileExistsEPKc */ + + virtual bool IsDirectory(const char *) = 0; /* linkage=_ZN11IFileSystem11IsDirectoryEPKc */ + + virtual FileHandle_t Open(const char *, const char *, const char *) = 0; /* linkage=_ZN11IFileSystem4OpenEPKcS1_S1_ */ + + virtual void Close(FileHandle_t) = 0; /* linkage=_ZN11IFileSystem5CloseEPv */ + + virtual void Seek(FileHandle_t, int, FileSystemSeek_t) = 0; /* linkage=_ZN11IFileSystem4SeekEPvi16FileSystemSeek_t */ + + virtual unsigned int Tell(FileHandle_t) = 0; /* linkage=_ZN11IFileSystem4TellEPv */ + + virtual unsigned int Size(FileHandle_t) = 0; /* linkage=_ZN11IFileSystem4SizeEPv */ + + virtual unsigned int Size(const char *) = 0; /* linkage=_ZN11IFileSystem4SizeEPKc */ + + virtual long int GetFileTime(const char *) = 0; /* linkage=_ZN11IFileSystem11GetFileTimeEPKc */ + + virtual void FileTimeToString(char *, int, long int) = 0; /* linkage=_ZN11IFileSystem16FileTimeToStringEPcil */ + + virtual bool IsOk(FileHandle_t) = 0; /* linkage=_ZN11IFileSystem4IsOkEPv */ + + virtual void Flush(FileHandle_t) = 0; /* linkage=_ZN11IFileSystem5FlushEPv */ + + virtual bool EndOfFile(FileHandle_t) = 0; /* linkage=_ZN11IFileSystem9EndOfFileEPv */ + + virtual int Read(void *, int, FileHandle_t) = 0; /* linkage=_ZN11IFileSystem4ReadEPviS0_ */ + + virtual int Write(const void *, int, FileHandle_t) = 0; /* linkage=_ZN11IFileSystem5WriteEPKviPv */ + + virtual char * ReadLine(char *, int, FileHandle_t) = 0; /* linkage=_ZN11IFileSystem8ReadLineEPciPv */ + + virtual int FPrintf(FileHandle_t, char *, ...) = 0; /* linkage=_ZN11IFileSystem7FPrintfEPvPcz */ + + virtual void * GetReadBuffer(FileHandle_t, int *, bool) = 0; /* linkage=_ZN11IFileSystem13GetReadBufferEPvPib */ + + virtual void ReleaseReadBuffer(FileHandle_t, void *) = 0; /* linkage=_ZN11IFileSystem17ReleaseReadBufferEPvS0_ */ + + virtual const char * FindFirst(const char *, FileFindHandle_t *, const char *) = 0; /* linkage=_ZN11IFileSystem9FindFirstEPKcPiS1_ */ + + virtual const char * FindNext(FileFindHandle_t) = 0; /* linkage=_ZN11IFileSystem8FindNextEi */ + + virtual bool FindIsDirectory(FileFindHandle_t) = 0; /* linkage=_ZN11IFileSystem15FindIsDirectoryEi */ + + virtual void FindClose(FileFindHandle_t) = 0; /* linkage=_ZN11IFileSystem9FindCloseEi */ + + virtual void GetLocalCopy(const char *) = 0; /* linkage=_ZN11IFileSystem12GetLocalCopyEPKc */ + + virtual const char * GetLocalPath(const char *, char *, int) = 0; /* linkage=_ZN11IFileSystem12GetLocalPathEPKcPci */ + + virtual char * ParseFile(char *, char *, bool *) = 0; /* linkage=_ZN11IFileSystem9ParseFileEPcS0_Pb */ + + virtual bool FullPathToRelativePath(const char *, char *) = 0; /* linkage=_ZN11IFileSystem22FullPathToRelativePathEPKcPc */ + + virtual bool GetCurrentDirectory(char *, int) = 0; /* linkage=_ZN11IFileSystem19GetCurrentDirectoryEPci */ + + virtual void PrintOpenedFiles() = 0; /* linkage=_ZN11IFileSystem16PrintOpenedFilesEv */ + + virtual void SetWarningFunc(void (*)(const char *, ...)) = 0; /* linkage=_ZN11IFileSystem14SetWarningFuncEPFvPKczE */ + + virtual void SetWarningLevel(FileWarningLevel_t) = 0; /* linkage=_ZN11IFileSystem15SetWarningLevelE18FileWarningLevel_t */ + + virtual void LogLevelLoadStarted(const char *) = 0; /* linkage=_ZN11IFileSystem19LogLevelLoadStartedEPKc */ + + virtual void LogLevelLoadFinished(const char *) = 0; /* linkage=_ZN11IFileSystem20LogLevelLoadFinishedEPKc */ + + virtual int HintResourceNeed(const char *, int) = 0; /* linkage=_ZN11IFileSystem16HintResourceNeedEPKci */ + + virtual int PauseResourcePreloading() = 0; /* linkage=_ZN11IFileSystem23PauseResourcePreloadingEv */ + + virtual int ResumeResourcePreloading() = 0; /* linkage=_ZN11IFileSystem24ResumeResourcePreloadingEv */ + + virtual int SetVBuf(FileHandle_t, char *, int, long int) = 0; /* linkage=_ZN11IFileSystem7SetVBufEPvPcil */ + + virtual void GetInterfaceVersion(char *, int) = 0; /* linkage=_ZN11IFileSystem19GetInterfaceVersionEPci */ + + virtual bool IsFileImmediatelyAvailable(const char *) = 0; /* linkage=_ZN11IFileSystem26IsFileImmediatelyAvailableEPKc */ + + virtual WaitForResourcesHandle_t WaitForResources(const char *) = 0; /* linkage=_ZN11IFileSystem16WaitForResourcesEPKc */ + + virtual bool GetWaitForResourcesProgress(WaitForResourcesHandle_t, float *, bool *) = 0; /* linkage=_ZN11IFileSystem27GetWaitForResourcesProgressEiPfPb */ + + virtual void CancelWaitForResources(WaitForResourcesHandle_t) = 0; /* linkage=_ZN11IFileSystem22CancelWaitForResourcesEi */ + + virtual bool IsAppReadyForOfflinePlay(int) = 0; /* linkage=_ZN11IFileSystem24IsAppReadyForOfflinePlayEi */ + + virtual bool AddPackFile(const char *, const char *) = 0; /* linkage=_ZN11IFileSystem11AddPackFileEPKcS1_ */ + + virtual FileHandle_t OpenFromCacheForRead(const char *, const char *, const char *) = 0; /* linkage=_ZN11IFileSystem20OpenFromCacheForReadEPKcS1_S1_ */ + + virtual void AddSearchPathNoWrite(const char *, const char *) = 0; /* linkage=_ZN11IFileSystem20AddSearchPathNoWriteEPKcS1_ */ +}; + +#endif // VFILESYSTEM009_H From 55a29e6e6bdc0e861f5e585745a1e7b52a290602 Mon Sep 17 00:00:00 2001 From: Alibek Omarov Date: Mon, 11 Jul 2022 03:58:59 +0300 Subject: [PATCH 110/300] filesystem: implement VFileSystem009 interface --- filesystem/VFileSystem009.cpp | 497 +++++++++++++++++++++++++++++++ filesystem/filesystem.c | 7 +- filesystem/filesystem_internal.h | 4 + filesystem/wscript | 4 +- 4 files changed, 506 insertions(+), 6 deletions(-) create mode 100644 filesystem/VFileSystem009.cpp diff --git a/filesystem/VFileSystem009.cpp b/filesystem/VFileSystem009.cpp new file mode 100644 index 00000000..adac91bc --- /dev/null +++ b/filesystem/VFileSystem009.cpp @@ -0,0 +1,497 @@ +/* +VFileSystem009.h - C++ interface for filesystem_stdio +Copyright (C) 2022 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 +#include +#include +#include +#include ALLOCA_H +#include "crtlib.h" +#include "filesystem.h" +#include "filesystem_internal.h" +#include "VFileSystem009.h" + +#if __cplusplus < 201103L +#define override +#endif + +// GoldSrc Directories and ID +// GAME gamedir +// GAMECONFIG gamedir (rodir integration?) +// GAMEDOWNLOAD gamedir_downloads (gamedir/downloads for us) +// GAME_FALLBACK liblist.gam's fallback_dir +// ROOT and BASE rootdir +// PLATFORM platform +// CONFIG platform/config + +static inline qboolean IsIdGamedir( const char *id ) +{ + return !Q_strcmp( id, "GAME" ) || + !Q_strcmp( id, "GAMECONFIG" ) || + !Q_strcmp( id, "GAMEDOWNLOAD" ); +} + +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 + else if( !Q_strcmp( id, "PLATFORM" )) + return "platform"; // stub + else if( !Q_strcmp( id, "CONFIG" )) + return "platform/config"; // stub + else // ROOT || BASE + return fs_rootdir; // give at least root directory +} + +static inline void CopyAndFixSlashes( char *p, const char *in ) +{ + Q_strcpy( p, in ); + COM_FixSlashes( p ); +} + +class CXashFS : public IVFileSystem009 +{ +private: + class CSearchState + { + public: + CSearchState( CSearchState **head, search_t *search ) : + next( *head ), search( search ), index( 0 ) + { + if( *head ) + handle = (*head)->handle + 1; + else handle = 0; + + *head = this; + } + ~CSearchState() + { + Mem_Free( search ); + } + + CSearchState *next; + search_t *search; + int index; + FileFindHandle_t handle; + }; + + CSearchState *searchHead; + + CSearchState *GetSearchStateByHandle( FileFindHandle_t handle ) + { + for( CSearchState *state = searchHead; state; state = state->next ) + { + if( state->handle == handle ) + { + return state; + } + } + + Con_DPrintf( "Can't find search state by handle %d\n", handle ); + return NULL; + } + +public: + CXashFS() : searchHead( NULL ) + { + } + + void RemoveAllSearchPaths() override + { + FS_ClearSearchPath(); + } + + void AddSearchPath( const char *path, const char *id ) override + { + char *p = (char *)alloca( Q_strlen( path ) + 1 ); + CopyAndFixSlashes( p, path ); + + FS_AddGameDirectory( p, FS_CUSTOM_PATH ); + } + + void AddSearchPathNoWrite( const char *path, const char *id ) override + { + char *p = (char *)alloca( Q_strlen( path ) + 1 ); + CopyAndFixSlashes( p, path ); + + FS_AddGameDirectory( p, FS_NOWRITE_PATH | FS_CUSTOM_PATH ); + } + + bool RemoveSearchPath( const char *id ) override + { + // TODO: + return true; + } + + void RemoveFile( const char *path, const char *id ) override + { + FS_Delete( path ); // FS_Delete is aware of slashes + } + + void CreateDirHierarchy( const char *path, const char *id ) override + { + FS_CreatePath( va( "%s/%s", IdToDir( id ), path )); // FS_CreatePath is aware of slashes + } + + bool FileExists( const char *path ) override + { + char *p = (char *)alloca( Q_strlen( path ) + 1 ); + CopyAndFixSlashes( p, path ); + + return FS_FileExists( p, false ); + } + + bool IsDirectory( const char *path ) override + { + char *p = (char *)alloca( Q_strlen( path ) + 1 ); + CopyAndFixSlashes( p, path ); + + return FS_SysFolderExists( p ); + } + + FileHandle_t Open( const char *path, const char *mode, const char *id ) override + { + char *p = (char *)alloca( Q_strlen( path ) + 1 ); + CopyAndFixSlashes( p, path ); + + file_t *fd = FS_Open( p, mode, IsIdGamedir( id ) ); + + return fd; + } + + void Close( FileHandle_t handle ) override + { + FS_Close( (file_t *)handle ); + } + + void Seek( FileHandle_t handle, int offset, FileSystemSeek_t whence ) override + { + int whence_ = SEEK_SET; + switch( whence ) + { + case FILESYSTEM_SEEK_HEAD: whence_ = SEEK_SET; break; + case FILESYSTEM_SEEK_CURRENT: whence_ = SEEK_CUR; break; + case FILESYSTEM_SEEK_TAIL: whence_ = SEEK_END; break; + } + + FS_Seek( (file_t *)handle, offset, whence_ ); + } + + unsigned int Tell( FileHandle_t handle ) override + { + return FS_Tell( (file_t *)handle ); + } + + unsigned int Size( FileHandle_t handle ) override + { + file_t *fd = (file_t *)handle; + return fd->real_length; + } + + unsigned int Size( const char *path ) override + { + char *p = (char *)alloca( Q_strlen( path ) + 1 ); + CopyAndFixSlashes( p, path ); + return FS_FileSize( p, false ); + } + + long int GetFileTime( const char *path ) override + { + char *p = (char *)alloca( Q_strlen( path ) + 1 ); + CopyAndFixSlashes( p, path ); + return FS_FileTime( p, false ); + } + + void FileTimeToString( char *p, int size, long int time ) override + { + char *buf = ctime( &time ); + + Q_strncpy( p, buf, size ); + } + + bool IsOk( FileHandle_t handle ) override + { + return !FS_Eof( (file_t *)handle ); + } + + void Flush( FileHandle_t handle ) override + { + FS_Flush( (file_t *)handle ); + } + + bool EndOfFile( FileHandle_t handle ) override + { + return FS_Eof( (file_t *)handle ); + } + + int Read( void *buf, int size, FileHandle_t handle ) override + { + return FS_Read( (file_t *)handle, buf, size ); + } + + int Write( const void *buf, int size, FileHandle_t handle ) override + { + return FS_Write( (file_t *)handle, buf, size ); + } + + char *ReadLine( char *buf, int size, FileHandle_t handle ) override + { + int c = FS_Gets( (file_t *)handle, (byte*)buf, size ); + + return c >= 0 ? buf : NULL; + } + + int FPrintf( FileHandle_t handle, char *fmt, ... ) override + { + va_list ap; + int ret; + + va_start( ap, fmt ); + ret = FS_VPrintf( (file_t *)handle, fmt, ap ); + va_end( ap ); + + return ret; + } + + void * GetReadBuffer(FileHandle_t, int *size, bool) override + { + // deprecated by Valve + *size = 0; + return NULL; + } + + void ReleaseReadBuffer(FileHandle_t, void *) override + { + // deprecated by Valve + return; + } + + const char *FindFirst(const char *pattern, FileFindHandle_t *handle, const char *id) override + { + if( !handle || !pattern ) + return NULL; + + char *p = (char *)alloca( Q_strlen( pattern ) + 1 ); + CopyAndFixSlashes( p, pattern ); + search_t *search = FS_Search( p, true, IsIdGamedir( id )); + + if( !search ) + return NULL; + + CSearchState *state = new CSearchState( &searchHead, search ); + + *handle = state->handle; + + return state->search->filenames[0]; + } + + const char *FindNext( FileFindHandle_t handle ) override + { + CSearchState *state = GetSearchStateByHandle( handle ); + + if( !state ) return NULL; + + if( state->index + 1 >= state->search->numfilenames ) + return NULL; + + return state->search->filenames[++state->index]; + } + + bool FindIsDirectory( FileFindHandle_t handle ) override + { + CSearchState *state = GetSearchStateByHandle( handle ); + + if( !state ) + return false; + + if( state->index >= state->search->numfilenames ) + return false; + + return IsDirectory( state->search->filenames[state->index] ); + } + + void FindClose( FileFindHandle_t handle ) override + { + for( CSearchState *state = searchHead, **prev = NULL; + state; + *prev = state, state = state->next ) + { + if( state->handle == handle ) + { + if( prev ) + (*prev)->next = state->next; + else searchHead = state->next; + + delete state; + + return; + } + } + + Con_DPrintf( "FindClose: Can't find search state by handle %d\n", handle ); + return; + } + + const char * GetLocalPath( const char *name, char *buf, int size ) override + { + if( !name ) return NULL; + + char *p = (char *)alloca( Q_strlen( name ) + 1 ); + CopyAndFixSlashes( p, name ); + +#if !XASH_WIN32 + if( p[0] == '/' ) +#else + if( Q_strchr( p, ':' )) +#endif + { + Q_strncpy( buf, p, size ); + + return buf; + } + + + const char *fullpath = FS_GetDiskPath( p, false ); + if( !fullpath ) + return NULL; + + Q_strncpy( buf, fullpath, size ); + return buf; + } + + char *ParseFile( char *buf, char *token, bool *quoted ) override + { + qboolean qquoted; + + char *p = COM_ParseFileSafe( buf, token, PFILE_FS_TOKEN_MAX_LENGTH, 0, NULL, &qquoted ); + if( quoted ) *quoted = qquoted; + + return p; + } + + bool FullPathToRelativePath( const char *path, char *out ) override + { + if( !COM_CheckString( path )) + { + *out = 0; + return false; + } + + char *p = (char *)alloca( Q_strlen( path ) + 1 ); + CopyAndFixSlashes( p, path ); + searchpath_t *sp; + + for( sp = fs_searchpaths; sp; sp = sp->next ) + { + size_t splen = Q_strlen( sp->filename ); + + if( !Q_strnicmp( sp->filename, p, splen )) + { + Q_strcpy( out, p + splen + 1 ); + return true; + } + } + + Q_strcpy( out, p ); + return false; + } + + bool GetCurrentDirectory( char *p, int size ) override + { + Q_strncpy( p, fs_rootdir, size ); + + return true; + } + + void PrintOpenedFiles() override + { + // we don't track this yet + return; + } + + void SetWarningFunc(void (*)(const char *, ...)) override + { + // TODO: + return; + } + + void SetWarningLevel(FileWarningLevel_t) override + { + // TODO: + return; + } + + int SetVBuf( FileHandle_t handle, char *buf, int mode, long int size ) override + { + // TODO: + return 0; + } + + void GetInterfaceVersion(char *p, int size) override + { + Q_strncpy( p, "Stdio", size ); + } + + bool AddPackFile( const char *path, const char *id ) override + { + char *p = va( "%s/%s", IdToDir( id ), path ); + CopyAndFixSlashes( p, path ); + + return !!FS_AddPak_Fullpath( p, NULL, FS_CUSTOM_PATH ); + } + + FileHandle_t OpenFromCacheForRead( const char *path , const char *mode, const char *id ) override + { + char *p = (char *)alloca( Q_strlen( path ) + 1 ); + CopyAndFixSlashes( p, path ); + + return FS_OpenReadFile( p, mode, IsIdGamedir( id )); + } + + // stubs + void Mount() override {} + void Unmount() override {} + void GetLocalCopy(const char *) override {} + void LogLevelLoadStarted(const char *) override {} + void LogLevelLoadFinished(const char *) override {} + void CancelWaitForResources(WaitForResourcesHandle_t) override {} + int HintResourceNeed(const char *, int) override { return 0; } + WaitForResourcesHandle_t WaitForResources(const char *) override { return 0; } + int PauseResourcePreloading() override { return 0; } + int ResumeResourcePreloading() override { return 0; } + bool IsAppReadyForOfflinePlay(int) override { return true; } + bool IsFileImmediatelyAvailable(const char *) override { return true; } + bool GetWaitForResourcesProgress(WaitForResourcesHandle_t, float *progress, bool *override) override + { + if( progress ) *progress = 0; + if( override ) *override = true; + return false; + } +} g_VFileSystem009; + +extern "C" void EXPORT *CreateInterface( const char *interface, int *retval ) +{ + if( !Q_strcmp( interface, "VFileSystem009" )) + { + if( retval ) *retval = 0; + return &g_VFileSystem009; + } + + if( retval ) *retval = 1; + return NULL; +} diff --git a/filesystem/filesystem.c b/filesystem/filesystem.c index 6f755d92..55298c6c 100644 --- a/filesystem/filesystem.c +++ b/filesystem/filesystem.c @@ -40,23 +40,22 @@ GNU General Public License for more details. #include "common/protocol.h" #define FILE_COPY_SIZE (1024 * 1024) + +fs_globals_t FI; qboolean fs_ext_path = false; // attempt to read\write from ./ or ../ pathes 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) 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 -static char fs_writedir[MAX_SYSPATH]; // path that game allows to overwrite, delete and rename files (and create new of course) #if !XASH_WIN32 static qboolean fs_caseinsensitive = true; // try to search missing files #endif -static fs_globals_t FI; -#define GI FI.GameInfo - #ifdef XASH_REDUCE_FD static file_t *fs_last_readfile; static zip_t *fs_last_zip; diff --git a/filesystem/filesystem_internal.h b/filesystem/filesystem_internal.h index c466b1db..b70cb77f 100644 --- a/filesystem/filesystem_internal.h +++ b/filesystem/filesystem_internal.h @@ -79,12 +79,16 @@ typedef struct searchpath_s struct searchpath_s *next; } searchpath_t; +extern fs_globals_t FI; extern searchpath_t *fs_searchpaths; 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]; + +#define GI FI.GameInfo #define Mem_Malloc( pool, size ) g_engfuncs._Mem_Alloc( pool, size, false, __FILE__, __LINE__ ) #define Mem_Calloc( pool, size ) g_engfuncs._Mem_Alloc( pool, size, true, __FILE__, __LINE__ ) diff --git a/filesystem/wscript b/filesystem/wscript index bfcf35c0..b96d00d8 100644 --- a/filesystem/wscript +++ b/filesystem/wscript @@ -9,8 +9,8 @@ def configure(conf): def build(bld): bld.shlib(target = 'filesystem_stdio', - features = 'c', - source = bld.path.ant_glob(['*.c']), + features = 'cxx c', + source = bld.path.ant_glob(['*.c', '*.cpp']), includes = ['.', '../common', '../public', '../engine'], use = ['public'], install_path = bld.env.LIBDIR, From b26cd6cc94aaad82682a74dceb29d76b370d5fb3 Mon Sep 17 00:00:00 2001 From: SNMetamorph <25657591+SNMetamorph@users.noreply.github.com> Date: Mon, 1 Aug 2022 04:11:13 +0400 Subject: [PATCH 111/300] wscript: added /Zc:__cplusplus compiler flag for MSVC --- scripts/waifulib/compiler_optimizations.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scripts/waifulib/compiler_optimizations.py b/scripts/waifulib/compiler_optimizations.py index 77eb951a..3d653770 100644 --- a/scripts/waifulib/compiler_optimizations.py +++ b/scripts/waifulib/compiler_optimizations.py @@ -51,7 +51,7 @@ LINKFLAGS = { CFLAGS = { 'common': { # disable thread-safe local static initialization for C++11 code, as it cause crashes on Windows XP - 'msvc': ['/D_USING_V110_SDK71_', '/FS', '/Zc:threadSafeInit-', '/MT', '/MP'], + 'msvc': ['/D_USING_V110_SDK71_', '/FS', '/Zc:threadSafeInit-', '/MT', '/MP', '/Zc:__cplusplus'], 'clang': ['-g', '-gdwarf-2', '-fvisibility=hidden', '-fno-threadsafe-statics'], 'gcc': ['-g', '-fvisibility=hidden'], 'owcc': ['-fno-short-enum', '-ffloat-store', '-g3'] From a8674c18df242145f6fe9be5856a8442be2e65c3 Mon Sep 17 00:00:00 2001 From: SNMetamorph <25657591+SNMetamorph@users.noreply.github.com> Date: Mon, 1 Aug 2022 04:11:36 +0400 Subject: [PATCH 112/300] engine: platform: win32: fixed build for Windows --- engine/platform/win32/lib_win.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/engine/platform/win32/lib_win.c b/engine/platform/win32/lib_win.c index b6cf9cf8..21ee1fab 100644 --- a/engine/platform/win32/lib_win.c +++ b/engine/platform/win32/lib_win.c @@ -391,7 +391,7 @@ qboolean COM_CheckLibraryDirectDependency( const char *name, const char *depname dll_user_t *hInst; qboolean ret = FALSE; - hInst = COM_FindLibrary( name, directpath ); + hInst = FS_FindLibrary( name, directpath ); if ( !hInst ) return FALSE; data = FS_LoadFile( name, NULL, false ); @@ -439,7 +439,7 @@ void *COM_LoadLibrary( const char *dllname, int build_ordinals_table, qboolean d COM_ResetLibraryError(); - hInst = COM_FindLibrary( dllname, directpath ); + hInst = FS_FindLibrary( dllname, directpath ); if( !hInst ) { COM_PushLibraryError( va( "Failed to find library %s", dllname ) ); From fd7dba74cd54fd943850b720f9aba0d5cc82fd21 Mon Sep 17 00:00:00 2001 From: SNMetamorph <25657591+SNMetamorph@users.noreply.github.com> Date: Mon, 1 Aug 2022 04:12:35 +0400 Subject: [PATCH 113/300] filesystem: fixed build for Windows --- filesystem/VFileSystem009.cpp | 4 ++-- filesystem/filesystem.c | 10 ++++++++-- filesystem/pak.c | 3 +++ filesystem/wad.c | 2 ++ filesystem/zip.c | 2 ++ 5 files changed, 17 insertions(+), 4 deletions(-) diff --git a/filesystem/VFileSystem009.cpp b/filesystem/VFileSystem009.cpp index adac91bc..ad2a72c0 100644 --- a/filesystem/VFileSystem009.cpp +++ b/filesystem/VFileSystem009.cpp @@ -219,8 +219,8 @@ public: void FileTimeToString( char *p, int size, long int time ) override { - char *buf = ctime( &time ); - + time_t curtime = time; + char *buf = ctime( &curtime ); Q_strncpy( p, buf, size ); } diff --git a/filesystem/filesystem.c b/filesystem/filesystem.c index 55298c6c..ebe9c87e 100644 --- a/filesystem/filesystem.c +++ b/filesystem/filesystem.c @@ -1313,7 +1313,7 @@ static qboolean FS_FindLibrary( const char *dllname, qboolean directpath, fs_dll #if XASH_WIN32 && XASH_X86 // a1ba: custom loader is non-portable (I just don't want to touch it) Con_Printf( S_WARN "%s: loading libraries from packs is deprecated " "and will be removed in the future\n", __FUNCTION__ ); - *custom_loader = true; + dllInfo->custom_loader = true; #else Con_Printf( S_WARN "%s: loading libraries from packs is unsupported on " "this platform\n", __FUNCTION__ ); @@ -1390,8 +1390,9 @@ qboolean FS_InitStdio( qboolean caseinsensitive, const char *rootdir, const char int i; FS_InitMemory(); - +#if !XASH_WIN32 fs_caseinsensitive = caseinsensitive; +#endif Q_strncpy( fs_rootdir, rootdir, sizeof( fs_rootdir )); Q_strncpy( fs_gamedir, gamedir, sizeof( fs_gamedir )); @@ -1979,8 +1980,13 @@ int FS_Flush( file_t *file ) FS_Purge( file ); // sync +#if XASH_POSIX if( fsync( file->handle ) < 0 ) return EOF; +#else + if( fflush( file->handle ) < 0 ) + return EOF; +#endif return 0; } diff --git a/filesystem/pak.c b/filesystem/pak.c index 7dac5b8f..c0a8a911 100644 --- a/filesystem/pak.c +++ b/filesystem/pak.c @@ -14,10 +14,13 @@ 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 #include "port.h" diff --git a/filesystem/wad.c b/filesystem/wad.c index 9cbfa886..f741ff68 100644 --- a/filesystem/wad.c +++ b/filesystem/wad.c @@ -17,7 +17,9 @@ GNU General Public License for more details. #include #include #include +#if XASH_POSIX #include +#endif #include #include #include "port.h" diff --git a/filesystem/zip.c b/filesystem/zip.c index dcad291b..838928e0 100644 --- a/filesystem/zip.c +++ b/filesystem/zip.c @@ -17,7 +17,9 @@ GNU General Public License for more details. #include #include #include +#if XASH_POSIX #include +#endif #include #include #include STDINT_H From 3c4eec62aed833c79ed46e4f2e9c8ab116ec8d94 Mon Sep 17 00:00:00 2001 From: Alibek Omarov Date: Mon, 1 Aug 2022 13:25:51 +0300 Subject: [PATCH 114/300] engine: let engine find the filesystem library, if it wasn't loaded yet --- engine/common/lib_common.c | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/engine/common/lib_common.c b/engine/common/lib_common.c index 5b86df77..cdd3f8b6 100644 --- a/engine/common/lib_common.c +++ b/engine/common/lib_common.c @@ -91,9 +91,16 @@ dll_user_t *FS_FindLibrary( const char *dllname, qboolean directpath ) dll_user_t *p; fs_dllinfo_t dllInfo; - // no fs loaded, can't search + // no fs loaded yet, but let engine find fs if( !g_fsapi.FindLibrary ) - return NULL; + { + p = Mem_Calloc( host.mempool, sizeof( dll_user_t )); + Q_strncpy( p->shortPath, dllname, sizeof( p->shortPath )); + Q_strncpy( p->fullPath, dllname, sizeof( p->fullPath )); + Q_strncpy( p->dllName, dllname, sizeof( p->dllName )); + + return p; + } // fs can't find library if( !g_fsapi.FindLibrary( dllname, directpath, &dllInfo )) From b7b9c611cfc2e3ae38f807df20632d6d07c5dfbd Mon Sep 17 00:00:00 2001 From: Alibek Omarov Date: Mon, 1 Aug 2022 13:34:44 +0300 Subject: [PATCH 115/300] filesystem: wscript: disable RTTI and exceptions(useful on Android), we don't need it here. Link as C++ library. --- filesystem/wscript | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/filesystem/wscript b/filesystem/wscript index b96d00d8..730facd2 100644 --- a/filesystem/wscript +++ b/filesystem/wscript @@ -4,12 +4,17 @@ def options(opt): pass def configure(conf): + nortti = { + 'msvc': ['/GR-'], + 'default': ['-fno-rtti', '-fno-exceptions'] + } + conf.env.append_unique('CXXFLAGS', conf.get_flags_by_compiler(nortti, conf.env.COMPILER_CC)) if conf.env.cxxshlib_PATTERN.startswith('lib'): conf.env.cxxshlib_PATTERN = conf.env.cxxshlib_PATTERN[3:] def build(bld): bld.shlib(target = 'filesystem_stdio', - features = 'cxx c', + features = 'cxx', source = bld.path.ant_glob(['*.c', '*.cpp']), includes = ['.', '../common', '../public', '../engine'], use = ['public'], From 067e9be37afaead171608b6727e511f6c0f16e9f Mon Sep 17 00:00:00 2001 From: Alibek Omarov Date: Mon, 1 Aug 2022 14:25:44 +0300 Subject: [PATCH 116/300] scripts: upgrade to Android NDK 25 --- scripts/gha/deps_android.sh | 4 ++-- scripts/waifulib/xcompile.py | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/scripts/gha/deps_android.sh b/scripts/gha/deps_android.sh index 75e70979..59b5c549 100755 --- a/scripts/gha/deps_android.sh +++ b/scripts/gha/deps_android.sh @@ -15,9 +15,9 @@ popd echo "Download all needed tools and NDK" yes | sdk/tools/bin/sdkmanager --licenses > /dev/null 2>/dev/null # who even reads licenses? :) sdk/tools/bin/sdkmanager --install build-tools\;29.0.1 platform-tools platforms\;android-29 > /dev/null 2>/dev/null -wget https://dl.google.com/android/repository/android-ndk-r23b-linux.zip -qO ndk.zip > /dev/null || exit 1 +wget https://dl.google.com/android/repository/android-ndk-r25-linux.zip -qO ndk.zip > /dev/null || exit 1 unzip -q ndk.zip || exit 1 -mv android-ndk-r23b sdk/ndk-bundle || exit 1 +mv android-ndk-r25 sdk/ndk-bundle || exit 1 echo "Download Xash3D FWGS Android source" git clone --depth 1 https://github.com/FWGS/xash3d-android-project -b waf android || exit 1 diff --git a/scripts/waifulib/xcompile.py b/scripts/waifulib/xcompile.py index 0abf53f1..b9ce398f 100644 --- a/scripts/waifulib/xcompile.py +++ b/scripts/waifulib/xcompile.py @@ -20,12 +20,12 @@ import os import sys ANDROID_NDK_ENVVARS = ['ANDROID_NDK_HOME', 'ANDROID_NDK'] -ANDROID_NDK_SUPPORTED = [10, 19, 20, 23] +ANDROID_NDK_SUPPORTED = [10, 19, 20, 23, 25] ANDROID_NDK_HARDFP_MAX = 11 # latest version that supports hardfp ANDROID_NDK_GCC_MAX = 17 # latest NDK that ships with GCC ANDROID_NDK_UNIFIED_SYSROOT_MIN = 15 ANDROID_NDK_SYSROOT_FLAG_MAX = 19 # latest NDK that need --sysroot flag -ANDROID_NDK_API_MIN = { 10: 3, 19: 16, 20: 16, 23: 16 } # minimal API level ndk revision supports +ANDROID_NDK_API_MIN = { 10: 3, 19: 16, 20: 16, 23: 16, 25: 19 } # minimal API level ndk revision supports ANDROID_STPCPY_API_MIN = 21 # stpcpy() introduced in SDK 21 ANDROID_64BIT_API_MIN = 21 # minimal API level that supports 64-bit targets From c025606739553f4e37af3f13c8e177e1eca9646f Mon Sep 17 00:00:00 2001 From: Alibek Omarov Date: Mon, 1 Aug 2022 14:33:36 +0300 Subject: [PATCH 117/300] engine: whereami: fix C89 in Android detection --- engine/common/whereami.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/engine/common/whereami.c b/engine/common/whereami.c index 79b01dcc..4d3401a1 100644 --- a/engine/common/whereami.c +++ b/engine/common/whereami.c @@ -295,6 +295,7 @@ int WAI_PREFIX(getModulePath)(char* out, int capacity, int* dirname_length) &&buffer[length - 3] == 'a' &&buffer[length - 4] == '.') { + char *begin, *p; int fd = open(path, O_RDONLY); if (fd == -1) { @@ -302,7 +303,7 @@ int WAI_PREFIX(getModulePath)(char* out, int capacity, int* dirname_length) break; } - char* begin = (char*)mmap(0, offset, PROT_READ, MAP_SHARED, fd, 0); + begin = (char*)mmap(0, offset, PROT_READ, MAP_SHARED, fd, 0); if (begin == MAP_FAILED) { close(fd); @@ -310,7 +311,7 @@ int WAI_PREFIX(getModulePath)(char* out, int capacity, int* dirname_length) break; } - char* p = begin + offset - 30; // minimum size of local file header + p = begin + offset - 30; // minimum size of local file header while (p >= begin) // scan backwards { if (*((uint32_t*)p) == 0x04034b50UL) // local file header signature found From 3eb4862794a0bcecdfb041ec04377cdad0b6ddcf Mon Sep 17 00:00:00 2001 From: Alibek Omarov Date: Mon, 1 Aug 2022 15:06:54 +0300 Subject: [PATCH 118/300] common: remove unneeded typedef for wad file type --- common/xash3d_types.h | 1 - 1 file changed, 1 deletion(-) diff --git a/common/xash3d_types.h b/common/xash3d_types.h index 71472d43..33f82bb1 100644 --- a/common/xash3d_types.h +++ b/common/xash3d_types.h @@ -138,7 +138,6 @@ typedef unsigned int dword; typedef unsigned int uint; typedef char string[MAX_STRING]; typedef struct file_s file_t; // normal file -typedef struct wfile_s wfile_t; // wad file typedef struct stream_s stream_t; // sound stream for background music playing typedef off_t fs_offset_t; #if XASH_WIN32 From a5ba43ea39e980def67873eab6108e763b91cd30 Mon Sep 17 00:00:00 2001 From: SNMetamorph <25657591+SNMetamorph@users.noreply.github.com> Date: Tue, 2 Aug 2022 18:57:09 +0400 Subject: [PATCH 119/300] engine: fixed vulnerability in NAT bypass mechanism --- engine/server/sv_client.c | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/engine/server/sv_client.c b/engine/server/sv_client.c index 93a7ae4a..97fd122f 100644 --- a/engine/server/sv_client.c +++ b/engine/server/sv_client.c @@ -2268,10 +2268,14 @@ void SV_ConnectionlessPacket( netadr_t from, sizebuf_t *msg ) else if( !Q_strcmp( pcmd, "i" )) NET_SendPacket( NS_SERVER, 5, "\xFF\xFF\xFF\xFFj", from ); // A2A_PING else if (!Q_strcmp( pcmd, "c" )) { - netadr_t to; + qboolean sv_nat = Cvar_VariableInteger( "sv_nat" ); + if( sv_nat ) + { + netadr_t to; - if( NET_StringToAdr( Cmd_Argv( 1 ), &to )) - SV_Info( to ); + if( NET_StringToAdr( Cmd_Argv( 1 ), &to ) && !NET_IsReservedAdr( to )) + SV_Info( to ); + } } else if( svgame.dllFuncs.pfnConnectionlessPacket( &from, args, buf, &len )) { From 3d5aa7c20ca3e50bffd399c14a5a001544d608a6 Mon Sep 17 00:00:00 2001 From: Alibek Omarov Date: Fri, 5 Aug 2022 18:05:15 +0300 Subject: [PATCH 120/300] engine: remove mistakingly placed NORETURN attributes --- engine/client/client.h | 2 +- engine/common/common.h | 2 +- engine/common/system.h | 2 +- engine/menu_int.h | 7 +------ engine/ref_api.h | 2 +- 5 files changed, 5 insertions(+), 10 deletions(-) diff --git a/engine/client/client.h b/engine/client/client.h index cc6683df..fc4f9a9c 100644 --- a/engine/client/client.h +++ b/engine/client/client.h @@ -702,7 +702,7 @@ dlight_t *CL_GetEntityLight( int number ); // // cl_cmds.c // -void CL_Quit_f( void ) NORETURN; +void CL_Quit_f( void ); void CL_ScreenShot_f( void ); void CL_SnapShot_f( void ); void CL_PlayCDTrack_f( void ); diff --git a/engine/common/common.h b/engine/common/common.h index 58741bf3..b954dbd3 100644 --- a/engine/common/common.h +++ b/engine/common/common.h @@ -569,7 +569,7 @@ void Host_WriteConfig( void ); qboolean Host_IsLocalGame( void ); qboolean Host_IsLocalClient( void ); void Host_ShutdownServer( void ); -void Host_Error( const char *error, ... ) _format( 1 ) NORETURN; +void Host_Error( const char *error, ... ) _format( 1 ); void Host_PrintEngineFeatures( void ); void Host_Frame( float time ); void Host_InitDecals( void ); diff --git a/engine/common/system.h b/engine/common/system.h index 6a186602..c6d8956d 100644 --- a/engine/common/system.h +++ b/engine/common/system.h @@ -51,7 +51,7 @@ char *Sys_GetClipboardData( void ); const char *Sys_GetCurrentUser( void ); int Sys_CheckParm( const char *parm ); void Sys_Warn( const char *format, ... ) _format( 1 ); -void Sys_Error( const char *error, ... ) _format( 1 ) NORETURN; +void Sys_Error( const char *error, ... ) _format( 1 ); qboolean Sys_LoadLibrary( dll_info_t *dll ); void* Sys_GetProcAddress( dll_info_t *dll, const char* name ); qboolean Sys_FreeLibrary( dll_info_t *dll ); diff --git a/engine/menu_int.h b/engine/menu_int.h index 8a6905d6..d273d8f6 100644 --- a/engine/menu_int.h +++ b/engine/menu_int.h @@ -54,11 +54,6 @@ typedef struct ui_globalvars_s struct ref_viewpass_s; -#if __GNUC__ == 3 -#undef NORETURN -#define NORETURN -#endif // GCC 3.x have problems with noreturn attribute on function pointer - typedef struct ui_enginefuncs_s { // image handlers @@ -122,7 +117,7 @@ typedef struct ui_enginefuncs_s int (*CL_CreateVisibleEntity)( int type, struct cl_entity_s *ent ); // misc handlers - void (*pfnHostError)( const char *szFmt, ... ) _format( 1 ) NORETURN; + void (*pfnHostError)( const char *szFmt, ... ) _format( 1 ); int (*pfnFileExists)( const char *filename, int gamedironly ); void (*pfnGetGameDir)( char *szGetGameDir ); diff --git a/engine/ref_api.h b/engine/ref_api.h index 2f78b709..2b2e4ca1 100644 --- a/engine/ref_api.h +++ b/engine/ref_api.h @@ -339,7 +339,7 @@ typedef struct ref_api_s // utils void (*CL_ExtraUpdate)( void ); - void (*Host_Error)( const char *fmt, ... ) _format( 1 ) NORETURN; + void (*Host_Error)( const char *fmt, ... ) _format( 1 ); void (*COM_SetRandomSeed)( int lSeed ); float (*COM_RandomFloat)( float rmin, float rmax ); int (*COM_RandomLong)( int rmin, int rmax ); From bc00c6c54b4922066c21cfb2ed46d5c0ed35dcbd Mon Sep 17 00:00:00 2001 From: Alibek Omarov Date: Tue, 26 Jul 2022 04:51:32 +0300 Subject: [PATCH 121/300] vgui_support: update --- vgui_support | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/vgui_support b/vgui_support index 99108598..697de1ef 160000 --- a/vgui_support +++ b/vgui_support @@ -1 +1 @@ -Subproject commit 991085982209a1b8eefabae04d842004d4f4fe4f +Subproject commit 697de1efe2eea79d48118968d9e6a5a7e1117700 From d6dfeaeba7a8c2a436a8a9cad30335fef0f27145 Mon Sep 17 00:00:00 2001 From: Alibek Omarov Date: Sat, 6 Aug 2022 20:15:18 +0300 Subject: [PATCH 122/300] engine: fix searching filesystem_stdio on mobile platforms --- engine/common/common.h | 3 +-- engine/common/filesystem_engine.c | 9 ++++++++- engine/common/host.c | 2 +- 3 files changed, 10 insertions(+), 4 deletions(-) diff --git a/engine/common/common.h b/engine/common/common.h index b954dbd3..f70337e2 100644 --- a/engine/common/common.h +++ b/engine/common/common.h @@ -398,8 +398,7 @@ typedef void (*xcommand_t)( void ); // // filesystem_engine.c // -#define FILESYSTEM_STDIO_DLL "filesystem_stdio." OS_LIB_EXT -qboolean FS_LoadProgs( const char *name ); +qboolean FS_LoadProgs( void ); void FS_Init( void ); void FS_Shutdown( void ); diff --git a/engine/common/filesystem_engine.c b/engine/common/filesystem_engine.c index 42754134..6d3ff03e 100644 --- a/engine/common/filesystem_engine.c +++ b/engine/common/filesystem_engine.c @@ -56,8 +56,15 @@ static void FS_UnloadProgs( void ) fs_hInstance = 0; } -qboolean FS_LoadProgs( const char *name ) +#ifdef XASH_INTERNAL_GAMELIBS +#define FILESYSTEM_STDIO_DLL "filesystem_stdio" +#else +#define FILESYSTEM_STDIO_DLL "filesystem_stdio." OS_LIB_EXT +#endif + +qboolean FS_LoadProgs( void ) { + const char *name = FILESYSTEM_STDIO_DLL; FSAPI GetFSAPI; fs_hInstance = COM_LoadLibrary( name, false, true ); diff --git a/engine/common/host.c b/engine/common/host.c index 3f89d176..f259cdca 100644 --- a/engine/common/host.c +++ b/engine/common/host.c @@ -1033,7 +1033,7 @@ void Host_InitCommon( int argc, char **argv, const char *progname, qboolean bCha return; } - FS_LoadProgs( FILESYSTEM_STDIO_DLL ); + FS_LoadProgs(); if( FS_SetCurrentDirectory( host.rootdir ) != 0 ) Con_Reportf( "%s is working directory now\n", host.rootdir ); From 33cbead4a46ae54f5bd0b844df7a57c46689c958 Mon Sep 17 00:00:00 2001 From: Alibek Omarov Date: Sat, 6 Aug 2022 20:16:04 +0300 Subject: [PATCH 123/300] filesystem: wscript: fix library name on Android --- filesystem/wscript | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/filesystem/wscript b/filesystem/wscript index 730facd2..8a4e421a 100644 --- a/filesystem/wscript +++ b/filesystem/wscript @@ -9,8 +9,10 @@ def configure(conf): 'default': ['-fno-rtti', '-fno-exceptions'] } conf.env.append_unique('CXXFLAGS', conf.get_flags_by_compiler(nortti, conf.env.COMPILER_CC)) - if conf.env.cxxshlib_PATTERN.startswith('lib'): - conf.env.cxxshlib_PATTERN = conf.env.cxxshlib_PATTERN[3:] + + if conf.env.DEST_OS != 'android': + if conf.env.cxxshlib_PATTERN.startswith('lib'): + conf.env.cxxshlib_PATTERN = conf.env.cxxshlib_PATTERN[3:] def build(bld): bld.shlib(target = 'filesystem_stdio', From 90c566dde3a8ec023eb31f7c2b5c01d74377ed8c Mon Sep 17 00:00:00 2001 From: Alibek Omarov Date: Sat, 6 Aug 2022 20:16:27 +0300 Subject: [PATCH 124/300] wscript: enable GCC's -Wmisleading-indentation --- wscript | 1 + 1 file changed, 1 insertion(+) diff --git a/wscript b/wscript index 6565e53c..07e49635 100644 --- a/wscript +++ b/wscript @@ -209,6 +209,7 @@ def configure(conf): # '-Werror=format=2', # '-Wdouble-promotion', # disable warning flood '-Wstrict-aliasing', + '-Wmisleading-indentation', ] c_compiler_optional_flags = [ From 10ad1c3b380b53aeb7cab341c423ff0410c4a43f Mon Sep 17 00:00:00 2001 From: Alibek Omarov Date: Sat, 6 Aug 2022 20:19:42 +0300 Subject: [PATCH 125/300] filesystem: use correct flushing function for file descriptors on Win32 --- filesystem/filesystem.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/filesystem/filesystem.c b/filesystem/filesystem.c index ebe9c87e..66a9b5ae 100644 --- a/filesystem/filesystem.c +++ b/filesystem/filesystem.c @@ -1984,7 +1984,7 @@ int FS_Flush( file_t *file ) if( fsync( file->handle ) < 0 ) return EOF; #else - if( fflush( file->handle ) < 0 ) + if( _commit( file->handle ) < 0 ) return EOF; #endif From 0d449370e0fee49bcf9df6684ea320eb764d6680 Mon Sep 17 00:00:00 2001 From: Alibek Omarov Date: Sat, 6 Aug 2022 20:33:01 +0300 Subject: [PATCH 126/300] filesystem: fixes for GCC 3 --- filesystem/VFileSystem009.cpp | 6 +++--- filesystem/filesystem_internal.h | 1 - filesystem/pak.c | 4 ++-- filesystem/wad.c | 4 ++-- filesystem/zip.c | 4 ++-- 5 files changed, 9 insertions(+), 10 deletions(-) diff --git a/filesystem/VFileSystem009.cpp b/filesystem/VFileSystem009.cpp index ad2a72c0..ffcffe40 100644 --- a/filesystem/VFileSystem009.cpp +++ b/filesystem/VFileSystem009.cpp @@ -476,10 +476,10 @@ public: int ResumeResourcePreloading() override { return 0; } bool IsAppReadyForOfflinePlay(int) override { return true; } bool IsFileImmediatelyAvailable(const char *) override { return true; } - bool GetWaitForResourcesProgress(WaitForResourcesHandle_t, float *progress, bool *override) override + bool GetWaitForResourcesProgress(WaitForResourcesHandle_t, float *pProgress, bool *pOverride) override { - if( progress ) *progress = 0; - if( override ) *override = true; + if( pProgress ) *pProgress = 0; + if( pOverride ) *pOverride = true; return false; } } g_VFileSystem009; diff --git a/filesystem/filesystem_internal.h b/filesystem/filesystem_internal.h index b70cb77f..3cf5a462 100644 --- a/filesystem/filesystem_internal.h +++ b/filesystem/filesystem_internal.h @@ -28,7 +28,6 @@ typedef struct zip_s zip_t; typedef struct pack_s pack_t; typedef struct wfile_s wfile_t; - #define FILE_BUFF_SIZE (2048) struct file_s diff --git a/filesystem/pak.c b/filesystem/pak.c index c0a8a911..1cc0037a 100644 --- a/filesystem/pak.c +++ b/filesystem/pak.c @@ -63,14 +63,14 @@ typedef struct #define PAK_LOAD_NO_FILES 5 #define PAK_LOAD_CORRUPTED 6 -typedef struct pack_s +struct pack_s { string filename; int handle; int numfiles; time_t filetime; // common for all packed files dpackfile_t *files; -} pack_t; +}; /* ==================== diff --git a/filesystem/wad.c b/filesystem/wad.c index f741ff68..646476d3 100644 --- a/filesystem/wad.c +++ b/filesystem/wad.c @@ -69,7 +69,7 @@ typedef struct char name[WAD3_NAMELEN]; // must be null terminated } dlumpinfo_t; -typedef struct wfile_s +struct wfile_s { string filename; int infotableofs; @@ -78,7 +78,7 @@ typedef struct wfile_s file_t *handle; dlumpinfo_t *lumps; time_t filetime; -} wfile_t; +}; // WAD errors #define WAD_LOAD_OK 0 diff --git a/filesystem/zip.c b/filesystem/zip.c index 838928e0..829d1553 100644 --- a/filesystem/zip.c +++ b/filesystem/zip.c @@ -118,14 +118,14 @@ typedef struct zipfile_s unsigned short flags; } zipfile_t; -typedef struct zip_s +struct zip_s { string filename; int handle; int numfiles; time_t filetime; zipfile_t *files; -} zip_t; +}; #ifdef XASH_REDUCE_FD static void FS_EnsureOpenZip( zip_t *zip ) From 28d7f2eaa2f80856c876a40c3a06a49811cfafc1 Mon Sep 17 00:00:00 2001 From: Andrey Akhmichin <15944199+nekonomicon@users.noreply.github.com> Date: Sun, 7 Aug 2022 23:44:11 +0500 Subject: [PATCH 127/300] Documentation: not-supported-mod-list-and-reasons-why.md: update. --- Documentation/not-supported-mod-list-and-reasons-why.md | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/Documentation/not-supported-mod-list-and-reasons-why.md b/Documentation/not-supported-mod-list-and-reasons-why.md index 89f834cc..f2ec68a2 100644 --- a/Documentation/not-supported-mod-list-and-reasons-why.md +++ b/Documentation/not-supported-mod-list-and-reasons-why.md @@ -7,6 +7,5 @@ |Counter Strike: Condition Zero |The latest steam release |Uses vgui2 library which xash3d does not support |Some work on vgui2 support was made here: https://github.com/FWGS/xash3d/tree/vinterface |Counter Strike: Condition Zero - Deleted scenes |The latest steam release |Uses vgui2 library which xash3d does not support |Some work on vgui2 support was made here: https://github.com/FWGS/xash3d/tree/vinterface |Day of Defeat |The latest steam release |Uses vgui2 library which xash3d does not support |Some work on vgui2 support was made here: https://github.com/FWGS/xash3d/tree/vinterface -|Sven-Coop |5.0+ |Uses filesystem_stdio library which xash3d does not support |filesystem_stdio replacement already was made: https://github.com/FWGS/filesystem_stdio_xash -|XDM |3.0.4.0 | | +|Sven-Coop |5.0+ |Uses custom GoldSrc engine | From 7157c3b441321cb841316c4b408e0b9462d5f51e Mon Sep 17 00:00:00 2001 From: Alibek Omarov Date: Mon, 8 Aug 2022 23:53:17 +0300 Subject: [PATCH 128/300] 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 129/300] 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 130/300] 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 131/300] 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 132/300] 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 133/300] 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 134/300] 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 135/300] 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 136/300] 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 137/300] 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 138/300] 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 139/300] 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 140/300] 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 141/300] 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 142/300] 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 143/300] 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 144/300] 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 145/300] 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 146/300] 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 147/300] 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 148/300] 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 149/300] 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 150/300] 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 151/300] 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 152/300] 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 153/300] 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 154/300] 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 155/300] 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 156/300] 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 157/300] 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 158/300] 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 159/300] 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 160/300] 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 161/300] 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 162/300] 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 163/300] 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 164/300] 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 165/300] 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 166/300] 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 167/300] 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 168/300] 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 169/300] 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 170/300] 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 171/300] 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 172/300] 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 173/300] 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 174/300] 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 175/300] 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 176/300] 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 177/300] 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 178/300] 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 179/300] 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 180/300] 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 181/300] 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 182/300] 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 183/300] 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 184/300] 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 185/300] 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 186/300] 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 187/300] 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 188/300] 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 189/300] 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 190/300] 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 191/300] 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 192/300] 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 193/300] 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 194/300] 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 195/300] 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 196/300] 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 197/300] 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 198/300] 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 199/300] 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 200/300] 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 201/300] 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 202/300] 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 203/300] 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 204/300] 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 205/300] 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 206/300] 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 207/300] 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 208/300] 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 209/300] 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 210/300] 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 211/300] 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 212/300] 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 213/300] 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 214/300] 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 215/300] 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 216/300] 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 217/300] 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 218/300] 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 219/300] 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 220/300] 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 221/300] 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 222/300] 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 223/300] 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 224/300] 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 225/300] 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 226/300] 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 227/300] 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 228/300] 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 229/300] 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 230/300] 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 231/300] 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 232/300] 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 233/300] 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 234/300] 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 235/300] 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 236/300] 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 237/300] 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 238/300] 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 239/300] 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 240/300] 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 241/300] 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 242/300] 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 243/300] 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 244/300] 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 245/300] 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 246/300] 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 247/300] 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 248/300] 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 249/300] 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 250/300] 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 251/300] 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 252/300] 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 253/300] 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 254/300] 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 255/300] 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 256/300] 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 257/300] 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 258/300] 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 259/300] 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 260/300] 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 261/300] 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 262/300] 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 263/300] 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 264/300] 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 265/300] 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 266/300] 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 267/300] .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 268/300] 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 269/300] 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 270/300] 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 271/300] 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 272/300] 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 273/300] 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 274/300] 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 275/300] 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 276/300] 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 277/300] 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 278/300] 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 279/300] 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 280/300] 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 281/300] 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 282/300] 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 283/300] 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 284/300] 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 285/300] 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 286/300] 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 287/300] 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 288/300] 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 289/300] 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 290/300] 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 291/300] 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 292/300] 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 293/300] 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 294/300] 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 295/300] 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 296/300] 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 297/300] 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 298/300] 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 299/300] 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 300/300] 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