"Games"
{
	"left4dead2"
	{
		"Keys"
		{
			"SpitFilterClass1"	"infected"
			"SpitFilterClass2"	"trigger_finale"
			
			"SpreadFilterClass1"			"func_tracktrain"
			"SpreadFilterClass1_maxflames"	"2"
			"SpreadFilterClass2"			"func_elevator"
			"SpreadFilterClass2_maxflames"	"6"
		}
		
		"Functions"
		{
			"CSpitterProjectile::Detonate"
			{
				"signature"		"CSpitterProjectile::Detonate"
				"callconv"		"thiscall"
				"return"		"void"
				"this"			"entity"
			}
			
			"CSpitterProjectile::PhysicsSolidMaskForEntity"
			{
				"signature"		"CSpitterProjectile::PhysicsSolidMaskForEntity"
				"callconv"		"thiscall"
				"return"		"int"
				"this"			"ignore"
			}
		}
		
		"MemPatches"
		{
			"CSpitterProjectile::Detonate__TraceFlag_patch"	// Add `CONTENTS_MONSTER` to the trace flags `MASK_SOLID_BRUSHONLY`
			{
				"signature"		"CSpitterProjectile::Detonate"
				"linux"
				{
					"offset"	"285h"
					"verify"	"\x0B\x40\x00\x00"
					"patch"		"\x0B\x40\x00\x02"
				}
				"windows"
				{
					"offset"	"12Bh"
					"verify"	"\x0B\x40\x00\x00"
					"patch"		"\x0B\x40\x00\x02"
				}
			}
			
			/**< `CInferno::Spread` calls TraceRay twice to confirm a spread */
			"CInferno::Spread__TraceFlag_patch"		// Add `CONTENTS_MONSTER` to the trace flags `MASK_SHOT_PORTAL`
													// v1.15: Add `CONTENTS_WATER` and `CONTENTS_SLIME`
			{
				"signature"		"CInferno::Spread"
				"linux"
				{
					"offset"	"680h"
					"verify"	"\x03\x40\x00\x00"
					"patch"		"\x33\x40\x00\x02"
				}
				"windows"
				{
					"offset"	"6A9h"
					"verify"	"\x03\x40\x00\x00"
					"patch"		"\x33\x40\x00\x02"
				}
			}
			
			"CInferno::Spread__TraceFlag_patch2"	// Add `CONTENTS_MONSTER` to the trace flags `MASK_SHOT_PORTAL`
													// v1.15: Add `CONTENTS_WATER` and `CONTENTS_SLIME`
			{
				"signature"		"CInferno::Spread"
				"linux"
				{
					"offset"	"997h"
					"verify"	"\x03\x40\x00\x00"
					"patch"		"\x33\x40\x00\x02"
				}
				"windows"
				{
					"offset"	"8D1h"
					"verify"	"\x03\x40\x00\x00"
					"patch"		"\x33\x40\x00\x02"
				}
			}
			
			/**< Change the trace filter to have pass entity so `PassServerEntityFilter` can be called. */
			"CInferno::Spread__PassEnt_patch"
			{
				"signature"		"CInferno::Spread"
				"linux"
				{
					"offset"	"5C1h"
					"verify"	"\xC7\x44\x24\x04\x00\x00\x00\x00" // mov dword ptr [esp+4], 0 ; almost certain `passEnt` is the first param
					"patch"		"\x89\x74\x24\x04\x90\x90\x90\x90" // mov [esp+4], esi ; this
				}
				"windows"
				{
					"offset"	"62Ch"
					"verify"	"\x6A\x00" // push 0
					"patch"		"\x90\x57" // nop + push edi ; this
				}
			}
			
			"CInferno::Spread__PassEnt_patch2"
			{
				"signature"		"CInferno::Spread"
				"linux"
				{
					"offset"	"961h"
					"verify"	"\xC7\x44\x24\x04\x00\x00\x00\x00" // mov dword ptr [esp+4], 0 ; almost certain `passEnt` is the first param
					"patch"		"\x89\x74\x24\x04\x90\x90\x90\x90" // mov [esp+4], esi ; this
				}
				"windows"
				{
					"offset"	"85Bh"
					"verify"	"\x6A\x00" // push 0
					"patch"		"\x90\x57" // nop + push edi ; this
				}
			}
			
			/**< The height death spit tries to trace within. */
			"CTerrorPlayer::Event_Killed__TraceHeight_patch" // This patch only provides address and restores bytes automatically.
			{
				"signature"		"CTerrorPlayer::Event_Killed"
				"linux"
				{
					"offset"	"310Dh"
					"verify"	"\xF3\x0F\x5C" // subss xmm?, m32 ; a float pointer
					"preserve"	"\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF"
					"patch"		"\x00\x00\x00\x00\x00\x00\x00\x00"
				}
				"windows"
				{
					"offset"	"249Eh"
					"verify"	"\xF3\x0F\x5C" // subss xmm?, m32 ; a float pointer
					"preserve"	"\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF"
					"patch"		"\x00\x00\x00\x00\x00\x00\x00\x00"
				}
			}
		}
		
		"Signatures"
		{
			/** 
			 * Windows sig:
			 *	Search string "CInferno::InfernoThink (spread)" (binary search leads you to .rdata).
			 *	Go to the only xref function `CInferno::InfernoThink` and generate pseudocodes.
			 *	There you can easily find a function called inside profiling and that's it.
			**/
			"CInferno::Spread"
			{
				"library"		"server"
				"linux"			"@_ZN8CInferno6SpreadERK6Vector"
				"windows"		"\x53\x8B\x2A\x83\x2A\x2A\x83\x2A\x2A\x83\x2A\x2A\x55\x8B\x2A\x2A\x89\x2A\x2A\x2A\x8B\x2A\x81\x2A\x2A\x2A\x2A\x2A\xA1\x2A\x2A\x2A\x2A\x33\x2A\x89\x2A\x2A\x8B\x2A\x2A\x56\x57\x8B\x2A\xF3"
				/* 53 8B ? 83 ? ? 83 ? ? 83 ? ? 55 8B ? ? 89 ? ? ? 8B ? 81 ? ? ? ? ? A1 ? ? ? ? 33 ? 89 ? ? 8B ? ? 56 57 8B ? F3 */
			}
			
			/**
			 * Big thanks to @Psykotikism
			 *
			 * CTerrorPlayer::Event_Killed(CTakeDamageInfo const&)
			 *
			 * How to find on Windows:
			 * 1a. Search for any of the following strings (they can each only be found in this function):
			 * - "charger_carry_kill"
			 * - "blood_bleedout"
			 * - "spitter_killed"
			 * - "SubjectIsA:%s,Distance:%f"
			 * - "DeadCharacter:"
			 * - ",SubjectTeam:Survivor"
			 * - "SurvivorDied"
			 * - "pounce_attempt_stopped"
			 * - "infected_explosive_barrel_kill"
			 * - "mulletTongue"
			 * - "charger_killed"
			 * - "charging"
			 * - "jockey_killed"
			 * - "boomer_exploded"
			 * - "splashedbile"
			 *
			 * 1b. Find the function's offsets with asherkin's VTable dump.
			 * 2b. In IDA Pro, go to the ".rdata" section of the Windows binary.
			 * 3b. Search for "CTerrorPlayer::`vftable'" to jump to the "CTerrorPlayer" vtable.
			 * 4b. Compare your dump's offsets with asherkin's dump's offsets to find the target function.
			 * 5b. Look for the target function in the Windows binary.
			 **/
			"CTerrorPlayer::Event_Killed"
			{
				"library"	"server"
				"linux"		"@_ZN13CTerrorPlayer12Event_KilledERK15CTakeDamageInfo"
				"mac"		"@_ZN13CTerrorPlayer12Event_KilledERK15CTakeDamageInfo"
				"windows"	"\x2A\x2A\x2A\x2A\x2A\x2A\x83\x2A\x2A\x83\x2A\x2A\x55\x8B\x2A\x2A\x89\x2A\x2A\x2A\x8B\x2A\x81\xEC\x2A\x2A\x2A\x2A\xA1\x2A\x2A\x2A\x2A\x33\x2A\x89\x2A\x2A\x8B\x2A\x2A\x56\x8B\x2A\x8B\x2A\x89\x85\x2A\x2A\x2A\x2A\x8B\x82\x2A\x2A\x2A\x2A\x57\xFF\x2A\x8B\x2A\x8B"
						/* ? ? ? ? ? ? 83 ? ? 83 ? ? 55 8B ? ? 89 ? ? ? 8B ? 81 EC ? ? ? ? A1 ? ? ? ? 33 ? 89 ? ? 8B ? ? 56 8B ? 8B ? 89 85 ? ? ? ? 8B 82 ? ? ? ? 57 FF ? 8B ? 8B */
			}			
			/* Windows sig:
			 *	Search string "SpitProjectile.Bounce", only 2 of them reference to functions.
			 *	Theoretically they are closed to each other, so you can choose anyone to look into.
			 *	Find the one that has only 1 string, then go to the vtable of the function.
			 *	The virtual function 2 offsets above is the sig.
			**/
			"CSpitterProjectile::Detonate"
			{
				"library"		"server"
				"linux"			"@_ZN18CSpitterProjectile8DetonateEv"
				"windows"		"\x55\x8B\xEC\x81\x2A\x2A\x2A\x2A\x2A\xA1\x2A\x2A\x2A\x2A\x33\x2A\x89\x2A\x2A\x53\x8B\x2A\x57"
				/* 55 8B EC 81 ? ? ? ? ? A1 ? ? ? ? 33 ? 89 ? ? 53 8B ? 57 */
			}
			
			"CSpitterProjectile::PhysicsSolidMaskForEntity"
			{
				"library"		"server"
				"linux"			"@_ZNK18CSpitterProjectile25PhysicsSolidMaskForEntityEv"
				"windows"		"\xE8\x2A\x2A\x2A\x2A\x83\xE0\xF7\xC3"
								/* E8 ? ? ? ? 83 E0 F7 C3 */
			}
		}
	}
}
