y_xml.inc 31 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097
  1. #if defined _INC_y_xml
  2. #endinput
  3. #endif
  4. #define _INC_y_xml
  5. /**
  6. * <library name="y_xml">
  7. * <section>
  8. * Description
  9. * </section>
  10. * <p>Parses XML files according to a set of defined rules. A rule is a custom
  11. * function called on a tag when all the data for that tag has been collected.
  12. * The data for a tag could consist of simple <c>bla="bla"</c> pairs,
  13. * <c>&lt;tag&gt;data&lt;/tag&gt;</c> pairs or cominations of the two,
  14. * including subtags, each with their own possible custom handlers.</p>
  15. *
  16. * <p>Data for the tag is retrieved from a custom function using only:</p>
  17. *
  18. * <c>while (XML_GetKeyValue(ident, data)) {}</c>
  19. * <section>
  20. * Version
  21. * </section>
  22. * 1.0
  23. * </library>
  24. *//** *//*
  25. Legal:
  26. Version: MPL 1.1
  27. The contents of this file are subject to the Mozilla Public License Version
  28. 1.1 the "License"; you may not use this file except in compliance with
  29. the License. You may obtain a copy of the License at
  30. http://www.mozilla.org/MPL/
  31. Software distributed under the License is distributed on an "AS IS" basis,
  32. WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
  33. for the specific language governing rights and limitations under the
  34. License.
  35. The Original Code is the YSI framework.
  36. The Initial Developer of the Original Code is Alex "Y_Less" Cole.
  37. Portions created by the Initial Developer are Copyright C 2011
  38. the Initial Developer. All Rights Reserved.
  39. Contributors:
  40. Y_Less
  41. koolk
  42. JoeBullet/Google63
  43. g_aSlice/Slice
  44. Misiur
  45. samphunter
  46. tianmeta
  47. maddinat0r
  48. spacemud
  49. Crayder
  50. Dayvison
  51. Ahmad45123
  52. Zeex
  53. irinel1996
  54. Yiin-
  55. Chaprnks
  56. Konstantinos
  57. Masterchen09
  58. Southclaws
  59. PatchwerkQWER
  60. m0k1
  61. paulommu
  62. udan111
  63. Thanks:
  64. JoeBullet/Google63 - Handy arbitrary ASM jump code using SCTRL.
  65. ZeeX - Very productive conversations.
  66. koolk - IsPlayerinAreaEx code.
  67. TheAlpha - Danish translation.
  68. breadfish - German translation.
  69. Fireburn - Dutch translation.
  70. yom - French translation.
  71. 50p - Polish translation.
  72. Zamaroht - Spanish translation.
  73. Los - Portuguese translation.
  74. Dracoblue, sintax, mabako, Xtreme, other coders - Producing other modes for
  75. me to strive to better.
  76. Pixels^ - Running XScripters where the idea was born.
  77. Matite - Pestering me to release it and using it.
  78. Very special thanks to:
  79. Thiadmer - PAWN, whose limits continue to amaze me!
  80. Kye/Kalcor - SA:MP.
  81. SA:MP Team past, present and future - SA:MP.
  82. Optional plugins:
  83. Gamer_Z - GPS.
  84. Incognito - Streamer.
  85. Me - sscanf2, fixes2, Whirlpool.
  86. */
  87. #include "..\YSI_Internal\y_compilerdata"
  88. #include "..\YSI_Internal\y_version"
  89. #include <a_samp>
  90. #include "..\YSI_Core\y_debug"
  91. #include "..\YSI_Core\y_utils"
  92. #include "..\YSI_Internal\y_natives"
  93. #if !defined MAX_XML_FILES
  94. #define MAX_XML_FILES (5)
  95. #endif
  96. #define XML_MAX_XML_FILES (XML:MAX_XML_FILES)
  97. #define NO_XML_FILE (XML:-1)
  98. #define MAX_XML_FUNCTION FUNCTION_LENGTH
  99. #define MAX_XML_ENTRY_NAME (32)
  100. #define MAX_XML_ENTRY_TEXT (80)
  101. #define XML_BUFFER_SIZE (512)
  102. #define MAX_XML_HANDLERS (16)
  103. #if !defined XML_WRITE_BUFFER_SIZE
  104. #define XML_WRITE_BUFFER_SIZE (512)
  105. #endif
  106. #define XML_NO_WRITE (XMLEntry:cellmax)
  107. enum E_XML_PARA
  108. {
  109. E_XML_PARA_NAME[MAX_XML_ENTRY_NAME],
  110. E_XML_PARA_VALUE[MAX_XML_ENTRY_TEXT],
  111. E_XML_PARA_LEVEL
  112. }
  113. enum E_XML_HANDLER
  114. {
  115. E_XML_HANDLER_TRIGGER[MAX_XML_ENTRY_NAME],
  116. E_XML_HANDLER_FUNCTION[MAX_XML_FUNCTION]
  117. }
  118. enum E_XML_WRITE
  119. {
  120. E_XML_WRITE_TAG[MAX_XML_ENTRY_NAME],
  121. E_XML_WRITE_VALUE[MAX_XML_ENTRY_TEXT],
  122. E_XML_WRITE_CHILDREN,
  123. E_XML_WRITE_SIBLINGS
  124. }
  125. static stock
  126. YSI_g_sXMLWriteBuffer[XML_WRITE_BUFFER_SIZE][E_XML_WRITE],
  127. YSI_g_sXMLWritePointer,
  128. YSI_g_sParameters[XML_BUFFER_SIZE][E_XML_PARA],
  129. YSI_g_sHandlers[XML_MAX_XML_FILES][MAX_XML_HANDLERS][E_XML_HANDLER],
  130. YSI_g_sCurHandler[XML_MAX_XML_FILES] = {-1, ...},
  131. YSI_g_sCurBuffer = -1,
  132. YSI_g_sEndTag = 0;
  133. /*-------------------------------------------------------------------------*//**
  134. * <param name="file">File to check validity of.</param>
  135. *//*------------------------------------------------------------------------**/
  136. P:D(bool:XML_IsValid(XML:file));
  137. #define XML_IsValid(%1) \
  138. ((%1) >= XML:0 && (%1) < XML_MAX_XML_FILES && YSI_g_sCurHandler[(%1)] != -1)
  139. /*-------------------------------------------------------------------------*//**
  140. * <param name="ch">Checks if a cell is a valid identifier character.</param>
  141. *//*------------------------------------------------------------------------**/
  142. P:D(bool:XML_IsChar(ch));
  143. #define XML_IsChar(%1) \
  144. (((%1) >= 'a' && (%1) <= 'z') || ((%1) >= 'A' && (%1) <= 'Z') || ((%1) >= '0' && (%1) <= '9') || (%1 == '_'))
  145. /*-------------------------------------------------------------------------*//**
  146. * <returns>
  147. * XML
  148. * </returns>
  149. * <remarks>
  150. * Creates a new set of rules for parsing XML files.
  151. * </remarks>
  152. *//*------------------------------------------------------------------------**/
  153. stock XML:XML_New()
  154. {
  155. P:3("XML:XML_New called");
  156. new
  157. XML:i;
  158. while (i < XML_MAX_XML_FILES && YSI_g_sCurHandler[i] != -1) i++;
  159. if (i == XML_MAX_XML_FILES) return NO_XML_FILE;
  160. YSI_g_sCurHandler[i] = 0;
  161. return i;
  162. }
  163. /*-------------------------------------------------------------------------*//**
  164. * <param name="rule">Removes a set of rules from the system</param>
  165. *//*------------------------------------------------------------------------**/
  166. stock XML_Destroy(XML:rule)
  167. {
  168. P:3("XML_Destroy called: %i", _:rule);
  169. if (!XML_IsValid(rule)) return 0;
  170. YSI_g_sCurBuffer = -1;
  171. YSI_g_sCurHandler[rule] = -1;
  172. return 1;
  173. }
  174. /*-------------------------------------------------------------------------*//**
  175. * <param name="rule">Set of XML rules to parse against.</param>
  176. * <param name="filename">XML file to parse.</param>
  177. * <remarks>
  178. * Now supports self-closing tags and XML comments.
  179. * </remarks>
  180. *//*------------------------------------------------------------------------**/
  181. stock XML_Parse(XML:rule, filename[])
  182. {
  183. P:3("XML_Parse called: %i, \"%s\"", _:rule, filename);
  184. if (!XML_IsValid(rule)) return 0;
  185. YSI_g_sCurBuffer = 0;
  186. new
  187. File:xFile = fopen(filename);
  188. if (xFile)
  189. {
  190. new
  191. line[YSI_MAX_STRING],
  192. tagCount,
  193. gotLastValue,
  194. inClose,
  195. inOpen,
  196. inComment,
  197. lineOffset,
  198. lineLen,
  199. value[MAX_XML_ENTRY_TEXT],
  200. name[MAX_XML_ENTRY_NAME],
  201. inPar;
  202. while ((lineLen = fread(xFile, line[lineOffset], sizeof (line) - lineOffset)))
  203. {
  204. P:5("XML_Parse() line: %s", line);
  205. new
  206. pos,
  207. ch;
  208. lineOffset = 0;
  209. if (inComment)
  210. {
  211. pos = strfind(line, "-->", false, 0);
  212. if (pos == -1)
  213. {
  214. // Skip this whole line.
  215. if (lineLen == YSI_MAX_STRING - 1)
  216. {
  217. // Check that "-->" doesn't span two read-in blocks.
  218. memcpy(line, line[sizeof (line) - 3], 0, 3 * cellbytes);
  219. lineOffset = 2;
  220. }
  221. continue;
  222. }
  223. else
  224. {
  225. pos += 3;
  226. inComment = 0;
  227. }
  228. }
  229. for ( ; ; ) switch ((ch = line[pos]))
  230. {
  231. case '\0':
  232. {
  233. break;
  234. }
  235. case '\1' .. ' ':
  236. {
  237. if (lineLen == YSI_MAX_STRING - 1 && pos > (YSI_MAX_STRING * 2 / 3))
  238. {
  239. // 2/3 of the way through the line and found a space -
  240. // use this as a handy break point for long lines.
  241. ++pos;
  242. lineOffset = sizeof (line) - 1 - pos;
  243. memcpy(line, line[pos], 0, lineOffset * cellbytes);
  244. break;
  245. }
  246. ++pos;
  247. }
  248. case '<':
  249. {
  250. ++pos;
  251. if (line[pos] == '/')
  252. {
  253. ++pos;
  254. tagCount--;
  255. if (gotLastValue)
  256. {
  257. XML_Push(XML_GetName(line, pos), value, tagCount);
  258. }
  259. else
  260. {
  261. name = XML_GetName(line, pos);
  262. value = XML_ParseTag(rule, name, tagCount);
  263. if (value[0] && tagCount > 1)
  264. {
  265. XML_Push(name, value, tagCount);
  266. }
  267. }
  268. inClose = 1;
  269. }
  270. else if (line[pos] == '!' && line[pos + 1] == '-' && line[pos + 2] == '-')
  271. {
  272. // XML comments. XML doesn't support nested comments.
  273. // I am a firm believer that they are the future, but
  274. // this code isn't clever enough to parse them (being
  275. // very old), and since they aren't supported there is
  276. // no need to add them.
  277. pos = strfind(line, "-->", false, pos + 3);
  278. if (pos == -1)
  279. {
  280. inComment = 1;
  281. if (lineLen == YSI_MAX_STRING - 1)
  282. {
  283. memcpy(line, line[sizeof (line) - 3], 0, 3 * cellbytes);
  284. lineOffset = 2;
  285. }
  286. break;
  287. }
  288. else
  289. {
  290. pos += 3;
  291. }
  292. }
  293. else
  294. {
  295. inOpen = 1;
  296. tagCount++;
  297. while ((ch = line[pos]) && XML_IsChar(ch)) ++pos;
  298. //name = XML_GetName(line, pos);
  299. }
  300. gotLastValue = 0;
  301. inPar = 0;
  302. }
  303. case '>':
  304. {
  305. inPar = inClose ? 0 : 1;
  306. inOpen = 0;
  307. inClose = 0;
  308. ++pos;
  309. }
  310. case '/':
  311. {
  312. // Self-closing tags (FINALLY)!
  313. ++pos;
  314. if (inOpen)
  315. {
  316. tagCount--;
  317. inOpen = strfind(line, "<") + 1;
  318. if (inOpen)
  319. {
  320. name = XML_GetName(line, inOpen);
  321. value = XML_ParseTag(rule, name, tagCount);
  322. if (value[0] && tagCount > 1)
  323. {
  324. XML_Push(name, value, tagCount);
  325. }
  326. inOpen = 0;
  327. }
  328. inClose = 1; // Well, we sort of are...
  329. }
  330. }
  331. default:
  332. {
  333. if (inPar)
  334. {
  335. value = XML_GetValue(line, pos);
  336. gotLastValue = 1;
  337. }
  338. else if (inOpen)
  339. {
  340. name = XML_GetName(line, pos);
  341. value = XML_GetParameter(line, pos);
  342. XML_Push(name, value, tagCount);
  343. }
  344. else
  345. {
  346. ++pos;
  347. }
  348. }
  349. }
  350. }
  351. fclose(xFile);
  352. return 1;
  353. }
  354. return 0;
  355. }
  356. /*-------------------------------------------------------------------------*//**
  357. * <param name="name">Identifer of data.</param>
  358. * <param name="text">Data.</param>
  359. * <param name="depth">Current XML tree depth.</param>
  360. * <remarks>
  361. * Pushes an identifier and it's value (either explicitaly stated or returned
  362. * from another function) to the stack with basic parent info.
  363. * </remarks>
  364. *//*------------------------------------------------------------------------**/
  365. stock XML_Push(name[], text[], depth)
  366. {
  367. P:3("XML_Push called: \"%s\", \"%s\", %i, %i", name, text, depth, YSI_g_sCurBuffer);
  368. if (YSI_g_sCurBuffer < XML_BUFFER_SIZE && YSI_g_sCurBuffer >= 0)
  369. {
  370. P:7("XML_Push: first");
  371. strcpy(YSI_g_sParameters[YSI_g_sCurBuffer][E_XML_PARA_NAME], name, MAX_XML_ENTRY_NAME);
  372. P:7("XML_Push: second");
  373. strcpy(YSI_g_sParameters[YSI_g_sCurBuffer][E_XML_PARA_VALUE], text, MAX_XML_ENTRY_TEXT);
  374. P:7("XML_Push: third");
  375. YSI_g_sParameters[YSI_g_sCurBuffer][E_XML_PARA_LEVEL] = depth;
  376. P:7("XML_Push: fourth");
  377. ++YSI_g_sCurBuffer;
  378. P:7("XML_Push: fifth");
  379. }
  380. P:5("XML_Push: YSI_g_sCurBuffer = %d", YSI_g_sCurBuffer);
  381. }
  382. /*-------------------------------------------------------------------------*//**
  383. * <param name="line">Data to extract from.</param>
  384. * <param name="pos">Start/end point of the text.</param>
  385. * <remarks>
  386. * Gets the data from inside ""s in an identifier. Now supports
  387. * \ for escape characters.
  388. * </remarks>
  389. *//*------------------------------------------------------------------------**/
  390. stock XML_GetParameter(line[], &pos)
  391. {
  392. P:3("XML_GetParameter called: \"%s\", %i", line, pos);
  393. new
  394. ch,
  395. ret[MAX_XML_ENTRY_TEXT],
  396. i;
  397. while ((ch = line[pos++]) && ch != '"') {}
  398. if (ch)
  399. {
  400. while ((ch = line[pos++]) && i < (sizeof (ret) - 1))
  401. {
  402. if (ch == '\\')
  403. {
  404. switch (line[pos++])
  405. {
  406. case '"':
  407. {
  408. ch = '"';
  409. }
  410. case 'n':
  411. {
  412. ch = '\n';
  413. }
  414. case 'r':
  415. {
  416. ch = '\r';
  417. }
  418. case '\\': {}
  419. default:
  420. {
  421. pos--;
  422. continue;
  423. }
  424. }
  425. }
  426. else if (ch == '"')
  427. {
  428. break;
  429. }
  430. ret[i++] = ch;
  431. }
  432. }
  433. if (i == (sizeof (ret) - 1))
  434. {
  435. while ((ch = line[pos++]))
  436. {
  437. if (ch == '\\')
  438. {
  439. switch (line[pos++])
  440. {
  441. case '\\', '"', 'n', 'r': {}
  442. default:
  443. {
  444. pos--;
  445. }
  446. }
  447. }
  448. else if (ch == '"') break;
  449. }
  450. }
  451. ret[i] = '\0';
  452. return ret;
  453. }
  454. /*-------------------------------------------------------------------------*//**
  455. * <param name="line">Line to get data from.</param>
  456. * <param name="pos">Start and end position of the data.</param>
  457. * <remarks>
  458. * Gets the text between tags.
  459. * </remarks>
  460. *//*------------------------------------------------------------------------**/
  461. stock XML_GetValue(line[], &pos)
  462. {
  463. P:3("XML_GetValue called: \"%s\", %i", line, pos);
  464. new
  465. ch,
  466. ret[MAX_XML_ENTRY_TEXT],
  467. i;
  468. while (((ch = line[pos++]) >= ' ' || ch == '\t') && (ch != '<') && i < (sizeof (ret) - 1)) ret[i++] = ch;
  469. pos--;
  470. if (i == (sizeof (ret) - 1))
  471. {
  472. while (((ch = line[pos]) >= ' ' || ch == '\t') && (ch != '<')) ++pos;
  473. }
  474. ret[i] = '\0';
  475. return ret;
  476. }
  477. /*-------------------------------------------------------------------------*//**
  478. * <param name="line">Line to get data from.</param>
  479. * <param name="pos">Start and end position of text.</param>
  480. * <remarks>
  481. * Gets the identifier of a piece of data.
  482. * </remarks>
  483. *//*------------------------------------------------------------------------**/
  484. stock XML_GetName(line[], &pos)
  485. {
  486. P:3("XML_GetName called: \"%s\", %i", line, pos);
  487. new
  488. ch,
  489. ret[MAX_XML_ENTRY_NAME],
  490. i;
  491. while ((ch = line[pos++]) && XML_IsChar(ch) && i < (sizeof (ret) - 1)) ret[i++] = ch;
  492. pos--;
  493. if (i == (sizeof (ret) - 1))
  494. {
  495. while ((ch = line[pos]) >= ' ' && XML_IsChar(ch)) ++pos;
  496. }
  497. ret[i] = '\0';
  498. return ret;
  499. }
  500. /*-------------------------------------------------------------------------*//**
  501. * <param name="rule">Rule set to parse according to.</param>
  502. * <param name="name">Name if identifier.</param>
  503. * <param name="tagCount">New tree depth.</param>
  504. *//*------------------------------------------------------------------------**/
  505. stock XML_ParseTag(XML:rule, name[], tagCount)
  506. {
  507. P:3("XML_ParseTag called: %i, \"%s\", %i", _:rule, name, tagCount);
  508. new
  509. i,
  510. j = YSI_g_sCurHandler[rule],
  511. ret[MAX_XML_ENTRY_TEXT] = "\1";
  512. YSI_g_sEndTag = tagCount;
  513. while (i < j)
  514. {
  515. if (!strcmp(YSI_g_sHandlers[rule][i][E_XML_HANDLER_TRIGGER], name, true))
  516. {
  517. break;
  518. }
  519. i++;
  520. }
  521. P:5("XML_ParseTag: Check %d != %d", i, j);
  522. if (i != j)
  523. {
  524. //format(ret, sizeof (ret), "%d", CallLocalFunction(YSI_g_sHandlers[rule][i][E_XML_HANDLER_FUNCTION], ""));
  525. valstr(ret, CallLocalFunction(YSI_g_sHandlers[rule][i][E_XML_HANDLER_FUNCTION], ""));
  526. }
  527. while (YSI_g_sCurBuffer-- && YSI_g_sParameters[YSI_g_sCurBuffer][E_XML_PARA_LEVEL] > tagCount)
  528. {
  529. P:3("XML_ParseTag item: %d %s %d", YSI_g_sCurBuffer, YSI_g_sParameters[YSI_g_sCurBuffer][E_XML_PARA_NAME], YSI_g_sParameters[YSI_g_sCurBuffer][E_XML_PARA_LEVEL]);
  530. //--YSI_g_sCurBuffer;
  531. }
  532. ++YSI_g_sCurBuffer;
  533. /*while (YSI_g_sCurBuffer)
  534. {
  535. if (YSI_g_sParameters[--YSI_g_sCurBuffer][E_XML_PARA_LEVEL] <= tagCount)
  536. {
  537. ++YSI_g_sCurBuffer;
  538. break;
  539. }
  540. }*/
  541. return ret;
  542. }
  543. /*-------------------------------------------------------------------------*//**
  544. * <param name="key">Variable to return identifier in.</param>
  545. * <param name="value">Variable to return value in.</param>
  546. * <returns>
  547. * Data found.
  548. * </returns>
  549. * <remarks>
  550. * Pops items off the stack for use in custom functions.
  551. * </remarks>
  552. *//*------------------------------------------------------------------------**/
  553. stock XML_GetKeyValue(key[], value[])
  554. {
  555. P:3("XML_GetKeyValue called: \"%s\", \"%s\"", key, value);
  556. key[0] = 1;
  557. key[1] = 0;
  558. value[0] = 1;
  559. value[1] = 0;
  560. P:5("XML_GetKeyValue: YSI_g_sCurBuffer: %d", YSI_g_sCurBuffer);
  561. if (YSI_g_sCurBuffer)
  562. {
  563. --YSI_g_sCurBuffer;
  564. if (YSI_g_sParameters[YSI_g_sCurBuffer][E_XML_PARA_LEVEL] <= YSI_g_sEndTag)
  565. {
  566. ++YSI_g_sCurBuffer;
  567. return 0;
  568. }
  569. P:5("XML_GetKeyValue: first");
  570. strcpy(key, YSI_g_sParameters[YSI_g_sCurBuffer][E_XML_PARA_NAME], MAX_XML_ENTRY_NAME);
  571. P:5("XML_GetKeyValue: second");
  572. strcpy(value, YSI_g_sParameters[YSI_g_sCurBuffer][E_XML_PARA_VALUE], MAX_XML_ENTRY_TEXT);
  573. P:3("XML_GetKeyValue ending: \"%s\", \"%s\"", key, value);
  574. return 1;
  575. }
  576. return 0;
  577. }
  578. /*-------------------------------------------------------------------------*//**
  579. * <param name="key">Name of the parameter to get.</param>
  580. * <param name="value">Variable to return value in.</param>
  581. * <returns>
  582. * Data found.
  583. * </returns>
  584. * <remarks>
  585. * Does no poping, just searches for a value with the right name at the right
  586. * depth so children can use the data if they HAVE to.
  587. * </remarks>
  588. *//*------------------------------------------------------------------------**/
  589. stock XML_GetParentValue(const key[], value[])
  590. {
  591. P:3("XML_GetParentValue called: \"%s\"", key);
  592. value[0] = 1;
  593. value[1] = 0;
  594. P:7("XML_GetParentValue: first");
  595. //P:C(for (new i = 0; i != YSI_g_sCurBuffer; ++i) P:0("XML_GetParentValue: buffer[%d] = %s", i, YSI_g_sParameters[i][E_XML_PARA_NAME]););
  596. for (new i = 0; i != YSI_g_sCurBuffer; ++i)
  597. {
  598. P:7("XML_GetParentValue: second %d %d %d", i, YSI_g_sParameters[i][E_XML_PARA_LEVEL], YSI_g_sEndTag);
  599. if (YSI_g_sParameters[i][E_XML_PARA_LEVEL] == YSI_g_sEndTag)
  600. {
  601. P:7("XML_GetParentValue: 2b: %s %s", key, YSI_g_sParameters[i][E_XML_PARA_NAME]);
  602. if (!strcmp(key, YSI_g_sParameters[i][E_XML_PARA_NAME]))
  603. {
  604. P:7("XML_GetParentValue: third");
  605. strcpy(value, YSI_g_sParameters[i][E_XML_PARA_VALUE], MAX_XML_ENTRY_TEXT);
  606. return 1;
  607. }
  608. }
  609. else if (YSI_g_sParameters[i][E_XML_PARA_LEVEL] > YSI_g_sEndTag)
  610. {
  611. P:7("XML_GetParentValue: fourth");
  612. // In to child data.
  613. return 0;
  614. }
  615. }
  616. P:7("XML_GetParentValue: fifth");
  617. return 0;
  618. }
  619. /*-------------------------------------------------------------------------*//**
  620. * <param name="ruls">Rule set to add data to.</param>
  621. * <param name="trigger">Identifier which calls it.</param>
  622. * <param name="function">Function to parse identifier in.</param>
  623. *//*------------------------------------------------------------------------**/
  624. stock XML_AddHandler(XML:rule, trigger[], function[])
  625. {
  626. P:3("XML_AddHandler called: %i, \"%s\", \"%s\"", _:rule, trigger, function);
  627. new
  628. handle;
  629. if (!XML_IsValid(rule) || (handle = YSI_g_sCurHandler[rule]) >= MAX_XML_HANDLERS && handle >= 0) return 0;
  630. strcpy(YSI_g_sHandlers[rule][handle][E_XML_HANDLER_TRIGGER], trigger, MAX_XML_ENTRY_NAME);
  631. strcpy(YSI_g_sHandlers[rule][handle][E_XML_HANDLER_FUNCTION], function, MAX_XML_FUNCTION);
  632. YSI_g_sCurHandler[rule]++;
  633. return 1;
  634. }
  635. /*-------------------------------------------------------------------------*//**
  636. * <param name="rule">Set to remove handler from.</param>
  637. * <param name="trigger">Handler name to remove.</param>
  638. *//*------------------------------------------------------------------------**/
  639. stock XML_RemoveHandler(XML:rule, trigger[])
  640. {
  641. P:3("XML_RemoveHandler called: %i, \"%s\"", _:rule, trigger);
  642. if (XML_IsValid(rule))
  643. {
  644. for (new i = 0, j = YSI_g_sCurHandler[rule]; i < j; i++)
  645. {
  646. if (!strcmp(YSI_g_sHandlers[rule][i][E_XML_HANDLER_TRIGGER], trigger, true))
  647. {
  648. new
  649. last = --YSI_g_sCurHandler[rule];
  650. if (last)
  651. {
  652. strcpy(YSI_g_sHandlers[rule][i][E_XML_HANDLER_TRIGGER], YSI_g_sHandlers[rule][last][E_XML_HANDLER_TRIGGER], MAX_XML_ENTRY_NAME);
  653. strcpy(YSI_g_sHandlers[rule][i][E_XML_HANDLER_FUNCTION], YSI_g_sHandlers[rule][last][E_XML_HANDLER_FUNCTION], MAX_XML_FUNCTION);
  654. }
  655. return 1;
  656. }
  657. }
  658. }
  659. return 0;
  660. }
  661. /*-------------------------------------------------------------------------*//**
  662. * <param name="parent">Tag this is a parameter of.</param>
  663. * <param name="tag">Name of this data.</param>
  664. * <param name="value">Value of this data, if this is blank there is sub parameters.</param>
  665. *//*------------------------------------------------------------------------**/
  666. //#define XML_AddSubEntry XML_AddParameter
  667. stock XMLEntry:XML_AddParameter(XMLEntry:parent, tag[], value[] = "")
  668. {
  669. P:3("XMLEntry:XML_AddParameter called: %i, \"%s\", \"%s\"", _:parent, tag, value);
  670. if (_:parent < YSI_g_sXMLWritePointer < XML_WRITE_BUFFER_SIZE)
  671. {
  672. strcpy(YSI_g_sXMLWriteBuffer[YSI_g_sXMLWritePointer][E_XML_WRITE_TAG], tag, MAX_XML_ENTRY_NAME);
  673. strcpy(YSI_g_sXMLWriteBuffer[YSI_g_sXMLWritePointer][E_XML_WRITE_VALUE], value, MAX_XML_ENTRY_TEXT);
  674. YSI_g_sXMLWriteBuffer[YSI_g_sXMLWritePointer][E_XML_WRITE_CHILDREN] = -1;
  675. YSI_g_sXMLWriteBuffer[YSI_g_sXMLWritePointer][E_XML_WRITE_SIBLINGS] = YSI_g_sXMLWriteBuffer[_:parent][E_XML_WRITE_CHILDREN];
  676. YSI_g_sXMLWriteBuffer[_:parent][E_XML_WRITE_CHILDREN] = YSI_g_sXMLWritePointer;
  677. return XMLEntry:YSI_g_sXMLWritePointer++;
  678. }
  679. return XMLEntry:cellmax;
  680. }
  681. /*-------------------------------------------------------------------------*//**
  682. * <param name="tag">Type of data being added.</param>
  683. * <param name="name">The optional name parameter for identifying tags.</param>
  684. * <remarks>
  685. * Starts the creation of a new tag to be written to a file, the structure
  686. * has to be manually created then written. There is no buffering of multiple
  687. * tags before writing as a single tag can have quite a bit of data.
  688. * </remarks>
  689. *//*------------------------------------------------------------------------**/
  690. #define XML_CreateEntry XML_AddItem
  691. #define XML_AddSubEntry XML_AddItem
  692. stock XMLEntry:XML_AddItem(tag[], name[] = "", XMLEntry:parent = XMLEntry:cellmax)
  693. {
  694. P:3("XMLEntry:XML_AddItem called: \"%s\", \"%s\", %i", tag, name, _:parent);
  695. if (_:parent != cellmax)
  696. {
  697. return XML_AddParameter(parent, tag, name);
  698. }
  699. if (YSI_g_sXMLWritePointer < XML_WRITE_BUFFER_SIZE)
  700. {
  701. strcpy(YSI_g_sXMLWriteBuffer[YSI_g_sXMLWritePointer][E_XML_WRITE_TAG], tag, MAX_XML_ENTRY_NAME);
  702. strcpy(YSI_g_sXMLWriteBuffer[YSI_g_sXMLWritePointer][E_XML_WRITE_VALUE], name, MAX_XML_ENTRY_TEXT);
  703. YSI_g_sXMLWriteBuffer[YSI_g_sXMLWritePointer][E_XML_WRITE_CHILDREN] = -1;
  704. YSI_g_sXMLWriteBuffer[YSI_g_sXMLWritePointer][E_XML_WRITE_SIBLINGS] = -1;
  705. return XMLEntry:YSI_g_sXMLWritePointer++;
  706. }
  707. return XMLEntry:cellmax;
  708. }
  709. /*-------------------------------------------------------------------------*//**
  710. * <param name="filename">File to write to.</param>
  711. * <param name="item">Handle to the tag to write.</param>
  712. * <param name="bIncludeXML">Write the default &lt;XML&gt; tags or not?</param>
  713. * <param name="bFavourShort">Write the shortest tags possible.</param>
  714. * <remarks>
  715. * Writea the data for a tag to a file.
  716. * </remarks>
  717. *//*------------------------------------------------------------------------**/
  718. #define XML_WriteEntry XML_WriteItem
  719. stock XML_WriteItem(filename[], XMLEntry:item, bool:bIncludeXML = true, bool:bFavourShort = false)
  720. {
  721. P:3("XML_WriteItem called: \"%s\", %i", filename, _:item);
  722. if (_:item < YSI_g_sXMLWritePointer)
  723. {
  724. new
  725. data;
  726. if (fexist(filename))
  727. {
  728. new
  729. File:fHnd = fopen(filename, io_read),
  730. File:__ftemp = ftemp();//fopen("_temp_ysi_user_file_.ysi", io_write);
  731. if (fHnd && __ftemp)
  732. {
  733. new
  734. str[YSI_MAX_STRING];
  735. while (fread(fHnd, str))
  736. {
  737. fwrite(__ftemp, str);
  738. if (!data)
  739. {
  740. new
  741. i,
  742. ch;
  743. while ((ch = str[i++]) && ch <= ' ') {}
  744. if (ch == '<')
  745. {
  746. if (bFavourShort) XML_WriteItemDataShort(_:item, __ftemp, 2);
  747. else XML_WriteItemData(_:item, __ftemp, 2);
  748. data = 1;
  749. }
  750. }
  751. }
  752. fclose(fHnd);
  753. //fclose(__ftemp);
  754. fremove(filename);
  755. if (data)
  756. {
  757. fHnd = fopen(filename, io_write);
  758. fseek(__ftemp);
  759. //__ftemp = fopen("_temp_ysi_user_file_.ysi", io_read);
  760. if (fHnd)// && __ftemp)
  761. {
  762. while (fread(__ftemp, str)) fwrite(fHnd, str);
  763. fclose(fHnd);
  764. fclose(__ftemp);
  765. //fremove("_temp_ysi_user_file_.ysi");
  766. YSI_g_sXMLWritePointer = _:item;
  767. return 1;
  768. }
  769. }
  770. }
  771. if (fHnd)
  772. {
  773. fclose(fHnd);
  774. }
  775. if (__ftemp)
  776. {
  777. fclose(__ftemp);
  778. //fremove("_temp_ysi_user_file_.ysi");
  779. }
  780. }
  781. if (!data)
  782. {
  783. new
  784. File:fHnd = fopen(filename, io_write);
  785. if (fHnd)
  786. {
  787. if (bIncludeXML) fwrite(fHnd, "<XML>\r\n");
  788. if (bFavourShort) XML_WriteItemDataShort(_:item, fHnd, _:bIncludeXML * 2);
  789. else XML_WriteItemData(_:item, fHnd, _:bIncludeXML * 2);
  790. if (bIncludeXML) fwrite(fHnd, "</XML>");
  791. fclose(fHnd);
  792. YSI_g_sXMLWritePointer = _:item;
  793. return 1;
  794. }
  795. }
  796. }
  797. return 0;
  798. }
  799. /*-------------------------------------------------------------------------*//**
  800. * <param name="item">Item to write data for.</param>
  801. * <param name="fHnd">File to write to.</param>
  802. * <param name="depth">Current indentation.</param>
  803. * <remarks>
  804. * Recursive function to write a tag and it's children to a file.
  805. * </remarks>
  806. *//*------------------------------------------------------------------------**/
  807. static stock XML_WriteItemData(item, File:fHnd, depth)
  808. {
  809. P:4("XML_WriteItemData called: %i, %i, %i", item, _:fHnd, depth);
  810. new
  811. str[YSI_MAX_STRING],
  812. i = YSI_g_sXMLWriteBuffer[item][E_XML_WRITE_CHILDREN];
  813. if (i == -1)
  814. {
  815. format(str, sizeof (str), "%*s<%s>%s</%s>\n", depth, "", YSI_g_sXMLWriteBuffer[item][E_XML_WRITE_TAG], YSI_g_sXMLWriteBuffer[item][E_XML_WRITE_VALUE], YSI_g_sXMLWriteBuffer[item][E_XML_WRITE_TAG]);
  816. fwrite(fHnd, str);
  817. P:7("XML_WriteItemData: writing %s", str);
  818. }
  819. else
  820. {
  821. if (YSI_g_sXMLWriteBuffer[item][E_XML_WRITE_VALUE][0]) format(str, sizeof (str), "%*s<%s name=\"%s\">\n", depth, "", YSI_g_sXMLWriteBuffer[item][E_XML_WRITE_TAG], YSI_g_sXMLWriteBuffer[item][E_XML_WRITE_VALUE]);
  822. else format(str, sizeof (str), "%*s<%s>\n", depth, "", YSI_g_sXMLWriteBuffer[item][E_XML_WRITE_TAG]);
  823. P:7("XML_WriteItemData: writing %s", str);
  824. fwrite(fHnd, str);
  825. depth += 2;
  826. while (i != -1)
  827. {
  828. XML_WriteItemData(i, fHnd, depth);
  829. i = YSI_g_sXMLWriteBuffer[i][E_XML_WRITE_SIBLINGS];
  830. }
  831. depth -= 2;
  832. format(str, sizeof (str), "%*s</%s>\n", depth, "", YSI_g_sXMLWriteBuffer[item][E_XML_WRITE_TAG]);
  833. P:7("XML_WriteItemData: writing %s", str);
  834. fwrite(fHnd, str);
  835. }
  836. }
  837. /*-------------------------------------------------------------------------*//**
  838. * <param name="item">Item to write data for.</param>
  839. * <param name="fHnd">File to write to.</param>
  840. * <param name="depth">Current indentation.</param>
  841. * <remarks>
  842. * Recursive function to write a tag and it's children to a file. Writes tags
  843. * in the shortest manner possible. This does make it slower however (not much
  844. * though given the use of linked lists).
  845. * </remarks>
  846. *//*------------------------------------------------------------------------**/
  847. static stock XML_WriteItemDataShort(item, File:fHnd, depth)
  848. {
  849. P:4("XML_WriteItemDataShort called: %i, %i, %i", item, _:fHnd, depth);
  850. new
  851. str[YSI_MAX_STRING],
  852. i = YSI_g_sXMLWriteBuffer[item][E_XML_WRITE_CHILDREN];
  853. if (i == -1)
  854. {
  855. format(str, sizeof (str), "%*s<%s>%s</%s>\n", depth, "", YSI_g_sXMLWriteBuffer[item][E_XML_WRITE_TAG], YSI_g_sXMLWriteBuffer[item][E_XML_WRITE_VALUE], YSI_g_sXMLWriteBuffer[item][E_XML_WRITE_TAG]);
  856. fwrite(fHnd, str);
  857. P:7("XML_WriteItemDataShort: writing 0 %s", str);
  858. }
  859. else
  860. {
  861. // Loop through all children to find those with no children.
  862. format(str, sizeof (str), "%*s<%s", depth, "", YSI_g_sXMLWriteBuffer[item][E_XML_WRITE_TAG]);
  863. P:7("XML_WriteItemDataShort: writing 1 %s", str);
  864. fwrite(fHnd, str);
  865. new
  866. notDone = 0;
  867. do
  868. {
  869. if (YSI_g_sXMLWriteBuffer[i][E_XML_WRITE_CHILDREN] == -1)
  870. {
  871. format(str, sizeof (str), " %s=\"%s\"", YSI_g_sXMLWriteBuffer[i][E_XML_WRITE_TAG], YSI_g_sXMLWriteBuffer[i][E_XML_WRITE_VALUE]);
  872. P:7("XML_WriteItemDataShort: writing 2 %s", str);
  873. fwrite(fHnd, str);
  874. }
  875. else
  876. {
  877. ++notDone;
  878. }
  879. i = YSI_g_sXMLWriteBuffer[i][E_XML_WRITE_SIBLINGS];
  880. }
  881. while (i != -1);
  882. if (notDone)
  883. {
  884. P:7("XML_WriteItemDataShort: writing 3 >\n");
  885. fwrite(fHnd, ">\n");
  886. depth += 2;
  887. i = YSI_g_sXMLWriteBuffer[item][E_XML_WRITE_CHILDREN];
  888. do
  889. {
  890. if (YSI_g_sXMLWriteBuffer[i][E_XML_WRITE_CHILDREN] != -1)
  891. {
  892. XML_WriteItemDataShort(i, fHnd, depth);
  893. }
  894. i = YSI_g_sXMLWriteBuffer[i][E_XML_WRITE_SIBLINGS];
  895. }
  896. while (i != -1);
  897. depth -= 2;
  898. format(str, sizeof (str), "%*s</%s>\n", depth, "", YSI_g_sXMLWriteBuffer[item][E_XML_WRITE_TAG]);
  899. fwrite(fHnd, str);
  900. P:7("XML_WriteItemDataShort: writing 4 %s", str);
  901. }
  902. else
  903. {
  904. P:7("XML_WriteItemData: writing 5 />\n");
  905. fwrite(fHnd, " />\n");
  906. }
  907. }
  908. }
  909. /*-------------------------------------------------------------------------*//**
  910. * <param name="file">File to remove the tag from.</param>
  911. * <param name="tag">Type of tag to remove.</param>
  912. * <param name="name">Name of the tag to remove.</param>
  913. * <remarks>
  914. * Does a replace on data in a file with no new data.
  915. * </remarks>
  916. *//*------------------------------------------------------------------------**/
  917. stock XML_RemoveItem(file[], tag[], name[])
  918. {
  919. P:3("XML_RemoveItem called: \"%s\", \"%s\", \"%s\", ", file, tag, name);
  920. return XML_ReplaceItem(file, tag, name, -1);
  921. }
  922. /*-------------------------------------------------------------------------*//**
  923. * <param name="file">File to replace an item in.</param>
  924. * <param name="tag">Tag type of data to replace.</param>
  925. * <param name="name">Name of data to replace.</param>
  926. * <param name="replacement">Handle to the replacement data.</param>
  927. * <remarks>
  928. * Replaces a tag's data with new data, basically changes a tag's value.
  929. * </remarks>
  930. *//*------------------------------------------------------------------------**/
  931. stock XML_ReplaceItem(file[], tag[], name[], replacement)
  932. {
  933. P:3("XML_ReplaceItem called: \"%s\", \"%s\", \"%s\", %i", file, tag, name, replacement);
  934. if (fexist(file))
  935. {
  936. new
  937. File:fHnd = fopen(file, io_read),
  938. File:__ftemp = ftemp();//fopen("_temp_ysi_user_file_.ysi", io_write);
  939. if (fHnd && __ftemp)
  940. {
  941. new
  942. tagCount,
  943. line[YSI_MAX_STRING],
  944. inTag,
  945. atStart;
  946. while (fread(fHnd, line))
  947. {
  948. new
  949. pos,
  950. ch;
  951. while ((ch = line[pos]) && ch <= ' ') ++pos;
  952. while (ch)
  953. {
  954. if (ch <= ' ') ++pos;
  955. else if (ch == '<')
  956. {
  957. if (line[++pos] == '/')
  958. {
  959. ++pos;
  960. tagCount--;
  961. if (inTag && tagCount <= atStart) inTag = 3;
  962. }
  963. else
  964. {
  965. tagCount++;
  966. if (!inTag)
  967. {
  968. if (!strcmp(XML_GetName(line, pos), tag)) inTag = 1;
  969. }
  970. else while ((ch = line[pos]) && XML_IsChar(ch)) ++pos;
  971. }
  972. }
  973. else if (ch == '>')
  974. {
  975. if (inTag == 1) inTag = 0;
  976. ++pos;
  977. }
  978. else if (inTag == 1)
  979. {
  980. if (!strcmp(XML_GetName(line, pos), "name"))
  981. {
  982. if (!(strcmp(XML_GetParameter(line, pos), name)))
  983. {
  984. inTag = 2;
  985. atStart = tagCount - 1;
  986. if (replacement != -1)
  987. {
  988. if (replacement >= YSI_g_sXMLWritePointer) replacement = -1;
  989. else XML_WriteItemData(replacement, __ftemp, (tagCount - 1) * 2);
  990. }
  991. }
  992. }
  993. else XML_GetParameter(line, pos);
  994. }
  995. else
  996. ++pos;
  997. ch = line[pos];
  998. }
  999. if (!inTag) fwrite(__ftemp, line);
  1000. if (inTag == 3) inTag = 0;
  1001. }
  1002. fclose(fHnd);
  1003. //fclose(__ftemp);
  1004. fremove(file);
  1005. fHnd = fopen(file, io_write);
  1006. //__ftemp = fopen("_temp_ysi_user_file_.ysi", io_read);
  1007. fseek(__ftemp);
  1008. if (fHnd && __ftemp)
  1009. {
  1010. while (fread(__ftemp, line)) fwrite(fHnd, line);
  1011. fclose(fHnd);
  1012. fclose(__ftemp);
  1013. //fremove("_temp_ysi_user_file_.ysi");
  1014. if (replacement != -1) YSI_g_sXMLWritePointer = replacement;
  1015. return 1;
  1016. }
  1017. }
  1018. if (fHnd)
  1019. {
  1020. fclose(fHnd);
  1021. }
  1022. if (__ftemp)
  1023. {
  1024. fclose(__ftemp);
  1025. //fremove("_temp_ysi_user_file_.ysi");
  1026. }
  1027. return 0;
  1028. }
  1029. return 1;
  1030. }