using System;
using System.Collections.Generic;
using System.IO;
using System.Text.RegularExpressions;
using BepInEx.Logging;
namespace BepInEx
{
///
/// A helper class to handle persistent data.
///
public static class Config
{
private static readonly Dictionary> cache = new Dictionary>();
private static string ConfigPath => Path.Combine(Paths.BepInExRootPath, "config.ini");
private static readonly Regex sanitizeKeyRegex = new Regex(@"[^a-zA-Z0-9\-\.]+");
private static void RaiseConfigReloaded()
{
ConfigReloaded?.Invoke();
}
///
/// An event that is fired every time the config is reloaded.
///
public static event Action ConfigReloaded;
///
/// If enabled, writes the config to disk every time a value is set.
///
public static bool SaveOnConfigSet { get; set; } = true;
static Config()
{
if (File.Exists(ConfigPath))
{
ReloadConfig();
}
else
{
SaveConfig();
}
}
///
/// Returns the value of the key if found, otherwise returns the default value.
///
/// The key to search for.
/// The default value to return if the key is not found.
/// The section of the config to search the key for.
/// The value of the key.
public static string GetEntry(string key, string defaultValue = "", string section = "")
{
try
{
key = Sanitize(key);
section = section.IsNullOrWhiteSpace() ? "Global" : Sanitize(section);
if (!cache.TryGetValue(section, out Dictionary subdict))
{
SetEntry(key, defaultValue, section);
return defaultValue;
}
if (subdict.TryGetValue(key, out string value))
return value;
SetEntry(key, defaultValue, section);
return defaultValue;
}
catch (Exception ex)
{
Logger.Log(LogLevel.Error | LogLevel.Message, "Unable to read config entry!");
Logger.LogError(ex);
return defaultValue;
}
}
///
/// Reloads the config from disk. Unwritten changes are lost.
///
public static void ReloadConfig()
{
cache.Clear();
string currentSection = "";
foreach (string rawLine in File.ReadAllLines(ConfigPath))
{
string line = rawLine.Trim();
bool commentIndex = line.StartsWith(";") || line.StartsWith("#");
if (commentIndex) //trim comment
continue;
if (line.StartsWith("[") && line.EndsWith("]")) //section
{
currentSection = line.Substring(1, line.Length - 2);
continue;
}
string[] split = line.Split('='); //actual config line
if (split.Length != 2)
continue; //empty/invalid line
if (!cache.ContainsKey(currentSection))
cache[currentSection] = new Dictionary();
cache[currentSection][split[0]] = split[1];
}
RaiseConfigReloaded();
}
///
/// Writes the config to disk.
///
public static void SaveConfig()
{
using (StreamWriter writer = new StreamWriter(File.Create(ConfigPath), System.Text.Encoding.UTF8))
foreach (var sectionKv in cache)
{
writer.WriteLine($"[{sectionKv.Key}]");
foreach (var entryKv in sectionKv.Value)
writer.WriteLine($"{entryKv.Key}={entryKv.Value}");
writer.WriteLine();
}
}
///
/// Sets the value of the key in the config.
///
/// The key to set the value to.
/// The value to set.
public static void SetEntry(string key, string value, string section = "")
{
try
{
key = Sanitize(key);
section = section.IsNullOrWhiteSpace() ? "Global" : Sanitize(section);
if (!cache.TryGetValue(section, out Dictionary subdict))
{
subdict = new Dictionary();
cache[section] = subdict;
}
subdict[key] = value;
if (SaveOnConfigSet)
SaveConfig();
}
catch (Exception ex)
{
Logger.Log(LogLevel.Error | LogLevel.Message, "Unable to save config entry!");
Logger.LogError(ex);
}
}
///
/// Returns whether a value is currently set.
///
/// The key to check against
/// The section to check in
/// True if the key is present
public static bool HasEntry(string key, string section = "")
{
key = Sanitize(key);
section = section.IsNullOrWhiteSpace() ? "Global" : Sanitize(section);
return cache.ContainsKey(section) && cache[section].ContainsKey(key);
}
///
/// Removes a value from the config.
///
/// The key to remove
/// The section to remove from
/// True if the key was removed
public static bool UnsetEntry(string key, string section = "")
{
key = Sanitize(key);
section = section.IsNullOrWhiteSpace() ? "Global" : Sanitize(section);
if (!HasEntry(key, section))
return false;
cache[section].Remove(key);
return true;
}
///
/// Replaces any potentially breaking input with underscores.
///
/// The text to sanitize.
/// Sanitized text.
public static string Sanitize(string text)
{
return sanitizeKeyRegex.Replace(text, "_");
}
#region Extensions
public static string GetEntry(this BaseUnityPlugin plugin, string key, string defaultValue = "")
{
return GetEntry(key, defaultValue, MetadataHelper.GetMetadata(plugin).GUID);
}
public static void SetEntry(this BaseUnityPlugin plugin, string key, string value)
{
SetEntry(key, value, MetadataHelper.GetMetadata(plugin).GUID);
}
public static bool HasEntry(this BaseUnityPlugin plugin, string key)
{
return HasEntry(key, MetadataHelper.GetMetadata(plugin).GUID);
}
#endregion Extensions
}
}