From 8c6d43e6be5693145ee971b5b1be3c2e587a633f Mon Sep 17 00:00:00 2001 From: Sebastian Buchwald Date: Tue, 26 Jul 2016 14:39:18 +0200 Subject: [PATCH 001/240] Remove dead variables --- code/qcommon/cm_patch.c | 5 ++--- code/qcommon/common.c | 7 +------ 2 files changed, 3 insertions(+), 9 deletions(-) diff --git a/code/qcommon/cm_patch.c b/code/qcommon/cm_patch.c index e4b90002e8..c1465b50c3 100644 --- a/code/qcommon/cm_patch.c +++ b/code/qcommon/cm_patch.c @@ -826,7 +826,7 @@ CM_AddFacetBevels void CM_AddFacetBevels( facet_t *facet ) { int i, j, k, l; - int axis, dir, order, flipped; + int axis, dir, flipped; float plane[4], d, newplane[4]; winding_t *w, *w2; vec3_t mins, maxs, vec, vec2; @@ -852,10 +852,9 @@ void CM_AddFacetBevels( facet_t *facet ) { WindingBounds(w, mins, maxs); // add the axial planes - order = 0; for ( axis = 0 ; axis < 3 ; axis++ ) { - for ( dir = -1 ; dir <= 1 ; dir += 2, order++ ) + for ( dir = -1 ; dir <= 1 ; dir += 2 ) { VectorClear(plane); plane[axis] = dir; diff --git a/code/qcommon/common.c b/code/qcommon/common.c index 84b5f62864..a932193fea 100644 --- a/code/qcommon/common.c +++ b/code/qcommon/common.c @@ -899,7 +899,6 @@ Z_FreeTags ================ */ void Z_FreeTags( int tag ) { - int count; memzone_t *zone; if ( tag == TAG_SMALL ) { @@ -908,13 +907,11 @@ void Z_FreeTags( int tag ) { else { zone = mainzone; } - count = 0; // use the rover as our pointer, because // Z_Free automatically adjusts it zone->rover = zone->blocklist.next; do { if ( zone->rover->tag == tag ) { - count++; Z_Free( (void *)(zone->rover + 1) ); continue; } @@ -1268,7 +1265,7 @@ Com_Meminfo_f void Com_Meminfo_f( void ) { memblock_t *block; int zoneBytes, zoneBlocks; - int smallZoneBytes, smallZoneBlocks; + int smallZoneBytes; int botlibBytes, rendererBytes; int unused; @@ -1306,11 +1303,9 @@ void Com_Meminfo_f( void ) { } smallZoneBytes = 0; - smallZoneBlocks = 0; for (block = smallzone->blocklist.next ; ; block = block->next) { if ( block->tag ) { smallZoneBytes += block->size; - smallZoneBlocks++; } if (block->next == &smallzone->blocklist) { From 5eae6de2f4c587023e48f4cb00d75eee8cfdb4f5 Mon Sep 17 00:00:00 2001 From: Sebastian Buchwald Date: Tue, 26 Jul 2016 14:41:03 +0200 Subject: [PATCH 002/240] Fix some typos in comments --- code/game/g_combat.c | 2 +- code/qcommon/cm_patch.c | 4 ++-- code/qcommon/files.c | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/code/game/g_combat.c b/code/game/g_combat.c index b981c58103..7e1f1f4ef3 100644 --- a/code/game/g_combat.c +++ b/code/game/g_combat.c @@ -815,7 +815,7 @@ void G_Damage( gentity_t *targ, gentity_t *inflictor, gentity_t *attacker, return; } - // the intermission has allready been qualified for, so don't + // the intermission has already been qualified for, so don't // allow any extra scoring if ( level.intermissionQueued ) { return; diff --git a/code/qcommon/cm_patch.c b/code/qcommon/cm_patch.c index e4b90002e8..1a33308e47 100644 --- a/code/qcommon/cm_patch.c +++ b/code/qcommon/cm_patch.c @@ -869,7 +869,7 @@ void CM_AddFacetBevels( facet_t *facet ) { if (CM_PlaneEqual(&planes[facet->surfacePlane], plane, &flipped)) { continue; } - // see if the plane is allready present + // see if the plane is already present for ( i = 0 ; i < facet->numBorders ; i++ ) { if (CM_PlaneEqual(&planes[facet->borderPlanes[i]], plane, &flipped)) break; @@ -933,7 +933,7 @@ void CM_AddFacetBevels( facet_t *facet ) { if (CM_PlaneEqual(&planes[facet->surfacePlane], plane, &flipped)) { continue; } - // see if the plane is allready present + // see if the plane is already present for ( i = 0 ; i < facet->numBorders ; i++ ) { if (CM_PlaneEqual(&planes[facet->borderPlanes[i]], plane, &flipped)) { break; diff --git a/code/qcommon/files.c b/code/qcommon/files.c index 3b4a8aeb4b..4df92f6336 100644 --- a/code/qcommon/files.c +++ b/code/qcommon/files.c @@ -2208,7 +2208,7 @@ static int FS_AddFileToList( char *name, char *list[MAX_FOUND_FILES], int nfiles } for ( i = 0 ; i < nfiles ; i++ ) { if ( !Q_stricmp( name, list[i] ) ) { - return nfiles; // allready in list + return nfiles; // already in list } } list[nfiles] = CopyString( name ); From 0c3879f1926bfa1a2745b5fee0e0ab771f362b3a Mon Sep 17 00:00:00 2001 From: Wolf Date: Wed, 1 Feb 2017 12:33:55 -0600 Subject: [PATCH 003/240] Redundant #ifndef MISSIONPACK block MISSIONPACK define is already required for this file or else it triggers an #error at the top of the file - removing redundant test that will never occur. --- code/cgame/cg_newdraw.c | 9 --------- 1 file changed, 9 deletions(-) diff --git a/code/cgame/cg_newdraw.c b/code/cgame/cg_newdraw.c index 85c8040b55..a2749b2a37 100644 --- a/code/cgame/cg_newdraw.c +++ b/code/cgame/cg_newdraw.c @@ -199,15 +199,6 @@ static void CG_DrawPlayerArmorValue(rectDef_t *rect, float scale, vec4_t color, } } -#ifndef MISSIONPACK -static float healthColors[4][4] = { -// { 0.2, 1.0, 0.2, 1.0 } , { 1.0, 0.2, 0.2, 1.0 }, {0.5, 0.5, 0.5, 1} }; - { 1.0f, 0.69f, 0.0f, 1.0f } , // normal - { 1.0f, 0.2f, 0.2f, 1.0f }, // low health - { 0.5f, 0.5f, 0.5f, 1.0f}, // weapon firing - { 1.0f, 1.0f, 1.0f, 1.0f } }; // health > 100 -#endif - static void CG_DrawPlayerAmmoIcon( rectDef_t *rect, qboolean draw2D ) { centity_t *cent; vec3_t angles; From 411d20bec6dad96b21221713920eba32c94c0a53 Mon Sep 17 00:00:00 2001 From: Wolf Date: Thu, 2 Feb 2017 10:28:46 -0600 Subject: [PATCH 004/240] ...too much MISSIONPACK ifdeffery. --- code/cgame/cg_main.c | 4 ---- 1 file changed, 4 deletions(-) diff --git a/code/cgame/cg_main.c b/code/cgame/cg_main.c index 3b1f77b740..d5ecd81f08 100644 --- a/code/cgame/cg_main.c +++ b/code/cgame/cg_main.c @@ -1682,18 +1682,14 @@ static void CG_FeederSelection(float feederID, int index) { cg.selectedScore = index; } } -#endif -#ifdef MISSIONPACK static float CG_Cvar_Get(const char *cvar) { char buff[128]; memset(buff, 0, sizeof(buff)); trap_Cvar_VariableStringBuffer(cvar, buff, sizeof(buff)); return atof(buff); } -#endif -#ifdef MISSIONPACK void CG_Text_PaintWithCursor(float x, float y, float scale, vec4_t color, const char *text, int cursorPos, char cursor, int limit, int style) { CG_Text_Paint(x, y, scale, color, text, 0, limit, style); } From 40ec42a42520c293c147d38f00f350f1ce44bb20 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Micka=C3=ABl=20Thomas?= Date: Sun, 12 Mar 2017 22:55:49 +0100 Subject: [PATCH 005/240] Don't include (mission)pak_checksums in standalone build Avoids a compiler warning --- code/qcommon/files.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/code/qcommon/files.c b/code/qcommon/files.c index 92a9e40577..3d9bff802a 100644 --- a/code/qcommon/files.c +++ b/code/qcommon/files.c @@ -174,6 +174,7 @@ or configs will never get loaded from disk! // every time a new demo pk3 file is built, this checksum must be updated. // the easiest way to get it is to just run the game and see what it spits out +#ifndef STANDALONE #define DEMO_PAK0_CHECKSUM 2985612116u static const unsigned int pak_checksums[] = { 1566731103u, @@ -194,6 +195,7 @@ static const unsigned int missionpak_checksums[] = 2662638993u, 1438664554u }; +#endif // if this is defined, the executable positively won't work with any paks other // than the demo pak, even if productid is present. This is only used for our From eecc8326a0b9af15976b0910d2e9bcdc925ceec6 Mon Sep 17 00:00:00 2001 From: Zack Middleton Date: Wed, 24 May 2017 12:30:25 -0500 Subject: [PATCH 006/240] Save connect and playdemo argument before calling CL_Disconnect() Save argument instead of using a pointer to cmd token memory that might be overwritten when Cmd_TokenizeString() is called. No known method for causing the issue without engine changes. Cmd_TokenizeString() is called by FS_PureServerSetReferencedPaks() in CL_Disconnect() but it's not an issue because the string is blank. Thanks @mickael9. --- code/client/cl_main.c | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/code/client/cl_main.c b/code/client/cl_main.c index 31dd9ab782..e9a406e6d2 100644 --- a/code/client/cl_main.c +++ b/code/client/cl_main.c @@ -1069,7 +1069,8 @@ demo */ void CL_PlayDemo_f( void ) { char name[MAX_OSPATH]; - char *arg, *ext_test; + char arg[MAX_OSPATH]; + char *ext_test; int protocol, i; char retry[MAX_OSPATH]; @@ -1083,7 +1084,7 @@ void CL_PlayDemo_f( void ) { Cvar_Set( "sv_killserver", "2" ); // open the demo file - arg = Cmd_Argv(1); + Q_strncpyz( arg, Cmd_Argv(1), sizeof( arg ) ); CL_Disconnect( qtrue ); @@ -1690,7 +1691,7 @@ CL_Connect_f ================ */ void CL_Connect_f( void ) { - char *server; + char server[MAX_OSPATH]; const char *serverString; int argc = Cmd_Argc(); netadrtype_t family = NA_UNSPEC; @@ -1701,7 +1702,7 @@ void CL_Connect_f( void ) { } if(argc == 2) - server = Cmd_Argv(1); + Q_strncpyz( server, Cmd_Argv(1), sizeof( server ) ); else { if(!strcmp(Cmd_Argv(1), "-4")) @@ -1711,7 +1712,7 @@ void CL_Connect_f( void ) { else Com_Printf( "warning: only -4 or -6 as address type understood.\n"); - server = Cmd_Argv(2); + Q_strncpyz( server, Cmd_Argv(2), sizeof( server ) ); } // save arguments for reconnect From abce15055cbbd0ee4a01277839a4830b5f8488ee Mon Sep 17 00:00:00 2001 From: Zack Middleton Date: Wed, 24 May 2017 14:09:48 -0500 Subject: [PATCH 007/240] Fix compiling Mac OS 10.5 Universal App Bundle Need to be able to specify minimum Mac OS X version outside of the Makefile to avoid conflicting CFLAGS. Moved -mmacosx-version-min LDFLAGS into the Makefile. Moved -arch x86_64 from OPTIMIZEVM to CFLAGS to fix linker errors (previously make-macosx-ub.sh passed it to CFLAGS manually). --- Makefile | 15 +++++++++++++-- make-macosx-ub.sh | 27 ++++++++++++--------------- 2 files changed, 25 insertions(+), 17 deletions(-) diff --git a/Makefile b/Makefile index 89aa5b2b90..f0d6251e01 100644 --- a/Makefile +++ b/Makefile @@ -409,7 +409,17 @@ ifeq ($(PLATFORM),darwin) RENDERER_LIBS= OPTIMIZEVM= - BASE_CFLAGS += -mmacosx-version-min=10.7 -DMAC_OS_X_VERSION_MIN_REQUIRED=1070 + # Default minimum Mac OS X version + ifeq ($(MACOSX_VERSION_MIN),) + MACOSX_VERSION_MIN=10.7 + endif + + # Multiply by 100 and then remove decimal. 10.7 -> 1070.0 -> 1070 + MAC_OS_X_VERSION_MIN_REQUIRED=$(shell echo '$(MACOSX_VERSION_MIN) * 100' | bc | cut -d. -f1) + + LDFLAGS += -mmacosx-version-min=$(MACOSX_VERSION_MIN) + BASE_CFLAGS += -mmacosx-version-min=$(MACOSX_VERSION_MIN) \ + -DMAC_OS_X_VERSION_MIN_REQUIRED=$(MAC_OS_X_VERSION_MIN_REQUIRED) ifeq ($(ARCH),ppc) BASE_CFLAGS += -arch ppc -faltivec @@ -425,7 +435,8 @@ ifeq ($(PLATFORM),darwin) BASE_CFLAGS += -arch i386 -m32 -mstackrealign endif ifeq ($(ARCH),x86_64) - OPTIMIZEVM += -arch x86_64 -mfpmath=sse + OPTIMIZEVM += -mfpmath=sse + BASE_CFLAGS += -arch x86_64 endif # When compiling on OSX for OSX, we're not cross compiling as far as the diff --git a/make-macosx-ub.sh b/make-macosx-ub.sh index bd7c9b911b..6702ea941a 100755 --- a/make-macosx-ub.sh +++ b/make-macosx-ub.sh @@ -15,29 +15,26 @@ fi unset X86_64_SDK unset X86_64_CFLAGS -unset X86_64_LDFLAGS +unset X86_64_MACOSX_VERSION_MIN unset X86_SDK unset X86_CFLAGS -unset X86_LDFLAGS +unset X86_MACOSX_VERSION_MIN unset PPC_64_SDK unset PPC_CFLAGS -unset PPC_LDFLAGS +unset PPC_MACOSX_VERSION_MIN if [ -d /Developer/SDKs/MacOSX10.5.sdk ]; then X86_64_SDK=/Developer/SDKs/MacOSX10.5.sdk - X86_64_CFLAGS="-arch x86_64 -isysroot /Developer/SDKs/MacOSX10.5.sdk \ - -DMAC_OS_X_VERSION_MIN_REQUIRED=1050" - X86_64_LDFLAGS=" -mmacosx-version-min=10.5" + X86_64_CFLAGS="-isysroot /Developer/SDKs/MacOSX10.5.sdk" + X86_64_MACOSX_VERSION_MIN="10.5" X86_SDK=/Developer/SDKs/MacOSX10.5.sdk - X86_CFLAGS="-arch i386 -isysroot /Developer/SDKs/MacOSX10.5.sdk \ - -DMAC_OS_X_VERSION_MIN_REQUIRED=1050" - X86_LDFLAGS=" -mmacosx-version-min=10.5" + X86_CFLAGS="-isysroot /Developer/SDKs/MacOSX10.5.sdk" + X86_MACOSX_VERSION_MIN="10.5" PPC_SDK=/Developer/SDKs/MacOSX10.5.sdk - PPC_CFLAGS="-arch ppc -isysroot /Developer/SDKs/MacOSX10.5.sdk \ - -DMAC_OS_X_VERSION_MIN_REQUIRED=1050" - PPC_LDFLAGS=" -mmacosx-version-min=10.5" + PPC_CFLAGS="-isysroot /Developer/SDKs/MacOSX10.5.sdk" + PPC_MACOSX_VERSION_MIN="10.5" fi if [ -z $X86_64_SDK ] || [ -z $X86_SDK ] || [ -z $PPC_SDK ]; then @@ -70,7 +67,7 @@ NCPU=`sysctl -n hw.ncpu` #if [ -d build/release-release-x86_64 ]; then # rm -r build/release-darwin-x86_64 #fi -(ARCH=x86_64 CC=gcc-4.0 CFLAGS=$X86_64_CFLAGS LDFLAGS=$X86_64_LDFLAGS make -j$NCPU) || exit 1; +(ARCH=x86_64 CC=gcc-4.0 CFLAGS=$X86_64_CFLAGS MACOSX_VERSION_MIN=$X86_64_MACOSX_VERSION_MIN make -j$NCPU) || exit 1; echo;echo @@ -78,7 +75,7 @@ echo;echo #if [ -d build/release-darwin-x86 ]; then # rm -r build/release-darwin-x86 #fi -(ARCH=x86 CC=gcc-4.0 CFLAGS=$X86_CFLAGS LDFLAGS=$X86_LDFLAGS make -j$NCPU) || exit 1; +(ARCH=x86 CC=gcc-4.0 CFLAGS=$X86_CFLAGS MACOSX_VERSION_MIN=$X86_MACOSX_VERSION_MIN make -j$NCPU) || exit 1; echo;echo @@ -86,7 +83,7 @@ echo;echo #if [ -d build/release-darwin-ppc ]; then # rm -r build/release-darwin-ppc #fi -(ARCH=ppc CC=gcc-4.0 CFLAGS=$PPC_CFLAGS LDFLAGS=$PPC_LDFLAGS make -j$NCPU) || exit 1; +(ARCH=ppc CC=gcc-4.0 CFLAGS=$PPC_CFLAGS MACOSX_VERSION_MIN=$PPC_MACOSX_VERSION_MIN make -j$NCPU) || exit 1; echo From 566fb0edfc6d88efcd161c912c7db025b8c173ce Mon Sep 17 00:00:00 2001 From: ec- Date: Wed, 15 Mar 2017 11:42:58 +0200 Subject: [PATCH 008/240] Allow unaligned load/store in QVM interpreter/x86 compiler constructions like (dataMask & ~3) was used to protect against out-of-bound load/store when address is 4-byte closer to dataMask but at the same time it effectively cut low address bits for ALL load/store operations which is totally wrong in terms of conformance to ALLOWED (i.e. generated by q3lcc from C sources) low-level operations like packed binary data parsing --- code/qcommon/vm.c | 8 +++++--- code/qcommon/vm_interpreted.c | 14 +++++++------- code/qcommon/vm_local.h | 1 + code/qcommon/vm_x86.c | 8 ++++---- 4 files changed, 17 insertions(+), 14 deletions(-) diff --git a/code/qcommon/vm.c b/code/qcommon/vm.c index e8818a6c9b..dcf5117e76 100644 --- a/code/qcommon/vm.c +++ b/code/qcommon/vm.c @@ -451,13 +451,15 @@ vmHeader_t *VM_LoadQVM( vm_t *vm, qboolean alloc, qboolean unpure) if(alloc) { // allocate zero filled space for initialized and uninitialized data - vm->dataBase = Hunk_Alloc(dataLength, h_high); + // leave some space beyound data mask so we can secure all mask operations + vm->dataAlloc = dataLength + 4; + vm->dataBase = Hunk_Alloc(vm->dataAlloc, h_high); vm->dataMask = dataLength - 1; } else { // clear the data, but make sure we're not clearing more than allocated - if(vm->dataMask + 1 != dataLength) + if(vm->dataAlloc != dataLength + 4) { VM_Free(vm); FS_FreeFile(header.v); @@ -467,7 +469,7 @@ vmHeader_t *VM_LoadQVM( vm_t *vm, qboolean alloc, qboolean unpure) return NULL; } - Com_Memset(vm->dataBase, 0, dataLength); + Com_Memset(vm->dataBase, 0, vm->dataAlloc); } // copy the intialized data diff --git a/code/qcommon/vm_interpreted.c b/code/qcommon/vm_interpreted.c index f630d0530a..cb86a08d8f 100644 --- a/code/qcommon/vm_interpreted.c +++ b/code/qcommon/vm_interpreted.c @@ -436,31 +436,31 @@ int VM_CallInterpreted( vm_t *vm, int *args ) { return 0; } #endif - r0 = opStack[opStackOfs] = *(int *) &image[r0 & dataMask & ~3 ]; + r0 = opStack[opStackOfs] = *(int *) &image[ r0 & dataMask ]; goto nextInstruction2; case OP_LOAD2: - r0 = opStack[opStackOfs] = *(unsigned short *)&image[ r0&dataMask&~1 ]; + r0 = opStack[opStackOfs] = *(unsigned short *)&image[ r0 & dataMask ]; goto nextInstruction2; case OP_LOAD1: - r0 = opStack[opStackOfs] = image[ r0&dataMask ]; + r0 = opStack[opStackOfs] = image[ r0 & dataMask ]; goto nextInstruction2; case OP_STORE4: - *(int *)&image[ r1&(dataMask & ~3) ] = r0; + *(int *)&image[ r1 & dataMask ] = r0; opStackOfs -= 2; goto nextInstruction; case OP_STORE2: - *(short *)&image[ r1&(dataMask & ~1) ] = r0; + *(short *)&image[ r1 & dataMask ] = r0; opStackOfs -= 2; goto nextInstruction; case OP_STORE1: - image[ r1&dataMask ] = r0; + image[ r1 & dataMask ] = r0; opStackOfs -= 2; goto nextInstruction; case OP_ARG: // single byte offset from programStack - *(int *)&image[ (codeImage[programCounter] + programStack)&dataMask&~3 ] = r0; + *(int *)&image[ (codeImage[programCounter] + programStack) & dataMask ] = r0; opStackOfs--; programCounter += 1; goto nextInstruction; diff --git a/code/qcommon/vm_local.h b/code/qcommon/vm_local.h index 76b1a4b1c3..07e896755e 100644 --- a/code/qcommon/vm_local.h +++ b/code/qcommon/vm_local.h @@ -170,6 +170,7 @@ struct vm_s { byte *dataBase; int dataMask; + int dataAlloc; // actually allocated int stackBottom; // if programStack < stackBottom, error diff --git a/code/qcommon/vm_x86.c b/code/qcommon/vm_x86.c index 9ee26799df..2435a6e909 100644 --- a/code/qcommon/vm_x86.c +++ b/code/qcommon/vm_x86.c @@ -790,7 +790,7 @@ qboolean ConstOptimize(vm_t *vm, int callProcOfsSyscall) return qtrue; case OP_STORE4: - EmitMovEAXStack(vm, (vm->dataMask & ~3)); + EmitMovEAXStack(vm, vm->dataMask); #if idx64 EmitRexString(0x41, "C7 04 01"); // mov dword ptr [r9 + eax], 0x12345678 Emit4(Constant4()); @@ -805,7 +805,7 @@ qboolean ConstOptimize(vm_t *vm, int callProcOfsSyscall) return qtrue; case OP_STORE2: - EmitMovEAXStack(vm, (vm->dataMask & ~1)); + EmitMovEAXStack(vm, vm->dataMask); #if idx64 Emit1(0x66); // mov word ptr [r9 + eax], 0x1234 EmitRexString(0x41, "C7 04 01"); @@ -1377,7 +1377,7 @@ void VM_Compile(vm_t *vm, vmHeader_t *header) case OP_STORE4: EmitMovEAXStack(vm, 0); EmitString("8B 54 9F FC"); // mov edx, dword ptr -4[edi + ebx * 4] - MASK_REG("E2", vm->dataMask & ~3); // and edx, 0x12345678 + MASK_REG("E2", vm->dataMask); // and edx, 0x12345678 #if idx64 EmitRexString(0x41, "89 04 11"); // mov dword ptr [r9 + edx], eax #else @@ -1389,7 +1389,7 @@ void VM_Compile(vm_t *vm, vmHeader_t *header) case OP_STORE2: EmitMovEAXStack(vm, 0); EmitString("8B 54 9F FC"); // mov edx, dword ptr -4[edi + ebx * 4] - MASK_REG("E2", vm->dataMask & ~1); // and edx, 0x12345678 + MASK_REG("E2", vm->dataMask); // and edx, 0x12345678 #if idx64 Emit1(0x66); // mov word ptr [r9 + edx], eax EmitRexString(0x41, "89 04 11"); From c259e7cba3e31c3d400e7e7135280a4d6feae477 Mon Sep 17 00:00:00 2001 From: Eugene C Date: Wed, 15 Mar 2017 14:00:13 +0200 Subject: [PATCH 009/240] Fix comment --- code/qcommon/vm.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/code/qcommon/vm.c b/code/qcommon/vm.c index dcf5117e76..03bdb966c8 100644 --- a/code/qcommon/vm.c +++ b/code/qcommon/vm.c @@ -451,7 +451,7 @@ vmHeader_t *VM_LoadQVM( vm_t *vm, qboolean alloc, qboolean unpure) if(alloc) { // allocate zero filled space for initialized and uninitialized data - // leave some space beyound data mask so we can secure all mask operations + // leave some space beyond data mask so we can secure all mask operations vm->dataAlloc = dataLength + 4; vm->dataBase = Hunk_Alloc(vm->dataAlloc, h_high); vm->dataMask = dataLength - 1; From 4729c683fd0cca6223955da36eff8b7285e6ad31 Mon Sep 17 00:00:00 2001 From: "Ryan C. Gordon" Date: Thu, 25 May 2017 14:13:18 -0400 Subject: [PATCH 010/240] Initial shot at writing an ioquake3 autoupdater. --- Makefile | 85 +++- autoupdater-readme.txt | 125 ++++++ code/autoupdater/autoupdater.c | 721 +++++++++++++++++++++++++++++++++ code/autoupdater/sha256.c | 158 ++++++++ code/autoupdater/sha256.h | 34 ++ code/sys/sys_main.c | 46 +++ 6 files changed, 1147 insertions(+), 22 deletions(-) create mode 100644 autoupdater-readme.txt create mode 100644 code/autoupdater/autoupdater.c create mode 100644 code/autoupdater/sha256.c create mode 100644 code/autoupdater/sha256.h diff --git a/Makefile b/Makefile index cd98122bee..06507596f6 100644 --- a/Makefile +++ b/Makefile @@ -35,6 +35,9 @@ endif ifndef BUILD_RENDERER_OPENGL2 BUILD_RENDERER_OPENGL2= endif +ifndef BUILD_AUTOUPDATER + BUILD_AUTOUPDATER= +endif ############################################################################# # @@ -228,6 +231,10 @@ ifndef USE_YACC USE_YACC=0 endif +ifndef USE_AUTOUPDATER +USE_AUTOUPDATER=1 +endif + ifndef DEBUG_CFLAGS DEBUG_CFLAGS=-ggdb -O0 endif @@ -262,6 +269,7 @@ LBURGDIR=$(MOUNT_DIR)/tools/lcc/lburg Q3CPPDIR=$(MOUNT_DIR)/tools/lcc/cpp Q3LCCETCDIR=$(MOUNT_DIR)/tools/lcc/etc Q3LCCSRCDIR=$(MOUNT_DIR)/tools/lcc/src +AUTOUPDATERSRCDIR=$(MOUNT_DIR)/autoupdater LOKISETUPDIR=misc/setup NSISDIR=misc/nsis SDLHDIR=$(MOUNT_DIR)/SDL2 @@ -269,29 +277,30 @@ LIBSDIR=$(MOUNT_DIR)/libs bin_path=$(shell which $(1) 2> /dev/null) +# The autoupdater uses curl, so figure out its flags no matter what. # We won't need this if we only build the server -ifneq ($(BUILD_CLIENT),0) - # set PKG_CONFIG_PATH to influence this, e.g. - # PKG_CONFIG_PATH=/opt/cross/i386-mingw32msvc/lib/pkgconfig - ifneq ($(call bin_path, pkg-config),) - CURL_CFLAGS ?= $(shell pkg-config --silence-errors --cflags libcurl) - CURL_LIBS ?= $(shell pkg-config --silence-errors --libs libcurl) - OPENAL_CFLAGS ?= $(shell pkg-config --silence-errors --cflags openal) - OPENAL_LIBS ?= $(shell pkg-config --silence-errors --libs openal) - SDL_CFLAGS ?= $(shell pkg-config --silence-errors --cflags sdl2|sed 's/-Dmain=SDL_main//') - SDL_LIBS ?= $(shell pkg-config --silence-errors --libs sdl2) - FREETYPE_CFLAGS ?= $(shell pkg-config --silence-errors --cflags freetype2) - else - # assume they're in the system default paths (no -I or -L needed) - CURL_LIBS ?= -lcurl - OPENAL_LIBS ?= -lopenal - endif - # Use sdl2-config if all else fails - ifeq ($(SDL_CFLAGS),) - ifneq ($(call bin_path, sdl2-config),) - SDL_CFLAGS ?= $(shell sdl2-config --cflags) - SDL_LIBS ?= $(shell sdl2-config --libs) - endif + +# set PKG_CONFIG_PATH to influence this, e.g. +# PKG_CONFIG_PATH=/opt/cross/i386-mingw32msvc/lib/pkgconfig +ifneq ($(call bin_path, pkg-config),) + CURL_CFLAGS ?= $(shell pkg-config --silence-errors --cflags libcurl) + CURL_LIBS ?= $(shell pkg-config --silence-errors --libs libcurl) + OPENAL_CFLAGS ?= $(shell pkg-config --silence-errors --cflags openal) + OPENAL_LIBS ?= $(shell pkg-config --silence-errors --libs openal) + SDL_CFLAGS ?= $(shell pkg-config --silence-errors --cflags sdl2|sed 's/-Dmain=SDL_main//') + SDL_LIBS ?= $(shell pkg-config --silence-errors --libs sdl2) + FREETYPE_CFLAGS ?= $(shell pkg-config --silence-errors --cflags freetype2) +else + # assume they're in the system default paths (no -I or -L needed) + CURL_LIBS ?= -lcurl + OPENAL_LIBS ?= -lopenal +endif + +# Use sdl2-config if all else fails +ifeq ($(SDL_CFLAGS),) + ifneq ($(call bin_path, sdl2-config),) + SDL_CFLAGS ?= $(shell sdl2-config --cflags) + SDL_LIBS ?= $(shell sdl2-config --libs) endif endif @@ -975,6 +984,11 @@ ifneq ($(BUILD_GAME_QVM),0) endif endif +ifneq ($(BUILD_AUTOUPDATER),0) + AUTOUPDATER_BIN := autoupdater$(FULLBINEXT) + TARGETS += $(B)/$(AUTOUPDATER_BIN) +endif + ifeq ($(USE_OPENAL),1) CLIENT_CFLAGS += -DUSE_OPENAL ifeq ($(USE_OPENAL_DLOPEN),1) @@ -1075,6 +1089,11 @@ ifeq ($(USE_FREETYPE),1) RENDERER_LIBS += $(FREETYPE_LIBS) endif +ifeq ($(USE_AUTOUPDATER),1) + CLIENT_CFLAGS += -DUSE_AUTOUPDATER -DAUTOUPDATER_BIN=\\\"$(AUTOUPDATER_BIN)\\\" + SERVER_CFLAGS += -DUSE_AUTOUPDATER -DAUTOUPDATER_BIN=\\\"$(AUTOUPDATER_BIN)\\\" +endif + ifeq ("$(CC)", $(findstring "$(CC)", "clang" "clang++")) BASE_CFLAGS += -Qunused-arguments endif @@ -1331,6 +1350,7 @@ endif makedirs: @if [ ! -d $(BUILD_DIR) ];then $(MKDIR) $(BUILD_DIR);fi @if [ ! -d $(B) ];then $(MKDIR) $(B);fi + @if [ ! -d $(B)/autoupdater ];then $(MKDIR) $(B)/autoupdater;fi @if [ ! -d $(B)/client ];then $(MKDIR) $(B)/client;fi @if [ ! -d $(B)/client/opus ];then $(MKDIR) $(B)/client/opus;fi @if [ ! -d $(B)/client/vorbis ];then $(MKDIR) $(B)/client/vorbis;fi @@ -1550,6 +1570,27 @@ $(Q3ASM): $(Q3ASMOBJ) $(Q)$(TOOLS_CC) $(TOOLS_CFLAGS) $(TOOLS_LDFLAGS) -o $@ $^ $(TOOLS_LIBS) +############################################################################# +# AUTOUPDATER +############################################################################# + +define DO_AUTOUPDATER_CC +$(echo_cmd) "AUTOUPDATER_CC $<" +$(Q)$(TOOLS_CC) $(CFLAGS) $(CURL_CFLAGS) -o $@ -c $< +endef + +Q3AUTOUPDATEROBJ = \ + $(B)/autoupdater/autoupdater.o \ + $(B)/autoupdater/sha256.o \ + +$(B)/autoupdater/%.o: $(AUTOUPDATERSRCDIR)/%.c + $(DO_AUTOUPDATER_CC) + +$(B)/$(AUTOUPDATER_BIN): $(Q3AUTOUPDATEROBJ) + $(echo_cmd) "AUTOUPDATER_LD $@" + $(Q)$(CC) $(LDFLAGS) $(CURL_LIBS) -o $@ $(Q3AUTOUPDATEROBJ) + + ############################################################################# # CLIENT/SERVER ############################################################################# diff --git a/autoupdater-readme.txt b/autoupdater-readme.txt new file mode 100644 index 0000000000..1353f4f06c --- /dev/null +++ b/autoupdater-readme.txt @@ -0,0 +1,125 @@ +The updater program's code is public domain. The rest of ioquake3 is not. + +The source code to the autoupdater is in the code/autoupdater directory. +There is a small piece of code in ioquake3 itself at startup, too. + +(This is all Unix terminology, but similar approaches on Windows apply.) + +The updater is a separate program, written in C, with no dependencies on +the game. It (statically) links to libcurl and uses the C runtime, but +otherwise has no external dependencies. It has to be a single binary file +with no shared libraries. + +The basic flow looks like this: + +- The game launches as usual. +- Right after main() starts, the game creates a pipe, forks off a new process, + and execs the updater in that process. The game won't ever touch the pipe + again. It's just there to block the child app until the game terminates. +- The updater has no UI. It writes a log file. +- The updater downloads a manifest from a known URL over https://, using + libCurl. The base URL is platform-specific (it might be + https://example.com/mac/, or https://example.com/linux-x86/, whatever). + The manifest is at $BASEURL/manifest.txt +- The manifest looks like this: three lines per file... + +Contents/MacOS/baseq3/uix86_64.dylib +332428 +a49bbe77f8eb6c195265ea136f881f7830db58e4d8a883b27f59e1e23e396a20 + +- That's the file's path, its size in bytes, and an sha256 hash of the data. +- The file will be at this path under the base url on the webserver. +- The manifest only lists files that ever needed updating; it's not necessary + to list every file in the game's installation (unless you want to allow the + entire game to download). +- The updater will check each item in the manifest: + - Does the file not exist in the install? Needs downloading. + - Does the file have a different size? Needs downloading. + - Does the file have a different sha256sum? Needs downloading. + - Otherwise, file is up to date, leave it alone. +- If an item needs downloading, do these same checks against the file in the + download directory (if it's already there and matches, don't download again.) +- Download necessary files with libcurl, put it in a download directory. +- The downloaded file is also checked for size and sha256 vs the manifest, to + make sure there was no corruption or confusion. If a downloaded file doesn't + match what was expected, the updater aborts and will try again next time. + This could fail checksum due to i/o errors and compromised security, but + it might just be that a new version was being published and bad luck + happened, and a retry later could correct everything. +- If the updater itself needs upgrading, we deal with that first. It's + downloaded, then the updater relaunches from the downloaded binary with + a special command line. That relaunched process copies itself to the proper + location, and then relaunches _again_ to restart the normal updating + process with the new updater in its correct position. +- Once the downloads are complete and the updater itself doesn't need + upgrading, we are ready to start the normal upgrade. Since we can't replace + executables on some platforms while they are running, and swapping out a + game's data files at runtime isn't wise in general, the updater will now + block until the game terminates. It does this by reading on the pipe that + the game created when forking the updater; since the game never writes + anything to this pipe, it causes the updater to block until the pipe closes. + Since the game never deliberately closes the pipe either, it remains open + until the OS forcibly closes it as the game process terminates. Being an + unnamed pipe, it just vaporizes at this point, leaving no state that might + accidentally hang us up later, like a global semaphore or whatnot. This + technique also lets us localize the game's code changes to one small block + of C code, with no need to manage these resources elsewhere. +- As a sanity check, the updater will also kill(game_process_id, 0) until it + fails, sleeping for 100 milliseconds between each attempt, in case the + process is still being cleaned up by the OS after closing the pipe. +- Once the updater is confident the game process is gone, it will start + upgrading the appropriate files. It does this in two steps: it moves + the old file to a "rollback" directory so it's out of the way but still + available, then it moves the newly-downloaded file into place. Since these + are all simple renames and not copies, this can move fast. Any missing + parent directories are created, in case the update is adding a new file + in a directory that didn't previously exist. +- If something goes wrong at this point (file i/o error, etc), the updater + will roll back the changes by deleting the updated files, and moving the + files in the "rollback" directory back to their original locations. Then + the updater aborts. +- If nothing went wrong, the rollback files are deleted. And we are officially + up to date! The updater terminates. + + +The updater is designed to fail at any point. If a download fails, it'll +pick up and try again next time, etc. Completed downloads will remain, so it +will just need to download any missing/incomplete files. + +The server side just needs to be able to serve static files over HTTPS from +any standard Apache/nginx/whatever process. + +Failure points: +- If the updater fails when still downloading data, it just picks up on next + restart. +- If the updater fails when replacing files, it rolls back any changes it has + made. +- If the updater fails when rolling back, then running the updater again after + fixing the specific problem (disk error, etc?) will redownload and replace + any files that were left in an uncertain state. The only true point of + risk is crashing during a rollback and then having the updater bricked for + some reason, but that's an extremely small surface area, knock on wood. +- If the updater crashes or totally bricks, ioquake3 should just keep being + ioquake3. It will still launch and play, even if the updater is quietly + segfaulting in the background on startup. +- If an update bricks ioquake3 to the point where it can't run the updater, + running the updater directly should let it recover (assuming a future update + fixes the problem). + + +Items to consider for future revisions: +- GPG sign the manifest; if we can be confident that the manifest isn't + compromised, then the sha256 hashes of each file it contains should protect + the rest of the process. As it currently stands, we trust the download + server isn't compromised. +- Maybe put a limit on the number manifest downloads, so we only check once + every hour? Every day? +- Channels? Stable (what everyone gets by default), Nightly (once a day), + Experimental (some other work-in-progress branch), Bloody (literally the + latest commit). +- Let mods update, separate from the main game? + +Questions? Ask Ryan: icculus@icculus.org + +--ryan. + diff --git a/code/autoupdater/autoupdater.c b/code/autoupdater/autoupdater.c new file mode 100644 index 0000000000..e8b8d40c1f --- /dev/null +++ b/code/autoupdater/autoupdater.c @@ -0,0 +1,721 @@ +/* +The code in this file is in the public domain. The rest of ioquake3 +is licensed until the GPLv2. Do not mingle code, please! +*/ + +#include +#include +#include +#include +#include + +#include +#include +#include + +#include +#include "sha256.h" + +#ifndef AUTOUPDATE_USER_AGENT +#define AUTOUPDATE_USER_AGENT "ioq3autoupdater/0.1" +#endif + + +#ifndef AUTOUPDATE_URL + +#ifndef AUTOUPDATE_BASEURL +#define AUTOUPDATE_BASEURL "https://upd.ioquake3.org/updates/v1" +#endif + +#ifndef AUTOUPDATE_PACKAGE +#define AUTOUPDATE_PACKAGE "ioquake3" +#endif + +#ifdef __APPLE__ +#define AUTOUPDATE_PLATFORM "mac" +#elif defined(__linux__) +#define AUTOUPDATE_PLATFORM "linux" +#else +#error Please define your platform. +#endif + +#ifdef __i386__ +#define AUTOUPDATE_ARCH "i386" +#elif defined(__x86_64__) +#define AUTOUPDATE_ARCH "x86-64" +#else +#error Please define your platform. +#endif + +#define AUTOUPDATE_URL AUTOUPDATE_BASEURL "/" AUTOUPDATE_PACKAGE "/" AUTOUPDATE_PLATFORM "/" AUTOUPDATE_ARCH "/" +#endif + +#if defined(__GNUC__) || defined(__clang__) +#define NEVER_RETURNS __attribute__((noreturn)) +#else +#define NEVER_RETURNS +#endif + + +typedef struct +{ + pid_t waitforprocess; + const char *updateself; +} Options; + +static Options options; + + +typedef struct ManifestItem +{ + char *fname; + unsigned char sha256[32]; + int64_t len; + int update; + int rollback; + struct ManifestItem *next; +} ManifestItem; + +static ManifestItem *manifest; + +static void freeManifest(void) +{ + ManifestItem *item = manifest; + manifest = NULL; + + while (item != NULL) { + ManifestItem *next = item->next; + free(item->fname); + free(item); + item = next; + } + manifest = NULL; +} + + +static FILE *logfile = NULL; + +#define SDL_PRINTF_VARARG_FUNC( fmtargnumber ) __attribute__ (( format( __printf__, fmtargnumber, fmtargnumber+1 ))) + +static void info(const char *str) +{ + fputs(str, logfile); + fputs("\n", logfile); + fflush(logfile); +} + +static void infof(const char *fmt, ...) +#if defined(__GNUC__) || defined(__clang__) +__attribute__ (( format( __printf__, 1, 2 ))) +#endif +; + +static void infof(const char *fmt, ...) +{ + va_list ap; + va_start(ap, fmt); + vfprintf(logfile, fmt, ap); + va_end(ap); + fputs("\n", logfile); + fflush(logfile); +} + +static void restoreRollbacks(void) +{ + /* you can't call die() in this function! If this fails, you're doomed. */ + ManifestItem *item; + for (item = manifest; item != NULL; item = item->next) { + if (item->rollback) { + char rollbackPath[64]; + snprintf(rollbackPath, sizeof (rollbackPath), "updates/rollbacks/%d", item->rollback); + infof("restore rollback: '%s' -> '%s'", rollbackPath, item->fname); + remove(item->fname); + rename(rollbackPath, item->fname); + } + } +} + + +static void die(const char *why) NEVER_RETURNS; +static void die(const char *why) +{ + infof("FAILURE: %s", why); + restoreRollbacks(); + freeManifest(); + exit(1); +} + +static void outOfMemory() NEVER_RETURNS; +static void outOfMemory() +{ + die("Out of memory"); +} + +static void makeDir(const char *dirname) +{ + /* !!! FIXME: we don't care if this fails right now. */ + mkdir(dirname, 0777); +} + +static void buildParentDirs(const char *_path) +{ + char *ptr; + char *path = (char *) alloca(strlen(_path) + 1); + if (!path) { + outOfMemory(); + } + strcpy(path, _path); + + for (ptr = path; *ptr; ptr++) { + if (*ptr == '/') { + *ptr = '\0'; + makeDir(path); + *ptr = '/'; + } + } +} + +static int64_t fileLength(const char *fname) +{ + struct stat statbuf; + if (stat(fname, &statbuf) == -1) { + return -1; + } + return (int64_t) statbuf.st_size; +} + +static void parseArgv(int argc, char **argv) +{ + int i; + + infof("command line (argc=%d)...", argc); + for (i = 0; i < argc; i++) { + infof(" argv[%d]: %s",i, argv[i]); + } + + for (i = 1; i < argc; i += 2) { + if (strcmp(argv[i], "--waitpid") == 0) { + options.waitforprocess = atoll(argv[i + 1]); + infof("We will wait for process %lld if necessary", (long long) options.waitforprocess); + } else if (strcmp(argv[i], "--updateself") == 0) { + options.updateself = argv[i + 1]; + infof("We are updating ourself ('%s')", options.updateself); + } + } +} + +static CURL *prepCurl(const char *url, FILE *outfile) +{ + char *fullurl; + const size_t len = strlen(AUTOUPDATE_URL) + strlen(url) + 1; + CURL *curl = curl_easy_init(); + if (!curl) { + die("curl_easy_init() failed"); + } + + fullurl = (char *) alloca(len); + if (!fullurl) { + outOfMemory(); + } + + snprintf(fullurl, len, "%s%s", AUTOUPDATE_URL, url); + + infof("Downloading from '%s'", fullurl); + + #if 0 + /* !!! FIXME: enable compression? */ + curl_easy_setopt(curl, CURLOPT_ACCEPT_ENCODING, ""); /* enable compression */ + + /* !!! FIXME; hook up proxy support to libcurl */ + curl_easy_setopt(curl, CURLOPT_PROXY, proxyURL); + #endif + + curl_easy_setopt(curl, CURLOPT_VERBOSE, 1L); + curl_easy_setopt(curl, CURLOPT_NOPROGRESS, 1L); + curl_easy_setopt(curl, CURLOPT_STDERR, logfile); + + curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, fwrite); + curl_easy_setopt(curl, CURLOPT_WRITEDATA, outfile); + + curl_easy_setopt(curl, CURLOPT_URL, fullurl); + + curl_easy_setopt(curl, CURLOPT_FAILONERROR, 1L); + curl_easy_setopt(curl, CURLOPT_FOLLOWLOCATION, 1L); /* allow redirects. */ + curl_easy_setopt(curl, CURLOPT_USERAGENT, AUTOUPDATE_USER_AGENT); + + curl_easy_setopt(curl, CURLOPT_SSL_VERIFYPEER, 1L); /* require valid SSL cert. */ + curl_easy_setopt(curl, CURLOPT_SSL_VERIFYHOST, 2L); /* require SSL cert with same hostname as we connected to. */ + + return curl; +} + +static void downloadURL(const char *from, const char *to) +{ + FILE *io; + CURL *curl; + + infof("Preparing to download to '%s'", to); + + buildParentDirs(to); + io = fopen(to, "wb"); + if (!io) { + die("Failed to open output file"); + } + + curl = prepCurl(from, io); + if (curl_easy_perform(curl) != CURLE_OK) { + remove(to); + die("Download failed"); + } + curl_easy_cleanup(curl); + + if (fclose(io) == EOF) { + die("Can't flush file on close. i/o error? Disk full?"); + } + + chmod(to, 0777); /* !!! FIXME */ +} + +static int hexcvt(const int ch) +{ + if ((ch >= 'a') && (ch <= 'f')) { + return (ch - 'a') + 10; + } else if ((ch >= 'A') && (ch <= 'F')) { + return (ch - 'A') + 10; + } else if ((ch >= '0') && (ch <= '9')) { + return ch - '0'; + } else { + die("Invalid hex character"); + } + return 0; +} + +static void convertSha256(char *str, BYTE *sha256) +{ + int i; + for (i = 0; i < 32; i++) { + const int a = hexcvt(*(str++)); + const int b = hexcvt(*(str++)); + *sha256 = (a << 4) | b; + sha256++; + } +} + +static void parseManifest(const char *fname) +{ + ManifestItem *item = NULL; + FILE *io = fopen(fname, "r"); + char buf[512]; + if (!io) { + die("Failed to open manifest for reading"); + } + + /* !!! FIXME: this code sucks. */ + while (fgets(buf, sizeof (buf), io)) { + char *ptr = (buf + strlen(buf)) - 1; + while (ptr >= buf) { + if ((*ptr != '\n') && (*ptr != '\r')) { + break; + } + *ptr = '\0'; + ptr--; + } + + if (!item && !buf[0]) { + continue; /* blank line between items or blank at EOF */ + } + + if (!item) { + infof("Next manifest item: %s", buf); + + item = (ManifestItem *) malloc(sizeof (ManifestItem)); + if (!item) { + outOfMemory(); + } + item->fname = strdup(buf); + if (!item->fname) { + outOfMemory(); + } + item->len = -1; + item->next = NULL; + } else if (item->len == -1) { + infof("Item size: %s", buf); + item->len = atoll(buf); + } else { + infof("Item sha256: %s", buf); + convertSha256(buf, item->sha256); + item->next = manifest; + manifest = item; + item = NULL; + } + } + + if (ferror(io)) { + die("Error reading manifest"); + } else if (item) { + die("Incomplete manifest"); + } + + fclose(io); +} + +static void downloadManifest(void) +{ + const char *manifestfname = "updates/manifest.txt"; + downloadURL("manifest.txt", manifestfname); + /* !!! FIXME: verify manifest download is complete... */ + parseManifest(manifestfname); +} + +static void upgradeSelfAndRestart(const char *argv0) NEVER_RETURNS; +static void upgradeSelfAndRestart(const char *argv0) +{ + const char *tempfname = "origUpdater"; + const char *why = NULL; + FILE *in = NULL; + FILE *out = NULL; + + in = fopen(argv0, "rb"); + if (!in) { + die("Can't open self for input while upgrading updater"); + } + + remove(tempfname); + if (rename(options.updateself, tempfname) == -1) { + die("Can't rename original while upgrading updater"); + } + + out = fopen(options.updateself, "wb"); + if (!out) { + die("Can't open file for output while upgrading updater"); + } + + while (!feof(in) && !why) { + char buf[512]; + const size_t br = fread(buf, 1, sizeof (buf), in); + if (br > 0) { + if (fwrite(buf, br, 1, out) != 1) { + why = "write failure while upgrading updater"; + } + } else if (ferror(in)) { + why = "read failure while upgrading updater"; + } + } + + fclose(in); + + if ((fclose(out) == EOF) && (!why)) { + why = "close failure while upgrading updater"; + } + + if (why) { + remove(options.updateself); + rename(tempfname, options.updateself); + die(why); + } + + remove(tempfname); + + chmod(options.updateself, 0777); + + if (options.waitforprocess) { + char pidstr[64]; + snprintf(pidstr, sizeof (pidstr), "%lld", (long long) options.waitforprocess); + execl(options.updateself, options.updateself, "--waitpid", pidstr, NULL); + } else { + execl(options.updateself, options.updateself, NULL); + } + die("Failed to relaunch upgraded updater"); +} + +static const char *justFilename(const char *path) +{ + const char *fname = strrchr(path, '/'); + return fname ? fname + 1 : path; +} + +static void hashFile(const char *fname, unsigned char *sha256) +{ + SHA256_CTX sha256ctx; + BYTE buf[512]; + FILE *io; + + io = fopen(fname, "rb"); + if (!io) { + die("Failed to open file for hashing"); + } + + sha256_init(&sha256ctx); + do { + size_t br = fread(buf, 1, sizeof (buf), io); + if (br > 0) { + sha256_update(&sha256ctx, buf, br); + } + if (ferror(io)) { + die("Error reading file for hashing"); + } + } while (!feof(io)); + + fclose(io); + + sha256_final(&sha256ctx, sha256); +} + +static int fileHashMatches(const char *fname, const unsigned char *wanted) +{ + unsigned char sha256[32]; + hashFile(fname, sha256); + return (memcmp(sha256, wanted, 32) == 0); +} + +static int fileNeedsUpdate(const ManifestItem *item) +{ + if (item->len != fileLength(item->fname)) { + infof("Update '%s', file size is different", item->fname); + return 1; /* obviously different. */ + } else if (!fileHashMatches(item->fname, item->sha256)) { + infof("Update '%s', file sha256 is different", item->fname); + return 1; + } + + infof("Don't update '%s', the file is already up to date", item->fname); + return 0; +} + +static void downloadFile(const ManifestItem *item) +{ + const char *outpath = "updates/downloads/"; + const size_t len = strlen(outpath) + strlen(item->fname) + 1; + char *to = (char *) alloca(len); + if (!to) { + outOfMemory(); + } + + snprintf(to, len, "%s%s", outpath, item->fname); + + if ((item->len == fileLength(to)) && fileHashMatches(to, item->sha256)) { + infof("Already downloaded '%s', not getting again", item->fname); + } else { + downloadURL(item->fname, to); + if ((item->len != fileLength(to)) || !fileHashMatches(to, item->sha256)) { + die("Download is incorrect or corrupted"); + } + } +} + +static int downloadUpdates(void) +{ + int updatesAvailable = 0; + ManifestItem *item; + for (item = manifest; item != NULL; item = item->next) { + item->update = fileNeedsUpdate(item); + if (item->update) { + updatesAvailable = 1; + downloadFile(item); + } + } + return updatesAvailable; +} + +static void maybeUpdateSelf(const char *argv0) +{ + ManifestItem *item; + + /* !!! FIXME: this needs to be a different string on macOS. */ + const char *fname = justFilename(argv0); + + for (item = manifest; item != NULL; item = item->next) { + if (strcasecmp(item->fname, fname) == 0) { + if (fileNeedsUpdate(item)) { + const char *outpath = "updates/downloads/"; + const size_t len = strlen(outpath) + strlen(item->fname) + 1; + char *to = (char *) alloca(len); + if (!to) { + outOfMemory(); + } + snprintf(to, len, "%s%s", outpath, item->fname); + info("Have to upgrade the updater"); + downloadFile(item); + chmod(to, 0777); + + if (options.waitforprocess) { + char pidstr[64]; + snprintf(pidstr, sizeof (pidstr), "%lld", (long long) options.waitforprocess); + execl(to, to, "--updateself", argv0, "--waitpid", pidstr, NULL); + } else { + execl(to, to, "--updateself", argv0, NULL); + } + die("Failed to initially launch upgraded updater"); + } + break; /* done in any case. */ + } + } +} + +static void installUpdatedFile(const ManifestItem *item) +{ + const char *basepath = "updates/downloads/"; + const size_t len = strlen(basepath) + strlen(item->fname) + 1; + char *downloadPath = (char *) alloca(len); + if (!downloadPath) { + outOfMemory(); + } + + snprintf(downloadPath, len, "%s%s", basepath, item->fname); + + infof("Moving file for update: '%s' -> '%s'", downloadPath, item->fname); + buildParentDirs(item->fname); + if (rename(downloadPath, item->fname) == -1) { + die("Failed to move updated file to final position"); + } +} + +static void applyUpdates(void) +{ + FILE *io; + ManifestItem *item; + for (item = manifest; item != NULL; item = item->next) { + if (!item->update) { + continue; + } + + io = fopen(item->fname, "rb"); + fclose(io); + if (io != NULL) { + static int rollbackIndex = 0; + char rollbackPath[64]; + item->rollback = ++rollbackIndex; + snprintf(rollbackPath, sizeof (rollbackPath), "updates/rollbacks/%d", rollbackIndex); + infof("Moving file for rollback: '%s' -> '%s'", item->fname, rollbackPath); + remove(rollbackPath); + if (rename(item->fname, rollbackPath) == -1) { + die("failed to move to rollback dir"); + } + } + + installUpdatedFile(item); + } +} + +static void waitToApplyUpdates(void) +{ + if (options.waitforprocess) { + /* ioquake3 opens a pipe on fd 3, and then forgets about it. We block + on a read to that pipe here. When the game process quits (and the + OS forcibly closes the pipe), we will unblock. Then we can loop on + kill() until the process is truly gone. */ + int x = 0; + infof("Waiting for pid %lld to die...", (long long) options.waitforprocess); + read(3, &x, sizeof (x)); + info("Pipe has closed, waiting for process to fully go away now."); + while (kill(options.waitforprocess, 0) == 0) { + usleep(100000); + } + info("pid is gone, continuing"); + } +} + +static void deleteRollbacks(void) +{ + ManifestItem *item; + for (item = manifest; item != NULL; item = item->next) { + if (item->rollback) { + char rollbackPath[64]; + snprintf(rollbackPath, sizeof (rollbackPath), "updates/rollbacks/%d", item->rollback); + infof("delete rollback: %s", rollbackPath); + remove(rollbackPath); + } + } +} + +static const char *timestamp(void) +{ + time_t t = time(NULL); + char *retval = asctime(localtime(&t)); + if (retval) { + char *ptr; + for (ptr = retval; *ptr; ptr++) { + if ((*ptr == '\r') || (*ptr == '\n')) { + *ptr = '\0'; + break; + } + } + } + return retval ? retval : "[date unknown]"; +} + +static void chdirToBasePath(const char *argv0) +{ + const char *fname = justFilename(argv0); + size_t len; + char *buf; + + if (fname == argv0) { /* no path? Assume we're already there. */ + return; + } + + len = ((size_t) (fname - argv0)) - 1; + buf = (char *) alloca(len); + if (!buf) { + outOfMemory(); + } + + memcpy(buf, argv0, len); + buf[len] = '\0'; + if (chdir(buf) == -1) { + infof("base path is '%s'", buf); + die("chdir to base path failed"); + } +} + +int main(int argc, char **argv) +{ + signal(SIGPIPE, SIG_IGN); /* don't trigger signal when fd3 closes */ + + logfile = stdout; + chdirToBasePath(argv[0]); + + makeDir("updates"); + makeDir("updates/downloads"); + makeDir("updates/rollbacks"); + + logfile = fopen("updates/updater-log.txt", "a"); + if (!logfile) { + logfile = stdout; + } + + infof("Updater starting, %s", timestamp()); + + parseArgv(argc, argv); + + /* if we have downloaded a new updater and restarted with that binary, + replace the original updater and restart again in the right place. */ + if (options.updateself) { + upgradeSelfAndRestart(argv[0]); + } + + if (curl_global_init(CURL_GLOBAL_DEFAULT) != CURLE_OK) { + die("curl_global_init() failed!"); + } + + downloadManifest(); /* see if we need an update at all. */ + + maybeUpdateSelf(argv[0]); /* might relaunch if there's an updater upgrade. */ + + if (!downloadUpdates()) { + info("Nothing needs updating, so we're done here!"); + } else { + waitToApplyUpdates(); + applyUpdates(); + deleteRollbacks(); + info("You are now up to date!"); + } + + freeManifest(); + curl_global_cleanup(); + + infof("Updater ending, %s", timestamp()); + + return 0; +} + diff --git a/code/autoupdater/sha256.c b/code/autoupdater/sha256.c new file mode 100644 index 0000000000..eb9c5c0733 --- /dev/null +++ b/code/autoupdater/sha256.c @@ -0,0 +1,158 @@ +/********************************************************************* +* Filename: sha256.c +* Author: Brad Conte (brad AT bradconte.com) +* Copyright: +* Disclaimer: This code is presented "as is" without any guarantees. +* Details: Implementation of the SHA-256 hashing algorithm. + SHA-256 is one of the three algorithms in the SHA2 + specification. The others, SHA-384 and SHA-512, are not + offered in this implementation. + Algorithm specification can be found here: + * http://csrc.nist.gov/publications/fips/fips180-2/fips180-2withchangenotice.pdf + This implementation uses little endian byte order. +*********************************************************************/ + +/*************************** HEADER FILES ***************************/ +#include +#include +#include "sha256.h" + +/****************************** MACROS ******************************/ +#define ROTLEFT(a,b) (((a) << (b)) | ((a) >> (32-(b)))) +#define ROTRIGHT(a,b) (((a) >> (b)) | ((a) << (32-(b)))) + +#define CH(x,y,z) (((x) & (y)) ^ (~(x) & (z))) +#define MAJ(x,y,z) (((x) & (y)) ^ ((x) & (z)) ^ ((y) & (z))) +#define EP0(x) (ROTRIGHT(x,2) ^ ROTRIGHT(x,13) ^ ROTRIGHT(x,22)) +#define EP1(x) (ROTRIGHT(x,6) ^ ROTRIGHT(x,11) ^ ROTRIGHT(x,25)) +#define SIG0(x) (ROTRIGHT(x,7) ^ ROTRIGHT(x,18) ^ ((x) >> 3)) +#define SIG1(x) (ROTRIGHT(x,17) ^ ROTRIGHT(x,19) ^ ((x) >> 10)) + +/**************************** VARIABLES *****************************/ +static const WORD k[64] = { + 0x428a2f98,0x71374491,0xb5c0fbcf,0xe9b5dba5,0x3956c25b,0x59f111f1,0x923f82a4,0xab1c5ed5, + 0xd807aa98,0x12835b01,0x243185be,0x550c7dc3,0x72be5d74,0x80deb1fe,0x9bdc06a7,0xc19bf174, + 0xe49b69c1,0xefbe4786,0x0fc19dc6,0x240ca1cc,0x2de92c6f,0x4a7484aa,0x5cb0a9dc,0x76f988da, + 0x983e5152,0xa831c66d,0xb00327c8,0xbf597fc7,0xc6e00bf3,0xd5a79147,0x06ca6351,0x14292967, + 0x27b70a85,0x2e1b2138,0x4d2c6dfc,0x53380d13,0x650a7354,0x766a0abb,0x81c2c92e,0x92722c85, + 0xa2bfe8a1,0xa81a664b,0xc24b8b70,0xc76c51a3,0xd192e819,0xd6990624,0xf40e3585,0x106aa070, + 0x19a4c116,0x1e376c08,0x2748774c,0x34b0bcb5,0x391c0cb3,0x4ed8aa4a,0x5b9cca4f,0x682e6ff3, + 0x748f82ee,0x78a5636f,0x84c87814,0x8cc70208,0x90befffa,0xa4506ceb,0xbef9a3f7,0xc67178f2 +}; + +/*********************** FUNCTION DEFINITIONS ***********************/ +void sha256_transform(SHA256_CTX *ctx, const BYTE data[]) +{ + WORD a, b, c, d, e, f, g, h, i, j, t1, t2, m[64]; + + for (i = 0, j = 0; i < 16; ++i, j += 4) + m[i] = (data[j] << 24) | (data[j + 1] << 16) | (data[j + 2] << 8) | (data[j + 3]); + for ( ; i < 64; ++i) + m[i] = SIG1(m[i - 2]) + m[i - 7] + SIG0(m[i - 15]) + m[i - 16]; + + a = ctx->state[0]; + b = ctx->state[1]; + c = ctx->state[2]; + d = ctx->state[3]; + e = ctx->state[4]; + f = ctx->state[5]; + g = ctx->state[6]; + h = ctx->state[7]; + + for (i = 0; i < 64; ++i) { + t1 = h + EP1(e) + CH(e,f,g) + k[i] + m[i]; + t2 = EP0(a) + MAJ(a,b,c); + h = g; + g = f; + f = e; + e = d + t1; + d = c; + c = b; + b = a; + a = t1 + t2; + } + + ctx->state[0] += a; + ctx->state[1] += b; + ctx->state[2] += c; + ctx->state[3] += d; + ctx->state[4] += e; + ctx->state[5] += f; + ctx->state[6] += g; + ctx->state[7] += h; +} + +void sha256_init(SHA256_CTX *ctx) +{ + ctx->datalen = 0; + ctx->bitlen = 0; + ctx->state[0] = 0x6a09e667; + ctx->state[1] = 0xbb67ae85; + ctx->state[2] = 0x3c6ef372; + ctx->state[3] = 0xa54ff53a; + ctx->state[4] = 0x510e527f; + ctx->state[5] = 0x9b05688c; + ctx->state[6] = 0x1f83d9ab; + ctx->state[7] = 0x5be0cd19; +} + +void sha256_update(SHA256_CTX *ctx, const BYTE data[], size_t len) +{ + WORD i; + + for (i = 0; i < len; ++i) { + ctx->data[ctx->datalen] = data[i]; + ctx->datalen++; + if (ctx->datalen == 64) { + sha256_transform(ctx, ctx->data); + ctx->bitlen += 512; + ctx->datalen = 0; + } + } +} + +void sha256_final(SHA256_CTX *ctx, BYTE hash[]) +{ + WORD i; + + i = ctx->datalen; + + // Pad whatever data is left in the buffer. + if (ctx->datalen < 56) { + ctx->data[i++] = 0x80; + while (i < 56) + ctx->data[i++] = 0x00; + } + else { + ctx->data[i++] = 0x80; + while (i < 64) + ctx->data[i++] = 0x00; + sha256_transform(ctx, ctx->data); + memset(ctx->data, 0, 56); + } + + // Append to the padding the total message's length in bits and transform. + ctx->bitlen += ctx->datalen * 8; + ctx->data[63] = ctx->bitlen; + ctx->data[62] = ctx->bitlen >> 8; + ctx->data[61] = ctx->bitlen >> 16; + ctx->data[60] = ctx->bitlen >> 24; + ctx->data[59] = ctx->bitlen >> 32; + ctx->data[58] = ctx->bitlen >> 40; + ctx->data[57] = ctx->bitlen >> 48; + ctx->data[56] = ctx->bitlen >> 56; + sha256_transform(ctx, ctx->data); + + // Since this implementation uses little endian byte ordering and SHA uses big endian, + // reverse all the bytes when copying the final state to the output hash. + for (i = 0; i < 4; ++i) { + hash[i] = (ctx->state[0] >> (24 - i * 8)) & 0x000000ff; + hash[i + 4] = (ctx->state[1] >> (24 - i * 8)) & 0x000000ff; + hash[i + 8] = (ctx->state[2] >> (24 - i * 8)) & 0x000000ff; + hash[i + 12] = (ctx->state[3] >> (24 - i * 8)) & 0x000000ff; + hash[i + 16] = (ctx->state[4] >> (24 - i * 8)) & 0x000000ff; + hash[i + 20] = (ctx->state[5] >> (24 - i * 8)) & 0x000000ff; + hash[i + 24] = (ctx->state[6] >> (24 - i * 8)) & 0x000000ff; + hash[i + 28] = (ctx->state[7] >> (24 - i * 8)) & 0x000000ff; + } +} diff --git a/code/autoupdater/sha256.h b/code/autoupdater/sha256.h new file mode 100644 index 0000000000..7123a30dd4 --- /dev/null +++ b/code/autoupdater/sha256.h @@ -0,0 +1,34 @@ +/********************************************************************* +* Filename: sha256.h +* Author: Brad Conte (brad AT bradconte.com) +* Copyright: +* Disclaimer: This code is presented "as is" without any guarantees. +* Details: Defines the API for the corresponding SHA1 implementation. +*********************************************************************/ + +#ifndef SHA256_H +#define SHA256_H + +/*************************** HEADER FILES ***************************/ +#include + +/****************************** MACROS ******************************/ +#define SHA256_BLOCK_SIZE 32 // SHA256 outputs a 32 byte digest + +/**************************** DATA TYPES ****************************/ +typedef unsigned char BYTE; // 8-bit byte +typedef unsigned int WORD; // 32-bit word, change to "long" for 16-bit machines + +typedef struct { + BYTE data[64]; + WORD datalen; + unsigned long long bitlen; + WORD state[8]; +} SHA256_CTX; + +/*********************** FUNCTION DECLARATIONS **********************/ +void sha256_init(SHA256_CTX *ctx); +void sha256_update(SHA256_CTX *ctx, const BYTE data[], size_t len); +void sha256_final(SHA256_CTX *ctx, BYTE hash[]); + +#endif // SHA256_H diff --git a/code/sys/sys_main.c b/code/sys/sys_main.c index 6d7fe7bf95..e7543044d7 100644 --- a/code/sys/sys_main.c +++ b/code/sys/sys_main.c @@ -31,6 +31,10 @@ Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA #include #include +#ifndef _WIN32 +#include +#endif + #ifndef DEDICATED #ifdef USE_LOCAL_HEADERS # include "SDL.h" @@ -659,6 +663,48 @@ int main( int argc, char **argv ) int i; char commandLine[ MAX_STRING_CHARS ] = { 0 }; +#ifdef USE_AUTOUPDATER +{ + #ifndef AUTOUPDATER_BIN + #error The build system should have defined AUTOUPDATER_BIN + #endif + + int updater_pipes[2]; + if (pipe(updater_pipes) == 0) + { + pid_t pid = fork(); + if (pid == -1) /* failure, oh well. */ + { + close(updater_pipes[0]); + close(updater_pipes[1]); + } + else if (pid == 0) /* child process */ + { + close(updater_pipes[1]); /* don't need write end. */ + if (dup2(updater_pipes[0], 3) != -1) + { + char pidstr[64]; + char *ptr = strrchr(argv[0], '/'); + if (ptr) + *ptr = '\0'; + chdir(argv[0]); + #ifdef __APPLE__ + chdir("../.."); /* put this at base of app bundle so paths make sense later. */ + #endif + snprintf(pidstr, sizeof (pidstr), "%lld", (long long) getppid()); + execl(AUTOUPDATER_BIN, AUTOUPDATER_BIN, "--waitpid", pidstr, NULL); + } + _exit(0); /* oh well. */ + } + else /* parent process */ + { + /* leave the write end open until we terminate so updater can block on it. */ + close(updater_pipes[0]); + } + } +} +#endif + #ifndef DEDICATED // SDL version check From 8e001e6b99e64163d9a32896592839f6aeb38e2c Mon Sep 17 00:00:00 2001 From: "Ryan C. Gordon" Date: Thu, 25 May 2017 22:01:47 -0400 Subject: [PATCH 011/240] autoupdater should build with $(CC), not $(TOOLS_CC), in Makefile. --- Makefile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Makefile b/Makefile index 06507596f6..5315fbfc83 100644 --- a/Makefile +++ b/Makefile @@ -1576,7 +1576,7 @@ $(Q3ASM): $(Q3ASMOBJ) define DO_AUTOUPDATER_CC $(echo_cmd) "AUTOUPDATER_CC $<" -$(Q)$(TOOLS_CC) $(CFLAGS) $(CURL_CFLAGS) -o $@ -c $< +$(Q)$(CC) $(CFLAGS) $(CURL_CFLAGS) -o $@ -c $< endef Q3AUTOUPDATEROBJ = \ From 240965ddbce2844afaabd0e6d7aba22b8940fa6b Mon Sep 17 00:00:00 2001 From: "Ryan C. Gordon" Date: Thu, 25 May 2017 22:02:33 -0400 Subject: [PATCH 012/240] Removed extraneous '\' in Makefile in autoupdater source file list. --- Makefile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Makefile b/Makefile index 5315fbfc83..28fec01ae8 100644 --- a/Makefile +++ b/Makefile @@ -1581,7 +1581,7 @@ endef Q3AUTOUPDATEROBJ = \ $(B)/autoupdater/autoupdater.o \ - $(B)/autoupdater/sha256.o \ + $(B)/autoupdater/sha256.o $(B)/autoupdater/%.o: $(AUTOUPDATERSRCDIR)/%.c $(DO_AUTOUPDATER_CC) From bc2f45508d33c50a23ccd5668ac82f673fec3a83 Mon Sep 17 00:00:00 2001 From: Zack Middleton Date: Fri, 26 May 2017 10:49:42 -0500 Subject: [PATCH 013/240] Fix dllHandle possibly being uninitialized in Sys_LoadDll --- code/sys/sys_main.c | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/code/sys/sys_main.c b/code/sys/sys_main.c index 14179b90d4..b559861b9f 100644 --- a/code/sys/sys_main.c +++ b/code/sys/sys_main.c @@ -498,7 +498,7 @@ from executable path, then fs_basepath. void *Sys_LoadDll(const char *name, qboolean useSystemLib) { - void *dllhandle; + void *dllhandle = NULL; if(!Sys_DllExtension(name)) { @@ -507,9 +507,12 @@ void *Sys_LoadDll(const char *name, qboolean useSystemLib) } if(useSystemLib) + { Com_Printf("Trying to load \"%s\"...\n", name); + dllhandle = Sys_LoadLibrary(name); + } - if(!useSystemLib || !(dllhandle = Sys_LoadLibrary(name))) + if(!dllhandle) { const char *topDir; char libPath[MAX_OSPATH]; From 024a8842bd9c090eaab70efec77b75c8855c4ba5 Mon Sep 17 00:00:00 2001 From: Zack Middleton Date: Mon, 29 May 2017 09:53:19 -0500 Subject: [PATCH 014/240] Fix compiling debug code for writing VoIP data --- code/client/cl_parse.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/code/client/cl_parse.c b/code/client/cl_parse.c index 39682507e5..318c8d8830 100644 --- a/code/client/cl_parse.c +++ b/code/client/cl_parse.c @@ -804,10 +804,10 @@ void CL_ParseVoip ( msg_t *msg, qboolean ignoreData ) { #if 0 static FILE *encio = NULL; if (encio == NULL) encio = fopen("voip-incoming-encoded.bin", "wb"); - if (encio != NULL) { fwrite(encoded, len, 1, encio); fflush(encio); } + if (encio != NULL) { fwrite(encoded, packetsize, 1, encio); fflush(encio); } static FILE *decio = NULL; if (decio == NULL) decio = fopen("voip-incoming-decoded.bin", "wb"); - if (decio != NULL) { fwrite(decoded+written, clc.speexFrameSize*2, 1, decio); fflush(decio); } + if (decio != NULL) { fwrite(decoded+written, numSamples*2, 1, decio); fflush(decio); } #endif written += numSamples; From 8a50e2aa09e05149f4d0b72b148da12caa56f73f Mon Sep 17 00:00:00 2001 From: Zack Middleton Date: Mon, 29 May 2017 16:34:55 -0500 Subject: [PATCH 015/240] Don't repeat alt+enter key event Holding alt+enter should not continuously toggle fullscreen mode. --- code/client/cl_keys.c | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/code/client/cl_keys.c b/code/client/cl_keys.c index f73083a57a..4945497bce 100644 --- a/code/client/cl_keys.c +++ b/code/client/cl_keys.c @@ -1239,6 +1239,11 @@ void CL_KeyDownEvent( int key, unsigned time ) if( keys[K_ALT].down && key == K_ENTER ) { + // don't repeat fullscreen toggle when keys are held down + if ( keys[K_ENTER].repeats > 1 ) { + return; + } + Cvar_SetValue( "r_fullscreen", !Cvar_VariableIntegerValue( "r_fullscreen" ) ); return; From 69829916b51c80c77297d96aedbd8b5be72e7d4a Mon Sep 17 00:00:00 2001 From: "Ryan C. Gordon" Date: Tue, 30 May 2017 17:37:53 -0400 Subject: [PATCH 016/240] Fixed -Wstrict-prototypes warning. --- code/autoupdater/autoupdater.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/code/autoupdater/autoupdater.c b/code/autoupdater/autoupdater.c index e8b8d40c1f..7e20d33125 100644 --- a/code/autoupdater/autoupdater.c +++ b/code/autoupdater/autoupdater.c @@ -145,8 +145,8 @@ static void die(const char *why) exit(1); } -static void outOfMemory() NEVER_RETURNS; -static void outOfMemory() +static void outOfMemory(void) NEVER_RETURNS; +static void outOfMemory(void) { die("Out of memory"); } From 86e71b11eb87a83a0ea1c55c53ae9df470cad27d Mon Sep 17 00:00:00 2001 From: "Ryan C. Gordon" Date: Tue, 30 May 2017 17:39:13 -0400 Subject: [PATCH 017/240] Fixed failure to link libcurl on some platforms. --- Makefile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Makefile b/Makefile index 28fec01ae8..99f2ffe43b 100644 --- a/Makefile +++ b/Makefile @@ -1588,7 +1588,7 @@ $(B)/autoupdater/%.o: $(AUTOUPDATERSRCDIR)/%.c $(B)/$(AUTOUPDATER_BIN): $(Q3AUTOUPDATEROBJ) $(echo_cmd) "AUTOUPDATER_LD $@" - $(Q)$(CC) $(LDFLAGS) $(CURL_LIBS) -o $@ $(Q3AUTOUPDATEROBJ) + $(Q)$(CC) $(LDFLAGS) -o $@ $(Q3AUTOUPDATEROBJ) $(CURL_LIBS) ############################################################################# From b892bcfdbc02c9089e60ebce854cf3b384b62139 Mon Sep 17 00:00:00 2001 From: "Ryan C. Gordon" Date: Tue, 30 May 2017 18:02:48 -0400 Subject: [PATCH 018/240] Cleanup in failures a little better. --- code/autoupdater/autoupdater.c | 36 ++++++++++++++++++---------------- 1 file changed, 19 insertions(+), 17 deletions(-) diff --git a/code/autoupdater/autoupdater.c b/code/autoupdater/autoupdater.c index 7e20d33125..f2e0535760 100644 --- a/code/autoupdater/autoupdater.c +++ b/code/autoupdater/autoupdater.c @@ -76,7 +76,7 @@ typedef struct ManifestItem struct ManifestItem *next; } ManifestItem; -static ManifestItem *manifest; +static ManifestItem *manifest = NULL; static void freeManifest(void) { @@ -92,6 +92,22 @@ static void freeManifest(void) manifest = NULL; } +static const char *timestamp(void) +{ + time_t t = time(NULL); + char *retval = asctime(localtime(&t)); + if (retval) { + char *ptr; + for (ptr = retval; *ptr; ptr++) { + if ((*ptr == '\r') || (*ptr == '\n')) { + *ptr = '\0'; + break; + } + } + } + return retval ? retval : "[date unknown]"; +} + static FILE *logfile = NULL; @@ -140,8 +156,10 @@ static void die(const char *why) NEVER_RETURNS; static void die(const char *why) { infof("FAILURE: %s", why); + curl_global_cleanup(); restoreRollbacks(); freeManifest(); + infof("Updater ending (in failure), %s", timestamp()); exit(1); } @@ -628,22 +646,6 @@ static void deleteRollbacks(void) } } -static const char *timestamp(void) -{ - time_t t = time(NULL); - char *retval = asctime(localtime(&t)); - if (retval) { - char *ptr; - for (ptr = retval; *ptr; ptr++) { - if ((*ptr == '\r') || (*ptr == '\n')) { - *ptr = '\0'; - break; - } - } - } - return retval ? retval : "[date unknown]"; -} - static void chdirToBasePath(const char *argv0) { const char *fname = justFilename(argv0); From 973e0a7e9ce72bf9434aef0f6b2d31b5d12696af Mon Sep 17 00:00:00 2001 From: Zack Middleton Date: Tue, 30 May 2017 17:57:49 -0500 Subject: [PATCH 019/240] Refresh master server address cache every 24 hours Resolve master server addresses every 24 hours instead of keeping result forever. Don't clear sv_master[1-5] cvar if the address fails to resolve; it might work later. --- code/server/server.h | 6 ++++-- code/server/sv_main.c | 22 +++++++++------------- 2 files changed, 13 insertions(+), 15 deletions(-) diff --git a/code/server/server.h b/code/server/server.h index 39d6f4d30c..7ffe66ff98 100644 --- a/code/server/server.h +++ b/code/server/server.h @@ -241,8 +241,10 @@ typedef struct { int nextHeartbeatTime; challenge_t challenges[MAX_CHALLENGES]; // to prevent invalid IPs from connecting netadr_t redirectAddress; // for rcon return messages - - netadr_t authorizeAddress; // for rcon return messages +#ifndef STANDALONE + netadr_t authorizeAddress; // authorize server address +#endif + int masterResolveTime[MAX_MASTER_SERVERS]; // next svs.time that server should do dns lookup for master server } serverStatic_t; #define SERVER_MAXBANS 1024 diff --git a/code/server/sv_main.c b/code/server/sv_main.c index 993910feea..0dd8ad47f4 100644 --- a/code/server/sv_main.c +++ b/code/server/sv_main.c @@ -236,6 +236,7 @@ but not on every player enter or exit. ================ */ #define HEARTBEAT_MSEC 300*1000 +#define MASTERDNS_MSEC 24*60*60*1000 void SV_MasterHeartbeat(const char *message) { static netadr_t adr[MAX_MASTER_SERVERS][2]; // [2] for v4 and v6 address for the same address string. @@ -264,12 +265,12 @@ void SV_MasterHeartbeat(const char *message) if(!sv_master[i]->string[0]) continue; - // see if we haven't already resolved the name - // resolving usually causes hitches on win95, so only - // do it when needed - if(sv_master[i]->modified || (adr[i][0].type == NA_BAD && adr[i][1].type == NA_BAD)) + // see if we haven't already resolved the name or if it's been over 24 hours + // resolving usually causes hitches on win95, so only do it when needed + if (sv_master[i]->modified || svs.time > svs.masterResolveTime[i]) { sv_master[i]->modified = qfalse; + svs.masterResolveTime[i] = svs.time + MASTERDNS_MSEC; if(netenabled & NET_ENABLEV4) { @@ -304,16 +305,11 @@ void SV_MasterHeartbeat(const char *message) else Com_Printf( "%s has no IPv6 address.\n", sv_master[i]->string); } + } - if(adr[i][0].type == NA_BAD && adr[i][1].type == NA_BAD) - { - // if the address failed to resolve, clear it - // so we don't take repeated dns hits - Com_Printf("Couldn't resolve address: %s\n", sv_master[i]->string); - Cvar_Set(sv_master[i]->name, ""); - sv_master[i]->modified = qfalse; - continue; - } + if(adr[i][0].type == NA_BAD && adr[i][1].type == NA_BAD) + { + continue; } From f518f75149198ed45c1124f646e1351910279d21 Mon Sep 17 00:00:00 2001 From: "Ryan C. Gordon" Date: Tue, 30 May 2017 20:15:59 -0400 Subject: [PATCH 020/240] Don't link directly to libcurl. Lots of Linux distros have different names (libcurl-gnutls.so vs etc), and version the symbols (curl_global_init@@CURL_LIBSSL_3), so it's more compatible to just dlsym the basic entry points we need and just demand that libcurl is installed at all. Alternately: we'll use our own libcurl build, but we'll probably have to dump SSL support to make this sane to do. --- Makefile | 2 +- code/autoupdater/autoupdater.c | 59 +++++++++++++++++++++++++++++++++- 2 files changed, 59 insertions(+), 2 deletions(-) diff --git a/Makefile b/Makefile index 99f2ffe43b..73db3f730e 100644 --- a/Makefile +++ b/Makefile @@ -1588,7 +1588,7 @@ $(B)/autoupdater/%.o: $(AUTOUPDATERSRCDIR)/%.c $(B)/$(AUTOUPDATER_BIN): $(Q3AUTOUPDATEROBJ) $(echo_cmd) "AUTOUPDATER_LD $@" - $(Q)$(CC) $(LDFLAGS) -o $@ $(Q3AUTOUPDATEROBJ) $(CURL_LIBS) + $(Q)$(CC) $(LDFLAGS) -o $@ $(Q3AUTOUPDATEROBJ) $(LIBS) ############################################################################# diff --git a/code/autoupdater/autoupdater.c b/code/autoupdater/autoupdater.c index f2e0535760..cffbfb528b 100644 --- a/code/autoupdater/autoupdater.c +++ b/code/autoupdater/autoupdater.c @@ -151,8 +151,61 @@ static void restoreRollbacks(void) } } - static void die(const char *why) NEVER_RETURNS; + + +#ifndef _WIN32 /* hooray for Unix linker hostility! */ +#undef curl_easy_setopt +#include +typedef void (*CURLFN_curl_easy_cleanup)(CURL *curl); +typedef CURL *(*CURLFN_curl_easy_init)(void); +typedef CURLcode (*CURLFN_curl_easy_setopt)(CURL *curl, CURLoption option, ...); +typedef CURLcode (*CURLFN_curl_easy_perform)(CURL *curl); +typedef CURLcode (*CURLFN_curl_global_init)(long flags); +typedef void (*CURLFN_curl_global_cleanup)(void); + +static CURLFN_curl_easy_cleanup CURL_curl_easy_cleanup; +static CURLFN_curl_easy_init CURL_curl_easy_init; +static CURLFN_curl_easy_setopt CURL_curl_easy_setopt; +static CURLFN_curl_easy_perform CURL_curl_easy_perform; +static CURLFN_curl_global_init CURL_curl_global_init; +static CURLFN_curl_global_cleanup CURL_curl_global_cleanup; + +static void load_libcurl(void) +{ + #ifdef __APPLE__ + const char *libname = "libcurl.4.dylib"; + #else + const char *libname = "libcurl.so.4"; + #endif + + void *handle = dlopen(libname, RTLD_NOW | RTLD_GLOBAL); + if (!handle) { + infof("dlopen(\"%s\") failed: %s", libname, dlerror()); + die("Failed to load libcurl library"); + } + #define LOADCURLSYM(fn) \ + if ((CURL_##fn = (CURLFN_##fn) dlsym(handle, #fn)) == NULL) { \ + die("Failed to load libcurl symbol '" #fn "'"); \ + } + + LOADCURLSYM(curl_easy_cleanup); + LOADCURLSYM(curl_easy_init); + LOADCURLSYM(curl_easy_setopt); + LOADCURLSYM(curl_easy_perform); + LOADCURLSYM(curl_global_init); + LOADCURLSYM(curl_global_cleanup); +} + +#define curl_easy_cleanup CURL_curl_easy_cleanup +#define curl_easy_init CURL_curl_easy_init +#define curl_easy_setopt CURL_curl_easy_setopt +#define curl_easy_perform CURL_curl_easy_perform +#define curl_global_init CURL_curl_global_init +#define curl_global_cleanup CURL_curl_global_cleanup +#endif + + static void die(const char *why) { infof("FAILURE: %s", why); @@ -696,6 +749,10 @@ int main(int argc, char **argv) upgradeSelfAndRestart(argv[0]); } + #ifndef _WIN32 + load_libcurl(); + #endif + if (curl_global_init(CURL_GLOBAL_DEFAULT) != CURLE_OK) { die("curl_global_init() failed!"); } From 67b0cccc75a6eb920f3db4977ec9fb5779f297c3 Mon Sep 17 00:00:00 2001 From: "Ryan C. Gordon" Date: Tue, 30 May 2017 20:20:18 -0400 Subject: [PATCH 021/240] Don't fclose(NULL) if a file doesn't exist. --- code/autoupdater/autoupdater.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/code/autoupdater/autoupdater.c b/code/autoupdater/autoupdater.c index cffbfb528b..5cd053bfa6 100644 --- a/code/autoupdater/autoupdater.c +++ b/code/autoupdater/autoupdater.c @@ -651,10 +651,10 @@ static void applyUpdates(void) } io = fopen(item->fname, "rb"); - fclose(io); if (io != NULL) { static int rollbackIndex = 0; char rollbackPath[64]; + fclose(io); item->rollback = ++rollbackIndex; snprintf(rollbackPath, sizeof (rollbackPath), "updates/rollbacks/%d", rollbackIndex); infof("Moving file for rollback: '%s' -> '%s'", item->fname, rollbackPath); From 02b116aae049349ee343a6fcc4a0b0d1a0911575 Mon Sep 17 00:00:00 2001 From: "Ryan C. Gordon" Date: Wed, 31 May 2017 01:02:26 -0400 Subject: [PATCH 022/240] Initial Windows autoupdater support: the ioq3 internal bits. This is just the piece that will launch the autoupdater; the autoupdater itself will be a separate commit. --- code/sys/sys_main.c | 22 +++++++++++++++++++++- 1 file changed, 21 insertions(+), 1 deletion(-) diff --git a/code/sys/sys_main.c b/code/sys/sys_main.c index e7543044d7..81d5c97718 100644 --- a/code/sys/sys_main.c +++ b/code/sys/sys_main.c @@ -31,9 +31,14 @@ Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA #include #include -#ifndef _WIN32 +#ifdef USE_AUTOUPDATER +#ifdef _WIN32 +#define WIN32_LEAN_AND_MEAN 1 +#include +#else #include #endif +#endif #ifndef DEDICATED #ifdef USE_LOCAL_HEADERS @@ -669,6 +674,20 @@ int main( int argc, char **argv ) #error The build system should have defined AUTOUPDATER_BIN #endif + #ifdef _WIN32 + { + /* We don't need the Unix pipe() tapdance here because Windows lets children wait on parent processes. */ + PROCESS_INFORMATION procinfo; + char cmdline[128]; + Com_sprintf(cmdline, sizeof (cmdline), AUTOUPDATER_BIN " --waitpid %u", (unsigned int) GetCurrentProcessId()); + if (CreateProcessA(AUTOUPDATER_BIN, cmdline, NULL, NULL, FALSE, 0, NULL, NULL, NULL, &procinfo)) + { + /* close handles now so child cleans up immediately if nothing to do */ + CloseHandle(procinfo.hProcess); + CloseHandle(procinfo.hThread); + } + } + #else int updater_pipes[2]; if (pipe(updater_pipes) == 0) { @@ -702,6 +721,7 @@ int main( int argc, char **argv ) close(updater_pipes[0]); } } + #endif } #endif From cf5dd87f57a70936cdb92537939a9013a9015ba1 Mon Sep 17 00:00:00 2001 From: "Ryan C. Gordon" Date: Wed, 31 May 2017 01:04:17 -0400 Subject: [PATCH 023/240] Fix tabs vs spaces. --- code/sys/sys_main.c | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/code/sys/sys_main.c b/code/sys/sys_main.c index 81d5c97718..5f1d4fc4b1 100644 --- a/code/sys/sys_main.c +++ b/code/sys/sys_main.c @@ -670,9 +670,9 @@ int main( int argc, char **argv ) #ifdef USE_AUTOUPDATER { - #ifndef AUTOUPDATER_BIN - #error The build system should have defined AUTOUPDATER_BIN - #endif + #ifndef AUTOUPDATER_BIN + #error The build system should have defined AUTOUPDATER_BIN + #endif #ifdef _WIN32 { @@ -707,9 +707,9 @@ int main( int argc, char **argv ) if (ptr) *ptr = '\0'; chdir(argv[0]); - #ifdef __APPLE__ - chdir("../.."); /* put this at base of app bundle so paths make sense later. */ - #endif + #ifdef __APPLE__ + chdir("../.."); /* put this at base of app bundle so paths make sense later. */ + #endif snprintf(pidstr, sizeof (pidstr), "%lld", (long long) getppid()); execl(AUTOUPDATER_BIN, AUTOUPDATER_BIN, "--waitpid", pidstr, NULL); } @@ -721,7 +721,7 @@ int main( int argc, char **argv ) close(updater_pipes[0]); } } - #endif + #endif } #endif From d0da0724e7cc048b8fd6b82e14bb48c76c86ebc2 Mon Sep 17 00:00:00 2001 From: "Ryan C. Gordon" Date: Wed, 31 May 2017 01:21:21 -0400 Subject: [PATCH 024/240] Move the autoupdater launcher into its own public domain source file. So other games can steal this piece if they want. --- Makefile | 2 + code/sys/sys_autoupdater.c | 77 ++++++++++++++++++++++++++++++++++++++ code/sys/sys_main.c | 67 +-------------------------------- 3 files changed, 81 insertions(+), 65 deletions(-) create mode 100644 code/sys/sys_autoupdater.c diff --git a/Makefile b/Makefile index 73db3f730e..964d3a7634 100644 --- a/Makefile +++ b/Makefile @@ -1694,6 +1694,7 @@ Q3OBJ = \ $(B)/client/sdl_snd.o \ \ $(B)/client/con_log.o \ + $(B)/client/sys_autoupdater.o \ $(B)/client/sys_main.o ifdef MINGW @@ -2230,6 +2231,7 @@ Q3DOBJ = \ $(B)/ded/null_snddma.o \ \ $(B)/ded/con_log.o \ + $(B)/ded/sys_autoupdater.o \ $(B)/ded/sys_main.o ifeq ($(ARCH),x86) diff --git a/code/sys/sys_autoupdater.c b/code/sys/sys_autoupdater.c new file mode 100644 index 0000000000..56873d4ef0 --- /dev/null +++ b/code/sys/sys_autoupdater.c @@ -0,0 +1,77 @@ +/* +The code in this file is in the public domain. The rest of ioquake3 +is licensed until the GPLv2. Do not mingle code, please! +*/ + +#ifdef USE_AUTOUPDATER +# ifndef AUTOUPDATER_BIN +# error The build system should have defined AUTOUPDATER_BIN +# endif + +# ifdef _WIN32 +# define WIN32_LEAN_AND_MEAN 1 +# include +# else +# include +# endif + +# include +# include +#endif + +void Sys_LaunchAutoupdater(int argc, char **argv) +{ +#ifdef USE_AUTOUPDATER + #ifdef _WIN32 + { + /* We don't need the Unix pipe() tapdance here because Windows lets children wait on parent processes. */ + PROCESS_INFORMATION procinfo; + char cmdline[128]; + sprintf(cmdline, AUTOUPDATER_BIN " --waitpid %u", (unsigned int) GetCurrentProcessId()); + if (CreateProcessA(AUTOUPDATER_BIN, cmdline, NULL, NULL, FALSE, 0, NULL, NULL, NULL, &procinfo)) + { + /* close handles now so child cleans up immediately if nothing to do */ + CloseHandle(procinfo.hProcess); + CloseHandle(procinfo.hThread); + } + } + #else + int updater_pipes[2]; + if (pipe(updater_pipes) == 0) + { + pid_t pid = fork(); + if (pid == -1) /* failure, oh well. */ + { + close(updater_pipes[0]); + close(updater_pipes[1]); + } + else if (pid == 0) /* child process */ + { + close(updater_pipes[1]); /* don't need write end. */ + if (dup2(updater_pipes[0], 3) != -1) + { + char pidstr[64]; + char *ptr = strrchr(argv[0], '/'); + if (ptr) + *ptr = '\0'; + chdir(argv[0]); + #ifdef __APPLE__ + chdir("../.."); /* put this at base of app bundle so paths make sense later. */ + #endif + snprintf(pidstr, sizeof (pidstr), "%lld", (long long) getppid()); + execl(AUTOUPDATER_BIN, AUTOUPDATER_BIN, "--waitpid", pidstr, NULL); + } + _exit(0); /* oh well. */ + } + else /* parent process */ + { + /* leave the write end open until we terminate so updater can block on it. */ + close(updater_pipes[0]); + } + } + #endif +#endif + + (void) argc; (void) argv; /* possibly unused. Pacify compilers. */ +} + diff --git a/code/sys/sys_main.c b/code/sys/sys_main.c index 5f1d4fc4b1..44d74f92f5 100644 --- a/code/sys/sys_main.c +++ b/code/sys/sys_main.c @@ -31,15 +31,6 @@ Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA #include #include -#ifdef USE_AUTOUPDATER -#ifdef _WIN32 -#define WIN32_LEAN_AND_MEAN 1 -#include -#else -#include -#endif -#endif - #ifndef DEDICATED #ifdef USE_LOCAL_HEADERS # include "SDL.h" @@ -668,62 +659,8 @@ int main( int argc, char **argv ) int i; char commandLine[ MAX_STRING_CHARS ] = { 0 }; -#ifdef USE_AUTOUPDATER -{ - #ifndef AUTOUPDATER_BIN - #error The build system should have defined AUTOUPDATER_BIN - #endif - - #ifdef _WIN32 - { - /* We don't need the Unix pipe() tapdance here because Windows lets children wait on parent processes. */ - PROCESS_INFORMATION procinfo; - char cmdline[128]; - Com_sprintf(cmdline, sizeof (cmdline), AUTOUPDATER_BIN " --waitpid %u", (unsigned int) GetCurrentProcessId()); - if (CreateProcessA(AUTOUPDATER_BIN, cmdline, NULL, NULL, FALSE, 0, NULL, NULL, NULL, &procinfo)) - { - /* close handles now so child cleans up immediately if nothing to do */ - CloseHandle(procinfo.hProcess); - CloseHandle(procinfo.hThread); - } - } - #else - int updater_pipes[2]; - if (pipe(updater_pipes) == 0) - { - pid_t pid = fork(); - if (pid == -1) /* failure, oh well. */ - { - close(updater_pipes[0]); - close(updater_pipes[1]); - } - else if (pid == 0) /* child process */ - { - close(updater_pipes[1]); /* don't need write end. */ - if (dup2(updater_pipes[0], 3) != -1) - { - char pidstr[64]; - char *ptr = strrchr(argv[0], '/'); - if (ptr) - *ptr = '\0'; - chdir(argv[0]); - #ifdef __APPLE__ - chdir("../.."); /* put this at base of app bundle so paths make sense later. */ - #endif - snprintf(pidstr, sizeof (pidstr), "%lld", (long long) getppid()); - execl(AUTOUPDATER_BIN, AUTOUPDATER_BIN, "--waitpid", pidstr, NULL); - } - _exit(0); /* oh well. */ - } - else /* parent process */ - { - /* leave the write end open until we terminate so updater can block on it. */ - close(updater_pipes[0]); - } - } - #endif -} -#endif + extern void Sys_LaunchAutoupdater(int argc, char **argv); + Sys_LaunchAutoupdater(argc, argv); #ifndef DEDICATED // SDL version check From a69020b21724fdd460120ab8d1e5764ce203dfd3 Mon Sep 17 00:00:00 2001 From: "Ryan C. Gordon" Date: Wed, 31 May 2017 03:39:45 -0400 Subject: [PATCH 025/240] Fixed up some types in sha256.* --- code/autoupdater/sha256.c | 16 +++++++--------- code/autoupdater/sha256.h | 24 ++++++++++++++++-------- 2 files changed, 23 insertions(+), 17 deletions(-) diff --git a/code/autoupdater/sha256.c b/code/autoupdater/sha256.c index eb9c5c0733..0861860f4a 100644 --- a/code/autoupdater/sha256.c +++ b/code/autoupdater/sha256.c @@ -29,7 +29,7 @@ #define SIG1(x) (ROTRIGHT(x,17) ^ ROTRIGHT(x,19) ^ ((x) >> 10)) /**************************** VARIABLES *****************************/ -static const WORD k[64] = { +static const uint32 k[64] = { 0x428a2f98,0x71374491,0xb5c0fbcf,0xe9b5dba5,0x3956c25b,0x59f111f1,0x923f82a4,0xab1c5ed5, 0xd807aa98,0x12835b01,0x243185be,0x550c7dc3,0x72be5d74,0x80deb1fe,0x9bdc06a7,0xc19bf174, 0xe49b69c1,0xefbe4786,0x0fc19dc6,0x240ca1cc,0x2de92c6f,0x4a7484aa,0x5cb0a9dc,0x76f988da, @@ -41,9 +41,9 @@ static const WORD k[64] = { }; /*********************** FUNCTION DEFINITIONS ***********************/ -void sha256_transform(SHA256_CTX *ctx, const BYTE data[]) +void sha256_transform(SHA256_CTX *ctx, const uint8 data[]) { - WORD a, b, c, d, e, f, g, h, i, j, t1, t2, m[64]; + uint32 a, b, c, d, e, f, g, h, i, j, t1, t2, m[64]; for (i = 0, j = 0; i < 16; ++i, j += 4) m[i] = (data[j] << 24) | (data[j + 1] << 16) | (data[j + 2] << 8) | (data[j + 3]); @@ -96,9 +96,9 @@ void sha256_init(SHA256_CTX *ctx) ctx->state[7] = 0x5be0cd19; } -void sha256_update(SHA256_CTX *ctx, const BYTE data[], size_t len) +void sha256_update(SHA256_CTX *ctx, const uint8 data[], size_t len) { - WORD i; + size_t i; for (i = 0; i < len; ++i) { ctx->data[ctx->datalen] = data[i]; @@ -111,11 +111,9 @@ void sha256_update(SHA256_CTX *ctx, const BYTE data[], size_t len) } } -void sha256_final(SHA256_CTX *ctx, BYTE hash[]) +void sha256_final(SHA256_CTX *ctx, uint8 hash[]) { - WORD i; - - i = ctx->datalen; + uint32 i = ctx->datalen; // Pad whatever data is left in the buffer. if (ctx->datalen < 56) { diff --git a/code/autoupdater/sha256.h b/code/autoupdater/sha256.h index 7123a30dd4..fc9a09a95e 100644 --- a/code/autoupdater/sha256.h +++ b/code/autoupdater/sha256.h @@ -16,19 +16,27 @@ #define SHA256_BLOCK_SIZE 32 // SHA256 outputs a 32 byte digest /**************************** DATA TYPES ****************************/ -typedef unsigned char BYTE; // 8-bit byte -typedef unsigned int WORD; // 32-bit word, change to "long" for 16-bit machines +#ifdef _MSC_VER +typedef unsigned __int8 uint8; +typedef unsigned __int32 uint32; +typedef unsigned __int64 uint64; +#else +#include +typedef uint8_t uint8; +typedef uint32_t uint32; +typedef uint64_t uint64; +#endif typedef struct { - BYTE data[64]; - WORD datalen; - unsigned long long bitlen; - WORD state[8]; + uint8 data[64]; + uint32 datalen; + uint64 bitlen; + uint32 state[8]; } SHA256_CTX; /*********************** FUNCTION DECLARATIONS **********************/ void sha256_init(SHA256_CTX *ctx); -void sha256_update(SHA256_CTX *ctx, const BYTE data[], size_t len); -void sha256_final(SHA256_CTX *ctx, BYTE hash[]); +void sha256_update(SHA256_CTX *ctx, const uint8 data[], size_t len); +void sha256_final(SHA256_CTX *ctx, uint8 hash[]); #endif // SHA256_H From b6a83a1494031b090897657f8a3349cb35590f71 Mon Sep 17 00:00:00 2001 From: "Ryan C. Gordon" Date: Thu, 1 Jun 2017 13:00:37 -0400 Subject: [PATCH 026/240] ioquake3 calls this arch "x86" and not "i386". --- code/autoupdater/autoupdater.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/code/autoupdater/autoupdater.c b/code/autoupdater/autoupdater.c index 5cd053bfa6..cf1dee5588 100644 --- a/code/autoupdater/autoupdater.c +++ b/code/autoupdater/autoupdater.c @@ -40,7 +40,7 @@ is licensed until the GPLv2. Do not mingle code, please! #endif #ifdef __i386__ -#define AUTOUPDATE_ARCH "i386" +#define AUTOUPDATE_ARCH "x86" #elif defined(__x86_64__) #define AUTOUPDATE_ARCH "x86-64" #else From 8cf088ae276a7baa858e58eff78d6f72e368b8b0 Mon Sep 17 00:00:00 2001 From: "Ryan C. Gordon" Date: Thu, 1 Jun 2017 17:17:25 -0400 Subject: [PATCH 027/240] Fully initialize ManifestItems (rollback, etc, was uninitialized before!). --- code/autoupdater/autoupdater.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/code/autoupdater/autoupdater.c b/code/autoupdater/autoupdater.c index cf1dee5588..2576a719b2 100644 --- a/code/autoupdater/autoupdater.c +++ b/code/autoupdater/autoupdater.c @@ -399,7 +399,7 @@ static void parseManifest(const char *fname) if (!item) { infof("Next manifest item: %s", buf); - item = (ManifestItem *) malloc(sizeof (ManifestItem)); + item = (ManifestItem *) calloc(1, sizeof (ManifestItem)); if (!item) { outOfMemory(); } From f6f2710f94bcb35c5af82405da667fb1592725e1 Mon Sep 17 00:00:00 2001 From: Zack Middleton Date: Thu, 1 Jun 2017 12:45:05 -0500 Subject: [PATCH 028/240] Make server browser default to Internet The default use to be local. In q3_ui you have to press spacebar or wait for scan to time out before you can switch to Internet. --- code/q3_ui/ui_main.c | 2 +- code/ui/ui_main.c | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/code/q3_ui/ui_main.c b/code/q3_ui/ui_main.c index 1dd7e89f71..bd9359a069 100644 --- a/code/q3_ui/ui_main.c +++ b/code/q3_ui/ui_main.c @@ -185,7 +185,7 @@ static cvarTable_t cvarTable[] = { { &ui_spSelection, "ui_spSelection", "", CVAR_ROM }, - { &ui_browserMaster, "ui_browserMaster", "0", CVAR_ARCHIVE }, + { &ui_browserMaster, "ui_browserMaster", "1", CVAR_ARCHIVE }, { &ui_browserGameType, "ui_browserGameType", "0", CVAR_ARCHIVE }, { &ui_browserSortKey, "ui_browserSortKey", "4", CVAR_ARCHIVE }, { &ui_browserShowFull, "ui_browserShowFull", "1", CVAR_ARCHIVE }, diff --git a/code/ui/ui_main.c b/code/ui/ui_main.c index e9e36ea975..8bb35226d2 100644 --- a/code/ui/ui_main.c +++ b/code/ui/ui_main.c @@ -5779,7 +5779,7 @@ static cvarTable_t cvarTable[] = { { &ui_blueteam3, "ui_blueteam3", "0", CVAR_ARCHIVE }, { &ui_blueteam4, "ui_blueteam4", "0", CVAR_ARCHIVE }, { &ui_blueteam5, "ui_blueteam5", "0", CVAR_ARCHIVE }, - { &ui_netSource, "ui_netSource", "0", CVAR_ARCHIVE }, + { &ui_netSource, "ui_netSource", "1", CVAR_ARCHIVE }, { &ui_menuFiles, "ui_menuFiles", "ui/menus.txt", CVAR_ARCHIVE }, { &ui_currentTier, "ui_currentTier", "0", CVAR_ARCHIVE }, { &ui_currentMap, "ui_currentMap", "0", CVAR_ARCHIVE }, From e8f092637c7fd286555e84a2754f9599d30bb42b Mon Sep 17 00:00:00 2001 From: Zack Middleton Date: Thu, 1 Jun 2017 13:35:09 -0500 Subject: [PATCH 029/240] Automatically get initial Internet servers in Team Arena UI Having to manually request the list, with two buttons (get new list, refresh list) is somewhat confusing. Also since it looks like there are no servers, users might not try to figure out how to get the server list. The first time viewing a master server list in Team Arena UI, automatically request a new server list. After that the cache will be available with a timestamp of the last refresh time. I think this will make it easier to understand how the menu works. This may cause unneeded updating of the server cache because the last refresh timestamp is per-fs_game but the server cache is shared by all games. This will only occur once for each game though so it's not a big concern. --- code/ui/ui_main.c | 29 ++++++++++++++++++----------- 1 file changed, 18 insertions(+), 11 deletions(-) diff --git a/code/ui/ui_main.c b/code/ui/ui_main.c index 8bb35226d2..63d90eb6bb 100644 --- a/code/ui/ui_main.c +++ b/code/ui/ui_main.c @@ -114,7 +114,7 @@ static int gamecodetoui[] = {4,2,3,0,5,1,6}; static int uitogamecode[] = {4,6,2,3,1,5,7}; -static void UI_StartServerRefresh(qboolean full); +static void UI_StartServerRefresh(qboolean full, qboolean force); static void UI_StopServerRefresh( void ); static void UI_DoServerRefresh( void ); static void UI_FeederSelection(float feederID, int index); @@ -2503,9 +2503,7 @@ static qboolean UI_NetSource_HandleKey(int flags, float *special, int key) { } UI_BuildServerDisplayList(qtrue); - if (!(ui_netSource.integer >= UIAS_GLOBAL1 && ui_netSource.integer <= UIAS_GLOBAL5)) { - UI_StartServerRefresh(qtrue); - } + UI_StartServerRefresh(qtrue, qfalse); trap_Cvar_SetValue( "ui_netSource", ui_netSource.integer); return qtrue; } @@ -3277,10 +3275,10 @@ static void UI_RunMenuScript(char **args) { } else if (Q_stricmp(name, "resetScores") == 0) { UI_ClearScores(); } else if (Q_stricmp(name, "RefreshServers") == 0) { - UI_StartServerRefresh(qtrue); + UI_StartServerRefresh(qtrue, qtrue); UI_BuildServerDisplayList(qtrue); } else if (Q_stricmp(name, "RefreshFilter") == 0) { - UI_StartServerRefresh(qfalse); + UI_StartServerRefresh(qfalse, qtrue); UI_BuildServerDisplayList(qtrue); } else if (Q_stricmp(name, "RunSPDemo") == 0) { if (uiInfo.demoAvailable) { @@ -3322,9 +3320,8 @@ static void UI_RunMenuScript(char **args) { uiInfo.nextServerStatusRefresh = 0; uiInfo.nextFindPlayerRefresh = 0; } else if (Q_stricmp(name, "UpdateFilter") == 0) { - if (ui_netSource.integer == UIAS_LOCAL) { - UI_StartServerRefresh(qtrue); - } + // UpdateFilter is called when server broser menu is opened and when a favorite server is deleted. + UI_StartServerRefresh(qtrue, qfalse); UI_BuildServerDisplayList(qtrue); UI_FeederSelection(FEEDER_SERVERS, 0); } else if (Q_stricmp(name, "ServerStatus") == 0) { @@ -5949,12 +5946,22 @@ static void UI_DoServerRefresh( void ) UI_StartServerRefresh ================= */ -static void UI_StartServerRefresh(qboolean full) +static void UI_StartServerRefresh(qboolean full, qboolean force) { char *ptr; int lanSource; - qtime_t q; + + // This function is called with force=qfalse when server browser menu opens or net source changes. + // Automatically update local and favorite servers. + // Only update master server list the first time because the server info cache will be available after that. + if ( !force && ( ui_netSource.integer >= UIAS_GLOBAL1 && ui_netSource.integer <= UIAS_GLOBAL5 ) ) { + char *value = UI_Cvar_VariableString( va( "ui_lastServerRefresh_%i", ui_netSource.integer ) ); + if ( value[0] != 0 ) { + return; // should have cached list + } + } + trap_RealTime(&q); trap_Cvar_Set( va("ui_lastServerRefresh_%i", ui_netSource.integer), va("%s-%i, %i at %i:%i", MonthAbbrev[q.tm_mon],q.tm_mday, 1900+q.tm_year,q.tm_hour,q.tm_min)); From 2bbe178bc8bcc7d293748a2821652da7de890318 Mon Sep 17 00:00:00 2001 From: Zack Middleton Date: Thu, 1 Jun 2017 14:36:13 -0500 Subject: [PATCH 030/240] Replace constant value with UIAS_GLOBAL1 --- code/q3_ui/ui_servers2.c | 4 ++-- code/ui/ui_main.c | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/code/q3_ui/ui_servers2.c b/code/q3_ui/ui_servers2.c index c5c4c743ff..d376d363bf 100644 --- a/code/q3_ui/ui_servers2.c +++ b/code/q3_ui/ui_servers2.c @@ -1073,10 +1073,10 @@ static void ArenaServers_StartRefresh( void ) protocol[0] = '\0'; trap_Cvar_VariableStringBuffer( "debug_protocol", protocol, sizeof(protocol) ); if (strlen(protocol)) { - trap_Cmd_ExecuteText( EXEC_APPEND, va( "globalservers %d %s%s\n", g_servertype - 1, protocol, myargs )); + trap_Cmd_ExecuteText( EXEC_APPEND, va( "globalservers %d %s%s\n", g_servertype - UIAS_GLOBAL1, protocol, myargs )); } else { - trap_Cmd_ExecuteText( EXEC_APPEND, va( "globalservers %d %d%s\n", g_servertype - 1, (int)trap_Cvar_VariableValue( "protocol" ), myargs ) ); + trap_Cmd_ExecuteText( EXEC_APPEND, va( "globalservers %d %d%s\n", g_servertype - UIAS_GLOBAL1, (int)trap_Cvar_VariableValue( "protocol" ), myargs ) ); } } } diff --git a/code/ui/ui_main.c b/code/ui/ui_main.c index 63d90eb6bb..21cd6f03b0 100644 --- a/code/ui/ui_main.c +++ b/code/ui/ui_main.c @@ -5993,10 +5993,10 @@ static void UI_StartServerRefresh(qboolean full, qboolean force) ptr = UI_Cvar_VariableString("debug_protocol"); if (strlen(ptr)) { - trap_Cmd_ExecuteText( EXEC_NOW, va( "globalservers %d %s full empty\n", ui_netSource.integer-1, ptr)); + trap_Cmd_ExecuteText( EXEC_NOW, va( "globalservers %d %s full empty\n", ui_netSource.integer - UIAS_GLOBAL1, ptr ) ); } else { - trap_Cmd_ExecuteText( EXEC_NOW, va( "globalservers %d %d full empty\n", ui_netSource.integer-1, (int)trap_Cvar_VariableValue( "protocol" ) ) ); + trap_Cmd_ExecuteText( EXEC_NOW, va( "globalservers %d %d full empty\n", ui_netSource.integer - UIAS_GLOBAL1, (int)trap_Cvar_VariableValue( "protocol" ) ) ); } } } From 0b853a659a2c76023389f88ae40938dd5e9f73e7 Mon Sep 17 00:00:00 2001 From: Zack Middleton Date: Thu, 1 Jun 2017 15:38:56 -0500 Subject: [PATCH 031/240] Make Team Arena server list sub-sort clients by max clients The player column in Team Arena UI lists clients and max clients in format of "clients [maxclients]". When sorting by clients the max clients is ignored which results in player column being disorganized. When servers have the same number of clients, sort based on max clients. Otherwise client sort is sub-sorted based on order of getinfo responses (ping). --- code/client/cl_ui.c | 14 ++++++++++++-- 1 file changed, 12 insertions(+), 2 deletions(-) diff --git a/code/client/cl_ui.c b/code/client/cl_ui.c index f3fca9283c..4d2628e5cb 100644 --- a/code/client/cl_ui.c +++ b/code/client/cl_ui.c @@ -374,6 +374,7 @@ LAN_CompareServers static int LAN_CompareServers( int source, int sortKey, int sortDir, int s1, int s2 ) { int res; serverInfo_t *server1, *server2; + int clients1, clients2; server1 = LAN_GetServerPtr(source, s1); server2 = LAN_GetServerPtr(source, s2); @@ -391,10 +392,19 @@ static int LAN_CompareServers( int source, int sortKey, int sortDir, int s1, int res = Q_stricmp( server1->mapName, server2->mapName ); break; case SORT_CLIENTS: - if (server1->clients < server2->clients) { + // sub sort by max clients + if ( server1->clients == server2->clients ) { + clients1 = server1->maxClients; + clients2 = server2->maxClients; + } else { + clients1 = server1->clients; + clients2 = server2->clients; + } + + if (clients1 < clients2) { res = -1; } - else if (server1->clients > server2->clients) { + else if (clients1 > clients2) { res = 1; } else { From 6b5674e6bb493d09880776df10f81a7890ff72c6 Mon Sep 17 00:00:00 2001 From: Zack Middleton Date: Thu, 1 Jun 2017 15:54:41 -0500 Subject: [PATCH 032/240] Fix filtering favorite servers in Team Arena UI Filter favorite servers based on cached server info and new info instead of only the cached info. If cached server info is filtered out, don't add it to server list but wait for getinfo response before marking server as invisible. --- code/ui/ui_main.c | 26 ++++++++++++++++++-------- 1 file changed, 18 insertions(+), 8 deletions(-) diff --git a/code/ui/ui_main.c b/code/ui/ui_main.c index 21cd6f03b0..d2370f6ca4 100644 --- a/code/ui/ui_main.c +++ b/code/ui/ui_main.c @@ -3781,6 +3781,12 @@ static void UI_BuildServerDisplayList(int force) { // get the ping for this server ping = trap_LAN_GetServerPing(lanSource, i); if (ping > 0 || ui_netSource.integer == UIAS_FAVORITES) { + // Remove favorite servers so they do not appear multiple times + // or appear when the cached server info was not filtered out + // but the new server info is filtered out. + if (ui_netSource.integer == UIAS_FAVORITES) { + UI_RemoveServerFromDisplayList(i); + } trap_LAN_GetServerInfo(lanSource, i, info, MAX_STRING_CHARS); @@ -3789,7 +3795,9 @@ static void UI_BuildServerDisplayList(int force) { if (ui_browserShowEmpty.integer == 0) { if (clients == 0) { - trap_LAN_MarkServerVisible(lanSource, i, qfalse); + if (ping > 0) { + trap_LAN_MarkServerVisible(lanSource, i, qfalse); + } continue; } } @@ -3797,7 +3805,9 @@ static void UI_BuildServerDisplayList(int force) { if (ui_browserShowFull.integer == 0) { maxClients = atoi(Info_ValueForKey(info, "sv_maxclients")); if (clients == maxClients) { - trap_LAN_MarkServerVisible(lanSource, i, qfalse); + if (ping > 0) { + trap_LAN_MarkServerVisible(lanSource, i, qfalse); + } continue; } } @@ -3805,21 +3815,21 @@ static void UI_BuildServerDisplayList(int force) { if (uiInfo.joinGameTypes[ui_joinGameType.integer].gtEnum != -1) { game = atoi(Info_ValueForKey(info, "gametype")); if (game != uiInfo.joinGameTypes[ui_joinGameType.integer].gtEnum) { - trap_LAN_MarkServerVisible(lanSource, i, qfalse); + if (ping > 0) { + trap_LAN_MarkServerVisible(lanSource, i, qfalse); + } continue; } } if (ui_serverFilterType.integer > 0) { if (Q_stricmp(Info_ValueForKey(info, "game"), serverFilters[ui_serverFilterType.integer].basedir) != 0) { - trap_LAN_MarkServerVisible(lanSource, i, qfalse); + if (ping > 0) { + trap_LAN_MarkServerVisible(lanSource, i, qfalse); + } continue; } } - // make sure we never add a favorite server twice - if (ui_netSource.integer == UIAS_FAVORITES) { - UI_RemoveServerFromDisplayList(i); - } // insert the server into the list UI_BinaryServerInsertion(i); // done with this server From 2091a2e2d5f6e88bded029d4d1b93ea2b712f37d Mon Sep 17 00:00:00 2001 From: Zack Middleton Date: Thu, 1 Jun 2017 16:31:17 -0500 Subject: [PATCH 033/240] Fix favorite servers player count message in Team Arena UI The console message "1 servers listed in browser with 2 players." would count clients multiple times when viewing favorite servers. When viewing favorite servers in Team Arena UI, servers are added to list before getting ping response. Each time UI checked pings and inserted server it incremented the player count. --- code/ui/ui_main.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/code/ui/ui_main.c b/code/ui/ui_main.c index d2370f6ca4..578d33dea7 100644 --- a/code/ui/ui_main.c +++ b/code/ui/ui_main.c @@ -3791,7 +3791,6 @@ static void UI_BuildServerDisplayList(int force) { trap_LAN_GetServerInfo(lanSource, i, info, MAX_STRING_CHARS); clients = atoi(Info_ValueForKey(info, "clients")); - uiInfo.serverStatus.numPlayersOnServers += clients; if (ui_browserShowEmpty.integer == 0) { if (clients == 0) { @@ -3835,6 +3834,7 @@ static void UI_BuildServerDisplayList(int force) { // done with this server if (ping > 0) { trap_LAN_MarkServerVisible(lanSource, i, qfalse); + uiInfo.serverStatus.numPlayersOnServers += clients; numinvisible++; } } From 0a19ae0306165ada0fe8e04b897c4869b6209046 Mon Sep 17 00:00:00 2001 From: Zack Middleton Date: Thu, 1 Jun 2017 18:03:22 -0500 Subject: [PATCH 034/240] Fix levelshot displayed in Team Arena server browser Levelshot was not updated when server list was initially loaded or server list was sorted. --- code/ui/ui_main.c | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/code/ui/ui_main.c b/code/ui/ui_main.c index 578d33dea7..4fbe6cb8ca 100644 --- a/code/ui/ui_main.c +++ b/code/ui/ui_main.c @@ -2742,6 +2742,9 @@ void UI_ServersSort(int column, qboolean force) { uiInfo.serverStatus.sortKey = column; qsort( &uiInfo.serverStatus.displayServers[0], uiInfo.serverStatus.numDisplayServers, sizeof(int), UI_ServersQsortCompare); + + // update displayed levelshot + UI_FeederSelection( FEEDER_SERVERS, uiInfo.serverStatus.currentServer ); } /* @@ -3656,6 +3659,11 @@ static void UI_InsertServerIntoDisplayList(int num, int position) { uiInfo.serverStatus.displayServers[i] = uiInfo.serverStatus.displayServers[i-1]; } uiInfo.serverStatus.displayServers[position] = num; + + // update displayed levelshot + if ( position == uiInfo.serverStatus.currentServer ) { + UI_FeederSelection( FEEDER_SERVERS, uiInfo.serverStatus.currentServer ); + } } /* From bd067540f5670185624e100818936654516490a0 Mon Sep 17 00:00:00 2001 From: Zack Middleton Date: Thu, 1 Jun 2017 18:39:23 -0500 Subject: [PATCH 035/240] Fix hitch when opening Team Arena find friend menu Opening the find friend menu in the Team Arena server browser hitches due to trying to resolve blank host names. In UI_BuildFindPlayerList() status requests that are initial or completed state or have timed out get reset. This means it starts with MAX_SERVERSTATUSREQUESTS (16) blank host names. So just ignore them in UI_GetServerStatusInfo(). --- code/ui/ui_main.c | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/code/ui/ui_main.c b/code/ui/ui_main.c index 4fbe6cb8ca..f0649328cd 100644 --- a/code/ui/ui_main.c +++ b/code/ui/ui_main.c @@ -3920,11 +3920,21 @@ static int UI_GetServerStatusInfo( const char *serverAddress, serverStatusInfo_t char *p, *score, *ping, *name; int i, len; + if (info) { + memset(info, 0, sizeof(*info)); + } + + // ignore initial unset addresses + if (serverAddress && *serverAddress == '\0') { + return qfalse; + } + + // reset server status request for this address if (!info) { trap_LAN_ServerStatus( serverAddress, NULL, 0); return qfalse; } - memset(info, 0, sizeof(*info)); + if ( trap_LAN_ServerStatus( serverAddress, info->text, sizeof(info->text)) ) { Q_strncpyz(info->address, serverAddress, sizeof(info->address)); p = info->text; From 82977da9c86555ea7bab06330e91b74108d88edf Mon Sep 17 00:00:00 2001 From: "Ryan C. Gordon" Date: Fri, 2 Jun 2017 00:49:42 -0400 Subject: [PATCH 036/240] Working Windows port of the autoupdater! --- Makefile | 14 +- code/autoupdater/autoupdater.c | 378 +++++++++++++++++++++++++-------- code/sys/sys_autoupdater.c | 9 +- 3 files changed, 304 insertions(+), 97 deletions(-) diff --git a/Makefile b/Makefile index 964d3a7634..7b16040b59 100644 --- a/Makefile +++ b/Makefile @@ -592,6 +592,8 @@ ifdef MINGW endif LIBS= -lws2_32 -lwinmm -lpsapi + AUTOUPDATER_LIBS += -lwininet + # clang 3.4 doesn't support this ifneq ("$(CC)", $(findstring "$(CC)", "clang" "clang++")) CLIENT_LDFLAGS += -mwindows @@ -985,7 +987,12 @@ ifneq ($(BUILD_GAME_QVM),0) endif ifneq ($(BUILD_AUTOUPDATER),0) - AUTOUPDATER_BIN := autoupdater$(FULLBINEXT) + # PLEASE NOTE that if you run an exe on Windows Vista or later + # with "setup", "install", "update" or other related terms, it + # will unconditionally trigger a UAC prompt, and in the case of + # ioq3 calling CreateProcess() on it, it'll just fail immediately. + # So don't call this thing "autoupdater" here! + AUTOUPDATER_BIN := autosyncerator$(FULLBINEXT) TARGETS += $(B)/$(AUTOUPDATER_BIN) endif @@ -1325,6 +1332,9 @@ endif @echo " CLIENT_LIBS:" $(call print_wrapped, $(CLIENT_LIBS)) @echo "" + @echo " AUTOUPDATER_LIBS:" + $(call print_wrapped, $(AUTOUPDATER_LIBS)) + @echo "" @echo " Output:" $(call print_list, $(NAKED_TARGETS)) @echo "" @@ -1588,7 +1598,7 @@ $(B)/autoupdater/%.o: $(AUTOUPDATERSRCDIR)/%.c $(B)/$(AUTOUPDATER_BIN): $(Q3AUTOUPDATEROBJ) $(echo_cmd) "AUTOUPDATER_LD $@" - $(Q)$(CC) $(LDFLAGS) -o $@ $(Q3AUTOUPDATEROBJ) $(LIBS) + $(Q)$(CC) $(LDFLAGS) -o $@ $(Q3AUTOUPDATEROBJ) $(AUTOUPDATER_LIBS) ############################################################################# diff --git a/code/autoupdater/autoupdater.c b/code/autoupdater/autoupdater.c index 2576a719b2..ed631e33c6 100644 --- a/code/autoupdater/autoupdater.c +++ b/code/autoupdater/autoupdater.c @@ -6,16 +6,37 @@ is licensed until the GPLv2. Do not mingle code, please! #include #include #include -#include #include +#ifdef _MSC_VER +typedef __int64 int64_t; +#else +#include +#endif + +#include #include #include -#include +#ifdef _WIN32 +#define WIN32_LEAN_AND_MEAN 1 +#include +#include +#include +#define PIDFMT "%u" +#define PIDFMTCAST unsigned int +typedef DWORD PID; +#else +#include #include +typedef pid_t PID; +#define PIDFMT "%llu" +#define PIDFMTCAST unsigned long long +#endif + #include "sha256.h" + #ifndef AUTOUPDATE_USER_AGENT #define AUTOUPDATE_USER_AGENT "ioq3autoupdater/0.1" #endif @@ -35,6 +56,8 @@ is licensed until the GPLv2. Do not mingle code, please! #define AUTOUPDATE_PLATFORM "mac" #elif defined(__linux__) #define AUTOUPDATE_PLATFORM "linux" +#elif defined(_WIN32) +#define AUTOUPDATE_PLATFORM "windows" #else #error Please define your platform. #endif @@ -154,7 +177,146 @@ static void restoreRollbacks(void) static void die(const char *why) NEVER_RETURNS; -#ifndef _WIN32 /* hooray for Unix linker hostility! */ + +#ifdef _WIN32 + +#define chmod(a,b) do {} while (0) +#define makeDir(path) mkdir(path) + +static void windowsWaitForProcessToDie(const DWORD pid) +{ + HANDLE h; + infof("Waiting on process ID #%u", (unsigned int) pid); + h = OpenProcess(SYNCHRONIZE, FALSE, pid); + if (!h) { +// !!! FIXME: what does this return if process is already dead? + die("OpenProcess failed"); + } + if (WaitForSingleObject(h, INFINITE) != WAIT_OBJECT_0) { + die("WaitForSingleObject failed"); + } + CloseHandle(h); +} + +static void launchProcess(const char *exe, ...) +{ + PROCESS_INFORMATION procinfo; + STARTUPINFO startinfo; + va_list ap; + char cmdline[1024]; + char *ptr = cmdline; + size_t totallen = 0; + const char *arg = NULL; + + #define APPENDCMDLINE(str) { \ + const size_t len = strlen(str); \ + totallen += len; \ + if ((totallen + 1) < sizeof (cmdline)) { \ + strcpy(ptr, str); \ + ptr += len; \ + } \ + } + + va_start(ap, exe); + APPENDCMDLINE(exe); + while ((arg = va_arg(ap, const char *)) != NULL) { + APPENDCMDLINE(arg); + } + va_end(ap); + + if (totallen >= sizeof (cmdline)) { + die("command line too long to launch."); + } + + cmdline[totallen] = 0; + + infof("launching process '%s' with cmdline '%s'", exe, cmdline); + + memset(&procinfo, '\0', sizeof (procinfo)); + memset(&startinfo, '\0', sizeof (startinfo)); + startinfo.cb = sizeof (startinfo); + if (CreateProcessA(exe, cmdline, NULL, NULL, FALSE, CREATE_NO_WINDOW, NULL, NULL, &startinfo, &procinfo)) + { + CloseHandle(procinfo.hProcess); + CloseHandle(procinfo.hThread); + exit(0); /* we're done, it's launched. */ + } + + infof("CreateProcess failed: err=%d", (int) GetLastError()); +} + +static HINTERNET hInternet; +static void prepHttpLib(void) +{ + hInternet = InternetOpenA(AUTOUPDATE_USER_AGENT, + INTERNET_OPEN_TYPE_PRECONFIG, + NULL, NULL, 0); + if (!hInternet) { + die("InternetOpen failed"); + } +} + +static void shutdownHttpLib(void) +{ + if (hInternet) { + InternetCloseHandle(hInternet); + hInternet = NULL; + } +} + +static int runHttpDownload(const char *from, FILE *to) +{ + /* !!! FIXME: some of this could benefit from GetLastError+FormatMessage. */ + int retval = 0; + DWORD httpcode = 0; + DWORD dwordlen = sizeof (DWORD); + DWORD zero = 0; + HINTERNET hUrl = InternetOpenUrlA(hInternet, from, NULL, 0, + INTERNET_FLAG_HYPERLINK | + INTERNET_FLAG_IGNORE_REDIRECT_TO_HTTP | + INTERNET_FLAG_IGNORE_REDIRECT_TO_HTTPS | + INTERNET_FLAG_NO_CACHE_WRITE | + INTERNET_FLAG_NO_COOKIES | + INTERNET_FLAG_NO_UI | + INTERNET_FLAG_RESYNCHRONIZE | + INTERNET_FLAG_RELOAD | + INTERNET_FLAG_SECURE, 0); + + if (!hUrl) { + infof("InternetOpenUrl failed. err=%d", (int) GetLastError()); + } else if (!HttpQueryInfo(hUrl, HTTP_QUERY_STATUS_CODE | HTTP_QUERY_FLAG_NUMBER, &httpcode, &dwordlen, &zero)) { + infof("HttpQueryInfo failed. err=%d", (int) GetLastError()); + } else if (httpcode != 200) { + infof("HTTP request failed with response code %d", (int) httpcode); + } else { + while (1) { + DWORD br = 0; + BYTE buf[1024 * 64]; + if (!InternetReadFile(hUrl, buf, sizeof (buf), &br)) { + infof("InternetReadFile failed. err=%d", (int) GetLastError()); + break; + } else if (br == 0) { + retval = 1; + break; /* done! */ + } else { + if (fwrite(buf, br, 1, to) != 1) { + info("fwrite failed"); + break; + } + } + } + } + + InternetCloseHandle(hUrl); + return retval; +} + +#else /* Everything that isn't Windows. */ + +#define launchProcess execl +#define makeDir(path) mkdir(path, 0777) + +/* hooray for Unix linker hostility! */ #undef curl_easy_setopt #include typedef void (*CURLFN_curl_easy_cleanup)(CURL *curl); @@ -171,7 +333,7 @@ static CURLFN_curl_easy_perform CURL_curl_easy_perform; static CURLFN_curl_global_init CURL_curl_global_init; static CURLFN_curl_global_cleanup CURL_curl_global_cleanup; -static void load_libcurl(void) +static void prepHttpLib(void) { #ifdef __APPLE__ const char *libname = "libcurl.4.dylib"; @@ -195,21 +357,69 @@ static void load_libcurl(void) LOADCURLSYM(curl_easy_perform); LOADCURLSYM(curl_global_init); LOADCURLSYM(curl_global_cleanup); + + #define curl_easy_cleanup CURL_curl_easy_cleanup + #define curl_easy_init CURL_curl_easy_init + #define curl_easy_setopt CURL_curl_easy_setopt + #define curl_easy_perform CURL_curl_easy_perform + #define curl_global_init CURL_curl_global_init + #define curl_global_cleanup CURL_curl_global_cleanup + + if (curl_global_init(CURL_GLOBAL_DEFAULT) != CURLE_OK) { + die("curl_global_init() failed!"); + } +} + +static void shutdownHttpLib(void) +{ + if (curl_global_cleanup) { + curl_global_cleanup(); + } } -#define curl_easy_cleanup CURL_curl_easy_cleanup -#define curl_easy_init CURL_curl_easy_init -#define curl_easy_setopt CURL_curl_easy_setopt -#define curl_easy_perform CURL_curl_easy_perform -#define curl_global_init CURL_curl_global_init -#define curl_global_cleanup CURL_curl_global_cleanup +static int runHttpDownload(const char *from, FILE *to) +{ + int retval; + CURL *curl = curl_easy_init(); + if (!curl) { + info("curl_easy_init() failed"); + return 0; + } + + #if 0 + /* !!! FIXME: enable compression? */ + curl_easy_setopt(curl, CURLOPT_ACCEPT_ENCODING, ""); /* enable compression */ + + /* !!! FIXME; hook up proxy support to libcurl */ + curl_easy_setopt(curl, CURLOPT_PROXY, proxyURL); + #endif + + curl_easy_setopt(curl, CURLOPT_VERBOSE, 1L); + curl_easy_setopt(curl, CURLOPT_NOPROGRESS, 1L); + curl_easy_setopt(curl, CURLOPT_STDERR, logfile); + + curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, fwrite); + curl_easy_setopt(curl, CURLOPT_WRITEDATA, to); + + curl_easy_setopt(curl, CURLOPT_URL, from); + + curl_easy_setopt(curl, CURLOPT_FAILONERROR, 1L); + curl_easy_setopt(curl, CURLOPT_FOLLOWLOCATION, 1L); /* allow redirects. */ + curl_easy_setopt(curl, CURLOPT_USERAGENT, AUTOUPDATE_USER_AGENT); + + curl_easy_setopt(curl, CURLOPT_SSL_VERIFYPEER, 1L); /* require valid SSL cert. */ + curl_easy_setopt(curl, CURLOPT_SSL_VERIFYHOST, 2L); /* require SSL cert with same hostname as we connected to. */ + + retval = (curl_easy_perform(curl) == CURLE_OK); + curl_easy_cleanup(curl); + return retval; +} #endif static void die(const char *why) { infof("FAILURE: %s", why); - curl_global_cleanup(); restoreRollbacks(); freeManifest(); infof("Updater ending (in failure), %s", timestamp()); @@ -222,12 +432,6 @@ static void outOfMemory(void) die("Out of memory"); } -static void makeDir(const char *dirname) -{ - /* !!! FIXME: we don't care if this fails right now. */ - mkdir(dirname, 0777); -} - static void buildParentDirs(const char *_path) { char *ptr; @@ -267,7 +471,7 @@ static void parseArgv(int argc, char **argv) for (i = 1; i < argc; i += 2) { if (strcmp(argv[i], "--waitpid") == 0) { options.waitforprocess = atoll(argv[i + 1]); - infof("We will wait for process %lld if necessary", (long long) options.waitforprocess); + infof("We will wait for process " PIDFMT " if necessary", (PIDFMTCAST) options.waitforprocess); } else if (strcmp(argv[i], "--updateself") == 0) { options.updateself = argv[i + 1]; infof("We are updating ourself ('%s')", options.updateself); @@ -275,57 +479,17 @@ static void parseArgv(int argc, char **argv) } } -static CURL *prepCurl(const char *url, FILE *outfile) +static void downloadURL(const char *from, const char *to) { - char *fullurl; - const size_t len = strlen(AUTOUPDATE_URL) + strlen(url) + 1; - CURL *curl = curl_easy_init(); - if (!curl) { - die("curl_easy_init() failed"); - } - - fullurl = (char *) alloca(len); + FILE *io = NULL; + const size_t len = strlen(AUTOUPDATE_URL) + strlen(from) + 1; + char *fullurl = (char *) alloca(len); if (!fullurl) { outOfMemory(); } + snprintf(fullurl, len, "%s%s", AUTOUPDATE_URL, from); - snprintf(fullurl, len, "%s%s", AUTOUPDATE_URL, url); - - infof("Downloading from '%s'", fullurl); - - #if 0 - /* !!! FIXME: enable compression? */ - curl_easy_setopt(curl, CURLOPT_ACCEPT_ENCODING, ""); /* enable compression */ - - /* !!! FIXME; hook up proxy support to libcurl */ - curl_easy_setopt(curl, CURLOPT_PROXY, proxyURL); - #endif - - curl_easy_setopt(curl, CURLOPT_VERBOSE, 1L); - curl_easy_setopt(curl, CURLOPT_NOPROGRESS, 1L); - curl_easy_setopt(curl, CURLOPT_STDERR, logfile); - - curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, fwrite); - curl_easy_setopt(curl, CURLOPT_WRITEDATA, outfile); - - curl_easy_setopt(curl, CURLOPT_URL, fullurl); - - curl_easy_setopt(curl, CURLOPT_FAILONERROR, 1L); - curl_easy_setopt(curl, CURLOPT_FOLLOWLOCATION, 1L); /* allow redirects. */ - curl_easy_setopt(curl, CURLOPT_USERAGENT, AUTOUPDATE_USER_AGENT); - - curl_easy_setopt(curl, CURLOPT_SSL_VERIFYPEER, 1L); /* require valid SSL cert. */ - curl_easy_setopt(curl, CURLOPT_SSL_VERIFYHOST, 2L); /* require SSL cert with same hostname as we connected to. */ - - return curl; -} - -static void downloadURL(const char *from, const char *to) -{ - FILE *io; - CURL *curl; - - infof("Preparing to download to '%s'", to); + infof("Downloading from '%s' to '%s'", fullurl, to); buildParentDirs(to); io = fopen(to, "wb"); @@ -333,12 +497,11 @@ static void downloadURL(const char *from, const char *to) die("Failed to open output file"); } - curl = prepCurl(from, io); - if (curl_easy_perform(curl) != CURLE_OK) { + if (!runHttpDownload(fullurl, io)) { + fclose(io); remove(to); die("Download failed"); } - curl_easy_cleanup(curl); if (fclose(io) == EOF) { die("Can't flush file on close. i/o error? Disk full?"); @@ -361,7 +524,7 @@ static int hexcvt(const int ch) return 0; } -static void convertSha256(char *str, BYTE *sha256) +static void convertSha256(char *str, uint8 *sha256) { int i; for (i = 0; i < 32; i++) { @@ -446,6 +609,32 @@ static void upgradeSelfAndRestart(const char *argv0) FILE *in = NULL; FILE *out = NULL; + /* unix replaces the process with execl(), but Windows needs to wait for the parent to terminate. */ + #ifdef _WIN32 + DWORD ppid = 0; + HANDLE h = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0); + if (h) { + const DWORD myPid = GetCurrentProcessId(); + PROCESSENTRY32 pe; + memset(&pe, '\0', sizeof (pe)); + pe.dwSize = sizeof(PROCESSENTRY32); + + if (Process32First(h, &pe)) { + do { + if (pe.th32ProcessID == myPid) { + ppid = pe.th32ParentProcessID; + break; + } + } while (Process32Next(h, &pe)); + } + CloseHandle(h); + } + if (!ppid) { + die("Can't determine parent process id"); + } + windowsWaitForProcessToDie(ppid); + #endif + in = fopen(argv0, "rb"); if (!in) { die("Can't open self for input while upgrading updater"); @@ -491,10 +680,10 @@ static void upgradeSelfAndRestart(const char *argv0) if (options.waitforprocess) { char pidstr[64]; - snprintf(pidstr, sizeof (pidstr), "%lld", (long long) options.waitforprocess); - execl(options.updateself, options.updateself, "--waitpid", pidstr, NULL); + snprintf(pidstr, sizeof (pidstr), PIDFMT, (PIDFMTCAST) options.waitforprocess); + launchProcess(options.updateself, options.updateself, "--waitpid", pidstr, NULL); } else { - execl(options.updateself, options.updateself, NULL); + launchProcess(options.updateself, options.updateself, NULL); } die("Failed to relaunch upgraded updater"); } @@ -508,7 +697,7 @@ static const char *justFilename(const char *path) static void hashFile(const char *fname, unsigned char *sha256) { SHA256_CTX sha256ctx; - BYTE buf[512]; + uint8 buf[512]; FILE *io; io = fopen(fname, "rb"); @@ -611,10 +800,10 @@ static void maybeUpdateSelf(const char *argv0) if (options.waitforprocess) { char pidstr[64]; - snprintf(pidstr, sizeof (pidstr), "%lld", (long long) options.waitforprocess); - execl(to, to, "--updateself", argv0, "--waitpid", pidstr, NULL); + snprintf(pidstr, sizeof (pidstr), PIDFMT, (PIDFMTCAST) options.waitforprocess); + launchProcess(to, to, "--updateself", argv0, "--waitpid", pidstr, NULL); } else { - execl(to, to, "--updateself", argv0, NULL); + launchProcess(to, to, "--updateself", argv0, NULL); } die("Failed to initially launch upgraded updater"); } @@ -668,19 +857,26 @@ static void applyUpdates(void) } } + static void waitToApplyUpdates(void) { if (options.waitforprocess) { - /* ioquake3 opens a pipe on fd 3, and then forgets about it. We block - on a read to that pipe here. When the game process quits (and the - OS forcibly closes the pipe), we will unblock. Then we can loop on - kill() until the process is truly gone. */ - int x = 0; - infof("Waiting for pid %lld to die...", (long long) options.waitforprocess); - read(3, &x, sizeof (x)); - info("Pipe has closed, waiting for process to fully go away now."); - while (kill(options.waitforprocess, 0) == 0) { - usleep(100000); + infof("Waiting for pid " PIDFMT " to die...", (PIDFMTCAST) options.waitforprocess); + { + #ifdef _WIN32 + windowsWaitForProcessToDie(options.waitforprocess); + #else + /* The parent opens a pipe on fd 3, and then forgets about it. We block + on a read to that pipe here. When the game process quits (and the + OS forcibly closes the pipe), we will unblock. Then we can loop on + kill() until the process is truly gone. */ + int x = 0; + read(3, &x, sizeof (x)); + info("Pipe has closed, waiting for process to fully go away now."); + while (kill(options.waitforprocess, 0) == 0) { + usleep(100000); + } + #endif } info("pid is gone, continuing"); } @@ -725,7 +921,9 @@ static void chdirToBasePath(const char *argv0) int main(int argc, char **argv) { + #ifndef _WIN32 signal(SIGPIPE, SIG_IGN); /* don't trigger signal when fd3 closes */ + #endif logfile = stdout; chdirToBasePath(argv[0]); @@ -749,13 +947,7 @@ int main(int argc, char **argv) upgradeSelfAndRestart(argv[0]); } - #ifndef _WIN32 - load_libcurl(); - #endif - - if (curl_global_init(CURL_GLOBAL_DEFAULT) != CURLE_OK) { - die("curl_global_init() failed!"); - } + prepHttpLib(); downloadManifest(); /* see if we need an update at all. */ @@ -771,7 +963,7 @@ int main(int argc, char **argv) } freeManifest(); - curl_global_cleanup(); + shutdownHttpLib(); infof("Updater ending, %s", timestamp()); diff --git a/code/sys/sys_autoupdater.c b/code/sys/sys_autoupdater.c index 56873d4ef0..2982f8550f 100644 --- a/code/sys/sys_autoupdater.c +++ b/code/sys/sys_autoupdater.c @@ -26,9 +26,14 @@ void Sys_LaunchAutoupdater(int argc, char **argv) { /* We don't need the Unix pipe() tapdance here because Windows lets children wait on parent processes. */ PROCESS_INFORMATION procinfo; + STARTUPINFO startinfo; char cmdline[128]; - sprintf(cmdline, AUTOUPDATER_BIN " --waitpid %u", (unsigned int) GetCurrentProcessId()); - if (CreateProcessA(AUTOUPDATER_BIN, cmdline, NULL, NULL, FALSE, 0, NULL, NULL, NULL, &procinfo)) + memset(&procinfo, '\0', sizeof (procinfo)); + memset(&startinfo, '\0', sizeof (startinfo)); + startinfo.cb = sizeof (startinfo); + sprintf(cmdline, "" AUTOUPDATER_BIN " --waitpid %u", (unsigned int) GetCurrentProcessId()); + + if (CreateProcessA(AUTOUPDATER_BIN, cmdline, NULL, NULL, FALSE, CREATE_NO_WINDOW, NULL, NULL, &startinfo, &procinfo)) { /* close handles now so child cleans up immediately if nothing to do */ CloseHandle(procinfo.hProcess); From b5c54ec0190aaf47c6667286d3efeecfdbe13333 Mon Sep 17 00:00:00 2001 From: "Ryan C. Gordon" Date: Fri, 2 Jun 2017 01:31:45 -0400 Subject: [PATCH 037/240] Use stdint.h on Visual C if >= Visual Studio 2010. --- code/autoupdater/autoupdater.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/code/autoupdater/autoupdater.c b/code/autoupdater/autoupdater.c index ed631e33c6..3a2d17b004 100644 --- a/code/autoupdater/autoupdater.c +++ b/code/autoupdater/autoupdater.c @@ -8,7 +8,7 @@ is licensed until the GPLv2. Do not mingle code, please! #include #include -#ifdef _MSC_VER +#if defined(_MSC_VER) && (_MSC_VER < 1600) typedef __int64 int64_t; #else #include From cd4aa2d9a9cffc5e600bbccc152397c8d5f744cc Mon Sep 17 00:00:00 2001 From: "Ryan C. Gordon" Date: Fri, 2 Jun 2017 01:32:33 -0400 Subject: [PATCH 038/240] Don't fail if the game process went away before we were ready to wait for it. --- code/autoupdater/autoupdater.c | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/code/autoupdater/autoupdater.c b/code/autoupdater/autoupdater.c index 3a2d17b004..7c0cebaaed 100644 --- a/code/autoupdater/autoupdater.c +++ b/code/autoupdater/autoupdater.c @@ -189,7 +189,12 @@ static void windowsWaitForProcessToDie(const DWORD pid) infof("Waiting on process ID #%u", (unsigned int) pid); h = OpenProcess(SYNCHRONIZE, FALSE, pid); if (!h) { -// !!! FIXME: what does this return if process is already dead? + const DWORD err = GetLastError(); + if (err == ERROR_INVALID_PARAMETER) { + info("No such process; probably already dead. Carry on."); + return; /* process is (probably) already gone. */ + } + infof("OpenProcess failed. err=%d", (unsigned int) err); die("OpenProcess failed"); } if (WaitForSingleObject(h, INFINITE) != WAIT_OBJECT_0) { From 063875e89a3d1ab4f98e0f0e1d47a7f8173b7f07 Mon Sep 17 00:00:00 2001 From: "Ryan C. Gordon" Date: Fri, 2 Jun 2017 01:39:03 -0400 Subject: [PATCH 039/240] Fixed linking on things that need -ldl, and compiler warnings. --- Makefile | 4 ++++ code/sys/sys_autoupdater.c | 8 ++++++-- 2 files changed, 10 insertions(+), 2 deletions(-) diff --git a/Makefile b/Makefile index 7b16040b59..322fc15db7 100644 --- a/Makefile +++ b/Makefile @@ -376,6 +376,7 @@ ifneq (,$(findstring "$(PLATFORM)", "linux" "gnu_kfreebsd" "kfreebsd-gnu" "gnu") THREAD_LIBS=-lpthread LIBS=-ldl -lm + AUTOUPDATER_LIBS += -ldl CLIENT_LIBS=$(SDL_LIBS) RENDERER_LIBS = $(SDL_LIBS) -lGL @@ -834,6 +835,8 @@ ifeq ($(PLATFORM),irix64) SHLIBLDFLAGS=-shared LIBS=-ldl -lm -lgen + AUTOUPDATER_LIBS += -ldl + # FIXME: The X libraries probably aren't necessary? CLIENT_LIBS=-L/usr/X11/$(LIB) $(SDL_LIBS) \ -lX11 -lXext -lm @@ -888,6 +891,7 @@ ifeq ($(PLATFORM),sunos) THREAD_LIBS=-lpthread LIBS=-lsocket -lnsl -ldl -lm + AUTOUPDATER_LIBS += -ldl BOTCFLAGS=-O0 diff --git a/code/sys/sys_autoupdater.c b/code/sys/sys_autoupdater.c index 2982f8550f..1ed908d870 100644 --- a/code/sys/sys_autoupdater.c +++ b/code/sys/sys_autoupdater.c @@ -59,9 +59,13 @@ void Sys_LaunchAutoupdater(int argc, char **argv) char *ptr = strrchr(argv[0], '/'); if (ptr) *ptr = '\0'; - chdir(argv[0]); + if (chdir(argv[0]) == -1) { + _exit(1); /* oh well. */ + } #ifdef __APPLE__ - chdir("../.."); /* put this at base of app bundle so paths make sense later. */ + if (chdir("../..") == -1) { /* put this at base of app bundle so paths make sense later. */ + _exit(1); /* oh well. */ + } #endif snprintf(pidstr, sizeof (pidstr), "%lld", (long long) getppid()); execl(AUTOUPDATER_BIN, AUTOUPDATER_BIN, "--waitpid", pidstr, NULL); From 0eb497b01bb7cdd503bffab352f95495c74a470c Mon Sep 17 00:00:00 2001 From: "Ryan C. Gordon" Date: Fri, 2 Jun 2017 02:29:09 -0400 Subject: [PATCH 040/240] Disable the autoupdater; enable only if intentional, like for official builds. --- Makefile | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/Makefile b/Makefile index 322fc15db7..1d472de29d 100644 --- a/Makefile +++ b/Makefile @@ -35,8 +35,8 @@ endif ifndef BUILD_RENDERER_OPENGL2 BUILD_RENDERER_OPENGL2= endif -ifndef BUILD_AUTOUPDATER - BUILD_AUTOUPDATER= +ifndef BUILD_AUTOUPDATER # DON'T build unless you mean to! + BUILD_AUTOUPDATER=0 endif ############################################################################# @@ -231,8 +231,8 @@ ifndef USE_YACC USE_YACC=0 endif -ifndef USE_AUTOUPDATER -USE_AUTOUPDATER=1 +ifndef USE_AUTOUPDATER # DON'T include unless you mean to! +USE_AUTOUPDATER=0 endif ifndef DEBUG_CFLAGS From b33551dfa25794d7ffb61af89257c000cbdd6602 Mon Sep 17 00:00:00 2001 From: "Ryan C. Gordon" Date: Fri, 2 Jun 2017 11:28:33 -0400 Subject: [PATCH 041/240] Fixed comment typo: s/until/under --- code/autoupdater/autoupdater.c | 2 +- code/sys/sys_autoupdater.c | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/code/autoupdater/autoupdater.c b/code/autoupdater/autoupdater.c index 7c0cebaaed..eb7937ee2c 100644 --- a/code/autoupdater/autoupdater.c +++ b/code/autoupdater/autoupdater.c @@ -1,6 +1,6 @@ /* The code in this file is in the public domain. The rest of ioquake3 -is licensed until the GPLv2. Do not mingle code, please! +is licensed under the GPLv2. Do not mingle code, please! */ #include diff --git a/code/sys/sys_autoupdater.c b/code/sys/sys_autoupdater.c index 1ed908d870..2f59e07135 100644 --- a/code/sys/sys_autoupdater.c +++ b/code/sys/sys_autoupdater.c @@ -1,6 +1,6 @@ /* The code in this file is in the public domain. The rest of ioquake3 -is licensed until the GPLv2. Do not mingle code, please! +is licensed under the GPLv2. Do not mingle code, please! */ #ifdef USE_AUTOUPDATER From 4506ebd5d7690b8677aaa598c173dcc71f013426 Mon Sep 17 00:00:00 2001 From: Zack Middleton Date: Fri, 2 Jun 2017 21:07:25 -0500 Subject: [PATCH 042/240] Fix joining team when starting local team play server AKA fix joining team in Team Arena single player. Though it also affects starting Q3 skirmish in team dm or ctf modes. --- code/game/g_cmds.c | 17 +++++++++++++++-- 1 file changed, 15 insertions(+), 2 deletions(-) diff --git a/code/game/g_cmds.c b/code/game/g_cmds.c index 29a1ce1dc6..df74ef06b0 100644 --- a/code/game/g_cmds.c +++ b/code/game/g_cmds.c @@ -592,8 +592,8 @@ void SetTeam( gentity_t *ent, char *s ) { // execute the team change // - // if the player was dead leave the body - if ( client->ps.stats[STAT_HEALTH] <= 0 ) { + // if the player was dead leave the body, but only if they're actually in game + if ( client->ps.stats[STAT_HEALTH] <= 0 && client->pers.connected == CON_CONNECTED ) { CopyToBodyQue(ent); } @@ -633,6 +633,11 @@ void SetTeam( gentity_t *ent, char *s ) { // get and distribute relevent paramters ClientUserinfoChanged( clientNum ); + // client hasn't spawned yet, they sent an early team command + if ( client->pers.connected != CON_CONNECTED ) { + return; + } + ClientBegin( clientNum ); } @@ -1659,6 +1664,14 @@ void ClientCommand( int clientNum ) { ent = g_entities + clientNum; if (!ent->client || ent->client->pers.connected != CON_CONNECTED) { + if (ent->client && ent->client->pers.localClient) { + // Handle early team command sent by UI when starting a local + // team play game. + trap_Argv( 0, cmd, sizeof( cmd ) ); + if (Q_stricmp (cmd, "team") == 0) { + Cmd_Team_f (ent); + } + } return; // not fully in game yet } From 4227d97958d7ba1915ecdfc8b8039472c26145d2 Mon Sep 17 00:00:00 2001 From: Zack Middleton Date: Fri, 2 Jun 2017 21:24:57 -0500 Subject: [PATCH 043/240] Make Team Arena win logic handle more game types/blue team The Team Arena menu uses red team for single player but q3_ui (and mods could) use blue. Also handle all the game types, not just the ones used by Team Arena. Fixes FFA and Team DM. --- code/game/g_main.c | 14 +++++++++++--- 1 file changed, 11 insertions(+), 3 deletions(-) diff --git a/code/game/g_main.c b/code/game/g_main.c index 6d9d9fae3b..af88d3498b 100644 --- a/code/game/g_main.c +++ b/code/game/g_main.c @@ -1145,6 +1145,7 @@ void LogExit( const char *string ) { gclient_t *cl; #ifdef MISSIONPACK qboolean won = qtrue; + team_t team = TEAM_RED; #endif G_LogPrintf( "Exit: %s\n", string ); @@ -1181,7 +1182,10 @@ void LogExit( const char *string ) { G_LogPrintf( "score: %i ping: %i client: %i %s\n", cl->ps.persistant[PERS_SCORE], ping, level.sortedClients[i], cl->pers.netname ); #ifdef MISSIONPACK - if (g_singlePlayer.integer && g_gametype.integer == GT_TOURNAMENT) { + if (g_singlePlayer.integer && !(g_entities[cl - level.clients].r.svFlags & SVF_BOT)) { + team = cl->sess.sessionTeam; + } + if (g_singlePlayer.integer && g_gametype.integer < GT_TEAM) { if (g_entities[cl - level.clients].r.svFlags & SVF_BOT && cl->ps.persistant[PERS_RANK] == 0) { won = qfalse; } @@ -1192,8 +1196,12 @@ void LogExit( const char *string ) { #ifdef MISSIONPACK if (g_singlePlayer.integer) { - if (g_gametype.integer >= GT_CTF) { - won = level.teamScores[TEAM_RED] > level.teamScores[TEAM_BLUE]; + if (g_gametype.integer >= GT_TEAM) { + if (team == TEAM_BLUE) { + won = level.teamScores[TEAM_BLUE] > level.teamScores[TEAM_RED]; + } else { + won = level.teamScores[TEAM_RED] > level.teamScores[TEAM_BLUE]; + } } trap_SendConsoleCommand( EXEC_APPEND, (won) ? "spWin\n" : "spLose\n" ); } From 4006358492b800db0602ed0eca600166caec22e4 Mon Sep 17 00:00:00 2001 From: Zack Middleton Date: Fri, 2 Jun 2017 21:31:38 -0500 Subject: [PATCH 044/240] Fix spawn/freed entity logic (specifically harvester skulls) - Actually use the second 'force' pass in G_Spawn when out of available slots. - Make G_EntitiesFree return qtrue if we can open a new slot. (Only used when spawning Harvester skulls.) Fixes not spawning Harvester skulls when there are no 'open freed slots', but we have other slots available to open. --- code/game/g_utils.c | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/code/game/g_utils.c b/code/game/g_utils.c index 417815561d..bcc1200d65 100644 --- a/code/game/g_utils.c +++ b/code/game/g_utils.c @@ -411,11 +411,11 @@ gentity_t *G_Spawn( void ) { G_InitGentity( e ); return e; } - if ( i != MAX_GENTITIES ) { + if ( level.num_entities < ENTITYNUM_MAX_NORMAL ) { break; } } - if ( i == ENTITYNUM_MAX_NORMAL ) { + if ( level.num_entities == ENTITYNUM_MAX_NORMAL ) { for (i = 0; i < MAX_GENTITIES; i++) { G_Printf("%4i: %s\n", i, g_entities[i].classname); } @@ -442,6 +442,11 @@ qboolean G_EntitiesFree( void ) { int i; gentity_t *e; + if ( level.num_entities < ENTITYNUM_MAX_NORMAL ) { + // can open a new slot if needed + return qtrue; + } + e = &g_entities[MAX_CLIENTS]; for ( i = MAX_CLIENTS; i < level.num_entities; i++, e++) { if ( e->inuse ) { From 1066214548d6a154c05332c86610f60f03bd4193 Mon Sep 17 00:00:00 2001 From: Zack Middleton Date: Fri, 2 Jun 2017 21:40:21 -0500 Subject: [PATCH 045/240] Fix "brought in 1 skulls" Harvester message Use correct singular/plural form of skulls for Harvester's brought in skulls message. Reported by Tobias. --- code/game/g_team.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/code/game/g_team.c b/code/game/g_team.c index b3fb2f201c..12d9f6a282 100644 --- a/code/game/g_team.c +++ b/code/game/g_team.c @@ -1280,8 +1280,8 @@ static void ObeliskTouch( gentity_t *self, gentity_t *other, trace_t *trace ) { return; } - PrintMsg(NULL, "%s" S_COLOR_WHITE " brought in %i skull%s.\n", - other->client->pers.netname, tokens, tokens ? "s" : "" ); + PrintMsg(NULL, "%s" S_COLOR_WHITE " brought in %i %s.\n", + other->client->pers.netname, tokens, ( tokens == 1 ) ? "skull" : "skulls" ); AddTeamScore(self->s.pos.trBase, other->client->sess.sessionTeam, tokens); Team_ForceGesture(other->client->sess.sessionTeam); From 082376ed9e82e578d78678c47d6be21826fa767e Mon Sep 17 00:00:00 2001 From: Zack Middleton Date: Fri, 2 Jun 2017 21:52:05 -0500 Subject: [PATCH 046/240] Enable tourney scoreboard in Team Arena "/team score" draws an oversized scoreboard in Q3. In Team Arena it draws nothing. They probably intended to replace it with the new .menu UI. But since it didn't happen, go ahead and use the Q3 tournament scoreboard. --- code/cgame/cg_draw.c | 7 ------- code/cgame/cg_local.h | 2 +- code/cgame/cg_scoreboard.c | 2 +- 3 files changed, 2 insertions(+), 9 deletions(-) diff --git a/code/cgame/cg_draw.c b/code/cgame/cg_draw.c index cb914c95e0..e3122e8c67 100644 --- a/code/cgame/cg_draw.c +++ b/code/cgame/cg_draw.c @@ -2610,13 +2610,6 @@ static void CG_Draw2D(stereoFrame_t stereoFrame) } -static void CG_DrawTourneyScoreboard( void ) { -#ifdef MISSIONPACK -#else - CG_DrawOldTourneyScoreboard(); -#endif -} - /* ===================== CG_DrawActive diff --git a/code/cgame/cg_local.h b/code/cgame/cg_local.h index d78fad72a2..822c257669 100644 --- a/code/cgame/cg_local.h +++ b/code/cgame/cg_local.h @@ -1440,7 +1440,7 @@ void CG_DrawInformation( void ); // cg_scoreboard.c // qboolean CG_DrawOldScoreboard( void ); -void CG_DrawOldTourneyScoreboard( void ); +void CG_DrawTourneyScoreboard( void ); // // cg_consolecmds.c diff --git a/code/cgame/cg_scoreboard.c b/code/cgame/cg_scoreboard.c index 34935fe188..ae4c60813f 100644 --- a/code/cgame/cg_scoreboard.c +++ b/code/cgame/cg_scoreboard.c @@ -449,7 +449,7 @@ CG_DrawTourneyScoreboard Draw the oversize scoreboard for tournements ================= */ -void CG_DrawOldTourneyScoreboard( void ) { +void CG_DrawTourneyScoreboard( void ) { const char *s; vec4_t color; int min, tens, ones; From c14cb70f15039c90f36a0ece95dd574a7ad172d1 Mon Sep 17 00:00:00 2001 From: Zack Middleton Date: Fri, 2 Jun 2017 21:55:40 -0500 Subject: [PATCH 047/240] Draw disconnect icon over lagometer in Team Arena too The blinking disconnect icon is drawn over lagometer in Q3. Team Arena moved the lagometer location. Now let's draw the disconnect icon over lagometer in Team Arena too! --- code/cgame/cg_draw.c | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/code/cgame/cg_draw.c b/code/cgame/cg_draw.c index e3122e8c67..4b6df017df 100644 --- a/code/cgame/cg_draw.c +++ b/code/cgame/cg_draw.c @@ -1606,8 +1606,13 @@ static void CG_DrawDisconnect( void ) { return; } +#ifdef MISSIONPACK + x = 640 - 48; + y = 480 - 144; +#else x = 640 - 48; y = 480 - 48; +#endif CG_DrawPic( x, y, 48, 48, trap_R_RegisterShader("gfx/2d/net.tga" ) ); } From 1aa20487a4b8924bff7343b4a6e8f1a5fa89b837 Mon Sep 17 00:00:00 2001 From: "Ryan C. Gordon" Date: Sat, 3 Jun 2017 12:02:29 -0400 Subject: [PATCH 048/240] autoupdater: Fixed up GCC/clang printf function attribute. --- code/autoupdater/autoupdater.c | 11 +++-------- 1 file changed, 3 insertions(+), 8 deletions(-) diff --git a/code/autoupdater/autoupdater.c b/code/autoupdater/autoupdater.c index eb7937ee2c..0e8392b868 100644 --- a/code/autoupdater/autoupdater.c +++ b/code/autoupdater/autoupdater.c @@ -75,8 +75,10 @@ typedef pid_t PID; #if defined(__GNUC__) || defined(__clang__) #define NEVER_RETURNS __attribute__((noreturn)) +#define PRINTF_FUNC(fmtargnum, dotargnum) __attribute__ (( format( __printf__, fmtargnum, dotargnum ))) #else #define NEVER_RETURNS +#define PRINTF_FUNC(fmtargnum, dotargnum) #endif @@ -134,8 +136,6 @@ static const char *timestamp(void) static FILE *logfile = NULL; -#define SDL_PRINTF_VARARG_FUNC( fmtargnumber ) __attribute__ (( format( __printf__, fmtargnumber, fmtargnumber+1 ))) - static void info(const char *str) { fputs(str, logfile); @@ -143,12 +143,7 @@ static void info(const char *str) fflush(logfile); } -static void infof(const char *fmt, ...) -#if defined(__GNUC__) || defined(__clang__) -__attribute__ (( format( __printf__, 1, 2 ))) -#endif -; - +static void infof(const char *fmt, ...) PRINTF_FUNC(1, 2); static void infof(const char *fmt, ...) { va_list ap; From 66fec1b059c8aaeb6d516065cd74ed57dfd1901b Mon Sep 17 00:00:00 2001 From: Zack Middleton Date: Fri, 2 Jun 2017 22:58:22 -0500 Subject: [PATCH 049/240] Remove unneeded code from OpenGL2's RB_RenderDrawSurfList Make it more similar to OpenGL1. --- code/renderergl2/tr_backend.c | 21 +++------------------ 1 file changed, 3 insertions(+), 18 deletions(-) diff --git a/code/renderergl2/tr_backend.c b/code/renderergl2/tr_backend.c index bc0d5e7401..a5a7fe27e5 100644 --- a/code/renderergl2/tr_backend.c +++ b/code/renderergl2/tr_backend.c @@ -475,9 +475,6 @@ void RB_RenderDrawSurfList( drawSurf_t *drawSurfs, int numDrawSurfs ) { FBO_t* fbo = NULL; qboolean inQuery = qfalse; - float depth[2]; - - // save original time for entity shader offsets originalTime = backEnd.refdef.floatTime; @@ -495,9 +492,6 @@ void RB_RenderDrawSurfList( drawSurf_t *drawSurfs, int numDrawSurfs ) { oldCubemapIndex = -1; oldSort = -1; - depth[0] = 0.f; - depth[1] = 1.f; - backEnd.pc.c_surfaces += numDrawSurfs; for (i = 0, drawSurf = drawSurfs ; i < numDrawSurfs ; i++, drawSurf++) { @@ -538,7 +532,6 @@ void RB_RenderDrawSurfList( drawSurf_t *drawSurfs, int numDrawSurfs ) { // change the modelview matrix if needed // if ( entityNum != oldEntityNum ) { - qboolean sunflare = qfalse; depthRange = isCrosshair = qfalse; if ( entityNum != REFENTITYNUM_WORLD ) { @@ -604,12 +597,8 @@ void RB_RenderDrawSurfList( drawSurf_t *drawSurfs, int numDrawSurfs ) { } } - if(!oldDepthRange) - { - depth[0] = 0; - depth[1] = 0.3f; - qglDepthRange (depth[0], depth[1]); - } + if(!oldDepthRange) + qglDepthRange (0, 0.3); } else { @@ -618,11 +607,7 @@ void RB_RenderDrawSurfList( drawSurf_t *drawSurfs, int numDrawSurfs ) { GL_SetProjectionMatrix( backEnd.viewParms.projectionMatrix ); } - if (!sunflare) - qglDepthRange (0, 1); - - depth[0] = 0; - depth[1] = 1; + qglDepthRange (0, 1); } oldDepthRange = depthRange; From 5592342b1b452f78a6d7dc82c73f9ac2a16a7093 Mon Sep 17 00:00:00 2001 From: Zack Middleton Date: Sat, 3 Jun 2017 11:45:11 -0500 Subject: [PATCH 050/240] Only auto update empty Team Arena internet server cache Previously tested a mod cvar which may be wrong when multiple mods are involved or config is reset. Let's check the server cache's internet server count directly. --- code/ui/ui_main.c | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/code/ui/ui_main.c b/code/ui/ui_main.c index f0649328cd..bb43ec44b8 100644 --- a/code/ui/ui_main.c +++ b/code/ui/ui_main.c @@ -5982,11 +5982,10 @@ static void UI_StartServerRefresh(qboolean full, qboolean force) // This function is called with force=qfalse when server browser menu opens or net source changes. // Automatically update local and favorite servers. - // Only update master server list the first time because the server info cache will be available after that. + // Only auto update master server list if there is no server info cache. if ( !force && ( ui_netSource.integer >= UIAS_GLOBAL1 && ui_netSource.integer <= UIAS_GLOBAL5 ) ) { - char *value = UI_Cvar_VariableString( va( "ui_lastServerRefresh_%i", ui_netSource.integer ) ); - if ( value[0] != 0 ) { - return; // should have cached list + if ( trap_LAN_GetServerCount( UI_SourceForLAN() ) > 0 ) { + return; // have cached list } } From db1198f6ea2e31769c2b3683ff25e2e973edf15c Mon Sep 17 00:00:00 2001 From: Zack Middleton Date: Sat, 3 Jun 2017 13:52:38 -0500 Subject: [PATCH 051/240] Add mouse wheel support to UI list boxes Allows scrolling server browser list and some other lists. --- code/q3_ui/ui_qmenu.c | 44 +++++++++++++++++++++++++++++++++++++++++++ code/ui/ui_shared.c | 21 +++++++++++++++++++++ 2 files changed, 65 insertions(+) diff --git a/code/q3_ui/ui_qmenu.c b/code/q3_ui/ui_qmenu.c index 330b6d2b9f..90d9cb05c1 100644 --- a/code/q3_ui/ui_qmenu.c +++ b/code/q3_ui/ui_qmenu.c @@ -1034,6 +1034,50 @@ sfxHandle_t ScrollList_Key( menulist_s *l, int key ) } return (menu_buzz_sound); + case K_MWHEELUP: + if( l->columns > 1 ) { + return menu_null_sound; + } + + if (l->top > 0) + { + // if scrolling 3 lines would replace over half of the + // displayed items, only scroll 1 item at a time. + int scroll = l->height < 6 ? 1 : 3; + l->top -= scroll; + if (l->top < 0) + l->top = 0; + + if (l->generic.callback) + l->generic.callback( l, QM_GOTFOCUS ); + + // make scrolling silent + return (menu_null_sound); + } + return (menu_buzz_sound); + + case K_MWHEELDOWN: + if( l->columns > 1 ) { + return menu_null_sound; + } + + if (l->top < l->numitems-l->height) + { + // if scrolling 3 items would replace over half of the + // displayed items, only scroll 1 item at a time. + int scroll = l->height < 6 ? 1 : 3; + l->top += scroll; + if (l->top > l->numitems-l->height) + l->top = l->numitems-l->height; + + if (l->generic.callback) + l->generic.callback( l, QM_GOTFOCUS ); + + // make scrolling silent + return (menu_null_sound); + } + return (menu_buzz_sound); + case K_KP_UPARROW: case K_UPARROW: if( l->curvalue == 0 ) { diff --git a/code/ui/ui_shared.c b/code/ui/ui_shared.c index afef613d92..b1718e77cd 100644 --- a/code/ui/ui_shared.c +++ b/code/ui/ui_shared.c @@ -1828,6 +1828,27 @@ qboolean Item_ListBox_HandleKey(itemDef_t *item, int key, qboolean down, qboolea return qtrue; } } + + // Use mouse wheel in vertical and horizontal menus. + // If scrolling 3 items would replace over half of the + // displayed items, only scroll 1 item at a time. + if ( key == K_MWHEELUP ) { + int scroll = viewmax < 6 ? 1 : 3; + listPtr->startPos -= scroll; + if (listPtr->startPos < 0) { + listPtr->startPos = 0; + } + return qtrue; + } + if ( key == K_MWHEELDOWN ) { + int scroll = viewmax < 6 ? 1 : 3; + listPtr->startPos += scroll; + if (listPtr->startPos > max) { + listPtr->startPos = max; + } + return qtrue; + } + // mouse hit if (key == K_MOUSE1 || key == K_MOUSE2) { if (item->window.flags & WINDOW_LB_LEFTARROW) { From ece37f13905ca39e028c13be372fc79eae87639a Mon Sep 17 00:00:00 2001 From: "Ryan C. Gordon" Date: Sat, 3 Jun 2017 20:26:07 -0400 Subject: [PATCH 052/240] Initial add of rsa_tools. This is just a simple RSA public key digital signature thing built on libtomcrypt. The gist: Some admin will generate a public/private key with rsa_make_keys, keeping the private key secret. Using the private key and rsa_sign, the admin will sign the autoupdater manifests, generating manifest.txt.sig. The public key ships with the game (adding 270 bytes to the download), the .sig is downloaded with the manifest by the autoupdater (256 bytes extra download), then the autoupdater checks the manifest against the signature with the public key. if the signature isn't valid (the manifest was tampered with or corrupt), the autoupdater refuses to continue. If the manifest is to be trusted, it lists sha256 checksums for every file to download, so there's no need to sign every file; if they can't tamper with the manifest, they can't tamper with any other file to be updated since the file's listed sha256 won't match. If the private key is compromised, we generate new keys and ship new installers, so new installations will be able to update but existing ones will need to do a new install to keep getting updates. Don't let the private key get compromised. The private key doesn't go on a public server. Maybe it doesn't even live on the admin's laptop hard drive. If the download server is compromised and serving malware, the autoupdater will reject it outright if they haven't compromised the private key, generated a new manifest, and signed it with the private key. libtomcrypt is sort of a big pile of source code, so instead of putting it in revision control, we have a script to download it. Most things don't need it. It lives on GitHub, so we _could_ do a git submodule, but most people don't need it, so why waste their disk and bandwidth? That said, when compiled you end up with a few hundred kilobytes of binary code to verify a signature and no external dependencies, so it seems like a win. --- .../rsa_tools/build-libtom-unix.sh | 68 +++++++++++++++++ code/autoupdater/rsa_tools/build-rsa-tools.sh | 23 ++++++ code/autoupdater/rsa_tools/rsa_common.c | 61 +++++++++++++++ code/autoupdater/rsa_tools/rsa_common.h | 30 ++++++++ code/autoupdater/rsa_tools/rsa_make_keys.c | 45 +++++++++++ code/autoupdater/rsa_tools/rsa_sign.c | 75 +++++++++++++++++++ code/autoupdater/rsa_tools/rsa_verify.c | 60 +++++++++++++++ code/autoupdater/rsa_tools/test-rsa-tools.sh | 17 +++++ 8 files changed, 379 insertions(+) create mode 100755 code/autoupdater/rsa_tools/build-libtom-unix.sh create mode 100755 code/autoupdater/rsa_tools/build-rsa-tools.sh create mode 100644 code/autoupdater/rsa_tools/rsa_common.c create mode 100644 code/autoupdater/rsa_tools/rsa_common.h create mode 100644 code/autoupdater/rsa_tools/rsa_make_keys.c create mode 100644 code/autoupdater/rsa_tools/rsa_sign.c create mode 100644 code/autoupdater/rsa_tools/rsa_verify.c create mode 100755 code/autoupdater/rsa_tools/test-rsa-tools.sh diff --git a/code/autoupdater/rsa_tools/build-libtom-unix.sh b/code/autoupdater/rsa_tools/build-libtom-unix.sh new file mode 100755 index 0000000000..8700e86887 --- /dev/null +++ b/code/autoupdater/rsa_tools/build-libtom-unix.sh @@ -0,0 +1,68 @@ +#!/bin/bash + +TFMVER=0.13.1 +LTCVER=1.17 +set -e + +OSTYPE=`uname -s` +if [ "$OSTYPE" = "Linux" ]; then + NCPU=`cat /proc/cpuinfo |grep vendor_id |wc -l` + let NCPU=$NCPU+1 +elif [ "$OSTYPE" = "Darwin" ]; then + NCPU=`sysctl -n hw.ncpu` +elif [ "$OSTYPE" = "SunOS" ]; then + NCPU=`/usr/sbin/psrinfo |wc -l |sed -e 's/^ *//g;s/ *$//g'` +else + NCPU=1 +fi + +if [ -z "$NCPU" ]; then + NCPU=1 +elif [ "$NCPU" = "0" ]; then + NCPU=1 +fi + +if [ ! -f ./crypt-$LTCVER.tar.bz2 ]; then + echo "Downloading LibTomCrypt $LTCVER sources..." + curl -L -o crypt-$LTCVER.tar.bz2 https://github.com/libtom/libtomcrypt/releases/download/$LTCVER/crypt-$LTCVER.tar.bz2 || exit 1 +fi + +if [ ! -f tfm-$TFMVER.tar.xz ]; then + echo "Downloading TomsFastMath $TFMVER sources..." + curl -L -o tfm-$TFMVER.tar.xz https://github.com/libtom/tomsfastmath/releases/download/v$TFMVER/tfm-$TFMVER.tar.xz || exit 1 +fi + +if [ ! -d tomsfastmath-$TFMVER ]; then + echo "Unpacking TomsFastMath $TFMVER sources..." + tar -xJvvf ./tfm-$TFMVER.tar.xz +fi + +if [ ! -d libtomcrypt-$LTCVER ]; then + echo "Unpacking LibTomCrypt $LTCVER sources..." + tar -xjvvf ./crypt-$LTCVER.tar.bz2 +fi + +echo +echo +echo "Will use make -j$NCPU. If this is wrong, check NCPU at top of script." +echo +echo + +set -e +set -x + +# Some compilers can't handle the ROLC inline asm; just turn it off. +cd tomsfastmath-$TFMVER +make -j$NCPU +cd .. + +export CFLAGS="$CFLAGS -DTFM_DESC -DLTC_NO_ROLC -I ../tomsfastmath-$TFMVER/src/headers" +cd libtomcrypt-$LTCVER +make -j$NCPU +cd .. + +set +x +echo "All done." + +# end of build-libtom-unix.sh ... + diff --git a/code/autoupdater/rsa_tools/build-rsa-tools.sh b/code/autoupdater/rsa_tools/build-rsa-tools.sh new file mode 100755 index 0000000000..212d6e709b --- /dev/null +++ b/code/autoupdater/rsa_tools/build-rsa-tools.sh @@ -0,0 +1,23 @@ +#!/bin/bash + +# You don't need these to be built with the autoupdater, so here's a simple +# shell file to make them on a Mac. + +export TFMDIR="tomsfastmath-0.13.1" +export LTCDIR="libtomcrypt-1.17" + +function build { + clang -I $TFMDIR/src/headers -I $LTCDIR/src/headers -o "$1" -Wall -O3 "$1.c" rsa_common.c $LTCDIR/libtomcrypt.a $TFMDIR/libtfm.a +} + +set -e +set -x + +./build-libtom-unix.sh +build rsa_make_keys +build rsa_sign +build rsa_verify + +set +x +echo "rsa_tools are compiled!" + diff --git a/code/autoupdater/rsa_tools/rsa_common.c b/code/autoupdater/rsa_tools/rsa_common.c new file mode 100644 index 0000000000..d0a10a0a30 --- /dev/null +++ b/code/autoupdater/rsa_tools/rsa_common.c @@ -0,0 +1,61 @@ +#include "rsa_common.h" + +void fail(const char *fmt, ...) +{ + va_list ap; + va_start(ap, fmt); + vfprintf(stderr, fmt, ap); + va_end(ap); + fputs("\n", stderr); + fflush(stderr); + exit(1); +} + +void write_file(const char *fname, const void *buf, const unsigned long len) +{ + FILE *io = fopen(fname, "wb"); + if (!io) { + fail("Can't open '%s' for writing: %s", fname, strerror(errno)); + } + + if (fwrite(buf, len, 1, io) != 1) { + fail("Couldn't write '%s': %s", fname, strerror(errno)); + } + + if (fclose(io) != 0) { + fail("Couldn't flush '%s' to disk: %s", fname, strerror(errno)); + } +} + +void read_file(const char *fname, void *buf, unsigned long *len) +{ + ssize_t br; + FILE *io = fopen(fname, "rb"); + if (!io) { + fail("Can't open '%s' for reading: %s", fname, strerror(errno)); + } + + br = fread(buf, 1, *len, io); + if (ferror(io)) { + fail("Couldn't read '%s': %s", fname, strerror(errno)); + } else if (!feof(io)) { + fail("Buffer too small to read '%s'", fname); + } + fclose(io); + + *len = (unsigned long) br; +} + +void read_rsakey(rsa_key *key, const char *fname) +{ + unsigned char buf[4096]; + unsigned long len = sizeof (buf); + int rc; + + read_file(fname, buf, &len); + + if ((rc = rsa_import(buf, len, key)) != CRYPT_OK) { + fail("rsa_import for '%s' failed: %s", fname, error_to_string(rc)); + } +} + diff --git a/code/autoupdater/rsa_tools/rsa_common.h b/code/autoupdater/rsa_tools/rsa_common.h new file mode 100644 index 0000000000..486955224f --- /dev/null +++ b/code/autoupdater/rsa_tools/rsa_common.h @@ -0,0 +1,30 @@ +#ifndef _INCL_RSA_COMMON_H_ +#define _INCL_RSA_COMMON_H_ 1 + +#include +#include +#include + +#define TFM_DESC +#define LTC_NO_ROLC +#include "tomcrypt.h" + +#define SALT_LEN 8 + +#if defined(__GNUC__) || defined(__clang__) +#define NEVER_RETURNS __attribute__((noreturn)) +#define PRINTF_FUNC(fmtargnum, dotargnum) __attribute__ (( format( __printf__, fmtargnum, dotargnum ))) +#else +#define NEVER_RETURNS +#define PRINTF_FUNC(fmtargnum, dotargnum) +#endif + +void fail(const char *fmt, ...) NEVER_RETURNS PRINTF_FUNC(1, 2); +void write_file(const char *fname, const void *buf, const unsigned long len); +void read_file(const char *fname, void *buf, unsigned long *len); +void read_rsakey(rsa_key *key, const char *fname); + +#endif + +/* end of rsa_common.h ... */ + diff --git a/code/autoupdater/rsa_tools/rsa_make_keys.c b/code/autoupdater/rsa_tools/rsa_make_keys.c new file mode 100644 index 0000000000..a7f801c006 --- /dev/null +++ b/code/autoupdater/rsa_tools/rsa_make_keys.c @@ -0,0 +1,45 @@ +#include "rsa_common.h" + +static void write_rsakey(rsa_key *key, const int type, const char *fname) +{ + unsigned char buf[4096]; + unsigned long len = sizeof (buf); + int rc; + + if ((rc = rsa_export(buf, &len, type, key)) != CRYPT_OK) { + fail("rsa_export for '%s' failed: %s", fname, error_to_string(rc)); + } + write_file(fname, buf, len); +} + +int main(int argc, char **argv) +{ + int rc = 0; + prng_state prng; + int prng_index; + rsa_key key; + + ltc_mp = tfm_desc; + prng_index = register_prng(&sprng_desc); /* (fortuna_desc is a good choice if your platform's PRNG sucks.) */ + + if (prng_index == -1) { + fail("Failed to register a RNG"); + } + + if ((rc = rng_make_prng(128, prng_index, &prng, NULL)) != CRYPT_OK) { + fail("rng_make_prng failed: %s", error_to_string(rc)); + } + + if ((rc = rsa_make_key(&prng, prng_index, 256, 65537, &key)) != CRYPT_OK) { + fail("rng_make_key failed: %s", error_to_string(rc)); + } + + write_rsakey(&key, PK_PRIVATE, "privatekey.bin"); + write_rsakey(&key, PK_PUBLIC, "publickey.bin"); + + rsa_free(&key); + + return 0; +} + +/* end of rsa_make_keys.c ... */ diff --git a/code/autoupdater/rsa_tools/rsa_sign.c b/code/autoupdater/rsa_tools/rsa_sign.c new file mode 100644 index 0000000000..5eec24dd39 --- /dev/null +++ b/code/autoupdater/rsa_tools/rsa_sign.c @@ -0,0 +1,75 @@ +#include "rsa_common.h" + +static void sign_file(const char *fname, rsa_key *key, prng_state *prng, const int prng_index, const int hash_index) +{ + const size_t sigfnamelen = strlen(fname) + 5; + char *sigfname = (char *) malloc(sigfnamelen); + unsigned char hash[256]; + unsigned long hashlen = sizeof (hash); + unsigned char sig[1024]; + unsigned long siglen = sizeof (sig); + int rc = 0; + int status = 0; + + if (!sigfname) { + fail("out of memory"); + } + + if ((rc = hash_file(hash_index, fname, hash, &hashlen)) != CRYPT_OK) { + fail("hash_file for '%s' failed: %s", fname, error_to_string(rc)); + } + + if ((rc = rsa_sign_hash(hash, hashlen, sig, &siglen, prng, prng_index, hash_index, SALT_LEN, key)) != CRYPT_OK) { + fail("rsa_sign_hash for '%s' failed: %s", fname, error_to_string(rc)); + } + + if ((rc = rsa_verify_hash(sig, siglen, hash, hashlen, hash_index, SALT_LEN, &status, key)) != CRYPT_OK) { + fail("rsa_verify_hash for '%s' failed: %s", fname, error_to_string(rc)); + } + + if (!status) { + fail("Generated signature isn't valid! Bug in the program!"); + } + + snprintf(sigfname, sigfnamelen, "%s.sig", fname); + write_file(sigfname, sig, siglen); + free(sigfname); +} + +int main(int argc, char **argv) +{ + int rc = 0; + prng_state prng; + int prng_index, hash_index; + rsa_key key; + int i; + + ltc_mp = tfm_desc; + + prng_index = register_prng(&sprng_desc); /* (fortuna_desc is a good choice if your platform's PRNG sucks.) */ + if (prng_index == -1) { + fail("Failed to register a RNG"); + } + + hash_index = register_hash(&sha256_desc); + if (hash_index == -1) { + fail("Failed to register sha256 hasher"); + } + + if ((rc = rng_make_prng(128, prng_index, &prng, NULL)) != CRYPT_OK) { + fail("rng_make_prng failed: %s", error_to_string(rc)); + } + + read_rsakey(&key, "privatekey.bin"); + + for (i = 1; i < argc; i++) { + sign_file(argv[i], &key, &prng, prng_index, hash_index); + } + + rsa_free(&key); + + return 0; +} + +/* end of rsa_sign.c ... */ + diff --git a/code/autoupdater/rsa_tools/rsa_verify.c b/code/autoupdater/rsa_tools/rsa_verify.c new file mode 100644 index 0000000000..09abfb2443 --- /dev/null +++ b/code/autoupdater/rsa_tools/rsa_verify.c @@ -0,0 +1,60 @@ +#include "rsa_common.h" + +static void verify_file(const char *fname, rsa_key *key, const int hash_index) +{ + const size_t sigfnamelen = strlen(fname) + 5; + char *sigfname = (char *) malloc(sigfnamelen); + unsigned char hash[256]; + unsigned long hashlen = sizeof (hash); + unsigned char sig[1024]; + unsigned long siglen = sizeof (sig); + int status = 0; + int rc = 0; + + if (!sigfname) { + fail("out of memory"); + } + + snprintf(sigfname, sigfnamelen, "%s.sig", fname); + read_file(sigfname, sig, &siglen); + free(sigfname); + + if ((rc = hash_file(hash_index, fname, hash, &hashlen)) != CRYPT_OK) { + fail("hash_file for '%s' failed: %s", fname, error_to_string(rc)); + } + + if ((rc = rsa_verify_hash(sig, siglen, hash, hashlen, hash_index, SALT_LEN, &status, key)) != CRYPT_OK) { + fail("rsa_verify_hash for '%s' failed: %s", fname, error_to_string(rc)); + } + + if (!status) { + fail("Invalid signature for '%s'! Don't trust this file!", fname); + } +} + +int main(int argc, char **argv) +{ + int hash_index; + rsa_key key; + int i; + + ltc_mp = tfm_desc; + + hash_index = register_hash(&sha256_desc); + if (hash_index == -1) { + fail("Failed to register sha256 hasher"); + } + + read_rsakey(&key, "publickey.bin"); + + for (i = 1; i < argc; i++) { + verify_file(argv[i], &key, hash_index); + } + + rsa_free(&key); + + return 0; +} + +/* end of rsa_verify.c ... */ + diff --git a/code/autoupdater/rsa_tools/test-rsa-tools.sh b/code/autoupdater/rsa_tools/test-rsa-tools.sh new file mode 100755 index 0000000000..f4b4bdb093 --- /dev/null +++ b/code/autoupdater/rsa_tools/test-rsa-tools.sh @@ -0,0 +1,17 @@ +#!/bin/bash + +if [ -f privatekey.bin ]; then + echo "move your existing keys out of the way." + exit 1 +fi + +( ./rsa_make_keys && echo "key making okay") || echo "key making NOT okay" +echo "The quick brown fox jumped over the lazy dog." >testmsg.txt +( ./rsa_sign testmsg.txt && echo "signing okay" ) || echo "signing NOT okay" +( ./rsa_verify testmsg.txt && echo "basic verifying okay" ) || echo "basic verifying NOT okay" +echo "The quick brown fox jumped over the lazy dog!" >testmsg.txt +( ./rsa_verify testmsg.txt 2>/dev/null && echo "tamper test NOT okay" ) || echo "tamper test okay" +echo "The quick brown fox jumped over the lazy dog." >testmsg.txt +( ./rsa_verify testmsg.txt && echo "reverify okay" ) || echo "reverify NOT okay" +rm -f testmsg.txt testmsg.txt.sig publickey.bin privatekey.bin + From 7542966e3360ab994972f9d3db5aac8a3edec06d Mon Sep 17 00:00:00 2001 From: "Ryan C. Gordon" Date: Sun, 4 Jun 2017 01:15:38 -0400 Subject: [PATCH 053/240] Verify libTom source archives aren't tampered with. --- .../rsa_tools/build-libtom-unix.sh | 19 ++++++++++++++----- 1 file changed, 14 insertions(+), 5 deletions(-) diff --git a/code/autoupdater/rsa_tools/build-libtom-unix.sh b/code/autoupdater/rsa_tools/build-libtom-unix.sh index 8700e86887..4e61732f24 100755 --- a/code/autoupdater/rsa_tools/build-libtom-unix.sh +++ b/code/autoupdater/rsa_tools/build-libtom-unix.sh @@ -22,22 +22,31 @@ elif [ "$NCPU" = "0" ]; then NCPU=1 fi -if [ ! -f ./crypt-$LTCVER.tar.bz2 ]; then - echo "Downloading LibTomCrypt $LTCVER sources..." - curl -L -o crypt-$LTCVER.tar.bz2 https://github.com/libtom/libtomcrypt/releases/download/$LTCVER/crypt-$LTCVER.tar.bz2 || exit 1 -fi - if [ ! -f tfm-$TFMVER.tar.xz ]; then echo "Downloading TomsFastMath $TFMVER sources..." curl -L -o tfm-$TFMVER.tar.xz https://github.com/libtom/tomsfastmath/releases/download/v$TFMVER/tfm-$TFMVER.tar.xz || exit 1 fi +if [ ! -f ./crypt-$LTCVER.tar.bz2 ]; then + echo "Downloading LibTomCrypt $LTCVER sources..." + curl -L -o crypt-$LTCVER.tar.bz2 https://github.com/libtom/libtomcrypt/releases/download/$LTCVER/crypt-$LTCVER.tar.bz2 || exit 1 +fi + if [ ! -d tomsfastmath-$TFMVER ]; then + echo "Checking TomsFastMath archive hash..." + if [ "`shasum -a 256 tfm-$TFMVER.tar.xz |awk '{print $1;}'`" != "47c97a1ada3ccc9fcbd2a8a922d5859a84b4ba53778c84c1d509c1a955ac1738" ]; then + echo "Uhoh, tfm-$TFMVER.tar.xz does not have the sha256sum we expected!" + exit 1 + fi echo "Unpacking TomsFastMath $TFMVER sources..." tar -xJvvf ./tfm-$TFMVER.tar.xz fi if [ ! -d libtomcrypt-$LTCVER ]; then + if [ "`shasum -a 256 crypt-$LTCVER.tar.bz2 |awk '{print $1;}'`" != "e33b47d77a495091c8703175a25c8228aff043140b2554c08a3c3cd71f79d116" ]; then + echo "Uhoh, crypt-$LTCVER.tar.bz2 does not have the sha256sum we expected!" + exit 1 + fi echo "Unpacking LibTomCrypt $LTCVER sources..." tar -xjvvf ./crypt-$LTCVER.tar.bz2 fi From 62f6f0c7e0be4bcd6dc45631fc8cf6350168a281 Mon Sep 17 00:00:00 2001 From: "Ryan C. Gordon" Date: Sun, 4 Jun 2017 01:16:37 -0400 Subject: [PATCH 054/240] Wire up libTom stuff to build system. --- Makefile | 9 ++++++--- code/autoupdater/rsa_tools/build-libtom-unix.sh | 2 ++ code/autoupdater/rsa_tools/build-rsa-tools.sh | 2 +- 3 files changed, 9 insertions(+), 4 deletions(-) diff --git a/Makefile b/Makefile index 4714655c7b..b5be7703e4 100644 --- a/Makefile +++ b/Makefile @@ -270,6 +270,8 @@ Q3CPPDIR=$(MOUNT_DIR)/tools/lcc/cpp Q3LCCETCDIR=$(MOUNT_DIR)/tools/lcc/etc Q3LCCSRCDIR=$(MOUNT_DIR)/tools/lcc/src AUTOUPDATERSRCDIR=$(MOUNT_DIR)/autoupdater +LIBTOMCRYPTSRCDIR=$(AUTOUPDATERSRCDIR)/rsa_tools/libtomcrypt-1.17 +TOMSFASTMATHSRCDIR=$(AUTOUPDATERSRCDIR)/rsa_tools/tomsfastmath-0.13.1 LOKISETUPDIR=misc/setup NSISDIR=misc/nsis SDLHDIR=$(MOUNT_DIR)/SDL2 @@ -376,7 +378,7 @@ ifneq (,$(findstring "$(PLATFORM)", "linux" "gnu_kfreebsd" "kfreebsd-gnu" "gnu") THREAD_LIBS=-lpthread LIBS=-ldl -lm - AUTOUPDATER_LIBS += -ldl + AUTOUPDATER_LIBS += -ldl $(LIBTOMCRYPTSRCDIR)/libtomcrypt.a $(TOMSFASTMATHSRCDIR)/libtfm.a CLIENT_LIBS=$(SDL_LIBS) RENDERER_LIBS = $(SDL_LIBS) -lGL @@ -419,6 +421,8 @@ ifeq ($(PLATFORM),darwin) RENDERER_LIBS= OPTIMIZEVM= + AUTOUPDATER_LIBS += $(LIBTOMCRYPTSRCDIR)/libtomcrypt.a $(TOMSFASTMATHSRCDIR)/libtfm.a + # Default minimum Mac OS X version ifeq ($(MACOSX_VERSION_MIN),) MACOSX_VERSION_MIN=10.7 @@ -1601,12 +1605,11 @@ $(Q3ASM): $(Q3ASMOBJ) define DO_AUTOUPDATER_CC $(echo_cmd) "AUTOUPDATER_CC $<" -$(Q)$(CC) $(CFLAGS) $(CURL_CFLAGS) -o $@ -c $< +$(Q)$(CC) $(CFLAGS) -I$(LIBTOMCRYPTSRCDIR)/src/headers -I$(TOMSFASTMATHSRCDIR)/src/headers $(CURL_CFLAGS) -o $@ -c $< endef Q3AUTOUPDATEROBJ = \ $(B)/autoupdater/autoupdater.o \ - $(B)/autoupdater/sha256.o $(B)/autoupdater/%.o: $(AUTOUPDATERSRCDIR)/%.c $(DO_AUTOUPDATER_CC) diff --git a/code/autoupdater/rsa_tools/build-libtom-unix.sh b/code/autoupdater/rsa_tools/build-libtom-unix.sh index 4e61732f24..bbd863123f 100755 --- a/code/autoupdater/rsa_tools/build-libtom-unix.sh +++ b/code/autoupdater/rsa_tools/build-libtom-unix.sh @@ -10,6 +10,8 @@ if [ "$OSTYPE" = "Linux" ]; then let NCPU=$NCPU+1 elif [ "$OSTYPE" = "Darwin" ]; then NCPU=`sysctl -n hw.ncpu` + export CFLAGS="$CFLAGS -mmacosx-version-min=10.7 -DMAC_OS_X_VERSION_MIN_REQUIRED=1070" + export LDFLAGS="$LDFLAGS -mmacosx-version-min=10.7" elif [ "$OSTYPE" = "SunOS" ]; then NCPU=`/usr/sbin/psrinfo |wc -l |sed -e 's/^ *//g;s/ *$//g'` else diff --git a/code/autoupdater/rsa_tools/build-rsa-tools.sh b/code/autoupdater/rsa_tools/build-rsa-tools.sh index 212d6e709b..422e48d5d1 100755 --- a/code/autoupdater/rsa_tools/build-rsa-tools.sh +++ b/code/autoupdater/rsa_tools/build-rsa-tools.sh @@ -7,7 +7,7 @@ export TFMDIR="tomsfastmath-0.13.1" export LTCDIR="libtomcrypt-1.17" function build { - clang -I $TFMDIR/src/headers -I $LTCDIR/src/headers -o "$1" -Wall -O3 "$1.c" rsa_common.c $LTCDIR/libtomcrypt.a $TFMDIR/libtfm.a + clang -mmacosx-version-min=10.7 -DMAC_OS_X_VERSION_MIN_REQUIRED=1070 -I $TFMDIR/src/headers -I $LTCDIR/src/headers -o "$1" -Wall -O3 "$1.c" rsa_common.c $LTCDIR/libtomcrypt.a $TFMDIR/libtfm.a } set -e From f71260eb8c5287761f1f2c15b74d64f65a04fe6e Mon Sep 17 00:00:00 2001 From: "Ryan C. Gordon" Date: Sun, 4 Jun 2017 01:17:17 -0400 Subject: [PATCH 055/240] Replace sha256.c with libTomCrypt equivalent, since we'll be using it anyway. --- code/autoupdater/autoupdater.c | 46 +++++----- code/autoupdater/sha256.c | 156 --------------------------------- code/autoupdater/sha256.h | 42 --------- 3 files changed, 22 insertions(+), 222 deletions(-) delete mode 100644 code/autoupdater/sha256.c delete mode 100644 code/autoupdater/sha256.h diff --git a/code/autoupdater/autoupdater.c b/code/autoupdater/autoupdater.c index 0e8392b868..fcaf006a30 100644 --- a/code/autoupdater/autoupdater.c +++ b/code/autoupdater/autoupdater.c @@ -34,7 +34,13 @@ typedef pid_t PID; #define PIDFMTCAST unsigned long long #endif -#include "sha256.h" +/* If your build fails here with tomcrypt.h missing, you probably need to + run the build-libtom script in the rsa_tools subdirectory. */ +#define TFM_DESC +#define LTC_NO_ROLC +#include "tomcrypt.h" + +static int sha256_hash_index = 0; #ifndef AUTOUPDATE_USER_AGENT @@ -524,7 +530,7 @@ static int hexcvt(const int ch) return 0; } -static void convertSha256(char *str, uint8 *sha256) +static void convertSha256(char *str, unsigned char *sha256) { int i; for (i = 0; i < 32; i++) { @@ -696,29 +702,12 @@ static const char *justFilename(const char *path) static void hashFile(const char *fname, unsigned char *sha256) { - SHA256_CTX sha256ctx; - uint8 buf[512]; - FILE *io; - - io = fopen(fname, "rb"); - if (!io) { - die("Failed to open file for hashing"); + int rc = 0; + unsigned long hashlen = 32; + if ((rc = hash_file(sha256_hash_index, fname, sha256, &hashlen)) != CRYPT_OK) { + infof("hash_file failed for '%s': %s", fname, error_to_string(rc)); + die("Can't hash file"); } - - sha256_init(&sha256ctx); - do { - size_t br = fread(buf, 1, sizeof (buf), io); - if (br > 0) { - sha256_update(&sha256ctx, buf, br); - } - if (ferror(io)) { - die("Error reading file for hashing"); - } - } while (!feof(io)); - - fclose(io); - - sha256_final(&sha256ctx, sha256); } static int fileHashMatches(const char *fname, const unsigned char *wanted) @@ -941,6 +930,13 @@ int main(int argc, char **argv) parseArgv(argc, argv); + /* set up crypto */ + ltc_mp = tfm_desc; + sha256_hash_index = register_hash(&sha256_desc); + if (sha256_hash_index == -1) { + die("Failed to register sha256 hasher"); + } + /* if we have downloaded a new updater and restarted with that binary, replace the original updater and restart again in the right place. */ if (options.updateself) { @@ -965,6 +961,8 @@ int main(int argc, char **argv) freeManifest(); shutdownHttpLib(); + unregister_hash(&sha256_desc); + infof("Updater ending, %s", timestamp()); return 0; diff --git a/code/autoupdater/sha256.c b/code/autoupdater/sha256.c deleted file mode 100644 index 0861860f4a..0000000000 --- a/code/autoupdater/sha256.c +++ /dev/null @@ -1,156 +0,0 @@ -/********************************************************************* -* Filename: sha256.c -* Author: Brad Conte (brad AT bradconte.com) -* Copyright: -* Disclaimer: This code is presented "as is" without any guarantees. -* Details: Implementation of the SHA-256 hashing algorithm. - SHA-256 is one of the three algorithms in the SHA2 - specification. The others, SHA-384 and SHA-512, are not - offered in this implementation. - Algorithm specification can be found here: - * http://csrc.nist.gov/publications/fips/fips180-2/fips180-2withchangenotice.pdf - This implementation uses little endian byte order. -*********************************************************************/ - -/*************************** HEADER FILES ***************************/ -#include -#include -#include "sha256.h" - -/****************************** MACROS ******************************/ -#define ROTLEFT(a,b) (((a) << (b)) | ((a) >> (32-(b)))) -#define ROTRIGHT(a,b) (((a) >> (b)) | ((a) << (32-(b)))) - -#define CH(x,y,z) (((x) & (y)) ^ (~(x) & (z))) -#define MAJ(x,y,z) (((x) & (y)) ^ ((x) & (z)) ^ ((y) & (z))) -#define EP0(x) (ROTRIGHT(x,2) ^ ROTRIGHT(x,13) ^ ROTRIGHT(x,22)) -#define EP1(x) (ROTRIGHT(x,6) ^ ROTRIGHT(x,11) ^ ROTRIGHT(x,25)) -#define SIG0(x) (ROTRIGHT(x,7) ^ ROTRIGHT(x,18) ^ ((x) >> 3)) -#define SIG1(x) (ROTRIGHT(x,17) ^ ROTRIGHT(x,19) ^ ((x) >> 10)) - -/**************************** VARIABLES *****************************/ -static const uint32 k[64] = { - 0x428a2f98,0x71374491,0xb5c0fbcf,0xe9b5dba5,0x3956c25b,0x59f111f1,0x923f82a4,0xab1c5ed5, - 0xd807aa98,0x12835b01,0x243185be,0x550c7dc3,0x72be5d74,0x80deb1fe,0x9bdc06a7,0xc19bf174, - 0xe49b69c1,0xefbe4786,0x0fc19dc6,0x240ca1cc,0x2de92c6f,0x4a7484aa,0x5cb0a9dc,0x76f988da, - 0x983e5152,0xa831c66d,0xb00327c8,0xbf597fc7,0xc6e00bf3,0xd5a79147,0x06ca6351,0x14292967, - 0x27b70a85,0x2e1b2138,0x4d2c6dfc,0x53380d13,0x650a7354,0x766a0abb,0x81c2c92e,0x92722c85, - 0xa2bfe8a1,0xa81a664b,0xc24b8b70,0xc76c51a3,0xd192e819,0xd6990624,0xf40e3585,0x106aa070, - 0x19a4c116,0x1e376c08,0x2748774c,0x34b0bcb5,0x391c0cb3,0x4ed8aa4a,0x5b9cca4f,0x682e6ff3, - 0x748f82ee,0x78a5636f,0x84c87814,0x8cc70208,0x90befffa,0xa4506ceb,0xbef9a3f7,0xc67178f2 -}; - -/*********************** FUNCTION DEFINITIONS ***********************/ -void sha256_transform(SHA256_CTX *ctx, const uint8 data[]) -{ - uint32 a, b, c, d, e, f, g, h, i, j, t1, t2, m[64]; - - for (i = 0, j = 0; i < 16; ++i, j += 4) - m[i] = (data[j] << 24) | (data[j + 1] << 16) | (data[j + 2] << 8) | (data[j + 3]); - for ( ; i < 64; ++i) - m[i] = SIG1(m[i - 2]) + m[i - 7] + SIG0(m[i - 15]) + m[i - 16]; - - a = ctx->state[0]; - b = ctx->state[1]; - c = ctx->state[2]; - d = ctx->state[3]; - e = ctx->state[4]; - f = ctx->state[5]; - g = ctx->state[6]; - h = ctx->state[7]; - - for (i = 0; i < 64; ++i) { - t1 = h + EP1(e) + CH(e,f,g) + k[i] + m[i]; - t2 = EP0(a) + MAJ(a,b,c); - h = g; - g = f; - f = e; - e = d + t1; - d = c; - c = b; - b = a; - a = t1 + t2; - } - - ctx->state[0] += a; - ctx->state[1] += b; - ctx->state[2] += c; - ctx->state[3] += d; - ctx->state[4] += e; - ctx->state[5] += f; - ctx->state[6] += g; - ctx->state[7] += h; -} - -void sha256_init(SHA256_CTX *ctx) -{ - ctx->datalen = 0; - ctx->bitlen = 0; - ctx->state[0] = 0x6a09e667; - ctx->state[1] = 0xbb67ae85; - ctx->state[2] = 0x3c6ef372; - ctx->state[3] = 0xa54ff53a; - ctx->state[4] = 0x510e527f; - ctx->state[5] = 0x9b05688c; - ctx->state[6] = 0x1f83d9ab; - ctx->state[7] = 0x5be0cd19; -} - -void sha256_update(SHA256_CTX *ctx, const uint8 data[], size_t len) -{ - size_t i; - - for (i = 0; i < len; ++i) { - ctx->data[ctx->datalen] = data[i]; - ctx->datalen++; - if (ctx->datalen == 64) { - sha256_transform(ctx, ctx->data); - ctx->bitlen += 512; - ctx->datalen = 0; - } - } -} - -void sha256_final(SHA256_CTX *ctx, uint8 hash[]) -{ - uint32 i = ctx->datalen; - - // Pad whatever data is left in the buffer. - if (ctx->datalen < 56) { - ctx->data[i++] = 0x80; - while (i < 56) - ctx->data[i++] = 0x00; - } - else { - ctx->data[i++] = 0x80; - while (i < 64) - ctx->data[i++] = 0x00; - sha256_transform(ctx, ctx->data); - memset(ctx->data, 0, 56); - } - - // Append to the padding the total message's length in bits and transform. - ctx->bitlen += ctx->datalen * 8; - ctx->data[63] = ctx->bitlen; - ctx->data[62] = ctx->bitlen >> 8; - ctx->data[61] = ctx->bitlen >> 16; - ctx->data[60] = ctx->bitlen >> 24; - ctx->data[59] = ctx->bitlen >> 32; - ctx->data[58] = ctx->bitlen >> 40; - ctx->data[57] = ctx->bitlen >> 48; - ctx->data[56] = ctx->bitlen >> 56; - sha256_transform(ctx, ctx->data); - - // Since this implementation uses little endian byte ordering and SHA uses big endian, - // reverse all the bytes when copying the final state to the output hash. - for (i = 0; i < 4; ++i) { - hash[i] = (ctx->state[0] >> (24 - i * 8)) & 0x000000ff; - hash[i + 4] = (ctx->state[1] >> (24 - i * 8)) & 0x000000ff; - hash[i + 8] = (ctx->state[2] >> (24 - i * 8)) & 0x000000ff; - hash[i + 12] = (ctx->state[3] >> (24 - i * 8)) & 0x000000ff; - hash[i + 16] = (ctx->state[4] >> (24 - i * 8)) & 0x000000ff; - hash[i + 20] = (ctx->state[5] >> (24 - i * 8)) & 0x000000ff; - hash[i + 24] = (ctx->state[6] >> (24 - i * 8)) & 0x000000ff; - hash[i + 28] = (ctx->state[7] >> (24 - i * 8)) & 0x000000ff; - } -} diff --git a/code/autoupdater/sha256.h b/code/autoupdater/sha256.h deleted file mode 100644 index fc9a09a95e..0000000000 --- a/code/autoupdater/sha256.h +++ /dev/null @@ -1,42 +0,0 @@ -/********************************************************************* -* Filename: sha256.h -* Author: Brad Conte (brad AT bradconte.com) -* Copyright: -* Disclaimer: This code is presented "as is" without any guarantees. -* Details: Defines the API for the corresponding SHA1 implementation. -*********************************************************************/ - -#ifndef SHA256_H -#define SHA256_H - -/*************************** HEADER FILES ***************************/ -#include - -/****************************** MACROS ******************************/ -#define SHA256_BLOCK_SIZE 32 // SHA256 outputs a 32 byte digest - -/**************************** DATA TYPES ****************************/ -#ifdef _MSC_VER -typedef unsigned __int8 uint8; -typedef unsigned __int32 uint32; -typedef unsigned __int64 uint64; -#else -#include -typedef uint8_t uint8; -typedef uint32_t uint32; -typedef uint64_t uint64; -#endif - -typedef struct { - uint8 data[64]; - uint32 datalen; - uint64 bitlen; - uint32 state[8]; -} SHA256_CTX; - -/*********************** FUNCTION DECLARATIONS **********************/ -void sha256_init(SHA256_CTX *ctx); -void sha256_update(SHA256_CTX *ctx, const uint8 data[], size_t len); -void sha256_final(SHA256_CTX *ctx, uint8 hash[]); - -#endif // SHA256_H From e04bfd49676b5ed88abc930befb7724c171abb35 Mon Sep 17 00:00:00 2001 From: "Ryan C. Gordon" Date: Sun, 4 Jun 2017 01:32:10 -0400 Subject: [PATCH 056/240] Autoupdater now checks RSA digital signature for manifest. --- code/autoupdater/autoupdater.c | 74 +++++++++++++++++++++++++++++++++- 1 file changed, 73 insertions(+), 1 deletion(-) diff --git a/code/autoupdater/autoupdater.c b/code/autoupdater/autoupdater.c index fcaf006a30..d7aa51d845 100644 --- a/code/autoupdater/autoupdater.c +++ b/code/autoupdater/autoupdater.c @@ -7,6 +7,7 @@ is licensed under the GPLv2. Do not mingle code, please! #include #include #include +#include #if defined(_MSC_VER) && (_MSC_VER < 1600) typedef __int64 int64_t; @@ -40,6 +41,8 @@ typedef pid_t PID; #define LTC_NO_ROLC #include "tomcrypt.h" +#define PUBLICKEY_FNAME "updater-publickey.bin" +#define SALT_LEN 8 static int sha256_hash_index = 0; @@ -599,11 +602,80 @@ static void parseManifest(const char *fname) fclose(io); } +static void read_file(const char *fname, void *buf, unsigned long *len) +{ + ssize_t br; + FILE *io = fopen(fname, "rb"); + if (!io) { + infof("Can't open '%s' for reading: %s", fname, strerror(errno)); + die("Failed to read file"); + } + + br = fread(buf, 1, *len, io); + if (ferror(io)) { + infof("Couldn't read '%s': %s", fname, strerror(errno)); + die("Failed to read file"); + } else if (!feof(io)) { + infof("Buffer too small to read '%s'", fname); + die("Failed to read file"); + } + fclose(io); + + *len = (unsigned long) br; +} + +static void read_rsakey(rsa_key *key, const char *fname) +{ + unsigned char buf[4096]; + unsigned long len = sizeof (buf); + int rc; + + read_file(fname, buf, &len); + + if ((rc = rsa_import(buf, len, key)) != CRYPT_OK) { + infof("rsa_import for '%s' failed: %s", fname, error_to_string(rc)); + die("Couldn't import public key"); + } +} + +static void verifySignature(const char *fname, const char *sigfname, const char *keyfname) +{ + rsa_key key; + unsigned char hash[256]; + unsigned long hashlen = sizeof (hash); + unsigned char sig[1024]; + unsigned long siglen = sizeof (sig); + int status = 0; + int rc = 0; + + read_rsakey(&key, keyfname); + read_file(sigfname, sig, &siglen); + + if ((rc = hash_file(sha256_hash_index, fname, hash, &hashlen)) != CRYPT_OK) { + infof("hash_file for '%s' failed: %s", fname, error_to_string(rc)); + die("Couldn't verify manifest signature"); + } + + if ((rc = rsa_verify_hash(sig, siglen, hash, hashlen, sha256_hash_index, SALT_LEN, &status, &key)) != CRYPT_OK) { + infof("rsa_verify_hash for '%s' failed: %s", fname, error_to_string(rc)); + die("Couldn't verify manifest signature"); + } + + if (!status) { + infof("Invalid signature for '%s'! Don't trust this file!", fname); + die("Manifest is incomplete, corrupt, or compromised"); + } + + rsa_free(&key); +} + static void downloadManifest(void) { const char *manifestfname = "updates/manifest.txt"; + const char *manifestsigfname = "updates/manifest.txt.sig"; downloadURL("manifest.txt", manifestfname); - /* !!! FIXME: verify manifest download is complete... */ + downloadURL("manifest.txt.sig", manifestsigfname); + verifySignature(manifestfname, manifestsigfname, PUBLICKEY_FNAME); parseManifest(manifestfname); } From ced74370426e63d751ee64ed46cec9f6d1ac7eb9 Mon Sep 17 00:00:00 2001 From: "Ryan C. Gordon" Date: Sun, 4 Jun 2017 01:32:41 -0400 Subject: [PATCH 057/240] Updated autoupdater-readme.txt with manifest signing details. --- autoupdater-readme.txt | 52 +++++++++++++++++++++++++++++++++++++----- 1 file changed, 46 insertions(+), 6 deletions(-) diff --git a/autoupdater-readme.txt b/autoupdater-readme.txt index 1353f4f06c..34561b7c8a 100644 --- a/autoupdater-readme.txt +++ b/autoupdater-readme.txt @@ -1,7 +1,8 @@ The updater program's code is public domain. The rest of ioquake3 is not. The source code to the autoupdater is in the code/autoupdater directory. -There is a small piece of code in ioquake3 itself at startup, too. +There is a small piece of code in ioquake3 itself at startup, too; this is +in code/sys/sys_autoupdater.c ... (This is all Unix terminology, but similar approaches on Windows apply.) @@ -20,8 +21,14 @@ The basic flow looks like this: - The updater downloads a manifest from a known URL over https://, using libCurl. The base URL is platform-specific (it might be https://example.com/mac/, or https://example.com/linux-x86/, whatever). + The url might have other features, like a updater version or a specific + product name, etc. The manifest is at $BASEURL/manifest.txt -- The manifest looks like this: three lines per file... +- The updater also downloads $BASEURL/manifest.txt.sig, which is a digital + signature for the manifest. It checks the manifest against this signature + and a known public RSA key; if the manifest doesn't match the signature, + the updater refuses to continue. +- The manifest looks like this: three lines per item... Contents/MacOS/baseq3/uix86_64.dylib 332428 @@ -105,13 +112,46 @@ Failure points: - If an update bricks ioquake3 to the point where it can't run the updater, running the updater directly should let it recover (assuming a future update fixes the problem). +- If the download server is compromised, they would need the private key + (not stored on the download server) to alter the manifest to serve + compromised files to players. If they try to change a file or the manifest, + the updater will know to abort without updating anything. +- If the private key is compromised, we generate a new one, ship new + installers with an updated public key, and re-sign the manifest with the + new private key. Existing installations will never update again until they + do a fresh install, or at least update their copy of the public key. + + +How manifest signing works: + +Some admin will generate a public/private key with the rsa_make_keys program, +keeping the private key secret. Using the private key and the rsa_sign +program, the admin will sign the manifest, generating manifest.txt.sig. + +The public key ships with the game (adding 270 bytes to the download), the +.sig is downloaded with the manifest by the autoupdater (256 bytes extra +download), then the autoupdater checks the manifest against the signature +with the public key. if the signature isn't valid (the manifest was tampered +with or corrupt), the autoupdater refuses to continue. + +If the manifest is to be trusted, it lists sha256 checksums for every file to +download, so there's no need to sign every file; if they can't tamper with the +manifest, they can't tamper with any other file to be updated since the file's +listed sha256 won't match. + +If the private key is compromised, we generate new keys and ship new +installers, so new installations will be able to update but existing ones +will need to do a new install to keep getting updates. Don't let the private +key get compromised. The private key doesn't go on a public server. Maybe it +doesn't even live on the admin's laptop hard drive. + +If the download server is compromised and serving malware, the autoupdater +will reject it outright if they haven't compromised the private key, generated +a new manifest, and signed it with the private key. + Items to consider for future revisions: -- GPG sign the manifest; if we can be confident that the manifest isn't - compromised, then the sha256 hashes of each file it contains should protect - the rest of the process. As it currently stands, we trust the download - server isn't compromised. - Maybe put a limit on the number manifest downloads, so we only check once every hour? Every day? - Channels? Stable (what everyone gets by default), Nightly (once a day), From 06cc3a4e1bfcb3f2936ef5f4f0fec4aa7b6885e6 Mon Sep 17 00:00:00 2001 From: "Ryan C. Gordon" Date: Sun, 4 Jun 2017 01:44:39 -0400 Subject: [PATCH 058/240] build-rsa-tools.sh now works on Linux. --- code/autoupdater/rsa_tools/build-rsa-tools.sh | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/code/autoupdater/rsa_tools/build-rsa-tools.sh b/code/autoupdater/rsa_tools/build-rsa-tools.sh index 422e48d5d1..23e65c6875 100755 --- a/code/autoupdater/rsa_tools/build-rsa-tools.sh +++ b/code/autoupdater/rsa_tools/build-rsa-tools.sh @@ -7,7 +7,11 @@ export TFMDIR="tomsfastmath-0.13.1" export LTCDIR="libtomcrypt-1.17" function build { - clang -mmacosx-version-min=10.7 -DMAC_OS_X_VERSION_MIN_REQUIRED=1070 -I $TFMDIR/src/headers -I $LTCDIR/src/headers -o "$1" -Wall -O3 "$1.c" rsa_common.c $LTCDIR/libtomcrypt.a $TFMDIR/libtfm.a + if [ "$OSTYPE" = "Darwin" ]; then + clang -mmacosx-version-min=10.7 -DMAC_OS_X_VERSION_MIN_REQUIRED=1070 -I $TFMDIR/src/headers -I $LTCDIR/src/headers -o "$1" -Wall -O3 "$1.c" rsa_common.c $LTCDIR/libtomcrypt.a $TFMDIR/libtfm.a + else + gcc -I $TFMDIR/src/headers -I $LTCDIR/src/headers -o "$1" -Wall -O3 "$1.c" rsa_common.c $LTCDIR/libtomcrypt.a $TFMDIR/libtfm.a + fi } set -e From c9c5d8710a9981c784d8b7271aca49d0f912c6ea Mon Sep 17 00:00:00 2001 From: "Ryan C. Gordon" Date: Sun, 4 Jun 2017 01:49:33 -0400 Subject: [PATCH 059/240] Added .gitignore for rsa_tools directory. --- code/autoupdater/rsa_tools/.gitignore | 8 ++++++++ 1 file changed, 8 insertions(+) create mode 100644 code/autoupdater/rsa_tools/.gitignore diff --git a/code/autoupdater/rsa_tools/.gitignore b/code/autoupdater/rsa_tools/.gitignore new file mode 100644 index 0000000000..8182fafcc5 --- /dev/null +++ b/code/autoupdater/rsa_tools/.gitignore @@ -0,0 +1,8 @@ +crypt-*.tar.bz2 +tfm-*.tar.xz +libtomcrypt-* +tomsfastmath-* +rsa_make_keys +rsa_sign +rsa_verify +*.exe From a761684a2356ddac693697e758ce3232af69a395 Mon Sep 17 00:00:00 2001 From: "Ryan C. Gordon" Date: Sun, 4 Jun 2017 02:21:55 -0400 Subject: [PATCH 060/240] Windows support for autoupdater manifest signatures. --- Makefile | 8 +++++--- code/autoupdater/rsa_tools/build-rsa-tools.sh | 16 +++++++++++----- 2 files changed, 16 insertions(+), 8 deletions(-) diff --git a/Makefile b/Makefile index b5be7703e4..a048ba9bc8 100644 --- a/Makefile +++ b/Makefile @@ -378,7 +378,7 @@ ifneq (,$(findstring "$(PLATFORM)", "linux" "gnu_kfreebsd" "kfreebsd-gnu" "gnu") THREAD_LIBS=-lpthread LIBS=-ldl -lm - AUTOUPDATER_LIBS += -ldl $(LIBTOMCRYPTSRCDIR)/libtomcrypt.a $(TOMSFASTMATHSRCDIR)/libtfm.a + AUTOUPDATER_LIBS += -ldl CLIENT_LIBS=$(SDL_LIBS) RENDERER_LIBS = $(SDL_LIBS) -lGL @@ -421,8 +421,6 @@ ifeq ($(PLATFORM),darwin) RENDERER_LIBS= OPTIMIZEVM= - AUTOUPDATER_LIBS += $(LIBTOMCRYPTSRCDIR)/libtomcrypt.a $(TOMSFASTMATHSRCDIR)/libtfm.a - # Default minimum Mac OS X version ifeq ($(MACOSX_VERSION_MIN),) MACOSX_VERSION_MIN=10.7 @@ -1120,6 +1118,10 @@ ifeq ($(USE_AUTOUPDATER),1) SERVER_CFLAGS += -DUSE_AUTOUPDATER -DAUTOUPDATER_BIN=\\\"$(AUTOUPDATER_BIN)\\\" endif +ifeq ($(BUILD_AUTOUPDATER),1) + AUTOUPDATER_LIBS += $(LIBTOMCRYPTSRCDIR)/libtomcrypt.a $(TOMSFASTMATHSRCDIR)/libtfm.a +endif + ifeq ("$(CC)", $(findstring "$(CC)", "clang" "clang++")) BASE_CFLAGS += -Qunused-arguments endif diff --git a/code/autoupdater/rsa_tools/build-rsa-tools.sh b/code/autoupdater/rsa_tools/build-rsa-tools.sh index 23e65c6875..dda9396c4f 100755 --- a/code/autoupdater/rsa_tools/build-rsa-tools.sh +++ b/code/autoupdater/rsa_tools/build-rsa-tools.sh @@ -1,16 +1,22 @@ #!/bin/bash -# You don't need these to be built with the autoupdater, so here's a simple -# shell file to make them on a Mac. - export TFMDIR="tomsfastmath-0.13.1" export LTCDIR="libtomcrypt-1.17" +OSTYPE=`uname -s` +if [ -z "$CC" ]; then + if [ "`uname -o`" = "Cygwin" ]; then + export CC=/usr/bin/i686-w64-mingw32-gcc + else + export CC=cc + fi +fi + function build { if [ "$OSTYPE" = "Darwin" ]; then - clang -mmacosx-version-min=10.7 -DMAC_OS_X_VERSION_MIN_REQUIRED=1070 -I $TFMDIR/src/headers -I $LTCDIR/src/headers -o "$1" -Wall -O3 "$1.c" rsa_common.c $LTCDIR/libtomcrypt.a $TFMDIR/libtfm.a + $CC -mmacosx-version-min=10.7 -DMAC_OS_X_VERSION_MIN_REQUIRED=1070 -I $TFMDIR/src/headers -I $LTCDIR/src/headers -o "$1" -Wall -O3 "$1.c" rsa_common.c $LTCDIR/libtomcrypt.a $TFMDIR/libtfm.a else - gcc -I $TFMDIR/src/headers -I $LTCDIR/src/headers -o "$1" -Wall -O3 "$1.c" rsa_common.c $LTCDIR/libtomcrypt.a $TFMDIR/libtfm.a + $CC -I $TFMDIR/src/headers -I $LTCDIR/src/headers -o "$1" -Wall -O3 "$1.c" rsa_common.c $LTCDIR/libtomcrypt.a $TFMDIR/libtfm.a fi } From 749e17ab1dd7cc31f4189a8050d24b21e82800b2 Mon Sep 17 00:00:00 2001 From: "Ryan C. Gordon" Date: Sun, 4 Jun 2017 02:23:49 -0400 Subject: [PATCH 061/240] Add a log message if manifest is valid. --- code/autoupdater/autoupdater.c | 1 + 1 file changed, 1 insertion(+) diff --git a/code/autoupdater/autoupdater.c b/code/autoupdater/autoupdater.c index d7aa51d845..221381b5c6 100644 --- a/code/autoupdater/autoupdater.c +++ b/code/autoupdater/autoupdater.c @@ -666,6 +666,7 @@ static void verifySignature(const char *fname, const char *sigfname, const char die("Manifest is incomplete, corrupt, or compromised"); } + info("Manifest signature appears to be valid"); rsa_free(&key); } From b771192d52da1aea6987f53ad34b318300c34bec Mon Sep 17 00:00:00 2001 From: "Ryan C. Gordon" Date: Sun, 4 Jun 2017 02:46:49 -0400 Subject: [PATCH 062/240] Use "x86_64" and not "x86-64" like everything else in ioq3. --- code/autoupdater/autoupdater.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/code/autoupdater/autoupdater.c b/code/autoupdater/autoupdater.c index 221381b5c6..4a0520273c 100644 --- a/code/autoupdater/autoupdater.c +++ b/code/autoupdater/autoupdater.c @@ -74,7 +74,7 @@ static int sha256_hash_index = 0; #ifdef __i386__ #define AUTOUPDATE_ARCH "x86" #elif defined(__x86_64__) -#define AUTOUPDATE_ARCH "x86-64" +#define AUTOUPDATE_ARCH "x86_64" #else #error Please define your platform. #endif From 76e6b3c53442d125e65dee927caec56b272e2ba9 Mon Sep 17 00:00:00 2001 From: "Ryan C. Gordon" Date: Sun, 4 Jun 2017 16:43:50 -0400 Subject: [PATCH 063/240] Removed unnecessary trailing backslash. --- Makefile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Makefile b/Makefile index a048ba9bc8..bfe14a5b64 100644 --- a/Makefile +++ b/Makefile @@ -1611,7 +1611,7 @@ $(Q)$(CC) $(CFLAGS) -I$(LIBTOMCRYPTSRCDIR)/src/headers -I$(TOMSFASTMATHSRCDIR)/s endef Q3AUTOUPDATEROBJ = \ - $(B)/autoupdater/autoupdater.o \ + $(B)/autoupdater/autoupdater.o $(B)/autoupdater/%.o: $(AUTOUPDATERSRCDIR)/%.c $(DO_AUTOUPDATER_CC) From d1631d6ea3cfc61936d7bcd5562101e4e6d37ab7 Mon Sep 17 00:00:00 2001 From: Zack Middleton Date: Wed, 7 Jun 2017 18:54:49 -0500 Subject: [PATCH 064/240] Use const char pointers when possible in botlib's libvar code --- code/botlib/be_interface.c | 4 ++-- code/botlib/botlib.h | 4 ++-- code/botlib/l_libvar.c | 22 +++++++++++----------- code/botlib/l_libvar.h | 18 +++++++++--------- 4 files changed, 24 insertions(+), 24 deletions(-) diff --git a/code/botlib/be_interface.c b/code/botlib/be_interface.c index 6f59975304..415c1b2beb 100644 --- a/code/botlib/be_interface.c +++ b/code/botlib/be_interface.c @@ -238,7 +238,7 @@ int Export_BotLibShutdown(void) // Returns: - // Changes Globals: - //=========================================================================== -int Export_BotLibVarSet(char *var_name, char *value) +int Export_BotLibVarSet(const char *var_name, const char *value) { LibVarSet(var_name, value); return BLERR_NOERROR; @@ -249,7 +249,7 @@ int Export_BotLibVarSet(char *var_name, char *value) // Returns: - // Changes Globals: - //=========================================================================== -int Export_BotLibVarGet(char *var_name, char *value, int size) +int Export_BotLibVarGet(const char *var_name, char *value, int size) { char *varvalue; diff --git a/code/botlib/botlib.h b/code/botlib/botlib.h index abdaa6885f..830a5eaa5f 100644 --- a/code/botlib/botlib.h +++ b/code/botlib/botlib.h @@ -409,9 +409,9 @@ typedef struct botlib_export_s //shutdown the bot library, returns BLERR_ int (*BotLibShutdown)(void); //sets a library variable returns BLERR_ - int (*BotLibVarSet)(char *var_name, char *value); + int (*BotLibVarSet)(const char *var_name, const char *value); //gets a library variable returns BLERR_ - int (*BotLibVarGet)(char *var_name, char *value, int size); + int (*BotLibVarGet)(const char *var_name, char *value, int size); //sets a C-like define returns BLERR_ int (*PC_AddGlobalDefine)(char *string); diff --git a/code/botlib/l_libvar.c b/code/botlib/l_libvar.c index 0270781fef..4020d0fe5a 100644 --- a/code/botlib/l_libvar.c +++ b/code/botlib/l_libvar.c @@ -42,7 +42,7 @@ libvar_t *libvarlist = NULL; // Returns: - // Changes Globals: - //=========================================================================== -float LibVarStringValue(char *string) +float LibVarStringValue(const char *string) { int dotfound = 0; float value = 0; @@ -80,7 +80,7 @@ float LibVarStringValue(char *string) // Returns: - // Changes Globals: - //=========================================================================== -libvar_t *LibVarAlloc(char *var_name) +libvar_t *LibVarAlloc(const char *var_name) { libvar_t *v; @@ -128,7 +128,7 @@ void LibVarDeAllocAll(void) // Returns: - // Changes Globals: - //=========================================================================== -libvar_t *LibVarGet(char *var_name) +libvar_t *LibVarGet(const char *var_name) { libvar_t *v; @@ -147,7 +147,7 @@ libvar_t *LibVarGet(char *var_name) // Returns: - // Changes Globals: - //=========================================================================== -char *LibVarGetString(char *var_name) +char *LibVarGetString(const char *var_name) { libvar_t *v; @@ -167,7 +167,7 @@ char *LibVarGetString(char *var_name) // Returns: - // Changes Globals: - //=========================================================================== -float LibVarGetValue(char *var_name) +float LibVarGetValue(const char *var_name) { libvar_t *v; @@ -187,7 +187,7 @@ float LibVarGetValue(char *var_name) // Returns: - // Changes Globals: - //=========================================================================== -libvar_t *LibVar(char *var_name, char *value) +libvar_t *LibVar(const char *var_name, const char *value) { libvar_t *v; v = LibVarGet(var_name); @@ -210,7 +210,7 @@ libvar_t *LibVar(char *var_name, char *value) // Returns: - // Changes Globals: - //=========================================================================== -char *LibVarString(char *var_name, char *value) +char *LibVarString(const char *var_name, const char *value) { libvar_t *v; @@ -223,7 +223,7 @@ char *LibVarString(char *var_name, char *value) // Returns: - // Changes Globals: - //=========================================================================== -float LibVarValue(char *var_name, char *value) +float LibVarValue(const char *var_name, const char *value) { libvar_t *v; @@ -236,7 +236,7 @@ float LibVarValue(char *var_name, char *value) // Returns: - // Changes Globals: - //=========================================================================== -void LibVarSet(char *var_name, char *value) +void LibVarSet(const char *var_name, const char *value) { libvar_t *v; @@ -263,7 +263,7 @@ void LibVarSet(char *var_name, char *value) // Returns: - // Changes Globals: - //=========================================================================== -qboolean LibVarChanged(char *var_name) +qboolean LibVarChanged(const char *var_name) { libvar_t *v; @@ -283,7 +283,7 @@ qboolean LibVarChanged(char *var_name) // Returns: - // Changes Globals: - //=========================================================================== -void LibVarSetNotModified(char *var_name) +void LibVarSetNotModified(const char *var_name) { libvar_t *v; diff --git a/code/botlib/l_libvar.h b/code/botlib/l_libvar.h index d96685f40d..531da04baa 100644 --- a/code/botlib/l_libvar.h +++ b/code/botlib/l_libvar.h @@ -43,21 +43,21 @@ typedef struct libvar_s //removes all library variables void LibVarDeAllocAll(void); //gets the library variable with the given name -libvar_t *LibVarGet(char *var_name); +libvar_t *LibVarGet(const char *var_name); //gets the string of the library variable with the given name -char *LibVarGetString(char *var_name); +char *LibVarGetString(const char *var_name); //gets the value of the library variable with the given name -float LibVarGetValue(char *var_name); +float LibVarGetValue(const char *var_name); //creates the library variable if not existing already and returns it -libvar_t *LibVar(char *var_name, char *value); +libvar_t *LibVar(const char *var_name, const char *value); //creates the library variable if not existing already and returns the value -float LibVarValue(char *var_name, char *value); +float LibVarValue(const char *var_name, const char *value); //creates the library variable if not existing already and returns the value string -char *LibVarString(char *var_name, char *value); +char *LibVarString(const char *var_name, const char *value); //sets the library variable -void LibVarSet(char *var_name, char *value); +void LibVarSet(const char *var_name, const char *value); //returns true if the library variable has been modified -qboolean LibVarChanged(char *var_name); +qboolean LibVarChanged(const char *var_name); //sets the library variable to unmodified -void LibVarSetNotModified(char *var_name); +void LibVarSetNotModified(const char *var_name); From 74aa4268b20f5b1a3ff8710cfc9ac1329d0b6934 Mon Sep 17 00:00:00 2001 From: Zack Middleton Date: Wed, 7 Jun 2017 19:00:28 -0500 Subject: [PATCH 065/240] Stop caching sv_maxclients in bot code --- code/game/ai_chat.c | 36 ++++++------------------------------ code/game/ai_cmd.c | 20 +++++--------------- code/game/ai_dmq3.c | 28 ++++++++++------------------ code/game/ai_main.c | 9 ++++----- code/game/ai_team.c | 18 +++--------------- 5 files changed, 28 insertions(+), 83 deletions(-) diff --git a/code/game/ai_chat.c b/code/game/ai_chat.c index 57166482b4..ecb0b9be12 100644 --- a/code/game/ai_chat.c +++ b/code/game/ai_chat.c @@ -68,13 +68,9 @@ BotNumActivePlayers int BotNumActivePlayers(void) { int i, num; char buf[MAX_INFO_STRING]; - static int maxclients; - - if (!maxclients) - maxclients = trap_Cvar_VariableIntegerValue("sv_maxclients"); num = 0; - for (i = 0; i < maxclients && i < MAX_CLIENTS; i++) { + for (i = 0; i < level.maxclients; i++) { trap_GetConfigstring(CS_PLAYERS+i, buf, sizeof(buf)); //if no config string or no name if (!strlen(buf) || !strlen(Info_ValueForKey(buf, "n"))) continue; @@ -94,14 +90,10 @@ BotIsFirstInRankings int BotIsFirstInRankings(bot_state_t *bs) { int i, score; char buf[MAX_INFO_STRING]; - static int maxclients; playerState_t ps; - if (!maxclients) - maxclients = trap_Cvar_VariableIntegerValue("sv_maxclients"); - score = bs->cur_ps.persistant[PERS_SCORE]; - for (i = 0; i < maxclients && i < MAX_CLIENTS; i++) { + for (i = 0; i < level.maxclients; i++) { trap_GetConfigstring(CS_PLAYERS+i, buf, sizeof(buf)); //if no config string or no name if (!strlen(buf) || !strlen(Info_ValueForKey(buf, "n"))) continue; @@ -122,14 +114,10 @@ BotIsLastInRankings int BotIsLastInRankings(bot_state_t *bs) { int i, score; char buf[MAX_INFO_STRING]; - static int maxclients; playerState_t ps; - if (!maxclients) - maxclients = trap_Cvar_VariableIntegerValue("sv_maxclients"); - score = bs->cur_ps.persistant[PERS_SCORE]; - for (i = 0; i < maxclients && i < MAX_CLIENTS; i++) { + for (i = 0; i < level.maxclients; i++) { trap_GetConfigstring(CS_PLAYERS+i, buf, sizeof(buf)); //if no config string or no name if (!strlen(buf) || !strlen(Info_ValueForKey(buf, "n"))) continue; @@ -151,15 +139,11 @@ char *BotFirstClientInRankings(void) { int i, bestscore, bestclient; char buf[MAX_INFO_STRING]; static char name[32]; - static int maxclients; playerState_t ps; - if (!maxclients) - maxclients = trap_Cvar_VariableIntegerValue("sv_maxclients"); - bestscore = -999999; bestclient = 0; - for (i = 0; i < maxclients && i < MAX_CLIENTS; i++) { + for (i = 0; i < level.maxclients; i++) { trap_GetConfigstring(CS_PLAYERS+i, buf, sizeof(buf)); //if no config string or no name if (!strlen(buf) || !strlen(Info_ValueForKey(buf, "n"))) continue; @@ -185,15 +169,11 @@ char *BotLastClientInRankings(void) { int i, worstscore, bestclient; char buf[MAX_INFO_STRING]; static char name[32]; - static int maxclients; playerState_t ps; - if (!maxclients) - maxclients = trap_Cvar_VariableIntegerValue("sv_maxclients"); - worstscore = 999999; bestclient = 0; - for (i = 0; i < maxclients && i < MAX_CLIENTS; i++) { + for (i = 0; i < level.maxclients; i++) { trap_GetConfigstring(CS_PLAYERS+i, buf, sizeof(buf)); //if no config string or no name if (!strlen(buf) || !strlen(Info_ValueForKey(buf, "n"))) continue; @@ -219,15 +199,11 @@ char *BotRandomOpponentName(bot_state_t *bs) { int i, count; char buf[MAX_INFO_STRING]; int opponents[MAX_CLIENTS], numopponents; - static int maxclients; static char name[32]; - if (!maxclients) - maxclients = trap_Cvar_VariableIntegerValue("sv_maxclients"); - numopponents = 0; opponents[0] = 0; - for (i = 0; i < maxclients && i < MAX_CLIENTS; i++) { + for (i = 0; i < level.maxclients; i++) { if (i == bs->client) continue; // trap_GetConfigstring(CS_PLAYERS+i, buf, sizeof(buf)); diff --git a/code/game/ai_cmd.c b/code/game/ai_cmd.c index db954858d8..2944fb06d3 100644 --- a/code/game/ai_cmd.c +++ b/code/game/ai_cmd.c @@ -237,15 +237,12 @@ FindClientByName int FindClientByName(char *name) { int i; char buf[MAX_INFO_STRING]; - static int maxclients; - if (!maxclients) - maxclients = trap_Cvar_VariableIntegerValue("sv_maxclients"); - for (i = 0; i < maxclients && i < MAX_CLIENTS; i++) { + for (i = 0; i < level.maxclients; i++) { ClientName(i, buf, sizeof(buf)); if (!Q_stricmp(buf, name)) return i; } - for (i = 0; i < maxclients && i < MAX_CLIENTS; i++) { + for (i = 0; i < level.maxclients; i++) { ClientName(i, buf, sizeof(buf)); if (stristr(buf, name)) return i; } @@ -260,16 +257,13 @@ FindEnemyByName int FindEnemyByName(bot_state_t *bs, char *name) { int i; char buf[MAX_INFO_STRING]; - static int maxclients; - if (!maxclients) - maxclients = trap_Cvar_VariableIntegerValue("sv_maxclients"); - for (i = 0; i < maxclients && i < MAX_CLIENTS; i++) { + for (i = 0; i < level.maxclients; i++) { if (BotSameTeam(bs, i)) continue; ClientName(i, buf, sizeof(buf)); if (!Q_stricmp(buf, name)) return i; } - for (i = 0; i < maxclients && i < MAX_CLIENTS; i++) { + for (i = 0; i < level.maxclients; i++) { if (BotSameTeam(bs, i)) continue; ClientName(i, buf, sizeof(buf)); if (stristr(buf, name)) return i; @@ -285,13 +279,9 @@ NumPlayersOnSameTeam int NumPlayersOnSameTeam(bot_state_t *bs) { int i, num; char buf[MAX_INFO_STRING]; - static int maxclients; - - if (!maxclients) - maxclients = trap_Cvar_VariableIntegerValue("sv_maxclients"); num = 0; - for (i = 0; i < maxclients && i < MAX_CLIENTS; i++) { + for (i = 0; i < level.maxclients; i++) { trap_GetConfigstring(CS_PLAYERS+i, buf, MAX_INFO_STRING); if (strlen(buf)) { if (BotSameTeam(bs, i+1)) num++; diff --git a/code/game/ai_dmq3.c b/code/game/ai_dmq3.c index 75749229db..6de1117baf 100644 --- a/code/game/ai_dmq3.c +++ b/code/game/ai_dmq3.c @@ -72,7 +72,6 @@ bot_waypoint_t *botai_freewaypoints; //NOTE: not using a cvars which can be updated because the game should be reloaded anyway int gametype; //game type -int maxclients; //maximum number of clients vmCvar_t bot_grapple; vmCvar_t bot_rocketjump; @@ -1427,11 +1426,8 @@ ClientFromName int ClientFromName(char *name) { int i; char buf[MAX_INFO_STRING]; - static int maxclients; - if (!maxclients) - maxclients = trap_Cvar_VariableIntegerValue("sv_maxclients"); - for (i = 0; i < maxclients && i < MAX_CLIENTS; i++) { + for (i = 0; i < level.maxclients; i++) { trap_GetConfigstring(CS_PLAYERS+i, buf, sizeof(buf)); Q_CleanStr( buf ); if (!Q_stricmp(Info_ValueForKey(buf, "n"), name)) return i; @@ -1447,11 +1443,8 @@ ClientOnSameTeamFromName int ClientOnSameTeamFromName(bot_state_t *bs, char *name) { int i; char buf[MAX_INFO_STRING]; - static int maxclients; - if (!maxclients) - maxclients = trap_Cvar_VariableIntegerValue("sv_maxclients"); - for (i = 0; i < maxclients && i < MAX_CLIENTS; i++) { + for (i = 0; i < level.maxclients; i++) { if (!BotSameTeam(bs, i)) continue; trap_GetConfigstring(CS_PLAYERS+i, buf, sizeof(buf)); @@ -2983,7 +2976,7 @@ int BotFindEnemy(bot_state_t *bs, int curenemy) { } #endif // - for (i = 0; i < maxclients && i < MAX_CLIENTS; i++) { + for (i = 0; i < level.maxclients; i++) { if (i == bs->client) continue; //if it's the current enemy @@ -3062,7 +3055,7 @@ int BotTeamFlagCarrierVisible(bot_state_t *bs) { float vis; aas_entityinfo_t entinfo; - for (i = 0; i < maxclients && i < MAX_CLIENTS; i++) { + for (i = 0; i < level.maxclients; i++) { if (i == bs->client) continue; // @@ -3095,7 +3088,7 @@ int BotTeamFlagCarrier(bot_state_t *bs) { int i; aas_entityinfo_t entinfo; - for (i = 0; i < maxclients && i < MAX_CLIENTS; i++) { + for (i = 0; i < level.maxclients; i++) { if (i == bs->client) continue; // @@ -3125,7 +3118,7 @@ int BotEnemyFlagCarrierVisible(bot_state_t *bs) { float vis; aas_entityinfo_t entinfo; - for (i = 0; i < maxclients && i < MAX_CLIENTS; i++) { + for (i = 0; i < level.maxclients; i++) { if (i == bs->client) continue; // @@ -3164,7 +3157,7 @@ void BotVisibleTeamMatesAndEnemies(bot_state_t *bs, int *teammates, int *enemies *teammates = 0; if (enemies) *enemies = 0; - for (i = 0; i < maxclients && i < MAX_CLIENTS; i++) { + for (i = 0; i < level.maxclients; i++) { if (i == bs->client) continue; // @@ -3206,7 +3199,7 @@ int BotTeamCubeCarrierVisible(bot_state_t *bs) { float vis; aas_entityinfo_t entinfo; - for (i = 0; i < maxclients && i < MAX_CLIENTS; i++) { + for (i = 0; i < level.maxclients; i++) { if (i == bs->client) continue; // BotEntityInfo(i, &entinfo); @@ -3235,7 +3228,7 @@ int BotEnemyCubeCarrierVisible(bot_state_t *bs) { float vis; aas_entityinfo_t entinfo; - for (i = 0; i < maxclients && i < MAX_CLIENTS; i++) { + for (i = 0; i < level.maxclients; i++) { if (i == bs->client) continue; // @@ -3706,7 +3699,7 @@ void BotMapScripts(bot_state_t *bs) { } shootbutton = qfalse; //if an enemy is below this bounding box then shoot the button - for (i = 0; i < maxclients && i < MAX_CLIENTS; i++) { + for (i = 0; i < level.maxclients; i++) { if (i == bs->client) continue; // @@ -5403,7 +5396,6 @@ void BotSetupDeathmatchAI(void) { char model[128]; gametype = trap_Cvar_VariableIntegerValue("g_gametype"); - maxclients = trap_Cvar_VariableIntegerValue("sv_maxclients"); trap_Cvar_Register(&bot_rocketjump, "bot_rocketjump", "1", 0); trap_Cvar_Register(&bot_grapple, "bot_grapple", "0", 0); diff --git a/code/game/ai_main.c b/code/game/ai_main.c index c6958318f8..a01e1530ee 100644 --- a/code/game/ai_main.c +++ b/code/game/ai_main.c @@ -392,7 +392,7 @@ void BotTeamplayReport(void) { char buf[MAX_INFO_STRING]; BotAI_Print(PRT_MESSAGE, S_COLOR_RED"RED\n"); - for (i = 0; i < maxclients && i < MAX_CLIENTS; i++) { + for (i = 0; i < level.maxclients; i++) { // if ( !botstates[i] || !botstates[i]->inuse ) continue; // @@ -405,7 +405,7 @@ void BotTeamplayReport(void) { } } BotAI_Print(PRT_MESSAGE, S_COLOR_BLUE"BLUE\n"); - for (i = 0; i < maxclients && i < MAX_CLIENTS; i++) { + for (i = 0; i < level.maxclients; i++) { // if ( !botstates[i] || !botstates[i]->inuse ) continue; // @@ -546,7 +546,7 @@ void BotUpdateInfoConfigStrings(void) { int i; char buf[MAX_INFO_STRING]; - for (i = 0; i < maxclients && i < MAX_CLIENTS; i++) { + for (i = 0; i < level.maxclients; i++) { // if ( !botstates[i] || !botstates[i]->inuse ) continue; @@ -1597,8 +1597,7 @@ int BotInitLibrary(void) { char buf[144]; //set the maxclients and maxentities library variables before calling BotSetupLibrary - trap_Cvar_VariableStringBuffer("sv_maxclients", buf, sizeof(buf)); - if (!strlen(buf)) strcpy(buf, "8"); + Com_sprintf(buf, sizeof(buf), "%d", level.maxclients); trap_BotLibVarSet("maxclients", buf); Com_sprintf(buf, sizeof(buf), "%d", MAX_GENTITIES); trap_BotLibVarSet("maxentities", buf); diff --git a/code/game/ai_team.c b/code/game/ai_team.c index 925a94d6b2..6176bd5505 100644 --- a/code/game/ai_team.c +++ b/code/game/ai_team.c @@ -83,13 +83,9 @@ BotNumTeamMates int BotNumTeamMates(bot_state_t *bs) { int i, numplayers; char buf[MAX_INFO_STRING]; - static int maxclients; - - if (!maxclients) - maxclients = trap_Cvar_VariableIntegerValue("sv_maxclients"); numplayers = 0; - for (i = 0; i < maxclients && i < MAX_CLIENTS; i++) { + for (i = 0; i < level.maxclients; i++) { trap_GetConfigstring(CS_PLAYERS+i, buf, sizeof(buf)); //if no config string or no name if (!strlen(buf) || !strlen(Info_ValueForKey(buf, "n"))) continue; @@ -127,7 +123,6 @@ int BotSortTeamMatesByBaseTravelTime(bot_state_t *bs, int *teammates, int maxtea int i, j, k, numteammates, traveltime; char buf[MAX_INFO_STRING]; - static int maxclients; int traveltimes[MAX_CLIENTS]; bot_goal_t *goal = NULL; @@ -150,11 +145,8 @@ int BotSortTeamMatesByBaseTravelTime(bot_state_t *bs, int *teammates, int maxtea goal = &blueobelisk; } #endif - if (!maxclients) - maxclients = trap_Cvar_VariableIntegerValue("sv_maxclients"); - numteammates = 0; - for (i = 0; i < maxclients && i < MAX_CLIENTS; i++) { + for (i = 0; i < level.maxclients; i++) { trap_GetConfigstring(CS_PLAYERS+i, buf, sizeof(buf)); //if no config string or no name if (!strlen(buf) || !strlen(Info_ValueForKey(buf, "n"))) continue; @@ -885,13 +877,9 @@ void BotTeamOrders(bot_state_t *bs) { int teammates[MAX_CLIENTS]; int numteammates, i; char buf[MAX_INFO_STRING]; - static int maxclients; - - if (!maxclients) - maxclients = trap_Cvar_VariableIntegerValue("sv_maxclients"); numteammates = 0; - for (i = 0; i < maxclients && i < MAX_CLIENTS; i++) { + for (i = 0; i < level.maxclients; i++) { trap_GetConfigstring(CS_PLAYERS+i, buf, sizeof(buf)); //if no config string or no name if (!strlen(buf) || !strlen(Info_ValueForKey(buf, "n"))) continue; From 6e340f9a5b185b58b80063655cd4cffd463b707c Mon Sep 17 00:00:00 2001 From: Zack Middleton Date: Wed, 7 Jun 2017 19:02:01 -0500 Subject: [PATCH 066/240] Don't use uninitialized ps from BotAI_GetClientState If BotAI_GetPlayerState returns qfalse, ps is untouched and in some cases means uninitialized. So don't use it if not valid. --- code/game/ai_chat.c | 12 ++++-------- code/game/ai_dmq3.c | 5 ++++- code/game/ai_main.c | 6 ++++-- code/game/ai_team.c | 8 ++++++-- 4 files changed, 18 insertions(+), 13 deletions(-) diff --git a/code/game/ai_chat.c b/code/game/ai_chat.c index ecb0b9be12..a65875ff5e 100644 --- a/code/game/ai_chat.c +++ b/code/game/ai_chat.c @@ -100,8 +100,7 @@ int BotIsFirstInRankings(bot_state_t *bs) { //skip spectators if (atoi(Info_ValueForKey(buf, "t")) == TEAM_SPECTATOR) continue; // - BotAI_GetClientState(i, &ps); - if (score < ps.persistant[PERS_SCORE]) return qfalse; + if (BotAI_GetClientState(i, &ps) && score < ps.persistant[PERS_SCORE]) return qfalse; } return qtrue; } @@ -124,8 +123,7 @@ int BotIsLastInRankings(bot_state_t *bs) { //skip spectators if (atoi(Info_ValueForKey(buf, "t")) == TEAM_SPECTATOR) continue; // - BotAI_GetClientState(i, &ps); - if (score > ps.persistant[PERS_SCORE]) return qfalse; + if (BotAI_GetClientState(i, &ps) && score > ps.persistant[PERS_SCORE]) return qfalse; } return qtrue; } @@ -150,8 +148,7 @@ char *BotFirstClientInRankings(void) { //skip spectators if (atoi(Info_ValueForKey(buf, "t")) == TEAM_SPECTATOR) continue; // - BotAI_GetClientState(i, &ps); - if (ps.persistant[PERS_SCORE] > bestscore) { + if (BotAI_GetClientState(i, &ps) && ps.persistant[PERS_SCORE] > bestscore) { bestscore = ps.persistant[PERS_SCORE]; bestclient = i; } @@ -180,8 +177,7 @@ char *BotLastClientInRankings(void) { //skip spectators if (atoi(Info_ValueForKey(buf, "t")) == TEAM_SPECTATOR) continue; // - BotAI_GetClientState(i, &ps); - if (ps.persistant[PERS_SCORE] < worstscore) { + if (BotAI_GetClientState(i, &ps) && ps.persistant[PERS_SCORE] < worstscore) { worstscore = ps.persistant[PERS_SCORE]; bestclient = i; } diff --git a/code/game/ai_dmq3.c b/code/game/ai_dmq3.c index 6de1117baf..3cc61f22bb 100644 --- a/code/game/ai_dmq3.c +++ b/code/game/ai_dmq3.c @@ -206,7 +206,10 @@ qboolean EntityIsDead(aas_entityinfo_t *entinfo) { if (entinfo->number >= 0 && entinfo->number < MAX_CLIENTS) { //retrieve the current client state - BotAI_GetClientState( entinfo->number, &ps ); + if (!BotAI_GetClientState(entinfo->number, &ps)) { + return qfalse; + } + if (ps.pm_type != PM_NORMAL) return qtrue; } return qfalse; diff --git a/code/game/ai_main.c b/code/game/ai_main.c index a01e1530ee..907a161d9f 100644 --- a/code/game/ai_main.c +++ b/code/game/ai_main.c @@ -990,8 +990,10 @@ int BotAI(int client, float thinktime) { } //retrieve the current client state - BotAI_GetClientState( client, &bs->cur_ps ); - + if (!BotAI_GetClientState(client, &bs->cur_ps)) { + BotAI_Print(PRT_FATAL, "BotAI: failed to get player state for player %d\n", client); + return qfalse; + } //retrieve any waiting server commands while( trap_BotGetServerCommand(client, buf, sizeof(buf)) ) { //have buf point to the command and args to the command arguments diff --git a/code/game/ai_team.c b/code/game/ai_team.c index 6176bd5505..95b445cd63 100644 --- a/code/game/ai_team.c +++ b/code/game/ai_team.c @@ -108,8 +108,12 @@ int BotClientTravelTimeToGoal(int client, bot_goal_t *goal) { playerState_t ps; int areanum; - BotAI_GetClientState(client, &ps); - areanum = BotPointAreaNum(ps.origin); + if (BotAI_GetClientState(client, &ps)) { + areanum = BotPointAreaNum(ps.origin); + } else { + areanum = 0; + } + if (!areanum) return 1; return trap_AAS_AreaTravelTimeToGoalArea(areanum, ps.origin, goal->areanum, TFL_DEFAULT); } From d58234a6c7475ea57ab5783dd0a0dd6ca5e54583 Mon Sep 17 00:00:00 2001 From: Zack Middleton Date: Wed, 7 Jun 2017 19:04:32 -0500 Subject: [PATCH 067/240] Fix 'missing token' in parsers for animations.cfg --- code/cgame/cg_players.c | 16 ++++++++-------- code/q3_ui/ui_players.c | 16 ++++++++-------- code/ui/ui_players.c | 16 ++++++++-------- 3 files changed, 24 insertions(+), 24 deletions(-) diff --git a/code/cgame/cg_players.c b/code/cgame/cg_players.c index 9c76aac3eb..500c63be6f 100644 --- a/code/cgame/cg_players.c +++ b/code/cgame/cg_players.c @@ -128,12 +128,12 @@ static qboolean CG_ParseAnimationFile( const char *filename, clientInfo_t *ci ) while ( 1 ) { prev = text_p; // so we can unget token = COM_Parse( &text_p ); - if ( !token ) { + if ( !token[0] ) { break; } if ( !Q_stricmp( token, "footsteps" ) ) { token = COM_Parse( &text_p ); - if ( !token ) { + if ( !token[0] ) { break; } if ( !Q_stricmp( token, "default" ) || !Q_stricmp( token, "normal" ) ) { @@ -153,7 +153,7 @@ static qboolean CG_ParseAnimationFile( const char *filename, clientInfo_t *ci ) } else if ( !Q_stricmp( token, "headoffset" ) ) { for ( i = 0 ; i < 3 ; i++ ) { token = COM_Parse( &text_p ); - if ( !token ) { + if ( !token[0] ) { break; } ci->headOffset[i] = atof( token ); @@ -161,7 +161,7 @@ static qboolean CG_ParseAnimationFile( const char *filename, clientInfo_t *ci ) continue; } else if ( !Q_stricmp( token, "sex" ) ) { token = COM_Parse( &text_p ); - if ( !token ) { + if ( !token[0] ) { break; } if ( token[0] == 'f' || token[0] == 'F' ) { @@ -192,7 +192,7 @@ static qboolean CG_ParseAnimationFile( const char *filename, clientInfo_t *ci ) for ( i = 0 ; i < MAX_ANIMATIONS ; i++ ) { token = COM_Parse( &text_p ); - if ( !*token ) { + if ( !token[0] ) { if( i >= TORSO_GETFLAG && i <= TORSO_NEGATIVE ) { animations[i].firstFrame = animations[TORSO_GESTURE].firstFrame; animations[i].frameLerp = animations[TORSO_GESTURE].frameLerp; @@ -215,7 +215,7 @@ static qboolean CG_ParseAnimationFile( const char *filename, clientInfo_t *ci ) } token = COM_Parse( &text_p ); - if ( !*token ) { + if ( !token[0] ) { break; } animations[i].numFrames = atoi( token ); @@ -229,13 +229,13 @@ static qboolean CG_ParseAnimationFile( const char *filename, clientInfo_t *ci ) } token = COM_Parse( &text_p ); - if ( !*token ) { + if ( !token[0] ) { break; } animations[i].loopFrames = atoi( token ); token = COM_Parse( &text_p ); - if ( !*token ) { + if ( !token[0] ) { break; } fps = atof( token ); diff --git a/code/q3_ui/ui_players.c b/code/q3_ui/ui_players.c index d8794cbb5c..b40ec90f7d 100644 --- a/code/q3_ui/ui_players.c +++ b/code/q3_ui/ui_players.c @@ -964,26 +964,26 @@ static qboolean UI_ParseAnimationFile( const char *filename, animation_t *animat while ( 1 ) { prev = text_p; // so we can unget token = COM_Parse( &text_p ); - if ( !token ) { + if ( !token[0] ) { break; } if ( !Q_stricmp( token, "footsteps" ) ) { token = COM_Parse( &text_p ); - if ( !token ) { + if ( !token[0] ) { break; } continue; } else if ( !Q_stricmp( token, "headoffset" ) ) { for ( i = 0 ; i < 3 ; i++ ) { token = COM_Parse( &text_p ); - if ( !token ) { + if ( !token[0] ) { break; } } continue; } else if ( !Q_stricmp( token, "sex" ) ) { token = COM_Parse( &text_p ); - if ( !token ) { + if ( !token[0] ) { break; } continue; @@ -1002,7 +1002,7 @@ static qboolean UI_ParseAnimationFile( const char *filename, animation_t *animat for ( i = 0 ; i < MAX_ANIMATIONS ; i++ ) { token = COM_Parse( &text_p ); - if ( !token ) { + if ( !token[0] ) { break; } animations[i].firstFrame = atoi( token ); @@ -1015,19 +1015,19 @@ static qboolean UI_ParseAnimationFile( const char *filename, animation_t *animat } token = COM_Parse( &text_p ); - if ( !token ) { + if ( !token[0] ) { break; } animations[i].numFrames = atoi( token ); token = COM_Parse( &text_p ); - if ( !token ) { + if ( !token[0] ) { break; } animations[i].loopFrames = atoi( token ); token = COM_Parse( &text_p ); - if ( !token ) { + if ( !token[0] ) { break; } fps = atof( token ); diff --git a/code/ui/ui_players.c b/code/ui/ui_players.c index 53475d310b..87812ef30d 100644 --- a/code/ui/ui_players.c +++ b/code/ui/ui_players.c @@ -1051,26 +1051,26 @@ static qboolean UI_ParseAnimationFile( const char *filename, animation_t *animat while ( 1 ) { prev = text_p; // so we can unget token = COM_Parse( &text_p ); - if ( !token ) { + if ( !token[0] ) { break; } if ( !Q_stricmp( token, "footsteps" ) ) { token = COM_Parse( &text_p ); - if ( !token ) { + if ( !token[0] ) { break; } continue; } else if ( !Q_stricmp( token, "headoffset" ) ) { for ( i = 0 ; i < 3 ; i++ ) { token = COM_Parse( &text_p ); - if ( !token ) { + if ( !token[0] ) { break; } } continue; } else if ( !Q_stricmp( token, "sex" ) ) { token = COM_Parse( &text_p ); - if ( !token ) { + if ( !token[0] ) { break; } continue; @@ -1089,7 +1089,7 @@ static qboolean UI_ParseAnimationFile( const char *filename, animation_t *animat for ( i = 0 ; i < MAX_ANIMATIONS ; i++ ) { token = COM_Parse( &text_p ); - if ( !token ) { + if ( !token[0] ) { break; } animations[i].firstFrame = atoi( token ); @@ -1102,19 +1102,19 @@ static qboolean UI_ParseAnimationFile( const char *filename, animation_t *animat } token = COM_Parse( &text_p ); - if ( !token ) { + if ( !token[0] ) { break; } animations[i].numFrames = atoi( token ); token = COM_Parse( &text_p ); - if ( !token ) { + if ( !token[0] ) { break; } animations[i].loopFrames = atoi( token ); token = COM_Parse( &text_p ); - if ( !token ) { + if ( !token[0] ) { break; } fps = atof( token ); From 1048073e2624102dff884c38d5f6417ae860b2f5 Mon Sep 17 00:00:00 2001 From: Zack Middleton Date: Wed, 7 Jun 2017 19:16:05 -0500 Subject: [PATCH 068/240] Unify checks for missing COM_Parse() token --- code/cgame/cg_main.c | 4 ++-- code/cgame/cg_servercmds.c | 10 +++++----- code/q3_ui/ui_startserver.c | 2 +- code/ui/ui_main.c | 14 +++++++------- 4 files changed, 15 insertions(+), 15 deletions(-) diff --git a/code/cgame/cg_main.c b/code/cgame/cg_main.c index d5ecd81f08..780ed3e771 100644 --- a/code/cgame/cg_main.c +++ b/code/cgame/cg_main.c @@ -1417,7 +1417,7 @@ qboolean CG_Load_Menu(char **p) { return qtrue; } - if ( !token || token[0] == 0 ) { + if (!token[0]) { return qfalse; } @@ -1464,7 +1464,7 @@ void CG_LoadMenus(const char *menuFile) { while ( 1 ) { token = COM_ParseExt( &p, qtrue ); - if( !token || token[0] == 0 || token[0] == '}') { + if (!token[0]) { break; } diff --git a/code/cgame/cg_servercmds.c b/code/cgame/cg_servercmds.c index e863edca23..d1c8b28b7b 100644 --- a/code/cgame/cg_servercmds.c +++ b/code/cgame/cg_servercmds.c @@ -566,7 +566,7 @@ int CG_ParseVoiceChats( const char *filename, voiceChatList_t *voiceChatList, in voiceChats[i].id[0] = 0; } token = COM_ParseExt(p, qtrue); - if (!token || token[0] == 0) { + if (!token[0]) { return qtrue; } if (!Q_stricmp(token, "female")) { @@ -586,7 +586,7 @@ int CG_ParseVoiceChats( const char *filename, voiceChatList_t *voiceChatList, in voiceChatList->numVoiceChats = 0; while ( 1 ) { token = COM_ParseExt(p, qtrue); - if (!token || token[0] == 0) { + if (!token[0]) { return qtrue; } Com_sprintf(voiceChats[voiceChatList->numVoiceChats].id, sizeof( voiceChats[voiceChatList->numVoiceChats].id ), "%s", token); @@ -598,7 +598,7 @@ int CG_ParseVoiceChats( const char *filename, voiceChatList_t *voiceChatList, in voiceChats[voiceChatList->numVoiceChats].numSounds = 0; while(1) { token = COM_ParseExt(p, qtrue); - if (!token || token[0] == 0) { + if (!token[0]) { return qtrue; } if (!Q_stricmp(token, "}")) @@ -606,7 +606,7 @@ int CG_ParseVoiceChats( const char *filename, voiceChatList_t *voiceChatList, in sound = trap_S_RegisterSound( token, compress ); voiceChats[voiceChatList->numVoiceChats].sounds[voiceChats[voiceChatList->numVoiceChats].numSounds] = sound; token = COM_ParseExt(p, qtrue); - if (!token || token[0] == 0) { + if (!token[0]) { return qtrue; } Com_sprintf(voiceChats[voiceChatList->numVoiceChats].chats[ @@ -674,7 +674,7 @@ int CG_HeadModelVoiceChats( char *filename ) { p = &ptr; token = COM_ParseExt(p, qtrue); - if (!token || token[0] == 0) { + if ( !token[0] ) { return -1; } diff --git a/code/q3_ui/ui_startserver.c b/code/q3_ui/ui_startserver.c index cb09a3b492..82865ad5f6 100644 --- a/code/q3_ui/ui_startserver.c +++ b/code/q3_ui/ui_startserver.c @@ -119,7 +119,7 @@ static int GametypeBits( char *string ) { p = string; while( 1 ) { token = COM_ParseExt( &p, qfalse ); - if( token[0] == 0 ) { + if ( !token[0] ) { break; } diff --git a/code/ui/ui_main.c b/code/ui/ui_main.c index bb43ec44b8..60170614fa 100644 --- a/code/ui/ui_main.c +++ b/code/ui/ui_main.c @@ -4549,7 +4549,7 @@ static qboolean Team_Parse(char **p) { return qtrue; } - if ( !token || token[0] == 0 ) { + if (!token[0]) { return qfalse; } @@ -4610,7 +4610,7 @@ static qboolean Character_Parse(char **p) { return qtrue; } - if ( !token || token[0] == 0 ) { + if (!token[0]) { return qfalse; } @@ -4666,7 +4666,7 @@ static qboolean Alias_Parse(char **p) { return qtrue; } - if ( !token || token[0] == 0 ) { + if (!token[0]) { return qfalse; } @@ -4715,7 +4715,7 @@ static void UI_ParseTeamInfo(const char *teamFile) { while ( 1 ) { token = COM_ParseExt( &p, qtrue ); - if( !token || token[0] == 0 || token[0] == '}') { + if (!token[0] || token[0] == '}') { break; } @@ -4767,7 +4767,7 @@ static qboolean GameType_Parse(char **p, qboolean join) { return qtrue; } - if ( !token || token[0] == 0 ) { + if (!token[0]) { return qfalse; } @@ -4824,7 +4824,7 @@ static qboolean MapList_Parse(char **p) { return qtrue; } - if ( !token || token[0] == 0 ) { + if (!token[0]) { return qfalse; } @@ -4885,7 +4885,7 @@ static void UI_ParseGameInfo(const char *teamFile) { while ( 1 ) { token = COM_ParseExt( &p, qtrue ); - if( !token || token[0] == 0 || token[0] == '}') { + if (!token[0] || token[0] == '}') { break; } From 1456008d961bc7cf3bc633e4cd7ebc981d408cc8 Mon Sep 17 00:00:00 2001 From: Zack Middleton Date: Wed, 7 Jun 2017 19:41:46 -0500 Subject: [PATCH 069/240] Fix comment for CG_SetInitialSnapshot --- code/cgame/cg_snapshot.c | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/code/cgame/cg_snapshot.c b/code/cgame/cg_snapshot.c index a2e018db6a..4ad77d613e 100644 --- a/code/cgame/cg_snapshot.c +++ b/code/cgame/cg_snapshot.c @@ -76,11 +76,8 @@ static void CG_TransitionEntity( centity_t *cent ) { ================== CG_SetInitialSnapshot -This will only happen on the very first snapshot, or -on tourney restarts. All other times will use -CG_TransitionSnapshot instead. - -FIXME: Also called by map_restart? +This will only happen on the very first snapshot. +All other times will use CG_TransitionSnapshot instead. ================== */ void CG_SetInitialSnapshot( snapshot_t *snap ) { From c96acec4286625f3cdf6a28b6341802139aa4144 Mon Sep 17 00:00:00 2001 From: Zack Middleton Date: Wed, 7 Jun 2017 19:42:17 -0500 Subject: [PATCH 070/240] Fix (unused) check for map restart in CG_TransitionSnapshot --- code/cgame/cg_snapshot.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/code/cgame/cg_snapshot.c b/code/cgame/cg_snapshot.c index 4ad77d613e..3a70559036 100644 --- a/code/cgame/cg_snapshot.c +++ b/code/cgame/cg_snapshot.c @@ -138,8 +138,7 @@ static void CG_TransitionSnapshot( void ) { CG_ExecuteNewServerCommands( cg.nextSnap->serverCommandSequence ); // if we had a map_restart, set everthing with initial - if ( !cg.snap ) { - return; + if ( cg.mapRestart ) { } // clear the currentValid flag for all entities in the existing snapshot From b511b8f2f6690fa6e8268acfbb020aeff974413f Mon Sep 17 00:00:00 2001 From: Zack Middleton Date: Wed, 7 Jun 2017 19:51:32 -0500 Subject: [PATCH 071/240] Fix Coverity warning that endVelocity is uninitialized --- code/game/bg_slidemove.c | 27 +++++++++++++++++++-------- 1 file changed, 19 insertions(+), 8 deletions(-) diff --git a/code/game/bg_slidemove.c b/code/game/bg_slidemove.c index 0806b2fa57..9228ada446 100644 --- a/code/game/bg_slidemove.c +++ b/code/game/bg_slidemove.c @@ -158,8 +158,10 @@ qboolean PM_SlideMove( qboolean gravity ) { // slide along the plane PM_ClipVelocity (pm->ps->velocity, planes[i], clipVelocity, OVERCLIP ); - // slide along the plane - PM_ClipVelocity (endVelocity, planes[i], endClipVelocity, OVERCLIP ); + if ( gravity ) { + // slide along the plane + PM_ClipVelocity (endVelocity, planes[i], endClipVelocity, OVERCLIP ); + } // see if there is a second plane that the new move enters for ( j = 0 ; j < numplanes ; j++ ) { @@ -172,7 +174,10 @@ qboolean PM_SlideMove( qboolean gravity ) { // try clipping the move to the plane PM_ClipVelocity( clipVelocity, planes[j], clipVelocity, OVERCLIP ); - PM_ClipVelocity( endClipVelocity, planes[j], endClipVelocity, OVERCLIP ); + + if ( gravity ) { + PM_ClipVelocity( endClipVelocity, planes[j], endClipVelocity, OVERCLIP ); + } // see if it goes back into the first clip plane if ( DotProduct( clipVelocity, planes[i] ) >= 0 ) { @@ -185,10 +190,12 @@ qboolean PM_SlideMove( qboolean gravity ) { d = DotProduct( dir, pm->ps->velocity ); VectorScale( dir, d, clipVelocity ); - CrossProduct (planes[i], planes[j], dir); - VectorNormalize( dir ); - d = DotProduct( dir, endVelocity ); - VectorScale( dir, d, endClipVelocity ); + if ( gravity ) { + CrossProduct (planes[i], planes[j], dir); + VectorNormalize( dir ); + d = DotProduct( dir, endVelocity ); + VectorScale( dir, d, endClipVelocity ); + } // see if there is a third plane the the new move enters for ( k = 0 ; k < numplanes ; k++ ) { @@ -207,7 +214,11 @@ qboolean PM_SlideMove( qboolean gravity ) { // if we have fixed all interactions, try another move VectorCopy( clipVelocity, pm->ps->velocity ); - VectorCopy( endClipVelocity, endVelocity ); + + if ( gravity ) { + VectorCopy( endClipVelocity, endVelocity ); + } + break; } } From 91acf8a681e8661a80f13e0a82255cb52c56cf79 Mon Sep 17 00:00:00 2001 From: Zack Middleton Date: Wed, 7 Jun 2017 19:54:34 -0500 Subject: [PATCH 072/240] Don't build score info for bots, they don't parse it --- code/game/g_cmds.c | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/code/game/g_cmds.c b/code/game/g_cmds.c index df74ef06b0..dde25d9174 100644 --- a/code/game/g_cmds.c +++ b/code/game/g_cmds.c @@ -40,6 +40,11 @@ void DeathmatchScoreboardMessage( gentity_t *ent ) { gclient_t *cl; int numSorted, scoreFlags, accuracy, perfect; + // don't send scores to bots, they don't parse it + if ( ent->r.svFlags & SVF_BOT ) { + return; + } + // send the latest information on all clients string[0] = 0; stringlength = 0; From eeb28dc1d0d7046a0176e926de3907b5b9cf3c96 Mon Sep 17 00:00:00 2001 From: Zack Middleton Date: Wed, 7 Jun 2017 19:55:34 -0500 Subject: [PATCH 073/240] Fix score info being dropped by server Server drops reliable command string if it's length is more than 1022. --- code/game/g_cmds.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/code/game/g_cmds.c b/code/game/g_cmds.c index dde25d9174..70681c7cbb 100644 --- a/code/game/g_cmds.c +++ b/code/game/g_cmds.c @@ -34,7 +34,7 @@ DeathmatchScoreboardMessage */ void DeathmatchScoreboardMessage( gentity_t *ent ) { char entry[1024]; - char string[1400]; + char string[1000]; int stringlength; int i, j; gclient_t *cl; From 71512bb1fd8c955011cc82fe93d69d69c1575721 Mon Sep 17 00:00:00 2001 From: Zack Middleton Date: Wed, 7 Jun 2017 20:15:51 -0500 Subject: [PATCH 074/240] Show client's name in callvote clientkick vote display message Make callvote always kick by client num so player can't rename to avoid being kicked. Don't allow calling a vote to kick host or non-existent players. --- code/game/g_cmds.c | 59 +++++++++++++++++++++++++++++----------------- 1 file changed, 38 insertions(+), 21 deletions(-) diff --git a/code/game/g_cmds.c b/code/game/g_cmds.c index 70681c7cbb..7606edbeea 100644 --- a/code/game/g_cmds.c +++ b/code/game/g_cmds.c @@ -192,31 +192,35 @@ Returns a player number for either a number or name string Returns -1 if invalid ================== */ -int ClientNumberFromString( gentity_t *to, char *s ) { +int ClientNumberFromString( gentity_t *to, char *s, qboolean checkNums, qboolean checkNames ) { gclient_t *cl; int idnum; char cleanName[MAX_STRING_CHARS]; - // numeric values could be slot numbers - if ( StringIsInteger( s ) ) { - idnum = atoi( s ); - if ( idnum >= 0 && idnum < level.maxclients ) { - cl = &level.clients[idnum]; - if ( cl->pers.connected == CON_CONNECTED ) { - return idnum; + if ( checkNums ) { + // numeric values could be slot numbers + if ( StringIsInteger( s ) ) { + idnum = atoi( s ); + if ( idnum >= 0 && idnum < level.maxclients ) { + cl = &level.clients[idnum]; + if ( cl->pers.connected == CON_CONNECTED ) { + return idnum; + } } } } - // check for a name match - for ( idnum=0,cl=level.clients ; idnum < level.maxclients ; idnum++,cl++ ) { - if ( cl->pers.connected != CON_CONNECTED ) { - continue; - } - Q_strncpyz(cleanName, cl->pers.netname, sizeof(cleanName)); - Q_CleanStr(cleanName); - if ( !Q_stricmp( cleanName, s ) ) { - return idnum; + if ( checkNames ) { + // check for a name match + for ( idnum=0,cl=level.clients ; idnum < level.maxclients ; idnum++,cl++ ) { + if ( cl->pers.connected != CON_CONNECTED ) { + continue; + } + Q_strncpyz(cleanName, cl->pers.netname, sizeof(cleanName)); + Q_CleanStr(cleanName); + if ( !Q_stricmp( cleanName, s ) ) { + return idnum; + } } } @@ -734,7 +738,7 @@ void Cmd_Follow_f( gentity_t *ent ) { } trap_Argv( 1, arg, sizeof( arg ) ); - i = ClientNumberFromString( ent, arg ); + i = ClientNumberFromString( ent, arg, qtrue, qtrue ); if ( i == -1 ) { return; } @@ -966,7 +970,7 @@ static void Cmd_Tell_f( gentity_t *ent ) { } trap_Argv( 1, arg, sizeof( arg ) ); - targetNum = ClientNumberFromString( ent, arg ); + targetNum = ClientNumberFromString( ent, arg, qtrue, qtrue ); if ( targetNum == -1 ) { return; } @@ -1092,7 +1096,7 @@ static void Cmd_VoiceTell_f( gentity_t *ent, qboolean voiceonly ) { } trap_Argv( 1, arg, sizeof( arg ) ); - targetNum = ClientNumberFromString( ent, arg ); + targetNum = ClientNumberFromString( ent, arg, qtrue, qtrue ); if ( targetNum == -1 ) { return; } @@ -1221,7 +1225,7 @@ void Cmd_GameCommand_f( gentity_t *ent ) { } trap_Argv( 1, arg, sizeof( arg ) ); - targetNum = ClientNumberFromString( ent, arg ); + targetNum = ClientNumberFromString( ent, arg, qtrue, qtrue ); if ( targetNum == -1 ) { return; } @@ -1365,6 +1369,19 @@ void Cmd_CallVote_f( gentity_t *ent ) { } Com_sprintf( level.voteString, sizeof( level.voteString ), "vstr nextmap"); Com_sprintf( level.voteDisplayString, sizeof( level.voteDisplayString ), "%s", level.voteString ); + } else if ( !Q_stricmp( arg1, "clientkick" ) || !Q_stricmp( arg1, "kick" ) ) { + i = ClientNumberFromString( ent, arg2, !Q_stricmp( arg1, "clientkick" ), !Q_stricmp( arg1, "kick" ) ); + if ( i == -1 ) { + return; + } + + if ( level.clients[i].pers.localClient ) { + trap_SendServerCommand( ent - g_entities, "print \"Cannot kick host player.\n\"" ); + return; + } + + Com_sprintf( level.voteString, sizeof( level.voteString ), "clientkick %d", i ); + Com_sprintf( level.voteDisplayString, sizeof( level.voteDisplayString ), "kick %s", level.clients[i].pers.netname ); } else { Com_sprintf( level.voteString, sizeof( level.voteString ), "%s \"%s\"", arg1, arg2 ); Com_sprintf( level.voteDisplayString, sizeof( level.voteDisplayString ), "%s", level.voteString ); From 5b9302a7efe480c5ee97f3a5caa20a32d4866252 Mon Sep 17 00:00:00 2001 From: Zack Middleton Date: Wed, 7 Jun 2017 20:32:57 -0500 Subject: [PATCH 075/240] Don't start game entity loops at index 1 --- code/game/g_main.c | 2 +- code/game/g_svcmds.c | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/code/game/g_main.c b/code/game/g_main.c index af88d3498b..0928762328 100644 --- a/code/game/g_main.c +++ b/code/game/g_main.c @@ -275,7 +275,7 @@ void G_FindTeams( void ) { c = 0; c2 = 0; - for ( i=1, e=g_entities+i ; i < level.num_entities ; i++,e++ ){ + for ( i=MAX_CLIENTS, e=g_entities+i ; i < level.num_entities ; i++,e++ ) { if (!e->inuse) continue; if (!e->team) diff --git a/code/game/g_svcmds.c b/code/game/g_svcmds.c index 755a7da390..3d37715629 100644 --- a/code/game/g_svcmds.c +++ b/code/game/g_svcmds.c @@ -320,8 +320,8 @@ void Svcmd_EntityList_f (void) { int e; gentity_t *check; - check = g_entities+1; - for (e = 1; e < level.num_entities ; e++, check++) { + check = g_entities; + for (e = 0; e < level.num_entities ; e++, check++) { if ( !check->inuse ) { continue; } From 1a8bf792e7555463072b36a969350198a880d834 Mon Sep 17 00:00:00 2001 From: Zack Middleton Date: Wed, 7 Jun 2017 20:38:12 -0500 Subject: [PATCH 076/240] Range check client number for trap_BotUserCommand --- code/server/sv_game.c | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/code/server/sv_game.c b/code/server/sv_game.c index 81f47933ea..161d975e63 100644 --- a/code/server/sv_game.c +++ b/code/server/sv_game.c @@ -465,7 +465,13 @@ intptr_t SV_GameSystemCalls( intptr_t *args ) { case BOTLIB_GET_CONSOLE_MESSAGE: return SV_BotGetConsoleMessage( args[1], VMA(2), args[3] ); case BOTLIB_USER_COMMAND: - SV_ClientThink( &svs.clients[args[1]], VMA(2) ); + { + int clientNum = args[1]; + + if ( clientNum >= 0 && clientNum < sv_maxclients->integer ) { + SV_ClientThink( &svs.clients[clientNum], VMA(2) ); + } + } return 0; case BOTLIB_AAS_BBOX_AREAS: From a738cb95926a78f23716e8310dedf07dab17a2e0 Mon Sep 17 00:00:00 2001 From: Zack Middleton Date: Wed, 7 Jun 2017 20:40:14 -0500 Subject: [PATCH 077/240] Fix overdraw in CG_DrawRect It was noticeable in the corners when alpha was less than 1. --- code/cgame/cg_drawtools.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/code/cgame/cg_drawtools.c b/code/cgame/cg_drawtools.c index c0ce1ef8ae..fb9e29a4f3 100644 --- a/code/cgame/cg_drawtools.c +++ b/code/cgame/cg_drawtools.c @@ -91,7 +91,7 @@ void CG_DrawRect( float x, float y, float width, float height, float size, const trap_R_SetColor( color ); CG_DrawTopBottom(x, y, width, height, size); - CG_DrawSides(x, y, width, height, size); + CG_DrawSides(x, y + size, width, height - size * 2, size); trap_R_SetColor( NULL ); } From 730b91705955c5cd1cff4ee9264998012db8dfa6 Mon Sep 17 00:00:00 2001 From: Zack Middleton Date: Wed, 7 Jun 2017 20:53:35 -0500 Subject: [PATCH 078/240] Fix comment in BotAIPredictObstacles --- code/game/ai_dmq3.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/code/game/ai_dmq3.c b/code/game/ai_dmq3.c index 3cc61f22bb..d87b20c90c 100644 --- a/code/game/ai_dmq3.c +++ b/code/game/ai_dmq3.c @@ -4556,7 +4556,7 @@ int BotAIPredictObstacles(bot_state_t *bs, bot_goal_t *goal) { bs->predictobstacles_goalareanum = goal->areanum; bs->predictobstacles_time = FloatTime(); - // predict at most 100 areas or 10 seconds ahead + // predict at most 100 areas or 1 second ahead trap_AAS_PredictRoute(&route, bs->areanum, bs->origin, goal->areanum, bs->tfl, 100, 1000, RSE_USETRAVELTYPE|RSE_ENTERCONTENTS, From 8a6c9d10389eb9bab17ac589128827a86055b0cf Mon Sep 17 00:00:00 2001 From: Zack Middleton Date: Wed, 7 Jun 2017 20:55:15 -0500 Subject: [PATCH 079/240] Remove unneeded 'angles' variables/clearing in ai_dmq3.c --- code/game/ai_dmq3.c | 10 +++------- 1 file changed, 3 insertions(+), 7 deletions(-) diff --git a/code/game/ai_dmq3.c b/code/game/ai_dmq3.c index d87b20c90c..a18df550fa 100644 --- a/code/game/ai_dmq3.c +++ b/code/game/ai_dmq3.c @@ -3830,7 +3830,6 @@ int BotFuncButtonActivateGoal(bot_state_t *bs, int bspent, bot_activategoal_t *a modelindex = atoi(model+1); if (!modelindex) return qfalse; - VectorClear(angles); entitynum = BotModelMinsMaxs(modelindex, ET_MOVER, 0, mins, maxs); //get the lip of the button trap_AAS_FloatForBSPEpairKey(bspent, "lip", &lip); @@ -3968,7 +3967,7 @@ BotFuncDoorGoal int BotFuncDoorActivateGoal(bot_state_t *bs, int bspent, bot_activategoal_t *activategoal) { int modelindex, entitynum; char model[MAX_INFO_STRING]; - vec3_t mins, maxs, origin, angles; + vec3_t mins, maxs, origin; //shoot at the shootable door trap_AAS_ValueForBSPEpairKey(bspent, "model", model, sizeof(model)); @@ -3977,7 +3976,6 @@ int BotFuncDoorActivateGoal(bot_state_t *bs, int bspent, bot_activategoal_t *act modelindex = atoi(model+1); if (!modelindex) return qfalse; - VectorClear(angles); entitynum = BotModelMinsMaxs(modelindex, ET_MOVER, 0, mins, maxs); //door origin VectorAdd(mins, maxs, origin); @@ -4003,7 +4001,7 @@ BotTriggerMultipleGoal int BotTriggerMultipleActivateGoal(bot_state_t *bs, int bspent, bot_activategoal_t *activategoal) { int i, areas[10], numareas, modelindex, entitynum; char model[128]; - vec3_t start, end, mins, maxs, angles; + vec3_t start, end, mins, maxs; vec3_t origin, goalorigin; activategoal->shoot = qfalse; @@ -4015,7 +4013,6 @@ int BotTriggerMultipleActivateGoal(bot_state_t *bs, int bspent, bot_activategoal modelindex = atoi(model+1); if (!modelindex) return qfalse; - VectorClear(angles); entitynum = BotModelMinsMaxs(modelindex, 0, CONTENTS_TRIGGER, mins, maxs); //trigger origin VectorAdd(mins, maxs, origin); @@ -4163,7 +4160,7 @@ int BotGetActivateGoal(bot_state_t *bs, int entitynum, bot_activategoal_t *activ char targetname[10][128]; aas_entityinfo_t entinfo; aas_areainfo_t areainfo; - vec3_t origin, angles, absmins, absmaxs; + vec3_t origin, absmins, absmaxs; memset(activategoal, 0, sizeof(bot_activategoal_t)); BotEntityInfo(entitynum, &entinfo); @@ -4207,7 +4204,6 @@ int BotGetActivateGoal(bot_state_t *bs, int entitynum, bot_activategoal_t *activ if (*model) { modelindex = atoi(model+1); if (modelindex) { - VectorClear(angles); BotModelMinsMaxs(modelindex, ET_MOVER, 0, absmins, absmaxs); // numareas = trap_AAS_BBoxAreas(absmins, absmaxs, areas, MAX_ACTIVATEAREAS*2); From 74a59f17c7afb6e427df22a25e44327b7b6e0739 Mon Sep 17 00:00:00 2001 From: Zack Middleton Date: Wed, 7 Jun 2017 21:07:34 -0500 Subject: [PATCH 080/240] Don't copy p->org to itself in cg_particles.c --- code/cgame/cg_particles.c | 4 ---- 1 file changed, 4 deletions(-) diff --git a/code/cgame/cg_particles.c b/code/cgame/cg_particles.c index 53507ad55b..2c70281f96 100644 --- a/code/cgame/cg_particles.c +++ b/code/cgame/cg_particles.c @@ -1027,10 +1027,6 @@ void CG_ParticleSnowFlurry (qhandle_t pshader, centity_t *cent) VectorCopy(cent->currentState.origin, p->org); - p->org[0] = p->org[0]; - p->org[1] = p->org[1]; - p->org[2] = p->org[2]; - p->vel[0] = p->vel[1] = 0; p->accel[0] = p->accel[1] = p->accel[2] = 0; From 9aae0948abeaf3e3507c6f070ca7ff929ea46a2c Mon Sep 17 00:00:00 2001 From: Zack Middleton Date: Wed, 7 Jun 2017 21:11:38 -0500 Subject: [PATCH 081/240] Remove unused define MAX_COMMANDARGUMENTS from be_ea.c --- code/botlib/be_ea.c | 1 - 1 file changed, 1 deletion(-) diff --git a/code/botlib/be_ea.c b/code/botlib/be_ea.c index 41653fdaa9..efecd28df6 100644 --- a/code/botlib/be_ea.c +++ b/code/botlib/be_ea.c @@ -39,7 +39,6 @@ Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA #include "be_ea.h" #define MAX_USERMOVE 400 -#define MAX_COMMANDARGUMENTS 10 bot_input_t *botinputs; From c99281a0da0df95f6bb8a26676c9c5afdbaa995d Mon Sep 17 00:00:00 2001 From: Zack Middleton Date: Wed, 7 Jun 2017 21:11:50 -0500 Subject: [PATCH 082/240] Make bots stop attacking player after disconnect Bots did not notice player disconnected, so they kept attacking the last known position. Checking if entity is valid in BotEntityVisible might fix other similar issues too. --- code/game/ai_dmq3.c | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/code/game/ai_dmq3.c b/code/game/ai_dmq3.c index a18df550fa..8f22e2f571 100644 --- a/code/game/ai_dmq3.c +++ b/code/game/ai_dmq3.c @@ -2830,8 +2830,12 @@ float BotEntityVisible(int viewer, vec3_t eye, vec3_t viewangles, float fov, int aas_entityinfo_t entinfo; vec3_t dir, entangles, start, end, middle; - //calculate middle of bounding box BotEntityInfo(ent, &entinfo); + if (!entinfo.valid) { + return 0; + } + + //calculate middle of bounding box VectorAdd(entinfo.mins, entinfo.maxs, middle); VectorScale(middle, 0.5, middle); VectorAdd(entinfo.origin, middle, middle); From 8956ab41dd67c9eff889821bec1e1b13ea012568 Mon Sep 17 00:00:00 2001 From: Tobias Kuehnhammer Date: Wed, 7 Jun 2017 21:19:42 -0500 Subject: [PATCH 083/240] Fix notarget cheat Bots no longer target players in 'no target mode'. --- code/game/ai_dmq3.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/code/game/ai_dmq3.c b/code/game/ai_dmq3.c index 8f22e2f571..5198cb3d7a 100644 --- a/code/game/ai_dmq3.c +++ b/code/game/ai_dmq3.c @@ -2988,6 +2988,10 @@ int BotFindEnemy(bot_state_t *bs, int curenemy) { if (i == bs->client) continue; //if it's the current enemy if (i == curenemy) continue; + //if the enemy has targeting disabled + if (g_entities[i].flags & FL_NOTARGET) { + continue; + } // BotEntityInfo(i, &entinfo); // From c12b81a2734dc661d68e384c7188349eb1ef58a9 Mon Sep 17 00:00:00 2001 From: Zack Middleton Date: Wed, 7 Jun 2017 21:44:59 -0500 Subject: [PATCH 084/240] Fix strncpy usage in botlib All usage of strncpy in botlib should now either set string terminator or use Q_strncpyz. --- code/botlib/be_aas_main.c | 2 +- code/botlib/be_ai_chat.c | 8 ++++---- code/botlib/be_ai_goal.c | 7 +++---- code/botlib/be_ai_weap.c | 2 +- code/botlib/l_log.c | 2 +- code/botlib/l_precomp.c | 2 +- code/botlib/l_script.c | 2 +- code/botlib/l_struct.c | 2 +- 8 files changed, 13 insertions(+), 14 deletions(-) diff --git a/code/botlib/be_aas_main.c b/code/botlib/be_aas_main.c index 08f821359c..f4ef606ecd 100644 --- a/code/botlib/be_aas_main.c +++ b/code/botlib/be_aas_main.c @@ -238,7 +238,7 @@ int AAS_LoadFiles(const char *mapname) return errnum; botimport.Print(PRT_MESSAGE, "loaded %s\n", aasfile); - strncpy(aasworld.filename, aasfile, MAX_PATH); + Q_strncpyz(aasworld.filename, aasfile, sizeof(aasworld.filename)); return BLERR_NOERROR; } //end of the function AAS_LoadFiles //=========================================================================== diff --git a/code/botlib/be_ai_chat.c b/code/botlib/be_ai_chat.c index e90aaddc39..2413706b45 100644 --- a/code/botlib/be_ai_chat.c +++ b/code/botlib/be_ai_chat.c @@ -342,7 +342,7 @@ void BotQueueConsoleMessage(int chatstate, int type, char *message) m->handle = cs->handle; m->time = AAS_Time(); m->type = type; - strncpy(m->message, message, MAX_MESSAGE_SIZE); + Q_strncpyz(m->message, message, MAX_MESSAGE_SIZE); m->next = NULL; if (cs->lastmessage) { @@ -1456,7 +1456,7 @@ int BotFindMatch(char *str, bot_match_t *match, unsigned long int context) int i; bot_matchtemplate_t *ms; - strncpy(match->string, str, MAX_MESSAGE_SIZE); + Q_strncpyz(match->string, str, MAX_MESSAGE_SIZE); //remove any trailing enters while(strlen(match->string) && match->string[strlen(match->string)-1] == '\n') @@ -2114,7 +2114,7 @@ bot_chat_t *BotLoadInitialChat(char *chatfile, char *chatname) if (pass && ptr) { chattype = (bot_chattype_t *) ptr; - strncpy(chattype->name, token.string, MAX_CHATTYPE_NAME); + Q_strncpyz(chattype->name, token.string, MAX_CHATTYPE_NAME); chattype->firstchatmessage = NULL; //add the chat type to the chat chattype->next = chat->types; @@ -2884,7 +2884,7 @@ void BotSetChatName(int chatstate, char *name, int client) if (!cs) return; cs->client = client; Com_Memset(cs->name, 0, sizeof(cs->name)); - strncpy(cs->name, name, sizeof(cs->name)); + strncpy(cs->name, name, sizeof(cs->name)-1); cs->name[sizeof(cs->name)-1] = '\0'; } //end of the function BotSetChatName //=========================================================================== diff --git a/code/botlib/be_ai_goal.c b/code/botlib/be_ai_goal.c index 3a5d01abb3..7c56b56e11 100644 --- a/code/botlib/be_ai_goal.c +++ b/code/botlib/be_ai_goal.c @@ -281,7 +281,7 @@ itemconfig_t *LoadItemConfig(char *filename) LibVarSet( "max_iteminfo", "256" ); } - strncpy( path, filename, MAX_PATH ); + Q_strncpyz(path, filename, sizeof(path)); PC_SetBaseFolder(BOTFILESBASEFOLDER); source = LoadSourceFile( path ); if( !source ) { @@ -314,7 +314,7 @@ itemconfig_t *LoadItemConfig(char *filename) return NULL; } //end if StripDoubleQuotes(token.string); - strncpy(ii->classname, token.string, sizeof(ii->classname)-1); + Q_strncpyz(ii->classname, token.string, sizeof(ii->classname)); if (!ReadStructure(source, &iteminfo_struct, (char *) ii)) { FreeMemory(ic); @@ -685,8 +685,7 @@ void BotGoalName(int number, char *name, int size) { if (li->number == number) { - strncpy(name, itemconfig->iteminfo[li->iteminfo].name, size-1); - name[size-1] = '\0'; + Q_strncpyz(name, itemconfig->iteminfo[li->iteminfo].name, size); return; } //end for } //end for diff --git a/code/botlib/be_ai_weap.c b/code/botlib/be_ai_weap.c index 8fab4d7987..88fb197c16 100644 --- a/code/botlib/be_ai_weap.c +++ b/code/botlib/be_ai_weap.c @@ -219,7 +219,7 @@ weaponconfig_t *LoadWeaponConfig(char *filename) max_projectileinfo = 32; LibVarSet("max_projectileinfo", "32"); } //end if - strncpy(path, filename, MAX_PATH); + Q_strncpyz(path, filename, sizeof(path)); PC_SetBaseFolder(BOTFILESBASEFOLDER); source = LoadSourceFile(path); if (!source) diff --git a/code/botlib/l_log.c b/code/botlib/l_log.c index ee25604e06..ba51e0081d 100644 --- a/code/botlib/l_log.c +++ b/code/botlib/l_log.c @@ -75,7 +75,7 @@ void Log_Open(char *filename) botimport.Print(PRT_ERROR, "can't open the log file %s\n", filename); return; } //end if - strncpy(logfile.filename, filename, MAX_LOGFILENAMESIZE); + Q_strncpyz(logfile.filename, filename, MAX_LOGFILENAMESIZE); botimport.Print(PRT_MESSAGE, "Opened log %s\n", logfile.filename); } //end of the function Log_Create //=========================================================================== diff --git a/code/botlib/l_precomp.c b/code/botlib/l_precomp.c index 4ffacad958..5c866b2ded 100644 --- a/code/botlib/l_precomp.c +++ b/code/botlib/l_precomp.c @@ -1323,7 +1323,7 @@ define_t *PC_DefineFromString(char *string) script = LoadScriptMemory(string, strlen(string), "*extern"); //create a new source Com_Memset(&src, 0, sizeof(source_t)); - strncpy(src.filename, "*extern", sizeof(src.filename) - 1); + Q_strncpyz(src.filename, "*extern", sizeof(src.filename)); src.scriptstack = script; #if DEFINEHASHING src.definehash = GetClearedMemory(DEFINEHASHSIZE * sizeof(define_t *)); diff --git a/code/botlib/l_script.c b/code/botlib/l_script.c index ee9cddc0f8..23694386fe 100644 --- a/code/botlib/l_script.c +++ b/code/botlib/l_script.c @@ -804,7 +804,7 @@ int PS_ReadPunctuation(script_t *script, token_t *token) //if the script contains the punctuation if (!strncmp(script->script_p, p, len)) { - strncpy(token->string, p, MAX_TOKEN); + Q_strncpyz(token->string, p, MAX_TOKEN); script->script_p += len; token->type = TT_PUNCTUATION; //sub type is the number of the punctuation diff --git a/code/botlib/l_struct.c b/code/botlib/l_struct.c index 0983b4d43a..0d5d6b01e1 100644 --- a/code/botlib/l_struct.c +++ b/code/botlib/l_struct.c @@ -221,7 +221,7 @@ int ReadString(source_t *source, fielddef_t *fd, void *p) //remove the double quotes StripDoubleQuotes(token.string); //copy the string - strncpy((char *) p, token.string, MAX_STRINGFIELD); + strncpy((char *) p, token.string, MAX_STRINGFIELD-1); //make sure the string is closed with a zero ((char *)p)[MAX_STRINGFIELD-1] = '\0'; // From dfce71929a1094526f6482eeb6a0cca11f4bc7a1 Mon Sep 17 00:00:00 2001 From: Zack Middleton Date: Wed, 7 Jun 2017 22:09:51 -0500 Subject: [PATCH 085/240] Add con_autochat and con_autoclear cvars --- README.md | 6 ++++++ code/client/cl_console.c | 7 ++++++- code/client/cl_keys.c | 7 +++++-- code/qcommon/common.c | 11 +++++++++-- code/qcommon/qcommon.h | 3 +++ code/sys/con_tty.c | 8 ++++++-- 6 files changed, 35 insertions(+), 7 deletions(-) diff --git a/README.md b/README.md index 8bb0fc9056..1e5d896544 100644 --- a/README.md +++ b/README.md @@ -140,6 +140,12 @@ The defaults for these variables differ depending on the target platform. behaviour, 0 for standard q3 cl_mouseAccelOffset - Tuning the acceleration curve, see below + con_autochat - Set to 0 to disable sending console input + text as chat when there is not a slash + at the beginning + con_autoclear - Set to 0 to disable clearing console + input text when console is closed + in_joystickUseAnalog - Do not translate joystick axis events to keyboard commands diff --git a/code/client/cl_console.c b/code/client/cl_console.c index 32ab87e037..6a65bb7963 100644 --- a/code/client/cl_console.c +++ b/code/client/cl_console.c @@ -56,6 +56,7 @@ typedef struct { console_t con; cvar_t *con_conspeed; +cvar_t *con_autoclear; cvar_t *con_notifytime; #define DEFAULT_CONSOLE_WIDTH 78 @@ -72,7 +73,10 @@ void Con_ToggleConsole_f (void) { return; } - Field_Clear( &g_consoleField ); + if ( con_autoclear->integer ) { + Field_Clear( &g_consoleField ); + } + g_consoleField.widthInChars = g_console_field_width; Con_ClearNotify (); @@ -354,6 +358,7 @@ void Con_Init (void) { con_notifytime = Cvar_Get ("con_notifytime", "3", 0); con_conspeed = Cvar_Get ("scr_conspeed", "3", 0); + con_autoclear = Cvar_Get("con_autoclear", "1", CVAR_ARCHIVE); Field_Clear( &g_consoleField ); g_consoleField.widthInChars = g_console_field_width; diff --git a/code/client/cl_keys.c b/code/client/cl_keys.c index 4945497bce..7ddea0c7b8 100644 --- a/code/client/cl_keys.c +++ b/code/client/cl_keys.c @@ -613,7 +613,7 @@ void Console_Key (int key) { // enter finishes the line if ( key == K_ENTER || key == K_KP_ENTER ) { // if not in the game explicitly prepend a slash if needed - if ( clc.state != CA_ACTIVE && + if ( clc.state != CA_ACTIVE && con_autochat->integer && g_consoleField.buffer[0] && g_consoleField.buffer[0] != '\\' && g_consoleField.buffer[0] != '/' ) { @@ -635,7 +635,10 @@ void Console_Key (int key) { if ( !g_consoleField.buffer[0] ) { return; // empty lines just scroll the console without adding to history } else { - Cbuf_AddText ("cmd say "); + if ( con_autochat->integer ) { + Cbuf_AddText ("cmd say "); + } + Cbuf_AddText( g_consoleField.buffer ); Cbuf_AddText ("\n"); } diff --git a/code/qcommon/common.c b/code/qcommon/common.c index c4412ea75e..5d64d6872d 100644 --- a/code/qcommon/common.c +++ b/code/qcommon/common.c @@ -95,6 +95,9 @@ cvar_t *com_legacyprotocol; cvar_t *com_basegame; cvar_t *com_homepath; cvar_t *com_busyWait; +#ifndef DEDICATED +cvar_t *con_autochat; +#endif #if idx64 int (*Q_VMftol)(void); @@ -2784,6 +2787,10 @@ void Com_Init( char *commandLine ) { #endif Cvar_Get("protocol", com_protocol->string, CVAR_ROM); +#ifndef DEDICATED + con_autochat = Cvar_Get("con_autochat", "1", CVAR_ARCHIVE); +#endif + Sys_Init(); Sys_InitPIDFile( FS_GetCurrentGameDir() ); @@ -3471,8 +3478,8 @@ void Field_CompleteCommand( char *cmd, completionString = Cmd_Argv( completionArgument - 1 ); #ifndef DEDICATED - // Unconditionally add a '\' to the start of the buffer - if( completionField->buffer[ 0 ] && + // add a '\' to the start of the buffer if it might be sent as chat otherwise + if( con_autochat->integer && completionField->buffer[ 0 ] && completionField->buffer[ 0 ] != '\\' ) { if( completionField->buffer[ 0 ] != '/' ) diff --git a/code/qcommon/qcommon.h b/code/qcommon/qcommon.h index a73a03cc31..124c393a66 100644 --- a/code/qcommon/qcommon.h +++ b/code/qcommon/qcommon.h @@ -881,6 +881,9 @@ extern cvar_t *com_protocol; #ifdef LEGACY_PROTOCOL extern cvar_t *com_legacyprotocol; #endif +#ifndef DEDICATED +extern cvar_t *con_autochat; +#endif // com_speeds times extern int time_game; diff --git a/code/sys/con_tty.c b/code/sys/con_tty.c index 9a6ee95ca8..2c2b595cf9 100644 --- a/code/sys/con_tty.c +++ b/code/sys/con_tty.c @@ -378,7 +378,7 @@ char *CON_Input( void ) { #ifndef DEDICATED // if not in the game explicitly prepend a slash if needed - if (clc.state != CA_ACTIVE && TTY_con.cursor && + if (clc.state != CA_ACTIVE && con_autochat->integer && TTY_con.cursor && TTY_con.buffer[0] != '/' && TTY_con.buffer[0] != '\\') { memmove(TTY_con.buffer + 1, TTY_con.buffer, sizeof(TTY_con.buffer) - 1); @@ -389,7 +389,11 @@ char *CON_Input( void ) if (TTY_con.buffer[0] == '/' || TTY_con.buffer[0] == '\\') { Q_strncpyz(text, TTY_con.buffer + 1, sizeof(text)); } else if (TTY_con.cursor) { - Com_sprintf(text, sizeof(text), "cmd say %s", TTY_con.buffer); + if (con_autochat->integer) { + Com_sprintf(text, sizeof(text), "cmd say %s", TTY_con.buffer); + } else { + Q_strncpyz(text, TTY_con.buffer, sizeof(text)); + } } else { text[0] = '\0'; } From e9436abff0feed3e316c95ed706779f4bdfdf1c9 Mon Sep 17 00:00:00 2001 From: Zack Middleton Date: Sat, 10 Jun 2017 19:06:04 -0500 Subject: [PATCH 086/240] Only allow connectionless print/echo from server/rcon address Reported by Ensiform. --- code/client/cl_main.c | 18 ++++++++++++------ code/client/client.h | 2 ++ 2 files changed, 14 insertions(+), 6 deletions(-) diff --git a/code/client/cl_main.c b/code/client/cl_main.c index e9a406e6d2..b0712a3dbd 100644 --- a/code/client/cl_main.c +++ b/code/client/cl_main.c @@ -1891,6 +1891,7 @@ void CL_Rcon_f( void ) { } NET_SendPacket (NS_CLIENT, strlen(message)+1, message, to); + cls.rconAddress = to; } /* @@ -2749,7 +2750,10 @@ void CL_ConnectionlessPacket( netadr_t from, msg_t *msg ) { // echo request from server if ( !Q_stricmp(c, "echo") ) { - NET_OutOfBandPrint( NS_CLIENT, from, "%s", Cmd_Argv(1) ); + // NOTE: we may have to add exceptions for auth and update servers + if ( NET_CompareAdr( from, clc.serverAddress ) || NET_CompareAdr( from, cls.rconAddress ) ) { + NET_OutOfBandPrint( NS_CLIENT, from, "%s", Cmd_Argv(1) ); + } return; } @@ -2766,12 +2770,14 @@ void CL_ConnectionlessPacket( netadr_t from, msg_t *msg ) { } // echo request from server - if(!Q_stricmp(c, "print")){ - s = MSG_ReadString( msg ); - - Q_strncpyz( clc.serverMessage, s, sizeof( clc.serverMessage ) ); - Com_Printf( "%s", s ); + if ( !Q_stricmp(c, "print") ) { + // NOTE: we may have to add exceptions for auth and update servers + if ( NET_CompareAdr( from, clc.serverAddress ) || NET_CompareAdr( from, cls.rconAddress ) ) { + s = MSG_ReadString( msg ); + Q_strncpyz( clc.serverMessage, s, sizeof( clc.serverMessage ) ); + Com_Printf( "%s", s ); + } return; } diff --git a/code/client/client.h b/code/client/client.h index 0fe3889c4e..0d3e1c3369 100644 --- a/code/client/client.h +++ b/code/client/client.h @@ -344,6 +344,8 @@ typedef struct { netadr_t authorizeServer; + netadr_t rconAddress; + // rendering info glconfig_t glconfig; qhandle_t charSetShader; From e03cdf444c687b2291c4f5b91684e5f3d46cf79a Mon Sep 17 00:00:00 2001 From: SmileTheory Date: Fri, 16 Jun 2017 16:52:53 -0700 Subject: [PATCH 087/240] OpenGL2: Remove SF_VAO_MESH. --- code/renderergl2/tr_light.c | 1 - code/renderergl2/tr_local.h | 10 +--------- code/renderergl2/tr_surface.c | 36 ----------------------------------- code/renderergl2/tr_world.c | 2 -- 4 files changed, 1 insertion(+), 48 deletions(-) diff --git a/code/renderergl2/tr_light.c b/code/renderergl2/tr_light.c index cf4c0b5bee..f188626886 100644 --- a/code/renderergl2/tr_light.c +++ b/code/renderergl2/tr_light.c @@ -99,7 +99,6 @@ void R_DlightBmodel( bmodel_t *bmodel ) { case SF_FACE: case SF_GRID: case SF_TRIANGLES: - case SF_VAO_MESH: ((srfBspSurface_t *)surf->data)->dlightBits = mask; break; diff --git a/code/renderergl2/tr_local.h b/code/renderergl2/tr_local.h index b8f34df527..7e8e4b7717 100644 --- a/code/renderergl2/tr_local.h +++ b/code/renderergl2/tr_local.h @@ -850,7 +850,6 @@ typedef enum { SF_IQM, SF_FLARE, SF_ENTITY, // beams, rails, lightning, etc that can be determined by entity - SF_VAO_MESH, SF_VAO_MDVMESH, SF_NUM_SURFACE_TYPES, @@ -903,7 +902,7 @@ typedef struct #define srfVert_t_cleared(x) srfVert_t (x) = {{0, 0, 0}, {0, 0}, {0, 0}, {0, 0, 0, 0}, {0, 0, 0, 0}, {0, 0, 0, 0}, {0, 0, 0, 0}} -// srfBspSurface_t covers SF_GRID, SF_TRIANGLES, SF_POLY, and SF_VAO_MESH +// srfBspSurface_t covers SF_GRID, SF_TRIANGLES, and SF_POLY typedef struct srfBspSurface_s { surfaceType_t surfaceType; @@ -925,13 +924,6 @@ typedef struct srfBspSurface_s // vertexes int numVerts; srfVert_t *verts; - - // BSP VBO offsets - int firstVert; - int firstIndex; - - // static render data - vao_t *vao; // SF_GRID specific variables after here diff --git a/code/renderergl2/tr_surface.c b/code/renderergl2/tr_surface.c index 0fc0e0aaa4..e123b54cac 100644 --- a/code/renderergl2/tr_surface.c +++ b/code/renderergl2/tr_surface.c @@ -451,35 +451,6 @@ static qboolean RB_SurfaceVaoCached(int numVerts, srfVert_t *verts, int numIndex } -static qboolean RB_SurfaceVao(vao_t *vao, int numVerts, int numIndexes, int firstIndex, int dlightBits, int pshadowBits, qboolean shaderCheck) -{ - if (!vao) - { - return qfalse; - } - - if (shaderCheck && !(!ShaderRequiresCPUDeforms(tess.shader) && !tess.shader->isSky && !tess.shader->isPortal)) - { - return qfalse; - } - - RB_CheckVao(vao); - - tess.dlightBits |= dlightBits; - tess.pshadowBits |= pshadowBits; - - RB_EndSurface(); - RB_BeginSurface(tess.shader, tess.fogNum, tess.cubemapIndex); - - backEnd.pc.c_staticVaoDraws++; - - tess.numIndexes = numIndexes; - tess.numVertexes = numVerts; - - return qtrue; -} - - /* ============= RB_SurfaceTriangles @@ -1236,12 +1207,6 @@ static void RB_SurfaceFlare(srfFlare_t *surf) RB_AddFlare(surf, tess.fogNum, surf->origin, surf->color, surf->normal); } -static void RB_SurfaceVaoMesh(srfBspSurface_t * srf) -{ - RB_SurfaceVao (srf->vao, srf->numVerts, srf->numIndexes, srf->firstIndex, - srf->dlightBits, srf->pshadowBits, qfalse ); -} - void RB_SurfaceVaoMdvMesh(srfVaoMdvMesh_t * surface) { //mdvModel_t *mdvModel; @@ -1348,6 +1313,5 @@ void (*rb_surfaceTable[SF_NUM_SURFACE_TYPES])( void *) = { (void(*)(void*))RB_IQMSurfaceAnim, // SF_IQM, (void(*)(void*))RB_SurfaceFlare, // SF_FLARE, (void(*)(void*))RB_SurfaceEntity, // SF_ENTITY - (void(*)(void*))RB_SurfaceVaoMesh, // SF_VAO_MESH, (void(*)(void*))RB_SurfaceVaoMdvMesh, // SF_VAO_MDVMESH }; diff --git a/code/renderergl2/tr_world.c b/code/renderergl2/tr_world.c index 3821bee207..f9a24424c4 100644 --- a/code/renderergl2/tr_world.c +++ b/code/renderergl2/tr_world.c @@ -213,7 +213,6 @@ static int R_DlightSurface( msurface_t *surf, int dlightBits ) { case SF_FACE: case SF_GRID: case SF_TRIANGLES: - case SF_VAO_MESH: ((srfBspSurface_t *)surf->data)->dlightBits = dlightBits; break; @@ -299,7 +298,6 @@ static int R_PshadowSurface( msurface_t *surf, int pshadowBits ) { case SF_FACE: case SF_GRID: case SF_TRIANGLES: - case SF_VAO_MESH: ((srfBspSurface_t *)surf->data)->pshadowBits = pshadowBits; break; From f7c3276fe803388bd613ab6bf6ad8e0a6647b740 Mon Sep 17 00:00:00 2001 From: Zack Middleton Date: Thu, 22 Jun 2017 21:56:00 -0500 Subject: [PATCH 088/240] Fix g_teamAutoJoin and g_teamForceBalance There are various issues caused by not knowing the initial team for the local client and bots when they connect. This is can be reproduced by starting a team game from the main menu. When g_teamAutoJoin is enabled, bots and local client join a random team at connect and then execute their team command a few frames later. This may result in the player being killed if they specify a different team. In Team Arena's Harvester mode this causes harvester skulls to be spawned at the beginning of the game. When g_teamForceBalance is enabled, the local client and bots may not be able to join their desired team. This may result in them being spectators. If g_teamAutoJoin is also enabled they may be left on the opposite (red/blue) team they were meant to join. There is a hack for including bot's team in their player info string (used by cgame for which team skin to use) before the bot joins their desired team. Bots aren't guaranteed to join their desired team (as may happen when both g_teamAutoJoin and g_teamForceBalance are enabled) so clients may see them as being on the wrong team! ---- Add teampref userinfo option for team preference. If teampref is set it will be used for attempting to join the team immediately at connect. Bots now join team at connect using teampref userinfo. So remove the hack for setting bot's team in player info string before the bot joins the team. To avoid the client sending teampref userinfo to all network servers, the local client uses a g_localTeamPref cvar. The g_localTeamPref cvar is cleared after it's used so it doesn't get used when starting another server later. Another reason not to use a teampref userinfo cvar is there isn't a reliable way to clear it in CGame/UI which are likely loaded from baseq3 pk3. Make it so g_teamAutoJoin doesn't affect clients who specify teampref. If teampref is invalid, the client will join a random team like g_teamAutoJoin. Don't apply g_teamForceBalance to the local client or bots. Otherwise they may be left as spectators when starting team game from menu. The start server menus use team command and g_localTeamPref to set the human player's team. This way it's compatible with vanilla Q3 game VMs and the new setting team at connect feature. --- code/game/ai_dmq3.c | 7 +------ code/game/g_bot.c | 3 +-- code/game/g_client.c | 40 +++++++++++-------------------------- code/game/g_cmds.c | 6 +++--- code/game/g_local.h | 4 ++-- code/game/g_main.c | 4 +++- code/game/g_session.c | 27 +++++++++++++++++-------- code/q3_ui/ui_main.c | 7 ++++++- code/q3_ui/ui_startserver.c | 4 ++++ code/ui/ui_main.c | 9 +++++++++ 10 files changed, 60 insertions(+), 51 deletions(-) diff --git a/code/game/ai_dmq3.c b/code/game/ai_dmq3.c index 5198cb3d7a..6e79665681 100644 --- a/code/game/ai_dmq3.c +++ b/code/game/ai_dmq3.c @@ -5218,7 +5218,7 @@ BotDeathmatchAI ================== */ void BotDeathmatchAI(bot_state_t *bs, float thinktime) { - char gender[144], name[144], buf[144]; + char gender[144], name[144]; char userinfo[MAX_INFO_STRING]; int i; @@ -5232,11 +5232,6 @@ void BotDeathmatchAI(bot_state_t *bs, float thinktime) { trap_GetUserinfo(bs->client, userinfo, sizeof(userinfo)); Info_SetValueForKey(userinfo, "sex", gender); trap_SetUserinfo(bs->client, userinfo); - //set the team - if ( !bs->map_restart && g_gametype.integer != GT_TOURNAMENT ) { - Com_sprintf(buf, sizeof(buf), "team %s", bs->settings.team); - trap_EA_Command(bs->client, buf); - } //set the chat gender if (gender[0] == 'm') trap_BotSetChatGender(bs->cs, CHAT_GENDERMALE); else if (gender[0] == 'f') trap_BotSetChatGender(bs->cs, CHAT_GENDERFEMALE); diff --git a/code/game/g_bot.c b/code/game/g_bot.c index 6cc3917857..c509772b12 100644 --- a/code/game/g_bot.c +++ b/code/game/g_bot.c @@ -542,7 +542,6 @@ qboolean G_BotConnect( int clientNum, qboolean restart ) { Q_strncpyz( settings.characterfile, Info_ValueForKey( userinfo, "characterfile" ), sizeof(settings.characterfile) ); settings.skill = atof( Info_ValueForKey( userinfo, "skill" ) ); - Q_strncpyz( settings.team, Info_ValueForKey( userinfo, "team" ), sizeof(settings.team) ); if (!BotAISetupClient( clientNum, &settings, restart )) { trap_DropClient( clientNum, "BotAISetupClient failed" ); @@ -670,7 +669,7 @@ static void G_AddBot( const char *name, float skill, const char *team, int delay team = "red"; } } - Info_SetValueForKey( userinfo, "team", team ); + Info_SetValueForKey( userinfo, "teampref", team ); // register the userinfo trap_SetUserinfo( clientNum, userinfo ); diff --git a/code/game/g_client.c b/code/game/g_client.c index d1dcbc3c9c..89f20982d0 100644 --- a/code/game/g_client.c +++ b/code/game/g_client.c @@ -702,7 +702,7 @@ if desired. */ void ClientUserinfoChanged( int clientNum ) { gentity_t *ent; - int teamTask, teamLeader, team, health; + int teamTask, teamLeader, health; char *s; char model[MAX_QPATH]; char headModel[MAX_QPATH]; @@ -726,12 +726,6 @@ void ClientUserinfoChanged( int clientNum ) { trap_DropClient(clientNum, "Invalid userinfo"); } - // check for local client - s = Info_ValueForKey( userinfo, "ip" ); - if ( !strcmp( s, "localhost" ) ) { - client->pers.localClient = qtrue; - } - // check the item prediction s = Info_ValueForKey( userinfo, "cg_predictItems" ); if ( !atoi( s ) ) { @@ -787,22 +781,6 @@ void ClientUserinfoChanged( int clientNum ) { Q_strncpyz( headModel, Info_ValueForKey (userinfo, "headmodel"), sizeof( headModel ) ); } - // bots set their team a few frames later - if (g_gametype.integer >= GT_TEAM && g_entities[clientNum].r.svFlags & SVF_BOT) { - s = Info_ValueForKey( userinfo, "team" ); - if ( !Q_stricmp( s, "red" ) || !Q_stricmp( s, "r" ) ) { - team = TEAM_RED; - } else if ( !Q_stricmp( s, "blue" ) || !Q_stricmp( s, "b" ) ) { - team = TEAM_BLUE; - } else { - // pick the team with the least number of players - team = PickTeam( clientNum ); - } - } - else { - team = client->sess.sessionTeam; - } - /* NOTE: all client side now // team @@ -871,7 +849,7 @@ void ClientUserinfoChanged( int clientNum ) { if (ent->r.svFlags & SVF_BOT) { s = va("n\\%s\\t\\%i\\model\\%s\\hmodel\\%s\\c1\\%s\\c2\\%s\\hc\\%i\\w\\%i\\l\\%i\\skill\\%s\\tt\\%d\\tl\\%d", - client->pers.netname, team, model, headModel, c1, c2, + client->pers.netname, client->sess.sessionTeam, model, headModel, c1, c2, client->pers.maxHealth, client->sess.wins, client->sess.losses, Info_ValueForKey( userinfo, "skill" ), teamTask, teamLeader ); } @@ -956,11 +934,11 @@ char *ClientConnect( int clientNum, qboolean firstTime, qboolean isBot ) { client->pers.connected = CON_CONNECTING; - // read or initialize the session data - if ( firstTime || level.newSession ) { - G_InitSessionData( client, userinfo ); + // check for local client + value = Info_ValueForKey( userinfo, "ip" ); + if ( !strcmp( value, "localhost" ) ) { + client->pers.localClient = qtrue; } - G_ReadSessionData( client ); if( isBot ) { ent->r.svFlags |= SVF_BOT; @@ -970,6 +948,12 @@ char *ClientConnect( int clientNum, qboolean firstTime, qboolean isBot ) { } } + // read or initialize the session data + if ( firstTime || level.newSession ) { + G_InitSessionData( client, userinfo ); + } + G_ReadSessionData( client ); + // get and distribute relevent paramters G_LogPrintf( "ClientConnect: %i\n", clientNum ); ClientUserinfoChanged( clientNum ); diff --git a/code/game/g_cmds.c b/code/game/g_cmds.c index 7606edbeea..46a69ca8ca 100644 --- a/code/game/g_cmds.c +++ b/code/game/g_cmds.c @@ -512,7 +512,7 @@ void BroadcastTeamChange( gclient_t *client, int oldTeam ) SetTeam ================= */ -void SetTeam( gentity_t *ent, char *s ) { +void SetTeam( gentity_t *ent, const char *s ) { int team, oldTeam; gclient_t *client; int clientNum; @@ -554,7 +554,7 @@ void SetTeam( gentity_t *ent, char *s ) { team = PickTeam( clientNum ); } - if ( g_teamForceBalance.integer ) { + if ( g_teamForceBalance.integer && !client->pers.localClient && !( ent->r.svFlags & SVF_BOT ) ) { int counts[TEAM_NUM_TEAMS]; counts[TEAM_BLUE] = TeamCount( clientNum, TEAM_BLUE ); @@ -642,7 +642,7 @@ void SetTeam( gentity_t *ent, char *s ) { // get and distribute relevent paramters ClientUserinfoChanged( clientNum ); - // client hasn't spawned yet, they sent an early team command + // client hasn't spawned yet, they sent an early team command, teampref userinfo, or g_teamAutoJoin is enabled if ( client->pers.connected != CON_CONNECTED ) { return; } diff --git a/code/game/g_local.h b/code/game/g_local.h index d2ff996ca4..ebac56c6b2 100644 --- a/code/game/g_local.h +++ b/code/game/g_local.h @@ -427,7 +427,7 @@ char *G_NewString( const char *string ); void Cmd_Score_f (gentity_t *ent); void StopFollowing( gentity_t *ent ); void BroadcastTeamChange( gclient_t *client, int oldTeam ); -void SetTeam( gentity_t *ent, char *s ); +void SetTeam( gentity_t *ent, const char *s ); void Cmd_FollowCycle_f( gentity_t *ent, int dir ); // @@ -676,7 +676,6 @@ typedef struct bot_settings_s { char characterfile[MAX_FILEPATH]; float skill; - char team[MAX_FILEPATH]; } bot_settings_t; int BotAISetup( int restart ); @@ -745,6 +744,7 @@ extern vmCvar_t g_enableDust; extern vmCvar_t g_enableBreath; extern vmCvar_t g_singlePlayer; extern vmCvar_t g_proxMineTimeout; +extern vmCvar_t g_localTeamPref; void trap_Print( const char *text ); void trap_Error( const char *text ) __attribute__((noreturn)); diff --git a/code/game/g_main.c b/code/game/g_main.c index 0928762328..fb32be9218 100644 --- a/code/game/g_main.c +++ b/code/game/g_main.c @@ -81,6 +81,7 @@ vmCvar_t pmove_fixed; vmCvar_t pmove_msec; vmCvar_t g_rankings; vmCvar_t g_listEntity; +vmCvar_t g_localTeamPref; #ifdef MISSIONPACK vmCvar_t g_obeliskHealth; vmCvar_t g_obeliskRegenPeriod; @@ -176,7 +177,8 @@ static cvarTable_t gameCvarTable[] = { { &pmove_fixed, "pmove_fixed", "0", CVAR_SYSTEMINFO, 0, qfalse}, { &pmove_msec, "pmove_msec", "8", CVAR_SYSTEMINFO, 0, qfalse}, - { &g_rankings, "g_rankings", "0", 0, 0, qfalse} + { &g_rankings, "g_rankings", "0", 0, 0, qfalse}, + { &g_localTeamPref, "g_localTeamPref", "", 0, 0, qfalse } }; diff --git a/code/game/g_session.c b/code/game/g_session.c index eaac1a7189..6f97ba9947 100644 --- a/code/game/g_session.c +++ b/code/game/g_session.c @@ -105,17 +105,27 @@ void G_InitSessionData( gclient_t *client, char *userinfo ) { sess = &client->sess; + // check for team preference, mainly for bots + value = Info_ValueForKey( userinfo, "teampref" ); + + // check for human's team preference set by start server menu + if ( !value[0] && g_localTeamPref.string[0] && client->pers.localClient ) { + value = g_localTeamPref.string; + + // clear team so it's only used once + trap_Cvar_Set( "g_localTeamPref", "" ); + } + // initial team determination if ( g_gametype.integer >= GT_TEAM ) { - if ( g_teamAutoJoin.integer && !(g_entities[ client - level.clients ].r.svFlags & SVF_BOT) ) { - sess->sessionTeam = PickTeam( -1 ); - BroadcastTeamChange( client, -1 ); - } else { - // always spawn as spectator in team games - sess->sessionTeam = TEAM_SPECTATOR; + // always spawn as spectator in team games + sess->sessionTeam = TEAM_SPECTATOR; + sess->spectatorState = SPECTATOR_FREE; + + if ( value[0] || g_teamAutoJoin.integer ) { + SetTeam( &g_entities[client - level.clients], value ); } } else { - value = Info_ValueForKey( userinfo, "team" ); if ( value[0] == 's' ) { // a willing spectator, not a waiting-in-line sess->sessionTeam = TEAM_SPECTATOR; @@ -141,9 +151,10 @@ void G_InitSessionData( gclient_t *client, char *userinfo ) { break; } } + + sess->spectatorState = SPECTATOR_FREE; } - sess->spectatorState = SPECTATOR_FREE; AddTournamentQueue(client); G_WriteClientSessionData( client ); diff --git a/code/q3_ui/ui_main.c b/code/q3_ui/ui_main.c index bd9359a069..216269cf6e 100644 --- a/code/q3_ui/ui_main.c +++ b/code/q3_ui/ui_main.c @@ -214,7 +214,8 @@ static cvarTable_t cvarTable[] = { { &ui_server16, "server16", "", CVAR_ARCHIVE }, { &ui_cdkeychecked, "ui_cdkeychecked", "0", CVAR_ROM }, - { &ui_ioq3, "ui_ioq3", "1", CVAR_ROM } + { &ui_ioq3, "ui_ioq3", "1", CVAR_ROM }, + { NULL, "g_localTeamPref", "", 0 } }; static int cvarTableSize = ARRAY_LEN( cvarTable ); @@ -244,6 +245,10 @@ void UI_UpdateCvars( void ) { cvarTable_t *cv; for ( i = 0, cv = cvarTable ; i < cvarTableSize ; i++, cv++ ) { + if ( !cv->vmCvar ) { + continue; + } + trap_Cvar_Update( cv->vmCvar ); } } diff --git a/code/q3_ui/ui_startserver.c b/code/q3_ui/ui_startserver.c index 82865ad5f6..b4883d4d49 100644 --- a/code/q3_ui/ui_startserver.c +++ b/code/q3_ui/ui_startserver.c @@ -815,7 +815,11 @@ static void ServerOptions_Start( void ) { // set player's team if( dedicated == 0 && s_serveroptions.gametype >= GT_TEAM ) { + // send team command for vanilla q3 game qvm trap_Cmd_ExecuteText( EXEC_APPEND, va( "wait 5; team %s\n", playerTeam_list[s_serveroptions.playerTeam[0].curvalue] ) ); + + // set g_localTeamPref for ioq3 game qvm + trap_Cvar_Set( "g_localTeamPref", playerTeam_list[s_serveroptions.playerTeam[0].curvalue] ); } } diff --git a/code/ui/ui_main.c b/code/ui/ui_main.c index 60170614fa..3660470529 100644 --- a/code/ui/ui_main.c +++ b/code/ui/ui_main.c @@ -3026,7 +3026,11 @@ static void UI_StartSkirmish(qboolean next) { } } if (g >= GT_TEAM ) { + // send team command for vanilla q3 game qvm trap_Cmd_ExecuteText( EXEC_APPEND, "wait 5; team Red\n" ); + + // set g_localTeamPref for ioq3 game qvm + trap_Cvar_Set( "g_localTeamPref", "Red" ); } } @@ -5849,6 +5853,7 @@ static cvarTable_t cvarTable[] = { { &ui_realCaptureLimit, "capturelimit", "8", CVAR_SERVERINFO | CVAR_ARCHIVE | CVAR_NORESTART}, { &ui_serverStatusTimeOut, "ui_serverStatusTimeOut", "7000", CVAR_ARCHIVE}, + { NULL, "g_localTeamPref", "", 0 }, }; static int cvarTableSize = ARRAY_LEN( cvarTable ); @@ -5878,6 +5883,10 @@ void UI_UpdateCvars( void ) { cvarTable_t *cv; for ( i = 0, cv = cvarTable ; i < cvarTableSize ; i++, cv++ ) { + if ( !cv->vmCvar ) { + continue; + } + trap_Cvar_Update( cv->vmCvar ); } } From f19efb77c8cae5768cbcf1e96448a7dad108e2bb Mon Sep 17 00:00:00 2001 From: Zack Middleton Date: Sat, 24 Jun 2017 19:31:23 -0500 Subject: [PATCH 089/240] Fix Team Arena team base models not dropping to floor Team Arena's Overload gametype has red and blue team Obelisk base objects. It uses separate entities for visual and damage. Only the damageable entity was dropped to floor. Leaving model floating off the ground. Team Arena's Harvester base has the same problem. Model entity floats in air but trigger entity drops to floor. Drop all Team Arena team base models to floor. Fixes CTF, 1Flag, Overload, and Harvester base models. --- code/game/g_team.c | 54 ++++++++++++++++++++++++++++------------------ 1 file changed, 33 insertions(+), 21 deletions(-) diff --git a/code/game/g_team.c b/code/game/g_team.c index 12d9f6a282..7e0e90b969 100644 --- a/code/game/g_team.c +++ b/code/game/g_team.c @@ -1313,9 +1313,8 @@ static void ObeliskPain( gentity_t *self, gentity_t *attacker, int damage ) { AddScore(attacker, self->r.currentOrigin, actualDamage); } -gentity_t *SpawnObelisk( vec3_t origin, int team, int spawnflags) { - trace_t tr; - vec3_t dest; +// spawn invisible damagable obelisk entity / harvester base trigger. +gentity_t *SpawnObelisk( vec3_t origin, vec3_t mins, vec3_t maxs, int team ) { gentity_t *ent; ent = G_Spawn(); @@ -1324,8 +1323,8 @@ gentity_t *SpawnObelisk( vec3_t origin, int team, int spawnflags) { VectorCopy( origin, ent->s.pos.trBase ); VectorCopy( origin, ent->r.currentOrigin ); - VectorSet( ent->r.mins, -15, -15, 0 ); - VectorSet( ent->r.maxs, 15, 15, 87 ); + VectorCopy( mins, ent->r.mins ); + VectorCopy( maxs, ent->r.maxs ); ent->s.eType = ET_GENERAL; ent->flags = FL_NO_KNOCKBACK; @@ -1344,7 +1343,26 @@ gentity_t *SpawnObelisk( vec3_t origin, int team, int spawnflags) { ent->touch = ObeliskTouch; } - if ( spawnflags & 1 ) { + G_SetOrigin( ent, ent->s.origin ); + + ent->spawnflags = team; + + trap_LinkEntity( ent ); + + return ent; +} + +// setup entity for team base model / obelisk model. +void ObeliskInit( gentity_t *ent ) { + trace_t tr; + vec3_t dest; + + ent->s.eType = ET_TEAM; + + VectorSet( ent->r.mins, -15, -15, 0 ); + VectorSet( ent->r.maxs, 15, 15, 87 ); + + if ( ent->spawnflags & 1 ) { // suspended G_SetOrigin( ent, ent->s.origin ); } else { @@ -1368,12 +1386,6 @@ gentity_t *SpawnObelisk( vec3_t origin, int team, int spawnflags) { G_SetOrigin( ent, tr.endpos ); } } - - ent->spawnflags = team; - - trap_LinkEntity( ent ); - - return ent; } /*QUAKED team_redobelisk (1 0 0) (-16 -16 0) (16 16 8) @@ -1385,16 +1397,16 @@ void SP_team_redobelisk( gentity_t *ent ) { G_FreeEntity(ent); return; } - ent->s.eType = ET_TEAM; + ObeliskInit( ent ); if ( g_gametype.integer == GT_OBELISK ) { - obelisk = SpawnObelisk( ent->s.origin, TEAM_RED, ent->spawnflags ); + obelisk = SpawnObelisk( ent->s.origin, ent->r.mins, ent->r.maxs, TEAM_RED ); obelisk->activator = ent; // initial obelisk health value ent->s.modelindex2 = 0xff; ent->s.frame = 0; } if ( g_gametype.integer == GT_HARVESTER ) { - obelisk = SpawnObelisk( ent->s.origin, TEAM_RED, ent->spawnflags ); + obelisk = SpawnObelisk( ent->s.origin, ent->r.mins, ent->r.maxs, TEAM_RED ); obelisk->activator = ent; } ent->s.modelindex = TEAM_RED; @@ -1410,16 +1422,16 @@ void SP_team_blueobelisk( gentity_t *ent ) { G_FreeEntity(ent); return; } - ent->s.eType = ET_TEAM; + ObeliskInit( ent ); if ( g_gametype.integer == GT_OBELISK ) { - obelisk = SpawnObelisk( ent->s.origin, TEAM_BLUE, ent->spawnflags ); + obelisk = SpawnObelisk( ent->s.origin, ent->r.mins, ent->r.maxs, TEAM_BLUE ); obelisk->activator = ent; // initial obelisk health value ent->s.modelindex2 = 0xff; ent->s.frame = 0; } if ( g_gametype.integer == GT_HARVESTER ) { - obelisk = SpawnObelisk( ent->s.origin, TEAM_BLUE, ent->spawnflags ); + obelisk = SpawnObelisk( ent->s.origin, ent->r.mins, ent->r.maxs, TEAM_BLUE ); obelisk->activator = ent; } ent->s.modelindex = TEAM_BLUE; @@ -1433,10 +1445,10 @@ void SP_team_neutralobelisk( gentity_t *ent ) { G_FreeEntity(ent); return; } - ent->s.eType = ET_TEAM; + ObeliskInit( ent ); if ( g_gametype.integer == GT_HARVESTER) { - neutralObelisk = SpawnObelisk( ent->s.origin, TEAM_FREE, ent->spawnflags); - neutralObelisk->spawnflags = TEAM_FREE; + neutralObelisk = SpawnObelisk( ent->s.origin, ent->r.mins, ent->r.maxs, TEAM_FREE ); + neutralObelisk->activator = ent; } ent->s.modelindex = TEAM_FREE; trap_LinkEntity(ent); From 520b10044924b6e687d857827801752a1c5be523 Mon Sep 17 00:00:00 2001 From: Zack Middleton Date: Sat, 24 Jun 2017 19:47:35 -0500 Subject: [PATCH 090/240] Make cg_teamChatsOnly only affect team gametypes Also made it so that 'tell voice chats' are always allowed for consistancy with 'tell chat'. Reported by Tobias Kuehnhammer. --- code/cgame/cg_servercmds.c | 45 ++++++++++++++++++++------------------ 1 file changed, 24 insertions(+), 21 deletions(-) diff --git a/code/cgame/cg_servercmds.c b/code/cgame/cg_servercmds.c index d1c8b28b7b..57ca19b58a 100644 --- a/code/cgame/cg_servercmds.c +++ b/code/cgame/cg_servercmds.c @@ -899,6 +899,10 @@ void CG_VoiceChatLocal( int mode, qboolean voiceOnly, int clientNum, int color, return; } + if ( mode == SAY_ALL && cgs.gametype >= GT_TEAM && cg_teamChatsOnly.integer ) { + return; + } + if ( clientNum < 0 || clientNum >= MAX_CLIENTS ) { clientNum = 0; } @@ -909,23 +913,20 @@ void CG_VoiceChatLocal( int mode, qboolean voiceOnly, int clientNum, int color, voiceChatList = CG_VoiceChatListForClient( clientNum ); if ( CG_GetVoiceChat( voiceChatList, cmd, &snd, &chat ) ) { - // - if ( mode == SAY_TEAM || !cg_teamChatsOnly.integer ) { - vchat.clientNum = clientNum; - vchat.snd = snd; - vchat.voiceOnly = voiceOnly; - Q_strncpyz(vchat.cmd, cmd, sizeof(vchat.cmd)); - if ( mode == SAY_TELL ) { - Com_sprintf(vchat.message, sizeof(vchat.message), "[%s]: %c%c%s", ci->name, Q_COLOR_ESCAPE, color, chat); - } - else if ( mode == SAY_TEAM ) { - Com_sprintf(vchat.message, sizeof(vchat.message), "(%s): %c%c%s", ci->name, Q_COLOR_ESCAPE, color, chat); - } - else { - Com_sprintf(vchat.message, sizeof(vchat.message), "%s: %c%c%s", ci->name, Q_COLOR_ESCAPE, color, chat); - } - CG_AddBufferedVoiceChat(&vchat); + vchat.clientNum = clientNum; + vchat.snd = snd; + vchat.voiceOnly = voiceOnly; + Q_strncpyz(vchat.cmd, cmd, sizeof(vchat.cmd)); + if ( mode == SAY_TELL ) { + Com_sprintf(vchat.message, sizeof(vchat.message), "[%s]: %c%c%s", ci->name, Q_COLOR_ESCAPE, color, chat); + } + else if ( mode == SAY_TEAM ) { + Com_sprintf(vchat.message, sizeof(vchat.message), "(%s): %c%c%s", ci->name, Q_COLOR_ESCAPE, color, chat); } + else { + Com_sprintf(vchat.message, sizeof(vchat.message), "%s: %c%c%s", ci->name, Q_COLOR_ESCAPE, color, chat); + } + CG_AddBufferedVoiceChat(&vchat); } } @@ -1017,12 +1018,14 @@ static void CG_ServerCommand( void ) { } if ( !strcmp( cmd, "chat" ) ) { - if ( !cg_teamChatsOnly.integer ) { - trap_S_StartLocalSound( cgs.media.talkSound, CHAN_LOCAL_SOUND ); - Q_strncpyz( text, CG_Argv(1), MAX_SAY_TEXT ); - CG_RemoveChatEscapeChar( text ); - CG_Printf( "%s\n", text ); + if ( cgs.gametype >= GT_TEAM && cg_teamChatsOnly.integer ) { + return; } + + trap_S_StartLocalSound( cgs.media.talkSound, CHAN_LOCAL_SOUND ); + Q_strncpyz( text, CG_Argv(1), MAX_SAY_TEXT ); + CG_RemoveChatEscapeChar( text ); + CG_Printf( "%s\n", text ); return; } From c2ca5e7856bb66715fbacdebf9d70c01fcc9ef11 Mon Sep 17 00:00:00 2001 From: Zack Middleton Date: Sat, 24 Jun 2017 19:53:37 -0500 Subject: [PATCH 091/240] Check for unlimited time power up using INT_MAX It is possible for a power up to exceed 999 seconds without it being unlimited time. --- code/cgame/cg_draw.c | 11 ++++++++--- code/cgame/cg_newdraw.c | 11 ++++++++--- 2 files changed, 16 insertions(+), 6 deletions(-) diff --git a/code/cgame/cg_draw.c b/code/cgame/cg_draw.c index 4b6df017df..9566682229 100644 --- a/code/cgame/cg_draw.c +++ b/code/cgame/cg_draw.c @@ -1197,10 +1197,15 @@ static float CG_DrawPowerups( float y ) { if ( !ps->powerups[ i ] ) { continue; } - t = ps->powerups[ i ] - cg.time; - // ZOID--don't draw if the power up has unlimited time (999 seconds) + + // ZOID--don't draw if the power up has unlimited time // This is true of the CTF flags - if ( t < 0 || t > 999000) { + if ( ps->powerups[ i ] == INT_MAX ) { + continue; + } + + t = ps->powerups[ i ] - cg.time; + if ( t <= 0 ) { continue; } diff --git a/code/cgame/cg_newdraw.c b/code/cgame/cg_newdraw.c index a2749b2a37..680bd6c1ef 100644 --- a/code/cgame/cg_newdraw.c +++ b/code/cgame/cg_newdraw.c @@ -832,10 +832,15 @@ static void CG_DrawAreaPowerUp(rectDef_t *rect, int align, float special, float if ( !ps->powerups[ i ] ) { continue; } - t = ps->powerups[ i ] - cg.time; - // ZOID--don't draw if the power up has unlimited time (999 seconds) + + // ZOID--don't draw if the power up has unlimited time // This is true of the CTF flags - if ( t <= 0 || t >= 999000) { + if ( ps->powerups[ i ] == INT_MAX ) { + continue; + } + + t = ps->powerups[ i ] - cg.time; + if ( t <= 0 ) { continue; } From 7b9ccd14634df177280484db845ada007ea13be9 Mon Sep 17 00:00:00 2001 From: Zack Middleton Date: Sat, 24 Jun 2017 20:02:44 -0500 Subject: [PATCH 092/240] Have spectator always be in first person Flying around in third person with no body is weird. --- code/cgame/cg_view.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/code/cgame/cg_view.c b/code/cgame/cg_view.c index 1131841f70..74bd2488c1 100644 --- a/code/cgame/cg_view.c +++ b/code/cgame/cg_view.c @@ -796,7 +796,8 @@ void CG_DrawActiveFrame( int serverTime, stereoFrame_t stereoView, qboolean demo CG_PredictPlayerState(); // decide on third person view - cg.renderingThirdPerson = cg_thirdPerson.integer || (cg.snap->ps.stats[STAT_HEALTH] <= 0); + cg.renderingThirdPerson = cg.snap->ps.persistant[PERS_TEAM] != TEAM_SPECTATOR + && (cg_thirdPerson.integer || (cg.snap->ps.stats[STAT_HEALTH] <= 0)); // build cg.refdef inwater = CG_CalcViewValues(); From 03336dd0bf823b8f3743b5329613d2e00f7a94e1 Mon Sep 17 00:00:00 2001 From: Zack Middleton Date: Sat, 24 Jun 2017 20:04:27 -0500 Subject: [PATCH 093/240] Allow spectators to use noclip cheat --- code/game/g_active.c | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/code/game/g_active.c b/code/game/g_active.c index 93ff7147a6..775604a4e1 100644 --- a/code/game/g_active.c +++ b/code/game/g_active.c @@ -321,7 +321,12 @@ void SpectatorThink( gentity_t *ent, usercmd_t *ucmd ) { client = ent->client; if ( client->sess.spectatorState != SPECTATOR_FOLLOW ) { - client->ps.pm_type = PM_SPECTATOR; + if ( client->noclip ) { + client->ps.pm_type = PM_NOCLIP; + } else { + client->ps.pm_type = PM_SPECTATOR; + } + client->ps.speed = 400; // faster than normal // set up for pmove From 2e5c4bd96a206eabd0801f59c914958d7ef22fc7 Mon Sep 17 00:00:00 2001 From: Zack Middleton Date: Sat, 24 Jun 2017 20:48:06 -0500 Subject: [PATCH 094/240] travis-ci: Don't run coverity_scan on master branch Once the weekly limit for Coverity scan is reached all builds exit success without compiling. --- .travis.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.travis.yml b/.travis.yml index 0d46590637..0fd2926017 100644 --- a/.travis.yml +++ b/.travis.yml @@ -34,4 +34,4 @@ addons: notification_email: quake3-commits@icculus.org build_command_prepend: "make clean" build_command: "make release" - branch_pattern: master + branch_pattern: coverity_scan From ccfc9011e24d952beb61a5051c90581cc7106e36 Mon Sep 17 00:00:00 2001 From: Zack Middleton Date: Sat, 24 Jun 2017 20:51:07 -0500 Subject: [PATCH 095/240] travis-ci: Upgrade to docker build system --- .travis.yml | 22 ++++++++++++++++------ 1 file changed, 16 insertions(+), 6 deletions(-) diff --git a/.travis.yml b/.travis.yml index 0fd2926017..09eb7699a4 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,3 +1,8 @@ +# sudo is required for travis-ci to use ubuntu trusty +# ubuntu trusty is required for libsdl2-dev +sudo: required +dist: trusty + language: c env: @@ -17,16 +22,21 @@ env: script: ./travis-ci-build.sh -before_install: - - echo "yes" | sudo apt-add-repository ppa:zoogie/sdl2-snapshots - - sudo apt-get update -qq - - sudo apt-get remove -qq -y mingw32 - - sudo apt-get install -q -y libgl1-mesa-dev libsdl2-dev libfreetype6-dev mingw-w64 - notifications: email: false addons: + apt: + packages: + - binutils-mingw-w64-i686 + - gcc-mingw-w64-i686 + - binutils-mingw-w64-x86-64 + - gcc-mingw-w64-x86-64 + - gcc-mingw-w64 + - mingw-w64 + - libgl1-mesa-dev + - libsdl2-dev + - libfreetype6-dev coverity_scan: project: name: "ioquake/ioq3" From 007e250e114d9f74ad3b3e1446082b652b1cff61 Mon Sep 17 00:00:00 2001 From: Zack Middleton Date: Thu, 29 Jun 2017 15:30:16 -0500 Subject: [PATCH 096/240] Split G_AddRandomBot into multiple functions --- code/game/g_bot.c | 123 +++++++++++++++++++++++++++------------------- 1 file changed, 73 insertions(+), 50 deletions(-) diff --git a/code/game/g_bot.c b/code/game/g_bot.c index c509772b12..52db7cbb19 100644 --- a/code/game/g_bot.c +++ b/code/game/g_bot.c @@ -230,71 +230,94 @@ static void PlayerIntroSound( const char *modelAndSkin ) { /* =============== -G_AddRandomBot +G_CountBotPlayersByName + +Returns number of bots with name on specified team or whole server if team is -1. =============== */ -void G_AddRandomBot( int team ) { - int i, n, num; - float skill; - char *value, netname[36], *teamstr; +int G_CountBotPlayersByName( const char *name, int team ) { + int i, num; gclient_t *cl; num = 0; - for ( n = 0; n < g_numBots ; n++ ) { - value = Info_ValueForKey( g_botInfos[n], "name" ); - // - for ( i=0 ; i< g_maxclients.integer ; i++ ) { - cl = level.clients + i; - if ( cl->pers.connected != CON_CONNECTED ) { - continue; - } - if ( !(g_entities[i].r.svFlags & SVF_BOT) ) { - continue; - } - if ( team >= 0 && cl->sess.sessionTeam != team ) { - continue; - } - if ( !Q_stricmp( value, cl->pers.netname ) ) { - break; - } + for ( i=0 ; i< g_maxclients.integer ; i++ ) { + cl = level.clients + i; + if ( cl->pers.connected != CON_CONNECTED ) { + continue; + } + if ( !(g_entities[i].r.svFlags & SVF_BOT) ) { + continue; } - if (i >= g_maxclients.integer) { - num++; + if ( team >= 0 && cl->sess.sessionTeam != team ) { + continue; + } + if ( name && Q_stricmp( name, cl->pers.netname ) ) { + continue; } + num++; } - num = random() * num; + return num; +} + +/* +=============== +G_SelectRandomBotInfo + +Get random unused bot info on team or whole server if team is -1. +=============== +*/ +int G_SelectRandomBotInfo( int team ) { + int selection[MAX_BOTS]; + int n, num; + char *value; + + num = 0; for ( n = 0; n < g_numBots ; n++ ) { value = Info_ValueForKey( g_botInfos[n], "name" ); // - for ( i=0 ; i< g_maxclients.integer ; i++ ) { - cl = level.clients + i; - if ( cl->pers.connected != CON_CONNECTED ) { - continue; - } - if ( !(g_entities[i].r.svFlags & SVF_BOT) ) { - continue; - } - if ( team >= 0 && cl->sess.sessionTeam != team ) { - continue; - } - if ( !Q_stricmp( value, cl->pers.netname ) ) { + if ( G_CountBotPlayersByName( value, team ) == 0 ) { + selection[num++] = n; + + if ( num == MAX_BOTS ) { break; } } - if (i >= g_maxclients.integer) { - num--; - if (num <= 0) { - skill = trap_Cvar_VariableValue( "g_spSkill" ); - if (team == TEAM_RED) teamstr = "red"; - else if (team == TEAM_BLUE) teamstr = "blue"; - else teamstr = ""; - Q_strncpyz(netname, value, sizeof(netname)); - Q_CleanStr(netname); - trap_SendConsoleCommand( EXEC_INSERT, va("addbot %s %f %s %i\n", netname, skill, teamstr, 0) ); - return; - } - } } + + if ( num > 0 ) { + num = random() * ( num - 1 ); + return selection[num]; + } + + return -1; +} + +/* +=============== +G_AddRandomBot +=============== +*/ +void G_AddRandomBot( int team ) { + int n; + char *value, netname[36], *teamstr; + float skill; + + n = G_SelectRandomBotInfo( team ); + + if ( n < 0 ) { + // all bot types are in use on team + return; + } + + value = Info_ValueForKey( g_botInfos[n], "name" ); + + skill = trap_Cvar_VariableValue( "g_spSkill" ); + if (team == TEAM_RED) teamstr = "red"; + else if (team == TEAM_BLUE) teamstr = "blue"; + else teamstr = ""; + Q_strncpyz(netname, value, sizeof(netname)); + Q_CleanStr(netname); + trap_SendConsoleCommand( EXEC_INSERT, va("addbot %s %f %s %i\n", netname, skill, teamstr, 0) ); } /* From 23a331c9f819a3465b14216096be794f207826e4 Mon Sep 17 00:00:00 2001 From: Zack Middleton Date: Thu, 29 Jun 2017 15:33:01 -0500 Subject: [PATCH 097/240] Make 'addbot random' command select a random bot info It reuses the random bot selection code for bot_minplayers cvar. --- code/game/g_bot.c | 62 +++++++++++++++++++++++++++++++++++------------ 1 file changed, 46 insertions(+), 16 deletions(-) diff --git a/code/game/g_bot.c b/code/game/g_bot.c index 52db7cbb19..3b1e0112fa 100644 --- a/code/game/g_bot.c +++ b/code/game/g_bot.c @@ -582,6 +582,8 @@ G_AddBot */ static void G_AddBot( const char *name, float skill, const char *team, int delay, char *altname) { int clientNum; + int teamNum; + int botinfoNum; char *botinfo; char *key; char *s; @@ -598,8 +600,50 @@ static void G_AddBot( const char *name, float skill, const char *team, int delay return; } + // set default team + if( !team || !*team ) { + if( g_gametype.integer >= GT_TEAM ) { + if( PickTeam(clientNum) == TEAM_RED) { + team = "red"; + } + else { + team = "blue"; + } + } + else { + team = "free"; + } + } + // get the botinfo from bots.txt - botinfo = G_GetBotInfoByName( name ); + if ( Q_stricmp( name, "random" ) == 0 ) { + if ( Q_stricmp( team, "red" ) == 0 || Q_stricmp( team, "r" ) == 0 ) { + teamNum = TEAM_RED; + } + else if ( Q_stricmp( team, "blue" ) == 0 || Q_stricmp( team, "b" ) == 0 ) { + teamNum = TEAM_BLUE; + } + else if ( !Q_stricmp( team, "spectator" ) || !Q_stricmp( team, "s" ) ) { + teamNum = TEAM_SPECTATOR; + } + else { + teamNum = TEAM_FREE; + } + + botinfoNum = G_SelectRandomBotInfo( teamNum ); + + if ( botinfoNum < 0 ) { + G_Printf( S_COLOR_YELLOW "WARNING: Cannot add random bot: all bot types in use on team '%s'.\n", team ); + trap_BotFreeClient( clientNum ); + return; + } + + botinfo = G_GetBotInfoByNumber( botinfoNum ); + } + else { + botinfo = G_GetBotInfoByName( name ); + } + if ( !botinfo ) { G_Printf( S_COLOR_RED "Error: Bot '%s' not defined\n", name ); trap_BotFreeClient( clientNum ); @@ -621,6 +665,7 @@ static void G_AddBot( const char *name, float skill, const char *team, int delay Info_SetValueForKey( userinfo, "rate", "25000" ); Info_SetValueForKey( userinfo, "snaps", "20" ); Info_SetValueForKey( userinfo, "skill", va("%.2f", skill) ); + Info_SetValueForKey( userinfo, "teampref", team ); if ( skill >= 1 && skill < 2 ) { Info_SetValueForKey( userinfo, "handicap", "50" ); @@ -679,21 +724,6 @@ static void G_AddBot( const char *name, float skill, const char *team, int delay } Info_SetValueForKey( userinfo, "characterfile", s ); - if( !team || !*team ) { - if( g_gametype.integer >= GT_TEAM ) { - if( PickTeam(clientNum) == TEAM_RED) { - team = "red"; - } - else { - team = "blue"; - } - } - else { - team = "red"; - } - } - Info_SetValueForKey( userinfo, "teampref", team ); - // register the userinfo trap_SetUserinfo( clientNum, userinfo ); From 51649695a56c86333113991458318d450935ec2b Mon Sep 17 00:00:00 2001 From: Zack Middleton Date: Thu, 29 Jun 2017 15:37:02 -0500 Subject: [PATCH 098/240] Fix random bot not looking for bots by funname Quake 3's Anarki bot has a 'funname' with colors in it. This commit fixes Anarki not being detected as in use. --- code/game/g_bot.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/code/game/g_bot.c b/code/game/g_bot.c index 3b1e0112fa..4d3eb612c8 100644 --- a/code/game/g_bot.c +++ b/code/game/g_bot.c @@ -273,7 +273,10 @@ int G_SelectRandomBotInfo( int team ) { num = 0; for ( n = 0; n < g_numBots ; n++ ) { - value = Info_ValueForKey( g_botInfos[n], "name" ); + value = Info_ValueForKey( g_botInfos[n], "funname" ); + if ( !value[0] ) { + value = Info_ValueForKey( g_botInfos[n], "name" ); + } // if ( G_CountBotPlayersByName( value, team ) == 0 ) { selection[num++] = n; From d0d1fe1b7c7e0321b06e170b6552fd1bd2194820 Mon Sep 17 00:00:00 2001 From: Zack Middleton Date: Thu, 29 Jun 2017 15:52:20 -0500 Subject: [PATCH 099/240] Fix bot_minplayers passing delay as team to addbot in non-team gametypes --- code/game/g_bot.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/code/game/g_bot.c b/code/game/g_bot.c index 4d3eb612c8..2c363ebd0d 100644 --- a/code/game/g_bot.c +++ b/code/game/g_bot.c @@ -317,7 +317,7 @@ void G_AddRandomBot( int team ) { skill = trap_Cvar_VariableValue( "g_spSkill" ); if (team == TEAM_RED) teamstr = "red"; else if (team == TEAM_BLUE) teamstr = "blue"; - else teamstr = ""; + else teamstr = "free"; Q_strncpyz(netname, value, sizeof(netname)); Q_CleanStr(netname); trap_SendConsoleCommand( EXEC_INSERT, va("addbot %s %f %s %i\n", netname, skill, teamstr, 0) ); From 7c601da6510252b17fdb1909f15d94f6a912142d Mon Sep 17 00:00:00 2001 From: Zack Middleton Date: Thu, 29 Jun 2017 15:59:09 -0500 Subject: [PATCH 100/240] Fix not adding random bot when all bot info are in use on team If there are two bot infos in scripts/bots.txt then each team can only add two random bots via bot_minplayers or addbot random. Pick random bot info from least used bot infos instead of only ones that are used by zero players. That way a random bot can always be added to the game. This rarely affected Quake 3 since there is 32 bot infos. It could easily affect new games though. --- code/game/g_bot.c | 17 +++++++++++++---- 1 file changed, 13 insertions(+), 4 deletions(-) diff --git a/code/game/g_bot.c b/code/game/g_bot.c index 2c363ebd0d..fc6a321400 100644 --- a/code/game/g_bot.c +++ b/code/game/g_bot.c @@ -263,22 +263,31 @@ int G_CountBotPlayersByName( const char *name, int team ) { =============== G_SelectRandomBotInfo -Get random unused bot info on team or whole server if team is -1. +Get random least used bot info on team or whole server if team is -1. =============== */ int G_SelectRandomBotInfo( int team ) { int selection[MAX_BOTS]; int n, num; + int count, bestCount; char *value; num = 0; + bestCount = MAX_CLIENTS; for ( n = 0; n < g_numBots ; n++ ) { value = Info_ValueForKey( g_botInfos[n], "funname" ); if ( !value[0] ) { value = Info_ValueForKey( g_botInfos[n], "name" ); } // - if ( G_CountBotPlayersByName( value, team ) == 0 ) { + count = G_CountBotPlayersByName( value, team ); + + if ( count < bestCount ) { + bestCount = count; + num = 0; + } + + if ( count == bestCount ) { selection[num++] = n; if ( num == MAX_BOTS ) { @@ -308,7 +317,7 @@ void G_AddRandomBot( int team ) { n = G_SelectRandomBotInfo( team ); if ( n < 0 ) { - // all bot types are in use on team + // no bot info available return; } @@ -636,7 +645,7 @@ static void G_AddBot( const char *name, float skill, const char *team, int delay botinfoNum = G_SelectRandomBotInfo( teamNum ); if ( botinfoNum < 0 ) { - G_Printf( S_COLOR_YELLOW "WARNING: Cannot add random bot: all bot types in use on team '%s'.\n", team ); + G_Printf( S_COLOR_RED "Error: Cannot add random bot, no bot info available.\n" ); trap_BotFreeClient( clientNum ); return; } From d8f2ff7a4b452ebe30bf5628cb3710284066359b Mon Sep 17 00:00:00 2001 From: Zack Middleton Date: Thu, 29 Jun 2017 16:40:22 -0500 Subject: [PATCH 101/240] Check delayed bot's team when counting bots for bot_minplayers note: This requires my previous commit that added teampref userinfo so that bots choose correct team in ClientConnect. --- code/game/g_bot.c | 15 ++++----------- 1 file changed, 4 insertions(+), 11 deletions(-) diff --git a/code/game/g_bot.c b/code/game/g_bot.c index fc6a321400..1ec4f68013 100644 --- a/code/game/g_bot.c +++ b/code/game/g_bot.c @@ -387,16 +387,18 @@ int G_CountHumanPlayers( int team ) { /* =============== G_CountBotPlayers + +Check connected and connecting (delay join) bots. =============== */ int G_CountBotPlayers( int team ) { - int i, n, num; + int i, num; gclient_t *cl; num = 0; for ( i=0 ; i< g_maxclients.integer ; i++ ) { cl = level.clients + i; - if ( cl->pers.connected != CON_CONNECTED ) { + if ( cl->pers.connected == CON_DISCONNECTED ) { continue; } if ( !(g_entities[i].r.svFlags & SVF_BOT) ) { @@ -407,15 +409,6 @@ int G_CountBotPlayers( int team ) { } num++; } - for( n = 0; n < BOT_SPAWN_QUEUE_DEPTH; n++ ) { - if( !botSpawnQueue[n].spawnTime ) { - continue; - } - if ( botSpawnQueue[n].spawnTime > level.time ) { - continue; - } - num++; - } return num; } From 0999aff28dbc198b19e38399cc50c468bbbdb0b3 Mon Sep 17 00:00:00 2001 From: Zack Middleton Date: Thu, 29 Jun 2017 17:07:32 -0500 Subject: [PATCH 102/240] Fix duplicate (delayed) random bots being choosen Count delayed bots when looking for least used bot infos for deciding which bot to add. --- code/game/g_bot.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/code/game/g_bot.c b/code/game/g_bot.c index 1ec4f68013..bda1f7d55b 100644 --- a/code/game/g_bot.c +++ b/code/game/g_bot.c @@ -232,6 +232,8 @@ static void PlayerIntroSound( const char *modelAndSkin ) { =============== G_CountBotPlayersByName +Check connected and connecting (delay join) bots. + Returns number of bots with name on specified team or whole server if team is -1. =============== */ @@ -242,7 +244,7 @@ int G_CountBotPlayersByName( const char *name, int team ) { num = 0; for ( i=0 ; i< g_maxclients.integer ; i++ ) { cl = level.clients + i; - if ( cl->pers.connected != CON_CONNECTED ) { + if ( cl->pers.connected == CON_DISCONNECTED ) { continue; } if ( !(g_entities[i].r.svFlags & SVF_BOT) ) { From cabc32362c1093320ce4b03d939df2deabba020a Mon Sep 17 00:00:00 2001 From: Zack Middleton Date: Thu, 29 Jun 2017 17:51:30 -0500 Subject: [PATCH 103/240] Don't pick duplicate random bots until all bot types are added Don't add the same bot to multiple teams until there are as many bots on the server as there are bot types. Previously each team would have unique bots until all bot types were added to the team but other teams may have the same bot. Now there will not be any duplicate bots until there are more bots than bot types. Now Quake 3 (32 bot types) in 16 vs 16 bot CTF will not contain duplicate bot types. (You have to increase memory in code/game/ g_mem.c in order to add 32 bots though.) I had to change G_AddRandomBot() to use 'addbot random' or else the same bot could be added to red and blue teams. The bot was selected and stored in console command buffer so game doesn't know not to select the bot again. --- code/game/g_bot.c | 21 +++++++-------------- 1 file changed, 7 insertions(+), 14 deletions(-) diff --git a/code/game/g_bot.c b/code/game/g_bot.c index bda1f7d55b..66c9ae34ac 100644 --- a/code/game/g_bot.c +++ b/code/game/g_bot.c @@ -274,6 +274,11 @@ int G_SelectRandomBotInfo( int team ) { int count, bestCount; char *value; + // don't add duplicate bots to the server if there are less bots than bot types + if ( team != -1 && G_CountBotPlayersByName( NULL, -1 ) < g_numBots ) { + team = -1; + } + num = 0; bestCount = MAX_CLIENTS; for ( n = 0; n < g_numBots ; n++ ) { @@ -312,26 +317,14 @@ G_AddRandomBot =============== */ void G_AddRandomBot( int team ) { - int n; - char *value, netname[36], *teamstr; + char *teamstr; float skill; - n = G_SelectRandomBotInfo( team ); - - if ( n < 0 ) { - // no bot info available - return; - } - - value = Info_ValueForKey( g_botInfos[n], "name" ); - skill = trap_Cvar_VariableValue( "g_spSkill" ); if (team == TEAM_RED) teamstr = "red"; else if (team == TEAM_BLUE) teamstr = "blue"; else teamstr = "free"; - Q_strncpyz(netname, value, sizeof(netname)); - Q_CleanStr(netname); - trap_SendConsoleCommand( EXEC_INSERT, va("addbot %s %f %s %i\n", netname, skill, teamstr, 0) ); + trap_SendConsoleCommand( EXEC_INSERT, va("addbot random %f %s %i\n", skill, teamstr, 0) ); } /* From b984dd4a234da326ff50fdaa3f8d38516f4fd98b Mon Sep 17 00:00:00 2001 From: Zack Middleton Date: Thu, 29 Jun 2017 19:22:25 -0500 Subject: [PATCH 104/240] Add range check for bot skill in addbot command Adding a bot with skill of 0 doesn't show icon on hud. --- code/game/g_bot.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/code/game/g_bot.c b/code/game/g_bot.c index 66c9ae34ac..af4906a339 100644 --- a/code/game/g_bot.c +++ b/code/game/g_bot.c @@ -772,7 +772,7 @@ void Svcmd_AddBot_f( void ) { skill = 4; } else { - skill = atof( string ); + skill = Com_Clamp( 1, 5, atof( string ) ); } // team From 4b5067cce289b6fd6914e802a9d196a2f2b6178a Mon Sep 17 00:00:00 2001 From: Zack Middleton Date: Thu, 29 Jun 2017 19:57:13 -0500 Subject: [PATCH 105/240] Add 'addbot random' to README --- README.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/README.md b/README.md index 1e5d896544..3dd355ce36 100644 --- a/README.md +++ b/README.md @@ -333,6 +333,8 @@ The defaults for these variables differ depending on the target platform. cvar_modified [filter] - list modified cvars, can filter results (such as "r*" for renderer cvars) like cvarlist which lists all cvars + + addbot random - the bot name "random" now selects a random bot ``` From 09a23e04177202d197348104e1991f0bd622045a Mon Sep 17 00:00:00 2001 From: Zack Middleton Date: Thu, 29 Jun 2017 21:34:39 -0500 Subject: [PATCH 106/240] OpenGL2: Fix checking r_shadowCascadeZFar pointer instead of value --- code/renderergl2/tr_scene.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/code/renderergl2/tr_scene.c b/code/renderergl2/tr_scene.c index 2d039e7c15..e336978ee3 100644 --- a/code/renderergl2/tr_scene.c +++ b/code/renderergl2/tr_scene.c @@ -491,7 +491,7 @@ void RE_RenderScene( const refdef_t *fd ) { // playing with even more shadows if(glRefConfig.framebufferObject && r_sunlightMode->integer && !( fd->rdflags & RDF_NOWORLDMODEL ) && (r_forceSun->integer || tr.sunShadows)) { - if (r_shadowCascadeZFar != 0) + if (r_shadowCascadeZFar->integer != 0) { R_RenderSunShadowMaps(fd, 0); R_RenderSunShadowMaps(fd, 1); From 102c79eb4985464d4e5967cdb760816e120fe089 Mon Sep 17 00:00:00 2001 From: Zack Middleton Date: Fri, 30 Jun 2017 13:48:38 -0500 Subject: [PATCH 107/240] OpenGL2: Fix black planar projection shadows (cg_shadows 3) Restore MD3 code for cg_shadows 2 and 3 like other model formats. Fix planar projection shadow deform (cg_shadows 3) to use correct light direction. I fixed light direction for stencil shadows (cg_shadows 2) but it's still broken. --- code/renderergl2/tr_mesh.c | 26 +++++++++++++++++++++----- code/renderergl2/tr_shadows.c | 4 ++-- 2 files changed, 23 insertions(+), 7 deletions(-) diff --git a/code/renderergl2/tr_mesh.c b/code/renderergl2/tr_mesh.c index 0d6844a43c..5f7c4ee938 100644 --- a/code/renderergl2/tr_mesh.c +++ b/code/renderergl2/tr_mesh.c @@ -385,12 +385,28 @@ void R_AddMD3Surfaces( trRefEntity_t *ent ) { shader = tr.shaders[ surface->shaderIndexes[ ent->e.skinNum % surface->numShaderIndexes ] ]; } - // don't add third_person objects if not viewing through a portal - if(!personalModel) - { - srfVaoMdvMesh_t *vaoSurface = &model->vaoSurfaces[i]; + // we will add shadows even if the main object isn't visible in the view + + // stencil shadows can't do personal models unless I polyhedron clip + if ( !personalModel + && r_shadows->integer == 2 + && fogNum == 0 + && !(ent->e.renderfx & ( RF_NOSHADOW | RF_DEPTHHACK ) ) + && shader->sort == SS_OPAQUE ) { + R_AddDrawSurf( (void *)&model->vaoSurfaces[i], tr.shadowShader, 0, qfalse, qfalse, 0 ); + } - R_AddDrawSurf((void *)vaoSurface, shader, fogNum, qfalse, qfalse, cubemapIndex ); + // projection shadows work fine with personal models + if ( r_shadows->integer == 3 + && fogNum == 0 + && (ent->e.renderfx & RF_SHADOW_PLANE ) + && shader->sort == SS_OPAQUE ) { + R_AddDrawSurf( (void *)&model->vaoSurfaces[i], tr.projectionShadowShader, 0, qfalse, qfalse, 0 ); + } + + // don't add third_person objects if not viewing through a portal + if ( !personalModel ) { + R_AddDrawSurf((void *)&model->vaoSurfaces[i], shader, fogNum, qfalse, qfalse, cubemapIndex ); } surface++; diff --git a/code/renderergl2/tr_shadows.c b/code/renderergl2/tr_shadows.c index 7637175254..520bc3f2a5 100644 --- a/code/renderergl2/tr_shadows.c +++ b/code/renderergl2/tr_shadows.c @@ -162,7 +162,7 @@ void RB_ShadowTessEnd( void ) { return; } - VectorCopy( backEnd.currentEntity->lightDir, lightDir ); + VectorCopy( backEnd.currentEntity->modelLightDir, lightDir ); // project vertexes away from light direction for ( i = 0 ; i < tess.numVertexes ; i++ ) { @@ -302,7 +302,7 @@ void RB_ProjectionShadowDeform( void ) { groundDist = backEnd.or.origin[2] - backEnd.currentEntity->e.shadowPlane; - VectorCopy( backEnd.currentEntity->lightDir, lightDir ); + VectorCopy( backEnd.currentEntity->modelLightDir, lightDir ); d = DotProduct( lightDir, ground ); // don't let the shadows get too long or go negative if ( d < 0.5 ) { From e77153766a2beff3b4fd4b115e52e0a617abb9c0 Mon Sep 17 00:00:00 2001 From: Zack Middleton Date: Fri, 30 Jun 2017 13:59:07 -0500 Subject: [PATCH 108/240] OpenGL2: Draw sun shadows for first person IQM player models This makes IQM have the same behavior as MD3 and MDR. --- code/renderergl2/tr_model_iqm.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/code/renderergl2/tr_model_iqm.c b/code/renderergl2/tr_model_iqm.c index ce6d3610f9..67d3491cd3 100644 --- a/code/renderergl2/tr_model_iqm.c +++ b/code/renderergl2/tr_model_iqm.c @@ -849,7 +849,8 @@ void R_AddIQMSurfaces( trRefEntity_t *ent ) { surface = data->surfaces; // don't add third_person objects if not in a portal - personalModel = (ent->e.renderfx & RF_THIRD_PERSON) && !tr.viewParms.isPortal; + personalModel = (ent->e.renderfx & RF_THIRD_PERSON) && !(tr.viewParms.isPortal + || (tr.viewParms.flags & (VPF_SHADOWMAP | VPF_DEPTHSHADOW))); if ( ent->e.renderfx & RF_WRAP_FRAMES ) { ent->e.frame %= data->num_frames; From d9c2e9191937e96ea0d40e4e5ac2e7253da978b9 Mon Sep 17 00:00:00 2001 From: Zack Middleton Date: Sat, 1 Jul 2017 11:26:17 -0500 Subject: [PATCH 109/240] Fix q3_ui cursor going off screen in widescreen --- code/q3_ui/ui_atoms.c | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/code/q3_ui/ui_atoms.c b/code/q3_ui/ui_atoms.c index 020a33e5e3..f369d353fb 100644 --- a/code/q3_ui/ui_atoms.c +++ b/code/q3_ui/ui_atoms.c @@ -872,17 +872,21 @@ UI_MouseEvent void UI_MouseEvent( int dx, int dy ) { int i; + int bias; menucommon_s* m; if (!uis.activemenu) return; + // convert X bias to 640 coords + bias = uis.bias / uis.xscale; + // update mouse screen position uis.cursorx += dx; - if (uis.cursorx < -uis.bias) - uis.cursorx = -uis.bias; - else if (uis.cursorx > SCREEN_WIDTH+uis.bias) - uis.cursorx = SCREEN_WIDTH+uis.bias; + if (uis.cursorx < -bias) + uis.cursorx = -bias; + else if (uis.cursorx > SCREEN_WIDTH+bias) + uis.cursorx = SCREEN_WIDTH+bias; uis.cursory += dy; if (uis.cursory < 0) From 21eeaee9c840ac781996c06e97071dda6a2dc600 Mon Sep 17 00:00:00 2001 From: Zack Middleton Date: Sat, 1 Jul 2017 11:27:31 -0500 Subject: [PATCH 110/240] Make Team Arena UI aspect correct in widescreen --- code/ui/ui_atoms.c | 10 +--------- code/ui/ui_main.c | 36 ++++++++++++++++++++++++------------ 2 files changed, 25 insertions(+), 21 deletions(-) diff --git a/code/ui/ui_atoms.c b/code/ui/ui_atoms.c index cdc4aeb05e..44ff3b0eb8 100644 --- a/code/ui/ui_atoms.c +++ b/code/ui/ui_atoms.c @@ -420,18 +420,10 @@ Adjusted for resolution and screen aspect ratio */ void UI_AdjustFrom640( float *x, float *y, float *w, float *h ) { // expect valid pointers -#if 0 - *x = *x * uiInfo.uiDC.scale + uiInfo.uiDC.bias; - *y *= uiInfo.uiDC.scale; - *w *= uiInfo.uiDC.scale; - *h *= uiInfo.uiDC.scale; -#endif - - *x *= uiInfo.uiDC.xscale; + *x = *x * uiInfo.uiDC.xscale + uiInfo.uiDC.bias; *y *= uiInfo.uiDC.yscale; *w *= uiInfo.uiDC.xscale; *h *= uiInfo.uiDC.yscale; - } void UI_DrawNamedPic( float x, float y, float width, float height, const char *picname ) { diff --git a/code/ui/ui_main.c b/code/ui/ui_main.c index 3660470529..c4f3ba54fe 100644 --- a/code/ui/ui_main.c +++ b/code/ui/ui_main.c @@ -129,6 +129,7 @@ static void UI_ParseTeamInfo(const char *teamFile); static const char *UI_SelectedMap(int index, int *actual); static const char *UI_SelectedHead(int index, int *actual); static int UI_GetIndexFromSelection(int actual); +static void UI_DrawCinematic(int handle, float x, float y, float w, float h); int ProcessNewUI( int command, int arg0, int arg1, int arg2, int arg3, int arg4, int arg5, int arg6 ); @@ -1110,8 +1111,7 @@ static void UI_DrawClanCinematic(rectDef_t *rect, float scale, vec4_t color) { } if (uiInfo.teamList[i].cinematic >= 0) { trap_CIN_RunCinematic(uiInfo.teamList[i].cinematic); - trap_CIN_SetExtents(uiInfo.teamList[i].cinematic, rect->x, rect->y, rect->w, rect->h); - trap_CIN_DrawCinematic(uiInfo.teamList[i].cinematic); + UI_DrawCinematic(uiInfo.teamList[i].cinematic, rect->x, rect->y, rect->w, rect->h); } else { trap_R_SetColor( color ); UI_DrawHandlePic( rect->x, rect->y, rect->w, rect->h, uiInfo.teamList[i].teamIcon_Metal); @@ -1132,8 +1132,7 @@ static void UI_DrawPreviewCinematic(rectDef_t *rect, float scale, vec4_t color) uiInfo.previewMovie = trap_CIN_PlayCinematic(va("%s.roq", uiInfo.movieList[uiInfo.movieIndex]), 0, 0, 0, 0, (CIN_loop | CIN_silent) ); if (uiInfo.previewMovie >= 0) { trap_CIN_RunCinematic(uiInfo.previewMovie); - trap_CIN_SetExtents(uiInfo.previewMovie, rect->x, rect->y, rect->w, rect->h); - trap_CIN_DrawCinematic(uiInfo.previewMovie); + UI_DrawCinematic(uiInfo.previewMovie, rect->x, rect->y, rect->w, rect->h); } else { uiInfo.previewMovie = -2; } @@ -1256,8 +1255,7 @@ static void UI_DrawMapCinematic(rectDef_t *rect, float scale, vec4_t color, qboo } if (uiInfo.mapList[map].cinematic >= 0) { trap_CIN_RunCinematic(uiInfo.mapList[map].cinematic); - trap_CIN_SetExtents(uiInfo.mapList[map].cinematic, rect->x, rect->y, rect->w, rect->h); - trap_CIN_DrawCinematic(uiInfo.mapList[map].cinematic); + UI_DrawCinematic(uiInfo.mapList[map].cinematic, rect->x, rect->y, rect->w, rect->h); } else { uiInfo.mapList[map].cinematic = -2; } @@ -1337,8 +1335,7 @@ static void UI_DrawNetMapCinematic(rectDef_t *rect, float scale, vec4_t color) { if (uiInfo.serverStatus.currentServerCinematic >= 0) { trap_CIN_RunCinematic(uiInfo.serverStatus.currentServerCinematic); - trap_CIN_SetExtents(uiInfo.serverStatus.currentServerCinematic, rect->x, rect->y, rect->w, rect->h); - trap_CIN_DrawCinematic(uiInfo.serverStatus.currentServerCinematic); + UI_DrawCinematic(uiInfo.serverStatus.currentServerCinematic, rect->x, rect->y, rect->w, rect->h); } else { UI_DrawNetMapPreview(rect, scale, color); } @@ -4974,6 +4971,15 @@ static void UI_StopCinematic(int handle) { } static void UI_DrawCinematic(int handle, float x, float y, float w, float h) { + // adjust coords to get correct placement in wide screen + UI_AdjustFrom640( &x, &y, &w, &h ); + + // CIN_SetExtents takes stretched 640x480 virtualized coords + x *= SCREEN_WIDTH / (float)uiInfo.uiDC.glconfig.vidWidth; + w *= SCREEN_WIDTH / (float)uiInfo.uiDC.glconfig.vidWidth; + y *= SCREEN_HEIGHT / (float)uiInfo.uiDC.glconfig.vidHeight; + h *= SCREEN_HEIGHT / (float)uiInfo.uiDC.glconfig.vidHeight; + trap_CIN_SetExtents(handle, x, y, w, h); trap_CIN_DrawCinematic(handle); } @@ -5077,6 +5083,7 @@ void _UI_Init( qboolean inGameLoad ) { if ( uiInfo.uiDC.glconfig.vidWidth * 480 > uiInfo.uiDC.glconfig.vidHeight * 640 ) { // wide screen uiInfo.uiDC.bias = 0.5 * ( uiInfo.uiDC.glconfig.vidWidth - ( uiInfo.uiDC.glconfig.vidHeight * (640.0/480.0) ) ); + uiInfo.uiDC.xscale = uiInfo.uiDC.yscale; } else { // no wide screen @@ -5234,12 +5241,17 @@ UI_MouseEvent */ void _UI_MouseEvent( int dx, int dy ) { + int bias; + + // convert X bias to 640 coords + bias = uiInfo.uiDC.bias / uiInfo.uiDC.xscale; + // update mouse screen position uiInfo.uiDC.cursorx += dx; - if (uiInfo.uiDC.cursorx < 0) - uiInfo.uiDC.cursorx = 0; - else if (uiInfo.uiDC.cursorx > SCREEN_WIDTH) - uiInfo.uiDC.cursorx = SCREEN_WIDTH; + if (uiInfo.uiDC.cursorx < -bias) + uiInfo.uiDC.cursorx = -bias; + else if (uiInfo.uiDC.cursorx > SCREEN_WIDTH+bias) + uiInfo.uiDC.cursorx = SCREEN_WIDTH+bias; uiInfo.uiDC.cursory += dy; if (uiInfo.uiDC.cursory < 0) From 0ba359c29ec543ba4b6bdf62d1daa603208952d7 Mon Sep 17 00:00:00 2001 From: Zack Middleton Date: Sat, 1 Jul 2017 12:46:54 -0500 Subject: [PATCH 111/240] Make UI continue searching for local servers until found When refreshing local servers, Team Arean UI never changed the status message when it timed out. This gave a false impression it was still looking for servers. Let's continue looking for local servers in q3_ui and Team Arena UI until one is found. --- code/q3_ui/ui_servers2.c | 7 +++++++ code/ui/ui_main.c | 6 +++++- 2 files changed, 12 insertions(+), 1 deletion(-) diff --git a/code/q3_ui/ui_servers2.c b/code/q3_ui/ui_servers2.c index d376d363bf..b6c4ea5d09 100644 --- a/code/q3_ui/ui_servers2.c +++ b/code/q3_ui/ui_servers2.c @@ -881,6 +881,13 @@ static void ArenaServers_DoRefresh( void ) return; } } + } else if (g_servertype == UIAS_LOCAL) { + if (!trap_LAN_GetServerCount(AS_LOCAL)) { + // no local servers found, check again + trap_Cmd_ExecuteText( EXEC_APPEND, "localservers\n" ); + g_arenaservers.refreshtime = uis.realtime + 5000; + return; + } } if (uis.realtime < g_arenaservers.nextpingtime) diff --git a/code/ui/ui_main.c b/code/ui/ui_main.c index c4f3ba54fe..81b324b553 100644 --- a/code/ui/ui_main.c +++ b/code/ui/ui_main.c @@ -5985,6 +5985,10 @@ static void UI_DoServerRefresh( void ) UI_BuildServerDisplayList(2); // stop the refresh UI_StopServerRefresh(); + } else if ( ui_netSource.integer == UIAS_LOCAL ) { + // no local servers found, check again + trap_Cmd_ExecuteText( EXEC_NOW, "localservers\n" ); + uiInfo.serverStatus.refreshtime = uiInfo.uiDC.realTime + 5000; } // UI_BuildServerDisplayList(qfalse); @@ -6032,7 +6036,7 @@ static void UI_StartServerRefresh(qboolean full, qboolean force) // if( ui_netSource.integer == UIAS_LOCAL ) { trap_Cmd_ExecuteText( EXEC_NOW, "localservers\n" ); - uiInfo.serverStatus.refreshtime = uiInfo.uiDC.realTime + 1000; + uiInfo.serverStatus.refreshtime = uiInfo.uiDC.realTime + 5000; return; } From 8c3d1fcf59da56620282dd50e40c42b4830a8e0e Mon Sep 17 00:00:00 2001 From: Zack Middleton Date: Sat, 1 Jul 2017 13:30:16 -0500 Subject: [PATCH 112/240] Allow changing q3_ui server source during refresh This makes it easier to scroll through the list, especially since "Local" source now refreshes until a local server is found. --- code/q3_ui/ui_servers2.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/code/q3_ui/ui_servers2.c b/code/q3_ui/ui_servers2.c index b6c4ea5d09..73e41b6554 100644 --- a/code/q3_ui/ui_servers2.c +++ b/code/q3_ui/ui_servers2.c @@ -421,7 +421,6 @@ static void ArenaServers_UpdateMenu( void ) { } else { // all servers pinged - enable controls - g_arenaservers.master.generic.flags &= ~QMF_GRAYED; g_arenaservers.gametype.generic.flags &= ~QMF_GRAYED; g_arenaservers.sortkey.generic.flags &= ~QMF_GRAYED; g_arenaservers.showempty.generic.flags &= ~QMF_GRAYED; @@ -448,7 +447,6 @@ static void ArenaServers_UpdateMenu( void ) { g_arenaservers.statusbar.string = "Press SPACE to stop"; // disable controls during refresh - g_arenaservers.master.generic.flags |= QMF_GRAYED; g_arenaservers.gametype.generic.flags |= QMF_GRAYED; g_arenaservers.sortkey.generic.flags |= QMF_GRAYED; g_arenaservers.showempty.generic.flags |= QMF_GRAYED; @@ -1128,6 +1126,8 @@ ArenaServers_SetType */ int ArenaServers_SetType( int type ) { + ArenaServers_StopRefresh(); + if(type >= UIAS_GLOBAL1 && type <= UIAS_GLOBAL5) { char masterstr[2], cvarname[sizeof("sv_master1")]; From cbd9e432b58477d45bbce3d027ad2c8c05e492e3 Mon Sep 17 00:00:00 2001 From: Zack Middleton Date: Sat, 1 Jul 2017 14:10:46 -0500 Subject: [PATCH 113/240] Fix nonresponsive favorites in q3_ui having no hostname ArenaServers_InsertFavorites() fails to do anything because favorites are added to the server list even if nonresponsive. Set nonresponsive favorite server hostname to address. --- code/q3_ui/ui_servers2.c | 44 ++++++---------------------------------- 1 file changed, 6 insertions(+), 38 deletions(-) diff --git a/code/q3_ui/ui_servers2.c b/code/q3_ui/ui_servers2.c index 73e41b6554..483080534f 100644 --- a/code/q3_ui/ui_servers2.c +++ b/code/q3_ui/ui_servers2.c @@ -717,38 +717,6 @@ static void ArenaServers_Insert( char* adrstr, char* info, int pingtime ) } -/* -================= -ArenaServers_InsertFavorites - -Insert nonresponsive address book entries into display lists. -================= -*/ -void ArenaServers_InsertFavorites( void ) -{ - int i; - int j; - char info[MAX_INFO_STRING]; - - // resync existing results with new or deleted cvars - info[0] = '\0'; - Info_SetValueForKey( info, "hostname", "No Response" ); - for (i=0; i= g_numfavoriteservers) - { - // not in list, add it - ArenaServers_Insert( g_arenaservers.favoriteaddresses[i], info, ArenaServers_MaxPing() ); - } - } -} - - /* ================= ArenaServers_LoadFavorites @@ -832,12 +800,6 @@ static void ArenaServers_StopRefresh( void ) g_arenaservers.refreshservers = qfalse; - if (g_servertype == UIAS_FAVORITES) - { - // nonresponsive favorites must be shown - ArenaServers_InsertFavorites(); - } - // final tally if (g_arenaservers.numqueriedservers >= 0) { @@ -931,6 +893,12 @@ static void ArenaServers_DoRefresh( void ) // stale it out info[0] = '\0'; time = maxPing; + + // set hostname for nonresponsive favorite server + if (g_servertype == UIAS_FAVORITES) { + Info_SetValueForKey( info, "hostname", adrstr ); + Info_SetValueForKey( info, "game", "???" ); + } } else { From 5aa7fb39c266dafc03d52d56aec91b29b1b5640d Mon Sep 17 00:00:00 2001 From: Zack Middleton Date: Sun, 2 Jul 2017 16:47:55 -0500 Subject: [PATCH 114/240] OpenGL2: Remove two unused cvars and update readme --- code/renderergl2/tr_init.c | 5 ----- code/renderergl2/tr_local.h | 3 --- opengl2-readme.md | 21 ++------------------- 3 files changed, 2 insertions(+), 27 deletions(-) diff --git a/code/renderergl2/tr_init.c b/code/renderergl2/tr_init.c index 3f36496089..2e63559383 100644 --- a/code/renderergl2/tr_init.c +++ b/code/renderergl2/tr_init.c @@ -105,9 +105,6 @@ cvar_t *r_arb_seamless_cube_map; cvar_t *r_arb_vertex_array_object; cvar_t *r_ext_direct_state_access; -cvar_t *r_mergeMultidraws; -cvar_t *r_mergeLeafSurfaces; - cvar_t *r_cameraExposure; cvar_t *r_externalGLSL; @@ -1293,8 +1290,6 @@ void R_Register( void ) r_directedScale = ri.Cvar_Get( "r_directedScale", "1", CVAR_CHEAT ); r_anaglyphMode = ri.Cvar_Get("r_anaglyphMode", "0", CVAR_ARCHIVE); - r_mergeMultidraws = ri.Cvar_Get("r_mergeMultidraws", "1", CVAR_ARCHIVE); - r_mergeLeafSurfaces = ri.Cvar_Get("r_mergeLeafSurfaces", "1", CVAR_ARCHIVE); // // temporary variables that can change at any time diff --git a/code/renderergl2/tr_local.h b/code/renderergl2/tr_local.h index 7e8e4b7717..1fc91f7098 100644 --- a/code/renderergl2/tr_local.h +++ b/code/renderergl2/tr_local.h @@ -1708,9 +1708,6 @@ extern cvar_t *r_skipBackEnd; extern cvar_t *r_anaglyphMode; -extern cvar_t *r_mergeMultidraws; -extern cvar_t *r_mergeLeafSurfaces; - extern cvar_t *r_externalGLSL; extern cvar_t *r_hdr; diff --git a/opengl2-readme.md b/opengl2-readme.md index 857a4600d7..1b1db2034d 100644 --- a/opengl2-readme.md +++ b/opengl2-readme.md @@ -136,7 +136,7 @@ Cvars for HDR and tonemapping: r_hdr, r_postprocess, and r_toneMap. 0 - No. 1 - Yes. (default) - + * `r_forceAutoExposure` - Cheat. Override built-in and map auto exposure settings and use cvars r_forceAutoExposureMin and @@ -252,7 +252,7 @@ Cvars for image interpolation and generation: FCBI without second derivatives) 2 - Okay but slow (normal FCBI) -* `r_genNormalMaps* - Naively generate normal maps for all +* `r_genNormalMaps` - Naively generate normal maps for all textures. 0 - Don't. (default) 1 - Do. @@ -302,23 +302,6 @@ Cvars for the sunlight and cascaded shadow maps: Cvars that you probably don't care about or shouldn't mess with: -* `r_mergeMultidraws` - Optimize number of calls to - glMultiDrawElements(). - 0 - Don't. - 1 - Do some. (default) - 2 - Do more than necessary (eats CPU). - -* `r_mergeLeafSurfaces` - Merge surfaces that share common materials - and a common leaf. Speeds up rendering. - 0 - Don't. - 1 - Do. (default) - -* `r_recalcMD3Normals` - Recalculate the normals when loading an MD3. - Fixes normal maps in some cases but looks - ugly in others. - 0 - Don't. (default) - 1 - Do. - * `r_depthPrepass` - Do a depth-only pass before rendering. Speeds up rendering in cases where advanced features are used. Required for From 4dffc52c1dac517073123e29d19cc7c4b9452541 Mon Sep 17 00:00:00 2001 From: Zack Middleton Date: Sun, 2 Jul 2017 16:57:49 -0500 Subject: [PATCH 115/240] Add warnings for animMap and videoMap shader keywords --- code/renderergl1/tr_shader.c | 10 ++++++++++ code/renderergl2/tr_shader.c | 10 ++++++++++ 2 files changed, 20 insertions(+) diff --git a/code/renderergl1/tr_shader.c b/code/renderergl1/tr_shader.c index c931326fbb..c1d9628b60 100644 --- a/code/renderergl1/tr_shader.c +++ b/code/renderergl1/tr_shader.c @@ -687,6 +687,8 @@ static qboolean ParseStage( shaderStage_t *stage, char **text ) // else if ( !Q_stricmp( token, "animMap" ) ) { + int totalImages = 0; + token = COM_ParseExt( text, qfalse ); if ( !token[0] ) { @@ -721,6 +723,12 @@ static qboolean ParseStage( shaderStage_t *stage, char **text ) } stage->bundle[0].numImageAnimations++; } + totalImages++; + } + + if ( totalImages > MAX_IMAGE_ANIMATIONS ) { + ri.Printf( PRINT_WARNING, "WARNING: ignoring excess images for 'animMap' (found %d, max is %d) in shader '%s'\n", + totalImages, MAX_IMAGE_ANIMATIONS, shader.name ); } } else if ( !Q_stricmp( token, "videoMap" ) ) @@ -735,6 +743,8 @@ static qboolean ParseStage( shaderStage_t *stage, char **text ) if (stage->bundle[0].videoMapHandle != -1) { stage->bundle[0].isVideoMap = qtrue; stage->bundle[0].image[0] = tr.scratchImage[stage->bundle[0].videoMapHandle]; + } else { + ri.Printf( PRINT_WARNING, "WARNING: could not load '%s' for 'videoMap' keyword in shader '%s'\n", token, shader.name ); } } // diff --git a/code/renderergl2/tr_shader.c b/code/renderergl2/tr_shader.c index 574cfb15cf..1df374e68a 100644 --- a/code/renderergl2/tr_shader.c +++ b/code/renderergl2/tr_shader.c @@ -744,6 +744,8 @@ static qboolean ParseStage( shaderStage_t *stage, char **text ) // else if ( !Q_stricmp( token, "animMap" ) ) { + int totalImages = 0; + token = COM_ParseExt( text, qfalse ); if ( !token[0] ) { @@ -778,6 +780,12 @@ static qboolean ParseStage( shaderStage_t *stage, char **text ) } stage->bundle[0].numImageAnimations++; } + totalImages++; + } + + if ( totalImages > MAX_IMAGE_ANIMATIONS ) { + ri.Printf( PRINT_WARNING, "WARNING: ignoring excess images for 'animMap' (found %d, max is %d) in shader '%s'\n", + totalImages, MAX_IMAGE_ANIMATIONS, shader.name ); } } else if ( !Q_stricmp( token, "videoMap" ) ) @@ -792,6 +800,8 @@ static qboolean ParseStage( shaderStage_t *stage, char **text ) if (stage->bundle[0].videoMapHandle != -1) { stage->bundle[0].isVideoMap = qtrue; stage->bundle[0].image[0] = tr.scratchImage[stage->bundle[0].videoMapHandle]; + } else { + ri.Printf( PRINT_WARNING, "WARNING: could not load '%s' for 'videoMap' keyword in shader '%s'\n", token, shader.name ); } } // From 904bbc1a8fd83d6798c35975726440e4b8221d31 Mon Sep 17 00:00:00 2001 From: Zack Middleton Date: Tue, 4 Jul 2017 13:48:52 -0500 Subject: [PATCH 116/240] Allow more than 32 surfaces in skin files Models don't have a surface limit; skins shouldn't either. Some player models require more than 32 surfaces since vanilla Quake 3 did not enforce the limit. Skins are now limited to 256 surfaces because having no limit would require parsing the skin file twice. The skin surfaces are dynamically allocated so it doesn't increase memory usage when less surfaces are used. --- code/renderergl1/tr_animation.c | 4 ++-- code/renderergl1/tr_image.c | 23 ++++++++++++++--------- code/renderergl1/tr_local.h | 8 +++++++- code/renderergl1/tr_mesh.c | 4 ++-- code/renderergl1/tr_model_iqm.c | 4 ++-- code/renderergl2/tr_animation.c | 4 ++-- code/renderergl2/tr_image.c | 23 ++++++++++++++--------- code/renderergl2/tr_local.h | 8 +++++++- code/renderergl2/tr_mesh.c | 4 ++-- code/renderergl2/tr_model_iqm.c | 4 ++-- 10 files changed, 54 insertions(+), 32 deletions(-) diff --git a/code/renderergl1/tr_animation.c b/code/renderergl1/tr_animation.c index b01cf81337..2979ab9fd6 100644 --- a/code/renderergl1/tr_animation.c +++ b/code/renderergl1/tr_animation.c @@ -263,9 +263,9 @@ void R_MDRAddAnimSurfaces( trRefEntity_t *ent ) { for(j = 0; j < skin->numSurfaces; j++) { - if (!strcmp(skin->surfaces[j]->name, surface->name)) + if (!strcmp(skin->surfaces[j].name, surface->name)) { - shader = skin->surfaces[j]->shader; + shader = skin->surfaces[j].shader; break; } } diff --git a/code/renderergl1/tr_image.c b/code/renderergl1/tr_image.c index b131f3663f..a926dd8211 100644 --- a/code/renderergl1/tr_image.c +++ b/code/renderergl1/tr_image.c @@ -1508,6 +1508,7 @@ RE_RegisterSkin =============== */ qhandle_t RE_RegisterSkin( const char *name ) { + skinSurface_t parseSurfaces[MAX_SKIN_SURFACES]; qhandle_t hSkin; skin_t *skin; skinSurface_t *surf; @@ -1557,8 +1558,8 @@ qhandle_t RE_RegisterSkin( const char *name ) { // If not a .skin file, load as a single shader if ( strcmp( name + strlen( name ) - 5, ".skin" ) ) { skin->numSurfaces = 1; - skin->surfaces[0] = ri.Hunk_Alloc( sizeof( *skin->surfaces[0] ), h_low ); - skin->surfaces[0]->shader = R_FindShader( name, LIGHTMAP_NONE, qtrue ); + skin->surfaces = ri.Hunk_Alloc( sizeof( skinSurface_t ), h_low ); + skin->surfaces[0].shader = R_FindShader( name, LIGHTMAP_NONE, qtrue ); return hSkin; } @@ -1591,12 +1592,12 @@ qhandle_t RE_RegisterSkin( const char *name ) { // parse the shader name token = CommaParse( &text_p ); - if ( skin->numSurfaces >= MD3_MAX_SURFACES ) { - ri.Printf( PRINT_WARNING, "WARNING: Ignoring surfaces in '%s', the max is %d surfaces!\n", name, MD3_MAX_SURFACES ); + if ( skin->numSurfaces >= MAX_SKIN_SURFACES ) { + ri.Printf( PRINT_WARNING, "WARNING: Ignoring surfaces in '%s', the max is %d surfaces!\n", name, MAX_SKIN_SURFACES ); break; } - surf = skin->surfaces[ skin->numSurfaces ] = ri.Hunk_Alloc( sizeof( *skin->surfaces[0] ), h_low ); + surf = &parseSurfaces[skin->numSurfaces]; Q_strncpyz( surf->name, surfName, sizeof( surf->name ) ); surf->shader = R_FindShader( token, LIGHTMAP_NONE, qtrue ); skin->numSurfaces++; @@ -1610,6 +1611,10 @@ qhandle_t RE_RegisterSkin( const char *name ) { return 0; // use default skin } + // copy surfaces to skin + skin->surfaces = ri.Hunk_Alloc( skin->numSurfaces * sizeof( skinSurface_t ), h_low ); + memcpy( skin->surfaces, parseSurfaces, skin->numSurfaces * sizeof( skinSurface_t ) ); + return hSkin; } @@ -1628,8 +1633,8 @@ void R_InitSkins( void ) { skin = tr.skins[0] = ri.Hunk_Alloc( sizeof( skin_t ), h_low ); Q_strncpyz( skin->name, "", sizeof( skin->name ) ); skin->numSurfaces = 1; - skin->surfaces[0] = ri.Hunk_Alloc( sizeof( *skin->surfaces[0] ), h_low ); - skin->surfaces[0]->shader = tr.defaultShader; + skin->surfaces = ri.Hunk_Alloc( sizeof( skinSurface_t ), h_low ); + skin->surfaces[0].shader = tr.defaultShader; } /* @@ -1658,10 +1663,10 @@ void R_SkinList_f( void ) { for ( i = 0 ; i < tr.numSkins ; i++ ) { skin = tr.skins[i]; - ri.Printf( PRINT_ALL, "%3i:%s\n", i, skin->name ); + ri.Printf( PRINT_ALL, "%3i:%s (%d surfaces)\n", i, skin->name, skin->numSurfaces ); for ( j = 0 ; j < skin->numSurfaces ; j++ ) { ri.Printf( PRINT_ALL, " %s = %s\n", - skin->surfaces[j]->name, skin->surfaces[j]->shader->name ); + skin->surfaces[j].name, skin->surfaces[j].shader->name ); } } ri.Printf (PRINT_ALL, "------------------\n"); diff --git a/code/renderergl1/tr_local.h b/code/renderergl1/tr_local.h index aedf1a4f89..37e0e0701f 100644 --- a/code/renderergl1/tr_local.h +++ b/code/renderergl1/tr_local.h @@ -411,6 +411,12 @@ typedef struct { //================================================================================= +// max surfaces per-skin +// This is an arbitry limit. Vanilla Q3 only supported 32 surfaces in skins but failed to +// enforce the maximum limit when reading skin files. It was possile to use more than 32 +// surfaces which accessed out of bounds memory past end of skin->surfaces hunk block. +#define MAX_SKIN_SURFACES 256 + // skins allow models to be retextured without modifying the model file typedef struct { char name[MAX_QPATH]; @@ -420,7 +426,7 @@ typedef struct { typedef struct skin_s { char name[MAX_QPATH]; // game path, including extension int numSurfaces; - skinSurface_t *surfaces[MD3_MAX_SURFACES]; + skinSurface_t *surfaces; // dynamically allocated array of surfaces } skin_t; diff --git a/code/renderergl1/tr_mesh.c b/code/renderergl1/tr_mesh.c index d0be29bc2e..4cdd9fc279 100644 --- a/code/renderergl1/tr_mesh.c +++ b/code/renderergl1/tr_mesh.c @@ -361,8 +361,8 @@ void R_AddMD3Surfaces( trRefEntity_t *ent ) { shader = tr.defaultShader; for ( j = 0 ; j < skin->numSurfaces ; j++ ) { // the names have both been lowercased - if ( !strcmp( skin->surfaces[j]->name, surface->name ) ) { - shader = skin->surfaces[j]->shader; + if ( !strcmp( skin->surfaces[j].name, surface->name ) ) { + shader = skin->surfaces[j].shader; break; } } diff --git a/code/renderergl1/tr_model_iqm.c b/code/renderergl1/tr_model_iqm.c index 6c47ed0737..2cb5cd4fd1 100644 --- a/code/renderergl1/tr_model_iqm.c +++ b/code/renderergl1/tr_model_iqm.c @@ -903,9 +903,9 @@ void R_AddIQMSurfaces( trRefEntity_t *ent ) { for(j = 0; j < skin->numSurfaces; j++) { - if (!strcmp(skin->surfaces[j]->name, surface->name)) + if (!strcmp(skin->surfaces[j].name, surface->name)) { - shader = skin->surfaces[j]->shader; + shader = skin->surfaces[j].shader; break; } } diff --git a/code/renderergl2/tr_animation.c b/code/renderergl2/tr_animation.c index 9acd6fc790..38fffc6479 100644 --- a/code/renderergl2/tr_animation.c +++ b/code/renderergl2/tr_animation.c @@ -267,9 +267,9 @@ void R_MDRAddAnimSurfaces( trRefEntity_t *ent ) { for(j = 0; j < skin->numSurfaces; j++) { - if (!strcmp(skin->surfaces[j]->name, surface->name)) + if (!strcmp(skin->surfaces[j].name, surface->name)) { - shader = skin->surfaces[j]->shader; + shader = skin->surfaces[j].shader; break; } } diff --git a/code/renderergl2/tr_image.c b/code/renderergl2/tr_image.c index 0fa7961256..7340377d55 100644 --- a/code/renderergl2/tr_image.c +++ b/code/renderergl2/tr_image.c @@ -3060,6 +3060,7 @@ RE_RegisterSkin =============== */ qhandle_t RE_RegisterSkin( const char *name ) { + skinSurface_t parseSurfaces[MAX_SKIN_SURFACES]; qhandle_t hSkin; skin_t *skin; skinSurface_t *surf; @@ -3109,8 +3110,8 @@ qhandle_t RE_RegisterSkin( const char *name ) { // If not a .skin file, load as a single shader if ( strcmp( name + strlen( name ) - 5, ".skin" ) ) { skin->numSurfaces = 1; - skin->surfaces[0] = ri.Hunk_Alloc( sizeof( *skin->surfaces[0] ), h_low ); - skin->surfaces[0]->shader = R_FindShader( name, LIGHTMAP_NONE, qtrue ); + skin->surfaces = ri.Hunk_Alloc( sizeof( skinSurface_t ), h_low ); + skin->surfaces[0].shader = R_FindShader( name, LIGHTMAP_NONE, qtrue ); return hSkin; } @@ -3143,12 +3144,12 @@ qhandle_t RE_RegisterSkin( const char *name ) { // parse the shader name token = CommaParse( &text_p ); - if ( skin->numSurfaces >= MD3_MAX_SURFACES ) { - ri.Printf( PRINT_WARNING, "WARNING: Ignoring surfaces in '%s', the max is %d surfaces!\n", name, MD3_MAX_SURFACES ); + if ( skin->numSurfaces >= MAX_SKIN_SURFACES ) { + ri.Printf( PRINT_WARNING, "WARNING: Ignoring surfaces in '%s', the max is %d surfaces!\n", name, MAX_SKIN_SURFACES ); break; } - surf = skin->surfaces[ skin->numSurfaces ] = ri.Hunk_Alloc( sizeof( *skin->surfaces[0] ), h_low ); + surf = &parseSurfaces[skin->numSurfaces]; Q_strncpyz( surf->name, surfName, sizeof( surf->name ) ); surf->shader = R_FindShader( token, LIGHTMAP_NONE, qtrue ); skin->numSurfaces++; @@ -3162,6 +3163,10 @@ qhandle_t RE_RegisterSkin( const char *name ) { return 0; // use default skin } + // copy surfaces to skin + skin->surfaces = ri.Hunk_Alloc( skin->numSurfaces * sizeof( skinSurface_t ), h_low ); + memcpy( skin->surfaces, parseSurfaces, skin->numSurfaces * sizeof( skinSurface_t ) ); + return hSkin; } @@ -3180,8 +3185,8 @@ void R_InitSkins( void ) { skin = tr.skins[0] = ri.Hunk_Alloc( sizeof( skin_t ), h_low ); Q_strncpyz( skin->name, "", sizeof( skin->name ) ); skin->numSurfaces = 1; - skin->surfaces[0] = ri.Hunk_Alloc( sizeof( *skin->surfaces[0] ), h_low ); - skin->surfaces[0]->shader = tr.defaultShader; + skin->surfaces = ri.Hunk_Alloc( sizeof( skinSurface_t ), h_low ); + skin->surfaces[0].shader = tr.defaultShader; } /* @@ -3210,10 +3215,10 @@ void R_SkinList_f( void ) { for ( i = 0 ; i < tr.numSkins ; i++ ) { skin = tr.skins[i]; - ri.Printf( PRINT_ALL, "%3i:%s\n", i, skin->name ); + ri.Printf( PRINT_ALL, "%3i:%s (%d surfaces)\n", i, skin->name, skin->numSurfaces ); for ( j = 0 ; j < skin->numSurfaces ; j++ ) { ri.Printf( PRINT_ALL, " %s = %s\n", - skin->surfaces[j]->name, skin->surfaces[j]->shader->name ); + skin->surfaces[j].name, skin->surfaces[j].shader->name ); } } ri.Printf (PRINT_ALL, "------------------\n"); diff --git a/code/renderergl2/tr_local.h b/code/renderergl2/tr_local.h index 1fc91f7098..bdd5a85912 100644 --- a/code/renderergl2/tr_local.h +++ b/code/renderergl2/tr_local.h @@ -766,6 +766,12 @@ typedef struct { //================================================================================= +// max surfaces per-skin +// This is an arbitry limit. Vanilla Q3 only supported 32 surfaces in skins but failed to +// enforce the maximum limit when reading skin files. It was possile to use more than 32 +// surfaces which accessed out of bounds memory past end of skin->surfaces hunk block. +#define MAX_SKIN_SURFACES 256 + // skins allow models to be retextured without modifying the model file typedef struct { char name[MAX_QPATH]; @@ -775,7 +781,7 @@ typedef struct { typedef struct skin_s { char name[MAX_QPATH]; // game path, including extension int numSurfaces; - skinSurface_t *surfaces[MD3_MAX_SURFACES]; + skinSurface_t *surfaces; // dynamically allocated array of surfaces } skin_t; diff --git a/code/renderergl2/tr_mesh.c b/code/renderergl2/tr_mesh.c index 5f7c4ee938..53bccb46b2 100644 --- a/code/renderergl2/tr_mesh.c +++ b/code/renderergl2/tr_mesh.c @@ -365,8 +365,8 @@ void R_AddMD3Surfaces( trRefEntity_t *ent ) { shader = tr.defaultShader; for ( j = 0 ; j < skin->numSurfaces ; j++ ) { // the names have both been lowercased - if ( !strcmp( skin->surfaces[j]->name, surface->name ) ) { - shader = skin->surfaces[j]->shader; + if ( !strcmp( skin->surfaces[j].name, surface->name ) ) { + shader = skin->surfaces[j].shader; break; } } diff --git a/code/renderergl2/tr_model_iqm.c b/code/renderergl2/tr_model_iqm.c index 67d3491cd3..caa4308be3 100644 --- a/code/renderergl2/tr_model_iqm.c +++ b/code/renderergl2/tr_model_iqm.c @@ -907,9 +907,9 @@ void R_AddIQMSurfaces( trRefEntity_t *ent ) { for(j = 0; j < skin->numSurfaces; j++) { - if (!strcmp(skin->surfaces[j]->name, surface->name)) + if (!strcmp(skin->surfaces[j].name, surface->name)) { - shader = skin->surfaces[j]->shader; + shader = skin->surfaces[j].shader; break; } } From dd73e1954653b73e05668732843b7e9b97a4ee4e Mon Sep 17 00:00:00 2001 From: Zack Middleton Date: Tue, 4 Jul 2017 15:20:45 -0500 Subject: [PATCH 117/240] Improve warning for too many skin surfaces --- code/renderergl1/tr_image.c | 19 ++++++++++++------- code/renderergl2/tr_image.c | 19 ++++++++++++------- 2 files changed, 24 insertions(+), 14 deletions(-) diff --git a/code/renderergl1/tr_image.c b/code/renderergl1/tr_image.c index a926dd8211..7a4b1baeca 100644 --- a/code/renderergl1/tr_image.c +++ b/code/renderergl1/tr_image.c @@ -1519,6 +1519,7 @@ qhandle_t RE_RegisterSkin( const char *name ) { char *text_p; char *token; char surfName[MAX_QPATH]; + int totalSurfaces; if ( !name || !name[0] ) { ri.Printf( PRINT_DEVELOPER, "Empty name passed to RE_RegisterSkin\n" ); @@ -1569,6 +1570,7 @@ qhandle_t RE_RegisterSkin( const char *name ) { return 0; } + totalSurfaces = 0; text_p = text.c; while ( text_p && *text_p ) { // get surface name @@ -1592,19 +1594,22 @@ qhandle_t RE_RegisterSkin( const char *name ) { // parse the shader name token = CommaParse( &text_p ); - if ( skin->numSurfaces >= MAX_SKIN_SURFACES ) { - ri.Printf( PRINT_WARNING, "WARNING: Ignoring surfaces in '%s', the max is %d surfaces!\n", name, MAX_SKIN_SURFACES ); - break; + if ( skin->numSurfaces < MAX_SKIN_SURFACES ) { + surf = &parseSurfaces[skin->numSurfaces]; + Q_strncpyz( surf->name, surfName, sizeof( surf->name ) ); + surf->shader = R_FindShader( token, LIGHTMAP_NONE, qtrue ); + skin->numSurfaces++; } - surf = &parseSurfaces[skin->numSurfaces]; - Q_strncpyz( surf->name, surfName, sizeof( surf->name ) ); - surf->shader = R_FindShader( token, LIGHTMAP_NONE, qtrue ); - skin->numSurfaces++; + totalSurfaces++; } ri.FS_FreeFile( text.v ); + if ( totalSurfaces > MAX_SKIN_SURFACES ) { + ri.Printf( PRINT_WARNING, "WARNING: Ignoring excess surfaces (found %d, max is %d) in skin '%s'!\n", + totalSurfaces, MAX_SKIN_SURFACES, name ); + } // never let a skin have 0 shaders if ( skin->numSurfaces == 0 ) { diff --git a/code/renderergl2/tr_image.c b/code/renderergl2/tr_image.c index 7340377d55..3672910d4c 100644 --- a/code/renderergl2/tr_image.c +++ b/code/renderergl2/tr_image.c @@ -3071,6 +3071,7 @@ qhandle_t RE_RegisterSkin( const char *name ) { char *text_p; char *token; char surfName[MAX_QPATH]; + int totalSurfaces; if ( !name || !name[0] ) { ri.Printf( PRINT_DEVELOPER, "Empty name passed to RE_RegisterSkin\n" ); @@ -3121,6 +3122,7 @@ qhandle_t RE_RegisterSkin( const char *name ) { return 0; } + totalSurfaces = 0; text_p = text.c; while ( text_p && *text_p ) { // get surface name @@ -3144,19 +3146,22 @@ qhandle_t RE_RegisterSkin( const char *name ) { // parse the shader name token = CommaParse( &text_p ); - if ( skin->numSurfaces >= MAX_SKIN_SURFACES ) { - ri.Printf( PRINT_WARNING, "WARNING: Ignoring surfaces in '%s', the max is %d surfaces!\n", name, MAX_SKIN_SURFACES ); - break; + if ( skin->numSurfaces < MAX_SKIN_SURFACES ) { + surf = &parseSurfaces[skin->numSurfaces]; + Q_strncpyz( surf->name, surfName, sizeof( surf->name ) ); + surf->shader = R_FindShader( token, LIGHTMAP_NONE, qtrue ); + skin->numSurfaces++; } - surf = &parseSurfaces[skin->numSurfaces]; - Q_strncpyz( surf->name, surfName, sizeof( surf->name ) ); - surf->shader = R_FindShader( token, LIGHTMAP_NONE, qtrue ); - skin->numSurfaces++; + totalSurfaces++; } ri.FS_FreeFile( text.v ); + if ( totalSurfaces > MAX_SKIN_SURFACES ) { + ri.Printf( PRINT_WARNING, "WARNING: Ignoring excess surfaces (found %d, max is %d) in skin '%s'!\n", + totalSurfaces, MAX_SKIN_SURFACES, name ); + } // never let a skin have 0 shaders if ( skin->numSurfaces == 0 ) { From 7c252066a3bb3b33e0a9f514a1085333c1fca9f5 Mon Sep 17 00:00:00 2001 From: Zack Middleton Date: Sat, 8 Jul 2017 16:46:41 -0500 Subject: [PATCH 118/240] Fix set-but-not-used variable warnings in vm_armv7l.c offsidx and const_value were set but not used. --- code/qcommon/vm_armv7l.c | 28 ++++++++++++++++++++-------- 1 file changed, 20 insertions(+), 8 deletions(-) diff --git a/code/qcommon/vm_armv7l.c b/code/qcommon/vm_armv7l.c index dd63f5c71d..8f2bd8ce51 100644 --- a/code/qcommon/vm_armv7l.c +++ b/code/qcommon/vm_armv7l.c @@ -599,11 +599,11 @@ void VM_Compile(vm_t *vm, vmHeader_t *header) unsigned char *code; int i_count, pc = 0; int pass; - int codeoffsets[1024]; + int codeoffsets[2]; // was 1024 but it's only used for OFF_CODE and OFF_IMMEDIATES #define j_rel(x) (pass?_j_rel(x, pc):0xBAD) #define OFFSET(i) (pass?(j_rel(codeoffsets[i]-vm->codeLength)):(0xF000000F)) -#define new_offset() (offsidx++) +//#define new_offset() (offsidx++) #define get_offset(i) (codeoffsets[i]) #define save_offset(i) (codeoffsets[i] = vm->codeLength) #define OFF_CODE 0 @@ -616,10 +616,12 @@ void VM_Compile(vm_t *vm, vmHeader_t *header) for (pass = 0; pass < 2; ++pass) { - int offsidx = 0; +// int offsidx = 0; +#ifdef CONST_OPTIMIZE // const optimization unsigned got_const = 0, const_value = 0; +#endif if(pass) { @@ -656,7 +658,7 @@ void VM_Compile(vm_t *vm, vmHeader_t *header) emit(BKPT(0)); save_offset(OFF_CODE); - offsidx = OFF_IMMEDIATES+1; +// offsidx = OFF_IMMEDIATES+1; code = (unsigned char *) header + header->codeOffset; pc = 0; @@ -744,9 +746,14 @@ void VM_Compile(vm_t *vm, vmHeader_t *header) emit_MOVR0i(i_count); emit(STRa(R0, rDATABASE, rPSTACK)); // dataBase[pstack] = r0 #endif - if (got_const) { +#ifdef CONST_OPTIMIZE + if (got_const) + { NOTIMPL(op); - } else { + } + else +#endif + { static int bytes_to_skip = -1; static unsigned start_block = -1; MAYBE_EMIT_CONST(); @@ -803,9 +810,14 @@ void VM_Compile(vm_t *vm, vmHeader_t *header) break; case OP_JUMP: - if(got_const) { +#ifdef CONST_OPTIMIZE + if (got_const) + { NOTIMPL(op); - } else { + } + else +#endif + { emit(LDRTxi(R0, rOPSTACK, 4)); // r0 = *opstack; rOPSTACK -= 4 CHECK_JUMP; emit_MOVRxi(R1, (unsigned)vm->instructionPointers); From a33a904225e92ccacbcd7094732624e3d6a2a96b Mon Sep 17 00:00:00 2001 From: Brenton Bostick Date: Sun, 9 Jul 2017 15:00:56 -0400 Subject: [PATCH 119/240] Fix warning about using abs() with floats (#222) --- code/ui/ui_shared.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/code/ui/ui_shared.c b/code/ui/ui_shared.c index b1718e77cd..f26d11b837 100644 --- a/code/ui/ui_shared.c +++ b/code/ui/ui_shared.c @@ -1121,10 +1121,10 @@ void Menu_TransitionItemByName(menuDef_t *menu, const char *p, rectDef_t rectFro item->window.offsetTime = time; memcpy(&item->window.rectClient, &rectFrom, sizeof(rectDef_t)); memcpy(&item->window.rectEffects, &rectTo, sizeof(rectDef_t)); - item->window.rectEffects2.x = abs(rectTo.x - rectFrom.x) / amt; - item->window.rectEffects2.y = abs(rectTo.y - rectFrom.y) / amt; - item->window.rectEffects2.w = abs(rectTo.w - rectFrom.w) / amt; - item->window.rectEffects2.h = abs(rectTo.h - rectFrom.h) / amt; + item->window.rectEffects2.x = fabs(rectTo.x - rectFrom.x) / amt; + item->window.rectEffects2.y = fabs(rectTo.y - rectFrom.y) / amt; + item->window.rectEffects2.w = fabs(rectTo.w - rectFrom.w) / amt; + item->window.rectEffects2.h = fabs(rectTo.h - rectFrom.h) / amt; Item_UpdatePosition(item); } } From e78da55a753d55b6e0f5e6de88d7f8f055031a8f Mon Sep 17 00:00:00 2001 From: Zack Middleton Date: Sun, 9 Jul 2017 12:58:52 -0500 Subject: [PATCH 120/240] Fix more maybe-uninitialized warnings in opusfile --- code/opusfile-0.8/src/opusfile.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/code/opusfile-0.8/src/opusfile.c b/code/opusfile-0.8/src/opusfile.c index 7c3691b4cc..53bf4dd15e 100644 --- a/code/opusfile-0.8/src/opusfile.c +++ b/code/opusfile-0.8/src/opusfile.c @@ -2193,7 +2193,7 @@ static int op_pcm_seek_page(OggOpusFile *_of, ogg_int64_t pcm_start; ogg_int64_t pcm_end; ogg_int64_t best_gp; - ogg_int64_t diff; + ogg_int64_t diff=0; ogg_uint32_t serialno; opus_int32 pre_skip; opus_int64 begin; @@ -2331,7 +2331,7 @@ static int op_pcm_seek_page(OggOpusFile *_of, d2=end-begin>>1; if(force_bisect)bisect=begin+(end-begin>>1); else{ - ogg_int64_t diff2; + ogg_int64_t diff2=0; OP_ALWAYS_TRUE(!op_granpos_diff(&diff,_target_gp,pcm_start)); OP_ALWAYS_TRUE(!op_granpos_diff(&diff2,pcm_end,pcm_start)); /*Take a (pretty decent) guess.*/ From 51ca4d35ea33b722d0084d76b9d2782f3a5fa37f Mon Sep 17 00:00:00 2001 From: Zack Middleton Date: Sun, 9 Jul 2017 13:09:06 -0500 Subject: [PATCH 121/240] Fix misleading-indentation warnings in cl_cin.c --- code/client/cl_cin.c | 16 ++++++++++++---- 1 file changed, 12 insertions(+), 4 deletions(-) diff --git a/code/client/cl_cin.c b/code/client/cl_cin.c index 327e5c3777..73eecd4677 100644 --- a/code/client/cl_cin.c +++ b/code/client/cl_cin.c @@ -577,8 +577,12 @@ static unsigned short yuv_to_rgb( long y, long u, long v ) g = (YY + ROQ_UG_tab[u] + ROQ_VG_tab[v]) >> 8; b = (YY + ROQ_UB_tab[u]) >> 9; - if (r<0) r = 0; if (g<0) g = 0; if (b<0) b = 0; - if (r > 31) r = 31; if (g > 63) g = 63; if (b > 31) b = 31; + if (r<0) r = 0; + if (g<0) g = 0; + if (b<0) b = 0; + if (r > 31) r = 31; + if (g > 63) g = 63; + if (b > 31) b = 31; return (unsigned short)((r<<11)+(g<<5)+(b)); } @@ -598,8 +602,12 @@ static unsigned int yuv_to_rgb24( long y, long u, long v ) g = (YY + ROQ_UG_tab[u] + ROQ_VG_tab[v]) >> 6; b = (YY + ROQ_UB_tab[u]) >> 6; - if (r<0) r = 0; if (g<0) g = 0; if (b<0) b = 0; - if (r > 255) r = 255; if (g > 255) g = 255; if (b > 255) b = 255; + if (r<0) r = 0; + if (g<0) g = 0; + if (b<0) b = 0; + if (r > 255) r = 255; + if (g > 255) g = 255; + if (b > 255) b = 255; return LittleLong ((r)|(g<<8)|(b<<16)|(255<<24)); } From d549b642bcdc32b6f2c0b2207cb80060993916f7 Mon Sep 17 00:00:00 2001 From: SmileTheory Date: Thu, 13 Jul 2017 12:03:10 -0700 Subject: [PATCH 122/240] OpenGL2: Use an OpenGL 3.2 core context if available. --- code/renderercommon/qgl.h | 127 ++++------------------- code/renderercommon/tr_common.h | 2 +- code/renderergl1/tr_init.c | 2 +- code/renderergl2/glsl/dlight_fp.glsl | 22 +++- code/renderergl2/glsl/generic_fp.glsl | 23 +++- code/renderergl2/glsl/lightall_fp.glsl | 21 +++- code/renderergl2/glsl/shadowmask_fp.glsl | 28 ++--- code/renderergl2/tr_backend.c | 38 ------- code/renderergl2/tr_dsa.c | 46 ++++---- code/renderergl2/tr_extensions.c | 99 +++++------------- code/renderergl2/tr_fbo.c | 124 ++++++++++------------ code/renderergl2/tr_glsl.c | 13 ++- code/renderergl2/tr_image.c | 10 +- code/renderergl2/tr_init.c | 22 ++-- code/renderergl2/tr_local.h | 2 + code/renderergl2/tr_shade.c | 24 +++++ code/renderergl2/tr_shader.c | 4 +- code/renderergl2/tr_sky.c | 2 + code/renderergl2/tr_surface.c | 2 + code/sdl/sdl_glimp.c | 51 +++++++-- 20 files changed, 313 insertions(+), 349 deletions(-) diff --git a/code/renderercommon/qgl.h b/code/renderercommon/qgl.h index 3aaa0e8bc4..80fa0a3aeb 100644 --- a/code/renderercommon/qgl.h +++ b/code/renderercommon/qgl.h @@ -381,6 +381,7 @@ extern void (APIENTRYP qglUnlockArraysEXT) (void); // OpenGL 1.3, was GL_ARB_texture_compression #define QGL_1_3_PROCS \ + GLE(void, ActiveTexture, GLenum texture) \ GLE(void, CompressedTexImage2D, GLenum target, GLint level, GLenum internalformat, GLsizei width, GLsizei height, GLint border, GLsizei imageSize, const void *data) \ GLE(void, CompressedTexSubImage2D, GLenum target, GLint level, GLint xoffset, GLint yoffset, GLsizei width, GLsizei height, GLenum format, GLsizei imageSize, const void *data) \ @@ -478,97 +479,25 @@ extern void (APIENTRYP qglUnlockArraysEXT) (void); #define GL_HALF_FLOAT_ARB 0x140B #endif -// GL_EXT_framebuffer_object -#define QGL_EXT_framebuffer_object_PROCS \ - GLE(void, BindRenderbufferEXT, GLenum target, GLuint renderbuffer) \ - GLE(void, DeleteRenderbuffersEXT, GLsizei n, const GLuint *renderbuffers) \ - GLE(void, GenRenderbuffersEXT, GLsizei n, GLuint *renderbuffers) \ - GLE(void, RenderbufferStorageEXT, GLenum target, GLenum internalformat, GLsizei width, GLsizei height) \ - GLE(void, BindFramebufferEXT, GLenum target, GLuint framebuffer) \ - GLE(void, DeleteFramebuffersEXT, GLsizei n, const GLuint *framebuffers) \ - GLE(void, GenFramebuffersEXT, GLsizei n, GLuint *framebuffers) \ - GLE(GLenum, CheckFramebufferStatusEXT, GLenum target) \ - GLE(void, FramebufferTexture2DEXT, GLenum target, GLenum attachment, GLenum textarget, GLuint texture, GLint level) \ - GLE(void, FramebufferRenderbufferEXT, GLenum target, GLenum attachment, GLenum renderbuffertarget, GLuint renderbuffer) \ - GLE(void, GenerateMipmapEXT, GLenum target) \ - -#ifndef GL_EXT_framebuffer_object -#define GL_EXT_framebuffer_object -#define GL_FRAMEBUFFER_EXT 0x8D40 -#define GL_RENDERBUFFER_EXT 0x8D41 -#define GL_STENCIL_INDEX1_EXT 0x8D46 -#define GL_STENCIL_INDEX4_EXT 0x8D47 -#define GL_STENCIL_INDEX8_EXT 0x8D48 -#define GL_STENCIL_INDEX16_EXT 0x8D49 -#define GL_RENDERBUFFER_WIDTH_EXT 0x8D42 -#define GL_RENDERBUFFER_HEIGHT_EXT 0x8D43 -#define GL_RENDERBUFFER_INTERNAL_FORMAT_EXT 0x8D44 -#define GL_RENDERBUFFER_RED_SIZE_EXT 0x8D50 -#define GL_RENDERBUFFER_GREEN_SIZE_EXT 0x8D51 -#define GL_RENDERBUFFER_BLUE_SIZE_EXT 0x8D52 -#define GL_RENDERBUFFER_ALPHA_SIZE_EXT 0x8D53 -#define GL_RENDERBUFFER_DEPTH_SIZE_EXT 0x8D54 -#define GL_RENDERBUFFER_STENCIL_SIZE_EXT 0x8D55 -#define GL_FRAMEBUFFER_ATTACHMENT_OBJECT_TYPE_EXT 0x8CD0 -#define GL_FRAMEBUFFER_ATTACHMENT_OBJECT_NAME_EXT 0x8CD1 -#define GL_FRAMEBUFFER_ATTACHMENT_TEXTURE_LEVEL_EXT 0x8CD2 -#define GL_FRAMEBUFFER_ATTACHMENT_TEXTURE_CUBE_MAP_FACE_EXT 0x8CD3 -#define GL_FRAMEBUFFER_ATTACHMENT_TEXTURE_3D_ZOFFSET_EXT 0x8CD4 -#define GL_COLOR_ATTACHMENT0_EXT 0x8CE0 -#define GL_COLOR_ATTACHMENT1_EXT 0x8CE1 -#define GL_COLOR_ATTACHMENT2_EXT 0x8CE2 -#define GL_COLOR_ATTACHMENT3_EXT 0x8CE3 -#define GL_COLOR_ATTACHMENT4_EXT 0x8CE4 -#define GL_COLOR_ATTACHMENT5_EXT 0x8CE5 -#define GL_COLOR_ATTACHMENT6_EXT 0x8CE6 -#define GL_COLOR_ATTACHMENT7_EXT 0x8CE7 -#define GL_COLOR_ATTACHMENT8_EXT 0x8CE8 -#define GL_COLOR_ATTACHMENT9_EXT 0x8CE9 -#define GL_COLOR_ATTACHMENT10_EXT 0x8CEA -#define GL_COLOR_ATTACHMENT11_EXT 0x8CEB -#define GL_COLOR_ATTACHMENT12_EXT 0x8CEC -#define GL_COLOR_ATTACHMENT13_EXT 0x8CED -#define GL_COLOR_ATTACHMENT14_EXT 0x8CEE -#define GL_COLOR_ATTACHMENT15_EXT 0x8CEF -#define GL_DEPTH_ATTACHMENT_EXT 0x8D00 -#define GL_STENCIL_ATTACHMENT_EXT 0x8D20 -#define GL_FRAMEBUFFER_COMPLETE_EXT 0x8CD5 -#define GL_FRAMEBUFFER_INCOMPLETE_ATTACHMENT_EXT 0x8CD6 -#define GL_FRAMEBUFFER_INCOMPLETE_MISSING_ATTACHMENT_EXT 0x8CD7 -#define GL_FRAMEBUFFER_INCOMPLETE_DIMENSIONS_EXT 0x8CD9 -#define GL_FRAMEBUFFER_INCOMPLETE_FORMATS_EXT 0x8CDA -#define GL_FRAMEBUFFER_INCOMPLETE_DRAW_BUFFER_EXT 0x8CDB -#define GL_FRAMEBUFFER_INCOMPLETE_READ_BUFFER_EXT 0x8CDC -#define GL_FRAMEBUFFER_UNSUPPORTED_EXT 0x8CDD -#define GL_FRAMEBUFFER_BINDING_EXT 0x8CA6 -#define GL_RENDERBUFFER_BINDING_EXT 0x8CA7 -#define GL_MAX_COLOR_ATTACHMENTS_EXT 0x8CDF -#define GL_MAX_RENDERBUFFER_SIZE_EXT 0x84E8 -#define GL_INVALID_FRAMEBUFFER_OPERATION_EXT 0x0506 -#endif - -// GL_EXT_framebuffer_blit -#define QGL_EXT_framebuffer_blit_PROCS \ - GLE(void, BlitFramebufferEXT, GLint srcX0, GLint srcY0, GLint srcX1, GLint srcY1, GLint dstX0, GLint dstY0, GLint dstX1, GLint dstY1, GLbitfield mask, GLenum filter) \ - -#ifndef GL_EXT_framebuffer_blit -#define GL_EXT_framebuffer_blit -#define GL_READ_FRAMEBUFFER_EXT 0x8CA8 -#define GL_DRAW_FRAMEBUFFER_EXT 0x8CA9 -#define GL_DRAW_FRAMEBUFFER_BINDING_EXT 0x8CA6 -#define GL_READ_FRAMEBUFFER_BINDING_EXT 0x8CAA -#endif - -// GL_EXT_framebuffer_multisample -#define QGL_EXT_framebuffer_multisample_PROCS \ - GLE(void, RenderbufferStorageMultisampleEXT, GLenum target, GLsizei samples, GLenum internalformat, GLsizei width, GLsizei height) \ - -#ifndef GL_EXT_framebuffer_multisample -#define GL_EXT_framebuffer_multisample -#define GL_RENDERBUFFER_SAMPLES_EXT 0x8CAB -#define GL_FRAMEBUFFER_INCOMPLETE_MULTISAMPLE_EXT 0x8D56 -#define GL_MAX_SAMPLES_EXT 0x8D57 -#endif +// OpenGL 3.0, was GL_EXT_framebuffer_object, GL_EXT_framebuffer_blit, GL_EXT_framebuffer_multisample, and GL_ARB_vertex_array_object +#define QGL_3_0_PROCS \ + GLE(const GLubyte *, GetStringi, GLenum name, GLuint index) \ + GLE(void, BindRenderbuffer, GLenum target, GLuint renderbuffer) \ + GLE(void, DeleteRenderbuffers, GLsizei n, const GLuint *renderbuffers) \ + GLE(void, GenRenderbuffers, GLsizei n, GLuint *renderbuffers) \ + GLE(void, RenderbufferStorage, GLenum target, GLenum internalformat, GLsizei width, GLsizei height) \ + GLE(void, BindFramebuffer, GLenum target, GLuint framebuffer) \ + GLE(void, DeleteFramebuffers, GLsizei n, const GLuint *framebuffers) \ + GLE(void, GenFramebuffers, GLsizei n, GLuint *framebuffers) \ + GLE(GLenum, CheckFramebufferStatus, GLenum target) \ + GLE(void, FramebufferTexture2D, GLenum target, GLenum attachment, GLenum textarget, GLuint texture, GLint level) \ + GLE(void, FramebufferRenderbuffer, GLenum target, GLenum attachment, GLenum renderbuffertarget, GLuint renderbuffer) \ + GLE(void, GenerateMipmap, GLenum target) \ + GLE(void, BlitFramebuffer, GLint srcX0, GLint srcY0, GLint srcX1, GLint srcY1, GLint dstX0, GLint dstY0, GLint dstX1, GLint dstY1, GLbitfield mask, GLenum filter) \ + GLE(void, RenderbufferStorageMultisample, GLenum target, GLsizei samples, GLenum internalformat, GLsizei width, GLsizei height) \ + GLE(void, BindVertexArray, GLuint array) \ + GLE(void, DeleteVertexArrays, GLsizei n, const GLuint *arrays) \ + GLE(void, GenVertexArrays, GLsizei n, GLuint *arrays) \ #ifndef GL_ARB_texture_compression_rgtc #define GL_ARB_texture_compression_rgtc @@ -596,17 +525,6 @@ extern void (APIENTRYP qglUnlockArraysEXT) (void); #define GL_TEXTURE_CUBE_MAP_SEAMLESS 0x884F #endif -// GL_ARB_vertex_array_object -#define QGL_ARB_vertex_array_object_PROCS \ - GLE(void, BindVertexArray, GLuint array) \ - GLE(void, DeleteVertexArrays, GLsizei n, const GLuint *arrays) \ - GLE(void, GenVertexArrays, GLsizei n, GLuint *arrays) \ - -#ifndef GL_ARB_vertex_array_object -#define GL_ARB_vertex_array_object -#define GL_VERTEX_ARRAY_BINDING_ARB 0x85B5 -#endif - // GL_EXT_direct_state_access #define QGL_EXT_direct_state_access_PROCS \ GLE(GLvoid, BindMultiTextureEXT, GLenum texunit, GLenum target, GLuint texture) \ @@ -635,10 +553,7 @@ extern void (APIENTRYP qglUnlockArraysEXT) (void); QGL_1_3_PROCS; QGL_1_5_PROCS; QGL_2_0_PROCS; -QGL_EXT_framebuffer_object_PROCS; -QGL_EXT_framebuffer_blit_PROCS; -QGL_EXT_framebuffer_multisample_PROCS; -QGL_ARB_vertex_array_object_PROCS; +QGL_3_0_PROCS; QGL_EXT_direct_state_access_PROCS; #undef GLE diff --git a/code/renderercommon/tr_common.h b/code/renderercommon/tr_common.h index 591849789a..3d463aacad 100644 --- a/code/renderercommon/tr_common.h +++ b/code/renderercommon/tr_common.h @@ -156,7 +156,7 @@ IMPLEMENTATION SPECIFIC FUNCTIONS ==================================================================== */ -void GLimp_Init( void ); +void GLimp_Init( qboolean ); void GLimp_Shutdown( void ); void GLimp_EndFrame( void ); diff --git a/code/renderergl1/tr_init.c b/code/renderergl1/tr_init.c index 15e7972907..266a2f249a 100644 --- a/code/renderergl1/tr_init.c +++ b/code/renderergl1/tr_init.c @@ -195,7 +195,7 @@ static void InitOpenGL( void ) { GLint temp; - GLimp_Init(); + GLimp_Init( qfalse ); strcpy( renderer_buffer, glConfig.renderer_string ); Q_strlwr( renderer_buffer ); diff --git a/code/renderergl2/glsl/dlight_fp.glsl b/code/renderergl2/glsl/dlight_fp.glsl index 8ffca5b98d..41be0494fc 100644 --- a/code/renderergl2/glsl/dlight_fp.glsl +++ b/code/renderergl2/glsl/dlight_fp.glsl @@ -1,5 +1,7 @@ uniform sampler2D u_DiffuseMap; +uniform int u_AlphaTest; + varying vec2 var_Tex1; varying vec4 var_Color; @@ -8,5 +10,23 @@ void main() { vec4 color = texture2D(u_DiffuseMap, var_Tex1); - gl_FragColor = color * var_Color; + float alpha = color.a * var_Color.a; + if (u_AlphaTest == 1) + { + if (alpha == 0.0) + discard; + } + else if (u_AlphaTest == 2) + { + if (alpha >= 0.5) + discard; + } + else if (u_AlphaTest == 3) + { + if (alpha < 0.5) + discard; + } + + gl_FragColor.rgb = color.rgb * var_Color.rgb; + gl_FragColor.a = alpha; } diff --git a/code/renderergl2/glsl/generic_fp.glsl b/code/renderergl2/glsl/generic_fp.glsl index 50db078547..c0a49407c7 100644 --- a/code/renderergl2/glsl/generic_fp.glsl +++ b/code/renderergl2/glsl/generic_fp.glsl @@ -1,5 +1,7 @@ uniform sampler2D u_DiffuseMap; +uniform int u_AlphaTest; + varying vec2 var_DiffuseTex; varying vec4 var_Color; @@ -8,5 +10,24 @@ varying vec4 var_Color; void main() { vec4 color = texture2D(u_DiffuseMap, var_DiffuseTex); - gl_FragColor = color * var_Color; + + float alpha = color.a * var_Color.a; + if (u_AlphaTest == 1) + { + if (alpha == 0.0) + discard; + } + else if (u_AlphaTest == 2) + { + if (alpha >= 0.5) + discard; + } + else if (u_AlphaTest == 3) + { + if (alpha < 0.5) + discard; + } + + gl_FragColor.rgb = color.rgb * var_Color.rgb; + gl_FragColor.a = alpha; } diff --git a/code/renderergl2/glsl/lightall_fp.glsl b/code/renderergl2/glsl/lightall_fp.glsl index 5cb8233df9..8e7c9b4acc 100644 --- a/code/renderergl2/glsl/lightall_fp.glsl +++ b/code/renderergl2/glsl/lightall_fp.glsl @@ -45,6 +45,8 @@ uniform vec4 u_CubeMapInfo; #endif #endif +uniform int u_AlphaTest; + varying vec4 var_TexCoords; varying vec4 var_Color; @@ -228,6 +230,23 @@ void main() #endif vec4 diffuse = texture2D(u_DiffuseMap, texCoords); + + float alpha = diffuse.a * var_Color.a; + if (u_AlphaTest == 1) + { + if (alpha == 0.0) + discard; + } + else if (u_AlphaTest == 2) + { + if (alpha >= 0.5) + discard; + } + else if (u_AlphaTest == 3) + { + if (alpha < 0.5) + discard; + } #if defined(USE_LIGHT) && !defined(USE_FAST_LIGHT) L = var_LightDir.xyz; @@ -406,5 +425,5 @@ void main() #endif - gl_FragColor.a = diffuse.a * var_Color.a; + gl_FragColor.a = alpha; } diff --git a/code/renderergl2/glsl/shadowmask_fp.glsl b/code/renderergl2/glsl/shadowmask_fp.glsl index 053907cf22..2b57e3ba4b 100644 --- a/code/renderergl2/glsl/shadowmask_fp.glsl +++ b/code/renderergl2/glsl/shadowmask_fp.glsl @@ -52,10 +52,10 @@ float PCF(const sampler2DShadow shadowmap, const vec2 st, const float dist) offset.y += offset.x; if (offset.y > 1.1) offset.y = 0.0; - mult = shadow2D(shadowmap, vec3(st + (offset + vec2(-1.5, 0.5)) * scale, dist)).r - + shadow2D(shadowmap, vec3(st + (offset + vec2( 0.5, 0.5)) * scale, dist)).r - + shadow2D(shadowmap, vec3(st + (offset + vec2(-1.5, -1.5)) * scale, dist)).r - + shadow2D(shadowmap, vec3(st + (offset + vec2( 0.5, -1.5)) * scale, dist)).r; + mult = shadow2D(shadowmap, vec3(st + (offset + vec2(-1.5, 0.5)) * scale, dist)) + + shadow2D(shadowmap, vec3(st + (offset + vec2( 0.5, 0.5)) * scale, dist)) + + shadow2D(shadowmap, vec3(st + (offset + vec2(-1.5, -1.5)) * scale, dist)) + + shadow2D(shadowmap, vec3(st + (offset + vec2( 0.5, -1.5)) * scale, dist)); mult *= 0.25; #endif @@ -66,23 +66,23 @@ float PCF(const sampler2DShadow shadowmap, const vec2 st, const float dist) float cosr = cos(r) * scale; mat2 rmat = mat2(cosr, sinr, -sinr, cosr); - mult = shadow2D(shadowmap, vec3(st + rmat * vec2(-0.7055767, 0.196515), dist)).r; - mult += shadow2D(shadowmap, vec3(st + rmat * vec2(0.3524343, -0.7791386), dist)).r; - mult += shadow2D(shadowmap, vec3(st + rmat * vec2(0.2391056, 0.9189604), dist)).r; + mult = shadow2D(shadowmap, vec3(st + rmat * vec2(-0.7055767, 0.196515), dist)); + mult += shadow2D(shadowmap, vec3(st + rmat * vec2(0.3524343, -0.7791386), dist)); + mult += shadow2D(shadowmap, vec3(st + rmat * vec2(0.2391056, 0.9189604), dist)); #if defined(USE_SHADOW_FILTER2) - mult += shadow2D(shadowmap, vec3(st + rmat * vec2(-0.07580382, -0.09224417), dist)).r; - mult += shadow2D(shadowmap, vec3(st + rmat * vec2(0.5784913, -0.002528916), dist)).r; - mult += shadow2D(shadowmap, vec3(st + rmat * vec2(0.192888, 0.4064181), dist)).r; - mult += shadow2D(shadowmap, vec3(st + rmat * vec2(-0.6335801, -0.5247476), dist)).r; - mult += shadow2D(shadowmap, vec3(st + rmat * vec2(-0.5579782, 0.7491854), dist)).r; - mult += shadow2D(shadowmap, vec3(st + rmat * vec2(0.7320465, 0.6317794), dist)).r; + mult += shadow2D(shadowmap, vec3(st + rmat * vec2(-0.07580382, -0.09224417), dist)); + mult += shadow2D(shadowmap, vec3(st + rmat * vec2(0.5784913, -0.002528916), dist)); + mult += shadow2D(shadowmap, vec3(st + rmat * vec2(0.192888, 0.4064181), dist)); + mult += shadow2D(shadowmap, vec3(st + rmat * vec2(-0.6335801, -0.5247476), dist)); + mult += shadow2D(shadowmap, vec3(st + rmat * vec2(-0.5579782, 0.7491854), dist)); + mult += shadow2D(shadowmap, vec3(st + rmat * vec2(0.7320465, 0.6317794), dist)); mult *= 0.11111; #else mult *= 0.33333; #endif #else - mult = shadow2D(shadowmap, vec3(st, dist)).r; + mult = shadow2D(shadowmap, vec3(st, dist)); #endif return mult; diff --git a/code/renderergl2/tr_backend.c b/code/renderergl2/tr_backend.c index a5a7fe27e5..061d9d5599 100644 --- a/code/renderergl2/tr_backend.c +++ b/code/renderergl2/tr_backend.c @@ -263,44 +263,6 @@ void GL_State( unsigned long stateBits ) } } - // - // alpha test - // - if ( diff & GLS_ATEST_BITS ) - { - uint32_t oldState = glState.glStateBits & GLS_ATEST_BITS; - uint32_t newState = stateBits & GLS_ATEST_BITS; - uint32_t storedState = glState.storedGlState & GLS_ATEST_BITS; - - if (oldState == 0) - { - qglEnable(GL_ALPHA_TEST); - } - else if (newState == 0) - { - qglDisable(GL_ALPHA_TEST); - } - - if (newState != 0 && storedState != newState) - { - glState.storedGlState &= ~GLS_ATEST_BITS; - glState.storedGlState |= newState; - - switch ( newState ) - { - case GLS_ATEST_GT_0: - qglAlphaFunc( GL_GREATER, 0.0f ); - break; - case GLS_ATEST_LT_80: - qglAlphaFunc( GL_LESS, 0.5f ); - break; - case GLS_ATEST_GE_80: - qglAlphaFunc( GL_GEQUAL, 0.5f ); - break; - } - } - } - glState.glStateBits = stateBits; } diff --git a/code/renderergl2/tr_dsa.c b/code/renderergl2/tr_dsa.c index 8fde8419e5..a9d07568e2 100644 --- a/code/renderergl2/tr_dsa.c +++ b/code/renderergl2/tr_dsa.c @@ -43,7 +43,7 @@ void GL_BindNullTextures() { for (i = 0; i < NUM_TEXTURE_BUNDLES; i++) { - qglBindMultiTextureEXT(GL_TEXTURE0_ARB + i, GL_TEXTURE_2D, 0); + qglBindMultiTextureEXT(GL_TEXTURE0 + i, GL_TEXTURE_2D, 0); glDsaState.textures[i] = 0; } } @@ -51,19 +51,19 @@ void GL_BindNullTextures() { for (i = 0; i < NUM_TEXTURE_BUNDLES; i++) { - qglActiveTextureARB(GL_TEXTURE0_ARB + i); + qglActiveTexture(GL_TEXTURE0 + i); qglBindTexture(GL_TEXTURE_2D, 0); glDsaState.textures[i] = 0; } - qglActiveTextureARB(GL_TEXTURE0_ARB); - glDsaState.texunit = GL_TEXTURE0_ARB; + qglActiveTexture(GL_TEXTURE0); + glDsaState.texunit = GL_TEXTURE0; } } int GL_BindMultiTexture(GLenum texunit, GLenum target, GLuint texture) { - GLuint tmu = texunit - GL_TEXTURE0_ARB; + GLuint tmu = texunit - GL_TEXTURE0; if (glDsaState.textures[tmu] == texture) return 0; @@ -80,7 +80,7 @@ GLvoid APIENTRY GLDSA_BindMultiTextureEXT(GLenum texunit, GLenum target, GLuint { if (glDsaState.texunit != texunit) { - qglActiveTextureARB(texunit); + qglActiveTexture(texunit); glDsaState.texunit = texunit; } @@ -138,7 +138,7 @@ GLvoid APIENTRY GLDSA_CompressedTextureSubImage2DEXT(GLuint texture, GLenum targ GLvoid APIENTRY GLDSA_GenerateTextureMipmapEXT(GLuint texture, GLenum target) { GL_BindMultiTexture(glDsaState.texunit, target, texture); - qglGenerateMipmapEXT(target); + qglGenerateMipmap(target); } void GL_BindNullProgram() @@ -207,9 +207,9 @@ GLvoid APIENTRY GLDSA_ProgramUniformMatrix4fvEXT(GLuint program, GLint location, void GL_BindNullFramebuffers() { - qglBindFramebufferEXT(GL_FRAMEBUFFER_EXT, 0); + qglBindFramebuffer(GL_FRAMEBUFFER, 0); glDsaState.drawFramebuffer = glDsaState.readFramebuffer = 0; - qglBindRenderbufferEXT(GL_RENDERBUFFER_EXT, 0); + qglBindRenderbuffer(GL_RENDERBUFFER, 0); glDsaState.renderbuffer = 0; } @@ -217,26 +217,26 @@ void GL_BindFramebuffer(GLenum target, GLuint framebuffer) { switch (target) { - case GL_FRAMEBUFFER_EXT: + case GL_FRAMEBUFFER: if (framebuffer != glDsaState.drawFramebuffer || framebuffer != glDsaState.readFramebuffer) { - qglBindFramebufferEXT(target, framebuffer); + qglBindFramebuffer(target, framebuffer); glDsaState.drawFramebuffer = glDsaState.readFramebuffer = framebuffer; } break; - case GL_DRAW_FRAMEBUFFER_EXT: + case GL_DRAW_FRAMEBUFFER: if (framebuffer != glDsaState.drawFramebuffer) { - qglBindFramebufferEXT(target, framebuffer); + qglBindFramebuffer(target, framebuffer); glDsaState.drawFramebuffer = framebuffer; } break; - case GL_READ_FRAMEBUFFER_EXT: + case GL_READ_FRAMEBUFFER: if (framebuffer != glDsaState.readFramebuffer) { - qglBindFramebufferEXT(target, framebuffer); + qglBindFramebuffer(target, framebuffer); glDsaState.readFramebuffer = framebuffer; } break; @@ -247,7 +247,7 @@ void GL_BindRenderbuffer(GLuint renderbuffer) { if (renderbuffer != glDsaState.renderbuffer) { - qglBindRenderbufferEXT(GL_RENDERBUFFER_EXT, renderbuffer); + qglBindRenderbuffer(GL_RENDERBUFFER, renderbuffer); glDsaState.renderbuffer = renderbuffer; } } @@ -256,32 +256,32 @@ GLvoid APIENTRY GLDSA_NamedRenderbufferStorageEXT(GLuint renderbuffer, GLenum internalformat, GLsizei width, GLsizei height) { GL_BindRenderbuffer(renderbuffer); - qglRenderbufferStorageEXT(GL_RENDERBUFFER_EXT, internalformat, width, height); + qglRenderbufferStorage(GL_RENDERBUFFER, internalformat, width, height); } GLvoid APIENTRY GLDSA_NamedRenderbufferStorageMultisampleEXT(GLuint renderbuffer, GLsizei samples, GLenum internalformat, GLsizei width, GLsizei height) { GL_BindRenderbuffer(renderbuffer); - qglRenderbufferStorageMultisampleEXT(GL_RENDERBUFFER_EXT, samples, internalformat, width, height); + qglRenderbufferStorageMultisample(GL_RENDERBUFFER, samples, internalformat, width, height); } GLenum APIENTRY GLDSA_CheckNamedFramebufferStatusEXT(GLuint framebuffer, GLenum target) { GL_BindFramebuffer(target, framebuffer); - return qglCheckFramebufferStatusEXT(target); + return qglCheckFramebufferStatus(target); } GLvoid APIENTRY GLDSA_NamedFramebufferTexture2DEXT(GLuint framebuffer, GLenum attachment, GLenum textarget, GLuint texture, GLint level) { - GL_BindFramebuffer(GL_FRAMEBUFFER_EXT, framebuffer); - qglFramebufferTexture2DEXT(GL_FRAMEBUFFER_EXT, attachment, textarget, texture, level); + GL_BindFramebuffer(GL_FRAMEBUFFER, framebuffer); + qglFramebufferTexture2D(GL_FRAMEBUFFER, attachment, textarget, texture, level); } GLvoid APIENTRY GLDSA_NamedFramebufferRenderbufferEXT(GLuint framebuffer, GLenum attachment, GLenum renderbuffertarget, GLuint renderbuffer) { - GL_BindFramebuffer(GL_FRAMEBUFFER_EXT, framebuffer); - qglFramebufferRenderbufferEXT(GL_FRAMEBUFFER_EXT, attachment, renderbuffertarget, renderbuffer); + GL_BindFramebuffer(GL_FRAMEBUFFER, framebuffer); + qglFramebufferRenderbuffer(GL_FRAMEBUFFER, attachment, renderbuffertarget, renderbuffer); } diff --git a/code/renderergl2/tr_extensions.c b/code/renderergl2/tr_extensions.c index 37bbe849e4..7df52956b3 100644 --- a/code/renderergl2/tr_extensions.c +++ b/code/renderergl2/tr_extensions.c @@ -34,10 +34,7 @@ Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA QGL_1_3_PROCS; QGL_1_5_PROCS; QGL_2_0_PROCS; -QGL_EXT_framebuffer_object_PROCS; -QGL_EXT_framebuffer_blit_PROCS; -QGL_EXT_framebuffer_multisample_PROCS; -QGL_ARB_vertex_array_object_PROCS; +QGL_3_0_PROCS; QGL_EXT_direct_state_access_PROCS; #undef GLE @@ -75,6 +72,33 @@ void GLimp_InitExtraExtensions() // OpenGL 2.0, was GL_ARB_shading_language_100, GL_ARB_vertex_program, GL_ARB_shader_objects, and GL_ARB_vertex_shader QGL_2_0_PROCS; + // OpenGL 3.0, was GL_EXT_framebuffer_object, GL_EXT_framebuffer_blit, GL_EXT_framebuffer_multisample, and GL_ARB_vertex_array_object + // QGL_*_PROCS becomes several functions, do not remove {} + if (glRefConfig.openglMajorVersion >= 3) + { + QGL_3_0_PROCS; + + glRefConfig.framebufferObject = !!r_ext_framebuffer_object->integer; + glRefConfig.framebufferBlit = qtrue; + glRefConfig.framebufferMultisample = qtrue; + + qglGetIntegerv(GL_MAX_RENDERBUFFER_SIZE_EXT, &glRefConfig.maxRenderbufferSize); + qglGetIntegerv(GL_MAX_COLOR_ATTACHMENTS_EXT, &glRefConfig.maxColorAttachments); + + ri.Printf(PRINT_ALL, result[glRefConfig.framebufferObject], "OpenGL 3.0+ framebuffer procs"); + + // Don't let this be disabled, core context requires it + glRefConfig.vertexArrayObject = qtrue; + + ri.Printf(PRINT_ALL, result[glRefConfig.vertexArrayObject], "OpenGL 3.0+ vertex array object procs"); + } + else + { + ri.Printf(PRINT_ALL, result[2], "OpenGL 3.0+ framebuffer procs"); + ri.Printf(PRINT_ALL, result[2], "OpenGL 3.0+ vertex array object procs"); + + } + // Determine GLSL version if (1) { @@ -136,57 +160,6 @@ void GLimp_InitExtraExtensions() ri.Printf(PRINT_ALL, result[2], extension); } - // GL_EXT_framebuffer_object - extension = "GL_EXT_framebuffer_object"; - glRefConfig.framebufferObject = qfalse; - if( SDL_GL_ExtensionSupported( extension ) ) - { - glRefConfig.framebufferObject = !!r_ext_framebuffer_object->integer; - - qglGetIntegerv(GL_MAX_RENDERBUFFER_SIZE_EXT, &glRefConfig.maxRenderbufferSize); - qglGetIntegerv(GL_MAX_COLOR_ATTACHMENTS_EXT, &glRefConfig.maxColorAttachments); - - QGL_EXT_framebuffer_object_PROCS; - - ri.Printf(PRINT_ALL, result[glRefConfig.framebufferObject], extension); - } - else - { - ri.Printf(PRINT_ALL, result[2], extension); - } - - // GL_EXT_framebuffer_blit - extension = "GL_EXT_framebuffer_blit"; - glRefConfig.framebufferBlit = qfalse; - if (SDL_GL_ExtensionSupported(extension)) - { - glRefConfig.framebufferBlit = qtrue; - - QGL_EXT_framebuffer_blit_PROCS; - - ri.Printf(PRINT_ALL, result[glRefConfig.framebufferBlit], extension); - } - else - { - ri.Printf(PRINT_ALL, result[2], extension); - } - - // GL_EXT_framebuffer_multisample - extension = "GL_EXT_framebuffer_multisample"; - glRefConfig.framebufferMultisample = qfalse; - if (SDL_GL_ExtensionSupported(extension)) - { - glRefConfig.framebufferMultisample = qtrue; - - QGL_EXT_framebuffer_multisample_PROCS; - - ri.Printf(PRINT_ALL, result[glRefConfig.framebufferMultisample], extension); - } - else - { - ri.Printf(PRINT_ALL, result[2], extension); - } - glRefConfig.textureCompression = TCR_NONE; // GL_ARB_texture_compression_rgtc @@ -251,22 +224,6 @@ void GLimp_InitExtraExtensions() ri.Printf(PRINT_ALL, result[2], extension); } - // GL_ARB_vertex_array_object - extension = "GL_ARB_vertex_array_object"; - glRefConfig.vertexArrayObject = qfalse; - if( SDL_GL_ExtensionSupported( extension ) ) - { - glRefConfig.vertexArrayObject = !!r_arb_vertex_array_object->integer; - - QGL_ARB_vertex_array_object_PROCS; - - ri.Printf(PRINT_ALL, result[glRefConfig.vertexArrayObject], extension); - } - else - { - ri.Printf(PRINT_ALL, result[2], extension); - } - // GL_EXT_direct_state_access extension = "GL_EXT_direct_state_access"; glRefConfig.directStateAccess = qfalse; diff --git a/code/renderergl2/tr_fbo.c b/code/renderergl2/tr_fbo.c index 6b7ab01a27..54dc8fe03c 100644 --- a/code/renderergl2/tr_fbo.c +++ b/code/renderergl2/tr_fbo.c @@ -32,48 +32,38 @@ R_CheckFBO */ qboolean R_CheckFBO(const FBO_t * fbo) { - GLenum code = qglCheckNamedFramebufferStatusEXT(fbo->frameBuffer, GL_FRAMEBUFFER_EXT); + GLenum code = qglCheckNamedFramebufferStatusEXT(fbo->frameBuffer, GL_FRAMEBUFFER); - if(code == GL_FRAMEBUFFER_COMPLETE_EXT) + if(code == GL_FRAMEBUFFER_COMPLETE) return qtrue; // an error occured switch (code) { - case GL_FRAMEBUFFER_UNSUPPORTED_EXT: + case GL_FRAMEBUFFER_UNSUPPORTED: ri.Printf(PRINT_WARNING, "R_CheckFBO: (%s) Unsupported framebuffer format\n", fbo->name); break; - case GL_FRAMEBUFFER_INCOMPLETE_ATTACHMENT_EXT: + case GL_FRAMEBUFFER_INCOMPLETE_ATTACHMENT: ri.Printf(PRINT_WARNING, "R_CheckFBO: (%s) Framebuffer incomplete attachment\n", fbo->name); break; - case GL_FRAMEBUFFER_INCOMPLETE_MISSING_ATTACHMENT_EXT: + case GL_FRAMEBUFFER_INCOMPLETE_MISSING_ATTACHMENT: ri.Printf(PRINT_WARNING, "R_CheckFBO: (%s) Framebuffer incomplete, missing attachment\n", fbo->name); break; - //case GL_FRAMEBUFFER_INCOMPLETE_DUPLICATE_ATTACHMENT_EXT: - // ri.Printf(PRINT_WARNING, "R_CheckFBO: (%s) Framebuffer incomplete, duplicate attachment\n", fbo->name); - // break; - - case GL_FRAMEBUFFER_INCOMPLETE_DIMENSIONS_EXT: - ri.Printf(PRINT_WARNING, "R_CheckFBO: (%s) Framebuffer incomplete, attached images must have same dimensions\n", - fbo->name); - break; - - case GL_FRAMEBUFFER_INCOMPLETE_FORMATS_EXT: - ri.Printf(PRINT_WARNING, "R_CheckFBO: (%s) Framebuffer incomplete, attached images must have same format\n", - fbo->name); - break; - - case GL_FRAMEBUFFER_INCOMPLETE_DRAW_BUFFER_EXT: + case GL_FRAMEBUFFER_INCOMPLETE_DRAW_BUFFER: ri.Printf(PRINT_WARNING, "R_CheckFBO: (%s) Framebuffer incomplete, missing draw buffer\n", fbo->name); break; - case GL_FRAMEBUFFER_INCOMPLETE_READ_BUFFER_EXT: + case GL_FRAMEBUFFER_INCOMPLETE_READ_BUFFER: ri.Printf(PRINT_WARNING, "R_CheckFBO: (%s) Framebuffer incomplete, missing read buffer\n", fbo->name); break; + case GL_FRAMEBUFFER_INCOMPLETE_MULTISAMPLE: + ri.Printf(PRINT_WARNING, "R_CheckFBO: (%s) Framebuffer incomplete multisample\n", fbo->name); + break; + default: ri.Printf(PRINT_WARNING, "R_CheckFBO: (%s) unknown error 0x%X\n", fbo->name, code); break; @@ -117,7 +107,7 @@ FBO_t *FBO_Create(const char *name, int width, int height) fbo->width = width; fbo->height = height; - qglGenFramebuffersEXT(1, &fbo->frameBuffer); + qglGenFramebuffers(1, &fbo->frameBuffer); return fbo; } @@ -145,7 +135,7 @@ void FBO_CreateBuffer(FBO_t *fbo, int format, int index, int multisample) case GL_RGBA32F_ARB: fbo->colorFormat = format; pRenderBuffer = &fbo->colorBuffers[index]; - attachment = GL_COLOR_ATTACHMENT0_EXT + index; + attachment = GL_COLOR_ATTACHMENT0 + index; break; case GL_DEPTH_COMPONENT: @@ -154,21 +144,21 @@ void FBO_CreateBuffer(FBO_t *fbo, int format, int index, int multisample) case GL_DEPTH_COMPONENT32_ARB: fbo->depthFormat = format; pRenderBuffer = &fbo->depthBuffer; - attachment = GL_DEPTH_ATTACHMENT_EXT; + attachment = GL_DEPTH_ATTACHMENT; break; case GL_STENCIL_INDEX: - case GL_STENCIL_INDEX1_EXT: - case GL_STENCIL_INDEX4_EXT: - case GL_STENCIL_INDEX8_EXT: - case GL_STENCIL_INDEX16_EXT: + case GL_STENCIL_INDEX1: + case GL_STENCIL_INDEX4: + case GL_STENCIL_INDEX8: + case GL_STENCIL_INDEX16: fbo->stencilFormat = format; pRenderBuffer = &fbo->stencilBuffer; - attachment = GL_STENCIL_ATTACHMENT_EXT; + attachment = GL_STENCIL_ATTACHMENT; break; - case GL_DEPTH_STENCIL_EXT: - case GL_DEPTH24_STENCIL8_EXT: + case GL_DEPTH_STENCIL: + case GL_DEPTH24_STENCIL8: fbo->packedDepthStencilFormat = format; pRenderBuffer = &fbo->packedDepthStencilBuffer; attachment = 0; // special for stencil and depth @@ -181,7 +171,7 @@ void FBO_CreateBuffer(FBO_t *fbo, int format, int index, int multisample) absent = *pRenderBuffer == 0; if (absent) - qglGenRenderbuffersEXT(1, pRenderBuffer); + qglGenRenderbuffers(1, pRenderBuffer); if (multisample && glRefConfig.framebufferMultisample) qglNamedRenderbufferStorageMultisampleEXT(*pRenderBuffer, multisample, format, fbo->width, fbo->height); @@ -192,12 +182,12 @@ void FBO_CreateBuffer(FBO_t *fbo, int format, int index, int multisample) { if (attachment == 0) { - qglNamedFramebufferRenderbufferEXT(fbo->frameBuffer, GL_DEPTH_ATTACHMENT_EXT, GL_RENDERBUFFER_EXT, *pRenderBuffer); - qglNamedFramebufferRenderbufferEXT(fbo->frameBuffer, GL_STENCIL_ATTACHMENT_EXT, GL_RENDERBUFFER_EXT, *pRenderBuffer); + qglNamedFramebufferRenderbufferEXT(fbo->frameBuffer, GL_DEPTH_ATTACHMENT, GL_RENDERBUFFER, *pRenderBuffer); + qglNamedFramebufferRenderbufferEXT(fbo->frameBuffer, GL_STENCIL_ATTACHMENT, GL_RENDERBUFFER, *pRenderBuffer); } else { - qglNamedFramebufferRenderbufferEXT(fbo->frameBuffer, attachment, GL_RENDERBUFFER_EXT, *pRenderBuffer); + qglNamedFramebufferRenderbufferEXT(fbo->frameBuffer, attachment, GL_RENDERBUFFER, *pRenderBuffer); } } } @@ -217,7 +207,7 @@ void FBO_AttachImage(FBO_t *fbo, image_t *image, GLenum attachment, GLuint cubem target = GL_TEXTURE_CUBE_MAP_POSITIVE_X_ARB + cubemapside; qglNamedFramebufferTexture2DEXT(fbo->frameBuffer, attachment, target, image->texnum, 0); - index = attachment - GL_COLOR_ATTACHMENT0_EXT; + index = attachment - GL_COLOR_ATTACHMENT0; if (index >= 0 && index <= 15) fbo->colorImage[index] = image; } @@ -245,7 +235,7 @@ void FBO_Bind(FBO_t * fbo) GLimp_LogComment(va("--- FBO_Bind( %s ) ---\n", fbo ? fbo->name : "NULL")); } - GL_BindFramebuffer(GL_FRAMEBUFFER_EXT, fbo ? fbo->frameBuffer : 0); + GL_BindFramebuffer(GL_FRAMEBUFFER, fbo ? fbo->frameBuffer : 0); glState.currentFBO = fbo; } @@ -275,7 +265,7 @@ void FBO_Init(void) hdrFormat = GL_RGBA16F_ARB; if (glRefConfig.framebufferMultisample) - qglGetIntegerv(GL_MAX_SAMPLES_EXT, &multisample); + qglGetIntegerv(GL_MAX_SAMPLES, &multisample); if (r_ext_framebuffer_multisample->integer < multisample) multisample = r_ext_framebuffer_multisample->integer; @@ -292,19 +282,19 @@ void FBO_Init(void) { tr.renderFbo = FBO_Create("_render", tr.renderDepthImage->width, tr.renderDepthImage->height); FBO_CreateBuffer(tr.renderFbo, hdrFormat, 0, multisample); - FBO_CreateBuffer(tr.renderFbo, GL_DEPTH_COMPONENT24_ARB, 0, multisample); + FBO_CreateBuffer(tr.renderFbo, GL_DEPTH_COMPONENT24, 0, multisample); R_CheckFBO(tr.renderFbo); tr.msaaResolveFbo = FBO_Create("_msaaResolve", tr.renderDepthImage->width, tr.renderDepthImage->height); - FBO_AttachImage(tr.msaaResolveFbo, tr.renderImage, GL_COLOR_ATTACHMENT0_EXT, 0); - FBO_AttachImage(tr.msaaResolveFbo, tr.renderDepthImage, GL_DEPTH_ATTACHMENT_EXT, 0); + FBO_AttachImage(tr.msaaResolveFbo, tr.renderImage, GL_COLOR_ATTACHMENT0, 0); + FBO_AttachImage(tr.msaaResolveFbo, tr.renderDepthImage, GL_DEPTH_ATTACHMENT, 0); R_CheckFBO(tr.msaaResolveFbo); } else if (r_hdr->integer) { tr.renderFbo = FBO_Create("_render", tr.renderDepthImage->width, tr.renderDepthImage->height); - FBO_AttachImage(tr.renderFbo, tr.renderImage, GL_COLOR_ATTACHMENT0_EXT, 0); - FBO_AttachImage(tr.renderFbo, tr.renderDepthImage, GL_DEPTH_ATTACHMENT_EXT, 0); + FBO_AttachImage(tr.renderFbo, tr.renderImage, GL_COLOR_ATTACHMENT0, 0); + FBO_AttachImage(tr.renderFbo, tr.renderDepthImage, GL_DEPTH_ATTACHMENT, 0); R_CheckFBO(tr.renderFbo); } @@ -312,23 +302,23 @@ void FBO_Init(void) // this fixes the corrupt screen bug with r_hdr 1 on older hardware if (tr.renderFbo) { - GL_BindFramebuffer(GL_FRAMEBUFFER_EXT, tr.renderFbo->frameBuffer); + GL_BindFramebuffer(GL_FRAMEBUFFER, tr.renderFbo->frameBuffer); qglClear( GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT ); } if (tr.screenScratchImage) { tr.screenScratchFbo = FBO_Create("screenScratch", tr.screenScratchImage->width, tr.screenScratchImage->height); - FBO_AttachImage(tr.screenScratchFbo, tr.screenScratchImage, GL_COLOR_ATTACHMENT0_EXT, 0); - FBO_AttachImage(tr.screenScratchFbo, tr.renderDepthImage, GL_DEPTH_ATTACHMENT_EXT, 0); + FBO_AttachImage(tr.screenScratchFbo, tr.screenScratchImage, GL_COLOR_ATTACHMENT0, 0); + FBO_AttachImage(tr.screenScratchFbo, tr.renderDepthImage, GL_DEPTH_ATTACHMENT, 0); R_CheckFBO(tr.screenScratchFbo); } if (tr.sunRaysImage) { tr.sunRaysFbo = FBO_Create("_sunRays", tr.renderDepthImage->width, tr.renderDepthImage->height); - FBO_AttachImage(tr.sunRaysFbo, tr.sunRaysImage, GL_COLOR_ATTACHMENT0_EXT, 0); - FBO_AttachImage(tr.sunRaysFbo, tr.renderDepthImage, GL_DEPTH_ATTACHMENT_EXT, 0); + FBO_AttachImage(tr.sunRaysFbo, tr.sunRaysImage, GL_COLOR_ATTACHMENT0, 0); + FBO_AttachImage(tr.sunRaysFbo, tr.renderDepthImage, GL_DEPTH_ATTACHMENT, 0); R_CheckFBO(tr.sunRaysFbo); } @@ -338,7 +328,7 @@ void FBO_Init(void) for( i = 0; i < MAX_DRAWN_PSHADOWS; i++) { tr.pshadowFbos[i] = FBO_Create(va("_shadowmap%d", i), tr.pshadowMaps[i]->width, tr.pshadowMaps[i]->height); - FBO_AttachImage(tr.pshadowFbos[i], tr.pshadowMaps[i], GL_COLOR_ATTACHMENT0_EXT, 0); + FBO_AttachImage(tr.pshadowFbos[i], tr.pshadowMaps[i], GL_COLOR_ATTACHMENT0, 0); FBO_CreateBuffer(tr.pshadowFbos[i], GL_DEPTH_COMPONENT24_ARB, 0, 0); R_CheckFBO(tr.pshadowFbos[i]); } @@ -352,7 +342,7 @@ void FBO_Init(void) // FIXME: this next line wastes 16mb with 4x1024x1024 sun shadow maps, skip if OpenGL 4.3+ or ARB_framebuffer_no_attachments // This at least gets sun shadows working on older GPUs (Intel) FBO_CreateBuffer(tr.sunShadowFbo[i], GL_RGBA8, 0, 0); - FBO_AttachImage(tr.sunShadowFbo[i], tr.sunShadowDepthImage[i], GL_DEPTH_ATTACHMENT_EXT, 0); + FBO_AttachImage(tr.sunShadowFbo[i], tr.sunShadowDepthImage[i], GL_DEPTH_ATTACHMENT, 0); R_CheckFBO(tr.sunShadowFbo[i]); } } @@ -360,7 +350,7 @@ void FBO_Init(void) if (tr.screenShadowImage) { tr.screenShadowFbo = FBO_Create("_screenshadow", tr.screenShadowImage->width, tr.screenShadowImage->height); - FBO_AttachImage(tr.screenShadowFbo, tr.screenShadowImage, GL_COLOR_ATTACHMENT0_EXT, 0); + FBO_AttachImage(tr.screenShadowFbo, tr.screenShadowImage, GL_COLOR_ATTACHMENT0, 0); R_CheckFBO(tr.screenShadowFbo); } @@ -369,7 +359,7 @@ void FBO_Init(void) for (i = 0; i < 2; i++) { tr.textureScratchFbo[i] = FBO_Create(va("_texturescratch%d", i), tr.textureScratchImage[i]->width, tr.textureScratchImage[i]->height); - FBO_AttachImage(tr.textureScratchFbo[i], tr.textureScratchImage[i], GL_COLOR_ATTACHMENT0_EXT, 0); + FBO_AttachImage(tr.textureScratchFbo[i], tr.textureScratchImage[i], GL_COLOR_ATTACHMENT0, 0); R_CheckFBO(tr.textureScratchFbo[i]); } } @@ -377,14 +367,14 @@ void FBO_Init(void) if (tr.calcLevelsImage) { tr.calcLevelsFbo = FBO_Create("_calclevels", tr.calcLevelsImage->width, tr.calcLevelsImage->height); - FBO_AttachImage(tr.calcLevelsFbo, tr.calcLevelsImage, GL_COLOR_ATTACHMENT0_EXT, 0); + FBO_AttachImage(tr.calcLevelsFbo, tr.calcLevelsImage, GL_COLOR_ATTACHMENT0, 0); R_CheckFBO(tr.calcLevelsFbo); } if (tr.targetLevelsImage) { tr.targetLevelsFbo = FBO_Create("_targetlevels", tr.targetLevelsImage->width, tr.targetLevelsImage->height); - FBO_AttachImage(tr.targetLevelsFbo, tr.targetLevelsImage, GL_COLOR_ATTACHMENT0_EXT, 0); + FBO_AttachImage(tr.targetLevelsFbo, tr.targetLevelsImage, GL_COLOR_ATTACHMENT0, 0); R_CheckFBO(tr.targetLevelsFbo); } @@ -393,7 +383,7 @@ void FBO_Init(void) for (i = 0; i < 2; i++) { tr.quarterFbo[i] = FBO_Create(va("_quarter%d", i), tr.quarterImage[i]->width, tr.quarterImage[i]->height); - FBO_AttachImage(tr.quarterFbo[i], tr.quarterImage[i], GL_COLOR_ATTACHMENT0_EXT, 0); + FBO_AttachImage(tr.quarterFbo[i], tr.quarterImage[i], GL_COLOR_ATTACHMENT0, 0); R_CheckFBO(tr.quarterFbo[i]); } } @@ -401,28 +391,28 @@ void FBO_Init(void) if (tr.hdrDepthImage) { tr.hdrDepthFbo = FBO_Create("_hdrDepth", tr.hdrDepthImage->width, tr.hdrDepthImage->height); - FBO_AttachImage(tr.hdrDepthFbo, tr.hdrDepthImage, GL_COLOR_ATTACHMENT0_EXT, 0); + FBO_AttachImage(tr.hdrDepthFbo, tr.hdrDepthImage, GL_COLOR_ATTACHMENT0, 0); R_CheckFBO(tr.hdrDepthFbo); } if (tr.screenSsaoImage) { tr.screenSsaoFbo = FBO_Create("_screenssao", tr.screenSsaoImage->width, tr.screenSsaoImage->height); - FBO_AttachImage(tr.screenSsaoFbo, tr.screenSsaoImage, GL_COLOR_ATTACHMENT0_EXT, 0); + FBO_AttachImage(tr.screenSsaoFbo, tr.screenSsaoImage, GL_COLOR_ATTACHMENT0, 0); R_CheckFBO(tr.screenSsaoFbo); } if (tr.renderCubeImage) { tr.renderCubeFbo = FBO_Create("_renderCubeFbo", tr.renderCubeImage->width, tr.renderCubeImage->height); - FBO_AttachImage(tr.renderCubeFbo, tr.renderCubeImage, GL_COLOR_ATTACHMENT0_EXT, 0); + FBO_AttachImage(tr.renderCubeFbo, tr.renderCubeImage, GL_COLOR_ATTACHMENT0, 0); FBO_CreateBuffer(tr.renderCubeFbo, GL_DEPTH_COMPONENT24_ARB, 0, 0); R_CheckFBO(tr.renderCubeFbo); } GL_CheckErrors(); - GL_BindFramebuffer(GL_FRAMEBUFFER_EXT, 0); + GL_BindFramebuffer(GL_FRAMEBUFFER, 0); glState.currentFBO = NULL; } @@ -450,17 +440,17 @@ void FBO_Shutdown(void) for(j = 0; j < glRefConfig.maxColorAttachments; j++) { if(fbo->colorBuffers[j]) - qglDeleteRenderbuffersEXT(1, &fbo->colorBuffers[j]); + qglDeleteRenderbuffers(1, &fbo->colorBuffers[j]); } if(fbo->depthBuffer) - qglDeleteRenderbuffersEXT(1, &fbo->depthBuffer); + qglDeleteRenderbuffers(1, &fbo->depthBuffer); if(fbo->stencilBuffer) - qglDeleteRenderbuffersEXT(1, &fbo->stencilBuffer); + qglDeleteRenderbuffers(1, &fbo->stencilBuffer); if(fbo->frameBuffer) - qglDeleteFramebuffersEXT(1, &fbo->frameBuffer); + qglDeleteFramebuffers(1, &fbo->frameBuffer); } } @@ -661,12 +651,12 @@ void FBO_FastBlit(FBO_t *src, ivec4_t srcBox, FBO_t *dst, ivec4_t dstBox, int bu VectorSet4(dstBoxFinal, dstBox[0], dstBox[1], dstBox[0] + dstBox[2], dstBox[1] + dstBox[3]); } - GL_BindFramebuffer(GL_READ_FRAMEBUFFER_EXT, srcFb); - GL_BindFramebuffer(GL_DRAW_FRAMEBUFFER_EXT, dstFb); - qglBlitFramebufferEXT(srcBoxFinal[0], srcBoxFinal[1], srcBoxFinal[2], srcBoxFinal[3], + GL_BindFramebuffer(GL_READ_FRAMEBUFFER, srcFb); + GL_BindFramebuffer(GL_DRAW_FRAMEBUFFER, dstFb); + qglBlitFramebuffer(srcBoxFinal[0], srcBoxFinal[1], srcBoxFinal[2], srcBoxFinal[3], dstBoxFinal[0], dstBoxFinal[1], dstBoxFinal[2], dstBoxFinal[3], buffers, filter); - GL_BindFramebuffer(GL_FRAMEBUFFER_EXT, 0); + GL_BindFramebuffer(GL_FRAMEBUFFER, 0); glState.currentFBO = NULL; } diff --git a/code/renderergl2/tr_glsl.c b/code/renderergl2/tr_glsl.c index 966c2176a0..9f01f22a5f 100644 --- a/code/renderergl2/tr_glsl.c +++ b/code/renderergl2/tr_glsl.c @@ -146,6 +146,8 @@ static uniformInfo_t uniformsInfo[] = { "u_PrimaryLightRadius", GLSL_FLOAT }, { "u_CubeMapInfo", GLSL_VEC4 }, + + { "u_AlphaTest", GLSL_INT }, }; typedef enum @@ -239,7 +241,10 @@ static void GLSL_GetShaderHeader( GLenum shaderType, const GLchar *extra, char * // HACK: abuse the GLSL preprocessor to turn GLSL 1.20 shaders into 1.30 ones if(glRefConfig.glslMajorVersion > 1 || (glRefConfig.glslMajorVersion == 1 && glRefConfig.glslMinorVersion >= 30)) { - Q_strcat(dest, size, "#version 130\n"); + if (glRefConfig.glslMajorVersion > 1 || (glRefConfig.glslMajorVersion == 1 && glRefConfig.glslMinorVersion >= 50)) + Q_strcat(dest, size, "#version 150\n"); + else + Q_strcat(dest, size, "#version 130\n"); if(shaderType == GL_VERTEX_SHADER) { @@ -252,11 +257,15 @@ static void GLSL_GetShaderHeader( GLenum shaderType, const GLchar *extra, char * Q_strcat(dest, size, "out vec4 out_Color;\n"); Q_strcat(dest, size, "#define gl_FragColor out_Color\n"); + Q_strcat(dest, size, "#define texture2D texture\n"); + Q_strcat(dest, size, "#define textureCubeLod textureLod\n"); + Q_strcat(dest, size, "#define shadow2D texture\n"); } } else { Q_strcat(dest, size, "#version 120\n"); + Q_strcat(dest, size, "#define shadow2D(a,b) shadow2D(a,b).r \n"); } // HACK: add some macros to avoid extra uniforms and save speed and code maintenance @@ -945,7 +954,7 @@ void GLSL_InitGPUShaders(void) attribs = ATTR_POSITION | ATTR_TEXCOORD; - if (!GLSL_InitGPUShader(&tr.textureColorShader, "texturecolor", attribs, qtrue, NULL, qfalse, fallbackShader_texturecolor_vp, fallbackShader_texturecolor_fp)) + if (!GLSL_InitGPUShader(&tr.textureColorShader, "texturecolor", attribs, qtrue, extradefines, qtrue, fallbackShader_texturecolor_vp, fallbackShader_texturecolor_fp)) { ri.Error(ERR_FATAL, "Could not load texturecolor shader!"); } diff --git a/code/renderergl2/tr_image.c b/code/renderergl2/tr_image.c index 3672910d4c..ae15abcb22 100644 --- a/code/renderergl2/tr_image.c +++ b/code/renderergl2/tr_image.c @@ -2126,7 +2126,7 @@ image_t *R_CreateImage2( const char *name, byte *pic, int width, int height, GLe } image = tr.images[tr.numImages] = ri.Hunk_Alloc( sizeof( image_t ), h_low ); - image->texnum = 1024 + tr.numImages; + qglGenTextures(1, &image->texnum); tr.numImages++; image->type = type; @@ -2772,13 +2772,13 @@ void R_CreateBuiltinImages( void ) { tr.screenScratchImage = R_CreateImage("screenScratch", NULL, width, height, IMGTYPE_COLORALPHA, IMGFLAG_NO_COMPRESSION | IMGFLAG_CLAMPTOEDGE, rgbFormat); if (r_shadowBlur->integer || r_ssao->integer) - tr.hdrDepthImage = R_CreateImage("*hdrDepth", NULL, width, height, IMGTYPE_COLORALPHA, IMGFLAG_NO_COMPRESSION | IMGFLAG_CLAMPTOEDGE, GL_INTENSITY32F_ARB); + tr.hdrDepthImage = R_CreateImage("*hdrDepth", NULL, width, height, IMGTYPE_COLORALPHA, IMGFLAG_NO_COMPRESSION | IMGFLAG_CLAMPTOEDGE, GL_R32F); if (r_drawSunRays->integer) tr.sunRaysImage = R_CreateImage("*sunRays", NULL, width, height, IMGTYPE_COLORALPHA, IMGFLAG_NO_COMPRESSION | IMGFLAG_CLAMPTOEDGE, rgbFormat); - tr.renderDepthImage = R_CreateImage("*renderdepth", NULL, width, height, IMGTYPE_COLORALPHA, IMGFLAG_NO_COMPRESSION | IMGFLAG_CLAMPTOEDGE, GL_DEPTH_COMPONENT24_ARB); - tr.textureDepthImage = R_CreateImage("*texturedepth", NULL, PSHADOW_MAP_SIZE, PSHADOW_MAP_SIZE, IMGTYPE_COLORALPHA, IMGFLAG_NO_COMPRESSION | IMGFLAG_CLAMPTOEDGE, GL_DEPTH_COMPONENT24_ARB); + tr.renderDepthImage = R_CreateImage("*renderdepth", NULL, width, height, IMGTYPE_COLORALPHA, IMGFLAG_NO_COMPRESSION | IMGFLAG_CLAMPTOEDGE, GL_DEPTH_COMPONENT24); + tr.textureDepthImage = R_CreateImage("*texturedepth", NULL, PSHADOW_MAP_SIZE, PSHADOW_MAP_SIZE, IMGTYPE_COLORALPHA, IMGFLAG_NO_COMPRESSION | IMGFLAG_CLAMPTOEDGE, GL_DEPTH_COMPONENT24); { void *p; @@ -2820,7 +2820,7 @@ void R_CreateBuiltinImages( void ) { { for ( x = 0; x < 4; x++) { - tr.sunShadowDepthImage[x] = R_CreateImage(va("*sunshadowdepth%i", x), NULL, r_shadowMapSize->integer, r_shadowMapSize->integer, IMGTYPE_COLORALPHA, IMGFLAG_NO_COMPRESSION | IMGFLAG_CLAMPTOEDGE, GL_DEPTH_COMPONENT24_ARB); + tr.sunShadowDepthImage[x] = R_CreateImage(va("*sunshadowdepth%i", x), NULL, r_shadowMapSize->integer, r_shadowMapSize->integer, IMGTYPE_COLORALPHA, IMGFLAG_NO_COMPRESSION | IMGFLAG_CLAMPTOEDGE, GL_DEPTH_COMPONENT24); qglTextureParameterfEXT(tr.sunShadowDepthImage[x]->texnum, GL_TEXTURE_2D, GL_TEXTURE_COMPARE_MODE, GL_COMPARE_R_TO_TEXTURE); qglTextureParameterfEXT(tr.sunShadowDepthImage[x]->texnum, GL_TEXTURE_2D, GL_TEXTURE_COMPARE_FUNC, GL_LEQUAL); } diff --git a/code/renderergl2/tr_init.c b/code/renderergl2/tr_init.c index 2e63559383..fe1014ba09 100644 --- a/code/renderergl2/tr_init.c +++ b/code/renderergl2/tr_init.c @@ -259,7 +259,7 @@ static void InitOpenGL( void ) { GLint temp; - GLimp_Init(); + GLimp_Init( qtrue ); GLimp_InitExtraExtensions(); strcpy( renderer_buffer, glConfig.renderer_string ); @@ -943,14 +943,11 @@ void GL_SetDefaultState( void ) qglCullFace(GL_FRONT); - qglColor4f (1,1,1,1); - GL_BindNullTextures(); if (glRefConfig.framebufferObject) GL_BindNullFramebuffers(); - qglEnable(GL_TEXTURE_2D); GL_TextureMode( r_textureMode->string ); //qglShadeModel( GL_SMOOTH ); @@ -1034,7 +1031,21 @@ void GfxInfo_f( void ) ri.Printf( PRINT_ALL, "GL_RENDERER: %s\n", glConfig.renderer_string ); ri.Printf( PRINT_ALL, "GL_VERSION: %s\n", glConfig.version_string ); ri.Printf( PRINT_ALL, "GL_EXTENSIONS: " ); - R_PrintLongString( glConfig.extensions_string ); + if (glRefConfig.openglMajorVersion >= 3) + { + GLint numExtensions; + int i; + + glGetIntegerv(GL_NUM_EXTENSIONS, &numExtensions); + for (i = 0; i < numExtensions; i++) + { + ri.Printf(PRINT_ALL, "%s ", qglGetStringi(GL_EXTENSIONS, i)); + } + } + else + { + R_PrintLongString( glConfig.extensions_string ); + } ri.Printf( PRINT_ALL, "\n" ); ri.Printf( PRINT_ALL, "GL_MAX_TEXTURE_SIZE: %d\n", glConfig.maxTextureSize ); ri.Printf( PRINT_ALL, "GL_MAX_TEXTURE_UNITS_ARB: %d\n", glConfig.numTextureUnits ); @@ -1060,7 +1071,6 @@ void GfxInfo_f( void ) ri.Printf( PRINT_ALL, "texturemode: %s\n", r_textureMode->string ); ri.Printf( PRINT_ALL, "picmip: %d\n", r_picmip->integer ); ri.Printf( PRINT_ALL, "texture bits: %d\n", r_texturebits->integer ); - ri.Printf( PRINT_ALL, "multitexture: %s\n", enablestrings[qglActiveTextureARB != 0] ); ri.Printf( PRINT_ALL, "compiled vertex arrays: %s\n", enablestrings[qglLockArraysEXT != 0 ] ); ri.Printf( PRINT_ALL, "texenv add: %s\n", enablestrings[glConfig.textureEnvAddAvailable != 0] ); ri.Printf( PRINT_ALL, "compressed textures: %s\n", enablestrings[glConfig.textureCompression!=TC_NONE] ); diff --git a/code/renderergl2/tr_local.h b/code/renderergl2/tr_local.h index bdd5a85912..dd959d5cb9 100644 --- a/code/renderergl2/tr_local.h +++ b/code/renderergl2/tr_local.h @@ -694,6 +694,8 @@ typedef enum UNIFORM_CUBEMAPINFO, + UNIFORM_ALPHATEST, + UNIFORM_COUNT } uniform_t; diff --git a/code/renderergl2/tr_shade.c b/code/renderergl2/tr_shade.c index ac435fcb98..657f50736f 100644 --- a/code/renderergl2/tr_shade.c +++ b/code/renderergl2/tr_shade.c @@ -115,6 +115,7 @@ static void DrawTris (shaderCommands_t *input) { GLSL_SetUniformMat4(sp, UNIFORM_MODELVIEWPROJECTIONMATRIX, glState.modelviewProjection); VectorSet4(color, 1, 1, 1, 1); GLSL_SetUniformVec4(sp, UNIFORM_COLOR, color); + GLSL_SetUniformInt(sp, UNIFORM_ALPHATEST, 0); R_DrawElements(input->numIndexes, input->firstIndex); } @@ -383,6 +384,8 @@ static void ProjectDlightTexture( void ) { GL_State( GLS_ATEST_GT_0 | GLS_SRCBLEND_DST_COLOR | GLS_DSTBLEND_ONE | GLS_DEPTHFUNC_EQUAL ); } + GLSL_SetUniformInt(sp, UNIFORM_ALPHATEST, 1); + R_DrawElements(tess.numIndexes, tess.firstIndex); backEnd.pc.c_totalIndexes += tess.numIndexes; @@ -746,6 +749,7 @@ static void ForwardDlight( void ) { // include GLS_DEPTHFUNC_EQUAL so alpha tested surfaces don't add light // where they aren't rendered GL_State( GLS_SRCBLEND_ONE | GLS_DSTBLEND_ONE | GLS_DEPTHFUNC_EQUAL ); + GLSL_SetUniformInt(sp, UNIFORM_ALPHATEST, 0); GLSL_SetUniformMat4(sp, UNIFORM_MODELMATRIX, backEnd.or.transformMatrix); @@ -858,6 +862,7 @@ static void ProjectPshadowVBOGLSL( void ) { // include GLS_DEPTHFUNC_EQUAL so alpha tested surfaces don't add light // where they aren't rendered GL_State( GLS_SRCBLEND_SRC_ALPHA | GLS_DSTBLEND_ONE_MINUS_SRC_ALPHA | GLS_DEPTHFUNC_EQUAL ); + GLSL_SetUniformInt(sp, UNIFORM_ALPHATEST, 0); GL_BindToTMU( tr.pshadowMaps[l], TB_DIFFUSEMAP ); @@ -939,6 +944,7 @@ static void RB_FogPass( void ) { } else { GL_State( GLS_SRCBLEND_SRC_ALPHA | GLS_DSTBLEND_ONE_MINUS_SRC_ALPHA ); } + GLSL_SetUniformInt(sp, UNIFORM_ALPHATEST, 0); R_DrawElements(tess.numIndexes, tess.firstIndex); } @@ -1081,6 +1087,23 @@ static void RB_IterateStagesGeneric( shaderCommands_t *input ) } GL_State( pStage->stateBits ); + if ((pStage->stateBits & GLS_ATEST_BITS) == GLS_ATEST_GT_0) + { + GLSL_SetUniformInt(sp, UNIFORM_ALPHATEST, 1); + } + else if ((pStage->stateBits & GLS_ATEST_BITS) == GLS_ATEST_LT_80) + { + GLSL_SetUniformInt(sp, UNIFORM_ALPHATEST, 2); + } + else if ((pStage->stateBits & GLS_ATEST_BITS) == GLS_ATEST_GE_80) + { + GLSL_SetUniformInt(sp, UNIFORM_ALPHATEST, 3); + } + else + { + GLSL_SetUniformInt(sp, UNIFORM_ALPHATEST, 0); + } + { vec4_t baseColor; @@ -1368,6 +1391,7 @@ static void RB_RenderShadowmap( shaderCommands_t *input ) GLSL_SetUniformFloat(sp, UNIFORM_LIGHTRADIUS, backEnd.viewParms.zFar); GL_State( 0 ); + GLSL_SetUniformInt(sp, UNIFORM_ALPHATEST, 0); // // do multitexture diff --git a/code/renderergl2/tr_shader.c b/code/renderergl2/tr_shader.c index 1df374e68a..a774f7ec11 100644 --- a/code/renderergl2/tr_shader.c +++ b/code/renderergl2/tr_shader.c @@ -3072,9 +3072,7 @@ static shader_t *FinishShader( void ) { // // look for multitexture potential // - if ( qglActiveTextureARB ) { - stage = CollapseStagesToGLSL(); - } + stage = CollapseStagesToGLSL(); if ( shader.lightmapIndex >= 0 && !hasLightmapStage ) { if (vertexLightmap) { diff --git a/code/renderergl2/tr_sky.c b/code/renderergl2/tr_sky.c index dd1fe53639..94f68d26e6 100644 --- a/code/renderergl2/tr_sky.c +++ b/code/renderergl2/tr_sky.c @@ -458,6 +458,8 @@ static void DrawSkySide( struct image_s *image, const int mins[2], const int max VectorSet4(vector, 0.0, 0.0, 0.0, 0.0); GLSL_SetUniformVec4(sp, UNIFORM_DIFFUSETEXOFFTURB, vector); + + GLSL_SetUniformInt(sp, UNIFORM_ALPHATEST, 0); } R_DrawElements(tess.numIndexes - tess.firstIndex, tess.firstIndex); diff --git a/code/renderergl2/tr_surface.c b/code/renderergl2/tr_surface.c index e123b54cac..5b6b933e67 100644 --- a/code/renderergl2/tr_surface.c +++ b/code/renderergl2/tr_surface.c @@ -546,6 +546,8 @@ static void RB_SurfaceBeam( void ) GLSL_SetUniformVec4(sp, UNIFORM_COLOR, colorRed); + GLSL_SetUniformInt(sp, UNIFORM_ALPHATEST, 0); + R_DrawElements(tess.numIndexes, tess.firstIndex); tess.numIndexes = 0; diff --git a/code/sdl/sdl_glimp.c b/code/sdl/sdl_glimp.c index 155b84bed2..abb66ec327 100644 --- a/code/sdl/sdl_glimp.c +++ b/code/sdl/sdl_glimp.c @@ -215,7 +215,7 @@ static void GLimp_DetectAvailableModes(void) GLimp_SetMode =============== */ -static int GLimp_SetMode(int mode, qboolean fullscreen, qboolean noborder) +static int GLimp_SetMode(int mode, qboolean fullscreen, qboolean noborder, qboolean coreContext) { const char *glstring; int perChannelColorBits; @@ -477,7 +477,37 @@ static int GLimp_SetMode(int mode, qboolean fullscreen, qboolean noborder) SDL_SetWindowIcon( SDL_window, icon ); - if( ( SDL_glContext = SDL_GL_CreateContext( SDL_window ) ) == NULL ) + if (coreContext) + { + int profileMask, majorVersion, minorVersion; + SDL_GL_GetAttribute(SDL_GL_CONTEXT_PROFILE_MASK, &profileMask); + SDL_GL_GetAttribute(SDL_GL_CONTEXT_MAJOR_VERSION, &majorVersion); + SDL_GL_GetAttribute(SDL_GL_CONTEXT_MINOR_VERSION, &minorVersion); + + ri.Printf(PRINT_ALL, "Trying to get an OpenGL 3.2 core context\n"); + SDL_GL_SetAttribute(SDL_GL_CONTEXT_PROFILE_MASK, SDL_GL_CONTEXT_PROFILE_CORE); + SDL_GL_SetAttribute(SDL_GL_CONTEXT_MAJOR_VERSION, 3); + SDL_GL_SetAttribute(SDL_GL_CONTEXT_MINOR_VERSION, 2); + if ((SDL_glContext = SDL_GL_CreateContext(SDL_window)) == NULL) + { + ri.Printf(PRINT_ALL, "SDL_GL_CreateContext failed: %s\n", SDL_GetError()); + ri.Printf(PRINT_ALL, "Reverting to default context\n"); + + SDL_GL_SetAttribute(SDL_GL_CONTEXT_PROFILE_MASK, profileMask); + SDL_GL_SetAttribute(SDL_GL_CONTEXT_MAJOR_VERSION, majorVersion); + SDL_GL_SetAttribute(SDL_GL_CONTEXT_MINOR_VERSION, minorVersion); + } + else + { + ri.Printf(PRINT_ALL, "SDL_GL_CreateContext succeeded, but: %s\n", SDL_GetError()); + } + } + else + { + SDL_glContext = NULL; + } + + if( !SDL_glContext && ( SDL_glContext = SDL_GL_CreateContext( SDL_window ) ) == NULL ) { ri.Printf( PRINT_DEVELOPER, "SDL_GL_CreateContext failed: %s\n", SDL_GetError( ) ); continue; @@ -522,7 +552,7 @@ static int GLimp_SetMode(int mode, qboolean fullscreen, qboolean noborder) GLimp_StartDriverAndSetMode =============== */ -static qboolean GLimp_StartDriverAndSetMode(int mode, qboolean fullscreen, qboolean noborder) +static qboolean GLimp_StartDriverAndSetMode(int mode, qboolean fullscreen, qboolean noborder, qboolean gl3Core) { rserr_t err; @@ -549,7 +579,7 @@ static qboolean GLimp_StartDriverAndSetMode(int mode, qboolean fullscreen, qbool fullscreen = qfalse; } - err = GLimp_SetMode(mode, fullscreen, noborder); + err = GLimp_SetMode(mode, fullscreen, noborder, gl3Core); switch ( err ) { @@ -744,7 +774,7 @@ This routine is responsible for initializing the OS specific portions of OpenGL =============== */ -void GLimp_Init( void ) +void GLimp_Init( qboolean coreContext) { ri.Printf( PRINT_DEVELOPER, "Glimp_Init( )\n" ); @@ -764,13 +794,13 @@ void GLimp_Init( void ) ri.Sys_GLimpInit( ); // Create the window and set up the context - if(GLimp_StartDriverAndSetMode(r_mode->integer, r_fullscreen->integer, r_noborder->integer)) + if(GLimp_StartDriverAndSetMode(r_mode->integer, r_fullscreen->integer, r_noborder->integer, coreContext)) goto success; // Try again, this time in a platform specific "safe mode" ri.Sys_GLimpSafeInit( ); - if(GLimp_StartDriverAndSetMode(r_mode->integer, r_fullscreen->integer, qfalse)) + if(GLimp_StartDriverAndSetMode(r_mode->integer, r_fullscreen->integer, qfalse, coreContext)) goto success; // Finally, try the default screen resolution @@ -779,7 +809,7 @@ void GLimp_Init( void ) ri.Printf( PRINT_ALL, "Setting r_mode %d failed, falling back on r_mode %d\n", r_mode->integer, R_MODE_FALLBACK ); - if(GLimp_StartDriverAndSetMode(R_MODE_FALLBACK, qfalse, qfalse)) + if(GLimp_StartDriverAndSetMode(R_MODE_FALLBACK, qfalse, qfalse, coreContext)) goto success; } @@ -801,7 +831,10 @@ void GLimp_Init( void ) if (*glConfig.renderer_string && glConfig.renderer_string[strlen(glConfig.renderer_string) - 1] == '\n') glConfig.renderer_string[strlen(glConfig.renderer_string) - 1] = 0; Q_strncpyz( glConfig.version_string, (char *) qglGetString (GL_VERSION), sizeof( glConfig.version_string ) ); - Q_strncpyz( glConfig.extensions_string, (char *) qglGetString (GL_EXTENSIONS), sizeof( glConfig.extensions_string ) ); + if (qglGetString(GL_EXTENSIONS)) + Q_strncpyz( glConfig.extensions_string, (char *) qglGetString (GL_EXTENSIONS), sizeof( glConfig.extensions_string ) ); + else + Q_strncpyz( glConfig.extensions_string, "Not available (core context, fixme)", sizeof( glConfig.extensions_string ) ); // initialize extensions GLimp_InitExtensions( ); From a4c09236b1cb7fe5ccd70126b6f872f51c84fdb6 Mon Sep 17 00:00:00 2001 From: SmileTheory Date: Thu, 13 Jul 2017 12:10:09 -0700 Subject: [PATCH 123/240] OpenGL2: Remove GLSL_ValidateProgram(). https://stackoverflow.com/questions/39761456/why-does-glvalidateprogram-fail-when-no-vao-is-bound --- code/renderergl2/tr_glsl.c | 15 --------------- 1 file changed, 15 deletions(-) diff --git a/code/renderergl2/tr_glsl.c b/code/renderergl2/tr_glsl.c index 9f01f22a5f..70ad816593 100644 --- a/code/renderergl2/tr_glsl.c +++ b/code/renderergl2/tr_glsl.c @@ -487,20 +487,6 @@ static void GLSL_LinkProgram(GLuint program) } } -static void GLSL_ValidateProgram(GLuint program) -{ - GLint validated; - - qglValidateProgram(program); - - qglGetProgramiv(program, GL_VALIDATE_STATUS, &validated); - if(!validated) - { - GLSL_PrintLog(program, GLSL_PRINTLOG_PROGRAM_INFO, qfalse); - ri.Error(ERR_DROP, "shaders failed to validate"); - } -} - static void GLSL_ShowProgramUniforms(GLuint program) { int i, count, size; @@ -695,7 +681,6 @@ void GLSL_InitUniforms(shaderProgram_t *program) void GLSL_FinishGPUShader(shaderProgram_t *program) { - GLSL_ValidateProgram(program->program); GLSL_ShowProgramUniforms(program->program); GL_CheckErrors(); } From 6a77f4e3634af9bec0693ab29422c52d95b43062 Mon Sep 17 00:00:00 2001 From: SmileTheory Date: Fri, 14 Jul 2017 15:42:41 -0700 Subject: [PATCH 124/240] OpenGL2: Don't do MSAA resolve/shadow mask/SSAO on shadow views. --- code/renderergl2/tr_backend.c | 305 ++++++++++++++++++---------------- 1 file changed, 162 insertions(+), 143 deletions(-) diff --git a/code/renderergl2/tr_backend.c b/code/renderergl2/tr_backend.c index 061d9d5599..dc27cdddfa 100644 --- a/code/renderergl2/tr_backend.c +++ b/code/renderergl2/tr_backend.c @@ -878,6 +878,7 @@ RB_DrawSurfs */ const void *RB_DrawSurfs( const void *data ) { const drawSurfsCommand_t *cmd; + qboolean isShadowView; // finish any 2D drawing if needed if ( tess.numIndexes ) { @@ -889,6 +890,8 @@ const void *RB_DrawSurfs( const void *data ) { backEnd.refdef = cmd->refdef; backEnd.viewParms = cmd->viewParms; + isShadowView = !!(backEnd.viewParms.flags & VPF_DEPTHSHADOW); + // clear the z buffer, set the modelview, etc RB_BeginDrawingView (); @@ -897,7 +900,7 @@ const void *RB_DrawSurfs( const void *data ) { qglEnable(GL_DEPTH_CLAMP); } - if (glRefConfig.framebufferObject && !(backEnd.refdef.rdflags & RDF_NOWORLDMODEL) && (r_depthPrepass->integer || (backEnd.viewParms.flags & VPF_DEPTHSHADOW))) + if (glRefConfig.framebufferObject && !(backEnd.refdef.rdflags & RDF_NOWORLDMODEL) && (r_depthPrepass->integer || isShadowView)) { FBO_t *oldFbo = glState.currentFBO; vec4_t viewInfo; @@ -910,205 +913,208 @@ const void *RB_DrawSurfs( const void *data ) { qglColorMask(!backEnd.colorMask[0], !backEnd.colorMask[1], !backEnd.colorMask[2], !backEnd.colorMask[3]); backEnd.depthFill = qfalse; - if (tr.msaaResolveFbo) - { - // If we're using multisampling, resolve the depth first - FBO_FastBlit(tr.renderFbo, NULL, tr.msaaResolveFbo, NULL, GL_DEPTH_BUFFER_BIT, GL_NEAREST); - } - else if (tr.renderFbo == NULL && tr.renderDepthImage) + if (!isShadowView) { - // If we're rendering directly to the screen, copy the depth to a texture - // This is incredibly slow on Intel Graphics, so just skip it on there - if (!glRefConfig.intelGraphics) - qglCopyTextureSubImage2DEXT(tr.renderDepthImage->texnum, GL_TEXTURE_2D, 0, 0, 0, 0, 0, glConfig.vidWidth, glConfig.vidHeight); - } + if (tr.msaaResolveFbo) + { + // If we're using multisampling, resolve the depth first + FBO_FastBlit(tr.renderFbo, NULL, tr.msaaResolveFbo, NULL, GL_DEPTH_BUFFER_BIT, GL_NEAREST); + } + else if (tr.renderFbo == NULL && tr.renderDepthImage) + { + // If we're rendering directly to the screen, copy the depth to a texture + // This is incredibly slow on Intel Graphics, so just skip it on there + if (!glRefConfig.intelGraphics) + qglCopyTextureSubImage2DEXT(tr.renderDepthImage->texnum, GL_TEXTURE_2D, 0, 0, 0, 0, 0, glConfig.vidWidth, glConfig.vidHeight); + } - if (tr.hdrDepthFbo) - { - // need the depth in a texture we can do GL_LINEAR sampling on, so copy it to an HDR image - vec4_t srcTexCoords; + if (tr.hdrDepthFbo) + { + // need the depth in a texture we can do GL_LINEAR sampling on, so copy it to an HDR image + vec4_t srcTexCoords; - VectorSet4(srcTexCoords, 0.0f, 0.0f, 1.0f, 1.0f); + VectorSet4(srcTexCoords, 0.0f, 0.0f, 1.0f, 1.0f); - FBO_BlitFromTexture(tr.renderDepthImage, srcTexCoords, NULL, tr.hdrDepthFbo, NULL, NULL, NULL, 0); - } + FBO_BlitFromTexture(tr.renderDepthImage, srcTexCoords, NULL, tr.hdrDepthFbo, NULL, NULL, NULL, 0); + } - if (r_sunlightMode->integer && backEnd.viewParms.flags & VPF_USESUNLIGHT) - { - vec4_t quadVerts[4]; - vec2_t texCoords[4]; - vec4_t box; + if (r_sunlightMode->integer && backEnd.viewParms.flags & VPF_USESUNLIGHT) + { + vec4_t quadVerts[4]; + vec2_t texCoords[4]; + vec4_t box; - FBO_Bind(tr.screenShadowFbo); + FBO_Bind(tr.screenShadowFbo); - box[0] = backEnd.viewParms.viewportX * tr.screenShadowFbo->width / (float)glConfig.vidWidth; - box[1] = backEnd.viewParms.viewportY * tr.screenShadowFbo->height / (float)glConfig.vidHeight; - box[2] = backEnd.viewParms.viewportWidth * tr.screenShadowFbo->width / (float)glConfig.vidWidth; - box[3] = backEnd.viewParms.viewportHeight * tr.screenShadowFbo->height / (float)glConfig.vidHeight; + box[0] = backEnd.viewParms.viewportX * tr.screenShadowFbo->width / (float)glConfig.vidWidth; + box[1] = backEnd.viewParms.viewportY * tr.screenShadowFbo->height / (float)glConfig.vidHeight; + box[2] = backEnd.viewParms.viewportWidth * tr.screenShadowFbo->width / (float)glConfig.vidWidth; + box[3] = backEnd.viewParms.viewportHeight * tr.screenShadowFbo->height / (float)glConfig.vidHeight; - qglViewport(box[0], box[1], box[2], box[3]); - qglScissor(box[0], box[1], box[2], box[3]); + qglViewport(box[0], box[1], box[2], box[3]); + qglScissor(box[0], box[1], box[2], box[3]); - box[0] = backEnd.viewParms.viewportX / (float)glConfig.vidWidth; - box[1] = backEnd.viewParms.viewportY / (float)glConfig.vidHeight; - box[2] = box[0] + backEnd.viewParms.viewportWidth / (float)glConfig.vidWidth; - box[3] = box[1] + backEnd.viewParms.viewportHeight / (float)glConfig.vidHeight; + box[0] = backEnd.viewParms.viewportX / (float)glConfig.vidWidth; + box[1] = backEnd.viewParms.viewportY / (float)glConfig.vidHeight; + box[2] = box[0] + backEnd.viewParms.viewportWidth / (float)glConfig.vidWidth; + box[3] = box[1] + backEnd.viewParms.viewportHeight / (float)glConfig.vidHeight; - texCoords[0][0] = box[0]; texCoords[0][1] = box[3]; - texCoords[1][0] = box[2]; texCoords[1][1] = box[3]; - texCoords[2][0] = box[2]; texCoords[2][1] = box[1]; - texCoords[3][0] = box[0]; texCoords[3][1] = box[1]; + texCoords[0][0] = box[0]; texCoords[0][1] = box[3]; + texCoords[1][0] = box[2]; texCoords[1][1] = box[3]; + texCoords[2][0] = box[2]; texCoords[2][1] = box[1]; + texCoords[3][0] = box[0]; texCoords[3][1] = box[1]; - box[0] = -1.0f; - box[1] = -1.0f; - box[2] = 1.0f; - box[3] = 1.0f; + box[0] = -1.0f; + box[1] = -1.0f; + box[2] = 1.0f; + box[3] = 1.0f; - VectorSet4(quadVerts[0], box[0], box[3], 0, 1); - VectorSet4(quadVerts[1], box[2], box[3], 0, 1); - VectorSet4(quadVerts[2], box[2], box[1], 0, 1); - VectorSet4(quadVerts[3], box[0], box[1], 0, 1); + VectorSet4(quadVerts[0], box[0], box[3], 0, 1); + VectorSet4(quadVerts[1], box[2], box[3], 0, 1); + VectorSet4(quadVerts[2], box[2], box[1], 0, 1); + VectorSet4(quadVerts[3], box[0], box[1], 0, 1); - GL_State( GLS_DEPTHTEST_DISABLE ); + GL_State(GLS_DEPTHTEST_DISABLE); - GLSL_BindProgram(&tr.shadowmaskShader); + GLSL_BindProgram(&tr.shadowmaskShader); - GL_BindToTMU(tr.renderDepthImage, TB_COLORMAP); - - if (r_shadowCascadeZFar->integer != 0) - { - GL_BindToTMU(tr.sunShadowDepthImage[0], TB_SHADOWMAP); - GL_BindToTMU(tr.sunShadowDepthImage[1], TB_SHADOWMAP2); - GL_BindToTMU(tr.sunShadowDepthImage[2], TB_SHADOWMAP3); - GL_BindToTMU(tr.sunShadowDepthImage[3], TB_SHADOWMAP4); - - GLSL_SetUniformMat4(&tr.shadowmaskShader, UNIFORM_SHADOWMVP, backEnd.refdef.sunShadowMvp[0]); - GLSL_SetUniformMat4(&tr.shadowmaskShader, UNIFORM_SHADOWMVP2, backEnd.refdef.sunShadowMvp[1]); - GLSL_SetUniformMat4(&tr.shadowmaskShader, UNIFORM_SHADOWMVP3, backEnd.refdef.sunShadowMvp[2]); - GLSL_SetUniformMat4(&tr.shadowmaskShader, UNIFORM_SHADOWMVP4, backEnd.refdef.sunShadowMvp[3]); - } - else - { - GL_BindToTMU(tr.sunShadowDepthImage[3], TB_SHADOWMAP); - GLSL_SetUniformMat4(&tr.shadowmaskShader, UNIFORM_SHADOWMVP, backEnd.refdef.sunShadowMvp[3]); - } - - GLSL_SetUniformVec3(&tr.shadowmaskShader, UNIFORM_VIEWORIGIN, backEnd.refdef.vieworg); - { - vec3_t viewVector; + GL_BindToTMU(tr.renderDepthImage, TB_COLORMAP); - float zmax = backEnd.viewParms.zFar; - float ymax = zmax * tan(backEnd.viewParms.fovY * M_PI / 360.0f); - float xmax = zmax * tan(backEnd.viewParms.fovX * M_PI / 360.0f); + if (r_shadowCascadeZFar->integer != 0) + { + GL_BindToTMU(tr.sunShadowDepthImage[0], TB_SHADOWMAP); + GL_BindToTMU(tr.sunShadowDepthImage[1], TB_SHADOWMAP2); + GL_BindToTMU(tr.sunShadowDepthImage[2], TB_SHADOWMAP3); + GL_BindToTMU(tr.sunShadowDepthImage[3], TB_SHADOWMAP4); + + GLSL_SetUniformMat4(&tr.shadowmaskShader, UNIFORM_SHADOWMVP, backEnd.refdef.sunShadowMvp[0]); + GLSL_SetUniformMat4(&tr.shadowmaskShader, UNIFORM_SHADOWMVP2, backEnd.refdef.sunShadowMvp[1]); + GLSL_SetUniformMat4(&tr.shadowmaskShader, UNIFORM_SHADOWMVP3, backEnd.refdef.sunShadowMvp[2]); + GLSL_SetUniformMat4(&tr.shadowmaskShader, UNIFORM_SHADOWMVP4, backEnd.refdef.sunShadowMvp[3]); + } + else + { + GL_BindToTMU(tr.sunShadowDepthImage[3], TB_SHADOWMAP); + GLSL_SetUniformMat4(&tr.shadowmaskShader, UNIFORM_SHADOWMVP, backEnd.refdef.sunShadowMvp[3]); + } - VectorScale(backEnd.refdef.viewaxis[0], zmax, viewVector); - GLSL_SetUniformVec3(&tr.shadowmaskShader, UNIFORM_VIEWFORWARD, viewVector); - VectorScale(backEnd.refdef.viewaxis[1], xmax, viewVector); - GLSL_SetUniformVec3(&tr.shadowmaskShader, UNIFORM_VIEWLEFT, viewVector); - VectorScale(backEnd.refdef.viewaxis[2], ymax, viewVector); - GLSL_SetUniformVec3(&tr.shadowmaskShader, UNIFORM_VIEWUP, viewVector); + GLSL_SetUniformVec3(&tr.shadowmaskShader, UNIFORM_VIEWORIGIN, backEnd.refdef.vieworg); + { + vec3_t viewVector; - GLSL_SetUniformVec4(&tr.shadowmaskShader, UNIFORM_VIEWINFO, viewInfo); - } + float zmax = backEnd.viewParms.zFar; + float ymax = zmax * tan(backEnd.viewParms.fovY * M_PI / 360.0f); + float xmax = zmax * tan(backEnd.viewParms.fovX * M_PI / 360.0f); - RB_InstantQuad2(quadVerts, texCoords); //, color, shaderProgram, invTexRes); + VectorScale(backEnd.refdef.viewaxis[0], zmax, viewVector); + GLSL_SetUniformVec3(&tr.shadowmaskShader, UNIFORM_VIEWFORWARD, viewVector); + VectorScale(backEnd.refdef.viewaxis[1], xmax, viewVector); + GLSL_SetUniformVec3(&tr.shadowmaskShader, UNIFORM_VIEWLEFT, viewVector); + VectorScale(backEnd.refdef.viewaxis[2], ymax, viewVector); + GLSL_SetUniformVec3(&tr.shadowmaskShader, UNIFORM_VIEWUP, viewVector); - if (r_shadowBlur->integer) - { - viewInfo[2] = 1.0f / (float)(tr.screenScratchFbo->width); - viewInfo[3] = 1.0f / (float)(tr.screenScratchFbo->height); + GLSL_SetUniformVec4(&tr.shadowmaskShader, UNIFORM_VIEWINFO, viewInfo); + } - FBO_Bind(tr.screenScratchFbo); + RB_InstantQuad2(quadVerts, texCoords); //, color, shaderProgram, invTexRes); - GLSL_BindProgram(&tr.depthBlurShader[0]); + if (r_shadowBlur->integer) + { + viewInfo[2] = 1.0f / (float)(tr.screenScratchFbo->width); + viewInfo[3] = 1.0f / (float)(tr.screenScratchFbo->height); - GL_BindToTMU(tr.screenShadowImage, TB_COLORMAP); - GL_BindToTMU(tr.hdrDepthImage, TB_LIGHTMAP); + FBO_Bind(tr.screenScratchFbo); - GLSL_SetUniformVec4(&tr.depthBlurShader[0], UNIFORM_VIEWINFO, viewInfo); + GLSL_BindProgram(&tr.depthBlurShader[0]); - RB_InstantQuad2(quadVerts, texCoords); + GL_BindToTMU(tr.screenShadowImage, TB_COLORMAP); + GL_BindToTMU(tr.hdrDepthImage, TB_LIGHTMAP); - FBO_Bind(tr.screenShadowFbo); + GLSL_SetUniformVec4(&tr.depthBlurShader[0], UNIFORM_VIEWINFO, viewInfo); - GLSL_BindProgram(&tr.depthBlurShader[1]); + RB_InstantQuad2(quadVerts, texCoords); - GL_BindToTMU(tr.screenScratchImage, TB_COLORMAP); - GL_BindToTMU(tr.hdrDepthImage, TB_LIGHTMAP); + FBO_Bind(tr.screenShadowFbo); - GLSL_SetUniformVec4(&tr.depthBlurShader[1], UNIFORM_VIEWINFO, viewInfo); + GLSL_BindProgram(&tr.depthBlurShader[1]); + + GL_BindToTMU(tr.screenScratchImage, TB_COLORMAP); + GL_BindToTMU(tr.hdrDepthImage, TB_LIGHTMAP); - RB_InstantQuad2(quadVerts, texCoords); + GLSL_SetUniformVec4(&tr.depthBlurShader[1], UNIFORM_VIEWINFO, viewInfo); + + RB_InstantQuad2(quadVerts, texCoords); + } } - } - if (r_ssao->integer) - { - vec4_t quadVerts[4]; - vec2_t texCoords[4]; + if (r_ssao->integer) + { + vec4_t quadVerts[4]; + vec2_t texCoords[4]; - viewInfo[2] = 1.0f / ((float)(tr.quarterImage[0]->width) * tan(backEnd.viewParms.fovX * M_PI / 360.0f) * 2.0f); - viewInfo[3] = 1.0f / ((float)(tr.quarterImage[0]->height) * tan(backEnd.viewParms.fovY * M_PI / 360.0f) * 2.0f); - viewInfo[3] *= (float)backEnd.viewParms.viewportHeight / (float)backEnd.viewParms.viewportWidth; + viewInfo[2] = 1.0f / ((float)(tr.quarterImage[0]->width) * tan(backEnd.viewParms.fovX * M_PI / 360.0f) * 2.0f); + viewInfo[3] = 1.0f / ((float)(tr.quarterImage[0]->height) * tan(backEnd.viewParms.fovY * M_PI / 360.0f) * 2.0f); + viewInfo[3] *= (float)backEnd.viewParms.viewportHeight / (float)backEnd.viewParms.viewportWidth; - FBO_Bind(tr.quarterFbo[0]); + FBO_Bind(tr.quarterFbo[0]); - qglViewport(0, 0, tr.quarterFbo[0]->width, tr.quarterFbo[0]->height); - qglScissor(0, 0, tr.quarterFbo[0]->width, tr.quarterFbo[0]->height); + qglViewport(0, 0, tr.quarterFbo[0]->width, tr.quarterFbo[0]->height); + qglScissor(0, 0, tr.quarterFbo[0]->width, tr.quarterFbo[0]->height); - VectorSet4(quadVerts[0], -1, 1, 0, 1); - VectorSet4(quadVerts[1], 1, 1, 0, 1); - VectorSet4(quadVerts[2], 1, -1, 0, 1); - VectorSet4(quadVerts[3], -1, -1, 0, 1); + VectorSet4(quadVerts[0], -1, 1, 0, 1); + VectorSet4(quadVerts[1], 1, 1, 0, 1); + VectorSet4(quadVerts[2], 1, -1, 0, 1); + VectorSet4(quadVerts[3], -1, -1, 0, 1); - texCoords[0][0] = 0; texCoords[0][1] = 1; - texCoords[1][0] = 1; texCoords[1][1] = 1; - texCoords[2][0] = 1; texCoords[2][1] = 0; - texCoords[3][0] = 0; texCoords[3][1] = 0; + texCoords[0][0] = 0; texCoords[0][1] = 1; + texCoords[1][0] = 1; texCoords[1][1] = 1; + texCoords[2][0] = 1; texCoords[2][1] = 0; + texCoords[3][0] = 0; texCoords[3][1] = 0; - GL_State( GLS_DEPTHTEST_DISABLE ); + GL_State( GLS_DEPTHTEST_DISABLE ); - GLSL_BindProgram(&tr.ssaoShader); + GLSL_BindProgram(&tr.ssaoShader); - GL_BindToTMU(tr.hdrDepthImage, TB_COLORMAP); + GL_BindToTMU(tr.hdrDepthImage, TB_COLORMAP); - GLSL_SetUniformVec4(&tr.ssaoShader, UNIFORM_VIEWINFO, viewInfo); + GLSL_SetUniformVec4(&tr.ssaoShader, UNIFORM_VIEWINFO, viewInfo); - RB_InstantQuad2(quadVerts, texCoords); //, color, shaderProgram, invTexRes); + RB_InstantQuad2(quadVerts, texCoords); //, color, shaderProgram, invTexRes); - viewInfo[2] = 1.0f / (float)(tr.quarterImage[0]->width); - viewInfo[3] = 1.0f / (float)(tr.quarterImage[0]->height); + viewInfo[2] = 1.0f / (float)(tr.quarterImage[0]->width); + viewInfo[3] = 1.0f / (float)(tr.quarterImage[0]->height); - FBO_Bind(tr.quarterFbo[1]); + FBO_Bind(tr.quarterFbo[1]); - qglViewport(0, 0, tr.quarterFbo[1]->width, tr.quarterFbo[1]->height); - qglScissor(0, 0, tr.quarterFbo[1]->width, tr.quarterFbo[1]->height); + qglViewport(0, 0, tr.quarterFbo[1]->width, tr.quarterFbo[1]->height); + qglScissor(0, 0, tr.quarterFbo[1]->width, tr.quarterFbo[1]->height); - GLSL_BindProgram(&tr.depthBlurShader[0]); + GLSL_BindProgram(&tr.depthBlurShader[0]); - GL_BindToTMU(tr.quarterImage[0], TB_COLORMAP); - GL_BindToTMU(tr.hdrDepthImage, TB_LIGHTMAP); + GL_BindToTMU(tr.quarterImage[0], TB_COLORMAP); + GL_BindToTMU(tr.hdrDepthImage, TB_LIGHTMAP); - GLSL_SetUniformVec4(&tr.depthBlurShader[0], UNIFORM_VIEWINFO, viewInfo); + GLSL_SetUniformVec4(&tr.depthBlurShader[0], UNIFORM_VIEWINFO, viewInfo); - RB_InstantQuad2(quadVerts, texCoords); //, color, shaderProgram, invTexRes); + RB_InstantQuad2(quadVerts, texCoords); //, color, shaderProgram, invTexRes); - FBO_Bind(tr.screenSsaoFbo); + FBO_Bind(tr.screenSsaoFbo); - qglViewport(0, 0, tr.screenSsaoFbo->width, tr.screenSsaoFbo->height); - qglScissor(0, 0, tr.screenSsaoFbo->width, tr.screenSsaoFbo->height); + qglViewport(0, 0, tr.screenSsaoFbo->width, tr.screenSsaoFbo->height); + qglScissor(0, 0, tr.screenSsaoFbo->width, tr.screenSsaoFbo->height); - GLSL_BindProgram(&tr.depthBlurShader[1]); + GLSL_BindProgram(&tr.depthBlurShader[1]); - GL_BindToTMU(tr.quarterImage[1], TB_COLORMAP); - GL_BindToTMU(tr.hdrDepthImage, TB_LIGHTMAP); + GL_BindToTMU(tr.quarterImage[1], TB_COLORMAP); + GL_BindToTMU(tr.hdrDepthImage, TB_LIGHTMAP); - GLSL_SetUniformVec4(&tr.depthBlurShader[1], UNIFORM_VIEWINFO, viewInfo); + GLSL_SetUniformVec4(&tr.depthBlurShader[1], UNIFORM_VIEWINFO, viewInfo); - RB_InstantQuad2(quadVerts, texCoords); //, color, shaderProgram, invTexRes); + RB_InstantQuad2(quadVerts, texCoords); //, color, shaderProgram, invTexRes); + } } // reset viewport and scissor @@ -1121,7 +1127,7 @@ const void *RB_DrawSurfs( const void *data ) { qglDisable(GL_DEPTH_CLAMP); } - if (!(backEnd.viewParms.flags & VPF_DEPTHSHADOW)) + if (!isShadowView) { RB_RenderDrawSurfList( cmd->drawSurfs, cmd->numDrawSurfs ); @@ -1608,6 +1614,19 @@ const void *RB_PostProcess(const void *data) FBO_BlitFromTexture(tr.sunShadowDepthImage[3], NULL, NULL, NULL, dstBox, NULL, NULL, 0); } + if (0 && r_shadows->integer == 4) + { + ivec4_t dstBox; + VectorSet4(dstBox, 0, glConfig.vidHeight - 128, 128, 128); + FBO_BlitFromTexture(tr.pshadowMaps[0], NULL, NULL, NULL, dstBox, NULL, NULL, 0); + VectorSet4(dstBox, 128, glConfig.vidHeight - 128, 128, 128); + FBO_BlitFromTexture(tr.pshadowMaps[1], NULL, NULL, NULL, dstBox, NULL, NULL, 0); + VectorSet4(dstBox, 256, glConfig.vidHeight - 128, 128, 128); + FBO_BlitFromTexture(tr.pshadowMaps[2], NULL, NULL, NULL, dstBox, NULL, NULL, 0); + VectorSet4(dstBox, 384, glConfig.vidHeight - 128, 128, 128); + FBO_BlitFromTexture(tr.pshadowMaps[3], NULL, NULL, NULL, dstBox, NULL, NULL, 0); + } + if (0) { ivec4_t dstBox; From 6f1712dafe2d32d424d110e20218532c41d16388 Mon Sep 17 00:00:00 2001 From: SmileTheory Date: Fri, 14 Jul 2017 16:15:02 -0700 Subject: [PATCH 125/240] OpenGL2: "Fix" cg_shadows 4. --- code/renderergl2/glsl/pshadow_fp.glsl | 44 ++++++++------------------- code/renderergl2/tr_backend.c | 18 +++++------ code/renderergl2/tr_fbo.c | 6 ++-- code/renderergl2/tr_image.c | 9 +++--- code/renderergl2/tr_main.c | 2 +- 5 files changed, 29 insertions(+), 50 deletions(-) diff --git a/code/renderergl2/glsl/pshadow_fp.glsl b/code/renderergl2/glsl/pshadow_fp.glsl index b152971af6..c196f4882b 100644 --- a/code/renderergl2/glsl/pshadow_fp.glsl +++ b/code/renderergl2/glsl/pshadow_fp.glsl @@ -8,12 +8,6 @@ uniform float u_LightRadius; varying vec3 var_Position; varying vec3 var_Normal; -float sampleDistMap(sampler2D texMap, vec2 uv, float scale) -{ - vec3 distv = texture2D(texMap, uv).xyz; - return dot(distv, vec3(1.0 / (256.0 * 256.0), 1.0 / 256.0, 1.0)) * scale; -} - void main() { vec3 lightToPos = var_Position - u_LightOrigin.xyz; @@ -57,42 +51,28 @@ void main() #endif intensity *= fade; -#if defined(USE_PCF) - float part; - - dist = sampleDistMap(u_ShadowMap, st + vec2(-1.0/512.0, -1.0/512.0), u_LightRadius); - part = max(sign(lightDist - dist), 0.0); - - dist = sampleDistMap(u_ShadowMap, st + vec2( 1.0/512.0, -1.0/512.0), u_LightRadius); - part += max(sign(lightDist - dist), 0.0); - dist = sampleDistMap(u_ShadowMap, st + vec2(-1.0/512.0, 1.0/512.0), u_LightRadius); - part += max(sign(lightDist - dist), 0.0); - - dist = sampleDistMap(u_ShadowMap, st + vec2( 1.0/512.0, 1.0/512.0), u_LightRadius); - part += max(sign(lightDist - dist), 0.0); + float part; +#if defined(USE_PCF) + part = float(texture2D(u_ShadowMap, st + vec2(-1.0/512.0, -1.0/512.0)).r != 1.0); + part += float(texture2D(u_ShadowMap, st + vec2( 1.0/512.0, -1.0/512.0)).r != 1.0); + part += float(texture2D(u_ShadowMap, st + vec2(-1.0/512.0, 1.0/512.0)).r != 1.0); + part += float(texture2D(u_ShadowMap, st + vec2( 1.0/512.0, 1.0/512.0)).r != 1.0); +#else + part = float(texture2D(u_ShadowMap, st).r != 1.0); +#endif - #if defined(USE_DISCARD) if (part <= 0.0) { discard; } - #endif +#if defined(USE_PCF) intensity *= part * 0.25; #else - dist = sampleDistMap(u_ShadowMap, st, u_LightRadius); - - #if defined(USE_DISCARD) - if (lightDist - dist <= 0.0) - { - discard; - } - #endif - - intensity *= max(sign(lightDist - dist), 0.0); + intensity *= part; #endif - + gl_FragColor.rgb = vec3(0); gl_FragColor.a = clamp(intensity, 0.0, 0.75); } diff --git a/code/renderergl2/tr_backend.c b/code/renderergl2/tr_backend.c index dc27cdddfa..39e9014545 100644 --- a/code/renderergl2/tr_backend.c +++ b/code/renderergl2/tr_backend.c @@ -1604,26 +1604,26 @@ const void *RB_PostProcess(const void *data) if (0 && r_sunlightMode->integer) { ivec4_t dstBox; - VectorSet4(dstBox, 0, 0, 128, 128); + VectorSet4(dstBox, 0, glConfig.vidHeight - 128, 128, 128); FBO_BlitFromTexture(tr.sunShadowDepthImage[0], NULL, NULL, NULL, dstBox, NULL, NULL, 0); - VectorSet4(dstBox, 128, 0, 128, 128); + VectorSet4(dstBox, 128, glConfig.vidHeight - 128, 128, 128); FBO_BlitFromTexture(tr.sunShadowDepthImage[1], NULL, NULL, NULL, dstBox, NULL, NULL, 0); - VectorSet4(dstBox, 256, 0, 128, 128); + VectorSet4(dstBox, 256, glConfig.vidHeight - 128, 128, 128); FBO_BlitFromTexture(tr.sunShadowDepthImage[2], NULL, NULL, NULL, dstBox, NULL, NULL, 0); - VectorSet4(dstBox, 384, 0, 128, 128); + VectorSet4(dstBox, 384, glConfig.vidHeight - 128, 128, 128); FBO_BlitFromTexture(tr.sunShadowDepthImage[3], NULL, NULL, NULL, dstBox, NULL, NULL, 0); } - if (0 && r_shadows->integer == 4) + if (1 && r_shadows->integer == 4) { ivec4_t dstBox; - VectorSet4(dstBox, 0, glConfig.vidHeight - 128, 128, 128); + VectorSet4(dstBox, 512 + 0, glConfig.vidHeight - 128, 128, 128); FBO_BlitFromTexture(tr.pshadowMaps[0], NULL, NULL, NULL, dstBox, NULL, NULL, 0); - VectorSet4(dstBox, 128, glConfig.vidHeight - 128, 128, 128); + VectorSet4(dstBox, 512 + 128, glConfig.vidHeight - 128, 128, 128); FBO_BlitFromTexture(tr.pshadowMaps[1], NULL, NULL, NULL, dstBox, NULL, NULL, 0); - VectorSet4(dstBox, 256, glConfig.vidHeight - 128, 128, 128); + VectorSet4(dstBox, 512 + 256, glConfig.vidHeight - 128, 128, 128); FBO_BlitFromTexture(tr.pshadowMaps[2], NULL, NULL, NULL, dstBox, NULL, NULL, 0); - VectorSet4(dstBox, 384, glConfig.vidHeight - 128, 128, 128); + VectorSet4(dstBox, 512 + 384, glConfig.vidHeight - 128, 128, 128); FBO_BlitFromTexture(tr.pshadowMaps[3], NULL, NULL, NULL, dstBox, NULL, NULL, 0); } diff --git a/code/renderergl2/tr_fbo.c b/code/renderergl2/tr_fbo.c index 54dc8fe03c..98c5b50300 100644 --- a/code/renderergl2/tr_fbo.c +++ b/code/renderergl2/tr_fbo.c @@ -322,14 +322,14 @@ void FBO_Init(void) R_CheckFBO(tr.sunRaysFbo); } - // FIXME: Don't use separate color/depth buffers for a shadow buffer if (MAX_DRAWN_PSHADOWS && tr.pshadowMaps[0]) { for( i = 0; i < MAX_DRAWN_PSHADOWS; i++) { tr.pshadowFbos[i] = FBO_Create(va("_shadowmap%d", i), tr.pshadowMaps[i]->width, tr.pshadowMaps[i]->height); - FBO_AttachImage(tr.pshadowFbos[i], tr.pshadowMaps[i], GL_COLOR_ATTACHMENT0, 0); - FBO_CreateBuffer(tr.pshadowFbos[i], GL_DEPTH_COMPONENT24_ARB, 0, 0); + // FIXME: this next line wastes 16mb with 16x512x512 sun shadow maps, skip if OpenGL 4.3+ or ARB_framebuffer_no_attachments + FBO_CreateBuffer(tr.pshadowFbos[i], GL_RGBA8, 0, 0); + FBO_AttachImage(tr.pshadowFbos[i], tr.pshadowMaps[i], GL_DEPTH_ATTACHMENT, 0); R_CheckFBO(tr.pshadowFbos[i]); } } diff --git a/code/renderergl2/tr_image.c b/code/renderergl2/tr_image.c index ae15abcb22..169343dc9f 100644 --- a/code/renderergl2/tr_image.c +++ b/code/renderergl2/tr_image.c @@ -2808,12 +2808,11 @@ void R_CreateBuiltinImages( void ) { tr.screenSsaoImage = R_CreateImage("*screenSsao", NULL, width / 2, height / 2, IMGTYPE_COLORALPHA, IMGFLAG_NO_COMPRESSION | IMGFLAG_CLAMPTOEDGE, GL_RGBA8); } - if (r_shadows->integer == 4) + for( x = 0; x < MAX_DRAWN_PSHADOWS; x++) { - for( x = 0; x < MAX_DRAWN_PSHADOWS; x++) - { - tr.pshadowMaps[x] = R_CreateImage(va("*shadowmap%i", x), NULL, PSHADOW_MAP_SIZE, PSHADOW_MAP_SIZE, IMGTYPE_COLORALPHA, IMGFLAG_NO_COMPRESSION | IMGFLAG_CLAMPTOEDGE, GL_RGBA8); - } + tr.pshadowMaps[x] = R_CreateImage(va("*shadowmap%i", x), NULL, PSHADOW_MAP_SIZE, PSHADOW_MAP_SIZE, IMGTYPE_COLORALPHA, IMGFLAG_NO_COMPRESSION | IMGFLAG_CLAMPTOEDGE, GL_DEPTH_COMPONENT24); + //qglTextureParameterfEXT(tr.pshadowMaps[x]->texnum, GL_TEXTURE_2D, GL_TEXTURE_COMPARE_MODE, GL_COMPARE_R_TO_TEXTURE); + //qglTextureParameterfEXT(tr.pshadowMaps[x]->texnum, GL_TEXTURE_2D, GL_TEXTURE_COMPARE_FUNC, GL_LEQUAL); } if (r_sunlightMode->integer) diff --git a/code/renderergl2/tr_main.c b/code/renderergl2/tr_main.c index cc33c7199f..1f66d90bb9 100644 --- a/code/renderergl2/tr_main.c +++ b/code/renderergl2/tr_main.c @@ -2098,7 +2098,7 @@ void R_RenderPshadowMaps(const refdef_t *fd) if (glRefConfig.framebufferObject) shadowParms.targetFbo = tr.pshadowFbos[i]; - shadowParms.flags = VPF_SHADOWMAP | VPF_DEPTHSHADOW | VPF_NOVIEWMODEL; + shadowParms.flags = VPF_DEPTHSHADOW | VPF_NOVIEWMODEL; shadowParms.zFar = shadow->lightRadius; VectorCopy(shadow->lightOrigin, shadowParms.or.origin); From 786f6fc525b7cd3e76dd721396566de32f4a0085 Mon Sep 17 00:00:00 2001 From: SmileTheory Date: Fri, 14 Jul 2017 16:26:41 -0700 Subject: [PATCH 126/240] whoops, left a bit of debugging enabled --- code/renderergl2/tr_backend.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/code/renderergl2/tr_backend.c b/code/renderergl2/tr_backend.c index 39e9014545..d6ff122649 100644 --- a/code/renderergl2/tr_backend.c +++ b/code/renderergl2/tr_backend.c @@ -1614,7 +1614,7 @@ const void *RB_PostProcess(const void *data) FBO_BlitFromTexture(tr.sunShadowDepthImage[3], NULL, NULL, NULL, dstBox, NULL, NULL, 0); } - if (1 && r_shadows->integer == 4) + if (0 && r_shadows->integer == 4) { ivec4_t dstBox; VectorSet4(dstBox, 512 + 0, glConfig.vidHeight - 128, 128, 128); From c8db6c55e5f2dbf9ee908e6d09bb706281bf5c57 Mon Sep 17 00:00:00 2001 From: Zack Middleton Date: Fri, 14 Jul 2017 15:43:44 -0500 Subject: [PATCH 127/240] Fix score bonus for defending the flag carrier in CTF Fix copy-paste error in the original Quake 3 code. The wrong values are used for v1 and v2. v2 was previously set to distance of attacker to flag base; which should be handled already. The game now gives defense score bonus to player when they frag an enemy near their team's flag carrier while the player is more than 1000 units from the flag carrier. This may also fix not giving defense bonus when near carrier due to checking if carrier and enemy (instead of attacker) are in PVS. Found by @Razish. --- code/game/g_team.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/code/game/g_team.c b/code/game/g_team.c index 7e0e90b969..e736a4a419 100644 --- a/code/game/g_team.c +++ b/code/game/g_team.c @@ -462,7 +462,7 @@ void Team_FragBonuses(gentity_t *targ, gentity_t *inflictor, gentity_t *attacker if (carrier && carrier != attacker) { VectorSubtract(targ->r.currentOrigin, carrier->r.currentOrigin, v1); - VectorSubtract(attacker->r.currentOrigin, carrier->r.currentOrigin, v1); + VectorSubtract(attacker->r.currentOrigin, carrier->r.currentOrigin, v2); if ( ( ( VectorLength(v1) < CTF_ATTACKER_PROTECT_RADIUS && trap_InPVS(carrier->r.currentOrigin, targ->r.currentOrigin ) ) || From 13831f9569f08b18e6762eeab0ae39c753ed2f2f Mon Sep 17 00:00:00 2001 From: Zack Middleton Date: Fri, 14 Jul 2017 17:06:22 -0500 Subject: [PATCH 128/240] Restore not giving defense score bonus to flag carrier The Team Arena code for giving defense bonus for fragging player who recently damaged a skull carrier unintentionally applied to the flag carrier. The skull carrier case would of been handled by the flag carrier block above it. However, Harvest mode doesn't call Team_CheckHurtCarrier() so the skull carrier defense bonus does not work. This restores the pre-Team Arena behavior of not giving defense score bonus to flag carrier. --- code/game/g_team.c | 17 ----------------- 1 file changed, 17 deletions(-) diff --git a/code/game/g_team.c b/code/game/g_team.c index e736a4a419..de81e52f10 100644 --- a/code/game/g_team.c +++ b/code/game/g_team.c @@ -365,23 +365,6 @@ void Team_FragBonuses(gentity_t *targ, gentity_t *inflictor, gentity_t *attacker return; } - if (targ->client->pers.teamState.lasthurtcarrier && - level.time - targ->client->pers.teamState.lasthurtcarrier < CTF_CARRIER_DANGER_PROTECT_TIMEOUT) { - // attacker is on the same team as the skull carrier and - AddScore(attacker, targ->r.currentOrigin, CTF_CARRIER_DANGER_PROTECT_BONUS); - - attacker->client->pers.teamState.carrierdefense++; - targ->client->pers.teamState.lasthurtcarrier = 0; - - attacker->client->ps.persistant[PERS_DEFEND_COUNT]++; - // add the sprite over the player's head - attacker->client->ps.eFlags &= ~(EF_AWARD_IMPRESSIVE | EF_AWARD_EXCELLENT | EF_AWARD_GAUNTLET | EF_AWARD_ASSIST | EF_AWARD_DEFEND | EF_AWARD_CAP ); - attacker->client->ps.eFlags |= EF_AWARD_DEFEND; - attacker->client->rewardTime = level.time + REWARD_SPRITE_TIME; - - return; - } - // flag and flag carrier area defense bonuses // we have to find the flag and carrier entities From 5f2e4a03c56f916a32c0364ca71c0ef9e004299a Mon Sep 17 00:00:00 2001 From: Zack Middleton Date: Fri, 14 Jul 2017 17:38:22 -0500 Subject: [PATCH 129/240] Add score bonus for defending the flag carrier in 1 Flag CTF Set flag_pw to neutral flag instead of using CTF value for red or blue flag so that flag carrier is detected correctly. --- code/game/g_team.c | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/code/game/g_team.c b/code/game/g_team.c index de81e52f10..0f5a34c5f2 100644 --- a/code/game/g_team.c +++ b/code/game/g_team.c @@ -300,6 +300,7 @@ void Team_FragBonuses(gentity_t *targ, gentity_t *inflictor, gentity_t *attacker #ifdef MISSIONPACK if (g_gametype.integer == GT_1FCTF) { + flag_pw = PW_NEUTRALFLAG; enemy_flag_pw = PW_NEUTRALFLAG; } #endif @@ -486,6 +487,12 @@ void Team_CheckHurtCarrier(gentity_t *targ, gentity_t *attacker) else flag_pw = PW_REDFLAG; +#ifdef MISSIONPACK + if (g_gametype.integer == GT_1FCTF) { + flag_pw = PW_NEUTRALFLAG; + } +#endif + // flags if (targ->client->ps.powerups[flag_pw] && targ->client->sess.sessionTeam != attacker->client->sess.sessionTeam) From fa1549d4576c562600f16b0fdd1300812727fadd Mon Sep 17 00:00:00 2001 From: Noah Metzger Date: Fri, 14 Jul 2017 08:23:38 -0500 Subject: [PATCH 130/240] Move CON_Init ahead of Com_Init to avoid Windows dedicated server crash --- code/sys/sys_main.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/code/sys/sys_main.c b/code/sys/sys_main.c index fc1ff4ad30..586e37a137 100644 --- a/code/sys/sys_main.c +++ b/code/sys/sys_main.c @@ -744,11 +744,10 @@ int main( int argc, char **argv ) Q_strcat( commandLine, sizeof( commandLine ), " " ); } + CON_Init( ); Com_Init( commandLine ); NET_Init( ); - CON_Init( ); - signal( SIGILL, Sys_SigHandler ); signal( SIGFPE, Sys_SigHandler ); signal( SIGSEGV, Sys_SigHandler ); From 9f239d647b5e7d21e437ad6f012af436b30bb456 Mon Sep 17 00:00:00 2001 From: Zack Middleton Date: Thu, 20 Jul 2017 15:39:51 -0500 Subject: [PATCH 131/240] Make 'globalservers 0' fetch all masters This commit fixes the vanilla Q3 UI VMs not displaying a server list when id Software's master server is down. Originally master 0 for the globalservers command was Internet and master 1 was MPlayer (defunct). In 2008 ioquake3 changed it so that master 0 to 4 were five separate master servers with no affect on original Quake3/Team Arena UI VMs; they continued to get the server list from master.quake3arena.com. id Software's master server (master.quake3arena.com) goes down occasionally. Using ioq3's UI VM additional master servers can be accessed but players using the original UI VMs are unable to get a server list. In order to fix the original UI VMs in Quake3/Team Arena's pk3s this commit makes 'globalservers 0' fetch all master servers. So players get a combined list of id Software's and ioquake3's master list. Or just ioquake3's list if id Software's master is down. Getting lists from individual master servers using globalservers has changed from 0 through 4 to 1 through 5 to accommodate using 0 for other purposes. This commit modifies ioq3's UI code to support the new values for globalservers command. A side affect of these changes is that UI VMs based on ioq3 since 2008 will have Internet1 fetch all master servers and Internet2 request sv_master1 instead of sv_master2 and so on. It may be worth noting that getting server list from masters 3-5 could not be done using ioq3's UI before 2011. --- code/client/cl_main.c | 37 ++++++++++++++++++++++++++++----- code/q3_ui/ui_servers2.c | 44 ++++++++++++++++++++++------------------ code/ui/ui_main.c | 35 +++++++++++++++++--------------- 3 files changed, 75 insertions(+), 41 deletions(-) diff --git a/code/client/cl_main.c b/code/client/cl_main.c index b0712a3dbd..506940b14e 100644 --- a/code/client/cl_main.c +++ b/code/client/cl_main.c @@ -2471,7 +2471,7 @@ void CL_ServersResponsePacket( const netadr_t* from, msg_t *msg, qboolean extend byte* buffptr; byte* buffend; - Com_Printf("CL_ServersResponsePacket\n"); + Com_Printf("CL_ServersResponsePacket from %s\n", NET_AdrToStringwPort(*from)); if (cls.numglobalservers == -1) { // state to detect lack of servers or lack of response @@ -4165,6 +4165,10 @@ void CL_LocalServers_f( void ) { /* ================== CL_GlobalServers_f + +Originally master 0 was Internet and master 1 was MPlayer. +ioquake3 2008; added support for requesting five separate master servers using 0-4. +ioquake3 2017; made master 0 fetch all master servers and 1-5 request a single master server. ================== */ void CL_GlobalServers_f( void ) { @@ -4172,13 +4176,36 @@ void CL_GlobalServers_f( void ) { int count, i, masterNum; char command[1024], *masteraddress; - if ((count = Cmd_Argc()) < 3 || (masterNum = atoi(Cmd_Argv(1))) < 0 || masterNum > MAX_MASTER_SERVERS - 1) + if ((count = Cmd_Argc()) < 3 || (masterNum = atoi(Cmd_Argv(1))) < 0 || masterNum > MAX_MASTER_SERVERS) { - Com_Printf("usage: globalservers [keywords]\n", MAX_MASTER_SERVERS - 1); + Com_Printf("usage: globalservers [keywords]\n", MAX_MASTER_SERVERS); return; } - sprintf(command, "sv_master%d", masterNum + 1); + // request from all master servers + if ( masterNum == 0 ) { + int numAddress = 0; + + for ( i = 1; i <= MAX_MASTER_SERVERS; i++ ) { + sprintf(command, "sv_master%d", i); + masteraddress = Cvar_VariableString(command); + + if(!*masteraddress) + continue; + + numAddress++; + + Com_sprintf(command, sizeof(command), "globalservers %d %s %s\n", i, Cmd_Argv(2), Cmd_ArgsFrom(3)); + Cbuf_AddText(command); + } + + if ( !numAddress ) { + Com_Printf( "CL_GlobalServers_f: Error: No master server addresses.\n"); + } + return; + } + + sprintf(command, "sv_master%d", masterNum); masteraddress = Cvar_VariableString(command); if(!*masteraddress) @@ -4200,7 +4227,7 @@ void CL_GlobalServers_f( void ) { else if(i == 2) to.port = BigShort(PORT_MASTER); - Com_Printf("Requesting servers from master %s...\n", masteraddress); + Com_Printf("Requesting servers from %s (%s)...\n", masteraddress, NET_AdrToStringwPort(to)); cls.numglobalservers = -1; cls.pingUpdateSource = AS_GLOBAL; diff --git a/code/q3_ui/ui_servers2.c b/code/q3_ui/ui_servers2.c index 483080534f..93e4697ba1 100644 --- a/code/q3_ui/ui_servers2.c +++ b/code/q3_ui/ui_servers2.c @@ -81,14 +81,15 @@ MULTIPLAYER MENU (SERVER BROWSER) #define GR_LETTERS 31 #define UIAS_LOCAL 0 -#define UIAS_GLOBAL1 1 -#define UIAS_GLOBAL2 2 -#define UIAS_GLOBAL3 3 -#define UIAS_GLOBAL4 4 -#define UIAS_GLOBAL5 5 -#define UIAS_FAVORITES 6 +#define UIAS_GLOBAL0 1 +#define UIAS_GLOBAL1 2 +#define UIAS_GLOBAL2 3 +#define UIAS_GLOBAL3 4 +#define UIAS_GLOBAL4 5 +#define UIAS_GLOBAL5 6 +#define UIAS_FAVORITES 7 -#define UI_MAX_MASTER_SERVERS 5 +#define UI_MAX_MASTER_SERVERS 6 #define SORT_HOST 0 #define SORT_MAP 1 @@ -104,11 +105,12 @@ MULTIPLAYER MENU (SERVER BROWSER) static const char *master_items[] = { "Local", - "Internet1", - "Internet2", - "Internet3", - "Internet4", - "Internet5", + "Internet", + "Master1", + "Master2", + "Master3", + "Master4", + "Master5", "Favorites", NULL }; @@ -347,6 +349,7 @@ int ArenaServers_SourceForLAN(void) { default: case UIAS_LOCAL: return AS_LOCAL; + case UIAS_GLOBAL0: case UIAS_GLOBAL1: case UIAS_GLOBAL2: case UIAS_GLOBAL3: @@ -431,7 +434,7 @@ static void ArenaServers_UpdateMenu( void ) { g_arenaservers.punkbuster.generic.flags &= ~QMF_GRAYED; // update status bar - if( g_servertype >= UIAS_GLOBAL1 && g_servertype <= UIAS_GLOBAL5 ) { + if( g_servertype >= UIAS_GLOBAL0 && g_servertype <= UIAS_GLOBAL5 ) { g_arenaservers.statusbar.string = quake3worldMessage; } else { @@ -465,7 +468,7 @@ static void ArenaServers_UpdateMenu( void ) { } // update status bar - if( g_servertype >= UIAS_GLOBAL1 && g_servertype <= UIAS_GLOBAL5 ) { + if( g_servertype >= UIAS_GLOBAL0 && g_servertype <= UIAS_GLOBAL5 ) { g_arenaservers.statusbar.string = quake3worldMessage; } else { @@ -1010,7 +1013,7 @@ static void ArenaServers_StartRefresh( void ) return; } - if( g_servertype >= UIAS_GLOBAL1 && g_servertype <= UIAS_GLOBAL5 ) { + if( g_servertype >= UIAS_GLOBAL0 && g_servertype <= UIAS_GLOBAL5 ) { switch( g_arenaservers.gametype.curvalue ) { default: case GAMES_ALL: @@ -1046,10 +1049,10 @@ static void ArenaServers_StartRefresh( void ) protocol[0] = '\0'; trap_Cvar_VariableStringBuffer( "debug_protocol", protocol, sizeof(protocol) ); if (strlen(protocol)) { - trap_Cmd_ExecuteText( EXEC_APPEND, va( "globalservers %d %s%s\n", g_servertype - UIAS_GLOBAL1, protocol, myargs )); + trap_Cmd_ExecuteText( EXEC_APPEND, va( "globalservers %d %s%s\n", g_servertype - UIAS_GLOBAL0, protocol, myargs )); } else { - trap_Cmd_ExecuteText( EXEC_APPEND, va( "globalservers %d %d%s\n", g_servertype - UIAS_GLOBAL1, (int)trap_Cvar_VariableValue( "protocol" ), myargs ) ); + trap_Cmd_ExecuteText( EXEC_APPEND, va( "globalservers %d %d%s\n", g_servertype - UIAS_GLOBAL0, (int)trap_Cvar_VariableValue( "protocol" ), myargs ) ); } } } @@ -1102,7 +1105,7 @@ int ArenaServers_SetType( int type ) while(type <= UIAS_GLOBAL5) { - Com_sprintf(cvarname, sizeof(cvarname), "sv_master%d", type); + Com_sprintf(cvarname, sizeof(cvarname), "sv_master%d", type - UIAS_GLOBAL0); trap_Cvar_VariableStringBuffer(cvarname, masterstr, sizeof(masterstr)); if(*masterstr) break; @@ -1122,14 +1125,15 @@ int ArenaServers_SetType( int type ) g_arenaservers.maxservers = MAX_LOCALSERVERS; break; + case UIAS_GLOBAL0: case UIAS_GLOBAL1: case UIAS_GLOBAL2: case UIAS_GLOBAL3: case UIAS_GLOBAL4: case UIAS_GLOBAL5: g_arenaservers.remove.generic.flags |= (QMF_INACTIVE|QMF_HIDDEN); - g_arenaservers.serverlist = g_globalserverlist[type-UIAS_GLOBAL1]; - g_arenaservers.numservers = &g_numglobalservers[type-UIAS_GLOBAL1]; + g_arenaservers.serverlist = g_globalserverlist[type-UIAS_GLOBAL0]; + g_arenaservers.numservers = &g_numglobalservers[type-UIAS_GLOBAL0]; g_arenaservers.maxservers = MAX_GLOBALSERVERS; break; diff --git a/code/ui/ui_main.c b/code/ui/ui_main.c index 81b324b553..73d3f60a8e 100644 --- a/code/ui/ui_main.c +++ b/code/ui/ui_main.c @@ -55,20 +55,22 @@ static const int numSkillLevels = ARRAY_LEN( skillLevels ); #define UIAS_LOCAL 0 -#define UIAS_GLOBAL1 1 -#define UIAS_GLOBAL2 2 -#define UIAS_GLOBAL3 3 -#define UIAS_GLOBAL4 4 -#define UIAS_GLOBAL5 5 -#define UIAS_FAVORITES 6 +#define UIAS_GLOBAL0 1 +#define UIAS_GLOBAL1 2 +#define UIAS_GLOBAL2 3 +#define UIAS_GLOBAL3 4 +#define UIAS_GLOBAL4 5 +#define UIAS_GLOBAL5 6 +#define UIAS_FAVORITES 7 static const char *netSources[] = { "Local", - "Internet1", - "Internet2", - "Internet3", - "Internet4", - "Internet5", + "Internet", + "Master1", + "Master2", + "Master3", + "Master4", + "Master5", "Favorites" }; static const int numNetSources = ARRAY_LEN( netSources ); @@ -999,6 +1001,7 @@ int UI_SourceForLAN(void) { default: case UIAS_LOCAL: return AS_LOCAL; + case UIAS_GLOBAL0: case UIAS_GLOBAL1: case UIAS_GLOBAL2: case UIAS_GLOBAL3: @@ -2484,7 +2487,7 @@ static qboolean UI_NetSource_HandleKey(int flags, float *special, int key) { while(ui_netSource.integer >= UIAS_GLOBAL1 && ui_netSource.integer <= UIAS_GLOBAL5) { - Com_sprintf(cvarname, sizeof(cvarname), "sv_master%d", ui_netSource.integer); + Com_sprintf(cvarname, sizeof(cvarname), "sv_master%d", ui_netSource.integer - UIAS_GLOBAL0); trap_Cvar_VariableStringBuffer(cvarname, masterstr, sizeof(masterstr)); if(*masterstr) break; @@ -6008,7 +6011,7 @@ static void UI_StartServerRefresh(qboolean full, qboolean force) // This function is called with force=qfalse when server browser menu opens or net source changes. // Automatically update local and favorite servers. // Only auto update master server list if there is no server info cache. - if ( !force && ( ui_netSource.integer >= UIAS_GLOBAL1 && ui_netSource.integer <= UIAS_GLOBAL5 ) ) { + if ( !force && ( ui_netSource.integer >= UIAS_GLOBAL0 && ui_netSource.integer <= UIAS_GLOBAL5 ) ) { if ( trap_LAN_GetServerCount( UI_SourceForLAN() ) > 0 ) { return; // have cached list } @@ -6041,14 +6044,14 @@ static void UI_StartServerRefresh(qboolean full, qboolean force) } uiInfo.serverStatus.refreshtime = uiInfo.uiDC.realTime + 5000; - if( ui_netSource.integer >= UIAS_GLOBAL1 && ui_netSource.integer <= UIAS_GLOBAL5 ) { + if( ui_netSource.integer >= UIAS_GLOBAL0 && ui_netSource.integer <= UIAS_GLOBAL5 ) { ptr = UI_Cvar_VariableString("debug_protocol"); if (strlen(ptr)) { - trap_Cmd_ExecuteText( EXEC_NOW, va( "globalservers %d %s full empty\n", ui_netSource.integer - UIAS_GLOBAL1, ptr ) ); + trap_Cmd_ExecuteText( EXEC_NOW, va( "globalservers %d %s full empty\n", ui_netSource.integer - UIAS_GLOBAL0, ptr ) ); } else { - trap_Cmd_ExecuteText( EXEC_NOW, va( "globalservers %d %d full empty\n", ui_netSource.integer - UIAS_GLOBAL1, (int)trap_Cvar_VariableValue( "protocol" ) ) ); + trap_Cmd_ExecuteText( EXEC_NOW, va( "globalservers %d %d full empty\n", ui_netSource.integer - UIAS_GLOBAL0, (int)trap_Cvar_VariableValue( "protocol" ) ) ); } } } From 67dace6c209c91fac5861846364e62c1c67aab77 Mon Sep 17 00:00:00 2001 From: Zack Middleton Date: Thu, 20 Jul 2017 22:37:36 -0500 Subject: [PATCH 132/240] Escape quotes for sv_dlURL in shell so it doesn't become "http:" q3ded +set sv_dlURL "http://example.org" The shell removes the quotes but makes the content be a single argument for progam args. Quake 3 concatenates all the program args and splits lines at + or newlines. Then Quake 3 parses them using a tokenizer that skips unquoted C comments beginning with //. This results in the cvar being set to "http:". Escape the quotes so they are passed to the program and the tokenizer knows not to skip C comments. q3ded +set sv_dlURL \"http://example.org\" --- misc/linux/start_server.sh | 8 ++++++-- misc/osx/start_server.sh | 8 ++++++-- 2 files changed, 12 insertions(+), 4 deletions(-) diff --git a/misc/linux/start_server.sh b/misc/linux/start_server.sh index da980072ad..c68292e0f1 100644 --- a/misc/linux/start_server.sh +++ b/misc/linux/start_server.sh @@ -1,3 +1,7 @@ #!/bin/sh -echo "Edit this script to change the path to ioquake3's dedicated server executable and which binary if you aren't on x86_64.\n Set the sv_dlURL setting to a url like http://yoursite.com/ioquake3_path for ioquake3 clients to download extra data" -~/ioquake3/ioq3ded.x86_64 +set dedicated 2 +set sv_allowDownload 1 +set sv_dlURL "" +set com_hunkmegs 64 "$@" +echo "Edit this script to change the path to ioquake3's dedicated server executable and which binary if you aren't on x86_64." +echo "Set the sv_dlURL setting to a url like http://yoursite.com/ioquake3_path for ioquake3 clients to download extra data." + +# sv_dlURL needs to have quotes escaped like \"http://yoursite.com/ioquake3_path\" or it will be set to "http:" in-game. + +~/ioquake3/ioq3ded.x86_64 +set dedicated 2 +set sv_allowDownload 1 +set sv_dlURL \"\" +set com_hunkmegs 64 "$@" diff --git a/misc/osx/start_server.sh b/misc/osx/start_server.sh index 66a295ffd1..d9154b2fbc 100644 --- a/misc/osx/start_server.sh +++ b/misc/osx/start_server.sh @@ -1,3 +1,7 @@ #!/bin/sh -echo "Edit this script to change the path to ioquake3's dedicated server executable.\n Set the sv_dlURL setting to a url like http://yoursite.com/ioquake3_path for ioquake3 clients to download extra data" -/Applications/ioquake3/ioquake3.app/Contents/MacOS/ioq3ded +set dedicated 2 +set sv_allowDownload 1 +set sv_dlURL "" +set com_hunkmegs 64 "$@" +echo "Edit this script to change the path to ioquake3's dedicated server executable." +echo "Set the sv_dlURL setting to a url like http://yoursite.com/ioquake3_path for ioquake3 clients to download extra data." + +# sv_dlURL needs to have quotes escaped like \"http://yoursite.com/ioquake3_path\" or it will be set to "http:" in-game. + +/Applications/ioquake3/ioquake3.app/Contents/MacOS/ioq3ded +set dedicated 2 +set sv_allowDownload 1 +set sv_dlURL \"\" +set com_hunkmegs 64 "$@" From 356ae10ef65d4401958d50f03288dcb22d957c96 Mon Sep 17 00:00:00 2001 From: SmileTheory Date: Mon, 24 Jul 2017 16:29:04 -0700 Subject: [PATCH 133/240] OpenGL2: Don't use initialized arrays in glsl shaders. Unsupported in GLSL 1.20 (Mac OS X 10.6) --- code/renderergl2/glsl/bokeh_fp.glsl | 18 ++++++++++++++++-- code/renderergl2/glsl/depthblur_fp.glsl | 8 +++++++- code/renderergl2/glsl/ssao_fp.glsl | 15 +++++++++++++++ 3 files changed, 38 insertions(+), 3 deletions(-) diff --git a/code/renderergl2/glsl/bokeh_fp.glsl b/code/renderergl2/glsl/bokeh_fp.glsl index d08816aeff..d2ec5b4b36 100644 --- a/code/renderergl2/glsl/bokeh_fp.glsl +++ b/code/renderergl2/glsl/bokeh_fp.glsl @@ -11,7 +11,15 @@ void main() vec2 tc; #if 0 - float c[7] = float[7](1.0, 0.9659258263, 0.8660254038, 0.7071067812, 0.5, 0.2588190451, 0.0); + float c[7]; + + c[0] = 1.0; + c[1] = 0.9659258263; + c[2] = 0.8660254038; + c[3] = 0.7071067812; + c[4] = 0.5; + c[5] = 0.2588190451; + c[6] = 0.0; tc = var_TexCoords + u_InvTexRes * vec2( c[0], c[6]); color = texture2D(u_TextureMap, tc); tc = var_TexCoords + u_InvTexRes * vec2( c[1], c[5]); color += texture2D(u_TextureMap, tc); @@ -44,7 +52,13 @@ void main() gl_FragColor = color * 0.04166667 * u_Color; #endif - float c[5] = float[5](1.0, 0.9238795325, 0.7071067812, 0.3826834324, 0.0); + float c[5]; + + c[0] = 1.0; + c[1] = 0.9238795325; + c[2] = 0.7071067812; + c[3] = 0.3826834324; + c[4] = 0.0; tc = var_TexCoords + u_InvTexRes * vec2( c[0], c[4]); color = texture2D(u_TextureMap, tc); tc = var_TexCoords + u_InvTexRes * vec2( c[1], c[3]); color += texture2D(u_TextureMap, tc); diff --git a/code/renderergl2/glsl/depthblur_fp.glsl b/code/renderergl2/glsl/depthblur_fp.glsl index d71b3487ed..d63df88a6a 100644 --- a/code/renderergl2/glsl/depthblur_fp.glsl +++ b/code/renderergl2/glsl/depthblur_fp.glsl @@ -6,7 +6,7 @@ varying vec2 var_ScreenTex; //float gauss[8] = float[8](0.17, 0.17, 0.16, 0.14, 0.12, 0.1, 0.08, 0.06); //float gauss[5] = float[5](0.30, 0.23, 0.097, 0.024, 0.0033); -float gauss[4] = float[4](0.40, 0.24, 0.054, 0.0044); +//float gauss[4] = float[4](0.40, 0.24, 0.054, 0.0044); //float gauss[3] = float[3](0.60, 0.19, 0.0066); #define BLUR_SIZE 4 @@ -22,6 +22,12 @@ float getLinearDepth(sampler2D depthMap, const vec2 tex, const float zFarDivZNea vec4 depthGaussian1D(sampler2D imageMap, sampler2D depthMap, vec2 tex, float zFarDivZNear, float zFar, vec2 scale) { + float gauss[4]; + + gauss[0] = 0.40; + gauss[1] = 0.24; + gauss[2] = 0.054; + gauss[3] = 0.0044; #if defined(USE_DEPTH) float depthCenter = getLinearDepth(depthMap, tex, zFarDivZNear); diff --git a/code/renderergl2/glsl/ssao_fp.glsl b/code/renderergl2/glsl/ssao_fp.glsl index 93f61859d2..d4c430638b 100644 --- a/code/renderergl2/glsl/ssao_fp.glsl +++ b/code/renderergl2/glsl/ssao_fp.glsl @@ -4,6 +4,7 @@ uniform vec4 u_ViewInfo; // zfar / znear, zfar, 1/width, 1/height varying vec2 var_ScreenTex; +#if 0 vec2 poissonDisc[9] = vec2[9]( vec2(-0.7055767, 0.196515), vec2(0.3524343, -0.7791386), vec2(0.2391056, 0.9189604), vec2(-0.07580382, -0.09224417), @@ -11,6 +12,8 @@ vec2(0.5784913, -0.002528916), vec2(0.192888, 0.4064181), vec2(-0.6335801, -0.5247476), vec2(-0.5579782, 0.7491854), vec2(0.7320465, 0.6317794) ); +#endif + #define NUM_SAMPLES 3 // Input: It uses texture coords as the random number seed. @@ -46,6 +49,18 @@ float getLinearDepth(sampler2D depthMap, const vec2 tex, const float zFarDivZNea float ambientOcclusion(sampler2D depthMap, const vec2 tex, const float zFarDivZNear, const float zFar, const vec2 scale) { + vec2 poissonDisc[9]; + + poissonDisc[0] = vec2(-0.7055767, 0.196515); + poissonDisc[1] = vec2(0.3524343, -0.7791386); + poissonDisc[2] = vec2(0.2391056, 0.9189604); + poissonDisc[3] = vec2(-0.07580382, -0.09224417); + poissonDisc[4] = vec2(0.5784913, -0.002528916); + poissonDisc[5] = vec2(0.192888, 0.4064181); + poissonDisc[6] = vec2(-0.6335801, -0.5247476); + poissonDisc[7] = vec2(-0.5579782, 0.7491854); + poissonDisc[8] = vec2(0.7320465, 0.6317794); + float result = 0; float sampleZ = getLinearDepth(depthMap, tex, zFarDivZNear); From fa034c17ecb85e5b1574f102755131fa9d6460e3 Mon Sep 17 00:00:00 2001 From: Zack Middleton Date: Mon, 24 Jul 2017 20:55:14 -0500 Subject: [PATCH 134/240] Echo server say/tell/sayto message to console --- code/server/sv_ccmds.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/code/server/sv_ccmds.c b/code/server/sv_ccmds.c index 5f26b8532b..2ba8194da6 100644 --- a/code/server/sv_ccmds.c +++ b/code/server/sv_ccmds.c @@ -1260,6 +1260,7 @@ static void SV_ConSay_f(void) { strcat(text, p); + Com_Printf("%s\n", text); SV_SendServerCommand(NULL, "chat \"%s\"", text); } @@ -1299,6 +1300,7 @@ static void SV_ConTell_f(void) { strcat(text, p); + Com_Printf("%s\n", text); SV_SendServerCommand(cl, "chat \"%s\"", text); } @@ -1364,6 +1366,7 @@ static void SV_ConSayto_f(void) { strcat(text, p); + Com_Printf("%s\n", text); SV_SendServerCommand(saytocl, "chat \"%s\"", text); } From 6693465336d203432b4e02e76fd91aabe7a159a0 Mon Sep 17 00:00:00 2001 From: Zack Middleton Date: Thu, 27 Jul 2017 18:58:43 -0500 Subject: [PATCH 135/240] Don't try to remove non-existant command 'shaderstate' Remove commands in the order they are added because the different orders is annoying. --- code/renderergl1/tr_init.c | 17 ++++++++--------- code/renderergl2/tr_init.c | 17 ++++++++--------- 2 files changed, 16 insertions(+), 18 deletions(-) diff --git a/code/renderergl1/tr_init.c b/code/renderergl1/tr_init.c index 266a2f249a..11278df7f9 100644 --- a/code/renderergl1/tr_init.c +++ b/code/renderergl1/tr_init.c @@ -1259,16 +1259,15 @@ void RE_Shutdown( qboolean destroyWindow ) { ri.Printf( PRINT_ALL, "RE_Shutdown( %i )\n", destroyWindow ); - ri.Cmd_RemoveCommand ("modellist"); - ri.Cmd_RemoveCommand ("screenshotJPEG"); - ri.Cmd_RemoveCommand ("screenshot"); - ri.Cmd_RemoveCommand ("imagelist"); - ri.Cmd_RemoveCommand ("shaderlist"); - ri.Cmd_RemoveCommand ("skinlist"); - ri.Cmd_RemoveCommand ("gfxinfo"); - ri.Cmd_RemoveCommand("minimize"); + ri.Cmd_RemoveCommand( "imagelist" ); + ri.Cmd_RemoveCommand( "shaderlist" ); + ri.Cmd_RemoveCommand( "skinlist" ); + ri.Cmd_RemoveCommand( "modellist" ); ri.Cmd_RemoveCommand( "modelist" ); - ri.Cmd_RemoveCommand( "shaderstate" ); + ri.Cmd_RemoveCommand( "screenshot" ); + ri.Cmd_RemoveCommand( "screenshotJPEG" ); + ri.Cmd_RemoveCommand( "gfxinfo" ); + ri.Cmd_RemoveCommand( "minimize" ); if ( tr.registered ) { diff --git a/code/renderergl2/tr_init.c b/code/renderergl2/tr_init.c index fe1014ba09..583a04b639 100644 --- a/code/renderergl2/tr_init.c +++ b/code/renderergl2/tr_init.c @@ -1501,16 +1501,15 @@ void RE_Shutdown( qboolean destroyWindow ) { ri.Printf( PRINT_ALL, "RE_Shutdown( %i )\n", destroyWindow ); - ri.Cmd_RemoveCommand ("modellist"); - ri.Cmd_RemoveCommand ("screenshotJPEG"); - ri.Cmd_RemoveCommand ("screenshot"); - ri.Cmd_RemoveCommand ("imagelist"); - ri.Cmd_RemoveCommand ("shaderlist"); - ri.Cmd_RemoveCommand ("skinlist"); - ri.Cmd_RemoveCommand ("gfxinfo"); - ri.Cmd_RemoveCommand("minimize"); + ri.Cmd_RemoveCommand( "imagelist" ); + ri.Cmd_RemoveCommand( "shaderlist" ); + ri.Cmd_RemoveCommand( "skinlist" ); + ri.Cmd_RemoveCommand( "modellist" ); ri.Cmd_RemoveCommand( "modelist" ); - ri.Cmd_RemoveCommand( "shaderstate" ); + ri.Cmd_RemoveCommand( "screenshot" ); + ri.Cmd_RemoveCommand( "screenshotJPEG" ); + ri.Cmd_RemoveCommand( "gfxinfo" ); + ri.Cmd_RemoveCommand( "minimize" ); ri.Cmd_RemoveCommand( "gfxmeminfo" ); ri.Cmd_RemoveCommand( "exportCubemaps" ); From e07ffa715cbb2dabd8ad22da2d3e1996fedcc328 Mon Sep 17 00:00:00 2001 From: MAN-AT-ARMS Date: Fri, 28 Jul 2017 09:59:43 -0400 Subject: [PATCH 136/240] Update internal curl to 7.54.0 --- Makefile | 4 +- code/client/cl_curl.h | 2 +- code/curl-7.54.0/include/README | 55 ++ .../include}/curl/curl.h | 508 +++++++++++++----- .../include}/curl/curlbuild.h | 15 +- .../include}/curl/curlrules.h | 29 +- .../include}/curl/curlver.h | 22 +- .../include}/curl/easy.h | 6 +- .../include}/curl/mprintf.h | 37 +- .../include}/curl/multi.h | 46 +- .../include}/curl/stdcheaders.h | 8 +- code/curl-7.54.0/include/curl/system.h | 484 +++++++++++++++++ .../include}/curl/typecheck-gcc.h | 174 +++--- code/libs/win32/libcurl.a | Bin 385192 -> 434836 bytes code/libs/win64/libcurl.a | Bin 417936 -> 504330 bytes 15 files changed, 1096 insertions(+), 294 deletions(-) create mode 100644 code/curl-7.54.0/include/README rename code/{libcurl-7.35.0 => curl-7.54.0/include}/curl/curl.h (83%) rename code/{libcurl-7.35.0 => curl-7.54.0/include}/curl/curlbuild.h (98%) rename code/{libcurl-7.35.0 => curl-7.54.0/include}/curl/curlrules.h (91%) rename code/{libcurl-7.35.0 => curl-7.54.0/include}/curl/curlver.h (77%) rename code/{libcurl-7.35.0 => curl-7.54.0/include}/curl/easy.h (94%) rename code/{libcurl-7.35.0 => curl-7.54.0/include}/curl/mprintf.h (68%) rename code/{libcurl-7.35.0 => curl-7.54.0/include}/curl/multi.h (90%) rename code/{libcurl-7.35.0 => curl-7.54.0/include}/curl/stdcheaders.h (82%) create mode 100644 code/curl-7.54.0/include/curl/system.h rename code/{libcurl-7.35.0 => curl-7.54.0/include}/curl/typecheck-gcc.h (93%) diff --git a/Makefile b/Makefile index bfe14a5b64..45339aab9b 100644 --- a/Makefile +++ b/Makefile @@ -625,9 +625,9 @@ ifdef MINGW ifeq ($(USE_LOCAL_HEADERS),1) CLIENT_CFLAGS += -DCURL_STATICLIB ifeq ($(ARCH),x86_64) - CLIENT_LIBS += $(LIBSDIR)/win64/libcurl.a + CLIENT_LIBS += $(LIBSDIR)/win64/libcurl.a -lcrypt32 else - CLIENT_LIBS += $(LIBSDIR)/win32/libcurl.a + CLIENT_LIBS += $(LIBSDIR)/win32/libcurl.a -lcrypt32 endif else CLIENT_LIBS += $(CURL_LIBS) diff --git a/code/client/cl_curl.h b/code/client/cl_curl.h index 1c0be55aef..147afc2122 100644 --- a/code/client/cl_curl.h +++ b/code/client/cl_curl.h @@ -28,7 +28,7 @@ Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA #include "../qcommon/qcommon.h" #ifdef USE_LOCAL_HEADERS - #include "../libcurl-7.35.0/curl/curl.h" + #include "../curl-7.54.0/include/curl/curl.h" #else #include #endif diff --git a/code/curl-7.54.0/include/README b/code/curl-7.54.0/include/README new file mode 100644 index 0000000000..6eb73b2b74 --- /dev/null +++ b/code/curl-7.54.0/include/README @@ -0,0 +1,55 @@ + _ _ ____ _ + ___| | | | _ \| | + / __| | | | |_) | | + | (__| |_| | _ <| |___ + \___|\___/|_| \_\_____| + +Include files for libcurl, external users. + +They're all placed in the curl subdirectory here for better fit in any kind +of environment. You must include files from here using... + + #include + +... style and point the compiler's include path to the directory holding the +curl subdirectory. It makes it more likely to survive future modifications. + +NOTE FOR LIBCURL HACKERS + +The following notes apply to libcurl version 7.19.0 and later. + +* The distributed curl/curlbuild.h file is only intended to be used on systems + which can not run the also distributed configure script. + +* The distributed curlbuild.h file is generated as a copy of curlbuild.h.dist + when the libcurl source code distribution archive file is originally created. + +* If you check out from git on a non-configure platform, you must run the + appropriate buildconf* script to set up curlbuild.h and other local files + before being able of compiling the library. + +* On systems capable of running the configure script, the configure process + will overwrite the distributed include/curl/curlbuild.h file with one that + is suitable and specific to the library being configured and built, which + is generated from the include/curl/curlbuild.h.in template file. + +* If you intend to distribute an already compiled libcurl library you _MUST_ + also distribute along with it the generated curl/curlbuild.h which has been + used to compile it. Otherwise the library will be of no use for the users of + the library that you have built. It is _your_ responsibility to provide this + file. No one at the curl project can know how you have built the library. + +* File curl/curlbuild.h includes platform and configuration dependent info, + and must not be modified by anyone. Configure script generates it for you. + +* We cannot assume anything else but very basic compiler features being + present. While libcurl requires an ANSI C compiler to build, some of the + earlier ANSI compilers clearly can't deal with some preprocessor operators. + +* Newlines must remain unix-style for older compilers' sake. + +* Comments must be written in the old-style /* unnested C-fashion */ + +To figure out how to do good and portable checks for features, operating +systems or specific hardwarare, a very good resource is Bjorn Reese's +collection at http://predef.sf.net/ diff --git a/code/libcurl-7.35.0/curl/curl.h b/code/curl-7.54.0/include/curl/curl.h similarity index 83% rename from code/libcurl-7.35.0/curl/curl.h rename to code/curl-7.54.0/include/curl/curl.h index aafaeed2d9..1030712648 100644 --- a/code/libcurl-7.35.0/curl/curl.h +++ b/code/curl-7.54.0/include/curl/curl.h @@ -7,11 +7,11 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) 1998 - 2013, Daniel Stenberg, , et al. + * Copyright (C) 1998 - 2017, Daniel Stenberg, , et al. * * This software is licensed as described in the file COPYING, which * you should have received as part of this distribution. The terms - * are also available at http://curl.haxx.se/docs/copyright.html. + * are also available at https://curl.haxx.se/docs/copyright.html. * * You may opt to use, copy, modify, merge, publish, distribute and/or sell * copies of the Software, and permit persons to whom the Software is @@ -24,13 +24,18 @@ /* * If you have libcurl problems, all docs and details are found here: - * http://curl.haxx.se/libcurl/ + * https://curl.haxx.se/libcurl/ * * curl-library mailing list subscription and unsubscription web interface: - * http://cool.haxx.se/mailman/listinfo/curl-library/ + * https://cool.haxx.se/mailman/listinfo/curl-library/ */ +#ifdef CURL_NO_OLDIES +#define CURL_STRICTER +#endif + #include "curlver.h" /* libcurl version defines */ +#include "system.h" /* determine things run-time */ #include "curlbuild.h" /* libcurl build definitions */ #include "curlrules.h" /* libcurl rules enforcement */ @@ -56,7 +61,8 @@ #include #if defined(WIN32) && !defined(_WIN32_WCE) && !defined(__CYGWIN__) -#if !(defined(_WINSOCKAPI_) || defined(_WINSOCK_H) || defined(__LWIP_OPT_H__)) +#if !(defined(_WINSOCKAPI_) || defined(_WINSOCK_H) || \ + defined(__LWIP_OPT_H__) || defined(LWIP_HDR_OPT_H)) /* The check above prevents the winsock2 inclusion if winsock.h already was included, since they can't co-exist without problems */ #include @@ -90,7 +96,13 @@ extern "C" { #endif +#if defined(BUILDING_LIBCURL) || defined(CURL_STRICTER) +typedef struct Curl_easy CURL; +typedef struct Curl_share CURLSH; +#else typedef void CURL; +typedef void CURLSH; +#endif /* * libcurl external API function linkage decorations. @@ -112,7 +124,7 @@ typedef void CURL; #ifndef curl_socket_typedef /* socket typedef */ -#if defined(WIN32) && !defined(__LWIP_OPT_H__) +#if defined(WIN32) && !defined(__LWIP_OPT_H__) && !defined(LWIP_HDR_OPT_H) typedef SOCKET curl_socket_t; #define CURL_SOCKET_BAD INVALID_SOCKET #else @@ -127,33 +139,43 @@ struct curl_httppost { char *name; /* pointer to allocated name */ long namelength; /* length of name length */ char *contents; /* pointer to allocated data contents */ - long contentslength; /* length of contents field */ + long contentslength; /* length of contents field, see also + CURL_HTTPPOST_LARGE */ char *buffer; /* pointer to allocated buffer contents */ long bufferlength; /* length of buffer field */ char *contenttype; /* Content-Type */ - struct curl_slist* contentheader; /* list of extra headers for this form */ + struct curl_slist *contentheader; /* list of extra headers for this form */ struct curl_httppost *more; /* if one field name has more than one file, this link should link to following files */ long flags; /* as defined below */ -#define HTTPPOST_FILENAME (1<<0) /* specified content is a file name */ -#define HTTPPOST_READFILE (1<<1) /* specified content is a file name */ -#define HTTPPOST_PTRNAME (1<<2) /* name is only stored pointer - do not free in formfree */ -#define HTTPPOST_PTRCONTENTS (1<<3) /* contents is only stored pointer - do not free in formfree */ -#define HTTPPOST_BUFFER (1<<4) /* upload file from buffer */ -#define HTTPPOST_PTRBUFFER (1<<5) /* upload file from pointer contents */ -#define HTTPPOST_CALLBACK (1<<6) /* upload file contents by using the - regular read callback to get the data - and pass the given pointer as custom - pointer */ + +/* specified content is a file name */ +#define CURL_HTTPPOST_FILENAME (1<<0) +/* specified content is a file name */ +#define CURL_HTTPPOST_READFILE (1<<1) +/* name is only stored pointer do not free in formfree */ +#define CURL_HTTPPOST_PTRNAME (1<<2) +/* contents is only stored pointer do not free in formfree */ +#define CURL_HTTPPOST_PTRCONTENTS (1<<3) +/* upload file from buffer */ +#define CURL_HTTPPOST_BUFFER (1<<4) +/* upload file from pointer contents */ +#define CURL_HTTPPOST_PTRBUFFER (1<<5) +/* upload file contents by using the regular read callback to get the data and + pass the given pointer as custom pointer */ +#define CURL_HTTPPOST_CALLBACK (1<<6) +/* use size in 'contentlen', added in 7.46.0 */ +#define CURL_HTTPPOST_LARGE (1<<7) char *showfilename; /* The file name to show. If not set, the actual file name will be used (if this is a file part) */ void *userp; /* custom pointer used for HTTPPOST_CALLBACK posts */ + curl_off_t contentlen; /* alternative length of contents + field. Used if CURL_HTTPPOST_LARGE is + set. Added in 7.46.0 */ }; /* This is the CURLOPT_PROGRESSFUNCTION callback proto. It is now considered @@ -172,6 +194,11 @@ typedef int (*curl_xferinfo_callback)(void *clientp, curl_off_t ultotal, curl_off_t ulnow); +#ifndef CURL_MAX_READ_SIZE + /* The maximum receive buffer size configurable via CURLOPT_BUFFERSIZE. */ +#define CURL_MAX_READ_SIZE 524288 +#endif + #ifndef CURL_MAX_WRITE_SIZE /* Tests have proven that 20K is a very bad buffer size for uploads on Windows, while 16K for some odd reason performed a lot better. @@ -249,7 +276,7 @@ struct curl_fileinfo { unsigned int flags; /* used internally */ - char * b_data; + char *b_data; size_t b_size; size_t b_used; }; @@ -362,6 +389,7 @@ typedef curlioerr (*curl_ioctl_callback)(CURL *handle, int cmd, void *clientp); +#ifndef CURL_DID_MEMORY_FUNC_TYPEDEFS /* * The following typedef's are signatures of malloc, free, realloc, strdup and * calloc respectively. Function pointers of these types can be passed to the @@ -374,6 +402,9 @@ typedef void *(*curl_realloc_callback)(void *ptr, size_t size); typedef char *(*curl_strdup_callback)(const char *str); typedef void *(*curl_calloc_callback)(size_t nmemb, size_t size); +#define CURL_DID_MEMORY_FUNC_TYPEDEFS +#endif + /* the kind of data that is passed to information_callback*/ typedef enum { CURLINFO_TEXT = 0, @@ -410,7 +441,7 @@ typedef enum { CURLE_COULDNT_RESOLVE_PROXY, /* 5 */ CURLE_COULDNT_RESOLVE_HOST, /* 6 */ CURLE_COULDNT_CONNECT, /* 7 */ - CURLE_FTP_WEIRD_SERVER_REPLY, /* 8 */ + CURLE_WEIRD_SERVER_REPLY, /* 8 */ CURLE_REMOTE_ACCESS_DENIED, /* 9 a service was denied by the server due to lack of access - when login fails this is not returned. */ @@ -423,7 +454,9 @@ typedef enum { CURLE_FTP_WEIRD_PASV_REPLY, /* 13 */ CURLE_FTP_WEIRD_227_FORMAT, /* 14 */ CURLE_FTP_CANT_GET_HOST, /* 15 */ - CURLE_OBSOLETE16, /* 16 - NOT USED */ + CURLE_HTTP2, /* 16 - A problem in the http2 framing layer. + [was obsoleted in August 2007 for 7.17.0, + reused in July 2014 for 7.38.0] */ CURLE_FTP_COULDNT_SET_TYPE, /* 17 */ CURLE_PARTIAL_FILE, /* 18 */ CURLE_FTP_COULDNT_RETR_FILE, /* 19 */ @@ -452,15 +485,15 @@ typedef enum { CURLE_LDAP_CANNOT_BIND, /* 38 */ CURLE_LDAP_SEARCH_FAILED, /* 39 */ CURLE_OBSOLETE40, /* 40 - NOT USED */ - CURLE_FUNCTION_NOT_FOUND, /* 41 */ + CURLE_FUNCTION_NOT_FOUND, /* 41 - NOT USED starting with 7.53.0 */ CURLE_ABORTED_BY_CALLBACK, /* 42 */ CURLE_BAD_FUNCTION_ARGUMENT, /* 43 */ CURLE_OBSOLETE44, /* 44 - NOT USED */ CURLE_INTERFACE_FAILED, /* 45 - CURLOPT_INTERFACE failed */ CURLE_OBSOLETE46, /* 46 - NOT USED */ - CURLE_TOO_MANY_REDIRECTS , /* 47 - catch endless re-direct loops */ + CURLE_TOO_MANY_REDIRECTS, /* 47 - catch endless re-direct loops */ CURLE_UNKNOWN_OPTION, /* 48 - User specified an unknown option */ - CURLE_TELNET_OPTION_SYNTAX , /* 49 - Malformed telnet option */ + CURLE_TELNET_OPTION_SYNTAX, /* 49 - Malformed telnet option */ CURLE_OBSOLETE50, /* 50 - NOT USED */ CURLE_PEER_FAILED_VERIFICATION, /* 51 - peer's certificate or fingerprint wasn't verified fine */ @@ -519,18 +552,27 @@ typedef enum { CURLE_CHUNK_FAILED, /* 88 - chunk callback reported error */ CURLE_NO_CONNECTION_AVAILABLE, /* 89 - No connection available, the session will be queued */ + CURLE_SSL_PINNEDPUBKEYNOTMATCH, /* 90 - specified pinned public key did not + match */ + CURLE_SSL_INVALIDCERTSTATUS, /* 91 - invalid certificate status */ + CURLE_HTTP2_STREAM, /* 92 - stream error in HTTP/2 framing layer + */ CURL_LAST /* never use! */ } CURLcode; #ifndef CURL_NO_OLDIES /* define this to test if your app builds with all the obsolete stuff removed! */ -/* Previously obsoletes error codes re-used in 7.24.0 */ +/* Previously obsolete error code re-used in 7.38.0 */ +#define CURLE_OBSOLETE16 CURLE_HTTP2 + +/* Previously obsolete error codes re-used in 7.24.0 */ #define CURLE_OBSOLETE10 CURLE_FTP_ACCEPT_FAILED #define CURLE_OBSOLETE12 CURLE_FTP_ACCEPT_TIMEOUT /* compatibility with older names */ #define CURLOPT_ENCODING CURLOPT_ACCEPT_ENCODING +#define CURLE_FTP_WEIRD_SERVER_REPLY CURLE_WEIRD_SERVER_REPLY /* The following were added in 7.21.5, April 2011 */ #define CURLE_UNKNOWN_TELNET_OPTION CURLE_UNKNOWN_OPTION @@ -579,6 +621,16 @@ typedef enum { make programs break */ #define CURLE_ALREADY_COMPLETE 99999 +/* Provide defines for really old option names */ +#define CURLOPT_FILE CURLOPT_WRITEDATA /* name changed in 7.9.7 */ +#define CURLOPT_INFILE CURLOPT_READDATA /* name changed in 7.9.7 */ +#define CURLOPT_WRITEHEADER CURLOPT_HEADERDATA + +/* Since long deprecated options with no code in the lib that does anything + with them. */ +#define CURLOPT_WRITEINFO CURLOPT_OBSOLETE40 +#define CURLOPT_CLOSEPOLICY CURLOPT_OBSOLETE72 + #endif /*!CURL_NO_OLDIES*/ /* This prototype applies to all conversion callbacks */ @@ -594,6 +646,7 @@ typedef enum { CONNECT HTTP/1.1 */ CURLPROXY_HTTP_1_0 = 1, /* added in 7.19.4, force to use CONNECT HTTP/1.0 */ + CURLPROXY_HTTPS = 2, /* added in 7.52.0 */ CURLPROXY_SOCKS4 = 4, /* support added in 7.15.2, enum existed already in 7.10 */ CURLPROXY_SOCKS5 = 5, /* added in 7.10 */ @@ -609,7 +662,8 @@ typedef enum { * CURLAUTH_NONE - No HTTP authentication * CURLAUTH_BASIC - HTTP Basic authentication (default) * CURLAUTH_DIGEST - HTTP Digest authentication - * CURLAUTH_GSSNEGOTIATE - HTTP GSS-Negotiate authentication + * CURLAUTH_NEGOTIATE - HTTP Negotiate (SPNEGO) authentication + * CURLAUTH_GSSNEGOTIATE - Alias for CURLAUTH_NEGOTIATE (deprecated) * CURLAUTH_NTLM - HTTP NTLM authentication * CURLAUTH_DIGEST_IE - HTTP Digest authentication with IE flavour * CURLAUTH_NTLM_WB - HTTP NTLM authentication delegated to winbind helper @@ -622,7 +676,9 @@ typedef enum { #define CURLAUTH_NONE ((unsigned long)0) #define CURLAUTH_BASIC (((unsigned long)1)<<0) #define CURLAUTH_DIGEST (((unsigned long)1)<<1) -#define CURLAUTH_GSSNEGOTIATE (((unsigned long)1)<<2) +#define CURLAUTH_NEGOTIATE (((unsigned long)1)<<2) +/* Deprecated since the advent of CURLAUTH_NEGOTIATE */ +#define CURLAUTH_GSSNEGOTIATE CURLAUTH_NEGOTIATE #define CURLAUTH_NTLM (((unsigned long)1)<<3) #define CURLAUTH_DIGEST_IE (((unsigned long)1)<<4) #define CURLAUTH_NTLM_WB (((unsigned long)1)<<5) @@ -704,6 +760,10 @@ typedef enum { servers, a user can this way allow the vulnerability back. */ #define CURLSSLOPT_ALLOW_BEAST (1<<0) +/* - NO_REVOKE tells libcurl to disable certificate revocation checks for those + SSL backends where such behavior is present. */ +#define CURLSSLOPT_NO_REVOKE (1<<1) + #ifndef CURL_NO_OLDIES /* define this to test if your app builds with all the obsolete stuff removed! */ @@ -754,6 +814,10 @@ typedef enum { CURLFTPMETHOD_LAST /* not an option, never use */ } curl_ftpmethod; +/* bitmask defines for CURLOPT_HEADEROPT */ +#define CURLHEADER_UNIFIED 0 +#define CURLHEADER_SEPARATE (1<<0) + /* CURLPROTO_ defines are for the CURLOPT_*PROTOCOLS options */ #define CURLPROTO_HTTP (1<<0) #define CURLPROTO_HTTPS (1<<1) @@ -781,15 +845,21 @@ typedef enum { #define CURLPROTO_RTMPS (1<<23) #define CURLPROTO_RTMPTS (1<<24) #define CURLPROTO_GOPHER (1<<25) +#define CURLPROTO_SMB (1<<26) +#define CURLPROTO_SMBS (1<<27) #define CURLPROTO_ALL (~0) /* enable everything */ /* long may be 32 or 64 bits, but we should never depend on anything else but 32 */ #define CURLOPTTYPE_LONG 0 #define CURLOPTTYPE_OBJECTPOINT 10000 +#define CURLOPTTYPE_STRINGPOINT 10000 #define CURLOPTTYPE_FUNCTIONPOINT 20000 #define CURLOPTTYPE_OFF_T 30000 +/* *STRINGPOINT is an alias for OBJECTPOINT to allow tools to extract the + string options from the header file */ + /* name is uppercase CURLOPT_, type is one of the defined CURLOPTTYPE_ number is unique identifier */ @@ -803,6 +873,7 @@ typedef enum { /* The macro "##" is ISO C, we assume pre-ISO C doesn't support it. */ #define LONG CURLOPTTYPE_LONG #define OBJECTPOINT CURLOPTTYPE_OBJECTPOINT +#define STRINGPOINT CURLOPTTYPE_OBJECTPOINT #define FUNCTIONPOINT CURLOPTTYPE_FUNCTIONPOINT #define OFF_T CURLOPTTYPE_OFF_T #define CINIT(name,type,number) CURLOPT_/**/name = type + number @@ -816,30 +887,30 @@ typedef enum { typedef enum { /* This is the FILE * or void * the regular output should be written to. */ - CINIT(FILE, OBJECTPOINT, 1), + CINIT(WRITEDATA, OBJECTPOINT, 1), /* The full URL to get/put */ - CINIT(URL, OBJECTPOINT, 2), + CINIT(URL, STRINGPOINT, 2), /* Port number to connect to, if other than default. */ CINIT(PORT, LONG, 3), /* Name of proxy to use. */ - CINIT(PROXY, OBJECTPOINT, 4), + CINIT(PROXY, STRINGPOINT, 4), /* "user:password;options" to use when fetching. */ - CINIT(USERPWD, OBJECTPOINT, 5), + CINIT(USERPWD, STRINGPOINT, 5), /* "user:password" to use with proxy. */ - CINIT(PROXYUSERPWD, OBJECTPOINT, 6), + CINIT(PROXYUSERPWD, STRINGPOINT, 6), /* Range to get, specified as an ASCII string. */ - CINIT(RANGE, OBJECTPOINT, 7), + CINIT(RANGE, STRINGPOINT, 7), /* not used */ /* Specified file stream to upload from (use as input): */ - CINIT(INFILE, OBJECTPOINT, 9), + CINIT(READDATA, OBJECTPOINT, 9), /* Buffer to receive error messages in, must be at least CURL_ERROR_SIZE * bytes big. If this is not used, error messages go to stderr instead: */ @@ -871,14 +942,14 @@ typedef enum { CINIT(POSTFIELDS, OBJECTPOINT, 15), /* Set the referrer page (needed by some CGIs) */ - CINIT(REFERER, OBJECTPOINT, 16), + CINIT(REFERER, STRINGPOINT, 16), /* Set the FTP PORT string (interface name, named or numerical IP address) Use i.e '-' to use default address. */ - CINIT(FTPPORT, OBJECTPOINT, 17), + CINIT(FTPPORT, STRINGPOINT, 17), /* Set the User-Agent string (examined by some CGIs) */ - CINIT(USERAGENT, OBJECTPOINT, 18), + CINIT(USERAGENT, STRINGPOINT, 18), /* If the download receives less than "low speed limit" bytes/second * during "low speed time" seconds, the operations is aborted. @@ -901,19 +972,20 @@ typedef enum { CINIT(RESUME_FROM, LONG, 21), /* Set cookie in request: */ - CINIT(COOKIE, OBJECTPOINT, 22), + CINIT(COOKIE, STRINGPOINT, 22), - /* This points to a linked list of headers, struct curl_slist kind */ + /* This points to a linked list of headers, struct curl_slist kind. This + list is also used for RTSP (in spite of its name) */ CINIT(HTTPHEADER, OBJECTPOINT, 23), /* This points to a linked list of post entries, struct curl_httppost */ CINIT(HTTPPOST, OBJECTPOINT, 24), /* name of the file keeping your private SSL-certificate */ - CINIT(SSLCERT, OBJECTPOINT, 25), + CINIT(SSLCERT, STRINGPOINT, 25), /* password for the SSL or SSH private key */ - CINIT(KEYPASSWD, OBJECTPOINT, 26), + CINIT(KEYPASSWD, STRINGPOINT, 26), /* send TYPE parameter? */ CINIT(CRLF, LONG, 27), @@ -923,11 +995,11 @@ typedef enum { /* send FILE * or void * to store headers to, if you use a callback it is simply passed to the callback unmodified */ - CINIT(WRITEHEADER, OBJECTPOINT, 29), + CINIT(HEADERDATA, OBJECTPOINT, 29), /* point to a file to read the initial cookies from, also enables "cookie awareness" */ - CINIT(COOKIEFILE, OBJECTPOINT, 31), + CINIT(COOKIEFILE, STRINGPOINT, 31), /* What version to specifically try to use. See CURL_SSLVERSION defines below. */ @@ -946,9 +1018,9 @@ typedef enum { HTTP: DELETE, TRACE and others FTP: to use a different list command */ - CINIT(CUSTOMREQUEST, OBJECTPOINT, 36), + CINIT(CUSTOMREQUEST, STRINGPOINT, 36), - /* HTTP request, for odd commands like DELETE, TRACE and others */ + /* FILE handle to use instead of stderr */ CINIT(STDERR, OBJECTPOINT, 37), /* 38 is not used */ @@ -956,13 +1028,13 @@ typedef enum { /* send linked-list of post-transfer QUOTE commands */ CINIT(POSTQUOTE, OBJECTPOINT, 39), - CINIT(WRITEINFO, OBJECTPOINT, 40), /* DEPRECATED, do not use! */ + CINIT(OBSOLETE40, OBJECTPOINT, 40), /* OBSOLETE, do not use! */ CINIT(VERBOSE, LONG, 41), /* talk a lot */ CINIT(HEADER, LONG, 42), /* throw the header out too */ CINIT(NOPROGRESS, LONG, 43), /* shut off the progress meter */ CINIT(NOBODY, LONG, 44), /* use HEAD to get http document */ - CINIT(FAILONERROR, LONG, 45), /* no output on http error codes >= 300 */ + CINIT(FAILONERROR, LONG, 45), /* no output on http error codes >= 400 */ CINIT(UPLOAD, LONG, 46), /* this is an upload */ CINIT(POST, LONG, 47), /* HTTP POST method */ CINIT(DIRLISTONLY, LONG, 48), /* bare names when listing directories */ @@ -1005,19 +1077,19 @@ typedef enum { CINIT(HTTPPROXYTUNNEL, LONG, 61), /* Set the interface string to use as outgoing network interface */ - CINIT(INTERFACE, OBJECTPOINT, 62), + CINIT(INTERFACE, STRINGPOINT, 62), /* Set the krb4/5 security level, this also enables krb4/5 awareness. This * is a string, 'clear', 'safe', 'confidential' or 'private'. If the string * is set but doesn't match one of these, 'private' will be used. */ - CINIT(KRBLEVEL, OBJECTPOINT, 63), + CINIT(KRBLEVEL, STRINGPOINT, 63), /* Set if we should verify the peer in ssl handshake, set 1 to verify. */ CINIT(SSL_VERIFYPEER, LONG, 64), /* The CApath or CAfile used to validate the peer certificate this option is used only if SSL_VERIFYPEER is true */ - CINIT(CAINFO, OBJECTPOINT, 65), + CINIT(CAINFO, STRINGPOINT, 65), /* 66 = OBSOLETE */ /* 67 = OBSOLETE */ @@ -1035,7 +1107,7 @@ typedef enum { /* Max amount of cached alive connections */ CINIT(MAXCONNECTS, LONG, 71), - CINIT(CLOSEPOLICY, LONG, 72), /* DEPRECATED, do not use! */ + CINIT(OBSOLETE72, LONG, 72), /* OBSOLETE, do not use! */ /* 73 = OBSOLETE */ @@ -1051,10 +1123,10 @@ typedef enum { /* Set to a file name that contains random data for libcurl to use to seed the random engine when doing SSL connects. */ - CINIT(RANDOM_FILE, OBJECTPOINT, 76), + CINIT(RANDOM_FILE, STRINGPOINT, 76), /* Set to the Entropy Gathering Daemon socket pathname */ - CINIT(EGDSOCKET, OBJECTPOINT, 77), + CINIT(EGDSOCKET, STRINGPOINT, 77), /* Time-out connect operations after this amount of seconds, if connects are OK within this time, then fine... This only aborts the connect phase. */ @@ -1076,10 +1148,10 @@ typedef enum { /* Specify which file name to write all known cookies in after completed operation. Set file name to "-" (dash) to make it go to stdout. */ - CINIT(COOKIEJAR, OBJECTPOINT, 82), + CINIT(COOKIEJAR, STRINGPOINT, 82), /* Specify which SSL ciphers to use */ - CINIT(SSL_CIPHER_LIST, OBJECTPOINT, 83), + CINIT(SSL_CIPHER_LIST, STRINGPOINT, 83), /* Specify which HTTP version to use! This must be set to one of the CURL_HTTP_VERSION* enums set below. */ @@ -1091,16 +1163,16 @@ typedef enum { CINIT(FTP_USE_EPSV, LONG, 85), /* type of the file keeping your SSL-certificate ("DER", "PEM", "ENG") */ - CINIT(SSLCERTTYPE, OBJECTPOINT, 86), + CINIT(SSLCERTTYPE, STRINGPOINT, 86), /* name of the file keeping your private SSL-key */ - CINIT(SSLKEY, OBJECTPOINT, 87), + CINIT(SSLKEY, STRINGPOINT, 87), /* type of the file keeping your private SSL-key ("DER", "PEM", "ENG") */ - CINIT(SSLKEYTYPE, OBJECTPOINT, 88), + CINIT(SSLKEYTYPE, STRINGPOINT, 88), /* crypto engine for the SSL-sub system */ - CINIT(SSLENGINE, OBJECTPOINT, 89), + CINIT(SSLENGINE, STRINGPOINT, 89), /* set the crypto engine for the SSL-sub system as default the param has no meaning... @@ -1127,7 +1199,7 @@ typedef enum { /* The CApath directory used to validate the peer certificate this option is used only if SSL_VERIFYPEER is true */ - CINIT(CAPATH, OBJECTPOINT, 97), + CINIT(CAPATH, STRINGPOINT, 97), /* Instruct libcurl to use a smaller receive buffer */ CINIT(BUFFERSIZE, LONG, 98), @@ -1141,13 +1213,14 @@ typedef enum { CINIT(SHARE, OBJECTPOINT, 100), /* indicates type of proxy. accepted values are CURLPROXY_HTTP (default), - CURLPROXY_SOCKS4, CURLPROXY_SOCKS4A and CURLPROXY_SOCKS5. */ + CURLPROXY_HTTPS, CURLPROXY_SOCKS4, CURLPROXY_SOCKS4A and + CURLPROXY_SOCKS5. */ CINIT(PROXYTYPE, LONG, 101), /* Set the Accept-Encoding string. Use this to tell a server you would like the response to be compressed. Before 7.21.6, this was known as CURLOPT_ENCODING */ - CINIT(ACCEPT_ENCODING, OBJECTPOINT, 102), + CINIT(ACCEPT_ENCODING, STRINGPOINT, 102), /* Set pointer to private data */ CINIT(PRIVATE, OBJECTPOINT, 103), @@ -1228,7 +1301,7 @@ typedef enum { to parse (using the CURLOPT_NETRC option). If not set, libcurl will do a poor attempt to find the user's home directory and check for a .netrc file in there. */ - CINIT(NETRC_FILE, OBJECTPOINT, 118), + CINIT(NETRC_FILE, STRINGPOINT, 118), /* Enable SSL/TLS for FTP, pick one of: CURLUSESSL_TRY - try using SSL, proceed anyway otherwise @@ -1271,10 +1344,10 @@ typedef enum { /* zero terminated string for pass on to the FTP server when asked for "account" info */ - CINIT(FTP_ACCOUNT, OBJECTPOINT, 134), + CINIT(FTP_ACCOUNT, STRINGPOINT, 134), - /* feed cookies into cookie engine */ - CINIT(COOKIELIST, OBJECTPOINT, 135), + /* feed cookie into cookie engine */ + CINIT(COOKIELIST, STRINGPOINT, 135), /* ignore Content-Length */ CINIT(IGNORE_CONTENT_LENGTH, LONG, 136), @@ -1320,7 +1393,7 @@ typedef enum { CINIT(MAX_RECV_SPEED_LARGE, OFF_T, 146), /* Pointer to command string to send if USER/PASS fails. */ - CINIT(FTP_ALTERNATIVE_TO_USER, OBJECTPOINT, 147), + CINIT(FTP_ALTERNATIVE_TO_USER, STRINGPOINT, 147), /* callback function for setting socket options */ CINIT(SOCKOPTFUNCTION, FUNCTIONPOINT, 148), @@ -1334,8 +1407,8 @@ typedef enum { CINIT(SSH_AUTH_TYPES, LONG, 151), /* Used by scp/sftp to do public/private key authentication */ - CINIT(SSH_PUBLIC_KEYFILE, OBJECTPOINT, 152), - CINIT(SSH_PRIVATE_KEYFILE, OBJECTPOINT, 153), + CINIT(SSH_PUBLIC_KEYFILE, STRINGPOINT, 152), + CINIT(SSH_PRIVATE_KEYFILE, STRINGPOINT, 153), /* Send CCC (Clear Command Channel) after authentication */ CINIT(FTP_SSL_CCC, LONG, 154), @@ -1359,7 +1432,7 @@ typedef enum { CINIT(POSTREDIR, LONG, 161), /* used by scp/sftp to verify the host's public key */ - CINIT(SSH_HOST_PUBLIC_KEY_MD5, OBJECTPOINT, 162), + CINIT(SSH_HOST_PUBLIC_KEY_MD5, STRINGPOINT, 162), /* Callback function for opening socket (instead of socket(2)). Optionally, callback is able change the address or refuse to connect returning @@ -1379,10 +1452,10 @@ typedef enum { CINIT(SEEKDATA, OBJECTPOINT, 168), /* CRL file */ - CINIT(CRLFILE, OBJECTPOINT, 169), + CINIT(CRLFILE, STRINGPOINT, 169), /* Issuer certificate */ - CINIT(ISSUERCERT, OBJECTPOINT, 170), + CINIT(ISSUERCERT, STRINGPOINT, 170), /* (IPv6) Address scope */ CINIT(ADDRESS_SCOPE, LONG, 171), @@ -1392,12 +1465,12 @@ typedef enum { CINIT(CERTINFO, LONG, 172), /* "name" and "pwd" to use when fetching. */ - CINIT(USERNAME, OBJECTPOINT, 173), - CINIT(PASSWORD, OBJECTPOINT, 174), + CINIT(USERNAME, STRINGPOINT, 173), + CINIT(PASSWORD, STRINGPOINT, 174), /* "name" and "pwd" to use with Proxy when fetching. */ - CINIT(PROXYUSERNAME, OBJECTPOINT, 175), - CINIT(PROXYPASSWORD, OBJECTPOINT, 176), + CINIT(PROXYUSERNAME, STRINGPOINT, 175), + CINIT(PROXYPASSWORD, STRINGPOINT, 176), /* Comma separated list of hostnames defining no-proxy zones. These should match both hostnames directly, and hostnames within a domain. For @@ -1406,13 +1479,13 @@ typedef enum { implementations of this, .local.com will be considered to be the same as local.com. A single * is the only valid wildcard, and effectively disables the use of proxy. */ - CINIT(NOPROXY, OBJECTPOINT, 177), + CINIT(NOPROXY, STRINGPOINT, 177), /* block size for TFTP transfers */ CINIT(TFTP_BLKSIZE, LONG, 178), /* Socks Service */ - CINIT(SOCKS5_GSSAPI_SERVICE, OBJECTPOINT, 179), + CINIT(SOCKS5_GSSAPI_SERVICE, STRINGPOINT, 179), /* DEPRECATED, do not use! */ /* Socks Service */ CINIT(SOCKS5_GSSAPI_NEC, LONG, 180), @@ -1430,7 +1503,7 @@ typedef enum { CINIT(REDIR_PROTOCOLS, LONG, 182), /* set the SSH knownhost file name to use */ - CINIT(SSH_KNOWNHOSTS, OBJECTPOINT, 183), + CINIT(SSH_KNOWNHOSTS, STRINGPOINT, 183), /* set the SSH host key callback, must point to a curl_sshkeycallback function */ @@ -1440,9 +1513,9 @@ typedef enum { CINIT(SSH_KEYDATA, OBJECTPOINT, 185), /* set the SMTP mail originator */ - CINIT(MAIL_FROM, OBJECTPOINT, 186), + CINIT(MAIL_FROM, STRINGPOINT, 186), - /* set the SMTP mail receiver(s) */ + /* set the list of SMTP mail receiver(s) */ CINIT(MAIL_RCPT, OBJECTPOINT, 187), /* FTP: send PRET before PASV */ @@ -1452,13 +1525,13 @@ typedef enum { CINIT(RTSP_REQUEST, LONG, 189), /* The RTSP session identifier */ - CINIT(RTSP_SESSION_ID, OBJECTPOINT, 190), + CINIT(RTSP_SESSION_ID, STRINGPOINT, 190), /* The RTSP stream URI */ - CINIT(RTSP_STREAM_URI, OBJECTPOINT, 191), + CINIT(RTSP_STREAM_URI, STRINGPOINT, 191), /* The Transport: header to use in RTSP requests */ - CINIT(RTSP_TRANSPORT, OBJECTPOINT, 192), + CINIT(RTSP_TRANSPORT, STRINGPOINT, 192), /* Manually initialize the client RTSP CSeq for this handle */ CINIT(RTSP_CLIENT_CSEQ, LONG, 193), @@ -1496,13 +1569,13 @@ typedef enum { CINIT(RESOLVE, OBJECTPOINT, 203), /* Set a username for authenticated TLS */ - CINIT(TLSAUTH_USERNAME, OBJECTPOINT, 204), + CINIT(TLSAUTH_USERNAME, STRINGPOINT, 204), /* Set a password for authenticated TLS */ - CINIT(TLSAUTH_PASSWORD, OBJECTPOINT, 205), + CINIT(TLSAUTH_PASSWORD, STRINGPOINT, 205), /* Set authentication type for authenticated TLS */ - CINIT(TLSAUTH_TYPE, OBJECTPOINT, 206), + CINIT(TLSAUTH_TYPE, STRINGPOINT, 206), /* Set to 1 to enable the "TE:" header in HTTP requests to ask for compressed transfer-encoded responses. Set to 0 to disable the use of TE: @@ -1525,10 +1598,10 @@ typedef enum { CINIT(GSSAPI_DELEGATION, LONG, 210), /* Set the name servers to use for DNS resolution */ - CINIT(DNS_SERVERS, OBJECTPOINT, 211), + CINIT(DNS_SERVERS, STRINGPOINT, 211), /* Time-out accept operations (currently for FTP only) after this amount - of miliseconds. */ + of milliseconds. */ CINIT(ACCEPTTIMEOUT_MS, LONG, 212), /* Set TCP keepalive */ @@ -1542,7 +1615,7 @@ typedef enum { CINIT(SSL_OPTIONS, LONG, 216), /* Set the SMTP auth originator */ - CINIT(MAIL_AUTH, OBJECTPOINT, 217), + CINIT(MAIL_AUTH, STRINGPOINT, 217), /* Enable/disable SASL initial response */ CINIT(SASL_IR, LONG, 218), @@ -1553,23 +1626,161 @@ typedef enum { CINIT(XFERINFOFUNCTION, FUNCTIONPOINT, 219), /* The XOAUTH2 bearer token */ - CINIT(XOAUTH2_BEARER, OBJECTPOINT, 220), + CINIT(XOAUTH2_BEARER, STRINGPOINT, 220), /* Set the interface string to use as outgoing network * interface for DNS requests. * Only supported by the c-ares DNS backend */ - CINIT(DNS_INTERFACE, OBJECTPOINT, 221), + CINIT(DNS_INTERFACE, STRINGPOINT, 221), /* Set the local IPv4 address to use for outgoing DNS requests. * Only supported by the c-ares DNS backend */ - CINIT(DNS_LOCAL_IP4, OBJECTPOINT, 222), + CINIT(DNS_LOCAL_IP4, STRINGPOINT, 222), /* Set the local IPv4 address to use for outgoing DNS requests. * Only supported by the c-ares DNS backend */ - CINIT(DNS_LOCAL_IP6, OBJECTPOINT, 223), + CINIT(DNS_LOCAL_IP6, STRINGPOINT, 223), /* Set authentication options directly */ - CINIT(LOGIN_OPTIONS, OBJECTPOINT, 224), + CINIT(LOGIN_OPTIONS, STRINGPOINT, 224), + + /* Enable/disable TLS NPN extension (http2 over ssl might fail without) */ + CINIT(SSL_ENABLE_NPN, LONG, 225), + + /* Enable/disable TLS ALPN extension (http2 over ssl might fail without) */ + CINIT(SSL_ENABLE_ALPN, LONG, 226), + + /* Time to wait for a response to a HTTP request containing an + * Expect: 100-continue header before sending the data anyway. */ + CINIT(EXPECT_100_TIMEOUT_MS, LONG, 227), + + /* This points to a linked list of headers used for proxy requests only, + struct curl_slist kind */ + CINIT(PROXYHEADER, OBJECTPOINT, 228), + + /* Pass in a bitmask of "header options" */ + CINIT(HEADEROPT, LONG, 229), + + /* The public key in DER form used to validate the peer public key + this option is used only if SSL_VERIFYPEER is true */ + CINIT(PINNEDPUBLICKEY, STRINGPOINT, 230), + + /* Path to Unix domain socket */ + CINIT(UNIX_SOCKET_PATH, STRINGPOINT, 231), + + /* Set if we should verify the certificate status. */ + CINIT(SSL_VERIFYSTATUS, LONG, 232), + + /* Set if we should enable TLS false start. */ + CINIT(SSL_FALSESTART, LONG, 233), + + /* Do not squash dot-dot sequences */ + CINIT(PATH_AS_IS, LONG, 234), + + /* Proxy Service Name */ + CINIT(PROXY_SERVICE_NAME, STRINGPOINT, 235), + + /* Service Name */ + CINIT(SERVICE_NAME, STRINGPOINT, 236), + + /* Wait/don't wait for pipe/mutex to clarify */ + CINIT(PIPEWAIT, LONG, 237), + + /* Set the protocol used when curl is given a URL without a protocol */ + CINIT(DEFAULT_PROTOCOL, STRINGPOINT, 238), + + /* Set stream weight, 1 - 256 (default is 16) */ + CINIT(STREAM_WEIGHT, LONG, 239), + + /* Set stream dependency on another CURL handle */ + CINIT(STREAM_DEPENDS, OBJECTPOINT, 240), + + /* Set E-xclusive stream dependency on another CURL handle */ + CINIT(STREAM_DEPENDS_E, OBJECTPOINT, 241), + + /* Do not send any tftp option requests to the server */ + CINIT(TFTP_NO_OPTIONS, LONG, 242), + + /* Linked-list of host:port:connect-to-host:connect-to-port, + overrides the URL's host:port (only for the network layer) */ + CINIT(CONNECT_TO, OBJECTPOINT, 243), + + /* Set TCP Fast Open */ + CINIT(TCP_FASTOPEN, LONG, 244), + + /* Continue to send data if the server responds early with an + * HTTP status code >= 300 */ + CINIT(KEEP_SENDING_ON_ERROR, LONG, 245), + + /* The CApath or CAfile used to validate the proxy certificate + this option is used only if PROXY_SSL_VERIFYPEER is true */ + CINIT(PROXY_CAINFO, STRINGPOINT, 246), + + /* The CApath directory used to validate the proxy certificate + this option is used only if PROXY_SSL_VERIFYPEER is true */ + CINIT(PROXY_CAPATH, STRINGPOINT, 247), + + /* Set if we should verify the proxy in ssl handshake, + set 1 to verify. */ + CINIT(PROXY_SSL_VERIFYPEER, LONG, 248), + + /* Set if we should verify the Common name from the proxy certificate in ssl + * handshake, set 1 to check existence, 2 to ensure that it matches + * the provided hostname. */ + CINIT(PROXY_SSL_VERIFYHOST, LONG, 249), + + /* What version to specifically try to use for proxy. + See CURL_SSLVERSION defines below. */ + CINIT(PROXY_SSLVERSION, LONG, 250), + + /* Set a username for authenticated TLS for proxy */ + CINIT(PROXY_TLSAUTH_USERNAME, STRINGPOINT, 251), + + /* Set a password for authenticated TLS for proxy */ + CINIT(PROXY_TLSAUTH_PASSWORD, STRINGPOINT, 252), + + /* Set authentication type for authenticated TLS for proxy */ + CINIT(PROXY_TLSAUTH_TYPE, STRINGPOINT, 253), + + /* name of the file keeping your private SSL-certificate for proxy */ + CINIT(PROXY_SSLCERT, STRINGPOINT, 254), + + /* type of the file keeping your SSL-certificate ("DER", "PEM", "ENG") for + proxy */ + CINIT(PROXY_SSLCERTTYPE, STRINGPOINT, 255), + + /* name of the file keeping your private SSL-key for proxy */ + CINIT(PROXY_SSLKEY, STRINGPOINT, 256), + + /* type of the file keeping your private SSL-key ("DER", "PEM", "ENG") for + proxy */ + CINIT(PROXY_SSLKEYTYPE, STRINGPOINT, 257), + + /* password for the SSL private key for proxy */ + CINIT(PROXY_KEYPASSWD, STRINGPOINT, 258), + + /* Specify which SSL ciphers to use for proxy */ + CINIT(PROXY_SSL_CIPHER_LIST, STRINGPOINT, 259), + + /* CRL file for proxy */ + CINIT(PROXY_CRLFILE, STRINGPOINT, 260), + + /* Enable/disable specific SSL features with a bitmask for proxy, see + CURLSSLOPT_* */ + CINIT(PROXY_SSL_OPTIONS, LONG, 261), + + /* Name of pre proxy to use. */ + CINIT(PRE_PROXY, STRINGPOINT, 262), + + /* The public key in DER form used to validate the proxy public key + this option is used only if PROXY_SSL_VERIFYPEER is true */ + CINIT(PROXY_PINNEDPUBLICKEY, STRINGPOINT, 263), + + /* Path to an abstract Unix domain socket */ + CINIT(ABSTRACT_UNIX_SOCKET, STRINGPOINT, 264), + + /* Suppress proxy CONNECT response headers from user callbacks */ + CINIT(SUPPRESS_CONNECT_HEADERS, LONG, 265), CURLOPT_LASTENTRY /* the last unused */ } CURLoption; @@ -1607,13 +1818,10 @@ typedef enum { option might be handy to force libcurl to use a specific IP version. */ #define CURL_IPRESOLVE_WHATEVER 0 /* default, resolves addresses to all IP versions that your system allows */ -#define CURL_IPRESOLVE_V4 1 /* resolve to ipv4 addresses */ -#define CURL_IPRESOLVE_V6 2 /* resolve to ipv6 addresses */ +#define CURL_IPRESOLVE_V4 1 /* resolve to IPv4 addresses */ +#define CURL_IPRESOLVE_V6 2 /* resolve to IPv6 addresses */ /* three convenient "aliases" that follow the name scheme better */ -#define CURLOPT_WRITEDATA CURLOPT_FILE -#define CURLOPT_READDATA CURLOPT_INFILE -#define CURLOPT_HEADERDATA CURLOPT_WRITEHEADER #define CURLOPT_RTSPHEADER CURLOPT_HTTPHEADER /* These enums are for use with the CURLOPT_HTTP_VERSION option. */ @@ -1623,11 +1831,19 @@ enum { for us! */ CURL_HTTP_VERSION_1_0, /* please use HTTP 1.0 in the request */ CURL_HTTP_VERSION_1_1, /* please use HTTP 1.1 in the request */ - CURL_HTTP_VERSION_2_0, /* please use HTTP 2.0 in the request */ + CURL_HTTP_VERSION_2_0, /* please use HTTP 2 in the request */ + CURL_HTTP_VERSION_2TLS, /* use version 2 for HTTPS, version 1.1 for HTTP */ + CURL_HTTP_VERSION_2_PRIOR_KNOWLEDGE, /* please use HTTP 2 without HTTP/1.1 + Upgrade */ CURL_HTTP_VERSION_LAST /* *ILLEGAL* http version */ }; +/* Convenience definition simple because the name of the version is HTTP/2 and + not 2.0. The 2_0 version of the enum name was set while the version was + still planned to be 2.0 and we stick to it for compatibility. */ +#define CURL_HTTP_VERSION_2 CURL_HTTP_VERSION_2_0 + /* * Public API enums for RTSP requests */ @@ -1667,10 +1883,23 @@ enum { CURL_SSLVERSION_TLSv1_0, CURL_SSLVERSION_TLSv1_1, CURL_SSLVERSION_TLSv1_2, + CURL_SSLVERSION_TLSv1_3, CURL_SSLVERSION_LAST /* never use, keep last */ }; +enum { + CURL_SSLVERSION_MAX_NONE = 0, + CURL_SSLVERSION_MAX_DEFAULT = (CURL_SSLVERSION_TLSv1 << 16), + CURL_SSLVERSION_MAX_TLSv1_0 = (CURL_SSLVERSION_TLSv1_0 << 16), + CURL_SSLVERSION_MAX_TLSv1_1 = (CURL_SSLVERSION_TLSv1_1 << 16), + CURL_SSLVERSION_MAX_TLSv1_2 = (CURL_SSLVERSION_TLSv1_2 << 16), + CURL_SSLVERSION_MAX_TLSv1_3 = (CURL_SSLVERSION_TLSv1_3 << 16), + + /* never use, keep last */ + CURL_SSLVERSION_MAX_LAST = (CURL_SSLVERSION_LAST << 16) +}; + enum CURL_TLSAUTH { CURL_TLSAUTH_NONE, CURL_TLSAUTH_SRP, @@ -1701,7 +1930,10 @@ typedef enum { /* curl_strequal() and curl_strnequal() are subject for removal in a future - libcurl, see lib/README.curlx for details */ + libcurl, see lib/README.curlx for details + + !checksrc! disable SPACEBEFOREPAREN 2 +*/ CURL_EXTERN int (curl_strequal)(const char *s1, const char *s2); CURL_EXTERN int (curl_strnequal)(const char *s1, const char *s2, size_t n); @@ -1743,6 +1975,7 @@ typedef enum { CFINIT(OBSOLETE2), CFINIT(STREAM), + CFINIT(CONTENTLEN), /* added in 7.46.0, provide a curl_off_t length */ CURLFORM_LASTENTRY /* the last unused */ } CURLformoption; @@ -1991,17 +2224,24 @@ typedef enum { CURLSSLBACKEND_OPENSSL = 1, CURLSSLBACKEND_GNUTLS = 2, CURLSSLBACKEND_NSS = 3, - CURLSSLBACKEND_QSOSSL = 4, + CURLSSLBACKEND_OBSOLETE4 = 4, /* Was QSOSSL. */ CURLSSLBACKEND_GSKIT = 5, CURLSSLBACKEND_POLARSSL = 6, CURLSSLBACKEND_CYASSL = 7, CURLSSLBACKEND_SCHANNEL = 8, - CURLSSLBACKEND_DARWINSSL = 9 + CURLSSLBACKEND_DARWINSSL = 9, + CURLSSLBACKEND_AXTLS = 10, + CURLSSLBACKEND_MBEDTLS = 11 } curl_sslbackend; +/* aliases for library clones and renames */ +#define CURLSSLBACKEND_LIBRESSL 1 +#define CURLSSLBACKEND_BORINGSSL 1 +#define CURLSSLBACKEND_WOLFSSL 6 + /* Information about the SSL library used and the respective internal SSL handle, which can be used to obtain further information regarding the - connection. Asked for with CURLINFO_TLS_SESSION. */ + connection. Asked for with CURLINFO_TLS_SSL_PTR or CURLINFO_TLS_SESSION. */ struct curl_tlssessioninfo { curl_sslbackend backend; void *internals; @@ -2011,6 +2251,7 @@ struct curl_tlssessioninfo { #define CURLINFO_LONG 0x200000 #define CURLINFO_DOUBLE 0x300000 #define CURLINFO_SLIST 0x400000 +#define CURLINFO_SOCKET 0x500000 #define CURLINFO_MASK 0x0fffff #define CURLINFO_TYPEMASK 0xf00000 @@ -2059,9 +2300,15 @@ typedef enum { CURLINFO_LOCAL_IP = CURLINFO_STRING + 41, CURLINFO_LOCAL_PORT = CURLINFO_LONG + 42, CURLINFO_TLS_SESSION = CURLINFO_SLIST + 43, + CURLINFO_ACTIVESOCKET = CURLINFO_SOCKET + 44, + CURLINFO_TLS_SSL_PTR = CURLINFO_SLIST + 45, + CURLINFO_HTTP_VERSION = CURLINFO_LONG + 46, + CURLINFO_PROXY_SSL_VERIFYRESULT = CURLINFO_LONG + 47, + CURLINFO_PROTOCOL = CURLINFO_LONG + 48, + CURLINFO_SCHEME = CURLINFO_STRING + 49, /* Fill in new entries below here! */ - CURLINFO_LASTONE = 43 + CURLINFO_LASTONE = 49 } CURLINFO; /* CURLINFO_RESPONSE_CODE is the new name for the option previously known as @@ -2123,7 +2370,6 @@ typedef void (*curl_unlock_function)(CURL *handle, curl_lock_data data, void *userptr); -typedef void CURLSH; typedef enum { CURLSHE_OK, /* all is fine */ @@ -2197,23 +2443,33 @@ typedef struct { } curl_version_info_data; -#define CURL_VERSION_IPV6 (1<<0) /* IPv6-enabled */ -#define CURL_VERSION_KERBEROS4 (1<<1) /* kerberos auth is supported */ -#define CURL_VERSION_SSL (1<<2) /* SSL options are present */ -#define CURL_VERSION_LIBZ (1<<3) /* libz features are present */ -#define CURL_VERSION_NTLM (1<<4) /* NTLM auth is supported */ -#define CURL_VERSION_GSSNEGOTIATE (1<<5) /* Negotiate auth support */ -#define CURL_VERSION_DEBUG (1<<6) /* built with debug capabilities */ -#define CURL_VERSION_ASYNCHDNS (1<<7) /* asynchronous dns resolves */ -#define CURL_VERSION_SPNEGO (1<<8) /* SPNEGO auth */ -#define CURL_VERSION_LARGEFILE (1<<9) /* supports files bigger than 2GB */ -#define CURL_VERSION_IDN (1<<10) /* International Domain Names support */ -#define CURL_VERSION_SSPI (1<<11) /* SSPI is supported */ -#define CURL_VERSION_CONV (1<<12) /* character conversions supported */ -#define CURL_VERSION_CURLDEBUG (1<<13) /* debug memory tracking supported */ -#define CURL_VERSION_TLSAUTH_SRP (1<<14) /* TLS-SRP auth is supported */ -#define CURL_VERSION_NTLM_WB (1<<15) /* NTLM delegating to winbind helper */ -#define CURL_VERSION_HTTP2 (1<<16) /* HTTP2 support built-in */ +#define CURL_VERSION_IPV6 (1<<0) /* IPv6-enabled */ +#define CURL_VERSION_KERBEROS4 (1<<1) /* Kerberos V4 auth is supported + (deprecated) */ +#define CURL_VERSION_SSL (1<<2) /* SSL options are present */ +#define CURL_VERSION_LIBZ (1<<3) /* libz features are present */ +#define CURL_VERSION_NTLM (1<<4) /* NTLM auth is supported */ +#define CURL_VERSION_GSSNEGOTIATE (1<<5) /* Negotiate auth is supported + (deprecated) */ +#define CURL_VERSION_DEBUG (1<<6) /* Built with debug capabilities */ +#define CURL_VERSION_ASYNCHDNS (1<<7) /* Asynchronous DNS resolves */ +#define CURL_VERSION_SPNEGO (1<<8) /* SPNEGO auth is supported */ +#define CURL_VERSION_LARGEFILE (1<<9) /* Supports files larger than 2GB */ +#define CURL_VERSION_IDN (1<<10) /* Internationized Domain Names are + supported */ +#define CURL_VERSION_SSPI (1<<11) /* Built against Windows SSPI */ +#define CURL_VERSION_CONV (1<<12) /* Character conversions supported */ +#define CURL_VERSION_CURLDEBUG (1<<13) /* Debug memory tracking supported */ +#define CURL_VERSION_TLSAUTH_SRP (1<<14) /* TLS-SRP auth is supported */ +#define CURL_VERSION_NTLM_WB (1<<15) /* NTLM delegation to winbind helper + is supported */ +#define CURL_VERSION_HTTP2 (1<<16) /* HTTP2 support built-in */ +#define CURL_VERSION_GSSAPI (1<<17) /* Built against a GSS-API library */ +#define CURL_VERSION_KERBEROS5 (1<<18) /* Kerberos V5 auth is supported */ +#define CURL_VERSION_UNIX_SOCKETS (1<<19) /* Unix domain sockets support */ +#define CURL_VERSION_PSL (1<<20) /* Mozilla's Public Suffix List, used + for cookie domain verification */ +#define CURL_VERSION_HTTPS_PROXY (1<<21) /* HTTPS-proxy support built-in */ /* * NAME curl_version_info() diff --git a/code/libcurl-7.35.0/curl/curlbuild.h b/code/curl-7.54.0/include/curl/curlbuild.h similarity index 98% rename from code/libcurl-7.35.0/curl/curlbuild.h rename to code/curl-7.54.0/include/curl/curlbuild.h index d7790db599..f596c60704 100644 --- a/code/libcurl-7.35.0/curl/curlbuild.h +++ b/code/curl-7.54.0/include/curl/curlbuild.h @@ -7,11 +7,11 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) 1998 - 2013, Daniel Stenberg, , et al. + * Copyright (C) 1998 - 2016, Daniel Stenberg, , et al. * * This software is licensed as described in the file COPYING, which * you should have received as part of this distribution. The terms - * are also available at http://curl.haxx.se/docs/copyright.html. + * are also available at https://curl.haxx.se/docs/copyright.html. * * You may opt to use, copy, modify, merge, publish, distribute and/or sell * copies of the Software, and permit persons to whom the Software is @@ -49,7 +49,7 @@ * * If you think that something actually needs to be changed, adjusted * or fixed in this file, then, report it on the libcurl development - * mailing list: http://cool.haxx.se/mailman/listinfo/curl-library/ + * mailing list: https://cool.haxx.se/mailman/listinfo/curl-library/ * * Try to keep one section per platform, compiler and architecture, * otherwise, if an existing section is reused for a different one and @@ -527,9 +527,9 @@ /* ===================================== */ #elif defined(__GNUC__) -# if defined(__ILP32__) || \ - defined(__i386__) || defined(__ppc__) || defined(__powerpc__) || \ - defined(__arm__) || defined(__sparc__) +# if !defined(__LP64__) && (defined(__ILP32__) || \ + defined(__i386__) || defined(__ppc__) || defined(__powerpc__) || defined(__arm__) || \ + defined(__sparc__) || defined(__mips__) || defined(__sh__)) # define CURL_SIZEOF_LONG 4 # define CURL_TYPEOF_CURL_OFF_T long long # define CURL_FORMAT_CURL_OFF_T "lld" @@ -539,8 +539,7 @@ # define CURL_SUFFIX_CURL_OFF_T LL # define CURL_SUFFIX_CURL_OFF_TU ULL # elif defined(__LP64__) || \ - defined(__x86_64__) || defined(__ppc64__) || defined(__powerpc64__) || \ - defined(__sparc64__) + defined(__x86_64__) || defined(__ppc64__) || defined(__powerpc64__) || defined(__sparc64__) # define CURL_SIZEOF_LONG 8 # define CURL_TYPEOF_CURL_OFF_T long # define CURL_FORMAT_CURL_OFF_T "ld" diff --git a/code/libcurl-7.35.0/curl/curlrules.h b/code/curl-7.54.0/include/curl/curlrules.h similarity index 91% rename from code/libcurl-7.35.0/curl/curlrules.h rename to code/curl-7.54.0/include/curl/curlrules.h index 7c2ede35b6..0abd9f71d8 100644 --- a/code/libcurl-7.35.0/curl/curlrules.h +++ b/code/curl-7.54.0/include/curl/curlrules.h @@ -7,11 +7,11 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) 1998 - 2012, Daniel Stenberg, , et al. + * Copyright (C) 1998 - 2017, Daniel Stenberg, , et al. * * This software is licensed as described in the file COPYING, which * you should have received as part of this distribution. The terms - * are also available at http://curl.haxx.se/docs/copyright.html. + * are also available at https://curl.haxx.se/docs/copyright.html. * * You may opt to use, copy, modify, merge, publish, distribute and/or sell * copies of the Software, and permit persons to whom the Software is @@ -47,7 +47,7 @@ * library is properly built and used. * * You can find further help on the libcurl development mailing list: - * http://cool.haxx.se/mailman/listinfo/curl-library/ + * https://cool.haxx.se/mailman/listinfo/curl-library/ * * NOTE 2 * ------ @@ -105,11 +105,6 @@ Error Compilation_aborted_CURL_FORMAT_CURL_OFF_TU_is_missing #endif -#ifndef CURL_FORMAT_OFF_T -# error "CURL_FORMAT_OFF_T definition is missing!" - Error Compilation_aborted_CURL_FORMAT_OFF_T_is_missing -#endif - #ifndef CURL_SIZEOF_CURL_OFF_T # error "CURL_SIZEOF_CURL_OFF_T definition is missing!" Error Compilation_aborted_CURL_SIZEOF_CURL_OFF_T_is_missing @@ -241,22 +236,4 @@ typedef char #undef CurlchkszEQ #undef CurlchkszGE -/* - * Get rid of macros not intended to exist beyond this point. - */ - -#undef CURL_PULL_WS2TCPIP_H -#undef CURL_PULL_SYS_TYPES_H -#undef CURL_PULL_SYS_SOCKET_H -#undef CURL_PULL_SYS_POLL_H -#undef CURL_PULL_STDINT_H -#undef CURL_PULL_INTTYPES_H - -#undef CURL_TYPEOF_CURL_SOCKLEN_T -#undef CURL_TYPEOF_CURL_OFF_T - -#ifdef CURL_NO_OLDIES -#undef CURL_FORMAT_OFF_T /* not required since 7.19.0 - obsoleted in 7.20.0 */ -#endif - #endif /* __CURL_CURLRULES_H */ diff --git a/code/libcurl-7.35.0/curl/curlver.h b/code/curl-7.54.0/include/curl/curlver.h similarity index 77% rename from code/libcurl-7.35.0/curl/curlver.h rename to code/curl-7.54.0/include/curl/curlver.h index 2de9e5a563..95a2cbbe78 100644 --- a/code/libcurl-7.35.0/curl/curlver.h +++ b/code/curl-7.54.0/include/curl/curlver.h @@ -7,11 +7,11 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) 1998 - 2014, Daniel Stenberg, , et al. + * Copyright (C) 1998 - 2017, Daniel Stenberg, , et al. * * This software is licensed as described in the file COPYING, which * you should have received as part of this distribution. The terms - * are also available at http://curl.haxx.se/docs/copyright.html. + * are also available at https://curl.haxx.se/docs/copyright.html. * * You may opt to use, copy, modify, merge, publish, distribute and/or sell * copies of the Software, and permit persons to whom the Software is @@ -26,16 +26,16 @@ a script at release-time. This was made its own header file in 7.11.2 */ /* This is the global package copyright */ -#define LIBCURL_COPYRIGHT "1996 - 2014 Daniel Stenberg, ." +#define LIBCURL_COPYRIGHT "1996 - 2017 Daniel Stenberg, ." /* This is the version number of the libcurl package from which this header file origins: */ -#define LIBCURL_VERSION "7.35.0" +#define LIBCURL_VERSION "7.54.0" /* The numeric version number is also available "in parts" by using these defines: */ #define LIBCURL_VERSION_MAJOR 7 -#define LIBCURL_VERSION_MINOR 35 +#define LIBCURL_VERSION_MINOR 54 #define LIBCURL_VERSION_PATCH 0 /* This is the numeric version of the libcurl version number, meant for easier @@ -52,8 +52,12 @@ This 6-digit (24 bits) hexadecimal number does not show pre-release number, and it is always a greater number in a more recent release. It makes comparisons with greater than and less than work. + + Note: This define is the full hex number and _does not_ use the + CURL_VERSION_BITS() macro since curl's own configure script greps for it + and needs it to contain the full number. */ -#define LIBCURL_VERSION_NUM 0x072300 +#define LIBCURL_VERSION_NUM 0x073600 /* * This is the date and time when the full source package was created. The @@ -64,6 +68,10 @@ * * "Mon Feb 12 11:35:33 UTC 2007" */ -#define LIBCURL_TIMESTAMP "Wed Jan 29 07:09:27 UTC 2014" +#define LIBCURL_TIMESTAMP "Wed Apr 19 05:43:55 UTC 2017" + +#define CURL_VERSION_BITS(x,y,z) ((x)<<16|(y)<<8|z) +#define CURL_AT_LEAST_VERSION(x,y,z) \ + (LIBCURL_VERSION_NUM >= CURL_VERSION_BITS(x, y, z)) #endif /* __CURL_CURLVER_H */ diff --git a/code/libcurl-7.35.0/curl/easy.h b/code/curl-7.54.0/include/curl/easy.h similarity index 94% rename from code/libcurl-7.35.0/curl/easy.h rename to code/curl-7.54.0/include/curl/easy.h index c1e3e76096..752c5049f8 100644 --- a/code/libcurl-7.35.0/curl/easy.h +++ b/code/curl-7.54.0/include/curl/easy.h @@ -7,11 +7,11 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) 1998 - 2008, Daniel Stenberg, , et al. + * Copyright (C) 1998 - 2016, Daniel Stenberg, , et al. * * This software is licensed as described in the file COPYING, which * you should have received as part of this distribution. The terms - * are also available at http://curl.haxx.se/docs/copyright.html. + * are also available at https://curl.haxx.se/docs/copyright.html. * * You may opt to use, copy, modify, merge, publish, distribute and/or sell * copies of the Software, and permit persons to whom the Software is @@ -58,7 +58,7 @@ CURL_EXTERN CURLcode curl_easy_getinfo(CURL *curl, CURLINFO info, ...); * curl_easy_duphandle() for each new thread to avoid a series of identical * curl_easy_setopt() invokes in every thread. */ -CURL_EXTERN CURL* curl_easy_duphandle(CURL *curl); +CURL_EXTERN CURL *curl_easy_duphandle(CURL *curl); /* * NAME curl_easy_reset() diff --git a/code/libcurl-7.35.0/curl/mprintf.h b/code/curl-7.54.0/include/curl/mprintf.h similarity index 68% rename from code/libcurl-7.35.0/curl/mprintf.h rename to code/curl-7.54.0/include/curl/mprintf.h index cc9e7f5d1f..e20f546e19 100644 --- a/code/libcurl-7.35.0/curl/mprintf.h +++ b/code/curl-7.54.0/include/curl/mprintf.h @@ -7,11 +7,11 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) 1998 - 2013, Daniel Stenberg, , et al. + * Copyright (C) 1998 - 2016, Daniel Stenberg, , et al. * * This software is licensed as described in the file COPYING, which * you should have received as part of this distribution. The terms - * are also available at http://curl.haxx.se/docs/copyright.html. + * are also available at https://curl.haxx.se/docs/copyright.html. * * You may opt to use, copy, modify, merge, publish, distribute and/or sell * copies of the Software, and permit persons to whom the Software is @@ -24,8 +24,7 @@ #include #include /* needed for FILE */ - -#include "curl.h" +#include "curl.h" /* for CURL_EXTERN */ #ifdef __cplusplus extern "C" { @@ -44,36 +43,6 @@ CURL_EXTERN int curl_mvsnprintf(char *buffer, size_t maxlength, CURL_EXTERN char *curl_maprintf(const char *format, ...); CURL_EXTERN char *curl_mvaprintf(const char *format, va_list args); -#ifdef _MPRINTF_REPLACE -# undef printf -# undef fprintf -# undef sprintf -# undef vsprintf -# undef snprintf -# undef vprintf -# undef vfprintf -# undef vsnprintf -# undef aprintf -# undef vaprintf -# define printf curl_mprintf -# define fprintf curl_mfprintf -#ifdef CURLDEBUG -/* When built with CURLDEBUG we define away the sprintf functions since we - don't want internal code to be using them */ -# define sprintf sprintf_was_used -# define vsprintf vsprintf_was_used -#else -# define sprintf curl_msprintf -# define vsprintf curl_mvsprintf -#endif -# define snprintf curl_msnprintf -# define vprintf curl_mvprintf -# define vfprintf curl_mvfprintf -# define vsnprintf curl_mvsnprintf -# define aprintf curl_maprintf -# define vaprintf curl_mvaprintf -#endif - #ifdef __cplusplus } #endif diff --git a/code/libcurl-7.35.0/curl/multi.h b/code/curl-7.54.0/include/curl/multi.h similarity index 90% rename from code/libcurl-7.35.0/curl/multi.h rename to code/curl-7.54.0/include/curl/multi.h index 3c4acb0f6e..f93e511be0 100644 --- a/code/libcurl-7.35.0/curl/multi.h +++ b/code/curl-7.54.0/include/curl/multi.h @@ -7,11 +7,11 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) 1998 - 2013, Daniel Stenberg, , et al. + * Copyright (C) 1998 - 2016, Daniel Stenberg, , et al. * * This software is licensed as described in the file COPYING, which * you should have received as part of this distribution. The terms - * are also available at http://curl.haxx.se/docs/copyright.html. + * are also available at https://curl.haxx.se/docs/copyright.html. * * You may opt to use, copy, modify, merge, publish, distribute and/or sell * copies of the Software, and permit persons to whom the Software is @@ -52,7 +52,11 @@ extern "C" { #endif +#if defined(BUILDING_LIBCURL) || defined(CURL_STRICTER) +typedef struct Curl_multi CURLM; +#else typedef void CURLM; +#endif typedef enum { CURLM_CALL_MULTI_PERFORM = -1, /* please call curl_multi_perform() or @@ -74,6 +78,11 @@ typedef enum { curl_multi_perform() and CURLM_CALL_MULTI_PERFORM */ #define CURLM_CALL_MULTI_SOCKET CURLM_CALL_MULTI_PERFORM +/* bitmask bits for CURLMOPT_PIPELINING */ +#define CURLPIPE_NOTHING 0L +#define CURLPIPE_HTTP1 1L +#define CURLPIPE_MULTIPLEX 2L + typedef enum { CURLMSG_NONE, /* first, not used */ CURLMSG_DONE, /* This easy handle has completed. 'result' contains @@ -209,7 +218,7 @@ CURL_EXTERN CURLMcode curl_multi_cleanup(CURLM *multi_handle); * curl_multi_cleanup(). * * The 'CURLMsg' struct is meant to be very simple and only contain - * very basic informations. If more involved information is wanted, + * very basic information. If more involved information is wanted, * we will provide the particular "transfer handle" in that struct * and that should/could/would be used in subsequent * curl_easy_getinfo() calls (or similar). The point being that we @@ -365,6 +374,12 @@ typedef enum { /* maximum number of open connections in total */ CINIT(MAX_TOTAL_CONNECTIONS, LONG, 13), + /* This is the server push callback function pointer */ + CINIT(PUSHFUNCTION, FUNCTIONPOINT, 14), + + /* This is the argument passed to the server push callback */ + CINIT(PUSHDATA, OBJECTPOINT, 15), + CURLMOPT_LASTENTRY /* the last unused */ } CURLMoption; @@ -392,6 +407,31 @@ CURL_EXTERN CURLMcode curl_multi_setopt(CURLM *multi_handle, CURL_EXTERN CURLMcode curl_multi_assign(CURLM *multi_handle, curl_socket_t sockfd, void *sockp); + +/* + * Name: curl_push_callback + * + * Desc: This callback gets called when a new stream is being pushed by the + * server. It approves or denies the new stream. + * + * Returns: CURL_PUSH_OK or CURL_PUSH_DENY. + */ +#define CURL_PUSH_OK 0 +#define CURL_PUSH_DENY 1 + +struct curl_pushheaders; /* forward declaration only */ + +CURL_EXTERN char *curl_pushheader_bynum(struct curl_pushheaders *h, + size_t num); +CURL_EXTERN char *curl_pushheader_byname(struct curl_pushheaders *h, + const char *name); + +typedef int (*curl_push_callback)(CURL *parent, + CURL *easy, + size_t num_headers, + struct curl_pushheaders *headers, + void *userp); + #ifdef __cplusplus } /* end of extern "C" */ #endif diff --git a/code/libcurl-7.35.0/curl/stdcheaders.h b/code/curl-7.54.0/include/curl/stdcheaders.h similarity index 82% rename from code/libcurl-7.35.0/curl/stdcheaders.h rename to code/curl-7.54.0/include/curl/stdcheaders.h index ad82ef6335..027b6f4211 100644 --- a/code/libcurl-7.35.0/curl/stdcheaders.h +++ b/code/curl-7.54.0/include/curl/stdcheaders.h @@ -7,11 +7,11 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) 1998 - 2010, Daniel Stenberg, , et al. + * Copyright (C) 1998 - 2016, Daniel Stenberg, , et al. * * This software is licensed as described in the file COPYING, which * you should have received as part of this distribution. The terms - * are also available at http://curl.haxx.se/docs/copyright.html. + * are also available at https://curl.haxx.se/docs/copyright.html. * * You may opt to use, copy, modify, merge, publish, distribute and/or sell * copies of the Software, and permit persons to whom the Software is @@ -24,8 +24,8 @@ #include -size_t fread (void *, size_t, size_t, FILE *); -size_t fwrite (const void *, size_t, size_t, FILE *); +size_t fread(void *, size_t, size_t, FILE *); +size_t fwrite(const void *, size_t, size_t, FILE *); int strcasecmp(const char *, const char *); int strncasecmp(const char *, const char *, size_t); diff --git a/code/curl-7.54.0/include/curl/system.h b/code/curl-7.54.0/include/curl/system.h new file mode 100644 index 0000000000..ed3a55c954 --- /dev/null +++ b/code/curl-7.54.0/include/curl/system.h @@ -0,0 +1,484 @@ +#ifndef __CURL_SYSTEM_H +#define __CURL_SYSTEM_H +/*************************************************************************** + * _ _ ____ _ + * Project ___| | | | _ \| | + * / __| | | | |_) | | + * | (__| |_| | _ <| |___ + * \___|\___/|_| \_\_____| + * + * Copyright (C) 1998 - 2017, Daniel Stenberg, , et al. + * + * This software is licensed as described in the file COPYING, which + * you should have received as part of this distribution. The terms + * are also available at https://curl.haxx.se/docs/copyright.html. + * + * You may opt to use, copy, modify, merge, publish, distribute and/or sell + * copies of the Software, and permit persons to whom the Software is + * furnished to do so, under the terms of the COPYING file. + * + * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY + * KIND, either express or implied. + * + ***************************************************************************/ + +/* + * This header is supposed to eventually replace curlbuild.h. This little one + * is still learning. During the experimental phase, this header files + * defines symbols using the prefixes CURLSYS_ or curlsys_. When we feel + * confident enough, we replace curlbuild.h with this file and rename all + * prefixes to CURL_ and curl_. + */ + +/* + * Try to keep one section per platform, compiler and architecture, otherwise, + * if an existing section is reused for a different one and later on the + * original is adjusted, probably the piggybacking one can be adversely + * changed. + * + * In order to differentiate between platforms/compilers/architectures use + * only compiler built in predefined preprocessor symbols. + * + * curl_off_t + * ---------- + * + * For any given platform/compiler curl_off_t must be typedef'ed to a 64-bit + * wide signed integral data type. The width of this data type must remain + * constant and independent of any possible large file support settings. + * + * As an exception to the above, curl_off_t shall be typedef'ed to a 32-bit + * wide signed integral data type if there is no 64-bit type. + * + * As a general rule, curl_off_t shall not be mapped to off_t. This rule shall + * only be violated if off_t is the only 64-bit data type available and the + * size of off_t is independent of large file support settings. Keep your + * build on the safe side avoiding an off_t gating. If you have a 64-bit + * off_t then take for sure that another 64-bit data type exists, dig deeper + * and you will find it. + * + */ + +#if defined(__DJGPP__) || defined(__GO32__) +# if defined(__DJGPP__) && (__DJGPP__ > 1) +# define CURLSYS_SIZEOF_LONG 4 +# define CURLSYS_TYPEOF_CURL_OFF_T long long +# define CURLSYS_FORMAT_CURL_OFF_T "lld" +# define CURLSYS_FORMAT_CURL_OFF_TU "llu" +# define CURLSYS_SIZEOF_CURL_OFF_T 8 +# define CURLSYS_SUFFIX_CURL_OFF_T LL +# define CURLSYS_SUFFIX_CURL_OFF_TU ULL +# else +# define CURLSYS_SIZEOF_LONG 4 +# define CURLSYS_TYPEOF_CURL_OFF_T long +# define CURLSYS_FORMAT_CURL_OFF_T "ld" +# define CURLSYS_FORMAT_CURL_OFF_TU "lu" +# define CURLSYS_SIZEOF_CURL_OFF_T 4 +# define CURLSYS_SUFFIX_CURL_OFF_T L +# define CURLSYS_SUFFIX_CURL_OFF_TU UL +# endif +# define CURLSYS_TYPEOF_CURL_SOCKLEN_T int +# define CURLSYS_SIZEOF_CURL_SOCKLEN_T 4 + +#elif defined(__SALFORDC__) +# define CURLSYS_SIZEOF_LONG 4 +# define CURLSYS_TYPEOF_CURL_OFF_T long +# define CURLSYS_FORMAT_CURL_OFF_T "ld" +# define CURLSYS_FORMAT_CURL_OFF_TU "lu" +# define CURLSYS_SIZEOF_CURL_OFF_T 4 +# define CURLSYS_SUFFIX_CURL_OFF_T L +# define CURLSYS_SUFFIX_CURL_OFF_TU UL +# define CURLSYS_TYPEOF_CURL_SOCKLEN_T int +# define CURLSYS_SIZEOF_CURL_SOCKLEN_T 4 + +#elif defined(__BORLANDC__) +# if (__BORLANDC__ < 0x520) +# define CURLSYS_SIZEOF_LONG 4 +# define CURLSYS_TYPEOF_CURL_OFF_T long +# define CURLSYS_FORMAT_CURL_OFF_T "ld" +# define CURLSYS_FORMAT_CURL_OFF_TU "lu" +# define CURLSYS_SIZEOF_CURL_OFF_T 4 +# define CURLSYS_SUFFIX_CURL_OFF_T L +# define CURLSYS_SUFFIX_CURL_OFF_TU UL +# else +# define CURLSYS_SIZEOF_LONG 4 +# define CURLSYS_TYPEOF_CURL_OFF_T __int64 +# define CURLSYS_FORMAT_CURL_OFF_T "I64d" +# define CURLSYS_FORMAT_CURL_OFF_TU "I64u" +# define CURLSYS_SIZEOF_CURL_OFF_T 8 +# define CURLSYS_SUFFIX_CURL_OFF_T i64 +# define CURLSYS_SUFFIX_CURL_OFF_TU ui64 +# endif +# define CURLSYS_TYPEOF_CURL_SOCKLEN_T int +# define CURLSYS_SIZEOF_CURL_SOCKLEN_T 4 + +#elif defined(__TURBOC__) +# define CURLSYS_SIZEOF_LONG 4 +# define CURLSYS_TYPEOF_CURL_OFF_T long +# define CURLSYS_FORMAT_CURL_OFF_T "ld" +# define CURLSYS_FORMAT_CURL_OFF_TU "lu" +# define CURLSYS_SIZEOF_CURL_OFF_T 4 +# define CURLSYS_SUFFIX_CURL_OFF_T L +# define CURLSYS_SUFFIX_CURL_OFF_TU UL +# define CURLSYS_TYPEOF_CURL_SOCKLEN_T int +# define CURLSYS_SIZEOF_CURL_SOCKLEN_T 4 + +#elif defined(__WATCOMC__) +# if defined(__386__) +# define CURLSYS_SIZEOF_LONG 4 +# define CURLSYS_TYPEOF_CURL_OFF_T __int64 +# define CURLSYS_FORMAT_CURL_OFF_T "I64d" +# define CURLSYS_FORMAT_CURL_OFF_TU "I64u" +# define CURLSYS_SIZEOF_CURL_OFF_T 8 +# define CURLSYS_SUFFIX_CURL_OFF_T i64 +# define CURLSYS_SUFFIX_CURL_OFF_TU ui64 +# else +# define CURLSYS_SIZEOF_LONG 4 +# define CURLSYS_TYPEOF_CURL_OFF_T long +# define CURLSYS_FORMAT_CURL_OFF_T "ld" +# define CURLSYS_FORMAT_CURL_OFF_TU "lu" +# define CURLSYS_SIZEOF_CURL_OFF_T 4 +# define CURLSYS_SUFFIX_CURL_OFF_T L +# define CURLSYS_SUFFIX_CURL_OFF_TU UL +# endif +# define CURLSYS_TYPEOF_CURL_SOCKLEN_T int +# define CURLSYS_SIZEOF_CURL_SOCKLEN_T 4 + +#elif defined(__POCC__) +# if (__POCC__ < 280) +# define CURLSYS_SIZEOF_LONG 4 +# define CURLSYS_TYPEOF_CURL_OFF_T long +# define CURLSYS_FORMAT_CURL_OFF_T "ld" +# define CURLSYS_FORMAT_CURL_OFF_TU "lu" +# define CURLSYS_SIZEOF_CURL_OFF_T 4 +# define CURLSYS_SUFFIX_CURL_OFF_T L +# define CURLSYS_SUFFIX_CURL_OFF_TU UL +# elif defined(_MSC_VER) +# define CURLSYS_SIZEOF_LONG 4 +# define CURLSYS_TYPEOF_CURL_OFF_T __int64 +# define CURLSYS_FORMAT_CURL_OFF_T "I64d" +# define CURLSYS_FORMAT_CURL_OFF_TU "I64u" +# define CURLSYS_SIZEOF_CURL_OFF_T 8 +# define CURLSYS_SUFFIX_CURL_OFF_T i64 +# define CURLSYS_SUFFIX_CURL_OFF_TU ui64 +# else +# define CURLSYS_SIZEOF_LONG 4 +# define CURLSYS_TYPEOF_CURL_OFF_T long long +# define CURLSYS_FORMAT_CURL_OFF_T "lld" +# define CURLSYS_FORMAT_CURL_OFF_TU "llu" +# define CURLSYS_SIZEOF_CURL_OFF_T 8 +# define CURLSYS_SUFFIX_CURL_OFF_T LL +# define CURLSYS_SUFFIX_CURL_OFF_TU ULL +# endif +# define CURLSYS_TYPEOF_CURL_SOCKLEN_T int +# define CURLSYS_SIZEOF_CURL_SOCKLEN_T 4 + +#elif defined(__LCC__) +# define CURLSYS_SIZEOF_LONG 4 +# define CURLSYS_TYPEOF_CURL_OFF_T long +# define CURLSYS_FORMAT_CURL_OFF_T "ld" +# define CURLSYS_FORMAT_CURL_OFF_TU "lu" +# define CURLSYS_SIZEOF_CURL_OFF_T 4 +# define CURLSYS_SUFFIX_CURL_OFF_T L +# define CURLSYS_SUFFIX_CURL_OFF_TU UL +# define CURLSYS_TYPEOF_CURL_SOCKLEN_T int +# define CURLSYS_SIZEOF_CURL_SOCKLEN_T 4 + +#elif defined(__SYMBIAN32__) +# if defined(__EABI__) /* Treat all ARM compilers equally */ +# define CURLSYS_SIZEOF_LONG 4 +# define CURLSYS_TYPEOF_CURL_OFF_T long long +# define CURLSYS_FORMAT_CURL_OFF_T "lld" +# define CURLSYS_FORMAT_CURL_OFF_TU "llu" +# define CURLSYS_SIZEOF_CURL_OFF_T 8 +# define CURLSYS_SUFFIX_CURL_OFF_T LL +# define CURLSYS_SUFFIX_CURL_OFF_TU ULL +# elif defined(__CW32__) +# pragma longlong on +# define CURLSYS_SIZEOF_LONG 4 +# define CURLSYS_TYPEOF_CURL_OFF_T long long +# define CURLSYS_FORMAT_CURL_OFF_T "lld" +# define CURLSYS_FORMAT_CURL_OFF_TU "llu" +# define CURLSYS_SIZEOF_CURL_OFF_T 8 +# define CURLSYS_SUFFIX_CURL_OFF_T LL +# define CURLSYS_SUFFIX_CURL_OFF_TU ULL +# elif defined(__VC32__) +# define CURLSYS_SIZEOF_LONG 4 +# define CURLSYS_TYPEOF_CURL_OFF_T __int64 +# define CURLSYS_FORMAT_CURL_OFF_T "lld" +# define CURLSYS_FORMAT_CURL_OFF_TU "llu" +# define CURLSYS_SIZEOF_CURL_OFF_T 8 +# define CURLSYS_SUFFIX_CURL_OFF_T LL +# define CURLSYS_SUFFIX_CURL_OFF_TU ULL +# endif +# define CURLSYS_TYPEOF_CURL_SOCKLEN_T unsigned int +# define CURLSYS_SIZEOF_CURL_SOCKLEN_T 4 + +#elif defined(__MWERKS__) +# define CURLSYS_SIZEOF_LONG 4 +# define CURLSYS_TYPEOF_CURL_OFF_T long long +# define CURLSYS_FORMAT_CURL_OFF_T "lld" +# define CURLSYS_FORMAT_CURL_OFF_TU "llu" +# define CURLSYS_SIZEOF_CURL_OFF_T 8 +# define CURLSYS_SUFFIX_CURL_OFF_T LL +# define CURLSYS_SUFFIX_CURL_OFF_TU ULL +# define CURLSYS_TYPEOF_CURL_SOCKLEN_T int +# define CURLSYS_SIZEOF_CURL_SOCKLEN_T 4 + +#elif defined(_WIN32_WCE) +# define CURLSYS_SIZEOF_LONG 4 +# define CURLSYS_TYPEOF_CURL_OFF_T __int64 +# define CURLSYS_FORMAT_CURL_OFF_T "I64d" +# define CURLSYS_FORMAT_CURL_OFF_TU "I64u" +# define CURLSYS_SIZEOF_CURL_OFF_T 8 +# define CURLSYS_SUFFIX_CURL_OFF_T i64 +# define CURLSYS_SUFFIX_CURL_OFF_TU ui64 +# define CURLSYS_TYPEOF_CURL_SOCKLEN_T int +# define CURLSYS_SIZEOF_CURL_SOCKLEN_T 4 + +#elif defined(__MINGW32__) +# define CURLSYS_SIZEOF_LONG 4 +# define CURLSYS_TYPEOF_CURL_OFF_T long long +# define CURLSYS_FORMAT_CURL_OFF_T "I64d" +# define CURLSYS_FORMAT_CURL_OFF_TU "I64u" +# define CURLSYS_SIZEOF_CURL_OFF_T 8 +# define CURLSYS_SUFFIX_CURL_OFF_T LL +# define CURLSYS_SUFFIX_CURL_OFF_TU ULL +# define CURLSYS_TYPEOF_CURL_SOCKLEN_T socklen_t +# define CURLSYS_SIZEOF_CURL_SOCKLEN_T 4 +# define CURLSYS_PULL_SYS_TYPES_H 1 +# define CURLSYS_PULL_WS2TCPIP_H 1 + +#elif defined(__VMS) +# if defined(__VAX) +# define CURLSYS_SIZEOF_LONG 4 +# define CURLSYS_TYPEOF_CURL_OFF_T long +# define CURLSYS_FORMAT_CURL_OFF_T "ld" +# define CURLSYS_FORMAT_CURL_OFF_TU "lu" +# define CURLSYS_SIZEOF_CURL_OFF_T 4 +# define CURLSYS_SUFFIX_CURL_OFF_T L +# define CURLSYS_SUFFIX_CURL_OFF_TU UL +# else +# define CURLSYS_SIZEOF_LONG 4 +# define CURLSYS_TYPEOF_CURL_OFF_T long long +# define CURLSYS_FORMAT_CURL_OFF_T "lld" +# define CURLSYS_FORMAT_CURL_OFF_TU "llu" +# define CURLSYS_SIZEOF_CURL_OFF_T 8 +# define CURLSYS_SUFFIX_CURL_OFF_T LL +# define CURLSYS_SUFFIX_CURL_OFF_TU ULL +# endif +# define CURLSYS_TYPEOF_CURL_SOCKLEN_T unsigned int +# define CURLSYS_SIZEOF_CURL_SOCKLEN_T 4 + +#elif defined(__OS400__) +# if defined(__ILEC400__) +# define CURLSYS_SIZEOF_LONG 4 +# define CURLSYS_TYPEOF_CURL_OFF_T long long +# define CURLSYS_FORMAT_CURL_OFF_T "lld" +# define CURLSYS_FORMAT_CURL_OFF_TU "llu" +# define CURLSYS_SIZEOF_CURL_OFF_T 8 +# define CURLSYS_SUFFIX_CURL_OFF_T LL +# define CURLSYS_SUFFIX_CURL_OFF_TU ULL +# define CURLSYS_TYPEOF_CURL_SOCKLEN_T socklen_t +# define CURLSYS_SIZEOF_CURL_SOCKLEN_T 4 +# define CURLSYS_PULL_SYS_TYPES_H 1 +# define CURLSYS_PULL_SYS_SOCKET_H 1 +# endif + +#elif defined(__MVS__) +# if defined(__IBMC__) || defined(__IBMCPP__) +# if defined(_ILP32) +# define CURLSYS_SIZEOF_LONG 4 +# elif defined(_LP64) +# define CURLSYS_SIZEOF_LONG 8 +# endif +# if defined(_LONG_LONG) +# define CURLSYS_TYPEOF_CURL_OFF_T long long +# define CURLSYS_FORMAT_CURL_OFF_T "lld" +# define CURLSYS_FORMAT_CURL_OFF_TU "llu" +# define CURLSYS_SIZEOF_CURL_OFF_T 8 +# define CURLSYS_SUFFIX_CURL_OFF_T LL +# define CURLSYS_SUFFIX_CURL_OFF_TU ULL +# elif defined(_LP64) +# define CURLSYS_TYPEOF_CURL_OFF_T long +# define CURLSYS_FORMAT_CURL_OFF_T "ld" +# define CURLSYS_FORMAT_CURL_OFF_TU "lu" +# define CURLSYS_SIZEOF_CURL_OFF_T 8 +# define CURLSYS_SUFFIX_CURL_OFF_T L +# define CURLSYS_SUFFIX_CURL_OFF_TU UL +# else +# define CURLSYS_TYPEOF_CURL_OFF_T long +# define CURLSYS_FORMAT_CURL_OFF_T "ld" +# define CURLSYS_FORMAT_CURL_OFF_TU "lu" +# define CURLSYS_SIZEOF_CURL_OFF_T 4 +# define CURLSYS_SUFFIX_CURL_OFF_T L +# define CURLSYS_SUFFIX_CURL_OFF_TU UL +# endif +# define CURLSYS_TYPEOF_CURL_SOCKLEN_T socklen_t +# define CURLSYS_SIZEOF_CURL_SOCKLEN_T 4 +# define CURLSYS_PULL_SYS_TYPES_H 1 +# define CURLSYS_PULL_SYS_SOCKET_H 1 +# endif + +#elif defined(__370__) +# if defined(__IBMC__) || defined(__IBMCPP__) +# if defined(_ILP32) +# define CURLSYS_SIZEOF_LONG 4 +# elif defined(_LP64) +# define CURLSYS_SIZEOF_LONG 8 +# endif +# if defined(_LONG_LONG) +# define CURLSYS_TYPEOF_CURL_OFF_T long long +# define CURLSYS_FORMAT_CURL_OFF_T "lld" +# define CURLSYS_FORMAT_CURL_OFF_TU "llu" +# define CURLSYS_SIZEOF_CURL_OFF_T 8 +# define CURLSYS_SUFFIX_CURL_OFF_T LL +# define CURLSYS_SUFFIX_CURL_OFF_TU ULL +# elif defined(_LP64) +# define CURLSYS_TYPEOF_CURL_OFF_T long +# define CURLSYS_FORMAT_CURL_OFF_T "ld" +# define CURLSYS_FORMAT_CURL_OFF_TU "lu" +# define CURLSYS_SIZEOF_CURL_OFF_T 8 +# define CURLSYS_SUFFIX_CURL_OFF_T L +# define CURLSYS_SUFFIX_CURL_OFF_TU UL +# else +# define CURLSYS_TYPEOF_CURL_OFF_T long +# define CURLSYS_FORMAT_CURL_OFF_T "ld" +# define CURLSYS_FORMAT_CURL_OFF_TU "lu" +# define CURLSYS_SIZEOF_CURL_OFF_T 4 +# define CURLSYS_SUFFIX_CURL_OFF_T L +# define CURLSYS_SUFFIX_CURL_OFF_TU UL +# endif +# define CURLSYS_TYPEOF_CURL_SOCKLEN_T socklen_t +# define CURLSYS_SIZEOF_CURL_SOCKLEN_T 4 +# define CURLSYS_PULL_SYS_TYPES_H 1 +# define CURLSYS_PULL_SYS_SOCKET_H 1 +# endif + +#elif defined(TPF) +# define CURLSYS_SIZEOF_LONG 8 +# define CURLSYS_TYPEOF_CURL_OFF_T long +# define CURLSYS_FORMAT_CURL_OFF_T "ld" +# define CURLSYS_FORMAT_CURL_OFF_TU "lu" +# define CURLSYS_SIZEOF_CURL_OFF_T 8 +# define CURLSYS_SUFFIX_CURL_OFF_T L +# define CURLSYS_SUFFIX_CURL_OFF_TU UL +# define CURLSYS_TYPEOF_CURL_SOCKLEN_T int +# define CURLSYS_SIZEOF_CURL_SOCKLEN_T 4 + +#elif defined(__TINYC__) /* also known as tcc */ + +# define CURLSYS_SIZEOF_LONG 4 +# define CURLSYS_TYPEOF_CURL_OFF_T long long +# define CURLSYS_FORMAT_CURL_OFF_T "lld" +# define CURLSYS_FORMAT_CURL_OFF_TU "llu" +# define CURLSYS_SIZEOF_CURL_OFF_T 8 +# define CURLSYS_SUFFIX_CURL_OFF_T LL +# define CURLSYS_SUFFIX_CURL_OFF_TU ULL +# define CURLSYS_TYPEOF_CURL_SOCKLEN_T socklen_t +# define CURLSYS_PULL_SYS_TYPES_H 1 +# define CURLSYS_PULL_SYS_SOCKET_H 1 + +/* ===================================== */ +/* KEEP MSVC THE PENULTIMATE ENTRY */ +/* ===================================== */ + +#elif defined(_MSC_VER) +# if (_MSC_VER >= 900) && (_INTEGRAL_MAX_BITS >= 64) +# define CURLSYS_SIZEOF_LONG 4 +# define CURLSYS_TYPEOF_CURL_OFF_T __int64 +# define CURLSYS_FORMAT_CURL_OFF_T "I64d" +# define CURLSYS_FORMAT_CURL_OFF_TU "I64u" +# define CURLSYS_SIZEOF_CURL_OFF_T 8 +# define CURLSYS_SUFFIX_CURL_OFF_T i64 +# define CURLSYS_SUFFIX_CURL_OFF_TU ui64 +# else +# define CURLSYS_SIZEOF_LONG 4 +# define CURLSYS_TYPEOF_CURL_OFF_T long +# define CURLSYS_FORMAT_CURL_OFF_T "ld" +# define CURLSYS_FORMAT_CURL_OFF_TU "lu" +# define CURLSYS_SIZEOF_CURL_OFF_T 4 +# define CURLSYS_SUFFIX_CURL_OFF_T L +# define CURLSYS_SUFFIX_CURL_OFF_TU UL +# endif +# define CURLSYS_TYPEOF_CURL_SOCKLEN_T int +# define CURLSYS_SIZEOF_CURL_SOCKLEN_T 4 + +/* ===================================== */ +/* KEEP GENERIC GCC THE LAST ENTRY */ +/* ===================================== */ + +#elif defined(__GNUC__) +# if !defined(__LP64__) && (defined(__ILP32__) || \ + defined(__i386__) || defined(__ppc__) || defined(__arm__) || \ + defined(__sparc__) || defined(__mips__) || defined(__sh__)) +# define CURLSYS_SIZEOF_LONG 4 +# define CURLSYS_TYPEOF_CURL_OFF_T long long +# define CURLSYS_FORMAT_CURL_OFF_T "lld" +# define CURLSYS_FORMAT_CURL_OFF_TU "llu" +# define CURLSYS_SIZEOF_CURL_OFF_T 8 +# define CURLSYS_SUFFIX_CURL_OFF_T LL +# define CURLSYS_SUFFIX_CURL_OFF_TU ULL +# elif defined(__LP64__) || \ + defined(__x86_64__) || defined(__ppc64__) || defined(__sparc64__) +# define CURLSYS_SIZEOF_LONG 8 +# define CURLSYS_TYPEOF_CURL_OFF_T long +# define CURLSYS_FORMAT_CURL_OFF_T "ld" +# define CURLSYS_FORMAT_CURL_OFF_TU "lu" +# define CURLSYS_SIZEOF_CURL_OFF_T 8 +# define CURLSYS_SUFFIX_CURL_OFF_T L +# define CURLSYS_SUFFIX_CURL_OFF_TU UL +# endif +# define CURLSYS_TYPEOF_CURL_SOCKLEN_T socklen_t +# define CURLSYS_SIZEOF_CURL_SOCKLEN_T 4 +# define CURLSYS_PULL_SYS_TYPES_H 1 +# define CURLSYS_PULL_SYS_SOCKET_H 1 + +#else +/* generic "safe guess" on old 32 bit style */ +# define CURLSYS_SIZEOF_LONG 4 +# define CURLSYS_SIZEOF_CURL_SOCKLEN_T 4 +# define CURLSYS_SIZEOF_CURL_OFF_T 4 +# define CURLSYS_TYPEOF_CURL_OFF_T long +# define CURLSYS_FORMAT_CURL_OFF_T "ld" +# define CURLSYS_FORMAT_CURL_OFF_TU "lu" +# define CURLSYS_SUFFIX_CURL_OFF_T L +# define CURLSYS_SUFFIX_CURL_OFF_TU UL +# define CURLSYS_TYPEOF_CURL_SOCKLEN_T int +#endif + +/* CURLSYS_PULL_WS2TCPIP_H is defined above when inclusion of header file */ +/* ws2tcpip.h is required here to properly make type definitions below. */ +#ifdef CURLSYS_PULL_WS2TCPIP_H +# ifndef WIN32_LEAN_AND_MEAN +# define WIN32_LEAN_AND_MEAN +# endif +# include +# include +# include +#endif + +/* CURLSYS_PULL_SYS_TYPES_H is defined above when inclusion of header file */ +/* sys/types.h is required here to properly make type definitions below. */ +#ifdef CURLSYS_PULL_SYS_TYPES_H +# include +#endif + +/* CURLSYS_PULL_SYS_SOCKET_H is defined above when inclusion of header file */ +/* sys/socket.h is required here to properly make type definitions below. */ +#ifdef CURLSYS_PULL_SYS_SOCKET_H +# include +#endif + +/* Data type definition of curl_socklen_t. */ +#ifdef CURLSYS_TYPEOF_CURL_SOCKLEN_T + typedef CURLSYS_TYPEOF_CURL_SOCKLEN_T curlsys_socklen_t; +#endif + +/* Data type definition of curl_off_t. */ + +#ifdef CURLSYS_TYPEOF_CURL_OFF_T + typedef CURLSYS_TYPEOF_CURL_OFF_T curlsys_off_t; +#endif + +#endif /* __CURL_SYSTEM_H */ + diff --git a/code/libcurl-7.35.0/curl/typecheck-gcc.h b/code/curl-7.54.0/include/curl/typecheck-gcc.h similarity index 93% rename from code/libcurl-7.35.0/curl/typecheck-gcc.h rename to code/curl-7.54.0/include/curl/typecheck-gcc.h index cdeba21a29..3d683152b6 100644 --- a/code/libcurl-7.35.0/curl/typecheck-gcc.h +++ b/code/curl-7.54.0/include/curl/typecheck-gcc.h @@ -7,11 +7,11 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) 1998 - 2013, Daniel Stenberg, , et al. + * Copyright (C) 1998 - 2016, Daniel Stenberg, , et al. * * This software is licensed as described in the file COPYING, which * you should have received as part of this distribution. The terms - * are also available at http://curl.haxx.se/docs/copyright.html. + * are also available at https://curl.haxx.se/docs/copyright.html. * * You may opt to use, copy, modify, merge, publish, distribute and/or sell * copies of the Software, and permit persons to whom the Software is @@ -40,7 +40,7 @@ */ #define curl_easy_setopt(handle, option, value) \ __extension__ ({ \ - __typeof__ (option) _curl_opt = option; \ + __typeof__(option) _curl_opt = option; \ if(__builtin_constant_p(_curl_opt)) { \ if(_curl_is_long_option(_curl_opt)) \ if(!_curl_is_long(value)) \ @@ -110,7 +110,7 @@ __extension__ ({ \ /* FIXME: don't allow const pointers */ #define curl_easy_getinfo(handle, info, arg) \ __extension__ ({ \ - __typeof__ (info) _curl_info = info; \ + __typeof__(info) _curl_info = info; \ if(__builtin_constant_p(_curl_info)) { \ if(_curl_is_string_info(_curl_info)) \ if(!_curl_is_arr((arg), char *)) \ @@ -151,7 +151,7 @@ _CURL_WARNING(_curl_easy_setopt_err_curl_off_t, "curl_easy_setopt expects a curl_off_t argument for this option") _CURL_WARNING(_curl_easy_setopt_err_string, "curl_easy_setopt expects a " - "string (char* or char[]) argument for this option" + "string ('char *' or char[]) argument for this option" ) _CURL_WARNING(_curl_easy_setopt_err_write_callback, "curl_easy_setopt expects a curl_write_callback argument for this option") @@ -182,24 +182,25 @@ _CURL_WARNING(_curl_easy_setopt_err_error_buffer, "curl_easy_setopt expects a " "char buffer of CURL_ERROR_SIZE as argument for this option") _CURL_WARNING(_curl_easy_setopt_err_FILE, - "curl_easy_setopt expects a FILE* argument for this option") + "curl_easy_setopt expects a 'FILE *' argument for this option") _CURL_WARNING(_curl_easy_setopt_err_postfields, - "curl_easy_setopt expects a void* or char* argument for this option") + "curl_easy_setopt expects a 'void *' or 'char *' argument for this option") _CURL_WARNING(_curl_easy_setopt_err_curl_httpost, - "curl_easy_setopt expects a struct curl_httppost* argument for this option") + "curl_easy_setopt expects a 'struct curl_httppost *' " + "argument for this option") _CURL_WARNING(_curl_easy_setopt_err_curl_slist, - "curl_easy_setopt expects a struct curl_slist* argument for this option") + "curl_easy_setopt expects a 'struct curl_slist *' argument for this option") _CURL_WARNING(_curl_easy_setopt_err_CURLSH, "curl_easy_setopt expects a CURLSH* argument for this option") _CURL_WARNING(_curl_easy_getinfo_err_string, - "curl_easy_getinfo expects a pointer to char * for this info") + "curl_easy_getinfo expects a pointer to 'char *' for this info") _CURL_WARNING(_curl_easy_getinfo_err_long, "curl_easy_getinfo expects a pointer to long for this info") _CURL_WARNING(_curl_easy_getinfo_err_double, "curl_easy_getinfo expects a pointer to double for this info") _CURL_WARNING(_curl_easy_getinfo_err_curl_slist, - "curl_easy_getinfo expects a pointer to struct curl_slist * for this info") + "curl_easy_getinfo expects a pointer to 'struct curl_slist *' for this info") /* groups of curl_easy_setops options that take the same type of argument */ @@ -218,58 +219,68 @@ _CURL_WARNING(_curl_easy_getinfo_err_curl_slist, /* evaluates to true if option takes a char* argument */ #define _curl_is_string_option(option) \ - ((option) == CURLOPT_URL || \ - (option) == CURLOPT_PROXY || \ - (option) == CURLOPT_INTERFACE || \ - (option) == CURLOPT_NETRC_FILE || \ - (option) == CURLOPT_USERPWD || \ - (option) == CURLOPT_USERNAME || \ - (option) == CURLOPT_PASSWORD || \ - (option) == CURLOPT_PROXYUSERPWD || \ - (option) == CURLOPT_PROXYUSERNAME || \ - (option) == CURLOPT_PROXYPASSWORD || \ - (option) == CURLOPT_NOPROXY || \ + ((option) == CURLOPT_ABSTRACT_UNIX_SOCKET || \ (option) == CURLOPT_ACCEPT_ENCODING || \ - (option) == CURLOPT_REFERER || \ - (option) == CURLOPT_USERAGENT || \ + (option) == CURLOPT_CAINFO || \ + (option) == CURLOPT_CAPATH || \ (option) == CURLOPT_COOKIE || \ (option) == CURLOPT_COOKIEFILE || \ (option) == CURLOPT_COOKIEJAR || \ (option) == CURLOPT_COOKIELIST || \ + (option) == CURLOPT_CRLFILE || \ + (option) == CURLOPT_CUSTOMREQUEST || \ + (option) == CURLOPT_DEFAULT_PROTOCOL || \ + (option) == CURLOPT_DNS_INTERFACE || \ + (option) == CURLOPT_DNS_LOCAL_IP4 || \ + (option) == CURLOPT_DNS_LOCAL_IP6 || \ + (option) == CURLOPT_DNS_SERVERS || \ + (option) == CURLOPT_EGDSOCKET || \ (option) == CURLOPT_FTPPORT || \ - (option) == CURLOPT_FTP_ALTERNATIVE_TO_USER || \ (option) == CURLOPT_FTP_ACCOUNT || \ - (option) == CURLOPT_RANGE || \ - (option) == CURLOPT_CUSTOMREQUEST || \ - (option) == CURLOPT_SSLCERT || \ - (option) == CURLOPT_SSLCERTTYPE || \ - (option) == CURLOPT_SSLKEY || \ - (option) == CURLOPT_SSLKEYTYPE || \ + (option) == CURLOPT_FTP_ALTERNATIVE_TO_USER || \ + (option) == CURLOPT_INTERFACE || \ + (option) == CURLOPT_ISSUERCERT || \ (option) == CURLOPT_KEYPASSWD || \ - (option) == CURLOPT_SSLENGINE || \ - (option) == CURLOPT_CAINFO || \ - (option) == CURLOPT_CAPATH || \ - (option) == CURLOPT_RANDOM_FILE || \ - (option) == CURLOPT_EGDSOCKET || \ - (option) == CURLOPT_SSL_CIPHER_LIST || \ (option) == CURLOPT_KRBLEVEL || \ - (option) == CURLOPT_SSH_HOST_PUBLIC_KEY_MD5 || \ - (option) == CURLOPT_SSH_PUBLIC_KEYFILE || \ - (option) == CURLOPT_SSH_PRIVATE_KEYFILE || \ - (option) == CURLOPT_CRLFILE || \ - (option) == CURLOPT_ISSUERCERT || \ - (option) == CURLOPT_SOCKS5_GSSAPI_SERVICE || \ - (option) == CURLOPT_SSH_KNOWNHOSTS || \ + (option) == CURLOPT_LOGIN_OPTIONS || \ + (option) == CURLOPT_MAIL_AUTH || \ (option) == CURLOPT_MAIL_FROM || \ + (option) == CURLOPT_NETRC_FILE || \ + (option) == CURLOPT_NOPROXY || \ + (option) == CURLOPT_PASSWORD || \ + (option) == CURLOPT_PINNEDPUBLICKEY || \ + (option) == CURLOPT_PROXY || \ + (option) == CURLOPT_PROXYPASSWORD || \ + (option) == CURLOPT_PROXYUSERNAME || \ + (option) == CURLOPT_PROXYUSERPWD || \ + (option) == CURLOPT_PROXY_SERVICE_NAME || \ + (option) == CURLOPT_RANDOM_FILE || \ + (option) == CURLOPT_RANGE || \ + (option) == CURLOPT_REFERER || \ (option) == CURLOPT_RTSP_SESSION_ID || \ (option) == CURLOPT_RTSP_STREAM_URI || \ (option) == CURLOPT_RTSP_TRANSPORT || \ + (option) == CURLOPT_SERVICE_NAME || \ + (option) == CURLOPT_SOCKS5_GSSAPI_SERVICE || \ + (option) == CURLOPT_SSH_HOST_PUBLIC_KEY_MD5 || \ + (option) == CURLOPT_SSH_KNOWNHOSTS || \ + (option) == CURLOPT_SSH_PRIVATE_KEYFILE || \ + (option) == CURLOPT_SSH_PUBLIC_KEYFILE || \ + (option) == CURLOPT_SSLCERT || \ + (option) == CURLOPT_SSLCERTTYPE || \ + (option) == CURLOPT_SSLENGINE || \ + (option) == CURLOPT_SSLKEY || \ + (option) == CURLOPT_SSLKEYTYPE || \ + (option) == CURLOPT_SSL_CIPHER_LIST || \ + (option) == CURLOPT_TLSAUTH_PASSWORD || \ + (option) == CURLOPT_TLSAUTH_TYPE || \ + (option) == CURLOPT_TLSAUTH_USERNAME || \ + (option) == CURLOPT_UNIX_SOCKET_PATH || \ + (option) == CURLOPT_URL || \ + (option) == CURLOPT_USERAGENT || \ + (option) == CURLOPT_USERNAME || \ + (option) == CURLOPT_USERPWD || \ (option) == CURLOPT_XOAUTH2_BEARER || \ - (option) == CURLOPT_DNS_SERVERS || \ - (option) == CURLOPT_DNS_INTERFACE || \ - (option) == CURLOPT_DNS_LOCAL_IP4 || \ - (option) == CURLOPT_DNS_LOCAL_IP6 || \ - (option) == CURLOPT_LOGIN_OPTIONS || \ 0) /* evaluates to true if option takes a curl_write_callback argument */ @@ -285,21 +296,22 @@ _CURL_WARNING(_curl_easy_getinfo_err_curl_slist, /* evaluates to true if option takes a data argument to pass to a callback */ #define _curl_is_cb_data_option(option) \ - ((option) == CURLOPT_WRITEDATA || \ - (option) == CURLOPT_READDATA || \ + ((option) == CURLOPT_CHUNK_DATA || \ + (option) == CURLOPT_CLOSESOCKETDATA || \ + (option) == CURLOPT_DEBUGDATA || \ + (option) == CURLOPT_FNMATCH_DATA || \ + (option) == CURLOPT_HEADERDATA || \ + (option) == CURLOPT_INTERLEAVEDATA || \ (option) == CURLOPT_IOCTLDATA || \ - (option) == CURLOPT_SOCKOPTDATA || \ (option) == CURLOPT_OPENSOCKETDATA || \ + (option) == CURLOPT_PRIVATE || \ (option) == CURLOPT_PROGRESSDATA || \ - (option) == CURLOPT_WRITEHEADER || \ - (option) == CURLOPT_DEBUGDATA || \ - (option) == CURLOPT_SSL_CTX_DATA || \ + (option) == CURLOPT_READDATA || \ (option) == CURLOPT_SEEKDATA || \ - (option) == CURLOPT_PRIVATE || \ + (option) == CURLOPT_SOCKOPTDATA || \ (option) == CURLOPT_SSH_KEYDATA || \ - (option) == CURLOPT_INTERLEAVEDATA || \ - (option) == CURLOPT_CHUNK_DATA || \ - (option) == CURLOPT_FNMATCH_DATA || \ + (option) == CURLOPT_SSL_CTX_DATA || \ + (option) == CURLOPT_WRITEDATA || \ 0) /* evaluates to true if option takes a POST data argument (void* or char*) */ @@ -310,13 +322,15 @@ _CURL_WARNING(_curl_easy_getinfo_err_curl_slist, /* evaluates to true if option takes a struct curl_slist * argument */ #define _curl_is_slist_option(option) \ - ((option) == CURLOPT_HTTPHEADER || \ - (option) == CURLOPT_HTTP200ALIASES || \ - (option) == CURLOPT_QUOTE || \ + ((option) == CURLOPT_HTTP200ALIASES || \ + (option) == CURLOPT_HTTPHEADER || \ + (option) == CURLOPT_MAIL_RCPT || \ (option) == CURLOPT_POSTQUOTE || \ (option) == CURLOPT_PREQUOTE || \ + (option) == CURLOPT_PROXYHEADER || \ + (option) == CURLOPT_QUOTE || \ + (option) == CURLOPT_RESOLVE || \ (option) == CURLOPT_TELNETOPTIONS || \ - (option) == CURLOPT_MAIL_RCPT || \ 0) /* groups of curl_easy_getinfo infos that take the same type of argument */ @@ -351,7 +365,7 @@ _CURL_WARNING(_curl_easy_getinfo_err_curl_slist, /* XXX: should evaluate to true iff expr is a pointer */ #define _curl_is_any_ptr(expr) \ - (sizeof(expr) == sizeof(void*)) + (sizeof(expr) == sizeof(void *)) /* evaluates to true if expr is NULL */ /* XXX: must not evaluate expr, so this check is not accurate */ @@ -443,12 +457,12 @@ _CURL_WARNING(_curl_easy_getinfo_err_curl_slist, _curl_callback_compatible((expr), _curl_read_callback4) || \ _curl_callback_compatible((expr), _curl_read_callback5) || \ _curl_callback_compatible((expr), _curl_read_callback6)) -typedef size_t (_curl_read_callback1)(char *, size_t, size_t, void*); -typedef size_t (_curl_read_callback2)(char *, size_t, size_t, const void*); -typedef size_t (_curl_read_callback3)(char *, size_t, size_t, FILE*); -typedef size_t (_curl_read_callback4)(void *, size_t, size_t, void*); -typedef size_t (_curl_read_callback5)(void *, size_t, size_t, const void*); -typedef size_t (_curl_read_callback6)(void *, size_t, size_t, FILE*); +typedef size_t (_curl_read_callback1)(char *, size_t, size_t, void *); +typedef size_t (_curl_read_callback2)(char *, size_t, size_t, const void *); +typedef size_t (_curl_read_callback3)(char *, size_t, size_t, FILE *); +typedef size_t (_curl_read_callback4)(void *, size_t, size_t, void *); +typedef size_t (_curl_read_callback5)(void *, size_t, size_t, const void *); +typedef size_t (_curl_read_callback6)(void *, size_t, size_t, FILE *); /* evaluates to true if expr is of type curl_write_callback or "similar" */ #define _curl_is_write_cb(expr) \ @@ -461,14 +475,14 @@ typedef size_t (_curl_read_callback6)(void *, size_t, size_t, FILE*); _curl_callback_compatible((expr), _curl_write_callback4) || \ _curl_callback_compatible((expr), _curl_write_callback5) || \ _curl_callback_compatible((expr), _curl_write_callback6)) -typedef size_t (_curl_write_callback1)(const char *, size_t, size_t, void*); +typedef size_t (_curl_write_callback1)(const char *, size_t, size_t, void *); typedef size_t (_curl_write_callback2)(const char *, size_t, size_t, - const void*); -typedef size_t (_curl_write_callback3)(const char *, size_t, size_t, FILE*); -typedef size_t (_curl_write_callback4)(const void *, size_t, size_t, void*); + const void *); +typedef size_t (_curl_write_callback3)(const char *, size_t, size_t, FILE *); +typedef size_t (_curl_write_callback4)(const void *, size_t, size_t, void *); typedef size_t (_curl_write_callback5)(const void *, size_t, size_t, - const void*); -typedef size_t (_curl_write_callback6)(const void *, size_t, size_t, FILE*); + const void *); +typedef size_t (_curl_write_callback6)(const void *, size_t, size_t, FILE *); /* evaluates to true if expr is of type curl_ioctl_callback or "similar" */ #define _curl_is_ioctl_cb(expr) \ @@ -478,10 +492,10 @@ typedef size_t (_curl_write_callback6)(const void *, size_t, size_t, FILE*); _curl_callback_compatible((expr), _curl_ioctl_callback2) || \ _curl_callback_compatible((expr), _curl_ioctl_callback3) || \ _curl_callback_compatible((expr), _curl_ioctl_callback4)) -typedef curlioerr (_curl_ioctl_callback1)(CURL *, int, void*); -typedef curlioerr (_curl_ioctl_callback2)(CURL *, int, const void*); -typedef curlioerr (_curl_ioctl_callback3)(CURL *, curliocmd, void*); -typedef curlioerr (_curl_ioctl_callback4)(CURL *, curliocmd, const void*); +typedef curlioerr (_curl_ioctl_callback1)(CURL *, int, void *); +typedef curlioerr (_curl_ioctl_callback2)(CURL *, int, const void *); +typedef curlioerr (_curl_ioctl_callback3)(CURL *, curliocmd, void *); +typedef curlioerr (_curl_ioctl_callback4)(CURL *, curliocmd, const void *); /* evaluates to true if expr is of type curl_sockopt_callback or "similar" */ #define _curl_is_sockopt_cb(expr) \ diff --git a/code/libs/win32/libcurl.a b/code/libs/win32/libcurl.a index eef9dc724ac583b2f9b5e269abc75d86a64663ea..47f4d284c0fba3815c0997ee5a277e1a76db381c 100644 GIT binary patch literal 434836 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

` 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�q=OS0+;qCEJ+Xr$wE8U&*Gdx|t>0C6T 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 VCHjWGuWZHQcx%uSz`st3{x536yrcjC literal 385192 zcmd443w%`7wLg9)8A1rb37XoVsSX+xlt;ot36B~G6Yvq8BqX6~978f;2J$j<27(F( zCxaZegJ{7=E4{Wq+xoY*wJlgg!31ammMS1s5L8sunTA$GFGTQ?-*@eO&Ym+f(R%y8 zx1ZmEGg)Wtwbx#I?Y-B2t+UVYY1J(?p6{oPv&UX}6Y|GT%$qbRe}dhf%Y)3nc6O42s) zqE{&d&~1sgGe@x+cllz)YQ8I9 zRjjwA#oi4g6|4Ea=NiRozVBb7SdIJXql(ph|7xsaHQ&GgnPPqIgV_6xrxdICervg6 zJ+x4(zuM=%W}?g8SkqMNj>XlwO}IvXgR8o>w#Du9xecy(XsTcA z@;BBu)hyEEdYXJ{O?8dO?W(DFS2ya|hDEO0Mjyu~&^5RC8;z7jOiD*5t8c2VC6>+A z-j>*8bpl-m)!X21@~b+sxy9S4R?n|@8zSrY1@%qytLt6F&?Sw-K{h1eP~{ZIBVK%zL+y)tE?e_)@petnd)ywK%-ID!Q3_9 zUsvaDahcdgv4+GRcg-T9T7zFrQ$urgi#tZ&HBHS+T>|4;TwU)s%8I95?N>dn>Ka3h z99J7Hef~_6xg|+d3|I?p8VEzJ2XwVw)!SswMvdR6HZ|zU8&XDDAn%Pv!Sb}cjm>`5 zMN;X^ITr1*AGDdG1r=&Zi&u5WCFQAZtgVM0iH$*TLKzbAsCsyzWLfb!xLYwb^d9Xs#1Y zyt%2-CyP_h7HH!#O0jhmwHj16{fR1<%kOiy)Vk}ujcy~SP0em2kIgMjYEw;9y$g-N z7d6qfP2R=@aX3k;R~Jy|$;c2M%_NDd#eJ9G4NK}%qxPq^$<@%*V&tl}NvB`zZkgYN z{wSs{dVNlBGt59E5_2??xi<1p>xagSd?qw7DN!st{mEVH0zvCiJ>?d6L(^jS9IoD? zmZkp|1soBU#O+8egi{-6z zd3~@C=tSzOYuv8JYH6gsb>qE8CGa)7-L>dUW%tSv!mMx$4#oP=X2Gcrx;CTqCuGpw zxY)48QCko5Z}jgxmb==w6g{~copdvVQ`b~qAMN2=Aa+bDWYt>SOT5zb z62;PZ9^U$TA`~5w&RCjRy@Y&fOC#EFH9A~gVt6(`^70lYo(J=1GKM-ie{QaZ2{I!v zAd`JRr$A#CcHB%QH6G>62PV{^M^Vja9T;1fQAUg6MEZ~|Hl~g=H4-j{>Dff#YH)*n zAytwZs0WHSsz{A#U{q5R6Ved#1g#;9M%gtsRI4>c_eTQ=s#q@5FjL~pOkm_iC8e>7 zi^fu9lB6vC1e>Ib4W$O((jrH-F@d^}OT6{9HPtP(X0aqr=!t8Dy4@O&zj2XyAQMGW zb<#OP2WrzIm(dVVZa!p$s<*jap$n2$of|{ylq3d%>+QiDr;(~KuTfki8mZK~>!e8) z4TuKxx+H!+8jedDrdDSyyO?^eX|s|A#*03=MRhg0TMdo^FZvNZ5rL{j=hxC?bTk52 zLlc7-D(Zf8gr^!q$Y!rJ=KL8v%)#hPkvxV%EzwSu(~(3jFtdow9;ZQa0fb*al2oxfKA(4iF|c#DHhbkDOJ~s1RD(GZ2~bOk2<0NjCLYvy_$1kctE-JR zEScpyFmtSO61YtR>bZi!tH?CmKFgnAmi-yP_vbiP%<`Jr^S*6Ctv%{Mp%#7ZnaGYL(u}D3S63J8c`I^0$9T>3%F80yej7hE6 zuqt?(h=B~PD!FB6f_?$zhZP+f;R+rQ63a=Leo{*@S~Zyr!pcE)95o6K*1+35@oVtc z=yi+ztq$wRgpK2<#P|?h|=E`mS@}gqqAzXiySYRv!2GYOl`G+dSSRA1a3VQHbVR z;PX|Z)-<{oG^yTb7s#tp{BTc5L6yujqElQxm#55KGmDfCdkC<|u$w;7=_8YkwXVh{ zK5VNYdteUGOIn~PM<)FVB;wV3Yq)R}yA`Y&xwiFuVoZt6Ktqqs&2du+AQEPfz|p)} ztfKLJ0mIjv!~ontnYvsMQ0bz%7&R4`#paa;JjXxMRGm*oOpB$UB6I^*ZAIQ&UFf`< z{Cel1KVe)fGD~4T6si7ncl?uIx9Nz7Fu$bq#l)kh*f3Li#Pe<=1s@hM{L!bF^w5=> z!xoukp)j8@s^Estq&)r{V>UU)Y&X%$Xk2NUnd{~{p>pbmJHE~e+Z~VPixY*n+M8k;l64gMq9M%FQ-ESC6a7yqJ_c!BMFN7rUl-{ewm7JE?|F>^JH|% zB+)meB#GklXck!Sq|tx&CzGCBO-pry>zhfwnGa1nQ6S&MMMb4CwDYbtmsj^scp+aJ&g#&7y+Yc^s}ao027Tq+SbG61kpqRE@H_3bv$B1B?n$4 zVvLCm53Hk;F-9kg(xL@r%zN}~Q)YEKgc=F!fySelT43^F43=u$wM{Did+Uth0_N0> zZkLZ*VN+wB*I2>jeOLmP5TqbXd@$nC6N^Q%hk zxWp`jd6ky(um~$@P!!LbDJCZ~j|@PHa?}ash?Y2DuH7G6l#z?C7s*9uqDRN`HT1NR zBL^Fp<(cIXZPSd2wZvd-Y9m-uH0qJB26eB|4LNDqDNbufm@W|CxHHl)!s66wpyPT1 z18ovQrW{`k25n4PNMvkI~e*s2rzBsh8lNyH>+m3Na8X?4?_-HQQJM#Kfw*H*_TqF0JILR4++M#Le-EAj6?IWBVV|UuV2V1)@kF&sS|}^iNo^s%s3n^0>+{EYCs$g z4Ef3#cPfFIiVNmz1#vhovKT1Eg*4PoOu)qJK@66SDPtVDT+@idb6sHbFFxZ`8)yMN zj=Z|TyP&!$UZK>uQB4}-h(()^7hGVk>4!FvgItA(qvkb+mLTHrJ}l@al%bl~`1z7T zd|bYR5s$>`L40m~3B`{^0q83(DsK+x4EdT(To^6n`Se+Szd`XB_1MwLV_1#fd0HjEi?yA}cI_qMu7h!9cr2ano{T9Ixuu#1w0m zVk;zGSWzhYP%w@O538+iLB&ptxWKo_%R7Lyal$cQjtLFV{cFeD?RmMm2$jFr&?s^I zGqjJGBa;=UkLuKRo&H`{A19daJF=4k0T_&qp@=mI;fw^hB{xntC!l2}|KKd`IX zu~tt8{3|QU0>{(C#N|aUtmDM#_qDf}<3#7DDS@5ox7`uS+)AOAGokeL9C%d%ogXBR z+8fFYa`g6)RfIj(UA9h=<@#QCcPREFc^1OeL|_8Z)ezimQma0I5*E zNpYG7BYvHa-vZp6hGRzHcWn%2Jf5DCn8fyw3f@$UqSyaadl+k~LzJ6jy?#ei63}%f zM3Qm~p7a&>f63Q-Hu)3REaHC41-VMvxQTh9f$6{htVNE`B|Au0v_;;Lq9_+|DHB^{ zF0w`~vUY&T#xVT(ceKbhq;t%uBqC|lg*yw38h;Zt$?_~#mC?;ZyUDaE!Na!pttj!$ zzk2rBXG57A2zUB}eSbp>32e8P_I-yCnqy;NX9g?ZxcOo{1^3(9zr6&A2Xt9!aY75m)V*Ea*+FNwvYRimOv#vU(74MxHIAisHnAfq|@sYMYa5TB| zZ%Lg;t*gp6=j~rz{;{?%@PVx}V(t7OX;7#3dfx8U#h+-qI>RZ0wg=8xZ0$$DFmNu( z*8VU7?_XD8+qS|E(3#hXqjNXczB9z4|S77i%o1|YE0&Kw5< z9aiMTaljV5uCK3;6(2zEyDZ2O6t+>z5TvX)3rf%|c^x757q{JkHXeEboRltPOo`+m zqLYhZq@&BgmPx5%#TdxNs5HcgsNkUWBIU0a9inq@`!Y=ILN%R5L1F#}Qp?JOttx zfvh~W?cNJ4({1ZIc)clzEJxAj?an(KSqOlYAJNK>Gskhf?#RlCkCsH0z!BDxz#^}M zS7f8J;(tffjHsDDU7U>x7-Iy&y#sWA0JymrZr|gs(T4~$Ou+Te#uO7L zUvrJ1jE*UOg32<`ML#v{{M_Aas`{1-!`S7!#qNxzyr89B5m`< zNBMaj7~+?w2KTG!A@vG{m1Y+N_xpQ|KCxVcqgP4|+?}fUGuRC4)ibSAEw&B9Oc{ix zQGejL6*iNVrFIwc3J%^dqF?RafiVS{z+_=1VOpu%pz$<}?N__|-bXmbI2C0S+n*jh ztbT_T&VfBY&$%#ZVRD(#E3%^0Ia*O_u){x)6~WLD{{dwA6{N(iy&kx`CY; zI+pasxdev(5Uuw>@DXLxj9NFeZU8KA!xe#1dCnMZCZHoT`h^L)^ ztUKz~f*bzcRMYHNtYgpN2 zl)&x%suF319K9k57N7{2HlZ_Rj$TC59AAVSCp>l}=G2Eyg>xVYayP{3G?xgK?pj?R zVphh=GnpeJd<0#W)}7Z8>{ES6Obn84{lUU?ZO1~3R6kc(EyBACQjr+O4(VD-sK}}d zR%8{4=7$O`6;u+HY~Ey99kd;gXEK|Uiug0MIjNQUsN^6b8J%l)k(ElaXB){N!t**1 z-t#UH6?%kedVLTfJ5BjnaXcHULBj0uFHpOJhyCYA#(-hnT6P--oVIp59#}Hu__S^E z7?sA{q4g*JtFNy#RDPlqAhb~G!Bj&rO4Af(uI1PrE)lBYkvTO$vm=Ux`fQ1T3YG!Oh8}-uaHm< zhUL&t@F+3>E0fpJeLpFXE~99ih{mQ?dZLX?zDze9ipa|`GN{s2zUU+@Xrrcn-l#8i=7&W7gg}?t5REp|jYo}R!RHYSE%e8Ja z8=+4r7uRPQD>9Us9ji>$=M+tox-uCluuZhY^AHbJWFVUj+oihkC+~29HanFSk&)vq zO2(mrSqCN%SDOq~X$SfE4k`iZ-vWQ`i{hM~pMub1O7hR7pPpx^JyIz-j)$tkdSzKj zqG*nAWm$Om8Pa1G<^C=xvXZy+vb;Wa^JMTB(}{VvdT6CXiWPC{Dlblz^Nn^eSOhdo zg%c_+C7T6piv&rJj-!;u43LPN#_1Gh!pWAUSF+-xB^BY9&Vnqe|9Gva12G={1eF6B zV~(RTk;ogsiT*>kKUJ_l>1-LM9VeJSqnp>dz_Kjm-aP-sP@`6rsreyWy0#2G1)l~+ zUPsMxIz7*j-fc2}J^K*88SMMSTrqZ)i~2|EG0+O{wkD}byLa&XkPc>MVLFPgi?;dPG?0l^s$G_m3qkA0MmsI;er^j!MA(J#- z6P`!FUSz#{wEgaCc(~T4DL3CT*AAY#+rPc3(N4~c_IkX7?QgbwefGvC)$W4>Z&Qm( z^thJbrnRw5C4k@W;Z9M0jbECuWGbieD?%}V)p?W|fZ!LgmWS*%!2xtt4wgD2U z9RwsK{W~BUS7s``fCRrxtD&{40SQUV013`N07Pa#Q&}x>Yb9eOj z3C`01UBP4A4(Lh_-3v%a`b$6}-A+IvN0|c+F4=$t7dN2cJnj-eG!siv+9mXegdPKA z=X4t+?)MUUSwedN3F-F%5_RoOK%%a_3+PhLb;_9^6${$+;64Qmk z;+F-oeckFb_0@LVM1v}VYa(tMltwW#@w8UQpn~G6z)hptD9r+33Suz#0<%`fh**yR z^Lz~EX<+uoV92yNLC$3ctSEoRZHH)4%zMBbMdqTIURWHacBV(tLuc^NBlhW-`8ZV{36-+yg}PVFZ4GSB;{B$F-sVhQvoTI@3} zq2~t@FzY48n4$kX0rNrvhE`g{MX}&2iuZu=J_f%8hTcRmE`#%5V`5S0u>{P?1dLUP z4r+tXcM>pUZN+8KOo_q36QHKLu`Z70);OB@wFn~j#?TPv?gY&01k8p6%=0lAvhZS+ zBks5wzW>forJ>Fc-DCs3JRx4qq?HZ4H-uOHS`0^8xFwhmwu#2}Hj1~Mh0nGLZ#Yv% zQQZAem*)TR<8Mf4{X87<&h+AqEbeAZcp5Q?Fg-8&t2S|Q^uVuwX_~)3YV!R|c;{3) zJc(5%_{AEp_!p8JX59OyvG0w+&Bbsx(ghLZrdOyr0?T50IG}&w)mg+czp^A=dNaKc z$P+QW;427>7X*1esJ6w)N{)(k%DBnKp0}M}|7`W_ntUTdboK0S-xZ@Ctav8ukvq>9 zt7iu?skJGJ1GoL(ut!e4jr9UWSqa(bqG2XqJ;T~rF|8J1-lIL74TGz#$ilC&D2Cam zDLjXV+Y`fS89zO^Ut5t%6c4B4w>6VJJQ;rjuNG(=0L#S+TfuQhU(q;FwWDW9%!{ z=~vA5v>n=;+UtQc%JNd|D&F^9+k>=~umW?*zGmCX0f6~xPT*XYZOuzyx8mH6L0GJR zh<3Ixb+SG8`6Zp_E~zN7eR{|=P631T!g?r7*;R>%Z@9yHLnsU#nV<|i6Jpy(PcgOlhJpyv4@|I_*9>s%s&3Oowu5hrK1`&BnTw|5yd9I$Gwb6%rI5 zFjg0&1iXIUJ1EdwV|(Cgr~?~&tT>DvOICH<_VjkE?dd8!{nSyN!`{$#X>Su2 zm{Gfiy*;XH*jt+aptkG?8rw8IHIjU^Zk@jpA9=Zs`%ir zD(uM~DPzS4v3iYdMciShr&!3h?10e%a*ce)tcIqe*>ndLhzWXH7wryOjR!>w&La8S zs6lJNbikrXufLPf>nTtR))M?9YUHC1l1C6*P3@ZniRPURt@s_aOS1|up`A#L-JgYU z_zG!!G4x1~NZwh`e}7bfphN#)kB|X=M})lp-qCjsY3skhvdyWT4qNbND6{u|P3x$K zmaTdBvpVKjvoXC+b3Dj|w9k`SW}C@GA8L5;z0Gqtu;!gM!TZqecQ{ARs|dU*E8ZKO z^X_LNJB(*1urswQ_$g9Q^6yu4K3klEkF9+HGO-zx4TY^Im1v!81J}gf&NG*u{!(zz z)_;ZQT7Y7+4J11Yk;wUHZEHG737fcPIThQ=)yY%V9(?mDMA4pn^J)6?@|y?fPy3sD z>CZ{_0+mFO_IvshIG1wYAnt+j6vml(hXdyZ*dFo|Z=r`xoa&TXrL zhf>ylkeE8zRtl?Rn+O|in8Dleh_$A)ZVrL1!a^pQH|`@j*~|M#&Z{>bqNj^rK15G} zKE?Lna>TH$AY4{P9xVEp1Jz@t?%3h?-vXcES1Ai*C*Vq%hQ$JnC zG^&3k>{05ROW6yl6wdyGc7n?K_6Bz5@?D6Gk`m7{a5h>G?LX)%x4iw>LU)rKpJ`_! zxhU7Qyh3OvQ3cRePn`Zzdn2M!sq;p{?r+nDEydr!@zL)+OESK5drsDS&yswAGTwWZ zP4bGK&7EQ zHX6U&fr0FM-r=Z1==q(4hX&AES>)GPh{t$DK9`7RA+6TM9={1}*s3}7`{-QyZFAx` z|LyzEvpskg?J<9s?f%d4SBTM{3?E~0H~%wR;E!M(S&FQ-ZRL&C)?P<>`mB9->kXn! zvLDv20jI#7_|@JV)MX3KLd&VF^mLE{c|O@6Ga$C8`0S5V(U9{MueE_d+y^3p*Bhg!!J?_P%U?|0M#W~H?&|DXOoRz zv_xSt*(T!34d(_*MVMP^eOo6JUBjBGn^fd-+hF@nOBN z!@?#fhluS38z>3O32nnfN-C6#-=Sw{92(e47?jsUUS2;(a-?<^Bs451QAz_rBQ@SV zqW);XkqFY(kH)Vs^`YWjp{hsOqoYa8l_T*R9`v6GrtBEpd` zjL2FrlfoaRS`;eY$W~HC3h$1Fce6*y`h|+0Vk^nAgnvz8ylSDziF|V%Xotd&AShJ) zthR%#pGaJTl#B4az>*aWktCt=k4r+u`?2MDq@;Y1$sX37bwDqKp$WX`>=!MN451^YjG+`gI5h@_3 zgy!l{WHE@17E4W){gC%gd_*M1I*~G1IlcS%Jtfb!@D6ShRatE-RoTdCaKS(mxq4s; zz`TyU{gLGWR(zRpB{KQIEnuP7;pJXxrm*(t3*NPf?1Hh_?)7$%EazHV7?H^&lwH z0|`lED}Mvx$avWA6)>IogEzEJ8ZvWBa!Yex$a zOu11(=yHZH{{q>dJ#`G;E;z+!*%|5u(Mh@VP-%N9mm>L(VLXAUZG2Jo>=E0P(fK`j z^Q2>oUl_TwgBA}qoN>_q_vLJOl9>GDK`?6G3okiCSp5wBK=u}ZCUy| zc0?}yDb~^WYevu3(Z=7o`m<9fn=&GolN*1P5fg|THgE;rql~zgBa}j%WHukCL)fe5 z^Sx9Q^Ho>bu@UTVz)OO5wo#CLFbB7s)|HWr$D!?QGk>2BZ+$xt9NF!Ee$~MXAUAFzy#{AI43t zqJ$rTzmsv#CLG=qQIxjpup2O|9F?1PjRi5rMzJt501KqBs334JOd3raMbb9o4mh_P8hZ%Sw?d^ttvYzaAs7@X$= z5?q!7qP=UBG$0XUAXcpeG(tjISoRaR34nxr*8vh-K9HfaF>Mue=Ku-1G}JCZHyV)8 zS|K12Iu8)-%VsLeB=j>0bxY{DgodKViWvET1V1vz5SDxnB=od|o{`XV5_(=j8zn>& zP{D<~BLqYPFaag*M9Y&TYLK+NP)Yb@QLuA|*!^9@fwbf1G@M`>ehXqS6?mdyN0g=! zPe)@gtMF8SG@>*=2BuBNpsM5gIc_IFq7j(i0&@alMq_mV^E^r{ig_IvJ9I8EiF?7c zr{Mri^!nHKf}`;bC~+@%FY<$`9e2GepN$>TZ~~?`0YhH<;xb}oC19>dz>JT0fUU!C}W;A<5P)vc5GyOaFzx$)7BxNrXj3l~n!!)Hz=82f|L?>y$c zI2_eOUp=9he(AF){6izMucUO!N9cuxBcI+2X`m>uZ>9iA~Lf)O={=Sx8gFf)ZL~1 zd0~Ocub3R))EsSl@RXXYHK)S~Ia#|qt9z!MY9XA)2eHCw+RSNM*)&?0+ROJ>43AOr z9b}h+XgHe?xW{T)d<90$W!mRa-`P|CkGj!8(Wp+mM#jh3G`22G)nD?#n7U_|zPS)P zCQc={;fD~J73Q!B5HlSSo#A&eW=AAeIPw{*|GTWr3Ln%Agy`?@0gupU=J|)Q!gNGB zpCU<#c>1g`8v#RN0$Abb1RMxrb7KpV8>lflUo79_C1bA8MkY2=QsE9fOiSyY$!&A@ zO!T{0)tZT}5l&nke2=m`Z@1yYe*uKI!IPiOOlLQwYAGy9OV)OgqdP7BMfG9q;W0%k zKY?AJRyb06*o{cc|Gm;u4>4!WsroYWYkZn`Myl4g^UN-`BUJnrfBURIW#cM5_YX9alc;Dobep{1g4KPa`Xo{dmSaGzAiSuzF39`G;or`x9b)!xeMKmtn7&*IU4%29X!Z{y%TG#T_cne*k-1ojOwJe=QxO004)|ST;vY4)Vz*JC0PsDo8rxK z5ejbcrNEX}5K@#d5+ci$tj90MD&jkq4+x{7$4ZWXY z{X=u~mU!reHxV`uUJAErhn(;SaHPUvxdoiWdp$3JT|#?Cdzm+y8g%h-yf9Hjau%43 zm3Wn5A0ko+H|lzYwC41^k@%XzFF~oD#cLbCq_e=lr_0(=Jmp=q=`?nX4_pW=^}^f4nQ{c03t4dR8htBf(j0jGl0Y-=t=T zS0{Ez-snuo$m(yCK*7-({SwVjqK}aIGPBG3vD4p*wg4SYnU8ckHGQb{w+> zX*{VjD8~MJ6GBTW!_V^Qd@;!uq$OR{@uU1b0%v77WDcB-z={eu&wzm4V0E%vQ?=f_ z{bk|r(rO>nL9e?6hl46qeze3xWnEeszCo~{peken^_7+6z=Bt>O(Y{adouiN13l>N z61<>|umXn=jc0^3L4MO*w5P!rqMbA+`W$fd!pO`V@XtG?g$@vF#0pWKL&O_Gmv|0{ zxGzNG`qfbJQSA&1QjGB5N%^#_b_e4u*bM5Phw*g35^?V~#huL7Q#5ufUYqe}MAncL z;uWI@2uucRr^f~npy!#JL%ozDh z1~$S#{!D=_7yy=Dd&)pMY)*P9E68weK^R`}*561$sq3n`K1WykZd*G|GhvFwo(3-$ zDt{lDAbJ}4Oo^@iNAy%Wnr!+Ikc(o#OK4sycB)G4t)PhP!%s^|XK?7k-3W_(j0ac+pQO zck(lkq4A(DWjmvE9`cOF5LxcUpflY@t2W{^iby?TQF+x;Y^nu0U~oC(jgQx#Jj6%+ zS!U#)it}0`Msc36m1m$7gX>UmCi^l4QBd!1Fh{X+{#md{aP!ehteQnMP7G9e)n89l zUmrcAV5#bJi>G(w{8joel{mdcZ#03J{%rGTJIab)BNMuzhIZ7s5Oq5huU3@+3eTfm z0KDmjERc<2{^K-icoJY}>)!X!Y`(|tNG~a46{-B~sJ`+Hq?o_MU&F3}wOWF=Sj7-> z$-$6gZ-q0w3d`~HZtWRH!(?Y!CG5RZ#>vLuaMz0p$#e$P;g>J&fJHt7jL&jZHxsg%blMFdQNE$-;JC8;{{2D$VpQ|AUUY9zdbwy`bub@0A z3C-~|)zE2p)ErM~JGvb-U_x(og#P5=FbsM}>_fn;{BdfX2L;HIVDtmqQ?+88ZSZ)Y zcc`s>6G8&L7uZ(r0l?=ouh4*$RXyEyk5y5>$6vDK<9g8%>tj5)9pGK#wtF%Z^-?i2 z%0#~PGVU%+2MZFkt1tsZ*_;S3zNs-pS&}FlM7f-ZrcE(Kqa{%;4D&R4x3LDzq-Qk7 z{u^mBkqgkm16@|i%LDnK=*tP-3H{Wq)vBJNN?86ByLDQ$Uc%acOchWc>lfMs zyHd46%wsUiv$a=aZS)ngPm#~jdKraUDKr`V$|u^FI1HkFw|@Xq?y?ej2&7taBq(Uv zjS~aA3WB>4!M_Fl5{OX*Rq#dPtckX8qE?t5DF;0-s^AnLLlst*!fu?5Nmx7F@d_tL zCQ-OZ3$snhs1<|Gb8*CHxBHLp!8x&+7;~PEfTaWG|3i zh1r(KU#TeLG&9Ay%x6G)cC#zk~YLRhVmu6d@^$-(`-6f+Hr2l;ih7BXV2^ zE)?q`1jcf6tRwtGpwtfK?GKk@KrQln8P9KqCGrss4X9}ZpQCznA=&^jrv*b4ByU`i zKZ6D-rP+dCL7do(5dWVMv8?H&X;EXoi*(tIc2syvV0V&sqdh`R(>c@2%zM7uqTnk;EjBjZ5x3OTYx77#VnxyVP*f`Dk~$cZDJfG9CPqQ8P$ ziCjzc{-=#mtAmD4N{@#X?_uRTX?o_^rQMjW6=H7P!7#ZiB2RW>Wns%~=9%1n`vct4{p;aAVDWtFo zREkBsRgENYc_s$(X{U-%eS?<~qQb824IG6d<{(=;FY-Hbs8|0r!YCju(3^%j3&{f? z3{?lSo3nzbrH46JJJxJ6ruv_@wf`L9x;=Pik^dqj$SN|!zoR-Y(%ucByNWU_tYRX& zc{E-O*2G=&5lvvqibiV4YDJD#{IcfgU^i!ihgQB5U5Mt`qZRMc%J;JGV`eeY zwA>}?S0h+s+E4G?V|qj-*~WM7JA3Ec;J(DzNMvIr7?` z-`#@q^&WIO%R*M$sJ(Jb2DZPaDAqsGOHG>NNwj?CcoJg+I_p5=O7wLV!IU;b=s2^0 zMu9qj90|knEJlso#7F)Qa00p*4OBpX$H0qpR0vEQA1cn5c^71X366{hrQpb$J}Rl> z!3drDAnd^t7^?|r0g}OxNgEc?5SrH)IYr}EWT0v-j~|_jM9$Mg9P4;Ubj>C51!gxf zZ#=AB+cF>TvVP4qB6o5t-SoEWR6Cs^Nv{G;zifo>GNn))@H}QNz9KTk&JR7sxy0%? zI=*}iU*;d-n`)m=FF#keES)~WHzZBL|1o1Yq{#TQy!Z&N*ax42cct~nQ~7Jk(=X#M z6C>W&rPknWb=QuLO9u2J86#Fq8r7pN+R!%KL~|$pUXJ^uO;KLM-H7{U+=QcWx-Y@=*ahA?1?LqnaE~Ks?84>+`s2c8 z_X2vRUlr$$(&3}y#0QDTdenw`f#A^CRy>-8rsw`InmBI4O;u?pZd%WvzkkL3Htvsb z)A>*TiTe!h_=}o4O_x%X7W`&&=m`l$BxFTg5&As|(HuwM3MBLaAlf=f;i--Ul&Pem zIgjGda6nga=nsIzo60uyUjmv2XbcY}A9MjZ0SSKPfXE_dDy@J3;)A#P|as8s=syy8#X7P%1hEk=iAIL>e~$x|D~~ zzW!w#`W+yVTDOG$0_buc`VJtG+82Ow1ZVUoBRKROKtjGB01~p?1xUoWUqa6S5^-OY zbT?v?M#NwedPYJ$5*nUr@T1)V5#v4yy(FQ}B{UlT1VV!ABvcMa#BG&O6*gT3&L^Q( zKv(d*X@Km2Qk2Ie^fN$L@z8ewjpERZ!3Gy6pphKcDsd|SUCD8qC9X$8SEADul1`CO zJs?p=!_YQGYD)l#+I|L*h;a#8yMPKMm>BJ zgveV%r2CYFo|e!v5+ZL6K}TZ^p(`3f2xz2)Xw_KYawRlTLX#y#{z`(bKtklXBye*i zbd!V3 zxxjSj7#Koa^Kst^22mOxFu4dMOtOLxS2fTF_wJM!inRjwi5ScpUrcSki8dLF&Gzpln9@?G^hm4hT@`F ziRTPb*xZ;{gsF+axaw$BMR>Y6AG{TfZ%ONyz=&`E2o%Sl{?s%tHPH}A6A^I{4PEpe zl(+~(OBpVlx>JO~<@M1oh*AU6U(n|+AoMIwION_Tf?9qxl|t!r?;Z$=;~4m77s->8 zpR_JKZYV~n9!%uM*jFY#3m;dCd8kvMhn5B!Ez^gi@QF1s!q#1Q@JhQ8tUJ8WBe13? zM8lJ+`o@kQKk=GeJ!Vbp$I4CFiaXK~#mT>b>GI+<1eeR(G+%M?PeFRG!B;_rYs})P zmkk{pDL#hCgJ_T|z8%k1R%dpoiG6jSe`^Z9HRJQ`rt6>i*5u{pkH41FM}2F~$M9r2 zFWf}mniDXf8xi(Abw9Xh9oqhH@U5Zsx~H*tzvptEtb!qtqQBo>hSMDJ)<*#rw-cO7 zGW)^N^f2D}g!ZB0an^otw8s!nM|1vo9F_>8W7M+lZ);g}*8aihK|lz;4gtS(PqRel!BtwVaQ2-9+xb`b0E7qhIkWoH%t-(%;< z2!eYCor##Zm&^H8!TU$e#<6-9It=qsEKP`Y?C46h$bv_%OTLFK4e=3rgERNQUQ$4Ns3}IQ}4{5!GviZ4&)BDgvVY!Pd z5QlgQMH>3&dk0zAHPBz|-;V8LyzhuL0cgGaIQTj8`g%r+Ernp)PPSHlggLXcaxCB+>vn|6{eXHl> zU=N-`vEYud<2=lnNkd;8r>-VxZ?)qPMnGvV!dFdcn=;lv;6(t+7Fy71YaOqsqp|yW zgpRR1#{6OYIf_@n@RD~~Wl5=r+FzK+jqGfphn~Oa*?0+~10_PctU_PgD3q_yiRWm2 zL8-U(VoZV>n-+CuVNNVxtDo{Z6oJ~au*4+S15p$s*fsDiI;Fm@oe1xxN{$73|6x>a zK_m{w{C%{sY1722l?Zr(PA}EV8yiJ$>Z@FI;3UrzmH3YQ?!_j7EM3k~=bZ3WIbquC zRNv$7MEPgcgKf+E#u^7=8!G9?cWm-%xUE#dCuNo6Lzz@Js^|H-O9Yf{MCUt5VeMep zg-XRP)~faxZWD*qgW4ICHz}1~$z6s+&JWP)bV)XwkwtHUs+m)9H2s6L*ni)ksgAw2 zl{SFPv4`r4I<#%shl;KJD@51Ji-o1VhipeYX2T=CKqL*vzy&#o}C(SvI&#|?a2`u)mql;o<)psRgV>$RblcR~6+=VJPb)0ICmKY8g zq&g(MSSYeuOyN>*jwAVw<4Z=f;^P%1=Gph-oekfq|7PAC8t)aDj=gyrAoah_AeZ>cDxYY3J?QLy%?JuSCZyABk zBxOGfk_3T6$)h?$*1XkO?T6LOy0vX*krO&nvh6IgvWfbiKAg*R4h_AqFBH=_&i!KB3xWqxiG4_ zXz;_-xOvrJPVRAZEsRs!l*`JD?rLG0W&1)!v`xJ@H&OM{PUGYR4gE<;w)R=zwLb0a zsMlUW<-Bj$d#I=RXKWAN4Nmu^=AW>wB!kDc^7=PB3p9P*v$Kh`Z7Z(^w)T1upcsV~ zqefK-7M>2*kZ8qayuuom;Va#j@D&=DyofbM)Vj}q;UB^_QAH2zNb4#ZZ)ux)xvl-T zSjH?VSvW*iUFes377Sn0;&J3hSm~i*i75nPLTGJA-l?9ei}L#5{d3&OY4m2Nn+;q0 zX=+wy=!+lp$psu$vSe>x&ZbnDcEoae$ixe_Y(41&@la6fXXY05{E94uXm)Iq9tJ}( z4BM@#fln;kpeChfC5X1A5EI!zIgMy$Akzx1+_ToXFpUnc0;OKJ^7^)+S#nnwUT~4g z$WW|AybeO5ZIGO7#QGUI*yG9oUE5SSgn(IKXohtmG z?V{C}w8N3*eQfqc{(+y>^Uzdm?F9e}A|vrzL=6<)mH=sN(ty9BNmf>tdc0s`Zju_F zMFrGO20GHiOHQJt2zxezW``Gs|3sUD+_nWfu-%XIHNa4qHcRA}#C&!ij*+x%Cr5X9 z3!$uqeOsNR?Ym)`T}_372zC;h6LNp}4>6>sUbb+Cuj2?RPaLzx$bH+&Yg z0h#2wLDizGAjcB%k!(C^C)&fEaY?(8wBXoPFdmL3AVy`!f!|0>J4;0|6-o%48)ysC z`2cu>Vs%9-KFyQGx75!U#mr`B4W6BCYoCK^AegOk=3&4Pet`6iP40`xZ6On(6j9CN}Vj9G2%geWP|PyQE;t$jSFC+9pKY?LcCL~hS3 zDq`uAh@Pk3wlq)ir?uUahvCufB#lTa8C4jg4lPB#;X=;ps3vhUPx4$zz)^LIr|U1S zy&t;7MFiH*TQXk>F~w-`J;;jDAaA$2xEk%e_ate18Fb?B)i#jz)Pj5P+ZB8hzZM*A z-iBQywUzNOww_bG+a27A&<@lL?KDl_OYs4VbS?M{MPL6Me%WoAY;h{P&)T+?_&|3e z%5FpA{>ZMpqH-eSZPgrZyCO7SEv#$4LTbnddjLq3so9&QG!+jZeaN{X-Tx`El5S zPHgh)ZC+@U%|tQbQwoq0-Hkl^gDRxf2`%H}gIZyhb|d_!ZS7BkHym%Yli(tXs9U&< zbi-c+SL6@^Fdmr>FAn$&M2f*BYLrHXKlnT4n^*Q3s0pwpMAZr^ItGI9I2&S4mNqZj z=xc-PC@J0z>FHX}W}0KLB6 zrD57w$df&Y+)<_3K%*_Aqcj-92@|Reu18R0yC5`3xRK`V5q@QAR(LQun-WVEQxwsG zjDbA7V-b&+M74AuT&ryDuYoO_igDmToO*Q^9<&Wq=|t;LBJYra6QMDVo-sXF5g2mhT#Rn@d6&=*dV9Rl zp)0wJUc{nTdSKXEPy?w-{031YZag5q2YmH$y->JN%8XPLH3X`|2qQ9sJX~+H^uVj@ zJiHlT3!YE1?S`&V$;O#0S>z8oUv6!pBc13tF!DL#c5^#GnKBAuE~V5P9`rZ!m@(1~ zT1>2>>!D(PO6x6t%%9R%IZje_zmSj7x{6X2>U5}!#OP=gkf!==YyUCujIVvuD_yG* zwQp4s9IMs4EV4%afYF~^&*ST{>?bE+twCHxUqVWfJvhEYxRQKFoNOg6_BpYdToJCM znF)P)B}3y_tS`KU6xbT#A(q*?R}xGYCc9B~`qFJb$=9h)KN9K=MvDW*33S-$Ga!x= zPR|2CEwp&ZSDlmN(n_eXqcL}*U^)BX zD}bm-{U>Bqi2MpTyevx_pc08plP+3lSnBvxiJZ~n8QDV`k#YnYE_KS>(sGAVG~=c+ z2flgWZ(M{|&l~Y3_~mo($?aPj>zBHe5kC4iDEgm{ei4t#h`ikK6K=a>>X0=1^s4D2 zCg$ho^QS5WFSPqx+zNfUy9sQR=4#cW)HXF#dudp%-~-n5wGVp@zUtw1+n0D%k9|H) zby2Y)*HDeGj@t$86r9MCrr-b>9~{+`hU(Taa3fRPt<7GXcCHM30@PC1`4ZXE48j`dWxwvP3=$JI8~ z_{LGd7?E!u_i_XATo1l$LVKzFgD4B!jW{s}A2-3FJNA0-eBv?O?x^*u_G;C>w8`IM zM=9A`yuL+a)6$g7!Jr5mvh@b7-QCjC)RG2e&@5l6oKbr7^*7IkuOgHSrucq~uX?dt zSCr3=53+Dd+Pk$%@am$Se1RK; zwk{y@OA*k`fX0C~Q~44QHYy=i3JivTzAK^2By^R8=$nm#ZnlJOlF(feS`26cPa`04 z4@>B$68ad>WKK62bDJp~$_6AP&5^hQKx2654H9YqM4l;`$|gV}NAof16S#IjBF0Yu z3B7CrG?K@74UkCVuYg334okYf0}^qqnDJf7V~hnPV$j!2gqPDpfZ+TD{XGt7Jg0kI z(!DRCPXUP-w_)u>aCsPzNQ1oR1oXOuK90>0VH(wUw|&<(&uAQO2|j2`;6pty8#K#uLHWA<30o=BzOe1UxbpEozQp>AQ3kW zo^k@31?Vas_jiDV%*OzUxHeb-L3g=?t_37Qrvn)Lc|fAf z-28m!WGU^m7UQO44nWxE&HlXA=qOuL2}E?~%BD5;xms z(A@+`(7h$0ff)wwS_w4)68Z~B=rIZXN55BGR`;Xz5^(i-eHdsFV-}!^ zJhU8;9Z-tm2P9} z9)|=XN3%V}>K>dxTMSJjVsBKXUKX(eHB`}F+ zZiP|0FMv<<`rq@C91OA1HO5_2AhU6ad=({NqCSXSKX2pujoV~|arvt0OHOy=+f96$<>Ir(7;(7ws98SK*O~N4bUY}{rHqX56X$e}1}^54 zPlijWU>o}dal~2~^=dU3H@Pak;jcUf5kB}7%)uu%*d1 z`qe1@79caU!Z9so*@ z|J>^p{%8L2{o>t$V0S-7QwU@wJ?V>4s+UIrDg5Tk?5YOCuCo zYs}b8{WlotWboagBhc>mAgNOY1s1FwI%p%}d+dJdV(5F?7qa_#?DBLiNspa}L+Jv& zN&eI3AdrJ`qHk_sGri(sSuQ^NUbO>9|2;C9c%K=p9uJ&JQ-=mN&qD6fZ0kGtDs;}DqBV$4I1^9hQGyO%Py^Ou8-qVP6F$6Ae*z@z zF`k$s4DVg+EMqCrbczKwFUZ#e>!LK+6ib(ez>yme(%;%LX)FWYMmWtcf0u8<*BasHfG^+qa4f^yx7@0#71 z+S9>nQ0w=DyIZFK3}p_obU+Sh|7tv1c7>+l$RRZcTli7e8d^`i6&V319AwP^3AzSo(rJlEuO~V&9z1wkuN{lYk-`+rg|Xl@&TcjdR%s7tU30U}Y%&IoQ(E%5ZEYzTcfm2Qd^DX!q|zg=K}a zwEOpPU|vCYp_B4~iqc&;mmpJ$$gh6HqVB?O4in8B{!9;yhvFh$Q4OSiw71yv?MMTQ z@Pm5t*kUVmeif~5Ut%;N6rll9i$8*V3Ohu#v~1ykN>3%k36o678cIlSo-z6;*I=5_ zK*Ir~zneE-z2)lm+wfhcZ;^9eIEM;H9^osnOB0*{UsH8O;qpBNu(@o6H<>D2?_5;skpD#d6{s`O;Clsaa0 z29Bme3BzY|ii`602c}w9ZC5`;uazo2L9xT~9{Fs^%>%@sV;f=q9T}s}O3AgZ{2ji* zJ)=;5H|D>)WcY0OV#BxC9F08dquP#Uj@-XYbpD@0fao?y&m%^D>D18GN${mv-)Ree zgna1sPkWPRa5`m8WD^-oHf4}I9jTPT>v#r{pV$l{!6DJD4!&LZ1eoQ2zIck*W$eik zTW!n|Wqaw32p3FOU!cAhueJ9_eGWB+?y-bs8yoPvN~z7JgDmO%O78d}iyXenGmT8P zUxo@cZHoUNbbQT)fDsZo4}XzXTCBYMLp`LoICMU!dZi|U7a52v#XQ!uB^L=TZ8T*VG4?m4=xU?w?TJC z#g@lR8RflKd-ZBZ_~LT(q{_X+ZEMIXGDZst*!CZaZ@XX(fE)&>RiQrW0;@OZ@uM|Q z^k+D+Q~!)tpH(di^d%wnSNj6H`}Ax@CP0Z(%fq$@t^yDljvo&ug<2OOAJgc!GU;4hF=F5+ z6SE)gB1O%75}yoQoYnRc4Z9R|Sldf9u(J%?9r&y-@cxC+8lAWcJK6RMACIY5t~f(O zI$JwmHhG2D3ibRIXJ`Z4w&o1+A41&K?Y4nCwcy(xh>q$X_bRy;x*w)V5&@C_`IfIxq7C(0H3%rzvQQZIK?cL*}s;&k883-6K zI8jmYQ4K083QE)}LDcXV@P$qil28?SCJe}{Fe7LMC(KO3;TViX(AuiB_1q9sVtL?Ol)=Sb*SS(Dq`%1UH>(p-;4B%>!`CO!&`Ceo` z$2CqvZFR)9leWOK)(maE$D?^7y0fd*=|6Bgd#S{oN)IL0`H;A6oSn#$r#DnC?BH+n zRn>&6Bz-OSZKfY4z1KertG(5u&W&@s$z4l)H2aZ?yOb{|p`TOFFm3xT0< z1N>wNGehO*G-Ws5E{&KDRk@v19NT0@3rQI*NaeR}6e_!fN`IlUx{%6=AsN+zUkAP> zBX4GD>*GFwjOPRDD>ioz5sq^=Vc_juPCTxuW(8C?^c`s!K!v-NN{mlOvG`{lb< zZ;My>BX1vC{C6mRbam`I-IqmIrN(uS%1)=D*UUT0KB_8wdjZ>lgp3Lt4sW4Ov)|&k zlRtw?DbTHlql<}7sn}K04dVvz)y&U$@vnRrW$NcOcdW_-DxiC4 z>MEefkh-v}q$oa=c^frUb z%tl+uxJ-L_>oiTct8Cx*ZxYn80o@BqTzgo;h$XZs+is-?zfVwN?8Q}8OV97#5; zyQOsX)0dU^ODRM4I8lM%{&@Ri^oYG8H`20rr2EjsztpVvqe}J@GHP~Ko1mvQ!dfMr zVIVO?@(Auv#dN5jjtNarzU&XFV!AJxwKzJcDGf9J>?%=+;C?E%&ofkPQ(6YO)N}Sk z0z&ip)`a^N$Rbky`NZO0fkZR3u~?-9lH8l&sPjdytp4m7N~KRtSQbz7CE8!|MKgNE zclfPd_QYcO=JT#1tj%BOBGMC!$wKlF*1bFcD7Pu5Kl>sP9V)#XD!rh>*lSXzik9K6 zc`*8nK9gNfbhP0K*5_pj6UEPsr|RphRE@=9*kNl}c{_e}@J%jwqlx43W;wB_Y5PtK zO*>eWrV5x!1*!T#;?IB#I0A`NC>=Mj|AXJDi36Aqxkk(FuXtfph)At?M50?*e#40U zR!_YyPlZAp*$uwQDP74zYd8Aq)Wi~hx;Nx~o8&ZUY{i?t$PBxA7_f{G-sF4p-2|f_ z43fe>0L1K>`jF{s*_w$Z@dN(V69?p)0cWL!Om(conZwDvNHWS^iAw7DqWKgjgRo@q zU8r_FRNh%tRlwgSX(qyjw6bhJK4Kk}pnQ?1s5aSmR93d`cRT#=+kIO%tOjq%Xpo_w zO!Ga*1sfz!yv+(T!99A9W$HdN9OQE6Op=BDmF}1?{^CICjO4l?*Gn9UF>)2_?))l~ zwx^H4Pu>4SKRJ6Y5ArhP)UHF1)tasX`-!w=xT=b_S$e+Miaow)pUsN^x#bjI%(s#y zxz+0?PKdkL1!^U2yG-)zW8{KBFY?$V+O7h{Z%aj$?J(E!n~6(BTTz_gotm0Xhxx65vD);3&TjhzRTro zrVh*;WL6+Ss_NlRsD(ttW%LmbxuSc}56XsBVb7DUmV3Iy^!_^Rpw$W|)>s!}JpGHjU zK;~03hLU<&CR%+0AF>)|=G3C&x&E?g|8rz73oe)1%^^rw$vcU#@%Kz>(F9HN7~I!X?x7y>=vPs8{=3`%s}F$Vo0tJ z!GIaNrN@>L69-IZny+)r*D(3|ymEA(KynIGE4$f;MdMvoDY4A8Xt}S!TwSI(>u4K0 zT`e(CNH>2?M{z<7mE^&DEjqo|-7FA4gffFQ(?EG5CD!q8s=yaW$a9PF6>Tg=UT+{) zG{ys^T4YY)w_Dl{lab)$50du78Q7J!d=jY``R*aCE^~9nYZ<-3S6AE35|88c1Uu%S z)N+Ui5)TuHTM;HJ-Z=?>s#K2ms3e~0C#~o{$(7(QH29x?ax+OhX)+&79(9@bbNExJ zgX-|bpu-Y|?_5ZxgR7-suWs%~)08r_xU()X+QtN>73UO^9AzR_mq{ARMvgy{`fZsH zB|9{lt>QrPk4V;Tc}66|MsNSd_`wj*kBhjsjwKoc?%D9UNiw%yCa)p#NI{63&E0&AWle+O0}?xA9sZ^1oB%6*JcHF~#>q&as=r#Stv^pK8{acB^o1e)z3c zE&N(ZYM8dxi!G^{*S9+SQ;@C_L-(Xu$9_{AkE0UK(rq9)lOL&IT$&ei^?xG`^ymeW z)@3gLL^XHwONOytIBC+|1uMiWB5mqkqSvzX>B?EN$g*&qn1eM!;hh8eSzJ4cb{Ahn*z(bnSL;u=SHgh+RLxZHwbk6K%BvEMrLj0_~c_cJR?q=2IP9l+i@E z__jjQZ#5kl%f?1$6VqTioxB-lq$L>3APOT7v>JnBC1n{vaDxQbT&!{mB(5Nr*htv< zret@2g5=iv*%FnGez4%~*(kWyu;AFy`L-li(b|=REZy!Fs&@_^nxV&tCYSH8Rx>oijt@JV(_WlIKB!q5Pr*dikW$@9Na2c9SEj;`*0 zR?6P}tdz0)Sz*6hUA}ieJ6~bps0P<_wpJKJs*gfpalMOQjhf7N8FR3y)FPS0U#vVt ztrhL4b=vzORq-iUd#zmDvTN^y*Y!L|6IicKfMgwfA)Lpammby^xiTFT8|Hh$?_dJS zc`(n|`x|!f8FD3m-fmGvJ9HJJ<=yW_jq#Il>Gz;GxmlE?^q%up=N#(qRIqfX`S`XOG@OfT1X`4%RJrIg3* z;1Y5ia&rPLYj5U*noclSUk&IGr_+~amz9g-xpqAU3E3^uoDbD!!5Own(VK_G5Y&X_ zMVK5tlMJ65+=kAI31@u2Nm$q{EM(;-_!coV*@=iSos{;bKjnQFGcsfJ<&RwFXh>w7 ztx~d8V)0MAIb_mrTXeX$5j2fv?F=De9WsPKsWy=}xH*#H&w*1qws50m9|VQ*m*vU2 zOeIQc)IE+joq=ak2Su<)bCGuqXDCD3M8V!ODJ#Yn<|kP zWEf=-a%lYs&Z!^k5&4n!22R;)C^aSrq&4ZjY>+F`I(@TV&wfmK@-D|ZYn%5=dKr&Z zj<(mz@SP>>-VDdz$i(;gL-+togp36<0~9OxMD9W?4o65SHuy@Z)=5t|HjGu(t}!-C zt5rptCYwGiR9zLWca7J$KZ-EQq=lV~0I+YB;?o2Fjo5|VQUQGhjn{~=a**SXcI`7f z9q|hWy4MN`tXsyXBy?eEH}y|kl|BjV>Y}c-_C1{xOE`=3%_BqfnJ)r`^a`=$aH|ws zRxMg-sO)#Y4HaYkw#dBz%9XvCLOI4s4MwsBMD_y~pc!HKV_R>4gfEb|7l~(A^T7hs zXYgH@dDEzc>2BricPZ&zG>L424~p@mu&hHHZ{$QdN3RL>mCcI_pcPwwgfEaB0)+Ym zI<~p`S2}upAaNAv(N`lS`5uE7$whdbdui|aCU&j0_M=$G&tQ<#m>h?Rcg_ff+sQTW zfJin?GF48aOC?u@au0%0$y4101(ot5v?sg3G>O*c$59mNFG4IN(;NMW0lwI<;7%#& zOXTm&!NyA3#2)FBwV`oTrSlvH-}-|)$4orub@QKdEG8Xn7hcjqnNo^Xv|Dv5S2fHY zbB>J07<|z*F{P%e+}9OF3p*oywwz0<{3MsvJ{P!q0V(7y!uG(bnUc85Uh!PL`2OTH z!A8dThLqhQ%P?da|I@!y&Hn)QTZ&3Izr_#v%br2gUR}p+Hu&PSKr4w+>+ZVpT-CNne53IcdeK}9ZnkVbH(^C6qsV(FXNZ_6a zh;}DStG+->DZm zkN#}nr;aiLi65dQGIY5Vn)ak;+NJ%nP|-&TGKxWH)7*CaoeyCsjt+5Z*O%cBC*`pn^{S6FrqwQhyt6 z=F`GeZY_Jc?So&h3?!a{j@Kn!g7myTXXkF0BYn_~cK?9(MbG5$yD4aO{`S^E6Slfp z_P5;gGk9=9uXv7gD9g#$(e+?EetYw_J;oH9lvY)VHKx?r<}jNh%zK|> zfY9#scqRFx$~C`pIZl2P*|K!XvyXTH)%H$Xmp<=TA4?p!tv@1`%o*!zTYpECnTY*Kc`6nkiw0(e>{l3^6Osm+(ZFKI@}$9JI@v`w=TcF5*tbF*aNXQ)6?g=I^Y) zv4)pqZ0ph%zHi|fqiiwFQ`B_XfTk7gK4=A+-y^+STc0w-mbfR=pTPJlbg6WnPis*( zUDUhSD;q9&@`f zf3~lw(lRF?&1+5QI+LhZ+?;71o^ z{lIk3-Pv^jlzF<>%@IHQD=C(9G%t{lcb_2b8CV*d)Bk3N5QJQs3Q{j8^_^0!vW^yL zxn1JqH@FFJxZ+9v9o6z$YlXJf=hDH~kvd(x_NqrtqEY=rRTY~Qu689bc&`${O(wNP zWLagCQB-0JRQOJAfV64L)Q!a!ob)uyCgK{m5pq?}0>b zXxNjU6K9|=cf%vE;i!%E1|A}Ir==%I$BwRjTwe%dM4Z(Ax%P1#=F%NJ778EmTDkCWIXtLW z^<|IAq}6TPINp8;X>fA$9GEwAEr0e-QB1|2C7p4;B3%9&a^bWSa@^J{{svy)UWdFj ziIWn6uM?*2r-jiIJBR*0 z-r2iOYCO-R%Oj!5gCeB(V~i`UPr1#ckR6YaiWPo68VrlpIzhw_&JKLnT;TfW?!nu* zm}9L=fY<4zuF`H1*0Gdd7DJRHPjMqS5n?(nf|9>jg)hp%PN^43nY>6eJjjVH<6YTE zxYE+{b8uiMvWso`TvA4ljcs|G!jZ!hNYLnqoHvV0DSMtdyBaeh{i6xa{K&K_W~*z(cM2+lV*&GJ>E!Nc@naA+&KsX~$(_=*YOtVOfkI*a)2Beb>a@ zC~=cXX_6~S+^R~*i%Balfw$OZt;y)Q_EY1DLz6G#iC|ZJrLryX7qJ=k4ob#BXlv3= z7}&{=2!pPja=ams{5|7d`)OV9|2)Fg$w%SF@7z9fuAZgGO(zX^&SuM3DlW~+pWQ^l z)-irdt1UZ=VUAOunXRVDh@og8|J?EXyY7JNHLI>gP_km#3kn$d>!=dLG^ZXQ9pBMQ z8jr%+_Z+%vB&#w5F||@FDPq{GwA7s(tq_YKyWa9T;@wbfgO%JZ!tCu*#z`gKIa`Zd zP?T7^(-K~WAq^MKAT@_RkMj}yFk5+nmU`HTZTSbG1Yg4sMqH=l_~wAqI$=E((?${h z@Za#4ic5UIr6I8`I^$+e165;^sYc0lO_SuB?UL3xsl;wFY9pQc#*XgNdRtBy0b>TR4P5)=8Q?**Qv^FVLQ<3(!rSen) zSeT2`=B4N@Vt*%xDFZ*;QZ><4#YEPNzqu8?%VP$hN^p{k$VgZ(0`~ zU+hiRr1p|$iIEhuCQ?*C*Vij>WFR5CB#OBjOm(=Kl;NlNaQmcFVGcP-8~TO_qP8Tp z%2?xjU~ElH73x8{vCgpR1VjvF#b;;wt7RYTXWsoGIwrg&k^@;Xo94J;l@R^cD(dv4kK7 zRNOuI0*T*{1$^fn%<=9t-9j>&Z>D~SnfgCSRK{P~{jyr9cY&-GJF`bilQnzSf#fp8 zR3E<16QquC>N3CkjVH+U(*A-arUlDEm`)<+6!sGGFvV?uh+6-O({`IsaG&*iEB)Dj z;=G(f()sKRbqWvhfW>3b_p>3rya_G~`r_I3~<)Rm5HF`Jf&jr?YqB*&U+ zLiV+j&E2>`oJMr4BsYg^CR^jCTQ#%E!X7vM$Z@j+$@?W)?Z8wozhg%CeQj*0JLpJ^ zb^2Vc+}7FLnV?PQ^|sDzI{TkYzJT?X)q}ulD4lbSQR+WCo)33Wk@%KOhQ@{XPzkZBIGJL2ke zfEI3=L$Vw3dR8>sF7f9bWr$s;P9)rgfStBk|NR>KkLQa|<>a?r|+5S{=J$ zSY%RY-Vt?rDk(=UdW~)GhPF(L`@whhyyCiWhT`O+aIrI0oQ_K@`P00f?fUM7F00_M zb=L#veJU=aHs&2A@zfFCG^OkNn)BZa$sR)#LzW~y<1S%dQC;|7h0(5!r;1E8(ZTG| z)D9Qob5_kN&;FFvqw4CMofZotJK-u)>1|UaydMMynd`z|7tjwR>d8Ky>YJCx&(SzK zc;e}HmRn)BZ1XB?fz*MTz^q@pGw!Cg!(M&(!~&xdxx;>1%p!xF#iq{UsiReb70&hJ zf%XxN-i7Tm&3)!?6rO1>xYXRWn>*ZxR*|tuxjM>Tfkf)L&SDHkShn>uu;;4KZvl;2 zh;KVe?j%QpRpQUW?8Vu(jP1(bu-3h(@$?{2yc4)w)-TwDO1{UGH5HdVDlQnx^!1su zfEjm;7~Su-XY`tL^wX}NDXV=7BoaupP>-HQAsb)za?__coe(|L-H1MC#J)`iIU7gt zl!D-D31*ZmW;=3PXx@=-Rh(CtadIt{7D6aaOEcEH1EejxEJILTA}HjQ_*+u$b@ zGFECvqGmY$fok!wZqz9SQ8}a{da0(Fu3YcA;$nv+!{s=zOun1Hl6Jw>4(E*&CQXT> z$sW@w{X%vc>x10A!J$oA{6M9^RFY*Q4G<`|6C;cQNFfd}Dr_3Z8?31^OFW}+iWpfwKE|aIGW-KV1(HLo1QiiA0*^FMVpM2dU zg@={tgJwJsyflKB*s?{9ik`c0j(Ikj=ZPZ?axjSZmBI~h`0Z!|F=v<6R$s@jNOZ|U ze&}PwyyHCE7hJG7aytQw=ff4AXrZ=*r=5LQFT8EW{JFEsCSN@%SU2W69;D{!;$dY& zMqD_Xmx;@UT+A2nDDR_e;-tyGs~d+;S~PoZTl62M#pD*bq(2_;V zZyzop5WGXP8Re5~^L`FlI%>#LC#J}}Ft|(!8KEjQ9>CW6qQ0pX%{01HwP9(QzTh3D zLd(t`vh?i!zN>;`#``7)C;LJ-)bLR|*;h;8@G-n?jp(Wy8XJ~0EXe^TmvPSH+GA$T zYFHc@9$Yw!YFs!6z7Ii|=#%l$yA(os5od*Y!@Oa(Z=8JJUWTA}3{Kj}!bOp?rP0NU zdHWBYoO!!>vb+5J5x$;O${Z@x-%Y-$K2;HNl>b2G{~R?W90k>=Pks9?i=aN2J6}U) zIb6Hkk19L&f^&&HZc%jpY{O4O!>vvWls04nuz2{G`Eze;5YC{Z zHcg6WYU$8VL@UqCk1F$BRz`H5h9@ui_Gx{X;+xh!s%*r7{$L$rDkf1hT(sCVaM&xX z+Rt@4A>yes9`qQ4q48JDIan@q=>n~IEMVN5!^7+JB=$( z`;b z#rnB(MB=80;$EISxUWJlo7q4?8zjW-poYW}Dvzs-9dEx?qSGA`DQ5|3km0FP$pCDE+Xq8Tkb>{qD>~ zvv2S3n>1tj+y&7E#tnc7p{!x~tcC_^j(q4jFyZ=WztJ#imA=UlwCSitPYuDy5@Q(q zI~IGC?^bCsbh5WK7};F_FVgEQ7wh46ZwN>PQYMixE<{Eo=1o)KpETu%Por^&o7YC+ z*-10vf6IYE4n1MycHiuV#`)4s&bedmV&C%N`X$pifQ- zoUNgc0^O*%E}&Y4#D~H#4ZQ)##H$4|@zw(k*U-m2=sh5lwv4%lNh@d4&NuOZOy~z5 zw4S+qqvo~|2*re}=YdRa+dSM+ENhu~7Xz7i-}Z2Ft;yi-2f9FWdkAQRLQGwJ7b|61 z0jyK#uRx6o9YqbBw3h&x(1{+zkyVG@N+3h;zkm$Ab}!y*Uc9%xcwc$(j$?XjWNeSMz+$-`9xee#B>;W-IpLTw-udKueBraqI51N)YrGmgZ>0$c>cGCI|u&(V>QL~Ko@EX3+N(+?g28nJqR>WB$l&I9xH~-PE)Ux1K?9HFohy~bBS1#Zzx1HJKyx(o z_<^p5jR!Kgp92lnlAm*&%fWCU!$Abdl;e9qrW}s}nR2`hWO&{SWKtaPaR2S$E;`P`T#24aiX00c14xD-Y*C!KHj6kcoFXkcl_e!%g(!t?+QoUcAjfqcz_} zgWOmClR$>v!yfb#4|)p7q?GHEaNptuLE1)8Yweh6fA{FgxARNT8j zrjGs!WT^bhgZwACxV}KwXo};3Oo|a+=p>*D4XyQXb3EwV9<;)PHnLnkPE(vuk7e?W z0U65cfJ}Y-)e8-L!wp^TK@S3%w3|Gr1ITc?&%d(huccT*gDhMQX)$WZwNsLCg{^Qbdj+>IWz z(1Y&rpbtE#?+}+>6_BA<2V`XRu!sAx2mQi>p7!E>1Y~6JwHMm^ESJh)AVc{q4;tY? zS9(y~gYNgBpL)=*Jm?QVS7~{_25MAjFd3LU8u+SHTpY-7)$WD93^ZLs#X-xp3Jn07 zsn9oph(MiJdQjYh9`T?*c+f{cH)`5{dbl%&x>PRlpldv+4k)N8Rsu~>sM8DG0W?u@ z?*N&yWq`&j?o%LBwp-3+166Trfoc@m05n;lH-St!-T|7bxFgSVam^ldH<01}#~$wI z9`tJuN_o)lJm|kY=xYxuW{76;y$Q(3ex8S033Q$E@_>hX9>~Od)x*6BbdAP4mmouL ztOtF|gKqJlM}cT8)Z1%7rY*eVgl>%sPX{uTS9;JhK$A2y<>7V$nY8;n+}9p70RwBOTnl9Ko$le* z02wXW?BP0r488Zg(1Tv+*Iwv1E_5l+^q}tpRVjbJ@Nh?5 zI~>TQ9R(Cp$`(+)LQh@F#+^boU9-up4X8nJZvmk^@HdFg(!_ff$i&+MWLngzqulWj ztBFqN0-z~MW$I-<-?tPh8SRE117y-R0+~Es^g>_uLce*rn_?7@;p$sJ)0Ey_UT6}? zl=7Dz?pYwi!9k##G{q5Dxb%(%GNCCCw-acp#{0~}9RxD@4!_b(d!YwC4rI80)x*6B zWKs;PbVmiZ0U4=%$Af5AbhD=TjTic|7y1_u_o)Y+f!Q~d&jB)&uLm+bF90&-xD&{5wFbzfxCh9j zX!mf>d$s0httG54RA=NVmnqwR*T;c(|v58kF8|J=`u2_b;GX8hR8R z-fV?N0vS#(2Qq0JJ>0Dx?p_bK!NdIxs7|R2oajOq0vUQEfsE$V15MX>Yk_7c^gW=N z3jGx5Y@oirUji8pe&^xd^>Bf!+&n6POo}UjOp0m`SMTAL0h#u(+6!&+e!7@}QpH!^D;7p}xc9K&tuk z`|vO1cYYl5brQcT`6qFDYnX7luKx>uE3{ijw2-1&PxqAF~ULywQ-0^D!8dB#+KNK};{e90|XnBl6?O z8#Wt@^XbULu^W+mj-PA6$SPnCa}yX@56oc}f@v$j#K4pW^5Z-Jrm{32^Q2&oF&HD& z=fHG27{tV%JZiXaV1BBMV3_sh%KmRKuT!x(eg=|QRu^-a)4)upZ#9_nBMr+VN?$On zD*7Kr^VPSEgJoVNtL4rw;pK4t=+2+X0$WhC)dM4|x`lE6qX%YF0cKjG z*&QfMB~K3&Vm>aw9L_^sWCaXNS5!d%+>##O19MIf%q2ZA!5)|!dtes# zz}(dX)7As?A3ZSQ_qUMe-}bM`L9OewS>c9Rw+az9%^K$}_Zb8meQbz281)rr z;wfp*B_2C%GnO=%{G5ztEn0lL%K>L}IOk&cUEHuFJY(_FC3=V`Ut}VmX)|WIo&Z$< zv&DaUo>-j?#~x*A%)>e$GZo0jG{-3PM~;>XMmf$4&ZCeAs?^o<=Ap@FESx=`>Jl<;{5+H=YuBxysSN5+ z&UQHo3Vai~-UuE4gVPqw6bFCPIQiw9=HM33l5;0+ViZ5;5^w?M5ztr7W!bH>XWTA{ zmgdKwIb&(VB^OUKX9S#>qEZD_>&RoFcaX_YbkULd3!Ib=#vDs>a7ONjrggBnqy|xt z(zrG?4h9ok*+MyY zmB&VoiXI_53?0XNr4~k7kDkGD?5HApc&~J%nezx*;Lg(2BJ$5GOAkk!vm0hcv9U^j z*8I7g2bgA*?t=15D$4Ty{x@Hk7nfIDY@+9UVP3;*L1tBqT>beYZNZc=o_&VzA20@% zKbB?br9#v5N81{3n2$=|h|c^G_hF(aDkkyrM%{wk!N0f2JO2-QmE2Y4<=`@LBfvef zse{tNgA%2pX|kVdSiysw{~gHH;E;JCUOa|XS9RZauN*AkWs&k-R_|oz?Ku8A$ir60 zZf402Z%X;T5941{MgNl40UeQ@pkGNAo-ek#8lEJKt%joYDrdjH!D(6B9>~us@q$-Z zUDM5Z^)kmj0#CVo(QDbKcD$4bq1Ht8laS)^CHF~wgXefLueO_`&-!k*le)2u^48Tf z?cYc$eJy!Vtm82mphwOp>vQec4}gKn;wwscQ%e}V?LuJQ;1n2$lh*eMB-cj=a)p6Y z*dbobyqKfF99?$uA)Y_FcQf&Pn+EVvQ`7p;6ge!+>tRgheBq6P%ufOKOxWU_+K8*1f$^3I$EXH}OzAyZ_H2y*_-!AJw{GHzAyN13~WxbPmL2lHT z%XkZ+-4)*~I;DfCuC)f1yt`HzK4id?T}niAsr8VzqtD@$>5;?`q4c#YE6uTJefM|} znb;ko4{0JWNn7!eFEY~J(2m^f=I8hvO1%!`<=B=a!Sd+RJA8BRrs-e4O0HA22m|)R z&k(cgbi40nLE5o9`Ki7pCJn53dF74X+v=s4aXmJ80Ht#rpKuSWs;0WG`@Rjc;Q>|9 zx9wIx1yNx|Tn`O0o+omTqU#HyiQ7dip-qw>b^56FLf1xX8+=u~eAoS58GT!tH^w`J zG2R3!buSr~S`SO9tcRt9T|duXg5pTRC5)dKL2hcW+?{N ztU)=p8r$-`CAz*u)c4LH+%)p`H16B#^Sty}+uPaOMWi@uo3uYvU*(*#-*5mdq~b4= z&s9j>dbm#`T7{KXgkiE z=0l&c)sn{KvG<3T^MKtt)i)+GUjob^oflE44|2zZ0z2o3s#}!FLoCjfcS{H{`yw^Y<+f;lr`aQB^73w6T>dr7?aPi%B?bw( z{*?|Oad@JsP8`QBFeS>RuM3|B>99|%n`0li%PaZ$yV{`d%3FMU$U_n<28x`gmf*X? z9+B*heEaie(T&o`ciN>nS3E4*5v{SCMKk;dc^kfldwpv~TbwuV)jN`_X6M=)RE2g| z)#ITS?-!1G<9XI-m& zSsFR1zCL`WQh1x2O&*0JDBzI7dKWTXTXXfXLR-~9^haBkolqA%Mt-Pwa8UfqZeu_r z=!^TZtk$uW(k2%f8N%DBQ|l$6@I7R1bN_CNT&y}mTFDcWsW(Wa^|VTkilStDEr)XK zX+D2+@6zyLFD(?`b-c1H?)vE}m@Elu~9OmfZws;g<0U=Th0ZUhj1 z9}`guzvAy>h$tqgRg*;pvbV^M|k8m3i?tLHnj4Q8BV-AJ=L^g)gc_0mybvxfu-WfvW zK9P66yg^1=?oY0(_{PghXk?M^DlK`p$7FxWeTErU)uzAGnz%gz~cN3?A4(DgVHW*9WaTRdIuXOuTVGCZ1eN zHF^9Q$ng9Hkl~=`j*l#AH1^o?q2T)GM<$2c@tL+Xyod`rz8JA24x;6cO$eV1n+5*r>bNDJBO(N8zXv-P7sVdxs{7k2c{CTIm}EjKBS$)$lBZ!1(+sDh1_y+ zz6)kw0p`bG79*croM(lOhsmF3`3dRmKaSas%&u~O`Ex9acKF?j|4%y_saz&`&O>9oh~KvjR~ti8q;xynCcAKxj?{nEb_vS?&^Cea)DO4H3Cs5L~ik z(UMAc&9Pv;4o=Nd-Yi{2x-GYg>+UQYIG=N9288axv=?7mX~x&?LyMPK<2w`5x8^?YTO!xW=qbjBhkGF8hdO z|39owlpRB#G}-4X`Hsm~_^b3ym(>a5>DEf)CWRNh>+P#5lP|5ju>5nKJ++=9R={T$ zoy71m?YEQfh3ImDc#kk4| zVo0TP1>-5GZ3pJf`ef>MclL&(vDVmhkUW+rDdbtq+|XT}7L;t3g%=hHo@Y$MC`FtK z7>8w)PgXx-?Gb5Gk+TdXW(Hx5Umled%pEH!)w`=1=ya`WY_oQ6rib!nrf4B|(w0NW`*db7wMSywUY@7m;Y zp8YOAnO!!O7!Kwj14E*$2Z_XHdM6=q6lrwD#EUGn3-SvjYeZ0eRcJ<9ep0^0h&@|D z2ug7dY0MB?3rN|rrxBu}%w7m2ij|w4tEb`EiW1&Hk|G8Y50E|;|G8vs#~Ump0`Y6or`{63%sZ()Gy+;J5pgL4UUaS@&lK6_!-nW(G(XbbfgTxQ^TQ&rEtl#-DNPf5>N5<~x zT^8x5^&<;K`bd~rA;8s>Dguj0qL{tfg%6gJ@>i!Ed(RUcKI;{IM66AW3JVt&v-1u2 zArw0%&x%>U@0W@cRlK_r-wyj(`mwGmwWbWTH_NLh_EY-W$-#aHA~#z*2Jfyk-W}y- zuwRm8#g}*W58Qp1K!0EIouPkc-hbkk)-|QpjRUj0z^=H7wTL9ZmMi&SA<5NWSx|Fa z!f5%JNoVkr|v_s*9pURd%QJGx)|r&|+B;m@iYXsy;F zKOn{L!rh6^#=V6ZV&if4$O!`@(}-@bmRMDGns}d0%JukAWOG@*lt>3ZoEOCtn{O$vG#G2QDjOgCK!KtcYEE@h|TRp zduDg0wZ&wi8D`R~EwGf7kP@8iHB8YYU3uExeiP)j66)*vY1e?<(gTpo&nea`_8r4y zljhQ@)=8{#detTEXoI{G$>LZq>)56h2mS*}(NhuGj{%H1v<{Z<%2dKeyG)a{wVN?b z?dmt25!Ns(We>9BI!ah48o;ZyJmsgaMD|Mu62B*5g4d-^f~YtWyMu4Lxf6k;;*X11 zq;Fn9BvzR8;h`#foobieA^uA0b+HfoIaPg?EEU$!d%kM6$mG}2k-YX*fxF^Wj}k%N zecpF;?Z>vVWjLy+{Wx%U6@hmRs0i*0teOBQ-Kf28FAzN$pK+d)J|nPpJJiq9Vw`Ns zrGb`4B+5$MuS)yfd<ZLf)Hp+g{HLQSrTW#f4s0GQY)mY!jb_eB*hL&y8|*nNlFK?$ycZB7*fZ_zZVbTJNDOjcPwIkhrkz z$bxOG#8fF|-dbV&pg+)j)gj2uv|b)%3ItkCC4|?M{MFvGg;qM9tb`A-lCYlmD?-A9 zBAD1^qwwm+Pu?IJVe<*T+UuVaGY)?=9nEAe*{1y)I;ly;w#ReZH{t-+6Fl)?k=5Hq7= zJNPl#i$?vMSfQG*90<*h0d9R(YL4v~;N0_Y@#m$Udqr-r*9(y~t0objH_(p4xd6XK z+%cFm(O_i-KN{oD%lm-r>|QAi;US4Q&eAW3T=YrV$ioGL(6m;G9XCTeT^Y}J#bBb) zC@dZNrB+QKq2EYkv)I|pt2{wvYyG=DId5-e-(_xq1N-wRQd?tMg$-kA6*tPN+Cgg6 zHWTBEH&8R7R&~}BagAt?kW`H?bG4PeqI`b(Ixv?Q9qIN z=aD+<6Ze{uz^JUzR_P{*P{_KDcY5-h-A8fcb{K#u>pE-@aHM(EygY2=H8u92@S|K!m zr7AM}rc;u8qHk+{A~`o6lG`VeD`X;9GWCUSs>QU3cxRDm6fiD2R^DwKw7NZ8wja!g z@Da|xL2iW_7RHq}+S)7;I&af8R%mMtv^+sQ*w0GK&FQV4j+ZSjS0(UWtyFFlP57p( z3Dy^hU6CU%A9J=$5oI;y@=Cx+P5j`1$n{d#=cTYlBQQ0F)$m3s@w1{UuBpMJ=za>6 zFPYLjA)HE?CNy65teT4EI*QGNSN6_v$_f?3*_0c*Hc?k@E{;G-urM-f%%0B&{fDrc zpHYr0xiFBg!SNSQp-r@|7b&}q1W#PTV|lmzgiOi|``;3gY(XJ4%R@@7J66&-vLWak zO0XW^UeWy?ndtL%b##5MtZ8*O1`-X-pOo_9nw=<5_H%6vVr1YcXMB?E1a>#tT6_ef z!pFwRgRDx=^ikG!>+dkRW^6aUGmjp9ejp(`WUKvS$Hc$zM>8ZW?_9k*<@bdGzeWqeMmay?9Gr)`G;lyld97Bxx3!Th`Ttq zj&&NNw^}Gwbx3dTsIZP!%^1Gviyh!2rFA%$muPN`_?O3;C9C+C10zHAEKvN*<02*H zpU1yEKDu?&QKXXb<3Szu=tlHz5yqwQjtBUrquNZ2%>h1~BNNt}w)!6x-9Cnb)!@kN zmt^VYC}V@jtWayqbdWW9lOYE9aw38rHd2Z(WmfBrQ$)Dl4|$&YblSvb8JJrKGe_Wm zRo7#bQRVR;(t8FE2oFCck|Eb2qnsQ~oekO#>l={N;YcXPF2fqh_?1D=1+KzLm!CKZ zWC0Uej4DvBLB>5$@9qmC$1xDfPZ}voRr-CKe??)fsRy$AsK$0Viy!OwIr8%bl6wX7 zQ!v?GK+0owJdpNh?)<7G_J#k%J=UZH@sEmm-hKO+Q4ff#smKkXK*x4>rBE;)H*y9g zq6%P6l9=(Vb%2G4%$S!9 zlQI*FZPHhnBWKz%8R(PaNwG~oqg;MK_SfaotlVcP-<6&q$$e|? zmB`laO~>*h6F|o9!gKz5BZm`wvG$*cxT5{-)x!YjUN6Hlg2}Voo9cFg$VotE)+L)+ zxtGcGhj>oRO{Sg2+w3s1G?MX7`#J3^#X$Wvj;_SJ2iU8HS%e?ZN4BxDEYre-xFPWq zl5UnZEPn`~(SBHdCHJfxSpHJm7+uj0tlw7g&Yf4$<6T8bMxGl;+@{HdUHSW;mv5u} z)LGaNW!_!cN$iULSG@bEK;q}5jduqEiQg$OAduJ%5bOAb&Y8|9ZX1StO}pB$?EQVCPDQ<(s;+A60oGcP!Cc55f#nER>OKeLpL1%I0?y1w5g zEl74fvP;Fg&?+Cj96Dc86$-ybgzPe!ld*1_)w)IhsFtnk{kGg`pThkfS+XAOmMn6i zvu4Hq(%+$%6TT^p?3PiQ6MB?JIC>J1n$xplFU)>Ye0hmKa-_?2S5uppQS#?RMt(F9>kq8&+p9UYG1ckwZRZNnQEQ^z(QQ)ViqCl z?eE^lPjku+Tv-So7%IE=b77kJ>1HVjD%G78m#a8Lo57c3uAgH#uKOj9z20t*h_y>y z`s@yAO3`3^%TCHRI5JDtvTQs}yv|R1%Ue=l{NOTPcI&0g+MSBau*lCcwom+FY@ zx21^r>&vV^6>;*D<pxXK!^B4qC!TlW z_PObW=^_Z-wXh$M;SZh}@EOPAS}La7WvQ49*u>Yfy-G%Tx+E%|v&BhJLe5i>A|2bP zWu?%uM}d$MPa4iEn%Bn+)a=@w@R;nhR*Aj`64S92EU89_=M>^S4W|o%vcta&o;(GW zAxt14Te5J%qNDL-Xs;2Lhweyrt~{Os*-gR*5$`jDcb0`K{>k&`aAO^mao^Oph92b&+Q{F zc1Y5E&mE;NL6ka5p9|o%2e`N1)$w;AA+u#^Sk+Pmey`qD*Q`~-Yasb2E#ThF<>bZ0 zNyQS~Z4|&0$DI(OIBW>nZIXm(rw z(n^WMK6`ADeW4YXQ|I>BVk=$-kRFQ+<*Qh}JTog9{!W55v4}qFm+IRhZBF#aTjMvY z|F06awbCD3DGnM~-cfDwCg)Z><`m*r@R4HU3lb%+)`>Q&RsS)8rYk!_^54o&Ho2`W zQjm1~Za$|o9I<=F+nr^GZ%K^c{>-`TQ5!x!Bx~Ert?*2Ua3FsoXQZfN4Rx;*s-O&c z3FqX36EiC-6{foACS^0f+8$A7fB#OxYr+RmF=bZRXM`9?e9v$rJCfHCg}Oh5oE&RT zL5OIdl-4lG$a-30Sx<{ZxiogbKDs*m-w-t>(23v6ZWG1F$P5qJEuuMgC09#YM4#v@ zSPj`F1(IEh4rpqpd^Z3ydi%yA>6c_unr=~b1)sf4G+q_XzDnESxga{OCn&F~&jH8w zNpyJ*Ct}{tW-JR0s{8c$y@(b6T4(iN#XI{Ps(?@QE&n{*NF3Vg0gP_ARGJ0tXN0F^ zd3^ntDMiN=;qYfb$gRGI_C)SX9pTGvgCY}1sYAaVQ+3RXDRm=tu|;LDUuK$!P&D4E zFS6T2X$Ha$of%Q!-R77 zEwO=a)Pmzlg{r6&a;44w;QyG;sIZg&|EnHf1I0!nW<#u+QW>9*8Ce-Q&K@(!t~vz? zSxx(-hq_m0j8e^e*?CosPL@f0)w%+Eq$p5sx{zv?d9T0gerG(3 zjn>Xg7cj`*aN^HKNF~E`dPOZ=OB?xmMG#5L0%unCtYRHM73Kp;*(hk*Du=E%!g%Dg zbc?{9toIt{pK@#Hsv>99)ct`8o=$Mr`?>RwdYYnX|0!^;H_D`=3vu~#Kh+=pHLLR9(8G&wZ$^FtE*>Vju@-lBRyrZ zLK+<^2h)1F_uzg*eB28ksIK?!!*96vXiq9Y}nJoYJD0=ytJtazK7^pigAF2M7+l{tVn&Suxw3A<^Z3;^VGf+`rV- zA541R_==T#@ z+E3|f)PvIItmt#)pWC(1t!a_Dp^Q6aaf%DX*9V#fN-3T%nKSySC)deibhq82qkqoR zB9h!xhh>LY`!iBckqX2eb4E`m+V|*8;w3YV`feMUo7I8zVcMJw@oS*uH5zl~U^q`+r%iv__fygN~ZqqmGXd_Z;J))d{9vP7;>h0?) zBVkzfI2`+><+4sW)9a0F6Ms+SSk47Jpfb;K*7|FyZY%m0X5^JOP=>9#t`SK57>Z~J znlUG~^>O0(BDYYa`10AlJ8J`LcfnXF+zz#{gxS%zJZ(e$DP4J9>DUkMR3&-cD2Z1s za!SEZd3F6LwpEu%0!iJ`Y7ss}o4>){qiGds#i2~ysTMgeAU0ZA3?wH?!>fJH9?P0t zAo(F-v8}pXVAh{wTUGZ0i5(KMVha}QI7#uyYM4*A$fqn`X=PX~F@9S2C-`KIjLqWU z{`yenC>ajO5R0zK(sYQXU;OJ+0*UjG1xursIC2UkK7=_}R?`A&D+!ka?ni)~^tzdU zIf@8Hu`SNPy|$$L%=H7eTcqggsWmT zBIX!NHEV+`;ANhquNL8|zWp?|;(an|TCq>uJO=KO{RsQEB00lwqJXEe-1Il{Vm;v8 z=sjJlQM9xwy+|jw6PFzvYp@Y6+3-_ zyMF-`SbG6{WAL644ahSK(2~uitVN{8HaY#|ng8sU=$mPOM-WYxK?++%lEs$7_q%Z$M!3BUU$&ct+QkB{x1S5zK4zWUoMayQ%JHBEE ztSlmacALjpKz=?c#YO+!{l`Ei@d$@MddD9-XgKD#zXQRrPvdg_*_oCRyi&mabvOB$G?ABu-WHG%il*!65 z(f);WbcGi`fO7WFwf17yu7mQJ$BLu3Jm!y7aN@LtbS`&!BsV}3u9{ZtmzCkthP6&o)uNQ(sK@0!1tXB^t@G#@!ru{#F|5WgAcrGta z1^>!HN*OGxy|r~@Y$fH!Odxq3{KU6bGGSrX_e;_6*5Kb*Id%)dVanKwzxkrWG0BM= z2)A3FQ-KDOaxjg9@z%j~FGzJ~s@{|;%AN~4w)F~Qi~a%|NP0A4F^$h|8AM)sT%z^< z43zAE*o>sCzgz3)%V(>MU4{4f){CKaM4)*E#1O&FP}zb`ax_Dtc(y6F^=gybU4%JP zg2-)lQpdnkV`ituN-ic%^}Ols)c||Fgxe_|h6f%k*D!k=GTIf71qtU2On zUb64dRnJOS;xE*p7;1t>vbUIOQ9Up*S`5C7ZBB!@4FpX& ztv9H%VSXX6<2`wqTSWBqx}T9TrL^8)XdTQTr^|vLM{d$>a`s5#&x=0zmQO|-PPdct zBiXrp!g=dOUrugSW!dJBzu~iLrd!v}wv8UC5?Ojk-O@wqX4vA-4wY`>Ep$%`pSL7f3%7y;!zb zDj9o5=UE3iaW;^-HX7v5KO)eUY95aQ`MW-H=ik8t9Eq*VB%HwyDi!&)>As|Y_zh}h z3}0@qQEs+)Q>#Mdb_XbBU2%uL%P$S6L~J>FeS}k&&J@R7?4g=}DCcTUqaW2}*7!l* zY5JNfj34aFIcoqJMLf%Qp}%nUOk9gdFZh4?Aq=QsC`PQ^oX;6x#F(xWJ4ypD*(4pY z>L@Hrd;0^r#>!@NvTW#Is5}!SKc^~QqTob}4Q_9CD+5?A||zoyQEB?mKKiteYnewmXm_sw|nX=;gXUGLqY z#tcULunUxXQbRWE&5?%95MZEo$o?me-+e6Rr^7E&2J81`iZtcMdBx6h$L`F9`y9%* zpUyx*hAbwCEpTVh!sH$9b=+co3ak4#p2tElynGV7!WU&JScdj&OP-5XnZ#5<*AAlA zLfk~%ECOJ2p9ftkSy7_%l}S-9_JgkC=I?Je?U$JbYZ}}#E@jJ>AJ+OaX?jiR=`;a5 zwwzD*S^#IT7_H|92&tUN8`}tBO3=#dFkgh&!Mq%U3XfXzyX~nNM*VNdB_2z9NvpL? zTKB&Ov-QCxsN&2c+u1L9vir=|fvi6ft?5gdJlGY1dp;J;V96z>JMO4fY^$DN2_%XU zq)9!4)UCCD6GF0aWmX-fdyoN4AXx{5;2&d_ZFpx*1lCfk#4!4^mx>y(eitOy4Wh#% z?G{nyK;mbNbK_sGpi30sdExoAK;kXvFtmQdBL^)LSjOfs{CDWAK#t=i$Q$1F{Y|n*;CJO9+9@ zrZ0s}yT9R%og!k&*zb?j0EhQ^dk(W=Y9n*kZ$-@6uV@jb=qCpfFTrNhm$#_oUWK+) z$Fe;dPwoU8^7!3GI(AZ2teW%E#50{r2Up;go5zSNLQBfYCizPz$2(Bdm%kGZxjphL z(wU4;2qfgS3-e=8ATb55&5siU$(x0i60+9|5vxV+Wm)&jB`?@hE0y^SV?xu(jqV>QqQB&-sk>7-#iYJ5{N|CaY^0Ljl8!7cCrQ-_ z`gCb0%0Ne@2yTZ{`WK8tr^Syn+T&ko3F>Hy2Un{|v%y}z~D$)vUB$`; z1Uh7_XCLEEk4v-P(u6v@O|}7xR*n(wD&>c0{U0R-x_vFoLuIxn_6{Ib$>Sudnk?&G zN$*EWXw292{7qkCV`-x^RQa!0<*x-6{#Lyx{IL)`SX#rOmE{k#JP!e(R|P-N`-RdI zb^?hTX=qJf?p63R+Am$zLn5c}KL{|*yF)5XRO2>7#%UcR?fb=gS?fm=os$#r(Ddb5 z2t1JZL{|Mc(cvDYxiA5(FhO z$q+ygLm&ea3`tC8xKz-Axg>|T+G*t1P zs+HgOyY@MIW)k|m&+~rX|9%6RS!eCF*Is+=wb#C$y@#? z=BZ-Z_&YpB{sk4?iBcKA4=)xXL+oEOzj&YLQ!Jcj6m&bH>HJSKMGYQTjIVa#@Gw<* zFfUUMF;qD@g`Ii=jMjbmF}U7&9ynk)peH(PYn{*P1(n_L8&TQq65$Q1DfT4b|4?bW zF_sj7N`0MF+VHcipl(-e4)QkzbXBzQ#0IDFD~T z0(_h0UUTKF*}gzs*XHj+yaV71G-U>`zQ@>Zn(Nqpo8LXi?_V9lw{FK-OVE0oFSY%I z-5xH2#~#JBiE6yR3kb?jH{Pnruh}iNmmb1y#_K>xt1He;uBT(*$GV487E?_5nRw4u zJh<*&jPKdjI&Oxs3Y)+32G}FXaAv8?z{zRI>Z#_kTr`~9Sc&bOTzmL>0IS}YkQhZK z`*z#j+=YkYT(3&C{UKG%O5U$AIgeucE$Aa(;#klfECZ)U;e-9z@%BLXX77iMAz`08P;j3sVFH3~m3{eD-p5Y~WXo$dLq=x89q&;LZME_xY5 z9e2_+5KoxjP?Id$H?yEQ`(+h{Ej0lY8^^rJ(9ue8*2dZ*Q#CzeEX`zW7=mH{=tzwb z!c`zKV|j#&qUaU%nq~?OMjFDZ)Y`--r&Wt4JZb;th8<&YxU9RLYE$*{bm7Wad`Xd< zFq)%MX@K8>-=cn6Q^!6Qk=h1Acs%|QkKqXyyJF{{X2Gc@uFrv=b(?Rt_i5@jc>^E% zPnmRa=%dNu@fU|qYwdP%dy1nfwxPzXWBVDW!i1lGb&_wCYqhY-zw$?RZVI z$KKbR`uHQ2;pFR*;bZpWX4OspBObkhPa(vqI$+1>pwn5+%30TDWEVRnsx6_;mpp@} zAq4*GzI+b>uGmqwFPqre5AR}SMze3C_9fPCODuSzz0XvPCtcB%#1uc5DB4+)DBF=J z-j2hWeRBerqwnktGB9-LiCITdPiI}0p-Xt@VO70H(k(8?wbt$^jqIOmm2I;^+pXd! zd~;@(lm-@pjdC2w7Ut{1yVNXgtcqp0o)c;^E}4hNNJlSMv_t^RT5!XHc$#ZTxi;6g zF4=~16s;}G(>~fN+KFYZy+?C-PHXNGb3JF4QL^D@?a5~!RGPpRSZm#OXaGOk_+ytV zd{*csE6fm-EndaE5z1me`BCNTtpzWs7n-rB*r%M;JhSyCZmCsTaGWAuBgQL)kWY`) zj-PriM7ppzJle;A=@ASXvrh}(0Oky`mwRZ?6vhQz)jb6 zOB6MCb(hkhzxDy_^dBF>^GFkejvWY{9w^>RhWMro(Ncq1cZzd;mbne8Es;ZIbjd3( z6a%weZd>}wr@*-Z80ho{T{BhgryM!x5<2>%QCvwP4X6 zxwTcP;&x*S-#|2!zc|cn&$o%fj+HR=Bk)fT@tERX1{Up| zPy`P?7bFvk_PJs+1=#P3O{GB>y^znhMP0JM&F=QqETLi;{5l$Z_*uJJeVYzsovv2r z<58*=Z&s4Yz}^C2GSDC;?0;=;q-CzETi(uRoPhl+59gX4;1#L zh^873+nr@M9;)S#)8pt?f9{vb|-Cc+1#(_<`C-zSbI+IpntOpiMmwLfFJI zd*4FQfw?GgtM~|JNe|-hkQY06tObWvFT@AG+RPqGjt6?ME6$Ka(E*5=*-`6fHgXJ? zDuavoZ+nf4(2}F7yLC0dc@)TQa!G;9S$qIS_LD!FeC3Gx-Be%Fg^(i2*9FgTdWL&d}y{*(RNF?*WEbq0s z%E-}cpz)=iV&f?GZ{x!X-vJ}lPCL2``Louw!28g4ZpKqmp6E2~&VA%|othm?xzkpb;0RwT+OnR*aPgq4etm18U1)Pn}=VnxgbOYy3oku~H`kSbGY;}*) zg`zORyMh$N728L_WN?i~XL!4glReavIzfRGr_2arsQ1BzmV*>&qyAIHV#Y6ICTPI) zYG3~xnK%O&*i0L2CN*EFvh79Hcznbq2yEBeQjJ1T$Cdc3DKnI%;#GOJu_zJo1bBG? zosAx_7Uv}jF6zC7GQh^;U>SRsta*W9sLhJOo;-*_uW2C};lYMVHKlk-1cBmRe1R9B zN9B=7y#UncUUPvA^lDg$5d*Zw_L(|KeTanAu~XhTg*?*QJHPL;|Ek4) ze&P*?2_`m0xixw^>4U)jf)UW2n*LyLnAPRhYr zcfG$MwA}CJ;o$CCm7XNm$0#lGK|@R$;4D!dWMZD7ry{)G=dK6_{mbiu!`+2d4Nw(T zKztu6c{XSZaP1hbwsP~rAvceStyu1^t7vGbS^>%W7<2sYrL{qKg?r4Xk?t$qyxItt zFJ08fpaQ5qTes`){>5I1-5L6_vuAa{Y(=hvYUP=66Kx?P+sI*gCy~6Lt#YJvh(gS5RxN&XMiYgSL8{yuWrL&}@jYU;8 zl|xv3`GW(Yq3&9qlU{Qru5YTS)-e@d()a01pAJG^gqI`IF!xHoyS}2v4c(xG+)Sh4 zB7>sCHW<%2Kph9UuNhpqW^ltAh_J>kD+Fr$Eb^9S+vl*smH2PS3Gk<&XYR7>88rXD z{)jw9>^!apA0DsRMHg`Qt?u)W41e3(~2Zs=C6{8HSwg z?5=>{stXPo%4%tQ8t2+N(Aage3Wll(A(J=(MJoQMu$FNtwa+(W#H<-UcL1IZS5)2V zr-3sdqiXlT?a(!~Zrt-&Tff>+w|GhXT2%3}(iv_X-;VnRS>eSWvf^E2lq%N?&hSdW zz(E89xOf27%)g?lHq?N*+lrb7Tu2CqQB_^JxT3xi&ca(!-yj(aR@B39_YnnV68L6K zhVeq_7Y}Z@7`e%aON_hW)+*eT28X}ejf1+u9G|x?HHYbE_QU6+4%DDUawKC^D1{A-A~bHSRrbV&4o99#oQu%b&YVTxPl;<-%fzl7 zU)?eSM=D?W-~W-uq05Awcn;2?$;1=xjQ>x#{|@i){D1j-D!Klr{28}^H*Hd_xQVZm zi;()jo=;uSX_(!$i*bfKJ>JRyOMPX9y6~f`Z}lN9oYKfip6jo ziU;F5X4uE}TiM*ZipDy`P7W%WG6iKcX=Co<#|m(fs-z-bMqF0cLY!1?xo0U4Q{BrTK)ANYZ(07hv(np?>_q%eZO+t z(#ZP{-RL?th-Y;BiV9}lSU9=#CVC3dzZbQzLhPPeP?=?M*&0WJA~b8N&}XzkNa9MH zOgBezd&{U%dD4+ow~nGCichGom_3*feAdS{ix`=JGxTsCW(crgNO zjHC};?_UN-hnVoBDn-z+kL)c)y+un_P*7m|GZ29(uY3e|*nr(tb}vS=Qsb|74*}^= z!*>(f!y-60x(c)fu%pWdH(cH)qvN-vYAO3QY)nYf$C&9GF$Nc?`lin+a^E<)xM*te zEcZy|Ih;gP4@;?9G@g^#WA zuY_gRhL%FN|0mj7^wAw!A6r$LD4R%@FYULXR@JE>61Z}Pzh-GL0BzClBM-Vug;v6evMOZom@UGYw%+E7lLsa)2plDWU8fG?5+v1X=Q^d;}cN_h8b1$m0B&d)Qmvs z0IP1W-5EyuS|(i9>aRyf1U?Yz!i67KI+e*_PEj7{MJOTZS zVTO@61~(A(fbHXN;Y7s4FaN^$HuRV=#X=oy#l|oknXq7;}1$2`@uLGJZ&>AceDhW0KQfWM;anAx$ zQvF@yKGk%Su+pdEP64z@_$|>m?iNtE^%~aUXJ-#757R&D#p(=?pJ_R8m|IcD*XNl2&)k&^FDA9 z3YrW^aj5{bOhTIg-6zl%4RIeiTq|rN1xUr^rj~M{>jy~Tt^iaaxY2+X3RI{=D|KkK z4t*NXBBA3<_N@Z_8<3Lf>@yAHcEN1|^ngGYxNvBzKvw{oEYJi%O9Uzaq~gv5q;yaX zNY$u61Bwe>kA8-+T%e(VROk#qm4dqkkm4K!q&Tkyq&S;^nuYEKK#K+XFF<~QF2`EK z6oJM9Qd}wkDK52u6qnlpDJ~lUsk}X{L-*>?KLRQcexCzUTu#FqfQrjE04dv;0LUYB z(*UV-=KxYM76MW+)@i!?02NA%9|BS_eg#Oy;E9?l#z%mPgzgG#UsAZyfa(P|N#iDK zs1%U0$>o4H2;B-Dx*m|q`Tc-Y&VK<&rSYz&`@4oZ04aX`1~~k_2}sE@7?9$3H6X<= zp+h(8&>b4L3y{)U?%58P%QZ9%(0Y-1ipEU?q$H>Sr0k_eLr-hyZ9u9nd<Gyzag<0U|eKw*h7AJ7;;J&k5SN`k)uQh93!v|i}GahVf35s(U<3`j{e@Nx%t z2_S`=4oK;xK||XBt&`NA2Bf&`2Bczq3P?$C{tyScT0=e!9RZ|rK58gN$imMLNb$Q3 z&_g2gU4XU-RPb#lbS5AbxUT9Rd`SxMyEs7>xqu18NfJ*MQ~=^gf`Q1?oP` zFct_@0cf&70YJ7?fK*98qH#afxZzhi`HccnR`NI?WxG2xbR3Z4_Yoka-JIc0+_M3x z&~E`!I$f-B0S(;-$Q1dafHnw}(4h|iQj-1-kn#+pM>xKz8IVfjyMR<0zt*@_c~0m$ zKq`%9K;MxxIsmEioj%f`n+d2{LhseM?`zyMfL2TBd83>dZa^wVk;WBk+!~FGY25cU z?lFz~#%PD%xqwtY25a0EfK(bcYuqADcRwIi=D*aTPwUVRH11PCic4=)F{L#ZAeGv8 z0No*ZYt^9#bZFnPPK*lxDK4`CEt43l0Id~h$W;z>KcF>&`!%5J0rfQg4(JAfz5sNi zg!aGM@zi$%Qc`UPR3)Jw0tyK9z&MBBi{l+TI05KE2@N3PW`RP0zAMmrK;IMSZb07` z=%0Y_AF_OkpG|`M)kFvPEFgv34`{Z89@e3615!CZe-c)>By_rlz7HrUxX%EoG)nRv zXbvF7d8@`f4oLAE05jYuamN8tajyYXBe*#lw*Zie@mGy&*SHOErqdClr|}6OpaJE0 z9Dcn3DG9t9SEix)fK>h1qH&LE+=m+XIUps$D7X|Q>9v3qZk5K}p>gL2*j`=>NagJwKq}n_0VO2#SwJek4;MP2KLVsU@6@svO@LI~p91>6aJdX> zRJxi0NW~}xr1G&s*9qiJ$7CPXt_W~0jU^&1Ei$-LgP+{Y8CEcK#Fb%AeGwf8W+)Y z_iNlE8v410_5xCxJO*fuNRW#{Q=HEPq;ft+<0b%7sg-M-U*jGFq{`zBKuT9tH#+=o z1Eh5D6F|30YH#b%V}MlNTr-^*XKUzuK(;IzH(o>6>d?i2RJx`Py&I5{xgAigqnLvH}2;x5s+YK?nR<8}giNa9{n;%M?GfRrYG2}nu*PmTK&kV-dD z>d@6_CPt8o)G?v);faf{-jAs#D>MiI4k zoQ5W8$fKb`4e>}T72~*uPH5;84RvVfD-9XBPTU?E%GFR`4fWH|01XY)&>#)DHFT+l zhG=M*hB)V@WFDiTaT=PWA&-U%H8f2_Gc@GY&@2tj)zHlvTBxB)4Fxn*t)V&%1vRuv zLu)h?)=*SKO&YpOL(LkxUqhQU^pJ+OYUnWyZPU;b8rrU*9U9uHq31NzqM;Ww^pb`S zXy~AZ4r%DHhK^|HsD_Sds7*u1HFQElpJ=EH00LMr5YNdp*#)oNLH2KNyaH7NDiNp! zkfM75kfM7DkfJ*Ps6^<7a}gF0KVP53tbPu*i40$?5E^@#F!=bS!ksjjdT2j&iu#nWk9+4A(`|7`CP1EX>`& zaJ@85!#4dy7Un06by_CPPSRvyUINB|ze)3X6NtJj%=^Ib;$$%AD>(Rk!7uodbgW$D zX<-&-5HLqi&!pi=Q|`W*7!NSKM=s5I1~7F4GHEJ+S$Iw+rUsZ3Ss0wSZY(@ElV&3@ z2eU8_1LHX_lLi;S8+qqvVtxxua~9?;V2nYTG$()wXJJl5>4j1FX>FVf43B9~V+I4W zH48Hyn1h#O(p(1&*T~bc763zQPwQs|FsvtO%tm0ge_O=@h|d<%T#<=+5*V(!r#U|d z%+XPqnAd^fs(hN}C@@}(@D-+<+qf#xCq>mu(|iOPuHUCI{|07j7Uml;nuU`xX)Xn( zQ0HQJ(7!4ul9L8Dma`0fK;EL2{tV|<6Z???r>dvJ7d13EF=(f!q^VCc5*_CBILk18 z;6&5ETvMk1C5;lKh%9ZH6=p6m_D5kVyI|_OVAf}0%DMU7GZH1CKHZG+KoGmBJ#@SB zQJgnr$Epd|2D;EZoJGUPKktH(L$?)UJ738zG=J=ZIo1W!(FN0ks>hlOa&qxx77YVm=z@8x3+DYU zn3Gu;rd6}JZna9xNvj{cbsD4O3E2WUG~9iVrXlq0E|>{fm~w2&)=3!-pX;({$Y*vJ zOl23$ZCMy{#!EbmjE}?ljw~96SY0q%vM}Wh4T~#k*b?$XKIPnFrygw#AAXe;i83&XVP7YFLI zCA_Z-jV94{_`@t3#^2rr^IR9q!7L0@ujaOA`8fJ{JBx-v?{~p`(FM~N{Very_*~Ql zGpP&arY@MLe=H)Tp{G5ssUS7 zrv~eM@)kxHj@W;$IgZM+IjR)7J3d1dHH*rL$>h-ad3j}Ud1m-+p;`l3azi7otVLPZ+e)act;2g(ejpi31!K21 zlu=z>yYdvVb;h7JrPzv4FaX0Df7O_-!gs}>CP~&8vt~IkUWvqek%nXId=HErzVbP~ z$;cu%!pjS6o;;(x8oSl~HJ))!RMg^18l);jiKY0%eic*oxZHj!uUcMLUS3|eSXr6< zSiXEw`QlK0`SOZYHsj@ZnF>WIkyqYYS+UwEhtb=y1RDs_Bd|J1!P>OMW`MS0!Ml1@ zIo|(VgnZs=hdTynFlht_S75KdQ?v~=m5V}r+1DnnTUy^Ri_NyT9Lv;}E zZ#k58s9sLp6-nzH&&3q+iiAz5UKlOMyEj@Srczc1^LElfRPA$=lyFse2hV0vwPaM4 zlijKsf3O_AZs%FCchbVUzV-F2Fp5hSY6!5F*@-kX z!0QD}1Is=-MHZ|W#pV67Dw#JZt;Q%2=FMQCQl$O?A!RFS2oYT;Xtw>VcgW|0+( zD$gj~^5rW!={ZxumHtH`xEDu5wl|ZK^H(&iX2Wdra!4{8OQ9plZJ~;4ZA;(DuztSO zgYr!?4AuD2Pi6~4+^X`BP3^F%v3V8Hc&cia)TXTirIl^OQrXk3j6`9T@%e-5?N+=e z!WTrG{HdTq`-S^c2C8^IX_YehIgv6#>MK^3qt@Uh)q11Ph*5c0yE8r`M~@#hX5`qh zR|7jTFE20S`>HWx-0soiuFAVg=oj{xk)wuHHDSD!Abp@7IhivIL^UrlWT$(NL4|bv_856BnKKQfz(hfpPuTFZ49}QtAVCX88HGzRUc6J0V>-d`7z;i6 zv~1jWur?RB3Pm60lpqcY8<;*QEUA!;iQ^%r7DT=t8v0ZC`Qsdk2ZvaVZ2%A>^OiNi z*%4YBg)*se&Y8_PJs9b$+o#PxfkXd{G-a5N3ceIYjQ@zt<68-}Ms^U_8hKj%@Q8l|H}OfJ z0DdrS1>7t^93bM3v~jSmWTcW5f*@bqF&+fdWWf+V>|#J`g!7GHK1ThFb{H?cJ z|=sqT*R{Qq)a?m9v%rk zptN-M9Jw5pCsc+`i(He#`{tcnMsQaAO4NxoLMbAhA9dR~Tqlkxy3p#5L*?zl^5la1 zh0twDNvTgI&>HEuI(R;g>Cd%~=`SWIX!kN+)dY5Pwz^1n}MaB{9Wxo>-pCp^vwp0&0E z#}Va%WroUh`NDo+5qaS~thz=p&zf6kHEjnN-YLffhOV%h=^9ZkI1SBLg0s^-c+>|V z+Lo^PS++vUX>_+GQ@kY%42ipPw*nv+FGz=g)LF!KA8 zxDM8nYYoKhF1_3Ht@*r%{VHp8_zsT|`p}9O!ZxfjoC1a$yEc08P-0*73muK`xa0ST zfXkOdM-crLdJd%PXcxMD6!#VB<_Tsi_BVhE@pu%SoU(-*-FI9@k?i&cSKdfv`^+;b zCv-aQ*{1E%?2&?1V8?_2II;6xD>4R(<(Xcm;W)?8YmwdK@SNka4-1#58Rhp^v9Ccm z*tqN%YV~R12}X$K$(mfgVE>#sr2+IQ^kulv(HRKc$r>UAlpu6WmCn4IaEr{c3BGiO zUD3lJ#35t6T)7RAe}s$PMxM;sZ!S1W`K<-Va=x5B+qI{=HxS59rq^`^MyqO_l(%71FX#86xCnv#-Jad;O8#hV|8Cyg8KTos>_9Qx(>1;-oC zO5Bk{=Hb*?U~Glj+7DSpM=9osQXh);SWZVqsRjRlIIievBrQ4-UUhPK==s#=s0hfr z&MR`5`X(!~D!5MdcNp$*2<7trtl~pFp7K%@iO;uef=Y##-S1EIIF}~0-xcYFK${J! zAZ*@g}fj}NZxShYBp`3BFJizfEyW%h4*ZgSc7g1bL2QBh( z{M)WLkBc);3_UyzLgTgz=p7M*uy|M0jkFnaAb!omLr;L@1P;ApTlgSiNg=wTcL8L< z7qw*^#7~-!$Qjp#711~0+R)=b#{L0hvgjz2fml=*Q}2bpz2kNK&6IdkBF^GLYb+BW5>i?a|oD;M9^tK@RR$#&Kn9k2?A~3Q2^S zI=!|FGdehj8C>U4Fk5g8JwqsDFhng$Oa;pmVCmFmtLzv|L9^`JRldF%EO}#r*)3hE zMt%m)auy-)qWcWp%o9U9P<8BUO;Z8bma=aglg1m-%{QGgloLf^04RP{eyNEdwjEe1 z3K(*+B(^B~#8XBQnMKE_{w9(YA4R(@w`JqDK;89$;-Tr`Qn*KKWlL`u*>N0~oxIpa z>K%wD1Wu?#J)V{5QLubGg{=xF;m>S;(`vq979X*#&(1i=uC)yXvh~CbaJU9&OD^&NU;1G9x5YQ|Ry{1)t9wxFaJFI{1MKDG7vu_bL$-Dx>M8InueO zmg9>l+?T-wm)BJ@1vC_${OP4|GW4du4Qv6uI$F?t2f9EWh9*{Z_b%(@3p|p+idAAQ zREi3h40Gs0Lzq2#nJ3-%DT0~0qP!@@+y~Pe`h_o1e8PtVEpefqa+0>0wq1sDLRxa- zcjViu44#4R_FvwC+ia7d~Dyrjf}k^8grjEB}?L(5*I>S z&B7ix3=Co}!g>EOF^2GCg^vG+#JD}=)QK@J6SLW?3kZQ|4Npd@Iq-}gSM>WJLUTqt z4-Nny+e_FwLWafdX9uwl%?>7dMM1M8eC_<;O57r0H-EDJNq-5mXduN@J*5oI$% zmkm_zY!wX=BjjtuFp5$GS_WIkhyLf3En$DZN=AZ=!MF3 zlli4J7C}b)n9RHJ28V%y=?>h`ebR*BS(63_&zv;e6{G5vKQ7qN9kDMM`5JohP5Xkp z|B_L3Kev4!7>AAk{UXp#pS7QPICTaL*}JT#FYprtI6eUR@FO3|^|VN4JypdKX7Ldo zq&nL^!?%n^WQuvEK4Nf@3f6rG31-oyEyA@hYXtojL0hNd zD_QhkKp0I$N0UW=CA1`2l;U~X?I4~-;@}vhuyZn?@GiN^BjgJ2;w^_c-)xB-JuT8w zh_S}eKu<7RmcED@ags#`T|nZ_bW8z+ck#x1TxSL>>pbqw#`hAl=#}vEQ$(g%1&H@Z z*n`qMd<;A1We~V<_Mmr(RrHD+xa#$l&bAt>5ycwl3!IHOb_wU1`_(KK`Y>CWLfL`v zu49~Oz}3a;j{<=4W%{b311Rfcm?8XtzIDCOTSW)V%|y3``J98gWDgFOG_x$d?1N zYd?lD(SlS-w>DBeIUZBW9ev^j8D&rZ&AYFr!j92CyP_Z9VdQ72HvHIDre#;pmgsiR zHz3jO{`g8gh?JmcNTB)JHiOGu=dv+6W&a-cf?DB)$O?KjuR^hxg9lF0RlS0_mc^iw zbRVH7C2c{*t@|nwf^pZC=rS{oq2ojtVn1QV1uBZQn7HhRZ8nDHjE1%j9qR087DMc$ zFgIXF3(mIg*&~u3}I4`zzbW-s!u45VJb#WYomvnABp);btRrO z%NPpM*~m~3(?nkD*Ni_qFbm35W22ER8KtYnvuHW3+>*ALIJ+Fz4O-nJxIMWA(X`*` zjws2z@`!|y)p=P+-mgD4Ol-fIv3Y(!1nV$}C1rM2{X79@` zb@0MNBGLmKlr=(CXbO66h_EP{;`~N)RAa^MMlo})OK{-I%4Nmf5FIIJ5fJAPM99IE zI97xKrbI~!oP(QOlRR_?x)1=z50kJsEsk!I-7e0xp4tw? zJainyv09NGcm!d3{yZ5W1wWnnl9V5iXU(sv`r1R?t@9mv7F-dbY!P>9=BOHO$ua_> z@`6>&My9XAv?5-rl<3SGGOY+F08T-DvxCm2K81DvFH3{#7oGDgYZ@xgR^-xC5D>=? zl`WRonZy6CJl8i3pMromO|bbC1jK2AhfYC2oF?$1r`cJP#A$+4xQIxWi`WX%&K!v2M~a&jqBB88 zB~g`+w?3&(>0C>y7Al|Zs3WYs;PgMKd?d9K?&R+?*La-u3a)RohP~8>HBjALVT5GWse8k=Idsw`TJ!#j)I|D%ngO@tPlpHQ#KT_jhz1BirzR z;yjHrA0<$-bsswc7|*VImt9j+`V8o}pYWEdMnd~AH)qnw$OUkMg8;l&Sf@!)q`kDJ+8+%hI}+TB9W+g(`C00?wRNy%)ru7_TD@74&DME4t$Q%)*Nnp=D3wEvY~F_k~G*Idu! zbtJNw7W|gXxQEWw_j?6BE6&7SUcoBdMHCz}vgKu5|I#D$Ib9j9Z?V?1UZ|0!wO)n- zYGO;dHCzbtGUhxw6ROd<8g_e6LB9+)#b?Qf#YNn#h8`A{*e=9s8*)2ve814AU~FFp zCJRh3pO>M^Q?0k!vrV`q9P29K-P@>8S1f_=ktf5uf5OMmA{4W@W!(54LCpX=B!tQM z6G(Wh^!Gs_-Kdh;Z55j0bWDQwYK;gJ$D`Q8zE-s>p*`uW&&b|{woONI1K0^?mB~z^ zIMgsvrQa6H4lg>9n$4Oj`%~zRFi+IBa_Z5Qi4U;(;q0m)<{1Jp7OFdF->j7*(p~SSo1p9G~JbiOsf`-lr z@7aeS^a7q|@x`g6)(9DP9??h;LY4kH0CZAtoH%~SzQC~qRVc{{F6_taW|tf`*X0*_&N$C(EsCr<9|xbUKVuIO_NiPH)u zdV9DcKgT03%Q}ICRR&S?zj#NShAip0qGV;}Vaf@8ycbXeP|CywCqgTeacAStl4doF zqm~k9x7GXwOLra0W~8Ig?6vdnYg?~4#7>5Oi%cisGq}yBwdjMK)XOjw+e~zhc(H2g zpP0v5^8B}r)L-#r{(#BaO-SflcCWy?p|B)yDYT$6fwWTr*d`1G(zhRV>iaP4g)ob- z8hxKL^4#LhF8$_fLXz6`r+?!*QtR$RMQdHQyr(if#3Y^H>vd8);DJ)$}*!QOVM1nU)h zjtg)joNnb&razN)R9EyK24Sa2*%6bDLs~V}WpvsHCfio_Cd#wbZY-wZfqwWXGyNva z@N<|^6cdIsS&&7Ctozt?v$o6k6i?)6j&-9a@?SaQ_o8i^Iw>|~680B>6n)imP#)I$ zLXa<+7#Rk0amD(ON%5f#>)1b-5aG8g_9^U13M1NxM43jj%sybb=^i4&V-R=A_y~Vo zu_UNjs*!PcoVbqPxaONk8TD{(^D#W(!t&@t_{H|5jDk_C&+Ri+e}J?@ArU(2->`F8 zg(#`Bfn!;_V)Mv1L!el#$^dRwq)cECW3Lw(-`2ww<8H)lIj-0x_(fe}R>C{&N&Y8* z`)sNQp+5xV^u5jr?^M09Gwg1eD>hAnrT^rLzKJ>~Cd?(8iL&d_6?+lL4eJMFG_Hq{L-GI<}_z^-M*YNCt^T(LWV46l99a7BN^B-efbXc-A32f>uO zxtUMJx#of+tvNZVACrC}Qu`g82iBOXL6uRT`n49lYovaKu+ma%?V&kZ>oA`CTRuPR zE_ydL8lmYHOeOw0RLrgtr&-{c4_RS>GQ~e6396YvT!47UdXf`h+Q(2-rVB;UgIT|# zZKm#&!gqACovO)-v6)5NStk)9pJe=!61z9tuhi7h;oLYVpA#*2v__+Qyfia7At>TzmorY!


%@UJwbHXU0}B&UGFo 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�+f0HpIM2hw@W0MhYdZoExyyf3-& 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 PbMXz${L3mnYTLdCYkPqV diff --git a/code/libs/win64/libcurl.a b/code/libs/win64/libcurl.a index e50e75e2c0cf9d6d5b537fa92a5370fe60302780..d343d480d87273902a1b0d26b5b3b684ec25cbb9 100644 GIT binary patch literal 504330 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�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 z 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%0A|pd!vs@y`S5@j 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 C|cPef(JTp)C{AduR@M409oWr;0e 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 Q?ugye Date: Fri, 28 Jul 2017 14:32:28 -0500 Subject: [PATCH 137/240] Workaround Travis-CI MinGW builds failing with new cURL --- .travis.yml | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/.travis.yml b/.travis.yml index 09eb7699a4..77c6901992 100644 --- a/.travis.yml +++ b/.travis.yml @@ -17,8 +17,9 @@ env: - CC=gcc USE_CODEC_VORBIS=1 USE_FREETYPE=1 - CC=clang USE_CODEC_VORBIS=1 USE_FREETYPE=1 # cross-compile using mingw - - CC= PLATFORM="mingw32" ARCH="x86" - - CC= PLATFORM="mingw32" ARCH="x86_64" + # dlopen curl to workaround link error because mingw-w64 in trusty is missing strtok_r required by libcurl.a + - CC= PLATFORM="mingw32" ARCH="x86" USE_CURL_DLOPEN=1 + - CC= PLATFORM="mingw32" ARCH="x86_64" USE_CURL_DLOPEN=1 script: ./travis-ci-build.sh From 39716745f9ffc7b04356be23b53669890d67ec5b Mon Sep 17 00:00:00 2001 From: Zack Middleton Date: Fri, 28 Jul 2017 17:24:34 -0500 Subject: [PATCH 138/240] Silence g_util.c warning about set but not read variable Probably caused by my commit on June 2 2017. commit 4006358492b800db0602ed0eca600166caec22e4 Fix spawn/freed entity logic (specifically harvester skulls) --- code/game/g_utils.c | 1 - 1 file changed, 1 deletion(-) diff --git a/code/game/g_utils.c b/code/game/g_utils.c index bcc1200d65..d0e407ff09 100644 --- a/code/game/g_utils.c +++ b/code/game/g_utils.c @@ -391,7 +391,6 @@ gentity_t *G_Spawn( void ) { gentity_t *e; e = NULL; // shut up warning - i = 0; // shut up warning for ( force = 0 ; force < 2 ; force++ ) { // if we go through all entities and can't find one to free, // override the normal minimum times before use From e62941396d852a7e96484f2ce10aed5b2e8a77f4 Mon Sep 17 00:00:00 2001 From: Zack Middleton Date: Sun, 30 Jul 2017 15:39:32 -0500 Subject: [PATCH 139/240] Remove unused imgFlag_t value IMGFLAG_SRGB --- code/renderercommon/tr_common.h | 3 +-- code/renderergl2/tr_shader.c | 4 ++-- 2 files changed, 3 insertions(+), 4 deletions(-) diff --git a/code/renderercommon/tr_common.h b/code/renderercommon/tr_common.h index 3d463aacad..9f15a2d00c 100644 --- a/code/renderercommon/tr_common.h +++ b/code/renderercommon/tr_common.h @@ -43,8 +43,7 @@ typedef enum IMGFLAG_NO_COMPRESSION = 0x0010, IMGFLAG_NOLIGHTSCALE = 0x0020, IMGFLAG_CLAMPTOEDGE = 0x0040, - IMGFLAG_SRGB = 0x0080, - IMGFLAG_GENNORMALMAP = 0x0100, + IMGFLAG_GENNORMALMAP = 0x0080, } imgFlags_t; typedef struct image_s { diff --git a/code/renderergl2/tr_shader.c b/code/renderergl2/tr_shader.c index a774f7ec11..d9c8627938 100644 --- a/code/renderergl2/tr_shader.c +++ b/code/renderergl2/tr_shader.c @@ -2255,7 +2255,7 @@ static void CollapseStagesToLightall(shaderStage_t *diffuse, { char normalName[MAX_QPATH]; image_t *normalImg; - imgFlags_t normalFlags = (diffuseImg->flags & ~(IMGFLAG_GENNORMALMAP | IMGFLAG_SRGB)) | IMGFLAG_NOLIGHTSCALE; + imgFlags_t normalFlags = (diffuseImg->flags & ~IMGFLAG_GENNORMALMAP) | IMGFLAG_NOLIGHTSCALE; // try a normalheight image first COM_StripExtension(diffuseImg->imgName, normalName, MAX_QPATH); @@ -2301,7 +2301,7 @@ static void CollapseStagesToLightall(shaderStage_t *diffuse, { char specularName[MAX_QPATH]; image_t *specularImg; - imgFlags_t specularFlags = (diffuseImg->flags & ~(IMGFLAG_GENNORMALMAP | IMGFLAG_SRGB)) | IMGFLAG_NOLIGHTSCALE; + imgFlags_t specularFlags = (diffuseImg->flags & ~IMGFLAG_GENNORMALMAP) | IMGFLAG_NOLIGHTSCALE; COM_StripExtension(diffuseImg->imgName, specularName, MAX_QPATH); Q_strcat(specularName, MAX_QPATH, "_s"); From c05fbe554e89a12ee51f8716c81d9e718dede441 Mon Sep 17 00:00:00 2001 From: Zack Middleton Date: Mon, 31 Jul 2017 15:13:19 -0500 Subject: [PATCH 140/240] Make warmup in Team Deathmatch wait for players to join both teams Reported by Cyrax. --- code/game/g_main.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/code/game/g_main.c b/code/game/g_main.c index fb32be9218..0267de46d6 100644 --- a/code/game/g_main.c +++ b/code/game/g_main.c @@ -1500,7 +1500,7 @@ void CheckTournament( void ) { int counts[TEAM_NUM_TEAMS]; qboolean notEnough = qfalse; - if ( g_gametype.integer > GT_TEAM ) { + if ( g_gametype.integer >= GT_TEAM ) { counts[TEAM_BLUE] = TeamCount( -1, TEAM_BLUE ); counts[TEAM_RED] = TeamCount( -1, TEAM_RED ); From 2d6171f44c72177a4b996eb721f353839fb5273d Mon Sep 17 00:00:00 2001 From: Zack Middleton Date: Tue, 1 Aug 2017 01:51:38 -0500 Subject: [PATCH 141/240] Remove CVAR_PROTECTED from cl_renderer Setting cl_renderer isn't a security concern and valid behavior for a menu to provide. Sys_LoadDLL ensures only libries are loaded and engine prevents QVMs from writing them. --- code/client/cl_main.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/code/client/cl_main.c b/code/client/cl_main.c index 506940b14e..62dcf52832 100644 --- a/code/client/cl_main.c +++ b/code/client/cl_main.c @@ -3207,7 +3207,7 @@ void CL_InitRef( void ) { Com_Printf( "----- Initializing Renderer ----\n" ); #ifdef USE_RENDERER_DLOPEN - cl_renderer = Cvar_Get("cl_renderer", "opengl2", CVAR_ARCHIVE | CVAR_LATCH | CVAR_PROTECTED); + cl_renderer = Cvar_Get("cl_renderer", "opengl2", CVAR_ARCHIVE | CVAR_LATCH); Com_sprintf(dllName, sizeof(dllName), "renderer_%s_" ARCH_STRING DLL_EXT, cl_renderer->string); From d2b1d124d4055c2fcbe5126863487c52fd58cca1 Mon Sep 17 00:00:00 2001 From: Zack Middleton Date: Wed, 2 Aug 2017 14:55:10 -0500 Subject: [PATCH 142/240] Fix/improve buffer overflow in MSG_ReadBits/MSG_WriteBits Prevent reading past end of message in MSG_ReadBits. If read past end of msg->data buffer (16348 bytes) the engine could SEGFAULT. Make MSG_WriteBits use an exact buffer overflow check instead of possibly failing with a few bytes left. --- code/qcommon/huffman.c | 27 ++++++++++++++++++--------- code/qcommon/msg.c | 40 +++++++++++++++++++++++++++++++++++----- code/qcommon/qcommon.h | 6 +++--- 3 files changed, 56 insertions(+), 17 deletions(-) diff --git a/code/qcommon/huffman.c b/code/qcommon/huffman.c index c1b9f242ce..1f4249842c 100644 --- a/code/qcommon/huffman.c +++ b/code/qcommon/huffman.c @@ -279,9 +279,14 @@ int Huff_Receive (node_t *node, int *ch, byte *fin) { } /* Get a symbol */ -void Huff_offsetReceive (node_t *node, int *ch, byte *fin, int *offset) { +void Huff_offsetReceive (node_t *node, int *ch, byte *fin, int *offset, int maxoffset) { bloc = *offset; while (node && node->symbol == INTERNAL_NODE) { + if (bloc >= maxoffset) { + *ch = 0; + *offset = maxoffset + 1; + return; + } if (get_bit(fin)) { node = node->right; } else { @@ -298,11 +303,15 @@ void Huff_offsetReceive (node_t *node, int *ch, byte *fin, int *offset) { } /* Send the prefix code for this node */ -static void send(node_t *node, node_t *child, byte *fout) { +static void send(node_t *node, node_t *child, byte *fout, int maxoffset) { if (node->parent) { - send(node->parent, node, fout); + send(node->parent, node, fout, maxoffset); } if (child) { + if (bloc >= maxoffset) { + bloc = maxoffset + 1; + return; + } if (node->right == child) { add_bit(1, fout); } else { @@ -312,22 +321,22 @@ static void send(node_t *node, node_t *child, byte *fout) { } /* Send a symbol */ -void Huff_transmit (huff_t *huff, int ch, byte *fout) { +void Huff_transmit (huff_t *huff, int ch, byte *fout, int maxoffset) { int i; if (huff->loc[ch] == NULL) { /* node_t hasn't been transmitted, send a NYT, then the symbol */ - Huff_transmit(huff, NYT, fout); + Huff_transmit(huff, NYT, fout, maxoffset); for (i = 7; i >= 0; i--) { add_bit((char)((ch >> i) & 0x1), fout); } } else { - send(huff->loc[ch], NULL, fout); + send(huff->loc[ch], NULL, fout, maxoffset); } } -void Huff_offsetTransmit (huff_t *huff, int ch, byte *fout, int *offset) { +void Huff_offsetTransmit (huff_t *huff, int ch, byte *fout, int *offset, int maxoffset) { bloc = *offset; - send(huff->loc[ch], NULL, fout); + send(huff->loc[ch], NULL, fout, maxoffset); *offset = bloc; } @@ -413,7 +422,7 @@ void Huff_Compress(msg_t *mbuf, int offset) { for (i=0; imaxsize - msg->cursize < 4 ) { - msg->overflowed = qtrue; + if ( msg->overflowed ) { return; } @@ -122,6 +120,11 @@ void MSG_WriteBits( msg_t *msg, int value, int bits ) { } if ( msg->oob ) { + if ( msg->cursize + ( bits >> 3 ) > msg->maxsize ) { + msg->overflowed = qtrue; + return; + } + if ( bits == 8 ) { msg->data[msg->cursize] = value; msg->cursize += 1; @@ -144,6 +147,10 @@ void MSG_WriteBits( msg_t *msg, int value, int bits ) { if ( bits&7 ) { int nbits; nbits = bits&7; + if ( msg->bit + nbits > msg->maxsize << 3 ) { + msg->overflowed = qtrue; + return; + } for( i = 0; i < nbits; i++ ) { Huff_putBit( (value & 1), msg->data, &msg->bit ); value = (value >> 1); @@ -152,8 +159,13 @@ void MSG_WriteBits( msg_t *msg, int value, int bits ) { } if ( bits ) { for( i = 0; i < bits; i += 8 ) { - Huff_offsetTransmit( &msgHuff.compressor, (value & 0xff), msg->data, &msg->bit ); + Huff_offsetTransmit( &msgHuff.compressor, (value & 0xff), msg->data, &msg->bit, msg->maxsize << 3 ); value = (value >> 8); + + if ( msg->bit > msg->maxsize << 3 ) { + msg->overflowed = qtrue; + return; + } } } msg->cursize = (msg->bit >> 3) + 1; @@ -167,6 +179,10 @@ int MSG_ReadBits( msg_t *msg, int bits ) { int i, nbits; // FILE* fp; + if ( msg->readcount > msg->cursize ) { + return 0; + } + value = 0; if ( bits < 0 ) { @@ -177,6 +193,11 @@ int MSG_ReadBits( msg_t *msg, int bits ) { } if (msg->oob) { + if (msg->readcount + (bits>>3) > msg->cursize) { + msg->readcount = msg->cursize + 1; + return 0; + } + if(bits==8) { value = msg->data[msg->readcount]; @@ -204,6 +225,10 @@ int MSG_ReadBits( msg_t *msg, int bits ) { nbits = 0; if (bits&7) { nbits = bits&7; + if (msg->bit + nbits > msg->cursize << 3) { + msg->readcount = msg->cursize + 1; + return 0; + } for(i=0;idata, &msg->bit)<data, &msg->bit); + Huff_offsetReceive (msgHuff.decompressor.tree, &get, msg->data, &msg->bit, msg->cursize<<3); // fwrite(&get, 1, 1, fp); value |= (get<<(i+nbits)); + + if (msg->bit > msg->cursize<<3) { + msg->readcount = msg->cursize + 1; + return 0; + } } // fclose(fp); } diff --git a/code/qcommon/qcommon.h b/code/qcommon/qcommon.h index 124c393a66..07eebe7128 100644 --- a/code/qcommon/qcommon.h +++ b/code/qcommon/qcommon.h @@ -1196,9 +1196,9 @@ void Huff_Decompress(msg_t *buf, int offset); void Huff_Init(huffman_t *huff); void Huff_addRef(huff_t* huff, byte ch); int Huff_Receive (node_t *node, int *ch, byte *fin); -void Huff_transmit (huff_t *huff, int ch, byte *fout); -void Huff_offsetReceive (node_t *node, int *ch, byte *fin, int *offset); -void Huff_offsetTransmit (huff_t *huff, int ch, byte *fout, int *offset); +void Huff_transmit (huff_t *huff, int ch, byte *fout, int maxoffset); +void Huff_offsetReceive (node_t *node, int *ch, byte *fin, int *offset, int maxoffset); +void Huff_offsetTransmit (huff_t *huff, int ch, byte *fout, int *offset, int maxoffset); void Huff_putBit( int bit, byte *fout, int *offset); int Huff_getBit( byte *fout, int *offset); From 9c4c363cccf0d891c93500d392fa9d80d3070d27 Mon Sep 17 00:00:00 2001 From: Tobias Kuehnhammer Date: Wed, 2 Aug 2017 20:18:43 +0200 Subject: [PATCH 143/240] Fix friction in AAS_ClientMovementPrediction Ground and water friction were reversed. --- code/botlib/be_aas_move.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/code/botlib/be_aas_move.c b/code/botlib/be_aas_move.c index 9ce95c6cd9..8ff56aa99b 100644 --- a/code/botlib/be_aas_move.c +++ b/code/botlib/be_aas_move.c @@ -553,7 +553,7 @@ int AAS_ClientMovementPrediction(struct aas_clientmove_s *move, //if on the ground or swimming if (onground || swimming) { - friction = swimming ? phys_friction : phys_waterfriction; + friction = swimming ? phys_waterfriction : phys_friction; //apply friction VectorScale(frame_test_vel, 1/frametime, frame_test_vel); AAS_ApplyFriction(frame_test_vel, friction, phys_stopspeed, frametime); From 30fdd88c9fecd152080044360fc33bbc65500b5d Mon Sep 17 00:00:00 2001 From: Zack Middleton Date: Wed, 2 Aug 2017 22:39:27 -0500 Subject: [PATCH 144/240] Fix floating point precision loss in renderer [part 1] Patch for https://bugzilla.icculus.org/show_bug.cgi?id=5931 by Eugene C. from 2013 plus recent fix for tcMod rotate. I merged the changes into the OpenGL2 renderer though the fix for tcMod turb doesn't translate. --- code/renderergl1/tr_backend.c | 9 ++++++--- code/renderergl1/tr_local.h | 18 +++++++++--------- code/renderergl1/tr_shade.c | 9 ++++++--- code/renderergl1/tr_shade_calc.c | 20 +++++++++++--------- code/renderergl2/tr_backend.c | 9 ++++++--- code/renderergl2/tr_local.h | 18 +++++++++--------- code/renderergl2/tr_shade.c | 9 ++++++--- code/renderergl2/tr_shade_calc.c | 18 ++++++++++-------- 8 files changed, 63 insertions(+), 47 deletions(-) diff --git a/code/renderergl1/tr_backend.c b/code/renderergl1/tr_backend.c index 41fe47ac1d..22ec04800f 100644 --- a/code/renderergl1/tr_backend.c +++ b/code/renderergl1/tr_backend.c @@ -509,7 +509,7 @@ void RB_RenderDrawSurfList( drawSurf_t *drawSurfs, int numDrawSurfs ) { int i; drawSurf_t *drawSurf; int oldSort; - float originalTime; + double originalTime; // save original time for entity shader offsets originalTime = backEnd.refdef.floatTime; @@ -562,7 +562,10 @@ void RB_RenderDrawSurfList( drawSurf_t *drawSurfs, int numDrawSurfs ) { if ( entityNum != REFENTITYNUM_WORLD ) { backEnd.currentEntity = &backEnd.refdef.entities[entityNum]; - backEnd.refdef.floatTime = originalTime - backEnd.currentEntity->e.shaderTime; + + // FIXME: e.shaderTime must be passed as int to avoid fp-precision loss issues + backEnd.refdef.floatTime = originalTime - (double)backEnd.currentEntity->e.shaderTime; + // we have to reset the shaderTime as well otherwise image animations start // from the wrong frame tess.shaderTime = backEnd.refdef.floatTime - tess.shader->timeOffset; @@ -713,7 +716,7 @@ void RB_SetGL2D (void) { // set time for 2D shaders backEnd.refdef.time = ri.Milliseconds(); - backEnd.refdef.floatTime = backEnd.refdef.time * 0.001f; + backEnd.refdef.floatTime = (double)backEnd.refdef.time * 0.001f; } diff --git a/code/renderergl1/tr_local.h b/code/renderergl1/tr_local.h index 37e0e0701f..54279a6bec 100644 --- a/code/renderergl1/tr_local.h +++ b/code/renderergl1/tr_local.h @@ -189,10 +189,10 @@ typedef enum { typedef struct { genFunc_t func; - float base; - float amplitude; - float phase; - float frequency; + double base; + double amplitude; + double phase; + double frequency; } waveForm_t; #define TR_MAX_TEXMODS 4 @@ -252,7 +252,7 @@ typedef struct { typedef struct { image_t *image[MAX_IMAGE_ANIMATIONS]; int numImageAnimations; - float imageAnimationSpeed; + double imageAnimationSpeed; texCoordGen_t tcGen; vec3_t tcGenVectors[2]; @@ -362,8 +362,8 @@ typedef struct shader_s { void (*optimalStageIteratorFunc)( void ); - float clampTime; // time this shader is clamped to - float timeOffset; // current time offset for this shader + double clampTime; // time this shader is clamped to + double timeOffset; // current time offset for this shader struct shader_s *remappedShader; // current shader this one is remapped too @@ -388,7 +388,7 @@ typedef struct { byte areamask[MAX_MAP_AREA_BYTES]; qboolean areamaskModified; // qtrue if areamask changed since last scene - float floatTime; // tr.refdef.time / 1000.0 + double floatTime; // tr.refdef.time / 1000.0 // text messages for deform text shaders char text[MAX_RENDER_STRINGS][MAX_RENDER_STRING_LENGTH]; @@ -1217,7 +1217,7 @@ typedef struct shaderCommands_s color4ub_t constantColor255[SHADER_MAX_VERTEXES] QALIGN(16); shader_t *shader; - float shaderTime; + double shaderTime; int fogNum; int dlightBits; // or together of all vertexDlightBits diff --git a/code/renderergl1/tr_shade.c b/code/renderergl1/tr_shade.c index d26cb1acae..ab5813f085 100644 --- a/code/renderergl1/tr_shade.c +++ b/code/renderergl1/tr_shade.c @@ -218,7 +218,8 @@ R_BindAnimatedImage ================= */ static void R_BindAnimatedImage( textureBundle_t *bundle ) { - int index; + int64_t index; + double v; if ( bundle->isVideoMap ) { ri.CIN_RunCinematic(bundle->videoMapHandle); @@ -233,8 +234,10 @@ static void R_BindAnimatedImage( textureBundle_t *bundle ) { // it is necessary to do this messy calc to make sure animations line up // exactly with waveforms of the same frequency - index = ri.ftol(tess.shaderTime * bundle->imageAnimationSpeed * FUNCTABLE_SIZE); - index >>= FUNCTABLE_SIZE2; + //index = ri.ftol(tess.shaderTime * bundle->imageAnimationSpeed * FUNCTABLE_SIZE); + //index >>= FUNCTABLE_SIZE2; + v = tess.shaderTime * bundle->imageAnimationSpeed; + index = v; if ( index < 0 ) { index = 0; // may happen with shader time offsets diff --git a/code/renderergl1/tr_shade_calc.c b/code/renderergl1/tr_shade_calc.c index 82f7c0cce9..34975e2533 100644 --- a/code/renderergl1/tr_shade_calc.c +++ b/code/renderergl1/tr_shade_calc.c @@ -27,7 +27,7 @@ Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA #endif -#define WAVEVALUE( table, base, amplitude, phase, freq ) ((base) + table[ ri.ftol( ( ( (phase) + tess.shaderTime * (freq) ) * FUNCTABLE_SIZE ) ) & FUNCTABLE_MASK ] * (amplitude)) +#define WAVEVALUE( table, base, amplitude, phase, freq ) ((base) + table[ (int)( ( ( (phase) + tess.shaderTime * (freq) ) * FUNCTABLE_SIZE ) ) & FUNCTABLE_MASK ] * (amplitude)) static float *TableForFunc( genFunc_t func ) { @@ -920,7 +920,7 @@ void RB_CalcEnvironmentTexCoords( float *st ) void RB_CalcTurbulentTexCoords( const waveForm_t *wf, float *st ) { int i; - float now; + double now; now = ( wf->phase + tess.shaderTime * wf->frequency ); @@ -954,11 +954,13 @@ void RB_CalcScaleTexCoords( const float scale[2], float *st ) void RB_CalcScrollTexCoords( const float scrollSpeed[2], float *st ) { int i; - float timeScale = tess.shaderTime; - float adjustedScrollS, adjustedScrollT; + double timeScale; + double adjustedScrollS, adjustedScrollT; - adjustedScrollS = scrollSpeed[0] * timeScale; - adjustedScrollT = scrollSpeed[1] * timeScale; + timeScale = tess.shaderTime; + + adjustedScrollS = (double)scrollSpeed[0] * timeScale; + adjustedScrollT = (double)scrollSpeed[1] * timeScale; // clamp so coordinates don't continuously get larger, causing problems // with hardware limits @@ -994,9 +996,9 @@ void RB_CalcTransformTexCoords( const texModInfo_t *tmi, float *st ) */ void RB_CalcRotateTexCoords( float degsPerSecond, float *st ) { - float timeScale = tess.shaderTime; - float degs; - int index; + double timeScale = tess.shaderTime; + double degs; + int64_t index; float sinValue, cosValue; texModInfo_t tmi; diff --git a/code/renderergl2/tr_backend.c b/code/renderergl2/tr_backend.c index d6ff122649..4f8f3f4fec 100644 --- a/code/renderergl2/tr_backend.c +++ b/code/renderergl2/tr_backend.c @@ -433,7 +433,7 @@ void RB_RenderDrawSurfList( drawSurf_t *drawSurfs, int numDrawSurfs ) { int i; drawSurf_t *drawSurf; int oldSort; - float originalTime; + double originalTime; FBO_t* fbo = NULL; qboolean inQuery = qfalse; @@ -498,7 +498,10 @@ void RB_RenderDrawSurfList( drawSurf_t *drawSurfs, int numDrawSurfs ) { if ( entityNum != REFENTITYNUM_WORLD ) { backEnd.currentEntity = &backEnd.refdef.entities[entityNum]; - backEnd.refdef.floatTime = originalTime - backEnd.currentEntity->e.shaderTime; + + // FIXME: e.shaderTime must be passed as int to avoid fp-precision loss issues + backEnd.refdef.floatTime = originalTime - (double)backEnd.currentEntity->e.shaderTime; + // we have to reset the shaderTime as well otherwise image animations start // from the wrong frame tess.shaderTime = backEnd.refdef.floatTime - tess.shader->timeOffset; @@ -658,7 +661,7 @@ void RB_SetGL2D (void) { // set time for 2D shaders backEnd.refdef.time = ri.Milliseconds(); - backEnd.refdef.floatTime = backEnd.refdef.time * 0.001f; + backEnd.refdef.floatTime = (double)backEnd.refdef.time * 0.001f; } diff --git a/code/renderergl2/tr_local.h b/code/renderergl2/tr_local.h index dd959d5cb9..9426ede674 100644 --- a/code/renderergl2/tr_local.h +++ b/code/renderergl2/tr_local.h @@ -268,10 +268,10 @@ typedef enum { typedef struct { genFunc_t func; - float base; - float amplitude; - float phase; - float frequency; + double base; + double amplitude; + double phase; + double frequency; } waveForm_t; #define TR_MAX_TEXMODS 4 @@ -331,7 +331,7 @@ typedef struct { typedef struct { image_t *image[MAX_IMAGE_ANIMATIONS]; int numImageAnimations; - float imageAnimationSpeed; + double imageAnimationSpeed; texCoordGen_t tcGen; vec3_t tcGenVectors[2]; @@ -471,8 +471,8 @@ typedef struct shader_s { void (*optimalStageIteratorFunc)( void ); - float clampTime; // time this shader is clamped to - float timeOffset; // current time offset for this shader + double clampTime; // time this shader is clamped to + double timeOffset; // current time offset for this shader struct shader_s *remappedShader; // current shader this one is remapped too @@ -733,7 +733,7 @@ typedef struct { byte areamask[MAX_MAP_AREA_BYTES]; qboolean areamaskModified; // qtrue if areamask changed since last scene - float floatTime; // tr.refdef.time / 1000.0 + double floatTime; // tr.refdef.time / 1000.0 float blurFactor; @@ -1978,7 +1978,7 @@ typedef struct shaderCommands_s //color4ub_t constantColor255[SHADER_MAX_VERTEXES] QALIGN(16); shader_t *shader; - float shaderTime; + double shaderTime; int fogNum; int cubemapIndex; diff --git a/code/renderergl2/tr_shade.c b/code/renderergl2/tr_shade.c index 657f50736f..0fd15486fd 100644 --- a/code/renderergl2/tr_shade.c +++ b/code/renderergl2/tr_shade.c @@ -65,7 +65,8 @@ R_BindAnimatedImageToTMU ================= */ static void R_BindAnimatedImageToTMU( textureBundle_t *bundle, int tmu ) { - int index; + int64_t index; + double v; if ( bundle->isVideoMap ) { ri.CIN_RunCinematic(bundle->videoMapHandle); @@ -81,8 +82,10 @@ static void R_BindAnimatedImageToTMU( textureBundle_t *bundle, int tmu ) { // it is necessary to do this messy calc to make sure animations line up // exactly with waveforms of the same frequency - index = ri.ftol(tess.shaderTime * bundle->imageAnimationSpeed * FUNCTABLE_SIZE); - index >>= FUNCTABLE_SIZE2; + //index = ri.ftol(tess.shaderTime * bundle->imageAnimationSpeed * FUNCTABLE_SIZE); + //index >>= FUNCTABLE_SIZE2; + v = tess.shaderTime * bundle->imageAnimationSpeed; + index = v; if ( index < 0 ) { index = 0; // may happen with shader time offsets diff --git a/code/renderergl2/tr_shade_calc.c b/code/renderergl2/tr_shade_calc.c index cb7bcf74d2..c411e03f9a 100644 --- a/code/renderergl2/tr_shade_calc.c +++ b/code/renderergl2/tr_shade_calc.c @@ -27,7 +27,7 @@ Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA #endif -#define WAVEVALUE( table, base, amplitude, phase, freq ) ((base) + table[ ri.ftol( ( ( (phase) + tess.shaderTime * (freq) ) * FUNCTABLE_SIZE ) ) & FUNCTABLE_MASK ] * (amplitude)) +#define WAVEVALUE( table, base, amplitude, phase, freq ) ((base) + table[ (int)( ( ( (phase) + tess.shaderTime * (freq) ) * FUNCTABLE_SIZE ) ) & FUNCTABLE_MASK ] * (amplitude)) static float *TableForFunc( genFunc_t func ) { @@ -776,16 +776,18 @@ void RB_CalcScaleTexMatrix( const float scale[2], float *matrix ) */ void RB_CalcScrollTexMatrix( const float scrollSpeed[2], float *matrix ) { - float timeScale = tess.shaderTime; - float adjustedScrollS, adjustedScrollT; + double timeScale; + double adjustedScrollS, adjustedScrollT; + + timeScale = tess.shaderTime; adjustedScrollS = scrollSpeed[0] * timeScale; adjustedScrollT = scrollSpeed[1] * timeScale; // clamp so coordinates don't continuously get larger, causing problems // with hardware limits - adjustedScrollS = adjustedScrollS - floor( adjustedScrollS ); - adjustedScrollT = adjustedScrollT - floor( adjustedScrollT ); + adjustedScrollS = (double)adjustedScrollS - floor( adjustedScrollS ); + adjustedScrollT = (double)adjustedScrollT - floor( adjustedScrollT ); matrix[0] = 1.0f; matrix[2] = 0.0f; matrix[4] = adjustedScrollS; matrix[1] = 0.0f; matrix[3] = 1.0f; matrix[5] = adjustedScrollT; @@ -805,9 +807,9 @@ void RB_CalcTransformTexMatrix( const texModInfo_t *tmi, float *matrix ) */ void RB_CalcRotateTexMatrix( float degsPerSecond, float *matrix ) { - float timeScale = tess.shaderTime; - float degs; - int index; + double timeScale = tess.shaderTime; + double degs; + int64_t index; float sinValue, cosValue; degs = -degsPerSecond * timeScale; From 59b1262b82a2d2fa80f7f5ffce098cb4d3459a14 Mon Sep 17 00:00:00 2001 From: Zack Middleton Date: Wed, 2 Aug 2017 23:29:46 -0500 Subject: [PATCH 145/240] Fix floating point precision loss in renderer [part 2] Fix floatTime using float precision instead of double using GCC. Fix R_BindAnimatedImage to be in sync with function table. Fix vertexDeform bulge, vertexDeform normals, noise wave function at high level time. Revert unnecessary float -> double conversions. --- code/renderercommon/tr_common.h | 2 +- code/renderercommon/tr_noise.c | 2 +- code/renderergl1/tr_backend.c | 2 +- code/renderergl1/tr_local.h | 10 +++--- code/renderergl1/tr_scene.c | 2 +- code/renderergl1/tr_shade.c | 7 ++-- code/renderergl1/tr_shade_calc.c | 20 +++++------ code/renderergl2/tr_backend.c | 2 +- code/renderergl2/tr_local.h | 59 +++++++++++++++++--------------- code/renderergl2/tr_scene.c | 2 +- code/renderergl2/tr_shade.c | 7 ++-- code/renderergl2/tr_shade_calc.c | 16 ++++----- 12 files changed, 62 insertions(+), 69 deletions(-) diff --git a/code/renderercommon/tr_common.h b/code/renderercommon/tr_common.h index 9f15a2d00c..eb79fdfcb9 100644 --- a/code/renderercommon/tr_common.h +++ b/code/renderercommon/tr_common.h @@ -116,7 +116,7 @@ extern cvar_t *r_saveFontData; qboolean R_GetModeInfo( int *width, int *height, float *windowAspect, int mode ); -float R_NoiseGet4f( float x, float y, float z, float t ); +float R_NoiseGet4f( float x, float y, float z, double t ); void R_NoiseInit( void ); image_t *R_FindImageFile( const char *name, imgType_t type, imgFlags_t flags ); diff --git a/code/renderercommon/tr_noise.c b/code/renderercommon/tr_noise.c index 445ef82729..abd27cbd9c 100644 --- a/code/renderercommon/tr_noise.c +++ b/code/renderercommon/tr_noise.c @@ -49,7 +49,7 @@ void R_NoiseInit( void ) } } -float R_NoiseGet4f( float x, float y, float z, float t ) +float R_NoiseGet4f( float x, float y, float z, double t ) { int i; int ix, iy, iz, it; diff --git a/code/renderergl1/tr_backend.c b/code/renderergl1/tr_backend.c index 22ec04800f..869bfcd6e9 100644 --- a/code/renderergl1/tr_backend.c +++ b/code/renderergl1/tr_backend.c @@ -716,7 +716,7 @@ void RB_SetGL2D (void) { // set time for 2D shaders backEnd.refdef.time = ri.Milliseconds(); - backEnd.refdef.floatTime = (double)backEnd.refdef.time * 0.001f; + backEnd.refdef.floatTime = backEnd.refdef.time * 0.001; } diff --git a/code/renderergl1/tr_local.h b/code/renderergl1/tr_local.h index 54279a6bec..ffd824a5f0 100644 --- a/code/renderergl1/tr_local.h +++ b/code/renderergl1/tr_local.h @@ -189,10 +189,10 @@ typedef enum { typedef struct { genFunc_t func; - double base; - double amplitude; - double phase; - double frequency; + float base; + float amplitude; + float phase; + float frequency; } waveForm_t; #define TR_MAX_TEXMODS 4 @@ -252,7 +252,7 @@ typedef struct { typedef struct { image_t *image[MAX_IMAGE_ANIMATIONS]; int numImageAnimations; - double imageAnimationSpeed; + float imageAnimationSpeed; texCoordGen_t tcGen; vec3_t tcGenVectors[2]; diff --git a/code/renderergl1/tr_scene.c b/code/renderergl1/tr_scene.c index 5d7a3dd1a5..639f4e2d09 100644 --- a/code/renderergl1/tr_scene.c +++ b/code/renderergl1/tr_scene.c @@ -344,7 +344,7 @@ void RE_RenderScene( const refdef_t *fd ) { // derived info - tr.refdef.floatTime = tr.refdef.time * 0.001f; + tr.refdef.floatTime = tr.refdef.time * 0.001; tr.refdef.numDrawSurfs = r_firstSceneDrawSurf; tr.refdef.drawSurfs = backEndData->drawSurfs; diff --git a/code/renderergl1/tr_shade.c b/code/renderergl1/tr_shade.c index ab5813f085..61a6e0b1ed 100644 --- a/code/renderergl1/tr_shade.c +++ b/code/renderergl1/tr_shade.c @@ -219,7 +219,6 @@ R_BindAnimatedImage */ static void R_BindAnimatedImage( textureBundle_t *bundle ) { int64_t index; - double v; if ( bundle->isVideoMap ) { ri.CIN_RunCinematic(bundle->videoMapHandle); @@ -234,10 +233,8 @@ static void R_BindAnimatedImage( textureBundle_t *bundle ) { // it is necessary to do this messy calc to make sure animations line up // exactly with waveforms of the same frequency - //index = ri.ftol(tess.shaderTime * bundle->imageAnimationSpeed * FUNCTABLE_SIZE); - //index >>= FUNCTABLE_SIZE2; - v = tess.shaderTime * bundle->imageAnimationSpeed; - index = v; + index = tess.shaderTime * bundle->imageAnimationSpeed * FUNCTABLE_SIZE; + index >>= FUNCTABLE_SIZE2; if ( index < 0 ) { index = 0; // may happen with shader time offsets diff --git a/code/renderergl1/tr_shade_calc.c b/code/renderergl1/tr_shade_calc.c index 34975e2533..58b54270df 100644 --- a/code/renderergl1/tr_shade_calc.c +++ b/code/renderergl1/tr_shade_calc.c @@ -27,7 +27,7 @@ Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA #endif -#define WAVEVALUE( table, base, amplitude, phase, freq ) ((base) + table[ (int)( ( ( (phase) + tess.shaderTime * (freq) ) * FUNCTABLE_SIZE ) ) & FUNCTABLE_MASK ] * (amplitude)) +#define WAVEVALUE( table, base, amplitude, phase, freq ) ((base) + table[ ( (int64_t) ( ( (phase) + tess.shaderTime * (freq) ) * FUNCTABLE_SIZE ) ) & FUNCTABLE_MASK ] * (amplitude)) static float *TableForFunc( genFunc_t func ) { @@ -206,12 +206,12 @@ void RB_CalcBulgeVertexes( deformStage_t *ds ) { const float *st = ( const float * ) tess.texCoords[0]; float *xyz = ( float * ) tess.xyz; float *normal = ( float * ) tess.normal; - float now; + double now; - now = backEnd.refdef.time * ds->bulgeSpeed * 0.001f; + now = backEnd.refdef.time * 0.001 * ds->bulgeSpeed; for ( i = 0; i < tess.numVertexes; i++, xyz += 4, st += 4, normal += 4 ) { - int off; + int64_t off; float scale; off = (float)( FUNCTABLE_SIZE / (M_PI*2) ) * ( st[0] * ds->bulgeWidth + now ); @@ -929,8 +929,8 @@ void RB_CalcTurbulentTexCoords( const waveForm_t *wf, float *st ) float s = st[0]; float t = st[1]; - st[0] = s + tr.sinTable[ ( ( int ) ( ( ( tess.xyz[i][0] + tess.xyz[i][2] )* 1.0/128 * 0.125 + now ) * FUNCTABLE_SIZE ) ) & ( FUNCTABLE_MASK ) ] * wf->amplitude; - st[1] = t + tr.sinTable[ ( ( int ) ( ( tess.xyz[i][1] * 1.0/128 * 0.125 + now ) * FUNCTABLE_SIZE ) ) & ( FUNCTABLE_MASK ) ] * wf->amplitude; + st[0] = s + tr.sinTable[ ( ( int64_t ) ( ( ( tess.xyz[i][0] + tess.xyz[i][2] )* 1.0/128 * 0.125 + now ) * FUNCTABLE_SIZE ) ) & ( FUNCTABLE_MASK ) ] * wf->amplitude; + st[1] = t + tr.sinTable[ ( ( int64_t ) ( ( tess.xyz[i][1] * 1.0/128 * 0.125 + now ) * FUNCTABLE_SIZE ) ) & ( FUNCTABLE_MASK ) ] * wf->amplitude; } } @@ -954,13 +954,11 @@ void RB_CalcScaleTexCoords( const float scale[2], float *st ) void RB_CalcScrollTexCoords( const float scrollSpeed[2], float *st ) { int i; - double timeScale; + double timeScale = tess.shaderTime; double adjustedScrollS, adjustedScrollT; - timeScale = tess.shaderTime; - - adjustedScrollS = (double)scrollSpeed[0] * timeScale; - adjustedScrollT = (double)scrollSpeed[1] * timeScale; + adjustedScrollS = scrollSpeed[0] * timeScale; + adjustedScrollT = scrollSpeed[1] * timeScale; // clamp so coordinates don't continuously get larger, causing problems // with hardware limits diff --git a/code/renderergl2/tr_backend.c b/code/renderergl2/tr_backend.c index 4f8f3f4fec..decccbf90b 100644 --- a/code/renderergl2/tr_backend.c +++ b/code/renderergl2/tr_backend.c @@ -661,7 +661,7 @@ void RB_SetGL2D (void) { // set time for 2D shaders backEnd.refdef.time = ri.Milliseconds(); - backEnd.refdef.floatTime = (double)backEnd.refdef.time * 0.001f; + backEnd.refdef.floatTime = backEnd.refdef.time * 0.001; } diff --git a/code/renderergl2/tr_local.h b/code/renderergl2/tr_local.h index 9426ede674..4926149f5a 100644 --- a/code/renderergl2/tr_local.h +++ b/code/renderergl2/tr_local.h @@ -268,10 +268,10 @@ typedef enum { typedef struct { genFunc_t func; - double base; - double amplitude; - double phase; - double frequency; + float base; + float amplitude; + float phase; + float frequency; } waveForm_t; #define TR_MAX_TEXMODS 4 @@ -331,7 +331,7 @@ typedef struct { typedef struct { image_t *image[MAX_IMAGE_ANIMATIONS]; int numImageAnimations; - double imageAnimationSpeed; + float imageAnimationSpeed; texCoordGen_t tcGen; vec3_t tcGenVectors[2]; @@ -479,29 +479,6 @@ typedef struct shader_s { struct shader_s *next; } shader_t; -static ID_INLINE qboolean ShaderRequiresCPUDeforms(const shader_t * shader) -{ - if(shader->numDeforms) - { - const deformStage_t *ds = &shader->deforms[0]; - - if (shader->numDeforms > 1) - return qtrue; - - switch (ds->deformation) - { - case DEFORM_WAVE: - case DEFORM_BULGE: - return qfalse; - - default: - return qtrue; - } - } - - return qfalse; -} - enum { ATTR_INDEX_POSITION = 0, @@ -1791,6 +1768,32 @@ extern cvar_t *r_marksOnTriangleMeshes; //==================================================================== +static ID_INLINE qboolean ShaderRequiresCPUDeforms(const shader_t * shader) +{ + if(shader->numDeforms) + { + const deformStage_t *ds = &shader->deforms[0]; + + if (shader->numDeforms > 1) + return qtrue; + + switch (ds->deformation) + { + case DEFORM_WAVE: + case DEFORM_BULGE: + // need CPU deforms at high level-times to avoid floating point percision loss + return ( backEnd.refdef.floatTime != (float)backEnd.refdef.floatTime ); + + default: + return qtrue; + } + } + + return qfalse; +} + +//==================================================================== + void R_SwapBuffers( int ); void R_RenderView( viewParms_t *parms ); diff --git a/code/renderergl2/tr_scene.c b/code/renderergl2/tr_scene.c index e336978ee3..ff0a22e2fe 100644 --- a/code/renderergl2/tr_scene.c +++ b/code/renderergl2/tr_scene.c @@ -400,7 +400,7 @@ void RE_BeginScene(const refdef_t *fd) // derived info - tr.refdef.floatTime = tr.refdef.time * 0.001f; + tr.refdef.floatTime = tr.refdef.time * 0.001; tr.refdef.numDrawSurfs = r_firstSceneDrawSurf; tr.refdef.drawSurfs = backEndData->drawSurfs; diff --git a/code/renderergl2/tr_shade.c b/code/renderergl2/tr_shade.c index 0fd15486fd..b0ca8628ad 100644 --- a/code/renderergl2/tr_shade.c +++ b/code/renderergl2/tr_shade.c @@ -66,7 +66,6 @@ R_BindAnimatedImageToTMU */ static void R_BindAnimatedImageToTMU( textureBundle_t *bundle, int tmu ) { int64_t index; - double v; if ( bundle->isVideoMap ) { ri.CIN_RunCinematic(bundle->videoMapHandle); @@ -82,10 +81,8 @@ static void R_BindAnimatedImageToTMU( textureBundle_t *bundle, int tmu ) { // it is necessary to do this messy calc to make sure animations line up // exactly with waveforms of the same frequency - //index = ri.ftol(tess.shaderTime * bundle->imageAnimationSpeed * FUNCTABLE_SIZE); - //index >>= FUNCTABLE_SIZE2; - v = tess.shaderTime * bundle->imageAnimationSpeed; - index = v; + index = tess.shaderTime * bundle->imageAnimationSpeed * FUNCTABLE_SIZE; + index >>= FUNCTABLE_SIZE2; if ( index < 0 ) { index = 0; // may happen with shader time offsets diff --git a/code/renderergl2/tr_shade_calc.c b/code/renderergl2/tr_shade_calc.c index c411e03f9a..8369ad7432 100644 --- a/code/renderergl2/tr_shade_calc.c +++ b/code/renderergl2/tr_shade_calc.c @@ -27,7 +27,7 @@ Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA #endif -#define WAVEVALUE( table, base, amplitude, phase, freq ) ((base) + table[ (int)( ( ( (phase) + tess.shaderTime * (freq) ) * FUNCTABLE_SIZE ) ) & FUNCTABLE_MASK ] * (amplitude)) +#define WAVEVALUE( table, base, amplitude, phase, freq ) ((base) + table[ ( (int64_t) ( ( (phase) + tess.shaderTime * (freq) ) * FUNCTABLE_SIZE ) ) & FUNCTABLE_MASK ] * (amplitude)) static float *TableForFunc( genFunc_t func ) { @@ -204,12 +204,12 @@ void RB_CalcBulgeVertexes( deformStage_t *ds ) { const float *st = ( const float * ) tess.texCoords[0]; float *xyz = ( float * ) tess.xyz; int16_t *normal = tess.normal[0]; - float now; + double now; - now = backEnd.refdef.time * ds->bulgeSpeed * 0.001f; + now = backEnd.refdef.time * 0.001 * ds->bulgeSpeed; for ( i = 0; i < tess.numVertexes; i++, xyz += 4, st += 2, normal += 4 ) { - int off; + int64_t off; float scale; vec3_t fNormal; @@ -776,18 +776,16 @@ void RB_CalcScaleTexMatrix( const float scale[2], float *matrix ) */ void RB_CalcScrollTexMatrix( const float scrollSpeed[2], float *matrix ) { - double timeScale; + double timeScale = tess.shaderTime; double adjustedScrollS, adjustedScrollT; - timeScale = tess.shaderTime; - adjustedScrollS = scrollSpeed[0] * timeScale; adjustedScrollT = scrollSpeed[1] * timeScale; // clamp so coordinates don't continuously get larger, causing problems // with hardware limits - adjustedScrollS = (double)adjustedScrollS - floor( adjustedScrollS ); - adjustedScrollT = (double)adjustedScrollT - floor( adjustedScrollT ); + adjustedScrollS = adjustedScrollS - floor( adjustedScrollS ); + adjustedScrollT = adjustedScrollT - floor( adjustedScrollT ); matrix[0] = 1.0f; matrix[2] = 0.0f; matrix[4] = adjustedScrollS; matrix[1] = 0.0f; matrix[3] = 1.0f; matrix[5] = adjustedScrollT; From 2955f8dadb0c521218b634eb98da41de9c429a51 Mon Sep 17 00:00:00 2001 From: SmileTheory Date: Thu, 3 Aug 2017 14:20:33 -0700 Subject: [PATCH 146/240] Reject OpenGL contexts w/ software renderer when core context requested. --- code/sdl/sdl_glimp.c | 17 ++++++++++++++++- 1 file changed, 16 insertions(+), 1 deletion(-) diff --git a/code/sdl/sdl_glimp.c b/code/sdl/sdl_glimp.c index abb66ec327..454c2bf398 100644 --- a/code/sdl/sdl_glimp.c +++ b/code/sdl/sdl_glimp.c @@ -499,7 +499,22 @@ static int GLimp_SetMode(int mode, qboolean fullscreen, qboolean noborder, qbool } else { - ri.Printf(PRINT_ALL, "SDL_GL_CreateContext succeeded, but: %s\n", SDL_GetError()); + const char *renderer; + + ri.Printf(PRINT_ALL, "SDL_GL_CreateContext succeeded.\n"); + + renderer = (const char *)qglGetString(GL_RENDERER); + if (renderer && (strstr(renderer, "Software Renderer") || strstr(renderer, "Software Rasterizer"))) + { + ri.Printf(PRINT_ALL, "GL_RENDERER is %s, rejecting context\n", renderer); + + SDL_GL_DeleteContext(SDL_glContext); + SDL_glContext = NULL; + + SDL_GL_SetAttribute(SDL_GL_CONTEXT_PROFILE_MASK, profileMask); + SDL_GL_SetAttribute(SDL_GL_CONTEXT_MAJOR_VERSION, majorVersion); + SDL_GL_SetAttribute(SDL_GL_CONTEXT_MINOR_VERSION, minorVersion); + } } } else From 00a5339b2722eec678dcf2237b9d996cb9d11467 Mon Sep 17 00:00:00 2001 From: Zack Middleton Date: Mon, 7 Aug 2017 05:39:23 -0500 Subject: [PATCH 147/240] Update arch in misc/setup for ioq3 changing from i386 to x86 --- misc/setup/Solaris_pkg.sh | 4 ++-- misc/setup/doit | 4 ++-- misc/setup/ioq3demo.sh | 4 ++-- misc/setup/ioquake3.SlackBuild | 4 ++-- misc/setup/ioquake3.sh | 4 ++-- misc/setup/pkg/ioq3ded.sh | 2 +- misc/setup/pkg/ioquake3.sh | 2 +- misc/setup/setup.xml.in | 4 ++-- 8 files changed, 14 insertions(+), 14 deletions(-) diff --git a/misc/setup/Solaris_pkg.sh b/misc/setup/Solaris_pkg.sh index 650d2854ec..306dbcaa3d 100644 --- a/misc/setup/Solaris_pkg.sh +++ b/misc/setup/Solaris_pkg.sh @@ -11,7 +11,7 @@ fi if [ "X`uname -m`" = "Xi86pc" ]; then - ARCH=i386 + ARCH=x86 else ARCH=sparc fi @@ -94,7 +94,7 @@ if [ -d ${BUILD_DIR} ]; then fi done - for EXEC_SO in cgamesparc.so qagamesparc.so uisparc.so cgamei386.so qagamei386.so uii386.so + for EXEC_SO in cgamesparc.so qagamesparc.so uisparc.so cgamex86.so qagamex86.so uix86.so do if [ -f ${BUILD_DIR}/baseq3/${EXEC_SO} ]; then ${INSTALL_BIN} ${BUILD_DIR}/baseq3/${EXEC_SO} ${PKG_BUILD_DIR}/baseq3/${EXEC_SO} diff --git a/misc/setup/doit b/misc/setup/doit index 2e082ba7b7..010dc48099 100755 --- a/misc/setup/doit +++ b/misc/setup/doit @@ -46,8 +46,8 @@ archs=() for arch in $topdir/build/release-*; do arch=${arch##*-} case "$arch" in - i386) echo "define(HAVE_I386,yes)dnl" >> defines.m4 - copystartscript x86 + x86) echo "define(HAVE_X86,yes)dnl" >> defines.m4 + copystartscript $arch ;; x86_64) echo "define(HAVE_X86_64,yes)dnl" >> defines.m4 copystartscript $arch diff --git a/misc/setup/ioq3demo.sh b/misc/setup/ioq3demo.sh index 05df7ff8bc..b7102241a6 100644 --- a/misc/setup/ioq3demo.sh +++ b/misc/setup/ioq3demo.sh @@ -38,8 +38,8 @@ export LD_LIBRARY_PATH archs=`uname -m` case "$archs" in - i?86) archs=i386 ;; - x86_64) archs="x86_64 i386" ;; + i?86) archs=x86 ;; + x86_64) archs="x86_64 x86" ;; ppc64) archs="ppc64 ppc" ;; esac diff --git a/misc/setup/ioquake3.SlackBuild b/misc/setup/ioquake3.SlackBuild index 187d58b5af..e48d0d3788 100644 --- a/misc/setup/ioquake3.SlackBuild +++ b/misc/setup/ioquake3.SlackBuild @@ -24,9 +24,9 @@ PKG_VERSION=$VERSION ARCH=${ARCH:-i586} if [ "$ARCH" = "i?86" ]; then - ARCHSUFFIX="i386" + ARCHSUFFIX="x86" elif [ "$ARCH" = "x86_64" ]; then - ARCHSUFFIX="64" + ARCHSUFFIX="x86_64" fi BUILD=${BUILD:-1_io} diff --git a/misc/setup/ioquake3.sh b/misc/setup/ioquake3.sh index fbef56760c..6ebc4d1730 100644 --- a/misc/setup/ioquake3.sh +++ b/misc/setup/ioquake3.sh @@ -38,8 +38,8 @@ export LD_LIBRARY_PATH archs=`uname -m` case "$archs" in - i?86) archs=i386 ;; - x86_64) archs="x86_64 i386" ;; + i?86) archs=x86 ;; + x86_64) archs="x86_64 x86" ;; ppc64) archs="ppc64 ppc" ;; esac diff --git a/misc/setup/pkg/ioq3ded.sh b/misc/setup/pkg/ioq3ded.sh index 511f4fb538..2df6368292 100644 --- a/misc/setup/pkg/ioq3ded.sh +++ b/misc/setup/pkg/ioq3ded.sh @@ -13,7 +13,7 @@ fi export LD_LIBRARY_PATH COMPILE_PLATFORM=`uname|sed -e 's/_.*//'|tr '[:upper:]' '[:lower:]'` -COMPILE_ARCH=`uname -p | sed -e 's/i.86/i386/'` +COMPILE_ARCH=`uname -p | sed -e 's/i.86/x86/'` EXEC_REL=release diff --git a/misc/setup/pkg/ioquake3.sh b/misc/setup/pkg/ioquake3.sh index 29a050b71b..fdc7816039 100644 --- a/misc/setup/pkg/ioquake3.sh +++ b/misc/setup/pkg/ioquake3.sh @@ -13,7 +13,7 @@ fi export LD_LIBRARY_PATH COMPILE_PLATFORM=`uname|sed -e 's/_.*//'|tr '[:upper:]' '[:lower:]'` -COMPILE_ARCH=`uname -p | sed -e 's/i.86/i386/'` +COMPILE_ARCH=`uname -p | sed -e 's/i.86/x86/'` EXEC_REL=release diff --git a/misc/setup/setup.xml.in b/misc/setup/setup.xml.in index b8201018e5..4b076755ad 100644 --- a/misc/setup/setup.xml.in +++ b/misc/setup/setup.xml.in @@ -24,12 +24,12 @@ ifelse(HAVE_X86_64,yes,dnl you need the binaries to play the game )dnl -ifelse(HAVE_I386,yes,dnl +ifelse(HAVE_X86,yes,dnl From 3f415abe612f5bed3856c921f57e2c8a2d722a6c Mon Sep 17 00:00:00 2001 From: SmileTheory Date: Mon, 7 Aug 2017 18:00:00 -0700 Subject: [PATCH 148/240] OpenGL2: Use extension functions with OpenGL versions before 3.0. --- code/renderercommon/qgl.h | 10 ++- code/renderergl2/tr_extensions.c | 130 ++++++++++++++++++------------- 2 files changed, 87 insertions(+), 53 deletions(-) diff --git a/code/renderercommon/qgl.h b/code/renderercommon/qgl.h index 80fa0a3aeb..8f76c92ffe 100644 --- a/code/renderercommon/qgl.h +++ b/code/renderercommon/qgl.h @@ -479,9 +479,12 @@ extern void (APIENTRYP qglUnlockArraysEXT) (void); #define GL_HALF_FLOAT_ARB 0x140B #endif -// OpenGL 3.0, was GL_EXT_framebuffer_object, GL_EXT_framebuffer_blit, GL_EXT_framebuffer_multisample, and GL_ARB_vertex_array_object +// OpenGL 3.0 specific #define QGL_3_0_PROCS \ GLE(const GLubyte *, GetStringi, GLenum name, GLuint index) \ + +// GL_ARB_framebuffer_object, built-in to OpenGL 3.0 +#define QGL_ARB_framebuffer_object_PROCS \ GLE(void, BindRenderbuffer, GLenum target, GLuint renderbuffer) \ GLE(void, DeleteRenderbuffers, GLsizei n, const GLuint *renderbuffers) \ GLE(void, GenRenderbuffers, GLsizei n, GLuint *renderbuffers) \ @@ -495,6 +498,9 @@ extern void (APIENTRYP qglUnlockArraysEXT) (void); GLE(void, GenerateMipmap, GLenum target) \ GLE(void, BlitFramebuffer, GLint srcX0, GLint srcY0, GLint srcX1, GLint srcY1, GLint dstX0, GLint dstY0, GLint dstX1, GLint dstY1, GLbitfield mask, GLenum filter) \ GLE(void, RenderbufferStorageMultisample, GLenum target, GLsizei samples, GLenum internalformat, GLsizei width, GLsizei height) \ + +// GL_ARB_vertex_array_object, built-in to OpenGL 3.0 +#define QGL_ARB_vertex_array_object_PROCS \ GLE(void, BindVertexArray, GLuint array) \ GLE(void, DeleteVertexArrays, GLsizei n, const GLuint *arrays) \ GLE(void, GenVertexArrays, GLsizei n, GLuint *arrays) \ @@ -554,6 +560,8 @@ QGL_1_3_PROCS; QGL_1_5_PROCS; QGL_2_0_PROCS; QGL_3_0_PROCS; +QGL_ARB_framebuffer_object_PROCS; +QGL_ARB_vertex_array_object_PROCS; QGL_EXT_direct_state_access_PROCS; #undef GLE diff --git a/code/renderergl2/tr_extensions.c b/code/renderergl2/tr_extensions.c index 7df52956b3..8447608ee8 100644 --- a/code/renderergl2/tr_extensions.c +++ b/code/renderergl2/tr_extensions.c @@ -35,6 +35,8 @@ QGL_1_3_PROCS; QGL_1_5_PROCS; QGL_2_0_PROCS; QGL_3_0_PROCS; +QGL_ARB_framebuffer_object_PROCS; +QGL_ARB_vertex_array_object_PROCS; QGL_EXT_direct_state_access_PROCS; #undef GLE @@ -42,6 +44,8 @@ void GLimp_InitExtraExtensions() { char *extension; const char* result[3] = { "...ignoring %s\n", "...using %s\n", "...%s not found\n" }; + qboolean q_gl_version_at_least_3_0; + qboolean q_gl_version_at_least_3_2; // Check OpenGL version sscanf(glConfig.version_string, "%d.%d", &glRefConfig.openglMajorVersion, &glRefConfig.openglMinorVersion); @@ -49,6 +53,9 @@ void GLimp_InitExtraExtensions() ri.Error(ERR_FATAL, "OpenGL 2.0 required!"); ri.Printf(PRINT_ALL, "...using OpenGL %s\n", glConfig.version_string); + q_gl_version_at_least_3_0 = (glRefConfig.openglMajorVersion >= 3); + q_gl_version_at_least_3_2 = (glRefConfig.openglMajorVersion > 3 || (glRefConfig.openglMajorVersion == 3 && glRefConfig.openglMinorVersion > 2)); + // Check if we need Intel graphics specific fixes. glRefConfig.intelGraphics = qfalse; if (strstr((char *)qglGetString(GL_RENDERER), "Intel")) @@ -72,31 +79,92 @@ void GLimp_InitExtraExtensions() // OpenGL 2.0, was GL_ARB_shading_language_100, GL_ARB_vertex_program, GL_ARB_shader_objects, and GL_ARB_vertex_shader QGL_2_0_PROCS; - // OpenGL 3.0, was GL_EXT_framebuffer_object, GL_EXT_framebuffer_blit, GL_EXT_framebuffer_multisample, and GL_ARB_vertex_array_object + // OpenGL 3.0 - no matching extension // QGL_*_PROCS becomes several functions, do not remove {} - if (glRefConfig.openglMajorVersion >= 3) + if (q_gl_version_at_least_3_0) { QGL_3_0_PROCS; + } + // OpenGL 3.0 - GL_ARB_framebuffer_object + extension = "GL_ARB_framebuffer_object"; + glRefConfig.framebufferObject = qfalse; + glRefConfig.framebufferBlit = qfalse; + glRefConfig.framebufferMultisample = qfalse; + if (q_gl_version_at_least_3_0 || SDL_GL_ExtensionSupported(extension)) + { glRefConfig.framebufferObject = !!r_ext_framebuffer_object->integer; glRefConfig.framebufferBlit = qtrue; glRefConfig.framebufferMultisample = qtrue; - qglGetIntegerv(GL_MAX_RENDERBUFFER_SIZE_EXT, &glRefConfig.maxRenderbufferSize); - qglGetIntegerv(GL_MAX_COLOR_ATTACHMENTS_EXT, &glRefConfig.maxColorAttachments); + qglGetIntegerv(GL_MAX_RENDERBUFFER_SIZE, &glRefConfig.maxRenderbufferSize); + qglGetIntegerv(GL_MAX_COLOR_ATTACHMENTS, &glRefConfig.maxColorAttachments); + + QGL_ARB_framebuffer_object_PROCS; + + ri.Printf(PRINT_ALL, result[glRefConfig.framebufferObject], extension); + } + else + { + ri.Printf(PRINT_ALL, result[2], extension); + } + + // OpenGL 3.0 - GL_ARB_vertex_array_object + extension = "GL_ARB_vertex_array_object"; + glRefConfig.vertexArrayObject = qfalse; + if (SDL_GL_ExtensionSupported(extension)) + { + glRefConfig.vertexArrayObject = !!r_arb_vertex_array_object->integer; - ri.Printf(PRINT_ALL, result[glRefConfig.framebufferObject], "OpenGL 3.0+ framebuffer procs"); + QGL_ARB_vertex_array_object_PROCS; - // Don't let this be disabled, core context requires it - glRefConfig.vertexArrayObject = qtrue; + ri.Printf(PRINT_ALL, result[glRefConfig.vertexArrayObject], extension); + } + else + { + ri.Printf(PRINT_ALL, result[2], extension); + } + + // OpenGL 3.0 - GL_ARB_texture_float + extension = "GL_ARB_texture_float"; + glRefConfig.textureFloat = qfalse; + if (q_gl_version_at_least_3_0 || SDL_GL_ExtensionSupported(extension)) + { + glRefConfig.textureFloat = !!r_ext_texture_float->integer; - ri.Printf(PRINT_ALL, result[glRefConfig.vertexArrayObject], "OpenGL 3.0+ vertex array object procs"); + ri.Printf(PRINT_ALL, result[glRefConfig.textureFloat], extension); } else { - ri.Printf(PRINT_ALL, result[2], "OpenGL 3.0+ framebuffer procs"); - ri.Printf(PRINT_ALL, result[2], "OpenGL 3.0+ vertex array object procs"); + ri.Printf(PRINT_ALL, result[2], extension); + } + // OpenGL 3.2 - GL_ARB_depth_clamp + extension = "GL_ARB_depth_clamp"; + glRefConfig.depthClamp = qfalse; + if (q_gl_version_at_least_3_2 || SDL_GL_ExtensionSupported(extension)) + { + glRefConfig.depthClamp = qtrue; + + ri.Printf(PRINT_ALL, result[glRefConfig.depthClamp], extension); + } + else + { + ri.Printf(PRINT_ALL, result[2], extension); + } + + // OpenGL 3.2 - GL_ARB_seamless_cube_map + extension = "GL_ARB_seamless_cube_map"; + glRefConfig.seamlessCubeMap = qfalse; + if (q_gl_version_at_least_3_2 || SDL_GL_ExtensionSupported(extension)) + { + glRefConfig.seamlessCubeMap = !!r_arb_seamless_cube_map->integer; + + ri.Printf(PRINT_ALL, result[glRefConfig.seamlessCubeMap], extension); + } + else + { + ri.Printf(PRINT_ALL, result[2], extension); } // Determine GLSL version @@ -146,20 +214,6 @@ void GLimp_InitExtraExtensions() ri.Printf(PRINT_ALL, result[2], extension); } - // GL_ARB_texture_float - extension = "GL_ARB_texture_float"; - glRefConfig.textureFloat = qfalse; - if( SDL_GL_ExtensionSupported( extension ) ) - { - glRefConfig.textureFloat = !!r_ext_texture_float->integer; - - ri.Printf(PRINT_ALL, result[glRefConfig.textureFloat], extension); - } - else - { - ri.Printf(PRINT_ALL, result[2], extension); - } - glRefConfig.textureCompression = TCR_NONE; // GL_ARB_texture_compression_rgtc @@ -196,34 +250,6 @@ void GLimp_InitExtraExtensions() ri.Printf(PRINT_ALL, result[2], extension); } - // GL_ARB_depth_clamp - extension = "GL_ARB_depth_clamp"; - glRefConfig.depthClamp = qfalse; - if( SDL_GL_ExtensionSupported( extension ) ) - { - glRefConfig.depthClamp = qtrue; - - ri.Printf(PRINT_ALL, result[glRefConfig.depthClamp], extension); - } - else - { - ri.Printf(PRINT_ALL, result[2], extension); - } - - // GL_ARB_seamless_cube_map - extension = "GL_ARB_seamless_cube_map"; - glRefConfig.seamlessCubeMap = qfalse; - if( SDL_GL_ExtensionSupported( extension ) ) - { - glRefConfig.seamlessCubeMap = !!r_arb_seamless_cube_map->integer; - - ri.Printf(PRINT_ALL, result[glRefConfig.seamlessCubeMap], extension); - } - else - { - ri.Printf(PRINT_ALL, result[2], extension); - } - // GL_EXT_direct_state_access extension = "GL_EXT_direct_state_access"; glRefConfig.directStateAccess = qfalse; From 9d1c6748f594b90836d3aded3ffd15bc3c6461a5 Mon Sep 17 00:00:00 2001 From: SmileTheory Date: Tue, 8 Aug 2017 20:20:11 -0700 Subject: [PATCH 149/240] OpenGL2: Force VAO usage on OpenGL 3.0+ --- code/renderergl2/tr_extensions.c | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/code/renderergl2/tr_extensions.c b/code/renderergl2/tr_extensions.c index 8447608ee8..e90f51893c 100644 --- a/code/renderergl2/tr_extensions.c +++ b/code/renderergl2/tr_extensions.c @@ -112,9 +112,17 @@ void GLimp_InitExtraExtensions() // OpenGL 3.0 - GL_ARB_vertex_array_object extension = "GL_ARB_vertex_array_object"; glRefConfig.vertexArrayObject = qfalse; - if (SDL_GL_ExtensionSupported(extension)) + if (q_gl_version_at_least_3_0 || SDL_GL_ExtensionSupported(extension)) { - glRefConfig.vertexArrayObject = !!r_arb_vertex_array_object->integer; + if (q_gl_version_at_least_3_0) + { + // force VAO, core context requires it + glRefConfig.vertexArrayObject = qtrue; + } + else + { + glRefConfig.vertexArrayObject = !!r_arb_vertex_array_object->integer; + } QGL_ARB_vertex_array_object_PROCS; From 0bce5463f735aad509b27f76f5e98b4afb4c5b75 Mon Sep 17 00:00:00 2001 From: Zack Middleton Date: Wed, 9 Aug 2017 17:47:23 -0500 Subject: [PATCH 150/240] Add spawnflags to QUAKED for trigger_multiple --- code/game/g_trigger.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/code/game/g_trigger.c b/code/game/g_trigger.c index 74613918e0..489040b487 100644 --- a/code/game/g_trigger.c +++ b/code/game/g_trigger.c @@ -84,7 +84,7 @@ void Touch_Multi( gentity_t *self, gentity_t *other, trace_t *trace ) { multi_trigger( self, other ); } -/*QUAKED trigger_multiple (.5 .5 .5) ? +/*QUAKED trigger_multiple (.5 .5 .5) ? RED_ONLY BLUE_ONLY "wait" : Seconds between triggerings, 0.5 default, -1 = one time only. "random" wait variance, default is 0 Variable sized repeatable trigger. Must be targeted at one or more entities. From f0b74a27c97f6b03cc270d0536a77bdfcc1e8fad Mon Sep 17 00:00:00 2001 From: Zack Middleton Date: Wed, 9 Aug 2017 18:27:39 -0500 Subject: [PATCH 151/240] Check for all command separators in callTeamVote Make callTeamVote check for all command separators like in callVote. It's not exploitable as the only vote option (leader) always uses an integer argument. Also the ioquake3 engine remove command separators from client game commands in Cmd_Args_Sanitize(). --- code/game/g_cmds.c | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) diff --git a/code/game/g_cmds.c b/code/game/g_cmds.c index 46a69ca8ca..b02a19e72d 100644 --- a/code/game/g_cmds.c +++ b/code/game/g_cmds.c @@ -1489,9 +1489,16 @@ void Cmd_CallTeamVote_f( gentity_t *ent ) { trap_Argv( i, &arg2[strlen(arg2)], sizeof( arg2 ) - strlen(arg2) ); } - if( strchr( arg1, ';' ) || strchr( arg2, ';' ) ) { - trap_SendServerCommand( ent-g_entities, "print \"Invalid vote string.\n\"" ); - return; + // check for command separators in arg2 + for( c = arg2; *c; ++c) { + switch(*c) { + case '\n': + case '\r': + case ';': + trap_SendServerCommand( ent-g_entities, "print \"Invalid vote string.\n\"" ); + return; + break; + } } if ( !Q_stricmp( arg1, "leader" ) ) { From 3f267728c396c39bed6ef40de42422485b7b6794 Mon Sep 17 00:00:00 2001 From: Zack Middleton Date: Wed, 9 Aug 2017 18:34:26 -0500 Subject: [PATCH 152/240] Remove newlines from chat messages in Game VM This has no affect due to ioquake3 engine already removing command separators (which includes newlines) in Cmd_Args_Sanitize(). --- code/game/g_cmds.c | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/code/game/g_cmds.c b/code/game/g_cmds.c index b02a19e72d..c56a1de4cc 100644 --- a/code/game/g_cmds.c +++ b/code/game/g_cmds.c @@ -928,6 +928,16 @@ void G_Say( gentity_t *ent, gentity_t *target, int mode, const char *chatText ) } } +static void SanitizeChatText( char *text ) { + int i; + + for ( i = 0; text[i]; i++ ) { + if ( text[i] == '\n' || text[i] == '\r' ) { + text[i] = ' '; + } + } +} + /* ================== @@ -950,6 +960,8 @@ static void Cmd_Say_f( gentity_t *ent, int mode, qboolean arg0 ) { p = ConcatArgs( 1 ); } + SanitizeChatText( p ); + G_Say( ent, NULL, mode, p ); } @@ -982,6 +994,8 @@ static void Cmd_Tell_f( gentity_t *ent ) { p = ConcatArgs( 2 ); + SanitizeChatText( p ); + G_LogPrintf( "tell: %s to %s: %s\n", ent->client->pers.netname, target->client->pers.netname, p ); G_Say( ent, target, SAY_TELL, p ); // don't tell to the player self if it was already directed to this player @@ -1076,6 +1090,8 @@ static void Cmd_Voice_f( gentity_t *ent, int mode, qboolean arg0, qboolean voice p = ConcatArgs( 1 ); } + SanitizeChatText( p ); + G_Voice( ent, NULL, mode, p, voiceonly ); } @@ -1108,6 +1124,8 @@ static void Cmd_VoiceTell_f( gentity_t *ent, qboolean voiceonly ) { id = ConcatArgs( 2 ); + SanitizeChatText( id ); + G_LogPrintf( "vtell: %s to %s: %s\n", ent->client->pers.netname, target->client->pers.netname, id ); G_Voice( ent, target, SAY_TELL, id, voiceonly ); // don't tell to the player self if it was already directed to this player From 9736e7ff91a45e301da8d8b7e1642ed1b0bb7363 Mon Sep 17 00:00:00 2001 From: Zack Middleton Date: Wed, 9 Aug 2017 19:19:59 -0500 Subject: [PATCH 153/240] Fix compiling Cmd_CallTeamVote_f --- code/game/g_cmds.c | 1 + 1 file changed, 1 insertion(+) diff --git a/code/game/g_cmds.c b/code/game/g_cmds.c index c56a1de4cc..4f73c1d94a 100644 --- a/code/game/g_cmds.c +++ b/code/game/g_cmds.c @@ -1468,6 +1468,7 @@ Cmd_CallTeamVote_f ================== */ void Cmd_CallTeamVote_f( gentity_t *ent ) { + char* c; int i, team, cs_offset; char arg1[MAX_STRING_TOKENS]; char arg2[MAX_STRING_TOKENS]; From bae86208f94499fbd49ac8c87289153ad8169a9d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Micka=C3=ABl=20Thomas?= Date: Wed, 16 Aug 2017 04:47:27 +0200 Subject: [PATCH 154/240] Only allow safe protocols for cURL downloads A malicious server could abuse dangerous protocols such as gopher:// to, for instance, send mail via SMTP. --- code/client/cl_curl.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/code/client/cl_curl.c b/code/client/cl_curl.c index 3ff5a3d89f..5384390e89 100644 --- a/code/client/cl_curl.c +++ b/code/client/cl_curl.c @@ -299,6 +299,8 @@ void CL_cURL_BeginDownload( const char *localName, const char *remoteURL ) qcurl_easy_setopt_warn(clc.downloadCURL, CURLOPT_FAILONERROR, 1); qcurl_easy_setopt_warn(clc.downloadCURL, CURLOPT_FOLLOWLOCATION, 1); qcurl_easy_setopt_warn(clc.downloadCURL, CURLOPT_MAXREDIRS, 5); + qcurl_easy_setopt_warn(clc.downloadCURL, CURLOPT_PROTOCOLS, + CURLPROTO_HTTP | CURLPROTO_HTTPS | CURLPROTO_FTP | CURLPROTO_FTPS); clc.downloadCURLM = qcurl_multi_init(); if(!clc.downloadCURLM) { qcurl_easy_cleanup(clc.downloadCURL); From e793c0c37c32a38341a284cf405a1fc64b016206 Mon Sep 17 00:00:00 2001 From: Zack Middleton Date: Sat, 19 Aug 2017 01:28:49 -0500 Subject: [PATCH 155/240] Fix crash when pmove_msec is 0 When pmove_fixed is set to 1, setting pmove_msec to 0 would crash. --- code/cgame/cg_predict.c | 2 ++ code/game/g_active.c | 2 ++ 2 files changed, 4 insertions(+) diff --git a/code/cgame/cg_predict.c b/code/cgame/cg_predict.c index de33e4c948..6ec837c8ab 100644 --- a/code/cgame/cg_predict.c +++ b/code/cgame/cg_predict.c @@ -489,9 +489,11 @@ void CG_PredictPlayerState( void ) { if ( pmove_msec.integer < 8 ) { trap_Cvar_Set("pmove_msec", "8"); + trap_Cvar_Update(&pmove_msec); } else if (pmove_msec.integer > 33) { trap_Cvar_Set("pmove_msec", "33"); + trap_Cvar_Update(&pmove_msec); } cg_pmove.pmove_fixed = pmove_fixed.integer;// | cg_pmove_fixed.integer; diff --git a/code/game/g_active.c b/code/game/g_active.c index 775604a4e1..413331e98d 100644 --- a/code/game/g_active.c +++ b/code/game/g_active.c @@ -785,9 +785,11 @@ void ClientThink_real( gentity_t *ent ) { if ( pmove_msec.integer < 8 ) { trap_Cvar_Set("pmove_msec", "8"); + trap_Cvar_Update(&pmove_msec); } else if (pmove_msec.integer > 33) { trap_Cvar_Set("pmove_msec", "33"); + trap_Cvar_Update(&pmove_msec); } if ( pmove_fixed.integer || client->pers.pmoveFixed ) { From be1c71d36306637490348a128ecc2245b909c5da Mon Sep 17 00:00:00 2001 From: Zack Middleton Date: Mon, 21 Aug 2017 19:48:44 -0500 Subject: [PATCH 156/240] Combine mouse movement events in event queue Fixes 'Com_QueueEvent: overflow' spam after hitch / map load. Based on patch by @ec-. --- code/qcommon/common.c | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/code/qcommon/common.c b/code/qcommon/common.c index 5d64d6872d..0359a3078c 100644 --- a/code/qcommon/common.c +++ b/code/qcommon/common.c @@ -1961,6 +1961,19 @@ void Com_QueueEvent( int time, sysEventType_t type, int value, int value2, int p { sysEvent_t *ev; + // combine mouse movement with previous mouse event + if ( type == SE_MOUSE && eventHead != eventTail ) + { + ev = &eventQueue[ ( eventHead + MAX_QUEUED_EVENTS - 1 ) & MASK_QUEUED_EVENTS ]; + + if ( ev->evType == SE_MOUSE ) + { + ev->evValue += value; + ev->evValue2 += value2; + return; + } + } + ev = &eventQueue[ eventHead & MASK_QUEUED_EVENTS ]; if ( eventHead - eventTail >= MAX_QUEUED_EVENTS ) From 34d0b7875421ccb3b90251105ff5bf6c301b300a Mon Sep 17 00:00:00 2001 From: Zack Middleton Date: Mon, 21 Aug 2017 20:20:19 -0500 Subject: [PATCH 157/240] Improve keys using international key layouts Always map number row to number keys for AZERTY. Map Unicode character keys to world keys using scan code. Based on patches by @mickael9. --- code/sdl/sdl_input.c | 22 +++++++++++++++++++++- 1 file changed, 21 insertions(+), 1 deletion(-) diff --git a/code/sdl/sdl_input.c b/code/sdl/sdl_input.c index 4c8ff1feea..9d45a5757f 100644 --- a/code/sdl/sdl_input.c +++ b/code/sdl/sdl_input.c @@ -192,7 +192,18 @@ static keyNum_t IN_TranslateSDLToQ3Key( SDL_Keysym *keysym, qboolean down ) { keyNum_t key = 0; - if( keysym->sym >= SDLK_SPACE && keysym->sym < SDLK_DELETE ) + if( keysym->scancode >= SDL_SCANCODE_1 && keysym->scancode <= SDL_SCANCODE_0 ) + { + // Always map the number keys as such even if they actually map + // to other characters (eg, "1" is "&" on an AZERTY keyboard). + // This is required for SDL before 2.0.6, except on Windows + // which already had this behavior. + if( keysym->scancode == SDL_SCANCODE_0 ) + key = '0'; + else + key = '1' + keysym->scancode - SDL_SCANCODE_1; + } + else if( keysym->sym >= SDLK_SPACE && keysym->sym < SDLK_DELETE ) { // These happen to match the ASCII chars key = (int)keysym->sym; @@ -280,6 +291,15 @@ static keyNum_t IN_TranslateSDLToQ3Key( SDL_Keysym *keysym, qboolean down ) case SDLK_CAPSLOCK: key = K_CAPSLOCK; break; default: + if( !( keysym->sym & SDLK_SCANCODE_MASK ) && keysym->scancode <= 95 ) + { + // Map Unicode characters to 95 world keys using the key's scan code. + // FIXME: There aren't enough world keys to cover all the scancodes. + // Maybe create a map of scancode to quake key at start up and on + // key map change; allocate world key numbers as needed similar + // to SDL 1.2. + key = K_WORLD_0 + (int)keysym->scancode; + } break; } } From ead54782d05b67e33ba5338fae081ea3c4af45af Mon Sep 17 00:00:00 2001 From: Zack Middleton Date: Mon, 21 Aug 2017 21:32:24 -0500 Subject: [PATCH 158/240] Improve client input responsiveness Move sampling input to the other side of framerate limiter. Thanks to Alexander "wareya" Nadeau and Juraj "youurayy" Vitko for pointing this out. --- code/qcommon/common.c | 2 ++ code/qcommon/qcommon.h | 8 ++++++++ code/sys/sys_main.c | 1 - 3 files changed, 10 insertions(+), 1 deletion(-) diff --git a/code/qcommon/common.c b/code/qcommon/common.c index 0359a3078c..fd9f9b18de 100644 --- a/code/qcommon/common.c +++ b/code/qcommon/common.c @@ -3162,6 +3162,8 @@ void Com_Frame( void ) { NET_Sleep(timeVal - 1); } while(Com_TimeVal(minMsec)); + IN_Frame(); + lastTime = com_frameTime; com_frameTime = Com_EventLoop(); diff --git a/code/qcommon/qcommon.h b/code/qcommon/qcommon.h index 07eebe7128..b1224ac650 100644 --- a/code/qcommon/qcommon.h +++ b/code/qcommon/qcommon.h @@ -1058,6 +1058,14 @@ int SV_SendQueuedPackets(void); qboolean UI_GameCommand( void ); qboolean UI_usesUniqueCDKey(void); +// +// input interface +// +void IN_Init( void *windowData ); +void IN_Frame( void ); +void IN_Shutdown( void ); +void IN_Restart( void ); + /* ============================================================== diff --git a/code/sys/sys_main.c b/code/sys/sys_main.c index 586e37a137..0598f293d3 100644 --- a/code/sys/sys_main.c +++ b/code/sys/sys_main.c @@ -756,7 +756,6 @@ int main( int argc, char **argv ) while( 1 ) { - IN_Frame( ); Com_Frame( ); } From 5993c63c4e5637a8f3c11adf163ee4542154a0ed Mon Sep 17 00:00:00 2001 From: Zack Middleton Date: Mon, 21 Aug 2017 22:27:09 -0500 Subject: [PATCH 159/240] Removing input functions from sys_local.h for last commit It helps to actually save files before making commits. --- code/sys/sys_local.h | 6 ------ 1 file changed, 6 deletions(-) diff --git a/code/sys/sys_local.h b/code/sys/sys_local.h index 96edd98ebc..4398f87554 100644 --- a/code/sys/sys_local.h +++ b/code/sys/sys_local.h @@ -28,12 +28,6 @@ Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA #define MINSDL_MINOR 0 #define MINSDL_PATCH 0 -// Input subsystem -void IN_Init( void *windowData ); -void IN_Frame( void ); -void IN_Shutdown( void ); -void IN_Restart( void ); - // Console void CON_Shutdown( void ); void CON_Init( void ); From 63e07afae922050d9b9fec1600044d9e07f088be Mon Sep 17 00:00:00 2001 From: Zack Middleton Date: Mon, 21 Aug 2017 22:30:32 -0500 Subject: [PATCH 160/240] Make input events use earliest possible time SDL doesn't provide exact the exact time that input events happen so use the earliest possible time that an event could happen. This make sub-frame input actions such as walking take affect immediately instead of in the next frame. Based on patch by Alexander "wareya" Nadeau. --- code/sdl/sdl_input.c | 101 +++++++++++++++++++++++-------------------- 1 file changed, 53 insertions(+), 48 deletions(-) diff --git a/code/sdl/sdl_input.c b/code/sdl/sdl_input.c index 9d45a5757f..9abf1491ee 100644 --- a/code/sdl/sdl_input.c +++ b/code/sdl/sdl_input.c @@ -51,6 +51,8 @@ static cvar_t *in_joystickUseAnalog = NULL; static int vidRestartTime = 0; +static int in_eventTime = 0; + static SDL_Window *SDL_window = NULL; #define CTRL(a) ((a)-'a'+1) @@ -649,7 +651,7 @@ static void IN_GamepadMove( void ) qboolean pressed = SDL_GameControllerGetButton(gamepad, SDL_CONTROLLER_BUTTON_A + i); if (pressed != stick_state.buttons[i]) { - Com_QueueEvent(0, SE_KEY, K_PAD0_A + i, pressed, 0, NULL); + Com_QueueEvent(in_eventTime, SE_KEY, K_PAD0_A + i, pressed, 0, NULL); stick_state.buttons[i] = pressed; } } @@ -729,19 +731,19 @@ static void IN_GamepadMove( void ) // positive to negative/neutral -> keyup if (!posAnalog && posKey && oldAxis > 0 && axis <= 0) - Com_QueueEvent(0, SE_KEY, posKey, qfalse, 0, NULL); + Com_QueueEvent(in_eventTime, SE_KEY, posKey, qfalse, 0, NULL); // negative to positive/neutral -> keyup if (!negAnalog && negKey && oldAxis < 0 && axis >= 0) - Com_QueueEvent(0, SE_KEY, negKey, qfalse, 0, NULL); + Com_QueueEvent(in_eventTime, SE_KEY, negKey, qfalse, 0, NULL); // negative/neutral to positive -> keydown if (!posAnalog && posKey && oldAxis <= 0 && axis > 0) - Com_QueueEvent(0, SE_KEY, posKey, qtrue, 0, NULL); + Com_QueueEvent(in_eventTime, SE_KEY, posKey, qtrue, 0, NULL); // positive/neutral to negative -> keydown if (!negAnalog && negKey && oldAxis >= 0 && axis < 0) - Com_QueueEvent(0, SE_KEY, negKey, qtrue, 0, NULL); + Com_QueueEvent(in_eventTime, SE_KEY, negKey, qtrue, 0, NULL); stick_state.oldaaxes[i] = axis; } @@ -753,7 +755,7 @@ static void IN_GamepadMove( void ) for (i = 0; i < MAX_JOYSTICK_AXIS; i++) { if (translatedAxesSet[i]) - Com_QueueEvent(0, SE_JOYSTICK_AXIS, i, translatedAxes[i], 0, NULL); + Com_QueueEvent(in_eventTime, SE_JOYSTICK_AXIS, i, translatedAxes[i], 0, NULL); } } } @@ -804,7 +806,7 @@ static void IN_JoyMove( void ) balldx *= 2; if (abs(balldy) > 1) balldy *= 2; - Com_QueueEvent( 0, SE_MOUSE, balldx, balldy, 0, NULL ); + Com_QueueEvent( in_eventTime, SE_MOUSE, balldx, balldy, 0, NULL ); } } @@ -819,7 +821,7 @@ static void IN_JoyMove( void ) qboolean pressed = (SDL_JoystickGetButton(stick, i) != 0); if (pressed != stick_state.buttons[i]) { - Com_QueueEvent( 0, SE_KEY, K_JOY1 + i, pressed, 0, NULL ); + Com_QueueEvent( in_eventTime, SE_KEY, K_JOY1 + i, pressed, 0, NULL ); stick_state.buttons[i] = pressed; } } @@ -844,32 +846,32 @@ static void IN_JoyMove( void ) // release event switch( ((Uint8 *)&stick_state.oldhats)[i] ) { case SDL_HAT_UP: - Com_QueueEvent( 0, SE_KEY, hat_keys[4*i + 0], qfalse, 0, NULL ); + Com_QueueEvent( in_eventTime, SE_KEY, hat_keys[4*i + 0], qfalse, 0, NULL ); break; case SDL_HAT_RIGHT: - Com_QueueEvent( 0, SE_KEY, hat_keys[4*i + 1], qfalse, 0, NULL ); + Com_QueueEvent( in_eventTime, SE_KEY, hat_keys[4*i + 1], qfalse, 0, NULL ); break; case SDL_HAT_DOWN: - Com_QueueEvent( 0, SE_KEY, hat_keys[4*i + 2], qfalse, 0, NULL ); + Com_QueueEvent( in_eventTime, SE_KEY, hat_keys[4*i + 2], qfalse, 0, NULL ); break; case SDL_HAT_LEFT: - Com_QueueEvent( 0, SE_KEY, hat_keys[4*i + 3], qfalse, 0, NULL ); + Com_QueueEvent( in_eventTime, SE_KEY, hat_keys[4*i + 3], qfalse, 0, NULL ); break; case SDL_HAT_RIGHTUP: - Com_QueueEvent( 0, SE_KEY, hat_keys[4*i + 0], qfalse, 0, NULL ); - Com_QueueEvent( 0, SE_KEY, hat_keys[4*i + 1], qfalse, 0, NULL ); + Com_QueueEvent( in_eventTime, SE_KEY, hat_keys[4*i + 0], qfalse, 0, NULL ); + Com_QueueEvent( in_eventTime, SE_KEY, hat_keys[4*i + 1], qfalse, 0, NULL ); break; case SDL_HAT_RIGHTDOWN: - Com_QueueEvent( 0, SE_KEY, hat_keys[4*i + 2], qfalse, 0, NULL ); - Com_QueueEvent( 0, SE_KEY, hat_keys[4*i + 1], qfalse, 0, NULL ); + Com_QueueEvent( in_eventTime, SE_KEY, hat_keys[4*i + 2], qfalse, 0, NULL ); + Com_QueueEvent( in_eventTime, SE_KEY, hat_keys[4*i + 1], qfalse, 0, NULL ); break; case SDL_HAT_LEFTUP: - Com_QueueEvent( 0, SE_KEY, hat_keys[4*i + 0], qfalse, 0, NULL ); - Com_QueueEvent( 0, SE_KEY, hat_keys[4*i + 3], qfalse, 0, NULL ); + Com_QueueEvent( in_eventTime, SE_KEY, hat_keys[4*i + 0], qfalse, 0, NULL ); + Com_QueueEvent( in_eventTime, SE_KEY, hat_keys[4*i + 3], qfalse, 0, NULL ); break; case SDL_HAT_LEFTDOWN: - Com_QueueEvent( 0, SE_KEY, hat_keys[4*i + 2], qfalse, 0, NULL ); - Com_QueueEvent( 0, SE_KEY, hat_keys[4*i + 3], qfalse, 0, NULL ); + Com_QueueEvent( in_eventTime, SE_KEY, hat_keys[4*i + 2], qfalse, 0, NULL ); + Com_QueueEvent( in_eventTime, SE_KEY, hat_keys[4*i + 3], qfalse, 0, NULL ); break; default: break; @@ -877,32 +879,32 @@ static void IN_JoyMove( void ) // press event switch( ((Uint8 *)&hats)[i] ) { case SDL_HAT_UP: - Com_QueueEvent( 0, SE_KEY, hat_keys[4*i + 0], qtrue, 0, NULL ); + Com_QueueEvent( in_eventTime, SE_KEY, hat_keys[4*i + 0], qtrue, 0, NULL ); break; case SDL_HAT_RIGHT: - Com_QueueEvent( 0, SE_KEY, hat_keys[4*i + 1], qtrue, 0, NULL ); + Com_QueueEvent( in_eventTime, SE_KEY, hat_keys[4*i + 1], qtrue, 0, NULL ); break; case SDL_HAT_DOWN: - Com_QueueEvent( 0, SE_KEY, hat_keys[4*i + 2], qtrue, 0, NULL ); + Com_QueueEvent( in_eventTime, SE_KEY, hat_keys[4*i + 2], qtrue, 0, NULL ); break; case SDL_HAT_LEFT: - Com_QueueEvent( 0, SE_KEY, hat_keys[4*i + 3], qtrue, 0, NULL ); + Com_QueueEvent( in_eventTime, SE_KEY, hat_keys[4*i + 3], qtrue, 0, NULL ); break; case SDL_HAT_RIGHTUP: - Com_QueueEvent( 0, SE_KEY, hat_keys[4*i + 0], qtrue, 0, NULL ); - Com_QueueEvent( 0, SE_KEY, hat_keys[4*i + 1], qtrue, 0, NULL ); + Com_QueueEvent( in_eventTime, SE_KEY, hat_keys[4*i + 0], qtrue, 0, NULL ); + Com_QueueEvent( in_eventTime, SE_KEY, hat_keys[4*i + 1], qtrue, 0, NULL ); break; case SDL_HAT_RIGHTDOWN: - Com_QueueEvent( 0, SE_KEY, hat_keys[4*i + 2], qtrue, 0, NULL ); - Com_QueueEvent( 0, SE_KEY, hat_keys[4*i + 1], qtrue, 0, NULL ); + Com_QueueEvent( in_eventTime, SE_KEY, hat_keys[4*i + 2], qtrue, 0, NULL ); + Com_QueueEvent( in_eventTime, SE_KEY, hat_keys[4*i + 1], qtrue, 0, NULL ); break; case SDL_HAT_LEFTUP: - Com_QueueEvent( 0, SE_KEY, hat_keys[4*i + 0], qtrue, 0, NULL ); - Com_QueueEvent( 0, SE_KEY, hat_keys[4*i + 3], qtrue, 0, NULL ); + Com_QueueEvent( in_eventTime, SE_KEY, hat_keys[4*i + 0], qtrue, 0, NULL ); + Com_QueueEvent( in_eventTime, SE_KEY, hat_keys[4*i + 3], qtrue, 0, NULL ); break; case SDL_HAT_LEFTDOWN: - Com_QueueEvent( 0, SE_KEY, hat_keys[4*i + 2], qtrue, 0, NULL ); - Com_QueueEvent( 0, SE_KEY, hat_keys[4*i + 3], qtrue, 0, NULL ); + Com_QueueEvent( in_eventTime, SE_KEY, hat_keys[4*i + 2], qtrue, 0, NULL ); + Com_QueueEvent( in_eventTime, SE_KEY, hat_keys[4*i + 3], qtrue, 0, NULL ); break; default: break; @@ -930,7 +932,7 @@ static void IN_JoyMove( void ) if ( axis != stick_state.oldaaxes[i] ) { - Com_QueueEvent( 0, SE_JOYSTICK_AXIS, i, axis, 0, NULL ); + Com_QueueEvent( in_eventTime, SE_JOYSTICK_AXIS, i, axis, 0, NULL ); stick_state.oldaaxes[i] = axis; } } @@ -956,11 +958,11 @@ static void IN_JoyMove( void ) { for( i = 0; i < 16; i++ ) { if( ( axes & ( 1 << i ) ) && !( stick_state.oldaxes & ( 1 << i ) ) ) { - Com_QueueEvent( 0, SE_KEY, joy_keys[i], qtrue, 0, NULL ); + Com_QueueEvent( in_eventTime, SE_KEY, joy_keys[i], qtrue, 0, NULL ); } if( !( axes & ( 1 << i ) ) && ( stick_state.oldaxes & ( 1 << i ) ) ) { - Com_QueueEvent( 0, SE_KEY, joy_keys[i], qfalse, 0, NULL ); + Com_QueueEvent( in_eventTime, SE_KEY, joy_keys[i], qfalse, 0, NULL ); } } } @@ -992,19 +994,19 @@ static void IN_ProcessEvents( void ) break; if( ( key = IN_TranslateSDLToQ3Key( &e.key.keysym, qtrue ) ) ) - Com_QueueEvent( 0, SE_KEY, key, qtrue, 0, NULL ); + Com_QueueEvent( in_eventTime, SE_KEY, key, qtrue, 0, NULL ); if( key == K_BACKSPACE ) - Com_QueueEvent( 0, SE_CHAR, CTRL('h'), 0, 0, NULL ); + Com_QueueEvent( in_eventTime, SE_CHAR, CTRL('h'), 0, 0, NULL ); else if( keys[K_CTRL].down && key >= 'a' && key <= 'z' ) - Com_QueueEvent( 0, SE_CHAR, CTRL(key), 0, 0, NULL ); + Com_QueueEvent( in_eventTime, SE_CHAR, CTRL(key), 0, 0, NULL ); lastKeyDown = key; break; case SDL_KEYUP: if( ( key = IN_TranslateSDLToQ3Key( &e.key.keysym, qfalse ) ) ) - Com_QueueEvent( 0, SE_KEY, key, qfalse, 0, NULL ); + Com_QueueEvent( in_eventTime, SE_KEY, key, qfalse, 0, NULL ); lastKeyDown = 0; break; @@ -1049,11 +1051,11 @@ static void IN_ProcessEvents( void ) { if( IN_IsConsoleKey( 0, utf32 ) ) { - Com_QueueEvent( 0, SE_KEY, K_CONSOLE, qtrue, 0, NULL ); - Com_QueueEvent( 0, SE_KEY, K_CONSOLE, qfalse, 0, NULL ); + Com_QueueEvent( in_eventTime, SE_KEY, K_CONSOLE, qtrue, 0, NULL ); + Com_QueueEvent( in_eventTime, SE_KEY, K_CONSOLE, qfalse, 0, NULL ); } else - Com_QueueEvent( 0, SE_CHAR, utf32, 0, 0, NULL ); + Com_QueueEvent( in_eventTime, SE_CHAR, utf32, 0, 0, NULL ); } } } @@ -1064,7 +1066,7 @@ static void IN_ProcessEvents( void ) { if( !e.motion.xrel && !e.motion.yrel ) break; - Com_QueueEvent( 0, SE_MOUSE, e.motion.xrel, e.motion.yrel, 0, NULL ); + Com_QueueEvent( in_eventTime, SE_MOUSE, e.motion.xrel, e.motion.yrel, 0, NULL ); } break; @@ -1081,7 +1083,7 @@ static void IN_ProcessEvents( void ) case SDL_BUTTON_X2: b = K_MOUSE5; break; default: b = K_AUX1 + ( e.button.button - SDL_BUTTON_X2 + 1 ) % 16; break; } - Com_QueueEvent( 0, SE_KEY, b, + Com_QueueEvent( in_eventTime, SE_KEY, b, ( e.type == SDL_MOUSEBUTTONDOWN ? qtrue : qfalse ), 0, NULL ); } break; @@ -1089,13 +1091,13 @@ static void IN_ProcessEvents( void ) case SDL_MOUSEWHEEL: if( e.wheel.y > 0 ) { - Com_QueueEvent( 0, SE_KEY, K_MWHEELUP, qtrue, 0, NULL ); - Com_QueueEvent( 0, SE_KEY, K_MWHEELUP, qfalse, 0, NULL ); + Com_QueueEvent( in_eventTime, SE_KEY, K_MWHEELUP, qtrue, 0, NULL ); + Com_QueueEvent( in_eventTime, SE_KEY, K_MWHEELUP, qfalse, 0, NULL ); } else if( e.wheel.y < 0 ) { - Com_QueueEvent( 0, SE_KEY, K_MWHEELDOWN, qtrue, 0, NULL ); - Com_QueueEvent( 0, SE_KEY, K_MWHEELDOWN, qfalse, 0, NULL ); + Com_QueueEvent( in_eventTime, SE_KEY, K_MWHEELDOWN, qtrue, 0, NULL ); + Com_QueueEvent( in_eventTime, SE_KEY, K_MWHEELDOWN, qfalse, 0, NULL ); } break; @@ -1184,6 +1186,9 @@ void IN_Frame( void ) IN_ProcessEvents( ); + // Set event time for next frame to earliest possible time an event could happen + in_eventTime = Sys_Milliseconds( ); + // In case we had to delay actual restart of video system if( ( vidRestartTime != 0 ) && ( vidRestartTime < Sys_Milliseconds( ) ) ) { From ad1d0e616e4a4702505ee56fb288121c2c1f7a8f Mon Sep 17 00:00:00 2001 From: Zack Middleton Date: Sat, 26 Aug 2017 23:21:46 -0500 Subject: [PATCH 161/240] Make bots use crusher on other q3tourney6 maps Make bots activate crusher on q3tourney6_ctf and mpq3tourney6. Check if player is inside the crush bounds instead of below the crusher. There is a jumppad the goes under it in mpq3tourney6. --- code/game/ai_dmq3.c | 21 +++++++++++---------- 1 file changed, 11 insertions(+), 10 deletions(-) diff --git a/code/game/ai_dmq3.c b/code/game/ai_dmq3.c index 6e79665681..1a6a665392 100644 --- a/code/game/ai_dmq3.c +++ b/code/game/ai_dmq3.c @@ -3695,21 +3695,26 @@ void BotMapScripts(bot_state_t *bs) { strncpy(mapname, Info_ValueForKey( info, "mapname" ), sizeof(mapname)-1); mapname[sizeof(mapname)-1] = '\0'; - if (!Q_stricmp(mapname, "q3tourney6")) { - vec3_t mins = {700, 204, 672}, maxs = {964, 468, 680}; + if (!Q_stricmp(mapname, "q3tourney6") || !Q_stricmp(mapname, "q3tourney6_ctf") || !Q_stricmp(mapname, "mpq3tourney6")) { + vec3_t mins = {694, 200, 480}, maxs = {968, 472, 680}; vec3_t buttonorg = {304, 352, 920}; //NOTE: NEVER use the func_bobbing in q3tourney6 bs->tfl &= ~TFL_FUNCBOB; - //if the bot is below the bounding box + //crush area is higher in mpq3tourney6 + if (!Q_stricmp(mapname, "mpq3tourney6")) { + mins[2] += 64; + maxs[2] += 64; + } + //if the bot is in the bounding box of the crush area if (bs->origin[0] > mins[0] && bs->origin[0] < maxs[0]) { if (bs->origin[1] > mins[1] && bs->origin[1] < maxs[1]) { - if (bs->origin[2] < mins[2]) { + if (bs->origin[2] > mins[2] && bs->origin[2] < maxs[2]) { return; } } } shootbutton = qfalse; - //if an enemy is below this bounding box then shoot the button + //if an enemy is in the bounding box then shoot the button for (i = 0; i < level.maxclients; i++) { if (i == bs->client) continue; @@ -3722,7 +3727,7 @@ void BotMapScripts(bot_state_t *bs) { // if (entinfo.origin[0] > mins[0] && entinfo.origin[0] < maxs[0]) { if (entinfo.origin[1] > mins[1] && entinfo.origin[1] < maxs[1]) { - if (entinfo.origin[2] < mins[2]) { + if (entinfo.origin[2] > mins[2] && entinfo.origin[2] < maxs[2]) { //if there's a team mate below the crusher if (BotSameTeam(bs, i)) { shootbutton = qfalse; @@ -3750,10 +3755,6 @@ void BotMapScripts(bot_state_t *bs) { } } } - else if (!Q_stricmp(mapname, "mpq3tourney6")) { - //NOTE: NEVER use the func_bobbing in mpq3tourney6 - bs->tfl &= ~TFL_FUNCBOB; - } } /* From c3e64d380685419f96f71035c961907b9dc09a9a Mon Sep 17 00:00:00 2001 From: Zack Middleton Date: Sat, 26 Aug 2017 23:22:54 -0500 Subject: [PATCH 162/240] Make bots only use q3tourney6 crusher to kill their enemy Only use the crusher for killing the bot's enemy. This doesn't affect 1v1 very much but prevents the whole team of bots in CTF from suddenly shooting at the crusher button. Entering the crusher bounds was basically instant death. --- code/game/ai_dmq3.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/code/game/ai_dmq3.c b/code/game/ai_dmq3.c index 1a6a665392..fdd598ad59 100644 --- a/code/game/ai_dmq3.c +++ b/code/game/ai_dmq3.c @@ -3733,7 +3733,7 @@ void BotMapScripts(bot_state_t *bs) { shootbutton = qfalse; break; } - else { + else if (bs->enemy == i) { shootbutton = qtrue; } } From f4d68590c24c93f53458cea581b3504970d0bcf2 Mon Sep 17 00:00:00 2001 From: Zack Middleton Date: Sat, 2 Sep 2017 17:31:01 -0500 Subject: [PATCH 163/240] Fix going to previous browser source in q3_ui It wasn't possible to go to previous source from favorites if sv_master5 cvar wasn't set. --- code/q3_ui/ui_servers2.c | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/code/q3_ui/ui_servers2.c b/code/q3_ui/ui_servers2.c index 93e4697ba1..e709925e44 100644 --- a/code/q3_ui/ui_servers2.c +++ b/code/q3_ui/ui_servers2.c @@ -1102,15 +1102,22 @@ int ArenaServers_SetType( int type ) if(type >= UIAS_GLOBAL1 && type <= UIAS_GLOBAL5) { char masterstr[2], cvarname[sizeof("sv_master1")]; + int direction; - while(type <= UIAS_GLOBAL5) + if (type == g_servertype || type == ((g_servertype+1) % (ARRAY_LEN(master_items)-1))) { + direction = 1; + } else { + direction = -1; + } + + while(type >= UIAS_GLOBAL1 && type <= UIAS_GLOBAL5) { Com_sprintf(cvarname, sizeof(cvarname), "sv_master%d", type - UIAS_GLOBAL0); trap_Cvar_VariableStringBuffer(cvarname, masterstr, sizeof(masterstr)); if(*masterstr) break; - type++; + type += direction; } } From 2c22ead0782648e1ff2d5d379d5ae021ea33389c Mon Sep 17 00:00:00 2001 From: Zack Middleton Date: Sat, 2 Sep 2017 17:35:09 -0500 Subject: [PATCH 164/240] Make map names in q3_ui map select menu be uppercase Makes map names match on both pages of start server menu. --- code/q3_ui/ui_startserver.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/code/q3_ui/ui_startserver.c b/code/q3_ui/ui_startserver.c index b4883d4d49..505f877fa0 100644 --- a/code/q3_ui/ui_startserver.c +++ b/code/q3_ui/ui_startserver.c @@ -331,6 +331,7 @@ static void StartServer_LevelshotDraw( void *self ) { int h; int n; const char *info; + char mapname[ MAX_NAMELENGTH ]; b = (menubitmap_s *)self; @@ -366,7 +367,9 @@ static void StartServer_LevelshotDraw( void *self ) { n = s_startserver.page * MAX_MAPSPERPAGE + b->generic.id - ID_PICTURES; info = UI_GetArenaInfoByNumber( s_startserver.maplist[ n ]); - UI_DrawString( x, y, Info_ValueForKey( info, "map" ), UI_CENTER|UI_SMALLFONT, color_orange ); + Q_strncpyz( mapname, Info_ValueForKey( info, "map"), MAX_NAMELENGTH ); + Q_strupr( mapname ); + UI_DrawString( x, y, mapname, UI_CENTER|UI_SMALLFONT, color_orange ); x = b->generic.x; y = b->generic.y; From 5d88c6fdeef5cd85aae20c7878cc2f39faf4e91d Mon Sep 17 00:00:00 2001 From: Zack Middleton Date: Sat, 2 Sep 2017 17:39:45 -0500 Subject: [PATCH 165/240] Limit ui_smallFont/ui_bigFont/cg_noTaunt cvars to missionpack ui_smallFont and ui_bigFont are for missionpack HUD font size selection. cg_noTaunt disables missionpack taunt voice chats. --- code/cgame/cg_local.h | 2 ++ code/cgame/cg_main.c | 8 ++++++-- 2 files changed, 8 insertions(+), 2 deletions(-) diff --git a/code/cgame/cg_local.h b/code/cgame/cg_local.h index 822c257669..1800bda41b 100644 --- a/code/cgame/cg_local.h +++ b/code/cgame/cg_local.h @@ -1167,9 +1167,11 @@ extern vmCvar_t cg_timescaleFadeEnd; extern vmCvar_t cg_timescaleFadeSpeed; extern vmCvar_t cg_timescale; extern vmCvar_t cg_cameraMode; +#ifdef MISSIONPACK extern vmCvar_t cg_smallFont; extern vmCvar_t cg_bigFont; extern vmCvar_t cg_noTaunt; +#endif extern vmCvar_t cg_noProjectileTrail; extern vmCvar_t cg_oldRail; extern vmCvar_t cg_oldRocket; diff --git a/code/cgame/cg_main.c b/code/cgame/cg_main.c index 780ed3e771..8da095ab8e 100644 --- a/code/cgame/cg_main.c +++ b/code/cgame/cg_main.c @@ -175,9 +175,11 @@ vmCvar_t cg_cameraOrbitDelay; vmCvar_t cg_timescaleFadeEnd; vmCvar_t cg_timescaleFadeSpeed; vmCvar_t cg_timescale; +#ifdef MISSIONPACK vmCvar_t cg_smallFont; vmCvar_t cg_bigFont; vmCvar_t cg_noTaunt; +#endif vmCvar_t cg_noProjectileTrail; vmCvar_t cg_oldRail; vmCvar_t cg_oldRocket; @@ -309,10 +311,12 @@ static cvarTable_t cvarTable[] = { { &pmove_fixed, "pmove_fixed", "0", CVAR_SYSTEMINFO}, { &pmove_msec, "pmove_msec", "8", CVAR_SYSTEMINFO}, - { &cg_noTaunt, "cg_noTaunt", "0", CVAR_ARCHIVE}, - { &cg_noProjectileTrail, "cg_noProjectileTrail", "0", CVAR_ARCHIVE}, +#ifdef MISSIONPACK { &cg_smallFont, "ui_smallFont", "0.25", CVAR_ARCHIVE}, { &cg_bigFont, "ui_bigFont", "0.4", CVAR_ARCHIVE}, + { &cg_noTaunt, "cg_noTaunt", "0", CVAR_ARCHIVE}, +#endif + { &cg_noProjectileTrail, "cg_noProjectileTrail", "0", CVAR_ARCHIVE}, { &cg_oldRail, "cg_oldRail", "1", CVAR_ARCHIVE}, { &cg_oldRocket, "cg_oldRocket", "1", CVAR_ARCHIVE}, { &cg_oldPlasma, "cg_oldPlasma", "1", CVAR_ARCHIVE}, From e152761c284259d3a8a3728d60705f3aa3480f54 Mon Sep 17 00:00:00 2001 From: Zack Middleton Date: Sat, 2 Sep 2017 17:57:24 -0500 Subject: [PATCH 166/240] Fix team chat box for spectators Make spectators use green background for team chat box when following players. The team chat messages are from spectators not the team of the followed player. Make spectators draw team chat box even when not following a player. --- code/cgame/cg_draw.c | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/code/cgame/cg_draw.c b/code/cgame/cg_draw.c index 9566682229..afe0941201 100644 --- a/code/cgame/cg_draw.c +++ b/code/cgame/cg_draw.c @@ -1374,12 +1374,12 @@ static void CG_DrawTeamInfo( void ) { h = (cgs.teamChatPos - cgs.teamLastChatPos) * TINYCHAR_HEIGHT; - if ( cg.snap->ps.persistant[PERS_TEAM] == TEAM_RED ) { + if ( cgs.clientinfo[cg.clientNum].team == TEAM_RED ) { hcolor[0] = 1.0f; hcolor[1] = 0.0f; hcolor[2] = 0.0f; hcolor[3] = 0.33f; - } else if ( cg.snap->ps.persistant[PERS_TEAM] == TEAM_BLUE ) { + } else if ( cgs.clientinfo[cg.clientNum].team == TEAM_BLUE ) { hcolor[0] = 0.0f; hcolor[1] = 0.0f; hcolor[2] = 1.0f; @@ -2582,12 +2582,12 @@ static void CG_Draw2D(stereoFrame_t stereoFrame) #endif CG_DrawReward(); } - - if ( cgs.gametype >= GT_TEAM ) { + } + + if ( cgs.gametype >= GT_TEAM ) { #ifndef MISSIONPACK - CG_DrawTeamInfo(); + CG_DrawTeamInfo(); #endif - } } CG_DrawVote(); From 8192f66b392893c3880a2175bb2b9ebefd0854b6 Mon Sep 17 00:00:00 2001 From: Zack Middleton Date: Sat, 2 Sep 2017 18:11:01 -0500 Subject: [PATCH 167/240] Don't draw crosshair 0 in Team Arena setup menu Don't draw crosshair 0 in Team Arena setup menu. It isn't drawn in CGame. Wrap cg_drawCrosshair in missionpack menu like CGame instead of treating as index 0 which is no longer drawn. --- code/ui/ui_main.c | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/code/ui/ui_main.c b/code/ui/ui_main.c index 73d3f60a8e..05d9bbc60a 100644 --- a/code/ui/ui_main.c +++ b/code/ui/ui_main.c @@ -1777,10 +1777,10 @@ static void UI_DrawRedBlue(rectDef_t *rect, float scale, vec4_t color, int textS } static void UI_DrawCrosshair(rectDef_t *rect, float scale, vec4_t color) { - trap_R_SetColor( color ); - if (uiInfo.currentCrosshair < 0 || uiInfo.currentCrosshair >= NUM_CROSSHAIRS) { - uiInfo.currentCrosshair = 0; + if (!uiInfo.currentCrosshair) { + return; } + trap_R_SetColor( color ); UI_DrawHandlePic( rect->x, rect->y - rect->h, rect->w, rect->h, uiInfo.uiDC.Assets.crosshairShader[uiInfo.currentCrosshair]); trap_R_SetColor( NULL ); } @@ -5192,7 +5192,10 @@ void _UI_Init( qboolean inGameLoad ) { // sets defaults for ui temp cvars uiInfo.effectsColor = gamecodetoui[(int)trap_Cvar_VariableValue("color1")-1]; - uiInfo.currentCrosshair = (int)trap_Cvar_VariableValue("cg_drawCrosshair"); + uiInfo.currentCrosshair = (int)trap_Cvar_VariableValue("cg_drawCrosshair") % NUM_CROSSHAIRS; + if (uiInfo.currentCrosshair < 0) { + uiInfo.currentCrosshair = 0; + } trap_Cvar_Set("ui_mousePitch", (trap_Cvar_VariableValue("m_pitch") >= 0) ? "0" : "1"); uiInfo.serverStatus.currentServerCinematic = -1; From c2ce1c2f512223f786a1f724cfcbf9f0efe35b8f Mon Sep 17 00:00:00 2001 From: Zack Middleton Date: Mon, 4 Sep 2017 20:02:17 -0500 Subject: [PATCH 168/240] Make client for Windows x86_64 use OpenAL64.dll by default ioquake3.x86_64.exe can't load x86 OpenAL32.dll. Using separate library names allows shipping OpenAL for both architectures. Though since the dll name is saved in the config file, using both clients on the same computer will cause one client arch to always try to load the wrong OpenAL dll and then fallback to the correct default OpenAL dll. I guess it could be fixed by using separate cvar names for s_alDriver. --- code/client/snd_openal.c | 4 +++- misc/nsis/Makefile | 9 ++++++++- 2 files changed, 11 insertions(+), 2 deletions(-) diff --git a/code/client/snd_openal.c b/code/client/snd_openal.c index dc1d817dff..5ba5c7c659 100644 --- a/code/client/snd_openal.c +++ b/code/client/snd_openal.c @@ -2206,7 +2206,9 @@ static ALCdevice *alCaptureDevice; static cvar_t *s_alCapture; #endif -#ifdef _WIN32 +#if defined(_WIN64) +#define ALDRIVER_DEFAULT "OpenAL64.dll" +#elif defined(_WIN32) #define ALDRIVER_DEFAULT "OpenAL32.dll" #elif defined(__APPLE__) #define ALDRIVER_DEFAULT "/System/Library/Frameworks/OpenAL.framework/OpenAL" diff --git a/misc/nsis/Makefile b/misc/nsis/Makefile index d56fbdec79..3875e4e502 100644 --- a/misc/nsis/Makefile +++ b/misc/nsis/Makefile @@ -38,6 +38,13 @@ ifndef SDLDLL SDLDLL=SDL2.dll endif endif +ifndef OPENALDLL + ifeq ($(ARCH),x86_64) + OPENALDLL=OpenAL64.dll + else + OPENALDLL=OpenAL32.dll + endif +endif DEFINES= ifeq ($(USE_RENDERER_DLOPEN),1) @@ -63,7 +70,7 @@ endif all: ioquake3-$(VERSION)-$(RELEASE).$(ARCH).exe ioquake3.$(ARCH).nsi: ioquake3.nsi.in - sed 's/XXXVERSIONXXX/$(VERSION)/;s/XXXRELEASEXXX/$(RELEASE)/;s/mingw32/$(PLATFORM)/g;s/x86/$(ARCH)/g;s/SDL2.dll/$(SDLDLL)/g' < $< > $@ + sed 's/XXXVERSIONXXX/$(VERSION)/;s/XXXRELEASEXXX/$(RELEASE)/;s/mingw32/$(PLATFORM)/g;s/x86/$(ARCH)/g;s/SDL2.dll/$(SDLDLL)/g;s/OpenAL32.dll/$(OPENALDLL)/g' < $< > $@ ioquake3-$(VERSION)-$(RELEASE).$(ARCH).exe: ioquake3.$(ARCH).nsi makensis $(DEFINES) ioquake3.$(ARCH).nsi From 6f0736ce9a29ab402b5b0e884055eb81e47e4a4b Mon Sep 17 00:00:00 2001 From: Zack Middleton Date: Thu, 7 Sep 2017 20:38:10 -0500 Subject: [PATCH 169/240] Fix loading renderer DLLs on Windows x86 After 'Fix floating point precision loss in renderer', Windows x86 client won't load the renderer DLLs. The problem is a 64 bit modulus. I couldn't find any reports of this online. However, client with built-in renderer worked with the 64 bit modulus. Only tested with mingw-w64. --- code/renderergl1/tr_shade.c | 7 ++++++- code/renderergl2/tr_shade.c | 7 ++++++- 2 files changed, 12 insertions(+), 2 deletions(-) diff --git a/code/renderergl1/tr_shade.c b/code/renderergl1/tr_shade.c index 61a6e0b1ed..57a43c5a5d 100644 --- a/code/renderergl1/tr_shade.c +++ b/code/renderergl1/tr_shade.c @@ -239,7 +239,12 @@ static void R_BindAnimatedImage( textureBundle_t *bundle ) { if ( index < 0 ) { index = 0; // may happen with shader time offsets } - index %= bundle->numImageAnimations; + + // Windows x86 doesn't load renderer DLL with 64 bit modulus + //index %= bundle->numImageAnimations; + while ( index >= bundle->numImageAnimations ) { + index -= bundle->numImageAnimations; + } GL_Bind( bundle->image[ index ] ); } diff --git a/code/renderergl2/tr_shade.c b/code/renderergl2/tr_shade.c index b0ca8628ad..0366247202 100644 --- a/code/renderergl2/tr_shade.c +++ b/code/renderergl2/tr_shade.c @@ -87,7 +87,12 @@ static void R_BindAnimatedImageToTMU( textureBundle_t *bundle, int tmu ) { if ( index < 0 ) { index = 0; // may happen with shader time offsets } - index %= bundle->numImageAnimations; + + // Windows x86 doesn't load renderer DLL with 64 bit modulus + //index %= bundle->numImageAnimations; + while ( index >= bundle->numImageAnimations ) { + index -= bundle->numImageAnimations; + } GL_BindToTMU( bundle->image[ index ], tmu ); } From aeaecb4ae581c25b7628ccde0303dbc4a5ea11e1 Mon Sep 17 00:00:00 2001 From: Zack Middleton Date: Thu, 7 Sep 2017 21:01:23 -0500 Subject: [PATCH 170/240] Add Windows application manifest - Use common controls 6 so error dialogs use correct visuals on Windows XP or later! - Specify running as invoker so Windows doesn't guess if it should prompt for admin permission on Vista or later. - Specify compatible with Vista through Windows 10. Tells Windows not to emulate Vista behavior, not sure if it affects anything. Makefile automatically runs windres when manifest changes. --- Makefile | 4 ++-- code/sys/win_manifest.xml | 31 +++++++++++++++++++++++++++++++ code/sys/win_resource.rc | 6 ++++++ 3 files changed, 39 insertions(+), 2 deletions(-) create mode 100644 code/sys/win_manifest.xml diff --git a/Makefile b/Makefile index 45339aab9b..44d2862c10 100644 --- a/Makefile +++ b/Makefile @@ -2665,7 +2665,7 @@ $(B)/client/%.o: $(SYSDIR)/%.c $(B)/client/%.o: $(SYSDIR)/%.m $(DO_CC) -$(B)/client/%.o: $(SYSDIR)/%.rc +$(B)/client/win_resource.o: $(SYSDIR)/win_resource.rc $(SYSDIR)/win_manifest.xml $(DO_WINDRES) @@ -2722,7 +2722,7 @@ $(B)/ded/%.o: $(SYSDIR)/%.c $(B)/ded/%.o: $(SYSDIR)/%.m $(DO_DED_CC) -$(B)/ded/%.o: $(SYSDIR)/%.rc +$(B)/ded/win_resource.o: $(SYSDIR)/win_resource.rc $(SYSDIR)/win_manifest.xml $(DO_WINDRES) $(B)/ded/%.o: $(NDIR)/%.c diff --git a/code/sys/win_manifest.xml b/code/sys/win_manifest.xml new file mode 100644 index 0000000000..2055dc9e8f --- /dev/null +++ b/code/sys/win_manifest.xml @@ -0,0 +1,31 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/code/sys/win_resource.rc b/code/sys/win_resource.rc index b1c39d500d..b9c8c6ea56 100644 --- a/code/sys/win_resource.rc +++ b/code/sys/win_resource.rc @@ -70,6 +70,12 @@ BEGIN IDS_STRING1 "Quake3" END +///////////////////////////////////////////////////////////////////////////// +// +// Application Manifest +// +CREATEPROCESS_MANIFEST_RESOURCE_ID RT_MANIFEST "win_manifest.xml" + #endif // English (U.S.) resources ///////////////////////////////////////////////////////////////////////////// From 5f743bdb229cc80e2ce3dc4b8b6c42ec3d552114 Mon Sep 17 00:00:00 2001 From: Zack Middleton Date: Thu, 7 Sep 2017 21:09:46 -0500 Subject: [PATCH 171/240] Disable DPI scaling on Windows Windows DPI scaling prevents using full monitor resolution in fullscreen mode. --- code/sys/win_manifest.xml | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/code/sys/win_manifest.xml b/code/sys/win_manifest.xml index 2055dc9e8f..dccbcf5645 100644 --- a/code/sys/win_manifest.xml +++ b/code/sys/win_manifest.xml @@ -28,4 +28,10 @@ + + + True/PM + PerMonitorV2, PerMonitor + + From e0a367451f04988790384fc59d998b6dcb6bf2e3 Mon Sep 17 00:00:00 2001 From: Eugene Molotov Date: Sun, 10 Sep 2017 14:31:51 +0500 Subject: [PATCH 172/240] ignore window resize event on fullscreen --- code/sdl/sdl_input.c | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/code/sdl/sdl_input.c b/code/sdl/sdl_input.c index 9abf1491ee..9514636daf 100644 --- a/code/sdl/sdl_input.c +++ b/code/sdl/sdl_input.c @@ -1121,6 +1121,12 @@ static void IN_ProcessEvents( void ) width = e.window.data1; height = e.window.data2; + // ignore this event on fullscreen + if( cls.glconfig.isFullscreen ) + { + break; + } + // check if size actually changed if( cls.glconfig.vidWidth == width && cls.glconfig.vidHeight == height ) { From dcf5707493f13191eb97bf5f200cc41bc3593f98 Mon Sep 17 00:00:00 2001 From: Zack Middleton Date: Sat, 9 Sep 2017 17:04:17 -0500 Subject: [PATCH 173/240] Don't reload arenas.txt/*.arena files in Team Arena UI Entering Team Arena's start server or in-game callvote menu causes arenas to be reloaded. The existing memory is not freed so after a entering the menus a few times the UI runs out of memory and crashes. Just load arenas once when the UI loads like in q3_ui. --- code/ui/ui_main.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/code/ui/ui_main.c b/code/ui/ui_main.c index 05d9bbc60a..bd8c515dd0 100644 --- a/code/ui/ui_main.c +++ b/code/ui/ui_main.c @@ -3263,7 +3263,6 @@ static void UI_RunMenuScript(char **args) { trap_Cvar_Set("ui_cdkeyvalid", "CD Key does not appear to be valid."); } } else if (Q_stricmp(name, "loadArenas") == 0) { - UI_LoadArenas(); UI_MapCountByGameType(qfalse); Menu_SetFeederSelection(NULL, FEEDER_ALLMAPS, 0, "createserver"); } else if (Q_stricmp(name, "saveControls") == 0) { @@ -5165,6 +5164,7 @@ void _UI_Init( qboolean inGameLoad ) { UI_ParseTeamInfo("teaminfo.txt"); UI_LoadTeams(); UI_ParseGameInfo("gameinfo.txt"); + UI_LoadArenas(); #endif menuSet = UI_Cvar_VariableString("ui_menuFiles"); From a48dcdf22472bf0c1206aefc3e01b6e22a49f3e6 Mon Sep 17 00:00:00 2001 From: Zack Middleton Date: Sat, 9 Sep 2017 17:11:26 -0500 Subject: [PATCH 174/240] Fix crash when out of memory in Team Arena's String_Alloc --- code/ui/ui_shared.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/code/ui/ui_shared.c b/code/ui/ui_shared.c index f26d11b837..e574f78df3 100644 --- a/code/ui/ui_shared.c +++ b/code/ui/ui_shared.c @@ -198,6 +198,9 @@ const char *String_Alloc(const char *p) { } str = UI_Alloc(sizeof(stringDef_t)); + if (!str) { + return NULL; + } str->next = NULL; str->str = &strPool[ph]; if (last) { From 098d97bdb0cd1d23a02b9c4489766364ac31fb0c Mon Sep 17 00:00:00 2001 From: Zack Middleton Date: Sun, 10 Sep 2017 18:27:50 -0500 Subject: [PATCH 175/240] Fix in_nograb not releasing the mouse cursor Disable SDL relative mouse mode when in_nograb is enabled. Relative mouse mode hides the cursor and it cannot exit the window regardless of the window's grab state. This wasn't always the case. SDL before 2.0.4 on GNU/Linux released the mouse cursor in relative mode. However, SDL 2.0.3/4 on Windows does not. (I did not test other Windows versions.) So I think SDL 2.0.4 made GNU/Linux X11 behavior consistent with other platforms. This fixes mouse input being unusable when debuging client crashes in gdb. --- code/sdl/sdl_input.c | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/code/sdl/sdl_input.c b/code/sdl/sdl_input.c index 9514636daf..6af6751858 100644 --- a/code/sdl/sdl_input.c +++ b/code/sdl/sdl_input.c @@ -360,10 +360,13 @@ static void IN_ActivateMouse( void ) { if( in_nograb->modified || !mouseActive ) { - if( in_nograb->integer ) + if( in_nograb->integer ) { + SDL_SetRelativeMouseMode( SDL_FALSE ); SDL_SetWindowGrab( SDL_window, SDL_FALSE ); - else + } else { + SDL_SetRelativeMouseMode( SDL_TRUE ); SDL_SetWindowGrab( SDL_window, SDL_TRUE ); + } in_nograb->modified = qfalse; } From 11b3bca5555e8dd1fb73081ae27f34c46edfed1e Mon Sep 17 00:00:00 2001 From: Zack Middleton Date: Sun, 10 Sep 2017 19:16:47 -0500 Subject: [PATCH 176/240] Update UI player animation handling to match CGame Fix "Error parsing animation file" messages in UI. Caused by fixing the handling of missing tokens in animation.cfg parser in a past commit. Fix new Team Arena torso animation frame numbers in UI. Add support for fixedtorso and fixedlegs keywords. Add support for reversed animations (negative numframes). --- code/q3_ui/ui_local.h | 3 ++ code/q3_ui/ui_players.c | 72 ++++++++++++++++++++++++++++++++++----- code/ui/ui_local.h | 3 ++ code/ui/ui_players.c | 74 ++++++++++++++++++++++++++++++++++++----- 4 files changed, 135 insertions(+), 17 deletions(-) diff --git a/code/q3_ui/ui_local.h b/code/q3_ui/ui_local.h index 72979a9e75..6bb0fb3172 100644 --- a/code/q3_ui/ui_local.h +++ b/code/q3_ui/ui_local.h @@ -486,6 +486,9 @@ typedef struct { animation_t animations[MAX_ANIMATIONS]; + qboolean fixedlegs; // true if legs yaw is always the same as torso yaw + qboolean fixedtorso; // true if torso never changes yaw + qhandle_t weaponModel; qhandle_t barrelModel; qhandle_t flashModel; diff --git a/code/q3_ui/ui_players.c b/code/q3_ui/ui_players.c index b40ec90f7d..cf2730a78b 100644 --- a/code/q3_ui/ui_players.c +++ b/code/q3_ui/ui_players.c @@ -363,7 +363,7 @@ UI_RunLerpFrame =============== */ static void UI_RunLerpFrame( playerInfo_t *ci, lerpFrame_t *lf, int newAnimation ) { - int f; + int f, numFrames; animation_t *anim; // see if the animation sequence is switching @@ -379,25 +379,41 @@ static void UI_RunLerpFrame( playerInfo_t *ci, lerpFrame_t *lf, int newAnimation // get the next frame based on the animation anim = lf->animation; + if ( !anim->frameLerp ) { + return; // shouldn't happen + } if ( dp_realtime < lf->animationTime ) { lf->frameTime = lf->animationTime; // initial lerp } else { lf->frameTime = lf->oldFrameTime + anim->frameLerp; } f = ( lf->frameTime - lf->animationTime ) / anim->frameLerp; - if ( f >= anim->numFrames ) { - f -= anim->numFrames; + + numFrames = anim->numFrames; + if (anim->flipflop) { + numFrames *= 2; + } + if ( f >= numFrames ) { + f -= numFrames; if ( anim->loopFrames ) { f %= anim->loopFrames; f += anim->numFrames - anim->loopFrames; } else { - f = anim->numFrames - 1; + f = numFrames - 1; // the animation is stuck at the end, so it // can immediately transition to another sequence lf->frameTime = dp_realtime; } } - lf->frame = anim->firstFrame + f; + if ( anim->reversed ) { + lf->frame = anim->firstFrame + anim->numFrames - 1 - f; + } + else if (anim->flipflop && f>=anim->numFrames) { + lf->frame = anim->firstFrame + anim->numFrames - 1 - (f%anim->numFrames); + } + else { + lf->frame = anim->firstFrame + f; + } if ( dp_realtime > lf->frameTime ) { lf->frameTime = dp_realtime; } @@ -615,6 +631,16 @@ static void UI_PlayerAngles( playerInfo_t *pi, vec3_t legs[3], vec3_t torso[3], UI_SwingAngles( dest, 15, 30, 0.1f, &pi->torso.pitchAngle, &pi->torso.pitching ); torsoAngles[PITCH] = pi->torso.pitchAngle; + if ( pi->fixedtorso ) { + torsoAngles[PITCH] = 0.0f; + } + + if ( pi->fixedlegs ) { + legsAngles[YAW] = torsoAngles[YAW]; + legsAngles[PITCH] = 0.0f; + legsAngles[ROLL] = 0.0f; + } + // pull the angles back out of the hierarchial chain AnglesSubtract( headAngles, torsoAngles, headAngles ); AnglesSubtract( torsoAngles, legsAngles, torsoAngles ); @@ -930,7 +956,7 @@ static qboolean UI_RegisterClientSkin( playerInfo_t *pi, const char *modelName, UI_ParseAnimationFile ====================== */ -static qboolean UI_ParseAnimationFile( const char *filename, animation_t *animations ) { +static qboolean UI_ParseAnimationFile( const char *filename, playerInfo_t *pi ) { char *text_p, *prev; int len; int i; @@ -939,9 +965,15 @@ static qboolean UI_ParseAnimationFile( const char *filename, animation_t *animat int skip; char text[20000]; fileHandle_t f; + animation_t *animations; + + animations = pi->animations; memset( animations, 0, sizeof( animation_t ) * MAX_ANIMATIONS ); + pi->fixedlegs = qfalse; + pi->fixedtorso = qfalse; + // load the file len = trap_FS_FOpenFile( filename, &f, FS_READ ); if ( len <= 0 ) { @@ -987,6 +1019,12 @@ static qboolean UI_ParseAnimationFile( const char *filename, animation_t *animat break; } continue; + } else if ( !Q_stricmp( token, "fixedlegs" ) ) { + pi->fixedlegs = qtrue; + continue; + } else if ( !Q_stricmp( token, "fixedtorso" ) ) { + pi->fixedtorso = qtrue; + continue; } // if it is a number, start parsing animations @@ -1003,6 +1041,16 @@ static qboolean UI_ParseAnimationFile( const char *filename, animation_t *animat token = COM_Parse( &text_p ); if ( !token[0] ) { + if( i >= TORSO_GETFLAG && i <= TORSO_NEGATIVE ) { + animations[i].firstFrame = animations[TORSO_GESTURE].firstFrame; + animations[i].frameLerp = animations[TORSO_GESTURE].frameLerp; + animations[i].initialLerp = animations[TORSO_GESTURE].initialLerp; + animations[i].loopFrames = animations[TORSO_GESTURE].loopFrames; + animations[i].numFrames = animations[TORSO_GESTURE].numFrames; + animations[i].reversed = qfalse; + animations[i].flipflop = qfalse; + continue; + } break; } animations[i].firstFrame = atoi( token ); @@ -1010,7 +1058,7 @@ static qboolean UI_ParseAnimationFile( const char *filename, animation_t *animat if ( i == LEGS_WALKCR ) { skip = animations[LEGS_WALKCR].firstFrame - animations[TORSO_GESTURE].firstFrame; } - if ( i >= LEGS_WALKCR ) { + if ( i >= LEGS_WALKCR && ianimations ) ) { + if ( !UI_ParseAnimationFile( filename, pi ) ) { Com_Printf( "Failed to load animation file %s\n", filename ); return qfalse; } diff --git a/code/ui/ui_local.h b/code/ui/ui_local.h index cb41362a24..f63548219a 100644 --- a/code/ui/ui_local.h +++ b/code/ui/ui_local.h @@ -533,6 +533,9 @@ typedef struct { animation_t animations[MAX_TOTALANIMATIONS]; + qboolean fixedlegs; // true if legs yaw is always the same as torso yaw + qboolean fixedtorso; // true if torso never changes yaw + qhandle_t weaponModel; qhandle_t barrelModel; qhandle_t flashModel; diff --git a/code/ui/ui_players.c b/code/ui/ui_players.c index 87812ef30d..7f7ff0e7b4 100644 --- a/code/ui/ui_players.c +++ b/code/ui/ui_players.c @@ -364,7 +364,7 @@ UI_RunLerpFrame =============== */ static void UI_RunLerpFrame( playerInfo_t *ci, lerpFrame_t *lf, int newAnimation ) { - int f; + int f, numFrames; animation_t *anim; // see if the animation sequence is switching @@ -380,25 +380,41 @@ static void UI_RunLerpFrame( playerInfo_t *ci, lerpFrame_t *lf, int newAnimation // get the next frame based on the animation anim = lf->animation; + if ( !anim->frameLerp ) { + return; // shouldn't happen + } if ( dp_realtime < lf->animationTime ) { lf->frameTime = lf->animationTime; // initial lerp } else { lf->frameTime = lf->oldFrameTime + anim->frameLerp; } f = ( lf->frameTime - lf->animationTime ) / anim->frameLerp; - if ( f >= anim->numFrames ) { - f -= anim->numFrames; + + numFrames = anim->numFrames; + if (anim->flipflop) { + numFrames *= 2; + } + if ( f >= numFrames ) { + f -= numFrames; if ( anim->loopFrames ) { f %= anim->loopFrames; f += anim->numFrames - anim->loopFrames; } else { - f = anim->numFrames - 1; + f = numFrames - 1; // the animation is stuck at the end, so it // can immediately transition to another sequence lf->frameTime = dp_realtime; } } - lf->frame = anim->firstFrame + f; + if ( anim->reversed ) { + lf->frame = anim->firstFrame + anim->numFrames - 1 - f; + } + else if (anim->flipflop && f>=anim->numFrames) { + lf->frame = anim->firstFrame + anim->numFrames - 1 - (f%anim->numFrames); + } + else { + lf->frame = anim->firstFrame + f; + } if ( dp_realtime > lf->frameTime ) { lf->frameTime = dp_realtime; } @@ -616,6 +632,16 @@ static void UI_PlayerAngles( playerInfo_t *pi, vec3_t legs[3], vec3_t torso[3], UI_SwingAngles( dest, 15, 30, 0.1f, &pi->torso.pitchAngle, &pi->torso.pitching ); torsoAngles[PITCH] = pi->torso.pitchAngle; + if ( pi->fixedtorso ) { + torsoAngles[PITCH] = 0.0f; + } + + if ( pi->fixedlegs ) { + legsAngles[YAW] = torsoAngles[YAW]; + legsAngles[PITCH] = 0.0f; + legsAngles[ROLL] = 0.0f; + } + // pull the angles back out of the hierarchial chain AnglesSubtract( headAngles, torsoAngles, headAngles ); AnglesSubtract( torsoAngles, legsAngles, torsoAngles ); @@ -1015,7 +1041,7 @@ static qboolean UI_RegisterClientSkin( playerInfo_t *pi, const char *modelName, UI_ParseAnimationFile ====================== */ -static qboolean UI_ParseAnimationFile( const char *filename, animation_t *animations ) { +static qboolean UI_ParseAnimationFile( const char *filename, playerInfo_t *pi ) { char *text_p, *prev; int len; int i; @@ -1024,9 +1050,15 @@ static qboolean UI_ParseAnimationFile( const char *filename, animation_t *animat int skip; char text[20000]; fileHandle_t f; + animation_t *animations; + + animations = pi->animations; memset( animations, 0, sizeof( animation_t ) * MAX_ANIMATIONS ); + pi->fixedlegs = qfalse; + pi->fixedtorso = qfalse; + // load the file len = trap_FS_FOpenFile( filename, &f, FS_READ ); if ( len <= 0 ) { @@ -1074,6 +1106,12 @@ static qboolean UI_ParseAnimationFile( const char *filename, animation_t *animat break; } continue; + } else if ( !Q_stricmp( token, "fixedlegs" ) ) { + pi->fixedlegs = qtrue; + continue; + } else if ( !Q_stricmp( token, "fixedtorso" ) ) { + pi->fixedtorso = qtrue; + continue; } // if it is a number, start parsing animations @@ -1090,6 +1128,16 @@ static qboolean UI_ParseAnimationFile( const char *filename, animation_t *animat token = COM_Parse( &text_p ); if ( !token[0] ) { + if( i >= TORSO_GETFLAG && i <= TORSO_NEGATIVE ) { + animations[i].firstFrame = animations[TORSO_GESTURE].firstFrame; + animations[i].frameLerp = animations[TORSO_GESTURE].frameLerp; + animations[i].initialLerp = animations[TORSO_GESTURE].initialLerp; + animations[i].loopFrames = animations[TORSO_GESTURE].loopFrames; + animations[i].numFrames = animations[TORSO_GESTURE].numFrames; + animations[i].reversed = qfalse; + animations[i].flipflop = qfalse; + continue; + } break; } animations[i].firstFrame = atoi( token ); @@ -1097,7 +1145,7 @@ static qboolean UI_ParseAnimationFile( const char *filename, animation_t *animat if ( i == LEGS_WALKCR ) { skip = animations[LEGS_WALKCR].firstFrame - animations[TORSO_GESTURE].firstFrame; } - if ( i >= LEGS_WALKCR ) { + if ( i >= LEGS_WALKCR && ianimations ) ) { + if ( !UI_ParseAnimationFile( filename, pi ) ) { Com_sprintf( filename, sizeof( filename ), "models/players/characters/%s/animation.cfg", modelName ); - if ( !UI_ParseAnimationFile( filename, pi->animations ) ) { + if ( !UI_ParseAnimationFile( filename, pi ) ) { Com_Printf( "Failed to load animation file %s\n", filename ); return qfalse; } From 0a599268bb757536d6a55eee30c08d51198dc1eb Mon Sep 17 00:00:00 2001 From: Zack Middleton Date: Thu, 14 Sep 2017 16:44:20 -0500 Subject: [PATCH 177/240] Fix specifying minimum mac os version in make-macosx.sh Use newer method of telling Makefile instead of specifying CFLAGS and LDFLAGS which results in using both Makefile's and scripts flags. --- make-macosx.sh | 14 ++++---------- 1 file changed, 4 insertions(+), 10 deletions(-) diff --git a/make-macosx.sh b/make-macosx.sh index 035b3998e1..91fecf198d 100755 --- a/make-macosx.sh +++ b/make-macosx.sh @@ -14,7 +14,6 @@ fi if [ "$1" == "x86" ]; then BUILDARCH=x86 - DARWIN_GCC_ARCH=i386 elif [ "$1" == "x86_64" ]; then BUILDARCH=x86_64 elif [ "$1" == "ppc" ]; then @@ -25,10 +24,6 @@ else exit 1 fi -if [ -z "$DARWIN_GCC_ARCH" ]; then - DARWIN_GCC_ARCH=${BUILDARCH} -fi - CC=gcc-4.0 DESTDIR=build/release-darwin-${BUILDARCH} @@ -46,13 +41,12 @@ fi unset ARCH_SDK unset ARCH_CFLAGS -unset ARCH_LDFLAGS +unset ARCH_MACOSX_VERSION_MIN if [ -d /Developer/SDKs/MacOSX10.5.sdk ]; then ARCH_SDK=/Developer/SDKs/MacOSX10.5.sdk - ARCH_CFLAGS="-arch ${DARWIN_GCC_ARCH} -isysroot /Developer/SDKs/MacOSX10.5.sdk \ - -DMAC_OS_X_VERSION_MIN_REQUIRED=1050" - ARCH_LDFLAGS=" -mmacosx-version-min=10.5" + ARCH_CFLAGS="-isysroot /Developer/SDKs/MacOSX10.5.sdk" + ARCH_MACOSX_VERSION_MIN="10.5" fi @@ -71,7 +65,7 @@ NCPU=`sysctl -n hw.ncpu` #if [ -d build/release-darwin-${BUILDARCH} ]; then # rm -r build/release-darwin-${BUILDARCH} #fi -(ARCH=${BUILDARCH} CFLAGS=$ARCH_CFLAGS LDFLAGS=$ARCH_LDFLAGS make -j$NCPU) || exit 1; +(ARCH=${BUILDARCH} CFLAGS=$ARCH_CFLAGS MACOSX_VERSION_MIN=$ARCH_MACOSX_VERSION_MIN make -j$NCPU) || exit 1; # use the following shell script to build an application bundle "./make-macosx-app.sh" release ${BUILDARCH} From c04cf19b77db5f95ffa633e77f8aa281edc92814 Mon Sep 17 00:00:00 2001 From: Zack Middleton Date: Thu, 14 Sep 2017 16:49:30 -0500 Subject: [PATCH 178/240] Fix minimum macOS version not being set in AppBundle Info.plist make-macosx-app.sh reads MACOSX_DEPLOYMENT_TARGET variable but it wasn't ever set. --- make-macosx-ub.sh | 1 + make-macosx.sh | 3 +++ 2 files changed, 4 insertions(+) diff --git a/make-macosx-ub.sh b/make-macosx-ub.sh index 6702ea941a..f359824a68 100755 --- a/make-macosx-ub.sh +++ b/make-macosx-ub.sh @@ -88,4 +88,5 @@ echo;echo echo # use the following shell script to build a universal application bundle +export MACOSX_DEPLOYMENT_TARGET="10.5" "./make-macosx-app.sh" release diff --git a/make-macosx.sh b/make-macosx.sh index 91fecf198d..387fe640c1 100755 --- a/make-macosx.sh +++ b/make-macosx.sh @@ -47,6 +47,8 @@ if [ -d /Developer/SDKs/MacOSX10.5.sdk ]; then ARCH_SDK=/Developer/SDKs/MacOSX10.5.sdk ARCH_CFLAGS="-isysroot /Developer/SDKs/MacOSX10.5.sdk" ARCH_MACOSX_VERSION_MIN="10.5" +else + ARCH_MACOSX_VERSION_MIN="10.7" fi @@ -68,4 +70,5 @@ NCPU=`sysctl -n hw.ncpu` (ARCH=${BUILDARCH} CFLAGS=$ARCH_CFLAGS MACOSX_VERSION_MIN=$ARCH_MACOSX_VERSION_MIN make -j$NCPU) || exit 1; # use the following shell script to build an application bundle +export MACOSX_DEPLOYMENT_TARGET="${ARCH_MACOSX_VERSION_MIN}" "./make-macosx-app.sh" release ${BUILDARCH} From 45de6be4c233891eb70e5592f3287a4af39d6002 Mon Sep 17 00:00:00 2001 From: Zack Middleton Date: Fri, 15 Sep 2017 19:14:03 -0500 Subject: [PATCH 179/240] Fix listen server sending snapshots each client frame Running a server using the client engine would send server snapshots to loopback and LAN clients each client frame (com_maxfps). This causes excessive network traffic and likely does not provide new information to the clients because SV_Frame() won't have run between the extra snapshots. This commit restores the original behavior of respecting local/LAN client's snaps userinfo and sv_fps. The issue was introduced by the following commit: Commit ac30d86db01a43130d2c9ff6fe31d6135d8e2592 From: Thilo Schulz Date: Wed, 13 Jul 2011 17:11:30 +0000 Subject: - Improve snapshot rate and data rate control Reported by Eugene C. --- code/server/sv_snapshot.c | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/code/server/sv_snapshot.c b/code/server/sv_snapshot.c index 2237abd345..7c04afdf03 100644 --- a/code/server/sv_snapshot.c +++ b/code/server/sv_snapshot.c @@ -653,6 +653,9 @@ void SV_SendClientMessages(void) if(!c->state) continue; // not connected + if(svs.time - c->lastSnapshotTime < c->snapshotMsec * com_timescale->value) + continue; // It's not time yet + if(*c->downloadName) continue; // Client is downloading, don't send snapshots @@ -666,10 +669,6 @@ void SV_SendClientMessages(void) (sv_lanForceRate->integer && Sys_IsLANAddress(c->netchan.remoteAddress)))) { // rate control for clients not on LAN - - if(svs.time - c->lastSnapshotTime < c->snapshotMsec * com_timescale->value) - continue; // It's not time yet - if(SV_RateMsec(c) > 0) { // Not enough time since last packet passed through the line From 414f3c5e3e2fd95eb59f28d0f94269bf201417c7 Mon Sep 17 00:00:00 2001 From: Zack Middleton Date: Sat, 23 Sep 2017 19:55:49 -0500 Subject: [PATCH 180/240] Enable Ogg Vorbis support by default libvorbis is included in-tree so this doesn't require additional dependencies. --- .travis.yml | 4 ++-- Makefile | 2 +- jenkins-ci-build.sh | 1 - 3 files changed, 3 insertions(+), 4 deletions(-) diff --git a/.travis.yml b/.travis.yml index 77c6901992..2281b9893c 100644 --- a/.travis.yml +++ b/.travis.yml @@ -14,8 +14,8 @@ env: - CC=gcc - CC=clang # extra libs - - CC=gcc USE_CODEC_VORBIS=1 USE_FREETYPE=1 - - CC=clang USE_CODEC_VORBIS=1 USE_FREETYPE=1 + - CC=gcc USE_FREETYPE=1 + - CC=clang USE_FREETYPE=1 # cross-compile using mingw # dlopen curl to workaround link error because mingw-w64 in trusty is missing strtok_r required by libcurl.a - CC= PLATFORM="mingw32" ARCH="x86" USE_CURL_DLOPEN=1 diff --git a/Makefile b/Makefile index 44d2862c10..dc40a65e39 100644 --- a/Makefile +++ b/Makefile @@ -176,7 +176,7 @@ ifndef USE_CURL_DLOPEN endif ifndef USE_CODEC_VORBIS -USE_CODEC_VORBIS=0 +USE_CODEC_VORBIS=1 endif ifndef USE_CODEC_OPUS diff --git a/jenkins-ci-build.sh b/jenkins-ci-build.sh index 31bdd97fc7..e28c80a57a 100755 --- a/jenkins-ci-build.sh +++ b/jenkins-ci-build.sh @@ -8,7 +8,6 @@ cd ${MASTER_DIR} if [ "${OPTIONS}" == "all_options" ]; then - export USE_CODEC_VORBIS=1 export USE_FREETYPE=1 fi From eeaade70bd47d88125800870be090c53db469cda Mon Sep 17 00:00:00 2001 From: Zack Middleton Date: Tue, 26 Sep 2017 01:05:46 -0500 Subject: [PATCH 181/240] Statically link libgcc on Windows Fix for ioq3 test builds for Windows x86 (cross-compiled from Ubuntu using mingw-w64) requiring libgcc_s_sjlj-1.dll. I'm unable to reproduce the issue using mingw-w64 in Debian or Cygwin. --- Makefile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Makefile b/Makefile index dc40a65e39..e40c0da7b2 100644 --- a/Makefile +++ b/Makefile @@ -613,7 +613,7 @@ ifdef MINGW CLIENT_LDFLAGS += -mwindows endif CLIENT_LIBS = -lgdi32 -lole32 - RENDERER_LIBS = -lgdi32 -lole32 -lopengl32 + RENDERER_LIBS = -lgdi32 -lole32 -lopengl32 -static-libgcc ifeq ($(USE_FREETYPE),1) FREETYPE_CFLAGS = -Ifreetype2 From 51e9aa2df037e3df72bc5f3b663010b8f4986b14 Mon Sep 17 00:00:00 2001 From: Zack Middleton Date: Fri, 29 Sep 2017 18:28:34 -0500 Subject: [PATCH 182/240] Fix hit accuracy stats for lightning gun and shotgun kills If a lightning bolt killed a player or the first shotgun pellet that hit a player killed them, the shot was not counted as accurate. Check if shot player is alive for hit accuracy before dealing damage. --- code/game/g_weapon.c | 32 ++++++++++---------------------- 1 file changed, 10 insertions(+), 22 deletions(-) diff --git a/code/game/g_weapon.c b/code/game/g_weapon.c index ecf650575d..e4d32f9c59 100644 --- a/code/game/g_weapon.c +++ b/code/game/g_weapon.c @@ -275,6 +275,7 @@ qboolean ShotgunPellet( vec3_t start, vec3_t end, gentity_t *ent ) { vec3_t impactpoint, bouncedir; #endif vec3_t tr_start, tr_end; + qboolean hitClient = qfalse; passent = ent->s.number; VectorCopy( start, tr_start ); @@ -304,19 +305,12 @@ qboolean ShotgunPellet( vec3_t start, vec3_t end, gentity_t *ent ) { } continue; } - else { - G_Damage( traceEnt, ent, ent, forward, tr.endpos, - damage, 0, MOD_SHOTGUN); - if( LogAccuracyHit( traceEnt, ent ) ) { - return qtrue; - } - } -#else - G_Damage( traceEnt, ent, ent, forward, tr.endpos, damage, 0, MOD_SHOTGUN); - if( LogAccuracyHit( traceEnt, ent ) ) { - return qtrue; - } #endif + if( LogAccuracyHit( traceEnt, ent ) ) { + hitClient = qtrue; + } + G_Damage( traceEnt, ent, ent, forward, tr.endpos, damage, 0, MOD_SHOTGUN); + return hitClient; } return qfalse; } @@ -663,14 +657,11 @@ void Weapon_LightningFire( gentity_t *ent ) { } continue; } - else { - G_Damage( traceEnt, ent, ent, forward, tr.endpos, - damage, 0, MOD_LIGHTNING); - } -#else - G_Damage( traceEnt, ent, ent, forward, tr.endpos, - damage, 0, MOD_LIGHTNING); #endif + if( LogAccuracyHit( traceEnt, ent ) ) { + ent->client->accuracy_hits++; + } + G_Damage( traceEnt, ent, ent, forward, tr.endpos, damage, 0, MOD_LIGHTNING); } if ( traceEnt->takedamage && traceEnt->client ) { @@ -678,9 +669,6 @@ void Weapon_LightningFire( gentity_t *ent ) { tent->s.otherEntityNum = traceEnt->s.number; tent->s.eventParm = DirToByte( tr.plane.normal ); tent->s.weapon = ent->s.weapon; - if( LogAccuracyHit( traceEnt, ent ) ) { - ent->client->accuracy_hits++; - } } else if ( !( tr.surfaceFlags & SURF_NOIMPACT ) ) { tent = G_TempEntity( tr.endpos, EV_MISSILE_MISS ); tent->s.eventParm = DirToByte( tr.plane.normal ); From 20573bce434c08d18293e3f8a093eab809e34d83 Mon Sep 17 00:00:00 2001 From: Zack Middleton Date: Sun, 1 Oct 2017 18:59:01 -0500 Subject: [PATCH 183/240] Don't link to libGL at compile time Get all OpenGL functions using SDL_GL_GetProcAddress(). This makes it easier to cross-arch compile on Linux and add support for OpenGL ES in the future. Users still have to supply their own libSDL2 for cross-arch compiling on Linux. But now the user does not have to re-install libgl1-mesa-dev package for i386 or amd64 on Debian when switching between compiling ioquake3 for x86 and x86_64. --- Makefile | 12 +- code/renderercommon/qgl.h | 410 +++++++------------------------------ code/renderergl2/tr_init.c | 2 +- code/sdl/sdl_glimp.c | 87 +++++++- 4 files changed, 163 insertions(+), 348 deletions(-) diff --git a/Makefile b/Makefile index e40c0da7b2..54ea5d3c1f 100644 --- a/Makefile +++ b/Makefile @@ -381,7 +381,7 @@ ifneq (,$(findstring "$(PLATFORM)", "linux" "gnu_kfreebsd" "kfreebsd-gnu" "gnu") AUTOUPDATER_LIBS += -ldl CLIENT_LIBS=$(SDL_LIBS) - RENDERER_LIBS = $(SDL_LIBS) -lGL + RENDERER_LIBS = $(SDL_LIBS) ifeq ($(USE_OPENAL),1) ifneq ($(USE_OPENAL_DLOPEN),1) @@ -613,7 +613,7 @@ ifdef MINGW CLIENT_LDFLAGS += -mwindows endif CLIENT_LIBS = -lgdi32 -lole32 - RENDERER_LIBS = -lgdi32 -lole32 -lopengl32 -static-libgcc + RENDERER_LIBS = -lgdi32 -lole32 -static-libgcc ifeq ($(USE_FREETYPE),1) FREETYPE_CFLAGS = -Ifreetype2 @@ -699,7 +699,7 @@ ifeq ($(PLATFORM),freebsd) CLIENT_LIBS = CLIENT_LIBS += $(SDL_LIBS) - RENDERER_LIBS = $(SDL_LIBS) -lGL + RENDERER_LIBS = $(SDL_LIBS) # optional features/libraries ifeq ($(USE_OPENAL),1) @@ -790,7 +790,7 @@ ifeq ($(PLATFORM),openbsd) CLIENT_LIBS = CLIENT_LIBS += $(SDL_LIBS) - RENDERER_LIBS = $(SDL_LIBS) -lGL + RENDERER_LIBS = $(SDL_LIBS) ifeq ($(USE_OPENAL),1) ifneq ($(USE_OPENAL_DLOPEN),1) @@ -853,7 +853,7 @@ ifeq ($(PLATFORM),irix64) # FIXME: The X libraries probably aren't necessary? CLIENT_LIBS=-L/usr/X11/$(LIB) $(SDL_LIBS) \ -lX11 -lXext -lm - RENDERER_LIBS = $(SDL_LIBS) -lGL + RENDERER_LIBS = $(SDL_LIBS) else # ifeq IRIX @@ -909,7 +909,7 @@ ifeq ($(PLATFORM),sunos) BOTCFLAGS=-O0 CLIENT_LIBS +=$(SDL_LIBS) -lX11 -lXext -liconv -lm - RENDERER_LIBS = $(SDL_LIBS) -lGL + RENDERER_LIBS = $(SDL_LIBS) else # ifeq sunos diff --git a/code/renderercommon/qgl.h b/code/renderercommon/qgl.h index 8f76c92ffe..d490f3ff15 100644 --- a/code/renderercommon/qgl.h +++ b/code/renderercommon/qgl.h @@ -42,342 +42,80 @@ extern void (APIENTRYP qglUnlockArraysEXT) (void); //=========================================================================== -#define qglAccum glAccum -#define qglAlphaFunc glAlphaFunc -#define qglAreTexturesResident glAreTexturesResident -#define qglArrayElement glArrayElement -#define qglBegin glBegin -#define qglBindTexture glBindTexture -#define qglBitmap glBitmap -#define qglBlendFunc glBlendFunc -#define qglCallList glCallList -#define qglCallLists glCallLists -#define qglClear glClear -#define qglClearAccum glClearAccum -#define qglClearColor glClearColor -#define qglClearDepth glClearDepth -#define qglClearIndex glClearIndex -#define qglClearStencil glClearStencil -#define qglClipPlane glClipPlane -#define qglColor3b glColor3b -#define qglColor3bv glColor3bv -#define qglColor3d glColor3d -#define qglColor3dv glColor3dv -#define qglColor3f glColor3f -#define qglColor3fv glColor3fv -#define qglColor3i glColor3i -#define qglColor3iv glColor3iv -#define qglColor3s glColor3s -#define qglColor3sv glColor3sv -#define qglColor3ub glColor3ub -#define qglColor3ubv glColor3ubv -#define qglColor3ui glColor3ui -#define qglColor3uiv glColor3uiv -#define qglColor3us glColor3us -#define qglColor3usv glColor3usv -#define qglColor4b glColor4b -#define qglColor4bv glColor4bv -#define qglColor4d glColor4d -#define qglColor4dv glColor4dv -#define qglColor4f glColor4f -#define qglColor4fv glColor4fv -#define qglColor4i glColor4i -#define qglColor4iv glColor4iv -#define qglColor4s glColor4s -#define qglColor4sv glColor4sv -#define qglColor4ub glColor4ub -#define qglColor4ubv glColor4ubv -#define qglColor4ui glColor4ui -#define qglColor4uiv glColor4uiv -#define qglColor4us glColor4us -#define qglColor4usv glColor4usv -#define qglColorMask glColorMask -#define qglColorMaterial glColorMaterial -#define qglColorPointer glColorPointer -#define qglCopyPixels glCopyPixels -#define qglCopyTexImage1D glCopyTexImage1D -#define qglCopyTexImage2D glCopyTexImage2D -#define qglCopyTexSubImage1D glCopyTexSubImage1D -#define qglCopyTexSubImage2D glCopyTexSubImage2D -#define qglCullFace glCullFace -#define qglDeleteLists glDeleteLists -#define qglDeleteTextures glDeleteTextures -#define qglDepthFunc glDepthFunc -#define qglDepthMask glDepthMask -#define qglDepthRange glDepthRange -#define qglDisable glDisable -#define qglDisableClientState glDisableClientState -#define qglDrawArrays glDrawArrays -#define qglDrawBuffer glDrawBuffer -#define qglDrawElements glDrawElements -#define qglDrawPixels glDrawPixels -#define qglEdgeFlag glEdgeFlag -#define qglEdgeFlagPointer glEdgeFlagPointer -#define qglEdgeFlagv glEdgeFlagv -#define qglEnable glEnable -#define qglEnableClientState glEnableClientState -#define qglEnd glEnd -#define qglEndList glEndList -#define qglEvalCoord1d glEvalCoord1d -#define qglEvalCoord1dv glEvalCoord1dv -#define qglEvalCoord1f glEvalCoord1f -#define qglEvalCoord1fv glEvalCoord1fv -#define qglEvalCoord2d glEvalCoord2d -#define qglEvalCoord2dv glEvalCoord2dv -#define qglEvalCoord2f glEvalCoord2f -#define qglEvalCoord2fv glEvalCoord2fv -#define qglEvalMesh1 glEvalMesh1 -#define qglEvalMesh2 glEvalMesh2 -#define qglEvalPoint1 glEvalPoint1 -#define qglEvalPoint2 glEvalPoint2 -#define qglFeedbackBuffer glFeedbackBuffer -#define qglFinish glFinish -#define qglFlush glFlush -#define qglFogf glFogf -#define qglFogfv glFogfv -#define qglFogi glFogi -#define qglFogiv glFogiv -#define qglFrontFace glFrontFace -#define qglFrustum glFrustum -#define qglGenLists glGenLists -#define qglGenTextures glGenTextures -#define qglGetBooleanv glGetBooleanv -#define qglGetClipPlane glGetClipPlane -#define qglGetDoublev glGetDoublev -#define qglGetError glGetError -#define qglGetFloatv glGetFloatv -#define qglGetIntegerv glGetIntegerv -#define qglGetLightfv glGetLightfv -#define qglGetLightiv glGetLightiv -#define qglGetMapdv glGetMapdv -#define qglGetMapfv glGetMapfv -#define qglGetMapiv glGetMapiv -#define qglGetMaterialfv glGetMaterialfv -#define qglGetMaterialiv glGetMaterialiv -#define qglGetPixelMapfv glGetPixelMapfv -#define qglGetPixelMapuiv glGetPixelMapuiv -#define qglGetPixelMapusv glGetPixelMapusv -#define qglGetPointerv glGetPointerv -#define qglGetPolygonStipple glGetPolygonStipple -#define qglGetString glGetString -#define qglGetTexGendv glGetTexGendv -#define qglGetTexGenfv glGetTexGenfv -#define qglGetTexGeniv glGetTexGeniv -#define qglGetTexImage glGetTexImage -#define qglGetTexLevelParameterfv glGetTexLevelParameterfv -#define qglGetTexLevelParameteriv glGetTexLevelParameteriv -#define qglGetTexParameterfv glGetTexParameterfv -#define qglGetTexParameteriv glGetTexParameteriv -#define qglHint glHint -#define qglIndexMask glIndexMask -#define qglIndexPointer glIndexPointer -#define qglIndexd glIndexd -#define qglIndexdv glIndexdv -#define qglIndexf glIndexf -#define qglIndexfv glIndexfv -#define qglIndexi glIndexi -#define qglIndexiv glIndexiv -#define qglIndexs glIndexs -#define qglIndexsv glIndexsv -#define qglIndexub glIndexub -#define qglIndexubv glIndexubv -#define qglInitNames glInitNames -#define qglInterleavedArrays glInterleavedArrays -#define qglIsEnabled glIsEnabled -#define qglIsList glIsList -#define qglIsTexture glIsTexture -#define qglLightModelf glLightModelf -#define qglLightModelfv glLightModelfv -#define qglLightModeli glLightModeli -#define qglLightModeliv glLightModeliv -#define qglLightf glLightf -#define qglLightfv glLightfv -#define qglLighti glLighti -#define qglLightiv glLightiv -#define qglLineStipple glLineStipple -#define qglLineWidth glLineWidth -#define qglListBase glListBase -#define qglLoadIdentity glLoadIdentity -#define qglLoadMatrixd glLoadMatrixd -#define qglLoadMatrixf glLoadMatrixf -#define qglLoadName glLoadName -#define qglLogicOp glLogicOp -#define qglMap1d glMap1d -#define qglMap1f glMap1f -#define qglMap2d glMap2d -#define qglMap2f glMap2f -#define qglMapGrid1d glMapGrid1d -#define qglMapGrid1f glMapGrid1f -#define qglMapGrid2d glMapGrid2d -#define qglMapGrid2f glMapGrid2f -#define qglMaterialf glMaterialf -#define qglMaterialfv glMaterialfv -#define qglMateriali glMateriali -#define qglMaterialiv glMaterialiv -#define qglMatrixMode glMatrixMode -#define qglMultMatrixd glMultMatrixd -#define qglMultMatrixf glMultMatrixf -#define qglNewList glNewList -#define qglNormal3b glNormal3b -#define qglNormal3bv glNormal3bv -#define qglNormal3d glNormal3d -#define qglNormal3dv glNormal3dv -#define qglNormal3f glNormal3f -#define qglNormal3fv glNormal3fv -#define qglNormal3i glNormal3i -#define qglNormal3iv glNormal3iv -#define qglNormal3s glNormal3s -#define qglNormal3sv glNormal3sv -#define qglNormalPointer glNormalPointer -#define qglOrtho glOrtho -#define qglPassThrough glPassThrough -#define qglPixelMapfv glPixelMapfv -#define qglPixelMapuiv glPixelMapuiv -#define qglPixelMapusv glPixelMapusv -#define qglPixelStoref glPixelStoref -#define qglPixelStorei glPixelStorei -#define qglPixelTransferf glPixelTransferf -#define qglPixelTransferi glPixelTransferi -#define qglPixelZoom glPixelZoom -#define qglPointSize glPointSize -#define qglPolygonMode glPolygonMode -#define qglPolygonOffset glPolygonOffset -#define qglPolygonStipple glPolygonStipple -#define qglPopAttrib glPopAttrib -#define qglPopClientAttrib glPopClientAttrib -#define qglPopMatrix glPopMatrix -#define qglPopName glPopName -#define qglPrioritizeTextures glPrioritizeTextures -#define qglPushAttrib glPushAttrib -#define qglPushClientAttrib glPushClientAttrib -#define qglPushMatrix glPushMatrix -#define qglPushName glPushName -#define qglRasterPos2d glRasterPos2d -#define qglRasterPos2dv glRasterPos2dv -#define qglRasterPos2f glRasterPos2f -#define qglRasterPos2fv glRasterPos2fv -#define qglRasterPos2i glRasterPos2i -#define qglRasterPos2iv glRasterPos2iv -#define qglRasterPos2s glRasterPos2s -#define qglRasterPos2sv glRasterPos2sv -#define qglRasterPos3d glRasterPos3d -#define qglRasterPos3dv glRasterPos3dv -#define qglRasterPos3f glRasterPos3f -#define qglRasterPos3fv glRasterPos3fv -#define qglRasterPos3i glRasterPos3i -#define qglRasterPos3iv glRasterPos3iv -#define qglRasterPos3s glRasterPos3s -#define qglRasterPos3sv glRasterPos3sv -#define qglRasterPos4d glRasterPos4d -#define qglRasterPos4dv glRasterPos4dv -#define qglRasterPos4f glRasterPos4f -#define qglRasterPos4fv glRasterPos4fv -#define qglRasterPos4i glRasterPos4i -#define qglRasterPos4iv glRasterPos4iv -#define qglRasterPos4s glRasterPos4s -#define qglRasterPos4sv glRasterPos4sv -#define qglReadBuffer glReadBuffer -#define qglReadPixels glReadPixels -#define qglRectd glRectd -#define qglRectdv glRectdv -#define qglRectf glRectf -#define qglRectfv glRectfv -#define qglRecti glRecti -#define qglRectiv glRectiv -#define qglRects glRects -#define qglRectsv glRectsv -#define qglRenderMode glRenderMode -#define qglRotated glRotated -#define qglRotatef glRotatef -#define qglScaled glScaled -#define qglScalef glScalef -#define qglScissor glScissor -#define qglSelectBuffer glSelectBuffer -#define qglShadeModel glShadeModel -#define qglStencilFunc glStencilFunc -#define qglStencilMask glStencilMask -#define qglStencilOp glStencilOp -#define qglTexCoord1d glTexCoord1d -#define qglTexCoord1dv glTexCoord1dv -#define qglTexCoord1f glTexCoord1f -#define qglTexCoord1fv glTexCoord1fv -#define qglTexCoord1i glTexCoord1i -#define qglTexCoord1iv glTexCoord1iv -#define qglTexCoord1s glTexCoord1s -#define qglTexCoord1sv glTexCoord1sv -#define qglTexCoord2d glTexCoord2d -#define qglTexCoord2dv glTexCoord2dv -#define qglTexCoord2f glTexCoord2f -#define qglTexCoord2fv glTexCoord2fv -#define qglTexCoord2i glTexCoord2i -#define qglTexCoord2iv glTexCoord2iv -#define qglTexCoord2s glTexCoord2s -#define qglTexCoord2sv glTexCoord2sv -#define qglTexCoord3d glTexCoord3d -#define qglTexCoord3dv glTexCoord3dv -#define qglTexCoord3f glTexCoord3f -#define qglTexCoord3fv glTexCoord3fv -#define qglTexCoord3i glTexCoord3i -#define qglTexCoord3iv glTexCoord3iv -#define qglTexCoord3s glTexCoord3s -#define qglTexCoord3sv glTexCoord3sv -#define qglTexCoord4d glTexCoord4d -#define qglTexCoord4dv glTexCoord4dv -#define qglTexCoord4f glTexCoord4f -#define qglTexCoord4fv glTexCoord4fv -#define qglTexCoord4i glTexCoord4i -#define qglTexCoord4iv glTexCoord4iv -#define qglTexCoord4s glTexCoord4s -#define qglTexCoord4sv glTexCoord4sv -#define qglTexCoordPointer glTexCoordPointer -#define qglTexEnvf glTexEnvf -#define qglTexEnvfv glTexEnvfv -#define qglTexEnvi glTexEnvi -#define qglTexEnviv glTexEnviv -#define qglTexGend glTexGend -#define qglTexGendv glTexGendv -#define qglTexGenf glTexGenf -#define qglTexGenfv glTexGenfv -#define qglTexGeni glTexGeni -#define qglTexGeniv glTexGeniv -#define qglTexImage1D glTexImage1D -#define qglTexImage2D glTexImage2D -#define qglTexParameterf glTexParameterf -#define qglTexParameterfv glTexParameterfv -#define qglTexParameteri glTexParameteri -#define qglTexParameteriv glTexParameteriv -#define qglTexSubImage1D glTexSubImage1D -#define qglTexSubImage2D glTexSubImage2D -#define qglTranslated glTranslated -#define qglTranslatef glTranslatef -#define qglVertex2d glVertex2d -#define qglVertex2dv glVertex2dv -#define qglVertex2f glVertex2f -#define qglVertex2fv glVertex2fv -#define qglVertex2i glVertex2i -#define qglVertex2iv glVertex2iv -#define qglVertex2s glVertex2s -#define qglVertex2sv glVertex2sv -#define qglVertex3d glVertex3d -#define qglVertex3dv glVertex3dv -#define qglVertex3f glVertex3f -#define qglVertex3fv glVertex3fv -#define qglVertex3i glVertex3i -#define qglVertex3iv glVertex3iv -#define qglVertex3s glVertex3s -#define qglVertex3sv glVertex3sv -#define qglVertex4d glVertex4d -#define qglVertex4dv glVertex4dv -#define qglVertex4f glVertex4f -#define qglVertex4fv glVertex4fv -#define qglVertex4i glVertex4i -#define qglVertex4iv glVertex4iv -#define qglVertex4s glVertex4s -#define qglVertex4sv glVertex4sv -#define qglVertexPointer glVertexPointer -#define qglViewport glViewport - // GL function loader, based on https://gist.github.com/rygorous/16796a0c876cf8a5f542caddb55bce8a +// get missing functions from code/SDL2/include/SDL_opengl.h + +// OpenGL 1.0 +#define QGL_1_0_PROCS \ + GLE(void, AlphaFunc, GLenum func, GLclampf ref) \ + GLE(void, Begin, GLenum mode) \ + GLE(void, BlendFunc, GLenum sfactor, GLenum dfactor) \ + GLE(void, ClearColor, GLclampf red, GLclampf green, GLclampf blue, GLclampf alpha) \ + GLE(void, ClearDepth, GLclampd depth) \ + GLE(void, Clear, GLbitfield mask) \ + GLE(void, ClearStencil, GLint s) \ + GLE(void, ClipPlane, GLenum plane, const GLdouble *equation) \ + GLE(void, Color3f, GLfloat red, GLfloat green, GLfloat blue) \ + GLE(void, Color4f, GLfloat red, GLfloat green, GLfloat blue, GLfloat alpha) \ + GLE(void, Color4ubv, const GLubyte *v) \ + GLE(void, ColorMask, GLboolean red, GLboolean green, GLboolean blue, GLboolean alpha) \ + GLE(void, CullFace, GLenum mode) \ + GLE(void, DepthFunc, GLenum func) \ + GLE(void, DepthMask, GLboolean flag) \ + GLE(void, DepthRange, GLclampd near_val, GLclampd far_val) \ + GLE(void, Disable, GLenum cap) \ + GLE(void, DrawBuffer, GLenum mode) \ + GLE(void, Enable, GLenum cap) \ + GLE(void, End, void) \ + GLE(void, Finish, void) \ + GLE(void, Flush, void) \ + GLE(void, Frustum, GLdouble left, GLdouble right, GLdouble bottom, GLdouble top, GLdouble near_val, GLdouble far_val) \ + GLE(void, GetBooleanv, GLenum pname, GLboolean *params) \ + GLE(GLenum, GetError, void) \ + GLE(void, GetIntegerv, GLenum pname, GLint *params) \ + GLE(const GLubyte *, GetString, GLenum name) \ + GLE(void, LineWidth, GLfloat width) \ + GLE(void, LoadIdentity, void) \ + GLE(void, LoadMatrixf, const GLfloat *m) \ + GLE(void, MatrixMode, GLenum mode) \ + GLE(void, Ortho, GLdouble left, GLdouble right, GLdouble bottom, GLdouble top, GLdouble near_val, GLdouble far_val) \ + GLE(void, PolygonMode, GLenum face, GLenum mode) \ + GLE(void, PolygonOffset, GLfloat factor, GLfloat units) \ + GLE(void, PopMatrix, void) \ + GLE(void, PushMatrix, void) \ + GLE(void, ReadPixels, GLint x, GLint y, GLsizei width, GLsizei height, GLenum format, GLenum type, GLvoid *pixels) \ + GLE(void, Scissor, GLint x, GLint y, GLsizei width, GLsizei height) \ + GLE(void, ShadeModel, GLenum mode) \ + GLE(void, StencilFunc, GLenum func, GLint ref, GLuint mask) \ + GLE(void, StencilMask, GLuint mask) \ + GLE(void, StencilOp, GLenum fail, GLenum zfail, GLenum zpass) \ + GLE(void, TexCoord2f, GLfloat s, GLfloat t) \ + GLE(void, TexCoord2fv, const GLfloat *v) \ + GLE(void, TexEnvf, GLenum target, GLenum pname, GLfloat param) \ + GLE(void, TexImage2D, GLenum target, GLint level, GLint internalFormat, GLsizei width, GLsizei height, GLint border, GLenum format, GLenum type, const GLvoid *pixels) \ + GLE(void, TexParameterf, GLenum target, GLenum pname, GLfloat param) \ + GLE(void, TexParameteri, GLenum target, GLenum pname, GLint param) \ + GLE(void, Translatef, GLfloat x, GLfloat y, GLfloat z) \ + GLE(void, Vertex2f, GLfloat x, GLfloat y) \ + GLE(void, Vertex3f, GLfloat x, GLfloat y, GLfloat z) \ + GLE(void, Vertex3fv, const GLfloat *v) \ + GLE(void, Viewport, GLint x, GLint y, GLsizei width, GLsizei height) \ + +// OpenGL 1.1 +#define QGL_1_1_PROCS \ + GLE(void, GenTextures, GLsizei n, GLuint *textures ) \ + GLE(void, DeleteTextures, GLsizei n, const GLuint *textures) \ + GLE(void, BindTexture, GLenum target, GLuint texture) \ + GLE(void, EnableClientState, GLenum cap) \ + GLE(void, DisableClientState, GLenum cap) \ + GLE(void, ArrayElement, GLint i) \ + GLE(void, DrawArrays, GLenum mode, GLint first, GLsizei count) \ + GLE(void, DrawElements, GLenum mode, GLsizei count, GLenum type, const GLvoid *indices) \ + GLE(void, TexSubImage2D, GLenum target, GLint level, GLint xoffset, GLint yoffset, GLsizei width, GLsizei height, GLenum format, GLenum type, const GLvoid *pixels) \ + GLE(void, CopyTexSubImage2D, GLenum target, GLint level, GLint xoffset, GLint yoffset, GLint x, GLint y, GLsizei width, GLsizei height) \ + GLE(void, VertexPointer, GLint size, GLenum type, GLsizei stride, const GLvoid *ptr) \ + GLE(void, ColorPointer, GLint size, GLenum type, GLsizei stride, const GLvoid *ptr) \ + GLE(void, TexCoordPointer, GLint size, GLenum type, GLsizei stride, const GLvoid *ptr) \ // OpenGL 1.3, was GL_ARB_texture_compression #define QGL_1_3_PROCS \ @@ -556,6 +294,8 @@ extern void (APIENTRYP qglUnlockArraysEXT) (void); GLE(GLvoid, NamedFramebufferRenderbufferEXT, GLuint framebuffer, GLenum attachment, GLenum renderbuffertarget, GLuint renderbuffer) \ #define GLE(ret, name, ...) typedef ret APIENTRY name##proc(__VA_ARGS__); extern name##proc * qgl##name; +QGL_1_0_PROCS; +QGL_1_1_PROCS; QGL_1_3_PROCS; QGL_1_5_PROCS; QGL_2_0_PROCS; diff --git a/code/renderergl2/tr_init.c b/code/renderergl2/tr_init.c index 583a04b639..212cacb14c 100644 --- a/code/renderergl2/tr_init.c +++ b/code/renderergl2/tr_init.c @@ -1036,7 +1036,7 @@ void GfxInfo_f( void ) GLint numExtensions; int i; - glGetIntegerv(GL_NUM_EXTENSIONS, &numExtensions); + qglGetIntegerv(GL_NUM_EXTENSIONS, &numExtensions); for (i = 0; i < numExtensions; i++) { ri.Printf(PRINT_ALL, "%s ", qglGetStringi(GL_EXTENSIONS, i)); diff --git a/code/sdl/sdl_glimp.c b/code/sdl/sdl_glimp.c index 454c2bf398..c81d38b043 100644 --- a/code/sdl/sdl_glimp.c +++ b/code/sdl/sdl_glimp.c @@ -60,6 +60,11 @@ void (APIENTRYP qglMultiTexCoord2fARB) (GLenum target, GLfloat s, GLfloat t); void (APIENTRYP qglLockArraysEXT) (GLint first, GLsizei count); void (APIENTRYP qglUnlockArraysEXT) (void); +#define GLE(ret, name, ...) name##proc * qgl##name; +QGL_1_0_PROCS; +QGL_1_1_PROCS; +#undef GLE + /* =============== GLimp_Shutdown @@ -210,6 +215,50 @@ static void GLimp_DetectAvailableModes(void) SDL_free( modes ); } +/* +=============== +GLimp_GetProcAddresses + +Get addresses for OpenGL functions. +=============== +*/ +static qboolean GLimp_GetProcAddresses( void ) { + qboolean success = qtrue; + +#ifdef __SDL_NOGETPROCADDR__ +#define GLE( ret, name, ... ) qgl##name = gl#name; +#else +#define GLE( ret, name, ... ) qgl##name = (name##proc *) SDL_GL_GetProcAddress("gl" #name); \ + if ( qgl##name == NULL ) { \ + ri.Printf( PRINT_ALL, "ERROR: Missing OpenGL function %s\n", "gl" #name ); \ + success = qfalse; \ + } +#endif + + QGL_1_0_PROCS; + QGL_1_1_PROCS; + +#undef GLE + + return success; +} + +/* +=============== +GLimp_ClearProcAddresses + +Clear addresses for OpenGL functions. +=============== +*/ +static void GLimp_ClearProcAddresses( void ) { +#define GLE( ret, name, ... ) qgl##name = NULL; + + QGL_1_0_PROCS; + QGL_1_1_PROCS; + +#undef GLE +} + /* =============== GLimp_SetMode @@ -309,6 +358,7 @@ static int GLimp_SetMode(int mode, qboolean fullscreen, qboolean noborder, qbool // Destroy existing state if it exists if( SDL_glContext != NULL ) { + GLimp_ClearProcAddresses(); SDL_GL_DeleteContext( SDL_glContext ); SDL_glContext = NULL; } @@ -503,11 +553,22 @@ static int GLimp_SetMode(int mode, qboolean fullscreen, qboolean noborder, qbool ri.Printf(PRINT_ALL, "SDL_GL_CreateContext succeeded.\n"); - renderer = (const char *)qglGetString(GL_RENDERER); - if (renderer && (strstr(renderer, "Software Renderer") || strstr(renderer, "Software Rasterizer"))) + if ( GLimp_GetProcAddresses() ) { - ri.Printf(PRINT_ALL, "GL_RENDERER is %s, rejecting context\n", renderer); + renderer = (const char *)qglGetString(GL_RENDERER); + } + else + { + ri.Printf( PRINT_ALL, "GLimp_GetProcAddresses() failed for OpenGL 3.2 core context\n" ); + renderer = NULL; + } + if (!renderer || (strstr(renderer, "Software Renderer") || strstr(renderer, "Software Rasterizer"))) + { + if ( renderer ) + ri.Printf(PRINT_ALL, "GL_RENDERER is %s, rejecting context\n", renderer); + + GLimp_ClearProcAddresses(); SDL_GL_DeleteContext(SDL_glContext); SDL_glContext = NULL; @@ -522,10 +583,24 @@ static int GLimp_SetMode(int mode, qboolean fullscreen, qboolean noborder, qbool SDL_glContext = NULL; } - if( !SDL_glContext && ( SDL_glContext = SDL_GL_CreateContext( SDL_window ) ) == NULL ) + if ( !SDL_glContext ) { - ri.Printf( PRINT_DEVELOPER, "SDL_GL_CreateContext failed: %s\n", SDL_GetError( ) ); - continue; + if( ( SDL_glContext = SDL_GL_CreateContext( SDL_window ) ) == NULL ) + { + ri.Printf( PRINT_DEVELOPER, "SDL_GL_CreateContext failed: %s\n", SDL_GetError( ) ); + continue; + } + + if ( !GLimp_GetProcAddresses() ) + { + ri.Printf( PRINT_ALL, "GLimp_GetProcAddresses() failed\n" ); + GLimp_ClearProcAddresses(); + SDL_GL_DeleteContext( SDL_glContext ); + SDL_glContext = NULL; + SDL_DestroyWindow( SDL_window ); + SDL_window = NULL; + continue; + } } qglClearColor( 0, 0, 0, 1 ); From c9d12aa3f3e7b43b5c6a9c2d79ecb988a0e76a05 Mon Sep 17 00:00:00 2001 From: Zack Middleton Date: Sun, 1 Oct 2017 20:16:23 -0500 Subject: [PATCH 184/240] Add common OpenGL version parsing + OpenGL 3 fixes - Parse OpenGL version in sdl_glimp.c to share with both renderers. - Add GL_VERSION_ATLEAST(major, minor) macro. - Get address of glGetStringi if using OpenGL 3. - Fix glConfig.extensions_string when using GL3 core context in opengl2 renderer. - Make opengl1 renderer's gfxinfo support qglGetStringi too. --- code/renderercommon/qgl.h | 3 ++ code/renderergl1/tr_init.c | 16 +++++++- code/renderergl2/tr_extensions.c | 15 ++----- code/renderergl2/tr_init.c | 8 ++-- code/renderergl2/tr_local.h | 3 -- code/sdl/sdl_glimp.c | 69 +++++++++++++++++++++++++++++--- 6 files changed, 89 insertions(+), 25 deletions(-) diff --git a/code/renderercommon/qgl.h b/code/renderercommon/qgl.h index d490f3ff15..29fee3e4d3 100644 --- a/code/renderercommon/qgl.h +++ b/code/renderercommon/qgl.h @@ -305,4 +305,7 @@ QGL_ARB_vertex_array_object_PROCS; QGL_EXT_direct_state_access_PROCS; #undef GLE +extern int qglMajorVersion, qglMinorVersion; +#define QGL_VERSION_ATLEAST( major, minor ) ( qglMajorVersion > major || ( qglMajorVersion == major && qglMinorVersion >= minor ) ) + #endif diff --git a/code/renderergl1/tr_init.c b/code/renderergl1/tr_init.c index 11278df7f9..62b590e23f 100644 --- a/code/renderergl1/tr_init.c +++ b/code/renderergl1/tr_init.c @@ -920,7 +920,21 @@ void GfxInfo_f( void ) ri.Printf( PRINT_ALL, "GL_RENDERER: %s\n", glConfig.renderer_string ); ri.Printf( PRINT_ALL, "GL_VERSION: %s\n", glConfig.version_string ); ri.Printf( PRINT_ALL, "GL_EXTENSIONS: " ); - R_PrintLongString( glConfig.extensions_string ); + if ( qglGetStringi ) + { + GLint numExtensions; + int i; + + qglGetIntegerv( GL_NUM_EXTENSIONS, &numExtensions ); + for ( i = 0; i < numExtensions; i++ ) + { + ri.Printf( PRINT_ALL, "%s ", qglGetStringi( GL_EXTENSIONS, i ) ); + } + } + else + { + R_PrintLongString( glConfig.extensions_string ); + } ri.Printf( PRINT_ALL, "\n" ); ri.Printf( PRINT_ALL, "GL_MAX_TEXTURE_SIZE: %d\n", glConfig.maxTextureSize ); ri.Printf( PRINT_ALL, "GL_MAX_TEXTURE_UNITS_ARB: %d\n", glConfig.numTextureUnits ); diff --git a/code/renderergl2/tr_extensions.c b/code/renderergl2/tr_extensions.c index e90f51893c..59cfab536f 100644 --- a/code/renderergl2/tr_extensions.c +++ b/code/renderergl2/tr_extensions.c @@ -34,7 +34,6 @@ Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA QGL_1_3_PROCS; QGL_1_5_PROCS; QGL_2_0_PROCS; -QGL_3_0_PROCS; QGL_ARB_framebuffer_object_PROCS; QGL_ARB_vertex_array_object_PROCS; QGL_EXT_direct_state_access_PROCS; @@ -48,13 +47,12 @@ void GLimp_InitExtraExtensions() qboolean q_gl_version_at_least_3_2; // Check OpenGL version - sscanf(glConfig.version_string, "%d.%d", &glRefConfig.openglMajorVersion, &glRefConfig.openglMinorVersion); - if (glRefConfig.openglMajorVersion < 2) + if ( !QGL_VERSION_ATLEAST( 2, 0 ) ) ri.Error(ERR_FATAL, "OpenGL 2.0 required!"); ri.Printf(PRINT_ALL, "...using OpenGL %s\n", glConfig.version_string); - q_gl_version_at_least_3_0 = (glRefConfig.openglMajorVersion >= 3); - q_gl_version_at_least_3_2 = (glRefConfig.openglMajorVersion > 3 || (glRefConfig.openglMajorVersion == 3 && glRefConfig.openglMinorVersion > 2)); + q_gl_version_at_least_3_0 = QGL_VERSION_ATLEAST( 3, 0 ); + q_gl_version_at_least_3_2 = QGL_VERSION_ATLEAST( 3, 2 ); // Check if we need Intel graphics specific fixes. glRefConfig.intelGraphics = qfalse; @@ -79,13 +77,6 @@ void GLimp_InitExtraExtensions() // OpenGL 2.0, was GL_ARB_shading_language_100, GL_ARB_vertex_program, GL_ARB_shader_objects, and GL_ARB_vertex_shader QGL_2_0_PROCS; - // OpenGL 3.0 - no matching extension - // QGL_*_PROCS becomes several functions, do not remove {} - if (q_gl_version_at_least_3_0) - { - QGL_3_0_PROCS; - } - // OpenGL 3.0 - GL_ARB_framebuffer_object extension = "GL_ARB_framebuffer_object"; glRefConfig.framebufferObject = qfalse; diff --git a/code/renderergl2/tr_init.c b/code/renderergl2/tr_init.c index 212cacb14c..ea315e6605 100644 --- a/code/renderergl2/tr_init.c +++ b/code/renderergl2/tr_init.c @@ -1031,15 +1031,15 @@ void GfxInfo_f( void ) ri.Printf( PRINT_ALL, "GL_RENDERER: %s\n", glConfig.renderer_string ); ri.Printf( PRINT_ALL, "GL_VERSION: %s\n", glConfig.version_string ); ri.Printf( PRINT_ALL, "GL_EXTENSIONS: " ); - if (glRefConfig.openglMajorVersion >= 3) + if ( qglGetStringi ) { GLint numExtensions; int i; - qglGetIntegerv(GL_NUM_EXTENSIONS, &numExtensions); - for (i = 0; i < numExtensions; i++) + qglGetIntegerv( GL_NUM_EXTENSIONS, &numExtensions ); + for ( i = 0; i < numExtensions; i++ ) { - ri.Printf(PRINT_ALL, "%s ", qglGetStringi(GL_EXTENSIONS, i)); + ri.Printf( PRINT_ALL, "%s ", qglGetStringi( GL_EXTENSIONS, i ) ); } } else diff --git a/code/renderergl2/tr_local.h b/code/renderergl2/tr_local.h index 4926149f5a..f5f4b79e4f 100644 --- a/code/renderergl2/tr_local.h +++ b/code/renderergl2/tr_local.h @@ -1336,9 +1336,6 @@ typedef enum { // We can't change glConfig_t without breaking DLL/vms compatibility, so // store extensions we have here. typedef struct { - int openglMajorVersion; - int openglMinorVersion; - qboolean intelGraphics; qboolean occlusionQuery; diff --git a/code/sdl/sdl_glimp.c b/code/sdl/sdl_glimp.c index c81d38b043..b52da43091 100644 --- a/code/sdl/sdl_glimp.c +++ b/code/sdl/sdl_glimp.c @@ -53,6 +53,8 @@ cvar_t *r_allowResize; // make window resizable cvar_t *r_centerWindow; cvar_t *r_sdlDriver; +int qglMajorVersion, qglMinorVersion; + void (APIENTRYP qglActiveTextureARB) (GLenum texture); void (APIENTRYP qglClientActiveTextureARB) (GLenum texture); void (APIENTRYP qglMultiTexCoord2fARB) (GLenum target, GLfloat s, GLfloat t); @@ -63,6 +65,7 @@ void (APIENTRYP qglUnlockArraysEXT) (void); #define GLE(ret, name, ...) name##proc * qgl##name; QGL_1_0_PROCS; QGL_1_1_PROCS; +QGL_3_0_PROCS; #undef GLE /* @@ -224,6 +227,7 @@ Get addresses for OpenGL functions. */ static qboolean GLimp_GetProcAddresses( void ) { qboolean success = qtrue; + const char *version; #ifdef __SDL_NOGETPROCADDR__ #define GLE( ret, name, ... ) qgl##name = gl#name; @@ -235,8 +239,32 @@ static qboolean GLimp_GetProcAddresses( void ) { } #endif - QGL_1_0_PROCS; - QGL_1_1_PROCS; + // OpenGL 1.0 + GLE(const GLubyte *, GetString, GLenum name) + + if ( !qglGetString ) { + Com_Error( ERR_FATAL, "glGetString is NULL" ); + } + + version = (const char *)qglGetString( GL_VERSION ); + + if ( !version ) { + Com_Error( ERR_FATAL, "GL_VERSION is NULL\n" ); + } + + sscanf( version, "%d.%d", &qglMajorVersion, &qglMinorVersion ); + + // require OpenGL 1.1 + if ( QGL_VERSION_ATLEAST( 1, 1 ) ) { + QGL_1_0_PROCS; + QGL_1_1_PROCS; + } else { + Com_Error( ERR_FATAL, "Unsupported OpenGL Version: %s\n", version ); + } + + if ( QGL_VERSION_ATLEAST( 3, 0 ) ) { + QGL_3_0_PROCS; + } #undef GLE @@ -253,8 +281,12 @@ Clear addresses for OpenGL functions. static void GLimp_ClearProcAddresses( void ) { #define GLE( ret, name, ... ) qgl##name = NULL; + qglMajorVersion = 0; + qglMinorVersion = 0; + QGL_1_0_PROCS; QGL_1_1_PROCS; + QGL_3_0_PROCS; #undef GLE } @@ -921,10 +953,37 @@ void GLimp_Init( qboolean coreContext) if (*glConfig.renderer_string && glConfig.renderer_string[strlen(glConfig.renderer_string) - 1] == '\n') glConfig.renderer_string[strlen(glConfig.renderer_string) - 1] = 0; Q_strncpyz( glConfig.version_string, (char *) qglGetString (GL_VERSION), sizeof( glConfig.version_string ) ); - if (qglGetString(GL_EXTENSIONS)) - Q_strncpyz( glConfig.extensions_string, (char *) qglGetString (GL_EXTENSIONS), sizeof( glConfig.extensions_string ) ); + + // manually create extension list if using OpenGL 3 + if ( qglGetStringi ) + { + int i, numExtensions, extensionLength, listLength; + const char *extension; + + qglGetIntegerv( GL_NUM_EXTENSIONS, &numExtensions ); + listLength = 0; + + for ( i = 0; i < numExtensions; i++ ) + { + extension = (char *) qglGetStringi( GL_EXTENSIONS, i ); + extensionLength = strlen( extension ); + + if ( ( listLength + extensionLength + 1 ) >= sizeof( glConfig.extensions_string ) ) + break; + + if ( i > 0 ) { + Q_strcat( glConfig.extensions_string, sizeof( glConfig.extensions_string ), " " ); + listLength++; + } + + Q_strcat( glConfig.extensions_string, sizeof( glConfig.extensions_string ), extension ); + listLength += extensionLength; + } + } else - Q_strncpyz( glConfig.extensions_string, "Not available (core context, fixme)", sizeof( glConfig.extensions_string ) ); + { + Q_strncpyz( glConfig.extensions_string, (char *) qglGetString (GL_EXTENSIONS), sizeof( glConfig.extensions_string ) ); + } // initialize extensions GLimp_InitExtensions( ); From da07a6dbd9e391194720a45ab995b2954a72f307 Mon Sep 17 00:00:00 2001 From: Zack Middleton Date: Sun, 1 Oct 2017 22:53:21 -0500 Subject: [PATCH 185/240] Support parsing OpenGL ES version strings The renderers don't support OpenGL ES though. --- code/renderercommon/qgl.h | 2 ++ code/sdl/sdl_glimp.c | 24 +++++++++++++++++++----- 2 files changed, 21 insertions(+), 5 deletions(-) diff --git a/code/renderercommon/qgl.h b/code/renderercommon/qgl.h index 29fee3e4d3..ab9c35c88f 100644 --- a/code/renderercommon/qgl.h +++ b/code/renderercommon/qgl.h @@ -306,6 +306,8 @@ QGL_EXT_direct_state_access_PROCS; #undef GLE extern int qglMajorVersion, qglMinorVersion; +extern int qglesMajorVersion, qglesMinorVersion; #define QGL_VERSION_ATLEAST( major, minor ) ( qglMajorVersion > major || ( qglMajorVersion == major && qglMinorVersion >= minor ) ) +#define QGLES_VERSION_ATLEAST( major, minor ) ( qglesMajorVersion > major || ( qglesMajorVersion == major && qglesMinorVersion >= minor ) ) #endif diff --git a/code/sdl/sdl_glimp.c b/code/sdl/sdl_glimp.c index b52da43091..11b291cca8 100644 --- a/code/sdl/sdl_glimp.c +++ b/code/sdl/sdl_glimp.c @@ -54,6 +54,7 @@ cvar_t *r_centerWindow; cvar_t *r_sdlDriver; int qglMajorVersion, qglMinorVersion; +int qglesMajorVersion, qglesMinorVersion; void (APIENTRYP qglActiveTextureARB) (GLenum texture); void (APIENTRYP qglClientActiveTextureARB) (GLenum texture); @@ -239,7 +240,7 @@ static qboolean GLimp_GetProcAddresses( void ) { } #endif - // OpenGL 1.0 + // OpenGL 1.0 and OpenGL ES 1.0 GLE(const GLubyte *, GetString, GLenum name) if ( !qglGetString ) { @@ -252,17 +253,28 @@ static qboolean GLimp_GetProcAddresses( void ) { Com_Error( ERR_FATAL, "GL_VERSION is NULL\n" ); } - sscanf( version, "%d.%d", &qglMajorVersion, &qglMinorVersion ); + if ( Q_stricmpn( "OpenGL ES", version, 9 ) == 0 ) { + char profile[6]; // ES, ES-CM, or ES-CL + sscanf( version, "OpenGL %5s %d.%d", profile, &qglesMajorVersion, &qglesMinorVersion ); + // common lite profile (no floating point) is not supported + if ( Q_stricmp( profile, "ES-CL" ) == 0 ) { + qglesMajorVersion = 0; + qglesMinorVersion = 0; + } + } else { + sscanf( version, "%d.%d", &qglMajorVersion, &qglMinorVersion ); + } - // require OpenGL 1.1 - if ( QGL_VERSION_ATLEAST( 1, 1 ) ) { + // require OpenGL 1.1 or OpenGL ES 1.0 + // FIXME: Not all of these functions are available in OpenGL ES + if ( QGL_VERSION_ATLEAST( 1, 1 ) /*|| QGLES_VERSION_ATLEAST( 1, 0 )*/ ) { QGL_1_0_PROCS; QGL_1_1_PROCS; } else { Com_Error( ERR_FATAL, "Unsupported OpenGL Version: %s\n", version ); } - if ( QGL_VERSION_ATLEAST( 3, 0 ) ) { + if ( QGL_VERSION_ATLEAST( 3, 0 ) || QGLES_VERSION_ATLEAST( 3, 0 ) ) { QGL_3_0_PROCS; } @@ -283,6 +295,8 @@ static void GLimp_ClearProcAddresses( void ) { qglMajorVersion = 0; qglMinorVersion = 0; + qglesMajorVersion = 0; + qglesMinorVersion = 0; QGL_1_0_PROCS; QGL_1_1_PROCS; From e657dbd6b26a54c024a668c01add5a1d32050e91 Mon Sep 17 00:00:00 2001 From: Zack Middleton Date: Mon, 2 Oct 2017 02:02:08 -0500 Subject: [PATCH 186/240] Fix setting cflags/libs from sdl2-config SDL_CFLAGS and SDL_LIBS assignment was "only if absent". However due to previously assigning them to "pkg-config sdl2" values, the values from sdl2-config were ignored. --- Makefile | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Makefile b/Makefile index 54ea5d3c1f..464ad3c4ee 100644 --- a/Makefile +++ b/Makefile @@ -301,8 +301,8 @@ endif # Use sdl2-config if all else fails ifeq ($(SDL_CFLAGS),) ifneq ($(call bin_path, sdl2-config),) - SDL_CFLAGS ?= $(shell sdl2-config --cflags) - SDL_LIBS ?= $(shell sdl2-config --libs) + SDL_CFLAGS = $(shell sdl2-config --cflags) + SDL_LIBS = $(shell sdl2-config --libs) endif endif From a83ae01d93c778274fa05e0961c39775f33b6328 Mon Sep 17 00:00:00 2001 From: Zack Middleton Date: Mon, 2 Oct 2017 04:47:17 -0500 Subject: [PATCH 187/240] Load OpenGL ES 1.1 function procs --- code/renderercommon/qgl.h | 73 ++++++++++++++++++++++----------------- code/sdl/sdl_glimp.c | 18 ++++++---- 2 files changed, 53 insertions(+), 38 deletions(-) diff --git a/code/renderercommon/qgl.h b/code/renderercommon/qgl.h index ab9c35c88f..3377f92c21 100644 --- a/code/renderercommon/qgl.h +++ b/code/renderercommon/qgl.h @@ -45,31 +45,31 @@ extern void (APIENTRYP qglUnlockArraysEXT) (void); // GL function loader, based on https://gist.github.com/rygorous/16796a0c876cf8a5f542caddb55bce8a // get missing functions from code/SDL2/include/SDL_opengl.h -// OpenGL 1.0 -#define QGL_1_0_PROCS \ +// OpenGL 1.0/1.1 and OpenGL ES 1.0 +#define QGL_1_1_PROCS \ GLE(void, AlphaFunc, GLenum func, GLclampf ref) \ - GLE(void, Begin, GLenum mode) \ + GLE(void, BindTexture, GLenum target, GLuint texture) \ GLE(void, BlendFunc, GLenum sfactor, GLenum dfactor) \ GLE(void, ClearColor, GLclampf red, GLclampf green, GLclampf blue, GLclampf alpha) \ - GLE(void, ClearDepth, GLclampd depth) \ GLE(void, Clear, GLbitfield mask) \ GLE(void, ClearStencil, GLint s) \ - GLE(void, ClipPlane, GLenum plane, const GLdouble *equation) \ - GLE(void, Color3f, GLfloat red, GLfloat green, GLfloat blue) \ GLE(void, Color4f, GLfloat red, GLfloat green, GLfloat blue, GLfloat alpha) \ - GLE(void, Color4ubv, const GLubyte *v) \ GLE(void, ColorMask, GLboolean red, GLboolean green, GLboolean blue, GLboolean alpha) \ + GLE(void, ColorPointer, GLint size, GLenum type, GLsizei stride, const GLvoid *ptr) \ + GLE(void, CopyTexSubImage2D, GLenum target, GLint level, GLint xoffset, GLint yoffset, GLint x, GLint y, GLsizei width, GLsizei height) \ GLE(void, CullFace, GLenum mode) \ + GLE(void, DeleteTextures, GLsizei n, const GLuint *textures) \ GLE(void, DepthFunc, GLenum func) \ GLE(void, DepthMask, GLboolean flag) \ - GLE(void, DepthRange, GLclampd near_val, GLclampd far_val) \ + GLE(void, DisableClientState, GLenum cap) \ GLE(void, Disable, GLenum cap) \ - GLE(void, DrawBuffer, GLenum mode) \ + GLE(void, DrawArrays, GLenum mode, GLint first, GLsizei count) \ + GLE(void, DrawElements, GLenum mode, GLsizei count, GLenum type, const GLvoid *indices) \ + GLE(void, EnableClientState, GLenum cap) \ GLE(void, Enable, GLenum cap) \ - GLE(void, End, void) \ GLE(void, Finish, void) \ GLE(void, Flush, void) \ - GLE(void, Frustum, GLdouble left, GLdouble right, GLdouble bottom, GLdouble top, GLdouble near_val, GLdouble far_val) \ + GLE(void, GenTextures, GLsizei n, GLuint *textures ) \ GLE(void, GetBooleanv, GLenum pname, GLboolean *params) \ GLE(GLenum, GetError, void) \ GLE(void, GetIntegerv, GLenum pname, GLint *params) \ @@ -78,8 +78,6 @@ extern void (APIENTRYP qglUnlockArraysEXT) (void); GLE(void, LoadIdentity, void) \ GLE(void, LoadMatrixf, const GLfloat *m) \ GLE(void, MatrixMode, GLenum mode) \ - GLE(void, Ortho, GLdouble left, GLdouble right, GLdouble bottom, GLdouble top, GLdouble near_val, GLdouble far_val) \ - GLE(void, PolygonMode, GLenum face, GLenum mode) \ GLE(void, PolygonOffset, GLfloat factor, GLfloat units) \ GLE(void, PopMatrix, void) \ GLE(void, PushMatrix, void) \ @@ -89,33 +87,43 @@ extern void (APIENTRYP qglUnlockArraysEXT) (void); GLE(void, StencilFunc, GLenum func, GLint ref, GLuint mask) \ GLE(void, StencilMask, GLuint mask) \ GLE(void, StencilOp, GLenum fail, GLenum zfail, GLenum zpass) \ - GLE(void, TexCoord2f, GLfloat s, GLfloat t) \ - GLE(void, TexCoord2fv, const GLfloat *v) \ + GLE(void, TexCoordPointer, GLint size, GLenum type, GLsizei stride, const GLvoid *ptr) \ GLE(void, TexEnvf, GLenum target, GLenum pname, GLfloat param) \ GLE(void, TexImage2D, GLenum target, GLint level, GLint internalFormat, GLsizei width, GLsizei height, GLint border, GLenum format, GLenum type, const GLvoid *pixels) \ GLE(void, TexParameterf, GLenum target, GLenum pname, GLfloat param) \ GLE(void, TexParameteri, GLenum target, GLenum pname, GLint param) \ + GLE(void, TexSubImage2D, GLenum target, GLint level, GLint xoffset, GLint yoffset, GLsizei width, GLsizei height, GLenum format, GLenum type, const GLvoid *pixels) \ GLE(void, Translatef, GLfloat x, GLfloat y, GLfloat z) \ + GLE(void, VertexPointer, GLint size, GLenum type, GLsizei stride, const GLvoid *ptr) \ + GLE(void, Viewport, GLint x, GLint y, GLsizei width, GLsizei height) \ + +// OpenGL 1.0/1.1 but not OpenGL ES 1.x +#define QGL_DESKTOP_1_1_PROCS \ + GLE(void, ArrayElement, GLint i) \ + GLE(void, Begin, GLenum mode) \ + GLE(void, ClearDepth, GLclampd depth) \ + GLE(void, ClipPlane, GLenum plane, const GLdouble *equation) \ + GLE(void, Color3f, GLfloat red, GLfloat green, GLfloat blue) \ + GLE(void, Color4ubv, const GLubyte *v) \ + GLE(void, DepthRange, GLclampd near_val, GLclampd far_val) \ + GLE(void, DrawBuffer, GLenum mode) \ + GLE(void, End, void) \ + GLE(void, Frustum, GLdouble left, GLdouble right, GLdouble bottom, GLdouble top, GLdouble near_val, GLdouble far_val) \ + GLE(void, Ortho, GLdouble left, GLdouble right, GLdouble bottom, GLdouble top, GLdouble near_val, GLdouble far_val) \ + GLE(void, PolygonMode, GLenum face, GLenum mode) \ + GLE(void, TexCoord2f, GLfloat s, GLfloat t) \ + GLE(void, TexCoord2fv, const GLfloat *v) \ GLE(void, Vertex2f, GLfloat x, GLfloat y) \ GLE(void, Vertex3f, GLfloat x, GLfloat y, GLfloat z) \ GLE(void, Vertex3fv, const GLfloat *v) \ - GLE(void, Viewport, GLint x, GLint y, GLsizei width, GLsizei height) \ -// OpenGL 1.1 -#define QGL_1_1_PROCS \ - GLE(void, GenTextures, GLsizei n, GLuint *textures ) \ - GLE(void, DeleteTextures, GLsizei n, const GLuint *textures) \ - GLE(void, BindTexture, GLenum target, GLuint texture) \ - GLE(void, EnableClientState, GLenum cap) \ - GLE(void, DisableClientState, GLenum cap) \ - GLE(void, ArrayElement, GLint i) \ - GLE(void, DrawArrays, GLenum mode, GLint first, GLsizei count) \ - GLE(void, DrawElements, GLenum mode, GLsizei count, GLenum type, const GLvoid *indices) \ - GLE(void, TexSubImage2D, GLenum target, GLint level, GLint xoffset, GLint yoffset, GLsizei width, GLsizei height, GLenum format, GLenum type, const GLvoid *pixels) \ - GLE(void, CopyTexSubImage2D, GLenum target, GLint level, GLint xoffset, GLint yoffset, GLint x, GLint y, GLsizei width, GLsizei height) \ - GLE(void, VertexPointer, GLint size, GLenum type, GLsizei stride, const GLvoid *ptr) \ - GLE(void, ColorPointer, GLint size, GLenum type, GLsizei stride, const GLvoid *ptr) \ - GLE(void, TexCoordPointer, GLint size, GLenum type, GLsizei stride, const GLvoid *ptr) \ +// OpenGL ES 1.1 but not desktop OpenGL 1.x +#define QGL_ES_1_1_PROCS \ + GLE(void, ClearDepthf, GLclampf depth) \ + GLE(void, ClipPlanef, GLenum plane, const GLfloat *equation) \ + GLE(void, DepthRangef, GLclampf near_val, GLclampf far_val) \ + GLE(void, Frustumf, GLfloat left, GLfloat right, GLfloat bottom, GLfloat top, GLfloat near_val, GLfloat far_val) \ + GLE(void, Orthof, GLfloat left, GLfloat right, GLfloat bottom, GLfloat top, GLfloat near_val, GLfloat far_val) \ // OpenGL 1.3, was GL_ARB_texture_compression #define QGL_1_3_PROCS \ @@ -294,8 +302,9 @@ extern void (APIENTRYP qglUnlockArraysEXT) (void); GLE(GLvoid, NamedFramebufferRenderbufferEXT, GLuint framebuffer, GLenum attachment, GLenum renderbuffertarget, GLuint renderbuffer) \ #define GLE(ret, name, ...) typedef ret APIENTRY name##proc(__VA_ARGS__); extern name##proc * qgl##name; -QGL_1_0_PROCS; QGL_1_1_PROCS; +QGL_DESKTOP_1_1_PROCS; +QGL_ES_1_1_PROCS; QGL_1_3_PROCS; QGL_1_5_PROCS; QGL_2_0_PROCS; diff --git a/code/sdl/sdl_glimp.c b/code/sdl/sdl_glimp.c index 11b291cca8..e707bbedc1 100644 --- a/code/sdl/sdl_glimp.c +++ b/code/sdl/sdl_glimp.c @@ -64,8 +64,9 @@ void (APIENTRYP qglLockArraysEXT) (GLint first, GLsizei count); void (APIENTRYP qglUnlockArraysEXT) (void); #define GLE(ret, name, ...) name##proc * qgl##name; -QGL_1_0_PROCS; QGL_1_1_PROCS; +QGL_DESKTOP_1_1_PROCS; +QGL_ES_1_1_PROCS; QGL_3_0_PROCS; #undef GLE @@ -265,11 +266,15 @@ static qboolean GLimp_GetProcAddresses( void ) { sscanf( version, "%d.%d", &qglMajorVersion, &qglMinorVersion ); } - // require OpenGL 1.1 or OpenGL ES 1.0 - // FIXME: Not all of these functions are available in OpenGL ES - if ( QGL_VERSION_ATLEAST( 1, 1 ) /*|| QGLES_VERSION_ATLEAST( 1, 0 )*/ ) { - QGL_1_0_PROCS; + if ( QGL_VERSION_ATLEAST( 1, 1 ) ) { QGL_1_1_PROCS; + QGL_DESKTOP_1_1_PROCS; + } else if ( qglesMajorVersion == 1 && qglesMinorVersion >= 1 ) { + // OpenGL ES 1.1 (2.0 is not backward compatible) + QGL_1_1_PROCS; + QGL_ES_1_1_PROCS; + // error so this doesn't segfault due to NULL desktop GL functions being used + Com_Error( ERR_FATAL, "Unsupported OpenGL Version: %s\n", version ); } else { Com_Error( ERR_FATAL, "Unsupported OpenGL Version: %s\n", version ); } @@ -298,8 +303,9 @@ static void GLimp_ClearProcAddresses( void ) { qglesMajorVersion = 0; qglesMinorVersion = 0; - QGL_1_0_PROCS; QGL_1_1_PROCS; + QGL_DESKTOP_1_1_PROCS; + QGL_ES_1_1_PROCS; QGL_3_0_PROCS; #undef GLE From b3223dcfcb83d7054eb232af3bd4ea50827558ad Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Thomas=20K=C3=B6ppe?= Date: Tue, 3 Oct 2017 01:46:37 +0100 Subject: [PATCH 188/240] [qcommon] Use unsigned types where wrapping arithmetic is intended The use of signed types in these expressions lead to overflow, hence undefined behaviour. The "sum" aggregator in Com_TouchMemory isn't even used (and presumbably just exists to inhibit optimizations from removing the memory access). --- code/qcommon/common.c | 2 +- code/qcommon/q_math.c | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/code/qcommon/common.c b/code/qcommon/common.c index fd9f9b18de..40fbde00a4 100644 --- a/code/qcommon/common.c +++ b/code/qcommon/common.c @@ -1376,7 +1376,7 @@ Touch all known used data to make sure it is paged in void Com_TouchMemory( void ) { int start, end; int i, j; - int sum; + unsigned sum; memblock_t *block; Z_CheckHeap(); diff --git a/code/qcommon/q_math.c b/code/qcommon/q_math.c index ce47317ab3..393822569c 100644 --- a/code/qcommon/q_math.c +++ b/code/qcommon/q_math.c @@ -148,7 +148,7 @@ vec3_t bytedirs[NUMVERTEXNORMALS] = //============================================================== int Q_rand( int *seed ) { - *seed = (69069 * *seed + 1); + *seed = (69069U * *seed + 1U); return *seed; } From ac4802af8d431af0813f5def9dbb5190d8649639 Mon Sep 17 00:00:00 2001 From: Zack Middleton Date: Mon, 2 Oct 2017 20:23:16 -0500 Subject: [PATCH 189/240] OpenGL2: Fix brightness when r_autoExposure is disabled The game world is too dark when r_autoExposure is disabled. It can be fixed by setting (cheat) r_cameraExposure to 1 but then the game is too bright when r_autoExposure is enabled. So default r_cameraExposure to 1 and make auto exposure subtract 1 from r_cameraExposure value. --- code/renderergl2/tr_init.c | 2 +- code/renderergl2/tr_postprocess.c | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/code/renderergl2/tr_init.c b/code/renderergl2/tr_init.c index ea315e6605..2a91d53826 100644 --- a/code/renderergl2/tr_init.c +++ b/code/renderergl2/tr_init.c @@ -1218,7 +1218,7 @@ void R_Register( void ) r_forceAutoExposureMin = ri.Cvar_Get( "r_forceAutoExposureMin", "-2.0", CVAR_CHEAT ); r_forceAutoExposureMax = ri.Cvar_Get( "r_forceAutoExposureMax", "2.0", CVAR_CHEAT ); - r_cameraExposure = ri.Cvar_Get( "r_cameraExposure", "0", CVAR_CHEAT ); + r_cameraExposure = ri.Cvar_Get( "r_cameraExposure", "1", CVAR_CHEAT ); r_depthPrepass = ri.Cvar_Get( "r_depthPrepass", "1", CVAR_ARCHIVE ); r_ssao = ri.Cvar_Get( "r_ssao", "0", CVAR_LATCH | CVAR_ARCHIVE ); diff --git a/code/renderergl2/tr_postprocess.c b/code/renderergl2/tr_postprocess.c index ade9ebf3ae..9931757b82 100644 --- a/code/renderergl2/tr_postprocess.c +++ b/code/renderergl2/tr_postprocess.c @@ -82,7 +82,7 @@ void RB_ToneMap(FBO_t *hdrFbo, ivec4_t hdrBox, FBO_t *ldrFbo, ivec4_t ldrBox, in // tonemap color[0] = color[1] = - color[2] = pow(2, r_cameraExposure->value); //exp2(r_cameraExposure->value); + color[2] = pow(2, r_cameraExposure->value - autoExposure); //exp2(r_cameraExposure->value); color[3] = 1.0f; if (autoExposure) From d824cfa5a2ced2cfbfbc89509b8415aefd30949b Mon Sep 17 00:00:00 2001 From: Zack Middleton Date: Wed, 4 Oct 2017 21:00:00 -0500 Subject: [PATCH 190/240] OpenGL2: Fix MD3 surface with zero shaders dividing by zero Reported by @DescX. --- code/renderergl2/tr_mesh.c | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/code/renderergl2/tr_mesh.c b/code/renderergl2/tr_mesh.c index 53bccb46b2..4f2c67245b 100644 --- a/code/renderergl2/tr_mesh.c +++ b/code/renderergl2/tr_mesh.c @@ -376,12 +376,9 @@ void R_AddMD3Surfaces( trRefEntity_t *ent ) { else if (shader->defaultShader) { ri.Printf( PRINT_DEVELOPER, "WARNING: shader %s in skin %s not found\n", shader->name, skin->name); } - //} else if ( surface->numShaders <= 0 ) { - //shader = tr.defaultShader; + } else if ( surface->numShaderIndexes <= 0 ) { + shader = tr.defaultShader; } else { - //md3Shader = (md3Shader_t *) ( (byte *)surface + surface->ofsShaders ); - //md3Shader += ent->e.skinNum % surface->numShaders; - //shader = tr.shaders[ md3Shader->shaderIndex ]; shader = tr.shaders[ surface->shaderIndexes[ ent->e.skinNum % surface->numShaderIndexes ] ]; } From 39b07025509f6901fb53c91603217a57a4345091 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Thomas=20K=C3=B6ppe?= Date: Thu, 5 Oct 2017 03:41:41 +0100 Subject: [PATCH 191/240] [botlib/be_aas_def.h] Change array size from MAX_PATH to MAX_QPATH The array is part of a structure and should have a fixed size that does not depend on inclusion order. --- code/botlib/be_aas_def.h | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/code/botlib/be_aas_def.h b/code/botlib/be_aas_def.h index 4e177cfa7b..dee60f03e0 100644 --- a/code/botlib/be_aas_def.h +++ b/code/botlib/be_aas_def.h @@ -39,10 +39,6 @@ Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA #define DF_AASENTCLIENT(x) (x - aasworld.entities - 1) #define DF_CLIENTAASENT(x) (&aasworld.entities[x + 1]) -#ifndef MAX_PATH - #define MAX_PATH MAX_QPATH -#endif - //structure to link entities to areas and areas to entities typedef struct aas_link_s { @@ -187,8 +183,8 @@ typedef struct aas_s float time; int numframes; //name of the aas file - char filename[MAX_PATH]; - char mapname[MAX_PATH]; + char filename[MAX_QPATH]; + char mapname[MAX_QPATH]; //bounding boxes int numbboxes; aas_bbox_t *bboxes; From 815c898bf5717a1edff01f9434683a368b24c239 Mon Sep 17 00:00:00 2001 From: Zack Middleton Date: Wed, 4 Oct 2017 22:13:41 -0500 Subject: [PATCH 192/240] Don't redefine MAX_PATH in bot code --- code/botlib/be_aas_main.c | 7 +++---- code/botlib/be_ai_goal.c | 2 +- code/botlib/be_ai_weap.c | 2 +- code/botlib/l_precomp.c | 2 +- code/botlib/l_precomp.h | 4 ---- code/botlib/l_script.c | 8 ++------ code/botlib/l_utils.h | 3 --- code/game/ai_main.c | 16 ++++++---------- 8 files changed, 14 insertions(+), 30 deletions(-) diff --git a/code/botlib/be_aas_main.c b/code/botlib/be_aas_main.c index f4ef606ecd..bd55fda14a 100644 --- a/code/botlib/be_aas_main.c +++ b/code/botlib/be_aas_main.c @@ -220,10 +220,9 @@ void AAS_ProjectPointOntoVector( vec3_t point, vec3_t vStart, vec3_t vEnd, vec3_ int AAS_LoadFiles(const char *mapname) { int errnum; - char aasfile[MAX_PATH]; -// char bspfile[MAX_PATH]; + char aasfile[MAX_QPATH]; - strcpy(aasworld.mapname, mapname); + Q_strncpyz(aasworld.mapname, mapname, sizeof(aasworld.mapname)); //NOTE: first reset the entity links into the AAS areas and BSP leaves // the AAS link heap and BSP link heap are reset after respectively the // AAS file and BSP file are loaded @@ -232,7 +231,7 @@ int AAS_LoadFiles(const char *mapname) AAS_LoadBSPFile(); //load the aas file - Com_sprintf(aasfile, MAX_PATH, "maps/%s.aas", mapname); + Com_sprintf(aasfile, sizeof(aasfile), "maps/%s.aas", mapname); errnum = AAS_LoadAASFile(aasfile); if (errnum != BLERR_NOERROR) return errnum; diff --git a/code/botlib/be_ai_goal.c b/code/botlib/be_ai_goal.c index 7c56b56e11..0bbc6a5e84 100644 --- a/code/botlib/be_ai_goal.c +++ b/code/botlib/be_ai_goal.c @@ -268,7 +268,7 @@ itemconfig_t *LoadItemConfig(char *filename) { int max_iteminfo; token_t token; - char path[MAX_PATH]; + char path[MAX_QPATH]; source_t *source; itemconfig_t *ic; iteminfo_t *ii; diff --git a/code/botlib/be_ai_weap.c b/code/botlib/be_ai_weap.c index 88fb197c16..df6851143b 100644 --- a/code/botlib/be_ai_weap.c +++ b/code/botlib/be_ai_weap.c @@ -199,7 +199,7 @@ weaponconfig_t *LoadWeaponConfig(char *filename) { int max_weaponinfo, max_projectileinfo; token_t token; - char path[MAX_PATH]; + char path[MAX_QPATH]; int i, j; source_t *source; weaponconfig_t *wc; diff --git a/code/botlib/l_precomp.c b/code/botlib/l_precomp.c index 5c866b2ded..2fbc599fbd 100644 --- a/code/botlib/l_precomp.c +++ b/code/botlib/l_precomp.c @@ -971,7 +971,7 @@ int PC_Directive_include(source_t *source) { script_t *script; token_t token; - char path[MAX_PATH]; + char path[MAX_QPATH]; #ifdef QUAKE foundfile_t file; #endif //QUAKE diff --git a/code/botlib/l_precomp.h b/code/botlib/l_precomp.h index bdf8e9c919..cce79db912 100644 --- a/code/botlib/l_precomp.h +++ b/code/botlib/l_precomp.h @@ -29,10 +29,6 @@ Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA * *****************************************************************************/ -#ifndef MAX_PATH - #define MAX_PATH MAX_QPATH -#endif - #ifndef PATH_SEPERATORSTR #if defined(WIN32)|defined(_WIN32)|defined(__NT__)|defined(__WINDOWS__)|defined(__WINDOWS_386__) #define PATHSEPERATOR_STR "\\" diff --git a/code/botlib/l_script.c b/code/botlib/l_script.c index 23694386fe..c1c3266a11 100644 --- a/code/botlib/l_script.c +++ b/code/botlib/l_script.c @@ -160,9 +160,7 @@ punctuation_t default_punctuations[] = {NULL, 0} }; -#ifdef BSPC -char basefolder[MAX_PATH]; -#else +#ifdef BOTLIB char basefolder[MAX_QPATH]; #endif @@ -1441,9 +1439,7 @@ void FreeScript(script_t *script) //============================================================================ void PS_SetBaseFolder(char *path) { -#ifdef BSPC - sprintf(basefolder, path); -#else +#ifdef BOTLIB Com_sprintf(basefolder, sizeof(basefolder), "%s", path); #endif } //end of the function PS_SetBaseFolder diff --git a/code/botlib/l_utils.h b/code/botlib/l_utils.h index 6944d06f99..0c7e6fa4fe 100644 --- a/code/botlib/l_utils.h +++ b/code/botlib/l_utils.h @@ -30,8 +30,5 @@ Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA *****************************************************************************/ #define Vector2Angles(v,a) vectoangles(v,a) -#ifndef MAX_PATH -#define MAX_PATH MAX_QPATH -#endif #define Maximum(x,y) (x > y ? x : y) #define Minimum(x,y) (x < y ? x : y) diff --git a/code/game/ai_main.c b/code/game/ai_main.c index 907a161d9f..4b350d0f46 100644 --- a/code/game/ai_main.c +++ b/code/game/ai_main.c @@ -55,10 +55,6 @@ Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA #include "inv.h" #include "syn.h" -#ifndef MAX_PATH -#define MAX_PATH 144 -#endif - //bot states bot_state_t *botstates[MAX_CLIENTS]; @@ -1174,7 +1170,7 @@ BotAISetupClient ============== */ int BotAISetupClient(int client, struct bot_settings_s *settings, qboolean restart) { - char filename[MAX_PATH], name[MAX_PATH], gender[MAX_PATH]; + char filename[144], name[144], gender[144]; bot_state_t *bs; int errnum; @@ -1206,7 +1202,7 @@ int BotAISetupClient(int client, struct bot_settings_s *settings, qboolean resta //allocate a goal state bs->gs = trap_BotAllocGoalState(client); //load the item weights - trap_Characteristic_String(bs->character, CHARACTERISTIC_ITEMWEIGHTS, filename, MAX_PATH); + trap_Characteristic_String(bs->character, CHARACTERISTIC_ITEMWEIGHTS, filename, sizeof(filename)); errnum = trap_BotLoadItemWeights(bs->gs, filename); if (errnum != BLERR_NOERROR) { trap_BotFreeGoalState(bs->gs); @@ -1215,7 +1211,7 @@ int BotAISetupClient(int client, struct bot_settings_s *settings, qboolean resta //allocate a weapon state bs->ws = trap_BotAllocWeaponState(); //load the weapon weights - trap_Characteristic_String(bs->character, CHARACTERISTIC_WEAPONWEIGHTS, filename, MAX_PATH); + trap_Characteristic_String(bs->character, CHARACTERISTIC_WEAPONWEIGHTS, filename, sizeof(filename)); errnum = trap_BotLoadWeaponWeights(bs->ws, filename); if (errnum != BLERR_NOERROR) { trap_BotFreeGoalState(bs->gs); @@ -1225,8 +1221,8 @@ int BotAISetupClient(int client, struct bot_settings_s *settings, qboolean resta //allocate a chat state bs->cs = trap_BotAllocChatState(); //load the chat file - trap_Characteristic_String(bs->character, CHARACTERISTIC_CHAT_FILE, filename, MAX_PATH); - trap_Characteristic_String(bs->character, CHARACTERISTIC_CHAT_NAME, name, MAX_PATH); + trap_Characteristic_String(bs->character, CHARACTERISTIC_CHAT_FILE, filename, sizeof(filename)); + trap_Characteristic_String(bs->character, CHARACTERISTIC_CHAT_NAME, name, sizeof(name)); errnum = trap_BotLoadChatFile(bs->cs, filename, name); if (errnum != BLERR_NOERROR) { trap_BotFreeChatState(bs->cs); @@ -1235,7 +1231,7 @@ int BotAISetupClient(int client, struct bot_settings_s *settings, qboolean resta return qfalse; } //get the gender characteristic - trap_Characteristic_String(bs->character, CHARACTERISTIC_GENDER, gender, MAX_PATH); + trap_Characteristic_String(bs->character, CHARACTERISTIC_GENDER, gender, sizeof(gender)); //set the chat gender if (*gender == 'f' || *gender == 'F') trap_BotSetChatGender(bs->cs, CHAT_GENDERFEMALE); else if (*gender == 'm' || *gender == 'M') trap_BotSetChatGender(bs->cs, CHAT_GENDERMALE); From e46e9846fced5025bfa446500be84f537f962072 Mon Sep 17 00:00:00 2001 From: Zack Middleton Date: Sat, 7 Oct 2017 00:51:19 -0500 Subject: [PATCH 193/240] Fix memory leak in (unused) AAS_FloodAreas() --- code/botlib/be_aas_debug.c | 1 + 1 file changed, 1 insertion(+) diff --git a/code/botlib/be_aas_debug.c b/code/botlib/be_aas_debug.c index ab44bc0bc1..b9a1465b4e 100644 --- a/code/botlib/be_aas_debug.c +++ b/code/botlib/be_aas_debug.c @@ -774,4 +774,5 @@ void AAS_FloodAreas(vec3_t origin) areanum = AAS_PointAreaNum(origin); cluster = AAS_AreaCluster(areanum); AAS_FloodAreas_r(areanum, cluster, done); + FreeMemory(done); } From 96f94a2891eeaa4b486de2c174ed8c00fcf0d271 Mon Sep 17 00:00:00 2001 From: Walter Barrett Date: Sat, 7 Oct 2017 00:23:34 -0400 Subject: [PATCH 194/240] Fix compiling GLSL shaders under Windows. Now the `sed` command cope with files using Windows-style line endings. --- Makefile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Makefile b/Makefile index 464ad3c4ee..de2b35268b 100644 --- a/Makefile +++ b/Makefile @@ -1183,7 +1183,7 @@ define DO_REF_STR $(echo_cmd) "REF_STR $<" $(Q)rm -f $@ $(Q)echo "const char *fallbackShader_$(notdir $(basename $<)) =" >> $@ -$(Q)cat $< | sed 's/^/\"/;s/$$/\\n\"/' >> $@ +$(Q)cat $< | sed -e 's/^/\"/;s/$$/\\n\"/' -e 's/\r//g' >> $@ $(Q)echo ";" >> $@ endef From 9e502bda4d5ce7ca57800e94ad8c7478f543d7b8 Mon Sep 17 00:00:00 2001 From: Zack Middleton Date: Sat, 7 Oct 2017 10:13:46 -0500 Subject: [PATCH 195/240] Only draw cm_patch/bot debug polygons in world scenes Fixes debug polygons appearing in HUD head model scene. --- code/renderergl1/tr_main.c | 3 +++ code/renderergl2/tr_main.c | 3 +++ 2 files changed, 6 insertions(+) diff --git a/code/renderergl1/tr_main.c b/code/renderergl1/tr_main.c index 0e388314df..09559a2e71 100644 --- a/code/renderergl1/tr_main.c +++ b/code/renderergl1/tr_main.c @@ -1332,6 +1332,9 @@ Visualization aid for movement clipping debugging ==================== */ void R_DebugGraphics( void ) { + if ( tr.refdef.rdflags & RDF_NOWORLDMODEL ) { + return; + } if ( !r_debugSurface->integer ) { return; } diff --git a/code/renderergl2/tr_main.c b/code/renderergl2/tr_main.c index 1f66d90bb9..02f7d489fb 100644 --- a/code/renderergl2/tr_main.c +++ b/code/renderergl2/tr_main.c @@ -1715,6 +1715,9 @@ Visualization aid for movement clipping debugging ==================== */ void R_DebugGraphics( void ) { + if ( tr.refdef.rdflags & RDF_NOWORLDMODEL ) { + return; + } if ( !r_debugSurface->integer ) { return; } From b4a4fe98d4b8fad7e477b78217972384225dfbe9 Mon Sep 17 00:00:00 2001 From: Zack Middleton Date: Sun, 8 Oct 2017 07:07:53 -0500 Subject: [PATCH 196/240] Fix reading crash log when log wraps around buffer --- code/sys/con_log.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/code/sys/con_log.c b/code/sys/con_log.c index c3a4a5b841..bc1c5aef28 100644 --- a/code/sys/con_log.c +++ b/code/sys/con_log.c @@ -121,7 +121,7 @@ unsigned int CON_LogRead( char *out, unsigned int outSize ) } Com_Memcpy( out, consoleLog + readPos, firstChunk ); - Com_Memcpy( out + firstChunk, out, secondChunk ); + Com_Memcpy( out + firstChunk, consoleLog, secondChunk ); readPos = ( readPos + outSize ) % MAX_LOG; From 690c5a4dac3c3d0d59ee271aadd8f19a29a9f338 Mon Sep 17 00:00:00 2001 From: Zack Middleton Date: Sun, 8 Oct 2017 11:19:40 -0500 Subject: [PATCH 197/240] Don't send team overlay info to bots --- code/game/g_bot.c | 3 +++ code/game/g_client.c | 2 +- 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/code/game/g_bot.c b/code/game/g_bot.c index af4906a339..b5731a67a5 100644 --- a/code/game/g_bot.c +++ b/code/game/g_bot.c @@ -724,6 +724,9 @@ static void G_AddBot( const char *name, float skill, const char *team, int delay } Info_SetValueForKey( userinfo, "characterfile", s ); + // don't send tinfo to bots, they don't parse it + Info_SetValueForKey( userinfo, "teamoverlay", "0" ); + // register the userinfo trap_SetUserinfo( clientNum, userinfo ); diff --git a/code/game/g_client.c b/code/game/g_client.c index 89f20982d0..3366d176f8 100644 --- a/code/game/g_client.c +++ b/code/game/g_client.c @@ -803,7 +803,7 @@ void ClientUserinfoChanged( int clientNum ) { */ #ifdef MISSIONPACK - if (g_gametype.integer >= GT_TEAM) { + if (g_gametype.integer >= GT_TEAM && !(ent->r.svFlags & SVF_BOT)) { client->pers.teamInfo = qtrue; } else { s = Info_ValueForKey( userinfo, "teamoverlay" ); From 7d012f229e4b04c21fd1adb9a053cad4f86f8742 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Micka=C3=ABl=20Thomas?= Date: Mon, 16 Oct 2017 16:44:06 +0200 Subject: [PATCH 198/240] Fix a race condition in the makedirs target A race condition can happen when running "make all" with parallel jobs. The issue is that the build directory can be created by another concurrent job between the moment it was detected as missing and the moment mkdir is called (which fails if the directory already exists). This fixes the problem by always using `mkdir -p` which doesn't fail if the directory already exists. --- Makefile | 59 +++++++++++++++++++++++++------------------------------- 1 file changed, 26 insertions(+), 33 deletions(-) diff --git a/Makefile b/Makefile index de2b35268b..08f7623f93 100644 --- a/Makefile +++ b/Makefile @@ -322,7 +322,7 @@ endif ############################################################################# INSTALL=install -MKDIR=mkdir +MKDIR=mkdir -p EXTRA_FILES= CLIENT_EXTRA_FILES= @@ -836,7 +836,6 @@ ifeq ($(PLATFORM),irix64) ARCH=mips CC = c99 - MKDIR = mkdir -p BASE_CFLAGS=-Dstricmp=strcasecmp -Xcpluscomm -woff 1185 \ -I. -I$(ROOT)/usr/include @@ -865,7 +864,7 @@ ifeq ($(PLATFORM),sunos) CC=gcc INSTALL=ginstall - MKDIR=gmkdir + MKDIR=gmkdir -p COPYDIR="/usr/local/share/games/quake3" ifneq ($(ARCH),x86) @@ -1379,34 +1378,28 @@ ifneq ($(PLATFORM),darwin) endif makedirs: - @if [ ! -d $(BUILD_DIR) ];then $(MKDIR) $(BUILD_DIR);fi - @if [ ! -d $(B) ];then $(MKDIR) $(B);fi - @if [ ! -d $(B)/autoupdater ];then $(MKDIR) $(B)/autoupdater;fi - @if [ ! -d $(B)/client ];then $(MKDIR) $(B)/client;fi - @if [ ! -d $(B)/client/opus ];then $(MKDIR) $(B)/client/opus;fi - @if [ ! -d $(B)/client/vorbis ];then $(MKDIR) $(B)/client/vorbis;fi - @if [ ! -d $(B)/renderergl1 ];then $(MKDIR) $(B)/renderergl1;fi - @if [ ! -d $(B)/renderergl2 ];then $(MKDIR) $(B)/renderergl2;fi - @if [ ! -d $(B)/renderergl2/glsl ];then $(MKDIR) $(B)/renderergl2/glsl;fi - @if [ ! -d $(B)/ded ];then $(MKDIR) $(B)/ded;fi - @if [ ! -d $(B)/$(BASEGAME) ];then $(MKDIR) $(B)/$(BASEGAME);fi - @if [ ! -d $(B)/$(BASEGAME)/cgame ];then $(MKDIR) $(B)/$(BASEGAME)/cgame;fi - @if [ ! -d $(B)/$(BASEGAME)/game ];then $(MKDIR) $(B)/$(BASEGAME)/game;fi - @if [ ! -d $(B)/$(BASEGAME)/ui ];then $(MKDIR) $(B)/$(BASEGAME)/ui;fi - @if [ ! -d $(B)/$(BASEGAME)/qcommon ];then $(MKDIR) $(B)/$(BASEGAME)/qcommon;fi - @if [ ! -d $(B)/$(BASEGAME)/vm ];then $(MKDIR) $(B)/$(BASEGAME)/vm;fi - @if [ ! -d $(B)/$(MISSIONPACK) ];then $(MKDIR) $(B)/$(MISSIONPACK);fi - @if [ ! -d $(B)/$(MISSIONPACK)/cgame ];then $(MKDIR) $(B)/$(MISSIONPACK)/cgame;fi - @if [ ! -d $(B)/$(MISSIONPACK)/game ];then $(MKDIR) $(B)/$(MISSIONPACK)/game;fi - @if [ ! -d $(B)/$(MISSIONPACK)/ui ];then $(MKDIR) $(B)/$(MISSIONPACK)/ui;fi - @if [ ! -d $(B)/$(MISSIONPACK)/qcommon ];then $(MKDIR) $(B)/$(MISSIONPACK)/qcommon;fi - @if [ ! -d $(B)/$(MISSIONPACK)/vm ];then $(MKDIR) $(B)/$(MISSIONPACK)/vm;fi - @if [ ! -d $(B)/tools ];then $(MKDIR) $(B)/tools;fi - @if [ ! -d $(B)/tools/asm ];then $(MKDIR) $(B)/tools/asm;fi - @if [ ! -d $(B)/tools/etc ];then $(MKDIR) $(B)/tools/etc;fi - @if [ ! -d $(B)/tools/rcc ];then $(MKDIR) $(B)/tools/rcc;fi - @if [ ! -d $(B)/tools/cpp ];then $(MKDIR) $(B)/tools/cpp;fi - @if [ ! -d $(B)/tools/lburg ];then $(MKDIR) $(B)/tools/lburg;fi + @$(MKDIR) $(B)/autoupdater + @$(MKDIR) $(B)/client/opus + @$(MKDIR) $(B)/client/vorbis + @$(MKDIR) $(B)/renderergl1 + @$(MKDIR) $(B)/renderergl2 + @$(MKDIR) $(B)/renderergl2/glsl + @$(MKDIR) $(B)/ded + @$(MKDIR) $(B)/$(BASEGAME)/cgame + @$(MKDIR) $(B)/$(BASEGAME)/game + @$(MKDIR) $(B)/$(BASEGAME)/ui + @$(MKDIR) $(B)/$(BASEGAME)/qcommon + @$(MKDIR) $(B)/$(BASEGAME)/vm + @$(MKDIR) $(B)/$(MISSIONPACK)/cgame + @$(MKDIR) $(B)/$(MISSIONPACK)/game + @$(MKDIR) $(B)/$(MISSIONPACK)/ui + @$(MKDIR) $(B)/$(MISSIONPACK)/qcommon + @$(MKDIR) $(B)/$(MISSIONPACK)/vm + @$(MKDIR) $(B)/tools/asm + @$(MKDIR) $(B)/tools/etc + @$(MKDIR) $(B)/tools/rcc + @$(MKDIR) $(B)/tools/cpp + @$(MKDIR) $(B)/tools/lburg ############################################################################# # QVM BUILD TOOLS @@ -2831,10 +2824,10 @@ copyfiles: release @if [ ! -d $(COPYDIR)/$(BASEGAME) ]; then echo "You need to set COPYDIR to where your Quake3 data is!"; fi ifneq ($(BUILD_GAME_SO),0) ifneq ($(BUILD_BASEGAME),0) - -$(MKDIR) -p -m 0755 $(COPYDIR)/$(BASEGAME) + -$(MKDIR) -m 0755 $(COPYDIR)/$(BASEGAME) endif ifneq ($(BUILD_MISSIONPACK),0) - -$(MKDIR) -p -m 0755 $(COPYDIR)/$(MISSIONPACK) + -$(MKDIR) -m 0755 $(COPYDIR)/$(MISSIONPACK) endif endif From 6ecfa4f26387f46d2dea62cd632152559a8b7b2e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Micka=C3=ABl=20Thomas?= Date: Mon, 16 Oct 2017 17:12:43 +0200 Subject: [PATCH 199/240] Fix shader corruption on OpenBSD OpenBSD's sed (and possibly other platforms') interprets `\r` as a literal `r` rather than a carriage return, which leads to all `r` letters being stripped from the shaders' source. This fixes the issue by using the POSIX-compliant `tr -d '\r'` to remove carriage returns. Thanks to @ryan-sg for reporting the issue --- Makefile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Makefile b/Makefile index 08f7623f93..2ed9af8e32 100644 --- a/Makefile +++ b/Makefile @@ -1182,7 +1182,7 @@ define DO_REF_STR $(echo_cmd) "REF_STR $<" $(Q)rm -f $@ $(Q)echo "const char *fallbackShader_$(notdir $(basename $<)) =" >> $@ -$(Q)cat $< | sed -e 's/^/\"/;s/$$/\\n\"/' -e 's/\r//g' >> $@ +$(Q)cat $< | sed -e 's/^/\"/;s/$$/\\n\"/' | tr -d '\r' >> $@ $(Q)echo ";" >> $@ endef From e8450cae33fbfaa24202e28dce1a49fa88e857c4 Mon Sep 17 00:00:00 2001 From: Zack Middleton Date: Sun, 5 Nov 2017 20:58:34 -0600 Subject: [PATCH 200/240] OpenGL2: Use ri.Error instead of Com_Error in tr_vbo.c Com_Error is a wrapper for ri.Error. Use it directly. --- code/renderergl2/tr_vbo.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/code/renderergl2/tr_vbo.c b/code/renderergl2/tr_vbo.c index e4d0ca372f..89e991121d 100644 --- a/code/renderergl2/tr_vbo.c +++ b/code/renderergl2/tr_vbo.c @@ -115,7 +115,7 @@ vao_t *R_CreateVao(const char *name, byte *vertexes, int vertexesSize, byte *ind break; default: - Com_Error(ERR_FATAL, "bad vaoUsage_t given: %i", usage); + ri.Error(ERR_FATAL, "bad vaoUsage_t given: %i", usage); return NULL; } From 471ea9e56411606e814a703419433494a71a3f42 Mon Sep 17 00:00:00 2001 From: Zack Middleton Date: Fri, 10 Nov 2017 20:05:51 -0600 Subject: [PATCH 201/240] Fix Team Arena server refresh time format Make minute less than 10 have a leading 0. Change '7:1' to '7:01'. --- code/ui/ui_main.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/code/ui/ui_main.c b/code/ui/ui_main.c index bd8c515dd0..32b6acd99b 100644 --- a/code/ui/ui_main.c +++ b/code/ui/ui_main.c @@ -6021,7 +6021,7 @@ static void UI_StartServerRefresh(qboolean full, qboolean force) } trap_RealTime(&q); - trap_Cvar_Set( va("ui_lastServerRefresh_%i", ui_netSource.integer), va("%s-%i, %i at %i:%i", MonthAbbrev[q.tm_mon],q.tm_mday, 1900+q.tm_year,q.tm_hour,q.tm_min)); + trap_Cvar_Set( va("ui_lastServerRefresh_%i", ui_netSource.integer), va("%s-%i, %i at %i:%02i", MonthAbbrev[q.tm_mon],q.tm_mday, 1900+q.tm_year,q.tm_hour,q.tm_min)); if (!full) { UI_UpdatePendingPings(); From 4af2c91fbfd0b9032b89aca17a3553216256dfe1 Mon Sep 17 00:00:00 2001 From: Zack Middleton Date: Fri, 10 Nov 2017 20:28:50 -0600 Subject: [PATCH 202/240] Fix -1 (unlimited) ammo decreasing ammo time remaining Gauntlet and Grappling Hook use -1 ammo. Gauntlet is excluded from the check ammo loop but Grappling Hook causes ammo time remaining to decrease 200 milliseconds. The out of ammo check tests time remaining is equal to zero. This means carrying Grappling Hook and out of ammo will have negative time remaining which results in the low ammo message being displayed instead of out of ammo. --- code/cgame/cg_playerstate.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/code/cgame/cg_playerstate.c b/code/cgame/cg_playerstate.c index c696a91f4a..fa79bcb36d 100644 --- a/code/cgame/cg_playerstate.c +++ b/code/cgame/cg_playerstate.c @@ -47,6 +47,9 @@ void CG_CheckAmmo( void ) { if ( ! ( weapons & ( 1 << i ) ) ) { continue; } + if ( cg.cur_ps->ammo[i] < 0 ) { + continue; + } switch ( i ) { case WP_ROCKET_LAUNCHER: case WP_GRENADE_LAUNCHER: From 3e1599ac4bed2adb125600fd99b44f4167a75cd9 Mon Sep 17 00:00:00 2001 From: Zack Middleton Date: Fri, 10 Nov 2017 21:09:01 -0600 Subject: [PATCH 203/240] Fix my previous commit about -1 ammo --- code/cgame/cg_playerstate.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/code/cgame/cg_playerstate.c b/code/cgame/cg_playerstate.c index fa79bcb36d..f48e7b3b08 100644 --- a/code/cgame/cg_playerstate.c +++ b/code/cgame/cg_playerstate.c @@ -47,7 +47,7 @@ void CG_CheckAmmo( void ) { if ( ! ( weapons & ( 1 << i ) ) ) { continue; } - if ( cg.cur_ps->ammo[i] < 0 ) { + if ( cg.snap->ps.ammo[i] < 0 ) { continue; } switch ( i ) { From 14cb72f91219b760d21ee50a5a663ab3dcd28fdb Mon Sep 17 00:00:00 2001 From: Matt Palmer Date: Wed, 22 Nov 2017 16:07:54 +1100 Subject: [PATCH 204/240] Improvements to the linux build script In no particular order: * Use `set -e`, because it prevents accidents, and means we can avoid lengthy &&-joined command chains. * Override defaults by setting env vars; this means people don't have to edit the script to change things. * Use an unpredictable and safely-created tmpdir for building; ain't nobody wants to cleanup from a tmpdir race condition attack. * Test for the presence of `git` and `make` *before* asking questions, and only prompt the user about them if they're missing. No need to bother people with unnecessary reading. * Automatically clean up the build directory after use. * Tidy up some indenting that had come asunder. --- misc/linux/server_compile.sh | 75 +++++++++++++++++++++++------------- 1 file changed, 49 insertions(+), 26 deletions(-) diff --git a/misc/linux/server_compile.sh b/misc/linux/server_compile.sh index 66bdc65206..b4e10377b1 100644 --- a/misc/linux/server_compile.sh +++ b/misc/linux/server_compile.sh @@ -1,30 +1,53 @@ #!/bin/bash -localPATH=`pwd` -export BUILD_CLIENT="0" -export BUILD_SERVER="1" -export USE_CURL="1" -export USE_CODEC_OPUS="1" -export USE_VOIP="1" -export COPYDIR="~/ioquake3" -IOQ3REMOTE="https://github.com/ioquake/ioq3.git" -IOQ3LOCAL="/tmp/ioquake3compile" -JOPTS="-j2" -echo "This process requires you to have the following installed through your distribution: - make - git - and all of the ioquake3 dependencies necessary for an ioquake3 server. - If you do not have the necessary dependencies this script will bail out. + +set -e + +export BUILD_CLIENT="${BUILD_CLIENT:-0}" +export BUILD_SERVER="${BUILD_SERVER:-1}" +export USE_CURL="${USE_CURL:-1}" +export USE_CODEC_OPUS="${USE_CODEC_OPUS:-1}" +export USE_VOIP="${USE_VOIP:-1}" +export COPYDIR="${COPYDIR:-~/ioquake3}" +IOQ3REMOTE="${IOQ3REMOTE:-https://github.com/ioquake/ioq3.git}" +MAKE_OPTS="${MAKE_OPTS:--j2}" + +if ! [ -x "$(command -v git)" ] || ! [ -x "$(command -v make)" ]; then + echo "This build script requires 'git' and 'make' to be installed." >&2 + echo "Please install them through your normal package installation system." >&2 + exit 1 +fi + +echo " This build process requires all of the ioquake3 dependencies necessary for an ioquake3 server. + If you do not have the necessary dependencies the build will fail. + Please post a message to http://discourse.ioquake.org/ asking for help and include whatever error messages you received during the compile phase. - Please edit this script. Inside you will find a COPYDIR variable you can alter to change where ioquake3 will be installed to." + + We will be building from the git repo at ${IOQ3REMOTE} + The resulting binary will be installed to ${COPYDIR} + + If you need to change these, please set variables as follows: + + IOQ3REMOTE=https://github.com/something/something.git COPYDIR=~/somewhere $0" + +BUILD_DIR="$(mktemp -d)" +trap "rm -rf $BUILD_DIR" EXIT + while true; do - read -p "Are you ready to compile ioquake3 in the $IOQ3LOCAL directory, and have it installed into $COPYDIR? " yn -case $yn in - [Yy]* ) -if [ -x "$(command -v git)" ] && [ -x "$(command -v make)" ] ; then - git clone $IOQ3REMOTE $IOQ3LOCAL && cd $IOQ3LOCAL && make $JOPTS && make copyfiles && cd $localPATH && rm -rf $IOQ3LOCAL -fi - exit;; - [Nn]* ) exit;; - * ) echo "Please answer yes or no.";; -esac + read -p "Are you ready to compile ioquake3 from ${IOQ3REMOTE}, and have it installed into $COPYDIR? " yn + case $yn in + [Yy]*) + git clone $IOQ3REMOTE $BUILD_DIR/ioq3 + cd $BUILD_DIR/ioq3 + make $MAKE_OPTS + make copyfiles + exit + ;; + [Nn]*) + echo "aborting installation." + exit + ;; + *) + echo "Please answer yes or no." + ;; + esac done From fe42b8653d8bcb213a3532a1cbacc0e1c6854a87 Mon Sep 17 00:00:00 2001 From: Edward Betts Date: Wed, 22 Nov 2017 07:40:20 +0000 Subject: [PATCH 205/240] Correct spelling mistakes. --- ChangeLog | 18 +++++++++--------- code/botlib/aasfile.h | 2 +- code/botlib/be_aas_bspq3.c | 2 +- code/botlib/be_aas_cluster.c | 6 +++--- code/botlib/be_aas_main.c | 2 +- code/botlib/be_aas_reach.c | 4 ++-- code/botlib/be_aas_sample.c | 6 +++--- code/botlib/be_ai_chat.c | 4 ++-- code/botlib/be_ai_gen.c | 2 +- code/botlib/be_ai_move.c | 6 +++--- code/botlib/be_ai_weight.c | 2 +- code/botlib/be_ai_weight.h | 2 +- code/botlib/l_script.c | 4 ++-- code/cgame/cg_local.h | 8 ++++---- code/cgame/cg_main.c | 2 +- code/cgame/cg_players.c | 2 +- code/cgame/cg_public.h | 2 +- code/cgame/cg_servercmds.c | 2 +- code/cgame/cg_snapshot.c | 2 +- code/cgame/cg_view.c | 2 +- code/cgame/cg_weapons.c | 2 +- code/client/cl_cgame.c | 4 ++-- code/client/cl_input.c | 2 +- code/client/cl_keys.c | 8 ++++---- code/client/cl_main.c | 8 ++++---- code/client/snd_adpcm.c | 2 +- code/client/snd_dma.c | 2 +- code/client/snd_mix.c | 2 +- code/game/ai_dmnet.c | 2 +- code/game/ai_dmq3.c | 4 ++-- code/game/ai_dmq3.h | 2 +- code/game/ai_main.c | 2 +- code/game/ai_team.c | 8 ++++---- code/game/bg_lib.c | 2 +- code/game/bg_pmove.c | 4 ++-- code/game/bg_public.h | 2 +- code/game/g_active.c | 4 ++-- code/game/g_client.c | 6 +++--- code/game/g_cmds.c | 6 +++--- code/game/g_items.c | 2 +- code/game/g_missile.c | 2 +- code/game/g_mover.c | 8 ++++---- code/game/g_spawn.c | 2 +- code/game/g_target.c | 2 +- code/game/g_trigger.c | 2 +- code/game/g_weapon.c | 4 ++-- code/q3_ui/ui_atoms.c | 2 +- code/q3_ui/ui_local.h | 2 +- code/q3_ui/ui_playermodel.c | 8 ++++---- code/q3_ui/ui_qmenu.c | 14 +++++++------- code/qcommon/cm_patch.c | 14 +++++++------- code/qcommon/cm_polylib.c | 6 +++--- code/qcommon/cm_trace.c | 16 ++++++++-------- code/qcommon/cmd.c | 4 ++-- code/qcommon/common.c | 12 ++++++------ code/qcommon/files.c | 6 +++--- code/qcommon/q_shared.h | 4 ++-- code/qcommon/qcommon.h | 2 +- code/qcommon/surfaceflags.h | 2 +- code/qcommon/unzip.c | 6 +++--- code/qcommon/unzip.h | 4 ++-- code/qcommon/vm_powerpc.c | 4 ++-- code/renderercommon/tr_image_png.c | 4 ++-- code/renderercommon/tr_public.h | 2 +- code/renderercommon/tr_types.h | 2 +- code/renderergl1/tr_backend.c | 4 ++-- code/renderergl1/tr_bsp.c | 2 +- code/renderergl1/tr_image.c | 4 ++-- code/renderergl1/tr_light.c | 4 ++-- code/renderergl1/tr_local.h | 4 ++-- code/renderergl1/tr_shade.c | 6 +++--- code/renderergl1/tr_shade_calc.c | 4 ++-- code/renderergl1/tr_shader.c | 12 ++++++------ code/renderergl1/tr_surface.c | 4 ++-- code/renderergl2/tr_backend.c | 4 ++-- code/renderergl2/tr_bsp.c | 2 +- code/renderergl2/tr_fbo.c | 2 +- code/renderergl2/tr_image.c | 4 ++-- code/renderergl2/tr_light.c | 4 ++-- code/renderergl2/tr_local.h | 2 +- code/renderergl2/tr_shade.c | 6 +++--- code/renderergl2/tr_shade_calc.c | 2 +- code/renderergl2/tr_shader.c | 8 ++++---- code/server/server.h | 4 ++-- code/server/sv_init.c | 6 +++--- code/server/sv_main.c | 2 +- code/server/sv_snapshot.c | 2 +- code/sys/sys_unix.c | 2 +- code/tools/asm/cmdlib.c | 4 ++-- code/tools/asm/cmdlib.h | 2 +- code/tools/asm/notes.txt | 2 +- code/tools/asm/q3asm.c | 8 ++++---- code/tools/lcc/doc/4.html | 2 +- code/tools/lcc/etc/lcc.c | 2 +- code/tools/lcc/src/enode.c | 2 +- code/tools/lcc/src/error.c | 2 +- code/tools/lcc/src/gen.c | 2 +- code/tools/lcc/src/init.c | 2 +- code/tools/lcc/src/tree.c | 2 +- code/ui/ui_local.h | 2 +- code/ui/ui_main.c | 2 +- code/ui/ui_shared.c | 2 +- make-macosx-ub.sh | 2 +- make-macosx.sh | 2 +- opengl2-readme.md | 2 +- 105 files changed, 212 insertions(+), 212 deletions(-) diff --git a/ChangeLog b/ChangeLog index ab963fe35b..e26df8c6b7 100644 --- a/ChangeLog +++ b/ChangeLog @@ -634,7 +634,7 @@ #1 current directory #2 fs_homepath #3 fs_basepath - this was needed to make mod developement easier + this was needed to make mod development easier 2001-10-09 Timothee Besset + https://zerowing.idsoftware.com/bugzilla/show_bug.cgi?id=51 @@ -814,7 +814,7 @@ * rebuilding 1.28b, various fixes on linux build: - SetProgramPath was renamed to Sys_SetDefaultCDPath in unix_shared.c updated unix_main.c accordingly - - some prototypes in qgl.h are guarded by #ifndef GL_VERSION_1_2 (ARB extentions) + - some prototypes in qgl.h are guarded by #ifndef GL_VERSION_1_2 (ARB extensions) those prototypes are needed by linux_glimp for importing functions and casting, added a #ifdef __linux__ (not a clean solution) - game/q_shared.h @@ -861,7 +861,7 @@ 2001-04-23 Timothee Besset * cleanup the mod selection code, remove duplicates - * some issues with release builds, my main developement box doesn't build stable binaries with release settings + * some issues with release builds, my main development box doesn't build stable binaries with release settings removing -fomit-frame-pointer seems to fix (there's probably a performance hit) see OMIT-FRAME-POINTER.txt @@ -984,7 +984,7 @@ //* or // /* or variations of this. I reverted to exact mirror image of SOS to be sure - short of removing it's too easy to mistake live code for dead one. - Later: have to change 5 occurences to avoid gcc complaints about + Later: have to change 5 occurrences to avoid gcc complaints about nested comment tokens. TODO: somebody please get rid of the cruft in here. @@ -1235,7 +1235,7 @@ * code/game/g_cmds.c (G_SayTo): CON_CONNECTED. * code/game/ai_main.c: HOOK added (SOS). * code/botlib/be_aas_move.c (AAS_HorizontalVelocityForJump): - correct fix for FPE occuring (SOS). + correct fix for FPE occurring (SOS). * code/game/ai_dmq3.c: initmove.viewoffset (SOS). * code/game/q_math.c: guard asser/isnan with Q3_VM (q3asm). @@ -1679,7 +1679,7 @@ * code/game/q_shared.c: Q_strncpyz does zero padding (duh). Note: calls strncpy, which does a zero fill up to destsize. If destsize exceeds memory size, zero padding will overwrite - adjacent memory. Suspicion was this happend to botimport. + adjacent memory. Suspicion was this happened to botimport. * code/qcommon/cvar.c: possible problem in Q_strncpyz call. @@ -1783,7 +1783,7 @@ * TEST: running with RC4 data files. TODO: "bot library used before setup" (Q3+TA) TODO: Q3 old mods wreak havoc (graceful bounce) - TODO: supress "FreeType code not available" in renderer + TODO: suppress "FreeType code not available" in renderer TODO: can't move in Q3 TODO: items flicker in Q3 TODO: no decals in Q3 @@ -2015,7 +2015,7 @@ * TEST: tried executing a script - get bounced. TODO: is there any way to jump into a map? - TODO: cl_cinematics 0 (supress all fullscreen RoQ) + TODO: cl_cinematics 0 (suppress all fullscreen RoQ) Next: used r_logfile 200 in Win32 (RC4) and Linux. There is a buckload of setup code seemingly not done at all in Linux? Either that, or logging is enabled @@ -2983,7 +2983,7 @@ Modules: code: the Q3 engine code, including a jpeg-6/ copy common: code shared by tools - libs: code shared by tools, inlcuding a jpeg6/ copy + libs: code shared by tools, including a jpeg6/ copy q3asm: VM bytecode assembly q3data: misc. Q3 data conversions q3map: BSP builder diff --git a/code/botlib/aasfile.h b/code/botlib/aasfile.h index a0593df7c5..f23dbab110 100644 --- a/code/botlib/aasfile.h +++ b/code/botlib/aasfile.h @@ -191,7 +191,7 @@ typedef struct aas_edge_s //edge index, negative if vertexes are reversed typedef int aas_edgeindex_t; -//a face bounds an area, often it will also seperate two areas +//a face bounds an area, often it will also separate two areas typedef struct aas_face_s { int planenum; //number of the plane this face is in diff --git a/code/botlib/be_aas_bspq3.c b/code/botlib/be_aas_bspq3.c index a6b6edb0f4..34862bcbe4 100644 --- a/code/botlib/be_aas_bspq3.c +++ b/code/botlib/be_aas_bspq3.c @@ -70,7 +70,7 @@ typedef struct bsp_entity_s bsp_epair_t *epairs; } bsp_entity_t; -//id Sofware BSP data +//id Software BSP data typedef struct bsp_s { //true when bsp file is loaded diff --git a/code/botlib/be_aas_cluster.c b/code/botlib/be_aas_cluster.c index c2fed970b4..f23d7a675c 100644 --- a/code/botlib/be_aas_cluster.c +++ b/code/botlib/be_aas_cluster.c @@ -1168,7 +1168,7 @@ void AAS_RemoveNotClusterClosingPortals(void) if (aasworld.areasettings[otherareanum].contents & AREACONTENTS_CLUSTERPORTAL) continue; //if the area already has a cluster set if (aasworld.areasettings[otherareanum].cluster) continue; - //another cluster is seperated by this portal + //another cluster is separated by this portal numseperatedclusters++; //flood the cluster AAS_FloodCluster_r(otherareanum, numseperatedclusters); @@ -1185,13 +1185,13 @@ void AAS_RemoveNotClusterClosingPortals(void) if (aasworld.areasettings[otherareanum].contents & AREACONTENTS_CLUSTERPORTAL) continue; //if the area already has a cluster set if (aasworld.areasettings[otherareanum].cluster) continue; - //another cluster is seperated by this portal + //another cluster is separated by this portal numseperatedclusters++; //flood the cluster AAS_FloodCluster_r(otherareanum, numseperatedclusters); AAS_FloodClusterReachabilities(numseperatedclusters); } //end for - //a portal must seperate no more and no less than 2 clusters + //a portal must separate no more and no less than 2 clusters if (numseperatedclusters != 2) { aasworld.areasettings[i].contents &= ~AREACONTENTS_CLUSTERPORTAL; diff --git a/code/botlib/be_aas_main.c b/code/botlib/be_aas_main.c index bd55fda14a..88dfc68b57 100644 --- a/code/botlib/be_aas_main.c +++ b/code/botlib/be_aas_main.c @@ -241,7 +241,7 @@ int AAS_LoadFiles(const char *mapname) return BLERR_NOERROR; } //end of the function AAS_LoadFiles //=========================================================================== -// called everytime a map changes +// called every time a map changes // // Parameter: - // Returns: - diff --git a/code/botlib/be_aas_reach.c b/code/botlib/be_aas_reach.c index 62acd47766..eac360e584 100644 --- a/code/botlib/be_aas_reach.c +++ b/code/botlib/be_aas_reach.c @@ -1416,7 +1416,7 @@ int AAS_Reachability_Step_Barrier_WaterJump_WalkOffLedge(int area1num, int area2 //if there IS water the sv_maxwaterjump height below the bestend point if (aasworld.areasettings[AAS_PointAreaNum(testpoint)].areaflags & AREA_LIQUID) { - //don't create rediculous water jump reachabilities from areas very far below + //don't create ridiculous water jump reachabilities from areas very far below //the water surface if (water_bestdist < aassettings.phys_maxwaterjump + 24) { @@ -3054,7 +3054,7 @@ void AAS_Reachability_Elevator(void) bottomorg[2] += 24; } //end else //look at adjacent areas around the top of the plat - //make larger steps to outside the plat everytime + //make larger steps to outside the plat every time for (n = 0; n < 3; n++) { for (k = 0; k < 3; k++) diff --git a/code/botlib/be_aas_sample.c b/code/botlib/be_aas_sample.c index 095641a4ae..9ca18f4601 100644 --- a/code/botlib/be_aas_sample.c +++ b/code/botlib/be_aas_sample.c @@ -688,7 +688,7 @@ aas_trace_t AAS_TraceClientBBox(vec3_t start, vec3_t end, int presencetype, side = front < 0; //first put the end part of the line on the stack (back side) VectorCopy(cur_mid, tstack_p->start); - //not necesary to store because still on stack + //not necessary to store because still on stack //VectorCopy(cur_end, tstack_p->end); tstack_p->planenum = aasnode->planenum; tstack_p->nodenum = aasnode->children[!side]; @@ -874,7 +874,7 @@ int AAS_TraceAreas(vec3_t start, vec3_t end, int *areas, vec3_t *points, int max side = front < 0; //first put the end part of the line on the stack (back side) VectorCopy(cur_mid, tstack_p->start); - //not necesary to store because still on stack + //not necessary to store because still on stack //VectorCopy(cur_end, tstack_p->end); tstack_p->planenum = aasnode->planenum; tstack_p->nodenum = aasnode->children[!side]; @@ -959,7 +959,7 @@ qboolean AAS_InsideFace(aas_face_t *face, vec3_t pnormal, vec3_t point, float ep //edge) and through both the edge vector and the normal vector //of the plane AAS_OrthogonalToVectors(edgevec, pnormal, sepnormal); - //check on wich side of the above plane the point is + //check on which side of the above plane the point is //this is done by checking the sign of the dot product of the //vector orthogonal vector from above and the vector from the //origin (first vertex of edge) to the point diff --git a/code/botlib/be_ai_chat.c b/code/botlib/be_ai_chat.c index 2413706b45..71f496a60d 100644 --- a/code/botlib/be_ai_chat.c +++ b/code/botlib/be_ai_chat.c @@ -553,11 +553,11 @@ void StringReplaceWords(char *string, char *synonym, char *replacement) //find the synonym in the string str = StringContainsWord(string, synonym, qfalse); - //if the synonym occured in the string + //if the synonym occurred in the string while(str) { //if the synonym isn't part of the replacement which is already in the string - //usefull for abreviations + //useful for abreviations str2 = StringContainsWord(string, replacement, qfalse); while(str2) { diff --git a/code/botlib/be_ai_gen.c b/code/botlib/be_ai_gen.c index 5839f8a14d..3f27d9c3fb 100644 --- a/code/botlib/be_ai_gen.c +++ b/code/botlib/be_ai_gen.c @@ -62,7 +62,7 @@ int GeneticSelection(int numranks, float *rankings) } //end for if (sum > 0) { - //select a bot where the ones with the higest rankings have + //select a bot where the ones with the highest rankings have //the highest chance of being selected //sum *= random(); for (i = 0; i < numranks; i++) diff --git a/code/botlib/be_ai_move.c b/code/botlib/be_ai_move.c index 42232d6506..814089ff7e 100644 --- a/code/botlib/be_ai_move.c +++ b/code/botlib/be_ai_move.c @@ -1605,7 +1605,7 @@ bot_moveresult_t BotTravel_WalkOffLedge(bot_movestate_t *ms, aas_reachability_t VectorSubtract(reach->start, ms->origin, dir); VectorNormalize(dir); BotCheckBlocked(ms, dir, qtrue, &result); - //if the reachability start and end are practially above each other + //if the reachability start and end are practically above each other VectorSubtract(reach->end, reach->start, dir); dir[2] = 0; reachhordist = VectorLength(dir); @@ -2744,7 +2744,7 @@ bot_moveresult_t BotTravel_RocketJump(bot_movestate_t *ms, aas_reachability_t *r result.ideal_viewangles[PITCH] = 90; //set the view angles directly EA_View(ms->client, result.ideal_viewangles); - //view is important for the movment + //view is important for the movement result.flags |= MOVERESULT_MOVEMENTVIEWSET; //select the rocket launcher EA_SelectWeapon(ms->client, (int) weapindex_rocketlauncher->value); @@ -2804,7 +2804,7 @@ bot_moveresult_t BotTravel_BFGJump(bot_movestate_t *ms, aas_reachability_t *reac result.ideal_viewangles[PITCH] = 90; //set the view angles directly EA_View(ms->client, result.ideal_viewangles); - //view is important for the movment + //view is important for the movement result.flags |= MOVERESULT_MOVEMENTVIEWSET; //select the rocket launcher EA_SelectWeapon(ms->client, (int) weapindex_bfg10k->value); diff --git a/code/botlib/be_ai_weight.c b/code/botlib/be_ai_weight.c index a6a478e3bd..1d73b479ee 100644 --- a/code/botlib/be_ai_weight.c +++ b/code/botlib/be_ai_weight.c @@ -726,7 +726,7 @@ void EvolveFuzzySeperator_r(fuzzyseperator_t *fs) //every once in a while an evolution leap occurs, mutation if (random() < 0.01) fs->weight += crandom() * (fs->maxweight - fs->minweight); else fs->weight += crandom() * (fs->maxweight - fs->minweight) * 0.5; - //modify bounds if necesary because of mutation + //modify bounds if necessary because of mutation if (fs->weight < fs->minweight) fs->minweight = fs->weight; else if (fs->weight > fs->maxweight) fs->maxweight = fs->weight; } //end else if diff --git a/code/botlib/be_ai_weight.h b/code/botlib/be_ai_weight.h index fb1c885345..283fdd06f7 100644 --- a/code/botlib/be_ai_weight.h +++ b/code/botlib/be_ai_weight.h @@ -64,7 +64,7 @@ typedef struct weightconfig_s weightconfig_t *ReadWeightConfig(char *filename); //free a weight configuration void FreeWeightConfig(weightconfig_t *config); -//writes a weight configuration, returns true if successfull +//writes a weight configuration, returns true if successful qboolean WriteWeightConfig(char *filename, weightconfig_t *config); //find the fuzzy weight with the given name int FindFuzzyWeight(weightconfig_t *wc, char *name); diff --git a/code/botlib/l_script.c b/code/botlib/l_script.c index c1c3266a11..c9b680917d 100644 --- a/code/botlib/l_script.c +++ b/code/botlib/l_script.c @@ -218,7 +218,7 @@ char *PunctuationFromNum(script_t *script, int num) { if (script->punctuations[i].n == num) return script->punctuations[i].p; } //end for - return "unkown punctuation"; + return "unknown punctuation"; } //end of the function PunctuationFromNum //=========================================================================== // @@ -836,7 +836,7 @@ int PS_ReadPrimitive(script_t *script, token_t *token) token->string[len] = 0; //copy the token into the script structure Com_Memcpy(&script->token, token, sizeof(token_t)); - //primitive reading successfull + //primitive reading successful return 1; } //end of the function PS_ReadPrimitive //============================================================================ diff --git a/code/cgame/cg_local.h b/code/cgame/cg_local.h index 1800bda41b..479498c3af 100644 --- a/code/cgame/cg_local.h +++ b/code/cgame/cg_local.h @@ -197,7 +197,7 @@ typedef struct centity_s { //====================================================================== // local entities are created as a result of events or predicted actions, -// and live independantly from all server transmitted entities +// and live independently from all server transmitted entities typedef struct markPoly_s { struct markPoly_s *prevMark, *nextMark; @@ -458,7 +458,7 @@ typedef struct { qboolean loading; // don't defer players at initial startup qboolean intermissionStarted; // don't play voice rewards, because game will end shortly - // there are only one or two snapshot_t that are relevent at a time + // there are only one or two snapshot_t that are relevant at a time int latestSnapshotNum; // the number of snapshots the client system has received int latestSnapshotTime; // the time from latestSnapshotNum, so we don't need to read the snapshot yet @@ -601,7 +601,7 @@ typedef struct { int itemPickup; int itemPickupTime; - int itemPickupBlendTime; // the pulse around the crosshair is timed seperately + int itemPickupBlendTime; // the pulse around the crosshair is timed separately int weaponSelectTime; int weaponAnimation; @@ -1613,7 +1613,7 @@ void trap_GetGlconfig( glconfig_t *glconfig ); void trap_GetGameState( gameState_t *gamestate ); // cgame will poll each frame to see if a newer snapshot has arrived -// that it is interested in. The time is returned seperately so that +// that it is interested in. The time is returned separately so that // snapshot latency can be calculated. void trap_GetCurrentSnapshotNumber( int *snapshotNumber, int *serverTime ); diff --git a/code/cgame/cg_main.c b/code/cgame/cg_main.c index 8da095ab8e..1269e1b640 100644 --- a/code/cgame/cg_main.c +++ b/code/cgame/cg_main.c @@ -499,7 +499,7 @@ static void CG_RegisterItemSounds( int itemNum ) { trap_S_RegisterSound( item->pickup_sound, qfalse ); } - // parse the space seperated precache string for other media + // parse the space separated precache string for other media s = item->sounds; if (!s || !s[0]) return; diff --git a/code/cgame/cg_players.c b/code/cgame/cg_players.c index 500c63be6f..b803367f1d 100644 --- a/code/cgame/cg_players.c +++ b/code/cgame/cg_players.c @@ -1371,7 +1371,7 @@ static void CG_AddPainTwitch( centity_t *cent, vec3_t torsoAngles ) { =============== CG_PlayerAngles -Handles seperate torso motion +Handles separate torso motion legs pivot based on direction of movement diff --git a/code/cgame/cg_public.h b/code/cgame/cg_public.h index 7449d38ead..8cf4ac5784 100644 --- a/code/cgame/cg_public.h +++ b/code/cgame/cg_public.h @@ -206,7 +206,7 @@ typedef enum { CG_SHUTDOWN, // void (*CG_Shutdown)( void ); - // oportunity to flush and close any open files + // opportunity to flush and close any open files CG_CONSOLE_COMMAND, // qboolean (*CG_ConsoleCommand)( void ); diff --git a/code/cgame/cg_servercmds.c b/code/cgame/cg_servercmds.c index 57ca19b58a..c8c54fcd68 100644 --- a/code/cgame/cg_servercmds.c +++ b/code/cgame/cg_servercmds.c @@ -21,7 +21,7 @@ Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ // // cg_servercmds.c -- reliably sequenced text commands sent by the server -// these are processed at snapshot transition time, so there will definately +// these are processed at snapshot transition time, so there will definitely // be a valid snapshot this frame #include "cg_local.h" diff --git a/code/cgame/cg_snapshot.c b/code/cgame/cg_snapshot.c index 3a70559036..11b9a0678f 100644 --- a/code/cgame/cg_snapshot.c +++ b/code/cgame/cg_snapshot.c @@ -137,7 +137,7 @@ static void CG_TransitionSnapshot( void ) { // execute any server string commands before transitioning entities CG_ExecuteNewServerCommands( cg.nextSnap->serverCommandSequence ); - // if we had a map_restart, set everthing with initial + // if we had a map_restart, set everything with initial if ( cg.mapRestart ) { } diff --git a/code/cgame/cg_view.c b/code/cgame/cg_view.c index 74bd2488c1..5f53cd5ade 100644 --- a/code/cgame/cg_view.c +++ b/code/cgame/cg_view.c @@ -42,7 +42,7 @@ Testmodel will create a fake entity 100 units in front of the current view position, directly facing the viewer. It will remain immobile, so you can move around it to view it from different angles. -Testgun will cause the model to follow the player around and supress the real +Testgun will cause the model to follow the player around and suppress the real view weapon model. The default frame 0 of most guns is completely off screen, so you will probably have to cycle a couple frames to see it. diff --git a/code/cgame/cg_weapons.c b/code/cgame/cg_weapons.c index 4dbe6d55c5..644292eeea 100644 --- a/code/cgame/cg_weapons.c +++ b/code/cgame/cg_weapons.c @@ -1289,7 +1289,7 @@ void CG_AddPlayerWeapon( refEntity_t *parent, playerState_t *ps, centity_t *cent nonPredictedCent = &cg_entities[cent->currentState.clientNum]; // if the index of the nonPredictedCent is not the same as the clientNum - // then this is a fake player (like on teh single player podiums), so + // then this is a fake player (like on the single player podiums), so // go ahead and use the cent if( ( nonPredictedCent - cg_entities ) != cent->currentState.clientNum ) { nonPredictedCent = cent; diff --git a/code/client/cl_cgame.c b/code/client/cl_cgame.c index 157c7bd9d6..613d084209 100644 --- a/code/client/cl_cgame.c +++ b/code/client/cl_cgame.c @@ -341,7 +341,7 @@ qboolean CL_GetServerCommand( int serverCommandNumber ) { // the clientLevelShot command is used during development // to generate 128*128 screenshots from the intermission // point of levels for the menu system to use - // we pass it along to the cgame to make apropriate adjustments, + // we pass it along to the cgame to make appropriate adjustments, // but we also clear the console and notify lines here if ( !strcmp( cmd, "clientLevelShot" ) ) { // don't do it if we aren't running the server locally, @@ -1026,7 +1026,7 @@ void CL_SetCGameTime( void ) { } // if we are playing a demo back, we can just keep reading - // messages from the demo file until the cgame definately + // messages from the demo file until the cgame definitely // has valid snapshots to interpolate between // a timedemo will always use a deterministic set of time samples diff --git a/code/client/cl_input.c b/code/client/cl_input.c index f4089d7e11..cfdaa1d165 100644 --- a/code/client/cl_input.c +++ b/code/client/cl_input.c @@ -320,7 +320,7 @@ void CL_KeyMove( usercmd_t *cmd ) { // // adjust for speed key / running - // the walking flag is to keep animations consistant + // the walking flag is to keep animations consistent // even during acceleration and develeration // if ( in_speed.active ^ cl_run->integer ) { diff --git a/code/client/cl_keys.c b/code/client/cl_keys.c index 7ddea0c7b8..2fb2c1ca3d 100644 --- a/code/client/cl_keys.c +++ b/code/client/cl_keys.c @@ -177,7 +177,7 @@ keyname_t keynames[] = {"PAUSE", K_PAUSE}, - {"SEMICOLON", ';'}, // because a raw semicolon seperates commands + {"SEMICOLON", ';'}, // because a raw semicolon separates commands {"WORLD_0", K_WORLD_0}, {"WORLD_1", K_WORLD_1}, @@ -919,7 +919,7 @@ void Key_SetBinding( int keynum, const char *binding ) { keys[keynum].binding = CopyString( binding ); // consider this like modifying an archived cvar, so the - // file write will be triggered at the next oportunity + // file write will be triggered at the next opportunity cvar_modifiedFlags |= CVAR_ARCHIVE; } @@ -1305,7 +1305,7 @@ void CL_KeyDownEvent( int key, unsigned time ) // send the bound action CL_ParseBinding( key, qtrue, time ); - // distribute the key down event to the apropriate handler + // distribute the key down event to the appropriate handler if ( Key_GetCatcher( ) & KEYCATCH_CONSOLE ) { Console_Key( key ); } else if ( Key_GetCatcher( ) & KEYCATCH_UI ) { @@ -1387,7 +1387,7 @@ void CL_CharEvent( int key ) { return; } - // distribute the key down event to the apropriate handler + // distribute the key down event to the appropriate handler if ( Key_GetCatcher( ) & KEYCATCH_CONSOLE ) { Field_CharEvent( &g_consoleField, key ); diff --git a/code/client/cl_main.c b/code/client/cl_main.c index 62dcf52832..fde9fa1012 100644 --- a/code/client/cl_main.c +++ b/code/client/cl_main.c @@ -573,7 +573,7 @@ CLIENT RELIABLE COMMAND COMMUNICATION ====================== CL_AddReliableCommand -The given command will be transmitted to the server, and is gauranteed to +The given command will be transmitted to the server, and is guaranteed to not have future usercmd_t executed before it is executed ====================== */ @@ -1960,7 +1960,7 @@ void CL_Vid_Restart_f( void ) { CL_ShutdownCGame(); // shutdown the renderer and clear the renderer interface CL_ShutdownRef(); - // client is no longer pure untill new checksums are sent + // client is no longer pure until new checksums are sent CL_ResetPureClientAtServer(); // clear pak references FS_ClearPakReferences( FS_UI_REF | FS_CGAME_REF ); @@ -1971,7 +1971,7 @@ void CL_Vid_Restart_f( void ) { cls.cgameStarted = qfalse; cls.soundRegistered = qfalse; - // unpause so the cgame definately gets a snapshot and renders a frame + // unpause so the cgame definitely gets a snapshot and renders a frame Cvar_Set("cl_paused", "0"); // initialize the renderer interface @@ -3301,7 +3301,7 @@ void CL_InitRef( void ) { re = *ret; - // unpause so the cgame definately gets a snapshot and renders a frame + // unpause so the cgame definitely gets a snapshot and renders a frame Cvar_Set( "cl_paused", "0" ); } diff --git a/code/client/snd_adpcm.c b/code/client/snd_adpcm.c index 4e138726f2..1a114edf0f 100644 --- a/code/client/snd_adpcm.c +++ b/code/client/snd_adpcm.c @@ -25,7 +25,7 @@ OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. /* ** Intel/DVI ADPCM coder/decoder. ** -** The algorithm for this coder was taken from the IMA Compatability Project +** The algorithm for this coder was taken from the IMA Compatibility Project ** proceedings, Vol 2, Number 2; May 1992. ** ** Version 1.2, 18-Dec-92. diff --git a/code/client/snd_dma.c b/code/client/snd_dma.c index e243f40649..2078b81190 100644 --- a/code/client/snd_dma.c +++ b/code/client/snd_dma.c @@ -432,7 +432,7 @@ void S_SpatializeOrigin (vec3_t origin, int master_vol, int *left_vol, int *righ const float dist_mult = SOUND_ATTENUATE; - // calculate stereo seperation and distance attenuation + // calculate stereo separation and distance attenuation VectorSubtract(origin, listener_origin, source_vec); dist = VectorNormalize(source_vec); diff --git a/code/client/snd_mix.c b/code/client/snd_mix.c index 4418d22d3a..5323081230 100644 --- a/code/client/snd_mix.c +++ b/code/client/snd_mix.c @@ -515,7 +515,7 @@ static void S_PaintChannelFrom16_scalar( channel_t *ch, const sfx_t *sc, int cou static void S_PaintChannelFrom16( channel_t *ch, const sfx_t *sc, int count, int sampleOffset, int bufferOffset ) { #if idppc_altivec if (com_altivec->integer) { - // must be in a seperate function or G3 systems will crash. + // must be in a separate function or G3 systems will crash. S_PaintChannelFrom16_altivec( ch, sc, count, sampleOffset, bufferOffset ); return; } diff --git a/code/game/ai_dmnet.c b/code/game/ai_dmnet.c index 73bc4cafea..b107895d2e 100644 --- a/code/game/ai_dmnet.c +++ b/code/game/ai_dmnet.c @@ -329,7 +329,7 @@ int BotGetItemLongTermGoal(bot_state_t *bs, int tfl, bot_goal_t *goal) { ================== BotGetLongTermGoal -we could also create a seperate AI node for every long term goal type +we could also create a separate AI node for every long term goal type however this saves us a lot of code ================== */ diff --git a/code/game/ai_dmq3.c b/code/game/ai_dmq3.c index fdd598ad59..dbc68b4a94 100644 --- a/code/game/ai_dmq3.c +++ b/code/game/ai_dmq3.c @@ -2759,7 +2759,7 @@ bot_moveresult_t BotAttackMove(bot_state_t *bs, int tfl) { bs->flags ^= BFL_STRAFERIGHT; bs->attackstrafe_time = 0; } - //bot couldn't do any usefull movement + //bot couldn't do any useful movement // bs->attackchase_time = AAS_Time() + 6; return moveresult; } @@ -4862,7 +4862,7 @@ void BotCheckEvents(bot_state_t *bs, entityState_t *state) { else*/ #ifdef MISSIONPACK if (!strcmp(buf, "sound/items/kamikazerespawn.wav" )) { - //the kamikaze respawned so dont avoid it + //the kamikaze respawned so don't avoid it BotDontAvoid(bs, "Kamikaze"); } else diff --git a/code/game/ai_dmq3.h b/code/game/ai_dmq3.h index 7b69bf3812..bd7783cfaa 100644 --- a/code/game/ai_dmq3.h +++ b/code/game/ai_dmq3.h @@ -136,7 +136,7 @@ int BotPopFromActivateGoalStack(bot_state_t *bs); void BotClearActivateGoalStack(bot_state_t *bs); //returns the team the bot is in int BotTeam(bot_state_t *bs); -//retuns the opposite team of the bot +//returns the opposite team of the bot int BotOppositeTeam(bot_state_t *bs); //returns the flag the bot is carrying (CTFFLAG_?) int BotCTFCarryingFlag(bot_state_t *bs); diff --git a/code/game/ai_main.c b/code/game/ai_main.c index 4b350d0f46..1d57035a4a 100644 --- a/code/game/ai_main.c +++ b/code/game/ai_main.c @@ -1260,7 +1260,7 @@ int BotAISetupClient(int client, struct bot_settings_s *settings, qboolean resta if (restart) { BotReadSessionData(bs); } - //bot has been setup succesfully + //bot has been setup successfully return qtrue; } diff --git a/code/game/ai_team.c b/code/game/ai_team.c index 95b445cd63..78e25b0c05 100644 --- a/code/game/ai_team.c +++ b/code/game/ai_team.c @@ -1016,7 +1016,7 @@ void Bot1FCTFOrders_FlagAtCenter(bot_state_t *bs) { } } } - else { //agressive + else { //aggressive //different orders based on the number of team mates switch(numteammates) { case 1: break; @@ -1202,7 +1202,7 @@ void Bot1FCTFOrders_TeamHasFlag(bot_state_t *bs) { } } } - else { //agressive + else { //aggressive //different orders based on the number of team mates switch(numteammates) { case 1: break; @@ -1366,7 +1366,7 @@ void Bot1FCTFOrders_EnemyHasFlag(bot_state_t *bs) { } } } - else { //agressive + else { //aggressive //different orders based on the number of team mates switch(numteammates) { case 1: break; @@ -1513,7 +1513,7 @@ void Bot1FCTFOrders_EnemyDroppedFlag(bot_state_t *bs) { } } } - else { //agressive + else { //aggressive //different orders based on the number of team mates switch(numteammates) { case 1: break; diff --git a/code/game/bg_lib.c b/code/game/bg_lib.c index f893f442f4..723da95e88 100644 --- a/code/game/bg_lib.c +++ b/code/game/bg_lib.c @@ -1376,7 +1376,7 @@ double fabs( double x ) { * probably requires libm on most operating systems. Don't yet * support the exponent (e,E) and sigfig (g,G). Also, fmtint() * was pretty badly broken, it just wasn't being exercised in ways - * which showed it, so that's been fixed. Also, formated the code + * which showed it, so that's been fixed. Also, formatted the code * to mutt conventions, and removed dead code left over from the * original. Also, there is now a builtin-test, just compile with: * gcc -DTEST_SNPRINTF -o snprintf snprintf.c -lm diff --git a/code/game/bg_pmove.c b/code/game/bg_pmove.c index 2d0e8cf358..446008c04c 100644 --- a/code/game/bg_pmove.c +++ b/code/game/bg_pmove.c @@ -899,7 +899,7 @@ static void PM_NoclipMove( void ) { ================ PM_FootstepForSurface -Returns an event number apropriate for the groundsurface +Returns an event number appropriate for the groundsurface ================ */ static int PM_FootstepForSurface( void ) { @@ -1402,7 +1402,7 @@ static void PM_Footsteps( void ) { old = pm->ps->bobCycle; pm->ps->bobCycle = (int)( old + bobmove * pml.msec ) & 255; - // if we just crossed a cycle boundary, play an apropriate footstep event + // if we just crossed a cycle boundary, play an appropriate footstep event if ( ( ( old + 64 ) ^ ( pm->ps->bobCycle + 64 ) ) & 128 ) { if ( pm->waterlevel == 0 ) { // on ground will only play sounds if running diff --git a/code/game/bg_public.h b/code/game/bg_public.h index f61dd4c15e..fa898c4438 100644 --- a/code/game/bg_public.h +++ b/code/game/bg_public.h @@ -210,7 +210,7 @@ typedef enum { STAT_ARMOR, STAT_DEAD_YAW, // look this direction when dead (FIXME: get rid of?) STAT_CLIENTS_READY, // bit mask of clients wishing to exit the intermission (FIXME: configstring?) - STAT_MAX_HEALTH // health / armor limit, changable by handicap + STAT_MAX_HEALTH // health / armor limit, changeable by handicap } statIndex_t; diff --git a/code/game/g_active.c b/code/game/g_active.c index 413331e98d..89c1a5248c 100644 --- a/code/game/g_active.c +++ b/code/game/g_active.c @@ -69,7 +69,7 @@ void P_DamageFeedback( gentity_t *player ) { client->ps.damageYaw = angles[YAW]/360.0 * 256; } - // play an apropriate pain sound + // play an appropriate pain sound if ( (level.time > player->pain_debounce_time) && !(player->flags & FL_GODMODE) ) { player->pain_debounce_time = level.time + 700; G_AddEvent( player, EV_PAIN, player->health ); @@ -279,7 +279,7 @@ void G_TouchTriggers( gentity_t *ent ) { } } - // use seperate code for determining if an item is picked up + // use separate code for determining if an item is picked up // so you don't have to actually contact its bounding box if ( hit->s.eType == ET_ITEM ) { if ( !BG_PlayerTouchesItem( &ent->client->ps, &hit->s, level.time ) ) { diff --git a/code/game/g_client.c b/code/game/g_client.c index 3366d176f8..457e058af8 100644 --- a/code/game/g_client.c +++ b/code/game/g_client.c @@ -48,7 +48,7 @@ void SP_info_player_deathmatch( gentity_t *ent ) { } /*QUAKED info_player_start (1 0 0) (-16 -16 -24) (16 16 32) -equivelant to info_player_deathmatch +equivalent to info_player_deathmatch */ void SP_info_player_start(gentity_t *ent) { ent->classname = "info_player_deathmatch"; @@ -377,7 +377,7 @@ void InitBodyQue (void) { ============= BodySink -After sitting around for five seconds, fall into the ground and dissapear +After sitting around for five seconds, fall into the ground and disappear ============= */ void BodySink( gentity_t *ent ) { @@ -954,7 +954,7 @@ char *ClientConnect( int clientNum, qboolean firstTime, qboolean isBot ) { } G_ReadSessionData( client ); - // get and distribute relevent paramters + // get and distribute relevant parameters G_LogPrintf( "ClientConnect: %i\n", clientNum ); ClientUserinfoChanged( clientNum ); diff --git a/code/game/g_cmds.c b/code/game/g_cmds.c index 4f73c1d94a..b96b567dc4 100644 --- a/code/game/g_cmds.c +++ b/code/game/g_cmds.c @@ -639,7 +639,7 @@ void SetTeam( gentity_t *ent, const char *s ) { BroadcastTeamChange( client, oldTeam ); - // get and distribute relevent paramters + // get and distribute relevant parameters ClientUserinfoChanged( clientNum ); // client hasn't spawned yet, they sent an early team command, teampref userinfo, or g_teamAutoJoin is enabled @@ -921,7 +921,7 @@ void G_Say( gentity_t *ent, gentity_t *target, int mode, const char *chatText ) G_Printf( "%s%s\n", name, text); } - // send it to all the apropriate clients + // send it to all the appropriate clients for (j = 0; j < level.maxclients; j++) { other = &g_entities[j]; G_SayTo( ent, other, mode, color, name, text ); @@ -1062,7 +1062,7 @@ void G_Voice( gentity_t *ent, gentity_t *target, int mode, const char *id, qbool G_Printf( "voice: %s %s\n", ent->client->pers.netname, id); } - // send it to all the apropriate clients + // send it to all the appropriate clients for (j = 0; j < level.maxclients; j++) { other = &g_entities[j]; G_VoiceTo( ent, other, mode, id, voiceonly ); diff --git a/code/game/g_items.c b/code/game/g_items.c index 59461d1e67..ed15a16fa2 100644 --- a/code/game/g_items.c +++ b/code/game/g_items.c @@ -32,7 +32,7 @@ Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA Respawnable items don't actually go away when picked up, they are just made invisible and untouchable. This allows them to ride - movers and respawn apropriately. + movers and respawn appropriately. */ diff --git a/code/game/g_missile.c b/code/game/g_missile.c index 29efc62a52..3a138b959a 100644 --- a/code/game/g_missile.c +++ b/code/game/g_missile.c @@ -145,7 +145,7 @@ void ProximityMine_Trigger( gentity_t *trigger, gentity_t *other, trace_t *trace } } - // ok, now check for ability to damage so we don't get triggered thru walls, closed doors, etc... + // ok, now check for ability to damage so we don't get triggered through walls, closed doors, etc... if( !CanDamage( other, trigger->s.pos.trBase ) ) { return; } diff --git a/code/game/g_mover.c b/code/game/g_mover.c index 756c0dd0d9..ad9b6c3c50 100644 --- a/code/game/g_mover.c +++ b/code/game/g_mover.c @@ -180,7 +180,7 @@ qboolean G_TryPushingEntity( gentity_t *check, gentity_t *pusher, vec3_t move, v } // if it is ok to leave in the old position, do it - // this is only relevent for riding entities, not pushed + // this is only relevant for riding entities, not pushed // Sliding trapdoors can cause this. VectorCopy( (pushed_p-1)->origin, check->s.pos.trBase); if ( check->client ) { @@ -424,7 +424,7 @@ void G_MoverTeam( gentity_t *ent ) { obstacle = NULL; - // make sure all team slaves can move before commiting + // make sure all team slaves can move before committing // any moves or calling any think functions // if the move is blocked, all moved objects will be backed out pushed_p = pushed; @@ -718,7 +718,7 @@ void InitMover( gentity_t *ent ) { qboolean lightSet, colorSet; char *sound; - // if the "model2" key is set, use a seperate model + // if the "model2" key is set, use a separate model // for drawing, but clip against the brushes if ( ent->model2 ) { ent->s.modelindex2 = G_ModelIndex( ent->model2 ); @@ -1279,7 +1279,7 @@ void Reached_Train( gentity_t *ent ) { vec3_t move; float length; - // copy the apropriate values + // copy the appropriate values next = ent->nextTrain; if ( !next || !next->nextTrain ) { return; // just stop diff --git a/code/game/g_spawn.c b/code/game/g_spawn.c index 2828a6e969..a34ced5f07 100644 --- a/code/game/g_spawn.c +++ b/code/game/g_spawn.c @@ -391,7 +391,7 @@ void G_ParseField( const char *key, const char *value, gentity_t *ent ) { G_SpawnGEntityFromSpawnVars Spawn an entity and fill in all of the level fields from -level.spawnVars[], then call the class specfic spawn function +level.spawnVars[], then call the class specific spawn function =================== */ void G_SpawnGEntityFromSpawnVars( void ) { diff --git a/code/game/g_target.c b/code/game/g_target.c index 5205990d59..195cfc6c6c 100644 --- a/code/game/g_target.c +++ b/code/game/g_target.c @@ -102,7 +102,7 @@ void Use_Target_Delay( gentity_t *ent, gentity_t *other, gentity_t *activator ) } void SP_target_delay( gentity_t *ent ) { - // check delay for backwards compatability + // check delay for backwards compatibility if ( !G_SpawnFloat( "delay", "0", &ent->wait ) ) { G_SpawnFloat( "wait", "1", &ent->wait ); } diff --git a/code/game/g_trigger.c b/code/game/g_trigger.c index 489040b487..a468d469c9 100644 --- a/code/game/g_trigger.c +++ b/code/game/g_trigger.c @@ -336,7 +336,7 @@ Any entity that touches this will be hurt. It does dmg points of damage each server frame Targeting the trigger will toggle its on / off state. -SILENT supresses playing the sound +SILENT suppresses playing the sound SLOW changes the damage rate to once per second NO_PROTECTION *nothing* stops the damage diff --git a/code/game/g_weapon.c b/code/game/g_weapon.c index e4d32f9c59..752cb1e21e 100644 --- a/code/game/g_weapon.c +++ b/code/game/g_weapon.c @@ -918,7 +918,7 @@ static void KamikazeRadiusDamage( vec3_t origin, gentity_t *attacker, float dama continue; } - // dont hit things we have already hit + // don't hit things we have already hit if( ent->kamikazeTime > level.time ) { continue; } @@ -978,7 +978,7 @@ static void KamikazeShockWave( vec3_t origin, gentity_t *attacker, float damage, for ( e = 0 ; e < numListedEntities ; e++ ) { ent = &g_entities[entityList[ e ]]; - // dont hit things we have already hit + // don't hit things we have already hit if( ent->kamikazeShockTime > level.time ) { continue; } diff --git a/code/q3_ui/ui_atoms.c b/code/q3_ui/ui_atoms.c index f369d353fb..55a7bfceab 100644 --- a/code/q3_ui/ui_atoms.c +++ b/code/q3_ui/ui_atoms.c @@ -803,7 +803,7 @@ static void NeedCDKeyAction( qboolean result ) { void UI_SetActiveMenu( uiMenuCommand_t menu ) { // this should be the ONLY way the menu system is brought up - // enusure minumum menu data is cached + // ensure minimum menu data is cached Menu_Cache(); switch ( menu ) { diff --git a/code/q3_ui/ui_local.h b/code/q3_ui/ui_local.h index 6bb0fb3172..1135a52da2 100644 --- a/code/q3_ui/ui_local.h +++ b/code/q3_ui/ui_local.h @@ -221,7 +221,7 @@ typedef struct int width; int height; int columns; - int seperation; + int separation; } menulist_s; typedef struct diff --git a/code/q3_ui/ui_playermodel.c b/code/q3_ui/ui_playermodel.c index a2b2eff4af..1d8fc93220 100644 --- a/code/q3_ui/ui_playermodel.c +++ b/code/q3_ui/ui_playermodel.c @@ -339,14 +339,14 @@ static void PlayerModel_PicEvent( void* ptr, int event ) Q_strncpyz(s_playermodel.modelskin,buffptr,pdest-buffptr+1); strcat(s_playermodel.modelskin,pdest + 5); - // seperate the model name + // separate the model name maxlen = pdest-buffptr; if (maxlen > 16) maxlen = 16; Q_strncpyz( s_playermodel.modelname.string, buffptr, maxlen ); Q_strupr( s_playermodel.modelname.string ); - // seperate the skin name + // separate the skin name maxlen = strlen(pdest+5)+1; if (maxlen > 16) maxlen = 16; @@ -494,14 +494,14 @@ static void PlayerModel_SetMenuItems( void ) s_playermodel.selectedmodel = i; s_playermodel.modelpage = i/MAX_MODELSPERPAGE; - // seperate the model name + // separate the model name maxlen = pdest-buffptr; if (maxlen > 16) maxlen = 16; Q_strncpyz( s_playermodel.modelname.string, buffptr, maxlen ); Q_strupr( s_playermodel.modelname.string ); - // seperate the skin name + // separate the skin name maxlen = strlen(pdest+5)+1; if (maxlen > 16) maxlen = 16; diff --git a/code/q3_ui/ui_qmenu.c b/code/q3_ui/ui_qmenu.c index 90d9cb05c1..f9df6e1c61 100644 --- a/code/q3_ui/ui_qmenu.c +++ b/code/q3_ui/ui_qmenu.c @@ -881,13 +881,13 @@ static void ScrollList_Init( menulist_s *l ) if( !l->columns ) { l->columns = 1; - l->seperation = 0; + l->separation = 0; } - else if( !l->seperation ) { - l->seperation = 3; + else if( !l->separation ) { + l->separation = 3; } - w = ( (l->width + l->seperation) * l->columns - l->seperation) * SMALLCHAR_WIDTH; + w = ( (l->width + l->separation) * l->columns - l->separation) * SMALLCHAR_WIDTH; l->generic.left = l->generic.x; l->generic.top = l->generic.y; @@ -926,14 +926,14 @@ sfxHandle_t ScrollList_Key( menulist_s *l, int key ) // check scroll region x = l->generic.x; y = l->generic.y; - w = ( (l->width + l->seperation) * l->columns - l->seperation) * SMALLCHAR_WIDTH; + w = ( (l->width + l->separation) * l->columns - l->separation) * SMALLCHAR_WIDTH; if( l->generic.flags & QMF_CENTER_JUSTIFY ) { x -= w / 2; } if (UI_CursorInRect( x, y, w, l->height*SMALLCHAR_HEIGHT )) { cursorx = (uis.cursorx - x)/SMALLCHAR_WIDTH; - column = cursorx / (l->width + l->seperation); + column = cursorx / (l->width + l->separation); cursory = (uis.cursory - y)/SMALLCHAR_HEIGHT; index = column * l->height + cursory; if (l->top + index < l->numitems) @@ -1285,7 +1285,7 @@ void ScrollList_Draw( menulist_s *l ) y += SMALLCHAR_HEIGHT; } - x += (l->width + l->seperation) * SMALLCHAR_WIDTH; + x += (l->width + l->separation) * SMALLCHAR_WIDTH; } } diff --git a/code/qcommon/cm_patch.c b/code/qcommon/cm_patch.c index c284e4d5a4..002d942867 100644 --- a/code/qcommon/cm_patch.c +++ b/code/qcommon/cm_patch.c @@ -1105,7 +1105,7 @@ static void CM_PatchCollideFromGrid( cGrid_t *grid, patchCollide_t *pf ) { numFacets++; } } else { - // two seperate triangles + // two separate triangles facet->surfacePlane = gridPlanes[i][j][0]; facet->numBorders = 3; facet->borderPlanes[0] = borders[EN_TOP]; @@ -1214,7 +1214,7 @@ struct patchCollide_s *CM_GeneratePatchCollide( int width, int height, vec3_t *p CM_RemoveDegenerateColumns( &grid ); // we now have a grid of points exactly on the curve - // the aproximate surface defined by these points will be + // the approximate surface defined by these points will be // collided against pf = Hunk_Alloc( sizeof( *pf ), h_high ); ClearBounds( pf->bounds[0], pf->bounds[1] ); @@ -1369,7 +1369,7 @@ int CM_CheckFacetPlane(float *plane, vec3_t start, vec3_t end, float *enterFrac, return qfalse; } - // if it doesn't cross the plane, the plane isn't relevent + // if it doesn't cross the plane, the plane isn't relevant if (d1 <= 0 && d2 <= 0 ) { return qtrue; } @@ -1433,7 +1433,7 @@ void CM_TraceThroughPatchCollide( traceWork_t *tw, const struct patchCollide_s * VectorCopy(planes->plane, plane); plane[3] = planes->plane[3]; if ( tw->sphere.use ) { - // adjust the plane distance apropriately for radius + // adjust the plane distance appropriately for radius plane[3] += tw->sphere.radius; // find the closest point on the capsule to the plane @@ -1472,7 +1472,7 @@ void CM_TraceThroughPatchCollide( traceWork_t *tw, const struct patchCollide_s * plane[3] = planes->plane[3]; } if ( tw->sphere.use ) { - // adjust the plane distance apropriately for radius + // adjust the plane distance appropriately for radius plane[3] += tw->sphere.radius; // find the closest point on the capsule to the plane @@ -1561,7 +1561,7 @@ qboolean CM_PositionTestInPatchCollide( traceWork_t *tw, const struct patchColli VectorCopy(planes->plane, plane); plane[3] = planes->plane[3]; if ( tw->sphere.use ) { - // adjust the plane distance apropriately for radius + // adjust the plane distance appropriately for radius plane[3] += tw->sphere.radius; // find the closest point on the capsule to the plane @@ -1594,7 +1594,7 @@ qboolean CM_PositionTestInPatchCollide( traceWork_t *tw, const struct patchColli plane[3] = planes->plane[3]; } if ( tw->sphere.use ) { - // adjust the plane distance apropriately for radius + // adjust the plane distance appropriately for radius plane[3] += tw->sphere.radius; // find the closest point on the capsule to the plane diff --git a/code/qcommon/cm_polylib.c b/code/qcommon/cm_polylib.c index 4fa978658d..28393bc67c 100644 --- a/code/qcommon/cm_polylib.c +++ b/code/qcommon/cm_polylib.c @@ -353,7 +353,7 @@ void ClipWindingEpsilon (winding_t *in, vec3_t normal, vec_t dist, return; } - maxpts = in->numpoints+4; // cant use counts[0]+2 because + maxpts = in->numpoints+4; // can't use counts[0]+2 because // of fp grouping errors *front = f = AllocWinding (maxpts); @@ -462,7 +462,7 @@ void ChopWindingInPlace (winding_t **inout, vec3_t normal, vec_t dist, vec_t eps if (!counts[1]) return; // inout stays the same - maxpts = in->numpoints+4; // cant use counts[0]+2 because + maxpts = in->numpoints+4; // can't use counts[0]+2 because // of fp grouping errors f = AllocWinding (maxpts); @@ -574,7 +574,7 @@ void CheckWinding (winding_t *w) if (d < -ON_EPSILON || d > ON_EPSILON) Com_Error (ERR_DROP, "CheckWinding: point off plane"); - // check the edge isnt degenerate + // check the edge isn't degenerate p2 = w->p[j]; VectorSubtract (p2, p1, dir); diff --git a/code/qcommon/cm_trace.c b/code/qcommon/cm_trace.c index e6ca055ff0..ec9fd0a039 100644 --- a/code/qcommon/cm_trace.c +++ b/code/qcommon/cm_trace.c @@ -189,7 +189,7 @@ void CM_TestBoxInBrush( traceWork_t *tw, cbrush_t *brush ) { side = brush->sides + i; plane = side->plane; - // adjust the plane distance apropriately for radius + // adjust the plane distance appropriately for radius dist = plane->dist + tw->sphere.radius; // find the closest point on the capsule to the plane t = DotProduct( plane->normal, tw->sphere.offset ); @@ -214,7 +214,7 @@ void CM_TestBoxInBrush( traceWork_t *tw, cbrush_t *brush ) { side = brush->sides + i; plane = side->plane; - // adjust the plane distance apropriately for mins/maxs + // adjust the plane distance appropriately for mins/maxs dist = plane->dist - DotProduct( tw->offsets[ plane->signbits ], plane->normal ); d1 = DotProduct( tw->start, plane->normal ) - dist; @@ -517,7 +517,7 @@ void CM_TraceThroughBrush( traceWork_t *tw, cbrush_t *brush ) { side = brush->sides + i; plane = side->plane; - // adjust the plane distance apropriately for radius + // adjust the plane distance appropriately for radius dist = plane->dist + tw->sphere.radius; // find the closest point on the capsule to the plane @@ -548,7 +548,7 @@ void CM_TraceThroughBrush( traceWork_t *tw, cbrush_t *brush ) { return; } - // if it doesn't cross the plane, the plane isn't relevent + // if it doesn't cross the plane, the plane isn't relevant if (d1 <= 0 && d2 <= 0 ) { continue; } @@ -584,7 +584,7 @@ void CM_TraceThroughBrush( traceWork_t *tw, cbrush_t *brush ) { side = brush->sides + i; plane = side->plane; - // adjust the plane distance apropriately for mins/maxs + // adjust the plane distance appropriately for mins/maxs dist = plane->dist - DotProduct( tw->offsets[ plane->signbits ], plane->normal ); d1 = DotProduct( tw->start, plane->normal ) - dist; @@ -602,7 +602,7 @@ void CM_TraceThroughBrush( traceWork_t *tw, cbrush_t *brush ) { return; } - // if it doesn't cross the plane, the plane isn't relevent + // if it doesn't cross the plane, the plane isn't relevant if (d1 <= 0 && d2 <= 0 ) { continue; } @@ -1059,7 +1059,7 @@ void CM_TraceThroughTree( traceWork_t *tw, int num, float p1f, float p2f, vec3_t node = cm.nodes + num; plane = node->plane; - // adjust the plane distance apropriately for mins/maxs + // adjust the plane distance appropriately for mins/maxs if ( plane->type < 3 ) { t1 = p1[plane->type] - plane->dist; t2 = p2[plane->type] - plane->dist; @@ -1204,7 +1204,7 @@ void CM_Trace( trace_t *results, const vec3_t start, const vec3_t end, vec3_t mi tw.maxOffset = tw.size[1][0] + tw.size[1][1] + tw.size[1][2]; - // tw.offsets[signbits] = vector to apropriate corner from origin + // tw.offsets[signbits] = vector to appropriate corner from origin tw.offsets[0][0] = tw.size[0][0]; tw.offsets[0][1] = tw.size[0][1]; tw.offsets[0][2] = tw.size[0][2]; diff --git a/code/qcommon/cmd.c b/code/qcommon/cmd.c index 45122e074a..ae605f7b16 100644 --- a/code/qcommon/cmd.c +++ b/code/qcommon/cmd.c @@ -489,8 +489,8 @@ void Cmd_Args_Sanitize(void) Cmd_TokenizeString Parses the given string into command line tokens. -The text is copied to a seperate buffer and 0 characters -are inserted in the apropriate place, The argv array +The text is copied to a separate buffer and 0 characters +are inserted in the appropriate place, The argv array will point into this temporary buffer. ============ */ diff --git a/code/qcommon/common.c b/code/qcommon/common.c index 40fbde00a4..dc340b5379 100644 --- a/code/qcommon/common.c +++ b/code/qcommon/common.c @@ -46,7 +46,7 @@ int demo_protocols[] = int com_argc; char *com_argv[MAX_NUM_ARGVS+1]; -jmp_buf abortframe; // an ERR_DROP occured, exit the entire frame +jmp_buf abortframe; // an ERR_DROP occurred, exit the entire frame FILE *debuglogfile; @@ -158,7 +158,7 @@ void Com_EndRedirect (void) Com_Printf Both client and server can use this, and it will output -to the apropriate place. +to the appropriate place. A raw string should NEVER be passed as fmt, because of "%f" type crashers. ============= @@ -370,7 +370,7 @@ void QDECL Com_Error( int code, const char *fmt, ... ) { Com_Quit_f Both client and server can use this, and it will -do the apropriate things. +do the appropriate things. ============= */ void Com_Quit_f( void ) { @@ -398,7 +398,7 @@ void Com_Quit_f( void ) { COMMAND LINE FUNCTIONS -+ characters seperate the commandLine string into multiple console ++ characters separate the commandLine string into multiple console command lines. All of these are valid: @@ -507,7 +507,7 @@ void Com_StartupVariable( const char *match ) { Com_AddStartupCommands Adds command line parameters as script statements -Commands are seperated by + signs +Commands are separated by + signs Returns qtrue if any late commands were added, which will keep the demoloop from immediately starting @@ -1209,7 +1209,7 @@ char *CopyString( const char *in ) { ============================================================================== Goals: - reproducable without history effects -- no out of memory errors on weird map to map changes + reproducible without history effects -- no out of memory errors on weird map to map changes allow restarting of the client without fragmentation minimize total pages in use at run time minimize total pages needed during load time diff --git a/code/qcommon/files.c b/code/qcommon/files.c index 7637333164..246b3cd96a 100644 --- a/code/qcommon/files.c +++ b/code/qcommon/files.c @@ -88,7 +88,7 @@ File search order: when FS_FOpenFileRead gets called it will go through the fs_s structure and stop on the first successful hit. fs_searchpaths is built with successive calls to FS_AddGameDirectory -Additionaly, we search in several subdirectories: +Additionally, we search in several subdirectories: current game is the current mode base game is a variable to allow mods based on other mods (such as baseq3 + missionpack content combination in a mod for instance) @@ -1420,7 +1420,7 @@ long FS_FOpenFileRead(const char *filename, fileHandle_t *file, qboolean uniqueF } else { - // When file is NULL, we're querying the existance of the file + // When file is NULL, we're querying the existence of the file // If we've got here, it doesn't exist return 0; } @@ -3931,7 +3931,7 @@ void FS_PureServerSetReferencedPaks( const char *pakSums, const char *pakNames ) ================ FS_InitFilesystem -Called only at inital startup, not when the filesystem +Called only at initial startup, not when the filesystem is resetting due to a game change ================ */ diff --git a/code/qcommon/q_shared.h b/code/qcommon/q_shared.h index 9f63f92881..40bb760afd 100644 --- a/code/qcommon/q_shared.h +++ b/code/qcommon/q_shared.h @@ -264,7 +264,7 @@ typedef int clipHandle_t; #define MAX_SAY_TEXT 150 -// paramters for command buffer stuffing +// parameters for command buffer stuffing typedef enum { EXEC_NOW, // don't return until completed, a VM should NEVER use this, // because some commands might cause the VM to be unloaded... @@ -1221,7 +1221,7 @@ typedef struct playerState_s { #define BUTTON_TALK 2 // displays talk balloon and disables actions #define BUTTON_USE_HOLDABLE 4 #define BUTTON_GESTURE 8 -#define BUTTON_WALKING 16 // walking can't just be infered from MOVE_RUN +#define BUTTON_WALKING 16 // walking can't just be inferred from MOVE_RUN // because a key pressed late in the frame will // only generate a small move value for that frame // walking will use different animations and diff --git a/code/qcommon/qcommon.h b/code/qcommon/qcommon.h index b1224ac650..2880f70de5 100644 --- a/code/qcommon/qcommon.h +++ b/code/qcommon/qcommon.h @@ -669,7 +669,7 @@ long FS_ReadFileDir(const char *qpath, void *searchPath, qboolean unpure, void * long FS_ReadFile(const char *qpath, void **buffer); // returns the length of the file // a null buffer will just return the file length without loading -// as a quick check for existance. -1 length == not present +// as a quick check for existence. -1 length == not present // A 0 byte will always be appended at the end, so string ops are safe. // the buffer should be considered read-only, because it may be cached // for other uses. diff --git a/code/qcommon/surfaceflags.h b/code/qcommon/surfaceflags.h index b7c10a17de..c1b91e2eab 100644 --- a/code/qcommon/surfaceflags.h +++ b/code/qcommon/surfaceflags.h @@ -22,7 +22,7 @@ Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA // // This file must be identical in the quake and utils directories -// contents flags are seperate bits +// contents flags are separate bits // a given brush can contribute multiple content bits // these definitions also need to be in q_shared.h! diff --git a/code/qcommon/unzip.c b/code/qcommon/unzip.c index b5043c1927..9a8ee451cb 100644 --- a/code/qcommon/unzip.c +++ b/code/qcommon/unzip.c @@ -151,7 +151,7 @@ typedef struct /* =========================================================================== Read a byte from a gz_stream; update next_in and avail_in. Return EOF for end of file. - IN assertion: the stream s has been sucessfully opened for reading. + IN assertion: the stream s has been successfully opened for reading. */ @@ -290,8 +290,8 @@ local int strcmpcasenosensitive_internal (fileName1,fileName2) /* Compare two filename (fileName1,fileName2). - If iCaseSenisivity = 1, comparision is case sensitivity (like strcmp) - If iCaseSenisivity = 2, comparision is not case sensitivity (like strcmpi + If iCaseSenisivity = 1, comparison is case sensitivity (like strcmp) + If iCaseSenisivity = 2, comparison is not case sensitivity (like strcmpi or strcasecmp) If iCaseSenisivity = 0, case sensitivity is defaut of your operating system (like 1 on Unix, 2 on Windows) diff --git a/code/qcommon/unzip.h b/code/qcommon/unzip.h index b22b72ea13..74dee975b6 100644 --- a/code/qcommon/unzip.h +++ b/code/qcommon/unzip.h @@ -125,8 +125,8 @@ extern int ZEXPORT unzStringFileNameCompare OF ((const char* fileName1, int iCaseSensitivity)); /* Compare two filename (fileName1,fileName2). - If iCaseSenisivity = 1, comparision is case sensitivity (like strcmp) - If iCaseSenisivity = 2, comparision is not case sensitivity (like strcmpi + If iCaseSenisivity = 1, comparison is case sensitivity (like strcmp) + If iCaseSenisivity = 2, comparison is not case sensitivity (like strcmpi or strcasecmp) If iCaseSenisivity = 0, case sensitivity is defaut of your operating system (like 1 on Unix, 2 on Windows) diff --git a/code/qcommon/vm_powerpc.c b/code/qcommon/vm_powerpc.c index 3d9ede65f5..75e10aaa3d 100644 --- a/code/qcommon/vm_powerpc.c +++ b/code/qcommon/vm_powerpc.c @@ -401,7 +401,7 @@ struct symbolic_jump { // extensions / modifiers (branch-link) unsigned long ext; - // dest_instruction refering to this jump + // dest_instruction referring to this jump dest_instruction_t *parent; // next jump @@ -656,7 +656,7 @@ PPC_MakeFastMask( int mask ) * function local registers, * * normally only volatile registers are used, but if there aren't enough - * or function has to preserve some value while calling annother one + * or function has to preserve some value while calling another one * then caller safe registers are used as well */ static const long int gpr_list[] = { diff --git a/code/renderercommon/tr_image_png.c b/code/renderercommon/tr_image_png.c index 7b6fbada89..3ec8fdec5d 100644 --- a/code/renderercommon/tr_image_png.c +++ b/code/renderercommon/tr_image_png.c @@ -571,7 +571,7 @@ static uint32_t DecompressIDATs(struct BufferedFile *BF, uint8_t **Buffer) { /* * Rewind to the start of this adventure - * and return unsuccessfull + * and return unsuccessful */ BufferedFileRewind(BF, BytesToRewind); @@ -754,7 +754,7 @@ static uint32_t DecompressIDATs(struct BufferedFile *BF, uint8_t **Buffer) ri.Free(CompressedData); /* - * Check if the last puff() was successfull. + * Check if the last puff() was successful. */ if(!((puffResult == 0) && (puffDestLen > 0))) diff --git a/code/renderercommon/tr_public.h b/code/renderercommon/tr_public.h index 38d647f673..8573860b16 100644 --- a/code/renderercommon/tr_public.h +++ b/code/renderercommon/tr_public.h @@ -151,7 +151,7 @@ typedef struct { void (*CM_DrawDebugSurface)( void (*drawPoly)(int color, int numPoints, float *points) ); // a -1 return means the file does not exist - // NULL can be passed for buf to just determine existance + // NULL can be passed for buf to just determine existence int (*FS_FileIsInPAK)( const char *name, int *pCheckSum ); long (*FS_ReadFile)( const char *name, void **buf ); void (*FS_FreeFile)( void *buf ); diff --git a/code/renderercommon/tr_types.h b/code/renderercommon/tr_types.h index dd55e2e79a..445a6da2b1 100644 --- a/code/renderercommon/tr_types.h +++ b/code/renderercommon/tr_types.h @@ -173,7 +173,7 @@ typedef enum { } glDriverType_t; typedef enum { - GLHW_GENERIC, // where everthing works the way it should + GLHW_GENERIC, // where everything works the way it should GLHW_3DFX_2D3D, // Voodoo Banshee or Voodoo3, relevant since if this is // the hardware type then there can NOT exist a secondary // display adapter diff --git a/code/renderergl1/tr_backend.c b/code/renderergl1/tr_backend.c index 869bfcd6e9..072f52c368 100644 --- a/code/renderergl1/tr_backend.c +++ b/code/renderergl1/tr_backend.c @@ -541,7 +541,7 @@ void RB_RenderDrawSurfList( drawSurf_t *drawSurfs, int numDrawSurfs ) { // // change the tess parameters if needed - // a "entityMergable" shader is a shader that can have surfaces from seperate + // a "entityMergable" shader is a shader that can have surfaces from separate // entities merged into a single batch, like smoke and blood puff sprites if ( shader != NULL && ( shader != oldShader || fogNum != oldFogNum || dlighted != oldDlighted || ( entityNum != oldEntityNum && !shader->entityMergable ) ) ) { @@ -742,7 +742,7 @@ void RE_StretchRaw (int x, int y, int w, int h, int cols, int rows, const byte * RB_EndSurface(); } - // we definately want to sync every frame for the cinematics + // we definitely want to sync every frame for the cinematics qglFinish(); start = 0; diff --git a/code/renderergl1/tr_bsp.c b/code/renderergl1/tr_bsp.c index 1e660f28ae..83862c28d0 100644 --- a/code/renderergl1/tr_bsp.c +++ b/code/renderergl1/tr_bsp.c @@ -1428,7 +1428,7 @@ static void R_LoadNodesAndLeafs (lump_t *nodeLump, lump_t *leafLump) { out->nummarksurfaces = LittleLong(inLeaf->numLeafSurfaces); } - // chain decendants + // chain descendants R_SetParent (s_worldData.nodes, NULL); } diff --git a/code/renderergl1/tr_image.c b/code/renderergl1/tr_image.c index 7a4b1baeca..c8eb7c0f59 100644 --- a/code/renderergl1/tr_image.c +++ b/code/renderergl1/tr_image.c @@ -941,7 +941,7 @@ static int numImageLoaders = ARRAY_LEN( imageLoaders ); ================= R_LoadImage -Loads any of the supported image types into a cannonical +Loads any of the supported image types into a canonical 32 bit format. ================= */ @@ -1400,7 +1400,7 @@ SKINS CommaParse This is unfortunate, but the skin files aren't -compatable with our normal parsing rules. +compatible with our normal parsing rules. ================== */ static char *CommaParse( char **data_p ) { diff --git a/code/renderergl1/tr_light.c b/code/renderergl1/tr_light.c index 80b55f5aa8..3eac0535f9 100644 --- a/code/renderergl1/tr_light.c +++ b/code/renderergl1/tr_light.c @@ -134,7 +134,7 @@ static void R_SetupEntityLightingGrid( trRefEntity_t *ent ) { float totalFactor; if ( ent->e.renderfx & RF_LIGHTING_ORIGIN ) { - // seperate lightOrigins are needed so an object that is + // separate lightOrigins are needed so an object that is // sinking into the ground can still be lit, and so // multi-part models can be lit identically VectorCopy( ent->e.lightingOrigin, lightOrigin ); @@ -304,7 +304,7 @@ void R_SetupEntityLighting( const trRefdef_t *refdef, trRefEntity_t *ent ) { // trace a sample point down to find ambient light // if ( ent->e.renderfx & RF_LIGHTING_ORIGIN ) { - // seperate lightOrigins are needed so an object that is + // separate lightOrigins are needed so an object that is // sinking into the ground can still be lit, and so // multi-part models can be lit identically VectorCopy( ent->e.lightingOrigin, lightOrigin ); diff --git a/code/renderergl1/tr_local.h b/code/renderergl1/tr_local.h index ffd824a5f0..b7789eae49 100644 --- a/code/renderergl1/tr_local.h +++ b/code/renderergl1/tr_local.h @@ -840,7 +840,7 @@ typedef struct { int msec; // total msec for backend run } backEndCounters_t; -// all state modified by the back end is seperated +// all state modified by the back end is separated // from the front end state typedef struct { trRefdef_t refdef; @@ -981,7 +981,7 @@ extern cvar_t *r_measureOverdraw; // enables stencil buffer overdraw measuremen extern cvar_t *r_lodbias; // push/pull LOD transitions extern cvar_t *r_lodscale; -extern cvar_t *r_primitives; // "0" = based on compiled vertex array existance +extern cvar_t *r_primitives; // "0" = based on compiled vertex array existence // "1" = glDrawElemet tristrips // "2" = glDrawElements triangles // "-1" = no drawing diff --git a/code/renderergl1/tr_shade.c b/code/renderergl1/tr_shade.c index 57a43c5a5d..1931000613 100644 --- a/code/renderergl1/tr_shade.c +++ b/code/renderergl1/tr_shade.c @@ -448,7 +448,7 @@ static void ProjectDlightTexture_altivec( void ) { dlight_t *dl; if ( !( tess.dlightBits & ( 1 << l ) ) ) { - continue; // this surface definately doesn't have any of this light + continue; // this surface definitely doesn't have any of this light } texCoords = texCoordsArray[0]; colors = colorArray[0]; @@ -613,7 +613,7 @@ static void ProjectDlightTexture_scalar( void ) { dlight_t *dl; if ( !( tess.dlightBits & ( 1 << l ) ) ) { - continue; // this surface definately doesn't have any of this light + continue; // this surface definitely doesn't have any of this light } texCoords = texCoordsArray[0]; colors = colorArray[0]; @@ -745,7 +745,7 @@ static void ProjectDlightTexture_scalar( void ) { static void ProjectDlightTexture( void ) { #if idppc_altivec if (com_altivec->integer) { - // must be in a seperate function or G3 systems will crash. + // must be in a separate function or G3 systems will crash. ProjectDlightTexture_altivec(); return; } diff --git a/code/renderergl1/tr_shade_calc.c b/code/renderergl1/tr_shade_calc.c index 58b54270df..511ebb4aaa 100644 --- a/code/renderergl1/tr_shade_calc.c +++ b/code/renderergl1/tr_shade_calc.c @@ -345,7 +345,7 @@ static void GlobalVectorToLocal( const vec3_t in, vec3_t out ) { ===================== AutospriteDeform -Assuming all the triangles for this shader are independant +Assuming all the triangles for this shader are independent quads, rebuild them as forward facing sprites ===================== */ @@ -1206,7 +1206,7 @@ void RB_CalcDiffuseColor( unsigned char *colors ) { #if idppc_altivec if (com_altivec->integer) { - // must be in a seperate function or G3 systems will crash. + // must be in a separate function or G3 systems will crash. RB_CalcDiffuseColor_altivec( colors ); return; } diff --git a/code/renderergl1/tr_shader.c b/code/renderergl1/tr_shader.c index c1d9628b60..2384a1188b 100644 --- a/code/renderergl1/tr_shader.c +++ b/code/renderergl1/tr_shader.c @@ -2088,7 +2088,7 @@ static shader_t *GeneratePermanentShader( void ) { VertexLightingCollapse If vertex lighting is enabled, only render a single -pass, trying to guess which is the correct one to best aproximate +pass, trying to guess which is the correct one to best approximate what it is supposed to look like. ================= */ @@ -2493,18 +2493,18 @@ be defined for every single image used in the game, three default shader behaviors can be auto-created for any image: If lightmapIndex == LIGHTMAP_NONE, then the image will have -dynamic diffuse lighting applied to it, as apropriate for most +dynamic diffuse lighting applied to it, as appropriate for most entity skin surfaces. If lightmapIndex == LIGHTMAP_2D, then the image will be used for 2D rendering unless an explicit shader is found If lightmapIndex == LIGHTMAP_BY_VERTEX, then the image will use -the vertex rgba modulate values, as apropriate for misc_model +the vertex rgba modulate values, as appropriate for misc_model pre-lit surfaces. Other lightmapIndex values will have a lightmap stage created -and src*dest blending applied with the texture, as apropriate for +and src*dest blending applied with the texture, as appropriate for most world construction surfaces. =============== @@ -2551,7 +2551,7 @@ shader_t *R_FindShader( const char *name, int lightmapIndex, qboolean mipRawImag InitShader( strippedName, lightmapIndex ); - // FIXME: set these "need" values apropriately + // FIXME: set these "need" values appropriately shader.needsNormal = qtrue; shader.needsST1 = qtrue; shader.needsST2 = qtrue; @@ -2689,7 +2689,7 @@ qhandle_t RE_RegisterShaderFromImage(const char *name, int lightmapIndex, image_ InitShader( name, lightmapIndex ); - // FIXME: set these "need" values apropriately + // FIXME: set these "need" values appropriately shader.needsNormal = qtrue; shader.needsST1 = qtrue; shader.needsST2 = qtrue; diff --git a/code/renderergl1/tr_surface.c b/code/renderergl1/tr_surface.c index 9aadf95f24..0eb1e5c869 100644 --- a/code/renderergl1/tr_surface.c +++ b/code/renderergl1/tr_surface.c @@ -569,7 +569,7 @@ static void VectorArrayNormalize(vec4_t *normals, unsigned int count) // Vanilla PPC code, but since PPC has a reciprocal square root estimate instruction, // runs *much* faster than calling sqrt(). We'll use a single Newton-Raphson - // refinement step to get a little more precision. This seems to yeild results + // refinement step to get a little more precision. This seems to yield results // that are correct to 3 decimal places and usually correct to at least 4 (sometimes 5). // (That is, for the given input range of about 0.6 to 2.0). do { @@ -844,7 +844,7 @@ static void LerpMeshVertexes(md3Surface_t *surf, float backlerp) { #if idppc_altivec if (com_altivec->integer) { - // must be in a seperate function or G3 systems will crash. + // must be in a separate function or G3 systems will crash. LerpMeshVertexes_altivec( surf, backlerp ); return; } diff --git a/code/renderergl2/tr_backend.c b/code/renderergl2/tr_backend.c index decccbf90b..955bb70eb1 100644 --- a/code/renderergl2/tr_backend.c +++ b/code/renderergl2/tr_backend.c @@ -471,7 +471,7 @@ void RB_RenderDrawSurfList( drawSurf_t *drawSurfs, int numDrawSurfs ) { // // change the tess parameters if needed - // a "entityMergable" shader is a shader that can have surfaces from seperate + // a "entityMergable" shader is a shader that can have surfaces from separate // entities merged into a single batch, like smoke and blood puff sprites if ( shader != NULL && ( shader != oldShader || fogNum != oldFogNum || dlighted != oldDlighted || pshadowed != oldPshadowed || cubemapIndex != oldCubemapIndex || ( entityNum != oldEntityNum && !shader->entityMergable ) ) ) { @@ -689,7 +689,7 @@ void RE_StretchRaw (int x, int y, int w, int h, int cols, int rows, const byte * RB_EndSurface(); } - // we definately want to sync every frame for the cinematics + // we definitely want to sync every frame for the cinematics qglFinish(); start = 0; diff --git a/code/renderergl2/tr_bsp.c b/code/renderergl2/tr_bsp.c index 0480f91eac..ad5fe3a30d 100644 --- a/code/renderergl2/tr_bsp.c +++ b/code/renderergl2/tr_bsp.c @@ -1947,7 +1947,7 @@ static void R_LoadNodesAndLeafs (lump_t *nodeLump, lump_t *leafLump) { out->nummarksurfaces = LittleLong(inLeaf->numLeafSurfaces); } - // chain decendants + // chain descendants R_SetParent (s_worldData.nodes, NULL); } diff --git a/code/renderergl2/tr_fbo.c b/code/renderergl2/tr_fbo.c index 98c5b50300..26f50c3f98 100644 --- a/code/renderergl2/tr_fbo.c +++ b/code/renderergl2/tr_fbo.c @@ -37,7 +37,7 @@ qboolean R_CheckFBO(const FBO_t * fbo) if(code == GL_FRAMEBUFFER_COMPLETE) return qtrue; - // an error occured + // an error occurred switch (code) { case GL_FRAMEBUFFER_UNSUPPORTED: diff --git a/code/renderergl2/tr_image.c b/code/renderergl2/tr_image.c index 169343dc9f..90931877e3 100644 --- a/code/renderergl2/tr_image.c +++ b/code/renderergl2/tr_image.c @@ -2286,7 +2286,7 @@ static int numImageLoaders = ARRAY_LEN( imageLoaders ); ================= R_LoadImage -Loads any of the supported image types into a cannonical +Loads any of the supported image types into a canonical 32 bit format. ================= */ @@ -2951,7 +2951,7 @@ SKINS CommaParse This is unfortunate, but the skin files aren't -compatable with our normal parsing rules. +compatible with our normal parsing rules. ================== */ static char *CommaParse( char **data_p ) { diff --git a/code/renderergl2/tr_light.c b/code/renderergl2/tr_light.c index f188626886..484159f3a1 100644 --- a/code/renderergl2/tr_light.c +++ b/code/renderergl2/tr_light.c @@ -138,7 +138,7 @@ static void R_SetupEntityLightingGrid( trRefEntity_t *ent, world_t *world ) { float totalFactor; if ( ent->e.renderfx & RF_LIGHTING_ORIGIN ) { - // seperate lightOrigins are needed so an object that is + // separate lightOrigins are needed so an object that is // sinking into the ground can still be lit, and so // multi-part models can be lit identically VectorCopy( ent->e.lightingOrigin, lightOrigin ); @@ -335,7 +335,7 @@ void R_SetupEntityLighting( const trRefdef_t *refdef, trRefEntity_t *ent ) { // trace a sample point down to find ambient light // if ( ent->e.renderfx & RF_LIGHTING_ORIGIN ) { - // seperate lightOrigins are needed so an object that is + // separate lightOrigins are needed so an object that is // sinking into the ground can still be lit, and so // multi-part models can be lit identically VectorCopy( ent->e.lightingOrigin, lightOrigin ); diff --git a/code/renderergl2/tr_local.h b/code/renderergl2/tr_local.h index f5f4b79e4f..238fb0754a 100644 --- a/code/renderergl2/tr_local.h +++ b/code/renderergl2/tr_local.h @@ -1392,7 +1392,7 @@ typedef struct { int msec; // total msec for backend run } backEndCounters_t; -// all state modified by the back end is seperated +// all state modified by the back end is separated // from the front end state typedef struct { trRefdef_t refdef; diff --git a/code/renderergl2/tr_shade.c b/code/renderergl2/tr_shade.c index 0366247202..778a74c4e6 100644 --- a/code/renderergl2/tr_shade.c +++ b/code/renderergl2/tr_shade.c @@ -341,7 +341,7 @@ static void ProjectDlightTexture( void ) { vec4_t vector; if ( !( tess.dlightBits & ( 1 << l ) ) ) { - continue; // this surface definately doesn't have any of this light + continue; // this surface definitely doesn't have any of this light } dl = &backEnd.refdef.dlights[l]; @@ -672,7 +672,7 @@ static void ForwardDlight( void ) { vec4_t texOffTurb; if ( !( tess.dlightBits & ( 1 << l ) ) ) { - continue; // this surface definately doesn't have any of this light + continue; // this surface definitely doesn't have any of this light } dl = &backEnd.refdef.dlights[l]; @@ -836,7 +836,7 @@ static void ProjectPshadowVBOGLSL( void ) { vec4_t vector; if ( !( tess.pshadowBits & ( 1 << l ) ) ) { - continue; // this surface definately doesn't have any of this shadow + continue; // this surface definitely doesn't have any of this shadow } ps = &backEnd.refdef.pshadows[l]; diff --git a/code/renderergl2/tr_shade_calc.c b/code/renderergl2/tr_shade_calc.c index 8369ad7432..80e1a3839d 100644 --- a/code/renderergl2/tr_shade_calc.c +++ b/code/renderergl2/tr_shade_calc.c @@ -350,7 +350,7 @@ static void GlobalVectorToLocal( const vec3_t in, vec3_t out ) { ===================== AutospriteDeform -Assuming all the triangles for this shader are independant +Assuming all the triangles for this shader are independent quads, rebuild them as forward facing sprites ===================== */ diff --git a/code/renderergl2/tr_shader.c b/code/renderergl2/tr_shader.c index d9c8627938..b4f47004bd 100644 --- a/code/renderergl2/tr_shader.c +++ b/code/renderergl2/tr_shader.c @@ -2794,7 +2794,7 @@ static shader_t *GeneratePermanentShader( void ) { VertexLightingCollapse If vertex lighting is enabled, only render a single -pass, trying to guess which is the correct one to best aproximate +pass, trying to guess which is the correct one to best approximate what it is supposed to look like. ================= */ @@ -3214,18 +3214,18 @@ be defined for every single image used in the game, three default shader behaviors can be auto-created for any image: If lightmapIndex == LIGHTMAP_NONE, then the image will have -dynamic diffuse lighting applied to it, as apropriate for most +dynamic diffuse lighting applied to it, as appropriate for most entity skin surfaces. If lightmapIndex == LIGHTMAP_2D, then the image will be used for 2D rendering unless an explicit shader is found If lightmapIndex == LIGHTMAP_BY_VERTEX, then the image will use -the vertex rgba modulate values, as apropriate for misc_model +the vertex rgba modulate values, as appropriate for misc_model pre-lit surfaces. Other lightmapIndex values will have a lightmap stage created -and src*dest blending applied with the texture, as apropriate for +and src*dest blending applied with the texture, as appropriate for most world construction surfaces. =============== diff --git a/code/server/server.h b/code/server/server.h index 7ffe66ff98..ec1620aa89 100644 --- a/code/server/server.h +++ b/code/server/server.h @@ -135,9 +135,9 @@ typedef struct client_s { char userinfo[MAX_INFO_STRING]; // name, etc char reliableCommands[MAX_RELIABLE_COMMANDS][MAX_STRING_CHARS]; - int reliableSequence; // last added reliable message, not necesarily sent or acknowledged yet + int reliableSequence; // last added reliable message, not necessarily sent or acknowledged yet int reliableAcknowledge; // last acknowledged reliable message - int reliableSent; // last sent reliable message, not necesarily acknowledged yet + int reliableSent; // last sent reliable message, not necessarily acknowledged yet int messageAcknowledge; int gamestateMessageNum; // netchan->outgoingSequence of gamestate diff --git a/code/server/sv_init.c b/code/server/sv_init.c index ae9086ed55..aba0e8db1f 100644 --- a/code/server/sv_init.c +++ b/code/server/sv_init.c @@ -128,7 +128,7 @@ void SV_SetConfigstring (int index, const char *val) { // spawning a new server if ( sv.state == SS_GAME || sv.restarting ) { - // send the data to all relevent clients + // send the data to all relevant clients for (i = 0, client = svs.clients; i < sv_maxclients->integer ; i++, client++) { if ( client->state < CS_ACTIVE ) { if ( client->state == CS_PRIMED ) @@ -376,7 +376,7 @@ static void SV_ClearServer(void) { ================ SV_TouchCGame - touch the cgame.vm so that a pure client can load it if it's in a seperate pk3 + touch the cgame.vm so that a pure client can load it if it's in a separate pk3 ================ */ static void SV_TouchCGame(void) { @@ -575,7 +575,7 @@ void SV_SpawnServer( char *server, qboolean killBots ) { Cvar_Set( "sv_pakNames", p ); // if a dedicated pure server we need to touch the cgame because it could be in a - // seperate pk3 file and the client will need to load the latest cgame.qvm + // separate pk3 file and the client will need to load the latest cgame.qvm if ( com_dedicated->integer ) { SV_TouchCGame(); } diff --git a/code/server/sv_main.c b/code/server/sv_main.c index 0dd8ad47f4..4d94c1e32a 100644 --- a/code/server/sv_main.c +++ b/code/server/sv_main.c @@ -209,7 +209,7 @@ void QDECL SV_SendServerCommand(client_t *cl, const char *fmt, ...) { Com_Printf ("broadcast: %s\n", SV_ExpandNewlines((char *)message) ); } - // send the data to all relevent clients + // send the data to all relevant clients for (j = 0, client = svs.clients; j < sv_maxclients->integer ; j++, client++) { SV_AddServerCommand( client, (char *)message ); } diff --git a/code/server/sv_snapshot.c b/code/server/sv_snapshot.c index 7c04afdf03..ec76071318 100644 --- a/code/server/sv_snapshot.c +++ b/code/server/sv_snapshot.c @@ -87,7 +87,7 @@ static void SV_EmitPacketEntities( clientSnapshot_t *from, clientSnapshot_t *to, if ( newnum == oldnum ) { // delta update from old position // because the force parm is qfalse, this will not result - // in any bytes being emited if the entity has not changed at all + // in any bytes being emitted if the entity has not changed at all MSG_WriteDeltaEntity (msg, oldent, newent, qfalse ); oldindex++; newindex++; diff --git a/code/sys/sys_unix.c b/code/sys/sys_unix.c index 44d7492fb4..ebba37e8da 100644 --- a/code/sys/sys_unix.c +++ b/code/sys/sys_unix.c @@ -495,7 +495,7 @@ void Sys_FreeFileList( char **list ) ================== Sys_Sleep -Block execution for msec or until input is recieved. +Block execution for msec or until input is received. ================== */ void Sys_Sleep( int msec ) diff --git a/code/tools/asm/cmdlib.c b/code/tools/asm/cmdlib.c index d130d8dfc9..7d79e2c5a1 100644 --- a/code/tools/asm/cmdlib.c +++ b/code/tools/asm/cmdlib.c @@ -734,7 +734,7 @@ int LoadFile( const char *filename, void **bufferptr ) ============== LoadFileBlock - -rounds up memory allocation to 4K boundry +rounds up memory allocation to 4K boundary - ============== */ @@ -810,7 +810,7 @@ void DefaultExtension (char *path, const char *extension) { char *src; // -// if path doesnt have a .EXT, append extension +// if path doesn't have a .EXT, append extension // (extension should include the .) // src = path + strlen(path) - 1; diff --git a/code/tools/asm/cmdlib.h b/code/tools/asm/cmdlib.h index c2e6dd8810..3233abd6eb 100644 --- a/code/tools/asm/cmdlib.h +++ b/code/tools/asm/cmdlib.h @@ -59,7 +59,7 @@ typedef unsigned char byte; #define MAX_OS_PATH 1024 #define MEM_BLOCKSIZE 4096 -// the dec offsetof macro doesnt work very well... +// the dec offsetof macro doesn't work very well... #define myoffsetof(type,identifier) ((size_t)&((type *)0)->identifier) diff --git a/code/tools/asm/notes.txt b/code/tools/asm/notes.txt index 63297f300a..571f8cac59 100644 --- a/code/tools/asm/notes.txt +++ b/code/tools/asm/notes.txt @@ -1,5 +1,5 @@ -don't do any paramter conversion (double to float, etc) +don't do any parameter conversion (double to float, etc) diff --git a/code/tools/asm/q3asm.c b/code/tools/asm/q3asm.c index 8be0124b02..c4e1413db0 100644 --- a/code/tools/asm/q3asm.c +++ b/code/tools/asm/q3asm.c @@ -788,7 +788,7 @@ HackToSegment BIG HACK: I want to put all 32 bit values in the data segment so they can be byte swapped, and all char data in the lit -segment, but switch jump tables are emited in the lit segment and +segment, but switch jump tables are emitted in the lit segment and initialized strng variables are put in the data segment. I can change segments here, but I also need to fixup the @@ -1129,7 +1129,7 @@ STAT("BYTE"); return 0; } - // code labels are emited as instruction counts, not byte offsets, + // code labels are emitted as instruction counts, not byte offsets, // because the physical size of the code will change with // different run time compilers and we want to minimize the // size of the required translation table @@ -1564,7 +1564,7 @@ int main( int argc, char **argv ) { if ( !strcmp( argv[i], "-o" ) ) { if ( i == argc - 1 ) { - Error( "-o must preceed a filename" ); + Error( "-o must precede a filename" ); } /* Timbo of Tremulous pointed out -o not working; stock ID q3asm folded in the change. Yay. */ strcpy( outputFilename, argv[ i+1 ] ); @@ -1574,7 +1574,7 @@ int main( int argc, char **argv ) { if ( !strcmp( argv[i], "-f" ) ) { if ( i == argc - 1 ) { - Error( "-f must preceed a filename" ); + Error( "-f must precede a filename" ); } ParseOptionFile( argv[ i+1 ] ); i++; diff --git a/code/tools/lcc/doc/4.html b/code/tools/lcc/doc/4.html index 0b4b36d230..c1cd8daa8a 100644 --- a/code/tools/lcc/doc/4.html +++ b/code/tools/lcc/doc/4.html @@ -41,7 +41,7 @@

Introduction

C Compiler). These assumptions simplified the compiler, and were suitable for 32-bit architectures. But on 64-bit architectures, such as the DEC ALPHA, it's natural to have four sizes of integers and perhaps three sizes of floats, and on 16-bit -architectures, 32-bit pointers don't fit in unsigned integers. Also, the 3.x constaints +architectures, 32-bit pointers don't fit in unsigned integers. Also, the 3.x constraints limited the use of lcc's back ends for other languages, such as Java.

Version 4.x removes all of these restrictions: It supports any number of sizes for diff --git a/code/tools/lcc/etc/lcc.c b/code/tools/lcc/etc/lcc.c index aa3e78947f..a12799b62c 100644 --- a/code/tools/lcc/etc/lcc.c +++ b/code/tools/lcc/etc/lcc.c @@ -636,7 +636,7 @@ static void opt(char *arg) { clist = append(&arg[3], clist); return; } - break; /* and fall thru */ + break; /* and fall through */ case 'a': alist = append(&arg[3], alist); return; diff --git a/code/tools/lcc/src/enode.c b/code/tools/lcc/src/enode.c index 4a37618c71..d59c31bf4c 100644 --- a/code/tools/lcc/src/enode.c +++ b/code/tools/lcc/src/enode.c @@ -401,7 +401,7 @@ Tree addrof(Tree p) { Symbol t1 = q->u.sym; q->u.sym = 0; q = idtree(t1); - /* fall thru */ + /* fall through */ } case INDIR: if (p == q) diff --git a/code/tools/lcc/src/error.c b/code/tools/lcc/src/error.c index 2187c1014e..52ca11f5e0 100644 --- a/code/tools/lcc/src/error.c +++ b/code/tools/lcc/src/error.c @@ -80,7 +80,7 @@ int fatal(const char *name, const char *fmt, int n) { return 0; } -/* printtoken - print current token preceeded by a space */ +/* printtoken - print current token preceded by a space */ static void printtoken(void) { switch (t) { case ID: fprint(stderr, " `%s'", token); break; diff --git a/code/tools/lcc/src/gen.c b/code/tools/lcc/src/gen.c index 4ee170d913..1413a787b4 100644 --- a/code/tools/lcc/src/gen.c +++ b/code/tools/lcc/src/gen.c @@ -292,7 +292,7 @@ static void dumptree(Node p) { dumptree(p->kids[0]); break; } - /* else fall thru */ + /* else fall through */ case EQ: case NE: case GT: case GE: case LE: case LT: case ASGN: case BOR: case BAND: case BXOR: case RSH: case LSH: case ADD: case SUB: case DIV: case MUL: case MOD: diff --git a/code/tools/lcc/src/init.c b/code/tools/lcc/src/init.c index 172d7c0473..2f015b40fc 100644 --- a/code/tools/lcc/src/init.c +++ b/code/tools/lcc/src/init.c @@ -40,7 +40,7 @@ static int genconst(Tree e, int def) { if (isarith(e->type)) error("cast from `%t' to `%t' is illegal in constant expressions\n", e->kids[0]->type, e->type); - /* fall thru */ + /* fall through */ case CVI: case CVU: case CVF: e = e->kids[0]; continue; diff --git a/code/tools/lcc/src/tree.c b/code/tools/lcc/src/tree.c index d2b6a9173d..e8e9e1f437 100644 --- a/code/tools/lcc/src/tree.c +++ b/code/tools/lcc/src/tree.c @@ -86,7 +86,7 @@ static Tree root1(Tree p) { warning("reference to `%t' elided\n", p->type); if (isptr(p->kids[0]->type) && isvolatile(p->kids[0]->type->type)) warning("reference to `volatile %t' elided\n", p->type); - /* fall thru */ + /* fall through */ case CVI: case CVF: case CVU: case CVP: case NEG: case BCOM: case FIELD: if (warn++ == 0) diff --git a/code/ui/ui_local.h b/code/ui/ui_local.h index f63548219a..9129aee91b 100644 --- a/code/ui/ui_local.h +++ b/code/ui/ui_local.h @@ -258,7 +258,7 @@ typedef struct int width; int height; int columns; - int seperation; + int separation; } menulist_s; typedef struct diff --git a/code/ui/ui_main.c b/code/ui/ui_main.c index 32b6acd99b..c1f91364b8 100644 --- a/code/ui/ui_main.c +++ b/code/ui/ui_main.c @@ -5286,7 +5286,7 @@ void _UI_SetActiveMenu( uiMenuCommand_t menu ) { char buf[256]; // this should be the ONLY way the menu system is brought up - // enusure minumum menu data is cached + // ensure minimum menu data is cached if (Menu_Count() > 0) { vec3_t v; v[0] = v[1] = v[2] = 0; diff --git a/code/ui/ui_shared.c b/code/ui/ui_shared.c index e574f78df3..aa8489d3ac 100644 --- a/code/ui/ui_shared.c +++ b/code/ui/ui_shared.c @@ -20,7 +20,7 @@ Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA =========================================================================== */ // -// string allocation/managment +// string allocation/management #include "ui_shared.h" diff --git a/make-macosx-ub.sh b/make-macosx-ub.sh index f359824a68..c7419ecb6c 100755 --- a/make-macosx-ub.sh +++ b/make-macosx-ub.sh @@ -7,7 +7,7 @@ if [ ! -f Makefile ]; then exit 1 fi -# we want to use the oldest available SDK for max compatiblity. However 10.4 and older +# we want to use the oldest available SDK for max compatibility. However 10.4 and older # can not build 64bit binaries, making 10.5 the minimum version. This has been tested # with xcode 3.1 (xcode31_2199_developerdvd.dmg). It contains the 10.5 SDK and a decent # enough gcc to actually compile ioquake3 diff --git a/make-macosx.sh b/make-macosx.sh index 387fe640c1..6090bed0ca 100755 --- a/make-macosx.sh +++ b/make-macosx.sh @@ -33,7 +33,7 @@ if [ ! -f Makefile ]; then exit 1 fi -# we want to use the oldest available SDK for max compatiblity. However 10.4 and older +# we want to use the oldest available SDK for max compatibility. However 10.4 and older # can not build 64bit binaries, making 10.5 the minimum version. This has been tested # with xcode 3.1 (xcode31_2199_developerdvd.dmg). It contains the 10.5 SDK and a decent # enough gcc to actually compile ioquake3 diff --git a/opengl2-readme.md b/opengl2-readme.md index 1b1db2034d..7e572611e8 100644 --- a/opengl2-readme.md +++ b/opengl2-readme.md @@ -230,7 +230,7 @@ Cvars for advanced material usage: Cvars for image interpolation and generation: -* `r_imageUpsample` - Use interpolation to artifically increase +* `r_imageUpsample` - Use interpolation to artificially increase the resolution of all textures. Looks good in certain circumstances. 0 - No. (default) From d40b047f13689d58d074f31959394161e6e31621 Mon Sep 17 00:00:00 2001 From: Zack Middleton Date: Wed, 22 Nov 2017 20:17:21 -0600 Subject: [PATCH 206/240] Correct spelling of two more words Thanks Undeference for pointing out abreviations. --- code/botlib/be_aas_reach.c | 2 +- code/botlib/be_ai_chat.c | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/code/botlib/be_aas_reach.c b/code/botlib/be_aas_reach.c index eac360e584..0124bfd2c7 100644 --- a/code/botlib/be_aas_reach.c +++ b/code/botlib/be_aas_reach.c @@ -439,7 +439,7 @@ int AAS_BestReachableArea(vec3_t origin, vec3_t mins, vec3_t maxs, vec3_t goalor //VectorSubtract(absmaxs, bbmins, absmaxs); //link an invalid (-1) entity areas = AAS_LinkEntityClientBBox(absmins, absmaxs, -1, PRESENCE_CROUCH); - //get the reachable link arae + //get the reachable link area areanum = AAS_BestReachableLinkArea(areas); //unlink the invalid entity AAS_UnlinkFromAreas(areas); diff --git a/code/botlib/be_ai_chat.c b/code/botlib/be_ai_chat.c index 71f496a60d..abbcdc09a1 100644 --- a/code/botlib/be_ai_chat.c +++ b/code/botlib/be_ai_chat.c @@ -557,7 +557,7 @@ void StringReplaceWords(char *string, char *synonym, char *replacement) while(str) { //if the synonym isn't part of the replacement which is already in the string - //useful for abreviations + //useful for abbreviations str2 = StringContainsWord(string, replacement, qfalse); while(str2) { From 424e1ac7b1075b04fdb7d91ada243ecf380866e7 Mon Sep 17 00:00:00 2001 From: Zack Middleton Date: Wed, 22 Nov 2017 20:21:02 -0600 Subject: [PATCH 207/240] Fix invalid model frame developer warnings in Team Arena Models for Team Arena's holdable medkit and invulnerability effects use the frames numbers from the player's torso but the actual models only have one frame (0). --- code/cgame/cg_players.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/code/cgame/cg_players.c b/code/cgame/cg_players.c index b803367f1d..4a4abaadd8 100644 --- a/code/cgame/cg_players.c +++ b/code/cgame/cg_players.c @@ -2508,6 +2508,8 @@ void CG_Player( centity_t *cent ) { memcpy(&powerup, &torso, sizeof(torso)); powerup.hModel = cgs.media.invulnerabilityPowerupModel; + powerup.frame = 0; + powerup.oldframe = 0; powerup.customSkin = 0; // always draw powerup.renderfx &= ~RF_THIRD_PERSON; @@ -2532,6 +2534,8 @@ void CG_Player( centity_t *cent ) { if ( ci->medkitUsageTime && t < 500 ) { memcpy(&powerup, &torso, sizeof(torso)); powerup.hModel = cgs.media.medkitUsageModel; + powerup.frame = 0; + powerup.oldframe = 0; powerup.customSkin = 0; // always draw powerup.renderfx &= ~RF_THIRD_PERSON; From bad8c3ba4e3c4db43144e40503a1afed8050c682 Mon Sep 17 00:00:00 2001 From: Zack Middleton Date: Sun, 3 Dec 2017 05:51:40 -0600 Subject: [PATCH 208/240] Fix GCC 6 misleading-indentation warning --- code/game/bg_pmove.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/code/game/bg_pmove.c b/code/game/bg_pmove.c index 446008c04c..5465c57927 100644 --- a/code/game/bg_pmove.c +++ b/code/game/bg_pmove.c @@ -1694,8 +1694,8 @@ static void PM_Weapon( void ) { else if( bg_itemlist[pm->ps->stats[STAT_PERSISTANT_POWERUP]].giTag == PW_AMMOREGEN ) { addTime /= 1.3; - } - else + } + else #endif if ( pm->ps->powerups[PW_HASTE] ) { addTime /= 1.3; From bfb6ef590b0a8b67328b8e7b10b443f8d18f04d8 Mon Sep 17 00:00:00 2001 From: "Zachary J. Slater" Date: Tue, 5 Dec 2017 22:25:43 -1000 Subject: [PATCH 209/240] add SECURITY.md --- SECURITY.md | 7 +++++++ 1 file changed, 7 insertions(+) create mode 100644 SECURITY.md diff --git a/SECURITY.md b/SECURITY.md new file mode 100644 index 0000000000..449b660723 --- /dev/null +++ b/SECURITY.md @@ -0,0 +1,7 @@ +## ioquake3 Security +We take security very seriously at ioquake3. We welcome any peer review of our 100% free software source code to ensure nobody's ioquake3 clients or servers are ever compromised or hacked. + +### Where should I report security issues? + +In order to give the community time to respond and upgrade we strongly urge you report all security issues privately. +Please contact zachary@ioquake.org directly to provide details and repro steps and we will respond ASAP. From 7c2dd01873f4f1762368639869899229f071427a Mon Sep 17 00:00:00 2001 From: Zack Middleton Date: Fri, 15 Dec 2017 17:30:42 -0600 Subject: [PATCH 210/240] OpenGL2: Restore adding fixed ambient light when HDR is enabled Use opengl1 renderer behavior of adding fixed amount of ambient light to all models regardless of HDR setting. It fixes the view weapon having zero ambient light on pillcity map. --- code/renderergl2/tr_light.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/code/renderergl2/tr_light.c b/code/renderergl2/tr_light.c index 484159f3a1..3ac2154e65 100644 --- a/code/renderergl2/tr_light.c +++ b/code/renderergl2/tr_light.c @@ -356,7 +356,7 @@ void R_SetupEntityLighting( const trRefdef_t *refdef, trRefEntity_t *ent ) { } // bonus items and view weapons have a fixed minimum add - if ( !r_hdr->integer /* ent->e.renderfx & RF_MINLIGHT */ ) { + if ( 1 /* ent->e.renderfx & RF_MINLIGHT */ ) { // give everything a minimum light add ent->ambientLight[0] += tr.identityLight * 32; ent->ambientLight[1] += tr.identityLight * 32; From 76ec9fb6bd4c16d4d0b7978de726a9ff7cf67106 Mon Sep 17 00:00:00 2001 From: David CARLIER Date: Sat, 16 Dec 2017 00:08:23 +0000 Subject: [PATCH 211/240] Few LCC memory fixes. --- code/tools/lcc/cpp/tokens.c | 2 +- code/tools/lcc/etc/bytecode.c | 4 +++- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/code/tools/lcc/cpp/tokens.c b/code/tools/lcc/cpp/tokens.c index 3570896cf7..964453c392 100644 --- a/code/tools/lcc/cpp/tokens.c +++ b/code/tools/lcc/cpp/tokens.c @@ -315,7 +315,7 @@ puttokens(Tokenrow *trp) if (wbp >= &wbuf[OBS]) { write(1, wbuf, OBS); if (wbp > &wbuf[OBS]) - memcpy(wbuf, wbuf+OBS, wbp - &wbuf[OBS]); + memmove(wbuf, wbuf+OBS, wbp - &wbuf[OBS]); wbp -= OBS; } } diff --git a/code/tools/lcc/etc/bytecode.c b/code/tools/lcc/etc/bytecode.c index a5855de3a3..6e58022820 100644 --- a/code/tools/lcc/etc/bytecode.c +++ b/code/tools/lcc/etc/bytecode.c @@ -34,8 +34,10 @@ void UpdatePaths( const char *lccBinary ) { char basepath[ 1024 ]; char *p; + size_t basepathsz = sizeof( basepath ) - 1; - strncpy( basepath, lccBinary, 1024 ); + strncpy( basepath, lccBinary, basepathsz ); + basepath[basepathsz] = 0; p = strrchr( basepath, PATH_SEP ); if( p ) From c904f6d4aa2ba2760b1ab7a5a2bba5b0d1d28631 Mon Sep 17 00:00:00 2001 From: David CARLIER Date: Sat, 16 Dec 2017 18:51:34 +0000 Subject: [PATCH 212/240] fix a few potential buffer overwrite in Game VM --- code/game/g_bot.c | 8 ++++---- code/game/g_client.c | 8 ++++---- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/code/game/g_bot.c b/code/game/g_bot.c index b5731a67a5..b6b205f117 100644 --- a/code/game/g_bot.c +++ b/code/game/g_bot.c @@ -817,19 +817,19 @@ void Svcmd_BotList_f( void ) { trap_Print("^1name model aifile funname\n"); for (i = 0; i < g_numBots; i++) { - strcpy(name, Info_ValueForKey( g_botInfos[i], "name" )); + Q_strncpyz(name, Info_ValueForKey( g_botInfos[i], "name" ), sizeof( name )); if ( !*name ) { strcpy(name, "UnnamedPlayer"); } - strcpy(funname, Info_ValueForKey( g_botInfos[i], "funname" )); + Q_strncpyz(funname, Info_ValueForKey( g_botInfos[i], "funname" ), sizeof( funname )); if ( !*funname ) { strcpy(funname, ""); } - strcpy(model, Info_ValueForKey( g_botInfos[i], "model" )); + Q_strncpyz(model, Info_ValueForKey( g_botInfos[i], "model" ), sizeof( model )); if ( !*model ) { strcpy(model, "visor/default"); } - strcpy(aifile, Info_ValueForKey( g_botInfos[i], "aifile")); + Q_strncpyz(aifile, Info_ValueForKey( g_botInfos[i], "aifile"), sizeof( aifile )); if (!*aifile ) { strcpy(aifile, "bots/default_c.c"); } diff --git a/code/game/g_client.c b/code/game/g_client.c index 457e058af8..c6a0e87484 100644 --- a/code/game/g_client.c +++ b/code/game/g_client.c @@ -838,11 +838,11 @@ void ClientUserinfoChanged( int clientNum ) { teamLeader = client->sess.teamLeader; // colors - strcpy(c1, Info_ValueForKey( userinfo, "color1" )); - strcpy(c2, Info_ValueForKey( userinfo, "color2" )); + Q_strncpyz(c1, Info_ValueForKey( userinfo, "color1" ), sizeof( c1 )); + Q_strncpyz(c2, Info_ValueForKey( userinfo, "color2" ), sizeof( c2 )); - strcpy(redTeam, Info_ValueForKey( userinfo, "g_redteam" )); - strcpy(blueTeam, Info_ValueForKey( userinfo, "g_blueteam" )); + Q_strncpyz(redTeam, Info_ValueForKey( userinfo, "g_redteam" ), sizeof( redTeam )); + Q_strncpyz(blueTeam, Info_ValueForKey( userinfo, "g_blueteam" ), sizeof( blueTeam )); // send over a subset of the userinfo keys so other clients can // print scoreboards, display models, and play custom sounds From 7166a3cd3934ae743c4259768d44a90c4ab7923a Mon Sep 17 00:00:00 2001 From: Zack Middleton Date: Sat, 16 Dec 2017 12:51:56 -0600 Subject: [PATCH 213/240] Enable compiler optimization on all macOS architectures It seems to improve framerate and still work on x86 and x86_64. I haven't tested ppc64 as I don't have the hardware. ppc64 isn't built into the universal bundle either. I noticed this because compiling opus warns it may be slow due to optimization being disabled. --- Makefile | 3 +-- make-macosx-ub.sh | 2 +- 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/Makefile b/Makefile index 2ed9af8e32..4aa9f51973 100644 --- a/Makefile +++ b/Makefile @@ -419,7 +419,7 @@ ifeq ($(PLATFORM),darwin) LIBS = -framework Cocoa CLIENT_LIBS= RENDERER_LIBS= - OPTIMIZEVM= + OPTIMIZEVM = -O3 # Default minimum Mac OS X version ifeq ($(MACOSX_VERSION_MIN),) @@ -435,7 +435,6 @@ ifeq ($(PLATFORM),darwin) ifeq ($(ARCH),ppc) BASE_CFLAGS += -arch ppc -faltivec - OPTIMIZEVM += -O3 endif ifeq ($(ARCH),ppc64) BASE_CFLAGS += -arch ppc64 -faltivec diff --git a/make-macosx-ub.sh b/make-macosx-ub.sh index c7419ecb6c..997c9f425d 100755 --- a/make-macosx-ub.sh +++ b/make-macosx-ub.sh @@ -19,7 +19,7 @@ unset X86_64_MACOSX_VERSION_MIN unset X86_SDK unset X86_CFLAGS unset X86_MACOSX_VERSION_MIN -unset PPC_64_SDK +unset PPC_SDK unset PPC_CFLAGS unset PPC_MACOSX_VERSION_MIN From f3bdd6f022555fbff4528105c670354893880f90 Mon Sep 17 00:00:00 2001 From: Eugene C Date: Sun, 24 Dec 2017 00:41:13 +0200 Subject: [PATCH 214/240] Don't allow qagame module to create "botlib.log" at ANY filesystem location --- code/botlib/be_interface.c | 21 +-------------------- code/botlib/l_log.c | 5 ++++- 2 files changed, 5 insertions(+), 21 deletions(-) diff --git a/code/botlib/be_interface.c b/code/botlib/be_interface.c index 415c1b2beb..eb0efb9518 100644 --- a/code/botlib/be_interface.c +++ b/code/botlib/be_interface.c @@ -144,26 +144,7 @@ int Export_BotLibSetup(void) if(botDeveloper) { - char *homedir, *gamedir, *basegame; - char logfilename[MAX_OSPATH]; - - homedir = LibVarGetString("homedir"); - gamedir = LibVarGetString("gamedir"); - basegame = LibVarGetString("basegame"); - - if (*homedir) - { - if(*gamedir) - Com_sprintf(logfilename, sizeof(logfilename), "%s%c%s%cbotlib.log", homedir, PATH_SEP, gamedir, PATH_SEP); - else if(*basegame) - Com_sprintf(logfilename, sizeof(logfilename), "%s%c%s%cbotlib.log", homedir, PATH_SEP, basegame, PATH_SEP); - else - Com_sprintf(logfilename, sizeof(logfilename), "%s%c" BASEGAME "%cbotlib.log", homedir, PATH_SEP, PATH_SEP); - } - else - Com_sprintf(logfilename, sizeof(logfilename), "botlib.log"); - - Log_Open(logfilename); + Log_Open("botlib.log"); } botimport.Print(PRT_MESSAGE, "------- BotLib Initialization -------\n"); diff --git a/code/botlib/l_log.c b/code/botlib/l_log.c index ba51e0081d..0cfd491c8f 100644 --- a/code/botlib/l_log.c +++ b/code/botlib/l_log.c @@ -34,6 +34,7 @@ Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA #include #include "../qcommon/q_shared.h" +#include "../qcommon/qcommon.h" #include "botlib.h" #include "be_interface.h" //for botimport.Print #include "l_libvar.h" @@ -58,6 +59,7 @@ static logfile_t logfile; //=========================================================================== void Log_Open(char *filename) { + char *ospath; if (!LibVarValue("log", "0")) return; if (!filename || !strlen(filename)) { @@ -69,7 +71,8 @@ void Log_Open(char *filename) botimport.Print(PRT_ERROR, "log file %s is already opened\n", logfile.filename); return; } //end if - logfile.fp = fopen(filename, "wb"); + ospath = FS_BuildOSPath(Cvar_VariableString("fs_homepath"), "", filename); + logfile.fp = fopen(ospath, "wb"); if (!logfile.fp) { botimport.Print(PRT_ERROR, "can't open the log file %s\n", filename); From 22fcd82965f7a65b511176389149c47391f97cb1 Mon Sep 17 00:00:00 2001 From: Zack Middleton Date: Sat, 23 Dec 2017 17:01:06 -0600 Subject: [PATCH 215/240] Make FS_BuildOSPath for botlib.log consistent with typical usage I don't think anywhere else uses "" to mean current game directory. Though it's only an issue for my fork where I removed that behavior. --- code/botlib/l_log.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/code/botlib/l_log.c b/code/botlib/l_log.c index 0cfd491c8f..4d7c4e892b 100644 --- a/code/botlib/l_log.c +++ b/code/botlib/l_log.c @@ -71,7 +71,7 @@ void Log_Open(char *filename) botimport.Print(PRT_ERROR, "log file %s is already opened\n", logfile.filename); return; } //end if - ospath = FS_BuildOSPath(Cvar_VariableString("fs_homepath"), "", filename); + ospath = FS_BuildOSPath(Cvar_VariableString("fs_homepath"), Cvar_VariableString("fs_game"), filename); logfile.fp = fopen(ospath, "wb"); if (!logfile.fp) { From 6387c336b645a5cffe7b44880609ff4f10c7320a Mon Sep 17 00:00:00 2001 From: "Zachary J. Slater" Date: Sun, 7 Jan 2018 21:41:55 -1000 Subject: [PATCH 216/240] tiny readme thing --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 3dd355ce36..5e16a3da6e 100644 --- a/README.md +++ b/README.md @@ -11,7 +11,7 @@ The intent of this project is to provide a baseline Quake 3 which may be used for further development and baseq3 fun. Some of the major features currently implemented are: - * SDL backend + * SDL 2 backend * OpenAL sound API support (multiple speaker support and better sound quality) * Full x86_64 support on Linux From 7e2aa2c6274e6cdcdc07d5e987e6d851c31e8718 Mon Sep 17 00:00:00 2001 From: Zack Middleton Date: Sun, 14 Jan 2018 18:24:12 -0600 Subject: [PATCH 217/240] Remove extra plus sign from Huff_Compress() There was an extra plus sign in Huff_Compress(). It wasn't causing any issues as it does not affect the generated code. Removing it makes the source code the same as Huff_Decompress(). The odd source code was brought to my attention by Tobias Kuehnhammer. --- code/qcommon/huffman.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/code/qcommon/huffman.c b/code/qcommon/huffman.c index 1f4249842c..0283fb701d 100644 --- a/code/qcommon/huffman.c +++ b/code/qcommon/huffman.c @@ -401,7 +401,7 @@ void Huff_Compress(msg_t *mbuf, int offset) { huff_t huff; size = mbuf->cursize - offset; - buffer = mbuf->data+ + offset; + buffer = mbuf->data + offset; if (size<=0) { return; From ed1794fe172c209c0c354a085c3e995e9c4462fc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Thomas=20K=C3=B6ppe?= Date: Tue, 16 Jan 2018 16:04:17 +0000 Subject: [PATCH 218/240] Change shift expressions to unsigned types. Shifting signed values to a result that is not representable has undefined behaviour. --- code/client/cl_main.c | 4 ++-- code/qcommon/md4.c | 7 +++++-- code/qcommon/net_chan.c | 2 +- code/qcommon/vm_x86.c | 2 +- code/server/sv_client.c | 2 +- code/server/sv_init.c | 2 +- 6 files changed, 11 insertions(+), 8 deletions(-) diff --git a/code/client/cl_main.c b/code/client/cl_main.c index fde9fa1012..59be427e55 100644 --- a/code/client/cl_main.c +++ b/code/client/cl_main.c @@ -1539,7 +1539,7 @@ void CL_RequestMotd( void ) { info[0] = 0; - Com_sprintf( cls.updateChallenge, sizeof( cls.updateChallenge ), "%i", ((rand() << 16) ^ rand()) ^ Com_Milliseconds()); + Com_sprintf( cls.updateChallenge, sizeof( cls.updateChallenge ), "%i", (int)((((unsigned int)rand() << 16) ^ (unsigned int)rand()) ^ Com_Milliseconds())); Info_SetValueForKey( info, "challenge", cls.updateChallenge ); Info_SetValueForKey( info, "renderer", cls.glconfig.renderer_string ); @@ -1768,7 +1768,7 @@ void CL_Connect_f( void ) { clc.state = CA_CONNECTING; // Set a client challenge number that ideally is mirrored back by the server. - clc.challenge = ((rand() << 16) ^ rand()) ^ Com_Milliseconds(); + clc.challenge = (((unsigned int)rand() << 16) ^ (unsigned int)rand()) ^ Com_Milliseconds(); } Key_SetCatcher( 0 ); diff --git a/code/qcommon/md4.c b/code/qcommon/md4.c index 0eb23a5059..abb951c883 100644 --- a/code/qcommon/md4.c +++ b/code/qcommon/md4.c @@ -106,8 +106,11 @@ static void copy64(uint32_t *M, byte *in) int i; for (i=0;i<16;i++) - M[i] = (in[i*4+3]<<24) | (in[i*4+2]<<16) | - (in[i*4+1]<<8) | (in[i*4+0]<<0); + M[i] = + ((uint32_t)in[i*4+3] << 24) | + ((uint32_t)in[i*4+2] << 16) | + ((uint32_t)in[i*4+1] << 8) | + ((uint32_t)in[i*4+0] << 0) ; } static void copy4(byte *out,uint32_t x) diff --git a/code/qcommon/net_chan.c b/code/qcommon/net_chan.c index 0b099b95f5..9e7d9b879f 100644 --- a/code/qcommon/net_chan.c +++ b/code/qcommon/net_chan.c @@ -52,7 +52,7 @@ to the new value before sending out any replies. #define FRAGMENT_SIZE (MAX_PACKETLEN - 100) #define PACKET_HEADER 10 // two ints and a short -#define FRAGMENT_BIT (1<<31) +#define FRAGMENT_BIT (1U<<31) cvar_t *showpackets; cvar_t *showdrop; diff --git a/code/qcommon/vm_x86.c b/code/qcommon/vm_x86.c index 2435a6e909..6bf349f2a2 100644 --- a/code/qcommon/vm_x86.c +++ b/code/qcommon/vm_x86.c @@ -103,7 +103,7 @@ static int isu8(uint32_t v) static int NextConstant4(void) { - return (code[pc] | (code[pc+1]<<8) | (code[pc+2]<<16) | (code[pc+3]<<24)); + return ((unsigned int)code[pc] | ((unsigned int)code[pc+1]<<8) | ((unsigned int)code[pc+2]<<16) | ((unsigned int)code[pc+3]<<24)); } static int Constant4( void ) { diff --git a/code/server/sv_client.c b/code/server/sv_client.c index ad759eff2d..d295017258 100644 --- a/code/server/sv_client.c +++ b/code/server/sv_client.c @@ -140,7 +140,7 @@ void SV_GetChallenge(netadr_t from) } // always generate a new challenge number, so the client cannot circumvent sv_maxping - challenge->challenge = ( (rand() << 16) ^ rand() ) ^ svs.time; + challenge->challenge = ( ((unsigned int)rand() << 16) ^ (unsigned int)rand() ) ^ svs.time; challenge->wasrefused = qfalse; challenge->time = svs.time; diff --git a/code/server/sv_init.c b/code/server/sv_init.c index aba0e8db1f..51f228fd42 100644 --- a/code/server/sv_init.c +++ b/code/server/sv_init.c @@ -468,7 +468,7 @@ void SV_SpawnServer( char *server, qboolean killBots ) { Cvar_Set("cl_paused", "0"); // get a new checksum feed and restart the file system - sv.checksumFeed = ( ((int) rand() << 16) ^ rand() ) ^ Com_Milliseconds(); + sv.checksumFeed = ( ((unsigned int)rand() << 16) ^ (unsigned int)rand() ) ^ Com_Milliseconds(); FS_Restart( sv.checksumFeed ); CM_LoadMap( va("maps/%s.bsp", server), qfalse, &checksum ); From 917bca4f7d8bdef5cfe159aaa2bac6bb2b559d29 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Thomas=20K=C3=B6ppe?= Date: Tue, 16 Jan 2018 16:07:48 +0000 Subject: [PATCH 219/240] Use standard offsetof facility. Dereferencing a null pointer results in undefined behaviour. --- code/qcommon/q_shared.h | 1 + code/renderergl1/tr_bsp.c | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/code/qcommon/q_shared.h b/code/qcommon/q_shared.h index 40bb760afd..4c1b821a43 100644 --- a/code/qcommon/q_shared.h +++ b/code/qcommon/q_shared.h @@ -160,6 +160,7 @@ typedef int intptr_t; #include #include #include +#include #include #include #include diff --git a/code/renderergl1/tr_bsp.c b/code/renderergl1/tr_bsp.c index 83862c28d0..5de7ff9081 100644 --- a/code/renderergl1/tr_bsp.c +++ b/code/renderergl1/tr_bsp.c @@ -330,7 +330,7 @@ static void ParseFace( dsurface_t *ds, drawVert_t *verts, msurface_t *surf, int numIndexes = LittleLong( ds->numIndexes ); // create the srfSurfaceFace_t - sfaceSize = ( size_t ) &((srfSurfaceFace_t *)0)->points[numPoints]; + sfaceSize = offsetof( srfSurfaceFace_t, points ) + sizeof( *cv->points ) * numPoints; ofsIndexes = sfaceSize; sfaceSize += sizeof( int ) * numIndexes; From 3a6af1bc48dca97d589b9643e39bc96be31acba9 Mon Sep 17 00:00:00 2001 From: Zack Middleton Date: Sun, 21 Jan 2018 02:02:47 -0600 Subject: [PATCH 220/240] Fix VMs being able to change CVAR_PROTECTED cvars VM could use Cvar_Register to set a protected cvar as user created and was then able to use Cvar_Register with CVAR_ROM to change the value. Don't allow Cvar_Register to affect protected cvars and prevent VMs from adding internal flags to any cvars (creator, modified, protected, nonexistent). Reported by Noah Metzger (Chomenor). --- code/qcommon/cvar.c | 34 ++++++++++++++++++++++++++++++++-- 1 file changed, 32 insertions(+), 2 deletions(-) diff --git a/code/qcommon/cvar.c b/code/qcommon/cvar.c index f07705dba4..5a0e6906ba 100644 --- a/code/qcommon/cvar.c +++ b/code/qcommon/cvar.c @@ -1332,12 +1332,42 @@ void Cvar_Register(vmCvar_t *vmCvar, const char *varName, const char *defaultVal // flags. Unfortunately some historical game code (including single player // baseq3) sets both flags. We unset CVAR_ROM for such cvars. if ((flags & (CVAR_ARCHIVE | CVAR_ROM)) == (CVAR_ARCHIVE | CVAR_ROM)) { - Com_DPrintf( S_COLOR_YELLOW "WARNING: Unsetting CVAR_ROM cvar '%s', " + Com_DPrintf( S_COLOR_YELLOW "WARNING: Unsetting CVAR_ROM from cvar '%s', " "since it is also CVAR_ARCHIVE\n", varName ); flags &= ~CVAR_ROM; } - cv = Cvar_Get(varName, defaultValue, flags | CVAR_VM_CREATED); + // Don't allow VM to specific a different creator or other internal flags. + if ( flags & CVAR_USER_CREATED ) { + Com_DPrintf( S_COLOR_YELLOW "WARNING: VM tried to set CVAR_USER_CREATED on cvar '%s'\n", varName ); + flags &= ~CVAR_USER_CREATED; + } + if ( flags & CVAR_SERVER_CREATED ) { + Com_DPrintf( S_COLOR_YELLOW "WARNING: VM tried to set CVAR_SERVER_CREATED on cvar '%s'\n", varName ); + flags &= ~CVAR_SERVER_CREATED; + } + if ( flags & CVAR_PROTECTED ) { + Com_DPrintf( S_COLOR_YELLOW "WARNING: VM tried to set CVAR_PROTECTED on cvar '%s'\n", varName ); + flags &= ~CVAR_PROTECTED; + } + if ( flags & CVAR_MODIFIED ) { + Com_DPrintf( S_COLOR_YELLOW "WARNING: VM tried to set CVAR_MODIFIED on cvar '%s'\n", varName ); + flags &= ~CVAR_MODIFIED; + } + if ( flags & CVAR_NONEXISTENT ) { + Com_DPrintf( S_COLOR_YELLOW "WARNING: VM tried to set CVAR_NONEXISTENT on cvar '%s'\n", varName ); + flags &= ~CVAR_NONEXISTENT; + } + + cv = Cvar_FindVar(varName); + + // Don't modify cvar if it's protected. + if ( cv && ( cv->flags & CVAR_PROTECTED ) ) { + Com_DPrintf( S_COLOR_YELLOW "WARNING: VM tried to register protected cvar '%s' with value '%s'%s\n", + varName, defaultValue, ( flags & ~cv->flags ) != 0 ? " and new flags" : "" ); + } else { + cv = Cvar_Get(varName, defaultValue, flags | CVAR_VM_CREATED); + } if (!vmCvar) return; From adef4e6c9effb4d3e1c65bca4597116eca99ef4b Mon Sep 17 00:00:00 2001 From: Zack Middleton Date: Sun, 21 Jan 2018 02:38:08 -0600 Subject: [PATCH 221/240] Don't register fs_game cvar everywhere just to get the value --- code/client/cl_ui.c | 12 ++++++------ code/qcommon/common.c | 11 ++++------- code/qcommon/files.c | 10 +++------- code/server/sv_client.c | 10 ++++------ 4 files changed, 17 insertions(+), 26 deletions(-) diff --git a/code/client/cl_ui.c b/code/client/cl_ui.c index 4d2628e5cb..714364847f 100644 --- a/code/client/cl_ui.c +++ b/code/client/cl_ui.c @@ -639,9 +639,9 @@ CLUI_GetCDKey */ static void CLUI_GetCDKey( char *buf, int buflen ) { #ifndef STANDALONE - cvar_t *fs; - fs = Cvar_Get ("fs_game", "", CVAR_INIT|CVAR_SYSTEMINFO ); - if (UI_usesUniqueCDKey() && fs && fs->string[0] != 0) { + const char *gamedir; + gamedir = Cvar_VariableString( "fs_game" ); + if (UI_usesUniqueCDKey() && gamedir[0] != 0) { Com_Memcpy( buf, &cl_cdkey[16], 16); buf[16] = 0; } else { @@ -661,9 +661,9 @@ CLUI_SetCDKey */ #ifndef STANDALONE static void CLUI_SetCDKey( char *buf ) { - cvar_t *fs; - fs = Cvar_Get ("fs_game", "", CVAR_INIT|CVAR_SYSTEMINFO ); - if (UI_usesUniqueCDKey() && fs && fs->string[0] != 0) { + const char *gamedir; + gamedir = Cvar_VariableString( "fs_game" ); + if (UI_usesUniqueCDKey() && gamedir[0] != 0) { Com_Memcpy( &cl_cdkey[16], buf, 16 ); cl_cdkey[32] = 0; // set the flag so the fle will be written at the next opportunity diff --git a/code/qcommon/common.c b/code/qcommon/common.c index dc340b5379..3dbee88f4b 100644 --- a/code/qcommon/common.c +++ b/code/qcommon/common.c @@ -2943,9 +2943,6 @@ Writes key bindings and archived cvars to config file if modified =============== */ void Com_WriteConfiguration( void ) { -#if !defined(DEDICATED) && !defined(STANDALONE) - cvar_t *fs; -#endif // if we are quiting without fully initializing, make sure // we don't write out anything if ( !com_fullyInitialized ) { @@ -2961,12 +2958,12 @@ void Com_WriteConfiguration( void ) { // not needed for dedicated or standalone #if !defined(DEDICATED) && !defined(STANDALONE) - fs = Cvar_Get ("fs_game", "", CVAR_INIT|CVAR_SYSTEMINFO ); - if(!com_standalone->integer) { - if (UI_usesUniqueCDKey() && fs && fs->string[0] != 0) { - Com_WriteCDKey( fs->string, &cl_cdkey[16] ); + const char *gamedir; + gamedir = Cvar_VariableString( "fs_game" ); + if (UI_usesUniqueCDKey() && gamedir[0] != 0) { + Com_WriteCDKey( gamedir, &cl_cdkey[16] ); } else { Com_WriteCDKey( BASEGAME, cl_cdkey ); } diff --git a/code/qcommon/files.c b/code/qcommon/files.c index 246b3cd96a..3545cd1d8c 100644 --- a/code/qcommon/files.c +++ b/code/qcommon/files.c @@ -3369,14 +3369,10 @@ static void FS_Startup( const char *gameName ) } #ifndef STANDALONE - if(!com_standalone->integer) - { - cvar_t *fs; - + if (!com_standalone->integer) { Com_ReadCDKey(BASEGAME); - fs = Cvar_Get ("fs_game", "", CVAR_INIT|CVAR_SYSTEMINFO ); - if (fs && fs->string[0] != 0) { - Com_AppendCDKey( fs->string ); + if (fs_gamedirvar->string[0]) { + Com_AppendCDKey(fs_gamedirvar->string); } } #endif diff --git a/code/server/sv_client.c b/code/server/sv_client.c index d295017258..3d3ab6e180 100644 --- a/code/server/sv_client.c +++ b/code/server/sv_client.c @@ -176,15 +176,13 @@ void SV_GetChallenge(netadr_t from) else { // otherwise send their ip to the authorize server - cvar_t *fs; - char game[1024]; + const char *game; Com_DPrintf( "sending getIpAuthorize for %s\n", NET_AdrToString( from )); - strcpy(game, BASEGAME); - fs = Cvar_Get ("fs_game", "", CVAR_INIT|CVAR_SYSTEMINFO ); - if (fs && fs->string[0] != 0) { - strcpy(game, fs->string); + game = Cvar_VariableString( "fs_game" ); + if (game[0] == 0) { + game = BASEGAME; } // the 0 is for backwards compatibility with obsolete sv_allowanonymous flags From 78ca670d4f89ffa7f887f8f32d6d6e39bd9c78c3 Mon Sep 17 00:00:00 2001 From: Zack Middleton Date: Sun, 21 Jan 2018 03:50:43 -0600 Subject: [PATCH 222/240] Don't let VMs change engine latch cvars immediately If a VM increases sv_maxclients while a server is running the engine will crash. The value should be latched until engine decides to update the cvar; the same as when a user sets it. --- code/qcommon/cvar.c | 29 ++++++++++++++++++++--------- 1 file changed, 20 insertions(+), 9 deletions(-) diff --git a/code/qcommon/cvar.c b/code/qcommon/cvar.c index 5a0e6906ba..6f1ca432fb 100644 --- a/code/qcommon/cvar.c +++ b/code/qcommon/cvar.c @@ -638,18 +638,29 @@ Cvar_SetSafe void Cvar_SetSafe( const char *var_name, const char *value ) { int flags = Cvar_Flags( var_name ); + qboolean force = qtrue; - if((flags != CVAR_NONEXISTENT) && (flags & CVAR_PROTECTED)) + if ( flags != CVAR_NONEXISTENT ) { - if( value ) - Com_Error( ERR_DROP, "Restricted source tried to set " - "\"%s\" to \"%s\"", var_name, value ); - else - Com_Error( ERR_DROP, "Restricted source tried to " - "modify \"%s\"", var_name ); - return; + if ( flags & CVAR_PROTECTED ) + { + if( value ) + Com_Error( ERR_DROP, "Restricted source tried to set " + "\"%s\" to \"%s\"", var_name, value ); + else + Com_Error( ERR_DROP, "Restricted source tried to " + "modify \"%s\"", var_name ); + return; + } + + // don't let VMs or server change engine latched cvars instantly + if ( ( flags & CVAR_LATCH ) && !( flags & CVAR_VM_CREATED ) ) + { + force = qfalse; + } } - Cvar_Set( var_name, value ); + + Cvar_Set2 (var_name, value, force); } /* From 3638f69dff3d6b3e624c0a6550f16482b172bc11 Mon Sep 17 00:00:00 2001 From: Zack Middleton Date: Sun, 21 Jan 2018 04:27:55 -0600 Subject: [PATCH 223/240] Fix fs_game '..' reading outside of home and base path VMs could set fs_game to '..' at anytime to access files outside of home and base path. fs_game sent by server to clients could also be '..' to access files outside of home and base path. '..' was not caught by FS_CheckDirTraversal() as it expects filenames not a single directory. I've made fs_game be latched to prevent VMs from changing it with no good way to validate it before it's used. com_basegame and fs_basegame are now latched as well. Additionally, it's now possible to change com_basegame while the engine is running. game_restart or vid_restart will make it take affect. com_homepath is now CVAR_PROTECTED to prevent VMs from changing it to a directory traversal. This requires my two previous commits for preventing VMs from changing engine latch cvars and only Cvar_Get fs_game in FS_Startup (so CVAR_INIT isn't added in serveral other places). Reported by Noah Metzger (Chomenor). --- code/client/cl_main.c | 4 +-- code/client/cl_parse.c | 2 +- code/qcommon/common.c | 21 +++++---------- code/qcommon/files.c | 58 ++++++++++++++++++++++++++++++++---------- code/qcommon/qcommon.h | 1 + 5 files changed, 54 insertions(+), 32 deletions(-) diff --git a/code/client/cl_main.c b/code/client/cl_main.c index 59be427e55..82d0edfba2 100644 --- a/code/client/cl_main.c +++ b/code/client/cl_main.c @@ -1245,7 +1245,7 @@ void CL_ClearMemory(qboolean shutdownRef) CL_ShutdownAll(shutdownRef); // if not running a server clear the whole hunk - if ( !com_sv_running->integer ) { + if ( !com_sv_running || !com_sv_running->integer ) { // clear the whole hunk Hunk_Clear(); // clear collision map data @@ -1361,7 +1361,7 @@ static void CL_OldGame(void) { // change back to previous fs_game cl_oldGameSet = qfalse; - Cvar_Set2("fs_game", cl_oldGame, qtrue); + Cvar_Set("fs_game", cl_oldGame); FS_ConditionalRestart(clc.checksumFeed, qfalse); } } diff --git a/code/client/cl_parse.c b/code/client/cl_parse.c index 318c8d8830..80bbec2d5f 100644 --- a/code/client/cl_parse.c +++ b/code/client/cl_parse.c @@ -399,7 +399,7 @@ void CL_SystemInfoChanged( void ) { // ehw! if (!Q_stricmp(key, "fs_game")) { - if(FS_CheckDirTraversal(value)) + if(FS_InvalidGameDir(value)) { Com_Printf(S_COLOR_YELLOW "WARNING: Server sent invalid fs_game value %s\n", value); continue; diff --git a/code/qcommon/common.c b/code/qcommon/common.c index 3dbee88f4b..6580073b38 100644 --- a/code/qcommon/common.c +++ b/code/qcommon/common.c @@ -2404,6 +2404,9 @@ void Com_GameRestart(int checksumFeed, qboolean disconnect) CL_Shutdown("Game directory changed", disconnect, qfalse); } + // change com_basegame to latched value + com_basegame = Cvar_Get("com_basegame", BASEGAME, CVAR_LATCH|CVAR_NORESTART); + FS_Restart(checksumFeed); // Clean out any user and VM created cvars @@ -2439,16 +2442,7 @@ Expose possibility to change current running mod to the user void Com_GameRestart_f(void) { - if(!FS_FilenameCompare(Cmd_Argv(1), com_basegame->string)) - { - // This is the standard base game. Servers and clients should - // use "" and not the standard basegame name because this messes - // up pak file negotiation and lots of other stuff - - Cvar_Set("fs_game", ""); - } - else - Cvar_Set("fs_game", Cmd_Argv(1)); + Cvar_Set("fs_game", Cmd_Argv(1)); Com_GameRestart(0, qtrue); } @@ -2705,11 +2699,8 @@ void Com_Init( char *commandLine ) { CL_InitKeyCommands(); com_standalone = Cvar_Get("com_standalone", "0", CVAR_ROM); - com_basegame = Cvar_Get("com_basegame", BASEGAME, CVAR_INIT); - com_homepath = Cvar_Get("com_homepath", "", CVAR_INIT); - - if(!com_basegame->string[0]) - Cvar_ForceReset("com_basegame"); + com_basegame = Cvar_Get("com_basegame", BASEGAME, CVAR_LATCH|CVAR_NORESTART); + com_homepath = Cvar_Get("com_homepath", "", CVAR_INIT|CVAR_PROTECTED); FS_InitFilesystem (); diff --git a/code/qcommon/files.c b/code/qcommon/files.c index 3545cd1d8c..eb61af9c14 100644 --- a/code/qcommon/files.c +++ b/code/qcommon/files.c @@ -3067,6 +3067,24 @@ qboolean FS_CheckDirTraversal(const char *checkdir) return qfalse; } +/* +================ +FS_InvalidGameDir + +return true if path is a reference to current directory or directory traversal +================ +*/ +qboolean FS_InvalidGameDir( const char *gamedir ) { + if ( !strcmp( gamedir, "." ) || !strcmp( gamedir, ".." ) + || !strcmp( gamedir, "/" ) || !strcmp( gamedir, "\\" ) + || strstr( gamedir, "/.." ) || strstr( gamedir, "\\.." ) + || FS_CheckDirTraversal( gamedir ) ) { + return qtrue; + } + + return qfalse; +} + /* ================ FS_ComparePaks @@ -3301,13 +3319,34 @@ static void FS_Startup( const char *gameName ) fs_debug = Cvar_Get( "fs_debug", "0", 0 ); fs_basepath = Cvar_Get ("fs_basepath", Sys_DefaultInstallPath(), CVAR_INIT|CVAR_PROTECTED ); - fs_basegame = Cvar_Get ("fs_basegame", "", CVAR_INIT ); + fs_basegame = Cvar_Get ("fs_basegame", "", CVAR_LATCH|CVAR_NORESTART ); homePath = Sys_DefaultHomePath(); if (!homePath || !homePath[0]) { homePath = fs_basepath->string; } fs_homepath = Cvar_Get ("fs_homepath", homePath, CVAR_INIT|CVAR_PROTECTED ); - fs_gamedirvar = Cvar_Get ("fs_game", "", CVAR_INIT|CVAR_SYSTEMINFO ); + fs_gamedirvar = Cvar_Get ("fs_game", "", CVAR_LATCH|CVAR_NORESTART|CVAR_SYSTEMINFO ); + + if (!gameName[0]) { + Cvar_ForceReset( "com_basegame" ); + } + + if (!FS_FilenameCompare(fs_gamedirvar->string, gameName)) { + // This is the standard base game. Servers and clients should + // use "" and not the standard basegame name because this messes + // up pak file negotiation and lots of other stuff + Cvar_ForceReset( "fs_game" ); + } + + if (FS_InvalidGameDir(gameName)) { + Com_Error( ERR_DROP, "Invalid com_basegame '%s'", gameName ); + } + if (FS_InvalidGameDir(fs_basegame->string)) { + Com_Error( ERR_DROP, "Invalid fs_basegame '%s'", fs_basegame->string ); + } + if (FS_InvalidGameDir(fs_gamedirvar->string)) { + Com_Error( ERR_DROP, "Invalid fs_game '%s'", fs_gamedirvar->string ); + } // add search path elements in reverse priority order fs_gogpath = Cvar_Get ("fs_gogpath", Sys_GogPath(), CVAR_INIT|CVAR_PROTECTED ); @@ -3391,8 +3430,6 @@ static void FS_Startup( const char *gameName ) // print the current search paths FS_Path_f(); - fs_gamedirvar->modified = qfalse; // We just loaded, it's not modified - Com_Printf( "----------------------\n" ); #ifdef FS_MISSING @@ -4040,17 +4077,10 @@ Return qtrue if restarting due to game directory changed, qfalse otherwise */ qboolean FS_ConditionalRestart(int checksumFeed, qboolean disconnect) { - if(fs_gamedirvar->modified) + if (com_basegame->latchedString || fs_basegame->latchedString || fs_gamedirvar->latchedString) { - if(FS_FilenameCompare(lastValidGame, fs_gamedirvar->string) && - (*lastValidGame || FS_FilenameCompare(fs_gamedirvar->string, com_basegame->string)) && - (*fs_gamedirvar->string || FS_FilenameCompare(lastValidGame, com_basegame->string))) - { - Com_GameRestart(checksumFeed, disconnect); - return qtrue; - } - else - fs_gamedirvar->modified = qfalse; + Com_GameRestart(checksumFeed, disconnect); + return qtrue; } if(checksumFeed != fs_checksumFeed) diff --git a/code/qcommon/qcommon.h b/code/qcommon/qcommon.h index 2880f70de5..4471198ca8 100644 --- a/code/qcommon/qcommon.h +++ b/code/qcommon/qcommon.h @@ -726,6 +726,7 @@ void FS_PureServerSetLoadedPaks( const char *pakSums, const char *pakNames ); // sole exception of .cfg files. qboolean FS_CheckDirTraversal(const char *checkdir); +qboolean FS_InvalidGameDir(const char *gamedir); qboolean FS_idPak(char *pak, char *base, int numPaks); qboolean FS_ComparePaks( char *neededpaks, int len, qboolean dlstring ); From ed8d48cac3689205e95a462f438e3a8afe218041 Mon Sep 17 00:00:00 2001 From: Zack Middleton Date: Sun, 21 Jan 2018 19:49:16 -0600 Subject: [PATCH 224/240] Fix VMs forcing engine latch cvar to update to latched value A few commits ago I stopped VM's Cvar_Set() from instantly updating latched values. Now VM can't call Cvar_Register() afterword to force latched value to be used. Reported by Noah Metzger (Chomenor). --- code/qcommon/cvar.c | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/code/qcommon/cvar.c b/code/qcommon/cvar.c index 6f1ca432fb..f12eb7d70c 100644 --- a/code/qcommon/cvar.c +++ b/code/qcommon/cvar.c @@ -1376,7 +1376,13 @@ void Cvar_Register(vmCvar_t *vmCvar, const char *varName, const char *defaultVal if ( cv && ( cv->flags & CVAR_PROTECTED ) ) { Com_DPrintf( S_COLOR_YELLOW "WARNING: VM tried to register protected cvar '%s' with value '%s'%s\n", varName, defaultValue, ( flags & ~cv->flags ) != 0 ? " and new flags" : "" ); - } else { + } + // Don't set engine latch cvar to latched value. + else if ( cv && ( cv->flags & CVAR_LATCH ) && !( cv->flags & CVAR_VM_CREATED ) ) { + cv->flags |= flags; + cvar_modifiedFlags |= flags; + } + else { cv = Cvar_Get(varName, defaultValue, flags | CVAR_VM_CREATED); } From 738465d677bddac1385da7d6099b58ca0c4a797f Mon Sep 17 00:00:00 2001 From: Zack Middleton Date: Sun, 21 Jan 2018 22:09:42 -0600 Subject: [PATCH 225/240] Revert my recent cvar latch changes My cvar latch system changes prevent the Game VM from changing g_gametype when the value is out of range due to it being registed in the engine. It's been pointed out as fragile method of security, which was still exploitable, by Noah Metzger (Chomenor). It doesn't seem like this is working out to be a good solution. The issue of fs_game '..' on server being relicated on client via systeminfo exploit is still fixed as it's not affected by latch. There are a few cases from current values of fs_game are used which ideally should use fs_gamedir char array which has been validated. Revert "Don't let VMs change engine latch cvars immediately" Partially revert "Fix fs_game '..' reading outside of home and base path" Revert "Fix VMs forcing engine latch cvar to update to latched value" --- code/client/cl_main.c | 2 +- code/qcommon/common.c | 5 +---- code/qcommon/cvar.c | 37 ++++++++++--------------------------- code/qcommon/files.c | 19 ++++++++++++++----- 4 files changed, 26 insertions(+), 37 deletions(-) diff --git a/code/client/cl_main.c b/code/client/cl_main.c index 82d0edfba2..528df6a930 100644 --- a/code/client/cl_main.c +++ b/code/client/cl_main.c @@ -1361,7 +1361,7 @@ static void CL_OldGame(void) { // change back to previous fs_game cl_oldGameSet = qfalse; - Cvar_Set("fs_game", cl_oldGame); + Cvar_Set2("fs_game", cl_oldGame, qtrue); FS_ConditionalRestart(clc.checksumFeed, qfalse); } } diff --git a/code/qcommon/common.c b/code/qcommon/common.c index 6580073b38..11bcaf1921 100644 --- a/code/qcommon/common.c +++ b/code/qcommon/common.c @@ -2404,9 +2404,6 @@ void Com_GameRestart(int checksumFeed, qboolean disconnect) CL_Shutdown("Game directory changed", disconnect, qfalse); } - // change com_basegame to latched value - com_basegame = Cvar_Get("com_basegame", BASEGAME, CVAR_LATCH|CVAR_NORESTART); - FS_Restart(checksumFeed); // Clean out any user and VM created cvars @@ -2699,7 +2696,7 @@ void Com_Init( char *commandLine ) { CL_InitKeyCommands(); com_standalone = Cvar_Get("com_standalone", "0", CVAR_ROM); - com_basegame = Cvar_Get("com_basegame", BASEGAME, CVAR_LATCH|CVAR_NORESTART); + com_basegame = Cvar_Get("com_basegame", BASEGAME, CVAR_INIT); com_homepath = Cvar_Get("com_homepath", "", CVAR_INIT|CVAR_PROTECTED); FS_InitFilesystem (); diff --git a/code/qcommon/cvar.c b/code/qcommon/cvar.c index f12eb7d70c..5a0e6906ba 100644 --- a/code/qcommon/cvar.c +++ b/code/qcommon/cvar.c @@ -638,29 +638,18 @@ Cvar_SetSafe void Cvar_SetSafe( const char *var_name, const char *value ) { int flags = Cvar_Flags( var_name ); - qboolean force = qtrue; - if ( flags != CVAR_NONEXISTENT ) + if((flags != CVAR_NONEXISTENT) && (flags & CVAR_PROTECTED)) { - if ( flags & CVAR_PROTECTED ) - { - if( value ) - Com_Error( ERR_DROP, "Restricted source tried to set " - "\"%s\" to \"%s\"", var_name, value ); - else - Com_Error( ERR_DROP, "Restricted source tried to " - "modify \"%s\"", var_name ); - return; - } - - // don't let VMs or server change engine latched cvars instantly - if ( ( flags & CVAR_LATCH ) && !( flags & CVAR_VM_CREATED ) ) - { - force = qfalse; - } + if( value ) + Com_Error( ERR_DROP, "Restricted source tried to set " + "\"%s\" to \"%s\"", var_name, value ); + else + Com_Error( ERR_DROP, "Restricted source tried to " + "modify \"%s\"", var_name ); + return; } - - Cvar_Set2 (var_name, value, force); + Cvar_Set( var_name, value ); } /* @@ -1376,13 +1365,7 @@ void Cvar_Register(vmCvar_t *vmCvar, const char *varName, const char *defaultVal if ( cv && ( cv->flags & CVAR_PROTECTED ) ) { Com_DPrintf( S_COLOR_YELLOW "WARNING: VM tried to register protected cvar '%s' with value '%s'%s\n", varName, defaultValue, ( flags & ~cv->flags ) != 0 ? " and new flags" : "" ); - } - // Don't set engine latch cvar to latched value. - else if ( cv && ( cv->flags & CVAR_LATCH ) && !( cv->flags & CVAR_VM_CREATED ) ) { - cv->flags |= flags; - cvar_modifiedFlags |= flags; - } - else { + } else { cv = Cvar_Get(varName, defaultValue, flags | CVAR_VM_CREATED); } diff --git a/code/qcommon/files.c b/code/qcommon/files.c index eb61af9c14..71613cd80a 100644 --- a/code/qcommon/files.c +++ b/code/qcommon/files.c @@ -3319,13 +3319,13 @@ static void FS_Startup( const char *gameName ) fs_debug = Cvar_Get( "fs_debug", "0", 0 ); fs_basepath = Cvar_Get ("fs_basepath", Sys_DefaultInstallPath(), CVAR_INIT|CVAR_PROTECTED ); - fs_basegame = Cvar_Get ("fs_basegame", "", CVAR_LATCH|CVAR_NORESTART ); + fs_basegame = Cvar_Get ("fs_basegame", "", CVAR_INIT ); homePath = Sys_DefaultHomePath(); if (!homePath || !homePath[0]) { homePath = fs_basepath->string; } fs_homepath = Cvar_Get ("fs_homepath", homePath, CVAR_INIT|CVAR_PROTECTED ); - fs_gamedirvar = Cvar_Get ("fs_game", "", CVAR_LATCH|CVAR_NORESTART|CVAR_SYSTEMINFO ); + fs_gamedirvar = Cvar_Get ("fs_game", "", CVAR_INIT|CVAR_SYSTEMINFO ); if (!gameName[0]) { Cvar_ForceReset( "com_basegame" ); @@ -3430,6 +3430,8 @@ static void FS_Startup( const char *gameName ) // print the current search paths FS_Path_f(); + fs_gamedirvar->modified = qfalse; // We just loaded, it's not modified + Com_Printf( "----------------------\n" ); #ifdef FS_MISSING @@ -4077,10 +4079,17 @@ Return qtrue if restarting due to game directory changed, qfalse otherwise */ qboolean FS_ConditionalRestart(int checksumFeed, qboolean disconnect) { - if (com_basegame->latchedString || fs_basegame->latchedString || fs_gamedirvar->latchedString) + if(fs_gamedirvar->modified) { - Com_GameRestart(checksumFeed, disconnect); - return qtrue; + if(FS_FilenameCompare(lastValidGame, fs_gamedirvar->string) && + (*lastValidGame || FS_FilenameCompare(fs_gamedirvar->string, com_basegame->string)) && + (*fs_gamedirvar->string || FS_FilenameCompare(lastValidGame, com_basegame->string))) + { + Com_GameRestart(checksumFeed, disconnect); + return qtrue; + } + else + fs_gamedirvar->modified = qfalse; } if(checksumFeed != fs_checksumFeed) From acce0e5452752f5346d3f6eacf10658bf4b21f0d Mon Sep 17 00:00:00 2001 From: Zack Middleton Date: Tue, 30 Jan 2018 05:04:01 -0600 Subject: [PATCH 226/240] Fix building msvc12 project * Don't try to embed manifest (it causes build to fail because it's specified in win_resource.rc). * Fix curl include path. * Compile sys_autoupdater.c. * Make quake3 debug link to msvcrtd.lib instead of msvcrt.lib to fix unresolved external symbol __imp___CrtDbgReportW. * Fix q3_ui dll output directory. * Fix opus path filter. * Use UseOfMfc false and CharacterSet NotSet. * Fix header paths and remove non-existent files. --- misc/msvc12/cgame.vcxproj | 4 + misc/msvc12/game.vcxproj | 4 + misc/msvc12/opengl1.vcxproj | 4 + misc/msvc12/opengl2.vcxproj | 4 + misc/msvc12/q3_ui.vcxproj | 28 +-- misc/msvc12/quake3.vcxproj | 35 ++-- misc/msvc12/quake3.vcxproj.filters | 283 ++++++++++++++--------------- misc/msvc12/ui.vcxproj | 8 +- 8 files changed, 196 insertions(+), 174 deletions(-) diff --git a/misc/msvc12/cgame.vcxproj b/misc/msvc12/cgame.vcxproj index 6aa1afce1d..f740c5fafc 100644 --- a/misc/msvc12/cgame.vcxproj +++ b/misc/msvc12/cgame.vcxproj @@ -27,21 +27,25 @@ DynamicLibrary v120 false + NotSet DynamicLibrary v120 false + NotSet DynamicLibrary v120 false + NotSet DynamicLibrary v120 false + NotSet diff --git a/misc/msvc12/game.vcxproj b/misc/msvc12/game.vcxproj index abd039691f..8aa2a51279 100644 --- a/misc/msvc12/game.vcxproj +++ b/misc/msvc12/game.vcxproj @@ -26,21 +26,25 @@ DynamicLibrary v120 false + NotSet DynamicLibrary v120 false + NotSet DynamicLibrary v120 false + NotSet DynamicLibrary v120 false + NotSet diff --git a/misc/msvc12/opengl1.vcxproj b/misc/msvc12/opengl1.vcxproj index 5427d37682..2bc878e9ad 100644 --- a/misc/msvc12/opengl1.vcxproj +++ b/misc/msvc12/opengl1.vcxproj @@ -26,21 +26,25 @@ DynamicLibrary v120 false + NotSet DynamicLibrary v120 false + NotSet DynamicLibrary v120 false + NotSet DynamicLibrary v120 false + NotSet diff --git a/misc/msvc12/opengl2.vcxproj b/misc/msvc12/opengl2.vcxproj index 2f91590ee6..5a837890bc 100644 --- a/misc/msvc12/opengl2.vcxproj +++ b/misc/msvc12/opengl2.vcxproj @@ -27,21 +27,25 @@ DynamicLibrary v120 false + NotSet DynamicLibrary v120 false + NotSet DynamicLibrary v120 false + NotSet DynamicLibrary v120 false + NotSet diff --git a/misc/msvc12/q3_ui.vcxproj b/misc/msvc12/q3_ui.vcxproj index 9a345618c8..e59a237b53 100644 --- a/misc/msvc12/q3_ui.vcxproj +++ b/misc/msvc12/q3_ui.vcxproj @@ -26,25 +26,25 @@ DynamicLibrary v120 false - MultiByte + NotSet DynamicLibrary v120 false - MultiByte + NotSet DynamicLibrary v120 false - MultiByte + NotSet DynamicLibrary v120 false - MultiByte + NotSet @@ -66,24 +66,28 @@ <_ProjectFileVersion>12.0.30501.0 - ..\..\build\q3_ui_debug_ta\ - ..\..\build\q3_ui_debug_ta\ + ..\..\build\debug_ta-msvc12-x86\missionpack\ + ..\..\build\debug_ta-msvc12-x86\missionpack\q3_ui\ true + uix86_old - ..\..\build\q3_ui_release_ta\ - ..\..\build\q3_ui_release_ta\ + ..\..\build\release_ta-msvc12-x86\missionpack\ + ..\..\build\release_ta-msvc12-x86\missionpack\q3_ui\ false + uix86_old - ..\..\build\q3_ui_release\ - ..\..\build\q3_ui_release\ + ..\..\build\release-msvc12-x86\baseq3\ + ..\..\build\release-msvc12-x86\baseq3\q3_ui\ false + uix86 - ..\..\build\q3_ui_debug\ - ..\..\build\q3_ui_debug\ + ..\..\build\debug-msvc12-x86\baseq3\ + ..\..\build\debug-msvc12-x86\baseq3\q3_ui\ true + uix86 diff --git a/misc/msvc12/quake3.vcxproj b/misc/msvc12/quake3.vcxproj index 7a35e8e838..992fc5eab9 100644 --- a/misc/msvc12/quake3.vcxproj +++ b/misc/msvc12/quake3.vcxproj @@ -26,21 +26,25 @@ Application v120 false + NotSet Application v120 - Static + false + NotSet Application v120 false + NotSet Application v120 false + NotSet @@ -66,24 +70,28 @@ ..\..\build\release_ta-msvc12-x86\client\ false ioquake3.x86 + false ..\..\build\debug-msvc12-x86\ ..\..\build\debug-msvc12-x86\client\ true ioquake3.x86 + false ..\..\build\release-msvc12-x86\ ..\..\build\release-msvc12-x86\client\ false ioquake3.x86 + false ..\..\build\debug_ta-msvc12-x86\ ..\..\build\debug_ta-msvc12-x86\client\ true ioquake3.x86 + false @@ -97,7 +105,7 @@ MaxSpeed AnySuitable - ..\..\code\SDL2\include;..\..\code\libcurl-7.35.0;..\..\code\AL;..\..\code\opus-1.1.4\include;..\..\code\opus-1.1.4\celt;..\..\code\opus-1.1.4\silk;..\..\code\opus-1.1.4\silk\float;..\..\code\zlib;..\..\code\jpeg-8c;%(AdditionalIncludeDirectories) + ..\..\code\SDL2\include;..\..\code\curl-7.54.0\include;..\..\code\AL;..\..\code\opus-1.1.4\include;..\..\code\opus-1.1.4\celt;..\..\code\opus-1.1.4\silk;..\..\code\opus-1.1.4\silk\float;..\..\code\zlib;..\..\code\jpeg-8c;%(AdditionalIncludeDirectories) _WIN32;WIN32;NDEBUG;_WINDOWS;_CRT_SECURE_NO_DEPRECATE;BOTLIB;USE_ICON;USE_CURL;USE_CURL_DLOPEN;USE_OPENAL;USE_OPENAL_DLOPEN;USE_VOIP;OPUS_BUILD;HAVE_LRINTF;FLOATING_POINT;FLOAT_APPROX;USE_ALLOCA;MISSIONPACK;USE_RENDERER_DLOPEN;%(PreprocessorDefinitions) true MultiThreaded @@ -149,7 +157,7 @@ Disabled - ..\..\code\SDL2\include;..\..\code\libcurl-7.35.0;..\..\code\AL;..\..\code\opus-1.1.4\include;..\..\code\opus-1.1.4\celt;..\..\code\opus-1.1.4\silk;..\..\code\opus-1.1.4\silk\float;..\..\code\zlib;..\..\code\jpeg-8c;%(AdditionalIncludeDirectories) + ..\..\code\SDL2\include;..\..\code\curl-7.54.0\include;..\..\code\AL;..\..\code\opus-1.1.4\include;..\..\code\opus-1.1.4\celt;..\..\code\opus-1.1.4\silk;..\..\code\opus-1.1.4\silk\float;..\..\code\zlib;..\..\code\jpeg-8c;%(AdditionalIncludeDirectories) _WIN32;WIN32;_DEBUG;_WINDOWS;_CRT_SECURE_NO_DEPRECATE;BOTLIB;USE_ICON;USE_CURL;USE_CURL_DLOPEN;USE_OPENAL;USE_OPENAL_DLOPEN;USE_VOIP;OPUS_BUILD;HAVE_LRINTF;FLOATING_POINT;FLOAT_APPROX;USE_ALLOCA;USE_INTERNAL_JPEG;USE_RENDERER_DLOPEN;%(PreprocessorDefinitions) MultiThreadedDebugDLL @@ -171,7 +179,7 @@ ..\winquake.res - user32.lib;advapi32.lib;winmm.lib;wsock32.lib;ws2_32.lib;SDL2main.lib;SDL2.lib;OpenGL32.lib;msvcrt.lib;psapi.lib;gdi32.lib;%(AdditionalDependencies) + user32.lib;advapi32.lib;winmm.lib;wsock32.lib;ws2_32.lib;SDL2main.lib;SDL2.lib;OpenGL32.lib;msvcrtd.lib;psapi.lib;gdi32.lib;%(AdditionalDependencies) $(OutDir)ioquake3.x86.exe true ..\..\code\libs\win32;%(AdditionalLibraryDirectories) @@ -202,7 +210,7 @@ MaxSpeed AnySuitable - ..\..\code\SDL2\include;..\..\code\libcurl-7.35.0;..\..\code\AL;..\..\code\opus-1.1.4\include;..\..\code\opus-1.1.4\celt;..\..\code\opus-1.1.4\silk;..\..\code\opus-1.1.4\silk\float;..\..\code\zlib;..\..\code\jpeg-8c;%(AdditionalIncludeDirectories) + ..\..\code\SDL2\include;..\..\code\curl-7.54.0\include;..\..\code\AL;..\..\code\opus-1.1.4\include;..\..\code\opus-1.1.4\celt;..\..\code\opus-1.1.4\silk;..\..\code\opus-1.1.4\silk\float;..\..\code\zlib;..\..\code\jpeg-8c;%(AdditionalIncludeDirectories) _WIN32;WIN32;NDEBUG;_WINDOWS;_CRT_SECURE_NO_DEPRECATE;BOTLIB;USE_ICON;USE_CURL;USE_CURL_DLOPEN;USE_OPENAL;USE_OPENAL_DLOPEN;USE_VOIP;OPUS_BUILD;HAVE_LRINTF;FLOATING_POINT;FLOAT_APPROX;USE_ALLOCA;USE_INTERNAL_JPEG;USE_RENDERER_DLOPEN;%(PreprocessorDefinitions) true MultiThreaded @@ -248,7 +256,7 @@ Disabled - ..\..\code\SDL2\include;..\..\code\libcurl-7.35.0;..\..\code\AL;..\..\code\opus-1.1.4\include;..\..\code\opus-1.1.4\celt;..\..\code\opus-1.1.4\silk;..\..\code\opus-1.1.4\silk\float;..\..\code\zlib;..\..\code\jpeg-8c;%(AdditionalIncludeDirectories) + ..\..\code\SDL2\include;..\..\code\curl-7.54.0\include;..\..\code\AL;..\..\code\opus-1.1.4\include;..\..\code\opus-1.1.4\celt;..\..\code\opus-1.1.4\silk;..\..\code\opus-1.1.4\silk\float;..\..\code\zlib;..\..\code\jpeg-8c;%(AdditionalIncludeDirectories) _WIN32;WIN32;_DEBUG;_WINDOWS;_CRT_SECURE_NO_DEPRECATE;BOTLIB;USE_ICON;USE_CURL;USE_CURL_DLOPEN;USE_OPENAL;USE_OPENAL_DLOPEN;USE_VOIP;OPUS_BUILD;HAVE_LRINTF;FLOATING_POINT;FLOAT_APPROX;USE_ALLOCA;MISSIONPACK;USE_RENDERER_DLOPEN;%(PreprocessorDefinitions) MultiThreadedDebugDLL @@ -270,7 +278,7 @@ ..\winquake.res - user32.lib;advapi32.lib;winmm.lib;wsock32.lib;ws2_32.lib;SDL2main.lib;SDL2.lib;OpenGL32.lib;msvcrt.lib;psapi.lib;gdi32.lib;%(AdditionalDependencies) + user32.lib;advapi32.lib;winmm.lib;wsock32.lib;ws2_32.lib;SDL2main.lib;SDL2.lib;OpenGL32.lib;msvcrtd.lib;psapi.lib;gdi32.lib;%(AdditionalDependencies) $(OutDir)ioquake3.x86.exe true ..\..\code\libs\win32;%(AdditionalLibraryDirectories) @@ -856,6 +864,7 @@ + @@ -930,16 +939,15 @@ - + - @@ -951,15 +959,12 @@ - - - + + + - - - diff --git a/misc/msvc12/quake3.vcxproj.filters b/misc/msvc12/quake3.vcxproj.filters index 0cb1d4b59d..732955610a 100644 --- a/misc/msvc12/quake3.vcxproj.filters +++ b/misc/msvc12/quake3.vcxproj.filters @@ -60,411 +60,412 @@ + - + opus - + opus - + opus - + opus - + opus - + opus - + opus - + opus - + opus - + opus - + opus\celt - + opus\celt - + opus\celt - + opus\celt - + opus\celt - + opus\celt - + opus\celt - + opus\celt - + opus\celt - + opus\celt - + opus\celt - + opus\celt - + opus\celt - + opus\celt - + opus\celt - + opus\celt - + opus\celt - + opus\celt - + opus\silk - + opus\silk - + opus\silk - + opus\silk - + opus\silk - + opus\silk - + opus\silk - + opus\silk - + opus\silk - + opus\silk - + opus\silk - + opus\silk - + opus\silk - + opus\silk - + opus\silk - + opus\silk - + opus\silk - + opus\silk - + opus\silk - + opus\silk - + opus\silk - + opus\silk - + opus\silk - + opus\silk - + opus\silk - + opus\silk - + opus\silk - + opus\silk - + opus\silk - + opus\silk - + opus\silk - + opus\silk - + opus\silk - + opus\silk - + opus\silk - + opus\silk - + opus\silk - + opus\silk - + opus\silk - + opus\silk - + opus\silk - + opus\silk - + opus\silk - + opus\silk - + opus\silk - + opus\silk - + opus\silk - + opus\silk - + opus\silk - + opus\silk - + opus\silk - + opus\silk - + opus\silk - + opus\silk - + opus\silk - + opus\silk - + opus\silk - + opus\silk - + opus\silk - + opus\silk - + opus\silk - + opus\silk - + opus\silk - + opus\silk - + opus\silk - + opus\silk - + opus\silk - + opus\silk - + opus\silk - + opus\silk - + opus\silk - + opus\silk - + opus\silk - + opus\silk - + opus\silk - + opus\silk - + opus\silk\float - + opus\silk\float - + opus\silk\float - + opus\silk\float - + opus\silk\float - + opus\silk\float - + opus\silk\float - + opus\silk\float - + opus\silk\float - + opus\silk\float - + opus\silk\float - + opus\silk\float - + opus\silk\float - + opus\silk\float - + opus\silk\float - + opus\silk\float - + opus\silk\float - + opus\silk\float - + opus\silk\float - + opus\silk\float - + opus\silk\float - + opus\silk\float - + opus\silk\float - + opus\silk\float - + opus\silk\float - + opus\silk\float - + opus\silk\float - + opus\silk\float - + opus\silk\float - + opus\silk\float - + opus\silk\float @@ -572,16 +573,15 @@ - + - @@ -593,15 +593,12 @@ - - - + + + - - - botlib diff --git a/misc/msvc12/ui.vcxproj b/misc/msvc12/ui.vcxproj index a5584ba4b2..78205ec651 100644 --- a/misc/msvc12/ui.vcxproj +++ b/misc/msvc12/ui.vcxproj @@ -26,25 +26,25 @@ DynamicLibrary v120 false - MultiByte + NotSet DynamicLibrary v120 false - MultiByte + NotSet DynamicLibrary v120 false - MultiByte + NotSet DynamicLibrary v120 false - MultiByte + NotSet From df8f657f09b43c8e097c0f61e170c4b6599ffd74 Mon Sep 17 00:00:00 2001 From: Zack Middleton Date: Tue, 30 Jan 2018 07:43:36 -0600 Subject: [PATCH 227/240] Fix exploit to bypass filename restrictions on Windows Windows API ignores all trailing spaces and periods which can get around Quake 3 file system restrictions. QVM opening 'uix86.dll.' actually opens 'uix86.dll' which allows QVM to write native code. This is done in the low-level Sys_FOpen() instead of the function directly used by VMs ( FS_FOpenFileByMode() ) in case there are engine commands now or in the future that can read or write arbitrary files. Reported by Noah Metzger (Chomenor). --- code/sys/sys_win32.c | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/code/sys/sys_win32.c b/code/sys/sys_win32.c index 6979dce9af..ee026bd849 100644 --- a/code/sys/sys_win32.c +++ b/code/sys/sys_win32.c @@ -357,6 +357,14 @@ Sys_FOpen ============== */ FILE *Sys_FOpen( const char *ospath, const char *mode ) { + size_t length; + + // Windows API ignores all trailing spaces and periods which can get around Quake 3 file system restrictions. + length = strlen( ospath ); + if ( length == 0 || ospath[length-1] == ' ' || ospath[length-1] == '.' ) { + return NULL; + } + return fopen( ospath, mode ); } From ad10e6610c2f1064ac781936050f1d06ad8c67d1 Mon Sep 17 00:00:00 2001 From: Zack Middleton Date: Sun, 4 Feb 2018 07:31:24 -0600 Subject: [PATCH 228/240] Changes to systemd q3a.service Use an absolute path as required by systemd. Distro packages will need to change it. (Reported by @andreyv and @zeroxfourc.) Link to server documentation on the wiki instead of maintaining a subset in the service file. --- misc/linux/q3a.service | 14 ++++---------- 1 file changed, 4 insertions(+), 10 deletions(-) diff --git a/misc/linux/q3a.service b/misc/linux/q3a.service index e039eec89b..83287ea9bf 100644 --- a/misc/linux/q3a.service +++ b/misc/linux/q3a.service @@ -1,18 +1,12 @@ [Unit] -Description=This service spawns a ioquake3 dedicated server with sane defaults -# However, these defaults may not apply to all use cases. -# See comments below for further details +Description=This service spawns an ioquake3 Internet server +# A map will need to be loaded for players to be able to join. +# For additional settings see http://wiki.ioquake3.org/Sys_Admin_Guide [Service] User=q3a -ExecStart=$(which ioq3ded.x86_64) +set dedicated 2 +ExecStart=/usr/local/games/quake3/ioq3ded.x86_64 +set dedicated 2 Restart=on-abort -# there are several options to tweak the server's performance: -# net_port defines the UDP-port used for connections to the server -# fs_game should be the mod you want to play. Not necessary for vanilla-q3/FFA -# dedicated 0 is non-dedicted an not available with ioq3ded, 1 is LAN, 2 is Internet -# sv_pure 1 prevents clients from using their own pk3-files -# com_hunkmegs defaults to 56, should be 128/192/256 on a modern system(?) [Install] WantedBy=multi-user.target From 83119a990a160e9c1e19dc06e04ae8f947a57904 Mon Sep 17 00:00:00 2001 From: Zack Middleton Date: Sun, 4 Feb 2018 08:03:51 -0600 Subject: [PATCH 229/240] Fix Q_vsnprintf for mingw-w64 By default mingw-w64 uses Microsoft's broken _vsnprintf() in msvcrt.dll. It can be overriden by defining __USE_MINGW_ANSI_STDIO but let's just use the same behavior for both MSVC and mingw-w64. Reported by @birdstakes. --- code/qcommon/q_shared.c | 7 ++++--- code/qcommon/q_shared.h | 6 ++++-- 2 files changed, 8 insertions(+), 5 deletions(-) diff --git a/code/qcommon/q_shared.c b/code/qcommon/q_shared.c index 820c862c76..aa17f1c1d9 100644 --- a/code/qcommon/q_shared.c +++ b/code/qcommon/q_shared.c @@ -746,13 +746,14 @@ qboolean Q_isintegral( float f ) return (int)f == f; } -#ifdef _MSC_VER +#ifdef _WIN32 /* ============= Q_vsnprintf - + Special wrapper function for Microsoft's broken _vsnprintf() function. -MinGW comes with its own snprintf() which is not broken. +MinGW comes with its own vsnprintf() which is not broken. mingw-w64 +however, uses Microsoft's broken _vsnprintf() function. ============= */ diff --git a/code/qcommon/q_shared.h b/code/qcommon/q_shared.h index 4c1b821a43..629334af84 100644 --- a/code/qcommon/q_shared.h +++ b/code/qcommon/q_shared.h @@ -177,13 +177,15 @@ typedef int intptr_t; typedef unsigned __int32 uint32_t; typedef unsigned __int16 uint16_t; typedef unsigned __int8 uint8_t; +#else + #include +#endif +#ifdef _WIN32 // vsnprintf is ISO/IEC 9899:1999 // abstracting this to make it portable int Q_vsnprintf(char *str, size_t size, const char *format, va_list ap); #else - #include - #define Q_vsnprintf vsnprintf #endif From 0822772ea2868bb7e56855c007f3bed2ab681043 Mon Sep 17 00:00:00 2001 From: Zack Middleton Date: Sun, 4 Feb 2018 09:00:08 -0600 Subject: [PATCH 230/240] Fix timelimit causing an infinite map ending loop A negative timelimit value or a value that would overflow the multiplication by 60000 caused an endless map change/reload. Based on patch and description by @vloup. --- code/game/g_main.c | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/code/game/g_main.c b/code/game/g_main.c index 0267de46d6..63e93e0770 100644 --- a/code/game/g_main.c +++ b/code/game/g_main.c @@ -1366,6 +1366,12 @@ void CheckExitRules( void ) { return; } + if ( g_timelimit.integer < 0 || g_timelimit.integer > INT_MAX / 60000 ) { + G_Printf( "timelimit %i is out of range, defaulting to 0\n", g_timelimit.integer ); + trap_Cvar_Set( "timelimit", "0" ); + trap_Cvar_Update( &g_timelimit ); + } + if ( g_timelimit.integer && !level.warmupTime ) { if ( level.time - level.startTime >= g_timelimit.integer*60000 ) { trap_SendServerCommand( -1, "print \"Timelimit hit.\n\""); From fc16ac6bd2d05cba8a2d057994b097e95bae28a0 Mon Sep 17 00:00:00 2001 From: Zack Middleton Date: Sun, 4 Feb 2018 09:07:44 -0600 Subject: [PATCH 231/240] Fix invalid access to cluster 0 in AAS_AreaRouteToGoalArea() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Newer versions of BSPC such as 2.1h included with the Quake 3 GPL source code create AAS files containing areas in cluster 0 if the area has no reachabilities. The AAS files included with Quake 3 and Team Arena do not contain areas in cluster 0. It's apparent that BSPC would not create them. Instead it created clusters with no reachability areas. It seems the intention was to check if the areanum and goalareanum have reachable areas using AAS_AreaReachability(areanum) everywhere before calling AAS_AreaRouteToGoalArea(). This prevents adding cluster 0 to the routing cache and portal cache. However, it is not checked everywhere and including some places in the Game VM. Fix AAS_AreaRouteToGoalArea() instead of trying to wack-a-mole with all the places that call it. Cluster 0 access reported by Thomas Köppe (github @tkoeppe) as causing crashes in rare cases. --- code/botlib/be_aas_route.c | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/code/botlib/be_aas_route.c b/code/botlib/be_aas_route.c index bad375d4fc..7bdc9f22ee 100644 --- a/code/botlib/be_aas_route.c +++ b/code/botlib/be_aas_route.c @@ -1603,7 +1603,7 @@ int AAS_AreaRouteToGoalArea(int areanum, vec3_t origin, int goalareanum, int tra *reachnum = 0; return qtrue; } - // + //check !AAS_AreaReachability(areanum) with custom developer-only debug message if (areanum <= 0 || areanum >= aasworld.numareas) { if (botDeveloper) @@ -1620,6 +1620,10 @@ int AAS_AreaRouteToGoalArea(int areanum, vec3_t origin, int goalareanum, int tra } //end if return qfalse; } //end if + if (!aasworld.areasettings[areanum].numreachableareas || !aasworld.areasettings[goalareanum].numreachableareas) + { + return qfalse; + } //end if // make sure the routing cache doesn't grow to large while(AvailableMemory() < 1 * 1024 * 1024) { if (!AAS_FreeOldestCache()) break; From 0b6d97f84937750891a2d22c72af30de79d686fe Mon Sep 17 00:00:00 2001 From: Zack Middleton Date: Sun, 4 Feb 2018 10:37:20 -0600 Subject: [PATCH 232/240] Fix negative frag/capturelimit causing an infinite map end loop Reported by @vloup. --- code/game/g_main.c | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/code/game/g_main.c b/code/game/g_main.c index 63e93e0770..4398113e4b 100644 --- a/code/game/g_main.c +++ b/code/game/g_main.c @@ -1380,6 +1380,12 @@ void CheckExitRules( void ) { } } + if ( g_fraglimit.integer < 0 ) { + G_Printf( "fraglimit %i is out of range, defaulting to 0\n", g_fraglimit.integer ); + trap_Cvar_Set( "fraglimit", "0" ); + trap_Cvar_Update( &g_fraglimit ); + } + if ( g_gametype.integer < GT_CTF && g_fraglimit.integer ) { if ( level.teamScores[TEAM_RED] >= g_fraglimit.integer ) { trap_SendServerCommand( -1, "print \"Red hit the fraglimit.\n\"" ); @@ -1411,6 +1417,12 @@ void CheckExitRules( void ) { } } + if ( g_capturelimit.integer < 0 ) { + G_Printf( "capturelimit %i is out of range, defaulting to 0\n", g_capturelimit.integer ); + trap_Cvar_Set( "capturelimit", "0" ); + trap_Cvar_Update( &g_capturelimit ); + } + if ( g_gametype.integer >= GT_CTF && g_capturelimit.integer ) { if ( level.teamScores[TEAM_RED] >= g_capturelimit.integer ) { From 3d6aa05694cdfbfd630edb95f1096ed107b6a5d4 Mon Sep 17 00:00:00 2001 From: Zack Middleton Date: Sun, 4 Feb 2018 11:15:13 -0600 Subject: [PATCH 233/240] OpenGL2: Fix dark lightmap on shader in mpteam6 Team Arena's mpteam6 map has a shader textures/base_wall2/space_concrete that contains an opaque stage, two non-lightmap blendfunc filter stages, a blendfunc add stage, and a lightmap stage. The lightmap was attached to all four of the non-lightmap stages causing the filter stages to darken the lightmap multiple times. Change setting up the lightall GLSL shader to only use lightmap if it's the first stage or not a blendfunc filter stage. Now only the opaque and blendfunc add stages of the mpteam6 shader use the lightmap. Reported by Alexander Nadeau (wareya). --- code/renderergl2/tr_shader.c | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/code/renderergl2/tr_shader.c b/code/renderergl2/tr_shader.c index b4f47004bd..b1c88f9b25 100644 --- a/code/renderergl2/tr_shader.c +++ b/code/renderergl2/tr_shader.c @@ -2421,6 +2421,8 @@ static int CollapseStagesToGLSL(void) if (!skip) { + qboolean usedLightmap = qfalse; + for (i = 0; i < MAX_SHADER_STAGES; i++) { shaderStage_t *pStage = &stages[i]; @@ -2479,7 +2481,16 @@ static int CollapseStagesToGLSL(void) case ST_COLORMAP: if (pStage2->bundle[0].tcGen == TCGEN_LIGHTMAP) { - lightmap = pStage2; + int blendBits = pStage->stateBits & ( GLS_DSTBLEND_BITS | GLS_SRCBLEND_BITS ); + + // Only add lightmap to blendfunc filter stage if it's the first time lightmap is used + // otherwise it will cause the shader to be darkened by the lightmap multiple times. + if (!usedLightmap || (blendBits != (GLS_DSTBLEND_SRC_COLOR | GLS_SRCBLEND_ZERO) + && blendBits != (GLS_DSTBLEND_ZERO | GLS_SRCBLEND_DST_COLOR))) + { + lightmap = pStage2; + usedLightmap = qtrue; + } } break; From 71a9a5efa6c2c9f2913cd6664519779dc2ac5414 Mon Sep 17 00:00:00 2001 From: Zack Middleton Date: Sun, 4 Feb 2018 20:34:05 -0600 Subject: [PATCH 234/240] Make FS_InvalidGameDir() consider subdirectories invalid fs_game shouldn't be a subdirectory and it simplifies the code. --- code/qcommon/files.c | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/code/qcommon/files.c b/code/qcommon/files.c index 71613cd80a..16869afc32 100644 --- a/code/qcommon/files.c +++ b/code/qcommon/files.c @@ -3072,13 +3072,12 @@ qboolean FS_CheckDirTraversal(const char *checkdir) FS_InvalidGameDir return true if path is a reference to current directory or directory traversal +or a sub-directory ================ */ qboolean FS_InvalidGameDir( const char *gamedir ) { if ( !strcmp( gamedir, "." ) || !strcmp( gamedir, ".." ) - || !strcmp( gamedir, "/" ) || !strcmp( gamedir, "\\" ) - || strstr( gamedir, "/.." ) || strstr( gamedir, "\\.." ) - || FS_CheckDirTraversal( gamedir ) ) { + || strchr( gamedir, '/' ) || strchr( gamedir, '\\' ) ) { return qtrue; } From d047210acaba2ea726169883094e525de33812e9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Thomas=20K=C3=B6ppe?= Date: Mon, 5 Feb 2018 12:49:22 +0000 Subject: [PATCH 235/240] [qcommon] Remove dead serialization code --- code/qcommon/msg.c | 45 --------------------------------------------- 1 file changed, 45 deletions(-) diff --git a/code/qcommon/msg.c b/code/qcommon/msg.c index eb767be4e3..c8344a1a63 100644 --- a/code/qcommon/msg.c +++ b/code/qcommon/msg.c @@ -556,55 +556,10 @@ int MSG_HashKey(const char *string, int maxlen) { return hash; } -/* -============================================================================= - -delta functions - -============================================================================= -*/ - extern cvar_t *cl_shownet; #define LOG(x) if( cl_shownet && cl_shownet->integer == 4 ) { Com_Printf("%s ", x ); }; -void MSG_WriteDelta( msg_t *msg, int oldV, int newV, int bits ) { - if ( oldV == newV ) { - MSG_WriteBits( msg, 0, 1 ); - return; - } - MSG_WriteBits( msg, 1, 1 ); - MSG_WriteBits( msg, newV, bits ); -} - -int MSG_ReadDelta( msg_t *msg, int oldV, int bits ) { - if ( MSG_ReadBits( msg, 1 ) ) { - return MSG_ReadBits( msg, bits ); - } - return oldV; -} - -void MSG_WriteDeltaFloat( msg_t *msg, float oldV, float newV ) { - floatint_t fi; - if ( oldV == newV ) { - MSG_WriteBits( msg, 0, 1 ); - return; - } - fi.f = newV; - MSG_WriteBits( msg, 1, 1 ); - MSG_WriteBits( msg, fi.i, 32 ); -} - -float MSG_ReadDeltaFloat( msg_t *msg, float oldV ) { - if ( MSG_ReadBits( msg, 1 ) ) { - floatint_t fi; - - fi.i = MSG_ReadBits( msg, 32 ); - return fi.f; - } - return oldV; -} - /* ============================================================================= From 3f29b8558d7e4c76539569dee7a0b0f222382a12 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Thomas=20K=C3=B6ppe?= Date: Wed, 7 Feb 2018 15:13:27 +0000 Subject: [PATCH 236/240] [qcommon] Make several zone variables and functions static. --- code/qcommon/common.c | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/code/qcommon/common.c b/code/qcommon/common.c index 11bcaf1921..8512cc0d93 100644 --- a/code/qcommon/common.c +++ b/code/qcommon/common.c @@ -794,19 +794,19 @@ typedef struct { } memzone_t; // main zone for all "dynamic" memory allocation -memzone_t *mainzone; +static memzone_t *mainzone; // we also have a small zone for small allocations that would only // fragment the main zone (think of cvar and cmd strings) -memzone_t *smallzone; +static memzone_t *smallzone; -void Z_CheckHeap( void ); +static void Z_CheckHeap( void ); /* ======================== Z_ClearZone ======================== */ -void Z_ClearZone( memzone_t *zone, int size ) { +static void Z_ClearZone( memzone_t *zone, int size ) { memblock_t *block; // set the entire zone to one free block @@ -831,7 +831,7 @@ void Z_ClearZone( memzone_t *zone, int size ) { Z_AvailableZoneMemory ======================== */ -int Z_AvailableZoneMemory( memzone_t *zone ) { +static int Z_AvailableZoneMemory( memzone_t *zone ) { return zone->size - zone->used; } @@ -1076,7 +1076,7 @@ void *S_Malloc( int size ) { Z_CheckHeap ======================== */ -void Z_CheckHeap( void ) { +static void Z_CheckHeap( void ) { memblock_t *block; for (block = mainzone->blocklist.next ; ; block = block->next) { From d4e7d987a39befc9793f83a15f7411e8aef8bc6f Mon Sep 17 00:00:00 2001 From: Zack Middleton Date: Thu, 8 Feb 2018 10:20:39 -0600 Subject: [PATCH 237/240] Fix MAC_OS_X_VERSION_MIN_REQUIRED for macOS 10.10 and later Manually specifying MACOSX_VERSION_MIN=10.10 or later would use the wrong value for MAC_OS_X_VERSION_MIN_REQUIRED define. 1100 instead of 101000. --- Makefile | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/Makefile b/Makefile index 4aa9f51973..ee8cbf926c 100644 --- a/Makefile +++ b/Makefile @@ -426,8 +426,15 @@ ifeq ($(PLATFORM),darwin) MACOSX_VERSION_MIN=10.7 endif - # Multiply by 100 and then remove decimal. 10.7 -> 1070.0 -> 1070 - MAC_OS_X_VERSION_MIN_REQUIRED=$(shell echo '$(MACOSX_VERSION_MIN) * 100' | bc | cut -d. -f1) + MACOSX_MAJOR=$(shell echo $(MACOSX_VERSION_MIN) | cut -d. -f1) + MACOSX_MINOR=$(shell echo $(MACOSX_VERSION_MIN) | cut -d. -f2) + ifeq ($(shell test $(MACOSX_MINOR) -gt 9; echo $$?),0) + # Multiply and then remove decimal. 10.10 -> 101000.0 -> 101000 + MAC_OS_X_VERSION_MIN_REQUIRED=$(shell echo "$(MACOSX_MAJOR) * 10000 + $(MACOSX_MINOR) * 100" | bc | cut -d. -f1) + else + # Multiply by 100 and then remove decimal. 10.7 -> 1070.0 -> 1070 + MAC_OS_X_VERSION_MIN_REQUIRED=$(shell echo "$(MACOSX_VERSION_MIN) * 100" | bc | cut -d. -f1) + endif LDFLAGS += -mmacosx-version-min=$(MACOSX_VERSION_MIN) BASE_CFLAGS += -mmacosx-version-min=$(MACOSX_VERSION_MIN) \ From d1f82ed56729b2dc44991e8822d1a959db60529d Mon Sep 17 00:00:00 2001 From: Zack Middleton Date: Sun, 11 Feb 2018 15:14:04 -0600 Subject: [PATCH 238/240] Increase q3_ui .arena filename list buffer size to 4096 bytes Allow listing about 273 .arena filenames for loading in q3_ui instead of only about 136 that fit in a 2048 byte buffer (average 15 bytes per file name). The buffer for filename list runs out of space long before the buffer for arena file content does. There is no warning for file list out of space but there is a warning for arena file content. This was requested by a user with many maps. --- code/q3_ui/ui_gameinfo.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/code/q3_ui/ui_gameinfo.c b/code/q3_ui/ui_gameinfo.c index 38cd3ea9e8..6d857de04d 100644 --- a/code/q3_ui/ui_gameinfo.c +++ b/code/q3_ui/ui_gameinfo.c @@ -169,7 +169,7 @@ static void UI_LoadArenas( void ) { int numdirs; vmCvar_t arenasFile; char filename[128]; - char dirlist[2048]; + char dirlist[4096]; char* dirptr; int i, n; int dirlen; @@ -188,7 +188,7 @@ static void UI_LoadArenas( void ) { } // get all arenas from .arena files - numdirs = trap_FS_GetFileList("scripts", ".arena", dirlist, 2048 ); + numdirs = trap_FS_GetFileList("scripts", ".arena", dirlist, 4096 ); dirptr = dirlist; for (i = 0; i < numdirs; i++, dirptr += dirlen+1) { dirlen = strlen(dirptr); From 00c348093807a71c4865a127ca9f3b592c979bd1 Mon Sep 17 00:00:00 2001 From: Zack Middleton Date: Sun, 18 Feb 2018 13:05:27 -0600 Subject: [PATCH 239/240] OpenGL2: Fix crash when BSP has deluxe maps and vertex lit surfaces MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Reported by Mickaël Thomas (mickael9). --- code/renderergl2/tr_shader.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/code/renderergl2/tr_shader.c b/code/renderergl2/tr_shader.c index b1c88f9b25..fe2cf0c3db 100644 --- a/code/renderergl2/tr_shader.c +++ b/code/renderergl2/tr_shader.c @@ -2232,7 +2232,7 @@ static void CollapseStagesToLightall(shaderStage_t *diffuse, defs |= LIGHTDEF_USE_LIGHT_VERTEX; } - if (r_deluxeMapping->integer && tr.worldDeluxeMapping && lightmap) + if (r_deluxeMapping->integer && tr.worldDeluxeMapping && lightmap && shader.lightmapIndex >= 0) { //ri.Printf(PRINT_ALL, ", deluxemap"); diffuse->bundle[TB_DELUXEMAP] = lightmap->bundle[0]; From 0d6edd227a13f1447938da1d1b020303c2545eb2 Mon Sep 17 00:00:00 2001 From: Zack Middleton Date: Thu, 22 Feb 2018 18:15:53 -0600 Subject: [PATCH 240/240] Support Unicode characters greater than 0xFF in cl_consoleKeys Unrelated: make '0x' value be -1 (invalid) instead of 0. Reported by MAN-AT-ARMS. --- code/qcommon/q_shared.c | 8 ++++---- code/sdl/sdl_input.c | 3 +-- 2 files changed, 5 insertions(+), 6 deletions(-) diff --git a/code/qcommon/q_shared.c b/code/qcommon/q_shared.c index aa17f1c1d9..a9fd68c763 100644 --- a/code/qcommon/q_shared.c +++ b/code/qcommon/q_shared.c @@ -660,15 +660,15 @@ Com_HexStrToInt */ int Com_HexStrToInt( const char *str ) { - if ( !str || !str[ 0 ] ) + if ( !str ) return -1; // check for hex code - if( str[ 0 ] == '0' && str[ 1 ] == 'x' ) + if( str[ 0 ] == '0' && str[ 1 ] == 'x' && str[ 2 ] != '\0' ) { - int i, n = 0; + int i, n = 0, len = strlen( str ); - for( i = 2; i < strlen( str ); i++ ) + for( i = 2; i < len; i++ ) { char digit; diff --git a/code/sdl/sdl_input.c b/code/sdl/sdl_input.c index 6af6751858..9b957354ba 100644 --- a/code/sdl/sdl_input.c +++ b/code/sdl/sdl_input.c @@ -138,8 +138,7 @@ static qboolean IN_IsConsoleKey( keyNum_t key, int character ) if( !token[ 0 ] ) break; - if( strlen( token ) == 4 ) - charCode = Com_HexStrToInt( token ); + charCode = Com_HexStrToInt( token ); if( charCode > 0 ) {