1
0

y_ini.inc 47 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479
  1. /**--------------------------------------------------------------------------**\
  2. ===========================
  3. Y Sever Includes - INI Core
  4. ===========================
  5. Description:
  6. Reads the INI and also exports a number of functions to other "classes" for
  7. easy reading of data files there.
  8. Legal:
  9. Version: MPL 1.1
  10. The contents of this file are subject to the Mozilla Public License Version
  11. 1.1 (the "License"); you may not use this file except in compliance with
  12. the License. You may obtain a copy of the License at
  13. http://www.mozilla.org/MPL/
  14. Software distributed under the License is distributed on an "AS IS" basis,
  15. WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
  16. for the specific language governing rights and limitations under the
  17. License.
  18. The Original Code is the YSI ini include.
  19. The Initial Developer of the Original Code is Alex "Y_Less" Cole.
  20. Portions created by the Initial Developer are Copyright (C) 2011
  21. the Initial Developer. All Rights Reserved.
  22. Contributors:
  23. ZeeX, koolk, JoeBullet/Google63, g_aSlice/Slice
  24. Thanks:
  25. JoeBullet/Google63 - Handy arbitrary ASM jump code using SCTRL.
  26. ZeeX - Very productive conversations.
  27. koolk - IsPlayerinAreaEx code.
  28. TheAlpha - Danish translation.
  29. breadfish - German translation.
  30. Fireburn - Dutch translation.
  31. yom - French translation.
  32. 50p - Polish translation.
  33. Zamaroht - Spanish translation.
  34. Dracoblue, sintax, mabako, Xtreme, other coders - Producing other modes
  35. for me to strive to better.
  36. Pixels^ - Running XScripters where the idea was born.
  37. Matite - Pestering me to release it and using it.
  38. Very special thanks to:
  39. Thiadmer - PAWN, whose limits continue to amaze me!
  40. Kye/Kalcor - SA:MP.
  41. SA:MP Team past, present and future - SA:MP.
  42. Version:
  43. 1.0
  44. Changelog:
  45. 08/09/10:
  46. Started adding sscanf and file plugin compatibility.
  47. Added tagless data at the start of a file (dini compatible).
  48. Added new INI:file[tag]() syntax.
  49. Added options to default file load.
  50. Fixed bugs in default file load configuration.
  51. Modified to be stand alone.
  52. 20/02/08:
  53. Added INI_RemoveEntry.
  54. 18/08/07:
  55. Fixed bug reading identifiers starting with a tag (i.e. names).
  56. Added local file reading for non-serverwide broadcasting.
  57. Added tag passing instead of tag based functions option.
  58. Increased default pool size.
  59. 30/07/07:
  60. Added auto creation of non-existant files.
  61. 13/07/07:
  62. Fixed INI writing to actually work.
  63. Added support for blank lines in INIs decently and quickly.
  64. 25/06/07:
  65. Altered file write options to use lists.
  66. Added buffer overwriting for updating values.
  67. 24/06/07:
  68. Added file write options.
  69. 21/06/07:
  70. Added INI_NEW_LINE for future writing functions.
  71. 20/06/07:
  72. Added support for an optional parameter in broadcastfunc data.
  73. 15/04/07:
  74. Updated for more whitespaces.
  75. Added INI comment code.
  76. Added support for value-less entries.
  77. Modified entry extraction to use end of name location parameter.
  78. Removed INI_GetTagName, now done via INI_GetEntryName.
  79. 14/04/07:
  80. Updated header documentation with more than changelog.
  81. 24/03/07:
  82. First version.
  83. Functions:
  84. Public:
  85. -
  86. Core:
  87. -
  88. Stock:
  89. INI_Load - Loads an INI file using standard features.
  90. INI_ParseFile - Loads a file as an ini and distributes data.
  91. INI_GetEntryName - Gets the name of an INI item.
  92. INI_GetEntryText - Gets the value of an INI item.
  93. INI_Open - Opens an INI for writing.
  94. INI_Close - Closes an INI being written to.
  95. INI_SetTag - Sets a subheading in an INI fo subsequent writes.
  96. INI_WriteString - Writes a string to an INI.
  97. INI_WriteInt - Writes an int to an INI.
  98. INI_WriteFloat - Writes a float to an INI.
  99. INI_WriteHex - Writes a hex to an INI.
  100. INI_WriteBin - Writes a binary to an INI.
  101. INI_WriteBool - Writes a boolean to an INI.
  102. INI_RemoveEntry - Remove an entry from a file.
  103. Static:
  104. INI_WriteBuffer - Writes an INI's buffer to the file.
  105. INI_AddToBuffer - Adds a string to an INI buffer.
  106. Inline:
  107. INI_Int - Parse an integer INI entry.
  108. INI_Float - Parse a float INI entry.
  109. INI_Hex - Parse a hex INI entry.
  110. INI_Bin - Parse a binary INI entry.
  111. INI_Bool - Parse a binary INI entry.
  112. INI_String - Parse a string INI entry.
  113. API:
  114. -
  115. Callbacks:
  116. -
  117. Definitions:
  118. MAX_INI_TAG - Maximum length of an INI tagname.
  119. MAX_INI_ENTRY_NAME - Maximum length of an INI entry name.
  120. MAX_INI_ENTRY_TEXT - Maximum length of an INI's entries' value.
  121. MAX_INI_LINE - Maximum length of a line in a file.
  122. INI_NEW_LINE - String for new lines.
  123. INI_MAX_WRITES - Maximum concurrent files open for writing.
  124. MAX_INI_TAGS - Number of tags the buffer can hold data for at once.
  125. Enums:
  126. E_INI_WRITE - Storage for entries to be written.
  127. E_INI_TAGS - Data for tags with data.
  128. Macros:
  129. INI_Parse - Header for ini parsing functions.
  130. Tags:
  131. INI - Handle to an INI file being written to.
  132. Variables:
  133. Global:
  134. -
  135. Static:
  136. YSI_g_sINIWriteBuffer - Basic data to be written.
  137. YSI_g_sINIWritePos - Next slot to write to.
  138. YSI_g_sINITagPos - Next slot to add a tag to.
  139. YSI_g_sINICurrentTag - Pointer to the tag been writen to.
  140. YSI_g_sINIWriteTag - Data for tags,
  141. YSI_g_sINIWriteFile - Current files been written to.
  142. Commands:
  143. -
  144. Compile options:
  145. -
  146. Operators:
  147. -
  148. </remarks>
  149. \**--------------------------------------------------------------------------**/
  150. // Ini file reading is not distributed. User file reading may be, though I
  151. // doubt it.
  152. #include "internal\y_version"
  153. //#tryinclude <sscanf>
  154. #include "y_utils"
  155. #include "y_bit"
  156. #include "y_debug"
  157. #include "y_inline"
  158. #include "internal\y_natives"
  159. #define MAX_INI_LINE (MAX_INI_ENTRY_NAME + MAX_INI_ENTRY_TEXT + 32)
  160. #define INI_NO_FILE (INI:-1)
  161. #if !defined INI_NEW_LINE
  162. #define INI_NEW_LINE "\r\n"
  163. #endif
  164. #if !defined MAX_INI_TAG
  165. #define MAX_INI_TAG (32)
  166. #endif
  167. #if !defined MAX_INI_ENTRY_NAME
  168. #define MAX_INI_ENTRY_NAME (32)
  169. #endif
  170. #if !defined MAX_INI_ENTRY_TEXT
  171. #define MAX_INI_ENTRY_TEXT YSI_MAX_STRING
  172. #endif
  173. #if !defined INI_MAX_WRITES
  174. // I doubt many people will use the multiple ini function.
  175. #define INI_MAX_WRITES (2)
  176. #endif
  177. #if !defined INI_BUFFER_SIZE
  178. #define INI_BUFFER_SIZE (64)
  179. #endif
  180. /*#if INI_BUFFER_SIZE <= 32
  181. #define INI_BUFFER_BITS 2
  182. #else
  183. #define INI_BUFFER_BITS Bit_Bits(INI_BUFFER_SIZE)
  184. #endif*/
  185. #if !defined MAX_INI_TAGS
  186. #define MAX_INI_TAGS 3
  187. /*#else
  188. #if MAX_INI_TAGS > 32
  189. #error Current code only supports up to 32 buffer tags
  190. #endif*/
  191. #endif
  192. //#define MAX_INI_STRING (64)
  193. #define MAX_INI_STRING FUNCTION_LENGTH
  194. #define Y_INI_WRITE_ARRAY_SIZE ((MAX_INI_ENTRY_TEXT - 1) / 16 * 16)
  195. enum E_INI_WRITE
  196. {
  197. E_INI_WRITE_NAME[MAX_INI_ENTRY_NAME],
  198. E_INI_WRITE_TEXT[MAX_INI_ENTRY_TEXT],
  199. E_INI_WRITE_NEXT
  200. }
  201. enum E_INI_TAGS
  202. {
  203. E_INI_TAGS_NAME[MAX_INI_TAG char],
  204. E_INI_TAGS_START,
  205. E_INI_TAGS_LAST
  206. }
  207. static stock
  208. YSI_g_sINIWriteBuffer[INI_MAX_WRITES][INI_BUFFER_SIZE][E_INI_WRITE],
  209. YSI_g_sINIWritePos[INI_MAX_WRITES],
  210. YSI_g_sINITagPos[INI_MAX_WRITES],
  211. YSI_g_sINICurrentTag[INI_MAX_WRITES],
  212. YSI_g_sINIWriteTag[INI_MAX_WRITES][MAX_INI_TAGS][E_INI_TAGS],
  213. YSI_g_sINIWriteFile[INI_MAX_WRITES][YSI_MAX_STRING];
  214. #define INI_Parse(%1,%2) \
  215. forward @INI_%1_%2(name[], value[]); \
  216. @INI_%1_%2(name[], value[])
  217. #define INI:%0[%1](%2) \
  218. forward @INI_%0_%1(%2); \
  219. @INI_%0_%1(%2)
  220. #if defined _inc_sscanf && 0
  221. /**----------------------------------------------------------------------**\
  222. <summary>INI_Int</summary>
  223. <param name=" name[]">Name of the INI textual identifier.</param>
  224. <param name=" function">Function to call with integer value.</param>
  225. <returns>
  226. function().
  227. </returns>
  228. <remarks>
  229. -
  230. </remarks>
  231. \**----------------------------------------------------------------------**/
  232. #define INI_Int(%1,%2) \
  233. if(!strcmp((%1),name,true)&&!sscanf(value,"d",%2))return;
  234. //if(!strcmp(){%2=floatstr(value);return;}
  235. //if (!strcmp((%1), name, true)) return %2(strval(value))
  236. /**----------------------------------------------------------------------**\
  237. <summary>INI_Float</summary>
  238. <param name=" name[]">Name of the INI textual identifier.</param>
  239. <param name=" function">Function to call with float value.</param>
  240. <returns>
  241. function().
  242. </returns>
  243. <remarks>
  244. -
  245. </remarks>
  246. \**----------------------------------------------------------------------**/
  247. #define INI_Float(%1,%2) \
  248. if(!strcmp((%1),name,true)&&!sscanf(value,"f",%2))return;
  249. //if (!strcmp((%1), name, true)) return %2(floatstr(value))
  250. /**----------------------------------------------------------------------**\
  251. <summary>INI_Hex</summary>
  252. <param name=" name[]">Name of the INI textual identifier.</param>
  253. <param name=" function">Function to call with hex value.</param>
  254. <returns>
  255. function().
  256. </returns>
  257. -
  258. </remarks>
  259. \**----------------------------------------------------------------------**/
  260. #define INI_Hex(%1,%2) \
  261. if(!strcmp((%1),name,true)&&!sscanf(value,"h",%2))return;
  262. //if (!strcmp((%1), name, true)) return %2(hexstr(value))
  263. /**----------------------------------------------------------------------**\
  264. <summary>INI_Bin</summary>
  265. <param name=" name[]">Name of the INI textual identifier.</param>
  266. <param name=" function">Function to call with binary value.</param>
  267. <returns>
  268. function().
  269. </returns>
  270. <remarks>
  271. -
  272. </remarks>
  273. \**----------------------------------------------------------------------**/
  274. #define INI_Bin(%1,%2) \
  275. if(!strcmp((%1),name,true)&&!sscanf(value,"l",%2))return;
  276. //if (!strcmp((%1), name, true)) return %2(binstr(value))
  277. /**----------------------------------------------------------------------**\
  278. <summary>INI_String</summary>
  279. <param name=" name[]">Name of the INI textual identifier.</param>
  280. <param name=" function">Function to call with string value.</param>
  281. <returns>
  282. function().
  283. </returns>
  284. <remarks>
  285. -
  286. </remarks>
  287. \**----------------------------------------------------------------------**/
  288. #define INI_String(%1,%2,%3) \
  289. if(!strcmp((%1),name,true)&&!sscanf(value,"s["#%3"]",%2))return;
  290. //if (!strcmp((%1), name, true)) return %2(value)
  291. #else
  292. #define INI_Int(%1,%2) \
  293. if(!strcmp((%1),name,true))return %2=strval(value)
  294. #define INI_Float(%1,%2) \
  295. if(!strcmp((%1),name,true))return _:(%2=floatstr(value))
  296. #define INI_Hex(%1,%2) \
  297. if(!strcmp((%1),name,true))return %2=hexstr(value)
  298. #define INI_Bin(%1,%2) \
  299. if(!strcmp((%1),name,true))return %2=binstr(value)
  300. #define INI_Bool(%1,%2) \
  301. if(!strcmp((%1),name,true))return %2=boolstr(value)
  302. #define INI_String(%1,%2,%3) \
  303. if(!strcmp((%1),name,true))return strcpy(%2,value,%3)
  304. #endif
  305. /**--------------------------------------------------------------------------**\
  306. <summary>INI_GetEntryName</summary>
  307. <param name="source">The string you want to get an entry name from.</param>
  308. <param name="dest">The place you want to store the entry name to</param>
  309. <returns>
  310. bool: Found the name correctly.
  311. </returns>
  312. <remarks>
  313. -
  314. </remarks>
  315. \**--------------------------------------------------------------------------**/
  316. stock bool:INI_GetEntryName(source[], dest[], &i)
  317. {
  318. P:3("bool:INI_GetEntryName called: \"%s\", %i, %i", source, dest, i);
  319. new
  320. j;
  321. while (source[j] && source[j] <= ' ') j++;
  322. P:7("bool:INI_GetEntryName: progress 0");
  323. i = j;
  324. while (source[i] > ' ' && source[i] != '=') i++;
  325. P:7("bool:INI_GetEntryName: progress 1");
  326. if (i == j) return false;
  327. P:7("bool:INI_GetEntryName: progress 2");
  328. i -= j;
  329. memcpy(dest, source, j, i * 4, MAX_INI_ENTRY_NAME);
  330. dest[i] = '\0';
  331. P:7("bool:INI_GetEntryName: progress 3, %s, %i, %s", dest, i, source);
  332. return true;
  333. }
  334. /**--------------------------------------------------------------------------**\
  335. <summary>INI_GetEntryText</summary>
  336. <param name="source">The string you want to get an entry from.</param>
  337. <param name="dest">The place you want to store the entry to</param>
  338. <returns>
  339. -
  340. </returns>
  341. <remarks>
  342. </remarks>
  343. \**--------------------------------------------------------------------------**/
  344. stock INI_GetEntryText(source[], dest[], i)
  345. {
  346. P:3("INI_GetEntryText called: \"%s\", %i, %i", source, dest, i);
  347. while ('\0' < source[i] <= ' ')
  348. {
  349. ++i;
  350. }
  351. P:7("bool:INI_GetEntryText: progress 1");
  352. if (source[i] == '=')
  353. {
  354. while ('\0' < source[++i] <= ' ')
  355. {
  356. // Do nothing.
  357. }
  358. }
  359. P:7("bool:INI_GetEntryText: progress 2");
  360. while (source[i] && (source[i] <= ' ' || source[i] == '=')) i++;
  361. //while (source[i] && (source[i] <= ' ' || source[i] == '=')) i++;
  362. P:7("bool:INI_GetEntryText: progress 3");
  363. dest[0] = 1;
  364. dest[1] = '\0';
  365. if (!source[i]) return;
  366. P:7("bool:INI_GetEntryText: progress 4, %s, %d, %s", dest, i, source);
  367. strcpy(dest, source[i], MAX_INI_ENTRY_TEXT);
  368. }
  369. /**--------------------------------------------------------------------------**\
  370. <summary>INI_ParseFile</summary>
  371. <param name="filename[]">The file to load.</param>
  372. <param name="remoteFormat[]">The format string to generate the remote function
  373. t pass the data to once loaded.</param>
  374. <param name="bool:bFileFirst">The order of the remoteFormat parameters.</param>
  375. <param name="bool:bExtra">Send additional data.</param>
  376. <param name="extra">Additional data to send.</param>
  377. <param name="bLocal">Call local functions instead of gloabal ones.</param>
  378. <param name="bPassTag">Pass the tag as an extra parameter not the function
  379. name.</param>
  380. <param name="bFilter">Apply the tag name filter to all tags or just prefixed
  381. ones?</param>
  382. <param name="filter">Text to use to search for which tags to load.</param>
  383. <returns>
  384. -
  385. </returns>
  386. <remarks>
  387. bFileFirst sets the order and inclusion of the possible remoteFormat
  388. parameters. If true the format will add the filename first then the
  389. current tag, if false the order will be reversed. This can also be used
  390. to exclude one or the other from the function name by setting the required
  391. parameter to be entered first and then only haing one %s in the format
  392. sting. The default order is tag first for languages compatability.
  393. </remarks>
  394. \**--------------------------------------------------------------------------**/
  395. stock bool:INI_ParseFile(fname[], remoteFormat[], bool:bFileFirst = false, bool:bExtra = false, extra = 0, bool:bLocal = true, bool:bPassTag = false, bool:bFilter = true, filter[] = "")
  396. {
  397. P:3("bool:INI_ParseFile called: \"%s\", \"%s\", %i, %i, %i, %i, %i", fname, remoteFormat, bFileFirst, bExtra, extra, bLocal, bPassTag);
  398. new
  399. File:f,
  400. filename[64],
  401. callbackFormat;
  402. if (bExtra)
  403. {
  404. if (bPassTag) callbackFormat = _F<isss>;
  405. else callbackFormat = _F<iss>;
  406. }
  407. else
  408. {
  409. if (bPassTag) callbackFormat = _F<sss>;
  410. else callbackFormat = _F<ss>;
  411. }
  412. strcpy(filename, fname, 64);
  413. if (!(f = fopen(filename, io_read))) return false;
  414. P:5("INI_ParseFile: open");
  415. new
  416. line[MAX_INI_LINE],
  417. tagName[MAX_INI_STRING] = "\1\0",
  418. function[MAX_INI_STRING],
  419. comment,
  420. pos,
  421. bool:bCallback,
  422. callback[E_CALLBACK_DATA];
  423. // Strip the extension from the filename.
  424. comment = chrfind('.', filename);
  425. if (comment != -1) filename[comment] = '\0';
  426. // Now reduce it to only the filename, no path.
  427. while (comment != -1)
  428. {
  429. if (filename[comment] == '\\' || filename[comment] == '/')
  430. {
  431. //strcpy(filename, filename[comment + 1], MAX_INI_STRING);
  432. ++comment;
  433. new
  434. i = 0;
  435. while ((filename[i] = filename[comment + i]))
  436. {
  437. ++i;
  438. }
  439. break;
  440. }
  441. --comment;
  442. }
  443. P:5("INI_ParseFile: filename only");
  444. // Set the default tag value.
  445. if (bFileFirst)
  446. {
  447. format(function, sizeof (function), remoteFormat, filename, "");
  448. }
  449. else
  450. {
  451. format(function, sizeof (function), remoteFormat, "", filename);
  452. }
  453. if (bLocal)
  454. {
  455. bCallback = bool:Callback_Get(callback_tag:function, callback, callbackFormat);
  456. }
  457. // Now read in the whole data.
  458. P:5("INI_ParseFile: parse");
  459. while (fread(f, line))
  460. {
  461. StripNL(line);
  462. if (!line[0]) continue;
  463. new
  464. stringIdent[MAX_INI_ENTRY_NAME];
  465. comment = chrfind(';', line);
  466. if (comment != -1)
  467. {
  468. line[comment] = '\0';
  469. }
  470. if (!INI_GetEntryName(line, stringIdent, pos))
  471. {
  472. continue;
  473. }
  474. if (stringIdent[0] == '[' && (comment = chrfind(']', stringIdent)) != -1 && !stringIdent[comment + 1])
  475. {
  476. // Got a tag, update the function to call.
  477. stringIdent[comment] = '\0';
  478. P:5("INI_ParseFile: Is checkable %d %d %d", stringIdent[1] == '@', stringIdent[2] == '@', (comment = chrfind('-', stringIdent)) != -1);
  479. if (stringIdent[1] == '@' && stringIdent[2] == '@' && (comment = chrfind('-', stringIdent)) != -1)
  480. {
  481. // We are keeping the ability to filter by tag names, but now
  482. // we are just using the existing unique tag load code in y_ini.
  483. stringIdent[comment] = '\0';
  484. P:5("INI_ParseFile: SingleTag check");
  485. if (strcmp(filter, stringIdent[3]))
  486. {
  487. P:5("INI_ParseFile: Not this time");
  488. // Skip tags we don't care about. This will not skip
  489. // anything if the passed tag is "" (here I'm (ab)using the
  490. // fact that "strcmp" returns 0 when an empty string is
  491. // involved).
  492. function[0] = '\0';
  493. continue;
  494. }
  495. stringIdent[comment] = '-';
  496. ++comment;
  497. }
  498. else
  499. {
  500. P:5("INI_ParseFile: SingleTag check");
  501. // Apply the filter to non-prefixed tags if desired.
  502. if (bFilter && strcmp(filter, stringIdent[1]))
  503. {
  504. P:5("INI_ParseFile: Not this time");
  505. // Skip tags we don't care about. This will not skip
  506. // anything if the passed tag is "" (here I'm (ab)using the
  507. // fact that "strcmp" returns 0 when an empty string is
  508. // involved).
  509. function[0] = '\0';
  510. continue;
  511. }
  512. comment = 1;
  513. }
  514. if (bFileFirst)
  515. {
  516. format(function, sizeof (function), remoteFormat, filename, stringIdent[comment]);
  517. }
  518. else
  519. {
  520. format(function, sizeof (function), remoteFormat, stringIdent[comment], filename);
  521. }
  522. if (bLocal)
  523. {
  524. bCallback = bool:Callback_Get(callback_tag:function, callback, callbackFormat);
  525. }
  526. P:5("INI_ParseFile: Call it \"%s\", %d, %d", function, bLocal, funcidx(function) == -1);
  527. // Skip complex checks where possible.
  528. if (bLocal && funcidx(function) == -1)
  529. {
  530. // This needs updating for inline functions.
  531. function[0] = '\0';
  532. }
  533. else
  534. {
  535. if (bPassTag)
  536. {
  537. strcpy(tagName, stringIdent[1], MAX_INI_STRING);
  538. }
  539. }
  540. }
  541. else if (function[0])
  542. {
  543. new
  544. stringText[MAX_INI_ENTRY_TEXT];
  545. INI_GetEntryText(line, stringText, pos);
  546. // Read in a value - distribute it as required.
  547. if (bCallback)
  548. {
  549. if (bExtra)
  550. {
  551. if (bPassTag)
  552. {
  553. Callback_Call(callback, extra, tagName, stringIdent, stringText);
  554. //CallLocalFunction(function, "isss", extra, tagName, stringIdent, stringText);
  555. }
  556. else
  557. {
  558. Callback_Call(callback, extra, stringIdent, stringText);
  559. //CallLocalFunction(function, "iss", extra, stringIdent, stringText);
  560. }
  561. }
  562. else
  563. {
  564. if (bPassTag)
  565. {
  566. Callback_Call(callback, tagName, stringIdent, stringText);
  567. //CallLocalFunction(function, "sss", tagName, stringIdent, stringText);
  568. }
  569. else
  570. {
  571. Callback_Call(callback, stringIdent, stringText);
  572. //CallLocalFunction(function, "ss", stringIdent, stringText);
  573. }
  574. }
  575. }
  576. else if (!bLocal)
  577. {
  578. // Can't use inilne functions remotely (it makes no sense at all
  579. // - inline functions come from the call point, and the call
  580. // point is in this local script.
  581. if (bExtra)
  582. {
  583. if (bPassTag)
  584. {
  585. CallRemoteFunction(function, "isss", extra, tagName, stringIdent, stringText);
  586. }
  587. else
  588. {
  589. CallRemoteFunction(function, "iss", extra, stringIdent, stringText);
  590. }
  591. }
  592. else
  593. {
  594. if (bPassTag)
  595. {
  596. CallRemoteFunction(function, "sss", tagName, stringIdent, stringText);
  597. }
  598. else
  599. {
  600. CallRemoteFunction(function, "ss", stringIdent, stringText);
  601. }
  602. }
  603. }
  604. }
  605. }
  606. fclose(f);
  607. return true;
  608. }
  609. /**--------------------------------------------------------------------------**\
  610. <summary>INI_Load</summary>
  611. <param name="filename[]">The file to load.</param>
  612. <param name="bool:bExtra">Send additional data.</param>
  613. <param name="extra">Additional data to send.</param>
  614. <param name="bLocal">Call local functions instead of gloabal ones.</param>
  615. <returns>
  616. INI_ParseFile
  617. </returns>
  618. <remarks>
  619. Wrapper for INI_ParseFile to use standard API features so people can
  620. worry even less. Designed for use with INI_Parse.
  621. </remarks>
  622. \**--------------------------------------------------------------------------**/
  623. stock bool:INI_Load(filename[], bool:bExtra = false, extra = 0, bool:bLocal = true)
  624. {
  625. P:3("bool:INI_Load called: \"%s\", %i, %i, %i", filename, _:bExtra, extra, _:bLocal);
  626. return INI_ParseFile(filename, "@INI_%s_%s", true, bExtra, extra, bLocal, false);
  627. }
  628. /**--------------------------------------------------------------------------**\
  629. <summary>INI_Open</summary>
  630. <param name="filename[]">INI file to open.</param>
  631. <returns>
  632. INI - handle to the file or INI_NO_FILE.
  633. </returns>
  634. <remarks>
  635. Doesn't actually open the file, just starts a new buffer if possible.
  636. </remarks>
  637. \**--------------------------------------------------------------------------**/
  638. stock INI:INI_Open(filename[])
  639. {
  640. if (ftouch(filename) == -1)
  641. {
  642. P:E("INI_Open could not find or create file %s", filename);
  643. }
  644. P:3("INI:INI_Open called: \"%s\"", filename);
  645. new
  646. i;
  647. for (i = 0; i < INI_MAX_WRITES; i++)
  648. {
  649. if (!YSI_g_sINIWriteFile[i][0]) break;
  650. }
  651. if (i == INI_MAX_WRITES)
  652. {
  653. return INI_NO_FILE;
  654. }
  655. strcpy(YSI_g_sINIWriteFile[i], filename, sizeof (YSI_g_sINIWriteFile[]));
  656. YSI_g_sINIWritePos[i] = 0;
  657. YSI_g_sINITagPos[i] = 0;
  658. // Reset tags.
  659. YSI_g_sINICurrentTag[i] = 0;
  660. YSI_g_sINIWriteTag[i][0][E_INI_TAGS_NAME][0] = '\0';
  661. YSI_g_sINIWriteTag[i][0][E_INI_TAGS_START] = -1;
  662. /* if (!fexist(filename))
  663. {
  664. new
  665. File:fHnd = fopen(filename, io_write);
  666. if (fHnd)
  667. {
  668. fclose(fHnd);
  669. }
  670. else P:E("Could not create target file");
  671. }*/
  672. return INI:i;
  673. }
  674. /**--------------------------------------------------------------------------**\
  675. <summary>INI_Close</summary>
  676. <param name="INI:file">Handle to the ini to close.</param>
  677. <returns>
  678. -
  679. </returns>
  680. <remarks>
  681. Writes any outstanding buffer data to the file and ends the stream.
  682. </remarks>
  683. \**--------------------------------------------------------------------------**/
  684. stock INI_Close(INI:file)
  685. {
  686. P:3("INI_Close called: %i", _:file);
  687. if (YSI_g_sINIWritePos[_:file] || YSI_g_sINITagPos[_:file]) INI_WriteBuffer(file);
  688. YSI_g_sINIWriteFile[_:file][0] = '\0';
  689. }
  690. /**--------------------------------------------------------------------------**\
  691. <summary>INI_SetTag</summary>
  692. <param name="INI:file">INI file handle to write to.</param>
  693. <param name="tag[]">Name of the new file subsection for subsequent data to write to.</param>
  694. <returns>
  695. -
  696. </returns>
  697. <remarks>
  698. Sets a new [tag] section header. Subsequent data is written under this
  699. header. Uses lists for constant tag switching and checks the tag doesn't
  700. already exist.
  701. </remarks>
  702. \**--------------------------------------------------------------------------**/
  703. stock INI_SetTag(INI:file, tag[])
  704. {
  705. P:3("INI_SetTag called: %i, \"%s\"", _:file, tag);
  706. if (file < INI:0 || file >= INI:INI_MAX_WRITES) return;
  707. new
  708. pos = YSI_g_sINITagPos[_:file];
  709. for (new i = 0; i < pos; i++)
  710. {
  711. if (YSI_g_sINIWriteTag[_:file][i][E_INI_TAGS_NAME][0] && !strcmp(tag, YSI_g_sINIWriteTag[_:file][i][E_INI_TAGS_NAME], true))
  712. {
  713. YSI_g_sINICurrentTag[_:file] = i;
  714. return;
  715. }
  716. }
  717. // Allow untagged data (not sure how it will go down...).
  718. /*if (pos == 0 && YSI_g_sINIWriteTag[_:file][0][E_INI_TAGS_START] != -1)
  719. {
  720. ++pos;
  721. ++YSI_g_sINITagPos[_:file];
  722. }*/
  723. if (pos >= MAX_INI_TAGS)
  724. {
  725. if (!INI_WriteBuffer(file)) return;
  726. // Loop.
  727. pos = 0;
  728. }
  729. //strcpy(YSI_g_sINIWriteTag[_:file][pos][E_INI_TAGS_NAME], tag, MAX_INI_TAG);
  730. strpack(YSI_g_sINIWriteTag[_:file][pos][E_INI_TAGS_NAME], tag, MAX_INI_TAG);
  731. YSI_g_sINIWriteTag[_:file][pos][E_INI_TAGS_START] = -1;
  732. YSI_g_sINICurrentTag[_:file] = pos;
  733. ++YSI_g_sINITagPos[_:file];
  734. }
  735. /**--------------------------------------------------------------------------**\
  736. <summary>INI_DeleteTag</summary>
  737. <param name="INI:file">INI file handle to write to.</param>
  738. <param name="tag[]">Name of the whole section to delete.</param>
  739. <returns>
  740. -
  741. </returns>
  742. <remarks>
  743. Removes a [tag] section from a file.
  744. </remarks>
  745. \**--------------------------------------------------------------------------**/
  746. stock INI_DeleteTag(INI:file, tag[])
  747. {
  748. P:3("INI_DeleteTag called: %i, \"%s\"", _:file, tag);
  749. if (file < INI:0 || file >= INI:INI_MAX_WRITES) return;
  750. new
  751. pos = YSI_g_sINITagPos[_:file];
  752. for (new i = 0; i < pos; i++)
  753. {
  754. P:6("INI_DeleteTag: %i, \"%s\", \"%s\"", i, tag, unpack(YSI_g_sINIWriteTag[_:file][i][E_INI_TAGS_NAME]));
  755. if (YSI_g_sINIWriteTag[_:file][i][E_INI_TAGS_NAME][0] && !strcmp(tag, YSI_g_sINIWriteTag[_:file][i][E_INI_TAGS_NAME], true))
  756. {
  757. YSI_g_sINICurrentTag[_:file] = i;
  758. YSI_g_sINIWriteTag[_:file][i][E_INI_TAGS_START] = cellmax;
  759. return;
  760. }
  761. }
  762. if (pos >= MAX_INI_TAGS)
  763. {
  764. if (!INI_WriteBuffer(file)) return;
  765. // Loop.
  766. pos = 0;
  767. }
  768. strpack(YSI_g_sINIWriteTag[_:file][pos][E_INI_TAGS_NAME], tag, MAX_INI_TAG);
  769. YSI_g_sINIWriteTag[_:file][pos][E_INI_TAGS_START] = cellmax;
  770. YSI_g_sINICurrentTag[_:file] = pos;
  771. ++YSI_g_sINITagPos[_:file];
  772. }
  773. stock INI_WriteArray(INI:file, const name[], data[], len)
  774. {
  775. // Write 6 bits at a time, in 3 cell chunks. It takes 16 bytes to record
  776. // three cells with 6 bits per byte.
  777. P:4("INI_WriteArray called");
  778. new
  779. dname[MAX_INI_ENTRY_NAME],
  780. write[Y_INI_WRITE_ARRAY_SIZE + 1],
  781. idx,
  782. wi,
  783. iter;
  784. // Write the length first just so the data exists.
  785. //INI_WriteInt(file, name, len);
  786. valstr(write, len);
  787. INI_AddToBuffer(file, name, write);
  788. write[0] = '\0';
  789. while (idx + 3 < len)
  790. {
  791. // Store all the data fast.
  792. //write[wi++] = (((data[idx] & 0xFC000000) >>> 2) | ((data[idx] & 0x03F00000) >> 4) | ((data[idx] & 0x000FC000) >> 6) | ((data[idx] & 0x00003F00) >> 8))
  793. write[wi++] = ((data[idx] & 0xFC000000) >>> 26) + '>';
  794. write[wi++] = ((data[idx] & 0x03F00000) >>> 20) + '>';
  795. write[wi++] = ((data[idx] & 0x000FC000) >>> 14) + '>';
  796. write[wi++] = ((data[idx] & 0x00003F00) >>> 8) + '>';
  797. //write[wi++] = (((data[idx - 1] & 0x000000FC) << 22) | ((data[idx - 1] & 0x00000003) << 20) | ((data[idx] & 0xF0000000) >>> 12) | ((data[idx] & 0x0FC00000) >> 14) | ((data[idx] & 0x003F0000) >> 16))
  798. write[wi++] = ((data[idx] & 0x000000FC) >>> 2) + '>';
  799. write[wi++] = (((data[idx] & 0x00000003) << 4) | ((data[idx + 1] & 0xF0000000) >>> 28)) + '>';
  800. ++idx;
  801. write[wi++] = ((data[idx] & 0x0FC00000) >>> 22) + '>';
  802. write[wi++] = ((data[idx] & 0x003F0000) >>> 16) + '>';
  803. //write[wi++] = (((data[idx] & 0x0000FC00) << 14) | ((data[idx] & 0x000003F0) << 12) | ((data[idx] & 0x0000000F) << 10) | ((data[idx + 1] & 0xC0000000) >>> 22) | ((data[idx + 1] & 0x3F000000) >> 24))
  804. write[wi++] = ((data[idx] & 0x0000FC00) >>> 10) + '>';
  805. write[wi++] = ((data[idx] & 0x000003F0) >>> 4) + '>';
  806. write[wi++] = (((data[idx] & 0x0000000F) << 2) | ((data[idx + 1] & 0xC0000000) >>> 30)) + '>';
  807. ++idx;
  808. write[wi++] = ((data[idx] & 0x3F000000) >>> 24) + '>';
  809. //write[wi++] = (((data[idx] & 0x00FC0000) << 6) | ((data[idx] & 0x0003F000) << 4) | ((data[idx] & 0x00000FC0) << 2) | ((data[idx] & 0x0000003F) << 0))
  810. write[wi++] = ((data[idx] & 0x00FC0000) >>> 18) + '>';
  811. write[wi++] = ((data[idx] & 0x0003F000) >>> 12) + '>';
  812. write[wi++] = ((data[idx] & 0x00000FC0) >>> 6) + '>';
  813. write[wi++] = ((data[idx] & 0x0000003F) >>> 0) + '>';
  814. ++idx;
  815. if (wi == Y_INI_WRITE_ARRAY_SIZE)
  816. {
  817. format(dname, sizeof (dname), "@@%s-%d", name, iter++);
  818. //printf("%s = %s", dname, write);
  819. P:5("Uvar_WriteArray: write %s = %s", dname, write);
  820. //Player_WriteString(dname, write);
  821. write[wi] = '\0';
  822. INI_AddToBuffer(file, dname, write);
  823. write[0] = '\0';
  824. wi = 0;
  825. }
  826. }
  827. // Do the straggling bytes.
  828. if (idx != len)
  829. {
  830. write[wi++] = ((data[idx] & 0xFC000000) >>> 26) + '>';
  831. write[wi++] = ((data[idx] & 0x03F00000) >>> 20) + '>';
  832. write[wi++] = ((data[idx] & 0x000FC000) >>> 14) + '>';
  833. write[wi++] = ((data[idx] & 0x00003F00) >>> 8) + '>';
  834. //write[wi++] = (((data[idx - 1] & 0x000000FC) << 22) | ((data[idx - 1] & 0x00000003) << 20) | ((data[idx] & 0xF0000000) >>> 12) | ((data[idx] & 0x0FC00000) >> 14) | ((data[idx] & 0x003F0000) >> 16))
  835. write[wi++] = ((data[idx] & 0x000000FC) >>> 2) + '>';
  836. if (++idx == len)
  837. {
  838. write[wi++] = ((data[idx - 1] & 0x00000003) << 4) + '>';
  839. }
  840. else
  841. {
  842. write[wi++] = (((data[idx - 1] & 0x00000003) << 4) | ((data[idx] & 0xF0000000) >>> 28)) + '>';
  843. write[wi++] = ((data[idx] & 0x0FC00000) >>> 22) + '>';
  844. write[wi++] = ((data[idx] & 0x003F0000) >>> 16) + '>';
  845. //write[wi++] = (((data[idx] & 0x0000FC00) << 14) | ((data[idx] & 0x000003F0) << 12) | ((data[idx] & 0x0000000F) << 10) | ((data[idx + 1] & 0xC0000000) >>> 22) | ((data[idx + 1] & 0x3F000000) >> 24))
  846. write[wi++] = ((data[idx] & 0x0000FC00) >>> 10) + '>';
  847. write[wi++] = ((data[idx] & 0x000003F0) >>> 4) + '>';
  848. if (++idx == len)
  849. {
  850. write[wi++] = ((data[idx - 1] & 0x0000000F) << 2) + '>';
  851. }
  852. else
  853. {
  854. write[wi++] = (((data[idx - 1] & 0x0000000F) << 2) | ((data[idx] & 0xC0000000) >>> 30)) + '>';
  855. //++idx;
  856. write[wi++] = ((data[idx] & 0x3F000000) >>> 24) + '>';
  857. //write[wi++] = (((data[idx] & 0x00FC0000) << 6) | ((data[idx] & 0x0003F000) << 4) | ((data[idx] & 0x00000FC0) << 2) | ((data[idx] & 0x0000003F) << 0))
  858. write[wi++] = ((data[idx] & 0x00FC0000) >>> 18) + '>';
  859. write[wi++] = ((data[idx] & 0x0003F000) >>> 12) + '>';
  860. write[wi++] = ((data[idx] & 0x00000FC0) >>> 6) + '>';
  861. write[wi++] = ((data[idx] & 0x0000003F) >>> 0) + '>';
  862. }
  863. }
  864. format(dname, sizeof (dname), "@@%s-%d", name, iter++);
  865. //printf("%s = %s", dname, write);
  866. P:5("Uvar_WriteArray: write %s = %s", dname, write);
  867. //Player_WriteString(dname, write);
  868. write[wi] = '\0';
  869. INI_AddToBuffer(file, dname, write);
  870. write[0] = '\0';
  871. wi = 0;
  872. }
  873. return 1;
  874. }
  875. /**--------------------------------------------------------------------------**\
  876. <summary>INI_AddToBuffer</summary>
  877. <param name="INI:file">INI file to write to.</param>
  878. <param name="name[]">Data name to write.</param>
  879. <param name="data[]">Data to write.</param>
  880. <returns>
  881. -
  882. </returns>
  883. <remarks>
  884. First checks the name doesn't already exist under the current tag header
  885. and if it does overwrites the current value. If not checks there's room
  886. in the buffer to write to and purges the buffer if not. Finally saves the
  887. data in the buffer for writing when required and adds the data to the
  888. relevant list for tag inclusion.
  889. </remarks>
  890. \**--------------------------------------------------------------------------**/
  891. static stock INI_AddToBuffer(INI:file, const name[], data[])
  892. {
  893. P:4("INI_AddToBuffer called: %i, \"%s\", \"%s\"", _:file, name, data);
  894. if (file < INI:0 || file >= INI:INI_MAX_WRITES)
  895. {
  896. return 0;
  897. }
  898. if (!YSI_g_sINITagPos[_:file])
  899. {
  900. // Tagless data.
  901. YSI_g_sINITagPos[_:file] = 1;
  902. YSI_g_sINIWriteTag[_:file][0][E_INI_TAGS_START] = -1;
  903. YSI_g_sINIWriteTag[_:file][0][E_INI_TAGS_NAME][0] = 0;
  904. }
  905. new
  906. pos = YSI_g_sINIWritePos[_:file],
  907. tmptag = YSI_g_sINICurrentTag[_:file],
  908. start = YSI_g_sINIWriteTag[_:file][tmptag][E_INI_TAGS_START];
  909. if (start == cellmax)
  910. {
  911. // Don't write to a deleted tag.
  912. return 0;
  913. }
  914. while (start != -1)
  915. {
  916. if (!strcmp(name, YSI_g_sINIWriteBuffer[_:file][start][E_INI_WRITE_NAME], true))
  917. {
  918. strcpy(YSI_g_sINIWriteBuffer[_:file][start][E_INI_WRITE_TEXT], data, MAX_INI_ENTRY_TEXT);
  919. //strpack(YSI_g_sINIWriteBuffer[_:file][start][E_INI_WRITE_TEXT], data, MAX_INI_ENTRY_TEXT);
  920. return 1;
  921. }
  922. start = YSI_g_sINIWriteBuffer[_:file][start][E_INI_WRITE_NEXT];
  923. }
  924. if (pos >= INI_BUFFER_SIZE)
  925. {
  926. if (!INI_WriteBuffer(file))
  927. {
  928. return 0;
  929. }
  930. INI_SetTag(file, YSI_g_sINIWriteTag[_:file][tmptag][E_INI_TAGS_NAME]);
  931. pos = 0;
  932. }
  933. new
  934. curtag = YSI_g_sINICurrentTag[_:file];
  935. if (YSI_g_sINIWriteTag[_:file][curtag][E_INI_TAGS_START] == -1)
  936. {
  937. YSI_g_sINIWriteTag[_:file][curtag][E_INI_TAGS_START] = pos;
  938. }
  939. else
  940. {
  941. YSI_g_sINIWriteBuffer[_:file][YSI_g_sINIWriteTag[_:file][curtag][E_INI_TAGS_LAST]][E_INI_WRITE_NEXT] = pos;
  942. }
  943. strcpy(YSI_g_sINIWriteBuffer[_:file][pos][E_INI_WRITE_NAME], name, MAX_INI_ENTRY_NAME);
  944. //strpack(YSI_g_sINIWriteBuffer[_:file][pos][E_INI_WRITE_NAME], name, MAX_INI_ENTRY_NAME);
  945. strcpy(YSI_g_sINIWriteBuffer[_:file][pos][E_INI_WRITE_TEXT], data, MAX_INI_ENTRY_TEXT);
  946. YSI_g_sINIWriteBuffer[_:file][pos][E_INI_WRITE_NEXT] = -1;
  947. YSI_g_sINIWriteTag[_:file][curtag][E_INI_TAGS_LAST] = pos;
  948. YSI_g_sINIWritePos[_:file]++;
  949. return 1;
  950. }
  951. /**--------------------------------------------------------------------------**\
  952. <summary>INI_RemoveEntry</summary>
  953. <param name="INI:file">File to write to.</param>
  954. <param name="name[]">Item to remove.</param>
  955. <returns>
  956. -
  957. </returns>
  958. <remarks>
  959. Wrapper for INI_AddToBuffer for removing data.
  960. </remarks>
  961. \**--------------------------------------------------------------------------**/
  962. stock INI_RemoveEntry(INI:file, name[])
  963. {
  964. P:3("INI_RemoveEntry called: %i, \"%s\"", _:file, name);
  965. static
  966. sData[2] = {-1, 0};
  967. INI_AddToBuffer(file, name, sData);
  968. }
  969. /**--------------------------------------------------------------------------**\
  970. <summary>INI_WriteString</summary>
  971. <param name="INI:file">File to write to.</param>
  972. <param name="name[]">Data name.</param>
  973. <param name="data[]">Data.</param>
  974. <returns>
  975. -
  976. </returns>
  977. <remarks>
  978. Wrapper for INI_AddToBuffer for strings.
  979. </remarks>
  980. \**--------------------------------------------------------------------------**/
  981. stock INI_WriteString(INI:file, name[], data[])
  982. {
  983. P:3("INI_WriteString called: %i, \"%s\", \"%s\"", _:file, name, data);
  984. INI_AddToBuffer(file, name, data);
  985. }
  986. /**--------------------------------------------------------------------------**\
  987. <summary>INI_WriteInt</summary>
  988. <param name="INI:file">File to write to.</param>
  989. <param name="name[]">Data name.</param>
  990. <param name="data">Integer data.</param>
  991. <returns>
  992. -
  993. </returns>
  994. <remarks>
  995. Wrapper for INI_AddToBuffer for integers.
  996. </remarks>
  997. \**--------------------------------------------------------------------------**/
  998. stock INI_WriteInt(INI:file, name[], data)
  999. {
  1000. P:3("INI_WriteInt called: %i, \"%s\", %i", _:file, name, data);
  1001. new
  1002. str[12];
  1003. valstr(str, data);
  1004. INI_AddToBuffer(file, name, str);
  1005. }
  1006. /**--------------------------------------------------------------------------**\
  1007. <summary>INI_WriteHex</summary>
  1008. <param name="INI:file">File to write to.</param>
  1009. <param name="name[]">Data name.</param>
  1010. <param name="data">Hex data.</param>
  1011. <returns>
  1012. -
  1013. </returns>
  1014. <remarks>
  1015. Wrapper for INI_AddToBuffer for integers to be written as hex values.
  1016. </remarks>
  1017. \**--------------------------------------------------------------------------**/
  1018. stock INI_WriteHex(INI:file, name[], data)
  1019. {
  1020. P:3("INI_WriteHex called: %i, \"%s\", %i", _:file, name, data);
  1021. static const
  1022. sc_values[] = !"0123456789ABCDEF";
  1023. new
  1024. str[11],
  1025. i = 9;
  1026. do
  1027. {
  1028. str[i--] = sc_values{data & 0x0F};
  1029. data >>>= 4;
  1030. }
  1031. while (data);
  1032. str[i--] = 'x';
  1033. str[i] = '0';
  1034. INI_AddToBuffer(file, name, str[i]);
  1035. }
  1036. /**--------------------------------------------------------------------------**\
  1037. <summary>INI_WriteBin</summary>
  1038. <param name="INI:file">File to write to.</param>
  1039. <param name="name[]">Data name.</param>
  1040. <param name="data">Binary data.</param>
  1041. <returns>
  1042. -
  1043. </returns>
  1044. <remarks>
  1045. Wrapper for INI_AddToBuffer for integers to be written as binary values.
  1046. </remarks>
  1047. \**--------------------------------------------------------------------------**/
  1048. stock INI_WriteBin(INI:file, name[], data)
  1049. {
  1050. P:3("INI_WriteBin called: %i, \"%s\", %i", _:file, name, data);
  1051. // Do four values at once for speed. This uses a packed string and unpacks
  1052. // it so that we can quickly write four values at once.
  1053. static const
  1054. sc_values[] = !"0000" "0001" "0010" "0011" "0100" "0101" "0110" "0111" "1000" "1001" "1010" "1011" "1100" "1101" "1110" "1111";
  1055. new
  1056. str[35],
  1057. i = 10;
  1058. do
  1059. {
  1060. str[--i] = sc_values[data & 0x0F];
  1061. data >>>= 4;
  1062. }
  1063. while (data);
  1064. // Convert the coalesced values to individual values.
  1065. strunpack(str[i], str[i], 33);
  1066. str[--i] = 'b';
  1067. str[--i] = '0';
  1068. INI_AddToBuffer(file, name, str[i]);
  1069. }
  1070. /**--------------------------------------------------------------------------**\
  1071. <summary>INI_WriteBool</summary>
  1072. <param name="INI:file">File to write to.</param>
  1073. <param name="name[]">Data name.</param>
  1074. <param name="data">Boolean data.</param>
  1075. <returns>
  1076. -
  1077. </returns>
  1078. <remarks>
  1079. Wrapper for INI_AddToBuffer for booleans.
  1080. </remarks>
  1081. \**--------------------------------------------------------------------------**/
  1082. stock INI_WriteBool(INI:file, name[], bool:data)
  1083. {
  1084. P:3("INI_WriteBool called: %i, \"%s\", %i", _:file, name, _:data);
  1085. if (data)
  1086. {
  1087. INI_AddToBuffer(file, name, "true");
  1088. }
  1089. else
  1090. {
  1091. INI_AddToBuffer(file, name, "false");
  1092. }
  1093. }
  1094. /**--------------------------------------------------------------------------**\
  1095. <summary>INI_WriteFloat</summary>
  1096. <param name="INI:file">File to write to.</param>
  1097. <param name="name[]">Data name.</param>
  1098. <param name="Float:data">Float data.</param>
  1099. <param name="accuracy">number of decimal places to write.</param>
  1100. <returns>
  1101. -
  1102. </returns>
  1103. <remarks>
  1104. Wrapper for INI_AddToBuffer for floats. Uses custom code instead of
  1105. format() as it's actually faster for something simple like this.
  1106. </remarks>
  1107. \**--------------------------------------------------------------------------**/
  1108. stock INI_WriteFloat(INI:file, name[], Float:data, accuracy = 6)
  1109. {
  1110. P:3("INI_WriteFloat called: %i, \"%s\", %f, %i", _:file, name, data, accuracy);
  1111. new
  1112. str[32];
  1113. format(str, sizeof (str), "%.*f", accuracy, data);
  1114. INI_AddToBuffer(file, name, str);
  1115. }
  1116. /**--------------------------------------------------------------------------**\
  1117. <summary>INI_WriteBuffer</summary>
  1118. <param name="INI:file">INI stream to write to file.</param>
  1119. <returns>
  1120. Success/fail.
  1121. </returns>
  1122. <remarks>
  1123. Opens the required file for reading and a temp file for read/writing. Goes
  1124. through the entire file reading all contained data. If it reaches a tag
  1125. line ([tag_name]) it dumps any unwritten data from the last tag (if there
  1126. was one) and starts processing the new tag. While a tag is being processed
  1127. every line is compared against the UNWRITTEN new data for that tag in the
  1128. buffer, if they're the same it writes the new data instead (it also writes
  1129. any comments which were after the data in the original line back), else it
  1130. writes the original line back.
  1131. Once all the new data is written to the temp file any tags which haven't
  1132. been processed at all (i.e. were not found in the original file) are
  1133. written to the temp file along with all their data. The original file is
  1134. then destroyed and reopend and all the data copied out from the temp file
  1135. to the newly opened original file, closed and saved.
  1136. </remarks>
  1137. \**--------------------------------------------------------------------------**/
  1138. static stock INI_WriteBuffer(INI:file)
  1139. {
  1140. P:4("INI_WriteBuffer called: %i", _:file);
  1141. if (_:file < 0 || _:file >= INI_MAX_WRITES) return 0;
  1142. new
  1143. //File:buffer = fopen("_temp_ysi_user_file_.ysi", io_write),
  1144. File:buffer = ftemp(),
  1145. File:source = fopen(YSI_g_sINIWriteFile[_:file], io_read);
  1146. if (buffer)
  1147. {
  1148. new
  1149. line[MAX_INI_LINE],
  1150. BitArray:read<INI_BUFFER_SIZE>, //:read[INI_BUFFER_BITS],
  1151. writing = 0,
  1152. skipTag = 0,
  1153. //Bit:tagswritten,
  1154. BitArray:tagswritten<MAX_INI_TAGS>,
  1155. tagpos = YSI_g_sINITagPos[_:file],
  1156. start = -1,
  1157. blank;
  1158. if (source)
  1159. {
  1160. // Write tagless data.
  1161. if (!YSI_g_sINIWriteTag[_:file][0][E_INI_TAGS_NAME][0])
  1162. {
  1163. start = YSI_g_sINIWriteTag[_:file][0][E_INI_TAGS_START];
  1164. // Will be -1 if there is no tagless data.
  1165. if (start != -1)
  1166. {
  1167. if (start == cellmax)
  1168. {
  1169. skipTag = 1;
  1170. start = -1;
  1171. }
  1172. else
  1173. {
  1174. writing = 1;
  1175. }
  1176. Bit_Set(tagswritten, 0, true);
  1177. }
  1178. }
  1179. while (fread(source, line))
  1180. {
  1181. new
  1182. pos = 1;
  1183. for (new i = 0; line[i]; i++)
  1184. {
  1185. if (line[i] == ';')
  1186. {
  1187. goto INI_WriteBuffer_cont1;
  1188. }
  1189. else if (line[i] > ' ')
  1190. {
  1191. // Determine that the line is not blank.
  1192. pos = 0;
  1193. break;
  1194. }
  1195. }
  1196. if (pos)
  1197. {
  1198. blank++;
  1199. continue;
  1200. }
  1201. if (line[0] == '[' && (pos = chrfind(']', line)) != -1 && endofline(line, pos + 1))
  1202. {
  1203. //pos--;
  1204. writing = 0;
  1205. new
  1206. form[MAX_INI_LINE];
  1207. // Reached a new tag - flush the rest of the last tag.
  1208. while (start != -1)
  1209. {
  1210. if (!Bit_GetBit(read, start))
  1211. {
  1212. if (YSI_g_sINIWriteBuffer[_:file][start][E_INI_WRITE_TEXT][0] != -1)
  1213. {
  1214. format(form, sizeof (form), "%s = %s" INI_NEW_LINE, YSI_g_sINIWriteBuffer[_:file][start][E_INI_WRITE_NAME], YSI_g_sINIWriteBuffer[_:file][start][E_INI_WRITE_TEXT]);
  1215. fwrite(buffer, form);
  1216. }
  1217. Bit_Set(read, start, true);
  1218. }
  1219. start = YSI_g_sINIWriteBuffer[_:file][start][E_INI_WRITE_NEXT];
  1220. }
  1221. if (!skipTag)
  1222. {
  1223. while (blank--) fwrite(buffer, INI_NEW_LINE);
  1224. }
  1225. blank = 0;
  1226. skipTag = 0;
  1227. // Note the start of the new tag.
  1228. line[pos] = '\0';
  1229. for (new j = 0; j < tagpos; j++)
  1230. {
  1231. //if (!Bit_Get(tagswritten, j) && !YSI_g_sINIWriteTag[_:file][j][E_INI_TAGS_NAME][pos] && !strcmp(YSI_g_sINIWriteTag[_:file][j][E_INI_TAGS_NAME], line[1], true, pos))
  1232. //printf("compare %d, %s, %s", Bit_Get(tagswritten, j), unpack(YSI_g_sINIWriteTag[_:file][j][E_INI_TAGS_NAME]), line[1]);
  1233. if (!Bit_Get(tagswritten, j) && !strcmp(YSI_g_sINIWriteTag[_:file][j][E_INI_TAGS_NAME], line[1])) //, true, pos))
  1234. {
  1235. /*if (YSI_g_sINIWriteTag[_:file][j][E_INI_TAGS_NAME])
  1236. {
  1237. continue;
  1238. }*/
  1239. start = YSI_g_sINIWriteTag[_:file][j][E_INI_TAGS_START];
  1240. P:7("INI_WriteBuffer: Next tag: %s, %d", line[1], start);
  1241. if (start == cellmax)
  1242. {
  1243. skipTag = 1;
  1244. start = -1;
  1245. writing = 0;
  1246. }
  1247. else
  1248. {
  1249. writing = 1;
  1250. }
  1251. //tagswritten |= Bit:(1 << j);
  1252. Bit_Set(tagswritten, j, true);
  1253. break;
  1254. }
  1255. }
  1256. line[pos--] = ']';
  1257. }
  1258. else if (writing)
  1259. {
  1260. new
  1261. name[MAX_INI_ENTRY_NAME],
  1262. temp,
  1263. liststart = start;
  1264. INI_GetEntryName(line, name, temp);
  1265. pos = chrfind(';', line, temp);
  1266. while (blank--) fwrite(buffer, INI_NEW_LINE);
  1267. blank = 0;
  1268. while (start != -1)
  1269. {
  1270. if (!Bit_GetBit(read, start) && !strcmp(name, YSI_g_sINIWriteBuffer[_:file][start][E_INI_WRITE_NAME]))
  1271. {
  1272. Bit_Set(read, start, true);//, INI_BUFFER_SIZE);
  1273. // Delete the current entry from the file, as per
  1274. // our request.
  1275. if (YSI_g_sINIWriteBuffer[_:file][start][E_INI_WRITE_TEXT][0] == -1)
  1276. {
  1277. // Can't use "continue" as we're in an inner
  1278. // loop.
  1279. goto INI_WriteBuffer_cont2;
  1280. }
  1281. if (pos != -1)
  1282. {
  1283. format(line, sizeof (line), "%s = %s %s", name, YSI_g_sINIWriteBuffer[_:file][start][E_INI_WRITE_TEXT], line[pos]);
  1284. }
  1285. else
  1286. {
  1287. format(line, sizeof (line), "%s = %s" INI_NEW_LINE, name, YSI_g_sINIWriteBuffer[_:file][start][E_INI_WRITE_TEXT]);
  1288. }
  1289. //start = -1;
  1290. break;
  1291. }
  1292. else
  1293. {
  1294. start = YSI_g_sINIWriteBuffer[_:file][start][E_INI_WRITE_NEXT];
  1295. }
  1296. }
  1297. /*if (start == -1)
  1298. {
  1299. // Wasn't found.
  1300. start = strlen(line);
  1301. if (line[start - 1] > ' ')
  1302. {
  1303. strcat(line, INI_NEW_LINE);
  1304. }
  1305. }*/
  1306. start = liststart;
  1307. }
  1308. INI_WriteBuffer_cont1:
  1309. if (!skipTag)
  1310. {
  1311. fwrite(buffer, line);
  1312. }
  1313. INI_WriteBuffer_cont2:
  1314. }
  1315. // Write any data from this tag group not in the original file.
  1316. // I.e. flush the final read tag.
  1317. while (start != -1)
  1318. {
  1319. if (!Bit_GetBit(read, start))
  1320. {
  1321. if (YSI_g_sINIWriteBuffer[_:file][start][E_INI_WRITE_TEXT][0] != -1)
  1322. {
  1323. format(line, sizeof (line), "%s = %s" INI_NEW_LINE, YSI_g_sINIWriteBuffer[_:file][start][E_INI_WRITE_NAME], YSI_g_sINIWriteBuffer[_:file][start][E_INI_WRITE_TEXT]);
  1324. fwrite(buffer, line);
  1325. }
  1326. }
  1327. start = YSI_g_sINIWriteBuffer[_:file][start][E_INI_WRITE_NEXT];
  1328. }
  1329. fclose(source);
  1330. }
  1331. // Write any tag groups not found in the original file.
  1332. //printf("write some tag");
  1333. for (new j = 0; j < tagpos; j++)
  1334. {
  1335. //if (!(tagswritten & Bit:(1 << j)))
  1336. //printf("write this tag: %s", unpack(YSI_g_sINIWriteTag[_:file][j][E_INI_TAGS_NAME]));
  1337. if (!Bit_Get(tagswritten, j))
  1338. {
  1339. start = YSI_g_sINIWriteTag[_:file][j][E_INI_TAGS_START];
  1340. if (start == cellmax)
  1341. {
  1342. continue;
  1343. }
  1344. if (j || YSI_g_sINIWriteTag[_:file][0][E_INI_TAGS_NAME][0])
  1345. {
  1346. strunpack(line, YSI_g_sINIWriteTag[_:file][j][E_INI_TAGS_NAME]);
  1347. format(line, sizeof (line), "[%s]" INI_NEW_LINE, line);
  1348. fwrite(buffer, line);
  1349. }
  1350. while (start != -1)
  1351. {
  1352. if (YSI_g_sINIWriteBuffer[_:file][start][E_INI_WRITE_TEXT][0] != -1)
  1353. {
  1354. format(line, sizeof (line), "%s = %s" INI_NEW_LINE, YSI_g_sINIWriteBuffer[_:file][start][E_INI_WRITE_NAME], YSI_g_sINIWriteBuffer[_:file][start][E_INI_WRITE_TEXT]);
  1355. fwrite(buffer, line);
  1356. }
  1357. start = YSI_g_sINIWriteBuffer[_:file][start][E_INI_WRITE_NEXT];
  1358. }
  1359. }
  1360. }
  1361. //fclose(buffer);
  1362. // This renames the file back (by copying all the data).
  1363. fremove(YSI_g_sINIWriteFile[_:file]);
  1364. #if defined _inc_TODO_COPY_INC
  1365. #else
  1366. #if defined _int_TODO_OTHER_COPY_INC
  1367. #else
  1368. source = fopen(YSI_g_sINIWriteFile[_:file], io_write);
  1369. //buffer = fopen("_temp_ysi_user_file_.ysi", io_read);
  1370. fseek(buffer);
  1371. if (source) // && buffer)
  1372. {
  1373. while (fread(buffer, line)) fwrite(source, line);
  1374. /*new
  1375. val;
  1376. printf("start: %d", _:E_INI_WRITE * INI_BUFFER_SIZE);
  1377. while ((val = fblockread(buffer, YSI_g_sINIWriteBuffer[_:file][0][E_INI_WRITE_NAME], _:E_INI_WRITE * INI_BUFFER_SIZE)))
  1378. {
  1379. printf("%d %d", val, strlen(YSI_g_sINIWriteBuffer[_:file][0][E_INI_WRITE_NAME]));
  1380. printf("%s", YSI_g_sINIWriteBuffer[_:file][0][E_INI_WRITE_NAME]);
  1381. fblockwrite(source, YSI_g_sINIWriteBuffer[_:file][0][E_INI_WRITE_NAME], val);
  1382. }*/
  1383. fclose(buffer);
  1384. fclose(source);
  1385. }
  1386. //fremove("_temp_ysi_user_file_.ysi");
  1387. #endif
  1388. #endif
  1389. // Reset the write buffer.
  1390. YSI_g_sINITagPos[_:file] = 0;
  1391. YSI_g_sINIWritePos[_:file] = 0;
  1392. YSI_g_sINICurrentTag[_:file] = 0;
  1393. return 1;
  1394. }
  1395. return 0;
  1396. }