y_utils.inc 31 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182
  1. /**--------------------------------------------------------------------------**\
  2. =================================
  3. Y Sever Includes - Misc Functions
  4. =================================
  5. Description:
  6. Misc functions used throughout.
  7. Legal:
  8. Version: MPL 1.1
  9. The contents of this file are subject to the Mozilla Public License Version
  10. 1.1 (the "License"); you may not use this file except in compliance with
  11. the License. You may obtain a copy of the License at
  12. http://www.mozilla.org/MPL/
  13. Software distributed under the License is distributed on an "AS IS" basis,
  14. WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
  15. for the specific language governing rights and limitations under the
  16. License.
  17. The Original Code is the YSI utils include.
  18. The Initial Developer of the Original Code is Alex "Y_Less" Cole.
  19. Portions created by the Initial Developer are Copyright (C) 2011
  20. the Initial Developer. All Rights Reserved.
  21. Contributors:
  22. ZeeX, koolk, JoeBullet/Google63, g_aSlice/Slice
  23. Thanks:
  24. JoeBullet/Google63 - Handy arbitrary ASM jump code using SCTRL.
  25. ZeeX - Very productive conversations.
  26. koolk - IsPlayerinAreaEx code.
  27. TheAlpha - Danish translation.
  28. breadfish - German translation.
  29. Fireburn - Dutch translation.
  30. yom - French translation.
  31. 50p - Polish translation.
  32. Zamaroht - Spanish translation.
  33. Los - Portuguese translation.
  34. Dracoblue, sintax, mabako, Xtreme, other coders - Producing other modes
  35. for me to strive to better.
  36. Pixels^ - Running XScripters where the idea was born.
  37. Matite - Pestering me to release it and using it.
  38. Very special thanks to:
  39. Thiadmer - PAWN, whose limits continue to amaze me!
  40. Kye/Kalcor - SA:MP.
  41. SA:MP Team past, present and future - SA:MP.
  42. Optional plugins:
  43. Gamer_Z - GPS.
  44. Incognito - Streamer.
  45. Me - sscanf2, fixes2, Whirlpool.
  46. Version:
  47. 0.1.3
  48. Changelog:
  49. 06/10/12:
  50. Upgraded "memset" to use "FILL".
  51. 22/12/11:
  52. Changed "ceildiv" to only evaluate arguments once.
  53. 05/12/11:
  54. Added NO_VALUE to test if macros have no value.
  55. 08/09/10:
  56. Added strcpy and StripNL.
  57. 08/08/10:
  58. Scrapped almost everything. Only VERY usefult things go in now.
  59. Functions:
  60. Stock:
  61. StripNL - Strips the newline characters from the end of a string.
  62. Inline:
  63. iseven - Checks if a number is even.
  64. isodd - Checks if a number is odd.
  65. isnull - Checks if a string is NULL ("\1\0").
  66. strcpy - Copy one string to another.
  67. Variables:
  68. Global:
  69. TRUE - True hack for infinate loops.
  70. FALSE - False hack for one-time loops.
  71. NULL - 1 long string for passing via Call(Remote|Local)Function.
  72. </remarks>
  73. \**--------------------------------------------------------------------------**/
  74. #if defined _INC_y_utils
  75. #endinput
  76. #endif
  77. #define _INC_y_utils
  78. #include "..\YSI_Internal\y_version"
  79. #include "..\YSI_Core\y_debug"
  80. #include "..\YSI_Storage\y_amx"
  81. //#tryinclude <sscanf>
  82. #include "..\amx\asm"
  83. //asm"
  84. // Add new tags to the START of this list.
  85. #include "..\YSI_Internal\y_globaltags"
  86. // VERY VERY VERY IMPORTANT!!! y_inline uses "130" instead of "YSI_MAX_STRING"
  87. // for two lines (one is "520" for "130 * 4").
  88. #define YSI_MAX_STRING (130)
  89. #define FUNCTION_LENGTH (32)
  90. // Better handling of operator precedences and floating point numbers. This
  91. // will now work for ALL regular numbers (including -0.5 which broke the old
  92. // version). I don't know of any complex expressions that break it with
  93. // operator precedences, but I'm not ruling it out. The brackets do try and
  94. // account for that possibility, but I just don't know.
  95. #define NO_VALUE(%0) ((2*%0-1)==2*(%0-1))
  96. #if !defined TRUE
  97. new stock
  98. bool:TRUE = true;
  99. #endif
  100. #if !defined FALSE
  101. new stock
  102. bool:FALSE = false;
  103. #endif
  104. #if !defined NULL
  105. new stock
  106. NULL[2] = "\1";
  107. #endif
  108. #define UNSIGNED(%0) ((%0) - cellmin)
  109. // Define "volatile" as nothing.
  110. #if !defined volatile
  111. #define volatile
  112. #endif
  113. #define YSIM_MASTER #M
  114. #define YSIM_RETURN #R
  115. #define YSIM_CALLER #C
  116. #define YSIM_TEXT_D #T
  117. #define YSIM_TEXT_L #L
  118. #define YSIM_TEXT_S #Y
  119. #define YSIM_TEXT_P #Z
  120. #define YSIM_ORDERS #O
  121. #define YSIM_HFIRST #H
  122. #define YSIM_OPDRET #D
  123. #define YSIM_TXTFND #X
  124. #define YSIM_TXTIND #I
  125. #define YSIM_TXTLEN #E
  126. #define YSIM_LOG_IN #U
  127. #if !defined YSIM_STRING
  128. #define YSIM_STRING (42)
  129. #endif
  130. #define FLOAT_INFINITY (Float:0x7F800000)
  131. #define FLOAT_NEG_INFINITY (Float:0xFF800000)
  132. #define FLOAT_NEGATIVE_INFINITY (Float:0xFF800000)
  133. #define FLOAT_NAN (Float:0x7FFFFFFF)
  134. #define FLOAT_NOT_A_NUMBER (Float:0x7FFFFFFF)
  135. #define FLOAT_QNAN (Float:0x7FFFFFFF)
  136. #define FLOAT_QUIET_NAN (Float:0x7FFFFFFF)
  137. #define FLOAT_QUIET_NOT_A_NUMBER (Float:0x7FFFFFFF)
  138. #define FLOAT_SNAN (Float:0x7FBFFFFF)
  139. #define FLOAT_SIGNALING_NAN (Float:0x7FBFFFFF)
  140. #define FLOAT_SIGNALING_NOT_A_NUMBER (Float:0x7FBFFFFF)
  141. //#pragma unused TRUE, FALSE, NULL
  142. #define __TY|||%0||| (1000000)
  143. #define __TX:__TY|||%0,%1||| (%1)
  144. #define __TF=fopen(%0,%2"%3",%4) __TF=fopen(%0".csv",%4)
  145. #if !defined TIMING_ITERATIONS
  146. #define TIMING_ITERATIONS (10)
  147. #endif
  148. stock __TU(t, iters)
  149. {
  150. // Number of times run. Accounts for the string and optional count.
  151. new
  152. ret[20];
  153. if (iters > 1000000000)
  154. format(ret, sizeof (ret), "%.2fps", float(t) / (float(iters) / 1000000000.0));
  155. else if (iters == 1000000000)
  156. format(ret, sizeof (ret), "%d.00ps", t);
  157. else if (iters > 1000000)
  158. format(ret, sizeof (ret), "%.2fns", float(t) / (float(iters) / 1000000.0));
  159. else if (iters == 1000000)
  160. format(ret, sizeof (ret), "%d.00ns", t);
  161. else if (iters > 1000)
  162. format(ret, sizeof (ret), "%.2fus", float(t) / (float(iters) / 1000.0));
  163. else if (iters == 1000)
  164. format(ret, sizeof (ret), "%d.00us", t);
  165. else if (iters > 1)
  166. format(ret, sizeof (ret), "%.2fms", float(t) / float(iters));
  167. else
  168. format(ret, sizeof (ret), "%d.00ms", t);
  169. return ret;
  170. }
  171. #define RUN_TIMING(%0) \
  172. for(new __TA[TIMING_ITERATIONS],__TC=sizeof __TA,__TE=_:__TX:__TY|||%0|||,__TS=printf("Timing \"%s\"...",%0);__TC; \
  173. printf("\t Mean = %s\n\t Mode = %s\n\tMedian = %s\n\t Range = %s", \
  174. __TU(Mean(__TA),__TE),__TU(Mode(__TA),__TE),__TU(Median(__TA),__TE),__TU(Range(__TA),__TE))) \
  175. for(;(__TS=GetTickCount(),__TC);__TA[--__TC]=GetTickCount()-__TS) \
  176. for(new __TI;__TI!=__TE;++__TI)
  177. #define CSV_TIMING(%0) \
  178. 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;\
  179. 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))\
  180. for(;(__TS=GetTickCount(),__TC);__TA[--__TC]=GetTickCount()-__TS)\
  181. for(new __TI;__TI!=__TE;++__TI)
  182. stock
  183. YSI_gPlayerIP[MAX_PLAYERS + 1] = {-1, ...};
  184. public OnPlayerConnect(playerid)
  185. {
  186. new
  187. ip[16];
  188. GetPlayerIp(playerid, ip, sizeof (ip));
  189. new
  190. ipv = strval(ip) << 24,
  191. pos = 0;
  192. while (pos < 15 && ip[pos++] != '.') {}
  193. ipv += strval(ip[pos]) << 16;
  194. while (pos < 15 && ip[pos++] != '.') {}
  195. ipv += strval(ip[pos]) << 8;
  196. while (pos < 15 && ip[pos++] != '.') {}
  197. ipv += strval(ip[pos]);
  198. YSI_gPlayerIP[playerid] = ipv;
  199. #if defined _y_utils_OnPlayerConnect
  200. _y_utils_OnPlayerConnect(playerid);
  201. #endif
  202. return 1;
  203. }
  204. #if defined _ALS_OnPlayerConnect
  205. #undef OnPlayerConnect
  206. #else
  207. #define _ALS_OnPlayerConnect
  208. #endif
  209. #define OnPlayerConnect _y_utils_OnPlayerConnect
  210. #if defined _y_utils_OnPlayerConnect
  211. forward _y_utils_OnPlayerConnect(playerid);
  212. #endif
  213. public OnPlayerDisconnect(playerid, reason)
  214. {
  215. YSI_gPlayerIP[playerid] = -1;
  216. #if defined _y_utils_OnPlayerDisconnect
  217. _y_utils_OnPlayerDisconnect(playerid, reason);
  218. #endif
  219. return 1;
  220. }
  221. #if defined _ALS_OnPlayerDisconnect
  222. #undef OnPlayerDisconnect
  223. #else
  224. #define _ALS_OnPlayerDisconnect
  225. #endif
  226. #define OnPlayerDisconnect _y_utils_OnPlayerDisconnect
  227. #if defined _y_utils_OnPlayerDisconnect
  228. forward _y_utils_OnPlayerDisconnect(playerid, reason);
  229. #endif
  230. /**--------------------------------------------------------------------------**\
  231. <summary>
  232. UCMP(value, upper);
  233. </summary>
  234. <param name="value">The unsigned number to compare.</param>
  235. <param name="upper">The upper limit.</param>
  236. <returns>
  237. An unsigned comparison between the two values.
  238. </returns>
  239. <remarks>
  240. -
  241. </remarks>
  242. \**--------------------------------------------------------------------------**/
  243. #define UCMP(%0,%1) IS_IN_RANGE(%0,0,(%1)) //(((%0) - cellmin) < ((%1) - cellmin))
  244. #define VALID_PLAYERID(%0) UCMP((%0), MAX_PLAYERS)
  245. /**--------------------------------------------------------------------------**\
  246. <summary>
  247. IS_IN_RANGE(value, lower, upper);
  248. </summary>
  249. <param name="value">The number to compare.</param>
  250. <param name="lower">The lower limit.</param>
  251. <param name="upper">The upper limit.</param>
  252. <returns>
  253. Is the value in the given range.
  254. </returns>
  255. <remarks>
  256. Equivalent to:
  257. (%1) <= (%0) < (%2)
  258. </remarks>
  259. \**--------------------------------------------------------------------------**/
  260. #define IS_IN_RANGE(%0,%1,%2) (((%0)-((%1)+cellmin))<((%2)-((%1)+cellmin)))
  261. #define NOT_IN_RANGE(%0,%1,%2) (((%0)-((%1)+cellmin))>=((%2)-((%1)+cellmin)))
  262. /**--------------------------------------------------------------------------**\
  263. <summary>
  264. ceildiv(numerator, denominator);
  265. </summary>
  266. <param name="numerator">The top of the division.</param>
  267. <param name="denominator">The bottom of the division.</param>
  268. <returns>
  269. (numerator / denominator) rounded up.
  270. </returns>
  271. <remarks>
  272. Normal integer division ALWAYS rounds down - this always rounds up.
  273. </remarks>
  274. \**--------------------------------------------------------------------------**/
  275. #define ceildiv(%0,%1) (((%0)-1)/(%1)+1)
  276. /**--------------------------------------------------------------------------**\
  277. <summary>
  278. floordiv(numerator, denominator);
  279. </summary>
  280. <param name="numerator">The top of the division.</param>
  281. <param name="denominator">The bottom of the division.</param>
  282. <returns>
  283. (numerator / denominator) rounded down.
  284. </returns>
  285. <remarks>
  286. Normal integer division ALWAYS rounds down - this also always rounds down,
  287. making it a little pointless, but also more explicit in function.
  288. </remarks>
  289. \**--------------------------------------------------------------------------**/
  290. #define floordiv(%0,%1) ((%0)/(%1))
  291. /**--------------------------------------------------------------------------**\
  292. <summary>isnull</summary>
  293. <param name="str">String to check if is null.</param>
  294. <returns>
  295. -
  296. </returns>
  297. <remarks>
  298. -
  299. </remarks>
  300. \**--------------------------------------------------------------------------**/
  301. #if !defined isnull
  302. #define isnull(%1) \
  303. ((%1[0] == 0) || (%1[0] == 1 && %1[1] == 0))
  304. #endif
  305. /**--------------------------------------------------------------------------**\
  306. <summary>isodd</summary>
  307. <param name="value">Value to check if is odd.</param>
  308. <returns>
  309. -
  310. </returns>
  311. <remarks>
  312. -
  313. </remarks>
  314. \**--------------------------------------------------------------------------**/
  315. #define isodd(%1) \
  316. ((%1) & 1)
  317. /**--------------------------------------------------------------------------**\
  318. <summary>iseven</summary>
  319. <param name="value">Value to check if is even.</param>
  320. <returns>
  321. -
  322. </returns>
  323. <remarks>
  324. -
  325. </remarks>
  326. \**--------------------------------------------------------------------------**/
  327. #define iseven(%1) \
  328. (!isodd(%1))
  329. /**--------------------------------------------------------------------------**\
  330. <summary>strcpy</summary>
  331. <param name="dest">Destination string.</param>
  332. <param name="src">Source string.</param>
  333. <param name="len">(Implicit) maximum length of the destination.</param>
  334. <returns>
  335. -
  336. </returns>
  337. <remarks>
  338. -
  339. </remarks>
  340. \**--------------------------------------------------------------------------**/
  341. #define strcpy(%0,%1) \
  342. strcat((%0[0] = '\0', %0), %1)
  343. /**--------------------------------------------------------------------------**\
  344. <summary>StripNL</summary>
  345. <param name="str[]">The string to remove the newline characters from</param>
  346. <returns>
  347. -
  348. </returns>
  349. <remarks>
  350. Updated from old versions, should be more efficient
  351. </remarks>
  352. \**--------------------------------------------------------------------------**/
  353. stock StripNL(str[])
  354. {
  355. P:7("StripNL called: \"%s\"", str);
  356. new
  357. i = strlen(str);
  358. while (i-- && str[i] <= ' ') str[i] = '\0';
  359. }
  360. /**--------------------------------------------------------------------------**\
  361. <summary>endofline</summary>
  362. <param name="line[]">String to check.</param>
  363. <param name="pos">Postion to start from.</param>
  364. <returns>
  365. -
  366. </returns>
  367. <remarks>
  368. Checks if the current point in a line is the end of non-whitespace data.
  369. </remarks>
  370. \**--------------------------------------------------------------------------**/
  371. stock endofline(line[], pos)
  372. {
  373. P:7("endofline called: \"%s\", %i", line, pos);
  374. if (NOT_IN_RANGE(pos, 0, strlen(line))) return 0;
  375. //if (pos < 0 || pos > strlen(line)) return 0;
  376. while (line[pos]) if (line[pos++] > ' ') return 0;
  377. return 1;
  378. }
  379. /**--------------------------------------------------------------------------**\
  380. <summary>chrfind</summary>
  381. <param name="needle">The character to find.</param>
  382. <param name="haystack[]">The string to find it in.</param>
  383. <param name="start">The offset to start from.</param>
  384. <returns>
  385. Fail - -1, Success - pos
  386. </returns>
  387. <remarks>
  388. -
  389. </remarks>
  390. \**--------------------------------------------------------------------------**/
  391. stock chrfind(needle, haystack[], start = 0)
  392. {
  393. P:7("chrfind called: %c, \"%s\", %i", needle, haystack, start);
  394. if (start < 0)
  395. {
  396. start = 0;
  397. }
  398. else if (start > strlen(haystack)) return -1;
  399. while (haystack[start]) if (haystack[start++] == needle) return start - 1;
  400. return -1;
  401. }
  402. stock chrfindp(needle, haystack[], start = 0)
  403. {
  404. P:7("chrfind called: %c, \"%s\", %i", needle, haystack, start);
  405. if (start < 0)
  406. {
  407. start = 0;
  408. }
  409. while (haystack{start}) if (haystack{start++} == needle) return start - 1;
  410. return -1;
  411. }
  412. /**--------------------------------------------------------------------------**\
  413. <summary>bernstein</summary>
  414. <param name="string[]">the string to hash.</param>
  415. <returns>
  416. the bernstein hash of the input string
  417. </returns>
  418. <remarks>
  419. This is a 32bit hash system so is not very secure, however we're only
  420. using this as a string enumerator to uniquely identify strings easilly
  421. and allow for a binary search of strings based on the hash of their name.
  422. crc32, then jenkins were originally used however this is far faster, if a
  423. little collision prone, but we're checking the strings manually anyway.
  424. This doesn't matter as it would be done regardless of hash method, so this
  425. doesn't need to be accounted for. Speed is all that matters with at
  426. least a bit of non collision (the number of strings we're dealing with,
  427. this should have none-few collisions).
  428. I modified it slightly from the original code pasted by aru, to code
  429. closer to the code http://www.burtleburtle.net/bob/hash/doobs.html and
  430. to work with PAWN (and shaved 0.2�s off the time for one call :D).
  431. Uber reduced version (just for fun):
  432. b(s[]){new h=-1,i,j;while((j=s[i++]))h=h*33+j;return h;}
  433. Update: Contrary to what I said above this is also used to identify colour
  434. strings for the updated text system involving file based styling and this
  435. is not checked for collisions as it's unimportant. But this doesn't affect
  436. the function at all, I just mentioned it here for "interest".
  437. Rewritten in self-generating assembly.
  438. </remarks>
  439. \**--------------------------------------------------------------------------**/
  440. stock bernstein(string[] /* 12 */)
  441. {
  442. // Only shown the very first time this function is run.
  443. P:7("bernstein called: \"%s\"", string);
  444. #pragma unused string
  445. new
  446. base,
  447. ctx[AsmContext];
  448. // Get this function.
  449. #emit CONST.pri bernstein
  450. #emit LOAD.alt AMX_HEADER_COD
  451. #emit ADD
  452. #emit STOR.S.pri base
  453. AsmInitPtr(ctx, base, 128), // Don't need any more than that.
  454. // Setup.
  455. @emit PROC
  456. @emit CONST.alt -1
  457. @emit PUSH.S 12 // string
  458. @emit LREF.S.pri 12 // string
  459. @emit JZER.rel 12 * 4 // bernstein_end
  460. // bernstein_loop:
  461. @emit XCHG
  462. @emit SMUL.C 33
  463. @emit ADD
  464. @emit MOVE.alt
  465. // Update the next pointer.
  466. @emit POP.pri
  467. @emit ADD.C 4
  468. @emit PUSH.pri
  469. // Load the data for the current pointer.
  470. @emit LOAD.I
  471. @emit JNZ.rel -(12 * 4) // bernstein_loop
  472. // bernstein_end:
  473. @emit MOVE.pri
  474. @emit STACK 4
  475. @emit RETN
  476. // Now actually CALL the written function.
  477. #emit LCTRL 5
  478. #emit SCTRL 4
  479. #emit CONST.pri bernstein
  480. #emit ADD.C 4
  481. #emit SCTRL 6
  482. return 0; // Make the compiler happy.
  483. }
  484. /**--------------------------------------------------------------------------**\
  485. <summary>ishex</summary>
  486. <param name="str[]">String to check.</param>
  487. <returns>
  488. true/false.
  489. </returns>
  490. <remarks>
  491. -
  492. </remarks>
  493. \**--------------------------------------------------------------------------**/
  494. stock ishex(str[])
  495. {
  496. P:7("ishex called: \"%s\"", str);
  497. new
  498. i,
  499. cur;
  500. if (str[0] == '0' && (str[1] | 0x20) == 'x') i = 2;
  501. do
  502. {
  503. cur = str[i++];
  504. }
  505. while (IS_IN_RANGE(cur, '0', '9' + 1) || IS_IN_RANGE(cur | 0x20, 'a', 'f' + 1));
  506. //while (('0' <= cur <= '9') || ('a' <= (cur | 0x20) <= 'f'));
  507. return !cur; // Valid if this is the end of the string.
  508. }
  509. /**--------------------------------------------------------------------------**\
  510. <summary>unpack</summary>
  511. <param name="str[]">String to unpack</param>
  512. <returns>
  513. unpacked string
  514. </returns>
  515. <remarks>
  516. Mainly used for debugging.
  517. </remarks>
  518. \**--------------------------------------------------------------------------**/
  519. stock unpack(const str[])
  520. {
  521. P:7("unpack called: \"%s\"", str);
  522. new
  523. ret[YSI_MAX_STRING] = {0};
  524. if (strlen(str) <= YSI_MAX_STRING)
  525. {
  526. strunpack(ret, str);
  527. }
  528. return ret;
  529. }
  530. /**--------------------------------------------------------------------------**\
  531. <summary>GetIP</summary>
  532. <param name="playerid">Player to get IP of.</param>
  533. <returns>
  534. IP as a 32bit int.
  535. </returns>
  536. <remarks>
  537. -
  538. </remarks>
  539. \**--------------------------------------------------------------------------**/
  540. // Cunning macro only uses "%0" once, yet is still safe.
  541. #define GetIP(%0) (YSI_gPlayerIP[min((%0) + cellmin, MAX_PLAYERS + cellmin) - cellmin])
  542. /**--------------------------------------------------------------------------**\
  543. <summary>getstring</summary>
  544. <param name="addr">Address of the string on the heap.</param>
  545. <returns>
  546. string
  547. </returns>
  548. <remarks>
  549. Is passed the result of getarg, which will be the address of a string (in
  550. theory) and uses that for DMA to get the string.
  551. </remarks>
  552. \**--------------------------------------------------------------------------**/
  553. stock getstring(addr)
  554. {
  555. new
  556. ret[YSI_MAX_STRING];
  557. va_getstring(ret, addr);
  558. return ret;
  559. }
  560. stock getstringarg(addr)
  561. {
  562. new
  563. ret[YSI_MAX_STRING];
  564. va_getstring(ret, addr);
  565. return ret;
  566. }
  567. /**--------------------------------------------------------------------------**\
  568. <summary>isnumeric</summary>
  569. <param name="str[]">String to check</param>
  570. <returns>
  571. -
  572. </returns>
  573. <remarks>
  574. Checks if a given string is numeric.
  575. </remarks>
  576. \**--------------------------------------------------------------------------**/
  577. stock isnumeric(str[])
  578. {
  579. P:7("isnumeric called: \"%s\"", str);
  580. new
  581. i;
  582. while (IS_IN_RANGE(str[i], '0', '9' + 1)) ++i;
  583. //while ((ch = str[i++])) if (!('0' <= ch <= '9')) return 0;
  584. return !str[i];
  585. }
  586. #if !defined _inc_sscanf || 1
  587. /**----------------------------------------------------------------------**\
  588. <summary>hexstr</summary>
  589. <param name=" string[]">String to convert to a number.</param>
  590. <returns>
  591. value of the passed hex string.
  592. </returns>
  593. <remarks>
  594. Now stops on invalid characters.
  595. </remarks>
  596. \**----------------------------------------------------------------------**/
  597. stock hexstr(string[])
  598. {
  599. new
  600. ret,
  601. val,
  602. i;
  603. if (string[0] == '0' && string[1] | 0x20 == 'x') i = 2;
  604. for ( ; ; )
  605. {
  606. switch ((val = string[i++]))
  607. {
  608. case '0' .. '9':
  609. {
  610. val -= '0';
  611. }
  612. case 'a' .. 'f':
  613. {
  614. val -= 'a' - 10;
  615. }
  616. case 'A' .. 'F':
  617. {
  618. val -= 'A' - 10;
  619. }
  620. default: break;
  621. }
  622. ret = ret << 4 | val;
  623. }
  624. return ret;
  625. }
  626. /**----------------------------------------------------------------------**\
  627. <summary>boolstr</summary>
  628. <param name=" string[]">String to try convert to a boolean.</param>
  629. <returns>
  630. bool: passed boolean.
  631. </returns>
  632. <remarks>
  633. This can take a number of ways of representing booleans - 0, false and
  634. nothing there. Anything not one of those things (false is not case
  635. sensitive) is assumed true.
  636. </remarks>
  637. \**----------------------------------------------------------------------**/
  638. stock bool:boolstr(string[])
  639. {
  640. // Hooray for De Morgan's rules!
  641. return string[0] && string[0] != '0' && strcmp(string, "false", true);
  642. }
  643. /**----------------------------------------------------------------------**\
  644. <summary>binstr</summary>
  645. <param name=" string[]">String to try convert to a boolean.</param>
  646. <returns>
  647. bool: passed boolean.
  648. </returns>
  649. <remarks>
  650. This takes a value in 0110101 (boolean) format and returns it as a
  651. regular value.
  652. </remarks>
  653. \**----------------------------------------------------------------------**/
  654. stock binstr(string[])
  655. {
  656. new
  657. pos = 0;
  658. switch (string[0])
  659. {
  660. case '0':
  661. {
  662. if (string[1] | 0x20 == 'b')
  663. {
  664. pos = 2;
  665. }
  666. }
  667. case '1':
  668. {
  669. }
  670. default:
  671. {
  672. return 0;
  673. }
  674. }
  675. new
  676. value = 0;
  677. for ( ; ; )
  678. {
  679. switch (string[pos++])
  680. {
  681. case '0':
  682. {
  683. value <<= 1;
  684. }
  685. case '1':
  686. {
  687. value = (value << 1) | 1;
  688. }
  689. default:
  690. {
  691. break;
  692. }
  693. }
  694. }
  695. return value;
  696. }
  697. #endif
  698. /**--------------------------------------------------------------------------**\
  699. <summary>
  700. rawMemcpy
  701. </summary>
  702. <param name="dest">Destination address.</param>
  703. <param name="src">Source data.</param>
  704. <param name="bytes">Number of bytes to copy.</param>
  705. <returns>
  706. -
  707. </returns>
  708. <remarks>
  709. Like memcpy, but takes addresses instead of arrays. Also far less secure.
  710. </remarks>
  711. \**--------------------------------------------------------------------------**/
  712. forward __rawMemcpy__(dest[], src[], bytes);
  713. public __rawMemcpy__(dest[], src[], bytes)
  714. {
  715. memcpy(dest, src, 0, bytes, bytes);
  716. memset("", 0, 0);
  717. strcmp("", "");
  718. }
  719. stock rawMemcpy(dest, src, bytes)
  720. {
  721. // Don't use "MOVS" as these blocks might overlap.
  722. #emit PUSH.S bytes
  723. #emit PUSH.S bytes
  724. #emit PUSH.C 0
  725. #emit PUSH.S src
  726. #emit PUSH.S dest
  727. #emit PUSH.C 20
  728. #emit SYSREQ.C memcpy
  729. #emit STACK 24
  730. #emit RETN
  731. return 0;
  732. }
  733. /**--------------------------------------------------------------------------**\
  734. <summary>
  735. memset
  736. rawMemset
  737. </summary>
  738. <param name="arr[], iAddress">Array or address to set to a value.</param>
  739. <param name="iValue">What to set the cells to.</param>
  740. <param name="iSize">Number of cells to fill.</param>
  741. <returns>
  742. -
  743. </returns>
  744. <remarks>
  745. Based on code by Slice:
  746. http://forum.sa-mp.com/showthread.php?p=1606781#post1606781
  747. Modified to use binary flags instead of a loop.
  748. "memset" takes an array, the size of the array, and a value to fill it with
  749. and sets the whole array to that value.
  750. "rawmemset" is similar, but takes an AMX data segment address instead and
  751. the size is in bytes, not cells. However, the size must still be a multiple
  752. of 4.
  753. </remarks>
  754. \**--------------------------------------------------------------------------**/
  755. stock memset(arr[], val = 0, size = sizeof (arr))
  756. {
  757. new
  758. addr;
  759. #emit LOAD.S.pri arr
  760. #emit STOR.S.pri addr
  761. // Convert the size from cells to bytes.
  762. return rawMemset(addr, val, size * 4);
  763. }
  764. stock rawMemset(iAddress /* 12 */, iValue /* 16 */, iSize /* 20 */)
  765. {
  766. // They are really, trust me!
  767. #pragma unused iAddress, iSize, iValue
  768. // The first time this is called it rewrites itself. Any other times it is
  769. // called it just uses the new code. This is like doing:
  770. //
  771. // static
  772. // bInitialised = false;
  773. // if (!bInitialised)
  774. // {
  775. // // Do something
  776. // bInitialised = true;
  777. // }
  778. // // Do rest.
  779. //
  780. // But better (though FAR more complex).
  781. // There is NO checking here that we don't write the function bigger than
  782. // the space available, or even that we don't overwrite "CIP", which would
  783. // be bad. The only way to make sure that doesn't happen is write a little
  784. // with a lot of code!
  785. new
  786. base,
  787. ctx[AsmContext];
  788. // Get this function.
  789. #emit CONST.pri rawMemset
  790. #emit LOAD.alt AMX_HEADER_COD
  791. #emit ADD
  792. #emit STOR.S.pri base
  793. AsmInitPtr(ctx, base, 80), // Don't need any more than that.
  794. // Frankly by this point we have probably already written more code than
  795. // will be generated!
  796. @emit PROC
  797. @emit LOAD.S.pri 20
  798. @emit CONST.alt 0xFFFFFFC
  799. @emit AND
  800. @emit STOR.pri (ctx[AsmContext_buffer] + 13 * 4)
  801. // The documentation says "PRI" should be a pointer, but that's not true!
  802. @emit LOAD.S.alt 12
  803. @emit LOAD.S.pri 16
  804. @emit FILL 0
  805. // Return the bytes filled.
  806. @emit LOAD.pri (ctx[AsmContext_buffer] + 13 * 4)
  807. @emit RETN
  808. // Do the second version.
  809. #emit CONST.pri memset
  810. #emit LOAD.alt AMX_HEADER_COD
  811. #emit ADD
  812. #emit STOR.S.pri base
  813. AsmInitPtr(ctx, base, 80),
  814. @emit PROC
  815. @emit LOAD.S.pri 20
  816. @emit SHL.C.pri 2
  817. @emit STOR.pri (ctx[AsmContext_buffer] + 12 * 4)
  818. @emit LOAD.S.alt 12
  819. @emit LOAD.S.pri 16
  820. @emit FILL 0
  821. // Return the bytes filled.
  822. @emit LOAD.pri (ctx[AsmContext_buffer] + 12 * 4)
  823. @emit RETN
  824. // Call this function again (the new version), but don't let the compiler
  825. // know... First clear the stack.
  826. #emit LCTRL 5
  827. #emit SCTRL 4
  828. #emit CONST.pri rawMemset
  829. #emit ADD.C 4
  830. #emit SCTRL 6
  831. // Never hit because of going to an earlier "RETN".
  832. return 0; //memset("", 0, 0);
  833. }
  834. /**--------------------------------------------------------------------------**\
  835. <summary>
  836. ReturnPlayerName
  837. </summary>
  838. <param name="playerid">Player whose name you want to get.</param>
  839. <returns>
  840. -
  841. </returns>
  842. <remarks>
  843. Now uses a global array to avoid repeated function calls. Actually doesn't
  844. because that causes issues with multiple scripts.
  845. </remarks>
  846. \**--------------------------------------------------------------------------**/
  847. stock ReturnPlayerName(playerid)
  848. {
  849. new
  850. str[MAX_PLAYER_NAME];
  851. GetPlayerName(playerid, str, sizeof (str));
  852. return str;
  853. }
  854. /**--------------------------------------------------------------------------**\
  855. <summary>
  856. ftouch(filename);
  857. </summary>
  858. <param name="filename">The file to "touch".</param>
  859. <returns>
  860. 0 - File already exists.
  861. 1 - File was created.
  862. -1 - File was not created.
  863. </returns>
  864. <remarks>
  865. This "touches" a file in the Unix sense of creating it but not opening or
  866. editing it in any way.
  867. </remarks>
  868. \**--------------------------------------------------------------------------**/
  869. stock ftouch(const filename[])
  870. {
  871. if (fexist(filename))
  872. {
  873. return 0;
  874. }
  875. else
  876. {
  877. new
  878. File:f = fopen(filename, io_write);
  879. if (f)
  880. {
  881. fclose(f);
  882. return 1;
  883. }
  884. else
  885. {
  886. return -1;
  887. }
  888. }
  889. }
  890. /**--------------------------------------------------------------------------**\
  891. <summary>
  892. InterpolateColour(startcolor, endcolor, value, maxvalue, minvalue = 0);
  893. </summary>
  894. <param name="startcolor">One of the two colours.</param>
  895. <param name="endcolor">The other of the two colours.</param>
  896. <param name="value">The interpolation value between the endpoints.</param>
  897. <param name="maxvalue">One of the two numbers.</param>
  898. <param name="minvalue">The other of the two numbers.</param>
  899. <returns>
  900. -
  901. </returns>
  902. <remarks>
  903. This function takes two endpoint values (minvalue and maxvalue, with
  904. minvalue defaulting to 0), along with a third value (value) whose distance
  905. between the two endpoints is calculated (as a percentage). This percentage
  906. value is then applied to the two colours given to find a third colour at
  907. some point between those two colours.
  908. For example, if the endpoints given are "0" and "10", and the value given is
  909. "3", then that is "30%" of the way between the two endpoints. We therefore
  910. want to find a colour that is 30% of the way between the two given colours.
  911. </remarks>
  912. \**--------------------------------------------------------------------------**/
  913. // "Interpolation" is the technical name for what you are doing here.
  914. #define InterpolateColor InterpolateColour
  915. stock InterpolateColour(startcolor, endcolor, value, maxvalue, minvalue = 0)
  916. {
  917. if (value >= maxvalue) return endcolor;
  918. if (value <= minvalue) return startcolor;
  919. static r, g, b, a;
  920. new
  921. time = maxvalue - minvalue,
  922. stage = value - minvalue;
  923. return
  924. // Step 1: Get the starting colour components.
  925. r = startcolor >>> 24 ,
  926. g = startcolor >>> 16 & 0xFF,
  927. b = startcolor >>> 8 & 0xFF,
  928. a = startcolor & 0xFF,
  929. // Step 2: Interpolate between the end points, and add to the start.
  930. r += ((endcolor >>> 24 ) - r) * stage / time,
  931. g += ((endcolor >>> 16 & 0xFF) - g) * stage / time,
  932. b += ((endcolor >>> 8 & 0xFF) - b) * stage / time,
  933. a += ((endcolor & 0xFF) - a) * stage / time,
  934. // Step 3: Combine the individual components.
  935. (r << 24) | ((g & 0xFF) << 16) | ((b & 0xFF) << 8) | (a & 0xFF);
  936. }
  937. /**--------------------------------------------------------------------------**\
  938. <summary>SkipWhitespace</summary>
  939. <param name="str[]">The string to skip over part of.</param>
  940. <param name="pos">The start of the whitespace.</param>
  941. <returns>
  942. The end of the whitespace.
  943. </returns>
  944. <remarks>
  945. Doesn't skip over NULL terminators.
  946. </remarks>
  947. \**--------------------------------------------------------------------------**/
  948. stock SkipWhitespace(const str[], pos)
  949. {
  950. while (IS_IN_RANGE(str[pos], '\0' + 1, ' ' + 1)) ++pos;
  951. //while ('\0' < str[pos] <= ' ') ++pos;
  952. return pos;
  953. }
  954. /**--------------------------------------------------------------------------**\
  955. <summary>Trim</summary>
  956. <param name="str[]">The string to trim.</param>
  957. <param name="start">Start of the substring.</param>
  958. <param name="end">End of the substring.</param>
  959. <returns>
  960. -
  961. </returns>
  962. <remarks>
  963. Modifies "start" and "end" to be tight on text in "str".
  964. </remarks>
  965. \**--------------------------------------------------------------------------**/
  966. stock Trim(const str[], &start, &end)
  967. {
  968. while (IS_IN_RANGE(str[start], '\0' + 1, ' ' + 1)) ++start;
  969. //while ('\0' < str[start] <= ' ') ++start;
  970. if (str[start])
  971. {
  972. while (end-- > start && str[end] <= ' ') {}
  973. ++end;
  974. }
  975. else
  976. {
  977. end = start;
  978. }
  979. }
  980. /**--------------------------------------------------------------------------**\
  981. <summary>ftell</summary>
  982. <param name="File:f">The file to get our position in.</param>
  983. <returns>
  984. The current position in the file.
  985. </returns>
  986. <remarks>
  987. Doesn't seem to work despite documentation claiming it will.
  988. </remarks>
  989. \**--------------------------------------------------------------------------**/
  990. //#define ftell(%0) fseek((%0), 0, seek_current)
  991. static stock Utils_PreSort(arr[], num = sizeof (arr))
  992. {
  993. // Very simple bubble sort (fast for pre-sorted arrays).
  994. new
  995. bool:sort;
  996. do
  997. {
  998. sort = false;
  999. for (new j = 1, temp; j != num; ++j)
  1000. {
  1001. if ((temp = arr[j]) < arr[j - 1])
  1002. {
  1003. arr[j] = arr[j - 1],
  1004. arr[j - 1] = temp,
  1005. sort = true;
  1006. }
  1007. }
  1008. }
  1009. while (sort);
  1010. }
  1011. stock Sum(const arr[], num = sizeof (arr))
  1012. {
  1013. new
  1014. tot;
  1015. while (num) tot += arr[--num];
  1016. return tot;
  1017. }
  1018. stock Mean(const arr[], num = sizeof (arr))
  1019. {
  1020. return Sum(arr, num) / num;
  1021. }
  1022. stock Mode(arr[], num = sizeof (arr))
  1023. {
  1024. Utils_PreSort(arr, num);
  1025. new
  1026. ret,
  1027. count = 0,
  1028. cn,
  1029. cc;
  1030. for (new i = 0; i != num; ++i)
  1031. {
  1032. if (arr[i] == cn) ++cc;
  1033. else
  1034. {
  1035. if (cc > count) count = cc, ret = cn;
  1036. cc = 1, cn = arr[i];
  1037. }
  1038. }
  1039. if (cc > count) return cn;
  1040. else return ret;
  1041. }
  1042. stock Median(arr[], num = sizeof (arr))
  1043. {
  1044. Utils_PreSort(arr, num);
  1045. new
  1046. idx = num >>> 1;
  1047. if (num & 1) return arr[idx];
  1048. else return (arr[idx] + arr[idx - 1]) >> 1;
  1049. }
  1050. stock Range(arr[], num = sizeof (arr))
  1051. {
  1052. Utils_PreSort(arr, num);
  1053. return arr[num - 1] - arr[0];
  1054. }
  1055. #include "..\YSI_Coding\y_va"
  1056. #include "..\YSI_Internal\y_shortfunc"