diff --git a/server/amx/amxDGram.c b/server/amx/amxDGram.c new file mode 100644 index 0000000..d988f3a --- /dev/null +++ b/server/amx/amxDGram.c @@ -0,0 +1,351 @@ +/* Datagram sending/receiving module for the Pawn Abstract Machine + * + * This module uses the UDP protocol (from the TCP/IP protocol suite). + * + * Copyright (c) ITB CompuPhase, 2005 + * + * This software is provided "as-is", without any express or implied warranty. + * In no event will the authors be held liable for any damages arising from + * the use of this software. + * + * Permission is granted to anyone to use this software for any purpose, + * including commercial applications, and to alter it and redistribute it + * freely, subject to the following restrictions: + * + * 1. The origin of this software must not be misrepresented; you must not + * claim that you wrote the original software. If you use this software in + * a product, an acknowledgment in the product documentation would be + * appreciated but is not required. + * 2. Altered source versions must be plainly marked as such, and must not be + * misrepresented as being the original software. + * 3. This notice may not be removed or altered from any source distribution. + * + * Version: $Id: amxDGram.c 3367 2005-07-24 12:25:04Z thiadmer $ + */ +#include +#include +#if defined LINUX + #include + #include + #include + #include + #include + #include +#else + #include + #include +#endif +#include "osdefs.h" +#include "amx.h" + + +#define SRC_BUFSIZE 22 +#define BUFLEN 512 +#define AMX_DGRAMPORT 9930 /* default port */ + +static int sLocal; + +static unsigned long udp_GetHostAddr(const char *host,int index) +{ + unsigned long addr=inet_addr(host); + if (addr==0xffffffffL) { + struct hostent FAR *phost=gethostbyname(host); + if (phost!=NULL) { + /* count the number of addresses in the list */ + int count; + for (count=0; phost->h_addr_list[count]!=0; count++) + /* nothing */; + if (indexh_addr_list[index]; + } /* if */ + } /* if */ + return addr; +} + +static int udp_Open(void) +{ +#if defined __WIN32 || defined _WIN32 || defined WIN32 + WORD wVersionRequested = MAKEWORD(1,1); + WSADATA wsaData; +#endif + int optval = 1; + + #if defined __WIN32 || defined _WIN32 || defined WIN32 + WSAStartup(wVersionRequested, &wsaData); + #endif + + if ((sLocal=socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP)) == -1) + return -1; + + if (setsockopt(sLocal, SOL_SOCKET, SO_BROADCAST, (void*)&optval, sizeof optval) == -1) + return -1; + + return sLocal; +} + +static int udp_Close(void) +{ + if (sLocal>=0) { + #if defined __WIN32 || defined _WIN32 || defined WIN32 + closesocket(sLocal); + #else + close(sLocal); + #endif + } /* if */ + + #if defined __WIN32 || defined _WIN32 || defined WIN32 + WSACleanup(); + #endif + + return 0; +} + +static int udp_Send(const char *host,short port,const char *message,int size) +{ + struct sockaddr_in sRemote; + + if (sLocal<0) + return -1; + + memset((void *)&sRemote,sizeof sRemote,0); + sRemote.sin_family=AF_INET; + sRemote.sin_port=htons(port); + sRemote.sin_addr.s_addr= (host==NULL) ? htonl(INADDR_BROADCAST) : udp_GetHostAddr(host,0); + + if (sendto(sLocal,message,size,0,(struct sockaddr *)&sRemote,sizeof sRemote)==-1) + return -1; + + return size; +} + +/* This call is blocking + * if source is not NULL, it must point to a buffer that can contain at least + * 22 characters. + */ +static int udp_Receive(char *message,size_t maxmsg,char *source) +{ + struct sockaddr_in sSource; + int slen=sizeof(sSource); + int size; + + size=recvfrom(sLocal, message, maxmsg, 0, (struct sockaddr *)&sSource, &slen); + if (size==-1) + return -1; + if (source!=NULL) + sprintf(source, "%s:%d", inet_ntoa(sSource.sin_addr), ntohs(sSource.sin_port)); + + return size; +} + +static int udp_IsPacket(void) +{ + int result; + fd_set rdset; + struct timeval time; + + /* the select() function waits until the socket can be read, or until a + * time-out occurs; the time-out is set to 1 microsecond (the shortest + * delay possible). + */ + time.tv_sec=0; + time.tv_usec=1; + FD_ZERO(&rdset); + FD_SET(sLocal,&rdset); + result=select(0,&rdset,NULL,NULL,&time); + if (result==SOCKET_ERROR) + return -1; + + return result != 0; +} + +static int udp_Listen(short port) +{ + struct sockaddr_in sFrom; + + memset((void *)&sFrom,sizeof sFrom,0); + sFrom.sin_family=AF_INET; + sFrom.sin_port=htons(port); + sFrom.sin_addr.s_addr=htonl(INADDR_ANY); + if (bind(sLocal,(struct sockaddr *)&sFrom,sizeof sFrom)==-1) + return -1; + + return 0; +} + +static AMX_DEBUG PrevIdle = NULL; +static int idxReceiveString = -1; +static int idxReceivePacket = -1; +static short dgramPort = 0; +static int dgramBound = 0; + +/* sendstring(const message[], const destination[]="") + * destination has the format "127.0.0.1:9930"; when set to an empty string, + * a broadcast is sent. + * To mark the text as a "string", the function inserts a "byte order mark" in + * front of it. It does this for Extended ASCII strings too, although this is + * not entirely correct. + * Returns true on success, false on failure. + */ +static cell AMX_NATIVE_CALL n_sendstring(AMX *amx, cell *params) +{ + int r = 0, length; + cell *cstr; + char *host, *message, *ptr; + short port=AMX_DGRAMPORT; + + amx_GetAddr(amx, params[1], &cstr); + amx_UTF8Len(cstr, &length); + + if ((message = alloca(length + 3 + 1)) != NULL) { + /* insert the byte order mark (BOM) */ + message[0]='\xef'; + message[1]='\xbb'; + message[2]='\xbf'; + /* if this is a wide string, convert it to UTF-8 */ + if ((ucell)*cstr<=UNPACKEDMAX) { + ptr=message+3; + while (*cstr!=0) + amx_UTF8Put(ptr, &ptr, length - (ptr-message), *cstr++); + *ptr='\0'; + } else { + amx_GetString(message+3, cstr, 0, UNLIMITED); + } /* if */ + + amx_StrParam(amx, params[2], host); + if (host != NULL && (ptr=strchr(host,':'))!=NULL && isdigit(ptr[1])) { + *ptr++='\0'; + port=(short)atoi(ptr); + } /* if */ + r= (udp_Send(host,port,message,strlen(message)+1) > 0); + } /* if */ + + return r; +} + +/* sendpacket(const packet[], size, const destination[]="") + * destination has the format "127.0.0.1:9930"; when set to an empty string, + * a broadcast is sent. + * Returns true on success, false on failure. + */ +static cell AMX_NATIVE_CALL n_sendpacket(AMX *amx, cell *params) +{ + cell *cstr; + char *host, *ptr; + short port=AMX_DGRAMPORT; + + amx_GetAddr(amx, params[1], &cstr); + amx_StrParam(amx, params[3], host); + if (host != NULL && (ptr=strchr(host,':'))!=NULL && isdigit(ptr[1])) { + *ptr++='\0'; + port=(short)atoi(ptr); + } /* if */ + return (udp_Send(host,port,(const char *)cstr,params[2] * sizeof(cell)) > 0); +} + +/* listenport(port) + * A program must call this function from main() or another start-up function + * because the module will use the default port 9930 otherwise. + */ +static cell AMX_NATIVE_CALL n_listenport(AMX *amx, cell *params) +{ + (void)amx; + dgramPort = (short)params[1]; + return 0; +} + +static int AMXAPI amx_DGramIdle(AMX *amx) +{ + char message[BUFLEN], source[SRC_BUFSIZE]; + cell amx_addr_msg, amx_addr_src; + int len, chars; + int err=0; + + assert(idxReceiveString >= 0 || idxReceivePacket >= 0); + + if (PrevIdle != NULL) + PrevIdle(amx); + + /* set up listener (first call only) */ + if (!dgramBound) { + if (dgramPort==0) + dgramPort=AMX_DGRAMPORT; /* use default port if none was set */ + if (udp_Listen(dgramPort)==-1) + return AMX_ERR_GENERAL; + dgramBound=1; + } /* if */ + + if (udp_IsPacket()) { + len=udp_Receive(message, sizeof message / sizeof message[0], source); + amx_PushString(amx,&amx_addr_src,NULL,source,1,0); + /* check the presence of a byte order mark: if it is absent, the received + * packet is no string; also check the packet size against string length + */ + if ((message[0]!='\xef' || message[1]!='\xbb' || message[2]!='\xbf' + || len!=(int)strlen(message)+1 || idxReceiveString<0) && idxReceivePacket>=0) + { + /* receive as "packet" */ + amx_Push(amx,len); + amx_PushArray(amx,&amx_addr_msg,NULL,(cell*)message,len); + err=amx_Exec(amx,NULL,idxReceivePacket); + } else { + const char *msg=message; + if (msg[0]=='\xef' && msg[1]=='\xbb' && msg[2]=='\xbf') + msg+=3; /* skip BOM */ + /* optionally convert from UTF-8 to a wide string */ + if (amx_UTF8Check(msg,&chars)==AMX_ERR_NONE) { + cell *array=alloca((chars+1)*sizeof(cell)); + cell *ptr=array; + if (array!=NULL) { + while (err==AMX_ERR_NONE && *msg!='\0') + amx_UTF8Get(msg,&msg,ptr++); + *ptr=0; /* zero-terminate */ + amx_PushArray(amx,&amx_addr_msg,NULL,array,chars+1); + } /* if */ + } else { + amx_PushString(amx,&amx_addr_msg,NULL,msg,1,0); + } /* if */ + err=amx_Exec(amx,NULL,idxReceiveString); + } /* if */ + while (err==AMX_ERR_SLEEP) + err=amx_Exec(amx,NULL,AMX_EXEC_CONT); + amx_Release(amx,amx_addr); + amx_Release(amx,amx_addr_src); + } /* if */ + + return err; +} + +#if defined __cplusplus + extern "C" +#endif +AMX_NATIVE_INFO dgram_Natives[] = { + { "sendstring", n_sendstring }, + { "sendpacket", n_sendpacket }, + { "listenport", n_listenport }, + { NULL, NULL } /* terminator */ +}; + +int AMXEXPORT amx_DGramInit(AMX *amx) +{ + dgramBound = 0; + if (udp_Open()==-1) + return AMX_ERR_GENERAL; + + /* see whether there is an @receivestring() function */ + if (amx_FindPublic(amx,"@receivestring",&idxReceiveString)==AMX_ERR_NONE + || amx_FindPublic(amx,"@receivepacket",&idxReceivePacket)==AMX_ERR_NONE) + { + if (amx_GetUserData(amx,AMX_USERTAG('I','d','l','e'),(void**)&PrevIdle)!=AMX_ERR_NONE) + PrevIdle=NULL; + amx_SetUserData(amx,AMX_USERTAG('I','d','l','e'),amx_DGramIdle); + } /* if */ + + return amx_Register(amx,dgram_Natives,-1); +} + +int AMXEXPORT amx_DGramCleanup(AMX *amx) +{ + (void)amx; + udp_Close(); + return AMX_ERR_NONE; +} diff --git a/server/amx/amxcons.c b/server/amx/amxcons.c new file mode 100644 index 0000000..51fd36d --- /dev/null +++ b/server/amx/amxcons.c @@ -0,0 +1,1159 @@ +/* Console output module (terminal I/O) for the Pawn AMX + * + * Since some of these routines go further than those of standard C, they + * cannot always be implemented with portable C functions. In other words, + * these routines must be ported to other environments. + * + * Copyright (c) ITB CompuPhase, 1997-2005 + * + * This software is provided "as-is", without any express or implied warranty. + * In no event will the authors be held liable for any damages arising from + * the use of this software. + * + * Permission is granted to anyone to use this software for any purpose, + * including commercial applications, and to alter it and redistribute it + * freely, subject to the following restrictions: + * + * 1. The origin of this software must not be misrepresented; you must not + * claim that you wrote the original software. If you use this software in + * a product, an acknowledgment in the product documentation would be + * appreciated but is not required. + * 2. Altered source versions must be plainly marked as such, and must not be + * misrepresented as being the original software. + * 3. This notice may not be removed or altered from any source distribution. + * + * Version: $Id: amxcons.c 3363 2005-07-23 09:03:29Z thiadmer $ + */ + +#if defined _UNICODE || defined __UNICODE__ || defined UNICODE +# if !defined UNICODE /* for Windows */ +# define UNICODE +# endif +# if !defined _UNICODE /* for C library */ +# define _UNICODE +# endif +#endif + +#include +#include +#include +#include +#include +#if defined __WIN32__ || defined _WIN32 || defined WIN32 || defined __MSDOS__ + #include + #include +#endif +#if defined USE_CURSES + #include +#endif +#include "amx.h" +#if defined __WIN32__ || defined _WIN32 || defined WIN32 || defined __MSDOS__ + #include +#endif + +#if defined _UNICODE +# include +#elif !defined __T + typedef char TCHAR; +# define __T(string) string +# define _tcschr strchr +# define _tcscpy strcpy +# define _tcsdup strdup +# define _tcslen strlen +# define _stprintf sprintf +#endif + +#if defined __MSDOS__ + #define EOL_CHAR '\r' +#endif +#if defined __WIN32__ || defined _WIN32 || defined WIN32 + #define EOL_CHAR '\r' +#endif +#if !defined EOL_CHAR + /* if not a "known" operating system, assume Linux/Unix */ + #define EOL_CHAR '\n' +#endif + +#if defined AMX_TERMINAL + /* required functions are implemented elsewhere */ + int amx_putstr(TCHAR *); + int amx_putchar(int); + int amx_fflush(void); + int amx_getch(void); + TCHAR *amx_gets(TCHAR *,int); + int amx_termctl(int,int); + void amx_clrscr(void); + void amx_clreol(void); + void amx_gotoxy(int x,int y); + void amx_wherexy(int *x,int *y); + unsigned int amx_setattr(int foregr,int backgr,int highlight); + void amx_console(int columns, int lines, int flags); +#elif defined VT100 || defined LINUX || defined ANSITERM + /* ANSI/VT100 terminal, or shell emulating "xterm" */ + #define amx_putstr(s) printf("%s",(s)) + #define amx_putchar(c) putchar(c) + #define amx_fflush() fflush(stdout) + #define amx_getch() getch() + #define amx_gets(s,n) fgets(s,n,stdin) + + int amx_termctl(int code,int value) + { + switch (code) { + case 0: /* query terminal support */ + return 1; + + case 1: /* switch "auto-wrap" on or off */ + if (value) + amx_putstr("\033[?7h"); /* enable "auto-wrap" */ + else + amx_putstr("\033[?7l"); /* disable "auto-wrap" */ + return 1; + + #if 0 + /* next to swapping buffers, more information should be saved and swapped, + * such as the cursor position and the current terminal attributes + */ + case 2: /* swap console buffers */ + amx_fflush(); + if (value==1) { + amx_putstr("\033[?47h"); + } else { + amx_putstr("\033[?47l"); + } /* if */ + amx_fflush(); + return 1; + #endif + + case 3: /* set bold/highlighted font */ + return 0; + + default: + return 0; + } /* switch */ + } + void amx_clrscr(void) + { + amx_putstr("\033[2J"); + amx_fflush(); /* pump through the terminal codes */ + } + void amx_clreol(void) + { + amx_putstr("\033[K"); + amx_fflush(); /* pump through the terminal codes */ + } + void amx_gotoxy(int x,int y) + { + char str[30]; + _stprintf(str,"\033[%d;%dH",y,x); + amx_putstr(str); + amx_fflush(); /* pump through the terminal codes */ + } + void amx_wherexy(int *x,int *y) + { + int val,i; + char str[10]; + + assert(x!=NULL && y!=NULL); + amx_putstr("\033[6n"); + amx_fflush(); + while (amx_getch()!='\033') + /* nothing */; + val=amx_getch(); + assert(val=='['); + for (i=0; i<8 && (val=amx_getch())!=';'; i++) + str[i]=(char)val; + str[i]='\0'; + *y=atoi(str); + for (i=0; i<8 && (val=amx_getch())!='R'; i++) + str[i]=(char)val; + str[i]='\0'; + *x=atoi(str); + #if defined ANSITERM + val=amx_getch(); + assert(val=='\r'); /* ANSI driver adds CR to the end of the command */ + #endif + } + unsigned int amx_setattr(int foregr,int backgr,int highlight) + { + static short current=(0 << 8) | 7; + short prev = current; + char str[30]; + + if (foregr>=0) { + _stprintf(str,"\x1b[%dm",foregr+30); + amx_putstr(str); + current=(current & 0xff00) | (foregr & 0x0f); + } /* if */ + if (backgr>=0) { + _stprintf(str,"\x1b[%dm",backgr+40); + amx_putstr(str); + current=(current & 0x00ff) | ((backgr & 0x0f) << 8); + } /* if */ + if (highlight>=0) { + _stprintf(str,"\x1b[%dm",highlight); + amx_putstr(str); + current=(current & 0x7fff) | ((highlight & 0x01) << 15); + } /* if */ + return prev; + } + void amx_console(int columns, int lines, int flags) + { + char str[30]; + + (void)flags; + /* There is no ANSI code (or VT100/VT220) to set the size of the console + * (indeed, the terminal was that of the alphanumeric display). In xterm (a + * terminal emulator) we can set the terminal size though, and most + * terminals that in use today are in fact emulators. + * Putty understands this code too, by many others do not. + */ + sprintf(str,"\033[8;%d;%dt",lines,columns); + amx_putstr(str); + amx_fflush(); + } +#elif defined __WIN32__ || defined _WIN32 || defined WIN32 + /* Win32 console */ + #define amx_putstr(s) printf("%s",(s)) + #define amx_putchar(c) putchar(c) + #define amx_fflush() fflush(stdout) + #define amx_getch() getch() + #define amx_gets(s,n) fgets(s,n,stdin) + + int amx_termctl(int code,int value) + { + switch (code) { + case 0: /* query terminal support */ + return 1; + + case 1: { /* switch auto-wrap on/off */ + /* only works in Windows 2000/XP */ + HANDLE hConsole=GetStdHandle(STD_OUTPUT_HANDLE); + DWORD Flags=ENABLE_PROCESSED_OUTPUT; + if (value) + Flags |= ENABLE_WRAP_AT_EOL_OUTPUT; + SetConsoleMode(hConsole,Flags); + return 1; + } /* case */ + + /* case 2: */ /* create/switch to another console */ + /* case 3: */ /* set emphasized font */ + /* case 4: */ /* query whether a terminal is "open" */ + default: + return 0; + } /* switch */ + } + void amx_clrscr(void) + { + COORD coordScreen={0,0}; + DWORD cCharsWritten; + CONSOLE_SCREEN_BUFFER_INFO csbi; + DWORD dwConSize; + HANDLE hConsole=GetStdHandle(STD_OUTPUT_HANDLE); + + amx_fflush(); /* make sure the I/O buffer is empty */ + GetConsoleScreenBufferInfo(hConsole,&csbi); + dwConSize=csbi.dwSize.X*csbi.dwSize.Y; + FillConsoleOutputCharacter(hConsole,' ',dwConSize,coordScreen,&cCharsWritten); + FillConsoleOutputAttribute(hConsole,csbi.wAttributes,dwConSize,coordScreen, &cCharsWritten); + SetConsoleCursorPosition(hConsole,coordScreen); + } + void amx_clreol(void) + { + DWORD cCharsWritten; + CONSOLE_SCREEN_BUFFER_INFO csbi; + DWORD dwConSize; + HANDLE hConsole=GetStdHandle(STD_OUTPUT_HANDLE); + + amx_fflush(); /* make sure all output is written */ + GetConsoleScreenBufferInfo(hConsole,&csbi); + dwConSize=csbi.dwSize.X - csbi.dwCursorPosition.X; + FillConsoleOutputCharacter(hConsole,' ',dwConSize,csbi.dwCursorPosition,&cCharsWritten); + FillConsoleOutputAttribute(hConsole,csbi.wAttributes,dwConSize,csbi.dwCursorPosition,&cCharsWritten); + } + void amx_gotoxy(int x,int y) + { + COORD point; + CONSOLE_SCREEN_BUFFER_INFO csbi; + HANDLE hConsole=GetStdHandle(STD_OUTPUT_HANDLE); + + GetConsoleScreenBufferInfo(hConsole, &csbi); + if (x<=0 || x>csbi.dwSize.X || y<=0 || y>csbi.dwSize.Y) + return; + amx_fflush(); /* make sure all output is written */ + point.X=(short)(x-1); + point.Y=(short)(y-1); + SetConsoleCursorPosition(hConsole,point); + } + void amx_wherexy(int *x,int *y) + { + CONSOLE_SCREEN_BUFFER_INFO csbi; + amx_fflush(); /* make sure all output is written */ + GetConsoleScreenBufferInfo(GetStdHandle(STD_OUTPUT_HANDLE), &csbi); + if (x!=NULL) + *x=csbi.dwCursorPosition.X+1; + if (y!=NULL) + *y=csbi.dwCursorPosition.Y+1; + } + unsigned int amx_setattr(int foregr,int backgr,int highlight) + { + static int ansi_colours[] = { 0, 4, 2, 6, 1, 5, 3, 7 }; + CONSOLE_SCREEN_BUFFER_INFO csbi; + int f,b,h,prev; + HANDLE hConsole=GetStdHandle(STD_OUTPUT_HANDLE); + + amx_fflush(); /* make sure all output is written */ + GetConsoleScreenBufferInfo(hConsole,&csbi); + f=csbi.wAttributes & 0x07; + b=(csbi.wAttributes >> 4) & 0x0f; + h=(csbi.wAttributes & 0x08) ? 1 : 0; + prev=(b << 8) | f | (h << 15); + if (foregr>=0 && foregr<8) + f=ansi_colours[foregr]; + if (backgr>=0 && backgr<8) + b=ansi_colours[backgr]; + if (highlight>=0) + h=highlight!=0; + SetConsoleTextAttribute(hConsole, (WORD)((b << 4) | f | (h << 3))); + return prev; + } + void amx_console(int columns, int lines, int flags) + { + SMALL_RECT rect; + COORD dwSize; + (void)flags; + dwSize.X=(short)columns; + dwSize.Y=(short)lines; + SetConsoleScreenBufferSize(GetStdHandle(STD_OUTPUT_HANDLE),dwSize); + rect.Left=0; + rect.Top=0; + rect.Right=(short)(columns-1); + rect.Bottom=(short)(lines-1); + SetConsoleWindowInfo(GetStdHandle(STD_OUTPUT_HANDLE),TRUE,&rect); + } +#elif defined(USE_CURSES) + /* Use the "curses" library to implement the console */ + #define amx_putstr(s) printw("%s",(s)) + #define amx_putchar(c) addch(c) + #define amx_fflush() (0) + #define amx_getch() getch() + #define amx_gets(s,n) getnstr(s,n) + #define amx_clrscr() clear() + #define amx_clreol() clrtoeol() + #define amx_gotoxy(x,y) (void)(0) + #define amx_wherexy(x,y) (*(x)=*(y)=0) + #define amx_setattr(c,b,h) (0) + #define amx_termsup(c,v) (0) + #define amx_console(c,l,f) (void)(0) +#else + /* assume a streaming terminal; limited features (no colour, no cursor + * control) + */ + #define amx_putstr(s) printf("%s",(s)) + #define amx_putchar(c) putchar(c) + #define amx_fflush() fflush(stdout) + #define amx_getch() getch() + #define amx_gets(s,n) fgets(s,n,stdin) + #define amx_clrscr() (void)(0) + #define amx_clreol() (void)(0) + #define amx_gotoxy(x,y) (void)(0) + #define amx_wherexy(x,y) (*(x)=*(y)=0) + #define amx_setattr(c,b,h) (0) + #define amx_termsup(c,v) (0) + #define amx_console(c,l,f) (void)(0) +#endif + +#if !defined AMX_TERMINAL && (defined __WIN32__ || defined _WIN32 || defined WIN32) + void CreateConsole(void) + { static int createdconsole=0; + if (!createdconsole) { + AllocConsole(); + createdconsole=1; + } /* if */ + } +#elif defined(USE_CURSES) + // The Mac OS X build variant uses curses. + void CreateConsole(void) + { static int createdconsole=0; + if (!createdconsole) { + initscr(); + cbreak(); + noecho(); + nonl(); + intrflush(stdscr, FALSE); + keypad(stdscr, TRUE); + + createdconsole=1; + } /* if */ + } +#else + #define CreateConsole() +#endif + +static int printstring(AMX *amx,cell *cstr,cell *params,int num); + +enum { + SV_DECIMAL, + SV_HEX +}; + +static TCHAR *reverse(TCHAR *string,int stop) +{ + int start=0; + TCHAR temp; + + /* swap the string */ + stop--; /* avoid swapping the '\0' byte to the first position */ + while (stop - start > 0) { + temp = string[start]; + string[start] = string[stop]; + string[stop] = temp; + start++; + stop--; + } /* while */ + return string; +} + +/* Converts an integral value to a string, with optional padding with spaces or + * zeros. + * The "format" must be decimal or hexadecimal + * The number is right-aligned in the field with the size of the absolute value + * of the "width" parameter. + * If the width value is positive, the string is padded with spaces; if it is + * negative, it is padded with zeros. + */ +static TCHAR *strval(TCHAR buffer[], long value, int format, int width) +{ + int start, stop; + TCHAR temp; + + start = stop = 0; + if (format == SV_DECIMAL) { + if (value < 0) { + buffer[0] = __T('-'); + start = stop = 1; + value = -value; + } /* if */ + do { + buffer[stop++] = (TCHAR)((value % 10) + __T('0')); + value /= 10; + } while (value > 0); + } else { + /* hexadecimal */ + unsigned long v = (unsigned long)value; /* copy to unsigned value for shifting */ + do { + buffer[stop] = (TCHAR)((v & 0x0f) + __T('0')); + if (buffer[stop] > __T('9')) + buffer[stop] += (TCHAR)(__T('A') - __T('0') - 10); + v >>= 4; + stop++; + } while (v != 0); + } /* if */ + + /* pad to given width */ + if (width < 0) { + temp = __T('0'); + width = -width; + } else { + temp = __T(' '); + } /* if */ + while (stop < width) + buffer[stop++] = temp; + + buffer[stop] = __T('\0'); + + /* swap the string, and we are done */ + reverse(buffer+start,stop-start); + return buffer; +} + +#if defined FIXEDPOINT + #define FIXEDMULT 1000 + #define FIXEDDIGITS 3 + +static TCHAR *formatfixed(TCHAR *string,cell value,char align,int width,char decpoint,int digits) +{ + int i, len; + cell ipart,v; + TCHAR vsign=__T('\0'); + + /* make the value positive (but keep the sign) */ + if (value<0) { + value=-value; + vsign='-'; + } /* if */ + + /* "prepare" the value so that when it is truncated to the requested + * number of digits, the result is rounded towards the dropped digits + */ + v=FIXEDMULT/2; + for (i=0; i=0); + assert(value>=0); + + /* truncate the fractional part to the requested number of digits */ + for (i=FIXEDDIGITS; i>digits; i--) + value/=10; + + string[0]='\0'; + + /* add sign */ + i=_tcslen(string); + string[i]=vsign; + string[i+1]='\0'; + + /* add integer part */ + strval(string+_tcslen(string),(long)ipart,SV_DECIMAL,0); + + /* add fractional part */ + if (digits>0) { + i=_tcslen(string); + string[i]=decpoint; + strval(string+i+1,(long)value,SV_DECIMAL,-digits); + } /* if */ + + len=_tcslen(string); + if (len=len); + reverse(string,len); + reverse(string+len,i-len); + reverse(string,i); + } /* if */ + } /* if */ + + return string; +} +#endif + +static int dochar(AMX *amx,TCHAR ch,cell param,char sign,char decpoint,int width,int digits) +{ + cell *cptr; + TCHAR buffer[40]; + #if defined FLOATPOINT + TCHAR formatstring[40]; + #endif + + #if !defined FIXEDPOINT && !defined FLOATPOINT + (void)decpoint; + (void)digits; + #endif + + switch (ch) { + case '%': + amx_putchar(ch); + return 0; + + case 'c': + amx_GetAddr(amx,param,&cptr); + width--; /* single character itself has a with of 1 */ + if (sign!='-') + while (width-->0) + amx_putchar(__T(' ')); + amx_putchar((TCHAR)*cptr); + while (width-->0) + amx_putchar(__T(' ')); + return 1; + + case 'd': { + cell value; + int length=1; + amx_GetAddr(amx,param,&cptr); + value=*cptr; + if (value<0 || sign=='+') { + length++; + value=-value; + } /* if */ + while (value>=10) { + length++; + value/=10; + } /* while */ + width-=length; + if (sign!='-') + while (width-->0) + amx_putchar(__T(' ')); + strval(buffer,*cptr,SV_DECIMAL,0); + amx_putstr(buffer); + while (width-->0) + amx_putchar(__T(' ')); + return 1; + } /* case */ + +#if defined FLOATPOINT + case 'f': /* 32-bit floating point number */ + case 'r': /* if floating point is enabled, %r == %f */ + /* build a format string */ + _tcscpy(formatstring,__T("%")); + if (sign!='\0') + _stprintf(formatstring+_tcslen(formatstring),__T("%c"),sign); + if (width>0) + _stprintf(formatstring+_tcslen(formatstring),__T("%d"),width); + _stprintf(formatstring+_tcslen(formatstring),__T(".%df"),digits); + /* ??? decimal comma? */ + amx_GetAddr(amx,param,&cptr); + _stprintf(buffer,formatstring,*(float*)cptr); + amx_putstr(buffer); + return 1; +#endif + +#if defined FIXEDPOINT + #define FIXEDMULT 1000 + case 'q': /* 32-bit fixed point number */ +#if !defined FLOATPOINT + case 'r': /* if fixed point is enabled, and floating point is not, %r == %q */ +#endif + amx_GetAddr(amx,param,&cptr); + /* format the number */ + formatfixed(buffer,*cptr,sign,width,decpoint,digits); + assert(_tcslen(buffer)=0x10) { + length++; + value>>=4; + } /* while */ + width-=length; + if (sign!='-') + while (width-->0) + amx_putchar(__T(' ')); + strval(buffer,(long)*cptr,SV_HEX,0); + amx_putstr(buffer); + while (width-->0) + amx_putchar(__T(' ')); + return 1; + } /* case */ + + } /* switch */ + /* error in the string format, try to repair */ + amx_putchar(ch); + return 0; +} + +enum { + FMT_NONE, /* not in format state; accept '%' */ + FMT_START, /* found '%', accept '+', ' ' (START), digit (WIDTH), '.' (DECIM) or format letter (done) */ + FMT_WIDTH, /* found digit after '%' or sign, accept digit (WIDTH), '.' (DECIM) or format letter (done) */ + FMT_DECIM, /* found digit after '.', accept accept digit (DECIM) or format letter (done) */ +}; + +static int formatstate(TCHAR c,int *state,char *sign,char *decpoint,int *width,int *digits) +{ + switch (*state) { + case FMT_NONE: + if (c=='%') { + *state=FMT_START; + *sign='\0'; + *decpoint='.'; + *width=0; + *digits=3; + } else { + return -1; /* print a single character */ + } /* if */ + break; + case FMT_START: + if (c=='+' || c==' ') { + *sign=' '; + } else if (c>='0' && c<='9') { + *width=(int)(c-'0'); + *state=FMT_WIDTH; + } else if (c=='.' || c==',') { + *decpoint=c; + *digits=0; + *state=FMT_DECIM; + } else { + return 1; /* print formatted character */ + } /* if */ + break; + case FMT_WIDTH: + if (c>='0' && c<='9') { + *width=*width*10+(int)(c-'0'); + } else if (c=='.' || c==',') { + *decpoint=c; + *digits=0; + *state=FMT_DECIM; + } else { + return 1; /* print formatted character */ + } /* if */ + break; + case FMT_DECIM: + if (c>='0' && c<='9') { + *digits=*digits*10+(int)(c-'0'); + } else { + return 1; /* print formatted character */ + } /* if */ + break; + } /* switch */ + + return 0; +} + +static int printstring(AMX *amx,cell *cstr,cell *params,int num) +{ + int i,paramidx=0; + int fmtstate=FMT_NONE,width,digits; + char sign,decpoint; + + /* if no placeholders appear, we can use a quicker routine */ + if (params==NULL) { + + TCHAR cache[100]; + int idx=0; + if ((ucell)*cstr>UNPACKEDMAX) { + int j=sizeof(cell)-sizeof(char); + char c; + /* the string is packed */ + i=0; + for ( ; ; ) { + c=(char)((ucell)cstr[i] >> 8*j); + if (c==0) + break; + assert(idx0) { + cache[idx]='\0'; + amx_putstr(cache); + } /* if */ + + } else { + + /* check whether this is a packed string */ + if ((ucell)*cstr>UNPACKEDMAX) { + int j=sizeof(cell)-sizeof(char); + char c; + /* the string is packed */ + i=0; + for ( ; ; ) { + c=(char)((ucell)cstr[i] >> 8*j); + if (c==0) + break; + switch (formatstate(c,&fmtstate,&sign,&decpoint,&width,&digits)) { + case -1: + amx_putchar(c); + break; + case 1: + assert(params!=NULL); + if (digits>25) + digits=25; + paramidx+=dochar(amx,c,params[paramidx],sign,decpoint,width,digits); + fmtstate=FMT_NONE; + break; + case 0: + if (params==NULL || paramidx>=num) /* insufficient parameters passed */ + amx_RaiseError(amx, AMX_ERR_NATIVE); + break; + } /* switch */ + if (j==0) + i++; + j=(j+sizeof(cell)-sizeof(char)) % sizeof(cell); + } /* for */ + } else { + /* the string is unpacked */ + for (i=0; cstr[i]!=0; i++) { + switch (formatstate((TCHAR)cstr[i],&fmtstate,&sign,&decpoint,&width,&digits)) { + case -1: + amx_putchar((TCHAR)cstr[i]); + break; + case 1: + assert(params!=NULL); + paramidx+=dochar(amx,(TCHAR)cstr[i],params[paramidx],sign,decpoint,width,digits); + fmtstate=FMT_NONE; + break; + case 0: + if (params==NULL || paramidx>=num) /* insufficient parameters passed */ + amx_RaiseError(amx, AMX_ERR_NATIVE); + break; + } /* switch */ + } /* for */ + } /* if */ + + } /* if (params==NULL) */ + + return paramidx; +} + +static cell AMX_NATIVE_CALL n_print(AMX *amx,cell *params) +{ + cell *cstr; + int oldcolours; + + CreateConsole(); + + /* set the new colours */ + oldcolours=amx_setattr((int)params[2],(int)params[3],(int)params[4]); + + amx_GetAddr(amx,params[1],&cstr); + printstring(amx,cstr,NULL,0); + + /* reset the colours */ + (void)amx_setattr(oldcolours & 0xff,(oldcolours >> 8) & 0x7f,(oldcolours >> 15) & 0x01); + amx_fflush(); + return 0; +} + +static cell AMX_NATIVE_CALL n_printf(AMX *amx,cell *params) +{ + cell *cstr; + + CreateConsole(); + amx_GetAddr(amx,params[1],&cstr); + printstring(amx,cstr,params+2,(int)(params[0]/sizeof(cell))-1); + amx_fflush(); + return 0; +} + +/* getchar(bool:echo=true) */ +static cell AMX_NATIVE_CALL n_getchar(AMX *amx,cell *params) +{ + int c; + + (void)amx; + CreateConsole(); + c=amx_getch(); + if (params[1]) { + #if defined(SUPPRESS_ECHO) + /* For Mac OS X, non-Curses, don't echo the character */ + #else + amx_putchar((TCHAR)c); + amx_fflush(); + #endif + } /* if */ + return c; +} + +/* getstring(string[], size=sizeof string, bool:pack=false) */ +static cell AMX_NATIVE_CALL n_getstring(AMX *amx,cell *params) +{ + int c,chars,max; + TCHAR *str; + cell *cptr; + + CreateConsole(); + max=(int)params[2]; + if (max<=0) + return 0; + if (params[3]) + max*=sizeof(cell); + + chars=0; + + str=(TCHAR *)alloca(max*sizeof(TCHAR)); + if (str!=NULL) { + + c=amx_getch(); + while (c!=EOF && c!=EOL_CHAR && chars1,max); + + } /* if */ + return chars; +} + +static void acceptchar(int c,int *num) +{ + switch (c) { + case '\b': + amx_putchar('\b'); + *num-=1; + #if defined amx_putchar && (defined __BORLANDC__ || defined __WATCOMC__) + /* the backspace key does not erase the + * character, so do this explicitly */ + amx_putchar(' '); /* erase */ + amx_putchar('\b'); /* go back */ + #endif + break; + case EOL_CHAR: + amx_putchar('\n'); + *num+=1; + break; + default: + #if defined(SUPPRESS_ECHO) + /* For Mac OS X, non-Curses, don't echo the character */ + #else + amx_putchar((TCHAR)c); + #endif + *num+=1; + } /* switch */ + amx_fflush(); +} + +static int inlist(AMX *amx,int c,cell *params,int num) +{ + int i, key; + + for (i=0; i36) + return 0; + + chars=0; + value=0; + sign=1; /* to avoid a compiler warning (Microsoft Visual C/C++ 6.0) */ + + c=amx_getch(); + while (c!=EOF) { + /* check for sign (if any) */ + if (chars==0) { + if (c=='-') { + sign=-1; + acceptchar(c,&chars); + c=amx_getch(); + } else { + sign=1; + } /* if */ + } /* if */ + + /* check end of input */ + #if defined LINUX || defined __FreeBSD__ || defined __OpenBSD__ || defined MACOS + if (c=='\n' && inlist(amx,'\r',params+2,(int)params[0]/sizeof(cell)-1)!=0) + c='\r'; + #endif + if ((chars>1 || chars>0 && sign>0) + && (n=inlist(amx,c,params+2,(int)params[0]/sizeof(cell)-1))!=0) + { + if (n>0) + acceptchar(c,&chars); + break; + } /* if */ + + /* get value */ + d=base; /* by default, do not accept the character */ + if (c>='0' && c<='9') { + d=c-'0'; + } else if (c>='a' && c<='z') { + d=c-'a'+10; + } else if (c>='A' && c<='Z') { + d=c-'A'+10; + } else if (c=='\b') { + if (chars>0) { + value/=base; + acceptchar(c,&chars); + } /* if */ + } /* if */ + if (d= 0); + + if (PrevIdle != NULL) + PrevIdle(amx); + + if (kbhit()) { + key = getch(); + amx_Push(amx, key); + err = amx_Exec(amx, NULL, idxKeyPressed); + while (err == AMX_ERR_SLEEP) + err = amx_Exec(amx, NULL, AMX_EXEC_CONT); + } /* if */ + + return err; +} +#endif + +#if defined __cplusplus + extern "C" +#endif +const AMX_NATIVE_INFO console_Natives[] = { + { "getchar", n_getchar }, + { "getstring", n_getstring }, + { "getvalue", n_getvalue }, + { "print", n_print }, + { "printf", n_printf }, + { "clrscr", n_clrscr }, + { "clreol", n_clreol }, + { "gotoxy", n_gotoxy }, + { "wherexy", n_wherexy }, + { "setattr", n_setattr }, + { "console", n_console }, + { "consctrl", n_consctrl }, + { NULL, NULL } /* terminator */ +}; + +int AMXEXPORT amx_ConsoleInit(AMX *amx) +{ + #if !defined AMXCONSOLE_NOIDLE + /* see whether there is an @keypressed() function */ + if (amx_FindPublic(amx, "@keypressed", &idxKeyPressed) == AMX_ERR_NONE) { + if (amx_GetUserData(amx, AMX_USERTAG('I','d','l','e'), (void**)&PrevIdle) != AMX_ERR_NONE) + PrevIdle = NULL; + amx_SetUserData(amx, AMX_USERTAG('I','d','l','e'), amx_ConsoleIdle); + } /* if */ + #endif + + return amx_Register(amx, console_Natives, -1); +} + +int AMXEXPORT amx_ConsoleCleanup(AMX *amx) +{ + (void)amx; + #if !defined AMXCONSOLE_NOIDLE + PrevIdle = NULL; + #endif + return AMX_ERR_NONE; +} diff --git a/server/amx/amxcore.c b/server/amx/amxcore.c new file mode 100644 index 0000000..07caee7 --- /dev/null +++ b/server/amx/amxcore.c @@ -0,0 +1,500 @@ +/* Core module for the Pawn AMX + * + * Copyright (c) ITB CompuPhase, 1997-2005 + * + * This software is provided "as-is", without any express or implied warranty. + * In no event will the authors be held liable for any damages arising from + * the use of this software. + * + * Permission is granted to anyone to use this software for any purpose, + * including commercial applications, and to alter it and redistribute it + * freely, subject to the following restrictions: + * + * 1. The origin of this software must not be misrepresented; you must not + * claim that you wrote the original software. If you use this software in + * a product, an acknowledgment in the product documentation would be + * appreciated but is not required. + * 2. Altered source versions must be plainly marked as such, and must not be + * misrepresented as being the original software. + * 3. This notice may not be removed or altered from any source distribution. + * + * Version: $Id: amxcore.c 3363 2005-07-23 09:03:29Z thiadmer $ + */ +#if defined _UNICODE || defined __UNICODE__ || defined UNICODE +# if !defined UNICODE /* for Windows */ +# define UNICODE +# endif +# if !defined _UNICODE /* for C library */ +# define _UNICODE +# endif +#endif + +#include +#include +#include +#include +#include +#include "amx.h" +#if defined __WIN32__ || defined _WIN32 || defined WIN32 || defined _Windows + #include +#endif + +/* A few compilers do not provide the ANSI C standard "time" functions */ +#if !defined SN_TARGET_PS2 && !defined _WIN32_WCE + #include +#endif + +#if defined _UNICODE +# include +#elif !defined __T + typedef char TCHAR; +# define __T(string) string +# define _tcschr strchr +# define _tcscpy strcpy +# define _tcsdup strdup +# define _tcslen strlen +#endif + + +#define CHARBITS (8*sizeof(char)) +typedef unsigned char uchar; + +#if !defined AMX_NOPROPLIST +typedef struct _property_list { + struct _property_list *next; + cell id; + char *name; + cell value; + //??? safe AMX (owner of the property) +} proplist; + +static proplist proproot = { NULL, 0, NULL, 0 }; + +static proplist *list_additem(proplist *root) +{ + proplist *item; + + assert(root!=NULL); + if ((item=(proplist *)malloc(sizeof(proplist)))==NULL) + return NULL; + item->name=NULL; + item->id=0; + item->value=0; + item->next=root->next; + root->next=item; + return item; +} +static void list_delete(proplist *pred,proplist *item) +{ + assert(pred!=NULL); + assert(item!=NULL); + pred->next=item->next; + assert(item->name!=NULL); + free(item->name); + free(item); +} +static void list_setitem(proplist *item,cell id,char *name,cell value) +{ + char *ptr; + + assert(item!=NULL); + if ((ptr=(char *)malloc(strlen(name)+1))==NULL) + return; + if (item->name!=NULL) + free(item->name); + strcpy(ptr,name); + item->name=ptr; + item->id=id; + item->value=value; +} +static proplist *list_finditem(proplist *root,cell id,char *name,cell value, + proplist **pred) +{ + proplist *item=root->next; + proplist *prev=root; + + /* check whether to find by name or by value */ + assert(name!=NULL); + if (strlen(name)>0) { + /* find by name */ + while (item!=NULL && (item->id!=id || stricmp(item->name,name)!=0)) { + prev=item; + item=item->next; + } /* while */ + } else { + /* find by value */ + while (item!=NULL && (item->id!=id || item->value!=value)) { + prev=item; + item=item->next; + } /* while */ + } /* if */ + if (pred!=NULL) + *pred=prev; + return item; +} +#endif + +static cell AMX_NATIVE_CALL numargs(AMX *amx, cell *params) +{ + AMX_HEADER *hdr; + uchar *data; + cell bytes; + + (void)params; + hdr=(AMX_HEADER *)amx->base; + data=amx->data ? amx->data : amx->base+(int)hdr->dat; + /* the number of bytes is on the stack, at "frm + 2*cell" */ + bytes= * (cell *)(data+(int)amx->frm+2*sizeof(cell)); + /* the number of arguments is the number of bytes divided + * by the size of a cell */ + return bytes/sizeof(cell); +} + +static cell AMX_NATIVE_CALL getarg(AMX *amx, cell *params) +{ + AMX_HEADER *hdr; + uchar *data; + cell value; + + hdr=(AMX_HEADER *)amx->base; + data=amx->data ? amx->data : amx->base+(int)hdr->dat; + /* get the base value */ + value= * (cell *)(data+(int)amx->frm+((int)params[1]+3)*sizeof(cell)); + /* adjust the address in "value" in case of an array access */ + value+=params[2]*sizeof(cell); + /* get the value indirectly */ + value= * (cell *)(data+(int)value); + return value; +} + +static cell AMX_NATIVE_CALL setarg(AMX *amx, cell *params) +{ + AMX_HEADER *hdr; + uchar *data; + cell value; + + hdr=(AMX_HEADER *)amx->base; + data=amx->data ? amx->data : amx->base+(int)hdr->dat; + /* get the base value */ + value= * (cell *)(data+(int)amx->frm+((int)params[1]+3)*sizeof(cell)); + /* adjust the address in "value" in case of an array access */ + value+=params[2]*sizeof(cell); + /* verify the address */ + if (value<0 || value>=amx->hea && valuestk) + return 0; + /* set the value indirectly */ + * (cell *)(data+(int)value) = params[3]; + return 1; +} + +static cell AMX_NATIVE_CALL heapspace(AMX *amx,cell *params) +{ + (void)params; + return amx->stk - amx->hea; +} + +static cell AMX_NATIVE_CALL funcidx(AMX *amx,cell *params) +{ + char name[64]; + cell *cstr; + int index,err,len; + + amx_GetAddr(amx,params[1],&cstr); + + /* verify string length */ + amx_StrLen(cstr,&len); + if (len>=64) { + amx_RaiseError(amx,AMX_ERR_NATIVE); + return 0; + } /* if */ + + amx_GetString(name,cstr,0,UNLIMITED); + err=amx_FindPublic(amx,name,&index); + if (err!=AMX_ERR_NONE) + index=-1; /* this is not considered a fatal error */ + return index; +} + +static cell AMX_NATIVE_CALL swapchars(AMX *amx,cell *params) +{ + union { + cell c; + #if PAWN_CELL_SIZE==16 + uchar b[2]; + #elif PAWN_CELL_SIZE==32 + uchar b[4]; + #elif PAWN_CELL_SIZE==64 + uchar b[8]; + #else + #error Unsupported cell size + #endif + } value; + uchar t; + + (void)amx; + assert((size_t)params[0]==sizeof(cell)); + value.c = params[1]; + #if PAWN_CELL_SIZE==16 + t = value.b[0]; + value.b[0] = value.b[1]; + value.b[1] = t; + #elif PAWN_CELL_SIZE==32 + t = value.b[0]; + value.b[0] = value.b[3]; + value.b[3] = t; + t = value.b[1]; + value.b[1] = value.b[2]; + value.b[2] = t; + #elif PAWN_CELL_SIZE==64 + t = value.b[0]; + value.b[0] = value.b[7]; + value.b[7] = t; + t = value.b[1]; + value.b[1] = value.b[6]; + value.b[6] = t; + t = value.b[2]; + value.b[2] = value.b[5]; + value.b[5] = t; + t = value.b[3]; + value.b[3] = value.b[4]; + value.b[4] = t; + #else + #error Unsupported cell size + #endif + return value.c; +} + +static cell AMX_NATIVE_CALL core_tolower(AMX *amx,cell *params) +{ + (void)amx; + #if defined __WIN32__ || defined _WIN32 || defined WIN32 + return (cell)CharLower((LPTSTR)params[1]); + #elif defined _Windows + return (cell)AnsiLower((LPSTR)params[1]); + #else + return tolower((int)params[1]); + #endif +} + +static cell AMX_NATIVE_CALL core_toupper(AMX *amx,cell *params) +{ + (void)amx; + #if defined __WIN32__ || defined _WIN32 || defined WIN32 + return (cell)CharUpper((LPTSTR)params[1]); + #elif defined _Windows + return (cell)AnsiUpper((LPSTR)params[1]); + #else + return toupper((int)params[1]); + #endif +} + +static cell AMX_NATIVE_CALL core_min(AMX *amx,cell *params) +{ + (void)amx; + return params[1] <= params[2] ? params[1] : params[2]; +} + +static cell AMX_NATIVE_CALL core_max(AMX *amx,cell *params) +{ + (void)amx; + return params[1] >= params[2] ? params[1] : params[2]; +} + +static cell AMX_NATIVE_CALL core_clamp(AMX *amx,cell *params) +{ + cell value = params[1]; + if (params[2] > params[3]) /* minimum value > maximum value ! */ + amx_RaiseError(amx,AMX_ERR_NATIVE); + if (value < params[2]) + value = params[2]; + else if (value > params[3]) + value = params[3]; + return value; +} + +#if !defined AMX_NOPROPLIST +static char *MakePackedString(cell *cptr) +{ + int len; + char *dest; + + amx_StrLen(cptr,&len); + dest=(char *)malloc(len+sizeof(cell)); + amx_GetString(dest,cptr,0,UNLIMITED); + return dest; +} + +static int verify_addr(AMX *amx,cell addr) +{ + int err; + cell *cdest; + + err=amx_GetAddr(amx,addr,&cdest); + if (err!=AMX_ERR_NONE) + amx_RaiseError(amx,err); + return err; +} + +static cell AMX_NATIVE_CALL getproperty(AMX *amx,cell *params) +{ + cell *cstr; + char *name; + proplist *item; + + amx_GetAddr(amx,params[2],&cstr); + name=MakePackedString(cstr); + item=list_finditem(&proproot,params[1],name,params[3],NULL); + /* if list_finditem() found the value, store the name */ + if (item!=NULL && item->value==params[3] && strlen(name)==0) { + int needed=(strlen(item->name)+sizeof(cell)-1)/sizeof(cell); /* # of cells needed */ + if (verify_addr(amx,(cell)(params[4]+needed))!=AMX_ERR_NONE) { + free(name); + return 0; + } /* if */ + amx_GetAddr(amx,params[4],&cstr); + amx_SetString(cstr,item->name,1,0,UNLIMITED); + } /* if */ + free(name); + return (item!=NULL) ? item->value : 0; +} + +static cell AMX_NATIVE_CALL setproperty(AMX *amx,cell *params) +{ + cell prev=0; + cell *cstr; + char *name; + proplist *item; + + amx_GetAddr(amx,params[2],&cstr); + name=MakePackedString(cstr); + item=list_finditem(&proproot,params[1],name,params[3],NULL); + if (item==NULL) + item=list_additem(&proproot); + if (item==NULL) { + amx_RaiseError(amx,AMX_ERR_MEMORY); + } else { + prev=item->value; + if (strlen(name)==0) { + free(name); + amx_GetAddr(amx,params[4],&cstr); + name=MakePackedString(cstr); + } /* if */ + list_setitem(item,params[1],name,params[3]); + } /* if */ + free(name); + return prev; +} + +static cell AMX_NATIVE_CALL delproperty(AMX *amx,cell *params) +{ + cell prev=0; + cell *cstr; + char *name; + proplist *item,*pred; + + amx_GetAddr(amx,params[2],&cstr); + name=MakePackedString(cstr); + item=list_finditem(&proproot,params[1],name,params[3],&pred); + if (item!=NULL) { + prev=item->value; + list_delete(pred,item); + } /* if */ + free(name); + return prev; +} + +static cell AMX_NATIVE_CALL existproperty(AMX *amx,cell *params) +{ + cell *cstr; + char *name; + proplist *item; + + amx_GetAddr(amx,params[2],&cstr); + name=MakePackedString(cstr); + item=list_finditem(&proproot,params[1],name,params[3],NULL); + free(name); + return (item!=NULL); +} +#endif + +#if !defined AMX_NORANDOM +/* This routine comes from the book "Inner Loops" by Rick Booth, Addison-Wesley + * (ISBN 0-201-47960-5). This is a "multiplicative congruential random number + * generator" that has been extended to 31-bits (the standard C version returns + * only 15-bits). + */ +#define INITIAL_SEED 0xcaa938dbL +static unsigned long IL_StandardRandom_seed = INITIAL_SEED; /* always use a non-zero seed */ +#define IL_RMULT 1103515245L +#if defined __BORLANDC__ || defined __WATCOMC__ + #pragma argsused +#endif +static cell AMX_NATIVE_CALL core_random(AMX *amx,cell *params) +{ + unsigned long lo, hi, ll, lh, hh, hl; + unsigned long result; + + /* one-time initialization (or, mostly one-time) */ + #if !defined SN_TARGET_PS2 && !defined _WIN32_WCE + if (IL_StandardRandom_seed == INITIAL_SEED) + IL_StandardRandom_seed=(unsigned long)time(NULL); + #endif + + lo = IL_StandardRandom_seed & 0xffff; + hi = IL_StandardRandom_seed >> 16; + IL_StandardRandom_seed = IL_StandardRandom_seed * IL_RMULT + 12345; + ll = lo * (IL_RMULT & 0xffff); + lh = lo * (IL_RMULT >> 16 ); + hl = hi * (IL_RMULT & 0xffff); + hh = hi * (IL_RMULT >> 16 ); + result = ((ll + 12345) >> 16) + lh + hl + (hh << 16); + result &= ~LONG_MIN; /* remove sign bit */ + if (params[1]!=0) + result %= params[1]; + return (cell)result; +} +#endif + + +#if defined __cplusplus + extern "C" +#endif +const AMX_NATIVE_INFO core_Natives[] = { + { "numargs", numargs }, + { "getarg", getarg }, + { "setarg", setarg }, + { "heapspace", heapspace }, + { "funcidx", funcidx }, + { "swapchars", swapchars }, + { "tolower", core_tolower }, + { "toupper", core_toupper }, + { "min", core_min }, + { "max", core_max }, + { "clamp", core_clamp }, +#if !defined AMX_NORANDOM + { "random", core_random }, +#endif +#if !defined AMX_NOPROPLIST + { "getproperty", getproperty }, + { "setproperty", setproperty }, + { "deleteproperty",delproperty }, + { "existproperty", existproperty }, +#endif + { NULL, NULL } /* terminator */ +}; + +int AMXEXPORT amx_CoreInit(AMX *amx) +{ + return amx_Register(amx, core_Natives, -1); +} + +int AMXEXPORT amx_CoreCleanup(AMX *amx) +{ + (void)amx; + #if !defined AMX_NOPROPLIST + //??? delete only the properties owned by the AMX + while (proproot.next!=NULL) + list_delete(&proproot,proproot.next); + #endif + return AMX_ERR_NONE; +} diff --git a/server/amx/amxdefn.asm b/server/amx/amxdefn.asm new file mode 100644 index 0000000..450f6a0 --- /dev/null +++ b/server/amx/amxdefn.asm @@ -0,0 +1,86 @@ +; Definition of the AMX structure for assembler syntax (NASM) + + struc amx_s +_base: resd 1 +_dataseg: resd 1 +_callback: resd 1 +_debug: resd 1 +_cip: resd 1 +_frm: resd 1 +_hea: resd 1 +_hlw: resd 1 +_stk: resd 1 +_stp: resd 1 +_flags: resd 1 +_usertags: resd 4 ; 4 = AMX_USERNUM (#define'd in amx.h) +_userdata: resd 4 ; 4 = AMX_USERNUM (#define'd in amx.h) +_error: resd 1 +_paramcount: resd 1 +_pri: resd 1 +_alt: resd 1 +_reset_stk: resd 1 +_reset_hea: resd 1 +_syscall_d: resd 1 +%ifdef JIT + ; the two fields below are for the JIT; they do not exist in + ; the non-JIT version of the abstract machine +_reloc_size: resd 1 ; memory block for relocations +_code_size: resd 1 ; memory size of the native code +%endif + endstruc + + struc amxhead_s +_size: resd 1 ; size of the "file" +_magic: resw 1 ; signature +_file_version: resb 1; file format version +_amx_version: resb 1 ; required version of the AMX +_h_flags: resw 1 +_defsize: resw 1 ; size of one public/native function entry +_cod: resd 1 ; initial value of COD - code block +_dat: resd 1 ; initial value of DAT - data block +_h_hea: resd 1 ; initial value of HEA - start of the heap +_h_stp: resd 1 ; initial value of STP - stack top +_h_cip: resd 1 ; initial value of CIP - the instruction pointer +_publics: resd 1 ; offset to the "public functions" table +_natives: resd 1 ; offset to the "native functions" table +_libraries: resd 1 ; offset to the "library" table +_pubvars: resd 1 ; offset to the "public variables" table +_tags: resd 1 ; offset to the "public tagnames" table +_nametable: resd 1 ; offset to the name table, file version 7 only + endstruc + + +AMX_ERR_NONE EQU 0 +AMX_ERR_EXIT EQU 1 +AMX_ERR_ASSERT EQU 2 +AMX_ERR_STACKERR EQU 3 +AMX_ERR_BOUNDS EQU 4 +AMX_ERR_MEMACCESS EQU 5 +AMX_ERR_INVINSTR EQU 6 +AMX_ERR_STACKLOW EQU 7 +AMX_ERR_HEAPLOW EQU 8 +AMX_ERR_CALLBACK EQU 9 +AMX_ERR_NATIVE EQU 10 +AMX_ERR_DIVIDE EQU 11 ; for catching divide errors +AMX_ERR_SLEEP EQU 12 + +AMX_ERR_MEMORY EQU 16 +AMX_ERR_FORMAT EQU 17 +AMX_ERR_VERSION EQU 18 +AMX_ERR_NOTFOUND EQU 19 +AMX_ERR_INDEX EQU 20 +AMX_ERR_DEBUG EQU 21 +AMX_ERR_INIT EQU 22 +AMX_ERR_USERDATA EQU 23 +AMX_ERR_INIT_JIT EQU 24 +AMX_ERR_PARAMS EQU 25 +AMX_ERR_DOMAIN EQU 26 +AMX_ERR_GENERAL EQU 27 + +AMX_FLAG_DEBUG EQU 0002h ; symbolic info. available +AMX_FLAG_COMPACT EQU 0004h +AMX_FLAG_BYTEOPC EQU 0008h +AMX_FLAG_NOCHECKS EQU 0010h +AMX_FLAG_BROWSE EQU 4000h +AMX_FLAG_RELOC EQU 8000h ; jump/call addresses relocated + diff --git a/server/amx/amxexecn.asm b/server/amx/amxexecn.asm new file mode 100644 index 0000000..a5a6cf7 --- /dev/null +++ b/server/amx/amxexecn.asm @@ -0,0 +1,1633 @@ +; AMXEXECN.ASM Abstract Machine for the "Pawn" language +; + +;Some notes: +; * This file was adapted from AMXEXEC.ASM (for MASM/TASM/WASM). This +; version is for NASM (Netwide Assembler). NASM uses Intel syntax for +; the mnemonics, but it is not compatible with MASM. +; * The "calling convention" is __cdecl for the amx_exec_asm() itself and +; __cdecl or __stdcall for the native routines (the default is __cdecl, +; define the macro STDECL to set __stdcall). +; * The borland compiler uses different segment definitions as Microsoft +; Visual C/C++ and GNU GCC. To assemble the abstract machine with "Borland" +; segments, add the definition "BORLAND" on the command line. +; * You will need to compile the standard AMX.C file with the macro ASM32 +; defined. On the command line, use: +; nasmw -f obj -d BORLAND amxexecn.asm +; bcc32 -DASM32 srun.c amx.c amxcore.c amxcons.c amxexecn.obj +; or +; nasmw -f win32 amxexecn.asm +; or +; nasm -f elf amxexecn.asm +; gcc -o srun -DLINUX -DASM32 -I../linux srun.c amx.c amxcore.c amxcons.c amxexecn.o +; * See the notes in AMXEXEC.ASM for more information and a change log). +; +; +;Copyright and license of use, please read +;----------------------------------------- +;The assembler implementation of the abstract machine for the Pawn language, +;specifically the file AMXEXEC.ASM, is copyright (c) 1998-2000 by Marc Peter. +; +;Permission is hereby granted, without written agreement and without paid +;license or royalty fees, to use, copy, modify, and distribute this software +;and its documentation for any purpose, subject to the following conditions: +; +;1. The above copyright notice and this permission notice shall appear in all +; copies or substantial portions of this software. +; +;2. Modifications of this software that do not originate from me (Marc Peter) +; must be explicitly mentioned in a README file or another appropriate +; place. +; +;The use of this software as a subsystem of a larger software product is +;explicitly allowed, regardless of whether that larger product is proprietary, +;gratis or commercially available. +; +;I (Marc Peter) specifically disclaim any warranties, including, but not +;limited to, the implied warranties of merchantability and fitness for a +;particular purpose. The software is provided on an "as is" basis, +;and I have no obligation to provide maintenance, support, updates, +;enhancements or modifications. +; +;I cannot be held liable for any damage or loss of profits that results +;from the use of the software (or part thereof), or from the inability to +;use it. +; +; +;History (list of changes) +;------------------------- +; 17 february 2005 by Thiadmer Riemersms +; Addition of the BREAK opcode, removal of the older debugging opcode table. +; 6 march 2004 by Thiadmer Riemersma +; Corrected a bug in OP_FILL, where a cell preceding the array would +; be overwritten (zero'ed out). This bug was brought to my attention +; by Robert Daniels. +; 2 february 2004 by Thiadmer Riemersma (TR) +; Added checking of the return address in the RET and RETN opcodes. +; Changed handling of LINE opcode, so that the debugger can force a +; sleep. +; 22 december 2003 by Thiadmer Riemersma (TR) +; Added support for the SYMTAG and SYSCALL.D opcodes +; 3 october 2003 by Thiadmer Riemersma (TR) +; Added "non-debug" versions of various opcodes, to avoid repetitive +; checking of the "debug" flag. +; 15 September 2003 by Thiadmer Riemersma (TR) +; Minor corrections, mostly to support older versions of NASM +; 26 January 2003 by Thiadmer Riemersma (TR) +; Port to NASM +;----- + +;CPU 386 -- some older versions of NASM do not support this keyword + +; Macro to begin a code segment +%macro Start_CODE 0 + %ifdef BORLAND + segment _TEXT public align=1 class=CODE use32 + %else + segment .text + %endif +%endmacro + +; Macro to begin a data segment +%macro Start_DATA 0 + %ifdef BORLAND + segment _DATA public align=4 class=DATA use32 + %else + segment .data + %endif +%endmacro + +%include "amxdefn.asm" + +;#define PUSH(v) ( stk-=sizeof(cell), *(cell *)(data+(int)stk)=v ) +%macro _PUSH 1 + mov [edi+ecx-4],%1 + sub ecx,4 +%endmacro + + +;#define POP(v) ( v=*(cell *)(data+(int)stk), stk+=sizeof(cell) ) +%macro _POP 1 + mov %1,[edi+ecx] + add ecx,4 +%endmacro + +%macro GO_ON 0 + jmp DWORD [esi] +; ALIGN 4 +%endmacro + + +%macro _CHKSTACK 0 + cmp ecx,stp + jg near err_stacklow +%endmacro + +%macro _CHKMARGIN 0 + lea ebp,[ecx-16*4] ;savety margin = 16 cells + cmp hea,ebp + jg near err_stack +%endmacro + +%macro _CHKHEAP 0 + mov ebp,amx + mov ebp,[ebp+_hlw] + cmp DWORD hea,ebp + jl near err_heaplow +%endmacro + +%macro _CHKDIVIDEZERO 0 + or ebp,ebp ; check for divide by zero + jz near err_divide +%endmacro + +%macro _VERIFYADDRESS 1 ; used in load.i, store.i & lidx + cmp %1,stp ; error if address >= stp + jae near err_memaccess + cmp %1,hea ; so address=hea, ok if address>=stk + jb near err_memaccess + %%address_ok: +%endmacro + +%macro _SAVEREGS 0 ; save the registers (that may not be + PUSHAD ; __stdcall calling conventions) +%endmacro + +%macro _RESTOREREGS 0 + POPAD +%endmacro + +%macro _DROPARGS 1 ; remove function arguments from the stack + %ifndef STDECL ; (only for __cdecl calling convention) + add esp,%1 + %endif +%endmacro + + +Start_CODE + +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +; ; +;cell asm_exec( cell *regs, cell *retval, cell stp, cell hea ); +; ; +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; + + GLOBAL _amx_exec_asm + GLOBAL amx_exec_asm +amx_exec_asm: +_amx_exec_asm: ;PROC + + push ebx + mov eax,[esp+08h] + mov edx,[esp+0ch] + mov ebx,[esp+10h] + mov ecx,[esp+14h] + + push edi + push esi + push ebp + + sub esp,4*3 ; place for PRI, ALT & STK at SYSREQs + + push DWORD [eax+20h] ; store code size + push DWORD [eax+1ch] ; store pointer to code segment + push DWORD [eax+18h] ; store pointer to AMX + push edx ; store address of retval + push ebx ; store STP + push ecx ; store HEA + push DWORD [eax+14h] ; store FRM + +%define stk [esp+36] ; define some aliases to registers +%define alt [esp+32] ; that are stored on the stack +%define pri [esp+28] +%define codesiz [esp+24] +%define code [esp+20] +%define amx [esp+16] +%define retval [esp+12] +%define stp [esp+8] +%define hea [esp+4] +%define frm [esp] ; FRM is NOT stored in ebp, rather FRM+DAT + ; is being held in ebx. + + mov edx,code ; change the code size to an... + add codesiz,edx ; ..."end of code" address + + mov edx,[eax+04h] ; get ALT + mov esi,[eax+08h] ; get CIP + mov edi,[eax+0ch] ; get pointer to data segment + mov ecx,[eax+10h] ; get STK + mov ebx,[eax+14h] ; get FRM + mov eax,[eax] ; get PRI + add ebx,edi ; relocate frame + + GO_ON ; start interpreting + + +OP_LOAD_PRI: + mov eax,[esi+4] + add esi,8 + mov eax,[edi+eax] + GO_ON + +OP_LOAD_ALT: + mov edx,[esi+4] + add esi,8 + mov edx,[edi+edx] + GO_ON + +;good +OP_LOAD_S_PRI: + mov eax,[esi+4] + add esi,8 + mov eax,[ebx+eax] + GO_ON + +;good +OP_LOAD_S_ALT: + mov edx,[esi+4] + add esi,8 + mov edx,[ebx+edx] + GO_ON + +OP_LOAD_I: + add esi,4 + _VERIFYADDRESS eax + mov eax,[edi+eax] + GO_ON + +OP_LODB_I: + _VERIFYADDRESS eax + mov ebp,[esi+4] + mov eax,[edi+eax] ;subject to misalignment stalls + add esi,8 + and eax,DWORD [(lodb_and-4)+ebp*4] + GO_ON + +OP_LREF_PRI: + mov eax,[esi+4] + add esi,8 + mov eax,[edi+eax] + mov eax,[edi+eax] + GO_ON + +OP_LREF_ALT: + mov edx,[esi+4] + add esi,8 + mov edx,[edi+edx] + mov edx,[edi+edx] + GO_ON + +OP_LREF_S_PRI: + mov eax,[esi+4] + add esi,8 + mov eax,[ebx+eax] + mov eax,[edi+eax] + GO_ON + +OP_LREF_S_ALT: + mov edx,[esi+4] + add esi,8 + mov edx,[ebx+edx] + mov edx,[edi+edx] + GO_ON + +;good +OP_CONST_PRI: + mov eax,[esi+4] + add esi,8 + GO_ON + +;good +OP_CONST_ALT: + mov edx,[esi+4] + add esi,8 + GO_ON + +;good +OP_ADDR_PRI: + mov eax,[esi+4] + add esi,8 + add eax,frm + GO_ON + +;good +OP_ADDR_ALT: + mov edx,[esi+4] + add esi,8 + add edx,frm + GO_ON + +OP_STOR_PRI: + mov ebp,[esi+4] + add esi,8 + mov [ebp+edi],eax + GO_ON + +OP_STOR_ALT: + mov ebp,[esi+4] + add esi,8 + mov [ebp+edi],edx + GO_ON + +;good +OP_STOR_S_PRI: + mov ebp,[esi+4] + add esi,8 + mov [ebp+ebx],eax + GO_ON + +;good +OP_STOR_S_ALT: + mov ebp,[esi+4] + add esi,8 + mov [ebp+ebx],edx + GO_ON + +;good +OP_STOR_I: + add esi,4 + _VERIFYADDRESS edx + mov [edi+edx],eax + GO_ON + +OP_STRB_I: + _VERIFYADDRESS edx + mov ebp,[esi+4] + add esi,8 + cmp ebp,1 + jne short strb_not1byte + mov [edi+edx],al + GO_ON + strb_not1byte: + cmp ebp,4 + je short strb_4byte + mov [edi+edx],ax + GO_ON + strb_4byte: + mov [edi+edx],eax + GO_ON + +OP_SREF_PRI: + mov ebp,[esi+4] + add esi,8 + mov ebp,[edi+ebp] + mov [edi+ebp],eax + GO_ON + +OP_SREF_ALT: + mov ebp,[esi+4] + add esi,8 + mov ebp,[edi+ebp] + mov [edi+ebp],edx + GO_ON + +OP_SREF_S_PRI: + mov ebp,[esi+4] + add esi,8 + mov ebp,[ebx+ebp] + mov [edi+ebp],eax + GO_ON + +OP_SREF_S_ALT: + mov ebp,[esi+4] + add esi,8 + mov ebp,[ebx+ebp] + mov [edi+ebp],edx + GO_ON + +;good +OP_LIDX: + lea eax,[edx+4*eax] + add esi,4 + _VERIFYADDRESS eax + mov eax,[edi+eax] + GO_ON + +OP_LIDX_B: + push ecx + mov ecx,[esi+4] + shl eax,cl + add esi,8 + add eax,edx + pop ecx + _VERIFYADDRESS eax + mov eax,[edi+eax] + GO_ON + +;good +OP_IDXADDR: + add esi,4 + lea eax,[edx+4*eax] + GO_ON + +OP_IDXADDR_B: + push ecx + mov ecx,[esi+4] + add esi,8 + shl eax,cl + pop ecx + add eax,edx + GO_ON + +OP_ALIGN_PRI: + mov ebp,4 ; ??? one operation too many? + sub ebp,[esi+4] + add esi,8 + xor eax,ebp + GO_ON + +OP_ALIGN_ALT: + mov ebp,4 + sub ebp,[esi+4] + add esi,8 + xor edx,ebp + GO_ON + +OP_LCTRL: + mov ebp,[esi+4] + add esi,8 + cmp ebp,0 + jne short lctrl_1 + mov eax,code ; COD + GO_ON + lctrl_1: + cmp ebp,1 + jne short lctrl_2 + mov eax,edi ; DAT + GO_ON + lctrl_2: + cmp ebp,2 + jne short lctrl_3 + mov eax,hea ; 2=HEA + GO_ON + lctrl_3: + cmp ebp,3 + jne short lctrl_4 + mov ebp,amx + mov eax,stp + GO_ON + lctrl_4: + cmp ebp,4 + jne short lctrl_5 + mov eax,ecx ; 4=STK + GO_ON + lctrl_5: + cmp ebp,5 + jne short lctrl_6 + mov eax,frm ; 5=FRM + GO_ON + lctrl_6: + mov eax,esi ; 6=CIP + sub eax,code + GO_ON + +OP_SCTRL: + mov ebp,[esi+4] + add esi,8 + cmp ebp,2 + jne short sctrl_4 + mov hea,eax ; 2=HEA + GO_ON + sctrl_4: + cmp ebp,4 + jne short sctrl_5 + mov ecx,eax ; 4=STK + GO_ON + sctrl_5: + cmp ebp,5 + jne short sctrl_6 + mov ebx,eax ; 5=FRM + mov frm,eax + add ebx,edi ; relocate FRM + sctrl_6: + GO_ON + +OP_MOVE_PRI: + add esi,4 + mov eax,edx + GO_ON + +;good +OP_MOVE_ALT: + add esi,4 + mov edx,eax + GO_ON + +OP_XCHG: + add esi,4 + xchg eax,edx + GO_ON + +;good +OP_PUSH_PRI: + add esi,4 + _PUSH eax + GO_ON + +;good +OP_PUSH_ALT: + add esi,4 + _PUSH edx + GO_ON + +OP_PUSH_R_PRI: + mov ebp,[esi+4] + add esi,8 + push_loop: + _PUSH eax + dec ebp + jnz short push_loop + GO_ON + +;good +OP_PUSH_C: + mov ebp,[esi+4] + add esi,8 + _PUSH ebp + GO_ON + +OP_PUSH: + mov ebp,[esi+4] + add esi,8 + mov ebp,[ebp+edi] + _PUSH ebp + GO_ON + +;good +OP_PUSH_S: + mov ebp,[esi+4] + add esi,8 + mov ebp,[ebp+ebx] + _PUSH ebp + GO_ON + +OP_POP_PRI: + add esi,4 + _POP eax + GO_ON + +;good +OP_POP_ALT: + add esi,4 + _POP edx + GO_ON + +;good +OP_STACK: + mov edx,ecx + add ecx,[esi+4] + _CHKMARGIN + _CHKSTACK + add esi,8 + GO_ON + +;good +OP_HEAP: + mov ebp,[esi+4] + mov edx,hea + add esi,8 + add hea,ebp + _CHKMARGIN + _CHKHEAP + GO_ON + +;good +OP_PROC: + mov ebx,frm + add esi,4 + _PUSH ebx + mov ebx,edi + mov frm,ecx + add ebx,ecx + _CHKMARGIN + GO_ON + +OP_RET: + _POP ebx + _POP esi + cmp esi,code ; verify ESI>=code + jb err_memaccess + cmp esi,codesiz ; verify ESI=code + jb err_memaccess + cmp esi,codesiz ; verify ESI 0 and 0 => 1 + GO_ON + +OP_NEG: + add esi,4 + neg eax + GO_ON + +OP_INVERT: + add esi,4 + not eax + GO_ON + +;good +OP_ADD_C: + add eax,[esi+4] + add esi,8 + GO_ON + +;good +OP_SMUL_C: + mov ebp,[esi+4] + push edx + imul ebp + pop edx + add esi,8 + GO_ON + +;good +OP_ZERO_PRI: + add esi,4 + sub eax,eax + GO_ON + +;good +OP_ZERO_ALT: + add esi,4 + sub edx,edx + GO_ON + +OP_ZERO: + mov ebp,[esi+4] + add esi,8 + mov DWORD [edi+ebp],0 + GO_ON + +OP_ZERO_S: + mov ebp,[esi+4] + add esi,8 + mov DWORD [ebx+ebp],0 + GO_ON + +OP_SIGN_PRI: + shl eax,24 + add esi,4 + sar eax,24 + GO_ON + +OP_SIGN_ALT: + shl edx,24 + add esi,4 + sar edx,24 + GO_ON + +OP_EQ: + add esi,4 + cmp eax,edx ; PRI == ALT ? + mov eax,0 + sete al + GO_ON + +OP_NEQ: + add esi,4 + cmp eax,edx ; PRI != ALT ? + mov eax,0 + setne al + GO_ON + +OP_LESS: + add esi,4 + cmp eax,edx ; PRI < ALT ? (unsigned) + mov eax,0 + setb al + GO_ON + +OP_LEQ: + add esi,4 + cmp eax,edx ; PRI <= ALT ? (unsigned) + mov eax,0 + setbe al + GO_ON + +OP_GRTR: + add esi,4 + cmp eax,edx ; PRI > ALT ? (unsigned) + mov eax,0 + seta al + GO_ON + +OP_GEQ: + add esi,4 + cmp eax,edx ; PRI >= ALT ? (unsigned) + mov eax,0 + setae al + GO_ON + +;good +OP_SLESS: + add esi,4 + cmp eax,edx ; PRI < ALT ? (signed) + mov eax,0 + setl al + GO_ON + +OP_SLEQ: + add esi,4 + cmp eax,edx ; PRI <= ALT ? (signed) + mov eax,0 + setle al + GO_ON + +OP_SGRTR: + add esi,4 + cmp eax,edx ; PRI > ALT ? (signed) + mov eax,0 + setg al + GO_ON + +OP_SGEQ: + add esi,4 + cmp eax,edx ; PRI >= ALT ? (signed) + mov eax,0 + setge al + GO_ON + +OP_EQ_C_PRI: + cmp eax,[esi+4] ; PRI == value ? + lea esi,[esi+8] + mov eax,0 + sete al + GO_ON + +OP_EQ_C_ALT: + xor eax,eax + cmp edx,[esi+4] ; ALT == value ? + lea esi,[esi+8] + sete al + GO_ON + +OP_INC_PRI: + add esi,4 + inc eax + GO_ON + +OP_INC_ALT: + add esi,4 + inc edx + GO_ON + +OP_INC: + mov ebp,[esi+4] + add esi,8 + inc DWORD [edi+ebp] + GO_ON + +;good +OP_INC_S: + mov ebp,[esi+4] + add esi,8 + inc DWORD [ebx+ebp] + GO_ON + +OP_INC_I: + add esi,4 + inc DWORD [edi+eax] + GO_ON + +OP_DEC_PRI: + add esi,4 + dec eax + GO_ON + +OP_DEC_ALT: + add esi,4 + dec edx + GO_ON + +OP_DEC: + mov ebp,[esi+4] + add esi,8 + dec DWORD [edi+ebp] + GO_ON + +OP_DEC_S: + mov ebp,[esi+4] + add esi,8 + dec DWORD [ebx+ebp] + GO_ON + +OP_DEC_I: + add esi,4 + sub DWORD [edi+eax],1 + GO_ON + +OP_MOVS: + _VERIFYADDRESS eax ; PRI + _VERIFYADDRESS edx ; ALT + mov ebp,eax + add ebp,[esi+4] + dec ebp + _VERIFYADDRESS ebp ; PRI + size - 1 + mov ebp,edx + add ebp,[esi+4] + dec ebp + _VERIFYADDRESS ebp ; ALT + size - 1 + + push ecx + mov ecx,[esi+4] + add esi,8 + push edi + push esi + lea esi,[edi+eax] + lea edi,[edi+edx] + + push ecx + shr ecx,2 + rep movsd + pop ecx + and ecx,3 + rep movsb + + pop esi + pop edi + pop ecx + GO_ON + +OP_CMPS: + _VERIFYADDRESS eax ; PRI + _VERIFYADDRESS edx ; ALT + mov ebp,eax + add ebp,[esi+4] ; size in bytes + dec ebp ; EBP = PRI + size - 1 + _VERIFYADDRESS ebp ; PRI + size - 1 + sub ebp,eax ; EBP = size - 1 + add ebp,edx ; EBP = ALT + size - 1 + _VERIFYADDRESS ebp ; ALT + size - 1 + + push ecx + mov ecx,[esi+4] + add esi,8 + push edi + push esi + lea esi,[edi+edx] + lea edi,[edi+eax] + + xor eax,eax + repe cmpsb + je short cmps1 + sbb eax,eax + sbb eax,0ffffffffh + cmps1: + pop esi + pop edi + pop ecx + GO_ON + + +OP_FILL: + mov ebp,[esi+4] ; get byte count + add esi,8 + and ebp,0fffffffch ; align to words + jz short fill_ready + _VERIFYADDRESS edx ; ALT + dec ebp ; EBP = size - 1 + add ebp,edx ; EBP = ALT + size - 1 + _VERIFYADDRESS ebp ; ALT + size - 1 + sub ebp,edx ; restore EBP + inc ebp + + push ecx + push edi + mov ecx,ebp ; ECX = count (in bytes) + lea edi,[edi+edx] ; EDI = physical starting address + shr ecx,2 ; ECX = count (in DWORDS) + rep stosd + pop edi + pop ecx + fill_ready: + GO_ON + + +OP_HALT: + cmp DWORD retval,0 + je short halt_no_retval + mov ebp,retval + mov [ebp],eax + halt_no_retval: + ; store the complete status in the AMX + mov ebp,amx ; get amx into ebp + mov [ebp+_pri],eax ; store values in AMX structure (PRI, ALT, STK, HEA, FRM, ...) + mov [ebp+_alt],edx + mov [ebp+_stk],ecx + mov ecx,hea + mov ebx,frm + mov [ebp+_hea],ecx + mov [ebp+_frm],ebx ; EBX & ECX are invalid by now + mov ebx,[esi+4] ; EBX=parameter of the HALT opcode + add esi,8 ; skip this instruction + mov eax,esi ; EAX=CIP + sub eax,code + mov [ebp+_cip],eax + mov eax,ebx ; return the parameter of the HALT opcode + jmp _return + + +OP_BOUNDS: + mov ebp,[esi+4] + add esi,8 + cmp eax,ebp + ja near err_bounds ; use unsigned comparison, so <0 is >bounds + GO_ON + + +OP_SYSREQ_C: + mov eax,[esi+4] ; get function number + add esi,4 + + +OP_SYSREQ_PRI: + mov ebp,amx ; get amx into ebp + add esi,4 + + mov stk,ecx ; save STK + mov alt,edx ; save ALT + + mov [ebp+_stk],ecx ; store values in AMX structure (STK, HEA, FRM) + mov ecx,hea + mov ebx,frm + mov [ebp+_hea],ecx + mov [ebp+_frm],ebx ; ebx & ecx are invalid by now + + mov ebx,esi ; also store CIP + sub ebx,code + mov [ebp+_cip],ebx + + mov edx,eax ; 2nd param: function number + mov eax,ebp ; 1st param: amx + mov ecx,stk + lea ebx,pri ; 3rd param: addr. of retval + add ecx,edi ; 4th param: addr. of function parameters + ; save a few registers (it is not necessary to save them all + ; and EAX should *not* be saved because it will hold the return + ; value) + push ebp + push esi + push edi + ; push the parameters + push ecx + push ebx + push edx + push eax + call [ebp+_callback] + _DROPARGS 10h ; remove arguments from stack + pop edi ; restore saved registers + pop esi + pop ebp + cmp eax,AMX_ERR_NONE + jne near _return ; return error code, if any + + mov eax,pri ; get retval into eax (PRI) + mov edx,alt ; restore ALT + mov ebx,frm + mov ecx,stk ; restore STK + add ebx,edi ; restore FRM + GO_ON + + +OP_SYSREQ_D: ; (TR) + mov ebx,[esi+4] ; get function address + mov ebp,amx ; get amx into ebp + add esi,8 + + mov stk,ecx ; save STK + mov alt,edx ; save ALT + + mov [ebp+_stk],ecx ; store values in AMX structure (STK, HEA, FRM) + mov ecx,hea + mov eax,frm + mov [ebp+_hea],ecx + mov [ebp+_frm],eax ; eax & ecx are invalid by now + + mov eax,ebp ; 1st param: amx + mov edx,stk + add edx,edi ; 2nd param: addr. of function parameters + ; save a few registers (it is not necessary to save them all + ; and EAX should *not* be saved because it will hold the return + ; value) + push ebp + push esi + push edi + ; push the parameters + push edx + push eax + call ebx ; direct call + _DROPARGS 8 ; remove arguments from stack + pop edi ; restore saved registers + pop esi + pop ebp + cmp DWORD [ebp+_error],AMX_ERR_NONE + jne near _return ; return error code, if any + + ; function result is in eax (PRI) + mov edx,alt ; restore ALT + mov ebx,frm + mov ecx,stk ; restore STK + add ebx,edi ; restore FRM + GO_ON + + +OP_FILE: + jmp OP_INVALID + + +OP_LINE: + add esi,12 + GO_ON + + +OP_SYMBOL: + add esi,[esi+4] + add esi,8 ; skip "fixed" part + GO_ON + + +OP_SRANGE: + add esi,12 + GO_ON + + +OP_SYMTAG: + add esi,8 + GO_ON + + +OP_JUMP_PRI: + mov esi,eax + GO_ON + + +OP_SWITCH: + push ecx + mov ebp,[esi+4] ; get offset of the switch table + add ebp,4 ; skip the "OP_CASETBL" opcode + mov ecx,[ebp] ; ECX = number of records + mov esi,[ebp+4] ; preset ESI to "none-matched" case + op_switch_loop: + or ecx, ecx ; number of records == 0? + jz short op_switch_end ; yes, no more records, exit loop + add ebp,8 ; skip previous record + dec ecx ; already decrement cases to do + cmp eax,[ebp] ; PRI == case label? + jne short op_switch_loop ; no, continue loop + mov esi,[ebp+4] ; yes, get jump address and exit loop + op_switch_end: + pop ecx + GO_ON + + +OP_CASETBL: + jmp OP_INVALID + + +OP_SWAP_PRI: + mov ebp,[edi+ecx] + add esi,4 + mov [edi+ecx],eax + mov eax,ebp + GO_ON + + +OP_SWAP_ALT: + mov ebp,[edi+ecx] + add esi,4 + mov [edi+ecx],edx + mov edx,ebp + GO_ON + + +OP_PUSHADDR: + mov ebp,[esi+4] + add esi,8 + add ebp,frm + _PUSH ebp + GO_ON + + +OP_NOP: + add esi,4 + GO_ON + + +OP_BREAK: + mov ebp,amx ; get amx into ebp + add esi,4 + cmp DWORD [ebp+_debug], 0 + jnz break_calldebug + GO_ON ; debug hook not active, ignore + + break_calldebug: + ; store the status in the AMX (FRM, STK, HEA, CIP, and PRI + ALT) + mov [ebp+_pri],eax + mov [ebp+_alt],edx ; EAX and EDX are now free to use + mov eax,frm + mov edx,hea + mov [ebp+_frm],eax ; store values in AMX structure (STK, FRM & HEA) + mov [ebp+_hea],edx + mov [ebp+_stk],ecx + mov eax,esi + sub eax,code ; EAX = CIP (relative to start of code segment) + mov [ebp+_cip],eax + ; call the debug hook + mov eax,ebp ; 1st parm: amx + _SAVEREGS + push eax + call [ebp+_debug] ; call debug function + _DROPARGS 4 ; remove arguments from stack + cmp eax,AMX_ERR_NONE + je short break_noabort; continue running + mov [ebp+_error],eax ; save EAX (error code) before restoring all regs + _RESTOREREGS ; abort run, but restore stack first + mov eax,[ebp+_error] ; get error code in EAX back again + jmp _return ; return error code + break_noabort: + _RESTOREREGS + mov eax,[ebp+_pri] ; restore PRI and ALT + mov edx,[ebp+_alt] + GO_ON + + +OP_INVALID: + mov eax,AMX_ERR_INVINSTR + jmp _return + +err_call: + mov eax,AMX_ERR_CALLBACK + jmp _return + +err_stack: + mov eax,AMX_ERR_STACKERR + jmp _return + +err_stacklow: + mov eax,AMX_ERR_STACKLOW + jmp _return + +err_memaccess: + mov eax,AMX_ERR_MEMACCESS + jmp _return + +err_bounds: + mov eax,AMX_ERR_BOUNDS + jmp _return + +err_heaplow: + mov eax,AMX_ERR_HEAPLOW + jmp _return + +err_divide: + mov eax,AMX_ERR_DIVIDE + jmp _return + + +_return: + ; save a few parameters, mostly for the "sleep"function + mov ebp,amx ; get amx into ebp + mov [ebp+_pri],eax ; store values in AMX structure (PRI, ALT) + mov [ebp+_alt],edx ; store values in AMX structure (PRI, ALT) + + pop esi ; remove FRM from stack + + pop ecx + pop ebx + pop edx + + pop esi ; remove pointer to amx from stack + pop esi ; remove code segment pointer + pop esi ; remove code size + + add esp,4*3 ; place for PRI, ALT & STK at SYSREQs + + pop ebp + pop esi + pop edi + pop ebx + ret + +; _amx_exec_asm ENDP + + +Start_DATA + ALIGN 4 ; This is essential to avoid misalignment stalls. + +lodb_and DD 0ffh, 0ffffh, 0, 0ffffffffh + + GLOBAL amx_opcodelist + GLOBAL _amx_opcodelist +amx_opcodelist: +_amx_opcodelist DD OP_INVALID + DD OP_LOAD_PRI + DD OP_LOAD_ALT + DD OP_LOAD_S_PRI + DD OP_LOAD_S_ALT + DD OP_LREF_PRI + DD OP_LREF_ALT + DD OP_LREF_S_PRI + DD OP_LREF_S_ALT + DD OP_LOAD_I + DD OP_LODB_I + DD OP_CONST_PRI + DD OP_CONST_ALT + DD OP_ADDR_PRI + DD OP_ADDR_ALT + DD OP_STOR_PRI + DD OP_STOR_ALT + DD OP_STOR_S_PRI + DD OP_STOR_S_ALT + DD OP_SREF_PRI + DD OP_SREF_ALT + DD OP_SREF_S_PRI + DD OP_SREF_S_ALT + DD OP_STOR_I + DD OP_STRB_I + DD OP_LIDX + DD OP_LIDX_B + DD OP_IDXADDR + DD OP_IDXADDR_B + DD OP_ALIGN_PRI + DD OP_ALIGN_ALT + DD OP_LCTRL + DD OP_SCTRL + DD OP_MOVE_PRI + DD OP_MOVE_ALT + DD OP_XCHG + DD OP_PUSH_PRI + DD OP_PUSH_ALT + DD OP_PUSH_R_PRI + DD OP_PUSH_C + DD OP_PUSH + DD OP_PUSH_S + DD OP_POP_PRI + DD OP_POP_ALT + DD OP_STACK + DD OP_HEAP + DD OP_PROC + DD OP_RET + DD OP_RETN + DD OP_CALL + DD OP_CALL_PRI + DD OP_JUMP + DD OP_JREL + DD OP_JZER + DD OP_JNZ + DD OP_JEQ + DD OP_JNEQ + DD OP_JLESS + DD OP_JLEQ + DD OP_JGRTR + DD OP_JGEQ + DD OP_JSLESS + DD OP_JSLEQ + DD OP_JSGRTR + DD OP_JSGEQ + DD OP_SHL + DD OP_SHR + DD OP_SSHR + DD OP_SHL_C_PRI + DD OP_SHL_C_ALT + DD OP_SHR_C_PRI + DD OP_SHR_C_ALT + DD OP_SMUL + DD OP_SDIV + DD OP_SDIV_ALT + DD OP_UMUL + DD OP_UDIV + DD OP_UDIV_ALT + DD OP_ADD + DD OP_SUB + DD OP_SUB_ALT + DD OP_AND + DD OP_OR + DD OP_XOR + DD OP_NOT + DD OP_NEG + DD OP_INVERT + DD OP_ADD_C + DD OP_SMUL_C + DD OP_ZERO_PRI + DD OP_ZERO_ALT + DD OP_ZERO + DD OP_ZERO_S + DD OP_SIGN_PRI + DD OP_SIGN_ALT + DD OP_EQ + DD OP_NEQ + DD OP_LESS + DD OP_LEQ + DD OP_GRTR + DD OP_GEQ + DD OP_SLESS + DD OP_SLEQ + DD OP_SGRTR + DD OP_SGEQ + DD OP_EQ_C_PRI + DD OP_EQ_C_ALT + DD OP_INC_PRI + DD OP_INC_ALT + DD OP_INC + DD OP_INC_S + DD OP_INC_I + DD OP_DEC_PRI + DD OP_DEC_ALT + DD OP_DEC + DD OP_DEC_S + DD OP_DEC_I + DD OP_MOVS + DD OP_CMPS + DD OP_FILL + DD OP_HALT + DD OP_BOUNDS + DD OP_SYSREQ_PRI + DD OP_SYSREQ_C + DD OP_FILE + DD OP_LINE + DD OP_SYMBOL + DD OP_SRANGE + DD OP_JUMP_PRI + DD OP_SWITCH + DD OP_CASETBL + DD OP_SWAP_PRI + DD OP_SWAP_ALT + DD OP_PUSHADDR + DD OP_NOP + DD OP_SYSREQ_D + DD OP_SYMTAG + DD OP_BREAK + diff --git a/server/amx/amxfile.c b/server/amx/amxfile.c new file mode 100644 index 0000000..d8164cc --- /dev/null +++ b/server/amx/amxfile.c @@ -0,0 +1,702 @@ +/* Text file I/O module for the Pawn Abstract Machine + * + * Copyright (c) ITB CompuPhase, 2003-2005 + * + * This software is provided "as-is", without any express or implied warranty. + * In no event will the authors be held liable for any damages arising from + * the use of this software. + * + * Permission is granted to anyone to use this software for any purpose, + * including commercial applications, and to alter it and redistribute it + * freely, subject to the following restrictions: + * + * 1. The origin of this software must not be misrepresented; you must not + * claim that you wrote the original software. If you use this software in + * a product, an acknowledgment in the product documentation would be + * appreciated but is not required. + * 2. Altered source versions must be plainly marked as such, and must not be + * misrepresented as being the original software. + * 3. This notice may not be removed or altered from any source distribution. + * + * Version: $Id: amxfile.c 3363 2005-07-23 09:03:29Z thiadmer $ + */ +#if defined _UNICODE || defined __UNICODE__ || defined UNICODE +# if !defined UNICODE /* for Windows */ +# define UNICODE +# endif +# if !defined _UNICODE /* for C library */ +# define _UNICODE +# endif +#endif + +#include +#include +#include +#include +#include +#if defined __WIN32__ || defined _WIN32 || defined WIN32 || defined __MSDOS__ + #include + #include +#endif +#if defined __WIN32__ || defined _WIN32 || defined WIN32 || defined _Windows + #include +#endif +#include "osdefs.h" +#include "amx.h" + +#if !defined AMXFILE_VAR + #define AMXFILE_VAR "AMXFILE" +#elif AMXFILE_VAR=="" + #undef AMXFILE_VAR +#endif + +#if defined _UNICODE +# include +#elif !defined __T + typedef char TCHAR; +# define __T(string) string +# define _tfopen fopen +# define _tgetenv getenv +# define _tfputs fputs +# define _tcscat strcat +# define _tcschr strchr +# define _tcscpy strcpy +# define _tcsdup strdup +# define _tcslen strlen +# define _tcspbrk strpbrk +# define _tcsrchr strrchr +#endif + +#if !defined UNUSED_PARAM + #define UNUSED_PARAM(p) ((void)(p)) +#endif + +enum filemode { + io_read, /* file must exist */ + io_write, /* creates a new file */ + io_readwrite, /* file must exist */ + io_append, /* file must exist, opened for writing only and seek to the end */ +}; + +enum seek_whence { + seek_start, + seek_current, + seek_end, +}; + + +/* This function only stores unpacked strings. UTF-8 is used for + * Unicode, and packed strings can only store 7-bit and 8-bit + * character sets (ASCII, Latin-1). + */ +static size_t fgets_cell(FILE *fp,cell *string,size_t max,int utf8mode) +{ + size_t index; + fpos_t pos; + cell c; + int follow,lastcr; + cell lowmark; + + assert(sizeof(cell)>=4); + assert(fp!=NULL); + assert(string!=NULL); + if (max<=0) + return 0; + + /* get the position, in case we have to back up */ + fgetpos(fp, &pos); + + index=0; + follow=0; + lowmark=0; + lastcr=0; + for ( ;; ) { + assert(index0 && (c & 0xc0)==0x80) { + /* leader code is active, combine with earlier code */ + string[index]=(string[index] << 6) | ((unsigned char)c & 0x3f); + if (--follow==0) { + /* encoding a character in more bytes than is strictly needed, + * is not really valid UTF-8; we are strict here to increase + * the chance of heuristic dectection of non-UTF-8 text + * (JAVA writes zero bytes as a 2-byte code UTF-8, which is invalid) + */ + if (string[index]=0xd800 && string[index]<=0xdfff + || string[index]==0xfffe || string[index]==0xffff) + utf8mode=0; + index++; + } /* if */ + } else if (follow==0 && (c & 0x80)==0x80) { + /* UTF-8 leader code */ + if ((c & 0xe0)==0xc0) { + /* 110xxxxx 10xxxxxx */ + follow=1; + lowmark=0x80; + string[index]=c & 0x1f; + } else if ((c & 0xf0)==0xe0) { + /* 1110xxxx 10xxxxxx 10xxxxxx (16 bits, BMP plane) */ + follow=2; + lowmark=0x800; + string[index]=c & 0x0f; + } else if ((c & 0xf8)==0xf0) { + /* 11110xxx 10xxxxxx 10xxxxxx 10xxxxxx */ + follow=3; + lowmark=0x10000; + string[index]=c & 0x07; + } else if ((c & 0xfc)==0xf8) { + /* 111110xx 10xxxxxx 10xxxxxx 10xxxxxx 10xxxxxx */ + follow=4; + lowmark=0x200000; + string[index]=c & 0x03; + } else if ((c & 0xfe)==0xfc) { + /* 1111110x 10xxxxxx 10xxxxxx 10xxxxxx 10xxxxxx 10xxxxxx (31 bits) */ + follow=5; + lowmark=0x4000000; + string[index]=c & 0x01; + } else { + /* this is invalid UTF-8 */ + utf8mode=0; + } /* if */ + } else if (follow==0 && (c & 0x80)==0x00) { + /* 0xxxxxxx (US-ASCII) */ + string[index++]=c; + if (c==__T('\n')) + break; /* read newline, done */ + } else { + /* this is invalid UTF-8 */ + utf8mode=0; + } /* if */ + if (!utf8mode) { + /* UTF-8 mode was switched just off, which means that non-conforming + * UTF-8 codes were found, which means in turn that the string is + * probably not intended as UTF-8; start over again + */ + index=0; + fsetpos(fp, &pos); + } /* if */ + } else { + string[index++]=c; + if (c==__T('\n')) { + break; /* read newline, done */ + } else if (lastcr) { + ungetc(c,fp); /* carriage return was read, no newline follows */ + break; + } /* if */ + lastcr=(c==__T('\r')); + } /* if */ + } /* for */ + assert(index=4); + assert(fp!=NULL); + assert(string!=NULL); + + while (*string!=0) { + if (utf8mode) { + cell c=*string; + if (c<0x80) { + /* 0xxxxxxx */ + fputc((unsigned char)c,fp); + } else if (c<0x800) { + /* 110xxxxx 10xxxxxx */ + fputc((unsigned char)((c>>6) & 0x1f | 0xc0),fp); + fputc((unsigned char)(c & 0x3f | 0x80),fp); + } else if (c<0x10000) { + /* 1110xxxx 10xxxxxx 10xxxxxx (16 bits, BMP plane) */ + fputc((unsigned char)((c>>12) & 0x0f | 0xe0),fp); + fputc((unsigned char)((c>>6) & 0x3f | 0x80),fp); + fputc((unsigned char)(c & 0x3f | 0x80),fp); + } else if (c<0x200000) { + /* 11110xxx 10xxxxxx 10xxxxxx 10xxxxxx */ + fputc((unsigned char)((c>>18) & 0x07 | 0xf0),fp); + fputc((unsigned char)((c>>12) & 0x3f | 0x80),fp); + fputc((unsigned char)((c>>6) & 0x3f | 0x80),fp); + fputc((unsigned char)(c & 0x3f | 0x80),fp); + } else if (c<0x4000000) { + /* 111110xx 10xxxxxx 10xxxxxx 10xxxxxx 10xxxxxx */ + fputc((unsigned char)((c>>24) & 0x03 | 0xf8),fp); + fputc((unsigned char)((c>>18) & 0x3f | 0x80),fp); + fputc((unsigned char)((c>>12) & 0x3f | 0x80),fp); + fputc((unsigned char)((c>>6) & 0x3f | 0x80),fp); + fputc((unsigned char)(c & 0x3f | 0x80),fp); + } else { + /* 1111110x 10xxxxxx 10xxxxxx 10xxxxxx 10xxxxxx 10xxxxxx (31 bits) */ + fputc((unsigned char)((c>>30) & 0x01 | 0xfc),fp); + fputc((unsigned char)((c>>24) & 0x3f | 0x80),fp); + fputc((unsigned char)((c>>18) & 0x3f | 0x80),fp); + fputc((unsigned char)((c>>12) & 0x3f | 0x80),fp); + fputc((unsigned char)((c>>6) & 0x3f | 0x80),fp); + fputc((unsigned char)(c & 0x3f | 0x80),fp); + } /* if */ + } else { + /* not UTF-8 mode */ + fputc((unsigned char)*string,fp); + } /* if */ + string++; + count++; + } /* while */ + return count; +} + +static size_t fgets_char(FILE *fp, char *string, size_t max) +{ + size_t index; + int c,lastcr; + + index=0; + lastcr=0; + for ( ;; ) { + assert(index=size) /* +1 because directory separator is appended */ + return NULL; + _tcscpy(dest,prefix); + /* append a directory separator (if not already present) */ + len=_tcslen(dest); + if (len==0) + return NULL; /* empty start directory is not allowed */ + if (dest[len-1]!=__T(DIRSEP_CHAR) && dest[len-1]!=__T('/') && len+1=size) + return NULL; + _tcscat(dest,src); + + /* change forward slashes into proper directory separators */ + #if DIRSEP_CHAR!='/' + while ((ptr=_tcschr(dest,__T('/')))!=NULL) + *ptr=__T(DIRSEP_CHAR); + #endif + return dest; + + #else + if (_tcslen(src)>=size) + return NULL; + _tcscpy(dest,src); + /* change forward slashes into proper directory separators */ + #if DIRSEP_CHAR!='/' + while ((ptr=_tcschr(dest,__T('/')))!=NULL) + *ptr=__T(DIRSEP_CHAR); + #endif + return dest; + #endif +} + +/* File: fopen(const name[], filemode: mode) */ +static cell AMX_NATIVE_CALL n_fopen(AMX *amx, cell *params) +{ + TCHAR *attrib,*altattrib; + TCHAR *name,fullname[_MAX_PATH]; + FILE *f = NULL; + + altattrib=NULL; + switch (params[2] & 0x7fff) { + case io_read: + attrib=__T("rb"); + break; + case io_write: + attrib=__T("wb"); + break; + case io_readwrite: + attrib=__T("r+b"); + altattrib=__T("w+b"); + break; + case io_append: + attrib=__T("ab"); + break; + default: + return 0; + } /* switch */ + + /* get the filename */ + amx_StrParam(amx,params[1],name); + if (name!=NULL && completename(fullname,name,sizeof fullname)!=NULL) { + f=_tfopen(fullname,attrib); + if (f==NULL && altattrib!=NULL) + f=_tfopen(fullname,altattrib); + } /* if */ + return (cell)f; +} + +/* fclose(File: handle) */ +static cell AMX_NATIVE_CALL n_fclose(AMX *amx, cell *params) +{ + UNUSED_PARAM(amx); + return fclose((FILE*)params[1]) == 0; +} + +/* fwrite(File: handle, const string[]) */ +static cell AMX_NATIVE_CALL n_fwrite(AMX *amx, cell *params) +{ + int r = 0; + cell *cptr; + char *str; + int len; + + amx_GetAddr(amx,params[2],&cptr); + amx_StrLen(cptr,&len); + if (len==0) + return 0; + + if ((ucell)*cptr>UNPACKEDMAX) { + /* the string is packed, write it as an ASCII/ANSI string */ + if ((str=(char*)alloca(len + 1))!=NULL) + r=_tfputs(str,(FILE*)params[1]); + } else { + /* the string is unpacked, write it as UTF-8 */ + r=fputs_cell((FILE*)params[1],cptr,1); + } /* if */ + return r; +} + +/* fread(File: handle, string[], size=sizeof string, bool:pack=false) */ +static cell AMX_NATIVE_CALL n_fread(AMX *amx, cell *params) +{ + int chars,max; + char *str; + cell *cptr; + + max=(int)params[3]; + if (max<=0) + return 0; + if (params[4]) + max*=sizeof(cell); + + amx_GetAddr(amx,params[2],&cptr); + str=(char *)alloca(max); + if (str==NULL || cptr==NULL) { + amx_RaiseError(amx, AMX_ERR_NATIVE); + return 0; + } /* if */ + + if (params[4]) { + /* store as packed string, read an ASCII/ANSI string */ + chars=fgets_char((FILE*)params[1],str,max); + assert(chars0) + // params[2]=-params[2]; + break; + default: + return 0; + } /* switch */ + UNUSED_PARAM(amx); + return lseek(fileno((FILE*)params[1]),params[2],whence); +} + +/* bool: fexist(const name[]) */ +static cell AMX_NATIVE_CALL n_fexist(AMX *amx, cell *params) +{ + int r=1; + TCHAR *name,fullname[_MAX_PATH]; + + amx_StrParam(amx,params[1],name); + if (name!=NULL && completename(fullname,name,sizeof fullname)!=NULL) + r=access(fullname,0); + return r==0; +} + +/* bool: fremove(const name[]) */ +static cell AMX_NATIVE_CALL n_fremove(AMX *amx, cell *params) +{ + int r=1; + TCHAR *name,fullname[_MAX_PATH]; + + amx_StrParam(amx,params[1],name); + if (name!=NULL && completename(fullname,name,sizeof fullname)!=NULL) + r=remove(fullname); + return r==0; +} + +/* flength(File: handle) */ +static cell AMX_NATIVE_CALL n_flength(AMX *amx, cell *params) +{ + long l,c; + int fn=fileno((FILE*)params[1]); + c=lseek(fn,0,SEEK_CUR); /* save the current position */ + l=lseek(fn,0,SEEK_END); /* return the file position at its end */ + lseek(fn,c,SEEK_SET); /* restore the file pointer */ + UNUSED_PARAM(amx); + return l; +} + + +#if defined __cplusplus + extern "C" +#endif +AMX_NATIVE_INFO file_Natives[] = { + { "fopen", n_fopen }, + { "fclose", n_fclose }, + { "fwrite", n_fwrite }, + { "fread", n_fread }, + { "fputchar", n_fputchar }, + { "fgetchar", n_fgetchar }, + { "fblockwrite", n_fblockwrite }, + { "fblockread", n_fblockread }, + { "ftemp", n_ftemp }, + { "fseek", n_fseek }, + { "fexist", n_fexist }, + { "flength", n_flength }, + { "fremove", n_fremove }, + { NULL, NULL } /* terminator */ +}; + +int AMXEXPORT amx_FileInit(AMX *amx) +{ + return amx_Register(amx, file_Natives, -1); +} + +int AMXEXPORT amx_FileCleanup(AMX *amx) +{ + UNUSED_PARAM(amx); + return AMX_ERR_NONE; +} diff --git a/server/amx/amxstring.c b/server/amx/amxstring.c new file mode 100644 index 0000000..aca62d4 --- /dev/null +++ b/server/amx/amxstring.c @@ -0,0 +1,791 @@ +/* String functions for the Pawn Abstract Machine + * + * Copyright (c) ITB CompuPhase, 2005 + * + * This software is provided "as-is", without any express or implied warranty. + * In no event will the authors be held liable for any damages arising from + * the use of this software. + * + * Permission is granted to anyone to use this software for any purpose, + * including commercial applications, and to alter it and redistribute it + * freely, subject to the following restrictions: + * + * 1. The origin of this software must not be misrepresented; you must not + * claim that you wrote the original software. If you use this software in + * a product, an acknowledgment in the product documentation would be + * appreciated but is not required. + * 2. Altered source versions must be plainly marked as such, and must not be + * misrepresented as being the original software. + * 3. This notice may not be removed or altered from any source distribution. + * + * Version: $Id: amxstring.c 3363 2005-07-23 09:03:29Z thiadmer $ + */ +#include +#include +#include +#include +#if defined __WIN32__ || defined _WIN32 || defined WIN32 || defined __MSDOS__ + #include +#endif +#include "osdefs.h" +#include "amx.h" +#if defined __WIN32__ || defined _WIN32 || defined WIN32 || defined _Windows + #include +#endif + + +#define CHARBITS (8*sizeof(char)) + +/* dest the destination buffer; the buffer must point to the start of a cell + * source the source buffer, this must be aligned to a cell edge + * len the number of characters (bytes) to copy + * offs the offset in dest, in characters (bytes) + */ +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; iUNPACKEDMAX) { + /* 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> (offs*CHARBITS)) & mask); + c=(*source << ((sizeof(cell)-offs)*CHARBITS)) & ~mask; + dest++; + source++; + } /* for */ + /* add the final cell, but only if there is not already a terminating zero + * in dest + */ + if ((*(dest-1) & 0xff)!=0) { + c &= ~0xff; /* force a terminating zero, for security */ + *dest=c; + } /* if */ + } 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) + */ + assert(offs>=0 && offsUNPACKEDMAX) { + /* 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; +} + +static unsigned char *packedptr(cell *string,int index) +{ + unsigned char *ptr=(unsigned char *)(string+index/sizeof(cell)); + #if BYTE_ORDER==BIG_ENDIAN + ptr+=index & (sizeof(cell)-1); + #else + ptr+=(sizeof(cell)-1) - (index & (sizeof(cell)-1)); + #endif + return ptr; +} + +static cell extractchar(cell *string,int index,int mklower) +{ + cell c; + + if ((ucell)*string>UNPACKEDMAX) + c=*packedptr(string,index); + else + c=string[index]; + if (mklower) { + #if defined __WIN32__ || defined _WIN32 || defined WIN32 + c=(cell)CharLower((LPTSTR)c); + #elif defined _Windows + c=(cell)AnsiLower((LPSTR)c); + #else + c=tolower((int)c); + #endif + } /* if */ + return c; +} + +static int verify_addr(AMX *amx,cell addr) +{ + int err; + cell *cdest; + + err=amx_GetAddr(amx,addr,&cdest); + if (err!=AMX_ERR_NONE) + amx_RaiseError(amx,err); + return err; +} + +/* strlen(const string[]) + */ +static cell AMX_NATIVE_CALL n_strlen(AMX *amx,cell *params) +{ + cell *cptr; + int len = 0; + + if (amx_GetAddr(amx,params[1],&cptr)==AMX_ERR_NONE) + amx_StrLen(cptr,&len); + return len; +} + +/* strpack(dest[], const source[], maxlength=sizeof dest) + */ +static cell AMX_NATIVE_CALL n_strpack(AMX *amx,cell *params) +{ + cell *cdest,*csrc; + int len,needed,err; + size_t lastaddr; + + /* calculate number of cells needed for (packed) destination */ + amx_GetAddr(amx,params[2],&csrc); + amx_StrLen(csrc,&len); + if ((unsigned)len>params[3]*sizeof(cell)-1) + len=params[3]*sizeof(cell)-1; + needed=(len+sizeof(cell))/sizeof(cell); /* # of cells needed */ + assert(needed>0); + lastaddr=(size_t)(params[1]+sizeof(cell)*needed-1); + if (verify_addr(amx,(cell)lastaddr)!=AMX_ERR_NONE) + return amx_RaiseError(amx,AMX_ERR_NATIVE); + + amx_GetAddr(amx,params[1],&cdest); + err=amx_StrPack(cdest,csrc,len,0); + if (err!=AMX_ERR_NONE) + return amx_RaiseError(amx,err); + + return len; +} + +/* strunpack(dest[], const source[], maxlength=sizeof dest) + */ +static cell AMX_NATIVE_CALL n_strunpack(AMX *amx,cell *params) +{ + cell *cdest,*csrc; + int len,err; + size_t lastaddr; + + /* calculate number of cells needed for (unpacked) destination */ + amx_GetAddr(amx,params[2],&csrc); + amx_StrLen(csrc,&len); + assert(len>=0); + if (len>=params[3]) + len=params[3]-1; + lastaddr=(size_t)(params[1]+sizeof(cell)*(len+1)-1); + if (verify_addr(amx,(cell)lastaddr)!=AMX_ERR_NONE) + return amx_RaiseError(amx,AMX_ERR_NATIVE); + + amx_GetAddr(amx,params[1],&cdest); + err=amx_StrUnpack(cdest,csrc,len); + if (err!=AMX_ERR_NONE) + return amx_RaiseError(amx,err); + + return len; +} + +/* strcat(dest[], const source[], maxlength=sizeof dest) + * packed/unpacked attribute is taken from dest[], or from source[] if dest[] + * is an empty string. + */ +static cell AMX_NATIVE_CALL n_strcat(AMX *amx,cell *params) +{ + cell *cdest,*csrc; + int len,len2,needed; + int packed,err; + size_t lastaddr; + + /* calculate number of cells needed for (packed) destination */ + amx_GetAddr(amx,params[2],&csrc); + amx_GetAddr(amx,params[1],&cdest); + amx_StrLen(csrc,&len); + amx_StrLen(cdest,&len2); + packed=(*cdest==0) ? ((ucell)*csrc>UNPACKEDMAX) : ((ucell)*cdest>UNPACKEDMAX); + if (packed) { + if ((unsigned)(len+len2)>params[3]*sizeof(cell)-1) + len=params[3]*sizeof(cell)-len2-1; + needed=(len+len2+sizeof(cell))/sizeof(cell); /* # of cells needed */ + assert(needed>0); + lastaddr=(size_t)(params[1]+sizeof(cell)*needed-1); + } else { + if (len+len2>params[3]-1) + len=params[3]-len2-1; + lastaddr=(size_t)(params[1]+sizeof(cell)*(len+len2+1)-1); + } /* if */ + if (verify_addr(amx,(cell)lastaddr)!=AMX_ERR_NONE) + return amx_RaiseError(amx,AMX_ERR_NATIVE); + + if (packed) { + err=amx_StrPack(cdest,csrc,len,len2); + } else { + /* destination string must either be unpacked, or empty */ + assert((ucell)*cdest<=UNPACKEDMAX || len2==0); + err=amx_StrUnpack(cdest+len2,csrc,len); + } /* if */ + if (err!=AMX_ERR_NONE) + return amx_RaiseError(amx,err); + + return len; +} + +static int compare(cell *cstr1,cell *cstr2,int ignorecase,int length,int offs1) +{ + int index; + cell c1=0,c2=0; + + for (index=0; indexc2) + return 1; + return 0; +} + +/* strcmp(const string1[], const string2[], bool:ignorecase=false, length=cellmax) + */ +static cell AMX_NATIVE_CALL n_strcmp(AMX *amx,cell *params) +{ + cell *cstr1,*cstr2; + int len1,len2,len; + cell result; + + amx_GetAddr(amx,params[1],&cstr1); + amx_GetAddr(amx,params[2],&cstr2); + + /* get the maximum length to compare */ + amx_StrLen(cstr1,&len1); + amx_StrLen(cstr2,&len2); + len=len1; + if (len>len2) + len=len2; + if (len>params[4]) + len=params[4]; + if (len==0) + return 0; + + result=compare(cstr1,cstr2,params[3],len,0); + if (result==0 && len!=params[4]) + result=len1-len2; + return result; +} + +/* strfind(const string[], const sub[], bool:ignorecase=false, offset=0) + */ +static cell AMX_NATIVE_CALL n_strfind(AMX *amx,cell *params) +{ + cell *cstr,*csub; + int lenstr,lensub,offs; + cell c,f; + + amx_GetAddr(amx,params[1],&cstr); + amx_GetAddr(amx,params[2],&csub); + + /* get the maximum length to compare */ + amx_StrLen(cstr,&lenstr); + amx_StrLen(csub,&lensub); + if (lensub==0) + return -1; + + /* get the start character of the substring, for quicker searching */ + f=extractchar(csub,0,params[3]); + assert(f!=0); /* string length is already checked */ + + for (offs=(int)params[4]; offs+lensub<=lenstr; offs++) { + /* find the initial character */ + c=extractchar(csub,0,params[3]); + assert(c!=0); /* string length is already checked */ + if (c!=f) + continue; + if (compare(cstr,csub,params[3],lensub,offs)==0) + return offs; + } /* for */ + return -1; +} + +/* strmid(dest[], const source[], start, end, maxlength=sizeof dest) + * packed/unpacked attribute is taken from source[] + */ +static cell AMX_NATIVE_CALL n_strmid(AMX *amx,cell *params) +{ + cell *cdest,*csrc; + int len,needed,err; + int soffs,doffs; + size_t lastaddr; + unsigned char *ptr; + unsigned char c; + + /* calculate number of cells needed for (packed) destination */ + amx_GetAddr(amx,params[2],&csrc); + amx_GetAddr(amx,params[1],&cdest); + amx_StrLen(csrc,&len); + + /* clamp the start/end parameters */ + if (params[3]<0) + params[3]=0; + else if (params[3]>len) + params[3]=len; + if (params[4]len) + params[4]=len; + else if (params[3]>params[4]) + params[3]=params[4]; + + len=params[4]-params[3]; + if ((ucell)*csrc>UNPACKEDMAX) { + if ((unsigned)len>params[5]*sizeof(cell)-1) + len=params[5]*sizeof(cell)-1; + needed=(len+sizeof(cell))/sizeof(cell); /* # of cells needed */ + assert(needed>0); + lastaddr=(size_t)(params[1]+sizeof(cell)*needed-1); + } else { + if (len>params[5]-1) + len=params[5]-1; + lastaddr=(size_t)(params[1]+sizeof(cell)*(len+1)-1); + } /* if */ + if (verify_addr(amx,(cell)lastaddr)!=AMX_ERR_NONE) + return amx_RaiseError(amx,AMX_ERR_NATIVE); + + if ((ucell)*csrc>UNPACKEDMAX) { + /* first align the source to a cell boundary */ + for (doffs=0,soffs=(int)params[3]; (soffs & (sizeof(cell)-1))!=0 && len>1; soffs++,doffs++,len--) { + ptr=packedptr(csrc,soffs); + c=*ptr; + ptr=packedptr(cdest,doffs); + *ptr=c; + } /* for */ + err=amx_StrPack(cdest,csrc+soffs/sizeof(cell),len,doffs); + } else { + err=amx_StrUnpack(cdest,csrc+params[3],len); + } /* if */ + if (err!=AMX_ERR_NONE) + return amx_RaiseError(amx,err); + + return len; +} + +/* strdel(string[], start, end) + */ +static cell AMX_NATIVE_CALL n_strdel(AMX *amx,cell *params) +{ + cell *cstr; + int index,offs,length; + unsigned char *ptr; + unsigned char c; + + /* calculate number of cells needed for (packed) destination */ + amx_GetAddr(amx,params[1],&cstr); + amx_StrLen(cstr,&length); + index=(int)params[2]; + offs=(int)params[3]-index; + if (index>=length || offs<=0) + return 0; + if (index+offs>length) + offs=length-index; + + index--; /* prepare for increment in the top of the loop */ + if (((ucell)*cstr>UNPACKEDMAX)) { + do { + index++; + ptr=packedptr(cstr,index+offs); + c=*ptr; + ptr=packedptr(cstr,index); + *ptr=c; + } while (c!='\0'); + if (index==0) + *cstr=0; + } else { + do { + index++; + cstr[index]=cstr[index+offs]; + } while (cstr[index]!=0); + } /* if */ + + return 1; +} + +/* strins(string[], const substr[], offset, maxlength=sizeof string) + */ +static cell AMX_NATIVE_CALL n_strins(AMX *amx,cell *params) +{ + cell *cstr,*csub; + int index,lenstr,lensub,count; + int needed; + size_t lastaddr; + unsigned char *ptr; + cell c; + + /* calculate number of cells needed for (packed) destination */ + amx_GetAddr(amx,params[1],&cstr); + amx_GetAddr(amx,params[2],&csub); + amx_StrLen(cstr,&lenstr); + amx_StrLen(csub,&lensub); + index=(int)params[3]; + if (index>lenstr) + return amx_RaiseError(amx,AMX_ERR_NATIVE); + + if (((ucell)*cstr>UNPACKEDMAX)) { + needed=(lenstr+lensub+sizeof(cell))/sizeof(cell); /* # of cells needed */ + assert(needed>0); + lastaddr=(size_t)(params[1]+sizeof(cell)*needed-1); + } else { + lastaddr=(size_t)(params[1]+sizeof(cell)*(lenstr+lensub+1)-1); + } /* if */ + if (verify_addr(amx,(cell)lastaddr)!=AMX_ERR_NONE) + return amx_RaiseError(amx,AMX_ERR_NATIVE); + + if (*cstr==0) { + /* current string is empty (and the insertion point is zero), just make a copy */ + assert(index==0); + if ((ucell)*csub>UNPACKEDMAX) + amx_StrPack(cstr,csub,lensub,0); + else + amx_StrUnpack(cstr,csub,lensub); + return 1; + } /* if */ + + if (((ucell)*cstr>UNPACKEDMAX)) { + /* make room for the new characters */ + for (count=lenstr+lensub; count>index; count--) { + ptr=packedptr(cstr,count-lensub); + c=*ptr; + ptr=packedptr(cstr,count); + *ptr=(unsigned char)c; + } /* for */ + /* copy in the new characters */ + for (count=0; countindex; count--) + cstr[count]=cstr[count-lensub]; + /* copy in the new characters */ + for (count=0; count=50) { + amx_RaiseError(amx,AMX_ERR_NATIVE); + return 0; + } /* if */ + amx_GetString(str,cstr,0,sizeof str); + ptr=str; + result=0; + while (*ptr!='\0' && *ptr<=' ') + ptr++; /* skip whitespace */ + if (*ptr=='-') { /* handle sign */ + negate=1; + ptr++; + } else if (*ptr=='+') { + ptr++; + } /* if */ + while (isdigit(*ptr)) { + result=result*10 + (*ptr-'0'); + ptr++; + } /* while */ + if (negate) + result=-result; + return result; +} + +/* valstr(dest[], value, bool:pack=false) */ +static cell AMX_NATIVE_CALL n_valstr(AMX *amx,cell *params) +{ + char str[50]; + cell value,mult; + cell *cstr; + int len,result,negate=0; + + /* find out how many digits are needed */ + mult=10; + len=1; + value=params[2]; + if (value<0) { + negate=1; + len++; + value=-value; + } /* if */ + while (value>=mult) { + len++; + mult*=10; + } /* while */ + + /* put in the string */ + result=len; + str[len--]='\0'; + while (len>=negate) { + str[len--]=(char)((value % 10)+'0'); + value/=10; + } /* while */ + if (negate) + str[0]='-'; + amx_GetAddr(amx,params[1],&cstr); + amx_SetString(cstr,str,params[3],0,UNLIMITED); + return result; +} + +/* ispacked(const string[]) */ +static cell AMX_NATIVE_CALL n_ispacked(AMX *amx,cell *params) +{ + cell *cstr; + amx_GetAddr(amx,params[1],&cstr); + return *cstr>=UNPACKEDMAX; +} + + +/* single character decode and encode */ +#define BITMASK 0x3f +#define DEC(c) (((c) - ' ') & BITMASK) +#define ENC(c) (char)(((c) & BITMASK) == 0 ? 0x60 : ((c) & BITMASK) + ' ') + +static int uudecode(unsigned char *target, char *source) +{ + int len, retval; + + len = DEC(*source++); + retval = len; + while (len > 0) { + if (len-- > 0) + *target++ = (unsigned char)(( DEC(source[0]) << 2 ) | ( DEC(source[1]) >> 4 )); + if (len-- > 0) + *target++ = (unsigned char)(( DEC(source[1]) << 4 ) | ( DEC(source[2]) >> 2 )); + if (len-- > 0) + *target++ = (unsigned char)(( DEC(source[2]) << 6 ) | DEC(source[3]) ); + source += 4; + } /* while */ + return retval; +} + +static int uuencode(char *target, unsigned char *source, int length) +{ + int split[4]; + + if (length > BITMASK) + return 0; /* can encode up to 64 bytes */ + + *target++ = ENC(length); + while (length > 0) { + split[0] = source[0] >> 2; /* split first byte to char. 0 & 1 */ + split[1] = source[0] << 4; + if (length > 1) { + split[1] |= source[1] >> 4; /* split 2nd byte to char. 1 & 2 */ + split[2] = source[1] << 2; + if (length > 2) { + split[2] |= source[2] >> 6; /* split 3th byte to char. 2 & 3 */ + split[3] = source[2]; + } /* if */ + } /* if */ + + *target++ = ENC(split[0]); + *target++ = ENC(split[1]); + if (length > 1) + *target++ = ENC(split[2]); + if (length > 2) + *target++ = ENC(split[3]); + source += 3; + length -= 3; + } /* while */ + + *target = '\0'; /* end string */ + return 1; +} + +/* uudecode(dest[], const source[], maxlength=sizeof dest) + * Returns the number of bytes (not cells) decoded; if the dest buffer is + * too small, not all bytes are stored. + * Always creates a (packed) array (not a string; the array is not + * zero-terminated). + * A buffer may be decoded "in-place"; the destination size is always smaller + * than the source size. + * Endian issues (for multi-byte values in the data stream) are not handled. + */ +static cell AMX_NATIVE_CALL n_uudecode(AMX *amx,cell *params) +{ + cell *cstr; + unsigned char dst[BITMASK+2]; + char src[BITMASK+BITMASK/3+2]; + int len; + size_t size; + + /* get the source */ + amx_GetAddr(amx,params[2],&cstr); + amx_GetString(src,cstr,0,sizeof src); + /* decode */ + len=uudecode(dst,src); + /* store */ + amx_GetAddr(amx,params[1],&cstr); + size=len; + if (size>params[3]*sizeof(cell)) + size=params[3]*sizeof(cell); + memcpy(cstr,dst,size); + return len; +} + +/* uuencode(dest[], const source[], numbytes, maxlength=sizeof dest) + * Returns the number of characters encoded, excluding the zero string + * terminator; if the dest buffer is too small, not all bytes are stored. + * Always creates a packed string. This string has a newline character at the + * end. A buffer may be encoded "in-place" if the destination is large enough. + * Endian issues (for multi-byte values in the data stream) are not handled. + */ +static cell AMX_NATIVE_CALL n_uuencode(AMX *amx,cell *params) +{ + cell *cstr; + unsigned char src[BITMASK+2]; + char dst[BITMASK+BITMASK/3+2]; + + /* get the source */ + amx_GetAddr(amx,params[2],&cstr); + /* encode (and check for errors) */ + if (uuencode(dst,src,params[3])) { + if (params[4]>0) { + amx_GetAddr(amx,params[1],&cstr); + *cstr=0; + } /* if */ + return 0; + } /* if */ + /* always add a \n */ + assert(strlen(dst)+1params[5]*(int)sizeof(cell)) + return 0; + amx_GetAddr(amx,params[1],&cdest); + amx_GetAddr(amx,params[2],&csrc); + pdest=(unsigned char*)cdest+params[3]; + psrc=(unsigned char*)csrc; + memmove(pdest,psrc,params[4]); + return 1; +} + +#if defined __cplusplus + extern "C" +#endif +const AMX_NATIVE_INFO string_Natives[] = { + { "ispacked", n_ispacked }, + { "memcpy", n_memcpy }, + { "strcat", n_strcat }, + { "strcmp", n_strcmp }, + { "strdel", n_strdel }, + { "strfind", n_strfind }, + { "strins", n_strins }, + { "strlen", n_strlen }, + { "strmid", n_strmid }, + { "strpack", n_strpack }, + { "strunpack", n_strunpack }, + { "strval", n_strval }, + { "uudecode", n_uudecode }, + { "uuencode", n_uuencode }, + { "valstr", n_valstr }, + { NULL, NULL } /* terminator */ +}; + +int AMXEXPORT amx_StringInit(AMX *amx) +{ + return amx_Register(amx, string_Natives, -1); +} + +int AMXEXPORT amx_StringCleanup(AMX *amx) +{ + (void)amx; + return AMX_ERR_NONE; +} diff --git a/server/amx/getch.c b/server/amx/getch.c new file mode 100644 index 0000000..5ffe9bb --- /dev/null +++ b/server/amx/getch.c @@ -0,0 +1,101 @@ +/* Extremely inefficient but portable POSIX getch() */ + +#include +#include +#include /* for tcgetattr() and tcsetattr() */ +#include /* for read() */ +#include +#include +#include +#include "getch.h" + +#ifndef STDIN_FILENO +# define STDIN_FILENO 0 +#endif + +int +getch (void) +{ + struct termios save_termios; + struct termios ios; + int c = 0; + + if (!isatty (STDIN_FILENO)) + return EOF; + + if (tcgetattr (STDIN_FILENO, &save_termios) < 0) + return EOF; + + ios = save_termios; + ios.c_lflag &= ~(ICANON | ECHO | ISIG); + ios.c_cc[VMIN] = 1; /* read() will return with one char */ + ios.c_cc[VTIME] = 0; /* read() blocks forever */ + + if (tcsetattr (STDIN_FILENO, TCSANOW, &ios) < 0) + return EOF; + + if (read (STDIN_FILENO, &c, 1) != 1) + c = EOF; + + tcsetattr (STDIN_FILENO, TCSANOW, &save_termios); + + return c; +} + +int +kbhit (void) +{ + struct termios save_termios; + struct termios ios; + fd_set inp; + struct timeval timeout = {0, 0}; + int result; + + if (!isatty (STDIN_FILENO)) + return 0; + + if (tcgetattr (STDIN_FILENO, &save_termios) < 0) + return 0; + + ios = save_termios; + ios.c_lflag &= ~(ICANON | ECHO | ISIG); + ios.c_cc[VMIN] = 1; /* read() will return with one char */ + ios.c_cc[VTIME] = 0; /* read() blocks forever */ + + if (tcsetattr (STDIN_FILENO, TCSANOW, &ios) < 0) + return 0; + + /* set up select() args */ + FD_ZERO(&inp); + FD_SET(STDIN_FILENO, &inp); + + result = select (STDIN_FILENO+1, &inp, NULL, NULL, &timeout) == 1; + + tcsetattr (STDIN_FILENO, TCSANOW, &save_termios); + + return result; +} + +#if defined TEST_GETCH +int +main () +{ + int i; + char c[4]; + + printf("Enter %d keys to continue: ", sizeof c - 1); + fflush (stdout); + + memset(c, 0, sizeof c); + for (i=0; i + * Hongli Lai + * http://autopackage.org/ + * + * This source code is public domain. You can relicense this code + * under whatever license you want. + * + * NOTE: if you're using C++ and are getting "undefined reference + * to br_*", try renaming prefix.c to prefix.cpp + */ + +/* WARNING, BEFORE YOU MODIFY PREFIX.C: + * + * If you make changes to any of the functions in prefix.c, you MUST + * change the BR_NAMESPACE macro (in prefix.h). + * This way you can avoid symbol table conflicts with other libraries + * that also happen to use BinReloc. + * + * Example: + * #define BR_NAMESPACE(funcName) foobar_ ## funcName + * --> expands br_locate to foobar_br_locate + */ + +#ifndef _PREFIX_C_ +#define _PREFIX_C_ + +#ifdef HAVE_CONFIG_H + #include "config.h" +#endif + +#ifndef BR_PTHREADS + /* Change 1 to 0 if you don't want pthread support */ + #define BR_PTHREADS 1 +#endif /* BR_PTHREADS */ + +#include +#include +#include +#include +#include "prefix.h" + +#ifdef __cplusplus +extern "C" { +#endif /* __cplusplus */ + + +#undef NULL +#define NULL ((void *) 0) + +#ifdef __GNUC__ + #define br_return_val_if_fail(expr,val) if (!(expr)) {fprintf (stderr, "** BinReloc (%s): assertion %s failed\n", __PRETTY_FUNCTION__, #expr); return val;} +#else + #define br_return_val_if_fail(expr,val) if (!(expr)) return val +#endif /* __GNUC__ */ + + +static br_locate_fallback_func fallback_func = (br_locate_fallback_func) NULL; +static void *fallback_data = NULL; + + +#ifdef ENABLE_BINRELOC +#include +#include +#include +#include + + +/** + * br_locate: + * symbol: A symbol that belongs to the app/library you want to locate. + * Returns: A newly allocated string containing the full path of the + * app/library that func belongs to, or NULL on error. This + * string should be freed when not when no longer needed. + * + * Finds out to which application or library symbol belongs, then locate + * the full path of that application or library. + * Note that symbol cannot be a pointer to a function. That will not work. + * + * Example: + * --> main.c + * #include "prefix.h" + * #include "libfoo.h" + * + * int main (int argc, char *argv[]) { + * printf ("Full path of this app: %s\n", br_locate (&argc)); + * libfoo_start (); + * return 0; + * } + * + * --> libfoo.c starts here + * #include "prefix.h" + * + * void libfoo_start () { + * --> "" is a symbol that belongs to libfoo (because it's called + * --> from libfoo_start()); that's why this works. + * printf ("libfoo is located in: %s\n", br_locate ("")); + * } + */ +char * +br_locate (void *symbol) +{ + char line[5000]; + FILE *f; + char *path; + + br_return_val_if_fail (symbol != NULL, NULL); + + f = fopen ("/proc/self/maps", "r"); + if (!f) { + if (fallback_func) + return fallback_func(symbol, fallback_data); + else + return NULL; + } + + while (!feof (f)) + { + unsigned long start, end; + + if (!fgets (line, sizeof (line), f)) + continue; + if (!strstr (line, " r-xp ") || !strchr (line, '/')) + continue; + + sscanf (line, "%lx-%lx ", &start, &end); + if (symbol >= (void *) start && symbol < (void *) end) + { + char *tmp; + size_t len; + + /* Extract the filename; it is always an absolute path */ + path = strchr (line, '/'); + + /* Get rid of the newline */ + tmp = strrchr (path, '\n'); + if (tmp) *tmp = 0; + + /* Get rid of "(deleted)" */ + len = strlen (path); + if (len > 10 && strcmp (path + len - 10, " (deleted)") == 0) + { + tmp = path + len - 10; + *tmp = 0; + } + + fclose(f); + return strdup (path); + } + } + + fclose (f); + return NULL; +} + + +/** + * br_locate_prefix: + * symbol: A symbol that belongs to the app/library you want to locate. + * Returns: A prefix. This string should be freed when no longer needed. + * + * Locates the full path of the app/library that symbol belongs to, and return + * the prefix of that path, or NULL on error. + * Note that symbol cannot be a pointer to a function. That will not work. + * + * Example: + * --> This application is located in /usr/bin/foo + * br_locate_prefix (&argc); --> returns: "/usr" + */ +char * +br_locate_prefix (void *symbol) +{ + char *path, *prefix; + + br_return_val_if_fail (symbol != NULL, NULL); + + path = br_locate (symbol); + if (!path) return NULL; + + prefix = br_extract_prefix (path); + free (path); + return prefix; +} + + +/** + * br_prepend_prefix: + * symbol: A symbol that belongs to the app/library you want to locate. + * path: The path that you want to prepend the prefix to. + * Returns: The new path, or NULL on error. This string should be freed when no + * longer needed. + * + * Gets the prefix of the app/library that symbol belongs to. Prepend that prefix to path. + * Note that symbol cannot be a pointer to a function. That will not work. + * + * Example: + * --> The application is /usr/bin/foo + * br_prepend_prefix (&argc, "/share/foo/data.png"); --> Returns "/usr/share/foo/data.png" + */ +char * +br_prepend_prefix (void *symbol, char *path) +{ + char *tmp, *newpath; + + br_return_val_if_fail (symbol != NULL, NULL); + br_return_val_if_fail (path != NULL, NULL); + + tmp = br_locate_prefix (symbol); + if (!tmp) return NULL; + + if (strcmp (tmp, "/") == 0) + newpath = strdup (path); + else + newpath = br_strcat (tmp, path); + + /* Get rid of compiler warning ("br_prepend_prefix never used") */ + if (0) br_prepend_prefix (NULL, NULL); + + free (tmp); + return newpath; +} + +#endif /* ENABLE_BINRELOC */ + + +/* Pthread stuff for thread safetiness */ +#if BR_PTHREADS && defined(ENABLE_BINRELOC) + +#include + +static pthread_key_t br_thread_key; +static pthread_once_t br_thread_key_once = PTHREAD_ONCE_INIT; + + +static void +br_thread_local_store_fini () +{ + char *specific; + + specific = (char *) pthread_getspecific (br_thread_key); + if (specific) + { + free (specific); + pthread_setspecific (br_thread_key, NULL); + } + pthread_key_delete (br_thread_key); + br_thread_key = 0; +} + + +static void +br_str_free (void *str) +{ + if (str) + free (str); +} + + +static void +br_thread_local_store_init () +{ + if (pthread_key_create (&br_thread_key, br_str_free) == 0) + atexit (br_thread_local_store_fini); +} + +#else /* BR_PTHREADS */ +#ifdef ENABLE_BINRELOC + +static char *br_last_value = (char *) NULL; + +static void +br_free_last_value () +{ + if (br_last_value) + free (br_last_value); +} + +#endif /* ENABLE_BINRELOC */ +#endif /* BR_PTHREADS */ + + +#ifdef ENABLE_BINRELOC + +/** + * br_thread_local_store: + * str: A dynamically allocated string. + * Returns: str. This return value must not be freed. + * + * Store str in a thread-local variable and return str. The next + * you run this function, that variable is freed too. + * This function is created so you don't have to worry about freeing + * strings. Just be careful about doing this sort of thing: + * + * some_function( BR_DATADIR("/one.png"), BR_DATADIR("/two.png") ) + * + * Examples: + * char *foo; + * foo = br_thread_local_store (strdup ("hello")); --> foo == "hello" + * foo = br_thread_local_store (strdup ("world")); --> foo == "world"; "hello" is now freed. + */ +const char * +br_thread_local_store (char *str) +{ + #if BR_PTHREADS + char *specific; + + pthread_once (&br_thread_key_once, br_thread_local_store_init); + + specific = (char *) pthread_getspecific (br_thread_key); + br_str_free (specific); + pthread_setspecific (br_thread_key, str); + + #else /* BR_PTHREADS */ + static int initialized = 0; + + if (!initialized) + { + atexit (br_free_last_value); + initialized = 1; + } + + if (br_last_value) + free (br_last_value); + br_last_value = str; + #endif /* BR_PTHREADS */ + + return (const char *) str; +} + +#endif /* ENABLE_BINRELOC */ + + +/** + * br_strcat: + * str1: A string. + * str2: Another string. + * Returns: A newly-allocated string. This string should be freed when no longer needed. + * + * Concatenate str1 and str2 to a newly allocated string. + */ +char * +br_strcat (const char *str1, const char *str2) +{ + char *result; + size_t len1, len2; + + if (!str1) str1 = ""; + if (!str2) str2 = ""; + + len1 = strlen (str1); + len2 = strlen (str2); + + result = (char *) malloc (len1 + len2 + 1); + memcpy (result, str1, len1); + memcpy (result + len1, str2, len2); + result[len1 + len2] = '\0'; + + return result; +} + + +/* Emulates glibc's strndup() */ +static char * +br_strndup (char *str, size_t size) +{ + char *result = (char *) NULL; + size_t len; + + br_return_val_if_fail (str != (char *) NULL, (char *) NULL); + + len = strlen (str); + if (!len) return strdup (""); + if (size > len) size = len; + + result = (char *) calloc (sizeof (char), len + 1); + memcpy (result, str, size); + return result; +} + + +/** + * br_extract_dir: + * path: A path. + * Returns: A directory name. This string should be freed when no longer needed. + * + * Extracts the directory component of path. Similar to g_dirname() or the dirname + * commandline application. + * + * Example: + * br_extract_dir ("/usr/local/foobar"); --> Returns: "/usr/local" + */ +char * +br_extract_dir (const char *path) +{ + char *end, *result; + + br_return_val_if_fail (path != (char *) NULL, (char *) NULL); + + end = strrchr (path, '/'); + if (!end) return strdup ("."); + + while (end > path && *end == '/') + end--; + result = br_strndup ((char *) path, end - path + 1); + if (!*result) + { + free (result); + return strdup ("/"); + } else + return result; +} + + +/** + * br_extract_prefix: + * path: The full path of an executable or library. + * Returns: The prefix, or NULL on error. This string should be freed when no longer needed. + * + * Extracts the prefix from path. This function assumes that your executable + * or library is installed in an LSB-compatible directory structure. + * + * Example: + * br_extract_prefix ("/usr/bin/gnome-panel"); --> Returns "/usr" + * br_extract_prefix ("/usr/local/lib/libfoo.so"); --> Returns "/usr/local" + * br_extract_prefix ("/usr/local/libfoo.so"); --> Returns "/usr" + */ +char * +br_extract_prefix (const char *path) +{ + char *end, *tmp, *result; + + br_return_val_if_fail (path != (char *) NULL, (char *) NULL); + + if (!*path) return strdup ("/"); + end = strrchr (path, '/'); + if (!end) return strdup (path); + + tmp = br_strndup ((char *) path, end - path); + if (!*tmp) + { + free (tmp); + return strdup ("/"); + } + end = strrchr (tmp, '/'); + if (!end) return tmp; + + result = br_strndup (tmp, end - tmp); + free (tmp); + + if (!*result) + { + free (result); + result = strdup ("/"); + } + + return result; +} + + +/** + * br_set_fallback_function: + * func: A function to call to find the binary. + * data: User data to pass to func. + * + * Sets a function to call to find the path to the binary, in + * case "/proc/self/maps" can't be opened. The function set should + * return a string that is safe to free with free(). + */ +void +br_set_locate_fallback_func (br_locate_fallback_func func, void *data) +{ + fallback_func = func; + fallback_data = data; +} + + +#ifdef __cplusplus +} +#endif /* __cplusplus */ + +#endif /* _PREFIX_C */ diff --git a/server/amx/prefix.h b/server/amx/prefix.h new file mode 100644 index 0000000..9e1b219 --- /dev/null +++ b/server/amx/prefix.h @@ -0,0 +1,139 @@ +/* + * BinReloc - a library for creating relocatable executables + * Written by: Mike Hearn + * Hongli Lai + * http://autopackage.org/ + * + * This source code is public domain. You can relicense this code + * under whatever license you want. + * + * See http://autopackage.org/docs/binreloc/ for + * more information and how to use this. + * + * NOTE: if you're using C++ and are getting "undefined reference + * to br_*", try renaming prefix.c to prefix.cpp + */ + +#ifndef _PREFIX_H_ +#define _PREFIX_H_ + +#ifdef __cplusplus +extern "C" { +#endif /* __cplusplus */ + +/* WARNING, BEFORE YOU MODIFY PREFIX.C: + * + * If you make changes to any of the functions in prefix.c, you MUST + * change the BR_NAMESPACE macro. + * This way you can avoid symbol table conflicts with other libraries + * that also happen to use BinReloc. + * + * Example: + * #define BR_NAMESPACE(funcName) foobar_ ## funcName + * --> expands br_locate to foobar_br_locate + */ +#undef BR_NAMESPACE +#define BR_NAMESPACE(funcName) funcName + + +#ifdef ENABLE_BINRELOC + +#define br_thread_local_store BR_NAMESPACE(br_thread_local_store) +#define br_locate BR_NAMESPACE(br_locate) +#define br_locate_prefix BR_NAMESPACE(br_locate_prefix) +#define br_prepend_prefix BR_NAMESPACE(br_prepend_prefix) + +#ifndef BR_NO_MACROS + /* These are convience macros that replace the ones usually used + in Autoconf/Automake projects */ + #undef SELFPATH + #undef PREFIX + #undef PREFIXDIR + #undef BINDIR + #undef SBINDIR + #undef DATADIR + #undef LIBDIR + #undef LIBEXECDIR + #undef ETCDIR + #undef SYSCONFDIR + #undef CONFDIR + #undef LOCALEDIR + + #define SELFPATH (br_thread_local_store (br_locate ((void *) ""))) + #define PREFIX (br_thread_local_store (br_locate_prefix ((void *) ""))) + #define PREFIXDIR (br_thread_local_store (br_locate_prefix ((void *) ""))) + #define BINDIR (br_thread_local_store (br_prepend_prefix ((void *) "", "/bin"))) + #define SBINDIR (br_thread_local_store (br_prepend_prefix ((void *) "", "/sbin"))) + #define DATADIR (br_thread_local_store (br_prepend_prefix ((void *) "", "/share"))) + #define LIBDIR (br_thread_local_store (br_prepend_prefix ((void *) "", "/lib"))) + #define LIBEXECDIR (br_thread_local_store (br_prepend_prefix ((void *) "", "/libexec"))) + #define ETCDIR (br_thread_local_store (br_prepend_prefix ((void *) "", "/etc"))) + #define SYSCONFDIR (br_thread_local_store (br_prepend_prefix ((void *) "", "/etc"))) + #define CONFDIR (br_thread_local_store (br_prepend_prefix ((void *) "", "/etc"))) + #define LOCALEDIR (br_thread_local_store (br_prepend_prefix ((void *) "", "/share/locale"))) +#endif /* BR_NO_MACROS */ + + +/* The following functions are used internally by BinReloc + and shouldn't be used directly in applications. */ + +char *br_locate (void *symbol); +char *br_locate_prefix (void *symbol); +char *br_prepend_prefix (void *symbol, char *path); + +#endif /* ENABLE_BINRELOC */ + +const char *br_thread_local_store (char *str); + + +/* These macros and functions are not guarded by the ENABLE_BINRELOC + * macro because they are portable. You can use these functions. + */ + +#define br_strcat BR_NAMESPACE(br_strcat) +#define br_extract_dir BR_NAMESPACE(br_extract_dir) +#define br_extract_prefix BR_NAMESPACE(br_extract_prefix) +#define br_set_locate_fallback_func BR_NAMESPACE(br_set_locate_fallback_func) + +#ifndef BR_NO_MACROS + #ifndef ENABLE_BINRELOC + #define BR_SELFPATH(suffix) SELFPATH suffix + #define BR_PREFIX(suffix) PREFIX suffix + #define BR_PREFIXDIR(suffix) BR_PREFIX suffix + #define BR_BINDIR(suffix) BINDIR suffix + #define BR_SBINDIR(suffix) SBINDIR suffix + #define BR_DATADIR(suffix) DATADIR suffix + #define BR_LIBDIR(suffix) LIBDIR suffix + #define BR_LIBEXECDIR(suffix) LIBEXECDIR suffix + #define BR_ETCDIR(suffix) ETCDIR suffix + #define BR_SYSCONFDIR(suffix) SYSCONFDIR suffix + #define BR_CONFDIR(suffix) CONFDIR suffix + #define BR_LOCALEDIR(suffix) LOCALEDIR suffix + #else + #define BR_SELFPATH(suffix) (br_thread_local_store (br_strcat (SELFPATH, suffix))) + #define BR_PREFIX(suffix) (br_thread_local_store (br_strcat (PREFIX, suffix))) + #define BR_PREFIXDIR(suffix) (br_thread_local_store (br_strcat (BR_PREFIX, suffix))) + #define BR_BINDIR(suffix) (br_thread_local_store (br_strcat (BINDIR, suffix))) + #define BR_SBINDIR(suffix) (br_thread_local_store (br_strcat (SBINDIR, suffix))) + #define BR_DATADIR(suffix) (br_thread_local_store (br_strcat (DATADIR, suffix))) + #define BR_LIBDIR(suffix) (br_thread_local_store (br_strcat (LIBDIR, suffix))) + #define BR_LIBEXECDIR(suffix) (br_thread_local_store (br_strcat (LIBEXECDIR, suffix))) + #define BR_ETCDIR(suffix) (br_thread_local_store (br_strcat (ETCDIR, suffix))) + #define BR_SYSCONFDIR(suffix) (br_thread_local_store (br_strcat (SYSCONFDIR, suffix))) + #define BR_CONFDIR(suffix) (br_thread_local_store (br_strcat (CONFDIR, suffix))) + #define BR_LOCALEDIR(suffix) (br_thread_local_store (br_strcat (LOCALEDIR, suffix))) + #endif +#endif + +char *br_strcat (const char *str1, const char *str2); +char *br_extract_dir (const char *path); +char *br_extract_prefix(const char *path); +typedef char *(*br_locate_fallback_func) (void *symbol, void *data); +void br_set_locate_fallback_func (br_locate_fallback_func func, void *data); + + +#ifdef __cplusplus +} +#endif /* __cplusplus */ + +#endif /* _PREFIX_H_ */ diff --git a/server/amx/sclinux.h b/server/amx/sclinux.h new file mode 100644 index 0000000..f22497a --- /dev/null +++ b/server/amx/sclinux.h @@ -0,0 +1,47 @@ +/* + * Things needed to compile under linux. + * + * Should be reworked totally to use GNU's 'configure' + */ +#ifndef SCLINUX_H +#define SCLINUX_H + +/* getchar() is not a 'cool' replacement for MSDOS getch: Linux/unix depends on the features activated or not about the + * controlling terminal's tty. This means that ioctl(2) calls must be performed, for instance to have the controlling + * terminal tty's in 'raw' mode, if we want to be able to fetch a single character. This also means that everything must + * be put back correctly when the function ends. See GETCH.C for an implementation. + * + * For interactive use of SRUN/SDBG if would be much better to use GNU's readline package: the user would be able to + * have a complete emacs/vi like line editing system. + */ +#include "getch.h" + +#define stricmp(a,b) strcasecmp(a,b) +#define strnicmp(a,b,c) strncasecmp(a,b,c) + +/* + * WinWorld wants '\'. Unices do not. + */ +#define DIRECTORY_SEP_CHAR '/' +#define DIRECTORY_SEP_STR "/" + +/* + * SC assumes that a computer is Little Endian unless told otherwise. It uses + * (and defines) the macros BYTE_ORDER and BIG_ENDIAN. + * For Linux, we must overrule these settings with those defined in glibc. + */ +#if !defined __BYTE_ORDER +# include +#endif + +#if defined __OpenBSD__ || defined __FreeBSD__ +# define __BYTE_ORDER BYTE_ORDER +# define __LITTLE_ENDIAN LITTLE_ENDIAN +# define __BIG_ENDIAN BIG_ENDIAN +#endif + +#if !defined __BYTE_ORDER +# error "Can't figure computer byte order (__BYTE_ORDER macro not found)" +#endif + +#endif /* SCLINUX_H */ diff --git a/server/server.vcproj b/server/server.vcproj index dec9637..3dd0199 100644 --- a/server/server.vcproj +++ b/server/server.vcproj @@ -121,6 +121,40 @@ RelativePath="..\ttmath\ttmathuint_x86.h"> + + + + + + + + + + + + + + + + + + + + + +