| 12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346134713481349135013511352135313541355135613571358135913601361136213631364136513661367136813691370137113721373137413751376137713781379138013811382138313841385138613871388138913901391139213931394139513961397139813991400140114021403140414051406140714081409141014111412141314141415141614171418141914201421142214231424142514261427142814291430143114321433143414351436143714381439144014411442144314441445144614471448144914501451145214531454145514561457145814591460146114621463146414651466146714681469147014711472147314741475147614771478147914801481148214831484 |
- #if defined _INC_y_utils
- #endinput
- #endif
- #define _INC_y_utils
- /**
- * <library name="y_utils">
- * <section>
- * Description
- * </section>
- * Misc functions used throughout.
- * <section>
- * Version
- * </section>
- * 0.1.3
- * <section>
- * Functions
- * </section>
- * <subsection>Stock</subsection>
- * <ul>
- * <symbol name="StrToLower">Convert a whole string to lower-case.</symbol>
- * <symbol name="StrToUpper">Convert a whole string to upper-case.</symbol>
- * <symbol name="Random">Generate a random number, optionally takes lower and upper bounds.</symbol>
- * <symbol name="RandomFloat">Same as <symbolref name="Random" />, but for floats.</symbol>
- * <symbol name="StripNL">Strips the newline characters from the end of a string.</symbol>
- * <symbol name="StripL">Remove whitespace from the start of a string.</symbol>
- * <symbol name="Strip">Remove whitespace from both ends of a string.</symbol>
- * <symbol name="endofline">Check if the given position is the end of a string (ignoring whitespace).</symbol>
- * <symbol name="chrfind">Return the first position (after <symbolref name="start" />) of the given character.</symbol>
- * <symbol name="chrfindp">Like <symbolref name="chrfind" />, but without the upper-bounds check.</symbol>
- * <symbol name="bernstein">Generate the Bernstein hash of the given string.</symbol>
- * <symbol name="ishex">Is the given string hexadecimal?</symbol>
- * <symbol name="unpack">Version of <symbolref name="strunpack" /> that returns the result.</symbol>
- * <symbol name="returnstringarg">Get the string passed as a variable argument from the given index.</symbol>
- * <symbol name="va_return">Like <symbolref name="sprintf" />, formats a string and returns the result.</symbol>
- * <symbol name="isnumeric">Is the given string a number?</symbol>
- * <symbol name="hexstr">Return the value of the given hexadecimal string.</symbol>
- * <symbol name="boolstr">Return the value of the given boolean string.</symbol>
- * <symbol name="binstr">Return the value of the given binary string.</symbol>
- * <symbol name="rawMemcpy">Copy memory between two address, instead of two arrays.</symbol>
- * <symbol name="memset">Set all of an array to a value.</symbol>
- * <symbol name="rawMemset">Set all of a given memory region to a value.</symbol>
- * <symbol name="ReturnPlayerName">Return a player's name.</symbol>
- * <symbol name="ftouch">Ensures that a file exists, but nothing more.</symbol>
- * <symbol name="InterpolateColour">Get the colour (in 3D RGB space) between two other colours.</symbol>
- * <symbol name="SkipWhitespace">Return the first position in a string of a non-whitespace character.</symbol>
- * <symbol name="Trim">Get the first and last positions of non-whitespace characters in the string. Like <symbolref name="Strip" />, but doesn't modify the string.</symbol>
- * <symbol name="Sum">Get the total (sum) of an array.</symbol>
- * <symbol name="Mean">Get the mathematical mean of an array.</symbol>
- * <symbol name="Mode">Get the mathematical mode of an array.</symbol>
- * <symbol name="Median">Get the mathematical median of an array.</symbol>
- * <symbol name="Range">Get the mathematical range of an array.</symbol>
- * </ul>
- * <subsection>Inline</subsection>
- * <ul>
- * <symbol name="UCMP">Unsigned compare.</symbol>
- * <symbol name="VALID_PLAYERID">Check if a player ID is valid (in range).</symbol>
- * <symbol name="IS_IN_RANGE">Check if a number is in range.</symbol>
- * <symbol name="NOT_IN_RANGE">Check if a number is outside a range.</symbol>
- * <symbol name="ceildiv">Divide two numbers and round up.</symbol>
- * <symbol name="floordiv">Divide two numbers and round down.</symbol>
- * <symbol name="isnull">Checks if a string is NULL (<c>\1\0</c> or <c>\0</c>).</symbol>
- * <symbol name="isodd">Checks if a number is odd.</symbol>
- * <symbol name="iseven">Checks if a number is even.</symbol>
- * <symbol name="strcpy">Copy one string to another.</symbol>
- * <symbol name="GetIP">Return the encoded (32-bit) version of a player's IP.</symbol>
- * <synonym name="getstring" for="returnstringarg"> (there are a lot)</synonym>
- * <synonym name="GetString" for="returnstringarg"> (there are a lot)</synonym>
- * <synonym name="getstringarg" for="returnstringarg"> (there are a lot)</synonym>
- * <synonym name="GetStringArg" for="returnstringarg"> (there are a lot)</synonym>
- * <synonym name="ReturnStringArg" for="returnstringarg"> (there are a lot)</synonym>
- * <synonym name="InterpolateColor" for="InterpolateColour" />
- * <synonym name="StripR" for="StripNL" />
- * </ul>
- * <section>
- * Variables
- * </section>
- * <subsection>Global</subsection>
- * <ul>
- * <symbol name="TRUE">True hack for infinate loops.</symbol>
- * <symbol name="FALSE">False hack for one-time loops.</symbol>
- * <symbol name="NULL">1 long string for passing via Call(Remote|Local)Function.</symbol>
- * </ul>
- * </library>
- *//** *//*
- 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 <sscanf>
- #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
- /*-------------------------------------------------------------------------*//**
- * <param name="t">The time in ms.</param>
- * <param name="iters">The number of iterations completed in this time.</param>
- * <remarks>
- * 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.
- * </remarks>
- *//*------------------------------------------------------------------------**/
- 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
- /*-------------------------------------------------------------------------*//**
- * <param name="value">The unsigned number to compare.</param>
- * <param name="upper">The upper limit.</param>
- * <returns>
- * An unsigned comparison between the two values.
- * </returns>
- *//*------------------------------------------------------------------------**/
- P:D(bool:UCMP(value, upper));
- #define UCMP(%0,%1) IS_IN_RANGE(%0,0,(%1))
- /*-------------------------------------------------------------------------*//**
- * <param name="playerid">The player to check.</param>
- * <returns>
- * Is this a valid playerid (NOT, is the player connected).
- * </returns>
- *//*------------------------------------------------------------------------**/
- P:D(bool:VALID_PLAYERID(playerid));
- #define VALID_PLAYERID(%0) UCMP((%0), MAX_PLAYERS)
- /*-------------------------------------------------------------------------*//**
- * <param name="value">The number to compare.</param>
- * <param name="lower">The lower limit.</param>
- * <param name="upper">The upper limit.</param>
- * <returns>
- * Is the value in the given range.
- * </returns>
- * <remarks>
- * Equivalent to:
- *
- * <code> (%1) <= (%0) < (%2)</code>
- *
- * </remarks>
- *//*------------------------------------------------------------------------**/
- P:D(bool:IS_IN_RANGE(value, lower, upper));
- #define IS_IN_RANGE(%0,%1,%2) (((%0)-((%1)+cellmin))<((%2)-((%1)+cellmin)))
- /*-------------------------------------------------------------------------*//**
- * <param name="value">The number to compare.</param>
- * <param name="lower">The lower limit.</param>
- * <param name="upper">The upper limit.</param>
- * <returns>
- * Is the value outside the given range.
- * </returns>
- * <remarks>
- * Equivalent to:
- *
- * <code> (%1) <= (%0) < (%2)</code>
- *
- * </remarks>
- *//*------------------------------------------------------------------------**/
- P:D(bool:NOT_IN_RANGE(value, lower, upper));
- #define NOT_IN_RANGE(%0,%1,%2) (((%0)-((%1)+cellmin))>=((%2)-((%1)+cellmin)))
- /*-------------------------------------------------------------------------*//**
- * <param name="numerator">The top of the division.</param>
- * <param name="denominator">The bottom of the division.</param>
- * <returns>
- * (numerator / denominator) rounded up.
- * </returns>
- * <remarks>
- * Normal integer division ALWAYS rounds down - this always rounds up.
- * </remarks>
- *//*------------------------------------------------------------------------**/
- P:D(ceildiv(numerator, denominator));
- #define ceildiv(%0,%1) (((%0)-1)/(%1)+1)
- /*-------------------------------------------------------------------------*//**
- * <summary>
- * floordiv(numerator, denominator);
- * </summary>
- * <param name="numerator">The top of the division.</param>
- * <param name="denominator">The bottom of the division.</param>
- * <returns>
- * (numerator / denominator) rounded down.
- * </returns>
- * <remarks>
- * Normal integer division ALWAYS rounds down - this also always rounds down,
- * making it a little pointless, but also more explicit in function.
- * </remarks>
- *//*------------------------------------------------------------------------**/
- P:D(floordiv(numerator, denominator));
- #define floordiv(%0,%1) ((%0)/(%1))
- /*-------------------------------------------------------------------------*//**
- * <param name="str">String to check if is null.</param>
- *//*------------------------------------------------------------------------**/
- P:D(bool:isnull(str[]));
- #define isnull(%0) ((%0[(%0[0])=='\1'])=='\0')
- /*-------------------------------------------------------------------------*//**
- * <param name="value">Value to check if is odd.</param>
- *//*------------------------------------------------------------------------**/
- P:D(bool:isodd(value));
- #define isodd(%1) ((%1) & 1)
- /*-------------------------------------------------------------------------*//**
- * <param name="value">Value to check if is even.</param>
- *//*------------------------------------------------------------------------**/
- P:D(bool:iseven(value));
- #define iseven(%1) (!isodd(%1))
- /*-------------------------------------------------------------------------*//**
- * <param name="dest">Destination string.</param>
- * <param name="src">Source string.</param>
- * <param name="len">(Implicit) maximum length of the destination.</param>
- *//*------------------------------------------------------------------------**/
- P:D(strcpy(dest[], const src[], len = sizeof (dest)));
- #define strcpy(%0,%1) strcat((%0[0] = '\0', %0), %1)
- /*-------------------------------------------------------------------------*//**
- * <param name="str">String to convert.</param>
- * <param name="len">How much of the string to convert.</param>
- *//*------------------------------------------------------------------------**/
- stock StrToLower(str[], len = sizeof (str))
- {
- new
- i = -1,
- ch;
- while ((ch = str[++i]) && len--)
- str[i] = tolower(ch);
- }
- /*-------------------------------------------------------------------------*//**
- * <param name="str">String to convert.</param>
- * <param name="len">How much of the string to convert.</param>
- *//*------------------------------------------------------------------------**/
- stock StrToUpper(str[], len = sizeof (str))
- {
- new
- i = -1,
- ch;
- while ((ch = str[++i]) && len--)
- str[i] = toupper(ch);
- }
- /*-------------------------------------------------------------------------*//**
- * <param name="minmax">Lower bound, or upper bound when only parameter.</param>
- * <param name="max">Upper bound.</param>
- * <remarks>
- * Generate a random float between the given numbers (min <= n < max).
- * Default minimum is 0 (changes the parameter order).
- * </remarks>
- *//*------------------------------------------------------------------------**/
- 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;
- }
- /*-------------------------------------------------------------------------*//**
- * <param name="minmax">Lower bound, or upper bound when only parameter.</param>
- * <param name="max">Upper bound.</param>
- * <param name="dp">How small to make the differences</param>
- * <remarks>
- * Generate a random float between the given numbers (min <= n < max). Default
- * minimum is 0.0 (changes the parameter order).
- * </remarks>
- *//*------------------------------------------------------------------------**/
- 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;
- }
- /*-------------------------------------------------------------------------*//**
- * <param name="str">The string to remove whitespace from the end of.</param>
- * <remarks>
- * Updated from old versions, should be more efficient
- * </remarks>
- *//*------------------------------------------------------------------------**/
- stock StripNL(str[])
- {
- P:7("StripNL called: \"%s\"", str);
- new
- i = strlen(str);
- while (i-- && str[i] <= ' ') str[i] = '\0';
- }
- #define StripR StripNL
- /*-------------------------------------------------------------------------*//**
- * <param name="str">The string to remove whitespace from the start of.</param>
- *//*------------------------------------------------------------------------**/
- 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);
- }
- /*-------------------------------------------------------------------------*//**
- * <param name="str">The string to remove whitespace from the start and end of.</param>
- *//*------------------------------------------------------------------------**/
- 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);
- }
- /*-------------------------------------------------------------------------*//**
- * <param name="line">String to check.</param>
- * <param name="pos">Postion to start from.</param>
- * <remarks>
- * Checks if the current point in a line is the end of non-whitespace data.
- * </remarks>
- *//*------------------------------------------------------------------------**/
- 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;
- }
- /*-------------------------------------------------------------------------*//**
- * <param name="needle">The character to find.</param>
- * <param name="haystack">The string to find it in.</param>
- * <param name="start">The offset to start from.</param>
- * <returns>
- * Fail - -1, Success - pos
- * </returns>
- *//*------------------------------------------------------------------------**/
- 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;
- }
- /*-------------------------------------------------------------------------*//**
- * <param name="needle">The character to find.</param>
- * <param name="haystack">The string to find it in.</param>
- * <param name="start">The offset to start from.</param>
- * <returns>
- * Fail - -1, Success - pos
- * </returns>
- * <remarks>
- * Like <symbolref name="chrfind" />, but with no upper-bounds check on
- * <paramref name="start" />.
- * </remarks>
- *//*------------------------------------------------------------------------**/
- 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;
- }
- /*-------------------------------------------------------------------------*//**
- * <param name="ip">Dot notation IP to convert to an integer.</param>
- *//*------------------------------------------------------------------------**/
- 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]);
- }
- /*-------------------------------------------------------------------------*//**
- * <param name="string">the string to hash.</param>
- * <returns>
- * the bernstein hash of the input string
- * </returns>
- * <remarks>
- * 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 <a href="http://www.burtleburtle.net/bob/hash/doobs.html" />
- * 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.
- * </remarks>
- *//*------------------------------------------------------------------------**/
- 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.
- }
- /*-------------------------------------------------------------------------*//**
- * <param name="str">String to check.</param>
- * <returns>
- * true/false.
- * </returns>
- *//*------------------------------------------------------------------------**/
- 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.
- }
- /*-------------------------------------------------------------------------*//**
- * <param name="str">String to unpack</param>
- * <returns>
- * unpacked string
- * </returns>
- * <remarks>
- * Mainly used for debugging.
- * </remarks>
- *//*------------------------------------------------------------------------**/
- 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;
- }
- /*-------------------------------------------------------------------------*//**
- * <param name="playerid">Player to get IP of.</param>
- * <returns>
- * IP as a 32bit int.
- * </returns>
- *//*------------------------------------------------------------------------**/
- // 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])
- /*-------------------------------------------------------------------------*//**
- * <param name="idx">Index of the string in the parameters.</param>
- * <returns>
- * string
- * </returns>
- * <remarks>
- * 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.
- * </remarks>
- *//*------------------------------------------------------------------------**/
- #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;
- }
- /*-------------------------------------------------------------------------*//**
- * <param name="fmat">String format.</param>
- * <param name="">Parameters.</param>
- * <returns>
- * Formatted string.
- * </returns>
- * <remarks>
- * Just wraps `format` and returns a string instead.
- *
- * Has extra code to ensure that it works correct on the old compiler.
- * </remarks>
- *//*------------------------------------------------------------------------**/
- 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;
- }
- /*-------------------------------------------------------------------------*//**
- * <param name="str">String to check</param>
- * <remarks>
- * Checks if a given string is numeric.
- * </remarks>
- *//*------------------------------------------------------------------------**/
- 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
- /*---------------------------------------------------------------------*//**
- *
- * <param name="string">String to convert to a number.</param>
- * <returns>
- * value of the passed hex string.
- * </returns>
- * <remarks>
- * Now stops on invalid characters.
- * </remarks>
- *//*--------------------------------------------------------------------**/
-
- 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;
- }
-
- /*---------------------------------------------------------------------*//**
- *
- * <param name="string">String to try convert to a boolean.</param>
- * <returns>
- * bool: passed boolean.
- * </returns>
- * <remarks>
- * 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.
- * </remarks>
- *//*--------------------------------------------------------------------**/
-
- stock bool:boolstr(string[])
- {
- // Hooray for De Morgan's rules!
- return string[0] && string[0] != '0' && strcmp(string, "false", true);
- }
-
- /*---------------------------------------------------------------------*//**
- *
- * <param name="string">String to try convert to a boolean.</param>
- * <returns>
- * bool: passed boolean.
- * </returns>
- * <remarks>
- * This takes a value in 0110101 (boolean) format and returns it as a
- * regular value.
- * </remarks>
- *//*--------------------------------------------------------------------**/
-
- 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
- /*-------------------------------------------------------------------------*//**
- * <summary>
- * rawMemcpy
- * </summary>
- * <param name="dest">Destination address.</param>
- * <param name="src">Source data.</param>
- * <param name="bytes">Number of bytes to copy.</param>
- * <remarks>
- * Like memcpy, but takes addresses instead of arrays. Also far less secure.
- * </remarks>
- *//*------------------------------------------------------------------------**/
- 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;
- }
- /*-------------------------------------------------------------------------*//**
- * <param name="arr">Array or address to set to a value.</param>
- * <param name="iValue">What to set the cells to.</param>
- * <param name="iSize">Number of cells to fill.</param>
- * <remarks>
- * Based on code by Slice:
- *
- * <a href="http://forum.sa-mp.com/showthread.php?p=1606781#post1606781" />
- *
- * 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.
- * </remarks>
- *//*------------------------------------------------------------------------**/
- 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);
- }
- /*-------------------------------------------------------------------------*//**
- * <param name="iAddress">Array or address to set to a value.</param>
- * <param name="iValue">What to set the cells to.</param>
- * <param name="iSize">Number of cells to fill.</param>
- * <remarks>
- * Based on code by Slice:
- *
- * <a href="http://forum.sa-mp.com/showthread.php?p=1606781#post1606781" />
- *
- * 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.
- * </remarks>
- *//*------------------------------------------------------------------------**/
- 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);
- }
- /*-------------------------------------------------------------------------*//**
- * <summary>
- * ReturnPlayerName
- * </summary>
- * <param name="playerid">Player whose name you want to get.</param>
- * <remarks>
- * Now uses a global array to avoid repeated function calls. Actually doesn't
- * because that causes issues with multiple scripts.
- * </remarks>
- *//*------------------------------------------------------------------------**/
- stock ReturnPlayerName(playerid)
- {
- new
- str[MAX_PLAYER_NAME];
- GetPlayerName(playerid, str, sizeof (str));
- return str;
- }
- /*-------------------------------------------------------------------------*//**
- * <summary>
- * ftouch(filename);
- * </summary>
- * <param name="filename">The file to "touch".</param>
- * <returns>
- * 0 - File already exists.
- * 1 - File was created.
- * -1 - File was not created.
- * </returns>
- * <remarks>
- * This "touches" a file in the Unix sense of creating it but not opening or
- * editing it in any way.
- * </remarks>
- *//*------------------------------------------------------------------------**/
- 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;
- }
- }
- }
- /*-------------------------------------------------------------------------*//**
- * <summary>
- * InterpolateColour(startcolor, endcolor, value, maxvalue, minvalue = 0);
- * </summary>
- * <param name="startcolor">One of the two colours.</param>
- * <param name="endcolor">The other of the two colours.</param>
- * <param name="value">The interpolation value between the endpoints.</param>
- * <param name="maxvalue">One of the two numbers.</param>
- * <param name="minvalue">The other of the two numbers.</param>
- * <remarks>
- * 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.
- * </remarks>
- *//*------------------------------------------------------------------------**/
- // "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);
- }
- /*-------------------------------------------------------------------------*//**
- * <param name="str">The string to skip over part of.</param>
- * <param name="pos">The start of the whitespace.</param>
- * <returns>
- * The end of the whitespace.
- * </returns>
- * <remarks>
- * Doesn't skip over NULL terminators.
- * </remarks>
- *//*------------------------------------------------------------------------**/
- stock SkipWhitespace(const str[], pos)
- {
- while (IS_IN_RANGE(str[pos], '\0' + 1, ' ' + 1)) ++pos;
- //while ('\0' < str[pos] <= ' ') ++pos;
- return pos;
- }
- /*-------------------------------------------------------------------------*//**
- * <param name="str">The string to trim.</param>
- * <param name="start">Start of the substring.</param>
- * <param name="end">End of the substring.</param>
- * <remarks>
- * Modifies "start" and "end" to be tight on text in "str".
- * </remarks>
- *//*------------------------------------------------------------------------**/
- 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;
- }
- }
- /*-------------------------------------------------------------------------*//**
- * <param name="arr">The array to sort.</param>
- * <param name="num">The size of the array.</param>
- * <remarks>
- * 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).
- * </remarks>
- *//*------------------------------------------------------------------------**/
- //#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);
- }
- /*-------------------------------------------------------------------------*//**
- * <param name="arr">The array whose values need summing.</param>
- * <param name="num">The size of the array.</param>
- * <returns>
- * All the values in the array added together.
- * </returns>
- *//*------------------------------------------------------------------------**/
- stock Sum(const arr[], num = sizeof (arr))
- {
- new
- tot;
- while (num) tot += arr[--num];
- return tot;
- }
- /*-------------------------------------------------------------------------*//**
- * <param name="arr">The array whose values need averaging.</param>
- * <param name="num">The size of the array.</param>
- * <returns>
- * The mathematical mean value of the array.
- * </returns>
- *//*------------------------------------------------------------------------**/
- stock Mean(const arr[], num = sizeof (arr))
- {
- return Sum(arr, num) / num;
- }
- /*-------------------------------------------------------------------------*//**
- * <param name="arr">The array whose values need averaging.</param>
- * <param name="num">The size of the array.</param>
- * <returns>
- * The mathematical modal value of the array.
- * </returns>
- *//*------------------------------------------------------------------------**/
- 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;
- }
- /*-------------------------------------------------------------------------*//**
- * <param name="arr">The array whose values need averaging.</param>
- * <param name="num">The size of the array.</param>
- * <returns>
- * The mathematical median value of the array.
- * </returns>
- *//*------------------------------------------------------------------------**/
- 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;
- }
- /*-------------------------------------------------------------------------*//**
- * <param name="arr">The array whose values need averaging.</param>
- * <param name="num">The size of the array.</param>
- * <returns>
- * The mathematical range of the values of the array.
- * </returns>
- *//*------------------------------------------------------------------------**/
- 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
|