Guest User

DOF2

a guest
Oct 20th, 2011
361
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
Pawn 31.32 KB | None | 0 0
  1. #include <a_samp>
  2. #include <dutils>
  3.  
  4. /*-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/
  5.  
  6. /*
  7.  * This is a new version of the INI script Double-O-Files.
  8.  * However, it's has completely been rewritten and has now a much better performance.
  9.  * There is also the support for sections in the INI file. (But there is no support for comments.)
  10.  * Double-O-Files 2 is compatible with DUDB, DINI, Double-O-Files and possibly y_ini since it
  11.  * can handle sections and entry of the format "key = value", not only "key=value".
  12.  * The number of spaces between the equal sign and key and value can actually be arbitrary.
  13.  * I've added some comments below. You may see that I've mentioned the big-O-notation,
  14.  * 'n' always Entry.Count.
  15.  * Double-O-Files 2 should also be useful for Russian letter because I'm using
  16.  * the functions fgetchar and fputchar to write and read the files.
  17.  *
  18.  * There is another new feature which has been inspired by ZCMD and y_ini:
  19.  * The OnParseFile callbacks. To learn more about it, read the description in
  20.  * the SA-MP forums if you haven't already.
  21.  * THE END
  22.  */
  23.  
  24. /*-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/
  25.  
  26. /*
  27. native DOF2_SetFile(file[]);
  28. native DOF2_LoadFile();
  29. native DOF2_SaveFile();
  30. native DOF2_ParseFile(file[],extraid,bool:callback=true);
  31. native DOF2_ReparseFile(file[],extraid,bool:callback=true);
  32. native DOF2_WriteFile();
  33. native DOF2_PrintFile(comment[]="");
  34. native DOF2_GetString(file[],key[],tag[]="");
  35. native DOF2_GetStringEx(file[],key[],result[],size,tag[]="");
  36. native Float:DOF2_GetFloat(file[],key[]);
  37. native DOF2_GetInt(file[],key[],tag[]="");
  38. native DOF2_GetHex(file[],key[],tag[]="");
  39. native DOF2_GetBin(file[],key[],tag[]="");
  40. native bool:DOF2_GetBool(file[],key[],tag[]="");
  41. native DOF2_SetString(file[],key[],value[],tag[]="");
  42. native DOF2_SetFloat(file[],key[],Float:value);
  43. native DOF2_SetInt(file[],key[],value,tag[]="");
  44. native DOF2_SetHex(file[],key[],value,tag[]="");
  45. native DOF2_SetBin(file[],key[],value,tag[]="");
  46. native DOF2_SetBool(file[],key[],bool:value,tag[]="");
  47. native DOF2_IsSet(file[],key[],tag[]="");
  48. native DOF2_Unset(file[],key[],tag[]="");
  49. native DOF2_FileExists(file[]);
  50. native DOF2_RemoveFile(file[]);
  51. native DOF2_CreateFile(file[],password[]="");
  52. native DOF2_RenameFile(oldfile[],newfile[]);
  53. native DOF2_RenameKey(file[],oldkey[],newkey[],tag[]="");
  54. native DOF2_CopyFile(filetocopy[],newfile[]);
  55. native DOF2_CheckLogin(file[],password[]);
  56. native DOF2_File(user[]);
  57. native DOF2_ParseInt();
  58. native DOF2_ParseFloat();
  59. native DOF2_ParseBool();
  60. native DOF2_ParseBin();
  61. native DOF2_ParseHex();
  62. native DOF2_SetUTF8(bool:set);
  63. native bool:DOF2_GetUTF8();
  64. native DOF2_GetFile();
  65. native DOF2_MakeBackup(file[]);
  66. native DOF2_RemoveTag (file [], tag []);
  67. */
  68.  
  69. // OnParseFile <Tag><Key>(extraid, value [])
  70. // OnParseFile <><Key>(extraid, value [])
  71. // OnDefaultParseFile (extraid, value [], key [], tag [], file [])
  72.  
  73. #define OnParseFile<%0><%1>(%2) \
  74.     forward _OnParseFile_%0_%1(%2); \
  75.     public _OnParseFile_%0_%1(%2)
  76.  
  77. #define OnDefaultParseFile(%0) \
  78.     forward _OnDefaultParseFile(%0); \
  79.     public _OnDefaultParseFile(%0)
  80.  
  81. #define DOF2_ParseBool() \
  82.     (strval (value) || (value [0] && !strcmp (value, "true", true)))
  83.  
  84. #define DOF2_ParseInt() \
  85.     (strval (value))
  86.  
  87. #define DOF2_ParseFloat() \
  88.     (floatstr (value))
  89.  
  90. #define DOF2_ParseBin() \
  91.     (strbin (value))
  92.  
  93. #define DOF2_ParseHex() \
  94.     (strhex (value))
  95.  
  96. #define DOF2_LoadFile() \
  97.     DOF2_ParseFile (CurrentFile, -1, false)
  98.  
  99. #define DOF2_SaveFile \
  100.     DOF2_WriteFile
  101.  
  102. #define DOF2_FileExists \
  103.     fexist
  104.  
  105. #define Tag. \
  106.     Tag_
  107.  
  108. #define Entry. \
  109.     Entry_
  110.  
  111. #define DOF2:: \
  112.     DOF2_
  113.  
  114. #define MAX_TAG_SIZE        (32)
  115. #define MAX_LINE_SIZE       (128)
  116. #define MAX_TAGS            (32)
  117. #define MAX_ENTRIES         (256)
  118. #define MAX_FILE_SIZE       (64)
  119.  
  120. #define USER_FILE_PATH      "Users/%s.ini"
  121.  
  122. //#define DUDB_CONVERT
  123. //#define DINI_CONVERT
  124.  
  125. /*-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/
  126.  
  127. static stock
  128.     bool: UTF8 = true,
  129.     CurrentFile [MAX_FILE_SIZE],
  130.     bool: FileChanged,
  131.     Tag.Name [MAX_TAGS][MAX_TAG_SIZE char],
  132.     Tag.FirstEntry [MAX_TAGS] = {-1, ...},
  133.     Tag.LastEntry [MAX_TAGS] = {-1, ...},
  134.     Tag.Count,
  135.     Entry.Line [MAX_ENTRIES][MAX_LINE_SIZE char],
  136.     Entry.Tag [MAX_ENTRIES][MAX_TAG_SIZE char],
  137.     Entry.TagID [MAX_ENTRIES],
  138.     Entry.NextEntry [MAX_ENTRIES] = {-1, ...},
  139.     Entry.PreviousEntry [MAX_ENTRIES] = {-1, ...},
  140.     Entry.Count,
  141.     SortedEntries [MAX_ENTRIES][2]; // Index 0: Hashcode, Index 1: EntryID
  142.  
  143. /*-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/
  144.  
  145. DOF2::Exit ()
  146.     DOF2::WriteFile ();
  147.  
  148. stock DOF2::SetUTF8 (bool: set)
  149.     UTF8 = set;
  150.  
  151. stock bool: DOF2::GetUTF8 ()
  152.     return UTF8;
  153.  
  154. stock DOF2::SetFile (file [])
  155. {
  156.     CurrentFile [0] = '\0';
  157.     strcat (CurrentFile, file);
  158. }
  159.  
  160. stock DOF2::GetFile ()
  161.     return CurrentFile;
  162.  
  163. stock DOF2::CreateFile (file [], password [] = "")
  164. {
  165.     if (!DOF2::FileExists (file))
  166.     {
  167.         if (password [0])
  168.             DOF2::SetInt (file, "password", num_hash (password));
  169.         else
  170.         {
  171.             new File: f = fopen (file, io_append);
  172.             fclose (f);
  173.         }
  174.         return 1;
  175.     }
  176.     return 0;
  177. }
  178.  
  179. stock DOF2::RenameFile (oldfile [], newfile [])
  180. {
  181.     if (DOF2::FileExists (oldfile) && !DOF2::FileExists (newfile))
  182.     {
  183.         if (CurrentFile [0] && !strcmp (CurrentFile, oldfile) && FileChanged)
  184.             DOF2::WriteFile ();
  185.         else
  186.             DOF2::ParseFile (oldfile, -1, false);
  187.         DOF2::SetFile (newfile);
  188.         if (DOF2::WriteFile ())
  189.         {
  190.             fremove (oldfile);
  191.             return 1;
  192.         }
  193.     }
  194.     return 0;
  195. }
  196.  
  197. stock DOF2::CopyFile (filetocopy [], newfile [])
  198. {
  199.     if (DOF2::FileExists (filetocopy) && !DOF2::FileExists (newfile))
  200.     {
  201.         if (CurrentFile [0] && !strcmp (CurrentFile, filetocopy) && FileChanged)
  202.             DOF2::WriteFile ();
  203.         else
  204.             DOF2::ParseFile (filetocopy, -1, false);
  205.         DOF2::SetFile (newfile);
  206.         return DOF2::WriteFile ();
  207.     }
  208.     return 0;
  209. }
  210.  
  211. stock DOF2::RemoveFile (file [])
  212. {
  213.     if (file [0])
  214.     {
  215.         if (CurrentFile [0] && !strcmp (CurrentFile, file))
  216.             CurrentFile [0] = '\0';
  217.         fremove (file);
  218.         return 1;
  219.     }
  220.     return 0;
  221. }
  222.  
  223. stock DOF2::MakeBackup (file [])
  224. {
  225.     if (DOF2::FileExists (file))
  226.     {
  227.         new
  228.             year,
  229.             month,
  230.             day,
  231.             hour,
  232.             minute,
  233.             second,
  234.             backupfile [MAX_FILE_SIZE];
  235.  
  236.         getdate (year, month, day);
  237.         gettime (hour, minute, second);
  238.         format (backupfile, sizeof (backupfile), "%s.%02d_%02d_%02d.%02d_%02d_%02d_%02d.bak", CurrentFile, month, day, year, hour, minute, second, GetTickCount ());
  239.         return DOF2::CopyFile (CurrentFile, backupfile);
  240.     }
  241.     return 0;
  242. }
  243.  
  244. stock DOF2::RemoveTag (file [], tag [])
  245. {
  246.     // Removes tag 'tag' with all it's entries.
  247.     if (file [0] && tag [0]) // You can't remove the empty tag.
  248.     {
  249.         if (CurrentFile [0] && !strcmp (CurrentFile, filetocopy) && FileChanged)
  250.             DOF2::WriteFile ();
  251.         else
  252.             DOF2::ParseFile (filetocopy, -1, false);
  253.  
  254.         new
  255.             line [MAX_LINE_SIZE],
  256.             buf [MAX_TAG_SIZE],
  257.             tagid = -1,
  258.             currententry,
  259.             key [MAX_KEY_SIZE];
  260.  
  261.         for (new i = 1; i < Tag.Count; ++i)
  262.         {
  263.             strunpack (buf, Tag.Name [i]);
  264.             if (!strcmp (buf, tag))
  265.             {
  266.                 tagid = i;
  267.                 break;
  268.             }
  269.         }
  270.  
  271.         if (tagid != -1)
  272.         {
  273.             currententry = Tag.FirstEntry [tagid];
  274.             while (currententry != -1)
  275.             {
  276.                 // Remove all entries under the current tag.
  277.                 strunpack (line, Entry.Line [currententry]);
  278.                 DOF2::ParseLine (line, key, buf);
  279.                 DOF2::Unset (file, key, tag);
  280.                 currententry = Entry.NextEntry [currententry];
  281.             }
  282.        
  283.             // Move the last tag to the position of the current tag. Creates a little mess.
  284.             --Tag.Count;
  285.             Tag.Name [tagid] = Tag.Name [Tag.Count];
  286.             Tag.FirstEntry [tagid] = Tag.FirstEntry [Tag.Count];
  287.             Tag.LastEntry [tagid] = Tag.LastEntry [Tag.Count];
  288.  
  289.             currententry = Tag.FirstEntry [tagid];
  290.             while (currententry != -1)
  291.             {
  292.                 Entry.TagID [currententry] = tagid;
  293.                 currententry = Entry.NextEntry [currententry];
  294.             }
  295.             FileChanged = true;
  296.             return 1;
  297.         }
  298.     }
  299.     return 0;
  300. }
  301.  
  302. static stock DOF2::FindEntry (key [], tag [], keybuf [], valbuf [], &pos, keybufsize = sizeof (keybuf), valbufsize = sizeof (valbuf))
  303. {
  304.     if (key [0])
  305.     {
  306.         new
  307.             entry = -1,
  308.             l,
  309.             m,
  310.             r,
  311.             h,
  312.             line [MAX_LINE_SIZE],
  313.             i;
  314.  
  315.         h = DOF2::bernstein (key);
  316.         l = 0;
  317.         r = Entry.Count - 1;
  318.  
  319.         /*
  320.          * 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.
  321.          * A sequential search would take up to 256 steps. That was the case in the first Double-O-Files script.
  322.          */
  323.         while (l <= r)
  324.         {
  325.             if ((r - l) < 2)
  326.             {
  327.                 if (h == SortedEntries [l][0])
  328.                 {
  329.                     m = l;
  330.                     entry = SortedEntries [l][1];
  331.                 }
  332.                 else if (r > l && h == SortedEntries [r][0])
  333.                 {
  334.                     m = r;
  335.                     entry = SortedEntries [r][1];
  336.                 }
  337.                 break;
  338.             }
  339.             else
  340.             {
  341.                 m = l + (r - l) / 2;
  342.                 if (h == SortedEntries [m][0])
  343.                 {
  344.                     entry = SortedEntries [m][1];
  345.                     break;
  346.                 }
  347.                 else if (h > SortedEntries [m][0])
  348.                     l = m + 1;
  349.                 else
  350.                     r = m - 1;
  351.             }
  352.         }
  353.  
  354.         // Candidate found?
  355.         if (entry != -1)
  356.         {
  357.             strunpack (line, Entry.Line [entry]);
  358.             DOF2::ParseLine (line, keybuf, valbuf, keybufsize, valbufsize);
  359.             // Check if it's the entry we want.
  360.             if (!strcmp (keybuf, key) && ((!tag [0] && !Entry.Tag [entry][0]) || (tag [0] && Entry.Tag [entry][0] && !strcmp (tag, Entry.Tag [entry]))))
  361.                 return (pos = m, entry);
  362.             else
  363.             {
  364.                 // 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.
  365.                 for (i = m - 1; i >= 0 && h == SortedEntries [i][0]; --i)
  366.                 {
  367.                     entry = SortedEntries [i][1];
  368.                     strunpack (line, Entry.Line [entry]);
  369.                     DOF2::ParseLine (line, keybuf, valbuf, keybufsize, valbufsize);
  370.                     if (!strcmp (keybuf, key) && ((!tag [0] && !Entry.Tag [entry][0]) || (tag [0] && Entry.Tag [entry][0] && !strcmp (tag, Entry.Tag [entry]))))
  371.                         return (pos = i, entry);
  372.                 }
  373.  
  374.                 for (i = m + 1; i < Entry.Count && h == SortedEntries [i][0]; ++i)
  375.                 {
  376.                     entry = SortedEntries [i][1];
  377.                     strunpack (line, Entry.Line [entry]);
  378.                     DOF2::ParseLine (line, keybuf, valbuf, keybufsize, valbufsize);
  379.                     if (!strcmp (keybuf, key) && ((!tag [0] && !Entry.Tag [entry][0]) || (tag [0] && Entry.Tag [entry][0] && !strcmp (tag, Entry.Tag [entry]))))
  380.                         return (pos = i, entry);
  381.                 }
  382.             }
  383.         }
  384.     }
  385.     return -1;
  386. }
  387.  
  388. stock DOF2::SetString (file [], key [], value [], tag [] = "")
  389. {
  390.     if (file [0] && key [0])
  391.     {
  392.         new
  393.             entry,
  394.             pos,
  395.             tagid = -1,
  396.             buf [MAX_TAG_SIZE],
  397.             keybuf [MAX_LINE_SIZE],
  398.             valbuf [MAX_LINE_SIZE],
  399.             line [MAX_LINE_SIZE],
  400.             i;
  401.  
  402.         if (!CurrentFile [0] || strcmp (CurrentFile, file))
  403.             DOF2::ParseFile (file, -1, false);
  404.  
  405.         entry = DOF2::FindEntry (key, tag, keybuf, valbuf, pos);
  406.  
  407.         // If the entry has been found, just change it's content.
  408.         if (entry != -1)
  409.         {
  410.             format (line, sizeof (line), "%s = %s", key, value);
  411.             FileChanged = true;
  412.             return strpack (Entry.Line [entry], line);
  413.         }
  414.  
  415.         if (Entry.Count >= MAX_ENTRIES)
  416.             return 0;
  417.  
  418.         // Search for the section where the entry belongs.
  419.         if (!tag [0])
  420.             tagid = 0;
  421.         else
  422.         {
  423.             for (i = 1; i < Tag.Count; ++i)
  424.             {
  425.                 strunpack (buf, Tag.Name [i]);
  426.                 if (buf [0] && !strcmp (tag, buf))
  427.                 {
  428.                     tagid = i;
  429.                     break;
  430.                 }
  431.             }
  432.         }
  433.  
  434.         // Section we want does not exist, create new one if possible.
  435.         if (tagid == -1)
  436.         {
  437.             if (Tag.Count >= MAX_TAGS)
  438.                 return 0;
  439.  
  440.             tagid = Tag.Count;
  441.             strpack (Tag.Name [tagid], tag);
  442.             Tag.FirstEntry [tagid] = Tag.LastEntry [tagid] = -1;
  443.             ++Tag.Count;
  444.         }
  445.  
  446.         // Add the entry to the section. Section's content is defined by a linear two way list.
  447.         format (line, sizeof (line), "%s = %s", key, value);
  448.         strpack (Entry.Line [Entry.Count], line);
  449.         Entry.Tag [Entry.Count] = Tag.Name [tagid];
  450.         Entry.TagID [Entry.Count] = tagid;
  451.         Entry.NextEntry [Entry.Count] = -1;
  452.  
  453.         // Add entry to sorted list of entries and move to right correct position in O(n) time.
  454.         SortedEntries [Entry.Count][0] = DOF2::bernstein (key);
  455.         SortedEntries [Entry.Count][1] = Entry.Count;
  456.         i = Entry.Count - 1;
  457.         while (i >= 0 && SortedEntries [i][0] > SortedEntries [i + 1][0])
  458.         {
  459.             DOF2::Swap (SortedEntries [i], SortedEntries [i + 1]);
  460.             --i;
  461.         }
  462.  
  463.         if (Tag.LastEntry [tagid] == -1)
  464.         {
  465.             Tag.FirstEntry [tagid] = Tag.LastEntry [tagid] = Entry.Count;
  466.             Entry.PreviousEntry [Entry.Count] = -1;
  467.         }
  468.         else
  469.         {
  470.             new name [MAX_TAG_SIZE];
  471.             strunpack (name, Tag.Name [tagid]);
  472.             Entry.NextEntry [Tag.LastEntry [tagid]] = Entry.Count;
  473.             Entry.PreviousEntry [Entry.Count] = Tag.LastEntry [tagid];
  474.             Tag.LastEntry [tagid] = Entry.Count;
  475.         }
  476.         ++Entry.Count;
  477.         FileChanged = true;
  478.     }
  479.     return 1;
  480. }
  481.  
  482. stock DOF2::GetString (file [], key [], tag [] = "")
  483. {
  484.     new buf [MAX_LINE_SIZE];
  485.     DOF2::GetStringEx (file, key, buf, sizeof (buf), tag);
  486.     return buf;
  487. }
  488.  
  489. stock DOF2::GetStringEx (file [], key [], result [], size, tag [] = "")
  490. {
  491.     if (file [0] && key [0])
  492.     {
  493.         new
  494.             pos,
  495.             keybuf [MAX_LINE_SIZE];
  496.  
  497.         if (!CurrentFile [0] || strcmp (CurrentFile, file))
  498.             DOF2::ParseFile (file, -1, false);
  499.  
  500.         // Find entry and assign the result with it's value.
  501.         return (DOF2::FindEntry (key, tag, keybuf, result, pos, sizeof (keybuf), size) != -1);
  502.     }
  503.     return 0;
  504. }
  505.  
  506. stock DOF2::Unset (file [], key [], tag [] = "")
  507. {
  508.     if (file [0] && key [0])
  509.     {
  510.         new
  511.             entry,
  512.             pos,
  513.             keybuf [MAX_LINE_SIZE],
  514.             valbuf [MAX_LINE_SIZE];
  515.  
  516.         if (!CurrentFile [0] || strcmp (CurrentFile, file))
  517.             DOF2::ParseFile (file, -1, false);
  518.  
  519.         if ((entry = DOF2::FindEntry (key, tag, keybuf, valbuf, pos)) != -1)
  520.         {
  521.             // Remove entry from it's section.
  522.             if (Tag.FirstEntry [Entry.TagID [entry]] == entry)
  523.             {
  524.                 Tag.FirstEntry [Entry.TagID [entry]] = Entry.NextEntry [entry];
  525.                 if (Entry.NextEntry [entry] != -1)
  526.                     Entry.PreviousEntry [Entry.NextEntry [entry]] = -1;
  527.             }
  528.             else
  529.             {
  530.                 Entry.NextEntry [Entry.PreviousEntry [entry]] = Entry.NextEntry [entry];
  531.                 if (Entry.NextEntry [entry] != -1)
  532.                     Entry.PreviousEntry [Entry.NextEntry [entry]] = Entry.PreviousEntry [entry];
  533.             }
  534.  
  535.             if (Tag.LastEntry [Entry.TagID [entry]] == entry)
  536.             {
  537.                 Tag.LastEntry [Entry.TagID [entry]] = Entry.PreviousEntry [entry];
  538.                 if (Entry.PreviousEntry [entry] != -1)
  539.                     Entry.NextEntry [Entry.PreviousEntry [entry]] = -1;
  540.             }
  541.             else
  542.             {
  543.                 Entry.PreviousEntry [Entry.NextEntry [entry]] = Entry.PreviousEntry [entry];
  544.                 if (Entry.PreviousEntry [entry] != -1)
  545.                     Entry.NextEntry [Entry.PreviousEntry [entry]] = Entry.NextEntry [entry];
  546.             }
  547.  
  548.             // Move the entry to the end of the sorted list and decrement Entry.Count to forget about the unset entry.
  549.             while (pos < (Entry.Count - 1))
  550.             {
  551.                 DOF2::Swap (SortedEntries [pos], SortedEntries [pos + 1]);
  552.                 ++pos;
  553.             }
  554.             --Entry.Count;
  555.             FileChanged = true;
  556.             return 1;
  557.         }
  558.     }
  559.     return 0;
  560. }
  561.  
  562. stock DOF2::RenameKey (file [], oldkey [], newkey [], tag [] = "")
  563. {
  564.     if (file [0] && oldkey [0])
  565.     {
  566.         new
  567.             entry,
  568.             pos,
  569.             keybuf [MAX_LINE_SIZE],
  570.             valbuf [MAX_LINE_SIZE],
  571.             line [MAX_LINE_SIZE];
  572.  
  573.         if (!CurrentFile [0] || strcmp (CurrentFile, file))
  574.             DOF2::ParseFile (file, -1, false);
  575.  
  576.         if ((entry = DOF2::FindEntry (oldkey, tag, keybuf, valbuf, pos)) != -1)
  577.         {
  578.             // Change content of entry.
  579.             format (line, sizeof (line), "%s = %s", newkey, valbuf);
  580.             strpack (Entry.Line [entry], line);
  581.  
  582.             // Because the hashcode has been changed, the entry has to move in the list.
  583.             SortedEntries [pos][0] = DOF2::bernstein (newkey);
  584.             if (pos < (MAX_ENTRIES - 1) && SortedEntries [pos][0] > SortedEntries [pos + 1][0])
  585.             {
  586.                 // Hash value of key is greater than the hash value of it's right neighbor, move to the right by swapping the 2 entries.
  587.                 while (pos < (MAX_ENTRIES - 1) && SortedEntries [pos][0] > SortedEntries [pos + 1][0])
  588.                 {
  589.                     DOF2::Swap (SortedEntries [pos], SortedEntries [pos + 1]);
  590.                     ++pos;
  591.                 }
  592.             }
  593.             else if (pos > 0 && SortedEntries [pos][0] < SortedEntries [pos + 1][0])
  594.             {
  595.                 // Hash value of key is smaller than the hash value of it' left neighbor, move to the left by swapping the 2 entries.
  596.                 while (pos > 0 && SortedEntries [pos][0] < SortedEntries [pos - 1][0])
  597.                 {
  598.                     DOF2::Swap (SortedEntries [pos], SortedEntries [pos - 1]);
  599.                     --pos;
  600.                 }
  601.             }
  602.  
  603.             FileChanged = true;
  604.             return 1;
  605.         }
  606.     }
  607.     return 0;
  608. }
  609.  
  610. stock bool: DOF2::IsSet (file [], key [], tag [] = "")
  611. {
  612.     new
  613.         pos,
  614.         keybuf [MAX_LINE_SIZE],
  615.         valbuf [MAX_LINE_SIZE];
  616.  
  617.     if (!CurrentFile [0] || strcmp (CurrentFile, file))
  618.         DOF2::ParseFile (file, -1, false);
  619.  
  620.     // Try to find the entry.
  621.     return (DOF2::FindEntry (key, tag, keybuf, valbuf, pos) != -1);
  622. }
  623.  
  624. stock DOF2::SetInt (file [], key [], value, tag [] = "")
  625. {
  626.     new buf [16];
  627.     format (buf, sizeof (buf), "%d", value);
  628.     return DOF2::SetString (file, key, buf, tag);
  629. }
  630.  
  631. stock DOF2::GetInt (file [], key [], tag [] = "")
  632. {
  633.     new buf [16];
  634.     DOF2::GetStringEx (file, key, buf, sizeof (buf), tag);
  635.     return strval (buf);
  636. }
  637.  
  638. stock DOF2::SetHex (file [], key [], value, tag [] = "")
  639. {
  640.     new buf [16];
  641.     hexstr (value, buf);
  642.     return DOF2::SetString (file, key, buf, tag);
  643. }
  644.  
  645. stock DOF2::GetHex (file [], key [], tag [] = "")
  646. {
  647.     new buf [16];
  648.     DOF2::GetStringEx (file, key, buf, sizeof (buf), tag);
  649.     return strhex (buf);
  650. }
  651.  
  652. stock DOF2::SetBin (file [], key [], value, tag [] = "")
  653. {
  654.     new buf [35];
  655.     binstr (value, buf);
  656.     return DOF2::SetString (file, key, buf, tag);
  657. }
  658.  
  659. stock DOF2::GetBin (file [], key [], tag [] = "")
  660. {
  661.     new buf [35];
  662.     DOF2::GetStringEx (file, key, buf, sizeof (buf), tag);
  663.     return strbin (buf);
  664. }
  665.  
  666. stock DOF2::SetFloat (file [], key [], Float: value, tag [] = "")
  667. {
  668.     new buf [32];
  669.     format (buf, sizeof (buf), "%.8f", value);
  670.     return DOF2::SetString (file, key, buf, tag);
  671. }
  672.  
  673. stock Float: DOF2::GetFloat (file [], key [], tag [] = "")
  674. {
  675.     new buf [32];
  676.     DOF2::GetStringEx (file, key, buf, sizeof (buf), tag);
  677.     return floatstr (buf);
  678. }
  679.  
  680. stock bool: DOF2::GetBool (file [], key [], tag [] = "")
  681. {
  682.     new buf [16];
  683.     DOF2::GetStringEx (file, key, buf, sizeof (buf), tag);
  684.     return (strval (buf) || (buf [0] && !buf (value, "true", true)));
  685. }
  686.  
  687. stock DOF2::SetBool (file [], key [], bool: value, tag [] = "")
  688. {
  689.     if (value)
  690.         return DOF2::SetString (file, key, "true", tag);
  691.     else
  692.         return DOF2::SetString (file, key, "false", tag);
  693. }
  694.  
  695. /*-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/
  696.  
  697. stock DOF2::PrintFile (comment [] = "")
  698. {
  699.     if (CurrentFile [0])
  700.     {
  701.         new
  702.             File: f = fopen (CurrentFile, io_write),
  703.             bool: firstline = true,
  704.             currententry,
  705.             buf [MAX_LINE_SIZE],
  706.             entries,
  707.             i;
  708.  
  709.         if (f)
  710.         {
  711.             printf ("[DOF] Current file: %s", CurrentFile);
  712.             for ( ; i < Tag.Count; ++i)
  713.             {
  714.                 if (i)
  715.                 {
  716.                     strunpack (buf, Tag.Name [i]);
  717.                     format (buf, sizeof (buf), "[%s]", buf);
  718.                     if (!firstline)
  719.                         print (" ");
  720.                     else
  721.                         firstline = false;
  722.                     print (buf);
  723.                 }
  724.                 currententry = Tag.FirstEntry [i];
  725.                 while (currententry != -1)
  726.                 {
  727.                     strunpack (buf, Entry.Line [currententry]);
  728.                     currententry = Entry.NextEntry [currententry];
  729.                     firstline = false;
  730.                     ++entries;
  731.                     print (buf);
  732.                 }
  733.             }
  734.             printf ("* %d sections, %d entries", i, entries);
  735.             if (comment [0])
  736.                 printf ("* Comment: %s", comment);
  737.             return fclose (f);
  738.         }
  739.     }
  740.     return 0;
  741. }
  742.  
  743. stock DOF2::WriteFile ()
  744. {
  745.     if (CurrentFile [0])
  746.     {
  747.         new
  748.             File: f = fopen (CurrentFile, io_write),
  749.             bool: firstline = true,
  750.             currententry;
  751.  
  752.         if (f)
  753.         {
  754.             for (new i; i < Tag.Count; ++i)
  755.             {
  756.                 if (Tag.FirstEntry [i] != -1) // Do not write when empty.
  757.                 {
  758.                     if (i)
  759.                     {
  760.                         if (!firstline)
  761.                         {
  762.                             fputchar (f, '\r', UTF8);
  763.                             fputchar (f, '\n', UTF8);
  764.                         }
  765.                         else
  766.                             firstline = false;
  767.                         fputchar (f, '[', UTF8);
  768.                         fwritechars (f, Tag.Name [i]);
  769.                         fputchar (f, ']', UTF8);
  770.                         fputchar (f, '\r', UTF8);
  771.                         fputchar (f, '\n', UTF8);
  772.                     }
  773.                    
  774.                     currententry = Tag.FirstEntry [i];
  775.                     while (currententry != -1)
  776.                     {
  777.                         fwritechars (f, Entry.Line [currententry]);
  778.                         fputchar (f, '\r', UTF8);
  779.                         fputchar (f, '\n', UTF8);
  780.                         currententry = Entry.NextEntry [currententry];
  781.                         firstline = false;
  782.                     }
  783.                 }
  784.             }
  785.             FileChanged = false;
  786.             return fclose (f);
  787.         }
  788.     }
  789.     return 0;
  790. }
  791.  
  792. stock DOF2::ParseFile (file [], extraid, bool: callback = true)
  793. {
  794.     if (file [0])
  795.     {
  796.         if (((CurrentFile [0] && strcmp (CurrentFile, file)) || callback) && FileChanged)
  797.             DOF2::WriteFile ();
  798.  
  799.         new
  800.             File: f = fopen (file, io_readwrite),
  801.             line [MAX_LINE_SIZE char],
  802.             buf [MAX_LINE_SIZE],
  803.             key [MAX_LINE_SIZE],
  804.             value [MAX_LINE_SIZE],
  805.             tag [MAX_TAG_SIZE],
  806.             c,
  807.             pos;
  808.  
  809.         if (f)
  810.         {
  811.             FileChanged = false;
  812.             DOF2::SetFile (file);
  813.  
  814.             Tag.Count = 1;
  815.             Entry.Count = 0;
  816.             Tag.FirstEntry [0] = Tag.LastEntry [0] = -1;
  817.  
  818.             for (new i, size = flength (f); i < size; ++i)
  819.             {
  820.                 c = fgetchar (f, 0, UTF8);
  821.                 if (pos == MAX_LINE_SIZE - 1 || c == '\n' || c == '\r')
  822.                     c = '\0';
  823.                 line {pos++} = c;
  824.  
  825.                 if (c == '\0')
  826.                 {
  827.                     // A new section found. Add the section to the list of sections.
  828.                     if (line {0} == '[')
  829.                     {
  830.                         if (Tag.Count < MAX_TAGS)
  831.                         {
  832.                             pos = 1;
  833.                             while (line {pos} && line {pos} != ']' && (pos - 1) < MAX_TAG_SIZE)
  834.                             {
  835.                                 Tag.Name [Tag.Count]{pos - 1} = line {pos};
  836.                                 ++pos;
  837.                             }
  838.                             Tag.Name [Tag.Count]{pos - 1} = '\0';
  839.                             Tag.FirstEntry [Tag.Count] = Tag.LastEntry [Tag.Count] = -1;
  840.                             ++Tag.Count;
  841.                         }
  842.                     }
  843.                     else
  844.                     {
  845.                         if (line {0})
  846.                         {
  847.                             strunpack (buf, line);
  848.                             DOF2::ParseLine (buf, key, value);
  849.                             strunpack (tag, Tag.Name [Tag.Count - 1]);
  850.  
  851.                             // Call a specific function for a specific entry - ZCMD-style!
  852.                             if (callback)
  853.                             {
  854.                                 format (buf, sizeof (buf), "_OnParseFile_%s_%s", tag, key);
  855.                                 if (!CallRemoteFunction (buf, "is", extraid, value))
  856.                                     CallRemoteFunction ("_OnDefaultParseFile", "issss", extraid, value, key, tag, file);
  857.                             }
  858.  
  859.                             // Add entry to it's section and to the list which will be sorted.
  860.                             Entry.Line [Entry.Count] = line;
  861.                             Entry.Tag [Entry.Count] = Tag.Name [Tag.Count - 1];
  862.                             Entry.TagID [Entry.Count] = Tag.Count - 1;
  863.                             Entry.NextEntry [Entry.Count] = -1;
  864.  
  865.                             SortedEntries [Entry.Count][0] = DOF2::bernstein (key);
  866.                             SortedEntries [Entry.Count][1] = Entry.Count;
  867.  
  868.                             if (Tag.LastEntry [Tag.Count - 1] == -1)
  869.                             {
  870.                                 Tag.FirstEntry [Tag.Count - 1] = Tag.LastEntry [Tag.Count - 1] = Entry.Count;
  871.                                 Entry.PreviousEntry [Entry.Count] = -1;
  872.                             }
  873.                             else
  874.                             {
  875.                                 Entry.NextEntry [Tag.LastEntry [Tag.Count - 1]] = Entry.Count;
  876.                                 Entry.PreviousEntry [Entry.Count] = Tag.LastEntry [Tag.Count - 1];
  877.                                 Tag.LastEntry [Tag.Count - 1] = Entry.Count;
  878.                             }
  879.                             ++Entry.Count;
  880.                         }
  881.                     }
  882.                     pos = 0;
  883.                 }
  884.             }
  885.             /*
  886.              * Sort list of entries by it's hashcodes in O(n * log n) time.
  887.              * (Worst case is actually O(n * n), however, this QuickSort implementation chooses a randomized pivot
  888.              * to minimize the chance for the worst case.)
  889.              */
  890.             DOF2::QuickSort (SortedEntries, 0, Entry.Count - 1, true);
  891.             return fclose (f);
  892.         }
  893.     }
  894.     return 0;
  895. }
  896.  
  897. stock DOF2::ReparseFile (file [], extraid, bool: callback = true)
  898. {
  899.     if (file [0] && CurrentFile [0] && !strcmp (file, CurrentFile))
  900.     {
  901.         CurrentFile [0] = '\0';
  902.         return DOF2::ParseFile (file, extraid, callback);
  903.     }
  904.     return 0;
  905. }
  906.  
  907. static stock DOF2::ParseLine (line [], key [], value [], keysize = sizeof (key), valuesize = sizeof (value))
  908. {
  909.     new
  910.         pos,
  911.         readpos;
  912.  
  913.     if ((pos = charfind (line, '=')) != -1)
  914.     {
  915.         // Read key and value.
  916.         readpos = pos - 1;
  917.         while (readpos >= 0 && line [readpos] == ' ')
  918.             --readpos;
  919.  
  920.         if (readpos >= 0 && keysize > (readpos + 1))
  921.         {
  922.             key [readpos + 1] = '\0';
  923.             while (readpos >= 0)
  924.             {
  925.                 key [readpos] = line [readpos];
  926.                 --readpos;
  927.             }
  928.         }
  929.         else
  930.             return 0;
  931.  
  932.         readpos = pos + 1;
  933.         ++pos;
  934.         while (line [readpos] == ' ')
  935.         {
  936.             ++pos;
  937.             ++readpos;
  938.         }
  939.  
  940.         if (line [readpos])
  941.         {
  942.             while (readpos >= 0 && line [readpos] && valuesize > (readpos - pos + 1))
  943.             {
  944.                 value [readpos - pos] = line [readpos];
  945.                 ++readpos;
  946.             }
  947.             value [readpos - pos] = '\0';
  948.         }
  949.         else
  950.         {
  951.             key [0] = '\0';
  952.             return 0;
  953.         }
  954.         return 1;
  955.     }
  956.     return 0;
  957. }
  958.  
  959. stock DOF2::File (user [])
  960. {
  961.     new newfile [MAX_FILE_SIZE];
  962.     format (newfile, sizeof (newfile), USER_FILE_PATH, DOF2_udb_encode (user));
  963.     return newfile;
  964. }
  965.  
  966. stock bool: DOF2::CheckLogin (file [], password [])
  967.     return (file [0] && password [0] && num_hash (password) == DOF2::GetInt (file, "password"));
  968.  
  969. /*-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/
  970.  
  971. stock binstr (value, dest [], size = sizeof (dest))
  972.     format (dest, size, "0b%b", value);
  973.  
  974. stock hexstr (value, dest [], size = sizeof (dest))
  975.     format (dest, size, "0x%x", value);
  976.  
  977. stock strhex (string [])
  978. {
  979.     new
  980.         i,
  981.         value;
  982.     if (string [0] == '0' && (string [1] == 'x' || string [1] == 'X'))
  983.         i = 2;
  984.  
  985.     while (string [i])
  986.     {
  987.         value <<= 4;
  988.         switch (string [i])
  989.         {
  990.             case '0' .. '9':
  991.                 value |= string [i] - '0';
  992.  
  993.             case 'A' .. 'F':
  994.                 value |= string [i] - 'A' + 10;
  995.  
  996.             case 'a' .. 'f':
  997.                 value |= string [i] - 'a' + 10;
  998.  
  999.             default:
  1000.                 return 0;
  1001.         }
  1002.         ++i;
  1003.     }
  1004.     return value;
  1005. }
  1006.  
  1007. stock strbin (string [])
  1008. {
  1009.     new
  1010.         i,
  1011.         value;
  1012.  
  1013.     if (string [0] == '0' && (string [1] == 'b' || string [1] == 'B'))
  1014.         i = 2;
  1015.  
  1016.     while (string [i])
  1017.     {
  1018.         if (string [i] != '1' && string [i] != '0')
  1019.             return 0;
  1020.  
  1021.         value <<= 1;
  1022.         value |= (string [i] - '0');
  1023.         ++i;
  1024.     }
  1025.     return value;
  1026. }
  1027.  
  1028. /*-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/
  1029.  
  1030. static stock charfind (string [], c)
  1031. {
  1032.     for (new i, len = strlen (string); i < len; ++i)
  1033.         if (string [i] == c)
  1034.             return i;
  1035.     return -1;
  1036. }
  1037.  
  1038. static stock fwritechars (File: handle, c [])
  1039. {
  1040.     new pos;
  1041.     while (c {pos})
  1042.         fputchar (handle, c {pos++}, UTF8);
  1043. }
  1044.  
  1045. static stock DOF2::QuickSort (array [][], l, r, bool: randomize = true)
  1046. {
  1047.     if (r > l)
  1048.     {
  1049.         if (randomize)
  1050.         {
  1051.             new k = l + (random (32000) % (r - l + 1));
  1052.             DOF2::Swap (array [k], array [r]);
  1053.         }
  1054.  
  1055.         new
  1056.             i = l - 1,
  1057.             j = r,
  1058.             pivot = array [r][0];
  1059.  
  1060.         while (i < j)
  1061.         {
  1062.             do
  1063.                 ++i;
  1064.             while (array [i][0] <= pivot && i < r);
  1065.  
  1066.             do
  1067.                 --j;
  1068.             while (array [j][0] >= pivot && j > l);
  1069.  
  1070.             if (i < j)
  1071.                 DOF2::Swap (array [i], array [j]);
  1072.         }
  1073.         DOF2::Swap (array [i], array [r]);
  1074.         DOF2::QuickSort (array, l, i - 1);
  1075.         DOF2::QuickSort (array, i + 1, r);
  1076.     }
  1077. }
  1078.  
  1079. static stock DOF2::Swap (a [], b [])
  1080. {
  1081.     new c [2];
  1082.     c [0] = a [0];
  1083.     c [1] = a [1];
  1084.     a [0] = b [0];
  1085.     a [1] = b [1];
  1086.     b [0] = c [0];
  1087.     b [1] = c [1];
  1088. }
  1089.  
  1090. static stock DOF2::bernstein (string [])
  1091. {
  1092.     new
  1093.         h = -1,
  1094.         i,
  1095.         j;
  1096.  
  1097.     while ((j = string [i++]))
  1098.         h = h * 33 + j;
  1099.     return h;
  1100. }
  1101.  
  1102. /*-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/
  1103.  
  1104. stock DOF2::udb_encode (nickname [])
  1105. {
  1106.     new tmp [64];
  1107.     set (tmp, nickname);
  1108.     tmp = strreplace ("_", "_00", tmp);
  1109.     tmp = strreplace (";", "_01", tmp);
  1110.     tmp = strreplace ("!", "_02", tmp);
  1111.     tmp = strreplace ("/", "_03", tmp);
  1112.     tmp = strreplace ("\\", "_04", tmp);
  1113.     tmp = strreplace ("[", "_05", tmp);
  1114.     tmp = strreplace ("]", "_06", tmp);
  1115.     tmp = strreplace ("?", "_07", tmp);
  1116.     tmp = strreplace (".", "_08", tmp);
  1117.     tmp = strreplace ("*", "_09", tmp);
  1118.     tmp = strreplace ("<", "_10", tmp);
  1119.     tmp = strreplace (">", "_11", tmp);
  1120.     tmp = strreplace ("{", "_12", tmp);
  1121.     tmp = strreplace ("}", "_13", tmp);
  1122.     tmp = strreplace (" ", "_14", tmp);
  1123.     tmp = strreplace ("\"", "_15", tmp);
  1124.     tmp = strreplace (":", "_16", tmp);
  1125.     tmp = strreplace ("|", "_17", tmp);
  1126.     tmp = strreplace ("=", "_18", tmp);
  1127.     return tmp;
  1128. }
  1129.  
  1130. stock DOF2::udb_decode (nickname [])
  1131. {
  1132.     new tmp [64];
  1133.     set (tmp, nickname);
  1134.     tmp = strreplace ("_01", ";", tmp);
  1135.     tmp = strreplace ("_02", "!", tmp);
  1136.     tmp = strreplace ("_03", "/", tmp);
  1137.     tmp = strreplace ("_04", "\\", tmp);
  1138.     tmp = strreplace ("_05", "[", tmp);
  1139.     tmp = strreplace ("_06", "]", tmp);
  1140.     tmp = strreplace ("_07", "?", tmp);
  1141.     tmp = strreplace ("_08", ".", tmp);
  1142.     tmp = strreplace ("_09", "*", tmp);
  1143.     tmp = strreplace ("_10", "<", tmp);
  1144.     tmp = strreplace ("_11", ">", tmp);
  1145.     tmp = strreplace ("_12", "{", tmp);
  1146.     tmp = strreplace ("_13", "}", tmp);
  1147.     tmp = strreplace ("_14", " ", tmp);
  1148.     tmp = strreplace ("_15", "\"", tmp);
  1149.     tmp = strreplace ("_16", ":", tmp);
  1150.     tmp = strreplace ("_17", "|", tmp);
  1151.     tmp = strreplace ("_18", "=", tmp);
  1152.     tmp = strreplace ("_00", "_", tmp);
  1153.     return tmp;
  1154. }
  1155.  
  1156. /*-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/
  1157.  
  1158. #define DOF_ \
  1159.     DOF2_
  1160.  
  1161. #if defined DUDB_CONVERT
  1162.  
  1163.     #define dUser(%0).(             DOF2_GetString(DOF2_File(%0),
  1164.     #define dUserSet(%0).(          DOF2_SetString(DOF2_File(%0),
  1165.     #define dUserINT(%0).(          DOF2_GetInt(DOF2_File(%0),
  1166.     #define dUserSetINT(%0).(       DOF2_SetInt(DOF2_File(%0),
  1167.     #define dUserFLOAT(%0).(        DOF2_GetFloat(DOF2_File(%0),
  1168.     #define dUserSetFLOAT(%0).(     DOF2_SetFloat(DOF2_File(%0),
  1169.     #define udb_Create(%0,%1)       DOF2_CreateFile(DOF2_File(%0),%1)
  1170.     #define udb_RenameUser(%0,%1)   DOF2_RenameFile(DOF2_File(%0),DOF2_File(%1))
  1171.     #define udb_Exists(%0)          DOF2_FileExists(DOF2_File(%0))
  1172.     #define udb_Remove(%0)          DOF2_RemoveFile(DOF2_File(%0))
  1173.     #define udb_CheckLogin(%0,%1)   DOF2_CheckLogin(DOF2_File(%0),%1)
  1174.     #define udb_hash                DOF2_num_hash
  1175.     #define udb_encode              DOF2_udb_encode
  1176.     #define udb_decode              DOF2_udb_decode
  1177.  
  1178.     #if !defined _dudb_included
  1179.         #define _dudb_included
  1180.     #endif
  1181.  
  1182. #endif
  1183.  
  1184. #if defined DINI_CONVERT
  1185.  
  1186.     #define dini_Exists             DOF2_FileExists
  1187.     #define dini_Remove             DOF2_RemoveFile
  1188.     #define dini_Create             DOF2_CreateFile
  1189.     #define dini_Set                DOF2_SetString
  1190.     #define dini_Get                DOF2_GetString
  1191.     #define dini_IntSet             DOF2_SetInt
  1192.     #define dini_Int                DOF2_GetInt
  1193.     #define dini_BoolSet            DOF2_SetBool
  1194.     #define dini_Bool               DOF2_GetBool
  1195.     #define dini_FloatSet           DOF2_SetFloat
  1196.     #define dini_Float              DOF2_GetFloat
  1197.     #define dini_Unset              DOF2_Unset
  1198.     #define dini_Isset              DOF2_IsSet
  1199.  
  1200.     #if !defined _dini_included
  1201.         #define _dini_included
  1202.     #endif
  1203. #endif
  1204.  
  1205.  
Advertisement
Add Comment
Please, Sign In to add comment