mirror of
https://github.com/alliedmodders/amxmodx.git
synced 2025-01-24 04:38:05 +03:00
initial import of new format zomg
This commit is contained in:
parent
2843c333aa
commit
2ba0b079ab
@ -32,6 +32,7 @@
|
||||
#include <stdarg.h>
|
||||
#include "amxmodx.h"
|
||||
#include "CLang.h"
|
||||
#include "format.h"
|
||||
|
||||
#ifdef __linux__
|
||||
#define _snprintf snprintf
|
||||
@ -368,277 +369,13 @@ int CLangMngr::GetKeyEntry(String &key)
|
||||
return val.index;
|
||||
}
|
||||
|
||||
/**
|
||||
* FORMATTING ROUTINES
|
||||
*/
|
||||
|
||||
#define FMTPM_NEXTPARAM() \
|
||||
if (*param > numParams) { \
|
||||
LogError(amx, AMX_ERR_PARAMS, "String formatted incorrectly - parameter %d (total %d)", *param, numParams); \
|
||||
return 0; \
|
||||
} \
|
||||
_addr = params[*param]; \
|
||||
(*param)++;
|
||||
|
||||
extern "C" size_t do_amx_format(AMX *amx, cell *params, int *param, const char **lex, char *output, size_t maxlen, int level);
|
||||
THash<String, lang_err> BadLang_Table;
|
||||
|
||||
static cvar_t *amx_mldebug = NULL;
|
||||
static cvar_t *amx_cl_langs = NULL;
|
||||
|
||||
extern "C" const char *translate(AMX *amx, cell amxaddr, const char *key)
|
||||
{
|
||||
const char *pLangName = NULL;
|
||||
const char *def = NULL;
|
||||
int status;
|
||||
cell *addr = get_amxaddr(amx, amxaddr);
|
||||
char name[4];
|
||||
if (addr[0] == LANG_PLAYER)
|
||||
{
|
||||
if (!amx_cl_langs)
|
||||
amx_cl_langs = CVAR_GET_POINTER("amx_client_languages");
|
||||
if ( (int)amx_cl_langs->value == 0 )
|
||||
{
|
||||
pLangName = g_vault.get("server_language");
|
||||
} else {
|
||||
pLangName = ENTITY_KEYVALUE(GET_PLAYER_POINTER_I(g_langMngr.GetDefLang())->pEdict, "lang");
|
||||
}
|
||||
} else if (addr[0] == LANG_SERVER) {
|
||||
pLangName = g_vault.get("server_language");
|
||||
} else if (addr[0] >= 1 && addr[0] <= gpGlobals->maxClients) {
|
||||
if (!amx_cl_langs)
|
||||
amx_cl_langs = CVAR_GET_POINTER("amx_client_languages");
|
||||
if ( (int)amx_cl_langs->value == 0 )
|
||||
{
|
||||
pLangName = g_vault.get("server_language");
|
||||
} else {
|
||||
pLangName = ENTITY_KEYVALUE(GET_PLAYER_POINTER_I(addr[0])->pEdict, "lang");
|
||||
}
|
||||
} else {
|
||||
get_amxstring_r(amx, amxaddr, name, 3);
|
||||
pLangName = name;
|
||||
}
|
||||
if (!pLangName || !isalpha(pLangName[0]))
|
||||
pLangName = "en";
|
||||
//next parameter!
|
||||
def = g_langMngr.GetDef(pLangName, key, status);
|
||||
|
||||
if (!amx_mldebug)
|
||||
amx_mldebug = CVAR_GET_POINTER("amx_mldebug");
|
||||
|
||||
bool debug = (amx_mldebug && amx_mldebug->string && (amx_mldebug->string[0] != '\0'));
|
||||
|
||||
if (debug)
|
||||
{
|
||||
int debug_status;
|
||||
bool validlang = true;
|
||||
const char *testlang = amx_mldebug->string;
|
||||
if (!g_langMngr.LangExists(testlang))
|
||||
{
|
||||
AMXXLOG_Log("[AMXX] \"%s\" is an invalid debug language", testlang);
|
||||
validlang = false;
|
||||
}
|
||||
|
||||
g_langMngr.GetDef(testlang, key, debug_status);
|
||||
|
||||
if (validlang && debug_status == ERR_BADKEY)
|
||||
AMXXLOG_Log("[AMXX] Language key \"%s\" not found for language \"%s\", check \"%s\"", key, testlang, GetFileName(amx));
|
||||
}
|
||||
|
||||
if (def == NULL)
|
||||
{
|
||||
if (debug)
|
||||
{
|
||||
if (status == ERR_BADLANG && (BadLang_Table[make_string(pLangName)].last + 120.0f < gpGlobals->time))
|
||||
{
|
||||
AMXXLOG_Log("[AMXX] Language \"%s\" not found", pLangName);
|
||||
BadLang_Table[make_string(pLangName)].last = gpGlobals->time;
|
||||
}
|
||||
}
|
||||
|
||||
if (addr[0] != LANG_SERVER)
|
||||
def = g_langMngr.GetDef(g_vault.get("server_language"), key, status);
|
||||
|
||||
if (!def && (strcmp(pLangName, "en") != 0 && strcmp(g_vault.get("server_language"), "en") != 0))
|
||||
def = g_langMngr.GetDef("en", key, status);
|
||||
}
|
||||
|
||||
return def;
|
||||
}
|
||||
|
||||
#if defined AMD64
|
||||
size_t do_amx_format_parameter(AMX *amx, cell *params, const char **fmtstr, int *param, char *output, size_t maxlen)
|
||||
{
|
||||
char fmt[32];
|
||||
size_t len = 0;
|
||||
char *fmtptr = fmt;
|
||||
register const char *fmtsrc = *fmtstr;
|
||||
char ctrl_code;
|
||||
int numParams = params[0] / sizeof(cell);
|
||||
cell _addr, *addr;
|
||||
|
||||
*fmtptr++ = '%';
|
||||
while (*fmtsrc && !isalpha(*fmtsrc))
|
||||
{
|
||||
if (len >= sizeof(fmt)-2)
|
||||
break;
|
||||
*fmtptr++ = static_cast<char>(*fmtsrc++);
|
||||
len++;
|
||||
}
|
||||
|
||||
//get the final character
|
||||
ctrl_code = *fmtsrc++;
|
||||
if (!ctrl_code)
|
||||
return 0;
|
||||
//inc the source pointer
|
||||
*fmtstr = fmtsrc;
|
||||
//finalize the string
|
||||
*fmtptr++ = ctrl_code;
|
||||
*fmtptr = '\0';
|
||||
len = 0;
|
||||
//reset the format pointer
|
||||
fmtptr = fmt;
|
||||
|
||||
//we now have the format
|
||||
switch (ctrl_code)
|
||||
{
|
||||
case 's':
|
||||
{
|
||||
FMTPM_NEXTPARAM();
|
||||
char buffer[2048];
|
||||
get_amxstring_r(amx, _addr, buffer, 2047);
|
||||
return _snprintf(output, maxlen, fmtptr, buffer);
|
||||
break;
|
||||
}
|
||||
case 'g':
|
||||
case 'f':
|
||||
{
|
||||
FMTPM_NEXTPARAM();
|
||||
addr = get_amxaddr(amx, _addr);
|
||||
return _snprintf(output, maxlen, fmtptr, *(REAL *)addr);
|
||||
break;
|
||||
}
|
||||
case 'p':
|
||||
{
|
||||
FMTPM_NEXTPARAM();
|
||||
addr = get_amxaddr(amx, _addr);
|
||||
return _snprintf(output, maxlen, fmtptr, addr);
|
||||
break;
|
||||
}
|
||||
case 'x':
|
||||
case 'i':
|
||||
case 'd':
|
||||
case 'c':
|
||||
{
|
||||
FMTPM_NEXTPARAM();
|
||||
addr = get_amxaddr(amx, _addr);
|
||||
return _snprintf(output, maxlen, fmtptr, (int)addr[0]);
|
||||
break;
|
||||
}
|
||||
case 'L':
|
||||
{
|
||||
FMTPM_NEXTPARAM();
|
||||
cell lang_addr = _addr;
|
||||
FMTPM_NEXTPARAM();
|
||||
int tmpLen;
|
||||
const char *key = get_amxstring(amx, _addr, 3, tmpLen);
|
||||
|
||||
const char *def = translate(amx, lang_addr, key);
|
||||
|
||||
if (!def)
|
||||
return _snprintf(output, maxlen, "ML_NOTFOUND: %s", key);
|
||||
|
||||
return do_amx_format(amx, params, param, &def, output, maxlen, 1);
|
||||
}
|
||||
default:
|
||||
{
|
||||
return _snprintf(output, maxlen, "%s", fmtptr);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//we implement this in raw asm for x86
|
||||
//we'll do it for AMD64 once VAC2 works on it
|
||||
extern "C" size_t do_amx_format(AMX *amx, cell *params, int *param, const char **lex, char *output, size_t maxlen, int level)
|
||||
{
|
||||
size_t written;
|
||||
size_t orig_maxlen = maxlen;
|
||||
const char *save = *lex;
|
||||
register const char *lexptr = save;
|
||||
|
||||
while (*lexptr && maxlen)
|
||||
{
|
||||
switch (*lexptr)
|
||||
{
|
||||
case '%':
|
||||
{
|
||||
lexptr++;
|
||||
if (*lexptr == '%' || *lexptr == '\0')
|
||||
{
|
||||
*output++ = *lexptr++;
|
||||
*output++ = *lexptr++;
|
||||
maxlen -= 2;
|
||||
} else {
|
||||
written = do_amx_format_parameter(amx, params, &lexptr, param, output, maxlen);
|
||||
output += written;
|
||||
maxlen -= written;
|
||||
}
|
||||
break;
|
||||
}
|
||||
case '^':
|
||||
{
|
||||
if (level)
|
||||
{
|
||||
lexptr++;
|
||||
switch (*lexptr)
|
||||
{
|
||||
case 'n':
|
||||
{
|
||||
*output++ = '\n';
|
||||
break;
|
||||
}
|
||||
case 't':
|
||||
{
|
||||
*output++ = '\t';
|
||||
break;
|
||||
}
|
||||
default:
|
||||
{
|
||||
*output++ = *lexptr;
|
||||
break;
|
||||
}
|
||||
}
|
||||
lexptr++;
|
||||
maxlen--;
|
||||
break;
|
||||
}
|
||||
}
|
||||
default:
|
||||
{
|
||||
*output++ = *lexptr++;
|
||||
maxlen--;
|
||||
}
|
||||
}
|
||||
}
|
||||
*output = '\0';
|
||||
|
||||
*lex = lexptr;
|
||||
|
||||
return (orig_maxlen-maxlen);
|
||||
}
|
||||
#endif
|
||||
|
||||
char * CLangMngr::FormatAmxString(AMX *amx, cell *params, int parm, int &len)
|
||||
{
|
||||
//do an initial run through all this
|
||||
static char mystr[4096];
|
||||
static char outbuf[4096];
|
||||
const char *ptr = mystr;
|
||||
cell *addr = get_amxaddr(amx, params[parm++]);
|
||||
|
||||
get_amxstring_r(amx, params[parm++], mystr, sizeof(mystr)-1);
|
||||
|
||||
len = do_amx_format(amx, params, &parm, &ptr, outbuf, sizeof(outbuf)-1, 0);
|
||||
len = atcprintf(outbuf, sizeof(outbuf)-1, addr, amx, params, &parm);
|
||||
|
||||
return outbuf;
|
||||
}
|
||||
|
Binary file not shown.
Binary file not shown.
414
amxmodx/format.cpp
Normal file
414
amxmodx/format.cpp
Normal file
@ -0,0 +1,414 @@
|
||||
#include "amxmodx.h"
|
||||
#include "format.h"
|
||||
|
||||
//Adapted from Quake3's snprintf
|
||||
|
||||
#define ALT 0x00000001 /* alternate form */
|
||||
#define HEXPREFIX 0x00000002 /* add 0x or 0X prefix */
|
||||
#define LADJUST 0x00000004 /* left adjustment */
|
||||
#define LONGDBL 0x00000008 /* long double */
|
||||
#define LONGINT 0x00000010 /* long integer */
|
||||
#define QUADINT 0x00000020 /* quad integer */
|
||||
#define SHORTINT 0x00000040 /* short integer */
|
||||
#define ZEROPAD 0x00000080 /* zero (as opposed to blank) pad */
|
||||
#define FPT 0x00000100 /* floating point number */
|
||||
#define to_digit(c) ((c) - '0')
|
||||
#define is_digit(c) ((unsigned)to_digit(c) <= 9)
|
||||
#define to_char(n) ((n) + '0')
|
||||
#define CHECK_ARGS(n) \
|
||||
if ((arg+n) > args) { \
|
||||
LogError(amx, AMX_ERR_PARAMS, "String formatted incorrectly - parameter %d (total %d)", arg, args); \
|
||||
return 0; \
|
||||
}
|
||||
|
||||
THash<String, lang_err> BadLang_Table;
|
||||
|
||||
static cvar_t *amx_mldebug = NULL;
|
||||
static cvar_t *amx_cl_langs = NULL;
|
||||
|
||||
const char *translate(AMX *amx, cell amxaddr, const char *key)
|
||||
{
|
||||
const char *pLangName = NULL;
|
||||
const char *def = NULL;
|
||||
int status;
|
||||
cell *addr = get_amxaddr(amx, amxaddr);
|
||||
char name[4];
|
||||
if (addr[0] == LANG_PLAYER)
|
||||
{
|
||||
if (!amx_cl_langs)
|
||||
amx_cl_langs = CVAR_GET_POINTER("amx_client_languages");
|
||||
if ( (int)amx_cl_langs->value == 0 )
|
||||
{
|
||||
pLangName = g_vault.get("server_language");
|
||||
} else {
|
||||
pLangName = ENTITY_KEYVALUE(GET_PLAYER_POINTER_I(g_langMngr.GetDefLang())->pEdict, "lang");
|
||||
}
|
||||
} else if (addr[0] == LANG_SERVER) {
|
||||
pLangName = g_vault.get("server_language");
|
||||
} else if (addr[0] >= 1 && addr[0] <= gpGlobals->maxClients) {
|
||||
if (!amx_cl_langs)
|
||||
amx_cl_langs = CVAR_GET_POINTER("amx_client_languages");
|
||||
if ( (int)amx_cl_langs->value == 0 )
|
||||
{
|
||||
pLangName = g_vault.get("server_language");
|
||||
} else {
|
||||
pLangName = ENTITY_KEYVALUE(GET_PLAYER_POINTER_I(addr[0])->pEdict, "lang");
|
||||
}
|
||||
} else {
|
||||
get_amxstring_r(amx, amxaddr, name, 3);
|
||||
pLangName = name;
|
||||
}
|
||||
if (!pLangName || !isalpha(pLangName[0]))
|
||||
pLangName = "en";
|
||||
//next parameter!
|
||||
def = g_langMngr.GetDef(pLangName, key, status);
|
||||
|
||||
if (!amx_mldebug)
|
||||
amx_mldebug = CVAR_GET_POINTER("amx_mldebug");
|
||||
|
||||
bool debug = (amx_mldebug && amx_mldebug->string && (amx_mldebug->string[0] != '\0'));
|
||||
|
||||
if (debug)
|
||||
{
|
||||
int debug_status;
|
||||
bool validlang = true;
|
||||
const char *testlang = amx_mldebug->string;
|
||||
if (!g_langMngr.LangExists(testlang))
|
||||
{
|
||||
AMXXLOG_Log("[AMXX] \"%s\" is an invalid debug language", testlang);
|
||||
validlang = false;
|
||||
}
|
||||
|
||||
g_langMngr.GetDef(testlang, key, debug_status);
|
||||
|
||||
if (validlang && debug_status == ERR_BADKEY)
|
||||
AMXXLOG_Log("[AMXX] Language key \"%s\" not found for language \"%s\", check \"%s\"", key, testlang, GetFileName(amx));
|
||||
}
|
||||
|
||||
if (def == NULL)
|
||||
{
|
||||
if (debug)
|
||||
{
|
||||
if (status == ERR_BADLANG && (BadLang_Table.AltFindOrInsert(pLangName).last + 120.0f < gpGlobals->time))
|
||||
{
|
||||
AMXXLOG_Log("[AMXX] Language \"%s\" not found", pLangName);
|
||||
BadLang_Table.AltFindOrInsert(pLangName).last = gpGlobals->time;
|
||||
}
|
||||
}
|
||||
|
||||
if (addr[0] != LANG_SERVER)
|
||||
def = g_langMngr.GetDef(g_vault.get("server_language"), key, status);
|
||||
|
||||
if (!def && (strcmp(pLangName, "en") != 0 && strcmp(g_vault.get("server_language"), "en") != 0))
|
||||
def = g_langMngr.GetDef("en", key, status);
|
||||
}
|
||||
|
||||
return def;
|
||||
}
|
||||
|
||||
template <typename U>
|
||||
void AddString(U **buf_p, size_t &maxlen, const cell *string, int width, int prec)
|
||||
{
|
||||
int size = 0;
|
||||
U *buf;
|
||||
static cell nlstr[] = {'(','n','u','l','l',')','\0'};
|
||||
|
||||
buf = *buf_p;
|
||||
|
||||
if (string == NULL)
|
||||
{
|
||||
string = nlstr;
|
||||
prec = -1;
|
||||
}
|
||||
|
||||
if (prec >= 0)
|
||||
{
|
||||
for (size = 0; size < prec; size++)
|
||||
{
|
||||
if (string[size] == '\0')
|
||||
break;
|
||||
}
|
||||
} else {
|
||||
while (string[size++]) ;
|
||||
size--;
|
||||
}
|
||||
|
||||
if (size > (int)maxlen)
|
||||
size = maxlen;
|
||||
|
||||
maxlen -= size;
|
||||
width -= size;
|
||||
|
||||
while (size--)
|
||||
*buf++ = static_cast<U>(*string++);
|
||||
|
||||
while (width-- > 0 && maxlen)
|
||||
{
|
||||
*buf++ = ' ';
|
||||
maxlen--;
|
||||
}
|
||||
|
||||
*buf_p = buf;
|
||||
}
|
||||
|
||||
template <typename U>
|
||||
void AddFloat(U **buf_p, size_t &maxlen, double fval, int width, int prec)
|
||||
{
|
||||
U text[32];
|
||||
int digits;
|
||||
double signedVal;
|
||||
U *buf;
|
||||
int val;
|
||||
|
||||
// get the sign
|
||||
signedVal = fval;
|
||||
if (fval < 0)
|
||||
fval = -fval;
|
||||
|
||||
// write the float number
|
||||
digits = 0;
|
||||
val = (int)fval;
|
||||
do {
|
||||
text[digits++] = '0' + val % 10;
|
||||
val /= 10;
|
||||
} while (val);
|
||||
|
||||
if (signedVal < 0)
|
||||
text[digits++] = '-';
|
||||
|
||||
buf = *buf_p;
|
||||
|
||||
while (digits < width && maxlen)
|
||||
{
|
||||
*buf++ = ' ';
|
||||
width--;
|
||||
maxlen--;
|
||||
}
|
||||
|
||||
while (digits-- && maxlen)
|
||||
{
|
||||
*buf++ = text[digits];
|
||||
maxlen--;
|
||||
}
|
||||
|
||||
*buf_p = buf;
|
||||
|
||||
if (prec < 0)
|
||||
prec = 6;
|
||||
// write the fraction
|
||||
digits = 0;
|
||||
while (digits < prec)
|
||||
{
|
||||
fval -= (int) fval;
|
||||
fval *= 10.0;
|
||||
val = (int) fval;
|
||||
text[digits++] = '0' + val % 10;
|
||||
}
|
||||
|
||||
if (digits > 0 && maxlen)
|
||||
{
|
||||
buf = *buf_p;
|
||||
*buf++ = '.';
|
||||
maxlen--;
|
||||
for (prec = 0; maxlen && prec < digits; prec++)
|
||||
{
|
||||
*buf++ = text[prec];
|
||||
maxlen--;
|
||||
}
|
||||
*buf_p = buf;
|
||||
}
|
||||
}
|
||||
|
||||
template <typename U>
|
||||
void AddInt(U **buf_p, size_t &maxlen, int val, int width, int flags)
|
||||
{
|
||||
U text[32];
|
||||
int digits;
|
||||
int signedVal;
|
||||
U *buf;
|
||||
|
||||
digits = 0;
|
||||
signedVal = val;
|
||||
if (val < 0)
|
||||
val = -val;
|
||||
do {
|
||||
text[digits++] = '0' + val % 10;
|
||||
val /= 10;
|
||||
} while (val);
|
||||
|
||||
if (signedVal < 0)
|
||||
text[digits++] = '-';
|
||||
|
||||
buf = *buf_p;
|
||||
|
||||
if( !(flags & LADJUST) )
|
||||
{
|
||||
while (digits < width && maxlen)
|
||||
{
|
||||
*buf++ = (flags & ZEROPAD) ? '0' : ' ';
|
||||
width--;
|
||||
maxlen--;
|
||||
}
|
||||
}
|
||||
|
||||
while (digits-- && maxlen)
|
||||
{
|
||||
*buf++ = text[digits];
|
||||
width--;
|
||||
maxlen--;
|
||||
}
|
||||
|
||||
if (flags & LADJUST)
|
||||
{
|
||||
while (width-- && maxlen)
|
||||
{
|
||||
*buf++ = (flags & ZEROPAD) ? '0' : ' ';
|
||||
maxlen--;
|
||||
}
|
||||
}
|
||||
|
||||
*buf_p = buf;
|
||||
}
|
||||
|
||||
template <typename D, typename S>
|
||||
size_t atcprintf(D *buffer, size_t maxlen, const S *format, AMX *amx, cell *params, int *param)
|
||||
{
|
||||
int arg;
|
||||
int args = params[0] / sizeof(cell);
|
||||
D *buf_p;
|
||||
D ch;
|
||||
int flags;
|
||||
int width;
|
||||
int prec;
|
||||
int n;
|
||||
char sign;
|
||||
const S *fmt;
|
||||
size_t llen = maxlen;
|
||||
|
||||
buf_p = buffer;
|
||||
arg = *param;
|
||||
fmt = format;
|
||||
|
||||
while (true)
|
||||
{
|
||||
// run through the format string until we hit a '%' or '\0'
|
||||
for (ch = static_cast<D>(*fmt);
|
||||
llen && ((ch = static_cast<D>(*fmt)) != '\0' && ch != '%');
|
||||
fmt++)
|
||||
{
|
||||
*buf_p++ = static_cast<D>(ch);
|
||||
llen--;
|
||||
}
|
||||
if (ch == '\0' || llen <= 0)
|
||||
goto done;
|
||||
|
||||
// skip over the '%'
|
||||
fmt++;
|
||||
|
||||
// reset formatting state
|
||||
flags = 0;
|
||||
width = 0;
|
||||
prec = -1;
|
||||
sign = '\0';
|
||||
|
||||
rflag:
|
||||
ch = static_cast<D>(*fmt++);
|
||||
reswitch:
|
||||
switch(ch)
|
||||
{
|
||||
case '-':
|
||||
flags |= LADJUST;
|
||||
goto rflag;
|
||||
case '.':
|
||||
n = 0;
|
||||
while( is_digit( ( ch = static_cast<D>(*fmt++)) ) )
|
||||
n = 10 * n + ( ch - '0' );
|
||||
prec = n < 0 ? -1 : n;
|
||||
goto reswitch;
|
||||
case '0':
|
||||
flags |= ZEROPAD;
|
||||
goto rflag;
|
||||
case '1':
|
||||
case '2':
|
||||
case '3':
|
||||
case '4':
|
||||
case '5':
|
||||
case '6':
|
||||
case '7':
|
||||
case '8':
|
||||
case '9':
|
||||
n = 0;
|
||||
do {
|
||||
n = 10 * n + ( ch - '0' );
|
||||
ch = static_cast<D>(*fmt++);
|
||||
} while( is_digit( ch ) );
|
||||
width = n;
|
||||
goto reswitch;
|
||||
case 'c':
|
||||
CHECK_ARGS(0);
|
||||
*buf_p++ = static_cast<D>(*get_amxaddr(amx, params[arg]));
|
||||
arg++;
|
||||
break;
|
||||
case 'd':
|
||||
case 'i':
|
||||
CHECK_ARGS(0);
|
||||
AddInt(&buf_p, llen, *get_amxaddr(amx, params[arg]), width, flags);
|
||||
arg++;
|
||||
break;
|
||||
case 'f':
|
||||
CHECK_ARGS(0);
|
||||
AddFloat(&buf_p, llen, amx_ctof(*get_amxaddr(amx, params[arg])), width, prec);
|
||||
arg++;
|
||||
break;
|
||||
case 's':
|
||||
CHECK_ARGS(0);
|
||||
AddString(&buf_p, llen, get_amxaddr(amx, params[arg]), width, prec);
|
||||
arg++;
|
||||
break;
|
||||
case 'L':
|
||||
{
|
||||
CHECK_ARGS(1);
|
||||
cell addr = params[arg++];
|
||||
int len;
|
||||
const char *key = get_amxstring(amx, params[arg++], 3, len);
|
||||
const char *def = translate(amx, addr, key);
|
||||
size_t written = atcprintf(buf_p, llen, def, amx, params, &arg);
|
||||
buf_p += written;
|
||||
maxlen -= written;
|
||||
break;
|
||||
}
|
||||
case '%':
|
||||
*buf_p++ = static_cast<D>(ch);
|
||||
break;
|
||||
case '\0':
|
||||
*buf_p++ = static_cast<D>('%');
|
||||
goto done;
|
||||
break;
|
||||
default:
|
||||
*buf_p++ = static_cast<D>(ch);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
done:
|
||||
*buf_p = static_cast<D>(0);
|
||||
*param = arg;
|
||||
return maxlen-llen;
|
||||
}
|
||||
|
||||
/**
|
||||
* HACKHACK: The compiler will generate code for each case we need.
|
||||
* Don't remove this, otherwise files that use certain code generations
|
||||
* will have extern problems. For each case you need, add dummy code
|
||||
* here.
|
||||
*/
|
||||
void __WHOA_DONT_CALL_ME_PLZ_K_lol_o_O()
|
||||
{
|
||||
//acsprintf
|
||||
atcprintf((cell *)NULL, 0, (const char *)NULL, NULL, NULL, NULL);
|
||||
//accprintf
|
||||
atcprintf((cell *)NULL, 0, (cell *)NULL, NULL, NULL, NULL);
|
||||
//acsprintf
|
||||
atcprintf((char *)NULL, 0, (cell *)NULL, NULL, NULL, NULL);
|
||||
}
|
||||
|
8
amxmodx/format.h
Normal file
8
amxmodx/format.h
Normal file
@ -0,0 +1,8 @@
|
||||
#ifndef _INCLUDE_FORMATTING_H
|
||||
#define _INCLUDE_FORMATTING_H
|
||||
|
||||
//Amx Templatized Cell Printf
|
||||
template <typename D, typename S>
|
||||
size_t atcprintf(D *buffer, size_t maxlen, const S *format, AMX *amx, cell *params, int *param);
|
||||
|
||||
#endif //_INCLUDE_FORMATTING_H
|
@ -1,424 +0,0 @@
|
||||
;(C)2004-2006 AMX Mod X Development Team
|
||||
;Written by David "BAILOPAN" Anderson
|
||||
;These routines were very hard to optimize.
|
||||
;They are basically unoptimizable as far as I can tell,
|
||||
; but it's one of the most expensive operations in core.
|
||||
|
||||
|
||||
section .text
|
||||
|
||||
%ifdef LINUX
|
||||
%define __snprintf snprintf
|
||||
%define _get_amxstring_r get_amxstring_r
|
||||
%define _MNF_GetAmxString MNF_GetAmxString
|
||||
%define _translate translate
|
||||
%define _LogError LogError
|
||||
%endif
|
||||
|
||||
extern _LogError, _get_amxstring_r, __snprintf, _MNF_GetAmxString
|
||||
extern _translate
|
||||
|
||||
global do_amx_format, _do_amx_format, format_parameter
|
||||
global init_format_jumps, _init_format_jumps
|
||||
|
||||
init_format_jumps:
|
||||
_init_format_jumps:
|
||||
push ebp
|
||||
mov ebp, esp
|
||||
|
||||
lea edx, [g_jumptbl]
|
||||
mov [edx+'c'*4], dword format_parameter.fmt_num
|
||||
mov [edx+'d'*4], dword format_parameter.fmt_num
|
||||
mov [edx+'f'*4], dword format_parameter.fmt_float
|
||||
mov [edx+'g'*4], dword format_parameter.fmt_float
|
||||
mov [edx+'i'*4], dword format_parameter.fmt_num
|
||||
mov [edx+'L'*4], dword format_parameter.fmt_ml
|
||||
;mov [edx+'p'*4], dword format_parameter.fmt_ptr
|
||||
mov [edx+'s'*4], dword format_parameter.fmt_string
|
||||
mov [edx+'x'*4], dword format_parameter.fmt_num
|
||||
|
||||
pop ebp
|
||||
ret
|
||||
|
||||
|
||||
;size_t do_amx_format_parameter(AMX *amx, cell *params, int *param, size_t maxlen);
|
||||
;REGISTER USAGE (fmt search)
|
||||
; edi - fmt output
|
||||
; esi - lexptr (given)
|
||||
; ebx - scrach address
|
||||
; eax - lexchar
|
||||
; edx - scratch
|
||||
; ecx - length
|
||||
;REGISTER USAGE (format)
|
||||
; edi - real output
|
||||
; esi - lexptr (given)
|
||||
format_parameter:
|
||||
push ebp
|
||||
mov ebp, esp
|
||||
|
||||
push edi
|
||||
push ebx
|
||||
push esi
|
||||
|
||||
;len=30
|
||||
mov ecx, 30
|
||||
;edi=char[32] (+1 for offset)
|
||||
sub esp, 32
|
||||
mov [esp], byte '%'
|
||||
lea edi, [esp+1]
|
||||
;esi=lexptr already
|
||||
;eax=*lexptr
|
||||
xor eax, eax
|
||||
mov al, [esi]
|
||||
test al, al
|
||||
jz .abort
|
||||
;ebx=g_chartbl
|
||||
lea ebx, [g_chartbl]
|
||||
;start looping
|
||||
.fmtloop
|
||||
mov edx, [ebx+eax*4] ;get char flag
|
||||
test edx, edx ;is it zero?
|
||||
jnz .fmtdone ;yes, we've got a format code
|
||||
test ecx, ecx ;are we over maxlen?
|
||||
jz .fmtdone ;yes, dump out
|
||||
mov [edi], al ;copy into destination
|
||||
inc edi ;dest++
|
||||
inc esi ;src++
|
||||
dec ecx ;len--
|
||||
mov al, [esi] ;get next char
|
||||
test al, al ;is it zero?
|
||||
jnz .fmtloop ;no, continue
|
||||
|
||||
.fmtdone:
|
||||
;if there's no control code, we dumped out.
|
||||
;just abort in that case.
|
||||
test al, al
|
||||
jz .abort
|
||||
;terminate fmtptr
|
||||
mov [edi], al
|
||||
mov [edi+1], byte 0
|
||||
;sto fmtsrc back
|
||||
inc esi
|
||||
;get output ptr
|
||||
mov edi, [ebp-4]
|
||||
mov edx, [ebp-12]
|
||||
lea ebx, [g_jumptbl]
|
||||
jmp [ebx+eax*4] ;LOLolOLoL.
|
||||
|
||||
.fmt_string
|
||||
;check parameter count
|
||||
mov ebx, [ebp+12] ;params
|
||||
mov eax, [ebx] ;params[0]
|
||||
shr eax, 2 ;params[0]/4
|
||||
mov edx, [ebp+16] ;param
|
||||
mov ecx, [edx] ;*param
|
||||
cmp ecx, eax ;*param / params[0]/4 ?
|
||||
ja .error
|
||||
;get the param - it's in eax
|
||||
add dword [edx], 1
|
||||
mov eax, ebx
|
||||
sub esp, 2048
|
||||
mov ebx, esp
|
||||
push 2047 ;buffer size
|
||||
push ebx
|
||||
push dword [eax+ecx*4]
|
||||
push dword [ebp+8] ;context
|
||||
call _get_amxstring_r
|
||||
push ebx ;push buffer
|
||||
lea ebx, [ebp-44]
|
||||
push ebx ;push format
|
||||
push dword [ebp+28] ;push maxlen
|
||||
push edi ;push output
|
||||
call __snprintf
|
||||
add esp, 4*8
|
||||
add esp, 2048
|
||||
add edi, eax
|
||||
jmp .end
|
||||
|
||||
.fmt_num
|
||||
;check parameter count
|
||||
mov ebx, [ebp+12] ;params
|
||||
mov eax, [ebx] ;params[0]
|
||||
shr eax, 2 ;params[0]/4
|
||||
mov edx, [ebp+16] ;param
|
||||
mov ecx, [edx] ;*param
|
||||
cmp ecx, eax ;*param / params[0]/4 ?
|
||||
ja .error
|
||||
;get the param - it's in eax
|
||||
add dword [edx], 1 ;incr *param
|
||||
mov edx, [ebp+8] ;get AMX into edx
|
||||
mov edx, [edx] ;get AMX->base into edx
|
||||
mov eax, [edx+16] ;get base->dat into eax
|
||||
add edx, eax ;add dat to base
|
||||
add edx, dword [ebx+ecx*4] ;add params[ecx]
|
||||
push dword [edx]
|
||||
lea ebx, [ebp-44]
|
||||
push ebx
|
||||
push dword [ebp+28]
|
||||
push edi
|
||||
call __snprintf
|
||||
add esp, 4*4
|
||||
add edi, eax
|
||||
jmp .end
|
||||
|
||||
.fmt_float
|
||||
;check parameter count
|
||||
mov ebx, [ebp+12] ;params
|
||||
mov eax, [ebx] ;params[0]
|
||||
shr eax, 2 ;params[0]/4
|
||||
mov edx, [ebp+16] ;param
|
||||
mov ecx, [edx] ;*param
|
||||
cmp ecx, eax ;*param / params[0]/4 ?
|
||||
ja .error
|
||||
;get the param - it's in eax
|
||||
add dword [edx], 1
|
||||
mov edx, [ebp+8]
|
||||
mov edx, [edx]
|
||||
mov eax, [edx+16]
|
||||
add edx, eax
|
||||
add edx, dword [ebx+ecx*4]
|
||||
;load the float, convert to double
|
||||
fld dword [edx]
|
||||
sub esp, 8
|
||||
fstp qword [esp]
|
||||
;it's already on the stack now, push rest
|
||||
lea ebx, [ebp-44]
|
||||
push ebx
|
||||
push dword [ebp+28]
|
||||
push edi
|
||||
call __snprintf
|
||||
add esp, 4*5
|
||||
add edi, eax
|
||||
jmp .end
|
||||
|
||||
.fmt_ml
|
||||
mov ebx, [ebp+12] ;params
|
||||
mov eax, [ebx] ;params[0]
|
||||
shr eax, 2 ;params[0]/4
|
||||
mov edx, [ebp+16] ;param
|
||||
mov ecx, [edx] ;*param
|
||||
inc ecx
|
||||
cmp ecx, eax ;*param / params[0]/4 ?
|
||||
ja .error
|
||||
add dword [edx], 2
|
||||
push ecx
|
||||
push dword 0 ;NULL
|
||||
push dword 3 ;buffer 3
|
||||
push dword [ebx+ecx*4]
|
||||
push dword [ebp+8]
|
||||
call _MNF_GetAmxString
|
||||
add esp, 4*4
|
||||
pop ecx
|
||||
dec ecx
|
||||
push eax
|
||||
push eax ;key
|
||||
push dword [ebx+ecx*4] ;lang_addr
|
||||
push dword [ebp+8] ;AMX
|
||||
call _translate
|
||||
add esp, 4*3
|
||||
pop ecx
|
||||
test eax, eax
|
||||
je .fmt_error
|
||||
|
||||
;invoke the translator
|
||||
;;store this on the stack so we can pass the address
|
||||
push eax
|
||||
mov edx, esp
|
||||
push 1 ;no reparse ^
|
||||
push dword [ebp+20] ;maxlen
|
||||
push edi ;output
|
||||
push edx ;lexptr
|
||||
push dword [ebp+16] ;param
|
||||
push ebx ;params
|
||||
push dword [ebp+8] ;amx
|
||||
call do_amx_format
|
||||
add esp, 4*8
|
||||
;we don't care about the return lex
|
||||
add edi, eax
|
||||
jmp .end
|
||||
|
||||
|
||||
.fmt_error
|
||||
push ecx
|
||||
lea eax, [g_mlfmt]
|
||||
push eax
|
||||
push dword [ebp+20]
|
||||
push edi
|
||||
call __snprintf
|
||||
add esp, 4*4
|
||||
add edi, eax
|
||||
jmp .end
|
||||
|
||||
|
||||
.fmt_default
|
||||
mov esi, edx
|
||||
;store the % at least
|
||||
mov [edi], byte '%'
|
||||
inc edi
|
||||
mov eax, 1
|
||||
jmp .end
|
||||
|
||||
.error
|
||||
push ecx
|
||||
push eax
|
||||
push g_errfmt
|
||||
push 10 ;AMX_ERR_NATIVE
|
||||
push dword [ebp+8]
|
||||
call _LogError
|
||||
add esp, 4*5
|
||||
|
||||
.abort
|
||||
xor eax, eax
|
||||
|
||||
.end
|
||||
add esp, 32
|
||||
pop ecx
|
||||
pop ebx
|
||||
pop ecx
|
||||
|
||||
pop ebp
|
||||
ret
|
||||
|
||||
;size_t do_amx_format(AMX *amx, cell *params, int *param, const char **lex, char *output, size_t maxlen, int level)
|
||||
;REGISTER USAGE -
|
||||
; esi=lex
|
||||
; edi=output
|
||||
; ecx=maxlen
|
||||
; eax=scratch
|
||||
do_amx_format:
|
||||
_do_amx_format:
|
||||
push esi ;input
|
||||
push edi ;output
|
||||
|
||||
;current esp offset is 12 (0=edi,4=esi,8=ret)
|
||||
mov esi, [esp+24] ;lex (dbl addr)
|
||||
mov esi, [esi]
|
||||
mov edi, [esp+28] ;get output
|
||||
mov ecx, [esp+32] ;get maxlen
|
||||
|
||||
;initial checks
|
||||
mov al, [esi]
|
||||
test al, al
|
||||
jz .done
|
||||
.loop:
|
||||
test ecx, ecx
|
||||
jz .done
|
||||
cmp al, '%'
|
||||
je .perc
|
||||
cmp al, '^'
|
||||
je .esc
|
||||
|
||||
.copy:
|
||||
;*output++ = *lexptr++
|
||||
mov [edi], al
|
||||
inc esi
|
||||
inc edi
|
||||
dec ecx
|
||||
.next
|
||||
mov al, [esi]
|
||||
test al, al
|
||||
jnz .loop
|
||||
jmp .done
|
||||
|
||||
;we got a '^'
|
||||
.esc:
|
||||
cmp dword [esp+36], 0
|
||||
je .copy
|
||||
inc esi
|
||||
mov al, [esi]
|
||||
cmp al, 'n'
|
||||
je .escn
|
||||
cmp al, 't'
|
||||
je .esct
|
||||
|
||||
;*outptr++ = *lexptr
|
||||
mov [edi], al
|
||||
inc edi
|
||||
|
||||
;lexptr++
|
||||
;maxlen--
|
||||
.escdone
|
||||
inc esi
|
||||
dec ecx
|
||||
jmp .next
|
||||
|
||||
.escn
|
||||
;*outptr++ = '\n'
|
||||
mov [edi], byte 0xA ;'\n'
|
||||
inc edi
|
||||
jmp .escdone
|
||||
|
||||
.esct
|
||||
;*outptr++ = '\t'
|
||||
mov [edi], byte 0x9 ;'\t'
|
||||
inc edi
|
||||
jmp .escdone
|
||||
|
||||
;we got a '%'
|
||||
.perc:
|
||||
inc esi
|
||||
mov al, [esi]
|
||||
test al, al ;'\0'
|
||||
je .percatend
|
||||
cmp al, '%'
|
||||
je .percnone
|
||||
jmp .percfmt
|
||||
.percatend
|
||||
dec esi
|
||||
add ecx, 1
|
||||
.percnone:
|
||||
;*outptr++=*lexptr++; x2
|
||||
;maxlen -= 2
|
||||
;note we recalculate eax and then
|
||||
;only move once, since this is a 2byte move anyway.
|
||||
mov ax, [esi]
|
||||
mov [edi], ax
|
||||
add esi, 2
|
||||
add edi, 2
|
||||
sub ecx, 2
|
||||
jmp .next
|
||||
|
||||
.percfmt:
|
||||
;call do_amx_format_parameter.
|
||||
push ecx
|
||||
push ecx ;maxlen
|
||||
push dword [esp+28] ;param
|
||||
push dword [esp+28] ;params
|
||||
push dword [esp+28] ;amx
|
||||
call format_parameter ;will return edi adjusted for us
|
||||
add esp, 4*4 ;will also return esi adjusted for us
|
||||
pop ecx
|
||||
sub ecx, eax ;adjust maxlength
|
||||
mov al, [esi] ;reiterate
|
||||
test al, al
|
||||
jnz .loop
|
||||
|
||||
.done:
|
||||
;end the string
|
||||
mov [edi], dword 0
|
||||
mov edi, [esp+24] ;get lexptr ref
|
||||
mov [edi], esi ;sto into lexptr ref
|
||||
mov eax, [esp+32] ;get maxlen
|
||||
sub eax, ecx ;subtract what we did
|
||||
|
||||
pop edi
|
||||
pop esi
|
||||
|
||||
ret
|
||||
|
||||
|
||||
section .data
|
||||
align 16
|
||||
g_errfmt db "String formatted incorrectly - parameter %d (total %d)", 0
|
||||
g_mlfmt db "ML_NOTFOUND: %s", 0
|
||||
;Stores whether a character is a letter or not. hAxXx
|
||||
g_chartbl times 65 dd 0
|
||||
times 26 dd 1
|
||||
times 6 dd 0
|
||||
times 26 dd 1
|
||||
times 133 dd 0
|
||||
|
||||
g_jumptbl times 256 dd format_parameter.fmt_default
|
||||
|
||||
;end
|
||||
|
@ -1145,14 +1145,6 @@ C_DLLEXPORT int Meta_Query(char *ifvers, plugin_info_t **pPlugInfo, mutil_funcs_
|
||||
return (TRUE);
|
||||
}
|
||||
|
||||
#if !defined AMD64
|
||||
extern "C" void init_format_jumps();
|
||||
#else
|
||||
void init_format_jumps()
|
||||
{
|
||||
}
|
||||
#endif
|
||||
|
||||
static META_FUNCTIONS gMetaFunctionTable;
|
||||
C_DLLEXPORT int Meta_Attach(PLUG_LOADTIME now, META_FUNCTIONS *pFunctionTable, meta_globals_t *pMGlobals, gamedll_funcs_t *pGamedllFuncs)
|
||||
{
|
||||
@ -1231,8 +1223,6 @@ C_DLLEXPORT int Meta_Attach(PLUG_LOADTIME now, META_FUNCTIONS *pFunctionTable, m
|
||||
// This will also call modules Meta_Query and Meta_Attach functions
|
||||
loadModules(get_localinfo("amxx_modules", "addons/amxmodx/configs/modules.ini"), now);
|
||||
|
||||
init_format_jumps();
|
||||
|
||||
return (TRUE);
|
||||
}
|
||||
|
||||
|
@ -303,7 +303,7 @@
|
||||
<Tool
|
||||
Name="VCCLCompilerTool"
|
||||
Optimization="0"
|
||||
AdditionalIncludeDirectories=""C:\Hry\Half-Life\SDK\Multiplayer Source\pm_shared";"C:\Hry\Half-Life\SDK\Multiplayer Source\dlls";"C:\Hry\Half-Life\SDK\Multiplayer Source\engine";"C:\Hry\Half-Life\SDK\Multiplayer Source\common";C:\Files\Programming\metamod\metamod"
|
||||
AdditionalIncludeDirectories=""
|
||||
PreprocessorDefinitions="WIN32;_DEBUG;_WINDOWS;_USRDLL;amxmodx_EXPORTS;PAWN_CELL_SIZE=32;ASM32;JIT"
|
||||
BasicRuntimeChecks="3"
|
||||
RuntimeLibrary="5"
|
||||
@ -671,6 +671,15 @@
|
||||
<File
|
||||
RelativePath="..\float.cpp">
|
||||
</File>
|
||||
<File
|
||||
RelativePath="..\format.cpp">
|
||||
<FileConfiguration
|
||||
Name="JITRelease|Win32">
|
||||
<Tool
|
||||
Name="VCCLCompilerTool"
|
||||
AssemblerOutput="4"/>
|
||||
</FileConfiguration>
|
||||
</File>
|
||||
<File
|
||||
RelativePath="..\md5.cpp">
|
||||
</File>
|
||||
@ -823,6 +832,9 @@
|
||||
<File
|
||||
RelativePath="..\fakemeta.h">
|
||||
</File>
|
||||
<File
|
||||
RelativePath="..\format.h">
|
||||
</File>
|
||||
<File
|
||||
RelativePath="..\md5.h">
|
||||
</File>
|
||||
@ -957,13 +969,6 @@
|
||||
RelativePath="..\sdk\moduleconfig.h">
|
||||
</File>
|
||||
</Filter>
|
||||
<Filter
|
||||
Name="Helpers"
|
||||
Filter="">
|
||||
<File
|
||||
RelativePath="..\Jit\helpers-x86.obj">
|
||||
</File>
|
||||
</Filter>
|
||||
</Files>
|
||||
<Globals>
|
||||
</Globals>
|
||||
|
@ -31,6 +31,7 @@
|
||||
|
||||
#include <ctype.h>
|
||||
#include "amxmodx.h"
|
||||
#include "format.h"
|
||||
|
||||
const char* stristr(const char* str, const char* substr)
|
||||
{
|
||||
@ -463,8 +464,14 @@ static cell AMX_NATIVE_CALL equali(AMX *amx, cell *params) /* 3 param */
|
||||
|
||||
static cell AMX_NATIVE_CALL format(AMX *amx, cell *params) /* 3 param */
|
||||
{
|
||||
int len;
|
||||
return set_amxstring(amx, params[1], format_amxstring(amx, params, 3, len), params[2]);
|
||||
//int len;
|
||||
//return set_amxstring(amx, params[1], format_amxstring(amx, params, 3, len), params[2]);
|
||||
cell *buf = get_amxaddr(amx, params[1]);
|
||||
cell *fmt = get_amxaddr(amx, params[3]);
|
||||
size_t maxlen = params[2];
|
||||
int param = 4;
|
||||
size_t total = atcprintf(buf, maxlen, fmt, amx, params, ¶m);
|
||||
return total;
|
||||
}
|
||||
|
||||
static cell AMX_NATIVE_CALL parse(AMX *amx, cell *params) /* 3 param */
|
||||
|
Loading…
x
Reference in New Issue
Block a user