using System; using System.Collections.Generic; using System.IO; using System.Linq; using System.Text; namespace SabreTools.Library.IO { public class IniReader : IDisposable { /// /// Internal stream reader for inputting /// private StreamReader sr; /// /// Get if at end of stream /// public bool EndOfStream { get { return sr?.EndOfStream ?? true; } } /// /// Contents of the currently read line as a key value pair /// public KeyValuePair? KeyValuePair { get; private set; } = null; /// /// Contents of the current line, unprocessed /// public string CurrentLine { get; private set; } = string.Empty; /// /// Get the current line number /// public long LineNumber { get; private set; } = 0; /// /// Current row type /// public IniRowType RowType { get; private set; } = IniRowType.None; /// /// Current section being read /// public string Section { get; private set; } = string.Empty; /// /// Validate that rows are in key=value format /// public bool ValidateRows { get; set; } = true; /// /// Constructor for reading from a file /// public IniReader(string filename) { sr = new StreamReader(filename); } /// /// Constructor for reading from a stream /// public IniReader(Stream stream, Encoding encoding) { sr = new StreamReader(stream, encoding); } /// /// Read the next line in the INI file /// public bool ReadNextLine() { if (!(sr.BaseStream?.CanRead ?? false) || sr.EndOfStream) return false; CurrentLine = sr.ReadLine().Trim(); LineNumber++; ProcessLine(); return true; } /// /// Process the current line and extract out values /// private void ProcessLine() { // Comment if (CurrentLine.StartsWith(";")) { KeyValuePair = null; RowType = IniRowType.Comment; } // Section else if (CurrentLine.StartsWith("[") && CurrentLine.EndsWith("]")) { KeyValuePair = null; RowType = IniRowType.SectionHeader; Section = CurrentLine.TrimStart('[').TrimEnd(']'); } // KeyValuePair else if (CurrentLine.Contains("=")) { // Split the line by '=' for key-value pairs string[] data = CurrentLine.Split('='); // If the value field contains an '=', we need to put them back in string key = data[0].Trim(); string value = string.Join("=", data.Skip(1)).Trim(); KeyValuePair = new KeyValuePair(key, value); RowType = IniRowType.KeyValue; } // Empty else if (string.IsNullOrEmpty(CurrentLine)) { KeyValuePair = null; CurrentLine = string.Empty; RowType = IniRowType.None; } // Invalid else { KeyValuePair = null; RowType = IniRowType.Invalid; if (ValidateRows) throw new InvalidDataException($"Invalid INI row found, cannot continue: {CurrentLine}"); } } /// /// Dispose of the underlying reader /// public void Dispose() { sr.Dispose(); } } }