Raised This Month: $319 Target: $400
 79%   Almost there!

Solved [L4D1/2] Don't switch weapon to secondary slot when pick up pistols


Post New Thread Reply   
 
Thread Tools Display Modes
Author Message
HarryPotter
Veteran Member
Join Date: Sep 2017
Location: TW
Old 09-20-2021 , 18:52   [L4D1/2] Don't switch weapon to secondary slot when pick up pistols
Reply With Quote #1

As title says, in game default, if I pick up pistol or magnum, it will switch current active weapon to secondary slot, I don't want this.
If any can help, I will appreciate.

-what I found so far-
SDKHook_WeaponEquipPost, SDKHook_WeaponEquip, and SDKHook_WeaponCanSwitchTo don't fire when picking up the second pistol for both games.

Edit: Problem sloved by Forgetest, thanks everyone, click here
__________________

Last edited by HarryPotter; 09-23-2021 at 07:45. Reason: solved, see post#8
HarryPotter is offline
Marttt
Veteran Member
Join Date: Jan 2019
Location: Brazil
Old 09-20-2021 , 20:47   Re: [L4D1/2] Don't switch weapon to secondary slot when pick up pistols
Reply With Quote #2

Try the following codes:

Method 1 (may be a bit glitch)

Spoiler


Method 2 (didn't have much time to work on this one, need some rework)

Spoiler
__________________

Last edited by Marttt; 09-21-2021 at 11:12.
Marttt is offline
Forgetest
Member
Join Date: Aug 2020
Old 09-21-2021 , 01:23   Re: [L4D1/2] Don't switch weapon to secondary slot when pick up pistols
Reply With Quote #3

Quote:
Originally Posted by Marttt View Post
Try the following codes:

Method 1 (may be a bit glitch)

Spoiler


Method 2 (didn't have much time to work on this one, need some rework)

Spoiler
As far as I know, WeaponEquip doesn't fire when picking up the second pistol for both games. Internally uses EquipSecondWeapon.

I've been trying to make memory patches on this, only to get crashes on both games on Windows, but it seems to work on Linux L4D2.

Partial pseudocode from EquipSecondWeapon:
PHP Code:
    if ( v4 // = (CBaseCombatCharacter *)CTerrorWeapon::GetPlayerOwner(this);
    
{
      if ( 
this == (CTerrorGun *)CBaseCombatCharacter::GetActiveWeapon(v4) )
      {
        if ( *((
_BYTE *)this 6114) ) // m_inInitialPickup
          
(*(void (__cdecl **)(CTerrorGun *))(*(_DWORD *)this 1128))(this); // CTerrorGun::Reload()
      
}
      else
      {
        if ( 
CBaseCombatCharacter::GetActiveWeapon(v4) )
        {
          
v9 CBaseCombatCharacter::GetActiveWeapon(v4);
          (*(
void (__cdecl **)(int_DWORD))(*(_DWORD *)v9 1064))(v90); // CTerrorGun::Holster(CBaseCombatWeapon*)
        
}
        
CBaseCombatCharacter::SetActiveWeapon(v4this);
        (*(
void (__cdecl **)(CTerrorGun *))(*(_DWORD *)this 1060))(this); // CTerrorGun::Deploy()
      

So I aimed to patch those calls of Holster, SetActiveWeapon and Deploy to NOPs. However as mentioned above, it's crashing on Windows.

My detour setup: (There should be another detour on CWeaponSpawn::Use, otherwise it only functions when picking up dropped pistols)
PHP Code:
    Handle hDetour null;
    if (!
g_bLeft4Dead2)
    {
        if (
os == 1// Linux
        
{
            
hDetour DHookCreateDetour(Address_NullCallConv_CDECLReturnType_VoidThisPointer_Ignore);
            
DHookAddParam(hDetourHookParamType_CBaseEntity);
            
DHookAddParam(hDetourHookParamType_CBaseEntity);
        } else {
            
hDetour DHookCreateDetour(Address_NullCallConv_THISCALLReturnType_VoidThisPointer_CBaseEntity);
            
DHookAddParam(hDetourHookParamType_Int);
            
DHookAddParam(hDetourHookParamType_CBaseEntity);
            
DHookAddParam(hDetourHookParamType_Int);
            
DHookAddParam(hDetourHookParamType_Int);            
        }
    }
    else
    {
        if (
os == 1// Linux
        
{
            
hDetour DHookCreateDetour(Address_NullCallConv_CDECLReturnType_VoidThisPointer_Ignore);
            
DHookAddParam(hDetourHookParamType_CBaseEntity);
            
DHookAddParam(hDetourHookParamType_CBaseEntity);
            
DHookAddParam(hDetourHookParamType_Int);
        } else {
            
hDetour DHookCreateDetour(Address_NullCallConv_THISCALLReturnType_VoidThisPointer_CBaseEntity);
            
DHookAddParam(hDetourHookParamType_CBaseEntity);
            
DHookAddParam(hDetourHookParamType_Int);
            
DHookAddParam(hDetourHookParamType_Int);
            
DHookAddParam(hDetourHookParamType_Int);
        }
    }
    
    if (!
hDetour)
        
SetFailState("detour");
    
    if (!
DHookSetFromConf(hDetourconfSDKConf_Signature"CTerrorGun_Use"))
        
SetFailState("DHookSetFromConf");
    
    if (
os == 1// Linux
    
{
        if (!
DHookEnableDetour(hDetourfalseCTerrorGun_OnUse) || !DHookEnableDetour(hDetourtrueCTerrorGun_OnUsePost))
            
SetFailState("DHookEnableDetour");
    } else {
        if (!
DHookEnableDetour(hDetourfalseWindows_CTerrorGun_OnUse) || !DHookEnableDetour(hDetourtrueWindows_CTerrorGun_OnUsePost))
            
SetFailState("DHookEnableDetour");
    } 
Attached Files
File Type: txt secondary.txt (3.6 KB, 10 views)

Last edited by Forgetest; 09-21-2021 at 01:36.
Forgetest is online now
Psyk0tik
AlliedModders Donor
Join Date: May 2012
Location: Homeless
Old 09-21-2021 , 03:36   Re: [L4D1/2] Don't switch weapon to secondary slot when pick up pistols
Reply With Quote #4

Quote:
Originally Posted by Forgetest View Post
I've been trying to make memory patches on this, only to get crashes on both games on Windows, but it seems to work on Linux L4D2.

...

So I aimed to patch those calls of Holster, SetActiveWeapon and Deploy to NOPs. However as mentioned above, it's crashing on Windows.
Patching out function calls with NOPs on Windows isn't sufficient most of the time. Even if you patch out the entire instruction (which is almost always needed on Windows), it won't work most of the time and you'll have to worry about patching a lot of bytes. You can instead try patching with unconditional jumps.

If you don't know how those work:

Basically, your patch should jump from one instruction to another (while skipping any other instruction between your source and your destination). The destination of the jump depends on which part(s) of the function you want to skip over.

For example, if you want to skip the function call for CTerrorGun::Holster, you'll want to start at the first byte of that function's call instruction. Since an unconditional jump (E9 ? ? ? ?) takes 5 bytes, the length of your jump will be calculated by the following equation:

Let a1 = Address of the patch location + 5 bytes (because the patch itself takes five bytes)
Let a2 = Address of the start of the instruction after the CTerrorGun::Holster's call instruction.
Equation: a2 - a1 = JMP instruction length

Here is CTerrorGun::Holster's call instruction on Windows:
Code:
.text:103E4712 8B 10                                         mov     edx, [eax]
.text:103E4714 8B C8                                         mov     ecx, eax
.text:103E4716 8B 82 24 04 00 00                             mov     eax, [edx+424h]
.text:103E471C 6A 00                                         push    0
.text:103E471E FF D0                                         call    eax
Your patch will start at the first byte (8B). The offset for that is 172h (370). Your patch length is 5 bytes so a1 will be 103E4717 (82).

Here's the next instruction:
Code:
.text:103E4720 56                                            push    esi
.text:103E4721 8B CB                                         mov     ecx, ebx
.text:103E4723 E8 C8 F9 C5 FF                                call    sub_100440F0
Your a2 will be 103E4720. Now you can use the equation and get 9. Your patch bytes will now be \xE9\x09\x00\x00\x00.

Since I like to be consistent on both platforms and both games as much as possible, I used the unconditional jump method for Linux as well. These are my offsets and sigs:
PHP Code:
"Games"
{
    
"#default"
    
{
        
"Offsets"
        
{
            
"OS"
            
{
                
"linux"            "1"
                "windows"        "0"
            
}
        }
    }
    
"left4dead"
    
{
        
"MemPatches"
        
{
            
"EquipSecondWeapon_StopHolster"
            
{
                
"signature"        "CTerrorGun_EquipSecondWeapon"
                "linux"
                
{
                    
"offset"    "297"
                    "verify"    "\x8B"
                    "patch"        "\xE9\x0E\x00\x00\x00"
                
}
                
"windows"
                
{
                    
"offset"    "403"
                    "verify"    "\x8B"
                    "patch"        "\xE9\x09\x00\x00\x00"
                
}
            }
            
"EquipSecondWeapon_SetActiveWeapon"
            
{
                
"signature"        "CTerrorGun_EquipSecondWeapon"
                "linux"
                
{
                    
"offset"    "316"
                    "verify"    "\x89"
                    "patch"        "\xE9\x0B\x00\x00\x00"
                
}
                
"windows"
                
{
                    
"offset"    "417"
                    "verify"    "\x56"
                    "patch"        "\xE9\x03\x00\x00\x00"
                
}
            }
            
"EquipSecondWeapon_Deploy"
            
{
                
"signature"        "CTerrorGun_EquipSecondWeapon"
                "linux"
                
{
                    
"offset"    "332"
                    "verify"    "\x8B"
                    "patch"        "\xE9\x06\x00\x00\x00"
                
}
                
"windows"
                
{
                    
"offset"    "425"
                    "verify"    "\x8B"
                    "patch"        "\xE9\x07\x00\x00\x00"
                
}
            }
        }
        
"Signatures"
        
{
            
/**
             * CTerrorGun::Use(CBaseEntity *, CBaseEntity *, USE_TYPE, float)
             *
             * How to find on Windows:
             * 1. Find the function's offsets with asherkin's VTable dump.
             * 2. Use IDA to get the VTable dump for the "CTerrorGun" class on Windows.
             * 3. Compare your dump's offsets with asherkin's dump's offsets to find the target function.
             * 4. Look for the target function in the Windows binary.
             **/
            
"CTerrorGun_Use"
            
{
                
"library"    "server"
                "linux"        "@_ZN10CTerrorGun3UseEP11CBaseEntityS1_8USE_TYPEf"
                "windows"    "\x2A\x2A\x2A\x2A\x2A\x2A\x2A\x2A\x2A\x85\x2A\x8B\x2A\x0F\x84\x2A\x2A\x2A\x2A\x8B\x2A\x8B\x90\x2A\x2A\x2A\x2A\x8B\x2A\xFF\x2A\x84\x2A\x0F\x84\x2A\x2A\x2A\x2A\x8B\x2A\x8B"
                        
/* ? ? ? ? ? ? ? ? ? 85 ? 8B ? 0F 84 ? ? ? ? 8B ? 8B 90 ? ? ? ? 8B ? FF ? 84 ? 0F 84 ? ? ? ? 8B ? 8B */
            
}
            
/**
             * CTerrorGun::EquipSecondWeapon()
             *
             * How to find on Windows:
             * 1a. Search for the "Player.PickupWeapon" string.
             * 2a. One of the functions that references that string should be this function.
             *
             * 1b. Find the function's offsets with asherkin's VTable dump.
             * 2b. Use IDA to get the VTable dump for the "CTerrorGun" class on Windows.
             * 3b. Compare your dump's offsets with asherkin's dump's offsets to find the target function.
             * 4b. Look for the target function in the Windows binary.
             **/
            
"CTerrorGun_EquipSecondWeapon"
            
{
                
"library"    "server"
                "linux"        "@_ZN10CTerrorGun17EquipSecondWeaponEv"
                "windows"    "\x2A\x2A\x2A\x2A\x2A\x2A\x2A\x80\xBE\x2A\x2A\x2A\x2A\x2A\x8D\x9E\x2A\x2A\x2A\x2A\x74\x2A\x5E"
                        
/* ? ? ? ? ? ? ? 80 BE ? ? ? ? ? 8D 9E ? ? ? ? 74 ? 5E */
            
}
            
/**
             * CWeaponSpawn::Use(CBaseEntity *, CBaseEntity *, USE_TYPE, float)
             *
             * How to find on Windows:
             * 1a. Search for the "spawner_give_item" string.
             * 2a. The string can only be found in this function.
             *
             * 1b. Find the function's offsets with asherkin's VTable dump.
             * 2b. In IDA Pro, go to the ".rdata" section of the Windows binary.
             * 3b. Search for "CWeaponSpawn::`vftable'" to jump to the "CWeaponSpawn" vtable.
             * 4b. Compare your dump's offsets with asherkin's dump's offsets to find the target function.
             * 5b. Look for the target function in the Windows binary.
             **/
            
"CWeaponSpawn::Use"
            
{
                
"library"    "server"
                "linux"        "@_ZN12CWeaponSpawn3UseEP11CBaseEntityS1_8USE_TYPEf"
                "windows"    "\x2A\x2A\x2A\x2A\x2A\x2A\x2A\x2A\x2A\x85\x2A\x8B\x2A\x89\x2A\x2A\x2A\x0F\x84\x2A\x2A\x2A\x2A\x8B\x2A\x8B\x90\x2A\x2A\x2A\x2A\x8B\x2A\xFF\x2A\x84\x2A\x0F\x84\x2A\x2A\x2A\x2A\x8B"
                        
/* ? ? ? ? ? ? ? ? ? 85 ? 8B ? 89 ? ? ? 0F 84 ? ? ? ? 8B ? 8B 90 ? ? ? ? 8B ? FF ? 84 ? 0F 84 ? ? ? ? 8B */
            
}
        }
    }
    
    
"left4dead2"
    
{
        
"MemPatches"
        
{
            
"EquipSecondWeapon_StopHolster"
            
{
                
"signature"        "CTerrorGun_EquipSecondWeapon"
                "linux"
                
{
                    
"offset"    "294"
                    "verify"    "\x8B"
                    "patch"        "\xE9\x0E\x00\x00\x00"
                
}
                
"windows"
                
{
                    
"offset"    "370"
                    "verify"    "\x8B"
                    "patch"        "\xE9\x09\x00\x00\x00"
                
}
            }
            
"EquipSecondWeapon_SetActiveWeapon"
            
{
                
"signature"        "CTerrorGun_EquipSecondWeapon"
                "linux"
                
{
                    
"offset"    "313"
                    "verify"    "\x89"
                    "patch"        "\xE9\x07\x00\x00\x00"
                
}
                
"windows"
                
{
                    
"offset"    "384"
                    "verify"    "\x56"
                    "patch"        "\xE9\x03\x00\x00\x00"
                
}
            }
            
"EquipSecondWeapon_Deploy"
            
{
                
"signature"        "CTerrorGun_EquipSecondWeapon"
                "linux"
                
{
                    
"offset"    "325"
                    "verify"    "\x8B"
                    "patch"        "\xE9\x06\x00\x00\x00"
                
}
                
"windows"
                
{
                    
"offset"    "392"
                    "verify"    "\x8B"
                    "patch"        "\xE9\x07\x00\x00\x00"
                
}
            }
        }
        
        
"Signatures"
        
{
            
/**
             * CTerrorGun::Use(CBaseEntity *, CBaseEntity *, USE_TYPE, float)
             *
             * How to find on Windows:
             * 1. Find the function's offsets with asherkin's VTable dump.
             * 2. Use IDA to get the VTable dump for the "CTerrorGun" class on Windows.
             * 3. Compare your dump's offsets with asherkin's dump's offsets to find the target function.
             * 4. Look for the target function in the Windows binary.
             **/
            
"CTerrorGun_Use"
            
{
                
"library"    "server"
                "linux"        "@_ZN10CTerrorGun3UseEP11CBaseEntityS1_8USE_TYPEf"
                "windows"    "\x2A\x2A\x2A\x2A\x2A\x2A\x56\x57\x8B\x2A\x2A\x8B\x2A\x85\x2A\x0F\x84\x2A\x2A\x2A\x2A\x8B\x2A\x8B\x90\x2A\x2A\x2A\x2A\x8B\x2A\xFF\x2A\x84\x2A\x0F\x84\x2A\x2A\x2A\x2A\x8B\x2A\x8B"
                        
/* ? ? ? ? ? ? 56 57 8B ? ? 8B ? 85 ? 0F 84 ? ? ? ? 8B ? 8B 90 ? ? ? ? 8B ? FF ? 84 ? 0F 84 ? ? ? ? 8B ? 8B */
            
}
            
/**
             * CTerrorGun::EquipSecondWeapon()
             *
             * How to find on Windows:
             * 1a. Search for the "Player.PickupWeapon" string.
             * 2a. One of the functions that references that string should be this function.
             *
             * 1b. Find the function's offsets with asherkin's VTable dump.
             * 2b. Use IDA to get the VTable dump for the "CTerrorGun" class on Windows.
             * 3b. Compare your dump's offsets with asherkin's dump's offsets to find the target function.
             * 4b. Look for the target function in the Windows binary.
             **/
            
"CTerrorGun_EquipSecondWeapon"
            
{
                
"library"    "server"
                "linux"        "@_ZN10CTerrorGun17EquipSecondWeaponEv"
                "windows"    "\x2A\x2A\x2A\x2A\x2A\x2A\x56\x8B\x2A\x80\xBE\x2A\x2A\x2A\x2A\x2A\x74\x2A\x32\x2A\x5E\x8B"
                        
/* ? ? ? ? ? ? 56 8B ? 80 BE ? ? ? ? ? 74 ? 32 ? 5E 8B */
            
}
            
/**
             * CWeaponSpawn::Use(CBaseEntity *, CBaseEntity *, USE_TYPE, float)
             *
             * How to find on Windows:
             * 1a. Search for the "spawner_give_item" string.
             * 2a. The string can only be found in this function.
             *
             * 1b. Find the function's offsets with asherkin's VTable dump.
             * 2b. In IDA Pro, go to the ".rdata" section of the Windows binary.
             * 3b. Search for "CWeaponSpawn::`vftable'" to jump to the "CWeaponSpawn" vtable.
             * 4b. Compare your dump's offsets with asherkin's dump's offsets to find the target function.
             * 5b. Look for the target function in the Windows binary.
             **/
            
"CWeaponSpawn::Use"
            
{
                
"library"    "server"
                "linux"        "@_ZN12CWeaponSpawn3UseEP11CBaseEntityS1_8USE_TYPEf"
                "windows"    "\x2A\x2A\x2A\x2A\x2A\x2A\x53\x57\x8B\x2A\x2A\x8B\x2A\x85\x2A\x0F\x84\x2A\x2A\x2A\x2A\x8B\x2A\x8B\x90\x2A\x2A\x2A\x2A\x8B\x2A\xFF\x2A\x84\x2A\x0F\x84\x2A\x2A\x2A\x2A\x8B"
                        
/* ? ? ? ? ? ? 53 57 8B ? ? 8B ? 85 ? 0F 84 ? ? ? ? 8B ? 8B 90 ? ? ? ? 8B ? FF ? 84 ? 0F 84 ? ? ? ? 8B */
            
}
        }
    }

I've tested both games on Windows and the patches work as intended without any crashes.
__________________
AFK like JFK
--
Donate to Motivate
--
Get updated L4D/L4D2 gamedata signatures here.
--
PM me if you need help with finding signatures and/or offsets in the following games: L4D1&2/CSGO/TF2/NMRIH/INS/DODS/CSS/HL2DM

Last edited by Psyk0tik; 09-21-2021 at 16:00.
Psyk0tik is offline
Marttt
Veteran Member
Join Date: Jan 2019
Location: Brazil
Old 09-21-2021 , 08:22   Re: [L4D1/2] Don't switch weapon to secondary slot when pick up pistols
Reply With Quote #5

I didn't test with dual pistols so I don't know if it will work
Since I don't know do patching stuff there are some workarounds like checking if the user is wielding "now" a dual weapon and rollback the active weapon aswell.

EDIT: Oh I noticed that you edited the main post after I paste the code.
__________________

Last edited by Marttt; 09-21-2021 at 11:16.
Marttt is offline
Silvers
AlliedModders Donor
Join Date: Aug 2010
Location: SpaceX
Old 09-21-2021 , 08:59   Re: [L4D1/2] Don't switch weapon to secondary slot when pick up pistols
Reply With Quote #6

So this is from Charms weapon detecting dual pistol pickup:

PHP Code:
HookEvent("player_use"Event_PlayerUse);

// Fix L4D/2 picking up second pistol, change fake VM to dual pistol model.
public void Event_PlayerUse(Event event, const char[] namebool dontBroadcast)
{
    
int client GetClientOfUserId(event.GetInt("userid"));
    if( 
client )
    {
        
int index g_iSelected[client]; // Has charm
        
if( index )
        {
            
int entity event.GetInt("targetid"); // What we're attempting to use/pickup
            
if( entity && IsValidEntity(entity) )
            {
                
GetEdictClassname(entityg_szBuffersizeof(g_szBuffer)); // Verify it's a pistol
                
if( strncmp(g_szBuffer"weapon_pistol"13) == )
                {
                    
entity GetEntPropEnt(clientProp_Data"m_hActiveWeapon");
                    if( 
GetEntProp(entityProp_Send"m_isDualWielding") ) // Are we dual wielding
                    
{
                        
GetEdictClassname(entityg_szBuffersizeof(g_szBuffer)); // Is our pistol equipped
                        
if( strcmp(g_szBuffer"weapon_pistol") == )
                        {
                            if( 
g_iEntBones[client] && EntRefToEntIndex(g_iEntBones[client]) != INVALID_ENT_REFERENCE // Do we have fake viewmodel
                            
{
                                
// Have to delete to reposition since it changes -_-
                                
DeleteCharm(client); // FIXME: Should teleport instead of delete.
                                
CreateCharm(clientindex 1);

                                
SetEntityModel(g_iEntBones[client], g_sPistol); // Fix wrong model bug
                                
SetEntityRenderMode(g_iEntBones[client], RENDER_NORMAL);
                            }
                        }
                    }
                }
            }
        }
    }

I guess you could use part of this with the "player_use" event and send: ClientCommand(client, "lastinv");

or force the weapon to change to the primary weapon if available.

I don't think patching is necessary, that creates extra overhead which must be maintained through game changes if anything were to update, and this looks like a lot of stuff to patch etc.
__________________
Silvers is offline
HarryPotter
Veteran Member
Join Date: Sep 2017
Location: TW
Old 09-21-2021 , 15:15   Re: [L4D1/2] Don't switch weapon to secondary slot when pick up pistols
Reply With Quote #7

Quote:
Originally Posted by Silvers View Post
So this is from Charms weapon detecting dual pistol pickup:
Thanks your suggestion, silvers, I tried it but that is not I want.
My goal is trying not to interrupts client status. (ex. primary weapon reloading)

PHP Code:
public void Event_PlayerUse(Event event, const char[] namebool dontBroadcast)
{
    
int client GetClientOfUserId(event.GetInt("userid"));
    if( 
IsPlayerSurvivor(client) )
    {
        
int entity event.GetInt("targetid"); // What we're attempting to use/pickup
        
if( entity && IsValidEntity(entity) )
        {
            
char g_szBuffer[16];
            
GetEdictClassname(entityg_szBuffersizeof(g_szBuffer)); // Verify it's a pistol
            
if( strncmp(g_szBuffer"weapon_pistol"13) == )
            {
                
entity GetEntPropEnt(clientProp_Data"m_hActiveWeapon");
                if( 
GetEntProp(entityProp_Send"m_isDualWielding") ) // Are we dual wielding
                
{
                    
GetEdictClassname(entityg_szBuffersizeof(g_szBuffer)); // Is our pistol equipped
                    
if( strcmp(g_szBuffer"weapon_pistol") == )
                    {
                        
//ClientCommand(client, "lastinv");

                        //int iSlot0 = GetPlayerWeaponSlot(client, 0); //get player primary weapon
                        //if(iSlot0 > 0) SetEntPropEnt(client, Prop_Send, "m_hActiveWeapon", iSlot0);
                    
}
                }
            }
        }
    }

__________________

Last edited by HarryPotter; 09-21-2021 at 15:15.
HarryPotter is offline
Forgetest
Member
Join Date: Aug 2020
Old 09-23-2021 , 03:01   Re: [L4D1/2] Don't switch weapon to secondary slot when pick up pistols
Reply With Quote #8

Quote:
Originally Posted by Psyk0tik View Post
Patching out function calls with NOPs on Windows isn't sufficient most of the time. Even if you patch out the entire instruction (which is almost always needed on Windows), it won't work most of the time and you'll have to worry about patching a lot of bytes. You can instead try patching with unconditional jumps.

If you don't know how those work:

Basically, your patch should jump from one instruction to another (while skipping any other instruction between your source and your destination). The destination of the jump depends on which part(s) of the function you want to skip over.

For example, if you want to skip the function call for CTerrorGun::Holster, you'll want to start at the first byte of that function's call instruction. Since an unconditional jump (E9 ? ? ? ?) takes 5 bytes, the length of your jump will be calculated by the following equation:

Let a1 = Address of the patch location + 5 bytes (because the patch itself takes five bytes)
Let a2 = Address of the start of the instruction after the CTerrorGun::Holster's call instruction.
Equation: a2 - a1 = JMP instruction length

Here is CTerrorGun::Holster's call instruction on Windows:
Code:
.text:103E4712 8B 10                                         mov     edx, [eax]
.text:103E4714 8B C8                                         mov     ecx, eax
.text:103E4716 8B 82 24 04 00 00                             mov     eax, [edx+424h]
.text:103E471C 6A 00                                         push    0
.text:103E471E FF D0                                         call    eax
Your patch will start at the first byte (8B). The offset for that is 172h (370). Your patch length is 5 bytes so a1 will be 103E4717 (82).

Here's the next instruction:
Code:
.text:103E4720 56                                            push    esi
.text:103E4721 8B CB                                         mov     ecx, ebx
.text:103E4723 E8 C8 F9 C5 FF                                call    sub_100440F0
Your a2 will be 103E4720. Now you can use the equation and get 9. Your patch bytes will now be \xE9\x09\x00\x00\x00.

Since I like to be consistent on both platforms and both games as much as possible, I used the unconditional jump method for Linux as well. These are my offsets and sigs:
PHP Code:
"Games"
{
    
"#default"
    
{
        
"Offsets"
        
{
            
"OS"
            
{
                
"linux"            "1"
                "windows"        "0"
            
}
        }
    }
    
"left4dead"
    
{
        
"MemPatches"
        
{
            
"EquipSecondWeapon_StopHolster"
            
{
                
"signature"        "CTerrorGun_EquipSecondWeapon"
                "linux"
                
{
                    
"offset"    "297"
                    "verify"    "\x8B"
                    "patch"        "\xE9\x0E\x00\x00\x00"
                
}
                
"windows"
                
{
                    
"offset"    "403"
                    "verify"    "\x8B"
                    "patch"        "\xE9\x09\x00\x00\x00"
                
}
            }
            
"EquipSecondWeapon_SetActiveWeapon"
            
{
                
"signature"        "CTerrorGun_EquipSecondWeapon"
                "linux"
                
{
                    
"offset"    "316"
                    "verify"    "\x89"
                    "patch"        "\xE9\x0B\x00\x00\x00"
                
}
                
"windows"
                
{
                    
"offset"    "417"
                    "verify"    "\x56"
                    "patch"        "\xE9\x03\x00\x00\x00"
                
}
            }
            
"EquipSecondWeapon_Deploy"
            
{
                
"signature"        "CTerrorGun_EquipSecondWeapon"
                "linux"
                
{
                    
"offset"    "332"
                    "verify"    "\x8B"
                    "patch"        "\xE9\x06\x00\x00\x00"
                
}
                
"windows"
                
{
                    
"offset"    "425"
                    "verify"    "\x8B"
                    "patch"        "\xE9\x07\x00\x00\x00"
                
}
            }
        }
        
"Signatures"
        
{
            
/**
             * CTerrorGun::Use(CBaseEntity *, CBaseEntity *, USE_TYPE, float)
             *
             * How to find on Windows:
             * 1. Find the function's offsets with asherkin's VTable dump.
             * 2. Use IDA to get the VTable dump for the "CTerrorGun" class on Windows.
             * 3. Compare your dump's offsets with asherkin's dump's offsets to find the target function.
             * 4. Look for the target function in the Windows binary.
             **/
            
"CTerrorGun_Use"
            
{
                
"library"    "server"
                "linux"        "@_ZN10CTerrorGun3UseEP11CBaseEntityS1_8USE_TYPEf"
                "windows"    "\x2A\x2A\x2A\x2A\x2A\x2A\x2A\x2A\x2A\x85\x2A\x8B\x2A\x0F\x84\x2A\x2A\x2A\x2A\x8B\x2A\x8B\x90\x2A\x2A\x2A\x2A\x8B\x2A\xFF\x2A\x84\x2A\x0F\x84\x2A\x2A\x2A\x2A\x8B\x2A\x8B"
                        
/* ? ? ? ? ? ? ? ? ? 85 ? 8B ? 0F 84 ? ? ? ? 8B ? 8B 90 ? ? ? ? 8B ? FF ? 84 ? 0F 84 ? ? ? ? 8B ? 8B */
            
}
            
/**
             * CTerrorGun::EquipSecondWeapon()
             *
             * How to find on Windows:
             * 1a. Search for the "Player.PickupWeapon" string.
             * 2a. One of the functions that references that string should be this function.
             *
             * 1b. Find the function's offsets with asherkin's VTable dump.
             * 2b. Use IDA to get the VTable dump for the "CTerrorGun" class on Windows.
             * 3b. Compare your dump's offsets with asherkin's dump's offsets to find the target function.
             * 4b. Look for the target function in the Windows binary.
             **/
            
"CTerrorGun_EquipSecondWeapon"
            
{
                
"library"    "server"
                "linux"        "@_ZN10CTerrorGun17EquipSecondWeaponEv"
                "windows"    "\x2A\x2A\x2A\x2A\x2A\x2A\x2A\x80\xBE\x2A\x2A\x2A\x2A\x2A\x8D\x9E\x2A\x2A\x2A\x2A\x74\x2A\x5E"
                        
/* ? ? ? ? ? ? ? 80 BE ? ? ? ? ? 8D 9E ? ? ? ? 74 ? 5E */
            
}
            
/**
             * CWeaponSpawn::Use(CBaseEntity *, CBaseEntity *, USE_TYPE, float)
             *
             * How to find on Windows:
             * 1a. Search for the "spawner_give_item" string.
             * 2a. The string can only be found in this function.
             *
             * 1b. Find the function's offsets with asherkin's VTable dump.
             * 2b. In IDA Pro, go to the ".rdata" section of the Windows binary.
             * 3b. Search for "CWeaponSpawn::`vftable'" to jump to the "CWeaponSpawn" vtable.
             * 4b. Compare your dump's offsets with asherkin's dump's offsets to find the target function.
             * 5b. Look for the target function in the Windows binary.
             **/
            
"CWeaponSpawn::Use"
            
{
                
"library"    "server"
                "linux"        "@_ZN12CWeaponSpawn3UseEP11CBaseEntityS1_8USE_TYPEf"
                "windows"    "\x2A\x2A\x2A\x2A\x2A\x2A\x2A\x2A\x2A\x85\x2A\x8B\x2A\x89\x2A\x2A\x2A\x0F\x84\x2A\x2A\x2A\x2A\x8B\x2A\x8B\x90\x2A\x2A\x2A\x2A\x8B\x2A\xFF\x2A\x84\x2A\x0F\x84\x2A\x2A\x2A\x2A\x8B"
                        
/* ? ? ? ? ? ? ? ? ? 85 ? 8B ? 89 ? ? ? 0F 84 ? ? ? ? 8B ? 8B 90 ? ? ? ? 8B ? FF ? 84 ? 0F 84 ? ? ? ? 8B */
            
}
        }
    }
    
    
"left4dead2"
    
{
        
"MemPatches"
        
{
            
"EquipSecondWeapon_StopHolster"
            
{
                
"signature"        "CTerrorGun_EquipSecondWeapon"
                "linux"
                
{
                    
"offset"    "294"
                    "verify"    "\x8B"
                    "patch"        "\xE9\x0E\x00\x00\x00"
                
}
                
"windows"
                
{
                    
"offset"    "370"
                    "verify"    "\x8B"
                    "patch"        "\xE9\x09\x00\x00\x00"
                
}
            }
            
"EquipSecondWeapon_SetActiveWeapon"
            
{
                
"signature"        "CTerrorGun_EquipSecondWeapon"
                "linux"
                
{
                    
"offset"    "313"
                    "verify"    "\x89"
                    "patch"        "\xE9\x07\x00\x00\x00"
                
}
                
"windows"
                
{
                    
"offset"    "384"
                    "verify"    "\x56"
                    "patch"        "\xE9\x03\x00\x00\x00"
                
}
            }
            
"EquipSecondWeapon_Deploy"
            
{
                
"signature"        "CTerrorGun_EquipSecondWeapon"
                "linux"
                
{
                    
"offset"    "325"
                    "verify"    "\x8B"
                    "patch"        "\xE9\x06\x00\x00\x00"
                
}
                
"windows"
                
{
                    
"offset"    "392"
                    "verify"    "\x8B"
                    "patch"        "\xE9\x07\x00\x00\x00"
                
}
            }
        }
        
        
"Signatures"
        
{
            
/**
             * CTerrorGun::Use(CBaseEntity *, CBaseEntity *, USE_TYPE, float)
             *
             * How to find on Windows:
             * 1. Find the function's offsets with asherkin's VTable dump.
             * 2. Use IDA to get the VTable dump for the "CTerrorGun" class on Windows.
             * 3. Compare your dump's offsets with asherkin's dump's offsets to find the target function.
             * 4. Look for the target function in the Windows binary.
             **/
            
"CTerrorGun_Use"
            
{
                
"library"    "server"
                "linux"        "@_ZN10CTerrorGun3UseEP11CBaseEntityS1_8USE_TYPEf"
                "windows"    "\x2A\x2A\x2A\x2A\x2A\x2A\x56\x57\x8B\x2A\x2A\x8B\x2A\x85\x2A\x0F\x84\x2A\x2A\x2A\x2A\x8B\x2A\x8B\x90\x2A\x2A\x2A\x2A\x8B\x2A\xFF\x2A\x84\x2A\x0F\x84\x2A\x2A\x2A\x2A\x8B\x2A\x8B"
                        
/* ? ? ? ? ? ? 56 57 8B ? ? 8B ? 85 ? 0F 84 ? ? ? ? 8B ? 8B 90 ? ? ? ? 8B ? FF ? 84 ? 0F 84 ? ? ? ? 8B ? 8B */
            
}
            
/**
             * CTerrorGun::EquipSecondWeapon()
             *
             * How to find on Windows:
             * 1a. Search for the "Player.PickupWeapon" string.
             * 2a. One of the functions that references that string should be this function.
             *
             * 1b. Find the function's offsets with asherkin's VTable dump.
             * 2b. Use IDA to get the VTable dump for the "CTerrorGun" class on Windows.
             * 3b. Compare your dump's offsets with asherkin's dump's offsets to find the target function.
             * 4b. Look for the target function in the Windows binary.
             **/
            
"CTerrorGun_EquipSecondWeapon"
            
{
                
"library"    "server"
                "linux"        "@_ZN10CTerrorGun17EquipSecondWeaponEv"
                "windows"    "\x2A\x2A\x2A\x2A\x2A\x2A\x56\x8B\x2A\x80\xBE\x2A\x2A\x2A\x2A\x2A\x74\x2A\x32\x2A\x5E\x8B"
                        
/* ? ? ? ? ? ? 56 8B ? 80 BE ? ? ? ? ? 74 ? 32 ? 5E 8B */
            
}
            
/**
             * CWeaponSpawn::Use(CBaseEntity *, CBaseEntity *, USE_TYPE, float)
             *
             * How to find on Windows:
             * 1a. Search for the "spawner_give_item" string.
             * 2a. The string can only be found in this function.
             *
             * 1b. Find the function's offsets with asherkin's VTable dump.
             * 2b. In IDA Pro, go to the ".rdata" section of the Windows binary.
             * 3b. Search for "CWeaponSpawn::`vftable'" to jump to the "CWeaponSpawn" vtable.
             * 4b. Compare your dump's offsets with asherkin's dump's offsets to find the target function.
             * 5b. Look for the target function in the Windows binary.
             **/
            
"CWeaponSpawn::Use"
            
{
                
"library"    "server"
                "linux"        "@_ZN12CWeaponSpawn3UseEP11CBaseEntityS1_8USE_TYPEf"
                "windows"    "\x2A\x2A\x2A\x2A\x2A\x2A\x53\x57\x8B\x2A\x2A\x8B\x2A\x85\x2A\x0F\x84\x2A\x2A\x2A\x2A\x8B\x2A\x8B\x90\x2A\x2A\x2A\x2A\x8B\x2A\xFF\x2A\x84\x2A\x0F\x84\x2A\x2A\x2A\x2A\x8B"
                        
/* ? ? ? ? ? ? 53 57 8B ? ? 8B ? 85 ? 0F 84 ? ? ? ? 8B ? 8B 90 ? ? ? ? 8B ? FF ? 84 ? 0F 84 ? ? ? ? 8B */
            
}
        }
    }

I've tested both games on Windows and the patches work as intended without any crashes.
Thanks a lot bro! You're always helpful and innovative to introduce another good method.

And thank you Marttt and Silvers for your time, but as Harry replied using events is afterwards and not perfect for this.

At first I wanted to change the pistol netprops instead, to fake client a pick-up, but it could be messy when targeting weapon_spawn. Memory patching is what I'm not actually willing for, while it should be the best choice.

Eventually I decided to make it into l4d2_pickup made by SirPlease, which originally provides functionality to block pick-up switches.

Link: https://github.com/Target5150/MoYu_S...ckup/README.md

Last edited by Forgetest; 09-23-2021 at 03:07. Reason: dead keyboard
Forgetest is online now
HarryPotter
Veteran Member
Join Date: Sep 2017
Location: TW
Old 09-23-2021 , 07:44   Re: [L4D1/2] Don't switch weapon to secondary slot when pick up pistols
Reply With Quote #9

Problem sloved by Forgetest, thanks everyone.
__________________

Last edited by HarryPotter; 09-23-2021 at 07:44.
HarryPotter is offline
yabi
Member
Join Date: Jun 2021
Location: Taiwan
Old 09-24-2021 , 00:00   Re: [L4D1/2] Don't switch weapon to secondary slot when pick up pistols
Reply With Quote #10

Can this plugin be used for teammates who come in to play the game?
yabi is offline
Reply


Thread Tools
Display Modes

Posting Rules
You may not post new threads
You may not post replies
You may not post attachments
You may not edit your posts

BB code is On
Smilies are On
[IMG] code is On
HTML code is Off

Forum Jump


All times are GMT -4. The time now is 10:31.


Powered by vBulletin®
Copyright ©2000 - 2021, vBulletin Solutions, Inc.
Theme made by Freecode