Config.cs 7.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231
  1. using System;
  2. using System.Collections.Generic;
  3. using System.IO;
  4. using System.Text.RegularExpressions;
  5. using BepInEx.Common;
  6. namespace BepInEx
  7. {
  8. /// <summary>
  9. /// A helper class to handle persistent data.
  10. /// </summary>
  11. public static class Config
  12. {
  13. private static Dictionary<string, Dictionary<string, string>> cache = new Dictionary<string, Dictionary<string, string>>();
  14. private static string configPath => Path.Combine(Common.Utility.PluginsDirectory, "config.ini");
  15. private static Regex sanitizeKeyRegex = new Regex(@"[^a-zA-Z0-9\-\.]+");
  16. private static void RaiseConfigReloaded()
  17. {
  18. var handler = ConfigReloaded;
  19. if (handler != null)
  20. handler.Invoke();
  21. }
  22. /// <summary>
  23. /// An event that is fired every time the config is reloaded.
  24. /// </summary>
  25. public static event Action ConfigReloaded;
  26. /// <summary>
  27. /// If enabled, writes the config to disk every time a value is set.
  28. /// </summary>
  29. public static bool SaveOnConfigSet { get; set; } = true;
  30. static Config()
  31. {
  32. if (File.Exists(configPath))
  33. {
  34. ReloadConfig();
  35. }
  36. else
  37. {
  38. SaveConfig();
  39. }
  40. }
  41. /// <summary>
  42. /// Returns the value of the key if found, otherwise returns the default value.
  43. /// </summary>
  44. /// <param name="key">The key to search for.</param>
  45. /// <param name="defaultValue">The default value to return if the key is not found.</param>
  46. /// <param name="section">The section of the config to search the key for.</param>
  47. /// <returns>The value of the key.</returns>
  48. public static string GetEntry(string key, string defaultValue = "", string section = "")
  49. {
  50. key = Sanitize(key);
  51. if (section.IsNullOrWhiteSpace())
  52. section = "Global";
  53. else
  54. section = Sanitize(section);
  55. Dictionary<string, string> subdict;
  56. if (!cache.TryGetValue(section, out subdict))
  57. {
  58. SetEntry(key, defaultValue, section);
  59. return defaultValue;
  60. }
  61. if (subdict.TryGetValue(key, out string value))
  62. {
  63. return value;
  64. }
  65. else
  66. {
  67. SetEntry(key, defaultValue, section);
  68. return defaultValue;
  69. }
  70. }
  71. /// <summary>
  72. /// Reloads the config from disk. Unwritten changes are lost.
  73. /// </summary>
  74. public static void ReloadConfig()
  75. {
  76. cache.Clear();
  77. string currentSection = "";
  78. foreach (string rawLine in File.ReadAllLines(configPath))
  79. {
  80. string line = rawLine.Trim();
  81. bool commentIndex = line.StartsWith(";") || line.StartsWith("#");
  82. if (commentIndex) //trim comment
  83. continue;
  84. if (line.StartsWith("[") && line.EndsWith("]")) //section
  85. {
  86. currentSection = line.Substring(1, line.Length - 2);
  87. continue;
  88. }
  89. string[] split = line.Split('='); //actual config line
  90. if (split.Length != 2)
  91. continue; //empty/invalid line
  92. if (!cache.ContainsKey(currentSection))
  93. cache[currentSection] = new Dictionary<string, string>();
  94. cache[currentSection][split[0]] = split[1];
  95. }
  96. RaiseConfigReloaded();
  97. }
  98. /// <summary>
  99. /// Writes the config to disk.
  100. /// </summary>
  101. public static void SaveConfig()
  102. {
  103. using (StreamWriter writer = new StreamWriter(File.Create(configPath), System.Text.Encoding.UTF8))
  104. foreach (var sectionKv in cache)
  105. {
  106. writer.WriteLine($"[{sectionKv.Key}]");
  107. foreach (var entryKv in sectionKv.Value)
  108. writer.WriteLine($"{entryKv.Key}={entryKv.Value}");
  109. writer.WriteLine();
  110. }
  111. }
  112. /// <summary>
  113. /// Sets the value of the key in the config.
  114. /// </summary>
  115. /// <param name="key">The key to set the value to.</param>
  116. /// <param name="value">The value to set.</param>
  117. public static void SetEntry(string key, string value, string section = "")
  118. {
  119. key = Sanitize(key);
  120. if (section.IsNullOrWhiteSpace())
  121. section = "Global";
  122. else
  123. section = Sanitize(section);
  124. Dictionary<string, string> subdict;
  125. if (!cache.TryGetValue(section, out subdict))
  126. {
  127. subdict = new Dictionary<string, string>();
  128. cache[section] = subdict;
  129. }
  130. subdict[key] = value;
  131. if (SaveOnConfigSet)
  132. SaveConfig();
  133. }
  134. /// <summary>
  135. /// Returns wether a value is currently set.
  136. /// </summary>
  137. /// <param name="key">The key to check against</param>
  138. /// <param name="section">The section to check in</param>
  139. /// <returns>True if the key is present</returns>
  140. public static bool HasEntry(string key, string section = "")
  141. {
  142. key = Sanitize(key);
  143. if (section.IsNullOrWhiteSpace())
  144. section = "Global";
  145. else
  146. section = Sanitize(section);
  147. return cache.ContainsKey(section) && cache[section].ContainsKey(key);
  148. }
  149. /// <summary>
  150. /// Removes a value from the config.
  151. /// </summary>
  152. /// <param name="key">The key to remove</param>
  153. /// <param name="section">The section to remove from</param>
  154. /// <returns>True if the key was removed</returns>
  155. public static bool UnsetEntry(string key, string section = "")
  156. {
  157. key = Sanitize(key);
  158. if (section.IsNullOrWhiteSpace())
  159. section = "Global";
  160. else
  161. section = Sanitize(section);
  162. if (!HasEntry(key, section))
  163. return false;
  164. cache[section].Remove(key);
  165. return true;
  166. }
  167. /// <summary>
  168. /// Replaces any potentially breaking input with underscores.
  169. /// </summary>
  170. /// <param name="text">The text to sanitize.</param>
  171. /// <returns>Sanitized text.</returns>
  172. public static string Sanitize(string text)
  173. {
  174. return sanitizeKeyRegex.Replace(text, "_");
  175. }
  176. #region Extensions
  177. public static string GetEntry(this BaseUnityPlugin plugin, string key, string defaultValue = "")
  178. {
  179. return GetEntry(key, defaultValue, MetadataHelper.GetMetadata(plugin).GUID);
  180. }
  181. public static void SetEntry(this BaseUnityPlugin plugin, string key, string value)
  182. {
  183. SetEntry(key, value, MetadataHelper.GetMetadata(plugin).GUID);
  184. }
  185. public static bool HasEntry(this BaseUnityPlugin plugin, string key)
  186. {
  187. return HasEntry(key, MetadataHelper.GetMetadata(plugin).GUID);
  188. }
  189. #endregion Extensions
  190. }
  191. }