About calling natives from C
#1

Well, I found out that Peter's Invoke snippet is not very efficient in speed, so I tried around with stuff like this

pawn Код:
typedef bool (*IsPlayerConnected_t)(int);
IsPlayerConnected_t pIsPlayerConnected = (IsPlayerConnected_t)0x4637E0;
bool IsPlayerConnected(int iPlayerID) { return pIsPlayerConnected(iPlayerID); }
This already works so far, but note I've made a seperate function for it since it seems to crash when I use pIsPlayerConnected directly (not quite sure why, but have an idea)
Also, my disassembly knowlege isn't good enough to see why this doesn't work for all functions (such as these that need more arguments), I was already guessing the parameter
could be an AMX cell* but this also failed.

So, if any of you is experienced enough to explain it, I will appreciate it
Reply
#2

Well here how It's done in Assembly( i tested with random and worked ):

Код:
typedef unsigned long DWORD;

bool vm_IsPlayerConnected(int playerid)
{
  DWORD
    ADDR_IsPlayerConnected = 0x4637E0;
  bool
    value = false;
  _asm
  {
    push playerid
    call ADDR_IsPlayerConnected
    mov value, eax
  }
  return value;
}
If you have more then 1 param then push it in reverse order( that's how stack works )

If you have any problem, tell...

EDIT:

NOTE: C++ standard says that return value from function must always be in "eax", I dont think C standard says same... but SAMP is written in C++ so you dont have to worry :P
Reply
#3

I think this is equivalent to what I did for, however if you try the same with e.g. SetPlayerTime (0.3a 0x463970) it will crash.
This is why I thought it could have some amx cell* as argument...
Reply
#4

Then you probably doing something wrong. Anyway try my method with SetPlayerTime.

Код:
typedef unsigned long DWORD;

void vm_SetPlayerTime(int playerid, int hour, int minute)
{
  DWORD
    ADDR_SetPlayerTime = /* address */;
  _asm
  {
    push minute
    push hour
    push playerid
    call ADDR_SetPlayerTime
  }
}
Reply
#5

This is what I tried, it crashes.

EDIT: looking at SetPlayerTime subroutine..

Код:
.text:00463970 sub_463970   proc near        ; DATA XREF: .data:004AA694o
.text:00463970
.text:00463970 arg_4      = dword ptr 8
.text:00463970
.text:00463970         mov   edx, [esp+arg_4]
.text:00463974         mov   ax, [edx+4]
.text:00463978         cmp   ax, 1F4h
.text:0046397C         jnb   short loc_463995
.text:0046397E         mov   ecx, dword_4BB07C
.text:00463984         mov   ecx, [ecx+4]
.text:00463987         movzx  eax, ax
.text:0046398A         mov   ecx, [ecx+eax*4+7D0h]
.text:00463991         test  ecx, ecx
.text:00463993         jnz   short loc_463998
Reply
#6

If that's correct then it crash because stack error. I will look into code to find type of param it needs.
Reply
#7

Thanks, ******, but could you explain me why it worked perfectly with 1 parameter for IsPlayerConnected?

EDIT: I think I got that now, perhaps SA-MP's IsPlayerConnected native does not use pAMX which gets *playerid*, and since I only tried it alone (ID 0) it also worked with that.
passing (AMX*, cell*) does work fine however, thanks
Reply
#8

A long time ago, I did this by Assembly and pointers, the pointers' way you already know, and here are the Assembly way with parameters calling too:

Код:
DWORD function = 0xEXAMPLE;
cell *params = (cell *)malloc(sizeof(cell) * FUNCTION_PARAM_COUNT + 1);
*params = FUNCTION_PARAM_COUNT * 4; //params[0] is "paramscount * 4"
cell retval;
//example calling the firat parameter as 5:
*(++params) = 5;
//etc

__asm
{
	PUSH params
	PUSH 0 //"amx" can be 0, or, if you like, use gamemode's amx, whatever...
	CALL function
	MOV retval, EAX
}
free(params);
logprintf("Return value: %d", retval);
Quote:
Originally Posted by MaVe - leeturl.de
Thanks, ******, but could you explain me why it worked perfectly with 1 parameter for IsPlayerConnected?

EDIT: I think I got that now, perhaps SA-MP's IsPlayerConnected native does not use pAMX which gets *playerid*, and since I only tried it alone (ID 0) it also worked with that.
passing (AMX*, cell*) does work fine however, thanks
Yes, it worked because you were ID 0, and parameters were invalid, so it was 0 too.
Reply
#9

These methods are essentially doing the exact same thing that Peter's function does, only his version finds the native's address automatically so that you don't have to (however, the native must be present in an AMX interface for this to work). Here is a snippet based on his code:

Код:
typedef int
	(* amx_Function_t)(AMX * amx, cell * params);

int IsPlayerConnected(int playerID)
{
	int
		amx_idx;
	amx_FindNative(pAMX, "IsPlayerConnected", &amx_idx);
	if (amx_idx != 2147483647)
	{
		AMX_HEADER
			* amx_hdr = (AMX_HEADER *)(pAMX)->base;
		unsigned int
			amx_addr = (unsigned int)((AMX_FUNCSTUB *)((char *)amx_hdr + amx_hdr->natives + amx_hdr->defsize * amx_idx))->address;
		if (!amx_addr)
		{
			return 0;
		}
		amx_Function_t
			amx_Function = (amx_Function_t)amx_addr;
		cell
			params[2];
		params[0] = 4;
		params[1] = playerID;
		return amx_Function(pAMX, params);
	}
	return 0;
}
Of course, it gets slightly more complex if you want to pass strings or values by reference (you need to allocate and free memory).

I suppose this is a question of whether you want to compromise a little speed for convenience, because remember that the addresses will change if the server is recompiled.
Reply
#10

Thank you all, I solved it but still prefer my method since the address-grabbing was easy copy/paste, so no need to get that address every time.

EDIT: if anyone wants to have the addresses, here: http://pastebin.com/f7b31f057 - Note: some of them have a "// ????" behind them, meaning i'm not sure about them since the format in disassembly was different.
Reply


Forum Jump:


Users browsing this thread: 1 Guest(s)