read rolling stock from dynamic/*/*/textures.txt, drop vehicles.json

This commit is contained in:
maj00r
2026-08-31 00:44:56 +02:00
parent 6a7255afdf
commit 8ed3765562
8 changed files with 82 additions and 179 deletions

View File

@@ -2,6 +2,7 @@ using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
namespace StarterNG.Classes;
@@ -25,43 +26,25 @@ public sealed class GameData
public bool Loaded { get; private set; }
public void Load(IProgress<LoadStatus>? progress = null,
string vehiclesDir = "databases/vehicles/",
string sceneryDir = "scenery/",
string miniDir = "textures/mini/")
string miniDir = "textures/mini/",
string dynamicRoot = "dynamic")
{
if (Loaded)
return;
var vehicleFiles = VehicleDatabase.EnumerateFiles(vehiclesDir);
var scnFiles = EnumerateScenery(sceneryDir);
int total = Math.Max(1, vehicleFiles.Count + scnFiles.Count);
int total = Math.Max(1, scnFiles.Count);
int done = 0;
Vehicles.BeginLoad();
foreach (string file in vehicleFiles)
{
progress?.Report(new LoadStatus((double)done / total, LoadPhase.Vehicles,
Path.GetFileName(file)));
try { Vehicles.LoadFile(file); }
catch { }
done++;
}
try
{
int legacy = Vehicles.ImportLegacy();
if (legacy > 0)
Infrastructure.Diagnostics.Log($"textures.txt: wczytano {legacy} tekstur");
}
catch (Exception ex)
{
Infrastructure.Diagnostics.Log("textures.txt import", ex);
}
progress?.Report(new LoadStatus(0, LoadPhase.Vehicles, "textures.txt"));
int vehicles = Vehicles.LoadFromTexturesTxt(dynamicRoot);
if (vehicles == 0)
Infrastructure.Diagnostics.Log($"{dynamicRoot}: brak plikow textures.txt");
Vehicles.EndLoad();
VehicleDatabase.PreloadMiniIndex(miniDir);
Physics.PreloadIndex();
Parallel.Invoke(
() => VehicleDatabase.PreloadMiniIndex(miniDir),
() => Physics.PreloadIndex());
var sceneryLock = new object();
var doneCount = done;
@@ -79,7 +62,10 @@ public sealed class GameData
Path.GetFileName(file)));
}
}
catch { }
catch (Exception ex)
{
StarterNG.Infrastructure.Diagnostics.Log($"scenery/{Path.GetFileName(file)}", ex);
}
});
done = doneCount;
@@ -101,7 +87,10 @@ public sealed class GameData
lock (sceneryLock)
Sceneries.Add(scenery);
}
catch { }
catch (Exception ex)
{
StarterNG.Infrastructure.Diagnostics.Log($"scenery/{Path.GetFileName(file)}", ex);
}
});
}

View File

@@ -5,7 +5,7 @@ using System.Text;
namespace StarterNG.Classes;
public static class LegacyTextureDatabase
public static class TexturesTxt
{
public static List<string> EnumerateFiles(string dynamicRoot = "dynamic")
{
@@ -139,8 +139,7 @@ public static class LegacyTextureDatabase
Id = groupId,
Category = categorySign,
Mini = first.Mini,
Archived = archived,
Implicit = true
Archived = archived
});
var texture = new VehicleTexture
@@ -148,6 +147,8 @@ public static class LegacyTextureDatabase
Uuid = $"legacy:{dir}{skin}",
Directory = dir,
Skinfile = skin,
Wreck = skin.Contains("wreck", StringComparison.OrdinalIgnoreCase) ||
skin.Contains("wrak", StringComparison.OrdinalIgnoreCase),
Model = first.Model,
Group = groupId,
MiniRef = first.Mini,

View File

@@ -2,78 +2,68 @@ using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text.Json;
using System.Text.Json.Serialization;
using System.Text.Json.Serialization.Metadata;
using System.Threading.Tasks;
namespace StarterNG.Classes;
public class VehicleEntry
{
[JsonPropertyName("uuid")] public string? Uuid { get; set; }
[JsonPropertyName("schema_version")] public int SchemaVersion { get; set; }
[JsonPropertyName("groups")] public List<VehicleGroup> Groups { get; set; } = new();
[JsonPropertyName("textures")] public List<VehicleTexture> Textures { get; set; } = new();
[JsonPropertyName("sets")] public List<VehicleSet> Sets { get; set; } = new();
[JsonPropertyName("unknown")] public List<string> Unknown { get; set; } = new();
}
public class VehicleEntryCollection
{
[JsonPropertyName("vehicles")] public List<VehicleEntry> Vehicles { get; set; } = new();
public string? Uuid { get; set; }
public List<VehicleGroup> Groups { get; set; } = new();
public List<VehicleTexture> Textures { get; set; } = new();
public List<VehicleSet> Sets { get; set; } = new();
}
public class VehicleGroup
{
[JsonPropertyName("id")] public string Id { get; set; } = "";
[JsonPropertyName("category")] public string? Category { get; set; }
[JsonPropertyName("mini")] public string? Mini { get; set; }
[JsonPropertyName("archived")] public bool Archived { get; set; }
[JsonPropertyName("implicit")] public bool Implicit { get; set; }
public string Id { get; set; } = "";
public string? Category { get; set; }
public string? Mini { get; set; }
public bool Archived { get; set; }
}
public class VehicleTexture
{
[JsonPropertyName("uuid")] public string? Uuid { get; set; }
[JsonPropertyName("directory")] public string Directory { get; set; } = "";
[JsonPropertyName("skinfile")] public string Skinfile { get; set; } = "";
[JsonPropertyName("model")] public string? Model { get; set; }
[JsonPropertyName("group")] public string? Group { get; set; }
[JsonPropertyName("mini_ref")] public string? MiniRef { get; set; }
[JsonPropertyName("texture_mini")] public string? TextureMini { get; set; }
[JsonPropertyName("wreck")] public bool Wreck { get; set; }
[JsonPropertyName("aliases")] public List<VehicleAlias> Aliases { get; set; } = new();
[JsonPropertyName("meta")] public VehicleMeta? Meta { get; set; }
public string? Uuid { get; set; }
public string Directory { get; set; } = "";
public string Skinfile { get; set; } = "";
public string? Model { get; set; }
public string? Group { get; set; }
public string? MiniRef { get; set; }
public string? TextureMini { get; set; }
public bool Wreck { get; set; }
public List<VehicleAlias> Aliases { get; set; } = new();
public VehicleMeta? Meta { get; set; }
[JsonIgnore] public string FullPath => Directory + Skinfile;
public string FullPath => Directory + Skinfile;
[JsonIgnore] public string ResolvedClass { get; internal set; } = "";
public string ResolvedClass { get; internal set; } = "";
[JsonIgnore] public string? ResolvedCategory { get; internal set; }
public string? ResolvedCategory { get; internal set; }
[JsonIgnore] public bool ResolvedArchived { get; internal set; }
public bool ResolvedArchived { get; internal set; }
}
public class VehicleAlias
{
[JsonPropertyName("model")] public string? Model { get; set; }
[JsonPropertyName("group")] public string? Group { get; set; }
[JsonPropertyName("mini_ref")] public string? MiniRef { get; set; }
[JsonPropertyName("texture_mini")] public string? TextureMini { get; set; }
public string? Model { get; set; }
public string? Group { get; set; }
public string? MiniRef { get; set; }
public string? TextureMini { get; set; }
}
public class VehicleMeta
{
[JsonPropertyName("raw")] public string? Raw { get; set; }
[JsonPropertyName("version")] public string? Version { get; set; }
[JsonPropertyName("vehicle")] public string? Vehicle { get; set; }
[JsonPropertyName("operator")] public string? Operator { get; set; }
[JsonPropertyName("depot")] public string? Depot { get; set; }
[JsonPropertyName("revision_date")] public string? RevisionDate { get; set; }
[JsonPropertyName("revision_place")] public string? RevisionPlace { get; set; }
[JsonPropertyName("texture_author")] public string? TextureAuthor { get; set; }
[JsonPropertyName("photo_author")] public string? PhotoAuthor { get; set; }
[JsonPropertyName("extra")] public List<string> Extra { get; set; } = new();
public string? Raw { get; set; }
public string? Version { get; set; }
public string? Vehicle { get; set; }
public string? Operator { get; set; }
public string? Depot { get; set; }
public string? RevisionDate { get; set; }
public string? RevisionPlace { get; set; }
public string? TextureAuthor { get; set; }
public string? PhotoAuthor { get; set; }
public List<string> Extra { get; set; } = new();
}
public class VehicleDatabase
@@ -88,20 +78,22 @@ public class VehicleDatabase
public Dictionary<string, VehicleTexture> TextureBySkin { get; } = new();
private static readonly JsonSerializerOptions JsonOpts = new()
{
PropertyNameCaseInsensitive = true,
ReadCommentHandling = JsonCommentHandling.Skip,
AllowTrailingCommas = true,
TypeInfoResolver = VehicleJsonContext.Default,
};
public void Load(string directory = "databases/vehicles/")
public int LoadFromTexturesTxt(string dynamicRoot = "dynamic")
{
BeginLoad();
foreach (string file in EnumerateFiles(directory))
LoadFile(file);
int count = 0;
foreach (string file in TexturesTxt.EnumerateFiles(dynamicRoot))
{
var entry = TexturesTxt.Parse(file, dynamicRoot);
if (entry is null) continue;
count += entry.Textures.Count;
Ingest(entry);
}
EndLoad();
return count;
}
public void BeginLoad()
@@ -120,79 +112,6 @@ public class VehicleDatabase
BuildTextureIndex();
}
public static List<string> EnumerateFiles(string directory = "databases/vehicles/")
{
if (!Directory.Exists(directory))
return new List<string>();
string merged = Path.Combine(directory, "vehicles.json");
if (File.Exists(merged))
return new List<string> { merged };
return Directory.GetFiles(directory, "*.json").ToList();
}
public void LoadFile(string file)
{
if (string.Equals(Path.GetFileName(file), "vehicles.json", StringComparison.OrdinalIgnoreCase))
LoadMerged(file);
else
LoadEntryFile(file);
}
private void LoadMerged(string file)
{
try
{
var collection = JsonSerializer.Deserialize(
File.ReadAllText(file),
(JsonTypeInfo<VehicleEntryCollection>)JsonOpts.GetTypeInfo(typeof(VehicleEntryCollection)));
if (collection?.Vehicles is null) return;
foreach (var entry in collection.Vehicles)
Ingest(entry);
}
catch
{
}
}
private void LoadEntryFile(string file)
{
try
{
var entry = JsonSerializer.Deserialize(
File.ReadAllText(file),
(JsonTypeInfo<VehicleEntry>)JsonOpts.GetTypeInfo(typeof(VehicleEntry)));
if (entry is not null)
Ingest(entry);
}
catch
{
}
}
public int ImportLegacy(string dynamicRoot = "dynamic")
{
int added = 0;
foreach (string file in LegacyTextureDatabase.EnumerateFiles(dynamicRoot))
{
var entry = LegacyTextureDatabase.Parse(file, dynamicRoot);
if (entry is null) continue;
entry.Textures.RemoveAll(t =>
TextureBySkin.ContainsKey(
Path.GetFileNameWithoutExtension(t.Skinfile).ToLowerInvariant()));
if (entry.Textures.Count == 0) continue;
added += entry.Textures.Count;
Ingest(entry);
}
return added;
}
private void Ingest(VehicleEntry entry)
{
foreach (var group in entry.Groups)
@@ -386,14 +305,8 @@ public class VehicleDatabase
public class VehicleSet
{
[JsonPropertyName("uuid")] public string? Uuid { get; set; }
[JsonPropertyName("mode")] public string? Mode { get; set; }
[JsonPropertyName("count")] public int Count { get; set; }
[JsonPropertyName("texture_refs")] public List<string> TextureRefs { get; set; } = new();
}
[JsonSerializable(typeof(VehicleEntry))]
[JsonSerializable(typeof(VehicleEntryCollection))]
internal partial class VehicleJsonContext : JsonSerializerContext
{
public string? Uuid { get; set; }
public string? Mode { get; set; }
public int Count { get; set; }
public List<string> TextureRefs { get; set; } = new();
}

View File

@@ -90,7 +90,7 @@
<String key="Vehicle">Vozidlo</String>
<String key="Crew">s osádkou</String>
<String key="Reversed">otočeno</String>
<String key="NoVehicles">Ve složce databases/vehicles/ nebyla nalezena žádná vozidla.</String>
<String key="NoVehicles">Ve složce dynamic/**/textures.txt nebyla nalezena žádná vozidla.</String>
<String key="ConsistEmpty">Přidejte vozidla z databáze vlevo nebo načtěte existující soupravu ze souboru scenérie.</String>
<String key="TipMoveLeft">Posunout doleva</String>
<String key="TipMoveRight">Posunout doprava</String>

View File

@@ -90,7 +90,7 @@
<String key="Vehicle">Vehicle</String>
<String key="Crew">with crew</String>
<String key="Reversed">reversed</String>
<String key="NoVehicles">No vehicles found in databases/vehicles/.</String>
<String key="NoVehicles">No vehicles found in dynamic/**/textures.txt.</String>
<String key="ConsistEmpty">Add vehicles from the database on the left, or load an existing consist from a scenery file.</String>
<String key="TipMoveLeft">Move left</String>
<String key="TipMoveRight">Move right</String>

View File

@@ -90,7 +90,7 @@
<String key="Vehicle">Jármű</String>
<String key="Crew">személyzettel</String>
<String key="Reversed">megfordítva</String>
<String key="NoVehicles">A databases/vehicles/ mappában nem található jármű.</String>
<String key="NoVehicles">A dynamic/**/textures.txt fájlokban nem található jármű.</String>
<String key="ConsistEmpty">Adjon hozzá járműveket a bal oldali adatbázisból, vagy töltsön be meglévő szerelvényt egy pályafájlból.</String>
<String key="TipMoveLeft">Mozgatás balra</String>
<String key="TipMoveRight">Mozgatás jobbra</String>

View File

@@ -90,7 +90,7 @@
<String key="Vehicle">Pojazd</String>
<String key="Crew">z obsadą</String>
<String key="Reversed">obrócony</String>
<String key="NoVehicles">Nie znaleziono pojazdów w databases/vehicles/.</String>
<String key="NoVehicles">Nie znaleziono pojazdów w dynamic/**/textures.txt.</String>
<String key="ConsistEmpty">Dodaj pojazdy z bazy po lewej stronie lub wczytaj istniejący skład z pliku scenerii.</String>
<String key="TipMoveLeft">Przesuń w lewo</String>
<String key="TipMoveRight">Przesuń w prawo</String>

View File

@@ -90,7 +90,7 @@
<String key="Vehicle">Единица</String>
<String key="Crew">с бригадой</String>
<String key="Reversed">развёрнут</String>
<String key="NoVehicles">В databases/vehicles/ подвижной состав не найден.</String>
<String key="NoVehicles">В dynamic/**/textures.txt подвижной состав не найден.</String>
<String key="ConsistEmpty">Добавьте единицы из базы слева или загрузите готовый состав из файла сценерии.</String>
<String key="TipMoveLeft">Сдвинуть влево</String>
<String key="TipMoveRight">Сдвинуть вправо</String>