strlib.inc 41 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489149014911492149314941495149614971498149915001501150215031504150515061507150815091510151115121513151415151516151715181519152015211522152315241525152615271528152915301531153215331534153515361537153815391540154115421543154415451546154715481549155015511552155315541555155615571558155915601561156215631564156515661567156815691570157115721573157415751576157715781579158015811582158315841585158615871588158915901591159215931594159515961597159815991600160116021603160416051606160716081609161016111612161316141615161616171618161916201621162216231624162516261627162816291630163116321633163416351636163716381639164016411642164316441645164616471648164916501651165216531654165516561657165816591660166116621663166416651666166716681669167016711672167316741675167616771678167916801681168216831684168516861687168816891690169116921693169416951696169716981699170017011702170317041705170617071708170917101711171217131714171517161717171817191720172117221723172417251726172717281729173017311732173317341735173617371738173917401741174217431744174517461747174817491750175117521753175417551756175717581759176017611762176317641765176617671768176917701771177217731774177517761777177817791780178117821783178417851786178717881789179017911792179317941795179617971798
  1. #if defined STRLIB_INC
  2. #endinput
  3. #endif
  4. #define STRLIB_INC
  5. #include <a_samp>
  6. #if !defined STRLIB_BUFFER_SIZE
  7. #define STRLIB_BUFFER_SIZE 2048
  8. #endif
  9. #if !defined STRLIB_RETURN_SIZE
  10. #define STRLIB_RETURN_SIZE 128
  11. #endif
  12. #if !defined STRLIB_USE_FORMATEX
  13. #if defined __fmt_funcinc
  14. #if !defined FormatSpecifier
  15. #error Please include formatex before strlib.
  16. #endif
  17. #define STRLIB_USE_FORMATEX true
  18. #else
  19. #define STRLIB_USE_FORMATEX false
  20. #endif
  21. #endif
  22. // Used in strtrim (deprecated)
  23. enum trim_edges {
  24. trim_left = 1,
  25. trim_right = 2,
  26. trim_both = trim_left | trim_right
  27. };
  28. // Used in strtrim and strpad
  29. enum string_edges {
  30. edge_left = 1,
  31. edge_right = 2,
  32. edge_both = edge_left | edge_right
  33. };
  34. /*
  35. * Returns a formatted string.
  36. *
  37. * Parameters:
  38. * fmat[] - The format string.
  39. * ... - The format variables.
  40. *
  41. * Returns:
  42. * The formatted string.
  43. */
  44. forward sprintf(const fmat[], {Float, _}:...);
  45. /*
  46. * Get the first character of a string
  47. *
  48. * Parameters:
  49. * string[] - The string.
  50. *
  51. * Returns:
  52. * The first character of the string.
  53. */
  54. forward strgetfirstc(const string[]);
  55. /*
  56. * Get a character from a specific index in a string.
  57. *
  58. * Parameters:
  59. * string[] - The string.
  60. * index - The position in the string.
  61. *
  62. * Returns:
  63. * The character at that index, or '\0' if out of range.
  64. */
  65. forward strgetc(const string[], index);
  66. /*
  67. * Get the size of a string.
  68. *
  69. * Parameters:
  70. * string[] - The string.
  71. *
  72. * Returns:
  73. * The size of the string, in bytes.
  74. */
  75. forward strsize(const string[]);
  76. /*
  77. * Find out if a string is empty.
  78. *
  79. * Parameters:
  80. * string[] - The string.
  81. *
  82. * Returns:
  83. * True if empty, otherwise false.
  84. */
  85. forward bool:isempty(const string[]);
  86. /*
  87. * Compare two strings.
  88. *
  89. * Parameters:
  90. * str1[] - The first string.
  91. * str2[] - The second string.
  92. * ignorecase - Whether to compare them in a case-insensitive manner.
  93. *
  94. * Returns:
  95. * True if equal, otherwise false.
  96. */
  97. forward bool:isequal(const str1[], const str2[], bool:ignorecase = false);
  98. /*
  99. * Compare two strings, return Levenshtein distance between the two
  100. *
  101. * Parameters:
  102. * str1[] - The first string.
  103. * str2[] - The second string.
  104. * ignorecase - Whether to compare them in a case-insensitive manner.
  105. *
  106. * Returns:
  107. * Number of changes between the two strings.
  108. * This includes insertions, removals, and added characters (in that order I believe).
  109. */
  110. forward strdistance(const str1[], const str2[], bool:ignorecase = false);
  111. /*
  112. * Split a string by a given delimiter.
  113. *
  114. * Parameters:
  115. * output[][] - A multi-dimensional array that will be filled with substrings.
  116. * input[] - The input string to split.
  117. * delimiter[] - The delimiter to split by. Defaults to ",".
  118. * limit - The max. no. substrings.
  119. * trim - Whether to trim the substrings from whitespace. Defaults to true.
  120. * ignorecase - Whether the search for "delimiter" should be case-insensitive.
  121. * size1 - The size of the 1st dimension of output (otput[this][]). Defaults to sizeof(output).
  122. * size2 - The size of the 2nd dimension of output (otput[][this]). Defaults to sizeof(output[]).
  123. *
  124. * Returns:
  125. * The number of substrings that were copied into the array.
  126. */
  127. forward strexplode(output[][], const input[], const delimiter[] = !",", limit = cellmax, bool:trim = true, bool:ignorecase = false, size1 = sizeof(output), size2 = sizeof(output[]));
  128. /*
  129. * Glue together strings into one.
  130. *
  131. * Parameters:
  132. * glue[] - The string that will be between all other strings.
  133. * output[] - The output string.
  134. * maxlength - The size of "output". Defaults to sizeof(output).
  135. * ...[] - Strings to glue together.
  136. *
  137. * Returns:
  138. * Nothing
  139. */
  140. forward strimplode(const glue[], output[], maxlength = sizeof(output), ...);
  141. /*
  142. * Replace occurrences of the search string with the replacement string.
  143. *
  144. * Parameters:
  145. * string[] - The string to perform the replacing in.
  146. * search[] - The string to look for.
  147. * replacement[] - The string to put instead of "search".
  148. * ignorecase - Whether the search for "search" should be case-insensitive. Defaults to false.
  149. * pos - The position to start at. Defaults to 0 (the beginning).
  150. * limit - Limit the number of replacements. Defaults to -1 (no limit).
  151. * maxlength - The size of "string". Defaults to sizeof(string).
  152. *
  153. * Returns:
  154. * The number of replacements that were made.
  155. */
  156. forward strreplace(string[], const search[], const replacement[], bool:ignorecase = false, pos = 0, limit = -1, maxlength = sizeof(string));
  157. /*
  158. * Trim whitespace or a specific group of characters from a string.
  159. *
  160. * Parameters:
  161. * string[] - The string to trim.
  162. * chars[] - A string with characters to trim, or all whitespace if empty. Default is all whitespace.
  163. * edge - The edge(s) to trim (edge_left/edge_right/edge_both). Default is edge_both.
  164. *
  165. * Returns:
  166. * Nothing
  167. */
  168. forward strtrim(string[], const chars[] = !"", string_edges:edge = edge_both);
  169. /*
  170. * Pad edge(s) of a string with spaces.
  171. *
  172. * Parameters:
  173. * string[] - The string to pad.
  174. * length - The new length of the string.
  175. * substr[] - The substring to pad with. Defaults to a space (" ").
  176. * edge - The edge(s) to pad (edge_left/edge_right/edge_both). Default is edge_both.
  177. * trim_first - Whether to trim the string before padding.
  178. * trim_chars[] - The chars to trim, defaults is all whitespace.
  179. * maxlength - The size of "string". Defaults to sizeof(string).
  180. * input - Used internally.
  181. */
  182. forward strpad(string[], length, const substr[] = !" ", string_edges:edge = edge_both, bool:trim_first = true, const trim_chars[] = !"", maxlength = sizeof(string), const input[] = !"");
  183. /*
  184. * Wrap a string inside two other strings.
  185. *
  186. * Parameters:
  187. * left[] - The string on the left side.
  188. * string[] - The middle string that will be modified.
  189. * right[] - The string on the right side.
  190. * maxlength - The size of "string". Defaults to sizeof(string).
  191. */
  192. forward strwrap(const left[], string[], const right[], maxlength = sizeof(string));
  193. /*
  194. * Count substrings.
  195. *
  196. * Parameters:
  197. * string[] - The string to search inside.
  198. * sub[] - The string to search for.
  199. * ignorecase - Whether the search should be case-insensitive.
  200. * count_overlapped - Whether to count overlapping strings ("abcabc" in "abcabcabc" will count 2 instead of 1).
  201. *
  202. * Returns:
  203. * The number of occurrences of "sub" in "string".
  204. */
  205. forward strcount(const string[], const sub[], bool:ignorecase = false, bool:count_overlapped = false);
  206. /*
  207. * Read a string from a PAWN string literal.
  208. *
  209. * Parameters:
  210. * output[] - The variable to save into.
  211. * input[] - The string literal.
  212. * pos - The position in "input" to start reading from. Will be modified to the end of the literal.
  213. * maxlength - The size of "output". Defaults to sizeof(output).
  214. *
  215. * Returns:
  216. * true on success, false on error.
  217. */
  218. forward bool:strfromliteral(output[], const input[], &pos = 0, maxlength = sizeof(output));
  219. /*
  220. * Build a PAWN string literal from a given string.
  221. *
  222. * Parameters:
  223. * output[] - The variable to save into.
  224. * substrings[] - The string to build from.
  225. * maxlength - The size of "output". Defaults to sizeof(output).
  226. *
  227. * Returns:
  228. * Nothing
  229. */
  230. forward strtoliteral(output[], const input[], maxlength = sizeof(output), bool:paranoid = true);
  231. /*
  232. * Convert an array to a string.
  233. *
  234. * Example: {0x1122, 0x5566} becomes "0000112200005566".
  235. *
  236. * Parameters:
  237. * output[] - The variable to save into.
  238. * input[] - The array to build from.
  239. * inputlength - The size of "input". Defaults to sizeof(input).
  240. * maxlength - The size of "output". Defaults to sizeof(output).
  241. *
  242. * Returns:
  243. * Nothing
  244. */
  245. forward strfrombin(output[], const input[], inputlength = sizeof(input), maxlength = sizeof(output));
  246. /*
  247. * Convert a string to an array.
  248. *
  249. * Example: "0000112200005566" becomes {0x1122, 0x5566}.
  250. *
  251. * Parameters:
  252. * output[] - The variable to save into.
  253. * input[] - The array to build from.
  254. * maxlength - The size of "output". Defaults to sizeof(output).
  255. *
  256. * Returns:
  257. * The length of the output, in cells.
  258. */
  259. forward strtobin(output[], const input[], maxlength = sizeof(output));
  260. /*
  261. * Concatenate one string with a part of another.
  262. *
  263. * Parameters:
  264. * dest[] - The variable to concatenate the other part with.
  265. * source[] - The string to extract from.
  266. * start - The start offset, defaults to 0.
  267. * end - The start offset, defaults to end of string.
  268. * maxlength - The size of "dest". Defaults to sizeof(dest).
  269. */
  270. forward strcatmid(dest[], const source[], start = 0, end = -1, maxlength = sizeof(dest));
  271. /*
  272. * UTF-8 encode a string. Characters above 127 will be encoded into
  273. * two or more characters.
  274. *
  275. * Parameters:
  276. * dest[] - The output variable.
  277. * source[] - The string to encode.
  278. * maxlength - The size of "dest". Defaults to sizeof(dest).
  279. */
  280. forward utf8encode(dest[], const source[], maxlength = sizeof(dest));
  281. /*
  282. * UTF-8 decode a string. UTF-8 characters will be collapsed into single
  283. * characters in the array.
  284. *
  285. * Parameters:
  286. * dest[] - The output variable.
  287. * source[] - The string to encode.
  288. * maxlength - The size of "dest". Defaults to sizeof(dest).
  289. */
  290. forward utf8decode(dest[], const source[], maxlength = sizeof(dest));
  291. /*
  292. * Decode an encoded URL.
  293. *
  294. * Parameters:
  295. * output[] - The output variable.
  296. * input[] - The string to decode.
  297. * maxlength - The size of "output". Defaults to sizeof(output).
  298. */
  299. forward strurldecode(output[], const input[], maxlength = sizeof(output));
  300. /*
  301. * URL encode a string.
  302. *
  303. * Parameters:
  304. * output[] - The output variable.
  305. * input[] - The string to encode.
  306. * maxlength - The size of "output". Defaults to sizeof(output).
  307. * pack - Whether to pack the output. Defaults to false.
  308. */
  309. forward strurlencode(output[], const input[], maxlength = sizeof(output), bool:pack = false);
  310. // Same as above, but output is returned
  311. forward ret_strcatmid(const string[], const source[], start = 0, end = -1);
  312. forward ret_strfrombin(const input[], inputlength = sizeof(input));
  313. forward ret_strimplode(const glue[], ...);
  314. forward ret_strreplace(const string[], const search[], const replacement[], bool:ignorecase = false, pos = 0, limit = -1);
  315. forward ret_strfromliteral(const input[], &pos = 0);
  316. forward ret_strtoliteral(const input[], bool:paranoid = true);
  317. forward ret_strtrim(const string[], const chars[] = !"", string_edges:edge = edge_both);
  318. forward ret_strpad(const string[], length, const substr[] = !" ", string_edges:edge = edge_both, bool:trim_first = true, const trim_chars[] = !"");
  319. forward ret_strwrap(const left[], const string[], const right[]);
  320. forward ret_strurldecode(const input[]);
  321. forward ret_strurlencode(const input[], bool:pack = false);
  322. forward ret_utf8encode(const input[]);
  323. forward ret_utf8decode(const input[]);
  324. // Return from native functions
  325. forward ret_strpack(const source[]);
  326. forward ret_strunpack(const source[]);
  327. forward ret_strcat(const string1[], const string2[]);
  328. forward ret_strmid(const source[], start, end);
  329. forward ret_strins(const string[], const substr[], pos, maxlength = sizeof(string));
  330. forward ret_strdel(const string[], start, end);
  331. forward ret_valstr(value, bool:pack = false);
  332. forward ret_GetPlayerName(playerid, bool:pack = false);
  333. stock
  334. // Used throughout the library
  335. g_StrlibBuffer[2048]
  336. ;
  337. // Workaround for compiler bug
  338. forward _strlib_funcinc();
  339. public _strlib_funcinc() {
  340. new temp[1];
  341. format(!temp, 0, !temp);
  342. strcat(temp, temp);
  343. strpack(temp, temp);
  344. strunpack(temp, temp);
  345. }
  346. // Internal functions
  347. static stock RedirectArgument(arg, ...) {
  348. #emit LOAD.S.pri 0
  349. #emit ADD.C 12
  350. #emit LOAD.S.alt arg
  351. #emit SHL.C.alt 2
  352. #emit ADD
  353. #emit MOVE.alt
  354. #emit LOAD.S.pri 16
  355. #emit STOR.I
  356. }
  357. static stock CopyArgumentToHeap(arg, bool:pack = false, const argptr[] = "") {
  358. new arg_address, address;
  359. #emit LOAD.S.pri 0
  360. #emit ADD.C 12
  361. #emit LOAD.S.alt arg
  362. #emit SHL.C.alt 2
  363. #emit ADD
  364. #emit LOAD.I
  365. #emit STOR.S.pri arg_address
  366. #emit STOR.S.pri argptr
  367. if (pack) {
  368. new bytes = ((strlen(argptr) + 1 + 3) / 4) * 4;
  369. #emit LCTRL 2
  370. #emit STOR.S.pri address
  371. #emit LOAD.S.alt bytes
  372. #emit ADD
  373. #emit SCTRL 2
  374. //strpack(dest[], const source[], maxlength = sizeof dest)
  375. #emit LOAD.S.pri bytes
  376. #emit SHR.C.pri 2
  377. #emit PUSH.pri
  378. #emit PUSH.S arg_address
  379. #emit PUSH.S address
  380. #emit PUSH.C 12
  381. #emit SYSREQ.C strpack
  382. #emit STACK 16
  383. } else {
  384. new bytes = (strlen(argptr) + 1) * 4;
  385. #emit LCTRL 2
  386. #emit STOR.S.pri address
  387. #emit LOAD.S.alt bytes
  388. #emit ADD
  389. #emit SCTRL 2
  390. //strunpack(dest[], const source[], maxlength = sizeof dest)
  391. #emit LOAD.S.pri bytes
  392. #emit SHR.C.pri 2
  393. #emit PUSH.pri
  394. #emit PUSH.S arg_address
  395. #emit PUSH.S address
  396. #emit PUSH.C 12
  397. #emit SYSREQ.C strunpack
  398. #emit STACK 16
  399. }
  400. #emit LOAD.S.pri 0
  401. #emit ADD.C 12
  402. #emit LOAD.S.alt arg
  403. #emit SHL.C.alt 2
  404. #emit ADD
  405. #emit MOVE.alt
  406. #emit LOAD.S.pri address
  407. #emit STOR.I
  408. return address;
  409. }
  410. static stock RestoreHeapToAddress(address) {
  411. #emit LOAD.S.pri address
  412. #emit SCTRL 2
  413. }
  414. static stock IsOverlapping(const str1[], size1 = sizeof(str1), const str2[], size2 = sizeof(str2)) {
  415. new addr1, addr2;
  416. if (size1 == -1) {
  417. size1 = strsize(str1);
  418. } else {
  419. size1 *= 4;
  420. }
  421. if (size2 == -1) {
  422. size2 = strsize(str2);
  423. } else {
  424. size2 *= 4;
  425. }
  426. #emit LOAD.S.pri str1
  427. #emit STOR.S.pri addr1
  428. #emit LOAD.S.pri str2
  429. #emit STOR.S.pri addr2
  430. return (addr1 < addr2 + size2) && (addr2 < addr1 + size1);
  431. }
  432. // strlib functions
  433. #if !defined ispacked
  434. #define ispacked(%1) \
  435. ((%1)[0] > 255)
  436. #endif
  437. stock strgetfirstc(const string[]) {
  438. return ispacked(string) ? string{0} : string[0];
  439. }
  440. stock strgetc(const string[], index) {
  441. if (index < 0)
  442. return '\0';
  443. new len = strlen(string);
  444. if (index >= len)
  445. return '\0';
  446. return ispacked(string) ? string{index} : string[index];
  447. }
  448. stock strsize(const string[]) {
  449. new len = strlen(string);
  450. if (ispacked(string))
  451. return len + 1;
  452. return (len + 1) * 4;
  453. }
  454. stock bool:isempty(const string[]) {
  455. if (ispacked(string))
  456. return string{0} == '\0';
  457. else
  458. return string[0] == '\0';
  459. }
  460. stock bool:isequal(const str1[], const str2[], bool:ignorecase = false) {
  461. new
  462. c1 = (str1[0] > 255) ? str1{0} : str1[0],
  463. c2 = (str2[0] > 255) ? str2{0} : str2[0]
  464. ;
  465. if (!c1 != !c2)
  466. return false;
  467. return !strcmp(str1, str2, ignorecase);
  468. }
  469. stock strdistance(const str1[], const str2[], bool:ignorecase = false) {
  470. // If they are equal, theres no distance anyways
  471. if(isequal(str1, str2, ignorecase))
  472. return 0;
  473. static data[128][128];
  474. new bool:pack1 = ispacked(str1),
  475. bool:pack2 = ispacked(str2);
  476. new size1 = strlen(str1),
  477. size2 = strlen(str2);
  478. // Zero-length strings would return the size of the other string, because it's that many insertions
  479. if (size1 == 0)
  480. return size2;
  481. if (size2 == 0)
  482. return size1;
  483. // Intitalize data array
  484. for (new i; i <= size1; i++)
  485. data[i][0] = i;
  486. for (new j; j <= size2; j++)
  487. data[0][j] = j;
  488. // Loop through both strings, comparing each character to each character in the other string (think matrix)
  489. for (new j = 1; j <= size2; j++) {
  490. for (new i = 1; i <= size1; i++) {
  491. new char1 = pack1 ? str1{i - 1} : str1[i - 1],
  492. char2 = pack2 ? str2{j - 1} : str2[j - 1];
  493. // If ignorecase, make chars lower case.
  494. if(ignorecase) {
  495. if (65 <= char1 <= 90)
  496. char1 += 32;
  497. if (65 <= char2 <= 90)
  498. char2 += 32;
  499. }
  500. if (char1 == char2)
  501. data[i][j] = data[i - 1][j - 1];
  502. else {
  503. new l1 = data[i - 1][j] + 1,
  504. l2 = data[i][j - 1] + 1,
  505. l3 = data[i - 1][j - 1] + 1;
  506. l2 = (l1 > l2 ? l2 : l1);
  507. data[i][j] = (l3 > l2 ? l2 : l3);
  508. }
  509. }
  510. }
  511. return data[size1][size2];
  512. }
  513. stock strimplode(const glue[], output[], maxlength = sizeof(output), ...) {
  514. new args = numargs();
  515. // Null-out "output"
  516. output[0] = '\0';
  517. // Loop the variable arguments (the ones after "maxlength").
  518. for (new arg = 3; arg < args; arg++) {
  519. // If this isn't the first string, append the glue.
  520. if (arg != 3)
  521. strcat(output, glue, maxlength);
  522. // Wrap these in braces or they will be a part of the above if statement (compiler bug)
  523. {
  524. // Get the address of argument no. <arg>
  525. #emit LCTRL 5
  526. #emit ADD.C 12
  527. #emit LOAD.S.alt arg
  528. #emit SHL.C.alt 2
  529. #emit ADD
  530. #emit LOAD.I
  531. // Push the maxlength, arg address, and output address
  532. #emit PUSH.S maxlength
  533. #emit PUSH.pri
  534. #emit PUSH.S output
  535. // Push the argument count
  536. #emit PUSH.C 12
  537. // call strcat
  538. #emit SYSREQ.C strcat
  539. // Restore the stack
  540. #emit STACK 16
  541. }
  542. }
  543. }
  544. stock strexplode(output[][], const input[], const delimiter[] = !",", limit = cellmax, bool:trim = true, bool:ignorecase = false, size1 = sizeof(output), size2 = sizeof(output[])) {
  545. if (!size1 || !size2) {
  546. printf("(strexplode) ERROR: size1 = %d, size2 = %d. Can't be 0.", size1, size2);
  547. return 0;
  548. }
  549. if (isempty(delimiter)) {
  550. print(!"(strexplode) ERROR: delimiter is empty.");
  551. return 0;
  552. }
  553. if (trim) {
  554. new i = -1;
  555. if (ispacked(input)) {
  556. while (input{++i}) {
  557. if (input{i} > ' ') {
  558. i = -1;
  559. break;
  560. }
  561. }
  562. } else {
  563. while (input[++i]) {
  564. if (input[i] > ' ') {
  565. i = -1;
  566. break;
  567. }
  568. }
  569. }
  570. if (i != -1)
  571. return 0;
  572. } else if (isempty(input)) {
  573. return 0;
  574. }
  575. if (limit == 0) {
  576. return 0;
  577. } else if (limit == cellmax) {
  578. limit = 0;
  579. }
  580. new
  581. pos = 0,
  582. next,
  583. bool:packed = ispacked(input),
  584. dlen = strlen(delimiter),
  585. count = 0,
  586. end
  587. ;
  588. while (pos != -1) {
  589. ++count;
  590. if (limit > 0 && count >= limit) {
  591. next = -1;
  592. } else {
  593. next = strfind(input, delimiter, ignorecase, pos);
  594. }
  595. end = (next == -1) ? cellmax : next;
  596. if (trim) {
  597. if (end == cellmax)
  598. end = strlen(input);
  599. if (packed) {
  600. while (0 < input{pos} <= ' ') pos++;
  601. while (end > 0 && input{end - 1} <= ' ') end--;
  602. } else {
  603. while (0 < input[pos] <= ' ') pos++;
  604. while (end > 0 && input[end - 1] <= ' ') end--;
  605. }
  606. }
  607. strmid(output[count - 1], input, pos, end, size2);
  608. if (count >= size1 || next == -1 || (limit < 0 && count >= -limit))
  609. break;
  610. pos = next + dlen;
  611. }
  612. return count;
  613. }
  614. stock strreplace(string[], const search[], const replacement[], bool:ignorecase = false, pos = 0, limit = -1, maxlength = sizeof(string)) {
  615. // No need to do anything if the limit is 0.
  616. if (limit == 0)
  617. return 0;
  618. new
  619. sublen = strlen(search),
  620. replen = strlen(replacement),
  621. bool:packed = ispacked(string),
  622. maxlen = maxlength,
  623. len = strlen(string),
  624. count = 0
  625. ;
  626. // "maxlen" holds the max string length (not to be confused with "maxlength", which holds the max. array size).
  627. // Since packed strings hold 4 characters per array slot, we multiply "maxlen" by 4.
  628. if (packed)
  629. maxlen *= 4;
  630. // If the length of the substring is 0, we have nothing to look for..
  631. if (!sublen)
  632. return 0;
  633. // In this line we both assign the return value from "strfind" to "pos" then check if it's -1.
  634. while (-1 != (pos = strfind(string, search, ignorecase, pos))) {
  635. // Delete the string we found
  636. strdel(string, pos, pos + sublen);
  637. len -= sublen;
  638. // If there's anything to put as replacement, insert it. Make sure there's enough room first.
  639. if (replen && len + replen < maxlen) {
  640. strins(string, replacement, pos, maxlength);
  641. pos += replen;
  642. len += replen;
  643. }
  644. // Is there a limit of number of replacements, if so, did we break it?
  645. if (limit != -1 && ++count >= limit)
  646. break;
  647. }
  648. return count;
  649. }
  650. stock strtrim(string[], const chars[] = !"", string_edges:edge = edge_both) {
  651. new bool:packed = ispacked(string);
  652. // If "chars" is empty, trim whitespace
  653. if (!strgetfirstc(chars)) {
  654. // Should the left side be trimmed?
  655. if (edge & edge_left) {
  656. new i = 0;
  657. if (packed)
  658. while (0 < string{i} <= ' ') i++;
  659. else
  660. while (0 < string[i] <= ' ') i++;
  661. if (i) {
  662. strdel(string, 0, i);
  663. }
  664. }
  665. // Should the right side be trimmed?
  666. if (edge & edge_right) {
  667. new i = strlen(string);
  668. if (i) {
  669. if (packed) {
  670. while (--i && 0 < string{i} <= ' ') {}
  671. string{i + 1} = '\0';
  672. } else {
  673. while (--i && 0 < string[i] <= ' ') {}
  674. string[i + 1] = '\0';
  675. }
  676. }
  677. }
  678. } else {
  679. // Should the left side be trimmed?
  680. if (edge & edge_left) {
  681. new i = 0, sub[2];
  682. if (packed) {
  683. while ((sub[0] = string{i})) {
  684. if (strfind(chars, sub) == -1)
  685. break;
  686. i++;
  687. }
  688. if (i) {
  689. strdel(string, 0, i);
  690. }
  691. } else {
  692. while ((sub[0] = string[i])) {
  693. if (strfind(chars, sub) == -1)
  694. break;
  695. i++;
  696. }
  697. if (i) strdel(string, 0, i);
  698. }
  699. }
  700. // Should the right side be trimmed?
  701. if (edge & edge_right) {
  702. new i = strlen(string), sub[2];
  703. if (i >= 0) {
  704. if (packed) {
  705. while (i--) {
  706. sub[0] = string{i};
  707. if (strfind(chars, sub) == -1)
  708. break;
  709. }
  710. string{i + 1} = '\0';
  711. } else {
  712. while (i--) {
  713. sub[0] = string[i];
  714. if (strfind(chars, sub) == -1)
  715. break;
  716. }
  717. string[i + 1] = '\0';
  718. }
  719. }
  720. }
  721. }
  722. }
  723. stock strpad(string[], length, const substr[] = !" ", string_edges:edge = edge_both, bool:trim_first = true, const trim_chars[] = !"", maxlength = sizeof(string), const input[] = !"") {
  724. if (trim_first) {
  725. strtrim(string, trim_chars, edge);
  726. }
  727. new
  728. heap,
  729. length_left = 0,
  730. length_right = 0,
  731. len = strlen(string),
  732. sublen = strlen(substr),
  733. bool:packed,
  734. bool:subpacked = ispacked(substr)
  735. ;
  736. if (len > length)
  737. return;
  738. else
  739. length -= len;
  740. // Make "input" a pointer to "string"
  741. #emit LOAD.S.pri string
  742. #emit STOR.S.pri input
  743. // Copy "input" to the heap so it won't be linked to "string" anymore.
  744. heap = CopyArgumentToHeap(7);
  745. string[0] = '\0';
  746. len = 0;
  747. switch (edge) {
  748. case edge_left:
  749. length_left = length;
  750. case edge_right:
  751. length_right = length;
  752. default:
  753. length_left = length / 2, length_right = length - length_left;
  754. }
  755. if (length_left) {
  756. while (len < length_left) {
  757. if (subpacked)
  758. strcat(string, substr, length_left * 4);
  759. else
  760. strcat(string, substr, length_left + 1);
  761. len += sublen;
  762. }
  763. if (subpacked)
  764. string{length_left} = 0;
  765. }
  766. strcat(string, input, maxlength);
  767. if (length_right) {
  768. len = strlen(string);
  769. length_right += len;
  770. packed = ispacked(string);
  771. while (len < length_right) {
  772. if (packed)
  773. strcat(string, substr, length_right / 4 + 1);
  774. else
  775. strcat(string, substr, length_right + 1);
  776. len += sublen;
  777. }
  778. if (packed)
  779. string{length_right + 1} = 0;
  780. }
  781. RestoreHeapToAddress(heap);
  782. }
  783. stock strwrap(const left[], string[], const right[], maxlength = sizeof(string)) {
  784. strins(string, left, 0, maxlength);
  785. strcat(string, right, maxlength);
  786. }
  787. stock strcount(const string[], const sub[], bool:ignorecase = false, bool:count_overlapped = false) {
  788. new
  789. increment = count_overlapped ? 1 : strlen(sub),
  790. pos = -increment,
  791. count = 0
  792. ;
  793. while (-1 != (pos = strfind(string, sub, ignorecase, pos + increment)))
  794. count++;
  795. return count;
  796. }
  797. stock bool:strfromliteral(output[], const input[], &pos = 0, maxlength = sizeof(output)) {
  798. new
  799. length = strlen(input),
  800. c,
  801. outlen = 0,
  802. heap = 0
  803. ;
  804. // No need to do anything else.
  805. if (!length)
  806. return true;
  807. if (IsOverlapping(output, maxlength, input, -1))
  808. heap = CopyArgumentToHeap(1);
  809. output[0] = '\0';
  810. if (input[0] == '"')
  811. pos++;
  812. for (;; pos++) {
  813. if (outlen >= maxlength - 1 || pos >= length)
  814. break;
  815. c = input[pos];
  816. switch (c) {
  817. // String ended
  818. case '"': break;
  819. case '\\': {}
  820. default: {
  821. output[outlen++] = c;
  822. continue;
  823. }
  824. }
  825. // String ends with a backslash - invalid.
  826. if (pos == length - 1)
  827. goto return_false;
  828. // We're after a backslash now, let's see what's there.
  829. c = input[++pos];
  830. switch (c) {
  831. case '"',
  832. '\'',
  833. '\\',
  834. '%': output[outlen++] = c;
  835. case 'a': output[outlen++] = '\a';
  836. case 'b': output[outlen++] = '\b';
  837. case 'e': output[outlen++] = '\e';
  838. case 'f': output[outlen++] = '\f';
  839. case 'r': output[outlen++] = '\r';
  840. case 'n': output[outlen++] = '\n';
  841. case 't': output[outlen++] = '\t';
  842. case 'v': output[outlen++] = '\v';
  843. case 'x': {
  844. new val = 0;
  845. // String ends with "\x" - invalid.
  846. if (c == length - 1)
  847. goto return_false;
  848. while ((c = input[pos + 1])) {
  849. if ('a' <= c <= 'f' || 'A' <= c <= 'F') {
  850. val = (val << 4) + (tolower(c) - 'a' + 10);
  851. } else if ('0' <= c <= '9') {
  852. val = (val << 4) + (c - '0');
  853. } else {
  854. break;
  855. }
  856. pos++;
  857. }
  858. if (c == ';')
  859. pos++;
  860. output[outlen++] = val;
  861. }
  862. case '0' .. '9': {
  863. new val = 0;
  864. while ((c = input[pos])) {
  865. if ('0' <= c <= '9') {
  866. val = val * 10 + (c - '0');
  867. } else {
  868. break;
  869. }
  870. pos++;
  871. }
  872. if (c != ';') pos--;
  873. output[outlen++] = val;
  874. }
  875. default: {
  876. goto return_false;
  877. }
  878. }
  879. }
  880. output[outlen] = '\0';
  881. pos++;
  882. new bool:ret = true;
  883. goto return_true;
  884. return_false:
  885. ret = false;
  886. return_true:
  887. if (heap)
  888. RestoreHeapToAddress(heap);
  889. return ret;
  890. }
  891. stock strtoliteral(output[], const input[], maxlength = sizeof(output), bool:paranoid = true) {
  892. new i, c, outlen, heap = 0;
  893. if (IsOverlapping(output, maxlength, input, -1))
  894. heap = CopyArgumentToHeap(1);
  895. output[outlen++] = '"';
  896. for (i = 0; (c = input[i]); i++) {
  897. if (maxlength - outlen <= 3) {
  898. outlen = min(outlen, maxlength - 2);
  899. break;
  900. }
  901. switch (c) {
  902. case ' ', '!', '#' .. '[', ']', '^' .. '~':
  903. output[outlen++] = c;
  904. case '"': strunpack(output[outlen], !"\\\"", 3), outlen += 2;
  905. case '\a': strunpack(output[outlen], !"\\a" , 3), outlen += 2;
  906. case '\b': strunpack(output[outlen], !"\\b" , 3), outlen += 2;
  907. case '\e': strunpack(output[outlen], !"\\e" , 3), outlen += 2;
  908. case '\f': strunpack(output[outlen], !"\\f" , 3), outlen += 2;
  909. case '\r': strunpack(output[outlen], !"\\r" , 3), outlen += 2;
  910. case '\n': strunpack(output[outlen], !"\\n" , 3), outlen += 2;
  911. case '\t': strunpack(output[outlen], !"\\t" , 3), outlen += 2;
  912. case '\v': strunpack(output[outlen], !"\\v" , 3), outlen += 2;
  913. case '\\': strunpack(output[outlen], !"\\\\" , 3), outlen += 2;
  914. default: {
  915. if (!paranoid && 0x80 <= c <= 0xFF) {
  916. output[outlen++] = c;
  917. continue;
  918. }
  919. if (maxlength - outlen <= 8)
  920. break;
  921. format(output[outlen], 7, "\\x%03x;", c);
  922. outlen += 6;
  923. }
  924. }
  925. }
  926. output[outlen++] = '"';
  927. output[outlen] = '\0';
  928. if (heap)
  929. RestoreHeapToAddress(heap);
  930. }
  931. stock strfrombin(output[], const input[], inputlength = sizeof(input), maxlength = sizeof(output)) {
  932. static const hex_chars[] = "0123456789ABCDEF";
  933. new outlen = 0, heap = 0;
  934. if (IsOverlapping(output, maxlength, input, -1))
  935. heap = CopyArgumentToHeap(1);
  936. for (new i = 0; i < inputlength; i++) {
  937. if (maxlength - outlen <= 7) {
  938. outlen = min(outlen, maxlength - 1);
  939. break;
  940. }
  941. new input_cell = input[i];
  942. output[outlen++] = hex_chars[(input_cell ) >>> 28];
  943. output[outlen++] = hex_chars[(input_cell & 0x0F000000) >>> 24];
  944. output[outlen++] = hex_chars[(input_cell & 0x00F00000) >>> 20];
  945. output[outlen++] = hex_chars[(input_cell & 0x000F0000) >>> 16];
  946. output[outlen++] = hex_chars[(input_cell & 0x0000F000) >>> 12];
  947. output[outlen++] = hex_chars[(input_cell & 0x00000F00) >>> 8];
  948. output[outlen++] = hex_chars[(input_cell & 0x000000F0) >>> 4];
  949. output[outlen++] = hex_chars[(input_cell & 0x0000000F) ];
  950. }
  951. output[outlen] = '\0';
  952. if (heap)
  953. RestoreHeapToAddress(heap);
  954. }
  955. stock strtobin(output[], const input[], maxlength = sizeof(output)) {
  956. new len = strlen(input), outlen = 0, heap = 0;
  957. if (IsOverlapping(output, maxlength, input, -1))
  958. heap = CopyArgumentToHeap(1);
  959. for (new i = 0; i < len;) {
  960. if (outlen >= maxlength || i > len - 8) {
  961. break;
  962. }
  963. new c, out = 0;
  964. #define ADD_OUT(%1) \
  965. c = input[i++]; out |= (('a' <= c <= 'f' || 'A' <= c <= 'F') ? (tolower(c) - 'a' + 10) : (c - '0')) << %1
  966. ADD_OUT(28);
  967. ADD_OUT(24);
  968. ADD_OUT(20);
  969. ADD_OUT(16);
  970. ADD_OUT(12);
  971. ADD_OUT(8);
  972. ADD_OUT(4);
  973. ADD_OUT(0);
  974. #undef ADD_OUT
  975. output[outlen++] = out;
  976. }
  977. if (heap)
  978. RestoreHeapToAddress(heap);
  979. return outlen;
  980. }
  981. stock strurlencode(output[], const input[], maxlength = sizeof(output), bool:pack = false) {
  982. static const hex_chars[] = "0123456789ABCDEF";
  983. new
  984. len = strlen(input),
  985. bool:packed = ispacked(input),
  986. outlen = 0,
  987. heap = 0
  988. ;
  989. if (IsOverlapping(output, maxlength, input, -1))
  990. heap = CopyArgumentToHeap(1, packed);
  991. if (pack)
  992. maxlength *= 4;
  993. for (new i = 0; i < len; i++) {
  994. if (maxlength - outlen <= 1)
  995. break;
  996. new c = packed ? input{i} : input[i];
  997. switch (c) {
  998. case 'a' .. 'z', 'A' .. 'Z', '0' .. '9', '_': {
  999. if (pack)
  1000. output{outlen++} = c;
  1001. else
  1002. output[outlen++] = c;
  1003. }
  1004. case ' ': {
  1005. if (pack)
  1006. output{outlen++} = '+';
  1007. else
  1008. output[outlen++] = '+';
  1009. }
  1010. default: {
  1011. if (maxlength - outlen <= 3)
  1012. break;
  1013. if (pack) {
  1014. output{outlen++} = '%';
  1015. output{outlen++} = hex_chars[(c & 0xF0) >>> 4];
  1016. output{outlen++} = hex_chars[c & 0x0F];
  1017. } else {
  1018. output[outlen++] = '%';
  1019. output[outlen++] = hex_chars[(c & 0xF0) >>> 4];
  1020. output[outlen++] = hex_chars[c & 0x0F];
  1021. }
  1022. }
  1023. }
  1024. }
  1025. if (pack)
  1026. output{outlen} = '\0';
  1027. else
  1028. output[outlen] = '\0';
  1029. if (heap)
  1030. RestoreHeapToAddress(heap);
  1031. }
  1032. stock strurldecode(output[], const input[], maxlength = sizeof(output)) {
  1033. new prev_pos = 0, pos = 0, inputlen = strlen(input), len, heap = 0;
  1034. if (IsOverlapping(output, maxlength, input, -1))
  1035. heap = CopyArgumentToHeap(1);
  1036. output[0] = '\0';
  1037. while (-1 != (pos = strfind(input, "%", _, pos))) {
  1038. static str[2];
  1039. new c;
  1040. if (prev_pos != pos) {
  1041. len = strlen(output);
  1042. strcatmid(output, input, prev_pos, pos, maxlength);
  1043. strreplace(output, "+", " ", _, len, _, maxlength);
  1044. }
  1045. if (inputlen < pos + 3)
  1046. goto func_end;
  1047. str[0] = 0;
  1048. c = input[pos + 1]; str[0] |= (('a' <= c <= 'f' || 'A' <= c <= 'F') ? (tolower(c) - 'a' + 10) : (c - '0')) << 4;
  1049. c = input[pos + 2]; str[0] |= (('a' <= c <= 'f' || 'A' <= c <= 'F') ? (tolower(c) - 'a' + 10) : (c - '0'));
  1050. strcat(output, str, maxlength);
  1051. prev_pos = (pos += 3);
  1052. }
  1053. len = strlen(output);
  1054. strcatmid(output, input, prev_pos, _, maxlength);
  1055. strreplace(output, "+", " ", _, len, _, maxlength);
  1056. func_end:
  1057. if (heap)
  1058. RestoreHeapToAddress(heap);
  1059. }
  1060. stock strcatmid(dest[], const source[], start = 0, end = -1, maxlength = sizeof(dest)) {
  1061. new heap = 0;
  1062. if (IsOverlapping(dest, maxlength, source, -1))
  1063. heap = CopyArgumentToHeap(1);
  1064. if (start == 0 && end == -1) {
  1065. strcat(dest, source, maxlength);
  1066. } else {
  1067. if (end == -1)
  1068. end = strlen(source);
  1069. if (ispacked(dest)) {
  1070. new len = strlen(dest);
  1071. if (ispacked(source)) {
  1072. strunpack(g_StrlibBuffer, source);
  1073. strcat(dest, g_StrlibBuffer[start], min(maxlength, (len + end - start) / 4 + 1));
  1074. } else {
  1075. strcat(dest, source[start], min(maxlength, (len + end - start) / 4 + 1));
  1076. }
  1077. dest{len + end - start} = '\0';
  1078. } else {
  1079. if (ispacked(source)) {
  1080. strunpack(g_StrlibBuffer, source);
  1081. strcat(dest, g_StrlibBuffer[start], min(maxlength, strlen(dest) + end - start + 1));
  1082. } else {
  1083. strcat(dest, source[start], min(maxlength, strlen(dest) + end - start + 1));
  1084. }
  1085. }
  1086. }
  1087. if (heap)
  1088. RestoreHeapToAddress(heap);
  1089. }
  1090. stock utf8encode(dest[], const source[], maxlength = sizeof(dest)) {
  1091. new heap = 0;
  1092. if (IsOverlapping(dest, maxlength, source, -1)) {
  1093. heap = CopyArgumentToHeap(1);
  1094. }
  1095. new len = strlen(source);
  1096. new packed = ispacked(source);
  1097. dest[0] = '\0';
  1098. new idx = 0;
  1099. for (new i = 0; i < len; i++) {
  1100. new c = packed ? source{i} : source[i];
  1101. if (c >= 0x80) {
  1102. if (c > 0x4000000) {
  1103. // 6 byte
  1104. dest[idx++] = 0b11111100 | ((c >>> 30) & 0b00000001);
  1105. dest[idx++] = 0b10000000 | ((c >>> 24) & 0b00111111);
  1106. dest[idx++] = 0b10000000 | ((c >>> 18) & 0b00111111);
  1107. dest[idx++] = 0b10000000 | ((c >>> 12) & 0b00111111);
  1108. dest[idx++] = 0b10000000 | ((c >>> 6) & 0b00111111);
  1109. dest[idx++] = 0b10000000 | (c & 0b00111111);
  1110. } else if (c > 0x200000) {
  1111. // 5 byte
  1112. dest[idx++] = 0b11111000 | ((c >>> 24) & 0b00000011);
  1113. dest[idx++] = 0b10000000 | ((c >>> 18) & 0b00111111);
  1114. dest[idx++] = 0b10000000 | ((c >>> 12) & 0b00111111);
  1115. dest[idx++] = 0b10000000 | ((c >>> 6) & 0b00111111);
  1116. dest[idx++] = 0b10000000 | (c & 0b00111111);
  1117. } else if (c > 0x10000) {
  1118. // 4 byte
  1119. dest[idx++] = 0b11110000 | ((c >>> 18) & 0b00000111);
  1120. dest[idx++] = 0b10000000 | ((c >>> 12) & 0b00111111);
  1121. dest[idx++] = 0b10000000 | ((c >>> 6) & 0b00111111);
  1122. dest[idx++] = 0b10000000 | (c & 0b00111111);
  1123. } else if (c > 0x800) {
  1124. // 3 byte
  1125. dest[idx++] = 0b11100000 | ((c >>> 12) & 0b00001111);
  1126. dest[idx++] = 0b10000000 | ((c >>> 6) & 0b00111111);
  1127. dest[idx++] = 0b10000000 | (c & 0b00111111);
  1128. } else {
  1129. // 2 byte
  1130. dest[idx++] = 0b11000000 | ((c >>> 6) & 0b00011111);
  1131. dest[idx++] = 0b10000000 | (c & 0b00111111);
  1132. }
  1133. } else if (c > 0) {
  1134. dest[idx++] = c;
  1135. }
  1136. }
  1137. dest[idx++] = '\0';
  1138. if (heap) {
  1139. RestoreHeapToAddress(heap);
  1140. }
  1141. }
  1142. stock utf8decode(dest[], const source[], maxlength = sizeof(dest)) {
  1143. new heap = 0;
  1144. if (IsOverlapping(dest, maxlength, source, -1)) {
  1145. heap = CopyArgumentToHeap(1);
  1146. }
  1147. new len = strlen(source);
  1148. dest[0] = '\0';
  1149. new idx = 0;
  1150. for (new i = 0; i < len; i++) {
  1151. new c = source[i];
  1152. if (c & 0b10000000) {
  1153. if (c & 0b11100000 == 0b11000000) {
  1154. // 2 byte
  1155. if (i + 1 >= len) continue;
  1156. dest[idx++] = (c & 0b00011111) << 6 | (source[++i] & 0b00111111);
  1157. } else if (c & 0b11110000 == 0b11100000) {
  1158. // 3 byte
  1159. if (i + 2 >= len) continue;
  1160. dest[idx++] = (c & 0b00001111) << 12 |
  1161. (source[++i] & 0b00111111) << 6 |
  1162. (source[++i] & 0b00111111);
  1163. } else if (c & 0b11111000 == 0b11110000) {
  1164. // 4 byte
  1165. if (i + 3 >= len) continue;
  1166. dest[idx++] = (c & 0b00000111) << 18 |
  1167. (source[++i] & 0b00111111) << 12 |
  1168. (source[++i] & 0b00111111) << 6 |
  1169. (source[++i] & 0b00111111);
  1170. } else if (c & 0b11111100 == 0b11111000) {
  1171. // 5 byte
  1172. if (i + 4 >= len) continue;
  1173. dest[idx++] = (c & 0b00000011) << 24 |
  1174. (source[++i] & 0b00111111) << 18 |
  1175. (source[++i] & 0b00111111) << 12 |
  1176. (source[++i] & 0b00111111) << 6 |
  1177. (source[++i] & 0b00111111);
  1178. } else if (c & 0b11111110 == 0b11111100) {
  1179. // 6 byte
  1180. if (i + 5 >= len) continue;
  1181. dest[idx++] = (c & 0b00000001) << 30 |
  1182. (source[++i] & 0b00111111) << 24 |
  1183. (source[++i] & 0b00111111) << 18 |
  1184. (source[++i] & 0b00111111) << 12 |
  1185. (source[++i] & 0b00111111) << 6 |
  1186. (source[++i] & 0b00111111);
  1187. }
  1188. } else {
  1189. dest[idx++] = c;
  1190. }
  1191. }
  1192. dest[idx++] = 0;
  1193. if (heap) {
  1194. RestoreHeapToAddress(heap);
  1195. }
  1196. }
  1197. stock ret_strcatmid(const string[], const source[], start = 0, end = -1) {
  1198. new output[STRLIB_RETURN_SIZE];
  1199. strcat(output, string);
  1200. strcatmid(output, source, start, end);
  1201. return output;
  1202. }
  1203. stock ret_strfrombin(const input[], inputlength = sizeof(input)) {
  1204. new output[STRLIB_RETURN_SIZE];
  1205. strfrombin(output, input, inputlength);
  1206. return output;
  1207. }
  1208. stock ret_strimplode(const glue[], ...) {
  1209. new output[STRLIB_RETURN_SIZE];
  1210. const maxlength = sizeof(output);
  1211. new args = numargs();
  1212. // Loop the variable arguments (the ones after "maxlength").
  1213. for (new arg = 1; arg < args; arg++) {
  1214. // If this isn't the first string, append the glue.
  1215. if (arg != 1)
  1216. strcat(output, glue, maxlength);
  1217. // Wrap these in braces or they will be a part of the above if statement (compiler bug)
  1218. {
  1219. // Get the address of argument no. <arg>
  1220. #emit LCTRL 5
  1221. #emit ADD.C 12
  1222. #emit LOAD.S.alt arg
  1223. #emit SHL.C.alt 2
  1224. #emit ADD
  1225. #emit LOAD.I
  1226. // Push the maxlength, arg address, and output address
  1227. #emit PUSH.C maxlength
  1228. #emit PUSH.pri
  1229. #emit PUSH.ADR output
  1230. // Push the argument count
  1231. #emit PUSH.C 12
  1232. // call strcat
  1233. #emit SYSREQ.C strcat
  1234. // Restore the stack
  1235. #emit STACK 16
  1236. }
  1237. }
  1238. // Fix compiler bug (returning strings in variadic functions)
  1239. #emit LOAD.S.pri 8
  1240. #emit ADD.C 12
  1241. #emit MOVE.alt
  1242. #emit LCTRL 5
  1243. #emit ADD
  1244. #emit LOAD.I
  1245. #emit STOR.S.pri 20
  1246. return output;
  1247. }
  1248. stock ret_strreplace(const string[], const search[], const replacement[], bool:ignorecase = false, pos = 0, limit = -1) {
  1249. new output[STRLIB_RETURN_SIZE];
  1250. strcat(output, string);
  1251. strreplace(output, search, replacement, ignorecase, pos, limit);
  1252. return output;
  1253. }
  1254. stock ret_strfromliteral(const input[], &pos = 0) {
  1255. new output[STRLIB_RETURN_SIZE];
  1256. strcat(output, input);
  1257. strfromliteral(output, input, pos);
  1258. return output;
  1259. }
  1260. stock ret_strtoliteral(const input[], bool:paranoid = true) {
  1261. new output[STRLIB_RETURN_SIZE];
  1262. strcat(output, input);
  1263. strtoliteral(output, input, paranoid);
  1264. return output;
  1265. }
  1266. stock ret_strtrim(const string[], const chars[] = !"", string_edges:edge = edge_both) {
  1267. new output[STRLIB_RETURN_SIZE];
  1268. strcat(output, string);
  1269. strtrim(output, chars, edge);
  1270. return output;
  1271. }
  1272. stock ret_strpad(const string[], length, const substr[] = !" ", string_edges:edge = edge_both, bool:trim_first = true, const trim_chars[] = !"") {
  1273. new output[STRLIB_RETURN_SIZE];
  1274. strcat(output, string);
  1275. strpad(output, length, substr, edge, trim_first, trim_chars);
  1276. return output;
  1277. }
  1278. stock ret_strwrap(const left[], const string[], const right[]) {
  1279. new output[STRLIB_RETURN_SIZE];
  1280. strcat(output, left);
  1281. strcat(output, string);
  1282. strcat(output, right);
  1283. return output;
  1284. }
  1285. stock ret_strurldecode(const input[]) {
  1286. new output[STRLIB_RETURN_SIZE];
  1287. strcat(output, input);
  1288. strurldecode(output, input);
  1289. return output;
  1290. }
  1291. stock ret_strurlencode(const input[], bool:pack = false) {
  1292. new output[STRLIB_RETURN_SIZE];
  1293. strcat(output, input);
  1294. strurlencode(output, input, _, pack);
  1295. return output;
  1296. }
  1297. stock ret_utf8encode(const input[]) {
  1298. new output[STRLIB_RETURN_SIZE];
  1299. utf8encode(output, input);
  1300. return output;
  1301. }
  1302. stock ret_utf8decode(const input[]) {
  1303. new output[STRLIB_RETURN_SIZE];
  1304. utf8decode(output, input);
  1305. return output;
  1306. }
  1307. stock ret_strpack(const source[]) {
  1308. new output[STRLIB_RETURN_SIZE];
  1309. strpack(output, source);
  1310. return output;
  1311. }
  1312. stock ret_strunpack(const source[]) {
  1313. new output[STRLIB_RETURN_SIZE];
  1314. strunpack(output, source);
  1315. return output;
  1316. }
  1317. stock ret_strcat(const string1[], const string2[]) {
  1318. new output[STRLIB_RETURN_SIZE];
  1319. strcat(output, string1);
  1320. strcat(output, string2);
  1321. return output;
  1322. }
  1323. stock ret_strmid(const source[], start, end) {
  1324. new output[STRLIB_RETURN_SIZE];
  1325. strmid(output, source, start, end);
  1326. return output;
  1327. }
  1328. stock ret_strins(const string[], const substr[], pos, maxlength = sizeof(string)) {
  1329. new output[STRLIB_RETURN_SIZE];
  1330. strcat(output, string);
  1331. strins(output, substr, pos);
  1332. return output;
  1333. }
  1334. stock ret_strdel(const string[], start, end) {
  1335. new output[STRLIB_RETURN_SIZE];
  1336. strcat(output, string);
  1337. strdel(output, start, end);
  1338. return output;
  1339. }
  1340. stock ret_valstr(value, bool:pack = false) {
  1341. new output[STRLIB_RETURN_SIZE];
  1342. format(output, sizeof(output), "%d", value);
  1343. if (pack)
  1344. strpack(output, output);
  1345. return output;
  1346. }
  1347. stock ret_GetPlayerName(playerid, bool:pack = false) {
  1348. new output[MAX_PLAYER_NAME];
  1349. GetPlayerName(playerid, output, sizeof(output));
  1350. if (pack)
  1351. strpack(output, output);
  1352. return output;
  1353. }
  1354. stock sprintf(const fmat[], {Float, _}:...) {
  1355. static output[STRLIB_RETURN_SIZE], frm_header[3], heap;
  1356. const output_size = sizeof(output);
  1357. if (ispacked(fmat)) {
  1358. heap = CopyArgumentToHeap(0);
  1359. } else {
  1360. heap = 0;
  1361. }{}
  1362. // Store current frame header
  1363. #emit LCTRL 5
  1364. #emit CONST.alt frm_header
  1365. #emit MOVS 12
  1366. // Change the stack pointer to FRM + 12
  1367. #emit ADD.C 12 // pri is FRM (see above)
  1368. #emit SCTRL 4
  1369. // Push sizeof(output)
  1370. #emit PUSH.C output_size
  1371. // Push output
  1372. #emit PUSH.C output
  1373. // Push the argument count
  1374. #emit LOAD.S.pri 8
  1375. #emit ADD.C 8
  1376. #emit PUSH.pri
  1377. #if !STRLIB_USE_FORMATEX
  1378. const formatex = 0; // Dummy used to avoid "unknown symbol" error
  1379. goto do_sysreq;
  1380. #endif
  1381. // Call formatex (unless this was skipped above)
  1382. #emit LCTRL 6
  1383. #emit ADD.C 36
  1384. #emit PUSH.pri
  1385. #emit CONST.pri formatex
  1386. #emit SCTRL 6
  1387. #if !STRLIB_USE_FORMATEX
  1388. do_sysreq:
  1389. #endif
  1390. // Call format (unless formatex was called, in which case this is skipped)
  1391. #emit SYSREQ.C format
  1392. // Restore the stack pointer to FRM
  1393. #emit LCTRL 5
  1394. #emit SCTRL 4
  1395. // Copy back the frame header
  1396. #emit MOVE.alt
  1397. #emit CONST.pri frm_header
  1398. #emit MOVS 12
  1399. // Restore heap if needed
  1400. if (heap) {
  1401. RestoreHeapToAddress(heap);
  1402. }{}
  1403. // IMPORTANT: Fix compiler bug (returning strings in variadic functions)
  1404. #emit LOAD.S.pri 8
  1405. #emit ADD.C 12
  1406. #emit MOVE.alt
  1407. #emit LCTRL 5
  1408. #emit ADD
  1409. #emit LOAD.I
  1410. #emit STOR.S.pri 20 // 16 + (static_args * 4)
  1411. return output;
  1412. // It is actually used, just not by its symbol name
  1413. #pragma unused fmat
  1414. }