using System; using System.Collections.Generic; using System.IO; using System.Linq; namespace FSI.Lib.WinSettings { /// /// Represents one name/value pair in an INI file. /// internal class IniSetting { /// /// The name of this INI setting. /// public string Name { get; set; } /// /// The value of this INI setting. /// public string Value { get; set; } } /// /// Class to create and read INI files. /// internal class IniFile { /// /// Section used for settings not under any section header (within []) /// public const string DefaultSectionName = "General"; /// /// Represents an entire INI file section. /// private class IniSection { public string Name { get; set; } public Dictionary Settings { get; private set; } public IniSection() { Name = string.Empty; Settings = new Dictionary(StringComparer.OrdinalIgnoreCase); } } private readonly Dictionary Sections = new Dictionary(StringComparer.OrdinalIgnoreCase); #region File functions /// /// Loads an INI settings file. /// /// Path of file to load. public void Load(string filename) { Sections.Clear(); // Default section IniSection section = new IniSection { Name = DefaultSectionName }; Sections.Add(section.Name, section); string line; #if NET472 using (StreamReader file = new StreamReader(filename)) { while ((line = file.ReadLine()) != null) { line = line.TrimStart(); if (line.Length > 0) { if (line[0] == ';') { // Ignore comments } else if (line[0] == '[') { // Parse section header int pos = line.IndexOf(']', 1); if (pos == -1) pos = line.Length; string name = line.Substring(1, pos - 1).Trim(); if (name.Length > 0) { if (!Sections.TryGetValue(name, out section)) { section = new IniSection { Name = name }; Sections.Add(section.Name, section); } } } else { // Parse setting name and value string name, value; int pos = line.IndexOf('='); if (pos == -1) { name = line.Trim(); value = string.Empty; } else { name = line.Substring(0, pos).Trim(); value = line.Substring(pos + 1); } if (name.Length > 0) { if (section.Settings.TryGetValue(name, out IniSetting setting)) { setting.Value = value; } else { setting = new IniSetting { Name = name, Value = value }; section.Settings.Add(name, setting); } } } } } } #elif NET6_0_OR_GREATER using StreamReader file = new StreamReader(filename); while ((line = file.ReadLine()) != null) { line = line.TrimStart(); if (line.Length > 0) { if (line[0] == ';') { // Ignore comments } else if (line[0] == '[') { // Parse section header int pos = line.IndexOf(']', 1); if (pos == -1) pos = line.Length; string name = line.Substring(1, pos - 1).Trim(); if (name.Length > 0) { if (!Sections.TryGetValue(name, out section)) { section = new IniSection { Name = name }; Sections.Add(section.Name, section); } } } else { // Parse setting name and value string name, value; int pos = line.IndexOf('='); if (pos == -1) { name = line.Trim(); value = string.Empty; } else { name = line.Substring(0, pos).Trim(); value = line.Substring(pos + 1); } if (name.Length > 0) { if (section.Settings.TryGetValue(name, out IniSetting setting)) { setting.Value = value; } else { setting = new IniSetting { Name = name, Value = value }; section.Settings.Add(name, setting); } } } } } #endif } /// /// Writes the current settings to an INI file. If the file already exists, it is overwritten. /// /// Path of file to write to. public void Save(string filename) { #if NET472 using (StreamWriter file = new StreamWriter(filename, false)) { bool firstLine = true; foreach (IniSection section in Sections.Values) { if (firstLine) firstLine = false; else file.WriteLine(); if (section.Settings.Any()) { file.WriteLine("[{0}]", section.Name); foreach (IniSetting setting in section.Settings.Values) file.WriteLine("{0}={1}", setting.Name, setting.Value); } } } #elif NET6_0_OR_GREATER using StreamWriter file = new StreamWriter(filename, false); bool firstLine = true; foreach (IniSection section in Sections.Values) { if (firstLine) firstLine = false; else file.WriteLine(); if (section.Settings.Any()) { file.WriteLine("[{0}]", section.Name); foreach (IniSetting setting in section.Settings.Values) file.WriteLine("{0}={1}", setting.Name, setting.Value); } } #endif } #endregion #region Read values /// /// Returns the value of an INI setting. /// /// The INI file section. /// The INI setting name. /// The value to return if the setting was not found. /// Returns the specified setting value. public string GetSetting(string section, string setting, string defaultValue = null) { if (Sections.TryGetValue(section, out IniSection iniSection)) { if (iniSection.Settings.TryGetValue(setting, out IniSetting iniSetting)) return iniSetting.Value; } return defaultValue; } /// /// Returns the value of an INI setting as an integer value. /// /// The INI file section. /// The INI setting name. /// The value to return if the setting was not found, /// or if it could not be converted to a integer value. /// Returns the specified setting value as an integer value. public int GetSetting(string section, string setting, int defaultValue) { if (int.TryParse(GetSetting(section, setting), out int value)) return value; return defaultValue; } /// /// Returns the value of an INI setting as a double value. /// /// The INI file section. /// The INI setting name. /// The value to return if the setting was not found, /// or if it could not be converted to a double value. /// Returns the specified setting value as a double value. public double GetSetting(string section, string setting, double defaultValue) { if (double.TryParse(GetSetting(section, setting), out double value)) return value; return defaultValue; } /// /// Returns the value of an INI setting as a Boolean value. /// /// The INI file section. /// The INI setting name. /// The value to return if the setting was not found, /// or if it could not be converted to a Boolean value. /// Returns the specified setting value as a Boolean. public bool GetSetting(string section, string setting, bool defaultValue) { if (ConvertToBool(GetSetting(section, setting), out bool value)) return value; return defaultValue; } /// /// Returns all settings in the given INI section. /// /// The section that contains the settings to be retrieved. /// Returns the settings in the given INI section. public IEnumerable GetSectionSettings(string section) { if (Sections.TryGetValue(section, out IniSection iniSection)) { foreach (var setting in iniSection.Settings) yield return setting.Value; } } #endregion #region Write values /// /// Sets an INI file setting. The setting is not written to disk until /// is called. /// /// The INI-file section. /// The name of the INI-file setting. /// The value of the INI-file setting public void SetSetting(string section, string setting, string value) { if (!Sections.TryGetValue(section, out IniSection iniSection)) { iniSection = new IniSection { Name = section }; Sections.Add(iniSection.Name, iniSection); } if (!iniSection.Settings.TryGetValue(setting, out IniSetting iniSetting)) { iniSetting = new IniSetting { Name = setting }; iniSection.Settings.Add(iniSetting.Name, iniSetting); } iniSetting.Value = value; } /// /// Sets an INI file setting with an integer value. /// /// The INI-file section. /// The name of the INI-file setting. /// The value of the INI-file setting public void SetSetting(string section, string setting, int value) { SetSetting(section, setting, value.ToString()); } /// /// Sets an INI file setting with a double value. /// /// The INI-file section. /// The name of the INI-file setting. /// The value of the INI-file setting public void SetSetting(string section, string setting, double value) { SetSetting(section, setting, value.ToString()); } /// /// Sets an INI file setting with a Boolean value. /// /// The INI-file section. /// The name of the INI-file setting. /// The value of the INI-file setting public void SetSetting(string section, string setting, bool value) { SetSetting(section, setting, value.ToString()); } #endregion #region Boolean parsing private readonly string[] TrueStrings = { "true", "yes", "on" }; private readonly string[] FalseStrings = { "false", "no", "off" }; private bool ConvertToBool(string s, out bool value) { if (s == null) value = false; if (TrueStrings.Any(s2 => string.Compare(s, s2, true) == 0)) value = true; else if (FalseStrings.Any(s2 => string.Compare(s, s2, true) == 0)) value = false; else if (int.TryParse(s, out int i)) value = i != 0; else { value = false; return false; } return true; } #endregion //public void Dump() //{ // foreach (IniSection section in Sections.Values) // { // Debug.WriteLine(string.Format("[{0}]", section.Name)); // foreach (IniSetting setting in section.Settings.Values) // Debug.WriteLine("[{0}]=[{1}]", setting.Name, setting.Value); // } //} } }