impl.inc 27 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834
  1. /*
  2. ad88888ba
  3. d8" "8b ,d
  4. Y8, 88
  5. `Y8aaaaa, ,adPPYba, MM88MMM 88 88 8b,dPPYba,
  6. `"""""8b, a8P_____88 88 88 88 88P' "8a
  7. `8b 8PP""""""" 88 88 88 88 d8
  8. Y8a a8P "8b, ,aa 88, "8a, ,a88 88b, ,a8"
  9. "Y88888P" `"Ybbd8"' "Y888 `"YbbdP'Y8 88`YbbdP"'
  10. 88
  11. 88
  12. */
  13. stock const
  14. INI:INI_NO_FILE = INI:-1;
  15. static stock const
  16. INI_HEADER_SIZE = 0;
  17. #define INI_FILE_NAME_LENGTH (64)
  18. // Load the file for reading and writing. Should now support variable access.
  19. enum E_INI_FILE
  20. {
  21. E_INI_FILE_NAME[INI_FILE_NAME_LENGTH char],
  22. //E_INI_FILE_HANDLE,
  23. E_INI_FILE_LENGTH,
  24. E_INI_FILE_ALLOCATED,
  25. Alloc:E_INI_FILE_DATA
  26. }
  27. enum e_INI_LINE_TYPE
  28. {
  29. e_INI_LINE_TYPE_INVALID,
  30. e_INI_LINE_TYPE_DATALESS,
  31. e_INI_LINE_TYPE_ENTRY,
  32. e_INI_LINE_TYPE_CONT,
  33. e_INI_LINE_TYPE_TAG
  34. }
  35. _Y_INI_STATIC stock
  36. YSI_g_sINIFiles[INI:2][E_INI_FILE],
  37. YSI_g_sOneLine[YSI_MAX_STRING];
  38. /*
  39. 88b d88
  40. 888b d888
  41. 88`8b d8'88
  42. 88 `8b d8' 88 ,adPPYYba, ,adPPYba, 8b,dPPYba, ,adPPYba, ,adPPYba,
  43. 88 `8b d8' 88 "" `Y8 a8" "" 88P' "Y8 a8" "8a I8[ ""
  44. 88 `8b d8' 88 ,adPPPPP88 8b 88 8b d8 `"Y8ba,
  45. 88 `888' 88 88, ,88 "8a, ,aa 88 "8a, ,a8" aa ]8I
  46. 88 `8' 88 `"8bbdP"Y8 `"Ybbd8"' 88 `"YbbdP"' `"YbbdP"'
  47. */
  48. #define INI_Int(%1,%2) \
  49. if (!strcmp((%1), name, true)) return %2 = strval(value)
  50. #define INI_Float(%1,%2) \
  51. if (!strcmp((%1), name, true)) return _:(%2 = floatstr(value))
  52. #define INI_Hex(%1,%2) \
  53. if (!strcmp((%1), name, true)) return %2 = hexstr(value)
  54. #define INI_Bin(%1,%2) \
  55. if (!strcmp((%1), name, true)) return %2 = binstr(value)
  56. #define INI_Bool(%1,%2) \
  57. if (!strcmp((%1), name, true)) return %2 = boolstr(value)
  58. #define INI_String(%1,%2) \
  59. if (!strcmp((%1), name, true)) return _:INI_CHECK_LEN:strcpy(%2, value)
  60. // The old version of "INI_String" didn't like not having a length. Correct
  61. // this. It gave a very odd error message too.
  62. #define INI_CHECK_LEN:strcpy(%0,%1,%2) strcpy(%0,%2,%1)
  63. // Basic checks to see if extension parts exist for INI tags, and if comments
  64. // exist for any lines.
  65. // #define INI_HAS_INHERITANCE_PART (p1s != p1e)
  66. // #define INI_HAS_COMMENT_PART (p2s != p2e)
  67. /*
  68. 88 88 88 88
  69. 88 88 ,d "" 88
  70. 88 88 88 88
  71. 88 88 MM88MMM 88 88 ,adPPYba,
  72. 88 88 88 88 88 I8[ ""
  73. 88 88 88 88 88 `"Y8ba,
  74. Y8a. .a8P 88, 88 88 aa ]8I
  75. `"Y8888Y"' "Y888 88 88 `"YbbdP"'
  76. */
  77. // Is the current index the start of a line?
  78. _Y_INI_STATIC stock bool:INI_AtStartOfLine(const str[], index)
  79. {
  80. for (new ch; index--; )
  81. {
  82. if ((ch = str[index]) > ' ') return false;
  83. if (ch == '\r' || ch == '\n') return true;
  84. }
  85. return true;
  86. }
  87. /**--------------------------------------------------------------------------**\
  88. <summary>INI_IsEscapeSequence</summary>
  89. <param name="str[]">String with the character in.</param>
  90. <param name="pos">Location of the character.</param>
  91. <returns>
  92. Is the current character escaped?
  93. </returns>
  94. <remarks>
  95. -
  96. </remarks>
  97. \**--------------------------------------------------------------------------**/
  98. /*_Y_INI_STATIC*/ stock bool:INI_IsEscapeSequence(const str[], pos)
  99. {
  100. new
  101. bool:escape = false;
  102. // Invert for every sequential escape character.
  103. while (pos && str[--pos] == '\\') escape ^= true;
  104. return escape;
  105. }
  106. /**--------------------------------------------------------------------------**\
  107. <summary>INI_ReverseWhitespace</summary>
  108. <param name="str[]">String with the whitespace in.</param>
  109. <param name="pos">End of the whitespace.</param>
  110. <returns>
  111. Start of the whitespace.
  112. </returns>
  113. <remarks>
  114. -
  115. </remarks>
  116. \**--------------------------------------------------------------------------**/
  117. /*_Y_INI_STATIC*/ stock INI_ReverseWhitespace(const str[], pos)
  118. {
  119. while (pos-- && '\0' < str[pos] <= ' ') {}
  120. return pos + 1;
  121. }
  122. /**--------------------------------------------------------------------------**\
  123. <summary>INI_FindString</summary>
  124. <param name="str[]">The string you want to search in.</param>
  125. <param name="sub[]">The string you want to search for.</param>
  126. <param name="pos">The start offset.</param>
  127. <returns>
  128. Position when found, "cellmax - 1" on fail.
  129. </returns>
  130. <remarks>
  131. Uses "cellmax" not "-1" as a failure return as it is easier to utilise in
  132. later code (it is only used as an upper-bound on line positions). This is
  133. similar to "strfind", but accounts for escape sequences.
  134. </remarks>
  135. \**--------------------------------------------------------------------------**/
  136. /*_Y_INI_STATIC*/ stock INI_FindString(const str[], const sub[], pos = -1)
  137. {
  138. // Determine if there is a substring in this string (can be escaped).
  139. do
  140. {
  141. // Find the next possible candidate for starting a comment.
  142. pos = strfind(str, sub, false, pos + 1);
  143. if (pos == -1) return cellmax;
  144. }
  145. while (INI_IsEscapeSequence(str, pos));
  146. return pos;
  147. }
  148. /*
  149. 88888888888
  150. 88 ,d
  151. 88 88
  152. 88aaaaa ,adPPYYba, ,adPPYba, MM88MMM
  153. 88""""" "" `Y8 I8[ "" 88
  154. 88 ,adPPPPP88 `"Y8ba, 88
  155. 88 88, ,88 aa ]8I 88,
  156. 88 `"8bbdP"Y8 `"YbbdP"' "Y888
  157. */
  158. //INI_FindEntry(const data[], const tag[]
  159. // stock bool:INI_ParseFile(fname[], remoteFormat[], bool:bFileFirst = false, bool:bExtra = false, extra = 0, bool:bLocal = true, bool:bPassTag = false, bool:bFilter = true, filter[] = "")
  160. // {
  161. // new
  162. // INI:ini = INI:0;
  163. // if (ini == INI_NO_FILE) return false;
  164. // INI_SetupParse(fname, YSI_g_sINIFiles[ini], false);
  165. // }
  166. // _Y_INI_STATIC stock bool:INI_SetupParse(fname[], ini, bool:allocate)
  167. // {
  168. // // Get an empty info slot.
  169. // // Open the file.
  170. // new
  171. // File:f = fopen(fname);
  172. // if (!f) return false;
  173. // // Save the filename.
  174. // strpack(ini[E_INI_FILE_NAME], fname, INI_FILE_NAME_LENGTH);
  175. // new
  176. // // Get the filesize.
  177. // len = flength(f),
  178. // ini[E_INI_FILE_LENGTH] = len;
  179. // ini[E_INI_FILE_ALLOCATED] = len * 2 + INI_HEADER_SIZE;
  180. // if (allocate)
  181. // {
  182. // new
  183. // // Allocate the memory.
  184. // Alloc:a = malloc(len * 2 + INI_HEADER_SIZE);
  185. // ini[E_INI_FILE_DATA] = a;
  186. // if (a == NO_ALLOC)
  187. // {
  188. // Alloc:a = malloc(len + INI_HEADER_SIZE);
  189. // ini[E_INI_FILE_ALLOCATED] = len + INI_HEADER_SIZE;
  190. // ini[E_INI_FILE_DATA] = a;
  191. // if (a == NO_ALLOC)
  192. // {
  193. // P:W("Could not allocate memory for INI file - using (slow) fallback.");
  194. // INI_OldLoad(f);
  195. // fclose(file);
  196. // return true;
  197. // }
  198. // P:W("Could not allocate extra memory for INI file - writes may be slow.");
  199. // }
  200. // }
  201. // else
  202. // {
  203. // ini[E_INI_FILE_ALLOCATED] = 0;
  204. // ini[E_INI_FILE_DATA] = NO_ALLOC;
  205. // }
  206. // INI_NewLoad(f, ini);
  207. // fclose(file);
  208. // return true;
  209. // }
  210. // _Y_INI_STATIC stock INI_NewLoad(File:file, INI:ini) // Alloc:data)
  211. // {
  212. // new
  213. // Alloc:data = ini[E_INI_FILE_DATA];
  214. // // Don't make assumptions here. It may be that it doesn't WANT saving.
  215. // if (data == NO_ALLOC) INI_ParseFileCalled(file);
  216. // // We have now loaded the file in to memory.
  217. // else INI_ParseFileSaved(file, data);
  218. // }
  219. // _Y_INI_STATIC stock INI_ParseFileCalled(File:file, INI:ini)
  220. // {
  221. // // Load the data into a line buffer.
  222. // while (fread(file, YSI_g_sOneLine))
  223. // {
  224. // // Parse the line for callbacks.
  225. // //INI_ParseLine(YSI_g_sOneLine, some, callback, data);
  226. // }
  227. // }
  228. // _Y_INI_STATIC stock INI_ParseFileSaved(File:file, INI:ini) //, Alloc:data)
  229. // {
  230. // // Load the data into the allocated slot.
  231. // // Read the whole file, accounting for unicode characters.
  232. // new
  233. // pos = INI_HEADER_SIZE,
  234. // read;
  235. // // We don't check the length because it must be enough.
  236. // while ((read = fread(file, mget(data, pos), cellmax)))
  237. // {
  238. // // Parse the line for callbacks.
  239. // //INI_ParseLine(mget(data, pos), some, callback, data);
  240. // pos += read;
  241. // }
  242. // }
  243. /*
  244. ad88888ba 88
  245. d8" "8b 88
  246. Y8, 88
  247. `Y8aaaaa, 88 ,adPPYba, 8b db d8
  248. `"""""8b, 88 a8" "8a `8b d88b d8'
  249. `8b 88 8b d8 `8b d8'`8b d8'
  250. Y8a a8P 88 "8a, ,a8" `8bd8' `8bd8'
  251. "Y88888P" 88 `"YbbdP"' YP YP
  252. */
  253. /*
  254. 88 88
  255. 88 ,d 88
  256. 88 88 88
  257. 88 8b,dPPYba, MM88MMM ,adPPYba, 8b,dPPYba, 8b,dPPYba, ,adPPYYba, 88
  258. 88 88P' `"8a 88 a8P_____88 88P' "Y8 88P' `"8a "" `Y8 88
  259. 88 88 88 88 8PP""""""" 88 88 88 ,adPPPPP88 88
  260. 88 88 88 88, "8b, ,aa 88 88 88 88, ,88 88
  261. 88 88 88 "Y888 `"Ybbd8"' 88 88 88 `"8bbdP"Y8 88
  262. */
  263. /**--------------------------------------------------------------------------**\
  264. <summary>INI_GetEntryText</summary>
  265. <param name="str[]">The string you want to type analyse.</param>
  266. <param name="p0s">Start of part 0.</param>
  267. <param name="p0e">End of part 0.</param>
  268. <param name="p1s">Start of part 1.</param>
  269. <param name="p1e">End of part 1.</param>
  270. <param name="p2s">Start of part 2.</param>
  271. <param name="p2e">End of part 2.</param>
  272. <param name="cont">Is this a line continuation?</param>
  273. <returns>
  274. e_INI_LINE_TYPE
  275. </returns>
  276. <remarks>
  277. This function's signature is so long that I put it on a separate line. This
  278. takes a line and determines what it is and where the parts are.
  279. </remarks>
  280. \**--------------------------------------------------------------------------**/
  281. #define INI_SkipWhitespace(%0,%1) while ('\0' < %0[%1] <= ' ') ++%1
  282. _Y_INI_STATIC stock e_INI_LINE_TYPE:
  283. INI_IdentifyLineType(const str[], &p0s, &p0e, &p1s, &p1e, &p2s, &p2e)
  284. {
  285. // Reset everything.
  286. p0s = p0e = p1s = p1e = p2s = p2e = 0;
  287. // Do this purely with a single main loop, and a state machine.
  288. new
  289. end,
  290. e_INI_LINE_TYPE:ret = e_INI_LINE_TYPE_DATALESS,
  291. pos;
  292. INI_SkipWhitespace(str, pos);
  293. switch (str[pos++])
  294. {
  295. case '\0': return e_INI_LINE_TYPE_DATALESS;
  296. case ';': goto state_in_comment;
  297. case '[': goto state_in_tag;
  298. case '\\':
  299. if (str[pos])
  300. {
  301. p0s = pos - 1,
  302. end = ++pos;
  303. }
  304. else return e_INI_LINE_TYPE_INVALID;
  305. case '=': return e_INI_LINE_TYPE_INVALID;
  306. default: p0s = pos - 1;
  307. }
  308. //state_in_entry: // Default (fall-through).
  309. // Get the key.
  310. for ( ; ; )
  311. {
  312. switch (str[pos++])
  313. {
  314. case '\0', ';':
  315. return e_INI_LINE_TYPE_INVALID; // No value.
  316. case '\\':
  317. if (str[pos]) end = ++pos; // Skip next character too.
  318. else return e_INI_LINE_TYPE_INVALID; // Escaping nothing.
  319. case '=': break;
  320. case '\1' .. ' ': {} // Whitespace, skip it.
  321. default: end = pos; // Characters, save this position.
  322. }
  323. }
  324. p0e = end;
  325. // See what comes next.
  326. INI_SkipWhitespace(str, pos);
  327. switch (str[pos])
  328. {
  329. case '\0', ';': return e_INI_LINE_TYPE_INVALID;
  330. case '\\':
  331. if (str[pos + 1]) p1s = pos++, end = ++pos;
  332. else return e_INI_LINE_TYPE_INVALID;
  333. default: p1s = pos++, end = pos;
  334. }
  335. for ( ; ; )
  336. {
  337. switch (str[pos++])
  338. {
  339. case '\0': return p1e = end, e_INI_LINE_TYPE_ENTRY;
  340. case ';':
  341. {
  342. p1e = end,
  343. ret = e_INI_LINE_TYPE_ENTRY;
  344. goto state_in_comment;
  345. }
  346. case '\\':
  347. if (str[pos]) end = ++pos; // Skip next character too.
  348. else return e_INI_LINE_TYPE_INVALID; // Escaping nothing.
  349. case '\1' .. ' ': {} // Whitespace, skip it.
  350. default: end = pos; // Characters, save this position.
  351. }
  352. }
  353. state_in_tag:
  354. // Get the tag name.
  355. INI_SkipWhitespace(str, pos);
  356. p0s = pos;
  357. for ( ; ; )
  358. {
  359. switch (str[pos++])
  360. {
  361. case '\0', ';':
  362. return e_INI_LINE_TYPE_INVALID; // No tag end.
  363. case '\\':
  364. if (str[pos]) end = ++pos; // Skip next character too.
  365. else return e_INI_LINE_TYPE_INVALID; // Escaping nothing.
  366. case ']':
  367. if (end) break; // End of the tag.
  368. else return e_INI_LINE_TYPE_INVALID; // Tag is empty.
  369. case '\1' .. ' ': {} // Whitespace, skip it.
  370. default: end = pos; // Characters, save this position.
  371. }
  372. }
  373. p0e = end;
  374. // See what comes next.
  375. INI_SkipWhitespace(str, pos);
  376. switch (str[pos++])
  377. {
  378. case '\0': return e_INI_LINE_TYPE_TAG; // Line over.
  379. case ';':
  380. {
  381. // Skip over the comments.
  382. ret = e_INI_LINE_TYPE_TAG;
  383. goto state_in_comment;
  384. }
  385. case ':': {}
  386. default : return e_INI_LINE_TYPE_INVALID; // Unexpected characters.
  387. }
  388. // Get the inheritance.
  389. INI_SkipWhitespace(str, pos);
  390. if (!str[pos]) return e_INI_LINE_TYPE_INVALID; // No parent tag.
  391. p1s = pos;
  392. while (str[pos] > ' ') ++pos;
  393. p1e = pos;
  394. INI_SkipWhitespace(str, pos);
  395. switch (str[pos++])
  396. {
  397. case '\0': return e_INI_LINE_TYPE_TAG; // Line over.
  398. case ';': ret = e_INI_LINE_TYPE_TAG;
  399. default : return e_INI_LINE_TYPE_INVALID; // Unexpected characters.
  400. }
  401. state_in_comment:
  402. INI_SkipWhitespace(str, pos);
  403. if (str[pos])
  404. {
  405. p2s = pos,
  406. // Non-empty comment.
  407. pos = strlen(str);
  408. while (pos-- && str[pos] <= ' ') {}
  409. p2e = pos + 1;
  410. }
  411. return ret;
  412. }
  413. /**--------------------------------------------------------------------------**\
  414. <summary>INI_SetupCallbackName</summary>
  415. <param name="fmat[]">The format destination.</param>
  416. <param name="const remoteFormat[]">The source format.</param>
  417. <param name="filename[]">The file we are currently parsing.</param>
  418. <param name="const bool:bFileFirst">The format parameter ordering.</param>
  419. <returns>
  420. -
  421. </returns>
  422. <remarks>
  423. Generates a partial function name for processing callbacks. Includes the
  424. filename and a placeholder for the tag name. This now takes extra
  425. characters in to account and strips or converts bits:
  426. some/dir/file name.ext
  427. Becomes:
  428. file_name
  429. Before being formatted in to the specified remote format. The filename
  430. also takes in to account "/" directory separators and "\\" ones on Windows.
  431. Because the majority of this function is concerned with formatting just part
  432. of the function name correctly, it short-circuits if it detects that there
  433. is no place for the function name to go.
  434. This is quite a complex function, but is only called once per file parse.
  435. </remarks>
  436. \**--------------------------------------------------------------------------**/
  437. _Y_INI_STATIC stock INI_SetupCallbackName(fmat[32], const remoteFormat[], filename[], const bool:bFileFirst)
  438. {
  439. // Copy the basic format over.
  440. strcpy(fmat, remoteFormat);
  441. // Before any complex filename parsing, check if we actually need it.
  442. new
  443. fpos = strfind(remoteFormat, "%s");
  444. if (!bFileFirst) fpos = strfind(remoteFormat, "%s", false, fpos + 1);
  445. // Is there a position in "remoteFormat" for "filename"?
  446. if (fpos == -1) return 0;
  447. new
  448. pos,
  449. prev = 0;
  450. // Isolate just the VALID name of the file, not the path or extension.
  451. while ((pos = strfind(filename, "/" , false, pos) + 1)) prev = pos;
  452. pos = prev;
  453. if (IsWindows())
  454. {
  455. while ((pos = strfind(filename, "\\", false, pos) + 1)) prev = pos;
  456. pos = prev;
  457. }
  458. // Got the start of the name, now find the end.
  459. for (new ch; ; )
  460. {
  461. // Get the extent of the valid function characters.
  462. for ( ; ; )
  463. {
  464. ch = filename[pos];
  465. if ('a' <= ch <= 'z' || 'A' <= ch <= 'Z' || '0' <= ch <= '9' || ch == '_' || ch == '@') ++pos;
  466. else break;
  467. }
  468. // Make the secondary format.
  469. filename[pos] = '\0';
  470. if (bFileFirst) format(fmat, sizeof (fmat), fmat, filename[prev], "%s");
  471. else format(fmat, sizeof (fmat), fmat, "%s", filename[prev]);
  472. filename[pos] = ch;
  473. // Add spaces as "_".
  474. P:7("INI_SetupCallbackName: '%02x' \"%s\" -> \"%s\" %d %d %d", ch, filename[prev], fmat, fpos, prev, pos);
  475. if (ch == ' ')
  476. {
  477. fpos += pos - prev,
  478. strins(fmat, "_%s", fpos++),
  479. prev = ++pos;
  480. P:7("INI_SetupCallbackName: \"%s\" -> \"%s\" %d %d %d", filename[prev], fmat, fpos, prev, pos);
  481. }
  482. else break;
  483. }
  484. return 1;
  485. }
  486. /**--------------------------------------------------------------------------**\
  487. <summary>INI_GetCallback</summary>
  488. <param name="callback[E_CALLBACK_DATA]">The callback destination.</param>
  489. <param name="const format[]">The function name format.</param>
  490. <param name="tag[]">The tag destination.</param>
  491. <param name="const input[]">The tag source.</param>
  492. <param name="const len">The tag length.</param>
  493. <param name="tag[]">The tag destination.</param>
  494. <param name="callbackFormat[]">The callback parameter specifiers.</param>
  495. <param name="const bool:remote">Use "CallRemoteFunction".</param>
  496. <returns>
  497. Was the function found?
  498. </returns>
  499. <remarks>
  500. Gets a callback given a partial function name and a tag name. Also saves
  501. the tag elsewhere. This might not work as a separate function - it will
  502. need to be in the function called by the function with the inlines in.
  503. </remarks>
  504. \**--------------------------------------------------------------------------**/
  505. #define INI_GetCallback(%0,%1,%2,%3,%4,%5) \
  506. ( \
  507. strcpy((%2), (%3)), \
  508. Inline_Reset((%0)), \
  509. format(YSI_g_sCurLine, sizeof (YSI_g_sCurLine), (%1), (%2)), \
  510. Callback_Get(callback_tag:YSI_g_sCurLine, (%0), (%4), (%5)) \
  511. )
  512. /**--------------------------------------------------------------------------**\
  513. <summary>INI_ParseFile</summary>
  514. <param name="filename[]">The file to load.</param>
  515. <param name="remoteFormat[]">The format string to generate the remote function
  516. t pass the data to once loaded.</param>
  517. <param name="bool:bFileFirst">The order of the remoteFormat parameters.</param>
  518. <param name="bool:bExtra">Send additional data.</param>
  519. <param name="extra">Additional data to send.</param>
  520. <param name="bLocal">Call local functions instead of global ones.</param>
  521. <param name="bPassTag">Pass the tag as an extra parameter not the function
  522. name.</param>
  523. <param name="bFilter">Apply the tag name filter to all tags or just prefixed
  524. ones?</param>
  525. <param name="filter">Text to use to search for which tags to load.</param>
  526. <returns>
  527. -
  528. </returns>
  529. <remarks>
  530. bFileFirst sets the order and inclusion of the possible remoteFormat
  531. parameters. If true the format will add the filename first then the
  532. current tag, if false the order will be reversed. This can also be used
  533. to exclude one or the other from the function name by setting the required
  534. parameter to be entered first and then only having one %s in the format
  535. sting. The default order is tag first for languages compatibility.
  536. </remarks>
  537. \**--------------------------------------------------------------------------**/
  538. static stock
  539. YSI_g_sCurLine[YSI_MAX_INLINE_STRING];
  540. stock bool:INI_ParseFile(fname[], remoteFormat[], bool:bFileFirst = false, bool:bExtra = false, extra = 0, bool:bLocal = true, bool:bPassTag = false, bool:bFilter = true, filter[] = "")
  541. {
  542. P:3("bool:INI_ParseFile called: \"%s\", \"%s\", %i, %i, %i, %i, %i, %i, \"%s\"", fname, remoteFormat, bFileFirst, bExtra, extra, bLocal, bPassTag, bFilter, filter);
  543. printf("0");
  544. static
  545. callback[E_CALLBACK_DATA],
  546. tag[32], // The current tag being parsed.
  547. cbSpec[5], // The callback specifier.
  548. cbName[32]; // The callback name format.
  549. new
  550. File:f = fopen(fname, io_read);
  551. if (!f) return false;
  552. new
  553. rlen, ppos,
  554. p0s, p0e, p1s, p1e, p2s, p2e;
  555. P:5("INI_ParseFile: Opened.");
  556. bLocal = !bLocal, // Invert for "remote".
  557. printf("0a");
  558. INI_MakeCallbackFormat(bExtra, bPassTag, cbSpec),
  559. printf("0b");
  560. INI_SetupCallbackName(cbName, remoteFormat, fname, bFileFirst);
  561. printf("0c");
  562. new
  563. bool:handle = INI_GetCallback(callback, cbName, tag, "", cbSpec, bLocal);
  564. printf("1");
  565. while ((rlen = fread(f, YSI_g_sCurLine)))
  566. {
  567. printf("2a");
  568. ppos += rlen;
  569. printf("2b");
  570. switch (INI_IdentifyLineType(YSI_g_sCurLine, p0s, p0e, p1s, p1e, p2s, p2e))
  571. {
  572. case e_INI_LINE_TYPE_INVALID:
  573. {
  574. printf("3a");
  575. // "P:I" not "P:W" because this is not a script issue.
  576. P:I("Invalid line in INI file \"%s\": %s", fname, YSI_g_sCurLine);
  577. }
  578. case e_INI_LINE_TYPE_DATALESS:
  579. {
  580. printf("3b");
  581. // Do nothing.
  582. }
  583. case e_INI_LINE_TYPE_TAG:
  584. {
  585. printf("3c");
  586. // A new tag.
  587. Callback_Restore(callback),
  588. Callback_Release(callback),
  589. // First, check if it is a tag we might care about.
  590. YSI_g_sCurLine[p0e] = '\0';
  591. if (YSI_g_sCurLine[p0s] == '@' && YSI_g_sCurLine[p0s + 1] == '@' && (p0e = strfind(YSI_g_sCurLine, "-")) != -1)
  592. {
  593. // Check if the current tag is one of the ones we want to
  594. // filer for. The "@@" prefix is the "filterable" prefix.
  595. // If there is no filter then everything will be loaded.
  596. if (p0e == p0s + 2 || !YSI_g_sCurLine[p0e + 1])
  597. {
  598. P:I("Invalid line in INI file \"%s\": %s", fname, YSI_g_sCurLine);
  599. continue;
  600. }
  601. YSI_g_sCurLine[p0e] = '\0';
  602. if (bFilter && strcmp(YSI_g_sCurLine[p0s + 2], filter))
  603. {
  604. // Only skip this if filtering is enabled. We can't put
  605. // the "bFilter" check higher or the "-" won't be found
  606. // to resolve the tag name accurately.
  607. handle = false;
  608. continue;
  609. }
  610. p0s = p0e + 1;
  611. }
  612. // This is a tag we can use, is there a handler for it?
  613. // Is this based on another tag? If so recurse and do that one
  614. // first.
  615. new
  616. parent[32];
  617. YSI_g_sCurLine[p1e] = '\0', strcat(parent, YSI_g_sCurLine[p1s]);
  618. if ((handle = INI_GetCallback(callback, cbName, tag, YSI_g_sCurLine[p0s], cbSpec, bLocal)) && p1s != p1e)
  619. {
  620. // No point recursing if there's no handler is there?
  621. if (!INI_DoParentTag(ppos, parent, f, callback, bExtra, extra, bPassTag, tag, bFilter, filter))
  622. P:I("Invalid hierarchy in INI file: \"%s\" for tag \"%s\"", fname, tag);
  623. fseek(f, ppos, seek_start);
  624. }
  625. }
  626. default: // e_INI_LINE_TYPE_ENTRY and e_INI_LINE_TYPE_CONT.
  627. {
  628. printf("3d");
  629. if (!handle) continue;
  630. // Standard key-value pair.
  631. YSI_g_sCurLine[p0e] = YSI_g_sCurLine[p1e] = '\0';
  632. if (bExtra)
  633. {
  634. if (bPassTag) Callback_Call(callback, extra, tag, YSI_g_sCurLine[p0s], YSI_g_sCurLine[p1s]);
  635. else Callback_Call(callback, extra, YSI_g_sCurLine[p0s], YSI_g_sCurLine[p1s]);
  636. }
  637. else
  638. {
  639. if (bPassTag) Callback_Call(callback, tag, YSI_g_sCurLine[p0s], YSI_g_sCurLine[p1s]);
  640. else Callback_Call(callback, YSI_g_sCurLine[p0s], YSI_g_sCurLine[p1s]);
  641. }
  642. }
  643. }
  644. printf("4");
  645. // Don't put any code down here (at the end of the loop).
  646. } // while (fread(f, line))
  647. printf("5");
  648. return
  649. Callback_Restore(callback),
  650. Callback_Release(callback),
  651. fclose(f),
  652. true;
  653. }
  654. _Y_INI_STATIC stock INI_MakeCallbackFormat(const bool:bExtra, const bool:bPassTag, callbackFormat[5])
  655. {
  656. if (bExtra)
  657. {
  658. if (bPassTag) callbackFormat = _F<isss>;
  659. else callbackFormat = _F<iss>;
  660. }
  661. else
  662. {
  663. if (bPassTag) callbackFormat = _F<sss>;
  664. else callbackFormat = _F<ss>;
  665. }
  666. }
  667. /**--------------------------------------------------------------------------**\
  668. <summary>INI_DoParentTag</summary>
  669. <param name="filename[]">The file to load.</param>
  670. <param name="remoteFormat[]">The format string to generate the remote function
  671. t pass the data to once loaded.</param>
  672. <param name="bool:bFileFirst">The order of the remoteFormat parameters.</param>
  673. <param name="bool:bExtra">Send additional data.</param>
  674. <param name="extra">Additional data to send.</param>
  675. <param name="bLocal">Call local functions instead of global ones.</param>
  676. <param name="bPassTag">Pass the tag as an extra parameter not the function
  677. name.</param>
  678. <param name="bFilter">Apply the tag name filter to all tags or just prefixed
  679. ones?</param>
  680. <param name="filter">Text to use to search for which tags to load.</param>
  681. <returns>
  682. -
  683. </returns>
  684. <remarks>
  685. bFileFirst sets the order and inclusion of the possible remoteFormat
  686. parameters. If true the format will add the filename first then the
  687. current tag, if false the order will be reversed. This can also be used
  688. to exclude one or the other from the function name by setting the required
  689. parameter to be entered first and then only having one %s in the format
  690. sting. The default order is tag first for languages compatibility.
  691. </remarks>
  692. \**--------------------------------------------------------------------------**/
  693. static stock INI_DoParentTag(const epos, const search[], File:f, callback[E_CALLBACK_DATA], bool:bExtra, extra, bool:bPassTag, tag[], bool:bFilter, filter[])
  694. {
  695. // The bulk of this function is basically the same as INI_ParseFile (which
  696. // is a shame as it is a symptom of poor, unmaintainable code).
  697. fseek(f, 0, seek_start); // Jump back to the start.
  698. P:4("bool:INI_DoParentTag called: \"%s\", %i, %i, %i, %i, \"%s\"", search, bExtra, extra, bPassTag, bFilter, filter);
  699. new
  700. ppos, bool:handle = false,
  701. p0s, p0e, p1s, p1e, p2s, p2e;
  702. while ((ppos += fread(f, YSI_g_sCurLine)) < epos)
  703. {
  704. switch (INI_IdentifyLineType(YSI_g_sCurLine, p0s, p0e, p1s, p1e, p2s, p2e))
  705. {
  706. case e_INI_LINE_TYPE_INVALID, e_INI_LINE_TYPE_DATALESS: {}
  707. case e_INI_LINE_TYPE_TAG:
  708. {
  709. // A new tag.
  710. // First, check if it is a tag we might care about.
  711. YSI_g_sCurLine[p0e] = '\0';
  712. if (YSI_g_sCurLine[p0s] == '@' && YSI_g_sCurLine[p0s + 1] == '@' && (p0e = strfind(YSI_g_sCurLine, "-")) != -1)
  713. {
  714. // Check if the current tag is one of the ones we want to
  715. // filer for. The "@@" prefix is the "filterable" prefix.
  716. // If there is no filter then everything will be loaded.
  717. if (p0e == p0s + 2 || !YSI_g_sCurLine[p0e + 1]) continue;
  718. YSI_g_sCurLine[p0e] = '\0';
  719. if (bFilter && strcmp(YSI_g_sCurLine[p0s + 2], filter))
  720. {
  721. // Only skip this if filtering is enabled. We can't put
  722. // the "bFilter" check higher or the "-" won't be found
  723. // to resolve the tag name accurately.
  724. continue;
  725. }
  726. p0s = p0e + 1;
  727. }
  728. if (!strcmp(YSI_g_sCurLine[p0s], search))
  729. {
  730. handle = true;
  731. if (p1s != p1e)
  732. {
  733. new
  734. parent[32];
  735. YSI_g_sCurLine[p1e] = '\0',
  736. strcat(parent, YSI_g_sCurLine[p1s]);
  737. if (!INI_DoParentTag(ppos, parent, f, callback, bExtra, extra, bPassTag, tag, bFilter, filter)) return false;
  738. fseek(f, ppos, seek_start);
  739. }
  740. }
  741. else if (handle)
  742. {
  743. // Parent tag over.
  744. return true;
  745. }
  746. }
  747. default: // e_INI_LINE_TYPE_ENTRY and e_INI_LINE_TYPE_CONT.
  748. {
  749. if (!handle) continue;
  750. // Standard key-value pair.
  751. YSI_g_sCurLine[p0e] = YSI_g_sCurLine[p1e] = '\0';
  752. if (bExtra)
  753. {
  754. if (bPassTag) Callback_Call(callback, extra, tag, YSI_g_sCurLine[p0s], YSI_g_sCurLine[p1s]);
  755. else Callback_Call(callback, extra, YSI_g_sCurLine[p0s], YSI_g_sCurLine[p1s]);
  756. }
  757. else
  758. {
  759. if (bPassTag) Callback_Call(callback, tag, YSI_g_sCurLine[p0s], YSI_g_sCurLine[p1s]);
  760. else Callback_Call(callback, YSI_g_sCurLine[p0s], YSI_g_sCurLine[p1s]);
  761. }
  762. }
  763. }
  764. }
  765. // Return to where we were before.
  766. return handle;
  767. }