diff --git a/Apewer/Models/IniFile.cs b/Apewer/Models/IniFile.cs
new file mode 100644
index 0000000..ef36a5a
--- /dev/null
+++ b/Apewer/Models/IniFile.cs
@@ -0,0 +1,196 @@
+using System;
+using System.Collections;
+using System.Collections.Generic;
+using System.Text;
+
+namespace Apewer.Models
+{
+
+ /// INI 文件。
+ [Serializable]
+ public sealed class IniFile : IEnumerable, IToJson
+ {
+
+ List _sections = new List();
+
+ #region Constructors
+
+ ///
+ public IniFile() { }
+
+ ///
+ public IniFile(IEnumerable sections)
+ {
+ if (sections == null) throw new ArgumentNullException(nameof(sections));
+ sections.ForEach(Add);
+ }
+
+ #endregion
+
+ #region CRUD
+
+ /// 节的数量。
+ public int Count { get => _sections.Count; }
+
+ /// 添加节。
+ public void Add(IniSection section)
+ {
+ if (section == null) return;
+ _sections.Add(section);
+ }
+
+ /// 添加节。
+ public void Add(string name, TextField[] fields) => _sections.Add(new IniSection(name, fields));
+
+ /// 获取节。
+ public IniSection Get(string sectionName)
+ {
+ var emptyName = sectionName.IsEmpty();
+ foreach (var section in _sections)
+ {
+ if (emptyName)
+ {
+ if (section.Name.IsEmpty()) return section;
+ }
+ else
+ {
+ if (section.Name == sectionName) return section;
+ }
+ }
+ return null;
+ }
+
+ /// 移除节。
+ public void Remove(IniSection section)
+ {
+ while (_sections.Contains(section)) _sections.Remove(section);
+ }
+
+ /// 移除节。
+ public void Remove(string sectionName)
+ {
+ var emptyName = sectionName.IsEmpty();
+ foreach (var section in _sections)
+ {
+ if (emptyName)
+ {
+ _sections = _sections.FindAll(x => x.Name.IsEmpty());
+ }
+ else
+ {
+ _sections = _sections.FindAll(x => x.Name == sectionName);
+ }
+ }
+ }
+
+ #endregion
+
+ #region IEnumerable
+
+ ///
+ public IEnumerator GetEnumerator() => new Enumerator(i => i < _sections.Count ? new Class(_sections[i]) : null);
+
+ IEnumerator IEnumerable.GetEnumerator() => GetEnumerator();
+
+ #endregion
+
+ #region IToJson
+
+ ///
+ public Json ToJson()
+ {
+ var json = Json.NewArray();
+ _sections.ForEach(section => json.AddItem(section.ToJson()));
+ return json;
+ }
+
+ #endregion
+
+ #region ToString
+
+ const string CRLF = "\r\n";
+
+ ///
+ public override string ToString() => ToString(CRLF);
+
+ ///
+ public string ToString(string seperator)
+ {
+ if (seperator.IsEmpty()) seperator = CRLF;
+
+ var sb = new StringBuilder();
+
+ var noName = 0;
+ foreach (var section in _sections)
+ {
+ if (section == null) continue;
+
+ if (section.Name.IsEmpty())
+ {
+ var count = section.Count;
+ for (var i = 0; i < count; i++)
+ {
+ var field = section.GetField(i);
+ if (Append(sb, field))
+ {
+ sb.Append(seperator);
+ noName += 1;
+ }
+ }
+ }
+
+ }
+ if (noName > 1) sb.Append(seperator);
+
+ foreach (var section in _sections)
+ {
+ if (section == null) continue;
+
+ if (section.Name.NotEmpty())
+ {
+ sb.Append("[");
+ sb.Append(section.Name);
+ sb.Append("]");
+ sb.Append(seperator);
+
+ foreach (var field in section)
+ {
+ if (Append(sb, field))
+ {
+ sb.Append(seperator);
+ }
+ }
+ }
+ sb.Append(seperator);
+ }
+
+ return sb.ToString();
+ }
+
+ static bool Append(StringBuilder sb, TextField field)
+ {
+ var nameEmpty = field.Name.IsEmpty();
+ var valueEmpty = field.Value.IsEmpty();
+ if (nameEmpty && valueEmpty) return false;
+
+ if (!nameEmpty)
+ {
+ sb.Append(field.Name);
+ sb.Append(" =");
+ }
+
+ if (!valueEmpty)
+ {
+ if (nameEmpty) sb.Append("; ");
+ else sb.Append(" ");
+ sb.Append(field.Value);
+ }
+
+ return true;
+ }
+
+ #endregion
+
+ }
+
+}
diff --git a/Apewer/Models/IniSection.cs b/Apewer/Models/IniSection.cs
new file mode 100644
index 0000000..df39f3b
--- /dev/null
+++ b/Apewer/Models/IniSection.cs
@@ -0,0 +1,196 @@
+using System;
+using System.Collections;
+using System.Collections.Generic;
+using System.Text;
+using static System.Collections.Specialized.BitVector32;
+
+namespace Apewer.Models
+{
+
+ /// INI 节。
+ [Serializable]
+ public sealed class IniSection : ICollection, IEnumerable, IToJson
+ {
+
+ private List _fields = new List();
+
+ /// 节的名称(不含方括号)。
+ public string Name { get; set; }
+
+ ///
+ public override string ToString() => $"Name = \"{Name}\", Count = {_fields.Count}";
+
+ #region ICollection
+
+ ///
+ public int Count { get => _fields.Count; }
+
+ /// 集合是只读的。
+ /// 此值始终为 FALSE。
+ public bool IsReadOnly { get => false; }
+
+ /// 添加字段。
+ ///
+ public void Add(TextField item)
+ {
+ if (item == null) throw new ArgumentNullException(nameof(item));
+ _fields.Add(item);
+ }
+
+ /// 清空字段。
+ public void Clear() => _fields.Clear();
+
+ /// 检查此集合是否包含指定的字段。
+ public bool Contains(TextField item) => _fields.Contains(item);
+
+ /// 复制字段到数组中。
+ ///
+ ///
+ ///
+ public void CopyTo(TextField[] array, int arrayIndex)
+ {
+ if (array == null) throw new ArgumentNullException(nameof(array));
+ if (arrayIndex < 0) throw new ArgumentOutOfRangeException(nameof(arrayIndex));
+ if (array.Length - arrayIndex < _fields.Count) throw new ArgumentException("目标数组长度不足。");
+
+ _fields.CopyTo(array, arrayIndex);
+ }
+
+ /// 移除字段。
+ public bool Remove(TextField item)
+ {
+ var removed = false;
+ while (_fields.Contains(item))
+ {
+ _fields.Remove(item);
+ removed = true;
+ }
+ return removed;
+ }
+
+ #endregion
+
+ #region Enumerable
+
+ ///
+ public IEnumerator GetEnumerator() => new Enumerator(i => i < _fields.Count ? new Class(_fields[i]) : null);
+
+ ///
+ IEnumerator IEnumerable.GetEnumerator() => GetEnumerator();
+
+ #endregion
+
+ #region IToJson
+
+ ///
+ public Json ToJson()
+ {
+ var fields = Json.NewObject();
+ foreach (var field in _fields)
+ {
+ fields.SetProperty(field.Name, field.Value);
+ }
+
+ var json = Json.NewObject();
+ json.SetProperty("name", Name);
+ json.SetProperty("fields", fields);
+
+ return json;
+ }
+
+ #endregion
+
+ /// 检查此集合是否包含指定的字段。
+ /// 字段名称。
+ public bool Contains(string name)
+ {
+ var count = _fields.Count;
+ for (var i = 0; i < count; i++)
+ {
+ if (_fields[0].Name == name) return true;
+ }
+ return false;
+ }
+
+ /// 获取字段。
+ /// "
+ public TextField GetField(int index)
+ {
+ if (index < 0 || index >= _fields.Count) throw new ArgumentOutOfRangeException(nameof(index));
+ return _fields[index];
+ }
+
+ ///
+ public string GetValue(string name)
+ {
+ if (name.IsEmpty()) throw new ArgumentNullException(nameof(name));
+
+ foreach (var field in _fields)
+ {
+ if (field.Name == name) return field.Value;
+ }
+ return null;
+ }
+
+ ///
+ public void SetValue(string name, string value)
+ {
+ if (name.IsEmpty()) throw new ArgumentNullException(nameof(name));
+
+ var count = _fields.Count;
+ var setted = false;
+ for (var i = 0; i < count; i++)
+ {
+ if (_fields[i].Name == name)
+ {
+ _fields[i].Value = value;
+ setted = true;
+ }
+ }
+
+ if (!setted)
+ {
+ var field = new TextField(name, value);
+ _fields.Add(field);
+ }
+ }
+
+ /// 获取或设置字段。
+ public string this[string name] { get => GetValue(name); set => SetValue(name, value); }
+
+ ///
+ public IniSection() { }
+
+ ///
+ public IniSection(string name) : this()
+ {
+ this.Name = name;
+ }
+
+ ///
+ ///
+ public IniSection(string name, IEnumerable fields) : this(name)
+ {
+ if (fields == null) throw new ArgumentNullException(nameof(fields));
+ _fields.AddRange(fields.Map(x => x == null ? null : new TextField(x.Name, x.Value)).FindAll(x => x != null));
+ }
+
+ ///
+ ///
+ public IniSection(IEnumerable fields) : this(null, fields) { }
+
+ ///
+ ///
+ public IniSection(string name, IEnumerable> fields) : this(name)
+ {
+ if (fields == null) throw new ArgumentNullException(nameof(fields));
+ _fields.AddRange(fields.Map(x => new TextField(x)));
+ }
+
+ ///
+ ///
+ public IniSection(IEnumerable> fields) : this(null, fields) { }
+
+ }
+
+}
diff --git a/Apewer/Models/TextField.cs b/Apewer/Models/TextField.cs
new file mode 100644
index 0000000..2f2009c
--- /dev/null
+++ b/Apewer/Models/TextField.cs
@@ -0,0 +1,57 @@
+using System;
+using System.Collections.Generic;
+using System.Text;
+
+namespace Apewer.Models
+{
+
+ /// 文本字段。
+ [Serializable]
+ public sealed class TextField
+ {
+
+ /// 名称。
+ public string Name { get; set; }
+
+ /// 值。
+ public string Value { get; set; }
+
+ /// 文本字段。
+ public TextField() { }
+
+ /// 创建文本字段。
+ public TextField(string name, string value)
+ {
+ this.Name = name;
+ this.Value = value;
+ }
+
+ /// 创建文本字段。
+ public TextField(KeyValuePair pair)
+ {
+ this.Name = pair.Key;
+ this.Value = pair.Value;
+ }
+
+ ///
+ public override string ToString() => $"{Name} = {Value}";
+
+ ///
+ public override bool Equals(object obj)
+ {
+ if (obj != null && obj is TextField field) return field.Name == Name && field.Value == Value;
+ return false;
+ }
+
+ ///
+ public override int GetHashCode() => (Name ?? "").GetHashCode() ^ (Value ?? "").GetHashCode();
+
+ ///
+ public static implicit operator KeyValuePair(TextField field) => field == null ? default : new KeyValuePair(field.Name, field.Value);
+
+ ///
+ public static implicit operator TextField(KeyValuePair pair) => new KeyValuePair(pair.Key, pair.Value);
+
+ }
+
+}