1
0

writing.inc 29 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851
  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_KV_ENTRY
  77. {
  78. E_INI_KV_ENTRY_NAME[MAX_INI_ENTRY_NAME],
  79. E_INI_KV_ENTRY_TEXT[MAX_INI_ENTRY_TEXT],
  80. E_INI_KV_ENTRY_NEXT
  81. }
  82. enum E_INI_TAGS
  83. {
  84. E_INI_TAGS_NAME[MAX_INI_TAG char],
  85. E_INI_TAGS_START,
  86. E_INI_TAGS_NEXT
  87. }
  88. _Y_INI_STATIC stock
  89. YSI_g_sINITmpBuffer[36],
  90. YSI_g_sINIWriteBuffer[INI_MAX_WRITES * INI_BUFFER_SIZE][E_INI_KV_ENTRY],
  91. YSI_g_sINIWritePos, // Pointer to the first free K/V slot.
  92. YSI_g_sINITagPos, // Pointer to the first free tag slot.
  93. YSI_g_sINITagBuffer[INI_MAX_WRITES * MAX_INI_TAGS][E_INI_TAGS],
  94. YSI_g_sINICurrentTag[INI:INI_MAX_WRITES],
  95. YSI_g_sINIStartTag[INI:INI_MAX_WRITES],
  96. YSI_g_sINIWriteFile[INI:INI_MAX_WRITES][YSI_MAX_STRING];
  97. forward bool:INI_WriteBuffer(INI:file);
  98. forward bool:INI_Flush();
  99. /*
  100. 88b d88
  101. 888b d888
  102. 88`8b d8'88
  103. 88 `8b d8' 88 ,adPPYYba, ,adPPYba, 8b,dPPYba, ,adPPYba, ,adPPYba,
  104. 88 `8b d8' 88 "" `Y8 a8" "" 88P' "Y8 a8" "8a I8[ ""
  105. 88 `8b d8' 88 ,adPPPPP88 8b 88 8b d8 `"Y8ba,
  106. 88 `888' 88 88, ,88 "8a, ,aa 88 "8a, ,a8" aa ]8I
  107. 88 `8' 88 `"8bbdP"Y8 `"Ybbd8"' 88 `"YbbdP"' `"YbbdP"'
  108. */
  109. #define INI_IsValid(%0) (0 <= _:(%0) < INI_MAX_WRITES && YSI_g_sINIWriteFile[(%0)][0])
  110. #define INI_WriteComments() \
  111. fwrite(buffer, ";"), \
  112. fwrite(buffer, sLine[p2s])
  113. #define INI_IsDeleted(%0) ((%0) != -1 && YSI_g_sINITagBuffer[(%0)][E_INI_TAGS_START] == cellmax)
  114. /*
  115. 88b d88 88 db 88888888ba 88
  116. 888b d888 "" d88b 88 "8b 88
  117. 88`8b d8'88 d8'`8b 88 ,8P 88
  118. 88 `8b d8' 88 ,adPPYYba, 88 8b,dPPYba, d8' `8b 88aaaaaa8P' 88
  119. 88 `8b d8' 88 "" `Y8 88 88P' `"8a d8YaaaaY8b 88""""""' 88
  120. 88 `8b d8' 88 ,adPPPPP88 88 88 88 d8""""""""8b 88 88
  121. 88 `888' 88 88, ,88 88 88 88 d8' `8b 88 88
  122. 88 `8' 88 `"8bbdP"Y8 88 88 88 d8' `8b 88 88
  123. */
  124. /*-------------------------------------------------------------------------*//**
  125. * <param name="filename">INI file to open.</param>
  126. * <returns>
  127. * INI - handle to the file or INI_NO_FILE.
  128. * </returns>
  129. * <remarks>
  130. * Doesn't actually open the file, just starts a new buffer if possible.
  131. * </remarks>
  132. *//*------------------------------------------------------------------------**/
  133. stock INI:INI_Open(const filename[])
  134. {
  135. if (ftouch(filename) == -1)
  136. {
  137. P:W("INI_Open could not find or create file %s", filename);
  138. }
  139. P:3("INI:INI_Open called: \"%s\"", filename);
  140. new
  141. i;
  142. while (i != INI_MAX_WRITES && YSI_g_sINIWriteFile[INI:i][0]) ++i;
  143. if (i == INI_MAX_WRITES) return INI_NO_FILE;
  144. return
  145. strcpy(YSI_g_sINIWriteFile[INI:i], filename, YSI_MAX_STRING),
  146. // Reset tags.
  147. YSI_g_sINIStartTag[INI:i] = -1,
  148. YSI_g_sINICurrentTag[INI:i] = -1,
  149. INI:i;
  150. }
  151. /*-------------------------------------------------------------------------*//**
  152. * <param name="file">Handle to the ini to close.</param>
  153. * <remarks>
  154. * Writes any outstanding buffer data to the file and ends the stream.
  155. * </remarks>
  156. *//*------------------------------------------------------------------------**/
  157. stock INI_Close(INI:file)
  158. {
  159. P:3("INI_Close called: %i", _:file);
  160. if (INI_IsValid(file))
  161. {
  162. INI_WriteBuffer(file), // Used to check, now just flush by default.
  163. YSI_g_sINIWriteFile[file][0] = '\0';
  164. }
  165. }
  166. /*-------------------------------------------------------------------------*//**
  167. * <param name="file">INI file handle to write to.</param>
  168. * <param name="tag">Name of the new file subsection for subsequent data to write to.</param>
  169. * <remarks>
  170. * Sets a new [tag] section header. Subsequent data is written under this
  171. * header. Uses lists for constant tag switching and checks the tag doesn't
  172. * already exist.
  173. * </remarks>
  174. *//*------------------------------------------------------------------------**/
  175. stock INI_SetTag(INI:file, const tag[])
  176. {
  177. P:3("INI_SetTag called: %i, \"%s\"", _:file, tag);
  178. if (INI_IsValid(file))
  179. {
  180. // Loop through the tags for this file.
  181. new
  182. cur = INI_GetTag(file, tag);
  183. if (cur != -1) return (YSI_g_sINICurrentTag[file] = cur);
  184. // May need some extra space.
  185. if (YSI_g_sINITagPos == -1 && !INI_Flush()) return -1;
  186. // Tag hasn't been written to yet this flush.
  187. return
  188. // Get a new tag.
  189. cur = YSI_g_sINITagPos,
  190. // Update the "free" list.
  191. YSI_g_sINITagPos = YSI_g_sINITagBuffer[cur][E_INI_TAGS_NEXT],
  192. // Add this tag to the current file's list.
  193. YSI_g_sINITagBuffer[cur][E_INI_TAGS_NEXT] = YSI_g_sINIStartTag[file],
  194. YSI_g_sINIStartTag[file] = cur,
  195. // Copy the name over.
  196. strpack(YSI_g_sINITagBuffer[cur][E_INI_TAGS_NAME], tag, MAX_INI_TAG char),
  197. // Set start points.
  198. YSI_g_sINITagBuffer[cur][E_INI_TAGS_START] = -1,
  199. YSI_g_sINICurrentTag[file] = cur;
  200. }
  201. return -1;
  202. }
  203. /*-------------------------------------------------------------------------*//**
  204. * <param name="file">INI file handle to write to.</param>
  205. * <param name="tag">Name of the whole section to delete.</param>
  206. * <remarks>
  207. * Removes a [tag] section from a file.
  208. * </remarks>
  209. *//*------------------------------------------------------------------------**/
  210. stock INI_DeleteTag(INI:file, const tag[])
  211. {
  212. P:3("INI_DeleteTag called: %i, \"%s\"", _:file, tag);
  213. if (INI_IsValid(file))
  214. {
  215. // Save the tag currently being written to.
  216. new
  217. curTag = YSI_g_sINICurrentTag[file],
  218. tag2[MAX_INI_TAG];
  219. if (curTag != -1) strunpack(tag2, YSI_g_sINITagBuffer[curTag][E_INI_TAGS_NAME]);
  220. new
  221. // Get a pointer to the new tag.
  222. tp = INI_SetTag(file, tag),
  223. cur = YSI_g_sINITagBuffer[tp][E_INI_TAGS_START];
  224. // If there is anything in the tag, discard it. Accounts for "cellmax"
  225. // specials.
  226. while (0 <= cur < sizeof (YSI_g_sINIWriteBuffer)) cur = INI_FreeEntry(cur);
  227. // Now set it to the special "deleted" mark.
  228. YSI_g_sINITagBuffer[tp][E_INI_TAGS_START] = cellmax;
  229. // Reset the current tag, using a name not a pointer - we may have
  230. // flushed the buffer in the meantime and thus invalidated "curTag".
  231. YSI_g_sINICurrentTag[file] = (curTag == -1) ? -1 : INI_GetTag(file, tag2);
  232. }
  233. }
  234. /*-------------------------------------------------------------------------*//**
  235. * <param name="file">File to write to.</param>
  236. * <param name="name">Item to remove.</param>
  237. * <remarks>
  238. * Wrapper for INI_AddToBuffer for removing data.
  239. * </remarks>
  240. *//*------------------------------------------------------------------------**/
  241. stock INI_RemoveEntry(INI:file, name[])
  242. {
  243. P:3("INI_RemoveEntry called: %i, \"%s\"", _:file, name);
  244. return INI_AddToBuffer(file, name, NULL);
  245. }
  246. /*
  247. I8, 8 ,8I 88 88
  248. `8b d8b d8' "" ,d ""
  249. "8, ,8"8, ,8" 88
  250. Y8 8P Y8 8P 8b,dPPYba, 88 MM88MMM 88 8b,dPPYba, ,adPPYb,d8
  251. `8b d8' `8b d8' 88P' "Y8 88 88 88 88P' `"8a a8" `Y88
  252. `8a a8' `8a a8' 88 88 88 88 88 88 8b 88
  253. `8a8' `8a8' 88 88 88, 88 88 88 "8a, ,d88
  254. `8' `8' 88 88 "Y888 88 88 88 `"YbbdP"Y8
  255. aa, ,88
  256. "Y8bbdP"
  257. */
  258. /*-------------------------------------------------------------------------*//**
  259. * <param name="file">File to write to.</param>
  260. * <param name="name">Data name.</param>
  261. * <param name="data">Data.</param>
  262. * <remarks>
  263. * Wrapper for INI_AddToBuffer for strings.
  264. * </remarks>
  265. *//*------------------------------------------------------------------------**/
  266. stock INI_WriteString(INI:file, name[], data[])
  267. {
  268. P:3("INI_WriteString called: %i, \"%s\", \"%s\"", _:file, name, data);
  269. return INI_AddToBuffer(file, name, data);
  270. }
  271. /*-------------------------------------------------------------------------*//**
  272. * <param name="file">File to write to.</param>
  273. * <param name="name">Data name.</param>
  274. * <param name="data">Integer data.</param>
  275. * <remarks>
  276. * Wrapper for INI_AddToBuffer for integers. Fixed for very large numbers
  277. * based on code by Slice from "fixes.inc" for "valstr".
  278. * </remarks>
  279. *//*------------------------------------------------------------------------**/
  280. stock INI_WriteInt(INI:file, name[], data)
  281. {
  282. P:3("INI_WriteInt called: %i, \"%s\", %i", _:file, name, data);
  283. static const
  284. sc_szCellmin[] = !"-2147483648";
  285. if (data == cellmin)
  286. {
  287. return INI_AddToBuffer(file, name, sc_szCellmin);
  288. }
  289. else
  290. {
  291. return
  292. format(YSI_g_sINITmpBuffer, sizeof (YSI_g_sINITmpBuffer), "%d", data),
  293. INI_AddToBuffer(file, name, YSI_g_sINITmpBuffer);
  294. }
  295. }
  296. /*-------------------------------------------------------------------------*//**
  297. * <param name="file">File to write to.</param>
  298. * <param name="name">Data name.</param>
  299. * <param name="data">Hex data.</param>
  300. * <remarks>
  301. * Wrapper for INI_AddToBuffer for integers to be written as hex values.
  302. * </remarks>
  303. *//*------------------------------------------------------------------------**/
  304. stock INI_WriteHex(INI:file, name[], data)
  305. {
  306. P:3("INI_WriteHex called: %i, \"%s\", %i", _:file, name, data);
  307. return
  308. format(YSI_g_sINITmpBuffer, sizeof (YSI_g_sINITmpBuffer), "0x%04x%04x", data >>> 16, data & 0xFFFF),
  309. INI_AddToBuffer(file, name, YSI_g_sINITmpBuffer);
  310. }
  311. /*-------------------------------------------------------------------------*//**
  312. * <param name="file">File to write to.</param>
  313. * <param name="name">Data name.</param>
  314. * <param name="data">Binary data.</param>
  315. * <remarks>
  316. * Wrapper for INI_AddToBuffer for integers to be written as binary values.
  317. * </remarks>
  318. *//*------------------------------------------------------------------------**/
  319. stock INI_WriteBin(INI:file, name[], data)
  320. {
  321. P:3("INI_WriteBin called: %i, \"%s\", %i", _:file, name, data);
  322. if (data < 0) format(YSI_g_sINITmpBuffer, sizeof (YSI_g_sINITmpBuffer), "0b1%031b", data & 0x7FFFFFFF);
  323. else format(YSI_g_sINITmpBuffer, sizeof (YSI_g_sINITmpBuffer), "0b%b", data);
  324. return INI_AddToBuffer(file, name, YSI_g_sINITmpBuffer);
  325. }
  326. /*-------------------------------------------------------------------------*//**
  327. * <param name="file">File to write to.</param>
  328. * <param name="name">Data name.</param>
  329. * <param name="data">Boolean data.</param>
  330. * <remarks>
  331. * Wrapper for INI_AddToBuffer for booleans.
  332. * </remarks>
  333. *//*------------------------------------------------------------------------**/
  334. stock INI_WriteBool(INI:file, name[], bool:data)
  335. {
  336. P:3("INI_WriteBool called: %i, \"%s\", %i", _:file, name, _:data);
  337. return INI_AddToBuffer(file, name, data ? ("true") : ("false"));
  338. }
  339. /*-------------------------------------------------------------------------*//**
  340. * <param name="file">File to write to.</param>
  341. * <param name="name">Data name.</param>
  342. * <param name="data">Float data.</param>
  343. * <param name="accuracy">number of decimal places to write.</param>
  344. * <remarks>
  345. * Wrapper for INI_AddToBuffer for floats.
  346. * </remarks>
  347. *//*------------------------------------------------------------------------**/
  348. stock INI_WriteFloat(INI:file, name[], Float:data, accuracy = 6)
  349. {
  350. P:3("INI_WriteFloat called: %i, \"%s\", %f, %i", _:file, name, data, accuracy);
  351. return
  352. format(YSI_g_sINITmpBuffer, sizeof (YSI_g_sINITmpBuffer), "%.*f", accuracy, data),
  353. INI_AddToBuffer(file, name, YSI_g_sINITmpBuffer);
  354. }
  355. /*
  356. 88 88 88
  357. 88 88 88
  358. 88 88 88
  359. 88aaaaaaaa88 ,adPPYba, ,adPPYba, 88 ,d8 ,adPPYba,
  360. 88""""""""88 a8" "8a a8" "8a 88 ,a8" I8[ ""
  361. 88 88 8b d8 8b d8 8888[ `"Y8ba,
  362. 88 88 "8a, ,a8" "8a, ,a8" 88`"Yba, aa ]8I
  363. 88 88 `"YbbdP"' `"YbbdP"' 88 `Y8a `"YbbdP"'
  364. */
  365. hook OnScriptInit()
  366. {
  367. // Set up the linked list of free tag and K/V slots.
  368. for (new i = 0; i != sizeof (YSI_g_sINITagBuffer) - 1; ++i)
  369. {
  370. YSI_g_sINITagBuffer[i][E_INI_TAGS_NEXT] = i + 1;
  371. }
  372. for (new i = 0; i != sizeof (YSI_g_sINIWriteBuffer) - 1; ++i)
  373. {
  374. YSI_g_sINIWriteBuffer[i][E_INI_KV_ENTRY_NEXT] = i + 1;
  375. }
  376. // Start and end value.
  377. YSI_g_sINITagBuffer[sizeof (YSI_g_sINITagBuffer) - 1][E_INI_TAGS_NEXT] = YSI_g_sINIWriteBuffer[sizeof (YSI_g_sINIWriteBuffer) - 1][E_INI_KV_ENTRY_NEXT] = -1,
  378. YSI_g_sINITagPos = YSI_g_sINIWritePos = 0;
  379. }
  380. /*
  381. 88 88
  382. 88 ,d 88
  383. 88 88 88
  384. 88 8b,dPPYba, MM88MMM ,adPPYba, 8b,dPPYba, 8b,dPPYba, ,adPPYYba, 88
  385. 88 88P' `"8a 88 a8P_____88 88P' "Y8 88P' `"8a "" `Y8 88
  386. 88 88 88 88 8PP""""""" 88 88 88 ,adPPPPP88 88
  387. 88 88 88 88, "8b, ,aa 88 88 88 88, ,88 88
  388. 88 88 88 "Y888 `"Ybbd8"' 88 88 88 `"8bbdP"Y8 88
  389. */
  390. static stock INI_GetTag(INI:file, const tag[])
  391. {
  392. new
  393. cur = YSI_g_sINIStartTag[file];
  394. while (cur != -1)
  395. {
  396. if (!strcmp(tag, YSI_g_sINITagBuffer[cur][E_INI_TAGS_NAME], true))
  397. {
  398. return cur;
  399. }
  400. cur = YSI_g_sINITagBuffer[cur][E_INI_TAGS_NEXT];
  401. }
  402. return -1;
  403. }
  404. /*-------------------------------------------------------------------------*//**
  405. * <param name="slot">Slot to remove.</param>
  406. * <remarks>
  407. *
  408. * </remarks>
  409. *//*------------------------------------------------------------------------**/
  410. static stock INI_FreeEntry(slot)
  411. {
  412. new
  413. ret = YSI_g_sINIWriteBuffer[slot][E_INI_KV_ENTRY_NEXT];
  414. // Add this entry to the "free" list.
  415. return
  416. YSI_g_sINIWriteBuffer[slot][E_INI_KV_ENTRY_NEXT] = YSI_g_sINIWritePos,
  417. YSI_g_sINIWritePos = slot,
  418. ret;
  419. }
  420. static stock INI_DumpTag(File:buffer, curTag)
  421. {
  422. new
  423. curSlot = YSI_g_sINITagBuffer[curTag][E_INI_TAGS_START];
  424. while (0 <= curSlot < sizeof (YSI_g_sINIWriteBuffer))
  425. {
  426. fwrite(buffer, YSI_g_sINIWriteBuffer[curSlot][E_INI_KV_ENTRY_NAME]),
  427. fwrite(buffer, " = "),
  428. fwrite(buffer, YSI_g_sINIWriteBuffer[curSlot][E_INI_KV_ENTRY_TEXT]),
  429. fwrite(buffer, INI_NEW_LINE),
  430. curSlot = INI_FreeEntry(curSlot);
  431. }
  432. }
  433. /*stock INI_WriteArray(INI:file, const name[], data[], len)
  434. {
  435. // Write 6 bits at a time, in 3 cell chunks. It takes 16 bytes to record
  436. // three cells with 6 bits per byte.
  437. P:4("INI_WriteArray called");
  438. new
  439. dname[MAX_INI_ENTRY_NAME],
  440. write[Y_INI_WRITE_ARRAY_SIZE + 1],
  441. idx,
  442. wi,
  443. iter;
  444. // Write the length first just so the data exists.
  445. //INI_WriteInt(file, name, len);
  446. valstr(write, len),
  447. INI_AddToBuffer(file, name, write),
  448. write[0] = '\0';
  449. while (idx + 3 < len)
  450. {
  451. // Store all the data fast.
  452. write[wi++] = ((data[idx] & 0xFC000000) >>> 26) + '>',
  453. write[wi++] = ((data[idx] & 0x03F00000) >>> 20) + '>',
  454. write[wi++] = ((data[idx] & 0x000FC000) >>> 14) + '>',
  455. write[wi++] = ((data[idx] & 0x00003F00) >>> 8) + '>',
  456. write[wi++] = ((data[idx] & 0x000000FC) >>> 2) + '>',
  457. write[wi++] = (((data[idx] & 0x00000003) << 4) | ((data[idx + 1] & 0xF0000000) >>> 28)) + '>',
  458. ++idx,
  459. write[wi++] = ((data[idx] & 0x0FC00000) >>> 22) + '>',
  460. write[wi++] = ((data[idx] & 0x003F0000) >>> 16) + '>',
  461. write[wi++] = ((data[idx] & 0x0000FC00) >>> 10) + '>',
  462. write[wi++] = ((data[idx] & 0x000003F0) >>> 4) + '>',
  463. write[wi++] = (((data[idx] & 0x0000000F) << 2) | ((data[idx + 1] & 0xC0000000) >>> 30)) + '>',
  464. ++idx,
  465. write[wi++] = ((data[idx] & 0x3F000000) >>> 24) + '>',
  466. write[wi++] = ((data[idx] & 0x00FC0000) >>> 18) + '>',
  467. write[wi++] = ((data[idx] & 0x0003F000) >>> 12) + '>',
  468. write[wi++] = ((data[idx] & 0x00000FC0) >>> 6) + '>',
  469. write[wi++] = ((data[idx] & 0x0000003F) >>> 0) + '>',
  470. ++idx;
  471. if (wi == Y_INI_WRITE_ARRAY_SIZE)
  472. {
  473. format(dname, sizeof (dname), "@@%s-%d", name, iter++);
  474. P:5("Uvar_WriteArray: write %s = %s", dname, write);
  475. write[wi] = '\0',
  476. INI_AddToBuffer(file, dname, write),
  477. write[0] = '\0',
  478. wi = 0;
  479. }
  480. }
  481. // Do the straggling bytes.
  482. if (idx != len)
  483. {
  484. write[wi++] = ((data[idx] & 0xFC000000) >>> 26) + '>',
  485. write[wi++] = ((data[idx] & 0x03F00000) >>> 20) + '>',
  486. write[wi++] = ((data[idx] & 0x000FC000) >>> 14) + '>',
  487. write[wi++] = ((data[idx] & 0x00003F00) >>> 8) + '>',
  488. write[wi++] = ((data[idx] & 0x000000FC) >>> 2) + '>';
  489. if (++idx == len)
  490. {
  491. write[wi++] = ((data[idx - 1] & 0x00000003) << 4) + '>';
  492. }
  493. else
  494. {
  495. write[wi++] = (((data[idx - 1] & 0x00000003) << 4) | ((data[idx] & 0xF0000000) >>> 28)) + '>',
  496. write[wi++] = ((data[idx] & 0x0FC00000) >>> 22) + '>',
  497. write[wi++] = ((data[idx] & 0x003F0000) >>> 16) + '>',
  498. write[wi++] = ((data[idx] & 0x0000FC00) >>> 10) + '>',
  499. write[wi++] = ((data[idx] & 0x000003F0) >>> 4) + '>';
  500. if (++idx == len)
  501. {
  502. write[wi++] = ((data[idx - 1] & 0x0000000F) << 2) + '>';
  503. }
  504. else
  505. {
  506. write[wi++] = (((data[idx - 1] & 0x0000000F) << 2) | ((data[idx] & 0xC0000000) >>> 30)) + '>',
  507. write[wi++] = ((data[idx] & 0x3F000000) >>> 24) + '>',
  508. write[wi++] = ((data[idx] & 0x00FC0000) >>> 18) + '>',
  509. write[wi++] = ((data[idx] & 0x0003F000) >>> 12) + '>',
  510. write[wi++] = ((data[idx] & 0x00000FC0) >>> 6) + '>',
  511. write[wi++] = ((data[idx] & 0x0000003F) >>> 0) + '>';
  512. }
  513. }
  514. format(dname, sizeof (dname), "@@%s-%d", name, iter++);
  515. P:5("Uvar_WriteArray: write %s = %s", dname, write);
  516. write[wi] = '\0',
  517. INI_AddToBuffer(file, dname, write),
  518. write[0] = '\0',
  519. wi = 0;
  520. }
  521. return 1;
  522. }*/
  523. /*-------------------------------------------------------------------------*//**
  524. * <param name="file">INI file to write to.</param>
  525. * <param name="name">Data name to write.</param>
  526. * <param name="data">Data to write.</param>
  527. * <returns>
  528. * The slot written to, or -1 on failure.
  529. * </returns>
  530. * <remarks>
  531. * First checks the name doesn't already exist under the current tag header
  532. * and if it does overwrites the current value. If not checks there's room
  533. * in the buffer to write to and purges the buffer if not. Finally saves the
  534. * data in the buffer for writing when required and adds the data to the
  535. * relevant list for tag inclusion.
  536. * </remarks>
  537. *//*------------------------------------------------------------------------**/
  538. _Y_INI_STATIC stock INI_AddToBuffer(INI:file, const key[], const value[])
  539. {
  540. P:4("INI_AddToBuffer called: %i, \"%s\", \"%s\"", _:file, key, value);
  541. if (INI_IsValid(file) && key[0])
  542. {
  543. // Get the current tag.
  544. new
  545. curTag = YSI_g_sINICurrentTag[file];
  546. if (curTag == -1) curTag = INI_SetTag(file, INI_NO_TAG);
  547. else
  548. {
  549. // Find if this entry already exists.
  550. new
  551. curSlot = YSI_g_sINITagBuffer[curTag][E_INI_TAGS_START];
  552. if (curSlot == cellmax) return -1; // Deleted tag, don't save.
  553. else while (curSlot != -1)
  554. {
  555. if (!strcmp(key, YSI_g_sINIWriteBuffer[curSlot][E_INI_KV_ENTRY_NAME], true))
  556. {
  557. return
  558. strcpy(YSI_g_sINIWriteBuffer[curSlot][E_INI_KV_ENTRY_TEXT], value, MAX_INI_ENTRY_TEXT),
  559. curSlot;
  560. }
  561. curSlot = YSI_g_sINIWriteBuffer[curSlot][E_INI_KV_ENTRY_NEXT];
  562. }
  563. }
  564. // Get a new slot.
  565. if (YSI_g_sINIWritePos == -1)
  566. {
  567. // Free up some space.
  568. if (!INI_Flush()) return -1;
  569. curTag = YSI_g_sINICurrentTag[file];
  570. }
  571. // Get a free slot in the system.
  572. new
  573. newSlot = YSI_g_sINIWritePos;
  574. YSI_g_sINIWritePos = YSI_g_sINIWriteBuffer[YSI_g_sINIWritePos][E_INI_KV_ENTRY_NEXT],
  575. YSI_g_sINIWriteBuffer[newSlot][E_INI_KV_ENTRY_NEXT] = YSI_g_sINITagBuffer[curTag][E_INI_TAGS_START],
  576. YSI_g_sINITagBuffer[curTag][E_INI_TAGS_START] = newSlot;
  577. // Now write to it.
  578. return
  579. strcpy(YSI_g_sINIWriteBuffer[newSlot][E_INI_KV_ENTRY_NAME], key, MAX_INI_ENTRY_NAME),
  580. strcpy(YSI_g_sINIWriteBuffer[newSlot][E_INI_KV_ENTRY_TEXT], value, MAX_INI_ENTRY_TEXT),
  581. newSlot;
  582. }
  583. return -1;
  584. }
  585. static stock INI_FreeTag(INI:file, tag)
  586. {
  587. new
  588. prev = -1,
  589. curSlot = YSI_g_sINIStartTag[file];
  590. while (curSlot != tag)
  591. {
  592. curSlot = YSI_g_sINITagBuffer[(prev = curSlot)][E_INI_TAGS_NEXT];
  593. }
  594. if (prev == -1) YSI_g_sINIStartTag[file] = YSI_g_sINITagBuffer[curSlot][E_INI_TAGS_NEXT];
  595. else YSI_g_sINITagBuffer[prev][E_INI_TAGS_NEXT] = YSI_g_sINITagBuffer[curSlot][E_INI_TAGS_NEXT];
  596. // Release this tag slot.
  597. return
  598. prev = YSI_g_sINITagBuffer[curSlot][E_INI_TAGS_NEXT],
  599. YSI_g_sINITagBuffer[curSlot][E_INI_TAGS_NEXT] = YSI_g_sINITagPos,
  600. YSI_g_sINITagPos = curSlot,
  601. prev;
  602. }
  603. /*
  604. I8, 8 ,8I 88 88
  605. `8b d8b d8' "" ,d ""
  606. "8, ,8"8, ,8" 88
  607. Y8 8P Y8 8P 8b,dPPYba, 88 MM88MMM 88 8b,dPPYba, ,adPPYb,d8
  608. `8b d8' `8b d8' 88P' "Y8 88 88 88 88P' `"8a a8" `Y88
  609. `8a a8' `8a a8' 88 88 88 88 88 88 8b 88
  610. `8a8' `8a8' 88 88 88, 88 88 88 "8a, ,d88
  611. `8' `8' 88 88 "Y888 88 88 88 `"YbbdP"Y8
  612. aa, ,88
  613. "Y8bbdP"
  614. */
  615. static stock bool:INI_Flush()
  616. {
  617. for (new INI:i = INI:0; i != INI:INI_MAX_WRITES; ++i)
  618. {
  619. if (YSI_g_sINIWriteFile[i][0])
  620. {
  621. new
  622. curTag = YSI_g_sINICurrentTag[i],
  623. tag2[MAX_INI_TAG] = "\1\0";
  624. if (curTag != -1) strunpack(tag2, YSI_g_sINITagBuffer[curTag][E_INI_TAGS_NAME]);
  625. if (!INI_WriteBuffer(i)) return false;
  626. if (curTag != -1) INI_SetTag(i, tag2);
  627. }
  628. }
  629. return true;
  630. }
  631. /*-------------------------------------------------------------------------*//**
  632. * <param name="file">INI stream to write to file.</param>
  633. * <returns>
  634. * Success/fail.
  635. * </returns>
  636. * <remarks>
  637. * Opens the required file for reading and a temp file for read/writing. Goes
  638. * through the entire file reading all contained data. If it reaches a tag
  639. * line ([tag_name]) it dumps any unwritten data from the last tag (if there
  640. * was one) and starts processing the new tag. While a tag is being processed
  641. * every line is compared against the UNWRITTEN new data for that tag in the
  642. * buffer, if they're the same it writes the new data instead (it also writes
  643. * any comments which were after the data in the original line back), else it
  644. * writes the original line back.
  645. *
  646. * Once all the new data is written to the temp file any tags which haven't
  647. * been processed at all (i.e. were not found in the original file) are
  648. * written to the temp file along with all their data. The original file is
  649. * then destroyed and reopend and all the data copied out from the temp file
  650. * to the newly opened original file, closed and saved.
  651. * </remarks>
  652. *//*------------------------------------------------------------------------**/
  653. static stock bool:INI_WriteBuffer(INI:file)
  654. {
  655. if (!INI_IsValid(file)) return false;
  656. static
  657. sLine[MAX_INI_LINE];
  658. new
  659. File:buffer = ftemp();
  660. if (!buffer) return false;
  661. new
  662. File:source = fopen(YSI_g_sINIWriteFile[file], io_read);
  663. if (!source) return fclose(buffer), false;
  664. // Start the processing. Code again copied from "INI_ParseFile".
  665. new
  666. p0s, p0e, p1s, p1e, p2s, p2e,
  667. curTag = INI_GetTag(file, NULL), // Get the empty tag.
  668. // See if this tag should be saved or not.
  669. bool:deleted = INI_IsDeleted(curTag);
  670. while (fread(source, sLine))
  671. {
  672. switch (INI_IdentifyLineType(sLine, p0s, p0e, p1s, p1e, p2s, p2e))
  673. {
  674. case e_INI_LINE_TYPE_INVALID:
  675. P:I("Invalid line in INI file \"%s\": %s", YSI_g_sINIWriteFile[file], sLine);
  676. case e_INI_LINE_TYPE_DATALESS:
  677. if (!deleted) fwrite(buffer, sLine);
  678. case e_INI_LINE_TYPE_TAG:
  679. {
  680. // Clean up the previous tag.
  681. if (curTag != -1)
  682. {
  683. if (!deleted) INI_DumpTag(buffer, curTag);
  684. // Get rid of this tag (now empty).
  685. INI_FreeTag(file, curTag);
  686. }
  687. // Then get the new tag to write from.
  688. new
  689. ch = sLine[p0e];
  690. sLine[p0e] = '\0',
  691. curTag = INI_GetTag(file, sLine[p0s]);
  692. if (!(deleted = INI_IsDeleted(curTag)))
  693. {
  694. sLine[p0e] = ch,
  695. fwrite(buffer, sLine);
  696. }
  697. }
  698. default: // e_INI_LINE_TYPE_ENTRY and e_INI_LINE_TYPE_CONT.
  699. {
  700. if (deleted) continue; // Delete the entry.
  701. if (curTag == -1) fwrite(buffer, sLine); // No replacement data.
  702. else
  703. {
  704. new
  705. prev = -1,
  706. curSlot = YSI_g_sINITagBuffer[curTag][E_INI_TAGS_START];
  707. p0e -= p0s;
  708. while (curSlot != -1)
  709. {
  710. if (!strcmp(sLine[p0s], YSI_g_sINIWriteBuffer[curSlot][E_INI_KV_ENTRY_NAME], true, p0e))
  711. {
  712. fwrite(buffer, YSI_g_sINIWriteBuffer[curSlot][E_INI_KV_ENTRY_NAME]),
  713. fwrite(buffer, " = "),
  714. fwrite(buffer, YSI_g_sINIWriteBuffer[curSlot][E_INI_KV_ENTRY_TEXT]);
  715. // Write the end bits.
  716. if (p2s != p2e) INI_WriteComments();
  717. else fwrite(buffer, INI_NEW_LINE);
  718. break;
  719. }
  720. curSlot = YSI_g_sINIWriteBuffer[(prev = curSlot)][E_INI_KV_ENTRY_NEXT];
  721. }
  722. if (curSlot == -1) fwrite(buffer, sLine); // Not replaced.
  723. else
  724. {
  725. // Free this now written slot.
  726. if (prev == -1)
  727. {
  728. if ((YSI_g_sINITagBuffer[curTag][E_INI_TAGS_START] = YSI_g_sINIWriteBuffer[curSlot][E_INI_KV_ENTRY_NEXT]) == -1)
  729. {
  730. // No items left in the tag.
  731. INI_FreeTag(file, curTag),
  732. curTag = -1;
  733. // Don't reset "deleted" though.
  734. }
  735. }
  736. else YSI_g_sINIWriteBuffer[prev][E_INI_KV_ENTRY_NEXT] = YSI_g_sINIWriteBuffer[curSlot][E_INI_KV_ENTRY_NEXT];
  737. YSI_g_sINIWriteBuffer[curSlot][E_INI_KV_ENTRY_NEXT] = YSI_g_sINIWritePos,
  738. YSI_g_sINIWritePos = curSlot;
  739. }
  740. }
  741. }
  742. }
  743. // Don't put any code down here (at the end of the loop).
  744. }
  745. // FIRST write out the rest of the current tag.
  746. if (curTag != -1)
  747. {
  748. if (!deleted) INI_DumpTag(buffer, curTag);
  749. INI_FreeTag(file, curTag);
  750. }
  751. // THEN do the remaining new tags.
  752. curTag = YSI_g_sINIStartTag[file];
  753. while (curTag != -1)
  754. {
  755. if (YSI_g_sINITagBuffer[curTag][E_INI_TAGS_START] != cellmax)
  756. {
  757. new
  758. tag[32];
  759. // Write the tag's name.
  760. strunpack(tag, YSI_g_sINITagBuffer[curTag][E_INI_TAGS_NAME]);
  761. fwrite(buffer, "["),
  762. fwrite(buffer, tag),
  763. fwrite(buffer, "]" INI_NEW_LINE),
  764. INI_DumpTag(buffer, curTag);
  765. }
  766. curTag = INI_FreeTag(file, curTag);
  767. }
  768. // Done writing, copy the results over.
  769. fclose(source),
  770. fremove(YSI_g_sINIWriteFile[file]),
  771. fseek(buffer);
  772. if (!(source = fopen(YSI_g_sINIWriteFile[file], io_write))) return fclose(buffer), false;
  773. // Copy the buffer over.
  774. while (fread(buffer, sLine)) fwrite(source, sLine);
  775. return
  776. fclose(buffer),
  777. fclose(source),
  778. true;
  779. }