REDIVIVUS

Proposals / #993

NET_D2GS_SERVER_Send_CMD_BYTE_INT's nArg1 is an int: callers push a unit's whole type dword

approved · by Niehztog · submitted 2026-11-02, approved 2026-11-02

nArg1 (Stack[0x4]) is declared byte, but the callers push a full dword, typically a unit's type read whole from offset 0: CLIENT_SendHandshakePacket0x0B 0053794f MOV ECX,dword ptr [EAX] / 00537955 PUSH ECX (call 0053795b); SKILLITEM_TownPortalScrollSpell 005be3a5 MOV EAX,dword ptr [EAX] / 005be3a8 PUSH EAX (call 005be3be); PLAYER_SendHoverTextToClient 00571718 MOV ESI,dword ptr [ESI] / 0057171b PUSH ESI (call 00571720). With a byte parameter VC8 narrows each argument at the call, so the parameter was an int; the body storing the low byte into the packet (0053b3d6 MOV AL,byte ptr [EBP+0x8]; 0053b3da MOV byte ptr [EBP-0x7],AL) is what VC8 makes of an int stored into a byte field. Recompiled with VC8 SP1 (oracle, tree 3dba660), callers come closer: PLAYER_SendHoverTextToClient OUR+2 0.733 -> OUR+1 0.760, SKILLITEM_TownPortalScrollSpell OUR+2 0.953 -> OUR+1 0.965, ITEMMODE_UseItemOnItem OUR+5 -> OUR+2; no function lost.

Changes 1, applied together

  1. Set prototype win 0x0053b3d0

    signaturevoid NET_D2GS_SERVER_Send_CMD_BYTE_INT(D2ClientStrc * pClient, byte nPacketId, byte nArg1, int32_t nArg2)→void NET_D2GS_SERVER_Send_CMD_BYTE_INT(D2ClientStrc * pClient, byte nPacketId, int nArg1, int32_t nArg2)
    convention__fastcall