mirror of
https://github.com/MaSzyna-EU07/StarterNG.git
synced 2026-10-16 23:49:19 +02:00
Removes every // and /// comment from the 28 .cs files, -1331 lines.
Done with a lexer rather than a regex: the sources carry plenty of // inside
string literals (@"//\$decor\b", the //$o trainset directives, sb.Append("// "),
"https://wiki.eu07.pl/") and those must survive. It tracks regular, verbatim and
interpolated strings, interpolation holes and char literals. Every file was
checked by comparing the sequence of string-literal characters before and after
the strip; all 28 match. BOMs preserved, blank-line runs collapsed to one.
Claude-Session: https://claude.ai/code/session_01HamVNWE9mwmz1vs2iEpNQq
201 lines
5.7 KiB
C#
201 lines
5.7 KiB
C#
using System;
|
|
using System.Collections.Generic;
|
|
using System.Globalization;
|
|
using System.Text;
|
|
|
|
namespace StarterNG.Classes;
|
|
|
|
public sealed class ConfigFile
|
|
{
|
|
private readonly List<string> _lines = new();
|
|
|
|
private readonly Dictionary<string, int> _index =
|
|
new(StringComparer.OrdinalIgnoreCase);
|
|
|
|
public static ConfigFile Parse(string text)
|
|
{
|
|
var cfg = new ConfigFile();
|
|
|
|
text = text.Replace("\r\n", "\n").Replace('\r', '\n');
|
|
foreach (var line in text.Split('\n'))
|
|
cfg.AddLine(line);
|
|
return cfg;
|
|
}
|
|
|
|
private void AddLine(string raw)
|
|
{
|
|
int idx = _lines.Count;
|
|
_lines.Add(raw);
|
|
if (TryParseKey(raw, out string key))
|
|
_index[key] = idx;
|
|
}
|
|
|
|
public bool Has(string key) => _index.ContainsKey(key);
|
|
|
|
public string? GetRaw(string key)
|
|
{
|
|
if (!_index.TryGetValue(key, out int i))
|
|
return null;
|
|
SplitLine(_lines[i], out _, out _, out string value, out _);
|
|
return value;
|
|
}
|
|
|
|
public string GetString(string key, string fallback) =>
|
|
GetRaw(key) is { Length: > 0 } v ? v : fallback;
|
|
|
|
public bool GetBool(string key, bool fallback)
|
|
{
|
|
var v = FirstToken(GetRaw(key));
|
|
if (v is null) return fallback;
|
|
return v.ToLowerInvariant() switch
|
|
{
|
|
"yes" or "true" or "1" or "tak" => true,
|
|
"no" or "false" or "0" or "nie" => false,
|
|
_ => fallback
|
|
};
|
|
}
|
|
|
|
public int GetInt(string key, int fallback)
|
|
{
|
|
var v = FirstToken(GetRaw(key));
|
|
return v != null && int.TryParse(v, NumberStyles.Integer,
|
|
CultureInfo.InvariantCulture, out int n) ? n : fallback;
|
|
}
|
|
|
|
public double GetDouble(string key, double fallback)
|
|
{
|
|
var v = FirstToken(GetRaw(key));
|
|
return v != null && double.TryParse(v, NumberStyles.Float,
|
|
CultureInfo.InvariantCulture, out double d) ? d : fallback;
|
|
}
|
|
|
|
public string[] GetTokens(string key)
|
|
{
|
|
var v = GetRaw(key);
|
|
return string.IsNullOrEmpty(v)
|
|
? Array.Empty<string>()
|
|
: v.Split((char[]?)null, StringSplitOptions.RemoveEmptyEntries);
|
|
}
|
|
|
|
public void Set(string key, string value)
|
|
{
|
|
value ??= string.Empty;
|
|
if (_index.TryGetValue(key, out int i))
|
|
{
|
|
SplitLine(_lines[i], out string indent, out _, out _, out string comment);
|
|
var sb = new StringBuilder();
|
|
sb.Append(indent).Append(key);
|
|
if (value.Length > 0)
|
|
sb.Append(' ').Append(value);
|
|
if (comment.Length > 0)
|
|
sb.Append('\t').Append(comment);
|
|
_lines[i] = sb.ToString();
|
|
}
|
|
else
|
|
{
|
|
int idx = _lines.Count;
|
|
_lines.Add(value.Length > 0 ? $"{key} {value}" : key);
|
|
_index[key] = idx;
|
|
}
|
|
}
|
|
|
|
public void Remove(string key)
|
|
{
|
|
if (!_index.TryGetValue(key, out int i))
|
|
return;
|
|
|
|
SplitLine(_lines[i], out string indent, out _, out string value, out string comment);
|
|
var sb = new StringBuilder();
|
|
sb.Append(indent).Append("// ").Append(key);
|
|
if (value.Length > 0)
|
|
sb.Append(' ').Append(value);
|
|
if (comment.Length > 0)
|
|
sb.Append('\t').Append(comment);
|
|
_lines[i] = sb.ToString();
|
|
|
|
_index.Remove(key);
|
|
}
|
|
|
|
public void SetBool(string key, bool value) => Set(key, value ? "yes" : "no");
|
|
public void SetInt(string key, int value) =>
|
|
Set(key, value.ToString(CultureInfo.InvariantCulture));
|
|
public void SetDouble(string key, double value) =>
|
|
Set(key, value.ToString("0.###", CultureInfo.InvariantCulture));
|
|
|
|
public string ToText() => string.Join(Environment.NewLine, _lines);
|
|
|
|
private static bool TryParseKey(string raw, out string key)
|
|
{
|
|
SplitLine(raw, out _, out key, out _, out _);
|
|
return key.Length > 0;
|
|
}
|
|
|
|
private static void SplitLine(string raw, out string indent, out string key,
|
|
out string value, out string comment)
|
|
{
|
|
indent = key = value = comment = string.Empty;
|
|
|
|
int start = 0;
|
|
while (start < raw.Length && char.IsWhiteSpace(raw[start]))
|
|
start++;
|
|
indent = raw.Substring(0, start);
|
|
|
|
string body = raw.Substring(start);
|
|
|
|
if (body.StartsWith("//"))
|
|
{
|
|
comment = body;
|
|
return;
|
|
}
|
|
if (body.Length == 0)
|
|
return;
|
|
|
|
int commentAt = FindCommentStart(body);
|
|
string content = commentAt >= 0 ? body.Substring(0, commentAt) : body;
|
|
if (commentAt >= 0)
|
|
comment = body.Substring(commentAt).TrimEnd();
|
|
|
|
content = content.TrimEnd();
|
|
if (content.Length == 0)
|
|
return;
|
|
|
|
int sp = IndexOfWhitespace(content);
|
|
if (sp < 0)
|
|
{
|
|
key = content;
|
|
}
|
|
else
|
|
{
|
|
key = content.Substring(0, sp);
|
|
value = content.Substring(sp).Trim();
|
|
}
|
|
}
|
|
|
|
private static int FindCommentStart(string body)
|
|
{
|
|
for (int i = 0; i + 1 < body.Length; i++)
|
|
{
|
|
if (body[i] == '/' && body[i + 1] == '/' &&
|
|
(i == 0 || char.IsWhiteSpace(body[i - 1])))
|
|
return i;
|
|
}
|
|
return -1;
|
|
}
|
|
|
|
private static int IndexOfWhitespace(string s)
|
|
{
|
|
for (int i = 0; i < s.Length; i++)
|
|
if (char.IsWhiteSpace(s[i]))
|
|
return i;
|
|
return -1;
|
|
}
|
|
|
|
private static string? FirstToken(string? value)
|
|
{
|
|
if (string.IsNullOrEmpty(value))
|
|
return null;
|
|
int sp = IndexOfWhitespace(value);
|
|
return sp < 0 ? value : value.Substring(0, sp);
|
|
}
|
|
}
|