Advertisement
delaypawn

double-o-files_2

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