initial import of new format zomg
This commit is contained in:
		| @@ -32,6 +32,7 @@ | |||||||
| #include <stdarg.h> | #include <stdarg.h> | ||||||
| #include "amxmodx.h" | #include "amxmodx.h" | ||||||
| #include "CLang.h" | #include "CLang.h" | ||||||
|  | #include "format.h" | ||||||
|  |  | ||||||
| #ifdef __linux__ | #ifdef __linux__ | ||||||
| #define _snprintf snprintf | #define _snprintf snprintf | ||||||
| @@ -368,277 +369,13 @@ int CLangMngr::GetKeyEntry(String &key) | |||||||
| 	return val.index; | 	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) | char * CLangMngr::FormatAmxString(AMX *amx, cell *params, int parm, int &len) | ||||||
| { | { | ||||||
| 	//do an initial run through all this  | 	//do an initial run through all this  | ||||||
| 	static char mystr[4096]; |  | ||||||
| 	static char outbuf[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 = atcprintf(outbuf, sizeof(outbuf)-1, addr, amx, params, &parm); | ||||||
|  |  | ||||||
| 	len = do_amx_format(amx, params, &parm, &ptr, outbuf, sizeof(outbuf)-1, 0); |  | ||||||
|  |  | ||||||
| 	return outbuf; | 	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); | 	return (TRUE); | ||||||
| } | } | ||||||
|  |  | ||||||
| #if !defined AMD64 |  | ||||||
| extern "C" void init_format_jumps(); |  | ||||||
| #else |  | ||||||
| void init_format_jumps() |  | ||||||
| { |  | ||||||
| } |  | ||||||
| #endif |  | ||||||
|  |  | ||||||
| static META_FUNCTIONS gMetaFunctionTable; | static META_FUNCTIONS gMetaFunctionTable; | ||||||
| C_DLLEXPORT	int	Meta_Attach(PLUG_LOADTIME now, META_FUNCTIONS *pFunctionTable, meta_globals_t *pMGlobals, gamedll_funcs_t *pGamedllFuncs) | 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 | 	// This will also call modules Meta_Query and Meta_Attach functions | ||||||
| 	loadModules(get_localinfo("amxx_modules", "addons/amxmodx/configs/modules.ini"), now); | 	loadModules(get_localinfo("amxx_modules", "addons/amxmodx/configs/modules.ini"), now); | ||||||
|  |  | ||||||
| 	init_format_jumps(); |  | ||||||
|  |  | ||||||
| 	return (TRUE); | 	return (TRUE); | ||||||
| } | } | ||||||
|  |  | ||||||
|   | |||||||
| @@ -303,7 +303,7 @@ | |||||||
| 			<Tool | 			<Tool | ||||||
| 				Name="VCCLCompilerTool" | 				Name="VCCLCompilerTool" | ||||||
| 				Optimization="0" | 				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" | 				PreprocessorDefinitions="WIN32;_DEBUG;_WINDOWS;_USRDLL;amxmodx_EXPORTS;PAWN_CELL_SIZE=32;ASM32;JIT" | ||||||
| 				BasicRuntimeChecks="3" | 				BasicRuntimeChecks="3" | ||||||
| 				RuntimeLibrary="5" | 				RuntimeLibrary="5" | ||||||
| @@ -671,6 +671,15 @@ | |||||||
| 			<File | 			<File | ||||||
| 				RelativePath="..\float.cpp"> | 				RelativePath="..\float.cpp"> | ||||||
| 			</File> | 			</File> | ||||||
|  | 			<File | ||||||
|  | 				RelativePath="..\format.cpp"> | ||||||
|  | 				<FileConfiguration | ||||||
|  | 					Name="JITRelease|Win32"> | ||||||
|  | 					<Tool | ||||||
|  | 						Name="VCCLCompilerTool" | ||||||
|  | 						AssemblerOutput="4"/> | ||||||
|  | 				</FileConfiguration> | ||||||
|  | 			</File> | ||||||
| 			<File | 			<File | ||||||
| 				RelativePath="..\md5.cpp"> | 				RelativePath="..\md5.cpp"> | ||||||
| 			</File> | 			</File> | ||||||
| @@ -823,6 +832,9 @@ | |||||||
| 			<File | 			<File | ||||||
| 				RelativePath="..\fakemeta.h"> | 				RelativePath="..\fakemeta.h"> | ||||||
| 			</File> | 			</File> | ||||||
|  | 			<File | ||||||
|  | 				RelativePath="..\format.h"> | ||||||
|  | 			</File> | ||||||
| 			<File | 			<File | ||||||
| 				RelativePath="..\md5.h"> | 				RelativePath="..\md5.h"> | ||||||
| 			</File> | 			</File> | ||||||
| @@ -957,13 +969,6 @@ | |||||||
| 				RelativePath="..\sdk\moduleconfig.h"> | 				RelativePath="..\sdk\moduleconfig.h"> | ||||||
| 			</File> | 			</File> | ||||||
| 		</Filter> | 		</Filter> | ||||||
| 		<Filter |  | ||||||
| 			Name="Helpers" |  | ||||||
| 			Filter=""> |  | ||||||
| 			<File |  | ||||||
| 				RelativePath="..\Jit\helpers-x86.obj"> |  | ||||||
| 			</File> |  | ||||||
| 		</Filter> |  | ||||||
| 	</Files> | 	</Files> | ||||||
| 	<Globals> | 	<Globals> | ||||||
| 	</Globals> | 	</Globals> | ||||||
|   | |||||||
| @@ -31,6 +31,7 @@ | |||||||
|  |  | ||||||
| #include <ctype.h> | #include <ctype.h> | ||||||
| #include "amxmodx.h" | #include "amxmodx.h" | ||||||
|  | #include "format.h" | ||||||
|  |  | ||||||
| const char* stristr(const char* str, const char* substr) | 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 */ | static cell AMX_NATIVE_CALL format(AMX *amx, cell *params) /* 3 param */ | ||||||
| { | { | ||||||
| 	int len; | 	//int len; | ||||||
| 	return set_amxstring(amx, params[1], format_amxstring(amx, params, 3, len), params[2]); | 	//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 */ | static cell AMX_NATIVE_CALL parse(AMX *amx, cell *params) /* 3 param */ | ||||||
|   | |||||||
		Reference in New Issue
	
	Block a user