diff --git a/StarterNG/Classes/PresetStore.cs b/StarterNG/Classes/PresetStore.cs
index 9d9ce65..6807f97 100644
--- a/StarterNG/Classes/PresetStore.cs
+++ b/StarterNG/Classes/PresetStore.cs
@@ -1,13 +1,22 @@
using System;
using System.Collections.Generic;
+using System.Globalization;
using System.IO;
using System.Linq;
+using System.Text;
using System.Text.Json;
using System.Text.Json.Serialization;
using System.Text.Json.Serialization.Metadata;
+using System.Text.RegularExpressions;
namespace StarterNG.Classes;
+public enum PresetSort
+{
+ Name,
+ Track
+}
+
/// One saved consist in the user's vehicle warehouse.
public sealed class TrainsetPreset
{
@@ -54,6 +63,9 @@ public static class PresetStore
public static string FilePath { get; } = ResolvePath();
+ /// Pascal miSortByVehicleName / miSortByTrackName for the warehouse menu.
+ public static PresetSort SortMode { get; set; } = PresetSort.Name;
+
// Source-generated metadata keeps (de)serialisation AOT/trim-safe, matching the
// approach used for the vehicle database.
private static readonly JsonSerializerOptions Options = new()
@@ -65,8 +77,16 @@ public static class PresetStore
private static JsonTypeInfo TypeInfo =>
(JsonTypeInfo)Options.GetTypeInfo(typeof(PresetCollection));
- /// All saved presets, sorted by name. Empty when none / unreadable.
- public static IReadOnlyList All() => Load().Presets;
+ /// All saved presets, sorted per .
+ public static IReadOnlyList All()
+ {
+ var list = Load().Presets.ToList();
+ if (SortMode == PresetSort.Track)
+ list.Sort((a, b) => string.Compare(TrackOf(a), TrackOf(b), StringComparison.OrdinalIgnoreCase));
+ else
+ list.Sort((a, b) => string.Compare(a.Name, b.Name, StringComparison.OrdinalIgnoreCase));
+ return list;
+ }
///
/// Saves (or overwrites, by name) a consist preset. No-op on a blank name or
@@ -93,6 +113,77 @@ public static class PresetStore
Persist(col);
}
+ ///
+ /// Import Pascal starter/magazyn.ini (or legacy starter.ini / RAINSTED.INI)
+ /// into the JSON warehouse. Returns how many consists were added.
+ ///
+ public static int ImportMagazyn(string? path = null)
+ {
+ path ??= FindMagazynPath();
+ if (path is null || !File.Exists(path)) return 0;
+
+ int added = 0;
+ try
+ {
+ string text = File.ReadAllText(path, Encoding.GetEncoding(1250));
+ var section = Regex.Matches(text,
+ @"\[TRAINSET\d+\s*=\s*([^\]]*)\](.*?)(?=\[TRAINSET|\z)",
+ RegexOptions.IgnoreCase | RegexOptions.Singleline);
+
+ foreach (Match m in section)
+ {
+ string name = m.Groups[1].Value.Trim();
+ if (name.Length == 0) name = $"import_{added + 1}";
+
+ var nodes = new List();
+ foreach (Match line in Regex.Matches(m.Groups[2].Value, @"(?m)^\s*\d+\s*=\s*(.+)$"))
+ {
+ string body = line.Groups[1].Value.Trim();
+ if (!body.Contains("enddynamic", StringComparison.OrdinalIgnoreCase))
+ body += " enddynamic";
+ nodes.Add(body);
+ }
+ if (nodes.Count == 0) continue;
+
+ var sb = new StringBuilder();
+ sb.Append("trainset ").Append(Sanitize(name)).Append(" none 0 0\n");
+ foreach (string n in nodes)
+ sb.Append(n).Append('\n');
+ sb.Append("endtrainset\n");
+
+ Save(name, sb.ToString());
+ added++;
+ }
+ }
+ catch { }
+ return added;
+ }
+
+ private static string? FindMagazynPath()
+ {
+ string cwd = Directory.GetCurrentDirectory();
+ foreach (string rel in new[]
+ {
+ Path.Combine("starter", "magazyn.ini"),
+ "starter.ini",
+ "RAINSTED.INI"
+ })
+ {
+ string p = Path.Combine(cwd, rel);
+ if (File.Exists(p)) return p;
+ }
+ return null;
+ }
+
+ private static string TrackOf(TrainsetPreset p)
+ {
+ var m = Regex.Match(p.Entry ?? "", @"^\s*trainset\s+\S+\s+(\S+)", RegexOptions.Multiline | RegexOptions.IgnoreCase);
+ return m.Success ? m.Groups[1].Value : "";
+ }
+
+ private static string Sanitize(string name) =>
+ string.IsNullOrWhiteSpace(name) ? "preset" : name.Replace(' ', '_');
+
private static PresetCollection Load()
{
try
diff --git a/StarterNG/Views/ConsistContextMenu.cs b/StarterNG/Views/ConsistContextMenu.cs
index db01485..e226352 100644
--- a/StarterNG/Views/ConsistContextMenu.cs
+++ b/StarterNG/Views/ConsistContextMenu.cs
@@ -61,6 +61,25 @@ public static class ConsistContextMenu
}
menu.Items.Add(load);
+ var sort = new MenuItem { Header = App.Loc["PresetSort"] };
+ var sortName = new MenuItem
+ {
+ Header = (PresetStore.SortMode == PresetSort.Name ? "● " : "○ ") + App.Loc["PresetSortName"]
+ };
+ sortName.Click += (_, _) => PresetStore.SortMode = PresetSort.Name;
+ var sortTrack = new MenuItem
+ {
+ Header = (PresetStore.SortMode == PresetSort.Track ? "● " : "○ ") + App.Loc["PresetSortTrack"]
+ };
+ sortTrack.Click += (_, _) => PresetStore.SortMode = PresetSort.Track;
+ sort.Items.Add(sortName);
+ sort.Items.Add(sortTrack);
+ menu.Items.Add(sort);
+
+ var import = new MenuItem { Header = App.Loc["PresetImportMagazyn"] };
+ import.Click += (_, _) => PresetStore.ImportMagazyn();
+ menu.Items.Add(import);
+
menu.Items.Add(new Separator());
// ── Clipboard (full trainset text) ─────────────────────────────────────