#if defined _INC_y_utils #endinput #endif #define _INC_y_utils /** * *
* Description *
* Misc functions used throughout. *
* Version *
* 0.1.3 *
* Functions *
* Stock * * Inline * *
* Variables *
* Global * *
*//** *//* Legal: Version: MPL 1.1 The contents of this file are subject to the Mozilla Public License Version 1.1 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.mozilla.org/MPL/ Software distributed under the License is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License for the specific language governing rights and limitations under the License. The Original Code is the YSI framework. The Initial Developer of the Original Code is Alex "Y_Less" Cole. Portions created by the Initial Developer are Copyright (C) 2011 the Initial Developer. All Rights Reserved. Contributors: Y_Less koolk JoeBullet/Google63 g_aSlice/Slice Misiur samphunter tianmeta maddinat0r spacemud Crayder Dayvison Ahmad45123 Zeex irinel1996 Yiin- Chaprnks Konstantinos Masterchen09 Southclaws PatchwerkQWER m0k1 paulommu udan111 Thanks: JoeBullet/Google63 - Handy arbitrary ASM jump code using SCTRL. ZeeX - Very productive conversations. koolk - IsPlayerinAreaEx code. TheAlpha - Danish translation. breadfish - German translation. Fireburn - Dutch translation. yom - French translation. 50p - Polish translation. Zamaroht - Spanish translation. Los - Portuguese translation. Dracoblue, sintax, mabako, Xtreme, other coders - Producing other modes for me to strive to better. Pixels^ - Running XScripters where the idea was born. Matite - Pestering me to release it and using it. Very special thanks to: Thiadmer - PAWN, whose limits continue to amaze me! Kye/Kalcor - SA:MP. SA:MP Team past, present and future - SA:MP. Optional plugins: Gamer_Z - GPS. Incognito - Streamer. Me - sscanf2, fixes2, Whirlpool. */ #include "..\YSI_Internal\y_compilerdata" #include "..\YSI_Internal\y_version" #include "..\YSI_Core\y_debug" #include "..\YSI_Storage\y_amx" //#tryinclude #include "..\YSI_Internal\y_thirdpartyinclude" #include "..\YSI_Internal\y_funcinc" // Add new tags to the START of this list. #include "..\YSI_Internal\y_globaltags" #define YSI_MAX_STRING (144) #define FUNCTION_LENGTH (32) // Better handling of operator precedences and floating point numbers. This // will now work for ALL regular numbers (including -0.5 which broke the old // version). I don't know of any complex expressions that break it with // operator precedences, but I'm not ruling it out. The brackets do try and // account for that possibility, but I just don't know. #define NO_VALUE(%0) ((2*%0-1)==2*(%0-1)) #if !defined TRUE new stock bool:TRUE = true; #endif #if !defined FALSE new stock bool:FALSE = false; #endif #if !defined NULL new stock NULL[2] = "\1"; #endif #if !defined cellbytes #define cellbytes (cellbits / 8) #endif #define UNSIGNED(%0) ((%0) - cellmin) // Define "volatile" as nothing. #if !defined volatile #define volatile #endif #define YSIM_MASTER #M #define YSIM_RETURN #R #define YSIM_CALLER #C #define YSIM_TEXT_D #T #define YSIM_TXTFND #X #define YSIM_TXTIND #I #define YSIM_TXTLEN #E #define YSIM_LOG_IN #U #define YSIM_VARARG #V #if !defined YSIM_STRING #define YSIM_STRING (42) #endif #define FLOAT_INFINITY (Float:0x7F800000) #define FLOAT_NEG_INFINITY (Float:0xFF800000) #define FLOAT_NEGATIVE_INFINITY (Float:0xFF800000) #define FLOAT_NAN (Float:0x7FFFFFFF) #define FLOAT_NOT_A_NUMBER (Float:0x7FFFFFFF) #define FLOAT_QNAN (Float:0x7FFFFFFF) #define FLOAT_QUIET_NAN (Float:0x7FFFFFFF) #define FLOAT_QUIET_NOT_A_NUMBER (Float:0x7FFFFFFF) #define FLOAT_SNAN (Float:0x7FBFFFFF) #define FLOAT_SIGNALING_NAN (Float:0x7FBFFFFF) #define FLOAT_SIGNALING_NOT_A_NUMBER (Float:0x7FBFFFFF) //#pragma unused TRUE, FALSE, NULL #define __TY|||%0||| (1000000) #define __TX:__TY|||%0,%1||| (%1) #define __TF=fopen(%0,%2"%3",%4) __TF=fopen(%0".csv",%4) #if !defined TIMING_ITERATIONS #define TIMING_ITERATIONS (10) #endif /*-------------------------------------------------------------------------*//** * The time in ms. * The number of iterations completed in this time. * * Formats and returns a string representing the time taken for one iteration, * given the time required for many iterations. This attempts to format the * number using a reasonable fraction of a second. * *//*------------------------------------------------------------------------**/ stock __TU(t, iters) { // Number of times run. Accounts for the string and optional count. new ret[20]; if (iters > 1000000000) format(ret, sizeof (ret), "%.2fps", float(t) / (float(iters) / 1000000000.0)); else if (iters == 1000000000) format(ret, sizeof (ret), "%d.00ps", t); else if (iters > 1000000) format(ret, sizeof (ret), "%.2fns", float(t) / (float(iters) / 1000000.0)); else if (iters == 1000000) format(ret, sizeof (ret), "%d.00ns", t); else if (iters > 1000) format(ret, sizeof (ret), "%.2fus", float(t) / (float(iters) / 1000.0)); else if (iters == 1000) format(ret, sizeof (ret), "%d.00us", t); else if (iters > 1) format(ret, sizeof (ret), "%.2fms", float(t) / float(iters)); else format(ret, sizeof (ret), "%d.00ms", t); return ret; } #define RUN_TIMING(%0) \ for(new __TA[TIMING_ITERATIONS],__TC=sizeof __TA,__TE=_:__TX:__TY|||%0|||,__TS=printf("Timing \"%s\"...",%0);__TC; \ printf("\t Mean = %s\n\t Mode = %s\n\tMedian = %s\n\t Range = %s", \ __TU(Mean(__TA),__TE),__TU(Mode(__TA),__TE),__TU(Median(__TA),__TE),__TU(Range(__TA),__TE))) \ for(;(__TS=GetTickCount(),__TC);__TA[--__TC]=GetTickCount()-__TS) \ for(new __TI;__TI!=__TE;++__TI) #define CSV_TIMING(%0) \ for(new __TA[TIMING_ITERATIONS],__TC=sizeof __TA,__TE=_:__TX:__TY|||%0|||,__TS=printf("Timing \"%s\"...",%0),File:__TF=fopen(%0".csv",io_append);__TF&&__TC;\ va_fprintf(__TF,"%d,%d,%s,%s,%s,%s\n",gettime(),__TE,__TU(Mean(__TA),__TE),__TU(Mode(__TA),__TE),__TU(Median(__TA),__TE),__TU(Range(__TA),__TE)),fclose(__TF))\ for(;(__TS=GetTickCount(),__TC);__TA[--__TC]=GetTickCount()-__TS)\ for(new __TI;__TI!=__TE;++__TI) stock YSI_gPlayerIP[MAX_PLAYERS + 1] = {-1, ...}; public OnPlayerConnect(playerid) { new ip[16]; GetPlayerIp(playerid, ip, sizeof (ip)), YSI_gPlayerIP[playerid] = IPToInt(ip); #if defined _y_utils_OnPlayerConnect _y_utils_OnPlayerConnect(playerid); #endif return 1; } #if defined _ALS_OnPlayerConnect #undef OnPlayerConnect #else #define _ALS_OnPlayerConnect #endif #define OnPlayerConnect _y_utils_OnPlayerConnect #if defined _y_utils_OnPlayerConnect forward _y_utils_OnPlayerConnect(playerid); #endif public OnPlayerDisconnect(playerid, reason) { YSI_gPlayerIP[playerid] = -1; #if defined _y_utils_OnPlayerDisconnect _y_utils_OnPlayerDisconnect(playerid, reason); #endif return 1; } #if defined _ALS_OnPlayerDisconnect #undef OnPlayerDisconnect #else #define _ALS_OnPlayerDisconnect #endif #define OnPlayerDisconnect _y_utils_OnPlayerDisconnect #if defined _y_utils_OnPlayerDisconnect forward _y_utils_OnPlayerDisconnect(playerid, reason); #endif /*-------------------------------------------------------------------------*//** * The unsigned number to compare. * The upper limit. * * An unsigned comparison between the two values. * *//*------------------------------------------------------------------------**/ P:D(bool:UCMP(value, upper)); #define UCMP(%0,%1) IS_IN_RANGE(%0,0,(%1)) /*-------------------------------------------------------------------------*//** * The player to check. * * Is this a valid playerid (NOT, is the player connected). * *//*------------------------------------------------------------------------**/ P:D(bool:VALID_PLAYERID(playerid)); #define VALID_PLAYERID(%0) UCMP((%0), MAX_PLAYERS) /*-------------------------------------------------------------------------*//** * The number to compare. * The lower limit. * The upper limit. * * Is the value in the given range. * * * Equivalent to: * * (%1) <= (%0) < (%2) * * *//*------------------------------------------------------------------------**/ P:D(bool:IS_IN_RANGE(value, lower, upper)); #define IS_IN_RANGE(%0,%1,%2) (((%0)-((%1)+cellmin))<((%2)-((%1)+cellmin))) /*-------------------------------------------------------------------------*//** * The number to compare. * The lower limit. * The upper limit. * * Is the value outside the given range. * * * Equivalent to: * * (%1) <= (%0) < (%2) * * *//*------------------------------------------------------------------------**/ P:D(bool:NOT_IN_RANGE(value, lower, upper)); #define NOT_IN_RANGE(%0,%1,%2) (((%0)-((%1)+cellmin))>=((%2)-((%1)+cellmin))) /*-------------------------------------------------------------------------*//** * The top of the division. * The bottom of the division. * * (numerator / denominator) rounded up. * * * Normal integer division ALWAYS rounds down - this always rounds up. * *//*------------------------------------------------------------------------**/ P:D(ceildiv(numerator, denominator)); #define ceildiv(%0,%1) (((%0)-1)/(%1)+1) /*-------------------------------------------------------------------------*//** * * floordiv(numerator, denominator); * * The top of the division. * The bottom of the division. * * (numerator / denominator) rounded down. * * * Normal integer division ALWAYS rounds down - this also always rounds down, * making it a little pointless, but also more explicit in function. * *//*------------------------------------------------------------------------**/ P:D(floordiv(numerator, denominator)); #define floordiv(%0,%1) ((%0)/(%1)) /*-------------------------------------------------------------------------*//** * String to check if is null. *//*------------------------------------------------------------------------**/ P:D(bool:isnull(str[])); #define isnull(%0) ((%0[(%0[0])=='\1'])=='\0') /*-------------------------------------------------------------------------*//** * Value to check if is odd. *//*------------------------------------------------------------------------**/ P:D(bool:isodd(value)); #define isodd(%1) ((%1) & 1) /*-------------------------------------------------------------------------*//** * Value to check if is even. *//*------------------------------------------------------------------------**/ P:D(bool:iseven(value)); #define iseven(%1) (!isodd(%1)) /*-------------------------------------------------------------------------*//** * Destination string. * Source string. * (Implicit) maximum length of the destination. *//*------------------------------------------------------------------------**/ P:D(strcpy(dest[], const src[], len = sizeof (dest))); #define strcpy(%0,%1) strcat((%0[0] = '\0', %0), %1) /*-------------------------------------------------------------------------*//** * String to convert. * How much of the string to convert. *//*------------------------------------------------------------------------**/ stock StrToLower(str[], len = sizeof (str)) { new i = -1, ch; while ((ch = str[++i]) && len--) str[i] = tolower(ch); } /*-------------------------------------------------------------------------*//** * String to convert. * How much of the string to convert. *//*------------------------------------------------------------------------**/ stock StrToUpper(str[], len = sizeof (str)) { new i = -1, ch; while ((ch = str[++i]) && len--) str[i] = toupper(ch); } /*-------------------------------------------------------------------------*//** * Lower bound, or upper bound when only parameter. * Upper bound. * * Generate a random float between the given numbers (min <= n < max). * Default minimum is 0 (changes the parameter order). * *//*------------------------------------------------------------------------**/ stock Random(min, max = cellmin) { if (max == cellmin) { if (min < 0) return -random(-min); return random(min); } if (max < min) return random(min - max) + max; return random(max - min) + min; } /*-------------------------------------------------------------------------*//** * Lower bound, or upper bound when only parameter. * Upper bound. * How small to make the differences * * Generate a random float between the given numbers (min <= n < max). Default * minimum is 0.0 (changes the parameter order). * *//*------------------------------------------------------------------------**/ stock Float:RandomFloat(Float:min, Float:max = FLOAT_NAN, dp = 2) { new Float:mul = floatpower(10.0, float(dp)); switch (dp) { case 0: mul = 1.0; case 1: mul = 10.0; case 2: mul = 100.0; case 3: mul = 1000.0; default: mul = floatpower(10.0, float(dp)); } if (max != max) { if (min < 0.0) return -(float(random(floatround(-min * mul))) / mul); return float(random(floatround(min * mul))) / mul; } // Parameters are the wrong way around - do it anyway. if (max < min) return float(random(floatround(min * mul - max * mul))) / mul + max; // NOT a silly check - "IsNaN". return float(random(floatround(max * mul - min * mul))) / mul + min; } /*-------------------------------------------------------------------------*//** * The string to remove whitespace from the end of. * * Updated from old versions, should be more efficient * *//*------------------------------------------------------------------------**/ stock StripNL(str[]) { P:7("StripNL called: \"%s\"", str); new i = strlen(str); while (i-- && str[i] <= ' ') str[i] = '\0'; } #define StripR StripNL /*-------------------------------------------------------------------------*//** * The string to remove whitespace from the start of. *//*------------------------------------------------------------------------**/ stock StripL(str[]) { P:7("StripL called: \"%s\"", str); new len = strlen(str), i = 0; while ('\0' < str[i] <= ' ') ++i; if (i) memcpy(str[0], str[i], 0, (len - i) * 4, len); } /*-------------------------------------------------------------------------*//** * The string to remove whitespace from the start and end of. *//*------------------------------------------------------------------------**/ stock Strip(str[]) { P:7("Strip called: \"%s\"", str); new len = strlen(str), i = len; while (i-- && str[i] <= ' ') str[i] = '\0'; i = 0; while ('\0' < str[i] <= ' ') ++i; if (i) memcpy(str[0], str[i], 0, (len - i) * 4, len); } /*-------------------------------------------------------------------------*//** * String to check. * Postion to start from. * * Checks if the current point in a line is the end of non-whitespace data. * *//*------------------------------------------------------------------------**/ stock endofline(line[], pos) { P:7("endofline called: \"%s\", %i", line, pos); if (NOT_IN_RANGE(pos, 0, strlen(line))) return 0; //if (pos < 0 || pos > strlen(line)) return 0; while (line[pos]) if (line[pos++] > ' ') return 0; return 1; } /*-------------------------------------------------------------------------*//** * The character to find. * The string to find it in. * The offset to start from. * * Fail - -1, Success - pos * *//*------------------------------------------------------------------------**/ stock chrfind(needle, haystack[], start = 0) { P:7("chrfind called: %c, \"%s\", %i", needle, haystack, start); if (start < 0) { start = 0; } else if (start > strlen(haystack)) return -1; while (haystack[start]) if (haystack[start++] == needle) return start - 1; return -1; } /*-------------------------------------------------------------------------*//** * The character to find. * The string to find it in. * The offset to start from. * * Fail - -1, Success - pos * * * Like , but with no upper-bounds check on * . * *//*------------------------------------------------------------------------**/ stock chrfindp(needle, haystack[], start = 0) { P:7("chrfind called: %c, \"%s\", %i", needle, haystack, start); if (start < 0) { start = 0; } while (haystack{start}) if (haystack{start++} == needle) return start - 1; return -1; } /*-------------------------------------------------------------------------*//** * Dot notation IP to convert to an integer. *//*------------------------------------------------------------------------**/ stock IPToInt(ip[]) { new ipv = strval(ip) << 24, pos = 0; while (pos < 15 && ip[pos++] != '.') {} ipv += strval(ip[pos]) << 16; while (pos < 15 && ip[pos++] != '.') {} ipv += strval(ip[pos]) << 8; while (pos < 15 && ip[pos++] != '.') {} return ipv + strval(ip[pos]); } /*-------------------------------------------------------------------------*//** * the string to hash. * * the bernstein hash of the input string * * * This is a 32bit hash system so is not very secure, however we're only * using this as a string enumerator to uniquely identify strings easilly * and allow for a binary search of strings based on the hash of their name. * crc32, then jenkins were originally used however this is far faster, if a * little collision prone, but we're checking the strings manually anyway. * This doesn't matter as it would be done regardless of hash method, so this * doesn't need to be accounted for. Speed is all that matters with at * least a bit of non collision (the number of strings we're dealing with, * this should have none-few collisions). * * I modified it slightly from the original code pasted by aru, to code * closer to the code * and to work with PAWN (and shaved 0.2�s off the time for one call :D). * * Uber reduced version (just for fun): * b(s[]){new h=-1,i,j;while((j=s[i++]))h=h*33+j;return h;} * * Update: Contrary to what I said above this is also used to identify colour * strings for the updated text system involving file based styling and this * is not checked for collisions as it's unimportant. But this doesn't affect * the function at all, I just mentioned it here for "interest". * * Rewritten in self-generating assembly. * *//*------------------------------------------------------------------------**/ stock bernstein(string[] /* 12 */) { // Only shown the very first time this function is run. P:7("bernstein called: \"%s\"", string); #pragma unused string new base, ctx[AsmContext]; // Get this function. #emit CONST.pri bernstein #emit LOAD.alt AMX_HEADER_COD #emit ADD #emit STOR.S.pri base AsmInitPtr(ctx, base, 128), // Don't need any more than that. // Setup. @emit PROC @emit CONST.alt -1 @emit PUSH.S 12 // string @emit LREF.S.pri 12 // string @emit JZER.rel 12 * 4 // bernstein_end // bernstein_loop: @emit XCHG @emit SMUL.C 33 @emit ADD @emit MOVE.alt // Update the next pointer. @emit POP.pri @emit ADD.C 4 @emit PUSH.pri // Load the data for the current pointer. @emit LOAD.I @emit JNZ.rel -(12 * 4) // bernstein_loop // bernstein_end: @emit MOVE.pri @emit STACK 4 @emit RETN // Now actually CALL the written function. #emit LCTRL 5 #emit SCTRL 4 #emit CONST.pri bernstein #emit ADD.C 4 #emit SCTRL 6 return 0; // Make the compiler happy. } /*-------------------------------------------------------------------------*//** * String to check. * * true/false. * *//*------------------------------------------------------------------------**/ stock ishex(str[]) { P:7("ishex called: \"%s\"", str); new i, cur; if (str[0] == '0' && (str[1] | 0x20) == 'x') i = 2; do { cur = str[i++]; } while (IS_IN_RANGE(cur, '0', '9' + 1) || IS_IN_RANGE(cur | 0x20, 'a', 'f' + 1)); //while (('0' <= cur <= '9') || ('a' <= (cur | 0x20) <= 'f')); return !cur; // Valid if this is the end of the string. } /*-------------------------------------------------------------------------*//** * String to unpack * * unpacked string * * * Mainly used for debugging. * *//*------------------------------------------------------------------------**/ stock unpack(const str[]) { P:7("unpack called: \"%s\"", str); new ret[YSI_MAX_STRING] = {0}; if (strlen(str) <= YSI_MAX_STRING) { strunpack(ret, str); } return ret; } /*-------------------------------------------------------------------------*//** * Player to get IP of. * * IP as a 32bit int. * *//*------------------------------------------------------------------------**/ // Cunning macro only uses "%0" once, yet is still safe. P:D(GetIP(playerid)); #define GetIP(%0) (YSI_gPlayerIP[min((%0) + cellmin, MAX_PLAYERS + cellmin) - cellmin]) /*-------------------------------------------------------------------------*//** * Index of the string in the parameters. * * string * * * Is passed the result of getarg, which will be the address of a string (in * theory) and uses that for DMA to get the string. * *//*------------------------------------------------------------------------**/ #define getstring returnstringarg #define GetString getstring #define getstringarg va_getstring #define GetStringArg va_getstring #define ReturnStringArg returnstringarg stock returnstringarg(idx) { static scSize = YSI_MAX_STRING; // Get the address of the previous function's stack. First get the index of // the argument required. #emit LOAD.S.pri idx // Then convert that number to bytes from cells. #emit SMUL.C 4 // Get the previous function's frame. Stored in variable 0 (in the current // frame). Parameters are FRM+n+12, locals are FRM-n, previous frame is // FRM+0, return address is FRM+4, parameter count is FRM+8. We could add // checks that "idx * 4 < *(*(FRM + 0) + 8)", for the previous frame // parameter count (in C pointer speak). #emit LOAD.S.alt 0 // Add the frame pointer to the argument offset in bytes. #emit ADD // Add 12 to skip over the function header. #emit ADD.C 12 // Load the address stored in the specified address. #emit LOAD.I // Push the length for "strcat". #emit PUSH scSize // Push the address we just determined was the source. #emit PUSH.pri // Load the address of the secret destination. #emit LOAD.S.alt 16 // Blank the first cell so "strcat" behaves like "strcpy". #emit CONST.pri 0 // Store the loaded number 0 to the loaded address. #emit STOR.I // Push the loaded address. #emit PUSH.alt // Push the number of parameters passed (in bytes) to the function. #emit PUSH.C 12 // Call the function. #emit SYSREQ.C strcat // Restore the stack to its level before we called this native. #emit STACK 16 // Return without more string copying. #emit RETN // Fake return to define the API. new ret[YSI_MAX_STRING]; return ret; } /*-------------------------------------------------------------------------*//** * String format. * Parameters. * * Formatted string. * * * Just wraps `format` and returns a string instead. * * Has extra code to ensure that it works correct on the old compiler. * *//*------------------------------------------------------------------------**/ stock va_return(const fmat[], GLOBAL_TAG_TYPES:...) { #pragma unused fmat static sFrm, sRet, sCnt, scSize = YSI_MAX_STRING; // Store the function preamble. #emit POP.pri #emit STOR.pri sFrm #emit STACK 0 // Load the stack pointer for later. #emit POP.pri #emit STOR.pri sRet #emit POP.pri #emit ADD.C 8 #emit STOR.pri sCnt // Put the secret return parameter on the stack again to format in to. #emit PUSH scSize #emit ADD #emit LOAD.I #emit PUSH.pri // Call the native. #emit PUSH sCnt #emit SYSREQ.C format // Return directly. This will clean up our entire stack, even the extra // parameters we put on it. The parameter count is already there! #emit PUSH sRet #emit PUSH sFrm #emit RETN // Fake return to define the API. new ret[YSI_MAX_STRING]; return ret; } /*-------------------------------------------------------------------------*//** * String to check * * Checks if a given string is numeric. * *//*------------------------------------------------------------------------**/ stock isnumeric(str[]) { P:7("isnumeric called: \"%s\"", str); new i; while (IS_IN_RANGE(str[i], '0', '9' + 1)) ++i; //while ((ch = str[i++])) if (!('0' <= ch <= '9')) return 0; return !str[i]; } #if !defined _inc_sscanf || 1 /*---------------------------------------------------------------------*//** * * String to convert to a number. * * value of the passed hex string. * * * Now stops on invalid characters. * *//*--------------------------------------------------------------------**/ stock hexstr(string[]) { new ret, val, i; if (string[0] == '0' && string[1] | 0x20 == 'x') i = 2; for ( ; ; ) { switch ((val = string[i++])) { case '0' .. '9': { val -= '0'; } case 'a' .. 'f': { val -= 'a' - 10; } case 'A' .. 'F': { val -= 'A' - 10; } default: break; } ret = ret << 4 | val; } return ret; } /*---------------------------------------------------------------------*//** * * String to try convert to a boolean. * * bool: passed boolean. * * * This can take a number of ways of representing booleans - 0, false and * nothing there. Anything not one of those things (false is not case * sensitive) is assumed true. * *//*--------------------------------------------------------------------**/ stock bool:boolstr(string[]) { // Hooray for De Morgan's rules! return string[0] && string[0] != '0' && strcmp(string, "false", true); } /*---------------------------------------------------------------------*//** * * String to try convert to a boolean. * * bool: passed boolean. * * * This takes a value in 0110101 (boolean) format and returns it as a * regular value. * *//*--------------------------------------------------------------------**/ stock binstr(string[]) { new pos = 0; switch (string[0]) { case '0': { if (string[1] | 0x20 == 'b') { pos = 2; } } case '1': { } default: { return 0; } } new value = 0; for ( ; ; ) { switch (string[pos++]) { case '0': { value <<= 1; } case '1': { value = (value << 1) | 1; } default: { break; } } } return value; } #endif /*-------------------------------------------------------------------------*//** * * rawMemcpy * * Destination address. * Source data. * Number of bytes to copy. * * Like memcpy, but takes addresses instead of arrays. Also far less secure. * *//*------------------------------------------------------------------------**/ stock rawMemcpy(dest, src, bytes) { // Don't use "MOVS" as these blocks might overlap. #emit PUSH.S bytes #emit PUSH.S bytes #emit PUSH.C 0 #emit PUSH.S src #emit PUSH.S dest #emit PUSH.C 20 #emit SYSREQ.C memcpy #emit STACK 24 #emit RETN return 0; } /*-------------------------------------------------------------------------*//** * Array or address to set to a value. * What to set the cells to. * Number of cells to fill. * * Based on code by Slice: * * * * Modified to use binary flags instead of a loop. * * "memset" takes an array, the size of the array, and a value to fill it with * and sets the whole array to that value. * * "rawmemset" is similar, but takes an AMX data segment address instead and * the size is in bytes, not cells. However, the size must still be a multiple * of 4. * *//*------------------------------------------------------------------------**/ stock memset(arr[], val = 0, size = sizeof (arr)) { new addr; #emit LOAD.S.pri arr #emit STOR.S.pri addr // Convert the size from cells to bytes. return rawMemset(addr, val, size * 4); } /*-------------------------------------------------------------------------*//** * Array or address to set to a value. * What to set the cells to. * Number of cells to fill. * * Based on code by Slice: * * * * Modified to use binary flags instead of a loop. * * "memset" takes an array, the size of the array, and a value to fill it with * and sets the whole array to that value. * * "rawmemset" is similar, but takes an AMX data segment address instead and * the size is in bytes, not cells. However, the size must still be a multiple * of 4. * *//*------------------------------------------------------------------------**/ stock rawMemset(iAddress /* 12 */, iValue /* 16 */, iSize /* 20 */) { // They are really, trust me! #pragma unused iAddress, iSize, iValue // The first time this is called it rewrites itself. Any other times it is // called it just uses the new code. This is like doing: // // static // bInitialised = false; // if (!bInitialised) // { // // Do something // bInitialised = true; // } // // Do rest. // // But better (though FAR more complex). // There is NO checking here that we don't write the function bigger than // the space available, or even that we don't overwrite "CIP", which would // be bad. The only way to make sure that doesn't happen is write a little // with a lot of code! new base, ctx[AsmContext]; // Get this function. #emit CONST.pri rawMemset #emit LOAD.alt AMX_HEADER_COD #emit ADD #emit STOR.S.pri base AsmInitPtr(ctx, base, 80), // Don't need any more than that. // Frankly by this point we have probably already written more code than // will be generated! @emit PROC @emit LOAD.S.pri 20 @emit CONST.alt 0xFFFFFFC @emit AND @emit STOR.pri (ctx[AsmContext_buffer] + 13 * 4) // The documentation says "PRI" should be a pointer, but that's not true! @emit LOAD.S.alt 12 @emit LOAD.S.pri 16 @emit FILL 0 // Return the bytes filled. @emit LOAD.pri (ctx[AsmContext_buffer] + 13 * 4) @emit RETN // Do the second version. #emit CONST.pri memset #emit LOAD.alt AMX_HEADER_COD #emit ADD #emit STOR.S.pri base AsmInitPtr(ctx, base, 80), @emit PROC @emit LOAD.S.pri 20 @emit SHL.C.pri 2 @emit STOR.pri (ctx[AsmContext_buffer] + 12 * 4) @emit LOAD.S.alt 12 @emit LOAD.S.pri 16 @emit FILL 0 // Return the bytes filled. @emit LOAD.pri (ctx[AsmContext_buffer] + 12 * 4) @emit RETN // Call this function again (the new version), but don't let the compiler // know... First clear the stack. #emit LCTRL 5 #emit SCTRL 4 #emit CONST.pri rawMemset #emit ADD.C 4 #emit SCTRL 6 // Never hit because of going to an earlier "RETN". return 0; //memset("", 0, 0); } /*-------------------------------------------------------------------------*//** * * ReturnPlayerName * * Player whose name you want to get. * * Now uses a global array to avoid repeated function calls. Actually doesn't * because that causes issues with multiple scripts. * *//*------------------------------------------------------------------------**/ stock ReturnPlayerName(playerid) { new str[MAX_PLAYER_NAME]; GetPlayerName(playerid, str, sizeof (str)); return str; } /*-------------------------------------------------------------------------*//** * * ftouch(filename); * * The file to "touch". * * 0 - File already exists. * 1 - File was created. * -1 - File was not created. * * * This "touches" a file in the Unix sense of creating it but not opening or * editing it in any way. * *//*------------------------------------------------------------------------**/ stock ftouch(const filename[]) { if (fexist(filename)) { return 0; } else { new File:f = fopen(filename, io_write); if (f) { fclose(f); return 1; } else { return -1; } } } /*-------------------------------------------------------------------------*//** * * InterpolateColour(startcolor, endcolor, value, maxvalue, minvalue = 0); * * One of the two colours. * The other of the two colours. * The interpolation value between the endpoints. * One of the two numbers. * The other of the two numbers. * * This function takes two endpoint values (minvalue and maxvalue, with * minvalue defaulting to 0), along with a third value (value) whose distance * between the two endpoints is calculated (as a percentage). This percentage * value is then applied to the two colours given to find a third colour at * some point between those two colours. * * For example, if the endpoints given are "0" and "10", and the value given is * "3", then that is "30%" of the way between the two endpoints. We therefore * want to find a colour that is 30% of the way between the two given colours. * *//*------------------------------------------------------------------------**/ // "Interpolation" is the technical name for what you are doing here. #define InterpolateColor InterpolateColour stock InterpolateColour(startcolor, endcolor, value, maxvalue, minvalue = 0) { if (value >= maxvalue) return endcolor; if (value <= minvalue) return startcolor; static r, g, b, a; new time = maxvalue - minvalue, stage = value - minvalue; return // Step 1: Get the starting colour components. r = startcolor >>> 24 , g = startcolor >>> 16 & 0xFF, b = startcolor >>> 8 & 0xFF, a = startcolor & 0xFF, // Step 2: Interpolate between the end points, and add to the start. r += ((endcolor >>> 24 ) - r) * stage / time, g += ((endcolor >>> 16 & 0xFF) - g) * stage / time, b += ((endcolor >>> 8 & 0xFF) - b) * stage / time, a += ((endcolor & 0xFF) - a) * stage / time, // Step 3: Combine the individual components. (r << 24) | ((g & 0xFF) << 16) | ((b & 0xFF) << 8) | (a & 0xFF); } /*-------------------------------------------------------------------------*//** * The string to skip over part of. * The start of the whitespace. * * The end of the whitespace. * * * Doesn't skip over NULL terminators. * *//*------------------------------------------------------------------------**/ stock SkipWhitespace(const str[], pos) { while (IS_IN_RANGE(str[pos], '\0' + 1, ' ' + 1)) ++pos; //while ('\0' < str[pos] <= ' ') ++pos; return pos; } /*-------------------------------------------------------------------------*//** * The string to trim. * Start of the substring. * End of the substring. * * Modifies "start" and "end" to be tight on text in "str". * *//*------------------------------------------------------------------------**/ stock Trim(const str[], &start, &end) { while (IS_IN_RANGE(str[start], '\0' + 1, ' ' + 1)) ++start; //while ('\0' < str[start] <= ' ') ++start; if (str[start]) { while (end-- > start && str[end] <= ' ') {} ++end; } else { end = start; } } /*-------------------------------------------------------------------------*//** * The array to sort. * The size of the array. * * Sorts the array in place. Uses bubble sort because it is easy and fast for * pre-sorted arrays (which the callers are likely to be). * *//*------------------------------------------------------------------------**/ //#define ftell(%0) fseek((%0), 0, seek_current) static stock Utils_PreSort(arr[], num = sizeof (arr)) { // Very simple bubble sort (fast for pre-sorted arrays). new bool:sort; do { sort = false; for (new j = 1, temp; j != num; ++j) { if ((temp = arr[j]) < arr[j - 1]) { arr[j] = arr[j - 1], arr[j - 1] = temp, sort = true; } } } while (sort); } /*-------------------------------------------------------------------------*//** * The array whose values need summing. * The size of the array. * * All the values in the array added together. * *//*------------------------------------------------------------------------**/ stock Sum(const arr[], num = sizeof (arr)) { new tot; while (num) tot += arr[--num]; return tot; } /*-------------------------------------------------------------------------*//** * The array whose values need averaging. * The size of the array. * * The mathematical mean value of the array. * *//*------------------------------------------------------------------------**/ stock Mean(const arr[], num = sizeof (arr)) { return Sum(arr, num) / num; } /*-------------------------------------------------------------------------*//** * The array whose values need averaging. * The size of the array. * * The mathematical modal value of the array. * *//*------------------------------------------------------------------------**/ stock Mode(arr[], num = sizeof (arr)) { Utils_PreSort(arr, num); new ret, count = 0, cn, cc; for (new i = 0; i != num; ++i) { if (arr[i] == cn) ++cc; else { if (cc > count) count = cc, ret = cn; cc = 1, cn = arr[i]; } } if (cc > count) return cn; else return ret; } /*-------------------------------------------------------------------------*//** * The array whose values need averaging. * The size of the array. * * The mathematical median value of the array. * *//*------------------------------------------------------------------------**/ stock Median(arr[], num = sizeof (arr)) { Utils_PreSort(arr, num); new idx = num >>> 1; if (num & 1) return arr[idx]; else return (arr[idx] + arr[idx - 1]) / 2; } /*-------------------------------------------------------------------------*//** * The array whose values need averaging. * The size of the array. * * The mathematical range of the values of the array. * *//*------------------------------------------------------------------------**/ stock Range(arr[], num = sizeof (arr)) { new min = cellmax, max = cellmin, cur; while (num) { cur = arr[--num]; if (cur < min) min = cur; if (cur > max) max = cur; } return max - min; } #include "..\YSI_Coding\y_va" #include "..\YSI_Internal\y_shortfunc" #if defined YSI_TESTS #include "y_testing" #include "y_utils/tests" #endif