04.08.2017, 08:39
I have updated ysi 4.0 but have 2 include my error I want to fix it
Include fuckcleo.inc
2 . Include strlib.inc
Код:
C:\Users\Administrator\Desktop\GTA-RP.VN\Windowns2k8\Windowns2k8\pawno\include\strlib.inc(1641) : error 021: symbol already defined: "sprintf" C:\Users\Administrator\Desktop\GTA-RP.VN\Windowns2k8\Windowns2k8\pawno\include\fuckcleo.inc(55) : warning 219: local variable "using_deprecated_foreach_syntax" shadows a variable at a preceding level C:\Users\Administrator\Desktop\GTA-RP.VN\Windowns2k8\Windowns2k8\pawno\include\fuckcleo.inc(107) : error 017: undefined symbol "OnVehicleDamageStatusUpdate" C:\Users\Administrator\Desktop\GTA-RP.VN\Windowns2k8\Windowns2k8\pawno\include\fuckcleo.inc(107) : error 010: invalid function or declaration Pawn compiler 3.2.3664 Copyright © 1997-2006, ITB CompuPhase 3 Errors.
Код:
/* * * "fuckCleo.inc" - #1 Anti-cleo mod include * Created/Debugged most by Lorenc_ * * Thanks to: * Cessil, Infamous and [FeK]Drakins, JernejL * */ /* ** Stop Duplicating Code! ** */ #if defined _fcleo_included #endinput #endif #define _fcleo_included /* ** Includes ** */ #include < a_samp > /* ** Configuration ** */ #define FC_FILE_VERSION "0.3.5" // Added the FC_ #define CLEO_FAKEKILL ( 1 ) #define CLEO_CARWARP ( 2 ) #define CLEO_PLAYERBUGGER ( 3 ) // This number will never be called from OnPlayerCleoDetected #define CLEO_CARSWING ( 4 ) #define CLEO_CAR_PARTICLE_SPAM ( 5 ) /* ** Variables ** */ enum E_CLEO_DATA { E_FK_LASTDEATH, E_FK_DEATHSPAM, E_CW_TIME, E_CW_VEHID, Float: E_PB_X, Float: E_PB_Y, Float: E_PB_Z, E_CPS_SPAMCOUNT, E_CPS_TICK }; static stock g_cleoData [ MAX_PLAYERS ] [ E_CLEO_DATA ], bool: FC_OPU, bool: FC_OPD, bool: FC_OPSC, bool: FC_OVDSU, bool: FC_OPDC ; /* ** Forwards ** */ forward OnPlayerCleoDetected ( playerid, cleoid ); forward fc_RepairVehicle ( vehicleid ); public fc_RepairVehicle( vehicleid ) { #if defined _FOREACH_LOCAL_VERSION // I guess this is always defined in foreach.inc foreach(Player, playerid) { if( GetPlayerVehicleID( playerid ) == vehicleid ) { g_cleoData[ playerid ] [ E_CPS_SPAMCOUNT ] = 0; g_cleoData[ playerid ] [ E_CPS_TICK ] = 0; } } #else for( new playerid; playerid < MAX_PLAYERS; playerid++ ) { if( IsPlayerConnected( playerid ) ) { if( GetPlayerVehicleID( playerid ) == vehicleid ) { g_cleoData[ playerid ] [ E_CPS_SPAMCOUNT ] = 0; g_cleoData[ playerid ] [ E_CPS_TICK ] = 0; } } } #endif return RepairVehicle( vehicleid ); } #define RepairVehicle fc_RepairVehicle public OnVehicleDamageStatusUpdate( vehicleid, playerid ) { static fc_tires, fc_lights; GetVehicleDamageStatus( vehicleid, fc_lights, fc_tires, fc_lights, fc_tires ); if( GetPlayerState( playerid ) == PLAYER_STATE_DRIVER ) { if( fc_lights || fc_tires ) return 1; new time = GetTickCount( ); switch( time - g_cleoData[ playerid ] [ E_CPS_TICK ] ) { case 0 .. 500: { g_cleoData[ playerid ] [ E_CPS_SPAMCOUNT ] ++; if( g_cleoData[ playerid ] [ E_CPS_SPAMCOUNT ] >= 10 ) { CallLocalFunction( "OnPlayerCleoDetected", "dd", playerid, CLEO_CAR_PARTICLE_SPAM ); return 1; } } default: g_cleoData[ playerid ] [ E_CPS_SPAMCOUNT ] = 0; } g_cleoData[ playerid ] [ E_CPS_TICK ] = time; } return ( FC_OVDSU ) ? CallLocalFunction( "FC_OnVehicleDamageStatusUpdate", "dd", vehicleid, playerid ) : 1; } #if defined ALS_OnVehicleDamageStatusUpdate #undef OnVehicleDamageStatusUpdate #else #define ALS_OnVehicleDamageStatusUpdate #endif #define OnVehicleDamageStatusUpdate FC_OnVehicleDamageStatusUpdate forward FC_OnVehicleDamageStatusUpdate( vehicleid, playerid ); public OnPlayerStateChange( playerid, newstate, oldstate ) { if( newstate == PLAYER_STATE_DRIVER ) { if( GetPlayerVehicleID( playerid ) != g_cleoData[ playerid ] [ E_CW_VEHID ] ) { if( g_cleoData[ playerid ] [ E_CW_TIME ] > gettime( ) ) { CallLocalFunction( "OnPlayerCleoDetected", "dd", playerid, CLEO_CARWARP ); return 1; } g_cleoData[ playerid ] [ E_CW_TIME ] = gettime( ) + 1; g_cleoData[ playerid ] [ E_CW_VEHID ] = GetPlayerVehicleID( playerid ); } } return ( FC_OPSC ) ? CallLocalFunction( "FC_OnPlayerStateChange", "ddd", playerid, newstate, oldstate ) : 1; } #if defined _ALS_OnPlayerStateChange #undef OnPlayerStateChange #else #define _ALS_OnPlayerStateChange #endif #define OnPlayerStateChange FC_OnPlayerStateChange forward FC_OnPlayerStateChange( playerid, newstate, oldstate ); public OnPlayerDeath( playerid, killerid, reason ) { new time = gettime( ); switch( time - g_cleoData[ playerid ] [ E_FK_LASTDEATH ] ) { case 0 .. 3: { g_cleoData[ playerid ] [ E_FK_DEATHSPAM ] ++; if( g_cleoData[ playerid ] [ E_FK_DEATHSPAM ] >= 3 ) { CallLocalFunction( "OnPlayerCleoDetected", "dd", playerid, CLEO_FAKEKILL ); return 1; } } default: g_cleoData[ playerid ] [ E_FK_DEATHSPAM ] = 0; } g_cleoData[ playerid ] [ E_FK_LASTDEATH ] = time; return ( FC_OPD ) ? CallLocalFunction( "FC_OnPlayerDeath", "ddd", playerid, killerid, reason ) : 1; } #if defined _ALS_OnPlayerDeath #undef OnPlayerDeath #else #define _ALS_OnPlayerDeath #endif #define OnPlayerDeath FC_OnPlayerDeath forward FC_OnPlayerDeath( playerid, killerid, reason ); public OnPlayerUpdate( playerid ) { static Float: X, Float: Y, Float: Z, Float: vX, Float: vY, Float: vZ ; GetPlayerPos( playerid, X, Y, Z ); if( X >= 99999.0 || Y >= 99999.0 || Z >= 99999.0 || X <= -99999.0 || Y <= -99999.0 || Z <= -99999.0 ) { SendClientMessage( playerid, 0xa9c4e4ff, "Warning: Excessive X, Y, Z has been breached thus last location set." ); SetPlayerPos( playerid, g_cleoData[ playerid ] [ E_PB_X ], g_cleoData[ playerid ] [ E_PB_Y ], g_cleoData[ playerid ] [ E_PB_Z ] ); } else { g_cleoData[ playerid ] [ E_PB_X ] = X; g_cleoData[ playerid ] [ E_PB_Y ] = Y; g_cleoData[ playerid ] [ E_PB_Z ] = Z; } if( IsPlayerInAnyVehicle( playerid ) && GetPlayerState( playerid ) == PLAYER_STATE_DRIVER ) { GetPlayerPos( playerid, X, Y, Z ); GetVehicleVelocity( GetPlayerVehicleID( playerid ), vX, vY, vZ ); if( ( vX > 3.0 || vY > 3.0 || vZ > 3.0 || vX < -3.0 || vY < -3.0 || vZ < -3.0 ) && ( vX != X && vY != Y && vZ != Z ) ) { CallLocalFunction( "OnPlayerCleoDetected", "dd", playerid, CLEO_CARSWING ); } } return ( FC_OPU ) ? CallLocalFunction( "FC_OnPlayerUpdate", "d", playerid ) : 1; } #if defined _ALS_OnPlayerUpdate #undef OnPlayerUpdate #else #define _ALS_OnPlayerUpdate #endif #define OnPlayerUpdate FC_OnPlayerUpdate forward FC_OnPlayerUpdate( playerid ); public OnPlayerDisconnect(playerid, reason) { // Pointless reseting, but just incase! g_cleoData[ playerid ] [ E_FK_LASTDEATH ] = 0; g_cleoData[ playerid ] [ E_FK_DEATHSPAM ] = 0; g_cleoData[ playerid ] [ E_CW_TIME ] = 0; g_cleoData[ playerid ] [ E_CW_VEHID ] = INVALID_VEHICLE_ID; g_cleoData[ playerid ] [ E_PB_X ] = -1; g_cleoData[ playerid ] [ E_PB_Y ] = -1; g_cleoData[ playerid ] [ E_PB_Z ] = -1; g_cleoData[ playerid ] [ E_CPS_SPAMCOUNT ] = 0; g_cleoData[ playerid ] [ E_CPS_TICK ] = 0; return ( FC_OPDC ) ? CallLocalFunction( "FC_OnPlayerDisconnect", "dd", playerid, reason ) : 1; } #if defined _ALS_OnPlayerDisconnect #undef OnPlayerDisconnect #else #define _ALS_OnPlayerDisconnect #endif #define OnPlayerDisconnect FC_OnPlayerDisconnect forward FC_OnPlayerDisconnect( playerid, reason ); #if !defined FILTERSCRIPT public OnGameModeInit() { FC_OPU = ( funcidx( "FC_OnPlayerUpdate" ) != -1 ); FC_OPD = ( funcidx( "FC_OnPlayerDeath" ) != -1 ); FC_OPSC = ( funcidx( "FC_OnPlayerStateChange" ) != -1 ); FC_OPDC = ( funcidx( "FC_OnPlayerDisconnect" ) != -1 ); FC_OVDSU = ( funcidx( "FC_OnVehicleDamageStatusUpdate" ) != -1 ); return ( funcidx( "FC_OnGameModeInit" ) != -1 ) ? CallLocalFunction( "FC_OnGameModeInit", "" ) : 1; } #if defined _ALS_OnGameModeInit #undef OnGameModeInit #else #define _ALS_OnGameModeInit #endif #define OnGameModeInit FC_OnGameModeInit forward FC_OnGameModeInit(); #else public OnFilterScriptInit() { FC_OPU = ( funcidx( "FC_OnPlayerUpdate" ) != -1 ); FC_OPD = ( funcidx( "FC_OnPlayerDeath" ) != -1 ); FC_OPSC = ( funcidx( "FC_OnPlayerStateChange" ) != -1 ); FC_OPDC = ( funcidx( "FC_OnPlayerDisconnect" ) != -1 ); FC_OVDSU = ( funcidx( "FC_OnVehicleDamageStatusUpdate" ) != -1 ); return ( funcidx( "FC_OnFilterScriptInit" ) != -1 ) ? CallLocalFunction( "FC_OnFilterScriptInit", "" ) : 1; } #if defined _ALS_OnFilterScriptInit #undef OnFilterScriptInit #else #define _ALS_OnFilterScriptInit #endif #define OnFilterScriptInit FC_OnFilterScriptInit forward FC_OnFilterScriptInit(); #endif
Код:
#include <a_samp> #if !defined STRLIB_BUFFER_SIZE #define STRLIB_BUFFER_SIZE 2048 #endif #if !defined STRLIB_RETURN_SIZE #define STRLIB_RETURN_SIZE 128 #endif #if !defined STRLIB_USE_FORMATEX #if defined __fmt_funcinc #if !defined FormatSpecifier #error Please include formatex before strlib. #endif #define STRLIB_USE_FORMATEX true #else #define STRLIB_USE_FORMATEX false #endif #endif // Used in strtrim (deprecated) enum trim_edges { trim_left = 1, trim_right = 2, trim_both = trim_left | trim_right }; // Used in strtrim and strpad enum string_edges { edge_left = 1, edge_right = 2, edge_both = edge_left | edge_right }; /* * Returns a formatted string. * * Parameters: * fmat[] - The format string. * ... - The format variables. * * Returns: * The formatted string. */ forward sprintf(const fmat[], {Float, _}:...); /* * Get the first character of a string * * Parameters: * string[] - The string. * * Returns: * The first character of the string. */ forward strgetfirstc(const string[]); /* * Get a character from a specific index in a string. * * Parameters: * string[] - The string. * index - The position in the string. * * Returns: * The character at that index, or '\0' if out of range. */ forward strgetc(const string[], index); /* * Get the size of a string. * * Parameters: * string[] - The string. * * Returns: * The size of the string, in bytes. */ forward strsize(const string[]); /* * Find out if a string is empty. * * Parameters: * string[] - The string. * * Returns: * True if empty, otherwise false. */ forward bool:isempty(const string[]); /* * Compare two strings. * * Parameters: * str1[] - The first string. * str2[] - The second string. * ignorecase - Whether to compare them in a case-insensitive manner. * * Returns: * True if equal, otherwise false. */ forward bool:isequal(const str1[], const str2[], bool:ignorecase = false); /* * Split a string by a given delimiter. * * Parameters: * output[][] - A multi-dimensional array that will be filled with substrings. * input[] - The input string to split. * delimiter[] - The delimiter to split by. Defaults to ",". * limit - The max. no. substrings. * trim - Whether to trim the substrings from whitespace. Defaults to true. * ignorecase - Whether the search for "delimiter" should be case-insensitive. * size1 - The size of the 1st dimension of output (otput[this][]). Defaults to sizeof(output). * size2 - The size of the 2nd dimension of output (otput[][this]). Defaults to sizeof(output[]). * * Returns: * The number of substrings that were copied into the array. */ forward strexplode(output[][], const input[], const delimiter[] = !",", limit = cellmax, bool:trim = true, bool:ignorecase = false, size1 = sizeof(output), size2 = sizeof(output[])); /* * Glue together strings into one. * * Parameters: * glue[] - The string that will be between all other strings. * output[] - The output string. * maxlength - The size of "output". Defaults to sizeof(output). * ...[] - Strings to glue together. * * Returns: * Nothing */ forward strimplode(const glue[], output[], maxlength = sizeof(output), ...); /* * Replace occurrences of the search string with the replacement string. * * Parameters: * string[] - The string to perform the replacing in. * search[] - The string to look for. * replacement[] - The string to put instead of "search". * ignorecase - Whether the search for "search" should be case-insensitive. Defaults to false. * pos - The position to start at. Defaults to 0 (the beginning). * limit - Limit the number of replacements. Defaults to -1 (no limit). * maxlength - The size of "string". Defaults to sizeof(string). * * Returns: * The number of replacements that were made. */ forward strreplace(string[], const search[], const replacement[], bool:ignorecase = false, pos = 0, limit = -1, maxlength = sizeof(string)); /* * Trim whitespace or a specific group of characters from a string. * * Parameters: * string[] - The string to trim. * chars[] - A string with characters to trim, or all whitespace if empty. Default is all whitespace. * edge - The edge(s) to trim (edge_left/edge_right/edge_both). Default is edge_both. * * Returns: * Nothing */ forward strtrim(string[], const chars[] = !"", string_edges:edge = edge_both); /* * Pad edge(s) of a string with spaces. * * Parameters: * string[] - The string to pad. * length - The new length of the string. * substr[] - The substring to pad with. Defaults to a space (" "). * edge - The edge(s) to pad (edge_left/edge_right/edge_both). Default is edge_both. * trim_first - Whether to trim the string before padding. * trim_chars[] - The chars to trim, defaults is all whitespace. * maxlength - The size of "string". Defaults to sizeof(string). * input - Used internally. */ forward strpad(string[], length, const substr[] = !" ", string_edges:edge = edge_both, bool:trim_first = true, const trim_chars[] = !"", maxlength = sizeof(string), const input[] = !""); /* * Wrap a string inside two other strings. * * Parameters: * left[] - The string on the left side. * string[] - The middle string that will be modified. * right[] - The string on the right side. * maxlength - The size of "string". Defaults to sizeof(string). */ forward strwrap(const left[], string[], const right[], maxlength = sizeof(string)); /* * Count substrings. * * Parameters: * string[] - The string to search inside. * sub[] - The string to search for. * ignorecase - Whether the search should be case-insensitive. * count_overlapped - Whether to count overlapping strings ("abcabc" in "abcabcabc" will count 2 instead of 1). * * Returns: * The number of occurrences of "sub" in "string". */ forward strcount(const string[], const sub[], bool:ignorecase = false, bool:count_overlapped = false); /* * Read a string from a PAWN string literal. * * Parameters: * output[] - The variable to save into. * input[] - The string literal. * pos - The position in "input" to start reading from. Will be modified to the end of the literal. * maxlength - The size of "output". Defaults to sizeof(output). * * Returns: * true on success, false on error. */ forward bool:strfromliteral(output[], const input[], &pos = 0, maxlength = sizeof(output)); /* * Build a PAWN string literal from a given string. * * Parameters: * output[] - The variable to save into. * substrings[] - The string to build from. * maxlength - The size of "output". Defaults to sizeof(output). * * Returns: * Nothing */ forward strtoliteral(output[], const input[], maxlength = sizeof(output), bool:paranoid = true); /* * Convert an array to a string. * * Example: {0x1122, 0x5566} becomes "0000112200005566". * * Parameters: * output[] - The variable to save into. * input[] - The array to build from. * inputlength - The size of "input". Defaults to sizeof(input). * maxlength - The size of "output". Defaults to sizeof(output). * * Returns: * Nothing */ forward strfrombin(output[], const input[], inputlength = sizeof(input), maxlength = sizeof(output)); /* * Convert a string to an array. * * Example: "0000112200005566" becomes {0x1122, 0x5566}. * * Parameters: * output[] - The variable to save into. * input[] - The array to build from. * maxlength - The size of "output". Defaults to sizeof(output). * * Returns: * The length of the output, in cells. */ forward strtobin(output[], const input[], maxlength = sizeof(output)); /* * Concatenate one string with a part of another. * * Parameters: * dest[] - The variable to concatenate the other part with. * source[] - The string to extract from. * start - The start offset, defaults to 0. * end - The start offset, defaults to end of string. * maxlength - The size of "dest". Defaults to sizeof(dest). */ forward strcatmid(dest[], const source[], start = 0, end = -1, maxlength = sizeof(dest)); /* * UTF-8 encode a string. Characters above 127 will be encoded into * two or more characters. * * Parameters: * dest[] - The output variable. * source[] - The string to encode. * maxlength - The size of "dest". Defaults to sizeof(dest). */ forward utf8encode(dest[], const source[], maxlength = sizeof(dest)); /* * UTF-8 decode a string. UTF-8 characters will be collapsed into single * characters in the array. * * Parameters: * dest[] - The output variable. * source[] - The string to encode. * maxlength - The size of "dest". Defaults to sizeof(dest). */ forward utf8decode(dest[], const source[], maxlength = sizeof(dest)); /* * Decode an encoded URL. * * Parameters: * output[] - The output variable. * input[] - The string to decode. * maxlength - The size of "output". Defaults to sizeof(output). */ forward strurldecode(output[], const input[], maxlength = sizeof(output)); /* * URL encode a string. * * Parameters: * output[] - The output variable. * input[] - The string to encode. * maxlength - The size of "output". Defaults to sizeof(output). * pack - Whether to pack the output. Defaults to false. */ forward strurlencode(output[], const input[], maxlength = sizeof(output), bool:pack = false); // Same as above, but output is returned forward ret_strcatmid(const string[], const source[], start = 0, end = -1); forward ret_strfrombin(const input[], inputlength = sizeof(input)); forward ret_strimplode(const glue[], ...); forward ret_strreplace(const string[], const search[], const replacement[], bool:ignorecase = false, pos = 0, limit = -1); forward ret_strfromliteral(const input[], &pos = 0); forward ret_strtoliteral(const input[], bool:paranoid = true); forward ret_strtrim(const string[], const chars[] = !"", string_edges:edge = edge_both); forward ret_strpad(const string[], length, const substr[] = !" ", string_edges:edge = edge_both, bool:trim_first = true, const trim_chars[] = !""); forward ret_strwrap(const left[], const string[], const right[]); forward ret_strurldecode(const input[]); forward ret_strurlencode(const input[], bool:pack = false); forward ret_utf8encode(const input[]); forward ret_utf8decode(const input[]); // Return from native functions forward ret_strpack(const source[]); forward ret_strunpack(const source[]); forward ret_strcat(const string1[], const string2[]); forward ret_strmid(const source[], start, end); forward ret_strins(const string[], const substr[], pos, maxlength = sizeof(string)); forward ret_strdel(const string[], start, end); forward ret_valstr(value, bool:pack = false); forward ret_GetPlayerName(playerid, bool:pack = false); stock // Used throughout the library g_StrlibBuffer[2048] ; // Workaround for compiler bug forward _strlib_funcinc(); public _strlib_funcinc() { new temp[1]; format(!"", 0, !""); strcat(temp, temp); strpack(temp, temp); strunpack(temp, temp); } // Internal functions static stock RedirectArgument(arg, ...) { #emit LOAD.S.pri 0 #emit ADD.C 12 #emit LOAD.S.alt arg #emit SHL.C.alt 2 #emit ADD #emit MOVE.alt #emit LOAD.S.pri 16 #emit STOR.I } static stock CopyArgumentToHeap(arg, bool:pack = false, const argptr[] = "") { new arg_address, address; #emit LOAD.S.pri 0 #emit ADD.C 12 #emit LOAD.S.alt arg #emit SHL.C.alt 2 #emit ADD #emit LOAD.I #emit STOR.S.pri arg_address #emit STOR.S.pri argptr if (pack) { new bytes = ((strlen(argptr) + 1 + 3) / 4) * 4; #emit LCTRL 2 #emit STOR.S.pri address #emit LOAD.S.alt bytes #emit ADD #emit SCTRL 2 //strpack(dest[], const source[], maxlength = sizeof dest) #emit LOAD.S.pri bytes #emit SHR.C.pri 2 #emit PUSH.pri #emit PUSH.S arg_address #emit PUSH.S address #emit PUSH.C 12 #emit SYSREQ.C strpack #emit STACK 16 } else { new bytes = (strlen(argptr) + 1) * 4; #emit LCTRL 2 #emit STOR.S.pri address #emit LOAD.S.alt bytes #emit ADD #emit SCTRL 2 //strunpack(dest[], const source[], maxlength = sizeof dest) #emit LOAD.S.pri bytes #emit SHR.C.pri 2 #emit PUSH.pri #emit PUSH.S arg_address #emit PUSH.S address #emit PUSH.C 12 #emit SYSREQ.C strunpack #emit STACK 16 } #emit LOAD.S.pri 0 #emit ADD.C 12 #emit LOAD.S.alt arg #emit SHL.C.alt 2 #emit ADD #emit MOVE.alt #emit LOAD.S.pri address #emit STOR.I return address; } static stock RestoreHeapToAddress(address) { #emit LOAD.S.pri address #emit SCTRL 2 } static stock IsOverlapping(const str1[], size1 = sizeof(str1), const str2[], size2 = sizeof(str2)) { new addr1, addr2; if (size1 == -1) { size1 = strsize(str1); } else { size1 *= 4; } if (size2 == -1) { size2 = strsize(str2); } else { size2 *= 4; } #emit LOAD.S.pri str1 #emit STOR.S.pri addr1 #emit LOAD.S.pri str2 #emit STOR.S.pri addr2 return (addr1 < addr2 + size2) && (addr2 < addr1 + size1); } // strlib functions #define ispacked(%1) \ ((%1)[0] > 255) stock strgetfirstc(const string[]) { return ispacked(string) ? string{0} : string[0]; } stock strgetc(const string[], index) { if (index < 0) return '\0'; new len = strlen(string); if (index >= len) return '\0'; return ispacked(string) ? string{index} : string[index]; } stock strsize(const string[]) { new len = strlen(string); if (ispacked(string)) return len + 1; return (len + 1) * 4; } stock bool:isempty(const string[]) { if (ispacked(string)) return string{0} == '\0'; else return string[0] == '\0'; } stock bool:isequal(const str1[], const str2[], bool:ignorecase = false) { new c1 = (str1[0] > 255) ? str1{0} : str1[0], c2 = (str2[0] > 255) ? str2{0} : str2[0] ; if (!c1 != !c2) return false; return !strcmp(str1, str2, ignorecase); } stock strimplode(const glue[], output[], maxlength = sizeof(output), ...) { new args = numargs(); // Null-out "output" output[0] = '\0'; // Loop the variable arguments (the ones after "maxlength"). for (new arg = 3; arg < args; arg++) { // If this isn't the first string, append the glue. if (arg != 3) strcat(output, glue, maxlength); // Wrap these in braces or they will be a part of the above if statement (compiler bug) { // Get the address of argument no. <arg> #emit LCTRL 5 #emit ADD.C 12 #emit LOAD.S.alt arg #emit SHL.C.alt 2 #emit ADD #emit LOAD.I // Push the maxlength, arg address, and output address #emit PUSH.S maxlength #emit PUSH.pri #emit PUSH.S output // Push the argument count #emit PUSH.C 12 // call strcat #emit SYSREQ.C strcat // Restore the stack #emit STACK 16 } } } stock strexplode(output[][], const input[], const delimiter[] = !",", limit = cellmax, bool:trim = true, bool:ignorecase = false, size1 = sizeof(output), size2 = sizeof(output[])) { if (!size1 || !size2) { printf("(strexplode) ERROR: size1 = %d, size2 = %d. Can't be 0.", size1, size2); return 0; } if (isempty(delimiter)) { print(!"(strexplode) ERROR: delimiter is empty."); return 0; } if (trim) { new i = -1; if (ispacked(input)) { while (input{++i}) { if (input{i} > ' ') { i = -1; break; } } } else { while (input[++i]) { if (input[i] > ' ') { i = -1; break; } } } if (i != -1) return 0; } else if (isempty(input)) { return 0; } if (limit == 0) { return 0; } else if (limit == cellmax) { limit = 0; } new pos = 0, next, bool:packed = ispacked(input), dlen = strlen(delimiter), count = 0, end ; while (pos != -1) { ++count; if (limit > 0 && count >= limit) { next = -1; } else { next = strfind(input, delimiter, ignorecase, pos); } end = (next == -1) ? cellmax : next; if (trim) { if (end == cellmax) end = strlen(input); if (packed) { while (0 < input{pos} <= ' ') pos++; while (end > 0 && input{end - 1} <= ' ') end--; } else { while (0 < input[pos] <= ' ') pos++; while (end > 0 && input[end - 1] <= ' ') end--; } } strmid(output[count - 1], input, pos, end, size2); if (count >= size1 || next == -1 || (limit < 0 && count >= -limit)) break; pos = next + dlen; } return count; } stock strreplace(string[], const search[], const replacement[], bool:ignorecase = false, pos = 0, limit = -1, maxlength = sizeof(string)) { // No need to do anything if the limit is 0. if (limit == 0) return 0; new sublen = strlen(search), replen = strlen(replacement), bool:packed = ispacked(string), maxlen = maxlength, len = strlen(string), count = 0 ; // "maxlen" holds the max string length (not to be confused with "maxlength", which holds the max. array size). // Since packed strings hold 4 characters per array slot, we multiply "maxlen" by 4. if (packed) maxlen *= 4; // If the length of the substring is 0, we have nothing to look for.. if (!sublen) return 0; // In this line we both assign the return value from "strfind" to "pos" then check if it's -1. while (-1 != (pos = strfind(string, search, ignorecase, pos))) { // Delete the string we found strdel(string, pos, pos + sublen); len -= sublen; // If there's anything to put as replacement, insert it. Make sure there's enough room first. if (replen && len + replen < maxlen) { strins(string, replacement, pos, maxlength); pos += replen; len += replen; } // Is there a limit of number of replacements, if so, did we break it? if (limit != -1 && ++count >= limit) break; } return count; } stock strtrim(string[], const chars[] = !"", string_edges:edge = edge_both) { new bool:packed = ispacked(string); // If "chars" is empty, trim whitespace if (!strgetfirstc(chars)) { // Should the left side be trimmed? if (edge & edge_left) { new i = 0; if (packed) while (0 < string{i} <= ' ') i++; else while (0 < string[i] <= ' ') i++; if (i) { strdel(string, 0, i); } } // Should the right side be trimmed? if (edge & edge_right) { new i = strlen(string); if (i) { if (packed) { while (--i && 0 < string{i} <= ' ') {} string{i + 1} = '\0'; } else { while (--i && 0 < string[i] <= ' ') {} string[i + 1] = '\0'; } } } } else { // Should the left side be trimmed? if (edge & edge_left) { new i = 0, sub[2]; if (packed) { while ((sub[0] = string{i})) { if (strfind(chars, sub) == -1) break; i++; } if (i) { strdel(string, 0, i); } } else { while ((sub[0] = string[i])) { if (strfind(chars, sub) == -1) break; i++; } if (i) strdel(string, 0, i); } } // Should the right side be trimmed? if (edge & edge_right) { new i = strlen(string), sub[2]; if (i >= 0) { if (packed) { while (i--) { sub[0] = string{i}; if (strfind(chars, sub) == -1) break; } string{i + 1} = '\0'; } else { while (i--) { sub[0] = string[i]; if (strfind(chars, sub) == -1) break; } string[i + 1] = '\0'; } } } } } stock strpad(string[], length, const substr[] = !" ", string_edges:edge = edge_both, bool:trim_first = true, const trim_chars[] = !"", maxlength = sizeof(string), const input[] = !"") { if (trim_first) { strtrim(string, trim_chars, edge); } new heap, length_left = 0, length_right = 0, len = strlen(string), sublen = strlen(substr), bool:packed, bool:subpacked = ispacked(substr) ; if (len > length) return; else length -= len; // Make "input" a pointer to "string" #emit LOAD.S.pri string #emit STOR.S.pri input // Copy "input" to the heap so it won't be linked to "string" anymore. heap = CopyArgumentToHeap(7); string[0] = '\0'; len = 0; switch (edge) { case edge_left: length_left = length; case edge_right: length_right = length; default: length_left = length / 2, length_right = length - length_left; } if (length_left) { while (len < length_left) { if (subpacked) strcat(string, substr, length_left * 4); else strcat(string, substr, length_left + 1); len += sublen; } if (subpacked) string{length_left} = 0; } strcat(string, input, maxlength); if (length_right) { len = strlen(string); length_right += len; packed = ispacked(string); while (len < length_right) { if (packed) strcat(string, substr, length_right / 4 + 1); else strcat(string, substr, length_right + 1); len += sublen; } if (packed) string{length_right + 1} = 0; } RestoreHeapToAddress(heap); } stock strwrap(const left[], string[], const right[], maxlength = sizeof(string)) { strins(string, left, 0, maxlength); strcat(string, right, maxlength); } stock strcount(const string[], const sub[], bool:ignorecase = false, bool:count_overlapped = false) { new increment = count_overlapped ? 1 : strlen(sub), pos = -increment, count = 0 ; while (-1 != (pos = strfind(string, sub, ignorecase, pos + increment))) count++; return count; } stock bool:strfromliteral(output[], const input[], &pos = 0, maxlength = sizeof(output)) { new length = strlen(input), c, outlen = 0, heap = 0 ; // No need to do anything else. if (!length) return true; if (IsOverlapping(output, maxlength, input, -1)) heap = CopyArgumentToHeap(1); output[0] = '\0'; if (input[0] == '"') pos++; for (;; pos++) { if (outlen >= maxlength - 1 || pos >= length) break; c = input[pos]; switch © { // String ended case '"': break; case '\\': {} default: { output[outlen++] = c; continue; } } // String ends with a backslash - invalid. if (pos == length - 1) goto return_false; // We're after a backslash now, let's see what's there. c = input[++pos]; switch © { case '"', '\'', '\\', '%': output[outlen++] = c; case 'a': output[outlen++] = '\a'; case 'b': output[outlen++] = '\b'; case 'e': output[outlen++] = '\e'; case 'f': output[outlen++] = '\f'; case 'r': output[outlen++] = '\r'; case 'n': output[outlen++] = '\n'; case 't': output[outlen++] = '\t'; case 'v': output[outlen++] = '\v'; case 'x': { new val = 0; // String ends with "\x" - invalid. if (c == length - 1) goto return_false; while ((c = input[pos + 1])) { if ('a' <= c <= 'f' || 'A' <= c <= 'F') { val = (val << 4) + (tolower© - 'a' + 10); } else if ('0' <= c <= '9') { val = (val << 4) + (c - '0'); } else { break; } pos++; } if (c == ';') pos++; output[outlen++] = val; } case '0' .. '9': { new val = 0; while ((c = input[pos])) { if ('0' <= c <= '9') { val = val * 10 + (c - '0'); } else { break; } pos++; } if (c != ';') pos--; output[outlen++] = val; } default: { goto return_false; } } } output[outlen] = '\0'; pos++; new bool:ret = true; goto return_true; return_false: ret = false; return_true: if (heap) RestoreHeapToAddress(heap); return ret; } stock strtoliteral(output[], const input[], maxlength = sizeof(output), bool:paranoid = true) { new i, c, outlen, heap = 0; if (IsOverlapping(output, maxlength, input, -1)) heap = CopyArgumentToHeap(1); output[outlen++] = '"'; for (i = 0; (c = input[i]); i++) { if (maxlength - outlen <= 3) { outlen = min(outlen, maxlength - 2); break; } switch © { case ' ', '!', '#' .. '[', ']', '^' .. '~': output[outlen++] = c; case '"': strunpack(output[outlen], !"\\\"", 3), outlen += 2; case '\a': strunpack(output[outlen], !"\\a" , 3), outlen += 2; case '\b': strunpack(output[outlen], !"\\b" , 3), outlen += 2; case '\e': strunpack(output[outlen], !"\\e" , 3), outlen += 2; case '\f': strunpack(output[outlen], !"\\f" , 3), outlen += 2; case '\r': strunpack(output[outlen], !"\\r" , 3), outlen += 2; case '\n': strunpack(output[outlen], !"\\n" , 3), outlen += 2; case '\t': strunpack(output[outlen], !"\\t" , 3), outlen += 2; case '\v': strunpack(output[outlen], !"\\v" , 3), outlen += 2; case '\\': strunpack(output[outlen], !"\\\\" , 3), outlen += 2; default: { if (!paranoid && 0x80 <= c <= 0xFF) { output[outlen++] = c; continue; } if (maxlength - outlen <= 8) break; format(output[outlen], 7, "\\x%03x;", c); outlen += 6; } } } output[outlen++] = '"'; output[outlen] = '\0'; if (heap) RestoreHeapToAddress(heap); } stock strfrombin(output[], const input[], inputlength = sizeof(input), maxlength = sizeof(output)) { static const hex_chars[] = "0123456789ABCDEF"; new outlen = 0, heap = 0; if (IsOverlapping(output, maxlength, input, -1)) heap = CopyArgumentToHeap(1); for (new i = 0; i < inputlength; i++) { if (maxlength - outlen <= 7) { outlen = min(outlen, maxlength - 1); break; } new input_cell = input[i]; output[outlen++] = hex_chars[(input_cell ) >>> 28]; output[outlen++] = hex_chars[(input_cell & 0x0F000000) >>> 24]; output[outlen++] = hex_chars[(input_cell & 0x00F00000) >>> 20]; output[outlen++] = hex_chars[(input_cell & 0x000F0000) >>> 16]; output[outlen++] = hex_chars[(input_cell & 0x0000F000) >>> 12]; output[outlen++] = hex_chars[(input_cell & 0x00000F00) >>> 8]; output[outlen++] = hex_chars[(input_cell & 0x000000F0) >>> 4]; output[outlen++] = hex_chars[(input_cell & 0x0000000F) ]; } output[outlen] = '\0'; if (heap) RestoreHeapToAddress(heap); } stock strtobin(output[], const input[], maxlength = sizeof(output)) { new len = strlen(input), outlen = 0, heap = 0; if (IsOverlapping(output, maxlength, input, -1)) heap = CopyArgumentToHeap(1); for (new i = 0; i < len;) { if (outlen >= maxlength || i > len - 8) { break; } new c, out = 0; #define ADD_OUT(%1) \ c = input[i++]; out |= (('a' <= c <= 'f' || 'A' <= c <= 'F') ? (tolower© - 'a' + 10) : (c - '0')) << %1 ADD_OUT(28); ADD_OUT(24); ADD_OUT(20); ADD_OUT(16); ADD_OUT(12); ADD_OUT(8); ADD_OUT(4); ADD_OUT(0); #undef ADD_OUT output[outlen++] = out; } if (heap) RestoreHeapToAddress(heap); return outlen; } stock strurlencode(output[], const input[], maxlength = sizeof(output), bool:pack = false) { static const hex_chars[] = "0123456789ABCDEF"; new len = strlen(input), bool:packed = ispacked(input), outlen = 0, heap = 0 ; if (IsOverlapping(output, maxlength, input, -1)) heap = CopyArgumentToHeap(1, packed); if (pack) maxlength *= 4; for (new i = 0; i < len; i++) { if (maxlength - outlen <= 1) break; new c = packed ? input{i} : input[i]; switch © { case 'a' .. 'z', 'A' .. 'Z', '0' .. '9', '_': { if (pack) output{outlen++} = c; else output[outlen++] = c; } case ' ': { if (pack) output{outlen++} = '+'; else output[outlen++] = '+'; } default: { if (maxlength - outlen <= 3) break; if (pack) { output{outlen++} = '%'; output{outlen++} = hex_chars[(c & 0xF0) >>> 4]; output{outlen++} = hex_chars[c & 0x0F]; } else { output[outlen++] = '%'; output[outlen++] = hex_chars[(c & 0xF0) >>> 4]; output[outlen++] = hex_chars[c & 0x0F]; } } } } if (pack) output{outlen} = '\0'; else output[outlen] = '\0'; if (heap) RestoreHeapToAddress(heap); } stock strurldecode(output[], const input[], maxlength = sizeof(output)) { new prev_pos = 0, pos = 0, inputlen = strlen(input), len, heap = 0; if (IsOverlapping(output, maxlength, input, -1)) heap = CopyArgumentToHeap(1); output[0] = '\0'; while (-1 != (pos = strfind(input, "%", _, pos))) { static str[2]; new c; if (prev_pos != pos) { len = strlen(output); strcatmid(output, input, prev_pos, pos, maxlength); strreplace(output, "+", " ", _, len, _, maxlength); } if (inputlen < pos + 3) goto func_end; str[0] = 0; c = input[pos + 1]; str[0] |= (('a' <= c <= 'f' || 'A' <= c <= 'F') ? (tolower© - 'a' + 10) : (c - '0')) << 4; c = input[pos + 2]; str[0] |= (('a' <= c <= 'f' || 'A' <= c <= 'F') ? (tolower© - 'a' + 10) : (c - '0')); strcat(output, str, maxlength); prev_pos = (pos += 3); } len = strlen(output); strcatmid(output, input, prev_pos, _, maxlength); strreplace(output, "+", " ", _, len, _, maxlength); func_end: if (heap) RestoreHeapToAddress(heap); } stock strcatmid(dest[], const source[], start = 0, end = -1, maxlength = sizeof(dest)) { new heap = 0; if (IsOverlapping(dest, maxlength, source, -1)) heap = CopyArgumentToHeap(1); if (start == 0 && end == -1) { strcat(dest, source, maxlength); } else { if (end == -1) end = strlen(source); if (ispacked(dest)) { new len = strlen(dest); if (ispacked(source)) { strunpack(g_StrlibBuffer, source); strcat(dest, g_StrlibBuffer[start], min(maxlength, (len + end - start) / 4 + 1)); } else { strcat(dest, source[start], min(maxlength, (len + end - start) / 4 + 1)); } dest{len + end - start} = '\0'; } else { if (ispacked(source)) { strunpack(g_StrlibBuffer, source); strcat(dest, g_StrlibBuffer[start], min(maxlength, strlen(dest) + end - start + 1)); } else { strcat(dest, source[start], min(maxlength, strlen(dest) + end - start + 1)); } } } if (heap) RestoreHeapToAddress(heap); } stock utf8encode(dest[], const source[], maxlength = sizeof(dest)) { new heap = 0; if (IsOverlapping(dest, maxlength, source, -1)) { heap = CopyArgumentToHeap(1); } new len = strlen(source); new packed = ispacked(source); dest[0] = '\0'; new idx = 0; for (new i = 0; i < len; i++) { new c = packed ? source{i} : source[i]; if (c >= 0x80) { if (c > 0x4000000) { // 6 byte dest[idx++] = 0b11111100 | ((c >>> 30) & 0b00000001); dest[idx++] = 0b10000000 | ((c >>> 24) & 0b00111111); dest[idx++] = 0b10000000 | ((c >>> 18) & 0b00111111); dest[idx++] = 0b10000000 | ((c >>> 12) & 0b00111111); dest[idx++] = 0b10000000 | ((c >>> 6) & 0b00111111); dest[idx++] = 0b10000000 | (c & 0b00111111); } else if (c > 0x200000) { // 5 byte dest[idx++] = 0b11111000 | ((c >>> 24) & 0b00000011); dest[idx++] = 0b10000000 | ((c >>> 18) & 0b00111111); dest[idx++] = 0b10000000 | ((c >>> 12) & 0b00111111); dest[idx++] = 0b10000000 | ((c >>> 6) & 0b00111111); dest[idx++] = 0b10000000 | (c & 0b00111111); } else if (c > 0x10000) { // 4 byte dest[idx++] = 0b11110000 | ((c >>> 18) & 0b00000111); dest[idx++] = 0b10000000 | ((c >>> 12) & 0b00111111); dest[idx++] = 0b10000000 | ((c >>> 6) & 0b00111111); dest[idx++] = 0b10000000 | (c & 0b00111111); } else if (c > 0x800) { // 3 byte dest[idx++] = 0b11100000 | ((c >>> 12) & 0b00001111); dest[idx++] = 0b10000000 | ((c >>> 6) & 0b00111111); dest[idx++] = 0b10000000 | (c & 0b00111111); } else { // 2 byte dest[idx++] = 0b11000000 | ((c >>> 6) & 0b00011111); dest[idx++] = 0b10000000 | (c & 0b00111111); } } else if (c > 0) { dest[idx++] = c; } } dest[idx++] = '\0'; if (heap) { RestoreHeapToAddress(heap); } } stock utf8decode(dest[], const source[], maxlength = sizeof(dest)) { new heap = 0; if (IsOverlapping(dest, maxlength, source, -1)) { heap = CopyArgumentToHeap(1); } new len = strlen(source); dest[0] = '\0'; new idx = 0; for (new i = 0; i < len; i++) { new c = source[i]; if (c & 0b10000000) { if (c & 0b11100000 == 0b11000000) { // 2 byte if (i + 3 >= len) continue; dest[idx++] = (c & 0b00011111) << 6 | (source[++i] & 0b00111111); } else if (c & 0b11110000 == 0b11100000) { // 3 byte if (i + 4 >= len) continue; dest[idx++] = (c & 0b00001111) << 12 | (source[++i] & 0b00111111) << 6 | (source[++i] & 0b00111111); } else if (c & 0b11111000 == 0b11110000) { // 4 byte if (i + 5 >= len) continue; dest[idx++] = (c & 0b00000111) << 18 | (source[++i] & 0b00111111) << 12 | (source[++i] & 0b00111111) << 6 | (source[++i] & 0b00111111); } else if (c & 0b11111100 == 0b11111000) { // 5 byte if (i + 6 >= len) continue; dest[idx++] = (c & 0b00000011) << 24 | (source[++i] & 0b00111111) << 18 | (source[++i] & 0b00111111) << 12 | (source[++i] & 0b00111111) << 6 | (source[++i] & 0b00111111); } else if (c & 0b11111110 == 0b11111100) { // 6 byte if (i + 7 >= len) continue; dest[idx++] = (c & 0b00000001) << 30 | (source[++i] & 0b00111111) << 24 | (source[++i] & 0b00111111) << 18 | (source[++i] & 0b00111111) << 12 | (source[++i] & 0b00111111) << 6 | (source[++i] & 0b00111111); } } else { dest[idx++] = c; } } dest[idx++] = 0; if (heap) { RestoreHeapToAddress(heap); } } stock ret_strcatmid(const string[], const source[], start = 0, end = -1) { new output[STRLIB_RETURN_SIZE]; strcat(output, string); strcatmid(output, source, start, end); return output; } stock ret_strfrombin(const input[], inputlength = sizeof(input)) { new output[STRLIB_RETURN_SIZE]; strfrombin(output, input, inputlength); return output; } stock ret_strimplode(const glue[], ...) { new output[STRLIB_RETURN_SIZE]; const maxlength = sizeof(output); new args = numargs(); // Loop the variable arguments (the ones after "maxlength"). for (new arg = 1; arg < args; arg++) { // If this isn't the first string, append the glue. if (arg != 1) strcat(output, glue, maxlength); // Wrap these in braces or they will be a part of the above if statement (compiler bug) { // Get the address of argument no. <arg> #emit LCTRL 5 #emit ADD.C 12 #emit LOAD.S.alt arg #emit SHL.C.alt 2 #emit ADD #emit LOAD.I // Push the maxlength, arg address, and output address #emit PUSH.C maxlength #emit PUSH.pri #emit PUSH.ADR output // Push the argument count #emit PUSH.C 12 // call strcat #emit SYSREQ.C strcat // Restore the stack #emit STACK 16 } } // Fix compiler bug (returning strings in variadic functions) #emit LOAD.S.pri 8 #emit ADD.C 12 #emit MOVE.alt #emit LCTRL 5 #emit ADD #emit LOAD.I #emit STOR.S.pri 20 return output; } stock ret_strreplace(const string[], const search[], const replacement[], bool:ignorecase = false, pos = 0, limit = -1) { new output[STRLIB_RETURN_SIZE]; strcat(output, string); strreplace(output, search, replacement, ignorecase, pos, limit); return output; } stock ret_strfromliteral(const input[], &pos = 0) { new output[STRLIB_RETURN_SIZE]; strcat(output, input); strfromliteral(output, input, pos); return output; } stock ret_strtoliteral(const input[], bool:paranoid = true) { new output[STRLIB_RETURN_SIZE]; strcat(output, input); strtoliteral(output, input, paranoid); return output; } stock ret_strtrim(const string[], const chars[] = !"", string_edges:edge = edge_both) { new output[STRLIB_RETURN_SIZE]; strcat(output, string); strtrim(output, chars, edge); return output; } stock ret_strpad(const string[], length, const substr[] = !" ", string_edges:edge = edge_both, bool:trim_first = true, const trim_chars[] = !"") { new output[STRLIB_RETURN_SIZE]; strcat(output, string); strpad(output, length, substr, edge, trim_first, trim_chars); return output; } stock ret_strwrap(const left[], const string[], const right[]) { new output[STRLIB_RETURN_SIZE]; strcat(output, left); strcat(output, string); strcat(output, right); return output; } stock ret_strurldecode(const input[]) { new output[STRLIB_RETURN_SIZE]; strcat(output, input); strurldecode(output, input); return output; } stock ret_strurlencode(const input[], bool:pack = false) { new output[STRLIB_RETURN_SIZE]; strcat(output, input); strurlencode(output, input, _, pack); return output; } stock ret_utf8encode(const input[]) { new output[STRLIB_RETURN_SIZE]; utf8encode(output, input); return output; } stock ret_utf8decode(const input[]) { new output[STRLIB_RETURN_SIZE]; utf8decode(output, input); return output; } stock ret_strpack(const source[]) { new output[STRLIB_RETURN_SIZE]; strpack(output, source); return output; } stock ret_strunpack(const source[]) { new output[STRLIB_RETURN_SIZE]; strunpack(output, source); return output; } stock ret_strcat(const string1[], const string2[]) { new output[STRLIB_RETURN_SIZE]; strcat(output, string1); strcat(output, string2); return output; } stock ret_strmid(const source[], start, end) { new output[STRLIB_RETURN_SIZE]; strmid(output, source, start, end); return output; } stock ret_strins(const string[], const substr[], pos, maxlength = sizeof(string)) { new output[STRLIB_RETURN_SIZE]; strcat(output, string); strins(output, substr, pos); return output; } stock ret_strdel(const string[], start, end) { new output[STRLIB_RETURN_SIZE]; strcat(output, string); strdel(output, start, end); return output; } stock ret_valstr(value, bool:pack = false) { new output[STRLIB_RETURN_SIZE]; format(output, sizeof(output), "%d", value); if (pack) strpack(output, output); return output; } stock ret_GetPlayerName(playerid, bool:pack = false) { new output[MAX_PLAYER_NAME]; GetPlayerName(playerid, output, sizeof(output)); if (pack) strpack(output, output); return output; } stock sprintf(const fmat[], {Float, _}:...) { static output[STRLIB_RETURN_SIZE], frm_header[3], heap; const output_size = sizeof(output); if (ispacked(fmat)) { heap = CopyArgumentToHeap(0); } else { heap = 0; }{} // Store current frame header #emit LCTRL 5 #emit CONST.alt frm_header #emit MOVS 12 // Change the stack pointer to FRM + 12 #emit ADD.C 12 // pri is FRM (see above) #emit SCTRL 4 // Push sizeof(output) #emit PUSH.C output_size // Push output #emit PUSH.C output // Push the argument count #emit LOAD.S.pri 8 #emit ADD.C 8 #emit PUSH.pri #if !STRLIB_USE_FORMATEX const formatex = 0; // Dummy used to avoid "unknown symbol" error goto do_sysreq; #endif // Call formatex (unless this was skipped above) #emit LCTRL 6 #emit ADD.C 36 #emit PUSH.pri #emit CONST.pri formatex #emit SCTRL 6 #if !STRLIB_USE_FORMATEX do_sysreq: #endif // Call format (unless formatex was called, in which case this is skipped) #emit SYSREQ.C format // Restore the stack pointer to FRM #emit LCTRL 5 #emit SCTRL 4 // Copy back the frame header #emit MOVE.alt #emit CONST.pri frm_header #emit MOVS 12 // Restore heap if needed if (heap) { RestoreHeapToAddress(heap); }{} // IMPORTANT: Fix compiler bug (returning strings in variadic functions) #emit LOAD.S.pri 8 #emit ADD.C 12 #emit MOVE.alt #emit LCTRL 5 #emit ADD #emit LOAD.I #emit STOR.S.pri 20 // 16 + (static_args * 4) return output; // It is actually used, just not by its symbol name #pragma unused fmat }