mirror of
https://github.com/MaSzyna-EU07/StarterNG.git
synced 2026-10-18 08:29:19 +02:00
213 lines
7.4 KiB
C#
213 lines
7.4 KiB
C#
using System;
|
|
using System.Collections.Generic;
|
|
using System.IO;
|
|
using System.Linq;
|
|
using System.Text;
|
|
|
|
namespace StarterNG.Classes;
|
|
|
|
public sealed class Physics
|
|
{
|
|
public double Mass;
|
|
public double VMax;
|
|
public double Length;
|
|
public string LoadAccepted = "";
|
|
public int MaxLoad;
|
|
public int AllowedFlagA = 3;
|
|
public int AllowedFlagB = 3;
|
|
public string ControlTypeA = "";
|
|
public string ControlTypeB = "";
|
|
|
|
private static readonly Dictionary<string, Physics?> Cache = new(StringComparer.OrdinalIgnoreCase);
|
|
|
|
private static Dictionary<string, string>? _fizIndex;
|
|
|
|
public static void PreloadIndex(string dynamicRoot = "dynamic") => FizIndex(dynamicRoot);
|
|
|
|
public static int IndexedCount => _fizIndex?.Count ?? 0;
|
|
|
|
private static Dictionary<string, string> FizIndex(string dynamicRoot)
|
|
{
|
|
if (_fizIndex != null)
|
|
return _fizIndex;
|
|
|
|
var index = new Dictionary<string, string>(StringComparer.OrdinalIgnoreCase);
|
|
try
|
|
{
|
|
if (Directory.Exists(dynamicRoot))
|
|
foreach (string f in Directory.EnumerateFiles(dynamicRoot, "*.fiz", SearchOption.AllDirectories))
|
|
index[Path.GetFileNameWithoutExtension(f)] = f;
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
StarterNG.Infrastructure.Diagnostics.Log($"indexing {dynamicRoot}", ex);
|
|
}
|
|
|
|
_fizIndex = index;
|
|
return index;
|
|
}
|
|
|
|
public static Physics? For(string? dataFolder, string? model, string dynamicRoot = "dynamic")
|
|
{
|
|
if (string.IsNullOrEmpty(model))
|
|
return null;
|
|
|
|
string m = Path.GetFileNameWithoutExtension(model!);
|
|
if (m.Length == 0)
|
|
return null;
|
|
|
|
if (Cache.TryGetValue(m, out var cached))
|
|
return cached;
|
|
|
|
var index = FizIndex(dynamicRoot);
|
|
string? path = null;
|
|
if (index.TryGetValue(m, out var p1)) path = p1;
|
|
else if (index.TryGetValue(m + "dumb", out var p2)) path = p2;
|
|
|
|
Physics? phys = null;
|
|
if (path != null)
|
|
{
|
|
phys = new Physics { AllowedFlagB = -1 };
|
|
try { Parse(phys, path, Path.GetDirectoryName(path) ?? "", null, 0); }
|
|
catch (Exception ex) { StarterNG.Infrastructure.Diagnostics.Log($"physics/{Path.GetFileName(path)}", ex); }
|
|
|
|
if (phys.AllowedFlagB == -1)
|
|
{
|
|
phys.AllowedFlagB = phys.AllowedFlagA;
|
|
if (string.IsNullOrEmpty(phys.ControlTypeB))
|
|
phys.ControlTypeB = phys.ControlTypeA;
|
|
}
|
|
}
|
|
|
|
Cache[m] = phys;
|
|
return phys;
|
|
}
|
|
|
|
private static readonly HashSet<string> KnownKeys = new(StringComparer.OrdinalIgnoreCase)
|
|
{
|
|
"M", "Vmax", "L", "LoadAccepted", "MaxLoad", "AllowedFlag", "ControlType"
|
|
};
|
|
|
|
private static void Parse(Physics p, string path, string dir, string[]? prms, int depth)
|
|
{
|
|
if (depth > 6 || !File.Exists(path))
|
|
return;
|
|
|
|
var tokens = Tokenize(File.ReadAllText(path, Encoding.GetEncoding(1250)));
|
|
|
|
string section = "";
|
|
string? pendingKey = null;
|
|
|
|
for (int i = 0; i < tokens.Count; i++)
|
|
{
|
|
string tok = tokens[i];
|
|
if (tok == "=")
|
|
continue;
|
|
|
|
if (tok.Equals("include", StringComparison.OrdinalIgnoreCase))
|
|
{
|
|
if (++i >= tokens.Count) break;
|
|
string incFile = Resolve(tokens[i], prms);
|
|
var incParams = new List<string>();
|
|
i++;
|
|
while (i < tokens.Count && !tokens[i].Equals("end", StringComparison.OrdinalIgnoreCase))
|
|
incParams.Add(Resolve(tokens[i++], prms));
|
|
Parse(p, Path.Combine(dir, incFile), dir, incParams.ToArray(), depth + 1);
|
|
pendingKey = null;
|
|
continue;
|
|
}
|
|
|
|
int eq = tok.IndexOf('=');
|
|
if (eq >= 0)
|
|
{
|
|
string k = tok[..eq].TrimStart('.').Trim();
|
|
string v = tok[(eq + 1)..].Trim();
|
|
if (v.Length == 0) { pendingKey = k; continue; }
|
|
Apply(p, section, k, Resolve(v, prms));
|
|
pendingKey = null;
|
|
continue;
|
|
}
|
|
|
|
string t = tok.TrimStart('.').Trim();
|
|
if (pendingKey != null)
|
|
{
|
|
Apply(p, section, pendingKey, Resolve(t, prms));
|
|
pendingKey = null;
|
|
}
|
|
else if (KnownKeys.Contains(t))
|
|
{
|
|
pendingKey = t;
|
|
}
|
|
else
|
|
{
|
|
|
|
section = new string(t.Where(char.IsLetterOrDigit).ToArray()).ToLowerInvariant();
|
|
}
|
|
}
|
|
}
|
|
|
|
private static void Apply(Physics p, string section, string key, string value)
|
|
{
|
|
switch (section)
|
|
{
|
|
case "param":
|
|
if (key.Equals("M", StringComparison.OrdinalIgnoreCase)) p.Mass = ParseD(value, p.Mass);
|
|
else if (key.Equals("Vmax", StringComparison.OrdinalIgnoreCase)) p.VMax = ParseD(value, p.VMax);
|
|
break;
|
|
case "load":
|
|
if (key.Equals("LoadAccepted", StringComparison.OrdinalIgnoreCase)) p.LoadAccepted = value;
|
|
else if (key.Equals("MaxLoad", StringComparison.OrdinalIgnoreCase)) p.MaxLoad = (int)ParseD(value, p.MaxLoad);
|
|
break;
|
|
case "dimensions":
|
|
if (key.Equals("L", StringComparison.OrdinalIgnoreCase)) p.Length = ParseD(value, p.Length);
|
|
break;
|
|
case "buffcoupl":
|
|
case "buffcoupl1":
|
|
if (key.Equals("AllowedFlag", StringComparison.OrdinalIgnoreCase)) p.AllowedFlagA = Flag(value, p.AllowedFlagA);
|
|
else if (key.Equals("ControlType", StringComparison.OrdinalIgnoreCase)) p.ControlTypeA = value;
|
|
break;
|
|
case "buffcoupl2":
|
|
if (key.Equals("AllowedFlag", StringComparison.OrdinalIgnoreCase)) p.AllowedFlagB = Flag(value, p.AllowedFlagB);
|
|
else if (key.Equals("ControlType", StringComparison.OrdinalIgnoreCase)) p.ControlTypeB = value;
|
|
break;
|
|
}
|
|
}
|
|
|
|
private static int Flag(string v, int fallback)
|
|
=> int.TryParse(v, System.Globalization.NumberStyles.Integer,
|
|
System.Globalization.CultureInfo.InvariantCulture, out int f)
|
|
? (f < 0 ? -f + 128 : f)
|
|
: fallback;
|
|
|
|
private static double ParseD(string v, double fallback)
|
|
=> double.TryParse(v, System.Globalization.NumberStyles.Float,
|
|
System.Globalization.CultureInfo.InvariantCulture, out double d)
|
|
? d
|
|
: fallback;
|
|
|
|
private static string Resolve(string token, string[]? prms)
|
|
{
|
|
if (prms == null || token.Length < 3 || token[0] != '(' || token[^1] != ')')
|
|
return token;
|
|
string digits = new string(token.Where(char.IsDigit).ToArray());
|
|
return int.TryParse(digits, out int idx) && idx >= 1 && idx <= prms.Length
|
|
? prms[idx - 1]
|
|
: token;
|
|
}
|
|
|
|
private static List<string> Tokenize(string text)
|
|
{
|
|
var sb = new StringBuilder();
|
|
foreach (var line in text.Split('\n'))
|
|
{
|
|
string l = line;
|
|
int hash = l.IndexOf('#');
|
|
if (hash >= 0) l = l[..hash];
|
|
sb.Append(l).Append(' ');
|
|
}
|
|
return sb.ToString()
|
|
.Split(new[] { ' ', '\t', '\r', '\n' }, StringSplitOptions.RemoveEmptyEntries)
|
|
.ToList();
|
|
}
|
|
}
|