diff --git a/AMBuilder b/AMBuilder index 1322ba33..25750102 100644 --- a/AMBuilder +++ b/AMBuilder @@ -7,6 +7,7 @@ projectName = 'sigsegv' sourceFiles = [ # 'src/common.h', 'libs/lz4/lib/lz4.c', + 'libs/lodepng/lodepng.cpp', os.path.join(Extension.sm_root, 'public', 'asm', 'asm.c'), 'src/sm/MemoryUtils.cpp', 'src/se2007/meshutils.cpp', @@ -39,6 +40,7 @@ sourceFiles = [ 'src/util/firehose_base.cpp', 'src/util/firehose_send.cpp', 'src/util/firehose_recv.cpp', + 'src/util/vgui.cpp', 'src/stub/baseentity.cpp', 'src/stub/gamerules.cpp', 'src/stub/misc.cpp', @@ -63,6 +65,7 @@ sourceFiles = [ 'src/stub/upgrades.cpp', 'src/stub/lagcompensation.cpp', 'src/stub/fonts.cpp', + 'src/stub/vgui.cpp', 'src/link/link.cpp', 'src/link/nextbot1.cpp', 'src/link/nextbot2.cpp', @@ -78,26 +81,26 @@ sourceFiles = [ 'src/mod/ai/rocketjump.cpp', 'src/mod/ai/wrapassassin_altfire.cpp', 'src/mod/ai/nextboteventresponder_hooks.cpp', - 'src/mod/ai/mvm_defender_bots.cpp', - 'src/mod/ai/mvm_defender_bots/quirks/isfakeclient.cpp', - 'src/mod/ai/mvm_defender_bots/quirks/isbot.cpp', - 'src/mod/ai/mvm_defender_bots/quirks/isbotoftype.cpp', - 'src/mod/ai/mvm_defender_bots/quirks/getlastknownarea.cpp', - 'src/mod/ai/mvm_defender_bots/quirks/mannvsmachinemode.cpp', - 'src/mod/ai/mvm_defender_bots/quirks/steamid.cpp', - 'src/mod/ai/mvm_defender_bots/trackers.cpp', - 'src/mod/ai/mvm_defender_bots/trackers/credits.cpp', - 'src/mod/ai/mvm_defender_bots/trackers/flags.cpp', - 'src/mod/ai/mvm_defender_bots/trackers/gates.cpp', - 'src/mod/ai/mvm_defender_bots/trackers/tanks.cpp', - 'src/mod/ai/mvm_defender_bots/actions/defender.cpp', - 'src/mod/ai/mvm_defender_bots/actions/attack_tank.cpp', - 'src/mod/ai/mvm_defender_bots/actions/defend_gate.cpp', - 'src/mod/ai/mvm_defender_bots/actions/collect_money.cpp', - 'src/mod/ai/mvm_defender_bots/actions/mark_giant.cpp', - 'src/mod/ai/mvm_defender_bots/actions/goto_upgrade_station.cpp', - 'src/mod/ai/mvm_defender_bots/actions/purchase_upgrades.cpp', - 'src/mod/ai/mvm_defender_bots/actions/prewave.cpp', +# 'src/mod/ai/mvm_defender_bots.cpp', +# 'src/mod/ai/mvm_defender_bots/quirks/isfakeclient.cpp', +# 'src/mod/ai/mvm_defender_bots/quirks/isbot.cpp', +# 'src/mod/ai/mvm_defender_bots/quirks/isbotoftype.cpp', +# 'src/mod/ai/mvm_defender_bots/quirks/getlastknownarea.cpp', +# 'src/mod/ai/mvm_defender_bots/quirks/mannvsmachinemode.cpp', +# 'src/mod/ai/mvm_defender_bots/quirks/steamid.cpp', +# 'src/mod/ai/mvm_defender_bots/trackers.cpp', +# 'src/mod/ai/mvm_defender_bots/trackers/credits.cpp', +# 'src/mod/ai/mvm_defender_bots/trackers/flags.cpp', +# 'src/mod/ai/mvm_defender_bots/trackers/gates.cpp', +# 'src/mod/ai/mvm_defender_bots/trackers/tanks.cpp', +# 'src/mod/ai/mvm_defender_bots/actions/defender.cpp', +# 'src/mod/ai/mvm_defender_bots/actions/attack_tank.cpp', +# 'src/mod/ai/mvm_defender_bots/actions/defend_gate.cpp', +# 'src/mod/ai/mvm_defender_bots/actions/collect_money.cpp', +# 'src/mod/ai/mvm_defender_bots/actions/mark_giant.cpp', +# 'src/mod/ai/mvm_defender_bots/actions/goto_upgrade_station.cpp', +# 'src/mod/ai/mvm_defender_bots/actions/purchase_upgrades.cpp', +# 'src/mod/ai/mvm_defender_bots/actions/prewave.cpp', 'src/mod/attr/undocumented.cpp', 'src/mod/bot/kill_before_forcespec.cpp', 'src/mod/bot/medieval_nonmelee.cpp', @@ -106,6 +109,7 @@ sourceFiles = [ 'src/mod/bot/isspacetospawnhere_scale.cpp', 'src/mod/canteen/share_recall_canteen.cpp', 'src/mod/cond/reprogrammed.cpp', + 'src/mod/cond/enhanced_cmds.cpp', 'src/mod/credits/spawn_autocollect.cpp', # 'src/mod/credits/magnet_disable.cpp', 'src/mod/debug/backtrace.cpp', @@ -146,7 +150,8 @@ sourceFiles = [ 'src/mod/debug/tele_autodetonate.cpp', 'src/mod/debug/scale_rate.cpp', 'src/mod/debug/melee_trace.cpp', - 'src/mod/debug/console_scramble.cpp', + 'src/mod/debug/console_scramble_v1.cpp', + 'src/mod/debug/console_scramble_v2.cpp', 'src/mod/debug/sound_leak.cpp', 'src/mod/debug/nextbot_input.cpp', 'src/mod/debug/static_props.cpp', @@ -165,6 +170,8 @@ sourceFiles = [ 'src/mod/debug/upgrade_tiers.cpp', 'src/mod/debug/mvm_shield_fps.cpp', 'src/mod/debug/rage.cpp', + 'src/mod/debug/ctfbotproxy.cpp', + 'src/mod/debug/sentrybuster_mannhattan.cpp', # 'src/mod/debug/scheme_load.cpp', # 'src/mod/debug/override_step_sound.cpp', 'src/mod/demo/stringtable_limit.cpp', @@ -175,6 +182,7 @@ sourceFiles = [ 'src/mod/etc/laserdot_fix.cpp', 'src/mod/etc/instant_scaling.cpp', 'src/mod/etc/mm_spoof.cpp', + 'src/mod/etc/melee_ignore_teammates.cpp', 'src/mod/mvm/changeclass_anytime.cpp', 'src/mod/mvm/disposable_dispenser.cpp', 'src/mod/mvm/explosive_headshot_on_everything.cpp', @@ -184,8 +192,11 @@ sourceFiles = [ 'src/mod/mvm/dominations.cpp', 'src/mod/mvm/friendlyfire.cpp', 'src/mod/mvm/gib_improvements.cpp', + 'src/mod/mvm/no_halloween_souls.cpp', + 'src/mod/mvm/robosapper_override.cpp', 'src/mod/perf/flame_breakable_collision.cpp', 'src/mod/perf/medigun_shield_damage_events.cpp', + 'src/mod/perf/medigun_shield_damage_interval.cpp', 'src/mod/pop/eventpopfile_improvements.cpp', # 'src/mod/pop/extattr/parse.cpp', # 'src/mod/pop/extattr/alwaysfireweaponalt.cpp', @@ -228,7 +239,14 @@ sourceFiles = [ 'src/mod/util/debugoverlay_font_v3.cpp', 'src/mod/util/override_vertex_limit.cpp', 'src/mod/util/entity_overlays.cpp', + 'src/mod/util/serialize_spew.cpp', + 'src/mod/util/screenshot_png.cpp', + 'src/mod/util/dtwatch.cpp', + 'src/mod/util/confilter.cpp', +# 'src/mod/util/netmsg_server.cpp', +# 'src/mod/util/netmsg_client.cpp', # 'src/mod/util/client_cmds.cpp', + 'src/mod/vgui/test.cpp', 'src/mod/visualize/airblast_box.cpp', 'src/mod/visualize/airblast_cone.cpp', 'src/mod/visualize/airblast_vectors.cpp', @@ -246,6 +264,8 @@ sourceFiles = [ 'src/mod/visualize/sapper_range.cpp', 'src/mod/visualize/huntsman.cpp', 'src/mod/visualize/ammo_counts.cpp', + 'src/mod/visualize/backstab.cpp', + 'src/mod/visualize/conds.cpp', ] ############### @@ -292,7 +312,9 @@ binary.compiler.linkflags += [ binary.compiler.includes += [ os.path.join(builder.buildPath), # for autogen.h os.path.join(builder.currentSourcePath, 'libs', 'lz4', 'lib'), + os.path.join(builder.currentSourcePath, 'libs', 'lodepng'), os.path.join(builder.currentSourcePath, 'libs', 'capstone', 'include'), +# os.path.join(builder.currentSourcePath, 'libs', 'cccapstone', 'cppbindings'), os.path.join(builder.currentSourcePath, 'libs', 'ann', 'include'), ] diff --git a/MSVC14/sigsegv.sln b/MSVC14/sigsegv.sln index 31f8d9c5..270a2837 100755 --- a/MSVC14/sigsegv.sln +++ b/MSVC14/sigsegv.sln @@ -1,6 +1,6 @@ Microsoft Visual Studio Solution File, Format Version 12.00 # Visual Studio 14 -VisualStudioVersion = 14.0.24720.0 +VisualStudioVersion = 14.0.25420.1 MinimumVisualStudioVersion = 10.0.40219.1 Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "sigsegv", "sigsegv.vcxproj", "{B3E797CF-4E77-4C9D-B8A8-7589B6902206}" EndProject @@ -18,7 +18,15 @@ Project("{911E67C6-3D85-4FCE-B560-20A9C3E3FF48}") = "hl2", "C:\Program Files (x8 AttachLaunchAction = No EndProjectSection EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution Items", "{C2774530-5E69-42AA-AA01-6EC3173D8E14}" + ProjectSection(SolutionItems) = preProject + Performance1.psess = Performance1.psess + EndProjectSection +EndProject Global + GlobalSection(Performance) = preSolution + HasPerformanceSessions = true + EndGlobalSection GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Win32 = Debug|Win32 Release|Win32 = Release|Win32 diff --git a/MSVC14/sigsegv.vcxproj b/MSVC14/sigsegv.vcxproj old mode 100644 new mode 100755 index 30dc347e..595e3f20 --- a/MSVC14/sigsegv.vcxproj +++ b/MSVC14/sigsegv.vcxproj @@ -68,12 +68,13 @@ - /D SE_EPISODEONE=1 /D SE_DARKMESSIAH=2 /D SE_ORANGEBOX=3 /D SE_BLOODYGOODTIME=4 /D SE_EYE=5 /D SE_CSS=6 /D SE_ORANGEBOXVALVE=7 /D SE_LEFT4DEAD=8 /D SE_LEFT4DEAD2=9 /D SE_ALIENSWARM=10 /D SE_PORTAL2=11 /D SE_CSGO=12 + + Disabled - ..\src;..\src\sdk;$(SOURCEMOD18);$(SOURCEMOD18)\public;$(SOURCEMOD18)\public\amtl;$(SOURCEMOD18)\public\extensions;$(SOURCEMOD18)\sourcepawn\include;$(HL2SDKOBVALVE)\common;$(HL2SDKOBVALVE)\game\shared;$(HL2SDKOBVALVE)\public;$(HL2SDKOBVALVE)\public\engine;$(HL2SDKOBVALVE)\public\game\server;$(HL2SDKOBVALVE)\public\tier0;$(HL2SDKOBVALVE)\public\tier1;$(HL2SDKOBVALVE)\public\toolframework;$(HL2SDKOBVALVE)\public\vstdlib;$(MMSOURCE110)\core;$(MMSOURCE110)\core\sourcehook;..\libs\boost_1_62_0_b2;..\libs\lz4\lib;..\libs\distorm\include;..\libs\capstone\include;..\libs\udis86;..\libs\ann\include;%(AdditionalIncludeDirectories) - WIN32;_DEBUG;_WINDOWS;_USRDLL;SDK_EXPORTS;_CRT_SECURE_NO_DEPRECATE;SOURCEMOD_BUILD;SOURCE_ENGINE=7;%(PreprocessorDefinitions) + ..\src;..\src\sdk;$(SOURCEMOD18);$(SOURCEMOD18)\public;$(SOURCEMOD18)\public\amtl;$(SOURCEMOD18)\public\extensions;$(SOURCEMOD18)\sourcepawn\include;$(HL2SDKOBVALVE)\common;$(HL2SDKOBVALVE)\game\shared;$(HL2SDKOBVALVE)\public;$(HL2SDKOBVALVE)\public\engine;$(HL2SDKOBVALVE)\public\game\server;$(HL2SDKOBVALVE)\public\tier0;$(HL2SDKOBVALVE)\public\tier1;$(HL2SDKOBVALVE)\public\toolframework;$(HL2SDKOBVALVE)\public\vstdlib;$(MMSOURCE110)\core;$(MMSOURCE110)\core\sourcehook;..\libs\boost_1_62_0_b2;..\libs\lz4\lib;..\libs\lodepng;..\libs\distorm\include;..\libs\capstone\include;..\libs\cccapstone\cppbindings;..\libs\udis86;..\libs\ann\include;%(AdditionalIncludeDirectories) + WIN32;_DEBUG;_WINDOWS;_USRDLL;SDK_EXPORTS;_CRT_SECURE_NO_DEPRECATE;SOURCEMOD_BUILD;SOURCE_ENGINE=7;SE_EPISODEONE=1;SE_DARKMESSIAH=2;SE_ORANGEBOX=3;SE_BLOODYGOODTIME=4;SE_EYE=5;SE_CSS=6;SE_ORANGEBOXVALVE=7;SE_LEFT4DEAD=8;SE_LEFT4DEAD2=9;SE_ALIENSWARM=10;SE_PORTAL2=11;SE_CSGO=12;%(PreprocessorDefinitions) false - UninitializedLocalUsageCheck + EnableFastChecks MultiThreadedDebug NotSet true @@ -87,6 +88,7 @@ $(IntDir)\%(Directory)\ + 4594 $(HL2SDKOBVALVE)\lib\public\tier0.lib;$(HL2SDKOBVALVE)\lib\public\tier1.lib;$(HL2SDKOBVALVE)\lib\public\vstdlib.lib;$(HL2SDKOBVALVE)\lib\public\mathlib.lib;legacy_stdio_definitions.lib;dbghelp.lib;ws2_32.lib;..\libs\distorm\distorm.lib;..\libs\capstone\msvc\Release\capstone.lib;..\libs\udis86\BuildVS2010\Build\Lib\x86\libudis86.lib;%(AdditionalDependencies) @@ -118,10 +120,10 @@ - /MP /D SE_EPISODEONE=1 /D SE_DARKMESSIAH=2 /D SE_ORANGEBOX=3 /D SE_BLOODYGOODTIME=4 /D SE_EYE=5 /D SE_CSS=6 /D SE_ORANGEBOXVALVE=7 /D SE_LEFT4DEAD=8 /D SE_LEFT4DEAD2=9 /D SE_ALIENSWARM=10 /D SE_PORTAL2=11 /D SE_CSGO=12 + /MP Speed - ..\src;..\src\sdk;$(SOURCEMOD18);$(SOURCEMOD18)\public;$(SOURCEMOD18)\public\amtl;$(SOURCEMOD18)\public\extensions;$(SOURCEMOD18)\sourcepawn\include;$(HL2SDKOBVALVE)\common;$(HL2SDKOBVALVE)\game\shared;$(HL2SDKOBVALVE)\public;$(HL2SDKOBVALVE)\public\engine;$(HL2SDKOBVALVE)\public\game\server;$(HL2SDKOBVALVE)\public\tier0;$(HL2SDKOBVALVE)\public\tier1;$(HL2SDKOBVALVE)\public\toolframework;$(HL2SDKOBVALVE)\public\vstdlib;$(MMSOURCE110)\core;$(MMSOURCE110)\core\sourcehook;..\libs\boost_1_62_0_b2;..\libs\lz4\lib;..\libs\distorm\include;..\libs\capstone\include;..\libs\udis86;..\libs\ann\include;%(AdditionalIncludeDirectories) - WIN32;_WINDOWS;_USRDLL;SDK_EXPORTS;_CRT_SECURE_NO_DEPRECATE;SOURCEMOD_BUILD;SOURCE_ENGINE=7;%(PreprocessorDefinitions) + ..\src;..\src\sdk;$(SOURCEMOD18);$(SOURCEMOD18)\public;$(SOURCEMOD18)\public\amtl;$(SOURCEMOD18)\public\extensions;$(SOURCEMOD18)\sourcepawn\include;$(HL2SDKOBVALVE)\common;$(HL2SDKOBVALVE)\game\shared;$(HL2SDKOBVALVE)\public;$(HL2SDKOBVALVE)\public\engine;$(HL2SDKOBVALVE)\public\game\server;$(HL2SDKOBVALVE)\public\tier0;$(HL2SDKOBVALVE)\public\tier1;$(HL2SDKOBVALVE)\public\toolframework;$(HL2SDKOBVALVE)\public\vstdlib;$(MMSOURCE110)\core;$(MMSOURCE110)\core\sourcehook;..\libs\boost_1_62_0_b2;..\libs\lz4\lib;..\libs\lodepng;..\libs\distorm\include;..\libs\capstone\include;..\libs\cccapstone\cppbindings;..\libs\udis86;..\libs\ann\include;%(AdditionalIncludeDirectories) + WIN32;_WINDOWS;_USRDLL;SDK_EXPORTS;_CRT_SECURE_NO_DEPRECATE;SOURCEMOD_BUILD;SOURCE_ENGINE=7;SE_EPISODEONE=1;SE_DARKMESSIAH=2;SE_ORANGEBOX=3;SE_BLOODYGOODTIME=4;SE_EYE=5;SE_CSS=6;SE_ORANGEBOXVALVE=7;SE_LEFT4DEAD=8;SE_LEFT4DEAD2=9;SE_ALIENSWARM=10;SE_PORTAL2=11;SE_CSGO=12;%(PreprocessorDefinitions) MultiThreaded NotSet true @@ -136,6 +138,7 @@ true + 4594 $(HL2SDKOBVALVE)\lib\public\tier0.lib;$(HL2SDKOBVALVE)\lib\public\tier1.lib;$(HL2SDKOBVALVE)\lib\public\vstdlib.lib;$(HL2SDKOBVALVE)\lib\public\mathlib.lib;legacy_stdio_definitions.lib;dbghelp.lib;ws2_32.lib;..\libs\distorm\distorm.lib;..\libs\capstone\msvc\Release\capstone.lib;..\libs\udis86\BuildVS2010\Build\Lib\x86\libudis86.lib;%(AdditionalDependencies) @@ -173,6 +176,7 @@ NotUsing NotUsing + NotUsing NotUsing @@ -210,7 +214,8 @@ - + + @@ -249,6 +254,8 @@ + + @@ -290,12 +297,16 @@ + NotUsing NotUsing - - + + + + + @@ -316,4 +327,4 @@ - + \ No newline at end of file diff --git a/MSVC14/sigsegv.vcxproj.filters b/MSVC14/sigsegv.vcxproj.filters old mode 100644 new mode 100755 index c646eb53..8707cea2 --- a/MSVC14/sigsegv.vcxproj.filters +++ b/MSVC14/sigsegv.vcxproj.filters @@ -332,9 +332,6 @@ Source Files - - Source Files - Source Files @@ -350,13 +347,40 @@ Source Files - + + Source Files + + Source Files - + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + Source Files - + Source Files diff --git a/PackageScript b/PackageScript index e81e3e46..e16652a5 100644 --- a/PackageScript +++ b/PackageScript @@ -71,6 +71,7 @@ CopyFiles('gamedata/sigsegv', 'addons/sourcemod/gamedata/sigsegv', 'misc.txt', 'debugoverlay.txt', 'client.txt', + 'convars.txt', ] ) diff --git a/gamedata/sigsegv/client.txt b/gamedata/sigsegv/client.txt index 81d0a092..9ce5b285 100644 --- a/gamedata/sigsegv/client.txt +++ b/gamedata/sigsegv/client.txt @@ -78,7 +78,7 @@ type "pattern" sym "_ZN13CDebugOverlay15DrawAllOverlaysEv" lib "engine" - seg ".text" + seg "text" seek "558beca1c43c491083ec20837830000f840601000053576a0168e5030000689417311068d41731108d45e068043d491050e84aa0ffff8b3da03c491083c41833db85ff0f8493000000f30f100db01e2f1056" mask "ffffffff00000000ffffffffffffffffff00000000ffffff00ff00000000ff00000000ff00000000ffffffff00000000ffff00000000ffff00000000ffffffffffffffffff00000000ffffffff00000000ff" } @@ -87,7 +87,7 @@ type "pattern" sym "_ZN13CDebugOverlay11DrawOverlayEPNS_13OverlayBase_tE" lib "engine" - seg ".text" + seg "text" seek "558bec83ec348d45cc566a016863030000684c173110688c17311068043d491050e86a9bffff8b750883c4188b0683f8050f87b5010000" mask "ffffffffffffffffffffffffff00000000ff00000000ff00000000ff00000000ffff00000000ffffffffffffffffffffffffff00000000" } @@ -97,7 +97,7 @@ type "pattern" sym "_Z21RenderWireframeSphereRK6Vectorfii5Colorb" lib "client" - seg ".text" + seg "text" seek "558bec81ec18020000535657e8dff6ffff8b5d108b4514438bfb895d100faff8488945f88d0c9dfcffffff8945dc0fafc8894dfc8b0da04ad7108b01ff90880100008bf08975e885f674078b068bceff5008" mask "ffffffffffffffffffffffffff00000000ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00000000ffffffff00000000ffffffffffffffff00ffffffffffff00" } @@ -106,7 +106,7 @@ // type "pattern" // sym "" // lib "engine" - // seg ".text" + // seg "text" // seek "" // mask "" // } @@ -115,7 +115,7 @@ type "pattern" sym "_Z12RenderSphereRK6Vectorfii5ColorP9IMaterial" lib "engine" - seg ".text" + seg "text" seek "538bdc83ec0883e4f083c404558b6b04896c24048bec81ec680200005657e81df7ffff8b0d7ccf67108b018b8088010000ffd08bf08975e085f67407" mask "ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00000000ffff00000000ffffffff00000000ffffffffffffffffffff00" } @@ -124,7 +124,7 @@ type "pattern" sym "_Z18RenderWireframeBoxRK6VectorRK6QAngleS1_S1_5Colorb" lib "engine" - seg ".text" + seg "text" seek "558bec81ec5802000056e801edffff8b0d04f067108b01ff90880100008bf08975fc85f674078b068bceff5008a138f067108bce807d1c008b160f450534f0671053576a0050ff5224" mask "ffffffffffffffffffffff00000000ffff00000000ffffffffffffffffffffffffffffffffffffffffffffffffff00000000ffffffffffffffffffffff00000000ffffffffffffffff" } @@ -133,7 +133,7 @@ type "pattern" sym "_Z23RenderWireframeSweptBoxRK6VectorS1_RK6QAngleS1_S1_5Colorb" lib "engine" - seg ".text" + seg "text" seek "558bec81ecc40200008b0d08f06710578b01ff90880100008bf8897df085ff74078b078bcfff5008a138f067108bcf807d20008b170f450534f0671053566a0050ff5224" mask "ffffffffffffffffffffff00000000ffffffffffffffffffffffffffffffffffffffffffffffffffff00000000ffffffffffffffffffffff00000000ffffffffffffffff" } @@ -142,7 +142,7 @@ type "pattern" sym "_Z9RenderBoxRK6VectorRK6QAngleS1_S1_5Colorbb" lib "engine" - seg ".text" + seg "text" seek "" mask "" } @@ -151,7 +151,7 @@ type "pattern" sym "_Z9RenderBoxRK6VectorRK6QAngleS1_S1_5ColorP9IMaterialb" lib "engine" - seg ".text" + seg "text" seek "558bec81ec7002000056e871feffff8b0d04f067108b01ff90880100008bf08975dc85f674078b068bceff50088b068bce53576a00ff751cff5024" mask "ffffffffffffffffffffff00000000ffff00000000ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff" } @@ -160,7 +160,7 @@ // type "pattern" // sym "" // lib "engine" - // seg ".text" + // seg "text" // seek "" // mask "" // } @@ -169,7 +169,7 @@ // type "pattern" // sym "" // lib "engine" - // seg ".text" + // seg "text" // seek "" // mask "" // } @@ -178,7 +178,7 @@ type "pattern" sym "_Z10RenderLineRK6VectorS1_5Colorb" lib "engine" - seg ".text" + seg "text" seek "558bec81ecf401000056e8b1f9ffff8b0d04f067108b01ff90880100008bf085f674078b068bceff5008a138f067108bce807d14008b160f450534f0671053576a0050ff5224" mask "ffffffffffffffffffffff00000000ffff00000000ffffffffffffffffffffffffffffffffffffffffffff00000000ffffffffffffffffffffff00000000ffffffffffffffff" } @@ -187,7 +187,7 @@ type "pattern" sym "_Z14RenderTriangleRK6VectorS1_S1_5Colorb" lib "engine" - seg ".text" + seg "text" seek "" mask "" } @@ -196,7 +196,7 @@ type "pattern" sym "_Z14RenderTriangleRK6VectorS1_S1_5ColorP9IMaterial" lib "engine" - seg ".text" + seg "text" seek "558bec81ec0402000056e881f1ffff8b0d04f067108b018b8088010000ffd08bf085f674078b068bceff50088b068bce576a00ff7518ff5024" mask "ffffffffffffffffffffff00000000ffff00000000ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff" } @@ -205,7 +205,7 @@ // type "pattern" // sym "" // lib "engine" - // seg ".text" + // seg "text" // seek "" // mask "" // } @@ -214,7 +214,7 @@ // type "pattern" // sym "_Z24DrawScreenSpaceRectangleP9IMaterialiiiiffffiiPviif" // lib "engine" - // seg ".text" + // seg "text" // seek "" // mask "" // } @@ -278,7 +278,7 @@ { type "pattern" sym "_ZN15CTFGameMovement15ProcessMovementEP12C_BasePlayerP9CMoveData" - seg ".text" + seg "text" seek "558bec56578b7d088bf185ff746f538b5d0c85db7466e85586dcffc7868806000000000000" mask "ffffffffffffffffffffffffffffffffffffffffffffff00000000ffff0000000000000000" lib "client" @@ -297,7 +297,7 @@ { type "pattern" sym "TODO" - seg ".text" + seg "text" seek "558bec81ec000c000056ff75108bf1ff750c8b06ff5050ff75208b068bceff751cff7518ff7514ff504cff75288d8500fcffffff7524680004000050" mask "ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff" lib "vguimatsurface" @@ -307,7 +307,7 @@ { type "pattern" sym "TODO" - seg ".text" + seg "text" seek "558bec83ec1c53568bf18b0d20d3cd1057e8da33e9ff84c00f84b6010000" mask "ffffffffffffffffffffffff00000000ffff00000000ffffffffffffffff" lib "client" @@ -317,7 +317,7 @@ { type "pattern" sym "TODO" - seg ".text" + seg "text" seek "558bec81ec5c0100008d85a4feffff568b750868000100005650e8da021e006a2e56e8f9d1250083c41485c0751b6aff68000100008d85a4feffff" mask "ffffffffffffffffffffffffffffffffffffffffffffffffffffff00000000ffffffffff00000000ffffffffffffffffffffffffffffffffffffff" lib "engine" @@ -327,7 +327,7 @@ { type "pattern" sym "TODO" - seg ".text" + seg "text" seek "558bec56ff75088bf1e802f0ffffc7463084842f108bc6c7068c842f10c74630d8842f10c7863c40000000000000" mask "ffffffffffffffffffff00000000ffffff00000000ffffffff00000000ffffff00000000ffffffffffffffffffff" lib "engine" @@ -336,7 +336,7 @@ { type "pattern" sym "TODO" - seg ".text" + seg "text" seek "558bec568bf18b4e34c7068c842f10c74630d8842f1085c974068b016a01ff108bcee8c9effffff6450801740956e8dda7130083c4048bc65e5dc20400" mask "ffffffffffffffffffffff00000000ffffff00000000ffffffffffffffffffffffffff00000000ffffffffffffffff00000000ffffffffffffffffffff" lib "engine" @@ -362,7 +362,7 @@ { type "pattern" sym "TODO" - seg ".text" + seg "text" seek "558bec81ec84000000538bd98b4d0c895df856578b7d0885c9751357b910cb1310e81aa3000033c984c00f95c141" mask "ffffffffff00ffffffffffffffffffffff00ffffffffffffffffffffff00000000ff00000000ffffffffffffffff" lib "vguimatsurface" @@ -376,7 +376,7 @@ { type "pattern" sym "TODO" - seg ".text" + seg "text" seek "558bec8b451483ec1485c00f84fb0000008b550c5733ff897df885d20f8ee90000008b4d1053568b750883c103" mask "ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff" lib "vguimatsurface" @@ -385,7 +385,7 @@ { type "pattern" sym "TODO" - seg ".text" + seg "text" seek "558bec83ec34538b5d14565785db0f84d20200008d04dd1300000083e0f0e8bd650600db45148d041b8bfc897dfc8945dc" mask "ffffffffffffffffffffffffffffffffffffffffffffff00000000ffffffff00000000ffffffffffffffffffffffffffff" lib "vguimatsurface" @@ -426,7 +426,7 @@ { type "func ebpprologue unistr" sym "TODO" - seg ".text" + seg "text" unistr "attacker_player" // also: "patient" lib "client" @@ -437,7 +437,7 @@ { type "func ebpprologue unistr" sym "TODO" - seg ".text" + seg "text" unistr "DamagedPlayer" lib "client" } @@ -447,7 +447,7 @@ { type "pattern" sym "TODO" - seg ".text" + seg "text" seek "558bec56ff750c8bf1ff75088b06ff9098000000508bcee814ffffff5e5dc20800cc" mask "ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff" lib "client" @@ -458,11 +458,69 @@ { type "pattern" sym "TODO" - seg ".text" + seg "text" seek "558bec51568bf15780be5b010000007405e87aedffffff750c8d450eb9bccedc1050e8f982fdff0fb738b8ffff0000" mask "ffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00000000ffffffffffffffffffffffffffff" lib "client" } + + "[client] CBuildFactoryHelper::GetFactoryNames" + { + type "pattern" + sym "_ZN4vgui19CBuildFactoryHelper15GetFactoryNamesER10CUtlVectorIPKc10CUtlMemoryIS3_iEE" + seg "text" + seek "558bec568b750857c7460c000000008b3dd0fbdc1085ff7462538d9b000000008b5e0c8b470c8b4e048945088d43013bc1" + mask "ffffffffffffffffffffffffffffffffff00000000ffffff00ffffffffffffffffffffffffffffffffffffffffffffffff" + lib "client" + } + + "[client] CBuildFactoryHelper::InstancePanel" + { + type "pattern" + sym "_ZN4vgui19CBuildFactoryHelper13InstancePanelEPKc" + seg "text" + seek "558bec568b35d0fbdc105785f674198b7d08ff760c57e8058dfbff83c40885c0740c8b3685f675ea5f33c05e5dc3" + mask "ffffffffffff00000000ffffffffffffffffffffffffff00000000ffffffffffffffffffffffffffffffffffffff" + lib "client" + } + + "[client] GetClientModeNormal" + { + type "pattern" + sym "_Z19GetClientModeNormalv" + seg "text" + seek "a130d3d610a801751f83c801b998d2d610a330d3d610e855e2ffff68a0039810e839c73d0083c404b898d2d610c3" + mask "ff00000000ffffffffffffffff00000000ff00000000ff00000000ff00000000ff00000000ffffffff00000000ff" + lib "client" + } + + "[client] CTFModeManager::LevelInit" + { + type "func ebpprologue unistr" + sym "_ZN14CTFModeManager9LevelInitEPKc" + seg "text" + unistr "voice_steal" + lib "client" + } + + "[client] vgui::Frame::Frame" + { + type "func ebpprologue unistr" + sym "_ZN4vgui5FrameC2EPNS_5PanelEPKcbb" + seg "text" + unistr "#Frame_Untitled" + lib "client" + } + + // "[client] MySpewOutputFunc" + // { + // type "pattern" + // sym "_ZL16MySpewOutputFunc10SpewType_tPKc" + // seg "text" + // seek "" + // mask "" + // lib "materialsystem" + // } } } } diff --git a/gamedata/sigsegv/convars.txt b/gamedata/sigsegv/convars.txt new file mode 100644 index 00000000..12d2e849 --- /dev/null +++ b/gamedata/sigsegv/convars.txt @@ -0,0 +1,37 @@ +// convars + +"Games" +{ + "#default" + { + "#supported" + { + engine "tf2" + } + + "sigsegv" + { + "addrs" + { + "con_filter_enable" + { + type "convar" + name "con_filter_enable" + lib "engine" + } + "con_filter_text" + { + type "convar" + name "con_filter_text" + lib "engine" + } + "con_filter_text_out" + { + type "convar" + name "con_filter_text_out" + lib "engine" + } + } + } + } +} diff --git a/gamedata/sigsegv/datamaps.txt b/gamedata/sigsegv/datamaps.txt index 62fe2fa0..48ae6420 100644 --- a/gamedata/sigsegv/datamaps.txt +++ b/gamedata/sigsegv/datamaps.txt @@ -55,6 +55,18 @@ sym "_ZN12CFuncNavCost9m_DataMapE" class "CFuncNavCost" } + "CFuncNavPrerequisite::m_DataMap" + { + type "datamap" + sym "_ZN20CFuncNavPrerequisite9m_DataMapE" + class "CFuncNavPrerequisite" + } + "CFilterTFBotHasTag::m_DataMap" + { + type "datamap" + sym "_ZN18CFilterTFBotHasTag9m_DataMapE" + class "CFilterTFBotHasTag" + } } } } diff --git a/gamedata/sigsegv/globals.txt b/gamedata/sigsegv/globals.txt index 635ff114..9f6097cf 100644 --- a/gamedata/sigsegv/globals.txt +++ b/gamedata/sigsegv/globals.txt @@ -83,6 +83,23 @@ type "sym" sym "lagcompensation" } + + "s_pTokenBuf" + { + type "sym" + sym "_ZL11s_pTokenBuf" + } + + "s_TankModel" + { + type "sym" + sym "_ZL11s_TankModel" + } + "s_TankModelRome" + { + type "sym" + sym "_ZL15s_TankModelRome" + } } } } diff --git a/gamedata/sigsegv/misc.txt b/gamedata/sigsegv/misc.txt index 5718f073..308d9ac8 100644 --- a/gamedata/sigsegv/misc.txt +++ b/gamedata/sigsegv/misc.txt @@ -115,11 +115,12 @@ "CCurrencyPack::ComeToRest" { - type "func datamap vthunk" - sym "_ZN13CCurrencyPack10ComeToRestEv" - datamap "CItem::m_DataMap" - func "CItemComeToRest" - vtable ".?AVCCurrencyPack@@" + type "func datamap vthunk" + sym "_ZN13CCurrencyPack10ComeToRestEv" + // classname "item_currencypack_custom" + datamap "CItem::m_DataMap" + func "CItemComeToRest" + vtable ".?AVCCurrencyPack@@" } "CCurrencyPack::MyTouch" { @@ -182,7 +183,7 @@ { type "pattern" sym "_ZN17CAttributeManager15AttribHookValueIiEET_S1_PKcPK11CBaseEntityP10CUtlVectorIPS4_10CUtlMemoryIS8_iEEb" - seg ".text" + seg "text" seek "558bec83ec248b0d8cc26a105333db895ddc895de08b4108895df0895df45785c0743e6894106f1068b8106f106890806c10683c1e92106a7768c8106f10535353538d4ddc5150" mask "ffffffffffffffff00000000ffffffffffffffffffffffffffffffffffffffffffffffff00000000ff00000000ff00000000ff00000000ffffff00000000ffffffffffffffffff" } @@ -190,7 +191,7 @@ { type "pattern" sym "_ZN17CAttributeManager15AttribHookValueIfEET_S1_PKcPK11CBaseEntityP10CUtlVectorIPS4_10CUtlMemoryIS8_iEEb" - seg ".text" + seg "text" seek "558bec83ec288b0d8cc26a105333db895dd8895ddc8b4108895dec895df05685c0743e6894106f1068b8106f106890806c1068481e92106a7768c8106f10535353538d4dd85150" mask "ffffffffffffffff00000000ffffffffffffffffffffffffffffffffffffffffffffffff00000000ff00000000ff00000000ff00000000ffffff00000000ffffffffffffffffff" } @@ -608,7 +609,7 @@ { type "pattern" sym "_ZN11IGameSystem3AddEPS_" - seg ".text" + seg "text" seek "558bec518b1514d58b108b0d0cd58b10568bf28d42013bc17e142bd1b908d58b104252e8d89c1c008b1514d58b10a108d58b1042891514d58b102bd64aa318d58b1085d27e1d8d0cb08d04950000000050518d410450e8055b4200a108d58b1083c40c8d04b08b750885c0740289306a0068b0de8f1068c4dd8f106a0056e81170420083c41485c074118d45fc8975fc50b91cd58b10e835981c005e8be55dc3" mask "ffffffffffff00000000ffff00000000ffffffffffffffffff00ffffff00000000ffffff00000000ffff00000000ff00000000ffffff00000000ffffffff00000000ffffff00ffffffffffffffffffffffffffffffffff00000000ff00000000ffffffffffffffffffffffff00ffffffffff00000000ff00000000ffffffff00000000ffffffffffff00ffffffffffffffff00000000ff00000000ffffffffff" } @@ -616,7 +617,7 @@ { type "pattern" sym "_ZN11IGameSystem6RemoveEPS_" - seg ".text" + seg "text" seek "558bec51568d4508b9b8148c1050e81dbb0a008b75086a00689020901068a41f90106a0056e84aed420083c41485c074118d45fc8975fc50b9cc148c10e8eeba0a005e8be55dc3" mask "ffffffffffffffffff00000000ffff00000000ffffffffffff00000000ff00000000ffffffff00000000ffffffffffffffffffffffffffffff00000000ff00000000ffffffffff" // seek "558bec51568bf18d45fc50b908d58b108975fcc706387a6d10e8a29708006a0068b0de8f1068c4dd8f106a0056e8f271420083c41485c074118d45fc8975fc50b91cd58b10e8769708005e8be55dc3" @@ -909,7 +910,7 @@ { type "pattern" sym "_ZN17CGlobalEntityList21FindEntityByClassnameEP11CBaseEntityPKc" - seg ".text" + seg "text" seek "558bec5356578bf98b4d0885c974158b01ff50088b3081e6ff0f00004603f68b34f7eb068bb70400010085f674318b5d0c8b3e85ff75106880a47010ff1574f26a1083c404eb11395f5c741c538bcfe8ac9ffaff84c075108b760c85f675d25f5e33c05b5dc208008bc75f5e5b5dc20800" mask "ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00000000ffffffffffffffffffffffffffff00000000ffff00000000ffffffffffffffffffffffffffff00000000ffffffffffffffffffffffffffffffffffffffffffffffffffffffffff" } @@ -1036,7 +1037,7 @@ // { // type "pattern" // sym "_ZN11CRConClient7SendCmdEPKc" -// seg ".text" +// seg "text" // seek "558bec83ec30538bd956578d4b34e8dd86fdff85c07f336a018d7b5c578d4b34e84baf070085c079216a008bcf" // mask "ffffffffffffffffffffffffffffff00000000ffffff00ffffffffffffffffffff00000000ffffff00ffffffff" // lib "engine" @@ -1046,7 +1047,7 @@ { type "pattern" sym "_ZN11CRConClient12SendResponseER10CUtlBufferb" - seg ".text" + seg "text" seek "558bec807d0c00538bd9742f807b70007529e819e9ffff8d4b34e8118dfdff85c07e5c8b45088d8ba4000000ff701cff30e8ba0019005b5dc20800" mask "ffffffffffffffffffffff00ffffffffff00ff00000000ffffffff00000000ffffff00ffffffffffffffffffffffffffffff00000000ffffffffff" lib "engine" @@ -1055,7 +1056,7 @@ { type "pattern" sym "_ZN11CRConClient13BuildResponseER10CUtlBuffer23ServerDataRequestType_tPKcS4_" - seg ".text" + seg "text" seek "558bec568b7508578bf96a00f646150175098bcee867f31100eb0e682c652f1056e84a15190083c40c" mask "ffffffffffffffffffffffffffffffffff00ffffff00000000ff00ff00000000ffff00000000ffffff" lib "engine" @@ -1111,7 +1112,7 @@ { type "pattern" sym "_Z20VProfRecord_Snapshotv" - seg ".text" + seg "text" seek "a19cf3651083f801750ab9a0d96510e9dcfaffff83f8027517803dc4f3651000750e6a006affb9a0d96510e8c0f1ffffc3" mask "ff00000000ffffffffffff00000000ff00000000ffffffffffffff00000000ffffffffffffffff00000000ff00000000ff" lib "engine" @@ -1121,7 +1122,7 @@ { type "pattern" sym "_Z23VProfRecord_StartOrStopv" - seg ".text" + seg "text" seek "803d04913c100074288b0d60052f10ff810c10000083b90c10000001750c81c118100000ff1544062f10c60504913c1000803d05913c100074218b0d60052f10ff890c100000750c81c118100000ff1540062f10c60505913c1000c3" mask "ffff00000000ffffffffff00000000ffffffffffffffffffffffffffffffffffffffffffffff00000000ffff00000000ffffff00000000ffffffffff00000000ffffffffffffffffffffffffffffffff00000000ffff00000000ffff" lib "engine" @@ -1166,7 +1167,7 @@ { type "pattern" sym "_Z14V_vsnprintfRetPciPKcS_Pb" - seg ".text" + seg "text" seek "558bec568b750c57ff75148b7d08ff75105657e8003601008b551883c41085d2741385c078083bc67d0433c9eb05b901000000880a85c078043bc67c07c64437ff008bc65f5e5dc3" mask "ffffffffffffffffffffffffffffffffffffffff00000000ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff" } @@ -1543,6 +1544,208 @@ type "sym" sym "_ZN19CTFSniperRifleDecap24SniperRifleChargeRateModEv" } + + "MapEntity_ParseAllEntities" + { + type "sym" + sym "_Z26MapEntity_ParseAllEntitiesPKcP16IMapEntityFilterb" + } + "MapEntity_ParseEntity" + { + type "sym" + sym "_Z21MapEntity_ParseEntityRP11CBaseEntityPKcP16IMapEntityFilter" + } + "MapEntity_ParseToken" + { + type "sym" + sym "_Z20MapEntity_ParseTokenPKcPc" + } + + "CTFBotProxy::CTFBotProxy [C1]" + { + type "sym" + sym "_ZN11CTFBotProxyC1Ev" + } + + "CTFKnife::CanPerformBackstabAgainstTarget" + { + type "sym" + sym "_ZN8CTFKnife31CanPerformBackstabAgainstTargetEP9CTFPlayer" + } + + "CTFKnife::IsBehindAndFacingTarget" + { + type "sym" + sym "_ZN8CTFKnife23IsBehindAndFacingTargetEP9CTFPlayer" + } + + "CTFWeaponBaseMelee::DoSwingTrace" + { + type "sym" + sym "_ZN18CTFWeaponBaseMelee12DoSwingTraceER10CGameTrace" + } + + "CTFGameRules::DropHalloweenSoulPack" + { + type "sym" + sym "_ZN12CTFGameRules21DropHalloweenSoulPackEiRK6VectorP11CBaseEntityi" + } + + "SVC_CmdKeyValues::Process" + { + type "func knownvtidx" + sym "_ZN16SVC_CmdKeyValues7ProcessEv" + vtable ".?AVSVC_CmdKeyValues@@" + idx "0x03" + lib "engine" + } + + "CBaseTrigger::PassesTriggerFilters" + { + type "sym regex" + sym ".*CBaseTrigger.*PassesTriggerFilters.*" + } + + "CL_TakeSnapshotAndSwap" + { + type "func ebpprologue unistr" + sym "_Z22CL_TakeSnapshotAndSwapv" + unistr "CL_TakeSnapshotAndSwap" + lib "engine" + } + + "CVideoMode_Common::TakeSnapshotTGA" + { + type "func ebpprologue nonunistr knownvtidx" + sym "_ZN17CVideoMode_Common15TakeSnapshotTGAEPKc" + str "Couldn't write bitmap data snapshot to file %s.\n" + vtable ".?AVCVideoMode_Common@@" + idx "0x18" + lib "engine" + } + + "CBaseCombatWeapon::HasAmmo" + { + type "sym" + sym "_ZN17CBaseCombatWeapon7HasAmmoEv" + } + + "CObjectSapper::IsValidRoboSapperTarget" + { + type "sym" + sym "_ZN13CObjectSapper23IsValidRoboSapperTargetEP9CTFPlayer" + } + + "CObjectSapper::ApplyRoboSapper" + { + type "sym" + sym "_ZN13CObjectSapper15ApplyRoboSapperEP9CTFPlayerfi" + } + + "CObjectSapper::ApplyRoboSapperEffects" + { + type "sym" + sym "_ZN13CObjectSapper22ApplyRoboSapperEffectsEP9CTFPlayerf" + } + + "CTFSniperRifle::CreateSniperDot" + { + type "sym" + sym "_ZN14CTFSniperRifle15CreateSniperDotEv" + } + + "CTFSniperRifle::CanFireCriticalShot" + { + type "sym" + sym "_ZN14CTFSniperRifle19CanFireCriticalShotEb" + } + + "CTFWeaponBase::CanFireCriticalShot" + { + type "sym" + sym "_ZN13CTFWeaponBase19CanFireCriticalShotEb" + } + + "CTFProjectile_Arrow::StrikeTarget" + { + type "sym" + sym "_ZN19CTFProjectile_Arrow12StrikeTargetEP13mstudiobbox_tP11CBaseEntity" + } + + "CTFGameRules::IsPVEModeControlled" + { + type "sym" + sym "_ZNK12CTFGameRules19IsPVEModeControlledEP11CBaseEntity" + } + + "CBaseEntity::ChangeTeam" + { + type "sym" + sym "_ZN11CBaseEntity10ChangeTeamEi" + } + + "SendProxy_LengthTable" + { + type "sym" + sym "_Z21SendProxy_LengthTablePK8SendPropPKvS3_P20CSendProxyRecipientsi" + } + + "CBaseEntity::SetModelIndexOverride" + { + type "sym" + sym "_ZN11CBaseEntity21SetModelIndexOverrideEii" + } + + "CBaseEntity::SetModelIndex" + { + type "sym" + sym "_ZN11CBaseEntity13SetModelIndexEi" + } + + "CBaseEntity::GetModelName" + { + type "sym" + sym "_ZNK11CBaseEntity12GetModelNameEv" + } + + "Con_ColorPrint" + { + type "sym" + sym "_Z15Con_ColorPrintfRK5ColorPKcz" + lib "engine" + } + + "AllocPooledString" + { + type "sym" + sym "_Z17AllocPooledStringPKc" + } + + "CEventQueue::AddEvent [EventQueuePrioritizedEvent_t *]" + { + type "sym" + sym "_ZN11CEventQueue8AddEventEP28EventQueuePrioritizedEvent_t" + } + + "CEventQueue::ServiceEvents" + { + type "sym" + sym "_ZN11CEventQueue13ServiceEventsEv" + } + + "CBaseEntity::GetDataDescMap" + { + type "func knownvtidx" + sym "_ZN11CBaseEntity14GetDataDescMapEv" + vtable ".?AVCBaseEntity@@" + idx "0x0b" + } + + "CUpgrades::UpgradeTouch" + { + type "sym" + sym "_ZN9CUpgrades12UpgradeTouchEP11CBaseEntity" + } } } } diff --git a/gamedata/sigsegv/tfbot_behavior.txt b/gamedata/sigsegv/tfbot_behavior.txt index 5c49c126..eaee838f 100644 --- a/gamedata/sigsegv/tfbot_behavior.txt +++ b/gamedata/sigsegv/tfbot_behavior.txt @@ -268,6 +268,11 @@ type "sym" sym "_ZN21CTFBotTacticalMonitor22InitialContainedActionEP6CTFBot" } + "CTFBotTacticalMonitor::OnNavAreaChanged" + { + type "sym" + sym "_ZN21CTFBotTacticalMonitor16OnNavAreaChangedEP6CTFBotP8CNavAreaS3_" + } "CTFBotSpyAttack::Update" { @@ -281,6 +286,11 @@ sym "_ZN25CTFBotStickybombSentrygun6UpdateEP6CTFBotf" } + "CTFBotFetchFlag::CTFBotFetchFlag [C1]" + { + type "sym" + sym "_ZN15CTFBotFetchFlagC1Eb" + } "CTFBotFetchFlag::OnStart" { type "sym" @@ -310,11 +320,22 @@ sym "_ZN12CTFBotAttackC1Ev" } + "CTFBotPushToCapturePoint::CTFBotPushToCapturePoint [C1]" + { + type "sym" + sym "_ZN24CTFBotPushToCapturePointC1EP6ActionI6CTFBotE" + } "CTFBotPushToCapturePoint::Update" { type "sym" sym "_ZN24CTFBotPushToCapturePoint6UpdateEP6CTFBotf" } + + "CTFBotDead::OnStart" + { + type "sym" + sym "_ZN10CTFBotDead7OnStartEP6CTFBotP6ActionIS0_E" + } } } } diff --git a/gamedata/sigsegv/tfplayer.txt b/gamedata/sigsegv/tfplayer.txt index 0a6550e6..d1134648 100644 --- a/gamedata/sigsegv/tfplayer.txt +++ b/gamedata/sigsegv/tfplayer.txt @@ -301,6 +301,12 @@ sym "_ZN9CTFPlayer19CreateRagdollEntityEbbbbbbbbib" } + "CTFPlayer::ClientCommand" + { + type "sym" + sym "_ZN9CTFPlayer13ClientCommandERK8CCommand" + } + "CTFPlayerShared::AddCond" { type "sym" @@ -346,29 +352,44 @@ type "sym" sym "_ZN15CTFPlayerShared12SetRageMeterEf" } - - "CTFPlayerClassShared::SetCustomModel" + "CTFPlayerShared::GetConditionsBits" + { + type "sym regex" + sym "_ZNK15CTFPlayerShared17GetConditionsBitsER7CBitVecILi[[:digit:]]{3}EE" + } + "CTFPlayerShared::GetConditionDuration" { type "sym" - sym "_ZN20CTFPlayerClassShared14SetCustomModelEPKcb" + sym "_ZNK15CTFPlayerShared20GetConditionDurationE7ETFCond" } - - "CTFPlayerSharedUtils::GetEconItemViewByLoadoutSlot" + "CTFPlayerShared::GetConditionProvider" { type "sym" - sym "_ZN20CTFPlayerSharedUtils28GetEconItemViewByLoadoutSlotEP9CTFPlayeriPP11CEconEntity" + sym "_ZNK15CTFPlayerShared20GetConditionProviderE7ETFCond" } - "GetTFConditionName" + "CTFPlayerClassShared::SetCustomModel" { type "sym" - sym "_Z18GetTFConditionName7ETFCond" + sym "_ZN20CTFPlayerClassShared14SetCustomModelEPKcb" } - "GetTFConditionFromName" + + "CTFPlayerSharedUtils::GetEconItemViewByLoadoutSlot" { type "sym" - sym "_Z22GetTFConditionFromNamePKc" + sym "_ZN20CTFPlayerSharedUtils28GetEconItemViewByLoadoutSlotEP9CTFPlayeriPP11CEconEntity" } + + // "GetTFConditionName" + // { + // type "sym" + // sym "_Z18GetTFConditionName7ETFCond" + // } + // "GetTFConditionFromName" + // { + // type "sym" + // sym "_Z22GetTFConditionFromNamePKc" + // } } } } diff --git a/src/addr/addr.cpp b/src/addr/addr.cpp index 1ad3a865..c8b6afe1 100644 --- a/src/addr/addr.cpp +++ b/src/addr/addr.cpp @@ -125,19 +125,19 @@ void AddrManager::CC_ListAddrs(const CCommand& cmd) } std::sort(addrs_sorted.begin(), addrs_sorted.end(), CompareAddrsForSorting); - size_t max_libname_len = LibMgr::MaxStringLen(); + size_t max_libname_len = LibMgr::Lib_MaxStringLen(); MAT_SINGLE_THREAD_BLOCK { for (auto addr : addrs_sorted) { switch (addr->GetState()) { case IAddr::State::INITIAL: - Msg("%-*s %-8s %s\n", max_libname_len, LibMgr::ToString(addr->GetLibrary()), "INITIAL", addr->GetName()); + Msg("%-*s %-8s %s\n", max_libname_len, LibMgr::Lib_ToString(addr->GetLibrary()), "INITIAL", addr->GetName()); break; case IAddr::State::OK: - Msg("%-*s %08x %s\n", max_libname_len, LibMgr::ToString(addr->GetLibrary()), (uintptr_t)addr->GetAddr(), addr->GetName()); + Msg("%-*s %08x %s\n", max_libname_len, LibMgr::Lib_ToString(addr->GetLibrary()), (uintptr_t)addr->GetAddr(), addr->GetName()); break; case IAddr::State::FAIL: - Msg("%-*s %-8s %s\n", max_libname_len, LibMgr::ToString(addr->GetLibrary()), "FAIL", addr->GetName()); + Msg("%-*s %-8s %s\n", max_libname_len, LibMgr::Lib_ToString(addr->GetLibrary()), "FAIL", addr->GetName()); break; } } diff --git a/src/addr/misc.cpp b/src/addr/misc.cpp index 126aac18..a1c6bb0e 100644 --- a/src/addr/misc.cpp +++ b/src/addr/misc.cpp @@ -4,6 +4,7 @@ #include "stub/gamerules.h" #include "util/rtti.h" #include "addr/standard.h" +#include "disasm/disasm.h" static constexpr uint8_t s_Buf_g_pGameRules[] = { @@ -88,11 +89,11 @@ class CAddr_pszWpnEntTranslationList : public IAddr_Sym // +0x24 ptr: "" // +0x28 ptr: "tf_weapon_shotgun_primary" - auto strscan1 = new StrScanner(CLibSegBounds(Library::SERVER, ".rdata"), "tf_weapon_shotgun"); - auto strscan2 = new StrScanner(CLibSegBounds(Library::SERVER, ".rdata"), "tf_weapon_shotgun_soldier"); - auto strscan3 = new StrScanner(CLibSegBounds(Library::SERVER, ".rdata"), "tf_weapon_shotgun_hwg"); - auto strscan4 = new StrScanner(CLibSegBounds(Library::SERVER, ".rdata"), "tf_weapon_shotgun_pyro"); - auto strscan5 = new StrScanner(CLibSegBounds(Library::SERVER, ".rdata"), "tf_weapon_shotgun_primary"); + auto strscan1 = new StrScanner(CLibSegBounds(Library::SERVER, Segment::RODATA), "tf_weapon_shotgun"); + auto strscan2 = new StrScanner(CLibSegBounds(Library::SERVER, Segment::RODATA), "tf_weapon_shotgun_soldier"); + auto strscan3 = new StrScanner(CLibSegBounds(Library::SERVER, Segment::RODATA), "tf_weapon_shotgun_hwg"); + auto strscan4 = new StrScanner(CLibSegBounds(Library::SERVER, Segment::RODATA), "tf_weapon_shotgun_pyro"); + auto strscan5 = new StrScanner(CLibSegBounds(Library::SERVER, Segment::RODATA), "tf_weapon_shotgun_primary"); CMultiScan scan1({ strscan1, strscan2, strscan3, strscan4, strscan5 }); if (!strscan1->ExactlyOneMatch()) { DevMsg("Fail strscan1\n"); return false; } if (!strscan2->ExactlyOneMatch()) { DevMsg("Fail strscan2\n"); return false; } @@ -107,7 +108,7 @@ class CAddr_pszWpnEntTranslationList : public IAddr_Sym mask.SetDword(0x1c, 0xffffffff); seek.SetDword(0x1c, (uint32_t)strscan3->FirstMatch()); mask.SetDword(0x20, 0xffffffff); seek.SetDword(0x20, (uint32_t)strscan4->FirstMatch()); mask.SetDword(0x28, 0xffffffff); seek.SetDword(0x28, (uint32_t)strscan5->FirstMatch()); - CScan scan2(CLibSegBounds(Library::SERVER, ".data"), seek, mask); + CScan scan2(CLibSegBounds(Library::SERVER, Segment::DATA), seek, mask); if (!scan2.ExactlyOneMatch()) { DevMsg("Fail scan2 %u\n", scan2.Matches().size()); return false; } auto match = (const char **)scan2.FirstMatch(); @@ -180,7 +181,7 @@ class CAddr_CTFPlayer_CanBeForcedToLaugh : public IAddr_Sym // DevMsg("g_pGameRules: %08x\n", (uintptr_t)addr_g_pGameRules); // DevMsg("m_bPlayingMannVsMachine: %08x\n", off_CTFGameRules_m_bPlayingMannVsMachine); - CScan scan1(CLibSegBounds(this->GetLibrary(), ".text"), seek, mask); + CScan scan1(CLibSegBounds(this->GetLibrary(), Segment::TEXT), seek, mask); if (!scan1.ExactlyOneMatch()) { DevMsg("Fail scan1 %u\n", scan1.Matches().size()); return false; @@ -296,6 +297,7 @@ class CAddr_IServerGameDLL : public CAddr_InterfaceVFunc CAddr_IServerGameDLL(const std::string& n_func, int vtidx) : CAddr_InterfaceVFunc(&gamedll, "IServerGameDLL", n_func, vtidx) {} }; +static CAddr_IServerGameDLL addr_IServerGameDLL_LevelInit("LevelInit", GetVIdxOfMemberFunc(&IServerGameDLL::LevelInit)); static CAddr_IServerGameDLL addr_IServerGameDLL_GameFrame("GameFrame", GetVIdxOfMemberFunc(&IServerGameDLL::GameFrame)); @@ -338,12 +340,12 @@ class CAddr_CTFBotUseItem_C1 : public IAddr_Sym virtual bool FindAddrWin(uintptr_t& addr) const override { - using VTRefScanner = CTypeScanner; + using VTRefScanner = CTypeScanner; auto p_VT = RTTI::GetVTable(".?AVCTFBotUseItem@@"); auto p_dtor = AddrManager::GetAddr("CTFBotUseItem::~CTFBotUseItem [D2]"); - CScan scan1(CLibSegBounds(this->GetLibrary(), ".text"), p_VT); + CScan scan1(CLibSegBounds(this->GetLibrary(), Segment::TEXT), p_VT); std::vector matches; for (auto match : scan1.Matches()) { @@ -377,7 +379,7 @@ class IAddr_Func_EBPPrologue_UniqueCall : public IAddr_Sym return false; } - CScan scan1(CLibSegBounds(this->GetLibrary(), ".text"), (uint32_t)p_ref); + CScan scan1(CLibSegBounds(this->GetLibrary(), Segment::TEXT), (uint32_t)p_ref); if (!scan1.ExactlyOneMatch()) { DevMsg("IAddr_Func_EBPPrologue_UniqueCall: \"%s\": found %u refs to ostensibly unique func\n", this->GetName(), scan1.Matches().size()); return false; @@ -500,7 +502,7 @@ class CAddr_Client_UserMessages : public IAddr_Sym seek.SetDword(0x08 + 1, (uint32_t)p_str); mask.SetRange(0x0d + 1, 0x04, 0x00); - CScan scan1(CLibSegBounds(Library::CLIENT, ".text"), seek, mask); + CScan scan1(CLibSegBounds(Library::CLIENT, Segment::TEXT), seek, mask); if (!scan1.ExactlyOneMatch()) { DevMsg("%s: %u matches\n", this->GetName(), scan1.Matches().size()); return false; @@ -550,7 +552,7 @@ class CAddr_Client_CUserMessages_Register : public IAddr_Sym seek.SetDword(0x08 + 1, (uint32_t)p_str); mask.SetRange(0x0d + 1, 0x04, 0x00); - CScan scan1(CLibSegBounds(Library::CLIENT, ".text"), seek, mask); + CScan scan1(CLibSegBounds(Library::CLIENT, Segment::TEXT), seek, mask); if (!scan1.ExactlyOneMatch()) { DevMsg("%s: %u matches\n", this->GetName(), scan1.Matches().size()); return false; @@ -602,7 +604,7 @@ class CAddr_Client_CUserMessages_HookMessage : public IAddr_Sym seek.SetDword(0x0b + 1, (uint32_t)p_str); mask.SetRange(0x10 + 1, 0x04, 0x00); - CScan scan1(CLibSegBounds(Library::CLIENT, ".text"), seek, mask); + CScan scan1(CLibSegBounds(Library::CLIENT, Segment::TEXT), seek, mask); if (!scan1.ExactlyOneMatch()) { DevMsg("%s: %u matches\n", this->GetName(), scan1.Matches().size()); return false; @@ -622,7 +624,7 @@ class IAddr_Client_CDebugOverlay : public IAddr_Sym public: virtual bool FindAddrWin(uintptr_t& addr) const override { - using StrRefScanner = CTypeScanner ; + using StrRefScanner = CTypeScanner ; using NearbyPatternScanner = CMaskedScanner; constexpr const char *str = "s_OverlayMutex"; @@ -632,7 +634,7 @@ class IAddr_Client_CDebugOverlay : public IAddr_Sym return false; } - CScan scan1(CLibSegBounds(this->GetLibrary(), ".text"), p_str); + CScan scan1(CLibSegBounds(this->GetLibrary(), Segment::TEXT), p_str); DevMsg("IAddr_Client_CDebugOverlay: \"%s\": found %u preliminary matches\n", this->GetName(), scan1.Matches().size()); for (auto match : scan1.Matches()) { DevMsg(" %08x\n", (uintptr_t)match); @@ -772,7 +774,7 @@ class CAddr_RCONClient : public IAddr_Sym mask.SetRange(0x30 + 1, 4, 0x00); mask.SetRange(0x35 + 1, 4, 0x00); - CScan scan1(CLibSegBounds(Library::ENGINE, ".text"), seek, mask); + CScan scan1(CLibSegBounds(Library::ENGINE, Segment::TEXT), seek, mask); if (!scan1.ExactlyOneMatch()) { DevMsg("%s: %u matches\n", this->GetName(), scan1.Matches().size()); return false; @@ -864,3 +866,126 @@ class CAddr_CAttributeManager_AttribHookValue : public IAddr_Func_EBPPrologue_VP static CAddr_CAttributeManager_AttribHookValue addr_CAttributeManager_AttribHookValue_int ("CAttributeManager::AttribHookValue", "_ZN17CAttributeManager15AttribHookValueIiEET_S1_PKcPK11CBaseEntityP10CUtlVectorIPS4_10CUtlMemoryIS8_iEEb", 0x24); static CAddr_CAttributeManager_AttribHookValue addr_CAttributeManager_AttribHookValue_float("CAttributeManager::AttribHookValue", "_ZN17CAttributeManager15AttribHookValueIfEET_S1_PKcPK11CBaseEntityP10CUtlVectorIPS4_10CUtlMemoryIS8_iEEb", 0x28); #endif + + +class CAddr_Client_g_pClientMode : public IAddr_Sym +{ +public: + CAddr_Client_g_pClientMode() + { + this->SetLibrary(Library::CLIENT); + } + + virtual const char *GetName() const override { return "g_pClientMode"; } + virtual const char *GetSymbol() const override { return "g_pClientMode"; } + + virtual bool FindAddrWin(uintptr_t& addr) const override + { + using MyScanner = CMaskedScanner; + + void *p_func = AddrManager::GetAddr("[client] CTFModeManager::LevelInit"); + if (p_func == nullptr) { + DevMsg("CAddr_Client_g_pClientMode: \"%s\": failed to find parent function\n", this->GetName()); + return false; + } + + constexpr uint8_t buf[] = { + 0x55, // +0000 push ebp + 0x8b, 0xec, // +0001 mov ebp,esp + 0x8b, 0x0d, 0x00, 0x00, 0x00, 0x00, // +0003 mov ecx,0xVVVVVVVV + 0x83, 0xec, 0x00, // +0009 sub esp,0xXX + 0x8b, 0x01, // +000C mov eax,[ecx] + 0xff, 0x75, 0x08, // +000E push [ebp+0x8] + 0xff, 0x50, 0x58, // +0011 call dword ptr [eax+0x58] + }; + + ByteBuf seek(sizeof(buf)); + ByteBuf mask(sizeof(buf)); + seek.CopyFrom(buf); + mask.SetAll(0xff); + + mask.SetRange(0x03 + 2, 0x04, 0x00); + mask.SetRange(0x09 + 2, 0x01, 0x00); + + CScan scan1(CAddrOffBounds(p_func, sizeof(buf)), seek, mask); + if (!scan1.ExactlyOneMatch()) { + DevMsg("%s: %u matches\n", this->GetName(), scan1.Matches().size()); + return false; + } + + addr = **(uintptr_t **)((uintptr_t)scan1.FirstMatch() + 0x05); + return true; + } +}; +static CAddr_Client_g_pClientMode addr_g_pClientMode; + + +class CAddr_TGAWriter_WriteToBuffer : public IAddr_Sym +{ +public: + CAddr_TGAWriter_WriteToBuffer() + { + this->SetLibrary(Library::ENGINE); + } + + virtual const char *GetName() const override { return "TGAWriter::WriteToBuffer"; } + virtual const char *GetSymbol() const override { return "_ZN9TGAWriter13WriteToBufferEPhR10CUtlBufferii11ImageFormatS3_"; } + + virtual bool FindAddrWin(uintptr_t& addr) + { + void *p_func = AddrManager::GetAddr("CVideoMode_Common::TakeSnapshotTGA"); + if (p_func == nullptr) { + DevMsg("CAddr_TGAWriter_WriteToBuffer: \"%s\": failed to find parent function\n", this->GetName()); + return false; + } + + #warning TODO TODO TODO + + /* + Disassembler dasm; + if (dasm.HasError()) { + DevMsg("CAddr_TGAWriter_WriteToBuffer: \"%s\": error in Disassembler ctor: \"%s\"\n", this->GetName(), dasm.ErrorStr()); + return false; + } + + auto insns = dasm.Disasm(p_func, 0x120); + if (dasm.HasError()) { + DevMsg("CAddr_TGAWriter_WriteToBuffer: \"%s\": error in Disassembler Disasm: \"%s\"\n", this->GetName(), dasm.ErrorStr()); + return false; + } + + for ()*/ + + } +}; +static CAddr_TGAWriter_WriteToBuffer addr_TGAWriter_WriteToBuffer; + + +class CAddr_g_aConditionNames : public IAddr_Sym +{ +public: + virtual const char *GetName() const override { return "g_aConditionNames"; } + virtual const char *GetSymbol() const override { return "_ZL17g_aConditionNames"; } + + virtual bool FindAddrWin(uintptr_t& addr) const override + { + using StrRefScanner = CAlignedTypeScanner; + + constexpr char str[] = "TF_COND_AIMING"; + const char *p_str = Scan::FindUniqueConstStr(this->GetLibrary(), str); + if (p_str == nullptr) { + DevMsg("CAddr_g_aConditionNames: \"%s\": failed to find string \"%s\"\n", this->GetName(), str); + return false; + } + + CScan scan1(CLibSegBounds(this->GetLibrary(), Segment::RODATA), p_str); + if (!scan1.ExactlyOneMatch()) { + DevMsg("CAddr_g_aConditionNames: \"%s\": %u string ref matches\n", this->GetName(), scan1.Matches().size()); + return false; + } + + addr = (uintptr_t)scan1.FirstMatch(); + return true; + } +}; +static CAddr_g_aConditionNames addr_g_aConditionNames; diff --git a/src/addr/prescan.cpp b/src/addr/prescan.cpp index 283bdc48..66f5ed03 100644 --- a/src/addr/prescan.cpp +++ b/src/addr/prescan.cpp @@ -9,7 +9,7 @@ void PreScan::DoScans() #if defined _WINDOWS DevMsg("PreScan::DoScans\n"); -// s_WinRTTI_server = new CSingleScan(ScanDir::FORWARD, CLibSegBounds(Library::SERVER, ".data"), 1, new CStringPrefixScanner(ScanResults::ALL, ".?AV")); +// s_WinRTTI_server = new CSingleScan(ScanDir::FORWARD, CLibSegBounds(Library::SERVER, Segment::DATA), 1, new CStringPrefixScanner(ScanResults::ALL, ".?AV")); // // for (auto match : s_WinRTTI_server->Matches()) { // DevMsg("[PreScan] 0x%08x \"%s\"\n", (uintptr_t)match, (const char *)match); diff --git a/src/addr/standard.cpp b/src/addr/standard.cpp index 26aa348a..98f3b21a 100644 --- a/src/addr/standard.cpp +++ b/src/addr/standard.cpp @@ -1,4 +1,5 @@ #include "addr/standard.h" +#include "prop.h" #include "mem/scan.h" #include "util/rtti.h" @@ -15,6 +16,27 @@ bool IAddr_Sym::FindAddrLinux(uintptr_t& addr) const } +bool CAddr_Sym_Regex::FindAddrLinux(uintptr_t& addr) const +{ + float t1 = Plat_FloatTime(); + auto results = LibMgr::FindSymRegex(this->GetLibrary(), this->GetSymbol()); + float t2 = Plat_FloatTime(); + ConColorMsg(Color(0xff, 0x00, 0xff, 0xff), "Regex lookup for \"%s\" \"%s\": %.3f ms\n", + this->GetName(), this->GetSymbol(), (t2 - t1) * 1000.0f); + + bool success = std::get<0>(results); + std::string& name = std::get<1>(results); + void *sym_addr = std::get<2>(results); + + if (!success || sym_addr == nullptr) { + return false; + } + + addr = (uintptr_t)sym_addr; + return true; +} + + bool IAddr_FixedAddr::FindAddrWin(uintptr_t& addr) const { if (engine->GetServerVersion() != this->GetServerVersion()) { @@ -22,8 +44,7 @@ bool IAddr_FixedAddr::FindAddrWin(uintptr_t& addr) const return false; } - const LibInfo& info = LibMgr::GetInfo(this->GetLibrary()); - addr = info.baseaddr + this->GetAddress(); + addr = LibMgr::GetInfo(this->GetLibrary()).BaseAddr() + this->GetAddress(); return true; } @@ -35,8 +56,8 @@ bool IAddr_DataDescMap::FindAddrWin(uintptr_t& addr) const uint8_t buf[6]; }; - using ClassNameRefScanner = CTypeScanner; - using GetDataDescMapScanner = CTypeScanner; + using ClassNameRefScanner = CTypeScanner; + using GetDataDescMapScanner = CTypeScanner; const char *p_str = Scan::FindUniqueConstStr(this->GetLibrary(), this->GetClassName()); if (p_str == nullptr) { @@ -44,7 +65,7 @@ bool IAddr_DataDescMap::FindAddrWin(uintptr_t& addr) const return false; } - CScan scan1(CLibSegBounds(this->GetLibrary(), ".data"), p_str); + CScan scan1(CLibSegBounds(this->GetLibrary(), Segment::DATA), p_str); std::vector scanners; for (auto match : scan1.Matches()) { GetDataDescMap gddm; @@ -52,7 +73,7 @@ bool IAddr_DataDescMap::FindAddrWin(uintptr_t& addr) const *(uint32_t *)(&gddm.buf[0x01]) = (uint32_t)match - offsetof(datamap_t, dataClassName); gddm.buf[0x05] = 0xc3; // ret - scanners.push_back(new GetDataDescMapScanner(CLibSegBounds(this->GetLibrary(), ".text"), gddm)); + scanners.push_back(new GetDataDescMapScanner(CLibSegBounds(this->GetLibrary(), Segment::TEXT), gddm)); } CMultiScan scan2(scanners); @@ -88,8 +109,9 @@ bool IAddr_Func_KnownVTIdx::FindAddrWin(uintptr_t& addr) const bool IAddr_Func_DataMap_VThunk::FindAddrWin(uintptr_t& addr) const { auto p_DM = (const datamap_t *)AddrManager::GetAddr(this->GetDataMapName()); +// auto p_DM = Prop::GetDataMapByClassname(this->GetEntClassname()); if (p_DM == nullptr) { - DevMsg("IAddr_Func_DataMap_VThunk: \"%s\": no addr for datamap\n", this->GetName()); + DevMsg("IAddr_Func_DataMap_VThunk: \"%s\": can't find datamap: %s\n", this->GetName(), this->GetDataMapName()); return false; } @@ -144,7 +166,7 @@ bool IAddr_Func_DataMap_VThunk::FindAddrWin(uintptr_t& addr) const bool IAddr_Func_EBPPrologue_UniqueRef::FindAddrWin(uintptr_t& addr) const { - using SymRefScanner = CTypeScanner; + using SymRefScanner = CTypeScanner; auto p_ref = AddrManager::GetAddr(this->GetUniqueSymbol()); if (p_ref == nullptr) { @@ -152,7 +174,7 @@ bool IAddr_Func_EBPPrologue_UniqueRef::FindAddrWin(uintptr_t& addr) const return false; } - CScan scan1(CLibSegBounds(this->GetLibrary(), ".text"), p_ref); + CScan scan1(CLibSegBounds(this->GetLibrary(), Segment::TEXT), p_ref); if (!scan1.ExactlyOneMatch()) { DevMsg("IAddr_Func_EBPPrologue_UniqueRef: \"%s\": found %u refs to ostensibly unique symbol\n", this->GetName(), scan1.Matches().size()); return false; @@ -172,7 +194,7 @@ bool IAddr_Func_EBPPrologue_UniqueRef::FindAddrWin(uintptr_t& addr) const bool IAddr_Func_EBPPrologue_UniqueStr::FindAddrWin(uintptr_t& addr) const { - using StrRefScanner = CTypeScanner; + using StrRefScanner = CTypeScanner; const char *p_str = Scan::FindUniqueConstStr(this->GetLibrary(), this->GetUniqueStr()); if (p_str == nullptr) { @@ -180,7 +202,7 @@ bool IAddr_Func_EBPPrologue_UniqueStr::FindAddrWin(uintptr_t& addr) const return false; } - CScan scan1(CLibSegBounds(this->GetLibrary(), ".text"), p_str); + CScan scan1(CLibSegBounds(this->GetLibrary(), Segment::TEXT), p_str); if (!scan1.ExactlyOneMatch()) { DevMsg("IAddr_Func_EBPPrologue_UniqueStr: \"%s\": found %u refs to ostensibly unique string\n", this->GetName(), scan1.Matches().size()); return false; @@ -200,7 +222,7 @@ bool IAddr_Func_EBPPrologue_UniqueStr::FindAddrWin(uintptr_t& addr) const bool IAddr_Func_EBPPrologue_UniqueStr_KnownVTIdx::FindAddrWin(uintptr_t& addr) const { - using StrRefScanner = CTypeScanner; + using StrRefScanner = CTypeScanner; const char *p_str = Scan::FindUniqueConstStr(this->GetLibrary(), this->GetUniqueStr()); if (p_str == nullptr) { @@ -208,7 +230,7 @@ bool IAddr_Func_EBPPrologue_UniqueStr_KnownVTIdx::FindAddrWin(uintptr_t& addr) c return false; } - CScan scan1(CLibSegBounds(this->GetLibrary(), ".text"), p_str); + CScan scan1(CLibSegBounds(this->GetLibrary(), Segment::TEXT), p_str); if (!scan1.ExactlyOneMatch()) { DevMsg("IAddr_Func_EBPPrologue_UniqueStr_KnownVTIdx: \"%s\": found %u refs to ostensibly unique string\n", this->GetName(), scan1.Matches().size()); return false; @@ -238,6 +260,46 @@ bool IAddr_Func_EBPPrologue_UniqueStr_KnownVTIdx::FindAddrWin(uintptr_t& addr) c } +bool IAddr_Func_EBPPrologue_NonUniqueStr_KnownVTIdx::FindAddrWin(uintptr_t& addr) const +{ + using StrRefScanner = CTypeScanner; + + const char *p_str = Scan::FindUniqueConstStr(this->GetLibrary(), this->GetStr()); + if (p_str == nullptr) { + DevMsg("IAddr_Func_EBPPrologue_NonUniqueStr_KnownVTIdx: \"%s\": failed to find string\n", this->GetName()); + return false; + } + + CScan scan1(CLibSegBounds(this->GetLibrary(), Segment::TEXT), p_str); + if (scan1.Matches().empty()) { + DevMsg("IAddr_Func_EBPPrologue_NonUniqueStr_KnownVTIdx: \"%s\": no refs to string\n", this->GetName()); + return false; + } + + auto p_VT = RTTI::GetVTable(this->GetVTableName()); + if (p_VT == nullptr) { + DevMsg("IAddr_Func_EBPPrologue_NonUniqueStr_KnownVTIdx: \"%s\": no addr for vtable\n", this->GetName()); + return false; + } + + for (auto p_in_func : scan1.Matches()) { + auto p_func = Scan::FindFuncPrologue(p_in_func); + if (p_func == nullptr) { + continue; + } + + auto vfptr = p_VT[this->GetVTableIndex()]; + if (vfptr == p_func) { + addr = (uintptr_t)p_func; + return true; + } + } + + DevMsg("IAddr_Func_EBPPrologue_NonUniqueStr_KnownVTIdx: \"%s\": found %u string refs, but none matched vtable entry\n", this->GetName(), scan1.Matches().size()); + return false; +} + + bool IAddr_Func_EBPPrologue_VProf::FindAddrWin(uintptr_t& addr) const { #if defined __GNUC__ @@ -263,7 +325,7 @@ bool IAddr_Func_EBPPrologue_VProf::FindAddrWin(uintptr_t& addr) const }; *(const char **)(vprof + 0x01) = p_name; *(const char **)(vprof + 0x06) = p_group; - CScan scan1(CLibSegBounds(this->GetLibrary(), ".text"), (const void *)vprof, sizeof(vprof)); + CScan scan1(CLibSegBounds(this->GetLibrary(), Segment::TEXT), (const void *)vprof, sizeof(vprof)); if (!scan1.ExactlyOneMatch()) { DevMsg("IAddr_Func_EBPPrologue_VProf: \"%s\": could not locate VPROF_BUDGET\n", this->GetName()); return false; @@ -284,7 +346,7 @@ bool IAddr_Func_EBPPrologue_VProf::FindAddrWin(uintptr_t& addr) const #if 0 bool IAddr_Func_EBPPrologue_UniqueConVar::FindAddrWin(uintptr_t& addr) const { - using ConVarRefScanner = CTypeScanner; + using ConVarRefScanner = CTypeScanner; ConVarRef cvref(this->GetConVarName()); if (!cvref.IsValid()) { @@ -317,7 +379,7 @@ bool IAddr_Func_EBPPrologue_UniqueConVar::FindAddrWin(uintptr_t& addr) const // return false; // } // -// CScan scan1(CLibSegBounds(this->GetLibrary(), ".text"), p_str); +// CScan scan1(CLibSegBounds(this->GetLibrary(), Segment::TEXT), p_str); // if (!scan1.ExactlyOneMatch()) { // DevMsg("IAddr_Func_EBPPrologue_UniqueStr: \"%s\": found %u refs to ostensibly unique string\n", this->GetName(), scan1.Matches().size()); // return false; @@ -381,7 +443,7 @@ bool IAddr_Pattern::FindAddrWin(uintptr_t& addr) const mask[i] = std::stoi(buf, nullptr, 0x10); } - CScan scan1(CLibSegBounds(this->GetLibrary(), this->GetSegment()), seek, mask); + CScan scan1(CLibSegBounds(this->GetLibrary(), LibMgr::Seg_FromString(this->GetSegment())), seek, mask); if (!scan1.ExactlyOneMatch()) { DevMsg("IAddr_Pattern: \"%s\": found %u pattern matches\n", this->GetName(), scan1.Matches().size()); return false; @@ -415,3 +477,17 @@ void CAddr_Pattern::ProcessStrings() buf_mask.push_back('\0'); this->m_strMask = buf_mask.data(); } + + +bool IAddr_ConCommandBase::FindAddrLinux(uintptr_t& addr) const +{ + const char *name = this->GetConName(); + bool is_command = this->IsCommand(); + + #warning FINISH THIS! + #warning FINISH THIS! + #warning FINISH THIS! + #warning FINISH THIS! + #warning FINISH THIS! + #warning FINISH THIS! +} diff --git a/src/addr/standard.h b/src/addr/standard.h index cc697141..11c2a0ef 100644 --- a/src/addr/standard.h +++ b/src/addr/standard.h @@ -29,6 +29,16 @@ class CAddr_Sym : public IAddr_Sym }; +class CAddr_Sym_Regex : public CAddr_Sym +{ +public: + CAddr_Sym_Regex(const std::string& name, const std::string& sym) : + CAddr_Sym(name, sym) {} + + virtual bool FindAddrLinux(uintptr_t& addr) const override; +}; + + #if 0 #define _ADDR_SYM(name, namestr, sym) \ class Addr_Base__##name : public IAddr_Sym \ @@ -264,6 +274,43 @@ class CAddr_Func_EBPPrologue_UniqueStr_KnownVTIdx : public IAddr_Func_EBPPrologu }; +/* address finder for functions with these traits: + * 1. func body starts with "push ebp; mov ebp,esp" + * 2. func body contains a string reference which may not be unique + * 3. func is virtual and has a confidently known vtable index + */ +class IAddr_Func_EBPPrologue_NonUniqueStr_KnownVTIdx : public IAddr_Sym +{ +public: + virtual bool FindAddrWin(uintptr_t& addr) const override; + +protected: + virtual const char *GetStr() const = 0; + virtual const char *GetVTableName() const = 0; + virtual int GetVTableIndex() const = 0; +}; + +class CAddr_Func_EBPPrologue_NonUniqueStr_KnownVTIdx : public IAddr_Func_EBPPrologue_NonUniqueStr_KnownVTIdx +{ +public: + CAddr_Func_EBPPrologue_NonUniqueStr_KnownVTIdx(const std::string& name, const std::string& sym, const std::string& str, const std::string& vt_name, int vt_idx) : + m_strName(name), m_strSymbol(sym), m_strStr(str), m_strVTName(vt_name), m_iVTIndex(vt_idx) {} + + virtual const char *GetName() const override { return this->m_strName.c_str(); } + virtual const char *GetSymbol() const override { return this->m_strSymbol.c_str(); } + virtual const char *GetStr() const override { return this->m_strStr.c_str(); } + virtual const char *GetVTableName() const override { return this->m_strVTName.c_str(); } + virtual int GetVTableIndex() const override { return this->m_iVTIndex; } + +private: + std::string m_strName; + std::string m_strSymbol; + std::string m_strStr; + std::string m_strVTName; + int m_iVTIndex; +}; + + class IAddr_Func_EBPPrologue_VProf : public IAddr_Sym { public: @@ -413,4 +460,32 @@ class CAddr_Pattern : public IAddr_Pattern }; +class IAddr_ConCommandBase : public IAddr +{ +public: + virtual bool FindAddrLinux(uintptr_t& addr) const override; + virtual bool FindAddrWin(uintptr_t& addr) const override { return this->FindAddrLinux(addr); } + +protected: + virtual const char *GetConName() const = 0; + virtual bool IsCommand() const = 0; +}; + +class CAddr_ConCommandBase : public IAddr_ConCommandBase +{ +public: + CAddr_ConCommandBase(const std::string& name, const std::string& con_name, bool is_command) : + m_strName(name), m_strConName(con_name), m_bIsCommand(is_command) {} + + virtual const char *GetName() const override { return this->m_strName.c_str(); } + virtual const char *GetConName() const override { return this->m_strConName.c_str(); } + virtual bool IsCommand() const override { return this->m_bIsCommand; } + +private: + std::string m_strName; + std::string m_strConName; + bool m_bIsCommand; +}; + + #endif diff --git a/src/common.h b/src/common.h index 1902d26e..29158fe2 100644 --- a/src/common.h +++ b/src/common.h @@ -33,7 +33,14 @@ class ISoundEmitterSystemBase; class IMaterialSystem; namespace vgui { + class IVGui; + class IInput; + class IPanel; class ISchemeManager; + class ISystem; + class ILocalize; + class IInputInternal; + class ISurface; } class IMatSystemSurface; @@ -61,6 +68,8 @@ namespace SourceMod { class IExtensionManager; } +class IClientMode; + extern IVEngineServer *engine; extern IServerGameDLL *gamedll; @@ -87,7 +96,13 @@ extern ISoundEmitterSystemBase *soundemitterbase; extern IMaterialSystem *g_pMaterialSystem; +extern vgui::IVGui *g_pVGui; +extern vgui::IInput *g_pVGuiInput; +extern vgui::IPanel *g_pVGuiPanel; extern vgui::ISchemeManager *g_pVGuiSchemeManager; +extern vgui::ISystem *g_pVGuiSystem; +extern vgui::ILocalize *g_pVGuiLocalize; +extern vgui::IInputInternal *g_pVGuiInputInternal; extern vgui::ISurface *g_pVGuiSurface; extern IMatSystemSurface *g_pMatSystemSurface; @@ -111,6 +126,8 @@ extern IMDLCache *mdlcache; extern SourcePawn::ISourcePawnEngine *g_pSourcePawn; extern SourceMod::IExtensionManager *smexts; +extern IClientMode *g_pClientMode; + /* C++ standard library */ #include @@ -195,6 +212,10 @@ using namespace std::literals; #include +/* LodePNG */ +#include + + /* Capstone */ #include @@ -278,7 +299,13 @@ class IVideoRecorder; #include #include #include +#include +#include +#include #include +#include +#include +#include #include #include #include diff --git a/src/disasm/disasm.cpp b/src/disasm/disasm.cpp index 24aa9738..c3e3027e 100644 --- a/src/disasm/disasm.cpp +++ b/src/disasm/disasm.cpp @@ -1,448 +1,4 @@ #include "disasm/disasm.h" -#include "library.h" -#include "util/prof.h" -Disasm g_Disasm; - -#if 0 -#include -#include -namespace Disasm_diStorm -{ - void Test() - { - DevMsg("Disasm_diStorm::Test BEGIN\n"); - - LibInfo info = LibMgr::GetInfo(Library::SERVER); - - uint32_t lib_base = info.baseaddr; - - uint32_t text_off = info.segs[".text"].off; - uint32_t text_len = info.segs[".text"].len; - - - // _DInst: 64 bytes per instruction - - // all instructions: - // 2,305,482 - // 148 MB - - // just flow control: - // 706,387 - // 45 MB - - - { - _CodeInfo codeinfo; - codeinfo.codeOffset = lib_base + text_off; - codeinfo.code = (const uint8_t *)(lib_base + text_off); - codeinfo.codeLen = text_len; - codeinfo.dt = Decode32Bits; - codeinfo.features = DF_NONE; - - constexpr unsigned int maxInstructions = 3000000; - DevMsg(" sizeof(OFFSET_INTEGER) = %u\n", sizeof(OFFSET_INTEGER)); - DevMsg(" sizeof(_DInst) = %u\n", sizeof(_DInst)); - DevMsg(" calling malloc(%u)\n", sizeof(_DInst) * maxInstructions); - _DInst *instrs = (_DInst *)malloc(sizeof(_DInst) * maxInstructions); - DevMsg(" malloc returned: %08x\n", (uintptr_t)instrs); - - Prof::Begin(); - unsigned int usedInstructionsCount = 0; - _DecodeResult result = distorm_decompose(&codeinfo, instrs, maxInstructions, &usedInstructionsCount); - Prof::End("diStorm all instrs"); - - DevMsg(" result: %d\n", result); - DevMsg(" usedInstructionsCount: %u\n", usedInstructionsCount); - - FILE *file = fopen("D:\\server.txt", "w"); - if (file != nullptr) { - char *buf = (char *)malloc(1024 * 1024); - setvbuf(file, buf, _IOFBF, 1024 * 1024); - - for (unsigned int i = 0; i < usedInstructionsCount; ++i) { - const _DInst *ins = instrs + i; - - uintptr_t addr = ins->addr - lib_base; - - if (ins->opcode == I_INT_3) { - fprintf(file, "%08x: int 0x3\n", addr); - } else if (ins->opcode == I_RET) { - if (ins->ops[0].type == O_NONE) { - fprintf(file, "%08x: ret\n", addr); - } else if (ins->ops[0].type == O_IMM) { - fprintf(file, "%08x: ret 0x%x\n", addr, ins->imm.word); - } else { - fprintf(file, "%08x: ret ???\n", addr); - } - } else if (ins->opcode == I_PUSH) { - // fprintf(file, "%08x: push [type:%x index:%x size:%x]\n", addr, - // ins->ops[0].type, ins->ops[0].index, ins->ops[0].size); - if (ins->ops[0].type == O_REG && ins->ops[0].index == R_EBP) { - fprintf(file, "%08x: push ebp\n", addr); - } - } - } - - fclose(file); - free(buf); - } - - free(instrs); - } - - { - _CodeInfo codeinfo; - codeinfo.codeOffset = lib_base + text_off; - codeinfo.code = (const uint8_t *)(lib_base + text_off); - codeinfo.codeLen = text_len; - codeinfo.dt = Decode32Bits; - codeinfo.features = DF_RETURN_FC_ONLY; - - constexpr unsigned int maxInstructions = 3000000; - DevMsg(" sizeof(OFFSET_INTEGER) = %u\n", sizeof(OFFSET_INTEGER)); - DevMsg(" sizeof(_DInst) = %u\n", sizeof(_DInst)); - DevMsg(" calling malloc(%u)\n", sizeof(_DInst) * maxInstructions); - _DInst *instrs = (_DInst *)malloc(sizeof(_DInst) * maxInstructions); - DevMsg(" malloc returned: %08x\n", (uintptr_t)instrs); - - Prof::Begin(); - unsigned int usedInstructionsCount = 0; - _DecodeResult result = distorm_decompose(&codeinfo, instrs, maxInstructions, &usedInstructionsCount); - Prof::End("diStorm flow control only"); - - DevMsg(" result: %d\n", result); - DevMsg(" usedInstructionsCount: %u\n", usedInstructionsCount); - - free(instrs); - } - - DevMsg("Disasm_diStorm::Test END\n"); - } -} -#endif - - -#if 0 -#include -namespace Disasm_Capstone -{ - csh handle = 0; - - - void *my_malloc(size_t size) - { - DevMsg(" malloc(%u)\n", size); - return malloc(size); - } - void *my_calloc(size_t nmemb, size_t size) - { - DevMsg(" calloc(%u, %u)\n", nmemb, size); - return calloc(nmemb, size); - } - void *my_realloc(void *ptr, size_t size) - { - DevMsg(" realloc(%08x, %u)\n", (uintptr_t)ptr, size); - return realloc(ptr, size); - } - void my_free(void *ptr) - { - DevMsg(" free(%08x)\n", (uintptr_t)ptr); - free(ptr); - } - int my_vsnprintf(char *str, size_t size, const char *format, va_list ap) - { - return vsnprintf(str, size, format, ap); - } - - - void ErrCheck(const char *what) - { - cs_err err = cs_errno(handle); - if (err != cs_err::CS_ERR_OK) { - DevMsg(" error in %s: %s\n", what, cs_strerror(err)); - } - } - - void Test() - { - DevMsg("Disasm_Capstone::Test BEGIN\n"); - - LibInfo info = LibMgr::GetInfo(Library::SERVER); - - uint32_t lib_base = info.baseaddr; - - uint32_t text_off = info.segs[".text"].off; - uint32_t text_len = info.segs[".text"].len; - - uint32_t code_begin = lib_base + text_off; - uint32_t code_end = lib_base + text_off + text_len; - - - cs_opt_mem my_mem = { - &my_malloc, - &my_calloc, - &my_realloc, - &my_free, - &my_vsnprintf, - }; - - - cs_open(CS_ARCH_X86, CS_MODE_32, &handle); - ErrCheck("cs_open"); - - cs_option(handle, CS_OPT_SYNTAX, CS_OPT_SYNTAX_INTEL); - ErrCheck("CS_OPT_SYNTAX"); - cs_option(handle, CS_OPT_DETAIL, CS_OPT_OFF); - ErrCheck("CS_OPT_DETAIL"); - cs_option(handle, CS_OPT_SKIPDATA, CS_OPT_ON); - ErrCheck("CS_OPT_SKIPDATA"); - cs_option(handle, CS_OPT_MEM, (size_t)&my_mem); - ErrCheck("CS_OPT_MEM"); - - - cs_insn *insn = cs_malloc(handle); - - const uint8_t *code = (const uint8_t *)code_begin; - size_t size = text_len; - uint64_t address = code_begin; - - FILE *file = fopen("D:\\server.txt", "w"); - - long n_insns = 0; - while (cs_disasm_iter(handle, &code, &size, &address, insn)) { - fprintf(file, " %08llx: %s\t%s\n", insn->address, insn->mnemonic, insn->op_str); - ++n_insns; - } - DevMsg(" %ld insns\n", n_insns); - - fclose(file); - - cs_free(insn, 1); - - cs_close(&handle); - - DevMsg("Disasm_Capstone::Test END\n"); - } -} -#endif - - -#if 0 -#include -namespace Disasm_Capstone_Cxx -{ - void Test() - { - DevMsg("Disasm_Capstone::Test BEGIN\n"); - - LibInfo info = LibMgr::GetInfo(Library::SERVER); - - uint32_t lib_base = info.baseaddr; - - uint32_t text_off = info.segs[".text"].off; - uint32_t text_len = info.segs[".text"].len; - - - CX86Disasm86 dis; - - cs_err err; - if ((err = dis.GetError()) != CS_ERR_OK) { - DevMsg(" capstone error: '%s'\n", dis.ErrToStr(err)); - } - - dis.SetSyntax(cs_opt_value::CS_OPT_SYNTAX_INTEL); - dis.SetDetail(cs_opt_value::CS_OPT_OFF); - dis.SetSkipData(cs_opt_value::CS_OPT_ON); - - auto insns = dis.Disasm((const void *)(lib_base + text_off), text_len, lib_base + text_off); -// if (!insns.get()) { -// DevMsg(" can't get insn\n"); -// } - - FILE *file = fopen("D:\\server.txt", "w"); - DevMsg(" %u insns\n", insns->Count); - for (size_t i = 0; i < insns->Count; ++i) { - auto insn = insns->Instructions(i); - - fprintf(file, "-> %08llx: %s\t%s\n", insn->address, insn->mnemonic, insn->op_str); - } - fclose(file); - - DevMsg("Disasm_Capstone::Test END\n"); - } -} -#endif - - -#if 0 -#include -namespace Disasm_udis86 -{ - void Test() - { - DevMsg("Disasm_udis86::Test BEGIN\n"); - DevMsg("Disasm_udis86::Test END\n"); - } -} -#endif - - -#if 0 -namespace Disasm -{ - void Test() - { -#if defined _MSC_VER - Disasm_diStorm::Test(); - Disasm_Capstone::Test(); -// Disasm_CapstoneCxx::Test(); -// Test_udis86(); -#endif - } -} -#endif - - -Disasm::~Disasm() -{ - // TODO: free memory -} - - -void Disasm::ErrCheck(const char *what) -{ - cs_err err = cs_errno(this->m_Handle); - if (err != cs_err::CS_ERR_OK) { - DevMsg(" error in %s: %s\n", what, cs_strerror(err)); - } -} - -void Disasm::Load() -{ - DevMsg("Disasm_Capstone::Test BEGIN\n"); - - LibInfo info = LibMgr::GetInfo(Library::SERVER); - - uint32_t lib_base = info.baseaddr; - - uint32_t text_off = info.segs[".text"].off; - uint32_t text_len = info.segs[".text"].len; - - uint32_t code_begin = lib_base + text_off; - uint32_t code_end = lib_base + text_off + text_len; - - cs_open(CS_ARCH_X86, CS_MODE_32, &this->m_Handle); - ErrCheck("cs_open"); - - cs_option(this->m_Handle, CS_OPT_SYNTAX, CS_OPT_SYNTAX_INTEL); - ErrCheck("CS_OPT_SYNTAX"); - cs_option(this->m_Handle, CS_OPT_DETAIL, CS_OPT_ON); // TODO: disable detail if possible - ErrCheck("CS_OPT_DETAIL"); - cs_option(this->m_Handle, CS_OPT_SKIPDATA, CS_OPT_ON); - ErrCheck("CS_OPT_SKIPDATA"); -// cs_option(this->m_Handle, CS_OPT_MEM, (size_t)&my_mem); -// ErrCheck("CS_OPT_MEM"); - - cs_insn *insn = cs_malloc(this->m_Handle); - - const uint8_t *code = (const uint8_t *)code_begin; - size_t size = text_len; - uint64_t address = code_begin; - - FILE *file = fopen("D:\\server.txt", "w"); - - - // PASS: iterate all insns, store all immediate operands that are addrs in .text - // PASS: iterate thru .rdata, store all dword-aligned dwords that are addrs in .text - // (in both cases, filter out addrs which are not 0x10-aligned) - - // treat all these addrs as "potential func start points" - // so if we have a long forward jmp and pass one of these addrs, assume the func ended - - - struct FuncInfo - { - uint32_t addr; - uint32_t len; - }; - - std::vector funcs; - - FuncInfo func = {(uint32_t)address, 0}; - bool in_func = true; - - while (cs_disasm_iter(this->m_Handle, &code, &size, &address, insn)) { - - - if (insn->id == X86_INS_INT3) { - if (in_func) { - func.len = insn->address - func.addr; - funcs.push_back(func); - in_func = false; - } - } else { - if (!in_func) { - func.addr = insn->address; - in_func = true; - } - } - } - - if (in_func) { - func.len = insn->address - func.addr; - funcs.push_back(func); - } - - for (const auto& func : funcs) { - fprintf(file, " func: %08x\n", 0x10001000 + func.addr - code_begin); - fprintf(file, " %08x\n", 0x10001000 + func.addr + func.len - code_begin - 1); - fprintf(file, "\n"); - } - - - // TODO: try to handle switch jump tables properly - // - look at how they work - // - see how we may be mishandling them - // - figure out a way to handle them properly - - -#if 0 - long n_insns = 0; - std::map stats; - while (cs_disasm_iter(this->m_Handle, &code, &size, &address, insn)) { - ++stats[insn->mnemonic]; -// fprintf(file, " %08llx: %s\t%s\n", insn->address, insn->mnemonic, insn->op_str); - ++n_insns; - } - DevMsg(" %ld insns\n", n_insns); - - struct InsnStat - { - std::string name; - long count; - }; - std::vector sorted; - - for (const auto& pair : stats) { - sorted.push_back({pair.first, pair.second}); - } - std::sort(sorted.begin(), sorted.end(), [](InsnStat& lhs, InsnStat& rhs){ - return lhs.count > rhs.count; - }); - - for (const auto& stat : sorted) { - DevMsg(" %6ld %s\n", stat.count, stat.name.c_str()); - } -#endif - - - fclose(file); - - cs_free(insn, 1); - cs_close(&this->m_Handle); - - DevMsg("Disasm_Capstone::Test END\n"); -} diff --git a/src/disasm/disasm.h b/src/disasm/disasm.h index f5d26462..7dbf313b 100644 --- a/src/disasm/disasm.h +++ b/src/disasm/disasm.h @@ -2,46 +2,273 @@ #define _INCLUDE_SIGSEGV_DISASM_DISASM_H_ -class Disasm +//////////////////////////////////////////////////////////////////////////////// +// Read-only pointer+count array accessor ////////////////////////////////////// +//////////////////////////////////////////////////////////////////////////////// +template +class ArrayView { public: - Disasm() {}; - ~Disasm(); + ArrayView(const T *ptr, SIZE count) : m_pArray(ptr), m_Count(count) {} - void Load(); + ArrayView(const ArrayView&) = delete; + ArrayView(ArrayView&&) = default; + + const T *begin() const { return this->m_pArray; } + const T *end() const { return this->m_pArray + this->m_Count; } + + const T *cbegin() const { return begin(); } + const T *cend() const { return end(); } + + SIZE size() const { return this->m_Count; } + bool empty() const { return (this->m_Count == 0); } + + const T& front() const { return this->m_pArray[0]; } + const T& back() const { return this->m_pArray[this->m_Count - 1]; } + + const T& operator[](SIZE idx) { return this->m_pArray[idx]; } private: - // int 3 - // int - // int0 - // int1 - // call - // ret - // jmp - // jcc - // iret - // iretd + const T *m_pArray; + SIZE m_Count; +}; + + +//////////////////////////////////////////////////////////////////////////////// +// Operand info //////////////////////////////////////////////////////////////// +//////////////////////////////////////////////////////////////////////////////// +class Operand : private cs_x86_op +{ +public: + x86_op_type Type() const { return this->type; } + uint_fast8_t Size() const { return this->size; } - // sysenter - // sysexit + x86_reg Reg() const { return this->reg; } + int64_t Imm() const { return this->imm; } + double FP () const { return this->fp; } - // enter - // leave + uint8_t Imm_U8 () const { return static_cast< uint8_t>(this->imm); } + int8_t Imm_S8 () const { return static_cast< int8_t>(this->imm); } + uint16_t Imm_U16() const { return static_cast(this->imm); } + int16_t Imm_S16() const { return static_cast< int16_t>(this->imm); } + uint32_t Imm_U32() const { return static_cast(this->imm); } + int32_t Imm_S32() const { return static_cast< int32_t>(this->imm); } + uint64_t Imm_U64() const { return static_cast(this->imm); } + int64_t Imm_S64() const { return static_cast< int64_t>(this->imm); } - // push ebp - // mov ebp,esp + x86_reg Mem_Seg () const { return static_cast(this->mem.segment); } + x86_reg Mem_Base () const { return static_cast(this->mem.base ); } + x86_reg Mem_Index() const { return static_cast(this->mem.index ); } + uint_fast8_t Mem_Scale() const { return this->mem.scale; } + int64_t Mem_Disp () const { return this->mem.disp; } - // mov esp,ebp - // pop ebp + x86_avx_bcast AVX_Broadcast () const { return this->avx_bcast; } + bool AVX_ZeroOpMask() const { return this->avx_zero_opmask; } +}; +static_assert(sizeof(Operand) == sizeof(cs_x86_op), ""); + +//////////////////////////////////////////////////////////////////////////////// +// Non-detailed instruction info /////////////////////////////////////////////// +//////////////////////////////////////////////////////////////////////////////// +class InstructionBase +{ +private: + using BytesView = ArrayView; - void ErrCheck(const char *what); +public: + InstructionBase(const cs_insn *insn) : m_pInsn(insn) {} - csh m_Handle = 0; + x86_insn ID () const { return static_cast(Insn().id); } + uint32_t Addr() const { return Insn().address; } + + BytesView Bytes() const { return BytesView(Insn().bytes, Insn().size); } + + const char *MnemonicStr() const { return Insn().mnemonic; } + const char *OperandStr () const { return Insn().op_str; } + +protected: + const cs_insn& Insn() const { return *this->m_pInsn; } + +private: + const cs_insn *m_pInsn; +}; + +//////////////////////////////////////////////////////////////////////////////// +// Detailed-only instruction info ////////////////////////////////////////////// +//////////////////////////////////////////////////////////////////////////////// +class InstructionDetailed : public InstructionBase +{ +private: + using RegsView = ArrayView; + using GroupsView = ArrayView; + using OpcodeView = ArrayView; + using OperandView = ArrayView; + +public: + InstructionDetailed(const cs_insn *insn) : InstructionBase(insn) {} + + RegsView RegsRead () const { return RegsView(Detail().regs_read, Detail().regs_read_count); } + RegsView RegsWrite() const { return RegsView(Detail().regs_write, Detail().regs_write_count); } + + GroupsView Groups() const { return GroupsView(Detail().groups, Detail().groups_count); } + + x86_prefix PrefixRepLock () const { return static_cast(X86().prefix[0]); } + x86_prefix PrefixSegment () const { return static_cast(X86().prefix[1]); } + x86_prefix PrefixOpSize () const { return static_cast(X86().prefix[2]); } + x86_prefix PrefixAddrSize() const { return static_cast(X86().prefix[3]); } + + OpcodeView Opcode() const { return OpcodeView(X86().opcode, 4); } + + uint8_t REX () const { return X86().rex; } + uint8_t AddrSize() const { return X86().addr_size; } + uint8_t ModRM () const { return X86().modrm; } + uint8_t SIB () const { return X86().sib; } + uint8_t Disp () const { return X86().disp; } + + x86_reg SIB_Index() const { return X86().sib_index; } + uint8_t SIB_Scale() const { return X86().sib_scale; } + x86_reg SIB_Base () const { return X86().sib_base; } + + x86_sse_cc SSE_CC () const { return X86().sse_cc; } + x86_avx_cc AVX_CC () const { return X86().avx_cc; } + bool AVX_SAE() const { return X86().avx_sae; } + x86_avx_rm AVX_RM () const { return X86().avx_rm; } + + OperandView Operands() const { return OperandView(reinterpret_cast(X86().operands), X86().op_count); } - std::map m_Int3; - std::map m_Ret; + // TODO: implement these funcs manually, without need for the csh handle +// bool IsInGroup (x86_insn_group group_id) const { return cs_insn_group(this->m_Handle, &this->Insn(), group_id); } +// bool DoesReadReg (x86_reg reg_id) const { return cs_reg_read (this->m_Handle, &this->Insn(), reg_id); } +// bool DoesWriteReg(x86_reg reg_id) const { return cs_reg_write (this->m_Handle, &this->Insn(), reg_id); } +// int NumOperands (x86_op_type op_type) const { return cs_op_count (this->m_Handle, &this->Insn(), op_type); } + +private: + const cs_detail& Detail() const { return *this->Insn().detail; } + const cs_x86& X86 () const { return this->Detail().x86; } +}; + + +template +class DisasmResult +{ +public: + using InstructionType = std::conditional_t; + + DisasmResult(size_t count, cs_insn *insns) : + m_InsnCount(count), m_pInsnBuffer(insns) + { + for (size_t i = 0; i < count; ++i) { + this->m_InsnVector.push_back(InstructionType(&insns[i])); + } + } + ~DisasmResult() + { + if (this->m_pInsnBuffer != nullptr) { + cs_free(this->m_pInsnBuffer, this->m_InsnCount); + } + } + + DisasmResult(const DisasmResult&) = delete; + DisasmResult(DisasmResult&&) = default; + + auto begin() const { return this->m_InsnVector.cbegin(); } + auto end() const { return this->m_InsnVector.cend(); } + + auto cbegin() const { return this->m_InsnVector.cbegin(); } + auto cend() const { return this->m_InsnVector.cend(); } + + size_t size() const { return this->m_InsnVector.size(); } + bool empty() const { return this->m_InsnVector.empty(); } + + const auto& front() const { return this->m_InsnVector.front(); } + const auto& back() const { return this->m_InsnVector.back(); } + + const auto& operator[](size_t idx) { return this->m_InsnVector[idx]; } + +private: + size_t m_InsnCount; + cs_insn *m_pInsnBuffer; + + std::vector m_InsnVector; +}; + + +template +class Disassembler +{ +private: + static constexpr bool SKIP_DATA = false; + +public: + Disassembler() + { + auto arch = static_cast(CS_ARCH_X86); + auto mode = static_cast(CS_MODE_32 | CS_MODE_LITTLE_ENDIAN); + + this->m_InitError = cs_open(arch, mode, &this->m_Handle); + + if (this->Initialized()) { + (void)cs_option(this->m_Handle, CS_OPT_SYNTAX, CS_OPT_SYNTAX_INTEL); + (void)cs_option(this->m_Handle, CS_OPT_DETAIL, (DETAIL ? CS_OPT_ON : CS_OPT_OFF)); + (void)cs_option(this->m_Handle, CS_OPT_SKIPDATA, (SKIP_DATA ? CS_OPT_ON : CS_OPT_OFF)); + } + } + ~Disassembler() + { + if (this->m_Handle != 0) { + (void)cs_close(&this->m_Handle); + } + } + + cs_err InitError() const { return this->m_InitError; } + + cs_err ErrorCode() const { return cs_errno(this->m_Handle); } + const char *ErrorString() const { return cs_strerror(this->ErrorCode()); } + + DisasmResult Disassemble(uintptr_t addr, size_t len, uintptr_t offset = 0, size_t max_insns = 0) + { + cs_insn *insns = nullptr; + size_t count = cs_disasm(this->m_Handle, reinterpret_cast(addr), len, addr + offset, max_insns, &insns); + + return DisasmResult(count, insns); + } + + template bool IterateRange(uintptr_t addr, size_t len, FUNCTOR&& func) + { + using InstructionType = std::conditional_t; + + auto iter_code = reinterpret_cast(addr); + size_t iter_size = len; + auto iter_addr = (uint64_t)addr; + cs_insn *iter_insn = cs_malloc(this->m_Handle); + + do { + if (!cs_disasm_iter(this->m_Handle, &iter_code, &iter_size, &iter_addr, iter_insn)) { + cs_free(iter_insn, 1); + return false; + } + } while (func(InstructionType(iter_insn))); + + cs_free(iter_insn, 1); + return true; + } + template bool Iterate(uintptr_t addr, FUNCTOR&& func) + { + return this->IterateRange(addr, SIZE_MAX, func); + } + + // TODO: version of Iterate with num-of-instrs restriction + + const char *RegName(x86_reg reg_id) const { return cs_reg_name (this->m_Handle, reg_id); } + const char *InsnName(x86_insn insn_id) const { return cs_insn_name (this->m_Handle, insn_id); } + const char *GroupName(x86_insn_group group_id) const { return cs_group_name(this->m_Handle, group_id); } + +private: + bool Initialized() const { return (this->m_InitError == CS_ERR_OK && this->m_Handle != 0); } + + csh m_Handle = 0; + cs_err m_InitError; }; -extern Disasm g_Disasm; #endif diff --git a/src/disasm/old1/disasm.cpp b/src/disasm/old1/disasm.cpp new file mode 100644 index 00000000..24aa9738 --- /dev/null +++ b/src/disasm/old1/disasm.cpp @@ -0,0 +1,448 @@ +#include "disasm/disasm.h" +#include "library.h" +#include "util/prof.h" + + +Disasm g_Disasm; + + +#if 0 +#include +#include +namespace Disasm_diStorm +{ + void Test() + { + DevMsg("Disasm_diStorm::Test BEGIN\n"); + + LibInfo info = LibMgr::GetInfo(Library::SERVER); + + uint32_t lib_base = info.baseaddr; + + uint32_t text_off = info.segs[".text"].off; + uint32_t text_len = info.segs[".text"].len; + + + // _DInst: 64 bytes per instruction + + // all instructions: + // 2,305,482 + // 148 MB + + // just flow control: + // 706,387 + // 45 MB + + + { + _CodeInfo codeinfo; + codeinfo.codeOffset = lib_base + text_off; + codeinfo.code = (const uint8_t *)(lib_base + text_off); + codeinfo.codeLen = text_len; + codeinfo.dt = Decode32Bits; + codeinfo.features = DF_NONE; + + constexpr unsigned int maxInstructions = 3000000; + DevMsg(" sizeof(OFFSET_INTEGER) = %u\n", sizeof(OFFSET_INTEGER)); + DevMsg(" sizeof(_DInst) = %u\n", sizeof(_DInst)); + DevMsg(" calling malloc(%u)\n", sizeof(_DInst) * maxInstructions); + _DInst *instrs = (_DInst *)malloc(sizeof(_DInst) * maxInstructions); + DevMsg(" malloc returned: %08x\n", (uintptr_t)instrs); + + Prof::Begin(); + unsigned int usedInstructionsCount = 0; + _DecodeResult result = distorm_decompose(&codeinfo, instrs, maxInstructions, &usedInstructionsCount); + Prof::End("diStorm all instrs"); + + DevMsg(" result: %d\n", result); + DevMsg(" usedInstructionsCount: %u\n", usedInstructionsCount); + + FILE *file = fopen("D:\\server.txt", "w"); + if (file != nullptr) { + char *buf = (char *)malloc(1024 * 1024); + setvbuf(file, buf, _IOFBF, 1024 * 1024); + + for (unsigned int i = 0; i < usedInstructionsCount; ++i) { + const _DInst *ins = instrs + i; + + uintptr_t addr = ins->addr - lib_base; + + if (ins->opcode == I_INT_3) { + fprintf(file, "%08x: int 0x3\n", addr); + } else if (ins->opcode == I_RET) { + if (ins->ops[0].type == O_NONE) { + fprintf(file, "%08x: ret\n", addr); + } else if (ins->ops[0].type == O_IMM) { + fprintf(file, "%08x: ret 0x%x\n", addr, ins->imm.word); + } else { + fprintf(file, "%08x: ret ???\n", addr); + } + } else if (ins->opcode == I_PUSH) { + // fprintf(file, "%08x: push [type:%x index:%x size:%x]\n", addr, + // ins->ops[0].type, ins->ops[0].index, ins->ops[0].size); + if (ins->ops[0].type == O_REG && ins->ops[0].index == R_EBP) { + fprintf(file, "%08x: push ebp\n", addr); + } + } + } + + fclose(file); + free(buf); + } + + free(instrs); + } + + { + _CodeInfo codeinfo; + codeinfo.codeOffset = lib_base + text_off; + codeinfo.code = (const uint8_t *)(lib_base + text_off); + codeinfo.codeLen = text_len; + codeinfo.dt = Decode32Bits; + codeinfo.features = DF_RETURN_FC_ONLY; + + constexpr unsigned int maxInstructions = 3000000; + DevMsg(" sizeof(OFFSET_INTEGER) = %u\n", sizeof(OFFSET_INTEGER)); + DevMsg(" sizeof(_DInst) = %u\n", sizeof(_DInst)); + DevMsg(" calling malloc(%u)\n", sizeof(_DInst) * maxInstructions); + _DInst *instrs = (_DInst *)malloc(sizeof(_DInst) * maxInstructions); + DevMsg(" malloc returned: %08x\n", (uintptr_t)instrs); + + Prof::Begin(); + unsigned int usedInstructionsCount = 0; + _DecodeResult result = distorm_decompose(&codeinfo, instrs, maxInstructions, &usedInstructionsCount); + Prof::End("diStorm flow control only"); + + DevMsg(" result: %d\n", result); + DevMsg(" usedInstructionsCount: %u\n", usedInstructionsCount); + + free(instrs); + } + + DevMsg("Disasm_diStorm::Test END\n"); + } +} +#endif + + +#if 0 +#include +namespace Disasm_Capstone +{ + csh handle = 0; + + + void *my_malloc(size_t size) + { + DevMsg(" malloc(%u)\n", size); + return malloc(size); + } + void *my_calloc(size_t nmemb, size_t size) + { + DevMsg(" calloc(%u, %u)\n", nmemb, size); + return calloc(nmemb, size); + } + void *my_realloc(void *ptr, size_t size) + { + DevMsg(" realloc(%08x, %u)\n", (uintptr_t)ptr, size); + return realloc(ptr, size); + } + void my_free(void *ptr) + { + DevMsg(" free(%08x)\n", (uintptr_t)ptr); + free(ptr); + } + int my_vsnprintf(char *str, size_t size, const char *format, va_list ap) + { + return vsnprintf(str, size, format, ap); + } + + + void ErrCheck(const char *what) + { + cs_err err = cs_errno(handle); + if (err != cs_err::CS_ERR_OK) { + DevMsg(" error in %s: %s\n", what, cs_strerror(err)); + } + } + + void Test() + { + DevMsg("Disasm_Capstone::Test BEGIN\n"); + + LibInfo info = LibMgr::GetInfo(Library::SERVER); + + uint32_t lib_base = info.baseaddr; + + uint32_t text_off = info.segs[".text"].off; + uint32_t text_len = info.segs[".text"].len; + + uint32_t code_begin = lib_base + text_off; + uint32_t code_end = lib_base + text_off + text_len; + + + cs_opt_mem my_mem = { + &my_malloc, + &my_calloc, + &my_realloc, + &my_free, + &my_vsnprintf, + }; + + + cs_open(CS_ARCH_X86, CS_MODE_32, &handle); + ErrCheck("cs_open"); + + cs_option(handle, CS_OPT_SYNTAX, CS_OPT_SYNTAX_INTEL); + ErrCheck("CS_OPT_SYNTAX"); + cs_option(handle, CS_OPT_DETAIL, CS_OPT_OFF); + ErrCheck("CS_OPT_DETAIL"); + cs_option(handle, CS_OPT_SKIPDATA, CS_OPT_ON); + ErrCheck("CS_OPT_SKIPDATA"); + cs_option(handle, CS_OPT_MEM, (size_t)&my_mem); + ErrCheck("CS_OPT_MEM"); + + + cs_insn *insn = cs_malloc(handle); + + const uint8_t *code = (const uint8_t *)code_begin; + size_t size = text_len; + uint64_t address = code_begin; + + FILE *file = fopen("D:\\server.txt", "w"); + + long n_insns = 0; + while (cs_disasm_iter(handle, &code, &size, &address, insn)) { + fprintf(file, " %08llx: %s\t%s\n", insn->address, insn->mnemonic, insn->op_str); + ++n_insns; + } + DevMsg(" %ld insns\n", n_insns); + + fclose(file); + + cs_free(insn, 1); + + cs_close(&handle); + + DevMsg("Disasm_Capstone::Test END\n"); + } +} +#endif + + +#if 0 +#include +namespace Disasm_Capstone_Cxx +{ + void Test() + { + DevMsg("Disasm_Capstone::Test BEGIN\n"); + + LibInfo info = LibMgr::GetInfo(Library::SERVER); + + uint32_t lib_base = info.baseaddr; + + uint32_t text_off = info.segs[".text"].off; + uint32_t text_len = info.segs[".text"].len; + + + CX86Disasm86 dis; + + cs_err err; + if ((err = dis.GetError()) != CS_ERR_OK) { + DevMsg(" capstone error: '%s'\n", dis.ErrToStr(err)); + } + + dis.SetSyntax(cs_opt_value::CS_OPT_SYNTAX_INTEL); + dis.SetDetail(cs_opt_value::CS_OPT_OFF); + dis.SetSkipData(cs_opt_value::CS_OPT_ON); + + auto insns = dis.Disasm((const void *)(lib_base + text_off), text_len, lib_base + text_off); +// if (!insns.get()) { +// DevMsg(" can't get insn\n"); +// } + + FILE *file = fopen("D:\\server.txt", "w"); + DevMsg(" %u insns\n", insns->Count); + for (size_t i = 0; i < insns->Count; ++i) { + auto insn = insns->Instructions(i); + + fprintf(file, "-> %08llx: %s\t%s\n", insn->address, insn->mnemonic, insn->op_str); + } + fclose(file); + + DevMsg("Disasm_Capstone::Test END\n"); + } +} +#endif + + +#if 0 +#include +namespace Disasm_udis86 +{ + void Test() + { + DevMsg("Disasm_udis86::Test BEGIN\n"); + DevMsg("Disasm_udis86::Test END\n"); + } +} +#endif + + +#if 0 +namespace Disasm +{ + void Test() + { +#if defined _MSC_VER + Disasm_diStorm::Test(); + Disasm_Capstone::Test(); +// Disasm_CapstoneCxx::Test(); +// Test_udis86(); +#endif + } +} +#endif + + +Disasm::~Disasm() +{ + // TODO: free memory +} + + +void Disasm::ErrCheck(const char *what) +{ + cs_err err = cs_errno(this->m_Handle); + if (err != cs_err::CS_ERR_OK) { + DevMsg(" error in %s: %s\n", what, cs_strerror(err)); + } +} + +void Disasm::Load() +{ + DevMsg("Disasm_Capstone::Test BEGIN\n"); + + LibInfo info = LibMgr::GetInfo(Library::SERVER); + + uint32_t lib_base = info.baseaddr; + + uint32_t text_off = info.segs[".text"].off; + uint32_t text_len = info.segs[".text"].len; + + uint32_t code_begin = lib_base + text_off; + uint32_t code_end = lib_base + text_off + text_len; + + cs_open(CS_ARCH_X86, CS_MODE_32, &this->m_Handle); + ErrCheck("cs_open"); + + cs_option(this->m_Handle, CS_OPT_SYNTAX, CS_OPT_SYNTAX_INTEL); + ErrCheck("CS_OPT_SYNTAX"); + cs_option(this->m_Handle, CS_OPT_DETAIL, CS_OPT_ON); // TODO: disable detail if possible + ErrCheck("CS_OPT_DETAIL"); + cs_option(this->m_Handle, CS_OPT_SKIPDATA, CS_OPT_ON); + ErrCheck("CS_OPT_SKIPDATA"); +// cs_option(this->m_Handle, CS_OPT_MEM, (size_t)&my_mem); +// ErrCheck("CS_OPT_MEM"); + + cs_insn *insn = cs_malloc(this->m_Handle); + + const uint8_t *code = (const uint8_t *)code_begin; + size_t size = text_len; + uint64_t address = code_begin; + + FILE *file = fopen("D:\\server.txt", "w"); + + + // PASS: iterate all insns, store all immediate operands that are addrs in .text + // PASS: iterate thru .rdata, store all dword-aligned dwords that are addrs in .text + // (in both cases, filter out addrs which are not 0x10-aligned) + + // treat all these addrs as "potential func start points" + // so if we have a long forward jmp and pass one of these addrs, assume the func ended + + + struct FuncInfo + { + uint32_t addr; + uint32_t len; + }; + + std::vector funcs; + + FuncInfo func = {(uint32_t)address, 0}; + bool in_func = true; + + while (cs_disasm_iter(this->m_Handle, &code, &size, &address, insn)) { + + + if (insn->id == X86_INS_INT3) { + if (in_func) { + func.len = insn->address - func.addr; + funcs.push_back(func); + in_func = false; + } + } else { + if (!in_func) { + func.addr = insn->address; + in_func = true; + } + } + } + + if (in_func) { + func.len = insn->address - func.addr; + funcs.push_back(func); + } + + for (const auto& func : funcs) { + fprintf(file, " func: %08x\n", 0x10001000 + func.addr - code_begin); + fprintf(file, " %08x\n", 0x10001000 + func.addr + func.len - code_begin - 1); + fprintf(file, "\n"); + } + + + // TODO: try to handle switch jump tables properly + // - look at how they work + // - see how we may be mishandling them + // - figure out a way to handle them properly + + +#if 0 + long n_insns = 0; + std::map stats; + while (cs_disasm_iter(this->m_Handle, &code, &size, &address, insn)) { + ++stats[insn->mnemonic]; +// fprintf(file, " %08llx: %s\t%s\n", insn->address, insn->mnemonic, insn->op_str); + ++n_insns; + } + DevMsg(" %ld insns\n", n_insns); + + struct InsnStat + { + std::string name; + long count; + }; + std::vector sorted; + + for (const auto& pair : stats) { + sorted.push_back({pair.first, pair.second}); + } + std::sort(sorted.begin(), sorted.end(), [](InsnStat& lhs, InsnStat& rhs){ + return lhs.count > rhs.count; + }); + + for (const auto& stat : sorted) { + DevMsg(" %6ld %s\n", stat.count, stat.name.c_str()); + } +#endif + + + fclose(file); + + cs_free(insn, 1); + cs_close(&this->m_Handle); + + DevMsg("Disasm_Capstone::Test END\n"); +} diff --git a/src/disasm/old1/disasm.h b/src/disasm/old1/disasm.h new file mode 100644 index 00000000..f5d26462 --- /dev/null +++ b/src/disasm/old1/disasm.h @@ -0,0 +1,47 @@ +#ifndef _INCLUDE_SIGSEGV_DISASM_DISASM_H_ +#define _INCLUDE_SIGSEGV_DISASM_DISASM_H_ + + +class Disasm +{ +public: + Disasm() {}; + ~Disasm(); + + void Load(); + +private: + // int 3 + // int + // int0 + // int1 + // call + // ret + // jmp + // jcc + // iret + // iretd + + // sysenter + // sysexit + + // enter + // leave + + // push ebp + // mov ebp,esp + + // mov esp,ebp + // pop ebp + + void ErrCheck(const char *what); + + csh m_Handle = 0; + + std::map m_Int3; + std::map m_Ret; +}; +extern Disasm g_Disasm; + + +#endif diff --git a/src/disasm/old2/disasm.h b/src/disasm/old2/disasm.h new file mode 100644 index 00000000..062761ff --- /dev/null +++ b/src/disasm/old2/disasm.h @@ -0,0 +1,49 @@ +#ifndef _INCLUDE_SIGSEGV_DISASM_DISASM_H_ +#define _INCLUDE_SIGSEGV_DISASM_DISASM_H_ + + +#if !defined _MSC_VER +#define __in +#define __out +#define __inout +#define __forceinline //[[gnu::always_inline]] +#define __checkReturn [[gnu::warn_unused_result]] +#endif + +#include + + +class Disassembler : private CX86Disasm86 +{ +private: + using Base = CX86Disasm86; + +public: + template Disassembler(ARGS... args) : Base(std::forward(args)...) + { + this->SetSyntax (cs_opt_value::CS_OPT_SYNTAX_INTEL); + this->SetDetail (cs_opt_value::CS_OPT_ON); + this->SetSkipData(cs_opt_value::CS_OPT_OFF); + } + + using Base::GetError; + using Base::Version; + using Base::ErrToStr; + using Base::SetSyntax; + using Base::SetDetail; + using Base::SetMode; + using Base::SetMemMgrFunc; + using Base::SetSkipData; + using Base::SetSkipDataCallback; + + bool HasError() { return (this->GetError() != CS_ERR_OK); } + const char *ErrorStr() { return Base::ErrToStr(this->GetError()); } + + template std::unique_ptr Disasm(ARGS... args) + { + return std::unique_ptr(Base::Disasm(std::forward(args)...)); + } +}; + + +#endif diff --git a/src/extension.cpp b/src/extension.cpp index df30101a..2f7eff7e 100644 --- a/src/extension.cpp +++ b/src/extension.cpp @@ -42,7 +42,13 @@ ISoundEmitterSystemBase *soundemitterbase = nullptr; IMaterialSystem *g_pMaterialSystem = nullptr; +vgui::IVGui *g_pVGui = nullptr; +vgui::IInput *g_pVGuiInput = nullptr; +vgui::IPanel *g_pVGuiPanel = nullptr; vgui::ISchemeManager *g_pVGuiSchemeManager = nullptr; +vgui::ISystem *g_pVGuiSystem = nullptr; +vgui::ILocalize *g_pVGuiLocalize = nullptr; +vgui::IInputInternal *g_pVGuiInputInternal = nullptr; vgui::ISurface *g_pVGuiSurface = nullptr; IMatSystemSurface *g_pMatSystemSurface = nullptr; @@ -68,6 +74,8 @@ IDedicatedExports *dedicated = nullptr; IMDLCache *mdlcache = nullptr; +IClientMode *g_pClientMode = nullptr; + bool CExtSigsegv::SDK_OnLoad(char *error, size_t maxlen, bool late) { @@ -96,6 +104,8 @@ bool CExtSigsegv::SDK_OnLoad(char *error, size_t maxlen, bool late) RTTI::PreLoad(); AddrManager::Load(); + g_pClientMode = reinterpret_cast(AddrManager::GetAddr("g_pClientMode")); + if (!Link::InitAll()) goto fail; Prop::PreloadAll(); @@ -152,13 +162,16 @@ bool CExtSigsegv::QueryRunning(char *error, size_t maxlen) #define GET_IFACE_OPTIONAL(factory, var, name) \ - var = reinterpret_cast(ismm->VInterfaceMatch(factory##Factory(), name, -1)) + var = reinterpret_cast(ismm->VInterfaceMatch(factory##Factory(), name, -1)); \ + if (var == nullptr) { \ + DevWarning("Could not find optional interface: %s\n", name); \ + } #define GET_IFACE_REQUIRED(factory, var, name) \ var = reinterpret_cast(ismm->VInterfaceMatch(factory##Factory(), name, -1)); \ if (var == nullptr) { \ if (error != nullptr && maxlen != 0) { \ - ismm->Format(error, maxlen, "Could not find interface: %s", name); \ + ismm->Format(error, maxlen, "Could not find required interface: %s", name); \ } \ return false; \ } @@ -191,8 +204,8 @@ bool CExtSigsegv::SDK_OnMetamodLoad(ISmmAPI *ismm, char *error, size_t maxlen, b GET_IFACE_REQUIRED(Server, botmanager, INTERFACEVERSION_PLAYERBOTMANAGER); GET_IFACE_REQUIRED(Server, servertools, VSERVERTOOLS_INTERFACE_VERSION); - GET_IFACE_REQUIRED(Physics, physics, VPHYSICS_INTERFACE_VERSION); - GET_IFACE_REQUIRED(Physics, physcollision, VPHYSICS_COLLISION_INTERFACE_VERSION); + GET_IFACE_REQUIRED(VPhysics, physics, VPHYSICS_INTERFACE_VERSION); + GET_IFACE_REQUIRED(VPhysics, physcollision, VPHYSICS_COLLISION_INTERFACE_VERSION); GET_IFACE_OPTIONAL(Engine, debugoverlay, VDEBUG_OVERLAY_INTERFACE_VERSION); GET_IFACE_OPTIONAL(Engine, enginetools, VENGINETOOL_INTERFACE_VERSION); @@ -206,7 +219,13 @@ bool CExtSigsegv::SDK_OnMetamodLoad(ISmmAPI *ismm, char *error, size_t maxlen, b } if (VGUIFactory() != nullptr) { + GET_IFACE_OPTIONAL(VGUI, g_pVGui, VGUI_IVGUI_INTERFACE_VERSION); + GET_IFACE_OPTIONAL(VGUI, g_pVGuiInput, VGUI_INPUT_INTERFACE_VERSION); + GET_IFACE_OPTIONAL(VGUI, g_pVGuiPanel, VGUI_PANEL_INTERFACE_VERSION); GET_IFACE_OPTIONAL(VGUI, g_pVGuiSchemeManager, VGUI_SCHEME_INTERFACE_VERSION); + GET_IFACE_OPTIONAL(VGUI, g_pVGuiSystem, VGUI_SYSTEM_INTERFACE_VERSION); + GET_IFACE_OPTIONAL(VGUI, g_pVGuiLocalize, VGUI_LOCALIZE_INTERFACE_VERSION); + GET_IFACE_OPTIONAL(VGUI, g_pVGuiInputInternal, VGUI_INPUTINTERNAL_INTERFACE_VERSION); } if (VGUIMatSurfaceFactory() != nullptr) { @@ -232,12 +251,17 @@ bool CExtSigsegv::SDK_OnMetamodLoad(ISmmAPI *ismm, char *error, size_t maxlen, b gpGlobals = ismm->GetCGlobals(); - LibMgr::SetPtr(Library::SERVER, ServerFactory()); - LibMgr::SetPtr(Library::ENGINE, EngineFactory()); - LibMgr::SetPtr(Library::DEDICATED, dedicated); - LibMgr::SetPtr(Library::TIER0, &MemAllocScratch); - LibMgr::SetPtr(Library::CLIENT, clientdll); - LibMgr::SetPtr(Library::VGUIMATSURFACE, g_pVGuiSurface); + LibMgr::SetPtr(Library::SERVER, ServerFactory()); + LibMgr::SetPtr(Library::ENGINE, EngineFactory()); + LibMgr::SetPtr(Library::DEDICATED, DedicatedFactory()); + LibMgr::SetPtr(Library::TIER0, &MemAllocScratch); + LibMgr::SetPtr(Library::CLIENT, ClientFactory()); + LibMgr::SetPtr(Library::VGUIMATSURFACE, VGUIMatSurfaceFactory()); + LibMgr::SetPtr(Library::MATERIALSYSTEM, MaterialSystemFactory()); + LibMgr::SetPtr(Library::SOUNDEMITTERSYSTEM, SoundEmitterSystemFactory()); + LibMgr::SetPtr(Library::DATACACHE, DataCacheFactory()); + LibMgr::SetPtr(Library::VGUI, VGUIFactory()); + LibMgr::SetPtr(Library::VPHYSICS, VPhysicsFactory()); return true; } diff --git a/src/factory.h b/src/factory.h index 27cc54a3..1fa201bf 100644 --- a/src/factory.h +++ b/src/factory.h @@ -3,7 +3,7 @@ inline CreateInterfaceFn EngineFactory() { return g_SMAPI->GetEngineFactory (false); } -inline CreateInterfaceFn PhysicsFactory() { return g_SMAPI->GetPhysicsFactory (false); } +inline CreateInterfaceFn VPhysicsFactory() { return g_SMAPI->GetPhysicsFactory (false); } inline CreateInterfaceFn FileSystemFactory() { return g_SMAPI->GetFileSystemFactory(false); } inline CreateInterfaceFn ServerFactory() { return g_SMAPI->GetServerFactory (false); } diff --git a/src/gameconf.cpp b/src/gameconf.cpp index 81ed486c..f71d707f 100644 --- a/src/gameconf.cpp +++ b/src/gameconf.cpp @@ -35,6 +35,7 @@ static const char *const configs[] = { "sigsegv/misc", "sigsegv/debugoverlay", "sigsegv/client", + "sigsegv/convars", nullptr, }; @@ -204,7 +205,7 @@ void CSigsegvGameConf::AddrEntry_Load_Common(IAddr *addr) const auto& kv = this->m_AddrEntry_State.m_KeyValues; if (kv.find("lib") != kv.end()) { - addr->SetLibrary(LibMgr::FromString(kv.at("lib").c_str())); + addr->SetLibrary(LibMgr::Lib_FromString(kv.at("lib").c_str())); } } @@ -229,6 +230,26 @@ SMCResult CSigsegvGameConf::AddrEntry_Load_Sym() return SMCResult_Continue; } +SMCResult CSigsegvGameConf::AddrEntry_Load_Sym_Regex() +{ + const auto& name = this->m_AddrEntry_State.m_Name; + const auto& kv = this->m_AddrEntry_State.m_KeyValues; + + for (const std::string& key : { "sym" }) { + if (kv.find(key) == kv.end()) { + DevMsg("GameData error: addr \"%s\" lacks required key \"%s\"\n", name.c_str(), key.c_str()); + return SMCResult_HaltFail; + } + } + + const auto& sym = kv.at("sym"); + + auto a = new CAddr_Sym_Regex(name, sym); + this->AddrEntry_Load_Common(a); + this->m_AddrPtrs.push_back(std::unique_ptr(a)); + return SMCResult_Continue; +} + SMCResult CSigsegvGameConf::AddrEntry_Load_Fixed() { const auto& name = this->m_AddrEntry_State.m_Name; @@ -330,11 +351,11 @@ SMCResult CSigsegvGameConf::AddrEntry_Load_Func_DataMap_VThunk() } const auto& sym = kv.at("sym"); - const auto& dm_name = kv.at("datamap"); + const auto& datamap = kv.at("datamap"); const auto& f_name = kv.at("func"); const auto& vtable = kv.at("vtable"); - auto a = new CAddr_Func_DataMap_VThunk(name, sym, dm_name, f_name, vtable); + auto a = new CAddr_Func_DataMap_VThunk(name, sym, datamap, f_name, vtable); this->AddrEntry_Load_Common(a); this->m_AddrPtrs.push_back(std::unique_ptr(a)); return SMCResult_Continue; @@ -405,6 +426,29 @@ SMCResult CSigsegvGameConf::AddrEntry_Load_Func_EBPPrologue_UniqueStr_KnownVTIdx return SMCResult_Continue; } +SMCResult CSigsegvGameConf::AddrEntry_Load_Func_EBPPrologue_NonUniqueStr_KnownVTIdx() +{ + const auto& name = this->m_AddrEntry_State.m_Name; + const auto& kv = this->m_AddrEntry_State.m_KeyValues; + + for (const std::string& key : { "sym", "str", "vtable", "idx" }) { + if (kv.find(key) == kv.end()) { + DevMsg("GameData error: addr \"%s\" lacks required key \"%s\"\n", name.c_str(), key.c_str()); + return SMCResult_HaltFail; + } + } + + const auto& sym = kv.at("sym"); + const auto& str = kv.at("str"); + const auto& vtable = kv.at("vtable"); + int idx = stoi(kv.at("idx"), nullptr, 0); + + auto a = new CAddr_Func_EBPPrologue_NonUniqueStr_KnownVTIdx(name, sym, str, vtable, idx); + this->AddrEntry_Load_Common(a); + this->m_AddrPtrs.push_back(std::unique_ptr(a)); + return SMCResult_Continue; +} + SMCResult CSigsegvGameConf::AddrEntry_Load_Func_EBPPrologue_VProf() { const auto& name = this->m_AddrEntry_State.m_Name; @@ -426,3 +470,23 @@ SMCResult CSigsegvGameConf::AddrEntry_Load_Func_EBPPrologue_VProf() this->m_AddrPtrs.push_back(std::unique_ptr(a)); return SMCResult_Continue; } + +SMCResult CSigsegvGameConf::AddrEntry_Load_ConCommandBase(bool is_command) +{ + const auto& name = this->m_AddrEntry_State.m_Name; + const auto& kv = this->m_AddrEntry_State.m_KeyValues; + + for (const std::string& key : { "name" }) { + if (kv.find(key) == kv.end()) { + DevMsg("GameData error: addr \"%s\" lacks required key \"%s\"\n", name.c_str(), key.c_str()); + return SMCResult_HaltFail; + } + } + + const auto& con_name = kv.at("name"); + + auto a = new CAddr_ConCommandBase(name, con_name, is_command); + this->AddrEntry_Load_Common(a); + this->m_AddrPtrs.push_back(std::unique_ptr(a)); + return SMCResult_Continue; +} diff --git a/src/gameconf.h b/src/gameconf.h index 99dbd050..c343abbb 100644 --- a/src/gameconf.h +++ b/src/gameconf.h @@ -44,20 +44,25 @@ class CSigsegvGameConf : public ITextListener_SMC SMCResult AddrEntry_End(); std::map m_AddrParsers{ - { "sym", &CSigsegvGameConf::AddrEntry_Load_Sym }, - { "fixed", &CSigsegvGameConf::AddrEntry_Load_Fixed }, - { "pattern", &CSigsegvGameConf::AddrEntry_Load_Pattern }, - { "datamap", &CSigsegvGameConf::AddrEntry_Load_DataDescMap }, - { "func knownvtidx", &CSigsegvGameConf::AddrEntry_Load_Func_KnownVTIdx }, - { "func datamap vthunk", &CSigsegvGameConf::AddrEntry_Load_Func_DataMap_VThunk }, - { "func ebpprologue uniref", &CSigsegvGameConf::AddrEntry_Load_Func_EBPPrologue_UniqueRef }, - { "func ebpprologue unistr", &CSigsegvGameConf::AddrEntry_Load_Func_EBPPrologue_UniqueStr }, - { "func ebpprologue unistr knownvtidx", &CSigsegvGameConf::AddrEntry_Load_Func_EBPPrologue_UniqueStr_KnownVTIdx }, - { "func ebpprologue vprof", &CSigsegvGameConf::AddrEntry_Load_Func_EBPPrologue_VProf }, + { "sym", &CSigsegvGameConf::AddrEntry_Load_Sym }, + { "sym regex", &CSigsegvGameConf::AddrEntry_Load_Sym_Regex }, + { "fixed", &CSigsegvGameConf::AddrEntry_Load_Fixed }, + { "pattern", &CSigsegvGameConf::AddrEntry_Load_Pattern }, + { "datamap", &CSigsegvGameConf::AddrEntry_Load_DataDescMap }, + { "func knownvtidx", &CSigsegvGameConf::AddrEntry_Load_Func_KnownVTIdx }, + { "func datamap vthunk", &CSigsegvGameConf::AddrEntry_Load_Func_DataMap_VThunk }, + { "func ebpprologue uniref", &CSigsegvGameConf::AddrEntry_Load_Func_EBPPrologue_UniqueRef }, + { "func ebpprologue unistr", &CSigsegvGameConf::AddrEntry_Load_Func_EBPPrologue_UniqueStr }, + { "func ebpprologue unistr knownvtidx", &CSigsegvGameConf::AddrEntry_Load_Func_EBPPrologue_UniqueStr_KnownVTIdx }, + { "func ebpprologue nonunistr knownvtidx", &CSigsegvGameConf::AddrEntry_Load_Func_EBPPrologue_NonUniqueStr_KnownVTIdx }, + { "func ebpprologue vprof", &CSigsegvGameConf::AddrEntry_Load_Func_EBPPrologue_VProf }, + { "convar", &CSigsegvGameConf::AddrEntry_Load_ConVar }, + { "concommand", &CSigsegvGameConf::AddrEntry_Load_ConCommand }, }; void AddrEntry_Load_Common(IAddr *addr); SMCResult AddrEntry_Load_Sym(); + SMCResult AddrEntry_Load_Sym_Regex(); SMCResult AddrEntry_Load_Fixed(); SMCResult AddrEntry_Load_Pattern(); SMCResult AddrEntry_Load_DataDescMap(); @@ -66,7 +71,12 @@ class CSigsegvGameConf : public ITextListener_SMC SMCResult AddrEntry_Load_Func_EBPPrologue_UniqueRef(); SMCResult AddrEntry_Load_Func_EBPPrologue_UniqueStr(); SMCResult AddrEntry_Load_Func_EBPPrologue_UniqueStr_KnownVTIdx(); + SMCResult AddrEntry_Load_Func_EBPPrologue_NonUniqueStr_KnownVTIdx(); SMCResult AddrEntry_Load_Func_EBPPrologue_VProf(); + + SMCResult AddrEntry_Load_ConCommandBase(bool is_command); + SMCResult AddrEntry_Load_ConVar() { return this->AddrEntry_Load_ConCommandBase(false); } + SMCResult AddrEntry_Load_ConCommand() { return this->AddrEntry_Load_ConCommandBase(true); } }; extern CSigsegvGameConf g_GCHook; diff --git a/src/library.cpp b/src/library.cpp index 3e35b9a2..eb1208c3 100644 --- a/src/library.cpp +++ b/src/library.cpp @@ -7,13 +7,42 @@ std::map LibMgr::s_LibHandles; static std::map libnames{ - { Library::INVALID, "invalid" }, - { Library::SERVER, "server" }, - { Library::ENGINE, "engine" }, - { Library::DEDICATED, "dedicated" }, - { Library::TIER0, "tier0" }, - { Library::CLIENT, "client" }, - { Library::VGUIMATSURFACE, "vguimatsurface" }, + { Library::INVALID, "invalid" }, + { Library::SERVER, "server" }, + { Library::ENGINE, "engine" }, + { Library::DEDICATED, "dedicated" }, + { Library::TIER0, "tier0" }, + { Library::CLIENT, "client" }, + { Library::VGUIMATSURFACE, "vguimatsurface" }, + { Library::MATERIALSYSTEM, "materialsystem" }, + { Library::SOUNDEMITTERSYSTEM, "soundemittersystem" }, + { Library::DATACACHE, "datacache" }, + { Library::VGUI, "vgui" }, + { Library::VPHYSICS, "vphysics" }, +}; + +static std::map segnames{ + { Segment::INVALID, "invalid" }, + { Segment::TEXT, "text" }, + { Segment::DATA, "data" }, + { Segment::RODATA, "rodata" }, + { Segment::BSS, "bss" }, +}; + +static std::map segnames_plat{ +#if defined _LINUX + { ".text", Segment::TEXT, }, + { ".data", Segment::DATA, }, + { ".rodata", Segment::RODATA, }, + { ".bss", Segment::BSS, }, +#elif defined _WINDOWS + { ".text", Segment::TEXT, }, + { ".data", Segment::DATA, }, + { ".rdata", Segment::RODATA, }, + { ".bss", Segment::BSS, }, +#elif defined _OSX + #error TODO +#endif }; @@ -64,37 +93,51 @@ void LibMgr::FindInfo(Library lib) memset(&dlinfo, 0x00, sizeof(dlinfo)); assert(g_MemUtils.GetLibraryInfo(GetPtr(lib), dlinfo)); - LibInfo info; - info.baseaddr = (uintptr_t)dlinfo.baseAddress; - info.len = (uintptr_t)dlinfo.memorySize; + LibInfo lib_info((uintptr_t)dlinfo.baseAddress, (uintptr_t)dlinfo.memorySize); #if defined _LINUX g_MemUtils.ForEachSection(s_LibHandles[lib], [&](const Elf32_Shdr *shdr, const char *name){ - SegInfo seg; - seg.off = shdr->sh_addr; - seg.len = shdr->sh_size; + SegInfo seg_info(shdr->sh_addr, shdr->sh_size); + seg_info.m_LibBaseAddr = lib_info.BaseAddr(); + + auto it = segnames_plat.find(name); + if (it != segnames_plat.end()) { + ConColorMsg(Color(0x00, 0xff, 0x00, 0xff), "Library %s: segment '%s' found: %s\n", + Lib_ToString(lib), name, Seg_ToString((*it).second)); + auto result = lib_info.m_SegmentsByType.insert(std::make_pair((*it).second, seg_info)); + assert(result.second); + } else { + ConColorMsg(Color(0xff, 0x00, 0x00, 0xff), "Library %s: segment '%s' not found!\n", + Lib_ToString(lib), name); + } - info.segs[name] = seg; + auto result = lib_info.m_SegmentsByName.insert(std::make_pair(name, seg_info)); + assert(result.second); }); #elif defined _WINDOWS g_MemUtils.ForEachSection(s_LibHandles[lib], [&](const IMAGE_SECTION_HEADER *pSectHdr){ - SegInfo seg; - seg.off = pSectHdr->VirtualAddress; - seg.len = pSectHdr->Misc.VirtualSize; + SegInfo seg_info(pSectHdr->VirtualAddress, pSectHdr->Misc.VirtualSize); + seg_info.m_LibBaseAddr = lib_info.BaseAddr(); auto name = (const char *)pSectHdr->Name; - info.segs[name] = seg; + + auto it = segnames_plat.find(name); + if (it != segnames_plat.end()) { + auto result = lib_info.m_SegmentsByType.insert(std::make_pair((*it).second, seg_info)); + assert(result.second); + } + + auto result = lib_info.m_SegmentsByName.insert(std::make_pair(name, seg_info)); + assert(result.second); }); #endif - s_LibInfos[lib] = info; + auto result = s_LibInfos.insert(std::make_pair(lib, lib_info)); + assert(result.second); - DevMsg("Library %-34s [ %08x %08x ]\n", libnames.at(lib), info.baseaddr, info.baseaddr + info.len); - for (const auto& pair : info.segs) { - DevMsg(" %-40s [ %08x %08x ]\n", - pair.first.c_str(), - info.baseaddr + pair.second.off, - info.baseaddr + pair.second.off + pair.second.len); + DevMsg("Library %-34s [ %08x %08x ]\n", libnames.at(lib), lib_info.AddrBegin(), lib_info.AddrEnd()); + for (const auto& pair : lib_info.m_SegmentsByName) { + DevMsg(" %-40s [ %08x %08x ]\n", pair.first.c_str(), pair.second.AddrBegin(), pair.second.AddrEnd()); } } @@ -109,18 +152,21 @@ void *LibMgr::FindSym(Library lib, const char *sym) return g_MemUtils.ResolveSymbol(handle, sym); } -std::tuple LibMgr::FindSymRegex(Library lib, const char *pattern, std::regex::flag_type flags) +std::tuple LibMgr::FindSymRegex(Library lib, const char *pattern) { - std::regex filter(pattern); + std::regex filter(pattern, std::regex_constants::ECMAScript); std::vector matches; LibMgr::ForEachSym(lib, [&](Symbol *sym){ std::string name(sym->buffer(), sym->length); - if (std::regex_search(name, filter, std::regex_constants::match_any)) { + if (std::regex_match(name, filter, std::regex_constants::match_any)) { matches.push_back(sym); + if (matches.size() > 1) return false; } + + return true; }); if (matches.size() == 1) { @@ -131,13 +177,6 @@ std::tuple LibMgr::FindSymRegex(Library lib, const ch } } -void LibMgr::ForEachSym(Library lib, const std::function& functor) -{ - void *handle = s_LibHandles.at(lib); - assert(handle != nullptr); - g_MemUtils.ForEachSymbol(handle, functor); -} - #if defined _LINUX || defined _OSX @@ -201,9 +240,9 @@ Library LibMgr::WhichLibAtAddr(void *ptr) Library lib = pair.first; if (lib == Library::INVALID) continue; - const LibInfo& info = GetInfo(lib); + const LibInfo& lib_info = GetInfo(lib); - if (addr >= info.baseaddr && addr < info.baseaddr + info.len) { + if (GetInfo(lib).ContainsAddr(addr, 1)) { return lib; } } @@ -212,7 +251,7 @@ Library LibMgr::WhichLibAtAddr(void *ptr) } -Library LibMgr::FromString(const char *str) +Library LibMgr::Lib_FromString(const char *str) { for (const auto& pair : libnames) { if (stricmp(pair.second, str) == 0) { @@ -223,7 +262,7 @@ Library LibMgr::FromString(const char *str) return Library::INVALID; } -const char *LibMgr::ToString(Library lib) +const char *LibMgr::Lib_ToString(Library lib) { for (const auto& pair : libnames) { if (pair.first == lib) { @@ -234,7 +273,7 @@ const char *LibMgr::ToString(Library lib) return libnames.at(Library::INVALID); } -size_t LibMgr::MaxStringLen() +size_t LibMgr::Lib_MaxStringLen() { size_t max = 0; @@ -244,3 +283,37 @@ size_t LibMgr::MaxStringLen() return max; } + + +Segment LibMgr::Seg_FromString(const char *str) +{ + for (const auto& pair : segnames) { + if (stricmp(pair.second, str) == 0) { + return pair.first; + } + } + + return Segment::INVALID; +} + +const char *LibMgr::Seg_ToString(Segment seg) +{ + for (const auto& pair : segnames) { + if (pair.first == seg) { + return pair.second; + } + } + + return segnames.at(Segment::INVALID); +} + +size_t LibMgr::Seg_MaxStringLen() +{ + size_t max = 0; + + for (const auto& pair : segnames) { + max = Max(max, strlen(pair.second)); + } + + return max; +} diff --git a/src/library.h b/src/library.h index 060321eb..e53ca473 100644 --- a/src/library.h +++ b/src/library.h @@ -11,33 +11,105 @@ enum class Library : int TIER0, CLIENT, VGUIMATSURFACE, + MATERIALSYSTEM, + SOUNDEMITTERSYSTEM, + DATACACHE, + VGUI, + VPHYSICS, }; -#if 0 enum class Segment : int { - SEG_TEXT, - SEG_DATA, - SEG_RODATA, - SEG_BSS, + INVALID, + TEXT, + DATA, + RODATA, + BSS, }; -#endif -struct SegInfo +class SegInfo { - uintptr_t off; - uintptr_t len; +public: + SegInfo(uintptr_t offset, uintptr_t length) : + m_Offset(offset), m_Length(length) {} + + uintptr_t Offset() const { return this->m_Offset; } + uintptr_t Length() const { return this->m_Length; } + + uintptr_t AddrBegin() const { return this->m_LibBaseAddr + this->m_Offset; } + uintptr_t AddrEnd() const { return this->m_LibBaseAddr + this->m_Offset + this->m_Length; } + + bool ContainsAddr(uintptr_t addr, uintptr_t len = 0) const + { + uintptr_t range_begin = addr; + uintptr_t range_end = addr + len; + + return (range_begin >= this->AddrBegin()) && (range_end <= this->AddrEnd()); + } + template bool ContainsAddr(const T *addr, uintptr_t len = 0) const + { + return this->ContainsAddr((uintptr_t)addr, len); + } + +private: + uintptr_t m_Offset; + uintptr_t m_Length; + + uintptr_t m_LibBaseAddr = 0x00000000; + + friend class LibMgr; }; -struct LibInfo +class LibInfo { - uintptr_t baseaddr; - uintptr_t len; +public: + LibInfo(uintptr_t baseaddr, uintptr_t length) : + m_BaseAddr(baseaddr), m_Length(length) {} + + uintptr_t BaseAddr() const { return this->m_BaseAddr; } + uintptr_t Length() const { return this->m_Length; } + + uintptr_t AddrBegin() const { return this->m_BaseAddr; } + uintptr_t AddrEnd() const { return this->m_BaseAddr + this->m_Length; } - std::map segs; + const SegInfo& GetSeg(Segment seg_type) const + { + auto it = this->m_SegmentsByType.find(seg_type); + assert(it != this->m_SegmentsByType.end()); + + return (*it).second; + } + const SegInfo& GetSeg(const char *seg_name) const + { + auto it = this->m_SegmentsByName.find(seg_name); + assert(it != this->m_SegmentsByName.end()); + + return (*it).second; + } + + bool ContainsAddr(uintptr_t addr, uintptr_t len = 0) const + { + uintptr_t range_begin = addr; + uintptr_t range_end = addr + len; + + return (range_begin >= this->AddrBegin()) && (range_end <= this->AddrEnd()); + } + template bool ContainsAddr(const T *addr, uintptr_t len = 0) const + { + return this->ContainsAddr((uintptr_t)addr, len); + } + +private: + uintptr_t m_BaseAddr; + uintptr_t m_Length; + + std::map m_SegmentsByType; + std::map m_SegmentsByName; + + friend class LibMgr; }; @@ -58,14 +130,25 @@ class LibMgr static const LibInfo& GetInfo(Library lib); static void *FindSym(Library lib, const char *sym); - static std::tuple FindSymRegex(Library lib, const char *pattern, std::regex::flag_type flags = std::regex::ECMAScript | std::regex::icase); - static void ForEachSym(Library lib, const std::function& functor); + static std::tuple FindSymRegex(Library lib, const char *pattern); + + template + static void ForEachSym(Library lib, FUNCTOR&& functor) + { + void *handle = s_LibHandles.at(lib); + assert(handle != nullptr); + g_MemUtils.ForEachSymbol(handle, functor); + } static Library WhichLibAtAddr(void *ptr); - static Library FromString(const char *str); - static const char *ToString(Library lib); - static size_t MaxStringLen(); + static Library Lib_FromString(const char *str); + static const char *Lib_ToString(Library lib); + static size_t Lib_MaxStringLen(); + + static Segment Seg_FromString(const char *str); + static const char *Seg_ToString(Segment lib); + static size_t Seg_MaxStringLen(); private: LibMgr() {} diff --git a/src/link/link.h b/src/link/link.h index f17749e4..618d30d9 100644 --- a/src/link/link.h +++ b/src/link/link.h @@ -56,11 +56,13 @@ class StaticFuncThunk : public ILinkage RET operator()(PARAMS... args) const { - assert(this->m_pFuncPtr != nullptr); - return (*this->m_pFuncPtr)(args...); + assert(this->GetFuncPtr() != nullptr); + return (*this->GetFuncPtr())(args...); } private: + FPtr GetFuncPtr() const { return this->m_pFuncPtr; } + const char *m_pszFuncName; FPtr m_pFuncPtr = nullptr; diff --git a/src/mem/detour.cpp b/src/mem/detour.cpp index 385f79f8..1ad6b45e 100644 --- a/src/mem/detour.cpp +++ b/src/mem/detour.cpp @@ -3,6 +3,7 @@ #include "mem/protect.h" #include "mem/opcode.h" #include "util/backtrace.h" +#include "util/demangle.h" #if !(defined(__i386) || defined(_M_IX86)) @@ -167,11 +168,11 @@ bool IDetour_SymRegex::DoLoad() return false; } - const LibInfo& info = LibMgr::GetInfo(this->m_Library); - void *text_begin = (void *)(info.baseaddr + info.segs.at(".text").off); - void *text_end = (void *)((uintptr_t)text_begin + info.segs.at(".text").len); + const SegInfo& info_seg_text = LibMgr::GetInfo(this->m_Library).GetSeg(Segment::TEXT); + auto text_begin = reinterpret_cast(info_seg_text.AddrBegin()); + auto text_end = reinterpret_cast(info_seg_text.AddrEnd()); - std::regex filter(this->m_strPattern); + std::regex filter(this->m_strPattern, std::regex_constants::ECMAScript); std::vector syms; LibMgr::ForEachSym(this->m_Library, [&](Symbol *sym){ const char *it_begin = sym->buffer(); @@ -181,6 +182,8 @@ bool IDetour_SymRegex::DoLoad() syms.push_back(sym); } } + + return true; }); if (syms.size() != 1) { @@ -195,7 +198,7 @@ bool IDetour_SymRegex::DoLoad() this->m_strSymbol = std::string(syms[0]->buffer(), syms[0]->length); this->m_pFunc = syms[0]->address; - this->Demangle(); + DemangleName(this->m_strSymbol.c_str(), this->m_strDemangled); return true; } @@ -206,22 +209,6 @@ void IDetour_SymRegex::DoUnload() } -void IDetour_SymRegex::Demangle() -{ -#if defined _LINUX || defined _OSX - const char *demangled = cplus_demangle(this->m_strSymbol.c_str(), DMGL_GNU_V3 | DMGL_TYPES | DMGL_ANSI | DMGL_PARAMS); - if (demangled != nullptr) { - this->m_strDemangled = demangled; - free((void *)demangled); - } else { - this->m_strDemangled = this->m_strSymbol; - } -#else - this->m_strDemangled = this->m_strSymbol; -#endif -} - - bool CDetour::DoLoad() { if (!IDetour_SymNormal::DoLoad()) { @@ -520,6 +507,9 @@ void CDetouredFunc::StorePrologue() size_t n_bytes = copy_bytes((unsigned char *)this->m_pFunc, nullptr, JmpRelImm32::Size()); assert(n_bytes >= JmpRelImm32::Size()); + // this assert will fail in some cases if you've set a breakpoint in gdb on + // the function in question, because copy_bytes stops early if it hits an + // int3 instruction this->m_Prologue.resize(n_bytes); memcpy(this->m_Prologue.data(), this->m_pFunc, n_bytes); diff --git a/src/mem/detour.h b/src/mem/detour.h index 77c251ca..4e85eed0 100644 --- a/src/mem/detour.h +++ b/src/mem/detour.h @@ -78,8 +78,6 @@ class IDetour_SymRegex : public IDetour virtual void DoUnload() override; private: - void Demangle(); - Library m_Library; std::string m_strPattern; diff --git a/src/mem/patch.cpp b/src/mem/patch.cpp index 4e053436..8c82ac59 100644 --- a/src/mem/patch.cpp +++ b/src/mem/patch.cpp @@ -3,19 +3,24 @@ #include "mem/protect.h" -bool IPatch::Init() +bool CPatch::Init() { this->m_pszFuncName = this->GetFuncName(); this->m_iFuncOffMin = this->GetFuncOffMin(); this->m_iFuncOffMax = this->GetFuncOffMax(); if (this->Verbose()) { - DevMsg("IPatch::Init: \"%s\" %s\n", this->m_pszFuncName, typeid(*this).name()); + DevMsg("CPatch::Init: \"%s\" %s\n", this->m_pszFuncName, typeid(*this).name()); } this->m_pFuncAddr = AddrManager::GetAddr(this->m_pszFuncName); if (this->m_pFuncAddr == nullptr) { - DevMsg("IPatch::Init: FAIL: no addr for \"%s\"\n", this->m_pszFuncName); + DevMsg("CPatch::Init: FAIL: no addr for \"%s\"\n", this->m_pszFuncName); + return false; + } + + if (!this->PostInit()) { + DevMsg("CPatch::Init: FAIL: \"%s\": PostInit returned false\n", this->m_pszFuncName); return false; } @@ -23,20 +28,20 @@ bool IPatch::Init() this->m_MaskPatch.SetAll(0x00); if (!this->GetVerifyInfo(this->m_BufVerify, this->m_MaskVerify)) { - DevMsg("IPatch::Init: FAIL: \"%s\": GetVerifyInfo returned false\n", this->m_pszFuncName); + DevMsg("CPatch::Init: FAIL: \"%s\": GetVerifyInfo returned false\n", this->m_pszFuncName); return false; } this->m_BufPatch.CopyFrom(this->m_BufVerify); if (!this->GetPatchInfo(this->m_BufPatch, this->m_MaskPatch)) { - DevMsg("IPatch::Init: FAIL: \"%s\": GetPatchInfo returned false\n", this->m_pszFuncName); + DevMsg("CPatch::Init: FAIL: \"%s\": GetPatchInfo returned false\n", this->m_pszFuncName); return false; } return true; } -bool IPatch::Check() +bool CPatch::Check() { using PatchScanner = CMaskedScanner; @@ -44,33 +49,35 @@ bool IPatch::Check() uintptr_t addr_max = (uintptr_t)this->m_pFuncAddr + this->m_iFuncOffMax + this->m_iLength + 1; if (this->Verbose()) { - DevMsg("IPatch::Check: \"%s\" %s\n", this->m_pszFuncName, typeid(*this).name()); - DevMsg("IPatch::Check: func %08x\n", (uintptr_t)this->m_pFuncAddr); - DevMsg("IPatch::Check: off_min %04x\n", this->m_iFuncOffMin); - DevMsg("IPatch::Check: off_max %04x\n", this->m_iFuncOffMax); - DevMsg("IPatch::Check: addr_min %08x\n", addr_min); - DevMsg("IPatch::Check: addr_max %08x\n", addr_max); + DevMsg("CPatch::Check: \"%s\" %s\n", this->m_pszFuncName, typeid(*this).name()); + DevMsg("CPatch::Check: func %08x\n", (uintptr_t)this->m_pFuncAddr); + DevMsg("CPatch::Check: off_min %04x\n", this->m_iFuncOffMin); + DevMsg("CPatch::Check: off_max %04x\n", this->m_iFuncOffMax); + DevMsg("CPatch::Check: addr_min %08x\n", addr_min); + DevMsg("CPatch::Check: addr_max %08x\n", addr_max); } CScan scan(CAddrAddrBounds((void *)addr_min, (void *)addr_max), this->m_BufVerify, this->m_MaskVerify); if (!scan.ExactlyOneMatch()) { - DevMsg("IPatch::Check: FAIL: \"%s\": found %u matching regions\n", this->m_pszFuncName, scan.Matches().size()); + DevMsg("CPatch::Check: FAIL: \"%s\": found %u matching regions\n", this->m_pszFuncName, scan.Matches().size()); return false; } this->m_bFoundOffset = true; this->m_iFuncOffActual = (uintptr_t)scan.FirstMatch() - (uintptr_t)this->m_pFuncAddr; - DevMsg("IPatch::Check: OK: \"%s\": actual offset +0x%04x\n", this->m_pszFuncName, this->m_iFuncOffActual); + DevMsg("CPatch::Check: OK: \"%s\": actual offset +0x%04x\n", this->m_pszFuncName, this->m_iFuncOffActual); return true; } -void IPatch::Apply() +void CPatch::Apply() { + if (this->VerifyOnly()) return; + if (this->Verbose()) { - DevMsg("IPatch::Apply: \"%s\" %s\n", this->m_pszFuncName, typeid(*this).name()); + DevMsg("CPatch::Apply: \"%s\" %s\n", this->m_pszFuncName, typeid(*this).name()); } if (this->m_bApplied) { @@ -78,7 +85,7 @@ void IPatch::Apply() } if (!this->m_bFoundOffset) { - DevWarning("IPatch::Apply: haven't found actual offset yet!\n"); + DevWarning("CPatch::Apply: haven't found actual offset yet!\n"); return; } @@ -100,10 +107,12 @@ void IPatch::Apply() this->m_bApplied = true; } -void IPatch::UnApply() +void CPatch::UnApply() { + if (this->VerifyOnly()) return; + if (this->Verbose()) { - DevMsg("IPatch::UnApply: \"%s\" %s\n", this->m_pszFuncName, typeid(*this).name()); + DevMsg("CPatch::UnApply: \"%s\" %s\n", this->m_pszFuncName, typeid(*this).name()); } if (!this->m_bApplied) { @@ -111,7 +120,7 @@ void IPatch::UnApply() } if (!this->m_bFoundOffset) { - DevWarning("IPatch::UnApply: haven't found actual offset yet!\n"); + DevWarning("CPatch::UnApply: haven't found actual offset yet!\n"); return; } @@ -132,13 +141,13 @@ void IPatch::UnApply() } -uint32_t IPatch::GetActualOffset() const +uint32_t CPatch::GetActualOffset() const { if (!this->m_bFoundOffset) return -1; return this->m_iFuncOffActual; } -void *IPatch::GetActualLocation() const +void *CPatch::GetActualLocation() const { if (!this->m_bFoundOffset) return nullptr; return (void *)((uintptr_t)this->m_pFuncAddr + this->m_iFuncOffActual); diff --git a/src/mem/patch.h b/src/mem/patch.h index 198bdde3..1014a746 100644 --- a/src/mem/patch.h +++ b/src/mem/patch.h @@ -11,34 +11,56 @@ class IPatch public: virtual ~IPatch() {} - virtual bool VerifyOnly() const { return false; } + virtual bool Verbose() const = 0; + virtual bool VerifyOnly() const = 0; - bool Init(); - bool Check(); + virtual bool Init() = 0; + virtual bool Check() = 0; - virtual void Apply(); - virtual void UnApply(); + virtual void Apply() = 0; + virtual void UnApply() = 0; - virtual int GetLength() const final { return this->m_iLength; } +protected: + IPatch() {} +}; + + +class CPatch : public IPatch +{ +public: + virtual ~CPatch() {} + + virtual bool Verbose() const override { return false; } + virtual bool VerifyOnly() const override { return false; } + + virtual bool Init() override final; + virtual bool Check() override final; + + virtual void Apply() override final; + virtual void UnApply() override final; + + int GetLength() const { return this->m_iLength; } virtual const char *GetFuncName() const = 0; virtual uint32_t GetFuncOffMin() const = 0; virtual uint32_t GetFuncOffMax() const = 0; - virtual bool Verbose() const { return false; } - uint32_t GetActualOffset() const; void *GetActualLocation() const; protected: - IPatch(int len) : + CPatch(int len) : m_iLength(len), m_BufVerify(len), m_BufPatch(len), m_MaskVerify(len), m_MaskPatch(len), m_BufRestore(len) {} + virtual bool PostInit() { return true; } + virtual bool GetVerifyInfo(ByteBuf& buf, ByteBuf& mask) const = 0; virtual bool GetPatchInfo(ByteBuf& buf, ByteBuf& mask) const = 0; + void *GetFuncAddr() const { return this->m_pFuncAddr; } + private: const int m_iLength; @@ -61,18 +83,15 @@ class IPatch }; -class IVerify : public IPatch +class CVerify : public CPatch { public: - virtual ~IVerify() {} + virtual ~CVerify() {} virtual bool VerifyOnly() const override final { return true; } - virtual void Apply() override final {} - virtual void UnApply() override final {} - protected: - IVerify(int len) : IPatch(len) {} + CVerify(int len) : CPatch(len) {} virtual bool GetPatchInfo(ByteBuf& buf, ByteBuf& mask) const override final { return true; } }; diff --git a/src/mem/scan.cpp b/src/mem/scan.cpp index 47575960..7e1be457 100644 --- a/src/mem/scan.cpp +++ b/src/mem/scan.cpp @@ -3,19 +3,18 @@ CLibBounds::CLibBounds(Library lib) { - const LibInfo& info = LibMgr::GetInfo(lib); + const LibInfo& lib_info = LibMgr::GetInfo(lib); - this->m_AddrLow = (const void *)info.baseaddr; - this->m_AddrHigh = (const void *)(info.baseaddr + info.len); + this->m_AddrLow = reinterpret_cast(lib_info.AddrBegin()); + this->m_AddrHigh = reinterpret_cast(lib_info.AddrEnd()); } -CLibSegBounds::CLibSegBounds(Library lib, const char *seg) +CLibSegBounds::CLibSegBounds(Library lib, Segment seg) { - const LibInfo& l_info = LibMgr::GetInfo(lib); - const SegInfo& s_info = l_info.segs.at(seg); + const SegInfo& seg_info = LibMgr::GetInfo(lib).GetSeg(seg); - this->m_AddrLow = (const void *)(l_info.baseaddr + s_info.off); - this->m_AddrHigh = (const void *)(l_info.baseaddr + s_info.off + s_info.len); + this->m_AddrLow = reinterpret_cast(seg_info.AddrBegin()); + this->m_AddrHigh = reinterpret_cast(seg_info.AddrEnd()); } @@ -25,7 +24,7 @@ namespace Scan { using StrScanner = CStringScanner; - CScan scan(CLibSegBounds(lib, ".rdata"), str); + CScan scan(CLibSegBounds(lib, Segment::RODATA), str); if (scan.ExactlyOneMatch()) { return (const char *)scan.FirstMatch(); } diff --git a/src/mem/scan.h b/src/mem/scan.h index ab9c7802..4ad47ffa 100644 --- a/src/mem/scan.h +++ b/src/mem/scan.h @@ -78,7 +78,7 @@ class CLibBounds : public IBounds class CLibSegBounds : public IBounds { public: - CLibSegBounds(Library lib, const char *seg); + CLibSegBounds(Library lib, Segment seg); virtual const void *GetLowerBound() const override { return this->m_AddrLow; } virtual const void *GetUpperBound() const override { return this->m_AddrHigh; } @@ -155,29 +155,24 @@ inline bool CBasicScanner::CheckOne(const void *where) } -template -class CTypeScanner : public IScanner +template +class CTypeScanner : public CBasicScanner { public: CTypeScanner(const IBounds& bounds, const T& seek) : - IScanner(bounds, sizeof(T)), m_Seek(seek) {} - - bool CheckOne(const void *where); - -private: - const T m_Seek; + CBasicScanner(bounds, reinterpret_cast(&seek), sizeof(T)) {} + virtual ~CTypeScanner() {} }; -template -inline bool CTypeScanner::CheckOne(const void *where) + +template +class CAlignedTypeScanner : public CTypeScanner { - if (*reinterpret_cast(where) == this->m_Seek) { - this->AddMatch(where); - return true; - } else { - return false; - } -} +public: + CAlignedTypeScanner(const IBounds& bounds, const T& seek) : + CTypeScanner(bounds, seek) {} + virtual ~CAlignedTypeScanner() {} +}; template diff --git a/src/mod/bot/medieval_nonmelee.cpp b/src/mod/bot/medieval_nonmelee.cpp index 048c2ee1..11da6a08 100644 --- a/src/mod/bot/medieval_nonmelee.cpp +++ b/src/mod/bot/medieval_nonmelee.cpp @@ -11,12 +11,20 @@ namespace Mod_Bot_Medieval_NonMelee 0x75, 0x00, // +000C jnz +0xXX }; - struct IPatch_CTFBot_EquipRequiredWeapon : public IPatch + struct CPatch_CTFBot_EquipRequiredWeapon : public CPatch { - IPatch_CTFBot_EquipRequiredWeapon() : IPatch(sizeof(s_Buf)) {} + CPatch_CTFBot_EquipRequiredWeapon() : CPatch(sizeof(s_Buf)) {} virtual const char *GetFuncName() const override { return "CTFBot::EquipRequiredWeapon"; } +#if defined _LINUX + virtual uint32_t GetFuncOffMin() const override { return 0x0000; } + virtual uint32_t GetFuncOffMax() const override { return 0x0100; } // @ 0x00b0 +#elif defined _WINDOWS + virtual uint32_t GetFuncOffMin() const override { return 0x0000; } + virtual uint32_t GetFuncOffMax() const override { return 0x00c0; } // @ 0x0071 +#endif + virtual bool GetVerifyInfo(ByteBuf& buf, ByteBuf& mask) const override { buf.CopyFrom(s_Buf); @@ -42,24 +50,6 @@ namespace Mod_Bot_Medieval_NonMelee } }; -#if defined _LINUX - - struct CPatch_CTFBot_EquipRequiredWeapon : public IPatch_CTFBot_EquipRequiredWeapon - { - virtual uint32_t GetFuncOffMin() const override { return 0x0000; } - virtual uint32_t GetFuncOffMax() const override { return 0x0100; } // @ 0x00b0 - }; - -#elif defined _WINDOWS - - struct CPatch_CTFBot_EquipRequiredWeapon : public IPatch_CTFBot_EquipRequiredWeapon - { - virtual uint32_t GetFuncOffMin() const override { return 0x0000; } - virtual uint32_t GetFuncOffMax() const override { return 0x00c0; } // @ 0x0071 - }; - -#endif - class CMod : public IMod { diff --git a/src/mod/bot/runfast.cpp b/src/mod/bot/runfast.cpp index 2c1cc1e6..8e699d44 100644 --- a/src/mod/bot/runfast.cpp +++ b/src/mod/bot/runfast.cpp @@ -21,9 +21,9 @@ namespace Mod_Bot_RunFast }; static_assert(sizeof(s_Buf_Verify) == sizeof(s_Buf_Patch), "size mismatch"); - struct CPatch_CTFBotPushToCapturePoint_Update : public IPatch + struct CPatch_CTFBotPushToCapturePoint_Update : public CPatch { - CPatch_CTFBotPushToCapturePoint_Update() : IPatch(sizeof(s_Buf_Verify)) {} + CPatch_CTFBotPushToCapturePoint_Update() : CPatch(sizeof(s_Buf_Verify)) {} virtual const char *GetFuncName() const override { return "CTFBotPushToCapturePoint::Update"; } virtual uint32_t GetFuncOffMin() const override { return 0x0000; } diff --git a/src/mod/cond/enhanced_cmds.cpp b/src/mod/cond/enhanced_cmds.cpp new file mode 100644 index 00000000..5439cc98 --- /dev/null +++ b/src/mod/cond/enhanced_cmds.cpp @@ -0,0 +1,136 @@ +#include "mod.h" +#include "stub/tfplayer.h" +#include "util/misc.h" + + +namespace Mod_Cond_Enhanced_Cmds +{ + // existing addcond syntax: + // addcond [condnum] [duration] [player name substring] + + // existing removecond syntax: + // removecond [condnum] [player name substring] + + + // desired features: + // - allow hex cond number input + // - allow condition name input + // - regex selection of target player(s) + // - apply to multiple players at once if regex matches multiple + // - print what was done to console (with both cond num and name, plus duration for addcond) + // - on invalid syntax or bad cond etc, print Warning message + // - have feature to print list of conds (number + name): maybe "listconds" + + + // ETFCond GetTFConditionFromName(const char *name); + // const char *GetTFConditionName(ETFCond cond); + + + DETOUR_DECL_MEMBER(bool, CTFPlayer_ClientCommand, const CCommand& args) + { + if (FStrEq(args[0], "addcond")) { + // int iMaxCondNum = GetNumberOfTFConds() - 1; + + #warning TODO: Cond:Enhanced_Cmds + // ... + + return true; + } + + if (FStrEq(args[0], "removecond")) { + // int iMaxCondNum = GetNumberOfTFConds() - 1; + + #warning TODO: Cond:Enhanced_Cmds + // ... + + return true; + } + + if (FStrEq(args[0], "listconds")) { + // int iMaxCondNum = GetNumberOfTFConds() - 1; + + #warning TODO: Cond:Enhanced_Cmds + + for (int i = 0; IsValidTFConditionNumber(i); ++i) { + // ... + } + + return true; + } + + return DETOUR_MEMBER_CALL(CTFPlayer_ClientCommand)(args); + } + + + class CMod : public IMod + { + public: + CMod() : IMod("Cond:Enhanced_Cmds") + { + MOD_ADD_DETOUR_MEMBER(CTFPlayer_ClientCommand, "CTFPlayer::ClientCommand"); + } + }; + CMod s_Mod; + + + ConVar cvar_enable("sig_cond_enhanced_cmds", "0", FCVAR_NOTIFY, + "Mod: enhance addcond and removecond", + [](IConVar *pConVar, const char *pOldValue, float flOldValue) { + ConVarRef var(pConVar); + s_Mod.Toggle(var.GetBool()); + }); +} + + +// existing implementation +#if 0 +bool CTFPlayer::ClientCommand(const CCommand& args) +{ + // ... + + if (FStrEq(args[0], "addcond")) { + if (sv_cheats.GetBool() && args.ArgC() >= 2) { + CTFPlayer *pTarget = this; + if (args.ArgC() >= 4) { + for (int i = 1; i <= gpGlobals->maxClients; ++i) { + CTFPlayer *pPlayer = ToTFPlayer(UTIL_PlayerByIndex(i)); + if (pPlayer == nullptr) continue; + + if (V_strstr(pPlayer->GetPlayerName(), args[3]) != nullptr) { + pTarget = pPlayer; + break; + } + } + } + + int iCond = Clamp(strtol(args[1], nullptr, 10), 0, TF_COND_LAST - 1)); + float flDuration = (args.ArgC() >= 3 ? strtod(args[2], nullptr) : -1.0f); + pTarget->m_Shared.AddCond(iCond, flDuration); + } + return true; + } + + if (FStrEq(args[0], "removecond")) { + if (sv_cheats.GetBool() && args.ArgC() >= 2) { + CTFPlayer *pTarget = this; + if (args.ArgC() >= 3) { + for (int i = 1; i <= gpGlobals->maxClients; ++i) { + CTFPlayer *pPlayer = ToTFPlayer(UTIL_PlayerByIndex(i)); + if (pPlayer == nullptr) continue; + + if (V_strstr(pPlayer->GetPlayerName(), args[3]) != nullptr) { + pTarget = pPlayer; + break; + } + } + } + + int iCond = Clamp(strtol(args[1], nullptr, 10), 0, TF_COND_LAST - 1)); + pTarget->m_Shared.RemoveCond(iCond); + } + return true; + } + + // ... +} +#endif diff --git a/src/mod/cond/reprogrammed.cpp b/src/mod/cond/reprogrammed.cpp index c8e6747c..2e8d5d9c 100644 --- a/src/mod/cond/reprogrammed.cpp +++ b/src/mod/cond/reprogrammed.cpp @@ -15,9 +15,9 @@ namespace Mod_Cond_Reprogrammed 0xe8, // +001B call CollectPlayers }; - struct CPatch_CMissionPopulator_UpdateMission : public IPatch + struct CPatch_CMissionPopulator_UpdateMission : public CPatch { - CPatch_CMissionPopulator_UpdateMission() : IPatch(sizeof(s_Buf_UpdateMission)) {} + CPatch_CMissionPopulator_UpdateMission() : CPatch(sizeof(s_Buf_UpdateMission)) {} virtual const char *GetFuncName() const override { return "CMissionPopulator::UpdateMission"; } virtual uint32_t GetFuncOffMin() const override { return 0x0000; } @@ -55,9 +55,9 @@ namespace Mod_Cond_Reprogrammed // }; using FPtr_IsBot = bool (CBasePlayer:: *)() const; - struct CPatch_CTFGameMovement_CheckStuck : public IPatch + struct CPatch_CTFGameMovement_CheckStuck : public CPatch { - CPatch_CTFGameMovement_CheckStuck() : IPatch(sizeof(s_Buf_CheckStuck)) {} + CPatch_CTFGameMovement_CheckStuck() : CPatch(sizeof(s_Buf_CheckStuck)) {} virtual const char *GetFuncName() const override { return "CTFGameMovement::CheckStuck"; } virtual uint32_t GetFuncOffMin() const override { return 0x0000; } @@ -102,6 +102,47 @@ namespace Mod_Cond_Reprogrammed "Mod: make some tweaks to TF_COND_REPROGRAMMED that Hell-met requested"); + void ChangeWeaponAndWearableTeam(CTFPlayer *player, int team) + { + // DevMsg("ChangeWeaponAndWearableTeam (#%d \"%s\"): teamnum %d => %d\n", + // ENTINDEX(player), player->GetPlayerName(), player->GetTeamNumber(), team); + + for (int i = player->WeaponCount() - 1; i >= 0; --i) { + CBaseCombatWeapon *weapon = player->GetWeapon(i); + if (weapon == nullptr) continue; + + int pre_team = weapon->GetTeamNumber(); + int pre_skin = weapon->m_nSkin; + + weapon->ChangeTeam(team); + weapon->m_nSkin = (team == TF_TEAM_BLUE ? 1 : 0); + + int post_team = weapon->GetTeamNumber(); + int post_skin = weapon->m_nSkin; + + // DevMsg(" weapon %d (#%d \"%s\"): [Team:%d Skin:%d] => [Team:%d Skin:%d]\n", + // i, ENTINDEX(weapon), weapon->GetClassname(), pre_team, pre_skin, post_team, post_skin); + } + + for (int i = player->GetNumWearables() - 1; i >= 0; --i) { + CEconWearable *wearable = player->GetWearable(i); + if (wearable == nullptr) continue; + + int pre_team = wearable->GetTeamNumber(); + int pre_skin = wearable->m_nSkin; + + wearable->ChangeTeam(team); + wearable->m_nSkin = (team == TF_TEAM_BLUE ? 1 : 0); + + int post_team = wearable->GetTeamNumber(); + int post_skin = wearable->m_nSkin; + + // DevMsg(" wearable %d (#%d \"%s\"): [Team:%d Skin:%d] => [Team:%d Skin:%d]\n", + // i, ENTINDEX(wearable), wearable->GetClassname(), pre_team, pre_skin, post_team, post_skin); + } + } + + void OnAddReprogrammed(CTFPlayer *player) { DevMsg("OnAddReprogrammed(#%d \"%s\")\n", ENTINDEX(player), player->GetPlayerName()); @@ -112,6 +153,11 @@ namespace Mod_Cond_Reprogrammed player->ForceChangeTeam(TF_TEAM_RED, false); + /* ensure that all weapons and wearables have their colors updated */ + if (cvar_hellmet.GetBool()) { + ChangeWeaponAndWearableTeam(player, TF_TEAM_RED); + } + /* this used to be in CTFPlayerShared::OnAddReprogrammed on the client * side, but we now have to do it from the server side */ if (!cvar_hellmet.GetBool()) { @@ -132,6 +178,20 @@ namespace Mod_Cond_Reprogrammed player->ForceChangeTeam(TF_TEAM_BLUE, false); + /* ensure that all weapons and wearables have their colors updated; + * we don't do this in the case of LIFE_DYING, however, because + * CTFPlayer::Event_Killed calls CTFPlayerShared::RemoveAllCond, and we + * end up making the ragdoll wearables and dropped hats etc the wrong + * color compared to the player ragdoll itself */ + if (cvar_hellmet.GetBool()) { + if (player->m_lifeState == LIFE_DYING) { + // hack hack hack: make wearable gibs be red + player->m_nSkin = 0; + } else { + ChangeWeaponAndWearableTeam(player, TF_TEAM_BLUE); + } + } + /* this is far from ideal; we can only remove ALL particle effects from * the server side */ if (!cvar_hellmet.GetBool()) { diff --git a/src/mod/credits/magnet_disable.cpp b/src/mod/credits/magnet_disable.cpp index 4fe9848e..b28afc0c 100644 --- a/src/mod/credits/magnet_disable.cpp +++ b/src/mod/credits/magnet_disable.cpp @@ -16,9 +16,9 @@ namespace Mod_Credits_Magnet_Disable // 0xc7, 0x43, 0x3c, 0x00, 0x00, 0x02, 0x44, // +0000 mov dword ptr [ebx+0x3c],520.0f }; - struct CPatch_CTFPlayerShared_RadiusCurrencyCollectionCheck : public IPatch + struct CPatch_CTFPlayerShared_RadiusCurrencyCollectionCheck : public CPatch { - CPatch_CTFPlayerShared_RadiusCurrencyCollectionCheck() : IPatch(sizeof(s_Buf)) {} + CPatch_CTFPlayerShared_RadiusCurrencyCollectionCheck() : CPatch(sizeof(s_Buf)) {} #error need addr virtual const char *GetFuncName() const override { return "[client] CTFGameMovement::ProcessMovement"; } diff --git a/src/mod/debug/backtrace.cpp b/src/mod/debug/backtrace.cpp index c5c0c9e3..5d34e2bf 100644 --- a/src/mod/debug/backtrace.cpp +++ b/src/mod/debug/backtrace.cpp @@ -24,7 +24,7 @@ namespace Mod_Debug_Backtrace void AddBacktrace(const char *lib, const char *pattern) { - auto backtrace = new CFuncBacktrace(LibMgr::FromString(lib), pattern); + auto backtrace = new CFuncBacktrace(LibMgr::Lib_FromString(lib), pattern); if (backtrace->Load()) { backtrace->Enable(); this->m_Backtraces.emplace_back(backtrace); diff --git a/src/mod/debug/console_scramble.cpp b/src/mod/debug/console_scramble.cpp deleted file mode 100644 index 20339529..00000000 --- a/src/mod/debug/console_scramble.cpp +++ /dev/null @@ -1,51 +0,0 @@ -#include "mod.h" - - -namespace Mod_Debug_Console_Scramble -{ - FILE *f = nullptr; - SpewOutputFunc_t RealOutputFunc = nullptr; - - - SpewRetval_t MyOutputFunc(SpewType_t spewType, const tchar *pMsg) - { - static std::mutex m; - std::lock_guard lock(m); - - fprintf(f, "MyOutputFunc [%15.10f] [T %5u] \"%s\"\n", Plat_FloatTime(), ThreadGetCurrentId(), pMsg); - fflush(f); - - return RealOutputFunc(spewType, pMsg); - } - - - class CMod : public IMod - { - public: - CMod() : IMod("Debug:Console_Scramble") {} - - virtual void OnEnable() override - { - f = fopen("spewlog.txt", "w"); - - RealOutputFunc = GetSpewOutputFunc(); - SpewOutputFunc(&MyOutputFunc); - } - - virtual void OnDisable() override - { - SpewOutputFunc(RealOutputFunc); - - fclose(f); - } - }; - CMod s_Mod; - - - ConVar cvar_enable("sig_debug_console_scramble", "0", FCVAR_NOTIFY, - "Debug: determine why mat_queue_mode 2 causes console output to go out of order", - [](IConVar *pConVar, const char *pOldValue, float flOldValue) { - ConVarRef var(pConVar); - s_Mod.Toggle(var.GetBool()); - }); -} diff --git a/src/mod/debug/console_scramble_v1.cpp b/src/mod/debug/console_scramble_v1.cpp new file mode 100644 index 00000000..63697519 --- /dev/null +++ b/src/mod/debug/console_scramble_v1.cpp @@ -0,0 +1,146 @@ +#include "mod.h" +#include + + +namespace Mod_Debug_Console_Scramble_v1 +{ + FILE *f = nullptr; + SpewOutputFunc_t RealOutputFunc = nullptr; + + + SpewRetval_t MyOutputFunc(SpewType_t spewType, const tchar *pMsg) + { + static std::mutex m; + std::lock_guard lock(m); + + fprintf(f, "MyOutputFunc [%15.10f] [T %4x] \"%s\"\n", Plat_FloatTime(), ThreadGetCurrentId(), pMsg); + fflush(f); + + // if (Coroutine_IsActive()) { + // size_t depth = Coroutine_GetStackDepth(); + // fprintf(f, ">>> In coroutine! Stack depth: %lu\n", depth); + // fflush(f); + // } + + // if (ThreadGetCurrentId() != 0x5914) { + // __asm { + // int 3 + // }; + // } + + // return RealOutputFunc(spewType, pMsg); + return SPEW_CONTINUE; + } + + + class CMod : public IMod + { + public: + CMod() : IMod("Debug:Console_Scramble_v1") {} + + virtual void OnEnable() override + { + f = fopen("spewlog.txt", "w"); + + RealOutputFunc = GetSpewOutputFunc(); + SpewOutputFunc(&MyOutputFunc); + } + + virtual void OnDisable() override + { + SpewOutputFunc(RealOutputFunc); + + fclose(f); + } + }; + CMod s_Mod; + + + // new plan: detour all of these funcs, and synchronously print enter/exit events with thread ID to file from each + // Msg + // _SpewMessage + // ... + + + ConVar cvar_enable("sig_debug_console_scramble_v1", "0", FCVAR_NOTIFY, + "Debug: determine why mat_queue_mode 2 causes console output to go out of order", + [](IConVar *pConVar, const char *pOldValue, float flOldValue) { + ConVarRef var(pConVar); + s_Mod.Toggle(var.GetBool()); + }); +} + +/* findings: + +Default client spew func is Sys_SpewFunc, which calls Con_ColorPrintf (engine/console.cpp) +Con_ColorPrintf calls Con_ColorPrint, which calls +- g_pCVar->ConsoleColorPrintf +- g_pConPanel->AddToNotify + +the deserialization has already happened by the time we get to Con_ColorPrint + +it's in Sys_SpewFunc: inlined function AddSpewRecord(const char *) with static CUtlLinkedList g_SpewHistory +no wait; actually all this does is add to a linked list for post-mortem minidump purposes (sys_minidumpspewlines); +messing with sys_minidumpspewlines does nothing + +also there's GetSpew +and g_SpewMutex + + +ACTUAL REALIZATION: +it's vstdlib coroutines, man! + + +non-main thread: +> tier0.dll!DevWarning() + 478 bytes Unknown + [Frames below may be incorrect and/or missing, no symbols loaded for tier0.dll] + atiumdag.dll!60c38567() Unknown + atiumdag.dll!60c37a54() Unknown + atiumdag.dll!60c8535a() Unknown + atiumdag.dll!60c83b66() Unknown + atiumdag.dll!60be4d54() Unknown + d3d9.dll!_AllocateCB@8() Unknown + ntdll.dll!_NtSetEvent@8() Unknown + GameOverlayRenderer.dll!NotifyVRCleanup() + 13193 bytes Unknown + atiu9pag.dll!OpenAdapter() + 1717 bytes Unknown + d3d9.dll!CD3DDDIDX10_DrawIndexedPrimitive(class CD3DBase *,enum _D3DPRIMITIVETYPE,unsigned int,unsigned int,unsigned int,unsigned int,unsigned int) Unknown + atiu9pag.dll!OpenAdapter() + 1434 bytes Unknown + d3d9.dll!CD3DBase::UpdateTextures(void) Unknown + d3d9.dll!CD3DHal::SetSamplerState_FP(unsigned long,enum _D3DSAMPLERSTATETYPE,unsigned long) Unknown + 1750d7c0() Unknown + atiu9pag.dll!OpenAdapter() + 1717 bytes Unknown + d3d9.dll!CD3DDDIDX10_DrawIndexedPrimitive(class CD3DBase *,enum _D3DPRIMITIVETYPE,unsigned int,unsigned int,unsigned int,unsigned int,unsigned int) Unknown + d3d9.dll!CD3DBase::DrawIndexedPrimitive(enum _D3DPRIMITIVETYPE,int,unsigned int,unsigned int,unsigned int,unsigned int) Unknown + shaderapidx9.dll!145ea027() Unknown + shaderapidx9.dll!145f5e91() Unknown + MaterialSystem.dll!0f8fa7e7() Unknown + MaterialSystem.dll!0f8fa7f5() Unknown + MaterialSystem.dll!0f8fa82e() Unknown + atiu9pag.dll!XopOpenAdapters9() + 3039 bytes Unknown + MaterialSystem.dll!0f8fa7f5() Unknown + MaterialSystem.dll!0f8fc73a() Unknown + MaterialSystem.dll!0f8fa5c8() Unknown + MaterialSystem.dll!0f8b957e() Unknown + shaderapidx9.dll!145f1605() Unknown + shaderapidx9.dll!145e7586() Unknown + shaderapidx9.dll!145e7b3f() Unknown + shaderapidx9.dll!145f23d2() Unknown + shaderapidx9.dll!145edae1() Unknown + MaterialSystem.dll!0f8dfb65() Unknown + MaterialSystem.dll!0f8dbeb0() Unknown + MaterialSystem.dll!0f8dc7ca() Unknown + MaterialSystem.dll!0f8dcf33() Unknown + MaterialSystem.dll!0f8cf6e4() Unknown + MaterialSystem.dll!0f8c3290() Unknown + MaterialSystem.dll!0f8c7158() Unknown + vstdlib.dll!Coroutine_YieldToMain() + 9984 bytes Unknown + ntdll.dll!_NtClearEvent@4() Unknown + tier0.dll!CThread::ThreadProc() + 256 bytes Unknown + tier0.dll!0fe0d4f7() Unknown + tier0.dll!0fe0d61f() Unknown + kernel32.dll!@BaseThreadInitThunk@12() Unknown + ntdll.dll!__RtlUserThreadStart() Unknown + ntdll.dll!__RtlUserThreadStart@8() Unknown + + +*/ diff --git a/src/mod/debug/console_scramble_v2.cpp b/src/mod/debug/console_scramble_v2.cpp new file mode 100644 index 00000000..b4a71c62 --- /dev/null +++ b/src/mod/debug/console_scramble_v2.cpp @@ -0,0 +1,118 @@ +#include "mod.h" + + +namespace Mod_Debug_Console_Scramble_v2 +{ + FILE *f = nullptr; + + + DETOUR_DECL_STATIC(SpewRetval_t, Sys_SpewFunc, SpewType_t spewType, const char *pMsg) + { + if (f != nullptr) { + fprintf(f, "%04X >> Sys_SpewFunc [%12.7f] [Tick %5d] [SpewFunc %08x] \"%s\"\n", ThreadGetCurrentId(), Plat_FloatTime(), gpGlobals->tickcount, (uintptr_t)GetSpewOutputFunc(), pMsg); + fflush(f); + } + + auto result = DETOUR_STATIC_CALL(Sys_SpewFunc)(spewType, pMsg); + + if (f != nullptr) { + fprintf(f, "%04X << Sys_SpewFunc [%12.7f] [Tick %5d] [SpewFunc %08x] \"%s\"\n", ThreadGetCurrentId(), Plat_FloatTime(), gpGlobals->tickcount, (uintptr_t)GetSpewOutputFunc(), pMsg); + fflush(f); + } + + return result; + } + + + DETOUR_DECL_STATIC(void, ConColorMsg, const Color& clr, const char *pMsgFormat, ...) + { + // auto buf = new char[0x1000]; + char buf[0x1000]; + + va_list va; + va_start(va, pMsgFormat); + V_vsnprintf(buf, 0x1000, pMsgFormat, va); + va_end(va); + + if (f != nullptr) { + fprintf(f, "%04X >> ConColorMsg [%12.7f] [Tick %5d] [SpewFunc %08x] \"%s\"\n", ThreadGetCurrentId(), Plat_FloatTime(), gpGlobals->tickcount, (uintptr_t)GetSpewOutputFunc(), buf); + fflush(f); + } + + DETOUR_STATIC_CALL(ConColorMsg)(clr, "%s", buf); + + if (f != nullptr) { + fprintf(f, "%04X << ConColorMsg [%12.7f] [Tick %5d] [SpewFunc %08x] \"%s\"\n", ThreadGetCurrentId(), Plat_FloatTime(), gpGlobals->tickcount, (uintptr_t)GetSpewOutputFunc(), buf); + fflush(f); + } + + // delete buf; + } + + + class CMod : public IMod + { + public: + CMod() : IMod("Debug:Console_Scramble_v2") + { + // this->AddDetour(new CDetour("Sys_SpewFunc", reinterpret_cast(GetSpewOutputFunc()), GET_STATIC_CALLBACK(Sys_SpewFunc), GET_STATIC_INNERPTR(Sys_SpewFunc))); + // this->AddDetour(new CDetour("ConColorMsg", reinterpret_cast(static_cast(&ConColorMsg)), GET_STATIC_CALLBACK(ConColorMsg), GET_STATIC_INNERPTR(ConColorMsg))); + } + + virtual bool OnLoad() override + { + f = fopen("spewlog.txt", "w"); + return (f != nullptr); + } + + virtual void OnUnload() override + { + fclose(f); + f = nullptr; + } + }; + CMod s_Mod; + + ConVar cvar_enable("sig_debug_console_scramble_v2", "0", FCVAR_NOTIFY, + "Debug: determine why mat_queue_mode 2 causes console output to go out of order", + [](IConVar *pConVar, const char *pOldValue, float flOldValue) { + ConVarRef var(pConVar); + s_Mod.Toggle(var.GetBool()); + }); + + + class CModA : public IMod + { + public: + CModA() : IMod("Debug:Console_Scramble_v2_PartA") + { + this->AddDetour(new CDetour("Sys_SpewFunc", reinterpret_cast(GetSpewOutputFunc()), GET_STATIC_CALLBACK(Sys_SpewFunc), GET_STATIC_INNERPTR(Sys_SpewFunc))); + } + }; + CModA s_ModA; + + ConVar cvar_spewfunc("sig_debug_console_scramble_v2_spewfunc", "0", FCVAR_NOTIFY, + "Debug: enable Sys_SpewFunc detour", + [](IConVar *pConVar, const char *pOldValue, float flOldValue) { + ConVarRef var(pConVar); + s_ModA.Toggle(var.GetBool()); + }); + + + class CModB : public IMod + { + public: + CModB() : IMod("Debug:Console_Scramble_v2_PartB") + { + this->AddDetour(new CDetour("ConColorMsg", reinterpret_cast(static_cast(&ConColorMsg)), GET_STATIC_CALLBACK(ConColorMsg), GET_STATIC_INNERPTR(ConColorMsg))); + } + }; + CModB s_ModB; + + ConVar cvar_concolormsg("sig_debug_console_scramble_v2_concolormsg", "0", FCVAR_NOTIFY, + "Debug: enable ConColorMsg detour", + [](IConVar *pConVar, const char *pOldValue, float flOldValue) { + ConVarRef var(pConVar); + s_ModB.Toggle(var.GetBool()); + }); +} diff --git a/src/mod/debug/ctfbotproxy.cpp b/src/mod/debug/ctfbotproxy.cpp new file mode 100644 index 00000000..5015d432 --- /dev/null +++ b/src/mod/debug/ctfbotproxy.cpp @@ -0,0 +1,347 @@ +#include "mod.h" +#include "util/misc.h" +#include "util/rtti.h" +#include "util/iterate.h" + +//////////////////////////////////////////////////////////////////////////////// +#define typeof decltype +#include <../server/variant_t.h> +#define private public +#include <../server/eventqueue.h> +#undef private +#undef typeof + +const char *variant_t::ToString( void ) const +{ + COMPILE_TIME_ASSERT( sizeof(string_t) == sizeof(int) ); + + static char szBuf[512]; + + switch (fieldType) + { + case FIELD_STRING: + { + return(STRING(iszVal)); + } + + case FIELD_BOOLEAN: + { + if (bVal == 0) + { + Q_strncpy(szBuf, "false",sizeof(szBuf)); + } + else + { + Q_strncpy(szBuf, "true",sizeof(szBuf)); + } + return(szBuf); + } + + case FIELD_INTEGER: + { + Q_snprintf( szBuf, sizeof( szBuf ), "%i", iVal ); + return(szBuf); + } + + case FIELD_FLOAT: + { + Q_snprintf(szBuf,sizeof(szBuf), "%g", flVal); + return(szBuf); + } + + case FIELD_COLOR32: + { + Q_snprintf(szBuf,sizeof(szBuf), "%d %d %d %d", (int)rgbaVal.r, (int)rgbaVal.g, (int)rgbaVal.b, (int)rgbaVal.a); + return(szBuf); + } + + case FIELD_VECTOR: + { + Q_snprintf(szBuf,sizeof(szBuf), "[%g %g %g]", (double)vecVal[0], (double)vecVal[1], (double)vecVal[2]); + return(szBuf); + } + + case FIELD_VOID: + { + szBuf[0] = '\0'; + return(szBuf); + } + + case FIELD_EHANDLE: + { + const char *pszName = (Entity()) ? STRING(Entity()->GetEntityName()) : "<>"; + Q_strncpy( szBuf, pszName, 512 ); + return (szBuf); + } + } + + return("No conversion to string"); +} +//////////////////////////////////////////////////////////////////////////////// + + +class IMapEntityFilter {}; + + +class CTFBotProxy : public CBaseEntity {}; + + +namespace Mod_Debug_CTFBotProxy +{ + const char *MakeEscapedVersion(const char *str) + { + static char buf[0x100000]; + size_t pos = 0; + + for (const char *c = str; *c != '\0'; ++c) { + if (isprint(*c)) { + buf[pos++] = *c; + } else { + char tmp[0x10]; + V_sprintf_safe(tmp, "%02X", *(const uint8_t *)c); + + buf[pos++] = '\\'; + buf[pos++] = 'x'; + buf[pos++] = tmp[0]; + buf[pos++] = tmp[1]; + } + } + buf[pos] = '\0'; + + return buf; + } + + + void PrintLineBrokenEscapedVersion(const char *str) + { + bool done = false; + + do { + static char buf[0x10000]; + size_t pos = 0; + + for ( ; ; ++str) { + if (*str == '\0') { + done = true; + break; + } + + if (isprint(*str)) { + buf[pos++] = *str; + } else { + char tmp[0x10]; + V_sprintf_safe(tmp, "%02X", *(const uint8_t *)str); + + buf[pos++] = '\\'; + buf[pos++] = 'x'; + buf[pos++] = tmp[0]; + buf[pos++] = tmp[1]; + } + + if (*str == '\n') { + ++str; + break; + } + } + + buf[pos++] = '\n'; + buf[pos++] = '\0'; + + Msg(buf); + } while (!done); + } + + + DETOUR_DECL_STATIC(string_t, AllocPooledString, const char *pszValue) + { + ConColorMsg(Color(0xff, 0xff, 0x00, 0xff), "AllocPooledString(%s)\n", pszValue); + return DETOUR_STATIC_CALL(AllocPooledString)(pszValue); + } + + DETOUR_DECL_MEMBER(void, CTFBotProxy_C1) + { + static uintptr_t prev = 0x00000000; + + uintptr_t diff = Max((uintptr_t)this, prev) - Min((uintptr_t)this, prev); + ConColorMsg(Color(0x00, 0xff, 0xff, 0xff), "CTFBotProxy constructed @ 0x%08x (diff: 0x%08x)\n", (uintptr_t)this, diff); + prev = (uintptr_t)this; + + DETOUR_MEMBER_CALL(CTFBotProxy_C1)(); + } + + + DETOUR_DECL_MEMBER(bool, IServerGameDLL_LevelInit, const char *pMapName, const char *pMapEntities, const char *pOldLevel, const char *pLandmarkName, bool loadGame, bool background) + { + ConColorMsg(Color(0xff, 0x00, 0xff, 0xff), "DETOUR for IServerGameDLL::LevelInit\n"); + + for (const uint8_t *c = reinterpret_cast(pMapEntities); *c != '\0'; ++c) { + if (*c >= 0x80 && *c <= 0xff) { + Msg("- Detected character %02x @ pMapEntities+0x%x\n", (int)(*c), (uintptr_t)c - (uintptr_t)pMapEntities); + } + } + + return DETOUR_MEMBER_CALL(IServerGameDLL_LevelInit)(pMapName, pMapEntities, pOldLevel, pLandmarkName, loadGame, background); + } + + DETOUR_DECL_STATIC(void, MapEntity_ParseAllEntities, const char *pMapData, IMapEntityFilter *pFilter, bool bActivateEntities) + { + printf("MapEntity_ParseAllEntities: %s\n", MakeEscapedVersion(pMapData)); + + DETOUR_STATIC_CALL(MapEntity_ParseAllEntities)(pMapData, pFilter, bActivateEntities); + } + + DETOUR_DECL_STATIC(const char *, MapEntity_ParseEntity, CBaseEntity *&pEntity, const char *pEntData, IMapEntityFilter *pFilter) + { + printf("MapEntity_ParseEntity: %s\n", MakeEscapedVersion(pEntData)); + // PrintLineBrokenEscapedVersion(pEntData); + + return DETOUR_STATIC_CALL(MapEntity_ParseEntity)(pEntity, pEntData, pFilter); + } + + DETOUR_DECL_STATIC(const char *, MapEntity_ParseToken, const char *data, char *newToken) + { + auto result = DETOUR_STATIC_CALL(MapEntity_ParseToken)(data, newToken); + + int len1 = result - data; + int len2 = strlen(newToken); + Msg("MapEntity_ParseToken: %d %d %s\n", len1, len2, MakeEscapedVersion(newToken)); + + return result; + } + + + DETOUR_DECL_MEMBER(void, CEventQueue_AddEvent, EventQueuePrioritizedEvent_t *pe) + { + auto queue = reinterpret_cast(this); + + ConColorMsg(Color(0x00, 0xff, 0x00, 0xff), + "[%10.6f] AddEvent: [t: %10.6f] [target: '%s' '%s'] [param: '%s'] [activator: '%s'] [caller: '%s']\n", + engine->GetServerTime(), + pe->m_flFireTime, + STRING(pe->m_iTarget), + STRING(pe->m_iTargetInput), + pe->m_VariantValue.String(), + (pe->m_pActivator != nullptr ? STRING(pe->m_pActivator->GetEntityName()) : "none"), + (pe->m_pCaller != nullptr ? STRING(pe->m_pCaller ->GetEntityName()) : "none")); + + DETOUR_MEMBER_CALL(CEventQueue_AddEvent)(pe); + } + + DETOUR_DECL_MEMBER(void, CEventQueue_ServiceEvents) + { + auto queue = reinterpret_cast(this); + + if (queue->m_Events.m_pNext != nullptr) { + ConColorMsg(Color(0xff, 0xff, 0x00, 0xff), + "[%10.6f] CEventQueue::ServiceEvents DUMP:\n", + engine->GetServerTime()); + + for (EventQueuePrioritizedEvent_t *pe = queue->m_Events.m_pNext; pe != nullptr; pe = pe->m_pNext) { + ConColorMsg(Color(0xff, 0xff, 0x00, 0xff), + "[t: %10.6f] [target: '%s' '%s'] [param: '%s'] [activator: '%s'] [caller: '%s']\n", + pe->m_flFireTime, + STRING(pe->m_iTarget), + STRING(pe->m_iTargetInput), + pe->m_VariantValue.String(), + (pe->m_pActivator != nullptr ? STRING(pe->m_pActivator->GetEntityName()) : "none"), + (pe->m_pCaller != nullptr ? STRING(pe->m_pCaller ->GetEntityName()) : "none")); + } + } + + DETOUR_MEMBER_CALL(CEventQueue_ServiceEvents)(); + } + + + class CMod : public IMod + { + public: + CMod() : IMod("Debug:CTFBotProxy") + { + MOD_ADD_DETOUR_STATIC(AllocPooledString, "AllocPooledString"); + MOD_ADD_DETOUR_MEMBER(CTFBotProxy_C1, "CTFBotProxy::CTFBotProxy [C1]"); + + // MOD_ADD_DETOUR_MEMBER(IServerGameDLL_LevelInit, "IServerGameDLL::LevelInit"); + // MOD_ADD_DETOUR_STATIC(MapEntity_ParseAllEntities, "MapEntity_ParseAllEntities"); + // MOD_ADD_DETOUR_STATIC(MapEntity_ParseEntity, "MapEntity_ParseEntity"); + // MOD_ADD_DETOUR_STATIC(MapEntity_ParseToken, "MapEntity_ParseToken"); + + MOD_ADD_DETOUR_MEMBER(CEventQueue_AddEvent, "CEventQueue::AddEvent [EventQueuePrioritizedEvent_t *]"); + MOD_ADD_DETOUR_MEMBER(CEventQueue_ServiceEvents, "CEventQueue::ServiceEvents"); + } + }; + CMod s_Mod; + + + ConVar cvar_enable("sig_debug_ctfbotproxy", "0", FCVAR_NOTIFY, + "", + [](IConVar *pConVar, const char *pOldValue, float flOldValue) { + ConVarRef var(pConVar); + s_Mod.Toggle(var.GetBool()); + }); + + + void PrintHeader() + { + Msg("\n--------------------------------------------------------------------------------\n"); + Msg("%-10s %5s %-12s | %-10s\n", "ADDR", "EIDX", "NAME", "VTADDR"); + } + + void PrintProxy(CTFBotProxy *proxy) + { + CFmtStr col_addr("0x%08x", (uintptr_t)proxy); + CFmtStr col_eidx("#%d", ENTINDEX(proxy)); + CFmtStr col_name("%s", STRING(proxy->GetEntityName())); + CFmtStr col_vt ("0x%08x", *reinterpret_cast(proxy)); + + Msg("%-10s %5s %-12s | %10s\n", col_addr.Get(), col_eidx.Get(), col_name.Get(), col_vt.Get()); + } + + CON_COMMAND(sig_debug_ctfbotproxy_cmd, "") + { + static auto addr_VT = (uintptr_t)RTTI::GetVTable(); + static auto addr_RTTI = (uintptr_t)RTTI::GetRTTI (); + + static auto addr_s_pTokenBuf = (uintptr_t)AddrManager::GetAddr("s_pTokenBuf"); + + Msg("\n================================================================================\n"); + Msg("CTFBotProxy VT: %08x\n", addr_VT); + Msg("CTFBotProxy RTTI: %08x\n", addr_RTTI); + Msg("s_pTokenBuf: %08x\n", addr_s_pTokenBuf); + + + if (args.ArgC() > 1 && FStrEq(args[1], "list")) { + std::map proxies_by_addr; + std::map proxies_by_eidx; + std::map proxies_by_name; + + ForEachEntityByClassname("bot_proxy", [&](CBaseEntity *ent){ + auto proxy = static_cast(ent); + + proxies_by_addr[(uintptr_t)ent] = proxy; + proxies_by_eidx[ENTINDEX(ent)] = proxy; + proxies_by_name[STRING(ent->GetEntityName())] = proxy; + }); + + bool by_addr = (args.ArgC() > 2 && FStrEq(args[2], "addr")); + bool by_eidx = (args.ArgC() > 2 && FStrEq(args[2], "eidx")); + bool by_name = (args.ArgC() > 2 && FStrEq(args[2], "name")); + + if (by_addr) { + PrintHeader(); + for (const auto& pair : proxies_by_addr) { + PrintProxy(pair.second); + } + } else if (by_eidx) { + PrintHeader(); + for (const auto& pair : proxies_by_eidx) { + PrintProxy(pair.second); + } + } else if (by_name) { + PrintHeader(); + for (const auto& pair : proxies_by_name) { + PrintProxy(pair.second); + } + } + } + } +} diff --git a/src/mod/debug/sentrybuster_mannhattan.cpp b/src/mod/debug/sentrybuster_mannhattan.cpp new file mode 100644 index 00000000..a329858e --- /dev/null +++ b/src/mod/debug/sentrybuster_mannhattan.cpp @@ -0,0 +1,135 @@ +#include "mod.h" +#include "stub/tfbot.h" +#include "util/scope.h" +#include "util/iterate.h" + + +namespace Mod_Debug_SentryBuster_Mannhattan +{ + std::vector messages; + + + RefCount rc_CTFBotTacticalMonitor_OnNavAreaChanged; + DETOUR_DECL_MEMBER(EventDesiredResult, CTFBotTacticalMonitor_OnNavAreaChanged, CTFBot *actor, CNavArea *area1, CNavArea *area2) + { + messages.clear(); + + SCOPED_INCREMENT(rc_CTFBotTacticalMonitor_OnNavAreaChanged); + auto result = DETOUR_MEMBER_CALL(CTFBotTacticalMonitor_OnNavAreaChanged)(actor, area1, area2); + + if (result.transition == ActionTransition::SUSPEND_FOR) { + DevMsg("CTFBotTacticalMonitor::OnNavAreaChanged\n"); + + for (const char *msg : messages) { + DevMsg("%s", msg); + } + } + + return result; + } + + DETOUR_DECL_MEMBER(bool, CBaseTrigger_PassesTriggerFilters, CBaseEntity *pOther) + { + auto trigger = reinterpret_cast(this); + + auto result = DETOUR_MEMBER_CALL(CBaseTrigger_PassesTriggerFilters)(pOther); + + if (rc_CTFBotTacticalMonitor_OnNavAreaChanged > 0) { + + + messages.emplace_back(CFmtStr("- [prereq: %s] [passed filter: %s]\n", STRING(trigger->GetEntityName()), (result ? "true" : "false"))); + } + + return result; + } + + + // CFilterMultiple filter_multi + // CFilterTFBotHasTag filter_tfbot_has_tag + + /* + _ZN11CBaseFilter16PassesFilterImplEP11CBaseEntityS1_ + _ZN15CFilterMultiple16PassesFilterImplEP11CBaseEntityS1_ + _ZN18CFilterTFBotHasTag16PassesFilterImplEP11CBaseEntityS1_ + */ + + + void DrawPrerequisiteOverlays() + { + static CountdownTimer ctDraw; + if (!ctDraw.IsElapsed()) return; + ctDraw.Start(1.000f); + + ForEachEntityByRTTI([](CFuncNavPrerequisite *prereq){ + constexpr float dur = 1.000f; + + int task = prereq->m_task; + const char *task_ent_name = STRING((string_t)prereq->m_taskEntityName); + float task_value = prereq->m_taskValue; + bool enabled = !prereq->m_isDisabled; + + CFmtStr task_str; + switch (task) { + case CFuncNavPrerequisite::DESTROY_ENTITY: task_str.sprintf("DESTROY_ENTITY"); break; + case CFuncNavPrerequisite::MOVE_TO: task_str.sprintf("MOVE_TO"); break; + case CFuncNavPrerequisite::WAIT: task_str.sprintf("WAIT"); break; + + default: + task_str.sprintf("(?) %d", task); + break; + } + + int r = (enabled ? 0x00 : 0xff); + int g = (enabled ? 0xff : 0x00); + int b = 0x00; + int a = 0x20; + NDebugOverlay::EntityBounds(prereq, r, g, b, a, dur); + + const Vector& wsc = prereq->WorldSpaceCenter(); + + int l = -2; + NDebugOverlay::EntityTextAtPosition(wsc, l++, CFmtStr("Name: %s", STRING(prereq->GetEntityName())), dur); + NDebugOverlay::EntityTextAtPosition(wsc, l++, CFmtStr("Task: %s", task_str.Get()), dur); + NDebugOverlay::EntityTextAtPosition(wsc, l++, CFmtStr("Task ent: %s", task_ent_name), dur); + NDebugOverlay::EntityTextAtPosition(wsc, l++, CFmtStr("Task value: %f", task_value), dur); + + ++l; + NDebugOverlay::EntityTextAtPosition(wsc, l++, CFmtStr("m_bDisabled: %s", (prereq->m_bDisabled ? "true" : "false")), dur); + NDebugOverlay::EntityTextAtPosition(wsc, l++, CFmtStr("m_isDisabled: %s", (prereq->m_isDisabled ? "true" : "false")), dur); + + CBaseEntity *task_ent = servertools->FindEntityByName(nullptr, task_ent_name); + if (task_ent != nullptr && !task_ent->ClassMatches("func_nav_prerequisite")) { + NDebugOverlay::EntityBounds(task_ent, 0xff, 0xff, 0xff, 0x20, dur); + + NDebugOverlay::EntityTextAtPosition(task_ent->WorldSpaceCenter(), 0, task_ent_name, dur); + } + }); + } + + + class CMod : public IMod, public IFrameUpdateListener + { + public: + CMod() : IMod("Debug:SentryBuster_Mannhattan") + { + MOD_ADD_DETOUR_MEMBER(CTFBotTacticalMonitor_OnNavAreaChanged, "CTFBotTacticalMonitor::OnNavAreaChanged"); + MOD_ADD_DETOUR_MEMBER(CBaseTrigger_PassesTriggerFilters, "CBaseTrigger::PassesTriggerFilters"); + } + + virtual bool ShouldReceiveFrameEvents() const override { return this->IsEnabled(); } + + virtual void FrameUpdatePostEntityThink() override + { + DrawPrerequisiteOverlays(); + } + }; + CMod s_Mod; + + + ConVar cvar_enable("sig_debug_sentrybuster_mannhattan", "0", FCVAR_NOTIFY, + "Debug: investigate the specifics of the func_nav_prerequisite gate issues related to busters", + [](IConVar *pConVar, const char *pOldValue, float flOldValue) { + ConVarRef var(pConVar); + s_Mod.Toggle(var.GetBool()); + }); +} diff --git a/src/mod/debug/useitem_broken.cpp b/src/mod/debug/useitem_broken.cpp index f5358b09..3294127c 100644 --- a/src/mod/debug/useitem_broken.cpp +++ b/src/mod/debug/useitem_broken.cpp @@ -7,6 +7,8 @@ #include "util/misc.h" #include "util/clientmsg.h" +#include + //#include "../mvm-reversed/server/tf/bot/behavior/tf_bot_use_item.h" class CTFBotUseItem : public Action @@ -215,6 +217,16 @@ namespace Mod_Debug_UseItem_Broken ClientMsg(" \n[%8.3f] CTFBotUseItem::Update(#%d)\n", gpGlobals->curtime, ENTINDEX(actor)); + if (useitem->m_hItem != nullptr) { + ClientMsg("%*s[m_bRageDraining: %s]\n", 13, " ", (actor->m_Shared->m_bRageDraining ? "true" : "false")); + ClientMsg("%*s[m_flRageMeter: %6.2f]\n", 13, " ", (float)actor->m_Shared->m_flRageMeter); + ClientMsg("%*s[m_flEnergyDrinkMeter: %6.2f]\n", 13, " ", (float)actor->m_Shared->m_flEnergyDrinkMeter); + ClientMsg("%*s[InCond(TF_COND_TAUNTING): %s]\n", 13, " ", (actor->m_Shared->InCond(TF_COND_TAUNTING) ? "true" : "false")); + ClientMsg("%*s[item->HasAmmo(): %s]\n", 13, " ", (useitem->m_hItem->HasAmmo() ? "true" : "false")); + ClientMsg("%*s[item->m_flLastFireTime: %8.3f]\n", 13, " ", (float)useitem->m_hItem->m_flLastFireTime); + ClientMsg("%*s[item->m_flEffectBarRegenTime: %8.3f]\n", 13, " ", (float)useitem->m_hItem->m_flEffectBarRegenTime); + } + if (useitem->m_ctInitialDelay.HasStarted() && useitem->m_ctInitialDelay.IsElapsed()) { ClientMsg("%*sInitial delay elapsed; pressing +attack now\n", 13, " "); } @@ -242,8 +254,8 @@ namespace Mod_Debug_UseItem_Broken auto bot = reinterpret_cast(this); /* TODO: remove this garbage! */ - constexpr int OFF_m_RequiredWeapons = 0x2570; - auto m_RequiredWeapons = reinterpret_cast> *>((uintptr_t)bot + OFF_m_RequiredWeapons); + constexpr int OFF_m_RequiredWeapons = 0x25c4; + auto m_RequiredWeapons = reinterpret_cast> *>((uintptr_t)bot + OFF_m_RequiredWeapons); DETOUR_MEMBER_CALL(CTFBot_PushRequiredWeapon)(weapon); if (rc_CTFBotUseItem_OnStart > 0) { @@ -265,8 +277,8 @@ namespace Mod_Debug_UseItem_Broken auto bot = reinterpret_cast(this); /* TODO: remove this garbage! */ - constexpr int OFF_m_RequiredWeapons = 0x2570; - auto m_RequiredWeapons = reinterpret_cast> *>((uintptr_t)bot + OFF_m_RequiredWeapons); + constexpr int OFF_m_RequiredWeapons = 0x25c4; + auto m_RequiredWeapons = reinterpret_cast> *>((uintptr_t)bot + OFF_m_RequiredWeapons); DETOUR_MEMBER_CALL(CTFBot_PopRequiredWeapon)(); if (rc_CTFBotUseItem_OnEnd > 0) { @@ -288,9 +300,9 @@ namespace Mod_Debug_UseItem_Broken { auto bot = reinterpret_cast(this); - const char *before = WeaponID_ToString(bot->GetActiveTFWeapon()->GetWeaponID()); + const char *before = (bot->GetActiveTFWeapon() != nullptr ? WeaponID_ToString(bot->GetActiveTFWeapon()->GetWeaponID()) : "nullptr"); auto result = DETOUR_MEMBER_CALL(CTFBot_EquipRequiredWeapon)(); - const char *after = WeaponID_ToString(bot->GetActiveTFWeapon()->GetWeaponID()); + const char *after = (bot->GetActiveTFWeapon() != nullptr ? WeaponID_ToString(bot->GetActiveTFWeapon()->GetWeaponID()) : "nullptr"); if (strcmp(before, after) != 0) { ClientMsg(" \n[%8.3f] CTFBot::EquipRequiredWeapon(#%d): %s -> %s\n", gpGlobals->curtime, ENTINDEX(bot), before, after); diff --git a/src/mod/etc/melee_ignore_teammates.cpp b/src/mod/etc/melee_ignore_teammates.cpp new file mode 100644 index 00000000..37725f70 --- /dev/null +++ b/src/mod/etc/melee_ignore_teammates.cpp @@ -0,0 +1,93 @@ +#include "mod.h" +#include "stub/tfweaponbase.h" +#include "util/scope.h" + + +namespace Mod_Etc_Melee_Ignore_Teammates +{ + CTFWeaponBaseMelee *melee = nullptr; + CTFPlayer *owner = nullptr; + bool is_whip = false; + + RefCount rc_CTFWeaponBaseMelee_DoSwingTraceInternal; + DETOUR_DECL_MEMBER(bool, CTFWeaponBaseMelee_DoSwingTraceInternal, CGameTrace& tr, bool cleave_attack, CUtlVector *traces) + { + auto weapon = reinterpret_cast(this); + + bool result; + if (!cleave_attack) { + melee = weapon; + owner = (weapon != nullptr ? weapon->GetTFPlayerOwner() : nullptr); + is_whip = (CAttributeManager::AttribHookValue(0, "speed_buff_ally", weapon) > 0); + + SCOPED_INCREMENT(rc_CTFWeaponBaseMelee_DoSwingTraceInternal); + result = DETOUR_MEMBER_CALL(CTFWeaponBaseMelee_DoSwingTraceInternal)(tr, cleave_attack, traces); + + melee = nullptr; + owner = nullptr; + is_whip = false; + } else { + result = DETOUR_MEMBER_CALL(CTFWeaponBaseMelee_DoSwingTraceInternal)(tr, cleave_attack, traces); + } + + return result; + } + + + RefCount rc_FindHullIntersection; + DETOUR_DECL_STATIC(void, FindHullIntersection, const Vector& vecSrc, trace_t& tr, const Vector& mins, const Vector& maxs, CBaseEntity *pEntity) + { + SCOPED_INCREMENT(rc_FindHullIntersection); + DETOUR_STATIC_CALL(FindHullIntersection)(vecSrc, tr, mins, maxs, pEntity); + } + + + DETOUR_DECL_MEMBER(void, IEngineTrace_TraceRay, const Ray_t& ray, unsigned int fMask, ITraceFilter *pTraceFilter, trace_t *pTrace) + { + if (rc_CTFWeaponBaseMelee_DoSwingTraceInternal > 0) { + // doing fallback stuff + if (rc_FindHullIntersection > 0) { + // if it's a CTraceFilterSimple and we're called from within FindHullIntersection, then we're doing fallback stuff + // ... + } + + // if it's a CTraceFilterIgnorePlayers, then we're doing the Homewrecker thing + // and we should leave it alone + + // if it's a CTraceFilterIgnoreTeammates, then we're doing the regular trace for MvM robots + // ... + + // if it's a CTraceFilterIgnoreFriendlyCombatItems, then we're doing the regular trace for non-MvM-robots + // ... + + #warning TODO + #warning TODO + #warning TODO + } + + DETOUR_MEMBER_CALL(IEngineTrace_TraceRay)(ray, fMask, pTraceFilter, pTrace); + } + + + class CMod : public IMod + { + public: + CMod() : IMod("Etc:Melee_Ignore_Teammates") + { + MOD_ADD_DETOUR_MEMBER(CTFWeaponBaseMelee_DoSwingTraceInternal, "CTFWeaponBaseMelee::DoSwingTraceInternal"); + + MOD_ADD_DETOUR_STATIC(FindHullIntersection, "FindHullIntersection"); + + MOD_ADD_DETOUR_MEMBER(IEngineTrace_TraceRay, "IEngineTrace::TraceRay"); + } + }; + CMod s_Mod; + + + ConVar cvar_enable("sig_etc_melee_ignore_teammates", "0", FCVAR_NOTIFY, + "Mod: allow melee traces to pass through teammates (for anyone, not just MvM blu team players)", + [](IConVar *pConVar, const char *pOldValue, float flOldValue) { + ConVarRef var(pConVar); + s_Mod.Toggle(var.GetBool()); + }); +} diff --git a/src/mod/etc/override_move_speed.cpp b/src/mod/etc/override_move_speed.cpp index e6fa0805..2dd910cd 100644 --- a/src/mod/etc/override_move_speed.cpp +++ b/src/mod/etc/override_move_speed.cpp @@ -20,9 +20,9 @@ namespace Mod_Etc_Override_Move_Speed 0xc7, 0x47, 0x3c, 0x00, 0x00, 0x02, 0x44, // +0000 mov dword ptr [edi+0x3c],520.0f }; - struct CPatch_CTFGameMovement_ProcessMovement_Server : public IPatch + struct CPatch_CTFGameMovement_ProcessMovement_Server : public CPatch { - CPatch_CTFGameMovement_ProcessMovement_Server() : IPatch(sizeof(s_Buf_Server)) {} + CPatch_CTFGameMovement_ProcessMovement_Server() : CPatch(sizeof(s_Buf_Server)) {} virtual const char *GetFuncName() const override { return "CTFGameMovement::ProcessMovement"; } virtual uint32_t GetFuncOffMin() const override { return 0x0000; } @@ -50,9 +50,9 @@ namespace Mod_Etc_Override_Move_Speed 0xc7, 0x43, 0x3c, 0x00, 0x00, 0x02, 0x44, // +0000 mov dword ptr [ebx+0x3c],520.0f }; - struct CPatch_CTFGameMovement_ProcessMovement_Client : public IPatch + struct CPatch_CTFGameMovement_ProcessMovement_Client : public CPatch { - CPatch_CTFGameMovement_ProcessMovement_Client() : IPatch(sizeof(s_Buf_Client)) {} + CPatch_CTFGameMovement_ProcessMovement_Client() : CPatch(sizeof(s_Buf_Client)) {} virtual const char *GetFuncName() const override { return "[client] CTFGameMovement::ProcessMovement"; } virtual uint32_t GetFuncOffMin() const override { return 0x0000; } diff --git a/src/mod/mvm/dominations.cpp b/src/mod/mvm/dominations.cpp index 87a17212..1af15ced 100644 --- a/src/mod/mvm/dominations.cpp +++ b/src/mod/mvm/dominations.cpp @@ -10,9 +10,9 @@ namespace Mod_MvM_Dominations 0x75, 0xcc, // +0009 jnz -0x34 }; - struct CPatch_CTFGameRules_CalcDominationAndRevenge : public IPatch + struct CPatch_CTFGameRules_CalcDominationAndRevenge : public CPatch { - CPatch_CTFGameRules_CalcDominationAndRevenge() : IPatch(sizeof(s_Buf)) {} + CPatch_CTFGameRules_CalcDominationAndRevenge() : CPatch(sizeof(s_Buf)) {} virtual const char *GetFuncName() const override { return "CTFGameRules::CalcDominationAndRevenge"; } virtual uint32_t GetFuncOffMin() const override { return 0x0000; } diff --git a/src/mod/mvm/gib_improvements.cpp b/src/mod/mvm/gib_improvements.cpp index fc307372..17b24368 100644 --- a/src/mod/mvm/gib_improvements.cpp +++ b/src/mod/mvm/gib_improvements.cpp @@ -15,9 +15,9 @@ namespace Mod_MvM_Gib_Improvements 0x75, 0xde, // +0009 jnz -0x22 }; - struct CPatch_CTFPlayer_ShouldGib : public IPatch + struct CPatch_CTFPlayer_ShouldGib : public CPatch { - CPatch_CTFPlayer_ShouldGib() : IPatch(sizeof(s_Buf)) {} + CPatch_CTFPlayer_ShouldGib() : CPatch(sizeof(s_Buf)) {} virtual const char *GetFuncName() const override { return "CTFPlayer::ShouldGib"; } virtual uint32_t GetFuncOffMin() const override { return 0x0000; } @@ -118,17 +118,24 @@ namespace Mod_MvM_Gib_Improvements } } - /* explosive headshot gibbing :) */ - if (eh_tick == gpGlobals->tickcount && eh_victims.count(bot) != 0) { - return true; - } - /* can't use the vfunc thunk for this call because we specifically * want to call the implementation in CTFPlayer */ static auto p_CTFPlayer_ShouldGib = MakePtrToMemberFunc(AddrManager::GetAddr("CTFPlayer::ShouldGib")); return (bot->*p_CTFPlayer_ShouldGib)(info); } + DETOUR_DECL_MEMBER(bool, CTFPlayer_ShouldGib, const CTakeDamageInfo& info) + { + auto player = reinterpret_cast(this); + + /* explosive headshot gibbing :) */ + if (eh_tick == gpGlobals->tickcount && eh_victims.count(player) != 0) { + return true; + } + + return DETOUR_MEMBER_CALL(CTFPlayer_ShouldGib)(info); + } + // Problems: // 1. TF_CUSTOM_PLASMA (Cow Mangler and medic shield) turns giants into @@ -170,7 +177,8 @@ namespace Mod_MvM_Gib_Improvements MOD_ADD_DETOUR_MEMBER(CTFSniperRifle_ExplosiveHeadShot, "CTFSniperRifle::ExplosiveHeadShot"); MOD_ADD_DETOUR_MEMBER(CTFPlayerShared_StunPlayer, "CTFPlayerShared::StunPlayer"); - MOD_ADD_DETOUR_MEMBER(CTFBot_ShouldGib, "CTFBot::ShouldGib"); + MOD_ADD_DETOUR_MEMBER(CTFBot_ShouldGib, "CTFBot::ShouldGib"); + MOD_ADD_DETOUR_MEMBER(CTFPlayer_ShouldGib, "CTFPlayer::ShouldGib"); MOD_ADD_DETOUR_MEMBER(CTFPlayer_CreateRagdollEntity, "CTFPlayer::CreateRagdollEntity [args]"); } diff --git a/src/mod/mvm/medigunshield_damage.cpp b/src/mod/mvm/medigunshield_damage.cpp index bdba4dbb..ba59e4b2 100644 --- a/src/mod/mvm/medigunshield_damage.cpp +++ b/src/mod/mvm/medigunshield_damage.cpp @@ -11,9 +11,9 @@ namespace Mod_MvM_MedigunShield_Damage 0xe9, 0x36, 0xfd, 0xff, 0xff, // +0011 jmp -0x2ca }; - struct CPatch_CTFMedigunShield_ShieldTouch : public IPatch + struct CPatch_CTFMedigunShield_ShieldTouch : public CPatch { - CPatch_CTFMedigunShield_ShieldTouch() : IPatch(sizeof(s_Buf)) {} + CPatch_CTFMedigunShield_ShieldTouch() : CPatch(sizeof(s_Buf)) {} virtual const char *GetFuncName() const override { return "CTFMedigunShield::ShieldTouch"; } virtual uint32_t GetFuncOffMin() const override { return 0x02c0; } diff --git a/src/mod/mvm/no_halloween_souls.cpp b/src/mod/mvm/no_halloween_souls.cpp new file mode 100644 index 00000000..db5a9497 --- /dev/null +++ b/src/mod/mvm/no_halloween_souls.cpp @@ -0,0 +1,35 @@ +#include "mod.h" +#include "stub/gamerules.h" + + +namespace Mod_MvM_No_Halloween_Souls +{ + DETOUR_DECL_MEMBER(void, CTFGameRules_DropHalloweenSoulPack, int i1, const Vector& vec1, CBaseEntity *ent1, int i2) + { + if (TFGameRules()->IsMannVsMachineMode()) { + /* don't spawn "halloween_souls_pack" entities in MvM mode */ + return; + } + + DETOUR_MEMBER_CALL(CTFGameRules_DropHalloweenSoulPack)(i1, vec1, ent1, i2); + } + + + class CMod : public IMod + { + public: + CMod() : IMod("MvM:No_Halloween_Souls") + { + MOD_ADD_DETOUR_MEMBER(CTFGameRules_DropHalloweenSoulPack, "CTFGameRules::DropHalloweenSoulPack"); + } + }; + CMod s_Mod; + + + ConVar cvar_enable("sig_mvm_no_halloween_souls", "0", FCVAR_NOTIFY, + "Mod: disable those stupid Halloween soul drop things in MvM mode", + [](IConVar *pConVar, const char *pOldValue, float flOldValue) { + ConVarRef var(pConVar); + s_Mod.Toggle(var.GetBool()); + }); +} diff --git a/src/mod/mvm/robosapper_override.cpp b/src/mod/mvm/robosapper_override.cpp new file mode 100644 index 00000000..b6d6e8f9 --- /dev/null +++ b/src/mod/mvm/robosapper_override.cpp @@ -0,0 +1,77 @@ +#include "mod.h" +#include "stub/tfplayer.h" +#include "stub/tf_shareddefs.h" +#include "util/scope.h" + + +namespace Mod_MvM_RoboSapper_Override +{ + ConVar cvar_radius("sig_mvm_robosapper_override_radius", "-1", FCVAR_NOTIFY, + "Mod: robo sapper radius (usual: 0 @ L0, 200 @ L1, 225 @ L2, 250 @ L3); if negative, no override will be applied"); + ConVar cvar_duration("sig_mvm_robosapper_override_duration", "-1.0", FCVAR_NOTIFY, + "Mod: robo sapper duration (usual: 4.0 @ L0, 4.0 @ L1, 5.5 @ L2, 7.0 @ L3); if negative, no override will be applied"); + + ConVar cvar_stun_minibosses("sig_mvm_robosapper_override_stun_minibosses", "0", FCVAR_NOTIFY, + "Mod: robo sapper will apply full stun to minibosses instead of just partial stun (usual: 0)"); + ConVar cvar_stun_amount("sig_mvm_robosapper_override_stun_amount", "0.85", FCVAR_NOTIFY, + "Mod: robo sapper will apply specified stun amount (usual: 0.85)"); + + + DETOUR_DECL_MEMBER(void, CObjectSapper_ApplyRoboSapper, CTFPlayer *target, float duration, int radius) + { + if (cvar_duration.GetFloat() >= 0.0f) { + duration = cvar_duration.GetFloat(); + } + + if (cvar_radius.GetInt() >= 0) { + radius = cvar_radius.GetInt(); + } + + DETOUR_MEMBER_CALL(CObjectSapper_ApplyRoboSapper)(target, duration, radius); + } + + RefCount rc_CObjectSapper_ApplyRoboSapperEffects; + DETOUR_DECL_MEMBER(bool, CObjectSapper_ApplyRoboSapperEffects, CTFPlayer *target, float duration) + { + SCOPED_INCREMENT(rc_CObjectSapper_ApplyRoboSapperEffects); + return DETOUR_MEMBER_CALL(CObjectSapper_ApplyRoboSapperEffects)(target, duration); + } + + DETOUR_DECL_MEMBER(void, CTFPlayerShared_StunPlayer, float duration, float slowdown, int flags, CTFPlayer *attacker) + { + auto shared = reinterpret_cast(this); + + if (rc_CObjectSapper_ApplyRoboSapperEffects > 0) { + CTFPlayer *player = shared->GetOuter(); + + if (cvar_stun_minibosses.GetBool() && player->IsMiniBoss()) { + flags = (TF_STUNFLAG_THIRDPERSON | TF_STUNFLAG_BONKSTUCK | TF_STUNFLAG_SLOWDOWN); + } + + slowdown = cvar_stun_amount.GetFloat(); + } + + DETOUR_MEMBER_CALL(CTFPlayerShared_StunPlayer)(duration, slowdown, flags, attacker); + } + + + class CMod : public IMod + { + public: + CMod() : IMod("MvM:RoboSapper_Override") + { + MOD_ADD_DETOUR_MEMBER(CObjectSapper_ApplyRoboSapper, "CObjectSapper::ApplyRoboSapper"); + MOD_ADD_DETOUR_MEMBER(CObjectSapper_ApplyRoboSapperEffects, "CObjectSapper::ApplyRoboSapperEffects"); + MOD_ADD_DETOUR_MEMBER(CTFPlayerShared_StunPlayer, "CTFPlayerShared::StunPlayer"); + } + }; + CMod s_Mod; + + + ConVar cvar_enable("sig_mvm_robosapper_override", "0", FCVAR_NOTIFY, + "Mod: enable overriding aspects of the robo sapper in MvM mode", + [](IConVar *pConVar, const char *pOldValue, float flOldValue) { + ConVarRef var(pConVar); + s_Mod.Toggle(var.GetBool()); + }); +} diff --git a/src/mod/perf/medigun_shield_damage_interval.cpp b/src/mod/perf/medigun_shield_damage_interval.cpp new file mode 100644 index 00000000..b03e5f3c --- /dev/null +++ b/src/mod/perf/medigun_shield_damage_interval.cpp @@ -0,0 +1,56 @@ +#include "mod.h" +#include "stub/tf_shareddefs.h" +#include "stub/tfweaponbase.h" + + +namespace Mod_Perf_Medigun_Shield_Damage_Interval +{ + DETOUR_DECL_MEMBER(int, CBaseEntity_TakeDamage, const CTakeDamageInfo& inputInfo) + { + auto ent = reinterpret_cast(this); + + if (inputInfo.GetDamageCustom() == TF_DMG_CUSTOM_PLASMA) { + auto medigun = rtti_cast(inputInfo.GetWeapon()); + + if (medigun != nullptr) { + extern ConVar cvar_enable; + int interval = Max(cvar_enable.GetInt(), 1); + + if (interval > 1) { + int victim_entindex = ENTINDEX(ent); + + if ((victim_entindex % interval) == (gpGlobals->tickcount % interval)) { + CTakeDamageInfo newInfo = inputInfo; + newInfo.ScaleDamage(interval); + + return DETOUR_MEMBER_CALL(CBaseEntity_TakeDamage)(newInfo); + } else { + return 0; + } + } + } + } + + + return DETOUR_MEMBER_CALL(CBaseEntity_TakeDamage)(inputInfo); + } + + + class CMod : public IMod + { + public: + CMod() : IMod("Perf:Medigun_Shield_Damage_Interval") + { + MOD_ADD_DETOUR_MEMBER(CBaseEntity_TakeDamage, "CBaseEntity::TakeDamage"); + } + }; + CMod s_Mod; + + + ConVar cvar_enable("sig_perf_medigun_shield_damage_interval", "0", FCVAR_NOTIFY, + "Mod: change the medigun shield damage interval to values greater than every single tick", + [](IConVar *pConVar, const char *pOldValue, float flOldValue) { + ConVarRef var(pConVar); + s_Mod.Toggle(var.GetBool()); + }); +} diff --git a/src/mod/pop/popmgr_extensions.cpp b/src/mod/pop/popmgr_extensions.cpp index f2fb7c17..98701ef6 100644 --- a/src/mod/pop/popmgr_extensions.cpp +++ b/src/mod/pop/popmgr_extensions.cpp @@ -103,11 +103,14 @@ namespace Mod_Pop_PopMgr_Extensions void Reset() { - this->m_bGiantsDropRareSpells = false; - this->m_flSpellDropRateCommon = 1.00f; - this->m_flSpellDropRateGiant = 1.00f; - this->m_bNoReanimators = false; - this->m_bNoMvMDeathTune = false; + this->m_bGiantsDropRareSpells = false; + this->m_flSpellDropRateCommon = 1.00f; + this->m_flSpellDropRateGiant = 1.00f; + this->m_bNoReanimators = false; + this->m_bNoMvMDeathTune = false; + this->m_bSniperHideLasers = false; + this->m_bSniperAllowHeadshots = false; + this->m_bDisableUpgradeStations = false; this->m_MedievalMode.Reset(); this->m_SpellsEnabled.Reset(); @@ -126,6 +129,9 @@ namespace Mod_Pop_PopMgr_Extensions float m_flSpellDropRateGiant; bool m_bNoReanimators; bool m_bNoMvMDeathTune; + bool m_bSniperHideLasers; + bool m_bSniperAllowHeadshots; + bool m_bDisableUpgradeStations; CPopOverride_MedievalMode m_MedievalMode; CPopOverride_ConVar m_SpellsEnabled; @@ -224,6 +230,70 @@ namespace Mod_Pop_PopMgr_Extensions DETOUR_MEMBER_CALL(CBaseEntity_EmitSound)(soundname, soundtime, duration); } + DETOUR_DECL_MEMBER(void, CTFSniperRifle_CreateSniperDot) + { + auto rifle = reinterpret_cast(this); + + if (state.m_bSniperHideLasers && TFGameRules()->IsMannVsMachineMode()) { + CTFPlayer *owner = rifle->GetTFPlayerOwner(); + if (owner != nullptr && owner->GetTeamNumber() == TF_TEAM_BLUE) { + return; + } + } + + DETOUR_MEMBER_CALL(CTFSniperRifle_CreateSniperDot)(); + } + + RefCount rc_CTFSniperRifle_CanFireCriticalShot; + DETOUR_DECL_MEMBER(bool, CTFSniperRifle_CanFireCriticalShot, bool bIsHeadshot) + { + SCOPED_INCREMENT(rc_CTFSniperRifle_CanFireCriticalShot); + return DETOUR_MEMBER_CALL(CTFSniperRifle_CanFireCriticalShot)(bIsHeadshot); + } + + DETOUR_DECL_MEMBER(bool, CTFWeaponBase_CanFireCriticalShot, bool bIsHeadshot) + { + auto weapon = reinterpret_cast(this); + + if (state.m_bSniperAllowHeadshots && rc_CTFSniperRifle_CanFireCriticalShot > 0 && TFGameRules()->IsMannVsMachineMode()) { + CTFPlayer *owner = weapon->GetTFPlayerOwner(); + if (owner != nullptr && owner->GetTeamNumber() == TF_TEAM_BLUE) { + return true; + } + } + + return DETOUR_MEMBER_CALL(CTFWeaponBase_CanFireCriticalShot)(bIsHeadshot); + } + + RefCount rc_CTFProjectile_Arrow_StrikeTarget; + DETOUR_DECL_MEMBER(bool, CTFProjectile_Arrow_StrikeTarget, mstudiobbox_t *bbox, CBaseEntity *ent) + { + SCOPED_INCREMENT(rc_CTFProjectile_Arrow_StrikeTarget); + return DETOUR_MEMBER_CALL(CTFProjectile_Arrow_StrikeTarget)(bbox, ent); + } + + DETOUR_DECL_MEMBER(bool, CTFGameRules_IsPVEModeControlled, CBaseEntity *ent) + { + if (state.m_bSniperAllowHeadshots && rc_CTFProjectile_Arrow_StrikeTarget > 0 && TFGameRules()->IsMannVsMachineMode()) { + return false; + } + + return DETOUR_MEMBER_CALL(CTFGameRules_IsPVEModeControlled)(ent); + } + + DETOUR_DECL_MEMBER(void, CUpgrades_UpgradeTouch, CBaseEntity *pOther) + { + if (state.m_bDisableUpgradeStations && TFGameRules()->IsMannVsMachineMode()) { + CTFPlayer *player = ToTFPlayer(pOther); + if (player != nullptr) { + gamehelpers->TextMsg(ENTINDEX(player), TEXTMSG_DEST_CENTER, "The Upgrade Station is disabled for this mission!"); + return; + } + } + + DETOUR_MEMBER_CALL(CUpgrades_UpgradeTouch)(pOther); + } + RefCount rc_CPopulationManager_Parse; DETOUR_DECL_MEMBER(bool, CPopulationManager_Parse) @@ -265,6 +335,12 @@ namespace Mod_Pop_PopMgr_Extensions state.m_bNoReanimators = subkey->GetBool(); } else if (V_stricmp(name, "NoMvMDeathTune") == 0) { state.m_bNoMvMDeathTune = subkey->GetBool(); + } else if (V_stricmp(name, "SniperHideLasers") == 0) { + state.m_bSniperHideLasers = subkey->GetBool(); + } else if (V_stricmp(name, "SniperAllowHeadshots") == 0) { + state.m_bSniperAllowHeadshots = subkey->GetBool(); + } else if (V_stricmp(name, "DisableUpgradeStations") == 0) { + state.m_bDisableUpgradeStations = subkey->GetBool(); } else if (V_stricmp(name, "MedievalMode") == 0) { state.m_MedievalMode.Set(subkey->GetBool()); } else if (V_stricmp(name, "GrapplingHook") == 0) { @@ -317,6 +393,12 @@ namespace Mod_Pop_PopMgr_Extensions MOD_ADD_DETOUR_MEMBER(CTFGameRules_IsUsingSpells, "CTFGameRules::IsUsingSpells"); MOD_ADD_DETOUR_STATIC(CTFReviveMarker_Create, "CTFReviveMarker::Create"); MOD_ADD_DETOUR_MEMBER(CBaseEntity_EmitSound, "CBaseEntity::EmitSound [const char *, float, float *]"); + MOD_ADD_DETOUR_MEMBER(CTFSniperRifle_CreateSniperDot, "CTFSniperRifle::CreateSniperDot"); + MOD_ADD_DETOUR_MEMBER(CTFSniperRifle_CanFireCriticalShot, "CTFSniperRifle::CanFireCriticalShot"); + MOD_ADD_DETOUR_MEMBER(CTFWeaponBase_CanFireCriticalShot, "CTFWeaponBase::CanFireCriticalShot"); + MOD_ADD_DETOUR_MEMBER(CTFProjectile_Arrow_StrikeTarget, "CTFProjectile_Arrow::StrikeTarget"); + MOD_ADD_DETOUR_MEMBER(CTFGameRules_IsPVEModeControlled, "CTFGameRules::IsPVEModeControlled"); + MOD_ADD_DETOUR_MEMBER(CUpgrades_UpgradeTouch, "CUpgrades::UpgradeTouch"); MOD_ADD_DETOUR_MEMBER(CPopulationManager_Parse, "CPopulationManager::Parse"); MOD_ADD_DETOUR_MEMBER(KeyValues_LoadFromFile, "KeyValues::LoadFromFile"); diff --git a/src/mod/pop/tank_extensions.cpp b/src/mod/pop/tank_extensions.cpp index 22a35668..3e1c101c 100644 --- a/src/mod/pop/tank_extensions.cpp +++ b/src/mod/pop/tank_extensions.cpp @@ -11,12 +11,30 @@ namespace Mod_Pop_Tank_Extensions { bool disable_smokestack = false; float scale = 1.00f; + bool force_romevision = false; + + std::vector> tanks; }; std::map spawners; + SpawnerData *FindSpawnerDataForTank(const CTFTankBoss *tank) + { + for (auto& pair : spawners) { + SpawnerData& data = pair.second; + for (auto h_tank : data.tanks) { + if (h_tank.Get() == tank) { + return &data; + } + } + } + + return nullptr; + } + + DETOUR_DECL_MEMBER(void, CTankSpawner_dtor0) { auto spawner = reinterpret_cast(this); @@ -55,6 +73,9 @@ namespace Mod_Pop_Tank_Extensions } else if (V_stricmp(name, "Scale") == 0) { // DevMsg("Got \"Scale\" = %f\n", subkey->GetFloat()); spawners[spawner].scale = subkey->GetFloat(); + } else if (V_stricmp(name, "ForceRomeVision") == 0) { + // DevMsg("Got \"ForceRomeVision\" = %d\n", subkey->GetBool()); + spawners[spawner].force_romevision = subkey->GetBool(); } else { del = false; } @@ -83,7 +104,7 @@ namespace Mod_Pop_Tank_Extensions { auto spawner = reinterpret_cast(this); - // DevMsg("CTankSpawner::Spawn %08x\n", (uintptr_t)this); + DevMsg("CTankSpawner::Spawn %08x\n", (uintptr_t)this); SCOPED_INCREMENT(rc_CTankSpawner_Spawn); current_spawner = spawner; @@ -100,6 +121,8 @@ namespace Mod_Pop_Tank_Extensions auto tank = rtti_cast(ent); if (tank != nullptr) { + data.tanks.push_back(tank); + if (data.scale != 1.00f) { /* need to call this BEFORE changing the scale; otherwise, * the collision bounding box will be very screwed up */ @@ -107,6 +130,49 @@ namespace Mod_Pop_Tank_Extensions tank->SetModelScale(data.scale); } + + if (data.force_romevision) { + // DevMsg(" tank->m_iModelIndex: %d\n", (int)tank->m_iModelIndex); + + // DevMsg(" s_TankModel[0]: \"%s\"\n", s_TankModel[0]); + // DevMsg(" s_TankModel[1]: \"%s\"\n", s_TankModel[1]); + // DevMsg(" s_TankModel[2]: \"%s\"\n", s_TankModel[2]); + // DevMsg(" s_TankModel[3]: \"%s\"\n", s_TankModel[3]); + + // DevMsg(" s_TankModelRome[0]: \"%s\"\n", s_TankModelRome[0]); + // DevMsg(" s_TankModelRome[1]: \"%s\"\n", s_TankModelRome[1]); + // DevMsg(" s_TankModelRome[2]: \"%s\"\n", s_TankModelRome[2]); + // DevMsg(" s_TankModelRome[3]: \"%s\"\n", s_TankModelRome[3]); + + // DevMsg(" modelinfo->GetModelIndex(s_TankModel[0]): %d\n", modelinfo->GetModelIndex(s_TankModel[0])); + // DevMsg(" modelinfo->GetModelIndex(s_TankModel[1]): %d\n", modelinfo->GetModelIndex(s_TankModel[1])); + // DevMsg(" modelinfo->GetModelIndex(s_TankModel[2]): %d\n", modelinfo->GetModelIndex(s_TankModel[2])); + // DevMsg(" modelinfo->GetModelIndex(s_TankModel[3]): %d\n", modelinfo->GetModelIndex(s_TankModel[3])); + + // DevMsg(" modelinfo->GetModelIndex(s_TankModelRome[0]): %d\n", modelinfo->GetModelIndex(s_TankModelRome[0])); + // DevMsg(" modelinfo->GetModelIndex(s_TankModelRome[1]): %d\n", modelinfo->GetModelIndex(s_TankModelRome[1])); + // DevMsg(" modelinfo->GetModelIndex(s_TankModelRome[2]): %d\n", modelinfo->GetModelIndex(s_TankModelRome[2])); + // DevMsg(" modelinfo->GetModelIndex(s_TankModelRome[3]): %d\n", modelinfo->GetModelIndex(s_TankModelRome[3])); + + // DevMsg(" [BEFORE] m_nModelIndexOverrides[0]: %d \"%s\"\n", tank->m_nModelIndexOverrides[0], modelinfo->GetModelName(modelinfo->GetModel(tank->m_nModelIndexOverrides[0]))); + // DevMsg(" [BEFORE] m_nModelIndexOverrides[1]: %d \"%s\"\n", tank->m_nModelIndexOverrides[1], modelinfo->GetModelName(modelinfo->GetModel(tank->m_nModelIndexOverrides[1]))); + // DevMsg(" [BEFORE] m_nModelIndexOverrides[2]: %d \"%s\"\n", tank->m_nModelIndexOverrides[2], modelinfo->GetModelName(modelinfo->GetModel(tank->m_nModelIndexOverrides[2]))); + // DevMsg(" [BEFORE] m_nModelIndexOverrides[3]: %d \"%s\"\n", tank->m_nModelIndexOverrides[3], modelinfo->GetModelName(modelinfo->GetModel(tank->m_nModelIndexOverrides[3]))); + + tank->SetModelIndexOverride(0, modelinfo->GetModelIndex(s_TankModelRome[tank->m_iModelIndex])); + + // DevMsg(" [AFTER] m_nModelIndexOverrides[0]: %d \"%s\"\n", tank->m_nModelIndexOverrides[0], modelinfo->GetModelName(modelinfo->GetModel(tank->m_nModelIndexOverrides[0]))); + // DevMsg(" [AFTER] m_nModelIndexOverrides[1]: %d \"%s\"\n", tank->m_nModelIndexOverrides[1], modelinfo->GetModelName(modelinfo->GetModel(tank->m_nModelIndexOverrides[1]))); + // DevMsg(" [AFTER] m_nModelIndexOverrides[2]: %d \"%s\"\n", tank->m_nModelIndexOverrides[2], modelinfo->GetModelName(modelinfo->GetModel(tank->m_nModelIndexOverrides[2]))); + // DevMsg(" [AFTER] m_nModelIndexOverrides[3]: %d \"%s\"\n", tank->m_nModelIndexOverrides[3], modelinfo->GetModelName(modelinfo->GetModel(tank->m_nModelIndexOverrides[3]))); + + for (CBaseEntity *child = tank->FirstMoveChild(); child != nullptr; child = child->NextMovePeer()) { + // DevMsg(" child [classname \"%s\"] [model \"%s\"]\n", child->GetClassname(), STRING(child->GetModelName())); + if (!child->ClassMatches("prop_dynamic")) continue; + + child->SetModelIndexOverride(0, child->m_nModelIndexOverrides[3]); + } + } } } } @@ -116,6 +182,57 @@ namespace Mod_Pop_Tank_Extensions } + CTFTankBoss *thinking_tank = nullptr; + SpawnerData *thinking_tank_data = nullptr; + + RefCount rc_CTFTankBoss_TankBossThink; + DETOUR_DECL_MEMBER(void, CTFTankBoss_TankBossThink) + { + auto tank = reinterpret_cast(this); + + SpawnerData *data = FindSpawnerDataForTank(tank); + if (data != nullptr) { + thinking_tank = tank; + thinking_tank_data = data; + } + + SCOPED_INCREMENT(rc_CTFTankBoss_TankBossThink); + DETOUR_MEMBER_CALL(CTFTankBoss_TankBossThink)(); + + thinking_tank = nullptr; + thinking_tank_data = nullptr; + } + + DETOUR_DECL_MEMBER(void, CBaseEntity_SetModelIndexOverride, int index, int nValue) + { + auto ent = reinterpret_cast(this); + + if (rc_CTFTankBoss_TankBossThink > 0 && thinking_tank != nullptr && thinking_tank_data != nullptr) { + CTFTankBoss *tank = thinking_tank; + SpawnerData *data = thinking_tank_data; + + if (data->force_romevision) { + // DevMsg("SetModelIndexOverride(%d, %d) for ent #%d \"%s\" \"%s\"\n", index, nValue, ENTINDEX(ent), ent->GetClassname(), STRING(ent->GetModelName())); + + if (ent == tank) { + if (index == 3) { + DETOUR_MEMBER_CALL(CBaseEntity_SetModelIndexOverride)(0, nValue); + DETOUR_MEMBER_CALL(CBaseEntity_SetModelIndexOverride)(3, nValue); + } + return; + } + + // if (ent->GetMoveParent() == tank && ent->ClassMatches("prop_dynamic")) { + // DevMsg("Blocking SetModelIndexOverride(%d, %d) for tank %d prop %d \"%s\"\n", index, nValue, ENTINDEX(tank), ENTINDEX(ent), STRING(ent->GetModelName())); + // return; + // } + } + } + + DETOUR_MEMBER_CALL(CBaseEntity_SetModelIndexOverride)(index, nValue); + } + + DETOUR_DECL_MEMBER(int, CBaseAnimating_LookupAttachment, const char *szName) { if (rc_CTankSpawner_Spawn > 0 && current_spawner != nullptr && @@ -150,6 +267,9 @@ namespace Mod_Pop_Tank_Extensions MOD_ADD_DETOUR_MEMBER(CTankSpawner_Spawn, "CTankSpawner::Spawn"); + MOD_ADD_DETOUR_MEMBER(CTFTankBoss_TankBossThink, "CTFTankBoss::TankBossThink"); + MOD_ADD_DETOUR_MEMBER(CBaseEntity_SetModelIndexOverride, "CBaseEntity::SetModelIndexOverride"); + MOD_ADD_DETOUR_MEMBER(CBaseAnimating_LookupAttachment, "CBaseAnimating::LookupAttachment"); } diff --git a/src/mod/pop/tfbot_extensions.cpp b/src/mod/pop/tfbot_extensions.cpp index fe6c23fb..fe182207 100644 --- a/src/mod/pop/tfbot_extensions.cpp +++ b/src/mod/pop/tfbot_extensions.cpp @@ -90,11 +90,23 @@ namespace Mod_Pop_TFBot_Extensions float delay = 0.0f; }; + enum ActionType + { + Action_Default, + + // built-in + Action_FetchFlag, + Action_PushToCapturePoint, + + // custom + Action_Mobber, + }; + struct SpawnerData { std::vector addconds; - bool action_mobber = false; + ActionType action = Action_Default; bool use_human_model = false; }; @@ -103,9 +115,9 @@ namespace Mod_Pop_TFBot_Extensions std::map spawners; - /* this is really dodgy... should probably do this with the ECAttr - * extension system, provided we ever finish that contraption */ - std::vector> action_override_mobber; +// /* this is really dodgy... should probably do this with the ECAttr +// * extension system, provided we ever finish that contraption */ +// std::map, ActionType> pending_action_overrides; struct DelayedAddCond @@ -140,12 +152,49 @@ namespace Mod_Pop_TFBot_Extensions } + /* for keeping track of which spawner each bot came from */ + CTFBotSpawner *bot_spawner_tracker[33]; + CTFBotSpawner *GetSpawnerOfBot(const CTFBot *bot) + { + int idx = ENTINDEX(bot); + if (idx == 0) return nullptr; + + assert(idx > 0); + assert(idx < 33); + return bot_spawner_tracker[idx]; + } + void SetSpawnerOfBot(const CTFBot *bot, CTFBotSpawner *spawner) + { + int idx = ENTINDEX(bot); + if (idx == 0) return; + + assert(idx > 0); + assert(idx < 33); + bot_spawner_tracker[idx] = spawner; + } + void ClearTrackingForSpawner(CTFBotSpawner *spawner) + { + for (auto& elem : bot_spawner_tracker) { + if (elem == spawner) { + elem = nullptr; + } + } + } + void ClearAllTracking() + { + for (auto& elem : bot_spawner_tracker) { + elem = nullptr; + } + } + + DETOUR_DECL_MEMBER(void, CTFBotSpawner_dtor0) { auto spawner = reinterpret_cast(this); DevMsg("CTFBotSpawner %08x: dtor0\n", (uintptr_t)spawner); spawners.erase(spawner); + ClearTrackingForSpawner(spawner); DETOUR_MEMBER_CALL(CTFBotSpawner_dtor0)(); } @@ -156,6 +205,7 @@ namespace Mod_Pop_TFBot_Extensions DevMsg("CTFBotSpawner %08x: dtor2\n", (uintptr_t)spawner); spawners.erase(spawner); + ClearTrackingForSpawner(spawner); DETOUR_MEMBER_CALL(CTFBotSpawner_dtor2)(); } @@ -207,8 +257,14 @@ namespace Mod_Pop_TFBot_Extensions { const char *value = kv->GetString(); - if (V_stricmp(value, "Mobber") == 0) { - spawners[spawner].action_mobber = true; + if (V_stricmp(value, "Default") == 0) { + spawners[spawner].action = Action_Default; + } else if (V_stricmp(value, "FetchFlag") == 0) { + spawners[spawner].action = Action_FetchFlag; + } else if (V_stricmp(value, "PushToCapturePoint") == 0) { + spawners[spawner].action = Action_PushToCapturePoint; + } else if (V_stricmp(value, "Mobber") == 0) { + spawners[spawner].action = Action_Mobber; } else { Warning("Unknown value \'%s\' for TFBot Action.\n", value); } @@ -264,40 +320,53 @@ namespace Mod_Pop_TFBot_Extensions auto result = DETOUR_MEMBER_CALL(CTFBotSpawner_Spawn)(where, ents); - if (ents != nullptr) { + // DevMsg("\nCTFBotSpawner %08x: SPAWNED\n", (uintptr_t)spawner); + // DevMsg(" [classicon \"%s\"] [miniboss %d]\n", STRING(spawner->GetClassIcon(0)), spawner->IsMiniBoss(0)); + // DevMsg("- result: %d\n", result); + // if (ents != nullptr) { + // DevMsg("- ents: "); + // FOR_EACH_VEC((*ents), i) { + // DevMsg(" #%d", ENTINDEX((*ents)[i])); + // } + // DevMsg("\n"); + // } + + if (result && ents != nullptr && !ents->IsEmpty()) { auto it = spawners.find(spawner); if (it != spawners.end()) { SpawnerData& data = (*it).second; - FOR_EACH_VEC((*ents), i) { - CTFBot *bot = ToTFBot((*ents)[i]); - if (bot != nullptr) { - // DevMsg("CTFBotSpawner %08x: found %u AddCond's\n", (uintptr_t)spawner, data.addconds.size()); - for (auto addcond : data.addconds) { - if (addcond.delay == 0.0f) { - DevMsg("CTFBotSpawner %08x: applying AddCond(%d, %f)\n", (uintptr_t)spawner, addcond.cond, addcond.duration); - bot->m_Shared->AddCond(addcond.cond, addcond.duration); - } else { - delayed_addconds.push_back({ - bot, - gpGlobals->curtime + addcond.delay, - addcond.cond, - addcond.duration, - }); - } - } - - if (data.action_mobber) { - action_override_mobber.push_back(bot); + CTFBot *bot = ToTFBot(ents->Tail()); + if (bot != nullptr) { + SetSpawnerOfBot(bot, spawner); + + // DevMsg("CTFBotSpawner %08x: found %u AddCond's\n", (uintptr_t)spawner, data.addconds.size()); + for (auto addcond : data.addconds) { + if (addcond.delay == 0.0f) { + DevMsg("CTFBotSpawner %08x: applying AddCond(%d, %f)\n", (uintptr_t)spawner, addcond.cond, addcond.duration); + bot->m_Shared->AddCond(addcond.cond, addcond.duration); + } else { + delayed_addconds.push_back({ + bot, + gpGlobals->curtime + addcond.delay, + addcond.cond, + addcond.duration, + }); } + } + + // if (data.action != Action_Default) { + // pending_action_overrides.emplace(bot, data.action); + // } + + if (data.use_human_model) { + DevMsg("CTFBotSpawner %08x: applying UseHumanModel on bot #%d\n", (uintptr_t)spawner, ENTINDEX(bot)); - if (data.use_human_model) { - // calling SetCustomModel with a nullptr string *seems* to reset the model - // dunno what the bool parameter should be; I think it doesn't matter for the nullptr case - bot->GetPlayerClass()->SetCustomModel(nullptr, true); - bot->UpdateModel(); - bot->SetBloodColor(BLOOD_COLOR_RED); - } + // calling SetCustomModel with a nullptr string *seems* to reset the model + // dunno what the bool parameter should be; I think it doesn't matter for the nullptr case + bot->GetPlayerClass()->SetCustomModel(nullptr, true); + bot->UpdateModel(); + bot->SetBloodColor(BLOOD_COLOR_RED); } } } @@ -309,18 +378,26 @@ namespace Mod_Pop_TFBot_Extensions DETOUR_DECL_MEMBER(Action *, CTFBotScenarioMonitor_DesiredScenarioAndClassAction, CTFBot *actor) { - bool override_mobber = false; + ActionType action = Action_Default; - for (auto it = action_override_mobber.begin(); it != action_override_mobber.end(); ) { - if (*it == actor) { - it = action_override_mobber.erase(it); - override_mobber = true; - } else { - ++it; + CTFBotSpawner *spawner = GetSpawnerOfBot(actor); + if (spawner != nullptr) { + auto it = spawners.find(spawner); + if (it != spawners.end()) { + SpawnerData& data = (*it).second; + action = data.action; } } - if (override_mobber) { + switch (action) { + case Action_FetchFlag: + DevMsg("CTFBotSpawner: setting initial action of bot #%d to FetchFlag\n", ENTINDEX(actor)); + return CTFBotFetchFlag::New(); + case Action_PushToCapturePoint: + DevMsg("CTFBotSpawner: setting initial action of bot #%d to PushToCapturePoint[-->FetchFlag]\n", ENTINDEX(actor)); + return CTFBotPushToCapturePoint::New(CTFBotFetchFlag::New()); + case Action_Mobber: + DevMsg("CTFBotSpawner: setting initial action of bot #%d to Mobber\n", ENTINDEX(actor)); return new CTFBotMobber(); } @@ -328,6 +405,52 @@ namespace Mod_Pop_TFBot_Extensions } + /* make engiebots that don't have any Action override always have Attributes + * IgnoreFlag, so that we can safely remove the special-case class check in + * CTFBot::GetFlagToFetch */ + DETOUR_DECL_MEMBER(void, CTFBot_OnEventChangeAttributes, const CTFBot::EventChangeAttributes_t *ecattr) + { + auto bot = reinterpret_cast(this); + + if (bot->IsPlayerClass(TF_CLASS_ENGINEER)) { + CTFBotSpawner *spawner = GetSpawnerOfBot(bot); + if (spawner != nullptr) { + auto it = spawners.find(spawner); + if (it != spawners.end()) { + SpawnerData& data = (*it).second; + + if (data.action == Action_Default) { + DevMsg("CTFBot::OnEventChangeAttributes(engie #%d): adding Attributes IgnoreFlag\n", ENTINDEX(bot)); + // const_cast(ecattr)->m_nBotAttrs |= CTFBot::ATTR_IGNORE_FLAG; + (int&)(ecattr->m_nBotAttrs) |= CTFBot::ATTR_IGNORE_FLAG; + } else { + DevMsg("CTFBot::OnEventChangeAttributes(engie #%d): not adding Attributes IgnoreFlag due to Action override\n", ENTINDEX(bot)); + } + } + } + } + + DETOUR_MEMBER_CALL(CTFBot_OnEventChangeAttributes)(ecattr); + } + + + RefCount rc_CTFBot_GetFlagToFetch; + DETOUR_DECL_MEMBER(CCaptureFlag *, CTFBot_GetFlagToFetch) + { + SCOPED_INCREMENT(rc_CTFBot_GetFlagToFetch); + return DETOUR_MEMBER_CALL(CTFBot_GetFlagToFetch)(); + } + + DETOUR_DECL_MEMBER(bool, CTFPlayer_IsPlayerClass, int iClass) + { + if (rc_CTFBot_GetFlagToFetch > 0 && iClass == TF_CLASS_ENGINEER) { + return false; + } + + return DETOUR_MEMBER_CALL(CTFPlayer_IsPlayerClass)(iClass); + } + + // // TEST! REMOVE ME! // DETOUR_DECL_MEMBER(const char *, CTFPlayer_GetOverrideStepSound, const char *pszBaseStepSoundName) // { @@ -357,6 +480,11 @@ namespace Mod_Pop_TFBot_Extensions MOD_ADD_DETOUR_MEMBER(CTFBotScenarioMonitor_DesiredScenarioAndClassAction, "CTFBotScenarioMonitor::DesiredScenarioAndClassAction"); + MOD_ADD_DETOUR_MEMBER(CTFBot_OnEventChangeAttributes, "CTFBot::OnEventChangeAttributes"); + + MOD_ADD_DETOUR_MEMBER(CTFBot_GetFlagToFetch, "CTFBot::GetFlagToFetch"); + MOD_ADD_DETOUR_MEMBER(CTFPlayer_IsPlayerClass, "CTFPlayer::IsPlayerClass"); + // TEST! REMOVE ME! // MOD_ADD_DETOUR_MEMBER(CTFPlayer_GetOverrideStepSound, "CTFPlayer::GetOverrideStepSound"); // MOD_ADD_DETOUR_MEMBER(CTFPlayer_GetSceneSoundToken, "CTFPlayer::GetSceneSoundToken"); @@ -365,11 +493,13 @@ namespace Mod_Pop_TFBot_Extensions virtual void OnUnload() override { spawners.clear(); + ClearAllTracking(); } virtual void OnDisable() override { spawners.clear(); + ClearAllTracking(); } virtual bool ShouldReceiveFrameEvents() const override { return this->IsEnabled(); } diff --git a/src/mod/pop/tfbot_extensions__disasm_patch_experiment.cpp b/src/mod/pop/tfbot_extensions__disasm_patch_experiment.cpp new file mode 100644 index 00000000..02a3ff55 --- /dev/null +++ b/src/mod/pop/tfbot_extensions__disasm_patch_experiment.cpp @@ -0,0 +1,250 @@ +// class CPatch_CTFBot_GetFlagToFetch : public IPatch +// { +// public: +// CPatch_CTFBot_GetFlagToFetch() {} +// +// virtual bool Verbose() const override { return false; } +// virtual bool VerifyOnly() const override { return false; } +// +// virtual bool Init() override +// { +// +// } +// virtual bool Check() override +// { +// +// } +// +// virtual void Apply() override +// { +// +// } +// virtual void UnApply() override +// { +// +// } +// +// private: +// Disassembler m_Disasm; +// +// void *m_pFuncAddr = nullptr; +// +// +// }; + + class CPatch_CTFBot_GetFlagToFetch : public CPatch + { + public: + CPatch_CTFBot_GetFlagToFetch() : CPatch(s_Size), m_Buf(s_Size), m_Mask(s_Size) {} + + virtual const char *GetFuncName() const override { return "CTFBot::GetFlagToFetch"; } + + virtual uint32_t GetFuncOffMin() const override { return this->m_FuncOff; } + virtual uint32_t GetFuncOffMax() const override { return this->m_FuncOff; } + + virtual bool GetVerifyInfo(ByteBuf& buf, ByteBuf& mask) const override + { + if (!this->m_bFound) return false; + + // TODO + return false; + } + + virtual bool GetPatchInfo(ByteBuf& buf, ByteBuf& mask) const override + { + if (!this->m_bFound) return false; + + // TODO + return false; + } + + private: + virtual bool PostInit() override + { + auto func_base = (uintptr_t)this->GetFuncAddr(); + + auto l_is_call_to_addr = [](const auto& insn, void *addr){ + if (insn.ID() != X86_INS_CALL) return false; + + auto operands = insn.Operands(); + if (operands.size() != 1) return false; + + auto op0 = operands[0]; + if (op0.Type() != X86_OP_IMM || op0.Imm_U32() != (uintptr_t)addr) return false; + + return true; + }; + + auto l_is_push_imm32_arg = [](const auto& insn, uint32_t esp_off, uint32_t imm_val){ + if (insn.ID() != X86_INS_MOV) return false; + + auto operands = insn.Operands(); + if (operands.size() != 2) return false; + + auto op0 = operands[0]; + if (op0.Type() != X86_OP_MEM || op0.Size() != 4 || op0.Mem_Seg() != X86_REG_INVALID || + op0.Mem_Base() != X86_REG_ESP || op0.Mem_Index() != X86_REG_INVALID || op0.Mem_Disp() != esp_off) return false; + + auto op1 = operands[1]; + if (op1.Type() != X86_OP_IMM || op1.Size() != 4 || op1.Imm_U32() != imm_val) return false; + + return true; + }; + + auto l_is_test_al = [](const auto& insn){ + if (insn.ID() != X86_INS_TEST) return false; + + auto operands = insn.Operands(); + if (operands.size() != 2) return false; + + auto op0 = operands[0]; + if (op0.Type() != X86_OP_REG || op0.Reg() != X86_REG_AL) return false; + + auto op1 = operands[1]; + if (op1.Type() != X86_OP_REG || op1.Reg() != X86_REG_AL) return false; + + return true; + }; + + auto l_is_jcc_imm = [](const auto& insn){ + switch (insn.ID()) { + case X86_INS_JAE: + case X86_INS_JA: + case X86_INS_JBE: + case X86_INS_JB: + case X86_INS_JE: + case X86_INS_JGE: + case X86_INS_JG: + case X86_INS_JLE: + case X86_INS_JL: + case X86_INS_JNE: + case X86_INS_JNO: + case X86_INS_JNP: + case X86_INS_JNS: + case X86_INS_JO: + case X86_INS_JP: + case X86_INS_JS: + break; + default: + return false; + } + + auto operands = insn.Operands(); + if (operands.size() != 1) return false; + + auto op0 = operands[0]; + if (op0.Type() != X86_OP_IMM) return false; + + return true; + }; + + void *call_target = AddrManager::GetAddr("CTFPlayer::IsPlayerClass"); + if (call_target == nullptr) return false; + + Disassembler disasm; + auto result = disasm.Disassemble(func_base, s_DisasmLimit); + + auto it_arg4 = result.end(); + auto it_call = result.end(); + auto it_test = result.end(); + auto it_jcc = result.end(); + + for (auto i = result.begin(); i != result.end(); ++i) { + // auto insn = *i; + // uintptr_t off = insn.Addr() - func_base; + + // if (off >= 0x5e2 && off < 0x600) { + // DevMsg("\n+0x%03x: %s %s\n", off, insn.MnemonicStr(), insn.OperandStr()); + // + // DevMsg("Bytes:"); + // for (auto byte : insn.Bytes()) { + // DevMsg(" %02X", byte); + // } + // DevMsg("\n"); + // } + + /* find the call to CTFPlayer::IsPlayerClass */ + if (l_is_call_to_addr(*i, call_target)) { + it_call = i; + DevMsg("Found call @ +0x%03x\n", (*it_call).Addr() - func_base); + + for (auto j = i - 1; j >= i - 4 && j != result.begin(); --j) { + if (it_arg4 == result.end() && l_is_push_imm32_arg(*j, 0x4, TF_CLASS_ENGINEER)) { + it_arg4 = j; + DevMsg("Found arg4 @ +0x%03x\n", (*it_arg4).Addr() - func_base); + } + } + + for (auto j = i + 1; j <= i + 1 && j != result.end(); ++j) { + if (it_test == result.end() && l_is_test_al(*j)) { + it_test = j; + DevMsg("Found test @ +0x%03x\n", (*it_test).Addr() - func_base); + } + } + + for (auto j = i + 2; j <= i + 2 && j != result.end(); ++j) { + if (it_jcc == result.end() && l_is_jcc_imm(*j)) { + it_jcc = j; + DevMsg("Found jcc @ +0x%03x\n", (*it_jcc).Addr() - func_base); + } + } + } + } + + #if 0 + bool result = disasm.IterateRange(this->GetFuncAddr(), s_DisasmLimit, [=](const InstructionDetailed& insn){ + uint32_t off = insn.Addr() - (uintptr_t)this->GetFuncAddr(); + if (off >= 0x5e2 && off < 0x600) { + DevMsg("\n+0x%03x: %s %s\n", off, insn.MnemonicStr(), insn.OperandStr()); + + DevMsg("Bytes:"); + for (auto byte : insn.Bytes()) { + DevMsg(" %02X", byte); + } + DevMsg("\n"); + + + } + + /* + DevMsg("Insn @ %s+0x%03x: mnemonic '%s' opcode '%s'\n", + this->GetFuncName(), (insn.Addr() - (uintptr_t)this->GetFuncAddr()), + insn.MnemonicStr(), insn.OperandStr()); + + if (insn.ID() != X86_INS_CALL) return true; + + DevMsg("Call @ %s+0x%03x: mnemonic '%s' opcode '%s'\n", + this->GetFuncName(), (insn.Addr() - (uintptr_t)this->GetFuncAddr()), + insn.MnemonicStr(), insn.OperandStr()); + DevMsg("Bytes:"); + for (auto byte : insn.Bytes()) { + DevMsg(" %02X", byte); + } + DevMsg("\n");*/ + + return true; + + // TODO: return false if we find what we're looking for + // also set m_bFound + // also set m_FuncOff + // also set up m_Buf and m_Mask + }); + + DevMsg("PostInit: result is %d, error is '%s'\n", result, disasm.ErrorString()); + #endif + + // TODO: return false upon failure + return true; + } + + static constexpr size_t s_Size = 0x05; + + // ServerLinux 20161119a: @ +0x5f3 + static constexpr size_t s_DisasmLimit = 0x660; + + bool m_bFound = false; + uint32_t m_FuncOff = 0; + + ByteBuf m_Buf; + ByteBuf m_Mask; + }; diff --git a/src/mod/sniper/charge_uncap.cpp b/src/mod/sniper/charge_uncap.cpp index 22e2208d..2bd98ef2 100644 --- a/src/mod/sniper/charge_uncap.cpp +++ b/src/mod/sniper/charge_uncap.cpp @@ -17,9 +17,9 @@ namespace Mod_Sniper_Charge_Uncap 0xf3, 0x0f, 0x59, 0x40, 0x10, // +0025 mulss xmm0,DWORD PTR [eax+0x10] }; - struct CPatch_UncapChargeRate_Common : public IPatch + struct CPatch_UncapChargeRate_Common : public CPatch { - CPatch_UncapChargeRate_Common() : IPatch(sizeof(s_Buf)) {} + CPatch_UncapChargeRate_Common() : CPatch(sizeof(s_Buf)) {} virtual bool GetVerifyInfo(ByteBuf& buf, ByteBuf& mask) const override { @@ -71,9 +71,9 @@ namespace Mod_Sniper_Charge_Uncap 0xf3, 0x0f, 0x10, 0x05, 0x00, 0x00, 0x00, 0x00, // +002A movss xmm0,DWORD PTR [xxxxxxxx] }; - struct CPatch_UncapChargeRate_Common : public IPatch + struct CPatch_UncapChargeRate_Common : public CPatch { - CPatch_UncapChargeRate_Common() : IPatch(sizeof(s_Buf)) {} + CPatch_UncapChargeRate_Common() : CPatch(sizeof(s_Buf)) {} virtual bool GetVerifyInfo(ByteBuf& buf, ByteBuf& mask) const override { diff --git a/src/mod/util/confilter.cpp b/src/mod/util/confilter.cpp new file mode 100644 index 00000000..42dde9ec --- /dev/null +++ b/src/mod/util/confilter.cpp @@ -0,0 +1,37 @@ +#include "mod.h" + + +namespace Mod_Util_ConFilter +{ + + + + class CMod : public IMod + { + public: + CMod() : IMod("Util:ConFilter") + { + // MOD_ADD_DETOUR_STATIC(Con_ColorPrint, "Con_ColorPrint"); + } + }; + CMod s_Mod; + + + ConVar cvar_enable("sig_util_confilter", "0", FCVAR_NOTIFY, + "Utility: enable enhanced console message filtering system", + [](IConVar *pConVar, const char *pOldValue, float flOldValue) { + ConVarRef var(pConVar); + s_Mod.Toggle(var.GetBool()); + }); +} + + + + +// enhanced con_filter functionality? +// - regex based +// - multiple include filters +// - multiple exclude filters +// - multiple highlight filters (with color option) + +// MUST BE WINDOWS CLIENT COMPATIBLE diff --git a/src/mod/util/dtwatch.cpp b/src/mod/util/dtwatch.cpp new file mode 100644 index 00000000..d1ca9470 --- /dev/null +++ b/src/mod/util/dtwatch.cpp @@ -0,0 +1,34 @@ +#include "mod.h" + + +namespace Mod_Util_DTWatch +{ + +} + + + +// GOAL: enhanced version of dtwatchent/dtwatchvar/dtwatchclass +// allow multiple inclusion filters +// allow multiple exclusion filters +// allow specifying individual entities by name etc rather than entindex +// colored output if possible + + +// ShouldWatchThisProp +// determines filtering + +// Sendprop_UsingDebugWatch +// must return true for ShowEncodeDeltaWatchInfo to be called + +// ShowEncodeDeltaWatchInfo +// "delta entity: %i\n" +// "+ %s %s, %s, index %i, bits %i, value %s\n" + +// SendTable_WritePropList +// ConDMsg("= %i bits (%i bytes)\n") + + +// perhaps just hook into CBaseEntity::NetworkStateChanged? +// - calls CBaseEdict::StateChanged (2 versions) +// PROBLEM: all of this is inlined usually! diff --git a/src/mod/util/jumboframes.cpp b/src/mod/util/jumboframes.cpp index db978347..6c8c031f 100644 --- a/src/mod/util/jumboframes.cpp +++ b/src/mod/util/jumboframes.cpp @@ -19,11 +19,11 @@ namespace Mod_Util_JumboFrames #if defined _LINUX - class CPatch_ConVar_Max : public IPatch + class CPatch_ConVar_Max : public CPatch { public: CPatch_ConVar_Max(const char *name, float old_max, float new_max) : - IPatch(sizeof(float)), m_strName(name), m_flOldMax(old_max), m_flNewMax(new_max) {} + CPatch(sizeof(float)), m_strName(name), m_flOldMax(old_max), m_flNewMax(new_max) {} virtual const char *GetFuncName() const override { return this->m_strName.c_str(); } virtual uint32_t GetFuncOffMin() const override { return 0x0040; } @@ -51,9 +51,9 @@ namespace Mod_Util_JumboFrames constexpr uint8_t buf_NET_SendPacket_default[] = { 0xbe, 0xec, 0x04, 0x00, 0x00, // +0000 mov esi,1260 }; - struct CPatch_Net_SendPacket_Default : public IPatch + struct CPatch_Net_SendPacket_Default : public CPatch { - CPatch_Net_SendPacket_Default() : IPatch(sizeof(buf_NET_SendPacket_default)) {} + CPatch_Net_SendPacket_Default() : CPatch(sizeof(buf_NET_SendPacket_default)) {} virtual const char *GetFuncName() const override { return "NET_SendPacket"; } virtual uint32_t GetFuncOffMin() const override { return 0x0000; } virtual uint32_t GetFuncOffMax() const override { return 0x1200; } // @ +0x0c00 @@ -77,9 +77,9 @@ namespace Mod_Util_JumboFrames 0x81, 0xfe, 0xec, 0x04, 0x00, 0x00, // +0005 cmp esi,0x4ec 0x0f, 0x4d, 0xf0, // +000B cmovge esi,eax }; - struct CPatch_Net_SendPacket_Clamp : public IPatch + struct CPatch_Net_SendPacket_Clamp : public CPatch { - CPatch_Net_SendPacket_Clamp() : IPatch(sizeof(buf_NET_SendPacket_clamp)) {} + CPatch_Net_SendPacket_Clamp() : CPatch(sizeof(buf_NET_SendPacket_clamp)) {} virtual const char *GetFuncName() const override { return "NET_SendPacket"; } virtual uint32_t GetFuncOffMin() const override { return 0x0000; } virtual uint32_t GetFuncOffMax() const override { return 0x1200; } // @ +0x0855 @@ -108,9 +108,9 @@ namespace Mod_Util_JumboFrames 0x53, // +0005 push ebx 0x81, 0xec, 0x8c, 0x05, 0x00, 0x00, // +0006 sub esp,0x58c }; - struct CPatch_Net_SendLong_Prologue : public IPatch + struct CPatch_Net_SendLong_Prologue : public CPatch { - CPatch_Net_SendLong_Prologue() : IPatch(sizeof(buf_NET_SendLong_prologue)) {} + CPatch_Net_SendLong_Prologue() : CPatch(sizeof(buf_NET_SendLong_prologue)) {} virtual const char *GetFuncName() const override { return "NET_SendLong"; } virtual uint32_t GetFuncOffMin() const override { return 0x0000; } virtual uint32_t GetFuncOffMax() const override { return 0x0000; } @@ -140,9 +140,9 @@ namespace Mod_Util_JumboFrames 0x5d, // +0009 pop ebp 0xc3, // +000A ret }; - struct CPatch_Net_SendLong_Epilogue : public IPatch + struct CPatch_Net_SendLong_Epilogue : public CPatch { - CPatch_Net_SendLong_Epilogue() : IPatch(sizeof(buf_NET_SendLong_epilogue)) {} + CPatch_Net_SendLong_Epilogue() : CPatch(sizeof(buf_NET_SendLong_epilogue)) {} virtual const char *GetFuncName() const override { return "NET_SendLong"; } virtual uint32_t GetFuncOffMin() const override { return 0x0000; } virtual uint32_t GetFuncOffMax() const override { return 0x0700; } // @ +0x0497 @@ -171,9 +171,9 @@ namespace Mod_Util_JumboFrames 0x85, 0xd2, // +0016 test edx,edx 0x89, 0x85, 0xdc, 0xfa, 0xff, 0xff, // +0018 mov dword ptr [ebp+packet+0x4],eax }; - struct CPatch_Net_SendLong_PacketRef1 : public IPatch + struct CPatch_Net_SendLong_PacketRef1 : public CPatch { - CPatch_Net_SendLong_PacketRef1() : IPatch(sizeof(buf_NET_SendLong_packet_ref1)) {} + CPatch_Net_SendLong_PacketRef1() : CPatch(sizeof(buf_NET_SendLong_packet_ref1)) {} virtual const char *GetFuncName() const override { return "NET_SendLong"; } virtual uint32_t GetFuncOffMin() const override { return 0x0000; } virtual uint32_t GetFuncOffMax() const override { return 0x0700; } // @ +0x0132 @@ -213,9 +213,9 @@ namespace Mod_Util_JumboFrames 0x66, 0x89, 0x85, 0xe0, 0xfa, 0xff, 0xff, // +002C mov word ptr [ebp+packet+0x8],ax 0xe8, 0x85, 0x5e, 0x23, 0x00, // +0033 call memcpy }; - struct CPatch_Net_SendLong_PacketRef2 : public IPatch + struct CPatch_Net_SendLong_PacketRef2 : public CPatch { - CPatch_Net_SendLong_PacketRef2() : IPatch(sizeof(buf_NET_SendLong_packet_ref2)) {} + CPatch_Net_SendLong_PacketRef2() : CPatch(sizeof(buf_NET_SendLong_packet_ref2)) {} virtual const char *GetFuncName() const override { return "NET_SendLong"; } virtual uint32_t GetFuncOffMin() const override { return 0x0000; } virtual uint32_t GetFuncOffMax() const override { return 0x0700; } // @ +0x035b @@ -255,9 +255,9 @@ namespace Mod_Util_JumboFrames 0x89, 0x4c, 0x24, 0x04, // +002E mov [ebp+0x4],ecx 0xe8, 0x09, 0xb0, 0xff, 0xff, // +0032 call NET_SendTo }; - struct CPatch_Net_SendLong_PacketRef3 : public IPatch + struct CPatch_Net_SendLong_PacketRef3 : public CPatch { - CPatch_Net_SendLong_PacketRef3() : IPatch(sizeof(buf_NET_SendLong_packet_ref3)) {} + CPatch_Net_SendLong_PacketRef3() : CPatch(sizeof(buf_NET_SendLong_packet_ref3)) {} virtual const char *GetFuncName() const override { return "NET_SendLong"; } virtual uint32_t GetFuncOffMin() const override { return 0x0000; } virtual uint32_t GetFuncOffMax() const override { return 0x0700; } // @ +0x03c0 @@ -288,9 +288,9 @@ namespace Mod_Util_JumboFrames 0x85, 0xd2, // +0016 test edx,edx 0x89, 0x85, 0xdc, 0xfa, 0xff, 0xff, // +0018 mov dword ptr [ebp+packet+0x4],eax }; - struct CPatch_Net_SendLong_PacketRef1 : public IPatch + struct CPatch_Net_SendLong_PacketRef1 : public CPatch { - CPatch_Net_SendLong_PacketRef1() : IPatch(sizeof(buf_NET_SendLong_packet_ref1)) {} + CPatch_Net_SendLong_PacketRef1() : CPatch(sizeof(buf_NET_SendLong_packet_ref1)) {} virtual const char *GetFuncName() const override { return "NET_SendLong"; } virtual uint32_t GetFuncOffMin() const override { return 0x0000; } virtual uint32_t GetFuncOffMax() const override { return 0x0700; } // @ +0x0132 @@ -337,9 +337,9 @@ namespace Mod_Util_JumboFrames 0x66, 0x89, 0x85, 0xe0, 0xfa, 0xff, 0xff, // +002C mov word ptr [ebp+packet+0x8],ax 0xe8, 0x85, 0x5e, 0x23, 0x00, // +0033 call memcpy }; - struct CPatch_Net_SendLong_PacketRef2 : public IPatch + struct CPatch_Net_SendLong_PacketRef2 : public CPatch { - CPatch_Net_SendLong_PacketRef2() : IPatch(sizeof(buf_NET_SendLong_packet_ref2)) {} + CPatch_Net_SendLong_PacketRef2() : CPatch(sizeof(buf_NET_SendLong_packet_ref2)) {} virtual const char *GetFuncName() const override { return "NET_SendLong"; } virtual uint32_t GetFuncOffMin() const override { return 0x0000; } virtual uint32_t GetFuncOffMax() const override { return 0x0700; } // @ +0x035b @@ -385,9 +385,9 @@ namespace Mod_Util_JumboFrames 0x89, 0x4c, 0x24, 0x04, // +002E mov [ebp+0x4],ecx 0xe8, 0x09, 0xb0, 0xff, 0xff, // +0032 call NET_SendTo }; - struct CPatch_Net_SendLong_PacketRef3 : public IPatch + struct CPatch_Net_SendLong_PacketRef3 : public CPatch { - CPatch_Net_SendLong_PacketRef3() : IPatch(sizeof(buf_NET_SendLong_packet_ref3)) {} + CPatch_Net_SendLong_PacketRef3() : CPatch(sizeof(buf_NET_SendLong_packet_ref3)) {} virtual const char *GetFuncName() const override { return "NET_SendLong"; } virtual uint32_t GetFuncOffMin() const override { return 0x0000; } virtual uint32_t GetFuncOffMax() const override { return 0x0700; } // @ +0x03c0 diff --git a/src/mod/util/netmsg_client.cpp b/src/mod/util/netmsg_client.cpp new file mode 100644 index 00000000..98119591 --- /dev/null +++ b/src/mod/util/netmsg_client.cpp @@ -0,0 +1,27 @@ +#include "mod.h" +#include "mod/util/netmsg_shared.h" + + +namespace Mod_Util_NetMsg_Client +{ + + + + class CMod : public IMod + { + public: + CMod() : IMod("Util:NetMsg_Client") + { + + } + }; + CMod s_Mod; + + + ConVar cvar_enable("sig_util_netmsg_client", "0", FCVAR_NOTIFY, + "Utility: custom net messages: client", + [](IConVar *pConVar, const char *pOldValue, float flOldValue) { + ConVarRef var(pConVar); + s_Mod.Toggle(var.GetBool()); + }); +} diff --git a/src/mod/util/netmsg_server.cpp b/src/mod/util/netmsg_server.cpp new file mode 100644 index 00000000..7042b290 --- /dev/null +++ b/src/mod/util/netmsg_server.cpp @@ -0,0 +1,27 @@ +#include "mod.h" +#include "mod/util/netmsg_shared.h" + + +namespace Mod_Util_NetMsg_Server +{ + + + + class CMod : public IMod + { + public: + CMod() : IMod("Util:NetMsg_Server") + { + + } + }; + CMod s_Mod; + + + ConVar cvar_enable("sig_util_netmsg_server", "0", FCVAR_NOTIFY, + "Utility: custom net messages: server", + [](IConVar *pConVar, const char *pOldValue, float flOldValue) { + ConVarRef var(pConVar); + s_Mod.Toggle(var.GetBool()); + }); +} diff --git a/src/mod/util/netmsg_shared.h b/src/mod/util/netmsg_shared.h new file mode 100644 index 00000000..8328668e --- /dev/null +++ b/src/mod/util/netmsg_shared.h @@ -0,0 +1,47 @@ +#ifndef _INCLUDE_SIGSEGV_MOD_UTIL_NETMSG_SHARED_H_ +#define _INCLUDE_SIGSEGV_MOD_UTIL_NETMSG_SHARED_H_ + + +// 20161013a: TF2's SVC_LASTMSG is 33 +constexpr int svc_SigOverlay = 34; + + +class SVC_SigOverlay : public INetMessage +{ +public: + SVC_SigOverlay() {} + virtual ~SVC_SigOverlay() {} + + virtual void SetNetChannel(INetChannel *netchan) override { this->m_pNetChannel = netchan; } + virtual void SetReliable(bool state) override { this->m_bReliable = state; } + + virtual bool Process() override; + + virtual bool ReadFromBuffer(bf_read& buffer) override; + virtual bool WriteToBuffer(bf_read& buffer) override; + + virtual bool IsReliable() const override { return this->m_bReliable; } + virtual int GetType() const override { return svc_SigOverlay; } + virtual int GetGroup() const override { return INetChannelInfo::GENERIC; } + virtual const char *GetName() const override { return "svc_SigOverlay"; } + virtual INetChannel *GetNetChannel() const override { return this->m_pNetChannel; } + + virtual const char *ToString() const override; + +private: + bool m_bReliable = true; + INetChannel *m_pNetChannel = nullptr; +}; + + +#error TODO: register message on client side +// - in CBaseClientState::ConnectionStart (or later): +// - construct a new SVC_whatever (on the heap, leak it) +// - and call chan->RegisterMessage(msg) +// - if not in ConnectionStart, chan will presumably this->m_NetChannel (?) + +#error TODO: send message on server side +#error TODO: implement the remaining funcs + + +#endif diff --git a/src/mod/util/overlay_send.cpp b/src/mod/util/overlay_send.cpp index 8e12efaa..28c248f7 100644 --- a/src/mod/util/overlay_send.cpp +++ b/src/mod/util/overlay_send.cpp @@ -760,7 +760,6 @@ namespace Mod_Util_Overlay_Send MSG_END(); } - void Send_Clear() { MSG_BEGIN(); diff --git a/src/mod/util/screenshot_png.cpp b/src/mod/util/screenshot_png.cpp new file mode 100644 index 00000000..78989ced --- /dev/null +++ b/src/mod/util/screenshot_png.cpp @@ -0,0 +1,51 @@ +#include "mod.h" +#include "util/scope.h" + + +namespace Mod_Util_Screenshot_PNG +{ + // detour CL_TakeSnapshotAndSwap + // - not strictly necessary actually + + // detour CVideoMode_Common::TakeSnapshotTGA + // - refcount (for TGAWriter::WriteToBuffer) + // - wrap the call, but change pFileName to modify the extension from tga to png + + // detour TGAWriter::WriteToBuffer + // (not a member func, actually a namespace static func) + // (only when called from CVideoMode_Common::TakeSnapshotTGA) + // - //////return false so that TakeSnapshotTGA won't attempt to write a TGA file + // - return actual success value + // - completely take over and reimplement as a write-PNG-to-CUtlBuffer function + + + DETOUR_DECL_MEMBER(void, CVideoMode_Common_TakeSnapshotTGA, const char *pFilename) + { + #warning TODO + } + + DETOUR_DECL_STATIC(bool, TGAWriter_WriteToBuffer, unsigned char *pImageData, CUtlBuffer& buffer, int width, int height, ImageFormat srcFormat, ImageFormat dstFormat) + { + #warning TODO + } + + + class CMod : public IMod + { + public: + CMod() : IMod("Util:Screenshot_PNG") + { + MOD_ADD_DETOUR_MEMBER(CVideoMode_Common_TakeSnapshotTGA, "CVideoMode_Common::TakeSnapshotTGA"); + MOD_ADD_DETOUR_STATIC(TGAWriter_WriteToBuffer, "TGAWriter::WriteToBuffer"); + } + }; + CMod s_Mod; + + + ConVar cvar_enable("sig_util_screenshot_png", "0", FCVAR_NOTIFY, + "Utility: instead of writing TGA files for screenshots, write PNG files", + [](IConVar *pConVar, const char *pOldValue, float flOldValue) { + ConVarRef var(pConVar); + s_Mod.Toggle(var.GetBool()); + }); +} diff --git a/src/mod/util/serialize_spew.cpp b/src/mod/util/serialize_spew.cpp new file mode 100644 index 00000000..fdba9a1e --- /dev/null +++ b/src/mod/util/serialize_spew.cpp @@ -0,0 +1,75 @@ +#include "mod.h" +#include "util/scope.h" + + +#if 0 + + +namespace Mod_Util_Serialize_Spew +{ + FILE *f = nullptr; + + + // block SpewOutputFunc(x) calls from: + // - CShaderSystem::PrepForShaderDraw + // - CShaderSystem::DoneWithShaderDraw + + // or, block based on whether x is MySpewOutputFunc + + + DETOUR_DECL_STATIC(void, SpewOutputFunc, SpewOutputFunc_t func) + { + if (f != nullptr) { + // report the following info to fprintf(f, ...): + // thread ID + // func ptr addr + // LibMgr::WhichLibAtAddr(func ptr addr), if any + } + + DETOUR_STATIC_CALL(SpewOutputFunc)(func); + } + + + class CMod : public IMod + { + public: + CMod() : IMod("Util:Serialize_Spew") + { + #error need to setup SpewOutputFunc detour (can we use address-of operator on the name?) + } + + virtual bool OnLoad() override + { + f = fopen("spewlog2.txt", "w"); + return (f != nullptr); + } + + virtual void OnUnload() override + { + fclose(f); + f = nullptr; + } + }; + CMod s_Mod; + + + // method 1: carefully set up mutexes etc so that we don't attempt to add + // new spew from the main thread while CShaderSystem::PrintBufferedSpew is + // still in progress + + // method 2: just disable the damn buffered spew thing altogether and rely + // on the the Sys_SpewFunc suppression system based on the thread-local bool + // g_bInSpew + + // I like method 2 better + + + ConVar cvar_enable("sig_util_serialize_spew", "0", FCVAR_NOTIFY, + "Utility: fix spew ordering problems caused by CShaderSystem's spew buffering mechanism", + [](IConVar *pConVar, const char *pOldValue, float flOldValue) { + ConVarRef var(pConVar); + s_Mod.Toggle(var.GetBool()); + }); +} + +#endif diff --git a/src/mod/vgui/test.cpp b/src/mod/vgui/test.cpp new file mode 100644 index 00000000..24e83d99 --- /dev/null +++ b/src/mod/vgui/test.cpp @@ -0,0 +1,344 @@ +#include "mod.h" +#include "stub/baseentity.h" +#include "util/vgui.h" +#include "factory.h" + + +#if 0 +// CLIENT DEFINITION! +class CNetMessage : public INetMessage +{ +public: + CNetMessage() {} + virtual ~CNetMessage() {} + + virtual void SetNetChannel(INetChannel *netchan) override { this->m_NetChannel = netchan; } + virtual void SetReliable + + virtual int GetGroup() const override { return INetChannelInfo::GENERIC; } + virtual INetChannel *GetNetChannel() const override { return this->m_NetChannel; } + + virtual + +protected: + bool m_bReliable = true; + INetChannel *m_NetChannel = nullptr; +}; + +// ... +#endif + + +class Base_CmdKeyValues : public INetMessage +{ +public: + KeyValues *GetKeyValues() const { return this->m_pKeyValues; } + void SetKeyValues(KeyValues *kv) { this->m_pKeyValues = kv; } + +private: + bool m_bReliable; // +0x04 + INetChannel *m_NetChannel; // +0x08 + KeyValues *m_pKeyValues; // +0x0c +}; +class SVC_CmdKeyValues : public Base_CmdKeyValues {}; +class CLC_CmdKeyValues : public Base_CmdKeyValues {}; + + +namespace Mod_VGUI_Test +{ + bool IsClient() { return (ClientFactory() != nullptr); } + + + DETOUR_DECL_MEMBER(bool, SVC_CmdKeyValues_Process) + { + auto msg = reinterpret_cast(this); + + KeyValues *kv = msg->GetKeyValues(); + if (kv != nullptr) { + Msg("[CLIENT] Got SVC_CmdKeyValues with this content:\n"); + KeyValuesDumpAsDevMsg(kv, 0, 0); + } else { + Warning("[CLIENT] Got SVC_CmdKeyValues, but the KeyValues ptr is nullptr!\n"); + } + + return DETOUR_MEMBER_CALL(SVC_CmdKeyValues_Process)(); + } + + + class CMod : public IMod + { + public: + CMod() : IMod("VGUI:Test") + { + if (IsClient()) { + // ideally we'd detour CClientState::ProcessCmdKeyValues, but + // that turns out to be difficult to do on Windows + MOD_ADD_DETOUR_MEMBER(SVC_CmdKeyValues_Process, "SVC_CmdKeyValues::Process"); + } + } + }; + CMod s_Mod; + + + ConVar cvar_enable("sig_vgui_test", "0", FCVAR_NOTIFY, + "Mod: test custom VGUI elements", + [](IConVar *pConVar, const char *pOldValue, float flOldValue) { + ConVarRef var(pConVar); + s_Mod.Toggle(var.GetBool()); + }); + + + CON_COMMAND(sig_vgui_test_sv_kvtest, "") + { + KeyValues *kv = new KeyValues("CustomVGUI"); + kv->SetString("key1", "value1"); + + KeyValues *sub = kv->FindKey("sub", true); + sub->SetString("key2", "value2"); + + Msg("[SERVER] Sending SVC_CmdKeyValues with this content:\n"); + KeyValuesDumpAsDevMsg(kv, 0, 0); + + for (int i = 0; i < sv->GetNumClients(); ++i) { + IClient *client = sv->GetClient(i); + if (client == nullptr) continue; + + edict_t *edict = INDEXENT(client->GetPlayerSlot() + 1); + if (edict == nullptr) continue; + + Msg("[SERVER] Sending to client '%s'\n", client->GetClientName()); + engine->ClientCommandKeyValues(edict, kv); + } + } + + + CON_COMMAND(sig_vgui_test_cl_list, "") + { + if (!IsClient()) return; + + CUtlVector names; + CBuildFactoryHelper::GetFactoryNames(names); + + Msg("VGUI factories available:\n"); + for (auto name : names) { + Msg("%s\n", name); + } + } + + + CON_COMMAND(sig_vgui_test_cl_create, "") + { + if (!IsClient()) return; + + CUtlVector names; + CBuildFactoryHelper::GetFactoryNames(names); + + Msg("VGUI factories available:\n"); + for (auto name : names) { + Msg("%s\n", name); + } + } + + +#if 0 + CON_COMMAND(sig_vgui_test_delete, "") + { + if (!IsClient()) return; + if (pMyFrame == nullptr) return; + + Msg("Will delete MyFrame\n"); + delete pMyFrame; + + // TODO: try this + // Msg("Will delete MyFrame\n"); + // delete reinterpret_cast(pMyFrame); + + // TODO: try this + // Msg("Will delete MyFrame\n"); + // g_pVGuiPanel->DeletePanel(pMyFrame->GetVPanel()); + + // Msg("Will delete MyFrame\n"); + // pMyFrame->~MyFrame(); + // ::operator delete(pMyFrame); + // // (might want a reinterpret_cast version of this...?) + + // ALSO TRY: vcall ->MarkForDeletion() + + // last option: + // manually call non-deleting dtor via thunk + // then call ::operator delete + + pMyFrame = nullptr; + + Msg("Success\n"); + } +#endif + + + + +#if 0 + MyPanel *pMyPanel = nullptr; + + CON_COMMAND(sig_vgui_test_create, "") + { + if (!IsClient()) return; + if (pMyPanel != nullptr) return; + + Msg("Will create MyPanel\n"); + pMyPanel = MyPanel::Create(); + if (pMyPanel == nullptr) { + Msg("MyPanel::Create returned nullptr\n"); + return; + } + + // Msg("Will call GetClientModeNormal\n"); + // IClientMode *pClientMode = GetClientModeNormal(); + + Msg("g_pClientMode = %08x\n", (uintptr_t)g_pClientMode); + for (int i = 0; i < 0x100; i += 4) { + Msg("g_pClientMode->VT[%04x] = %08x\n", i, (*(uintptr_t **)(g_pClientMode))[i / 4]); + } + + Msg("Will call IClientMode::GetViewport\n"); + vgui::Panel *pViewport = g_pClientMode->GetViewport(); + if (pViewport == nullptr) { + Msg("IClientMode::GetViewport returned nullptr\n"); + return; + } + + Msg("Will set parent to %08x\n", (uintptr_t)pViewport); + pMyPanel->SetParent(pViewport); + + KeyValues *pSettings = new KeyValues("Settings"); + pSettings->SetString("fieldName", "MyPanel"); + pSettings->SetString("xpos", "100"); + pSettings->SetString("ypos", "10"); + pSettings->SetString("wide", "20"); + pSettings->SetString("tall", "200"); + pSettings->SetInt ("visible", 1); + pSettings->SetInt ("enabled", 1); + Msg("Will apply panel settings from KV\n"); + pMyPanel->ApplySettings(pSettings); + pSettings->deleteThis(); + + // Msg("Will set name to \"MyPanel\"\n"); + // pMyPanel->SetName("MyPanel"); + // + // Msg("Will set pos to (10, 10)\n"); + // pMyPanel->SetPos(10, 10); + // + // Msg("Will set size to (40, 30)\n"); + // pMyPanel->SetSize(40, 30); + + + + Msg("Success\n"); + } + + CON_COMMAND(sig_vgui_test_delete, "") + { + if (!IsClient()) return; + if (pMyPanel == nullptr) return; + + Msg("Will delete MyPanel\n"); + delete pMyPanel; + + pMyPanel = nullptr; + + Msg("Success\n"); + } +#endif + + + + +#if 0 + +#if defined _LINUX + constexpr ptrdiff_t OFF_PANEL_DTOR_D1 = 0x007c; + constexpr ptrdiff_t OFF_PANEL_DTOR_D0 = 0x0080; + + class FakePanel; + void (FakePanel:: *real_D1)() = nullptr; + void (FakePanel:: *real_D0)() = nullptr; + + class FakePanel + { + public: + void dtor_D1() + { + Msg("Panel dtor D1 called! [this: %08x]\n", (uintptr_t)this); + (this->*real_D1)(); + } + + void dtor_D0() + { + Msg("Panel dtor D0 called! [this: %08x]\n", (uintptr_t)this); + (this->*real_D0)(); + } + }; +#endif + +#if defined _WINDOWS + constexpr ptrdiff_t OFF_PANEL_DTOR = 0x007c; + + class FakePanel; + void (FakePanel:: *real_dtor)(bool) = nullptr; + + class FakePanel + { + public: + void dtor(bool b1) + { + Msg("Panel dtor called! [this: %08x] [b1: %d]\n", (uintptr_t)this, b1); + (this->*real_dtor)(b1); + } + }; +#endif + + + uintptr_t my_vtable[0x1000]; + + + vgui::Panel *pPanel = nullptr; + vgui::VPANEL vpanel = 0; + + CON_COMMAND(sig_vgui_test_create, "") + { + if (!IsClient()) return; + + vgui::Panel *pPanel = CBuildFactoryHelper::InstancePanel("Panel"); + Msg("InstancePanel returned ptr: %08x\n", (uintptr_t)pPanel); + if (pPanel == nullptr) return; + + vpanel = pPanel->GetVPanel(); + Msg("Created VPANEL: %08x\n", vpanel); + +#if defined _WINDOWS + uintptr_t **ppVT = reinterpret_cast(pPanel); + memcpy(my_vtable, *ppVT, sizeof(my_vtable)); + *ppVT = my_vtable; + + uintptr_t *p_dtor_entry = *ppVT + (OFF_PANEL_DTOR / 4); + Msg("Panel dtor was: %08x\n", *p_dtor_entry); + + real_dtor = MakePtrToMemberFunc((void *)(*p_dtor_entry)); + *p_dtor_entry = (uintptr_t)GetAddrOfMemberFunc(&FakePanel::dtor); + Msg("Panel dtor is now: %08x\n", *p_dtor_entry); +#endif + + + + } + + CON_COMMAND(sig_vgui_test_delete, "") + { + if (!IsClient()) return; + + Msg("About to delete VPANEL: %08x\n", vpanel); + g_pVGuiPanel->DeletePanel(vpanel); + Msg("Deleted panel\n"); + } +#endif +} diff --git a/src/mod/visualize/backstab.cpp b/src/mod/visualize/backstab.cpp new file mode 100644 index 00000000..5efe45bc --- /dev/null +++ b/src/mod/visualize/backstab.cpp @@ -0,0 +1,257 @@ +#include "mod.h" +#include "stub/tfplayer.h" +#include "stub/tfweaponbase.h" +#include "util/iterate.h" + + +namespace Mod_Visualize_Backstab +{ + ConVar cvar_dotproduct("sig_visualize_backstab_dotproduct", "0", FCVAR_NOTIFY, + "Visualization: show dot products instead of lines and stuff"); + ConVar cvar_dotproduct_vectors("sig_visualize_backstab_dotproduct_vectors", "0", FCVAR_NOTIFY, + "Visualization: also draw the relevant vectors"); + + + void DrawDotProductFigures(CTFPlayer *spy, CTFPlayer *victim) + { + Vector2D wsc_spy_to_victim = (victim->WorldSpaceCenter() - spy->WorldSpaceCenter()).AsVector2D(); + wsc_spy_to_victim.NormalizeInPlace(); + + Vector temp1; spy->EyeVectors(&temp1); + Vector2D eye_spy = temp1.AsVector2D(); + eye_spy.NormalizeInPlace(); + + Vector temp2; victim->EyeVectors(&temp2); + Vector2D eye_victim = temp2.AsVector2D(); + eye_victim.NormalizeInPlace(); + + + float dot0 = DotProduct2D(wsc_spy_to_victim, eye_victim); + float dot1 = DotProduct2D(wsc_spy_to_victim, eye_spy); + float dot2 = DotProduct2D(eye_spy, eye_victim); + + float ang0 = RAD2DEG(acos(dot0)); + float ang1 = RAD2DEG(acos(dot1)); + float ang2 = RAD2DEG(acos(dot2)); + + bool ok0 = (dot0 > 0.0f); + bool ok1 = (dot1 > 0.5f); + bool ok2 = (dot2 > -0.3f); + + int r0 = (ok0 ? 0x20 : 0xff); int g0 = (ok0 ? 0xff : 0x20); + int r1 = (ok1 ? 0x20 : 0xff); int g1 = (ok1 ? 0xff : 0x20); + int r2 = (ok2 ? 0x20 : 0xff); int g2 = (ok2 ? 0xff : 0x20); + + +#if 1 + float yt = 0.06f; + float y0 = 0.10f; + float y1 = 0.14f; + float y2 = 0.18f; +#else + float yt = 0.78f; + float y0 = 0.82f; + float y1 = 0.86f; + float y2 = 0.90f; +#endif + + + // Column 1 + NDebugOverlay::ScreenText(0.03f, y0, "VictimView-to-DeltaPosition", + 0xc0, 0xc0, 0xc0, 0xff, 0.032f); + NDebugOverlay::ScreenText(0.03f, y1, " SpyView-to-DeltaPosition", + 0xc0, 0xc0, 0xc0, 0xff, 0.032f); + NDebugOverlay::ScreenText(0.03f, y2, " SpyView-to-VictimView", + 0xc0, 0xc0, 0xc0, 0xff, 0.032f); + + // Column 2 + NDebugOverlay::ScreenText(0.28f, yt, "DotProduct", + 0xff, 0xff, 0xff, 0xff, 0.032f); + NDebugOverlay::ScreenText(0.28f, y0, CFmtStr(" % 5.2f", dot0), + r0, g0, 0x20, 0xff, 0.032f); + NDebugOverlay::ScreenText(0.28f, y1, CFmtStr(" % 5.2f", dot1), + r1, g1, 0x20, 0xff, 0.032f); + NDebugOverlay::ScreenText(0.28f, y2, CFmtStr(" % 5.2f", dot2), + r2, g2, 0x20, 0xff, 0.032f); + + // Column 3 + NDebugOverlay::ScreenText(0.40f, yt, " Angle", + 0xff, 0xff, 0xff, 0xff, 0.032f); + NDebugOverlay::ScreenText(0.40f, y0, CFmtStr("%5.1f\u00b0", ang0), + r0, g0, 0x20, 0xff, 0.032f); + NDebugOverlay::ScreenText(0.40f, y1, CFmtStr("%5.1f\u00b0", ang1), + r1, g1, 0x20, 0xff, 0.032f); + NDebugOverlay::ScreenText(0.40f, y2, CFmtStr("%5.1f\u00b0", ang2), + r2, g2, 0x20, 0xff, 0.032f); + + // Column 4 + NDebugOverlay::ScreenText(0.47f, y0, "Behind the victim? Must be < 90\u00b0", + 0xc0, 0xc0, 0xc0, 0xff, 0.032f); + NDebugOverlay::ScreenText(0.47f, y1, "Looking toward the victim? Must be < 60\u00b0", + 0xc0, 0xc0, 0xc0, 0xff, 0.032f); + NDebugOverlay::ScreenText(0.47f, y2, "Facing same direction as victim? Must be < ~107.5\u00b0", + 0xc0, 0xc0, 0xc0, 0xff, 0.032f); + + + if (cvar_dotproduct_vectors.GetBool()) { + NDebugOverlay::HorzArrow(spy->EyePosition(), spy->EyePosition() + (150.0f * Vector(Vector2DExpand(eye_spy), 0.0f)), + 3.0f, 0xff, 0xff, 0x00, 0xff, true, 0.032f); + NDebugOverlay::HorzArrow(victim->EyePosition(), victim->EyePosition() + (150.0f * Vector(Vector2DExpand(eye_victim), 0.0f)), + 3.0f, 0xff, 0x00, 0xff, 0xff, true, 0.032f); + + Vector v1 = spy->WorldSpaceCenter(); v1.z = spy->EyePosition().z; + Vector v2 = victim->WorldSpaceCenter(); v2.z = victim->EyePosition().z; + + NDebugOverlay::HorzArrow(v1, v2, + 3.0f, 0x00, 0xff, 0xff, 0xff, true, 0.032f); + } + } + + + void DrawEyeXYLine(CTFPlayer *player, Color c, float length, float duration) + { + Vector vecEyePos = player->EyePosition(); + Vector vecEyeDir; player->EyeVectors(&vecEyeDir); + + vecEyeDir.z = 0.0f; + + NDebugOverlay::Line(vecEyePos, vecEyePos + (vecEyeDir * length), c[0], c[1], c[2], false, duration); + } + + + void DrawEyeVectors() + { + ForEachTFPlayer([](CTFPlayer *player){ + if (player->GetTeamNumber() < FIRST_GAME_TEAM) return; + if (!player->IsAlive()) return; + + if (cvar_dotproduct.GetBool()) return; + + DrawEyeXYLine(player, Color(0xff, 0xff, 0xff, 0xff), 2000.0f, 0.032f); + }); + } + + void DrawPlayerHulls() + { + ForEachTFPlayer([](CTFPlayer *player){ + if (player->GetTeamNumber() < FIRST_GAME_TEAM) return; + if (!player->IsAlive()) return; + + if (cvar_dotproduct.GetBool() && !cvar_dotproduct_vectors.GetBool() && player->IsPlayerClass(TF_CLASS_SPY)) return; + + Vector vecOrigin = player->GetAbsOrigin(); + Vector vecMins = player->CollisionProp()->OBBMins(); + Vector vecMaxs = player->CollisionProp()->OBBMaxs(); + + NDebugOverlay::Box(vecOrigin, vecMins, vecMaxs, 0xc0, 0xc0, 0xc0, 00, 0.032f); + }); + } + + void DrawKnifeLines() + { + ForEachTFPlayer([](CTFPlayer *player){ + if (player->GetTeamNumber() < FIRST_GAME_TEAM) return; + if (!player->IsAlive()) return; + + auto knife = rtti_cast(player->GetActiveTFWeapon()); + if (knife == nullptr) return; + + if (cvar_dotproduct.GetBool()) { + ForEachTFPlayer([=](CTFPlayer *victim){ + if (victim->GetTeamNumber() < FIRST_GAME_TEAM) return; + if (!victim->IsAlive()) return; + + if (ENTINDEX(victim) == ENTINDEX(player)) return; + + DrawDotProductFigures(player, victim); + }); + } else { + trace_t tr; + if (!knife->DoSwingTrace(tr)) return; + + ForEachTFPlayer([&](CTFPlayer *victim){ + if (victim->GetTeamNumber() < FIRST_GAME_TEAM) return; + if (!victim->IsAlive()) return; + + if (ENTINDEX(victim) == ENTINDEX(player)) return; + + if (tr.m_pEnt == nullptr || ENTINDEX(tr.m_pEnt) != ENTINDEX(victim)) return; + + bool bBehind = knife->IsBehindAndFacingTarget(victim); + + int r = (bBehind ? 0x00 : 0xff); + int g = (bBehind ? 0xff : 0x00); + + // Ray_t ray; + // ray.Init(tr.startpos, tr.startpos + (100.0f * (tr.endpos - tr.startpos).Normalized())); + // enginetrace->ClipRayToEntity(ray, CONTENTS_SOLID, tr.m_pEnt, &tr); + + NDebugOverlay::Line(tr.startpos, tr.endpos, r, g, 0x00, false, 3600.0f); + }); + } + }); + } + + + // realization: there are two tests going on here (besides the melee trace itself) + // 1. is the spy BEHIND the victim + // 2. is the spy FACING the same direction as the victim + // + // ... + + + class CMod : public IMod, public IFrameUpdateListener + { + public: + CMod() : IMod("Visualize:Backstab") {} + + virtual bool ShouldReceiveFrameEvents() const override { return this->IsEnabled(); } + + virtual void FrameUpdatePostEntityThink() override + { + static long frame = 0; + if (frame++ % 2 != 0) return; + + DrawEyeVectors(); + DrawPlayerHulls(); + DrawKnifeLines(); + } + }; + CMod s_Mod; + + + ConVar cvar_enable("sig_visualize_backstab", "0", FCVAR_NOTIFY, + "Visualization: draw information about backstab angles", + [](IConVar *pConVar, const char *pOldValue, float flOldValue) { + ConVarRef var(pConVar); + s_Mod.Toggle(var.GetBool()); + }); +} + + +#if 0 +// reverse engineered from ServerLinux 20151007a + +bool CTFKnife::IsBehindAndFacingTarget(CTFPlayer *pVictim) +{ + CTFPlayer *pSpy = ToTFPlayer(this->GetPlayerOwner()); + if (pSpy == nullptr) return false; + + Vector2D wsc_spy_to_victim = (pVictim->WorldSpaceCenter() - pSpy->WorldSpaceCenter()).AsVector2D(); + wsc_spy_to_victim.NormalizeInPlace(); + + Vector temp1; pSpy->EyeVectors(&temp1); + Vector2D eye_spy = temp1.AsVector2D(); + eye_spy.NormalizeInPlace(); + + Vector temp2; pVictim->EyeVectors(&temp2); + Vector2D eye_victim = temp2.AsVector2D(); + eye_victim.NormalizeInPlace(); + + if (DotProduct2D(wsc_spy_to_victim, eye_victim) <= 0.0f) return false; + if (DotProduct2D(wsc_spy_to_victim, eye_spy) <= 0.5f) return false; + if (DotProduct2D(eye_spy, eye_victim) <= -0.3f) return false; + + return true; +} +#endif diff --git a/src/mod/visualize/conds.cpp b/src/mod/visualize/conds.cpp new file mode 100644 index 00000000..7fe9b47e --- /dev/null +++ b/src/mod/visualize/conds.cpp @@ -0,0 +1,194 @@ +#include "mod.h" +#include "stub/tfplayer.h" +#include "stub/tf_shareddefs.h" +#include "util/iterate.h" + + +namespace Mod_Visualize_Conds +{ + void DrawPlayerConds() + { + ForEachTFPlayer([](CTFPlayer *player){ + if (player->GetTeamNumber() < FIRST_GAME_TEAM) return; + if (!player->IsAlive()) return; + + if (player->IsBot()) return; + + CBitVec<192> bits; + player->m_Shared->GetConditionsBits(bits); + + int line = 0; + + CFmtStr header("%-4s %-40s %8s %8s", "NUM", "CONDITION", "DURATION", "PROVIDER"); + NDebugOverlay::EntityText(ENTINDEX(player), line++, header.Get(), 0.032f, 0xff, 0xff, 0xff, 0xff); + + for (int bit = bits.FindNextSetBit(0); bit != -1; bit = bits.FindNextSetBit(bit)) { + auto cond = (ETFCond)bit; + + CFmtStr str_num ("#%d", bit); + CFmtStr str_duration("% .2f", player->m_Shared->GetConditionDuration(cond)); + CFmtStr str_provider("#%d", ENTINDEX(player->m_Shared->GetConditionProvider(cond))); + + CFmtStr str("%4s %-40s %8s %8s", str_num.Get(), GetTFConditionName(cond), str_duration.Get(), str_provider.Get()); + NDebugOverlay::EntityText(ENTINDEX(player), line++, str, 0.032f, 0xe0, 0xe0, 0xe0, 0xff); + } + }); + } + + + class CMod : public IMod, public IFrameUpdateListener + { + public: + CMod() : IMod("Visualize:Conds") {} + + virtual bool ShouldReceiveFrameEvents() const override { return this->IsEnabled(); } + + virtual void FrameUpdatePostEntityThink() override + { + static long frame = 0; + if (frame++ % 2 != 0) return; + + DrawPlayerConds(); + } + }; + CMod s_Mod; + + + ConVar cvar_enable("sig_visualize_conds", "0", FCVAR_NOTIFY, + "Visualization: draw information about active player conditions", + [](IConVar *pConVar, const char *pOldValue, float flOldValue) { + ConVarRef var(pConVar); + s_Mod.Toggle(var.GetBool()); + }); +} + +#if 0 + +ETFCond g_aDebuffConditions[] = { + TF_COND_BURNING, + TF_COND_URINE, + TF_COND_BLEEDING, + TF_COND_MAD_MILK, +}; + +g_MedigunEffects: medigun_charge_types => ETFCond(s) + 0 => TF_COND_INVULNERABLE, TF_COND_INVULNERABLE_WEARINGOFF + 1 => TF_COND_CRITBOOSTED + 2 => TF_COND_MEGAHEAL + 3 => TF_COND_MEDIGUN_UBER_BULLET_RESIST + 4 => TF_COND_MEDIGUN_UBER_BLAST_RESIST + 5 => TF_COND_MEDIGUN_UBER_FIRE_RESIST + +UnknownStructType g_MedigunResistConditions[] = { + { 0, TF_COND_MEDIGUN_SMALL_BULLET_RESIST, TF_COND_MEDIGUN_UBER_BULLET_RESIST }, + { 1, TF_COND_MEDIGUN_SMALL_BLAST_RESIST, TF_COND_MEDIGUN_UBER_BLAST_RESIST }, + { 2, TF_COND_MEDIGUN_SMALL_FIRE_RESIST, TF_COND_MEDIGUN_UBER_FIRE_RESIST }, +}; + +#endif + + +namespace Mod_Visualize_Conds_Client +{ + void DrawPlayerConds() + { + debugoverlay->ClearAllOverlays(); + + // offsets only guaranteed valid for ClientWin 20161102a + constexpr ptrdiff_t OFF_CTFPlayer_m_Shared = 0x17f0; + constexpr ptrdiff_t OFF_CTFPlayerShared_m_nPlayerCond = 0x00cc; + constexpr ptrdiff_t OFF_CTFPlayerShared_m_nPlayerCondEx = 0x00d0; + constexpr ptrdiff_t OFF_CTFPlayerShared_m_nPlayerCondEx2 = 0x00d4; + constexpr ptrdiff_t OFF_CTFPlayerShared_m_nPlayerCondEx3 = 0x00d8; + constexpr ptrdiff_t OFF_CBaseEntity_m_iHealth = 0x00a8; + + for (EntitySearchResult cl_ent = clienttools->FirstEntity(); cl_ent != nullptr; cl_ent = clienttools->NextEntity(cl_ent)) { + if (!clienttools->IsPlayer(cl_ent)) continue; + + auto m_nPlayerCond = reinterpret_cast((uintptr_t)cl_ent + OFF_CTFPlayer_m_Shared + OFF_CTFPlayerShared_m_nPlayerCond); + auto m_nPlayerCondEx = reinterpret_cast((uintptr_t)cl_ent + OFF_CTFPlayer_m_Shared + OFF_CTFPlayerShared_m_nPlayerCondEx); + auto m_nPlayerCondEx2 = reinterpret_cast((uintptr_t)cl_ent + OFF_CTFPlayer_m_Shared + OFF_CTFPlayerShared_m_nPlayerCondEx2); + auto m_nPlayerCondEx3 = reinterpret_cast((uintptr_t)cl_ent + OFF_CTFPlayer_m_Shared + OFF_CTFPlayerShared_m_nPlayerCondEx3); + + std::vector conds; + for (int i = 0; i < 32; ++i) { + if (((*m_nPlayerCond >> i) & 1) != 0) conds.push_back(i + 0); + } + for (int i = 0; i < 32; ++i) { + if (((*m_nPlayerCondEx >> i) & 1) != 0) conds.push_back(i + 32); + } + for (int i = 0; i < 32; ++i) { + if (((*m_nPlayerCondEx2 >> i) & 1) != 0) conds.push_back(i + 64); + } + for (int i = 0; i < 32; ++i) { + if (((*m_nPlayerCondEx3 >> i) & 1) != 0) conds.push_back(i + 96); + } + + char buf[0x1000]; + V_strcpy_safe(buf, "Conds:"); + for (int cond : conds) { + V_strcat_safe(buf, CFmtStr(" %d", cond)); + } + + NDebugOverlay::EntityText(clienttools->GetEntIndex(cl_ent), -2, buf, 3600.0f); + + auto m_iHealth = reinterpret_cast((uintptr_t)cl_ent + OFF_CBaseEntity_m_iHealth); + NDebugOverlay::EntityText(clienttools->GetEntIndex(cl_ent), -1, CFmtStr("Health: %d", *m_iHealth), 3600.0f); + } + } + + + class CMod : public IMod, public IFrameUpdateListener + { + public: + CMod() : IMod("Visualize:Conds_Client") {} + + virtual bool OnLoad() override + { + if (clienttools == nullptr) return false; + if (debugoverlay == nullptr) return false; + + return true; + } + + virtual bool ShouldReceiveFrameEvents() const override { return this->IsEnabled(); } + + virtual void FrameUpdatePostEntityThink() override + { + static long frame = 0; + if (frame++ % 2 != 0) return; + + DrawPlayerConds(); + } + }; + CMod s_Mod; + + + ConVar cvar_enable("sig_visualize_conds_client", "0", FCVAR_NOTIFY, + "Visualization: player conds (crappy client-side edition)", + [](IConVar *pConVar, const char *pOldValue, float flOldValue) { + ConVarRef var(pConVar); + s_Mod.Toggle(var.GetBool()); + }); +} + + + + + + + + CON_COMMAND(sig_visualize_conds_client, "") + { + if (clienttools == nullptr) { + Msg("Client tools interface is nullptr!\n"); + return; + } + + + + + + // m_Shared @ +0x17f0 + // m_nPlayerCond @ +0x00cc + } diff --git a/src/prop.cpp b/src/prop.cpp index 327ba86f..6527c85f 100644 --- a/src/prop.cpp +++ b/src/prop.cpp @@ -1,5 +1,10 @@ #include "prop.h" +#include "stub/baseentity.h" #include "util/misc.h" +#include "util/demangle.h" + +// TODO: move this to common.h +#include <../extensions/sdktools/util.h> void CC_ListProps(const CCommand& cmd) @@ -71,4 +76,103 @@ namespace Prop assert(FindOffset(off, obj, var)); return off; } + + +#if 0 + static std::unordered_map datamaps_by_classname; + static std::unordered_map classnames_by_rtti_name; + + static void PreloadDataMaps() + { + static bool init = false; + + if (!init) { + assert(datamaps_by_classname .empty()); + assert(classnames_by_rtti_name.empty()); + + auto dict = static_cast(servertools->GetEntityFactoryDictionary()); + assert(dict != nullptr); + + FOR_EACH_DICT_FAST(dict->m_Factories, i) { + const char *classname = dict->m_Factories.GetElementName(i); + assert(classname != nullptr); + assert(classname[0] != '\0'); + + IServerNetworkable *sv_networkable = dict->Create(classname); + assert(sv_networkable != nullptr); + + CBaseEntity *entity = sv_networkable->GetBaseEntity(); + assert(entity != nullptr); + + const datamap_t *p_datamap = entity->GetDataDescMap(); + assert(p_datamap != nullptr); + + const char *rtti_name = TypeName(entity); + assert(rtti_name != nullptr); + assert(rtti_name[0] != '\0'); + + std::string rtti_name_demangled; + assert(DemangleTypeName(rtti_name, rtti_name_demangled)); + rtti_name = rtti_name_demangled.c_str(); + + // servertools->RemoveEntityImmediate(entity); + + sm_datatable_info_t info; + if (gamehelpers->FindDataMapInfo(gamehelpers->GetDataMap(entity), "m_iEFlags", &info)) { + *(int *)((char *)entity + info.actual_offset) |= EFL_KILLME; + } + + ConColorMsg(Color(0x00, 0xff, 0xff, 0xff), "%-60s%s", + CFmtStr("classname '%s':", classname).Get(), + CFmtStr("rtti_name '%s'\n", rtti_name).Get()); + + auto result1 = datamaps_by_classname.insert(std::make_pair(classname, p_datamap)); + assert(result1.second); + + auto result2 = classnames_by_rtti_name.insert(std::make_pair(rtti_name, classname)); + // assert(result2.second); + } + + // sm_datatable_info_t info; + // for ( int i = dict->m_Factories.First(); i != dict->m_Factories.InvalidIndex(); i = dict->m_Factories.Next( i ) ) + // { + // IServerNetworkable *entity = dict->Create(dict->m_Factories.GetElementName(i)); + // ServerClass *sclass = entity->GetServerClass(); + // fprintf(fp,"%s - %s\n",sclass->GetName(), dict->m_Factories.GetElementName(i)); + // + // if (!gamehelpers->FindDataMapInfo(gamehelpers->GetDataMap(entity->GetBaseEntity()), "m_iEFlags", &info)) + // continue; + // + // int *eflags = (int *)((char *)entity->GetBaseEntity() + info.actual_offset); + // *eflags |= (1<<0); // EFL_KILLME + // } + + init = true; + } + } + + const datamap_t *GetDataMapByClassname(const char *classname) + { + PreloadDataMaps(); + + auto it = datamaps_by_classname.find(classname); + if (it != datamaps_by_classname.end()) { + return (*it).second; + } else { + return nullptr; + } + } + + const datamap_t *GetDataMapByRTTIName(const char *rtti_name) + { + PreloadDataMaps(); + + auto it = classnames_by_rtti_name.find(rtti_name); + if (it != classnames_by_rtti_name.end()) { + return GetDataMapByClassname((*it).second.c_str()); + } else { + return nullptr; + } + } +#endif } diff --git a/src/prop.h b/src/prop.h index 5147ef7a..6e7564ed 100644 --- a/src/prop.h +++ b/src/prop.h @@ -3,6 +3,23 @@ #include "mem/extract.h" +#include "util/rtti.h" + +// TOOD: move to common.h +#include +#include + + +/* from src/public/dt_utlvector_send.cpp */ +struct CSendPropExtra_UtlVector +{ + SendTableProxyFn m_DataTableProxyFn; // If it's a datatable, then this is the proxy they specified. + SendVarProxyFn m_ProxyFn; // If it's a non-datatable, then this is the proxy they specified. + EnsureCapacityFn m_EnsureCapacityFn; + int m_ElementStride; // Distance between each element in the array. + int m_Offset; // # bytes from the parent structure to its utlvector. + int m_nMaxElements; // For debugging... +}; namespace Prop @@ -11,6 +28,14 @@ namespace Prop bool FindOffset(int& off, const char *obj, const char *var); int FindOffsetAssert(const char *obj, const char *var); + +// const datamap_t *GetDataMapByClassname(const char *classname); +// const datamap_t *GetDataMapByRTTIName(const char *rtti_name); +// +// template datamap_t *GetDataMapByRTTI() +// { +// return GetDataMapByRTTIName(TypeName()); +// } } @@ -124,16 +149,77 @@ class CProp_SendProp : public IPropTyped private: virtual bool CalcOffset(int& off) const override { - sm_sendprop_info_t info; - if (!gamehelpers->FindSendPropInfo(this->m_pszServerClass, this->GetSendPropMemberName(), &info)) { - Warning("CProp_SendProp: %s::%s FAIL: in FindSendPropInfo\n", this->GetObjectName(), this->GetMemberName()); + ServerClass *sv_class = this->FindServerClass(); + if (sv_class == nullptr) { + Warning("CProp_SendProp: %s::%s FAIL: can't find ServerClass \"%s\"\n", this->GetObjectName(), this->GetMemberName(), this->m_pszServerClass); + return false; + } + + if (!this->FindSendProp(off, sv_class->m_pTable)) { + Warning("CProp_SendProp: %s::%s FAIL: can't find SendProp \"%s\"\n", this->GetObjectName(), this->GetMemberName(), this->GetSendPropMemberName()); return false; } - off = info.prop->GetOffset(); return true; } + ServerClass *FindServerClass() const + { + for (ServerClass *sv_class = gamedll->GetAllServerClasses(); sv_class != nullptr; sv_class = sv_class->m_pNext) { + if (strcmp(sv_class->GetName(), this->m_pszServerClass) == 0) { + return sv_class; + } + } + + return nullptr; + } + + bool FindSendProp(int& off, SendTable *s_table) const + { + for (int i = 0; i < s_table->GetNumProps(); ++i) { + SendProp *s_prop = s_table->GetProp(i); + + if (s_prop->GetName() != nullptr && strcmp(s_prop->GetName(), this->GetSendPropMemberName()) == 0) { + if (!this->IsSendPropUtlVector(off, s_prop)) { + off = s_prop->GetOffset(); + } + return true; + } + + if (s_prop->GetDataTable() != nullptr) { + if (this->FindSendProp(off, s_prop->GetDataTable())) { + return true; + } + } + } + + return false; + } + + bool IsSendPropUtlVector(int& off, SendProp *q_prop) const + { + SendTable *s_table = q_prop->GetDataTable(); + if (s_table == nullptr) return false; + + auto SendProxy_LengthTable = reinterpret_cast(AddrManager::GetAddr("SendProxy_LengthTable")); + assert(SendProxy_LengthTable != nullptr); + + for (int i = 0; i < s_table->GetNumProps(); ++i) { + SendProp *s_prop = s_table->GetProp(i); + + if (s_prop->GetName() != nullptr && strcmp(s_prop->GetName(), "lengthproxy") == 0 && + s_prop->GetDataTable() != nullptr && s_prop->GetDataTableProxyFn() == SendProxy_LengthTable) { + auto extra = reinterpret_cast(s_prop->GetExtraData()); + if (extra != nullptr) { + off = extra->m_Offset; + return true; + } + } + } + + return false; + } + const char *GetSendPropMemberName() const { if (this->m_pszRemoteName != nullptr) { @@ -164,24 +250,39 @@ class CProp_DataMap : public IPropTyped virtual bool CalcOffset(int& off) const override { char str_DataMap[1024]; - snprintf(str_DataMap, sizeof(str_DataMap), "%s::m_DataMap", this->GetObjectName()); + V_sprintf_safe(str_DataMap, "%s::m_DataMap", this->GetObjectName()); - datamap_t *map = (datamap_t *)AddrManager::GetAddr(str_DataMap); - if (map == nullptr) { + auto datamap = (datamap_t *)AddrManager::GetAddr(str_DataMap); + if (datamap == nullptr) { Warning("CProp_DataMap: %s::%s FAIL: no addr for %s\n", this->GetObjectName(), this->GetMemberName(), str_DataMap); return false; } sm_datatable_info_t info; - if (!gamehelpers->FindDataMapInfo(map, this->GetMemberName(), &info)) { + if (!gamehelpers->FindDataMapInfo(datamap, this->GetMemberName(), &info)) { Warning("CProp_DataMap: %s::%s FAIL: in FindDataMapInfo\n", this->GetObjectName(), this->GetMemberName()); return false; } +// const datamap_t *datamap = Prop::GetDataMapByRTTIName(this->GetObjectName()); +// if (datamap == nullptr) { +// Warning("CProp_DataMap: %s::%s FAIL: can't find datamap for class %s\n", this->GetObjectName(), this->GetMemberName(), this->GetObjectName()); +// return false; +// } +// +// sm_datatable_info_t info; +// if (!gamehelpers->FindDataMapInfo(const_cast(datamap), this->GetMemberName(), &info)) { +// Warning("CProp_DataMap: %s::%s FAIL: in FindDataMapInfo\n", this->GetObjectName(), this->GetMemberName()); +// return false; +// } + off = GetTypeDescOffs(info.prop); return true; } }; +//#ifdef __GNUC__ +//#warning TODO: delete gamedata/sigsegv/datamaps.txt and remove from PackageScript and gameconf.cpp +//#endif template @@ -368,7 +469,7 @@ template class CPropAccessorBase /* indexing */ #ifdef __GNUC__ - #warning TODO: ensure that operator[] returns reference types and that their constness is correct + //#warning TODO: ensure that operator[] returns reference types and that their constness is correct #endif template auto operator[](const A& idx) const { return this->Get()[idx]; } @@ -382,7 +483,7 @@ template class CPropAccessorBase /* begin() and end() passthru */ #ifdef __GNUC__ - #warning TODO: ensure that begin() and end() return the right things based on constness + //#warning TODO: ensure that begin() and end() return the right things based on constness #endif auto begin() { return this->Get().begin(); } auto end() { return this->Get().end(); } diff --git a/src/sm/MemoryUtils.cpp b/src/sm/MemoryUtils.cpp index d64c430d..b9a35543 100644 --- a/src/sm/MemoryUtils.cpp +++ b/src/sm/MemoryUtils.cpp @@ -569,7 +569,7 @@ bool MemoryUtils::GetLibraryInfo(const void *libPtr, DynLibInfo &lib) return true; } -void MemoryUtils::ForEachSymbol(void *handle, const std::function& functor) +void MemoryUtils::ForEachSymbol(void *handle, const std::function& functor) { /* do a bogus symbol lookup to force everything into the symbol table cache */ assert(ResolveSymbol(handle, "________________") == nullptr); diff --git a/src/sm/MemoryUtils.h b/src/sm/MemoryUtils.h index e2724851..56350199 100644 --- a/src/sm/MemoryUtils.h +++ b/src/sm/MemoryUtils.h @@ -65,7 +65,7 @@ class MemoryUtils ~MemoryUtils(); void *ResolveSymbol(void *handle, const char *symbol); bool GetLibraryInfo(const void *libPtr, DynLibInfo &lib); - void ForEachSymbol(void *handle, const std::function& functor); + void ForEachSymbol(void *handle, const std::function& functor); #if defined PLATFORM_LINUX void ForEachSection(void *handle, const std::function& functor); #elif defined PLATFORM_WINDOWS diff --git a/src/sm/sm_symtable.h b/src/sm/sm_symtable.h index 8a778713..001a41e0 100644 --- a/src/sm/sm_symtable.h +++ b/src/sm/sm_symtable.h @@ -222,14 +222,12 @@ class SymbolTable return kvs; } - void ForEachSymbol(const std::function& functor) + template + void ForEachSymbol(FUNCTOR&& functor) { for (uint32_t i = 0; i < nbuckets; ++i) { - Symbol *sym = buckets[i]; - - while (sym != nullptr) { - functor(sym); - sym = sym->tbl_next; + for (Symbol *sym = buckets[i]; sym != nullptr; sym = sym->tbl_next) { + if (!functor(sym)) return; } } } diff --git a/src/stub/baseanimating.cpp b/src/stub/baseanimating.cpp index 3660f8a5..346cf75f 100644 --- a/src/stub/baseanimating.cpp +++ b/src/stub/baseanimating.cpp @@ -1,6 +1,7 @@ #include "stub/baseanimating.h" +IMPL_SENDPROP(int, CBaseAnimating, m_nSkin, CBaseAnimating); IMPL_SENDPROP(float, CBaseAnimating, m_flModelScale, CBaseAnimating); MemberFuncThunk CBaseAnimating::ft_SetModelScale ("CBaseAnimating::SetModelScale"); diff --git a/src/stub/baseanimating.h b/src/stub/baseanimating.h index bdfeba59..470bac20 100644 --- a/src/stub/baseanimating.h +++ b/src/stub/baseanimating.h @@ -13,6 +13,8 @@ class CBaseAnimating : public CBaseEntity void SetModelScale(float scale, float change_duration = 0.0f) { ft_SetModelScale (this, scale, change_duration); } void DrawServerHitboxes(float duration = 0.0f, bool monocolor = false) { ft_DrawServerHitboxes(this, duration, monocolor); } + DECL_SENDPROP(int, m_nSkin); + private: DECL_SENDPROP(float, m_flModelScale); diff --git a/src/stub/baseentity.cpp b/src/stub/baseentity.cpp index 9af3406d..190839b1 100644 --- a/src/stub/baseentity.cpp +++ b/src/stub/baseentity.cpp @@ -14,18 +14,22 @@ IMPL_DATAMAP(Vector, CBaseEntity, m_vecAbsVelocity); IMPL_DATAMAP(IPhysicsObject*, CBaseEntity, m_pPhysicsObject); IMPL_DATAMAP(matrix3x4_t, CBaseEntity, m_rgflCoordinateFrame); IMPL_DATAMAP(int, CBaseEntity, m_nNextThinkTick); +IMPL_DATAMAP(CHandle, CBaseEntity, m_hMoveChild); +IMPL_DATAMAP(CHandle, CBaseEntity, m_hMovePeer); +IMPL_DATAMAP(CHandle, CBaseEntity, m_hMoveParent); -IMPL_SENDPROP(CCollisionProperty, CBaseEntity, m_Collision, CBaseEntity); -IMPL_SENDPROP(int, CBaseEntity, m_iTeamNum, CBaseEntity); -IMPL_SENDPROP(int, CBaseEntity, m_iMaxHealth, CBaseObject); -IMPL_SENDPROP(int, CBaseEntity, m_iHealth, CBasePlayer); -IMPL_SENDPROP(char, CBaseEntity, m_lifeState, CBasePlayer); -IMPL_SENDPROP(CHandle, CBaseEntity, m_hGroundEntity, CBasePlayer); -IMPL_SENDPROP(CHandle, CBaseEntity, m_hOwnerEntity, CBaseEntity); -IMPL_SENDPROP(int, CBaseEntity, m_fFlags, CBasePlayer); -IMPL_SENDPROP(int, CBaseEntity, m_CollisionGroup, CBaseEntity); -IMPL_SENDPROP(unsigned char, CBaseEntity, m_nRenderMode, CBaseEntity); -IMPL_SENDPROP(unsigned char, CBaseEntity, m_MoveType, CBaseEntity, "movetype"); +IMPL_SENDPROP(CCollisionProperty, CBaseEntity, m_Collision, CBaseEntity); +IMPL_SENDPROP(int, CBaseEntity, m_iTeamNum, CBaseEntity); +IMPL_SENDPROP(int, CBaseEntity, m_iMaxHealth, CBaseObject); +IMPL_SENDPROP(int, CBaseEntity, m_iHealth, CBasePlayer); +IMPL_SENDPROP(char, CBaseEntity, m_lifeState, CBasePlayer); +IMPL_SENDPROP(CHandle, CBaseEntity, m_hGroundEntity, CBasePlayer); +IMPL_SENDPROP(CHandle, CBaseEntity, m_hOwnerEntity, CBaseEntity); +IMPL_SENDPROP(int, CBaseEntity, m_fFlags, CBasePlayer); +IMPL_SENDPROP(int, CBaseEntity, m_CollisionGroup, CBaseEntity); +IMPL_SENDPROP(unsigned char, CBaseEntity, m_nRenderMode, CBaseEntity); +IMPL_SENDPROP(unsigned char, CBaseEntity, m_MoveType, CBaseEntity, "movetype"); +IMPL_SENDPROP(int[4], CBaseEntity, m_nModelIndexOverrides, CBaseEntity); MemberFuncThunk< CBaseEntity *, void > CBaseEntity::ft_Remove ("CBaseEntity::Remove"); MemberFuncThunk< CBaseEntity *, void > CBaseEntity::ft_CalcAbsolutePosition("CBaseEntity::CalcAbsolutePosition"); @@ -44,10 +48,15 @@ MemberVFuncThunk< CBaseEntity *, void > CBaseEnt MemberVFuncThunk< CBaseEntity *, void, Vector *, AngularImpulse *> CBaseEntity::vt_GetVelocity (TypeName(), "CBaseEntity::GetVelocity"); MemberVFuncThunk CBaseEntity::vt_WorldSpaceCenter (TypeName(), "CBaseEntity::WorldSpaceCenter"); MemberVFuncThunk CBaseEntity::vt_IsCombatItem (TypeName(), "CBaseEntity::IsCombatItem"); +MemberVFuncThunk< CBaseEntity *, void, int > CBaseEntity::vt_SetModelIndex (TypeName(), "CBaseEntity::SetModelIndex"); MemberVFuncThunk CBaseEntity::vt_GetModelIndex (TypeName(), "CBaseEntity::GetModelIndex"); +MemberVFuncThunk CBaseEntity::vt_GetModelName (TypeName(), "CBaseEntity::GetModelName"); MemberVFuncThunk< CBaseEntity *, CBaseCombatCharacter * > CBaseEntity::vt_MyCombatCharacterPointer (TypeName(), "CBaseEntity::MyCombatCharacterPointer"); MemberVFuncThunk CBaseEntity::vt_ShouldCollide (TypeName(), "CBaseEntity::ShouldCollide"); MemberVFuncThunk< CBaseEntity *, void > CBaseEntity::vt_DrawDebugGeometryOverlays(TypeName(), "CBaseEntity::DrawDebugGeometryOverlays"); +MemberVFuncThunk< CBaseEntity *, void, int > CBaseEntity::vt_ChangeTeam (TypeName(), "CBaseEntity::ChangeTeam"); +MemberVFuncThunk< CBaseEntity *, void, int, int > CBaseEntity::vt_SetModelIndexOverride (TypeName(), "CBaseEntity::SetModelIndexOverride"); +MemberVFuncThunk< CBaseEntity *, datamap_t * > CBaseEntity::vt_GetDataDescMap (TypeName(), "CBaseEntity::GetDataDescMap"); bool CBaseEntity::IsPlayer() const diff --git a/src/stub/baseentity.h b/src/stub/baseentity.h index 37a17c22..e3810009 100644 --- a/src/stub/baseentity.h +++ b/src/stub/baseentity.h @@ -16,9 +16,8 @@ class CBaseEntity : public IServerEntity { public: /* inline */ - edict_t *edict() { return this->NetworkProp()->GetEdict(); } edict_t *edict() const { return this->NetworkProp()->GetEdict(); } - int entindex(); + int entindex() const; const Vector& GetAbsOrigin() const; const QAngle& GetAbsAngles() const; // const Vector& GetAbsVelocity() const; @@ -46,6 +45,9 @@ class CBaseEntity : public IServerEntity model_t *GetModel() const { return const_cast(modelinfo->GetModel(this->GetModelIndex())); } bool IsTransparent() const { return (this->m_nRenderMode != kRenderNormal); } MoveType_t GetMoveType() const { return (MoveType_t)(unsigned char)this->m_MoveType; } + CBaseEntity *GetMoveParent() { return this->m_hMoveParent; } + CBaseEntity *FirstMoveChild() { return this->m_hMoveChild; } + CBaseEntity *NextMovePeer() { return this->m_hMovePeer; } /* thunk */ void Remove() { ft_Remove (this); } @@ -63,10 +65,15 @@ class CBaseEntity : public IServerEntity void GetVelocity(Vector *vVelocity, AngularImpulse *vAngVelocity = nullptr) { vt_GetVelocity (this, vVelocity, vAngVelocity); } const Vector& WorldSpaceCenter() const { return vt_WorldSpaceCenter (this); } bool IsCombatItem() const { return vt_IsCombatItem (this); } + void SetModelIndex(int index) { vt_SetModelIndex (this, index); } int GetModelIndex() const { return vt_GetModelIndex (this); } + string_t GetModelName() const { return vt_GetModelName (this); } CBaseCombatCharacter *MyCombatCharacterPointer() { return vt_MyCombatCharacterPointer (this); } bool ShouldCollide(int collisionGroup, int contentsMask) const { return vt_ShouldCollide (this, collisionGroup, contentsMask); } void DrawDebugGeometryOverlays() { vt_DrawDebugGeometryOverlays(this); } + void ChangeTeam(int iTeamNum) { vt_ChangeTeam (this, iTeamNum); } + void SetModelIndexOverride(int index, int nValue) { vt_SetModelIndexOverride (this, index, nValue); } + datamap_t *GetDataDescMap() { return vt_GetDataDescMap (this); } /* hack */ bool IsCombatCharacter() { return (this->MyCombatCharacterPointer() != nullptr); } @@ -83,6 +90,7 @@ class CBaseEntity : public IServerEntity DECL_SENDPROP(int, m_fFlags); DECL_DATAMAP(int, m_nNextThinkTick); DECL_SENDPROP(char, m_lifeState); + DECL_SENDPROP(int[4], m_nModelIndexOverrides); private: DECL_DATAMAP(CServerNetworkProperty, m_Network); @@ -94,6 +102,9 @@ class CBaseEntity : public IServerEntity DECL_DATAMAP(Vector, m_vecAbsVelocity); DECL_DATAMAP(IPhysicsObject *, m_pPhysicsObject); DECL_DATAMAP(matrix3x4_t, m_rgflCoordinateFrame); + DECL_DATAMAP(CHandle, m_hMoveChild); + DECL_DATAMAP(CHandle, m_hMovePeer); + DECL_DATAMAP(CHandle, m_hMoveParent); DECL_SENDPROP_RW(CCollisionProperty, m_Collision); DECL_SENDPROP (int, m_iTeamNum); @@ -122,10 +133,15 @@ class CBaseEntity : public IServerEntity static MemberVFuncThunk< CBaseEntity *, void, Vector *, AngularImpulse *> vt_GetVelocity; static MemberVFuncThunk vt_WorldSpaceCenter; static MemberVFuncThunk vt_IsCombatItem; + static MemberVFuncThunk< CBaseEntity *, void, int> vt_SetModelIndex; static MemberVFuncThunk vt_GetModelIndex; + static MemberVFuncThunk vt_GetModelName; static MemberVFuncThunk< CBaseEntity *, CBaseCombatCharacter *> vt_MyCombatCharacterPointer; static MemberVFuncThunk vt_ShouldCollide; static MemberVFuncThunk< CBaseEntity *, void> vt_DrawDebugGeometryOverlays; + static MemberVFuncThunk< CBaseEntity *, void, int> vt_ChangeTeam; + static MemberVFuncThunk< CBaseEntity *, void, int, int> vt_SetModelIndexOverride; + static MemberVFuncThunk< CBaseEntity *, datamap_t *> vt_GetDataDescMap; }; inline CBaseEntity *GetContainingEntity(edict_t *pent) @@ -137,12 +153,12 @@ inline CBaseEntity *GetContainingEntity(edict_t *pent) return nullptr; } -inline int ENTINDEX(edict_t *pEdict) +inline int ENTINDEX(const edict_t *pEdict) { - return gamehelpers->IndexOfEdict(pEdict); + return gamehelpers->IndexOfEdict(const_cast(pEdict)); } -inline int ENTINDEX(CBaseEntity *pEnt) +inline int ENTINDEX(const CBaseEntity *pEnt) { if (pEnt == nullptr) { return 0; @@ -171,7 +187,7 @@ inline CBaseEntity *UTIL_EntityByIndex(int entityIndex) } -inline int CBaseEntity::entindex() +inline int CBaseEntity::entindex() const { return ENTINDEX(this->GetNetworkable()->GetEdict()); } diff --git a/src/stub/baseplayer.cpp b/src/stub/baseplayer.cpp index c2dc755d..a222cad1 100644 --- a/src/stub/baseplayer.cpp +++ b/src/stub/baseplayer.cpp @@ -2,7 +2,8 @@ #include "stub/tfplayer.h" -IMPL_SENDPROP(CHandle, CBaseCombatCharacter, m_hActiveWeapon, CBaseCombatCharacter); +IMPL_SENDPROP(CHandle, CBaseCombatCharacter, m_hActiveWeapon, CBaseCombatCharacter); +IMPL_SENDPROP(CHandle[MAX_WEAPONS], CBaseCombatCharacter, m_hMyWeapons, CBaseCombatCharacter); MemberFuncThunk CBaseCombatCharacter::ft_AddGlowEffect ("CBaseCombatCharacter::AddGlowEffect"); MemberFuncThunk CBaseCombatCharacter::ft_RemoveGlowEffect ("CBaseCombatCharacter::RemoveGlowEffect"); @@ -32,9 +33,10 @@ IMPL_DATAMAP(bool, CBasePlayer, m_bDuckToggled); IMPL_DATAMAP(unsigned int, CBasePlayer, m_afPhysicsFlags); IMPL_DATAMAP(int, CBasePlayer, m_vphysicsCollisionState); -IMPL_SENDPROP(CPlayerLocalData, CBasePlayer, m_Local, CBasePlayer); -IMPL_SENDPROP(int, CBasePlayer, m_nTickBase, CBasePlayer); -IMPL_SENDPROP(float, CBasePlayer, m_flMaxspeed, CBasePlayer); +IMPL_SENDPROP(CPlayerLocalData, CBasePlayer, m_Local, CBasePlayer); +IMPL_SENDPROP(int, CBasePlayer, m_nTickBase, CBasePlayer); +IMPL_SENDPROP(float, CBasePlayer, m_flMaxspeed, CBasePlayer); +IMPL_SENDPROP(CUtlVector>, CBasePlayer, m_hMyWearables, CBasePlayer); MemberFuncThunk CBasePlayer::ft_EyeVectors ("CBasePlayer::EyeVectors"); MemberFuncThunk CBasePlayer::ft_GetSteamID ("CBasePlayer::GetSteamID"); diff --git a/src/stub/baseplayer.h b/src/stub/baseplayer.h index 9aeb8219..a689bf4b 100644 --- a/src/stub/baseplayer.h +++ b/src/stub/baseplayer.h @@ -7,6 +7,7 @@ class CNavArea; class CBaseCombatWeapon; +class CEconWearable; class CBaseCombatCharacter : public CBaseFlex @@ -15,6 +16,9 @@ class CBaseCombatCharacter : public CBaseFlex enum FieldOfViewCheckType { USE_FOV, DISREGARD_FOV }; CBaseCombatWeapon *GetActiveWeapon() const { return this->m_hActiveWeapon; } + int WeaponCount() const { return MAX_WEAPONS; } + CBaseCombatWeapon *GetWeapon(int i) const { return this->m_hMyWeapons[i]; } + static_assert(MAX_WEAPONS == 48, ""); void AddGlowEffect() { ft_AddGlowEffect (this); } void RemoveGlowEffect() { ft_RemoveGlowEffect (this); } @@ -32,7 +36,8 @@ class CBaseCombatCharacter : public CBaseFlex bool ShouldGib(const CTakeDamageInfo& info) { return vt_ShouldGib (this, info); } private: - DECL_SENDPROP(CHandle, m_hActiveWeapon); + DECL_SENDPROP(CHandle, m_hActiveWeapon); + DECL_SENDPROP(CHandle[MAX_WEAPONS], m_hMyWeapons); static MemberFuncThunk ft_AddGlowEffect; static MemberFuncThunk ft_RemoveGlowEffect; @@ -65,9 +70,11 @@ class CPlayerLocalData class CBasePlayer : public CBaseCombatCharacter { public: - const char *GetPlayerName() { return this->m_szNetname; } - float MaxSpeed() const { return this->m_flMaxspeed; } - int GetUserID() { return engine->GetPlayerUserId(this->edict()); } + const char *GetPlayerName() { return this->m_szNetname; } + float MaxSpeed() const { return this->m_flMaxspeed; } + int GetUserID() { return engine->GetPlayerUserId(this->edict()); } + int GetNumWearables() const { return this->m_hMyWearables->Count(); } + CEconWearable *GetWearable(int i) const { return this->m_hMyWearables[i]; } /* easy-but-slow calls via IPlayerInfo */ const char *GetNetworkIDString() const { return this->GetPlayerInfo()->GetNetworkIDString(); } @@ -98,7 +105,8 @@ class CBasePlayer : public CBaseCombatCharacter private: IPlayerInfo *GetPlayerInfo() const { return playerinfomanager->GetPlayerInfo(this->edict()); } - DECL_SENDPROP(float, m_flMaxspeed); + DECL_SENDPROP(float, m_flMaxspeed); + DECL_SENDPROP(CUtlVector>, m_hMyWearables); DECL_DATAMAP(char[32], m_szNetname); DECL_DATAMAP(bool, m_bDuckToggled); diff --git a/src/stub/entities.cpp b/src/stub/entities.cpp index 32ecff39..ecb4b6b3 100644 --- a/src/stub/entities.cpp +++ b/src/stub/entities.cpp @@ -41,10 +41,6 @@ struct CExtract_CCurrencyPack_m_nAmount : public IExtract #if defined _LINUX -/* this is really an ugly ugly thing, but I don't have the patience to do it - * in a more reliable way at the moment; plus I doubt the TF team has touched - * CTFTankBoss a single time since Two Cities, so we're safe anyway */ - static constexpr uint8_t s_Buf_CTFTankBoss_m_pBodyInterface[] = { 0x55, // +0000 push ebp 0x89, 0xe5, // +0001 mov ebp,esp @@ -54,10 +50,9 @@ static constexpr uint8_t s_Buf_CTFTankBoss_m_pBodyInterface[] = { 0xc3, // +000D ret }; -template -struct IExtract_CTFTankBoss_m_pBodyInterface : public IExtract +struct CExtract_CTFTankBoss_m_pBodyInterface : public IExtract { - IExtract_CTFTankBoss_m_pBodyInterface() : IExtract(sizeof(s_Buf_CTFTankBoss_m_pBodyInterface)) {} + CExtract_CTFTankBoss_m_pBodyInterface() : IExtract(sizeof(s_Buf_CTFTankBoss_m_pBodyInterface)) {} virtual bool GetExtractInfo(ByteBuf& buf, ByteBuf& mask) const override { @@ -72,21 +67,8 @@ struct IExtract_CTFTankBoss_m_pBodyInterface : public IExtract virtual uint32_t GetFuncOffMin() const override { return 0x0000; } virtual uint32_t GetFuncOffMax() const override { return 0x0000; } virtual uint32_t GetExtractOffset() const override { return 0x0007 + 2; } - virtual T AdjustValue(T val) const override { return reinterpret_cast((uintptr_t)val + ADJ); } }; -struct CExtract_CTFTankBoss_m_hCurrentNode : - public IExtract_CTFTankBoss_m_pBodyInterface *, 0x000c> {}; - -struct CExtract_CTFTankBoss_m_NodeDists : - public IExtract_CTFTankBoss_m_pBodyInterface *, 0x0010> {}; - -struct CExtract_CTFTankBoss_m_flTotalDistance : - public IExtract_CTFTankBoss_m_pBodyInterface {}; - -struct CExtract_CTFTankBoss_m_iCurrentNode : - public IExtract_CTFTankBoss_m_pBodyInterface {}; - #elif defined _WINDOWS // TODO @@ -168,6 +150,18 @@ MemberFuncThunk CUpgrades::ft_GetUpgradeAt GlobalThunk> g_hUpgradeEntity("g_hUpgradeEntity"); +IMPL_DATAMAP(int, CFuncNavPrerequisite, m_task); +IMPL_DATAMAP(string_t, CFuncNavPrerequisite, m_taskEntityName); +IMPL_DATAMAP(float, CFuncNavPrerequisite, m_taskValue); +IMPL_DATAMAP(bool, CFuncNavPrerequisite, m_isDisabled); + + +IMPL_REL_BEFORE(CUtlStringList, CFilterTFBotHasTag, m_TagList, m_iszTags); +IMPL_DATAMAP (string_t, CFilterTFBotHasTag, m_iszTags); +IMPL_DATAMAP (bool, CFilterTFBotHasTag, m_bRequireAllTags); +IMPL_DATAMAP (bool, CFilterTFBotHasTag, m_bNegated); + + IMPL_SENDPROP(bool, CCurrencyPack, m_bDistributed, CCurrencyPack); IMPL_EXTRACT (int, CCurrencyPack, m_nAmount, new CExtract_CCurrencyPack_m_nAmount()); @@ -178,10 +172,12 @@ GlobalThunk> ICurrencyPackAutoList::m_ICurre MemberVFuncThunk CTFBaseBoss::vt_UpdateCollisionBounds(TypeName(), "CTFBaseBoss::UpdateCollisionBounds"); -IMPL_EXTRACT(CHandle, CTFTankBoss, m_hCurrentNode, new CExtract_CTFTankBoss_m_hCurrentNode()); -IMPL_EXTRACT(CUtlVector, CTFTankBoss, m_NodeDists, new CExtract_CTFTankBoss_m_NodeDists()); -IMPL_EXTRACT(float, CTFTankBoss, m_flTotalDistance, new CExtract_CTFTankBoss_m_flTotalDistance()); -IMPL_EXTRACT(int, CTFTankBoss, m_iCurrentNode, new CExtract_CTFTankBoss_m_iCurrentNode()); +IMPL_EXTRACT (IBody *, CTFTankBoss, m_pBodyInterface, new CExtract_CTFTankBoss_m_pBodyInterface()); +IMPL_RELATIVE(CHandle, CTFTankBoss, m_hCurrentNode, m_pBodyInterface, 0x0c); +IMPL_RELATIVE(CUtlVector, CTFTankBoss, m_NodeDists, m_pBodyInterface, 0x10); +IMPL_RELATIVE(float, CTFTankBoss, m_flTotalDistance, m_pBodyInterface, 0x24); +IMPL_RELATIVE(int, CTFTankBoss, m_iCurrentNode, m_pBodyInterface, 0x28); +IMPL_RELATIVE(int, CTFTankBoss, m_iModelIndex, m_pBodyInterface, 0x44); GlobalThunk> ICaptureZoneAutoList::m_ICaptureZoneAutoListAutoList("ICaptureZoneAutoList::m_ICaptureZoneAutoListAutoList"); @@ -193,3 +189,7 @@ GlobalThunk>> g_hControlPointMasters GlobalThunk> ITFFlameEntityAutoList::m_ITFFlameEntityAutoListAutoList("ITFFlameEntityAutoList::m_ITFFlameEntityAutoListAutoList"); + + +GlobalThunk s_TankModel ("s_TankModel"); +GlobalThunk s_TankModelRome("s_TankModelRome"); diff --git a/src/stub/entities.h b/src/stub/entities.h index 151d2f7a..cdcba672 100644 --- a/src/stub/entities.h +++ b/src/stub/entities.h @@ -124,13 +124,16 @@ class CTFBaseBoss : public NextBotCombatCharacter static MemberVFuncThunk vt_UpdateCollisionBounds; }; +class IBody; class CTFTankBoss : public CTFBaseBoss { public: - DECL_EXTRACT(CHandle, m_hCurrentNode); - DECL_EXTRACT(CUtlVector, m_NodeDists); - DECL_EXTRACT(float, m_flTotalDistance); - DECL_EXTRACT(int, m_iCurrentNode); + DECL_EXTRACT (IBody *, m_pBodyInterface); + DECL_RELATIVE(CHandle, m_hCurrentNode); + DECL_RELATIVE(CUtlVector, m_NodeDists); + DECL_RELATIVE(float, m_flTotalDistance); + DECL_RELATIVE(int, m_iCurrentNode); + DECL_RELATIVE(int, m_iModelIndex); }; @@ -152,6 +155,48 @@ class CUpgrades : public CBaseTrigger }; extern GlobalThunk> g_hUpgradeEntity; +class CFuncNavPrerequisite : public CBaseTrigger +{ +public: + enum TaskType + { + DESTROY_ENTITY = 1, + MOVE_TO = 2, + WAIT = 3, + }; + + DECL_DATAMAP(int, m_task); + DECL_DATAMAP(string_t, m_taskEntityName); + DECL_DATAMAP(float, m_taskValue); + DECL_DATAMAP(bool, m_isDisabled); + // CHandle @ m_isDisabled+4 +}; + + +class CServerOnlyEntity : public CBaseEntity {}; +class CLogicalEntity : public CServerOnlyEntity {}; + +class CBaseFilter : public CLogicalEntity +{ +public: + // TODO +}; + +class CFilterMultiple : public CBaseFilter +{ +public: + // TODO +}; + +class CFilterTFBotHasTag : public CBaseFilter +{ +public: + DECL_RELATIVE(CUtlStringList, m_TagList); + DECL_DATAMAP (string_t, m_iszTags); + DECL_DATAMAP (bool, m_bRequireAllTags); + DECL_DATAMAP (bool, m_bNegated); +}; + class CCurrencyPack : public CTFPowerup { @@ -213,6 +258,10 @@ class ITFFlameEntityAutoList }; +extern GlobalThunk s_TankModel; +extern GlobalThunk s_TankModelRome; + + // 20151007a // CTFPlayer::Event_Killed diff --git a/src/stub/tfbot.h b/src/stub/tfbot.h index b503d2cd..2838fb8c 100644 --- a/src/stub/tfbot.h +++ b/src/stub/tfbot.h @@ -16,6 +16,18 @@ class IVision; class IIntention; +union attribute_data_union_t +{ + void *ptr; +}; + +struct static_attrib_t +{ + unsigned short m_iAttrIndex; // +0x00 + attribute_data_union_t m_Value; // +0x04 +}; + + template class NextBotPlayer : public T { @@ -143,6 +155,31 @@ class CTFBot : public NextBotPlayer float m_flWhen; }; + enum DifficultyType : int; + enum WeaponRestriction : int; + + struct EventChangeAttributes_t + { + struct item_attributes_t + { + CUtlString m_strItemName; // +0x00 + CCopyableUtlVector m_Attrs; // +0x04 + }; + + CUtlString m_strName; // +0x00 + DifficultyType m_iSkill; // +0x04 + WeaponRestriction m_nWeaponRestrict; // +0x08 + uint32_t pad_0c; // TODO: 0x0c m_nMission, not parsed, defaults to 0 + uint32_t pad_10; // TODO: 0x10 + AttributeType m_nBotAttrs; // +0x14 + float m_flVisionRange; // +0x18 + CUtlStringList m_ItemNames; // +0x1c + CUtlVector m_ItemAttrs; // +0x30 + CUtlVector m_CharAttrs; // +0x44 + CUtlStringList m_Tags; // +0x58 + }; + SIZE_CHECK(EventChangeAttributes_t, 0x6c); + /* custom */ class ExtraData { diff --git a/src/stub/tfbot_behavior.cpp b/src/stub/tfbot_behavior.cpp index ad9d8ece..4969d149 100644 --- a/src/stub/tfbot_behavior.cpp +++ b/src/stub/tfbot_behavior.cpp @@ -75,6 +75,30 @@ CTFBotSeekAndDestroy *CTFBotSeekAndDestroy::New(float duration) } +static MemberFuncThunk ft_CTFBotFetchFlag_ctor("CTFBotFetchFlag::CTFBotFetchFlag [C1]"); +CTFBotFetchFlag *CTFBotFetchFlag::New(bool give_up_when_done) +{ + // TODO: verify sizeof(CTFBotFetchFlag) in the game code at runtime + // TODO: verify that the addr for the ctor actually exists + + auto action = reinterpret_cast(::operator new(sizeof(CTFBotFetchFlag))); + ft_CTFBotFetchFlag_ctor(action, give_up_when_done); + return action; +} + + +static MemberFuncThunk *> ft_CTFBotPushToCapturePoint_ctor("CTFBotPushToCapturePoint::CTFBotPushToCapturePoint [C1]"); +CTFBotPushToCapturePoint *CTFBotPushToCapturePoint::New(Action *done_action) +{ + // TODO: verify sizeof(CTFBotPushToCapturePoint) in the game code at runtime + // TODO: verify that the addr for the ctor actually exists + + auto action = reinterpret_cast(::operator new(sizeof(CTFBotPushToCapturePoint))); + ft_CTFBotPushToCapturePoint_ctor(action, done_action); + return action; +} + + static MemberFuncThunk ft_CTFBotMedicHeal_ctor("CTFBotMedicHeal::CTFBotMedicHeal [C1]"); CTFBotMedicHeal *CTFBotMedicHeal::New() { diff --git a/src/stub/tfbot_behavior.h b/src/stub/tfbot_behavior.h index 43b84378..ecc293d7 100644 --- a/src/stub/tfbot_behavior.h +++ b/src/stub/tfbot_behavior.h @@ -20,9 +20,11 @@ class ActionStub : public Action class CTFBotAttack : public ActionStub { public: - CTFBotAttack() = delete; static CTFBotAttack *New(); +protected: + CTFBotAttack() = delete; + private: PathFollower m_PathFollower; // +0x0034 ChasePath m_ChasePath; // +0x4808 @@ -34,9 +36,11 @@ SIZE_CHECK(CTFBotAttack, 0x9014); class CTFBotSeekAndDestroy : public ActionStub { public: - CTFBotSeekAndDestroy() = delete; static CTFBotSeekAndDestroy *New(float duration = -1.0f); +protected: + CTFBotSeekAndDestroy() = delete; + private: uint32_t m_Dword0034; // +0x0034 uint32_t m_Dword0038; // +0x0038 @@ -47,12 +51,46 @@ class CTFBotSeekAndDestroy : public ActionStub SIZE_CHECK(CTFBotSeekAndDestroy, 0x4828); +class CTFBotFetchFlag : public ActionStub +{ +public: + static CTFBotFetchFlag *New(bool give_up_when_done = false); + +protected: + CTFBotFetchFlag() = delete; + +private: + bool m_bGiveUpWhenDone; // +0x0032 + PathFollower m_PathFollower; // +0x0034 + CountdownTimer m_ctRecomputePath; // +0x4808 +}; +SIZE_CHECK(CTFBotFetchFlag, 0x4814); + + +class CTFBotPushToCapturePoint : public ActionStub +{ +public: + static CTFBotPushToCapturePoint *New(Action *done_action); + +protected: + CTFBotPushToCapturePoint() = delete; + +private: + PathFollower m_PathFollower; // +0x0034 + CountdownTimer m_ctRecomputePath; // +0x4808 + Action *m_DoneAction; // +0x4814 +}; +SIZE_CHECK(CTFBotPushToCapturePoint, 0x4818); + + class CTFBotMedicHeal : public ActionStub { public: - CTFBotMedicHeal() = delete; static CTFBotMedicHeal *New(); +protected: + CTFBotMedicHeal() = delete; + private: ChasePath m_ChasePath; // +0x0034 CountdownTimer m_ctUnknown1; // +0x4834 @@ -71,9 +109,11 @@ SIZE_CHECK(CTFBotMedicHeal, 0x9058); class CTFBotSniperLurk : public ActionStub { public: - CTFBotSniperLurk() = delete; static CTFBotSniperLurk *New(); +protected: + CTFBotSniperLurk() = delete; + private: CountdownTimer m_ctUnknown1; // +0x0034 CountdownTimer m_ctUnknown2; // +0x0040 @@ -88,9 +128,11 @@ SIZE_CHECK(CTFBotSniperLurk, 0x485c); class CTFBotSpyInfiltrate : public ActionStub { public: - CTFBotSpyInfiltrate() = default; static CTFBotSpyInfiltrate *New(); +protected: + CTFBotSpyInfiltrate() = default; + private: CountdownTimer m_ctUnknown1; // +0x0034 PathFollower m_PathFollower; // +0x0040 @@ -105,8 +147,10 @@ SIZE_CHECK(CTFBotSpyInfiltrate, 0x4834); class CTFBotEngineerBuild : public ActionStub { public: - CTFBotEngineerBuild() = default; static CTFBotEngineerBuild *New(); + +protected: + CTFBotEngineerBuild() = default; }; SIZE_CHECK(CTFBotEngineerBuild, 0x0034); @@ -114,9 +158,11 @@ SIZE_CHECK(CTFBotEngineerBuild, 0x0034); class CTFBotDead : public ActionStub { public: - CTFBotDead() = default; static CTFBotDead *New(); +protected: + CTFBotDead() = default; + private: IntervalTimer m_itDeathEpoch; // +0x0034 }; diff --git a/src/stub/tfplayer.cpp b/src/stub/tfplayer.cpp index 74a243c3..f70729cc 100644 --- a/src/stub/tfplayer.cpp +++ b/src/stub/tfplayer.cpp @@ -1,5 +1,6 @@ #include "stub/tfplayer.h" #include "stub/tfweaponbase.h" +#include "util/misc.h" IMPL_SENDPROP(int, CTFPlayerClassShared, m_iClass, CTFPlayer); @@ -9,15 +10,21 @@ IMPL_SENDPROP(string_t, CTFPlayerClassShared, m_iszCustomModel, CTFPlayer); MemberFuncThunk CTFPlayerClassShared::ft_SetCustomModel("CTFPlayerClassShared::SetCustomModel"); -IMPL_SENDPROP(float, CTFPlayerShared, m_flRageMeter, CTFPlayer); -IMPL_SENDPROP(bool, CTFPlayerShared, m_bRageDraining, CTFPlayer); -IMPL_SENDPROP(bool, CTFPlayerShared, m_bInUpgradeZone, CTFPlayer); +IMPL_SENDPROP(float, CTFPlayerShared, m_flEnergyDrinkMeter, CTFPlayer); +IMPL_SENDPROP(float, CTFPlayerShared, m_flHypeMeter, CTFPlayer); +IMPL_SENDPROP(float, CTFPlayerShared, m_flChargeMeter, CTFPlayer); +IMPL_SENDPROP(float, CTFPlayerShared, m_flRageMeter, CTFPlayer); +IMPL_SENDPROP(bool, CTFPlayerShared, m_bRageDraining, CTFPlayer); +IMPL_SENDPROP(bool, CTFPlayerShared, m_bInUpgradeZone, CTFPlayer); -MemberFuncThunk< CTFPlayerShared *, void, ETFCond, float, CBaseEntity * > CTFPlayerShared::ft_AddCond ("CTFPlayerShared::AddCond"); -MemberFuncThunk< CTFPlayerShared *, void, ETFCond, bool > CTFPlayerShared::ft_RemoveCond ("CTFPlayerShared::RemoveCond"); -MemberFuncThunk CTFPlayerShared::ft_InCond ("CTFPlayerShared::InCond"); -MemberFuncThunk CTFPlayerShared::ft_IsInvulnerable("CTFPlayerShared::IsInvulnerable"); -MemberFuncThunk< CTFPlayerShared *, void, float, float, int, CTFPlayer *> CTFPlayerShared::ft_StunPlayer ("CTFPlayerShared::StunPlayer"); +MemberFuncThunk< CTFPlayerShared *, void, ETFCond, float, CBaseEntity * > CTFPlayerShared::ft_AddCond ("CTFPlayerShared::AddCond"); +MemberFuncThunk< CTFPlayerShared *, void, ETFCond, bool > CTFPlayerShared::ft_RemoveCond ("CTFPlayerShared::RemoveCond"); +MemberFuncThunk CTFPlayerShared::ft_InCond ("CTFPlayerShared::InCond"); +MemberFuncThunk CTFPlayerShared::ft_IsInvulnerable ("CTFPlayerShared::IsInvulnerable"); +MemberFuncThunk< CTFPlayerShared *, void, float, float, int, CTFPlayer *> CTFPlayerShared::ft_StunPlayer ("CTFPlayerShared::StunPlayer"); +MemberFuncThunk< CTFPlayerShared *, void, CBitVec<192>& > CTFPlayerShared::ft_GetConditionsBits ("CTFPlayerShared::GetConditionsBits"); +MemberFuncThunk< CTFPlayerShared *, float, ETFCond > CTFPlayerShared::ft_GetConditionDuration("CTFPlayer::GetConditionDuration"); +MemberFuncThunk< CTFPlayerShared *, CBaseEntity *, ETFCond > CTFPlayerShared::ft_GetConditionProvider("CTFPlayer::GetConditionProvider"); IMPL_SENDPROP(CTFPlayerShared, CTFPlayer, m_Shared, CTFPlayer); @@ -60,8 +67,63 @@ CTFWeaponBase *CTFPlayer::GetActiveTFWeapon() const } -static StaticFuncThunk ft_GetTFConditionFromName("GetTFConditionFromName"); -ETFCond GetTFConditionFromName(const char *name) { return ft_GetTFConditionFromName(name); } +static GlobalThunk g_aConditionNames("g_aConditionNames"); -static StaticFuncThunk ft_GetTFConditionName("GetTFConditionName"); -const char *GetTFConditionName(ETFCond cond) { return ft_GetTFConditionName(cond); } +static int GetNumberOfTFConds() +{ + static int iNumTFConds = + []{ + ConColorMsg(Color(0xff, 0x00, 0xff, 0xff), "GetNumberOfTFConds: in lambda\n"); + + const SegInfo& info_seg_server_rodata = LibMgr::GetInfo(Library::SERVER).GetSeg(Segment::RODATA); + + constexpr char prefix[] = "TF_COND_"; + + for (int i = 0; i < 256; ++i) { + const char *str = g_aConditionNames[i]; + + if (str == nullptr || !info_seg_server_rodata.ContainsAddr(str, 1) || strncmp(str, prefix, strlen(prefix)) != 0) { + return i; + } + } + + assert(false); + return 0; + }(); + + return iNumTFConds; +} + +bool IsValidTFConditionNumber(int num) +{ + return (num >= 0 && num < GetNumberOfTFConds()); +} + +ETFCond ClampTFConditionNumber(int num) +{ + return static_cast(Clamp(num, 0, GetNumberOfTFConds() - 1)); +} + + +const char *GetTFConditionName(ETFCond cond) +{ + int num = static_cast(cond); + + if (!IsValidTFConditionNumber(num)) { + return nullptr; + } + + return g_aConditionNames[num]; +} + +ETFCond GetTFConditionFromName(const char *name) +{ + int cond_count = GetNumberOfTFConds(); + for (int i = 0; i < cond_count; ++i) { + if (FStrEq(g_aConditionNames[i], name)) { + return static_cast(i); + } + } + + return TF_COND_INVALID; +} diff --git a/src/stub/tfplayer.h b/src/stub/tfplayer.h index e37e9eae..b0936e3b 100644 --- a/src/stub/tfplayer.h +++ b/src/stub/tfplayer.h @@ -32,6 +32,8 @@ enum enum ETFCond { + TF_COND_INVALID = -1, + TF_COND_AIMING = 0, TF_COND_ZOOMED = 1, TF_COND_DISGUISING = 2, @@ -150,7 +152,10 @@ enum ETFCond TF_COND_KNOCKED_INTO_AIR = 115, TF_COND_COMPETITIVE_WINNER = 116, TF_COND_COMPETITIVE_LOSER = 117, - TF_COND_NO_TAUNTING = 118, + TF_COND_HEALING_DEBUFF = 118, + TF_COND_PASSTIME_PENALTY_DEBUFF = 119, + + TF_COND_COUNT, }; @@ -186,12 +191,18 @@ class CTFPlayerShared CTFPlayer *GetOuter(); - void AddCond(ETFCond cond, float duration = -1.0f, CBaseEntity *provider = nullptr) { ft_AddCond (this, cond, duration, provider); } - void RemoveCond(ETFCond cond, bool b1 = false) { ft_RemoveCond (this, cond, b1); } - bool InCond(ETFCond cond) const { return ft_InCond (this, cond); } - bool IsInvulnerable() const { return ft_IsInvulnerable(this); } - void StunPlayer(float duration, float amount, int flags, CTFPlayer *stunner) { ft_StunPlayer (this, duration, amount, flags, stunner); } + void AddCond(ETFCond cond, float duration = -1.0f, CBaseEntity *provider = nullptr) { ft_AddCond (this, cond, duration, provider); } + void RemoveCond(ETFCond cond, bool b1 = false) { ft_RemoveCond (this, cond, b1); } + bool InCond(ETFCond cond) const { return ft_InCond (this, cond); } + bool IsInvulnerable() const { return ft_IsInvulnerable (this); } + void StunPlayer(float duration, float amount, int flags, CTFPlayer *stunner) { ft_StunPlayer (this, duration, amount, flags, stunner); } + void GetConditionsBits(CBitVec<192>& bitvec) { ft_GetConditionsBits (this, bitvec); } + float GetConditionDuration(ETFCond cond) { return ft_GetConditionDuration(this, cond); } + CBaseEntity *GetConditionProvider(ETFCond cond) { return ft_GetConditionProvider(this, cond); } + DECL_SENDPROP(float, m_flEnergyDrinkMeter); + DECL_SENDPROP(float, m_flHypeMeter); + DECL_SENDPROP(float, m_flChargeMeter); DECL_SENDPROP(float, m_flRageMeter); DECL_SENDPROP(bool, m_bRageDraining); DECL_SENDPROP(bool, m_bInUpgradeZone); @@ -202,6 +213,9 @@ class CTFPlayerShared static MemberFuncThunk ft_InCond; static MemberFuncThunk ft_IsInvulnerable; static MemberFuncThunk< CTFPlayerShared *, void, float, float, int, CTFPlayer *> ft_StunPlayer; + static MemberFuncThunk< CTFPlayerShared *, void, CBitVec<192>& > ft_GetConditionsBits; + static MemberFuncThunk< CTFPlayerShared *, float, ETFCond > ft_GetConditionDuration; + static MemberFuncThunk< CTFPlayerShared *, CBaseEntity *, ETFCond > ft_GetConditionProvider; }; class CTFPlayer : public CBaseMultiplayerPlayer @@ -297,8 +311,10 @@ inline CTFPlayer *ToTFPlayer(CBaseEntity *pEntity) } -ETFCond GetTFConditionFromName(const char *name); +bool IsValidTFConditionNumber(int num); +ETFCond ClampTFConditionNumber(int num); const char *GetTFConditionName(ETFCond cond); +ETFCond GetTFConditionFromName(const char *name); #endif diff --git a/src/stub/tfweaponbase.cpp b/src/stub/tfweaponbase.cpp index 44763a63..a0690652 100644 --- a/src/stub/tfweaponbase.cpp +++ b/src/stub/tfweaponbase.cpp @@ -13,18 +13,28 @@ IMPL_SENDPROP(CHandle, CBaseCombatWeapon, m_hOwner, MemberFuncThunk CBaseCombatWeapon::ft_IsMeleeWeapon("CBaseCombatWeapon::IsMeleeWeapon"); -MemberVFuncThunk CBaseCombatWeapon::vt_GetMaxClip1(TypeName(), "CBaseCombatWeapon::GetMaxClip1"); -MemberVFuncThunk CBaseCombatWeapon::vt_GetMaxClip2(TypeName(), "CBaseCombatWeapon::GetMaxClip2"); +MemberVFuncThunk CBaseCombatWeapon::vt_GetMaxClip1(TypeName(), "CBaseCombatWeapon::GetMaxClip1"); +MemberVFuncThunk CBaseCombatWeapon::vt_GetMaxClip2(TypeName(), "CBaseCombatWeapon::GetMaxClip2"); +MemberVFuncThunk< CBaseCombatWeapon *, bool> CBaseCombatWeapon::vt_HasAmmo (TypeName(), "CBaseCombatWeapon::HasAmmo"); +IMPL_SENDPROP(float, CTFWeaponBase, m_flLastFireTime, CTFWeaponBase); +IMPL_SENDPROP(float, CTFWeaponBase, m_flEffectBarRegenTime, CTFWeaponBase); +IMPL_SENDPROP(float, CTFWeaponBase, m_flEnergy, CTFWeaponBase); + MemberVFuncThunk CTFWeaponBase::vt_GetWeaponID( TypeName(), "CTFBonesaw::GetWeaponID"); MemberVFuncThunk CTFWeaponBase::vt_GetPenetrateType(TypeName(), "CTFSniperRifle::GetPenetrateType"); -MemberVFuncThunk CTFWeaponBaseMelee::vt_GetSwingRange(TypeName(), "CTFWeaponBaseMelee::GetSwingRange"); +MemberFuncThunk CTFSniperRifleDecap::ft_GetCount("CTFSniperRifleDecap::GetCount"); -MemberFuncThunk CTFSniperRifleDecap::ft_GetCount("CTFSniperRifleDecap::GetCount"); +MemberVFuncThunk CTFWeaponBaseMelee::vt_GetSwingRange(TypeName(), "CTFWeaponBaseMelee::GetSwingRange"); +MemberVFuncThunk CTFWeaponBaseMelee::vt_DoSwingTrace (TypeName(), "CTFWeaponBaseMelee::DoSwingTrace"); + + +MemberFuncThunk CTFKnife::ft_CanPerformBackstabAgainstTarget("CTFKnife::CanPerformBackstabAgainstTarget"); +MemberFuncThunk CTFKnife::ft_IsBehindAndFacingTarget ("CTFKnife::IsBehindAndFacingTarget"); IMPL_SENDPROP(CHandle, CTFRobotArm, m_hRobotArm, CTFRobotArm); diff --git a/src/stub/tfweaponbase.h b/src/stub/tfweaponbase.h index c8b78d72..c0c0e477 100644 --- a/src/stub/tfweaponbase.h +++ b/src/stub/tfweaponbase.h @@ -16,23 +16,25 @@ class CBaseCombatWeapon : public CEconEntity int GetMaxClip1() const { return vt_GetMaxClip1(this); } int GetMaxClip2() const { return vt_GetMaxClip2(this); } + bool HasAmmo() { return vt_HasAmmo (this); } - DECL_SENDPROP(float, m_flNextPrimaryAttack); - DECL_SENDPROP(float, m_flNextSecondaryAttack); - DECL_SENDPROP(float, m_flTimeWeaponIdle); - DECL_SENDPROP(int, m_iState); - DECL_SENDPROP(int, m_iPrimaryAmmoType); - DECL_SENDPROP(int, m_iSecondaryAmmoType); - DECL_SENDPROP(int, m_iClip1); - DECL_SENDPROP(int, m_iClip2); + DECL_SENDPROP(float, m_flNextPrimaryAttack); + DECL_SENDPROP(float, m_flNextSecondaryAttack); + DECL_SENDPROP(float, m_flTimeWeaponIdle); + DECL_SENDPROP(int, m_iState); + DECL_SENDPROP(int, m_iPrimaryAmmoType); + DECL_SENDPROP(int, m_iSecondaryAmmoType); + DECL_SENDPROP(int, m_iClip1); + DECL_SENDPROP(int, m_iClip2); private: DECL_SENDPROP(CHandle, m_hOwner); static MemberFuncThunk ft_IsMeleeWeapon; - static MemberVFuncThunk vt_GetMaxClip1; - static MemberVFuncThunk vt_GetMaxClip2; + static MemberVFuncThunk vt_GetMaxClip1; + static MemberVFuncThunk vt_GetMaxClip2; + static MemberVFuncThunk< CBaseCombatWeapon *, bool> vt_HasAmmo; }; class CTFWeaponBase : public CBaseCombatWeapon @@ -43,20 +45,15 @@ class CTFWeaponBase : public CBaseCombatWeapon int GetWeaponID() const { return vt_GetWeaponID (this); } int GetPenetrateType() const { return vt_GetPenetrateType(this); } + DECL_SENDPROP(float, m_flLastFireTime); + DECL_SENDPROP(float, m_flEffectBarRegenTime); + DECL_SENDPROP(float, m_flEnergy); + private: static MemberVFuncThunk vt_GetWeaponID; static MemberVFuncThunk vt_GetPenetrateType; }; -class CTFWeaponBaseMelee : public CTFWeaponBase -{ -public: - int GetSwingRange() { return vt_GetSwingRange(this); } - -private: - static MemberVFuncThunk vt_GetSwingRange; -}; - class CTFWeaponBaseGun : public CTFWeaponBase {}; class CTFSniperRifle : public CTFWeaponBaseGun {}; @@ -73,6 +70,28 @@ class CTFSniperRifleDecap : public CTFSniperRifle }; +class CTFWeaponBaseMelee : public CTFWeaponBase +{ +public: + int GetSwingRange() { return vt_GetSwingRange(this); } + bool DoSwingTrace(trace_t& tr) { return vt_DoSwingTrace (this, tr); } + +private: + static MemberVFuncThunk vt_GetSwingRange; + static MemberVFuncThunk vt_DoSwingTrace; +}; + +class CTFKnife : public CTFWeaponBaseMelee +{ +public: + bool CanPerformBackstabAgainstTarget(CTFPlayer *player) { return ft_CanPerformBackstabAgainstTarget(this, player); } + bool IsBehindAndFacingTarget(CTFPlayer *player) { return ft_IsBehindAndFacingTarget (this, player); } + +private: + static MemberFuncThunk ft_CanPerformBackstabAgainstTarget; + static MemberFuncThunk ft_IsBehindAndFacingTarget; +}; + class CTFBonesaw : public CTFWeaponBaseMelee {}; class CTFWrench : public CTFWeaponBaseMelee {}; diff --git a/src/stub/vgui.cpp b/src/stub/vgui.cpp new file mode 100644 index 00000000..6249294c --- /dev/null +++ b/src/stub/vgui.cpp @@ -0,0 +1,16 @@ +#include "stub/vgui.h" + + +//namespace vgui +//{ +// static MemberFuncThunk ft_vgui_Frame_ctor("[client] vgui::Frame::Frame"); +// Frame::Frame(vgui::Panel *parent, const char *panelName, bool showTaskbarIcon, bool bPopup) { ft_vgui_Frame_ctor(this, parent, panelName, showTaskbarIcon, bPopup); } +//} + + +//GlobalThunk g_pClientMode("g_pClientMode"); +//StaticFuncThunk ft_GetClientModeNormal("[client] GetClientModeNormal"); + + +StaticFuncThunk&> CBuildFactoryHelper::ft_GetFactoryNames("[client] CBuildFactoryHelper::GetFactoryNames"); +StaticFuncThunk CBuildFactoryHelper::ft_InstancePanel ("[client] CBuildFactoryHelper::InstancePanel"); diff --git a/src/stub/vgui.h b/src/stub/vgui.h new file mode 100644 index 00000000..5f806c58 --- /dev/null +++ b/src/stub/vgui.h @@ -0,0 +1,33 @@ +#ifndef _INCLUDE_SIGSEGV_STUB_VGUI_H_ +#define _INCLUDE_SIGSEGV_STUB_VGUI_H_ + + +#include "link/link.h" + +#include <../client/iclientmode.h> +#include + + +//namespace vgui +//{ +// Frame::Frame(vgui::Panel *parent, const char *panelName, bool showTaskbarIcon = true, bool bPopup = true); +//} + + +//extern GlobalThunk g_pClientMode; +//inline IClientMode *GetClientModeNormal() { return g_pClientMode; } + + +class CBuildFactoryHelper +{ +public: + static void GetFactoryNames(CUtlVector& list) { ft_GetFactoryNames(list); } + static vgui::Panel *InstancePanel(const char *className) { return ft_InstancePanel(className); } + +private: + static StaticFuncThunk&> ft_GetFactoryNames; + static StaticFuncThunk ft_InstancePanel; +}; + + +#endif diff --git a/src/util/backtrace.cpp b/src/util/backtrace.cpp index 87ac72cd..2f8dec14 100644 --- a/src/util/backtrace.cpp +++ b/src/util/backtrace.cpp @@ -1,26 +1,11 @@ #include "util/backtrace.h" +#include "util/demangle.h" #include "library.h" #if defined _LINUX || defined _OSX -const char *try_demangle(const char *mangled) -{ - if (strlen(mangled) == 0) { - return strdup("???"); - } - - const char *demangled = cplus_demangle(mangled, DMGL_GNU_V3 | DMGL_TYPES | DMGL_ANSI | DMGL_PARAMS); - - if (demangled != nullptr) { - return demangled; - } else { - return strdup(mangled); - } -} - - void sym_get_proc_name(unw_cursor_t *cp, char *bufp, size_t len, unw_word_t *offp) { snprintf(bufp, len, "???"); @@ -43,6 +28,8 @@ void sym_get_proc_name(unw_cursor_t *cp, char *bufp, size_t len, unw_word_t *off best = sym; } } + + return true; } ); @@ -91,11 +78,14 @@ void cached_get_proc_name(unw_cursor_t *cp, const char **p_name, unw_word_t *p_o sym_get_proc_name(cp, buf, sizeof(buf), &off); } - const char *demangled = try_demangle(buf); + std::string demangled = "???"; + if (buf[0] != '\0') { + DemangleName(buf, demangled); + } + auto& entry = cache[r_ip]; entry.name = demangled; entry.off = off; - free((void *)demangled); *p_name = entry.name.c_str(); *p_off = entry.off; diff --git a/src/util/color.h b/src/util/color.h index f7c8ea84..02a2c874 100644 --- a/src/util/color.h +++ b/src/util/color.h @@ -7,7 +7,10 @@ class Color32 public: constexpr Color32() = default; - constexpr Color32(uint8_t r, uint8_t g, uint8_t b, uint8_t a = 0xff) +#if !defined _MSC_VER + constexpr +#endif + Color32(uint8_t r, uint8_t g, uint8_t b, uint8_t a = 0xff) { m_Bytes[0] = r; m_Bytes[1] = g; diff --git a/src/util/demangle.h b/src/util/demangle.h new file mode 100644 index 00000000..a266e4dc --- /dev/null +++ b/src/util/demangle.h @@ -0,0 +1,59 @@ +#ifndef _INCLUDE_SIGSEGV_UTIL_DEMANGLE_H +#define _INCLUDE_SIGSEGV_UTIL_DEMANGLE_H + + +inline bool DemangleName(const char *mangled, std::string& result) +{ +#if defined _WINDOWS + result = mangled; + return true; +#endif + + constexpr int options = DMGL_GNU_V3 | DMGL_TYPES | DMGL_ANSI | DMGL_PARAMS; + char *demangled = cplus_demangle(mangled, options); + + if (demangled != nullptr) { + result = demangled; + free(demangled); + return true; + } else { + result = mangled; + return false; + } +} + + +inline bool DemangleTypeName(const char *mangled, std::string& result) +{ +#if defined _WINDOWS + result = mangled; + return true; +#endif + + char *prefixed = new char[strlen(mangled) + 1 + 4]; + strcpy(prefixed, "_ZTS"); + strcat(prefixed, mangled); + + constexpr int options = DMGL_GNU_V3 | DMGL_TYPES | DMGL_ANSI | DMGL_PARAMS; + char *demangled = cplus_demangle(prefixed, options); + + delete[] prefixed; + + if (demangled != nullptr) { + result = demangled; + free(demangled); + + constexpr char strip[] = "typeinfo name for "; + if (strncmp(result.c_str(), strip, strlen(strip)) == 0) { + result = result.substr(strlen(strip)); + } + + return true; + } else { + result = mangled; + return false; + } +} + + +#endif diff --git a/src/util/iterate.h b/src/util/iterate.h index e9c79c55..96246289 100644 --- a/src/util/iterate.h +++ b/src/util/iterate.h @@ -37,6 +37,16 @@ inline void ForEachEntity(const FUNCTOR& func) } } +template +inline void ForEachEntityByClassname(const char *classname, const FUNCTOR& func) +{ + using T = CBaseEntity; + + for (CBaseEntity *ent = servertools->FindEntityByClassname(nullptr, classname); ent != nullptr; ent = servertools->FindEntityByClassname(ent, classname)) { + if (!CALL_FUNCTOR(T *)(func, ent)) break; + } +} + template inline void ForEachEntityByRTTI(const FUNCTOR& func) { diff --git a/src/util/misc.h b/src/util/misc.h index 4ef2e359..d7477c0a 100644 --- a/src/util/misc.h +++ b/src/util/misc.h @@ -48,7 +48,32 @@ class MatSingleThreadBlock int m_iCounter = 0; }; -#define MAT_SINGLE_THREAD_BLOCK for (MatSingleThreadBlock __mat_single_thread_block; __mat_single_thread_block.ShouldContinue(); ) +//#define MAT_SINGLE_THREAD_BLOCK for (MatSingleThreadBlock __mat_single_thread_block; __mat_single_thread_block.ShouldContinue(); ) +#define MAT_SINGLE_THREAD_BLOCK + + +inline bool FStrEq(const char *sz1, const char *sz2) +{ + return (sz1 == sz2 || V_stricmp(sz1, sz2) == 0); +} + + +#if 0 +/* allow using CHandle as the key type in std::unordered_map */ +namespace std +{ + template template<> struct hash> + { + using argument_type = CHandle; + using result_type = size_t; + + result_type operator()(const argument_type& arg) const + { + // TODO + } + }; +} +#endif #endif diff --git a/src/util/rtti.cpp b/src/util/rtti.cpp index 11c8f942..24f65a39 100644 --- a/src/util/rtti.cpp +++ b/src/util/rtti.cpp @@ -65,6 +65,8 @@ namespace RTTI } } } + + return true; } ); } @@ -93,8 +95,8 @@ namespace RTTI // REV 282-305 ms using TDScanner = CStringPrefixScanner; - using COLScanner = CTypeScanner; - using VTScanner = CTypeScanner; + using COLScanner = CAlignedTypeScanner ; + using VTScanner = CAlignedTypeScanner ; int n_total = 0; int n_skip = 0; @@ -129,7 +131,7 @@ namespace RTTI for (auto lib : {Library::SERVER, Library::ENGINE, Library::TIER0, Library::CLIENT}) { Prof::Begin(); - CScan scan1(CLibSegBounds(lib, ".data"), ".?AV"); + CScan scan1(CLibSegBounds(lib, Segment::DATA), ".?AV"); Prof::End("TD scan"); Prof::Begin(); for (auto match : scan1.Matches()) { @@ -173,7 +175,7 @@ namespace RTTI auto name = pair.first; auto p_TD = pair.second; - auto scanner = new COLScanner(CLibSegBounds(lib, ".rdata"), p_TD); + auto scanner = new COLScanner(CLibSegBounds(lib, Segment::RODATA), p_TD); scanners_COL.push_back(scanner); scannermap_COL[scanner] = name; @@ -215,7 +217,7 @@ namespace RTTI auto name = pair.first; auto p_COL = pair.second; - auto scanner = new VTScanner(CLibSegBounds(lib, ".rdata"), p_COL); + auto scanner = new VTScanner(CLibSegBounds(lib, Segment::RODATA), p_COL); scanners_VT.push_back(scanner); scannermap_VT[scanner] = name; diff --git a/src/util/rtti.h b/src/util/rtti.h index edcf5fdd..218a781d 100644 --- a/src/util/rtti.h +++ b/src/util/rtti.h @@ -13,11 +13,11 @@ typedef _TypeDescriptor rtti_t; #if defined __GNUC__ -template inline const char *TypeName() { return typeid( T).name(); } -template inline const char *TypeName(const T *t) { return typeid(*t).name(); } // if t is nullptr, will throw std::bad_typeid +template inline const char *TypeName() { return typeid( T).name(); } +template inline const char *TypeName(T *t) { return typeid(*t).name(); } // if t is nullptr, will throw std::bad_typeid #elif defined _MSC_VER -template inline const char *TypeName() { return typeid( T).raw_name(); } -template inline const char *TypeName(const T *t) { return typeid(*t).raw_name(); } // if t is nullptr, will throw std::bad_typeid +template inline const char *TypeName() { return typeid( T).raw_name(); } +template inline const char *TypeName(T *t) { return typeid(*t).raw_name(); } // if t is nullptr, will throw std::bad_typeid #endif diff --git a/src/util/vgui.cpp b/src/util/vgui.cpp new file mode 100644 index 00000000..fef3a47d --- /dev/null +++ b/src/util/vgui.cpp @@ -0,0 +1,79 @@ +#include "util/vgui.h" + + +// we have to use this stupid workaround to make MSVC do the member func ptr +// call properly due to vgui::Frame using virtual inheritance +class DummyClass {}; + +#if defined _MSC_VER +// there's a HIDDEN extra parameter in the ctor for classes with virtual +// inheritance on MSVC, which specifies whether the ctor is being called for the +// most-derived object (to tell it whether to initialize the vbtable at +0x4) +static MemberFuncThunk ft_Frame_ctor("[client] vgui::Frame::Frame"); +void Frame_ctor(vgui::Frame *_this, vgui::Panel *parent, const char *panelName, bool showTaskbarIcon = true, bool bPopup = true) { ft_Frame_ctor(reinterpret_cast(_this), parent, panelName, showTaskbarIcon, bPopup, true); } +#else +static MemberFuncThunk ft_Frame_ctor("[client] vgui::Frame::Frame"); +void Frame_ctor(vgui::Frame *_this, vgui::Panel *parent, const char *panelName, bool showTaskbarIcon = true, bool bPopup = true) { ft_Frame_ctor(reinterpret_cast(_this), parent, panelName, showTaskbarIcon, bPopup); } +#endif + + +FrameWrapper *FrameWrapper::Create(vgui::Panel *parent, const char *panelName) +{ + auto pFrame = reinterpret_cast(::operator new(0x400)); + + Frame_ctor(pFrame, parent, panelName); + +// pFrame->ModifyVTable(); + + return pFrame; +} + +void FrameWrapper::ModifyVTable() +{ + void ***ppVT = reinterpret_cast(this); + + static void *mod_vtable[0x1000]; + static bool init = false; + if (!init) { + memcpy(mod_vtable, *ppVT, sizeof(mod_vtable)); + +//#if defined _WINDOWS +// constexpr ptrdiff_t VTIDX_dtor = 0x007c / 4; +// +// (*ppVT)[VTIDX_dtor] = +//#endif + + init = true; + } + + *ppVT = mod_vtable; +} + + +CustomFrame::CustomFrame(const char *name, const char *title) +{ + this->m_pFrame = FrameWrapper::Create(g_pClientMode->GetViewport(), name); + this->m_VPanel = this->m_pFrame->GetVPanel(); + + this->m_pFrame->SetDeleteSelfOnClose(false); + this->m_pFrame->SetScheme("ClientScheme.res"); + this->m_pFrame->SetTitle(title, true); + + this->m_pFrame->SetKeyBoardInputEnabled(false); + this->m_pFrame->SetMouseInputEnabled (false); + + this->m_pFrame->Activate(); +} + +CustomFrame::~CustomFrame() +{ + delete this->m_pFrame; +} + + +void CustomFrame::RemoveAll() +{ + while (!AutoList::List().empty()) { + delete AutoList::List().front(); + } +} diff --git a/src/util/vgui.h b/src/util/vgui.h new file mode 100644 index 00000000..8e2a04de --- /dev/null +++ b/src/util/vgui.h @@ -0,0 +1,51 @@ +#ifndef _INCLUDE_SIGSEGV_UTIL_VGUI_H_ +#define _INCLUDE_SIGSEGV_UTIL_VGUI_H_ + + +#include "stub/vgui.h" +#include "util/autolist.h" + + +// be very careful: +// - the purpose of this is mainly for overriding virtual functions +// - only call parent virtual functions and/or use the global VGUI interfaces +class FrameWrapper : public vgui::Frame +{ +public: + // disallowed: using the constructor + FrameWrapper() = delete; + FrameWrapper(const FrameWrapper&) = delete; + + // disallowed: doing anything in the destructor + // (unless we can find a non-ugly way to override the dtor vtable entry) + virtual ~FrameWrapper() {} + + + + static FrameWrapper *Create(vgui::Panel *parent, const char *panelName); + +private: + void ModifyVTable(); + + // disallowed: non-static data members +}; +static_assert(sizeof(FrameWrapper) == sizeof(vgui::Frame), ""); + + +class CustomFrame : public AutoList +{ +public: + CustomFrame(const char *name, const char *title); + virtual ~CustomFrame(); + + + + static void RemoveAll(); + +private: + FrameWrapper *m_pFrame = nullptr; + vgui::VPANEL m_VPanel = 0; +}; + + +#endif