1
0

reading.inc 27 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762
  1. /*
  2. Legal:
  3. Version: MPL 1.1
  4. The contents of this file are subject to the Mozilla Public License Version
  5. 1.1 the "License"; you may not use this file except in compliance with
  6. the License. You may obtain a copy of the License at
  7. http://www.mozilla.org/MPL/
  8. Software distributed under the License is distributed on an "AS IS" basis,
  9. WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
  10. for the specific language governing rights and limitations under the
  11. License.
  12. The Original Code is the YSI framework.
  13. The Initial Developer of the Original Code is Alex "Y_Less" Cole.
  14. Portions created by the Initial Developer are Copyright C 2011
  15. the Initial Developer. All Rights Reserved.
  16. Contributors:
  17. Y_Less
  18. koolk
  19. JoeBullet/Google63
  20. g_aSlice/Slice
  21. Misiur
  22. samphunter
  23. tianmeta
  24. maddinat0r
  25. spacemud
  26. Crayder
  27. Dayvison
  28. Ahmad45123
  29. Zeex
  30. irinel1996
  31. Yiin-
  32. Chaprnks
  33. Konstantinos
  34. Masterchen09
  35. Southclaws
  36. PatchwerkQWER
  37. m0k1
  38. paulommu
  39. udan111
  40. Thanks:
  41. JoeBullet/Google63 - Handy arbitrary ASM jump code using SCTRL.
  42. ZeeX - Very productive conversations.
  43. koolk - IsPlayerinAreaEx code.
  44. TheAlpha - Danish translation.
  45. breadfish - German translation.
  46. Fireburn - Dutch translation.
  47. yom - French translation.
  48. 50p - Polish translation.
  49. Zamaroht - Spanish translation.
  50. Los - Portuguese translation.
  51. Dracoblue, sintax, mabako, Xtreme, other coders - Producing other modes for
  52. me to strive to better.
  53. Pixels^ - Running XScripters where the idea was born.
  54. Matite - Pestering me to release it and using it.
  55. Very special thanks to:
  56. Thiadmer - PAWN, whose limits continue to amaze me!
  57. Kye/Kalcor - SA:MP.
  58. SA:MP Team past, present and future - SA:MP.
  59. Optional plugins:
  60. Gamer_Z - GPS.
  61. Incognito - Streamer.
  62. Me - sscanf2, fixes2, Whirlpool.
  63. */
  64. /*
  65. ad88888ba
  66. d8" "8b ,d
  67. Y8, 88
  68. `Y8aaaaa, ,adPPYba, MM88MMM 88 88 8b,dPPYba,
  69. `"""""8b, a8P_____88 88 88 88 88P' "8a
  70. `8b 8PP""""""" 88 88 88 88 d8
  71. Y8a a8P "8b, ,aa 88, "8a, ,a88 88b, ,a8"
  72. "Y88888P" `"Ybbd8"' "Y888 `"YbbdP'Y8 88`YbbdP"'
  73. 88
  74. 88
  75. */
  76. enum e_INI_LINE_TYPE
  77. {
  78. e_INI_LINE_TYPE_INVALID,
  79. e_INI_LINE_TYPE_DATALESS,
  80. e_INI_LINE_TYPE_ENTRY,
  81. e_INI_LINE_TYPE_CONT,
  82. e_INI_LINE_TYPE_TAG
  83. }
  84. static stock
  85. YSI_g_sCurLine[MAX_INI_LINE];
  86. /*
  87. 88b d88
  88. 888b d888
  89. 88`8b d8'88
  90. 88 `8b d8' 88 ,adPPYYba, ,adPPYba, 8b,dPPYba, ,adPPYba, ,adPPYba,
  91. 88 `8b d8' 88 "" `Y8 a8" "" 88P' "Y8 a8" "8a I8[ ""
  92. 88 `8b d8' 88 ,adPPPPP88 8b 88 8b d8 `"Y8ba,
  93. 88 `888' 88 88, ,88 "8a, ,aa 88 "8a, ,a8" aa ]8I
  94. 88 `8' 88 `"8bbdP"Y8 `"Ybbd8"' 88 `"YbbdP"' `"YbbdP"'
  95. */
  96. #define INI_Parse(%1,%2) \
  97. forward @INI_%1_%2(name[], value[]); \
  98. @INI_%1_%2(name[], value[])
  99. #define INI:%0[%1](%2) \
  100. forward @INI_%0_%1(%2); \
  101. @INI_%0_%1(%2)
  102. /*-------------------------------------------------------------------------*//**
  103. * <param name="name">Name of the INI key.</param>
  104. * <param name="variable">Variable to fill with integer value.</param>
  105. *//*------------------------------------------------------------------------**/
  106. P:D(INI_Int(const name[],&variable));
  107. #define INI_Int(%1,%2) \
  108. if (!strcmp((%1), name, true)) return %2 = strval(value)
  109. /*-------------------------------------------------------------------------*//**
  110. * <param name="name">Name of the INI key.</param>
  111. * <param name="variable">Variable to fill with float value.</param>
  112. *//*------------------------------------------------------------------------**/
  113. P:D(INI_Float(const name[],&Float:variable));
  114. #define INI_Float(%1,%2) \
  115. if (!strcmp((%1), name, true)) return _:(%2 = floatstr(value))
  116. /*-------------------------------------------------------------------------*//**
  117. * <param name="name">Name of the INI key.</param>
  118. * <param name="variable">Variable to fill with hex value.</param>
  119. *//*------------------------------------------------------------------------**/
  120. P:D(INI_Hex(const name[],&variable));
  121. #define INI_Hex(%1,%2) \
  122. if (!strcmp((%1), name, true)) return %2 = hexstr(value)
  123. /*-------------------------------------------------------------------------*//**
  124. * <param name="name">Name of the INI key.</param>
  125. * <param name="variable">Variable to fill with binary value.</param>
  126. *//*------------------------------------------------------------------------**/
  127. P:D(INI_Bin(const name[],&variable));
  128. #define INI_Bin(%1,%2) \
  129. if (!strcmp((%1), name, true)) return %2 = binstr(value)
  130. /*-------------------------------------------------------------------------*//**
  131. * <param name="name">Name of the INI key.</param>
  132. * <param name="variable">Variable to fill with string value.</param>
  133. * <param name="len">Optional string length.</param>
  134. * <remarks>
  135. * The old version of "INI_String" didn't like not having a length. It gave a
  136. * very odd error message too. This has now been corrected by making the
  137. * length parameter optional.
  138. * </remarks>
  139. *//*------------------------------------------------------------------------**/
  140. P:D(INI_String(const name[],variable[]));
  141. #define INI_String(%1,%2) \
  142. if (!strcmp((%1), name, true)) return _:INI_CHECK_LEN:strcpy(%2, value)
  143. #define INI_CHECK_LEN:strcpy(%0,%1,%2) strcpy(%0,%2,%1)
  144. /*-------------------------------------------------------------------------*//**
  145. * <param name="name">Name of the INI key.</param>
  146. * <param name="variable">Variable to fill with string value.</param>
  147. *//*------------------------------------------------------------------------**/
  148. P:D(INI_Bool(const name[],&bool:variable));
  149. #define INI_Bool(%1,%2) \
  150. if (!strcmp((%1), name, true)) return %2 = boolstr(value)
  151. /*
  152. 88 88 88 88
  153. 88 88 ,d "" 88
  154. 88 88 88 88
  155. 88 88 MM88MMM 88 88 ,adPPYba,
  156. 88 88 88 88 88 I8[ ""
  157. 88 88 88 88 88 `"Y8ba,
  158. Y8a. .a8P 88, 88 88 aa ]8I
  159. `"Y8888Y"' "Y888 88 88 `"YbbdP"'
  160. */
  161. #define INI_SkipWhitespace(%0,%1) while ('\0' < %0[%1] <= ' ') ++%1
  162. /*-------------------------------------------------------------------------*//**
  163. * <param name="callback">The callback destination.</param>
  164. * <param name="format">The function name format.</param>
  165. * <param name="tag">The tag destination.</param>
  166. * <param name="input">The tag source.</param>
  167. * <param name="callbackFormat">The callback parameter specifiers.</param>
  168. * <param name="remote">Use "CallRemoteFunction".</param>
  169. * <returns>
  170. * Was the function found?
  171. * </returns>
  172. * <remarks>
  173. * Gets a callback given a partial function name and a tag name. Also saves
  174. * the tag elsewhere. This might not work as a separate function - it will
  175. * need to be in the function called by the function with the inlines in.
  176. * </remarks>
  177. *//*------------------------------------------------------------------------**/
  178. P:D(INI_GetCallback(callback,format,tag,input,callbackFormat,remote));
  179. #define INI_GetCallback(%0,%1,%2,%3,%4,%5) \
  180. ( \
  181. strcpy((%2), (%3)), \
  182. Inline_Reset((%0)), \
  183. format(YSI_g_sCurLine, sizeof (YSI_g_sCurLine), (%1), (%2)), \
  184. Callback_Get(callback_tag:YSI_g_sCurLine, (%0), (%4), (%5)) \
  185. )
  186. /*
  187. 88b d88 88 db 88888888ba 88
  188. 888b d888 "" d88b 88 "8b 88
  189. 88`8b d8'88 d8'`8b 88 ,8P 88
  190. 88 `8b d8' 88 ,adPPYYba, 88 8b,dPPYba, d8' `8b 88aaaaaa8P' 88
  191. 88 `8b d8' 88 "" `Y8 88 88P' `"8a d8YaaaaY8b 88""""""' 88
  192. 88 `8b d8' 88 ,adPPPPP88 88 88 88 d8""""""""8b 88 88
  193. 88 `888' 88 88, ,88 88 88 88 d8' `8b 88 88
  194. 88 `8' 88 `"8bbdP"Y8 88 88 88 d8' `8b 88 88
  195. */
  196. /*-------------------------------------------------------------------------*//**
  197. * <param name="filename">The file to load.</param>
  198. * <param name="remoteFormat">The format string to generate the remote function to
  199. * pass the data to once loaded.</param>
  200. * <param name="bFileFirst">The order of the remoteFormat parameters.</param>
  201. * <param name="bExtra">Send additional data.</param>
  202. * <param name="extra">Additional data to send.</param>
  203. * <param name="bLocal">Call local functions instead of global ones.</param>
  204. * <param name="bPassTag">Pass the tag as an extra parameter not the function
  205. * name.</param>
  206. * <param name="bFilter">Apply the tag name filter to all tags or just prefixed
  207. * ones?</param>
  208. * <param name="filter">Text to use to search for which tags to load.</param>
  209. * <remarks>
  210. * bFileFirst sets the order and inclusion of the possible remoteFormat
  211. * parameters. If true the format will add the filename first then the
  212. * current tag, if false the order will be reversed. This can also be used
  213. * to exclude one or the other from the function name by setting the required
  214. * parameter to be entered first and then only having one %s in the format
  215. * sting. The default order is tag first for languages compatibility.
  216. *
  217. * This function is now EXTENSIVELY documented here:
  218. *
  219. * <a href="http://forum.sa-mp.com/showthread.php?t=485611" />
  220. *
  221. * </remarks>
  222. *//*------------------------------------------------------------------------**/
  223. stock bool:INI_ParseFile(fname[], remoteFormat[], bool:bFileFirst = false, bool:bExtra = false, extra = 0, bool:bLocal = true, bool:bPassTag = false, bool:bFilter = true, filter[] = "")
  224. {
  225. P:3("bool:INI_ParseFile called: \"%s\", \"%s\", %i, %i, %i, %i, %i, %i, \"%s\"", fname, remoteFormat, bFileFirst, bExtra, extra, bLocal, bPassTag, bFilter, filter);
  226. static
  227. callback[E_CALLBACK_DATA],
  228. tag[32], // The current tag being parsed.
  229. cbSpec[5], // The callback specifier.
  230. cbName[32]; // The callback name format.
  231. new
  232. File:f = fopen(fname, io_read);
  233. if (!f) return false;
  234. new
  235. rlen, ppos,
  236. p0s, p0e, p1s, p1e, p2s, p2e;
  237. P:5("INI_ParseFile: Opened.");
  238. bLocal = !bLocal, // Invert for "remote".
  239. INI_MakeCallbackFormat(bExtra, bPassTag, cbSpec),
  240. INI_SetupCallbackName(cbName, remoteFormat, fname, bFileFirst);
  241. new
  242. bool:handle = INI_GetCallback(callback, cbName, tag, "", cbSpec, bLocal);
  243. while ((rlen = fread(f, YSI_g_sCurLine)))
  244. {
  245. ppos += rlen;
  246. switch (INI_IdentifyLineType(YSI_g_sCurLine, p0s, p0e, p1s, p1e, p2s, p2e))
  247. {
  248. case e_INI_LINE_TYPE_INVALID:
  249. {
  250. // "P:I" not "P:W" because this is not a script issue.
  251. P:I("Invalid line in INI file \"%s\": %s", fname, YSI_g_sCurLine);
  252. }
  253. case e_INI_LINE_TYPE_DATALESS:
  254. {
  255. // Do nothing.
  256. }
  257. case e_INI_LINE_TYPE_TAG:
  258. {
  259. // A new tag.
  260. Callback_Restore(callback),
  261. Callback_Release(callback),
  262. // First, check if it is a tag we might care about.
  263. YSI_g_sCurLine[p0e] = '\0';
  264. if (YSI_g_sCurLine[p0s] == '@' && YSI_g_sCurLine[p0s + 1] == '@' && (p0e = strfind(YSI_g_sCurLine, "-")) != -1)
  265. {
  266. // Check if the current tag is one of the ones we want to
  267. // filer for. The "@@" prefix is the "filterable" prefix.
  268. // If there is no filter then everything will be loaded.
  269. if (p0e == p0s + 2 || !YSI_g_sCurLine[p0e + 1])
  270. {
  271. P:I("Invalid line in INI file \"%s\": %s", fname, YSI_g_sCurLine);
  272. continue;
  273. }
  274. YSI_g_sCurLine[p0e] = '\0';
  275. if (bFilter && strcmp(YSI_g_sCurLine[p0s + 2], filter))
  276. {
  277. // Only skip this if filtering is enabled. We can't put
  278. // the "bFilter" check higher or the "-" won't be found
  279. // to resolve the tag name accurately.
  280. handle = false;
  281. continue;
  282. }
  283. p0s = p0e + 1;
  284. }
  285. // This is a tag we can use, is there a handler for it?
  286. // Is this based on another tag? If so recurse and do that one
  287. // first.
  288. new
  289. parent[32];
  290. YSI_g_sCurLine[p1e] = '\0', strcat(parent, YSI_g_sCurLine[p1s]);
  291. if ((handle = INI_GetCallback(callback, cbName, tag, YSI_g_sCurLine[p0s], cbSpec, bLocal)) && p1s != p1e)
  292. {
  293. // No point recursing if there's no handler is there?
  294. if (!INI_DoParentTag(ppos, parent, f, callback, bExtra, extra, bPassTag, tag, bFilter, filter))
  295. P:I("Invalid hierarchy in INI file: \"%s\" for tag \"%s\"", fname, tag);
  296. fseek(f, ppos, seek_start);
  297. }
  298. }
  299. default: // e_INI_LINE_TYPE_ENTRY and e_INI_LINE_TYPE_CONT.
  300. {
  301. if (!handle) continue;
  302. // Standard key-value pair.
  303. YSI_g_sCurLine[p0e] = YSI_g_sCurLine[p1e] = '\0';
  304. if (bExtra)
  305. {
  306. if (bPassTag) Callback_Call(callback, extra, tag, YSI_g_sCurLine[p0s], YSI_g_sCurLine[p1s]);
  307. else Callback_Call(callback, extra, YSI_g_sCurLine[p0s], YSI_g_sCurLine[p1s]);
  308. }
  309. else
  310. {
  311. if (bPassTag) Callback_Call(callback, tag, YSI_g_sCurLine[p0s], YSI_g_sCurLine[p1s]);
  312. else Callback_Call(callback, YSI_g_sCurLine[p0s], YSI_g_sCurLine[p1s]);
  313. }
  314. }
  315. }
  316. // Don't put any code down here (at the end of the loop).
  317. }
  318. return
  319. Callback_Restore(callback),
  320. Callback_Release(callback),
  321. fclose(f),
  322. true;
  323. }
  324. /*-------------------------------------------------------------------------*//**
  325. * <param name="filename">The file to load.</param>
  326. * <param name="bExtra">Send additional data.</param>
  327. * <param name="extra">Additional data to send.</param>
  328. * <param name="bLocal">Call local functions instead of gloabal ones.</param>
  329. * <returns>
  330. * INI_ParseFile
  331. * </returns>
  332. * <remarks>
  333. * Wrapper for INI_ParseFile to use standard API features so people can
  334. * worry even less. Designed for use with INI_Parse.
  335. * </remarks>
  336. *//*------------------------------------------------------------------------**/
  337. stock bool:INI_Load(filename[], bool:bExtra = false, extra = 0, bool:bLocal = true)
  338. {
  339. P:3("bool:INI_Load called: \"%s\", %i, %i, %i", filename, _:bExtra, extra, _:bLocal);
  340. return INI_ParseFile(filename, "@INI_%s_%s", .bFileFirst = true, .bExtra = bExtra, .extra = extra, .bLocal = bLocal);
  341. }
  342. /*
  343. 88 88 88 ad88
  344. 88 88 ,d "" d8"
  345. 88 88 88 88
  346. 88 ,adPPYb,88 ,adPPYba, 8b,dPPYba, MM88MMM 88 MM88MMM 8b d8
  347. 88 a8" `Y88 a8P_____88 88P' `"8a 88 88 88 `8b d8'
  348. 88 8b 88 8PP""""""" 88 88 88 88 88 `8b d8'
  349. 88 "8a, ,d88 "8b, ,aa 88 88 88, 88 88 `8b,d8'
  350. 88 `"8bbdP"Y8 `"Ybbd8"' 88 88 "Y888 88 88 Y88'
  351. d8'
  352. d8'
  353. */
  354. /*-------------------------------------------------------------------------*//**
  355. * <param name="str">The string you want to type analyse.</param>
  356. * <param name="p0s">Start of part 0.</param>
  357. * <param name="p0e">End of part 0.</param>
  358. * <param name="p1s">Start of part 1.</param>
  359. * <param name="p1e">End of part 1.</param>
  360. * <param name="p2s">Start of part 2.</param>
  361. * <param name="p2e">End of part 2.</param>
  362. * <param name="cont">Is this a line continuation?</param>
  363. * <returns>
  364. * e_INI_LINE_TYPE
  365. * </returns>
  366. * <remarks>
  367. * This function's signature is so long that I put it on a separate line. This
  368. * takes a line and determines what it is and where the parts are.
  369. * </remarks>
  370. *//*------------------------------------------------------------------------**/
  371. stock e_INI_LINE_TYPE:
  372. INI_IdentifyLineType(const str[], &p0s, &p0e, &p1s, &p1e, &p2s, &p2e)
  373. {
  374. // Reset everything.
  375. p0s = p0e = p1s = p1e = p2s = p2e = 0;
  376. // Do this purely with a single main loop, and a state machine.
  377. new
  378. end,
  379. e_INI_LINE_TYPE:ret = e_INI_LINE_TYPE_DATALESS,
  380. pos;
  381. INI_SkipWhitespace(str, pos);
  382. switch (str[pos++])
  383. {
  384. case '\0': return e_INI_LINE_TYPE_DATALESS;
  385. case ';': goto state_in_comment;
  386. case '[': goto state_in_tag;
  387. case '\\':
  388. if (str[pos])
  389. {
  390. p0s = pos - 1,
  391. end = ++pos;
  392. }
  393. else return e_INI_LINE_TYPE_INVALID;
  394. case '=': return e_INI_LINE_TYPE_INVALID;
  395. default: p0s = pos - 1;
  396. }
  397. // Default end point, for single-character lines.
  398. end = pos;
  399. //state_in_entry: // Default (fall-through).
  400. // Get the key.
  401. for ( ; ; )
  402. {
  403. switch (str[pos++])
  404. {
  405. case '\0', ';':
  406. return e_INI_LINE_TYPE_INVALID; // No value.
  407. case '\\':
  408. if (str[pos]) end = ++pos; // Skip next character too.
  409. else return e_INI_LINE_TYPE_INVALID; // Escaping nothing.
  410. case '=': break;
  411. case '\1' .. ' ': {} // Whitespace, skip it.
  412. default: end = pos; // Characters, save this position.
  413. }
  414. }
  415. p0e = end;
  416. // See what comes next.
  417. INI_SkipWhitespace(str, pos);
  418. switch (str[pos])
  419. {
  420. case '\0': return p1s = p1e = pos, e_INI_LINE_TYPE_ENTRY;
  421. case ';':
  422. {
  423. p1s = p1e = pos++,
  424. ret = e_INI_LINE_TYPE_ENTRY;
  425. goto state_in_comment;
  426. }
  427. case '\\':
  428. if (str[pos + 1]) p1s = pos++, end = ++pos;
  429. else return e_INI_LINE_TYPE_INVALID;
  430. default: p1s = pos++, end = pos;
  431. }
  432. for ( ; ; )
  433. {
  434. switch (str[pos++])
  435. {
  436. case '\0': return p1e = end, e_INI_LINE_TYPE_ENTRY;
  437. case ';':
  438. {
  439. p1e = end,
  440. ret = e_INI_LINE_TYPE_ENTRY;
  441. goto state_in_comment;
  442. }
  443. case '\\':
  444. if (str[pos]) end = ++pos; // Skip next character too.
  445. else return e_INI_LINE_TYPE_INVALID; // Escaping nothing.
  446. case '\1' .. ' ': {} // Whitespace, skip it.
  447. default: end = pos; // Characters, save this position.
  448. }
  449. }
  450. state_in_tag:
  451. // Get the tag name.
  452. INI_SkipWhitespace(str, pos);
  453. p0s = pos;
  454. for ( ; ; )
  455. {
  456. switch (str[pos++])
  457. {
  458. case '\0', ';':
  459. return e_INI_LINE_TYPE_INVALID; // No tag end.
  460. case '\\':
  461. if (str[pos]) end = ++pos; // Skip next character too.
  462. else return e_INI_LINE_TYPE_INVALID; // Escaping nothing.
  463. case ']':
  464. if (end) break; // End of the tag.
  465. else return e_INI_LINE_TYPE_INVALID; // Tag is empty.
  466. case '\1' .. ' ': {} // Whitespace, skip it.
  467. default: end = pos; // Characters, save this position.
  468. }
  469. }
  470. p0e = end;
  471. // See what comes next.
  472. INI_SkipWhitespace(str, pos);
  473. switch (str[pos++])
  474. {
  475. case '\0': return e_INI_LINE_TYPE_TAG; // Line over.
  476. case ';':
  477. {
  478. // Skip over the comments.
  479. ret = e_INI_LINE_TYPE_TAG;
  480. goto state_in_comment;
  481. }
  482. case ':': {}
  483. default : return e_INI_LINE_TYPE_INVALID; // Unexpected characters.
  484. }
  485. // Get the inheritance.
  486. INI_SkipWhitespace(str, pos);
  487. if (!str[pos]) return e_INI_LINE_TYPE_INVALID; // No parent tag.
  488. p1s = pos;
  489. while (str[pos] > ' ') ++pos;
  490. p1e = pos;
  491. INI_SkipWhitespace(str, pos);
  492. switch (str[pos++])
  493. {
  494. case '\0': return e_INI_LINE_TYPE_TAG; // Line over.
  495. case ';': ret = e_INI_LINE_TYPE_TAG;
  496. default : return e_INI_LINE_TYPE_INVALID; // Unexpected characters.
  497. }
  498. state_in_comment:
  499. INI_SkipWhitespace(str, pos);
  500. if (str[pos])
  501. {
  502. p2s = pos,
  503. // Non-empty comment.
  504. pos = strlen(str);
  505. while (pos-- && str[pos] <= ' ') {}
  506. p2e = pos + 1;
  507. }
  508. return ret;
  509. }
  510. /*
  511. 88 88
  512. 88 ,d 88
  513. 88 88 88
  514. 88 8b,dPPYba, MM88MMM ,adPPYba, 8b,dPPYba, 8b,dPPYba, ,adPPYYba, 88
  515. 88 88P' `"8a 88 a8P_____88 88P' "Y8 88P' `"8a "" `Y8 88
  516. 88 88 88 88 8PP""""""" 88 88 88 ,adPPPPP88 88
  517. 88 88 88 88, "8b, ,aa 88 88 88 88, ,88 88
  518. 88 88 88 "Y888 `"Ybbd8"' 88 88 88 `"8bbdP"Y8 88
  519. */
  520. /*-------------------------------------------------------------------------*//**
  521. * <param name="fmat">The format destination.</param>
  522. * <param name="remoteFormat">The source format.</param>
  523. * <param name="filename">The file we are currently parsing.</param>
  524. * <param name="bFileFirst">The format parameter ordering.</param>
  525. * <remarks>
  526. * Generates a partial function name for processing callbacks. Includes the
  527. * filename and a placeholder for the tag name. This now takes extra
  528. * characters in to account and strips or converts bits:
  529. *
  530. * some/dir/file name.ext
  531. *
  532. * Becomes:
  533. *
  534. * file_name
  535. *
  536. * Before being formatted in to the specified remote format. The filename
  537. * also takes in to account "/" directory separators and "\\" ones on Windows.
  538. *
  539. * Because the majority of this function is concerned with formatting just part
  540. * of the function name correctly, it short-circuits if it detects that there
  541. * is no place for the function name to go.
  542. *
  543. * This is quite a complex function, but is only called once per file parse.
  544. * </remarks>
  545. *//*------------------------------------------------------------------------**/
  546. _Y_INI_STATIC stock INI_SetupCallbackName(fmat[32], const remoteFormat[], filename[], const bool:bFileFirst)
  547. {
  548. // Copy the basic format over.
  549. strcpy(fmat, remoteFormat);
  550. // Before any complex filename parsing, check if we actually need it.
  551. new
  552. fpos = strfind(remoteFormat, "%s");
  553. if (!bFileFirst) fpos = strfind(remoteFormat, "%s", false, fpos + 1);
  554. // Is there a position in "remoteFormat" for "filename"?
  555. if (fpos == -1) return 0;
  556. new
  557. pos,
  558. prev = 0;
  559. // Isolate just the VALID name of the file, not the path or extension.
  560. while ((pos = strfind(filename, "/" , false, pos) + 1)) prev = pos;
  561. pos = prev;
  562. if (IsWindows())
  563. {
  564. while ((pos = strfind(filename, "\\", false, pos) + 1)) prev = pos;
  565. pos = prev;
  566. }
  567. // Got the start of the name, now find the end.
  568. for (new ch; ; )
  569. {
  570. // Get the extent of the valid function characters.
  571. for ( ; ; )
  572. {
  573. ch = filename[pos];
  574. if ('a' <= ch <= 'z' || 'A' <= ch <= 'Z' || '0' <= ch <= '9' || ch == '_' || ch == '@') ++pos;
  575. else break;
  576. }
  577. // Make the secondary format.
  578. filename[pos] = '\0';
  579. if (bFileFirst) format(fmat, sizeof (fmat), fmat, filename[prev], "%s");
  580. else format(fmat, sizeof (fmat), fmat, "%s", filename[prev]);
  581. filename[pos] = ch;
  582. // Add spaces as "_".
  583. P:7("INI_SetupCallbackName: '%02x' \"%s\" -> \"%s\" %d %d %d", ch, filename[prev], fmat, fpos, prev, pos);
  584. if (ch == ' ')
  585. {
  586. fpos += pos - prev,
  587. strins(fmat, "_%s", fpos++),
  588. prev = ++pos;
  589. P:7("INI_SetupCallbackName: \"%s\" -> \"%s\" %d %d %d", filename[prev], fmat, fpos, prev, pos);
  590. }
  591. else break;
  592. }
  593. return 1;
  594. }
  595. _Y_INI_STATIC stock INI_MakeCallbackFormat(const bool:bExtra, const bool:bPassTag, callbackFormat[5])
  596. {
  597. if (bExtra)
  598. {
  599. if (bPassTag) callbackFormat = _F<isss>;
  600. else callbackFormat = _F<iss>;
  601. }
  602. else
  603. {
  604. if (bPassTag) callbackFormat = _F<sss>;
  605. else callbackFormat = _F<ss>;
  606. }
  607. }
  608. /*-------------------------------------------------------------------------*//**
  609. * <param name="filename">The file to load.</param>
  610. * <param name="remoteFormat">The format string to generate the remote function
  611. * t pass the data to once loaded.</param>
  612. * <param name="bFileFirst">The order of the remoteFormat parameters.</param>
  613. * <param name="bExtra">Send additional data.</param>
  614. * <param name="extra">Additional data to send.</param>
  615. * <param name="bLocal">Call local functions instead of global ones.</param>
  616. * <param name="bPassTag">Pass the tag as an extra parameter not the function
  617. * name.</param>
  618. * <param name="bFilter">Apply the tag name filter to all tags or just prefixed
  619. * ones?</param>
  620. * <param name="filter">Text to use to search for which tags to load.</param>
  621. * <remarks>
  622. * bFileFirst sets the order and inclusion of the possible remoteFormat
  623. * parameters. If true the format will add the filename first then the
  624. * current tag, if false the order will be reversed. This can also be used
  625. * to exclude one or the other from the function name by setting the required
  626. * parameter to be entered first and then only having one %s in the format
  627. * sting. The default order is tag first for languages compatibility.
  628. * </remarks>
  629. *//*------------------------------------------------------------------------**/
  630. static stock INI_DoParentTag(const epos, const search[], File:f, callback[E_CALLBACK_DATA], bool:bExtra, extra, bool:bPassTag, tag[], bool:bFilter, filter[])
  631. {
  632. // The bulk of this function is basically the same as INI_ParseFile (which
  633. // is a shame as it is a symptom of poor, unmaintainable code).
  634. fseek(f, 0, seek_start); // Jump back to the start.
  635. P:4("bool:INI_DoParentTag called: \"%s\", %i, %i, %i, %i, \"%s\"", search, bExtra, extra, bPassTag, bFilter, filter);
  636. new
  637. ppos, bool:handle = false,
  638. p0s, p0e, p1s, p1e, p2s, p2e;
  639. while ((ppos += fread(f, YSI_g_sCurLine)) < epos)
  640. {
  641. switch (INI_IdentifyLineType(YSI_g_sCurLine, p0s, p0e, p1s, p1e, p2s, p2e))
  642. {
  643. case e_INI_LINE_TYPE_INVALID, e_INI_LINE_TYPE_DATALESS: {}
  644. case e_INI_LINE_TYPE_TAG:
  645. {
  646. // A new tag.
  647. // First, check if it is a tag we might care about.
  648. YSI_g_sCurLine[p0e] = '\0';
  649. if (YSI_g_sCurLine[p0s] == '@' && YSI_g_sCurLine[p0s + 1] == '@' && (p0e = strfind(YSI_g_sCurLine, "-")) != -1)
  650. {
  651. // Check if the current tag is one of the ones we want to
  652. // filer for. The "@@" prefix is the "filterable" prefix.
  653. // If there is no filter then everything will be loaded.
  654. if (p0e == p0s + 2 || !YSI_g_sCurLine[p0e + 1]) continue;
  655. YSI_g_sCurLine[p0e] = '\0';
  656. if (bFilter && strcmp(YSI_g_sCurLine[p0s + 2], filter))
  657. {
  658. // Only skip this if filtering is enabled. We can't put
  659. // the "bFilter" check higher or the "-" won't be found
  660. // to resolve the tag name accurately.
  661. continue;
  662. }
  663. p0s = p0e + 1;
  664. }
  665. if (!strcmp(YSI_g_sCurLine[p0s], search))
  666. {
  667. handle = true;
  668. if (p1s != p1e)
  669. {
  670. new
  671. parent[32];
  672. YSI_g_sCurLine[p1e] = '\0',
  673. strcat(parent, YSI_g_sCurLine[p1s]);
  674. if (!INI_DoParentTag(ppos, parent, f, callback, bExtra, extra, bPassTag, tag, bFilter, filter)) return false;
  675. fseek(f, ppos, seek_start);
  676. }
  677. }
  678. else if (handle)
  679. {
  680. // Parent tag over.
  681. return true;
  682. }
  683. }
  684. default: // e_INI_LINE_TYPE_ENTRY and e_INI_LINE_TYPE_CONT.
  685. {
  686. if (!handle) continue;
  687. // Standard key-value pair.
  688. YSI_g_sCurLine[p0e] = YSI_g_sCurLine[p1e] = '\0';
  689. if (bExtra)
  690. {
  691. if (bPassTag) Callback_Call(callback, extra, tag, YSI_g_sCurLine[p0s], YSI_g_sCurLine[p1s]);
  692. else Callback_Call(callback, extra, YSI_g_sCurLine[p0s], YSI_g_sCurLine[p1s]);
  693. }
  694. else
  695. {
  696. if (bPassTag) Callback_Call(callback, tag, YSI_g_sCurLine[p0s], YSI_g_sCurLine[p1s]);
  697. else Callback_Call(callback, YSI_g_sCurLine[p0s], YSI_g_sCurLine[p1s]);
  698. }
  699. }
  700. }
  701. }
  702. // Return to where we were before.
  703. return handle;
  704. }