Log in

View Full Version : Manipulating PAWN strings / data from a C / C++ script



foxtacles
05-01-2013, 09:18 PM
In the IRC chat, the question came up if it was possible to manipulate data on the AMX stack with a C++ script using CallPublic and MakePublic. The answer is yes. See the below code for an example.

#include "vaultscript.h"
#include <cstdio>
#include <vector>

using namespace vaultmp;

Result from_pawn(cell* str, unsigned int len)
{
// If the string is packed, unpack it
std::vector<cell> data(len + 1);
amx_StrUnpack(&data[0], str, len);

for (char c : data)
printf("%c", c);

return static_cast<Result>(0);
}

Void VAULTSCRIPT OnServerInit() noexcept
{
MakePublic(from_pawn, "from_pawn");
}


#include <vaultmp>

public OnServerInit()
{
new message{128};
strformat(message, sizeof(message), true, "Hello from PAWN!", message);
CallPublic("from_pawn", message, strlen(message));
}


To work with packed / unpacked data, you can use these functions and declarations (copied from PAWN code) in your C / C++ script:

typedef signed long long cell;
typedef unsigned long long ucell;

#define CHARBITS (8*sizeof(char))
#define UNPACKEDMAX (((cell)1 << (sizeof(cell)-1)*8) - 1)
#define AMX_ERR_NONE 0

static int amx_StrPack(cell *dest,cell *source,int len,int offs)
{
int i;

if ((ucell)*source>UNPACKEDMAX && offs%sizeof(cell)==0) {
/* source string is already packed and the destination is cell-aligned */
unsigned char* pdest=(unsigned char*)dest+offs;
i=(len+sizeof(cell)-1)/sizeof(cell);
memmove(pdest,source,i*sizeof(cell));
/* zero-terminate */
#if BYTE_ORDER==BIG_ENDIAN
pdest+=len;
for (i=len; i==len || i%sizeof(cell)!=0; i++)
*pdest++='\0';
#else
i=(len/sizeof(cell))*sizeof(cell);
pdest+=i;
len=(len==i) ? sizeof(cell) : sizeof(cell)-(len-i);
assert(len>0 && len<=sizeof(cell));
for (i=0; i<len; i++)
*pdest++='\0';
#endif
} else if ((ucell)*source>UNPACKEDMAX) {
/* source string is packed, destination is not aligned */
cell mask,c;
dest+=offs/sizeof(cell); /* increment whole number of cells */
offs%=sizeof(cell); /* get remainder */
mask=(~(ucell)0) >> (offs*CHARBITS);
c=*dest & ~mask;
for (i=0; i<len+offs+1; i+=sizeof(cell)) {
*dest=c | ((*source >> (offs*CHARBITS)) & mask);
c=(*source << ((sizeof(cell)-offs)*CHARBITS)) & ~mask;
dest++;
source++;
} /* for */
/* set the zero byte in the last cell */
mask=(~(ucell)0) >> (((offs+len)%sizeof(cell))*CHARBITS);
*(dest-1) &= ~mask;
} else {
/* source string is unpacked: pack string, from top-down */
cell c=0;
if (offs!=0) {
/* get the last cell in "dest" and mask of the characters that must be changed */
cell mask;
dest+=offs/sizeof(cell); /* increment whole number of cells */
offs%=sizeof(cell); /* get remainder */
mask=(~(ucell)0) >> (offs*CHARBITS);
c=(*dest & ~mask) >> ((sizeof(cell)-offs)*CHARBITS);
} /* if */
/* for proper alignement, add the offset to both the starting and the ending
* criterion (so that the number of iterations stays the same)
*/
for (i=offs; i<len+offs; i++) {
c=(c<<CHARBITS) | (*source++ & 0xff);
if (i%sizeof(cell)==sizeof(cell)-1) {
*dest++=c;
c=0;
} /* if */
} /* for */
if (i%sizeof(cell) != 0) /* store remaining packed characters */
*dest=c << (sizeof(cell)-i%sizeof(cell))*CHARBITS;
else
*dest=0; /* store full cell of zeros */
} /* if */
return AMX_ERR_NONE;
}

static int amx_StrUnpack(cell *dest,cell *source,int len)
{
/* len excludes the terminating '\0' byte */
if ((ucell)*source>UNPACKEDMAX) {
/* unpack string, from bottom up (so string can be unpacked in place) */
cell c;
int i;
for (i=len-1; i>=0; i--) {
c=source[i/sizeof(cell)] >> (sizeof(cell)-i%sizeof(cell)-1)*CHARBITS;
dest[i]=c & UCHAR_MAX;
} /* for */
dest[len]=0; /* zero-terminate */
} else {
/* source string is already unpacked */
while (len-->0)
*dest++=*source++;
*dest=0;
} /* if */
return AMX_ERR_NONE;
}