Since when using LTCG, internal functions often have their own call convention, you can't just call them via SDKCall on Windows, so you need to make a "translator" function that will match stdcall and call the desired function.
For example, let's take CBaseAnimating:: Sequence Duration. it returns via xmm0, the "translator" function is as follows
PHP Code:
55 push ebp 89 e5 mov ebp,esp 8b 45 0c mov eax,DWORD PTR [ebp+0xc] 50 push eax 8b 45 08 mov eax,DWORD PTR [ebp+0x8] 50 push eax b8 00 00 00 00 mov eax,0x0 ; in 0x0 write our function address which should call inside transistor func ff d0 call eax f3 0f 11 44 24 fc movss DWORD PTR [esp-0x4],xmm0 ; move value from xmm0 in stack d9 44 24 fc fld DWORD PTR [esp-0x4] ; move value from stack to FPU stack (where float value should be) 89 ec mov esp,ebp 5d pop ebp c2 08 00 ret 0x8
It remains the most important thing to implement it somehow on sourcepawn and here comes to the aid of a very good article of Kailo Blog: Working with memory in SourcePawn based on it, Phoenix (PTaH author) did this example
I also used a similar approach to call a function with fastcall convection which not clear the stack is well. The translator function pop the param from the top of the stack to ecx, edx regs and use a trampoline to call needed function.
Example: fastcall for windows from zp core
Spoiler
PHP Code:
/** * @brief Emulate bullet_shot on the server and does the damage calculations. * * @param client The client index. * @param weapon The weapon index. * @param vPosition The position to the spawn. * @param vAngle The angle to the spawn. * @param iMode The mode index. * @param iSeed The randomizing seed. * @param flInaccuracy The inaccuracy variable. * @param flSpread The spread variable. * @param flFishTail The fishtail variable. * @param iSoundType The sound type. * @param flRecoilIndex The recoil variable. **/ void ToolsFireBullets(int client, int weapon, float vPosition[3], float vAngle[3], int iMode, int iSeed, float flInaccuracy, float flSpread, float flFishTail, int iSoundType, float flRecoilIndex) { // Gets the item definition address int pItem = GetEntProp(weapon, Prop_Send, "m_Item");
// Disable the lag compensation and store the status bool bLock = view_as<bool>(GetEntProp(client, Prop_Data, "m_bLagCompensation")); SetEntProp(client, Prop_Data, "m_bLagCompensation", false);
// Validate windows if (gServerData.Platform == OS_Windows) { /** Assembler: http://shell-storm.org/online/Online-Assembler-and-Disassembler/ Callconv: https://en.wikipedia.org/wiki/X86_calling_conventions#Microsoft_fastcall
Assembly:
58 pop eax 59 pop ecx 5A pop edx 50 push eax B8 00 00 00 00 mov eax, 0x0 ; in 0x0, write the address of the "FX_FireBullets" function FF E0 jmp eax
// Generate a custom call, if is not yet created if (hSDKCallFireBullets == null) { // Find address of a signature Address pSignature; fnInitGameConfAddress(gServerData.Config, pSignature, "FX_FireBullets");
/// Create a memory for the trampoline pFireBullets = fnCreateMemoryForSDKCall();
// Starts the preparation of an SDK call StartPrepSDKCall(SDKCall_Static); PrepSDKCall_SetAddress(pFireBullets);
// Adds a parameter to the calling convention. This should be called in normal ascending order PrepSDKCall_AddParameter(SDKType_PlainOldData, SDKPass_Plain); PrepSDKCall_AddParameter(SDKType_CBaseEntity, SDKPass_Pointer); PrepSDKCall_AddParameter(SDKType_PlainOldData, SDKPass_Plain); PrepSDKCall_AddParameter(SDKType_Vector, SDKPass_ByRef); PrepSDKCall_AddParameter(SDKType_QAngle, SDKPass_ByRef); PrepSDKCall_AddParameter(SDKType_PlainOldData, SDKPass_Plain); PrepSDKCall_AddParameter(SDKType_PlainOldData, SDKPass_Plain); PrepSDKCall_AddParameter(SDKType_Float, SDKPass_Plain); PrepSDKCall_AddParameter(SDKType_Float, SDKPass_Plain); PrepSDKCall_AddParameter(SDKType_Float, SDKPass_Plain); PrepSDKCall_AddParameter(SDKType_Float, SDKPass_Plain); PrepSDKCall_AddParameter(SDKType_PlainOldData, SDKPass_Plain); PrepSDKCall_AddParameter(SDKType_Float, SDKPass_Plain);