NelsonC02

DOF2

Sep 29th, 2020
1,496
1
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
text 51.36 KB | None | 1 0
  1. #if defined _dof2_included
  2. #endinput
  3. #endif
  4. #define _dof2_included
  5.  
  6. #include <a_samp>
  7.  
  8. /*-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/
  9.  
  10. /*
  11. * This is a new version of the INI script Double-O-Files.
  12. * However, it's has completely been rewritten and has now a much better performance.
  13. * There is also the support for sections in the INI file. (But there is no support for comments.)
  14. * Double-O-Files 2 is compatible with DUDB, DINI, Double-O-Files and possibly y_ini since it
  15. * can handle sections and entry of the format "key = value", not only "key=value".
  16. * The number of spaces between the equal sign and key and value can actually be arbitrary.
  17. * I've added some comments below. You may see that I've mentioned the big-O-notation,
  18. * 'n' always Entries.Count.
  19. * Double-O-Files 2 should also be useful for Russian letter because I'm using
  20. * the functions fgetchar and fputchar to write and read the files.
  21. *
  22. * There is another new feature which has been inspired by ZCMD and y_ini:
  23. * The OnParseFile callbacks. To learn more about it, read the description in
  24. * the SA-MP forums if you haven't already.
  25. * THE END
  26. */
  27.  
  28. /*-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/
  29.  
  30. /*
  31. native DOF2_SetFile(file[]);
  32. native DOF2_LoadFile();
  33. native DOF2_SaveFile();
  34. native DOF2_ParseFile(file[],extraid,bool:callback=true);
  35. native DOF2_ReparseFile(file[],extraid,bool:callback=true);
  36. native DOF2_WriteFile();
  37. native DOF2_PrintFile(comment[]="");
  38. native DOF2_GetString(file[],key[],tag[]="");
  39. native DOF2_GetStringEx(file[],key[],result[],size,tag[]="");
  40. native Float:DOF2_GetFloat(file[],key[]);
  41. native DOF2_GetInt(file[],key[],tag[]="");
  42. native DOF2_GetHex(file[],key[],tag[]="");
  43. native DOF2_GetBin(file[],key[],tag[]="");
  44. native bool:DOF2_GetBool(file[],key[],tag[]="");
  45. native DOF2_SetString(file[],key[],value[],tag[]="");
  46. native DOF2_SetFloat(file[],key[],Float:value);
  47. native DOF2_SetInt(file[],key[],value,tag[]="");
  48. native DOF2_SetHex(file[],key[],value,tag[]="");
  49. native DOF2_SetBin(file[],key[],value,tag[]="");
  50. native DOF2_SetBool(file[],key[],bool:value,tag[]="");
  51. native DOF2_IsSet(file[],key[],tag[]="");
  52. native DOF2_Unset(file[],key[],tag[]="");
  53. native DOF2_FileExists(file[]);
  54. native DOF2_RemoveFile(file[]);
  55. native DOF2_CreateFile(file[],password[]="");
  56. native DOF2_RenameFile(oldfile[],newfile[]);
  57. native DOF2_RenameKey(file[],oldkey[],newkey[],tag[]="");
  58. native DOF2_CopyFile(filetocopy[],newfile[]);
  59. native DOF2_CheckLogin(file[],password[]);
  60. native DOF2_File(user[]);
  61. native DOF2_ParseInt();
  62. native DOF2_ParseFloat();
  63. native DOF2_ParseBool();
  64. native DOF2_ParseBin();
  65. native DOF2_ParseHex();
  66. native DOF2_SetUTF8(bool:set);
  67. native bool:DOF2_GetUTF8();
  68. native DOF2_GetFile();
  69. native DOF2_MakeBackup(file[]);
  70. native DOF2_RemoveSection (file [], tag []);
  71. native DOF2_SectionExists (file [], tag []);
  72. native DOF2_SortSection (file [], tag [], bool: ignorecase = true, bool: ascending = true);
  73. native DOF2_SortAllSections (file [], bool: ignorecase = true, bool: ascending = true);
  74. native DOF2_SetCaseSensitivity (bool: set);
  75. native DOF2_GetCaseSensitivity ();
  76. */
  77.  
  78. #define DOF2_TagExists DOF2_SectionExists
  79. #define DOF2_RemoveTag DOF2_RemoveSection
  80.  
  81. // OnParseFile <Tag><Key>(extraid, value [])
  82. // OnParseFile <><Key>(extraid, value [])
  83. // OnDefaultParseFile (extraid, value [], key [], tag [], file [])
  84.  
  85. // The arguments of your OnParseFile functions may have arbitrary names but must be an integer followed by a string.
  86. // Function must return a value.
  87. #define OnParseFile<%0><%1>(%2) \
  88. forward _OnParseFile_%0_%1 (extraid, value []); \
  89. public _OnParseFile_%0_%1 (extraid, value []) \
  90. return __OnParseFile_%0_%1 (extraid, (value [0] == '\1' && value [1] == '\0') ? ("") : value); \
  91. stock __OnParseFile_%0_%1 (%2)
  92.  
  93. // Also here: The argument names may be arbitrary but must be an integer followed by 4 strings.
  94. // Function must return a value.
  95. #define OnDefaultParseFile(%0) \
  96. forward _OnDefaultParseFile (extraid, value [], key [], tag [], file []); \
  97. public _OnDefaultParseFile (extraid, value [], key [], tag [], file []) \
  98. return __OnDefaultParseFile (extraid, (value [0] == '\1' && value [1] == '\0') ? ("") : value, key, (tag [0] == '\1' && tag [1] == '\0') ? ("") : tag, file); \
  99. stock __OnDefaultParseFile (%0)
  100.  
  101. #define DOF2_ParseBool() \
  102. (strval (value) || (value [0] && !strcmp (value, "true", true)))
  103.  
  104. #define DOF2_ParseInt() \
  105. (strval (value))
  106.  
  107. #define DOF2_ParseFloat() \
  108. (floatstr (value))
  109.  
  110. #define DOF2_ParseBin() \
  111. (DOF2_strbin (value))
  112.  
  113. #define DOF2_ParseHex() \
  114. (DOF2_strhex (value))
  115.  
  116. #define DOF2_LoadFile() \
  117. DOF2_ParseFile (CurrentFile, -1, false)
  118.  
  119. #define DOF2_SaveFile \
  120. DOF2_WriteFile
  121.  
  122. #define DOF2_FileExists \
  123. fexist
  124.  
  125. #define Sections. \
  126. Sections_
  127.  
  128. #define Entries. \
  129. Entries_
  130.  
  131. #define DOF2:: \
  132. DOF2_
  133.  
  134. #if !defined private
  135. #define private static stock
  136. #endif
  137.  
  138. #pragma dynamic 65536
  139.  
  140. /*
  141. #define MAX_SECTION_TAG (32)
  142. #define MAX_LINE_SIZE (128)
  143. #define MAX_SECTIONS (32)
  144. #define MAX_ENTRIES (256)
  145. #define MAX_FILE_SIZE (64)
  146.  
  147. #define USER_FILE_PATH "Users/%s.ini"
  148. */
  149.  
  150. // The maximum length of the name of a tag.
  151. #if !defined MAX_SECTION_TAG
  152. #define MAX_SECTION_TAG (32)
  153. #endif
  154.  
  155. // The maximum length of a line (including key and value).
  156. #if !defined MAX_LINE_SIZE
  157. #define MAX_LINE_SIZE (128)
  158. #endif
  159.  
  160. // The maximum number of sections which can be handled. Be careful: MUST NOT be higher than 255.
  161. #if !defined MAX_SECTIONS
  162. #define MAX_SECTIONS (32)
  163. #endif
  164.  
  165. // The maximum number of entries which can be loaded into the cache.
  166. #if !defined MAX_ENTRIES
  167. #define MAX_ENTRIES (256)
  168. #endif
  169.  
  170. // The maximum length of the name of a file.
  171. #if !defined MAX_FILE_SIZE
  172. #define MAX_FILE_SIZE (64)
  173. #endif
  174.  
  175. /*
  176. If PACK_CONTENT == true tag names and lines (key + value) will get stored in cache as packed strings.
  177. The result is less memory usage. However, you won't be able to use special characters like russian or chinese ones.
  178. */
  179. #if !defined PACK_CONTENT
  180. #define PACK_CONTENT (false)
  181. #endif
  182.  
  183. #define INVALID_ENTRY (-1)
  184. #define INVALID_SECTION (-1)
  185.  
  186. // Do you want to emulate DUDB?
  187. #if !defined DUDB_CONVERT && 0 // Change to 1 to enable.
  188. #define DUDB_CONVERT
  189. #endif
  190.  
  191. #if !defined USER_FILE_PATH
  192. #if defined DUDB_CONVERT
  193. #define USER_FILE_PATH "%s.dudb.sav"
  194. #else
  195. #define USER_FILE_PATH "%s.ini"
  196. #endif
  197. #endif
  198.  
  199. #if !defined USER_PW_HASH_KEY
  200. #if defined DUDB_CONVERT
  201. #define USER_PW_HASH_KEY "password_hash"
  202. #else
  203. #define USER_PW_HASH_KEY "password"
  204. #endif
  205. #endif
  206.  
  207.  
  208. // Do you want to emulate DINI?
  209. #if !defined DINI_CONVERT && 0 // Change to 1 to enable.
  210. #define DINI_CONVERT
  211. #endif
  212.  
  213. /*
  214. #if MAX_SECTIONS >= 256
  215. #error MAX_SECTIONS must not be greater than 255.
  216. #endif
  217. */
  218.  
  219. /*-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/
  220.  
  221. private
  222. bool: UseUTF8 = PACK_CONTENT,
  223. bool: CaseSensitive = false,
  224. CurrentFile [MAX_FILE_SIZE],
  225. bool: FileChanged,
  226. Sections.FirstEntry [MAX_SECTIONS] = {INVALID_ENTRY, ...},
  227. Sections.LastEntry [MAX_SECTIONS] = {INVALID_ENTRY, ...},
  228. Sections.Count,
  229. #if PACK_CONTENT == true
  230. Sections.Tag [MAX_SECTIONS][MAX_SECTION_TAG char],
  231. Entries.Line [MAX_ENTRIES][MAX_LINE_SIZE char],
  232. Entries.Tag [MAX_ENTRIES][MAX_SECTION_TAG char],
  233. #else
  234. Sections.Tag [MAX_SECTIONS][MAX_SECTION_TAG],
  235. Entries.Line [MAX_ENTRIES][MAX_LINE_SIZE],
  236. Entries.Tag [MAX_ENTRIES][MAX_SECTION_TAG],
  237. #endif
  238. #if MAX_SECTIONS >= 256
  239. Entries.Section [MAX_ENTRIES],
  240. #else
  241. Entries.Section [MAX_ENTRIES char],
  242. #endif
  243. Entries.NextEntry [MAX_ENTRIES] = {INVALID_ENTRY, ...},
  244. Entries.PreviousEntry [MAX_ENTRIES] = {INVALID_ENTRY, ...},
  245. Entries.Count,
  246. SortedEntryList [MAX_ENTRIES][2]; // Index 0: Hashcode, Index 1: EntryID
  247.  
  248. /*-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/
  249.  
  250. DOF2::Exit ()
  251. DOF2::WriteFile ();
  252.  
  253. stock DOF2::SetUTF8 (bool: set)
  254. UseUTF8 = set;
  255.  
  256. stock bool: DOF2::GetUTF8 ()
  257. return UseUTF8;
  258.  
  259. stock bool: DOF2::SetCaseSensitivity (bool: set)
  260. CaseSensitive = set;
  261.  
  262. stock bool: DOF2::GetCaseSensitivity ()
  263. return CaseSensitive;
  264.  
  265. stock DOF2::SetFile (file [])
  266. DOF2::strcpy (CurrentFile, file);
  267.  
  268. stock DOF2::GetFile ()
  269. return CurrentFile;
  270.  
  271. stock DOF2::CreateFile (file [], password [] = "")
  272. {
  273. if (!DOF2::FileExists (file))
  274. {
  275. new File: f = fopen (file, io_append);
  276.  
  277. if (fclose (f))
  278. {
  279. if (password [0])
  280. return DOF2::SetInt (file, USER_PW_HASH_KEY, DOF2::num_hash (password));
  281. return 1;
  282. }
  283. }
  284. return 0;
  285. }
  286.  
  287. stock DOF2::RenameFile (oldfile [], newfile [])
  288. {
  289. if (!DOF2::FileExists (newfile))
  290. {
  291. // If 'CurrentFile' is 'oldfile', write it if it has been changed.
  292. if (CurrentFile [0] && !strcmp (CurrentFile, oldfile) && FileChanged)
  293. DOF2::WriteFile ();
  294. else if (!DOF2::ParseFile (oldfile, -1, false)) // Otherwise parse 'oldfile'.
  295. return 0;
  296.  
  297. DOF2::SetFile (newfile);
  298. if (DOF2::WriteFile ())
  299. return fremove (oldfile);
  300. }
  301. return 0;
  302. }
  303.  
  304. stock DOF2::CopyFile (filetocopy [], newfile [])
  305. {
  306. if (!DOF2::FileExists (newfile))
  307. {
  308. if (CurrentFile [0] && !strcmp (CurrentFile, filetocopy) && FileChanged)
  309. DOF2::WriteFile ();
  310. else if(!DOF2::ParseFile (filetocopy, -1, false))
  311. return 0;
  312.  
  313. DOF2::SetFile (newfile);
  314. return DOF2::WriteFile ();
  315. }
  316. return 0;
  317. }
  318.  
  319. stock DOF2::RemoveFile (file [])
  320. {
  321. if (file [0])
  322. {
  323. if (CurrentFile [0] && !strcmp (CurrentFile, file))
  324. CurrentFile [0] = '\0';
  325. return fremove (file);
  326. }
  327. return 0;
  328. }
  329.  
  330. stock DOF2::MakeBackup (file [])
  331. {
  332. new
  333. year,
  334. month,
  335. day,
  336. hour,
  337. minute,
  338. second,
  339. backupfile [MAX_FILE_SIZE];
  340.  
  341. getdate (year, month, day);
  342. gettime (hour, minute, second);
  343. format (backupfile, sizeof (backupfile), "%s.%02d_%02d_%02d.%02d_%02d_%02d_%02d.bak", CurrentFile, month, day, year, hour, minute, second, GetTickCount ());
  344. return DOF2::CopyFile (CurrentFile, backupfile);
  345. }
  346.  
  347. stock bool: DOF2::SectionExists (file [], tag [])
  348. {
  349. if (file [0]) // You can't remove the empty Sections.
  350. {
  351. if (!tag [0])
  352. return true; // Emptry section always exists. In every file.
  353.  
  354. if (!CurrentFile [0] || strcmp (CurrentFile, file)) // No file in buffer or the file you want to read from is not the file in the buffer.
  355. if (!DOF2::ParseFile (file, -1, false))
  356. return false;
  357.  
  358. #if PACK_CONTENT == true
  359. new buf [MAX_SECTION_TAG];
  360. #endif
  361.  
  362. for (new i = 1; i < Sections.Count; ++i)
  363. {
  364. #if PACK_CONTENT == true
  365. strunpack (buf, Sections.Tag [i]);
  366. if (!strcmp (buf, tag, !CaseSensitive))
  367. return true;
  368. #else
  369. if (!strcmp (Sections.Tag [i], tag, !CaseSensitive))
  370. return true;
  371. #endif
  372. }
  373. }
  374. return false;
  375. }
  376.  
  377. stock DOF2::RemoveSection (file [], tag [])
  378. {
  379. // Removes tag 'tag' with all it's entries.
  380. if (file [0] && tag [0]) // You can't remove the empty Sections.
  381. {
  382. if (!CurrentFile [0] || strcmp (CurrentFile, file)) // No file in buffer or the file you want to read from is not the file in the buffer.
  383. if (!DOF2::ParseFile (file, -1, false))
  384. return 0;
  385.  
  386. new
  387. #if PACK_CONTENT == true
  388. line [MAX_LINE_SIZE],
  389. buf [MAX_SECTION_TAG],
  390. #endif
  391. section = INVALID_SECTION,
  392. entry,
  393. key [MAX_KEY_SIZE];
  394.  
  395. for (new i = 1; i < Sections.Count; ++i)
  396. {
  397. #if PACK_CONTENT == true
  398. strunpack (buf, Sections.Tag [i]);
  399. if (!strcmp (buf, tag, !CaseSensitive))
  400. {
  401. section = i;
  402. break;
  403. }
  404. #else
  405. if (!strcmp (Sections.Tag [i], tag, !CaseSensitive))
  406. {
  407. section = i;
  408. break;
  409. }
  410. #endif
  411. }
  412.  
  413. if (section != INVALID_SECTION)
  414. {
  415. entry = Sections.FirstEntry [section];
  416. while (entry != INVALID_ENTRY)
  417. {
  418. // Remove all entries under the current Sections.
  419. #if PACK_CONTENT == true
  420. strunpack (line, Entries.Line [entry]);
  421. DOF2::ParseLine (line, key, buf);
  422. #else
  423. DOF2::ParseLine (Entries.Line [entry], key, buf);
  424. #endif
  425. DOF2::Unset (file, key, tag);
  426. entry = Entries.NextEntry [entry];
  427. }
  428.  
  429. // Move the last tag to the position of the current tag. Creates a little mess.
  430. --Sections.Count;
  431. Sections.Tag [section] = Sections.Tag [Sections.Count];
  432. Sections.FirstEntry [section] = Sections.FirstEntry [Sections.Count];
  433. Sections.LastEntry [section] = Sections.LastEntry [Sections.Count];
  434.  
  435. // Adjust the tag IDs of the entries.
  436. entry = Sections.FirstEntry [section];
  437. while (entry != INVALID_ENTRY)
  438. {
  439. #if MAX_SECTIONS >= 256
  440. Entries.Section [entry] = section;
  441. #else
  442. Entries.Section {entry} = section;
  443. #endif
  444. entry = Entries.NextEntry [entry];
  445. }
  446. FileChanged = true;
  447. return 1;
  448. }
  449. }
  450. return 0;
  451. }
  452.  
  453. private DOF2::SearchEntry (key [], tag [], keybuf [], valbuf [], &pos, keybufsize = sizeof (keybuf), valbufsize = sizeof (valbuf))
  454. {
  455. if (key [0] && Entries.Count)
  456. {
  457. new
  458. entry = INVALID_ENTRY,
  459. l,
  460. m,
  461. r,
  462. h,
  463. #if PACK_CONTENT == true
  464. line [MAX_LINE_SIZE],
  465. buf [MAX_SECTION_TAG],
  466. #endif
  467. i;
  468.  
  469. h = DOF2::HashKey (key);
  470. l = 0;
  471. r = Entries.Count - 1;
  472.  
  473. /*
  474. * Binary search in a sorted list of entries in O(log n) time. This algorithm makes for example with 256 elements a maximum of ~8 steps until the entry is found if it exists.
  475. * A sequential search would take up to 256 steps. That was the case in the first Double-O-Files script.
  476. */
  477. while (l <= r)
  478. {
  479. if ((r - l) < 2)
  480. {
  481. if (h == SortedEntryList [l][0])
  482. {
  483. m = l;
  484. entry = SortedEntryList [l][1];
  485. }
  486. else if (r > l && h == SortedEntryList [r][0])
  487. {
  488. m = r;
  489. entry = SortedEntryList [r][1];
  490. }
  491. break;
  492. }
  493. else
  494. {
  495. m = l + (r - l) / 2;
  496. if (h == SortedEntryList [m][0])
  497. {
  498. entry = SortedEntryList [m][1];
  499. break;
  500. }
  501. else if (h > SortedEntryList [m][0])
  502. l = m + 1;
  503. else
  504. r = m - 1;
  505. }
  506. }
  507.  
  508. // Candidate found?
  509. if (entry != INVALID_ENTRY)
  510. {
  511. // Check if it's the entry we want.
  512. #if PACK_CONTENT == true
  513. strunpack (line, Entries.Line [entry]);
  514. DOF2::ParseLine (line, keybuf, valbuf, keybufsize, valbufsize);
  515. strunpack (buf, Entries.Tag [entry]);
  516. if (!strcmp (keybuf, key, !CaseSensitive) && ((!tag [0] && !buf [0]) || (tag [0] && buf [0] && !strcmp (tag, buf, !CaseSensitive))))
  517. #else
  518. DOF2::ParseLine (Entries.Line [entry], keybuf, valbuf, keybufsize, valbufsize);
  519. if (!strcmp (keybuf, key, !CaseSensitive) && ((!tag [0] && !Entries.Tag [entry][0]) || (tag [0] && Entries.Tag [entry][0] && !strcmp (tag, Entries.Tag [entry], !CaseSensitive))))
  520. #endif
  521. return (pos = m, entry);
  522. else
  523. {
  524. // If not, look left and right in the list for entries with the same hash code. This can be collisions or entries with the same key from another section.
  525. for (i = m - 1; i >= 0 && h == SortedEntryList [i][0]; --i)
  526. {
  527. entry = SortedEntryList [i][1];
  528. #if PACK_CONTENT == true
  529. strunpack (line, Entries.Line [entry]);
  530. DOF2::ParseLine (line, keybuf, valbuf, keybufsize, valbufsize);
  531. strunpack (buf, Entries.Tag [entry]);
  532. if (!strcmp (keybuf, key, !CaseSensitive) && ((!tag [0] && !buf [0]) || (tag [0] && buf [0] && !strcmp (tag, buf, !CaseSensitive))))
  533. #else
  534. DOF2::ParseLine (Entries.Line [entry], keybuf, valbuf, keybufsize, valbufsize);
  535. if (!strcmp (keybuf, key, !CaseSensitive) && ((!tag [0] && !Entries.Tag [entry][0]) || (tag [0] && Entries.Tag [entry][0] && !strcmp (tag, Entries.Tag [entry], !CaseSensitive))))
  536. #endif
  537. return (pos = i, entry);
  538. }
  539.  
  540. for (i = m + 1; i < Entries.Count && h == SortedEntryList [i][0]; ++i)
  541. {
  542. entry = SortedEntryList [i][1];
  543. #if PACK_CONTENT == true
  544. strunpack (line, Entries.Line [entry]);
  545. DOF2::ParseLine (line, keybuf, valbuf, keybufsize, valbufsize);
  546. strunpack (buf, Entries.Tag [entry]);
  547. if (!strcmp (keybuf, key, !CaseSensitive) && ((!tag [0] && !buf [0]) || (tag [0] && buf [0] && !strcmp (tag, buf, !CaseSensitive))))
  548. #else
  549. DOF2::ParseLine (Entries.Line [entry], keybuf, valbuf, keybufsize, valbufsize);
  550. if (!strcmp (keybuf, key, !CaseSensitive) && ((!tag [0] && !Entries.Tag [entry][0]) || (tag [0] && Entries.Tag [entry][0] && !strcmp (tag, Entries.Tag [entry], !CaseSensitive))))
  551. #endif
  552. return (pos = i, entry);
  553. }
  554. }
  555. }
  556. }
  557.  
  558. keybuf [0] = valbuf [0] = '\0';
  559. return INVALID_ENTRY;
  560. }
  561.  
  562. stock DOF2::SetString (file [], key [], value [], tag [] = "")
  563. {
  564. if (file [0] && key [0])
  565. {
  566. new
  567. entry,
  568. pos,
  569. section = INVALID_SECTION,
  570. keybuf [MAX_LINE_SIZE],
  571. valbuf [MAX_LINE_SIZE],
  572. #if PACK_CONTENT == true
  573. buf [MAX_SECTION_TAG],
  574. line [MAX_LINE_SIZE],
  575. #endif
  576. i;
  577.  
  578. if (!CurrentFile [0] || strcmp (CurrentFile, file)) // No file in buffer or the file you want to read from is not the file in the buffer.
  579. if (!DOF2::ParseFile (file, -1, false))
  580. return 0;
  581.  
  582. entry = DOF2::SearchEntry (key, tag, keybuf, valbuf, pos);
  583.  
  584. // If the entry has been found, just change it's content.
  585. if (entry != INVALID_ENTRY)
  586. {
  587. FileChanged = true;
  588. #if PACK_CONTENT == true
  589. format (line, sizeof (line), "%s = %s", key, value [0] ? value : ("(null)"));
  590. return strpack (Entries.Line [entry], line);
  591. #else
  592. format (Entries.Line [entry], sizeof (Entries.Line []), "%s = %s", key, value [0] ? value : ("(null)"));
  593. return 1;
  594. #endif
  595. }
  596.  
  597. if (Entries.Count >= MAX_ENTRIES)
  598. return 0;
  599.  
  600. // Search for the section where the entry belongs.
  601. if (!tag [0])
  602. section = 0;
  603. else
  604. {
  605. for (i = 1; i < Sections.Count; ++i)
  606. {
  607. #if PACK_CONTENT == true
  608. strunpack (buf, Sections.Tag [i]);
  609. if (buf [0] && !strcmp (tag, buf, !CaseSensitive))
  610. {
  611. section = i;
  612. break;
  613. }
  614. #else
  615. if (Sections.Tag [i][0] && !strcmp (tag, Sections.Tag [i], !CaseSensitive))
  616. {
  617. section = i;
  618. break;
  619. }
  620. #endif
  621. }
  622. }
  623.  
  624. // Section we want does not exist, create new one if possible.
  625. if (section == INVALID_SECTION)
  626. {
  627. if (Sections.Count >= MAX_SECTIONS)
  628. return 0;
  629.  
  630. section = Sections.Count++;
  631. #if PACK_CONTENT == true
  632. strpack (Sections.Tag [section], tag);
  633. #else
  634. DOF2::strcpy (Sections.Tag [section], tag);
  635. #endif
  636. Sections.FirstEntry [section] = Sections.LastEntry [section] = INVALID_ENTRY;
  637. }
  638.  
  639. // Add the entry to the section. Section's content is defined by a linear two way list.
  640. #if PACK_CONTENT == true
  641. format (line, sizeof (line), "%s = %s", key, value [0] ? value : ("(null)"));
  642. strpack (Entries.Line [Entries.Count], line);
  643. #else
  644. format (Entries.Line [Entries.Count], sizeof (Entries.Line []), "%s = %s", key, value [0] ? value : ("(null)"));
  645. #endif
  646. Entries.Tag [Entries.Count] = Sections.Tag [section];
  647. #if MAX_SECTIONS >= 256
  648. Entries.Section [Entries.Count] = section;
  649. #else
  650. Entries.Section {Entries.Count} = section;
  651. #endif
  652. Entries.NextEntry [Entries.Count] = INVALID_ENTRY;
  653.  
  654. // Add entry to sorted list of entries and move to right correct position in O(n) time.
  655. SortedEntryList [Entries.Count][0] = DOF2::HashKey (key);
  656. SortedEntryList [Entries.Count][1] = Entries.Count;
  657. i = Entries.Count - 1;
  658. while (i >= 0 && SortedEntryList [i][0] > SortedEntryList [i + 1][0])
  659. {
  660. DOF2::SwapSortedEntries (SortedEntryList [i], SortedEntryList [i + 1]);
  661. --i;
  662. }
  663.  
  664. if (Sections.LastEntry [section] == INVALID_ENTRY) // No entry in this section.
  665. {
  666. Sections.FirstEntry [section] = Sections.LastEntry [section] = Entries.Count;
  667. Entries.PreviousEntry [Entries.Count] = INVALID_ENTRY;
  668. }
  669. else
  670. {
  671. Entries.NextEntry [Sections.LastEntry [section]] = Entries.Count;
  672. Entries.PreviousEntry [Entries.Count] = Sections.LastEntry [section];
  673. Sections.LastEntry [section] = Entries.Count;
  674. }
  675. ++Entries.Count;
  676. FileChanged = true;
  677. }
  678. return 1;
  679. }
  680.  
  681. stock DOF2::GetString (file [], key [], tag [] = "")
  682. {
  683. new buf [MAX_LINE_SIZE];
  684. DOF2::GetStringEx (file, key, buf, sizeof (buf), tag);
  685. return buf;
  686. }
  687.  
  688. stock DOF2::GetStringEx (file [], key [], result [], size, tag [] = "")
  689. {
  690. if (file [0] && key [0])
  691. {
  692. new
  693. pos,
  694. keybuf [MAX_LINE_SIZE];
  695.  
  696. if (!CurrentFile [0] || strcmp (CurrentFile, file))
  697. {
  698. if (!DOF2::ParseFile (file, -1, false))
  699. {
  700. result [0] = '\0';
  701. return 0;
  702. }
  703. }
  704.  
  705. // Find entry and assign the result with it's value.
  706. return (DOF2::SearchEntry (key, tag, keybuf, result, pos, sizeof (keybuf), size) != INVALID_ENTRY);
  707. }
  708. return 0;
  709. }
  710.  
  711. stock DOF2::Unset (file [], key [], tag [] = "")
  712. {
  713. if (file [0] && key [0])
  714. {
  715. new
  716. entry,
  717. pos,
  718. keybuf [MAX_LINE_SIZE],
  719. valbuf [MAX_LINE_SIZE];
  720.  
  721. if (!CurrentFile [0] || strcmp (CurrentFile, file))
  722. if (!DOF2::ParseFile (file, -1, false))
  723. return 0;
  724.  
  725. if ((entry = DOF2::SearchEntry (key, tag, keybuf, valbuf, pos)) != INVALID_ENTRY)
  726. {
  727. // Remove entry from it's section.
  728. #if MAX_SECTIONS >= 256
  729. if (Sections.FirstEntry [Entries.Section [entry]] == entry) // Is the entry the first entry in the section? Make it's next entry the first entry.
  730. #else
  731. if (Sections.FirstEntry [Entries.Section {entry}] == entry)
  732. #endif
  733. {
  734. #if MAX_SECTIONS >= 256
  735. Sections.FirstEntry [Entries.Section [entry]] = Entries.NextEntry [entry];
  736. #else
  737. Sections.FirstEntry [Entries.Section {entry}] = Entries.NextEntry [entry];
  738. #endif
  739. if (Entries.NextEntry [entry] != INVALID_ENTRY)
  740. Entries.PreviousEntry [Entries.NextEntry [entry]] = INVALID_ENTRY;
  741. }
  742. else
  743. {
  744. Entries.NextEntry [Entries.PreviousEntry [entry]] = Entries.NextEntry [entry];
  745. if (Entries.NextEntry [entry] != INVALID_ENTRY)
  746. Entries.PreviousEntry [Entries.NextEntry [entry]] = Entries.PreviousEntry [entry];
  747. }
  748.  
  749. #if MAX_SECTIONS >= 256
  750. if (Sections.LastEntry [Entries.Section [entry]] == entry)
  751. #else
  752. if (Sections.LastEntry [Entries.Section {entry}] == entry)
  753. #endif
  754. {
  755. #if MAX_SECTIONS >= 256
  756. Sections.LastEntry [Entries.Section [entry]] = Entries.PreviousEntry [entry];
  757. #else
  758. Sections.LastEntry [Entries.Section {entry}] = Entries.PreviousEntry [entry];
  759. #endif
  760. if (Entries.PreviousEntry [entry] != INVALID_ENTRY)
  761. Entries.NextEntry [Entries.PreviousEntry [entry]] = INVALID_ENTRY;
  762. }
  763. else
  764. {
  765. Entries.PreviousEntry [Entries.NextEntry [entry]] = Entries.PreviousEntry [entry];
  766. if (Entries.PreviousEntry [entry] != INVALID_ENTRY)
  767. Entries.NextEntry [Entries.PreviousEntry [entry]] = Entries.NextEntry [entry];
  768. }
  769.  
  770. // Move the entry to the end of the sorted list and decrement Entries.Count to forget about the unset Entries.
  771. while (pos < (Entries.Count - 1))
  772. {
  773. DOF2::SwapSortedEntries (SortedEntryList [pos], SortedEntryList [pos + 1]);
  774. ++pos;
  775. }
  776. --Entries.Count;
  777. FileChanged = true;
  778. return 1;
  779. }
  780. }
  781. return 0;
  782. }
  783.  
  784. stock DOF2::RenameKey (file [], oldkey [], newkey [], tag [] = "")
  785. {
  786. if (file [0] && oldkey [0])
  787. {
  788. new
  789. entry,
  790. pos,
  791. #if PACK_CONTENT == true
  792. line [MAX_LINE_SIZE],
  793. #endif
  794. keybuf [MAX_LINE_SIZE],
  795. valbuf [MAX_LINE_SIZE];
  796.  
  797. if (!CurrentFile [0] || strcmp (CurrentFile, file))
  798. if (!DOF2::ParseFile (file, -1, false))
  799. return 0;
  800.  
  801. if ((entry = DOF2::SearchEntry (oldkey, tag, keybuf, valbuf, pos)) != INVALID_ENTRY)
  802. {
  803. // Change content of Entries.
  804. #if PACK_CONTENT == true
  805. format (line, sizeof (line), "%s = %s", newkey, valbuf [0] ? valbuf : ("(null)"));
  806. strpack (Entries.Line [entry], line);
  807. #else
  808. format (Entries.Line [entry], sizeof (Entries.Line []), "%s = %s", newkey, valbuf [0] ? valbuf : ("(null)"));
  809. #endif
  810.  
  811. // Because the hashcode has been changed, the entry has to move in the list.
  812. SortedEntryList [pos][0] = DOF2::HashKey (newkey);
  813. if (pos < (MAX_ENTRIES - 1) && SortedEntryList [pos][0] > SortedEntryList [pos + 1][0])
  814. {
  815. // Hash value of key is greater than the hash value of it's right neighbor, move to the right by swapping the 2 entries.
  816. while (pos < (MAX_ENTRIES - 1) && SortedEntryList [pos][0] > SortedEntryList [pos + 1][0])
  817. {
  818. DOF2::SwapSortedEntries (SortedEntryList [pos], SortedEntryList [pos + 1]);
  819. ++pos;
  820. }
  821. }
  822. else if (pos > 0 && SortedEntryList [pos][0] < SortedEntryList [pos + 1][0])
  823. {
  824. // Hash value of key is smaller than the hash value of it' left neighbor, move to the left by swapping the 2 entries.
  825. while (pos > 0 && SortedEntryList [pos][0] < SortedEntryList [pos - 1][0])
  826. {
  827. DOF2::SwapSortedEntries (SortedEntryList [pos], SortedEntryList [pos - 1]);
  828. --pos;
  829. }
  830. }
  831.  
  832. FileChanged = true;
  833. return 1;
  834. }
  835. }
  836. return 0;
  837. }
  838.  
  839. stock bool: DOF2::IsSet (file [], key [], tag [] = "")
  840. {
  841. new
  842. pos,
  843. keybuf [MAX_LINE_SIZE],
  844. valbuf [MAX_LINE_SIZE];
  845.  
  846. if (!CurrentFile [0] || strcmp (CurrentFile, file))
  847. if (!DOF2::ParseFile (file, -1, false))
  848. return false;
  849.  
  850. // Try to find the Entries.
  851. return (DOF2::SearchEntry (key, tag, keybuf, valbuf, pos) != INVALID_ENTRY);
  852. }
  853.  
  854. stock DOF2::SetInt (file [], key [], value, tag [] = "")
  855. {
  856. new buf [16];
  857. format (buf, sizeof (buf), "%d", value);
  858. return DOF2::SetString (file, key, buf, tag);
  859. }
  860.  
  861. stock DOF2::GetInt (file [], key [], tag [] = "")
  862. {
  863. new buf [16];
  864. DOF2::GetStringEx (file, key, buf, sizeof (buf), tag);
  865. return strval (buf);
  866. }
  867.  
  868. stock DOF2::SetHex (file [], key [], value, tag [] = "")
  869. {
  870. new buf [16];
  871. DOF2::hexstr (value, buf);
  872. return DOF2::SetString (file, key, buf, tag);
  873. }
  874.  
  875. stock DOF2::GetHex (file [], key [], tag [] = "")
  876. {
  877. new buf [16];
  878. DOF2::GetStringEx (file, key, buf, sizeof (buf), tag);
  879. return DOF2::strhex (buf);
  880. }
  881.  
  882. stock DOF2::SetBin (file [], key [], value, tag [] = "")
  883. {
  884. new buf [35];
  885. DOF2::binstr (value, buf);
  886. return DOF2::SetString (file, key, buf, tag);
  887. }
  888.  
  889. stock DOF2::GetBin (file [], key [], tag [] = "")
  890. {
  891. new buf [35];
  892. DOF2::GetStringEx (file, key, buf, sizeof (buf), tag);
  893. return DOF2::strbin (buf);
  894. }
  895.  
  896. stock DOF2::SetFloat (file [], key [], Float: value, tag [] = "")
  897. {
  898. new buf [32];
  899. format (buf, sizeof (buf), "%.8f", value);
  900. return DOF2::SetString (file, key, buf, tag);
  901. }
  902.  
  903. stock Float: DOF2::GetFloat (file [], key [], tag [] = "")
  904. {
  905. new buf [32];
  906. DOF2::GetStringEx (file, key, buf, sizeof (buf), tag);
  907. return floatstr (buf);
  908. }
  909.  
  910. stock bool: DOF2::GetBool (file [], key [], tag [] = "")
  911. {
  912. new buf [16];
  913. DOF2::GetStringEx (file, key, buf, sizeof (buf), tag);
  914. return (strval (buf) || (buf [0] && !strcmp (buf, "true", true)));
  915. }
  916.  
  917. stock DOF2::SetBool (file [], key [], bool: value, tag [] = "")
  918. return DOF2::SetString (file, key, value ? ("true") : ("false"), tag);
  919.  
  920. /*-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/
  921.  
  922. stock DOF2::PrintFile (comment [] = "")
  923. {
  924. if (CurrentFile [0])
  925. {
  926. new
  927. bool: firstline = true,
  928. entry,
  929. #if PACK_CONTENT == true
  930. buf [MAX_LINE_SIZE],
  931. #endif
  932. entries,
  933. i;
  934.  
  935. printf ("[DOF] Current file: %s", CurrentFile);
  936. for ( ; i < Sections.Count; ++i)
  937. {
  938. if (i)
  939. {
  940. if (!firstline)
  941. print (" ");
  942. else
  943. firstline = false;
  944. #if PACK_CONTENT == true
  945. strunpack (buf, Sections.Tag [i]);
  946. printf ("[%s]", buf);
  947. #else
  948. printf ("[%s]", Sections.Tag [i]);
  949. #endif
  950. }
  951. entry = Sections.FirstEntry [i];
  952. while (entry != INVALID_ENTRY)
  953. {
  954. #if PACK_CONTENT == true
  955. strunpack (buf, Entries.Line [entry]);
  956. print (buf);
  957. #else
  958. print (Entries.Line [entry]);
  959. #endif
  960. entry = Entries.NextEntry [entry];
  961. firstline = false;
  962. ++entries;
  963. }
  964. }
  965. printf ("* %d sections, %d entries", i, entries);
  966. if (comment [0])
  967. printf ("* Comment: %s", comment);
  968. return 1;
  969. }
  970. return 0;
  971. }
  972.  
  973. stock DOF2::WriteFile ()
  974. {
  975. if (CurrentFile [0])
  976. {
  977. new
  978. File: f = fopen (CurrentFile, io_write),
  979. bool: firstline = true,
  980. entry;
  981.  
  982. if (f)
  983. {
  984. for (new i; i < Sections.Count; ++i)
  985. {
  986. if (Sections.FirstEntry [i] != INVALID_ENTRY) // Do not write when empty.
  987. {
  988. if (i)
  989. {
  990. if (!firstline)
  991. {
  992. fputchar (f, '\r', UseUTF8);
  993. fputchar (f, '\n', UseUTF8);
  994. }
  995. else
  996. firstline = false;
  997. fputchar (f, '[', UseUTF8);
  998. fwritechars (f, Sections.Tag [i]);
  999. fputchar (f, ']', UseUTF8);
  1000. fputchar (f, '\r', UseUTF8);
  1001. fputchar (f, '\n', UseUTF8);
  1002. }
  1003.  
  1004. entry = Sections.FirstEntry [i];
  1005. while (entry != INVALID_ENTRY)
  1006. {
  1007. fwritechars (f, Entries.Line [entry]);
  1008. fputchar (f, '\r', UseUTF8);
  1009. fputchar (f, '\n', UseUTF8);
  1010. entry = Entries.NextEntry [entry];
  1011. firstline = false;
  1012. }
  1013. }
  1014. }
  1015. FileChanged = false;
  1016. return fclose (f);
  1017. }
  1018. }
  1019. return 0;
  1020. }
  1021.  
  1022. stock DOF2::ParseFile (file [], extraid = -1, bool: callback = false)
  1023. {
  1024. if (file [0] && DOF2::FileExists (file))
  1025. {
  1026. /*
  1027. Write the file in the buffer when:
  1028. - There is actually a file in the buffer
  1029. - The file in the buffer is not the file you want to parse and this file has been changed.
  1030. - Or the current file is the file you want to and has been changed.
  1031. */
  1032. //if (CurrentFile [0] && ((strcmp (CurrentFile, file) && FileChanged) || FileChanged))
  1033. if (CurrentFile [0] && FileChanged) // Equal to the query above but shorter.
  1034. DOF2::WriteFile ();
  1035.  
  1036. new
  1037. File: f = fopen (file, io_readwrite),
  1038. buf [MAX_LINE_SIZE],
  1039. #if PACK_CONTENT == true
  1040. line [MAX_LINE_SIZE char],
  1041. tag [MAX_SECTION_TAG],
  1042. #else
  1043. line [MAX_LINE_SIZE],
  1044. #endif
  1045. key [MAX_LINE_SIZE],
  1046. value [MAX_LINE_SIZE],
  1047. c,
  1048. pos;
  1049.  
  1050. if (f)
  1051. {
  1052. FileChanged = false;
  1053. DOF2::SetFile (file);
  1054.  
  1055. Sections.Count = 1;
  1056. Entries.Count = 0;
  1057. Sections.FirstEntry [0] = Sections.LastEntry [0] = INVALID_ENTRY;
  1058.  
  1059. for (new i, size = flength (f); i < size; ++i)
  1060. {
  1061. c = fgetchar (f, 0, UseUTF8);
  1062. if (pos == MAX_LINE_SIZE - 1 || c == '\n' || c == '\r')
  1063. c = '\0';
  1064. #if PACK_CONTENT == true
  1065. line {pos++} = c;
  1066. #else
  1067. line [pos++] = c;
  1068. #endif
  1069.  
  1070. if (c == '\0')
  1071. {
  1072. // A new section found. Add the section to the list of sections.
  1073. #if PACK_CONTENT == true
  1074. if (line {0} == '[')
  1075. #else
  1076. if (line [0] == '[')
  1077. #endif
  1078. {
  1079. if (Sections.Count < MAX_SECTIONS)
  1080. {
  1081. pos = 1;
  1082. #if PACK_CONTENT == true
  1083. while (line {pos} && line {pos} != ']' && (pos - 1) < MAX_SECTION_TAG)
  1084. {
  1085. Sections.Tag [Sections.Count]{pos - 1} = line {pos};
  1086. ++pos;
  1087. }
  1088. Sections.Tag [Sections.Count]{pos - 1} = '\0';
  1089. #else
  1090. while (line [pos] && line [pos] != ']' && (pos - 1) < MAX_SECTION_TAG)
  1091. {
  1092. Sections.Tag [Sections.Count][pos - 1] = line [pos];
  1093. ++pos;
  1094. }
  1095. Sections.Tag [Sections.Count][pos - 1] = '\0';
  1096. #endif
  1097. Sections.FirstEntry [Sections.Count] = Sections.LastEntry [Sections.Count] = INVALID_ENTRY;
  1098. ++Sections.Count;
  1099. }
  1100. }
  1101. else
  1102. {
  1103. #if PACK_CONTENT == true
  1104. if (line {0})
  1105. #else
  1106. if (line [0])
  1107. #endif
  1108. {
  1109. #if PACK_CONTENT == true
  1110. strunpack (buf, line);
  1111. DOF2::ParseLine (buf, key, value);
  1112. strunpack (tag, Sections.Tag [Sections.Count - 1]);
  1113.  
  1114. // Call a specific function for a specific entry - ZCMD-style!
  1115. if (callback)
  1116. {
  1117. format (buf, sizeof (buf), "_OnParseFile_%s_%s", tag, key);
  1118. if (!CallRemoteFunction (buf, "is", extraid, value))
  1119. CallRemoteFunction ("_OnDefaultParseFile", "issss", extraid, value [0] ? value : ("\1"), key, Sections.Tag [Sections.Count - 1][0] ? Sections.Tag [Sections.Count - 1] : ("\1"), file);
  1120. }
  1121. #else
  1122. DOF2::ParseLine (line, key, value);
  1123.  
  1124. // Call a specific function for a specific entry - ZCMD-style!
  1125. if (callback)
  1126. {
  1127. format (buf, sizeof (buf), "_OnParseFile_%s_%s", Sections.Tag [Sections.Count - 1], key);
  1128. if (!CallRemoteFunction (buf, "is", extraid, value))
  1129. CallRemoteFunction ("_OnDefaultParseFile", "issss", extraid, value [0] ? value : ("\1"), key, Sections.Tag [Sections.Count - 1][0] ? Sections.Tag [Sections.Count - 1] : ("\1"), file);
  1130. }
  1131. #endif
  1132.  
  1133. // Add entry to it's section and to the list which will be sorted.
  1134. Entries.Line [Entries.Count] = line;
  1135. Entries.Tag [Entries.Count] = Sections.Tag [Sections.Count - 1];
  1136. #if MAX_SECTIONS >= 256
  1137. Entries.Section [Entries.Count] = Sections.Count - 1;
  1138. #else
  1139. Entries.Section {Entries.Count} = Sections.Count - 1;
  1140. #endif
  1141. Entries.NextEntry [Entries.Count] = INVALID_ENTRY;
  1142.  
  1143. SortedEntryList [Entries.Count][0] = DOF2::HashKey (key);
  1144. SortedEntryList [Entries.Count][1] = Entries.Count;
  1145.  
  1146. if (Sections.LastEntry [Sections.Count - 1] == INVALID_ENTRY)
  1147. {
  1148. Sections.FirstEntry [Sections.Count - 1] = Sections.LastEntry [Sections.Count - 1] = Entries.Count;
  1149. Entries.PreviousEntry [Entries.Count] = INVALID_ENTRY;
  1150. }
  1151. else
  1152. {
  1153. Entries.NextEntry [Sections.LastEntry [Sections.Count - 1]] = Entries.Count;
  1154. Entries.PreviousEntry [Entries.Count] = Sections.LastEntry [Sections.Count - 1];
  1155. Sections.LastEntry [Sections.Count - 1] = Entries.Count;
  1156. }
  1157. ++Entries.Count;
  1158. }
  1159. }
  1160. pos = 0;
  1161. }
  1162. }
  1163. /*
  1164. * Sort list of entries by it's hashcodes in O(n * log n) time.
  1165. * (Worst case is actually O(n * n), however, this QuickSort implementation chooses a randomized pivot
  1166. * to minimize the chance for the worst case.)
  1167. */
  1168. DOF2::SortEntries (SortedEntryList, 0, Entries.Count - 1, true);
  1169. return fclose (f);
  1170. }
  1171. }
  1172. return 0;
  1173. }
  1174.  
  1175. // Rather useless.
  1176. stock DOF2::ReparseFile (file [], extraid, bool: callback = true)
  1177. {
  1178. if (file [0] && CurrentFile [0] && !strcmp (file, CurrentFile))
  1179. {
  1180. CurrentFile [0] = '\0';
  1181. return DOF2::ParseFile (file, extraid, callback);
  1182. }
  1183. return 0;
  1184. }
  1185.  
  1186. private DOF2::ParseLine (line [], key [], value [], keysize = sizeof (key), valuesize = sizeof (value))
  1187. {
  1188. new
  1189. pos,
  1190. readpos;
  1191.  
  1192. if ((pos = charfind (line, '=')) != -1)
  1193. {
  1194. // Read key and value.
  1195. readpos = pos - 1;
  1196. while (readpos >= 0 && line [readpos] == ' ')
  1197. --readpos;
  1198.  
  1199. if (readpos >= 0 && keysize > (readpos + 1))
  1200. {
  1201. key [readpos + 1] = '\0';
  1202. while (readpos >= 0)
  1203. {
  1204. key [readpos] = line [readpos];
  1205. --readpos;
  1206. }
  1207. }
  1208. else
  1209. return 0;
  1210.  
  1211. readpos = pos + 1;
  1212. ++pos;
  1213. while (line [readpos] == ' ')
  1214. {
  1215. ++pos;
  1216. ++readpos;
  1217. }
  1218.  
  1219. if (line [readpos])
  1220. {
  1221. while (readpos >= 0 && line [readpos] && valuesize > (readpos - pos + 1))
  1222. {
  1223. value [readpos - pos] = line [readpos];
  1224. ++readpos;
  1225. }
  1226. value [readpos - pos] = '\0';
  1227. }
  1228. else
  1229. {
  1230. key [0] = value [0] = '\0';
  1231. return 0;
  1232. }
  1233.  
  1234. if (!strcmp (value, "(null)", true))
  1235. value [0] = '\0';
  1236. return 1;
  1237. }
  1238. key [0] = value [0] = '\0';
  1239. return 0;
  1240. }
  1241.  
  1242. stock DOF2::File (user [])
  1243. {
  1244. new newfile [MAX_FILE_SIZE];
  1245. format (newfile, sizeof (newfile), USER_FILE_PATH, DOF2::udb_encode (user));
  1246. return newfile;
  1247. }
  1248.  
  1249. stock bool: DOF2::CheckLogin (file [], password [])
  1250. return (file [0] && password [0] && DOF2::num_hash (password) == DOF2::GetInt (file, USER_PW_HASH_KEY));
  1251.  
  1252. /*-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/
  1253.  
  1254. stock DOF2::binstr (value, dest [], size = sizeof (dest))
  1255. {
  1256. new buf [32 + 3] = "0b";
  1257. for (new i = 0; i < 32; ++i)
  1258. buf [i + 2] = '0' + ((value >>> (31 - i)) & 1);
  1259.  
  1260. DOF2::strcpy (dest, buf, size);
  1261. }
  1262. //format (dest, size, "0b%b", value);
  1263.  
  1264. stock DOF2::hexstr (value, dest [], size = sizeof (dest))
  1265. {
  1266. static const characters [] =
  1267. {
  1268. '0', '1', '2', '3',
  1269. '4', '5', '6', '7',
  1270. '8', '9', 'A', 'B',
  1271. 'C', 'D', 'E', 'F'
  1272. };
  1273.  
  1274. new buf [8 + 3] = "0x";
  1275.  
  1276. for (new i = 0; i < 8; ++i)
  1277. buf [2 + i] = characters [(value >>> ((7 - i) << 2)) & 0x0F];
  1278.  
  1279. DOF2::strcpy (dest, buf, size);
  1280. }
  1281. //format (dest, size, "0x%x", value);
  1282.  
  1283. stock DOF2::strhex (string [])
  1284. {
  1285. new
  1286. i,
  1287. value;
  1288.  
  1289. if (string [0] == '0' && (string [1] == 'x' || string [1] == 'X'))
  1290. i = 2;
  1291.  
  1292. while (string [i])
  1293. {
  1294. value <<= 4;
  1295. switch (string [i])
  1296. {
  1297. case '0' .. '9':
  1298. value |= string [i] - '0';
  1299.  
  1300. case 'A' .. 'F':
  1301. value |= string [i] - 'A' + 10;
  1302.  
  1303. case 'a' .. 'f':
  1304. value |= string [i] - 'a' + 10;
  1305.  
  1306. default:
  1307. return 0;
  1308. }
  1309. ++i;
  1310. }
  1311. return value;
  1312. }
  1313.  
  1314. stock DOF2::strbin (string [])
  1315. {
  1316. new
  1317. i,
  1318. value;
  1319.  
  1320. if (string [0] == '0' && (string [1] == 'b' || string [1] == 'B'))
  1321. i = 2;
  1322.  
  1323. while (string [i])
  1324. {
  1325. if (string [i] != '1' && string [i] != '0')
  1326. return 0;
  1327.  
  1328. value <<= 1;
  1329. value |= (string [i] - '0');
  1330. ++i;
  1331. }
  1332. return value;
  1333. }
  1334.  
  1335. /*-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/
  1336.  
  1337. private charfind (string [], c)
  1338. {
  1339. for (new i, len = strlen (string); i < len; ++i)
  1340. if (string [i] == c)
  1341. return i;
  1342. return -1;
  1343. }
  1344.  
  1345. private fwritechars (File: handle, c [])
  1346. {
  1347. new pos;
  1348. #if PACK_CONTENT == true
  1349. while (c {pos})
  1350. fputchar (handle, c {pos++}, UseUTF8);
  1351. #else
  1352. while (c [pos])
  1353. fputchar (handle, c [pos++], UseUTF8);
  1354. #endif
  1355. }
  1356.  
  1357. private DOF2::SortEntries (entries [][2], l, r, bool: randomize = true)
  1358. {
  1359. if (r > l)
  1360. {
  1361. if (randomize)
  1362. {
  1363. new k = l + (random (65535) % (r - l + 1));
  1364. DOF2::SwapSortedEntries (entries [k], entries [r]);
  1365. }
  1366.  
  1367. new
  1368. i = l - 1,
  1369. j = r,
  1370. pivot = entries [r][0];
  1371.  
  1372. while (i < j)
  1373. {
  1374. do
  1375. ++i;
  1376. while (entries [i][0] <= pivot && i < r);
  1377.  
  1378. do
  1379. --j;
  1380. while (entries [j][0] >= pivot && j > l);
  1381.  
  1382. if (i < j)
  1383. DOF2::SwapSortedEntries (entries [i], entries [j]);
  1384. }
  1385. DOF2::SwapSortedEntries (entries [i], entries [r]);
  1386. DOF2::SortEntries (entries, l, i - 1, randomize);
  1387. DOF2::SortEntries (entries, i + 1, r, randomize);
  1388. }
  1389. }
  1390.  
  1391. private DOF2::SwapSortedEntries (a [2], b [2])
  1392. {
  1393. new c [2];
  1394. c [0] = a [0];
  1395. c [1] = a [1];
  1396. a [0] = b [0];
  1397. a [1] = b [1];
  1398. b [0] = c [0];
  1399. b [1] = c [1];
  1400. }
  1401.  
  1402. stock DOF2::SortAllSections (file [], bool: ignorecase = true, bool: ascending = true)
  1403. {
  1404. if (file [0])
  1405. {
  1406. if (!CurrentFile [0] || strcmp (CurrentFile, file)) // No file in buffer or the file you want to read from is not the file in the buffer.
  1407. if (!DOF2::ParseFile (file, -1, false))
  1408. return 0;
  1409.  
  1410. new
  1411. entries [MAX_ENTRIES],
  1412. keys [MAX_ENTRIES][MAX_LINE_SIZE],
  1413. key [MAX_LINE_SIZE],
  1414. value [MAX_LINE_SIZE],
  1415. #if PACK_CONTENT == true
  1416. line [MAX_LINE_SIZE],
  1417. #endif
  1418. entry,
  1419. i;
  1420.  
  1421. for (new section = 0; section < Sections.Count; ++section)
  1422. {
  1423. i = 0;
  1424. entry = Sections.FirstEntry [section];
  1425. while (entry != INVALID_ENTRY)
  1426. {
  1427. #if PACK_CONTENT == true
  1428. strunpack (line, Entries.Line [entry]);
  1429. DOF2::ParseLine (line, key, value);
  1430. #else
  1431. DOF2::ParseLine (Entries.Line [entry], key, value);
  1432. #endif
  1433. keys [i][0] = '\0';
  1434. strcat (keys [i], key);
  1435. entries [i] = entry;
  1436. entry = Entries.NextEntry [entry];
  1437. ++i;
  1438. }
  1439.  
  1440. if (i > 0)
  1441. DOF2::SortSection_Internal (section, entries, keys, 0, i - 1, ignorecase, ascending);
  1442. }
  1443. return 1;
  1444. }
  1445. return 0;
  1446. }
  1447.  
  1448. stock DOF2::SortSection (file [], tag [], bool: ignorecase = true, bool: ascending = true)
  1449. {
  1450. if (file [0])
  1451. {
  1452. if (!CurrentFile [0] || strcmp (CurrentFile, file)) // No file in buffer or the file you want to read from is not the file in the buffer.
  1453. if (!DOF2::ParseFile (file, -1, false))
  1454. return 0;
  1455.  
  1456. new
  1457. section = INVALID_SECTION,
  1458. entries [MAX_ENTRIES],
  1459. keys [MAX_ENTRIES][MAX_LINE_SIZE],
  1460. key [MAX_LINE_SIZE],
  1461. buf [MAX_LINE_SIZE],
  1462. #if PACK_CONTENT == true
  1463. line [MAX_LINE_SIZE],
  1464. #endif
  1465. entry,
  1466. i;
  1467.  
  1468. if (!tag [0])
  1469. section = 0;
  1470. else
  1471. {
  1472. for (i = 1; i < Sections.Count; ++i)
  1473. {
  1474. #if PACK_CONTENT == true
  1475. strunpack (buf, Sections.Tag [i]);
  1476. if (buf [0] && !strcmp (tag, buf, !CaseSensitive))
  1477. {
  1478. section = i;
  1479. break;
  1480. }
  1481. #else
  1482. if (Sections.Tag [i][0] && !strcmp (tag, Sections.Tag [i], !CaseSensitive))
  1483. {
  1484. section = i;
  1485. break;
  1486. }
  1487. #endif
  1488. }
  1489. }
  1490.  
  1491. if (section != INVALID_SECTION)
  1492. {
  1493. i = 0;
  1494. entry = Sections.FirstEntry [section];
  1495. while (entry != INVALID_ENTRY)
  1496. {
  1497. #if PACK_CONTENT == true
  1498. strunpack (line, Entries.Line [entry]);
  1499. DOF2::ParseLine (line, key, buf);
  1500. #else
  1501. DOF2::ParseLine (Entries.Line [entry], key, buf);
  1502. #endif
  1503. keys [i][0] = '\0';
  1504. strcat (keys [i], key);
  1505. entries [i] = entry;
  1506. entry = Entries.NextEntry [entry];
  1507. ++i;
  1508. }
  1509.  
  1510. if (i > 0)
  1511. {
  1512. DOF2::SortSection_Internal (section, entries, keys, 0, i - 1, ignorecase, ascending);
  1513. return 1;
  1514. }
  1515. }
  1516. }
  1517. return 0;
  1518. }
  1519.  
  1520. private DOF2::SortSection_Internal (section, entries [], keys [][], l, r, bool: ignorecase = true, bool: ascending = true)
  1521. {
  1522. // Entries must be stored into an array...
  1523. if (0 <= section < Sections.Count && r > l)
  1524. {
  1525. new
  1526. i = l - 1,
  1527. j = r,
  1528. buf [MAX_LINE_SIZE];
  1529.  
  1530. static
  1531. pivot [MAX_LINE_SIZE]; // Must be static, otherwise too much memory usage during recursion ==> Script will crash!
  1532.  
  1533. pivot [0] = '\0';
  1534. strcat (pivot, keys [r]);
  1535.  
  1536. while (i < j)
  1537. {
  1538. if (ascending)
  1539. {
  1540. do
  1541. ++i;
  1542. while (strcmp (keys [i], pivot, ignorecase) <= 0 && i < r);
  1543.  
  1544. do
  1545. --j;
  1546. while (strcmp (keys [j], pivot, ignorecase) >= 0 && j > l);
  1547. }
  1548. else
  1549. {
  1550. do
  1551. ++i;
  1552. while (strcmp (keys [i], pivot, ignorecase) >= 0 && i < r);
  1553.  
  1554. do
  1555. --j;
  1556. while (strcmp (keys [j], pivot, ignorecase) <= 0 && j > l);
  1557. }
  1558.  
  1559. if (i < j)
  1560. {
  1561. DOF2::SwapEntries (section, entries [i], entries [j]);
  1562.  
  1563. DOF2::strcpy (buf, keys [i]);
  1564. DOF2::strcpy (keys [i], keys [j], MAX_LINE_SIZE);
  1565. DOF2::strcpy (keys [j], buf, MAX_LINE_SIZE);
  1566.  
  1567. entries [i] ^= entries [j];
  1568. entries [j] ^= entries [i];
  1569. entries [i] ^= entries [j];
  1570. }
  1571. }
  1572.  
  1573. if (i != r)
  1574. {
  1575. DOF2::SwapEntries (section, entries [i], entries [r]);
  1576.  
  1577. DOF2::strcpy (buf, keys [i]);
  1578. DOF2::strcpy (keys [i], keys [r], MAX_LINE_SIZE);
  1579. DOF2::strcpy (keys [r], buf, MAX_LINE_SIZE);
  1580.  
  1581. entries [i] ^= entries [r];
  1582. entries [r] ^= entries [i];
  1583. entries [i] ^= entries [r];
  1584. }
  1585.  
  1586. DOF2::SortSection_Internal (section, entries, keys, l, i - 1, ignorecase, ascending);
  1587. DOF2::SortSection_Internal (section, entries, keys, i + 1, r, ignorecase, ascending);
  1588. }
  1589. }
  1590.  
  1591. private DOF2::SwapEntries (section, entry1, entry2)
  1592. {
  1593. // This swaps two entries in the entry list of a section. (Pointers are swapped)
  1594. if (0 <= section < Sections.Count && 0 <= entry1 <= Entries.Count && 0 <= entry2 <= Entries.Count)
  1595. {
  1596. if (entry1 == Sections.FirstEntry [section])
  1597. Sections.FirstEntry [section] = entry2;
  1598. else if (entry2 == Sections.FirstEntry [section])
  1599. Sections.FirstEntry [section] = entry1;
  1600.  
  1601. if (entry1 == Sections.LastEntry [section])
  1602. Sections.LastEntry [section] = entry2;
  1603. else if (entry2 == Sections.LastEntry [section])
  1604. Sections.LastEntry [section] = entry1;
  1605.  
  1606. if (Entries.NextEntry [entry1] == entry2)
  1607. {
  1608. Entries.NextEntry [entry1] = Entries.NextEntry [entry2];
  1609. Entries.PreviousEntry [entry2] = Entries.PreviousEntry [entry1];
  1610.  
  1611. if (Entries.PreviousEntry [entry1] != INVALID_ENTRY)
  1612. Entries.NextEntry [Entries.PreviousEntry [entry1]] = entry2;
  1613.  
  1614. if (Entries.NextEntry [entry2] != INVALID_ENTRY)
  1615. Entries.PreviousEntry [Entries.NextEntry [entry2]] = entry1;
  1616.  
  1617. Entries.NextEntry [entry2] = entry1;
  1618. Entries.PreviousEntry [entry1] = entry2;
  1619. }
  1620. else if (Entries.NextEntry [entry2] == entry1)
  1621. {
  1622. Entries.NextEntry [entry2] = Entries.NextEntry [entry1];
  1623. Entries.PreviousEntry [entry1] = Entries.PreviousEntry [entry2];
  1624.  
  1625. if (Entries.PreviousEntry [entry2] != INVALID_ENTRY)
  1626. Entries.NextEntry [Entries.PreviousEntry [entry2]] = entry1;
  1627.  
  1628. if (Entries.NextEntry [entry1] != INVALID_ENTRY)
  1629. Entries.PreviousEntry [Entries.NextEntry [entry1]] = entry2;
  1630.  
  1631. Entries.NextEntry [entry1] = entry2;
  1632. Entries.PreviousEntry [entry2] = entry1;
  1633. }
  1634. else
  1635. {
  1636. new pointer;
  1637.  
  1638. if (Entries.PreviousEntry [entry1] != INVALID_ENTRY)
  1639. Entries.NextEntry [Entries.PreviousEntry [entry1]] = entry2;
  1640.  
  1641. if (Entries.NextEntry [entry1] != INVALID_ENTRY)
  1642. Entries.PreviousEntry [Entries.NextEntry [entry1]] = entry2;
  1643.  
  1644. if (Entries.PreviousEntry [entry2] != INVALID_ENTRY)
  1645. Entries.NextEntry [Entries.PreviousEntry [entry2]] = entry1;
  1646.  
  1647. if (Entries.NextEntry [entry2] != INVALID_ENTRY)
  1648. Entries.PreviousEntry [Entries.NextEntry [entry2]] = entry1;
  1649.  
  1650. pointer = Entries.NextEntry [entry1];
  1651. Entries.NextEntry [entry1] = Entries.NextEntry [entry2];
  1652. Entries.NextEntry [entry2] = pointer;
  1653.  
  1654. pointer = Entries.PreviousEntry [entry1];
  1655. Entries.PreviousEntry [entry1] = Entries.PreviousEntry [entry2];
  1656. Entries.PreviousEntry [entry2] = pointer;
  1657. }
  1658. return 1;
  1659. }
  1660. return 0;
  1661. }
  1662.  
  1663. private DOF2::HashKey (key [])
  1664. {
  1665. new
  1666. h = -1,
  1667. i,
  1668. j;
  1669.  
  1670. if (CaseSensitive)
  1671. {
  1672. while ((j = key [i++]))
  1673. h = h * 33 + j;
  1674. }
  1675. else
  1676. {
  1677. while ((j = tolower (key [i++])))
  1678. h = h * 33 + j;
  1679. }
  1680. return h;
  1681. }
  1682.  
  1683. /*-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/
  1684.  
  1685. stock DOF2::strcpy (dest [], const src [], size = sizeof (dest))
  1686. {
  1687. dest [0] = '\0';
  1688. strcat (dest, src, size);
  1689. }
  1690.  
  1691. // Replace [oldstr] with [newstr] in [srcstr] and copy write the new string to 'deststr'.
  1692.  
  1693. stock DOF2::strreplace (const newstr [], const oldstr [], const srcstr [], deststr [], bool: ignorecase = false, size = sizeof (deststr))
  1694. {
  1695. new
  1696. newlen = strlen (newstr),
  1697. oldlen = strlen (oldstr),
  1698. srclen = strlen (srcstr),
  1699. idx,
  1700. rep;
  1701.  
  1702. for (new i = 0; i < srclen; ++i)
  1703. {
  1704. if (idx < (size - 1))
  1705. {
  1706. if ((i + oldlen) <= srclen)
  1707. {
  1708. if (!strcmp (srcstr [i], oldstr, ignorecase, oldlen))
  1709. {
  1710. deststr [idx] = '\0';
  1711. strcat (deststr, newstr, size);
  1712. ++rep;
  1713. idx += newlen;
  1714. i += oldlen - 1;
  1715. }
  1716. else
  1717. deststr [idx++] = srcstr [i];
  1718. }
  1719. else
  1720. deststr [idx++] = srcstr [i];
  1721. }
  1722. else
  1723. return rep;
  1724. }
  1725. deststr [idx] = '\0';
  1726. return rep;
  1727. }
  1728.  
  1729. stock DOF2::udb_encode (nickname [])
  1730. {
  1731. new
  1732. buf [256],
  1733. result [256];
  1734.  
  1735. static const symbols [][2][] =
  1736. {
  1737. {"_", "_00"},
  1738. {";", "_01"},
  1739. {"!", "_02"},
  1740. {"/", "_03"},
  1741. {"\\", "_04"},
  1742. {"[", "_05"},
  1743. {"]", "_06"},
  1744. {"?", "_07"},
  1745. {".", "_08"},
  1746. {"*", "_09"},
  1747. {"<", "_10"},
  1748. {">", "_11"},
  1749. {"{", "_12"},
  1750. {"}", "_13"},
  1751. {" ", "_14"},
  1752. {"\"", "_15"},
  1753. {":", "_16"},
  1754. {"|", "_17"},
  1755. {"=", "_18"}
  1756. };
  1757.  
  1758. strcat (buf, nickname);
  1759. for (new i = 0; i < sizeof (symbols); ++i)
  1760. {
  1761. DOF2::strreplace (symbols [i][1], symbols [i][0], buf, result);
  1762. DOF2::strcpy (buf, result);
  1763. }
  1764. return result;
  1765. }
  1766.  
  1767. stock DOF2::udb_decode (nickname [])
  1768. {
  1769. new
  1770. buf [256],
  1771. result [256];
  1772.  
  1773. static const symbols [][2][] =
  1774. {
  1775. {"_", "_00"},
  1776. {";", "_01"},
  1777. {"!", "_02"},
  1778. {"/", "_03"},
  1779. {"\\", "_04"},
  1780. {"[", "_05"},
  1781. {"]", "_06"},
  1782. {"?", "_07"},
  1783. {".", "_08"},
  1784. {"*", "_09"},
  1785. {"<", "_10"},
  1786. {">", "_11"},
  1787. {"{", "_12"},
  1788. {"}", "_13"},
  1789. {" ", "_14"},
  1790. {"\"", "_15"},
  1791. {":", "_16"},
  1792. {"|", "_17"},
  1793. {"=", "_18"}
  1794. };
  1795.  
  1796. strcat (buf, nickname);
  1797. for (new i = 0; i < sizeof (symbols); ++i)
  1798. {
  1799. DOF2::strreplace (symbols [i][0], symbols [i][1], buf, result);
  1800. DOF2::strcpy (buf, result);
  1801. }
  1802. return result;
  1803. }
  1804.  
  1805. stock DOF2::num_hash (buf [])
  1806. {
  1807. new
  1808. length = strlen (buf),
  1809. s1 = 1,
  1810. s2 = 0,
  1811. n;
  1812.  
  1813. for (n = 0; n < length; n++)
  1814. {
  1815. s1 = (s1 + buf [n]) % 65521;
  1816. s2 = (s2 + s1) % 65521;
  1817. }
  1818. return (s2 << 16) + s1;
  1819. }
  1820.  
  1821. /*-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/
  1822.  
  1823. #if defined DUDB_CONVERT
  1824.  
  1825. #tryinclude <dutils>
  1826.  
  1827. #define dUser(%0).( DOF2_GetString(DOF2_File(%0),
  1828. #define dUserSet(%0).( DOF2_SetString(DOF2_File(%0),
  1829. #define dUserINT(%0).( DOF2_GetInt(DOF2_File(%0),
  1830. #define dUserSetINT(%0).( DOF2_SetInt(DOF2_File(%0),
  1831. #define dUserFLOAT(%0).( DOF2_GetFloat(DOF2_File(%0),
  1832. #define dUserSetFLOAT(%0).( DOF2_SetFloat(DOF2_File(%0),
  1833. #define udb_Create(%0,%1) DOF2_CreateFile(DOF2_File(%0),%1)
  1834. #define udb_RenameUser(%0,%1) DOF2_RenameFile(DOF2_File(%0),DOF2_File(%1))
  1835. #define udb_Exists(%0) DOF2_FileExists(DOF2_File(%0))
  1836. #define udb_Remove(%0) DOF2_RemoveFile(DOF2_File(%0))
  1837. #define udb_CheckLogin(%0,%1) DOF2_CheckLogin(DOF2_File(%0),%1)
  1838.  
  1839. #if !defined _dudb_included
  1840. #define _dudb_included
  1841. #endif
  1842.  
  1843. #endif
  1844.  
  1845. #if defined DINI_CONVERT
  1846.  
  1847. #define dini_Exists DOF2_FileExists
  1848. #define dini_Remove DOF2_RemoveFile
  1849. #define dini_Create DOF2_CreateFile
  1850. #define dini_Set DOF2_SetString
  1851. #define dini_Get DOF2_GetString
  1852. #define dini_IntSet DOF2_SetInt
  1853. #define dini_Int DOF2_GetInt
  1854. #define dini_BoolSet DOF2_SetBool
  1855. #define dini_Bool DOF2_GetBool
  1856. #define dini_FloatSet DOF2_SetFloat
  1857. #define dini_Float DOF2_GetFloat
  1858. #define dini_Unset DOF2_Unset
  1859. #define dini_Isset DOF2_IsSet
  1860.  
  1861. #if !defined _dini_included
  1862. #define _dini_included
  1863. #endif
  1864.  
  1865. #endif
  1866.  
  1867. /*
  1868. #if defined DINI_CONVERT || defined DUDB_CONVERT
  1869.  
  1870. #define udb_hash DOF2_num_hash
  1871. #define num_hash DOF2_num_hash
  1872. #define udb_encode DOF2_udb_encode
  1873. #define udb_decode DOF2_udb_decode
  1874.  
  1875. #endif
  1876. */
Add Comment
Please, Sign In to add comment