Advertisement
Guest User

Untitled

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