Skip to content

Commit c2522f1

Browse files
committed
fix: 修复写入 AppleChu TOML 配置不合法问题
1 parent 56de032 commit c2522f1

2 files changed

Lines changed: 49 additions & 8 deletions

File tree

‎ChuChartManager/Services/AppleChuConfigService.cs‎

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -65,6 +65,9 @@ public void Save(
6565
var schema = AppleChuConfigSchema.Parse(metadata);
6666
var values = NormalizeRequest(schema, requestedSections);
6767
var output = AppleChuConfigTemplate.Apply(metadata.DefaultConfigToml, schema, values);
68+
// Match MCM's serializer contract: never replace a valid config with
69+
// output that the TOML parser cannot read back.
70+
_ = ParseDocument(output, "生成的 AppleChu.toml");
6871
lock (writeLock)
6972
WriteAtomically(GetConfigPath(gamePath), output);
7073
}

‎ChuChartManager/Services/AppleChuConfigTemplate.cs‎

Lines changed: 46 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
using System.Collections;
12
using System.Globalization;
23
using System.Text;
34

@@ -42,6 +43,11 @@ internal static string Apply(
4243
continue;
4344
}
4445

46+
// Do not let entries under an unrecognized table header get
47+
// interpreted as part of the previous known section.
48+
if (IsTableHeader(trimmed))
49+
currentSection = null;
50+
4551
if (currentSection != null
4652
&& UsesDisabledFlag(currentSection)
4753
&& IsDisabledEntry(trimmed))
@@ -93,7 +99,7 @@ internal static string Apply(
9399
string trimmed,
94100
IReadOnlyList<AppleChuConfigSectionSchema> schema)
95101
{
96-
var header = trimmed.StartsWith("#[", StringComparison.Ordinal) ? trimmed[1..] : trimmed;
102+
var header = NormalizeHeader(trimmed);
97103
if (header.StartsWith('[') && header.EndsWith(']')
98104
&& !header.StartsWith("[[", StringComparison.Ordinal))
99105
{
@@ -106,6 +112,21 @@ internal static string Apply(
106112
return null;
107113
}
108114

115+
private static bool IsTableHeader(string trimmed) =>
116+
NormalizeHeader(trimmed).StartsWith("[", StringComparison.Ordinal);
117+
118+
private static string NormalizeHeader(string trimmed)
119+
{
120+
var header = trimmed.StartsWith("#[", StringComparison.Ordinal) ? trimmed[1..] : trimmed;
121+
if (!header.StartsWith("[", StringComparison.Ordinal))
122+
return header;
123+
124+
// A table header may have a trailing comment. Section identifiers in
125+
// the generated schema cannot contain '#', so this is unambiguous.
126+
var comment = header.IndexOf('#');
127+
return comment >= 0 ? header[..comment].TrimEnd() : header;
128+
}
129+
109130
private static AppleChuConfigEntrySchema? FindEntry(
110131
string trimmed,
111132
AppleChuConfigSectionSchema section)
@@ -212,14 +233,31 @@ private static void AppendEntryLine(StringBuilder output, string line, bool comm
212233
byte or sbyte or short or ushort or int or uint or long or ulong =>
213234
Convert.ToString(value, CultureInfo.InvariantCulture) ?? "0",
214235
float or double or decimal => Convert.ToString(value, CultureInfo.InvariantCulture) ?? "0",
215-
IEnumerable<object?> items => $"[{string.Join(", ", items.Select(FormatTomlValue))}]",
236+
IEnumerable items when value is not string =>
237+
$"[{string.Join(", ", items.Cast<object?>().Select(FormatTomlValue))}]",
216238
_ => throw new ArgumentException("无法写入不受支持的 TOML 值"),
217239
};
218240

219-
private static string EscapeTomlString(string value) => value
220-
.Replace("\\", "\\\\")
221-
.Replace("\"", "\\\"")
222-
.Replace("\r", "\\r")
223-
.Replace("\n", "\\n")
224-
.Replace("\t", "\\t");
241+
private static string EscapeTomlString(string value)
242+
{
243+
var escaped = new StringBuilder(value.Length);
244+
foreach (var character in value)
245+
{
246+
switch (character)
247+
{
248+
case '\\': escaped.Append("\\\\"); break;
249+
case '"': escaped.Append("\\\""); break;
250+
case '\b': escaped.Append("\\b"); break;
251+
case '\t': escaped.Append("\\t"); break;
252+
case '\n': escaped.Append("\\n"); break;
253+
case '\f': escaped.Append("\\f"); break;
254+
case '\r': escaped.Append("\\r"); break;
255+
case < '\u0020': escaped.Append($"\\u{(int)character:X4}"); break;
256+
case '\u007f': escaped.Append("\\u007F"); break;
257+
default: escaped.Append(character); break;
258+
}
259+
}
260+
261+
return escaped.ToString();
262+
}
225263
}

0 commit comments

Comments
 (0)